Linux Audio

Check our new training course

Loading...
  1/*
  2 * linux/sound/soc/pxa/mmp-pcm.c
  3 *
  4 * Copyright (C) 2011 Marvell International Ltd.
  5 *
  6 * This program is free software; you can redistribute it and/or modify
  7 * it under the terms of the GNU General Public License as published by
  8 * the Free Software Foundation; either version 2 of the License, or
  9 * (at your option) any later version.
 10 *
 11 */
 12#include <linux/module.h>
 13#include <linux/init.h>
 14#include <linux/platform_device.h>
 15#include <linux/slab.h>
 16#include <linux/dma-mapping.h>
 17#include <linux/dmaengine.h>
 18#include <linux/platform_data/dma-mmp_tdma.h>
 19#include <linux/platform_data/mmp_audio.h>
 20
 21#include <sound/pxa2xx-lib.h>
 22#include <sound/core.h>
 23#include <sound/pcm.h>
 24#include <sound/pcm_params.h>
 25#include <sound/soc.h>
 26#include <sound/dmaengine_pcm.h>
 27
 
 
 28struct mmp_dma_data {
 29	int ssp_id;
 30	struct resource *dma_res;
 31};
 32
 33#define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP |	\
 34		SNDRV_PCM_INFO_MMAP_VALID |	\
 35		SNDRV_PCM_INFO_INTERLEAVED |	\
 36		SNDRV_PCM_INFO_PAUSE |		\
 37		SNDRV_PCM_INFO_RESUME |		\
 38		SNDRV_PCM_INFO_NO_PERIOD_WAKEUP)
 39
 40static struct snd_pcm_hardware mmp_pcm_hardware[] = {
 41	{
 42		.info			= MMP_PCM_INFO,
 43		.period_bytes_min	= 1024,
 44		.period_bytes_max	= 2048,
 45		.periods_min		= 2,
 46		.periods_max		= 32,
 47		.buffer_bytes_max	= 4096,
 48		.fifo_size		= 32,
 49	},
 50	{
 51		.info			= MMP_PCM_INFO,
 52		.period_bytes_min	= 1024,
 53		.period_bytes_max	= 2048,
 54		.periods_min		= 2,
 55		.periods_max		= 32,
 56		.buffer_bytes_max	= 4096,
 57		.fifo_size		= 32,
 58	},
 59};
 60
 61static int mmp_pcm_hw_params(struct snd_pcm_substream *substream,
 62			      struct snd_pcm_hw_params *params)
 63{
 64	struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
 65	struct dma_slave_config slave_config;
 66	int ret;
 67
 68	ret =
 69	    snd_dmaengine_pcm_prepare_slave_config(substream, params,
 70						   &slave_config);
 71	if (ret)
 72		return ret;
 73
 74	ret = dmaengine_slave_config(chan, &slave_config);
 75	if (ret)
 76		return ret;
 77
 78	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 79
 80	return 0;
 81}
 82
 83static bool filter(struct dma_chan *chan, void *param)
 84{
 85	struct mmp_dma_data *dma_data = param;
 86	bool found = false;
 87	char *devname;
 88
 89	devname = kasprintf(GFP_KERNEL, "%s.%d", dma_data->dma_res->name,
 90		dma_data->ssp_id);
 91	if ((strcmp(dev_name(chan->device->dev), devname) == 0) &&
 92		(chan->chan_id == dma_data->dma_res->start)) {
 93		found = true;
 94	}
 95
 96	kfree(devname);
 97	return found;
 98}
 99
