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