18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// imx-ssi.c  --  ALSA Soc Audio Layer
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
68c2ecf20Sopenharmony_ci//
78c2ecf20Sopenharmony_ci// This code is based on code copyrighted by Freescale,
88c2ecf20Sopenharmony_ci// Liam Girdwood, Javier Martin and probably others.
98c2ecf20Sopenharmony_ci//
108c2ecf20Sopenharmony_ci// The i.MX SSI core has some nasty limitations in AC97 mode. While most
118c2ecf20Sopenharmony_ci// sane processor vendors have a FIFO per AC97 slot, the i.MX has only
128c2ecf20Sopenharmony_ci// one FIFO which combines all valid receive slots. We cannot even select
138c2ecf20Sopenharmony_ci// which slots we want to receive. The WM9712 with which this driver
148c2ecf20Sopenharmony_ci// was developed with always sends GPIO status data in slot 12 which
158c2ecf20Sopenharmony_ci// we receive in our (PCM-) data stream. The only chance we have is to
168c2ecf20Sopenharmony_ci// manually skip this data in the FIQ handler. With sampling rates different
178c2ecf20Sopenharmony_ci// from 48000Hz not every frame has valid receive data, so the ratio
188c2ecf20Sopenharmony_ci// between pcm data and GPIO status data changes. Our FIQ handler is not
198c2ecf20Sopenharmony_ci// able to handle this, hence this driver only works with 48000Hz sampling
208c2ecf20Sopenharmony_ci// rate.
218c2ecf20Sopenharmony_ci// Reading and writing AC97 registers is another challenge. The core
228c2ecf20Sopenharmony_ci// provides us status bits when the read register is updated with *another*
238c2ecf20Sopenharmony_ci// value. When we read the same register two times (and the register still
248c2ecf20Sopenharmony_ci// contains the same value) these status bits are not set. We work
258c2ecf20Sopenharmony_ci// around this by not polling these bits but only wait a fixed delay.
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/clk.h>
288c2ecf20Sopenharmony_ci#include <linux/delay.h>
298c2ecf20Sopenharmony_ci#include <linux/device.h>
308c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
318c2ecf20Sopenharmony_ci#include <linux/init.h>
328c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
338c2ecf20Sopenharmony_ci#include <linux/module.h>
348c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
358c2ecf20Sopenharmony_ci#include <linux/slab.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include <sound/core.h>
388c2ecf20Sopenharmony_ci#include <sound/initval.h>
398c2ecf20Sopenharmony_ci#include <sound/pcm.h>
408c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
418c2ecf20Sopenharmony_ci#include <sound/soc.h>
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#include <linux/platform_data/asoc-imx-ssi.h>
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#include "imx-ssi.h"
468c2ecf20Sopenharmony_ci#include "fsl_utils.h"
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define SSI_SACNT_DEFAULT (SSI_SACNT_AC97EN | SSI_SACNT_FV)
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/*
518c2ecf20Sopenharmony_ci * SSI Network Mode or TDM slots configuration.
528c2ecf20Sopenharmony_ci * Should only be called when port is inactive (i.e. SSIEN = 0).
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_cistatic int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
558c2ecf20Sopenharmony_ci	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
588c2ecf20Sopenharmony_ci	u32 sccr;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	sccr = readl(ssi->base + SSI_STCCR);
618c2ecf20Sopenharmony_ci	sccr &= ~SSI_STCCR_DC_MASK;
628c2ecf20Sopenharmony_ci	sccr |= SSI_STCCR_DC(slots - 1);
638c2ecf20Sopenharmony_ci	writel(sccr, ssi->base + SSI_STCCR);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	sccr = readl(ssi->base + SSI_SRCCR);
668c2ecf20Sopenharmony_ci	sccr &= ~SSI_STCCR_DC_MASK;
678c2ecf20Sopenharmony_ci	sccr |= SSI_STCCR_DC(slots - 1);
688c2ecf20Sopenharmony_ci	writel(sccr, ssi->base + SSI_SRCCR);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	writel(~tx_mask, ssi->base + SSI_STMSK);
718c2ecf20Sopenharmony_ci	writel(~rx_mask, ssi->base + SSI_SRMSK);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return 0;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/*
778c2ecf20Sopenharmony_ci * SSI DAI format configuration.
788c2ecf20Sopenharmony_ci * Should only be called when port is inactive (i.e. SSIEN = 0).
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_cistatic int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
838c2ecf20Sopenharmony_ci	u32 strcr = 0, scr;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	scr = readl(ssi->base + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/* DAI mode */
888c2ecf20Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
898c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_I2S:
908c2ecf20Sopenharmony_ci		/* data on rising edge of bclk, frame low 1clk before data */
918c2ecf20Sopenharmony_ci		strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI |
928c2ecf20Sopenharmony_ci			SSI_STCR_TEFS;
938c2ecf20Sopenharmony_ci		scr |= SSI_SCR_NET;
948c2ecf20Sopenharmony_ci		if (ssi->flags & IMX_SSI_USE_I2S_SLAVE) {
958c2ecf20Sopenharmony_ci			scr &= ~SSI_I2S_MODE_MASK;
968c2ecf20Sopenharmony_ci			scr |= SSI_SCR_I2S_MODE_SLAVE;
978c2ecf20Sopenharmony_ci		}
988c2ecf20Sopenharmony_ci		break;
998c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_LEFT_J:
1008c2ecf20Sopenharmony_ci		/* data on rising edge of bclk, frame high with data */
1018c2ecf20Sopenharmony_ci		strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP;
1028c2ecf20Sopenharmony_ci		break;
1038c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_DSP_B:
1048c2ecf20Sopenharmony_ci		/* data on rising edge of bclk, frame high with data */
1058c2ecf20Sopenharmony_ci		strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL;
1068c2ecf20Sopenharmony_ci		break;
1078c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_DSP_A:
1088c2ecf20Sopenharmony_ci		/* data on rising edge of bclk, frame high 1clk before data */
1098c2ecf20Sopenharmony_ci		strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL |
1108c2ecf20Sopenharmony_ci			SSI_STCR_TEFS;
1118c2ecf20Sopenharmony_ci		break;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* DAI clock inversion */
1158c2ecf20Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
1168c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_IB_IF:
1178c2ecf20Sopenharmony_ci		strcr ^= SSI_STCR_TSCKP | SSI_STCR_TFSI;
1188c2ecf20Sopenharmony_ci		break;
1198c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_IB_NF:
1208c2ecf20Sopenharmony_ci		strcr ^= SSI_STCR_TSCKP;
1218c2ecf20Sopenharmony_ci		break;
1228c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_NB_IF:
1238c2ecf20Sopenharmony_ci		strcr ^= SSI_STCR_TFSI;
1248c2ecf20Sopenharmony_ci		break;
1258c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_NB_NF:
1268c2ecf20Sopenharmony_ci		break;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/* DAI clock master masks */
1308c2ecf20Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
1318c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_CBM_CFM:
1328c2ecf20Sopenharmony_ci		break;
1338c2ecf20Sopenharmony_ci	default:
1348c2ecf20Sopenharmony_ci		/* Master mode not implemented, needs handling of clocks. */
1358c2ecf20Sopenharmony_ci		return -EINVAL;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	strcr |= SSI_STCR_TFEN0;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (ssi->flags & IMX_SSI_NET)
1418c2ecf20Sopenharmony_ci		scr |= SSI_SCR_NET;
1428c2ecf20Sopenharmony_ci	if (ssi->flags & IMX_SSI_SYN)
1438c2ecf20Sopenharmony_ci		scr |= SSI_SCR_SYN;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	writel(strcr, ssi->base + SSI_STCR);
1468c2ecf20Sopenharmony_ci	writel(strcr, ssi->base + SSI_SRCR);
1478c2ecf20Sopenharmony_ci	writel(scr, ssi->base + SSI_SCR);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return 0;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/*
1538c2ecf20Sopenharmony_ci * SSI system clock configuration.
1548c2ecf20Sopenharmony_ci * Should only be called when port is inactive (i.e. SSIEN = 0).
1558c2ecf20Sopenharmony_ci */
1568c2ecf20Sopenharmony_cistatic int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
1578c2ecf20Sopenharmony_ci				  int clk_id, unsigned int freq, int dir)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
1608c2ecf20Sopenharmony_ci	u32 scr;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	scr = readl(ssi->base + SSI_SCR);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	switch (clk_id) {
1658c2ecf20Sopenharmony_ci	case IMX_SSP_SYS_CLK:
1668c2ecf20Sopenharmony_ci		if (dir == SND_SOC_CLOCK_OUT)
1678c2ecf20Sopenharmony_ci			scr |= SSI_SCR_SYS_CLK_EN;
1688c2ecf20Sopenharmony_ci		else
1698c2ecf20Sopenharmony_ci			scr &= ~SSI_SCR_SYS_CLK_EN;
1708c2ecf20Sopenharmony_ci		break;
1718c2ecf20Sopenharmony_ci	default:
1728c2ecf20Sopenharmony_ci		return -EINVAL;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	writel(scr, ssi->base + SSI_SCR);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	return 0;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci/*
1818c2ecf20Sopenharmony_ci * SSI Clock dividers
1828c2ecf20Sopenharmony_ci * Should only be called when port is inactive (i.e. SSIEN = 0).
1838c2ecf20Sopenharmony_ci */
1848c2ecf20Sopenharmony_cistatic int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
1858c2ecf20Sopenharmony_ci				  int div_id, int div)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
1888c2ecf20Sopenharmony_ci	u32 stccr, srccr;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	stccr = readl(ssi->base + SSI_STCCR);
1918c2ecf20Sopenharmony_ci	srccr = readl(ssi->base + SSI_SRCCR);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	switch (div_id) {
1948c2ecf20Sopenharmony_ci	case IMX_SSI_TX_DIV_2:
1958c2ecf20Sopenharmony_ci		stccr &= ~SSI_STCCR_DIV2;
1968c2ecf20Sopenharmony_ci		stccr |= div;
1978c2ecf20Sopenharmony_ci		break;
1988c2ecf20Sopenharmony_ci	case IMX_SSI_TX_DIV_PSR:
1998c2ecf20Sopenharmony_ci		stccr &= ~SSI_STCCR_PSR;
2008c2ecf20Sopenharmony_ci		stccr |= div;
2018c2ecf20Sopenharmony_ci		break;
2028c2ecf20Sopenharmony_ci	case IMX_SSI_TX_DIV_PM:
2038c2ecf20Sopenharmony_ci		stccr &= ~0xff;
2048c2ecf20Sopenharmony_ci		stccr |= SSI_STCCR_PM(div);
2058c2ecf20Sopenharmony_ci		break;
2068c2ecf20Sopenharmony_ci	case IMX_SSI_RX_DIV_2:
2078c2ecf20Sopenharmony_ci		stccr &= ~SSI_STCCR_DIV2;
2088c2ecf20Sopenharmony_ci		stccr |= div;
2098c2ecf20Sopenharmony_ci		break;
2108c2ecf20Sopenharmony_ci	case IMX_SSI_RX_DIV_PSR:
2118c2ecf20Sopenharmony_ci		stccr &= ~SSI_STCCR_PSR;
2128c2ecf20Sopenharmony_ci		stccr |= div;
2138c2ecf20Sopenharmony_ci		break;
2148c2ecf20Sopenharmony_ci	case IMX_SSI_RX_DIV_PM:
2158c2ecf20Sopenharmony_ci		stccr &= ~0xff;
2168c2ecf20Sopenharmony_ci		stccr |= SSI_STCCR_PM(div);
2178c2ecf20Sopenharmony_ci		break;
2188c2ecf20Sopenharmony_ci	default:
2198c2ecf20Sopenharmony_ci		return -EINVAL;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	writel(stccr, ssi->base + SSI_STCCR);
2238c2ecf20Sopenharmony_ci	writel(srccr, ssi->base + SSI_SRCCR);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	return 0;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/*
2298c2ecf20Sopenharmony_ci * Should only be called when port is inactive (i.e. SSIEN = 0),
2308c2ecf20Sopenharmony_ci * although can be called multiple times by upper layers.
2318c2ecf20Sopenharmony_ci */
2328c2ecf20Sopenharmony_cistatic int imx_ssi_hw_params(struct snd_pcm_substream *substream,
2338c2ecf20Sopenharmony_ci			     struct snd_pcm_hw_params *params,
2348c2ecf20Sopenharmony_ci			     struct snd_soc_dai *cpu_dai)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
2378c2ecf20Sopenharmony_ci	u32 reg, sccr;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* Tx/Rx config */
2408c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
2418c2ecf20Sopenharmony_ci		reg = SSI_STCCR;
2428c2ecf20Sopenharmony_ci	else
2438c2ecf20Sopenharmony_ci		reg = SSI_SRCCR;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (ssi->flags & IMX_SSI_SYN)
2468c2ecf20Sopenharmony_ci		reg = SSI_STCCR;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* DAI data (word) size */
2518c2ecf20Sopenharmony_ci	switch (params_format(params)) {
2528c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
2538c2ecf20Sopenharmony_ci		sccr |= SSI_SRCCR_WL(16);
2548c2ecf20Sopenharmony_ci		break;
2558c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S20_3LE:
2568c2ecf20Sopenharmony_ci		sccr |= SSI_SRCCR_WL(20);
2578c2ecf20Sopenharmony_ci		break;
2588c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_LE:
2598c2ecf20Sopenharmony_ci		sccr |= SSI_SRCCR_WL(24);
2608c2ecf20Sopenharmony_ci		break;
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	writel(sccr, ssi->base + reg);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return 0;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
2698c2ecf20Sopenharmony_ci		struct snd_soc_dai *dai)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	struct imx_ssi *ssi = snd_soc_dai_get_drvdata(dai);
2728c2ecf20Sopenharmony_ci	unsigned int sier_bits, sier;
2738c2ecf20Sopenharmony_ci	unsigned int scr;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	scr = readl(ssi->base + SSI_SCR);
2768c2ecf20Sopenharmony_ci	sier = readl(ssi->base + SSI_SIER);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
2798c2ecf20Sopenharmony_ci		if (ssi->flags & IMX_SSI_DMA)
2808c2ecf20Sopenharmony_ci			sier_bits = SSI_SIER_TDMAE;
2818c2ecf20Sopenharmony_ci		else
2828c2ecf20Sopenharmony_ci			sier_bits = SSI_SIER_TIE | SSI_SIER_TFE0_EN;
2838c2ecf20Sopenharmony_ci	} else {
2848c2ecf20Sopenharmony_ci		if (ssi->flags & IMX_SSI_DMA)
2858c2ecf20Sopenharmony_ci			sier_bits = SSI_SIER_RDMAE;
2868c2ecf20Sopenharmony_ci		else
2878c2ecf20Sopenharmony_ci			sier_bits = SSI_SIER_RIE | SSI_SIER_RFF0_EN;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	switch (cmd) {
2918c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
2928c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
2938c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2948c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
2958c2ecf20Sopenharmony_ci			scr |= SSI_SCR_TE;
2968c2ecf20Sopenharmony_ci		else
2978c2ecf20Sopenharmony_ci			scr |= SSI_SCR_RE;
2988c2ecf20Sopenharmony_ci		sier |= sier_bits;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci		scr |= SSI_SCR_SSIEN;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci		break;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
3058c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
3068c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3078c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
3088c2ecf20Sopenharmony_ci			scr &= ~SSI_SCR_TE;
3098c2ecf20Sopenharmony_ci		else
3108c2ecf20Sopenharmony_ci			scr &= ~SSI_SCR_RE;
3118c2ecf20Sopenharmony_ci		sier &= ~sier_bits;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci		if (!(scr & (SSI_SCR_TE | SSI_SCR_RE)))
3148c2ecf20Sopenharmony_ci			scr &= ~SSI_SCR_SSIEN;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci		break;
3178c2ecf20Sopenharmony_ci	default:
3188c2ecf20Sopenharmony_ci		return -EINVAL;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (!(ssi->flags & IMX_SSI_USE_AC97))
3228c2ecf20Sopenharmony_ci		/* rx/tx are always enabled to access ac97 registers */
3238c2ecf20Sopenharmony_ci		writel(scr, ssi->base + SSI_SCR);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	writel(sier, ssi->base + SSI_SIER);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	return 0;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = {
3318c2ecf20Sopenharmony_ci	.hw_params	= imx_ssi_hw_params,
3328c2ecf20Sopenharmony_ci	.set_fmt	= imx_ssi_set_dai_fmt,
3338c2ecf20Sopenharmony_ci	.set_clkdiv	= imx_ssi_set_dai_clkdiv,
3348c2ecf20Sopenharmony_ci	.set_sysclk	= imx_ssi_set_dai_sysclk,
3358c2ecf20Sopenharmony_ci	.set_tdm_slot	= imx_ssi_set_dai_tdm_slot,
3368c2ecf20Sopenharmony_ci	.trigger	= imx_ssi_trigger,
3378c2ecf20Sopenharmony_ci};
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic int imx_ssi_dai_probe(struct snd_soc_dai *dai)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct imx_ssi *ssi = dev_get_drvdata(dai->dev);
3428c2ecf20Sopenharmony_ci	uint32_t val;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	snd_soc_dai_set_drvdata(dai, ssi);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	val = SSI_SFCSR_TFWM0(ssi->dma_params_tx.maxburst) |
3478c2ecf20Sopenharmony_ci		SSI_SFCSR_RFWM0(ssi->dma_params_rx.maxburst);
3488c2ecf20Sopenharmony_ci	writel(val, ssi->base + SSI_SFCSR);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	/* Tx/Rx config */
3518c2ecf20Sopenharmony_ci	dai->playback_dma_data = &ssi->dma_params_tx;
3528c2ecf20Sopenharmony_ci	dai->capture_dma_data = &ssi->dma_params_rx;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	return 0;
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver imx_ssi_dai = {
3588c2ecf20Sopenharmony_ci	.probe = imx_ssi_dai_probe,
3598c2ecf20Sopenharmony_ci	.playback = {
3608c2ecf20Sopenharmony_ci		.channels_min = 1,
3618c2ecf20Sopenharmony_ci		.channels_max = 2,
3628c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_8000_96000,
3638c2ecf20Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S16_LE,
3648c2ecf20Sopenharmony_ci	},
3658c2ecf20Sopenharmony_ci	.capture = {
3668c2ecf20Sopenharmony_ci		.channels_min = 1,
3678c2ecf20Sopenharmony_ci		.channels_max = 2,
3688c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_8000_96000,
3698c2ecf20Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S16_LE,
3708c2ecf20Sopenharmony_ci	},
3718c2ecf20Sopenharmony_ci	.ops = &imx_ssi_pcm_dai_ops,
3728c2ecf20Sopenharmony_ci};
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver imx_ac97_dai = {
3758c2ecf20Sopenharmony_ci	.probe = imx_ssi_dai_probe,
3768c2ecf20Sopenharmony_ci	.playback = {
3778c2ecf20Sopenharmony_ci		.stream_name = "AC97 Playback",
3788c2ecf20Sopenharmony_ci		.channels_min = 2,
3798c2ecf20Sopenharmony_ci		.channels_max = 2,
3808c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_8000_48000,
3818c2ecf20Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S16_LE,
3828c2ecf20Sopenharmony_ci	},
3838c2ecf20Sopenharmony_ci	.capture = {
3848c2ecf20Sopenharmony_ci		.stream_name = "AC97 Capture",
3858c2ecf20Sopenharmony_ci		.channels_min = 2,
3868c2ecf20Sopenharmony_ci		.channels_max = 2,
3878c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_48000,
3888c2ecf20Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S16_LE,
3898c2ecf20Sopenharmony_ci	},
3908c2ecf20Sopenharmony_ci	.ops = &imx_ssi_pcm_dai_ops,
3918c2ecf20Sopenharmony_ci};
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver imx_component = {
3948c2ecf20Sopenharmony_ci	.name		= DRV_NAME,
3958c2ecf20Sopenharmony_ci};
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic void setup_channel_to_ac97(struct imx_ssi *imx_ssi)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	void __iomem *base = imx_ssi->base;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	writel(0x0, base + SSI_SCR);
4028c2ecf20Sopenharmony_ci	writel(0x0, base + SSI_STCR);
4038c2ecf20Sopenharmony_ci	writel(0x0, base + SSI_SRCR);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	writel(SSI_SCR_SYN | SSI_SCR_NET, base + SSI_SCR);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	writel(SSI_SFCSR_RFWM0(8) |
4088c2ecf20Sopenharmony_ci		SSI_SFCSR_TFWM0(8) |
4098c2ecf20Sopenharmony_ci		SSI_SFCSR_RFWM1(8) |
4108c2ecf20Sopenharmony_ci		SSI_SFCSR_TFWM1(8), base + SSI_SFCSR);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_STCCR);
4138c2ecf20Sopenharmony_ci	writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_SRCCR);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN, base + SSI_SCR);
4168c2ecf20Sopenharmony_ci	writel(SSI_SOR_WAIT(3), base + SSI_SOR);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN |
4198c2ecf20Sopenharmony_ci			SSI_SCR_TE | SSI_SCR_RE,
4208c2ecf20Sopenharmony_ci			base + SSI_SCR);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	writel(SSI_SACNT_DEFAULT, base + SSI_SACNT);
4238c2ecf20Sopenharmony_ci	writel(0xff, base + SSI_SACCDIS);
4248c2ecf20Sopenharmony_ci	writel(0x300, base + SSI_SACCEN);
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic struct imx_ssi *ac97_ssi;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
4308c2ecf20Sopenharmony_ci		unsigned short val)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct imx_ssi *imx_ssi = ac97_ssi;
4338c2ecf20Sopenharmony_ci	void __iomem *base = imx_ssi->base;
4348c2ecf20Sopenharmony_ci	unsigned int lreg;
4358c2ecf20Sopenharmony_ci	unsigned int lval;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (reg > 0x7f)
4388c2ecf20Sopenharmony_ci		return;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	lreg = reg <<  12;
4438c2ecf20Sopenharmony_ci	writel(lreg, base + SSI_SACADD);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	lval = val << 4;
4468c2ecf20Sopenharmony_ci	writel(lval , base + SSI_SACDAT);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	writel(SSI_SACNT_DEFAULT | SSI_SACNT_WR, base + SSI_SACNT);
4498c2ecf20Sopenharmony_ci	udelay(100);
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97,
4538c2ecf20Sopenharmony_ci		unsigned short reg)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	struct imx_ssi *imx_ssi = ac97_ssi;
4568c2ecf20Sopenharmony_ci	void __iomem *base = imx_ssi->base;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	unsigned short val = -1;
4598c2ecf20Sopenharmony_ci	unsigned int lreg;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	lreg = (reg & 0x7f) <<  12 ;
4628c2ecf20Sopenharmony_ci	writel(lreg, base + SSI_SACADD);
4638c2ecf20Sopenharmony_ci	writel(SSI_SACNT_DEFAULT | SSI_SACNT_RD, base + SSI_SACNT);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	udelay(100);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	val = (readl(base + SSI_SACDAT) >> 4) & 0xffff;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	return val;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic void imx_ssi_ac97_reset(struct snd_ac97 *ac97)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	struct imx_ssi *imx_ssi = ac97_ssi;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	if (imx_ssi->ac97_reset)
4798c2ecf20Sopenharmony_ci		imx_ssi->ac97_reset(ac97);
4808c2ecf20Sopenharmony_ci	/* First read sometimes fails, do a dummy read */
4818c2ecf20Sopenharmony_ci	imx_ssi_ac97_read(ac97, 0);
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	struct imx_ssi *imx_ssi = ac97_ssi;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (imx_ssi->ac97_warm_reset)
4898c2ecf20Sopenharmony_ci		imx_ssi->ac97_warm_reset(ac97);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* First read sometimes fails, do a dummy read */
4928c2ecf20Sopenharmony_ci	imx_ssi_ac97_read(ac97, 0);
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic struct snd_ac97_bus_ops imx_ssi_ac97_ops = {
4968c2ecf20Sopenharmony_ci	.read		= imx_ssi_ac97_read,
4978c2ecf20Sopenharmony_ci	.write		= imx_ssi_ac97_write,
4988c2ecf20Sopenharmony_ci	.reset		= imx_ssi_ac97_reset,
4998c2ecf20Sopenharmony_ci	.warm_reset	= imx_ssi_ac97_warm_reset
5008c2ecf20Sopenharmony_ci};
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic int imx_ssi_probe(struct platform_device *pdev)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct resource *res;
5058c2ecf20Sopenharmony_ci	struct imx_ssi *ssi;
5068c2ecf20Sopenharmony_ci	struct imx_ssi_platform_data *pdata = pdev->dev.platform_data;
5078c2ecf20Sopenharmony_ci	int ret = 0;
5088c2ecf20Sopenharmony_ci	struct snd_soc_dai_driver *dai;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	ssi = devm_kzalloc(&pdev->dev, sizeof(*ssi), GFP_KERNEL);
5118c2ecf20Sopenharmony_ci	if (!ssi)
5128c2ecf20Sopenharmony_ci		return -ENOMEM;
5138c2ecf20Sopenharmony_ci	dev_set_drvdata(&pdev->dev, ssi);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	if (pdata) {
5168c2ecf20Sopenharmony_ci		ssi->ac97_reset = pdata->ac97_reset;
5178c2ecf20Sopenharmony_ci		ssi->ac97_warm_reset = pdata->ac97_warm_reset;
5188c2ecf20Sopenharmony_ci		ssi->flags = pdata->flags;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	ssi->irq = platform_get_irq(pdev, 0);
5228c2ecf20Sopenharmony_ci	if (ssi->irq < 0)
5238c2ecf20Sopenharmony_ci		return ssi->irq;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	ssi->clk = devm_clk_get(&pdev->dev, NULL);
5268c2ecf20Sopenharmony_ci	if (IS_ERR(ssi->clk)) {
5278c2ecf20Sopenharmony_ci		ret = PTR_ERR(ssi->clk);
5288c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Cannot get the clock: %d\n",
5298c2ecf20Sopenharmony_ci			ret);
5308c2ecf20Sopenharmony_ci		goto failed_clk;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(ssi->clk);
5338c2ecf20Sopenharmony_ci	if (ret)
5348c2ecf20Sopenharmony_ci		goto failed_clk;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
5378c2ecf20Sopenharmony_ci	ssi->base = devm_ioremap_resource(&pdev->dev, res);
5388c2ecf20Sopenharmony_ci	if (IS_ERR(ssi->base)) {
5398c2ecf20Sopenharmony_ci		ret = PTR_ERR(ssi->base);
5408c2ecf20Sopenharmony_ci		goto failed_register;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	if (ssi->flags & IMX_SSI_USE_AC97) {
5448c2ecf20Sopenharmony_ci		if (ac97_ssi) {
5458c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "AC'97 SSI already registered\n");
5468c2ecf20Sopenharmony_ci			ret = -EBUSY;
5478c2ecf20Sopenharmony_ci			goto failed_register;
5488c2ecf20Sopenharmony_ci		}
5498c2ecf20Sopenharmony_ci		ac97_ssi = ssi;
5508c2ecf20Sopenharmony_ci		setup_channel_to_ac97(ssi);
5518c2ecf20Sopenharmony_ci		dai = &imx_ac97_dai;
5528c2ecf20Sopenharmony_ci	} else
5538c2ecf20Sopenharmony_ci		dai = &imx_ssi_dai;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	writel(0x0, ssi->base + SSI_SIER);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	ssi->dma_params_rx.addr = res->start + SSI_SRX0;
5588c2ecf20Sopenharmony_ci	ssi->dma_params_tx.addr = res->start + SSI_STX0;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	ssi->dma_params_tx.maxburst = 6;
5618c2ecf20Sopenharmony_ci	ssi->dma_params_rx.maxburst = 4;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	ssi->dma_params_tx.filter_data = &ssi->filter_data_tx;
5648c2ecf20Sopenharmony_ci	ssi->dma_params_rx.filter_data = &ssi->filter_data_rx;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0");
5678c2ecf20Sopenharmony_ci	if (res) {
5688c2ecf20Sopenharmony_ci		imx_pcm_dma_params_init_data(&ssi->filter_data_tx, res->start,
5698c2ecf20Sopenharmony_ci			IMX_DMATYPE_SSI);
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0");
5738c2ecf20Sopenharmony_ci	if (res) {
5748c2ecf20Sopenharmony_ci		imx_pcm_dma_params_init_data(&ssi->filter_data_rx, res->start,
5758c2ecf20Sopenharmony_ci			IMX_DMATYPE_SSI);
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, ssi);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	ret = snd_soc_set_ac97_ops(&imx_ssi_ac97_ops);
5818c2ecf20Sopenharmony_ci	if (ret != 0) {
5828c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
5838c2ecf20Sopenharmony_ci		goto failed_register;
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	ret = snd_soc_register_component(&pdev->dev, &imx_component,
5878c2ecf20Sopenharmony_ci					 dai, 1);
5888c2ecf20Sopenharmony_ci	if (ret) {
5898c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "register DAI failed\n");
5908c2ecf20Sopenharmony_ci		goto failed_register;
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	ssi->fiq_params.irq = ssi->irq;
5948c2ecf20Sopenharmony_ci	ssi->fiq_params.base = ssi->base;
5958c2ecf20Sopenharmony_ci	ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx;
5968c2ecf20Sopenharmony_ci	ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	ssi->fiq_init = imx_pcm_fiq_init(pdev, &ssi->fiq_params);
5998c2ecf20Sopenharmony_ci	ssi->dma_init = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	if (ssi->fiq_init && ssi->dma_init) {
6028c2ecf20Sopenharmony_ci		ret = ssi->fiq_init;
6038c2ecf20Sopenharmony_ci		goto failed_pcm;
6048c2ecf20Sopenharmony_ci	}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	return 0;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cifailed_pcm:
6098c2ecf20Sopenharmony_ci	snd_soc_unregister_component(&pdev->dev);
6108c2ecf20Sopenharmony_cifailed_register:
6118c2ecf20Sopenharmony_ci	clk_disable_unprepare(ssi->clk);
6128c2ecf20Sopenharmony_cifailed_clk:
6138c2ecf20Sopenharmony_ci	snd_soc_set_ac97_ops(NULL);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	return ret;
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_cistatic int imx_ssi_remove(struct platform_device *pdev)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	struct imx_ssi *ssi = platform_get_drvdata(pdev);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	if (!ssi->fiq_init)
6238c2ecf20Sopenharmony_ci		imx_pcm_fiq_exit(pdev);
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	snd_soc_unregister_component(&pdev->dev);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	if (ssi->flags & IMX_SSI_USE_AC97)
6288c2ecf20Sopenharmony_ci		ac97_ssi = NULL;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	clk_disable_unprepare(ssi->clk);
6318c2ecf20Sopenharmony_ci	snd_soc_set_ac97_ops(NULL);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	return 0;
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic struct platform_driver imx_ssi_driver = {
6378c2ecf20Sopenharmony_ci	.probe = imx_ssi_probe,
6388c2ecf20Sopenharmony_ci	.remove = imx_ssi_remove,
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	.driver = {
6418c2ecf20Sopenharmony_ci		.name = "imx-ssi",
6428c2ecf20Sopenharmony_ci	},
6438c2ecf20Sopenharmony_ci};
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_cimodule_platform_driver(imx_ssi_driver);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci/* Module information */
6488c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sascha Hauer, <s.hauer@pengutronix.de>");
6498c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("i.MX I2S/ac97 SoC Interface");
6508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
6518c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:imx-ssi");
652