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