0001
0002
0003
0004
0005
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");