ux500_pcm.c (4784B)
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 34static 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 46static 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 84static 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 = asoc_substream_to_rtd(substream); 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 123static 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 130static 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 135int 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} 157EXPORT_SYMBOL_GPL(ux500_pcm_register_platform); 158 159int ux500_pcm_unregister_platform(struct platform_device *pdev) 160{ 161 snd_dmaengine_pcm_unregister(&pdev->dev); 162 return 0; 163} 164EXPORT_SYMBOL_GPL(ux500_pcm_unregister_platform); 165 166MODULE_AUTHOR("Ola Lilja"); 167MODULE_AUTHOR("Roger Nilsson"); 168MODULE_DESCRIPTION("ASoC UX500 driver"); 169MODULE_LICENSE("GPL v2");