olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 1 | /* |
| 2 | * STM32 ALSA SoC Digital Audio Interface (I2S) driver. |
| 3 | * |
| 4 | * Copyright (C) 2017, STMicroelectronics - All Rights Reserved |
| 5 | * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics. |
| 6 | * |
| 7 | * License terms: GPL V2.0. |
| 8 | * |
| 9 | * This program is free software; you can redistribute it and/or modify it |
| 10 | * under the terms of the GNU General Public License version 2 as published by |
| 11 | * the Free Software Foundation. |
| 12 | * |
| 13 | * This program is distributed in the hope that it will be useful, but |
| 14 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| 16 | * details. |
| 17 | */ |
| 18 | |
| 19 | #include <linux/clk.h> |
| 20 | #include <linux/delay.h> |
| 21 | #include <linux/module.h> |
| 22 | #include <linux/of_irq.h> |
| 23 | #include <linux/of_platform.h> |
| 24 | #include <linux/regmap.h> |
| 25 | #include <linux/reset.h> |
| 26 | #include <linux/spinlock.h> |
| 27 | |
| 28 | #include <sound/dmaengine_pcm.h> |
| 29 | #include <sound/pcm_params.h> |
| 30 | |
| 31 | #define STM32_I2S_CR1_REG 0x0 |
| 32 | #define STM32_I2S_CFG1_REG 0x08 |
| 33 | #define STM32_I2S_CFG2_REG 0x0C |
| 34 | #define STM32_I2S_IER_REG 0x10 |
| 35 | #define STM32_I2S_SR_REG 0x14 |
| 36 | #define STM32_I2S_IFCR_REG 0x18 |
| 37 | #define STM32_I2S_TXDR_REG 0X20 |
| 38 | #define STM32_I2S_RXDR_REG 0x30 |
| 39 | #define STM32_I2S_CGFR_REG 0X50 |
| 40 | |
| 41 | /* Bit definition for SPI2S_CR1 register */ |
| 42 | #define I2S_CR1_SPE BIT(0) |
| 43 | #define I2S_CR1_CSTART BIT(9) |
| 44 | #define I2S_CR1_CSUSP BIT(10) |
| 45 | #define I2S_CR1_HDDIR BIT(11) |
| 46 | #define I2S_CR1_SSI BIT(12) |
| 47 | #define I2S_CR1_CRC33_17 BIT(13) |
| 48 | #define I2S_CR1_RCRCI BIT(14) |
| 49 | #define I2S_CR1_TCRCI BIT(15) |
| 50 | |
| 51 | /* Bit definition for SPI_CFG2 register */ |
| 52 | #define I2S_CFG2_IOSWP_SHIFT 15 |
| 53 | #define I2S_CFG2_IOSWP BIT(I2S_CFG2_IOSWP_SHIFT) |
| 54 | #define I2S_CFG2_LSBFRST BIT(23) |
| 55 | #define I2S_CFG2_AFCNTR BIT(31) |
| 56 | |
| 57 | /* Bit definition for SPI_CFG1 register */ |
| 58 | #define I2S_CFG1_FTHVL_SHIFT 5 |
| 59 | #define I2S_CFG1_FTHVL_MASK GENMASK(8, I2S_CFG1_FTHVL_SHIFT) |
| 60 | #define I2S_CFG1_FTHVL_SET(x) ((x) << I2S_CFG1_FTHVL_SHIFT) |
| 61 | |
| 62 | #define I2S_CFG1_TXDMAEN BIT(15) |
| 63 | #define I2S_CFG1_RXDMAEN BIT(14) |
| 64 | |
| 65 | /* Bit definition for SPI2S_IER register */ |
| 66 | #define I2S_IER_RXPIE BIT(0) |
| 67 | #define I2S_IER_TXPIE BIT(1) |
| 68 | #define I2S_IER_DPXPIE BIT(2) |
| 69 | #define I2S_IER_EOTIE BIT(3) |
| 70 | #define I2S_IER_TXTFIE BIT(4) |
| 71 | #define I2S_IER_UDRIE BIT(5) |
| 72 | #define I2S_IER_OVRIE BIT(6) |
| 73 | #define I2S_IER_CRCEIE BIT(7) |
| 74 | #define I2S_IER_TIFREIE BIT(8) |
| 75 | #define I2S_IER_MODFIE BIT(9) |
| 76 | #define I2S_IER_TSERFIE BIT(10) |
| 77 | |
| 78 | /* Bit definition for SPI2S_SR register */ |
| 79 | #define I2S_SR_RXP BIT(0) |
| 80 | #define I2S_SR_TXP BIT(1) |
| 81 | #define I2S_SR_DPXP BIT(2) |
| 82 | #define I2S_SR_EOT BIT(3) |
| 83 | #define I2S_SR_TXTF BIT(4) |
| 84 | #define I2S_SR_UDR BIT(5) |
| 85 | #define I2S_SR_OVR BIT(6) |
| 86 | #define I2S_SR_CRCERR BIT(7) |
| 87 | #define I2S_SR_TIFRE BIT(8) |
| 88 | #define I2S_SR_MODF BIT(9) |
| 89 | #define I2S_SR_TSERF BIT(10) |
| 90 | #define I2S_SR_SUSP BIT(11) |
| 91 | #define I2S_SR_TXC BIT(12) |
| 92 | #define I2S_SR_RXPLVL GENMASK(14, 13) |
| 93 | #define I2S_SR_RXWNE BIT(15) |
| 94 | |
| 95 | #define I2S_SR_MASK GENMASK(15, 0) |
| 96 | |
| 97 | /* Bit definition for SPI_IFCR register */ |
| 98 | #define I2S_IFCR_EOTC BIT(3) |
| 99 | #define I2S_IFCR_TXTFC BIT(4) |
| 100 | #define I2S_IFCR_UDRC BIT(5) |
| 101 | #define I2S_IFCR_OVRC BIT(6) |
| 102 | #define I2S_IFCR_CRCEC BIT(7) |
| 103 | #define I2S_IFCR_TIFREC BIT(8) |
| 104 | #define I2S_IFCR_MODFC BIT(9) |
| 105 | #define I2S_IFCR_TSERFC BIT(10) |
| 106 | #define I2S_IFCR_SUSPC BIT(11) |
| 107 | |
| 108 | #define I2S_IFCR_MASK GENMASK(11, 3) |
| 109 | |
| 110 | /* Bit definition for SPI_I2SCGFR register */ |
| 111 | #define I2S_CGFR_I2SMOD BIT(0) |
| 112 | |
| 113 | #define I2S_CGFR_I2SCFG_SHIFT 1 |
| 114 | #define I2S_CGFR_I2SCFG_MASK GENMASK(3, I2S_CGFR_I2SCFG_SHIFT) |
| 115 | #define I2S_CGFR_I2SCFG_SET(x) ((x) << I2S_CGFR_I2SCFG_SHIFT) |
| 116 | |
| 117 | #define I2S_CGFR_I2SSTD_SHIFT 4 |
| 118 | #define I2S_CGFR_I2SSTD_MASK GENMASK(5, I2S_CGFR_I2SSTD_SHIFT) |
| 119 | #define I2S_CGFR_I2SSTD_SET(x) ((x) << I2S_CGFR_I2SSTD_SHIFT) |
| 120 | |
| 121 | #define I2S_CGFR_PCMSYNC BIT(7) |
| 122 | |
| 123 | #define I2S_CGFR_DATLEN_SHIFT 8 |
| 124 | #define I2S_CGFR_DATLEN_MASK GENMASK(9, I2S_CGFR_DATLEN_SHIFT) |
| 125 | #define I2S_CGFR_DATLEN_SET(x) ((x) << I2S_CGFR_DATLEN_SHIFT) |
| 126 | |
| 127 | #define I2S_CGFR_CHLEN_SHIFT 10 |
| 128 | #define I2S_CGFR_CHLEN BIT(I2S_CGFR_CHLEN_SHIFT) |
| 129 | #define I2S_CGFR_CKPOL BIT(11) |
| 130 | #define I2S_CGFR_FIXCH BIT(12) |
| 131 | #define I2S_CGFR_WSINV BIT(13) |
| 132 | #define I2S_CGFR_DATFMT BIT(14) |
| 133 | |
| 134 | #define I2S_CGFR_I2SDIV_SHIFT 16 |
| 135 | #define I2S_CGFR_I2SDIV_BIT_H 23 |
| 136 | #define I2S_CGFR_I2SDIV_MASK GENMASK(I2S_CGFR_I2SDIV_BIT_H,\ |
| 137 | I2S_CGFR_I2SDIV_SHIFT) |
| 138 | #define I2S_CGFR_I2SDIV_SET(x) ((x) << I2S_CGFR_I2SDIV_SHIFT) |
| 139 | #define I2S_CGFR_I2SDIV_MAX ((1 << (I2S_CGFR_I2SDIV_BIT_H -\ |
| 140 | I2S_CGFR_I2SDIV_SHIFT)) - 1) |
| 141 | |
| 142 | #define I2S_CGFR_ODD_SHIFT 24 |
| 143 | #define I2S_CGFR_ODD BIT(I2S_CGFR_ODD_SHIFT) |
| 144 | #define I2S_CGFR_MCKOE BIT(25) |
| 145 | |
| 146 | enum i2s_master_mode { |
| 147 | I2S_MS_NOT_SET, |
| 148 | I2S_MS_MASTER, |
| 149 | I2S_MS_SLAVE, |
| 150 | }; |
| 151 | |
| 152 | enum i2s_mode { |
| 153 | I2S_I2SMOD_TX_SLAVE, |
| 154 | I2S_I2SMOD_RX_SLAVE, |
| 155 | I2S_I2SMOD_TX_MASTER, |
| 156 | I2S_I2SMOD_RX_MASTER, |
| 157 | I2S_I2SMOD_FD_SLAVE, |
| 158 | I2S_I2SMOD_FD_MASTER, |
| 159 | }; |
| 160 | |
| 161 | enum i2s_fifo_th { |
| 162 | I2S_FIFO_TH_NONE, |
| 163 | I2S_FIFO_TH_ONE_QUARTER, |
| 164 | I2S_FIFO_TH_HALF, |
| 165 | I2S_FIFO_TH_THREE_QUARTER, |
| 166 | I2S_FIFO_TH_FULL, |
| 167 | }; |
| 168 | |
| 169 | enum i2s_std { |
| 170 | I2S_STD_I2S, |
| 171 | I2S_STD_LEFT_J, |
| 172 | I2S_STD_RIGHT_J, |
| 173 | I2S_STD_DSP, |
| 174 | }; |
| 175 | |
| 176 | enum i2s_datlen { |
| 177 | I2S_I2SMOD_DATLEN_16, |
| 178 | I2S_I2SMOD_DATLEN_24, |
| 179 | I2S_I2SMOD_DATLEN_32, |
| 180 | }; |
| 181 | |
| 182 | #define STM32_I2S_DAI_NAME_SIZE 20 |
| 183 | #define STM32_I2S_FIFO_SIZE 16 |
| 184 | |
| 185 | #define STM32_I2S_IS_MASTER(x) ((x)->ms_flg == I2S_MS_MASTER) |
| 186 | #define STM32_I2S_IS_SLAVE(x) ((x)->ms_flg == I2S_MS_SLAVE) |
| 187 | |
| 188 | /** |
Olivier Moysan | 307cce4 | 2019-02-08 11:49:53 +0100 | [diff] [blame] | 189 | * struct stm32_i2s_data - private data of I2S |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 190 | * @regmap_conf: I2S register map configuration pointer |
Olivier Moysan | 307cce4 | 2019-02-08 11:49:53 +0100 | [diff] [blame] | 191 | * @regmap: I2S register map pointer |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 192 | * @pdev: device data pointer |
| 193 | * @dai_drv: DAI driver pointer |
| 194 | * @dma_data_tx: dma configuration data for tx channel |
| 195 | * @dma_data_rx: dma configuration data for tx channel |
| 196 | * @substream: PCM substream data pointer |
| 197 | * @i2sclk: kernel clock feeding the I2S clock generator |
| 198 | * @pclk: peripheral clock driving bus interface |
| 199 | * @x8kclk: I2S parent clock for sampling frequencies multiple of 8kHz |
| 200 | * @x11kclk: I2S parent clock for sampling frequencies multiple of 11kHz |
| 201 | * @base: mmio register base virtual address |
| 202 | * @phys_addr: I2S registers physical base address |
| 203 | * @lock_fd: lock to manage race conditions in full duplex mode |
| 204 | * @dais_name: DAI name |
| 205 | * @mclk_rate: master clock frequency (Hz) |
| 206 | * @fmt: DAI protocol |
| 207 | * @refcount: keep count of opened streams on I2S |
| 208 | * @ms_flg: master mode flag. |
| 209 | */ |
| 210 | struct stm32_i2s_data { |
| 211 | const struct regmap_config *regmap_conf; |
| 212 | struct regmap *regmap; |
| 213 | struct platform_device *pdev; |
| 214 | struct snd_soc_dai_driver *dai_drv; |
| 215 | struct snd_dmaengine_dai_dma_data dma_data_tx; |
| 216 | struct snd_dmaengine_dai_dma_data dma_data_rx; |
| 217 | struct snd_pcm_substream *substream; |
| 218 | struct clk *i2sclk; |
| 219 | struct clk *pclk; |
| 220 | struct clk *x8kclk; |
| 221 | struct clk *x11kclk; |
| 222 | void __iomem *base; |
| 223 | dma_addr_t phys_addr; |
| 224 | spinlock_t lock_fd; /* Manage race conditions for full duplex */ |
| 225 | char dais_name[STM32_I2S_DAI_NAME_SIZE]; |
| 226 | unsigned int mclk_rate; |
| 227 | unsigned int fmt; |
| 228 | int refcount; |
| 229 | int ms_flg; |
| 230 | }; |
| 231 | |
| 232 | static irqreturn_t stm32_i2s_isr(int irq, void *devid) |
| 233 | { |
| 234 | struct stm32_i2s_data *i2s = (struct stm32_i2s_data *)devid; |
| 235 | struct platform_device *pdev = i2s->pdev; |
| 236 | u32 sr, ier; |
| 237 | unsigned long flags; |
| 238 | int err = 0; |
| 239 | |
| 240 | regmap_read(i2s->regmap, STM32_I2S_SR_REG, &sr); |
| 241 | regmap_read(i2s->regmap, STM32_I2S_IER_REG, &ier); |
| 242 | |
| 243 | flags = sr & ier; |
| 244 | if (!flags) { |
| 245 | dev_dbg(&pdev->dev, "Spurious IRQ sr=0x%08x, ier=0x%08x\n", |
| 246 | sr, ier); |
| 247 | return IRQ_NONE; |
| 248 | } |
| 249 | |
Olivier Moysan | 8ba3c52 | 2019-02-26 14:51:04 +0100 | [diff] [blame] | 250 | regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, |
| 251 | I2S_IFCR_MASK, flags); |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 252 | |
| 253 | if (flags & I2S_SR_OVR) { |
| 254 | dev_dbg(&pdev->dev, "Overrun\n"); |
| 255 | err = 1; |
| 256 | } |
| 257 | |
| 258 | if (flags & I2S_SR_UDR) { |
| 259 | dev_dbg(&pdev->dev, "Underrun\n"); |
| 260 | err = 1; |
| 261 | } |
| 262 | |
| 263 | if (flags & I2S_SR_TIFRE) |
| 264 | dev_dbg(&pdev->dev, "Frame error\n"); |
| 265 | |
| 266 | if (err) |
| 267 | snd_pcm_stop_xrun(i2s->substream); |
| 268 | |
| 269 | return IRQ_HANDLED; |
| 270 | } |
| 271 | |
| 272 | static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg) |
| 273 | { |
| 274 | switch (reg) { |
| 275 | case STM32_I2S_CR1_REG: |
| 276 | case STM32_I2S_CFG1_REG: |
| 277 | case STM32_I2S_CFG2_REG: |
| 278 | case STM32_I2S_IER_REG: |
| 279 | case STM32_I2S_SR_REG: |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 280 | case STM32_I2S_TXDR_REG: |
| 281 | case STM32_I2S_RXDR_REG: |
| 282 | case STM32_I2S_CGFR_REG: |
| 283 | return true; |
| 284 | default: |
| 285 | return false; |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | static bool stm32_i2s_volatile_reg(struct device *dev, unsigned int reg) |
| 290 | { |
| 291 | switch (reg) { |
| 292 | case STM32_I2S_TXDR_REG: |
| 293 | case STM32_I2S_RXDR_REG: |
| 294 | return true; |
| 295 | default: |
| 296 | return false; |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | static bool stm32_i2s_writeable_reg(struct device *dev, unsigned int reg) |
| 301 | { |
| 302 | switch (reg) { |
| 303 | case STM32_I2S_CR1_REG: |
| 304 | case STM32_I2S_CFG1_REG: |
| 305 | case STM32_I2S_CFG2_REG: |
| 306 | case STM32_I2S_IER_REG: |
| 307 | case STM32_I2S_IFCR_REG: |
| 308 | case STM32_I2S_TXDR_REG: |
| 309 | case STM32_I2S_CGFR_REG: |
| 310 | return true; |
| 311 | default: |
| 312 | return false; |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | static int stm32_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) |
| 317 | { |
| 318 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); |
| 319 | u32 cgfr; |
| 320 | u32 cgfr_mask = I2S_CGFR_I2SSTD_MASK | I2S_CGFR_CKPOL | |
| 321 | I2S_CGFR_WSINV | I2S_CGFR_I2SCFG_MASK; |
| 322 | |
| 323 | dev_dbg(cpu_dai->dev, "fmt %x\n", fmt); |
| 324 | |
| 325 | /* |
| 326 | * winv = 0 : default behavior (high/low) for all standards |
| 327 | * ckpol = 0 for all standards. |
| 328 | */ |
| 329 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
| 330 | case SND_SOC_DAIFMT_I2S: |
| 331 | cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_I2S); |
| 332 | break; |
| 333 | case SND_SOC_DAIFMT_MSB: |
| 334 | cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_LEFT_J); |
| 335 | break; |
| 336 | case SND_SOC_DAIFMT_LSB: |
| 337 | cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_RIGHT_J); |
| 338 | break; |
| 339 | case SND_SOC_DAIFMT_DSP_A: |
| 340 | cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_DSP); |
| 341 | break; |
| 342 | /* DSP_B not mapped on I2S PCM long format. 1 bit offset does not fit */ |
| 343 | default: |
| 344 | dev_err(cpu_dai->dev, "Unsupported protocol %#x\n", |
| 345 | fmt & SND_SOC_DAIFMT_FORMAT_MASK); |
| 346 | return -EINVAL; |
| 347 | } |
| 348 | |
| 349 | /* DAI clock strobing */ |
| 350 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
| 351 | case SND_SOC_DAIFMT_NB_NF: |
| 352 | break; |
| 353 | case SND_SOC_DAIFMT_IB_NF: |
| 354 | cgfr |= I2S_CGFR_CKPOL; |
| 355 | break; |
| 356 | case SND_SOC_DAIFMT_NB_IF: |
| 357 | cgfr |= I2S_CGFR_WSINV; |
| 358 | break; |
| 359 | case SND_SOC_DAIFMT_IB_IF: |
| 360 | cgfr |= I2S_CGFR_CKPOL; |
| 361 | cgfr |= I2S_CGFR_WSINV; |
| 362 | break; |
| 363 | default: |
| 364 | dev_err(cpu_dai->dev, "Unsupported strobing %#x\n", |
| 365 | fmt & SND_SOC_DAIFMT_INV_MASK); |
| 366 | return -EINVAL; |
| 367 | } |
| 368 | |
| 369 | /* DAI clock master masks */ |
| 370 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
| 371 | case SND_SOC_DAIFMT_CBM_CFM: |
| 372 | i2s->ms_flg = I2S_MS_SLAVE; |
| 373 | break; |
| 374 | case SND_SOC_DAIFMT_CBS_CFS: |
| 375 | i2s->ms_flg = I2S_MS_MASTER; |
| 376 | break; |
| 377 | default: |
| 378 | dev_err(cpu_dai->dev, "Unsupported mode %#x\n", |
| 379 | fmt & SND_SOC_DAIFMT_MASTER_MASK); |
| 380 | return -EINVAL; |
| 381 | } |
| 382 | |
| 383 | i2s->fmt = fmt; |
| 384 | return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, |
| 385 | cgfr_mask, cgfr); |
| 386 | } |
| 387 | |
| 388 | static int stm32_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, |
| 389 | int clk_id, unsigned int freq, int dir) |
| 390 | { |
| 391 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); |
| 392 | |
| 393 | dev_dbg(cpu_dai->dev, "I2S MCLK frequency is %uHz\n", freq); |
| 394 | |
| 395 | if ((dir == SND_SOC_CLOCK_OUT) && STM32_I2S_IS_MASTER(i2s)) { |
| 396 | i2s->mclk_rate = freq; |
| 397 | |
| 398 | /* Enable master clock if master mode and mclk-fs are set */ |
| 399 | return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, |
| 400 | I2S_CGFR_MCKOE, I2S_CGFR_MCKOE); |
| 401 | } |
| 402 | |
| 403 | return 0; |
| 404 | } |
| 405 | |
| 406 | static int stm32_i2s_configure_clock(struct snd_soc_dai *cpu_dai, |
| 407 | struct snd_pcm_hw_params *params) |
| 408 | { |
| 409 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); |
| 410 | unsigned long i2s_clock_rate; |
| 411 | unsigned int tmp, div, real_div, nb_bits, frame_len; |
| 412 | unsigned int rate = params_rate(params); |
| 413 | int ret; |
| 414 | u32 cgfr, cgfr_mask; |
| 415 | bool odd; |
| 416 | |
| 417 | if (!(rate % 11025)) |
| 418 | clk_set_parent(i2s->i2sclk, i2s->x11kclk); |
| 419 | else |
| 420 | clk_set_parent(i2s->i2sclk, i2s->x8kclk); |
| 421 | i2s_clock_rate = clk_get_rate(i2s->i2sclk); |
| 422 | |
| 423 | /* |
| 424 | * mckl = mclk_ratio x ws |
| 425 | * i2s mode : mclk_ratio = 256 |
| 426 | * dsp mode : mclk_ratio = 128 |
| 427 | * |
| 428 | * mclk on |
| 429 | * i2s mode : div = i2s_clk / (mclk_ratio * ws) |
| 430 | * dsp mode : div = i2s_clk / (mclk_ratio * ws) |
| 431 | * mclk off |
| 432 | * i2s mode : div = i2s_clk / (nb_bits x ws) |
| 433 | * dsp mode : div = i2s_clk / (nb_bits x ws) |
| 434 | */ |
| 435 | if (i2s->mclk_rate) { |
| 436 | tmp = DIV_ROUND_CLOSEST(i2s_clock_rate, i2s->mclk_rate); |
| 437 | } else { |
| 438 | frame_len = 32; |
| 439 | if ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == |
| 440 | SND_SOC_DAIFMT_DSP_A) |
| 441 | frame_len = 16; |
| 442 | |
| 443 | /* master clock not enabled */ |
| 444 | ret = regmap_read(i2s->regmap, STM32_I2S_CGFR_REG, &cgfr); |
| 445 | if (ret < 0) |
| 446 | return ret; |
| 447 | |
| 448 | nb_bits = frame_len * ((cgfr & I2S_CGFR_CHLEN) + 1); |
| 449 | tmp = DIV_ROUND_CLOSEST(i2s_clock_rate, (nb_bits * rate)); |
| 450 | } |
| 451 | |
| 452 | /* Check the parity of the divider */ |
| 453 | odd = tmp & 0x1; |
| 454 | |
| 455 | /* Compute the div prescaler */ |
| 456 | div = tmp >> 1; |
| 457 | |
| 458 | cgfr = I2S_CGFR_I2SDIV_SET(div) | (odd << I2S_CGFR_ODD_SHIFT); |
| 459 | cgfr_mask = I2S_CGFR_I2SDIV_MASK | I2S_CGFR_ODD; |
| 460 | |
| 461 | real_div = ((2 * div) + odd); |
| 462 | dev_dbg(cpu_dai->dev, "I2S clk: %ld, SCLK: %d\n", |
| 463 | i2s_clock_rate, rate); |
| 464 | dev_dbg(cpu_dai->dev, "Divider: 2*%d(div)+%d(odd) = %d\n", |
| 465 | div, odd, real_div); |
| 466 | |
| 467 | if (((div == 1) && odd) || (div > I2S_CGFR_I2SDIV_MAX)) { |
| 468 | dev_err(cpu_dai->dev, "Wrong divider setting\n"); |
| 469 | return -EINVAL; |
| 470 | } |
| 471 | |
| 472 | if (!div && !odd) |
| 473 | dev_warn(cpu_dai->dev, "real divider forced to 1\n"); |
| 474 | |
| 475 | ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, |
| 476 | cgfr_mask, cgfr); |
| 477 | if (ret < 0) |
| 478 | return ret; |
| 479 | |
| 480 | /* Set bitclock and frameclock to their inactive state */ |
| 481 | return regmap_update_bits(i2s->regmap, STM32_I2S_CFG2_REG, |
| 482 | I2S_CFG2_AFCNTR, I2S_CFG2_AFCNTR); |
| 483 | } |
| 484 | |
| 485 | static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, |
| 486 | struct snd_pcm_hw_params *params, |
| 487 | struct snd_pcm_substream *substream) |
| 488 | { |
| 489 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); |
| 490 | int format = params_width(params); |
| 491 | u32 cfgr, cfgr_mask, cfg1, cfg1_mask; |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 492 | unsigned int fthlv; |
| 493 | int ret; |
| 494 | |
| 495 | if ((params_channels(params) == 1) && |
| 496 | ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_DSP_A)) { |
| 497 | dev_err(cpu_dai->dev, "Mono mode supported only by DSP_A\n"); |
| 498 | return -EINVAL; |
| 499 | } |
| 500 | |
| 501 | switch (format) { |
| 502 | case 16: |
| 503 | cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_16); |
Olivier Moysan | 0c4c68d | 2019-02-26 14:51:05 +0100 | [diff] [blame] | 504 | cfgr_mask = I2S_CGFR_DATLEN_MASK | I2S_CGFR_CHLEN; |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 505 | break; |
| 506 | case 32: |
| 507 | cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_32) | |
| 508 | I2S_CGFR_CHLEN; |
| 509 | cfgr_mask = I2S_CGFR_DATLEN_MASK | I2S_CGFR_CHLEN; |
| 510 | break; |
| 511 | default: |
| 512 | dev_err(cpu_dai->dev, "Unexpected format %d", format); |
| 513 | return -EINVAL; |
| 514 | } |
| 515 | |
| 516 | if (STM32_I2S_IS_SLAVE(i2s)) { |
olivier moysan | e7cc49b | 2017-05-18 17:19:53 +0200 | [diff] [blame] | 517 | cfgr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_FD_SLAVE); |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 518 | |
| 519 | /* As data length is either 16 or 32 bits, fixch always set */ |
| 520 | cfgr |= I2S_CGFR_FIXCH; |
| 521 | cfgr_mask |= I2S_CGFR_FIXCH; |
| 522 | } else { |
olivier moysan | e7cc49b | 2017-05-18 17:19:53 +0200 | [diff] [blame] | 523 | cfgr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_FD_MASTER); |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 524 | } |
| 525 | cfgr_mask |= I2S_CGFR_I2SCFG_MASK; |
| 526 | |
| 527 | ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, |
| 528 | cfgr_mask, cfgr); |
| 529 | if (ret < 0) |
| 530 | return ret; |
| 531 | |
olivier moysan | e7cc49b | 2017-05-18 17:19:53 +0200 | [diff] [blame] | 532 | cfg1 = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 533 | cfg1_mask = cfg1; |
| 534 | |
| 535 | fthlv = STM32_I2S_FIFO_SIZE * I2S_FIFO_TH_ONE_QUARTER / 4; |
| 536 | cfg1 |= I2S_CFG1_FTHVL_SET(fthlv - 1); |
| 537 | cfg1_mask |= I2S_CFG1_FTHVL_MASK; |
| 538 | |
| 539 | return regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, |
| 540 | cfg1_mask, cfg1); |
| 541 | } |
| 542 | |
| 543 | static int stm32_i2s_startup(struct snd_pcm_substream *substream, |
| 544 | struct snd_soc_dai *cpu_dai) |
| 545 | { |
| 546 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); |
Olivier Moysan | 6a68eee | 2019-02-08 11:49:54 +0100 | [diff] [blame] | 547 | int ret; |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 548 | |
| 549 | i2s->substream = substream; |
| 550 | |
Olivier Moysan | 6a68eee | 2019-02-08 11:49:54 +0100 | [diff] [blame] | 551 | ret = clk_prepare_enable(i2s->i2sclk); |
| 552 | if (ret < 0) { |
| 553 | dev_err(cpu_dai->dev, "Failed to enable clock: %d\n", ret); |
| 554 | return ret; |
| 555 | } |
| 556 | |
Olivier Moysan | 8ba3c52 | 2019-02-26 14:51:04 +0100 | [diff] [blame] | 557 | return regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, |
| 558 | I2S_IFCR_MASK, I2S_IFCR_MASK); |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 559 | } |
| 560 | |
| 561 | static int stm32_i2s_hw_params(struct snd_pcm_substream *substream, |
| 562 | struct snd_pcm_hw_params *params, |
| 563 | struct snd_soc_dai *cpu_dai) |
| 564 | { |
| 565 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); |
| 566 | int ret; |
| 567 | |
| 568 | ret = stm32_i2s_configure(cpu_dai, params, substream); |
| 569 | if (ret < 0) { |
| 570 | dev_err(cpu_dai->dev, "Configuration returned error %d\n", ret); |
| 571 | return ret; |
| 572 | } |
| 573 | |
| 574 | if (STM32_I2S_IS_MASTER(i2s)) |
| 575 | ret = stm32_i2s_configure_clock(cpu_dai, params); |
| 576 | |
| 577 | return ret; |
| 578 | } |
| 579 | |
| 580 | static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, |
| 581 | struct snd_soc_dai *cpu_dai) |
| 582 | { |
| 583 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); |
| 584 | bool playback_flg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); |
olivier moysan | e7cc49b | 2017-05-18 17:19:53 +0200 | [diff] [blame] | 585 | u32 cfg1_mask, ier; |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 586 | int ret; |
| 587 | |
| 588 | switch (cmd) { |
| 589 | case SNDRV_PCM_TRIGGER_START: |
| 590 | case SNDRV_PCM_TRIGGER_RESUME: |
| 591 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
| 592 | /* Enable i2s */ |
| 593 | dev_dbg(cpu_dai->dev, "start I2S\n"); |
| 594 | |
| 595 | ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, |
| 596 | I2S_CR1_SPE, I2S_CR1_SPE); |
| 597 | if (ret < 0) { |
| 598 | dev_err(cpu_dai->dev, "Error %d enabling I2S\n", ret); |
| 599 | return ret; |
| 600 | } |
| 601 | |
Olivier Moysan | 307cce4 | 2019-02-08 11:49:53 +0100 | [diff] [blame] | 602 | ret = regmap_write_bits(i2s->regmap, STM32_I2S_CR1_REG, |
| 603 | I2S_CR1_CSTART, I2S_CR1_CSTART); |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 604 | if (ret < 0) { |
| 605 | dev_err(cpu_dai->dev, "Error %d starting I2S\n", ret); |
| 606 | return ret; |
| 607 | } |
olivier moysan | e7cc49b | 2017-05-18 17:19:53 +0200 | [diff] [blame] | 608 | |
Olivier Moysan | 8ba3c52 | 2019-02-26 14:51:04 +0100 | [diff] [blame] | 609 | regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, |
| 610 | I2S_IFCR_MASK, I2S_IFCR_MASK); |
olivier moysan | e7cc49b | 2017-05-18 17:19:53 +0200 | [diff] [blame] | 611 | |
Olivier Moysan | ebf629d | 2019-02-26 14:51:06 +0100 | [diff] [blame^] | 612 | spin_lock(&i2s->lock_fd); |
| 613 | i2s->refcount++; |
olivier moysan | e7cc49b | 2017-05-18 17:19:53 +0200 | [diff] [blame] | 614 | if (playback_flg) { |
| 615 | ier = I2S_IER_UDRIE; |
| 616 | } else { |
| 617 | ier = I2S_IER_OVRIE; |
| 618 | |
olivier moysan | e7cc49b | 2017-05-18 17:19:53 +0200 | [diff] [blame] | 619 | if (i2s->refcount == 1) |
| 620 | /* dummy write to trigger capture */ |
| 621 | regmap_write(i2s->regmap, |
| 622 | STM32_I2S_TXDR_REG, 0); |
olivier moysan | e7cc49b | 2017-05-18 17:19:53 +0200 | [diff] [blame] | 623 | } |
Olivier Moysan | ebf629d | 2019-02-26 14:51:06 +0100 | [diff] [blame^] | 624 | spin_unlock(&i2s->lock_fd); |
olivier moysan | e7cc49b | 2017-05-18 17:19:53 +0200 | [diff] [blame] | 625 | |
| 626 | if (STM32_I2S_IS_SLAVE(i2s)) |
| 627 | ier |= I2S_IER_TIFREIE; |
| 628 | |
| 629 | regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, ier, ier); |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 630 | break; |
| 631 | case SNDRV_PCM_TRIGGER_STOP: |
| 632 | case SNDRV_PCM_TRIGGER_SUSPEND: |
| 633 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
olivier moysan | e7cc49b | 2017-05-18 17:19:53 +0200 | [diff] [blame] | 634 | if (playback_flg) |
| 635 | regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, |
| 636 | I2S_IER_UDRIE, |
| 637 | (unsigned int)~I2S_IER_UDRIE); |
| 638 | else |
| 639 | regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, |
| 640 | I2S_IER_OVRIE, |
| 641 | (unsigned int)~I2S_IER_OVRIE); |
| 642 | |
| 643 | spin_lock(&i2s->lock_fd); |
| 644 | i2s->refcount--; |
| 645 | if (i2s->refcount) { |
| 646 | spin_unlock(&i2s->lock_fd); |
| 647 | break; |
| 648 | } |
olivier moysan | e7cc49b | 2017-05-18 17:19:53 +0200 | [diff] [blame] | 649 | |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 650 | dev_dbg(cpu_dai->dev, "stop I2S\n"); |
| 651 | |
| 652 | ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, |
| 653 | I2S_CR1_SPE, 0); |
| 654 | if (ret < 0) { |
| 655 | dev_err(cpu_dai->dev, "Error %d disabling I2S\n", ret); |
Olivier Moysan | ebf629d | 2019-02-26 14:51:06 +0100 | [diff] [blame^] | 656 | spin_unlock(&i2s->lock_fd); |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 657 | return ret; |
| 658 | } |
Olivier Moysan | ebf629d | 2019-02-26 14:51:06 +0100 | [diff] [blame^] | 659 | spin_unlock(&i2s->lock_fd); |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 660 | |
olivier moysan | e7cc49b | 2017-05-18 17:19:53 +0200 | [diff] [blame] | 661 | cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 662 | regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, |
| 663 | cfg1_mask, 0); |
| 664 | break; |
| 665 | default: |
| 666 | return -EINVAL; |
| 667 | } |
| 668 | |
| 669 | return 0; |
| 670 | } |
| 671 | |
| 672 | static void stm32_i2s_shutdown(struct snd_pcm_substream *substream, |
| 673 | struct snd_soc_dai *cpu_dai) |
| 674 | { |
| 675 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); |
| 676 | |
| 677 | i2s->substream = NULL; |
| 678 | |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 679 | regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, |
| 680 | I2S_CGFR_MCKOE, (unsigned int)~I2S_CGFR_MCKOE); |
Olivier Moysan | 6a68eee | 2019-02-08 11:49:54 +0100 | [diff] [blame] | 681 | |
| 682 | clk_disable_unprepare(i2s->i2sclk); |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 683 | } |
| 684 | |
| 685 | static int stm32_i2s_dai_probe(struct snd_soc_dai *cpu_dai) |
| 686 | { |
| 687 | struct stm32_i2s_data *i2s = dev_get_drvdata(cpu_dai->dev); |
| 688 | struct snd_dmaengine_dai_dma_data *dma_data_tx = &i2s->dma_data_tx; |
| 689 | struct snd_dmaengine_dai_dma_data *dma_data_rx = &i2s->dma_data_rx; |
| 690 | |
| 691 | /* Buswidth will be set by framework */ |
| 692 | dma_data_tx->addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; |
| 693 | dma_data_tx->addr = (dma_addr_t)(i2s->phys_addr) + STM32_I2S_TXDR_REG; |
| 694 | dma_data_tx->maxburst = 1; |
| 695 | dma_data_rx->addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; |
| 696 | dma_data_rx->addr = (dma_addr_t)(i2s->phys_addr) + STM32_I2S_RXDR_REG; |
| 697 | dma_data_rx->maxburst = 1; |
| 698 | |
| 699 | snd_soc_dai_init_dma_data(cpu_dai, dma_data_tx, dma_data_rx); |
| 700 | |
| 701 | return 0; |
| 702 | } |
| 703 | |
| 704 | static const struct regmap_config stm32_h7_i2s_regmap_conf = { |
| 705 | .reg_bits = 32, |
| 706 | .reg_stride = 4, |
| 707 | .val_bits = 32, |
| 708 | .max_register = STM32_I2S_CGFR_REG, |
| 709 | .readable_reg = stm32_i2s_readable_reg, |
| 710 | .volatile_reg = stm32_i2s_volatile_reg, |
| 711 | .writeable_reg = stm32_i2s_writeable_reg, |
| 712 | .fast_io = true, |
Olivier Moysan | 307cce4 | 2019-02-08 11:49:53 +0100 | [diff] [blame] | 713 | .cache_type = REGCACHE_FLAT, |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 714 | }; |
| 715 | |
| 716 | static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = { |
| 717 | .set_sysclk = stm32_i2s_set_sysclk, |
| 718 | .set_fmt = stm32_i2s_set_dai_fmt, |
| 719 | .startup = stm32_i2s_startup, |
| 720 | .hw_params = stm32_i2s_hw_params, |
| 721 | .trigger = stm32_i2s_trigger, |
| 722 | .shutdown = stm32_i2s_shutdown, |
| 723 | }; |
| 724 | |
| 725 | static const struct snd_pcm_hardware stm32_i2s_pcm_hw = { |
| 726 | .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP, |
| 727 | .buffer_bytes_max = 8 * PAGE_SIZE, |
| 728 | .period_bytes_max = 2048, |
| 729 | .periods_min = 2, |
| 730 | .periods_max = 8, |
| 731 | }; |
| 732 | |
| 733 | static const struct snd_dmaengine_pcm_config stm32_i2s_pcm_config = { |
| 734 | .pcm_hardware = &stm32_i2s_pcm_hw, |
| 735 | .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, |
| 736 | .prealloc_buffer_size = PAGE_SIZE * 8, |
| 737 | }; |
| 738 | |
| 739 | static const struct snd_soc_component_driver stm32_i2s_component = { |
| 740 | .name = "stm32-i2s", |
| 741 | }; |
| 742 | |
| 743 | static void stm32_i2s_dai_init(struct snd_soc_pcm_stream *stream, |
| 744 | char *stream_name) |
| 745 | { |
| 746 | stream->stream_name = stream_name; |
| 747 | stream->channels_min = 1; |
| 748 | stream->channels_max = 2; |
| 749 | stream->rates = SNDRV_PCM_RATE_8000_192000; |
| 750 | stream->formats = SNDRV_PCM_FMTBIT_S16_LE | |
| 751 | SNDRV_PCM_FMTBIT_S32_LE; |
| 752 | } |
| 753 | |
| 754 | static int stm32_i2s_dais_init(struct platform_device *pdev, |
| 755 | struct stm32_i2s_data *i2s) |
| 756 | { |
| 757 | struct snd_soc_dai_driver *dai_ptr; |
| 758 | |
| 759 | dai_ptr = devm_kzalloc(&pdev->dev, sizeof(struct snd_soc_dai_driver), |
| 760 | GFP_KERNEL); |
| 761 | if (!dai_ptr) |
| 762 | return -ENOMEM; |
| 763 | |
| 764 | snprintf(i2s->dais_name, STM32_I2S_DAI_NAME_SIZE, |
| 765 | "%s", dev_name(&pdev->dev)); |
| 766 | |
| 767 | dai_ptr->probe = stm32_i2s_dai_probe; |
| 768 | dai_ptr->ops = &stm32_i2s_pcm_dai_ops; |
| 769 | dai_ptr->name = i2s->dais_name; |
| 770 | dai_ptr->id = 1; |
| 771 | stm32_i2s_dai_init(&dai_ptr->playback, "playback"); |
| 772 | stm32_i2s_dai_init(&dai_ptr->capture, "capture"); |
| 773 | i2s->dai_drv = dai_ptr; |
| 774 | |
| 775 | return 0; |
| 776 | } |
| 777 | |
| 778 | static const struct of_device_id stm32_i2s_ids[] = { |
| 779 | { |
| 780 | .compatible = "st,stm32h7-i2s", |
| 781 | .data = &stm32_h7_i2s_regmap_conf |
| 782 | }, |
| 783 | {}, |
| 784 | }; |
| 785 | |
| 786 | static int stm32_i2s_parse_dt(struct platform_device *pdev, |
| 787 | struct stm32_i2s_data *i2s) |
| 788 | { |
| 789 | struct device_node *np = pdev->dev.of_node; |
| 790 | const struct of_device_id *of_id; |
| 791 | struct reset_control *rst; |
| 792 | struct resource *res; |
| 793 | int irq, ret; |
| 794 | |
| 795 | if (!np) |
| 796 | return -ENODEV; |
| 797 | |
| 798 | of_id = of_match_device(stm32_i2s_ids, &pdev->dev); |
| 799 | if (of_id) |
| 800 | i2s->regmap_conf = (const struct regmap_config *)of_id->data; |
| 801 | else |
| 802 | return -EINVAL; |
| 803 | |
| 804 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 805 | i2s->base = devm_ioremap_resource(&pdev->dev, res); |
| 806 | if (IS_ERR(i2s->base)) |
| 807 | return PTR_ERR(i2s->base); |
| 808 | |
| 809 | i2s->phys_addr = res->start; |
| 810 | |
| 811 | /* Get clocks */ |
| 812 | i2s->pclk = devm_clk_get(&pdev->dev, "pclk"); |
| 813 | if (IS_ERR(i2s->pclk)) { |
| 814 | dev_err(&pdev->dev, "Could not get pclk\n"); |
| 815 | return PTR_ERR(i2s->pclk); |
| 816 | } |
| 817 | |
| 818 | i2s->i2sclk = devm_clk_get(&pdev->dev, "i2sclk"); |
| 819 | if (IS_ERR(i2s->i2sclk)) { |
| 820 | dev_err(&pdev->dev, "Could not get i2sclk\n"); |
| 821 | return PTR_ERR(i2s->i2sclk); |
| 822 | } |
| 823 | |
| 824 | i2s->x8kclk = devm_clk_get(&pdev->dev, "x8k"); |
| 825 | if (IS_ERR(i2s->x8kclk)) { |
| 826 | dev_err(&pdev->dev, "missing x8k parent clock\n"); |
| 827 | return PTR_ERR(i2s->x8kclk); |
| 828 | } |
| 829 | |
| 830 | i2s->x11kclk = devm_clk_get(&pdev->dev, "x11k"); |
| 831 | if (IS_ERR(i2s->x11kclk)) { |
| 832 | dev_err(&pdev->dev, "missing x11k parent clock\n"); |
| 833 | return PTR_ERR(i2s->x11kclk); |
| 834 | } |
| 835 | |
| 836 | /* Get irqs */ |
| 837 | irq = platform_get_irq(pdev, 0); |
| 838 | if (irq < 0) { |
| 839 | dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); |
| 840 | return -ENOENT; |
| 841 | } |
| 842 | |
| 843 | ret = devm_request_irq(&pdev->dev, irq, stm32_i2s_isr, IRQF_ONESHOT, |
| 844 | dev_name(&pdev->dev), i2s); |
| 845 | if (ret) { |
| 846 | dev_err(&pdev->dev, "irq request returned %d\n", ret); |
| 847 | return ret; |
| 848 | } |
| 849 | |
| 850 | /* Reset */ |
Philipp Zabel | 635eac1 | 2017-07-19 17:26:42 +0200 | [diff] [blame] | 851 | rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 852 | if (!IS_ERR(rst)) { |
| 853 | reset_control_assert(rst); |
| 854 | udelay(2); |
| 855 | reset_control_deassert(rst); |
| 856 | } |
| 857 | |
| 858 | return 0; |
| 859 | } |
| 860 | |
| 861 | static int stm32_i2s_probe(struct platform_device *pdev) |
| 862 | { |
| 863 | struct stm32_i2s_data *i2s; |
| 864 | int ret; |
| 865 | |
| 866 | i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); |
| 867 | if (!i2s) |
| 868 | return -ENOMEM; |
| 869 | |
| 870 | ret = stm32_i2s_parse_dt(pdev, i2s); |
| 871 | if (ret) |
| 872 | return ret; |
| 873 | |
| 874 | i2s->pdev = pdev; |
| 875 | i2s->ms_flg = I2S_MS_NOT_SET; |
| 876 | spin_lock_init(&i2s->lock_fd); |
| 877 | platform_set_drvdata(pdev, i2s); |
| 878 | |
| 879 | ret = stm32_i2s_dais_init(pdev, i2s); |
| 880 | if (ret) |
| 881 | return ret; |
| 882 | |
Olivier Moysan | 6a68eee | 2019-02-08 11:49:54 +0100 | [diff] [blame] | 883 | i2s->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "pclk", |
| 884 | i2s->base, i2s->regmap_conf); |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 885 | if (IS_ERR(i2s->regmap)) { |
| 886 | dev_err(&pdev->dev, "regmap init failed\n"); |
| 887 | return PTR_ERR(i2s->regmap); |
| 888 | } |
| 889 | |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 890 | ret = devm_snd_soc_register_component(&pdev->dev, &stm32_i2s_component, |
| 891 | i2s->dai_drv, 1); |
| 892 | if (ret) |
Olivier Moysan | 6a68eee | 2019-02-08 11:49:54 +0100 | [diff] [blame] | 893 | return ret; |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 894 | |
| 895 | ret = devm_snd_dmaengine_pcm_register(&pdev->dev, |
| 896 | &stm32_i2s_pcm_config, 0); |
| 897 | if (ret) |
Olivier Moysan | 6a68eee | 2019-02-08 11:49:54 +0100 | [diff] [blame] | 898 | return ret; |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 899 | |
| 900 | /* Set SPI/I2S in i2s mode */ |
Olivier Moysan | 6a68eee | 2019-02-08 11:49:54 +0100 | [diff] [blame] | 901 | return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, |
| 902 | I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD); |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 903 | } |
| 904 | |
| 905 | static int stm32_i2s_remove(struct platform_device *pdev) |
| 906 | { |
| 907 | struct stm32_i2s_data *i2s = platform_get_drvdata(pdev); |
| 908 | |
| 909 | clk_disable_unprepare(i2s->i2sclk); |
| 910 | clk_disable_unprepare(i2s->pclk); |
| 911 | |
| 912 | return 0; |
| 913 | } |
| 914 | |
| 915 | MODULE_DEVICE_TABLE(of, stm32_i2s_ids); |
| 916 | |
Olivier Moysan | 307cce4 | 2019-02-08 11:49:53 +0100 | [diff] [blame] | 917 | #ifdef CONFIG_PM_SLEEP |
| 918 | static int stm32_i2s_suspend(struct device *dev) |
| 919 | { |
| 920 | struct stm32_i2s_data *i2s = dev_get_drvdata(dev); |
| 921 | |
| 922 | regcache_cache_only(i2s->regmap, true); |
| 923 | regcache_mark_dirty(i2s->regmap); |
| 924 | |
| 925 | return 0; |
| 926 | } |
| 927 | |
| 928 | static int stm32_i2s_resume(struct device *dev) |
| 929 | { |
| 930 | struct stm32_i2s_data *i2s = dev_get_drvdata(dev); |
| 931 | |
| 932 | regcache_cache_only(i2s->regmap, false); |
| 933 | return regcache_sync(i2s->regmap); |
| 934 | } |
| 935 | #endif /* CONFIG_PM_SLEEP */ |
| 936 | |
| 937 | static const struct dev_pm_ops stm32_i2s_pm_ops = { |
| 938 | SET_SYSTEM_SLEEP_PM_OPS(stm32_i2s_suspend, stm32_i2s_resume) |
| 939 | }; |
| 940 | |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 941 | static struct platform_driver stm32_i2s_driver = { |
| 942 | .driver = { |
| 943 | .name = "st,stm32-i2s", |
| 944 | .of_match_table = stm32_i2s_ids, |
Olivier Moysan | 307cce4 | 2019-02-08 11:49:53 +0100 | [diff] [blame] | 945 | .pm = &stm32_i2s_pm_ops, |
olivier moysan | e4e6ec7 | 2017-05-18 17:19:52 +0200 | [diff] [blame] | 946 | }, |
| 947 | .probe = stm32_i2s_probe, |
| 948 | .remove = stm32_i2s_remove, |
| 949 | }; |
| 950 | |
| 951 | module_platform_driver(stm32_i2s_driver); |
| 952 | |
| 953 | MODULE_DESCRIPTION("STM32 Soc i2s Interface"); |
| 954 | MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>"); |
| 955 | MODULE_ALIAS("platform:stm32-i2s"); |
| 956 | MODULE_LICENSE("GPL v2"); |