Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * linux/sound/soc/pxa/mmp-pcm.c
0004  *
0005  * Copyright (C) 2011 Marvell International Ltd.
0006  */
0007 #include <linux/module.h>
0008 #include <linux/init.h>
0009 #include <linux/platform_device.h>
0010 #include <linux/slab.h>
0011 #include <linux/dma-mapping.h>
0012 #include <linux/dmaengine.h>
0013 #include <linux/platform_data/dma-mmp_tdma.h>
0014 #include <linux/platform_data/mmp_audio.h>
0015 
0016 #include <sound/pxa2xx-lib.h>
0017 #include <sound/core.h>
0018 #include <sound/pcm.h>
0019 #include <sound/pcm_params.h>
0020 #include <sound/soc.h>
0021 #include <sound/dmaengine_pcm.h>
0022 
0023 #define DRV_NAME "mmp-pcm"
0024 
0025 struct mmp_dma_data {
0026     int ssp_id;
0027     struct resource *dma_res;
0028 };
0029 
0030 #define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \
0031         SNDRV_PCM_INFO_MMAP_VALID | \
0032         SNDRV_PCM_INFO_INTERLEAVED |    \
0033         SNDRV_PCM_INFO_PAUSE |      \
0034         SNDRV_PCM_INFO_RESUME |     \
0035         SNDRV_PCM_INFO_NO_PERIOD_WAKEUP)
0036 
0037 static struct snd_pcm_hardware mmp_pcm_hardware[] = {
0038     {
0039         .info           = MMP_PCM_INFO,
0040         .period_bytes_min   = 1024,
0041         .period_bytes_max   = 2048,
0042         .periods_min        = 2,
0043         .periods_max        = 32,
0044         .buffer_bytes_max   = 4096,
0045         .fifo_size      = 32,
0046     },
0047     {
0048         .info           = MMP_PCM_INFO,
0049         .period_bytes_min   = 1024,
0050         .period_bytes_max   = 2048,
0051         .periods_min        = 2,
0052         .periods_max        = 32,
0053         .buffer_bytes_max   = 4096,
0054         .fifo_size      = 32,
0055     },
0056 };
0057 
0058 static int mmp_pcm_hw_params(struct snd_soc_component *component,
0059                  struct snd_pcm_substream *substream,
0060                  struct snd_pcm_hw_params *params)
0061 {
0062     struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
0063     struct dma_slave_config slave_config;
0064     int ret;
0065 
0066     ret =
0067         snd_dmaengine_pcm_prepare_slave_config(substream, params,
0068                            &slave_config);
0069     if (ret)
0070         return ret;
0071 
0072     ret = dmaengine_slave_config(chan, &slave_config);
0073     if (ret)
0074         return ret;
0075 
0076     snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
0077 
0078     return 0;
0079 }
0080 
0081 static int mmp_pcm_trigger(struct snd_soc_component *component,
0082                struct snd_pcm_substream *substream, int cmd)
0083 {
0084     return snd_dmaengine_pcm_trigger(substream, cmd);
0085 }
0086 
0087 static snd_pcm_uframes_t mmp_pcm_pointer(struct snd_soc_component *component,
0088                      struct snd_pcm_substream *substream)
0089 {
0090     return snd_dmaengine_pcm_pointer(substream);
0091 }
0092 
0093 static bool filter(struct dma_chan *chan, void *param)
0094 {
0095     struct mmp_dma_data *dma_data = param;
0096     bool found = false;
0097     char *devname;
0098 
0099     devname = kasprintf(GFP_KERNEL, "%s.%d", dma_data->dma_res->name,
0100         dma_data->ssp_id);
0101     if ((strcmp(dev_name(chan->device->dev), devname) == 0) &&
0102         (chan->chan_id == dma_data->dma_res->start)) {
0103         found = true;
0104     }
0105 
0106     kfree(devname);
0107     return found;
0108 }
0109 
0110 static int mmp_pcm_open(struct snd_soc_component *component,
0111             struct snd_pcm_substream *substream)
0112 {
0113     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0114     struct platform_device *pdev = to_platform_device(component->dev);
0115     struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
0116     struct mmp_dma_data dma_data;
0117     struct resource *r;
0118 
0119     r = platform_get_resource(pdev, IORESOURCE_DMA, substream->stream);
0120     if (!r)
0121         return -EBUSY;
0122 
0123     snd_soc_set_runtime_hwparams(substream,
0124                 &mmp_pcm_hardware[substream->stream]);
0125 
0126     dma_data.dma_res = r;
0127     dma_data.ssp_id = cpu_dai->id;
0128 
0129     return snd_dmaengine_pcm_open_request_chan(substream, filter,
0130             &dma_data);
0131 }
0132 
0133 static int mmp_pcm_close(struct snd_soc_component *component,
0134              struct snd_pcm_substream *substream)
0135 {
0136     return snd_dmaengine_pcm_close_release_chan(substream);
0137 }
0138 
0139 static int mmp_pcm_mmap(struct snd_soc_component *component,
0140             struct snd_pcm_substream *substream,
0141             struct vm_area_struct *vma)
0142 {
0143     struct snd_pcm_runtime *runtime = substream->runtime;
0144     unsigned long off = vma->vm_pgoff;
0145 
0146     vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
0147     return remap_pfn_range(vma, vma->vm_start,
0148         __phys_to_pfn(runtime->dma_addr) + off,
0149         vma->vm_end - vma->vm_start, vma->vm_page_prot);
0150 }
0151 
0152 static void mmp_pcm_free_dma_buffers(struct snd_soc_component *component,
0153                      struct snd_pcm *pcm)
0154 {
0155     struct snd_pcm_substream *substream;
0156     struct snd_dma_buffer *buf;
0157     int stream;
0158     struct gen_pool *gpool;
0159 
0160     gpool = sram_get_gpool("asram");
0161     if (!gpool)
0162         return;
0163 
0164     for (stream = 0; stream < 2; stream++) {
0165         size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
0166 
0167         substream = pcm->streams[stream].substream;
0168         if (!substream)
0169             continue;
0170 
0171         buf = &substream->dma_buffer;
0172         if (!buf->area)
0173             continue;
0174         gen_pool_free(gpool, (unsigned long)buf->area, size);
0175         buf->area = NULL;
0176     }
0177 
0178 }
0179 
0180 static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream,
0181                                 int stream)
0182 {
0183     struct snd_dma_buffer *buf = &substream->dma_buffer;
0184     size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
0185     struct gen_pool *gpool;
0186 
0187     buf->dev.type = SNDRV_DMA_TYPE_DEV;
0188     buf->dev.dev = substream->pcm->card->dev;
0189     buf->private_data = NULL;
0190 
0191     gpool = sram_get_gpool("asram");
0192     if (!gpool)
0193         return -ENOMEM;
0194 
0195     buf->area = gen_pool_dma_alloc(gpool, size, &buf->addr);
0196     if (!buf->area)
0197         return -ENOMEM;
0198     buf->bytes = size;
0199     return 0;
0200 }
0201 
0202 static int mmp_pcm_new(struct snd_soc_component *component,
0203                struct snd_soc_pcm_runtime *rtd)
0204 {
0205     struct snd_pcm_substream *substream;
0206     struct snd_pcm *pcm = rtd->pcm;
0207     int ret, stream;
0208 
0209     for (stream = 0; stream < 2; stream++) {
0210         substream = pcm->streams[stream].substream;
0211 
0212         ret = mmp_pcm_preallocate_dma_buffer(substream, stream);
0213         if (ret)
0214             goto err;
0215     }
0216 
0217     return 0;
0218 
0219 err:
0220     mmp_pcm_free_dma_buffers(component, pcm);
0221     return ret;
0222 }
0223 
0224 static const struct snd_soc_component_driver mmp_soc_component = {
0225     .name       = DRV_NAME,
0226     .open       = mmp_pcm_open,
0227     .close      = mmp_pcm_close,
0228     .hw_params  = mmp_pcm_hw_params,
0229     .trigger    = mmp_pcm_trigger,
0230     .pointer    = mmp_pcm_pointer,
0231     .mmap       = mmp_pcm_mmap,
0232     .pcm_construct  = mmp_pcm_new,
0233     .pcm_destruct   = mmp_pcm_free_dma_buffers,
0234 };
0235 
0236 static int mmp_pcm_probe(struct platform_device *pdev)
0237 {
0238     struct mmp_audio_platdata *pdata = pdev->dev.platform_data;
0239 
0240     if (pdata) {
0241         mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].buffer_bytes_max =
0242                         pdata->buffer_max_playback;
0243         mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].period_bytes_max =
0244                         pdata->period_max_playback;
0245         mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].buffer_bytes_max =
0246                         pdata->buffer_max_capture;
0247         mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].period_bytes_max =
0248                         pdata->period_max_capture;
0249     }
0250     return devm_snd_soc_register_component(&pdev->dev, &mmp_soc_component,
0251                            NULL, 0);
0252 }
0253 
0254 static struct platform_driver mmp_pcm_driver = {
0255     .driver = {
0256         .name = "mmp-pcm-audio",
0257     },
0258 
0259     .probe = mmp_pcm_probe,
0260 };
0261 
0262 module_platform_driver(mmp_pcm_driver);
0263 
0264 MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
0265 MODULE_DESCRIPTION("MMP Soc Audio DMA module");
0266 MODULE_LICENSE("GPL");
0267 MODULE_ALIAS("platform:mmp-pcm-audio");