linux/sound/soc/ux500/ux500_pcm.c
<<
>>
Prefs
   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 = rtd->cpu_dai;
  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 = substream->private_data;
  89        struct msp_i2s_platform_data *pdata = rtd->cpu_dai->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(rtd->cpu_dai, substream);
  98                dma_addr = ste_dma_params->tx_rx_addr;
  99        } else {
 100                snd_dma_params =
 101                        snd_soc_dai_get_dma_data(rtd->cpu_dai, 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");
 170