1*3e086edfSolivier moysan /* 2*3e086edfSolivier moysan * STM32 ALSA SoC Digital Audio Interface (SAI) driver. 3*3e086edfSolivier moysan * 4*3e086edfSolivier moysan * Copyright (C) 2016, STMicroelectronics - All Rights Reserved 5*3e086edfSolivier moysan * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics. 6*3e086edfSolivier moysan * 7*3e086edfSolivier moysan * License terms: GPL V2.0. 8*3e086edfSolivier moysan * 9*3e086edfSolivier moysan * This program is free software; you can redistribute it and/or modify it 10*3e086edfSolivier moysan * under the terms of the GNU General Public License version 2 as published by 11*3e086edfSolivier moysan * the Free Software Foundation. 12*3e086edfSolivier moysan * 13*3e086edfSolivier moysan * This program is distributed in the hope that it will be useful, but 14*3e086edfSolivier moysan * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15*3e086edfSolivier moysan * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16*3e086edfSolivier moysan * details. 17*3e086edfSolivier moysan */ 18*3e086edfSolivier moysan 19*3e086edfSolivier moysan #include <linux/clk.h> 20*3e086edfSolivier moysan #include <linux/kernel.h> 21*3e086edfSolivier moysan #include <linux/module.h> 22*3e086edfSolivier moysan #include <linux/of_irq.h> 23*3e086edfSolivier moysan #include <linux/of_platform.h> 24*3e086edfSolivier moysan #include <linux/regmap.h> 25*3e086edfSolivier moysan 26*3e086edfSolivier moysan #include <sound/core.h> 27*3e086edfSolivier moysan #include <sound/dmaengine_pcm.h> 28*3e086edfSolivier moysan #include <sound/pcm_params.h> 29*3e086edfSolivier moysan 30*3e086edfSolivier moysan #include "stm32_sai.h" 31*3e086edfSolivier moysan 32*3e086edfSolivier moysan #define SAI_FREE_PROTOCOL 0x0 33*3e086edfSolivier moysan 34*3e086edfSolivier moysan #define SAI_SLOT_SIZE_AUTO 0x0 35*3e086edfSolivier moysan #define SAI_SLOT_SIZE_16 0x1 36*3e086edfSolivier moysan #define SAI_SLOT_SIZE_32 0x2 37*3e086edfSolivier moysan 38*3e086edfSolivier moysan #define SAI_DATASIZE_8 0x2 39*3e086edfSolivier moysan #define SAI_DATASIZE_10 0x3 40*3e086edfSolivier moysan #define SAI_DATASIZE_16 0x4 41*3e086edfSolivier moysan #define SAI_DATASIZE_20 0x5 42*3e086edfSolivier moysan #define SAI_DATASIZE_24 0x6 43*3e086edfSolivier moysan #define SAI_DATASIZE_32 0x7 44*3e086edfSolivier moysan 45*3e086edfSolivier moysan #define STM_SAI_FIFO_SIZE 8 46*3e086edfSolivier moysan #define STM_SAI_DAI_NAME_SIZE 15 47*3e086edfSolivier moysan 48*3e086edfSolivier moysan #define STM_SAI_IS_PLAYBACK(ip) ((ip)->dir == SNDRV_PCM_STREAM_PLAYBACK) 49*3e086edfSolivier moysan #define STM_SAI_IS_CAPTURE(ip) ((ip)->dir == SNDRV_PCM_STREAM_CAPTURE) 50*3e086edfSolivier moysan 51*3e086edfSolivier moysan #define STM_SAI_A_ID 0x0 52*3e086edfSolivier moysan #define STM_SAI_B_ID 0x1 53*3e086edfSolivier moysan 54*3e086edfSolivier moysan #define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B") 55*3e086edfSolivier moysan 56*3e086edfSolivier moysan /** 57*3e086edfSolivier moysan * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) 58*3e086edfSolivier moysan * @pdev: device data pointer 59*3e086edfSolivier moysan * @regmap: SAI register map pointer 60*3e086edfSolivier moysan * @dma_params: dma configuration data for rx or tx channel 61*3e086edfSolivier moysan * @cpu_dai_drv: DAI driver data pointer 62*3e086edfSolivier moysan * @cpu_dai: DAI runtime data pointer 63*3e086edfSolivier moysan * @substream: PCM substream data pointer 64*3e086edfSolivier moysan * @pdata: SAI block parent data pointer 65*3e086edfSolivier moysan * @sai_ck: kernel clock feeding the SAI clock generator 66*3e086edfSolivier moysan * @phys_addr: SAI registers physical base address 67*3e086edfSolivier moysan * @mclk_rate: SAI block master clock frequency (Hz). set at init 68*3e086edfSolivier moysan * @id: SAI sub block id corresponding to sub-block A or B 69*3e086edfSolivier moysan * @dir: SAI block direction (playback or capture). set at init 70*3e086edfSolivier moysan * @master: SAI block mode flag. (true=master, false=slave) set at init 71*3e086edfSolivier moysan * @fmt: SAI block format. relevant only for custom protocols. set at init 72*3e086edfSolivier moysan * @sync: SAI block synchronization mode. (none, internal or external) 73*3e086edfSolivier moysan * @fs_length: frame synchronization length. depends on protocol settings 74*3e086edfSolivier moysan * @slots: rx or tx slot number 75*3e086edfSolivier moysan * @slot_width: rx or tx slot width in bits 76*3e086edfSolivier moysan * @slot_mask: rx or tx active slots mask. set at init or at runtime 77*3e086edfSolivier moysan * @data_size: PCM data width. corresponds to PCM substream width. 78*3e086edfSolivier moysan */ 79*3e086edfSolivier moysan struct stm32_sai_sub_data { 80*3e086edfSolivier moysan struct platform_device *pdev; 81*3e086edfSolivier moysan struct regmap *regmap; 82*3e086edfSolivier moysan struct snd_dmaengine_dai_dma_data dma_params; 83*3e086edfSolivier moysan struct snd_soc_dai_driver *cpu_dai_drv; 84*3e086edfSolivier moysan struct snd_soc_dai *cpu_dai; 85*3e086edfSolivier moysan struct snd_pcm_substream *substream; 86*3e086edfSolivier moysan struct stm32_sai_data *pdata; 87*3e086edfSolivier moysan struct clk *sai_ck; 88*3e086edfSolivier moysan dma_addr_t phys_addr; 89*3e086edfSolivier moysan unsigned int mclk_rate; 90*3e086edfSolivier moysan unsigned int id; 91*3e086edfSolivier moysan int dir; 92*3e086edfSolivier moysan bool master; 93*3e086edfSolivier moysan int fmt; 94*3e086edfSolivier moysan int sync; 95*3e086edfSolivier moysan int fs_length; 96*3e086edfSolivier moysan int slots; 97*3e086edfSolivier moysan int slot_width; 98*3e086edfSolivier moysan int slot_mask; 99*3e086edfSolivier moysan int data_size; 100*3e086edfSolivier moysan }; 101*3e086edfSolivier moysan 102*3e086edfSolivier moysan enum stm32_sai_fifo_th { 103*3e086edfSolivier moysan STM_SAI_FIFO_TH_EMPTY, 104*3e086edfSolivier moysan STM_SAI_FIFO_TH_QUARTER, 105*3e086edfSolivier moysan STM_SAI_FIFO_TH_HALF, 106*3e086edfSolivier moysan STM_SAI_FIFO_TH_3_QUARTER, 107*3e086edfSolivier moysan STM_SAI_FIFO_TH_FULL, 108*3e086edfSolivier moysan }; 109*3e086edfSolivier moysan 110*3e086edfSolivier moysan static bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg) 111*3e086edfSolivier moysan { 112*3e086edfSolivier moysan switch (reg) { 113*3e086edfSolivier moysan case STM_SAI_CR1_REGX: 114*3e086edfSolivier moysan case STM_SAI_CR2_REGX: 115*3e086edfSolivier moysan case STM_SAI_FRCR_REGX: 116*3e086edfSolivier moysan case STM_SAI_SLOTR_REGX: 117*3e086edfSolivier moysan case STM_SAI_IMR_REGX: 118*3e086edfSolivier moysan case STM_SAI_SR_REGX: 119*3e086edfSolivier moysan case STM_SAI_CLRFR_REGX: 120*3e086edfSolivier moysan case STM_SAI_DR_REGX: 121*3e086edfSolivier moysan return true; 122*3e086edfSolivier moysan default: 123*3e086edfSolivier moysan return false; 124*3e086edfSolivier moysan } 125*3e086edfSolivier moysan } 126*3e086edfSolivier moysan 127*3e086edfSolivier moysan static bool stm32_sai_sub_volatile_reg(struct device *dev, unsigned int reg) 128*3e086edfSolivier moysan { 129*3e086edfSolivier moysan switch (reg) { 130*3e086edfSolivier moysan case STM_SAI_DR_REGX: 131*3e086edfSolivier moysan return true; 132*3e086edfSolivier moysan default: 133*3e086edfSolivier moysan return false; 134*3e086edfSolivier moysan } 135*3e086edfSolivier moysan } 136*3e086edfSolivier moysan 137*3e086edfSolivier moysan static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg) 138*3e086edfSolivier moysan { 139*3e086edfSolivier moysan switch (reg) { 140*3e086edfSolivier moysan case STM_SAI_CR1_REGX: 141*3e086edfSolivier moysan case STM_SAI_CR2_REGX: 142*3e086edfSolivier moysan case STM_SAI_FRCR_REGX: 143*3e086edfSolivier moysan case STM_SAI_SLOTR_REGX: 144*3e086edfSolivier moysan case STM_SAI_IMR_REGX: 145*3e086edfSolivier moysan case STM_SAI_SR_REGX: 146*3e086edfSolivier moysan case STM_SAI_CLRFR_REGX: 147*3e086edfSolivier moysan case STM_SAI_DR_REGX: 148*3e086edfSolivier moysan return true; 149*3e086edfSolivier moysan default: 150*3e086edfSolivier moysan return false; 151*3e086edfSolivier moysan } 152*3e086edfSolivier moysan } 153*3e086edfSolivier moysan 154*3e086edfSolivier moysan static const struct regmap_config stm32_sai_sub_regmap_config = { 155*3e086edfSolivier moysan .reg_bits = 32, 156*3e086edfSolivier moysan .reg_stride = 4, 157*3e086edfSolivier moysan .val_bits = 32, 158*3e086edfSolivier moysan .max_register = STM_SAI_DR_REGX, 159*3e086edfSolivier moysan .readable_reg = stm32_sai_sub_readable_reg, 160*3e086edfSolivier moysan .volatile_reg = stm32_sai_sub_volatile_reg, 161*3e086edfSolivier moysan .writeable_reg = stm32_sai_sub_writeable_reg, 162*3e086edfSolivier moysan .fast_io = true, 163*3e086edfSolivier moysan }; 164*3e086edfSolivier moysan 165*3e086edfSolivier moysan static irqreturn_t stm32_sai_isr(int irq, void *devid) 166*3e086edfSolivier moysan { 167*3e086edfSolivier moysan struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; 168*3e086edfSolivier moysan struct snd_pcm_substream *substream = sai->substream; 169*3e086edfSolivier moysan struct platform_device *pdev = sai->pdev; 170*3e086edfSolivier moysan unsigned int sr, imr, flags; 171*3e086edfSolivier moysan snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING; 172*3e086edfSolivier moysan 173*3e086edfSolivier moysan regmap_read(sai->regmap, STM_SAI_IMR_REGX, &imr); 174*3e086edfSolivier moysan regmap_read(sai->regmap, STM_SAI_SR_REGX, &sr); 175*3e086edfSolivier moysan 176*3e086edfSolivier moysan flags = sr & imr; 177*3e086edfSolivier moysan if (!flags) 178*3e086edfSolivier moysan return IRQ_NONE; 179*3e086edfSolivier moysan 180*3e086edfSolivier moysan regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, 181*3e086edfSolivier moysan SAI_XCLRFR_MASK); 182*3e086edfSolivier moysan 183*3e086edfSolivier moysan if (flags & SAI_XIMR_OVRUDRIE) { 184*3e086edfSolivier moysan dev_err(&pdev->dev, "IT %s\n", 185*3e086edfSolivier moysan STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun"); 186*3e086edfSolivier moysan status = SNDRV_PCM_STATE_XRUN; 187*3e086edfSolivier moysan } 188*3e086edfSolivier moysan 189*3e086edfSolivier moysan if (flags & SAI_XIMR_MUTEDETIE) 190*3e086edfSolivier moysan dev_dbg(&pdev->dev, "IT mute detected\n"); 191*3e086edfSolivier moysan 192*3e086edfSolivier moysan if (flags & SAI_XIMR_WCKCFGIE) { 193*3e086edfSolivier moysan dev_err(&pdev->dev, "IT wrong clock configuration\n"); 194*3e086edfSolivier moysan status = SNDRV_PCM_STATE_DISCONNECTED; 195*3e086edfSolivier moysan } 196*3e086edfSolivier moysan 197*3e086edfSolivier moysan if (flags & SAI_XIMR_CNRDYIE) 198*3e086edfSolivier moysan dev_warn(&pdev->dev, "IT Codec not ready\n"); 199*3e086edfSolivier moysan 200*3e086edfSolivier moysan if (flags & SAI_XIMR_AFSDETIE) { 201*3e086edfSolivier moysan dev_warn(&pdev->dev, "IT Anticipated frame synchro\n"); 202*3e086edfSolivier moysan status = SNDRV_PCM_STATE_XRUN; 203*3e086edfSolivier moysan } 204*3e086edfSolivier moysan 205*3e086edfSolivier moysan if (flags & SAI_XIMR_LFSDETIE) { 206*3e086edfSolivier moysan dev_warn(&pdev->dev, "IT Late frame synchro\n"); 207*3e086edfSolivier moysan status = SNDRV_PCM_STATE_XRUN; 208*3e086edfSolivier moysan } 209*3e086edfSolivier moysan 210*3e086edfSolivier moysan if (status != SNDRV_PCM_STATE_RUNNING) { 211*3e086edfSolivier moysan snd_pcm_stream_lock(substream); 212*3e086edfSolivier moysan snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); 213*3e086edfSolivier moysan snd_pcm_stream_unlock(substream); 214*3e086edfSolivier moysan } 215*3e086edfSolivier moysan 216*3e086edfSolivier moysan return IRQ_HANDLED; 217*3e086edfSolivier moysan } 218*3e086edfSolivier moysan 219*3e086edfSolivier moysan static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai, 220*3e086edfSolivier moysan int clk_id, unsigned int freq, int dir) 221*3e086edfSolivier moysan { 222*3e086edfSolivier moysan struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 223*3e086edfSolivier moysan 224*3e086edfSolivier moysan if ((dir == SND_SOC_CLOCK_OUT) && sai->master) { 225*3e086edfSolivier moysan sai->mclk_rate = freq; 226*3e086edfSolivier moysan dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq); 227*3e086edfSolivier moysan } 228*3e086edfSolivier moysan 229*3e086edfSolivier moysan return 0; 230*3e086edfSolivier moysan } 231*3e086edfSolivier moysan 232*3e086edfSolivier moysan static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, 233*3e086edfSolivier moysan u32 rx_mask, int slots, int slot_width) 234*3e086edfSolivier moysan { 235*3e086edfSolivier moysan struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 236*3e086edfSolivier moysan int slotr, slotr_mask, slot_size; 237*3e086edfSolivier moysan 238*3e086edfSolivier moysan dev_dbg(cpu_dai->dev, "masks tx/rx:%#x/%#x, slots:%d, width:%d\n", 239*3e086edfSolivier moysan tx_mask, rx_mask, slots, slot_width); 240*3e086edfSolivier moysan 241*3e086edfSolivier moysan switch (slot_width) { 242*3e086edfSolivier moysan case 16: 243*3e086edfSolivier moysan slot_size = SAI_SLOT_SIZE_16; 244*3e086edfSolivier moysan break; 245*3e086edfSolivier moysan case 32: 246*3e086edfSolivier moysan slot_size = SAI_SLOT_SIZE_32; 247*3e086edfSolivier moysan break; 248*3e086edfSolivier moysan default: 249*3e086edfSolivier moysan slot_size = SAI_SLOT_SIZE_AUTO; 250*3e086edfSolivier moysan break; 251*3e086edfSolivier moysan } 252*3e086edfSolivier moysan 253*3e086edfSolivier moysan slotr = SAI_XSLOTR_SLOTSZ_SET(slot_size) | 254*3e086edfSolivier moysan SAI_XSLOTR_NBSLOT_SET(slots - 1); 255*3e086edfSolivier moysan slotr_mask = SAI_XSLOTR_SLOTSZ_MASK | SAI_XSLOTR_NBSLOT_MASK; 256*3e086edfSolivier moysan 257*3e086edfSolivier moysan /* tx/rx mask set in machine init, if slot number defined in DT */ 258*3e086edfSolivier moysan if (STM_SAI_IS_PLAYBACK(sai)) { 259*3e086edfSolivier moysan sai->slot_mask = tx_mask; 260*3e086edfSolivier moysan slotr |= SAI_XSLOTR_SLOTEN_SET(tx_mask); 261*3e086edfSolivier moysan } 262*3e086edfSolivier moysan 263*3e086edfSolivier moysan if (STM_SAI_IS_CAPTURE(sai)) { 264*3e086edfSolivier moysan sai->slot_mask = rx_mask; 265*3e086edfSolivier moysan slotr |= SAI_XSLOTR_SLOTEN_SET(rx_mask); 266*3e086edfSolivier moysan } 267*3e086edfSolivier moysan 268*3e086edfSolivier moysan slotr_mask |= SAI_XSLOTR_SLOTEN_MASK; 269*3e086edfSolivier moysan 270*3e086edfSolivier moysan regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, slotr_mask, slotr); 271*3e086edfSolivier moysan 272*3e086edfSolivier moysan sai->slot_width = slot_width; 273*3e086edfSolivier moysan sai->slots = slots; 274*3e086edfSolivier moysan 275*3e086edfSolivier moysan return 0; 276*3e086edfSolivier moysan } 277*3e086edfSolivier moysan 278*3e086edfSolivier moysan static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) 279*3e086edfSolivier moysan { 280*3e086edfSolivier moysan struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 281*3e086edfSolivier moysan int cr1 = 0, frcr = 0; 282*3e086edfSolivier moysan int cr1_mask = 0, frcr_mask = 0; 283*3e086edfSolivier moysan int ret; 284*3e086edfSolivier moysan 285*3e086edfSolivier moysan dev_dbg(cpu_dai->dev, "fmt %x\n", fmt); 286*3e086edfSolivier moysan 287*3e086edfSolivier moysan switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 288*3e086edfSolivier moysan /* SCK active high for all protocols */ 289*3e086edfSolivier moysan case SND_SOC_DAIFMT_I2S: 290*3e086edfSolivier moysan cr1 |= SAI_XCR1_CKSTR; 291*3e086edfSolivier moysan frcr |= SAI_XFRCR_FSOFF | SAI_XFRCR_FSDEF; 292*3e086edfSolivier moysan break; 293*3e086edfSolivier moysan /* Left justified */ 294*3e086edfSolivier moysan case SND_SOC_DAIFMT_MSB: 295*3e086edfSolivier moysan frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF; 296*3e086edfSolivier moysan break; 297*3e086edfSolivier moysan /* Right justified */ 298*3e086edfSolivier moysan case SND_SOC_DAIFMT_LSB: 299*3e086edfSolivier moysan frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF; 300*3e086edfSolivier moysan break; 301*3e086edfSolivier moysan case SND_SOC_DAIFMT_DSP_A: 302*3e086edfSolivier moysan frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF; 303*3e086edfSolivier moysan break; 304*3e086edfSolivier moysan case SND_SOC_DAIFMT_DSP_B: 305*3e086edfSolivier moysan frcr |= SAI_XFRCR_FSPOL; 306*3e086edfSolivier moysan break; 307*3e086edfSolivier moysan default: 308*3e086edfSolivier moysan dev_err(cpu_dai->dev, "Unsupported protocol %#x\n", 309*3e086edfSolivier moysan fmt & SND_SOC_DAIFMT_FORMAT_MASK); 310*3e086edfSolivier moysan return -EINVAL; 311*3e086edfSolivier moysan } 312*3e086edfSolivier moysan 313*3e086edfSolivier moysan cr1_mask |= SAI_XCR1_PRTCFG_MASK | SAI_XCR1_CKSTR; 314*3e086edfSolivier moysan frcr_mask |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF | 315*3e086edfSolivier moysan SAI_XFRCR_FSDEF; 316*3e086edfSolivier moysan 317*3e086edfSolivier moysan /* DAI clock strobing. Invert setting previously set */ 318*3e086edfSolivier moysan switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 319*3e086edfSolivier moysan case SND_SOC_DAIFMT_NB_NF: 320*3e086edfSolivier moysan break; 321*3e086edfSolivier moysan case SND_SOC_DAIFMT_IB_NF: 322*3e086edfSolivier moysan cr1 ^= SAI_XCR1_CKSTR; 323*3e086edfSolivier moysan break; 324*3e086edfSolivier moysan case SND_SOC_DAIFMT_NB_IF: 325*3e086edfSolivier moysan frcr ^= SAI_XFRCR_FSPOL; 326*3e086edfSolivier moysan break; 327*3e086edfSolivier moysan case SND_SOC_DAIFMT_IB_IF: 328*3e086edfSolivier moysan /* Invert fs & sck */ 329*3e086edfSolivier moysan cr1 ^= SAI_XCR1_CKSTR; 330*3e086edfSolivier moysan frcr ^= SAI_XFRCR_FSPOL; 331*3e086edfSolivier moysan break; 332*3e086edfSolivier moysan default: 333*3e086edfSolivier moysan dev_err(cpu_dai->dev, "Unsupported strobing %#x\n", 334*3e086edfSolivier moysan fmt & SND_SOC_DAIFMT_INV_MASK); 335*3e086edfSolivier moysan return -EINVAL; 336*3e086edfSolivier moysan } 337*3e086edfSolivier moysan cr1_mask |= SAI_XCR1_CKSTR; 338*3e086edfSolivier moysan frcr_mask |= SAI_XFRCR_FSPOL; 339*3e086edfSolivier moysan 340*3e086edfSolivier moysan regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr); 341*3e086edfSolivier moysan 342*3e086edfSolivier moysan /* DAI clock master masks */ 343*3e086edfSolivier moysan switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 344*3e086edfSolivier moysan case SND_SOC_DAIFMT_CBM_CFM: 345*3e086edfSolivier moysan /* codec is master */ 346*3e086edfSolivier moysan cr1 |= SAI_XCR1_SLAVE; 347*3e086edfSolivier moysan sai->master = false; 348*3e086edfSolivier moysan break; 349*3e086edfSolivier moysan case SND_SOC_DAIFMT_CBS_CFS: 350*3e086edfSolivier moysan sai->master = true; 351*3e086edfSolivier moysan break; 352*3e086edfSolivier moysan default: 353*3e086edfSolivier moysan dev_err(cpu_dai->dev, "Unsupported mode %#x\n", 354*3e086edfSolivier moysan fmt & SND_SOC_DAIFMT_MASTER_MASK); 355*3e086edfSolivier moysan return -EINVAL; 356*3e086edfSolivier moysan } 357*3e086edfSolivier moysan cr1_mask |= SAI_XCR1_SLAVE; 358*3e086edfSolivier moysan 359*3e086edfSolivier moysan ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); 360*3e086edfSolivier moysan if (ret < 0) { 361*3e086edfSolivier moysan dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); 362*3e086edfSolivier moysan return ret; 363*3e086edfSolivier moysan } 364*3e086edfSolivier moysan 365*3e086edfSolivier moysan sai->fmt = fmt; 366*3e086edfSolivier moysan 367*3e086edfSolivier moysan return 0; 368*3e086edfSolivier moysan } 369*3e086edfSolivier moysan 370*3e086edfSolivier moysan static int stm32_sai_startup(struct snd_pcm_substream *substream, 371*3e086edfSolivier moysan struct snd_soc_dai *cpu_dai) 372*3e086edfSolivier moysan { 373*3e086edfSolivier moysan struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 374*3e086edfSolivier moysan int imr, cr2, ret; 375*3e086edfSolivier moysan 376*3e086edfSolivier moysan sai->substream = substream; 377*3e086edfSolivier moysan 378*3e086edfSolivier moysan ret = clk_prepare_enable(sai->sai_ck); 379*3e086edfSolivier moysan if (ret < 0) { 380*3e086edfSolivier moysan dev_err(cpu_dai->dev, "failed to enable clock: %d\n", ret); 381*3e086edfSolivier moysan return ret; 382*3e086edfSolivier moysan } 383*3e086edfSolivier moysan 384*3e086edfSolivier moysan /* Enable ITs */ 385*3e086edfSolivier moysan regmap_update_bits(sai->regmap, STM_SAI_SR_REGX, 386*3e086edfSolivier moysan SAI_XSR_MASK, (unsigned int)~SAI_XSR_MASK); 387*3e086edfSolivier moysan 388*3e086edfSolivier moysan regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, 389*3e086edfSolivier moysan SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); 390*3e086edfSolivier moysan 391*3e086edfSolivier moysan imr = SAI_XIMR_OVRUDRIE; 392*3e086edfSolivier moysan if (STM_SAI_IS_CAPTURE(sai)) { 393*3e086edfSolivier moysan regmap_read(sai->regmap, STM_SAI_CR2_REGX, &cr2); 394*3e086edfSolivier moysan if (cr2 & SAI_XCR2_MUTECNT_MASK) 395*3e086edfSolivier moysan imr |= SAI_XIMR_MUTEDETIE; 396*3e086edfSolivier moysan } 397*3e086edfSolivier moysan 398*3e086edfSolivier moysan if (sai->master) 399*3e086edfSolivier moysan imr |= SAI_XIMR_WCKCFGIE; 400*3e086edfSolivier moysan else 401*3e086edfSolivier moysan imr |= SAI_XIMR_AFSDETIE | SAI_XIMR_LFSDETIE; 402*3e086edfSolivier moysan 403*3e086edfSolivier moysan regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, 404*3e086edfSolivier moysan SAI_XIMR_MASK, imr); 405*3e086edfSolivier moysan 406*3e086edfSolivier moysan return 0; 407*3e086edfSolivier moysan } 408*3e086edfSolivier moysan 409*3e086edfSolivier moysan static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, 410*3e086edfSolivier moysan struct snd_pcm_substream *substream, 411*3e086edfSolivier moysan struct snd_pcm_hw_params *params) 412*3e086edfSolivier moysan { 413*3e086edfSolivier moysan struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 414*3e086edfSolivier moysan int cr1, cr1_mask, ret; 415*3e086edfSolivier moysan int fth = STM_SAI_FIFO_TH_HALF; 416*3e086edfSolivier moysan 417*3e086edfSolivier moysan /* FIFO config */ 418*3e086edfSolivier moysan regmap_update_bits(sai->regmap, STM_SAI_CR2_REGX, 419*3e086edfSolivier moysan SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK, 420*3e086edfSolivier moysan SAI_XCR2_FFLUSH | SAI_XCR2_FTH_SET(fth)); 421*3e086edfSolivier moysan 422*3e086edfSolivier moysan /* Mode, data format and channel config */ 423*3e086edfSolivier moysan cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL); 424*3e086edfSolivier moysan switch (params_format(params)) { 425*3e086edfSolivier moysan case SNDRV_PCM_FORMAT_S8: 426*3e086edfSolivier moysan cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_8); 427*3e086edfSolivier moysan break; 428*3e086edfSolivier moysan case SNDRV_PCM_FORMAT_S16_LE: 429*3e086edfSolivier moysan cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_16); 430*3e086edfSolivier moysan break; 431*3e086edfSolivier moysan case SNDRV_PCM_FORMAT_S32_LE: 432*3e086edfSolivier moysan cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_32); 433*3e086edfSolivier moysan break; 434*3e086edfSolivier moysan default: 435*3e086edfSolivier moysan dev_err(cpu_dai->dev, "Data format not supported"); 436*3e086edfSolivier moysan return -EINVAL; 437*3e086edfSolivier moysan } 438*3e086edfSolivier moysan cr1_mask = SAI_XCR1_DS_MASK | SAI_XCR1_PRTCFG_MASK; 439*3e086edfSolivier moysan 440*3e086edfSolivier moysan cr1_mask |= SAI_XCR1_RX_TX; 441*3e086edfSolivier moysan if (STM_SAI_IS_CAPTURE(sai)) 442*3e086edfSolivier moysan cr1 |= SAI_XCR1_RX_TX; 443*3e086edfSolivier moysan 444*3e086edfSolivier moysan cr1_mask |= SAI_XCR1_MONO; 445*3e086edfSolivier moysan if ((sai->slots == 2) && (params_channels(params) == 1)) 446*3e086edfSolivier moysan cr1 |= SAI_XCR1_MONO; 447*3e086edfSolivier moysan 448*3e086edfSolivier moysan ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); 449*3e086edfSolivier moysan if (ret < 0) { 450*3e086edfSolivier moysan dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); 451*3e086edfSolivier moysan return ret; 452*3e086edfSolivier moysan } 453*3e086edfSolivier moysan 454*3e086edfSolivier moysan /* DMA config */ 455*3e086edfSolivier moysan sai->dma_params.maxburst = STM_SAI_FIFO_SIZE * fth / sizeof(u32); 456*3e086edfSolivier moysan snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&sai->dma_params); 457*3e086edfSolivier moysan 458*3e086edfSolivier moysan return 0; 459*3e086edfSolivier moysan } 460*3e086edfSolivier moysan 461*3e086edfSolivier moysan static int stm32_sai_set_slots(struct snd_soc_dai *cpu_dai) 462*3e086edfSolivier moysan { 463*3e086edfSolivier moysan struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 464*3e086edfSolivier moysan int slotr, slot_sz; 465*3e086edfSolivier moysan 466*3e086edfSolivier moysan regmap_read(sai->regmap, STM_SAI_SLOTR_REGX, &slotr); 467*3e086edfSolivier moysan 468*3e086edfSolivier moysan /* 469*3e086edfSolivier moysan * If SLOTSZ is set to auto in SLOTR, align slot width on data size 470*3e086edfSolivier moysan * By default slot width = data size, if not forced from DT 471*3e086edfSolivier moysan */ 472*3e086edfSolivier moysan slot_sz = slotr & SAI_XSLOTR_SLOTSZ_MASK; 473*3e086edfSolivier moysan if (slot_sz == SAI_XSLOTR_SLOTSZ_SET(SAI_SLOT_SIZE_AUTO)) 474*3e086edfSolivier moysan sai->slot_width = sai->data_size; 475*3e086edfSolivier moysan 476*3e086edfSolivier moysan if (sai->slot_width < sai->data_size) { 477*3e086edfSolivier moysan dev_err(cpu_dai->dev, 478*3e086edfSolivier moysan "Data size %d larger than slot width\n", 479*3e086edfSolivier moysan sai->data_size); 480*3e086edfSolivier moysan return -EINVAL; 481*3e086edfSolivier moysan } 482*3e086edfSolivier moysan 483*3e086edfSolivier moysan /* Slot number is set to 2, if not specified in DT */ 484*3e086edfSolivier moysan if (!sai->slots) 485*3e086edfSolivier moysan sai->slots = 2; 486*3e086edfSolivier moysan 487*3e086edfSolivier moysan /* The number of slots in the audio frame is equal to NBSLOT[3:0] + 1*/ 488*3e086edfSolivier moysan regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, 489*3e086edfSolivier moysan SAI_XSLOTR_NBSLOT_MASK, 490*3e086edfSolivier moysan SAI_XSLOTR_NBSLOT_SET((sai->slots - 1))); 491*3e086edfSolivier moysan 492*3e086edfSolivier moysan /* Set default slots mask if not already set from DT */ 493*3e086edfSolivier moysan if (!(slotr & SAI_XSLOTR_SLOTEN_MASK)) { 494*3e086edfSolivier moysan sai->slot_mask = (1 << sai->slots) - 1; 495*3e086edfSolivier moysan regmap_update_bits(sai->regmap, 496*3e086edfSolivier moysan STM_SAI_SLOTR_REGX, SAI_XSLOTR_SLOTEN_MASK, 497*3e086edfSolivier moysan SAI_XSLOTR_SLOTEN_SET(sai->slot_mask)); 498*3e086edfSolivier moysan } 499*3e086edfSolivier moysan 500*3e086edfSolivier moysan dev_dbg(cpu_dai->dev, "slots %d, slot width %d\n", 501*3e086edfSolivier moysan sai->slots, sai->slot_width); 502*3e086edfSolivier moysan 503*3e086edfSolivier moysan return 0; 504*3e086edfSolivier moysan } 505*3e086edfSolivier moysan 506*3e086edfSolivier moysan static void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai) 507*3e086edfSolivier moysan { 508*3e086edfSolivier moysan struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 509*3e086edfSolivier moysan int fs_active, offset, format; 510*3e086edfSolivier moysan int frcr, frcr_mask; 511*3e086edfSolivier moysan 512*3e086edfSolivier moysan format = sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK; 513*3e086edfSolivier moysan sai->fs_length = sai->slot_width * sai->slots; 514*3e086edfSolivier moysan 515*3e086edfSolivier moysan fs_active = sai->fs_length / 2; 516*3e086edfSolivier moysan if ((format == SND_SOC_DAIFMT_DSP_A) || 517*3e086edfSolivier moysan (format == SND_SOC_DAIFMT_DSP_B)) 518*3e086edfSolivier moysan fs_active = 1; 519*3e086edfSolivier moysan 520*3e086edfSolivier moysan frcr = SAI_XFRCR_FRL_SET((sai->fs_length - 1)); 521*3e086edfSolivier moysan frcr |= SAI_XFRCR_FSALL_SET((fs_active - 1)); 522*3e086edfSolivier moysan frcr_mask = SAI_XFRCR_FRL_MASK | SAI_XFRCR_FSALL_MASK; 523*3e086edfSolivier moysan 524*3e086edfSolivier moysan dev_dbg(cpu_dai->dev, "frame length %d, frame active %d\n", 525*3e086edfSolivier moysan sai->fs_length, fs_active); 526*3e086edfSolivier moysan 527*3e086edfSolivier moysan regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr); 528*3e086edfSolivier moysan 529*3e086edfSolivier moysan if ((sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_LSB) { 530*3e086edfSolivier moysan offset = sai->slot_width - sai->data_size; 531*3e086edfSolivier moysan 532*3e086edfSolivier moysan regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, 533*3e086edfSolivier moysan SAI_XSLOTR_FBOFF_MASK, 534*3e086edfSolivier moysan SAI_XSLOTR_FBOFF_SET(offset)); 535*3e086edfSolivier moysan } 536*3e086edfSolivier moysan } 537*3e086edfSolivier moysan 538*3e086edfSolivier moysan static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, 539*3e086edfSolivier moysan struct snd_pcm_hw_params *params) 540*3e086edfSolivier moysan { 541*3e086edfSolivier moysan struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 542*3e086edfSolivier moysan int cr1, mask, div = 0; 543*3e086edfSolivier moysan int sai_clk_rate, ret; 544*3e086edfSolivier moysan 545*3e086edfSolivier moysan if (!sai->mclk_rate) { 546*3e086edfSolivier moysan dev_err(cpu_dai->dev, "Mclk rate is null\n"); 547*3e086edfSolivier moysan return -EINVAL; 548*3e086edfSolivier moysan } 549*3e086edfSolivier moysan 550*3e086edfSolivier moysan if (!(params_rate(params) % 11025)) 551*3e086edfSolivier moysan clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k); 552*3e086edfSolivier moysan else 553*3e086edfSolivier moysan clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k); 554*3e086edfSolivier moysan sai_clk_rate = clk_get_rate(sai->sai_ck); 555*3e086edfSolivier moysan 556*3e086edfSolivier moysan /* 557*3e086edfSolivier moysan * mclk_rate = 256 * fs 558*3e086edfSolivier moysan * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate 559*3e086edfSolivier moysan * MCKDIV = sai_ck / (2 * mclk_rate) otherwise 560*3e086edfSolivier moysan */ 561*3e086edfSolivier moysan if (2 * sai_clk_rate >= 3 * sai->mclk_rate) 562*3e086edfSolivier moysan div = DIV_ROUND_CLOSEST(sai_clk_rate, 2 * sai->mclk_rate); 563*3e086edfSolivier moysan 564*3e086edfSolivier moysan if (div > SAI_XCR1_MCKDIV_MAX) { 565*3e086edfSolivier moysan dev_err(cpu_dai->dev, "Divider %d out of range\n", div); 566*3e086edfSolivier moysan return -EINVAL; 567*3e086edfSolivier moysan } 568*3e086edfSolivier moysan dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div); 569*3e086edfSolivier moysan 570*3e086edfSolivier moysan mask = SAI_XCR1_MCKDIV_MASK; 571*3e086edfSolivier moysan cr1 = SAI_XCR1_MCKDIV_SET(div); 572*3e086edfSolivier moysan ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1); 573*3e086edfSolivier moysan if (ret < 0) { 574*3e086edfSolivier moysan dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); 575*3e086edfSolivier moysan return ret; 576*3e086edfSolivier moysan } 577*3e086edfSolivier moysan 578*3e086edfSolivier moysan return 0; 579*3e086edfSolivier moysan } 580*3e086edfSolivier moysan 581*3e086edfSolivier moysan static int stm32_sai_hw_params(struct snd_pcm_substream *substream, 582*3e086edfSolivier moysan struct snd_pcm_hw_params *params, 583*3e086edfSolivier moysan struct snd_soc_dai *cpu_dai) 584*3e086edfSolivier moysan { 585*3e086edfSolivier moysan struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 586*3e086edfSolivier moysan int ret; 587*3e086edfSolivier moysan 588*3e086edfSolivier moysan sai->data_size = params_width(params); 589*3e086edfSolivier moysan 590*3e086edfSolivier moysan ret = stm32_sai_set_slots(cpu_dai); 591*3e086edfSolivier moysan if (ret < 0) 592*3e086edfSolivier moysan return ret; 593*3e086edfSolivier moysan stm32_sai_set_frame(cpu_dai); 594*3e086edfSolivier moysan 595*3e086edfSolivier moysan ret = stm32_sai_set_config(cpu_dai, substream, params); 596*3e086edfSolivier moysan if (ret) 597*3e086edfSolivier moysan return ret; 598*3e086edfSolivier moysan 599*3e086edfSolivier moysan if (sai->master) 600*3e086edfSolivier moysan ret = stm32_sai_configure_clock(cpu_dai, params); 601*3e086edfSolivier moysan 602*3e086edfSolivier moysan return ret; 603*3e086edfSolivier moysan } 604*3e086edfSolivier moysan 605*3e086edfSolivier moysan static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd, 606*3e086edfSolivier moysan struct snd_soc_dai *cpu_dai) 607*3e086edfSolivier moysan { 608*3e086edfSolivier moysan struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 609*3e086edfSolivier moysan int ret; 610*3e086edfSolivier moysan 611*3e086edfSolivier moysan switch (cmd) { 612*3e086edfSolivier moysan case SNDRV_PCM_TRIGGER_START: 613*3e086edfSolivier moysan case SNDRV_PCM_TRIGGER_RESUME: 614*3e086edfSolivier moysan case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 615*3e086edfSolivier moysan dev_dbg(cpu_dai->dev, "Enable DMA and SAI\n"); 616*3e086edfSolivier moysan 617*3e086edfSolivier moysan regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, 618*3e086edfSolivier moysan SAI_XCR1_DMAEN, SAI_XCR1_DMAEN); 619*3e086edfSolivier moysan 620*3e086edfSolivier moysan /* Enable SAI */ 621*3e086edfSolivier moysan ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, 622*3e086edfSolivier moysan SAI_XCR1_SAIEN, SAI_XCR1_SAIEN); 623*3e086edfSolivier moysan if (ret < 0) 624*3e086edfSolivier moysan dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); 625*3e086edfSolivier moysan break; 626*3e086edfSolivier moysan case SNDRV_PCM_TRIGGER_SUSPEND: 627*3e086edfSolivier moysan case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 628*3e086edfSolivier moysan case SNDRV_PCM_TRIGGER_STOP: 629*3e086edfSolivier moysan dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n"); 630*3e086edfSolivier moysan 631*3e086edfSolivier moysan regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, 632*3e086edfSolivier moysan SAI_XCR1_DMAEN, 633*3e086edfSolivier moysan (unsigned int)~SAI_XCR1_DMAEN); 634*3e086edfSolivier moysan 635*3e086edfSolivier moysan ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, 636*3e086edfSolivier moysan SAI_XCR1_SAIEN, 637*3e086edfSolivier moysan (unsigned int)~SAI_XCR1_SAIEN); 638*3e086edfSolivier moysan if (ret < 0) 639*3e086edfSolivier moysan dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); 640*3e086edfSolivier moysan break; 641*3e086edfSolivier moysan default: 642*3e086edfSolivier moysan return -EINVAL; 643*3e086edfSolivier moysan } 644*3e086edfSolivier moysan 645*3e086edfSolivier moysan return ret; 646*3e086edfSolivier moysan } 647*3e086edfSolivier moysan 648*3e086edfSolivier moysan static void stm32_sai_shutdown(struct snd_pcm_substream *substream, 649*3e086edfSolivier moysan struct snd_soc_dai *cpu_dai) 650*3e086edfSolivier moysan { 651*3e086edfSolivier moysan struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); 652*3e086edfSolivier moysan 653*3e086edfSolivier moysan regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0); 654*3e086edfSolivier moysan 655*3e086edfSolivier moysan clk_disable_unprepare(sai->sai_ck); 656*3e086edfSolivier moysan sai->substream = NULL; 657*3e086edfSolivier moysan } 658*3e086edfSolivier moysan 659*3e086edfSolivier moysan static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) 660*3e086edfSolivier moysan { 661*3e086edfSolivier moysan struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); 662*3e086edfSolivier moysan 663*3e086edfSolivier moysan sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX); 664*3e086edfSolivier moysan sai->dma_params.maxburst = 1; 665*3e086edfSolivier moysan /* Buswidth will be set by framework at runtime */ 666*3e086edfSolivier moysan sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; 667*3e086edfSolivier moysan 668*3e086edfSolivier moysan if (STM_SAI_IS_PLAYBACK(sai)) 669*3e086edfSolivier moysan snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params, NULL); 670*3e086edfSolivier moysan else 671*3e086edfSolivier moysan snd_soc_dai_init_dma_data(cpu_dai, NULL, &sai->dma_params); 672*3e086edfSolivier moysan 673*3e086edfSolivier moysan return 0; 674*3e086edfSolivier moysan } 675*3e086edfSolivier moysan 676*3e086edfSolivier moysan static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = { 677*3e086edfSolivier moysan .set_sysclk = stm32_sai_set_sysclk, 678*3e086edfSolivier moysan .set_fmt = stm32_sai_set_dai_fmt, 679*3e086edfSolivier moysan .set_tdm_slot = stm32_sai_set_dai_tdm_slot, 680*3e086edfSolivier moysan .startup = stm32_sai_startup, 681*3e086edfSolivier moysan .hw_params = stm32_sai_hw_params, 682*3e086edfSolivier moysan .trigger = stm32_sai_trigger, 683*3e086edfSolivier moysan .shutdown = stm32_sai_shutdown, 684*3e086edfSolivier moysan }; 685*3e086edfSolivier moysan 686*3e086edfSolivier moysan static const struct snd_pcm_hardware stm32_sai_pcm_hw = { 687*3e086edfSolivier moysan .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP, 688*3e086edfSolivier moysan .buffer_bytes_max = 8 * PAGE_SIZE, 689*3e086edfSolivier moysan .period_bytes_min = 1024, /* 5ms at 48kHz */ 690*3e086edfSolivier moysan .period_bytes_max = PAGE_SIZE, 691*3e086edfSolivier moysan .periods_min = 2, 692*3e086edfSolivier moysan .periods_max = 8, 693*3e086edfSolivier moysan }; 694*3e086edfSolivier moysan 695*3e086edfSolivier moysan static struct snd_soc_dai_driver stm32_sai_playback_dai[] = { 696*3e086edfSolivier moysan { 697*3e086edfSolivier moysan .probe = stm32_sai_dai_probe, 698*3e086edfSolivier moysan .id = 1, /* avoid call to fmt_single_name() */ 699*3e086edfSolivier moysan .playback = { 700*3e086edfSolivier moysan .channels_min = 1, 701*3e086edfSolivier moysan .channels_max = 2, 702*3e086edfSolivier moysan .rate_min = 8000, 703*3e086edfSolivier moysan .rate_max = 192000, 704*3e086edfSolivier moysan .rates = SNDRV_PCM_RATE_CONTINUOUS, 705*3e086edfSolivier moysan /* DMA does not support 24 bits transfers */ 706*3e086edfSolivier moysan .formats = 707*3e086edfSolivier moysan SNDRV_PCM_FMTBIT_S8 | 708*3e086edfSolivier moysan SNDRV_PCM_FMTBIT_S16_LE | 709*3e086edfSolivier moysan SNDRV_PCM_FMTBIT_S32_LE, 710*3e086edfSolivier moysan }, 711*3e086edfSolivier moysan .ops = &stm32_sai_pcm_dai_ops, 712*3e086edfSolivier moysan } 713*3e086edfSolivier moysan }; 714*3e086edfSolivier moysan 715*3e086edfSolivier moysan static struct snd_soc_dai_driver stm32_sai_capture_dai[] = { 716*3e086edfSolivier moysan { 717*3e086edfSolivier moysan .probe = stm32_sai_dai_probe, 718*3e086edfSolivier moysan .id = 1, /* avoid call to fmt_single_name() */ 719*3e086edfSolivier moysan .capture = { 720*3e086edfSolivier moysan .channels_min = 1, 721*3e086edfSolivier moysan .channels_max = 2, 722*3e086edfSolivier moysan .rate_min = 8000, 723*3e086edfSolivier moysan .rate_max = 192000, 724*3e086edfSolivier moysan .rates = SNDRV_PCM_RATE_CONTINUOUS, 725*3e086edfSolivier moysan /* DMA does not support 24 bits transfers */ 726*3e086edfSolivier moysan .formats = 727*3e086edfSolivier moysan SNDRV_PCM_FMTBIT_S8 | 728*3e086edfSolivier moysan SNDRV_PCM_FMTBIT_S16_LE | 729*3e086edfSolivier moysan SNDRV_PCM_FMTBIT_S32_LE, 730*3e086edfSolivier moysan }, 731*3e086edfSolivier moysan .ops = &stm32_sai_pcm_dai_ops, 732*3e086edfSolivier moysan } 733*3e086edfSolivier moysan }; 734*3e086edfSolivier moysan 735*3e086edfSolivier moysan static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = { 736*3e086edfSolivier moysan .pcm_hardware = &stm32_sai_pcm_hw, 737*3e086edfSolivier moysan .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, 738*3e086edfSolivier moysan }; 739*3e086edfSolivier moysan 740*3e086edfSolivier moysan static const struct snd_soc_component_driver stm32_component = { 741*3e086edfSolivier moysan .name = "stm32-sai", 742*3e086edfSolivier moysan }; 743*3e086edfSolivier moysan 744*3e086edfSolivier moysan static const struct of_device_id stm32_sai_sub_ids[] = { 745*3e086edfSolivier moysan { .compatible = "st,stm32-sai-sub-a", 746*3e086edfSolivier moysan .data = (void *)STM_SAI_A_ID}, 747*3e086edfSolivier moysan { .compatible = "st,stm32-sai-sub-b", 748*3e086edfSolivier moysan .data = (void *)STM_SAI_B_ID}, 749*3e086edfSolivier moysan {} 750*3e086edfSolivier moysan }; 751*3e086edfSolivier moysan MODULE_DEVICE_TABLE(of, stm32_sai_sub_ids); 752*3e086edfSolivier moysan 753*3e086edfSolivier moysan static int stm32_sai_sub_parse_of(struct platform_device *pdev, 754*3e086edfSolivier moysan struct stm32_sai_sub_data *sai) 755*3e086edfSolivier moysan { 756*3e086edfSolivier moysan struct device_node *np = pdev->dev.of_node; 757*3e086edfSolivier moysan struct resource *res; 758*3e086edfSolivier moysan void __iomem *base; 759*3e086edfSolivier moysan 760*3e086edfSolivier moysan if (!np) 761*3e086edfSolivier moysan return -ENODEV; 762*3e086edfSolivier moysan 763*3e086edfSolivier moysan res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 764*3e086edfSolivier moysan 765*3e086edfSolivier moysan dev_err(&pdev->dev, "res %pr\n", res); 766*3e086edfSolivier moysan 767*3e086edfSolivier moysan base = devm_ioremap_resource(&pdev->dev, res); 768*3e086edfSolivier moysan if (IS_ERR(base)) 769*3e086edfSolivier moysan return PTR_ERR(base); 770*3e086edfSolivier moysan 771*3e086edfSolivier moysan sai->phys_addr = res->start; 772*3e086edfSolivier moysan sai->regmap = devm_regmap_init_mmio(&pdev->dev, base, 773*3e086edfSolivier moysan &stm32_sai_sub_regmap_config); 774*3e086edfSolivier moysan 775*3e086edfSolivier moysan /* Get direction property */ 776*3e086edfSolivier moysan if (of_property_match_string(np, "dma-names", "tx") >= 0) { 777*3e086edfSolivier moysan sai->dir = SNDRV_PCM_STREAM_PLAYBACK; 778*3e086edfSolivier moysan } else if (of_property_match_string(np, "dma-names", "rx") >= 0) { 779*3e086edfSolivier moysan sai->dir = SNDRV_PCM_STREAM_CAPTURE; 780*3e086edfSolivier moysan } else { 781*3e086edfSolivier moysan dev_err(&pdev->dev, "Unsupported direction\n"); 782*3e086edfSolivier moysan return -EINVAL; 783*3e086edfSolivier moysan } 784*3e086edfSolivier moysan 785*3e086edfSolivier moysan sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); 786*3e086edfSolivier moysan if (IS_ERR(sai->sai_ck)) { 787*3e086edfSolivier moysan dev_err(&pdev->dev, "missing kernel clock sai_ck\n"); 788*3e086edfSolivier moysan return PTR_ERR(sai->sai_ck); 789*3e086edfSolivier moysan } 790*3e086edfSolivier moysan 791*3e086edfSolivier moysan return 0; 792*3e086edfSolivier moysan } 793*3e086edfSolivier moysan 794*3e086edfSolivier moysan static int stm32_sai_sub_dais_init(struct platform_device *pdev, 795*3e086edfSolivier moysan struct stm32_sai_sub_data *sai) 796*3e086edfSolivier moysan { 797*3e086edfSolivier moysan sai->cpu_dai_drv = devm_kzalloc(&pdev->dev, 798*3e086edfSolivier moysan sizeof(struct snd_soc_dai_driver), 799*3e086edfSolivier moysan GFP_KERNEL); 800*3e086edfSolivier moysan if (!sai->cpu_dai_drv) 801*3e086edfSolivier moysan return -ENOMEM; 802*3e086edfSolivier moysan 803*3e086edfSolivier moysan sai->cpu_dai_drv->name = dev_name(&pdev->dev); 804*3e086edfSolivier moysan if (STM_SAI_IS_PLAYBACK(sai)) { 805*3e086edfSolivier moysan memcpy(sai->cpu_dai_drv, &stm32_sai_playback_dai, 806*3e086edfSolivier moysan sizeof(stm32_sai_playback_dai)); 807*3e086edfSolivier moysan sai->cpu_dai_drv->playback.stream_name = sai->cpu_dai_drv->name; 808*3e086edfSolivier moysan } else { 809*3e086edfSolivier moysan memcpy(sai->cpu_dai_drv, &stm32_sai_capture_dai, 810*3e086edfSolivier moysan sizeof(stm32_sai_capture_dai)); 811*3e086edfSolivier moysan sai->cpu_dai_drv->capture.stream_name = sai->cpu_dai_drv->name; 812*3e086edfSolivier moysan } 813*3e086edfSolivier moysan 814*3e086edfSolivier moysan return 0; 815*3e086edfSolivier moysan } 816*3e086edfSolivier moysan 817*3e086edfSolivier moysan static int stm32_sai_sub_probe(struct platform_device *pdev) 818*3e086edfSolivier moysan { 819*3e086edfSolivier moysan struct stm32_sai_sub_data *sai; 820*3e086edfSolivier moysan const struct of_device_id *of_id; 821*3e086edfSolivier moysan int ret; 822*3e086edfSolivier moysan 823*3e086edfSolivier moysan sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); 824*3e086edfSolivier moysan if (!sai) 825*3e086edfSolivier moysan return -ENOMEM; 826*3e086edfSolivier moysan 827*3e086edfSolivier moysan of_id = of_match_device(stm32_sai_sub_ids, &pdev->dev); 828*3e086edfSolivier moysan if (!of_id) 829*3e086edfSolivier moysan return -EINVAL; 830*3e086edfSolivier moysan sai->id = (uintptr_t)of_id->data; 831*3e086edfSolivier moysan 832*3e086edfSolivier moysan sai->pdev = pdev; 833*3e086edfSolivier moysan platform_set_drvdata(pdev, sai); 834*3e086edfSolivier moysan 835*3e086edfSolivier moysan sai->pdata = dev_get_drvdata(pdev->dev.parent); 836*3e086edfSolivier moysan if (!sai->pdata) { 837*3e086edfSolivier moysan dev_err(&pdev->dev, "Parent device data not available\n"); 838*3e086edfSolivier moysan return -EINVAL; 839*3e086edfSolivier moysan } 840*3e086edfSolivier moysan 841*3e086edfSolivier moysan ret = stm32_sai_sub_parse_of(pdev, sai); 842*3e086edfSolivier moysan if (ret) 843*3e086edfSolivier moysan return ret; 844*3e086edfSolivier moysan 845*3e086edfSolivier moysan ret = stm32_sai_sub_dais_init(pdev, sai); 846*3e086edfSolivier moysan if (ret) 847*3e086edfSolivier moysan return ret; 848*3e086edfSolivier moysan 849*3e086edfSolivier moysan ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr, 850*3e086edfSolivier moysan IRQF_SHARED, dev_name(&pdev->dev), sai); 851*3e086edfSolivier moysan if (ret) { 852*3e086edfSolivier moysan dev_err(&pdev->dev, "irq request returned %d\n", ret); 853*3e086edfSolivier moysan return ret; 854*3e086edfSolivier moysan } 855*3e086edfSolivier moysan 856*3e086edfSolivier moysan ret = devm_snd_soc_register_component(&pdev->dev, &stm32_component, 857*3e086edfSolivier moysan sai->cpu_dai_drv, 1); 858*3e086edfSolivier moysan if (ret) 859*3e086edfSolivier moysan return ret; 860*3e086edfSolivier moysan 861*3e086edfSolivier moysan ret = devm_snd_dmaengine_pcm_register(&pdev->dev, 862*3e086edfSolivier moysan &stm32_sai_pcm_config, 0); 863*3e086edfSolivier moysan if (ret) { 864*3e086edfSolivier moysan dev_err(&pdev->dev, "could not register pcm dma\n"); 865*3e086edfSolivier moysan return ret; 866*3e086edfSolivier moysan } 867*3e086edfSolivier moysan 868*3e086edfSolivier moysan return 0; 869*3e086edfSolivier moysan } 870*3e086edfSolivier moysan 871*3e086edfSolivier moysan static struct platform_driver stm32_sai_sub_driver = { 872*3e086edfSolivier moysan .driver = { 873*3e086edfSolivier moysan .name = "st,stm32-sai-sub", 874*3e086edfSolivier moysan .of_match_table = stm32_sai_sub_ids, 875*3e086edfSolivier moysan }, 876*3e086edfSolivier moysan .probe = stm32_sai_sub_probe, 877*3e086edfSolivier moysan }; 878*3e086edfSolivier moysan 879*3e086edfSolivier moysan module_platform_driver(stm32_sai_sub_driver); 880*3e086edfSolivier moysan 881*3e086edfSolivier moysan MODULE_DESCRIPTION("STM32 Soc SAI sub-block Interface"); 882*3e086edfSolivier moysan MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>"); 883*3e086edfSolivier moysan MODULE_ALIAS("platform:st,stm32-sai-sub"); 884*3e086edfSolivier moysan MODULE_LICENSE("GPL v2"); 885