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