]>
Commit | Line | Data |
---|---|---|
01a0c113 OL |
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> | |
865fab60 | 21 | #include <linux/platform_data/dma-ste-dma40.h> |
01a0c113 OL |
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 | ||
69b6f196 LPC |
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) | |
01a0c113 | 36 | |
22f38f79 | 37 | static const struct snd_pcm_hardware ux500_pcm_hw = { |
01a0c113 OL |
38 | .info = SNDRV_PCM_INFO_INTERLEAVED | |
39 | SNDRV_PCM_INFO_MMAP | | |
40 | SNDRV_PCM_INFO_RESUME | | |
41 | SNDRV_PCM_INFO_PAUSE, | |
01a0c113 OL |
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 | ||
22f38f79 LPC |
49 | static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, |
50 | struct snd_pcm_substream *substream) | |
01a0c113 | 51 | { |
01a0c113 | 52 | struct snd_soc_dai *dai = rtd->cpu_dai; |
01a0c113 OL |
53 | u16 per_data_width, mem_data_width; |
54 | struct stedma40_chan_cfg *dma_cfg; | |
22f38f79 | 55 | struct ux500_msp_dma_params *dma_params; |
01a0c113 | 56 | |
22f38f79 LPC |
57 | dma_params = snd_soc_dai_get_dma_data(dai, substream); |
58 | dma_cfg = dma_params->dma_cfg; | |
01a0c113 | 59 | |
43f2e1a3 | 60 | mem_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
01a0c113 | 61 | |
01a0c113 OL |
62 | switch (dma_params->data_size) { |
63 | case 32: | |
43f2e1a3 | 64 | per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
01a0c113 OL |
65 | break; |
66 | case 16: | |
43f2e1a3 | 67 | per_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
01a0c113 OL |
68 | break; |
69 | case 8: | |
43f2e1a3 | 70 | per_data_width = DMA_SLAVE_BUSWIDTH_1_BYTE; |
01a0c113 OL |
71 | break; |
72 | default: | |
43f2e1a3 | 73 | per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
01a0c113 OL |
74 | } |
75 | ||
01a0c113 OL |
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 | ||
22f38f79 | 84 | return snd_dmaengine_pcm_request_channel(stedma40_filter, dma_cfg); |
01a0c113 OL |
85 | } |
86 | ||
eef6473f FB |
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; | |
f6c37752 LJ |
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; | |
eef6473f FB |
96 | int ret; |
97 | ||
f6c37752 LJ |
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 | } | |
eef6473f FB |
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; | |
eef6473f | 113 | slave_config->src_maxburst = 4; |
609a3050 LJ |
114 | |
115 | slave_config->src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; | |
116 | slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; | |
eef6473f FB |
117 | |
118 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
f6c37752 | 119 | slave_config->dst_addr = dma_addr; |
eef6473f | 120 | else |
f6c37752 | 121 | slave_config->src_addr = dma_addr; |
eef6473f FB |
122 | |
123 | return 0; | |
124 | } | |
125 | ||
22f38f79 LPC |
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, | |
eef6473f | 130 | .prepare_slave_config = ux500_pcm_prepare_slave_config, |
01a0c113 OL |
131 | }; |
132 | ||
86a3fdfc LJ |
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 | ||
da794876 | 138 | int ux500_pcm_register_platform(struct platform_device *pdev) |
01a0c113 | 139 | { |
86a3fdfc LJ |
140 | const struct snd_dmaengine_pcm_config *pcm_config; |
141 | struct device_node *np = pdev->dev.of_node; | |
01a0c113 OL |
142 | int ret; |
143 | ||
86a3fdfc LJ |
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, | |
86a3fdfc | 150 | SND_DMAENGINE_PCM_FLAG_COMPAT); |
01a0c113 OL |
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 | } | |
1428c20f | 160 | EXPORT_SYMBOL_GPL(ux500_pcm_register_platform); |
01a0c113 | 161 | |
da794876 | 162 | int ux500_pcm_unregister_platform(struct platform_device *pdev) |
01a0c113 | 163 | { |
22f38f79 | 164 | snd_dmaengine_pcm_unregister(&pdev->dev); |
01a0c113 OL |
165 | return 0; |
166 | } | |
1428c20f | 167 | EXPORT_SYMBOL_GPL(ux500_pcm_unregister_platform); |
1783c9d7 AB |
168 | |
169 | MODULE_AUTHOR("Ola Lilja"); | |
170 | MODULE_AUTHOR("Roger Nilsson"); | |
171 | MODULE_DESCRIPTION("ASoC UX500 driver"); | |
172 | MODULE_LICENSE("GPL v2"); |