162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Author: Timur Tabi <timur@freescale.com>
662306a36Sopenharmony_ci//
762306a36Sopenharmony_ci// Copyright 2007-2010 Freescale Semiconductor, Inc.
862306a36Sopenharmony_ci//
962306a36Sopenharmony_ci// Some notes why imx-pcm-fiq is used instead of DMA on some boards:
1062306a36Sopenharmony_ci//
1162306a36Sopenharmony_ci// The i.MX SSI core has some nasty limitations in AC97 mode. While most
1262306a36Sopenharmony_ci// sane processor vendors have a FIFO per AC97 slot, the i.MX has only
1362306a36Sopenharmony_ci// one FIFO which combines all valid receive slots. We cannot even select
1462306a36Sopenharmony_ci// which slots we want to receive. The WM9712 with which this driver
1562306a36Sopenharmony_ci// was developed with always sends GPIO status data in slot 12 which
1662306a36Sopenharmony_ci// we receive in our (PCM-) data stream. The only chance we have is to
1762306a36Sopenharmony_ci// manually skip this data in the FIQ handler. With sampling rates different
1862306a36Sopenharmony_ci// from 48000Hz not every frame has valid receive data, so the ratio
1962306a36Sopenharmony_ci// between pcm data and GPIO status data changes. Our FIQ handler is not
2062306a36Sopenharmony_ci// able to handle this, hence this driver only works with 48000Hz sampling
2162306a36Sopenharmony_ci// rate.
2262306a36Sopenharmony_ci// Reading and writing AC97 registers is another challenge. The core
2362306a36Sopenharmony_ci// provides us status bits when the read register is updated with *another*
2462306a36Sopenharmony_ci// value. When we read the same register two times (and the register still
2562306a36Sopenharmony_ci// contains the same value) these status bits are not set. We work
2662306a36Sopenharmony_ci// around this by not polling these bits but only wait a fixed delay.
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <linux/init.h>
2962306a36Sopenharmony_ci#include <linux/io.h>
3062306a36Sopenharmony_ci#include <linux/module.h>
3162306a36Sopenharmony_ci#include <linux/interrupt.h>
3262306a36Sopenharmony_ci#include <linux/clk.h>
3362306a36Sopenharmony_ci#include <linux/ctype.h>
3462306a36Sopenharmony_ci#include <linux/device.h>
3562306a36Sopenharmony_ci#include <linux/delay.h>
3662306a36Sopenharmony_ci#include <linux/mutex.h>
3762306a36Sopenharmony_ci#include <linux/slab.h>
3862306a36Sopenharmony_ci#include <linux/spinlock.h>
3962306a36Sopenharmony_ci#include <linux/of.h>
4062306a36Sopenharmony_ci#include <linux/of_address.h>
4162306a36Sopenharmony_ci#include <linux/of_irq.h>
4262306a36Sopenharmony_ci#include <linux/of_platform.h>
4362306a36Sopenharmony_ci#include <linux/dma/imx-dma.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include <sound/core.h>
4662306a36Sopenharmony_ci#include <sound/pcm.h>
4762306a36Sopenharmony_ci#include <sound/pcm_params.h>
4862306a36Sopenharmony_ci#include <sound/initval.h>
4962306a36Sopenharmony_ci#include <sound/soc.h>
5062306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h>
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#include "fsl_ssi.h"
5362306a36Sopenharmony_ci#include "imx-pcm.h"
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* Define RX and TX to index ssi->regvals array; Can be 0 or 1 only */
5662306a36Sopenharmony_ci#define RX 0
5762306a36Sopenharmony_ci#define TX 1
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/**
6062306a36Sopenharmony_ci * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
6162306a36Sopenharmony_ci *
6262306a36Sopenharmony_ci * The SSI has a limitation in that the samples must be in the same byte
6362306a36Sopenharmony_ci * order as the host CPU.  This is because when multiple bytes are written
6462306a36Sopenharmony_ci * to the STX register, the bytes and bits must be written in the same
6562306a36Sopenharmony_ci * order.  The STX is a shift register, so all the bits need to be aligned
6662306a36Sopenharmony_ci * (bit-endianness must match byte-endianness).  Processors typically write
6762306a36Sopenharmony_ci * the bits within a byte in the same order that the bytes of a word are
6862306a36Sopenharmony_ci * written in.  So if the host CPU is big-endian, then only big-endian
6962306a36Sopenharmony_ci * samples will be written to STX properly.
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
7262306a36Sopenharmony_ci#define FSLSSI_I2S_FORMATS \
7362306a36Sopenharmony_ci	(SNDRV_PCM_FMTBIT_S8 | \
7462306a36Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S16_BE | \
7562306a36Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S18_3BE | \
7662306a36Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S20_3BE | \
7762306a36Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S24_3BE | \
7862306a36Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S24_BE)
7962306a36Sopenharmony_ci#else
8062306a36Sopenharmony_ci#define FSLSSI_I2S_FORMATS \
8162306a36Sopenharmony_ci	(SNDRV_PCM_FMTBIT_S8 | \
8262306a36Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S16_LE | \
8362306a36Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S18_3LE | \
8462306a36Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S20_3LE | \
8562306a36Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S24_3LE | \
8662306a36Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S24_LE)
8762306a36Sopenharmony_ci#endif
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/*
9062306a36Sopenharmony_ci * In AC97 mode, TXDIR bit is forced to 0 and TFDIR bit is forced to 1:
9162306a36Sopenharmony_ci *  - SSI inputs external bit clock and outputs frame sync clock -- CBM_CFS
9262306a36Sopenharmony_ci *  - Also have NB_NF to mark these two clocks will not be inverted
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_ci#define FSLSSI_AC97_DAIFMT \
9562306a36Sopenharmony_ci	(SND_SOC_DAIFMT_AC97 | \
9662306a36Sopenharmony_ci	 SND_SOC_DAIFMT_BC_FP | \
9762306a36Sopenharmony_ci	 SND_SOC_DAIFMT_NB_NF)
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#define FSLSSI_SIER_DBG_RX_FLAGS \
10062306a36Sopenharmony_ci	(SSI_SIER_RFF0_EN | \
10162306a36Sopenharmony_ci	 SSI_SIER_RLS_EN | \
10262306a36Sopenharmony_ci	 SSI_SIER_RFS_EN | \
10362306a36Sopenharmony_ci	 SSI_SIER_ROE0_EN | \
10462306a36Sopenharmony_ci	 SSI_SIER_RFRC_EN)
10562306a36Sopenharmony_ci#define FSLSSI_SIER_DBG_TX_FLAGS \
10662306a36Sopenharmony_ci	(SSI_SIER_TFE0_EN | \
10762306a36Sopenharmony_ci	 SSI_SIER_TLS_EN | \
10862306a36Sopenharmony_ci	 SSI_SIER_TFS_EN | \
10962306a36Sopenharmony_ci	 SSI_SIER_TUE0_EN | \
11062306a36Sopenharmony_ci	 SSI_SIER_TFRC_EN)
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cienum fsl_ssi_type {
11362306a36Sopenharmony_ci	FSL_SSI_MCP8610,
11462306a36Sopenharmony_ci	FSL_SSI_MX21,
11562306a36Sopenharmony_ci	FSL_SSI_MX35,
11662306a36Sopenharmony_ci	FSL_SSI_MX51,
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistruct fsl_ssi_regvals {
12062306a36Sopenharmony_ci	u32 sier;
12162306a36Sopenharmony_ci	u32 srcr;
12262306a36Sopenharmony_ci	u32 stcr;
12362306a36Sopenharmony_ci	u32 scr;
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	switch (reg) {
12962306a36Sopenharmony_ci	case REG_SSI_SACCEN:
13062306a36Sopenharmony_ci	case REG_SSI_SACCDIS:
13162306a36Sopenharmony_ci		return false;
13262306a36Sopenharmony_ci	default:
13362306a36Sopenharmony_ci		return true;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	switch (reg) {
14062306a36Sopenharmony_ci	case REG_SSI_STX0:
14162306a36Sopenharmony_ci	case REG_SSI_STX1:
14262306a36Sopenharmony_ci	case REG_SSI_SRX0:
14362306a36Sopenharmony_ci	case REG_SSI_SRX1:
14462306a36Sopenharmony_ci	case REG_SSI_SISR:
14562306a36Sopenharmony_ci	case REG_SSI_SFCSR:
14662306a36Sopenharmony_ci	case REG_SSI_SACNT:
14762306a36Sopenharmony_ci	case REG_SSI_SACADD:
14862306a36Sopenharmony_ci	case REG_SSI_SACDAT:
14962306a36Sopenharmony_ci	case REG_SSI_SATAG:
15062306a36Sopenharmony_ci	case REG_SSI_SACCST:
15162306a36Sopenharmony_ci	case REG_SSI_SOR:
15262306a36Sopenharmony_ci		return true;
15362306a36Sopenharmony_ci	default:
15462306a36Sopenharmony_ci		return false;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic bool fsl_ssi_precious_reg(struct device *dev, unsigned int reg)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	switch (reg) {
16162306a36Sopenharmony_ci	case REG_SSI_SRX0:
16262306a36Sopenharmony_ci	case REG_SSI_SRX1:
16362306a36Sopenharmony_ci	case REG_SSI_SISR:
16462306a36Sopenharmony_ci	case REG_SSI_SACADD:
16562306a36Sopenharmony_ci	case REG_SSI_SACDAT:
16662306a36Sopenharmony_ci	case REG_SSI_SATAG:
16762306a36Sopenharmony_ci		return true;
16862306a36Sopenharmony_ci	default:
16962306a36Sopenharmony_ci		return false;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	switch (reg) {
17662306a36Sopenharmony_ci	case REG_SSI_SRX0:
17762306a36Sopenharmony_ci	case REG_SSI_SRX1:
17862306a36Sopenharmony_ci	case REG_SSI_SACCST:
17962306a36Sopenharmony_ci		return false;
18062306a36Sopenharmony_ci	default:
18162306a36Sopenharmony_ci		return true;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic const struct regmap_config fsl_ssi_regconfig = {
18662306a36Sopenharmony_ci	.max_register = REG_SSI_SACCDIS,
18762306a36Sopenharmony_ci	.reg_bits = 32,
18862306a36Sopenharmony_ci	.val_bits = 32,
18962306a36Sopenharmony_ci	.reg_stride = 4,
19062306a36Sopenharmony_ci	.val_format_endian = REGMAP_ENDIAN_NATIVE,
19162306a36Sopenharmony_ci	.num_reg_defaults_raw = REG_SSI_SACCDIS / sizeof(uint32_t) + 1,
19262306a36Sopenharmony_ci	.readable_reg = fsl_ssi_readable_reg,
19362306a36Sopenharmony_ci	.volatile_reg = fsl_ssi_volatile_reg,
19462306a36Sopenharmony_ci	.precious_reg = fsl_ssi_precious_reg,
19562306a36Sopenharmony_ci	.writeable_reg = fsl_ssi_writeable_reg,
19662306a36Sopenharmony_ci	.cache_type = REGCACHE_FLAT,
19762306a36Sopenharmony_ci};
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistruct fsl_ssi_soc_data {
20062306a36Sopenharmony_ci	bool imx;
20162306a36Sopenharmony_ci	bool imx21regs; /* imx21-class SSI - no SACC{ST,EN,DIS} regs */
20262306a36Sopenharmony_ci	bool offline_config;
20362306a36Sopenharmony_ci	u32 sisr_write_mask;
20462306a36Sopenharmony_ci};
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci/**
20762306a36Sopenharmony_ci * struct fsl_ssi - per-SSI private data
20862306a36Sopenharmony_ci * @regs: Pointer to the regmap registers
20962306a36Sopenharmony_ci * @irq: IRQ of this SSI
21062306a36Sopenharmony_ci * @cpu_dai_drv: CPU DAI driver for this device
21162306a36Sopenharmony_ci * @dai_fmt: DAI configuration this device is currently used with
21262306a36Sopenharmony_ci * @streams: Mask of current active streams: BIT(TX) and BIT(RX)
21362306a36Sopenharmony_ci * @i2s_net: I2S and Network mode configurations of SCR register
21462306a36Sopenharmony_ci *           (this is the initial settings based on the DAI format)
21562306a36Sopenharmony_ci * @synchronous: Use synchronous mode - both of TX and RX use STCK and SFCK
21662306a36Sopenharmony_ci * @use_dma: DMA is used or FIQ with stream filter
21762306a36Sopenharmony_ci * @use_dual_fifo: DMA with support for dual FIFO mode
21862306a36Sopenharmony_ci * @use_dyna_fifo: DMA with support for multi FIFO script
21962306a36Sopenharmony_ci * @has_ipg_clk_name: If "ipg" is in the clock name list of device tree
22062306a36Sopenharmony_ci * @fifo_depth: Depth of the SSI FIFOs
22162306a36Sopenharmony_ci * @slot_width: Width of each DAI slot
22262306a36Sopenharmony_ci * @slots: Number of slots
22362306a36Sopenharmony_ci * @regvals: Specific RX/TX register settings
22462306a36Sopenharmony_ci * @clk: Clock source to access register
22562306a36Sopenharmony_ci * @baudclk: Clock source to generate bit and frame-sync clocks
22662306a36Sopenharmony_ci * @baudclk_streams: Active streams that are using baudclk
22762306a36Sopenharmony_ci * @regcache_sfcsr: Cache sfcsr register value during suspend and resume
22862306a36Sopenharmony_ci * @regcache_sacnt: Cache sacnt register value during suspend and resume
22962306a36Sopenharmony_ci * @dma_params_tx: DMA transmit parameters
23062306a36Sopenharmony_ci * @dma_params_rx: DMA receive parameters
23162306a36Sopenharmony_ci * @ssi_phys: physical address of the SSI registers
23262306a36Sopenharmony_ci * @fiq_params: FIQ stream filtering parameters
23362306a36Sopenharmony_ci * @card_pdev: Platform_device pointer to register a sound card for PowerPC or
23462306a36Sopenharmony_ci *             to register a CODEC platform device for AC97
23562306a36Sopenharmony_ci * @card_name: Platform_device name to register a sound card for PowerPC or
23662306a36Sopenharmony_ci *             to register a CODEC platform device for AC97
23762306a36Sopenharmony_ci * @card_idx: The index of SSI to register a sound card for PowerPC or
23862306a36Sopenharmony_ci *            to register a CODEC platform device for AC97
23962306a36Sopenharmony_ci * @dbg_stats: Debugging statistics
24062306a36Sopenharmony_ci * @soc: SoC specific data
24162306a36Sopenharmony_ci * @dev: Pointer to &pdev->dev
24262306a36Sopenharmony_ci * @fifo_watermark: The FIFO watermark setting. Notifies DMA when there are
24362306a36Sopenharmony_ci *                  @fifo_watermark or fewer words in TX fifo or
24462306a36Sopenharmony_ci *                  @fifo_watermark or more empty words in RX fifo.
24562306a36Sopenharmony_ci * @dma_maxburst: Max number of words to transfer in one go. So far,
24662306a36Sopenharmony_ci *                this is always the same as fifo_watermark.
24762306a36Sopenharmony_ci * @ac97_reg_lock: Mutex lock to serialize AC97 register access operations
24862306a36Sopenharmony_ci * @audio_config: configure for dma multi fifo script
24962306a36Sopenharmony_ci */
25062306a36Sopenharmony_cistruct fsl_ssi {
25162306a36Sopenharmony_ci	struct regmap *regs;
25262306a36Sopenharmony_ci	int irq;
25362306a36Sopenharmony_ci	struct snd_soc_dai_driver cpu_dai_drv;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	unsigned int dai_fmt;
25662306a36Sopenharmony_ci	u8 streams;
25762306a36Sopenharmony_ci	u8 i2s_net;
25862306a36Sopenharmony_ci	bool synchronous;
25962306a36Sopenharmony_ci	bool use_dma;
26062306a36Sopenharmony_ci	bool use_dual_fifo;
26162306a36Sopenharmony_ci	bool use_dyna_fifo;
26262306a36Sopenharmony_ci	bool has_ipg_clk_name;
26362306a36Sopenharmony_ci	unsigned int fifo_depth;
26462306a36Sopenharmony_ci	unsigned int slot_width;
26562306a36Sopenharmony_ci	unsigned int slots;
26662306a36Sopenharmony_ci	struct fsl_ssi_regvals regvals[2];
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	struct clk *clk;
26962306a36Sopenharmony_ci	struct clk *baudclk;
27062306a36Sopenharmony_ci	unsigned int baudclk_streams;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	u32 regcache_sfcsr;
27362306a36Sopenharmony_ci	u32 regcache_sacnt;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	struct snd_dmaengine_dai_dma_data dma_params_tx;
27662306a36Sopenharmony_ci	struct snd_dmaengine_dai_dma_data dma_params_rx;
27762306a36Sopenharmony_ci	dma_addr_t ssi_phys;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	struct imx_pcm_fiq_params fiq_params;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	struct platform_device *card_pdev;
28262306a36Sopenharmony_ci	char card_name[32];
28362306a36Sopenharmony_ci	u32 card_idx;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	struct fsl_ssi_dbg dbg_stats;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	const struct fsl_ssi_soc_data *soc;
28862306a36Sopenharmony_ci	struct device *dev;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	u32 fifo_watermark;
29162306a36Sopenharmony_ci	u32 dma_maxburst;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	struct mutex ac97_reg_lock;
29462306a36Sopenharmony_ci	struct sdma_peripheral_config audio_config[2];
29562306a36Sopenharmony_ci};
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/*
29862306a36Sopenharmony_ci * SoC specific data
29962306a36Sopenharmony_ci *
30062306a36Sopenharmony_ci * Notes:
30162306a36Sopenharmony_ci * 1) SSI in earlier SoCS has critical bits in control registers that
30262306a36Sopenharmony_ci *    cannot be changed after SSI starts running -- a software reset
30362306a36Sopenharmony_ci *    (set SSIEN to 0) is required to change their values. So adding
30462306a36Sopenharmony_ci *    an offline_config flag for these SoCs.
30562306a36Sopenharmony_ci * 2) SDMA is available since imx35. However, imx35 does not support
30662306a36Sopenharmony_ci *    DMA bits changing when SSI is running, so set offline_config.
30762306a36Sopenharmony_ci * 3) imx51 and later versions support register configurations when
30862306a36Sopenharmony_ci *    SSI is running (SSIEN); For these versions, DMA needs to be
30962306a36Sopenharmony_ci *    configured before SSI sends DMA request to avoid an undefined
31062306a36Sopenharmony_ci *    DMA request on the SDMA side.
31162306a36Sopenharmony_ci */
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic struct fsl_ssi_soc_data fsl_ssi_mpc8610 = {
31462306a36Sopenharmony_ci	.imx = false,
31562306a36Sopenharmony_ci	.offline_config = true,
31662306a36Sopenharmony_ci	.sisr_write_mask = SSI_SISR_RFRC | SSI_SISR_TFRC |
31762306a36Sopenharmony_ci			   SSI_SISR_ROE0 | SSI_SISR_ROE1 |
31862306a36Sopenharmony_ci			   SSI_SISR_TUE0 | SSI_SISR_TUE1,
31962306a36Sopenharmony_ci};
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic struct fsl_ssi_soc_data fsl_ssi_imx21 = {
32262306a36Sopenharmony_ci	.imx = true,
32362306a36Sopenharmony_ci	.imx21regs = true,
32462306a36Sopenharmony_ci	.offline_config = true,
32562306a36Sopenharmony_ci	.sisr_write_mask = 0,
32662306a36Sopenharmony_ci};
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic struct fsl_ssi_soc_data fsl_ssi_imx35 = {
32962306a36Sopenharmony_ci	.imx = true,
33062306a36Sopenharmony_ci	.offline_config = true,
33162306a36Sopenharmony_ci	.sisr_write_mask = SSI_SISR_RFRC | SSI_SISR_TFRC |
33262306a36Sopenharmony_ci			   SSI_SISR_ROE0 | SSI_SISR_ROE1 |
33362306a36Sopenharmony_ci			   SSI_SISR_TUE0 | SSI_SISR_TUE1,
33462306a36Sopenharmony_ci};
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic struct fsl_ssi_soc_data fsl_ssi_imx51 = {
33762306a36Sopenharmony_ci	.imx = true,
33862306a36Sopenharmony_ci	.offline_config = false,
33962306a36Sopenharmony_ci	.sisr_write_mask = SSI_SISR_ROE0 | SSI_SISR_ROE1 |
34062306a36Sopenharmony_ci			   SSI_SISR_TUE0 | SSI_SISR_TUE1,
34162306a36Sopenharmony_ci};
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic const struct of_device_id fsl_ssi_ids[] = {
34462306a36Sopenharmony_ci	{ .compatible = "fsl,mpc8610-ssi", .data = &fsl_ssi_mpc8610 },
34562306a36Sopenharmony_ci	{ .compatible = "fsl,imx51-ssi", .data = &fsl_ssi_imx51 },
34662306a36Sopenharmony_ci	{ .compatible = "fsl,imx35-ssi", .data = &fsl_ssi_imx35 },
34762306a36Sopenharmony_ci	{ .compatible = "fsl,imx21-ssi", .data = &fsl_ssi_imx21 },
34862306a36Sopenharmony_ci	{}
34962306a36Sopenharmony_ci};
35062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_ssi_ids);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic bool fsl_ssi_is_ac97(struct fsl_ssi *ssi)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	return (ssi->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
35562306a36Sopenharmony_ci		SND_SOC_DAIFMT_AC97;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic bool fsl_ssi_is_i2s_clock_provider(struct fsl_ssi *ssi)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	return (ssi->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) ==
36162306a36Sopenharmony_ci		SND_SOC_DAIFMT_BP_FP;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic bool fsl_ssi_is_i2s_bc_fp(struct fsl_ssi *ssi)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	return (ssi->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) ==
36762306a36Sopenharmony_ci		SND_SOC_DAIFMT_BC_FP;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci/**
37162306a36Sopenharmony_ci * fsl_ssi_isr - Interrupt handler to gather states
37262306a36Sopenharmony_ci * @irq: irq number
37362306a36Sopenharmony_ci * @dev_id: context
37462306a36Sopenharmony_ci */
37562306a36Sopenharmony_cistatic irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct fsl_ssi *ssi = dev_id;
37862306a36Sopenharmony_ci	struct regmap *regs = ssi->regs;
37962306a36Sopenharmony_ci	u32 sisr, sisr2;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	regmap_read(regs, REG_SSI_SISR, &sisr);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	sisr2 = sisr & ssi->soc->sisr_write_mask;
38462306a36Sopenharmony_ci	/* Clear the bits that we set */
38562306a36Sopenharmony_ci	if (sisr2)
38662306a36Sopenharmony_ci		regmap_write(regs, REG_SSI_SISR, sisr2);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	fsl_ssi_dbg_isr(&ssi->dbg_stats, sisr);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return IRQ_HANDLED;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci/**
39462306a36Sopenharmony_ci * fsl_ssi_config_enable - Set SCR, SIER, STCR and SRCR registers with
39562306a36Sopenharmony_ci * cached values in regvals
39662306a36Sopenharmony_ci * @ssi: SSI context
39762306a36Sopenharmony_ci * @tx: direction
39862306a36Sopenharmony_ci *
39962306a36Sopenharmony_ci * Notes:
40062306a36Sopenharmony_ci * 1) For offline_config SoCs, enable all necessary bits of both streams
40162306a36Sopenharmony_ci *    when 1st stream starts, even if the opposite stream will not start
40262306a36Sopenharmony_ci * 2) It also clears FIFO before setting regvals; SOR is safe to set online
40362306a36Sopenharmony_ci */
40462306a36Sopenharmony_cistatic void fsl_ssi_config_enable(struct fsl_ssi *ssi, bool tx)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct fsl_ssi_regvals *vals = ssi->regvals;
40762306a36Sopenharmony_ci	int dir = tx ? TX : RX;
40862306a36Sopenharmony_ci	u32 sier, srcr, stcr;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* Clear dirty data in the FIFO; It also prevents channel slipping */
41162306a36Sopenharmony_ci	regmap_update_bits(ssi->regs, REG_SSI_SOR,
41262306a36Sopenharmony_ci			   SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	/*
41562306a36Sopenharmony_ci	 * On offline_config SoCs, SxCR and SIER are already configured when
41662306a36Sopenharmony_ci	 * the previous stream started. So skip all SxCR and SIER settings
41762306a36Sopenharmony_ci	 * to prevent online reconfigurations, then jump to set SCR directly
41862306a36Sopenharmony_ci	 */
41962306a36Sopenharmony_ci	if (ssi->soc->offline_config && ssi->streams)
42062306a36Sopenharmony_ci		goto enable_scr;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (ssi->soc->offline_config) {
42362306a36Sopenharmony_ci		/*
42462306a36Sopenharmony_ci		 * Online reconfiguration not supported, so enable all bits for
42562306a36Sopenharmony_ci		 * both streams at once to avoid necessity of reconfigurations
42662306a36Sopenharmony_ci		 */
42762306a36Sopenharmony_ci		srcr = vals[RX].srcr | vals[TX].srcr;
42862306a36Sopenharmony_ci		stcr = vals[RX].stcr | vals[TX].stcr;
42962306a36Sopenharmony_ci		sier = vals[RX].sier | vals[TX].sier;
43062306a36Sopenharmony_ci	} else {
43162306a36Sopenharmony_ci		/* Otherwise, only set bits for the current stream */
43262306a36Sopenharmony_ci		srcr = vals[dir].srcr;
43362306a36Sopenharmony_ci		stcr = vals[dir].stcr;
43462306a36Sopenharmony_ci		sier = vals[dir].sier;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	/* Configure SRCR, STCR and SIER at once */
43862306a36Sopenharmony_ci	regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, srcr);
43962306a36Sopenharmony_ci	regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, stcr);
44062306a36Sopenharmony_ci	regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, sier);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cienable_scr:
44362306a36Sopenharmony_ci	/*
44462306a36Sopenharmony_ci	 * Start DMA before setting TE to avoid FIFO underrun
44562306a36Sopenharmony_ci	 * which may cause a channel slip or a channel swap
44662306a36Sopenharmony_ci	 *
44762306a36Sopenharmony_ci	 * TODO: FIQ cases might also need this upon testing
44862306a36Sopenharmony_ci	 */
44962306a36Sopenharmony_ci	if (ssi->use_dma && tx) {
45062306a36Sopenharmony_ci		int try = 100;
45162306a36Sopenharmony_ci		u32 sfcsr;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		/* Enable SSI first to send TX DMA request */
45462306a36Sopenharmony_ci		regmap_update_bits(ssi->regs, REG_SSI_SCR,
45562306a36Sopenharmony_ci				   SSI_SCR_SSIEN, SSI_SCR_SSIEN);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci		/* Busy wait until TX FIFO not empty -- DMA working */
45862306a36Sopenharmony_ci		do {
45962306a36Sopenharmony_ci			regmap_read(ssi->regs, REG_SSI_SFCSR, &sfcsr);
46062306a36Sopenharmony_ci			if (SSI_SFCSR_TFCNT0(sfcsr))
46162306a36Sopenharmony_ci				break;
46262306a36Sopenharmony_ci		} while (--try);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		/* FIFO still empty -- something might be wrong */
46562306a36Sopenharmony_ci		if (!SSI_SFCSR_TFCNT0(sfcsr))
46662306a36Sopenharmony_ci			dev_warn(ssi->dev, "Timeout waiting TX FIFO filling\n");
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci	/* Enable all remaining bits in SCR */
46962306a36Sopenharmony_ci	regmap_update_bits(ssi->regs, REG_SSI_SCR,
47062306a36Sopenharmony_ci			   vals[dir].scr, vals[dir].scr);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	/* Log the enabled stream to the mask */
47362306a36Sopenharmony_ci	ssi->streams |= BIT(dir);
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci/*
47762306a36Sopenharmony_ci * Exclude bits that are used by the opposite stream
47862306a36Sopenharmony_ci *
47962306a36Sopenharmony_ci * When both streams are active, disabling some bits for the current stream
48062306a36Sopenharmony_ci * might break the other stream if these bits are used by it.
48162306a36Sopenharmony_ci *
48262306a36Sopenharmony_ci * @vals : regvals of the current stream
48362306a36Sopenharmony_ci * @avals: regvals of the opposite stream
48462306a36Sopenharmony_ci * @aactive: active state of the opposite stream
48562306a36Sopenharmony_ci *
48662306a36Sopenharmony_ci *  1) XOR vals and avals to get the differences if the other stream is active;
48762306a36Sopenharmony_ci *     Otherwise, return current vals if the other stream is not active
48862306a36Sopenharmony_ci *  2) AND the result of 1) with the current vals
48962306a36Sopenharmony_ci */
49062306a36Sopenharmony_ci#define _ssi_xor_shared_bits(vals, avals, aactive) \
49162306a36Sopenharmony_ci	((vals) ^ ((avals) * (aactive)))
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci#define ssi_excl_shared_bits(vals, avals, aactive) \
49462306a36Sopenharmony_ci	((vals) & _ssi_xor_shared_bits(vals, avals, aactive))
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci/**
49762306a36Sopenharmony_ci * fsl_ssi_config_disable - Unset SCR, SIER, STCR and SRCR registers
49862306a36Sopenharmony_ci * with cached values in regvals
49962306a36Sopenharmony_ci * @ssi: SSI context
50062306a36Sopenharmony_ci * @tx: direction
50162306a36Sopenharmony_ci *
50262306a36Sopenharmony_ci * Notes:
50362306a36Sopenharmony_ci * 1) For offline_config SoCs, to avoid online reconfigurations, disable all
50462306a36Sopenharmony_ci *    bits of both streams at once when the last stream is abort to end
50562306a36Sopenharmony_ci * 2) It also clears FIFO after unsetting regvals; SOR is safe to set online
50662306a36Sopenharmony_ci */
50762306a36Sopenharmony_cistatic void fsl_ssi_config_disable(struct fsl_ssi *ssi, bool tx)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct fsl_ssi_regvals *vals, *avals;
51062306a36Sopenharmony_ci	u32 sier, srcr, stcr, scr;
51162306a36Sopenharmony_ci	int adir = tx ? RX : TX;
51262306a36Sopenharmony_ci	int dir = tx ? TX : RX;
51362306a36Sopenharmony_ci	bool aactive;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	/* Check if the opposite stream is active */
51662306a36Sopenharmony_ci	aactive = ssi->streams & BIT(adir);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	vals = &ssi->regvals[dir];
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	/* Get regvals of the opposite stream to keep opposite stream safe */
52162306a36Sopenharmony_ci	avals = &ssi->regvals[adir];
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/*
52462306a36Sopenharmony_ci	 * To keep the other stream safe, exclude shared bits between
52562306a36Sopenharmony_ci	 * both streams, and get safe bits to disable current stream
52662306a36Sopenharmony_ci	 */
52762306a36Sopenharmony_ci	scr = ssi_excl_shared_bits(vals->scr, avals->scr, aactive);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	/* Disable safe bits of SCR register for the current stream */
53062306a36Sopenharmony_ci	regmap_update_bits(ssi->regs, REG_SSI_SCR, scr, 0);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Log the disabled stream to the mask */
53362306a36Sopenharmony_ci	ssi->streams &= ~BIT(dir);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/*
53662306a36Sopenharmony_ci	 * On offline_config SoCs, if the other stream is active, skip
53762306a36Sopenharmony_ci	 * SxCR and SIER settings to prevent online reconfigurations
53862306a36Sopenharmony_ci	 */
53962306a36Sopenharmony_ci	if (ssi->soc->offline_config && aactive)
54062306a36Sopenharmony_ci		goto fifo_clear;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (ssi->soc->offline_config) {
54362306a36Sopenharmony_ci		/* Now there is only current stream active, disable all bits */
54462306a36Sopenharmony_ci		srcr = vals->srcr | avals->srcr;
54562306a36Sopenharmony_ci		stcr = vals->stcr | avals->stcr;
54662306a36Sopenharmony_ci		sier = vals->sier | avals->sier;
54762306a36Sopenharmony_ci	} else {
54862306a36Sopenharmony_ci		/*
54962306a36Sopenharmony_ci		 * To keep the other stream safe, exclude shared bits between
55062306a36Sopenharmony_ci		 * both streams, and get safe bits to disable current stream
55162306a36Sopenharmony_ci		 */
55262306a36Sopenharmony_ci		sier = ssi_excl_shared_bits(vals->sier, avals->sier, aactive);
55362306a36Sopenharmony_ci		srcr = ssi_excl_shared_bits(vals->srcr, avals->srcr, aactive);
55462306a36Sopenharmony_ci		stcr = ssi_excl_shared_bits(vals->stcr, avals->stcr, aactive);
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/* Clear configurations of SRCR, STCR and SIER at once */
55862306a36Sopenharmony_ci	regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, 0);
55962306a36Sopenharmony_ci	regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, 0);
56062306a36Sopenharmony_ci	regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, 0);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cififo_clear:
56362306a36Sopenharmony_ci	/* Clear remaining data in the FIFO */
56462306a36Sopenharmony_ci	regmap_update_bits(ssi->regs, REG_SSI_SOR,
56562306a36Sopenharmony_ci			   SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct regmap *regs = ssi->regs;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* no SACC{ST,EN,DIS} regs on imx21-class SSI */
57362306a36Sopenharmony_ci	if (!ssi->soc->imx21regs) {
57462306a36Sopenharmony_ci		/* Disable all channel slots */
57562306a36Sopenharmony_ci		regmap_write(regs, REG_SSI_SACCDIS, 0xff);
57662306a36Sopenharmony_ci		/* Enable slots 3 & 4 -- PCM Playback Left & Right channels */
57762306a36Sopenharmony_ci		regmap_write(regs, REG_SSI_SACCEN, 0x300);
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci/**
58262306a36Sopenharmony_ci * fsl_ssi_setup_regvals - Cache critical bits of SIER, SRCR, STCR and
58362306a36Sopenharmony_ci * SCR to later set them safely
58462306a36Sopenharmony_ci * @ssi: SSI context
58562306a36Sopenharmony_ci */
58662306a36Sopenharmony_cistatic void fsl_ssi_setup_regvals(struct fsl_ssi *ssi)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	struct fsl_ssi_regvals *vals = ssi->regvals;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	vals[RX].sier = SSI_SIER_RFF0_EN | FSLSSI_SIER_DBG_RX_FLAGS;
59162306a36Sopenharmony_ci	vals[RX].srcr = SSI_SRCR_RFEN0;
59262306a36Sopenharmony_ci	vals[RX].scr = SSI_SCR_SSIEN | SSI_SCR_RE;
59362306a36Sopenharmony_ci	vals[TX].sier = SSI_SIER_TFE0_EN | FSLSSI_SIER_DBG_TX_FLAGS;
59462306a36Sopenharmony_ci	vals[TX].stcr = SSI_STCR_TFEN0;
59562306a36Sopenharmony_ci	vals[TX].scr = SSI_SCR_SSIEN | SSI_SCR_TE;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	/* AC97 has already enabled SSIEN, RE and TE, so ignore them */
59862306a36Sopenharmony_ci	if (fsl_ssi_is_ac97(ssi))
59962306a36Sopenharmony_ci		vals[RX].scr = vals[TX].scr = 0;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if (ssi->use_dual_fifo) {
60262306a36Sopenharmony_ci		vals[RX].srcr |= SSI_SRCR_RFEN1;
60362306a36Sopenharmony_ci		vals[TX].stcr |= SSI_STCR_TFEN1;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (ssi->use_dma) {
60762306a36Sopenharmony_ci		vals[RX].sier |= SSI_SIER_RDMAE;
60862306a36Sopenharmony_ci		vals[TX].sier |= SSI_SIER_TDMAE;
60962306a36Sopenharmony_ci	} else {
61062306a36Sopenharmony_ci		vals[RX].sier |= SSI_SIER_RIE;
61162306a36Sopenharmony_ci		vals[TX].sier |= SSI_SIER_TIE;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic void fsl_ssi_setup_ac97(struct fsl_ssi *ssi)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	struct regmap *regs = ssi->regs;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	/* Setup the clock control register */
62062306a36Sopenharmony_ci	regmap_write(regs, REG_SSI_STCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13));
62162306a36Sopenharmony_ci	regmap_write(regs, REG_SSI_SRCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13));
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/* Enable AC97 mode and startup the SSI */
62462306a36Sopenharmony_ci	regmap_write(regs, REG_SSI_SACNT, SSI_SACNT_AC97EN | SSI_SACNT_FV);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	/* AC97 has to communicate with codec before starting a stream */
62762306a36Sopenharmony_ci	regmap_update_bits(regs, REG_SSI_SCR,
62862306a36Sopenharmony_ci			   SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE,
62962306a36Sopenharmony_ci			   SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	regmap_write(regs, REG_SSI_SOR, SSI_SOR_WAIT(3));
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_cistatic int fsl_ssi_startup(struct snd_pcm_substream *substream,
63562306a36Sopenharmony_ci			   struct snd_soc_dai *dai)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
63862306a36Sopenharmony_ci	struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
63962306a36Sopenharmony_ci	int ret;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	ret = clk_prepare_enable(ssi->clk);
64262306a36Sopenharmony_ci	if (ret)
64362306a36Sopenharmony_ci		return ret;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	/*
64662306a36Sopenharmony_ci	 * When using dual fifo mode, it is safer to ensure an even period
64762306a36Sopenharmony_ci	 * size. If appearing to an odd number while DMA always starts its
64862306a36Sopenharmony_ci	 * task from fifo0, fifo1 would be neglected at the end of each
64962306a36Sopenharmony_ci	 * period. But SSI would still access fifo1 with an invalid data.
65062306a36Sopenharmony_ci	 */
65162306a36Sopenharmony_ci	if (ssi->use_dual_fifo || ssi->use_dyna_fifo)
65262306a36Sopenharmony_ci		snd_pcm_hw_constraint_step(substream->runtime, 0,
65362306a36Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	return 0;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_cistatic void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
65962306a36Sopenharmony_ci			     struct snd_soc_dai *dai)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
66262306a36Sopenharmony_ci	struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	clk_disable_unprepare(ssi->clk);
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci/**
66862306a36Sopenharmony_ci * fsl_ssi_set_bclk - Configure Digital Audio Interface bit clock
66962306a36Sopenharmony_ci * @substream: ASoC substream
67062306a36Sopenharmony_ci * @dai: pointer to DAI
67162306a36Sopenharmony_ci * @hw_params: pointers to hw_params
67262306a36Sopenharmony_ci *
67362306a36Sopenharmony_ci * Notes: This function can be only called when using SSI as DAI master
67462306a36Sopenharmony_ci *
67562306a36Sopenharmony_ci * Quick instruction for parameters:
67662306a36Sopenharmony_ci * freq: Output BCLK frequency = samplerate * slots * slot_width
67762306a36Sopenharmony_ci *       (In 2-channel I2S Master mode, slot_width is fixed 32)
67862306a36Sopenharmony_ci */
67962306a36Sopenharmony_cistatic int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
68062306a36Sopenharmony_ci			    struct snd_soc_dai *dai,
68162306a36Sopenharmony_ci			    struct snd_pcm_hw_params *hw_params)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
68462306a36Sopenharmony_ci	struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
68562306a36Sopenharmony_ci	struct regmap *regs = ssi->regs;
68662306a36Sopenharmony_ci	u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i;
68762306a36Sopenharmony_ci	unsigned long clkrate, baudrate, tmprate;
68862306a36Sopenharmony_ci	unsigned int channels = params_channels(hw_params);
68962306a36Sopenharmony_ci	unsigned int slot_width = params_width(hw_params);
69062306a36Sopenharmony_ci	unsigned int slots = 2;
69162306a36Sopenharmony_ci	u64 sub, savesub = 100000;
69262306a36Sopenharmony_ci	unsigned int freq;
69362306a36Sopenharmony_ci	bool baudclk_is_used;
69462306a36Sopenharmony_ci	int ret;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	/* Override slots and slot_width if being specifically set... */
69762306a36Sopenharmony_ci	if (ssi->slots)
69862306a36Sopenharmony_ci		slots = ssi->slots;
69962306a36Sopenharmony_ci	if (ssi->slot_width)
70062306a36Sopenharmony_ci		slot_width = ssi->slot_width;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	/* ...but force 32 bits for stereo audio using I2S Master Mode */
70362306a36Sopenharmony_ci	if (channels == 2 &&
70462306a36Sopenharmony_ci	    (ssi->i2s_net & SSI_SCR_I2S_MODE_MASK) == SSI_SCR_I2S_MODE_MASTER)
70562306a36Sopenharmony_ci		slot_width = 32;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	/* Generate bit clock based on the slot number and slot width */
70862306a36Sopenharmony_ci	freq = slots * slot_width * params_rate(hw_params);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/* Don't apply it to any non-baudclk circumstance */
71162306a36Sopenharmony_ci	if (IS_ERR(ssi->baudclk))
71262306a36Sopenharmony_ci		return -EINVAL;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	/*
71562306a36Sopenharmony_ci	 * Hardware limitation: The bclk rate must be
71662306a36Sopenharmony_ci	 * never greater than 1/5 IPG clock rate
71762306a36Sopenharmony_ci	 */
71862306a36Sopenharmony_ci	if (freq * 5 > clk_get_rate(ssi->clk)) {
71962306a36Sopenharmony_ci		dev_err(dai->dev, "bitclk > ipgclk / 5\n");
72062306a36Sopenharmony_ci		return -EINVAL;
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	baudclk_is_used = ssi->baudclk_streams & ~(BIT(substream->stream));
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	/* It should be already enough to divide clock by setting pm alone */
72662306a36Sopenharmony_ci	psr = 0;
72762306a36Sopenharmony_ci	div2 = 0;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	factor = (div2 + 1) * (7 * psr + 1) * 2;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	for (i = 0; i < 255; i++) {
73262306a36Sopenharmony_ci		tmprate = freq * factor * (i + 1);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci		if (baudclk_is_used)
73562306a36Sopenharmony_ci			clkrate = clk_get_rate(ssi->baudclk);
73662306a36Sopenharmony_ci		else
73762306a36Sopenharmony_ci			clkrate = clk_round_rate(ssi->baudclk, tmprate);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci		clkrate /= factor;
74062306a36Sopenharmony_ci		afreq = clkrate / (i + 1);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		if (freq == afreq)
74362306a36Sopenharmony_ci			sub = 0;
74462306a36Sopenharmony_ci		else if (freq / afreq == 1)
74562306a36Sopenharmony_ci			sub = freq - afreq;
74662306a36Sopenharmony_ci		else if (afreq / freq == 1)
74762306a36Sopenharmony_ci			sub = afreq - freq;
74862306a36Sopenharmony_ci		else
74962306a36Sopenharmony_ci			continue;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci		/* Calculate the fraction */
75262306a36Sopenharmony_ci		sub *= 100000;
75362306a36Sopenharmony_ci		do_div(sub, freq);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci		if (sub < savesub && !(i == 0)) {
75662306a36Sopenharmony_ci			baudrate = tmprate;
75762306a36Sopenharmony_ci			savesub = sub;
75862306a36Sopenharmony_ci			pm = i;
75962306a36Sopenharmony_ci		}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci		/* We are lucky */
76262306a36Sopenharmony_ci		if (savesub == 0)
76362306a36Sopenharmony_ci			break;
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	/* No proper pm found if it is still remaining the initial value */
76762306a36Sopenharmony_ci	if (pm == 999) {
76862306a36Sopenharmony_ci		dev_err(dai->dev, "failed to handle the required sysclk\n");
76962306a36Sopenharmony_ci		return -EINVAL;
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	stccr = SSI_SxCCR_PM(pm + 1);
77362306a36Sopenharmony_ci	mask = SSI_SxCCR_PM_MASK | SSI_SxCCR_DIV2 | SSI_SxCCR_PSR;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	/* STCCR is used for RX in synchronous mode */
77662306a36Sopenharmony_ci	tx2 = tx || ssi->synchronous;
77762306a36Sopenharmony_ci	regmap_update_bits(regs, REG_SSI_SxCCR(tx2), mask, stccr);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	if (!baudclk_is_used) {
78062306a36Sopenharmony_ci		ret = clk_set_rate(ssi->baudclk, baudrate);
78162306a36Sopenharmony_ci		if (ret) {
78262306a36Sopenharmony_ci			dev_err(dai->dev, "failed to set baudclk rate\n");
78362306a36Sopenharmony_ci			return -EINVAL;
78462306a36Sopenharmony_ci		}
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	return 0;
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci/**
79162306a36Sopenharmony_ci * fsl_ssi_hw_params - Configure SSI based on PCM hardware parameters
79262306a36Sopenharmony_ci * @substream: ASoC substream
79362306a36Sopenharmony_ci * @hw_params: pointers to hw_params
79462306a36Sopenharmony_ci * @dai: pointer to DAI
79562306a36Sopenharmony_ci *
79662306a36Sopenharmony_ci * Notes:
79762306a36Sopenharmony_ci * 1) SxCCR.WL bits are critical bits that require SSI to be temporarily
79862306a36Sopenharmony_ci *    disabled on offline_config SoCs. Even for online configurable SoCs
79962306a36Sopenharmony_ci *    running in synchronous mode (both TX and RX use STCCR), it is not
80062306a36Sopenharmony_ci *    safe to re-configure them when both two streams start running.
80162306a36Sopenharmony_ci * 2) SxCCR.PM, SxCCR.DIV2 and SxCCR.PSR bits will be configured in the
80262306a36Sopenharmony_ci *    fsl_ssi_set_bclk() if SSI is the DAI clock master.
80362306a36Sopenharmony_ci */
80462306a36Sopenharmony_cistatic int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
80562306a36Sopenharmony_ci			     struct snd_pcm_hw_params *hw_params,
80662306a36Sopenharmony_ci			     struct snd_soc_dai *dai)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
80962306a36Sopenharmony_ci	struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
81062306a36Sopenharmony_ci	struct fsl_ssi_regvals *vals = ssi->regvals;
81162306a36Sopenharmony_ci	struct regmap *regs = ssi->regs;
81262306a36Sopenharmony_ci	unsigned int channels = params_channels(hw_params);
81362306a36Sopenharmony_ci	unsigned int sample_size = params_width(hw_params);
81462306a36Sopenharmony_ci	u32 wl = SSI_SxCCR_WL(sample_size);
81562306a36Sopenharmony_ci	int ret;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	if (fsl_ssi_is_i2s_clock_provider(ssi)) {
81862306a36Sopenharmony_ci		ret = fsl_ssi_set_bclk(substream, dai, hw_params);
81962306a36Sopenharmony_ci		if (ret)
82062306a36Sopenharmony_ci			return ret;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci		/* Do not enable the clock if it is already enabled */
82362306a36Sopenharmony_ci		if (!(ssi->baudclk_streams & BIT(substream->stream))) {
82462306a36Sopenharmony_ci			ret = clk_prepare_enable(ssi->baudclk);
82562306a36Sopenharmony_ci			if (ret)
82662306a36Sopenharmony_ci				return ret;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci			ssi->baudclk_streams |= BIT(substream->stream);
82962306a36Sopenharmony_ci		}
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	/*
83362306a36Sopenharmony_ci	 * SSI is properly configured if it is enabled and running in
83462306a36Sopenharmony_ci	 * the synchronous mode; Note that AC97 mode is an exception
83562306a36Sopenharmony_ci	 * that should set separate configurations for STCCR and SRCCR
83662306a36Sopenharmony_ci	 * despite running in the synchronous mode.
83762306a36Sopenharmony_ci	 */
83862306a36Sopenharmony_ci	if (ssi->streams && ssi->synchronous)
83962306a36Sopenharmony_ci		return 0;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (!fsl_ssi_is_ac97(ssi)) {
84262306a36Sopenharmony_ci		/*
84362306a36Sopenharmony_ci		 * Keep the ssi->i2s_net intact while having a local variable
84462306a36Sopenharmony_ci		 * to override settings for special use cases. Otherwise, the
84562306a36Sopenharmony_ci		 * ssi->i2s_net will lose the settings for regular use cases.
84662306a36Sopenharmony_ci		 */
84762306a36Sopenharmony_ci		u8 i2s_net = ssi->i2s_net;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci		/* Normal + Network mode to send 16-bit data in 32-bit frames */
85062306a36Sopenharmony_ci		if (fsl_ssi_is_i2s_bc_fp(ssi) && sample_size == 16)
85162306a36Sopenharmony_ci			i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci		/* Use Normal mode to send mono data at 1st slot of 2 slots */
85462306a36Sopenharmony_ci		if (channels == 1)
85562306a36Sopenharmony_ci			i2s_net = SSI_SCR_I2S_MODE_NORMAL;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci		regmap_update_bits(regs, REG_SSI_SCR,
85862306a36Sopenharmony_ci				   SSI_SCR_I2S_NET_MASK, i2s_net);
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	/* In synchronous mode, the SSI uses STCCR for capture */
86262306a36Sopenharmony_ci	tx2 = tx || ssi->synchronous;
86362306a36Sopenharmony_ci	regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	if (ssi->use_dyna_fifo) {
86662306a36Sopenharmony_ci		if (channels == 1) {
86762306a36Sopenharmony_ci			ssi->audio_config[0].n_fifos_dst = 1;
86862306a36Sopenharmony_ci			ssi->audio_config[1].n_fifos_src = 1;
86962306a36Sopenharmony_ci			vals[RX].srcr &= ~SSI_SRCR_RFEN1;
87062306a36Sopenharmony_ci			vals[TX].stcr &= ~SSI_STCR_TFEN1;
87162306a36Sopenharmony_ci			vals[RX].scr  &= ~SSI_SCR_TCH_EN;
87262306a36Sopenharmony_ci			vals[TX].scr  &= ~SSI_SCR_TCH_EN;
87362306a36Sopenharmony_ci		} else {
87462306a36Sopenharmony_ci			ssi->audio_config[0].n_fifos_dst = 2;
87562306a36Sopenharmony_ci			ssi->audio_config[1].n_fifos_src = 2;
87662306a36Sopenharmony_ci			vals[RX].srcr |= SSI_SRCR_RFEN1;
87762306a36Sopenharmony_ci			vals[TX].stcr |= SSI_STCR_TFEN1;
87862306a36Sopenharmony_ci			vals[RX].scr  |= SSI_SCR_TCH_EN;
87962306a36Sopenharmony_ci			vals[TX].scr  |= SSI_SCR_TCH_EN;
88062306a36Sopenharmony_ci		}
88162306a36Sopenharmony_ci		ssi->dma_params_tx.peripheral_config = &ssi->audio_config[0];
88262306a36Sopenharmony_ci		ssi->dma_params_tx.peripheral_size = sizeof(ssi->audio_config[0]);
88362306a36Sopenharmony_ci		ssi->dma_params_rx.peripheral_config = &ssi->audio_config[1];
88462306a36Sopenharmony_ci		ssi->dma_params_rx.peripheral_size = sizeof(ssi->audio_config[1]);
88562306a36Sopenharmony_ci	}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	return 0;
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_cistatic int fsl_ssi_hw_free(struct snd_pcm_substream *substream,
89162306a36Sopenharmony_ci			   struct snd_soc_dai *dai)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
89462306a36Sopenharmony_ci	struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	if (fsl_ssi_is_i2s_clock_provider(ssi) &&
89762306a36Sopenharmony_ci	    ssi->baudclk_streams & BIT(substream->stream)) {
89862306a36Sopenharmony_ci		clk_disable_unprepare(ssi->baudclk);
89962306a36Sopenharmony_ci		ssi->baudclk_streams &= ~BIT(substream->stream);
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	return 0;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_cistatic int _fsl_ssi_set_dai_fmt(struct fsl_ssi *ssi, unsigned int fmt)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	u32 strcr = 0, scr = 0, stcr, srcr, mask;
90862306a36Sopenharmony_ci	unsigned int slots;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	ssi->dai_fmt = fmt;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	/* Synchronize frame sync clock for TE to avoid data slipping */
91362306a36Sopenharmony_ci	scr |= SSI_SCR_SYNC_TX_FS;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	/* Set to default shifting settings: LSB_ALIGNED */
91662306a36Sopenharmony_ci	strcr |= SSI_STCR_TXBIT0;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	/* Use Network mode as default */
91962306a36Sopenharmony_ci	ssi->i2s_net = SSI_SCR_NET;
92062306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
92162306a36Sopenharmony_ci	case SND_SOC_DAIFMT_I2S:
92262306a36Sopenharmony_ci		switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
92362306a36Sopenharmony_ci		case SND_SOC_DAIFMT_BP_FP:
92462306a36Sopenharmony_ci			if (IS_ERR(ssi->baudclk)) {
92562306a36Sopenharmony_ci				dev_err(ssi->dev,
92662306a36Sopenharmony_ci					"missing baudclk for master mode\n");
92762306a36Sopenharmony_ci				return -EINVAL;
92862306a36Sopenharmony_ci			}
92962306a36Sopenharmony_ci			fallthrough;
93062306a36Sopenharmony_ci		case SND_SOC_DAIFMT_BC_FP:
93162306a36Sopenharmony_ci			ssi->i2s_net |= SSI_SCR_I2S_MODE_MASTER;
93262306a36Sopenharmony_ci			break;
93362306a36Sopenharmony_ci		case SND_SOC_DAIFMT_BC_FC:
93462306a36Sopenharmony_ci			ssi->i2s_net |= SSI_SCR_I2S_MODE_SLAVE;
93562306a36Sopenharmony_ci			break;
93662306a36Sopenharmony_ci		default:
93762306a36Sopenharmony_ci			return -EINVAL;
93862306a36Sopenharmony_ci		}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci		slots = ssi->slots ? : 2;
94162306a36Sopenharmony_ci		regmap_update_bits(ssi->regs, REG_SSI_STCCR,
94262306a36Sopenharmony_ci				   SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
94362306a36Sopenharmony_ci		regmap_update_bits(ssi->regs, REG_SSI_SRCCR,
94462306a36Sopenharmony_ci				   SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci		/* Data on rising edge of bclk, frame low, 1clk before data */
94762306a36Sopenharmony_ci		strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP | SSI_STCR_TEFS;
94862306a36Sopenharmony_ci		break;
94962306a36Sopenharmony_ci	case SND_SOC_DAIFMT_LEFT_J:
95062306a36Sopenharmony_ci		/* Data on rising edge of bclk, frame high */
95162306a36Sopenharmony_ci		strcr |= SSI_STCR_TSCKP;
95262306a36Sopenharmony_ci		break;
95362306a36Sopenharmony_ci	case SND_SOC_DAIFMT_DSP_A:
95462306a36Sopenharmony_ci		/* Data on rising edge of bclk, frame high, 1clk before data */
95562306a36Sopenharmony_ci		strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | SSI_STCR_TEFS;
95662306a36Sopenharmony_ci		break;
95762306a36Sopenharmony_ci	case SND_SOC_DAIFMT_DSP_B:
95862306a36Sopenharmony_ci		/* Data on rising edge of bclk, frame high */
95962306a36Sopenharmony_ci		strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP;
96062306a36Sopenharmony_ci		break;
96162306a36Sopenharmony_ci	case SND_SOC_DAIFMT_AC97:
96262306a36Sopenharmony_ci		/* Data on falling edge of bclk, frame high, 1clk before data */
96362306a36Sopenharmony_ci		strcr |= SSI_STCR_TEFS;
96462306a36Sopenharmony_ci		break;
96562306a36Sopenharmony_ci	default:
96662306a36Sopenharmony_ci		return -EINVAL;
96762306a36Sopenharmony_ci	}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	scr |= ssi->i2s_net;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	/* DAI clock inversion */
97262306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
97362306a36Sopenharmony_ci	case SND_SOC_DAIFMT_NB_NF:
97462306a36Sopenharmony_ci		/* Nothing to do for both normal cases */
97562306a36Sopenharmony_ci		break;
97662306a36Sopenharmony_ci	case SND_SOC_DAIFMT_IB_NF:
97762306a36Sopenharmony_ci		/* Invert bit clock */
97862306a36Sopenharmony_ci		strcr ^= SSI_STCR_TSCKP;
97962306a36Sopenharmony_ci		break;
98062306a36Sopenharmony_ci	case SND_SOC_DAIFMT_NB_IF:
98162306a36Sopenharmony_ci		/* Invert frame clock */
98262306a36Sopenharmony_ci		strcr ^= SSI_STCR_TFSI;
98362306a36Sopenharmony_ci		break;
98462306a36Sopenharmony_ci	case SND_SOC_DAIFMT_IB_IF:
98562306a36Sopenharmony_ci		/* Invert both clocks */
98662306a36Sopenharmony_ci		strcr ^= SSI_STCR_TSCKP;
98762306a36Sopenharmony_ci		strcr ^= SSI_STCR_TFSI;
98862306a36Sopenharmony_ci		break;
98962306a36Sopenharmony_ci	default:
99062306a36Sopenharmony_ci		return -EINVAL;
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	/* DAI clock provider masks */
99462306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
99562306a36Sopenharmony_ci	case SND_SOC_DAIFMT_BP_FP:
99662306a36Sopenharmony_ci		/* Output bit and frame sync clocks */
99762306a36Sopenharmony_ci		strcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
99862306a36Sopenharmony_ci		scr |= SSI_SCR_SYS_CLK_EN;
99962306a36Sopenharmony_ci		break;
100062306a36Sopenharmony_ci	case SND_SOC_DAIFMT_BC_FC:
100162306a36Sopenharmony_ci		/* Input bit or frame sync clocks */
100262306a36Sopenharmony_ci		break;
100362306a36Sopenharmony_ci	case SND_SOC_DAIFMT_BC_FP:
100462306a36Sopenharmony_ci		/* Input bit clock but output frame sync clock */
100562306a36Sopenharmony_ci		strcr |= SSI_STCR_TFDIR;
100662306a36Sopenharmony_ci		break;
100762306a36Sopenharmony_ci	default:
100862306a36Sopenharmony_ci		return -EINVAL;
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	stcr = strcr;
101262306a36Sopenharmony_ci	srcr = strcr;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	/* Set SYN mode and clear RXDIR bit when using SYN or AC97 mode */
101562306a36Sopenharmony_ci	if (ssi->synchronous || fsl_ssi_is_ac97(ssi)) {
101662306a36Sopenharmony_ci		srcr &= ~SSI_SRCR_RXDIR;
101762306a36Sopenharmony_ci		scr |= SSI_SCR_SYN;
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	mask = SSI_STCR_TFDIR | SSI_STCR_TXDIR | SSI_STCR_TSCKP |
102162306a36Sopenharmony_ci	       SSI_STCR_TFSL | SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	regmap_update_bits(ssi->regs, REG_SSI_STCR, mask, stcr);
102462306a36Sopenharmony_ci	regmap_update_bits(ssi->regs, REG_SSI_SRCR, mask, srcr);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	mask = SSI_SCR_SYNC_TX_FS | SSI_SCR_I2S_MODE_MASK |
102762306a36Sopenharmony_ci	       SSI_SCR_SYS_CLK_EN | SSI_SCR_SYN;
102862306a36Sopenharmony_ci	regmap_update_bits(ssi->regs, REG_SSI_SCR, mask, scr);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	return 0;
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci/**
103462306a36Sopenharmony_ci * fsl_ssi_set_dai_fmt - Configure Digital Audio Interface (DAI) Format
103562306a36Sopenharmony_ci * @dai: pointer to DAI
103662306a36Sopenharmony_ci * @fmt: format mask
103762306a36Sopenharmony_ci */
103862306a36Sopenharmony_cistatic int fsl_ssi_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
103962306a36Sopenharmony_ci{
104062306a36Sopenharmony_ci	struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	/* AC97 configured DAIFMT earlier in the probe() */
104362306a36Sopenharmony_ci	if (fsl_ssi_is_ac97(ssi))
104462306a36Sopenharmony_ci		return 0;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	return _fsl_ssi_set_dai_fmt(ssi, fmt);
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci/**
105062306a36Sopenharmony_ci * fsl_ssi_set_dai_tdm_slot - Set TDM slot number and slot width
105162306a36Sopenharmony_ci * @dai: pointer to DAI
105262306a36Sopenharmony_ci * @tx_mask: mask for TX
105362306a36Sopenharmony_ci * @rx_mask: mask for RX
105462306a36Sopenharmony_ci * @slots: number of slots
105562306a36Sopenharmony_ci * @slot_width: number of bits per slot
105662306a36Sopenharmony_ci */
105762306a36Sopenharmony_cistatic int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
105862306a36Sopenharmony_ci				    u32 rx_mask, int slots, int slot_width)
105962306a36Sopenharmony_ci{
106062306a36Sopenharmony_ci	struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
106162306a36Sopenharmony_ci	struct regmap *regs = ssi->regs;
106262306a36Sopenharmony_ci	u32 val;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	/* The word length should be 8, 10, 12, 16, 18, 20, 22 or 24 */
106562306a36Sopenharmony_ci	if (slot_width & 1 || slot_width < 8 || slot_width > 24) {
106662306a36Sopenharmony_ci		dev_err(dai->dev, "invalid slot width: %d\n", slot_width);
106762306a36Sopenharmony_ci		return -EINVAL;
106862306a36Sopenharmony_ci	}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	/* The slot number should be >= 2 if using Network mode or I2S mode */
107162306a36Sopenharmony_ci	if (ssi->i2s_net && slots < 2) {
107262306a36Sopenharmony_ci		dev_err(dai->dev, "slot number should be >= 2 in I2S or NET\n");
107362306a36Sopenharmony_ci		return -EINVAL;
107462306a36Sopenharmony_ci	}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	regmap_update_bits(regs, REG_SSI_STCCR,
107762306a36Sopenharmony_ci			   SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
107862306a36Sopenharmony_ci	regmap_update_bits(regs, REG_SSI_SRCCR,
107962306a36Sopenharmony_ci			   SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	/* Save the SCR register value */
108262306a36Sopenharmony_ci	regmap_read(regs, REG_SSI_SCR, &val);
108362306a36Sopenharmony_ci	/* Temporarily enable SSI to allow SxMSKs to be configurable */
108462306a36Sopenharmony_ci	regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, SSI_SCR_SSIEN);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	regmap_write(regs, REG_SSI_STMSK, ~tx_mask);
108762306a36Sopenharmony_ci	regmap_write(regs, REG_SSI_SRMSK, ~rx_mask);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	/* Restore the value of SSIEN bit */
109062306a36Sopenharmony_ci	regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, val);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	ssi->slot_width = slot_width;
109362306a36Sopenharmony_ci	ssi->slots = slots;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	return 0;
109662306a36Sopenharmony_ci}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci/**
109962306a36Sopenharmony_ci * fsl_ssi_trigger - Start or stop SSI and corresponding DMA transaction.
110062306a36Sopenharmony_ci * @substream: ASoC substream
110162306a36Sopenharmony_ci * @cmd: trigger command
110262306a36Sopenharmony_ci * @dai: pointer to DAI
110362306a36Sopenharmony_ci *
110462306a36Sopenharmony_ci * The DMA channel is in external master start and pause mode, which
110562306a36Sopenharmony_ci * means the SSI completely controls the flow of data.
110662306a36Sopenharmony_ci */
110762306a36Sopenharmony_cistatic int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
110862306a36Sopenharmony_ci			   struct snd_soc_dai *dai)
110962306a36Sopenharmony_ci{
111062306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
111162306a36Sopenharmony_ci	struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
111262306a36Sopenharmony_ci	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	switch (cmd) {
111562306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
111662306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
111762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
111862306a36Sopenharmony_ci		/*
111962306a36Sopenharmony_ci		 * SACCST might be modified via AC Link by a CODEC if it sends
112062306a36Sopenharmony_ci		 * extra bits in their SLOTREQ requests, which'll accidentally
112162306a36Sopenharmony_ci		 * send valid data to slots other than normal playback slots.
112262306a36Sopenharmony_ci		 *
112362306a36Sopenharmony_ci		 * To be safe, configure SACCST right before TX starts.
112462306a36Sopenharmony_ci		 */
112562306a36Sopenharmony_ci		if (tx && fsl_ssi_is_ac97(ssi))
112662306a36Sopenharmony_ci			fsl_ssi_tx_ac97_saccst_setup(ssi);
112762306a36Sopenharmony_ci		fsl_ssi_config_enable(ssi, tx);
112862306a36Sopenharmony_ci		break;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
113162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
113262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
113362306a36Sopenharmony_ci		fsl_ssi_config_disable(ssi, tx);
113462306a36Sopenharmony_ci		break;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	default:
113762306a36Sopenharmony_ci		return -EINVAL;
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	return 0;
114162306a36Sopenharmony_ci}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_cistatic int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
114462306a36Sopenharmony_ci{
114562306a36Sopenharmony_ci	struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	if (ssi->soc->imx && ssi->use_dma)
114862306a36Sopenharmony_ci		snd_soc_dai_init_dma_data(dai, &ssi->dma_params_tx,
114962306a36Sopenharmony_ci					  &ssi->dma_params_rx);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	return 0;
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
115562306a36Sopenharmony_ci	.probe = fsl_ssi_dai_probe,
115662306a36Sopenharmony_ci	.startup = fsl_ssi_startup,
115762306a36Sopenharmony_ci	.shutdown = fsl_ssi_shutdown,
115862306a36Sopenharmony_ci	.hw_params = fsl_ssi_hw_params,
115962306a36Sopenharmony_ci	.hw_free = fsl_ssi_hw_free,
116062306a36Sopenharmony_ci	.set_fmt = fsl_ssi_set_dai_fmt,
116162306a36Sopenharmony_ci	.set_tdm_slot = fsl_ssi_set_dai_tdm_slot,
116262306a36Sopenharmony_ci	.trigger = fsl_ssi_trigger,
116362306a36Sopenharmony_ci};
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_cistatic struct snd_soc_dai_driver fsl_ssi_dai_template = {
116662306a36Sopenharmony_ci	.playback = {
116762306a36Sopenharmony_ci		.stream_name = "CPU-Playback",
116862306a36Sopenharmony_ci		.channels_min = 1,
116962306a36Sopenharmony_ci		.channels_max = 32,
117062306a36Sopenharmony_ci		.rates = SNDRV_PCM_RATE_CONTINUOUS,
117162306a36Sopenharmony_ci		.formats = FSLSSI_I2S_FORMATS,
117262306a36Sopenharmony_ci	},
117362306a36Sopenharmony_ci	.capture = {
117462306a36Sopenharmony_ci		.stream_name = "CPU-Capture",
117562306a36Sopenharmony_ci		.channels_min = 1,
117662306a36Sopenharmony_ci		.channels_max = 32,
117762306a36Sopenharmony_ci		.rates = SNDRV_PCM_RATE_CONTINUOUS,
117862306a36Sopenharmony_ci		.formats = FSLSSI_I2S_FORMATS,
117962306a36Sopenharmony_ci	},
118062306a36Sopenharmony_ci	.ops = &fsl_ssi_dai_ops,
118162306a36Sopenharmony_ci};
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistatic const struct snd_soc_component_driver fsl_ssi_component = {
118462306a36Sopenharmony_ci	.name = "fsl-ssi",
118562306a36Sopenharmony_ci	.legacy_dai_naming = 1,
118662306a36Sopenharmony_ci};
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_cistatic struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
118962306a36Sopenharmony_ci	.symmetric_channels = 1,
119062306a36Sopenharmony_ci	.playback = {
119162306a36Sopenharmony_ci		.stream_name = "CPU AC97 Playback",
119262306a36Sopenharmony_ci		.channels_min = 2,
119362306a36Sopenharmony_ci		.channels_max = 2,
119462306a36Sopenharmony_ci		.rates = SNDRV_PCM_RATE_8000_48000,
119562306a36Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S20,
119662306a36Sopenharmony_ci	},
119762306a36Sopenharmony_ci	.capture = {
119862306a36Sopenharmony_ci		.stream_name = "CPU AC97 Capture",
119962306a36Sopenharmony_ci		.channels_min = 2,
120062306a36Sopenharmony_ci		.channels_max = 2,
120162306a36Sopenharmony_ci		.rates = SNDRV_PCM_RATE_48000,
120262306a36Sopenharmony_ci		/* 16-bit capture is broken (errata ERR003778) */
120362306a36Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S20,
120462306a36Sopenharmony_ci	},
120562306a36Sopenharmony_ci	.ops = &fsl_ssi_dai_ops,
120662306a36Sopenharmony_ci};
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_cistatic struct fsl_ssi *fsl_ac97_data;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_cistatic void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
121162306a36Sopenharmony_ci			       unsigned short val)
121262306a36Sopenharmony_ci{
121362306a36Sopenharmony_ci	struct regmap *regs = fsl_ac97_data->regs;
121462306a36Sopenharmony_ci	unsigned int lreg;
121562306a36Sopenharmony_ci	unsigned int lval;
121662306a36Sopenharmony_ci	int ret;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	if (reg > 0x7f)
121962306a36Sopenharmony_ci		return;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	mutex_lock(&fsl_ac97_data->ac97_reg_lock);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	ret = clk_prepare_enable(fsl_ac97_data->clk);
122462306a36Sopenharmony_ci	if (ret) {
122562306a36Sopenharmony_ci		pr_err("ac97 write clk_prepare_enable failed: %d\n",
122662306a36Sopenharmony_ci			ret);
122762306a36Sopenharmony_ci		goto ret_unlock;
122862306a36Sopenharmony_ci	}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	lreg = reg <<  12;
123162306a36Sopenharmony_ci	regmap_write(regs, REG_SSI_SACADD, lreg);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	lval = val << 4;
123462306a36Sopenharmony_ci	regmap_write(regs, REG_SSI_SACDAT, lval);
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	regmap_update_bits(regs, REG_SSI_SACNT,
123762306a36Sopenharmony_ci			   SSI_SACNT_RDWR_MASK, SSI_SACNT_WR);
123862306a36Sopenharmony_ci	udelay(100);
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	clk_disable_unprepare(fsl_ac97_data->clk);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ciret_unlock:
124362306a36Sopenharmony_ci	mutex_unlock(&fsl_ac97_data->ac97_reg_lock);
124462306a36Sopenharmony_ci}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_cistatic unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
124762306a36Sopenharmony_ci					unsigned short reg)
124862306a36Sopenharmony_ci{
124962306a36Sopenharmony_ci	struct regmap *regs = fsl_ac97_data->regs;
125062306a36Sopenharmony_ci	unsigned short val = 0;
125162306a36Sopenharmony_ci	u32 reg_val;
125262306a36Sopenharmony_ci	unsigned int lreg;
125362306a36Sopenharmony_ci	int ret;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	mutex_lock(&fsl_ac97_data->ac97_reg_lock);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	ret = clk_prepare_enable(fsl_ac97_data->clk);
125862306a36Sopenharmony_ci	if (ret) {
125962306a36Sopenharmony_ci		pr_err("ac97 read clk_prepare_enable failed: %d\n", ret);
126062306a36Sopenharmony_ci		goto ret_unlock;
126162306a36Sopenharmony_ci	}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	lreg = (reg & 0x7f) <<  12;
126462306a36Sopenharmony_ci	regmap_write(regs, REG_SSI_SACADD, lreg);
126562306a36Sopenharmony_ci	regmap_update_bits(regs, REG_SSI_SACNT,
126662306a36Sopenharmony_ci			   SSI_SACNT_RDWR_MASK, SSI_SACNT_RD);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	udelay(100);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	regmap_read(regs, REG_SSI_SACDAT, &reg_val);
127162306a36Sopenharmony_ci	val = (reg_val >> 4) & 0xffff;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	clk_disable_unprepare(fsl_ac97_data->clk);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ciret_unlock:
127662306a36Sopenharmony_ci	mutex_unlock(&fsl_ac97_data->ac97_reg_lock);
127762306a36Sopenharmony_ci	return val;
127862306a36Sopenharmony_ci}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_cistatic struct snd_ac97_bus_ops fsl_ssi_ac97_ops = {
128162306a36Sopenharmony_ci	.read = fsl_ssi_ac97_read,
128262306a36Sopenharmony_ci	.write = fsl_ssi_ac97_write,
128362306a36Sopenharmony_ci};
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci/**
128662306a36Sopenharmony_ci * fsl_ssi_hw_init - Initialize SSI registers
128762306a36Sopenharmony_ci * @ssi: SSI context
128862306a36Sopenharmony_ci */
128962306a36Sopenharmony_cistatic int fsl_ssi_hw_init(struct fsl_ssi *ssi)
129062306a36Sopenharmony_ci{
129162306a36Sopenharmony_ci	u32 wm = ssi->fifo_watermark;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	/* Initialize regvals */
129462306a36Sopenharmony_ci	fsl_ssi_setup_regvals(ssi);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	/* Set watermarks */
129762306a36Sopenharmony_ci	regmap_write(ssi->regs, REG_SSI_SFCSR,
129862306a36Sopenharmony_ci		     SSI_SFCSR_TFWM0(wm) | SSI_SFCSR_RFWM0(wm) |
129962306a36Sopenharmony_ci		     SSI_SFCSR_TFWM1(wm) | SSI_SFCSR_RFWM1(wm));
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	/* Enable Dual FIFO mode */
130262306a36Sopenharmony_ci	if (ssi->use_dual_fifo)
130362306a36Sopenharmony_ci		regmap_update_bits(ssi->regs, REG_SSI_SCR,
130462306a36Sopenharmony_ci				   SSI_SCR_TCH_EN, SSI_SCR_TCH_EN);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	/* AC97 should start earlier to communicate with CODECs */
130762306a36Sopenharmony_ci	if (fsl_ssi_is_ac97(ssi)) {
130862306a36Sopenharmony_ci		_fsl_ssi_set_dai_fmt(ssi, ssi->dai_fmt);
130962306a36Sopenharmony_ci		fsl_ssi_setup_ac97(ssi);
131062306a36Sopenharmony_ci	}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	return 0;
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci/**
131662306a36Sopenharmony_ci * fsl_ssi_hw_clean - Clear SSI registers
131762306a36Sopenharmony_ci * @ssi: SSI context
131862306a36Sopenharmony_ci */
131962306a36Sopenharmony_cistatic void fsl_ssi_hw_clean(struct fsl_ssi *ssi)
132062306a36Sopenharmony_ci{
132162306a36Sopenharmony_ci	/* Disable registers for AC97 */
132262306a36Sopenharmony_ci	if (fsl_ssi_is_ac97(ssi)) {
132362306a36Sopenharmony_ci		/* Disable TE and RE bits first */
132462306a36Sopenharmony_ci		regmap_update_bits(ssi->regs, REG_SSI_SCR,
132562306a36Sopenharmony_ci				   SSI_SCR_TE | SSI_SCR_RE, 0);
132662306a36Sopenharmony_ci		/* Disable AC97 mode */
132762306a36Sopenharmony_ci		regmap_write(ssi->regs, REG_SSI_SACNT, 0);
132862306a36Sopenharmony_ci		/* Unset WAIT bits */
132962306a36Sopenharmony_ci		regmap_write(ssi->regs, REG_SSI_SOR, 0);
133062306a36Sopenharmony_ci		/* Disable SSI -- software reset */
133162306a36Sopenharmony_ci		regmap_update_bits(ssi->regs, REG_SSI_SCR, SSI_SCR_SSIEN, 0);
133262306a36Sopenharmony_ci	}
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci/*
133662306a36Sopenharmony_ci * Make every character in a string lower-case
133762306a36Sopenharmony_ci */
133862306a36Sopenharmony_cistatic void make_lowercase(char *s)
133962306a36Sopenharmony_ci{
134062306a36Sopenharmony_ci	if (!s)
134162306a36Sopenharmony_ci		return;
134262306a36Sopenharmony_ci	for (; *s; s++)
134362306a36Sopenharmony_ci		*s = tolower(*s);
134462306a36Sopenharmony_ci}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_cistatic int fsl_ssi_imx_probe(struct platform_device *pdev,
134762306a36Sopenharmony_ci			     struct fsl_ssi *ssi, void __iomem *iomem)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
135062306a36Sopenharmony_ci	int ret;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	/* Backward compatible for a DT without ipg clock name assigned */
135362306a36Sopenharmony_ci	if (ssi->has_ipg_clk_name)
135462306a36Sopenharmony_ci		ssi->clk = devm_clk_get(dev, "ipg");
135562306a36Sopenharmony_ci	else
135662306a36Sopenharmony_ci		ssi->clk = devm_clk_get(dev, NULL);
135762306a36Sopenharmony_ci	if (IS_ERR(ssi->clk)) {
135862306a36Sopenharmony_ci		ret = PTR_ERR(ssi->clk);
135962306a36Sopenharmony_ci		dev_err(dev, "failed to get clock: %d\n", ret);
136062306a36Sopenharmony_ci		return ret;
136162306a36Sopenharmony_ci	}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	/* Enable the clock since regmap will not handle it in this case */
136462306a36Sopenharmony_ci	if (!ssi->has_ipg_clk_name) {
136562306a36Sopenharmony_ci		ret = clk_prepare_enable(ssi->clk);
136662306a36Sopenharmony_ci		if (ret) {
136762306a36Sopenharmony_ci			dev_err(dev, "clk_prepare_enable failed: %d\n", ret);
136862306a36Sopenharmony_ci			return ret;
136962306a36Sopenharmony_ci		}
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	/* Do not error out for consumer cases that live without a baud clock */
137362306a36Sopenharmony_ci	ssi->baudclk = devm_clk_get(dev, "baud");
137462306a36Sopenharmony_ci	if (IS_ERR(ssi->baudclk))
137562306a36Sopenharmony_ci		dev_dbg(dev, "failed to get baud clock: %ld\n",
137662306a36Sopenharmony_ci			 PTR_ERR(ssi->baudclk));
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	ssi->dma_params_tx.maxburst = ssi->dma_maxburst;
137962306a36Sopenharmony_ci	ssi->dma_params_rx.maxburst = ssi->dma_maxburst;
138062306a36Sopenharmony_ci	ssi->dma_params_tx.addr = ssi->ssi_phys + REG_SSI_STX0;
138162306a36Sopenharmony_ci	ssi->dma_params_rx.addr = ssi->ssi_phys + REG_SSI_SRX0;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	/* Use even numbers to avoid channel swap due to SDMA script design */
138462306a36Sopenharmony_ci	if (ssi->use_dual_fifo || ssi->use_dyna_fifo) {
138562306a36Sopenharmony_ci		ssi->dma_params_tx.maxburst &= ~0x1;
138662306a36Sopenharmony_ci		ssi->dma_params_rx.maxburst &= ~0x1;
138762306a36Sopenharmony_ci	}
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	if (!ssi->use_dma) {
139062306a36Sopenharmony_ci		/*
139162306a36Sopenharmony_ci		 * Some boards use an incompatible codec. Use imx-fiq-pcm-audio
139262306a36Sopenharmony_ci		 * to get it working, as DMA is not possible in this situation.
139362306a36Sopenharmony_ci		 */
139462306a36Sopenharmony_ci		ssi->fiq_params.irq = ssi->irq;
139562306a36Sopenharmony_ci		ssi->fiq_params.base = iomem;
139662306a36Sopenharmony_ci		ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx;
139762306a36Sopenharmony_ci		ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci		ret = imx_pcm_fiq_init(pdev, &ssi->fiq_params);
140062306a36Sopenharmony_ci		if (ret)
140162306a36Sopenharmony_ci			goto error_pcm;
140262306a36Sopenharmony_ci	} else {
140362306a36Sopenharmony_ci		ret = imx_pcm_dma_init(pdev);
140462306a36Sopenharmony_ci		if (ret)
140562306a36Sopenharmony_ci			goto error_pcm;
140662306a36Sopenharmony_ci	}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	return 0;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_cierror_pcm:
141162306a36Sopenharmony_ci	if (!ssi->has_ipg_clk_name)
141262306a36Sopenharmony_ci		clk_disable_unprepare(ssi->clk);
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	return ret;
141562306a36Sopenharmony_ci}
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_cistatic void fsl_ssi_imx_clean(struct platform_device *pdev, struct fsl_ssi *ssi)
141862306a36Sopenharmony_ci{
141962306a36Sopenharmony_ci	if (!ssi->use_dma)
142062306a36Sopenharmony_ci		imx_pcm_fiq_exit(pdev);
142162306a36Sopenharmony_ci	if (!ssi->has_ipg_clk_name)
142262306a36Sopenharmony_ci		clk_disable_unprepare(ssi->clk);
142362306a36Sopenharmony_ci}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_cistatic int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi)
142662306a36Sopenharmony_ci{
142762306a36Sopenharmony_ci	struct device *dev = ssi->dev;
142862306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
142962306a36Sopenharmony_ci	const char *p, *sprop;
143062306a36Sopenharmony_ci	const __be32 *iprop;
143162306a36Sopenharmony_ci	u32 dmas[4];
143262306a36Sopenharmony_ci	int ret;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	ret = of_property_match_string(np, "clock-names", "ipg");
143562306a36Sopenharmony_ci	/* Get error code if not found */
143662306a36Sopenharmony_ci	ssi->has_ipg_clk_name = ret >= 0;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	/* Check if being used in AC97 mode */
143962306a36Sopenharmony_ci	sprop = of_get_property(np, "fsl,mode", NULL);
144062306a36Sopenharmony_ci	if (sprop && !strcmp(sprop, "ac97-slave")) {
144162306a36Sopenharmony_ci		ssi->dai_fmt = FSLSSI_AC97_DAIFMT;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci		ret = of_property_read_u32(np, "cell-index", &ssi->card_idx);
144462306a36Sopenharmony_ci		if (ret) {
144562306a36Sopenharmony_ci			dev_err(dev, "failed to get SSI index property\n");
144662306a36Sopenharmony_ci			return -EINVAL;
144762306a36Sopenharmony_ci		}
144862306a36Sopenharmony_ci		strcpy(ssi->card_name, "ac97-codec");
144962306a36Sopenharmony_ci	} else if (!of_property_read_bool(np, "fsl,ssi-asynchronous")) {
145062306a36Sopenharmony_ci		/*
145162306a36Sopenharmony_ci		 * In synchronous mode, STCK and STFS ports are used by RX
145262306a36Sopenharmony_ci		 * as well. So the software should limit the sample rates,
145362306a36Sopenharmony_ci		 * sample bits and channels to be symmetric.
145462306a36Sopenharmony_ci		 *
145562306a36Sopenharmony_ci		 * This is exclusive with FSLSSI_AC97_FORMATS as AC97 runs
145662306a36Sopenharmony_ci		 * in the SSI synchronous mode however it does not have to
145762306a36Sopenharmony_ci		 * limit symmetric sample rates and sample bits.
145862306a36Sopenharmony_ci		 */
145962306a36Sopenharmony_ci		ssi->synchronous = true;
146062306a36Sopenharmony_ci	}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	/* Select DMA or FIQ */
146362306a36Sopenharmony_ci	ssi->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter");
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	/* Fetch FIFO depth; Set to 8 for older DT without this property */
146662306a36Sopenharmony_ci	iprop = of_get_property(np, "fsl,fifo-depth", NULL);
146762306a36Sopenharmony_ci	if (iprop)
146862306a36Sopenharmony_ci		ssi->fifo_depth = be32_to_cpup(iprop);
146962306a36Sopenharmony_ci	else
147062306a36Sopenharmony_ci		ssi->fifo_depth = 8;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	/* Use dual FIFO mode depending on the support from SDMA script */
147362306a36Sopenharmony_ci	ret = of_property_read_u32_array(np, "dmas", dmas, 4);
147462306a36Sopenharmony_ci	if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL)
147562306a36Sopenharmony_ci		ssi->use_dual_fifo = true;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI)
147862306a36Sopenharmony_ci		ssi->use_dyna_fifo = true;
147962306a36Sopenharmony_ci	/*
148062306a36Sopenharmony_ci	 * Backward compatible for older bindings by manually triggering the
148162306a36Sopenharmony_ci	 * machine driver's probe(). Use /compatible property, including the
148262306a36Sopenharmony_ci	 * address of CPU DAI driver structure, as the name of machine driver
148362306a36Sopenharmony_ci	 *
148462306a36Sopenharmony_ci	 * If card_name is set by AC97 earlier, bypass here since it uses a
148562306a36Sopenharmony_ci	 * different name to register the device.
148662306a36Sopenharmony_ci	 */
148762306a36Sopenharmony_ci	if (!ssi->card_name[0] && of_get_property(np, "codec-handle", NULL)) {
148862306a36Sopenharmony_ci		struct device_node *root = of_find_node_by_path("/");
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci		sprop = of_get_property(root, "compatible", NULL);
149162306a36Sopenharmony_ci		of_node_put(root);
149262306a36Sopenharmony_ci		/* Strip "fsl," in the compatible name if applicable */
149362306a36Sopenharmony_ci		p = strrchr(sprop, ',');
149462306a36Sopenharmony_ci		if (p)
149562306a36Sopenharmony_ci			sprop = p + 1;
149662306a36Sopenharmony_ci		snprintf(ssi->card_name, sizeof(ssi->card_name),
149762306a36Sopenharmony_ci			 "snd-soc-%s", sprop);
149862306a36Sopenharmony_ci		make_lowercase(ssi->card_name);
149962306a36Sopenharmony_ci		ssi->card_idx = 0;
150062306a36Sopenharmony_ci	}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	return 0;
150362306a36Sopenharmony_ci}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_cistatic int fsl_ssi_probe(struct platform_device *pdev)
150662306a36Sopenharmony_ci{
150762306a36Sopenharmony_ci	struct regmap_config regconfig = fsl_ssi_regconfig;
150862306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
150962306a36Sopenharmony_ci	struct fsl_ssi *ssi;
151062306a36Sopenharmony_ci	struct resource *res;
151162306a36Sopenharmony_ci	void __iomem *iomem;
151262306a36Sopenharmony_ci	int ret = 0;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	ssi = devm_kzalloc(dev, sizeof(*ssi), GFP_KERNEL);
151562306a36Sopenharmony_ci	if (!ssi)
151662306a36Sopenharmony_ci		return -ENOMEM;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	ssi->dev = dev;
151962306a36Sopenharmony_ci	ssi->soc = of_device_get_match_data(&pdev->dev);
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	/* Probe from DT */
152262306a36Sopenharmony_ci	ret = fsl_ssi_probe_from_dt(ssi);
152362306a36Sopenharmony_ci	if (ret)
152462306a36Sopenharmony_ci		return ret;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	if (fsl_ssi_is_ac97(ssi)) {
152762306a36Sopenharmony_ci		memcpy(&ssi->cpu_dai_drv, &fsl_ssi_ac97_dai,
152862306a36Sopenharmony_ci		       sizeof(fsl_ssi_ac97_dai));
152962306a36Sopenharmony_ci		fsl_ac97_data = ssi;
153062306a36Sopenharmony_ci	} else {
153162306a36Sopenharmony_ci		memcpy(&ssi->cpu_dai_drv, &fsl_ssi_dai_template,
153262306a36Sopenharmony_ci		       sizeof(fsl_ssi_dai_template));
153362306a36Sopenharmony_ci	}
153462306a36Sopenharmony_ci	ssi->cpu_dai_drv.name = dev_name(dev);
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	iomem = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
153762306a36Sopenharmony_ci	if (IS_ERR(iomem))
153862306a36Sopenharmony_ci		return PTR_ERR(iomem);
153962306a36Sopenharmony_ci	ssi->ssi_phys = res->start;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	if (ssi->soc->imx21regs) {
154262306a36Sopenharmony_ci		/* No SACC{ST,EN,DIS} regs in imx21-class SSI */
154362306a36Sopenharmony_ci		regconfig.max_register = REG_SSI_SRMSK;
154462306a36Sopenharmony_ci		regconfig.num_reg_defaults_raw =
154562306a36Sopenharmony_ci			REG_SSI_SRMSK / sizeof(uint32_t) + 1;
154662306a36Sopenharmony_ci	}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	if (ssi->has_ipg_clk_name)
154962306a36Sopenharmony_ci		ssi->regs = devm_regmap_init_mmio_clk(dev, "ipg", iomem,
155062306a36Sopenharmony_ci						      &regconfig);
155162306a36Sopenharmony_ci	else
155262306a36Sopenharmony_ci		ssi->regs = devm_regmap_init_mmio(dev, iomem, &regconfig);
155362306a36Sopenharmony_ci	if (IS_ERR(ssi->regs)) {
155462306a36Sopenharmony_ci		dev_err(dev, "failed to init register map\n");
155562306a36Sopenharmony_ci		return PTR_ERR(ssi->regs);
155662306a36Sopenharmony_ci	}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	ssi->irq = platform_get_irq(pdev, 0);
155962306a36Sopenharmony_ci	if (ssi->irq < 0)
156062306a36Sopenharmony_ci		return ssi->irq;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	/* Set software limitations for synchronous mode except AC97 */
156362306a36Sopenharmony_ci	if (ssi->synchronous && !fsl_ssi_is_ac97(ssi)) {
156462306a36Sopenharmony_ci		ssi->cpu_dai_drv.symmetric_rate = 1;
156562306a36Sopenharmony_ci		ssi->cpu_dai_drv.symmetric_channels = 1;
156662306a36Sopenharmony_ci		ssi->cpu_dai_drv.symmetric_sample_bits = 1;
156762306a36Sopenharmony_ci	}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	/*
157062306a36Sopenharmony_ci	 * Configure TX and RX DMA watermarks -- when to send a DMA request
157162306a36Sopenharmony_ci	 *
157262306a36Sopenharmony_ci	 * Values should be tested to avoid FIFO under/over run. Set maxburst
157362306a36Sopenharmony_ci	 * to fifo_watermark to maxiumize DMA transaction to reduce overhead.
157462306a36Sopenharmony_ci	 */
157562306a36Sopenharmony_ci	switch (ssi->fifo_depth) {
157662306a36Sopenharmony_ci	case 15:
157762306a36Sopenharmony_ci		/*
157862306a36Sopenharmony_ci		 * Set to 8 as a balanced configuration -- When TX FIFO has 8
157962306a36Sopenharmony_ci		 * empty slots, send a DMA request to fill these 8 slots. The
158062306a36Sopenharmony_ci		 * remaining 7 slots should be able to allow DMA to finish the
158162306a36Sopenharmony_ci		 * transaction before TX FIFO underruns; Same applies to RX.
158262306a36Sopenharmony_ci		 *
158362306a36Sopenharmony_ci		 * Tested with cases running at 48kHz @ 16 bits x 16 channels
158462306a36Sopenharmony_ci		 */
158562306a36Sopenharmony_ci		ssi->fifo_watermark = 8;
158662306a36Sopenharmony_ci		ssi->dma_maxburst = 8;
158762306a36Sopenharmony_ci		break;
158862306a36Sopenharmony_ci	case 8:
158962306a36Sopenharmony_ci	default:
159062306a36Sopenharmony_ci		/* Safely use old watermark configurations for older chips */
159162306a36Sopenharmony_ci		ssi->fifo_watermark = ssi->fifo_depth - 2;
159262306a36Sopenharmony_ci		ssi->dma_maxburst = ssi->fifo_depth - 2;
159362306a36Sopenharmony_ci		break;
159462306a36Sopenharmony_ci	}
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	dev_set_drvdata(dev, ssi);
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	if (ssi->soc->imx) {
159962306a36Sopenharmony_ci		ret = fsl_ssi_imx_probe(pdev, ssi, iomem);
160062306a36Sopenharmony_ci		if (ret)
160162306a36Sopenharmony_ci			return ret;
160262306a36Sopenharmony_ci	}
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	if (fsl_ssi_is_ac97(ssi)) {
160562306a36Sopenharmony_ci		mutex_init(&ssi->ac97_reg_lock);
160662306a36Sopenharmony_ci		ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
160762306a36Sopenharmony_ci		if (ret) {
160862306a36Sopenharmony_ci			dev_err(dev, "failed to set AC'97 ops\n");
160962306a36Sopenharmony_ci			goto error_ac97_ops;
161062306a36Sopenharmony_ci		}
161162306a36Sopenharmony_ci	}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	ret = devm_snd_soc_register_component(dev, &fsl_ssi_component,
161462306a36Sopenharmony_ci					      &ssi->cpu_dai_drv, 1);
161562306a36Sopenharmony_ci	if (ret) {
161662306a36Sopenharmony_ci		dev_err(dev, "failed to register DAI: %d\n", ret);
161762306a36Sopenharmony_ci		goto error_asoc_register;
161862306a36Sopenharmony_ci	}
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	if (ssi->use_dma) {
162162306a36Sopenharmony_ci		ret = devm_request_irq(dev, ssi->irq, fsl_ssi_isr, 0,
162262306a36Sopenharmony_ci				       dev_name(dev), ssi);
162362306a36Sopenharmony_ci		if (ret < 0) {
162462306a36Sopenharmony_ci			dev_err(dev, "failed to claim irq %u\n", ssi->irq);
162562306a36Sopenharmony_ci			goto error_asoc_register;
162662306a36Sopenharmony_ci		}
162762306a36Sopenharmony_ci	}
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	fsl_ssi_debugfs_create(&ssi->dbg_stats, dev);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	/* Initially configures SSI registers */
163262306a36Sopenharmony_ci	fsl_ssi_hw_init(ssi);
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	/* Register a platform device for older bindings or AC97 */
163562306a36Sopenharmony_ci	if (ssi->card_name[0]) {
163662306a36Sopenharmony_ci		struct device *parent = dev;
163762306a36Sopenharmony_ci		/*
163862306a36Sopenharmony_ci		 * Do not set SSI dev as the parent of AC97 CODEC device since
163962306a36Sopenharmony_ci		 * it does not have a DT node. Otherwise ASoC core will assume
164062306a36Sopenharmony_ci		 * CODEC has the same DT node as the SSI, so it may bypass the
164162306a36Sopenharmony_ci		 * dai_probe() of SSI and then cause NULL DMA data pointers.
164262306a36Sopenharmony_ci		 */
164362306a36Sopenharmony_ci		if (fsl_ssi_is_ac97(ssi))
164462306a36Sopenharmony_ci			parent = NULL;
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci		ssi->card_pdev = platform_device_register_data(parent,
164762306a36Sopenharmony_ci				ssi->card_name, ssi->card_idx, NULL, 0);
164862306a36Sopenharmony_ci		if (IS_ERR(ssi->card_pdev)) {
164962306a36Sopenharmony_ci			ret = PTR_ERR(ssi->card_pdev);
165062306a36Sopenharmony_ci			dev_err(dev, "failed to register %s: %d\n",
165162306a36Sopenharmony_ci				ssi->card_name, ret);
165262306a36Sopenharmony_ci			goto error_sound_card;
165362306a36Sopenharmony_ci		}
165462306a36Sopenharmony_ci	}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	return 0;
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_cierror_sound_card:
165962306a36Sopenharmony_ci	fsl_ssi_debugfs_remove(&ssi->dbg_stats);
166062306a36Sopenharmony_cierror_asoc_register:
166162306a36Sopenharmony_ci	if (fsl_ssi_is_ac97(ssi))
166262306a36Sopenharmony_ci		snd_soc_set_ac97_ops(NULL);
166362306a36Sopenharmony_cierror_ac97_ops:
166462306a36Sopenharmony_ci	if (fsl_ssi_is_ac97(ssi))
166562306a36Sopenharmony_ci		mutex_destroy(&ssi->ac97_reg_lock);
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	if (ssi->soc->imx)
166862306a36Sopenharmony_ci		fsl_ssi_imx_clean(pdev, ssi);
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	return ret;
167162306a36Sopenharmony_ci}
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_cistatic void fsl_ssi_remove(struct platform_device *pdev)
167462306a36Sopenharmony_ci{
167562306a36Sopenharmony_ci	struct fsl_ssi *ssi = dev_get_drvdata(&pdev->dev);
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	fsl_ssi_debugfs_remove(&ssi->dbg_stats);
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	if (ssi->card_pdev)
168062306a36Sopenharmony_ci		platform_device_unregister(ssi->card_pdev);
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	/* Clean up SSI registers */
168362306a36Sopenharmony_ci	fsl_ssi_hw_clean(ssi);
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	if (ssi->soc->imx)
168662306a36Sopenharmony_ci		fsl_ssi_imx_clean(pdev, ssi);
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	if (fsl_ssi_is_ac97(ssi)) {
168962306a36Sopenharmony_ci		snd_soc_set_ac97_ops(NULL);
169062306a36Sopenharmony_ci		mutex_destroy(&ssi->ac97_reg_lock);
169162306a36Sopenharmony_ci	}
169262306a36Sopenharmony_ci}
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
169562306a36Sopenharmony_cistatic int fsl_ssi_suspend(struct device *dev)
169662306a36Sopenharmony_ci{
169762306a36Sopenharmony_ci	struct fsl_ssi *ssi = dev_get_drvdata(dev);
169862306a36Sopenharmony_ci	struct regmap *regs = ssi->regs;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	regmap_read(regs, REG_SSI_SFCSR, &ssi->regcache_sfcsr);
170162306a36Sopenharmony_ci	regmap_read(regs, REG_SSI_SACNT, &ssi->regcache_sacnt);
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	regcache_cache_only(regs, true);
170462306a36Sopenharmony_ci	regcache_mark_dirty(regs);
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	return 0;
170762306a36Sopenharmony_ci}
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_cistatic int fsl_ssi_resume(struct device *dev)
171062306a36Sopenharmony_ci{
171162306a36Sopenharmony_ci	struct fsl_ssi *ssi = dev_get_drvdata(dev);
171262306a36Sopenharmony_ci	struct regmap *regs = ssi->regs;
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	regcache_cache_only(regs, false);
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	regmap_update_bits(regs, REG_SSI_SFCSR,
171762306a36Sopenharmony_ci			   SSI_SFCSR_RFWM1_MASK | SSI_SFCSR_TFWM1_MASK |
171862306a36Sopenharmony_ci			   SSI_SFCSR_RFWM0_MASK | SSI_SFCSR_TFWM0_MASK,
171962306a36Sopenharmony_ci			   ssi->regcache_sfcsr);
172062306a36Sopenharmony_ci	regmap_write(regs, REG_SSI_SACNT, ssi->regcache_sacnt);
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	return regcache_sync(regs);
172362306a36Sopenharmony_ci}
172462306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_cistatic const struct dev_pm_ops fsl_ssi_pm = {
172762306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume)
172862306a36Sopenharmony_ci};
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_cistatic struct platform_driver fsl_ssi_driver = {
173162306a36Sopenharmony_ci	.driver = {
173262306a36Sopenharmony_ci		.name = "fsl-ssi-dai",
173362306a36Sopenharmony_ci		.of_match_table = fsl_ssi_ids,
173462306a36Sopenharmony_ci		.pm = &fsl_ssi_pm,
173562306a36Sopenharmony_ci	},
173662306a36Sopenharmony_ci	.probe = fsl_ssi_probe,
173762306a36Sopenharmony_ci	.remove_new = fsl_ssi_remove,
173862306a36Sopenharmony_ci};
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_cimodule_platform_driver(fsl_ssi_driver);
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ciMODULE_ALIAS("platform:fsl-ssi-dai");
174362306a36Sopenharmony_ciMODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
174462306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
174562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1746