18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Author: Timur Tabi <timur@freescale.com> 68c2ecf20Sopenharmony_ci// 78c2ecf20Sopenharmony_ci// Copyright 2007-2010 Freescale Semiconductor, Inc. 88c2ecf20Sopenharmony_ci// 98c2ecf20Sopenharmony_ci// Some notes why imx-pcm-fiq is used instead of DMA on some boards: 108c2ecf20Sopenharmony_ci// 118c2ecf20Sopenharmony_ci// The i.MX SSI core has some nasty limitations in AC97 mode. While most 128c2ecf20Sopenharmony_ci// sane processor vendors have a FIFO per AC97 slot, the i.MX has only 138c2ecf20Sopenharmony_ci// one FIFO which combines all valid receive slots. We cannot even select 148c2ecf20Sopenharmony_ci// which slots we want to receive. The WM9712 with which this driver 158c2ecf20Sopenharmony_ci// was developed with always sends GPIO status data in slot 12 which 168c2ecf20Sopenharmony_ci// we receive in our (PCM-) data stream. The only chance we have is to 178c2ecf20Sopenharmony_ci// manually skip this data in the FIQ handler. With sampling rates different 188c2ecf20Sopenharmony_ci// from 48000Hz not every frame has valid receive data, so the ratio 198c2ecf20Sopenharmony_ci// between pcm data and GPIO status data changes. Our FIQ handler is not 208c2ecf20Sopenharmony_ci// able to handle this, hence this driver only works with 48000Hz sampling 218c2ecf20Sopenharmony_ci// rate. 228c2ecf20Sopenharmony_ci// Reading and writing AC97 registers is another challenge. The core 238c2ecf20Sopenharmony_ci// provides us status bits when the read register is updated with *another* 248c2ecf20Sopenharmony_ci// value. When we read the same register two times (and the register still 258c2ecf20Sopenharmony_ci// contains the same value) these status bits are not set. We work 268c2ecf20Sopenharmony_ci// around this by not polling these bits but only wait a fixed delay. 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <linux/init.h> 298c2ecf20Sopenharmony_ci#include <linux/io.h> 308c2ecf20Sopenharmony_ci#include <linux/module.h> 318c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 328c2ecf20Sopenharmony_ci#include <linux/clk.h> 338c2ecf20Sopenharmony_ci#include <linux/ctype.h> 348c2ecf20Sopenharmony_ci#include <linux/device.h> 358c2ecf20Sopenharmony_ci#include <linux/delay.h> 368c2ecf20Sopenharmony_ci#include <linux/mutex.h> 378c2ecf20Sopenharmony_ci#include <linux/slab.h> 388c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 398c2ecf20Sopenharmony_ci#include <linux/of.h> 408c2ecf20Sopenharmony_ci#include <linux/of_address.h> 418c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 428c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#include <sound/core.h> 458c2ecf20Sopenharmony_ci#include <sound/pcm.h> 468c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 478c2ecf20Sopenharmony_ci#include <sound/initval.h> 488c2ecf20Sopenharmony_ci#include <sound/soc.h> 498c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include "fsl_ssi.h" 528c2ecf20Sopenharmony_ci#include "imx-pcm.h" 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Define RX and TX to index ssi->regvals array; Can be 0 or 1 only */ 558c2ecf20Sopenharmony_ci#define RX 0 568c2ecf20Sopenharmony_ci#define TX 1 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/** 598c2ecf20Sopenharmony_ci * FSLSSI_I2S_FORMATS: audio formats supported by the SSI 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * The SSI has a limitation in that the samples must be in the same byte 628c2ecf20Sopenharmony_ci * order as the host CPU. This is because when multiple bytes are written 638c2ecf20Sopenharmony_ci * to the STX register, the bytes and bits must be written in the same 648c2ecf20Sopenharmony_ci * order. The STX is a shift register, so all the bits need to be aligned 658c2ecf20Sopenharmony_ci * (bit-endianness must match byte-endianness). Processors typically write 668c2ecf20Sopenharmony_ci * the bits within a byte in the same order that the bytes of a word are 678c2ecf20Sopenharmony_ci * written in. So if the host CPU is big-endian, then only big-endian 688c2ecf20Sopenharmony_ci * samples will be written to STX properly. 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN 718c2ecf20Sopenharmony_ci#define FSLSSI_I2S_FORMATS \ 728c2ecf20Sopenharmony_ci (SNDRV_PCM_FMTBIT_S8 | \ 738c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_BE | \ 748c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S18_3BE | \ 758c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S20_3BE | \ 768c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3BE | \ 778c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_BE) 788c2ecf20Sopenharmony_ci#else 798c2ecf20Sopenharmony_ci#define FSLSSI_I2S_FORMATS \ 808c2ecf20Sopenharmony_ci (SNDRV_PCM_FMTBIT_S8 | \ 818c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | \ 828c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S18_3LE | \ 838c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S20_3LE | \ 848c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | \ 858c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE) 868c2ecf20Sopenharmony_ci#endif 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* 898c2ecf20Sopenharmony_ci * In AC97 mode, TXDIR bit is forced to 0 and TFDIR bit is forced to 1: 908c2ecf20Sopenharmony_ci * - SSI inputs external bit clock and outputs frame sync clock -- CBM_CFS 918c2ecf20Sopenharmony_ci * - Also have NB_NF to mark these two clocks will not be inverted 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci#define FSLSSI_AC97_DAIFMT \ 948c2ecf20Sopenharmony_ci (SND_SOC_DAIFMT_AC97 | \ 958c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBM_CFS | \ 968c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_NB_NF) 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define FSLSSI_SIER_DBG_RX_FLAGS \ 998c2ecf20Sopenharmony_ci (SSI_SIER_RFF0_EN | \ 1008c2ecf20Sopenharmony_ci SSI_SIER_RLS_EN | \ 1018c2ecf20Sopenharmony_ci SSI_SIER_RFS_EN | \ 1028c2ecf20Sopenharmony_ci SSI_SIER_ROE0_EN | \ 1038c2ecf20Sopenharmony_ci SSI_SIER_RFRC_EN) 1048c2ecf20Sopenharmony_ci#define FSLSSI_SIER_DBG_TX_FLAGS \ 1058c2ecf20Sopenharmony_ci (SSI_SIER_TFE0_EN | \ 1068c2ecf20Sopenharmony_ci SSI_SIER_TLS_EN | \ 1078c2ecf20Sopenharmony_ci SSI_SIER_TFS_EN | \ 1088c2ecf20Sopenharmony_ci SSI_SIER_TUE0_EN | \ 1098c2ecf20Sopenharmony_ci SSI_SIER_TFRC_EN) 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cienum fsl_ssi_type { 1128c2ecf20Sopenharmony_ci FSL_SSI_MCP8610, 1138c2ecf20Sopenharmony_ci FSL_SSI_MX21, 1148c2ecf20Sopenharmony_ci FSL_SSI_MX35, 1158c2ecf20Sopenharmony_ci FSL_SSI_MX51, 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistruct fsl_ssi_regvals { 1198c2ecf20Sopenharmony_ci u32 sier; 1208c2ecf20Sopenharmony_ci u32 srcr; 1218c2ecf20Sopenharmony_ci u32 stcr; 1228c2ecf20Sopenharmony_ci u32 scr; 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci switch (reg) { 1288c2ecf20Sopenharmony_ci case REG_SSI_SACCEN: 1298c2ecf20Sopenharmony_ci case REG_SSI_SACCDIS: 1308c2ecf20Sopenharmony_ci return false; 1318c2ecf20Sopenharmony_ci default: 1328c2ecf20Sopenharmony_ci return true; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci switch (reg) { 1398c2ecf20Sopenharmony_ci case REG_SSI_STX0: 1408c2ecf20Sopenharmony_ci case REG_SSI_STX1: 1418c2ecf20Sopenharmony_ci case REG_SSI_SRX0: 1428c2ecf20Sopenharmony_ci case REG_SSI_SRX1: 1438c2ecf20Sopenharmony_ci case REG_SSI_SISR: 1448c2ecf20Sopenharmony_ci case REG_SSI_SFCSR: 1458c2ecf20Sopenharmony_ci case REG_SSI_SACNT: 1468c2ecf20Sopenharmony_ci case REG_SSI_SACADD: 1478c2ecf20Sopenharmony_ci case REG_SSI_SACDAT: 1488c2ecf20Sopenharmony_ci case REG_SSI_SATAG: 1498c2ecf20Sopenharmony_ci case REG_SSI_SACCST: 1508c2ecf20Sopenharmony_ci case REG_SSI_SOR: 1518c2ecf20Sopenharmony_ci return true; 1528c2ecf20Sopenharmony_ci default: 1538c2ecf20Sopenharmony_ci return false; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic bool fsl_ssi_precious_reg(struct device *dev, unsigned int reg) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci switch (reg) { 1608c2ecf20Sopenharmony_ci case REG_SSI_SRX0: 1618c2ecf20Sopenharmony_ci case REG_SSI_SRX1: 1628c2ecf20Sopenharmony_ci case REG_SSI_SISR: 1638c2ecf20Sopenharmony_ci case REG_SSI_SACADD: 1648c2ecf20Sopenharmony_ci case REG_SSI_SACDAT: 1658c2ecf20Sopenharmony_ci case REG_SSI_SATAG: 1668c2ecf20Sopenharmony_ci return true; 1678c2ecf20Sopenharmony_ci default: 1688c2ecf20Sopenharmony_ci return false; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci switch (reg) { 1758c2ecf20Sopenharmony_ci case REG_SSI_SRX0: 1768c2ecf20Sopenharmony_ci case REG_SSI_SRX1: 1778c2ecf20Sopenharmony_ci case REG_SSI_SACCST: 1788c2ecf20Sopenharmony_ci return false; 1798c2ecf20Sopenharmony_ci default: 1808c2ecf20Sopenharmony_ci return true; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic const struct regmap_config fsl_ssi_regconfig = { 1858c2ecf20Sopenharmony_ci .max_register = REG_SSI_SACCDIS, 1868c2ecf20Sopenharmony_ci .reg_bits = 32, 1878c2ecf20Sopenharmony_ci .val_bits = 32, 1888c2ecf20Sopenharmony_ci .reg_stride = 4, 1898c2ecf20Sopenharmony_ci .val_format_endian = REGMAP_ENDIAN_NATIVE, 1908c2ecf20Sopenharmony_ci .num_reg_defaults_raw = REG_SSI_SACCDIS / sizeof(uint32_t) + 1, 1918c2ecf20Sopenharmony_ci .readable_reg = fsl_ssi_readable_reg, 1928c2ecf20Sopenharmony_ci .volatile_reg = fsl_ssi_volatile_reg, 1938c2ecf20Sopenharmony_ci .precious_reg = fsl_ssi_precious_reg, 1948c2ecf20Sopenharmony_ci .writeable_reg = fsl_ssi_writeable_reg, 1958c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistruct fsl_ssi_soc_data { 1998c2ecf20Sopenharmony_ci bool imx; 2008c2ecf20Sopenharmony_ci bool imx21regs; /* imx21-class SSI - no SACC{ST,EN,DIS} regs */ 2018c2ecf20Sopenharmony_ci bool offline_config; 2028c2ecf20Sopenharmony_ci u32 sisr_write_mask; 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/** 2068c2ecf20Sopenharmony_ci * struct fsl_ssi - per-SSI private data 2078c2ecf20Sopenharmony_ci * @regs: Pointer to the regmap registers 2088c2ecf20Sopenharmony_ci * @irq: IRQ of this SSI 2098c2ecf20Sopenharmony_ci * @cpu_dai_drv: CPU DAI driver for this device 2108c2ecf20Sopenharmony_ci * @dai_fmt: DAI configuration this device is currently used with 2118c2ecf20Sopenharmony_ci * @streams: Mask of current active streams: BIT(TX) and BIT(RX) 2128c2ecf20Sopenharmony_ci * @i2s_net: I2S and Network mode configurations of SCR register 2138c2ecf20Sopenharmony_ci * (this is the initial settings based on the DAI format) 2148c2ecf20Sopenharmony_ci * @synchronous: Use synchronous mode - both of TX and RX use STCK and SFCK 2158c2ecf20Sopenharmony_ci * @use_dma: DMA is used or FIQ with stream filter 2168c2ecf20Sopenharmony_ci * @use_dual_fifo: DMA with support for dual FIFO mode 2178c2ecf20Sopenharmony_ci * @has_ipg_clk_name: If "ipg" is in the clock name list of device tree 2188c2ecf20Sopenharmony_ci * @fifo_depth: Depth of the SSI FIFOs 2198c2ecf20Sopenharmony_ci * @slot_width: Width of each DAI slot 2208c2ecf20Sopenharmony_ci * @slots: Number of slots 2218c2ecf20Sopenharmony_ci * @regvals: Specific RX/TX register settings 2228c2ecf20Sopenharmony_ci * @clk: Clock source to access register 2238c2ecf20Sopenharmony_ci * @baudclk: Clock source to generate bit and frame-sync clocks 2248c2ecf20Sopenharmony_ci * @baudclk_streams: Active streams that are using baudclk 2258c2ecf20Sopenharmony_ci * @regcache_sfcsr: Cache sfcsr register value during suspend and resume 2268c2ecf20Sopenharmony_ci * @regcache_sacnt: Cache sacnt register value during suspend and resume 2278c2ecf20Sopenharmony_ci * @dma_params_tx: DMA transmit parameters 2288c2ecf20Sopenharmony_ci * @dma_params_rx: DMA receive parameters 2298c2ecf20Sopenharmony_ci * @ssi_phys: physical address of the SSI registers 2308c2ecf20Sopenharmony_ci * @fiq_params: FIQ stream filtering parameters 2318c2ecf20Sopenharmony_ci * @card_pdev: Platform_device pointer to register a sound card for PowerPC or 2328c2ecf20Sopenharmony_ci * to register a CODEC platform device for AC97 2338c2ecf20Sopenharmony_ci * @card_name: Platform_device name to register a sound card for PowerPC or 2348c2ecf20Sopenharmony_ci * to register a CODEC platform device for AC97 2358c2ecf20Sopenharmony_ci * @card_idx: The index of SSI to register a sound card for PowerPC or 2368c2ecf20Sopenharmony_ci * to register a CODEC platform device for AC97 2378c2ecf20Sopenharmony_ci * @dbg_stats: Debugging statistics 2388c2ecf20Sopenharmony_ci * @soc: SoC specific data 2398c2ecf20Sopenharmony_ci * @dev: Pointer to &pdev->dev 2408c2ecf20Sopenharmony_ci * @fifo_watermark: The FIFO watermark setting. Notifies DMA when there are 2418c2ecf20Sopenharmony_ci * @fifo_watermark or fewer words in TX fifo or 2428c2ecf20Sopenharmony_ci * @fifo_watermark or more empty words in RX fifo. 2438c2ecf20Sopenharmony_ci * @dma_maxburst: Max number of words to transfer in one go. So far, 2448c2ecf20Sopenharmony_ci * this is always the same as fifo_watermark. 2458c2ecf20Sopenharmony_ci * @ac97_reg_lock: Mutex lock to serialize AC97 register access operations 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_cistruct fsl_ssi { 2488c2ecf20Sopenharmony_ci struct regmap *regs; 2498c2ecf20Sopenharmony_ci int irq; 2508c2ecf20Sopenharmony_ci struct snd_soc_dai_driver cpu_dai_drv; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci unsigned int dai_fmt; 2538c2ecf20Sopenharmony_ci u8 streams; 2548c2ecf20Sopenharmony_ci u8 i2s_net; 2558c2ecf20Sopenharmony_ci bool synchronous; 2568c2ecf20Sopenharmony_ci bool use_dma; 2578c2ecf20Sopenharmony_ci bool use_dual_fifo; 2588c2ecf20Sopenharmony_ci bool has_ipg_clk_name; 2598c2ecf20Sopenharmony_ci unsigned int fifo_depth; 2608c2ecf20Sopenharmony_ci unsigned int slot_width; 2618c2ecf20Sopenharmony_ci unsigned int slots; 2628c2ecf20Sopenharmony_ci struct fsl_ssi_regvals regvals[2]; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci struct clk *clk; 2658c2ecf20Sopenharmony_ci struct clk *baudclk; 2668c2ecf20Sopenharmony_ci unsigned int baudclk_streams; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci u32 regcache_sfcsr; 2698c2ecf20Sopenharmony_ci u32 regcache_sacnt; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data dma_params_tx; 2728c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data dma_params_rx; 2738c2ecf20Sopenharmony_ci dma_addr_t ssi_phys; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci struct imx_pcm_fiq_params fiq_params; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci struct platform_device *card_pdev; 2788c2ecf20Sopenharmony_ci char card_name[32]; 2798c2ecf20Sopenharmony_ci u32 card_idx; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci struct fsl_ssi_dbg dbg_stats; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci const struct fsl_ssi_soc_data *soc; 2848c2ecf20Sopenharmony_ci struct device *dev; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci u32 fifo_watermark; 2878c2ecf20Sopenharmony_ci u32 dma_maxburst; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci struct mutex ac97_reg_lock; 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* 2938c2ecf20Sopenharmony_ci * SoC specific data 2948c2ecf20Sopenharmony_ci * 2958c2ecf20Sopenharmony_ci * Notes: 2968c2ecf20Sopenharmony_ci * 1) SSI in earlier SoCS has critical bits in control registers that 2978c2ecf20Sopenharmony_ci * cannot be changed after SSI starts running -- a software reset 2988c2ecf20Sopenharmony_ci * (set SSIEN to 0) is required to change their values. So adding 2998c2ecf20Sopenharmony_ci * an offline_config flag for these SoCs. 3008c2ecf20Sopenharmony_ci * 2) SDMA is available since imx35. However, imx35 does not support 3018c2ecf20Sopenharmony_ci * DMA bits changing when SSI is running, so set offline_config. 3028c2ecf20Sopenharmony_ci * 3) imx51 and later versions support register configurations when 3038c2ecf20Sopenharmony_ci * SSI is running (SSIEN); For these versions, DMA needs to be 3048c2ecf20Sopenharmony_ci * configured before SSI sends DMA request to avoid an undefined 3058c2ecf20Sopenharmony_ci * DMA request on the SDMA side. 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic struct fsl_ssi_soc_data fsl_ssi_mpc8610 = { 3098c2ecf20Sopenharmony_ci .imx = false, 3108c2ecf20Sopenharmony_ci .offline_config = true, 3118c2ecf20Sopenharmony_ci .sisr_write_mask = SSI_SISR_RFRC | SSI_SISR_TFRC | 3128c2ecf20Sopenharmony_ci SSI_SISR_ROE0 | SSI_SISR_ROE1 | 3138c2ecf20Sopenharmony_ci SSI_SISR_TUE0 | SSI_SISR_TUE1, 3148c2ecf20Sopenharmony_ci}; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic struct fsl_ssi_soc_data fsl_ssi_imx21 = { 3178c2ecf20Sopenharmony_ci .imx = true, 3188c2ecf20Sopenharmony_ci .imx21regs = true, 3198c2ecf20Sopenharmony_ci .offline_config = true, 3208c2ecf20Sopenharmony_ci .sisr_write_mask = 0, 3218c2ecf20Sopenharmony_ci}; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic struct fsl_ssi_soc_data fsl_ssi_imx35 = { 3248c2ecf20Sopenharmony_ci .imx = true, 3258c2ecf20Sopenharmony_ci .offline_config = true, 3268c2ecf20Sopenharmony_ci .sisr_write_mask = SSI_SISR_RFRC | SSI_SISR_TFRC | 3278c2ecf20Sopenharmony_ci SSI_SISR_ROE0 | SSI_SISR_ROE1 | 3288c2ecf20Sopenharmony_ci SSI_SISR_TUE0 | SSI_SISR_TUE1, 3298c2ecf20Sopenharmony_ci}; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic struct fsl_ssi_soc_data fsl_ssi_imx51 = { 3328c2ecf20Sopenharmony_ci .imx = true, 3338c2ecf20Sopenharmony_ci .offline_config = false, 3348c2ecf20Sopenharmony_ci .sisr_write_mask = SSI_SISR_ROE0 | SSI_SISR_ROE1 | 3358c2ecf20Sopenharmony_ci SSI_SISR_TUE0 | SSI_SISR_TUE1, 3368c2ecf20Sopenharmony_ci}; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic const struct of_device_id fsl_ssi_ids[] = { 3398c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc8610-ssi", .data = &fsl_ssi_mpc8610 }, 3408c2ecf20Sopenharmony_ci { .compatible = "fsl,imx51-ssi", .data = &fsl_ssi_imx51 }, 3418c2ecf20Sopenharmony_ci { .compatible = "fsl,imx35-ssi", .data = &fsl_ssi_imx35 }, 3428c2ecf20Sopenharmony_ci { .compatible = "fsl,imx21-ssi", .data = &fsl_ssi_imx21 }, 3438c2ecf20Sopenharmony_ci {} 3448c2ecf20Sopenharmony_ci}; 3458c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_ssi_ids); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic bool fsl_ssi_is_ac97(struct fsl_ssi *ssi) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci return (ssi->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) == 3508c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_AC97; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic bool fsl_ssi_is_i2s_master(struct fsl_ssi *ssi) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci return (ssi->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == 3568c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBS_CFS; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic bool fsl_ssi_is_i2s_cbm_cfs(struct fsl_ssi *ssi) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci return (ssi->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == 3628c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBM_CFS; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci/** 3668c2ecf20Sopenharmony_ci * fsl_ssi_irq - Interrupt handler to gather states 3678c2ecf20Sopenharmony_ci * @irq: irq number 3688c2ecf20Sopenharmony_ci * @dev_id: context 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_cistatic irqreturn_t fsl_ssi_isr(int irq, void *dev_id) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct fsl_ssi *ssi = dev_id; 3738c2ecf20Sopenharmony_ci struct regmap *regs = ssi->regs; 3748c2ecf20Sopenharmony_ci u32 sisr, sisr2; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci regmap_read(regs, REG_SSI_SISR, &sisr); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci sisr2 = sisr & ssi->soc->sisr_write_mask; 3798c2ecf20Sopenharmony_ci /* Clear the bits that we set */ 3808c2ecf20Sopenharmony_ci if (sisr2) 3818c2ecf20Sopenharmony_ci regmap_write(regs, REG_SSI_SISR, sisr2); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci fsl_ssi_dbg_isr(&ssi->dbg_stats, sisr); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci/** 3898c2ecf20Sopenharmony_ci * fsl_ssi_config_enable - Set SCR, SIER, STCR and SRCR registers with 3908c2ecf20Sopenharmony_ci * cached values in regvals 3918c2ecf20Sopenharmony_ci * @ssi: SSI context 3928c2ecf20Sopenharmony_ci * @tx: direction 3938c2ecf20Sopenharmony_ci * 3948c2ecf20Sopenharmony_ci * Notes: 3958c2ecf20Sopenharmony_ci * 1) For offline_config SoCs, enable all necessary bits of both streams 3968c2ecf20Sopenharmony_ci * when 1st stream starts, even if the opposite stream will not start 3978c2ecf20Sopenharmony_ci * 2) It also clears FIFO before setting regvals; SOR is safe to set online 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_cistatic void fsl_ssi_config_enable(struct fsl_ssi *ssi, bool tx) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct fsl_ssi_regvals *vals = ssi->regvals; 4028c2ecf20Sopenharmony_ci int dir = tx ? TX : RX; 4038c2ecf20Sopenharmony_ci u32 sier, srcr, stcr; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* Clear dirty data in the FIFO; It also prevents channel slipping */ 4068c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SOR, 4078c2ecf20Sopenharmony_ci SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx)); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* 4108c2ecf20Sopenharmony_ci * On offline_config SoCs, SxCR and SIER are already configured when 4118c2ecf20Sopenharmony_ci * the previous stream started. So skip all SxCR and SIER settings 4128c2ecf20Sopenharmony_ci * to prevent online reconfigurations, then jump to set SCR directly 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_ci if (ssi->soc->offline_config && ssi->streams) 4158c2ecf20Sopenharmony_ci goto enable_scr; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (ssi->soc->offline_config) { 4188c2ecf20Sopenharmony_ci /* 4198c2ecf20Sopenharmony_ci * Online reconfiguration not supported, so enable all bits for 4208c2ecf20Sopenharmony_ci * both streams at once to avoid necessity of reconfigurations 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_ci srcr = vals[RX].srcr | vals[TX].srcr; 4238c2ecf20Sopenharmony_ci stcr = vals[RX].stcr | vals[TX].stcr; 4248c2ecf20Sopenharmony_ci sier = vals[RX].sier | vals[TX].sier; 4258c2ecf20Sopenharmony_ci } else { 4268c2ecf20Sopenharmony_ci /* Otherwise, only set bits for the current stream */ 4278c2ecf20Sopenharmony_ci srcr = vals[dir].srcr; 4288c2ecf20Sopenharmony_ci stcr = vals[dir].stcr; 4298c2ecf20Sopenharmony_ci sier = vals[dir].sier; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* Configure SRCR, STCR and SIER at once */ 4338c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, srcr); 4348c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, stcr); 4358c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, sier); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cienable_scr: 4388c2ecf20Sopenharmony_ci /* 4398c2ecf20Sopenharmony_ci * Start DMA before setting TE to avoid FIFO underrun 4408c2ecf20Sopenharmony_ci * which may cause a channel slip or a channel swap 4418c2ecf20Sopenharmony_ci * 4428c2ecf20Sopenharmony_ci * TODO: FIQ cases might also need this upon testing 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_ci if (ssi->use_dma && tx) { 4458c2ecf20Sopenharmony_ci int try = 100; 4468c2ecf20Sopenharmony_ci u32 sfcsr; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Enable SSI first to send TX DMA request */ 4498c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SCR, 4508c2ecf20Sopenharmony_ci SSI_SCR_SSIEN, SSI_SCR_SSIEN); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* Busy wait until TX FIFO not empty -- DMA working */ 4538c2ecf20Sopenharmony_ci do { 4548c2ecf20Sopenharmony_ci regmap_read(ssi->regs, REG_SSI_SFCSR, &sfcsr); 4558c2ecf20Sopenharmony_ci if (SSI_SFCSR_TFCNT0(sfcsr)) 4568c2ecf20Sopenharmony_ci break; 4578c2ecf20Sopenharmony_ci } while (--try); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* FIFO still empty -- something might be wrong */ 4608c2ecf20Sopenharmony_ci if (!SSI_SFCSR_TFCNT0(sfcsr)) 4618c2ecf20Sopenharmony_ci dev_warn(ssi->dev, "Timeout waiting TX FIFO filling\n"); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci /* Enable all remaining bits in SCR */ 4648c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SCR, 4658c2ecf20Sopenharmony_ci vals[dir].scr, vals[dir].scr); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* Log the enabled stream to the mask */ 4688c2ecf20Sopenharmony_ci ssi->streams |= BIT(dir); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci/* 4728c2ecf20Sopenharmony_ci * Exclude bits that are used by the opposite stream 4738c2ecf20Sopenharmony_ci * 4748c2ecf20Sopenharmony_ci * When both streams are active, disabling some bits for the current stream 4758c2ecf20Sopenharmony_ci * might break the other stream if these bits are used by it. 4768c2ecf20Sopenharmony_ci * 4778c2ecf20Sopenharmony_ci * @vals : regvals of the current stream 4788c2ecf20Sopenharmony_ci * @avals: regvals of the opposite stream 4798c2ecf20Sopenharmony_ci * @aactive: active state of the opposite stream 4808c2ecf20Sopenharmony_ci * 4818c2ecf20Sopenharmony_ci * 1) XOR vals and avals to get the differences if the other stream is active; 4828c2ecf20Sopenharmony_ci * Otherwise, return current vals if the other stream is not active 4838c2ecf20Sopenharmony_ci * 2) AND the result of 1) with the current vals 4848c2ecf20Sopenharmony_ci */ 4858c2ecf20Sopenharmony_ci#define _ssi_xor_shared_bits(vals, avals, aactive) \ 4868c2ecf20Sopenharmony_ci ((vals) ^ ((avals) * (aactive))) 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci#define ssi_excl_shared_bits(vals, avals, aactive) \ 4898c2ecf20Sopenharmony_ci ((vals) & _ssi_xor_shared_bits(vals, avals, aactive)) 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci/** 4928c2ecf20Sopenharmony_ci * fsl_ssi_config_disable - Unset SCR, SIER, STCR and SRCR registers 4938c2ecf20Sopenharmony_ci * with cached values in regvals 4948c2ecf20Sopenharmony_ci * @ssi: SSI context 4958c2ecf20Sopenharmony_ci * @tx: direction 4968c2ecf20Sopenharmony_ci * 4978c2ecf20Sopenharmony_ci * Notes: 4988c2ecf20Sopenharmony_ci * 1) For offline_config SoCs, to avoid online reconfigurations, disable all 4998c2ecf20Sopenharmony_ci * bits of both streams at once when the last stream is abort to end 5008c2ecf20Sopenharmony_ci * 2) It also clears FIFO after unsetting regvals; SOR is safe to set online 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_cistatic void fsl_ssi_config_disable(struct fsl_ssi *ssi, bool tx) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct fsl_ssi_regvals *vals, *avals; 5058c2ecf20Sopenharmony_ci u32 sier, srcr, stcr, scr; 5068c2ecf20Sopenharmony_ci int adir = tx ? RX : TX; 5078c2ecf20Sopenharmony_ci int dir = tx ? TX : RX; 5088c2ecf20Sopenharmony_ci bool aactive; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* Check if the opposite stream is active */ 5118c2ecf20Sopenharmony_ci aactive = ssi->streams & BIT(adir); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci vals = &ssi->regvals[dir]; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* Get regvals of the opposite stream to keep opposite stream safe */ 5168c2ecf20Sopenharmony_ci avals = &ssi->regvals[adir]; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* 5198c2ecf20Sopenharmony_ci * To keep the other stream safe, exclude shared bits between 5208c2ecf20Sopenharmony_ci * both streams, and get safe bits to disable current stream 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci scr = ssi_excl_shared_bits(vals->scr, avals->scr, aactive); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* Disable safe bits of SCR register for the current stream */ 5258c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SCR, scr, 0); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* Log the disabled stream to the mask */ 5288c2ecf20Sopenharmony_ci ssi->streams &= ~BIT(dir); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* 5318c2ecf20Sopenharmony_ci * On offline_config SoCs, if the other stream is active, skip 5328c2ecf20Sopenharmony_ci * SxCR and SIER settings to prevent online reconfigurations 5338c2ecf20Sopenharmony_ci */ 5348c2ecf20Sopenharmony_ci if (ssi->soc->offline_config && aactive) 5358c2ecf20Sopenharmony_ci goto fifo_clear; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (ssi->soc->offline_config) { 5388c2ecf20Sopenharmony_ci /* Now there is only current stream active, disable all bits */ 5398c2ecf20Sopenharmony_ci srcr = vals->srcr | avals->srcr; 5408c2ecf20Sopenharmony_ci stcr = vals->stcr | avals->stcr; 5418c2ecf20Sopenharmony_ci sier = vals->sier | avals->sier; 5428c2ecf20Sopenharmony_ci } else { 5438c2ecf20Sopenharmony_ci /* 5448c2ecf20Sopenharmony_ci * To keep the other stream safe, exclude shared bits between 5458c2ecf20Sopenharmony_ci * both streams, and get safe bits to disable current stream 5468c2ecf20Sopenharmony_ci */ 5478c2ecf20Sopenharmony_ci sier = ssi_excl_shared_bits(vals->sier, avals->sier, aactive); 5488c2ecf20Sopenharmony_ci srcr = ssi_excl_shared_bits(vals->srcr, avals->srcr, aactive); 5498c2ecf20Sopenharmony_ci stcr = ssi_excl_shared_bits(vals->stcr, avals->stcr, aactive); 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* Clear configurations of SRCR, STCR and SIER at once */ 5538c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, 0); 5548c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, 0); 5558c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, 0); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cififo_clear: 5588c2ecf20Sopenharmony_ci /* Clear remaining data in the FIFO */ 5598c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SOR, 5608c2ecf20Sopenharmony_ci SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx)); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct regmap *regs = ssi->regs; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* no SACC{ST,EN,DIS} regs on imx21-class SSI */ 5688c2ecf20Sopenharmony_ci if (!ssi->soc->imx21regs) { 5698c2ecf20Sopenharmony_ci /* Disable all channel slots */ 5708c2ecf20Sopenharmony_ci regmap_write(regs, REG_SSI_SACCDIS, 0xff); 5718c2ecf20Sopenharmony_ci /* Enable slots 3 & 4 -- PCM Playback Left & Right channels */ 5728c2ecf20Sopenharmony_ci regmap_write(regs, REG_SSI_SACCEN, 0x300); 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci/** 5778c2ecf20Sopenharmony_ci * fsl_ssi_setup_regvals - Cache critical bits of SIER, SRCR, STCR and 5788c2ecf20Sopenharmony_ci * SCR to later set them safely 5798c2ecf20Sopenharmony_ci * @ssi: SSI context 5808c2ecf20Sopenharmony_ci */ 5818c2ecf20Sopenharmony_cistatic void fsl_ssi_setup_regvals(struct fsl_ssi *ssi) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci struct fsl_ssi_regvals *vals = ssi->regvals; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci vals[RX].sier = SSI_SIER_RFF0_EN | FSLSSI_SIER_DBG_RX_FLAGS; 5868c2ecf20Sopenharmony_ci vals[RX].srcr = SSI_SRCR_RFEN0; 5878c2ecf20Sopenharmony_ci vals[RX].scr = SSI_SCR_SSIEN | SSI_SCR_RE; 5888c2ecf20Sopenharmony_ci vals[TX].sier = SSI_SIER_TFE0_EN | FSLSSI_SIER_DBG_TX_FLAGS; 5898c2ecf20Sopenharmony_ci vals[TX].stcr = SSI_STCR_TFEN0; 5908c2ecf20Sopenharmony_ci vals[TX].scr = SSI_SCR_SSIEN | SSI_SCR_TE; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* AC97 has already enabled SSIEN, RE and TE, so ignore them */ 5938c2ecf20Sopenharmony_ci if (fsl_ssi_is_ac97(ssi)) 5948c2ecf20Sopenharmony_ci vals[RX].scr = vals[TX].scr = 0; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (ssi->use_dual_fifo) { 5978c2ecf20Sopenharmony_ci vals[RX].srcr |= SSI_SRCR_RFEN1; 5988c2ecf20Sopenharmony_ci vals[TX].stcr |= SSI_STCR_TFEN1; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (ssi->use_dma) { 6028c2ecf20Sopenharmony_ci vals[RX].sier |= SSI_SIER_RDMAE; 6038c2ecf20Sopenharmony_ci vals[TX].sier |= SSI_SIER_TDMAE; 6048c2ecf20Sopenharmony_ci } else { 6058c2ecf20Sopenharmony_ci vals[RX].sier |= SSI_SIER_RIE; 6068c2ecf20Sopenharmony_ci vals[TX].sier |= SSI_SIER_TIE; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic void fsl_ssi_setup_ac97(struct fsl_ssi *ssi) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct regmap *regs = ssi->regs; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci /* Setup the clock control register */ 6158c2ecf20Sopenharmony_ci regmap_write(regs, REG_SSI_STCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13)); 6168c2ecf20Sopenharmony_ci regmap_write(regs, REG_SSI_SRCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13)); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci /* Enable AC97 mode and startup the SSI */ 6198c2ecf20Sopenharmony_ci regmap_write(regs, REG_SSI_SACNT, SSI_SACNT_AC97EN | SSI_SACNT_FV); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci /* AC97 has to communicate with codec before starting a stream */ 6228c2ecf20Sopenharmony_ci regmap_update_bits(regs, REG_SSI_SCR, 6238c2ecf20Sopenharmony_ci SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE, 6248c2ecf20Sopenharmony_ci SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci regmap_write(regs, REG_SSI_SOR, SSI_SOR_WAIT(3)); 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic int fsl_ssi_startup(struct snd_pcm_substream *substream, 6308c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 6338c2ecf20Sopenharmony_ci struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 6348c2ecf20Sopenharmony_ci int ret; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ssi->clk); 6378c2ecf20Sopenharmony_ci if (ret) 6388c2ecf20Sopenharmony_ci return ret; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* 6418c2ecf20Sopenharmony_ci * When using dual fifo mode, it is safer to ensure an even period 6428c2ecf20Sopenharmony_ci * size. If appearing to an odd number while DMA always starts its 6438c2ecf20Sopenharmony_ci * task from fifo0, fifo1 would be neglected at the end of each 6448c2ecf20Sopenharmony_ci * period. But SSI would still access fifo1 with an invalid data. 6458c2ecf20Sopenharmony_ci */ 6468c2ecf20Sopenharmony_ci if (ssi->use_dual_fifo) 6478c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_step(substream->runtime, 0, 6488c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci return 0; 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic void fsl_ssi_shutdown(struct snd_pcm_substream *substream, 6548c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 6578c2ecf20Sopenharmony_ci struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci clk_disable_unprepare(ssi->clk); 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci/** 6638c2ecf20Sopenharmony_ci * fsl_ssi_set_bclk - Configure Digital Audio Interface bit clock 6648c2ecf20Sopenharmony_ci * @substream: ASoC substream 6658c2ecf20Sopenharmony_ci * @dai: pointer to DAI 6668c2ecf20Sopenharmony_ci * @hw_params: pointers to hw_params 6678c2ecf20Sopenharmony_ci * 6688c2ecf20Sopenharmony_ci * Notes: This function can be only called when using SSI as DAI master 6698c2ecf20Sopenharmony_ci * 6708c2ecf20Sopenharmony_ci * Quick instruction for parameters: 6718c2ecf20Sopenharmony_ci * freq: Output BCLK frequency = samplerate * slots * slot_width 6728c2ecf20Sopenharmony_ci * (In 2-channel I2S Master mode, slot_width is fixed 32) 6738c2ecf20Sopenharmony_ci */ 6748c2ecf20Sopenharmony_cistatic int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, 6758c2ecf20Sopenharmony_ci struct snd_soc_dai *dai, 6768c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 6798c2ecf20Sopenharmony_ci struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); 6808c2ecf20Sopenharmony_ci struct regmap *regs = ssi->regs; 6818c2ecf20Sopenharmony_ci u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i; 6828c2ecf20Sopenharmony_ci unsigned long clkrate, baudrate, tmprate; 6838c2ecf20Sopenharmony_ci unsigned int channels = params_channels(hw_params); 6848c2ecf20Sopenharmony_ci unsigned int slot_width = params_width(hw_params); 6858c2ecf20Sopenharmony_ci unsigned int slots = 2; 6868c2ecf20Sopenharmony_ci u64 sub, savesub = 100000; 6878c2ecf20Sopenharmony_ci unsigned int freq; 6888c2ecf20Sopenharmony_ci bool baudclk_is_used; 6898c2ecf20Sopenharmony_ci int ret; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* Override slots and slot_width if being specifically set... */ 6928c2ecf20Sopenharmony_ci if (ssi->slots) 6938c2ecf20Sopenharmony_ci slots = ssi->slots; 6948c2ecf20Sopenharmony_ci if (ssi->slot_width) 6958c2ecf20Sopenharmony_ci slot_width = ssi->slot_width; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* ...but force 32 bits for stereo audio using I2S Master Mode */ 6988c2ecf20Sopenharmony_ci if (channels == 2 && 6998c2ecf20Sopenharmony_ci (ssi->i2s_net & SSI_SCR_I2S_MODE_MASK) == SSI_SCR_I2S_MODE_MASTER) 7008c2ecf20Sopenharmony_ci slot_width = 32; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* Generate bit clock based on the slot number and slot width */ 7038c2ecf20Sopenharmony_ci freq = slots * slot_width * params_rate(hw_params); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* Don't apply it to any non-baudclk circumstance */ 7068c2ecf20Sopenharmony_ci if (IS_ERR(ssi->baudclk)) 7078c2ecf20Sopenharmony_ci return -EINVAL; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* 7108c2ecf20Sopenharmony_ci * Hardware limitation: The bclk rate must be 7118c2ecf20Sopenharmony_ci * never greater than 1/5 IPG clock rate 7128c2ecf20Sopenharmony_ci */ 7138c2ecf20Sopenharmony_ci if (freq * 5 > clk_get_rate(ssi->clk)) { 7148c2ecf20Sopenharmony_ci dev_err(dai->dev, "bitclk > ipgclk / 5\n"); 7158c2ecf20Sopenharmony_ci return -EINVAL; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci baudclk_is_used = ssi->baudclk_streams & ~(BIT(substream->stream)); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* It should be already enough to divide clock by setting pm alone */ 7218c2ecf20Sopenharmony_ci psr = 0; 7228c2ecf20Sopenharmony_ci div2 = 0; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci factor = (div2 + 1) * (7 * psr + 1) * 2; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci for (i = 0; i < 255; i++) { 7278c2ecf20Sopenharmony_ci tmprate = freq * factor * (i + 1); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (baudclk_is_used) 7308c2ecf20Sopenharmony_ci clkrate = clk_get_rate(ssi->baudclk); 7318c2ecf20Sopenharmony_ci else 7328c2ecf20Sopenharmony_ci clkrate = clk_round_rate(ssi->baudclk, tmprate); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci clkrate /= factor; 7358c2ecf20Sopenharmony_ci afreq = clkrate / (i + 1); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci if (freq == afreq) 7388c2ecf20Sopenharmony_ci sub = 0; 7398c2ecf20Sopenharmony_ci else if (freq / afreq == 1) 7408c2ecf20Sopenharmony_ci sub = freq - afreq; 7418c2ecf20Sopenharmony_ci else if (afreq / freq == 1) 7428c2ecf20Sopenharmony_ci sub = afreq - freq; 7438c2ecf20Sopenharmony_ci else 7448c2ecf20Sopenharmony_ci continue; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci /* Calculate the fraction */ 7478c2ecf20Sopenharmony_ci sub *= 100000; 7488c2ecf20Sopenharmony_ci do_div(sub, freq); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (sub < savesub && !(i == 0 && psr == 0 && div2 == 0)) { 7518c2ecf20Sopenharmony_ci baudrate = tmprate; 7528c2ecf20Sopenharmony_ci savesub = sub; 7538c2ecf20Sopenharmony_ci pm = i; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* We are lucky */ 7578c2ecf20Sopenharmony_ci if (savesub == 0) 7588c2ecf20Sopenharmony_ci break; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* No proper pm found if it is still remaining the initial value */ 7628c2ecf20Sopenharmony_ci if (pm == 999) { 7638c2ecf20Sopenharmony_ci dev_err(dai->dev, "failed to handle the required sysclk\n"); 7648c2ecf20Sopenharmony_ci return -EINVAL; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci stccr = SSI_SxCCR_PM(pm + 1) | (div2 ? SSI_SxCCR_DIV2 : 0) | 7688c2ecf20Sopenharmony_ci (psr ? SSI_SxCCR_PSR : 0); 7698c2ecf20Sopenharmony_ci mask = SSI_SxCCR_PM_MASK | SSI_SxCCR_DIV2 | SSI_SxCCR_PSR; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* STCCR is used for RX in synchronous mode */ 7728c2ecf20Sopenharmony_ci tx2 = tx || ssi->synchronous; 7738c2ecf20Sopenharmony_ci regmap_update_bits(regs, REG_SSI_SxCCR(tx2), mask, stccr); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (!baudclk_is_used) { 7768c2ecf20Sopenharmony_ci ret = clk_set_rate(ssi->baudclk, baudrate); 7778c2ecf20Sopenharmony_ci if (ret) { 7788c2ecf20Sopenharmony_ci dev_err(dai->dev, "failed to set baudclk rate\n"); 7798c2ecf20Sopenharmony_ci return -EINVAL; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci return 0; 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci/** 7878c2ecf20Sopenharmony_ci * fsl_ssi_hw_params - Configure SSI based on PCM hardware parameters 7888c2ecf20Sopenharmony_ci * @substream: ASoC substream 7898c2ecf20Sopenharmony_ci * @hw_params: pointers to hw_params 7908c2ecf20Sopenharmony_ci * @dai: pointer to DAI 7918c2ecf20Sopenharmony_ci * 7928c2ecf20Sopenharmony_ci * Notes: 7938c2ecf20Sopenharmony_ci * 1) SxCCR.WL bits are critical bits that require SSI to be temporarily 7948c2ecf20Sopenharmony_ci * disabled on offline_config SoCs. Even for online configurable SoCs 7958c2ecf20Sopenharmony_ci * running in synchronous mode (both TX and RX use STCCR), it is not 7968c2ecf20Sopenharmony_ci * safe to re-configure them when both two streams start running. 7978c2ecf20Sopenharmony_ci * 2) SxCCR.PM, SxCCR.DIV2 and SxCCR.PSR bits will be configured in the 7988c2ecf20Sopenharmony_ci * fsl_ssi_set_bclk() if SSI is the DAI clock master. 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_cistatic int fsl_ssi_hw_params(struct snd_pcm_substream *substream, 8018c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params, 8028c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 8058c2ecf20Sopenharmony_ci struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); 8068c2ecf20Sopenharmony_ci struct regmap *regs = ssi->regs; 8078c2ecf20Sopenharmony_ci unsigned int channels = params_channels(hw_params); 8088c2ecf20Sopenharmony_ci unsigned int sample_size = params_width(hw_params); 8098c2ecf20Sopenharmony_ci u32 wl = SSI_SxCCR_WL(sample_size); 8108c2ecf20Sopenharmony_ci int ret; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (fsl_ssi_is_i2s_master(ssi)) { 8138c2ecf20Sopenharmony_ci ret = fsl_ssi_set_bclk(substream, dai, hw_params); 8148c2ecf20Sopenharmony_ci if (ret) 8158c2ecf20Sopenharmony_ci return ret; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci /* Do not enable the clock if it is already enabled */ 8188c2ecf20Sopenharmony_ci if (!(ssi->baudclk_streams & BIT(substream->stream))) { 8198c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ssi->baudclk); 8208c2ecf20Sopenharmony_ci if (ret) 8218c2ecf20Sopenharmony_ci return ret; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci ssi->baudclk_streams |= BIT(substream->stream); 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci /* 8288c2ecf20Sopenharmony_ci * SSI is properly configured if it is enabled and running in 8298c2ecf20Sopenharmony_ci * the synchronous mode; Note that AC97 mode is an exception 8308c2ecf20Sopenharmony_ci * that should set separate configurations for STCCR and SRCCR 8318c2ecf20Sopenharmony_ci * despite running in the synchronous mode. 8328c2ecf20Sopenharmony_ci */ 8338c2ecf20Sopenharmony_ci if (ssi->streams && ssi->synchronous) 8348c2ecf20Sopenharmony_ci return 0; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (!fsl_ssi_is_ac97(ssi)) { 8378c2ecf20Sopenharmony_ci /* 8388c2ecf20Sopenharmony_ci * Keep the ssi->i2s_net intact while having a local variable 8398c2ecf20Sopenharmony_ci * to override settings for special use cases. Otherwise, the 8408c2ecf20Sopenharmony_ci * ssi->i2s_net will lose the settings for regular use cases. 8418c2ecf20Sopenharmony_ci */ 8428c2ecf20Sopenharmony_ci u8 i2s_net = ssi->i2s_net; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* Normal + Network mode to send 16-bit data in 32-bit frames */ 8458c2ecf20Sopenharmony_ci if (fsl_ssi_is_i2s_cbm_cfs(ssi) && sample_size == 16) 8468c2ecf20Sopenharmony_ci i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* Use Normal mode to send mono data at 1st slot of 2 slots */ 8498c2ecf20Sopenharmony_ci if (channels == 1) 8508c2ecf20Sopenharmony_ci i2s_net = SSI_SCR_I2S_MODE_NORMAL; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci regmap_update_bits(regs, REG_SSI_SCR, 8538c2ecf20Sopenharmony_ci SSI_SCR_I2S_NET_MASK, i2s_net); 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci /* In synchronous mode, the SSI uses STCCR for capture */ 8578c2ecf20Sopenharmony_ci tx2 = tx || ssi->synchronous; 8588c2ecf20Sopenharmony_ci regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci return 0; 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic int fsl_ssi_hw_free(struct snd_pcm_substream *substream, 8648c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 8678c2ecf20Sopenharmony_ci struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (fsl_ssi_is_i2s_master(ssi) && 8708c2ecf20Sopenharmony_ci ssi->baudclk_streams & BIT(substream->stream)) { 8718c2ecf20Sopenharmony_ci clk_disable_unprepare(ssi->baudclk); 8728c2ecf20Sopenharmony_ci ssi->baudclk_streams &= ~BIT(substream->stream); 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci return 0; 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_cistatic int _fsl_ssi_set_dai_fmt(struct fsl_ssi *ssi, unsigned int fmt) 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci u32 strcr = 0, scr = 0, stcr, srcr, mask; 8818c2ecf20Sopenharmony_ci unsigned int slots; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci ssi->dai_fmt = fmt; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci /* Synchronize frame sync clock for TE to avoid data slipping */ 8868c2ecf20Sopenharmony_ci scr |= SSI_SCR_SYNC_TX_FS; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci /* Set to default shifting settings: LSB_ALIGNED */ 8898c2ecf20Sopenharmony_ci strcr |= SSI_STCR_TXBIT0; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci /* Use Network mode as default */ 8928c2ecf20Sopenharmony_ci ssi->i2s_net = SSI_SCR_NET; 8938c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 8948c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 8958c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 8968c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 8978c2ecf20Sopenharmony_ci if (IS_ERR(ssi->baudclk)) { 8988c2ecf20Sopenharmony_ci dev_err(ssi->dev, 8998c2ecf20Sopenharmony_ci "missing baudclk for master mode\n"); 9008c2ecf20Sopenharmony_ci return -EINVAL; 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci fallthrough; 9038c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 9048c2ecf20Sopenharmony_ci ssi->i2s_net |= SSI_SCR_I2S_MODE_MASTER; 9058c2ecf20Sopenharmony_ci break; 9068c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 9078c2ecf20Sopenharmony_ci ssi->i2s_net |= SSI_SCR_I2S_MODE_SLAVE; 9088c2ecf20Sopenharmony_ci break; 9098c2ecf20Sopenharmony_ci default: 9108c2ecf20Sopenharmony_ci return -EINVAL; 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci slots = ssi->slots ? : 2; 9148c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_STCCR, 9158c2ecf20Sopenharmony_ci SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots)); 9168c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SRCCR, 9178c2ecf20Sopenharmony_ci SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots)); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci /* Data on rising edge of bclk, frame low, 1clk before data */ 9208c2ecf20Sopenharmony_ci strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP | SSI_STCR_TEFS; 9218c2ecf20Sopenharmony_ci break; 9228c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 9238c2ecf20Sopenharmony_ci /* Data on rising edge of bclk, frame high */ 9248c2ecf20Sopenharmony_ci strcr |= SSI_STCR_TSCKP; 9258c2ecf20Sopenharmony_ci break; 9268c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 9278c2ecf20Sopenharmony_ci /* Data on rising edge of bclk, frame high, 1clk before data */ 9288c2ecf20Sopenharmony_ci strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | SSI_STCR_TEFS; 9298c2ecf20Sopenharmony_ci break; 9308c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 9318c2ecf20Sopenharmony_ci /* Data on rising edge of bclk, frame high */ 9328c2ecf20Sopenharmony_ci strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP; 9338c2ecf20Sopenharmony_ci break; 9348c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_AC97: 9358c2ecf20Sopenharmony_ci /* Data on falling edge of bclk, frame high, 1clk before data */ 9368c2ecf20Sopenharmony_ci strcr |= SSI_STCR_TEFS; 9378c2ecf20Sopenharmony_ci break; 9388c2ecf20Sopenharmony_ci default: 9398c2ecf20Sopenharmony_ci return -EINVAL; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci scr |= ssi->i2s_net; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* DAI clock inversion */ 9458c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 9468c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 9478c2ecf20Sopenharmony_ci /* Nothing to do for both normal cases */ 9488c2ecf20Sopenharmony_ci break; 9498c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 9508c2ecf20Sopenharmony_ci /* Invert bit clock */ 9518c2ecf20Sopenharmony_ci strcr ^= SSI_STCR_TSCKP; 9528c2ecf20Sopenharmony_ci break; 9538c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 9548c2ecf20Sopenharmony_ci /* Invert frame clock */ 9558c2ecf20Sopenharmony_ci strcr ^= SSI_STCR_TFSI; 9568c2ecf20Sopenharmony_ci break; 9578c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 9588c2ecf20Sopenharmony_ci /* Invert both clocks */ 9598c2ecf20Sopenharmony_ci strcr ^= SSI_STCR_TSCKP; 9608c2ecf20Sopenharmony_ci strcr ^= SSI_STCR_TFSI; 9618c2ecf20Sopenharmony_ci break; 9628c2ecf20Sopenharmony_ci default: 9638c2ecf20Sopenharmony_ci return -EINVAL; 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci /* DAI clock master masks */ 9678c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 9688c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 9698c2ecf20Sopenharmony_ci /* Output bit and frame sync clocks */ 9708c2ecf20Sopenharmony_ci strcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR; 9718c2ecf20Sopenharmony_ci scr |= SSI_SCR_SYS_CLK_EN; 9728c2ecf20Sopenharmony_ci break; 9738c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 9748c2ecf20Sopenharmony_ci /* Input bit or frame sync clocks */ 9758c2ecf20Sopenharmony_ci break; 9768c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 9778c2ecf20Sopenharmony_ci /* Input bit clock but output frame sync clock */ 9788c2ecf20Sopenharmony_ci strcr |= SSI_STCR_TFDIR; 9798c2ecf20Sopenharmony_ci break; 9808c2ecf20Sopenharmony_ci default: 9818c2ecf20Sopenharmony_ci return -EINVAL; 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci stcr = strcr; 9858c2ecf20Sopenharmony_ci srcr = strcr; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci /* Set SYN mode and clear RXDIR bit when using SYN or AC97 mode */ 9888c2ecf20Sopenharmony_ci if (ssi->synchronous || fsl_ssi_is_ac97(ssi)) { 9898c2ecf20Sopenharmony_ci srcr &= ~SSI_SRCR_RXDIR; 9908c2ecf20Sopenharmony_ci scr |= SSI_SCR_SYN; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci mask = SSI_STCR_TFDIR | SSI_STCR_TXDIR | SSI_STCR_TSCKP | 9948c2ecf20Sopenharmony_ci SSI_STCR_TFSL | SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_STCR, mask, stcr); 9978c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SRCR, mask, srcr); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci mask = SSI_SCR_SYNC_TX_FS | SSI_SCR_I2S_MODE_MASK | 10008c2ecf20Sopenharmony_ci SSI_SCR_SYS_CLK_EN | SSI_SCR_SYN; 10018c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SCR, mask, scr); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci return 0; 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci/** 10078c2ecf20Sopenharmony_ci * fsl_ssi_set_dai_fmt - Configure Digital Audio Interface (DAI) Format 10088c2ecf20Sopenharmony_ci * @dai: pointer to DAI 10098c2ecf20Sopenharmony_ci * @fmt: format mask 10108c2ecf20Sopenharmony_ci */ 10118c2ecf20Sopenharmony_cistatic int fsl_ssi_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci /* AC97 configured DAIFMT earlier in the probe() */ 10168c2ecf20Sopenharmony_ci if (fsl_ssi_is_ac97(ssi)) 10178c2ecf20Sopenharmony_ci return 0; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci return _fsl_ssi_set_dai_fmt(ssi, fmt); 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci/** 10238c2ecf20Sopenharmony_ci * fsl_ssi_set_dai_tdm_slot - Set TDM slot number and slot width 10248c2ecf20Sopenharmony_ci * @dai: pointer to DAI 10258c2ecf20Sopenharmony_ci * @tx_mask: mask for TX 10268c2ecf20Sopenharmony_ci * @rx_mask: mask for RX 10278c2ecf20Sopenharmony_ci * @slots: number of slots 10288c2ecf20Sopenharmony_ci * @slot_width: number of bits per slot 10298c2ecf20Sopenharmony_ci */ 10308c2ecf20Sopenharmony_cistatic int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, 10318c2ecf20Sopenharmony_ci u32 rx_mask, int slots, int slot_width) 10328c2ecf20Sopenharmony_ci{ 10338c2ecf20Sopenharmony_ci struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); 10348c2ecf20Sopenharmony_ci struct regmap *regs = ssi->regs; 10358c2ecf20Sopenharmony_ci u32 val; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci /* The word length should be 8, 10, 12, 16, 18, 20, 22 or 24 */ 10388c2ecf20Sopenharmony_ci if (slot_width & 1 || slot_width < 8 || slot_width > 24) { 10398c2ecf20Sopenharmony_ci dev_err(dai->dev, "invalid slot width: %d\n", slot_width); 10408c2ecf20Sopenharmony_ci return -EINVAL; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci /* The slot number should be >= 2 if using Network mode or I2S mode */ 10448c2ecf20Sopenharmony_ci if (ssi->i2s_net && slots < 2) { 10458c2ecf20Sopenharmony_ci dev_err(dai->dev, "slot number should be >= 2 in I2S or NET\n"); 10468c2ecf20Sopenharmony_ci return -EINVAL; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci regmap_update_bits(regs, REG_SSI_STCCR, 10508c2ecf20Sopenharmony_ci SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots)); 10518c2ecf20Sopenharmony_ci regmap_update_bits(regs, REG_SSI_SRCCR, 10528c2ecf20Sopenharmony_ci SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots)); 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci /* Save the SCR register value */ 10558c2ecf20Sopenharmony_ci regmap_read(regs, REG_SSI_SCR, &val); 10568c2ecf20Sopenharmony_ci /* Temporarily enable SSI to allow SxMSKs to be configurable */ 10578c2ecf20Sopenharmony_ci regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, SSI_SCR_SSIEN); 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci regmap_write(regs, REG_SSI_STMSK, ~tx_mask); 10608c2ecf20Sopenharmony_ci regmap_write(regs, REG_SSI_SRMSK, ~rx_mask); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci /* Restore the value of SSIEN bit */ 10638c2ecf20Sopenharmony_ci regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, val); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci ssi->slot_width = slot_width; 10668c2ecf20Sopenharmony_ci ssi->slots = slots; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci return 0; 10698c2ecf20Sopenharmony_ci} 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci/** 10728c2ecf20Sopenharmony_ci * fsl_ssi_trigger - Start or stop SSI and corresponding DMA transaction. 10738c2ecf20Sopenharmony_ci * @substream: ASoC substream 10748c2ecf20Sopenharmony_ci * @cmd: trigger command 10758c2ecf20Sopenharmony_ci * @dai: pointer to DAI 10768c2ecf20Sopenharmony_ci * 10778c2ecf20Sopenharmony_ci * The DMA channel is in external master start and pause mode, which 10788c2ecf20Sopenharmony_ci * means the SSI completely controls the flow of data. 10798c2ecf20Sopenharmony_ci */ 10808c2ecf20Sopenharmony_cistatic int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, 10818c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 10828c2ecf20Sopenharmony_ci{ 10838c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 10848c2ecf20Sopenharmony_ci struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 10858c2ecf20Sopenharmony_ci bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci switch (cmd) { 10888c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 10898c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 10908c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 10918c2ecf20Sopenharmony_ci /* 10928c2ecf20Sopenharmony_ci * SACCST might be modified via AC Link by a CODEC if it sends 10938c2ecf20Sopenharmony_ci * extra bits in their SLOTREQ requests, which'll accidentally 10948c2ecf20Sopenharmony_ci * send valid data to slots other than normal playback slots. 10958c2ecf20Sopenharmony_ci * 10968c2ecf20Sopenharmony_ci * To be safe, configure SACCST right before TX starts. 10978c2ecf20Sopenharmony_ci */ 10988c2ecf20Sopenharmony_ci if (tx && fsl_ssi_is_ac97(ssi)) 10998c2ecf20Sopenharmony_ci fsl_ssi_tx_ac97_saccst_setup(ssi); 11008c2ecf20Sopenharmony_ci fsl_ssi_config_enable(ssi, tx); 11018c2ecf20Sopenharmony_ci break; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 11048c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 11058c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 11068c2ecf20Sopenharmony_ci fsl_ssi_config_disable(ssi, tx); 11078c2ecf20Sopenharmony_ci break; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci default: 11108c2ecf20Sopenharmony_ci return -EINVAL; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci return 0; 11148c2ecf20Sopenharmony_ci} 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_cistatic int fsl_ssi_dai_probe(struct snd_soc_dai *dai) 11178c2ecf20Sopenharmony_ci{ 11188c2ecf20Sopenharmony_ci struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (ssi->soc->imx && ssi->use_dma) 11218c2ecf20Sopenharmony_ci snd_soc_dai_init_dma_data(dai, &ssi->dma_params_tx, 11228c2ecf20Sopenharmony_ci &ssi->dma_params_rx); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci return 0; 11258c2ecf20Sopenharmony_ci} 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops fsl_ssi_dai_ops = { 11288c2ecf20Sopenharmony_ci .startup = fsl_ssi_startup, 11298c2ecf20Sopenharmony_ci .shutdown = fsl_ssi_shutdown, 11308c2ecf20Sopenharmony_ci .hw_params = fsl_ssi_hw_params, 11318c2ecf20Sopenharmony_ci .hw_free = fsl_ssi_hw_free, 11328c2ecf20Sopenharmony_ci .set_fmt = fsl_ssi_set_dai_fmt, 11338c2ecf20Sopenharmony_ci .set_tdm_slot = fsl_ssi_set_dai_tdm_slot, 11348c2ecf20Sopenharmony_ci .trigger = fsl_ssi_trigger, 11358c2ecf20Sopenharmony_ci}; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver fsl_ssi_dai_template = { 11388c2ecf20Sopenharmony_ci .probe = fsl_ssi_dai_probe, 11398c2ecf20Sopenharmony_ci .playback = { 11408c2ecf20Sopenharmony_ci .stream_name = "CPU-Playback", 11418c2ecf20Sopenharmony_ci .channels_min = 1, 11428c2ecf20Sopenharmony_ci .channels_max = 32, 11438c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 11448c2ecf20Sopenharmony_ci .formats = FSLSSI_I2S_FORMATS, 11458c2ecf20Sopenharmony_ci }, 11468c2ecf20Sopenharmony_ci .capture = { 11478c2ecf20Sopenharmony_ci .stream_name = "CPU-Capture", 11488c2ecf20Sopenharmony_ci .channels_min = 1, 11498c2ecf20Sopenharmony_ci .channels_max = 32, 11508c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 11518c2ecf20Sopenharmony_ci .formats = FSLSSI_I2S_FORMATS, 11528c2ecf20Sopenharmony_ci }, 11538c2ecf20Sopenharmony_ci .ops = &fsl_ssi_dai_ops, 11548c2ecf20Sopenharmony_ci}; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver fsl_ssi_component = { 11578c2ecf20Sopenharmony_ci .name = "fsl-ssi", 11588c2ecf20Sopenharmony_ci}; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver fsl_ssi_ac97_dai = { 11618c2ecf20Sopenharmony_ci .symmetric_channels = 1, 11628c2ecf20Sopenharmony_ci .probe = fsl_ssi_dai_probe, 11638c2ecf20Sopenharmony_ci .playback = { 11648c2ecf20Sopenharmony_ci .stream_name = "CPU AC97 Playback", 11658c2ecf20Sopenharmony_ci .channels_min = 2, 11668c2ecf20Sopenharmony_ci .channels_max = 2, 11678c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 11688c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S20, 11698c2ecf20Sopenharmony_ci }, 11708c2ecf20Sopenharmony_ci .capture = { 11718c2ecf20Sopenharmony_ci .stream_name = "CPU AC97 Capture", 11728c2ecf20Sopenharmony_ci .channels_min = 2, 11738c2ecf20Sopenharmony_ci .channels_max = 2, 11748c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 11758c2ecf20Sopenharmony_ci /* 16-bit capture is broken (errata ERR003778) */ 11768c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S20, 11778c2ecf20Sopenharmony_ci }, 11788c2ecf20Sopenharmony_ci .ops = &fsl_ssi_dai_ops, 11798c2ecf20Sopenharmony_ci}; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_cistatic struct fsl_ssi *fsl_ac97_data; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_cistatic void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, 11848c2ecf20Sopenharmony_ci unsigned short val) 11858c2ecf20Sopenharmony_ci{ 11868c2ecf20Sopenharmony_ci struct regmap *regs = fsl_ac97_data->regs; 11878c2ecf20Sopenharmony_ci unsigned int lreg; 11888c2ecf20Sopenharmony_ci unsigned int lval; 11898c2ecf20Sopenharmony_ci int ret; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci if (reg > 0x7f) 11928c2ecf20Sopenharmony_ci return; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci mutex_lock(&fsl_ac97_data->ac97_reg_lock); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fsl_ac97_data->clk); 11978c2ecf20Sopenharmony_ci if (ret) { 11988c2ecf20Sopenharmony_ci pr_err("ac97 write clk_prepare_enable failed: %d\n", 11998c2ecf20Sopenharmony_ci ret); 12008c2ecf20Sopenharmony_ci goto ret_unlock; 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci lreg = reg << 12; 12048c2ecf20Sopenharmony_ci regmap_write(regs, REG_SSI_SACADD, lreg); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci lval = val << 4; 12078c2ecf20Sopenharmony_ci regmap_write(regs, REG_SSI_SACDAT, lval); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci regmap_update_bits(regs, REG_SSI_SACNT, 12108c2ecf20Sopenharmony_ci SSI_SACNT_RDWR_MASK, SSI_SACNT_WR); 12118c2ecf20Sopenharmony_ci udelay(100); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci clk_disable_unprepare(fsl_ac97_data->clk); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ciret_unlock: 12168c2ecf20Sopenharmony_ci mutex_unlock(&fsl_ac97_data->ac97_reg_lock); 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_cistatic unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, 12208c2ecf20Sopenharmony_ci unsigned short reg) 12218c2ecf20Sopenharmony_ci{ 12228c2ecf20Sopenharmony_ci struct regmap *regs = fsl_ac97_data->regs; 12238c2ecf20Sopenharmony_ci unsigned short val = 0; 12248c2ecf20Sopenharmony_ci u32 reg_val; 12258c2ecf20Sopenharmony_ci unsigned int lreg; 12268c2ecf20Sopenharmony_ci int ret; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci mutex_lock(&fsl_ac97_data->ac97_reg_lock); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fsl_ac97_data->clk); 12318c2ecf20Sopenharmony_ci if (ret) { 12328c2ecf20Sopenharmony_ci pr_err("ac97 read clk_prepare_enable failed: %d\n", ret); 12338c2ecf20Sopenharmony_ci goto ret_unlock; 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci lreg = (reg & 0x7f) << 12; 12378c2ecf20Sopenharmony_ci regmap_write(regs, REG_SSI_SACADD, lreg); 12388c2ecf20Sopenharmony_ci regmap_update_bits(regs, REG_SSI_SACNT, 12398c2ecf20Sopenharmony_ci SSI_SACNT_RDWR_MASK, SSI_SACNT_RD); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci udelay(100); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci regmap_read(regs, REG_SSI_SACDAT, ®_val); 12448c2ecf20Sopenharmony_ci val = (reg_val >> 4) & 0xffff; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci clk_disable_unprepare(fsl_ac97_data->clk); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ciret_unlock: 12498c2ecf20Sopenharmony_ci mutex_unlock(&fsl_ac97_data->ac97_reg_lock); 12508c2ecf20Sopenharmony_ci return val; 12518c2ecf20Sopenharmony_ci} 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_cistatic struct snd_ac97_bus_ops fsl_ssi_ac97_ops = { 12548c2ecf20Sopenharmony_ci .read = fsl_ssi_ac97_read, 12558c2ecf20Sopenharmony_ci .write = fsl_ssi_ac97_write, 12568c2ecf20Sopenharmony_ci}; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci/** 12598c2ecf20Sopenharmony_ci * fsl_ssi_hw_init - Initialize SSI registers 12608c2ecf20Sopenharmony_ci * @ssi: SSI context 12618c2ecf20Sopenharmony_ci */ 12628c2ecf20Sopenharmony_cistatic int fsl_ssi_hw_init(struct fsl_ssi *ssi) 12638c2ecf20Sopenharmony_ci{ 12648c2ecf20Sopenharmony_ci u32 wm = ssi->fifo_watermark; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* Initialize regvals */ 12678c2ecf20Sopenharmony_ci fsl_ssi_setup_regvals(ssi); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci /* Set watermarks */ 12708c2ecf20Sopenharmony_ci regmap_write(ssi->regs, REG_SSI_SFCSR, 12718c2ecf20Sopenharmony_ci SSI_SFCSR_TFWM0(wm) | SSI_SFCSR_RFWM0(wm) | 12728c2ecf20Sopenharmony_ci SSI_SFCSR_TFWM1(wm) | SSI_SFCSR_RFWM1(wm)); 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci /* Enable Dual FIFO mode */ 12758c2ecf20Sopenharmony_ci if (ssi->use_dual_fifo) 12768c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SCR, 12778c2ecf20Sopenharmony_ci SSI_SCR_TCH_EN, SSI_SCR_TCH_EN); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci /* AC97 should start earlier to communicate with CODECs */ 12808c2ecf20Sopenharmony_ci if (fsl_ssi_is_ac97(ssi)) { 12818c2ecf20Sopenharmony_ci _fsl_ssi_set_dai_fmt(ssi, ssi->dai_fmt); 12828c2ecf20Sopenharmony_ci fsl_ssi_setup_ac97(ssi); 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci return 0; 12868c2ecf20Sopenharmony_ci} 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci/** 12898c2ecf20Sopenharmony_ci * fsl_ssi_hw_clean - Clear SSI registers 12908c2ecf20Sopenharmony_ci * @ssi: SSI context 12918c2ecf20Sopenharmony_ci */ 12928c2ecf20Sopenharmony_cistatic void fsl_ssi_hw_clean(struct fsl_ssi *ssi) 12938c2ecf20Sopenharmony_ci{ 12948c2ecf20Sopenharmony_ci /* Disable registers for AC97 */ 12958c2ecf20Sopenharmony_ci if (fsl_ssi_is_ac97(ssi)) { 12968c2ecf20Sopenharmony_ci /* Disable TE and RE bits first */ 12978c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SCR, 12988c2ecf20Sopenharmony_ci SSI_SCR_TE | SSI_SCR_RE, 0); 12998c2ecf20Sopenharmony_ci /* Disable AC97 mode */ 13008c2ecf20Sopenharmony_ci regmap_write(ssi->regs, REG_SSI_SACNT, 0); 13018c2ecf20Sopenharmony_ci /* Unset WAIT bits */ 13028c2ecf20Sopenharmony_ci regmap_write(ssi->regs, REG_SSI_SOR, 0); 13038c2ecf20Sopenharmony_ci /* Disable SSI -- software reset */ 13048c2ecf20Sopenharmony_ci regmap_update_bits(ssi->regs, REG_SSI_SCR, SSI_SCR_SSIEN, 0); 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci} 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci/* 13098c2ecf20Sopenharmony_ci * Make every character in a string lower-case 13108c2ecf20Sopenharmony_ci */ 13118c2ecf20Sopenharmony_cistatic void make_lowercase(char *s) 13128c2ecf20Sopenharmony_ci{ 13138c2ecf20Sopenharmony_ci if (!s) 13148c2ecf20Sopenharmony_ci return; 13158c2ecf20Sopenharmony_ci for (; *s; s++) 13168c2ecf20Sopenharmony_ci *s = tolower(*s); 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_cistatic int fsl_ssi_imx_probe(struct platform_device *pdev, 13208c2ecf20Sopenharmony_ci struct fsl_ssi *ssi, void __iomem *iomem) 13218c2ecf20Sopenharmony_ci{ 13228c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 13238c2ecf20Sopenharmony_ci int ret; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci /* Backward compatible for a DT without ipg clock name assigned */ 13268c2ecf20Sopenharmony_ci if (ssi->has_ipg_clk_name) 13278c2ecf20Sopenharmony_ci ssi->clk = devm_clk_get(dev, "ipg"); 13288c2ecf20Sopenharmony_ci else 13298c2ecf20Sopenharmony_ci ssi->clk = devm_clk_get(dev, NULL); 13308c2ecf20Sopenharmony_ci if (IS_ERR(ssi->clk)) { 13318c2ecf20Sopenharmony_ci ret = PTR_ERR(ssi->clk); 13328c2ecf20Sopenharmony_ci dev_err(dev, "failed to get clock: %d\n", ret); 13338c2ecf20Sopenharmony_ci return ret; 13348c2ecf20Sopenharmony_ci } 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci /* Enable the clock since regmap will not handle it in this case */ 13378c2ecf20Sopenharmony_ci if (!ssi->has_ipg_clk_name) { 13388c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ssi->clk); 13398c2ecf20Sopenharmony_ci if (ret) { 13408c2ecf20Sopenharmony_ci dev_err(dev, "clk_prepare_enable failed: %d\n", ret); 13418c2ecf20Sopenharmony_ci return ret; 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci /* Do not error out for slave cases that live without a baud clock */ 13468c2ecf20Sopenharmony_ci ssi->baudclk = devm_clk_get(dev, "baud"); 13478c2ecf20Sopenharmony_ci if (IS_ERR(ssi->baudclk)) 13488c2ecf20Sopenharmony_ci dev_dbg(dev, "failed to get baud clock: %ld\n", 13498c2ecf20Sopenharmony_ci PTR_ERR(ssi->baudclk)); 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci ssi->dma_params_tx.maxburst = ssi->dma_maxburst; 13528c2ecf20Sopenharmony_ci ssi->dma_params_rx.maxburst = ssi->dma_maxburst; 13538c2ecf20Sopenharmony_ci ssi->dma_params_tx.addr = ssi->ssi_phys + REG_SSI_STX0; 13548c2ecf20Sopenharmony_ci ssi->dma_params_rx.addr = ssi->ssi_phys + REG_SSI_SRX0; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci /* Use even numbers to avoid channel swap due to SDMA script design */ 13578c2ecf20Sopenharmony_ci if (ssi->use_dual_fifo) { 13588c2ecf20Sopenharmony_ci ssi->dma_params_tx.maxburst &= ~0x1; 13598c2ecf20Sopenharmony_ci ssi->dma_params_rx.maxburst &= ~0x1; 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci if (!ssi->use_dma) { 13638c2ecf20Sopenharmony_ci /* 13648c2ecf20Sopenharmony_ci * Some boards use an incompatible codec. Use imx-fiq-pcm-audio 13658c2ecf20Sopenharmony_ci * to get it working, as DMA is not possible in this situation. 13668c2ecf20Sopenharmony_ci */ 13678c2ecf20Sopenharmony_ci ssi->fiq_params.irq = ssi->irq; 13688c2ecf20Sopenharmony_ci ssi->fiq_params.base = iomem; 13698c2ecf20Sopenharmony_ci ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx; 13708c2ecf20Sopenharmony_ci ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx; 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci ret = imx_pcm_fiq_init(pdev, &ssi->fiq_params); 13738c2ecf20Sopenharmony_ci if (ret) 13748c2ecf20Sopenharmony_ci goto error_pcm; 13758c2ecf20Sopenharmony_ci } else { 13768c2ecf20Sopenharmony_ci ret = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE); 13778c2ecf20Sopenharmony_ci if (ret) 13788c2ecf20Sopenharmony_ci goto error_pcm; 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci return 0; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cierror_pcm: 13848c2ecf20Sopenharmony_ci if (!ssi->has_ipg_clk_name) 13858c2ecf20Sopenharmony_ci clk_disable_unprepare(ssi->clk); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci return ret; 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_cistatic void fsl_ssi_imx_clean(struct platform_device *pdev, struct fsl_ssi *ssi) 13918c2ecf20Sopenharmony_ci{ 13928c2ecf20Sopenharmony_ci if (!ssi->use_dma) 13938c2ecf20Sopenharmony_ci imx_pcm_fiq_exit(pdev); 13948c2ecf20Sopenharmony_ci if (!ssi->has_ipg_clk_name) 13958c2ecf20Sopenharmony_ci clk_disable_unprepare(ssi->clk); 13968c2ecf20Sopenharmony_ci} 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_cistatic int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi) 13998c2ecf20Sopenharmony_ci{ 14008c2ecf20Sopenharmony_ci struct device *dev = ssi->dev; 14018c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 14028c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 14038c2ecf20Sopenharmony_ci const char *p, *sprop; 14048c2ecf20Sopenharmony_ci const __be32 *iprop; 14058c2ecf20Sopenharmony_ci u32 dmas[4]; 14068c2ecf20Sopenharmony_ci int ret; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci of_id = of_match_device(fsl_ssi_ids, dev); 14098c2ecf20Sopenharmony_ci if (!of_id || !of_id->data) 14108c2ecf20Sopenharmony_ci return -EINVAL; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci ssi->soc = of_id->data; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci ret = of_property_match_string(np, "clock-names", "ipg"); 14158c2ecf20Sopenharmony_ci /* Get error code if not found */ 14168c2ecf20Sopenharmony_ci ssi->has_ipg_clk_name = ret >= 0; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci /* Check if being used in AC97 mode */ 14198c2ecf20Sopenharmony_ci sprop = of_get_property(np, "fsl,mode", NULL); 14208c2ecf20Sopenharmony_ci if (sprop && !strcmp(sprop, "ac97-slave")) { 14218c2ecf20Sopenharmony_ci ssi->dai_fmt = FSLSSI_AC97_DAIFMT; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "cell-index", &ssi->card_idx); 14248c2ecf20Sopenharmony_ci if (ret) { 14258c2ecf20Sopenharmony_ci dev_err(dev, "failed to get SSI index property\n"); 14268c2ecf20Sopenharmony_ci return -EINVAL; 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci strcpy(ssi->card_name, "ac97-codec"); 14298c2ecf20Sopenharmony_ci } else if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) { 14308c2ecf20Sopenharmony_ci /* 14318c2ecf20Sopenharmony_ci * In synchronous mode, STCK and STFS ports are used by RX 14328c2ecf20Sopenharmony_ci * as well. So the software should limit the sample rates, 14338c2ecf20Sopenharmony_ci * sample bits and channels to be symmetric. 14348c2ecf20Sopenharmony_ci * 14358c2ecf20Sopenharmony_ci * This is exclusive with FSLSSI_AC97_FORMATS as AC97 runs 14368c2ecf20Sopenharmony_ci * in the SSI synchronous mode however it does not have to 14378c2ecf20Sopenharmony_ci * limit symmetric sample rates and sample bits. 14388c2ecf20Sopenharmony_ci */ 14398c2ecf20Sopenharmony_ci ssi->synchronous = true; 14408c2ecf20Sopenharmony_ci } 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci /* Select DMA or FIQ */ 14438c2ecf20Sopenharmony_ci ssi->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter"); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci /* Fetch FIFO depth; Set to 8 for older DT without this property */ 14468c2ecf20Sopenharmony_ci iprop = of_get_property(np, "fsl,fifo-depth", NULL); 14478c2ecf20Sopenharmony_ci if (iprop) 14488c2ecf20Sopenharmony_ci ssi->fifo_depth = be32_to_cpup(iprop); 14498c2ecf20Sopenharmony_ci else 14508c2ecf20Sopenharmony_ci ssi->fifo_depth = 8; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci /* Use dual FIFO mode depending on the support from SDMA script */ 14538c2ecf20Sopenharmony_ci ret = of_property_read_u32_array(np, "dmas", dmas, 4); 14548c2ecf20Sopenharmony_ci if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) 14558c2ecf20Sopenharmony_ci ssi->use_dual_fifo = true; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci /* 14588c2ecf20Sopenharmony_ci * Backward compatible for older bindings by manually triggering the 14598c2ecf20Sopenharmony_ci * machine driver's probe(). Use /compatible property, including the 14608c2ecf20Sopenharmony_ci * address of CPU DAI driver structure, as the name of machine driver 14618c2ecf20Sopenharmony_ci * 14628c2ecf20Sopenharmony_ci * If card_name is set by AC97 earlier, bypass here since it uses a 14638c2ecf20Sopenharmony_ci * different name to register the device. 14648c2ecf20Sopenharmony_ci */ 14658c2ecf20Sopenharmony_ci if (!ssi->card_name[0] && of_get_property(np, "codec-handle", NULL)) { 14668c2ecf20Sopenharmony_ci struct device_node *root = of_find_node_by_path("/"); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci sprop = of_get_property(root, "compatible", NULL); 14698c2ecf20Sopenharmony_ci of_node_put(root); 14708c2ecf20Sopenharmony_ci /* Strip "fsl," in the compatible name if applicable */ 14718c2ecf20Sopenharmony_ci p = strrchr(sprop, ','); 14728c2ecf20Sopenharmony_ci if (p) 14738c2ecf20Sopenharmony_ci sprop = p + 1; 14748c2ecf20Sopenharmony_ci snprintf(ssi->card_name, sizeof(ssi->card_name), 14758c2ecf20Sopenharmony_ci "snd-soc-%s", sprop); 14768c2ecf20Sopenharmony_ci make_lowercase(ssi->card_name); 14778c2ecf20Sopenharmony_ci ssi->card_idx = 0; 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci return 0; 14818c2ecf20Sopenharmony_ci} 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_cistatic int fsl_ssi_probe(struct platform_device *pdev) 14848c2ecf20Sopenharmony_ci{ 14858c2ecf20Sopenharmony_ci struct regmap_config regconfig = fsl_ssi_regconfig; 14868c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 14878c2ecf20Sopenharmony_ci struct fsl_ssi *ssi; 14888c2ecf20Sopenharmony_ci struct resource *res; 14898c2ecf20Sopenharmony_ci void __iomem *iomem; 14908c2ecf20Sopenharmony_ci int ret = 0; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci ssi = devm_kzalloc(dev, sizeof(*ssi), GFP_KERNEL); 14938c2ecf20Sopenharmony_ci if (!ssi) 14948c2ecf20Sopenharmony_ci return -ENOMEM; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci ssi->dev = dev; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci /* Probe from DT */ 14998c2ecf20Sopenharmony_ci ret = fsl_ssi_probe_from_dt(ssi); 15008c2ecf20Sopenharmony_ci if (ret) 15018c2ecf20Sopenharmony_ci return ret; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci if (fsl_ssi_is_ac97(ssi)) { 15048c2ecf20Sopenharmony_ci memcpy(&ssi->cpu_dai_drv, &fsl_ssi_ac97_dai, 15058c2ecf20Sopenharmony_ci sizeof(fsl_ssi_ac97_dai)); 15068c2ecf20Sopenharmony_ci fsl_ac97_data = ssi; 15078c2ecf20Sopenharmony_ci } else { 15088c2ecf20Sopenharmony_ci memcpy(&ssi->cpu_dai_drv, &fsl_ssi_dai_template, 15098c2ecf20Sopenharmony_ci sizeof(fsl_ssi_dai_template)); 15108c2ecf20Sopenharmony_ci } 15118c2ecf20Sopenharmony_ci ssi->cpu_dai_drv.name = dev_name(dev); 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 15148c2ecf20Sopenharmony_ci iomem = devm_ioremap_resource(dev, res); 15158c2ecf20Sopenharmony_ci if (IS_ERR(iomem)) 15168c2ecf20Sopenharmony_ci return PTR_ERR(iomem); 15178c2ecf20Sopenharmony_ci ssi->ssi_phys = res->start; 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci if (ssi->soc->imx21regs) { 15208c2ecf20Sopenharmony_ci /* No SACC{ST,EN,DIS} regs in imx21-class SSI */ 15218c2ecf20Sopenharmony_ci regconfig.max_register = REG_SSI_SRMSK; 15228c2ecf20Sopenharmony_ci regconfig.num_reg_defaults_raw = 15238c2ecf20Sopenharmony_ci REG_SSI_SRMSK / sizeof(uint32_t) + 1; 15248c2ecf20Sopenharmony_ci } 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci if (ssi->has_ipg_clk_name) 15278c2ecf20Sopenharmony_ci ssi->regs = devm_regmap_init_mmio_clk(dev, "ipg", iomem, 15288c2ecf20Sopenharmony_ci ®config); 15298c2ecf20Sopenharmony_ci else 15308c2ecf20Sopenharmony_ci ssi->regs = devm_regmap_init_mmio(dev, iomem, ®config); 15318c2ecf20Sopenharmony_ci if (IS_ERR(ssi->regs)) { 15328c2ecf20Sopenharmony_ci dev_err(dev, "failed to init register map\n"); 15338c2ecf20Sopenharmony_ci return PTR_ERR(ssi->regs); 15348c2ecf20Sopenharmony_ci } 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci ssi->irq = platform_get_irq(pdev, 0); 15378c2ecf20Sopenharmony_ci if (ssi->irq < 0) 15388c2ecf20Sopenharmony_ci return ssi->irq; 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci /* Set software limitations for synchronous mode except AC97 */ 15418c2ecf20Sopenharmony_ci if (ssi->synchronous && !fsl_ssi_is_ac97(ssi)) { 15428c2ecf20Sopenharmony_ci ssi->cpu_dai_drv.symmetric_rates = 1; 15438c2ecf20Sopenharmony_ci ssi->cpu_dai_drv.symmetric_channels = 1; 15448c2ecf20Sopenharmony_ci ssi->cpu_dai_drv.symmetric_samplebits = 1; 15458c2ecf20Sopenharmony_ci } 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci /* 15488c2ecf20Sopenharmony_ci * Configure TX and RX DMA watermarks -- when to send a DMA request 15498c2ecf20Sopenharmony_ci * 15508c2ecf20Sopenharmony_ci * Values should be tested to avoid FIFO under/over run. Set maxburst 15518c2ecf20Sopenharmony_ci * to fifo_watermark to maxiumize DMA transaction to reduce overhead. 15528c2ecf20Sopenharmony_ci */ 15538c2ecf20Sopenharmony_ci switch (ssi->fifo_depth) { 15548c2ecf20Sopenharmony_ci case 15: 15558c2ecf20Sopenharmony_ci /* 15568c2ecf20Sopenharmony_ci * Set to 8 as a balanced configuration -- When TX FIFO has 8 15578c2ecf20Sopenharmony_ci * empty slots, send a DMA request to fill these 8 slots. The 15588c2ecf20Sopenharmony_ci * remaining 7 slots should be able to allow DMA to finish the 15598c2ecf20Sopenharmony_ci * transaction before TX FIFO underruns; Same applies to RX. 15608c2ecf20Sopenharmony_ci * 15618c2ecf20Sopenharmony_ci * Tested with cases running at 48kHz @ 16 bits x 16 channels 15628c2ecf20Sopenharmony_ci */ 15638c2ecf20Sopenharmony_ci ssi->fifo_watermark = 8; 15648c2ecf20Sopenharmony_ci ssi->dma_maxburst = 8; 15658c2ecf20Sopenharmony_ci break; 15668c2ecf20Sopenharmony_ci case 8: 15678c2ecf20Sopenharmony_ci default: 15688c2ecf20Sopenharmony_ci /* Safely use old watermark configurations for older chips */ 15698c2ecf20Sopenharmony_ci ssi->fifo_watermark = ssi->fifo_depth - 2; 15708c2ecf20Sopenharmony_ci ssi->dma_maxburst = ssi->fifo_depth - 2; 15718c2ecf20Sopenharmony_ci break; 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci dev_set_drvdata(dev, ssi); 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci if (ssi->soc->imx) { 15778c2ecf20Sopenharmony_ci ret = fsl_ssi_imx_probe(pdev, ssi, iomem); 15788c2ecf20Sopenharmony_ci if (ret) 15798c2ecf20Sopenharmony_ci return ret; 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci if (fsl_ssi_is_ac97(ssi)) { 15838c2ecf20Sopenharmony_ci mutex_init(&ssi->ac97_reg_lock); 15848c2ecf20Sopenharmony_ci ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev); 15858c2ecf20Sopenharmony_ci if (ret) { 15868c2ecf20Sopenharmony_ci dev_err(dev, "failed to set AC'97 ops\n"); 15878c2ecf20Sopenharmony_ci goto error_ac97_ops; 15888c2ecf20Sopenharmony_ci } 15898c2ecf20Sopenharmony_ci } 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(dev, &fsl_ssi_component, 15928c2ecf20Sopenharmony_ci &ssi->cpu_dai_drv, 1); 15938c2ecf20Sopenharmony_ci if (ret) { 15948c2ecf20Sopenharmony_ci dev_err(dev, "failed to register DAI: %d\n", ret); 15958c2ecf20Sopenharmony_ci goto error_asoc_register; 15968c2ecf20Sopenharmony_ci } 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci if (ssi->use_dma) { 15998c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, ssi->irq, fsl_ssi_isr, 0, 16008c2ecf20Sopenharmony_ci dev_name(dev), ssi); 16018c2ecf20Sopenharmony_ci if (ret < 0) { 16028c2ecf20Sopenharmony_ci dev_err(dev, "failed to claim irq %u\n", ssi->irq); 16038c2ecf20Sopenharmony_ci goto error_asoc_register; 16048c2ecf20Sopenharmony_ci } 16058c2ecf20Sopenharmony_ci } 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci fsl_ssi_debugfs_create(&ssi->dbg_stats, dev); 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci /* Initially configures SSI registers */ 16108c2ecf20Sopenharmony_ci fsl_ssi_hw_init(ssi); 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci /* Register a platform device for older bindings or AC97 */ 16138c2ecf20Sopenharmony_ci if (ssi->card_name[0]) { 16148c2ecf20Sopenharmony_ci struct device *parent = dev; 16158c2ecf20Sopenharmony_ci /* 16168c2ecf20Sopenharmony_ci * Do not set SSI dev as the parent of AC97 CODEC device since 16178c2ecf20Sopenharmony_ci * it does not have a DT node. Otherwise ASoC core will assume 16188c2ecf20Sopenharmony_ci * CODEC has the same DT node as the SSI, so it may bypass the 16198c2ecf20Sopenharmony_ci * dai_probe() of SSI and then cause NULL DMA data pointers. 16208c2ecf20Sopenharmony_ci */ 16218c2ecf20Sopenharmony_ci if (fsl_ssi_is_ac97(ssi)) 16228c2ecf20Sopenharmony_ci parent = NULL; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci ssi->card_pdev = platform_device_register_data(parent, 16258c2ecf20Sopenharmony_ci ssi->card_name, ssi->card_idx, NULL, 0); 16268c2ecf20Sopenharmony_ci if (IS_ERR(ssi->card_pdev)) { 16278c2ecf20Sopenharmony_ci ret = PTR_ERR(ssi->card_pdev); 16288c2ecf20Sopenharmony_ci dev_err(dev, "failed to register %s: %d\n", 16298c2ecf20Sopenharmony_ci ssi->card_name, ret); 16308c2ecf20Sopenharmony_ci goto error_sound_card; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci return 0; 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_cierror_sound_card: 16378c2ecf20Sopenharmony_ci fsl_ssi_debugfs_remove(&ssi->dbg_stats); 16388c2ecf20Sopenharmony_cierror_asoc_register: 16398c2ecf20Sopenharmony_ci if (fsl_ssi_is_ac97(ssi)) 16408c2ecf20Sopenharmony_ci snd_soc_set_ac97_ops(NULL); 16418c2ecf20Sopenharmony_cierror_ac97_ops: 16428c2ecf20Sopenharmony_ci if (fsl_ssi_is_ac97(ssi)) 16438c2ecf20Sopenharmony_ci mutex_destroy(&ssi->ac97_reg_lock); 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci if (ssi->soc->imx) 16468c2ecf20Sopenharmony_ci fsl_ssi_imx_clean(pdev, ssi); 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci return ret; 16498c2ecf20Sopenharmony_ci} 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_cistatic int fsl_ssi_remove(struct platform_device *pdev) 16528c2ecf20Sopenharmony_ci{ 16538c2ecf20Sopenharmony_ci struct fsl_ssi *ssi = dev_get_drvdata(&pdev->dev); 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci fsl_ssi_debugfs_remove(&ssi->dbg_stats); 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci if (ssi->card_pdev) 16588c2ecf20Sopenharmony_ci platform_device_unregister(ssi->card_pdev); 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci /* Clean up SSI registers */ 16618c2ecf20Sopenharmony_ci fsl_ssi_hw_clean(ssi); 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci if (ssi->soc->imx) 16648c2ecf20Sopenharmony_ci fsl_ssi_imx_clean(pdev, ssi); 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci if (fsl_ssi_is_ac97(ssi)) { 16678c2ecf20Sopenharmony_ci snd_soc_set_ac97_ops(NULL); 16688c2ecf20Sopenharmony_ci mutex_destroy(&ssi->ac97_reg_lock); 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci return 0; 16728c2ecf20Sopenharmony_ci} 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 16758c2ecf20Sopenharmony_cistatic int fsl_ssi_suspend(struct device *dev) 16768c2ecf20Sopenharmony_ci{ 16778c2ecf20Sopenharmony_ci struct fsl_ssi *ssi = dev_get_drvdata(dev); 16788c2ecf20Sopenharmony_ci struct regmap *regs = ssi->regs; 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci regmap_read(regs, REG_SSI_SFCSR, &ssi->regcache_sfcsr); 16818c2ecf20Sopenharmony_ci regmap_read(regs, REG_SSI_SACNT, &ssi->regcache_sacnt); 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci regcache_cache_only(regs, true); 16848c2ecf20Sopenharmony_ci regcache_mark_dirty(regs); 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci return 0; 16878c2ecf20Sopenharmony_ci} 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_cistatic int fsl_ssi_resume(struct device *dev) 16908c2ecf20Sopenharmony_ci{ 16918c2ecf20Sopenharmony_ci struct fsl_ssi *ssi = dev_get_drvdata(dev); 16928c2ecf20Sopenharmony_ci struct regmap *regs = ssi->regs; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci regcache_cache_only(regs, false); 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci regmap_update_bits(regs, REG_SSI_SFCSR, 16978c2ecf20Sopenharmony_ci SSI_SFCSR_RFWM1_MASK | SSI_SFCSR_TFWM1_MASK | 16988c2ecf20Sopenharmony_ci SSI_SFCSR_RFWM0_MASK | SSI_SFCSR_TFWM0_MASK, 16998c2ecf20Sopenharmony_ci ssi->regcache_sfcsr); 17008c2ecf20Sopenharmony_ci regmap_write(regs, REG_SSI_SACNT, ssi->regcache_sacnt); 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci return regcache_sync(regs); 17038c2ecf20Sopenharmony_ci} 17048c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_cistatic const struct dev_pm_ops fsl_ssi_pm = { 17078c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume) 17088c2ecf20Sopenharmony_ci}; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_cistatic struct platform_driver fsl_ssi_driver = { 17118c2ecf20Sopenharmony_ci .driver = { 17128c2ecf20Sopenharmony_ci .name = "fsl-ssi-dai", 17138c2ecf20Sopenharmony_ci .of_match_table = fsl_ssi_ids, 17148c2ecf20Sopenharmony_ci .pm = &fsl_ssi_pm, 17158c2ecf20Sopenharmony_ci }, 17168c2ecf20Sopenharmony_ci .probe = fsl_ssi_probe, 17178c2ecf20Sopenharmony_ci .remove = fsl_ssi_remove, 17188c2ecf20Sopenharmony_ci}; 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_cimodule_platform_driver(fsl_ssi_driver); 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:fsl-ssi-dai"); 17238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); 17248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); 17258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1726