[alsa-devel] [PATCH 00/10] ARM: mxs: add audio support
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 in master mode due to hw limitation(rx can work in both master & slave mode).
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 | 7 + 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/audio.h | 18 + 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 | 20 + sound/soc/mxs/Makefile | 10 + sound/soc/mxs/mxs-pcm.c | 377 ++++++++++++++++ sound/soc/mxs/mxs-pcm.h | 43 ++ sound/soc/mxs/mxs-saif.c | 523 +++++++++++++++++++++++ sound/soc/mxs/mxs-saif.h | 132 ++++++ sound/soc/mxs/mxs-sgtl5000.c | 181 ++++++++ 19 files changed, 1437 insertions(+), 2 deletions(-) create mode 100644 arch/arm/mach-mxs/devices/platform-mxs-saif.c create mode 100644 arch/arm/mach-mxs/include/mach/audio.h 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 --- sound/soc/mxs/mxs-pcm.c | 378 +++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/mxs/mxs-pcm.h | 43 ++++++ 2 files changed, 421 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..32e4e29 --- /dev/null +++ b/sound/soc/mxs/mxs-pcm.c @@ -0,0 +1,378 @@ +/* + * 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; + + /* FIXME: There's unexpected dma irq before iprtd is initialised */ + if (iprtd->periods == 0) + return; + + 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 = (unsigned int *)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_prepare(struct snd_pcm_substream *substream) +{ + 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, + .prepare = snd_mxs_pcm_prepare, + .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 Fri, Jul 08, 2011 at 11:59:41PM +0800, Dong Aisheng wrote:
Signed-off-by: Dong Aisheng b29396@freescale.com
*Always* CC maintainers on patches.
- Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
- Refer to sound/soc/imx/imx-pcm-dma-mx2.c
Why?
- iprtd->buf = (unsigned int *)substream->dma_buffer.area;
This cast looks incredibly suspicious...
+static int snd_mxs_pcm_prepare(struct snd_pcm_substream *substream) +{
- return 0;
+}
Remove empty functions.
+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;
Why the random blank line?
+static struct platform_driver mxs_pcm_driver = {
- .driver = {
.name = "mxs-pcm-audio",
.owner = THIS_MODULE,
Indentation here is very odd...
2011/7/9 Mark Brown broonie@opensource.wolfsonmicro.com:
On Fri, Jul 08, 2011 at 11:59:41PM +0800, Dong Aisheng wrote:
Signed-off-by: Dong Aisheng b29396@freescale.com
*Always* CC maintainers on patches.
Sorry for missed it. Will add in patch v2. Thanks a lot for the review.
- Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
- Refer to sound/soc/imx/imx-pcm-dma-mx2.c
Why?
Just with the reference of imx-pcm-dma-mx2.c code.
- iprtd->buf = (unsigned int *)substream->dma_buffer.area;
This cast looks incredibly suspicious...
Will remove the cast.
+static int snd_mxs_pcm_prepare(struct snd_pcm_substream *substream) +{
- return 0;
+}
Remove empty functions.
Will clean up it.
+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;
Why the random blank line?
Sorry for the typo, WIll remove.
+static struct platform_driver mxs_pcm_driver = {
- .driver = {
- .name = "mxs-pcm-audio",
- .owner = THIS_MODULE,
Indentation here is very odd...
Will fix in the next patch.
linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Sun, Jul 10, 2011 at 02:28:26PM +0800, Dong Aisheng wrote:
2011/7/9 Mark Brown broonie@opensource.wolfsonmicro.com:
On Fri, Jul 08, 2011 at 11:59:41PM +0800, Dong Aisheng wrote:
- Refer to sound/soc/imx/imx-pcm-dma-mx2.c
Why?
Just with the reference of imx-pcm-dma-mx2.c code.
What is the relevance of that specific code compared to other DMA drivers, though?
Signed-off-by: Dong Aisheng b29396@freescale.com --- sound/soc/mxs/mxs-saif.c | 524 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/mxs/mxs-saif.h | 124 +++++++++++ 2 files changed, 648 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..afc6e57 --- /dev/null +++ b/sound/soc/mxs/mxs-saif.c @@ -0,0 +1,524 @@ +/* + * 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/mx28.h> + +#include "mxs-saif.h" + +/* +* SAIF system clock configuration. +* Should only be called when port is inactive. +*/ +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_SYS_CLK: + clk_set_rate(saif->clk, freq); + clk_enable(saif->clk); + break; + default: + return -EINVAL; + } + return 0; +} + +/* + * SAIF Clock dividers + * Should only be called when port is inactive. + */ +static int mxs_saif_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, + int div_id, int div) +{ + u32 scr; + struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); + + scr = __raw_readl(saif->base + SAIF_CTRL); + scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE; + scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; + + switch (div_id) { + case MXS_SAIF_MCLK: + switch (div) { + case 32: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(4); + scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; + break; + case 64: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3); + scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; + break; + case 128: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2); + scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; + break; + case 256: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1); + scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; + break; + case 512: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0); + scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; + break; + case 48: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3); + scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE; + break; + case 96: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2); + scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE; + break; + case 192: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1); + scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE; + break; + case 384: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0); + scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + __raw_writel(scr, saif->base + SAIF_CTRL); + + return 0; +} + +/* + * SAIF DAI format configuration. + * Should only be called when port is inactive. + * Note: We don't use the I2S modes but instead manually configure the + * SAIF for I2S. + */ +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) + return 0; + + 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; + } + + /* 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 only supports + * master mode. + */ + 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; + case SND_SOC_DAIFMT_CBM_CFS: + break; + case SND_SOC_DAIFMT_CBS_CFM: + break; + case SND_SOC_DAIFMT_CBM_CFM: + return -EINVAL; + break; + } + + 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); + + 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; + + scr = __raw_readl(saif->base + SAIF_CTRL); + + /* cant change any parameters when SAIF is running */ + /* DAI data (word) size */ + 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; + } + + /* 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); + __raw_writel(BM_SAIF_CTRL_CLKGATE, + saif->base + SAIF_CTRL + MXS_CLR_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: + 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); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + 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_clkdiv = mxs_saif_set_dai_clkdiv, + .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); + + /* Reset */ + __raw_writel(BM_SAIF_CTRL_SFTRST, + saif->base + SAIF_CTRL + MXS_CLR_ADDR); + + /* enable MCLK output early since codec may need it */ + __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; +} + +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; + + if (saif->fifo_err_counter++ % 100 == 0) + printk(KERN_ERR "saif_irq SAIF_STAT %x SAIF_CTRL %x \ + fifo_errs=%d \n", + __raw_readl(saif->base + SAIF_STAT), + __raw_readl(saif->base + SAIF_CTRL), + saif->fifo_err_counter); + __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 = kzalloc(sizeof(*saif), GFP_KERNEL); + if (!saif) + return -ENOMEM; + + 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->fifo_err_counter = 0; + 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; + } + clk_set_rate(saif->clk, 12000000); + clk_enable(saif->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_disable(saif->clk); + 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_disable(saif->clk); + clk_put(saif->clk); + + kfree(saif); + + 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..dd26b37 --- /dev/null +++ b/sound/soc/mxs/mxs-saif.h @@ -0,0 +1,124 @@ +/* + * 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_SYS_CLK 0 +#define MXS_SAIF_MCLK 1 + +#include "mxs-pcm.h" + +struct mxs_saif { + struct clk *clk; + void __iomem *base; + int irq; + struct mxs_pcm_dma_params dma_param; + + struct platform_device *soc_platform_pdev; + u32 fifo_err_counter; +}; + +#endif
On Fri, Jul 08, 2011 at 11:59:42PM +0800, Dong Aisheng wrote:
Signed-off-by: Dong Aisheng b29396@freescale.com
sound/soc/mxs/mxs-saif.c | 524 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/mxs/mxs-saif.h | 124 +++++++++++ 2 files changed, 648 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..afc6e57 --- /dev/null +++ b/sound/soc/mxs/mxs-saif.c @@ -0,0 +1,524 @@ +/*
- 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/mx28.h>
Is this include really needed?
+#include "mxs-saif.h"
+/* +* SAIF system clock configuration. +* Should only be called when port is inactive. +*/ +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_SYS_CLK:
clk_set_rate(saif->clk, freq);
clk_enable(saif->clk);
This clk_enable is unbalanced. You enabled the clock in probe() already.
break;
- default:
return -EINVAL;
- }
- return 0;
+}
+/*
- SAIF Clock dividers
- Should only be called when port is inactive.
- */
+static int mxs_saif_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
+{
- u32 scr;
- struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
- scr = __raw_readl(saif->base + SAIF_CTRL);
- scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE;
- scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
- switch (div_id) {
- case MXS_SAIF_MCLK:
switch (div) {
case 32:
scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(4);
scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
break;
case 64:
scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3);
scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
break;
case 128:
scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2);
scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
break;
case 256:
scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1);
scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
break;
case 512:
scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0);
scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
break;
case 48:
scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3);
scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
break;
case 96:
scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2);
scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
break;
case 192:
scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1);
scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
break;
case 384:
scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0);
scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
break;
default:
return -EINVAL;
}
break;
- default:
return -EINVAL;
- }
- __raw_writel(scr, saif->base + SAIF_CTRL);
- return 0;
+}
+/*
- SAIF DAI format configuration.
- Should only be called when port is inactive.
- Note: We don't use the I2S modes but instead manually configure the
- SAIF for I2S.
- */
+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)
return 0;
Returning succesfully doesn't seem right here.
- 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;
- }
A question more for Liam and Mark: Would it make sense to add individual masks for the clock inversion and frame sync inversion? I code like the above in every driver, and I think this could become simpler.
- /*
* Note: We simply just support master mode since SAIF TX only supports
* master mode.
*/
When this is true...
- 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;
- case SND_SOC_DAIFMT_CBM_CFS:
break;
- case SND_SOC_DAIFMT_CBS_CFM:
break;
You should return an error here.
- case SND_SOC_DAIFMT_CBM_CFM:
return -EINVAL;
break;
- }
- 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);
- 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;
- scr = __raw_readl(saif->base + SAIF_CTRL);
- /* cant change any parameters when SAIF is running */
- /* DAI data (word) size */
- 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);
- __raw_writel(BM_SAIF_CTRL_CLKGATE,
saif->base + SAIF_CTRL + MXS_CLR_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:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
/*write a data to saif data register to trigger
the transfer*/
/* multiline comments * like this please */
__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);
break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
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_clkdiv = mxs_saif_set_dai_clkdiv,
- .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);
- /* Reset */
- __raw_writel(BM_SAIF_CTRL_SFTRST,
saif->base + SAIF_CTRL + MXS_CLR_ADDR);
- /* enable MCLK output early since codec may need it */
- __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;
+}
+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;
- if (saif->fifo_err_counter++ % 100 == 0)
printk(KERN_ERR "saif_irq SAIF_STAT %x SAIF_CTRL %x \
fifo_errs=%d \n",
__raw_readl(saif->base + SAIF_STAT),
__raw_readl(saif->base + SAIF_CTRL),
saif->fifo_err_counter);
- __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 = kzalloc(sizeof(*saif), GFP_KERNEL);
- if (!saif)
return -ENOMEM;
Consider using devm_kzalloc and friends, it makes the error path less error prone.
- 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->fifo_err_counter = 0;
- 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;
- }
- clk_set_rate(saif->clk, 12000000);
- clk_enable(saif->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_disable(saif->clk);
- 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_disable(saif->clk);
- clk_put(saif->clk);
- kfree(saif);
- 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..dd26b37 --- /dev/null +++ b/sound/soc/mxs/mxs-saif.h @@ -0,0 +1,124 @@ +/*
- 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_SYS_CLK 0 +#define MXS_SAIF_MCLK 1
+#include "mxs-pcm.h"
+struct mxs_saif {
- struct clk *clk;
- void __iomem *base;
- int irq;
- struct mxs_pcm_dma_params dma_param;
- struct platform_device *soc_platform_pdev;
- u32 fifo_err_counter;
+};
+#endif
1.7.0.4
/*write a data to saif data register to trigger
the transfer*/
/* multiline comments
- like this please
*/
/* * Actually, * like this :) */
2011/7/9 Sascha Hauer s.hauer@pengutronix.de:
On Fri, Jul 08, 2011 at 11:59:42PM +0800, Dong Aisheng wrote:
Signed-off-by: Dong Aisheng b29396@freescale.com
sound/soc/mxs/mxs-saif.c | 524 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/mxs/mxs-saif.h | 124 +++++++++++ 2 files changed, 648 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..afc6e57 --- /dev/null +++ b/sound/soc/mxs/mxs-saif.c @@ -0,0 +1,524 @@ +/*
- 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/mx28.h>
Is this include really needed?
Thanks a lot for your quick review. i checked and it's not needed. Including mxs.h would be ok for using MXS_CLR_ADDR.
+#include "mxs-saif.h"
+/* +* SAIF system clock configuration. +* Should only be called when port is inactive. +*/ +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_SYS_CLK:
- clk_set_rate(saif->clk, freq);
- clk_enable(saif->clk);
This clk_enable is unbalanced. You enabled the clock in probe() already.
Yes, will fix it.
- break;
- default:
- return -EINVAL;
- }
- return 0;
+}
+/*
- SAIF Clock dividers
- Should only be called when port is inactive.
- */
+static int mxs_saif_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
- int div_id, int div)
+{
- u32 scr;
- struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
- scr = __raw_readl(saif->base + SAIF_CTRL);
- scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE;
- scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
- switch (div_id) {
- case MXS_SAIF_MCLK:
- switch (div) {
- case 32:
- scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(4);
- scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
- break;
- case 64:
- scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3);
- scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
- break;
- case 128:
- scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2);
- scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
- break;
- case 256:
- scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1);
- scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
- break;
- case 512:
- scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0);
- scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
- break;
- case 48:
- scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3);
- scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
- break;
- case 96:
- scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2);
- scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
- break;
- case 192:
- scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1);
- scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
- break;
- case 384:
- scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0);
- scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
- break;
- default:
- return -EINVAL;
- }
- break;
- default:
- return -EINVAL;
- }
- __raw_writel(scr, saif->base + SAIF_CTRL);
- return 0;
+}
+/*
- SAIF DAI format configuration.
- Should only be called when port is inactive.
- Note: We don't use the I2S modes but instead manually configure the
- SAIF for I2S.
- */
+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)
- return 0;
Returning succesfully doesn't seem right here.
Will replace with -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;
Will add.
- }
- /* 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;
- }
A question more for Liam and Mark: Would it make sense to add individual masks for the clock inversion and frame sync inversion? I code like the above in every driver, and I think this could become simpler.
- /*
- * Note: We simply just support master mode since SAIF TX only supports
- * master mode.
- */
When this is true...
It's hw limitation. Defined in MX28 spec 35.2.1. i quote it as follows; For transmit, BITCLK and LRCLK are always driven by the SAIF. ... Coniguring the SAIF for transmit makes it the master by default. ....
- 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;
- case SND_SOC_DAIFMT_CBM_CFS:
- break;
- case SND_SOC_DAIFMT_CBS_CFM:
- break;
You should return an error here.
Will add.
- case SND_SOC_DAIFMT_CBM_CFM:
- return -EINVAL;
- break;
- }
- 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);
- 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;
- scr = __raw_readl(saif->base + SAIF_CTRL);
- /* cant change any parameters when SAIF is running */
- /* DAI data (word) size */
- 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;
Will add.
- }
- /* 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);
- __raw_writel(BM_SAIF_CTRL_CLKGATE,
- saif->base + SAIF_CTRL + MXS_CLR_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:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- /*write a data to saif data register to trigger
- the transfer*/
/* multiline comments * like this please */
Got it.
- __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);
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- 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_clkdiv = mxs_saif_set_dai_clkdiv,
- .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);
- /* Reset */
- __raw_writel(BM_SAIF_CTRL_SFTRST,
- saif->base + SAIF_CTRL + MXS_CLR_ADDR);
- /* enable MCLK output early since codec may need it */
- __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;
+}
+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;
- if (saif->fifo_err_counter++ % 100 == 0)
- printk(KERN_ERR "saif_irq SAIF_STAT %x SAIF_CTRL %x \
- fifo_errs=%d \n",
- __raw_readl(saif->base + SAIF_STAT),
- __raw_readl(saif->base + SAIF_CTRL),
- saif->fifo_err_counter);
- __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 = kzalloc(sizeof(*saif), GFP_KERNEL);
- if (!saif)
- return -ENOMEM;
Consider using devm_kzalloc and friends, it makes the error path less error prone.
Will use it. Thanks for the suggestion.
- 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->fifo_err_counter = 0;
- 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;
- }
- clk_set_rate(saif->clk, 12000000);
- clk_enable(saif->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_disable(saif->clk);
- 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_disable(saif->clk);
- clk_put(saif->clk);
- kfree(saif);
- 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..dd26b37 --- /dev/null +++ b/sound/soc/mxs/mxs-saif.h @@ -0,0 +1,124 @@ +/*
- 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_SYS_CLK 0 +#define MXS_SAIF_MCLK 1
+#include "mxs-pcm.h"
+struct mxs_saif {
- struct clk *clk;
- void __iomem *base;
- int irq;
- struct mxs_pcm_dma_params dma_param;
- struct platform_device *soc_platform_pdev;
- u32 fifo_err_counter;
+};
+#endif
1.7.0.4
-- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Fri, Jul 08, 2011 at 11:59:42PM +0800, Dong Aisheng wrote:
Signed-off-by: Dong Aisheng b29396@freescale.com
Again, *always* CC maintainers on patches.
+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_SYS_CLK:
clk_set_rate(saif->clk, freq);
clk_enable(saif->clk);
How would one turn this clock off?
- SAIF Clock dividers
- Should only be called when port is inactive.
- */
+static int mxs_saif_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
+{
- u32 scr;
- struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
- scr = __raw_readl(saif->base + SAIF_CTRL);
- scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE;
- scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
- switch (div_id) {
- case MXS_SAIF_MCLK:
switch (div) {
case 32:
No, this stuff should all be figured out automatically by the driver - the user shouldn't have to manually set the ratio, especially if they've already set the master clock.
- 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;
- }
This should return an error if the parameters are invalid. The rest of the code has many other instances of this missing check.
- /*
* Note: We simply just support master mode since SAIF TX only supports
* master mode.
*/
- 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;
- case SND_SOC_DAIFMT_CBM_CFS:
break;
- case SND_SOC_DAIFMT_CBS_CFM:
break;
This is clearly wrong, you're doing exactly the same thing for two different inputs.
+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);
- return 0;
+}
Remove empty functions.
+/*
- 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)
The requirement for the port to be inactive is totally unreasonable, especially given that the params are configured by applications and you support simultaneous playback and record.
+static irqreturn_t mxs_saif_irq(int irq, void *dev_id) +{
- struct mxs_saif *saif = dev_id;
- if (saif->fifo_err_counter++ % 100 == 0)
The rate limit looks awfully suspicious...
printk(KERN_ERR "saif_irq SAIF_STAT %x SAIF_CTRL %x \
fifo_errs=%d \n",
__raw_readl(saif->base + SAIF_STAT),
__raw_readl(saif->base + SAIF_CTRL),
saif->fifo_err_counter);
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;
You're not actually checking that the relevant interrupt fired...
- 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;
- }
- clk_set_rate(saif->clk, 12000000);
- clk_enable(saif->clk);
How did you pick this clock rate and why does it need to be set? It's not an obvious audio rate...
2011/7/9 Mark Brown broonie@opensource.wolfsonmicro.com:
On Fri, Jul 08, 2011 at 11:59:42PM +0800, Dong Aisheng wrote:
Signed-off-by: Dong Aisheng b29396@freescale.com
Again, *always* CC maintainers on patches.
Will add in the updated patch.
+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_SYS_CLK:
- clk_set_rate(saif->clk, freq);
- clk_enable(saif->clk);
How would one turn this clock off?
Currenty simply enable clock always. The problem is that the codec may use the MCLK supplied by SAIF as its system clock for normal operations such as i2c r/w. If we disable it after playback or capture. The automatic dapm operations of codec may fail on i2c r/w. Will check if dapm has any machnism to avoid this. Can you share your experience ont this issue?
- SAIF Clock dividers
- Should only be called when port is inactive.
- */
+static int mxs_saif_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
- int div_id, int div)
+{
- u32 scr;
- struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
- scr = __raw_readl(saif->base + SAIF_CTRL);
- scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE;
- scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
- switch (div_id) {
- case MXS_SAIF_MCLK:
- switch (div) {
- case 32:
No, this stuff should all be figured out automatically by the driver - the user shouldn't have to manually set the ratio, especially if they've already set the master clock.
Yes, that's a good point and i will try to do it in the updated patch.
- 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;
- }
This should return an error if the parameters are invalid. The rest of the code has many other instances of this missing check.
Will add them.
- /*
- * Note: We simply just support master mode since SAIF TX only supports
- * master mode.
- */
- 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;
- case SND_SOC_DAIFMT_CBM_CFS:
- break;
- case SND_SOC_DAIFMT_CBS_CFM:
- break;
This is clearly wrong, you're doing exactly the same thing for two different inputs.
Will fix in next patch.
+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);
- return 0;
+}
Remove empty functions.
This is to set dma param.
+/*
- 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)
The requirement for the port to be inactive is totally unreasonable, especially given that the params are configured by applications and you support simultaneous playback and record.
Yes, i will remove it. Finally we will support simultaneous playback and record.
+static irqreturn_t mxs_saif_irq(int irq, void *dev_id) +{
- struct mxs_saif *saif = dev_id;
- if (saif->fifo_err_counter++ % 100 == 0)
The rate limit looks awfully suspicious...
Originally it's just for printing less error messages when the issue happens. I could remove the rate limit.
- printk(KERN_ERR "saif_irq SAIF_STAT %x SAIF_CTRL %x \
- fifo_errs=%d \n",
- __raw_readl(saif->base + SAIF_STAT),
- __raw_readl(saif->base + SAIF_CTRL),
- saif->fifo_err_counter);
dev_err()
Will change.
- __raw_writel(BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ |
- BM_SAIF_STAT_FIFO_OVERFLOW_IRQ,
- saif->base + SAIF_STAT + MXS_CLR_ADDR);
- return IRQ_HANDLED;
You're not actually checking that the relevant interrupt fired...
I will add the checking for different errors.
- 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;
- }
- clk_set_rate(saif->clk, 12000000);
- clk_enable(saif->clk);
How did you pick this clock rate and why does it need to be set? It's not an obvious audio rate...
It's initial clock rate for normal operations of codecs based on MCLK. We found the default MCLK ouput(only about 7.3Khz) can not work for codecs like sgtl5000 for its initialization since its clock range is 8Mhz~27Mhz. So we set a common working clock 12Mhz here. Maybe i should try to set it elsewhere or just set it via platform data.
linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Sun, Jul 10, 2011 at 04:17:13PM +0800, Dong Aisheng wrote:
2011/7/9 Mark Brown broonie@opensource.wolfsonmicro.com:
On Fri, Jul 08, 2011 at 11:59:42PM +0800, Dong Aisheng wrote:
- switch (clk_id) {
- case MXS_SAIF_SYS_CLK:
- clk_set_rate(saif->clk, freq);
- clk_enable(saif->clk);
How would one turn this clock off?
Currenty simply enable clock always.
If that's what you're doing you should enable the clock when the device is probed and disable it when the device is removed. These repeated enables will mean that you're constantly leaking references.
The problem is that the codec may use the MCLK supplied by SAIF as its system clock for normal operations such as i2c r/w.
That may be true for your particular system but it is not going to be true in general - most devices can handle having their MCLK removed.
If we disable it after playback or capture. The automatic dapm operations of codec may fail on i2c r/w. Will check if dapm has any machnism to avoid this. Can you share your experience ont this issue?
You should ideally let the machine driver or other system integration code control if the clock is constantly enabled, or at the very least control it from the probe and remove.
+static irqreturn_t mxs_saif_irq(int irq, void *dev_id) +{
- struct mxs_saif *saif = dev_id;
- if (saif->fifo_err_counter++ % 100 == 0)
The rate limit looks awfully suspicious...
Originally it's just for printing less error messages when the issue happens. I could remove the rate limit.
Please do so.
- clk_set_rate(saif->clk, 12000000);
- clk_enable(saif->clk);
How did you pick this clock rate and why does it need to be set? It's not an obvious audio rate...
It's initial clock rate for normal operations of codecs based on MCLK. We found the default MCLK ouput(only about 7.3Khz) can not work for codecs like sgtl5000 for its initialization since its clock range is 8Mhz~27Mhz. So we set a common working clock 12Mhz here. Maybe i should try to set it elsewhere or just set it via platform data.
This is the same issue as the above one with repeated enables? You should delegate the rate selection and ideally also the enable control to the machine driver, it can set something in its probe() function.
2011/7/10 Mark Brown broonie@opensource.wolfsonmicro.com:
On Sun, Jul 10, 2011 at 04:17:13PM +0800, Dong Aisheng wrote:
2011/7/9 Mark Brown broonie@opensource.wolfsonmicro.com:
On Fri, Jul 08, 2011 at 11:59:42PM +0800, Dong Aisheng wrote:
- switch (clk_id) {
- case MXS_SAIF_SYS_CLK:
- clk_set_rate(saif->clk, freq);
- clk_enable(saif->clk);
How would one turn this clock off?
Currenty simply enable clock always.
If that's what you're doing you should enable the clock when the device is probed and disable it when the device is removed. These repeated enables will mean that you're constantly leaking references.
Yes, i noticed this issue.
The problem is that the codec may use the MCLK supplied by SAIF as its system clock for normal operations such as i2c r/w.
That may be true for your particular system but it is not going to be true in general - most devices can handle having their MCLK removed.
Yes, if i cover that, that may make driver a little complicated. Should i do that now or as the next work? Also i still do not have such type of codecs integrated on hand.
If we disable it after playback or capture. The automatic dapm operations of codec may fail on i2c r/w. Will check if dapm has any machnism to avoid this. Can you share your experience ont this issue?
You should ideally let the machine driver or other system integration code control if the clock is constantly enabled, or at the very least control it from the probe and remove.
Thanks for the suggestions. I will try them.
+static irqreturn_t mxs_saif_irq(int irq, void *dev_id) +{
- struct mxs_saif *saif = dev_id;
- if (saif->fifo_err_counter++ % 100 == 0)
The rate limit looks awfully suspicious...
Originally it's just for printing less error messages when the issue happens. I could remove the rate limit.
Please do so.
Got it.
- clk_set_rate(saif->clk, 12000000);
- clk_enable(saif->clk);
How did you pick this clock rate and why does it need to be set? It's not an obvious audio rate...
It's initial clock rate for normal operations of codecs based on MCLK. We found the default MCLK ouput(only about 7.3Khz) can not work for codecs like sgtl5000 for its initialization since its clock range is 8Mhz~27Mhz. So we set a common working clock 12Mhz here. Maybe i should try to set it elsewhere or just set it via platform data.
This is the same issue as the above one with repeated enables? You should delegate the rate selection and ideally also the enable control to the machine driver, it can set something in its probe() function.
Not the same one. The repeated enables is a bug.(Will fix) Here we just need set a init proper clock rate of MCLK for codec firstly. (Codec's initialization(i2c r/w) in probe function may need it). I would try your suggestion to let machine driver to set it.
On Sun, Jul 10, 2011 at 04:58:10PM +0800, Dong Aisheng wrote:
2011/7/10 Mark Brown broonie@opensource.wolfsonmicro.com:
On Sun, Jul 10, 2011 at 04:17:13PM +0800, Dong Aisheng wrote:
The problem is that the codec may use the MCLK supplied by SAIF as its system clock for normal operations such as i2c r/w.
That may be true for your particular system but it is not going to be true in general - most devices can handle having their MCLK removed.
Yes, if i cover that, that may make driver a little complicated. Should i do that now or as the next work?
It should simply be a case of adding handling of setting the sysclk to 0 (this is how ASoC indicates clocks should be disabled). The machine driver is then responsible for making sure it does the refcounting properly, and for your system where the clock is enabled all the time that's just a case of enabling on startup and disabling on remove.
Also i still do not have such type of codecs integrated on hand.
That's fine, audio hardware is so flexible that it's unreasonable to expect that all possible systems have been tested. So long as the code looks sensible it should be fine and if there are problems people can always come back and fix them.
This is the same issue as the above one with repeated enables? You should delegate the rate selection and ideally also the enable control to the machine driver, it can set something in its probe() function.
Not the same one. The repeated enables is a bug.(Will fix)
Yes, but both are a result of the requirements of your CODEC? Just trying to understand if the I2S controller needs this itself or if it's only for the CODEC.
2011/7/10 Mark Brown broonie@opensource.wolfsonmicro.com:
On Sun, Jul 10, 2011 at 04:58:10PM +0800, Dong Aisheng wrote:
2011/7/10 Mark Brown broonie@opensource.wolfsonmicro.com:
On Sun, Jul 10, 2011 at 04:17:13PM +0800, Dong Aisheng wrote:
The problem is that the codec may use the MCLK supplied by SAIF as its system clock for normal operations such as i2c r/w.
That may be true for your particular system but it is not going to be true in general - most devices can handle having their MCLK removed.
Yes, if i cover that, that may make driver a little complicated. Should i do that now or as the next work?
It should simply be a case of adding handling of setting the sysclk to 0 (this is how ASoC indicates clocks should be disabled). The machine driver is then responsible for making sure it does the refcounting properly, and for your system where the clock is enabled all the time that's just a case of enabling on startup and disabling on remove.
Ok, i will take account of that. Thanks for your suggestion.
Also i still do not have such type of codecs integrated on hand.
That's fine, audio hardware is so flexible that it's unreasonable to expect that all possible systems have been tested. So long as the code looks sensible it should be fine and if there are problems people can always come back and fix them.
OK.
This is the same issue as the above one with repeated enables? You should delegate the rate selection and ideally also the enable control to the machine driver, it can set something in its probe() function.
Not the same one. The repeated enables is a bug.(Will fix)
Yes, but both are a result of the requirements of your CODEC? Just trying to understand if the I2S controller needs this itself or if it's only for the CODEC.
Based on my understanding, only for codec. I will double check it with IC man.
Regards Dong Aisheng
Also i still do not have such type of codecs integrated on hand.
That's fine, audio hardware is so flexible that it's unreasonable to expect that all possible systems have been tested. So long as the code looks sensible it should be fine and if there are problems people can always come back and fix them.
I will get a custom board with a different codec in about a month and will happily do tests.
-----Original Message----- From: Wolfram Sang [mailto:w.sang@pengutronix.de] Sent: Monday, July 11, 2011 6:00 PM To: Mark Brown Cc: Dong Aisheng; alsa-devel@alsa-project.org; Dong Aisheng-B29396; s.hauer@pengutronix.de; linux-arm-kernel@lists.infradead.org; lrg@ti.com Subject: Re: [PATCH 02/10] ASoc: mxs: add mxs-saif driver
Also i still do not have such type of codecs integrated on hand.
That's fine, audio hardware is so flexible that it's unreasonable to expect that all possible systems have been tested. So long as the code looks sensible it should be fine and if there are problems people can always come back and fix them.
I will get a custom board with a different codec in about a month and will happily do tests.
That would be good. Thanks a lot.
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 --- arch/arm/mach-mxs/include/mach/audio.h | 18 +++ sound/soc/mxs/mxs-sgtl5000.c | 181 ++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-mxs/include/mach/audio.h b/arch/arm/mach-mxs/include/mach/audio.h new file mode 100644 index 0000000..2f543d2 --- /dev/null +++ b/arch/arm/mach-mxs/include/mach/audio.h @@ -0,0 +1,18 @@ +/* + * 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. + */ + +#ifndef __MACH_MXS_SAIF_H__ +#define __MACH_MXS_SAIF_H__ + +struct mxs_audio_platform_data { + int sysclk; + + int (*init) (void); /* board specific init */ + int (*finit) (void); /* board specific finit */ +}; +#endif /* __MACH_MXS_SAIF_H__ */ diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c new file mode 100644 index 0000000..7ae7c86 --- /dev/null +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -0,0 +1,181 @@ +/* + * 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 <mach/audio.h> +#include "../codecs/sgtl5000.h" +#include "mxs-saif.h" + +static struct mxs_sgtl5000_priv { + int sysclk; + struct platform_device *pdev; +} card_priv; + +static struct snd_soc_card mxs_sgtl5000; +static struct platform_device *mxs_sgtl5000_snd_device; + +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; + int ret; + + /* The SAIF clock should be either 512*fs or 384*fs */ + card_priv.sysclk = 512 * rate; + ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_SYS_CLK, + card_priv.sysclk, + SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + /* set SGTL5000_SYSCLK as 256*fs to support 96k sample rate */ + snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, 256 * rate, 0); + + /* The MCLK output rate is 256*fs */ + snd_soc_dai_set_clkdiv(cpu_dai, MXS_SAIF_MCLK, 256); + + /* 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 < 0) + return ret; + + /* set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, dai_format); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops mxs_sgtl5000_hifi_ops = { + .hw_params = mxs_sgtl5000_hw_params, +}; + +static int mxs_sgtl5000_soc_init(struct snd_soc_pcm_runtime *rtd) +{ + /* TBD: add dapm widgets */ + + return 0; +} + +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", + .init = mxs_sgtl5000_soc_init, + .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 mxs_audio_platform_data *plat = pdev->dev.platform_data; + int ret; + + card_priv.pdev = pdev; + + ret = -EINVAL; + if (plat && plat->init && plat->init()) + return ret; + + return 0; +} + +static int mxs_sgtl5000_remove(struct platform_device *pdev) +{ + struct mxs_audio_platform_data *plat = pdev->dev.platform_data; + + if (plat && plat->finit) + plat->finit(); + + return 0; +} + +static struct platform_driver mxs_sgtl5000_audio_driver = { + .probe = mxs_sgtl5000_probe, + .remove = mxs_sgtl5000_remove, + .driver = { + .name = "mxs-sgtl5000", + }, +}; + +static int __init mxs_sgtl5000_init(void) +{ + int ret; + + ret = platform_driver_register(&mxs_sgtl5000_audio_driver); + if (ret) + return ret; + + mxs_sgtl5000_snd_device = platform_device_alloc("soc-audio", -1); + if (!mxs_sgtl5000_snd_device) + return -ENOMEM; + + platform_set_drvdata(mxs_sgtl5000_snd_device, &mxs_sgtl5000); + + ret = platform_device_add(mxs_sgtl5000_snd_device); + if (ret) { + printk(KERN_ERR "ASoC: Platform device allocation failed\n"); + platform_device_put(mxs_sgtl5000_snd_device); + } + + return ret; +} + +static void __exit mxs_sgtl5000_exit(void) +{ + platform_driver_unregister(&mxs_sgtl5000_audio_driver); + platform_device_unregister(mxs_sgtl5000_snd_device); +} + +late_initcall(mxs_sgtl5000_init); +module_exit(mxs_sgtl5000_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXS ALSA SoC Machine driver"); +MODULE_LICENSE("GPL");
On Fri, Jul 08, 2011 at 11:59:43PM +0800, Dong Aisheng wrote:
The driver only supports playback firstly.
Once more, *always* CC maintainers.
+struct mxs_audio_platform_data {
- int sysclk;
- int (*init) (void); /* board specific init */
- int (*finit) (void); /* board specific finit */
Eh? Your machine driver is already entirely board specific...
- /* The SAIF clock should be either 512*fs or 384*fs */
- card_priv.sysclk = 512 * rate;
- ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_SYS_CLK,
card_priv.sysclk,
SND_SOC_CLOCK_OUT);
Why are you storing the sysclk? You never reference it again and you're using different sysclks for everything in the system.
- /* set SGTL5000_SYSCLK as 256*fs to support 96k sample rate */
- snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, 256 * rate, 0);
- /* The MCLK output rate is 256*fs */
- snd_soc_dai_set_clkdiv(cpu_dai, MXS_SAIF_MCLK, 256);
Why are you not checking errors on these?
+static int mxs_sgtl5000_soc_init(struct snd_soc_pcm_runtime *rtd) +{
- /* TBD: add dapm widgets */
- return 0;
+}
Remove empty functions.
+static int __devinit mxs_sgtl5000_probe(struct platform_device *pdev) +{
- struct mxs_audio_platform_data *plat = pdev->dev.platform_data;
- int ret;
- card_priv.pdev = pdev;
- ret = -EINVAL;
- if (plat && plat->init && plat->init())
return ret;
You're not calling snd_soc_register_card()...
- mxs_sgtl5000_snd_device = platform_device_alloc("soc-audio", -1);
- if (!mxs_sgtl5000_snd_device)
return -ENOMEM;
...and you're using both a platform device and soc-audio to instantate which is a bit confused. New code should not be using soc-audio.
2011/7/9 Mark Brown broonie@opensource.wolfsonmicro.com:
On Fri, Jul 08, 2011 at 11:59:43PM +0800, Dong Aisheng wrote:
The driver only supports playback firstly.
Once more, *always* CC maintainers.
Got it.
+struct mxs_audio_platform_data {
- int sysclk;
- int (*init) (void); /* board specific init */
- int (*finit) (void); /* board specific finit */
Eh? Your machine driver is already entirely board specific...
Still not. It's originally used for some board's codec that do not need SAIF to provide mclk. I will remove it if we still do not need it to make the things simply.
- /* The SAIF clock should be either 512*fs or 384*fs */
- card_priv.sysclk = 512 * rate;
- ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_SYS_CLK,
- card_priv.sysclk,
- SND_SOC_CLOCK_OUT);
Why are you storing the sysclk? You never reference it again and you're using different sysclks for everything in the system.
Yes, it may not really need to store it in this version of code. I will change this part of clock setting in the next patch.
- /* set SGTL5000_SYSCLK as 256*fs to support 96k sample rate */
- snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, 256 * rate, 0);
- /* The MCLK output rate is 256*fs */
- snd_soc_dai_set_clkdiv(cpu_dai, MXS_SAIF_MCLK, 256);
Why are you not checking errors on these?
Will add it.
+static int mxs_sgtl5000_soc_init(struct snd_soc_pcm_runtime *rtd) +{
- /* TBD: add dapm widgets */
- return 0;
+}
Remove empty functions.
Will do that.
+static int __devinit mxs_sgtl5000_probe(struct platform_device *pdev) +{
- struct mxs_audio_platform_data *plat = pdev->dev.platform_data;
- int ret;
- card_priv.pdev = pdev;
- ret = -EINVAL;
- if (plat && plat->init && plat->init())
- return ret;
You're not calling snd_soc_register_card()...
- mxs_sgtl5000_snd_device = platform_device_alloc("soc-audio", -1);
- if (!mxs_sgtl5000_snd_device)
- return -ENOMEM;
...and you're using both a platform device and soc-audio to instantate which is a bit confused. New code should not be using soc-audio.
Thanks for reminder. I will change this part of code to following the new rule.
linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Sun, Jul 10, 2011 at 04:36:44PM +0800, Dong Aisheng wrote:
2011/7/9 Mark Brown broonie@opensource.wolfsonmicro.com:
On Fri, Jul 08, 2011 at 11:59:43PM +0800, Dong Aisheng wrote:
- int (*init) (void); /* board specific init */
- int (*finit) (void); /* board specific finit */
Eh? Your machine driver is already entirely board specific...
Still not.
It's originally used for some board's codec that do not need SAIF to provide mclk. I will remove it if we still do not need it to make the things simply.
If you've got different CODECs you almost certainly want to have different machine drivers for the relevant boards.
Signed-off-by: Dong Aisheng b29396@freescale.com --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/mxs/Kconfig | 20 ++++++++++++++++++++ sound/soc/mxs/Makefile | 10 ++++++++++ 4 files changed, 32 insertions(+), 0 deletions(-)
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 8224db5..aff2592 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -57,6 +57,7 @@ source "sound/soc/s6000/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" +source "sound/soc/mxs/Kconfig"
# Supported codecs source "sound/soc/codecs/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 1ed61c5..2023771 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_SND_SOC) += s6000/ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ +obj-$(CONFIG_SND_SOC) += mxs/ diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig new file mode 100644 index 0000000..675e27c --- /dev/null +++ b/sound/soc/mxs/Kconfig @@ -0,0 +1,20 @@ +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" +# depends on MACH_MX28 + 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
On Fri, Jul 08, 2011 at 11:59:44PM +0800, Dong Aisheng wrote:
Signed-off-by: Dong Aisheng b29396@freescale.com
*Always* CC maintainers on patches.
@@ -57,6 +57,7 @@ source "sound/soc/s6000/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" +source "sound/soc/mxs/Kconfig"
Keep all these files sorted.
2011/7/9 Mark Brown broonie@opensource.wolfsonmicro.com:
On Fri, Jul 08, 2011 at 11:59:44PM +0800, Dong Aisheng wrote:
Signed-off-by: Dong Aisheng b29396@freescale.com
*Always* CC maintainers on patches.
@@ -57,6 +57,7 @@ source "sound/soc/s6000/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" +source "sound/soc/mxs/Kconfig"
Keep all these files sorted.
Thanks for reminder. Will fix in the updated v2 patch soon.
linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Set pll0 as parent.
Signed-off-by: Dong Aisheng b29396@freescale.com --- 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 --- arch/arm/mach-mxs/Kconfig | 1 + arch/arm/mach-mxs/devices-mx28.h | 7 +++ 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, 104 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..f0fa46f 100644 --- a/arch/arm/mach-mxs/devices-mx28.h +++ b/arch/arm/mach-mxs/devices-mx28.h @@ -11,6 +11,7 @@ #include <mach/mx28.h> #include <mach/devices-common.h> #include <mach/mxsfb.h> +#include <mach/audio.h>
extern const struct amba_device mx28_duart_device __initconst; #define mx28_add_duart() \ @@ -45,3 +46,9 @@ 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]) + +struct platform_device *__init mx28_add_audio( + const struct mxs_audio_platform_data *pdata); 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");
Signed-off-by: Dong Aisheng b29396@freescale.com --- 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);
Signed-off-by: Dong Aisheng b29396@freescale.com --- 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 --- 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 --- 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; };
participants (6)
-
Dong Aisheng
-
Dong Aisheng
-
Dong Aisheng-B29396
-
Mark Brown
-
Sascha Hauer
-
Wolfram Sang