18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Matt Wu <Matt_Wu@acersoftech.com.cn>
48c2ecf20Sopenharmony_ci *  Apr 26, 2001
58c2ecf20Sopenharmony_ci *  Routines for control of ALi pci audio M5451
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  BUGS:
88c2ecf20Sopenharmony_ci *    --
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *  TODO:
118c2ecf20Sopenharmony_ci *    --
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/pci.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
228c2ecf20Sopenharmony_ci#include <sound/core.h>
238c2ecf20Sopenharmony_ci#include <sound/pcm.h>
248c2ecf20Sopenharmony_ci#include <sound/info.h>
258c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h>
268c2ecf20Sopenharmony_ci#include <sound/mpu401.h>
278c2ecf20Sopenharmony_ci#include <sound/initval.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matt Wu <Matt_Wu@acersoftech.com.cn>");
308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALI M5451");
318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
328c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{ALI,M5451,pci},{ALI,M5451}}");
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1;	/* Index */
358c2ecf20Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
368c2ecf20Sopenharmony_cistatic int pcm_channels = 32;
378c2ecf20Sopenharmony_cistatic bool spdif;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cimodule_param(index, int, 0444);
408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio.");
418c2ecf20Sopenharmony_cimodule_param(id, charp, 0444);
428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for ALI M5451 PCI Audio.");
438c2ecf20Sopenharmony_cimodule_param(pcm_channels, int, 0444);
448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcm_channels, "PCM Channels");
458c2ecf20Sopenharmony_cimodule_param(spdif, bool, 0444);
468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(spdif, "Support SPDIF I/O");
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* just for backward compatibility */
498c2ecf20Sopenharmony_cistatic bool enable;
508c2ecf20Sopenharmony_cimodule_param(enable, bool, 0444);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/*
548c2ecf20Sopenharmony_ci *  Constants definition
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define DEVICE_ID_ALI5451	((PCI_VENDOR_ID_AL<<16)|PCI_DEVICE_ID_AL_M5451)
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define ALI_CHANNELS		32
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define ALI_PCM_IN_CHANNEL	31
638c2ecf20Sopenharmony_ci#define ALI_SPDIF_IN_CHANNEL	19
648c2ecf20Sopenharmony_ci#define ALI_SPDIF_OUT_CHANNEL	15
658c2ecf20Sopenharmony_ci#define ALI_CENTER_CHANNEL	24
668c2ecf20Sopenharmony_ci#define ALI_LEF_CHANNEL		23
678c2ecf20Sopenharmony_ci#define ALI_SURR_LEFT_CHANNEL	26
688c2ecf20Sopenharmony_ci#define ALI_SURR_RIGHT_CHANNEL	25
698c2ecf20Sopenharmony_ci#define ALI_MODEM_IN_CHANNEL    21
708c2ecf20Sopenharmony_ci#define ALI_MODEM_OUT_CHANNEL   20
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#define	SNDRV_ALI_VOICE_TYPE_PCM	01
738c2ecf20Sopenharmony_ci#define SNDRV_ALI_VOICE_TYPE_OTH	02
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define	ALI_5451_V02		0x02
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/*
788c2ecf20Sopenharmony_ci *  Direct Registers
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#define ALI_LEGACY_DMAR0        0x00  /* ADR0 */
828c2ecf20Sopenharmony_ci#define ALI_LEGACY_DMAR4        0x04  /* CNT0 */
838c2ecf20Sopenharmony_ci#define ALI_LEGACY_DMAR11       0x0b  /* MOD  */
848c2ecf20Sopenharmony_ci#define ALI_LEGACY_DMAR15       0x0f  /* MMR  */
858c2ecf20Sopenharmony_ci#define ALI_MPUR0		0x20
868c2ecf20Sopenharmony_ci#define ALI_MPUR1		0x21
878c2ecf20Sopenharmony_ci#define ALI_MPUR2		0x22
888c2ecf20Sopenharmony_ci#define ALI_MPUR3		0x23
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#define	ALI_AC97_WRITE		0x40
918c2ecf20Sopenharmony_ci#define ALI_AC97_READ		0x44
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#define ALI_SCTRL		0x48
948c2ecf20Sopenharmony_ci#define   ALI_SPDIF_OUT_ENABLE		0x20
958c2ecf20Sopenharmony_ci#define   ALI_SCTRL_LINE_IN2		(1 << 9)
968c2ecf20Sopenharmony_ci#define   ALI_SCTRL_GPIO_IN2		(1 << 13)
978c2ecf20Sopenharmony_ci#define   ALI_SCTRL_LINE_OUT_EN 	(1 << 20)
988c2ecf20Sopenharmony_ci#define   ALI_SCTRL_GPIO_OUT_EN 	(1 << 23)
998c2ecf20Sopenharmony_ci#define   ALI_SCTRL_CODEC1_READY	(1 << 24)
1008c2ecf20Sopenharmony_ci#define   ALI_SCTRL_CODEC2_READY	(1 << 25)
1018c2ecf20Sopenharmony_ci#define ALI_AC97_GPIO		0x4c
1028c2ecf20Sopenharmony_ci#define   ALI_AC97_GPIO_ENABLE		0x8000
1038c2ecf20Sopenharmony_ci#define   ALI_AC97_GPIO_DATA_SHIFT	16
1048c2ecf20Sopenharmony_ci#define ALI_SPDIF_CS		0x70
1058c2ecf20Sopenharmony_ci#define ALI_SPDIF_CTRL		0x74
1068c2ecf20Sopenharmony_ci#define   ALI_SPDIF_IN_FUNC_ENABLE	0x02
1078c2ecf20Sopenharmony_ci#define   ALI_SPDIF_IN_CH_STATUS	0x40
1088c2ecf20Sopenharmony_ci#define   ALI_SPDIF_OUT_CH_STATUS	0xbf
1098c2ecf20Sopenharmony_ci#define ALI_START		0x80
1108c2ecf20Sopenharmony_ci#define ALI_STOP		0x84
1118c2ecf20Sopenharmony_ci#define ALI_CSPF		0x90
1128c2ecf20Sopenharmony_ci#define ALI_AINT		0x98
1138c2ecf20Sopenharmony_ci#define ALI_GC_CIR		0xa0
1148c2ecf20Sopenharmony_ci	#define ENDLP_IE		0x00001000
1158c2ecf20Sopenharmony_ci	#define MIDLP_IE		0x00002000
1168c2ecf20Sopenharmony_ci#define ALI_AINTEN		0xa4
1178c2ecf20Sopenharmony_ci#define ALI_VOLUME		0xa8
1188c2ecf20Sopenharmony_ci#define ALI_SBDELTA_DELTA_R     0xac
1198c2ecf20Sopenharmony_ci#define ALI_MISCINT		0xb0
1208c2ecf20Sopenharmony_ci	#define ADDRESS_IRQ		0x00000020
1218c2ecf20Sopenharmony_ci	#define TARGET_REACHED		0x00008000
1228c2ecf20Sopenharmony_ci	#define MIXER_OVERFLOW		0x00000800
1238c2ecf20Sopenharmony_ci	#define MIXER_UNDERFLOW		0x00000400
1248c2ecf20Sopenharmony_ci	#define GPIO_IRQ		0x01000000
1258c2ecf20Sopenharmony_ci#define ALI_SBBL_SBCL           0xc0
1268c2ecf20Sopenharmony_ci#define ALI_SBCTRL_SBE2R_SBDD   0xc4
1278c2ecf20Sopenharmony_ci#define ALI_STIMER		0xc8
1288c2ecf20Sopenharmony_ci#define ALI_GLOBAL_CONTROL	0xd4
1298c2ecf20Sopenharmony_ci#define   ALI_SPDIF_OUT_SEL_PCM		0x00000400 /* bit 10 */
1308c2ecf20Sopenharmony_ci#define   ALI_SPDIF_IN_SUPPORT		0x00000800 /* bit 11 */
1318c2ecf20Sopenharmony_ci#define   ALI_SPDIF_OUT_CH_ENABLE	0x00008000 /* bit 15 */
1328c2ecf20Sopenharmony_ci#define   ALI_SPDIF_IN_CH_ENABLE	0x00080000 /* bit 19 */
1338c2ecf20Sopenharmony_ci#define   ALI_PCM_IN_ENABLE		0x80000000 /* bit 31 */
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#define ALI_CSO_ALPHA_FMS	0xe0
1368c2ecf20Sopenharmony_ci#define ALI_LBA			0xe4
1378c2ecf20Sopenharmony_ci#define ALI_ESO_DELTA		0xe8
1388c2ecf20Sopenharmony_ci#define ALI_GVSEL_PAN_VOC_CTRL_EC	0xf0
1398c2ecf20Sopenharmony_ci#define ALI_EBUF1		0xf4
1408c2ecf20Sopenharmony_ci#define ALI_EBUF2		0xf8
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci#define ALI_REG(codec, x) ((codec)->port + x)
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci#define MAX_CODECS 2
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistruct snd_ali;
1488c2ecf20Sopenharmony_cistruct snd_ali_voice;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistruct snd_ali_channel_control {
1518c2ecf20Sopenharmony_ci	/* register data */
1528c2ecf20Sopenharmony_ci	struct REGDATA {
1538c2ecf20Sopenharmony_ci		unsigned int start;
1548c2ecf20Sopenharmony_ci		unsigned int stop;
1558c2ecf20Sopenharmony_ci		unsigned int aint;
1568c2ecf20Sopenharmony_ci		unsigned int ainten;
1578c2ecf20Sopenharmony_ci	} data;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* register addresses */
1608c2ecf20Sopenharmony_ci	struct REGS {
1618c2ecf20Sopenharmony_ci		unsigned int start;
1628c2ecf20Sopenharmony_ci		unsigned int stop;
1638c2ecf20Sopenharmony_ci		unsigned int aint;
1648c2ecf20Sopenharmony_ci		unsigned int ainten;
1658c2ecf20Sopenharmony_ci		unsigned int ac97read;
1668c2ecf20Sopenharmony_ci		unsigned int ac97write;
1678c2ecf20Sopenharmony_ci	} regs;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci};
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistruct snd_ali_voice {
1728c2ecf20Sopenharmony_ci	unsigned int number;
1738c2ecf20Sopenharmony_ci	unsigned int use :1,
1748c2ecf20Sopenharmony_ci		pcm :1,
1758c2ecf20Sopenharmony_ci		midi :1,
1768c2ecf20Sopenharmony_ci		mode :1,
1778c2ecf20Sopenharmony_ci		synth :1,
1788c2ecf20Sopenharmony_ci		running :1;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* PCM data */
1818c2ecf20Sopenharmony_ci	struct snd_ali *codec;
1828c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
1838c2ecf20Sopenharmony_ci	struct snd_ali_voice *extra;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	int eso;                /* final ESO value for channel */
1868c2ecf20Sopenharmony_ci	int count;              /* runtime->period_size */
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* --- */
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	void *private_data;
1918c2ecf20Sopenharmony_ci	void (*private_free)(void *private_data);
1928c2ecf20Sopenharmony_ci};
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistruct snd_alidev {
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	struct snd_ali_voice voices[ALI_CHANNELS];
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	unsigned int	chcnt;			/* num of opened channels */
2008c2ecf20Sopenharmony_ci	unsigned int	chmap;			/* bitmap for opened channels */
2018c2ecf20Sopenharmony_ci	unsigned int synthcount;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci};
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci#define ALI_GLOBAL_REGS		56
2078c2ecf20Sopenharmony_ci#define ALI_CHANNEL_REGS	8
2088c2ecf20Sopenharmony_cistruct snd_ali_image {
2098c2ecf20Sopenharmony_ci	u32 regs[ALI_GLOBAL_REGS];
2108c2ecf20Sopenharmony_ci	u32 channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS];
2118c2ecf20Sopenharmony_ci};
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistruct snd_ali {
2158c2ecf20Sopenharmony_ci	int		irq;
2168c2ecf20Sopenharmony_ci	unsigned long	port;
2178c2ecf20Sopenharmony_ci	unsigned char	revision;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	unsigned int hw_initialized :1;
2208c2ecf20Sopenharmony_ci	unsigned int spdif_support :1;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	struct pci_dev	*pci;
2238c2ecf20Sopenharmony_ci	struct pci_dev	*pci_m1533;
2248c2ecf20Sopenharmony_ci	struct pci_dev	*pci_m7101;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	struct snd_card	*card;
2278c2ecf20Sopenharmony_ci	struct snd_pcm	*pcm[MAX_CODECS];
2288c2ecf20Sopenharmony_ci	struct snd_alidev	synth;
2298c2ecf20Sopenharmony_ci	struct snd_ali_channel_control chregs;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	/* S/PDIF Mask */
2328c2ecf20Sopenharmony_ci	unsigned int	spdif_mask;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	unsigned int spurious_irq_count;
2358c2ecf20Sopenharmony_ci	unsigned int spurious_irq_max_delta;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	unsigned int num_of_codecs;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	struct snd_ac97_bus *ac97_bus;
2408c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97[MAX_CODECS];
2418c2ecf20Sopenharmony_ci	unsigned short	ac97_ext_id;
2428c2ecf20Sopenharmony_ci	unsigned short	ac97_ext_status;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	spinlock_t	reg_lock;
2458c2ecf20Sopenharmony_ci	spinlock_t	voice_alloc;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
2488c2ecf20Sopenharmony_ci	struct snd_ali_image *image;
2498c2ecf20Sopenharmony_ci#endif
2508c2ecf20Sopenharmony_ci};
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_ali_ids[] = {
2538c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5451), 0, 0, 0},
2548c2ecf20Sopenharmony_ci	{0, }
2558c2ecf20Sopenharmony_ci};
2568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_ali_ids);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic void snd_ali_clear_voices(struct snd_ali *, unsigned int, unsigned int);
2598c2ecf20Sopenharmony_cistatic unsigned short snd_ali_codec_peek(struct snd_ali *, int, unsigned short);
2608c2ecf20Sopenharmony_cistatic void snd_ali_codec_poke(struct snd_ali *, int, unsigned short,
2618c2ecf20Sopenharmony_ci			       unsigned short);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci/*
2648c2ecf20Sopenharmony_ci *  AC97 ACCESS
2658c2ecf20Sopenharmony_ci */
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic inline unsigned int snd_ali_5451_peek(struct snd_ali *codec,
2688c2ecf20Sopenharmony_ci					     unsigned int port)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	return (unsigned int)inl(ALI_REG(codec, port));
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic inline void snd_ali_5451_poke(struct snd_ali *codec,
2748c2ecf20Sopenharmony_ci				     unsigned int port,
2758c2ecf20Sopenharmony_ci				     unsigned int val)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	outl((unsigned int)val, ALI_REG(codec, port));
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic int snd_ali_codec_ready(struct snd_ali *codec,
2818c2ecf20Sopenharmony_ci			       unsigned int port)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	unsigned long end_time;
2848c2ecf20Sopenharmony_ci	unsigned int res;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	end_time = jiffies + msecs_to_jiffies(250);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	for (;;) {
2898c2ecf20Sopenharmony_ci		res = snd_ali_5451_peek(codec,port);
2908c2ecf20Sopenharmony_ci		if (!(res & 0x8000))
2918c2ecf20Sopenharmony_ci			return 0;
2928c2ecf20Sopenharmony_ci		if (!time_after_eq(end_time, jiffies))
2938c2ecf20Sopenharmony_ci			break;
2948c2ecf20Sopenharmony_ci		schedule_timeout_uninterruptible(1);
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	snd_ali_5451_poke(codec, port, res & ~0x8000);
2988c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "ali_codec_ready: codec is not ready.\n ");
2998c2ecf20Sopenharmony_ci	return -EIO;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic int snd_ali_stimer_ready(struct snd_ali *codec)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	unsigned long end_time;
3058c2ecf20Sopenharmony_ci	unsigned long dwChk1,dwChk2;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER);
3088c2ecf20Sopenharmony_ci	end_time = jiffies + msecs_to_jiffies(250);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	for (;;) {
3118c2ecf20Sopenharmony_ci		dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER);
3128c2ecf20Sopenharmony_ci		if (dwChk2 != dwChk1)
3138c2ecf20Sopenharmony_ci			return 0;
3148c2ecf20Sopenharmony_ci		if (!time_after_eq(end_time, jiffies))
3158c2ecf20Sopenharmony_ci			break;
3168c2ecf20Sopenharmony_ci		schedule_timeout_uninterruptible(1);
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	dev_err(codec->card->dev, "ali_stimer_read: stimer is not ready.\n");
3208c2ecf20Sopenharmony_ci	return -EIO;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic void snd_ali_codec_poke(struct snd_ali *codec,int secondary,
3248c2ecf20Sopenharmony_ci			       unsigned short reg,
3258c2ecf20Sopenharmony_ci			       unsigned short val)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	unsigned int dwVal;
3288c2ecf20Sopenharmony_ci	unsigned int port;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (reg >= 0x80) {
3318c2ecf20Sopenharmony_ci		dev_err(codec->card->dev,
3328c2ecf20Sopenharmony_ci			"ali_codec_poke: reg(%xh) invalid.\n", reg);
3338c2ecf20Sopenharmony_ci		return;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	port = codec->chregs.regs.ac97write;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (snd_ali_codec_ready(codec, port) < 0)
3398c2ecf20Sopenharmony_ci		return;
3408c2ecf20Sopenharmony_ci	if (snd_ali_stimer_ready(codec) < 0)
3418c2ecf20Sopenharmony_ci		return;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	dwVal  = (unsigned int) (reg & 0xff);
3448c2ecf20Sopenharmony_ci	dwVal |= 0x8000 | (val << 16);
3458c2ecf20Sopenharmony_ci	if (secondary)
3468c2ecf20Sopenharmony_ci		dwVal |= 0x0080;
3478c2ecf20Sopenharmony_ci	if (codec->revision == ALI_5451_V02)
3488c2ecf20Sopenharmony_ci		dwVal |= 0x0100;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	snd_ali_5451_poke(codec, port, dwVal);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	return ;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic unsigned short snd_ali_codec_peek(struct snd_ali *codec,
3568c2ecf20Sopenharmony_ci					 int secondary,
3578c2ecf20Sopenharmony_ci					 unsigned short reg)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	unsigned int dwVal;
3608c2ecf20Sopenharmony_ci	unsigned int port;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (reg >= 0x80) {
3638c2ecf20Sopenharmony_ci		dev_err(codec->card->dev,
3648c2ecf20Sopenharmony_ci			"ali_codec_peek: reg(%xh) invalid.\n", reg);
3658c2ecf20Sopenharmony_ci		return ~0;
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	port = codec->chregs.regs.ac97read;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (snd_ali_codec_ready(codec, port) < 0)
3718c2ecf20Sopenharmony_ci		return ~0;
3728c2ecf20Sopenharmony_ci	if (snd_ali_stimer_ready(codec) < 0)
3738c2ecf20Sopenharmony_ci		return ~0;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	dwVal  = (unsigned int) (reg & 0xff);
3768c2ecf20Sopenharmony_ci	dwVal |= 0x8000;				/* bit 15*/
3778c2ecf20Sopenharmony_ci	if (secondary)
3788c2ecf20Sopenharmony_ci		dwVal |= 0x0080;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	snd_ali_5451_poke(codec, port, dwVal);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	if (snd_ali_stimer_ready(codec) < 0)
3838c2ecf20Sopenharmony_ci		return ~0;
3848c2ecf20Sopenharmony_ci	if (snd_ali_codec_ready(codec, port) < 0)
3858c2ecf20Sopenharmony_ci		return ~0;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	return (snd_ali_5451_peek(codec, port) & 0xffff0000) >> 16;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic void snd_ali_codec_write(struct snd_ac97 *ac97,
3918c2ecf20Sopenharmony_ci				unsigned short reg,
3928c2ecf20Sopenharmony_ci				unsigned short val )
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct snd_ali *codec = ac97->private_data;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "codec_write: reg=%xh data=%xh.\n", reg, val);
3978c2ecf20Sopenharmony_ci	if (reg == AC97_GPIO_STATUS) {
3988c2ecf20Sopenharmony_ci		outl((val << ALI_AC97_GPIO_DATA_SHIFT) | ALI_AC97_GPIO_ENABLE,
3998c2ecf20Sopenharmony_ci		     ALI_REG(codec, ALI_AC97_GPIO));
4008c2ecf20Sopenharmony_ci		return;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci	snd_ali_codec_poke(codec, ac97->num, reg, val);
4038c2ecf20Sopenharmony_ci	return ;
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic unsigned short snd_ali_codec_read(struct snd_ac97 *ac97,
4088c2ecf20Sopenharmony_ci					 unsigned short reg)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct snd_ali *codec = ac97->private_data;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "codec_read reg=%xh.\n", reg);
4138c2ecf20Sopenharmony_ci	return snd_ali_codec_peek(codec, ac97->num, reg);
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci/*
4178c2ecf20Sopenharmony_ci *	AC97 Reset
4188c2ecf20Sopenharmony_ci */
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int snd_ali_reset_5451(struct snd_ali *codec)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev;
4238c2ecf20Sopenharmony_ci	unsigned short wCount, wReg;
4248c2ecf20Sopenharmony_ci	unsigned int   dwVal;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	pci_dev = codec->pci_m1533;
4278c2ecf20Sopenharmony_ci	if (pci_dev) {
4288c2ecf20Sopenharmony_ci		pci_read_config_dword(pci_dev, 0x7c, &dwVal);
4298c2ecf20Sopenharmony_ci		pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
4308c2ecf20Sopenharmony_ci		mdelay(5);
4318c2ecf20Sopenharmony_ci		pci_read_config_dword(pci_dev, 0x7c, &dwVal);
4328c2ecf20Sopenharmony_ci		pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
4338c2ecf20Sopenharmony_ci		mdelay(5);
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	pci_dev = codec->pci;
4378c2ecf20Sopenharmony_ci	pci_read_config_dword(pci_dev, 0x44, &dwVal);
4388c2ecf20Sopenharmony_ci	pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000);
4398c2ecf20Sopenharmony_ci	udelay(500);
4408c2ecf20Sopenharmony_ci	pci_read_config_dword(pci_dev, 0x44, &dwVal);
4418c2ecf20Sopenharmony_ci	pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff);
4428c2ecf20Sopenharmony_ci	mdelay(5);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	wCount = 200;
4458c2ecf20Sopenharmony_ci	while(wCount--) {
4468c2ecf20Sopenharmony_ci		wReg = snd_ali_codec_peek(codec, 0, AC97_POWERDOWN);
4478c2ecf20Sopenharmony_ci		if ((wReg & 0x000f) == 0x000f)
4488c2ecf20Sopenharmony_ci			return 0;
4498c2ecf20Sopenharmony_ci		mdelay(5);
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	/* non-fatal if you have a non PM capable codec */
4538c2ecf20Sopenharmony_ci	/* dev_warn(codec->card->dev, "ali5451: reset time out\n"); */
4548c2ecf20Sopenharmony_ci	return 0;
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci/*
4588c2ecf20Sopenharmony_ci *  ALI 5451 Controller
4598c2ecf20Sopenharmony_ci */
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic void snd_ali_enable_special_channel(struct snd_ali *codec,
4628c2ecf20Sopenharmony_ci					   unsigned int channel)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	unsigned long dwVal;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	dwVal  = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
4678c2ecf20Sopenharmony_ci	dwVal |= 1 << (channel & 0x0000001f);
4688c2ecf20Sopenharmony_ci	outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic void snd_ali_disable_special_channel(struct snd_ali *codec,
4728c2ecf20Sopenharmony_ci					    unsigned int channel)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	unsigned long dwVal;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	dwVal  = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
4778c2ecf20Sopenharmony_ci	dwVal &= ~(1 << (channel & 0x0000001f));
4788c2ecf20Sopenharmony_ci	outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic void snd_ali_enable_address_interrupt(struct snd_ali *codec)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	unsigned int gc;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	gc  = inl(ALI_REG(codec, ALI_GC_CIR));
4868c2ecf20Sopenharmony_ci	gc |= ENDLP_IE;
4878c2ecf20Sopenharmony_ci	gc |= MIDLP_IE;
4888c2ecf20Sopenharmony_ci	outl( gc, ALI_REG(codec, ALI_GC_CIR));
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic void snd_ali_disable_address_interrupt(struct snd_ali *codec)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	unsigned int gc;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	gc  = inl(ALI_REG(codec, ALI_GC_CIR));
4968c2ecf20Sopenharmony_ci	gc &= ~ENDLP_IE;
4978c2ecf20Sopenharmony_ci	gc &= ~MIDLP_IE;
4988c2ecf20Sopenharmony_ci	outl(gc, ALI_REG(codec, ALI_GC_CIR));
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic void snd_ali_disable_voice_irq(struct snd_ali *codec,
5028c2ecf20Sopenharmony_ci				      unsigned int channel)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	unsigned int mask;
5058c2ecf20Sopenharmony_ci	struct snd_ali_channel_control *pchregs = &(codec->chregs);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "disable_voice_irq channel=%d\n", channel);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	mask = 1 << (channel & 0x1f);
5108c2ecf20Sopenharmony_ci	pchregs->data.ainten  = inl(ALI_REG(codec, pchregs->regs.ainten));
5118c2ecf20Sopenharmony_ci	pchregs->data.ainten &= ~mask;
5128c2ecf20Sopenharmony_ci	outl(pchregs->data.ainten, ALI_REG(codec, pchregs->regs.ainten));
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cistatic int snd_ali_alloc_pcm_channel(struct snd_ali *codec, int channel)
5168c2ecf20Sopenharmony_ci{
5178c2ecf20Sopenharmony_ci	unsigned int idx =  channel & 0x1f;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	if (codec->synth.chcnt >= ALI_CHANNELS){
5208c2ecf20Sopenharmony_ci		dev_err(codec->card->dev,
5218c2ecf20Sopenharmony_ci			   "ali_alloc_pcm_channel: no free channels.\n");
5228c2ecf20Sopenharmony_ci		return -1;
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if (!(codec->synth.chmap & (1 << idx))) {
5268c2ecf20Sopenharmony_ci		codec->synth.chmap |= 1 << idx;
5278c2ecf20Sopenharmony_ci		codec->synth.chcnt++;
5288c2ecf20Sopenharmony_ci		dev_dbg(codec->card->dev, "alloc_pcm_channel no. %d.\n", idx);
5298c2ecf20Sopenharmony_ci		return idx;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci	return -1;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic int snd_ali_find_free_channel(struct snd_ali * codec, int rec)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	int idx;
5378c2ecf20Sopenharmony_ci	int result = -1;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev,
5408c2ecf20Sopenharmony_ci		"find_free_channel: for %s\n", rec ? "rec" : "pcm");
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	/* recording */
5438c2ecf20Sopenharmony_ci	if (rec) {
5448c2ecf20Sopenharmony_ci		if (codec->spdif_support &&
5458c2ecf20Sopenharmony_ci		    (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) &
5468c2ecf20Sopenharmony_ci		     ALI_SPDIF_IN_SUPPORT))
5478c2ecf20Sopenharmony_ci			idx = ALI_SPDIF_IN_CHANNEL;
5488c2ecf20Sopenharmony_ci		else
5498c2ecf20Sopenharmony_ci			idx = ALI_PCM_IN_CHANNEL;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci		result = snd_ali_alloc_pcm_channel(codec, idx);
5528c2ecf20Sopenharmony_ci		if (result >= 0)
5538c2ecf20Sopenharmony_ci			return result;
5548c2ecf20Sopenharmony_ci		else {
5558c2ecf20Sopenharmony_ci			dev_err(codec->card->dev,
5568c2ecf20Sopenharmony_ci				"ali_find_free_channel: record channel is busy now.\n");
5578c2ecf20Sopenharmony_ci			return -1;
5588c2ecf20Sopenharmony_ci		}
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	/* playback... */
5628c2ecf20Sopenharmony_ci	if (codec->spdif_support &&
5638c2ecf20Sopenharmony_ci	    (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) &
5648c2ecf20Sopenharmony_ci	     ALI_SPDIF_OUT_CH_ENABLE)) {
5658c2ecf20Sopenharmony_ci		idx = ALI_SPDIF_OUT_CHANNEL;
5668c2ecf20Sopenharmony_ci		result = snd_ali_alloc_pcm_channel(codec, idx);
5678c2ecf20Sopenharmony_ci		if (result >= 0)
5688c2ecf20Sopenharmony_ci			return result;
5698c2ecf20Sopenharmony_ci		else
5708c2ecf20Sopenharmony_ci			dev_err(codec->card->dev,
5718c2ecf20Sopenharmony_ci				"ali_find_free_channel: S/PDIF out channel is in busy now.\n");
5728c2ecf20Sopenharmony_ci	}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	for (idx = 0; idx < ALI_CHANNELS; idx++) {
5758c2ecf20Sopenharmony_ci		result = snd_ali_alloc_pcm_channel(codec, idx);
5768c2ecf20Sopenharmony_ci		if (result >= 0)
5778c2ecf20Sopenharmony_ci			return result;
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ci	dev_err(codec->card->dev, "ali_find_free_channel: no free channels.\n");
5808c2ecf20Sopenharmony_ci	return -1;
5818c2ecf20Sopenharmony_ci}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_cistatic void snd_ali_free_channel_pcm(struct snd_ali *codec, int channel)
5848c2ecf20Sopenharmony_ci{
5858c2ecf20Sopenharmony_ci	unsigned int idx = channel & 0x0000001f;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "free_channel_pcm channel=%d\n", channel);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	if (channel < 0 || channel >= ALI_CHANNELS)
5908c2ecf20Sopenharmony_ci		return;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	if (!(codec->synth.chmap & (1 << idx))) {
5938c2ecf20Sopenharmony_ci		dev_err(codec->card->dev,
5948c2ecf20Sopenharmony_ci			"ali_free_channel_pcm: channel %d is not in use.\n",
5958c2ecf20Sopenharmony_ci			channel);
5968c2ecf20Sopenharmony_ci		return;
5978c2ecf20Sopenharmony_ci	} else {
5988c2ecf20Sopenharmony_ci		codec->synth.chmap &= ~(1 << idx);
5998c2ecf20Sopenharmony_ci		codec->synth.chcnt--;
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_cistatic void snd_ali_stop_voice(struct snd_ali *codec, unsigned int channel)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	unsigned int mask = 1 << (channel & 0x1f);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "stop_voice: channel=%d\n", channel);
6088c2ecf20Sopenharmony_ci	outl(mask, ALI_REG(codec, codec->chregs.regs.stop));
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci/*
6128c2ecf20Sopenharmony_ci *    S/PDIF Part
6138c2ecf20Sopenharmony_ci */
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic void snd_ali_delay(struct snd_ali *codec,int interval)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	unsigned long  begintimer,currenttimer;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	begintimer   = inl(ALI_REG(codec, ALI_STIMER));
6208c2ecf20Sopenharmony_ci	currenttimer = inl(ALI_REG(codec, ALI_STIMER));
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	while (currenttimer < begintimer + interval) {
6238c2ecf20Sopenharmony_ci		if (snd_ali_stimer_ready(codec) < 0)
6248c2ecf20Sopenharmony_ci			break;
6258c2ecf20Sopenharmony_ci		currenttimer = inl(ALI_REG(codec,  ALI_STIMER));
6268c2ecf20Sopenharmony_ci		cpu_relax();
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic void snd_ali_detect_spdif_rate(struct snd_ali *codec)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	u16 wval;
6338c2ecf20Sopenharmony_ci	u16 count = 0;
6348c2ecf20Sopenharmony_ci	u8  bval, R1 = 0, R2;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	bval  = inb(ALI_REG(codec, ALI_SPDIF_CTRL + 1));
6378c2ecf20Sopenharmony_ci	bval |= 0x1F;
6388c2ecf20Sopenharmony_ci	outb(bval, ALI_REG(codec, ALI_SPDIF_CTRL + 1));
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	while ((R1 < 0x0b || R1 > 0x0e) && R1 != 0x12 && count <= 50000) {
6418c2ecf20Sopenharmony_ci		count ++;
6428c2ecf20Sopenharmony_ci		snd_ali_delay(codec, 6);
6438c2ecf20Sopenharmony_ci		bval = inb(ALI_REG(codec, ALI_SPDIF_CTRL + 1));
6448c2ecf20Sopenharmony_ci		R1 = bval & 0x1F;
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	if (count > 50000) {
6488c2ecf20Sopenharmony_ci		dev_err(codec->card->dev, "ali_detect_spdif_rate: timeout!\n");
6498c2ecf20Sopenharmony_ci		return;
6508c2ecf20Sopenharmony_ci	}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	for (count = 0; count <= 50000; count++) {
6538c2ecf20Sopenharmony_ci		snd_ali_delay(codec, 6);
6548c2ecf20Sopenharmony_ci		bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
6558c2ecf20Sopenharmony_ci		R2 = bval & 0x1F;
6568c2ecf20Sopenharmony_ci		if (R2 != R1)
6578c2ecf20Sopenharmony_ci			R1 = R2;
6588c2ecf20Sopenharmony_ci		else
6598c2ecf20Sopenharmony_ci			break;
6608c2ecf20Sopenharmony_ci	}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	if (count > 50000) {
6638c2ecf20Sopenharmony_ci		dev_err(codec->card->dev, "ali_detect_spdif_rate: timeout!\n");
6648c2ecf20Sopenharmony_ci		return;
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	if (R2 >= 0x0b && R2 <= 0x0e) {
6688c2ecf20Sopenharmony_ci		wval  = inw(ALI_REG(codec, ALI_SPDIF_CTRL + 2));
6698c2ecf20Sopenharmony_ci		wval &= 0xe0f0;
6708c2ecf20Sopenharmony_ci		wval |= (0x09 << 8) | 0x05;
6718c2ecf20Sopenharmony_ci		outw(wval, ALI_REG(codec, ALI_SPDIF_CTRL + 2));
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci		bval  = inb(ALI_REG(codec, ALI_SPDIF_CS + 3)) & 0xf0;
6748c2ecf20Sopenharmony_ci		outb(bval | 0x02, ALI_REG(codec, ALI_SPDIF_CS + 3));
6758c2ecf20Sopenharmony_ci	} else if (R2 == 0x12) {
6768c2ecf20Sopenharmony_ci		wval  = inw(ALI_REG(codec, ALI_SPDIF_CTRL + 2));
6778c2ecf20Sopenharmony_ci		wval &= 0xe0f0;
6788c2ecf20Sopenharmony_ci		wval |= (0x0e << 8) | 0x08;
6798c2ecf20Sopenharmony_ci		outw(wval, ALI_REG(codec, ALI_SPDIF_CTRL + 2));
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci		bval  = inb(ALI_REG(codec,ALI_SPDIF_CS + 3)) & 0xf0;
6828c2ecf20Sopenharmony_ci		outb(bval | 0x03, ALI_REG(codec, ALI_SPDIF_CS + 3));
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic unsigned int snd_ali_get_spdif_in_rate(struct snd_ali *codec)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	u32	dwRate;
6898c2ecf20Sopenharmony_ci	u8	bval;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	bval  = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
6928c2ecf20Sopenharmony_ci	bval &= 0x7f;
6938c2ecf20Sopenharmony_ci	bval |= 0x40;
6948c2ecf20Sopenharmony_ci	outb(bval, ALI_REG(codec, ALI_SPDIF_CTRL));
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	snd_ali_detect_spdif_rate(codec);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	bval  = inb(ALI_REG(codec, ALI_SPDIF_CS + 3));
6998c2ecf20Sopenharmony_ci	bval &= 0x0f;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	switch (bval) {
7028c2ecf20Sopenharmony_ci	case 0: dwRate = 44100; break;
7038c2ecf20Sopenharmony_ci	case 1: dwRate = 48000; break;
7048c2ecf20Sopenharmony_ci	case 2: dwRate = 32000; break;
7058c2ecf20Sopenharmony_ci	default: dwRate = 0; break;
7068c2ecf20Sopenharmony_ci	}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	return dwRate;
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cistatic void snd_ali_enable_spdif_in(struct snd_ali *codec)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	unsigned int dwVal;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
7168c2ecf20Sopenharmony_ci	dwVal |= ALI_SPDIF_IN_SUPPORT;
7178c2ecf20Sopenharmony_ci	outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	dwVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
7208c2ecf20Sopenharmony_ci	dwVal |= 0x02;
7218c2ecf20Sopenharmony_ci	outb(dwVal, ALI_REG(codec, ALI_SPDIF_CTRL));
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	snd_ali_enable_special_channel(codec, ALI_SPDIF_IN_CHANNEL);
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_cistatic void snd_ali_disable_spdif_in(struct snd_ali *codec)
7278c2ecf20Sopenharmony_ci{
7288c2ecf20Sopenharmony_ci	unsigned int dwVal;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
7318c2ecf20Sopenharmony_ci	dwVal &= ~ALI_SPDIF_IN_SUPPORT;
7328c2ecf20Sopenharmony_ci	outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	snd_ali_disable_special_channel(codec, ALI_SPDIF_IN_CHANNEL);
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_cistatic void snd_ali_set_spdif_out_rate(struct snd_ali *codec, unsigned int rate)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	unsigned char  bVal;
7418c2ecf20Sopenharmony_ci	unsigned int  dwRate;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	switch (rate) {
7448c2ecf20Sopenharmony_ci	case 32000: dwRate = 0x300; break;
7458c2ecf20Sopenharmony_ci	case 48000: dwRate = 0x200; break;
7468c2ecf20Sopenharmony_ci	default: dwRate = 0; break;
7478c2ecf20Sopenharmony_ci	}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	bVal  = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
7508c2ecf20Sopenharmony_ci	bVal &= (unsigned char)(~(1<<6));
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	bVal |= 0x80;		/* select right */
7538c2ecf20Sopenharmony_ci	outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL));
7548c2ecf20Sopenharmony_ci	outb(dwRate | 0x20, ALI_REG(codec, ALI_SPDIF_CS + 2));
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	bVal &= ~0x80;	/* select left */
7578c2ecf20Sopenharmony_ci	outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL));
7588c2ecf20Sopenharmony_ci	outw(rate | 0x10, ALI_REG(codec, ALI_SPDIF_CS + 2));
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cistatic void snd_ali_enable_spdif_out(struct snd_ali *codec)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	unsigned short wVal;
7648c2ecf20Sopenharmony_ci	unsigned char bVal;
7658c2ecf20Sopenharmony_ci        struct pci_dev *pci_dev;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci        pci_dev = codec->pci_m1533;
7688c2ecf20Sopenharmony_ci        if (pci_dev == NULL)
7698c2ecf20Sopenharmony_ci                return;
7708c2ecf20Sopenharmony_ci        pci_read_config_byte(pci_dev, 0x61, &bVal);
7718c2ecf20Sopenharmony_ci        bVal |= 0x40;
7728c2ecf20Sopenharmony_ci        pci_write_config_byte(pci_dev, 0x61, bVal);
7738c2ecf20Sopenharmony_ci        pci_read_config_byte(pci_dev, 0x7d, &bVal);
7748c2ecf20Sopenharmony_ci        bVal |= 0x01;
7758c2ecf20Sopenharmony_ci        pci_write_config_byte(pci_dev, 0x7d, bVal);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci        pci_read_config_byte(pci_dev, 0x7e, &bVal);
7788c2ecf20Sopenharmony_ci        bVal &= (~0x20);
7798c2ecf20Sopenharmony_ci        bVal |= 0x10;
7808c2ecf20Sopenharmony_ci        pci_write_config_byte(pci_dev, 0x7e, bVal);
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	bVal = inb(ALI_REG(codec, ALI_SCTRL));
7838c2ecf20Sopenharmony_ci	outb(bVal | ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL));
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
7868c2ecf20Sopenharmony_ci	outb(bVal & ALI_SPDIF_OUT_CH_STATUS, ALI_REG(codec, ALI_SPDIF_CTRL));
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
7898c2ecf20Sopenharmony_ci	wVal |= ALI_SPDIF_OUT_SEL_PCM;
7908c2ecf20Sopenharmony_ci	outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
7918c2ecf20Sopenharmony_ci	snd_ali_disable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL);
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cistatic void snd_ali_enable_spdif_chnout(struct snd_ali *codec)
7958c2ecf20Sopenharmony_ci{
7968c2ecf20Sopenharmony_ci	unsigned short wVal;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	wVal  = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
7998c2ecf20Sopenharmony_ci   	wVal &= ~ALI_SPDIF_OUT_SEL_PCM;
8008c2ecf20Sopenharmony_ci   	outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
8018c2ecf20Sopenharmony_ci/*
8028c2ecf20Sopenharmony_ci	wVal = inw(ALI_REG(codec, ALI_SPDIF_CS));
8038c2ecf20Sopenharmony_ci	if (flag & ALI_SPDIF_OUT_NON_PCM)
8048c2ecf20Sopenharmony_ci   		wVal |= 0x0002;
8058c2ecf20Sopenharmony_ci	else
8068c2ecf20Sopenharmony_ci		wVal &= (~0x0002);
8078c2ecf20Sopenharmony_ci   	outw(wVal, ALI_REG(codec, ALI_SPDIF_CS));
8088c2ecf20Sopenharmony_ci*/
8098c2ecf20Sopenharmony_ci	snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL);
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_cistatic void snd_ali_disable_spdif_chnout(struct snd_ali *codec)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	unsigned short wVal;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci  	wVal  = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
8178c2ecf20Sopenharmony_ci   	wVal |= ALI_SPDIF_OUT_SEL_PCM;
8188c2ecf20Sopenharmony_ci   	outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL);
8218c2ecf20Sopenharmony_ci}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_cistatic void snd_ali_disable_spdif_out(struct snd_ali *codec)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	unsigned char  bVal;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	bVal = inb(ALI_REG(codec, ALI_SCTRL));
8288c2ecf20Sopenharmony_ci	outb(bVal & ~ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL));
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	snd_ali_disable_spdif_chnout(codec);
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_cistatic void snd_ali_update_ptr(struct snd_ali *codec, int channel)
8348c2ecf20Sopenharmony_ci{
8358c2ecf20Sopenharmony_ci	struct snd_ali_voice *pvoice;
8368c2ecf20Sopenharmony_ci	struct snd_ali_channel_control *pchregs;
8378c2ecf20Sopenharmony_ci	unsigned int old, mask;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	pchregs = &(codec->chregs);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	/* check if interrupt occurred for channel */
8428c2ecf20Sopenharmony_ci	old  = pchregs->data.aint;
8438c2ecf20Sopenharmony_ci	mask = 1U << (channel & 0x1f);
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	if (!(old & mask))
8468c2ecf20Sopenharmony_ci		return;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	pvoice = &codec->synth.voices[channel];
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	udelay(100);
8518c2ecf20Sopenharmony_ci	spin_lock(&codec->reg_lock);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	if (pvoice->pcm && pvoice->substream) {
8548c2ecf20Sopenharmony_ci		/* pcm interrupt */
8558c2ecf20Sopenharmony_ci		if (pvoice->running) {
8568c2ecf20Sopenharmony_ci			dev_dbg(codec->card->dev,
8578c2ecf20Sopenharmony_ci				"update_ptr: cso=%4.4x cspf=%d.\n",
8588c2ecf20Sopenharmony_ci				inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)),
8598c2ecf20Sopenharmony_ci				(inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask);
8608c2ecf20Sopenharmony_ci			spin_unlock(&codec->reg_lock);
8618c2ecf20Sopenharmony_ci			snd_pcm_period_elapsed(pvoice->substream);
8628c2ecf20Sopenharmony_ci			spin_lock(&codec->reg_lock);
8638c2ecf20Sopenharmony_ci		} else {
8648c2ecf20Sopenharmony_ci			snd_ali_stop_voice(codec, channel);
8658c2ecf20Sopenharmony_ci			snd_ali_disable_voice_irq(codec, channel);
8668c2ecf20Sopenharmony_ci		}
8678c2ecf20Sopenharmony_ci	} else if (codec->synth.voices[channel].synth) {
8688c2ecf20Sopenharmony_ci		/* synth interrupt */
8698c2ecf20Sopenharmony_ci	} else if (codec->synth.voices[channel].midi) {
8708c2ecf20Sopenharmony_ci		/* midi interrupt */
8718c2ecf20Sopenharmony_ci	} else {
8728c2ecf20Sopenharmony_ci		/* unknown interrupt */
8738c2ecf20Sopenharmony_ci		snd_ali_stop_voice(codec, channel);
8748c2ecf20Sopenharmony_ci		snd_ali_disable_voice_irq(codec, channel);
8758c2ecf20Sopenharmony_ci	}
8768c2ecf20Sopenharmony_ci	spin_unlock(&codec->reg_lock);
8778c2ecf20Sopenharmony_ci	outl(mask,ALI_REG(codec,pchregs->regs.aint));
8788c2ecf20Sopenharmony_ci	pchregs->data.aint = old & (~mask);
8798c2ecf20Sopenharmony_ci}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cistatic irqreturn_t snd_ali_card_interrupt(int irq, void *dev_id)
8828c2ecf20Sopenharmony_ci{
8838c2ecf20Sopenharmony_ci	struct snd_ali 	*codec = dev_id;
8848c2ecf20Sopenharmony_ci	int channel;
8858c2ecf20Sopenharmony_ci	unsigned int audio_int;
8868c2ecf20Sopenharmony_ci	struct snd_ali_channel_control *pchregs;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	if (codec == NULL || !codec->hw_initialized)
8898c2ecf20Sopenharmony_ci		return IRQ_NONE;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	audio_int = inl(ALI_REG(codec, ALI_MISCINT));
8928c2ecf20Sopenharmony_ci	if (!audio_int)
8938c2ecf20Sopenharmony_ci		return IRQ_NONE;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	pchregs = &(codec->chregs);
8968c2ecf20Sopenharmony_ci	if (audio_int & ADDRESS_IRQ) {
8978c2ecf20Sopenharmony_ci		/* get interrupt status for all channels */
8988c2ecf20Sopenharmony_ci		pchregs->data.aint = inl(ALI_REG(codec, pchregs->regs.aint));
8998c2ecf20Sopenharmony_ci		for (channel = 0; channel < ALI_CHANNELS; channel++)
9008c2ecf20Sopenharmony_ci			snd_ali_update_ptr(codec, channel);
9018c2ecf20Sopenharmony_ci	}
9028c2ecf20Sopenharmony_ci	outl((TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW),
9038c2ecf20Sopenharmony_ci		ALI_REG(codec, ALI_MISCINT));
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_cistatic struct snd_ali_voice *snd_ali_alloc_voice(struct snd_ali * codec,
9108c2ecf20Sopenharmony_ci						 int type, int rec, int channel)
9118c2ecf20Sopenharmony_ci{
9128c2ecf20Sopenharmony_ci	struct snd_ali_voice *pvoice;
9138c2ecf20Sopenharmony_ci	int idx;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "alloc_voice: type=%d rec=%d\n", type, rec);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	spin_lock_irq(&codec->voice_alloc);
9188c2ecf20Sopenharmony_ci	if (type == SNDRV_ALI_VOICE_TYPE_PCM) {
9198c2ecf20Sopenharmony_ci		idx = channel > 0 ? snd_ali_alloc_pcm_channel(codec, channel) :
9208c2ecf20Sopenharmony_ci			snd_ali_find_free_channel(codec,rec);
9218c2ecf20Sopenharmony_ci		if (idx < 0) {
9228c2ecf20Sopenharmony_ci			dev_err(codec->card->dev, "ali_alloc_voice: err.\n");
9238c2ecf20Sopenharmony_ci			spin_unlock_irq(&codec->voice_alloc);
9248c2ecf20Sopenharmony_ci			return NULL;
9258c2ecf20Sopenharmony_ci		}
9268c2ecf20Sopenharmony_ci		pvoice = &(codec->synth.voices[idx]);
9278c2ecf20Sopenharmony_ci		pvoice->codec = codec;
9288c2ecf20Sopenharmony_ci		pvoice->use = 1;
9298c2ecf20Sopenharmony_ci		pvoice->pcm = 1;
9308c2ecf20Sopenharmony_ci		pvoice->mode = rec;
9318c2ecf20Sopenharmony_ci		spin_unlock_irq(&codec->voice_alloc);
9328c2ecf20Sopenharmony_ci		return pvoice;
9338c2ecf20Sopenharmony_ci	}
9348c2ecf20Sopenharmony_ci	spin_unlock_irq(&codec->voice_alloc);
9358c2ecf20Sopenharmony_ci	return NULL;
9368c2ecf20Sopenharmony_ci}
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_cistatic void snd_ali_free_voice(struct snd_ali * codec,
9408c2ecf20Sopenharmony_ci			       struct snd_ali_voice *pvoice)
9418c2ecf20Sopenharmony_ci{
9428c2ecf20Sopenharmony_ci	void (*private_free)(void *);
9438c2ecf20Sopenharmony_ci	void *private_data;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "free_voice: channel=%d\n", pvoice->number);
9468c2ecf20Sopenharmony_ci	if (!pvoice->use)
9478c2ecf20Sopenharmony_ci		return;
9488c2ecf20Sopenharmony_ci	snd_ali_clear_voices(codec, pvoice->number, pvoice->number);
9498c2ecf20Sopenharmony_ci	spin_lock_irq(&codec->voice_alloc);
9508c2ecf20Sopenharmony_ci	private_free = pvoice->private_free;
9518c2ecf20Sopenharmony_ci	private_data = pvoice->private_data;
9528c2ecf20Sopenharmony_ci	pvoice->private_free = NULL;
9538c2ecf20Sopenharmony_ci	pvoice->private_data = NULL;
9548c2ecf20Sopenharmony_ci	if (pvoice->pcm)
9558c2ecf20Sopenharmony_ci		snd_ali_free_channel_pcm(codec, pvoice->number);
9568c2ecf20Sopenharmony_ci	pvoice->use = pvoice->pcm = pvoice->synth = 0;
9578c2ecf20Sopenharmony_ci	pvoice->substream = NULL;
9588c2ecf20Sopenharmony_ci	spin_unlock_irq(&codec->voice_alloc);
9598c2ecf20Sopenharmony_ci	if (private_free)
9608c2ecf20Sopenharmony_ci		private_free(private_data);
9618c2ecf20Sopenharmony_ci}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_cistatic void snd_ali_clear_voices(struct snd_ali *codec,
9658c2ecf20Sopenharmony_ci				 unsigned int v_min,
9668c2ecf20Sopenharmony_ci				 unsigned int v_max)
9678c2ecf20Sopenharmony_ci{
9688c2ecf20Sopenharmony_ci	unsigned int i;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	for (i = v_min; i <= v_max; i++) {
9718c2ecf20Sopenharmony_ci		snd_ali_stop_voice(codec, i);
9728c2ecf20Sopenharmony_ci		snd_ali_disable_voice_irq(codec, i);
9738c2ecf20Sopenharmony_ci	}
9748c2ecf20Sopenharmony_ci}
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_cistatic void snd_ali_write_voice_regs(struct snd_ali *codec,
9778c2ecf20Sopenharmony_ci			 unsigned int Channel,
9788c2ecf20Sopenharmony_ci			 unsigned int LBA,
9798c2ecf20Sopenharmony_ci			 unsigned int CSO,
9808c2ecf20Sopenharmony_ci			 unsigned int ESO,
9818c2ecf20Sopenharmony_ci			 unsigned int DELTA,
9828c2ecf20Sopenharmony_ci			 unsigned int ALPHA_FMS,
9838c2ecf20Sopenharmony_ci			 unsigned int GVSEL,
9848c2ecf20Sopenharmony_ci			 unsigned int PAN,
9858c2ecf20Sopenharmony_ci			 unsigned int VOL,
9868c2ecf20Sopenharmony_ci			 unsigned int CTRL,
9878c2ecf20Sopenharmony_ci			 unsigned int EC)
9888c2ecf20Sopenharmony_ci{
9898c2ecf20Sopenharmony_ci	unsigned int ctlcmds[4];
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	outb((unsigned char)(Channel & 0x001f), ALI_REG(codec, ALI_GC_CIR));
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	ctlcmds[0] =  (CSO << 16) | (ALPHA_FMS & 0x0000ffff);
9948c2ecf20Sopenharmony_ci	ctlcmds[1] =  LBA;
9958c2ecf20Sopenharmony_ci	ctlcmds[2] =  (ESO << 16) | (DELTA & 0x0ffff);
9968c2ecf20Sopenharmony_ci	ctlcmds[3] =  (GVSEL << 31) |
9978c2ecf20Sopenharmony_ci		      ((PAN & 0x0000007f) << 24) |
9988c2ecf20Sopenharmony_ci		      ((VOL & 0x000000ff) << 16) |
9998c2ecf20Sopenharmony_ci		      ((CTRL & 0x0000000f) << 12) |
10008c2ecf20Sopenharmony_ci		      (EC & 0x00000fff);
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	outb(Channel, ALI_REG(codec, ALI_GC_CIR));
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	outl(ctlcmds[0], ALI_REG(codec, ALI_CSO_ALPHA_FMS));
10058c2ecf20Sopenharmony_ci	outl(ctlcmds[1], ALI_REG(codec, ALI_LBA));
10068c2ecf20Sopenharmony_ci	outl(ctlcmds[2], ALI_REG(codec, ALI_ESO_DELTA));
10078c2ecf20Sopenharmony_ci	outl(ctlcmds[3], ALI_REG(codec, ALI_GVSEL_PAN_VOC_CTRL_EC));
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	outl(0x30000000, ALI_REG(codec, ALI_EBUF1));	/* Still Mode */
10108c2ecf20Sopenharmony_ci	outl(0x30000000, ALI_REG(codec, ALI_EBUF2));	/* Still Mode */
10118c2ecf20Sopenharmony_ci}
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_cistatic unsigned int snd_ali_convert_rate(unsigned int rate, int rec)
10148c2ecf20Sopenharmony_ci{
10158c2ecf20Sopenharmony_ci	unsigned int delta;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	if (rate < 4000)
10188c2ecf20Sopenharmony_ci		rate = 4000;
10198c2ecf20Sopenharmony_ci	if (rate > 48000)
10208c2ecf20Sopenharmony_ci		rate = 48000;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	if (rec) {
10238c2ecf20Sopenharmony_ci		if (rate == 44100)
10248c2ecf20Sopenharmony_ci			delta = 0x116a;
10258c2ecf20Sopenharmony_ci		else if (rate == 8000)
10268c2ecf20Sopenharmony_ci			delta = 0x6000;
10278c2ecf20Sopenharmony_ci		else if (rate == 48000)
10288c2ecf20Sopenharmony_ci			delta = 0x1000;
10298c2ecf20Sopenharmony_ci		else
10308c2ecf20Sopenharmony_ci			delta = ((48000 << 12) / rate) & 0x0000ffff;
10318c2ecf20Sopenharmony_ci	} else {
10328c2ecf20Sopenharmony_ci		if (rate == 44100)
10338c2ecf20Sopenharmony_ci			delta = 0xeb3;
10348c2ecf20Sopenharmony_ci		else if (rate == 8000)
10358c2ecf20Sopenharmony_ci			delta = 0x2ab;
10368c2ecf20Sopenharmony_ci		else if (rate == 48000)
10378c2ecf20Sopenharmony_ci			delta = 0x1000;
10388c2ecf20Sopenharmony_ci		else
10398c2ecf20Sopenharmony_ci			delta = (((rate << 12) + rate) / 48000) & 0x0000ffff;
10408c2ecf20Sopenharmony_ci	}
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	return delta;
10438c2ecf20Sopenharmony_ci}
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_cistatic unsigned int snd_ali_control_mode(struct snd_pcm_substream *substream)
10468c2ecf20Sopenharmony_ci{
10478c2ecf20Sopenharmony_ci	unsigned int CTRL;
10488c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	/* set ctrl mode
10518c2ecf20Sopenharmony_ci	   CTRL default: 8-bit (unsigned) mono, loop mode enabled
10528c2ecf20Sopenharmony_ci	 */
10538c2ecf20Sopenharmony_ci	CTRL = 0x00000001;
10548c2ecf20Sopenharmony_ci	if (snd_pcm_format_width(runtime->format) == 16)
10558c2ecf20Sopenharmony_ci		CTRL |= 0x00000008;	/* 16-bit data */
10568c2ecf20Sopenharmony_ci	if (!snd_pcm_format_unsigned(runtime->format))
10578c2ecf20Sopenharmony_ci		CTRL |= 0x00000002;	/* signed data */
10588c2ecf20Sopenharmony_ci	if (runtime->channels > 1)
10598c2ecf20Sopenharmony_ci		CTRL |= 0x00000004;	/* stereo data */
10608c2ecf20Sopenharmony_ci	return CTRL;
10618c2ecf20Sopenharmony_ci}
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci/*
10648c2ecf20Sopenharmony_ci *  PCM part
10658c2ecf20Sopenharmony_ci */
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_cistatic int snd_ali_trigger(struct snd_pcm_substream *substream,
10688c2ecf20Sopenharmony_ci			       int cmd)
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci{
10718c2ecf20Sopenharmony_ci	struct snd_ali *codec = snd_pcm_substream_chip(substream);
10728c2ecf20Sopenharmony_ci	struct snd_pcm_substream *s;
10738c2ecf20Sopenharmony_ci	unsigned int what, whati;
10748c2ecf20Sopenharmony_ci	struct snd_ali_voice *pvoice, *evoice;
10758c2ecf20Sopenharmony_ci	unsigned int val;
10768c2ecf20Sopenharmony_ci	int do_start;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	switch (cmd) {
10798c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
10808c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
10818c2ecf20Sopenharmony_ci		do_start = 1;
10828c2ecf20Sopenharmony_ci		break;
10838c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
10848c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
10858c2ecf20Sopenharmony_ci		do_start = 0;
10868c2ecf20Sopenharmony_ci		break;
10878c2ecf20Sopenharmony_ci	default:
10888c2ecf20Sopenharmony_ci		return -EINVAL;
10898c2ecf20Sopenharmony_ci	}
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	what = whati = 0;
10928c2ecf20Sopenharmony_ci	snd_pcm_group_for_each_entry(s, substream) {
10938c2ecf20Sopenharmony_ci		if ((struct snd_ali *) snd_pcm_substream_chip(s) == codec) {
10948c2ecf20Sopenharmony_ci			pvoice = s->runtime->private_data;
10958c2ecf20Sopenharmony_ci			evoice = pvoice->extra;
10968c2ecf20Sopenharmony_ci			what |= 1 << (pvoice->number & 0x1f);
10978c2ecf20Sopenharmony_ci			if (evoice == NULL)
10988c2ecf20Sopenharmony_ci				whati |= 1 << (pvoice->number & 0x1f);
10998c2ecf20Sopenharmony_ci			else {
11008c2ecf20Sopenharmony_ci				whati |= 1 << (evoice->number & 0x1f);
11018c2ecf20Sopenharmony_ci				what |= 1 << (evoice->number & 0x1f);
11028c2ecf20Sopenharmony_ci			}
11038c2ecf20Sopenharmony_ci			if (do_start) {
11048c2ecf20Sopenharmony_ci				pvoice->running = 1;
11058c2ecf20Sopenharmony_ci				if (evoice != NULL)
11068c2ecf20Sopenharmony_ci					evoice->running = 1;
11078c2ecf20Sopenharmony_ci			} else {
11088c2ecf20Sopenharmony_ci				pvoice->running = 0;
11098c2ecf20Sopenharmony_ci				if (evoice != NULL)
11108c2ecf20Sopenharmony_ci					evoice->running = 0;
11118c2ecf20Sopenharmony_ci			}
11128c2ecf20Sopenharmony_ci			snd_pcm_trigger_done(s, substream);
11138c2ecf20Sopenharmony_ci		}
11148c2ecf20Sopenharmony_ci	}
11158c2ecf20Sopenharmony_ci	spin_lock(&codec->reg_lock);
11168c2ecf20Sopenharmony_ci	if (!do_start)
11178c2ecf20Sopenharmony_ci		outl(what, ALI_REG(codec, ALI_STOP));
11188c2ecf20Sopenharmony_ci	val = inl(ALI_REG(codec, ALI_AINTEN));
11198c2ecf20Sopenharmony_ci	if (do_start)
11208c2ecf20Sopenharmony_ci		val |= whati;
11218c2ecf20Sopenharmony_ci	else
11228c2ecf20Sopenharmony_ci		val &= ~whati;
11238c2ecf20Sopenharmony_ci	outl(val, ALI_REG(codec, ALI_AINTEN));
11248c2ecf20Sopenharmony_ci	if (do_start)
11258c2ecf20Sopenharmony_ci		outl(what, ALI_REG(codec, ALI_START));
11268c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "trigger: what=%xh whati=%xh\n", what, whati);
11278c2ecf20Sopenharmony_ci	spin_unlock(&codec->reg_lock);
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	return 0;
11308c2ecf20Sopenharmony_ci}
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_cistatic int snd_ali_playback_hw_params(struct snd_pcm_substream *substream,
11338c2ecf20Sopenharmony_ci				      struct snd_pcm_hw_params *hw_params)
11348c2ecf20Sopenharmony_ci{
11358c2ecf20Sopenharmony_ci	struct snd_ali *codec = snd_pcm_substream_chip(substream);
11368c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
11378c2ecf20Sopenharmony_ci	struct snd_ali_voice *pvoice = runtime->private_data;
11388c2ecf20Sopenharmony_ci	struct snd_ali_voice *evoice = pvoice->extra;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	/* voice management */
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	if (params_buffer_size(hw_params) / 2 !=
11438c2ecf20Sopenharmony_ci	    params_period_size(hw_params)) {
11448c2ecf20Sopenharmony_ci		if (!evoice) {
11458c2ecf20Sopenharmony_ci			evoice = snd_ali_alloc_voice(codec,
11468c2ecf20Sopenharmony_ci						     SNDRV_ALI_VOICE_TYPE_PCM,
11478c2ecf20Sopenharmony_ci						     0, -1);
11488c2ecf20Sopenharmony_ci			if (!evoice)
11498c2ecf20Sopenharmony_ci				return -ENOMEM;
11508c2ecf20Sopenharmony_ci			pvoice->extra = evoice;
11518c2ecf20Sopenharmony_ci			evoice->substream = substream;
11528c2ecf20Sopenharmony_ci		}
11538c2ecf20Sopenharmony_ci	} else {
11548c2ecf20Sopenharmony_ci		if (evoice) {
11558c2ecf20Sopenharmony_ci			snd_ali_free_voice(codec, evoice);
11568c2ecf20Sopenharmony_ci			pvoice->extra = evoice = NULL;
11578c2ecf20Sopenharmony_ci		}
11588c2ecf20Sopenharmony_ci	}
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	return 0;
11618c2ecf20Sopenharmony_ci}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_cistatic int snd_ali_playback_hw_free(struct snd_pcm_substream *substream)
11648c2ecf20Sopenharmony_ci{
11658c2ecf20Sopenharmony_ci	struct snd_ali *codec = snd_pcm_substream_chip(substream);
11668c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
11678c2ecf20Sopenharmony_ci	struct snd_ali_voice *pvoice = runtime->private_data;
11688c2ecf20Sopenharmony_ci	struct snd_ali_voice *evoice = pvoice ? pvoice->extra : NULL;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	if (evoice) {
11718c2ecf20Sopenharmony_ci		snd_ali_free_voice(codec, evoice);
11728c2ecf20Sopenharmony_ci		pvoice->extra = NULL;
11738c2ecf20Sopenharmony_ci	}
11748c2ecf20Sopenharmony_ci	return 0;
11758c2ecf20Sopenharmony_ci}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_cistatic int snd_ali_playback_prepare(struct snd_pcm_substream *substream)
11788c2ecf20Sopenharmony_ci{
11798c2ecf20Sopenharmony_ci	struct snd_ali *codec = snd_pcm_substream_chip(substream);
11808c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
11818c2ecf20Sopenharmony_ci	struct snd_ali_voice *pvoice = runtime->private_data;
11828c2ecf20Sopenharmony_ci	struct snd_ali_voice *evoice = pvoice->extra;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	unsigned int LBA;
11858c2ecf20Sopenharmony_ci	unsigned int Delta;
11868c2ecf20Sopenharmony_ci	unsigned int ESO;
11878c2ecf20Sopenharmony_ci	unsigned int CTRL;
11888c2ecf20Sopenharmony_ci	unsigned int GVSEL;
11898c2ecf20Sopenharmony_ci	unsigned int PAN;
11908c2ecf20Sopenharmony_ci	unsigned int VOL;
11918c2ecf20Sopenharmony_ci	unsigned int EC;
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "playback_prepare ...\n");
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	spin_lock_irq(&codec->reg_lock);
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	/* set Delta (rate) value */
11988c2ecf20Sopenharmony_ci	Delta = snd_ali_convert_rate(runtime->rate, 0);
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	if (pvoice->number == ALI_SPDIF_IN_CHANNEL ||
12018c2ecf20Sopenharmony_ci	    pvoice->number == ALI_PCM_IN_CHANNEL)
12028c2ecf20Sopenharmony_ci		snd_ali_disable_special_channel(codec, pvoice->number);
12038c2ecf20Sopenharmony_ci	else if (codec->spdif_support &&
12048c2ecf20Sopenharmony_ci		 (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) &
12058c2ecf20Sopenharmony_ci		  ALI_SPDIF_OUT_CH_ENABLE)
12068c2ecf20Sopenharmony_ci		 && pvoice->number == ALI_SPDIF_OUT_CHANNEL) {
12078c2ecf20Sopenharmony_ci		snd_ali_set_spdif_out_rate(codec, runtime->rate);
12088c2ecf20Sopenharmony_ci		Delta = 0x1000;
12098c2ecf20Sopenharmony_ci	}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	/* set Loop Back Address */
12128c2ecf20Sopenharmony_ci	LBA = runtime->dma_addr;
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	/* set interrupt count size */
12158c2ecf20Sopenharmony_ci	pvoice->count = runtime->period_size;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	/* set target ESO for channel */
12188c2ecf20Sopenharmony_ci	pvoice->eso = runtime->buffer_size;
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "playback_prepare: eso=%xh count=%xh\n",
12218c2ecf20Sopenharmony_ci		       pvoice->eso, pvoice->count);
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	/* set ESO to capture first MIDLP interrupt */
12248c2ecf20Sopenharmony_ci	ESO = pvoice->eso -1;
12258c2ecf20Sopenharmony_ci	/* set ctrl mode */
12268c2ecf20Sopenharmony_ci	CTRL = snd_ali_control_mode(substream);
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	GVSEL = 1;
12298c2ecf20Sopenharmony_ci	PAN = 0;
12308c2ecf20Sopenharmony_ci	VOL = 0;
12318c2ecf20Sopenharmony_ci	EC = 0;
12328c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "playback_prepare:\n");
12338c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev,
12348c2ecf20Sopenharmony_ci		"ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n",
12358c2ecf20Sopenharmony_ci		       pvoice->number,runtime->rate,Delta,GVSEL,PAN,CTRL);
12368c2ecf20Sopenharmony_ci	snd_ali_write_voice_regs(codec,
12378c2ecf20Sopenharmony_ci				 pvoice->number,
12388c2ecf20Sopenharmony_ci				 LBA,
12398c2ecf20Sopenharmony_ci				 0,	/* cso */
12408c2ecf20Sopenharmony_ci				 ESO,
12418c2ecf20Sopenharmony_ci				 Delta,
12428c2ecf20Sopenharmony_ci				 0,	/* alpha */
12438c2ecf20Sopenharmony_ci				 GVSEL,
12448c2ecf20Sopenharmony_ci				 PAN,
12458c2ecf20Sopenharmony_ci				 VOL,
12468c2ecf20Sopenharmony_ci				 CTRL,
12478c2ecf20Sopenharmony_ci				 EC);
12488c2ecf20Sopenharmony_ci	if (evoice) {
12498c2ecf20Sopenharmony_ci		evoice->count = pvoice->count;
12508c2ecf20Sopenharmony_ci		evoice->eso = pvoice->count << 1;
12518c2ecf20Sopenharmony_ci		ESO = evoice->eso - 1;
12528c2ecf20Sopenharmony_ci		snd_ali_write_voice_regs(codec,
12538c2ecf20Sopenharmony_ci					 evoice->number,
12548c2ecf20Sopenharmony_ci					 LBA,
12558c2ecf20Sopenharmony_ci					 0,	/* cso */
12568c2ecf20Sopenharmony_ci					 ESO,
12578c2ecf20Sopenharmony_ci					 Delta,
12588c2ecf20Sopenharmony_ci					 0,	/* alpha */
12598c2ecf20Sopenharmony_ci					 GVSEL,
12608c2ecf20Sopenharmony_ci					 0x7f,
12618c2ecf20Sopenharmony_ci					 0x3ff,
12628c2ecf20Sopenharmony_ci					 CTRL,
12638c2ecf20Sopenharmony_ci					 EC);
12648c2ecf20Sopenharmony_ci	}
12658c2ecf20Sopenharmony_ci	spin_unlock_irq(&codec->reg_lock);
12668c2ecf20Sopenharmony_ci	return 0;
12678c2ecf20Sopenharmony_ci}
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_cistatic int snd_ali_prepare(struct snd_pcm_substream *substream)
12718c2ecf20Sopenharmony_ci{
12728c2ecf20Sopenharmony_ci	struct snd_ali *codec = snd_pcm_substream_chip(substream);
12738c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
12748c2ecf20Sopenharmony_ci	struct snd_ali_voice *pvoice = runtime->private_data;
12758c2ecf20Sopenharmony_ci	unsigned int LBA;
12768c2ecf20Sopenharmony_ci	unsigned int Delta;
12778c2ecf20Sopenharmony_ci	unsigned int ESO;
12788c2ecf20Sopenharmony_ci	unsigned int CTRL;
12798c2ecf20Sopenharmony_ci	unsigned int GVSEL;
12808c2ecf20Sopenharmony_ci	unsigned int PAN;
12818c2ecf20Sopenharmony_ci	unsigned int VOL;
12828c2ecf20Sopenharmony_ci	unsigned int EC;
12838c2ecf20Sopenharmony_ci	u8	 bValue;
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	spin_lock_irq(&codec->reg_lock);
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "ali_prepare...\n");
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	snd_ali_enable_special_channel(codec,pvoice->number);
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	Delta = (pvoice->number == ALI_MODEM_IN_CHANNEL ||
12928c2ecf20Sopenharmony_ci		 pvoice->number == ALI_MODEM_OUT_CHANNEL) ?
12938c2ecf20Sopenharmony_ci		0x1000 : snd_ali_convert_rate(runtime->rate, pvoice->mode);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	/* Prepare capture intr channel */
12968c2ecf20Sopenharmony_ci	if (pvoice->number == ALI_SPDIF_IN_CHANNEL) {
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci		unsigned int rate;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci		spin_unlock_irq(&codec->reg_lock);
13018c2ecf20Sopenharmony_ci		if (codec->revision != ALI_5451_V02)
13028c2ecf20Sopenharmony_ci			return -1;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci		rate = snd_ali_get_spdif_in_rate(codec);
13058c2ecf20Sopenharmony_ci		if (rate == 0) {
13068c2ecf20Sopenharmony_ci			dev_warn(codec->card->dev,
13078c2ecf20Sopenharmony_ci				 "ali_capture_prepare: spdif rate detect err!\n");
13088c2ecf20Sopenharmony_ci			rate = 48000;
13098c2ecf20Sopenharmony_ci		}
13108c2ecf20Sopenharmony_ci		spin_lock_irq(&codec->reg_lock);
13118c2ecf20Sopenharmony_ci		bValue = inb(ALI_REG(codec,ALI_SPDIF_CTRL));
13128c2ecf20Sopenharmony_ci		if (bValue & 0x10) {
13138c2ecf20Sopenharmony_ci			outb(bValue,ALI_REG(codec,ALI_SPDIF_CTRL));
13148c2ecf20Sopenharmony_ci			dev_warn(codec->card->dev,
13158c2ecf20Sopenharmony_ci				 "clear SPDIF parity error flag.\n");
13168c2ecf20Sopenharmony_ci		}
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci		if (rate != 48000)
13198c2ecf20Sopenharmony_ci			Delta = ((rate << 12) / runtime->rate) & 0x00ffff;
13208c2ecf20Sopenharmony_ci	}
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	/* set target ESO for channel  */
13238c2ecf20Sopenharmony_ci	pvoice->eso = runtime->buffer_size;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	/* set interrupt count size  */
13268c2ecf20Sopenharmony_ci	pvoice->count = runtime->period_size;
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	/* set Loop Back Address  */
13298c2ecf20Sopenharmony_ci	LBA = runtime->dma_addr;
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	/* set ESO to capture first MIDLP interrupt  */
13328c2ecf20Sopenharmony_ci	ESO = pvoice->eso - 1;
13338c2ecf20Sopenharmony_ci	CTRL = snd_ali_control_mode(substream);
13348c2ecf20Sopenharmony_ci	GVSEL = 0;
13358c2ecf20Sopenharmony_ci	PAN = 0x00;
13368c2ecf20Sopenharmony_ci	VOL = 0x00;
13378c2ecf20Sopenharmony_ci	EC = 0;
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	snd_ali_write_voice_regs(    codec,
13408c2ecf20Sopenharmony_ci				     pvoice->number,
13418c2ecf20Sopenharmony_ci				     LBA,
13428c2ecf20Sopenharmony_ci				     0,	/* cso */
13438c2ecf20Sopenharmony_ci				     ESO,
13448c2ecf20Sopenharmony_ci				     Delta,
13458c2ecf20Sopenharmony_ci				     0,	/* alpha */
13468c2ecf20Sopenharmony_ci				     GVSEL,
13478c2ecf20Sopenharmony_ci				     PAN,
13488c2ecf20Sopenharmony_ci				     VOL,
13498c2ecf20Sopenharmony_ci				     CTRL,
13508c2ecf20Sopenharmony_ci				     EC);
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	spin_unlock_irq(&codec->reg_lock);
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	return 0;
13558c2ecf20Sopenharmony_ci}
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t
13598c2ecf20Sopenharmony_cisnd_ali_playback_pointer(struct snd_pcm_substream *substream)
13608c2ecf20Sopenharmony_ci{
13618c2ecf20Sopenharmony_ci	struct snd_ali *codec = snd_pcm_substream_chip(substream);
13628c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
13638c2ecf20Sopenharmony_ci	struct snd_ali_voice *pvoice = runtime->private_data;
13648c2ecf20Sopenharmony_ci	unsigned int cso;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	spin_lock(&codec->reg_lock);
13678c2ecf20Sopenharmony_ci	if (!pvoice->running) {
13688c2ecf20Sopenharmony_ci		spin_unlock(&codec->reg_lock);
13698c2ecf20Sopenharmony_ci		return 0;
13708c2ecf20Sopenharmony_ci	}
13718c2ecf20Sopenharmony_ci	outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
13728c2ecf20Sopenharmony_ci	cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
13738c2ecf20Sopenharmony_ci	spin_unlock(&codec->reg_lock);
13748c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "playback pointer returned cso=%xh.\n", cso);
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	cso %= runtime->buffer_size;
13778c2ecf20Sopenharmony_ci	return cso;
13788c2ecf20Sopenharmony_ci}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_ali_pointer(struct snd_pcm_substream *substream)
13828c2ecf20Sopenharmony_ci{
13838c2ecf20Sopenharmony_ci	struct snd_ali *codec = snd_pcm_substream_chip(substream);
13848c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
13858c2ecf20Sopenharmony_ci	struct snd_ali_voice *pvoice = runtime->private_data;
13868c2ecf20Sopenharmony_ci	unsigned int cso;
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	spin_lock(&codec->reg_lock);
13898c2ecf20Sopenharmony_ci	if (!pvoice->running) {
13908c2ecf20Sopenharmony_ci		spin_unlock(&codec->reg_lock);
13918c2ecf20Sopenharmony_ci		return 0;
13928c2ecf20Sopenharmony_ci	}
13938c2ecf20Sopenharmony_ci	outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
13948c2ecf20Sopenharmony_ci	cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
13958c2ecf20Sopenharmony_ci	spin_unlock(&codec->reg_lock);
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	cso %= runtime->buffer_size;
13988c2ecf20Sopenharmony_ci	return cso;
13998c2ecf20Sopenharmony_ci}
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_ali_playback =
14028c2ecf20Sopenharmony_ci{
14038c2ecf20Sopenharmony_ci	.info =		(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
14048c2ecf20Sopenharmony_ci			 SNDRV_PCM_INFO_BLOCK_TRANSFER |
14058c2ecf20Sopenharmony_ci			 SNDRV_PCM_INFO_MMAP_VALID |
14068c2ecf20Sopenharmony_ci			 SNDRV_PCM_INFO_RESUME |
14078c2ecf20Sopenharmony_ci			 SNDRV_PCM_INFO_SYNC_START),
14088c2ecf20Sopenharmony_ci	.formats =	(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
14098c2ecf20Sopenharmony_ci			 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
14108c2ecf20Sopenharmony_ci	.rates =	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
14118c2ecf20Sopenharmony_ci	.rate_min =		4000,
14128c2ecf20Sopenharmony_ci	.rate_max =		48000,
14138c2ecf20Sopenharmony_ci	.channels_min =		1,
14148c2ecf20Sopenharmony_ci	.channels_max =		2,
14158c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(256*1024),
14168c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
14178c2ecf20Sopenharmony_ci	.period_bytes_max =	(256*1024),
14188c2ecf20Sopenharmony_ci	.periods_min =		1,
14198c2ecf20Sopenharmony_ci	.periods_max =		1024,
14208c2ecf20Sopenharmony_ci	.fifo_size =		0,
14218c2ecf20Sopenharmony_ci};
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci/*
14248c2ecf20Sopenharmony_ci *  Capture support device description
14258c2ecf20Sopenharmony_ci */
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_ali_capture =
14288c2ecf20Sopenharmony_ci{
14298c2ecf20Sopenharmony_ci	.info =		(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
14308c2ecf20Sopenharmony_ci			 SNDRV_PCM_INFO_BLOCK_TRANSFER |
14318c2ecf20Sopenharmony_ci			 SNDRV_PCM_INFO_MMAP_VALID |
14328c2ecf20Sopenharmony_ci			 SNDRV_PCM_INFO_RESUME |
14338c2ecf20Sopenharmony_ci			 SNDRV_PCM_INFO_SYNC_START),
14348c2ecf20Sopenharmony_ci	.formats =	(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
14358c2ecf20Sopenharmony_ci			 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
14368c2ecf20Sopenharmony_ci	.rates =	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
14378c2ecf20Sopenharmony_ci	.rate_min =		4000,
14388c2ecf20Sopenharmony_ci	.rate_max =		48000,
14398c2ecf20Sopenharmony_ci	.channels_min =		1,
14408c2ecf20Sopenharmony_ci	.channels_max =		2,
14418c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(128*1024),
14428c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
14438c2ecf20Sopenharmony_ci	.period_bytes_max =	(128*1024),
14448c2ecf20Sopenharmony_ci	.periods_min =		1,
14458c2ecf20Sopenharmony_ci	.periods_max =		1024,
14468c2ecf20Sopenharmony_ci	.fifo_size =		0,
14478c2ecf20Sopenharmony_ci};
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_cistatic void snd_ali_pcm_free_substream(struct snd_pcm_runtime *runtime)
14508c2ecf20Sopenharmony_ci{
14518c2ecf20Sopenharmony_ci	struct snd_ali_voice *pvoice = runtime->private_data;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	if (pvoice)
14548c2ecf20Sopenharmony_ci		snd_ali_free_voice(pvoice->codec, pvoice);
14558c2ecf20Sopenharmony_ci}
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_cistatic int snd_ali_open(struct snd_pcm_substream *substream, int rec,
14588c2ecf20Sopenharmony_ci			int channel, const struct snd_pcm_hardware *phw)
14598c2ecf20Sopenharmony_ci{
14608c2ecf20Sopenharmony_ci	struct snd_ali *codec = snd_pcm_substream_chip(substream);
14618c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
14628c2ecf20Sopenharmony_ci	struct snd_ali_voice *pvoice;
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, rec,
14658c2ecf20Sopenharmony_ci				     channel);
14668c2ecf20Sopenharmony_ci	if (!pvoice)
14678c2ecf20Sopenharmony_ci		return -EAGAIN;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	pvoice->substream = substream;
14708c2ecf20Sopenharmony_ci	runtime->private_data = pvoice;
14718c2ecf20Sopenharmony_ci	runtime->private_free = snd_ali_pcm_free_substream;
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	runtime->hw = *phw;
14748c2ecf20Sopenharmony_ci	snd_pcm_set_sync(substream);
14758c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
14768c2ecf20Sopenharmony_ci				     0, 64*1024);
14778c2ecf20Sopenharmony_ci	return 0;
14788c2ecf20Sopenharmony_ci}
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_cistatic int snd_ali_playback_open(struct snd_pcm_substream *substream)
14818c2ecf20Sopenharmony_ci{
14828c2ecf20Sopenharmony_ci	return snd_ali_open(substream, 0, -1, &snd_ali_playback);
14838c2ecf20Sopenharmony_ci}
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_cistatic int snd_ali_capture_open(struct snd_pcm_substream *substream)
14868c2ecf20Sopenharmony_ci{
14878c2ecf20Sopenharmony_ci	return snd_ali_open(substream, 1, -1, &snd_ali_capture);
14888c2ecf20Sopenharmony_ci}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_cistatic int snd_ali_playback_close(struct snd_pcm_substream *substream)
14918c2ecf20Sopenharmony_ci{
14928c2ecf20Sopenharmony_ci	return 0;
14938c2ecf20Sopenharmony_ci}
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_cistatic int snd_ali_close(struct snd_pcm_substream *substream)
14968c2ecf20Sopenharmony_ci{
14978c2ecf20Sopenharmony_ci	struct snd_ali *codec = snd_pcm_substream_chip(substream);
14988c2ecf20Sopenharmony_ci	struct snd_ali_voice *pvoice = substream->runtime->private_data;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	snd_ali_disable_special_channel(codec,pvoice->number);
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	return 0;
15038c2ecf20Sopenharmony_ci}
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_ali_playback_ops = {
15068c2ecf20Sopenharmony_ci	.open =		snd_ali_playback_open,
15078c2ecf20Sopenharmony_ci	.close =	snd_ali_playback_close,
15088c2ecf20Sopenharmony_ci	.hw_params =	snd_ali_playback_hw_params,
15098c2ecf20Sopenharmony_ci	.hw_free =	snd_ali_playback_hw_free,
15108c2ecf20Sopenharmony_ci	.prepare =	snd_ali_playback_prepare,
15118c2ecf20Sopenharmony_ci	.trigger =	snd_ali_trigger,
15128c2ecf20Sopenharmony_ci	.pointer =	snd_ali_playback_pointer,
15138c2ecf20Sopenharmony_ci};
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_ali_capture_ops = {
15168c2ecf20Sopenharmony_ci	.open =		snd_ali_capture_open,
15178c2ecf20Sopenharmony_ci	.close =	snd_ali_close,
15188c2ecf20Sopenharmony_ci	.prepare =	snd_ali_prepare,
15198c2ecf20Sopenharmony_ci	.trigger =	snd_ali_trigger,
15208c2ecf20Sopenharmony_ci	.pointer =	snd_ali_pointer,
15218c2ecf20Sopenharmony_ci};
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci/*
15248c2ecf20Sopenharmony_ci * Modem PCM
15258c2ecf20Sopenharmony_ci */
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_cistatic int snd_ali_modem_hw_params(struct snd_pcm_substream *substream,
15288c2ecf20Sopenharmony_ci				 struct snd_pcm_hw_params *hw_params)
15298c2ecf20Sopenharmony_ci{
15308c2ecf20Sopenharmony_ci	struct snd_ali *chip = snd_pcm_substream_chip(substream);
15318c2ecf20Sopenharmony_ci	unsigned int modem_num = chip->num_of_codecs - 1;
15328c2ecf20Sopenharmony_ci	snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_RATE,
15338c2ecf20Sopenharmony_ci		       params_rate(hw_params));
15348c2ecf20Sopenharmony_ci	snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_LEVEL, 0);
15358c2ecf20Sopenharmony_ci	return 0;
15368c2ecf20Sopenharmony_ci}
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_ali_modem =
15398c2ecf20Sopenharmony_ci{
15408c2ecf20Sopenharmony_ci	.info =		(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
15418c2ecf20Sopenharmony_ci			 SNDRV_PCM_INFO_BLOCK_TRANSFER |
15428c2ecf20Sopenharmony_ci			 SNDRV_PCM_INFO_MMAP_VALID |
15438c2ecf20Sopenharmony_ci			 SNDRV_PCM_INFO_RESUME |
15448c2ecf20Sopenharmony_ci			 SNDRV_PCM_INFO_SYNC_START),
15458c2ecf20Sopenharmony_ci	.formats =	SNDRV_PCM_FMTBIT_S16_LE,
15468c2ecf20Sopenharmony_ci	.rates =	(SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000 |
15478c2ecf20Sopenharmony_ci			 SNDRV_PCM_RATE_16000),
15488c2ecf20Sopenharmony_ci	.rate_min =		8000,
15498c2ecf20Sopenharmony_ci	.rate_max =		16000,
15508c2ecf20Sopenharmony_ci	.channels_min =		1,
15518c2ecf20Sopenharmony_ci	.channels_max =		1,
15528c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(256*1024),
15538c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
15548c2ecf20Sopenharmony_ci	.period_bytes_max =	(256*1024),
15558c2ecf20Sopenharmony_ci	.periods_min =		1,
15568c2ecf20Sopenharmony_ci	.periods_max =		1024,
15578c2ecf20Sopenharmony_ci	.fifo_size =		0,
15588c2ecf20Sopenharmony_ci};
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_cistatic int snd_ali_modem_open(struct snd_pcm_substream *substream, int rec,
15618c2ecf20Sopenharmony_ci			      int channel)
15628c2ecf20Sopenharmony_ci{
15638c2ecf20Sopenharmony_ci	static const unsigned int rates[] = {8000, 9600, 12000, 16000};
15648c2ecf20Sopenharmony_ci	static const struct snd_pcm_hw_constraint_list hw_constraint_rates = {
15658c2ecf20Sopenharmony_ci		.count = ARRAY_SIZE(rates),
15668c2ecf20Sopenharmony_ci		.list = rates,
15678c2ecf20Sopenharmony_ci		.mask = 0,
15688c2ecf20Sopenharmony_ci	};
15698c2ecf20Sopenharmony_ci	int err = snd_ali_open(substream, rec, channel, &snd_ali_modem);
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci	if (err)
15728c2ecf20Sopenharmony_ci		return err;
15738c2ecf20Sopenharmony_ci	return snd_pcm_hw_constraint_list(substream->runtime, 0,
15748c2ecf20Sopenharmony_ci			SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates);
15758c2ecf20Sopenharmony_ci}
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_cistatic int snd_ali_modem_playback_open(struct snd_pcm_substream *substream)
15788c2ecf20Sopenharmony_ci{
15798c2ecf20Sopenharmony_ci	return snd_ali_modem_open(substream, 0, ALI_MODEM_OUT_CHANNEL);
15808c2ecf20Sopenharmony_ci}
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_cistatic int snd_ali_modem_capture_open(struct snd_pcm_substream *substream)
15838c2ecf20Sopenharmony_ci{
15848c2ecf20Sopenharmony_ci	return snd_ali_modem_open(substream, 1, ALI_MODEM_IN_CHANNEL);
15858c2ecf20Sopenharmony_ci}
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_ali_modem_playback_ops = {
15888c2ecf20Sopenharmony_ci	.open =		snd_ali_modem_playback_open,
15898c2ecf20Sopenharmony_ci	.close =	snd_ali_close,
15908c2ecf20Sopenharmony_ci	.hw_params =	snd_ali_modem_hw_params,
15918c2ecf20Sopenharmony_ci	.prepare =	snd_ali_prepare,
15928c2ecf20Sopenharmony_ci	.trigger =	snd_ali_trigger,
15938c2ecf20Sopenharmony_ci	.pointer =	snd_ali_pointer,
15948c2ecf20Sopenharmony_ci};
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_ali_modem_capture_ops = {
15978c2ecf20Sopenharmony_ci	.open =		snd_ali_modem_capture_open,
15988c2ecf20Sopenharmony_ci	.close =	snd_ali_close,
15998c2ecf20Sopenharmony_ci	.hw_params =	snd_ali_modem_hw_params,
16008c2ecf20Sopenharmony_ci	.prepare =	snd_ali_prepare,
16018c2ecf20Sopenharmony_ci	.trigger =	snd_ali_trigger,
16028c2ecf20Sopenharmony_ci	.pointer =	snd_ali_pointer,
16038c2ecf20Sopenharmony_ci};
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_cistruct ali_pcm_description {
16078c2ecf20Sopenharmony_ci	char *name;
16088c2ecf20Sopenharmony_ci	unsigned int playback_num;
16098c2ecf20Sopenharmony_ci	unsigned int capture_num;
16108c2ecf20Sopenharmony_ci	const struct snd_pcm_ops *playback_ops;
16118c2ecf20Sopenharmony_ci	const struct snd_pcm_ops *capture_ops;
16128c2ecf20Sopenharmony_ci	unsigned short class;
16138c2ecf20Sopenharmony_ci};
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_cistatic void snd_ali_pcm_free(struct snd_pcm *pcm)
16178c2ecf20Sopenharmony_ci{
16188c2ecf20Sopenharmony_ci	struct snd_ali *codec = pcm->private_data;
16198c2ecf20Sopenharmony_ci	codec->pcm[pcm->device] = NULL;
16208c2ecf20Sopenharmony_ci}
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_cistatic int snd_ali_pcm(struct snd_ali *codec, int device,
16248c2ecf20Sopenharmony_ci		       struct ali_pcm_description *desc)
16258c2ecf20Sopenharmony_ci{
16268c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
16278c2ecf20Sopenharmony_ci	int err;
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	err = snd_pcm_new(codec->card, desc->name, device,
16308c2ecf20Sopenharmony_ci			  desc->playback_num, desc->capture_num, &pcm);
16318c2ecf20Sopenharmony_ci	if (err < 0) {
16328c2ecf20Sopenharmony_ci		dev_err(codec->card->dev,
16338c2ecf20Sopenharmony_ci			"snd_ali_pcm: err called snd_pcm_new.\n");
16348c2ecf20Sopenharmony_ci		return err;
16358c2ecf20Sopenharmony_ci	}
16368c2ecf20Sopenharmony_ci	pcm->private_data = codec;
16378c2ecf20Sopenharmony_ci	pcm->private_free = snd_ali_pcm_free;
16388c2ecf20Sopenharmony_ci	if (desc->playback_ops)
16398c2ecf20Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
16408c2ecf20Sopenharmony_ci				desc->playback_ops);
16418c2ecf20Sopenharmony_ci	if (desc->capture_ops)
16428c2ecf20Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
16438c2ecf20Sopenharmony_ci				desc->capture_ops);
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
16468c2ecf20Sopenharmony_ci				       &codec->pci->dev, 64*1024, 128*1024);
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
16498c2ecf20Sopenharmony_ci	pcm->dev_class = desc->class;
16508c2ecf20Sopenharmony_ci	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
16518c2ecf20Sopenharmony_ci	strcpy(pcm->name, desc->name);
16528c2ecf20Sopenharmony_ci	codec->pcm[0] = pcm;
16538c2ecf20Sopenharmony_ci	return 0;
16548c2ecf20Sopenharmony_ci}
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_cistatic struct ali_pcm_description ali_pcms[] = {
16578c2ecf20Sopenharmony_ci	{ .name = "ALI 5451",
16588c2ecf20Sopenharmony_ci	  .playback_num = ALI_CHANNELS,
16598c2ecf20Sopenharmony_ci	  .capture_num = 1,
16608c2ecf20Sopenharmony_ci	  .playback_ops = &snd_ali_playback_ops,
16618c2ecf20Sopenharmony_ci	  .capture_ops = &snd_ali_capture_ops
16628c2ecf20Sopenharmony_ci	},
16638c2ecf20Sopenharmony_ci	{ .name = "ALI 5451 modem",
16648c2ecf20Sopenharmony_ci	  .playback_num = 1,
16658c2ecf20Sopenharmony_ci	  .capture_num = 1,
16668c2ecf20Sopenharmony_ci	  .playback_ops = &snd_ali_modem_playback_ops,
16678c2ecf20Sopenharmony_ci	  .capture_ops = &snd_ali_modem_capture_ops,
16688c2ecf20Sopenharmony_ci	  .class = SNDRV_PCM_CLASS_MODEM
16698c2ecf20Sopenharmony_ci	}
16708c2ecf20Sopenharmony_ci};
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_cistatic int snd_ali_build_pcms(struct snd_ali *codec)
16738c2ecf20Sopenharmony_ci{
16748c2ecf20Sopenharmony_ci	int i, err;
16758c2ecf20Sopenharmony_ci	for (i = 0; i < codec->num_of_codecs && i < ARRAY_SIZE(ali_pcms); i++) {
16768c2ecf20Sopenharmony_ci		err = snd_ali_pcm(codec, i, &ali_pcms[i]);
16778c2ecf20Sopenharmony_ci		if (err < 0)
16788c2ecf20Sopenharmony_ci			return err;
16798c2ecf20Sopenharmony_ci	}
16808c2ecf20Sopenharmony_ci	return 0;
16818c2ecf20Sopenharmony_ci}
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci#define ALI5451_SPDIF(xname, xindex, value) \
16858c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
16868c2ecf20Sopenharmony_ci.info = snd_ali5451_spdif_info, .get = snd_ali5451_spdif_get, \
16878c2ecf20Sopenharmony_ci.put = snd_ali5451_spdif_put, .private_value = value}
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci#define snd_ali5451_spdif_info		snd_ctl_boolean_mono_info
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_cistatic int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol,
16928c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
16938c2ecf20Sopenharmony_ci{
16948c2ecf20Sopenharmony_ci	struct snd_ali *codec = kcontrol->private_data;
16958c2ecf20Sopenharmony_ci	unsigned int spdif_enable;
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0;
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	spin_lock_irq(&codec->reg_lock);
17008c2ecf20Sopenharmony_ci	switch (kcontrol->private_value) {
17018c2ecf20Sopenharmony_ci	case 0:
17028c2ecf20Sopenharmony_ci		spdif_enable = (codec->spdif_mask & 0x02) ? 1 : 0;
17038c2ecf20Sopenharmony_ci		break;
17048c2ecf20Sopenharmony_ci	case 1:
17058c2ecf20Sopenharmony_ci		spdif_enable = ((codec->spdif_mask & 0x02) &&
17068c2ecf20Sopenharmony_ci			  (codec->spdif_mask & 0x04)) ? 1 : 0;
17078c2ecf20Sopenharmony_ci		break;
17088c2ecf20Sopenharmony_ci	case 2:
17098c2ecf20Sopenharmony_ci		spdif_enable = (codec->spdif_mask & 0x01) ? 1 : 0;
17108c2ecf20Sopenharmony_ci		break;
17118c2ecf20Sopenharmony_ci	default:
17128c2ecf20Sopenharmony_ci		break;
17138c2ecf20Sopenharmony_ci	}
17148c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = spdif_enable;
17158c2ecf20Sopenharmony_ci	spin_unlock_irq(&codec->reg_lock);
17168c2ecf20Sopenharmony_ci	return 0;
17178c2ecf20Sopenharmony_ci}
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_cistatic int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol,
17208c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
17218c2ecf20Sopenharmony_ci{
17228c2ecf20Sopenharmony_ci	struct snd_ali *codec = kcontrol->private_data;
17238c2ecf20Sopenharmony_ci	unsigned int change = 0, spdif_enable = 0;
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0;
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	spin_lock_irq(&codec->reg_lock);
17288c2ecf20Sopenharmony_ci	switch (kcontrol->private_value) {
17298c2ecf20Sopenharmony_ci	case 0:
17308c2ecf20Sopenharmony_ci		change = (codec->spdif_mask & 0x02) ? 1 : 0;
17318c2ecf20Sopenharmony_ci		change = change ^ spdif_enable;
17328c2ecf20Sopenharmony_ci		if (change) {
17338c2ecf20Sopenharmony_ci			if (spdif_enable) {
17348c2ecf20Sopenharmony_ci				codec->spdif_mask |= 0x02;
17358c2ecf20Sopenharmony_ci				snd_ali_enable_spdif_out(codec);
17368c2ecf20Sopenharmony_ci			} else {
17378c2ecf20Sopenharmony_ci				codec->spdif_mask &= ~(0x02);
17388c2ecf20Sopenharmony_ci				codec->spdif_mask &= ~(0x04);
17398c2ecf20Sopenharmony_ci				snd_ali_disable_spdif_out(codec);
17408c2ecf20Sopenharmony_ci			}
17418c2ecf20Sopenharmony_ci		}
17428c2ecf20Sopenharmony_ci		break;
17438c2ecf20Sopenharmony_ci	case 1:
17448c2ecf20Sopenharmony_ci		change = (codec->spdif_mask & 0x04) ? 1 : 0;
17458c2ecf20Sopenharmony_ci		change = change ^ spdif_enable;
17468c2ecf20Sopenharmony_ci		if (change && (codec->spdif_mask & 0x02)) {
17478c2ecf20Sopenharmony_ci			if (spdif_enable) {
17488c2ecf20Sopenharmony_ci				codec->spdif_mask |= 0x04;
17498c2ecf20Sopenharmony_ci				snd_ali_enable_spdif_chnout(codec);
17508c2ecf20Sopenharmony_ci			} else {
17518c2ecf20Sopenharmony_ci				codec->spdif_mask &= ~(0x04);
17528c2ecf20Sopenharmony_ci				snd_ali_disable_spdif_chnout(codec);
17538c2ecf20Sopenharmony_ci			}
17548c2ecf20Sopenharmony_ci		}
17558c2ecf20Sopenharmony_ci		break;
17568c2ecf20Sopenharmony_ci	case 2:
17578c2ecf20Sopenharmony_ci		change = (codec->spdif_mask & 0x01) ? 1 : 0;
17588c2ecf20Sopenharmony_ci		change = change ^ spdif_enable;
17598c2ecf20Sopenharmony_ci		if (change) {
17608c2ecf20Sopenharmony_ci			if (spdif_enable) {
17618c2ecf20Sopenharmony_ci				codec->spdif_mask |= 0x01;
17628c2ecf20Sopenharmony_ci				snd_ali_enable_spdif_in(codec);
17638c2ecf20Sopenharmony_ci			} else {
17648c2ecf20Sopenharmony_ci				codec->spdif_mask &= ~(0x01);
17658c2ecf20Sopenharmony_ci				snd_ali_disable_spdif_in(codec);
17668c2ecf20Sopenharmony_ci			}
17678c2ecf20Sopenharmony_ci		}
17688c2ecf20Sopenharmony_ci		break;
17698c2ecf20Sopenharmony_ci	default:
17708c2ecf20Sopenharmony_ci		break;
17718c2ecf20Sopenharmony_ci	}
17728c2ecf20Sopenharmony_ci	spin_unlock_irq(&codec->reg_lock);
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	return change;
17758c2ecf20Sopenharmony_ci}
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ali5451_mixer_spdif[] = {
17788c2ecf20Sopenharmony_ci	/* spdif aplayback switch */
17798c2ecf20Sopenharmony_ci	/* FIXME: "IEC958 Playback Switch" may conflict with one on ac97_codec */
17808c2ecf20Sopenharmony_ci	ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH), 0, 0),
17818c2ecf20Sopenharmony_ci	/* spdif out to spdif channel */
17828c2ecf20Sopenharmony_ci	ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("Channel Output ",NONE,SWITCH), 0, 1),
17838c2ecf20Sopenharmony_ci	/* spdif in from spdif channel */
17848c2ecf20Sopenharmony_ci	ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, 2)
17858c2ecf20Sopenharmony_ci};
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_cistatic int snd_ali_mixer(struct snd_ali *codec)
17888c2ecf20Sopenharmony_ci{
17898c2ecf20Sopenharmony_ci	struct snd_ac97_template ac97;
17908c2ecf20Sopenharmony_ci	unsigned int idx;
17918c2ecf20Sopenharmony_ci	int i, err;
17928c2ecf20Sopenharmony_ci	static const struct snd_ac97_bus_ops ops = {
17938c2ecf20Sopenharmony_ci		.write = snd_ali_codec_write,
17948c2ecf20Sopenharmony_ci		.read = snd_ali_codec_read,
17958c2ecf20Sopenharmony_ci	};
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci	err = snd_ac97_bus(codec->card, 0, &ops, codec, &codec->ac97_bus);
17988c2ecf20Sopenharmony_ci	if (err < 0)
17998c2ecf20Sopenharmony_ci		return err;
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	memset(&ac97, 0, sizeof(ac97));
18028c2ecf20Sopenharmony_ci	ac97.private_data = codec;
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci	for (i = 0; i < codec->num_of_codecs; i++) {
18058c2ecf20Sopenharmony_ci		ac97.num = i;
18068c2ecf20Sopenharmony_ci		err = snd_ac97_mixer(codec->ac97_bus, &ac97, &codec->ac97[i]);
18078c2ecf20Sopenharmony_ci		if (err < 0) {
18088c2ecf20Sopenharmony_ci			dev_err(codec->card->dev,
18098c2ecf20Sopenharmony_ci				   "ali mixer %d creating error.\n", i);
18108c2ecf20Sopenharmony_ci			if (i == 0)
18118c2ecf20Sopenharmony_ci				return err;
18128c2ecf20Sopenharmony_ci			codec->num_of_codecs = 1;
18138c2ecf20Sopenharmony_ci			break;
18148c2ecf20Sopenharmony_ci		}
18158c2ecf20Sopenharmony_ci	}
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_ci	if (codec->spdif_support) {
18188c2ecf20Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(snd_ali5451_mixer_spdif); idx++) {
18198c2ecf20Sopenharmony_ci			err = snd_ctl_add(codec->card,
18208c2ecf20Sopenharmony_ci					  snd_ctl_new1(&snd_ali5451_mixer_spdif[idx], codec));
18218c2ecf20Sopenharmony_ci			if (err < 0)
18228c2ecf20Sopenharmony_ci				return err;
18238c2ecf20Sopenharmony_ci		}
18248c2ecf20Sopenharmony_ci	}
18258c2ecf20Sopenharmony_ci	return 0;
18268c2ecf20Sopenharmony_ci}
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
18298c2ecf20Sopenharmony_cistatic int ali_suspend(struct device *dev)
18308c2ecf20Sopenharmony_ci{
18318c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
18328c2ecf20Sopenharmony_ci	struct snd_ali *chip = card->private_data;
18338c2ecf20Sopenharmony_ci	struct snd_ali_image *im;
18348c2ecf20Sopenharmony_ci	int i, j;
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci	im = chip->image;
18378c2ecf20Sopenharmony_ci	if (!im)
18388c2ecf20Sopenharmony_ci		return 0;
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
18418c2ecf20Sopenharmony_ci	for (i = 0; i < chip->num_of_codecs; i++)
18428c2ecf20Sopenharmony_ci		snd_ac97_suspend(chip->ac97[i]);
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci	im->regs[ALI_MISCINT >> 2] = inl(ALI_REG(chip, ALI_MISCINT));
18478c2ecf20Sopenharmony_ci	/* im->regs[ALI_START >> 2] = inl(ALI_REG(chip, ALI_START)); */
18488c2ecf20Sopenharmony_ci	im->regs[ALI_STOP >> 2] = inl(ALI_REG(chip, ALI_STOP));
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	/* disable all IRQ bits */
18518c2ecf20Sopenharmony_ci	outl(0, ALI_REG(chip, ALI_MISCINT));
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_ci	for (i = 0; i < ALI_GLOBAL_REGS; i++) {
18548c2ecf20Sopenharmony_ci		if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP))
18558c2ecf20Sopenharmony_ci			continue;
18568c2ecf20Sopenharmony_ci		im->regs[i] = inl(ALI_REG(chip, i*4));
18578c2ecf20Sopenharmony_ci	}
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_ci	for (i = 0; i < ALI_CHANNELS; i++) {
18608c2ecf20Sopenharmony_ci		outb(i, ALI_REG(chip, ALI_GC_CIR));
18618c2ecf20Sopenharmony_ci		for (j = 0; j < ALI_CHANNEL_REGS; j++)
18628c2ecf20Sopenharmony_ci			im->channel_regs[i][j] = inl(ALI_REG(chip, j*4 + 0xe0));
18638c2ecf20Sopenharmony_ci	}
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	/* stop all HW channel */
18668c2ecf20Sopenharmony_ci	outl(0xffffffff, ALI_REG(chip, ALI_STOP));
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
18698c2ecf20Sopenharmony_ci	return 0;
18708c2ecf20Sopenharmony_ci}
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_cistatic int ali_resume(struct device *dev)
18738c2ecf20Sopenharmony_ci{
18748c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
18758c2ecf20Sopenharmony_ci	struct snd_ali *chip = card->private_data;
18768c2ecf20Sopenharmony_ci	struct snd_ali_image *im;
18778c2ecf20Sopenharmony_ci	int i, j;
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	im = chip->image;
18808c2ecf20Sopenharmony_ci	if (!im)
18818c2ecf20Sopenharmony_ci		return 0;
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci	for (i = 0; i < ALI_CHANNELS; i++) {
18868c2ecf20Sopenharmony_ci		outb(i, ALI_REG(chip, ALI_GC_CIR));
18878c2ecf20Sopenharmony_ci		for (j = 0; j < ALI_CHANNEL_REGS; j++)
18888c2ecf20Sopenharmony_ci			outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0));
18898c2ecf20Sopenharmony_ci	}
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	for (i = 0; i < ALI_GLOBAL_REGS; i++) {
18928c2ecf20Sopenharmony_ci		if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) ||
18938c2ecf20Sopenharmony_ci		    (i*4 == ALI_START))
18948c2ecf20Sopenharmony_ci			continue;
18958c2ecf20Sopenharmony_ci		outl(im->regs[i], ALI_REG(chip, i*4));
18968c2ecf20Sopenharmony_ci	}
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	/* start HW channel */
18998c2ecf20Sopenharmony_ci	outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START));
19008c2ecf20Sopenharmony_ci	/* restore IRQ enable bits */
19018c2ecf20Sopenharmony_ci	outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT));
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ci	for (i = 0 ; i < chip->num_of_codecs; i++)
19068c2ecf20Sopenharmony_ci		snd_ac97_resume(chip->ac97[i]);
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
19098c2ecf20Sopenharmony_ci	return 0;
19108c2ecf20Sopenharmony_ci}
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume);
19138c2ecf20Sopenharmony_ci#define ALI_PM_OPS	&ali_pm
19148c2ecf20Sopenharmony_ci#else
19158c2ecf20Sopenharmony_ci#define ALI_PM_OPS	NULL
19168c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_cistatic int snd_ali_free(struct snd_ali * codec)
19198c2ecf20Sopenharmony_ci{
19208c2ecf20Sopenharmony_ci	if (codec->hw_initialized)
19218c2ecf20Sopenharmony_ci		snd_ali_disable_address_interrupt(codec);
19228c2ecf20Sopenharmony_ci	if (codec->irq >= 0)
19238c2ecf20Sopenharmony_ci		free_irq(codec->irq, codec);
19248c2ecf20Sopenharmony_ci	if (codec->port)
19258c2ecf20Sopenharmony_ci		pci_release_regions(codec->pci);
19268c2ecf20Sopenharmony_ci	pci_disable_device(codec->pci);
19278c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
19288c2ecf20Sopenharmony_ci	kfree(codec->image);
19298c2ecf20Sopenharmony_ci#endif
19308c2ecf20Sopenharmony_ci	pci_dev_put(codec->pci_m1533);
19318c2ecf20Sopenharmony_ci	pci_dev_put(codec->pci_m7101);
19328c2ecf20Sopenharmony_ci	kfree(codec);
19338c2ecf20Sopenharmony_ci	return 0;
19348c2ecf20Sopenharmony_ci}
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_cistatic int snd_ali_chip_init(struct snd_ali *codec)
19378c2ecf20Sopenharmony_ci{
19388c2ecf20Sopenharmony_ci	unsigned int legacy;
19398c2ecf20Sopenharmony_ci	unsigned char temp;
19408c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev;
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "chip initializing ...\n");
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci	if (snd_ali_reset_5451(codec)) {
19458c2ecf20Sopenharmony_ci		dev_err(codec->card->dev, "ali_chip_init: reset 5451 error.\n");
19468c2ecf20Sopenharmony_ci		return -1;
19478c2ecf20Sopenharmony_ci	}
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci	if (codec->revision == ALI_5451_V02) {
19508c2ecf20Sopenharmony_ci        	pci_dev = codec->pci_m1533;
19518c2ecf20Sopenharmony_ci		pci_read_config_byte(pci_dev, 0x59, &temp);
19528c2ecf20Sopenharmony_ci		temp |= 0x80;
19538c2ecf20Sopenharmony_ci		pci_write_config_byte(pci_dev, 0x59, temp);
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_ci		pci_dev = codec->pci_m7101;
19568c2ecf20Sopenharmony_ci		pci_read_config_byte(pci_dev, 0xb8, &temp);
19578c2ecf20Sopenharmony_ci		temp |= 0x20;
19588c2ecf20Sopenharmony_ci		pci_write_config_byte(pci_dev, 0xB8, temp);
19598c2ecf20Sopenharmony_ci	}
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci	pci_read_config_dword(codec->pci, 0x44, &legacy);
19628c2ecf20Sopenharmony_ci	legacy &= 0xff00ff00;
19638c2ecf20Sopenharmony_ci	legacy |= 0x000800aa;
19648c2ecf20Sopenharmony_ci	pci_write_config_dword(codec->pci, 0x44, legacy);
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci	outl(0x80000001, ALI_REG(codec, ALI_GLOBAL_CONTROL));
19678c2ecf20Sopenharmony_ci	outl(0x00000000, ALI_REG(codec, ALI_AINTEN));
19688c2ecf20Sopenharmony_ci	outl(0xffffffff, ALI_REG(codec, ALI_AINT));
19698c2ecf20Sopenharmony_ci	outl(0x00000000, ALI_REG(codec, ALI_VOLUME));
19708c2ecf20Sopenharmony_ci	outb(0x10, 	 ALI_REG(codec, ALI_MPUR2));
19718c2ecf20Sopenharmony_ci
19728c2ecf20Sopenharmony_ci	codec->ac97_ext_id = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_ID);
19738c2ecf20Sopenharmony_ci	codec->ac97_ext_status = snd_ali_codec_peek(codec, 0,
19748c2ecf20Sopenharmony_ci						    AC97_EXTENDED_STATUS);
19758c2ecf20Sopenharmony_ci	if (codec->spdif_support) {
19768c2ecf20Sopenharmony_ci		snd_ali_enable_spdif_out(codec);
19778c2ecf20Sopenharmony_ci		codec->spdif_mask = 0x00000002;
19788c2ecf20Sopenharmony_ci	}
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	codec->num_of_codecs = 1;
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci	/* secondary codec - modem */
19838c2ecf20Sopenharmony_ci	if (inl(ALI_REG(codec, ALI_SCTRL)) & ALI_SCTRL_CODEC2_READY) {
19848c2ecf20Sopenharmony_ci		codec->num_of_codecs++;
19858c2ecf20Sopenharmony_ci		outl(inl(ALI_REG(codec, ALI_SCTRL)) |
19868c2ecf20Sopenharmony_ci		     (ALI_SCTRL_LINE_IN2 | ALI_SCTRL_GPIO_IN2 |
19878c2ecf20Sopenharmony_ci		      ALI_SCTRL_LINE_OUT_EN),
19888c2ecf20Sopenharmony_ci		     ALI_REG(codec, ALI_SCTRL));
19898c2ecf20Sopenharmony_ci	}
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "chip initialize succeed.\n");
19928c2ecf20Sopenharmony_ci	return 0;
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_ci}
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci/* proc for register dump */
19978c2ecf20Sopenharmony_cistatic void snd_ali_proc_read(struct snd_info_entry *entry,
19988c2ecf20Sopenharmony_ci			      struct snd_info_buffer *buf)
19998c2ecf20Sopenharmony_ci{
20008c2ecf20Sopenharmony_ci	struct snd_ali *codec = entry->private_data;
20018c2ecf20Sopenharmony_ci	int i;
20028c2ecf20Sopenharmony_ci	for (i = 0; i < 256 ; i+= 4)
20038c2ecf20Sopenharmony_ci		snd_iprintf(buf, "%02x: %08x\n", i, inl(ALI_REG(codec, i)));
20048c2ecf20Sopenharmony_ci}
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_cistatic void snd_ali_proc_init(struct snd_ali *codec)
20078c2ecf20Sopenharmony_ci{
20088c2ecf20Sopenharmony_ci	snd_card_ro_proc_new(codec->card, "ali5451", codec, snd_ali_proc_read);
20098c2ecf20Sopenharmony_ci}
20108c2ecf20Sopenharmony_ci
20118c2ecf20Sopenharmony_cistatic int snd_ali_resources(struct snd_ali *codec)
20128c2ecf20Sopenharmony_ci{
20138c2ecf20Sopenharmony_ci	int err;
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "resources allocation ...\n");
20168c2ecf20Sopenharmony_ci	err = pci_request_regions(codec->pci, "ALI 5451");
20178c2ecf20Sopenharmony_ci	if (err < 0)
20188c2ecf20Sopenharmony_ci		return err;
20198c2ecf20Sopenharmony_ci	codec->port = pci_resource_start(codec->pci, 0);
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci	if (request_irq(codec->pci->irq, snd_ali_card_interrupt,
20228c2ecf20Sopenharmony_ci			IRQF_SHARED, KBUILD_MODNAME, codec)) {
20238c2ecf20Sopenharmony_ci		dev_err(codec->card->dev, "Unable to request irq.\n");
20248c2ecf20Sopenharmony_ci		return -EBUSY;
20258c2ecf20Sopenharmony_ci	}
20268c2ecf20Sopenharmony_ci	codec->irq = codec->pci->irq;
20278c2ecf20Sopenharmony_ci	codec->card->sync_irq = codec->irq;
20288c2ecf20Sopenharmony_ci	dev_dbg(codec->card->dev, "resources allocated.\n");
20298c2ecf20Sopenharmony_ci	return 0;
20308c2ecf20Sopenharmony_ci}
20318c2ecf20Sopenharmony_cistatic int snd_ali_dev_free(struct snd_device *device)
20328c2ecf20Sopenharmony_ci{
20338c2ecf20Sopenharmony_ci	struct snd_ali *codec = device->device_data;
20348c2ecf20Sopenharmony_ci	snd_ali_free(codec);
20358c2ecf20Sopenharmony_ci	return 0;
20368c2ecf20Sopenharmony_ci}
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_cistatic int snd_ali_create(struct snd_card *card,
20398c2ecf20Sopenharmony_ci			  struct pci_dev *pci,
20408c2ecf20Sopenharmony_ci			  int pcm_streams,
20418c2ecf20Sopenharmony_ci			  int spdif_support,
20428c2ecf20Sopenharmony_ci			  struct snd_ali **r_ali)
20438c2ecf20Sopenharmony_ci{
20448c2ecf20Sopenharmony_ci	struct snd_ali *codec;
20458c2ecf20Sopenharmony_ci	int i, err;
20468c2ecf20Sopenharmony_ci	unsigned short cmdw;
20478c2ecf20Sopenharmony_ci	static const struct snd_device_ops ops = {
20488c2ecf20Sopenharmony_ci		.dev_free = snd_ali_dev_free,
20498c2ecf20Sopenharmony_ci        };
20508c2ecf20Sopenharmony_ci
20518c2ecf20Sopenharmony_ci	*r_ali = NULL;
20528c2ecf20Sopenharmony_ci
20538c2ecf20Sopenharmony_ci	dev_dbg(card->dev, "creating ...\n");
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci	/* enable PCI device */
20568c2ecf20Sopenharmony_ci	err = pci_enable_device(pci);
20578c2ecf20Sopenharmony_ci	if (err < 0)
20588c2ecf20Sopenharmony_ci		return err;
20598c2ecf20Sopenharmony_ci	/* check, if we can restrict PCI DMA transfers to 31 bits */
20608c2ecf20Sopenharmony_ci	if (dma_set_mask(&pci->dev, DMA_BIT_MASK(31)) < 0 ||
20618c2ecf20Sopenharmony_ci	    dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(31)) < 0) {
20628c2ecf20Sopenharmony_ci		dev_err(card->dev,
20638c2ecf20Sopenharmony_ci			"architecture does not support 31bit PCI busmaster DMA\n");
20648c2ecf20Sopenharmony_ci		pci_disable_device(pci);
20658c2ecf20Sopenharmony_ci		return -ENXIO;
20668c2ecf20Sopenharmony_ci	}
20678c2ecf20Sopenharmony_ci
20688c2ecf20Sopenharmony_ci	codec = kzalloc(sizeof(*codec), GFP_KERNEL);
20698c2ecf20Sopenharmony_ci	if (!codec) {
20708c2ecf20Sopenharmony_ci		pci_disable_device(pci);
20718c2ecf20Sopenharmony_ci		return -ENOMEM;
20728c2ecf20Sopenharmony_ci	}
20738c2ecf20Sopenharmony_ci
20748c2ecf20Sopenharmony_ci	spin_lock_init(&codec->reg_lock);
20758c2ecf20Sopenharmony_ci	spin_lock_init(&codec->voice_alloc);
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	codec->card = card;
20788c2ecf20Sopenharmony_ci	codec->pci = pci;
20798c2ecf20Sopenharmony_ci	codec->irq = -1;
20808c2ecf20Sopenharmony_ci	codec->revision = pci->revision;
20818c2ecf20Sopenharmony_ci	codec->spdif_support = spdif_support;
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_ci	if (pcm_streams < 1)
20848c2ecf20Sopenharmony_ci		pcm_streams = 1;
20858c2ecf20Sopenharmony_ci	if (pcm_streams > 32)
20868c2ecf20Sopenharmony_ci		pcm_streams = 32;
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_ci	pci_set_master(pci);
20898c2ecf20Sopenharmony_ci	pci_read_config_word(pci, PCI_COMMAND, &cmdw);
20908c2ecf20Sopenharmony_ci	if ((cmdw & PCI_COMMAND_IO) != PCI_COMMAND_IO) {
20918c2ecf20Sopenharmony_ci		cmdw |= PCI_COMMAND_IO;
20928c2ecf20Sopenharmony_ci		pci_write_config_word(pci, PCI_COMMAND, cmdw);
20938c2ecf20Sopenharmony_ci	}
20948c2ecf20Sopenharmony_ci	pci_set_master(pci);
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ci	if (snd_ali_resources(codec)) {
20978c2ecf20Sopenharmony_ci		snd_ali_free(codec);
20988c2ecf20Sopenharmony_ci		return -EBUSY;
20998c2ecf20Sopenharmony_ci	}
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci	codec->synth.chmap = 0;
21028c2ecf20Sopenharmony_ci	codec->synth.chcnt = 0;
21038c2ecf20Sopenharmony_ci	codec->spdif_mask = 0;
21048c2ecf20Sopenharmony_ci	codec->synth.synthcount = 0;
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	if (codec->revision == ALI_5451_V02)
21078c2ecf20Sopenharmony_ci		codec->chregs.regs.ac97read = ALI_AC97_WRITE;
21088c2ecf20Sopenharmony_ci	else
21098c2ecf20Sopenharmony_ci		codec->chregs.regs.ac97read = ALI_AC97_READ;
21108c2ecf20Sopenharmony_ci	codec->chregs.regs.ac97write = ALI_AC97_WRITE;
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci	codec->chregs.regs.start  = ALI_START;
21138c2ecf20Sopenharmony_ci	codec->chregs.regs.stop   = ALI_STOP;
21148c2ecf20Sopenharmony_ci	codec->chregs.regs.aint   = ALI_AINT;
21158c2ecf20Sopenharmony_ci	codec->chregs.regs.ainten = ALI_AINTEN;
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci	codec->chregs.data.start  = 0x00;
21188c2ecf20Sopenharmony_ci	codec->chregs.data.stop   = 0x00;
21198c2ecf20Sopenharmony_ci	codec->chregs.data.aint   = 0x00;
21208c2ecf20Sopenharmony_ci	codec->chregs.data.ainten = 0x00;
21218c2ecf20Sopenharmony_ci
21228c2ecf20Sopenharmony_ci	/* M1533: southbridge */
21238c2ecf20Sopenharmony_ci	codec->pci_m1533 = pci_get_device(0x10b9, 0x1533, NULL);
21248c2ecf20Sopenharmony_ci	if (!codec->pci_m1533) {
21258c2ecf20Sopenharmony_ci		dev_err(card->dev, "cannot find ALi 1533 chip.\n");
21268c2ecf20Sopenharmony_ci		snd_ali_free(codec);
21278c2ecf20Sopenharmony_ci		return -ENODEV;
21288c2ecf20Sopenharmony_ci	}
21298c2ecf20Sopenharmony_ci	/* M7101: power management */
21308c2ecf20Sopenharmony_ci	codec->pci_m7101 = pci_get_device(0x10b9, 0x7101, NULL);
21318c2ecf20Sopenharmony_ci	if (!codec->pci_m7101 && codec->revision == ALI_5451_V02) {
21328c2ecf20Sopenharmony_ci		dev_err(card->dev, "cannot find ALi 7101 chip.\n");
21338c2ecf20Sopenharmony_ci		snd_ali_free(codec);
21348c2ecf20Sopenharmony_ci		return -ENODEV;
21358c2ecf20Sopenharmony_ci	}
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_ci	dev_dbg(card->dev, "snd_device_new is called.\n");
21388c2ecf20Sopenharmony_ci	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops);
21398c2ecf20Sopenharmony_ci	if (err < 0) {
21408c2ecf20Sopenharmony_ci		snd_ali_free(codec);
21418c2ecf20Sopenharmony_ci		return err;
21428c2ecf20Sopenharmony_ci	}
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_ci	/* initialise synth voices*/
21458c2ecf20Sopenharmony_ci	for (i = 0; i < ALI_CHANNELS; i++)
21468c2ecf20Sopenharmony_ci		codec->synth.voices[i].number = i;
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci	err = snd_ali_chip_init(codec);
21498c2ecf20Sopenharmony_ci	if (err < 0) {
21508c2ecf20Sopenharmony_ci		dev_err(card->dev, "ali create: chip init error.\n");
21518c2ecf20Sopenharmony_ci		return err;
21528c2ecf20Sopenharmony_ci	}
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
21558c2ecf20Sopenharmony_ci	codec->image = kmalloc(sizeof(*codec->image), GFP_KERNEL);
21568c2ecf20Sopenharmony_ci	if (!codec->image)
21578c2ecf20Sopenharmony_ci		dev_warn(card->dev, "can't allocate apm buffer\n");
21588c2ecf20Sopenharmony_ci#endif
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci	snd_ali_enable_address_interrupt(codec);
21618c2ecf20Sopenharmony_ci	codec->hw_initialized = 1;
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_ci	*r_ali = codec;
21648c2ecf20Sopenharmony_ci	dev_dbg(card->dev, "created.\n");
21658c2ecf20Sopenharmony_ci	return 0;
21668c2ecf20Sopenharmony_ci}
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_cistatic int snd_ali_probe(struct pci_dev *pci,
21698c2ecf20Sopenharmony_ci			 const struct pci_device_id *pci_id)
21708c2ecf20Sopenharmony_ci{
21718c2ecf20Sopenharmony_ci	struct snd_card *card;
21728c2ecf20Sopenharmony_ci	struct snd_ali *codec;
21738c2ecf20Sopenharmony_ci	int err;
21748c2ecf20Sopenharmony_ci
21758c2ecf20Sopenharmony_ci	dev_dbg(&pci->dev, "probe ...\n");
21768c2ecf20Sopenharmony_ci
21778c2ecf20Sopenharmony_ci	err = snd_card_new(&pci->dev, index, id, THIS_MODULE, 0, &card);
21788c2ecf20Sopenharmony_ci	if (err < 0)
21798c2ecf20Sopenharmony_ci		return err;
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_ci	err = snd_ali_create(card, pci, pcm_channels, spdif, &codec);
21828c2ecf20Sopenharmony_ci	if (err < 0)
21838c2ecf20Sopenharmony_ci		goto error;
21848c2ecf20Sopenharmony_ci	card->private_data = codec;
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci	dev_dbg(&pci->dev, "mixer building ...\n");
21878c2ecf20Sopenharmony_ci	err = snd_ali_mixer(codec);
21888c2ecf20Sopenharmony_ci	if (err < 0)
21898c2ecf20Sopenharmony_ci		goto error;
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ci	dev_dbg(&pci->dev, "pcm building ...\n");
21928c2ecf20Sopenharmony_ci	err = snd_ali_build_pcms(codec);
21938c2ecf20Sopenharmony_ci	if (err < 0)
21948c2ecf20Sopenharmony_ci		goto error;
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci	snd_ali_proc_init(codec);
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_ci	strcpy(card->driver, "ALI5451");
21998c2ecf20Sopenharmony_ci	strcpy(card->shortname, "ALI 5451");
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci	sprintf(card->longname, "%s at 0x%lx, irq %i",
22028c2ecf20Sopenharmony_ci		card->shortname, codec->port, codec->irq);
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci	dev_dbg(&pci->dev, "register card.\n");
22058c2ecf20Sopenharmony_ci	err = snd_card_register(card);
22068c2ecf20Sopenharmony_ci	if (err < 0)
22078c2ecf20Sopenharmony_ci		goto error;
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci	pci_set_drvdata(pci, card);
22108c2ecf20Sopenharmony_ci	return 0;
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci error:
22138c2ecf20Sopenharmony_ci	snd_card_free(card);
22148c2ecf20Sopenharmony_ci	return err;
22158c2ecf20Sopenharmony_ci}
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_cistatic void snd_ali_remove(struct pci_dev *pci)
22188c2ecf20Sopenharmony_ci{
22198c2ecf20Sopenharmony_ci	snd_card_free(pci_get_drvdata(pci));
22208c2ecf20Sopenharmony_ci}
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_cistatic struct pci_driver ali5451_driver = {
22238c2ecf20Sopenharmony_ci	.name = KBUILD_MODNAME,
22248c2ecf20Sopenharmony_ci	.id_table = snd_ali_ids,
22258c2ecf20Sopenharmony_ci	.probe = snd_ali_probe,
22268c2ecf20Sopenharmony_ci	.remove = snd_ali_remove,
22278c2ecf20Sopenharmony_ci	.driver = {
22288c2ecf20Sopenharmony_ci		.pm = ALI_PM_OPS,
22298c2ecf20Sopenharmony_ci	},
22308c2ecf20Sopenharmony_ci};
22318c2ecf20Sopenharmony_ci
22328c2ecf20Sopenharmony_cimodule_pci_driver(ali5451_driver);
2233