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_PERIODS_BYTES_MIN 128 32 #define UX500_PLATFORM_PERIODS_BYTES_MAX (64 * PAGE_SIZE) 33 #define UX500_PLATFORM_PERIODS_MIN 2 34 #define UX500_PLATFORM_PERIODS_MAX 48 35 #define UX500_PLATFORM_BUFFER_BYTES_MAX (2048 * PAGE_SIZE) 36 37 static const struct snd_pcm_hardware ux500_pcm_hw = { 38 .info = SNDRV_PCM_INFO_INTERLEAVED | 39 SNDRV_PCM_INFO_MMAP | 40 SNDRV_PCM_INFO_RESUME | 41 SNDRV_PCM_INFO_PAUSE, 42 .buffer_bytes_max = UX500_PLATFORM_BUFFER_BYTES_MAX, 43 .period_bytes_min = UX500_PLATFORM_PERIODS_BYTES_MIN, 44 .period_bytes_max = UX500_PLATFORM_PERIODS_BYTES_MAX, 45 .periods_min = UX500_PLATFORM_PERIODS_MIN, 46 .periods_max = UX500_PLATFORM_PERIODS_MAX, 47 }; 48 49 static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, 50 struct snd_pcm_substream *substream) 51 { 52 struct snd_soc_dai *dai = rtd->cpu_dai; 53 u16 per_data_width, mem_data_width; 54 struct stedma40_chan_cfg *dma_cfg; 55 struct ux500_msp_dma_params *dma_params; 56 57 dma_params = snd_soc_dai_get_dma_data(dai, substream); 58 dma_cfg = dma_params->dma_cfg; 59 60 mem_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 61 62 switch (dma_params->data_size) { 63 case 32: 64 per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 65 break; 66 case 16: 67 per_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 68 break; 69 case 8: 70 per_data_width = DMA_SLAVE_BUSWIDTH_1_BYTE; 71 break; 72 default: 73 per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 74 } 75 76 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 77 dma_cfg->src_info.data_width = mem_data_width; 78 dma_cfg->dst_info.data_width = per_data_width; 79 } else { 80 dma_cfg->src_info.data_width = per_data_width; 81 dma_cfg->dst_info.data_width = mem_data_width; 82 } 83 84 return snd_dmaengine_pcm_request_channel(stedma40_filter, dma_cfg); 85 } 86 87 static int ux500_pcm_prepare_slave_config(struct snd_pcm_substream *substream, 88 struct snd_pcm_hw_params *params, 89 struct dma_slave_config *slave_config) 90 { 91 struct snd_soc_pcm_runtime *rtd = substream->private_data; 92 struct msp_i2s_platform_data *pdata = rtd->cpu_dai->dev->platform_data; 93 struct snd_dmaengine_dai_dma_data *snd_dma_params; 94 struct ux500_msp_dma_params *ste_dma_params; 95 dma_addr_t dma_addr; 96 int ret; 97 98 if (pdata) { 99 ste_dma_params = 100 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 101 dma_addr = ste_dma_params->tx_rx_addr; 102 } else { 103 snd_dma_params = 104 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 105 dma_addr = snd_dma_params->addr; 106 } 107 108 ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); 109 if (ret) 110 return ret; 111 112 slave_config->dst_maxburst = 4; 113 slave_config->src_maxburst = 4; 114 115 slave_config->src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 116 slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 117 118 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 119 slave_config->dst_addr = dma_addr; 120 else 121 slave_config->src_addr = dma_addr; 122 123 return 0; 124 } 125 126 static const struct snd_dmaengine_pcm_config ux500_dmaengine_pcm_config = { 127 .pcm_hardware = &ux500_pcm_hw, 128 .compat_request_channel = ux500_pcm_request_chan, 129 .prealloc_buffer_size = 128 * 1024, 130 .prepare_slave_config = ux500_pcm_prepare_slave_config, 131 }; 132 133 static const struct snd_dmaengine_pcm_config ux500_dmaengine_of_pcm_config = { 134 .compat_request_channel = ux500_pcm_request_chan, 135 .prepare_slave_config = ux500_pcm_prepare_slave_config, 136 }; 137 138 int ux500_pcm_register_platform(struct platform_device *pdev) 139 { 140 const struct snd_dmaengine_pcm_config *pcm_config; 141 struct device_node *np = pdev->dev.of_node; 142 int ret; 143 144 if (np) 145 pcm_config = &ux500_dmaengine_of_pcm_config; 146 else 147 pcm_config = &ux500_dmaengine_pcm_config; 148 149 ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 150 SND_DMAENGINE_PCM_FLAG_COMPAT); 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 169 MODULE_AUTHOR("Ola Lilja"); 170 MODULE_AUTHOR("Roger Nilsson"); 171 MODULE_DESCRIPTION("ASoC UX500 driver"); 172 MODULE_LICENSE("GPL v2"); 173