100static int mmp_pcm_open(struct snd_pcm_substream *substream)
101{
102	struct snd_soc_pcm_runtime *rtd = substream->private_data;
103	struct platform_device *pdev = to_platform_device(rtd->platform->dev);
 
104	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
105	struct mmp_dma_data dma_data;
106	struct resource *r;
107
108	r = platform_get_resource(pdev, IORESOURCE_DMA, substream->stream);
109	if (!r)
110		return -EBUSY;
111
112	snd_soc_set_runtime_hwparams(substream,
113				&mmp_pcm_hardware[substream->stream]);
114
115	dma_data.dma_res = r;
116	dma_data.ssp_id = cpu_dai->id;
117
118	return snd_dmaengine_pcm_open_request_chan(substream, filter,
119		    &dma_data);
120}
121
122static int mmp_pcm_mmap(struct snd_pcm_substream *substream,
123			 struct vm_area_struct *vma)
124{
125	struct snd_pcm_runtime *runtime = substream->runtime;
126	unsigned long off = vma->vm_pgoff;
127
128	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
129	return remap_pfn_range(vma, vma->vm_start,
130		__phys_to_pfn(runtime->dma_addr) + off,
131		vma->vm_end - vma->vm_start, vma->vm_page_prot);
132}
133
134static struct snd_pcm_ops mmp_pcm_ops = {
135	.open		= mmp_pcm_open,
136	.close		= snd_dmaengine_pcm_close_release_chan,
137	.ioctl		= snd_pcm_lib_ioctl,
138	.hw_params	= mmp_pcm_hw_params,
139	.trigger	= snd_dmaengine_pcm_trigger,
140	.pointer	= snd_dmaengine_pcm_pointer,
141	.mmap		= mmp_pcm_mmap,
142};
143
144static void mmp_pcm_free_dma_buffers(struct snd_pcm *pcm)
145{
146	struct snd_pcm_substream *substream;
147	struct snd_dma_buffer *buf;
148	int stream;
149	struct gen_pool *gpool;
150
151	gpool = sram_get_gpool("asram");
152	if (!gpool)
153		return;
154
155	for (stream = 0; stream < 2; stream++) {
156		size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
157
158		substream = pcm->streams[stream].substream;
159		if (!substream)
160			continue;
161
162		buf = &substream->dma_buffer;
163		if (!buf->area)
164			continue;
165		gen_pool_free(gpool, (unsigned long)buf->area, size);
166		buf->area = NULL;
167	}
168
169	return;
170}
171
172static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream,
173								int stream)
174{
175	struct snd_dma_buffer *buf = &substream->dma_buffer;
176	size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
177	struct gen_pool *gpool;
178
179	buf->dev.type = SNDRV_DMA_TYPE_DEV;
180	buf->dev.dev = substream->pcm->card->dev;
181	buf->private_data = NULL;
182
183	gpool = sram_get_gpool("asram");
184	if (!gpool)
185		return -ENOMEM;
186
187	buf->area = gen_pool_dma_alloc(gpool, size, &buf->addr);
188	if (!buf->area)
189		return -ENOMEM;
190	buf->bytes = size;
191	return 0;
192}
193
194static int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd)
195{
196	struct snd_pcm_substream *substream;
197	struct snd_pcm *pcm = rtd->pcm;
198	int ret = 0, stream;
199
200	for (stream = 0; stream < 2; stream++) {
201		substream = pcm->streams[stream].substream;
202
203		ret = mmp_pcm_preallocate_dma_buffer(substream,	stream);
204		if (ret)
205			goto err;
206	}
207
208	return 0;
209
210err:
211	mmp_pcm_free_dma_buffers(pcm);
212	return ret;
213}
214
215static struct snd_soc_platform_driver mmp_soc_platform = {
 
216	.ops		= &mmp_pcm_ops,
217	.pcm_new	= mmp_pcm_new,
218	.pcm_free	= mmp_pcm_free_dma_buffers,
219};
220
221static int mmp_pcm_probe(struct platform_device *pdev)
222{
223	struct mmp_audio_platdata *pdata = pdev->dev.platform_data;
224
225	if (pdata) {
226		mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].buffer_bytes_max =
227						pdata->buffer_max_playback;
228		mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].period_bytes_max =
229						pdata->period_max_playback;
230		mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].buffer_bytes_max =
231						pdata->buffer_max_capture;
232		mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].period_bytes_max =
233						pdata->period_max_capture;
234	}
235	return devm_snd_soc_register_platform(&pdev->dev, &mmp_soc_platform);
 
