Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Copyright (C) 2012, Analog Devices Inc.
0004  *  Author: Lars-Peter Clausen <lars@metafoo.de>
0005  *
0006  *  Based on:
0007  *  imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
0008  *  mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc.
0009  *  ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
0010  *            Copyright (C) 2006 Applied Data Systems
0011  */
0012 #include <linux/module.h>
0013 #include <linux/init.h>
0014 #include <linux/dmaengine.h>
0015 #include <linux/slab.h>
0016 #include <sound/pcm.h>
0017 #include <sound/pcm_params.h>
0018 #include <sound/soc.h>
0019 
0020 #include <sound/dmaengine_pcm.h>
0021 
0022 struct dmaengine_pcm_runtime_data {
0023     struct dma_chan *dma_chan;
0024     dma_cookie_t cookie;
0025 
0026     unsigned int pos;
0027 };
0028 
0029 static inline struct dmaengine_pcm_runtime_data *substream_to_prtd(
0030     const struct snd_pcm_substream *substream)
0031 {
0032     return substream->runtime->private_data;
0033 }
0034 
0035 struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream)
0036 {
0037     struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
0038 
0039     return prtd->dma_chan;
0040 }
0041 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_chan);
0042 
0043 /**
0044  * snd_hwparams_to_dma_slave_config - Convert hw_params to dma_slave_config
0045  * @substream: PCM substream
0046  * @params: hw_params
0047  * @slave_config: DMA slave config
0048  *
0049  * This function can be used to initialize a dma_slave_config from a substream
0050  * and hw_params in a dmaengine based PCM driver implementation.
0051  *
0052  * Return: zero if successful, or a negative error code
0053  */
0054 int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream,
0055     const struct snd_pcm_hw_params *params,
0056     struct dma_slave_config *slave_config)
0057 {
0058     enum dma_slave_buswidth buswidth;
0059     int bits;
0060 
0061     bits = params_physical_width(params);
0062     if (bits < 8 || bits > 64)
0063         return -EINVAL;
0064     else if (bits == 8)
0065         buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
0066     else if (bits == 16)
0067         buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
0068     else if (bits == 24)
0069         buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
0070     else if (bits <= 32)
0071         buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
0072     else
0073         buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES;
0074 
0075     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
0076         slave_config->direction = DMA_MEM_TO_DEV;
0077         slave_config->dst_addr_width = buswidth;
0078     } else {
0079         slave_config->direction = DMA_DEV_TO_MEM;
0080         slave_config->src_addr_width = buswidth;
0081     }
0082 
0083     slave_config->device_fc = false;
0084 
0085     return 0;
0086 }
0087 EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config);
0088 
0089 /**
0090  * snd_dmaengine_pcm_set_config_from_dai_data() - Initializes a dma slave config
0091  *  using DAI DMA data.
0092  * @substream: PCM substream
0093  * @dma_data: DAI DMA data
0094  * @slave_config: DMA slave configuration
0095  *
0096  * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width
0097  * fields of the DMA slave config from the same fields of the DAI DMA
0098  * data struct. The src and dst fields will be initialized depending on the
0099  * direction of the substream. If the substream is a playback stream the dst
0100  * fields will be initialized, if it is a capture stream the src fields will be
0101  * initialized. The {dst,src}_addr_width field will only be initialized if the
0102  * SND_DMAENGINE_PCM_DAI_FLAG_PACK flag is set or if the addr_width field of
0103  * the DAI DMA data struct is not equal to DMA_SLAVE_BUSWIDTH_UNDEFINED. If
0104  * both conditions are met the latter takes priority.
0105  */
0106 void snd_dmaengine_pcm_set_config_from_dai_data(
0107     const struct snd_pcm_substream *substream,
0108     const struct snd_dmaengine_dai_dma_data *dma_data,
0109     struct dma_slave_config *slave_config)
0110 {
0111     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
0112         slave_config->dst_addr = dma_data->addr;
0113         slave_config->dst_maxburst = dma_data->maxburst;
0114         if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
0115             slave_config->dst_addr_width =
0116                 DMA_SLAVE_BUSWIDTH_UNDEFINED;
0117         if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
0118             slave_config->dst_addr_width = dma_data->addr_width;
0119     } else {
0120         slave_config->src_addr = dma_data->addr;
0121         slave_config->src_maxburst = dma_data->maxburst;
0122         if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
0123             slave_config->src_addr_width =
0124                 DMA_SLAVE_BUSWIDTH_UNDEFINED;
0125         if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
0126             slave_config->src_addr_width = dma_data->addr_width;
0127     }
0128 
0129     slave_config->peripheral_config = dma_data->peripheral_config;
0130     slave_config->peripheral_size = dma_data->peripheral_size;
0131 }
0132 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data);
0133 
0134 static void dmaengine_pcm_dma_complete(void *arg)
0135 {
0136     struct snd_pcm_substream *substream = arg;
0137     struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
0138 
0139     prtd->pos += snd_pcm_lib_period_bytes(substream);
0140     if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream))
0141         prtd->pos = 0;
0142 
0143     snd_pcm_period_elapsed(substream);
0144 }
0145 
0146 static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
0147 {
0148     struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
0149     struct dma_chan *chan = prtd->dma_chan;
0150     struct dma_async_tx_descriptor *desc;
0151     enum dma_transfer_direction direction;
0152     unsigned long flags = DMA_CTRL_ACK;
0153 
0154     direction = snd_pcm_substream_to_dma_direction(substream);
0155 
0156     if (!substream->runtime->no_period_wakeup)
0157         flags |= DMA_PREP_INTERRUPT;
0158 
0159     prtd->pos = 0;
0160     desc = dmaengine_prep_dma_cyclic(chan,
0161         substream->runtime->dma_addr,
0162         snd_pcm_lib_buffer_bytes(substream),
0163         snd_pcm_lib_period_bytes(substream), direction, flags);
0164 
0165     if (!desc)
0166         return -ENOMEM;
0167 
0168     desc->callback = dmaengine_pcm_dma_complete;
0169     desc->callback_param = substream;
0170     prtd->cookie = dmaengine_submit(desc);
0171 
0172     return 0;
0173 }
0174 
0175 /**
0176  * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation
0177  * @substream: PCM substream
0178  * @cmd: Trigger command
0179  *
0180  * This function can be used as the PCM trigger callback for dmaengine based PCM
0181  * driver implementations.
0182  *
0183  * Return: 0 on success, a negative error code otherwise
0184  */
0185 int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
0186 {
0187     struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
0188     struct snd_pcm_runtime *runtime = substream->runtime;
0189     int ret;
0190 
0191     switch (cmd) {
0192     case SNDRV_PCM_TRIGGER_START:
0193         ret = dmaengine_pcm_prepare_and_submit(substream);
0194         if (ret)
0195             return ret;
0196         dma_async_issue_pending(prtd->dma_chan);
0197         break;
0198     case SNDRV_PCM_TRIGGER_RESUME:
0199     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0200         dmaengine_resume(prtd->dma_chan);
0201         break;
0202     case SNDRV_PCM_TRIGGER_SUSPEND:
0203         if (runtime->info & SNDRV_PCM_INFO_PAUSE)
0204             dmaengine_pause(prtd->dma_chan);
0205         else
0206             dmaengine_terminate_async(prtd->dma_chan);
0207         break;
0208     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0209         dmaengine_pause(prtd->dma_chan);
0210         break;
0211     case SNDRV_PCM_TRIGGER_STOP:
0212         dmaengine_terminate_async(prtd->dma_chan);
0213         break;
0214     default:
0215         return -EINVAL;
0216     }
0217 
0218     return 0;
0219 }
0220 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger);
0221 
0222 /**
0223  * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation
0224  * @substream: PCM substream
0225  *
0226  * This function is deprecated and should not be used by new drivers, as its
0227  * results may be unreliable.
0228  *
0229  * Return: PCM position in frames
0230  */
0231 snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream)
0232 {
0233     struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
0234     return bytes_to_frames(substream->runtime, prtd->pos);
0235 }
0236 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue);
0237 
0238 /**
0239  * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation
0240  * @substream: PCM substream
0241  *
0242  * This function can be used as the PCM pointer callback for dmaengine based PCM
0243  * driver implementations.
0244  *
0245  * Return: PCM position in frames
0246  */
0247 snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream)
0248 {
0249     struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
0250     struct snd_pcm_runtime *runtime = substream->runtime;
0251     struct dma_tx_state state;
0252     enum dma_status status;
0253     unsigned int buf_size;
0254     unsigned int pos = 0;
0255 
0256     status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
0257     if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) {
0258         buf_size = snd_pcm_lib_buffer_bytes(substream);
0259         if (state.residue > 0 && state.residue <= buf_size)
0260             pos = buf_size - state.residue;
0261 
0262         runtime->delay = bytes_to_frames(runtime,
0263                          state.in_flight_bytes);
0264     }
0265 
0266     return bytes_to_frames(runtime, pos);
0267 }
0268 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer);
0269 
0270 /**
0271  * snd_dmaengine_pcm_request_channel - Request channel for the dmaengine PCM
0272  * @filter_fn: Filter function used to request the DMA channel
0273  * @filter_data: Data passed to the DMA filter function
0274  *
0275  * This function request a DMA channel for usage with dmaengine PCM.
0276  *
0277  * Return: NULL or the requested DMA channel
0278  */
0279 struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn,
0280     void *filter_data)
0281 {
0282     dma_cap_mask_t mask;
0283 
0284     dma_cap_zero(mask);
0285     dma_cap_set(DMA_SLAVE, mask);
0286     dma_cap_set(DMA_CYCLIC, mask);
0287 
0288     return dma_request_channel(mask, filter_fn, filter_data);
0289 }
0290 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel);
0291 
0292 /**
0293  * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream
0294  * @substream: PCM substream
0295  * @chan: DMA channel to use for data transfers
0296  *
0297  * The function should usually be called from the pcm open callback. Note that
0298  * this function will use private_data field of the substream's runtime. So it
0299  * is not available to your pcm driver implementation.
0300  *
0301  * Return: 0 on success, a negative error code otherwise
0302  */
0303 int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
0304     struct dma_chan *chan)
0305 {
0306     struct dmaengine_pcm_runtime_data *prtd;
0307     int ret;
0308 
0309     if (!chan)
0310         return -ENXIO;
0311 
0312     ret = snd_pcm_hw_constraint_integer(substream->runtime,
0313                         SNDRV_PCM_HW_PARAM_PERIODS);
0314     if (ret < 0)
0315         return ret;
0316 
0317     prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
0318     if (!prtd)
0319         return -ENOMEM;
0320 
0321     prtd->dma_chan = chan;
0322 
0323     substream->runtime->private_data = prtd;
0324 
0325     return 0;
0326 }
0327 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open);
0328 
0329 /**
0330  * snd_dmaengine_pcm_open_request_chan - Open a dmaengine based PCM substream and request channel
0331  * @substream: PCM substream
0332  * @filter_fn: Filter function used to request the DMA channel
0333  * @filter_data: Data passed to the DMA filter function
0334  *
0335  * This function will request a DMA channel using the passed filter function and
0336  * data. The function should usually be called from the pcm open callback. Note
0337  * that this function will use private_data field of the substream's runtime. So
0338  * it is not available to your pcm driver implementation.
0339  *
0340  * Return: 0 on success, a negative error code otherwise
0341  */
0342 int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream,
0343     dma_filter_fn filter_fn, void *filter_data)
0344 {
0345     return snd_dmaengine_pcm_open(substream,
0346             snd_dmaengine_pcm_request_channel(filter_fn, filter_data));
0347 }
0348 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan);
0349 
0350 /**
0351  * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream
0352  * @substream: PCM substream
0353  *
0354  * Return: 0 on success, a negative error code otherwise
0355  */
0356 int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream)
0357 {
0358     struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
0359 
0360     dmaengine_synchronize(prtd->dma_chan);
0361     kfree(prtd);
0362 
0363     return 0;
0364 }
0365 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close);
0366 
0367 /**
0368  * snd_dmaengine_pcm_close_release_chan - Close a dmaengine based PCM
0369  *                    substream and release channel
0370  * @substream: PCM substream
0371  *
0372  * Releases the DMA channel associated with the PCM substream.
0373  *
0374  * Return: zero if successful, or a negative error code
0375  */
0376 int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
0377 {
0378     struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
0379 
0380     dmaengine_synchronize(prtd->dma_chan);
0381     dma_release_channel(prtd->dma_chan);
0382     kfree(prtd);
0383 
0384     return 0;
0385 }
0386 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan);
0387 
0388 /**
0389  * snd_dmaengine_pcm_refine_runtime_hwparams - Refine runtime hw params
0390  * @substream: PCM substream
0391  * @dma_data: DAI DMA data
0392  * @hw: PCM hw params
0393  * @chan: DMA channel to use for data transfers
0394  *
0395  * This function will query DMA capability, then refine the pcm hardware
0396  * parameters.
0397  *
0398  * Return: 0 on success, a negative error code otherwise
0399  */
0400 int snd_dmaengine_pcm_refine_runtime_hwparams(
0401     struct snd_pcm_substream *substream,
0402     struct snd_dmaengine_dai_dma_data *dma_data,
0403     struct snd_pcm_hardware *hw,
0404     struct dma_chan *chan)
0405 {
0406     struct dma_slave_caps dma_caps;
0407     u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
0408               BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
0409               BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
0410     snd_pcm_format_t i;
0411     int ret = 0;
0412 
0413     if (!hw || !chan || !dma_data)
0414         return -EINVAL;
0415 
0416     ret = dma_get_slave_caps(chan, &dma_caps);
0417     if (ret == 0) {
0418         if (dma_caps.cmd_pause && dma_caps.cmd_resume)
0419             hw->info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
0420         if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
0421             hw->info |= SNDRV_PCM_INFO_BATCH;
0422 
0423         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
0424             addr_widths = dma_caps.dst_addr_widths;
0425         else
0426             addr_widths = dma_caps.src_addr_widths;
0427     }
0428 
0429     /*
0430      * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
0431      * hw.formats set to 0, meaning no restrictions are in place.
0432      * In this case it's the responsibility of the DAI driver to
0433      * provide the supported format information.
0434      */
0435     if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
0436         /*
0437          * Prepare formats mask for valid/allowed sample types. If the
0438          * dma does not have support for the given physical word size,
0439          * it needs to be masked out so user space can not use the
0440          * format which produces corrupted audio.
0441          * In case the dma driver does not implement the slave_caps the
0442          * default assumption is that it supports 1, 2 and 4 bytes
0443          * widths.
0444          */
0445         pcm_for_each_format(i) {
0446             int bits = snd_pcm_format_physical_width(i);
0447 
0448             /*
0449              * Enable only samples with DMA supported physical
0450              * widths
0451              */
0452             switch (bits) {
0453             case 8:
0454             case 16:
0455             case 24:
0456             case 32:
0457             case 64:
0458                 if (addr_widths & (1 << (bits / 8)))
0459                     hw->formats |= pcm_format_to_bits(i);
0460                 break;
0461             default:
0462                 /* Unsupported types */
0463                 break;
0464             }
0465         }
0466 
0467     return ret;
0468 }
0469 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_refine_runtime_hwparams);
0470 
0471 MODULE_LICENSE("GPL");