1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Au12x0/Au1550 PSC ALSA ASoC audio support. 4 * 5 * (c) 2007-2008 MSC Vertriebsges.m.b.H., 6 * Manuel Lauss <manuel.lauss@gmail.com> 7 * 8 * DMA glue for Au1x-PSC audio. 9 */ 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 18 #include <sound/core.h> 19 #include <sound/pcm.h> 20 #include <sound/pcm_params.h> 21 #include <sound/soc.h> 22 23 #include <asm/mach-au1x00/au1000.h> 24 #include <asm/mach-au1x00/au1xxx_dbdma.h> 25 #include <asm/mach-au1x00/au1xxx_psc.h> 26 27 #include "psc.h" 28 29 /*#define PCM_DEBUG*/ 30 31 #define DRV_NAME "dbdma2" 32 33 #define MSG(x...) printk(KERN_INFO "au1xpsc_pcm: " x) 34 #ifdef PCM_DEBUG 35 #define DBG MSG 36 #else 37 #define DBG(x...) do {} while (0) 38 #endif 39 40 struct au1xpsc_audio_dmadata { 41 /* DDMA control data */ 42 unsigned int ddma_id; /* DDMA direction ID for this PSC */ 43 u32 ddma_chan; /* DDMA context */ 44 45 /* PCM context (for irq handlers) */ 46 struct snd_pcm_substream *substream; 47 unsigned long curr_period; /* current segment DDMA is working on */ 48 unsigned long q_period; /* queue period(s) */ 49 dma_addr_t dma_area; /* address of queued DMA area */ 50 dma_addr_t dma_area_s; /* start address of DMA area */ 51 unsigned long pos; /* current byte position being played */ 52 unsigned long periods; /* number of SG segments in total */ 53 unsigned long period_bytes; /* size in bytes of one SG segment */ 54 55 /* runtime data */ 56 int msbits; 57 }; 58 59 /* 60 * These settings are somewhat okay, at least on my machine audio plays 61 * almost skip-free. Especially the 64kB buffer seems to help a LOT. 62 */ 63 #define AU1XPSC_PERIOD_MIN_BYTES 1024 64 #define AU1XPSC_BUFFER_MIN_BYTES 65536 65 66 /* PCM hardware DMA capabilities - platform specific */ 67 static const struct snd_pcm_hardware au1xpsc_pcm_hardware = { 68 .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 69 SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BATCH, 70 .period_bytes_min = AU1XPSC_PERIOD_MIN_BYTES, 71 .period_bytes_max = 4096 * 1024 - 1, 72 .periods_min = 2, 73 .periods_max = 4096, /* 2 to as-much-as-you-like */ 74 .buffer_bytes_max = 4096 * 1024 - 1, 75 .fifo_size = 16, /* fifo entries of AC97/I2S PSC */ 76 }; 77 78 static void au1x_pcm_queue_tx(struct au1xpsc_audio_dmadata *cd) 79 { 80 au1xxx_dbdma_put_source(cd->ddma_chan, cd->dma_area, 81 cd->period_bytes, DDMA_FLAGS_IE); 82 83 /* update next-to-queue period */ 84 ++cd->q_period; 85 cd->dma_area += cd->period_bytes; 86 if (cd->q_period >= cd->periods) { 87 cd->q_period = 0; 88 cd->dma_area = cd->dma_area_s; 89 } 90 } 91 92 static void au1x_pcm_queue_rx(struct au1xpsc_audio_dmadata *cd) 93 { 94 au1xxx_dbdma_put_dest(cd->ddma_chan, cd->dma_area, 95 cd->period_bytes, DDMA_FLAGS_IE); 96 97 /* update next-to-queue period */ 98 ++cd->q_period; 99 cd->dma_area += cd->period_bytes; 100 if (cd->q_period >= cd->periods) { 101 cd->q_period = 0; 102 cd->dma_area = cd->dma_area_s; 103 } 104 } 105 106 static void au1x_pcm_dmatx_cb(int irq, void *dev_id) 107 { 108 struct au1xpsc_audio_dmadata *cd = dev_id; 109 110 cd->pos += cd->period_bytes; 111 if (++cd->curr_period >= cd->periods) { 112 cd->pos = 0; 113 cd->curr_period = 0; 114 } 115 snd_pcm_period_elapsed(cd->substream); 116 au1x_pcm_queue_tx(cd); 117 } 118 119 static void au1x_pcm_dmarx_cb(int irq, void *dev_id) 120 { 121 struct au1xpsc_audio_dmadata *cd = dev_id; 122 123 cd->pos += cd->period_bytes; 124 if (++cd->curr_period >= cd->periods) { 125 cd->pos = 0; 126 cd->curr_period = 0; 127 } 128 snd_pcm_period_elapsed(cd->substream); 129 au1x_pcm_queue_rx(cd); 130 } 131 132 static void au1x_pcm_dbdma_free(struct au1xpsc_audio_dmadata *pcd) 133 { 134 if (pcd->ddma_chan) { 135 au1xxx_dbdma_stop(pcd->ddma_chan); 136 au1xxx_dbdma_reset(pcd->ddma_chan); 137 au1xxx_dbdma_chan_free(pcd->ddma_chan); 138 pcd->ddma_chan = 0; 139 pcd->msbits = 0; 140 } 141 } 142 143 /* in case of missing DMA ring or changed TX-source / RX-dest bit widths, 144 * allocate (or reallocate) a 2-descriptor DMA ring with bit depth according 145 * to ALSA-supplied sample depth. This is due to limitations in the dbdma api 146 * (cannot adjust source/dest widths of already allocated descriptor ring). 147 */ 148 static int au1x_pcm_dbdma_realloc(struct au1xpsc_audio_dmadata *pcd, 149 int stype, int msbits) 150 { 151 /* DMA only in 8/16/32 bit widths */ 152 if (msbits == 24) 153 msbits = 32; 154 155 /* check current config: correct bits and descriptors allocated? */ 156 if ((pcd->ddma_chan) && (msbits == pcd->msbits)) 157 goto out; /* all ok! */ 158 159 au1x_pcm_dbdma_free(pcd); 160 161 if (stype == SNDRV_PCM_STREAM_CAPTURE) 162 pcd->ddma_chan = au1xxx_dbdma_chan_alloc(pcd->ddma_id, 163 DSCR_CMD0_ALWAYS, 164 au1x_pcm_dmarx_cb, (void *)pcd); 165 else 166 pcd->ddma_chan = au1xxx_dbdma_chan_alloc(DSCR_CMD0_ALWAYS, 167 pcd->ddma_id, 168 au1x_pcm_dmatx_cb, (void *)pcd); 169 170 if (!pcd->ddma_chan) 171 return -ENOMEM; 172 173 au1xxx_dbdma_set_devwidth(pcd->ddma_chan, msbits); 174 au1xxx_dbdma_ring_alloc(pcd->ddma_chan, 2); 175 176 pcd->msbits = msbits; 177 178 au1xxx_dbdma_stop(pcd->ddma_chan); 179 au1xxx_dbdma_reset(pcd->ddma_chan); 180 181 out: 182 return 0; 183 } 184 185 static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss, 186 struct snd_soc_component *component) 187 { 188 struct au1xpsc_audio_dmadata *pcd = snd_soc_component_get_drvdata(component); 189 return &pcd[ss->stream]; 190 } 191 192 static int au1xpsc_pcm_hw_params(struct snd_soc_component *component, 193 struct snd_pcm_substream *substream, 194 struct snd_pcm_hw_params *params) 195 { 196 struct snd_pcm_runtime *runtime = substream->runtime; 197 struct au1xpsc_audio_dmadata *pcd; 198 int stype, ret; 199 200 stype = substream->stream; 201 pcd = to_dmadata(substream, component); 202 203 DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %zu " 204 "runtime->min_align %lu\n", 205 (unsigned long)runtime->dma_area, 206 (unsigned long)runtime->dma_addr, runtime->dma_bytes, 207 runtime->min_align); 208 209 DBG("bits %d frags %d frag_bytes %d is_rx %d\n", params->msbits, 210 params_periods(params), params_period_bytes(params), stype); 211 212 ret = au1x_pcm_dbdma_realloc(pcd, stype, params->msbits); 213 if (ret) { 214 MSG("DDMA channel (re)alloc failed!\n"); 215 goto out; 216 } 217 218 pcd->substream = substream; 219 pcd->period_bytes = params_period_bytes(params); 220 pcd->periods = params_periods(params); 221 pcd->dma_area_s = pcd->dma_area = runtime->dma_addr; 222 pcd->q_period = 0; 223 pcd->curr_period = 0; 224 pcd->pos = 0; 225 226 ret = 0; 227 out: 228 return ret; 229 } 230 231 static int au1xpsc_pcm_prepare(struct snd_soc_component *component, 232 struct snd_pcm_substream *substream) 233 { 234 struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component); 235 236 au1xxx_dbdma_reset(pcd->ddma_chan); 237 238 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 239 au1x_pcm_queue_rx(pcd); 240 au1x_pcm_queue_rx(pcd); 241 } else { 242 au1x_pcm_queue_tx(pcd); 243 au1x_pcm_queue_tx(pcd); 244 } 245 246 return 0; 247 } 248 249 static int au1xpsc_pcm_trigger(struct snd_soc_component *component, 250 struct snd_pcm_substream *substream, int cmd) 251 { 252 u32 c = to_dmadata(substream, component)->ddma_chan; 253 254 switch (cmd) { 255 case SNDRV_PCM_TRIGGER_START: 256 case SNDRV_PCM_TRIGGER_RESUME: 257 au1xxx_dbdma_start(c); 258 break; 259 case SNDRV_PCM_TRIGGER_STOP: 260 case SNDRV_PCM_TRIGGER_SUSPEND: 261 au1xxx_dbdma_stop(c); 262 break; 263 default: 264 return -EINVAL; 265 } 266 return 0; 267 } 268 269 static snd_pcm_uframes_t 270 au1xpsc_pcm_pointer(struct snd_soc_component *component, 271 struct snd_pcm_substream *substream) 272 { 273 return bytes_to_frames(substream->runtime, 274 to_dmadata(substream, component)->pos); 275 } 276 277 static int au1xpsc_pcm_open(struct snd_soc_component *component, 278 struct snd_pcm_substream *substream) 279 { 280 struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component); 281 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 282 int stype = substream->stream, *dmaids; 283 284 dmaids = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream); 285 if (!dmaids) 286 return -ENODEV; /* whoa, has ordering changed? */ 287 288 pcd->ddma_id = dmaids[stype]; 289 290 snd_soc_set_runtime_hwparams(substream, &au1xpsc_pcm_hardware); 291 return 0; 292 } 293 294 static int au1xpsc_pcm_close(struct snd_soc_component *component, 295 struct snd_pcm_substream *substream) 296 { 297 au1x_pcm_dbdma_free(to_dmadata(substream, component)); 298 return 0; 299 } 300 301 static int au1xpsc_pcm_new(struct snd_soc_component *component, 302 struct snd_soc_pcm_runtime *rtd) 303 { 304 struct snd_card *card = rtd->card->snd_card; 305 struct snd_pcm *pcm = rtd->pcm; 306 307 snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 308 card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1); 309 310 return 0; 311 } 312 313 /* au1xpsc audio platform */ 314 static struct snd_soc_component_driver au1xpsc_soc_component = { 315 .name = DRV_NAME, 316 .open = au1xpsc_pcm_open, 317 .close = au1xpsc_pcm_close, 318 .hw_params = au1xpsc_pcm_hw_params, 319 .prepare = au1xpsc_pcm_prepare, 320 .trigger = au1xpsc_pcm_trigger, 321 .pointer = au1xpsc_pcm_pointer, 322 .pcm_construct = au1xpsc_pcm_new, 323 }; 324 325 static int au1xpsc_pcm_drvprobe(struct platform_device *pdev) 326 { 327 struct au1xpsc_audio_dmadata *dmadata; 328 329 dmadata = devm_kcalloc(&pdev->dev, 330 2, sizeof(struct au1xpsc_audio_dmadata), 331 GFP_KERNEL); 332 if (!dmadata) 333 return -ENOMEM; 334 335 platform_set_drvdata(pdev, dmadata); 336 337 return devm_snd_soc_register_component(&pdev->dev, 338 &au1xpsc_soc_component, NULL, 0); 339 } 340 341 static struct platform_driver au1xpsc_pcm_driver = { 342 .driver = { 343 .name = "au1xpsc-pcm", 344 }, 345 .probe = au1xpsc_pcm_drvprobe, 346 }; 347 348 module_platform_driver(au1xpsc_pcm_driver); 349 350 MODULE_LICENSE("GPL"); 351 MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver"); 352 MODULE_AUTHOR("Manuel Lauss"); 353
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.