162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Au12x0/Au1550 PSC ALSA ASoC audio support.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (c) 2007-2009 MSC Vertriebsges.m.b.H.,
662306a36Sopenharmony_ci *	Manuel Lauss <manuel.lauss@gmail.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Au1xxx-PSC AC97 glue.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/device.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/mutex.h>
1762306a36Sopenharmony_ci#include <linux/suspend.h>
1862306a36Sopenharmony_ci#include <sound/core.h>
1962306a36Sopenharmony_ci#include <sound/pcm.h>
2062306a36Sopenharmony_ci#include <sound/initval.h>
2162306a36Sopenharmony_ci#include <sound/soc.h>
2262306a36Sopenharmony_ci#include <asm/mach-au1x00/au1000.h>
2362306a36Sopenharmony_ci#include <asm/mach-au1x00/au1xxx_psc.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "psc.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* how often to retry failed codec register reads/writes */
2862306a36Sopenharmony_ci#define AC97_RW_RETRIES	5
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define AC97_DIR	\
3162306a36Sopenharmony_ci	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define AC97_RATES	\
3462306a36Sopenharmony_ci	SNDRV_PCM_RATE_8000_48000
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define AC97_FMTS	\
3762306a36Sopenharmony_ci	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define AC97PCR_START(stype)	\
4062306a36Sopenharmony_ci	((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TS : PSC_AC97PCR_RS)
4162306a36Sopenharmony_ci#define AC97PCR_STOP(stype)	\
4262306a36Sopenharmony_ci	((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TP : PSC_AC97PCR_RP)
4362306a36Sopenharmony_ci#define AC97PCR_CLRFIFO(stype)	\
4462306a36Sopenharmony_ci	((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define AC97STAT_BUSY(stype)	\
4762306a36Sopenharmony_ci	((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97STAT_TB : PSC_AC97STAT_RB)
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* instance data. There can be only one, MacLeod!!!! */
5062306a36Sopenharmony_cistatic struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#if 0
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* this could theoretically work, but ac97->bus->card->private_data can be NULL
5562306a36Sopenharmony_ci * when snd_ac97_mixer() is called; I don't know if the rest further down the
5662306a36Sopenharmony_ci * chain are always valid either.
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_cistatic inline struct au1xpsc_audio_data *ac97_to_pscdata(struct snd_ac97 *x)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct snd_soc_card *c = x->bus->card->private_data;
6162306a36Sopenharmony_ci	return snd_soc_dai_get_drvdata(c->asoc_rtd_to_cpu(rtd, 0));
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#else
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define ac97_to_pscdata(x)	au1xpsc_ac97_workdata
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#endif
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* AC97 controller reads codec register */
7162306a36Sopenharmony_cistatic unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
7262306a36Sopenharmony_ci					unsigned short reg)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
7562306a36Sopenharmony_ci	unsigned short retry, tmo;
7662306a36Sopenharmony_ci	unsigned long data;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	__raw_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
7962306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	retry = AC97_RW_RETRIES;
8262306a36Sopenharmony_ci	do {
8362306a36Sopenharmony_ci		mutex_lock(&pscdata->lock);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		__raw_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg),
8662306a36Sopenharmony_ci			  AC97_CDC(pscdata));
8762306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci		tmo = 20;
9062306a36Sopenharmony_ci		do {
9162306a36Sopenharmony_ci			udelay(21);
9262306a36Sopenharmony_ci			if (__raw_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
9362306a36Sopenharmony_ci				break;
9462306a36Sopenharmony_ci		} while (--tmo);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci		data = __raw_readl(AC97_CDC(pscdata));
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		__raw_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
9962306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		mutex_unlock(&pscdata->lock);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		if (reg != ((data >> 16) & 0x7f))
10462306a36Sopenharmony_ci			tmo = 1;	/* wrong register, try again */
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	} while (--retry && !tmo);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return retry ? data & 0xffff : 0xffff;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/* AC97 controller writes to codec register */
11262306a36Sopenharmony_cistatic void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
11362306a36Sopenharmony_ci				unsigned short val)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
11662306a36Sopenharmony_ci	unsigned int tmo, retry;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	__raw_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
11962306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	retry = AC97_RW_RETRIES;
12262306a36Sopenharmony_ci	do {
12362306a36Sopenharmony_ci		mutex_lock(&pscdata->lock);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci		__raw_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff),
12662306a36Sopenharmony_ci			  AC97_CDC(pscdata));
12762306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		tmo = 20;
13062306a36Sopenharmony_ci		do {
13162306a36Sopenharmony_ci			udelay(21);
13262306a36Sopenharmony_ci			if (__raw_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
13362306a36Sopenharmony_ci				break;
13462306a36Sopenharmony_ci		} while (--tmo);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		__raw_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
13762306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		mutex_unlock(&pscdata->lock);
14062306a36Sopenharmony_ci	} while (--retry && !tmo);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/* AC97 controller asserts a warm reset */
14462306a36Sopenharmony_cistatic void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	__raw_writel(PSC_AC97RST_SNC, AC97_RST(pscdata));
14962306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
15062306a36Sopenharmony_ci	msleep(10);
15162306a36Sopenharmony_ci	__raw_writel(0, AC97_RST(pscdata));
15262306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
15862306a36Sopenharmony_ci	int i;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* disable PSC during cold reset */
16162306a36Sopenharmony_ci	__raw_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
16262306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
16362306a36Sopenharmony_ci	__raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(pscdata));
16462306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* issue cold reset */
16762306a36Sopenharmony_ci	__raw_writel(PSC_AC97RST_RST, AC97_RST(pscdata));
16862306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
16962306a36Sopenharmony_ci	msleep(500);
17062306a36Sopenharmony_ci	__raw_writel(0, AC97_RST(pscdata));
17162306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* enable PSC */
17462306a36Sopenharmony_ci	__raw_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
17562306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* wait for PSC to indicate it's ready */
17862306a36Sopenharmony_ci	i = 1000;
17962306a36Sopenharmony_ci	while (!((__raw_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
18062306a36Sopenharmony_ci		msleep(1);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (i == 0) {
18362306a36Sopenharmony_ci		printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n");
18462306a36Sopenharmony_ci		return;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* enable the ac97 function */
18862306a36Sopenharmony_ci	__raw_writel(pscdata->cfg | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
18962306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* wait for AC97 core to become ready */
19262306a36Sopenharmony_ci	i = 1000;
19362306a36Sopenharmony_ci	while (!((__raw_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
19462306a36Sopenharmony_ci		msleep(1);
19562306a36Sopenharmony_ci	if (i == 0)
19662306a36Sopenharmony_ci		printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci/* AC97 controller operations */
20062306a36Sopenharmony_cistatic struct snd_ac97_bus_ops psc_ac97_ops = {
20162306a36Sopenharmony_ci	.read		= au1xpsc_ac97_read,
20262306a36Sopenharmony_ci	.write		= au1xpsc_ac97_write,
20362306a36Sopenharmony_ci	.reset		= au1xpsc_ac97_cold_reset,
20462306a36Sopenharmony_ci	.warm_reset	= au1xpsc_ac97_warm_reset,
20562306a36Sopenharmony_ci};
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
20862306a36Sopenharmony_ci				  struct snd_pcm_hw_params *params,
20962306a36Sopenharmony_ci				  struct snd_soc_dai *dai)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
21262306a36Sopenharmony_ci	unsigned long r, ro, stat;
21362306a36Sopenharmony_ci	int chans, t, stype = substream->stream;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	chans = params_channels(params);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	r = ro = __raw_readl(AC97_CFG(pscdata));
21862306a36Sopenharmony_ci	stat = __raw_readl(AC97_STAT(pscdata));
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* already active? */
22162306a36Sopenharmony_ci	if (stat & (PSC_AC97STAT_TB | PSC_AC97STAT_RB)) {
22262306a36Sopenharmony_ci		/* reject parameters not currently set up */
22362306a36Sopenharmony_ci		if ((PSC_AC97CFG_GET_LEN(r) != params->msbits) ||
22462306a36Sopenharmony_ci		    (pscdata->rate != params_rate(params)))
22562306a36Sopenharmony_ci			return -EINVAL;
22662306a36Sopenharmony_ci	} else {
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		/* set sample bitdepth: REG[24:21]=(BITS-2)/2 */
22962306a36Sopenharmony_ci		r &= ~PSC_AC97CFG_LEN_MASK;
23062306a36Sopenharmony_ci		r |= PSC_AC97CFG_SET_LEN(params->msbits);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		/* channels: enable slots for front L/R channel */
23362306a36Sopenharmony_ci		if (stype == SNDRV_PCM_STREAM_PLAYBACK) {
23462306a36Sopenharmony_ci			r &= ~PSC_AC97CFG_TXSLOT_MASK;
23562306a36Sopenharmony_ci			r |= PSC_AC97CFG_TXSLOT_ENA(3);
23662306a36Sopenharmony_ci			r |= PSC_AC97CFG_TXSLOT_ENA(4);
23762306a36Sopenharmony_ci		} else {
23862306a36Sopenharmony_ci			r &= ~PSC_AC97CFG_RXSLOT_MASK;
23962306a36Sopenharmony_ci			r |= PSC_AC97CFG_RXSLOT_ENA(3);
24062306a36Sopenharmony_ci			r |= PSC_AC97CFG_RXSLOT_ENA(4);
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		/* do we need to poke the hardware? */
24462306a36Sopenharmony_ci		if (!(r ^ ro))
24562306a36Sopenharmony_ci			goto out;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		/* ac97 engine is about to be disabled */
24862306a36Sopenharmony_ci		mutex_lock(&pscdata->lock);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		/* disable AC97 device controller first... */
25162306a36Sopenharmony_ci		__raw_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
25262306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		/* ...wait for it... */
25562306a36Sopenharmony_ci		t = 100;
25662306a36Sopenharmony_ci		while ((__raw_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) && --t)
25762306a36Sopenharmony_ci			msleep(1);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		if (!t)
26062306a36Sopenharmony_ci			printk(KERN_ERR "PSC-AC97: can't disable!\n");
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		/* ...write config... */
26362306a36Sopenharmony_ci		__raw_writel(r, AC97_CFG(pscdata));
26462306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		/* ...enable the AC97 controller again... */
26762306a36Sopenharmony_ci		__raw_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
26862306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		/* ...and wait for ready bit */
27162306a36Sopenharmony_ci		t = 100;
27262306a36Sopenharmony_ci		while ((!(__raw_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && --t)
27362306a36Sopenharmony_ci			msleep(1);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		if (!t)
27662306a36Sopenharmony_ci			printk(KERN_ERR "PSC-AC97: can't enable!\n");
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		mutex_unlock(&pscdata->lock);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci		pscdata->cfg = r;
28162306a36Sopenharmony_ci		pscdata->rate = params_rate(params);
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ciout:
28562306a36Sopenharmony_ci	return 0;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
28962306a36Sopenharmony_ci				int cmd, struct snd_soc_dai *dai)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
29262306a36Sopenharmony_ci	int ret, stype = substream->stream;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	ret = 0;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	switch (cmd) {
29762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
29862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
29962306a36Sopenharmony_ci		__raw_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
30062306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
30162306a36Sopenharmony_ci		__raw_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
30262306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
30362306a36Sopenharmony_ci		break;
30462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
30562306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
30662306a36Sopenharmony_ci		__raw_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
30762306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		while (__raw_readl(AC97_STAT(pscdata)) & AC97STAT_BUSY(stype))
31062306a36Sopenharmony_ci			asm volatile ("nop");
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		__raw_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
31362306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		break;
31662306a36Sopenharmony_ci	default:
31762306a36Sopenharmony_ci		ret = -EINVAL;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci	return ret;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic int au1xpsc_ac97_startup(struct snd_pcm_substream *substream,
32362306a36Sopenharmony_ci				struct snd_soc_dai *dai)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
32662306a36Sopenharmony_ci	snd_soc_dai_set_dma_data(dai, substream, &pscdata->dmaids[0]);
32762306a36Sopenharmony_ci	return 0;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic int au1xpsc_ac97_probe(struct snd_soc_dai *dai)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	return au1xpsc_ac97_workdata ? 0 : -ENODEV;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
33662306a36Sopenharmony_ci	.probe		= au1xpsc_ac97_probe,
33762306a36Sopenharmony_ci	.startup	= au1xpsc_ac97_startup,
33862306a36Sopenharmony_ci	.trigger	= au1xpsc_ac97_trigger,
33962306a36Sopenharmony_ci	.hw_params	= au1xpsc_ac97_hw_params,
34062306a36Sopenharmony_ci};
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
34362306a36Sopenharmony_ci	.playback = {
34462306a36Sopenharmony_ci		.rates		= AC97_RATES,
34562306a36Sopenharmony_ci		.formats	= AC97_FMTS,
34662306a36Sopenharmony_ci		.channels_min	= 2,
34762306a36Sopenharmony_ci		.channels_max	= 2,
34862306a36Sopenharmony_ci	},
34962306a36Sopenharmony_ci	.capture = {
35062306a36Sopenharmony_ci		.rates		= AC97_RATES,
35162306a36Sopenharmony_ci		.formats	= AC97_FMTS,
35262306a36Sopenharmony_ci		.channels_min	= 2,
35362306a36Sopenharmony_ci		.channels_max	= 2,
35462306a36Sopenharmony_ci	},
35562306a36Sopenharmony_ci	.ops = &au1xpsc_ac97_dai_ops,
35662306a36Sopenharmony_ci};
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic const struct snd_soc_component_driver au1xpsc_ac97_component = {
35962306a36Sopenharmony_ci	.name			= "au1xpsc-ac97",
36062306a36Sopenharmony_ci	.legacy_dai_naming	= 1,
36162306a36Sopenharmony_ci};
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	int ret;
36662306a36Sopenharmony_ci	struct resource *dmares;
36762306a36Sopenharmony_ci	unsigned long sel;
36862306a36Sopenharmony_ci	struct au1xpsc_audio_data *wd;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	wd = devm_kzalloc(&pdev->dev, sizeof(struct au1xpsc_audio_data),
37162306a36Sopenharmony_ci			  GFP_KERNEL);
37262306a36Sopenharmony_ci	if (!wd)
37362306a36Sopenharmony_ci		return -ENOMEM;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	mutex_init(&wd->lock);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	wd->mmio = devm_platform_ioremap_resource(pdev, 0);
37862306a36Sopenharmony_ci	if (IS_ERR(wd->mmio))
37962306a36Sopenharmony_ci		return PTR_ERR(wd->mmio);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
38262306a36Sopenharmony_ci	if (!dmares)
38362306a36Sopenharmony_ci		return -EBUSY;
38462306a36Sopenharmony_ci	wd->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1);
38762306a36Sopenharmony_ci	if (!dmares)
38862306a36Sopenharmony_ci		return -EBUSY;
38962306a36Sopenharmony_ci	wd->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/* configuration: max dma trigger threshold, enable ac97 */
39262306a36Sopenharmony_ci	wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 |
39362306a36Sopenharmony_ci		  PSC_AC97CFG_DE_ENABLE;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/* preserve PSC clock source set up by platform	 */
39662306a36Sopenharmony_ci	sel = __raw_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
39762306a36Sopenharmony_ci	__raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
39862306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
39962306a36Sopenharmony_ci	__raw_writel(0, PSC_SEL(wd));
40062306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
40162306a36Sopenharmony_ci	__raw_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd));
40262306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/* name the DAI like this device instance ("au1xpsc-ac97.PSCINDEX") */
40562306a36Sopenharmony_ci	memcpy(&wd->dai_drv, &au1xpsc_ac97_dai_template,
40662306a36Sopenharmony_ci	       sizeof(struct snd_soc_dai_driver));
40762306a36Sopenharmony_ci	wd->dai_drv.name = dev_name(&pdev->dev);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	platform_set_drvdata(pdev, wd);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	ret = snd_soc_set_ac97_ops(&psc_ac97_ops);
41262306a36Sopenharmony_ci	if (ret)
41362306a36Sopenharmony_ci		return ret;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	ret = snd_soc_register_component(&pdev->dev, &au1xpsc_ac97_component,
41662306a36Sopenharmony_ci					 &wd->dai_drv, 1);
41762306a36Sopenharmony_ci	if (ret)
41862306a36Sopenharmony_ci		return ret;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	au1xpsc_ac97_workdata = wd;
42162306a36Sopenharmony_ci	return 0;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic void au1xpsc_ac97_drvremove(struct platform_device *pdev)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	snd_soc_unregister_component(&pdev->dev);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	/* disable PSC completely */
43162306a36Sopenharmony_ci	__raw_writel(0, AC97_CFG(wd));
43262306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
43362306a36Sopenharmony_ci	__raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
43462306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	au1xpsc_ac97_workdata = NULL;	/* MDEV */
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci#ifdef CONFIG_PM
44062306a36Sopenharmony_cistatic int au1xpsc_ac97_drvsuspend(struct device *dev)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/* save interesting registers and disable PSC */
44562306a36Sopenharmony_ci	wd->pm[0] = __raw_readl(PSC_SEL(wd));
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	__raw_writel(0, AC97_CFG(wd));
44862306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
44962306a36Sopenharmony_ci	__raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
45062306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return 0;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic int au1xpsc_ac97_drvresume(struct device *dev)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/* restore PSC clock config */
46062306a36Sopenharmony_ci	__raw_writel(wd->pm[0] | PSC_SEL_PS_AC97MODE, PSC_SEL(wd));
46162306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	/* after this point the ac97 core will cold-reset the codec.
46462306a36Sopenharmony_ci	 * During cold-reset the PSC is reinitialized and the last
46562306a36Sopenharmony_ci	 * configuration set up in hw_params() is restored.
46662306a36Sopenharmony_ci	 */
46762306a36Sopenharmony_ci	return 0;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic const struct dev_pm_ops au1xpscac97_pmops = {
47162306a36Sopenharmony_ci	.suspend	= au1xpsc_ac97_drvsuspend,
47262306a36Sopenharmony_ci	.resume		= au1xpsc_ac97_drvresume,
47362306a36Sopenharmony_ci};
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci#define AU1XPSCAC97_PMOPS &au1xpscac97_pmops
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci#else
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci#define AU1XPSCAC97_PMOPS NULL
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci#endif
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic struct platform_driver au1xpsc_ac97_driver = {
48462306a36Sopenharmony_ci	.driver	= {
48562306a36Sopenharmony_ci		.name	= "au1xpsc_ac97",
48662306a36Sopenharmony_ci		.pm	= AU1XPSCAC97_PMOPS,
48762306a36Sopenharmony_ci	},
48862306a36Sopenharmony_ci	.probe		= au1xpsc_ac97_drvprobe,
48962306a36Sopenharmony_ci	.remove_new	= au1xpsc_ac97_drvremove,
49062306a36Sopenharmony_ci};
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cimodule_platform_driver(au1xpsc_ac97_driver);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
49562306a36Sopenharmony_ciMODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
49662306a36Sopenharmony_ciMODULE_AUTHOR("Manuel Lauss");
49762306a36Sopenharmony_ci
498