linux/sound/soc/ux500/ux500_pcm.c
<<
>>
Prefs
   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
  37static 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
  49static 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
  87static 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
 126static 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
 133static 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
 138int 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_NO_RESIDUE |
 151                                         SND_DMAENGINE_PCM_FLAG_COMPAT);
 152        if (ret < 0) {
 153                dev_err(&pdev->dev,
 154                        "%s: ERROR: Failed to register platform '%s' (%d)!\n",
 155                        __func__, pdev->name, ret);
 156                return ret;
 157        }
 158
 159        return 0;
 160}
 161EXPORT_SYMBOL_GPL(ux500_pcm_register_platform);
 162
 163int ux500_pcm_unregister_platform(struct platform_device *pdev)
 164{
 165        snd_dmaengine_pcm_unregister(&pdev->dev);
 166        return 0;
 167}
 168EXPORT_SYMBOL_GPL(ux500_pcm_unregister_platform);
 169