18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ASoC driver for Cirrus Logic EP93xx AC97 controller.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2010 Mika Westerberg
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Based on s3c-ac97 ASoC driver by Jaswinder Singh.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/delay.h>
118c2ecf20Sopenharmony_ci#include <linux/err.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <sound/core.h>
198c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h>
208c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h>
218c2ecf20Sopenharmony_ci#include <sound/soc.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/platform_data/dma-ep93xx.h>
248c2ecf20Sopenharmony_ci#include <linux/soc/cirrus/ep93xx.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "ep93xx-pcm.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * Per channel (1-4) registers.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_ci#define AC97CH(n)		(((n) - 1) * 0x20)
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define AC97DR(n)		(AC97CH(n) + 0x0000)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define AC97RXCR(n)		(AC97CH(n) + 0x0004)
368c2ecf20Sopenharmony_ci#define AC97RXCR_REN		BIT(0)
378c2ecf20Sopenharmony_ci#define AC97RXCR_RX3		BIT(3)
388c2ecf20Sopenharmony_ci#define AC97RXCR_RX4		BIT(4)
398c2ecf20Sopenharmony_ci#define AC97RXCR_CM		BIT(15)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define AC97TXCR(n)		(AC97CH(n) + 0x0008)
428c2ecf20Sopenharmony_ci#define AC97TXCR_TEN		BIT(0)
438c2ecf20Sopenharmony_ci#define AC97TXCR_TX3		BIT(3)
448c2ecf20Sopenharmony_ci#define AC97TXCR_TX4		BIT(4)
458c2ecf20Sopenharmony_ci#define AC97TXCR_CM		BIT(15)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define AC97SR(n)		(AC97CH(n) + 0x000c)
488c2ecf20Sopenharmony_ci#define AC97SR_TXFE		BIT(1)
498c2ecf20Sopenharmony_ci#define AC97SR_TXUE		BIT(6)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define AC97RISR(n)		(AC97CH(n) + 0x0010)
528c2ecf20Sopenharmony_ci#define AC97ISR(n)		(AC97CH(n) + 0x0014)
538c2ecf20Sopenharmony_ci#define AC97IE(n)		(AC97CH(n) + 0x0018)
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/*
568c2ecf20Sopenharmony_ci * Global AC97 controller registers.
578c2ecf20Sopenharmony_ci */
588c2ecf20Sopenharmony_ci#define AC97S1DATA		0x0080
598c2ecf20Sopenharmony_ci#define AC97S2DATA		0x0084
608c2ecf20Sopenharmony_ci#define AC97S12DATA		0x0088
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define AC97RGIS		0x008c
638c2ecf20Sopenharmony_ci#define AC97GIS			0x0090
648c2ecf20Sopenharmony_ci#define AC97IM			0x0094
658c2ecf20Sopenharmony_ci/*
668c2ecf20Sopenharmony_ci * Common bits for RGIS, GIS and IM registers.
678c2ecf20Sopenharmony_ci */
688c2ecf20Sopenharmony_ci#define AC97_SLOT2RXVALID	BIT(1)
698c2ecf20Sopenharmony_ci#define AC97_CODECREADY		BIT(5)
708c2ecf20Sopenharmony_ci#define AC97_SLOT2TXCOMPLETE	BIT(6)
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#define AC97EOI			0x0098
738c2ecf20Sopenharmony_ci#define AC97EOI_WINT		BIT(0)
748c2ecf20Sopenharmony_ci#define AC97EOI_CODECREADY	BIT(1)
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#define AC97GCR			0x009c
778c2ecf20Sopenharmony_ci#define AC97GCR_AC97IFE		BIT(0)
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#define AC97RESET		0x00a0
808c2ecf20Sopenharmony_ci#define AC97RESET_TIMEDRESET	BIT(0)
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define AC97SYNC		0x00a4
838c2ecf20Sopenharmony_ci#define AC97SYNC_TIMEDSYNC	BIT(0)
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci#define AC97_TIMEOUT		msecs_to_jiffies(5)
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/**
888c2ecf20Sopenharmony_ci * struct ep93xx_ac97_info - EP93xx AC97 controller info structure
898c2ecf20Sopenharmony_ci * @lock: mutex serializing access to the bus (slot 1 & 2 ops)
908c2ecf20Sopenharmony_ci * @dev: pointer to the platform device dev structure
918c2ecf20Sopenharmony_ci * @regs: mapped AC97 controller registers
928c2ecf20Sopenharmony_ci * @done: bus ops wait here for an interrupt
938c2ecf20Sopenharmony_ci */
948c2ecf20Sopenharmony_cistruct ep93xx_ac97_info {
958c2ecf20Sopenharmony_ci	struct mutex		lock;
968c2ecf20Sopenharmony_ci	struct device		*dev;
978c2ecf20Sopenharmony_ci	void __iomem		*regs;
988c2ecf20Sopenharmony_ci	struct completion	done;
998c2ecf20Sopenharmony_ci	struct snd_dmaengine_dai_dma_data dma_params_rx;
1008c2ecf20Sopenharmony_ci	struct snd_dmaengine_dai_dma_data dma_params_tx;
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/* currently ALSA only supports a single AC97 device */
1048c2ecf20Sopenharmony_cistatic struct ep93xx_ac97_info *ep93xx_ac97_info;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic struct ep93xx_dma_data ep93xx_ac97_pcm_out = {
1078c2ecf20Sopenharmony_ci	.name		= "ac97-pcm-out",
1088c2ecf20Sopenharmony_ci	.port		= EP93XX_DMA_AAC1,
1098c2ecf20Sopenharmony_ci	.direction	= DMA_MEM_TO_DEV,
1108c2ecf20Sopenharmony_ci};
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic struct ep93xx_dma_data ep93xx_ac97_pcm_in = {
1138c2ecf20Sopenharmony_ci	.name		= "ac97-pcm-in",
1148c2ecf20Sopenharmony_ci	.port		= EP93XX_DMA_AAC1,
1158c2ecf20Sopenharmony_ci	.direction	= DMA_DEV_TO_MEM,
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic inline unsigned ep93xx_ac97_read_reg(struct ep93xx_ac97_info *info,
1198c2ecf20Sopenharmony_ci					    unsigned reg)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	return __raw_readl(info->regs + reg);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic inline void ep93xx_ac97_write_reg(struct ep93xx_ac97_info *info,
1258c2ecf20Sopenharmony_ci					 unsigned reg, unsigned val)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	__raw_writel(val, info->regs + reg);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic unsigned short ep93xx_ac97_read(struct snd_ac97 *ac97,
1318c2ecf20Sopenharmony_ci				       unsigned short reg)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct ep93xx_ac97_info *info = ep93xx_ac97_info;
1348c2ecf20Sopenharmony_ci	unsigned short val;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	mutex_lock(&info->lock);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	ep93xx_ac97_write_reg(info, AC97S1DATA, reg);
1398c2ecf20Sopenharmony_ci	ep93xx_ac97_write_reg(info, AC97IM, AC97_SLOT2RXVALID);
1408c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT)) {
1418c2ecf20Sopenharmony_ci		dev_warn(info->dev, "timeout reading register %x\n", reg);
1428c2ecf20Sopenharmony_ci		mutex_unlock(&info->lock);
1438c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci	val = (unsigned short)ep93xx_ac97_read_reg(info, AC97S2DATA);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	mutex_unlock(&info->lock);
1488c2ecf20Sopenharmony_ci	return val;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic void ep93xx_ac97_write(struct snd_ac97 *ac97,
1528c2ecf20Sopenharmony_ci			      unsigned short reg,
1538c2ecf20Sopenharmony_ci			      unsigned short val)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	struct ep93xx_ac97_info *info = ep93xx_ac97_info;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	mutex_lock(&info->lock);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/*
1608c2ecf20Sopenharmony_ci	 * Writes to the codec need to be done so that slot 2 is filled in
1618c2ecf20Sopenharmony_ci	 * before slot 1.
1628c2ecf20Sopenharmony_ci	 */
1638c2ecf20Sopenharmony_ci	ep93xx_ac97_write_reg(info, AC97S2DATA, val);
1648c2ecf20Sopenharmony_ci	ep93xx_ac97_write_reg(info, AC97S1DATA, reg);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	ep93xx_ac97_write_reg(info, AC97IM, AC97_SLOT2TXCOMPLETE);
1678c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
1688c2ecf20Sopenharmony_ci		dev_warn(info->dev, "timeout writing register %x\n", reg);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	mutex_unlock(&info->lock);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic void ep93xx_ac97_warm_reset(struct snd_ac97 *ac97)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct ep93xx_ac97_info *info = ep93xx_ac97_info;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	mutex_lock(&info->lock);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/*
1808c2ecf20Sopenharmony_ci	 * We are assuming that before this functions gets called, the codec
1818c2ecf20Sopenharmony_ci	 * BIT_CLK is stopped by forcing the codec into powerdown mode. We can
1828c2ecf20Sopenharmony_ci	 * control the SYNC signal directly via AC97SYNC register. Using
1838c2ecf20Sopenharmony_ci	 * TIMEDSYNC the controller will keep the SYNC high > 1us.
1848c2ecf20Sopenharmony_ci	 */
1858c2ecf20Sopenharmony_ci	ep93xx_ac97_write_reg(info, AC97SYNC, AC97SYNC_TIMEDSYNC);
1868c2ecf20Sopenharmony_ci	ep93xx_ac97_write_reg(info, AC97IM, AC97_CODECREADY);
1878c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
1888c2ecf20Sopenharmony_ci		dev_warn(info->dev, "codec warm reset timeout\n");
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	mutex_unlock(&info->lock);
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic void ep93xx_ac97_cold_reset(struct snd_ac97 *ac97)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct ep93xx_ac97_info *info = ep93xx_ac97_info;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	mutex_lock(&info->lock);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	/*
2008c2ecf20Sopenharmony_ci	 * For doing cold reset, we disable the AC97 controller interface, clear
2018c2ecf20Sopenharmony_ci	 * WINT and CODECREADY bits, and finally enable the interface again.
2028c2ecf20Sopenharmony_ci	 */
2038c2ecf20Sopenharmony_ci	ep93xx_ac97_write_reg(info, AC97GCR, 0);
2048c2ecf20Sopenharmony_ci	ep93xx_ac97_write_reg(info, AC97EOI, AC97EOI_CODECREADY | AC97EOI_WINT);
2058c2ecf20Sopenharmony_ci	ep93xx_ac97_write_reg(info, AC97GCR, AC97GCR_AC97IFE);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/*
2088c2ecf20Sopenharmony_ci	 * Now, assert the reset and wait for the codec to become ready.
2098c2ecf20Sopenharmony_ci	 */
2108c2ecf20Sopenharmony_ci	ep93xx_ac97_write_reg(info, AC97RESET, AC97RESET_TIMEDRESET);
2118c2ecf20Sopenharmony_ci	ep93xx_ac97_write_reg(info, AC97IM, AC97_CODECREADY);
2128c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
2138c2ecf20Sopenharmony_ci		dev_warn(info->dev, "codec cold reset timeout\n");
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/*
2168c2ecf20Sopenharmony_ci	 * Give the codec some time to come fully out from the reset. This way
2178c2ecf20Sopenharmony_ci	 * we ensure that the subsequent reads/writes will work.
2188c2ecf20Sopenharmony_ci	 */
2198c2ecf20Sopenharmony_ci	usleep_range(15000, 20000);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	mutex_unlock(&info->lock);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic irqreturn_t ep93xx_ac97_interrupt(int irq, void *dev_id)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct ep93xx_ac97_info *info = dev_id;
2278c2ecf20Sopenharmony_ci	unsigned status, mask;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	/*
2308c2ecf20Sopenharmony_ci	 * Just mask out the interrupt and wake up the waiting thread.
2318c2ecf20Sopenharmony_ci	 * Interrupts are cleared via reading/writing to slot 1 & 2 registers by
2328c2ecf20Sopenharmony_ci	 * the waiting thread.
2338c2ecf20Sopenharmony_ci	 */
2348c2ecf20Sopenharmony_ci	status = ep93xx_ac97_read_reg(info, AC97GIS);
2358c2ecf20Sopenharmony_ci	mask = ep93xx_ac97_read_reg(info, AC97IM);
2368c2ecf20Sopenharmony_ci	mask &= ~status;
2378c2ecf20Sopenharmony_ci	ep93xx_ac97_write_reg(info, AC97IM, mask);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	complete(&info->done);
2408c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic struct snd_ac97_bus_ops ep93xx_ac97_ops = {
2448c2ecf20Sopenharmony_ci	.read		= ep93xx_ac97_read,
2458c2ecf20Sopenharmony_ci	.write		= ep93xx_ac97_write,
2468c2ecf20Sopenharmony_ci	.reset		= ep93xx_ac97_cold_reset,
2478c2ecf20Sopenharmony_ci	.warm_reset	= ep93xx_ac97_warm_reset,
2488c2ecf20Sopenharmony_ci};
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic int ep93xx_ac97_trigger(struct snd_pcm_substream *substream,
2518c2ecf20Sopenharmony_ci			       int cmd, struct snd_soc_dai *dai)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai);
2548c2ecf20Sopenharmony_ci	unsigned v = 0;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	switch (cmd) {
2578c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
2588c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
2598c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2608c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
2618c2ecf20Sopenharmony_ci			/*
2628c2ecf20Sopenharmony_ci			 * Enable compact mode, TX slots 3 & 4, and the TX FIFO
2638c2ecf20Sopenharmony_ci			 * itself.
2648c2ecf20Sopenharmony_ci			 */
2658c2ecf20Sopenharmony_ci			v |= AC97TXCR_CM;
2668c2ecf20Sopenharmony_ci			v |= AC97TXCR_TX3 | AC97TXCR_TX4;
2678c2ecf20Sopenharmony_ci			v |= AC97TXCR_TEN;
2688c2ecf20Sopenharmony_ci			ep93xx_ac97_write_reg(info, AC97TXCR(1), v);
2698c2ecf20Sopenharmony_ci		} else {
2708c2ecf20Sopenharmony_ci			/*
2718c2ecf20Sopenharmony_ci			 * Enable compact mode, RX slots 3 & 4, and the RX FIFO
2728c2ecf20Sopenharmony_ci			 * itself.
2738c2ecf20Sopenharmony_ci			 */
2748c2ecf20Sopenharmony_ci			v |= AC97RXCR_CM;
2758c2ecf20Sopenharmony_ci			v |= AC97RXCR_RX3 | AC97RXCR_RX4;
2768c2ecf20Sopenharmony_ci			v |= AC97RXCR_REN;
2778c2ecf20Sopenharmony_ci			ep93xx_ac97_write_reg(info, AC97RXCR(1), v);
2788c2ecf20Sopenharmony_ci		}
2798c2ecf20Sopenharmony_ci		break;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
2828c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
2838c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
2848c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
2858c2ecf20Sopenharmony_ci			/*
2868c2ecf20Sopenharmony_ci			 * As per Cirrus EP93xx errata described below:
2878c2ecf20Sopenharmony_ci			 *
2888c2ecf20Sopenharmony_ci			 * https://www.cirrus.com/en/pubs/errata/ER667E2B.pdf
2898c2ecf20Sopenharmony_ci			 *
2908c2ecf20Sopenharmony_ci			 * we will wait for the TX FIFO to be empty before
2918c2ecf20Sopenharmony_ci			 * clearing the TEN bit.
2928c2ecf20Sopenharmony_ci			 */
2938c2ecf20Sopenharmony_ci			unsigned long timeout = jiffies + AC97_TIMEOUT;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci			do {
2968c2ecf20Sopenharmony_ci				v = ep93xx_ac97_read_reg(info, AC97SR(1));
2978c2ecf20Sopenharmony_ci				if (time_after(jiffies, timeout)) {
2988c2ecf20Sopenharmony_ci					dev_warn(info->dev, "TX timeout\n");
2998c2ecf20Sopenharmony_ci					break;
3008c2ecf20Sopenharmony_ci				}
3018c2ecf20Sopenharmony_ci			} while (!(v & (AC97SR_TXFE | AC97SR_TXUE)));
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci			/* disable the TX FIFO */
3048c2ecf20Sopenharmony_ci			ep93xx_ac97_write_reg(info, AC97TXCR(1), 0);
3058c2ecf20Sopenharmony_ci		} else {
3068c2ecf20Sopenharmony_ci			/* disable the RX FIFO */
3078c2ecf20Sopenharmony_ci			ep93xx_ac97_write_reg(info, AC97RXCR(1), 0);
3088c2ecf20Sopenharmony_ci		}
3098c2ecf20Sopenharmony_ci		break;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	default:
3128c2ecf20Sopenharmony_ci		dev_warn(info->dev, "unknown command %d\n", cmd);
3138c2ecf20Sopenharmony_ci		return -EINVAL;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	return 0;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic int ep93xx_ac97_dai_probe(struct snd_soc_dai *dai)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	info->dma_params_tx.filter_data = &ep93xx_ac97_pcm_out;
3248c2ecf20Sopenharmony_ci	info->dma_params_rx.filter_data = &ep93xx_ac97_pcm_in;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	dai->playback_dma_data = &info->dma_params_tx;
3278c2ecf20Sopenharmony_ci	dai->capture_dma_data = &info->dma_params_rx;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return 0;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
3338c2ecf20Sopenharmony_ci	.trigger	= ep93xx_ac97_trigger,
3348c2ecf20Sopenharmony_ci};
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver ep93xx_ac97_dai = {
3378c2ecf20Sopenharmony_ci	.name		= "ep93xx-ac97",
3388c2ecf20Sopenharmony_ci	.id		= 0,
3398c2ecf20Sopenharmony_ci	.probe		= ep93xx_ac97_dai_probe,
3408c2ecf20Sopenharmony_ci	.playback	= {
3418c2ecf20Sopenharmony_ci		.stream_name	= "AC97 Playback",
3428c2ecf20Sopenharmony_ci		.channels_min	= 2,
3438c2ecf20Sopenharmony_ci		.channels_max	= 2,
3448c2ecf20Sopenharmony_ci		.rates		= SNDRV_PCM_RATE_8000_48000,
3458c2ecf20Sopenharmony_ci		.formats	= SNDRV_PCM_FMTBIT_S16_LE,
3468c2ecf20Sopenharmony_ci	},
3478c2ecf20Sopenharmony_ci	.capture	= {
3488c2ecf20Sopenharmony_ci		.stream_name	= "AC97 Capture",
3498c2ecf20Sopenharmony_ci		.channels_min	= 2,
3508c2ecf20Sopenharmony_ci		.channels_max	= 2,
3518c2ecf20Sopenharmony_ci		.rates		= SNDRV_PCM_RATE_8000_48000,
3528c2ecf20Sopenharmony_ci		.formats	= SNDRV_PCM_FMTBIT_S16_LE,
3538c2ecf20Sopenharmony_ci	},
3548c2ecf20Sopenharmony_ci	.ops			= &ep93xx_ac97_dai_ops,
3558c2ecf20Sopenharmony_ci};
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver ep93xx_ac97_component = {
3588c2ecf20Sopenharmony_ci	.name		= "ep93xx-ac97",
3598c2ecf20Sopenharmony_ci};
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic int ep93xx_ac97_probe(struct platform_device *pdev)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct ep93xx_ac97_info *info;
3648c2ecf20Sopenharmony_ci	int irq;
3658c2ecf20Sopenharmony_ci	int ret;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
3688c2ecf20Sopenharmony_ci	if (!info)
3698c2ecf20Sopenharmony_ci		return -ENOMEM;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	info->regs = devm_platform_ioremap_resource(pdev, 0);
3728c2ecf20Sopenharmony_ci	if (IS_ERR(info->regs))
3738c2ecf20Sopenharmony_ci		return PTR_ERR(info->regs);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
3768c2ecf20Sopenharmony_ci	if (irq <= 0)
3778c2ecf20Sopenharmony_ci		return irq < 0 ? irq : -ENODEV;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq, ep93xx_ac97_interrupt,
3808c2ecf20Sopenharmony_ci			       IRQF_TRIGGER_HIGH, pdev->name, info);
3818c2ecf20Sopenharmony_ci	if (ret)
3828c2ecf20Sopenharmony_ci		goto fail;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	dev_set_drvdata(&pdev->dev, info);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	mutex_init(&info->lock);
3878c2ecf20Sopenharmony_ci	init_completion(&info->done);
3888c2ecf20Sopenharmony_ci	info->dev = &pdev->dev;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	ep93xx_ac97_info = info;
3918c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, info);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	ret = snd_soc_set_ac97_ops(&ep93xx_ac97_ops);
3948c2ecf20Sopenharmony_ci	if (ret)
3958c2ecf20Sopenharmony_ci		goto fail;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	ret = snd_soc_register_component(&pdev->dev, &ep93xx_ac97_component,
3988c2ecf20Sopenharmony_ci					 &ep93xx_ac97_dai, 1);
3998c2ecf20Sopenharmony_ci	if (ret)
4008c2ecf20Sopenharmony_ci		goto fail;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	ret = devm_ep93xx_pcm_platform_register(&pdev->dev);
4038c2ecf20Sopenharmony_ci	if (ret)
4048c2ecf20Sopenharmony_ci		goto fail_unregister;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	return 0;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cifail_unregister:
4098c2ecf20Sopenharmony_ci	snd_soc_unregister_component(&pdev->dev);
4108c2ecf20Sopenharmony_cifail:
4118c2ecf20Sopenharmony_ci	ep93xx_ac97_info = NULL;
4128c2ecf20Sopenharmony_ci	snd_soc_set_ac97_ops(NULL);
4138c2ecf20Sopenharmony_ci	return ret;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int ep93xx_ac97_remove(struct platform_device *pdev)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	struct ep93xx_ac97_info	*info = platform_get_drvdata(pdev);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	snd_soc_unregister_component(&pdev->dev);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	/* disable the AC97 controller */
4238c2ecf20Sopenharmony_ci	ep93xx_ac97_write_reg(info, AC97GCR, 0);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	ep93xx_ac97_info = NULL;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	snd_soc_set_ac97_ops(NULL);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	return 0;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic struct platform_driver ep93xx_ac97_driver = {
4338c2ecf20Sopenharmony_ci	.probe	= ep93xx_ac97_probe,
4348c2ecf20Sopenharmony_ci	.remove	= ep93xx_ac97_remove,
4358c2ecf20Sopenharmony_ci	.driver = {
4368c2ecf20Sopenharmony_ci		.name = "ep93xx-ac97",
4378c2ecf20Sopenharmony_ci	},
4388c2ecf20Sopenharmony_ci};
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cimodule_platform_driver(ep93xx_ac97_driver);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EP93xx AC97 ASoC Driver");
4438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
4448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4458c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ep93xx-ac97");
446