1 // SPDX-License-Identifier: GPL-2.0-or-later 1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 2 /* 3 * atmel-pcm.c -- ALSA PCM interface for the 3 * atmel-pcm.c -- ALSA PCM interface for the Atmel atmel SoC. 4 * 4 * 5 * Copyright (C) 2005 SAN People 5 * Copyright (C) 2005 SAN People 6 * Copyright (C) 2008 Atmel 6 * Copyright (C) 2008 Atmel 7 * 7 * 8 * Authors: Sedji Gaouaou <sedji.gaouaou@atmel 8 * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com> 9 * 9 * 10 * Based on at91-pcm. by: 10 * Based on at91-pcm. by: 11 * Frank Mandarino <fmandarino@endrelia.com> 11 * Frank Mandarino <fmandarino@endrelia.com> 12 * Copyright 2006 Endrelia Technologies Inc. 12 * Copyright 2006 Endrelia Technologies Inc. 13 * 13 * 14 * Based on pxa2xx-pcm.c by: 14 * Based on pxa2xx-pcm.c by: 15 * 15 * 16 * Author: Nicolas Pitre 16 * Author: Nicolas Pitre 17 * Created: Nov 30, 2004 17 * Created: Nov 30, 2004 18 * Copyright: (C) 2004 MontaVista Software, 18 * Copyright: (C) 2004 MontaVista Software, Inc. 19 */ 19 */ 20 20 21 #include <linux/module.h> 21 #include <linux/module.h> 22 #include <linux/init.h> 22 #include <linux/init.h> 23 #include <linux/platform_device.h> 23 #include <linux/platform_device.h> 24 #include <linux/slab.h> 24 #include <linux/slab.h> 25 #include <linux/dma-mapping.h> 25 #include <linux/dma-mapping.h> 26 #include <linux/atmel_pdc.h> 26 #include <linux/atmel_pdc.h> 27 #include <linux/atmel-ssc.h> 27 #include <linux/atmel-ssc.h> 28 28 29 #include <sound/core.h> 29 #include <sound/core.h> 30 #include <sound/pcm.h> 30 #include <sound/pcm.h> 31 #include <sound/pcm_params.h> 31 #include <sound/pcm_params.h> 32 #include <sound/soc.h> 32 #include <sound/soc.h> 33 33 34 #include "atmel-pcm.h" 34 #include "atmel-pcm.h" 35 35 36 36 37 static int atmel_pcm_new(struct snd_soc_compon 37 static int atmel_pcm_new(struct snd_soc_component *component, 38 struct snd_soc_pcm_ru 38 struct snd_soc_pcm_runtime *rtd) 39 { 39 { 40 struct snd_card *card = rtd->card->snd 40 struct snd_card *card = rtd->card->snd_card; 41 int ret; 41 int ret; 42 42 43 ret = dma_coerce_mask_and_coherent(car 43 ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); 44 if (ret) 44 if (ret) 45 return ret; 45 return ret; 46 46 47 snd_pcm_set_managed_buffer_all(rtd->pc 47 snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, 48 card->d 48 card->dev, ATMEL_SSC_DMABUF_SIZE, 49 ATMEL_S 49 ATMEL_SSC_DMABUF_SIZE); 50 50 51 return 0; 51 return 0; 52 } 52 } 53 53 54 /*-------------------------------------------- 54 /*--------------------------------------------------------------------------*\ 55 * Hardware definition 55 * Hardware definition 56 \*-------------------------------------------- 56 \*--------------------------------------------------------------------------*/ 57 /* TODO: These values were taken from the AT91 57 /* TODO: These values were taken from the AT91 platform driver, check 58 * them against real values for AT32 58 * them against real values for AT32 59 */ 59 */ 60 static const struct snd_pcm_hardware atmel_pcm 60 static const struct snd_pcm_hardware atmel_pcm_hardware = { 61 .info = SNDRV_PCM_IN 61 .info = SNDRV_PCM_INFO_MMAP | 62 SNDRV_PCM_IN 62 SNDRV_PCM_INFO_MMAP_VALID | 63 SNDRV_PCM_IN 63 SNDRV_PCM_INFO_INTERLEAVED | 64 SNDRV_PCM_IN 64 SNDRV_PCM_INFO_PAUSE, 65 .period_bytes_min = 32, 65 .period_bytes_min = 32, 66 .period_bytes_max = 8192, 66 .period_bytes_max = 8192, 67 .periods_min = 2, 67 .periods_min = 2, 68 .periods_max = 1024, 68 .periods_max = 1024, 69 .buffer_bytes_max = ATMEL_SSC_DM 69 .buffer_bytes_max = ATMEL_SSC_DMABUF_SIZE, 70 }; 70 }; 71 71 72 72 73 /*-------------------------------------------- 73 /*--------------------------------------------------------------------------*\ 74 * Data types 74 * Data types 75 \*-------------------------------------------- 75 \*--------------------------------------------------------------------------*/ 76 struct atmel_runtime_data { 76 struct atmel_runtime_data { 77 struct atmel_pcm_dma_params *params; 77 struct atmel_pcm_dma_params *params; 78 dma_addr_t dma_buffer; /* phy 78 dma_addr_t dma_buffer; /* physical address of dma buffer */ 79 dma_addr_t dma_buffer_end; /* fir 79 dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */ 80 size_t period_size; 80 size_t period_size; 81 81 82 dma_addr_t period_ptr; /* phy 82 dma_addr_t period_ptr; /* physical address of next period */ 83 }; 83 }; 84 84 85 /*-------------------------------------------- 85 /*--------------------------------------------------------------------------*\ 86 * ISR 86 * ISR 87 \*-------------------------------------------- 87 \*--------------------------------------------------------------------------*/ 88 static void atmel_pcm_dma_irq(u32 ssc_sr, 88 static void atmel_pcm_dma_irq(u32 ssc_sr, 89 struct snd_pcm_substream *substream) 89 struct snd_pcm_substream *substream) 90 { 90 { 91 struct atmel_runtime_data *prtd = subs 91 struct atmel_runtime_data *prtd = substream->runtime->private_data; 92 struct atmel_pcm_dma_params *params = 92 struct atmel_pcm_dma_params *params = prtd->params; 93 static int count; 93 static int count; 94 94 95 count++; 95 count++; 96 96 97 if (ssc_sr & params->mask->ssc_endbuf) 97 if (ssc_sr & params->mask->ssc_endbuf) { 98 pr_warn("atmel-pcm: buffer %s 98 pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n", 99 substream->str 99 substream->stream == SNDRV_PCM_STREAM_PLAYBACK 100 ? "underrun" : 100 ? "underrun" : "overrun", 101 params->name, 101 params->name, ssc_sr, count); 102 102 103 /* re-start the PDC */ 103 /* re-start the PDC */ 104 ssc_writex(params->ssc->regs, 104 ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, 105 params->mask->pdc_d 105 params->mask->pdc_disable); 106 prtd->period_ptr += prtd->peri 106 prtd->period_ptr += prtd->period_size; 107 if (prtd->period_ptr >= prtd-> 107 if (prtd->period_ptr >= prtd->dma_buffer_end) 108 prtd->period_ptr = prt 108 prtd->period_ptr = prtd->dma_buffer; 109 109 110 ssc_writex(params->ssc->regs, 110 ssc_writex(params->ssc->regs, params->pdc->xpr, 111 prtd->period_ptr); 111 prtd->period_ptr); 112 ssc_writex(params->ssc->regs, 112 ssc_writex(params->ssc->regs, params->pdc->xcr, 113 prtd->period_size / 113 prtd->period_size / params->pdc_xfer_size); 114 ssc_writex(params->ssc->regs, 114 ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, 115 params->mask->pdc_e 115 params->mask->pdc_enable); 116 } 116 } 117 117 118 if (ssc_sr & params->mask->ssc_endx) { 118 if (ssc_sr & params->mask->ssc_endx) { 119 /* Load the PDC next pointer a 119 /* Load the PDC next pointer and counter registers */ 120 prtd->period_ptr += prtd->peri 120 prtd->period_ptr += prtd->period_size; 121 if (prtd->period_ptr >= prtd-> 121 if (prtd->period_ptr >= prtd->dma_buffer_end) 122 prtd->period_ptr = prt 122 prtd->period_ptr = prtd->dma_buffer; 123 123 124 ssc_writex(params->ssc->regs, 124 ssc_writex(params->ssc->regs, params->pdc->xnpr, 125 prtd->period_ptr); 125 prtd->period_ptr); 126 ssc_writex(params->ssc->regs, 126 ssc_writex(params->ssc->regs, params->pdc->xncr, 127 prtd->period_size / 127 prtd->period_size / params->pdc_xfer_size); 128 } 128 } 129 129 130 snd_pcm_period_elapsed(substream); 130 snd_pcm_period_elapsed(substream); 131 } 131 } 132 132 133 133 134 /*-------------------------------------------- 134 /*--------------------------------------------------------------------------*\ 135 * PCM operations 135 * PCM operations 136 \*-------------------------------------------- 136 \*--------------------------------------------------------------------------*/ 137 static int atmel_pcm_hw_params(struct snd_soc_ 137 static int atmel_pcm_hw_params(struct snd_soc_component *component, 138 struct snd_pcm_ 138 struct snd_pcm_substream *substream, 139 struct snd_pcm_ 139 struct snd_pcm_hw_params *params) 140 { 140 { 141 struct snd_pcm_runtime *runtime = subs 141 struct snd_pcm_runtime *runtime = substream->runtime; 142 struct atmel_runtime_data *prtd = runt 142 struct atmel_runtime_data *prtd = runtime->private_data; 143 struct snd_soc_pcm_runtime *rtd = snd_ !! 143 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 144 144 145 /* this may get called several times b 145 /* this may get called several times by oss emulation 146 * with different params */ 146 * with different params */ 147 147 148 prtd->params = snd_soc_dai_get_dma_dat !! 148 prtd->params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 149 prtd->params->dma_intr_handler = atmel 149 prtd->params->dma_intr_handler = atmel_pcm_dma_irq; 150 150 151 prtd->dma_buffer = runtime->dma_addr; 151 prtd->dma_buffer = runtime->dma_addr; 152 prtd->dma_buffer_end = runtime->dma_ad 152 prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes; 153 prtd->period_size = params_period_byte 153 prtd->period_size = params_period_bytes(params); 154 154 155 pr_debug("atmel-pcm: " 155 pr_debug("atmel-pcm: " 156 "hw_params: DMA for %s initial 156 "hw_params: DMA for %s initialized " 157 "(dma_bytes=%zu, period_size=% 157 "(dma_bytes=%zu, period_size=%zu)\n", 158 prtd->params->name, 158 prtd->params->name, 159 runtime->dma_bytes, 159 runtime->dma_bytes, 160 prtd->period_size); 160 prtd->period_size); 161 return 0; 161 return 0; 162 } 162 } 163 163 164 static int atmel_pcm_hw_free(struct snd_soc_co 164 static int atmel_pcm_hw_free(struct snd_soc_component *component, 165 struct snd_pcm_su 165 struct snd_pcm_substream *substream) 166 { 166 { 167 struct atmel_runtime_data *prtd = subs 167 struct atmel_runtime_data *prtd = substream->runtime->private_data; 168 struct atmel_pcm_dma_params *params = 168 struct atmel_pcm_dma_params *params = prtd->params; 169 169 170 if (params != NULL) { 170 if (params != NULL) { 171 ssc_writex(params->ssc->regs, 171 ssc_writex(params->ssc->regs, SSC_PDC_PTCR, 172 params->mask->pdc_d 172 params->mask->pdc_disable); 173 prtd->params->dma_intr_handler 173 prtd->params->dma_intr_handler = NULL; 174 } 174 } 175 175 176 return 0; 176 return 0; 177 } 177 } 178 178 179 static int atmel_pcm_prepare(struct snd_soc_co 179 static int atmel_pcm_prepare(struct snd_soc_component *component, 180 struct snd_pcm_su 180 struct snd_pcm_substream *substream) 181 { 181 { 182 struct atmel_runtime_data *prtd = subs 182 struct atmel_runtime_data *prtd = substream->runtime->private_data; 183 struct atmel_pcm_dma_params *params = 183 struct atmel_pcm_dma_params *params = prtd->params; 184 184 185 ssc_writex(params->ssc->regs, SSC_IDR, 185 ssc_writex(params->ssc->regs, SSC_IDR, 186 params->mask->ssc_endx | pa 186 params->mask->ssc_endx | params->mask->ssc_endbuf); 187 ssc_writex(params->ssc->regs, ATMEL_PD 187 ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, 188 params->mask->pdc_disable); 188 params->mask->pdc_disable); 189 return 0; 189 return 0; 190 } 190 } 191 191 192 static int atmel_pcm_trigger(struct snd_soc_co 192 static int atmel_pcm_trigger(struct snd_soc_component *component, 193 struct snd_pcm_su 193 struct snd_pcm_substream *substream, int cmd) 194 { 194 { 195 struct snd_pcm_runtime *rtd = substrea 195 struct snd_pcm_runtime *rtd = substream->runtime; 196 struct atmel_runtime_data *prtd = rtd- 196 struct atmel_runtime_data *prtd = rtd->private_data; 197 struct atmel_pcm_dma_params *params = 197 struct atmel_pcm_dma_params *params = prtd->params; 198 int ret = 0; 198 int ret = 0; 199 199 200 pr_debug("atmel-pcm:buffer_size = %ld, 200 pr_debug("atmel-pcm:buffer_size = %ld," 201 "dma_area = %p, dma_bytes = %z 201 "dma_area = %p, dma_bytes = %zu\n", 202 rtd->buffer_size, rtd->dma_are 202 rtd->buffer_size, rtd->dma_area, rtd->dma_bytes); 203 203 204 switch (cmd) { 204 switch (cmd) { 205 case SNDRV_PCM_TRIGGER_START: 205 case SNDRV_PCM_TRIGGER_START: 206 prtd->period_ptr = prtd->dma_b 206 prtd->period_ptr = prtd->dma_buffer; 207 207 208 ssc_writex(params->ssc->regs, 208 ssc_writex(params->ssc->regs, params->pdc->xpr, 209 prtd->period_ptr); 209 prtd->period_ptr); 210 ssc_writex(params->ssc->regs, 210 ssc_writex(params->ssc->regs, params->pdc->xcr, 211 prtd->period_size / 211 prtd->period_size / params->pdc_xfer_size); 212 212 213 prtd->period_ptr += prtd->peri 213 prtd->period_ptr += prtd->period_size; 214 ssc_writex(params->ssc->regs, 214 ssc_writex(params->ssc->regs, params->pdc->xnpr, 215 prtd->period_ptr); 215 prtd->period_ptr); 216 ssc_writex(params->ssc->regs, 216 ssc_writex(params->ssc->regs, params->pdc->xncr, 217 prtd->period_size / 217 prtd->period_size / params->pdc_xfer_size); 218 218 219 pr_debug("atmel-pcm: trigger: 219 pr_debug("atmel-pcm: trigger: " 220 "period_ptr=%lx, xpr=% 220 "period_ptr=%lx, xpr=%u, " 221 "xcr=%u, xnpr=%u, xncr 221 "xcr=%u, xnpr=%u, xncr=%u\n", 222 (unsigned long)prtd->p 222 (unsigned long)prtd->period_ptr, 223 ssc_readx(params->ssc- 223 ssc_readx(params->ssc->regs, params->pdc->xpr), 224 ssc_readx(params->ssc- 224 ssc_readx(params->ssc->regs, params->pdc->xcr), 225 ssc_readx(params->ssc- 225 ssc_readx(params->ssc->regs, params->pdc->xnpr), 226 ssc_readx(params->ssc- 226 ssc_readx(params->ssc->regs, params->pdc->xncr)); 227 227 228 ssc_writex(params->ssc->regs, 228 ssc_writex(params->ssc->regs, SSC_IER, 229 params->mask->ssc_e 229 params->mask->ssc_endx | params->mask->ssc_endbuf); 230 ssc_writex(params->ssc->regs, 230 ssc_writex(params->ssc->regs, SSC_PDC_PTCR, 231 params->mask->pdc_e 231 params->mask->pdc_enable); 232 232 233 pr_debug("sr=%u imr=%u\n", 233 pr_debug("sr=%u imr=%u\n", 234 ssc_readx(params->ssc- 234 ssc_readx(params->ssc->regs, SSC_SR), 235 ssc_readx(params->ssc- 235 ssc_readx(params->ssc->regs, SSC_IER)); 236 break; /* SNDRV_PCM_T 236 break; /* SNDRV_PCM_TRIGGER_START */ 237 237 238 case SNDRV_PCM_TRIGGER_STOP: 238 case SNDRV_PCM_TRIGGER_STOP: 239 case SNDRV_PCM_TRIGGER_SUSPEND: 239 case SNDRV_PCM_TRIGGER_SUSPEND: 240 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 240 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 241 ssc_writex(params->ssc->regs, 241 ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, 242 params->mask->pdc_d 242 params->mask->pdc_disable); 243 break; 243 break; 244 244 245 case SNDRV_PCM_TRIGGER_RESUME: 245 case SNDRV_PCM_TRIGGER_RESUME: 246 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 246 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 247 ssc_writex(params->ssc->regs, 247 ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, 248 params->mask->pdc_e 248 params->mask->pdc_enable); 249 break; 249 break; 250 250 251 default: 251 default: 252 ret = -EINVAL; 252 ret = -EINVAL; 253 } 253 } 254 254 255 return ret; 255 return ret; 256 } 256 } 257 257 258 static snd_pcm_uframes_t atmel_pcm_pointer(str 258 static snd_pcm_uframes_t atmel_pcm_pointer(struct snd_soc_component *component, 259 str 259 struct snd_pcm_substream *substream) 260 { 260 { 261 struct snd_pcm_runtime *runtime = subs 261 struct snd_pcm_runtime *runtime = substream->runtime; 262 struct atmel_runtime_data *prtd = runt 262 struct atmel_runtime_data *prtd = runtime->private_data; 263 struct atmel_pcm_dma_params *params = 263 struct atmel_pcm_dma_params *params = prtd->params; 264 dma_addr_t ptr; 264 dma_addr_t ptr; 265 snd_pcm_uframes_t x; 265 snd_pcm_uframes_t x; 266 266 267 ptr = (dma_addr_t) ssc_readx(params->s 267 ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr); 268 x = bytes_to_frames(runtime, ptr - prt 268 x = bytes_to_frames(runtime, ptr - prtd->dma_buffer); 269 269 270 if (x == runtime->buffer_size) 270 if (x == runtime->buffer_size) 271 x = 0; 271 x = 0; 272 272 273 return x; 273 return x; 274 } 274 } 275 275 276 static int atmel_pcm_open(struct snd_soc_compo 276 static int atmel_pcm_open(struct snd_soc_component *component, 277 struct snd_pcm_subst 277 struct snd_pcm_substream *substream) 278 { 278 { 279 struct snd_pcm_runtime *runtime = subs 279 struct snd_pcm_runtime *runtime = substream->runtime; 280 struct atmel_runtime_data *prtd; 280 struct atmel_runtime_data *prtd; 281 int ret = 0; 281 int ret = 0; 282 282 283 snd_soc_set_runtime_hwparams(substream 283 snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware); 284 284 285 /* ensure that buffer size is a multip 285 /* ensure that buffer size is a multiple of period size */ 286 ret = snd_pcm_hw_constraint_integer(ru 286 ret = snd_pcm_hw_constraint_integer(runtime, 287 287 SNDRV_PCM_HW_PARAM_PERIODS); 288 if (ret < 0) 288 if (ret < 0) 289 goto out; 289 goto out; 290 290 291 prtd = kzalloc(sizeof(struct atmel_run 291 prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL); 292 if (prtd == NULL) { 292 if (prtd == NULL) { 293 ret = -ENOMEM; 293 ret = -ENOMEM; 294 goto out; 294 goto out; 295 } 295 } 296 runtime->private_data = prtd; 296 runtime->private_data = prtd; 297 297 298 out: 298 out: 299 return ret; 299 return ret; 300 } 300 } 301 301 302 static int atmel_pcm_close(struct snd_soc_comp 302 static int atmel_pcm_close(struct snd_soc_component *component, 303 struct snd_pcm_subs 303 struct snd_pcm_substream *substream) 304 { 304 { 305 struct atmel_runtime_data *prtd = subs 305 struct atmel_runtime_data *prtd = substream->runtime->private_data; 306 306 307 kfree(prtd); 307 kfree(prtd); 308 return 0; 308 return 0; 309 } 309 } 310 310 311 static const struct snd_soc_component_driver a 311 static const struct snd_soc_component_driver atmel_soc_platform = { 312 .open = atmel_pcm_open, 312 .open = atmel_pcm_open, 313 .close = atmel_pcm_close, 313 .close = atmel_pcm_close, 314 .hw_params = atmel_pcm_hw_params, 314 .hw_params = atmel_pcm_hw_params, 315 .hw_free = atmel_pcm_hw_free, 315 .hw_free = atmel_pcm_hw_free, 316 .prepare = atmel_pcm_prepare, 316 .prepare = atmel_pcm_prepare, 317 .trigger = atmel_pcm_trigger, 317 .trigger = atmel_pcm_trigger, 318 .pointer = atmel_pcm_pointer, 318 .pointer = atmel_pcm_pointer, 319 .pcm_construct = atmel_pcm_new, 319 .pcm_construct = atmel_pcm_new, 320 }; 320 }; 321 321 322 int atmel_pcm_pdc_platform_register(struct dev 322 int atmel_pcm_pdc_platform_register(struct device *dev) 323 { 323 { 324 return devm_snd_soc_register_component 324 return devm_snd_soc_register_component(dev, &atmel_soc_platform, 325 325 NULL, 0); 326 } 326 } 327 EXPORT_SYMBOL(atmel_pcm_pdc_platform_register) 327 EXPORT_SYMBOL(atmel_pcm_pdc_platform_register); 328 328 329 MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@at 329 MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>"); 330 MODULE_DESCRIPTION("Atmel PCM module"); 330 MODULE_DESCRIPTION("Atmel PCM module"); 331 MODULE_LICENSE("GPL"); 331 MODULE_LICENSE("GPL"); 332 332
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.