.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * linux/sound/soc/pxa/mmp-sspa.c |
---|
3 | 4 | * Base on pxa2xx-ssp.c |
---|
4 | 5 | * |
---|
5 | 6 | * Copyright (C) 2011 Marvell International Ltd. |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or modify |
---|
8 | | - * it under the terms of the GNU General Public License as published by |
---|
9 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
10 | | - * (at your option) any later version. |
---|
11 | | - * |
---|
12 | | - * This program is distributed in the hope that it will be useful, |
---|
13 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
14 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
15 | | - * GNU General Public License for more details. |
---|
16 | | - * |
---|
17 | | - * You should have received a copy of the GNU General Public License |
---|
18 | | - * along with this program; if not, write to the Free Software |
---|
19 | | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
20 | | - * |
---|
21 | 7 | */ |
---|
22 | 8 | #include <linux/init.h> |
---|
23 | 9 | #include <linux/module.h> |
---|
.. | .. |
---|
25 | 11 | #include <linux/delay.h> |
---|
26 | 12 | #include <linux/clk.h> |
---|
27 | 13 | #include <linux/slab.h> |
---|
28 | | -#include <linux/pxa2xx_ssp.h> |
---|
29 | 14 | #include <linux/io.h> |
---|
30 | 15 | #include <linux/dmaengine.h> |
---|
| 16 | +#include <linux/pm_runtime.h> |
---|
31 | 17 | |
---|
32 | 18 | #include <sound/core.h> |
---|
33 | 19 | #include <sound/pcm.h> |
---|
.. | .. |
---|
42 | 28 | * SSPA audio private data |
---|
43 | 29 | */ |
---|
44 | 30 | struct sspa_priv { |
---|
45 | | - struct ssp_device *sspa; |
---|
46 | | - struct snd_dmaengine_dai_dma_data *dma_params; |
---|
| 31 | + void __iomem *tx_base; |
---|
| 32 | + void __iomem *rx_base; |
---|
| 33 | + |
---|
| 34 | + struct snd_dmaengine_dai_dma_data playback_dma_data; |
---|
| 35 | + struct snd_dmaengine_dai_dma_data capture_dma_data; |
---|
| 36 | + struct clk *clk; |
---|
47 | 37 | struct clk *audio_clk; |
---|
48 | 38 | struct clk *sysclk; |
---|
49 | | - int dai_fmt; |
---|
| 39 | + |
---|
50 | 40 | int running_cnt; |
---|
| 41 | + u32 sp; |
---|
| 42 | + u32 ctrl; |
---|
51 | 43 | }; |
---|
52 | 44 | |
---|
53 | | -static void mmp_sspa_write_reg(struct ssp_device *sspa, u32 reg, u32 val) |
---|
| 45 | +static void mmp_sspa_tx_enable(struct sspa_priv *sspa) |
---|
54 | 46 | { |
---|
55 | | - __raw_writel(val, sspa->mmio_base + reg); |
---|
56 | | -} |
---|
| 47 | + unsigned int sspa_sp = sspa->sp; |
---|
57 | 48 | |
---|
58 | | -static u32 mmp_sspa_read_reg(struct ssp_device *sspa, u32 reg) |
---|
59 | | -{ |
---|
60 | | - return __raw_readl(sspa->mmio_base + reg); |
---|
61 | | -} |
---|
62 | | - |
---|
63 | | -static void mmp_sspa_tx_enable(struct ssp_device *sspa) |
---|
64 | | -{ |
---|
65 | | - unsigned int sspa_sp; |
---|
66 | | - |
---|
67 | | - sspa_sp = mmp_sspa_read_reg(sspa, SSPA_TXSP); |
---|
| 49 | + sspa_sp &= ~SSPA_SP_MSL; |
---|
68 | 50 | sspa_sp |= SSPA_SP_S_EN; |
---|
69 | 51 | sspa_sp |= SSPA_SP_WEN; |
---|
70 | | - mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); |
---|
| 52 | + __raw_writel(sspa_sp, sspa->tx_base + SSPA_SP); |
---|
71 | 53 | } |
---|
72 | 54 | |
---|
73 | | -static void mmp_sspa_tx_disable(struct ssp_device *sspa) |
---|
| 55 | +static void mmp_sspa_tx_disable(struct sspa_priv *sspa) |
---|
74 | 56 | { |
---|
75 | | - unsigned int sspa_sp; |
---|
| 57 | + unsigned int sspa_sp = sspa->sp; |
---|
76 | 58 | |
---|
77 | | - sspa_sp = mmp_sspa_read_reg(sspa, SSPA_TXSP); |
---|
| 59 | + sspa_sp &= ~SSPA_SP_MSL; |
---|
78 | 60 | sspa_sp &= ~SSPA_SP_S_EN; |
---|
79 | 61 | sspa_sp |= SSPA_SP_WEN; |
---|
80 | | - mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); |
---|
| 62 | + __raw_writel(sspa_sp, sspa->tx_base + SSPA_SP); |
---|
81 | 63 | } |
---|
82 | 64 | |
---|
83 | | -static void mmp_sspa_rx_enable(struct ssp_device *sspa) |
---|
| 65 | +static void mmp_sspa_rx_enable(struct sspa_priv *sspa) |
---|
84 | 66 | { |
---|
85 | | - unsigned int sspa_sp; |
---|
| 67 | + unsigned int sspa_sp = sspa->sp; |
---|
86 | 68 | |
---|
87 | | - sspa_sp = mmp_sspa_read_reg(sspa, SSPA_RXSP); |
---|
88 | 69 | sspa_sp |= SSPA_SP_S_EN; |
---|
89 | 70 | sspa_sp |= SSPA_SP_WEN; |
---|
90 | | - mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); |
---|
| 71 | + __raw_writel(sspa_sp, sspa->rx_base + SSPA_SP); |
---|
91 | 72 | } |
---|
92 | 73 | |
---|
93 | | -static void mmp_sspa_rx_disable(struct ssp_device *sspa) |
---|
| 74 | +static void mmp_sspa_rx_disable(struct sspa_priv *sspa) |
---|
94 | 75 | { |
---|
95 | | - unsigned int sspa_sp; |
---|
| 76 | + unsigned int sspa_sp = sspa->sp; |
---|
96 | 77 | |
---|
97 | | - sspa_sp = mmp_sspa_read_reg(sspa, SSPA_RXSP); |
---|
98 | 78 | sspa_sp &= ~SSPA_SP_S_EN; |
---|
99 | 79 | sspa_sp |= SSPA_SP_WEN; |
---|
100 | | - mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); |
---|
| 80 | + __raw_writel(sspa_sp, sspa->rx_base + SSPA_SP); |
---|
101 | 81 | } |
---|
102 | 82 | |
---|
103 | 83 | static int mmp_sspa_startup(struct snd_pcm_substream *substream, |
---|
104 | 84 | struct snd_soc_dai *dai) |
---|
105 | 85 | { |
---|
106 | | - struct sspa_priv *priv = snd_soc_dai_get_drvdata(dai); |
---|
| 86 | + struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); |
---|
107 | 87 | |
---|
108 | | - clk_enable(priv->sysclk); |
---|
109 | | - clk_enable(priv->sspa->clk); |
---|
| 88 | + clk_prepare_enable(sspa->sysclk); |
---|
| 89 | + clk_prepare_enable(sspa->clk); |
---|
110 | 90 | |
---|
111 | 91 | return 0; |
---|
112 | 92 | } |
---|
.. | .. |
---|
114 | 94 | static void mmp_sspa_shutdown(struct snd_pcm_substream *substream, |
---|
115 | 95 | struct snd_soc_dai *dai) |
---|
116 | 96 | { |
---|
117 | | - struct sspa_priv *priv = snd_soc_dai_get_drvdata(dai); |
---|
| 97 | + struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); |
---|
118 | 98 | |
---|
119 | | - clk_disable(priv->sspa->clk); |
---|
120 | | - clk_disable(priv->sysclk); |
---|
121 | | - |
---|
| 99 | + clk_disable_unprepare(sspa->clk); |
---|
| 100 | + clk_disable_unprepare(sspa->sysclk); |
---|
122 | 101 | } |
---|
123 | 102 | |
---|
124 | 103 | /* |
---|
.. | .. |
---|
127 | 106 | static int mmp_sspa_set_dai_sysclk(struct snd_soc_dai *cpu_dai, |
---|
128 | 107 | int clk_id, unsigned int freq, int dir) |
---|
129 | 108 | { |
---|
130 | | - struct sspa_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); |
---|
| 109 | + struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai); |
---|
| 110 | + struct device *dev = cpu_dai->component->dev; |
---|
131 | 111 | int ret = 0; |
---|
| 112 | + |
---|
| 113 | + if (dev->of_node) |
---|
| 114 | + return -ENOTSUPP; |
---|
132 | 115 | |
---|
133 | 116 | switch (clk_id) { |
---|
134 | 117 | case MMP_SSPA_CLK_AUDIO: |
---|
135 | | - ret = clk_set_rate(priv->audio_clk, freq); |
---|
| 118 | + ret = clk_set_rate(sspa->audio_clk, freq); |
---|
136 | 119 | if (ret) |
---|
137 | 120 | return ret; |
---|
138 | 121 | break; |
---|
.. | .. |
---|
151 | 134 | int source, unsigned int freq_in, |
---|
152 | 135 | unsigned int freq_out) |
---|
153 | 136 | { |
---|
154 | | - struct sspa_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); |
---|
| 137 | + struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai); |
---|
| 138 | + struct device *dev = cpu_dai->component->dev; |
---|
155 | 139 | int ret = 0; |
---|
| 140 | + |
---|
| 141 | + if (dev->of_node) |
---|
| 142 | + return -ENOTSUPP; |
---|
156 | 143 | |
---|
157 | 144 | switch (pll_id) { |
---|
158 | 145 | case MMP_SYSCLK: |
---|
159 | | - ret = clk_set_rate(priv->sysclk, freq_out); |
---|
| 146 | + ret = clk_set_rate(sspa->sysclk, freq_out); |
---|
160 | 147 | if (ret) |
---|
161 | 148 | return ret; |
---|
162 | 149 | break; |
---|
163 | 150 | case MMP_SSPA_CLK: |
---|
164 | | - ret = clk_set_rate(priv->sspa->clk, freq_out); |
---|
| 151 | + ret = clk_set_rate(sspa->clk, freq_out); |
---|
165 | 152 | if (ret) |
---|
166 | 153 | return ret; |
---|
167 | 154 | break; |
---|
.. | .. |
---|
173 | 160 | } |
---|
174 | 161 | |
---|
175 | 162 | /* |
---|
176 | | - * Set up the sspa dai format. The sspa port must be inactive |
---|
177 | | - * before calling this function as the physical |
---|
178 | | - * interface format is changed. |
---|
| 163 | + * Set up the sspa dai format. |
---|
179 | 164 | */ |
---|
180 | 165 | static int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai, |
---|
181 | 166 | unsigned int fmt) |
---|
182 | 167 | { |
---|
183 | | - struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(cpu_dai); |
---|
184 | | - struct ssp_device *sspa = sspa_priv->sspa; |
---|
185 | | - u32 sspa_sp, sspa_ctrl; |
---|
186 | | - |
---|
187 | | - /* check if we need to change anything at all */ |
---|
188 | | - if (sspa_priv->dai_fmt == fmt) |
---|
189 | | - return 0; |
---|
190 | | - |
---|
191 | | - /* we can only change the settings if the port is not in use */ |
---|
192 | | - if ((mmp_sspa_read_reg(sspa, SSPA_TXSP) & SSPA_SP_S_EN) || |
---|
193 | | - (mmp_sspa_read_reg(sspa, SSPA_RXSP) & SSPA_SP_S_EN)) { |
---|
194 | | - dev_err(&sspa->pdev->dev, |
---|
195 | | - "can't change hardware dai format: stream is in use\n"); |
---|
196 | | - return -EINVAL; |
---|
197 | | - } |
---|
| 168 | + struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai); |
---|
198 | 169 | |
---|
199 | 170 | /* reset port settings */ |
---|
200 | | - sspa_sp = SSPA_SP_WEN | SSPA_SP_S_RST | SSPA_SP_FFLUSH; |
---|
201 | | - sspa_ctrl = 0; |
---|
| 171 | + sspa->sp = SSPA_SP_WEN | SSPA_SP_S_RST | SSPA_SP_FFLUSH; |
---|
| 172 | + sspa->ctrl = 0; |
---|
202 | 173 | |
---|
203 | 174 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
---|
204 | 175 | case SND_SOC_DAIFMT_CBS_CFS: |
---|
205 | | - sspa_sp |= SSPA_SP_MSL; |
---|
| 176 | + sspa->sp |= SSPA_SP_MSL; |
---|
206 | 177 | break; |
---|
207 | 178 | case SND_SOC_DAIFMT_CBM_CFM: |
---|
208 | 179 | break; |
---|
.. | .. |
---|
212 | 183 | |
---|
213 | 184 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
---|
214 | 185 | case SND_SOC_DAIFMT_NB_NF: |
---|
215 | | - sspa_sp |= SSPA_SP_FSP; |
---|
| 186 | + sspa->sp |= SSPA_SP_FSP; |
---|
216 | 187 | break; |
---|
217 | 188 | default: |
---|
218 | 189 | return -EINVAL; |
---|
.. | .. |
---|
220 | 191 | |
---|
221 | 192 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
---|
222 | 193 | case SND_SOC_DAIFMT_I2S: |
---|
223 | | - sspa_sp |= SSPA_TXSP_FPER(63); |
---|
224 | | - sspa_sp |= SSPA_SP_FWID(31); |
---|
225 | | - sspa_ctrl |= SSPA_CTL_XDATDLY(1); |
---|
| 194 | + sspa->ctrl |= SSPA_CTL_XDATDLY(1); |
---|
226 | 195 | break; |
---|
227 | 196 | default: |
---|
228 | 197 | return -EINVAL; |
---|
229 | 198 | } |
---|
230 | 199 | |
---|
231 | | - mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); |
---|
232 | | - mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); |
---|
233 | | - |
---|
234 | | - sspa_sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH); |
---|
235 | | - mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); |
---|
236 | | - mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); |
---|
237 | | - |
---|
238 | | - /* |
---|
239 | | - * FIXME: hw issue, for the tx serial port, |
---|
240 | | - * can not config the master/slave mode; |
---|
241 | | - * so must clean this bit. |
---|
242 | | - * The master/slave mode has been set in the |
---|
243 | | - * rx port. |
---|
244 | | - */ |
---|
245 | | - sspa_sp &= ~SSPA_SP_MSL; |
---|
246 | | - mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); |
---|
247 | | - |
---|
248 | | - mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl); |
---|
249 | | - mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl); |
---|
250 | | - |
---|
251 | 200 | /* Since we are configuring the timings for the format by hand |
---|
252 | 201 | * we have to defer some things until hw_params() where we |
---|
253 | 202 | * know parameters like the sample size. |
---|
254 | 203 | */ |
---|
255 | | - sspa_priv->dai_fmt = fmt; |
---|
256 | 204 | return 0; |
---|
257 | 205 | } |
---|
258 | 206 | |
---|
.. | .. |
---|
264 | 212 | struct snd_pcm_hw_params *params, |
---|
265 | 213 | struct snd_soc_dai *dai) |
---|
266 | 214 | { |
---|
267 | | - struct snd_soc_pcm_runtime *rtd = substream->private_data; |
---|
268 | | - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
---|
269 | | - struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai); |
---|
270 | | - struct ssp_device *sspa = sspa_priv->sspa; |
---|
271 | | - struct snd_dmaengine_dai_dma_data *dma_params; |
---|
272 | | - u32 sspa_ctrl; |
---|
273 | | - |
---|
274 | | - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
---|
275 | | - sspa_ctrl = mmp_sspa_read_reg(sspa, SSPA_TXCTL); |
---|
276 | | - else |
---|
277 | | - sspa_ctrl = mmp_sspa_read_reg(sspa, SSPA_RXCTL); |
---|
278 | | - |
---|
279 | | - sspa_ctrl &= ~SSPA_CTL_XFRLEN1_MASK; |
---|
280 | | - sspa_ctrl |= SSPA_CTL_XFRLEN1(params_channels(params) - 1); |
---|
281 | | - sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK; |
---|
282 | | - sspa_ctrl |= SSPA_CTL_XWDLEN1(SSPA_CTL_32_BITS); |
---|
283 | | - sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK; |
---|
| 215 | + struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); |
---|
| 216 | + struct device *dev = dai->component->dev; |
---|
| 217 | + u32 sspa_ctrl = sspa->ctrl; |
---|
| 218 | + int bits; |
---|
| 219 | + int bitval; |
---|
284 | 220 | |
---|
285 | 221 | switch (params_format(params)) { |
---|
286 | 222 | case SNDRV_PCM_FORMAT_S8: |
---|
287 | | - sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_8_BITS); |
---|
| 223 | + bits = 8; |
---|
| 224 | + bitval = SSPA_CTL_8_BITS; |
---|
288 | 225 | break; |
---|
289 | 226 | case SNDRV_PCM_FORMAT_S16_LE: |
---|
290 | | - sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_16_BITS); |
---|
291 | | - break; |
---|
292 | | - case SNDRV_PCM_FORMAT_S20_3LE: |
---|
293 | | - sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_20_BITS); |
---|
| 227 | + bits = 16; |
---|
| 228 | + bitval = SSPA_CTL_16_BITS; |
---|
294 | 229 | break; |
---|
295 | 230 | case SNDRV_PCM_FORMAT_S24_3LE: |
---|
296 | | - sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_24_BITS); |
---|
| 231 | + bits = 24; |
---|
| 232 | + bitval = SSPA_CTL_24_BITS; |
---|
297 | 233 | break; |
---|
298 | 234 | case SNDRV_PCM_FORMAT_S32_LE: |
---|
299 | | - sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_32_BITS); |
---|
| 235 | + bits = 32; |
---|
| 236 | + bitval = SSPA_CTL_32_BITS; |
---|
300 | 237 | break; |
---|
301 | 238 | default: |
---|
302 | 239 | return -EINVAL; |
---|
303 | 240 | } |
---|
304 | 241 | |
---|
305 | | - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
---|
306 | | - mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl); |
---|
307 | | - mmp_sspa_write_reg(sspa, SSPA_TXFIFO_LL, 0x1); |
---|
308 | | - } else { |
---|
309 | | - mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl); |
---|
310 | | - mmp_sspa_write_reg(sspa, SSPA_RXFIFO_UL, 0x0); |
---|
| 242 | + if (dev->of_node || params_channels(params) == 2) |
---|
| 243 | + sspa_ctrl |= SSPA_CTL_XPH; |
---|
| 244 | + |
---|
| 245 | + sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK; |
---|
| 246 | + sspa_ctrl |= SSPA_CTL_XWDLEN1(bitval); |
---|
| 247 | + |
---|
| 248 | + sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK; |
---|
| 249 | + sspa_ctrl |= SSPA_CTL_XSSZ1(bitval); |
---|
| 250 | + |
---|
| 251 | + sspa_ctrl &= ~SSPA_CTL_XSSZ2_MASK; |
---|
| 252 | + sspa_ctrl |= SSPA_CTL_XSSZ2(bitval); |
---|
| 253 | + |
---|
| 254 | + sspa->sp &= ~SSPA_SP_FWID_MASK; |
---|
| 255 | + sspa->sp |= SSPA_SP_FWID(bits - 1); |
---|
| 256 | + |
---|
| 257 | + sspa->sp &= ~SSPA_TXSP_FPER_MASK; |
---|
| 258 | + sspa->sp |= SSPA_TXSP_FPER(bits * 2 - 1); |
---|
| 259 | + |
---|
| 260 | + if (dev->of_node) { |
---|
| 261 | + clk_set_rate(sspa->clk, params_rate(params) * |
---|
| 262 | + params_channels(params) * bits); |
---|
311 | 263 | } |
---|
312 | 264 | |
---|
313 | | - dma_params = &sspa_priv->dma_params[substream->stream]; |
---|
314 | | - dma_params->addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? |
---|
315 | | - (sspa->phys_base + SSPA_TXD) : |
---|
316 | | - (sspa->phys_base + SSPA_RXD); |
---|
317 | | - snd_soc_dai_set_dma_data(cpu_dai, substream, dma_params); |
---|
| 265 | + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
---|
| 266 | + __raw_writel(sspa_ctrl, sspa->tx_base + SSPA_CTL); |
---|
| 267 | + __raw_writel(0x1, sspa->tx_base + SSPA_FIFO_UL); |
---|
| 268 | + } else { |
---|
| 269 | + __raw_writel(sspa_ctrl, sspa->rx_base + SSPA_CTL); |
---|
| 270 | + __raw_writel(0x0, sspa->rx_base + SSPA_FIFO_UL); |
---|
| 271 | + } |
---|
| 272 | + |
---|
318 | 273 | return 0; |
---|
319 | 274 | } |
---|
320 | 275 | |
---|
321 | 276 | static int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd, |
---|
322 | 277 | struct snd_soc_dai *dai) |
---|
323 | 278 | { |
---|
324 | | - struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai); |
---|
325 | | - struct ssp_device *sspa = sspa_priv->sspa; |
---|
| 279 | + struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai); |
---|
326 | 280 | int ret = 0; |
---|
327 | 281 | |
---|
328 | 282 | switch (cmd) { |
---|
.. | .. |
---|
335 | 289 | * enabled or not; if has been enabled by another |
---|
336 | 290 | * stream, do not enable again. |
---|
337 | 291 | */ |
---|
338 | | - if (!sspa_priv->running_cnt) |
---|
| 292 | + if (!sspa->running_cnt) |
---|
339 | 293 | mmp_sspa_rx_enable(sspa); |
---|
340 | 294 | |
---|
341 | 295 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
---|
342 | 296 | mmp_sspa_tx_enable(sspa); |
---|
343 | 297 | |
---|
344 | | - sspa_priv->running_cnt++; |
---|
| 298 | + sspa->running_cnt++; |
---|
345 | 299 | break; |
---|
346 | 300 | |
---|
347 | 301 | case SNDRV_PCM_TRIGGER_STOP: |
---|
348 | 302 | case SNDRV_PCM_TRIGGER_SUSPEND: |
---|
349 | 303 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
---|
350 | | - sspa_priv->running_cnt--; |
---|
| 304 | + sspa->running_cnt--; |
---|
351 | 305 | |
---|
352 | 306 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
---|
353 | 307 | mmp_sspa_tx_disable(sspa); |
---|
354 | 308 | |
---|
355 | 309 | /* have no capture stream, disable rx port */ |
---|
356 | | - if (!sspa_priv->running_cnt) |
---|
| 310 | + if (!sspa->running_cnt) |
---|
357 | 311 | mmp_sspa_rx_disable(sspa); |
---|
358 | 312 | break; |
---|
359 | 313 | |
---|
.. | .. |
---|
366 | 320 | |
---|
367 | 321 | static int mmp_sspa_probe(struct snd_soc_dai *dai) |
---|
368 | 322 | { |
---|
369 | | - struct sspa_priv *priv = dev_get_drvdata(dai->dev); |
---|
| 323 | + struct sspa_priv *sspa = dev_get_drvdata(dai->dev); |
---|
370 | 324 | |
---|
371 | | - snd_soc_dai_set_drvdata(dai, priv); |
---|
| 325 | + snd_soc_dai_init_dma_data(dai, |
---|
| 326 | + &sspa->playback_dma_data, |
---|
| 327 | + &sspa->capture_dma_data); |
---|
| 328 | + |
---|
| 329 | + snd_soc_dai_set_drvdata(dai, sspa); |
---|
372 | 330 | return 0; |
---|
373 | | - |
---|
374 | 331 | } |
---|
375 | 332 | |
---|
376 | 333 | #define MMP_SSPA_RATES SNDRV_PCM_RATE_8000_192000 |
---|
377 | 334 | #define MMP_SSPA_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ |
---|
378 | 335 | SNDRV_PCM_FMTBIT_S16_LE | \ |
---|
379 | | - SNDRV_PCM_FMTBIT_S24_LE | \ |
---|
| 336 | + SNDRV_PCM_FMTBIT_S24_3LE | \ |
---|
380 | 337 | SNDRV_PCM_FMTBIT_S32_LE) |
---|
381 | 338 | |
---|
382 | 339 | static const struct snd_soc_dai_ops mmp_sspa_dai_ops = { |
---|
.. | .. |
---|
406 | 363 | .ops = &mmp_sspa_dai_ops, |
---|
407 | 364 | }; |
---|
408 | 365 | |
---|
| 366 | +#define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \ |
---|
| 367 | + SNDRV_PCM_INFO_MMAP_VALID | \ |
---|
| 368 | + SNDRV_PCM_INFO_INTERLEAVED | \ |
---|
| 369 | + SNDRV_PCM_INFO_PAUSE | \ |
---|
| 370 | + SNDRV_PCM_INFO_RESUME | \ |
---|
| 371 | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) |
---|
| 372 | + |
---|
| 373 | +static const struct snd_pcm_hardware mmp_pcm_hardware[] = { |
---|
| 374 | + { |
---|
| 375 | + .info = MMP_PCM_INFO, |
---|
| 376 | + .period_bytes_min = 1024, |
---|
| 377 | + .period_bytes_max = 2048, |
---|
| 378 | + .periods_min = 2, |
---|
| 379 | + .periods_max = 32, |
---|
| 380 | + .buffer_bytes_max = 4096, |
---|
| 381 | + .fifo_size = 32, |
---|
| 382 | + }, |
---|
| 383 | + { |
---|
| 384 | + .info = MMP_PCM_INFO, |
---|
| 385 | + .period_bytes_min = 1024, |
---|
| 386 | + .period_bytes_max = 2048, |
---|
| 387 | + .periods_min = 2, |
---|
| 388 | + .periods_max = 32, |
---|
| 389 | + .buffer_bytes_max = 4096, |
---|
| 390 | + .fifo_size = 32, |
---|
| 391 | + }, |
---|
| 392 | +}; |
---|
| 393 | + |
---|
| 394 | +static const struct snd_dmaengine_pcm_config mmp_pcm_config = { |
---|
| 395 | + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, |
---|
| 396 | + .pcm_hardware = mmp_pcm_hardware, |
---|
| 397 | + .prealloc_buffer_size = 4096, |
---|
| 398 | +}; |
---|
| 399 | + |
---|
| 400 | +static int mmp_pcm_mmap(struct snd_soc_component *component, |
---|
| 401 | + struct snd_pcm_substream *substream, |
---|
| 402 | + struct vm_area_struct *vma) |
---|
| 403 | +{ |
---|
| 404 | + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; |
---|
| 405 | + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); |
---|
| 406 | + return remap_pfn_range(vma, vma->vm_start, |
---|
| 407 | + substream->dma_buffer.addr >> PAGE_SHIFT, |
---|
| 408 | + vma->vm_end - vma->vm_start, vma->vm_page_prot); |
---|
| 409 | +} |
---|
| 410 | + |
---|
| 411 | +static int mmp_sspa_open(struct snd_soc_component *component, |
---|
| 412 | + struct snd_pcm_substream *substream) |
---|
| 413 | +{ |
---|
| 414 | + struct sspa_priv *sspa = snd_soc_component_get_drvdata(component); |
---|
| 415 | + |
---|
| 416 | + pm_runtime_get_sync(component->dev); |
---|
| 417 | + |
---|
| 418 | + /* we can only change the settings if the port is not in use */ |
---|
| 419 | + if ((__raw_readl(sspa->tx_base + SSPA_SP) & SSPA_SP_S_EN) || |
---|
| 420 | + (__raw_readl(sspa->rx_base + SSPA_SP) & SSPA_SP_S_EN)) { |
---|
| 421 | + dev_err(component->dev, |
---|
| 422 | + "can't change hardware dai format: stream is in use\n"); |
---|
| 423 | + return -EBUSY; |
---|
| 424 | + } |
---|
| 425 | + |
---|
| 426 | + __raw_writel(sspa->sp, sspa->tx_base + SSPA_SP); |
---|
| 427 | + __raw_writel(sspa->sp, sspa->rx_base + SSPA_SP); |
---|
| 428 | + |
---|
| 429 | + sspa->sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH); |
---|
| 430 | + __raw_writel(sspa->sp, sspa->tx_base + SSPA_SP); |
---|
| 431 | + __raw_writel(sspa->sp, sspa->rx_base + SSPA_SP); |
---|
| 432 | + |
---|
| 433 | + /* |
---|
| 434 | + * FIXME: hw issue, for the tx serial port, |
---|
| 435 | + * can not config the master/slave mode; |
---|
| 436 | + * so must clean this bit. |
---|
| 437 | + * The master/slave mode has been set in the |
---|
| 438 | + * rx port. |
---|
| 439 | + */ |
---|
| 440 | + __raw_writel(sspa->sp & ~SSPA_SP_MSL, sspa->tx_base + SSPA_SP); |
---|
| 441 | + |
---|
| 442 | + __raw_writel(sspa->ctrl, sspa->tx_base + SSPA_CTL); |
---|
| 443 | + __raw_writel(sspa->ctrl, sspa->rx_base + SSPA_CTL); |
---|
| 444 | + |
---|
| 445 | + return 0; |
---|
| 446 | +} |
---|
| 447 | + |
---|
| 448 | +static int mmp_sspa_close(struct snd_soc_component *component, |
---|
| 449 | + struct snd_pcm_substream *substream) |
---|
| 450 | +{ |
---|
| 451 | + pm_runtime_put_sync(component->dev); |
---|
| 452 | + return 0; |
---|
| 453 | +} |
---|
| 454 | + |
---|
409 | 455 | static const struct snd_soc_component_driver mmp_sspa_component = { |
---|
410 | 456 | .name = "mmp-sspa", |
---|
| 457 | + .mmap = mmp_pcm_mmap, |
---|
| 458 | + .open = mmp_sspa_open, |
---|
| 459 | + .close = mmp_sspa_close, |
---|
411 | 460 | }; |
---|
412 | 461 | |
---|
413 | 462 | static int asoc_mmp_sspa_probe(struct platform_device *pdev) |
---|
414 | 463 | { |
---|
415 | | - struct sspa_priv *priv; |
---|
416 | | - struct resource *res; |
---|
| 464 | + struct sspa_priv *sspa; |
---|
| 465 | + int ret; |
---|
417 | 466 | |
---|
418 | | - priv = devm_kzalloc(&pdev->dev, |
---|
| 467 | + sspa = devm_kzalloc(&pdev->dev, |
---|
419 | 468 | sizeof(struct sspa_priv), GFP_KERNEL); |
---|
420 | | - if (!priv) |
---|
| 469 | + if (!sspa) |
---|
421 | 470 | return -ENOMEM; |
---|
422 | 471 | |
---|
423 | | - priv->sspa = devm_kzalloc(&pdev->dev, |
---|
424 | | - sizeof(struct ssp_device), GFP_KERNEL); |
---|
425 | | - if (priv->sspa == NULL) |
---|
426 | | - return -ENOMEM; |
---|
| 472 | + if (pdev->dev.of_node) { |
---|
| 473 | + sspa->rx_base = devm_platform_ioremap_resource(pdev, 0); |
---|
| 474 | + if (IS_ERR(sspa->rx_base)) |
---|
| 475 | + return PTR_ERR(sspa->rx_base); |
---|
427 | 476 | |
---|
428 | | - priv->dma_params = devm_kcalloc(&pdev->dev, |
---|
429 | | - 2, sizeof(struct snd_dmaengine_dai_dma_data), |
---|
430 | | - GFP_KERNEL); |
---|
431 | | - if (priv->dma_params == NULL) |
---|
432 | | - return -ENOMEM; |
---|
| 477 | + sspa->tx_base = devm_platform_ioremap_resource(pdev, 1); |
---|
| 478 | + if (IS_ERR(sspa->tx_base)) |
---|
| 479 | + return PTR_ERR(sspa->tx_base); |
---|
433 | 480 | |
---|
434 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
435 | | - priv->sspa->mmio_base = devm_ioremap_resource(&pdev->dev, res); |
---|
436 | | - if (IS_ERR(priv->sspa->mmio_base)) |
---|
437 | | - return PTR_ERR(priv->sspa->mmio_base); |
---|
| 481 | + sspa->clk = devm_clk_get(&pdev->dev, "bitclk"); |
---|
| 482 | + if (IS_ERR(sspa->clk)) |
---|
| 483 | + return PTR_ERR(sspa->clk); |
---|
438 | 484 | |
---|
439 | | - priv->sspa->clk = devm_clk_get(&pdev->dev, NULL); |
---|
440 | | - if (IS_ERR(priv->sspa->clk)) |
---|
441 | | - return PTR_ERR(priv->sspa->clk); |
---|
| 485 | + sspa->audio_clk = devm_clk_get(&pdev->dev, "audio"); |
---|
| 486 | + if (IS_ERR(sspa->audio_clk)) |
---|
| 487 | + return PTR_ERR(sspa->audio_clk); |
---|
| 488 | + } else { |
---|
| 489 | + struct resource *res; |
---|
442 | 490 | |
---|
443 | | - priv->audio_clk = clk_get(NULL, "mmp-audio"); |
---|
444 | | - if (IS_ERR(priv->audio_clk)) |
---|
445 | | - return PTR_ERR(priv->audio_clk); |
---|
| 491 | + res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
---|
| 492 | + if (res == NULL) |
---|
| 493 | + return -ENODEV; |
---|
446 | 494 | |
---|
447 | | - priv->sysclk = clk_get(NULL, "mmp-sysclk"); |
---|
448 | | - if (IS_ERR(priv->sysclk)) { |
---|
449 | | - clk_put(priv->audio_clk); |
---|
450 | | - return PTR_ERR(priv->sysclk); |
---|
| 495 | + sspa->rx_base = devm_ioremap(&pdev->dev, res->start, 0x30); |
---|
| 496 | + if (!sspa->rx_base) |
---|
| 497 | + return -ENOMEM; |
---|
| 498 | + |
---|
| 499 | + sspa->tx_base = devm_ioremap(&pdev->dev, |
---|
| 500 | + res->start + 0x80, 0x30); |
---|
| 501 | + if (!sspa->tx_base) |
---|
| 502 | + return -ENOMEM; |
---|
| 503 | + |
---|
| 504 | + sspa->clk = devm_clk_get(&pdev->dev, NULL); |
---|
| 505 | + if (IS_ERR(sspa->clk)) |
---|
| 506 | + return PTR_ERR(sspa->clk); |
---|
| 507 | + |
---|
| 508 | + sspa->audio_clk = clk_get(NULL, "mmp-audio"); |
---|
| 509 | + if (IS_ERR(sspa->audio_clk)) |
---|
| 510 | + return PTR_ERR(sspa->audio_clk); |
---|
| 511 | + |
---|
| 512 | + sspa->sysclk = clk_get(NULL, "mmp-sysclk"); |
---|
| 513 | + if (IS_ERR(sspa->sysclk)) { |
---|
| 514 | + clk_put(sspa->audio_clk); |
---|
| 515 | + return PTR_ERR(sspa->sysclk); |
---|
| 516 | + } |
---|
451 | 517 | } |
---|
452 | | - clk_enable(priv->audio_clk); |
---|
453 | | - priv->dai_fmt = (unsigned int) -1; |
---|
454 | | - platform_set_drvdata(pdev, priv); |
---|
| 518 | + platform_set_drvdata(pdev, sspa); |
---|
455 | 519 | |
---|
456 | | - return devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component, |
---|
457 | | - &mmp_sspa_dai, 1); |
---|
| 520 | + sspa->playback_dma_data.maxburst = 4; |
---|
| 521 | + sspa->capture_dma_data.maxburst = 4; |
---|
| 522 | + /* You know, these addresses are actually ignored. */ |
---|
| 523 | + sspa->capture_dma_data.addr = SSPA_D; |
---|
| 524 | + sspa->playback_dma_data.addr = 0x80 + SSPA_D; |
---|
| 525 | + |
---|
| 526 | + if (pdev->dev.of_node) { |
---|
| 527 | + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, |
---|
| 528 | + &mmp_pcm_config, 0); |
---|
| 529 | + if (ret) |
---|
| 530 | + return ret; |
---|
| 531 | + } |
---|
| 532 | + |
---|
| 533 | + ret = devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component, |
---|
| 534 | + &mmp_sspa_dai, 1); |
---|
| 535 | + if (ret) |
---|
| 536 | + return ret; |
---|
| 537 | + |
---|
| 538 | + pm_runtime_enable(&pdev->dev); |
---|
| 539 | + clk_prepare_enable(sspa->audio_clk); |
---|
| 540 | + |
---|
| 541 | + return 0; |
---|
458 | 542 | } |
---|
459 | 543 | |
---|
460 | 544 | static int asoc_mmp_sspa_remove(struct platform_device *pdev) |
---|
461 | 545 | { |
---|
462 | | - struct sspa_priv *priv = platform_get_drvdata(pdev); |
---|
| 546 | + struct sspa_priv *sspa = platform_get_drvdata(pdev); |
---|
463 | 547 | |
---|
464 | | - clk_disable(priv->audio_clk); |
---|
465 | | - clk_put(priv->audio_clk); |
---|
466 | | - clk_put(priv->sysclk); |
---|
| 548 | + clk_disable_unprepare(sspa->audio_clk); |
---|
| 549 | + pm_runtime_disable(&pdev->dev); |
---|
| 550 | + |
---|
| 551 | + if (pdev->dev.of_node) |
---|
| 552 | + return 0; |
---|
| 553 | + |
---|
| 554 | + clk_put(sspa->audio_clk); |
---|
| 555 | + clk_put(sspa->sysclk); |
---|
467 | 556 | return 0; |
---|
468 | 557 | } |
---|
| 558 | + |
---|
| 559 | +#ifdef CONFIG_OF |
---|
| 560 | +static const struct of_device_id mmp_sspa_of_match[] = { |
---|
| 561 | + { .compatible = "marvell,mmp-sspa" }, |
---|
| 562 | + {}, |
---|
| 563 | +}; |
---|
| 564 | + |
---|
| 565 | +MODULE_DEVICE_TABLE(of, mmp_sspa_of_match); |
---|
| 566 | +#endif |
---|
469 | 567 | |
---|
470 | 568 | static struct platform_driver asoc_mmp_sspa_driver = { |
---|
471 | 569 | .driver = { |
---|
472 | 570 | .name = "mmp-sspa-dai", |
---|
| 571 | + .of_match_table = of_match_ptr(mmp_sspa_of_match), |
---|
473 | 572 | }, |
---|
474 | 573 | .probe = asoc_mmp_sspa_probe, |
---|
475 | 574 | .remove = asoc_mmp_sspa_remove, |
---|