236}
237
238static struct platform_driver mmp_pcm_driver = {
239	.driver = {
240		.name = "mmp-pcm-audio",
241	},
242
243	.probe = mmp_pcm_probe,
244};
245
246module_platform_driver(mmp_pcm_driver);
247
248MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
249MODULE_DESCRIPTION("MMP Soc Audio DMA module");
250MODULE_LICENSE("GPL");
 
  1/*
  2 * linux/sound/soc/pxa/mmp-pcm.c
  3 *
  4 * Copyright (C) 2011 Marvell International Ltd.
  5 *
  6 * This program is free software; you can redistribute it and/or modify
  7 * it under the terms of the GNU General Public License as published by
  8 * the Free Software Foundation; either version 2 of the License, or
  9 * (at your option) any later version.
 10 *
 11 */
 12#include <linux/module.h>
 13#include <linux/init.h>
 14#include <linux/platform_device.h>
 15#include <linux/slab.h>
 16#include <linux/dma-mapping.h>
 17#include <linux/dmaengine.h>
 18#include <linux/platform_data/dma-mmp_tdma.h>
 19#include <linux/platform_data/mmp_audio.h>
 20
 21#include <sound/pxa2xx-lib.h>
 22#include <sound/core.h>
 23#include <sound/pcm.h>
 24#include <sound/pcm_params.h>
 25#include <sound/soc.h>
 26#include <sound/dmaengine_pcm.h>
 27
 28#define DRV_NAME "mmp-pcm"
 29
 30struct mmp_dma_data {
 31	int ssp_id;
 32	struct resource *dma_res;
 33};
 34
 35#define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP |	\
 36		SNDRV_PCM_INFO_MMAP_VALID |	\
 37		SNDRV_PCM_INFO_INTERLEAVED |	\
 38		SNDRV_PCM_INFO_PAUSE |		\
 39		SNDRV_PCM_INFO_RESUME |		\
 40		SNDRV_PCM_INFO_NO_PERIOD_WAKEUP)
 41
 42static struct snd_pcm_hardware mmp_pcm_hardware[] = {
 43	{
 44		.info			= MMP_PCM_INFO,
 45		.period_bytes_min	= 1024,
 46		.period_bytes_max	= 2048,
 47		.periods_min		= 2,
 48		.periods_max		= 32,
 49		.buffer_bytes_max	= 4096,
 50		.fifo_size		= 32,
 51	},
 52	{
 53		.info			= MMP_PCM_INFO,
 54		.period_bytes_min	= 1024,
 55		.period_bytes_max	= 2048,
 56		.periods_min		= 2,
 57		.periods_max		= 32,
 58		.buffer_bytes_max	= 4096,
 59		.fifo_size		= 32,
 60	},
 61};
 62
 63static int mmp_pcm_hw_params(struct snd_pcm_substream *substream,
 64			      struct snd_pcm_hw_params *params)
 65{
 66	struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
 67	struct dma_slave_config slave_config;
 68	int ret;
 69
 70	ret =
 71	    snd_dmaengine_pcm_prepare_slave_config(substream, params,
 72						   &slave_config);
 73	if (ret)
 74		return ret;
 75
 76	ret = dmaengine_slave_config(chan, &slave_config);
 77	if (ret)
 78		return ret;
 79
 80	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 81
 82	return 0;
 83}
 84
 85static bool filter(struct dma_chan *chan, void *param)
 86{
 87	struct mmp_dma_data *dma_data = param;
 88	bool found = false;
 89	char *devname;
 90
 91	devname = kasprintf(GFP_KERNEL, "%s.%d", dma_data->dma_res->name,
 92		dma_data->ssp_id);
 93	if ((strcmp(dev_name(chan->device->dev), devname) == 0) &&
 94		(chan->chan_id == dma_data->dma_res->start)) {
 95		found = true;
 96	}
 97
 98	kfree(devname);
 99	return found;
100}
101
102static int mmp_pcm_open(struct snd_pcm_substream *substream)
103{
104	struct snd_soc_pcm_runtime *rtd = substream->private_data;
105	struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
106	struct platform_device *pdev = to_platform_device(component->dev);
107	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
108	struct mmp_dma_data dma_data;
109	struct resource *r;
110
111	r = platform_get_resource(pdev, IORESOURCE_DMA, substream->stream);
112	if (!r)
113		return -EBUSY;
114
115	snd_soc_set_runtime_hwparams(substream,
116				&mmp_pcm_hardware[substream->stream]);
117
118	dma_data.dma_res = r;
119	dma_data.ssp_id = cpu_dai->id;
120
121	return snd_dmaengine_pcm_open_request_chan(substream, filter,
122		    &dma_data);
123}
124
125static int mmp_pcm_mmap(struct snd_pcm_substream *substream,
126			 struct vm_area_struct *vma)
127{
128	struct snd_pcm_runtime *runtime = substream->runtime;
129	unsigned long off = vma->vm_pgoff;
130
131	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
132	return remap_pfn_range(vma, vma->vm_start,
133		__phys_to_pfn(runtime->dma_addr) + off,
134		vma->vm_end - vma->vm_start, vma->vm_page_prot);
135}
136
137static const struct snd_pcm_ops mmp_pcm_ops = {
138	.open		= mmp_pcm_open,
139	.close		= snd_dmaengine_pcm_close_release_chan,
140	.ioctl		= snd_pcm_lib_ioctl,
141	.hw_params	= mmp_pcm_hw_params,
142	.trigger	= snd_dmaengine_pcm_trigger,
143	.pointer	= snd_dmaengine_pcm_pointer,
144	.mmap		= mmp_pcm_mmap,
145};
146
147static void mmp_pcm_free_dma_buffers(struct snd_pcm *pcm)
148{
149	struct snd_pcm_substream *substream;
150	struct snd_dma_buffer *buf;
151	int stream;
152	struct gen_pool *gpool;
153
154	gpool = sram_get_gpool("asram");
155	if (!gpool)
156		return;
157
158	for (stream = 0; stream < 2; stream++) {
159		size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
160
161		substream = pcm->streams[stream].substream;
162		if (!substream)
163			continue;
164
165		buf = &substream->dma_buffer;
166		if (!buf->area)
167			continue;
168		gen_pool_free(gpool, (unsigned long)buf->area, size);
169		buf->area = NULL;
170	}
171
 
172}
173
174static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream,
175								int stream)
176{
177	struct snd_dma_buffer *buf = &substream->dma_buffer;
178	size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
179	struct gen_pool *gpool;
180
181	buf->dev.type = SNDRV_DMA_TYPE_DEV;
182	buf->dev.dev = substream->pcm->card->dev;
183	buf->private_data = NULL;
184
185	gpool = sram_get_gpool("asram");
186	if (!gpool)
187		return -ENOMEM;
188
189	buf->area = gen_pool_dma_alloc(gpool, size, &buf->addr);
190	if (!buf->area)
191		return -ENOMEM;
192	buf->bytes = size;
193	return 0;
194}
195
196static int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd)
197{
198	struct snd_pcm_substream *substream;
199	struct snd_pcm *pcm = rtd->pcm;
200	int ret = 0, stream;
201
202	for (stream = 0; stream < 2; stream++) {
203		substream = pcm->streams[stream].substream;
204
205		ret = mmp_pcm_preallocate_dma_buffer(substream,	stream);
206		if (ret)
207			goto err;
208	}
209
210	return 0;
211
212err:
213	mmp_pcm_free_dma_buffers(pcm);
214	return ret;
215}
216
217static const struct snd_soc_component_driver mmp_soc_component = {
218	.name		= DRV_NAME,
219	.ops		= &mmp_pcm_ops,
220	.pcm_new	= mmp_pcm_new,
221	.pcm_free	= mmp_pcm_free_dma_buffers,
222};
223
224static int mmp_pcm_probe(struct platform_device *pdev)
225{
226	struct mmp_audio_platdata *pdata = pdev->dev.platform_data;
227
228	if (pdata) {
229		mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].buffer_bytes_max =
230						pdata->buffer_max_playback;
231		mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].period_bytes_max =
232						pdata->period_max_playback;
233		mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].buffer_bytes_max =
234						pdata->buffer_max_capture;
235		mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].period_bytes_max =
236						pdata->period_max_capture;
237	}
238	return devm_snd_soc_register_component(&pdev->dev, &mmp_soc_component,
239					       NULL, 0);
240}
241
242static struct platform_driver mmp_pcm_driver = {
243	.driver = {
244		.name = "mmp-pcm-audio",
245	},
246
247	.probe = mmp_pcm_probe,
248};
249
250module_platform_driver(mmp_pcm_driver);
251
252MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
253MODULE_DESCRIPTION("MMP Soc Audio DMA module");
254MODULE_LICENSE("GPL");
255MODULE_ALIAS("platform:mmp-pcm-audio");