| .. | .. |
|---|
| 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, |
|---|