18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  The driver for the ForteMedia FM801 based soundcards
48c2ecf20Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/delay.h>
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/pci.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <sound/core.h>
158c2ecf20Sopenharmony_ci#include <sound/pcm.h>
168c2ecf20Sopenharmony_ci#include <sound/tlv.h>
178c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h>
188c2ecf20Sopenharmony_ci#include <sound/mpu401.h>
198c2ecf20Sopenharmony_ci#include <sound/opl3.h>
208c2ecf20Sopenharmony_ci#include <sound/initval.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_FM801_TEA575X_BOOL
238c2ecf20Sopenharmony_ci#include <media/drv-intf/tea575x.h>
248c2ecf20Sopenharmony_ci#endif
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ForteMedia FM801");
288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
298c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{ForteMedia,FM801},"
308c2ecf20Sopenharmony_ci		"{Genius,SoundMaker Live 5.1}}");
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
338c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
348c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci *  Enable TEA575x tuner
378c2ecf20Sopenharmony_ci *    1 = MediaForte 256-PCS
388c2ecf20Sopenharmony_ci *    2 = MediaForte 256-PCP
398c2ecf20Sopenharmony_ci *    3 = MediaForte 64-PCR
408c2ecf20Sopenharmony_ci *   16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card
418c2ecf20Sopenharmony_ci *  High 16-bits are video (radio) device number + 1
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_cistatic int tea575x_tuner[SNDRV_CARDS];
448c2ecf20Sopenharmony_cistatic int radio_nr[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for the FM801 soundcard.");
488c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for the FM801 soundcard.");
508c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
528c2ecf20Sopenharmony_cimodule_param_array(tea575x_tuner, int, NULL, 0444);
538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (0 = auto, 1 = SF256-PCS, 2=SF256-PCP, 3=SF64-PCR, 8=disable, +16=tuner-only).");
548c2ecf20Sopenharmony_cimodule_param_array(radio_nr, int, NULL, 0444);
558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "Radio device numbers");
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#define TUNER_DISABLED		(1<<3)
598c2ecf20Sopenharmony_ci#define TUNER_ONLY		(1<<4)
608c2ecf20Sopenharmony_ci#define TUNER_TYPE_MASK		(~TUNER_ONLY & 0xFFFF)
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/*
638c2ecf20Sopenharmony_ci *  Direct registers
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci#define fm801_writew(chip,reg,value)	outw((value), chip->port + FM801_##reg)
678c2ecf20Sopenharmony_ci#define fm801_readw(chip,reg)		inw(chip->port + FM801_##reg)
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#define fm801_writel(chip,reg,value)	outl((value), chip->port + FM801_##reg)
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define FM801_PCM_VOL		0x00	/* PCM Output Volume */
728c2ecf20Sopenharmony_ci#define FM801_FM_VOL		0x02	/* FM Output Volume */
738c2ecf20Sopenharmony_ci#define FM801_I2S_VOL		0x04	/* I2S Volume */
748c2ecf20Sopenharmony_ci#define FM801_REC_SRC		0x06	/* Record Source */
758c2ecf20Sopenharmony_ci#define FM801_PLY_CTRL		0x08	/* Playback Control */
768c2ecf20Sopenharmony_ci#define FM801_PLY_COUNT		0x0a	/* Playback Count */
778c2ecf20Sopenharmony_ci#define FM801_PLY_BUF1		0x0c	/* Playback Bufer I */
788c2ecf20Sopenharmony_ci#define FM801_PLY_BUF2		0x10	/* Playback Buffer II */
798c2ecf20Sopenharmony_ci#define FM801_CAP_CTRL		0x14	/* Capture Control */
808c2ecf20Sopenharmony_ci#define FM801_CAP_COUNT		0x16	/* Capture Count */
818c2ecf20Sopenharmony_ci#define FM801_CAP_BUF1		0x18	/* Capture Buffer I */
828c2ecf20Sopenharmony_ci#define FM801_CAP_BUF2		0x1c	/* Capture Buffer II */
838c2ecf20Sopenharmony_ci#define FM801_CODEC_CTRL	0x22	/* Codec Control */
848c2ecf20Sopenharmony_ci#define FM801_I2S_MODE		0x24	/* I2S Mode Control */
858c2ecf20Sopenharmony_ci#define FM801_VOLUME		0x26	/* Volume Up/Down/Mute Status */
868c2ecf20Sopenharmony_ci#define FM801_I2C_CTRL		0x29	/* I2C Control */
878c2ecf20Sopenharmony_ci#define FM801_AC97_CMD		0x2a	/* AC'97 Command */
888c2ecf20Sopenharmony_ci#define FM801_AC97_DATA		0x2c	/* AC'97 Data */
898c2ecf20Sopenharmony_ci#define FM801_MPU401_DATA	0x30	/* MPU401 Data */
908c2ecf20Sopenharmony_ci#define FM801_MPU401_CMD	0x31	/* MPU401 Command */
918c2ecf20Sopenharmony_ci#define FM801_GPIO_CTRL		0x52	/* General Purpose I/O Control */
928c2ecf20Sopenharmony_ci#define FM801_GEN_CTRL		0x54	/* General Control */
938c2ecf20Sopenharmony_ci#define FM801_IRQ_MASK		0x56	/* Interrupt Mask */
948c2ecf20Sopenharmony_ci#define FM801_IRQ_STATUS	0x5a	/* Interrupt Status */
958c2ecf20Sopenharmony_ci#define FM801_OPL3_BANK0	0x68	/* OPL3 Status Read / Bank 0 Write */
968c2ecf20Sopenharmony_ci#define FM801_OPL3_DATA0	0x69	/* OPL3 Data 0 Write */
978c2ecf20Sopenharmony_ci#define FM801_OPL3_BANK1	0x6a	/* OPL3 Bank 1 Write */
988c2ecf20Sopenharmony_ci#define FM801_OPL3_DATA1	0x6b	/* OPL3 Bank 1 Write */
998c2ecf20Sopenharmony_ci#define FM801_POWERDOWN		0x70	/* Blocks Power Down Control */
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/* codec access */
1028c2ecf20Sopenharmony_ci#define FM801_AC97_READ		(1<<7)	/* read=1, write=0 */
1038c2ecf20Sopenharmony_ci#define FM801_AC97_VALID	(1<<8)	/* port valid=1 */
1048c2ecf20Sopenharmony_ci#define FM801_AC97_BUSY		(1<<9)	/* busy=1 */
1058c2ecf20Sopenharmony_ci#define FM801_AC97_ADDR_SHIFT	10	/* codec id (2bit) */
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/* playback and record control register bits */
1088c2ecf20Sopenharmony_ci#define FM801_BUF1_LAST		(1<<1)
1098c2ecf20Sopenharmony_ci#define FM801_BUF2_LAST		(1<<2)
1108c2ecf20Sopenharmony_ci#define FM801_START		(1<<5)
1118c2ecf20Sopenharmony_ci#define FM801_PAUSE		(1<<6)
1128c2ecf20Sopenharmony_ci#define FM801_IMMED_STOP	(1<<7)
1138c2ecf20Sopenharmony_ci#define FM801_RATE_SHIFT	8
1148c2ecf20Sopenharmony_ci#define FM801_RATE_MASK		(15 << FM801_RATE_SHIFT)
1158c2ecf20Sopenharmony_ci#define FM801_CHANNELS_4	(1<<12)	/* playback only */
1168c2ecf20Sopenharmony_ci#define FM801_CHANNELS_6	(2<<12)	/* playback only */
1178c2ecf20Sopenharmony_ci#define FM801_CHANNELS_6MS	(3<<12)	/* playback only */
1188c2ecf20Sopenharmony_ci#define FM801_CHANNELS_MASK	(3<<12)
1198c2ecf20Sopenharmony_ci#define FM801_16BIT		(1<<14)
1208c2ecf20Sopenharmony_ci#define FM801_STEREO		(1<<15)
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/* IRQ status bits */
1238c2ecf20Sopenharmony_ci#define FM801_IRQ_PLAYBACK	(1<<8)
1248c2ecf20Sopenharmony_ci#define FM801_IRQ_CAPTURE	(1<<9)
1258c2ecf20Sopenharmony_ci#define FM801_IRQ_VOLUME	(1<<14)
1268c2ecf20Sopenharmony_ci#define FM801_IRQ_MPU		(1<<15)
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/* GPIO control register */
1298c2ecf20Sopenharmony_ci#define FM801_GPIO_GP0		(1<<0)	/* read/write */
1308c2ecf20Sopenharmony_ci#define FM801_GPIO_GP1		(1<<1)
1318c2ecf20Sopenharmony_ci#define FM801_GPIO_GP2		(1<<2)
1328c2ecf20Sopenharmony_ci#define FM801_GPIO_GP3		(1<<3)
1338c2ecf20Sopenharmony_ci#define FM801_GPIO_GP(x)	(1<<(0+(x)))
1348c2ecf20Sopenharmony_ci#define FM801_GPIO_GD0		(1<<8)	/* directions: 1 = input, 0 = output*/
1358c2ecf20Sopenharmony_ci#define FM801_GPIO_GD1		(1<<9)
1368c2ecf20Sopenharmony_ci#define FM801_GPIO_GD2		(1<<10)
1378c2ecf20Sopenharmony_ci#define FM801_GPIO_GD3		(1<<11)
1388c2ecf20Sopenharmony_ci#define FM801_GPIO_GD(x)	(1<<(8+(x)))
1398c2ecf20Sopenharmony_ci#define FM801_GPIO_GS0		(1<<12)	/* function select: */
1408c2ecf20Sopenharmony_ci#define FM801_GPIO_GS1		(1<<13)	/*    1 = GPIO */
1418c2ecf20Sopenharmony_ci#define FM801_GPIO_GS2		(1<<14)	/*    0 = other (S/PDIF, VOL) */
1428c2ecf20Sopenharmony_ci#define FM801_GPIO_GS3		(1<<15)
1438c2ecf20Sopenharmony_ci#define FM801_GPIO_GS(x)	(1<<(12+(x)))
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/**
1468c2ecf20Sopenharmony_ci * struct fm801 - describes FM801 chip
1478c2ecf20Sopenharmony_ci * @dev:		device for this chio
1488c2ecf20Sopenharmony_ci * @irq:		irq number
1498c2ecf20Sopenharmony_ci * @port:		I/O port number
1508c2ecf20Sopenharmony_ci * @multichannel:	multichannel support
1518c2ecf20Sopenharmony_ci * @secondary:		secondary codec
1528c2ecf20Sopenharmony_ci * @secondary_addr:	address of the secondary codec
1538c2ecf20Sopenharmony_ci * @tea575x_tuner:	tuner access method & flags
1548c2ecf20Sopenharmony_ci * @ply_ctrl:		playback control
1558c2ecf20Sopenharmony_ci * @cap_ctrl:		capture control
1568c2ecf20Sopenharmony_ci * @ply_buffer:		playback buffer
1578c2ecf20Sopenharmony_ci * @ply_buf:		playback buffer index
1588c2ecf20Sopenharmony_ci * @ply_count:		playback buffer count
1598c2ecf20Sopenharmony_ci * @ply_size:		playback buffer size
1608c2ecf20Sopenharmony_ci * @ply_pos:		playback position
1618c2ecf20Sopenharmony_ci * @cap_buffer:		capture buffer
1628c2ecf20Sopenharmony_ci * @cap_buf:		capture buffer index
1638c2ecf20Sopenharmony_ci * @cap_count:		capture buffer count
1648c2ecf20Sopenharmony_ci * @cap_size:		capture buffer size
1658c2ecf20Sopenharmony_ci * @cap_pos:		capture position
1668c2ecf20Sopenharmony_ci * @ac97_bus:		ac97 bus handle
1678c2ecf20Sopenharmony_ci * @ac97:		ac97 handle
1688c2ecf20Sopenharmony_ci * @ac97_sec:		ac97 secondary handle
1698c2ecf20Sopenharmony_ci * @card:		ALSA card
1708c2ecf20Sopenharmony_ci * @pcm:		PCM devices
1718c2ecf20Sopenharmony_ci * @rmidi:		rmidi device
1728c2ecf20Sopenharmony_ci * @playback_substream:	substream for playback
1738c2ecf20Sopenharmony_ci * @capture_substream:	substream for capture
1748c2ecf20Sopenharmony_ci * @p_dma_size:		playback DMA size
1758c2ecf20Sopenharmony_ci * @c_dma_size:		capture DMA size
1768c2ecf20Sopenharmony_ci * @reg_lock:		lock
1778c2ecf20Sopenharmony_ci * @proc_entry:		/proc entry
1788c2ecf20Sopenharmony_ci * @v4l2_dev:		v4l2 device
1798c2ecf20Sopenharmony_ci * @tea:		tea575a structure
1808c2ecf20Sopenharmony_ci * @saved_regs:		context saved during suspend
1818c2ecf20Sopenharmony_ci */
1828c2ecf20Sopenharmony_cistruct fm801 {
1838c2ecf20Sopenharmony_ci	struct device *dev;
1848c2ecf20Sopenharmony_ci	int irq;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	unsigned long port;
1878c2ecf20Sopenharmony_ci	unsigned int multichannel: 1,
1888c2ecf20Sopenharmony_ci		     secondary: 1;
1898c2ecf20Sopenharmony_ci	unsigned char secondary_addr;
1908c2ecf20Sopenharmony_ci	unsigned int tea575x_tuner;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	unsigned short ply_ctrl;
1938c2ecf20Sopenharmony_ci	unsigned short cap_ctrl;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	unsigned long ply_buffer;
1968c2ecf20Sopenharmony_ci	unsigned int ply_buf;
1978c2ecf20Sopenharmony_ci	unsigned int ply_count;
1988c2ecf20Sopenharmony_ci	unsigned int ply_size;
1998c2ecf20Sopenharmony_ci	unsigned int ply_pos;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	unsigned long cap_buffer;
2028c2ecf20Sopenharmony_ci	unsigned int cap_buf;
2038c2ecf20Sopenharmony_ci	unsigned int cap_count;
2048c2ecf20Sopenharmony_ci	unsigned int cap_size;
2058c2ecf20Sopenharmony_ci	unsigned int cap_pos;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	struct snd_ac97_bus *ac97_bus;
2088c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97;
2098c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97_sec;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	struct snd_card *card;
2128c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
2138c2ecf20Sopenharmony_ci	struct snd_rawmidi *rmidi;
2148c2ecf20Sopenharmony_ci	struct snd_pcm_substream *playback_substream;
2158c2ecf20Sopenharmony_ci	struct snd_pcm_substream *capture_substream;
2168c2ecf20Sopenharmony_ci	unsigned int p_dma_size;
2178c2ecf20Sopenharmony_ci	unsigned int c_dma_size;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	spinlock_t reg_lock;
2208c2ecf20Sopenharmony_ci	struct snd_info_entry *proc_entry;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_FM801_TEA575X_BOOL
2238c2ecf20Sopenharmony_ci	struct v4l2_device v4l2_dev;
2248c2ecf20Sopenharmony_ci	struct snd_tea575x tea;
2258c2ecf20Sopenharmony_ci#endif
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
2288c2ecf20Sopenharmony_ci	u16 saved_regs[0x20];
2298c2ecf20Sopenharmony_ci#endif
2308c2ecf20Sopenharmony_ci};
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci/*
2338c2ecf20Sopenharmony_ci * IO accessors
2348c2ecf20Sopenharmony_ci */
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic inline void fm801_iowrite16(struct fm801 *chip, unsigned short offset, u16 value)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	outw(value, chip->port + offset);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic inline u16 fm801_ioread16(struct fm801 *chip, unsigned short offset)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	return inw(chip->port + offset);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_fm801_ids[] = {
2478c2ecf20Sopenharmony_ci	{ 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },   /* FM801 */
2488c2ecf20Sopenharmony_ci	{ 0x5213, 0x0510, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },   /* Gallant Odyssey Sound 4 */
2498c2ecf20Sopenharmony_ci	{ 0, }
2508c2ecf20Sopenharmony_ci};
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_fm801_ids);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/*
2558c2ecf20Sopenharmony_ci *  common I/O routines
2568c2ecf20Sopenharmony_ci */
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic bool fm801_ac97_is_ready(struct fm801 *chip, unsigned int iterations)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	unsigned int idx;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	for (idx = 0; idx < iterations; idx++) {
2638c2ecf20Sopenharmony_ci		if (!(fm801_readw(chip, AC97_CMD) & FM801_AC97_BUSY))
2648c2ecf20Sopenharmony_ci			return true;
2658c2ecf20Sopenharmony_ci		udelay(10);
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci	return false;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic bool fm801_ac97_is_valid(struct fm801 *chip, unsigned int iterations)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	unsigned int idx;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	for (idx = 0; idx < iterations; idx++) {
2758c2ecf20Sopenharmony_ci		if (fm801_readw(chip, AC97_CMD) & FM801_AC97_VALID)
2768c2ecf20Sopenharmony_ci			return true;
2778c2ecf20Sopenharmony_ci		udelay(10);
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci	return false;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic int snd_fm801_update_bits(struct fm801 *chip, unsigned short reg,
2838c2ecf20Sopenharmony_ci				 unsigned short mask, unsigned short value)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	int change;
2868c2ecf20Sopenharmony_ci	unsigned long flags;
2878c2ecf20Sopenharmony_ci	unsigned short old, new;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->reg_lock, flags);
2908c2ecf20Sopenharmony_ci	old = fm801_ioread16(chip, reg);
2918c2ecf20Sopenharmony_ci	new = (old & ~mask) | value;
2928c2ecf20Sopenharmony_ci	change = old != new;
2938c2ecf20Sopenharmony_ci	if (change)
2948c2ecf20Sopenharmony_ci		fm801_iowrite16(chip, reg, new);
2958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->reg_lock, flags);
2968c2ecf20Sopenharmony_ci	return change;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic void snd_fm801_codec_write(struct snd_ac97 *ac97,
3008c2ecf20Sopenharmony_ci				  unsigned short reg,
3018c2ecf20Sopenharmony_ci				  unsigned short val)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct fm801 *chip = ac97->private_data;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/*
3068c2ecf20Sopenharmony_ci	 *  Wait until the codec interface is not ready..
3078c2ecf20Sopenharmony_ci	 */
3088c2ecf20Sopenharmony_ci	if (!fm801_ac97_is_ready(chip, 100)) {
3098c2ecf20Sopenharmony_ci		dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
3108c2ecf20Sopenharmony_ci		return;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/* write data and address */
3148c2ecf20Sopenharmony_ci	fm801_writew(chip, AC97_DATA, val);
3158c2ecf20Sopenharmony_ci	fm801_writew(chip, AC97_CMD, reg | (ac97->addr << FM801_AC97_ADDR_SHIFT));
3168c2ecf20Sopenharmony_ci	/*
3178c2ecf20Sopenharmony_ci	 *  Wait until the write command is not completed..
3188c2ecf20Sopenharmony_ci	 */
3198c2ecf20Sopenharmony_ci	if (!fm801_ac97_is_ready(chip, 1000))
3208c2ecf20Sopenharmony_ci		dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n",
3218c2ecf20Sopenharmony_ci		ac97->num);
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic unsigned short snd_fm801_codec_read(struct snd_ac97 *ac97, unsigned short reg)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	struct fm801 *chip = ac97->private_data;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	/*
3298c2ecf20Sopenharmony_ci	 *  Wait until the codec interface is not ready..
3308c2ecf20Sopenharmony_ci	 */
3318c2ecf20Sopenharmony_ci	if (!fm801_ac97_is_ready(chip, 100)) {
3328c2ecf20Sopenharmony_ci		dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
3338c2ecf20Sopenharmony_ci		return 0;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* read command */
3378c2ecf20Sopenharmony_ci	fm801_writew(chip, AC97_CMD,
3388c2ecf20Sopenharmony_ci		     reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ);
3398c2ecf20Sopenharmony_ci	if (!fm801_ac97_is_ready(chip, 100)) {
3408c2ecf20Sopenharmony_ci		dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n",
3418c2ecf20Sopenharmony_ci			ac97->num);
3428c2ecf20Sopenharmony_ci		return 0;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (!fm801_ac97_is_valid(chip, 1000)) {
3468c2ecf20Sopenharmony_ci		dev_err(chip->card->dev,
3478c2ecf20Sopenharmony_ci			"AC'97 interface #%d is not valid (2)\n", ac97->num);
3488c2ecf20Sopenharmony_ci		return 0;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	return fm801_readw(chip, AC97_DATA);
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic const unsigned int rates[] = {
3558c2ecf20Sopenharmony_ci  5500,  8000,  9600, 11025,
3568c2ecf20Sopenharmony_ci  16000, 19200, 22050, 32000,
3578c2ecf20Sopenharmony_ci  38400, 44100, 48000
3588c2ecf20Sopenharmony_ci};
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
3618c2ecf20Sopenharmony_ci	.count = ARRAY_SIZE(rates),
3628c2ecf20Sopenharmony_ci	.list = rates,
3638c2ecf20Sopenharmony_ci	.mask = 0,
3648c2ecf20Sopenharmony_ci};
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic const unsigned int channels[] = {
3678c2ecf20Sopenharmony_ci  2, 4, 6
3688c2ecf20Sopenharmony_ci};
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_channels = {
3718c2ecf20Sopenharmony_ci	.count = ARRAY_SIZE(channels),
3728c2ecf20Sopenharmony_ci	.list = channels,
3738c2ecf20Sopenharmony_ci	.mask = 0,
3748c2ecf20Sopenharmony_ci};
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci/*
3778c2ecf20Sopenharmony_ci *  Sample rate routines
3788c2ecf20Sopenharmony_ci */
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic unsigned short snd_fm801_rate_bits(unsigned int rate)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	unsigned int idx;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(rates); idx++)
3858c2ecf20Sopenharmony_ci		if (rates[idx] == rate)
3868c2ecf20Sopenharmony_ci			return idx;
3878c2ecf20Sopenharmony_ci	snd_BUG();
3888c2ecf20Sopenharmony_ci	return ARRAY_SIZE(rates) - 1;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci/*
3928c2ecf20Sopenharmony_ci *  PCM part
3938c2ecf20Sopenharmony_ci */
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic int snd_fm801_playback_trigger(struct snd_pcm_substream *substream,
3968c2ecf20Sopenharmony_ci				      int cmd)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_pcm_substream_chip(substream);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	spin_lock(&chip->reg_lock);
4018c2ecf20Sopenharmony_ci	switch (cmd) {
4028c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
4038c2ecf20Sopenharmony_ci		chip->ply_ctrl &= ~(FM801_BUF1_LAST |
4048c2ecf20Sopenharmony_ci				     FM801_BUF2_LAST |
4058c2ecf20Sopenharmony_ci				     FM801_PAUSE);
4068c2ecf20Sopenharmony_ci		chip->ply_ctrl |= FM801_START |
4078c2ecf20Sopenharmony_ci				   FM801_IMMED_STOP;
4088c2ecf20Sopenharmony_ci		break;
4098c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
4108c2ecf20Sopenharmony_ci		chip->ply_ctrl &= ~(FM801_START | FM801_PAUSE);
4118c2ecf20Sopenharmony_ci		break;
4128c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
4138c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
4148c2ecf20Sopenharmony_ci		chip->ply_ctrl |= FM801_PAUSE;
4158c2ecf20Sopenharmony_ci		break;
4168c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
4178c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
4188c2ecf20Sopenharmony_ci		chip->ply_ctrl &= ~FM801_PAUSE;
4198c2ecf20Sopenharmony_ci		break;
4208c2ecf20Sopenharmony_ci	default:
4218c2ecf20Sopenharmony_ci		spin_unlock(&chip->reg_lock);
4228c2ecf20Sopenharmony_ci		snd_BUG();
4238c2ecf20Sopenharmony_ci		return -EINVAL;
4248c2ecf20Sopenharmony_ci	}
4258c2ecf20Sopenharmony_ci	fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
4268c2ecf20Sopenharmony_ci	spin_unlock(&chip->reg_lock);
4278c2ecf20Sopenharmony_ci	return 0;
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic int snd_fm801_capture_trigger(struct snd_pcm_substream *substream,
4318c2ecf20Sopenharmony_ci				     int cmd)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_pcm_substream_chip(substream);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	spin_lock(&chip->reg_lock);
4368c2ecf20Sopenharmony_ci	switch (cmd) {
4378c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
4388c2ecf20Sopenharmony_ci		chip->cap_ctrl &= ~(FM801_BUF1_LAST |
4398c2ecf20Sopenharmony_ci				     FM801_BUF2_LAST |
4408c2ecf20Sopenharmony_ci				     FM801_PAUSE);
4418c2ecf20Sopenharmony_ci		chip->cap_ctrl |= FM801_START |
4428c2ecf20Sopenharmony_ci				   FM801_IMMED_STOP;
4438c2ecf20Sopenharmony_ci		break;
4448c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
4458c2ecf20Sopenharmony_ci		chip->cap_ctrl &= ~(FM801_START | FM801_PAUSE);
4468c2ecf20Sopenharmony_ci		break;
4478c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
4488c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
4498c2ecf20Sopenharmony_ci		chip->cap_ctrl |= FM801_PAUSE;
4508c2ecf20Sopenharmony_ci		break;
4518c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
4528c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
4538c2ecf20Sopenharmony_ci		chip->cap_ctrl &= ~FM801_PAUSE;
4548c2ecf20Sopenharmony_ci		break;
4558c2ecf20Sopenharmony_ci	default:
4568c2ecf20Sopenharmony_ci		spin_unlock(&chip->reg_lock);
4578c2ecf20Sopenharmony_ci		snd_BUG();
4588c2ecf20Sopenharmony_ci		return -EINVAL;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci	fm801_writew(chip, CAP_CTRL, chip->cap_ctrl);
4618c2ecf20Sopenharmony_ci	spin_unlock(&chip->reg_lock);
4628c2ecf20Sopenharmony_ci	return 0;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic int snd_fm801_playback_prepare(struct snd_pcm_substream *substream)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_pcm_substream_chip(substream);
4688c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	chip->ply_size = snd_pcm_lib_buffer_bytes(substream);
4718c2ecf20Sopenharmony_ci	chip->ply_count = snd_pcm_lib_period_bytes(substream);
4728c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
4738c2ecf20Sopenharmony_ci	chip->ply_ctrl &= ~(FM801_START | FM801_16BIT |
4748c2ecf20Sopenharmony_ci			     FM801_STEREO | FM801_RATE_MASK |
4758c2ecf20Sopenharmony_ci			     FM801_CHANNELS_MASK);
4768c2ecf20Sopenharmony_ci	if (snd_pcm_format_width(runtime->format) == 16)
4778c2ecf20Sopenharmony_ci		chip->ply_ctrl |= FM801_16BIT;
4788c2ecf20Sopenharmony_ci	if (runtime->channels > 1) {
4798c2ecf20Sopenharmony_ci		chip->ply_ctrl |= FM801_STEREO;
4808c2ecf20Sopenharmony_ci		if (runtime->channels == 4)
4818c2ecf20Sopenharmony_ci			chip->ply_ctrl |= FM801_CHANNELS_4;
4828c2ecf20Sopenharmony_ci		else if (runtime->channels == 6)
4838c2ecf20Sopenharmony_ci			chip->ply_ctrl |= FM801_CHANNELS_6;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci	chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
4868c2ecf20Sopenharmony_ci	chip->ply_buf = 0;
4878c2ecf20Sopenharmony_ci	fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
4888c2ecf20Sopenharmony_ci	fm801_writew(chip, PLY_COUNT, chip->ply_count - 1);
4898c2ecf20Sopenharmony_ci	chip->ply_buffer = runtime->dma_addr;
4908c2ecf20Sopenharmony_ci	chip->ply_pos = 0;
4918c2ecf20Sopenharmony_ci	fm801_writel(chip, PLY_BUF1, chip->ply_buffer);
4928c2ecf20Sopenharmony_ci	fm801_writel(chip, PLY_BUF2,
4938c2ecf20Sopenharmony_ci		     chip->ply_buffer + (chip->ply_count % chip->ply_size));
4948c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
4958c2ecf20Sopenharmony_ci	return 0;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic int snd_fm801_capture_prepare(struct snd_pcm_substream *substream)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_pcm_substream_chip(substream);
5018c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	chip->cap_size = snd_pcm_lib_buffer_bytes(substream);
5048c2ecf20Sopenharmony_ci	chip->cap_count = snd_pcm_lib_period_bytes(substream);
5058c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
5068c2ecf20Sopenharmony_ci	chip->cap_ctrl &= ~(FM801_START | FM801_16BIT |
5078c2ecf20Sopenharmony_ci			     FM801_STEREO | FM801_RATE_MASK);
5088c2ecf20Sopenharmony_ci	if (snd_pcm_format_width(runtime->format) == 16)
5098c2ecf20Sopenharmony_ci		chip->cap_ctrl |= FM801_16BIT;
5108c2ecf20Sopenharmony_ci	if (runtime->channels > 1)
5118c2ecf20Sopenharmony_ci		chip->cap_ctrl |= FM801_STEREO;
5128c2ecf20Sopenharmony_ci	chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
5138c2ecf20Sopenharmony_ci	chip->cap_buf = 0;
5148c2ecf20Sopenharmony_ci	fm801_writew(chip, CAP_CTRL, chip->cap_ctrl);
5158c2ecf20Sopenharmony_ci	fm801_writew(chip, CAP_COUNT, chip->cap_count - 1);
5168c2ecf20Sopenharmony_ci	chip->cap_buffer = runtime->dma_addr;
5178c2ecf20Sopenharmony_ci	chip->cap_pos = 0;
5188c2ecf20Sopenharmony_ci	fm801_writel(chip, CAP_BUF1, chip->cap_buffer);
5198c2ecf20Sopenharmony_ci	fm801_writel(chip, CAP_BUF2,
5208c2ecf20Sopenharmony_ci		     chip->cap_buffer + (chip->cap_count % chip->cap_size));
5218c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
5228c2ecf20Sopenharmony_ci	return 0;
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_fm801_playback_pointer(struct snd_pcm_substream *substream)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_pcm_substream_chip(substream);
5288c2ecf20Sopenharmony_ci	size_t ptr;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	if (!(chip->ply_ctrl & FM801_START))
5318c2ecf20Sopenharmony_ci		return 0;
5328c2ecf20Sopenharmony_ci	spin_lock(&chip->reg_lock);
5338c2ecf20Sopenharmony_ci	ptr = chip->ply_pos + (chip->ply_count - 1) - fm801_readw(chip, PLY_COUNT);
5348c2ecf20Sopenharmony_ci	if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_PLAYBACK) {
5358c2ecf20Sopenharmony_ci		ptr += chip->ply_count;
5368c2ecf20Sopenharmony_ci		ptr %= chip->ply_size;
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci	spin_unlock(&chip->reg_lock);
5398c2ecf20Sopenharmony_ci	return bytes_to_frames(substream->runtime, ptr);
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_fm801_capture_pointer(struct snd_pcm_substream *substream)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_pcm_substream_chip(substream);
5458c2ecf20Sopenharmony_ci	size_t ptr;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	if (!(chip->cap_ctrl & FM801_START))
5488c2ecf20Sopenharmony_ci		return 0;
5498c2ecf20Sopenharmony_ci	spin_lock(&chip->reg_lock);
5508c2ecf20Sopenharmony_ci	ptr = chip->cap_pos + (chip->cap_count - 1) - fm801_readw(chip, CAP_COUNT);
5518c2ecf20Sopenharmony_ci	if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_CAPTURE) {
5528c2ecf20Sopenharmony_ci		ptr += chip->cap_count;
5538c2ecf20Sopenharmony_ci		ptr %= chip->cap_size;
5548c2ecf20Sopenharmony_ci	}
5558c2ecf20Sopenharmony_ci	spin_unlock(&chip->reg_lock);
5568c2ecf20Sopenharmony_ci	return bytes_to_frames(substream->runtime, ptr);
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_cistatic irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	struct fm801 *chip = dev_id;
5628c2ecf20Sopenharmony_ci	unsigned short status;
5638c2ecf20Sopenharmony_ci	unsigned int tmp;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	status = fm801_readw(chip, IRQ_STATUS);
5668c2ecf20Sopenharmony_ci	status &= FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME;
5678c2ecf20Sopenharmony_ci	if (! status)
5688c2ecf20Sopenharmony_ci		return IRQ_NONE;
5698c2ecf20Sopenharmony_ci	/* ack first */
5708c2ecf20Sopenharmony_ci	fm801_writew(chip, IRQ_STATUS, status);
5718c2ecf20Sopenharmony_ci	if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) {
5728c2ecf20Sopenharmony_ci		spin_lock(&chip->reg_lock);
5738c2ecf20Sopenharmony_ci		chip->ply_buf++;
5748c2ecf20Sopenharmony_ci		chip->ply_pos += chip->ply_count;
5758c2ecf20Sopenharmony_ci		chip->ply_pos %= chip->ply_size;
5768c2ecf20Sopenharmony_ci		tmp = chip->ply_pos + chip->ply_count;
5778c2ecf20Sopenharmony_ci		tmp %= chip->ply_size;
5788c2ecf20Sopenharmony_ci		if (chip->ply_buf & 1)
5798c2ecf20Sopenharmony_ci			fm801_writel(chip, PLY_BUF1, chip->ply_buffer + tmp);
5808c2ecf20Sopenharmony_ci		else
5818c2ecf20Sopenharmony_ci			fm801_writel(chip, PLY_BUF2, chip->ply_buffer + tmp);
5828c2ecf20Sopenharmony_ci		spin_unlock(&chip->reg_lock);
5838c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(chip->playback_substream);
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci	if (chip->pcm && (status & FM801_IRQ_CAPTURE) && chip->capture_substream) {
5868c2ecf20Sopenharmony_ci		spin_lock(&chip->reg_lock);
5878c2ecf20Sopenharmony_ci		chip->cap_buf++;
5888c2ecf20Sopenharmony_ci		chip->cap_pos += chip->cap_count;
5898c2ecf20Sopenharmony_ci		chip->cap_pos %= chip->cap_size;
5908c2ecf20Sopenharmony_ci		tmp = chip->cap_pos + chip->cap_count;
5918c2ecf20Sopenharmony_ci		tmp %= chip->cap_size;
5928c2ecf20Sopenharmony_ci		if (chip->cap_buf & 1)
5938c2ecf20Sopenharmony_ci			fm801_writel(chip, CAP_BUF1, chip->cap_buffer + tmp);
5948c2ecf20Sopenharmony_ci		else
5958c2ecf20Sopenharmony_ci			fm801_writel(chip, CAP_BUF2, chip->cap_buffer + tmp);
5968c2ecf20Sopenharmony_ci		spin_unlock(&chip->reg_lock);
5978c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(chip->capture_substream);
5988c2ecf20Sopenharmony_ci	}
5998c2ecf20Sopenharmony_ci	if (chip->rmidi && (status & FM801_IRQ_MPU))
6008c2ecf20Sopenharmony_ci		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
6018c2ecf20Sopenharmony_ci	if (status & FM801_IRQ_VOLUME) {
6028c2ecf20Sopenharmony_ci		/* TODO */
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_fm801_playback =
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
6118c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
6128c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
6138c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID),
6148c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
6158c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
6168c2ecf20Sopenharmony_ci	.rate_min =		5500,
6178c2ecf20Sopenharmony_ci	.rate_max =		48000,
6188c2ecf20Sopenharmony_ci	.channels_min =		1,
6198c2ecf20Sopenharmony_ci	.channels_max =		2,
6208c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(128*1024),
6218c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
6228c2ecf20Sopenharmony_ci	.period_bytes_max =	(128*1024),
6238c2ecf20Sopenharmony_ci	.periods_min =		1,
6248c2ecf20Sopenharmony_ci	.periods_max =		1024,
6258c2ecf20Sopenharmony_ci	.fifo_size =		0,
6268c2ecf20Sopenharmony_ci};
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_fm801_capture =
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
6318c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
6328c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
6338c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID),
6348c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
6358c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
6368c2ecf20Sopenharmony_ci	.rate_min =		5500,
6378c2ecf20Sopenharmony_ci	.rate_max =		48000,
6388c2ecf20Sopenharmony_ci	.channels_min =		1,
6398c2ecf20Sopenharmony_ci	.channels_max =		2,
6408c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(128*1024),
6418c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
6428c2ecf20Sopenharmony_ci	.period_bytes_max =	(128*1024),
6438c2ecf20Sopenharmony_ci	.periods_min =		1,
6448c2ecf20Sopenharmony_ci	.periods_max =		1024,
6458c2ecf20Sopenharmony_ci	.fifo_size =		0,
6468c2ecf20Sopenharmony_ci};
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_cistatic int snd_fm801_playback_open(struct snd_pcm_substream *substream)
6498c2ecf20Sopenharmony_ci{
6508c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_pcm_substream_chip(substream);
6518c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
6528c2ecf20Sopenharmony_ci	int err;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	chip->playback_substream = substream;
6558c2ecf20Sopenharmony_ci	runtime->hw = snd_fm801_playback;
6568c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
6578c2ecf20Sopenharmony_ci				   &hw_constraints_rates);
6588c2ecf20Sopenharmony_ci	if (chip->multichannel) {
6598c2ecf20Sopenharmony_ci		runtime->hw.channels_max = 6;
6608c2ecf20Sopenharmony_ci		snd_pcm_hw_constraint_list(runtime, 0,
6618c2ecf20Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_CHANNELS,
6628c2ecf20Sopenharmony_ci					   &hw_constraints_channels);
6638c2ecf20Sopenharmony_ci	}
6648c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
6658c2ecf20Sopenharmony_ci		return err;
6668c2ecf20Sopenharmony_ci	return 0;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cistatic int snd_fm801_capture_open(struct snd_pcm_substream *substream)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_pcm_substream_chip(substream);
6728c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
6738c2ecf20Sopenharmony_ci	int err;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	chip->capture_substream = substream;
6768c2ecf20Sopenharmony_ci	runtime->hw = snd_fm801_capture;
6778c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
6788c2ecf20Sopenharmony_ci				   &hw_constraints_rates);
6798c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
6808c2ecf20Sopenharmony_ci		return err;
6818c2ecf20Sopenharmony_ci	return 0;
6828c2ecf20Sopenharmony_ci}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_cistatic int snd_fm801_playback_close(struct snd_pcm_substream *substream)
6858c2ecf20Sopenharmony_ci{
6868c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_pcm_substream_chip(substream);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	chip->playback_substream = NULL;
6898c2ecf20Sopenharmony_ci	return 0;
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic int snd_fm801_capture_close(struct snd_pcm_substream *substream)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_pcm_substream_chip(substream);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	chip->capture_substream = NULL;
6978c2ecf20Sopenharmony_ci	return 0;
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_fm801_playback_ops = {
7018c2ecf20Sopenharmony_ci	.open =		snd_fm801_playback_open,
7028c2ecf20Sopenharmony_ci	.close =	snd_fm801_playback_close,
7038c2ecf20Sopenharmony_ci	.prepare =	snd_fm801_playback_prepare,
7048c2ecf20Sopenharmony_ci	.trigger =	snd_fm801_playback_trigger,
7058c2ecf20Sopenharmony_ci	.pointer =	snd_fm801_playback_pointer,
7068c2ecf20Sopenharmony_ci};
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_fm801_capture_ops = {
7098c2ecf20Sopenharmony_ci	.open =		snd_fm801_capture_open,
7108c2ecf20Sopenharmony_ci	.close =	snd_fm801_capture_close,
7118c2ecf20Sopenharmony_ci	.prepare =	snd_fm801_capture_prepare,
7128c2ecf20Sopenharmony_ci	.trigger =	snd_fm801_capture_trigger,
7138c2ecf20Sopenharmony_ci	.pointer =	snd_fm801_capture_pointer,
7148c2ecf20Sopenharmony_ci};
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_cistatic int snd_fm801_pcm(struct fm801 *chip, int device)
7178c2ecf20Sopenharmony_ci{
7188c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(chip->dev);
7198c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
7208c2ecf20Sopenharmony_ci	int err;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	if ((err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm)) < 0)
7238c2ecf20Sopenharmony_ci		return err;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_fm801_playback_ops);
7268c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_fm801_capture_ops);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	pcm->private_data = chip;
7298c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
7308c2ecf20Sopenharmony_ci	strcpy(pcm->name, "FM801");
7318c2ecf20Sopenharmony_ci	chip->pcm = pcm;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &pdev->dev,
7348c2ecf20Sopenharmony_ci				       chip->multichannel ? 128*1024 : 64*1024, 128*1024);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
7378c2ecf20Sopenharmony_ci				     snd_pcm_alt_chmaps,
7388c2ecf20Sopenharmony_ci				     chip->multichannel ? 6 : 2, 0,
7398c2ecf20Sopenharmony_ci				     NULL);
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci/*
7438c2ecf20Sopenharmony_ci *  TEA5757 radio
7448c2ecf20Sopenharmony_ci */
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_FM801_TEA575X_BOOL
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci/* GPIO to TEA575x maps */
7498c2ecf20Sopenharmony_cistruct snd_fm801_tea575x_gpio {
7508c2ecf20Sopenharmony_ci	u8 data, clk, wren, most;
7518c2ecf20Sopenharmony_ci	char *name;
7528c2ecf20Sopenharmony_ci};
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cistatic const struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = {
7558c2ecf20Sopenharmony_ci	{ .data = 1, .clk = 3, .wren = 2, .most = 0, .name = "SF256-PCS" },
7568c2ecf20Sopenharmony_ci	{ .data = 1, .clk = 0, .wren = 2, .most = 3, .name = "SF256-PCP" },
7578c2ecf20Sopenharmony_ci	{ .data = 2, .clk = 0, .wren = 1, .most = 3, .name = "SF64-PCR" },
7588c2ecf20Sopenharmony_ci};
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci#define get_tea575x_gpio(chip) \
7618c2ecf20Sopenharmony_ci	(&snd_fm801_tea575x_gpios[((chip)->tea575x_tuner & TUNER_TYPE_MASK) - 1])
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_cistatic void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	struct fm801 *chip = tea->private_data;
7668c2ecf20Sopenharmony_ci	unsigned short reg = fm801_readw(chip, GPIO_CTRL);
7678c2ecf20Sopenharmony_ci	struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	reg &= ~(FM801_GPIO_GP(gpio.data) |
7708c2ecf20Sopenharmony_ci		 FM801_GPIO_GP(gpio.clk) |
7718c2ecf20Sopenharmony_ci		 FM801_GPIO_GP(gpio.wren));
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	reg |= (pins & TEA575X_DATA) ? FM801_GPIO_GP(gpio.data) : 0;
7748c2ecf20Sopenharmony_ci	reg |= (pins & TEA575X_CLK)  ? FM801_GPIO_GP(gpio.clk) : 0;
7758c2ecf20Sopenharmony_ci	/* WRITE_ENABLE is inverted */
7768c2ecf20Sopenharmony_ci	reg |= (pins & TEA575X_WREN) ? 0 : FM801_GPIO_GP(gpio.wren);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	fm801_writew(chip, GPIO_CTRL, reg);
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_cistatic u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea)
7828c2ecf20Sopenharmony_ci{
7838c2ecf20Sopenharmony_ci	struct fm801 *chip = tea->private_data;
7848c2ecf20Sopenharmony_ci	unsigned short reg = fm801_readw(chip, GPIO_CTRL);
7858c2ecf20Sopenharmony_ci	struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
7868c2ecf20Sopenharmony_ci	u8 ret;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	ret = 0;
7898c2ecf20Sopenharmony_ci	if (reg & FM801_GPIO_GP(gpio.data))
7908c2ecf20Sopenharmony_ci		ret |= TEA575X_DATA;
7918c2ecf20Sopenharmony_ci	if (reg & FM801_GPIO_GP(gpio.most))
7928c2ecf20Sopenharmony_ci		ret |= TEA575X_MOST;
7938c2ecf20Sopenharmony_ci	return ret;
7948c2ecf20Sopenharmony_ci}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_cistatic void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output)
7978c2ecf20Sopenharmony_ci{
7988c2ecf20Sopenharmony_ci	struct fm801 *chip = tea->private_data;
7998c2ecf20Sopenharmony_ci	unsigned short reg = fm801_readw(chip, GPIO_CTRL);
8008c2ecf20Sopenharmony_ci	struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	/* use GPIO lines and set write enable bit */
8038c2ecf20Sopenharmony_ci	reg |= FM801_GPIO_GS(gpio.data) |
8048c2ecf20Sopenharmony_ci	       FM801_GPIO_GS(gpio.wren) |
8058c2ecf20Sopenharmony_ci	       FM801_GPIO_GS(gpio.clk) |
8068c2ecf20Sopenharmony_ci	       FM801_GPIO_GS(gpio.most);
8078c2ecf20Sopenharmony_ci	if (output) {
8088c2ecf20Sopenharmony_ci		/* all of lines are in the write direction */
8098c2ecf20Sopenharmony_ci		/* clear data and clock lines */
8108c2ecf20Sopenharmony_ci		reg &= ~(FM801_GPIO_GD(gpio.data) |
8118c2ecf20Sopenharmony_ci			 FM801_GPIO_GD(gpio.wren) |
8128c2ecf20Sopenharmony_ci			 FM801_GPIO_GD(gpio.clk) |
8138c2ecf20Sopenharmony_ci			 FM801_GPIO_GP(gpio.data) |
8148c2ecf20Sopenharmony_ci			 FM801_GPIO_GP(gpio.clk) |
8158c2ecf20Sopenharmony_ci			 FM801_GPIO_GP(gpio.wren));
8168c2ecf20Sopenharmony_ci	} else {
8178c2ecf20Sopenharmony_ci		/* use GPIO lines, set data direction to input */
8188c2ecf20Sopenharmony_ci		reg |= FM801_GPIO_GD(gpio.data) |
8198c2ecf20Sopenharmony_ci		       FM801_GPIO_GD(gpio.most) |
8208c2ecf20Sopenharmony_ci		       FM801_GPIO_GP(gpio.data) |
8218c2ecf20Sopenharmony_ci		       FM801_GPIO_GP(gpio.most) |
8228c2ecf20Sopenharmony_ci		       FM801_GPIO_GP(gpio.wren);
8238c2ecf20Sopenharmony_ci		/* all of lines are in the write direction, except data */
8248c2ecf20Sopenharmony_ci		/* clear data, write enable and clock lines */
8258c2ecf20Sopenharmony_ci		reg &= ~(FM801_GPIO_GD(gpio.wren) |
8268c2ecf20Sopenharmony_ci			 FM801_GPIO_GD(gpio.clk) |
8278c2ecf20Sopenharmony_ci			 FM801_GPIO_GP(gpio.clk));
8288c2ecf20Sopenharmony_ci	}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	fm801_writew(chip, GPIO_CTRL, reg);
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_cistatic const struct snd_tea575x_ops snd_fm801_tea_ops = {
8348c2ecf20Sopenharmony_ci	.set_pins = snd_fm801_tea575x_set_pins,
8358c2ecf20Sopenharmony_ci	.get_pins = snd_fm801_tea575x_get_pins,
8368c2ecf20Sopenharmony_ci	.set_direction = snd_fm801_tea575x_set_direction,
8378c2ecf20Sopenharmony_ci};
8388c2ecf20Sopenharmony_ci#endif
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci/*
8418c2ecf20Sopenharmony_ci *  Mixer routines
8428c2ecf20Sopenharmony_ci */
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci#define FM801_SINGLE(xname, reg, shift, mask, invert) \
8458c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_single, \
8468c2ecf20Sopenharmony_ci  .get = snd_fm801_get_single, .put = snd_fm801_put_single, \
8478c2ecf20Sopenharmony_ci  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_cistatic int snd_fm801_info_single(struct snd_kcontrol *kcontrol,
8508c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_info *uinfo)
8518c2ecf20Sopenharmony_ci{
8528c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
8558c2ecf20Sopenharmony_ci	uinfo->count = 1;
8568c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
8578c2ecf20Sopenharmony_ci	uinfo->value.integer.max = mask;
8588c2ecf20Sopenharmony_ci	return 0;
8598c2ecf20Sopenharmony_ci}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_cistatic int snd_fm801_get_single(struct snd_kcontrol *kcontrol,
8628c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
8638c2ecf20Sopenharmony_ci{
8648c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_kcontrol_chip(kcontrol);
8658c2ecf20Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
8668c2ecf20Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
8678c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
8688c2ecf20Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 0xff;
8698c2ecf20Sopenharmony_ci	long *value = ucontrol->value.integer.value;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	value[0] = (fm801_ioread16(chip, reg) >> shift) & mask;
8728c2ecf20Sopenharmony_ci	if (invert)
8738c2ecf20Sopenharmony_ci		value[0] = mask - value[0];
8748c2ecf20Sopenharmony_ci	return 0;
8758c2ecf20Sopenharmony_ci}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_cistatic int snd_fm801_put_single(struct snd_kcontrol *kcontrol,
8788c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
8798c2ecf20Sopenharmony_ci{
8808c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_kcontrol_chip(kcontrol);
8818c2ecf20Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
8828c2ecf20Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
8838c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
8848c2ecf20Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 0xff;
8858c2ecf20Sopenharmony_ci	unsigned short val;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	val = (ucontrol->value.integer.value[0] & mask);
8888c2ecf20Sopenharmony_ci	if (invert)
8898c2ecf20Sopenharmony_ci		val = mask - val;
8908c2ecf20Sopenharmony_ci	return snd_fm801_update_bits(chip, reg, mask << shift, val << shift);
8918c2ecf20Sopenharmony_ci}
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci#define FM801_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
8948c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_double, \
8958c2ecf20Sopenharmony_ci  .get = snd_fm801_get_double, .put = snd_fm801_put_double, \
8968c2ecf20Sopenharmony_ci  .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
8978c2ecf20Sopenharmony_ci#define FM801_DOUBLE_TLV(xname, reg, shift_left, shift_right, mask, invert, xtlv) \
8988c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
8998c2ecf20Sopenharmony_ci  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
9008c2ecf20Sopenharmony_ci  .name = xname, .info = snd_fm801_info_double, \
9018c2ecf20Sopenharmony_ci  .get = snd_fm801_get_double, .put = snd_fm801_put_double, \
9028c2ecf20Sopenharmony_ci  .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24), \
9038c2ecf20Sopenharmony_ci  .tlv = { .p = (xtlv) } }
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_cistatic int snd_fm801_info_double(struct snd_kcontrol *kcontrol,
9068c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_info *uinfo)
9078c2ecf20Sopenharmony_ci{
9088c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
9118c2ecf20Sopenharmony_ci	uinfo->count = 2;
9128c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
9138c2ecf20Sopenharmony_ci	uinfo->value.integer.max = mask;
9148c2ecf20Sopenharmony_ci	return 0;
9158c2ecf20Sopenharmony_ci}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_cistatic int snd_fm801_get_double(struct snd_kcontrol *kcontrol,
9188c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
9198c2ecf20Sopenharmony_ci{
9208c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_kcontrol_chip(kcontrol);
9218c2ecf20Sopenharmony_ci        int reg = kcontrol->private_value & 0xff;
9228c2ecf20Sopenharmony_ci	int shift_left = (kcontrol->private_value >> 8) & 0x0f;
9238c2ecf20Sopenharmony_ci	int shift_right = (kcontrol->private_value >> 12) & 0x0f;
9248c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
9258c2ecf20Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 0xff;
9268c2ecf20Sopenharmony_ci	long *value = ucontrol->value.integer.value;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
9298c2ecf20Sopenharmony_ci	value[0] = (fm801_ioread16(chip, reg) >> shift_left) & mask;
9308c2ecf20Sopenharmony_ci	value[1] = (fm801_ioread16(chip, reg) >> shift_right) & mask;
9318c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
9328c2ecf20Sopenharmony_ci	if (invert) {
9338c2ecf20Sopenharmony_ci		value[0] = mask - value[0];
9348c2ecf20Sopenharmony_ci		value[1] = mask - value[1];
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci	return 0;
9378c2ecf20Sopenharmony_ci}
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_cistatic int snd_fm801_put_double(struct snd_kcontrol *kcontrol,
9408c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
9418c2ecf20Sopenharmony_ci{
9428c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_kcontrol_chip(kcontrol);
9438c2ecf20Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
9448c2ecf20Sopenharmony_ci	int shift_left = (kcontrol->private_value >> 8) & 0x0f;
9458c2ecf20Sopenharmony_ci	int shift_right = (kcontrol->private_value >> 12) & 0x0f;
9468c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
9478c2ecf20Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 0xff;
9488c2ecf20Sopenharmony_ci	unsigned short val1, val2;
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	val1 = ucontrol->value.integer.value[0] & mask;
9518c2ecf20Sopenharmony_ci	val2 = ucontrol->value.integer.value[1] & mask;
9528c2ecf20Sopenharmony_ci	if (invert) {
9538c2ecf20Sopenharmony_ci		val1 = mask - val1;
9548c2ecf20Sopenharmony_ci		val2 = mask - val2;
9558c2ecf20Sopenharmony_ci	}
9568c2ecf20Sopenharmony_ci	return snd_fm801_update_bits(chip, reg,
9578c2ecf20Sopenharmony_ci				     (mask << shift_left) | (mask << shift_right),
9588c2ecf20Sopenharmony_ci				     (val1 << shift_left ) | (val2 << shift_right));
9598c2ecf20Sopenharmony_ci}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_cistatic int snd_fm801_info_mux(struct snd_kcontrol *kcontrol,
9628c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_info *uinfo)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	static const char * const texts[5] = {
9658c2ecf20Sopenharmony_ci		"AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary"
9668c2ecf20Sopenharmony_ci	};
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 5, texts);
9698c2ecf20Sopenharmony_ci}
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_cistatic int snd_fm801_get_mux(struct snd_kcontrol *kcontrol,
9728c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_value *ucontrol)
9738c2ecf20Sopenharmony_ci{
9748c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_kcontrol_chip(kcontrol);
9758c2ecf20Sopenharmony_ci        unsigned short val;
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	val = fm801_readw(chip, REC_SRC) & 7;
9788c2ecf20Sopenharmony_ci	if (val > 4)
9798c2ecf20Sopenharmony_ci		val = 4;
9808c2ecf20Sopenharmony_ci        ucontrol->value.enumerated.item[0] = val;
9818c2ecf20Sopenharmony_ci        return 0;
9828c2ecf20Sopenharmony_ci}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_cistatic int snd_fm801_put_mux(struct snd_kcontrol *kcontrol,
9858c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_value *ucontrol)
9868c2ecf20Sopenharmony_ci{
9878c2ecf20Sopenharmony_ci	struct fm801 *chip = snd_kcontrol_chip(kcontrol);
9888c2ecf20Sopenharmony_ci        unsigned short val;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci        if ((val = ucontrol->value.enumerated.item[0]) > 4)
9918c2ecf20Sopenharmony_ci                return -EINVAL;
9928c2ecf20Sopenharmony_ci	return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val);
9938c2ecf20Sopenharmony_ci}
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci#define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls)
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_fm801_controls[] = {
10008c2ecf20Sopenharmony_ciFM801_DOUBLE_TLV("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1,
10018c2ecf20Sopenharmony_ci		 db_scale_dsp),
10028c2ecf20Sopenharmony_ciFM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1),
10038c2ecf20Sopenharmony_ciFM801_DOUBLE_TLV("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1,
10048c2ecf20Sopenharmony_ci		 db_scale_dsp),
10058c2ecf20Sopenharmony_ciFM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1),
10068c2ecf20Sopenharmony_ciFM801_DOUBLE_TLV("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1,
10078c2ecf20Sopenharmony_ci		 db_scale_dsp),
10088c2ecf20Sopenharmony_ciFM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1),
10098c2ecf20Sopenharmony_ci{
10108c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
10118c2ecf20Sopenharmony_ci	.name = "Digital Capture Source",
10128c2ecf20Sopenharmony_ci	.info = snd_fm801_info_mux,
10138c2ecf20Sopenharmony_ci	.get = snd_fm801_get_mux,
10148c2ecf20Sopenharmony_ci	.put = snd_fm801_put_mux,
10158c2ecf20Sopenharmony_ci}
10168c2ecf20Sopenharmony_ci};
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci#define FM801_CONTROLS_MULTI ARRAY_SIZE(snd_fm801_controls_multi)
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_fm801_controls_multi[] = {
10218c2ecf20Sopenharmony_ciFM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0),
10228c2ecf20Sopenharmony_ciFM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0),
10238c2ecf20Sopenharmony_ciFM801_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), FM801_I2S_MODE, 8, 1, 0),
10248c2ecf20Sopenharmony_ciFM801_SINGLE(SNDRV_CTL_NAME_IEC958("Raw Data ",PLAYBACK,SWITCH), FM801_I2S_MODE, 9, 1, 0),
10258c2ecf20Sopenharmony_ciFM801_SINGLE(SNDRV_CTL_NAME_IEC958("Raw Data ",CAPTURE,SWITCH), FM801_I2S_MODE, 10, 1, 0),
10268c2ecf20Sopenharmony_ciFM801_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), FM801_GEN_CTRL, 2, 1, 0),
10278c2ecf20Sopenharmony_ci};
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_cistatic void snd_fm801_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
10308c2ecf20Sopenharmony_ci{
10318c2ecf20Sopenharmony_ci	struct fm801 *chip = bus->private_data;
10328c2ecf20Sopenharmony_ci	chip->ac97_bus = NULL;
10338c2ecf20Sopenharmony_ci}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_cistatic void snd_fm801_mixer_free_ac97(struct snd_ac97 *ac97)
10368c2ecf20Sopenharmony_ci{
10378c2ecf20Sopenharmony_ci	struct fm801 *chip = ac97->private_data;
10388c2ecf20Sopenharmony_ci	if (ac97->num == 0) {
10398c2ecf20Sopenharmony_ci		chip->ac97 = NULL;
10408c2ecf20Sopenharmony_ci	} else {
10418c2ecf20Sopenharmony_ci		chip->ac97_sec = NULL;
10428c2ecf20Sopenharmony_ci	}
10438c2ecf20Sopenharmony_ci}
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_cistatic int snd_fm801_mixer(struct fm801 *chip)
10468c2ecf20Sopenharmony_ci{
10478c2ecf20Sopenharmony_ci	struct snd_ac97_template ac97;
10488c2ecf20Sopenharmony_ci	unsigned int i;
10498c2ecf20Sopenharmony_ci	int err;
10508c2ecf20Sopenharmony_ci	static const struct snd_ac97_bus_ops ops = {
10518c2ecf20Sopenharmony_ci		.write = snd_fm801_codec_write,
10528c2ecf20Sopenharmony_ci		.read = snd_fm801_codec_read,
10538c2ecf20Sopenharmony_ci	};
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
10568c2ecf20Sopenharmony_ci		return err;
10578c2ecf20Sopenharmony_ci	chip->ac97_bus->private_free = snd_fm801_mixer_free_ac97_bus;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	memset(&ac97, 0, sizeof(ac97));
10608c2ecf20Sopenharmony_ci	ac97.private_data = chip;
10618c2ecf20Sopenharmony_ci	ac97.private_free = snd_fm801_mixer_free_ac97;
10628c2ecf20Sopenharmony_ci	if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
10638c2ecf20Sopenharmony_ci		return err;
10648c2ecf20Sopenharmony_ci	if (chip->secondary) {
10658c2ecf20Sopenharmony_ci		ac97.num = 1;
10668c2ecf20Sopenharmony_ci		ac97.addr = chip->secondary_addr;
10678c2ecf20Sopenharmony_ci		if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_sec)) < 0)
10688c2ecf20Sopenharmony_ci			return err;
10698c2ecf20Sopenharmony_ci	}
10708c2ecf20Sopenharmony_ci	for (i = 0; i < FM801_CONTROLS; i++) {
10718c2ecf20Sopenharmony_ci		err = snd_ctl_add(chip->card,
10728c2ecf20Sopenharmony_ci			snd_ctl_new1(&snd_fm801_controls[i], chip));
10738c2ecf20Sopenharmony_ci		if (err < 0)
10748c2ecf20Sopenharmony_ci			return err;
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci	if (chip->multichannel) {
10778c2ecf20Sopenharmony_ci		for (i = 0; i < FM801_CONTROLS_MULTI; i++) {
10788c2ecf20Sopenharmony_ci			err = snd_ctl_add(chip->card,
10798c2ecf20Sopenharmony_ci				snd_ctl_new1(&snd_fm801_controls_multi[i], chip));
10808c2ecf20Sopenharmony_ci			if (err < 0)
10818c2ecf20Sopenharmony_ci				return err;
10828c2ecf20Sopenharmony_ci		}
10838c2ecf20Sopenharmony_ci	}
10848c2ecf20Sopenharmony_ci	return 0;
10858c2ecf20Sopenharmony_ci}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci/*
10888c2ecf20Sopenharmony_ci *  initialization routines
10898c2ecf20Sopenharmony_ci */
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_cistatic int wait_for_codec(struct fm801 *chip, unsigned int codec_id,
10928c2ecf20Sopenharmony_ci			  unsigned short reg, unsigned long waits)
10938c2ecf20Sopenharmony_ci{
10948c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + waits;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	fm801_writew(chip, AC97_CMD,
10978c2ecf20Sopenharmony_ci		     reg | (codec_id << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ);
10988c2ecf20Sopenharmony_ci	udelay(5);
10998c2ecf20Sopenharmony_ci	do {
11008c2ecf20Sopenharmony_ci		if ((fm801_readw(chip, AC97_CMD) &
11018c2ecf20Sopenharmony_ci		     (FM801_AC97_VALID | FM801_AC97_BUSY)) == FM801_AC97_VALID)
11028c2ecf20Sopenharmony_ci			return 0;
11038c2ecf20Sopenharmony_ci		schedule_timeout_uninterruptible(1);
11048c2ecf20Sopenharmony_ci	} while (time_after(timeout, jiffies));
11058c2ecf20Sopenharmony_ci	return -EIO;
11068c2ecf20Sopenharmony_ci}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_cistatic int reset_codec(struct fm801 *chip)
11098c2ecf20Sopenharmony_ci{
11108c2ecf20Sopenharmony_ci	/* codec cold reset + AC'97 warm reset */
11118c2ecf20Sopenharmony_ci	fm801_writew(chip, CODEC_CTRL, (1 << 5) | (1 << 6));
11128c2ecf20Sopenharmony_ci	fm801_readw(chip, CODEC_CTRL); /* flush posting data */
11138c2ecf20Sopenharmony_ci	udelay(100);
11148c2ecf20Sopenharmony_ci	fm801_writew(chip, CODEC_CTRL, 0);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	return wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750));
11178c2ecf20Sopenharmony_ci}
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_cistatic void snd_fm801_chip_multichannel_init(struct fm801 *chip)
11208c2ecf20Sopenharmony_ci{
11218c2ecf20Sopenharmony_ci	unsigned short cmdw;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	if (chip->multichannel) {
11248c2ecf20Sopenharmony_ci		if (chip->secondary_addr) {
11258c2ecf20Sopenharmony_ci			wait_for_codec(chip, chip->secondary_addr,
11268c2ecf20Sopenharmony_ci				       AC97_VENDOR_ID1, msecs_to_jiffies(50));
11278c2ecf20Sopenharmony_ci		} else {
11288c2ecf20Sopenharmony_ci			/* my card has the secondary codec */
11298c2ecf20Sopenharmony_ci			/* at address #3, so the loop is inverted */
11308c2ecf20Sopenharmony_ci			int i;
11318c2ecf20Sopenharmony_ci			for (i = 3; i > 0; i--) {
11328c2ecf20Sopenharmony_ci				if (!wait_for_codec(chip, i, AC97_VENDOR_ID1,
11338c2ecf20Sopenharmony_ci						     msecs_to_jiffies(50))) {
11348c2ecf20Sopenharmony_ci					cmdw = fm801_readw(chip, AC97_DATA);
11358c2ecf20Sopenharmony_ci					if (cmdw != 0xffff && cmdw != 0) {
11368c2ecf20Sopenharmony_ci						chip->secondary = 1;
11378c2ecf20Sopenharmony_ci						chip->secondary_addr = i;
11388c2ecf20Sopenharmony_ci						break;
11398c2ecf20Sopenharmony_ci					}
11408c2ecf20Sopenharmony_ci				}
11418c2ecf20Sopenharmony_ci			}
11428c2ecf20Sopenharmony_ci		}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci		/* the recovery phase, it seems that probing for non-existing codec might */
11458c2ecf20Sopenharmony_ci		/* cause timeout problems */
11468c2ecf20Sopenharmony_ci		wait_for_codec(chip, 0, AC97_VENDOR_ID1, msecs_to_jiffies(750));
11478c2ecf20Sopenharmony_ci	}
11488c2ecf20Sopenharmony_ci}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_cistatic void snd_fm801_chip_init(struct fm801 *chip)
11518c2ecf20Sopenharmony_ci{
11528c2ecf20Sopenharmony_ci	unsigned short cmdw;
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	/* init volume */
11558c2ecf20Sopenharmony_ci	fm801_writew(chip, PCM_VOL, 0x0808);
11568c2ecf20Sopenharmony_ci	fm801_writew(chip, FM_VOL, 0x9f1f);
11578c2ecf20Sopenharmony_ci	fm801_writew(chip, I2S_VOL, 0x8808);
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	/* I2S control - I2S mode */
11608c2ecf20Sopenharmony_ci	fm801_writew(chip, I2S_MODE, 0x0003);
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	/* interrupt setup */
11638c2ecf20Sopenharmony_ci	cmdw = fm801_readw(chip, IRQ_MASK);
11648c2ecf20Sopenharmony_ci	if (chip->irq < 0)
11658c2ecf20Sopenharmony_ci		cmdw |= 0x00c3;		/* mask everything, no PCM nor MPU */
11668c2ecf20Sopenharmony_ci	else
11678c2ecf20Sopenharmony_ci		cmdw &= ~0x0083;	/* unmask MPU, PLAYBACK & CAPTURE */
11688c2ecf20Sopenharmony_ci	fm801_writew(chip, IRQ_MASK, cmdw);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	/* interrupt clear */
11718c2ecf20Sopenharmony_ci	fm801_writew(chip, IRQ_STATUS,
11728c2ecf20Sopenharmony_ci		     FM801_IRQ_PLAYBACK | FM801_IRQ_CAPTURE | FM801_IRQ_MPU);
11738c2ecf20Sopenharmony_ci}
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_cistatic int snd_fm801_free(struct fm801 *chip)
11768c2ecf20Sopenharmony_ci{
11778c2ecf20Sopenharmony_ci	unsigned short cmdw;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	if (chip->irq < 0)
11808c2ecf20Sopenharmony_ci		goto __end_hw;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	/* interrupt setup - mask everything */
11838c2ecf20Sopenharmony_ci	cmdw = fm801_readw(chip, IRQ_MASK);
11848c2ecf20Sopenharmony_ci	cmdw |= 0x00c3;
11858c2ecf20Sopenharmony_ci	fm801_writew(chip, IRQ_MASK, cmdw);
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	devm_free_irq(chip->dev, chip->irq, chip);
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci      __end_hw:
11908c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_FM801_TEA575X_BOOL
11918c2ecf20Sopenharmony_ci	if (!(chip->tea575x_tuner & TUNER_DISABLED)) {
11928c2ecf20Sopenharmony_ci		snd_tea575x_exit(&chip->tea);
11938c2ecf20Sopenharmony_ci		v4l2_device_unregister(&chip->v4l2_dev);
11948c2ecf20Sopenharmony_ci	}
11958c2ecf20Sopenharmony_ci#endif
11968c2ecf20Sopenharmony_ci	return 0;
11978c2ecf20Sopenharmony_ci}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_cistatic int snd_fm801_dev_free(struct snd_device *device)
12008c2ecf20Sopenharmony_ci{
12018c2ecf20Sopenharmony_ci	struct fm801 *chip = device->device_data;
12028c2ecf20Sopenharmony_ci	return snd_fm801_free(chip);
12038c2ecf20Sopenharmony_ci}
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_cistatic int snd_fm801_create(struct snd_card *card,
12068c2ecf20Sopenharmony_ci			    struct pci_dev *pci,
12078c2ecf20Sopenharmony_ci			    int tea575x_tuner,
12088c2ecf20Sopenharmony_ci			    int radio_nr,
12098c2ecf20Sopenharmony_ci			    struct fm801 **rchip)
12108c2ecf20Sopenharmony_ci{
12118c2ecf20Sopenharmony_ci	struct fm801 *chip;
12128c2ecf20Sopenharmony_ci	int err;
12138c2ecf20Sopenharmony_ci	static const struct snd_device_ops ops = {
12148c2ecf20Sopenharmony_ci		.dev_free =	snd_fm801_dev_free,
12158c2ecf20Sopenharmony_ci	};
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	*rchip = NULL;
12188c2ecf20Sopenharmony_ci	if ((err = pcim_enable_device(pci)) < 0)
12198c2ecf20Sopenharmony_ci		return err;
12208c2ecf20Sopenharmony_ci	chip = devm_kzalloc(&pci->dev, sizeof(*chip), GFP_KERNEL);
12218c2ecf20Sopenharmony_ci	if (chip == NULL)
12228c2ecf20Sopenharmony_ci		return -ENOMEM;
12238c2ecf20Sopenharmony_ci	spin_lock_init(&chip->reg_lock);
12248c2ecf20Sopenharmony_ci	chip->card = card;
12258c2ecf20Sopenharmony_ci	chip->dev = &pci->dev;
12268c2ecf20Sopenharmony_ci	chip->irq = -1;
12278c2ecf20Sopenharmony_ci	chip->tea575x_tuner = tea575x_tuner;
12288c2ecf20Sopenharmony_ci	if ((err = pci_request_regions(pci, "FM801")) < 0)
12298c2ecf20Sopenharmony_ci		return err;
12308c2ecf20Sopenharmony_ci	chip->port = pci_resource_start(pci, 0);
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	if (pci->revision >= 0xb1)	/* FM801-AU */
12338c2ecf20Sopenharmony_ci		chip->multichannel = 1;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	if (!(chip->tea575x_tuner & TUNER_ONLY)) {
12368c2ecf20Sopenharmony_ci		if (reset_codec(chip) < 0) {
12378c2ecf20Sopenharmony_ci			dev_info(chip->card->dev,
12388c2ecf20Sopenharmony_ci				 "Primary AC'97 codec not found, assume SF64-PCR (tuner-only)\n");
12398c2ecf20Sopenharmony_ci			chip->tea575x_tuner = 3 | TUNER_ONLY;
12408c2ecf20Sopenharmony_ci		} else {
12418c2ecf20Sopenharmony_ci			snd_fm801_chip_multichannel_init(chip);
12428c2ecf20Sopenharmony_ci		}
12438c2ecf20Sopenharmony_ci	}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	if ((chip->tea575x_tuner & TUNER_ONLY) == 0) {
12468c2ecf20Sopenharmony_ci		if (devm_request_irq(&pci->dev, pci->irq, snd_fm801_interrupt,
12478c2ecf20Sopenharmony_ci				IRQF_SHARED, KBUILD_MODNAME, chip)) {
12488c2ecf20Sopenharmony_ci			dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
12498c2ecf20Sopenharmony_ci			snd_fm801_free(chip);
12508c2ecf20Sopenharmony_ci			return -EBUSY;
12518c2ecf20Sopenharmony_ci		}
12528c2ecf20Sopenharmony_ci		chip->irq = pci->irq;
12538c2ecf20Sopenharmony_ci		card->sync_irq = chip->irq;
12548c2ecf20Sopenharmony_ci		pci_set_master(pci);
12558c2ecf20Sopenharmony_ci	}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	snd_fm801_chip_init(chip);
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
12608c2ecf20Sopenharmony_ci		snd_fm801_free(chip);
12618c2ecf20Sopenharmony_ci		return err;
12628c2ecf20Sopenharmony_ci	}
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_FM801_TEA575X_BOOL
12658c2ecf20Sopenharmony_ci	err = v4l2_device_register(&pci->dev, &chip->v4l2_dev);
12668c2ecf20Sopenharmony_ci	if (err < 0) {
12678c2ecf20Sopenharmony_ci		snd_fm801_free(chip);
12688c2ecf20Sopenharmony_ci		return err;
12698c2ecf20Sopenharmony_ci	}
12708c2ecf20Sopenharmony_ci	chip->tea.v4l2_dev = &chip->v4l2_dev;
12718c2ecf20Sopenharmony_ci	chip->tea.radio_nr = radio_nr;
12728c2ecf20Sopenharmony_ci	chip->tea.private_data = chip;
12738c2ecf20Sopenharmony_ci	chip->tea.ops = &snd_fm801_tea_ops;
12748c2ecf20Sopenharmony_ci	sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
12758c2ecf20Sopenharmony_ci	if ((chip->tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
12768c2ecf20Sopenharmony_ci	    (chip->tea575x_tuner & TUNER_TYPE_MASK) < 4) {
12778c2ecf20Sopenharmony_ci		if (snd_tea575x_init(&chip->tea, THIS_MODULE)) {
12788c2ecf20Sopenharmony_ci			dev_err(card->dev, "TEA575x radio not found\n");
12798c2ecf20Sopenharmony_ci			snd_fm801_free(chip);
12808c2ecf20Sopenharmony_ci			return -ENODEV;
12818c2ecf20Sopenharmony_ci		}
12828c2ecf20Sopenharmony_ci	} else if ((chip->tea575x_tuner & TUNER_TYPE_MASK) == 0) {
12838c2ecf20Sopenharmony_ci		unsigned int tuner_only = chip->tea575x_tuner & TUNER_ONLY;
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci		/* autodetect tuner connection */
12868c2ecf20Sopenharmony_ci		for (tea575x_tuner = 1; tea575x_tuner <= 3; tea575x_tuner++) {
12878c2ecf20Sopenharmony_ci			chip->tea575x_tuner = tea575x_tuner;
12888c2ecf20Sopenharmony_ci			if (!snd_tea575x_init(&chip->tea, THIS_MODULE)) {
12898c2ecf20Sopenharmony_ci				dev_info(card->dev,
12908c2ecf20Sopenharmony_ci					 "detected TEA575x radio type %s\n",
12918c2ecf20Sopenharmony_ci					   get_tea575x_gpio(chip)->name);
12928c2ecf20Sopenharmony_ci				break;
12938c2ecf20Sopenharmony_ci			}
12948c2ecf20Sopenharmony_ci		}
12958c2ecf20Sopenharmony_ci		if (tea575x_tuner == 4) {
12968c2ecf20Sopenharmony_ci			dev_err(card->dev, "TEA575x radio not found\n");
12978c2ecf20Sopenharmony_ci			chip->tea575x_tuner = TUNER_DISABLED;
12988c2ecf20Sopenharmony_ci		}
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci		chip->tea575x_tuner |= tuner_only;
13018c2ecf20Sopenharmony_ci	}
13028c2ecf20Sopenharmony_ci	if (!(chip->tea575x_tuner & TUNER_DISABLED)) {
13038c2ecf20Sopenharmony_ci		strlcpy(chip->tea.card, get_tea575x_gpio(chip)->name,
13048c2ecf20Sopenharmony_ci			sizeof(chip->tea.card));
13058c2ecf20Sopenharmony_ci	}
13068c2ecf20Sopenharmony_ci#endif
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	*rchip = chip;
13098c2ecf20Sopenharmony_ci	return 0;
13108c2ecf20Sopenharmony_ci}
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_cistatic int snd_card_fm801_probe(struct pci_dev *pci,
13138c2ecf20Sopenharmony_ci				const struct pci_device_id *pci_id)
13148c2ecf20Sopenharmony_ci{
13158c2ecf20Sopenharmony_ci	static int dev;
13168c2ecf20Sopenharmony_ci	struct snd_card *card;
13178c2ecf20Sopenharmony_ci	struct fm801 *chip;
13188c2ecf20Sopenharmony_ci	struct snd_opl3 *opl3;
13198c2ecf20Sopenharmony_ci	int err;
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci        if (dev >= SNDRV_CARDS)
13228c2ecf20Sopenharmony_ci                return -ENODEV;
13238c2ecf20Sopenharmony_ci	if (!enable[dev]) {
13248c2ecf20Sopenharmony_ci		dev++;
13258c2ecf20Sopenharmony_ci		return -ENOENT;
13268c2ecf20Sopenharmony_ci	}
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
13298c2ecf20Sopenharmony_ci			   0, &card);
13308c2ecf20Sopenharmony_ci	if (err < 0)
13318c2ecf20Sopenharmony_ci		return err;
13328c2ecf20Sopenharmony_ci	if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], radio_nr[dev], &chip)) < 0) {
13338c2ecf20Sopenharmony_ci		snd_card_free(card);
13348c2ecf20Sopenharmony_ci		return err;
13358c2ecf20Sopenharmony_ci	}
13368c2ecf20Sopenharmony_ci	card->private_data = chip;
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	strcpy(card->driver, "FM801");
13398c2ecf20Sopenharmony_ci	strcpy(card->shortname, "ForteMedia FM801-");
13408c2ecf20Sopenharmony_ci	strcat(card->shortname, chip->multichannel ? "AU" : "AS");
13418c2ecf20Sopenharmony_ci	sprintf(card->longname, "%s at 0x%lx, irq %i",
13428c2ecf20Sopenharmony_ci		card->shortname, chip->port, chip->irq);
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	if (chip->tea575x_tuner & TUNER_ONLY)
13458c2ecf20Sopenharmony_ci		goto __fm801_tuner_only;
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	if ((err = snd_fm801_pcm(chip, 0)) < 0) {
13488c2ecf20Sopenharmony_ci		snd_card_free(card);
13498c2ecf20Sopenharmony_ci		return err;
13508c2ecf20Sopenharmony_ci	}
13518c2ecf20Sopenharmony_ci	if ((err = snd_fm801_mixer(chip)) < 0) {
13528c2ecf20Sopenharmony_ci		snd_card_free(card);
13538c2ecf20Sopenharmony_ci		return err;
13548c2ecf20Sopenharmony_ci	}
13558c2ecf20Sopenharmony_ci	if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
13568c2ecf20Sopenharmony_ci				       chip->port + FM801_MPU401_DATA,
13578c2ecf20Sopenharmony_ci				       MPU401_INFO_INTEGRATED |
13588c2ecf20Sopenharmony_ci				       MPU401_INFO_IRQ_HOOK,
13598c2ecf20Sopenharmony_ci				       -1, &chip->rmidi)) < 0) {
13608c2ecf20Sopenharmony_ci		snd_card_free(card);
13618c2ecf20Sopenharmony_ci		return err;
13628c2ecf20Sopenharmony_ci	}
13638c2ecf20Sopenharmony_ci	if ((err = snd_opl3_create(card, chip->port + FM801_OPL3_BANK0,
13648c2ecf20Sopenharmony_ci				   chip->port + FM801_OPL3_BANK1,
13658c2ecf20Sopenharmony_ci				   OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) {
13668c2ecf20Sopenharmony_ci		snd_card_free(card);
13678c2ecf20Sopenharmony_ci		return err;
13688c2ecf20Sopenharmony_ci	}
13698c2ecf20Sopenharmony_ci	if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
13708c2ecf20Sopenharmony_ci		snd_card_free(card);
13718c2ecf20Sopenharmony_ci		return err;
13728c2ecf20Sopenharmony_ci	}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci      __fm801_tuner_only:
13758c2ecf20Sopenharmony_ci	if ((err = snd_card_register(card)) < 0) {
13768c2ecf20Sopenharmony_ci		snd_card_free(card);
13778c2ecf20Sopenharmony_ci		return err;
13788c2ecf20Sopenharmony_ci	}
13798c2ecf20Sopenharmony_ci	pci_set_drvdata(pci, card);
13808c2ecf20Sopenharmony_ci	dev++;
13818c2ecf20Sopenharmony_ci	return 0;
13828c2ecf20Sopenharmony_ci}
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_cistatic void snd_card_fm801_remove(struct pci_dev *pci)
13858c2ecf20Sopenharmony_ci{
13868c2ecf20Sopenharmony_ci	snd_card_free(pci_get_drvdata(pci));
13878c2ecf20Sopenharmony_ci}
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
13908c2ecf20Sopenharmony_cistatic const unsigned char saved_regs[] = {
13918c2ecf20Sopenharmony_ci	FM801_PCM_VOL, FM801_I2S_VOL, FM801_FM_VOL, FM801_REC_SRC,
13928c2ecf20Sopenharmony_ci	FM801_PLY_CTRL, FM801_PLY_COUNT, FM801_PLY_BUF1, FM801_PLY_BUF2,
13938c2ecf20Sopenharmony_ci	FM801_CAP_CTRL, FM801_CAP_COUNT, FM801_CAP_BUF1, FM801_CAP_BUF2,
13948c2ecf20Sopenharmony_ci	FM801_CODEC_CTRL, FM801_I2S_MODE, FM801_VOLUME, FM801_GEN_CTRL,
13958c2ecf20Sopenharmony_ci};
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_cistatic int snd_fm801_suspend(struct device *dev)
13988c2ecf20Sopenharmony_ci{
13998c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
14008c2ecf20Sopenharmony_ci	struct fm801 *chip = card->private_data;
14018c2ecf20Sopenharmony_ci	int i;
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
14068c2ecf20Sopenharmony_ci		chip->saved_regs[i] = fm801_ioread16(chip, saved_regs[i]);
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	if (chip->tea575x_tuner & TUNER_ONLY) {
14098c2ecf20Sopenharmony_ci		/* FIXME: tea575x suspend */
14108c2ecf20Sopenharmony_ci	} else {
14118c2ecf20Sopenharmony_ci		snd_ac97_suspend(chip->ac97);
14128c2ecf20Sopenharmony_ci		snd_ac97_suspend(chip->ac97_sec);
14138c2ecf20Sopenharmony_ci	}
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	return 0;
14168c2ecf20Sopenharmony_ci}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_cistatic int snd_fm801_resume(struct device *dev)
14198c2ecf20Sopenharmony_ci{
14208c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
14218c2ecf20Sopenharmony_ci	struct fm801 *chip = card->private_data;
14228c2ecf20Sopenharmony_ci	int i;
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	if (chip->tea575x_tuner & TUNER_ONLY) {
14258c2ecf20Sopenharmony_ci		snd_fm801_chip_init(chip);
14268c2ecf20Sopenharmony_ci	} else {
14278c2ecf20Sopenharmony_ci		reset_codec(chip);
14288c2ecf20Sopenharmony_ci		snd_fm801_chip_multichannel_init(chip);
14298c2ecf20Sopenharmony_ci		snd_fm801_chip_init(chip);
14308c2ecf20Sopenharmony_ci		snd_ac97_resume(chip->ac97);
14318c2ecf20Sopenharmony_ci		snd_ac97_resume(chip->ac97_sec);
14328c2ecf20Sopenharmony_ci	}
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
14358c2ecf20Sopenharmony_ci		fm801_iowrite16(chip, saved_regs[i], chip->saved_regs[i]);
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_FM801_TEA575X_BOOL
14388c2ecf20Sopenharmony_ci	if (!(chip->tea575x_tuner & TUNER_DISABLED))
14398c2ecf20Sopenharmony_ci		snd_tea575x_set_freq(&chip->tea);
14408c2ecf20Sopenharmony_ci#endif
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
14438c2ecf20Sopenharmony_ci	return 0;
14448c2ecf20Sopenharmony_ci}
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume);
14478c2ecf20Sopenharmony_ci#define SND_FM801_PM_OPS	&snd_fm801_pm
14488c2ecf20Sopenharmony_ci#else
14498c2ecf20Sopenharmony_ci#define SND_FM801_PM_OPS	NULL
14508c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_cistatic struct pci_driver fm801_driver = {
14538c2ecf20Sopenharmony_ci	.name = KBUILD_MODNAME,
14548c2ecf20Sopenharmony_ci	.id_table = snd_fm801_ids,
14558c2ecf20Sopenharmony_ci	.probe = snd_card_fm801_probe,
14568c2ecf20Sopenharmony_ci	.remove = snd_card_fm801_remove,
14578c2ecf20Sopenharmony_ci	.driver = {
14588c2ecf20Sopenharmony_ci		.pm = SND_FM801_PM_OPS,
14598c2ecf20Sopenharmony_ci	},
14608c2ecf20Sopenharmony_ci};
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_cimodule_pci_driver(fm801_driver);
1463