blob: 46a53551b955c6c33415df20d2fcb8f576e90a43 [file] [log] [blame]
Fabio Estevam0eb60482018-05-01 09:20:40 -03001// SPDX-License-Identifier: GPL-2.0
2//
3// Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver
4//
5// Author: Timur Tabi <timur@freescale.com>
6//
7// Copyright 2007-2010 Freescale Semiconductor, Inc.
8//
9// Some notes why imx-pcm-fiq is used instead of DMA on some boards:
10//
11// The i.MX SSI core has some nasty limitations in AC97 mode. While most
12// sane processor vendors have a FIFO per AC97 slot, the i.MX has only
13// one FIFO which combines all valid receive slots. We cannot even select
14// which slots we want to receive. The WM9712 with which this driver
15// was developed with always sends GPIO status data in slot 12 which
16// we receive in our (PCM-) data stream. The only chance we have is to
17// manually skip this data in the FIQ handler. With sampling rates different
18// from 48000Hz not every frame has valid receive data, so the ratio
19// between pcm data and GPIO status data changes. Our FIQ handler is not
20// able to handle this, hence this driver only works with 48000Hz sampling
21// rate.
22// Reading and writing AC97 registers is another challenge. The core
23// provides us status bits when the read register is updated with *another*
24// value. When we read the same register two times (and the register still
25// contains the same value) these status bits are not set. We work
26// around this by not polling these bits but only wait a fixed delay.
Timur Tabi17467f22008-01-11 18:15:26 +010027
28#include <linux/init.h>
Shawn Guodfa1a1072012-03-16 16:56:42 +080029#include <linux/io.h>
Timur Tabi17467f22008-01-11 18:15:26 +010030#include <linux/module.h>
31#include <linux/interrupt.h>
Shawn Guo95cd98f2012-03-29 10:53:41 +080032#include <linux/clk.h>
Fabio Estevamc6682fe2017-04-05 16:44:06 -030033#include <linux/ctype.h>
Timur Tabi17467f22008-01-11 18:15:26 +010034#include <linux/device.h>
35#include <linux/delay.h>
Maciej S. Szmigierob880b802017-11-20 23:16:07 +010036#include <linux/mutex.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090037#include <linux/slab.h>
Nicolin Chenaafa85e2013-12-12 18:44:45 +080038#include <linux/spinlock.h>
Mark Brown9c72a042014-04-15 12:02:02 +010039#include <linux/of.h>
Shawn Guodfa1a1072012-03-16 16:56:42 +080040#include <linux/of_address.h>
41#include <linux/of_irq.h>
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +000042#include <linux/of_platform.h>
Shengjiu Wang7aded702022-05-10 19:56:48 +080043#include <linux/dma/imx-dma.h>
Timur Tabi17467f22008-01-11 18:15:26 +010044
Timur Tabi17467f22008-01-11 18:15:26 +010045#include <sound/core.h>
46#include <sound/pcm.h>
47#include <sound/pcm_params.h>
48#include <sound/initval.h>
49#include <sound/soc.h>
Lars-Peter Clausena8909c92013-04-03 11:06:04 +020050#include <sound/dmaengine_pcm.h>
Timur Tabi17467f22008-01-11 18:15:26 +010051
Timur Tabi17467f22008-01-11 18:15:26 +010052#include "fsl_ssi.h"
Shawn Guo09ce1112012-03-16 16:56:43 +080053#include "imx-pcm.h"
Timur Tabi17467f22008-01-11 18:15:26 +010054
Nicolin Chen14761052018-02-12 14:03:09 -080055/* Define RX and TX to index ssi->regvals array; Can be 0 or 1 only */
56#define RX 0
57#define TX 1
58
Timur Tabi17467f22008-01-11 18:15:26 +010059/**
Timur Tabi17467f22008-01-11 18:15:26 +010060 * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
61 *
Timur Tabi17467f22008-01-11 18:15:26 +010062 * The SSI has a limitation in that the samples must be in the same byte
63 * order as the host CPU. This is because when multiple bytes are written
64 * to the STX register, the bytes and bits must be written in the same
65 * order. The STX is a shift register, so all the bits need to be aligned
66 * (bit-endianness must match byte-endianness). Processors typically write
67 * the bits within a byte in the same order that the bytes of a word are
68 * written in. So if the host CPU is big-endian, then only big-endian
69 * samples will be written to STX properly.
70 */
71#ifdef __BIG_ENDIAN
Nicolin Chenaf4f7f32017-12-17 18:52:04 -080072#define FSLSSI_I2S_FORMATS \
73 (SNDRV_PCM_FMTBIT_S8 | \
74 SNDRV_PCM_FMTBIT_S16_BE | \
75 SNDRV_PCM_FMTBIT_S18_3BE | \
76 SNDRV_PCM_FMTBIT_S20_3BE | \
77 SNDRV_PCM_FMTBIT_S24_3BE | \
78 SNDRV_PCM_FMTBIT_S24_BE)
Timur Tabi17467f22008-01-11 18:15:26 +010079#else
Nicolin Chenaf4f7f32017-12-17 18:52:04 -080080#define FSLSSI_I2S_FORMATS \
81 (SNDRV_PCM_FMTBIT_S8 | \
82 SNDRV_PCM_FMTBIT_S16_LE | \
83 SNDRV_PCM_FMTBIT_S18_3LE | \
84 SNDRV_PCM_FMTBIT_S20_3LE | \
85 SNDRV_PCM_FMTBIT_S24_3LE | \
86 SNDRV_PCM_FMTBIT_S24_LE)
Timur Tabi17467f22008-01-11 18:15:26 +010087#endif
88
Nicolin Chenb6c93f7f2018-02-12 14:03:16 -080089/*
90 * In AC97 mode, TXDIR bit is forced to 0 and TFDIR bit is forced to 1:
91 * - SSI inputs external bit clock and outputs frame sync clock -- CBM_CFS
92 * - Also have NB_NF to mark these two clocks will not be inverted
93 */
94#define FSLSSI_AC97_DAIFMT \
95 (SND_SOC_DAIFMT_AC97 | \
Charles Keepax3b14c152022-05-19 16:42:30 +010096 SND_SOC_DAIFMT_BC_FP | \
Nicolin Chenb6c93f7f2018-02-12 14:03:16 -080097 SND_SOC_DAIFMT_NB_NF)
98
Nicolin Chenaf4f7f32017-12-17 18:52:04 -080099#define FSLSSI_SIER_DBG_RX_FLAGS \
100 (SSI_SIER_RFF0_EN | \
101 SSI_SIER_RLS_EN | \
102 SSI_SIER_RFS_EN | \
103 SSI_SIER_ROE0_EN | \
104 SSI_SIER_RFRC_EN)
105#define FSLSSI_SIER_DBG_TX_FLAGS \
106 (SSI_SIER_TFE0_EN | \
107 SSI_SIER_TLS_EN | \
108 SSI_SIER_TFS_EN | \
109 SSI_SIER_TUE0_EN | \
110 SSI_SIER_TFRC_EN)
Markus Pargmannc1953bf2013-12-20 14:11:30 +0100111
112enum fsl_ssi_type {
113 FSL_SSI_MCP8610,
114 FSL_SSI_MX21,
Markus Pargmann0888efd2013-12-20 14:11:31 +0100115 FSL_SSI_MX35,
Markus Pargmannc1953bf2013-12-20 14:11:30 +0100116 FSL_SSI_MX51,
117};
118
Nicolin Chen2474e402017-12-17 18:52:08 -0800119struct fsl_ssi_regvals {
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100120 u32 sier;
121 u32 srcr;
122 u32 stcr;
123 u32 scr;
124};
125
Zidan Wang05cf2372015-09-18 11:09:12 +0800126static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
127{
128 switch (reg) {
Nicolin Chena818aa52017-12-17 18:52:03 -0800129 case REG_SSI_SACCEN:
130 case REG_SSI_SACCDIS:
Zidan Wang05cf2372015-09-18 11:09:12 +0800131 return false;
132 default:
133 return true;
134 }
135}
136
137static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
138{
139 switch (reg) {
Nicolin Chena818aa52017-12-17 18:52:03 -0800140 case REG_SSI_STX0:
141 case REG_SSI_STX1:
142 case REG_SSI_SRX0:
143 case REG_SSI_SRX1:
144 case REG_SSI_SISR:
145 case REG_SSI_SFCSR:
146 case REG_SSI_SACNT:
147 case REG_SSI_SACADD:
148 case REG_SSI_SACDAT:
149 case REG_SSI_SATAG:
150 case REG_SSI_SACCST:
151 case REG_SSI_SOR:
Zidan Wang05cf2372015-09-18 11:09:12 +0800152 return true;
153 default:
154 return false;
155 }
156}
157
Maciej S. Szmigierof51e3d52015-12-20 21:31:48 +0100158static bool fsl_ssi_precious_reg(struct device *dev, unsigned int reg)
159{
160 switch (reg) {
Nicolin Chena818aa52017-12-17 18:52:03 -0800161 case REG_SSI_SRX0:
162 case REG_SSI_SRX1:
163 case REG_SSI_SISR:
164 case REG_SSI_SACADD:
165 case REG_SSI_SACDAT:
166 case REG_SSI_SATAG:
Maciej S. Szmigierof51e3d52015-12-20 21:31:48 +0100167 return true;
168 default:
169 return false;
170 }
171}
172
Zidan Wang05cf2372015-09-18 11:09:12 +0800173static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg)
174{
175 switch (reg) {
Nicolin Chena818aa52017-12-17 18:52:03 -0800176 case REG_SSI_SRX0:
177 case REG_SSI_SRX1:
178 case REG_SSI_SACCST:
Zidan Wang05cf2372015-09-18 11:09:12 +0800179 return false;
180 default:
181 return true;
182 }
183}
184
Markus Pargmann43248122014-05-27 10:24:25 +0200185static const struct regmap_config fsl_ssi_regconfig = {
Nicolin Chena818aa52017-12-17 18:52:03 -0800186 .max_register = REG_SSI_SACCDIS,
Markus Pargmann43248122014-05-27 10:24:25 +0200187 .reg_bits = 32,
188 .val_bits = 32,
189 .reg_stride = 4,
190 .val_format_endian = REGMAP_ENDIAN_NATIVE,
Nicolin Chena818aa52017-12-17 18:52:03 -0800191 .num_reg_defaults_raw = REG_SSI_SACCDIS / sizeof(uint32_t) + 1,
Zidan Wang05cf2372015-09-18 11:09:12 +0800192 .readable_reg = fsl_ssi_readable_reg,
193 .volatile_reg = fsl_ssi_volatile_reg,
Maciej S. Szmigierof51e3d52015-12-20 21:31:48 +0100194 .precious_reg = fsl_ssi_precious_reg,
Zidan Wang05cf2372015-09-18 11:09:12 +0800195 .writeable_reg = fsl_ssi_writeable_reg,
Marek Vasutbfcf9282016-09-19 21:30:28 +0200196 .cache_type = REGCACHE_FLAT,
Markus Pargmann43248122014-05-27 10:24:25 +0200197};
Timur Tabid5a908b2009-03-26 11:42:38 -0500198
Sascha Hauerfcdbadef2014-05-27 10:24:18 +0200199struct fsl_ssi_soc_data {
200 bool imx;
Maciej S. Szmigiero6139b1b2016-01-18 20:07:44 +0100201 bool imx21regs; /* imx21-class SSI - no SACC{ST,EN,DIS} regs */
Sascha Hauerfcdbadef2014-05-27 10:24:18 +0200202 bool offline_config;
203 u32 sisr_write_mask;
204};
205
Timur Tabi17467f22008-01-11 18:15:26 +0100206/**
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -0500207 * struct fsl_ssi - per-SSI private data
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800208 * @regs: Pointer to the regmap registers
Timur Tabi17467f22008-01-11 18:15:26 +0100209 * @irq: IRQ of this SSI
Markus Pargmann737a6b42014-05-27 10:24:24 +0200210 * @cpu_dai_drv: CPU DAI driver for this device
Markus Pargmann737a6b42014-05-27 10:24:24 +0200211 * @dai_fmt: DAI configuration this device is currently used with
Nicolin Chene05827312018-02-12 14:03:12 -0800212 * @streams: Mask of current active streams: BIT(TX) and BIT(RX)
Nicolin Chen8bc84a32017-12-17 18:52:09 -0800213 * @i2s_net: I2S and Network mode configurations of SCR register
Nicolin Chenfac8a5a2018-04-07 21:40:21 -0700214 * (this is the initial settings based on the DAI format)
Nicolin Chenbadc9592018-02-12 14:03:23 -0800215 * @synchronous: Use synchronous mode - both of TX and RX use STCK and SFCK
Markus Pargmann737a6b42014-05-27 10:24:24 +0200216 * @use_dma: DMA is used or FIQ with stream filter
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800217 * @use_dual_fifo: DMA with support for dual FIFO mode
Shengjiu Wang7aded702022-05-10 19:56:48 +0800218 * @use_dyna_fifo: DMA with support for multi FIFO script
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800219 * @has_ipg_clk_name: If "ipg" is in the clock name list of device tree
220 * @fifo_depth: Depth of the SSI FIFOs
221 * @slot_width: Width of each DAI slot
222 * @slots: Number of slots
Nicolin Chen2474e402017-12-17 18:52:08 -0800223 * @regvals: Specific RX/TX register settings
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800224 * @clk: Clock source to access register
225 * @baudclk: Clock source to generate bit and frame-sync clocks
Markus Pargmann737a6b42014-05-27 10:24:24 +0200226 * @baudclk_streams: Active streams that are using baudclk
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800227 * @regcache_sfcsr: Cache sfcsr register value during suspend and resume
228 * @regcache_sacnt: Cache sacnt register value during suspend and resume
Markus Pargmann737a6b42014-05-27 10:24:24 +0200229 * @dma_params_tx: DMA transmit parameters
230 * @dma_params_rx: DMA receive parameters
231 * @ssi_phys: physical address of the SSI registers
Markus Pargmann737a6b42014-05-27 10:24:24 +0200232 * @fiq_params: FIQ stream filtering parameters
Nicolin Chen76f38452018-02-12 14:03:24 -0800233 * @card_pdev: Platform_device pointer to register a sound card for PowerPC or
234 * to register a CODEC platform device for AC97
235 * @card_name: Platform_device name to register a sound card for PowerPC or
236 * to register a CODEC platform device for AC97
237 * @card_idx: The index of SSI to register a sound card for PowerPC or
238 * to register a CODEC platform device for AC97
Markus Pargmann737a6b42014-05-27 10:24:24 +0200239 * @dbg_stats: Debugging statistics
Xiubo Lidcfcf2c2015-08-12 14:38:18 +0800240 * @soc: SoC specific data
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800241 * @dev: Pointer to &pdev->dev
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800242 * @fifo_watermark: The FIFO watermark setting. Notifies DMA when there are
243 * @fifo_watermark or fewer words in TX fifo or
244 * @fifo_watermark or more empty words in RX fifo.
245 * @dma_maxburst: Max number of words to transfer in one go. So far,
246 * this is always the same as fifo_watermark.
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800247 * @ac97_reg_lock: Mutex lock to serialize AC97 register access operations
Shengjiu Wang7aded702022-05-10 19:56:48 +0800248 * @audio_config: configure for dma multi fifo script
Timur Tabi17467f22008-01-11 18:15:26 +0100249 */
Nicolin Chenf3176832017-12-17 18:52:00 -0800250struct fsl_ssi {
Markus Pargmann43248122014-05-27 10:24:25 +0200251 struct regmap *regs;
Fabio Estevam9e446ad2015-01-14 10:48:59 -0200252 int irq;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000253 struct snd_soc_dai_driver cpu_dai_drv;
Timur Tabi17467f22008-01-11 18:15:26 +0100254
Markus Pargmann737a6b42014-05-27 10:24:24 +0200255 unsigned int dai_fmt;
Nicolin Chene05827312018-02-12 14:03:12 -0800256 u8 streams;
Nicolin Chen8bc84a32017-12-17 18:52:09 -0800257 u8 i2s_net;
Nicolin Chenbadc9592018-02-12 14:03:23 -0800258 bool synchronous;
Markus Pargmannde623ec2013-07-27 13:31:53 +0200259 bool use_dma;
Nicolin Chen0da9e552013-11-13 22:55:26 +0800260 bool use_dual_fifo;
Shengjiu Wang7aded702022-05-10 19:56:48 +0800261 bool use_dyna_fifo;
Shengjiu Wangf4a43ca2014-09-16 10:13:16 +0800262 bool has_ipg_clk_name;
Markus Pargmann737a6b42014-05-27 10:24:24 +0200263 unsigned int fifo_depth;
Nicolin Chenb0a70432017-09-13 20:07:09 -0700264 unsigned int slot_width;
265 unsigned int slots;
Nicolin Chen2474e402017-12-17 18:52:08 -0800266 struct fsl_ssi_regvals regvals[2];
Markus Pargmann737a6b42014-05-27 10:24:24 +0200267
Shawn Guo95cd98f2012-03-29 10:53:41 +0800268 struct clk *clk;
Markus Pargmann737a6b42014-05-27 10:24:24 +0200269 struct clk *baudclk;
Markus Pargmannd429d8e2014-05-27 10:24:23 +0200270 unsigned int baudclk_streams;
Markus Pargmann737a6b42014-05-27 10:24:24 +0200271
Zidan Wang05cf2372015-09-18 11:09:12 +0800272 u32 regcache_sfcsr;
Maciej S. Szmigiero3f1c2412015-12-20 21:30:25 +0100273 u32 regcache_sacnt;
Zidan Wang05cf2372015-09-18 11:09:12 +0800274
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200275 struct snd_dmaengine_dai_dma_data dma_params_tx;
276 struct snd_dmaengine_dai_dma_data dma_params_rx;
Markus Pargmann737a6b42014-05-27 10:24:24 +0200277 dma_addr_t ssi_phys;
278
Markus Pargmannde623ec2013-07-27 13:31:53 +0200279 struct imx_pcm_fiq_params fiq_params;
Markus Pargmann737a6b42014-05-27 10:24:24 +0200280
Nicolin Chen76f38452018-02-12 14:03:24 -0800281 struct platform_device *card_pdev;
282 char card_name[32];
283 u32 card_idx;
Shawn Guo09ce1112012-03-16 16:56:43 +0800284
Markus Pargmannf138e622014-04-28 12:54:43 +0200285 struct fsl_ssi_dbg dbg_stats;
Sascha Hauerfcdbadef2014-05-27 10:24:18 +0200286
287 const struct fsl_ssi_soc_data *soc;
Arnaud Mouiche0096b692016-05-03 14:13:57 +0200288 struct device *dev;
Caleb Crome4ee437f2017-01-03 10:22:57 -0800289
290 u32 fifo_watermark;
291 u32 dma_maxburst;
Maciej S. Szmigierob880b802017-11-20 23:16:07 +0100292
293 struct mutex ac97_reg_lock;
Shengjiu Wang7aded702022-05-10 19:56:48 +0800294 struct sdma_peripheral_config audio_config[2];
Timur Tabi17467f22008-01-11 18:15:26 +0100295};
296
Markus Pargmann171d6832014-04-28 12:54:48 +0200297/*
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800298 * SoC specific data
Markus Pargmann171d6832014-04-28 12:54:48 +0200299 *
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800300 * Notes:
301 * 1) SSI in earlier SoCS has critical bits in control registers that
302 * cannot be changed after SSI starts running -- a software reset
303 * (set SSIEN to 0) is required to change their values. So adding
304 * an offline_config flag for these SoCs.
305 * 2) SDMA is available since imx35. However, imx35 does not support
306 * DMA bits changing when SSI is running, so set offline_config.
307 * 3) imx51 and later versions support register configurations when
308 * SSI is running (SSIEN); For these versions, DMA needs to be
309 * configured before SSI sends DMA request to avoid an undefined
310 * DMA request on the SDMA side.
Markus Pargmann171d6832014-04-28 12:54:48 +0200311 */
Markus Pargmann171d6832014-04-28 12:54:48 +0200312
Sascha Hauerfcdbadef2014-05-27 10:24:18 +0200313static struct fsl_ssi_soc_data fsl_ssi_mpc8610 = {
314 .imx = false,
315 .offline_config = true,
Nicolin Chena818aa52017-12-17 18:52:03 -0800316 .sisr_write_mask = SSI_SISR_RFRC | SSI_SISR_TFRC |
Nicolin Chenaf4f7f32017-12-17 18:52:04 -0800317 SSI_SISR_ROE0 | SSI_SISR_ROE1 |
318 SSI_SISR_TUE0 | SSI_SISR_TUE1,
Sascha Hauerfcdbadef2014-05-27 10:24:18 +0200319};
320
321static struct fsl_ssi_soc_data fsl_ssi_imx21 = {
322 .imx = true,
Maciej S. Szmigiero6139b1b2016-01-18 20:07:44 +0100323 .imx21regs = true,
Sascha Hauerfcdbadef2014-05-27 10:24:18 +0200324 .offline_config = true,
325 .sisr_write_mask = 0,
326};
327
328static struct fsl_ssi_soc_data fsl_ssi_imx35 = {
329 .imx = true,
330 .offline_config = true,
Nicolin Chena818aa52017-12-17 18:52:03 -0800331 .sisr_write_mask = SSI_SISR_RFRC | SSI_SISR_TFRC |
Nicolin Chenaf4f7f32017-12-17 18:52:04 -0800332 SSI_SISR_ROE0 | SSI_SISR_ROE1 |
333 SSI_SISR_TUE0 | SSI_SISR_TUE1,
Sascha Hauerfcdbadef2014-05-27 10:24:18 +0200334};
335
336static struct fsl_ssi_soc_data fsl_ssi_imx51 = {
337 .imx = true,
338 .offline_config = false,
Nicolin Chena818aa52017-12-17 18:52:03 -0800339 .sisr_write_mask = SSI_SISR_ROE0 | SSI_SISR_ROE1 |
Nicolin Chenaf4f7f32017-12-17 18:52:04 -0800340 SSI_SISR_TUE0 | SSI_SISR_TUE1,
Sascha Hauerfcdbadef2014-05-27 10:24:18 +0200341};
342
343static const struct of_device_id fsl_ssi_ids[] = {
344 { .compatible = "fsl,mpc8610-ssi", .data = &fsl_ssi_mpc8610 },
345 { .compatible = "fsl,imx51-ssi", .data = &fsl_ssi_imx51 },
346 { .compatible = "fsl,imx35-ssi", .data = &fsl_ssi_imx35 },
347 { .compatible = "fsl,imx21-ssi", .data = &fsl_ssi_imx21 },
348 {}
349};
350MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
351
Nicolin Chenf3176832017-12-17 18:52:00 -0800352static bool fsl_ssi_is_ac97(struct fsl_ssi *ssi)
Sascha Hauerfcdbadef2014-05-27 10:24:18 +0200353{
Nicolin Chenf3176832017-12-17 18:52:00 -0800354 return (ssi->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
Adam Thomson5b64c172015-09-16 10:13:19 +0100355 SND_SOC_DAIFMT_AC97;
Markus Pargmann171d6832014-04-28 12:54:48 +0200356}
357
Mark Brown89efbda2021-09-21 22:35:33 +0100358static bool fsl_ssi_is_i2s_clock_provider(struct fsl_ssi *ssi)
Sascha Hauer8dd51e22014-05-27 10:24:20 +0200359{
Mark Brown89efbda2021-09-21 22:35:33 +0100360 return (ssi->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) ==
Charles Keepax3b14c152022-05-19 16:42:30 +0100361 SND_SOC_DAIFMT_BP_FP;
Sascha Hauer8dd51e22014-05-27 10:24:20 +0200362}
363
Charles Keepax3b14c152022-05-19 16:42:30 +0100364static bool fsl_ssi_is_i2s_bc_fp(struct fsl_ssi *ssi)
Fabio Falzoicf4f7fc2014-08-04 17:08:07 +0200365{
Mark Brown89efbda2021-09-21 22:35:33 +0100366 return (ssi->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) ==
Charles Keepax3b14c152022-05-19 16:42:30 +0100367 SND_SOC_DAIFMT_BC_FP;
Fabio Falzoicf4f7fc2014-08-04 17:08:07 +0200368}
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800369
Timur Tabi17467f22008-01-11 18:15:26 +0100370/**
Pierre-Louis Bossart6a9287f2021-03-02 14:59:23 -0600371 * fsl_ssi_isr - Interrupt handler to gather states
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -0500372 * @irq: irq number
373 * @dev_id: context
Timur Tabi17467f22008-01-11 18:15:26 +0100374 */
375static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
376{
Nicolin Chenf3176832017-12-17 18:52:00 -0800377 struct fsl_ssi *ssi = dev_id;
378 struct regmap *regs = ssi->regs;
Fabio Estevam671f8202018-04-25 19:53:52 -0300379 u32 sisr, sisr2;
Timur Tabi17467f22008-01-11 18:15:26 +0100380
Nicolin Chena818aa52017-12-17 18:52:03 -0800381 regmap_read(regs, REG_SSI_SISR, &sisr);
Timur Tabi17467f22008-01-11 18:15:26 +0100382
Nicolin Chenf3176832017-12-17 18:52:00 -0800383 sisr2 = sisr & ssi->soc->sisr_write_mask;
Timur Tabi17467f22008-01-11 18:15:26 +0100384 /* Clear the bits that we set */
385 if (sisr2)
Nicolin Chena818aa52017-12-17 18:52:03 -0800386 regmap_write(regs, REG_SSI_SISR, sisr2);
Timur Tabi17467f22008-01-11 18:15:26 +0100387
Nicolin Chenf3176832017-12-17 18:52:00 -0800388 fsl_ssi_dbg_isr(&ssi->dbg_stats, sisr);
Markus Pargmannf138e622014-04-28 12:54:43 +0200389
390 return IRQ_HANDLED;
Timur Tabi17467f22008-01-11 18:15:26 +0100391}
392
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800393/**
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -0500394 * fsl_ssi_config_enable - Set SCR, SIER, STCR and SRCR registers with
395 * cached values in regvals
396 * @ssi: SSI context
397 * @tx: direction
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800398 *
399 * Notes:
400 * 1) For offline_config SoCs, enable all necessary bits of both streams
401 * when 1st stream starts, even if the opposite stream will not start
402 * 2) It also clears FIFO before setting regvals; SOR is safe to set online
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100403 */
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800404static void fsl_ssi_config_enable(struct fsl_ssi *ssi, bool tx)
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100405{
Nicolin Chen2474e402017-12-17 18:52:08 -0800406 struct fsl_ssi_regvals *vals = ssi->regvals;
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800407 int dir = tx ? TX : RX;
408 u32 sier, srcr, stcr;
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100409
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800410 /* Clear dirty data in the FIFO; It also prevents channel slipping */
411 regmap_update_bits(ssi->regs, REG_SSI_SOR,
412 SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
413
414 /*
415 * On offline_config SoCs, SxCR and SIER are already configured when
416 * the previous stream started. So skip all SxCR and SIER settings
417 * to prevent online reconfigurations, then jump to set SCR directly
418 */
419 if (ssi->soc->offline_config && ssi->streams)
420 goto enable_scr;
421
422 if (ssi->soc->offline_config) {
423 /*
424 * Online reconfiguration not supported, so enable all bits for
425 * both streams at once to avoid necessity of reconfigurations
426 */
427 srcr = vals[RX].srcr | vals[TX].srcr;
428 stcr = vals[RX].stcr | vals[TX].stcr;
429 sier = vals[RX].sier | vals[TX].sier;
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100430 } else {
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800431 /* Otherwise, only set bits for the current stream */
432 srcr = vals[dir].srcr;
433 stcr = vals[dir].stcr;
434 sier = vals[dir].sier;
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100435 }
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800436
437 /* Configure SRCR, STCR and SIER at once */
438 regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, srcr);
439 regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, stcr);
440 regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, sier);
441
442enable_scr:
443 /*
444 * Start DMA before setting TE to avoid FIFO underrun
445 * which may cause a channel slip or a channel swap
446 *
447 * TODO: FIQ cases might also need this upon testing
448 */
449 if (ssi->use_dma && tx) {
450 int try = 100;
451 u32 sfcsr;
452
453 /* Enable SSI first to send TX DMA request */
454 regmap_update_bits(ssi->regs, REG_SSI_SCR,
455 SSI_SCR_SSIEN, SSI_SCR_SSIEN);
456
457 /* Busy wait until TX FIFO not empty -- DMA working */
458 do {
459 regmap_read(ssi->regs, REG_SSI_SFCSR, &sfcsr);
460 if (SSI_SFCSR_TFCNT0(sfcsr))
461 break;
462 } while (--try);
463
464 /* FIFO still empty -- something might be wrong */
465 if (!SSI_SFCSR_TFCNT0(sfcsr))
466 dev_warn(ssi->dev, "Timeout waiting TX FIFO filling\n");
467 }
468 /* Enable all remaining bits in SCR */
469 regmap_update_bits(ssi->regs, REG_SSI_SCR,
470 vals[dir].scr, vals[dir].scr);
471
472 /* Log the enabled stream to the mask */
473 ssi->streams |= BIT(dir);
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100474}
475
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -0500476/*
Nicolin Chen06a99452018-02-12 14:03:13 -0800477 * Exclude bits that are used by the opposite stream
Markus Pargmann65c961c2014-04-28 12:54:42 +0200478 *
Nicolin Chen06a99452018-02-12 14:03:13 -0800479 * When both streams are active, disabling some bits for the current stream
480 * might break the other stream if these bits are used by it.
Markus Pargmann65c961c2014-04-28 12:54:42 +0200481 *
Nicolin Chen06a99452018-02-12 14:03:13 -0800482 * @vals : regvals of the current stream
483 * @avals: regvals of the opposite stream
484 * @aactive: active state of the opposite stream
Markus Pargmann65c961c2014-04-28 12:54:42 +0200485 *
Nicolin Chen06a99452018-02-12 14:03:13 -0800486 * 1) XOR vals and avals to get the differences if the other stream is active;
487 * Otherwise, return current vals if the other stream is not active
488 * 2) AND the result of 1) with the current vals
Markus Pargmann65c961c2014-04-28 12:54:42 +0200489 */
Nicolin Chen06a99452018-02-12 14:03:13 -0800490#define _ssi_xor_shared_bits(vals, avals, aactive) \
491 ((vals) ^ ((avals) * (aactive)))
492
493#define ssi_excl_shared_bits(vals, avals, aactive) \
494 ((vals) & _ssi_xor_shared_bits(vals, avals, aactive))
Markus Pargmann65c961c2014-04-28 12:54:42 +0200495
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800496/**
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -0500497 * fsl_ssi_config_disable - Unset SCR, SIER, STCR and SRCR registers
498 * with cached values in regvals
499 * @ssi: SSI context
500 * @tx: direction
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800501 *
502 * Notes:
503 * 1) For offline_config SoCs, to avoid online reconfigurations, disable all
504 * bits of both streams at once when the last stream is abort to end
505 * 2) It also clears FIFO after unsetting regvals; SOR is safe to set online
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100506 */
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800507static void fsl_ssi_config_disable(struct fsl_ssi *ssi, bool tx)
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100508{
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800509 struct fsl_ssi_regvals *vals, *avals;
510 u32 sier, srcr, stcr, scr;
Nicolin Chen2e132742018-02-12 14:03:14 -0800511 int adir = tx ? RX : TX;
512 int dir = tx ? TX : RX;
Nicolin Chen06a99452018-02-12 14:03:13 -0800513 bool aactive;
Markus Pargmann65c961c2014-04-28 12:54:42 +0200514
Nicolin Chen06a99452018-02-12 14:03:13 -0800515 /* Check if the opposite stream is active */
516 aactive = ssi->streams & BIT(adir);
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100517
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800518 vals = &ssi->regvals[dir];
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100519
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800520 /* Get regvals of the opposite stream to keep opposite stream safe */
521 avals = &ssi->regvals[adir];
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100522
523 /*
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800524 * To keep the other stream safe, exclude shared bits between
525 * both streams, and get safe bits to disable current stream
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100526 */
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800527 scr = ssi_excl_shared_bits(vals->scr, avals->scr, aactive);
528
529 /* Disable safe bits of SCR register for the current stream */
530 regmap_update_bits(ssi->regs, REG_SSI_SCR, scr, 0);
531
532 /* Log the disabled stream to the mask */
533 ssi->streams &= ~BIT(dir);
534
535 /*
536 * On offline_config SoCs, if the other stream is active, skip
537 * SxCR and SIER settings to prevent online reconfigurations
538 */
539 if (ssi->soc->offline_config && aactive)
540 goto fifo_clear;
541
Nicolin Chenf3176832017-12-17 18:52:00 -0800542 if (ssi->soc->offline_config) {
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800543 /* Now there is only current stream active, disable all bits */
544 srcr = vals->srcr | avals->srcr;
545 stcr = vals->stcr | avals->stcr;
546 sier = vals->sier | avals->sier;
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100547 } else {
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100548 /*
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800549 * To keep the other stream safe, exclude shared bits between
550 * both streams, and get safe bits to disable current stream
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100551 */
Nicolin Chen06a99452018-02-12 14:03:13 -0800552 sier = ssi_excl_shared_bits(vals->sier, avals->sier, aactive);
553 srcr = ssi_excl_shared_bits(vals->srcr, avals->srcr, aactive);
554 stcr = ssi_excl_shared_bits(vals->stcr, avals->stcr, aactive);
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100555 }
556
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800557 /* Clear configurations of SRCR, STCR and SIER at once */
558 regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, 0);
559 regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, 0);
560 regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, 0);
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800561
Nicolin Chen7d67bcb2018-02-12 14:03:15 -0800562fifo_clear:
563 /* Clear remaining data in the FIFO */
564 regmap_update_bits(ssi->regs, REG_SSI_SOR,
565 SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
Markus Pargmann4e6ec0d2013-12-20 14:11:33 +0100566}
567
Nicolin Chenf3176832017-12-17 18:52:00 -0800568static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi)
Maciej S. Szmigiero01ca4852017-11-22 00:54:26 +0100569{
Nicolin Chenf3176832017-12-17 18:52:00 -0800570 struct regmap *regs = ssi->regs;
Maciej S. Szmigiero01ca4852017-11-22 00:54:26 +0100571
572 /* no SACC{ST,EN,DIS} regs on imx21-class SSI */
Nicolin Chenf3176832017-12-17 18:52:00 -0800573 if (!ssi->soc->imx21regs) {
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800574 /* Disable all channel slots */
Nicolin Chena818aa52017-12-17 18:52:03 -0800575 regmap_write(regs, REG_SSI_SACCDIS, 0xff);
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800576 /* Enable slots 3 & 4 -- PCM Playback Left & Right channels */
Nicolin Chena818aa52017-12-17 18:52:03 -0800577 regmap_write(regs, REG_SSI_SACCEN, 0x300);
Maciej S. Szmigiero01ca4852017-11-22 00:54:26 +0100578 }
579}
580
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800581/**
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -0500582 * fsl_ssi_setup_regvals - Cache critical bits of SIER, SRCR, STCR and
583 * SCR to later set them safely
584 * @ssi: SSI context
Markus Pargmann6de83872013-12-20 14:11:34 +0100585 */
Nicolin Chen2474e402017-12-17 18:52:08 -0800586static void fsl_ssi_setup_regvals(struct fsl_ssi *ssi)
Markus Pargmann6de83872013-12-20 14:11:34 +0100587{
Nicolin Chen2474e402017-12-17 18:52:08 -0800588 struct fsl_ssi_regvals *vals = ssi->regvals;
Markus Pargmann6de83872013-12-20 14:11:34 +0100589
Nicolin Chen501bc1d2018-02-12 14:03:17 -0800590 vals[RX].sier = SSI_SIER_RFF0_EN | FSLSSI_SIER_DBG_RX_FLAGS;
Nicolin Chen2474e402017-12-17 18:52:08 -0800591 vals[RX].srcr = SSI_SRCR_RFEN0;
Nicolin Chen501bc1d2018-02-12 14:03:17 -0800592 vals[RX].scr = SSI_SCR_SSIEN | SSI_SCR_RE;
593 vals[TX].sier = SSI_SIER_TFE0_EN | FSLSSI_SIER_DBG_TX_FLAGS;
Nicolin Chen2474e402017-12-17 18:52:08 -0800594 vals[TX].stcr = SSI_STCR_TFEN0;
Nicolin Chen501bc1d2018-02-12 14:03:17 -0800595 vals[TX].scr = SSI_SCR_SSIEN | SSI_SCR_TE;
Markus Pargmann6de83872013-12-20 14:11:34 +0100596
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800597 /* AC97 has already enabled SSIEN, RE and TE, so ignore them */
Nicolin Chen501bc1d2018-02-12 14:03:17 -0800598 if (fsl_ssi_is_ac97(ssi))
599 vals[RX].scr = vals[TX].scr = 0;
Markus Pargmann6de83872013-12-20 14:11:34 +0100600
Nicolin Chen702d79652018-02-12 14:03:18 -0800601 if (ssi->use_dual_fifo) {
602 vals[RX].srcr |= SSI_SRCR_RFEN1;
603 vals[TX].stcr |= SSI_STCR_TFEN1;
604 }
605
Nicolin Chenf3176832017-12-17 18:52:00 -0800606 if (ssi->use_dma) {
Nicolin Chen2474e402017-12-17 18:52:08 -0800607 vals[RX].sier |= SSI_SIER_RDMAE;
608 vals[TX].sier |= SSI_SIER_TDMAE;
Markus Pargmann6de83872013-12-20 14:11:34 +0100609 } else {
Nicolin Chen2474e402017-12-17 18:52:08 -0800610 vals[RX].sier |= SSI_SIER_RIE;
611 vals[TX].sier |= SSI_SIER_TIE;
Markus Pargmann6de83872013-12-20 14:11:34 +0100612 }
Markus Pargmann6de83872013-12-20 14:11:34 +0100613}
614
Nicolin Chenf3176832017-12-17 18:52:00 -0800615static void fsl_ssi_setup_ac97(struct fsl_ssi *ssi)
Markus Pargmannd8764642013-11-20 10:04:15 +0100616{
Nicolin Chenf3176832017-12-17 18:52:00 -0800617 struct regmap *regs = ssi->regs;
Markus Pargmannd8764642013-11-20 10:04:15 +0100618
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800619 /* Setup the clock control register */
Nicolin Chenaf4f7f32017-12-17 18:52:04 -0800620 regmap_write(regs, REG_SSI_STCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13));
621 regmap_write(regs, REG_SSI_SRCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13));
Markus Pargmannd8764642013-11-20 10:04:15 +0100622
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800623 /* Enable AC97 mode and startup the SSI */
Nicolin Chenaf4f7f32017-12-17 18:52:04 -0800624 regmap_write(regs, REG_SSI_SACNT, SSI_SACNT_AC97EN | SSI_SACNT_FV);
Maciej S. Szmigiero6139b1b2016-01-18 20:07:44 +0100625
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800626 /* AC97 has to communicate with codec before starting a stream */
Nicolin Chena818aa52017-12-17 18:52:03 -0800627 regmap_update_bits(regs, REG_SSI_SCR,
Nicolin Chenaf4f7f32017-12-17 18:52:04 -0800628 SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE,
629 SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE);
Markus Pargmannd8764642013-11-20 10:04:15 +0100630
Nicolin Chena818aa52017-12-17 18:52:03 -0800631 regmap_write(regs, REG_SSI_SOR, SSI_SOR_WAIT(3));
Markus Pargmannd8764642013-11-20 10:04:15 +0100632}
633
Mark Browndee89c42008-11-18 22:11:38 +0000634static int fsl_ssi_startup(struct snd_pcm_substream *substream,
635 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100636{
Kuninori Morimoto9f5f0782020-07-20 10:18:38 +0900637 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
Kuninori Morimoto17198ae2020-03-23 14:18:30 +0900638 struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
Shengjiu Wangf4a43ca2014-09-16 10:13:16 +0800639 int ret;
640
Nicolin Chenf3176832017-12-17 18:52:00 -0800641 ret = clk_prepare_enable(ssi->clk);
Shengjiu Wangf4a43ca2014-09-16 10:13:16 +0800642 if (ret)
643 return ret;
Timur Tabi17467f22008-01-11 18:15:26 +0100644
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800645 /*
646 * When using dual fifo mode, it is safer to ensure an even period
Nicolin Chen0da9e552013-11-13 22:55:26 +0800647 * size. If appearing to an odd number while DMA always starts its
648 * task from fifo0, fifo1 would be neglected at the end of each
649 * period. But SSI would still access fifo1 with an invalid data.
650 */
Shengjiu Wang7aded702022-05-10 19:56:48 +0800651 if (ssi->use_dual_fifo || ssi->use_dyna_fifo)
Nicolin Chen0da9e552013-11-13 22:55:26 +0800652 snd_pcm_hw_constraint_step(substream->runtime, 0,
Nicolin Chenaf4f7f32017-12-17 18:52:04 -0800653 SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
Nicolin Chen0da9e552013-11-13 22:55:26 +0800654
Timur Tabi17467f22008-01-11 18:15:26 +0100655 return 0;
656}
657
Shengjiu Wangf4a43ca2014-09-16 10:13:16 +0800658static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
Nicolin Chenaf4f7f32017-12-17 18:52:04 -0800659 struct snd_soc_dai *dai)
Shengjiu Wangf4a43ca2014-09-16 10:13:16 +0800660{
Kuninori Morimoto9f5f0782020-07-20 10:18:38 +0900661 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
Kuninori Morimoto17198ae2020-03-23 14:18:30 +0900662 struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
Shengjiu Wangf4a43ca2014-09-16 10:13:16 +0800663
Nicolin Chenf3176832017-12-17 18:52:00 -0800664 clk_disable_unprepare(ssi->clk);
Shengjiu Wangf4a43ca2014-09-16 10:13:16 +0800665}
666
667/**
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -0500668 * fsl_ssi_set_bclk - Configure Digital Audio Interface bit clock
669 * @substream: ASoC substream
670 * @dai: pointer to DAI
671 * @hw_params: pointers to hw_params
Sascha Haueree9daad2014-04-28 12:54:52 +0200672 *
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -0500673 * Notes: This function can be only called when using SSI as DAI master
Sascha Haueree9daad2014-04-28 12:54:52 +0200674 *
675 * Quick instruction for parameters:
Nicolin Chenb0a70432017-09-13 20:07:09 -0700676 * freq: Output BCLK frequency = samplerate * slots * slot_width
677 * (In 2-channel I2S Master mode, slot_width is fixed 32)
Sascha Haueree9daad2014-04-28 12:54:52 +0200678 */
Sascha Hauer8dd51e22014-05-27 10:24:20 +0200679static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
Nicolin Chen0c884be2017-12-17 18:52:06 -0800680 struct snd_soc_dai *dai,
Nicolin Chenaf4f7f32017-12-17 18:52:04 -0800681 struct snd_pcm_hw_params *hw_params)
Sascha Haueree9daad2014-04-28 12:54:52 +0200682{
Nicolin Chen52eee842017-12-17 18:52:10 -0800683 bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
Nicolin Chen0c884be2017-12-17 18:52:06 -0800684 struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
Nicolin Chenf3176832017-12-17 18:52:00 -0800685 struct regmap *regs = ssi->regs;
Sascha Haueree9daad2014-04-28 12:54:52 +0200686 u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i;
Sascha Hauerd8ced472014-05-27 10:24:21 +0200687 unsigned long clkrate, baudrate, tmprate;
Shengjiu Wanged1220d2020-06-16 10:53:48 +0800688 unsigned int channels = params_channels(hw_params);
689 unsigned int slot_width = params_width(hw_params);
690 unsigned int slots = 2;
Sascha Haueree9daad2014-04-28 12:54:52 +0200691 u64 sub, savesub = 100000;
Sascha Hauer8dd51e22014-05-27 10:24:20 +0200692 unsigned int freq;
Markus Pargmannd429d8e2014-05-27 10:24:23 +0200693 bool baudclk_is_used;
Nicolin Chenbadc9592018-02-12 14:03:23 -0800694 int ret;
Sascha Hauer8dd51e22014-05-27 10:24:20 +0200695
Nicolin Chenb0a70432017-09-13 20:07:09 -0700696 /* Override slots and slot_width if being specifically set... */
Nicolin Chenf3176832017-12-17 18:52:00 -0800697 if (ssi->slots)
698 slots = ssi->slots;
Shengjiu Wanged1220d2020-06-16 10:53:48 +0800699 if (ssi->slot_width)
Nicolin Chenf3176832017-12-17 18:52:00 -0800700 slot_width = ssi->slot_width;
Nicolin Chenb0a70432017-09-13 20:07:09 -0700701
Shengjiu Wanged1220d2020-06-16 10:53:48 +0800702 /* ...but force 32 bits for stereo audio using I2S Master Mode */
703 if (channels == 2 &&
704 (ssi->i2s_net & SSI_SCR_I2S_MODE_MASK) == SSI_SCR_I2S_MODE_MASTER)
705 slot_width = 32;
706
Nicolin Chenb0a70432017-09-13 20:07:09 -0700707 /* Generate bit clock based on the slot number and slot width */
708 freq = slots * slot_width * params_rate(hw_params);
Sascha Haueree9daad2014-04-28 12:54:52 +0200709
710 /* Don't apply it to any non-baudclk circumstance */
Nicolin Chenf3176832017-12-17 18:52:00 -0800711 if (IS_ERR(ssi->baudclk))
Sascha Haueree9daad2014-04-28 12:54:52 +0200712 return -EINVAL;
713
Arnaud Mouichee09745f2016-05-03 14:13:56 +0200714 /*
715 * Hardware limitation: The bclk rate must be
716 * never greater than 1/5 IPG clock rate
717 */
Nicolin Chenf3176832017-12-17 18:52:00 -0800718 if (freq * 5 > clk_get_rate(ssi->clk)) {
Nicolin Chen0c884be2017-12-17 18:52:06 -0800719 dev_err(dai->dev, "bitclk > ipgclk / 5\n");
Arnaud Mouichee09745f2016-05-03 14:13:56 +0200720 return -EINVAL;
721 }
722
Nicolin Chenf3176832017-12-17 18:52:00 -0800723 baudclk_is_used = ssi->baudclk_streams & ~(BIT(substream->stream));
Markus Pargmannd429d8e2014-05-27 10:24:23 +0200724
Sascha Haueree9daad2014-04-28 12:54:52 +0200725 /* It should be already enough to divide clock by setting pm alone */
726 psr = 0;
727 div2 = 0;
728
729 factor = (div2 + 1) * (7 * psr + 1) * 2;
730
731 for (i = 0; i < 255; i++) {
Nicolin Chen6c8ca302015-03-04 21:05:04 -0800732 tmprate = freq * factor * (i + 1);
Markus Pargmannd429d8e2014-05-27 10:24:23 +0200733
734 if (baudclk_is_used)
Nicolin Chenf3176832017-12-17 18:52:00 -0800735 clkrate = clk_get_rate(ssi->baudclk);
Markus Pargmannd429d8e2014-05-27 10:24:23 +0200736 else
Nicolin Chenf3176832017-12-17 18:52:00 -0800737 clkrate = clk_round_rate(ssi->baudclk, tmprate);
Sascha Haueree9daad2014-04-28 12:54:52 +0200738
Timur Tabiacf2c602014-06-13 07:42:40 -0500739 clkrate /= factor;
740 afreq = clkrate / (i + 1);
Sascha Haueree9daad2014-04-28 12:54:52 +0200741
742 if (freq == afreq)
743 sub = 0;
744 else if (freq / afreq == 1)
745 sub = freq - afreq;
746 else if (afreq / freq == 1)
747 sub = afreq - freq;
748 else
749 continue;
750
751 /* Calculate the fraction */
752 sub *= 100000;
753 do_div(sub, freq);
754
Pierre-Louis Bossart2fb56352021-02-19 17:29:33 -0600755 if (sub < savesub && !(i == 0)) {
Sascha Haueree9daad2014-04-28 12:54:52 +0200756 baudrate = tmprate;
757 savesub = sub;
758 pm = i;
759 }
760
761 /* We are lucky */
762 if (savesub == 0)
763 break;
764 }
765
766 /* No proper pm found if it is still remaining the initial value */
767 if (pm == 999) {
Nicolin Chen0c884be2017-12-17 18:52:06 -0800768 dev_err(dai->dev, "failed to handle the required sysclk\n");
Sascha Haueree9daad2014-04-28 12:54:52 +0200769 return -EINVAL;
770 }
771
Pierre-Louis Bossart2fb56352021-02-19 17:29:33 -0600772 stccr = SSI_SxCCR_PM(pm + 1);
Nicolin Chenaf4f7f32017-12-17 18:52:04 -0800773 mask = SSI_SxCCR_PM_MASK | SSI_SxCCR_DIV2 | SSI_SxCCR_PSR;
Sascha Haueree9daad2014-04-28 12:54:52 +0200774
Nicolin Chen52eee842017-12-17 18:52:10 -0800775 /* STCCR is used for RX in synchronous mode */
Nicolin Chenbadc9592018-02-12 14:03:23 -0800776 tx2 = tx || ssi->synchronous;
Nicolin Chen52eee842017-12-17 18:52:10 -0800777 regmap_update_bits(regs, REG_SSI_SxCCR(tx2), mask, stccr);
Sascha Haueree9daad2014-04-28 12:54:52 +0200778
Markus Pargmannd429d8e2014-05-27 10:24:23 +0200779 if (!baudclk_is_used) {
Nicolin Chenf3176832017-12-17 18:52:00 -0800780 ret = clk_set_rate(ssi->baudclk, baudrate);
Sascha Haueree9daad2014-04-28 12:54:52 +0200781 if (ret) {
Nicolin Chen0c884be2017-12-17 18:52:06 -0800782 dev_err(dai->dev, "failed to set baudclk rate\n");
Sascha Haueree9daad2014-04-28 12:54:52 +0200783 return -EINVAL;
784 }
Sascha Haueree9daad2014-04-28 12:54:52 +0200785 }
Sascha Haueree9daad2014-04-28 12:54:52 +0200786
787 return 0;
788}
789
790/**
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -0500791 * fsl_ssi_hw_params - Configure SSI based on PCM hardware parameters
792 * @substream: ASoC substream
793 * @hw_params: pointers to hw_params
794 * @dai: pointer to DAI
Timur Tabi17467f22008-01-11 18:15:26 +0100795 *
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800796 * Notes:
797 * 1) SxCCR.WL bits are critical bits that require SSI to be temporarily
798 * disabled on offline_config SoCs. Even for online configurable SoCs
799 * running in synchronous mode (both TX and RX use STCCR), it is not
800 * safe to re-configure them when both two streams start running.
801 * 2) SxCCR.PM, SxCCR.DIV2 and SxCCR.PSR bits will be configured in the
802 * fsl_ssi_set_bclk() if SSI is the DAI clock master.
Timur Tabi17467f22008-01-11 18:15:26 +0100803 */
Timur Tabi85ef2372009-02-05 17:56:02 -0600804static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
Nicolin Chenaf4f7f32017-12-17 18:52:04 -0800805 struct snd_pcm_hw_params *hw_params,
Nicolin Chen0c884be2017-12-17 18:52:06 -0800806 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100807{
Nicolin Chen52eee842017-12-17 18:52:10 -0800808 bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
Nicolin Chen0c884be2017-12-17 18:52:06 -0800809 struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
Shengjiu Wang7aded702022-05-10 19:56:48 +0800810 struct fsl_ssi_regvals *vals = ssi->regvals;
Nicolin Chenf3176832017-12-17 18:52:00 -0800811 struct regmap *regs = ssi->regs;
Nicolin Chen2924a992013-12-02 23:29:03 +0800812 unsigned int channels = params_channels(hw_params);
Zidan Wang4ca73042015-11-24 15:32:09 +0800813 unsigned int sample_size = params_width(hw_params);
Nicolin Chena818aa52017-12-17 18:52:03 -0800814 u32 wl = SSI_SxCCR_WL(sample_size);
Sascha Hauer8dd51e22014-05-27 10:24:20 +0200815 int ret;
Timur Tabi17467f22008-01-11 18:15:26 +0100816
Mark Brown89efbda2021-09-21 22:35:33 +0100817 if (fsl_ssi_is_i2s_clock_provider(ssi)) {
Nicolin Chen0c884be2017-12-17 18:52:06 -0800818 ret = fsl_ssi_set_bclk(substream, dai, hw_params);
Sascha Hauer8dd51e22014-05-27 10:24:20 +0200819 if (ret)
820 return ret;
Markus Pargmannd429d8e2014-05-27 10:24:23 +0200821
822 /* Do not enable the clock if it is already enabled */
Nicolin Chenf3176832017-12-17 18:52:00 -0800823 if (!(ssi->baudclk_streams & BIT(substream->stream))) {
824 ret = clk_prepare_enable(ssi->baudclk);
Markus Pargmannd429d8e2014-05-27 10:24:23 +0200825 if (ret)
826 return ret;
827
Nicolin Chenf3176832017-12-17 18:52:00 -0800828 ssi->baudclk_streams |= BIT(substream->stream);
Markus Pargmannd429d8e2014-05-27 10:24:23 +0200829 }
Sascha Hauer8dd51e22014-05-27 10:24:20 +0200830 }
831
Shengjiu Wang696d0522019-08-28 13:20:17 -0400832 /*
833 * SSI is properly configured if it is enabled and running in
834 * the synchronous mode; Note that AC97 mode is an exception
835 * that should set separate configurations for STCCR and SRCCR
836 * despite running in the synchronous mode.
837 */
838 if (ssi->streams && ssi->synchronous)
839 return 0;
840
Nicolin Chenf3176832017-12-17 18:52:00 -0800841 if (!fsl_ssi_is_ac97(ssi)) {
Nicolin Chenfac8a5a2018-04-07 21:40:21 -0700842 /*
843 * Keep the ssi->i2s_net intact while having a local variable
844 * to override settings for special use cases. Otherwise, the
845 * ssi->i2s_net will lose the settings for regular use cases.
846 */
847 u8 i2s_net = ssi->i2s_net;
848
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800849 /* Normal + Network mode to send 16-bit data in 32-bit frames */
Charles Keepax3b14c152022-05-19 16:42:30 +0100850 if (fsl_ssi_is_i2s_bc_fp(ssi) && sample_size == 16)
Nicolin Chenfac8a5a2018-04-07 21:40:21 -0700851 i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET;
Nicolin Chenebf08ae2018-02-12 14:03:10 -0800852
853 /* Use Normal mode to send mono data at 1st slot of 2 slots */
854 if (channels == 1)
Nicolin Chenfac8a5a2018-04-07 21:40:21 -0700855 i2s_net = SSI_SCR_I2S_MODE_NORMAL;
Fabio Falzoicf4f7fc2014-08-04 17:08:07 +0200856
Nicolin Chena818aa52017-12-17 18:52:03 -0800857 regmap_update_bits(regs, REG_SSI_SCR,
Nicolin Chenfac8a5a2018-04-07 21:40:21 -0700858 SSI_SCR_I2S_NET_MASK, i2s_net);
Fabio Falzoicf4f7fc2014-08-04 17:08:07 +0200859 }
860
Timur Tabi5e538ec2011-09-13 12:59:37 -0500861 /* In synchronous mode, the SSI uses STCCR for capture */
Nicolin Chenbadc9592018-02-12 14:03:23 -0800862 tx2 = tx || ssi->synchronous;
Nicolin Chen52eee842017-12-17 18:52:10 -0800863 regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl);
Timur Tabi17467f22008-01-11 18:15:26 +0100864
Shengjiu Wang7aded702022-05-10 19:56:48 +0800865 if (ssi->use_dyna_fifo) {
866 if (channels == 1) {
867 ssi->audio_config[0].n_fifos_dst = 1;
868 ssi->audio_config[1].n_fifos_src = 1;
869 vals[RX].srcr &= ~SSI_SRCR_RFEN1;
870 vals[TX].stcr &= ~SSI_STCR_TFEN1;
871 vals[RX].scr &= ~SSI_SCR_TCH_EN;
872 vals[TX].scr &= ~SSI_SCR_TCH_EN;
873 } else {
874 ssi->audio_config[0].n_fifos_dst = 2;
875 ssi->audio_config[1].n_fifos_src = 2;
876 vals[RX].srcr |= SSI_SRCR_RFEN1;
877 vals[TX].stcr |= SSI_STCR_TFEN1;
878 vals[RX].scr |= SSI_SCR_TCH_EN;
879 vals[TX].scr |= SSI_SCR_TCH_EN;
880 }
881 ssi->dma_params_tx.peripheral_config = &ssi->audio_config[0];
882 ssi->dma_params_tx.peripheral_size = sizeof(ssi->audio_config[0]);
883 ssi->dma_params_rx.peripheral_config = &ssi->audio_config[1];
884 ssi->dma_params_rx.peripheral_size = sizeof(ssi->audio_config[1]);
885 }
886
Timur Tabi17467f22008-01-11 18:15:26 +0100887 return 0;
888}
889
Markus Pargmannd429d8e2014-05-27 10:24:23 +0200890static int fsl_ssi_hw_free(struct snd_pcm_substream *substream,
Nicolin Chen0c884be2017-12-17 18:52:06 -0800891 struct snd_soc_dai *dai)
Markus Pargmannd429d8e2014-05-27 10:24:23 +0200892{
Kuninori Morimoto9f5f0782020-07-20 10:18:38 +0900893 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
Kuninori Morimoto17198ae2020-03-23 14:18:30 +0900894 struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
Markus Pargmannd429d8e2014-05-27 10:24:23 +0200895
Mark Brown89efbda2021-09-21 22:35:33 +0100896 if (fsl_ssi_is_i2s_clock_provider(ssi) &&
Nicolin Chenaf4f7f32017-12-17 18:52:04 -0800897 ssi->baudclk_streams & BIT(substream->stream)) {
Nicolin Chenf3176832017-12-17 18:52:00 -0800898 clk_disable_unprepare(ssi->baudclk);
899 ssi->baudclk_streams &= ~BIT(substream->stream);
Markus Pargmannd429d8e2014-05-27 10:24:23 +0200900 }
901
902 return 0;
903}
904
Nicolin Chen26b31f4f2018-02-12 14:03:22 -0800905static int _fsl_ssi_set_dai_fmt(struct fsl_ssi *ssi, unsigned int fmt)
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800906{
Nicolin Chen26b31f4f2018-02-12 14:03:22 -0800907 u32 strcr = 0, scr = 0, stcr, srcr, mask;
Alexander Shiyan87263962021-02-16 14:42:21 +0300908 unsigned int slots;
Markus Pargmann2b0db992014-03-15 13:44:09 +0100909
Nicolin Chenf3176832017-12-17 18:52:00 -0800910 ssi->dai_fmt = fmt;
Markus Pargmann171d6832014-04-28 12:54:48 +0200911
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800912 /* Synchronize frame sync clock for TE to avoid data slipping */
Nicolin Chena818aa52017-12-17 18:52:03 -0800913 scr |= SSI_SCR_SYNC_TX_FS;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800914
Nicolin Chen26b31f4f2018-02-12 14:03:22 -0800915 /* Set to default shifting settings: LSB_ALIGNED */
916 strcr |= SSI_STCR_TXBIT0;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800917
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800918 /* Use Network mode as default */
Nicolin Chen8bc84a32017-12-17 18:52:09 -0800919 ssi->i2s_net = SSI_SCR_NET;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800920 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
921 case SND_SOC_DAIFMT_I2S:
Mark Brown89efbda2021-09-21 22:35:33 +0100922 switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
Charles Keepax3b14c152022-05-19 16:42:30 +0100923 case SND_SOC_DAIFMT_BP_FP:
Nicolin Chen26b31f4f2018-02-12 14:03:22 -0800924 if (IS_ERR(ssi->baudclk)) {
925 dev_err(ssi->dev,
926 "missing baudclk for master mode\n");
927 return -EINVAL;
928 }
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -0500929 fallthrough;
Charles Keepax3b14c152022-05-19 16:42:30 +0100930 case SND_SOC_DAIFMT_BC_FP:
Nicolin Chen8bc84a32017-12-17 18:52:09 -0800931 ssi->i2s_net |= SSI_SCR_I2S_MODE_MASTER;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800932 break;
Charles Keepax3b14c152022-05-19 16:42:30 +0100933 case SND_SOC_DAIFMT_BC_FC:
Nicolin Chen8bc84a32017-12-17 18:52:09 -0800934 ssi->i2s_net |= SSI_SCR_I2S_MODE_SLAVE;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800935 break;
936 default:
937 return -EINVAL;
938 }
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800939
Alexander Shiyan87263962021-02-16 14:42:21 +0300940 slots = ssi->slots ? : 2;
Nicolin Chen26b31f4f2018-02-12 14:03:22 -0800941 regmap_update_bits(ssi->regs, REG_SSI_STCCR,
Alexander Shiyan87263962021-02-16 14:42:21 +0300942 SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
Nicolin Chen26b31f4f2018-02-12 14:03:22 -0800943 regmap_update_bits(ssi->regs, REG_SSI_SRCCR,
Alexander Shiyan87263962021-02-16 14:42:21 +0300944 SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
Nicolin Chen26b31f4f2018-02-12 14:03:22 -0800945
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800946 /* Data on rising edge of bclk, frame low, 1clk before data */
Nicolin Chen26b31f4f2018-02-12 14:03:22 -0800947 strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP | SSI_STCR_TEFS;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800948 break;
949 case SND_SOC_DAIFMT_LEFT_J:
950 /* Data on rising edge of bclk, frame high */
Nicolin Chen26b31f4f2018-02-12 14:03:22 -0800951 strcr |= SSI_STCR_TSCKP;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800952 break;
953 case SND_SOC_DAIFMT_DSP_A:
954 /* Data on rising edge of bclk, frame high, 1clk before data */
Nicolin Chen26b31f4f2018-02-12 14:03:22 -0800955 strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | SSI_STCR_TEFS;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800956 break;
957 case SND_SOC_DAIFMT_DSP_B:
958 /* Data on rising edge of bclk, frame high */
Nicolin Chen26b31f4f2018-02-12 14:03:22 -0800959 strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800960 break;
Markus Pargmann2b0db992014-03-15 13:44:09 +0100961 case SND_SOC_DAIFMT_AC97:
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800962 /* Data on falling edge of bclk, frame high, 1clk before data */
Nicolin Chen26b31f4f2018-02-12 14:03:22 -0800963 strcr |= SSI_STCR_TEFS;
Markus Pargmann2b0db992014-03-15 13:44:09 +0100964 break;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800965 default:
966 return -EINVAL;
967 }
Nicolin Chen26b31f4f2018-02-12 14:03:22 -0800968
Nicolin Chen8bc84a32017-12-17 18:52:09 -0800969 scr |= ssi->i2s_net;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800970
971 /* DAI clock inversion */
972 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
973 case SND_SOC_DAIFMT_NB_NF:
974 /* Nothing to do for both normal cases */
975 break;
976 case SND_SOC_DAIFMT_IB_NF:
977 /* Invert bit clock */
Nicolin Chena818aa52017-12-17 18:52:03 -0800978 strcr ^= SSI_STCR_TSCKP;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800979 break;
980 case SND_SOC_DAIFMT_NB_IF:
981 /* Invert frame clock */
Nicolin Chena818aa52017-12-17 18:52:03 -0800982 strcr ^= SSI_STCR_TFSI;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800983 break;
984 case SND_SOC_DAIFMT_IB_IF:
985 /* Invert both clocks */
Nicolin Chena818aa52017-12-17 18:52:03 -0800986 strcr ^= SSI_STCR_TSCKP;
987 strcr ^= SSI_STCR_TFSI;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800988 break;
989 default:
990 return -EINVAL;
991 }
992
Mark Brown89efbda2021-09-21 22:35:33 +0100993 /* DAI clock provider masks */
994 switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
Charles Keepax3b14c152022-05-19 16:42:30 +0100995 case SND_SOC_DAIFMT_BP_FP:
Nicolin Chen7a8fceb2017-12-17 18:52:02 -0800996 /* Output bit and frame sync clocks */
Nicolin Chena818aa52017-12-17 18:52:03 -0800997 strcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
998 scr |= SSI_SCR_SYS_CLK_EN;
Nicolin Chenaafa85e2013-12-12 18:44:45 +0800999 break;
Charles Keepax3b14c152022-05-19 16:42:30 +01001000 case SND_SOC_DAIFMT_BC_FC:
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001001 /* Input bit or frame sync clocks */
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001002 break;
Charles Keepax3b14c152022-05-19 16:42:30 +01001003 case SND_SOC_DAIFMT_BC_FP:
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001004 /* Input bit clock but output frame sync clock */
Nicolin Chena818aa52017-12-17 18:52:03 -08001005 strcr |= SSI_STCR_TFDIR;
Fabio Falzoicf4f7fc2014-08-04 17:08:07 +02001006 break;
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001007 default:
Nicolin Chenb6c93f7f2018-02-12 14:03:16 -08001008 return -EINVAL;
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001009 }
1010
Nicolin Chen26b31f4f2018-02-12 14:03:22 -08001011 stcr = strcr;
1012 srcr = strcr;
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001013
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001014 /* Set SYN mode and clear RXDIR bit when using SYN or AC97 mode */
Nicolin Chenbadc9592018-02-12 14:03:23 -08001015 if (ssi->synchronous || fsl_ssi_is_ac97(ssi)) {
Nicolin Chena818aa52017-12-17 18:52:03 -08001016 srcr &= ~SSI_SRCR_RXDIR;
1017 scr |= SSI_SCR_SYN;
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001018 }
1019
Nicolin Chen26b31f4f2018-02-12 14:03:22 -08001020 mask = SSI_STCR_TFDIR | SSI_STCR_TXDIR | SSI_STCR_TSCKP |
1021 SSI_STCR_TFSL | SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
1022
1023 regmap_update_bits(ssi->regs, REG_SSI_STCR, mask, stcr);
1024 regmap_update_bits(ssi->regs, REG_SSI_SRCR, mask, srcr);
1025
1026 mask = SSI_SCR_SYNC_TX_FS | SSI_SCR_I2S_MODE_MASK |
1027 SSI_SCR_SYS_CLK_EN | SSI_SCR_SYN;
1028 regmap_update_bits(ssi->regs, REG_SSI_SCR, mask, scr);
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001029
1030 return 0;
Markus Pargmann85e59af2014-05-27 10:24:19 +02001031}
1032
1033/**
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -05001034 * fsl_ssi_set_dai_fmt - Configure Digital Audio Interface (DAI) Format
1035 * @dai: pointer to DAI
1036 * @fmt: format mask
Markus Pargmann85e59af2014-05-27 10:24:19 +02001037 */
Nicolin Chen0c884be2017-12-17 18:52:06 -08001038static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
Markus Pargmann85e59af2014-05-27 10:24:19 +02001039{
Nicolin Chen0c884be2017-12-17 18:52:06 -08001040 struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
Markus Pargmann85e59af2014-05-27 10:24:19 +02001041
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001042 /* AC97 configured DAIFMT earlier in the probe() */
Nicolin Chenf3176832017-12-17 18:52:00 -08001043 if (fsl_ssi_is_ac97(ssi))
Maciej S. Szmigieroc997a922017-11-22 00:55:14 +01001044 return 0;
1045
Nicolin Chen26b31f4f2018-02-12 14:03:22 -08001046 return _fsl_ssi_set_dai_fmt(ssi, fmt);
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001047}
1048
1049/**
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -05001050 * fsl_ssi_set_dai_tdm_slot - Set TDM slot number and slot width
1051 * @dai: pointer to DAI
1052 * @tx_mask: mask for TX
1053 * @rx_mask: mask for RX
1054 * @slots: number of slots
1055 * @slot_width: number of bits per slot
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001056 */
Nicolin Chen0c884be2017-12-17 18:52:06 -08001057static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001058 u32 rx_mask, int slots, int slot_width)
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001059{
Nicolin Chen0c884be2017-12-17 18:52:06 -08001060 struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
Nicolin Chenf3176832017-12-17 18:52:00 -08001061 struct regmap *regs = ssi->regs;
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001062 u32 val;
1063
Nicolin Chenb0a70432017-09-13 20:07:09 -07001064 /* The word length should be 8, 10, 12, 16, 18, 20, 22 or 24 */
1065 if (slot_width & 1 || slot_width < 8 || slot_width > 24) {
Nicolin Chen0c884be2017-12-17 18:52:06 -08001066 dev_err(dai->dev, "invalid slot width: %d\n", slot_width);
Nicolin Chenb0a70432017-09-13 20:07:09 -07001067 return -EINVAL;
1068 }
1069
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001070 /* The slot number should be >= 2 if using Network mode or I2S mode */
Nicolin Chen09947632018-02-12 14:03:11 -08001071 if (ssi->i2s_net && slots < 2) {
Nicolin Chen0c884be2017-12-17 18:52:06 -08001072 dev_err(dai->dev, "slot number should be >= 2 in I2S or NET\n");
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001073 return -EINVAL;
1074 }
1075
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001076 regmap_update_bits(regs, REG_SSI_STCCR,
1077 SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
1078 regmap_update_bits(regs, REG_SSI_SRCCR,
1079 SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001080
Nicolin Chen09947632018-02-12 14:03:11 -08001081 /* Save the SCR register value */
Nicolin Chena818aa52017-12-17 18:52:03 -08001082 regmap_read(regs, REG_SSI_SCR, &val);
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001083 /* Temporarily enable SSI to allow SxMSKs to be configurable */
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001084 regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, SSI_SCR_SSIEN);
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001085
Nicolin Chena818aa52017-12-17 18:52:03 -08001086 regmap_write(regs, REG_SSI_STMSK, ~tx_mask);
1087 regmap_write(regs, REG_SSI_SRMSK, ~rx_mask);
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001088
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001089 /* Restore the value of SSIEN bit */
Nicolin Chena818aa52017-12-17 18:52:03 -08001090 regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, val);
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001091
Nicolin Chenf3176832017-12-17 18:52:00 -08001092 ssi->slot_width = slot_width;
1093 ssi->slots = slots;
Nicolin Chenb0a70432017-09-13 20:07:09 -07001094
Nicolin Chenaafa85e2013-12-12 18:44:45 +08001095 return 0;
1096}
1097
1098/**
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -05001099 * fsl_ssi_trigger - Start or stop SSI and corresponding DMA transaction.
1100 * @substream: ASoC substream
1101 * @cmd: trigger command
1102 * @dai: pointer to DAI
Timur Tabi17467f22008-01-11 18:15:26 +01001103 *
1104 * The DMA channel is in external master start and pause mode, which
1105 * means the SSI completely controls the flow of data.
1106 */
Mark Browndee89c42008-11-18 22:11:38 +00001107static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
1108 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +01001109{
Kuninori Morimoto9f5f0782020-07-20 10:18:38 +09001110 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
Kuninori Morimoto17198ae2020-03-23 14:18:30 +09001111 struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
Nicolin Chen7d67bcb2018-02-12 14:03:15 -08001112 bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
Michael Grzeschik9b443e32013-08-19 17:06:00 +02001113
Timur Tabi17467f22008-01-11 18:15:26 +01001114 switch (cmd) {
1115 case SNDRV_PCM_TRIGGER_START:
Fabio Estevamb20e53a2014-05-23 02:38:56 -03001116 case SNDRV_PCM_TRIGGER_RESUME:
Timur Tabi17467f22008-01-11 18:15:26 +01001117 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Nicolin Chen7d67bcb2018-02-12 14:03:15 -08001118 /*
1119 * SACCST might be modified via AC Link by a CODEC if it sends
1120 * extra bits in their SLOTREQ requests, which'll accidentally
1121 * send valid data to slots other than normal playback slots.
1122 *
1123 * To be safe, configure SACCST right before TX starts.
1124 */
1125 if (tx && fsl_ssi_is_ac97(ssi))
1126 fsl_ssi_tx_ac97_saccst_setup(ssi);
1127 fsl_ssi_config_enable(ssi, tx);
Timur Tabi17467f22008-01-11 18:15:26 +01001128 break;
1129
1130 case SNDRV_PCM_TRIGGER_STOP:
Fabio Estevamb20e53a2014-05-23 02:38:56 -03001131 case SNDRV_PCM_TRIGGER_SUSPEND:
Timur Tabi17467f22008-01-11 18:15:26 +01001132 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
Nicolin Chen7d67bcb2018-02-12 14:03:15 -08001133 fsl_ssi_config_disable(ssi, tx);
Timur Tabi17467f22008-01-11 18:15:26 +01001134 break;
1135
1136 default:
1137 return -EINVAL;
1138 }
1139
1140 return 0;
1141}
1142
Lars-Peter Clausenfc8ba7f2013-04-15 19:19:58 +02001143static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
1144{
Nicolin Chenf3176832017-12-17 18:52:00 -08001145 struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
Lars-Peter Clausenfc8ba7f2013-04-15 19:19:58 +02001146
Nicolin Chen40f25632018-02-12 14:03:19 -08001147 if (ssi->soc->imx && ssi->use_dma)
1148 snd_soc_dai_init_dma_data(dai, &ssi->dma_params_tx,
1149 &ssi->dma_params_rx);
Lars-Peter Clausenfc8ba7f2013-04-15 19:19:58 +02001150
1151 return 0;
1152}
1153
Lars-Peter Clausen85e76522011-11-23 11:40:40 +01001154static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001155 .startup = fsl_ssi_startup,
1156 .shutdown = fsl_ssi_shutdown,
1157 .hw_params = fsl_ssi_hw_params,
1158 .hw_free = fsl_ssi_hw_free,
Charles Keepax00778272022-05-19 16:42:57 +01001159 .set_fmt = fsl_ssi_set_dai_fmt,
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001160 .set_tdm_slot = fsl_ssi_set_dai_tdm_slot,
1161 .trigger = fsl_ssi_trigger,
Eric Miao6335d052009-03-03 09:41:00 +08001162};
1163
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00001164static struct snd_soc_dai_driver fsl_ssi_dai_template = {
Lars-Peter Clausenfc8ba7f2013-04-15 19:19:58 +02001165 .probe = fsl_ssi_dai_probe,
Timur Tabi17467f22008-01-11 18:15:26 +01001166 .playback = {
Nicolin Chene3655002014-07-30 11:10:29 +08001167 .stream_name = "CPU-Playback",
Nicolin Chen2924a992013-12-02 23:29:03 +08001168 .channels_min = 1,
Arnaud Mouiche48a260e2016-05-03 14:13:55 +02001169 .channels_max = 32,
Fabio Estevam58055672017-04-05 16:44:05 -03001170 .rates = SNDRV_PCM_RATE_CONTINUOUS,
Timur Tabi17467f22008-01-11 18:15:26 +01001171 .formats = FSLSSI_I2S_FORMATS,
1172 },
1173 .capture = {
Nicolin Chene3655002014-07-30 11:10:29 +08001174 .stream_name = "CPU-Capture",
Nicolin Chen2924a992013-12-02 23:29:03 +08001175 .channels_min = 1,
Arnaud Mouiche48a260e2016-05-03 14:13:55 +02001176 .channels_max = 32,
Fabio Estevam58055672017-04-05 16:44:05 -03001177 .rates = SNDRV_PCM_RATE_CONTINUOUS,
Timur Tabi17467f22008-01-11 18:15:26 +01001178 .formats = FSLSSI_I2S_FORMATS,
1179 },
Eric Miao6335d052009-03-03 09:41:00 +08001180 .ops = &fsl_ssi_dai_ops,
Timur Tabi17467f22008-01-11 18:15:26 +01001181};
1182
Kuninori Morimoto3580aa12013-03-21 03:32:04 -07001183static const struct snd_soc_component_driver fsl_ssi_component = {
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001184 .name = "fsl-ssi",
Charles Keepax1e63fcc2022-06-23 13:51:34 +01001185 .legacy_dai_naming = 1,
Kuninori Morimoto3580aa12013-03-21 03:32:04 -07001186};
1187
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001188static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
Nicolin Chen76f38452018-02-12 14:03:24 -08001189 .symmetric_channels = 1,
Maciej S. Szmigiero793e3e92015-08-05 17:22:53 +02001190 .probe = fsl_ssi_dai_probe,
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001191 .playback = {
Mark Brown8c6a42b2023-01-06 23:15:06 +00001192 .stream_name = "CPU AC97 Playback",
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001193 .channels_min = 2,
1194 .channels_max = 2,
1195 .rates = SNDRV_PCM_RATE_8000_48000,
Maciej S. Szmigiero10582632017-11-27 23:34:44 +01001196 .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S20,
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001197 },
1198 .capture = {
Mark Brown8c6a42b2023-01-06 23:15:06 +00001199 .stream_name = "CPU AC97 Capture",
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001200 .channels_min = 2,
1201 .channels_max = 2,
1202 .rates = SNDRV_PCM_RATE_48000,
Maciej S. Szmigiero10582632017-11-27 23:34:44 +01001203 /* 16-bit capture is broken (errata ERR003778) */
1204 .formats = SNDRV_PCM_FMTBIT_S20,
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001205 },
Markus Pargmanna5a7ee72013-12-20 14:11:35 +01001206 .ops = &fsl_ssi_dai_ops,
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001207};
1208
Nicolin Chenf3176832017-12-17 18:52:00 -08001209static struct fsl_ssi *fsl_ac97_data;
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001210
Sachin Kamata851a2b2013-09-13 15:22:17 +05301211static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001212 unsigned short val)
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001213{
Markus Pargmann43248122014-05-27 10:24:25 +02001214 struct regmap *regs = fsl_ac97_data->regs;
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001215 unsigned int lreg;
1216 unsigned int lval;
Maciej S. Szmigiero8277df32015-08-05 17:21:35 +02001217 int ret;
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001218
1219 if (reg > 0x7f)
1220 return;
1221
Maciej S. Szmigierob880b802017-11-20 23:16:07 +01001222 mutex_lock(&fsl_ac97_data->ac97_reg_lock);
1223
Maciej S. Szmigiero8277df32015-08-05 17:21:35 +02001224 ret = clk_prepare_enable(fsl_ac97_data->clk);
1225 if (ret) {
1226 pr_err("ac97 write clk_prepare_enable failed: %d\n",
1227 ret);
Maciej S. Szmigierob880b802017-11-20 23:16:07 +01001228 goto ret_unlock;
Maciej S. Szmigiero8277df32015-08-05 17:21:35 +02001229 }
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001230
1231 lreg = reg << 12;
Nicolin Chena818aa52017-12-17 18:52:03 -08001232 regmap_write(regs, REG_SSI_SACADD, lreg);
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001233
1234 lval = val << 4;
Nicolin Chena818aa52017-12-17 18:52:03 -08001235 regmap_write(regs, REG_SSI_SACDAT, lval);
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001236
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001237 regmap_update_bits(regs, REG_SSI_SACNT,
1238 SSI_SACNT_RDWR_MASK, SSI_SACNT_WR);
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001239 udelay(100);
Maciej S. Szmigiero8277df32015-08-05 17:21:35 +02001240
1241 clk_disable_unprepare(fsl_ac97_data->clk);
Maciej S. Szmigierob880b802017-11-20 23:16:07 +01001242
1243ret_unlock:
1244 mutex_unlock(&fsl_ac97_data->ac97_reg_lock);
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001245}
1246
Sachin Kamata851a2b2013-09-13 15:22:17 +05301247static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001248 unsigned short reg)
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001249{
Markus Pargmann43248122014-05-27 10:24:25 +02001250 struct regmap *regs = fsl_ac97_data->regs;
Maciej S. Szmigierob880b802017-11-20 23:16:07 +01001251 unsigned short val = 0;
Markus Pargmann43248122014-05-27 10:24:25 +02001252 u32 reg_val;
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001253 unsigned int lreg;
Maciej S. Szmigiero8277df32015-08-05 17:21:35 +02001254 int ret;
1255
Maciej S. Szmigierob880b802017-11-20 23:16:07 +01001256 mutex_lock(&fsl_ac97_data->ac97_reg_lock);
1257
Maciej S. Szmigiero8277df32015-08-05 17:21:35 +02001258 ret = clk_prepare_enable(fsl_ac97_data->clk);
1259 if (ret) {
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001260 pr_err("ac97 read clk_prepare_enable failed: %d\n", ret);
Maciej S. Szmigierob880b802017-11-20 23:16:07 +01001261 goto ret_unlock;
Maciej S. Szmigiero8277df32015-08-05 17:21:35 +02001262 }
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001263
1264 lreg = (reg & 0x7f) << 12;
Nicolin Chena818aa52017-12-17 18:52:03 -08001265 regmap_write(regs, REG_SSI_SACADD, lreg);
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001266 regmap_update_bits(regs, REG_SSI_SACNT,
1267 SSI_SACNT_RDWR_MASK, SSI_SACNT_RD);
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001268
1269 udelay(100);
1270
Nicolin Chena818aa52017-12-17 18:52:03 -08001271 regmap_read(regs, REG_SSI_SACDAT, &reg_val);
Markus Pargmann43248122014-05-27 10:24:25 +02001272 val = (reg_val >> 4) & 0xffff;
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001273
Maciej S. Szmigiero8277df32015-08-05 17:21:35 +02001274 clk_disable_unprepare(fsl_ac97_data->clk);
1275
Maciej S. Szmigierob880b802017-11-20 23:16:07 +01001276ret_unlock:
1277 mutex_unlock(&fsl_ac97_data->ac97_reg_lock);
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001278 return val;
1279}
1280
1281static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = {
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001282 .read = fsl_ssi_ac97_read,
1283 .write = fsl_ssi_ac97_write,
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001284};
1285
Timur Tabi17467f22008-01-11 18:15:26 +01001286/**
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -05001287 * fsl_ssi_hw_init - Initialize SSI registers
1288 * @ssi: SSI context
Nicolin Chena1d154a2018-02-12 14:03:20 -08001289 */
1290static int fsl_ssi_hw_init(struct fsl_ssi *ssi)
1291{
1292 u32 wm = ssi->fifo_watermark;
1293
1294 /* Initialize regvals */
1295 fsl_ssi_setup_regvals(ssi);
1296
1297 /* Set watermarks */
1298 regmap_write(ssi->regs, REG_SSI_SFCSR,
1299 SSI_SFCSR_TFWM0(wm) | SSI_SFCSR_RFWM0(wm) |
1300 SSI_SFCSR_TFWM1(wm) | SSI_SFCSR_RFWM1(wm));
1301
1302 /* Enable Dual FIFO mode */
1303 if (ssi->use_dual_fifo)
1304 regmap_update_bits(ssi->regs, REG_SSI_SCR,
1305 SSI_SCR_TCH_EN, SSI_SCR_TCH_EN);
1306
Nicolin Chen37ac30a2018-02-12 14:03:21 -08001307 /* AC97 should start earlier to communicate with CODECs */
1308 if (fsl_ssi_is_ac97(ssi)) {
Nicolin Chen26b31f4f2018-02-12 14:03:22 -08001309 _fsl_ssi_set_dai_fmt(ssi, ssi->dai_fmt);
Nicolin Chen37ac30a2018-02-12 14:03:21 -08001310 fsl_ssi_setup_ac97(ssi);
1311 }
1312
Nicolin Chena1d154a2018-02-12 14:03:20 -08001313 return 0;
1314}
1315
1316/**
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -05001317 * fsl_ssi_hw_clean - Clear SSI registers
1318 * @ssi: SSI context
Nicolin Chen37ac30a2018-02-12 14:03:21 -08001319 */
1320static void fsl_ssi_hw_clean(struct fsl_ssi *ssi)
1321{
1322 /* Disable registers for AC97 */
1323 if (fsl_ssi_is_ac97(ssi)) {
1324 /* Disable TE and RE bits first */
1325 regmap_update_bits(ssi->regs, REG_SSI_SCR,
1326 SSI_SCR_TE | SSI_SCR_RE, 0);
1327 /* Disable AC97 mode */
1328 regmap_write(ssi->regs, REG_SSI_SACNT, 0);
1329 /* Unset WAIT bits */
1330 regmap_write(ssi->regs, REG_SSI_SOR, 0);
1331 /* Disable SSI -- software reset */
1332 regmap_update_bits(ssi->regs, REG_SSI_SCR, SSI_SCR_SSIEN, 0);
1333 }
1334}
Pierre-Louis Bossart6ababfc2020-07-02 14:21:37 -05001335
1336/*
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00001337 * Make every character in a string lower-case
Timur Tabi17467f22008-01-11 18:15:26 +01001338 */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00001339static void make_lowercase(char *s)
Timur Tabi17467f22008-01-11 18:15:26 +01001340{
Fabio Estevamc6682fe2017-04-05 16:44:06 -03001341 if (!s)
1342 return;
1343 for (; *s; s++)
1344 *s = tolower(*s);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00001345}
1346
Markus Pargmann49da09e2014-04-28 12:54:45 +02001347static int fsl_ssi_imx_probe(struct platform_device *pdev,
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001348 struct fsl_ssi *ssi, void __iomem *iomem)
Markus Pargmann49da09e2014-04-28 12:54:45 +02001349{
Nicolin Chen8483c062017-12-17 18:52:01 -08001350 struct device *dev = &pdev->dev;
Markus Pargmann49da09e2014-04-28 12:54:45 +02001351 int ret;
1352
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001353 /* Backward compatible for a DT without ipg clock name assigned */
Nicolin Chenf3176832017-12-17 18:52:00 -08001354 if (ssi->has_ipg_clk_name)
Nicolin Chen8483c062017-12-17 18:52:01 -08001355 ssi->clk = devm_clk_get(dev, "ipg");
Shengjiu Wangf4a43ca2014-09-16 10:13:16 +08001356 else
Nicolin Chen8483c062017-12-17 18:52:01 -08001357 ssi->clk = devm_clk_get(dev, NULL);
Nicolin Chenf3176832017-12-17 18:52:00 -08001358 if (IS_ERR(ssi->clk)) {
1359 ret = PTR_ERR(ssi->clk);
Nicolin Chen2c225032017-12-17 18:52:05 -08001360 dev_err(dev, "failed to get clock: %d\n", ret);
Markus Pargmann49da09e2014-04-28 12:54:45 +02001361 return ret;
1362 }
1363
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001364 /* Enable the clock since regmap will not handle it in this case */
Nicolin Chenf3176832017-12-17 18:52:00 -08001365 if (!ssi->has_ipg_clk_name) {
1366 ret = clk_prepare_enable(ssi->clk);
Shengjiu Wangf4a43ca2014-09-16 10:13:16 +08001367 if (ret) {
Nicolin Chen8483c062017-12-17 18:52:01 -08001368 dev_err(dev, "clk_prepare_enable failed: %d\n", ret);
Shengjiu Wangf4a43ca2014-09-16 10:13:16 +08001369 return ret;
1370 }
Markus Pargmann49da09e2014-04-28 12:54:45 +02001371 }
1372
Mark Brown89efbda2021-09-21 22:35:33 +01001373 /* Do not error out for consumer cases that live without a baud clock */
Nicolin Chen8483c062017-12-17 18:52:01 -08001374 ssi->baudclk = devm_clk_get(dev, "baud");
Nicolin Chenf3176832017-12-17 18:52:00 -08001375 if (IS_ERR(ssi->baudclk))
Nicolin Chen2c225032017-12-17 18:52:05 -08001376 dev_dbg(dev, "failed to get baud clock: %ld\n",
Nicolin Chenf3176832017-12-17 18:52:00 -08001377 PTR_ERR(ssi->baudclk));
Markus Pargmann49da09e2014-04-28 12:54:45 +02001378
Nicolin Chenf3176832017-12-17 18:52:00 -08001379 ssi->dma_params_tx.maxburst = ssi->dma_maxburst;
1380 ssi->dma_params_rx.maxburst = ssi->dma_maxburst;
Nicolin Chena818aa52017-12-17 18:52:03 -08001381 ssi->dma_params_tx.addr = ssi->ssi_phys + REG_SSI_STX0;
1382 ssi->dma_params_rx.addr = ssi->ssi_phys + REG_SSI_SRX0;
Markus Pargmann49da09e2014-04-28 12:54:45 +02001383
Nicolin Chen76f38452018-02-12 14:03:24 -08001384 /* Use even numbers to avoid channel swap due to SDMA script design */
Shengjiu Wang7aded702022-05-10 19:56:48 +08001385 if (ssi->use_dual_fifo || ssi->use_dyna_fifo) {
Nicolin Chenf3176832017-12-17 18:52:00 -08001386 ssi->dma_params_tx.maxburst &= ~0x1;
1387 ssi->dma_params_rx.maxburst &= ~0x1;
Markus Pargmann49da09e2014-04-28 12:54:45 +02001388 }
1389
Nicolin Chenf3176832017-12-17 18:52:00 -08001390 if (!ssi->use_dma) {
Markus Pargmann4d9b79262014-04-28 12:54:47 +02001391 /*
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001392 * Some boards use an incompatible codec. Use imx-fiq-pcm-audio
1393 * to get it working, as DMA is not possible in this situation.
Markus Pargmann4d9b79262014-04-28 12:54:47 +02001394 */
Nicolin Chenf3176832017-12-17 18:52:00 -08001395 ssi->fiq_params.irq = ssi->irq;
1396 ssi->fiq_params.base = iomem;
1397 ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx;
1398 ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx;
Markus Pargmann4d9b79262014-04-28 12:54:47 +02001399
Nicolin Chenf3176832017-12-17 18:52:00 -08001400 ret = imx_pcm_fiq_init(pdev, &ssi->fiq_params);
Markus Pargmann4d9b79262014-04-28 12:54:47 +02001401 if (ret)
1402 goto error_pcm;
1403 } else {
Sascha Hauer9b3ff632022-02-23 14:06:25 +01001404 ret = imx_pcm_dma_init(pdev);
Markus Pargmann4d9b79262014-04-28 12:54:47 +02001405 if (ret)
1406 goto error_pcm;
1407 }
1408
Markus Pargmann49da09e2014-04-28 12:54:45 +02001409 return 0;
Markus Pargmann4d9b79262014-04-28 12:54:47 +02001410
1411error_pcm:
Nicolin Chenf3176832017-12-17 18:52:00 -08001412 if (!ssi->has_ipg_clk_name)
1413 clk_disable_unprepare(ssi->clk);
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001414
Markus Pargmann4d9b79262014-04-28 12:54:47 +02001415 return ret;
Markus Pargmann49da09e2014-04-28 12:54:45 +02001416}
1417
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001418static void fsl_ssi_imx_clean(struct platform_device *pdev, struct fsl_ssi *ssi)
Markus Pargmann49da09e2014-04-28 12:54:45 +02001419{
Nicolin Chenf3176832017-12-17 18:52:00 -08001420 if (!ssi->use_dma)
Markus Pargmann4d9b79262014-04-28 12:54:47 +02001421 imx_pcm_fiq_exit(pdev);
Nicolin Chenf3176832017-12-17 18:52:00 -08001422 if (!ssi->has_ipg_clk_name)
1423 clk_disable_unprepare(ssi->clk);
Markus Pargmann49da09e2014-04-28 12:54:45 +02001424}
1425
Nicolin Chen76f38452018-02-12 14:03:24 -08001426static int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi)
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00001427{
Nicolin Chen76f38452018-02-12 14:03:24 -08001428 struct device *dev = ssi->dev;
1429 struct device_node *np = dev->of_node;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00001430 const char *p, *sprop;
Fabio Estevamda18bcf2018-02-11 19:53:21 -02001431 const __be32 *iprop;
Nicolin Chen76f38452018-02-12 14:03:24 -08001432 u32 dmas[4];
1433 int ret;
Timur Tabi17467f22008-01-11 18:15:26 +01001434
Nicolin Chen76f38452018-02-12 14:03:24 -08001435 ret = of_property_match_string(np, "clock-names", "ipg");
1436 /* Get error code if not found */
1437 ssi->has_ipg_clk_name = ret >= 0;
Sascha Hauerfcdbadef2014-05-27 10:24:18 +02001438
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001439 /* Check if being used in AC97 mode */
Markus Pargmann85e59af2014-05-27 10:24:19 +02001440 sprop = of_get_property(np, "fsl,mode", NULL);
Nicolin Chen76f38452018-02-12 14:03:24 -08001441 if (sprop && !strcmp(sprop, "ac97-slave")) {
1442 ssi->dai_fmt = FSLSSI_AC97_DAIFMT;
1443
1444 ret = of_property_read_u32(np, "cell-index", &ssi->card_idx);
1445 if (ret) {
1446 dev_err(dev, "failed to get SSI index property\n");
1447 return -EINVAL;
1448 }
1449 strcpy(ssi->card_name, "ac97-codec");
1450 } else if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) {
1451 /*
1452 * In synchronous mode, STCK and STFS ports are used by RX
1453 * as well. So the software should limit the sample rates,
1454 * sample bits and channels to be symmetric.
1455 *
1456 * This is exclusive with FSLSSI_AC97_FORMATS as AC97 runs
1457 * in the SSI synchronous mode however it does not have to
1458 * limit symmetric sample rates and sample bits.
1459 */
1460 ssi->synchronous = true;
Markus Pargmann85e59af2014-05-27 10:24:19 +02001461 }
1462
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001463 /* Select DMA or FIQ */
Nicolin Chenf3176832017-12-17 18:52:00 -08001464 ssi->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter");
Markus Pargmannde623ec2013-07-27 13:31:53 +02001465
Nicolin Chen76f38452018-02-12 14:03:24 -08001466 /* Fetch FIFO depth; Set to 8 for older DT without this property */
1467 iprop = of_get_property(np, "fsl,fifo-depth", NULL);
1468 if (iprop)
1469 ssi->fifo_depth = be32_to_cpup(iprop);
1470 else
1471 ssi->fifo_depth = 8;
1472
1473 /* Use dual FIFO mode depending on the support from SDMA script */
1474 ret = of_property_read_u32_array(np, "dmas", dmas, 4);
1475 if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL)
1476 ssi->use_dual_fifo = true;
1477
Shengjiu Wang7aded702022-05-10 19:56:48 +08001478 if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI)
1479 ssi->use_dyna_fifo = true;
Nicolin Chen76f38452018-02-12 14:03:24 -08001480 /*
1481 * Backward compatible for older bindings by manually triggering the
1482 * machine driver's probe(). Use /compatible property, including the
1483 * address of CPU DAI driver structure, as the name of machine driver
1484 *
1485 * If card_name is set by AC97 earlier, bypass here since it uses a
1486 * different name to register the device.
1487 */
1488 if (!ssi->card_name[0] && of_get_property(np, "codec-handle", NULL)) {
Takashi Iwai27579702019-02-19 16:46:47 +01001489 struct device_node *root = of_find_node_by_path("/");
1490
1491 sprop = of_get_property(root, "compatible", NULL);
1492 of_node_put(root);
Nicolin Chen76f38452018-02-12 14:03:24 -08001493 /* Strip "fsl," in the compatible name if applicable */
1494 p = strrchr(sprop, ',');
1495 if (p)
1496 sprop = p + 1;
1497 snprintf(ssi->card_name, sizeof(ssi->card_name),
1498 "snd-soc-%s", sprop);
1499 make_lowercase(ssi->card_name);
1500 ssi->card_idx = 0;
1501 }
1502
1503 return 0;
1504}
1505
1506static int fsl_ssi_probe(struct platform_device *pdev)
1507{
1508 struct regmap_config regconfig = fsl_ssi_regconfig;
1509 struct device *dev = &pdev->dev;
1510 struct fsl_ssi *ssi;
1511 struct resource *res;
1512 void __iomem *iomem;
1513 int ret = 0;
1514
1515 ssi = devm_kzalloc(dev, sizeof(*ssi), GFP_KERNEL);
1516 if (!ssi)
1517 return -ENOMEM;
1518
1519 ssi->dev = dev;
Fabio Estevam9ce63202021-01-18 09:38:10 -03001520 ssi->soc = of_device_get_match_data(&pdev->dev);
Nicolin Chen76f38452018-02-12 14:03:24 -08001521
1522 /* Probe from DT */
1523 ret = fsl_ssi_probe_from_dt(ssi);
1524 if (ret)
1525 return ret;
1526
Nicolin Chenf3176832017-12-17 18:52:00 -08001527 if (fsl_ssi_is_ac97(ssi)) {
1528 memcpy(&ssi->cpu_dai_drv, &fsl_ssi_ac97_dai,
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001529 sizeof(fsl_ssi_ac97_dai));
Nicolin Chenf3176832017-12-17 18:52:00 -08001530 fsl_ac97_data = ssi;
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001531 } else {
Nicolin Chenf3176832017-12-17 18:52:00 -08001532 memcpy(&ssi->cpu_dai_drv, &fsl_ssi_dai_template,
Markus Pargmanncd7f0292013-08-19 17:05:58 +02001533 sizeof(fsl_ssi_dai_template));
1534 }
Nicolin Chen8483c062017-12-17 18:52:01 -08001535 ssi->cpu_dai_drv.name = dev_name(dev);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00001536
Yang Yingliang67798862021-06-15 09:39:21 +08001537 iomem = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
Fabio Estevamca264182015-04-10 07:12:29 -03001538 if (IS_ERR(iomem))
1539 return PTR_ERR(iomem);
Nicolin Chenf3176832017-12-17 18:52:00 -08001540 ssi->ssi_phys = res->start;
Markus Pargmann43248122014-05-27 10:24:25 +02001541
Nicolin Chenf3176832017-12-17 18:52:00 -08001542 if (ssi->soc->imx21regs) {
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001543 /* No SACC{ST,EN,DIS} regs in imx21-class SSI */
Nicolin Chena818aa52017-12-17 18:52:03 -08001544 regconfig.max_register = REG_SSI_SRMSK;
Mark Brownf26b3b22016-09-29 11:22:52 -07001545 regconfig.num_reg_defaults_raw =
Nicolin Chena818aa52017-12-17 18:52:03 -08001546 REG_SSI_SRMSK / sizeof(uint32_t) + 1;
Maciej S. Szmigiero6139b1b2016-01-18 20:07:44 +01001547 }
1548
Nicolin Chen76f38452018-02-12 14:03:24 -08001549 if (ssi->has_ipg_clk_name)
Nicolin Chen8483c062017-12-17 18:52:01 -08001550 ssi->regs = devm_regmap_init_mmio_clk(dev, "ipg", iomem,
1551 &regconfig);
Nicolin Chen76f38452018-02-12 14:03:24 -08001552 else
1553 ssi->regs = devm_regmap_init_mmio(dev, iomem, &regconfig);
Nicolin Chenf3176832017-12-17 18:52:00 -08001554 if (IS_ERR(ssi->regs)) {
Nicolin Chen2c225032017-12-17 18:52:05 -08001555 dev_err(dev, "failed to init register map\n");
Nicolin Chenf3176832017-12-17 18:52:00 -08001556 return PTR_ERR(ssi->regs);
Markus Pargmann43248122014-05-27 10:24:25 +02001557 }
Timur Tabi1fab6ca2011-08-16 18:47:45 -04001558
Nicolin Chenf3176832017-12-17 18:52:00 -08001559 ssi->irq = platform_get_irq(pdev, 0);
Stephen Boydcf9441a2019-07-30 11:15:49 -07001560 if (ssi->irq < 0)
Nicolin Chenf3176832017-12-17 18:52:00 -08001561 return ssi->irq;
Timur Tabi1fab6ca2011-08-16 18:47:45 -04001562
Nicolin Chen76f38452018-02-12 14:03:24 -08001563 /* Set software limitations for synchronous mode except AC97 */
1564 if (ssi->synchronous && !fsl_ssi_is_ac97(ssi)) {
Kuninori Morimotocb2f6922021-01-15 13:54:08 +09001565 ssi->cpu_dai_drv.symmetric_rate = 1;
Nicolin Chenf3176832017-12-17 18:52:00 -08001566 ssi->cpu_dai_drv.symmetric_channels = 1;
Kuninori Morimotocb2f6922021-01-15 13:54:08 +09001567 ssi->cpu_dai_drv.symmetric_sample_bits = 1;
Nicolin Chen07a94832013-12-03 18:38:07 +08001568 }
Timur Tabi17467f22008-01-11 18:15:26 +01001569
Caleb Crome4ee437f2017-01-03 10:22:57 -08001570 /*
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001571 * Configure TX and RX DMA watermarks -- when to send a DMA request
Caleb Crome4ee437f2017-01-03 10:22:57 -08001572 *
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001573 * Values should be tested to avoid FIFO under/over run. Set maxburst
1574 * to fifo_watermark to maxiumize DMA transaction to reduce overhead.
Caleb Crome4ee437f2017-01-03 10:22:57 -08001575 */
Nicolin Chenf3176832017-12-17 18:52:00 -08001576 switch (ssi->fifo_depth) {
Caleb Crome4ee437f2017-01-03 10:22:57 -08001577 case 15:
1578 /*
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001579 * Set to 8 as a balanced configuration -- When TX FIFO has 8
1580 * empty slots, send a DMA request to fill these 8 slots. The
1581 * remaining 7 slots should be able to allow DMA to finish the
1582 * transaction before TX FIFO underruns; Same applies to RX.
1583 *
1584 * Tested with cases running at 48kHz @ 16 bits x 16 channels
Caleb Crome4ee437f2017-01-03 10:22:57 -08001585 */
Nicolin Chenf3176832017-12-17 18:52:00 -08001586 ssi->fifo_watermark = 8;
1587 ssi->dma_maxburst = 8;
Caleb Crome4ee437f2017-01-03 10:22:57 -08001588 break;
1589 case 8:
1590 default:
Nicolin Chen7a8fceb2017-12-17 18:52:02 -08001591 /* Safely use old watermark configurations for older chips */
Nicolin Chenf3176832017-12-17 18:52:00 -08001592 ssi->fifo_watermark = ssi->fifo_depth - 2;
1593 ssi->dma_maxburst = ssi->fifo_depth - 2;
Caleb Crome4ee437f2017-01-03 10:22:57 -08001594 break;
1595 }
1596
Nicolin Chen8483c062017-12-17 18:52:01 -08001597 dev_set_drvdata(dev, ssi);
Markus Pargmann4d9b79262014-04-28 12:54:47 +02001598
Nicolin Chenf3176832017-12-17 18:52:00 -08001599 if (ssi->soc->imx) {
1600 ret = fsl_ssi_imx_probe(pdev, ssi, iomem);
Markus Pargmann49da09e2014-04-28 12:54:45 +02001601 if (ret)
Fabio Estevam2ffa5312014-12-01 19:57:14 -02001602 return ret;
Markus Pargmann0888efd2013-12-20 14:11:31 +01001603 }
1604
Nicolin Chenf3176832017-12-17 18:52:00 -08001605 if (fsl_ssi_is_ac97(ssi)) {
1606 mutex_init(&ssi->ac97_reg_lock);
Maciej S. Szmigiero695b78b2017-11-20 23:14:55 +01001607 ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
1608 if (ret) {
Nicolin Chen2c225032017-12-17 18:52:05 -08001609 dev_err(dev, "failed to set AC'97 ops\n");
Maciej S. Szmigiero695b78b2017-11-20 23:14:55 +01001610 goto error_ac97_ops;
1611 }
1612 }
1613
Nicolin Chen8483c062017-12-17 18:52:01 -08001614 ret = devm_snd_soc_register_component(dev, &fsl_ssi_component,
Nicolin Chenf3176832017-12-17 18:52:00 -08001615 &ssi->cpu_dai_drv, 1);
Markus Pargmann4d9b79262014-04-28 12:54:47 +02001616 if (ret) {
Nicolin Chen8483c062017-12-17 18:52:01 -08001617 dev_err(dev, "failed to register DAI: %d\n", ret);
Markus Pargmann4d9b79262014-04-28 12:54:47 +02001618 goto error_asoc_register;
1619 }
1620
Nicolin Chenf3176832017-12-17 18:52:00 -08001621 if (ssi->use_dma) {
Nicolin Chen8483c062017-12-17 18:52:01 -08001622 ret = devm_request_irq(dev, ssi->irq, fsl_ssi_isr, 0,
1623 dev_name(dev), ssi);
Michael Grzeschikf0377082013-08-19 17:06:01 +02001624 if (ret < 0) {
Nicolin Chen2c225032017-12-17 18:52:05 -08001625 dev_err(dev, "failed to claim irq %u\n", ssi->irq);
Fabio Estevam299e7e92015-04-09 14:56:41 -03001626 goto error_asoc_register;
Michael Grzeschikf0377082013-08-19 17:06:01 +02001627 }
Shawn Guo09ce1112012-03-16 16:56:43 +08001628 }
1629
Greg Kroah-Hartman227ab8b2019-06-14 11:47:55 +02001630 fsl_ssi_debugfs_create(&ssi->dbg_stats, dev);
Shawn Guo09ce1112012-03-16 16:56:43 +08001631
Nicolin Chena1d154a2018-02-12 14:03:20 -08001632 /* Initially configures SSI registers */
1633 fsl_ssi_hw_init(ssi);
1634
Nicolin Chen76f38452018-02-12 14:03:24 -08001635 /* Register a platform device for older bindings or AC97 */
1636 if (ssi->card_name[0]) {
1637 struct device *parent = dev;
1638 /*
1639 * Do not set SSI dev as the parent of AC97 CODEC device since
1640 * it does not have a DT node. Otherwise ASoC core will assume
1641 * CODEC has the same DT node as the SSI, so it may bypass the
1642 * dai_probe() of SSI and then cause NULL DMA data pointers.
1643 */
1644 if (fsl_ssi_is_ac97(ssi))
1645 parent = NULL;
Maciej S. Szmigiero8ed0c842015-08-05 17:26:44 +02001646
Nicolin Chen76f38452018-02-12 14:03:24 -08001647 ssi->card_pdev = platform_device_register_data(parent,
1648 ssi->card_name, ssi->card_idx, NULL, 0);
1649 if (IS_ERR(ssi->card_pdev)) {
1650 ret = PTR_ERR(ssi->card_pdev);
1651 dev_err(dev, "failed to register %s: %d\n",
1652 ssi->card_name, ret);
Maciej S. Szmigiero8ed0c842015-08-05 17:26:44 +02001653 goto error_sound_card;
1654 }
1655 }
1656
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00001657 return 0;
Timur Tabi87a06322010-08-03 17:55:28 -05001658
Markus Pargmann4d9b79262014-04-28 12:54:47 +02001659error_sound_card:
Nicolin Chenf3176832017-12-17 18:52:00 -08001660 fsl_ssi_debugfs_remove(&ssi->dbg_stats);
Markus Pargmann4d9b79262014-04-28 12:54:47 +02001661error_asoc_register:
Nicolin Chenf3176832017-12-17 18:52:00 -08001662 if (fsl_ssi_is_ac97(ssi))
Maciej S. Szmigiero695b78b2017-11-20 23:14:55 +01001663 snd_soc_set_ac97_ops(NULL);
Maciej S. Szmigiero695b78b2017-11-20 23:14:55 +01001664error_ac97_ops:
Nicolin Chenf3176832017-12-17 18:52:00 -08001665 if (fsl_ssi_is_ac97(ssi))
1666 mutex_destroy(&ssi->ac97_reg_lock);
Maciej S. Szmigierob880b802017-11-20 23:16:07 +01001667
Nicolin Chenf3176832017-12-17 18:52:00 -08001668 if (ssi->soc->imx)
1669 fsl_ssi_imx_clean(pdev, ssi);
Timur Tabi1fab6ca2011-08-16 18:47:45 -04001670
Timur Tabi87a06322010-08-03 17:55:28 -05001671 return ret;
Timur Tabi17467f22008-01-11 18:15:26 +01001672}
Timur Tabi17467f22008-01-11 18:15:26 +01001673
Timur Tabi38fec722010-08-19 15:26:58 -05001674static int fsl_ssi_remove(struct platform_device *pdev)
Timur Tabi17467f22008-01-11 18:15:26 +01001675{
Nicolin Chenf3176832017-12-17 18:52:00 -08001676 struct fsl_ssi *ssi = dev_get_drvdata(&pdev->dev);
Timur Tabi17467f22008-01-11 18:15:26 +01001677
Nicolin Chenf3176832017-12-17 18:52:00 -08001678 fsl_ssi_debugfs_remove(&ssi->dbg_stats);
Markus Pargmann9368acc2013-12-20 14:11:29 +01001679
Nicolin Chen76f38452018-02-12 14:03:24 -08001680 if (ssi->card_pdev)
1681 platform_device_unregister(ssi->card_pdev);
Markus Pargmann49da09e2014-04-28 12:54:45 +02001682
Nicolin Chen37ac30a2018-02-12 14:03:21 -08001683 /* Clean up SSI registers */
1684 fsl_ssi_hw_clean(ssi);
1685
Nicolin Chenf3176832017-12-17 18:52:00 -08001686 if (ssi->soc->imx)
1687 fsl_ssi_imx_clean(pdev, ssi);
Markus Pargmann49da09e2014-04-28 12:54:45 +02001688
Nicolin Chenf3176832017-12-17 18:52:00 -08001689 if (fsl_ssi_is_ac97(ssi)) {
Maciej S. Szmigiero04143d62015-08-05 17:25:31 +02001690 snd_soc_set_ac97_ops(NULL);
Nicolin Chenf3176832017-12-17 18:52:00 -08001691 mutex_destroy(&ssi->ac97_reg_lock);
Maciej S. Szmigierob880b802017-11-20 23:16:07 +01001692 }
Maciej S. Szmigiero04143d62015-08-05 17:25:31 +02001693
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00001694 return 0;
Timur Tabi17467f22008-01-11 18:15:26 +01001695}
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00001696
Zidan Wang05cf2372015-09-18 11:09:12 +08001697#ifdef CONFIG_PM_SLEEP
1698static int fsl_ssi_suspend(struct device *dev)
1699{
Nicolin Chenf3176832017-12-17 18:52:00 -08001700 struct fsl_ssi *ssi = dev_get_drvdata(dev);
1701 struct regmap *regs = ssi->regs;
Zidan Wang05cf2372015-09-18 11:09:12 +08001702
Nicolin Chena818aa52017-12-17 18:52:03 -08001703 regmap_read(regs, REG_SSI_SFCSR, &ssi->regcache_sfcsr);
1704 regmap_read(regs, REG_SSI_SACNT, &ssi->regcache_sacnt);
Zidan Wang05cf2372015-09-18 11:09:12 +08001705
1706 regcache_cache_only(regs, true);
1707 regcache_mark_dirty(regs);
1708
1709 return 0;
1710}
1711
1712static int fsl_ssi_resume(struct device *dev)
1713{
Nicolin Chenf3176832017-12-17 18:52:00 -08001714 struct fsl_ssi *ssi = dev_get_drvdata(dev);
1715 struct regmap *regs = ssi->regs;
Zidan Wang05cf2372015-09-18 11:09:12 +08001716
1717 regcache_cache_only(regs, false);
1718
Nicolin Chena818aa52017-12-17 18:52:03 -08001719 regmap_update_bits(regs, REG_SSI_SFCSR,
Nicolin Chenaf4f7f32017-12-17 18:52:04 -08001720 SSI_SFCSR_RFWM1_MASK | SSI_SFCSR_TFWM1_MASK |
1721 SSI_SFCSR_RFWM0_MASK | SSI_SFCSR_TFWM0_MASK,
1722 ssi->regcache_sfcsr);
Nicolin Chena818aa52017-12-17 18:52:03 -08001723 regmap_write(regs, REG_SSI_SACNT, ssi->regcache_sacnt);
Zidan Wang05cf2372015-09-18 11:09:12 +08001724
1725 return regcache_sync(regs);
1726}
1727#endif /* CONFIG_PM_SLEEP */
1728
1729static const struct dev_pm_ops fsl_ssi_pm = {
1730 SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume)
1731};
1732
Grant Likelyf07eb222011-02-22 21:05:04 -07001733static struct platform_driver fsl_ssi_driver = {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00001734 .driver = {
1735 .name = "fsl-ssi-dai",
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00001736 .of_match_table = fsl_ssi_ids,
Zidan Wang05cf2372015-09-18 11:09:12 +08001737 .pm = &fsl_ssi_pm,
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00001738 },
1739 .probe = fsl_ssi_probe,
1740 .remove = fsl_ssi_remove,
1741};
Timur Tabi17467f22008-01-11 18:15:26 +01001742
Axel Linba0a7e02011-11-25 10:10:55 +08001743module_platform_driver(fsl_ssi_driver);
Timur Tabia454dad2009-03-05 17:23:37 -06001744
Fabio Estevamf3142802013-07-20 16:16:01 -03001745MODULE_ALIAS("platform:fsl-ssi-dai");
Timur Tabi17467f22008-01-11 18:15:26 +01001746MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
1747MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00001748MODULE_LICENSE("GPL v2");