18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Freescale ALSA SoC Digital Audio Interface (SAI) driver. 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright 2012-2015 Freescale Semiconductor, Inc. 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/of_address.h> 128c2ecf20Sopenharmony_ci#include <linux/of_device.h> 138c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 148c2ecf20Sopenharmony_ci#include <linux/regmap.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/time.h> 178c2ecf20Sopenharmony_ci#include <sound/core.h> 188c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 198c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 208c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 218c2ecf20Sopenharmony_ci#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "fsl_sai.h" 248c2ecf20Sopenharmony_ci#include "imx-pcm.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\ 278c2ecf20Sopenharmony_ci FSL_SAI_CSR_FEIE) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic const unsigned int fsl_sai_rates[] = { 308c2ecf20Sopenharmony_ci 8000, 11025, 12000, 16000, 22050, 318c2ecf20Sopenharmony_ci 24000, 32000, 44100, 48000, 64000, 328c2ecf20Sopenharmony_ci 88200, 96000, 176400, 192000 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = { 368c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(fsl_sai_rates), 378c2ecf20Sopenharmony_ci .list = fsl_sai_rates, 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/** 418c2ecf20Sopenharmony_ci * fsl_sai_dir_is_synced - Check if stream is synced by the opposite stream 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * SAI supports synchronous mode using bit/frame clocks of either Transmitter's 448c2ecf20Sopenharmony_ci * or Receiver's for both streams. This function is used to check if clocks of 458c2ecf20Sopenharmony_ci * the stream's are synced by the opposite stream. 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * @sai: SAI context 488c2ecf20Sopenharmony_ci * @dir: stream direction 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistatic inline bool fsl_sai_dir_is_synced(struct fsl_sai *sai, int dir) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci int adir = (dir == TX) ? RX : TX; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* current dir in async mode while opposite dir in sync mode */ 558c2ecf20Sopenharmony_ci return !sai->synchronous[dir] && sai->synchronous[adir]; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic irqreturn_t fsl_sai_isr(int irq, void *devid) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct fsl_sai *sai = (struct fsl_sai *)devid; 618c2ecf20Sopenharmony_ci unsigned int ofs = sai->soc_data->reg_offset; 628c2ecf20Sopenharmony_ci struct device *dev = &sai->pdev->dev; 638c2ecf20Sopenharmony_ci u32 flags, xcsr, mask; 648c2ecf20Sopenharmony_ci bool irq_none = true; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* 678c2ecf20Sopenharmony_ci * Both IRQ status bits and IRQ mask bits are in the xCSR but 688c2ecf20Sopenharmony_ci * different shifts. And we here create a mask only for those 698c2ecf20Sopenharmony_ci * IRQs that we activated. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ci mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* Tx IRQ */ 748c2ecf20Sopenharmony_ci regmap_read(sai->regmap, FSL_SAI_TCSR(ofs), &xcsr); 758c2ecf20Sopenharmony_ci flags = xcsr & mask; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (flags) 788c2ecf20Sopenharmony_ci irq_none = false; 798c2ecf20Sopenharmony_ci else 808c2ecf20Sopenharmony_ci goto irq_rx; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (flags & FSL_SAI_CSR_WSF) 838c2ecf20Sopenharmony_ci dev_dbg(dev, "isr: Start of Tx word detected\n"); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (flags & FSL_SAI_CSR_SEF) 868c2ecf20Sopenharmony_ci dev_dbg(dev, "isr: Tx Frame sync error detected\n"); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (flags & FSL_SAI_CSR_FEF) { 898c2ecf20Sopenharmony_ci dev_dbg(dev, "isr: Transmit underrun detected\n"); 908c2ecf20Sopenharmony_ci /* FIFO reset for safety */ 918c2ecf20Sopenharmony_ci xcsr |= FSL_SAI_CSR_FR; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (flags & FSL_SAI_CSR_FWF) 958c2ecf20Sopenharmony_ci dev_dbg(dev, "isr: Enabled transmit FIFO is empty\n"); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (flags & FSL_SAI_CSR_FRF) 988c2ecf20Sopenharmony_ci dev_dbg(dev, "isr: Transmit FIFO watermark has been reached\n"); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci flags &= FSL_SAI_CSR_xF_W_MASK; 1018c2ecf20Sopenharmony_ci xcsr &= ~FSL_SAI_CSR_xF_MASK; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (flags) 1048c2ecf20Sopenharmony_ci regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), flags | xcsr); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ciirq_rx: 1078c2ecf20Sopenharmony_ci /* Rx IRQ */ 1088c2ecf20Sopenharmony_ci regmap_read(sai->regmap, FSL_SAI_RCSR(ofs), &xcsr); 1098c2ecf20Sopenharmony_ci flags = xcsr & mask; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (flags) 1128c2ecf20Sopenharmony_ci irq_none = false; 1138c2ecf20Sopenharmony_ci else 1148c2ecf20Sopenharmony_ci goto out; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (flags & FSL_SAI_CSR_WSF) 1178c2ecf20Sopenharmony_ci dev_dbg(dev, "isr: Start of Rx word detected\n"); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (flags & FSL_SAI_CSR_SEF) 1208c2ecf20Sopenharmony_ci dev_dbg(dev, "isr: Rx Frame sync error detected\n"); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (flags & FSL_SAI_CSR_FEF) { 1238c2ecf20Sopenharmony_ci dev_dbg(dev, "isr: Receive overflow detected\n"); 1248c2ecf20Sopenharmony_ci /* FIFO reset for safety */ 1258c2ecf20Sopenharmony_ci xcsr |= FSL_SAI_CSR_FR; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (flags & FSL_SAI_CSR_FWF) 1298c2ecf20Sopenharmony_ci dev_dbg(dev, "isr: Enabled receive FIFO is full\n"); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (flags & FSL_SAI_CSR_FRF) 1328c2ecf20Sopenharmony_ci dev_dbg(dev, "isr: Receive FIFO watermark has been reached\n"); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci flags &= FSL_SAI_CSR_xF_W_MASK; 1358c2ecf20Sopenharmony_ci xcsr &= ~FSL_SAI_CSR_xF_MASK; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (flags) 1388c2ecf20Sopenharmony_ci regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), flags | xcsr); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ciout: 1418c2ecf20Sopenharmony_ci if (irq_none) 1428c2ecf20Sopenharmony_ci return IRQ_NONE; 1438c2ecf20Sopenharmony_ci else 1448c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, 1488c2ecf20Sopenharmony_ci u32 rx_mask, int slots, int slot_width) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci sai->slots = slots; 1538c2ecf20Sopenharmony_ci sai->slot_width = slot_width; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int fsl_sai_set_dai_bclk_ratio(struct snd_soc_dai *dai, 1598c2ecf20Sopenharmony_ci unsigned int ratio) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci sai->bclk_ratio = ratio; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, 1698c2ecf20Sopenharmony_ci int clk_id, unsigned int freq, int fsl_dir) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); 1728c2ecf20Sopenharmony_ci unsigned int ofs = sai->soc_data->reg_offset; 1738c2ecf20Sopenharmony_ci bool tx = fsl_dir == FSL_FMT_TRANSMITTER; 1748c2ecf20Sopenharmony_ci u32 val_cr2 = 0; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci switch (clk_id) { 1778c2ecf20Sopenharmony_ci case FSL_SAI_CLK_BUS: 1788c2ecf20Sopenharmony_ci val_cr2 |= FSL_SAI_CR2_MSEL_BUS; 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci case FSL_SAI_CLK_MAST1: 1818c2ecf20Sopenharmony_ci val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1; 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci case FSL_SAI_CLK_MAST2: 1848c2ecf20Sopenharmony_ci val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2; 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci case FSL_SAI_CLK_MAST3: 1878c2ecf20Sopenharmony_ci val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3; 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci default: 1908c2ecf20Sopenharmony_ci return -EINVAL; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), 1948c2ecf20Sopenharmony_ci FSL_SAI_CR2_MSEL_MASK, val_cr2); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return 0; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, 2008c2ecf20Sopenharmony_ci int clk_id, unsigned int freq, int dir) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci int ret; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (dir == SND_SOC_CLOCK_IN) 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, 2088c2ecf20Sopenharmony_ci FSL_FMT_TRANSMITTER); 2098c2ecf20Sopenharmony_ci if (ret) { 2108c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret); 2118c2ecf20Sopenharmony_ci return ret; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, 2158c2ecf20Sopenharmony_ci FSL_FMT_RECEIVER); 2168c2ecf20Sopenharmony_ci if (ret) 2178c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return ret; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, 2238c2ecf20Sopenharmony_ci unsigned int fmt, int fsl_dir) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); 2268c2ecf20Sopenharmony_ci unsigned int ofs = sai->soc_data->reg_offset; 2278c2ecf20Sopenharmony_ci bool tx = fsl_dir == FSL_FMT_TRANSMITTER; 2288c2ecf20Sopenharmony_ci u32 val_cr2 = 0, val_cr4 = 0; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (!sai->is_lsb_first) 2318c2ecf20Sopenharmony_ci val_cr4 |= FSL_SAI_CR4_MF; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci sai->is_dsp_mode = false; 2348c2ecf20Sopenharmony_ci /* DAI mode */ 2358c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 2368c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 2378c2ecf20Sopenharmony_ci /* 2388c2ecf20Sopenharmony_ci * Frame low, 1clk before data, one word length for frame sync, 2398c2ecf20Sopenharmony_ci * frame sync starts one serial clock cycle earlier, 2408c2ecf20Sopenharmony_ci * that is, together with the last bit of the previous 2418c2ecf20Sopenharmony_ci * data word. 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_ci val_cr2 |= FSL_SAI_CR2_BCP; 2448c2ecf20Sopenharmony_ci val_cr4 |= FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP; 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 2478c2ecf20Sopenharmony_ci /* 2488c2ecf20Sopenharmony_ci * Frame high, one word length for frame sync, 2498c2ecf20Sopenharmony_ci * frame sync asserts with the first bit of the frame. 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ci val_cr2 |= FSL_SAI_CR2_BCP; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 2548c2ecf20Sopenharmony_ci /* 2558c2ecf20Sopenharmony_ci * Frame high, 1clk before data, one bit for frame sync, 2568c2ecf20Sopenharmony_ci * frame sync starts one serial clock cycle earlier, 2578c2ecf20Sopenharmony_ci * that is, together with the last bit of the previous 2588c2ecf20Sopenharmony_ci * data word. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci val_cr2 |= FSL_SAI_CR2_BCP; 2618c2ecf20Sopenharmony_ci val_cr4 |= FSL_SAI_CR4_FSE; 2628c2ecf20Sopenharmony_ci sai->is_dsp_mode = true; 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 2658c2ecf20Sopenharmony_ci /* 2668c2ecf20Sopenharmony_ci * Frame high, one bit for frame sync, 2678c2ecf20Sopenharmony_ci * frame sync asserts with the first bit of the frame. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_ci val_cr2 |= FSL_SAI_CR2_BCP; 2708c2ecf20Sopenharmony_ci sai->is_dsp_mode = true; 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 2738c2ecf20Sopenharmony_ci /* To be done */ 2748c2ecf20Sopenharmony_ci default: 2758c2ecf20Sopenharmony_ci return -EINVAL; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* DAI clock inversion */ 2798c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 2808c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 2818c2ecf20Sopenharmony_ci /* Invert both clocks */ 2828c2ecf20Sopenharmony_ci val_cr2 ^= FSL_SAI_CR2_BCP; 2838c2ecf20Sopenharmony_ci val_cr4 ^= FSL_SAI_CR4_FSP; 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 2868c2ecf20Sopenharmony_ci /* Invert bit clock */ 2878c2ecf20Sopenharmony_ci val_cr2 ^= FSL_SAI_CR2_BCP; 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 2908c2ecf20Sopenharmony_ci /* Invert frame clock */ 2918c2ecf20Sopenharmony_ci val_cr4 ^= FSL_SAI_CR4_FSP; 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 2948c2ecf20Sopenharmony_ci /* Nothing to do for both normal cases */ 2958c2ecf20Sopenharmony_ci break; 2968c2ecf20Sopenharmony_ci default: 2978c2ecf20Sopenharmony_ci return -EINVAL; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* DAI clock master masks */ 3018c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 3028c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 3038c2ecf20Sopenharmony_ci val_cr2 |= FSL_SAI_CR2_BCD_MSTR; 3048c2ecf20Sopenharmony_ci val_cr4 |= FSL_SAI_CR4_FSD_MSTR; 3058c2ecf20Sopenharmony_ci sai->is_slave_mode = false; 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 3088c2ecf20Sopenharmony_ci sai->is_slave_mode = true; 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFM: 3118c2ecf20Sopenharmony_ci val_cr2 |= FSL_SAI_CR2_BCD_MSTR; 3128c2ecf20Sopenharmony_ci sai->is_slave_mode = false; 3138c2ecf20Sopenharmony_ci break; 3148c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 3158c2ecf20Sopenharmony_ci val_cr4 |= FSL_SAI_CR4_FSD_MSTR; 3168c2ecf20Sopenharmony_ci sai->is_slave_mode = true; 3178c2ecf20Sopenharmony_ci break; 3188c2ecf20Sopenharmony_ci default: 3198c2ecf20Sopenharmony_ci return -EINVAL; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), 3238c2ecf20Sopenharmony_ci FSL_SAI_CR2_BCP | FSL_SAI_CR2_BCD_MSTR, val_cr2); 3248c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs), 3258c2ecf20Sopenharmony_ci FSL_SAI_CR4_MF | FSL_SAI_CR4_FSE | 3268c2ecf20Sopenharmony_ci FSL_SAI_CR4_FSP | FSL_SAI_CR4_FSD_MSTR, val_cr4); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci int ret; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER); 3368c2ecf20Sopenharmony_ci if (ret) { 3378c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret); 3388c2ecf20Sopenharmony_ci return ret; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER); 3428c2ecf20Sopenharmony_ci if (ret) 3438c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return ret; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai); 3518c2ecf20Sopenharmony_ci unsigned int ofs = sai->soc_data->reg_offset; 3528c2ecf20Sopenharmony_ci unsigned long clk_rate; 3538c2ecf20Sopenharmony_ci u32 savediv = 0, ratio, savesub = freq; 3548c2ecf20Sopenharmony_ci int adir = tx ? RX : TX; 3558c2ecf20Sopenharmony_ci int dir = tx ? TX : RX; 3568c2ecf20Sopenharmony_ci u32 id; 3578c2ecf20Sopenharmony_ci int ret = 0; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* Don't apply to slave mode */ 3608c2ecf20Sopenharmony_ci if (sai->is_slave_mode) 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci for (id = 0; id < FSL_SAI_MCLK_MAX; id++) { 3648c2ecf20Sopenharmony_ci clk_rate = clk_get_rate(sai->mclk_clk[id]); 3658c2ecf20Sopenharmony_ci if (!clk_rate) 3668c2ecf20Sopenharmony_ci continue; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ratio = clk_rate / freq; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci ret = clk_rate - ratio * freq; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* 3738c2ecf20Sopenharmony_ci * Drop the source that can not be 3748c2ecf20Sopenharmony_ci * divided into the required rate. 3758c2ecf20Sopenharmony_ci */ 3768c2ecf20Sopenharmony_ci if (ret != 0 && clk_rate / ret < 1000) 3778c2ecf20Sopenharmony_ci continue; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci dev_dbg(dai->dev, 3808c2ecf20Sopenharmony_ci "ratio %d for freq %dHz based on clock %ldHz\n", 3818c2ecf20Sopenharmony_ci ratio, freq, clk_rate); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (ratio % 2 == 0 && ratio >= 2 && ratio <= 512) 3848c2ecf20Sopenharmony_ci ratio /= 2; 3858c2ecf20Sopenharmony_ci else 3868c2ecf20Sopenharmony_ci continue; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (ret < savesub) { 3898c2ecf20Sopenharmony_ci savediv = ratio; 3908c2ecf20Sopenharmony_ci sai->mclk_id[tx] = id; 3918c2ecf20Sopenharmony_ci savesub = ret; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (ret == 0) 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (savediv == 0) { 3998c2ecf20Sopenharmony_ci dev_err(dai->dev, "failed to derive required %cx rate: %d\n", 4008c2ecf20Sopenharmony_ci tx ? 'T' : 'R', freq); 4018c2ecf20Sopenharmony_ci return -EINVAL; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* 4058c2ecf20Sopenharmony_ci * 1) For Asynchronous mode, we must set RCR2 register for capture, and 4068c2ecf20Sopenharmony_ci * set TCR2 register for playback. 4078c2ecf20Sopenharmony_ci * 2) For Tx sync with Rx clock, we must set RCR2 register for playback 4088c2ecf20Sopenharmony_ci * and capture. 4098c2ecf20Sopenharmony_ci * 3) For Rx sync with Tx clock, we must set TCR2 register for playback 4108c2ecf20Sopenharmony_ci * and capture. 4118c2ecf20Sopenharmony_ci * 4) For Tx and Rx are both Synchronous with another SAI, we just 4128c2ecf20Sopenharmony_ci * ignore it. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_ci if (fsl_sai_dir_is_synced(sai, adir)) { 4158c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs), 4168c2ecf20Sopenharmony_ci FSL_SAI_CR2_MSEL_MASK, 4178c2ecf20Sopenharmony_ci FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); 4188c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs), 4198c2ecf20Sopenharmony_ci FSL_SAI_CR2_DIV_MASK, savediv - 1); 4208c2ecf20Sopenharmony_ci } else if (!sai->synchronous[dir]) { 4218c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), 4228c2ecf20Sopenharmony_ci FSL_SAI_CR2_MSEL_MASK, 4238c2ecf20Sopenharmony_ci FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); 4248c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), 4258c2ecf20Sopenharmony_ci FSL_SAI_CR2_DIV_MASK, savediv - 1); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n", 4298c2ecf20Sopenharmony_ci sai->mclk_id[tx], savediv, savesub); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic int fsl_sai_hw_params(struct snd_pcm_substream *substream, 4358c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 4368c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); 4398c2ecf20Sopenharmony_ci unsigned int ofs = sai->soc_data->reg_offset; 4408c2ecf20Sopenharmony_ci bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 4418c2ecf20Sopenharmony_ci unsigned int channels = params_channels(params); 4428c2ecf20Sopenharmony_ci u32 word_width = params_width(params); 4438c2ecf20Sopenharmony_ci u32 val_cr4 = 0, val_cr5 = 0; 4448c2ecf20Sopenharmony_ci u32 slots = (channels == 1) ? 2 : channels; 4458c2ecf20Sopenharmony_ci u32 slot_width = word_width; 4468c2ecf20Sopenharmony_ci int adir = tx ? RX : TX; 4478c2ecf20Sopenharmony_ci u32 pins; 4488c2ecf20Sopenharmony_ci int ret; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (sai->slots) 4518c2ecf20Sopenharmony_ci slots = sai->slots; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (sai->slot_width) 4548c2ecf20Sopenharmony_ci slot_width = sai->slot_width; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci pins = DIV_ROUND_UP(channels, slots); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (!sai->is_slave_mode) { 4598c2ecf20Sopenharmony_ci if (sai->bclk_ratio) 4608c2ecf20Sopenharmony_ci ret = fsl_sai_set_bclk(cpu_dai, tx, 4618c2ecf20Sopenharmony_ci sai->bclk_ratio * 4628c2ecf20Sopenharmony_ci params_rate(params)); 4638c2ecf20Sopenharmony_ci else 4648c2ecf20Sopenharmony_ci ret = fsl_sai_set_bclk(cpu_dai, tx, 4658c2ecf20Sopenharmony_ci slots * slot_width * 4668c2ecf20Sopenharmony_ci params_rate(params)); 4678c2ecf20Sopenharmony_ci if (ret) 4688c2ecf20Sopenharmony_ci return ret; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* Do not enable the clock if it is already enabled */ 4718c2ecf20Sopenharmony_ci if (!(sai->mclk_streams & BIT(substream->stream))) { 4728c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[tx]]); 4738c2ecf20Sopenharmony_ci if (ret) 4748c2ecf20Sopenharmony_ci return ret; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci sai->mclk_streams |= BIT(substream->stream); 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (!sai->is_dsp_mode) 4818c2ecf20Sopenharmony_ci val_cr4 |= FSL_SAI_CR4_SYWD(slot_width); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci val_cr5 |= FSL_SAI_CR5_WNW(slot_width); 4848c2ecf20Sopenharmony_ci val_cr5 |= FSL_SAI_CR5_W0W(slot_width); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (sai->is_lsb_first) 4878c2ecf20Sopenharmony_ci val_cr5 |= FSL_SAI_CR5_FBT(0); 4888c2ecf20Sopenharmony_ci else 4898c2ecf20Sopenharmony_ci val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci val_cr4 |= FSL_SAI_CR4_FRSZ(slots); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* Set to output mode to avoid tri-stated data pins */ 4948c2ecf20Sopenharmony_ci if (tx) 4958c2ecf20Sopenharmony_ci val_cr4 |= FSL_SAI_CR4_CHMOD; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* 4988c2ecf20Sopenharmony_ci * For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will 4998c2ecf20Sopenharmony_ci * generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4), 5008c2ecf20Sopenharmony_ci * RCR5(TCR5) for playback(capture), or there will be sync error. 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (!sai->is_slave_mode && fsl_sai_dir_is_synced(sai, adir)) { 5048c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCR4(!tx, ofs), 5058c2ecf20Sopenharmony_ci FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK | 5068c2ecf20Sopenharmony_ci FSL_SAI_CR4_CHMOD_MASK, 5078c2ecf20Sopenharmony_ci val_cr4); 5088c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCR5(!tx, ofs), 5098c2ecf20Sopenharmony_ci FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK | 5108c2ecf20Sopenharmony_ci FSL_SAI_CR5_FBT_MASK, val_cr5); 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs), 5148c2ecf20Sopenharmony_ci FSL_SAI_CR3_TRCE_MASK, 5158c2ecf20Sopenharmony_ci FSL_SAI_CR3_TRCE((1 << pins) - 1)); 5168c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs), 5178c2ecf20Sopenharmony_ci FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK | 5188c2ecf20Sopenharmony_ci FSL_SAI_CR4_CHMOD_MASK, 5198c2ecf20Sopenharmony_ci val_cr4); 5208c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCR5(tx, ofs), 5218c2ecf20Sopenharmony_ci FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK | 5228c2ecf20Sopenharmony_ci FSL_SAI_CR5_FBT_MASK, val_cr5); 5238c2ecf20Sopenharmony_ci regmap_write(sai->regmap, FSL_SAI_xMR(tx), 5248c2ecf20Sopenharmony_ci ~0UL - ((1 << min(channels, slots)) - 1)); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return 0; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic int fsl_sai_hw_free(struct snd_pcm_substream *substream, 5308c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); 5338c2ecf20Sopenharmony_ci bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 5348c2ecf20Sopenharmony_ci unsigned int ofs = sai->soc_data->reg_offset; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs), 5378c2ecf20Sopenharmony_ci FSL_SAI_CR3_TRCE_MASK, 0); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (!sai->is_slave_mode && 5408c2ecf20Sopenharmony_ci sai->mclk_streams & BIT(substream->stream)) { 5418c2ecf20Sopenharmony_ci clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]); 5428c2ecf20Sopenharmony_ci sai->mclk_streams &= ~BIT(substream->stream); 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci return 0; 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic void fsl_sai_config_disable(struct fsl_sai *sai, int dir) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci unsigned int ofs = sai->soc_data->reg_offset; 5518c2ecf20Sopenharmony_ci bool tx = dir == TX; 5528c2ecf20Sopenharmony_ci u32 xcsr, count = 100; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), 5558c2ecf20Sopenharmony_ci FSL_SAI_CSR_TERE | FSL_SAI_CSR_BCE, 0); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* TERE will remain set till the end of current frame */ 5588c2ecf20Sopenharmony_ci do { 5598c2ecf20Sopenharmony_ci udelay(10); 5608c2ecf20Sopenharmony_ci regmap_read(sai->regmap, FSL_SAI_xCSR(tx, ofs), &xcsr); 5618c2ecf20Sopenharmony_ci } while (--count && xcsr & FSL_SAI_CSR_TERE); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), 5648c2ecf20Sopenharmony_ci FSL_SAI_CSR_FR, FSL_SAI_CSR_FR); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* 5678c2ecf20Sopenharmony_ci * For sai master mode, after several open/close sai, 5688c2ecf20Sopenharmony_ci * there will be no frame clock, and can't recover 5698c2ecf20Sopenharmony_ci * anymore. Add software reset to fix this issue. 5708c2ecf20Sopenharmony_ci * This is a hardware bug, and will be fix in the 5718c2ecf20Sopenharmony_ci * next sai version. 5728c2ecf20Sopenharmony_ci */ 5738c2ecf20Sopenharmony_ci if (!sai->is_slave_mode) { 5748c2ecf20Sopenharmony_ci /* Software Reset */ 5758c2ecf20Sopenharmony_ci regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR); 5768c2ecf20Sopenharmony_ci /* Clear SR bit to finish the reset */ 5778c2ecf20Sopenharmony_ci regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), 0); 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, 5828c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); 5858c2ecf20Sopenharmony_ci unsigned int ofs = sai->soc_data->reg_offset; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 5888c2ecf20Sopenharmony_ci int adir = tx ? RX : TX; 5898c2ecf20Sopenharmony_ci int dir = tx ? TX : RX; 5908c2ecf20Sopenharmony_ci u32 xcsr; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* 5938c2ecf20Sopenharmony_ci * Asynchronous mode: Clear SYNC for both Tx and Rx. 5948c2ecf20Sopenharmony_ci * Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx. 5958c2ecf20Sopenharmony_ci * Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx. 5968c2ecf20Sopenharmony_ci */ 5978c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_TCR2(ofs), FSL_SAI_CR2_SYNC, 5988c2ecf20Sopenharmony_ci sai->synchronous[TX] ? FSL_SAI_CR2_SYNC : 0); 5998c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_RCR2(ofs), FSL_SAI_CR2_SYNC, 6008c2ecf20Sopenharmony_ci sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* 6038c2ecf20Sopenharmony_ci * It is recommended that the transmitter is the last enabled 6048c2ecf20Sopenharmony_ci * and the first disabled. 6058c2ecf20Sopenharmony_ci */ 6068c2ecf20Sopenharmony_ci switch (cmd) { 6078c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 6088c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 6098c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 6108c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), 6118c2ecf20Sopenharmony_ci FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), 6148c2ecf20Sopenharmony_ci FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE); 6158c2ecf20Sopenharmony_ci /* 6168c2ecf20Sopenharmony_ci * Enable the opposite direction for synchronous mode 6178c2ecf20Sopenharmony_ci * 1. Tx sync with Rx: only set RE for Rx; set TE & RE for Tx 6188c2ecf20Sopenharmony_ci * 2. Rx sync with Tx: only set TE for Tx; set RE & TE for Rx 6198c2ecf20Sopenharmony_ci * 6208c2ecf20Sopenharmony_ci * RM recommends to enable RE after TE for case 1 and to enable 6218c2ecf20Sopenharmony_ci * TE after RE for case 2, but we here may not always guarantee 6228c2ecf20Sopenharmony_ci * that happens: "arecord 1.wav; aplay 2.wav" in case 1 enables 6238c2ecf20Sopenharmony_ci * TE after RE, which is against what RM recommends but should 6248c2ecf20Sopenharmony_ci * be safe to do, judging by years of testing results. 6258c2ecf20Sopenharmony_ci */ 6268c2ecf20Sopenharmony_ci if (fsl_sai_dir_is_synced(sai, adir)) 6278c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCSR((!tx), ofs), 6288c2ecf20Sopenharmony_ci FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), 6318c2ecf20Sopenharmony_ci FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS); 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 6348c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 6358c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 6368c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), 6378c2ecf20Sopenharmony_ci FSL_SAI_CSR_FRDE, 0); 6388c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), 6398c2ecf20Sopenharmony_ci FSL_SAI_CSR_xIE_MASK, 0); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* Check if the opposite FRDE is also disabled */ 6428c2ecf20Sopenharmony_ci regmap_read(sai->regmap, FSL_SAI_xCSR(!tx, ofs), &xcsr); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci /* 6458c2ecf20Sopenharmony_ci * If opposite stream provides clocks for synchronous mode and 6468c2ecf20Sopenharmony_ci * it is inactive, disable it before disabling the current one 6478c2ecf20Sopenharmony_ci */ 6488c2ecf20Sopenharmony_ci if (fsl_sai_dir_is_synced(sai, adir) && !(xcsr & FSL_SAI_CSR_FRDE)) 6498c2ecf20Sopenharmony_ci fsl_sai_config_disable(sai, adir); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* 6528c2ecf20Sopenharmony_ci * Disable current stream if either of: 6538c2ecf20Sopenharmony_ci * 1. current stream doesn't provide clocks for synchronous mode 6548c2ecf20Sopenharmony_ci * 2. current stream provides clocks for synchronous mode but no 6558c2ecf20Sopenharmony_ci * more stream is active. 6568c2ecf20Sopenharmony_ci */ 6578c2ecf20Sopenharmony_ci if (!fsl_sai_dir_is_synced(sai, dir) || !(xcsr & FSL_SAI_CSR_FRDE)) 6588c2ecf20Sopenharmony_ci fsl_sai_config_disable(sai, dir); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci break; 6618c2ecf20Sopenharmony_ci default: 6628c2ecf20Sopenharmony_ci return -EINVAL; 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci return 0; 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic int fsl_sai_startup(struct snd_pcm_substream *substream, 6698c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); 6728c2ecf20Sopenharmony_ci bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 6738c2ecf20Sopenharmony_ci int ret; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* 6768c2ecf20Sopenharmony_ci * EDMA controller needs period size to be a multiple of 6778c2ecf20Sopenharmony_ci * tx/rx maxburst 6788c2ecf20Sopenharmony_ci */ 6798c2ecf20Sopenharmony_ci if (sai->soc_data->use_edma) 6808c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_step(substream->runtime, 0, 6818c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 6828c2ecf20Sopenharmony_ci tx ? sai->dma_params_tx.maxburst : 6838c2ecf20Sopenharmony_ci sai->dma_params_rx.maxburst); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_list(substream->runtime, 0, 6868c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, &fsl_sai_rate_constraints); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci return ret; 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { 6928c2ecf20Sopenharmony_ci .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio, 6938c2ecf20Sopenharmony_ci .set_sysclk = fsl_sai_set_dai_sysclk, 6948c2ecf20Sopenharmony_ci .set_fmt = fsl_sai_set_dai_fmt, 6958c2ecf20Sopenharmony_ci .set_tdm_slot = fsl_sai_set_dai_tdm_slot, 6968c2ecf20Sopenharmony_ci .hw_params = fsl_sai_hw_params, 6978c2ecf20Sopenharmony_ci .hw_free = fsl_sai_hw_free, 6988c2ecf20Sopenharmony_ci .trigger = fsl_sai_trigger, 6998c2ecf20Sopenharmony_ci .startup = fsl_sai_startup, 7008c2ecf20Sopenharmony_ci}; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev); 7058c2ecf20Sopenharmony_ci unsigned int ofs = sai->soc_data->reg_offset; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci /* Software Reset for both Tx and Rx */ 7088c2ecf20Sopenharmony_ci regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR); 7098c2ecf20Sopenharmony_ci regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR); 7108c2ecf20Sopenharmony_ci /* Clear SR bit to finish the reset */ 7118c2ecf20Sopenharmony_ci regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), 0); 7128c2ecf20Sopenharmony_ci regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), 0); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_TCR1(ofs), 7158c2ecf20Sopenharmony_ci FSL_SAI_CR1_RFW_MASK(sai->soc_data->fifo_depth), 7168c2ecf20Sopenharmony_ci sai->soc_data->fifo_depth - FSL_SAI_MAXBURST_TX); 7178c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_RCR1(ofs), 7188c2ecf20Sopenharmony_ci FSL_SAI_CR1_RFW_MASK(sai->soc_data->fifo_depth), 7198c2ecf20Sopenharmony_ci FSL_SAI_MAXBURST_RX - 1); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx, 7228c2ecf20Sopenharmony_ci &sai->dma_params_rx); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci snd_soc_dai_set_drvdata(cpu_dai, sai); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci return 0; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver fsl_sai_dai_template = { 7308c2ecf20Sopenharmony_ci .probe = fsl_sai_dai_probe, 7318c2ecf20Sopenharmony_ci .playback = { 7328c2ecf20Sopenharmony_ci .stream_name = "CPU-Playback", 7338c2ecf20Sopenharmony_ci .channels_min = 1, 7348c2ecf20Sopenharmony_ci .channels_max = 32, 7358c2ecf20Sopenharmony_ci .rate_min = 8000, 7368c2ecf20Sopenharmony_ci .rate_max = 192000, 7378c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT, 7388c2ecf20Sopenharmony_ci .formats = FSL_SAI_FORMATS, 7398c2ecf20Sopenharmony_ci }, 7408c2ecf20Sopenharmony_ci .capture = { 7418c2ecf20Sopenharmony_ci .stream_name = "CPU-Capture", 7428c2ecf20Sopenharmony_ci .channels_min = 1, 7438c2ecf20Sopenharmony_ci .channels_max = 32, 7448c2ecf20Sopenharmony_ci .rate_min = 8000, 7458c2ecf20Sopenharmony_ci .rate_max = 192000, 7468c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT, 7478c2ecf20Sopenharmony_ci .formats = FSL_SAI_FORMATS, 7488c2ecf20Sopenharmony_ci }, 7498c2ecf20Sopenharmony_ci .ops = &fsl_sai_pcm_dai_ops, 7508c2ecf20Sopenharmony_ci}; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver fsl_component = { 7538c2ecf20Sopenharmony_ci .name = "fsl-sai", 7548c2ecf20Sopenharmony_ci}; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic struct reg_default fsl_sai_reg_defaults_ofs0[] = { 7578c2ecf20Sopenharmony_ci {FSL_SAI_TCR1(0), 0}, 7588c2ecf20Sopenharmony_ci {FSL_SAI_TCR2(0), 0}, 7598c2ecf20Sopenharmony_ci {FSL_SAI_TCR3(0), 0}, 7608c2ecf20Sopenharmony_ci {FSL_SAI_TCR4(0), 0}, 7618c2ecf20Sopenharmony_ci {FSL_SAI_TCR5(0), 0}, 7628c2ecf20Sopenharmony_ci {FSL_SAI_TDR0, 0}, 7638c2ecf20Sopenharmony_ci {FSL_SAI_TDR1, 0}, 7648c2ecf20Sopenharmony_ci {FSL_SAI_TDR2, 0}, 7658c2ecf20Sopenharmony_ci {FSL_SAI_TDR3, 0}, 7668c2ecf20Sopenharmony_ci {FSL_SAI_TDR4, 0}, 7678c2ecf20Sopenharmony_ci {FSL_SAI_TDR5, 0}, 7688c2ecf20Sopenharmony_ci {FSL_SAI_TDR6, 0}, 7698c2ecf20Sopenharmony_ci {FSL_SAI_TDR7, 0}, 7708c2ecf20Sopenharmony_ci {FSL_SAI_TMR, 0}, 7718c2ecf20Sopenharmony_ci {FSL_SAI_RCR1(0), 0}, 7728c2ecf20Sopenharmony_ci {FSL_SAI_RCR2(0), 0}, 7738c2ecf20Sopenharmony_ci {FSL_SAI_RCR3(0), 0}, 7748c2ecf20Sopenharmony_ci {FSL_SAI_RCR4(0), 0}, 7758c2ecf20Sopenharmony_ci {FSL_SAI_RCR5(0), 0}, 7768c2ecf20Sopenharmony_ci {FSL_SAI_RMR, 0}, 7778c2ecf20Sopenharmony_ci}; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic struct reg_default fsl_sai_reg_defaults_ofs8[] = { 7808c2ecf20Sopenharmony_ci {FSL_SAI_TCR1(8), 0}, 7818c2ecf20Sopenharmony_ci {FSL_SAI_TCR2(8), 0}, 7828c2ecf20Sopenharmony_ci {FSL_SAI_TCR3(8), 0}, 7838c2ecf20Sopenharmony_ci {FSL_SAI_TCR4(8), 0}, 7848c2ecf20Sopenharmony_ci {FSL_SAI_TCR5(8), 0}, 7858c2ecf20Sopenharmony_ci {FSL_SAI_TDR0, 0}, 7868c2ecf20Sopenharmony_ci {FSL_SAI_TDR1, 0}, 7878c2ecf20Sopenharmony_ci {FSL_SAI_TDR2, 0}, 7888c2ecf20Sopenharmony_ci {FSL_SAI_TDR3, 0}, 7898c2ecf20Sopenharmony_ci {FSL_SAI_TDR4, 0}, 7908c2ecf20Sopenharmony_ci {FSL_SAI_TDR5, 0}, 7918c2ecf20Sopenharmony_ci {FSL_SAI_TDR6, 0}, 7928c2ecf20Sopenharmony_ci {FSL_SAI_TDR7, 0}, 7938c2ecf20Sopenharmony_ci {FSL_SAI_TMR, 0}, 7948c2ecf20Sopenharmony_ci {FSL_SAI_RCR1(8), 0}, 7958c2ecf20Sopenharmony_ci {FSL_SAI_RCR2(8), 0}, 7968c2ecf20Sopenharmony_ci {FSL_SAI_RCR3(8), 0}, 7978c2ecf20Sopenharmony_ci {FSL_SAI_RCR4(8), 0}, 7988c2ecf20Sopenharmony_ci {FSL_SAI_RCR5(8), 0}, 7998c2ecf20Sopenharmony_ci {FSL_SAI_RMR, 0}, 8008c2ecf20Sopenharmony_ci {FSL_SAI_MCTL, 0}, 8018c2ecf20Sopenharmony_ci {FSL_SAI_MDIV, 0}, 8028c2ecf20Sopenharmony_ci}; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_cistatic bool fsl_sai_readable_reg(struct device *dev, unsigned int reg) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci struct fsl_sai *sai = dev_get_drvdata(dev); 8078c2ecf20Sopenharmony_ci unsigned int ofs = sai->soc_data->reg_offset; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if (reg >= FSL_SAI_TCSR(ofs) && reg <= FSL_SAI_TCR5(ofs)) 8108c2ecf20Sopenharmony_ci return true; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (reg >= FSL_SAI_RCSR(ofs) && reg <= FSL_SAI_RCR5(ofs)) 8138c2ecf20Sopenharmony_ci return true; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci switch (reg) { 8168c2ecf20Sopenharmony_ci case FSL_SAI_TFR0: 8178c2ecf20Sopenharmony_ci case FSL_SAI_TFR1: 8188c2ecf20Sopenharmony_ci case FSL_SAI_TFR2: 8198c2ecf20Sopenharmony_ci case FSL_SAI_TFR3: 8208c2ecf20Sopenharmony_ci case FSL_SAI_TFR4: 8218c2ecf20Sopenharmony_ci case FSL_SAI_TFR5: 8228c2ecf20Sopenharmony_ci case FSL_SAI_TFR6: 8238c2ecf20Sopenharmony_ci case FSL_SAI_TFR7: 8248c2ecf20Sopenharmony_ci case FSL_SAI_TMR: 8258c2ecf20Sopenharmony_ci case FSL_SAI_RDR0: 8268c2ecf20Sopenharmony_ci case FSL_SAI_RDR1: 8278c2ecf20Sopenharmony_ci case FSL_SAI_RDR2: 8288c2ecf20Sopenharmony_ci case FSL_SAI_RDR3: 8298c2ecf20Sopenharmony_ci case FSL_SAI_RDR4: 8308c2ecf20Sopenharmony_ci case FSL_SAI_RDR5: 8318c2ecf20Sopenharmony_ci case FSL_SAI_RDR6: 8328c2ecf20Sopenharmony_ci case FSL_SAI_RDR7: 8338c2ecf20Sopenharmony_ci case FSL_SAI_RFR0: 8348c2ecf20Sopenharmony_ci case FSL_SAI_RFR1: 8358c2ecf20Sopenharmony_ci case FSL_SAI_RFR2: 8368c2ecf20Sopenharmony_ci case FSL_SAI_RFR3: 8378c2ecf20Sopenharmony_ci case FSL_SAI_RFR4: 8388c2ecf20Sopenharmony_ci case FSL_SAI_RFR5: 8398c2ecf20Sopenharmony_ci case FSL_SAI_RFR6: 8408c2ecf20Sopenharmony_ci case FSL_SAI_RFR7: 8418c2ecf20Sopenharmony_ci case FSL_SAI_RMR: 8428c2ecf20Sopenharmony_ci case FSL_SAI_MCTL: 8438c2ecf20Sopenharmony_ci case FSL_SAI_MDIV: 8448c2ecf20Sopenharmony_ci case FSL_SAI_VERID: 8458c2ecf20Sopenharmony_ci case FSL_SAI_PARAM: 8468c2ecf20Sopenharmony_ci case FSL_SAI_TTCTN: 8478c2ecf20Sopenharmony_ci case FSL_SAI_RTCTN: 8488c2ecf20Sopenharmony_ci case FSL_SAI_TTCTL: 8498c2ecf20Sopenharmony_ci case FSL_SAI_TBCTN: 8508c2ecf20Sopenharmony_ci case FSL_SAI_TTCAP: 8518c2ecf20Sopenharmony_ci case FSL_SAI_RTCTL: 8528c2ecf20Sopenharmony_ci case FSL_SAI_RBCTN: 8538c2ecf20Sopenharmony_ci case FSL_SAI_RTCAP: 8548c2ecf20Sopenharmony_ci return true; 8558c2ecf20Sopenharmony_ci default: 8568c2ecf20Sopenharmony_ci return false; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct fsl_sai *sai = dev_get_drvdata(dev); 8638c2ecf20Sopenharmony_ci unsigned int ofs = sai->soc_data->reg_offset; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci if (reg == FSL_SAI_TCSR(ofs) || reg == FSL_SAI_RCSR(ofs)) 8668c2ecf20Sopenharmony_ci return true; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci /* Set VERID and PARAM be volatile for reading value in probe */ 8698c2ecf20Sopenharmony_ci if (ofs == 8 && (reg == FSL_SAI_VERID || reg == FSL_SAI_PARAM)) 8708c2ecf20Sopenharmony_ci return true; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci switch (reg) { 8738c2ecf20Sopenharmony_ci case FSL_SAI_TFR0: 8748c2ecf20Sopenharmony_ci case FSL_SAI_TFR1: 8758c2ecf20Sopenharmony_ci case FSL_SAI_TFR2: 8768c2ecf20Sopenharmony_ci case FSL_SAI_TFR3: 8778c2ecf20Sopenharmony_ci case FSL_SAI_TFR4: 8788c2ecf20Sopenharmony_ci case FSL_SAI_TFR5: 8798c2ecf20Sopenharmony_ci case FSL_SAI_TFR6: 8808c2ecf20Sopenharmony_ci case FSL_SAI_TFR7: 8818c2ecf20Sopenharmony_ci case FSL_SAI_RFR0: 8828c2ecf20Sopenharmony_ci case FSL_SAI_RFR1: 8838c2ecf20Sopenharmony_ci case FSL_SAI_RFR2: 8848c2ecf20Sopenharmony_ci case FSL_SAI_RFR3: 8858c2ecf20Sopenharmony_ci case FSL_SAI_RFR4: 8868c2ecf20Sopenharmony_ci case FSL_SAI_RFR5: 8878c2ecf20Sopenharmony_ci case FSL_SAI_RFR6: 8888c2ecf20Sopenharmony_ci case FSL_SAI_RFR7: 8898c2ecf20Sopenharmony_ci case FSL_SAI_RDR0: 8908c2ecf20Sopenharmony_ci case FSL_SAI_RDR1: 8918c2ecf20Sopenharmony_ci case FSL_SAI_RDR2: 8928c2ecf20Sopenharmony_ci case FSL_SAI_RDR3: 8938c2ecf20Sopenharmony_ci case FSL_SAI_RDR4: 8948c2ecf20Sopenharmony_ci case FSL_SAI_RDR5: 8958c2ecf20Sopenharmony_ci case FSL_SAI_RDR6: 8968c2ecf20Sopenharmony_ci case FSL_SAI_RDR7: 8978c2ecf20Sopenharmony_ci return true; 8988c2ecf20Sopenharmony_ci default: 8998c2ecf20Sopenharmony_ci return false; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct fsl_sai *sai = dev_get_drvdata(dev); 9068c2ecf20Sopenharmony_ci unsigned int ofs = sai->soc_data->reg_offset; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci if (reg >= FSL_SAI_TCSR(ofs) && reg <= FSL_SAI_TCR5(ofs)) 9098c2ecf20Sopenharmony_ci return true; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (reg >= FSL_SAI_RCSR(ofs) && reg <= FSL_SAI_RCR5(ofs)) 9128c2ecf20Sopenharmony_ci return true; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci switch (reg) { 9158c2ecf20Sopenharmony_ci case FSL_SAI_TDR0: 9168c2ecf20Sopenharmony_ci case FSL_SAI_TDR1: 9178c2ecf20Sopenharmony_ci case FSL_SAI_TDR2: 9188c2ecf20Sopenharmony_ci case FSL_SAI_TDR3: 9198c2ecf20Sopenharmony_ci case FSL_SAI_TDR4: 9208c2ecf20Sopenharmony_ci case FSL_SAI_TDR5: 9218c2ecf20Sopenharmony_ci case FSL_SAI_TDR6: 9228c2ecf20Sopenharmony_ci case FSL_SAI_TDR7: 9238c2ecf20Sopenharmony_ci case FSL_SAI_TMR: 9248c2ecf20Sopenharmony_ci case FSL_SAI_RMR: 9258c2ecf20Sopenharmony_ci case FSL_SAI_MCTL: 9268c2ecf20Sopenharmony_ci case FSL_SAI_MDIV: 9278c2ecf20Sopenharmony_ci case FSL_SAI_TTCTL: 9288c2ecf20Sopenharmony_ci case FSL_SAI_RTCTL: 9298c2ecf20Sopenharmony_ci return true; 9308c2ecf20Sopenharmony_ci default: 9318c2ecf20Sopenharmony_ci return false; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic struct regmap_config fsl_sai_regmap_config = { 9368c2ecf20Sopenharmony_ci .reg_bits = 32, 9378c2ecf20Sopenharmony_ci .reg_stride = 4, 9388c2ecf20Sopenharmony_ci .val_bits = 32, 9398c2ecf20Sopenharmony_ci .fast_io = true, 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci .max_register = FSL_SAI_RMR, 9428c2ecf20Sopenharmony_ci .reg_defaults = fsl_sai_reg_defaults_ofs0, 9438c2ecf20Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults_ofs0), 9448c2ecf20Sopenharmony_ci .readable_reg = fsl_sai_readable_reg, 9458c2ecf20Sopenharmony_ci .volatile_reg = fsl_sai_volatile_reg, 9468c2ecf20Sopenharmony_ci .writeable_reg = fsl_sai_writeable_reg, 9478c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 9488c2ecf20Sopenharmony_ci}; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cistatic int fsl_sai_check_version(struct device *dev) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci struct fsl_sai *sai = dev_get_drvdata(dev); 9538c2ecf20Sopenharmony_ci unsigned char ofs = sai->soc_data->reg_offset; 9548c2ecf20Sopenharmony_ci unsigned int val; 9558c2ecf20Sopenharmony_ci int ret; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (FSL_SAI_TCSR(ofs) == FSL_SAI_VERID) 9588c2ecf20Sopenharmony_ci return 0; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci ret = regmap_read(sai->regmap, FSL_SAI_VERID, &val); 9618c2ecf20Sopenharmony_ci if (ret < 0) 9628c2ecf20Sopenharmony_ci return ret; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci dev_dbg(dev, "VERID: 0x%016X\n", val); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci sai->verid.major = (val & FSL_SAI_VERID_MAJOR_MASK) >> 9678c2ecf20Sopenharmony_ci FSL_SAI_VERID_MAJOR_SHIFT; 9688c2ecf20Sopenharmony_ci sai->verid.minor = (val & FSL_SAI_VERID_MINOR_MASK) >> 9698c2ecf20Sopenharmony_ci FSL_SAI_VERID_MINOR_SHIFT; 9708c2ecf20Sopenharmony_ci sai->verid.feature = val & FSL_SAI_VERID_FEATURE_MASK; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci ret = regmap_read(sai->regmap, FSL_SAI_PARAM, &val); 9738c2ecf20Sopenharmony_ci if (ret < 0) 9748c2ecf20Sopenharmony_ci return ret; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci dev_dbg(dev, "PARAM: 0x%016X\n", val); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci /* Max slots per frame, power of 2 */ 9798c2ecf20Sopenharmony_ci sai->param.slot_num = 1 << 9808c2ecf20Sopenharmony_ci ((val & FSL_SAI_PARAM_SPF_MASK) >> FSL_SAI_PARAM_SPF_SHIFT); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci /* Words per fifo, power of 2 */ 9838c2ecf20Sopenharmony_ci sai->param.fifo_depth = 1 << 9848c2ecf20Sopenharmony_ci ((val & FSL_SAI_PARAM_WPF_MASK) >> FSL_SAI_PARAM_WPF_SHIFT); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci /* Number of datalines implemented */ 9878c2ecf20Sopenharmony_ci sai->param.dataline = val & FSL_SAI_PARAM_DLN_MASK; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci return 0; 9908c2ecf20Sopenharmony_ci} 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_cistatic int fsl_sai_probe(struct platform_device *pdev) 9938c2ecf20Sopenharmony_ci{ 9948c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 9958c2ecf20Sopenharmony_ci struct fsl_sai *sai; 9968c2ecf20Sopenharmony_ci struct regmap *gpr; 9978c2ecf20Sopenharmony_ci struct resource *res; 9988c2ecf20Sopenharmony_ci void __iomem *base; 9998c2ecf20Sopenharmony_ci char tmp[8]; 10008c2ecf20Sopenharmony_ci int irq, ret, i; 10018c2ecf20Sopenharmony_ci int index; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); 10048c2ecf20Sopenharmony_ci if (!sai) 10058c2ecf20Sopenharmony_ci return -ENOMEM; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci sai->pdev = pdev; 10088c2ecf20Sopenharmony_ci sai->soc_data = of_device_get_match_data(&pdev->dev); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci sai->is_lsb_first = of_property_read_bool(np, "lsb-first"); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10138c2ecf20Sopenharmony_ci base = devm_ioremap_resource(&pdev->dev, res); 10148c2ecf20Sopenharmony_ci if (IS_ERR(base)) 10158c2ecf20Sopenharmony_ci return PTR_ERR(base); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci if (sai->soc_data->reg_offset == 8) { 10188c2ecf20Sopenharmony_ci fsl_sai_regmap_config.reg_defaults = fsl_sai_reg_defaults_ofs8; 10198c2ecf20Sopenharmony_ci fsl_sai_regmap_config.max_register = FSL_SAI_MDIV; 10208c2ecf20Sopenharmony_ci fsl_sai_regmap_config.num_reg_defaults = 10218c2ecf20Sopenharmony_ci ARRAY_SIZE(fsl_sai_reg_defaults_ofs8); 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, 10258c2ecf20Sopenharmony_ci "bus", base, &fsl_sai_regmap_config); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci /* Compatible with old DTB cases */ 10288c2ecf20Sopenharmony_ci if (IS_ERR(sai->regmap) && PTR_ERR(sai->regmap) != -EPROBE_DEFER) 10298c2ecf20Sopenharmony_ci sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, 10308c2ecf20Sopenharmony_ci "sai", base, &fsl_sai_regmap_config); 10318c2ecf20Sopenharmony_ci if (IS_ERR(sai->regmap)) { 10328c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "regmap init failed\n"); 10338c2ecf20Sopenharmony_ci return PTR_ERR(sai->regmap); 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci /* No error out for old DTB cases but only mark the clock NULL */ 10378c2ecf20Sopenharmony_ci sai->bus_clk = devm_clk_get(&pdev->dev, "bus"); 10388c2ecf20Sopenharmony_ci if (IS_ERR(sai->bus_clk)) { 10398c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get bus clock: %ld\n", 10408c2ecf20Sopenharmony_ci PTR_ERR(sai->bus_clk)); 10418c2ecf20Sopenharmony_ci sai->bus_clk = NULL; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci sai->mclk_clk[0] = sai->bus_clk; 10458c2ecf20Sopenharmony_ci for (i = 1; i < FSL_SAI_MCLK_MAX; i++) { 10468c2ecf20Sopenharmony_ci sprintf(tmp, "mclk%d", i); 10478c2ecf20Sopenharmony_ci sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp); 10488c2ecf20Sopenharmony_ci if (IS_ERR(sai->mclk_clk[i])) { 10498c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n", 10508c2ecf20Sopenharmony_ci i + 1, PTR_ERR(sai->mclk_clk[i])); 10518c2ecf20Sopenharmony_ci sai->mclk_clk[i] = NULL; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci } 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 10568c2ecf20Sopenharmony_ci if (irq < 0) 10578c2ecf20Sopenharmony_ci return irq; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, fsl_sai_isr, IRQF_SHARED, 10608c2ecf20Sopenharmony_ci np->name, sai); 10618c2ecf20Sopenharmony_ci if (ret) { 10628c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to claim irq %u\n", irq); 10638c2ecf20Sopenharmony_ci return ret; 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci memcpy(&sai->cpu_dai_drv, &fsl_sai_dai_template, 10678c2ecf20Sopenharmony_ci sizeof(fsl_sai_dai_template)); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci /* Sync Tx with Rx as default by following old DT binding */ 10708c2ecf20Sopenharmony_ci sai->synchronous[RX] = true; 10718c2ecf20Sopenharmony_ci sai->synchronous[TX] = false; 10728c2ecf20Sopenharmony_ci sai->cpu_dai_drv.symmetric_rates = 1; 10738c2ecf20Sopenharmony_ci sai->cpu_dai_drv.symmetric_channels = 1; 10748c2ecf20Sopenharmony_ci sai->cpu_dai_drv.symmetric_samplebits = 1; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (of_find_property(np, "fsl,sai-synchronous-rx", NULL) && 10778c2ecf20Sopenharmony_ci of_find_property(np, "fsl,sai-asynchronous", NULL)) { 10788c2ecf20Sopenharmony_ci /* error out if both synchronous and asynchronous are present */ 10798c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "invalid binding for synchronous mode\n"); 10808c2ecf20Sopenharmony_ci return -EINVAL; 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci if (of_find_property(np, "fsl,sai-synchronous-rx", NULL)) { 10848c2ecf20Sopenharmony_ci /* Sync Rx with Tx */ 10858c2ecf20Sopenharmony_ci sai->synchronous[RX] = false; 10868c2ecf20Sopenharmony_ci sai->synchronous[TX] = true; 10878c2ecf20Sopenharmony_ci } else if (of_find_property(np, "fsl,sai-asynchronous", NULL)) { 10888c2ecf20Sopenharmony_ci /* Discard all settings for asynchronous mode */ 10898c2ecf20Sopenharmony_ci sai->synchronous[RX] = false; 10908c2ecf20Sopenharmony_ci sai->synchronous[TX] = false; 10918c2ecf20Sopenharmony_ci sai->cpu_dai_drv.symmetric_rates = 0; 10928c2ecf20Sopenharmony_ci sai->cpu_dai_drv.symmetric_channels = 0; 10938c2ecf20Sopenharmony_ci sai->cpu_dai_drv.symmetric_samplebits = 0; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) && 10978c2ecf20Sopenharmony_ci of_device_is_compatible(np, "fsl,imx6ul-sai")) { 10988c2ecf20Sopenharmony_ci gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr"); 10998c2ecf20Sopenharmony_ci if (IS_ERR(gpr)) { 11008c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot find iomuxc registers\n"); 11018c2ecf20Sopenharmony_ci return PTR_ERR(gpr); 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci index = of_alias_get_id(np, "sai"); 11058c2ecf20Sopenharmony_ci if (index < 0) 11068c2ecf20Sopenharmony_ci return index; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci regmap_update_bits(gpr, IOMUXC_GPR1, MCLK_DIR(index), 11098c2ecf20Sopenharmony_ci MCLK_DIR(index)); 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci sai->dma_params_rx.addr = res->start + FSL_SAI_RDR0; 11138c2ecf20Sopenharmony_ci sai->dma_params_tx.addr = res->start + FSL_SAI_TDR0; 11148c2ecf20Sopenharmony_ci sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; 11158c2ecf20Sopenharmony_ci sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, sai); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci /* Get sai version */ 11208c2ecf20Sopenharmony_ci ret = fsl_sai_check_version(&pdev->dev); 11218c2ecf20Sopenharmony_ci if (ret < 0) 11228c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Error reading SAI version: %d\n", ret); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci /* Select MCLK direction */ 11258c2ecf20Sopenharmony_ci if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) && 11268c2ecf20Sopenharmony_ci sai->verid.major >= 3 && sai->verid.minor >= 1) { 11278c2ecf20Sopenharmony_ci regmap_update_bits(sai->regmap, FSL_SAI_MCTL, 11288c2ecf20Sopenharmony_ci FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN); 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 11328c2ecf20Sopenharmony_ci regcache_cache_only(sai->regmap, true); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component, 11358c2ecf20Sopenharmony_ci &sai->cpu_dai_drv, 1); 11368c2ecf20Sopenharmony_ci if (ret) 11378c2ecf20Sopenharmony_ci goto err_pm_disable; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (sai->soc_data->use_imx_pcm) { 11408c2ecf20Sopenharmony_ci ret = imx_pcm_dma_init(pdev, IMX_SAI_DMABUF_SIZE); 11418c2ecf20Sopenharmony_ci if (ret) 11428c2ecf20Sopenharmony_ci goto err_pm_disable; 11438c2ecf20Sopenharmony_ci } else { 11448c2ecf20Sopenharmony_ci ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 11458c2ecf20Sopenharmony_ci if (ret) 11468c2ecf20Sopenharmony_ci goto err_pm_disable; 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci return ret; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_cierr_pm_disable: 11528c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci return ret; 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_cistatic int fsl_sai_remove(struct platform_device *pdev) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci return 0; 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_cistatic const struct fsl_sai_soc_data fsl_sai_vf610_data = { 11658c2ecf20Sopenharmony_ci .use_imx_pcm = false, 11668c2ecf20Sopenharmony_ci .use_edma = false, 11678c2ecf20Sopenharmony_ci .fifo_depth = 32, 11688c2ecf20Sopenharmony_ci .reg_offset = 0, 11698c2ecf20Sopenharmony_ci}; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_cistatic const struct fsl_sai_soc_data fsl_sai_imx6sx_data = { 11728c2ecf20Sopenharmony_ci .use_imx_pcm = true, 11738c2ecf20Sopenharmony_ci .use_edma = false, 11748c2ecf20Sopenharmony_ci .fifo_depth = 32, 11758c2ecf20Sopenharmony_ci .reg_offset = 0, 11768c2ecf20Sopenharmony_ci}; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_cistatic const struct fsl_sai_soc_data fsl_sai_imx7ulp_data = { 11798c2ecf20Sopenharmony_ci .use_imx_pcm = true, 11808c2ecf20Sopenharmony_ci .use_edma = false, 11818c2ecf20Sopenharmony_ci .fifo_depth = 16, 11828c2ecf20Sopenharmony_ci .reg_offset = 8, 11838c2ecf20Sopenharmony_ci}; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_cistatic const struct fsl_sai_soc_data fsl_sai_imx8mq_data = { 11868c2ecf20Sopenharmony_ci .use_imx_pcm = true, 11878c2ecf20Sopenharmony_ci .use_edma = false, 11888c2ecf20Sopenharmony_ci .fifo_depth = 128, 11898c2ecf20Sopenharmony_ci .reg_offset = 8, 11908c2ecf20Sopenharmony_ci}; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cistatic const struct fsl_sai_soc_data fsl_sai_imx8qm_data = { 11938c2ecf20Sopenharmony_ci .use_imx_pcm = true, 11948c2ecf20Sopenharmony_ci .use_edma = true, 11958c2ecf20Sopenharmony_ci .fifo_depth = 64, 11968c2ecf20Sopenharmony_ci .reg_offset = 0, 11978c2ecf20Sopenharmony_ci}; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic const struct of_device_id fsl_sai_ids[] = { 12008c2ecf20Sopenharmony_ci { .compatible = "fsl,vf610-sai", .data = &fsl_sai_vf610_data }, 12018c2ecf20Sopenharmony_ci { .compatible = "fsl,imx6sx-sai", .data = &fsl_sai_imx6sx_data }, 12028c2ecf20Sopenharmony_ci { .compatible = "fsl,imx6ul-sai", .data = &fsl_sai_imx6sx_data }, 12038c2ecf20Sopenharmony_ci { .compatible = "fsl,imx7ulp-sai", .data = &fsl_sai_imx7ulp_data }, 12048c2ecf20Sopenharmony_ci { .compatible = "fsl,imx8mq-sai", .data = &fsl_sai_imx8mq_data }, 12058c2ecf20Sopenharmony_ci { .compatible = "fsl,imx8qm-sai", .data = &fsl_sai_imx8qm_data }, 12068c2ecf20Sopenharmony_ci { /* sentinel */ } 12078c2ecf20Sopenharmony_ci}; 12088c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_sai_ids); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 12118c2ecf20Sopenharmony_cistatic int fsl_sai_runtime_suspend(struct device *dev) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci struct fsl_sai *sai = dev_get_drvdata(dev); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_CAPTURE)) 12168c2ecf20Sopenharmony_ci clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[0]]); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_PLAYBACK)) 12198c2ecf20Sopenharmony_ci clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[1]]); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci clk_disable_unprepare(sai->bus_clk); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci regcache_cache_only(sai->regmap, true); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci return 0; 12268c2ecf20Sopenharmony_ci} 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_cistatic int fsl_sai_runtime_resume(struct device *dev) 12298c2ecf20Sopenharmony_ci{ 12308c2ecf20Sopenharmony_ci struct fsl_sai *sai = dev_get_drvdata(dev); 12318c2ecf20Sopenharmony_ci unsigned int ofs = sai->soc_data->reg_offset; 12328c2ecf20Sopenharmony_ci int ret; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sai->bus_clk); 12358c2ecf20Sopenharmony_ci if (ret) { 12368c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable bus clock: %d\n", ret); 12378c2ecf20Sopenharmony_ci return ret; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_PLAYBACK)) { 12418c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[1]]); 12428c2ecf20Sopenharmony_ci if (ret) 12438c2ecf20Sopenharmony_ci goto disable_bus_clk; 12448c2ecf20Sopenharmony_ci } 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_CAPTURE)) { 12478c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[0]]); 12488c2ecf20Sopenharmony_ci if (ret) 12498c2ecf20Sopenharmony_ci goto disable_tx_clk; 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci regcache_cache_only(sai->regmap, false); 12538c2ecf20Sopenharmony_ci regcache_mark_dirty(sai->regmap); 12548c2ecf20Sopenharmony_ci regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR); 12558c2ecf20Sopenharmony_ci regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR); 12568c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 12578c2ecf20Sopenharmony_ci regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), 0); 12588c2ecf20Sopenharmony_ci regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), 0); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci ret = regcache_sync(sai->regmap); 12618c2ecf20Sopenharmony_ci if (ret) 12628c2ecf20Sopenharmony_ci goto disable_rx_clk; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci return 0; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cidisable_rx_clk: 12678c2ecf20Sopenharmony_ci if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_CAPTURE)) 12688c2ecf20Sopenharmony_ci clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[0]]); 12698c2ecf20Sopenharmony_cidisable_tx_clk: 12708c2ecf20Sopenharmony_ci if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_PLAYBACK)) 12718c2ecf20Sopenharmony_ci clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[1]]); 12728c2ecf20Sopenharmony_cidisable_bus_clk: 12738c2ecf20Sopenharmony_ci clk_disable_unprepare(sai->bus_clk); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci return ret; 12768c2ecf20Sopenharmony_ci} 12778c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_cistatic const struct dev_pm_ops fsl_sai_pm_ops = { 12808c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(fsl_sai_runtime_suspend, 12818c2ecf20Sopenharmony_ci fsl_sai_runtime_resume, NULL) 12828c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 12838c2ecf20Sopenharmony_ci pm_runtime_force_resume) 12848c2ecf20Sopenharmony_ci}; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_cistatic struct platform_driver fsl_sai_driver = { 12878c2ecf20Sopenharmony_ci .probe = fsl_sai_probe, 12888c2ecf20Sopenharmony_ci .remove = fsl_sai_remove, 12898c2ecf20Sopenharmony_ci .driver = { 12908c2ecf20Sopenharmony_ci .name = "fsl-sai", 12918c2ecf20Sopenharmony_ci .pm = &fsl_sai_pm_ops, 12928c2ecf20Sopenharmony_ci .of_match_table = fsl_sai_ids, 12938c2ecf20Sopenharmony_ci }, 12948c2ecf20Sopenharmony_ci}; 12958c2ecf20Sopenharmony_cimodule_platform_driver(fsl_sai_driver); 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale Soc SAI Interface"); 12988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Xiubo Li, <Li.Xiubo@freescale.com>"); 12998c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:fsl-sai"); 13008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1301