1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) ST-Ericsson SA 2012 4 * 5 * Author: Ola Lilja <ola.o.lilja@stericsson.com>, 6 * Roger Nilsson <roger.xr.nilsson@stericsson.com> 7 * for ST-Ericsson. 8 * 9 * License terms: 10 */ 11 12 #include <asm/page.h> 13 14 #include <linux/module.h> 15 #include <linux/dma-mapping.h> 16 #include <linux/dmaengine.h> 17 #include <linux/slab.h> 18 #include <linux/platform_data/dma-ste-dma40.h> 19 20 #include <sound/pcm.h> 21 #include <sound/pcm_params.h> 22 #include <sound/soc.h> 23 #include <sound/dmaengine_pcm.h> 24 25 #include "ux500_msp_i2s.h" 26 #include "ux500_pcm.h" 27 28 #define UX500_PLATFORM_PERIODS_BYTES_MIN 128 29 #define UX500_PLATFORM_PERIODS_BYTES_MAX (64 * PAGE_SIZE) 30 #define UX500_PLATFORM_PERIODS_MIN 2 31 #define UX500_PLATFORM_PERIODS_MAX 48 32 #define UX500_PLATFORM_BUFFER_BYTES_MAX (2048 * PAGE_SIZE) 33 34 static const struct snd_pcm_hardware ux500_pcm_hw = { 35 .info = SNDRV_PCM_INFO_INTERLEAVED | 36 SNDRV_PCM_INFO_MMAP | 37 SNDRV_PCM_INFO_RESUME | 38 SNDRV_PCM_INFO_PAUSE, 39 .buffer_bytes_max = UX500_PLATFORM_BUFFER_BYTES_MAX, 40 .period_bytes_min = UX500_PLATFORM_PERIODS_BYTES_MIN, 41 .period_bytes_max = UX500_PLATFORM_PERIODS_BYTES_MAX, 42 .periods_min = UX500_PLATFORM_PERIODS_MIN, 43 .periods_max = UX500_PLATFORM_PERIODS_MAX, 44 }; 45 46 static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, 47 struct snd_pcm_substream *substream) 48 { 49 struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); 50 u16 per_data_width, mem_data_width; 51 struct stedma40_chan_cfg *dma_cfg; 52 struct ux500_msp_dma_params *dma_params; 53 54 dma_params = snd_soc_dai_get_dma_data(dai, substream); 55 dma_cfg = dma_params->dma_cfg; 56 57 mem_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 58 59 switch (dma_params->data_size) { 60 case 32: 61 per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 62 break; 63 case 16: 64 per_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 65 break; 66 case 8: 67 per_data_width = DMA_SLAVE_BUSWIDTH_1_BYTE; 68 break; 69 default: 70 per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 71 } 72 73 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 74 dma_cfg->src_info.data_width = mem_data_width; 75 dma_cfg->dst_info.data_width = per_data_width; 76 } else { 77 dma_cfg->src_info.data_width = per_data_width; 78 dma_cfg->dst_info.data_width = mem_data_width; 79 } 80 81 return snd_dmaengine_pcm_request_channel(stedma40_filter, dma_cfg); 82 } 83 84 static int ux500_pcm_prepare_slave_config(struct snd_pcm_substream *substream, 85 struct snd_pcm_hw_params *params, 86 struct dma_slave_config *slave_config) 87 { 88 struct snd_soc_pcm_runtime *rtd = substream->private_data; 89 struct msp_i2s_platform_data *pdata = asoc_rtd_to_cpu(rtd, 0)->dev->platform_data; 90 struct snd_dmaengine_dai_dma_data *snd_dma_params; 91 struct ux500_msp_dma_params *ste_dma_params; 92 dma_addr_t dma_addr; 93 int ret; 94 95 if (pdata) { 96 ste_dma_params = 97 snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 98 dma_addr = ste_dma_params->tx_rx_addr; 99 } else { 100 snd_dma_params = 101 snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 102 dma_addr = snd_dma_params->addr; 103 } 104 105 ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); 106 if (ret) 107 return ret; 108 109 slave_config->dst_maxburst = 4; 110 slave_config->src_maxburst = 4; 111 112 slave_config->src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 113 slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 114 115 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 116 slave_config->dst_addr = dma_addr; 117 else 118 slave_config->src_addr = dma_addr; 119 120 return 0; 121 } 122 123 static const struct snd_dmaengine_pcm_config ux500_dmaengine_pcm_config = { 124 .pcm_hardware = &ux500_pcm_hw, 125 .compat_request_channel = ux500_pcm_request_chan, 126 .prealloc_buffer_size = 128 * 1024, 127 .prepare_slave_config = ux500_pcm_prepare_slave_config, 128 }; 129 130 static const struct snd_dmaengine_pcm_config ux500_dmaengine_of_pcm_config = { 131 .compat_request_channel = ux500_pcm_request_chan, 132 .prepare_slave_config = ux500_pcm_prepare_slave_config, 133 }; 134 135 int ux500_pcm_register_platform(struct platform_device *pdev) 136 { 137 const struct snd_dmaengine_pcm_config *pcm_config; 138 struct device_node *np = pdev->dev.of_node; 139 int ret; 140 141 if (np) 142 pcm_config = &ux500_dmaengine_of_pcm_config; 143 else 144 pcm_config = &ux500_dmaengine_pcm_config; 145 146 ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 147 SND_DMAENGINE_PCM_FLAG_COMPAT); 148 if (ret < 0) { 149 dev_err(&pdev->dev, 150 "%s: ERROR: Failed to register platform '%s' (%d)!\n", 151 __func__, pdev->name, ret); 152 return ret; 153 } 154 155 return 0; 156 } 157 EXPORT_SYMBOL_GPL(ux500_pcm_register_platform); 158 159 int ux500_pcm_unregister_platform(struct platform_device *pdev) 160 { 161 snd_dmaengine_pcm_unregister(&pdev->dev); 162 return 0; 163 } 164 EXPORT_SYMBOL_GPL(ux500_pcm_unregister_platform); 165 166 MODULE_AUTHOR("Ola Lilja"); 167 MODULE_AUTHOR("Roger Nilsson"); 168 MODULE_DESCRIPTION("ASoC UX500 driver"); 169 MODULE_LICENSE("GPL v2"); 170