Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * atmel-pcm.c  --  ALSA PCM interface for the Atmel atmel SoC.
0004  *
0005  *  Copyright (C) 2005 SAN People
0006  *  Copyright (C) 2008 Atmel
0007  *
0008  * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
0009  *
0010  * Based on at91-pcm. by:
0011  * Frank Mandarino <fmandarino@endrelia.com>
0012  * Copyright 2006 Endrelia Technologies Inc.
0013  *
0014  * Based on pxa2xx-pcm.c by:
0015  *
0016  * Author:  Nicolas Pitre
0017  * Created: Nov 30, 2004
0018  * Copyright:   (C) 2004 MontaVista Software, Inc.
0019  */
0020 
0021 #include <linux/module.h>
0022 #include <linux/init.h>
0023 #include <linux/platform_device.h>
0024 #include <linux/slab.h>
0025 #include <linux/dma-mapping.h>
0026 #include <linux/atmel_pdc.h>
0027 #include <linux/atmel-ssc.h>
0028 
0029 #include <sound/core.h>
0030 #include <sound/pcm.h>
0031 #include <sound/pcm_params.h>
0032 #include <sound/soc.h>
0033 
0034 #include "atmel-pcm.h"
0035 
0036 
0037 static int atmel_pcm_new(struct snd_soc_component *component,
0038              struct snd_soc_pcm_runtime *rtd)
0039 {
0040     struct snd_card *card = rtd->card->snd_card;
0041     int ret;
0042 
0043     ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
0044     if (ret)
0045         return ret;
0046 
0047     snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
0048                        card->dev, ATMEL_SSC_DMABUF_SIZE,
0049                        ATMEL_SSC_DMABUF_SIZE);
0050 
0051     return 0;
0052 }
0053 
0054 /*--------------------------------------------------------------------------*\
0055  * Hardware definition
0056 \*--------------------------------------------------------------------------*/
0057 /* TODO: These values were taken from the AT91 platform driver, check
0058  *   them against real values for AT32
0059  */
0060 static const struct snd_pcm_hardware atmel_pcm_hardware = {
0061     .info           = SNDRV_PCM_INFO_MMAP |
0062                   SNDRV_PCM_INFO_MMAP_VALID |
0063                   SNDRV_PCM_INFO_INTERLEAVED |
0064                   SNDRV_PCM_INFO_PAUSE,
0065     .period_bytes_min   = 32,
0066     .period_bytes_max   = 8192,
0067     .periods_min        = 2,
0068     .periods_max        = 1024,
0069     .buffer_bytes_max   = ATMEL_SSC_DMABUF_SIZE,
0070 };
0071 
0072 
0073 /*--------------------------------------------------------------------------*\
0074  * Data types
0075 \*--------------------------------------------------------------------------*/
0076 struct atmel_runtime_data {
0077     struct atmel_pcm_dma_params *params;
0078     dma_addr_t dma_buffer;      /* physical address of dma buffer */
0079     dma_addr_t dma_buffer_end;  /* first address beyond DMA buffer */
0080     size_t period_size;
0081 
0082     dma_addr_t period_ptr;      /* physical address of next period */
0083 };
0084 
0085 /*--------------------------------------------------------------------------*\
0086  * ISR
0087 \*--------------------------------------------------------------------------*/
0088 static void atmel_pcm_dma_irq(u32 ssc_sr,
0089     struct snd_pcm_substream *substream)
0090 {
0091     struct atmel_runtime_data *prtd = substream->runtime->private_data;
0092     struct atmel_pcm_dma_params *params = prtd->params;
0093     static int count;
0094 
0095     count++;
0096 
0097     if (ssc_sr & params->mask->ssc_endbuf) {
0098         pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
0099                 substream->stream == SNDRV_PCM_STREAM_PLAYBACK
0100                 ? "underrun" : "overrun",
0101                 params->name, ssc_sr, count);
0102 
0103         /* re-start the PDC */
0104         ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
0105                params->mask->pdc_disable);
0106         prtd->period_ptr += prtd->period_size;
0107         if (prtd->period_ptr >= prtd->dma_buffer_end)
0108             prtd->period_ptr = prtd->dma_buffer;
0109 
0110         ssc_writex(params->ssc->regs, params->pdc->xpr,
0111                prtd->period_ptr);
0112         ssc_writex(params->ssc->regs, params->pdc->xcr,
0113                prtd->period_size / params->pdc_xfer_size);
0114         ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
0115                params->mask->pdc_enable);
0116     }
0117 
0118     if (ssc_sr & params->mask->ssc_endx) {
0119         /* Load the PDC next pointer and counter registers */
0120         prtd->period_ptr += prtd->period_size;
0121         if (prtd->period_ptr >= prtd->dma_buffer_end)
0122             prtd->period_ptr = prtd->dma_buffer;
0123 
0124         ssc_writex(params->ssc->regs, params->pdc->xnpr,
0125                prtd->period_ptr);
0126         ssc_writex(params->ssc->regs, params->pdc->xncr,
0127                prtd->period_size / params->pdc_xfer_size);
0128     }
0129 
0130     snd_pcm_period_elapsed(substream);
0131 }
0132 
0133 
0134 /*--------------------------------------------------------------------------*\
0135  * PCM operations
0136 \*--------------------------------------------------------------------------*/
0137 static int atmel_pcm_hw_params(struct snd_soc_component *component,
0138                    struct snd_pcm_substream *substream,
0139                    struct snd_pcm_hw_params *params)
0140 {
0141     struct snd_pcm_runtime *runtime = substream->runtime;
0142     struct atmel_runtime_data *prtd = runtime->private_data;
0143     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0144 
0145     /* this may get called several times by oss emulation
0146      * with different params */
0147 
0148     prtd->params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
0149     prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
0150 
0151     prtd->dma_buffer = runtime->dma_addr;
0152     prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
0153     prtd->period_size = params_period_bytes(params);
0154 
0155     pr_debug("atmel-pcm: "
0156         "hw_params: DMA for %s initialized "
0157         "(dma_bytes=%zu, period_size=%zu)\n",
0158         prtd->params->name,
0159         runtime->dma_bytes,
0160         prtd->period_size);
0161     return 0;
0162 }
0163 
0164 static int atmel_pcm_hw_free(struct snd_soc_component *component,
0165                  struct snd_pcm_substream *substream)
0166 {
0167     struct atmel_runtime_data *prtd = substream->runtime->private_data;
0168     struct atmel_pcm_dma_params *params = prtd->params;
0169 
0170     if (params != NULL) {
0171         ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
0172                params->mask->pdc_disable);
0173         prtd->params->dma_intr_handler = NULL;
0174     }
0175 
0176     return 0;
0177 }
0178 
0179 static int atmel_pcm_prepare(struct snd_soc_component *component,
0180                  struct snd_pcm_substream *substream)
0181 {
0182     struct atmel_runtime_data *prtd = substream->runtime->private_data;
0183     struct atmel_pcm_dma_params *params = prtd->params;
0184 
0185     ssc_writex(params->ssc->regs, SSC_IDR,
0186            params->mask->ssc_endx | params->mask->ssc_endbuf);
0187     ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
0188            params->mask->pdc_disable);
0189     return 0;
0190 }
0191 
0192 static int atmel_pcm_trigger(struct snd_soc_component *component,
0193                  struct snd_pcm_substream *substream, int cmd)
0194 {
0195     struct snd_pcm_runtime *rtd = substream->runtime;
0196     struct atmel_runtime_data *prtd = rtd->private_data;
0197     struct atmel_pcm_dma_params *params = prtd->params;
0198     int ret = 0;
0199 
0200     pr_debug("atmel-pcm:buffer_size = %ld,"
0201         "dma_area = %p, dma_bytes = %zu\n",
0202         rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
0203 
0204     switch (cmd) {
0205     case SNDRV_PCM_TRIGGER_START:
0206         prtd->period_ptr = prtd->dma_buffer;
0207 
0208         ssc_writex(params->ssc->regs, params->pdc->xpr,
0209                prtd->period_ptr);
0210         ssc_writex(params->ssc->regs, params->pdc->xcr,
0211                prtd->period_size / params->pdc_xfer_size);
0212 
0213         prtd->period_ptr += prtd->period_size;
0214         ssc_writex(params->ssc->regs, params->pdc->xnpr,
0215                prtd->period_ptr);
0216         ssc_writex(params->ssc->regs, params->pdc->xncr,
0217                prtd->period_size / params->pdc_xfer_size);
0218 
0219         pr_debug("atmel-pcm: trigger: "
0220             "period_ptr=%lx, xpr=%u, "
0221             "xcr=%u, xnpr=%u, xncr=%u\n",
0222             (unsigned long)prtd->period_ptr,
0223             ssc_readx(params->ssc->regs, params->pdc->xpr),
0224             ssc_readx(params->ssc->regs, params->pdc->xcr),
0225             ssc_readx(params->ssc->regs, params->pdc->xnpr),
0226             ssc_readx(params->ssc->regs, params->pdc->xncr));
0227 
0228         ssc_writex(params->ssc->regs, SSC_IER,
0229                params->mask->ssc_endx | params->mask->ssc_endbuf);
0230         ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
0231                params->mask->pdc_enable);
0232 
0233         pr_debug("sr=%u imr=%u\n",
0234             ssc_readx(params->ssc->regs, SSC_SR),
0235             ssc_readx(params->ssc->regs, SSC_IER));
0236         break;      /* SNDRV_PCM_TRIGGER_START */
0237 
0238     case SNDRV_PCM_TRIGGER_STOP:
0239     case SNDRV_PCM_TRIGGER_SUSPEND:
0240     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0241         ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
0242                params->mask->pdc_disable);
0243         break;
0244 
0245     case SNDRV_PCM_TRIGGER_RESUME:
0246     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0247         ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
0248                params->mask->pdc_enable);
0249         break;
0250 
0251     default:
0252         ret = -EINVAL;
0253     }
0254 
0255     return ret;
0256 }
0257 
0258 static snd_pcm_uframes_t atmel_pcm_pointer(struct snd_soc_component *component,
0259                        struct snd_pcm_substream *substream)
0260 {
0261     struct snd_pcm_runtime *runtime = substream->runtime;
0262     struct atmel_runtime_data *prtd = runtime->private_data;
0263     struct atmel_pcm_dma_params *params = prtd->params;
0264     dma_addr_t ptr;
0265     snd_pcm_uframes_t x;
0266 
0267     ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
0268     x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
0269 
0270     if (x == runtime->buffer_size)
0271         x = 0;
0272 
0273     return x;
0274 }
0275 
0276 static int atmel_pcm_open(struct snd_soc_component *component,
0277               struct snd_pcm_substream *substream)
0278 {
0279     struct snd_pcm_runtime *runtime = substream->runtime;
0280     struct atmel_runtime_data *prtd;
0281     int ret = 0;
0282 
0283     snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
0284 
0285     /* ensure that buffer size is a multiple of period size */
0286     ret = snd_pcm_hw_constraint_integer(runtime,
0287                         SNDRV_PCM_HW_PARAM_PERIODS);
0288     if (ret < 0)
0289         goto out;
0290 
0291     prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
0292     if (prtd == NULL) {
0293         ret = -ENOMEM;
0294         goto out;
0295     }
0296     runtime->private_data = prtd;
0297 
0298  out:
0299     return ret;
0300 }
0301 
0302 static int atmel_pcm_close(struct snd_soc_component *component,
0303                struct snd_pcm_substream *substream)
0304 {
0305     struct atmel_runtime_data *prtd = substream->runtime->private_data;
0306 
0307     kfree(prtd);
0308     return 0;
0309 }
0310 
0311 static const struct snd_soc_component_driver atmel_soc_platform = {
0312     .open       = atmel_pcm_open,
0313     .close      = atmel_pcm_close,
0314     .hw_params  = atmel_pcm_hw_params,
0315     .hw_free    = atmel_pcm_hw_free,
0316     .prepare    = atmel_pcm_prepare,
0317     .trigger    = atmel_pcm_trigger,
0318     .pointer    = atmel_pcm_pointer,
0319     .pcm_construct  = atmel_pcm_new,
0320 };
0321 
0322 int atmel_pcm_pdc_platform_register(struct device *dev)
0323 {
0324     return devm_snd_soc_register_component(dev, &atmel_soc_platform,
0325                            NULL, 0);
0326 }
0327 EXPORT_SYMBOL(atmel_pcm_pdc_platform_register);
0328 
0329 MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
0330 MODULE_DESCRIPTION("Atmel PCM module");
0331 MODULE_LICENSE("GPL");