![Russell King](/assets/img/avatar_default.png)
This code sequence is unsafe in modules: static u64 mask = DMA_BIT_MASK(something); ... if (!dev->dma_mask) dev->dma_mask = &mask; as if a module is reloaded, the mask will be pointing at the original module's mask address, and this can lead to oopses. Moreover, they all follow this with: if (!dev->coherent_dma_mask) dev->coherent_dma_mask = mask; where 'mask' is the same value as the statically defined mask, and this bypasses the architecture's check on whether the DMA mask is possible. Fix these issues by using the new dma_coerce_coherent_and_mask() function. Acked-by: Mark Brown <broonie@linaro.org> Acked-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
157 lines
3.8 KiB
C
157 lines
3.8 KiB
C
/*
|
|
* linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip
|
|
*
|
|
* Author: Nicolas Pitre
|
|
* Created: Nov 30, 2004
|
|
* Copyright: (C) 2004 MontaVista Software, Inc.
|
|
*
|
|
* 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/dma-mapping.h>
|
|
#include <linux/module.h>
|
|
#include <linux/dmaengine.h>
|
|
#include <linux/of.h>
|
|
|
|
#include <sound/core.h>
|
|
#include <sound/soc.h>
|
|
#include <sound/pxa2xx-lib.h>
|
|
#include <sound/dmaengine_pcm.h>
|
|
|
|
#include "../../arm/pxa2xx-pcm.h"
|
|
|
|
static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *params)
|
|
{
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
struct pxa2xx_runtime_data *prtd = runtime->private_data;
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
struct snd_dmaengine_dai_dma_data *dma;
|
|
int ret;
|
|
|
|
dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
|
|
|
/* return if this is a bufferless transfer e.g.
|
|
* codec <--> BT codec or GSM modem -- lg FIXME */
|
|
if (!dma)
|
|
return 0;
|
|
|
|
/* this may get called several times by oss emulation
|
|
* with different params */
|
|
if (prtd->params == NULL) {
|
|
prtd->params = dma;
|
|
ret = pxa_request_dma("name", DMA_PRIO_LOW,
|
|
pxa2xx_pcm_dma_irq, substream);
|
|
if (ret < 0)
|
|
return ret;
|
|
prtd->dma_ch = ret;
|
|
} else if (prtd->params != dma) {
|
|
pxa_free_dma(prtd->dma_ch);
|
|
prtd->params = dma;
|
|
ret = pxa_request_dma("name", DMA_PRIO_LOW,
|
|
pxa2xx_pcm_dma_irq, substream);
|
|
if (ret < 0)
|
|
return ret;
|
|
prtd->dma_ch = ret;
|
|
}
|
|
|
|
return __pxa2xx_pcm_hw_params(substream, params);
|
|
}
|
|
|
|
static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
|
|
{
|
|
struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
|
|
|
|
__pxa2xx_pcm_hw_free(substream);
|
|
|
|
if (prtd->dma_ch >= 0) {
|
|
pxa_free_dma(prtd->dma_ch);
|
|
prtd->dma_ch = -1;
|
|
prtd->params = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct snd_pcm_ops pxa2xx_pcm_ops = {
|
|
.open = __pxa2xx_pcm_open,
|
|
.close = __pxa2xx_pcm_close,
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
.hw_params = pxa2xx_pcm_hw_params,
|
|
.hw_free = pxa2xx_pcm_hw_free,
|
|
.prepare = __pxa2xx_pcm_prepare,
|
|
.trigger = pxa2xx_pcm_trigger,
|
|
.pointer = pxa2xx_pcm_pointer,
|
|
.mmap = pxa2xx_pcm_mmap,
|
|
};
|
|
|
|
static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
struct snd_card *card = rtd->card->snd_card;
|
|
struct snd_pcm *pcm = rtd->pcm;
|
|
int ret;
|
|
|
|
ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
|
|
ret = pxa2xx_pcm_preallocate_dma_buffer(pcm,
|
|
SNDRV_PCM_STREAM_PLAYBACK);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
|
|
ret = pxa2xx_pcm_preallocate_dma_buffer(pcm,
|
|
SNDRV_PCM_STREAM_CAPTURE);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static struct snd_soc_platform_driver pxa2xx_soc_platform = {
|
|
.ops = &pxa2xx_pcm_ops,
|
|
.pcm_new = pxa2xx_soc_pcm_new,
|
|
.pcm_free = pxa2xx_pcm_free_dma_buffers,
|
|
};
|
|
|
|
static int pxa2xx_soc_platform_probe(struct platform_device *pdev)
|
|
{
|
|
return snd_soc_register_platform(&pdev->dev, &pxa2xx_soc_platform);
|
|
}
|
|
|
|
static int pxa2xx_soc_platform_remove(struct platform_device *pdev)
|
|
{
|
|
snd_soc_unregister_platform(&pdev->dev);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id snd_soc_pxa_audio_match[] = {
|
|
{ .compatible = "mrvl,pxa-pcm-audio" },
|
|
{ }
|
|
};
|
|
#endif
|
|
|
|
static struct platform_driver pxa_pcm_driver = {
|
|
.driver = {
|
|
.name = "pxa-pcm-audio",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(snd_soc_pxa_audio_match),
|
|
},
|
|
|
|
.probe = pxa2xx_soc_platform_probe,
|
|
.remove = pxa2xx_soc_platform_remove,
|
|
};
|
|
|
|
module_platform_driver(pxa_pcm_driver);
|
|
|
|
MODULE_AUTHOR("Nicolas Pitre");
|
|
MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module");
|
|
MODULE_LICENSE("GPL");
|