1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * atmel-pcm.c -- ALSA PCM interface for the Atmel atmel SoC. 4 * 5 * Copyright (C) 2005 SAN People 6 * Copyright (C) 2008 Atmel 7 * 8 * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com> 9 * 10 * Based on at91-pcm. by: 11 * Frank Mandarino <fmandarino@endrelia.com> 12 * Copyright 2006 Endrelia Technologies Inc. 13 * 14 * Based on pxa2xx-pcm.c by: 15 * 16 * Author: Nicolas Pitre 17 * Created: Nov 30, 2004 18 * Copyright: (C) 2004 MontaVista Software, Inc. 19 */ 20 21 #include <linux/module.h> 22 #include <linux/init.h> 23 #include <linux/platform_device.h> 24 #include <linux/slab.h> 25 #include <linux/dma-mapping.h> 26 #include <linux/atmel_pdc.h> 27 #include <linux/atmel-ssc.h> 28 29 #include <sound/core.h> 30 #include <sound/pcm.h> 31 #include <sound/pcm_params.h> 32 #include <sound/soc.h> 33 34 #include "atmel-pcm.h" 35 36 37 static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, 38 int stream) 39 { 40 struct snd_pcm_substream *substream = pcm->streams[stream].substream; 41 struct snd_dma_buffer *buf = &substream->dma_buffer; 42 size_t size = ATMEL_SSC_DMABUF_SIZE; 43 44 buf->dev.type = SNDRV_DMA_TYPE_DEV; 45 buf->dev.dev = pcm->card->dev; 46 buf->private_data = NULL; 47 buf->area = dma_alloc_coherent(pcm->card->dev, size, 48 &buf->addr, GFP_KERNEL); 49 pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n", 50 (void *)buf->area, (void *)(long)buf->addr, size); 51 52 if (!buf->area) 53 return -ENOMEM; 54 55 buf->bytes = size; 56 return 0; 57 } 58 59 static int atmel_pcm_mmap(struct snd_soc_component *component, 60 struct snd_pcm_substream *substream, 61 struct vm_area_struct *vma) 62 { 63 return remap_pfn_range(vma, vma->vm_start, 64 substream->dma_buffer.addr >> PAGE_SHIFT, 65 vma->vm_end - vma->vm_start, vma->vm_page_prot); 66 } 67 68 static int atmel_pcm_new(struct snd_soc_component *component, 69 struct snd_soc_pcm_runtime *rtd) 70 { 71 struct snd_card *card = rtd->card->snd_card; 72 struct snd_pcm *pcm = rtd->pcm; 73 int ret; 74 75 ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); 76 if (ret) 77 return ret; 78 79 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 80 pr_debug("atmel-pcm: allocating PCM playback DMA buffer\n"); 81 ret = atmel_pcm_preallocate_dma_buffer(pcm, 82 SNDRV_PCM_STREAM_PLAYBACK); 83 if (ret) 84 goto out; 85 } 86 87 if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 88 pr_debug("atmel-pcm: allocating PCM capture DMA buffer\n"); 89 ret = atmel_pcm_preallocate_dma_buffer(pcm, 90 SNDRV_PCM_STREAM_CAPTURE); 91 if (ret) 92 goto out; 93 } 94 out: 95 return ret; 96 } 97 98 static void atmel_pcm_free(struct snd_soc_component *component, 99 struct snd_pcm *pcm) 100 { 101 struct snd_pcm_substream *substream; 102 struct snd_dma_buffer *buf; 103 int stream; 104 105 for (stream = 0; stream < 2; stream++) { 106 substream = pcm->streams[stream].substream; 107 if (!substream) 108 continue; 109 110 buf = &substream->dma_buffer; 111 if (!buf->area) 112 continue; 113 dma_free_coherent(pcm->card->dev, buf->bytes, 114 buf->area, buf->addr); 115 buf->area = NULL; 116 } 117 } 118 119 /*--------------------------------------------------------------------------*\ 120 * Hardware definition 121 \*--------------------------------------------------------------------------*/ 122 /* TODO: These values were taken from the AT91 platform driver, check 123 * them against real values for AT32 124 */ 125 static const struct snd_pcm_hardware atmel_pcm_hardware = { 126 .info = SNDRV_PCM_INFO_MMAP | 127 SNDRV_PCM_INFO_MMAP_VALID | 128 SNDRV_PCM_INFO_INTERLEAVED | 129 SNDRV_PCM_INFO_PAUSE, 130 .period_bytes_min = 32, 131 .period_bytes_max = 8192, 132 .periods_min = 2, 133 .periods_max = 1024, 134 .buffer_bytes_max = ATMEL_SSC_DMABUF_SIZE, 135 }; 136 137 138 /*--------------------------------------------------------------------------*\ 139 * Data types 140 \*--------------------------------------------------------------------------*/ 141 struct atmel_runtime_data { 142 struct atmel_pcm_dma_params *params; 143 dma_addr_t dma_buffer; /* physical address of dma buffer */ 144 dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */ 145 size_t period_size; 146 147 dma_addr_t period_ptr; /* physical address of next period */ 148 }; 149 150 /*--------------------------------------------------------------------------*\ 151 * ISR 152 \*--------------------------------------------------------------------------*/ 153 static void atmel_pcm_dma_irq(u32 ssc_sr, 154 struct snd_pcm_substream *substream) 155 { 156 struct atmel_runtime_data *prtd = substream->runtime->private_data; 157 struct atmel_pcm_dma_params *params = prtd->params; 158 static int count; 159 160 count++; 161 162 if (ssc_sr & params->mask->ssc_endbuf) { 163 pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n", 164 substream->stream == SNDRV_PCM_STREAM_PLAYBACK 165 ? "underrun" : "overrun", 166 params->name, ssc_sr, count); 167 168 /* re-start the PDC */ 169 ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, 170 params->mask->pdc_disable); 171 prtd->period_ptr += prtd->period_size; 172 if (prtd->period_ptr >= prtd->dma_buffer_end) 173 prtd->period_ptr = prtd->dma_buffer; 174 175 ssc_writex(params->ssc->regs, params->pdc->xpr, 176 prtd->period_ptr); 177 ssc_writex(params->ssc->regs, params->pdc->xcr, 178 prtd->period_size / params->pdc_xfer_size); 179 ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, 180 params->mask->pdc_enable); 181 } 182 183 if (ssc_sr & params->mask->ssc_endx) { 184 /* Load the PDC next pointer and counter registers */ 185 prtd->period_ptr += prtd->period_size; 186 if (prtd->period_ptr >= prtd->dma_buffer_end) 187 prtd->period_ptr = prtd->dma_buffer; 188 189 ssc_writex(params->ssc->regs, params->pdc->xnpr, 190 prtd->period_ptr); 191 ssc_writex(params->ssc->regs, params->pdc->xncr, 192 prtd->period_size / params->pdc_xfer_size); 193 } 194 195 snd_pcm_period_elapsed(substream); 196 } 197 198 199 /*--------------------------------------------------------------------------*\ 200 * PCM operations 201 \*--------------------------------------------------------------------------*/ 202 static int atmel_pcm_hw_params(struct snd_soc_component *component, 203 struct snd_pcm_substream *substream, 204 struct snd_pcm_hw_params *params) 205 { 206 struct snd_pcm_runtime *runtime = substream->runtime; 207 struct atmel_runtime_data *prtd = runtime->private_data; 208 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 209 210 /* this may get called several times by oss emulation 211 * with different params */ 212 213 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 214 runtime->dma_bytes = params_buffer_bytes(params); 215 216 prtd->params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 217 prtd->params->dma_intr_handler = atmel_pcm_dma_irq; 218 219 prtd->dma_buffer = runtime->dma_addr; 220 prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes; 221 prtd->period_size = params_period_bytes(params); 222 223 pr_debug("atmel-pcm: " 224 "hw_params: DMA for %s initialized " 225 "(dma_bytes=%zu, period_size=%zu)\n", 226 prtd->params->name, 227 runtime->dma_bytes, 228 prtd->period_size); 229 return 0; 230 } 231 232 static int atmel_pcm_hw_free(struct snd_soc_component *component, 233 struct snd_pcm_substream *substream) 234 { 235 struct atmel_runtime_data *prtd = substream->runtime->private_data; 236 struct atmel_pcm_dma_params *params = prtd->params; 237 238 if (params != NULL) { 239 ssc_writex(params->ssc->regs, SSC_PDC_PTCR, 240 params->mask->pdc_disable); 241 prtd->params->dma_intr_handler = NULL; 242 } 243 244 return 0; 245 } 246 247 static int atmel_pcm_prepare(struct snd_soc_component *component, 248 struct snd_pcm_substream *substream) 249 { 250 struct atmel_runtime_data *prtd = substream->runtime->private_data; 251 struct atmel_pcm_dma_params *params = prtd->params; 252 253 ssc_writex(params->ssc->regs, SSC_IDR, 254 params->mask->ssc_endx | params->mask->ssc_endbuf); 255 ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, 256 params->mask->pdc_disable); 257 return 0; 258 } 259 260 static int atmel_pcm_trigger(struct snd_soc_component *component, 261 struct snd_pcm_substream *substream, int cmd) 262 { 263 struct snd_pcm_runtime *rtd = substream->runtime; 264 struct atmel_runtime_data *prtd = rtd->private_data; 265 struct atmel_pcm_dma_params *params = prtd->params; 266 int ret = 0; 267 268 pr_debug("atmel-pcm:buffer_size = %ld," 269 "dma_area = %p, dma_bytes = %zu\n", 270 rtd->buffer_size, rtd->dma_area, rtd->dma_bytes); 271 272 switch (cmd) { 273 case SNDRV_PCM_TRIGGER_START: 274 prtd->period_ptr = prtd->dma_buffer; 275 276 ssc_writex(params->ssc->regs, params->pdc->xpr, 277 prtd->period_ptr); 278 ssc_writex(params->ssc->regs, params->pdc->xcr, 279 prtd->period_size / params->pdc_xfer_size); 280 281 prtd->period_ptr += prtd->period_size; 282 ssc_writex(params->ssc->regs, params->pdc->xnpr, 283 prtd->period_ptr); 284 ssc_writex(params->ssc->regs, params->pdc->xncr, 285 prtd->period_size / params->pdc_xfer_size); 286 287 pr_debug("atmel-pcm: trigger: " 288 "period_ptr=%lx, xpr=%u, " 289 "xcr=%u, xnpr=%u, xncr=%u\n", 290 (unsigned long)prtd->period_ptr, 291 ssc_readx(params->ssc->regs, params->pdc->xpr), 292 ssc_readx(params->ssc->regs, params->pdc->xcr), 293 ssc_readx(params->ssc->regs, params->pdc->xnpr), 294 ssc_readx(params->ssc->regs, params->pdc->xncr)); 295 296 ssc_writex(params->ssc->regs, SSC_IER, 297 params->mask->ssc_endx | params->mask->ssc_endbuf); 298 ssc_writex(params->ssc->regs, SSC_PDC_PTCR, 299 params->mask->pdc_enable); 300 301 pr_debug("sr=%u imr=%u\n", 302 ssc_readx(params->ssc->regs, SSC_SR), 303 ssc_readx(params->ssc->regs, SSC_IER)); 304 break; /* SNDRV_PCM_TRIGGER_START */ 305 306 case SNDRV_PCM_TRIGGER_STOP: 307 case SNDRV_PCM_TRIGGER_SUSPEND: 308 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 309 ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, 310 params->mask->pdc_disable); 311 break; 312 313 case SNDRV_PCM_TRIGGER_RESUME: 314 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 315 ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, 316 params->mask->pdc_enable); 317 break; 318 319 default: 320 ret = -EINVAL; 321 } 322 323 return ret; 324 } 325 326 static snd_pcm_uframes_t atmel_pcm_pointer(struct snd_soc_component *component, 327 struct snd_pcm_substream *substream) 328 { 329 struct snd_pcm_runtime *runtime = substream->runtime; 330 struct atmel_runtime_data *prtd = runtime->private_data; 331 struct atmel_pcm_dma_params *params = prtd->params; 332 dma_addr_t ptr; 333 snd_pcm_uframes_t x; 334 335 ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr); 336 x = bytes_to_frames(runtime, ptr - prtd->dma_buffer); 337 338 if (x == runtime->buffer_size) 339 x = 0; 340 341 return x; 342 } 343 344 static int atmel_pcm_open(struct snd_soc_component *component, 345 struct snd_pcm_substream *substream) 346 { 347 struct snd_pcm_runtime *runtime = substream->runtime; 348 struct atmel_runtime_data *prtd; 349 int ret = 0; 350 351 snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware); 352 353 /* ensure that buffer size is a multiple of period size */ 354 ret = snd_pcm_hw_constraint_integer(runtime, 355 SNDRV_PCM_HW_PARAM_PERIODS); 356 if (ret < 0) 357 goto out; 358 359 prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL); 360 if (prtd == NULL) { 361 ret = -ENOMEM; 362 goto out; 363 } 364 runtime->private_data = prtd; 365 366 out: 367 return ret; 368 } 369 370 static int atmel_pcm_close(struct snd_soc_component *component, 371 struct snd_pcm_substream *substream) 372 { 373 struct atmel_runtime_data *prtd = substream->runtime->private_data; 374 375 kfree(prtd); 376 return 0; 377 } 378 379 static const struct snd_soc_component_driver atmel_soc_platform = { 380 .open = atmel_pcm_open, 381 .close = atmel_pcm_close, 382 .hw_params = atmel_pcm_hw_params, 383 .hw_free = atmel_pcm_hw_free, 384 .prepare = atmel_pcm_prepare, 385 .trigger = atmel_pcm_trigger, 386 .pointer = atmel_pcm_pointer, 387 .mmap = atmel_pcm_mmap, 388 .pcm_construct = atmel_pcm_new, 389 .pcm_destruct = atmel_pcm_free, 390 }; 391 392 int atmel_pcm_pdc_platform_register(struct device *dev) 393 { 394 return devm_snd_soc_register_component(dev, &atmel_soc_platform, 395 NULL, 0); 396 } 397 EXPORT_SYMBOL(atmel_pcm_pdc_platform_register); 398 399 MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>"); 400 MODULE_DESCRIPTION("Atmel PCM module"); 401 MODULE_LICENSE("GPL"); 402