ep93xx-pcm.c 5.4 KB
Newer Older
R
Ryan Mallon 已提交
1 2 3 4 5 6 7
/*
 * linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface
 *
 * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
 * Copyright (C) 2006 Applied Data Systems
 *
 * Rewritten for the SoC audio subsystem (Based on PXA2xx code):
8
 *   Copyright (c) 2008 Ryan Mallon
R
Ryan Mallon 已提交
9 10 11 12 13 14 15 16 17 18
 *
 * 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/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
19
#include <linux/dmaengine.h>
R
Ryan Mallon 已提交
20 21 22 23 24 25
#include <linux/dma-mapping.h>

#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
26
#include <sound/dmaengine_pcm.h>
R
Ryan Mallon 已提交
27

28
#include <linux/platform_data/dma-ep93xx.h>
R
Ryan Mallon 已提交
29 30 31 32 33 34 35 36 37
#include <mach/hardware.h>
#include <mach/ep93xx-regs.h>

static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
	.info			= (SNDRV_PCM_INFO_MMAP		|
				   SNDRV_PCM_INFO_MMAP_VALID	|
				   SNDRV_PCM_INFO_INTERLEAVED	|
				   SNDRV_PCM_INFO_BLOCK_TRANSFER),
				   
38
	.rates			= SNDRV_PCM_RATE_8000_192000,
R
Ryan Mallon 已提交
39
	.rate_min		= SNDRV_PCM_RATE_8000,
40
	.rate_max		= SNDRV_PCM_RATE_192000,
R
Ryan Mallon 已提交
41 42 43 44 45 46 47 48 49 50 51 52 53
	
	.formats		= (SNDRV_PCM_FMTBIT_S16_LE |
				   SNDRV_PCM_FMTBIT_S24_LE |
				   SNDRV_PCM_FMTBIT_S32_LE),
	
	.buffer_bytes_max	= 131072,
	.period_bytes_min	= 32,
	.period_bytes_max	= 32768,
	.periods_min		= 1,
	.periods_max		= 32,
	.fifo_size		= 32,
};

54
static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param)
R
Ryan Mallon 已提交
55
{
56
	struct ep93xx_dma_data *data = filter_param;
R
Ryan Mallon 已提交
57

58 59 60
	if (data->direction == ep93xx_dma_chan_direction(chan)) {
		chan->private = data;
		return true;
R
Ryan Mallon 已提交
61
	}
62 63

	return false;
R
Ryan Mallon 已提交
64 65 66 67
}

static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
{
68
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
R
Ryan Mallon 已提交
69 70 71

	snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);

72 73
	return snd_dmaengine_pcm_open_request_chan(substream,
			ep93xx_pcm_dma_filter,
74
			snd_soc_dai_get_dma_data(rtd->cpu_dai, substream));
75 76
}

R
Ryan Mallon 已提交
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
				struct snd_pcm_hw_params *params)
{
	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);

	return 0;
}

static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
{
	snd_pcm_set_runtime_buffer(substream, NULL);
	return 0;
}

static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
			   struct vm_area_struct *vma)
{
	struct snd_pcm_runtime *runtime = substream->runtime;

	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
				     runtime->dma_area,
				     runtime->dma_addr,
				     runtime->dma_bytes);
}

static struct snd_pcm_ops ep93xx_pcm_ops = {
	.open		= ep93xx_pcm_open,
104
	.close		= snd_dmaengine_pcm_close_release_chan,
R
Ryan Mallon 已提交
105 106 107
	.ioctl		= snd_pcm_lib_ioctl,
	.hw_params	= ep93xx_pcm_hw_params,
	.hw_free	= ep93xx_pcm_hw_free,
108
	.trigger	= snd_dmaengine_pcm_trigger,
109
	.pointer	= snd_dmaengine_pcm_pointer_no_residue,
R
Ryan Mallon 已提交
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
	.mmap		= ep93xx_pcm_mmap,
};

static int ep93xx_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 = ep93xx_pcm_hardware.buffer_bytes_max;

	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);
	buf->bytes = size;

	return (buf->area == NULL) ? -ENOMEM : 0;
}

static void ep93xx_pcm_free_dma_buffers(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;
	}
}

150
static u64 ep93xx_pcm_dmamask = DMA_BIT_MASK(32);
R
Ryan Mallon 已提交
151

152
static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd)
R
Ryan Mallon 已提交
153
{
154 155
	struct snd_card *card = rtd->card->snd_card;
	struct snd_pcm *pcm = rtd->pcm;
R
Ryan Mallon 已提交
156 157 158 159 160
	int ret = 0;

	if (!card->dev->dma_mask)
		card->dev->dma_mask = &ep93xx_pcm_dmamask;
	if (!card->dev->coherent_dma_mask)
161
		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
R
Ryan Mallon 已提交
162

163
	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
R
Ryan Mallon 已提交
164 165 166 167 168 169
		ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
					SNDRV_PCM_STREAM_PLAYBACK);
		if (ret)
			return ret;
	}

170
	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
R
Ryan Mallon 已提交
171 172 173 174 175 176 177 178 179
		ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
					SNDRV_PCM_STREAM_CAPTURE);
		if (ret)
			return ret;
	}

	return 0;
}

180 181
static struct snd_soc_platform_driver ep93xx_soc_platform = {
	.ops		= &ep93xx_pcm_ops,
R
Ryan Mallon 已提交
182 183 184
	.pcm_new	= &ep93xx_pcm_new,
	.pcm_free	= &ep93xx_pcm_free_dma_buffers,
};
185

186
static int ep93xx_soc_platform_probe(struct platform_device *pdev)
187 188 189 190
{
	return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform);
}

191
static int ep93xx_soc_platform_remove(struct platform_device *pdev)
192 193 194 195 196 197 198 199 200 201 202 203
{
	snd_soc_unregister_platform(&pdev->dev);
	return 0;
}

static struct platform_driver ep93xx_pcm_driver = {
	.driver = {
			.name = "ep93xx-pcm-audio",
			.owner = THIS_MODULE,
	},

	.probe = ep93xx_soc_platform_probe,
204
	.remove = ep93xx_soc_platform_remove,
205
};
R
Ryan Mallon 已提交
206

207
module_platform_driver(ep93xx_pcm_driver);
R
Ryan Mallon 已提交
208

209
MODULE_AUTHOR("Ryan Mallon");
R
Ryan Mallon 已提交
210 211
MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
MODULE_LICENSE("GPL");
212
MODULE_ALIAS("platform:ep93xx-pcm-audio");