Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 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> |
Linus Walleij | 865fab6 | 2012-10-18 14:20:16 +0200 | [diff] [blame] | 21 | #include <linux/platform_data/dma-ste-dma40.h> |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 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 | |
Lars-Peter Clausen | 69b6f19 | 2013-04-03 11:02:55 +0200 | [diff] [blame] | 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 | |
Lars-Peter Clausen | 22f38f7 | 2013-04-15 19:20:04 +0200 | [diff] [blame] | 37 | static const struct snd_pcm_hardware ux500_pcm_hw = { |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 38 | .info = SNDRV_PCM_INFO_INTERLEAVED | |
| 39 | SNDRV_PCM_INFO_MMAP | |
| 40 | SNDRV_PCM_INFO_RESUME | |
| 41 | SNDRV_PCM_INFO_PAUSE, |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 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 | |
Lars-Peter Clausen | 22f38f7 | 2013-04-15 19:20:04 +0200 | [diff] [blame] | 49 | static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, |
| 50 | struct snd_pcm_substream *substream) |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 51 | { |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 52 | struct snd_soc_dai *dai = rtd->cpu_dai; |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 53 | u16 per_data_width, mem_data_width; |
| 54 | struct stedma40_chan_cfg *dma_cfg; |
Lars-Peter Clausen | 22f38f7 | 2013-04-15 19:20:04 +0200 | [diff] [blame] | 55 | struct ux500_msp_dma_params *dma_params; |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 56 | |
Lars-Peter Clausen | 22f38f7 | 2013-04-15 19:20:04 +0200 | [diff] [blame] | 57 | dma_params = snd_soc_dai_get_dma_data(dai, substream); |
| 58 | dma_cfg = dma_params->dma_cfg; |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 59 | |
Lee Jones | 43f2e1a | 2013-05-15 11:51:57 +0200 | [diff] [blame] | 60 | mem_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 61 | |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 62 | switch (dma_params->data_size) { |
| 63 | case 32: |
Lee Jones | 43f2e1a | 2013-05-15 11:51:57 +0200 | [diff] [blame] | 64 | per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 65 | break; |
| 66 | case 16: |
Lee Jones | 43f2e1a | 2013-05-15 11:51:57 +0200 | [diff] [blame] | 67 | per_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 68 | break; |
| 69 | case 8: |
Lee Jones | 43f2e1a | 2013-05-15 11:51:57 +0200 | [diff] [blame] | 70 | per_data_width = DMA_SLAVE_BUSWIDTH_1_BYTE; |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 71 | break; |
| 72 | default: |
Lee Jones | 43f2e1a | 2013-05-15 11:51:57 +0200 | [diff] [blame] | 73 | per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 74 | } |
| 75 | |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 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 | |
Lars-Peter Clausen | 22f38f7 | 2013-04-15 19:20:04 +0200 | [diff] [blame] | 84 | return snd_dmaengine_pcm_request_channel(stedma40_filter, dma_cfg); |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 85 | } |
| 86 | |
Fabio Baltieri | eef6473 | 2013-06-12 09:57:59 +0200 | [diff] [blame] | 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; |
Lee Jones | f6c3775 | 2013-12-19 15:55:03 +0000 | [diff] [blame] | 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; |
Fabio Baltieri | eef6473 | 2013-06-12 09:57:59 +0200 | [diff] [blame] | 96 | int ret; |
| 97 | |
Lee Jones | f6c3775 | 2013-12-19 15:55:03 +0000 | [diff] [blame] | 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 | } |
Fabio Baltieri | eef6473 | 2013-06-12 09:57:59 +0200 | [diff] [blame] | 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; |
Fabio Baltieri | eef6473 | 2013-06-12 09:57:59 +0200 | [diff] [blame] | 113 | slave_config->src_maxburst = 4; |
Lee Jones | 609a305 | 2013-12-19 15:55:02 +0000 | [diff] [blame] | 114 | |
| 115 | slave_config->src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
| 116 | slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
Fabio Baltieri | eef6473 | 2013-06-12 09:57:59 +0200 | [diff] [blame] | 117 | |
| 118 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
Lee Jones | f6c3775 | 2013-12-19 15:55:03 +0000 | [diff] [blame] | 119 | slave_config->dst_addr = dma_addr; |
Fabio Baltieri | eef6473 | 2013-06-12 09:57:59 +0200 | [diff] [blame] | 120 | else |
Lee Jones | f6c3775 | 2013-12-19 15:55:03 +0000 | [diff] [blame] | 121 | slave_config->src_addr = dma_addr; |
Fabio Baltieri | eef6473 | 2013-06-12 09:57:59 +0200 | [diff] [blame] | 122 | |
| 123 | return 0; |
| 124 | } |
| 125 | |
Lars-Peter Clausen | 22f38f7 | 2013-04-15 19:20:04 +0200 | [diff] [blame] | 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, |
Fabio Baltieri | eef6473 | 2013-06-12 09:57:59 +0200 | [diff] [blame] | 130 | .prepare_slave_config = ux500_pcm_prepare_slave_config, |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 131 | }; |
| 132 | |
Lee Jones | 86a3fdf | 2013-12-19 15:55:07 +0000 | [diff] [blame] | 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 | |
Bill Pemberton | da79487 | 2012-12-07 09:26:35 -0500 | [diff] [blame] | 138 | int ux500_pcm_register_platform(struct platform_device *pdev) |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 139 | { |
Lee Jones | 86a3fdf | 2013-12-19 15:55:07 +0000 | [diff] [blame] | 140 | const struct snd_dmaengine_pcm_config *pcm_config; |
| 141 | struct device_node *np = pdev->dev.of_node; |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 142 | int ret; |
| 143 | |
Lee Jones | 86a3fdf | 2013-12-19 15:55:07 +0000 | [diff] [blame] | 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, |
Lee Jones | 86a3fdf | 2013-12-19 15:55:07 +0000 | [diff] [blame] | 150 | SND_DMAENGINE_PCM_FLAG_COMPAT); |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 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 | } |
Lee Jones | 1428c20 | 2012-11-23 13:05:41 +0000 | [diff] [blame] | 160 | EXPORT_SYMBOL_GPL(ux500_pcm_register_platform); |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 161 | |
Bill Pemberton | da79487 | 2012-12-07 09:26:35 -0500 | [diff] [blame] | 162 | int ux500_pcm_unregister_platform(struct platform_device *pdev) |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 163 | { |
Lars-Peter Clausen | 22f38f7 | 2013-04-15 19:20:04 +0200 | [diff] [blame] | 164 | snd_dmaengine_pcm_unregister(&pdev->dev); |
Ola Lilja | 01a0c11 | 2012-05-24 15:26:32 +0200 | [diff] [blame] | 165 | return 0; |
| 166 | } |
Lee Jones | 1428c20 | 2012-11-23 13:05:41 +0000 | [diff] [blame] | 167 | EXPORT_SYMBOL_GPL(ux500_pcm_unregister_platform); |