18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Au1000/Au1500/Au1100 AC97C controller driver for ASoC
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * based on the old ALSA driver originally written by
88c2ecf20Sopenharmony_ci *			Charles Eidsness <charles@cooper-street.com>
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/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/suspend.h>
198c2ecf20Sopenharmony_ci#include <sound/core.h>
208c2ecf20Sopenharmony_ci#include <sound/pcm.h>
218c2ecf20Sopenharmony_ci#include <sound/initval.h>
228c2ecf20Sopenharmony_ci#include <sound/soc.h>
238c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1000.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "psc.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* register offsets and bits */
288c2ecf20Sopenharmony_ci#define AC97_CONFIG	0x00
298c2ecf20Sopenharmony_ci#define AC97_STATUS	0x04
308c2ecf20Sopenharmony_ci#define AC97_DATA	0x08
318c2ecf20Sopenharmony_ci#define AC97_CMDRESP	0x0c
328c2ecf20Sopenharmony_ci#define AC97_ENABLE	0x10
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define CFG_RC(x)	(((x) & 0x3ff) << 13)	/* valid rx slots mask */
358c2ecf20Sopenharmony_ci#define CFG_XS(x)	(((x) & 0x3ff) << 3)	/* valid tx slots mask */
368c2ecf20Sopenharmony_ci#define CFG_SG		(1 << 2)	/* sync gate */
378c2ecf20Sopenharmony_ci#define CFG_SN		(1 << 1)	/* sync control */
388c2ecf20Sopenharmony_ci#define CFG_RS		(1 << 0)	/* acrst# control */
398c2ecf20Sopenharmony_ci#define STAT_XU		(1 << 11)	/* tx underflow */
408c2ecf20Sopenharmony_ci#define STAT_XO		(1 << 10)	/* tx overflow */
418c2ecf20Sopenharmony_ci#define STAT_RU		(1 << 9)	/* rx underflow */
428c2ecf20Sopenharmony_ci#define STAT_RO		(1 << 8)	/* rx overflow */
438c2ecf20Sopenharmony_ci#define STAT_RD		(1 << 7)	/* codec ready */
448c2ecf20Sopenharmony_ci#define STAT_CP		(1 << 6)	/* command pending */
458c2ecf20Sopenharmony_ci#define STAT_TE		(1 << 4)	/* tx fifo empty */
468c2ecf20Sopenharmony_ci#define STAT_TF		(1 << 3)	/* tx fifo full */
478c2ecf20Sopenharmony_ci#define STAT_RE		(1 << 1)	/* rx fifo empty */
488c2ecf20Sopenharmony_ci#define STAT_RF		(1 << 0)	/* rx fifo full */
498c2ecf20Sopenharmony_ci#define CMD_SET_DATA(x)	(((x) & 0xffff) << 16)
508c2ecf20Sopenharmony_ci#define CMD_GET_DATA(x)	((x) & 0xffff)
518c2ecf20Sopenharmony_ci#define CMD_READ	(1 << 7)
528c2ecf20Sopenharmony_ci#define CMD_WRITE	(0 << 7)
538c2ecf20Sopenharmony_ci#define CMD_IDX(x)	((x) & 0x7f)
548c2ecf20Sopenharmony_ci#define EN_D		(1 << 1)	/* DISable bit */
558c2ecf20Sopenharmony_ci#define EN_CE		(1 << 0)	/* clock enable bit */
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* how often to retry failed codec register reads/writes */
588c2ecf20Sopenharmony_ci#define AC97_RW_RETRIES	5
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define AC97_RATES	\
618c2ecf20Sopenharmony_ci	SNDRV_PCM_RATE_CONTINUOUS
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define AC97_FMTS	\
648c2ecf20Sopenharmony_ci	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE)
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* instance data. There can be only one, MacLeod!!!!, fortunately there IS only
678c2ecf20Sopenharmony_ci * once AC97C on early Alchemy chips. The newer ones aren't so lucky.
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_cistatic struct au1xpsc_audio_data *ac97c_workdata;
708c2ecf20Sopenharmony_ci#define ac97_to_ctx(x)		ac97c_workdata
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	return __raw_readl(ctx->mmio + reg);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	__raw_writel(v, ctx->mmio + reg);
808c2ecf20Sopenharmony_ci	wmb();
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic unsigned short au1xac97c_ac97_read(struct snd_ac97 *ac97,
848c2ecf20Sopenharmony_ci					  unsigned short r)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
878c2ecf20Sopenharmony_ci	unsigned int tmo, retry;
888c2ecf20Sopenharmony_ci	unsigned long data;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	data = ~0;
918c2ecf20Sopenharmony_ci	retry = AC97_RW_RETRIES;
928c2ecf20Sopenharmony_ci	do {
938c2ecf20Sopenharmony_ci		mutex_lock(&ctx->lock);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci		tmo = 6;
968c2ecf20Sopenharmony_ci		while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo)
978c2ecf20Sopenharmony_ci			udelay(21);	/* wait an ac97 frame time */
988c2ecf20Sopenharmony_ci		if (!tmo) {
998c2ecf20Sopenharmony_ci			pr_debug("ac97rd timeout #1\n");
1008c2ecf20Sopenharmony_ci			goto next;
1018c2ecf20Sopenharmony_ci		}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		WR(ctx, AC97_CMDRESP, CMD_IDX(r) | CMD_READ);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci		/* stupid errata: data is only valid for 21us, so
1068c2ecf20Sopenharmony_ci		 * poll, Forrest, poll...
1078c2ecf20Sopenharmony_ci		 */
1088c2ecf20Sopenharmony_ci		tmo = 0x10000;
1098c2ecf20Sopenharmony_ci		while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo)
1108c2ecf20Sopenharmony_ci			asm volatile ("nop");
1118c2ecf20Sopenharmony_ci		data = RD(ctx, AC97_CMDRESP);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci		if (!tmo)
1148c2ecf20Sopenharmony_ci			pr_debug("ac97rd timeout #2\n");
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cinext:
1178c2ecf20Sopenharmony_ci		mutex_unlock(&ctx->lock);
1188c2ecf20Sopenharmony_ci	} while (--retry && !tmo);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	pr_debug("AC97RD %04x %04lx %d\n", r, data, retry);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	return retry ? data & 0xffff : 0xffff;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic void au1xac97c_ac97_write(struct snd_ac97 *ac97, unsigned short r,
1268c2ecf20Sopenharmony_ci				 unsigned short v)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
1298c2ecf20Sopenharmony_ci	unsigned int tmo, retry;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	retry = AC97_RW_RETRIES;
1328c2ecf20Sopenharmony_ci	do {
1338c2ecf20Sopenharmony_ci		mutex_lock(&ctx->lock);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		for (tmo = 5; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--)
1368c2ecf20Sopenharmony_ci			udelay(21);
1378c2ecf20Sopenharmony_ci		if (!tmo) {
1388c2ecf20Sopenharmony_ci			pr_debug("ac97wr timeout #1\n");
1398c2ecf20Sopenharmony_ci			goto next;
1408c2ecf20Sopenharmony_ci		}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci		WR(ctx, AC97_CMDRESP, CMD_WRITE | CMD_IDX(r) | CMD_SET_DATA(v));
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci		for (tmo = 10; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--)
1458c2ecf20Sopenharmony_ci			udelay(21);
1468c2ecf20Sopenharmony_ci		if (!tmo)
1478c2ecf20Sopenharmony_ci			pr_debug("ac97wr timeout #2\n");
1488c2ecf20Sopenharmony_cinext:
1498c2ecf20Sopenharmony_ci		mutex_unlock(&ctx->lock);
1508c2ecf20Sopenharmony_ci	} while (--retry && !tmo);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	pr_debug("AC97WR %04x %04x %d\n", r, v, retry);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic void au1xac97c_ac97_warm_reset(struct snd_ac97 *ac97)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG | CFG_SN);
1608c2ecf20Sopenharmony_ci	msleep(20);
1618c2ecf20Sopenharmony_ci	WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG);
1628c2ecf20Sopenharmony_ci	WR(ctx, AC97_CONFIG, ctx->cfg);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic void au1xac97c_ac97_cold_reset(struct snd_ac97 *ac97)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
1688c2ecf20Sopenharmony_ci	int i;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	WR(ctx, AC97_CONFIG, ctx->cfg | CFG_RS);
1718c2ecf20Sopenharmony_ci	msleep(500);
1728c2ecf20Sopenharmony_ci	WR(ctx, AC97_CONFIG, ctx->cfg);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* wait for codec ready */
1758c2ecf20Sopenharmony_ci	i = 50;
1768c2ecf20Sopenharmony_ci	while (((RD(ctx, AC97_STATUS) & STAT_RD) == 0) && --i)
1778c2ecf20Sopenharmony_ci		msleep(20);
1788c2ecf20Sopenharmony_ci	if (!i)
1798c2ecf20Sopenharmony_ci		printk(KERN_ERR "ac97c: codec not ready after cold reset\n");
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/* AC97 controller operations */
1838c2ecf20Sopenharmony_cistatic struct snd_ac97_bus_ops ac97c_bus_ops = {
1848c2ecf20Sopenharmony_ci	.read		= au1xac97c_ac97_read,
1858c2ecf20Sopenharmony_ci	.write		= au1xac97c_ac97_write,
1868c2ecf20Sopenharmony_ci	.reset		= au1xac97c_ac97_cold_reset,
1878c2ecf20Sopenharmony_ci	.warm_reset	= au1xac97c_ac97_warm_reset,
1888c2ecf20Sopenharmony_ci};
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic int alchemy_ac97c_startup(struct snd_pcm_substream *substream,
1918c2ecf20Sopenharmony_ci				 struct snd_soc_dai *dai)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
1948c2ecf20Sopenharmony_ci	snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]);
1958c2ecf20Sopenharmony_ci	return 0;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops alchemy_ac97c_ops = {
1998c2ecf20Sopenharmony_ci	.startup		= alchemy_ac97c_startup,
2008c2ecf20Sopenharmony_ci};
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic int au1xac97c_dai_probe(struct snd_soc_dai *dai)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	return ac97c_workdata ? 0 : -ENODEV;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver au1xac97c_dai_driver = {
2088c2ecf20Sopenharmony_ci	.name			= "alchemy-ac97c",
2098c2ecf20Sopenharmony_ci	.probe			= au1xac97c_dai_probe,
2108c2ecf20Sopenharmony_ci	.playback = {
2118c2ecf20Sopenharmony_ci		.rates		= AC97_RATES,
2128c2ecf20Sopenharmony_ci		.formats	= AC97_FMTS,
2138c2ecf20Sopenharmony_ci		.channels_min	= 2,
2148c2ecf20Sopenharmony_ci		.channels_max	= 2,
2158c2ecf20Sopenharmony_ci	},
2168c2ecf20Sopenharmony_ci	.capture = {
2178c2ecf20Sopenharmony_ci		.rates		= AC97_RATES,
2188c2ecf20Sopenharmony_ci		.formats	= AC97_FMTS,
2198c2ecf20Sopenharmony_ci		.channels_min	= 2,
2208c2ecf20Sopenharmony_ci		.channels_max	= 2,
2218c2ecf20Sopenharmony_ci	},
2228c2ecf20Sopenharmony_ci	.ops			= &alchemy_ac97c_ops,
2238c2ecf20Sopenharmony_ci};
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver au1xac97c_component = {
2268c2ecf20Sopenharmony_ci	.name		= "au1xac97c",
2278c2ecf20Sopenharmony_ci};
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic int au1xac97c_drvprobe(struct platform_device *pdev)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	int ret;
2328c2ecf20Sopenharmony_ci	struct resource *iores, *dmares;
2338c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *ctx;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
2368c2ecf20Sopenharmony_ci	if (!ctx)
2378c2ecf20Sopenharmony_ci		return -ENOMEM;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	mutex_init(&ctx->lock);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2428c2ecf20Sopenharmony_ci	if (!iores)
2438c2ecf20Sopenharmony_ci		return -ENODEV;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (!devm_request_mem_region(&pdev->dev, iores->start,
2468c2ecf20Sopenharmony_ci				     resource_size(iores),
2478c2ecf20Sopenharmony_ci				     pdev->name))
2488c2ecf20Sopenharmony_ci		return -EBUSY;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	ctx->mmio = devm_ioremap(&pdev->dev, iores->start,
2518c2ecf20Sopenharmony_ci					 resource_size(iores));
2528c2ecf20Sopenharmony_ci	if (!ctx->mmio)
2538c2ecf20Sopenharmony_ci		return -EBUSY;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
2568c2ecf20Sopenharmony_ci	if (!dmares)
2578c2ecf20Sopenharmony_ci		return -EBUSY;
2588c2ecf20Sopenharmony_ci	ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1);
2618c2ecf20Sopenharmony_ci	if (!dmares)
2628c2ecf20Sopenharmony_ci		return -EBUSY;
2638c2ecf20Sopenharmony_ci	ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	/* switch it on */
2668c2ecf20Sopenharmony_ci	WR(ctx, AC97_ENABLE, EN_D | EN_CE);
2678c2ecf20Sopenharmony_ci	WR(ctx, AC97_ENABLE, EN_CE);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	ctx->cfg = CFG_RC(3) | CFG_XS(3);
2708c2ecf20Sopenharmony_ci	WR(ctx, AC97_CONFIG, ctx->cfg);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, ctx);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	ret = snd_soc_set_ac97_ops(&ac97c_bus_ops);
2758c2ecf20Sopenharmony_ci	if (ret)
2768c2ecf20Sopenharmony_ci		return ret;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	ret = snd_soc_register_component(&pdev->dev, &au1xac97c_component,
2798c2ecf20Sopenharmony_ci					 &au1xac97c_dai_driver, 1);
2808c2ecf20Sopenharmony_ci	if (ret)
2818c2ecf20Sopenharmony_ci		return ret;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	ac97c_workdata = ctx;
2848c2ecf20Sopenharmony_ci	return 0;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic int au1xac97c_drvremove(struct platform_device *pdev)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	snd_soc_unregister_component(&pdev->dev);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	WR(ctx, AC97_ENABLE, EN_D);	/* clock off, disable */
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	ac97c_workdata = NULL;	/* MDEV */
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
3018c2ecf20Sopenharmony_cistatic int au1xac97c_drvsuspend(struct device *dev)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	WR(ctx, AC97_ENABLE, EN_D);	/* clock off, disable */
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	return 0;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic int au1xac97c_drvresume(struct device *dev)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	WR(ctx, AC97_ENABLE, EN_D | EN_CE);
3158c2ecf20Sopenharmony_ci	WR(ctx, AC97_ENABLE, EN_CE);
3168c2ecf20Sopenharmony_ci	WR(ctx, AC97_CONFIG, ctx->cfg);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic const struct dev_pm_ops au1xpscac97_pmops = {
3228c2ecf20Sopenharmony_ci	.suspend	= au1xac97c_drvsuspend,
3238c2ecf20Sopenharmony_ci	.resume		= au1xac97c_drvresume,
3248c2ecf20Sopenharmony_ci};
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci#define AU1XPSCAC97_PMOPS (&au1xpscac97_pmops)
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci#else
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci#define AU1XPSCAC97_PMOPS NULL
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci#endif
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic struct platform_driver au1xac97c_driver = {
3358c2ecf20Sopenharmony_ci	.driver	= {
3368c2ecf20Sopenharmony_ci		.name	= "alchemy-ac97c",
3378c2ecf20Sopenharmony_ci		.pm	= AU1XPSCAC97_PMOPS,
3388c2ecf20Sopenharmony_ci	},
3398c2ecf20Sopenharmony_ci	.probe		= au1xac97c_drvprobe,
3408c2ecf20Sopenharmony_ci	.remove		= au1xac97c_drvremove,
3418c2ecf20Sopenharmony_ci};
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cimodule_platform_driver(au1xac97c_driver);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3468c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Au1000/1500/1100 AC97C ASoC driver");
3478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Manuel Lauss");
348