18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Au12x0/Au1550 PSC ALSA ASoC audio support.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (c) 2007-2009 MSC Vertriebsges.m.b.H.,
68c2ecf20Sopenharmony_ci *	Manuel Lauss <manuel.lauss@gmail.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Au1xxx-PSC AC97 glue.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/device.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/mutex.h>
178c2ecf20Sopenharmony_ci#include <linux/suspend.h>
188c2ecf20Sopenharmony_ci#include <sound/core.h>
198c2ecf20Sopenharmony_ci#include <sound/pcm.h>
208c2ecf20Sopenharmony_ci#include <sound/initval.h>
218c2ecf20Sopenharmony_ci#include <sound/soc.h>
228c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1000.h>
238c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1xxx_psc.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "psc.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* how often to retry failed codec register reads/writes */
288c2ecf20Sopenharmony_ci#define AC97_RW_RETRIES	5
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define AC97_DIR	\
318c2ecf20Sopenharmony_ci	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define AC97_RATES	\
348c2ecf20Sopenharmony_ci	SNDRV_PCM_RATE_8000_48000
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define AC97_FMTS	\
378c2ecf20Sopenharmony_ci	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define AC97PCR_START(stype)	\
408c2ecf20Sopenharmony_ci	((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TS : PSC_AC97PCR_RS)
418c2ecf20Sopenharmony_ci#define AC97PCR_STOP(stype)	\
428c2ecf20Sopenharmony_ci	((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TP : PSC_AC97PCR_RP)
438c2ecf20Sopenharmony_ci#define AC97PCR_CLRFIFO(stype)	\
448c2ecf20Sopenharmony_ci	((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define AC97STAT_BUSY(stype)	\
478c2ecf20Sopenharmony_ci	((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97STAT_TB : PSC_AC97STAT_RB)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* instance data. There can be only one, MacLeod!!!! */
508c2ecf20Sopenharmony_cistatic struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#if 0
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* this could theoretically work, but ac97->bus->card->private_data can be NULL
558c2ecf20Sopenharmony_ci * when snd_ac97_mixer() is called; I don't know if the rest further down the
568c2ecf20Sopenharmony_ci * chain are always valid either.
578c2ecf20Sopenharmony_ci */
588c2ecf20Sopenharmony_cistatic inline struct au1xpsc_audio_data *ac97_to_pscdata(struct snd_ac97 *x)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct snd_soc_card *c = x->bus->card->private_data;
618c2ecf20Sopenharmony_ci	return snd_soc_dai_get_drvdata(c->asoc_rtd_to_cpu(rtd, 0));
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#else
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci#define ac97_to_pscdata(x)	au1xpsc_ac97_workdata
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#endif
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* AC97 controller reads codec register */
718c2ecf20Sopenharmony_cistatic unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
728c2ecf20Sopenharmony_ci					unsigned short reg)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
758c2ecf20Sopenharmony_ci	unsigned short retry, tmo;
768c2ecf20Sopenharmony_ci	unsigned long data;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	__raw_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
798c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	retry = AC97_RW_RETRIES;
828c2ecf20Sopenharmony_ci	do {
838c2ecf20Sopenharmony_ci		mutex_lock(&pscdata->lock);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		__raw_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg),
868c2ecf20Sopenharmony_ci			  AC97_CDC(pscdata));
878c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		tmo = 20;
908c2ecf20Sopenharmony_ci		do {
918c2ecf20Sopenharmony_ci			udelay(21);
928c2ecf20Sopenharmony_ci			if (__raw_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
938c2ecf20Sopenharmony_ci				break;
948c2ecf20Sopenharmony_ci		} while (--tmo);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci		data = __raw_readl(AC97_CDC(pscdata));
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		__raw_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
998c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		mutex_unlock(&pscdata->lock);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		if (reg != ((data >> 16) & 0x7f))
1048c2ecf20Sopenharmony_ci			tmo = 1;	/* wrong register, try again */
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	} while (--retry && !tmo);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return retry ? data & 0xffff : 0xffff;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/* AC97 controller writes to codec register */
1128c2ecf20Sopenharmony_cistatic void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
1138c2ecf20Sopenharmony_ci				unsigned short val)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
1168c2ecf20Sopenharmony_ci	unsigned int tmo, retry;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	__raw_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
1198c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	retry = AC97_RW_RETRIES;
1228c2ecf20Sopenharmony_ci	do {
1238c2ecf20Sopenharmony_ci		mutex_lock(&pscdata->lock);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci		__raw_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff),
1268c2ecf20Sopenharmony_ci			  AC97_CDC(pscdata));
1278c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		tmo = 20;
1308c2ecf20Sopenharmony_ci		do {
1318c2ecf20Sopenharmony_ci			udelay(21);
1328c2ecf20Sopenharmony_ci			if (__raw_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
1338c2ecf20Sopenharmony_ci				break;
1348c2ecf20Sopenharmony_ci		} while (--tmo);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		__raw_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
1378c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		mutex_unlock(&pscdata->lock);
1408c2ecf20Sopenharmony_ci	} while (--retry && !tmo);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/* AC97 controller asserts a warm reset */
1448c2ecf20Sopenharmony_cistatic void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	__raw_writel(PSC_AC97RST_SNC, AC97_RST(pscdata));
1498c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
1508c2ecf20Sopenharmony_ci	msleep(10);
1518c2ecf20Sopenharmony_ci	__raw_writel(0, AC97_RST(pscdata));
1528c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
1588c2ecf20Sopenharmony_ci	int i;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	/* disable PSC during cold reset */
1618c2ecf20Sopenharmony_ci	__raw_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
1628c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
1638c2ecf20Sopenharmony_ci	__raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(pscdata));
1648c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* issue cold reset */
1678c2ecf20Sopenharmony_ci	__raw_writel(PSC_AC97RST_RST, AC97_RST(pscdata));
1688c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
1698c2ecf20Sopenharmony_ci	msleep(500);
1708c2ecf20Sopenharmony_ci	__raw_writel(0, AC97_RST(pscdata));
1718c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	/* enable PSC */
1748c2ecf20Sopenharmony_ci	__raw_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
1758c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	/* wait for PSC to indicate it's ready */
1788c2ecf20Sopenharmony_ci	i = 1000;
1798c2ecf20Sopenharmony_ci	while (!((__raw_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
1808c2ecf20Sopenharmony_ci		msleep(1);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (i == 0) {
1838c2ecf20Sopenharmony_ci		printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n");
1848c2ecf20Sopenharmony_ci		return;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	/* enable the ac97 function */
1888c2ecf20Sopenharmony_ci	__raw_writel(pscdata->cfg | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
1898c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	/* wait for AC97 core to become ready */
1928c2ecf20Sopenharmony_ci	i = 1000;
1938c2ecf20Sopenharmony_ci	while (!((__raw_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
1948c2ecf20Sopenharmony_ci		msleep(1);
1958c2ecf20Sopenharmony_ci	if (i == 0)
1968c2ecf20Sopenharmony_ci		printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/* AC97 controller operations */
2008c2ecf20Sopenharmony_cistatic struct snd_ac97_bus_ops psc_ac97_ops = {
2018c2ecf20Sopenharmony_ci	.read		= au1xpsc_ac97_read,
2028c2ecf20Sopenharmony_ci	.write		= au1xpsc_ac97_write,
2038c2ecf20Sopenharmony_ci	.reset		= au1xpsc_ac97_cold_reset,
2048c2ecf20Sopenharmony_ci	.warm_reset	= au1xpsc_ac97_warm_reset,
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
2088c2ecf20Sopenharmony_ci				  struct snd_pcm_hw_params *params,
2098c2ecf20Sopenharmony_ci				  struct snd_soc_dai *dai)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
2128c2ecf20Sopenharmony_ci	unsigned long r, ro, stat;
2138c2ecf20Sopenharmony_ci	int chans, t, stype = substream->stream;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	chans = params_channels(params);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	r = ro = __raw_readl(AC97_CFG(pscdata));
2188c2ecf20Sopenharmony_ci	stat = __raw_readl(AC97_STAT(pscdata));
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/* already active? */
2218c2ecf20Sopenharmony_ci	if (stat & (PSC_AC97STAT_TB | PSC_AC97STAT_RB)) {
2228c2ecf20Sopenharmony_ci		/* reject parameters not currently set up */
2238c2ecf20Sopenharmony_ci		if ((PSC_AC97CFG_GET_LEN(r) != params->msbits) ||
2248c2ecf20Sopenharmony_ci		    (pscdata->rate != params_rate(params)))
2258c2ecf20Sopenharmony_ci			return -EINVAL;
2268c2ecf20Sopenharmony_ci	} else {
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		/* set sample bitdepth: REG[24:21]=(BITS-2)/2 */
2298c2ecf20Sopenharmony_ci		r &= ~PSC_AC97CFG_LEN_MASK;
2308c2ecf20Sopenharmony_ci		r |= PSC_AC97CFG_SET_LEN(params->msbits);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci		/* channels: enable slots for front L/R channel */
2338c2ecf20Sopenharmony_ci		if (stype == SNDRV_PCM_STREAM_PLAYBACK) {
2348c2ecf20Sopenharmony_ci			r &= ~PSC_AC97CFG_TXSLOT_MASK;
2358c2ecf20Sopenharmony_ci			r |= PSC_AC97CFG_TXSLOT_ENA(3);
2368c2ecf20Sopenharmony_ci			r |= PSC_AC97CFG_TXSLOT_ENA(4);
2378c2ecf20Sopenharmony_ci		} else {
2388c2ecf20Sopenharmony_ci			r &= ~PSC_AC97CFG_RXSLOT_MASK;
2398c2ecf20Sopenharmony_ci			r |= PSC_AC97CFG_RXSLOT_ENA(3);
2408c2ecf20Sopenharmony_ci			r |= PSC_AC97CFG_RXSLOT_ENA(4);
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		/* do we need to poke the hardware? */
2448c2ecf20Sopenharmony_ci		if (!(r ^ ro))
2458c2ecf20Sopenharmony_ci			goto out;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		/* ac97 engine is about to be disabled */
2488c2ecf20Sopenharmony_ci		mutex_lock(&pscdata->lock);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci		/* disable AC97 device controller first... */
2518c2ecf20Sopenharmony_ci		__raw_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
2528c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci		/* ...wait for it... */
2558c2ecf20Sopenharmony_ci		t = 100;
2568c2ecf20Sopenharmony_ci		while ((__raw_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) && --t)
2578c2ecf20Sopenharmony_ci			msleep(1);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		if (!t)
2608c2ecf20Sopenharmony_ci			printk(KERN_ERR "PSC-AC97: can't disable!\n");
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		/* ...write config... */
2638c2ecf20Sopenharmony_ci		__raw_writel(r, AC97_CFG(pscdata));
2648c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci		/* ...enable the AC97 controller again... */
2678c2ecf20Sopenharmony_ci		__raw_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
2688c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci		/* ...and wait for ready bit */
2718c2ecf20Sopenharmony_ci		t = 100;
2728c2ecf20Sopenharmony_ci		while ((!(__raw_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && --t)
2738c2ecf20Sopenharmony_ci			msleep(1);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		if (!t)
2768c2ecf20Sopenharmony_ci			printk(KERN_ERR "PSC-AC97: can't enable!\n");
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		mutex_unlock(&pscdata->lock);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		pscdata->cfg = r;
2818c2ecf20Sopenharmony_ci		pscdata->rate = params_rate(params);
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ciout:
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
2898c2ecf20Sopenharmony_ci				int cmd, struct snd_soc_dai *dai)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
2928c2ecf20Sopenharmony_ci	int ret, stype = substream->stream;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	ret = 0;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	switch (cmd) {
2978c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
2988c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
2998c2ecf20Sopenharmony_ci		__raw_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
3008c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
3018c2ecf20Sopenharmony_ci		__raw_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
3028c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
3038c2ecf20Sopenharmony_ci		break;
3048c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
3058c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
3068c2ecf20Sopenharmony_ci		__raw_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
3078c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci		while (__raw_readl(AC97_STAT(pscdata)) & AC97STAT_BUSY(stype))
3108c2ecf20Sopenharmony_ci			asm volatile ("nop");
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci		__raw_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
3138c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		break;
3168c2ecf20Sopenharmony_ci	default:
3178c2ecf20Sopenharmony_ci		ret = -EINVAL;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	return ret;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic int au1xpsc_ac97_startup(struct snd_pcm_substream *substream,
3238c2ecf20Sopenharmony_ci				struct snd_soc_dai *dai)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
3268c2ecf20Sopenharmony_ci	snd_soc_dai_set_dma_data(dai, substream, &pscdata->dmaids[0]);
3278c2ecf20Sopenharmony_ci	return 0;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic int au1xpsc_ac97_probe(struct snd_soc_dai *dai)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	return au1xpsc_ac97_workdata ? 0 : -ENODEV;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
3368c2ecf20Sopenharmony_ci	.startup	= au1xpsc_ac97_startup,
3378c2ecf20Sopenharmony_ci	.trigger	= au1xpsc_ac97_trigger,
3388c2ecf20Sopenharmony_ci	.hw_params	= au1xpsc_ac97_hw_params,
3398c2ecf20Sopenharmony_ci};
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
3428c2ecf20Sopenharmony_ci	.probe			= au1xpsc_ac97_probe,
3438c2ecf20Sopenharmony_ci	.playback = {
3448c2ecf20Sopenharmony_ci		.rates		= AC97_RATES,
3458c2ecf20Sopenharmony_ci		.formats	= AC97_FMTS,
3468c2ecf20Sopenharmony_ci		.channels_min	= 2,
3478c2ecf20Sopenharmony_ci		.channels_max	= 2,
3488c2ecf20Sopenharmony_ci	},
3498c2ecf20Sopenharmony_ci	.capture = {
3508c2ecf20Sopenharmony_ci		.rates		= AC97_RATES,
3518c2ecf20Sopenharmony_ci		.formats	= AC97_FMTS,
3528c2ecf20Sopenharmony_ci		.channels_min	= 2,
3538c2ecf20Sopenharmony_ci		.channels_max	= 2,
3548c2ecf20Sopenharmony_ci	},
3558c2ecf20Sopenharmony_ci	.ops = &au1xpsc_ac97_dai_ops,
3568c2ecf20Sopenharmony_ci};
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver au1xpsc_ac97_component = {
3598c2ecf20Sopenharmony_ci	.name		= "au1xpsc-ac97",
3608c2ecf20Sopenharmony_ci};
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	int ret;
3658c2ecf20Sopenharmony_ci	struct resource *dmares;
3668c2ecf20Sopenharmony_ci	unsigned long sel;
3678c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *wd;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	wd = devm_kzalloc(&pdev->dev, sizeof(struct au1xpsc_audio_data),
3708c2ecf20Sopenharmony_ci			  GFP_KERNEL);
3718c2ecf20Sopenharmony_ci	if (!wd)
3728c2ecf20Sopenharmony_ci		return -ENOMEM;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	mutex_init(&wd->lock);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	wd->mmio = devm_platform_ioremap_resource(pdev, 0);
3778c2ecf20Sopenharmony_ci	if (IS_ERR(wd->mmio))
3788c2ecf20Sopenharmony_ci		return PTR_ERR(wd->mmio);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
3818c2ecf20Sopenharmony_ci	if (!dmares)
3828c2ecf20Sopenharmony_ci		return -EBUSY;
3838c2ecf20Sopenharmony_ci	wd->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1);
3868c2ecf20Sopenharmony_ci	if (!dmares)
3878c2ecf20Sopenharmony_ci		return -EBUSY;
3888c2ecf20Sopenharmony_ci	wd->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/* configuration: max dma trigger threshold, enable ac97 */
3918c2ecf20Sopenharmony_ci	wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 |
3928c2ecf20Sopenharmony_ci		  PSC_AC97CFG_DE_ENABLE;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/* preserve PSC clock source set up by platform	 */
3958c2ecf20Sopenharmony_ci	sel = __raw_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
3968c2ecf20Sopenharmony_ci	__raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
3978c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
3988c2ecf20Sopenharmony_ci	__raw_writel(0, PSC_SEL(wd));
3998c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
4008c2ecf20Sopenharmony_ci	__raw_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd));
4018c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	/* name the DAI like this device instance ("au1xpsc-ac97.PSCINDEX") */
4048c2ecf20Sopenharmony_ci	memcpy(&wd->dai_drv, &au1xpsc_ac97_dai_template,
4058c2ecf20Sopenharmony_ci	       sizeof(struct snd_soc_dai_driver));
4068c2ecf20Sopenharmony_ci	wd->dai_drv.name = dev_name(&pdev->dev);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, wd);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	ret = snd_soc_set_ac97_ops(&psc_ac97_ops);
4118c2ecf20Sopenharmony_ci	if (ret)
4128c2ecf20Sopenharmony_ci		return ret;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	ret = snd_soc_register_component(&pdev->dev, &au1xpsc_ac97_component,
4158c2ecf20Sopenharmony_ci					 &wd->dai_drv, 1);
4168c2ecf20Sopenharmony_ci	if (ret)
4178c2ecf20Sopenharmony_ci		return ret;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	au1xpsc_ac97_workdata = wd;
4208c2ecf20Sopenharmony_ci	return 0;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic int au1xpsc_ac97_drvremove(struct platform_device *pdev)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	snd_soc_unregister_component(&pdev->dev);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	/* disable PSC completely */
4308c2ecf20Sopenharmony_ci	__raw_writel(0, AC97_CFG(wd));
4318c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
4328c2ecf20Sopenharmony_ci	__raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
4338c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	au1xpsc_ac97_workdata = NULL;	/* MDEV */
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	return 0;
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4418c2ecf20Sopenharmony_cistatic int au1xpsc_ac97_drvsuspend(struct device *dev)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	/* save interesting registers and disable PSC */
4468c2ecf20Sopenharmony_ci	wd->pm[0] = __raw_readl(PSC_SEL(wd));
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	__raw_writel(0, AC97_CFG(wd));
4498c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
4508c2ecf20Sopenharmony_ci	__raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
4518c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	return 0;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic int au1xpsc_ac97_drvresume(struct device *dev)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* restore PSC clock config */
4618c2ecf20Sopenharmony_ci	__raw_writel(wd->pm[0] | PSC_SEL_PS_AC97MODE, PSC_SEL(wd));
4628c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	/* after this point the ac97 core will cold-reset the codec.
4658c2ecf20Sopenharmony_ci	 * During cold-reset the PSC is reinitialized and the last
4668c2ecf20Sopenharmony_ci	 * configuration set up in hw_params() is restored.
4678c2ecf20Sopenharmony_ci	 */
4688c2ecf20Sopenharmony_ci	return 0;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic const struct dev_pm_ops au1xpscac97_pmops = {
4728c2ecf20Sopenharmony_ci	.suspend	= au1xpsc_ac97_drvsuspend,
4738c2ecf20Sopenharmony_ci	.resume		= au1xpsc_ac97_drvresume,
4748c2ecf20Sopenharmony_ci};
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci#define AU1XPSCAC97_PMOPS &au1xpscac97_pmops
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci#else
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci#define AU1XPSCAC97_PMOPS NULL
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci#endif
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic struct platform_driver au1xpsc_ac97_driver = {
4858c2ecf20Sopenharmony_ci	.driver	= {
4868c2ecf20Sopenharmony_ci		.name	= "au1xpsc_ac97",
4878c2ecf20Sopenharmony_ci		.pm	= AU1XPSCAC97_PMOPS,
4888c2ecf20Sopenharmony_ci	},
4898c2ecf20Sopenharmony_ci	.probe		= au1xpsc_ac97_drvprobe,
4908c2ecf20Sopenharmony_ci	.remove		= au1xpsc_ac97_drvremove,
4918c2ecf20Sopenharmony_ci};
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cimodule_platform_driver(au1xpsc_ac97_driver);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4968c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
4978c2ecf20Sopenharmony_ciMODULE_AUTHOR("Manuel Lauss");
4988c2ecf20Sopenharmony_ci
499