18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson SA 2012 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Ola Lilja <ola.o.lilja@stericsson.com>, 68c2ecf20Sopenharmony_ci * Roger Nilsson <roger.xr.nilsson@stericsson.com> 78c2ecf20Sopenharmony_ci * for ST-Ericsson. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * License terms: 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <asm/page.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 168c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_data/dma-ste-dma40.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <sound/pcm.h> 218c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 228c2ecf20Sopenharmony_ci#include <sound/soc.h> 238c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "ux500_msp_i2s.h" 268c2ecf20Sopenharmony_ci#include "ux500_pcm.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define UX500_PLATFORM_PERIODS_BYTES_MIN 128 298c2ecf20Sopenharmony_ci#define UX500_PLATFORM_PERIODS_BYTES_MAX (64 * PAGE_SIZE) 308c2ecf20Sopenharmony_ci#define UX500_PLATFORM_PERIODS_MIN 2 318c2ecf20Sopenharmony_ci#define UX500_PLATFORM_PERIODS_MAX 48 328c2ecf20Sopenharmony_ci#define UX500_PLATFORM_BUFFER_BYTES_MAX (2048 * PAGE_SIZE) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware ux500_pcm_hw = { 358c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_INTERLEAVED | 368c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 378c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 388c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE, 398c2ecf20Sopenharmony_ci .buffer_bytes_max = UX500_PLATFORM_BUFFER_BYTES_MAX, 408c2ecf20Sopenharmony_ci .period_bytes_min = UX500_PLATFORM_PERIODS_BYTES_MIN, 418c2ecf20Sopenharmony_ci .period_bytes_max = UX500_PLATFORM_PERIODS_BYTES_MAX, 428c2ecf20Sopenharmony_ci .periods_min = UX500_PLATFORM_PERIODS_MIN, 438c2ecf20Sopenharmony_ci .periods_max = UX500_PLATFORM_PERIODS_MAX, 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, 478c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); 508c2ecf20Sopenharmony_ci u16 per_data_width, mem_data_width; 518c2ecf20Sopenharmony_ci struct stedma40_chan_cfg *dma_cfg; 528c2ecf20Sopenharmony_ci struct ux500_msp_dma_params *dma_params; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci dma_params = snd_soc_dai_get_dma_data(dai, substream); 558c2ecf20Sopenharmony_ci dma_cfg = dma_params->dma_cfg; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci mem_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci switch (dma_params->data_size) { 608c2ecf20Sopenharmony_ci case 32: 618c2ecf20Sopenharmony_ci per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 628c2ecf20Sopenharmony_ci break; 638c2ecf20Sopenharmony_ci case 16: 648c2ecf20Sopenharmony_ci per_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 658c2ecf20Sopenharmony_ci break; 668c2ecf20Sopenharmony_ci case 8: 678c2ecf20Sopenharmony_ci per_data_width = DMA_SLAVE_BUSWIDTH_1_BYTE; 688c2ecf20Sopenharmony_ci break; 698c2ecf20Sopenharmony_ci default: 708c2ecf20Sopenharmony_ci per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 748c2ecf20Sopenharmony_ci dma_cfg->src_info.data_width = mem_data_width; 758c2ecf20Sopenharmony_ci dma_cfg->dst_info.data_width = per_data_width; 768c2ecf20Sopenharmony_ci } else { 778c2ecf20Sopenharmony_ci dma_cfg->src_info.data_width = per_data_width; 788c2ecf20Sopenharmony_ci dma_cfg->dst_info.data_width = mem_data_width; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return snd_dmaengine_pcm_request_channel(stedma40_filter, dma_cfg); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int ux500_pcm_prepare_slave_config(struct snd_pcm_substream *substream, 858c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 868c2ecf20Sopenharmony_ci struct dma_slave_config *slave_config) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 898c2ecf20Sopenharmony_ci struct msp_i2s_platform_data *pdata = asoc_rtd_to_cpu(rtd, 0)->dev->platform_data; 908c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *snd_dma_params; 918c2ecf20Sopenharmony_ci struct ux500_msp_dma_params *ste_dma_params; 928c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 938c2ecf20Sopenharmony_ci int ret; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (pdata) { 968c2ecf20Sopenharmony_ci ste_dma_params = 978c2ecf20Sopenharmony_ci snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 988c2ecf20Sopenharmony_ci dma_addr = ste_dma_params->tx_rx_addr; 998c2ecf20Sopenharmony_ci } else { 1008c2ecf20Sopenharmony_ci snd_dma_params = 1018c2ecf20Sopenharmony_ci snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 1028c2ecf20Sopenharmony_ci dma_addr = snd_dma_params->addr; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); 1068c2ecf20Sopenharmony_ci if (ret) 1078c2ecf20Sopenharmony_ci return ret; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci slave_config->dst_maxburst = 4; 1108c2ecf20Sopenharmony_ci slave_config->src_maxburst = 4; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci slave_config->src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 1138c2ecf20Sopenharmony_ci slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1168c2ecf20Sopenharmony_ci slave_config->dst_addr = dma_addr; 1178c2ecf20Sopenharmony_ci else 1188c2ecf20Sopenharmony_ci slave_config->src_addr = dma_addr; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic const struct snd_dmaengine_pcm_config ux500_dmaengine_pcm_config = { 1248c2ecf20Sopenharmony_ci .pcm_hardware = &ux500_pcm_hw, 1258c2ecf20Sopenharmony_ci .compat_request_channel = ux500_pcm_request_chan, 1268c2ecf20Sopenharmony_ci .prealloc_buffer_size = 128 * 1024, 1278c2ecf20Sopenharmony_ci .prepare_slave_config = ux500_pcm_prepare_slave_config, 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic const struct snd_dmaengine_pcm_config ux500_dmaengine_of_pcm_config = { 1318c2ecf20Sopenharmony_ci .compat_request_channel = ux500_pcm_request_chan, 1328c2ecf20Sopenharmony_ci .prepare_slave_config = ux500_pcm_prepare_slave_config, 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ciint ux500_pcm_register_platform(struct platform_device *pdev) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci const struct snd_dmaengine_pcm_config *pcm_config; 1388c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 1398c2ecf20Sopenharmony_ci int ret; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (np) 1428c2ecf20Sopenharmony_ci pcm_config = &ux500_dmaengine_of_pcm_config; 1438c2ecf20Sopenharmony_ci else 1448c2ecf20Sopenharmony_ci pcm_config = &ux500_dmaengine_pcm_config; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 1478c2ecf20Sopenharmony_ci SND_DMAENGINE_PCM_FLAG_COMPAT); 1488c2ecf20Sopenharmony_ci if (ret < 0) { 1498c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 1508c2ecf20Sopenharmony_ci "%s: ERROR: Failed to register platform '%s' (%d)!\n", 1518c2ecf20Sopenharmony_ci __func__, pdev->name, ret); 1528c2ecf20Sopenharmony_ci return ret; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ux500_pcm_register_platform); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ciint ux500_pcm_unregister_platform(struct platform_device *pdev) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci snd_dmaengine_pcm_unregister(&pdev->dev); 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ux500_pcm_unregister_platform); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ola Lilja"); 1678c2ecf20Sopenharmony_ciMODULE_AUTHOR("Roger Nilsson"); 1688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ASoC UX500 driver"); 1698c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 170