18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *   ALSA driver for ICEnsemble ICE1712 (Envy24)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *	Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci  NOTES:
108c2ecf20Sopenharmony_ci  - spdif nonaudio consumer mode does not work (at least with my
118c2ecf20Sopenharmony_ci    Sony STR-DB830)
128c2ecf20Sopenharmony_ci*/
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/*
158c2ecf20Sopenharmony_ci * Changes:
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci *  2002.09.09	Takashi Iwai <tiwai@suse.de>
188c2ecf20Sopenharmony_ci *	split the code to several files.  each low-level routine
198c2ecf20Sopenharmony_ci *	is stored in the local file and called from registration
208c2ecf20Sopenharmony_ci *	function from card_info struct.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci *  2002.11.26	James Stafford <jstafford@ampltd.com>
238c2ecf20Sopenharmony_ci *	Added support for VT1724 (Envy24HT)
248c2ecf20Sopenharmony_ci *	I have left out support for 176.4 and 192 KHz for the moment.
258c2ecf20Sopenharmony_ci *  I also haven't done anything with the internal S/PDIF transmitter or the MPU-401
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci *  2003.02.20  Taksahi Iwai <tiwai@suse.de>
288c2ecf20Sopenharmony_ci *	Split vt1724 part to an independent driver.
298c2ecf20Sopenharmony_ci *	The GPIO is accessed through the callback functions now.
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * 2004.03.31 Doug McLain <nostar@comcast.net>
328c2ecf20Sopenharmony_ci *    Added support for Event Electronics EZ8 card to hoontech.c.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#include <linux/delay.h>
378c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
388c2ecf20Sopenharmony_ci#include <linux/init.h>
398c2ecf20Sopenharmony_ci#include <linux/pci.h>
408c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
418c2ecf20Sopenharmony_ci#include <linux/slab.h>
428c2ecf20Sopenharmony_ci#include <linux/module.h>
438c2ecf20Sopenharmony_ci#include <linux/mutex.h>
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#include <sound/core.h>
468c2ecf20Sopenharmony_ci#include <sound/cs8427.h>
478c2ecf20Sopenharmony_ci#include <sound/info.h>
488c2ecf20Sopenharmony_ci#include <sound/initval.h>
498c2ecf20Sopenharmony_ci#include <sound/tlv.h>
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#include <sound/asoundef.h>
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#include "ice1712.h"
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/* lowlevel routines */
568c2ecf20Sopenharmony_ci#include "delta.h"
578c2ecf20Sopenharmony_ci#include "ews.h"
588c2ecf20Sopenharmony_ci#include "hoontech.h"
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
618c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)");
628c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
638c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{"
648c2ecf20Sopenharmony_ci	       HOONTECH_DEVICE_DESC
658c2ecf20Sopenharmony_ci	       DELTA_DEVICE_DESC
668c2ecf20Sopenharmony_ci	       EWS_DEVICE_DESC
678c2ecf20Sopenharmony_ci	       "{ICEnsemble,Generic ICE1712},"
688c2ecf20Sopenharmony_ci	       "{ICEnsemble,Generic Envy24}}");
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
718c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
728c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
738c2ecf20Sopenharmony_cistatic char *model[SNDRV_CARDS];
748c2ecf20Sopenharmony_cistatic bool omni[SNDRV_CARDS];				/* Delta44 & 66 Omni I/O support */
758c2ecf20Sopenharmony_cistatic int cs8427_timeout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 500}; /* CS8427 S/PDIF transceiver reset timeout value in msec */
768c2ecf20Sopenharmony_cistatic int dxr_enable[SNDRV_CARDS];			/* DXR enable for DMX6FIRE */
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
798c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for ICE1712 soundcard.");
808c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
818c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for ICE1712 soundcard.");
828c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
838c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable ICE1712 soundcard.");
848c2ecf20Sopenharmony_cimodule_param_array(omni, bool, NULL, 0444);
858c2ecf20Sopenharmony_ciMODULE_PARM_DESC(omni, "Enable Midiman M-Audio Delta Omni I/O support.");
868c2ecf20Sopenharmony_cimodule_param_array(cs8427_timeout, int, NULL, 0444);
878c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cs8427_timeout, "Define reset timeout for cs8427 chip in msec resolution.");
888c2ecf20Sopenharmony_cimodule_param_array(model, charp, NULL, 0444);
898c2ecf20Sopenharmony_ciMODULE_PARM_DESC(model, "Use the given board model.");
908c2ecf20Sopenharmony_cimodule_param_array(dxr_enable, int, NULL, 0444);
918c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dxr_enable, "Enable DXR support for Terratec DMX6FIRE.");
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_ice1712_ids[] = {
958c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(ICE, PCI_DEVICE_ID_ICE_1712), 0 },   /* ICE1712 */
968c2ecf20Sopenharmony_ci	{ 0, }
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_ice1712_ids);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int snd_ice1712_build_pro_mixer(struct snd_ice1712 *ice);
1028c2ecf20Sopenharmony_cistatic int snd_ice1712_build_controls(struct snd_ice1712 *ice);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int PRO_RATE_LOCKED;
1058c2ecf20Sopenharmony_cistatic int PRO_RATE_RESET = 1;
1068c2ecf20Sopenharmony_cistatic unsigned int PRO_RATE_DEFAULT = 44100;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/*
1098c2ecf20Sopenharmony_ci *  Basic I/O
1108c2ecf20Sopenharmony_ci */
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/* check whether the clock mode is spdif-in */
1138c2ecf20Sopenharmony_cistatic inline int is_spdif_master(struct snd_ice1712 *ice)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	return (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER) ? 1 : 0;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic inline int is_pro_rate_locked(struct snd_ice1712 *ice)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	return is_spdif_master(ice) || PRO_RATE_LOCKED;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic inline void snd_ice1712_ds_write(struct snd_ice1712 *ice, u8 channel, u8 addr, u32 data)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	outb((channel << 4) | addr, ICEDS(ice, INDEX));
1268c2ecf20Sopenharmony_ci	outl(data, ICEDS(ice, DATA));
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic inline u32 snd_ice1712_ds_read(struct snd_ice1712 *ice, u8 channel, u8 addr)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	outb((channel << 4) | addr, ICEDS(ice, INDEX));
1328c2ecf20Sopenharmony_ci	return inl(ICEDS(ice, DATA));
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic void snd_ice1712_ac97_write(struct snd_ac97 *ac97,
1368c2ecf20Sopenharmony_ci				   unsigned short reg,
1378c2ecf20Sopenharmony_ci				   unsigned short val)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = ac97->private_data;
1408c2ecf20Sopenharmony_ci	int tm;
1418c2ecf20Sopenharmony_ci	unsigned char old_cmd = 0;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	for (tm = 0; tm < 0x10000; tm++) {
1448c2ecf20Sopenharmony_ci		old_cmd = inb(ICEREG(ice, AC97_CMD));
1458c2ecf20Sopenharmony_ci		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
1468c2ecf20Sopenharmony_ci			continue;
1478c2ecf20Sopenharmony_ci		if (!(old_cmd & ICE1712_AC97_READY))
1488c2ecf20Sopenharmony_ci			continue;
1498c2ecf20Sopenharmony_ci		break;
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci	outb(reg, ICEREG(ice, AC97_INDEX));
1528c2ecf20Sopenharmony_ci	outw(val, ICEREG(ice, AC97_DATA));
1538c2ecf20Sopenharmony_ci	old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);
1548c2ecf20Sopenharmony_ci	outb(old_cmd | ICE1712_AC97_WRITE, ICEREG(ice, AC97_CMD));
1558c2ecf20Sopenharmony_ci	for (tm = 0; tm < 0x10000; tm++)
1568c2ecf20Sopenharmony_ci		if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)
1578c2ecf20Sopenharmony_ci			break;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic unsigned short snd_ice1712_ac97_read(struct snd_ac97 *ac97,
1618c2ecf20Sopenharmony_ci					    unsigned short reg)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = ac97->private_data;
1648c2ecf20Sopenharmony_ci	int tm;
1658c2ecf20Sopenharmony_ci	unsigned char old_cmd = 0;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	for (tm = 0; tm < 0x10000; tm++) {
1688c2ecf20Sopenharmony_ci		old_cmd = inb(ICEREG(ice, AC97_CMD));
1698c2ecf20Sopenharmony_ci		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
1708c2ecf20Sopenharmony_ci			continue;
1718c2ecf20Sopenharmony_ci		if (!(old_cmd & ICE1712_AC97_READY))
1728c2ecf20Sopenharmony_ci			continue;
1738c2ecf20Sopenharmony_ci		break;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci	outb(reg, ICEREG(ice, AC97_INDEX));
1768c2ecf20Sopenharmony_ci	outb(old_cmd | ICE1712_AC97_READ, ICEREG(ice, AC97_CMD));
1778c2ecf20Sopenharmony_ci	for (tm = 0; tm < 0x10000; tm++)
1788c2ecf20Sopenharmony_ci		if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)
1798c2ecf20Sopenharmony_ci			break;
1808c2ecf20Sopenharmony_ci	if (tm >= 0x10000)		/* timeout */
1818c2ecf20Sopenharmony_ci		return ~0;
1828c2ecf20Sopenharmony_ci	return inw(ICEREG(ice, AC97_DATA));
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci/*
1868c2ecf20Sopenharmony_ci * pro ac97 section
1878c2ecf20Sopenharmony_ci */
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic void snd_ice1712_pro_ac97_write(struct snd_ac97 *ac97,
1908c2ecf20Sopenharmony_ci				       unsigned short reg,
1918c2ecf20Sopenharmony_ci				       unsigned short val)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = ac97->private_data;
1948c2ecf20Sopenharmony_ci	int tm;
1958c2ecf20Sopenharmony_ci	unsigned char old_cmd = 0;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	for (tm = 0; tm < 0x10000; tm++) {
1988c2ecf20Sopenharmony_ci		old_cmd = inb(ICEMT(ice, AC97_CMD));
1998c2ecf20Sopenharmony_ci		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
2008c2ecf20Sopenharmony_ci			continue;
2018c2ecf20Sopenharmony_ci		if (!(old_cmd & ICE1712_AC97_READY))
2028c2ecf20Sopenharmony_ci			continue;
2038c2ecf20Sopenharmony_ci		break;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci	outb(reg, ICEMT(ice, AC97_INDEX));
2068c2ecf20Sopenharmony_ci	outw(val, ICEMT(ice, AC97_DATA));
2078c2ecf20Sopenharmony_ci	old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);
2088c2ecf20Sopenharmony_ci	outb(old_cmd | ICE1712_AC97_WRITE, ICEMT(ice, AC97_CMD));
2098c2ecf20Sopenharmony_ci	for (tm = 0; tm < 0x10000; tm++)
2108c2ecf20Sopenharmony_ci		if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)
2118c2ecf20Sopenharmony_ci			break;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic unsigned short snd_ice1712_pro_ac97_read(struct snd_ac97 *ac97,
2168c2ecf20Sopenharmony_ci						unsigned short reg)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = ac97->private_data;
2198c2ecf20Sopenharmony_ci	int tm;
2208c2ecf20Sopenharmony_ci	unsigned char old_cmd = 0;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	for (tm = 0; tm < 0x10000; tm++) {
2238c2ecf20Sopenharmony_ci		old_cmd = inb(ICEMT(ice, AC97_CMD));
2248c2ecf20Sopenharmony_ci		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
2258c2ecf20Sopenharmony_ci			continue;
2268c2ecf20Sopenharmony_ci		if (!(old_cmd & ICE1712_AC97_READY))
2278c2ecf20Sopenharmony_ci			continue;
2288c2ecf20Sopenharmony_ci		break;
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci	outb(reg, ICEMT(ice, AC97_INDEX));
2318c2ecf20Sopenharmony_ci	outb(old_cmd | ICE1712_AC97_READ, ICEMT(ice, AC97_CMD));
2328c2ecf20Sopenharmony_ci	for (tm = 0; tm < 0x10000; tm++)
2338c2ecf20Sopenharmony_ci		if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)
2348c2ecf20Sopenharmony_ci			break;
2358c2ecf20Sopenharmony_ci	if (tm >= 0x10000)		/* timeout */
2368c2ecf20Sopenharmony_ci		return ~0;
2378c2ecf20Sopenharmony_ci	return inw(ICEMT(ice, AC97_DATA));
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/*
2418c2ecf20Sopenharmony_ci * consumer ac97 digital mix
2428c2ecf20Sopenharmony_ci */
2438c2ecf20Sopenharmony_ci#define snd_ice1712_digmix_route_ac97_info	snd_ctl_boolean_mono_info
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic int snd_ice1712_digmix_route_ac97_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0;
2508c2ecf20Sopenharmony_ci	return 0;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic int snd_ice1712_digmix_route_ac97_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
2568c2ecf20Sopenharmony_ci	unsigned char val, nval;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
2598c2ecf20Sopenharmony_ci	val = inb(ICEMT(ice, MONITOR_ROUTECTRL));
2608c2ecf20Sopenharmony_ci	nval = val & ~ICE1712_ROUTE_AC97;
2618c2ecf20Sopenharmony_ci	if (ucontrol->value.integer.value[0])
2628c2ecf20Sopenharmony_ci		nval |= ICE1712_ROUTE_AC97;
2638c2ecf20Sopenharmony_ci	outb(nval, ICEMT(ice, MONITOR_ROUTECTRL));
2648c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
2658c2ecf20Sopenharmony_ci	return val != nval;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_mixer_digmix_route_ac97 = {
2698c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2708c2ecf20Sopenharmony_ci	.name = "Digital Mixer To AC97",
2718c2ecf20Sopenharmony_ci	.info = snd_ice1712_digmix_route_ac97_info,
2728c2ecf20Sopenharmony_ci	.get = snd_ice1712_digmix_route_ac97_get,
2738c2ecf20Sopenharmony_ci	.put = snd_ice1712_digmix_route_ac97_put,
2748c2ecf20Sopenharmony_ci};
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci/*
2788c2ecf20Sopenharmony_ci * gpio operations
2798c2ecf20Sopenharmony_ci */
2808c2ecf20Sopenharmony_cistatic void snd_ice1712_set_gpio_dir(struct snd_ice1712 *ice, unsigned int data)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, data);
2838c2ecf20Sopenharmony_ci	inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic unsigned int snd_ice1712_get_gpio_dir(struct snd_ice1712 *ice)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DIRECTION);
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic unsigned int snd_ice1712_get_gpio_mask(struct snd_ice1712 *ice)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	return snd_ice1712_read(ice, ICE1712_IREG_GPIO_WRITE_MASK);
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic void snd_ice1712_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, data);
2998c2ecf20Sopenharmony_ci	inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic unsigned int snd_ice1712_get_gpio_data(struct snd_ice1712 *ice)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic void snd_ice1712_set_gpio_data(struct snd_ice1712 *ice, unsigned int val)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, val);
3108c2ecf20Sopenharmony_ci	inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci/*
3148c2ecf20Sopenharmony_ci *
3158c2ecf20Sopenharmony_ci * CS8427 interface
3168c2ecf20Sopenharmony_ci *
3178c2ecf20Sopenharmony_ci */
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci/*
3208c2ecf20Sopenharmony_ci * change the input clock selection
3218c2ecf20Sopenharmony_ci * spdif_clock = 1 - IEC958 input, 0 - Envy24
3228c2ecf20Sopenharmony_ci */
3238c2ecf20Sopenharmony_cistatic int snd_ice1712_cs8427_set_input_clock(struct snd_ice1712 *ice, int spdif_clock)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	unsigned char reg[2] = { 0x80 | 4, 0 };   /* CS8427 auto increment | register number 4 + data */
3268c2ecf20Sopenharmony_ci	unsigned char val, nval;
3278c2ecf20Sopenharmony_ci	int res = 0;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	snd_i2c_lock(ice->i2c);
3308c2ecf20Sopenharmony_ci	if (snd_i2c_sendbytes(ice->cs8427, reg, 1) != 1) {
3318c2ecf20Sopenharmony_ci		snd_i2c_unlock(ice->i2c);
3328c2ecf20Sopenharmony_ci		return -EIO;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci	if (snd_i2c_readbytes(ice->cs8427, &val, 1) != 1) {
3358c2ecf20Sopenharmony_ci		snd_i2c_unlock(ice->i2c);
3368c2ecf20Sopenharmony_ci		return -EIO;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci	nval = val & 0xf0;
3398c2ecf20Sopenharmony_ci	if (spdif_clock)
3408c2ecf20Sopenharmony_ci		nval |= 0x01;
3418c2ecf20Sopenharmony_ci	else
3428c2ecf20Sopenharmony_ci		nval |= 0x04;
3438c2ecf20Sopenharmony_ci	if (val != nval) {
3448c2ecf20Sopenharmony_ci		reg[1] = nval;
3458c2ecf20Sopenharmony_ci		if (snd_i2c_sendbytes(ice->cs8427, reg, 2) != 2) {
3468c2ecf20Sopenharmony_ci			res = -EIO;
3478c2ecf20Sopenharmony_ci		} else {
3488c2ecf20Sopenharmony_ci			res++;
3498c2ecf20Sopenharmony_ci		}
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci	snd_i2c_unlock(ice->i2c);
3528c2ecf20Sopenharmony_ci	return res;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci/*
3568c2ecf20Sopenharmony_ci * spdif callbacks
3578c2ecf20Sopenharmony_ci */
3588c2ecf20Sopenharmony_cistatic void open_cs8427(struct snd_ice1712 *ice, struct snd_pcm_substream *substream)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	snd_cs8427_iec958_active(ice->cs8427, 1);
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic void close_cs8427(struct snd_ice1712 *ice, struct snd_pcm_substream *substream)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	snd_cs8427_iec958_active(ice->cs8427, 0);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic void setup_cs8427(struct snd_ice1712 *ice, int rate)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	snd_cs8427_iec958_pcm(ice->cs8427, rate);
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci/*
3748c2ecf20Sopenharmony_ci * create and initialize callbacks for cs8427 interface
3758c2ecf20Sopenharmony_ci */
3768c2ecf20Sopenharmony_ciint snd_ice1712_init_cs8427(struct snd_ice1712 *ice, int addr)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	int err;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	err = snd_cs8427_create(ice->i2c, addr,
3818c2ecf20Sopenharmony_ci		(ice->cs8427_timeout * HZ) / 1000, &ice->cs8427);
3828c2ecf20Sopenharmony_ci	if (err < 0) {
3838c2ecf20Sopenharmony_ci		dev_err(ice->card->dev, "CS8427 initialization failed\n");
3848c2ecf20Sopenharmony_ci		return err;
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci	ice->spdif.ops.open = open_cs8427;
3878c2ecf20Sopenharmony_ci	ice->spdif.ops.close = close_cs8427;
3888c2ecf20Sopenharmony_ci	ice->spdif.ops.setup_rate = setup_cs8427;
3898c2ecf20Sopenharmony_ci	return 0;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic void snd_ice1712_set_input_clock_source(struct snd_ice1712 *ice, int spdif_is_master)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	/* change CS8427 clock source too */
3958c2ecf20Sopenharmony_ci	if (ice->cs8427)
3968c2ecf20Sopenharmony_ci		snd_ice1712_cs8427_set_input_clock(ice, spdif_is_master);
3978c2ecf20Sopenharmony_ci	/* notify ak4524 chip as well */
3988c2ecf20Sopenharmony_ci	if (spdif_is_master) {
3998c2ecf20Sopenharmony_ci		unsigned int i;
4008c2ecf20Sopenharmony_ci		for (i = 0; i < ice->akm_codecs; i++) {
4018c2ecf20Sopenharmony_ci			if (ice->akm[i].ops.set_rate_val)
4028c2ecf20Sopenharmony_ci				ice->akm[i].ops.set_rate_val(&ice->akm[i], 0);
4038c2ecf20Sopenharmony_ci		}
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci/*
4088c2ecf20Sopenharmony_ci *  Interrupt handler
4098c2ecf20Sopenharmony_ci */
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic irqreturn_t snd_ice1712_interrupt(int irq, void *dev_id)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = dev_id;
4148c2ecf20Sopenharmony_ci	unsigned char status;
4158c2ecf20Sopenharmony_ci	int handled = 0;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	while (1) {
4188c2ecf20Sopenharmony_ci		status = inb(ICEREG(ice, IRQSTAT));
4198c2ecf20Sopenharmony_ci		if (status == 0)
4208c2ecf20Sopenharmony_ci			break;
4218c2ecf20Sopenharmony_ci		handled = 1;
4228c2ecf20Sopenharmony_ci		if (status & ICE1712_IRQ_MPU1) {
4238c2ecf20Sopenharmony_ci			if (ice->rmidi[0])
4248c2ecf20Sopenharmony_ci				snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data);
4258c2ecf20Sopenharmony_ci			outb(ICE1712_IRQ_MPU1, ICEREG(ice, IRQSTAT));
4268c2ecf20Sopenharmony_ci			status &= ~ICE1712_IRQ_MPU1;
4278c2ecf20Sopenharmony_ci		}
4288c2ecf20Sopenharmony_ci		if (status & ICE1712_IRQ_TIMER)
4298c2ecf20Sopenharmony_ci			outb(ICE1712_IRQ_TIMER, ICEREG(ice, IRQSTAT));
4308c2ecf20Sopenharmony_ci		if (status & ICE1712_IRQ_MPU2) {
4318c2ecf20Sopenharmony_ci			if (ice->rmidi[1])
4328c2ecf20Sopenharmony_ci				snd_mpu401_uart_interrupt(irq, ice->rmidi[1]->private_data);
4338c2ecf20Sopenharmony_ci			outb(ICE1712_IRQ_MPU2, ICEREG(ice, IRQSTAT));
4348c2ecf20Sopenharmony_ci			status &= ~ICE1712_IRQ_MPU2;
4358c2ecf20Sopenharmony_ci		}
4368c2ecf20Sopenharmony_ci		if (status & ICE1712_IRQ_PROPCM) {
4378c2ecf20Sopenharmony_ci			unsigned char mtstat = inb(ICEMT(ice, IRQ));
4388c2ecf20Sopenharmony_ci			if (mtstat & ICE1712_MULTI_PBKSTATUS) {
4398c2ecf20Sopenharmony_ci				if (ice->playback_pro_substream)
4408c2ecf20Sopenharmony_ci					snd_pcm_period_elapsed(ice->playback_pro_substream);
4418c2ecf20Sopenharmony_ci				outb(ICE1712_MULTI_PBKSTATUS, ICEMT(ice, IRQ));
4428c2ecf20Sopenharmony_ci			}
4438c2ecf20Sopenharmony_ci			if (mtstat & ICE1712_MULTI_CAPSTATUS) {
4448c2ecf20Sopenharmony_ci				if (ice->capture_pro_substream)
4458c2ecf20Sopenharmony_ci					snd_pcm_period_elapsed(ice->capture_pro_substream);
4468c2ecf20Sopenharmony_ci				outb(ICE1712_MULTI_CAPSTATUS, ICEMT(ice, IRQ));
4478c2ecf20Sopenharmony_ci			}
4488c2ecf20Sopenharmony_ci		}
4498c2ecf20Sopenharmony_ci		if (status & ICE1712_IRQ_FM)
4508c2ecf20Sopenharmony_ci			outb(ICE1712_IRQ_FM, ICEREG(ice, IRQSTAT));
4518c2ecf20Sopenharmony_ci		if (status & ICE1712_IRQ_PBKDS) {
4528c2ecf20Sopenharmony_ci			u32 idx;
4538c2ecf20Sopenharmony_ci			u16 pbkstatus;
4548c2ecf20Sopenharmony_ci			struct snd_pcm_substream *substream;
4558c2ecf20Sopenharmony_ci			pbkstatus = inw(ICEDS(ice, INTSTAT));
4568c2ecf20Sopenharmony_ci			/* dev_dbg(ice->card->dev, "pbkstatus = 0x%x\n", pbkstatus); */
4578c2ecf20Sopenharmony_ci			for (idx = 0; idx < 6; idx++) {
4588c2ecf20Sopenharmony_ci				if ((pbkstatus & (3 << (idx * 2))) == 0)
4598c2ecf20Sopenharmony_ci					continue;
4608c2ecf20Sopenharmony_ci				substream = ice->playback_con_substream_ds[idx];
4618c2ecf20Sopenharmony_ci				if (substream != NULL)
4628c2ecf20Sopenharmony_ci					snd_pcm_period_elapsed(substream);
4638c2ecf20Sopenharmony_ci				outw(3 << (idx * 2), ICEDS(ice, INTSTAT));
4648c2ecf20Sopenharmony_ci			}
4658c2ecf20Sopenharmony_ci			outb(ICE1712_IRQ_PBKDS, ICEREG(ice, IRQSTAT));
4668c2ecf20Sopenharmony_ci		}
4678c2ecf20Sopenharmony_ci		if (status & ICE1712_IRQ_CONCAP) {
4688c2ecf20Sopenharmony_ci			if (ice->capture_con_substream)
4698c2ecf20Sopenharmony_ci				snd_pcm_period_elapsed(ice->capture_con_substream);
4708c2ecf20Sopenharmony_ci			outb(ICE1712_IRQ_CONCAP, ICEREG(ice, IRQSTAT));
4718c2ecf20Sopenharmony_ci		}
4728c2ecf20Sopenharmony_ci		if (status & ICE1712_IRQ_CONPBK) {
4738c2ecf20Sopenharmony_ci			if (ice->playback_con_substream)
4748c2ecf20Sopenharmony_ci				snd_pcm_period_elapsed(ice->playback_con_substream);
4758c2ecf20Sopenharmony_ci			outb(ICE1712_IRQ_CONPBK, ICEREG(ice, IRQSTAT));
4768c2ecf20Sopenharmony_ci		}
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci	return IRQ_RETVAL(handled);
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci/*
4838c2ecf20Sopenharmony_ci *  PCM part - consumer I/O
4848c2ecf20Sopenharmony_ci */
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic int snd_ice1712_playback_trigger(struct snd_pcm_substream *substream,
4878c2ecf20Sopenharmony_ci					int cmd)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
4908c2ecf20Sopenharmony_ci	int result = 0;
4918c2ecf20Sopenharmony_ci	u32 tmp;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	spin_lock(&ice->reg_lock);
4948c2ecf20Sopenharmony_ci	tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL);
4958c2ecf20Sopenharmony_ci	if (cmd == SNDRV_PCM_TRIGGER_START) {
4968c2ecf20Sopenharmony_ci		tmp |= 1;
4978c2ecf20Sopenharmony_ci	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
4988c2ecf20Sopenharmony_ci		tmp &= ~1;
4998c2ecf20Sopenharmony_ci	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) {
5008c2ecf20Sopenharmony_ci		tmp |= 2;
5018c2ecf20Sopenharmony_ci	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) {
5028c2ecf20Sopenharmony_ci		tmp &= ~2;
5038c2ecf20Sopenharmony_ci	} else {
5048c2ecf20Sopenharmony_ci		result = -EINVAL;
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
5078c2ecf20Sopenharmony_ci	spin_unlock(&ice->reg_lock);
5088c2ecf20Sopenharmony_ci	return result;
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic int snd_ice1712_playback_ds_trigger(struct snd_pcm_substream *substream,
5128c2ecf20Sopenharmony_ci					   int cmd)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
5158c2ecf20Sopenharmony_ci	int result = 0;
5168c2ecf20Sopenharmony_ci	u32 tmp;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	spin_lock(&ice->reg_lock);
5198c2ecf20Sopenharmony_ci	tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL);
5208c2ecf20Sopenharmony_ci	if (cmd == SNDRV_PCM_TRIGGER_START) {
5218c2ecf20Sopenharmony_ci		tmp |= 1;
5228c2ecf20Sopenharmony_ci	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
5238c2ecf20Sopenharmony_ci		tmp &= ~1;
5248c2ecf20Sopenharmony_ci	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) {
5258c2ecf20Sopenharmony_ci		tmp |= 2;
5268c2ecf20Sopenharmony_ci	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) {
5278c2ecf20Sopenharmony_ci		tmp &= ~2;
5288c2ecf20Sopenharmony_ci	} else {
5298c2ecf20Sopenharmony_ci		result = -EINVAL;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci	snd_ice1712_ds_write(ice, substream->number * 2, ICE1712_DSC_CONTROL, tmp);
5328c2ecf20Sopenharmony_ci	spin_unlock(&ice->reg_lock);
5338c2ecf20Sopenharmony_ci	return result;
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic int snd_ice1712_capture_trigger(struct snd_pcm_substream *substream,
5378c2ecf20Sopenharmony_ci				       int cmd)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
5408c2ecf20Sopenharmony_ci	int result = 0;
5418c2ecf20Sopenharmony_ci	u8 tmp;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	spin_lock(&ice->reg_lock);
5448c2ecf20Sopenharmony_ci	tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL);
5458c2ecf20Sopenharmony_ci	if (cmd == SNDRV_PCM_TRIGGER_START) {
5468c2ecf20Sopenharmony_ci		tmp |= 1;
5478c2ecf20Sopenharmony_ci	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
5488c2ecf20Sopenharmony_ci		tmp &= ~1;
5498c2ecf20Sopenharmony_ci	} else {
5508c2ecf20Sopenharmony_ci		result = -EINVAL;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
5538c2ecf20Sopenharmony_ci	spin_unlock(&ice->reg_lock);
5548c2ecf20Sopenharmony_ci	return result;
5558c2ecf20Sopenharmony_ci}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_cistatic int snd_ice1712_playback_prepare(struct snd_pcm_substream *substream)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
5608c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
5618c2ecf20Sopenharmony_ci	u32 period_size, buf_size, rate, tmp;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
5648c2ecf20Sopenharmony_ci	buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
5658c2ecf20Sopenharmony_ci	tmp = 0x0000;
5668c2ecf20Sopenharmony_ci	if (snd_pcm_format_width(runtime->format) == 16)
5678c2ecf20Sopenharmony_ci		tmp |= 0x10;
5688c2ecf20Sopenharmony_ci	if (runtime->channels == 2)
5698c2ecf20Sopenharmony_ci		tmp |= 0x08;
5708c2ecf20Sopenharmony_ci	rate = (runtime->rate * 8192) / 375;
5718c2ecf20Sopenharmony_ci	if (rate > 0x000fffff)
5728c2ecf20Sopenharmony_ci		rate = 0x000fffff;
5738c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
5748c2ecf20Sopenharmony_ci	outb(0, ice->ddma_port + 15);
5758c2ecf20Sopenharmony_ci	outb(ICE1712_DMA_MODE_WRITE | ICE1712_DMA_AUTOINIT, ice->ddma_port + 0x0b);
5768c2ecf20Sopenharmony_ci	outl(runtime->dma_addr, ice->ddma_port + 0);
5778c2ecf20Sopenharmony_ci	outw(buf_size, ice->ddma_port + 4);
5788c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_LO, rate & 0xff);
5798c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_MID, (rate >> 8) & 0xff);
5808c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_HI, (rate >> 16) & 0xff);
5818c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
5828c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_LO, period_size & 0xff);
5838c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_HI, period_size >> 8);
5848c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_PBK_LEFT, 0);
5858c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_PBK_RIGHT, 0);
5868c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
5878c2ecf20Sopenharmony_ci	return 0;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_cistatic int snd_ice1712_playback_ds_prepare(struct snd_pcm_substream *substream)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
5938c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
5948c2ecf20Sopenharmony_ci	u32 period_size, rate, tmp, chn;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	period_size = snd_pcm_lib_period_bytes(substream) - 1;
5978c2ecf20Sopenharmony_ci	tmp = 0x0064;
5988c2ecf20Sopenharmony_ci	if (snd_pcm_format_width(runtime->format) == 16)
5998c2ecf20Sopenharmony_ci		tmp &= ~0x04;
6008c2ecf20Sopenharmony_ci	if (runtime->channels == 2)
6018c2ecf20Sopenharmony_ci		tmp |= 0x08;
6028c2ecf20Sopenharmony_ci	rate = (runtime->rate * 8192) / 375;
6038c2ecf20Sopenharmony_ci	if (rate > 0x000fffff)
6048c2ecf20Sopenharmony_ci		rate = 0x000fffff;
6058c2ecf20Sopenharmony_ci	ice->playback_con_active_buf[substream->number] = 0;
6068c2ecf20Sopenharmony_ci	ice->playback_con_virt_addr[substream->number] = runtime->dma_addr;
6078c2ecf20Sopenharmony_ci	chn = substream->number * 2;
6088c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
6098c2ecf20Sopenharmony_ci	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR0, runtime->dma_addr);
6108c2ecf20Sopenharmony_ci	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT0, period_size);
6118c2ecf20Sopenharmony_ci	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR1, runtime->dma_addr + (runtime->periods > 1 ? period_size + 1 : 0));
6128c2ecf20Sopenharmony_ci	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT1, period_size);
6138c2ecf20Sopenharmony_ci	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_RATE, rate);
6148c2ecf20Sopenharmony_ci	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_VOLUME, 0);
6158c2ecf20Sopenharmony_ci	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_CONTROL, tmp);
6168c2ecf20Sopenharmony_ci	if (runtime->channels == 2) {
6178c2ecf20Sopenharmony_ci		snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_RATE, rate);
6188c2ecf20Sopenharmony_ci		snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_VOLUME, 0);
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
6218c2ecf20Sopenharmony_ci	return 0;
6228c2ecf20Sopenharmony_ci}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_cistatic int snd_ice1712_capture_prepare(struct snd_pcm_substream *substream)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
6278c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
6288c2ecf20Sopenharmony_ci	u32 period_size, buf_size;
6298c2ecf20Sopenharmony_ci	u8 tmp;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
6328c2ecf20Sopenharmony_ci	buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
6338c2ecf20Sopenharmony_ci	tmp = 0x06;
6348c2ecf20Sopenharmony_ci	if (snd_pcm_format_width(runtime->format) == 16)
6358c2ecf20Sopenharmony_ci		tmp &= ~0x04;
6368c2ecf20Sopenharmony_ci	if (runtime->channels == 2)
6378c2ecf20Sopenharmony_ci		tmp &= ~0x02;
6388c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
6398c2ecf20Sopenharmony_ci	outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR));
6408c2ecf20Sopenharmony_ci	outw(buf_size, ICEREG(ice, CONCAP_COUNT));
6418c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8);
6428c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff);
6438c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
6448c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
6458c2ecf20Sopenharmony_ci	snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
6468c2ecf20Sopenharmony_ci	return 0;
6478c2ecf20Sopenharmony_ci}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_ice1712_playback_pointer(struct snd_pcm_substream *substream)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
6528c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
6538c2ecf20Sopenharmony_ci	size_t ptr;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	if (!(snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL) & 1))
6568c2ecf20Sopenharmony_ci		return 0;
6578c2ecf20Sopenharmony_ci	ptr = runtime->buffer_size - inw(ice->ddma_port + 4);
6588c2ecf20Sopenharmony_ci	ptr = bytes_to_frames(substream->runtime, ptr);
6598c2ecf20Sopenharmony_ci	if (ptr == runtime->buffer_size)
6608c2ecf20Sopenharmony_ci		ptr = 0;
6618c2ecf20Sopenharmony_ci	return ptr;
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_ice1712_playback_ds_pointer(struct snd_pcm_substream *substream)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
6678c2ecf20Sopenharmony_ci	u8 addr;
6688c2ecf20Sopenharmony_ci	size_t ptr;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	if (!(snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL) & 1))
6718c2ecf20Sopenharmony_ci		return 0;
6728c2ecf20Sopenharmony_ci	if (ice->playback_con_active_buf[substream->number])
6738c2ecf20Sopenharmony_ci		addr = ICE1712_DSC_ADDR1;
6748c2ecf20Sopenharmony_ci	else
6758c2ecf20Sopenharmony_ci		addr = ICE1712_DSC_ADDR0;
6768c2ecf20Sopenharmony_ci	ptr = snd_ice1712_ds_read(ice, substream->number * 2, addr) -
6778c2ecf20Sopenharmony_ci		ice->playback_con_virt_addr[substream->number];
6788c2ecf20Sopenharmony_ci	ptr = bytes_to_frames(substream->runtime, ptr);
6798c2ecf20Sopenharmony_ci	if (ptr == substream->runtime->buffer_size)
6808c2ecf20Sopenharmony_ci		ptr = 0;
6818c2ecf20Sopenharmony_ci	return ptr;
6828c2ecf20Sopenharmony_ci}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_ice1712_capture_pointer(struct snd_pcm_substream *substream)
6858c2ecf20Sopenharmony_ci{
6868c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
6878c2ecf20Sopenharmony_ci	size_t ptr;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	if (!(snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL) & 1))
6908c2ecf20Sopenharmony_ci		return 0;
6918c2ecf20Sopenharmony_ci	ptr = inl(ICEREG(ice, CONCAP_ADDR)) - ice->capture_con_virt_addr;
6928c2ecf20Sopenharmony_ci	ptr = bytes_to_frames(substream->runtime, ptr);
6938c2ecf20Sopenharmony_ci	if (ptr == substream->runtime->buffer_size)
6948c2ecf20Sopenharmony_ci		ptr = 0;
6958c2ecf20Sopenharmony_ci	return ptr;
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_ice1712_playback = {
6998c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
7008c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
7018c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID |
7028c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_PAUSE),
7038c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
7048c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
7058c2ecf20Sopenharmony_ci	.rate_min =		4000,
7068c2ecf20Sopenharmony_ci	.rate_max =		48000,
7078c2ecf20Sopenharmony_ci	.channels_min =		1,
7088c2ecf20Sopenharmony_ci	.channels_max =		2,
7098c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(64*1024),
7108c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
7118c2ecf20Sopenharmony_ci	.period_bytes_max =	(64*1024),
7128c2ecf20Sopenharmony_ci	.periods_min =		1,
7138c2ecf20Sopenharmony_ci	.periods_max =		1024,
7148c2ecf20Sopenharmony_ci	.fifo_size =		0,
7158c2ecf20Sopenharmony_ci};
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_ice1712_playback_ds = {
7188c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
7198c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
7208c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID |
7218c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_PAUSE),
7228c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
7238c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
7248c2ecf20Sopenharmony_ci	.rate_min =		4000,
7258c2ecf20Sopenharmony_ci	.rate_max =		48000,
7268c2ecf20Sopenharmony_ci	.channels_min =		1,
7278c2ecf20Sopenharmony_ci	.channels_max =		2,
7288c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(128*1024),
7298c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
7308c2ecf20Sopenharmony_ci	.period_bytes_max =	(128*1024),
7318c2ecf20Sopenharmony_ci	.periods_min =		2,
7328c2ecf20Sopenharmony_ci	.periods_max =		2,
7338c2ecf20Sopenharmony_ci	.fifo_size =		0,
7348c2ecf20Sopenharmony_ci};
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_ice1712_capture = {
7378c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
7388c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
7398c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID),
7408c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
7418c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
7428c2ecf20Sopenharmony_ci	.rate_min =		4000,
7438c2ecf20Sopenharmony_ci	.rate_max =		48000,
7448c2ecf20Sopenharmony_ci	.channels_min =		1,
7458c2ecf20Sopenharmony_ci	.channels_max =		2,
7468c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(64*1024),
7478c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
7488c2ecf20Sopenharmony_ci	.period_bytes_max =	(64*1024),
7498c2ecf20Sopenharmony_ci	.periods_min =		1,
7508c2ecf20Sopenharmony_ci	.periods_max =		1024,
7518c2ecf20Sopenharmony_ci	.fifo_size =		0,
7528c2ecf20Sopenharmony_ci};
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cistatic int snd_ice1712_playback_open(struct snd_pcm_substream *substream)
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
7578c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	ice->playback_con_substream = substream;
7608c2ecf20Sopenharmony_ci	runtime->hw = snd_ice1712_playback;
7618c2ecf20Sopenharmony_ci	return 0;
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_cistatic int snd_ice1712_playback_ds_open(struct snd_pcm_substream *substream)
7658c2ecf20Sopenharmony_ci{
7668c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
7678c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
7688c2ecf20Sopenharmony_ci	u32 tmp;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	ice->playback_con_substream_ds[substream->number] = substream;
7718c2ecf20Sopenharmony_ci	runtime->hw = snd_ice1712_playback_ds;
7728c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
7738c2ecf20Sopenharmony_ci	tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2));
7748c2ecf20Sopenharmony_ci	outw(tmp, ICEDS(ice, INTMASK));
7758c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
7768c2ecf20Sopenharmony_ci	return 0;
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_cistatic int snd_ice1712_capture_open(struct snd_pcm_substream *substream)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
7828c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	ice->capture_con_substream = substream;
7858c2ecf20Sopenharmony_ci	runtime->hw = snd_ice1712_capture;
7868c2ecf20Sopenharmony_ci	runtime->hw.rates = ice->ac97->rates[AC97_RATES_ADC];
7878c2ecf20Sopenharmony_ci	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
7888c2ecf20Sopenharmony_ci		runtime->hw.rate_min = 48000;
7898c2ecf20Sopenharmony_ci	return 0;
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cistatic int snd_ice1712_playback_close(struct snd_pcm_substream *substream)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	ice->playback_con_substream = NULL;
7978c2ecf20Sopenharmony_ci	return 0;
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_cistatic int snd_ice1712_playback_ds_close(struct snd_pcm_substream *substream)
8018c2ecf20Sopenharmony_ci{
8028c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
8038c2ecf20Sopenharmony_ci	u32 tmp;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
8068c2ecf20Sopenharmony_ci	tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2));
8078c2ecf20Sopenharmony_ci	outw(tmp, ICEDS(ice, INTMASK));
8088c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
8098c2ecf20Sopenharmony_ci	ice->playback_con_substream_ds[substream->number] = NULL;
8108c2ecf20Sopenharmony_ci	return 0;
8118c2ecf20Sopenharmony_ci}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_cistatic int snd_ice1712_capture_close(struct snd_pcm_substream *substream)
8148c2ecf20Sopenharmony_ci{
8158c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	ice->capture_con_substream = NULL;
8188c2ecf20Sopenharmony_ci	return 0;
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_ice1712_playback_ops = {
8228c2ecf20Sopenharmony_ci	.open =		snd_ice1712_playback_open,
8238c2ecf20Sopenharmony_ci	.close =	snd_ice1712_playback_close,
8248c2ecf20Sopenharmony_ci	.prepare =	snd_ice1712_playback_prepare,
8258c2ecf20Sopenharmony_ci	.trigger =	snd_ice1712_playback_trigger,
8268c2ecf20Sopenharmony_ci	.pointer =	snd_ice1712_playback_pointer,
8278c2ecf20Sopenharmony_ci};
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_ice1712_playback_ds_ops = {
8308c2ecf20Sopenharmony_ci	.open =		snd_ice1712_playback_ds_open,
8318c2ecf20Sopenharmony_ci	.close =	snd_ice1712_playback_ds_close,
8328c2ecf20Sopenharmony_ci	.prepare =	snd_ice1712_playback_ds_prepare,
8338c2ecf20Sopenharmony_ci	.trigger =	snd_ice1712_playback_ds_trigger,
8348c2ecf20Sopenharmony_ci	.pointer =	snd_ice1712_playback_ds_pointer,
8358c2ecf20Sopenharmony_ci};
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_ice1712_capture_ops = {
8388c2ecf20Sopenharmony_ci	.open =		snd_ice1712_capture_open,
8398c2ecf20Sopenharmony_ci	.close =	snd_ice1712_capture_close,
8408c2ecf20Sopenharmony_ci	.prepare =	snd_ice1712_capture_prepare,
8418c2ecf20Sopenharmony_ci	.trigger =	snd_ice1712_capture_trigger,
8428c2ecf20Sopenharmony_ci	.pointer =	snd_ice1712_capture_pointer,
8438c2ecf20Sopenharmony_ci};
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_cistatic int snd_ice1712_pcm(struct snd_ice1712 *ice, int device)
8468c2ecf20Sopenharmony_ci{
8478c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
8488c2ecf20Sopenharmony_ci	int err;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	err = snd_pcm_new(ice->card, "ICE1712 consumer", device, 1, 1, &pcm);
8518c2ecf20Sopenharmony_ci	if (err < 0)
8528c2ecf20Sopenharmony_ci		return err;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ops);
8558c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_ops);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	pcm->private_data = ice;
8588c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
8598c2ecf20Sopenharmony_ci	strcpy(pcm->name, "ICE1712 consumer");
8608c2ecf20Sopenharmony_ci	ice->pcm = pcm;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
8638c2ecf20Sopenharmony_ci				       &ice->pci->dev, 64*1024, 64*1024);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	dev_warn(ice->card->dev,
8668c2ecf20Sopenharmony_ci		 "Consumer PCM code does not work well at the moment --jk\n");
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	return 0;
8698c2ecf20Sopenharmony_ci}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_cistatic int snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device)
8728c2ecf20Sopenharmony_ci{
8738c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
8748c2ecf20Sopenharmony_ci	int err;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	err = snd_pcm_new(ice->card, "ICE1712 consumer (DS)", device, 6, 0, &pcm);
8778c2ecf20Sopenharmony_ci	if (err < 0)
8788c2ecf20Sopenharmony_ci		return err;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ds_ops);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	pcm->private_data = ice;
8838c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
8848c2ecf20Sopenharmony_ci	strcpy(pcm->name, "ICE1712 consumer (DS)");
8858c2ecf20Sopenharmony_ci	ice->pcm_ds = pcm;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
8888c2ecf20Sopenharmony_ci				       &ice->pci->dev, 64*1024, 128*1024);
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	return 0;
8918c2ecf20Sopenharmony_ci}
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci/*
8948c2ecf20Sopenharmony_ci *  PCM code - professional part (multitrack)
8958c2ecf20Sopenharmony_ci */
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_cistatic const unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000,
8988c2ecf20Sopenharmony_ci				32000, 44100, 48000, 64000, 88200, 96000 };
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
9018c2ecf20Sopenharmony_ci	.count = ARRAY_SIZE(rates),
9028c2ecf20Sopenharmony_ci	.list = rates,
9038c2ecf20Sopenharmony_ci	.mask = 0,
9048c2ecf20Sopenharmony_ci};
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_trigger(struct snd_pcm_substream *substream,
9078c2ecf20Sopenharmony_ci				   int cmd)
9088c2ecf20Sopenharmony_ci{
9098c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
9108c2ecf20Sopenharmony_ci	switch (cmd) {
9118c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
9128c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
9138c2ecf20Sopenharmony_ci	{
9148c2ecf20Sopenharmony_ci		unsigned int what;
9158c2ecf20Sopenharmony_ci		unsigned int old;
9168c2ecf20Sopenharmony_ci		if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
9178c2ecf20Sopenharmony_ci			return -EINVAL;
9188c2ecf20Sopenharmony_ci		what = ICE1712_PLAYBACK_PAUSE;
9198c2ecf20Sopenharmony_ci		snd_pcm_trigger_done(substream, substream);
9208c2ecf20Sopenharmony_ci		spin_lock(&ice->reg_lock);
9218c2ecf20Sopenharmony_ci		old = inl(ICEMT(ice, PLAYBACK_CONTROL));
9228c2ecf20Sopenharmony_ci		if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
9238c2ecf20Sopenharmony_ci			old |= what;
9248c2ecf20Sopenharmony_ci		else
9258c2ecf20Sopenharmony_ci			old &= ~what;
9268c2ecf20Sopenharmony_ci		outl(old, ICEMT(ice, PLAYBACK_CONTROL));
9278c2ecf20Sopenharmony_ci		spin_unlock(&ice->reg_lock);
9288c2ecf20Sopenharmony_ci		break;
9298c2ecf20Sopenharmony_ci	}
9308c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
9318c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
9328c2ecf20Sopenharmony_ci	{
9338c2ecf20Sopenharmony_ci		unsigned int what = 0;
9348c2ecf20Sopenharmony_ci		unsigned int old;
9358c2ecf20Sopenharmony_ci		struct snd_pcm_substream *s;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci		snd_pcm_group_for_each_entry(s, substream) {
9388c2ecf20Sopenharmony_ci			if (s == ice->playback_pro_substream) {
9398c2ecf20Sopenharmony_ci				what |= ICE1712_PLAYBACK_START;
9408c2ecf20Sopenharmony_ci				snd_pcm_trigger_done(s, substream);
9418c2ecf20Sopenharmony_ci			} else if (s == ice->capture_pro_substream) {
9428c2ecf20Sopenharmony_ci				what |= ICE1712_CAPTURE_START_SHADOW;
9438c2ecf20Sopenharmony_ci				snd_pcm_trigger_done(s, substream);
9448c2ecf20Sopenharmony_ci			}
9458c2ecf20Sopenharmony_ci		}
9468c2ecf20Sopenharmony_ci		spin_lock(&ice->reg_lock);
9478c2ecf20Sopenharmony_ci		old = inl(ICEMT(ice, PLAYBACK_CONTROL));
9488c2ecf20Sopenharmony_ci		if (cmd == SNDRV_PCM_TRIGGER_START)
9498c2ecf20Sopenharmony_ci			old |= what;
9508c2ecf20Sopenharmony_ci		else
9518c2ecf20Sopenharmony_ci			old &= ~what;
9528c2ecf20Sopenharmony_ci		outl(old, ICEMT(ice, PLAYBACK_CONTROL));
9538c2ecf20Sopenharmony_ci		spin_unlock(&ice->reg_lock);
9548c2ecf20Sopenharmony_ci		break;
9558c2ecf20Sopenharmony_ci	}
9568c2ecf20Sopenharmony_ci	default:
9578c2ecf20Sopenharmony_ci		return -EINVAL;
9588c2ecf20Sopenharmony_ci	}
9598c2ecf20Sopenharmony_ci	return 0;
9608c2ecf20Sopenharmony_ci}
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci/*
9638c2ecf20Sopenharmony_ci */
9648c2ecf20Sopenharmony_cistatic void snd_ice1712_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, int force)
9658c2ecf20Sopenharmony_ci{
9668c2ecf20Sopenharmony_ci	unsigned long flags;
9678c2ecf20Sopenharmony_ci	unsigned char val, old;
9688c2ecf20Sopenharmony_ci	unsigned int i;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	switch (rate) {
9718c2ecf20Sopenharmony_ci	case 8000: val = 6; break;
9728c2ecf20Sopenharmony_ci	case 9600: val = 3; break;
9738c2ecf20Sopenharmony_ci	case 11025: val = 10; break;
9748c2ecf20Sopenharmony_ci	case 12000: val = 2; break;
9758c2ecf20Sopenharmony_ci	case 16000: val = 5; break;
9768c2ecf20Sopenharmony_ci	case 22050: val = 9; break;
9778c2ecf20Sopenharmony_ci	case 24000: val = 1; break;
9788c2ecf20Sopenharmony_ci	case 32000: val = 4; break;
9798c2ecf20Sopenharmony_ci	case 44100: val = 8; break;
9808c2ecf20Sopenharmony_ci	case 48000: val = 0; break;
9818c2ecf20Sopenharmony_ci	case 64000: val = 15; break;
9828c2ecf20Sopenharmony_ci	case 88200: val = 11; break;
9838c2ecf20Sopenharmony_ci	case 96000: val = 7; break;
9848c2ecf20Sopenharmony_ci	default:
9858c2ecf20Sopenharmony_ci		snd_BUG();
9868c2ecf20Sopenharmony_ci		val = 0;
9878c2ecf20Sopenharmony_ci		rate = 48000;
9888c2ecf20Sopenharmony_ci		break;
9898c2ecf20Sopenharmony_ci	}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ice->reg_lock, flags);
9928c2ecf20Sopenharmony_ci	if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|
9938c2ecf20Sopenharmony_ci						 ICE1712_PLAYBACK_PAUSE|
9948c2ecf20Sopenharmony_ci						 ICE1712_PLAYBACK_START)) {
9958c2ecf20Sopenharmony_ci__out:
9968c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ice->reg_lock, flags);
9978c2ecf20Sopenharmony_ci		return;
9988c2ecf20Sopenharmony_ci	}
9998c2ecf20Sopenharmony_ci	if (!force && is_pro_rate_locked(ice))
10008c2ecf20Sopenharmony_ci		goto __out;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	old = inb(ICEMT(ice, RATE));
10038c2ecf20Sopenharmony_ci	if (!force && old == val)
10048c2ecf20Sopenharmony_ci		goto __out;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	ice->cur_rate = rate;
10078c2ecf20Sopenharmony_ci	outb(val, ICEMT(ice, RATE));
10088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ice->reg_lock, flags);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	if (ice->gpio.set_pro_rate)
10118c2ecf20Sopenharmony_ci		ice->gpio.set_pro_rate(ice, rate);
10128c2ecf20Sopenharmony_ci	for (i = 0; i < ice->akm_codecs; i++) {
10138c2ecf20Sopenharmony_ci		if (ice->akm[i].ops.set_rate_val)
10148c2ecf20Sopenharmony_ci			ice->akm[i].ops.set_rate_val(&ice->akm[i], rate);
10158c2ecf20Sopenharmony_ci	}
10168c2ecf20Sopenharmony_ci	if (ice->spdif.ops.setup_rate)
10178c2ecf20Sopenharmony_ci		ice->spdif.ops.setup_rate(ice, rate);
10188c2ecf20Sopenharmony_ci}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_cistatic int snd_ice1712_playback_pro_prepare(struct snd_pcm_substream *substream)
10218c2ecf20Sopenharmony_ci{
10228c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	ice->playback_pro_size = snd_pcm_lib_buffer_bytes(substream);
10258c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
10268c2ecf20Sopenharmony_ci	outl(substream->runtime->dma_addr, ICEMT(ice, PLAYBACK_ADDR));
10278c2ecf20Sopenharmony_ci	outw((ice->playback_pro_size >> 2) - 1, ICEMT(ice, PLAYBACK_SIZE));
10288c2ecf20Sopenharmony_ci	outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, PLAYBACK_COUNT));
10298c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	return 0;
10328c2ecf20Sopenharmony_ci}
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_cistatic int snd_ice1712_playback_pro_hw_params(struct snd_pcm_substream *substream,
10358c2ecf20Sopenharmony_ci					      struct snd_pcm_hw_params *hw_params)
10368c2ecf20Sopenharmony_ci{
10378c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	snd_ice1712_set_pro_rate(ice, params_rate(hw_params), 0);
10408c2ecf20Sopenharmony_ci	return 0;
10418c2ecf20Sopenharmony_ci}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_cistatic int snd_ice1712_capture_pro_prepare(struct snd_pcm_substream *substream)
10448c2ecf20Sopenharmony_ci{
10458c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	ice->capture_pro_size = snd_pcm_lib_buffer_bytes(substream);
10488c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
10498c2ecf20Sopenharmony_ci	outl(substream->runtime->dma_addr, ICEMT(ice, CAPTURE_ADDR));
10508c2ecf20Sopenharmony_ci	outw((ice->capture_pro_size >> 2) - 1, ICEMT(ice, CAPTURE_SIZE));
10518c2ecf20Sopenharmony_ci	outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, CAPTURE_COUNT));
10528c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
10538c2ecf20Sopenharmony_ci	return 0;
10548c2ecf20Sopenharmony_ci}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_cistatic int snd_ice1712_capture_pro_hw_params(struct snd_pcm_substream *substream,
10578c2ecf20Sopenharmony_ci					     struct snd_pcm_hw_params *hw_params)
10588c2ecf20Sopenharmony_ci{
10598c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	snd_ice1712_set_pro_rate(ice, params_rate(hw_params), 0);
10628c2ecf20Sopenharmony_ci	return 0;
10638c2ecf20Sopenharmony_ci}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_ice1712_playback_pro_pointer(struct snd_pcm_substream *substream)
10668c2ecf20Sopenharmony_ci{
10678c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
10688c2ecf20Sopenharmony_ci	size_t ptr;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_PLAYBACK_START))
10718c2ecf20Sopenharmony_ci		return 0;
10728c2ecf20Sopenharmony_ci	ptr = ice->playback_pro_size - (inw(ICEMT(ice, PLAYBACK_SIZE)) << 2);
10738c2ecf20Sopenharmony_ci	ptr = bytes_to_frames(substream->runtime, ptr);
10748c2ecf20Sopenharmony_ci	if (ptr == substream->runtime->buffer_size)
10758c2ecf20Sopenharmony_ci		ptr = 0;
10768c2ecf20Sopenharmony_ci	return ptr;
10778c2ecf20Sopenharmony_ci}
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(struct snd_pcm_substream *substream)
10808c2ecf20Sopenharmony_ci{
10818c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
10828c2ecf20Sopenharmony_ci	size_t ptr;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_CAPTURE_START_SHADOW))
10858c2ecf20Sopenharmony_ci		return 0;
10868c2ecf20Sopenharmony_ci	ptr = ice->capture_pro_size - (inw(ICEMT(ice, CAPTURE_SIZE)) << 2);
10878c2ecf20Sopenharmony_ci	ptr = bytes_to_frames(substream->runtime, ptr);
10888c2ecf20Sopenharmony_ci	if (ptr == substream->runtime->buffer_size)
10898c2ecf20Sopenharmony_ci		ptr = 0;
10908c2ecf20Sopenharmony_ci	return ptr;
10918c2ecf20Sopenharmony_ci}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_ice1712_playback_pro = {
10948c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
10958c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
10968c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID |
10978c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
10988c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_S32_LE,
10998c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000,
11008c2ecf20Sopenharmony_ci	.rate_min =		4000,
11018c2ecf20Sopenharmony_ci	.rate_max =		96000,
11028c2ecf20Sopenharmony_ci	.channels_min =		10,
11038c2ecf20Sopenharmony_ci	.channels_max =		10,
11048c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(256*1024),
11058c2ecf20Sopenharmony_ci	.period_bytes_min =	10 * 4 * 2,
11068c2ecf20Sopenharmony_ci	.period_bytes_max =	131040,
11078c2ecf20Sopenharmony_ci	.periods_min =		1,
11088c2ecf20Sopenharmony_ci	.periods_max =		1024,
11098c2ecf20Sopenharmony_ci	.fifo_size =		0,
11108c2ecf20Sopenharmony_ci};
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_ice1712_capture_pro = {
11138c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
11148c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
11158c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID |
11168c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
11178c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_S32_LE,
11188c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000,
11198c2ecf20Sopenharmony_ci	.rate_min =		4000,
11208c2ecf20Sopenharmony_ci	.rate_max =		96000,
11218c2ecf20Sopenharmony_ci	.channels_min =		12,
11228c2ecf20Sopenharmony_ci	.channels_max =		12,
11238c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(256*1024),
11248c2ecf20Sopenharmony_ci	.period_bytes_min =	12 * 4 * 2,
11258c2ecf20Sopenharmony_ci	.period_bytes_max =	131040,
11268c2ecf20Sopenharmony_ci	.periods_min =		1,
11278c2ecf20Sopenharmony_ci	.periods_max =		1024,
11288c2ecf20Sopenharmony_ci	.fifo_size =		0,
11298c2ecf20Sopenharmony_ci};
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_cistatic int snd_ice1712_playback_pro_open(struct snd_pcm_substream *substream)
11328c2ecf20Sopenharmony_ci{
11338c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
11348c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	ice->playback_pro_substream = substream;
11378c2ecf20Sopenharmony_ci	runtime->hw = snd_ice1712_playback_pro;
11388c2ecf20Sopenharmony_ci	snd_pcm_set_sync(substream);
11398c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
11408c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
11418c2ecf20Sopenharmony_ci	if (is_pro_rate_locked(ice)) {
11428c2ecf20Sopenharmony_ci		runtime->hw.rate_min = PRO_RATE_DEFAULT;
11438c2ecf20Sopenharmony_ci		runtime->hw.rate_max = PRO_RATE_DEFAULT;
11448c2ecf20Sopenharmony_ci	}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	if (ice->spdif.ops.open)
11478c2ecf20Sopenharmony_ci		ice->spdif.ops.open(ice, substream);
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	return 0;
11508c2ecf20Sopenharmony_ci}
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_cistatic int snd_ice1712_capture_pro_open(struct snd_pcm_substream *substream)
11538c2ecf20Sopenharmony_ci{
11548c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
11558c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	ice->capture_pro_substream = substream;
11588c2ecf20Sopenharmony_ci	runtime->hw = snd_ice1712_capture_pro;
11598c2ecf20Sopenharmony_ci	snd_pcm_set_sync(substream);
11608c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
11618c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
11628c2ecf20Sopenharmony_ci	if (is_pro_rate_locked(ice)) {
11638c2ecf20Sopenharmony_ci		runtime->hw.rate_min = PRO_RATE_DEFAULT;
11648c2ecf20Sopenharmony_ci		runtime->hw.rate_max = PRO_RATE_DEFAULT;
11658c2ecf20Sopenharmony_ci	}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	return 0;
11688c2ecf20Sopenharmony_ci}
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_cistatic int snd_ice1712_playback_pro_close(struct snd_pcm_substream *substream)
11718c2ecf20Sopenharmony_ci{
11728c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	if (PRO_RATE_RESET)
11758c2ecf20Sopenharmony_ci		snd_ice1712_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
11768c2ecf20Sopenharmony_ci	ice->playback_pro_substream = NULL;
11778c2ecf20Sopenharmony_ci	if (ice->spdif.ops.close)
11788c2ecf20Sopenharmony_ci		ice->spdif.ops.close(ice, substream);
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	return 0;
11818c2ecf20Sopenharmony_ci}
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_cistatic int snd_ice1712_capture_pro_close(struct snd_pcm_substream *substream)
11848c2ecf20Sopenharmony_ci{
11858c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	if (PRO_RATE_RESET)
11888c2ecf20Sopenharmony_ci		snd_ice1712_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
11898c2ecf20Sopenharmony_ci	ice->capture_pro_substream = NULL;
11908c2ecf20Sopenharmony_ci	return 0;
11918c2ecf20Sopenharmony_ci}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_ice1712_playback_pro_ops = {
11948c2ecf20Sopenharmony_ci	.open =		snd_ice1712_playback_pro_open,
11958c2ecf20Sopenharmony_ci	.close =	snd_ice1712_playback_pro_close,
11968c2ecf20Sopenharmony_ci	.hw_params =	snd_ice1712_playback_pro_hw_params,
11978c2ecf20Sopenharmony_ci	.prepare =	snd_ice1712_playback_pro_prepare,
11988c2ecf20Sopenharmony_ci	.trigger =	snd_ice1712_pro_trigger,
11998c2ecf20Sopenharmony_ci	.pointer =	snd_ice1712_playback_pro_pointer,
12008c2ecf20Sopenharmony_ci};
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_ice1712_capture_pro_ops = {
12038c2ecf20Sopenharmony_ci	.open =		snd_ice1712_capture_pro_open,
12048c2ecf20Sopenharmony_ci	.close =	snd_ice1712_capture_pro_close,
12058c2ecf20Sopenharmony_ci	.hw_params =	snd_ice1712_capture_pro_hw_params,
12068c2ecf20Sopenharmony_ci	.prepare =	snd_ice1712_capture_pro_prepare,
12078c2ecf20Sopenharmony_ci	.trigger =	snd_ice1712_pro_trigger,
12088c2ecf20Sopenharmony_ci	.pointer =	snd_ice1712_capture_pro_pointer,
12098c2ecf20Sopenharmony_ci};
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_cistatic int snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device)
12128c2ecf20Sopenharmony_ci{
12138c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
12148c2ecf20Sopenharmony_ci	int err;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	err = snd_pcm_new(ice->card, "ICE1712 multi", device, 1, 1, &pcm);
12178c2ecf20Sopenharmony_ci	if (err < 0)
12188c2ecf20Sopenharmony_ci		return err;
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_pro_ops);
12218c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_pro_ops);
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	pcm->private_data = ice;
12248c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
12258c2ecf20Sopenharmony_ci	strcpy(pcm->name, "ICE1712 multi");
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
12288c2ecf20Sopenharmony_ci				       &ice->pci->dev, 256*1024, 256*1024);
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	ice->pcm_pro = pcm;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	if (ice->cs8427) {
12338c2ecf20Sopenharmony_ci		/* assign channels to iec958 */
12348c2ecf20Sopenharmony_ci		err = snd_cs8427_iec958_build(ice->cs8427,
12358c2ecf20Sopenharmony_ci					      pcm->streams[0].substream,
12368c2ecf20Sopenharmony_ci					      pcm->streams[1].substream);
12378c2ecf20Sopenharmony_ci		if (err < 0)
12388c2ecf20Sopenharmony_ci			return err;
12398c2ecf20Sopenharmony_ci	}
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	return snd_ice1712_build_pro_mixer(ice);
12428c2ecf20Sopenharmony_ci}
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci/*
12458c2ecf20Sopenharmony_ci *  Mixer section
12468c2ecf20Sopenharmony_ci */
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_cistatic void snd_ice1712_update_volume(struct snd_ice1712 *ice, int index)
12498c2ecf20Sopenharmony_ci{
12508c2ecf20Sopenharmony_ci	unsigned int vol = ice->pro_volumes[index];
12518c2ecf20Sopenharmony_ci	unsigned short val = 0;
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	val |= (vol & 0x8000) == 0 ? (96 - (vol & 0x7f)) : 0x7f;
12548c2ecf20Sopenharmony_ci	val |= ((vol & 0x80000000) == 0 ? (96 - ((vol >> 16) & 0x7f)) : 0x7f) << 8;
12558c2ecf20Sopenharmony_ci	outb(index, ICEMT(ice, MONITOR_INDEX));
12568c2ecf20Sopenharmony_ci	outw(val, ICEMT(ice, MONITOR_VOLUME));
12578c2ecf20Sopenharmony_ci}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci#define snd_ice1712_pro_mixer_switch_info	snd_ctl_boolean_stereo_info
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_mixer_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
12628c2ecf20Sopenharmony_ci{
12638c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
12648c2ecf20Sopenharmony_ci	int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
12658c2ecf20Sopenharmony_ci		kcontrol->private_value;
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
12688c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] =
12698c2ecf20Sopenharmony_ci		!((ice->pro_volumes[priv_idx] >> 15) & 1);
12708c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[1] =
12718c2ecf20Sopenharmony_ci		!((ice->pro_volumes[priv_idx] >> 31) & 1);
12728c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
12738c2ecf20Sopenharmony_ci	return 0;
12748c2ecf20Sopenharmony_ci}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_mixer_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
12778c2ecf20Sopenharmony_ci{
12788c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
12798c2ecf20Sopenharmony_ci	int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
12808c2ecf20Sopenharmony_ci		kcontrol->private_value;
12818c2ecf20Sopenharmony_ci	unsigned int nval, change;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) |
12848c2ecf20Sopenharmony_ci	       (ucontrol->value.integer.value[1] ? 0 : 0x80000000);
12858c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
12868c2ecf20Sopenharmony_ci	nval |= ice->pro_volumes[priv_idx] & ~0x80008000;
12878c2ecf20Sopenharmony_ci	change = nval != ice->pro_volumes[priv_idx];
12888c2ecf20Sopenharmony_ci	ice->pro_volumes[priv_idx] = nval;
12898c2ecf20Sopenharmony_ci	snd_ice1712_update_volume(ice, priv_idx);
12908c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
12918c2ecf20Sopenharmony_ci	return change;
12928c2ecf20Sopenharmony_ci}
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_mixer_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
12958c2ecf20Sopenharmony_ci{
12968c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
12978c2ecf20Sopenharmony_ci	uinfo->count = 2;
12988c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
12998c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 96;
13008c2ecf20Sopenharmony_ci	return 0;
13018c2ecf20Sopenharmony_ci}
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_mixer_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
13048c2ecf20Sopenharmony_ci{
13058c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
13068c2ecf20Sopenharmony_ci	int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
13078c2ecf20Sopenharmony_ci		kcontrol->private_value;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
13108c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] =
13118c2ecf20Sopenharmony_ci		(ice->pro_volumes[priv_idx] >> 0) & 127;
13128c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[1] =
13138c2ecf20Sopenharmony_ci		(ice->pro_volumes[priv_idx] >> 16) & 127;
13148c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
13158c2ecf20Sopenharmony_ci	return 0;
13168c2ecf20Sopenharmony_ci}
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_mixer_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
13198c2ecf20Sopenharmony_ci{
13208c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
13218c2ecf20Sopenharmony_ci	int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
13228c2ecf20Sopenharmony_ci		kcontrol->private_value;
13238c2ecf20Sopenharmony_ci	unsigned int nval, change;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	nval = (ucontrol->value.integer.value[0] & 127) |
13268c2ecf20Sopenharmony_ci	       ((ucontrol->value.integer.value[1] & 127) << 16);
13278c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
13288c2ecf20Sopenharmony_ci	nval |= ice->pro_volumes[priv_idx] & ~0x007f007f;
13298c2ecf20Sopenharmony_ci	change = nval != ice->pro_volumes[priv_idx];
13308c2ecf20Sopenharmony_ci	ice->pro_volumes[priv_idx] = nval;
13318c2ecf20Sopenharmony_ci	snd_ice1712_update_volume(ice, priv_idx);
13328c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
13338c2ecf20Sopenharmony_ci	return change;
13348c2ecf20Sopenharmony_ci}
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_playback, -14400, 150, 0);
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] = {
13398c2ecf20Sopenharmony_ci	{
13408c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
13418c2ecf20Sopenharmony_ci		.name = "Multi Playback Switch",
13428c2ecf20Sopenharmony_ci		.info = snd_ice1712_pro_mixer_switch_info,
13438c2ecf20Sopenharmony_ci		.get = snd_ice1712_pro_mixer_switch_get,
13448c2ecf20Sopenharmony_ci		.put = snd_ice1712_pro_mixer_switch_put,
13458c2ecf20Sopenharmony_ci		.private_value = 0,
13468c2ecf20Sopenharmony_ci		.count = 10,
13478c2ecf20Sopenharmony_ci	},
13488c2ecf20Sopenharmony_ci	{
13498c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
13508c2ecf20Sopenharmony_ci		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
13518c2ecf20Sopenharmony_ci			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
13528c2ecf20Sopenharmony_ci		.name = "Multi Playback Volume",
13538c2ecf20Sopenharmony_ci		.info = snd_ice1712_pro_mixer_volume_info,
13548c2ecf20Sopenharmony_ci		.get = snd_ice1712_pro_mixer_volume_get,
13558c2ecf20Sopenharmony_ci		.put = snd_ice1712_pro_mixer_volume_put,
13568c2ecf20Sopenharmony_ci		.private_value = 0,
13578c2ecf20Sopenharmony_ci		.count = 10,
13588c2ecf20Sopenharmony_ci		.tlv = { .p = db_scale_playback }
13598c2ecf20Sopenharmony_ci	},
13608c2ecf20Sopenharmony_ci};
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch = {
13638c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
13648c2ecf20Sopenharmony_ci	.name = "H/W Multi Capture Switch",
13658c2ecf20Sopenharmony_ci	.info = snd_ice1712_pro_mixer_switch_info,
13668c2ecf20Sopenharmony_ci	.get = snd_ice1712_pro_mixer_switch_get,
13678c2ecf20Sopenharmony_ci	.put = snd_ice1712_pro_mixer_switch_put,
13688c2ecf20Sopenharmony_ci	.private_value = 10,
13698c2ecf20Sopenharmony_ci};
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch = {
13728c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
13738c2ecf20Sopenharmony_ci	.name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, SWITCH),
13748c2ecf20Sopenharmony_ci	.info = snd_ice1712_pro_mixer_switch_info,
13758c2ecf20Sopenharmony_ci	.get = snd_ice1712_pro_mixer_switch_get,
13768c2ecf20Sopenharmony_ci	.put = snd_ice1712_pro_mixer_switch_put,
13778c2ecf20Sopenharmony_ci	.private_value = 18,
13788c2ecf20Sopenharmony_ci	.count = 2,
13798c2ecf20Sopenharmony_ci};
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume = {
13828c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
13838c2ecf20Sopenharmony_ci	.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
13848c2ecf20Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
13858c2ecf20Sopenharmony_ci	.name = "H/W Multi Capture Volume",
13868c2ecf20Sopenharmony_ci	.info = snd_ice1712_pro_mixer_volume_info,
13878c2ecf20Sopenharmony_ci	.get = snd_ice1712_pro_mixer_volume_get,
13888c2ecf20Sopenharmony_ci	.put = snd_ice1712_pro_mixer_volume_put,
13898c2ecf20Sopenharmony_ci	.private_value = 10,
13908c2ecf20Sopenharmony_ci	.tlv = { .p = db_scale_playback }
13918c2ecf20Sopenharmony_ci};
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume = {
13948c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
13958c2ecf20Sopenharmony_ci	.name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, VOLUME),
13968c2ecf20Sopenharmony_ci	.info = snd_ice1712_pro_mixer_volume_info,
13978c2ecf20Sopenharmony_ci	.get = snd_ice1712_pro_mixer_volume_get,
13988c2ecf20Sopenharmony_ci	.put = snd_ice1712_pro_mixer_volume_put,
13998c2ecf20Sopenharmony_ci	.private_value = 18,
14008c2ecf20Sopenharmony_ci	.count = 2,
14018c2ecf20Sopenharmony_ci};
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_cistatic int snd_ice1712_build_pro_mixer(struct snd_ice1712 *ice)
14048c2ecf20Sopenharmony_ci{
14058c2ecf20Sopenharmony_ci	struct snd_card *card = ice->card;
14068c2ecf20Sopenharmony_ci	unsigned int idx;
14078c2ecf20Sopenharmony_ci	int err;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	/* multi-channel mixer */
14108c2ecf20Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(snd_ice1712_multi_playback_ctrls); idx++) {
14118c2ecf20Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_playback_ctrls[idx], ice));
14128c2ecf20Sopenharmony_ci		if (err < 0)
14138c2ecf20Sopenharmony_ci			return err;
14148c2ecf20Sopenharmony_ci	}
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	if (ice->num_total_adcs > 0) {
14178c2ecf20Sopenharmony_ci		struct snd_kcontrol_new tmp = snd_ice1712_multi_capture_analog_switch;
14188c2ecf20Sopenharmony_ci		tmp.count = ice->num_total_adcs;
14198c2ecf20Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&tmp, ice));
14208c2ecf20Sopenharmony_ci		if (err < 0)
14218c2ecf20Sopenharmony_ci			return err;
14228c2ecf20Sopenharmony_ci	}
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_capture_spdif_switch, ice));
14258c2ecf20Sopenharmony_ci	if (err < 0)
14268c2ecf20Sopenharmony_ci		return err;
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	if (ice->num_total_adcs > 0) {
14298c2ecf20Sopenharmony_ci		struct snd_kcontrol_new tmp = snd_ice1712_multi_capture_analog_volume;
14308c2ecf20Sopenharmony_ci		tmp.count = ice->num_total_adcs;
14318c2ecf20Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&tmp, ice));
14328c2ecf20Sopenharmony_ci		if (err < 0)
14338c2ecf20Sopenharmony_ci			return err;
14348c2ecf20Sopenharmony_ci	}
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_capture_spdif_volume, ice));
14378c2ecf20Sopenharmony_ci	if (err < 0)
14388c2ecf20Sopenharmony_ci		return err;
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	/* initialize volumes */
14418c2ecf20Sopenharmony_ci	for (idx = 0; idx < 10; idx++) {
14428c2ecf20Sopenharmony_ci		ice->pro_volumes[idx] = 0x80008000;	/* mute */
14438c2ecf20Sopenharmony_ci		snd_ice1712_update_volume(ice, idx);
14448c2ecf20Sopenharmony_ci	}
14458c2ecf20Sopenharmony_ci	for (idx = 10; idx < 10 + ice->num_total_adcs; idx++) {
14468c2ecf20Sopenharmony_ci		ice->pro_volumes[idx] = 0x80008000;	/* mute */
14478c2ecf20Sopenharmony_ci		snd_ice1712_update_volume(ice, idx);
14488c2ecf20Sopenharmony_ci	}
14498c2ecf20Sopenharmony_ci	for (idx = 18; idx < 20; idx++) {
14508c2ecf20Sopenharmony_ci		ice->pro_volumes[idx] = 0x80008000;	/* mute */
14518c2ecf20Sopenharmony_ci		snd_ice1712_update_volume(ice, idx);
14528c2ecf20Sopenharmony_ci	}
14538c2ecf20Sopenharmony_ci	return 0;
14548c2ecf20Sopenharmony_ci}
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_cistatic void snd_ice1712_mixer_free_ac97(struct snd_ac97 *ac97)
14578c2ecf20Sopenharmony_ci{
14588c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = ac97->private_data;
14598c2ecf20Sopenharmony_ci	ice->ac97 = NULL;
14608c2ecf20Sopenharmony_ci}
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_cistatic int snd_ice1712_ac97_mixer(struct snd_ice1712 *ice)
14638c2ecf20Sopenharmony_ci{
14648c2ecf20Sopenharmony_ci	int err, bus_num = 0;
14658c2ecf20Sopenharmony_ci	struct snd_ac97_template ac97;
14668c2ecf20Sopenharmony_ci	struct snd_ac97_bus *pbus;
14678c2ecf20Sopenharmony_ci	static const struct snd_ac97_bus_ops con_ops = {
14688c2ecf20Sopenharmony_ci		.write = snd_ice1712_ac97_write,
14698c2ecf20Sopenharmony_ci		.read = snd_ice1712_ac97_read,
14708c2ecf20Sopenharmony_ci	};
14718c2ecf20Sopenharmony_ci	static const struct snd_ac97_bus_ops pro_ops = {
14728c2ecf20Sopenharmony_ci		.write = snd_ice1712_pro_ac97_write,
14738c2ecf20Sopenharmony_ci		.read = snd_ice1712_pro_ac97_read,
14748c2ecf20Sopenharmony_ci	};
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	if (ice_has_con_ac97(ice)) {
14778c2ecf20Sopenharmony_ci		err = snd_ac97_bus(ice->card, bus_num++, &con_ops, NULL, &pbus);
14788c2ecf20Sopenharmony_ci		if (err < 0)
14798c2ecf20Sopenharmony_ci			return err;
14808c2ecf20Sopenharmony_ci		memset(&ac97, 0, sizeof(ac97));
14818c2ecf20Sopenharmony_ci		ac97.private_data = ice;
14828c2ecf20Sopenharmony_ci		ac97.private_free = snd_ice1712_mixer_free_ac97;
14838c2ecf20Sopenharmony_ci		err = snd_ac97_mixer(pbus, &ac97, &ice->ac97);
14848c2ecf20Sopenharmony_ci		if (err < 0)
14858c2ecf20Sopenharmony_ci			dev_warn(ice->card->dev,
14868c2ecf20Sopenharmony_ci				 "cannot initialize ac97 for consumer, skipped\n");
14878c2ecf20Sopenharmony_ci		else {
14888c2ecf20Sopenharmony_ci			return snd_ctl_add(ice->card,
14898c2ecf20Sopenharmony_ci			snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97,
14908c2ecf20Sopenharmony_ci				     ice));
14918c2ecf20Sopenharmony_ci		}
14928c2ecf20Sopenharmony_ci	}
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci	if (!(ice->eeprom.data[ICE_EEP1_ACLINK] & ICE1712_CFG_PRO_I2S)) {
14958c2ecf20Sopenharmony_ci		err = snd_ac97_bus(ice->card, bus_num, &pro_ops, NULL, &pbus);
14968c2ecf20Sopenharmony_ci		if (err < 0)
14978c2ecf20Sopenharmony_ci			return err;
14988c2ecf20Sopenharmony_ci		memset(&ac97, 0, sizeof(ac97));
14998c2ecf20Sopenharmony_ci		ac97.private_data = ice;
15008c2ecf20Sopenharmony_ci		ac97.private_free = snd_ice1712_mixer_free_ac97;
15018c2ecf20Sopenharmony_ci		err = snd_ac97_mixer(pbus, &ac97, &ice->ac97);
15028c2ecf20Sopenharmony_ci		if (err < 0)
15038c2ecf20Sopenharmony_ci			dev_warn(ice->card->dev,
15048c2ecf20Sopenharmony_ci				 "cannot initialize pro ac97, skipped\n");
15058c2ecf20Sopenharmony_ci		else
15068c2ecf20Sopenharmony_ci			return 0;
15078c2ecf20Sopenharmony_ci	}
15088c2ecf20Sopenharmony_ci	/* I2S mixer only */
15098c2ecf20Sopenharmony_ci	strcat(ice->card->mixername, "ICE1712 - multitrack");
15108c2ecf20Sopenharmony_ci	return 0;
15118c2ecf20Sopenharmony_ci}
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci/*
15148c2ecf20Sopenharmony_ci *
15158c2ecf20Sopenharmony_ci */
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_cistatic inline unsigned int eeprom_double(struct snd_ice1712 *ice, int idx)
15188c2ecf20Sopenharmony_ci{
15198c2ecf20Sopenharmony_ci	return (unsigned int)ice->eeprom.data[idx] | ((unsigned int)ice->eeprom.data[idx + 1] << 8);
15208c2ecf20Sopenharmony_ci}
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_cistatic void snd_ice1712_proc_read(struct snd_info_entry *entry,
15238c2ecf20Sopenharmony_ci				  struct snd_info_buffer *buffer)
15248c2ecf20Sopenharmony_ci{
15258c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = entry->private_data;
15268c2ecf20Sopenharmony_ci	unsigned int idx;
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "%s\n\n", ice->card->longname);
15298c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "EEPROM:\n");
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  Subvendor        : 0x%x\n", ice->eeprom.subvendor);
15328c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  Size             : %i bytes\n", ice->eeprom.size);
15338c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  Version          : %i\n", ice->eeprom.version);
15348c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  Codec            : 0x%x\n", ice->eeprom.data[ICE_EEP1_CODEC]);
15358c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  ACLink           : 0x%x\n", ice->eeprom.data[ICE_EEP1_ACLINK]);
15368c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  I2S ID           : 0x%x\n", ice->eeprom.data[ICE_EEP1_I2SID]);
15378c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  S/PDIF           : 0x%x\n", ice->eeprom.data[ICE_EEP1_SPDIF]);
15388c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  GPIO mask        : 0x%x\n", ice->eeprom.gpiomask);
15398c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  GPIO state       : 0x%x\n", ice->eeprom.gpiostate);
15408c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  GPIO direction   : 0x%x\n", ice->eeprom.gpiodir);
15418c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  AC'97 main       : 0x%x\n", eeprom_double(ice, ICE_EEP1_AC97_MAIN_LO));
15428c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  AC'97 pcm        : 0x%x\n", eeprom_double(ice, ICE_EEP1_AC97_PCM_LO));
15438c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  AC'97 record     : 0x%x\n", eeprom_double(ice, ICE_EEP1_AC97_REC_LO));
15448c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  AC'97 record src : 0x%x\n", ice->eeprom.data[ICE_EEP1_AC97_RECSRC]);
15458c2ecf20Sopenharmony_ci	for (idx = 0; idx < 4; idx++)
15468c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "  DAC ID #%i        : 0x%x\n", idx, ice->eeprom.data[ICE_EEP1_DAC_ID + idx]);
15478c2ecf20Sopenharmony_ci	for (idx = 0; idx < 4; idx++)
15488c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "  ADC ID #%i        : 0x%x\n", idx, ice->eeprom.data[ICE_EEP1_ADC_ID + idx]);
15498c2ecf20Sopenharmony_ci	for (idx = 0x1c; idx < ice->eeprom.size; idx++)
15508c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "  Extra #%02i        : 0x%x\n", idx, ice->eeprom.data[idx]);
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "\nRegisters:\n");
15538c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  PSDOUT03         : 0x%04x\n", (unsigned)inw(ICEMT(ice, ROUTE_PSDOUT03)));
15548c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  CAPTURE          : 0x%08x\n", inl(ICEMT(ice, ROUTE_CAPTURE)));
15558c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  SPDOUT           : 0x%04x\n", (unsigned)inw(ICEMT(ice, ROUTE_SPDOUT)));
15568c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  RATE             : 0x%02x\n", (unsigned)inb(ICEMT(ice, RATE)));
15578c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  GPIO_DATA        : 0x%02x\n", (unsigned)snd_ice1712_get_gpio_data(ice));
15588c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  GPIO_WRITE_MASK  : 0x%02x\n", (unsigned)snd_ice1712_read(ice, ICE1712_IREG_GPIO_WRITE_MASK));
15598c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  GPIO_DIRECTION   : 0x%02x\n", (unsigned)snd_ice1712_read(ice, ICE1712_IREG_GPIO_DIRECTION));
15608c2ecf20Sopenharmony_ci}
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_cistatic void snd_ice1712_proc_init(struct snd_ice1712 *ice)
15638c2ecf20Sopenharmony_ci{
15648c2ecf20Sopenharmony_ci	snd_card_ro_proc_new(ice->card, "ice1712", ice, snd_ice1712_proc_read);
15658c2ecf20Sopenharmony_ci}
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci/*
15688c2ecf20Sopenharmony_ci *
15698c2ecf20Sopenharmony_ci */
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_cistatic int snd_ice1712_eeprom_info(struct snd_kcontrol *kcontrol,
15728c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_info *uinfo)
15738c2ecf20Sopenharmony_ci{
15748c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
15758c2ecf20Sopenharmony_ci	uinfo->count = sizeof(struct snd_ice1712_eeprom);
15768c2ecf20Sopenharmony_ci	return 0;
15778c2ecf20Sopenharmony_ci}
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_cistatic int snd_ice1712_eeprom_get(struct snd_kcontrol *kcontrol,
15808c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
15818c2ecf20Sopenharmony_ci{
15828c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	memcpy(ucontrol->value.bytes.data, &ice->eeprom, sizeof(ice->eeprom));
15858c2ecf20Sopenharmony_ci	return 0;
15868c2ecf20Sopenharmony_ci}
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_eeprom = {
15898c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
15908c2ecf20Sopenharmony_ci	.name = "ICE1712 EEPROM",
15918c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ,
15928c2ecf20Sopenharmony_ci	.info = snd_ice1712_eeprom_info,
15938c2ecf20Sopenharmony_ci	.get = snd_ice1712_eeprom_get
15948c2ecf20Sopenharmony_ci};
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci/*
15978c2ecf20Sopenharmony_ci */
15988c2ecf20Sopenharmony_cistatic int snd_ice1712_spdif_info(struct snd_kcontrol *kcontrol,
15998c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
16008c2ecf20Sopenharmony_ci{
16018c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
16028c2ecf20Sopenharmony_ci	uinfo->count = 1;
16038c2ecf20Sopenharmony_ci	return 0;
16048c2ecf20Sopenharmony_ci}
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_cistatic int snd_ice1712_spdif_default_get(struct snd_kcontrol *kcontrol,
16078c2ecf20Sopenharmony_ci					 struct snd_ctl_elem_value *ucontrol)
16088c2ecf20Sopenharmony_ci{
16098c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
16108c2ecf20Sopenharmony_ci	if (ice->spdif.ops.default_get)
16118c2ecf20Sopenharmony_ci		ice->spdif.ops.default_get(ice, ucontrol);
16128c2ecf20Sopenharmony_ci	return 0;
16138c2ecf20Sopenharmony_ci}
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_cistatic int snd_ice1712_spdif_default_put(struct snd_kcontrol *kcontrol,
16168c2ecf20Sopenharmony_ci					 struct snd_ctl_elem_value *ucontrol)
16178c2ecf20Sopenharmony_ci{
16188c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
16198c2ecf20Sopenharmony_ci	if (ice->spdif.ops.default_put)
16208c2ecf20Sopenharmony_ci		return ice->spdif.ops.default_put(ice, ucontrol);
16218c2ecf20Sopenharmony_ci	return 0;
16228c2ecf20Sopenharmony_ci}
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_spdif_default =
16258c2ecf20Sopenharmony_ci{
16268c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
16278c2ecf20Sopenharmony_ci	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
16288c2ecf20Sopenharmony_ci	.info =		snd_ice1712_spdif_info,
16298c2ecf20Sopenharmony_ci	.get =		snd_ice1712_spdif_default_get,
16308c2ecf20Sopenharmony_ci	.put =		snd_ice1712_spdif_default_put
16318c2ecf20Sopenharmony_ci};
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_cistatic int snd_ice1712_spdif_maskc_get(struct snd_kcontrol *kcontrol,
16348c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
16358c2ecf20Sopenharmony_ci{
16368c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
16378c2ecf20Sopenharmony_ci	if (ice->spdif.ops.default_get) {
16388c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
16398c2ecf20Sopenharmony_ci						     IEC958_AES0_PROFESSIONAL |
16408c2ecf20Sopenharmony_ci						     IEC958_AES0_CON_NOT_COPYRIGHT |
16418c2ecf20Sopenharmony_ci						     IEC958_AES0_CON_EMPHASIS;
16428c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL |
16438c2ecf20Sopenharmony_ci						     IEC958_AES1_CON_CATEGORY;
16448c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
16458c2ecf20Sopenharmony_ci	} else {
16468c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[0] = 0xff;
16478c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[1] = 0xff;
16488c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[2] = 0xff;
16498c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[3] = 0xff;
16508c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[4] = 0xff;
16518c2ecf20Sopenharmony_ci	}
16528c2ecf20Sopenharmony_ci	return 0;
16538c2ecf20Sopenharmony_ci}
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_cistatic int snd_ice1712_spdif_maskp_get(struct snd_kcontrol *kcontrol,
16568c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
16578c2ecf20Sopenharmony_ci{
16588c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
16598c2ecf20Sopenharmony_ci	if (ice->spdif.ops.default_get) {
16608c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
16618c2ecf20Sopenharmony_ci						     IEC958_AES0_PROFESSIONAL |
16628c2ecf20Sopenharmony_ci						     IEC958_AES0_PRO_FS |
16638c2ecf20Sopenharmony_ci						     IEC958_AES0_PRO_EMPHASIS;
16648c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[1] = IEC958_AES1_PRO_MODE;
16658c2ecf20Sopenharmony_ci	} else {
16668c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[0] = 0xff;
16678c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[1] = 0xff;
16688c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[2] = 0xff;
16698c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[3] = 0xff;
16708c2ecf20Sopenharmony_ci		ucontrol->value.iec958.status[4] = 0xff;
16718c2ecf20Sopenharmony_ci	}
16728c2ecf20Sopenharmony_ci	return 0;
16738c2ecf20Sopenharmony_ci}
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_spdif_maskc =
16768c2ecf20Sopenharmony_ci{
16778c2ecf20Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
16788c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
16798c2ecf20Sopenharmony_ci	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
16808c2ecf20Sopenharmony_ci	.info =		snd_ice1712_spdif_info,
16818c2ecf20Sopenharmony_ci	.get =		snd_ice1712_spdif_maskc_get,
16828c2ecf20Sopenharmony_ci};
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_spdif_maskp =
16858c2ecf20Sopenharmony_ci{
16868c2ecf20Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
16878c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
16888c2ecf20Sopenharmony_ci	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
16898c2ecf20Sopenharmony_ci	.info =		snd_ice1712_spdif_info,
16908c2ecf20Sopenharmony_ci	.get =		snd_ice1712_spdif_maskp_get,
16918c2ecf20Sopenharmony_ci};
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_cistatic int snd_ice1712_spdif_stream_get(struct snd_kcontrol *kcontrol,
16948c2ecf20Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
16958c2ecf20Sopenharmony_ci{
16968c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
16978c2ecf20Sopenharmony_ci	if (ice->spdif.ops.stream_get)
16988c2ecf20Sopenharmony_ci		ice->spdif.ops.stream_get(ice, ucontrol);
16998c2ecf20Sopenharmony_ci	return 0;
17008c2ecf20Sopenharmony_ci}
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_cistatic int snd_ice1712_spdif_stream_put(struct snd_kcontrol *kcontrol,
17038c2ecf20Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
17048c2ecf20Sopenharmony_ci{
17058c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
17068c2ecf20Sopenharmony_ci	if (ice->spdif.ops.stream_put)
17078c2ecf20Sopenharmony_ci		return ice->spdif.ops.stream_put(ice, ucontrol);
17088c2ecf20Sopenharmony_ci	return 0;
17098c2ecf20Sopenharmony_ci}
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_spdif_stream =
17128c2ecf20Sopenharmony_ci{
17138c2ecf20Sopenharmony_ci	.access =	(SNDRV_CTL_ELEM_ACCESS_READWRITE |
17148c2ecf20Sopenharmony_ci			 SNDRV_CTL_ELEM_ACCESS_INACTIVE),
17158c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
17168c2ecf20Sopenharmony_ci	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
17178c2ecf20Sopenharmony_ci	.info =		snd_ice1712_spdif_info,
17188c2ecf20Sopenharmony_ci	.get =		snd_ice1712_spdif_stream_get,
17198c2ecf20Sopenharmony_ci	.put =		snd_ice1712_spdif_stream_put
17208c2ecf20Sopenharmony_ci};
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ciint snd_ice1712_gpio_get(struct snd_kcontrol *kcontrol,
17238c2ecf20Sopenharmony_ci			 struct snd_ctl_elem_value *ucontrol)
17248c2ecf20Sopenharmony_ci{
17258c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
17268c2ecf20Sopenharmony_ci	unsigned char mask = kcontrol->private_value & 0xff;
17278c2ecf20Sopenharmony_ci	int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0;
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	snd_ice1712_save_gpio_status(ice);
17308c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] =
17318c2ecf20Sopenharmony_ci		(snd_ice1712_gpio_read(ice) & mask ? 1 : 0) ^ invert;
17328c2ecf20Sopenharmony_ci	snd_ice1712_restore_gpio_status(ice);
17338c2ecf20Sopenharmony_ci	return 0;
17348c2ecf20Sopenharmony_ci}
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ciint snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol,
17378c2ecf20Sopenharmony_ci			 struct snd_ctl_elem_value *ucontrol)
17388c2ecf20Sopenharmony_ci{
17398c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
17408c2ecf20Sopenharmony_ci	unsigned char mask = kcontrol->private_value & 0xff;
17418c2ecf20Sopenharmony_ci	int invert = (kcontrol->private_value & (1<<24)) ? mask : 0;
17428c2ecf20Sopenharmony_ci	unsigned int val, nval;
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	if (kcontrol->private_value & (1 << 31))
17458c2ecf20Sopenharmony_ci		return -EPERM;
17468c2ecf20Sopenharmony_ci	nval = (ucontrol->value.integer.value[0] ? mask : 0) ^ invert;
17478c2ecf20Sopenharmony_ci	snd_ice1712_save_gpio_status(ice);
17488c2ecf20Sopenharmony_ci	val = snd_ice1712_gpio_read(ice);
17498c2ecf20Sopenharmony_ci	nval |= val & ~mask;
17508c2ecf20Sopenharmony_ci	if (val != nval)
17518c2ecf20Sopenharmony_ci		snd_ice1712_gpio_write(ice, nval);
17528c2ecf20Sopenharmony_ci	snd_ice1712_restore_gpio_status(ice);
17538c2ecf20Sopenharmony_ci	return val != nval;
17548c2ecf20Sopenharmony_ci}
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_ci/*
17578c2ecf20Sopenharmony_ci *  rate
17588c2ecf20Sopenharmony_ci */
17598c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
17608c2ecf20Sopenharmony_ci					       struct snd_ctl_elem_info *uinfo)
17618c2ecf20Sopenharmony_ci{
17628c2ecf20Sopenharmony_ci	static const char * const texts[] = {
17638c2ecf20Sopenharmony_ci		"8000",		/* 0: 6 */
17648c2ecf20Sopenharmony_ci		"9600",		/* 1: 3 */
17658c2ecf20Sopenharmony_ci		"11025",	/* 2: 10 */
17668c2ecf20Sopenharmony_ci		"12000",	/* 3: 2 */
17678c2ecf20Sopenharmony_ci		"16000",	/* 4: 5 */
17688c2ecf20Sopenharmony_ci		"22050",	/* 5: 9 */
17698c2ecf20Sopenharmony_ci		"24000",	/* 6: 1 */
17708c2ecf20Sopenharmony_ci		"32000",	/* 7: 4 */
17718c2ecf20Sopenharmony_ci		"44100",	/* 8: 8 */
17728c2ecf20Sopenharmony_ci		"48000",	/* 9: 0 */
17738c2ecf20Sopenharmony_ci		"64000",	/* 10: 15 */
17748c2ecf20Sopenharmony_ci		"88200",	/* 11: 11 */
17758c2ecf20Sopenharmony_ci		"96000",	/* 12: 7 */
17768c2ecf20Sopenharmony_ci		"IEC958 Input",	/* 13: -- */
17778c2ecf20Sopenharmony_ci	};
17788c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 14, texts);
17798c2ecf20Sopenharmony_ci}
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
17828c2ecf20Sopenharmony_ci					      struct snd_ctl_elem_value *ucontrol)
17838c2ecf20Sopenharmony_ci{
17848c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
17858c2ecf20Sopenharmony_ci	static const unsigned char xlate[16] = {
17868c2ecf20Sopenharmony_ci		9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 255, 255, 255, 10
17878c2ecf20Sopenharmony_ci	};
17888c2ecf20Sopenharmony_ci	unsigned char val;
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
17918c2ecf20Sopenharmony_ci	if (is_spdif_master(ice)) {
17928c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 13;
17938c2ecf20Sopenharmony_ci	} else {
17948c2ecf20Sopenharmony_ci		val = xlate[inb(ICEMT(ice, RATE)) & 15];
17958c2ecf20Sopenharmony_ci		if (val == 255) {
17968c2ecf20Sopenharmony_ci			snd_BUG();
17978c2ecf20Sopenharmony_ci			val = 0;
17988c2ecf20Sopenharmony_ci		}
17998c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = val;
18008c2ecf20Sopenharmony_ci	}
18018c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
18028c2ecf20Sopenharmony_ci	return 0;
18038c2ecf20Sopenharmony_ci}
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
18068c2ecf20Sopenharmony_ci					      struct snd_ctl_elem_value *ucontrol)
18078c2ecf20Sopenharmony_ci{
18088c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
18098c2ecf20Sopenharmony_ci	static const unsigned int xrate[13] = {
18108c2ecf20Sopenharmony_ci		8000, 9600, 11025, 12000, 16000, 22050, 24000,
18118c2ecf20Sopenharmony_ci		32000, 44100, 48000, 64000, 88200, 96000
18128c2ecf20Sopenharmony_ci	};
18138c2ecf20Sopenharmony_ci	unsigned char oval;
18148c2ecf20Sopenharmony_ci	int change = 0;
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
18178c2ecf20Sopenharmony_ci	oval = inb(ICEMT(ice, RATE));
18188c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] == 13) {
18198c2ecf20Sopenharmony_ci		outb(oval | ICE1712_SPDIF_MASTER, ICEMT(ice, RATE));
18208c2ecf20Sopenharmony_ci	} else {
18218c2ecf20Sopenharmony_ci		PRO_RATE_DEFAULT = xrate[ucontrol->value.integer.value[0] % 13];
18228c2ecf20Sopenharmony_ci		spin_unlock_irq(&ice->reg_lock);
18238c2ecf20Sopenharmony_ci		snd_ice1712_set_pro_rate(ice, PRO_RATE_DEFAULT, 1);
18248c2ecf20Sopenharmony_ci		spin_lock_irq(&ice->reg_lock);
18258c2ecf20Sopenharmony_ci	}
18268c2ecf20Sopenharmony_ci	change = inb(ICEMT(ice, RATE)) != oval;
18278c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci	if ((oval & ICE1712_SPDIF_MASTER) !=
18308c2ecf20Sopenharmony_ci	    (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER))
18318c2ecf20Sopenharmony_ci		snd_ice1712_set_input_clock_source(ice, is_spdif_master(ice));
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci	return change;
18348c2ecf20Sopenharmony_ci}
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_pro_internal_clock = {
18378c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
18388c2ecf20Sopenharmony_ci	.name = "Multi Track Internal Clock",
18398c2ecf20Sopenharmony_ci	.info = snd_ice1712_pro_internal_clock_info,
18408c2ecf20Sopenharmony_ci	.get = snd_ice1712_pro_internal_clock_get,
18418c2ecf20Sopenharmony_ci	.put = snd_ice1712_pro_internal_clock_put
18428c2ecf20Sopenharmony_ci};
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_internal_clock_default_info(struct snd_kcontrol *kcontrol,
18458c2ecf20Sopenharmony_ci						       struct snd_ctl_elem_info *uinfo)
18468c2ecf20Sopenharmony_ci{
18478c2ecf20Sopenharmony_ci	static const char * const texts[] = {
18488c2ecf20Sopenharmony_ci		"8000",		/* 0: 6 */
18498c2ecf20Sopenharmony_ci		"9600",		/* 1: 3 */
18508c2ecf20Sopenharmony_ci		"11025",	/* 2: 10 */
18518c2ecf20Sopenharmony_ci		"12000",	/* 3: 2 */
18528c2ecf20Sopenharmony_ci		"16000",	/* 4: 5 */
18538c2ecf20Sopenharmony_ci		"22050",	/* 5: 9 */
18548c2ecf20Sopenharmony_ci		"24000",	/* 6: 1 */
18558c2ecf20Sopenharmony_ci		"32000",	/* 7: 4 */
18568c2ecf20Sopenharmony_ci		"44100",	/* 8: 8 */
18578c2ecf20Sopenharmony_ci		"48000",	/* 9: 0 */
18588c2ecf20Sopenharmony_ci		"64000",	/* 10: 15 */
18598c2ecf20Sopenharmony_ci		"88200",	/* 11: 11 */
18608c2ecf20Sopenharmony_ci		"96000",	/* 12: 7 */
18618c2ecf20Sopenharmony_ci		/* "IEC958 Input",	13: -- */
18628c2ecf20Sopenharmony_ci	};
18638c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 13, texts);
18648c2ecf20Sopenharmony_ci}
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_internal_clock_default_get(struct snd_kcontrol *kcontrol,
18678c2ecf20Sopenharmony_ci						      struct snd_ctl_elem_value *ucontrol)
18688c2ecf20Sopenharmony_ci{
18698c2ecf20Sopenharmony_ci	int val;
18708c2ecf20Sopenharmony_ci	static const unsigned int xrate[13] = {
18718c2ecf20Sopenharmony_ci		8000, 9600, 11025, 12000, 16000, 22050, 24000,
18728c2ecf20Sopenharmony_ci		32000, 44100, 48000, 64000, 88200, 96000
18738c2ecf20Sopenharmony_ci	};
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci	for (val = 0; val < 13; val++) {
18768c2ecf20Sopenharmony_ci		if (xrate[val] == PRO_RATE_DEFAULT)
18778c2ecf20Sopenharmony_ci			break;
18788c2ecf20Sopenharmony_ci	}
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = val;
18818c2ecf20Sopenharmony_ci	return 0;
18828c2ecf20Sopenharmony_ci}
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_internal_clock_default_put(struct snd_kcontrol *kcontrol,
18858c2ecf20Sopenharmony_ci						      struct snd_ctl_elem_value *ucontrol)
18868c2ecf20Sopenharmony_ci{
18878c2ecf20Sopenharmony_ci	static const unsigned int xrate[13] = {
18888c2ecf20Sopenharmony_ci		8000, 9600, 11025, 12000, 16000, 22050, 24000,
18898c2ecf20Sopenharmony_ci		32000, 44100, 48000, 64000, 88200, 96000
18908c2ecf20Sopenharmony_ci	};
18918c2ecf20Sopenharmony_ci	unsigned char oval;
18928c2ecf20Sopenharmony_ci	int change = 0;
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	oval = PRO_RATE_DEFAULT;
18958c2ecf20Sopenharmony_ci	PRO_RATE_DEFAULT = xrate[ucontrol->value.integer.value[0] % 13];
18968c2ecf20Sopenharmony_ci	change = PRO_RATE_DEFAULT != oval;
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	return change;
18998c2ecf20Sopenharmony_ci}
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_pro_internal_clock_default = {
19028c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
19038c2ecf20Sopenharmony_ci	.name = "Multi Track Internal Clock Default",
19048c2ecf20Sopenharmony_ci	.info = snd_ice1712_pro_internal_clock_default_info,
19058c2ecf20Sopenharmony_ci	.get = snd_ice1712_pro_internal_clock_default_get,
19068c2ecf20Sopenharmony_ci	.put = snd_ice1712_pro_internal_clock_default_put
19078c2ecf20Sopenharmony_ci};
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_ci#define snd_ice1712_pro_rate_locking_info	snd_ctl_boolean_mono_info
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_rate_locking_get(struct snd_kcontrol *kcontrol,
19128c2ecf20Sopenharmony_ci					    struct snd_ctl_elem_value *ucontrol)
19138c2ecf20Sopenharmony_ci{
19148c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = PRO_RATE_LOCKED;
19158c2ecf20Sopenharmony_ci	return 0;
19168c2ecf20Sopenharmony_ci}
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_rate_locking_put(struct snd_kcontrol *kcontrol,
19198c2ecf20Sopenharmony_ci					    struct snd_ctl_elem_value *ucontrol)
19208c2ecf20Sopenharmony_ci{
19218c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
19228c2ecf20Sopenharmony_ci	int change = 0, nval;
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci	nval = ucontrol->value.integer.value[0] ? 1 : 0;
19258c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
19268c2ecf20Sopenharmony_ci	change = PRO_RATE_LOCKED != nval;
19278c2ecf20Sopenharmony_ci	PRO_RATE_LOCKED = nval;
19288c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
19298c2ecf20Sopenharmony_ci	return change;
19308c2ecf20Sopenharmony_ci}
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_pro_rate_locking = {
19338c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
19348c2ecf20Sopenharmony_ci	.name = "Multi Track Rate Locking",
19358c2ecf20Sopenharmony_ci	.info = snd_ice1712_pro_rate_locking_info,
19368c2ecf20Sopenharmony_ci	.get = snd_ice1712_pro_rate_locking_get,
19378c2ecf20Sopenharmony_ci	.put = snd_ice1712_pro_rate_locking_put
19388c2ecf20Sopenharmony_ci};
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_ci#define snd_ice1712_pro_rate_reset_info		snd_ctl_boolean_mono_info
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_rate_reset_get(struct snd_kcontrol *kcontrol,
19438c2ecf20Sopenharmony_ci					  struct snd_ctl_elem_value *ucontrol)
19448c2ecf20Sopenharmony_ci{
19458c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = PRO_RATE_RESET;
19468c2ecf20Sopenharmony_ci	return 0;
19478c2ecf20Sopenharmony_ci}
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_rate_reset_put(struct snd_kcontrol *kcontrol,
19508c2ecf20Sopenharmony_ci					  struct snd_ctl_elem_value *ucontrol)
19518c2ecf20Sopenharmony_ci{
19528c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
19538c2ecf20Sopenharmony_ci	int change = 0, nval;
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_ci	nval = ucontrol->value.integer.value[0] ? 1 : 0;
19568c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
19578c2ecf20Sopenharmony_ci	change = PRO_RATE_RESET != nval;
19588c2ecf20Sopenharmony_ci	PRO_RATE_RESET = nval;
19598c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
19608c2ecf20Sopenharmony_ci	return change;
19618c2ecf20Sopenharmony_ci}
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_pro_rate_reset = {
19648c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
19658c2ecf20Sopenharmony_ci	.name = "Multi Track Rate Reset",
19668c2ecf20Sopenharmony_ci	.info = snd_ice1712_pro_rate_reset_info,
19678c2ecf20Sopenharmony_ci	.get = snd_ice1712_pro_rate_reset_get,
19688c2ecf20Sopenharmony_ci	.put = snd_ice1712_pro_rate_reset_put
19698c2ecf20Sopenharmony_ci};
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci/*
19728c2ecf20Sopenharmony_ci * routing
19738c2ecf20Sopenharmony_ci */
19748c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_route_info(struct snd_kcontrol *kcontrol,
19758c2ecf20Sopenharmony_ci				      struct snd_ctl_elem_info *uinfo)
19768c2ecf20Sopenharmony_ci{
19778c2ecf20Sopenharmony_ci	static const char * const texts[] = {
19788c2ecf20Sopenharmony_ci		"PCM Out", /* 0 */
19798c2ecf20Sopenharmony_ci		"H/W In 0", "H/W In 1", "H/W In 2", "H/W In 3", /* 1-4 */
19808c2ecf20Sopenharmony_ci		"H/W In 4", "H/W In 5", "H/W In 6", "H/W In 7", /* 5-8 */
19818c2ecf20Sopenharmony_ci		"IEC958 In L", "IEC958 In R", /* 9-10 */
19828c2ecf20Sopenharmony_ci		"Digital Mixer", /* 11 - optional */
19838c2ecf20Sopenharmony_ci	};
19848c2ecf20Sopenharmony_ci	int num_items = snd_ctl_get_ioffidx(kcontrol, &uinfo->id) < 2 ? 12 : 11;
19858c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, num_items, texts);
19868c2ecf20Sopenharmony_ci}
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_route_analog_get(struct snd_kcontrol *kcontrol,
19898c2ecf20Sopenharmony_ci					    struct snd_ctl_elem_value *ucontrol)
19908c2ecf20Sopenharmony_ci{
19918c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
19928c2ecf20Sopenharmony_ci	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
19938c2ecf20Sopenharmony_ci	unsigned int val, cval;
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
19968c2ecf20Sopenharmony_ci	val = inw(ICEMT(ice, ROUTE_PSDOUT03));
19978c2ecf20Sopenharmony_ci	cval = inl(ICEMT(ice, ROUTE_CAPTURE));
19988c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ci	val >>= ((idx % 2) * 8) + ((idx / 2) * 2);
20018c2ecf20Sopenharmony_ci	val &= 3;
20028c2ecf20Sopenharmony_ci	cval >>= ((idx / 2) * 8) + ((idx % 2) * 4);
20038c2ecf20Sopenharmony_ci	if (val == 1 && idx < 2)
20048c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 11;
20058c2ecf20Sopenharmony_ci	else if (val == 2)
20068c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = (cval & 7) + 1;
20078c2ecf20Sopenharmony_ci	else if (val == 3)
20088c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9;
20098c2ecf20Sopenharmony_ci	else
20108c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 0;
20118c2ecf20Sopenharmony_ci	return 0;
20128c2ecf20Sopenharmony_ci}
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_route_analog_put(struct snd_kcontrol *kcontrol,
20158c2ecf20Sopenharmony_ci					    struct snd_ctl_elem_value *ucontrol)
20168c2ecf20Sopenharmony_ci{
20178c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
20188c2ecf20Sopenharmony_ci	int change, shift;
20198c2ecf20Sopenharmony_ci	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
20208c2ecf20Sopenharmony_ci	unsigned int val, old_val, nval;
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci	/* update PSDOUT */
20238c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] >= 11)
20248c2ecf20Sopenharmony_ci		nval = idx < 2 ? 1 : 0; /* dig mixer (or pcm) */
20258c2ecf20Sopenharmony_ci	else if (ucontrol->value.enumerated.item[0] >= 9)
20268c2ecf20Sopenharmony_ci		nval = 3; /* spdif in */
20278c2ecf20Sopenharmony_ci	else if (ucontrol->value.enumerated.item[0] >= 1)
20288c2ecf20Sopenharmony_ci		nval = 2; /* analog in */
20298c2ecf20Sopenharmony_ci	else
20308c2ecf20Sopenharmony_ci		nval = 0; /* pcm */
20318c2ecf20Sopenharmony_ci	shift = ((idx % 2) * 8) + ((idx / 2) * 2);
20328c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
20338c2ecf20Sopenharmony_ci	val = old_val = inw(ICEMT(ice, ROUTE_PSDOUT03));
20348c2ecf20Sopenharmony_ci	val &= ~(0x03 << shift);
20358c2ecf20Sopenharmony_ci	val |= nval << shift;
20368c2ecf20Sopenharmony_ci	change = val != old_val;
20378c2ecf20Sopenharmony_ci	if (change)
20388c2ecf20Sopenharmony_ci		outw(val, ICEMT(ice, ROUTE_PSDOUT03));
20398c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
20408c2ecf20Sopenharmony_ci	if (nval < 2) /* dig mixer of pcm */
20418c2ecf20Sopenharmony_ci		return change;
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	/* update CAPTURE */
20448c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
20458c2ecf20Sopenharmony_ci	val = old_val = inl(ICEMT(ice, ROUTE_CAPTURE));
20468c2ecf20Sopenharmony_ci	shift = ((idx / 2) * 8) + ((idx % 2) * 4);
20478c2ecf20Sopenharmony_ci	if (nval == 2) { /* analog in */
20488c2ecf20Sopenharmony_ci		nval = ucontrol->value.enumerated.item[0] - 1;
20498c2ecf20Sopenharmony_ci		val &= ~(0x07 << shift);
20508c2ecf20Sopenharmony_ci		val |= nval << shift;
20518c2ecf20Sopenharmony_ci	} else { /* spdif in */
20528c2ecf20Sopenharmony_ci		nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
20538c2ecf20Sopenharmony_ci		val &= ~(0x08 << shift);
20548c2ecf20Sopenharmony_ci		val |= nval << shift;
20558c2ecf20Sopenharmony_ci	}
20568c2ecf20Sopenharmony_ci	if (val != old_val) {
20578c2ecf20Sopenharmony_ci		change = 1;
20588c2ecf20Sopenharmony_ci		outl(val, ICEMT(ice, ROUTE_CAPTURE));
20598c2ecf20Sopenharmony_ci	}
20608c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
20618c2ecf20Sopenharmony_ci	return change;
20628c2ecf20Sopenharmony_ci}
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_route_spdif_get(struct snd_kcontrol *kcontrol,
20658c2ecf20Sopenharmony_ci					   struct snd_ctl_elem_value *ucontrol)
20668c2ecf20Sopenharmony_ci{
20678c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
20688c2ecf20Sopenharmony_ci	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
20698c2ecf20Sopenharmony_ci	unsigned int val, cval;
20708c2ecf20Sopenharmony_ci	val = inw(ICEMT(ice, ROUTE_SPDOUT));
20718c2ecf20Sopenharmony_ci	cval = (val >> (idx * 4 + 8)) & 0x0f;
20728c2ecf20Sopenharmony_ci	val = (val >> (idx * 2)) & 0x03;
20738c2ecf20Sopenharmony_ci	if (val == 1)
20748c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 11;
20758c2ecf20Sopenharmony_ci	else if (val == 2)
20768c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = (cval & 7) + 1;
20778c2ecf20Sopenharmony_ci	else if (val == 3)
20788c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9;
20798c2ecf20Sopenharmony_ci	else
20808c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 0;
20818c2ecf20Sopenharmony_ci	return 0;
20828c2ecf20Sopenharmony_ci}
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
20858c2ecf20Sopenharmony_ci					   struct snd_ctl_elem_value *ucontrol)
20868c2ecf20Sopenharmony_ci{
20878c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
20888c2ecf20Sopenharmony_ci	int change, shift;
20898c2ecf20Sopenharmony_ci	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
20908c2ecf20Sopenharmony_ci	unsigned int val, old_val, nval;
20918c2ecf20Sopenharmony_ci
20928c2ecf20Sopenharmony_ci	/* update SPDOUT */
20938c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
20948c2ecf20Sopenharmony_ci	val = old_val = inw(ICEMT(ice, ROUTE_SPDOUT));
20958c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] >= 11)
20968c2ecf20Sopenharmony_ci		nval = 1;
20978c2ecf20Sopenharmony_ci	else if (ucontrol->value.enumerated.item[0] >= 9)
20988c2ecf20Sopenharmony_ci		nval = 3;
20998c2ecf20Sopenharmony_ci	else if (ucontrol->value.enumerated.item[0] >= 1)
21008c2ecf20Sopenharmony_ci		nval = 2;
21018c2ecf20Sopenharmony_ci	else
21028c2ecf20Sopenharmony_ci		nval = 0;
21038c2ecf20Sopenharmony_ci	shift = idx * 2;
21048c2ecf20Sopenharmony_ci	val &= ~(0x03 << shift);
21058c2ecf20Sopenharmony_ci	val |= nval << shift;
21068c2ecf20Sopenharmony_ci	shift = idx * 4 + 8;
21078c2ecf20Sopenharmony_ci	if (nval == 2) {
21088c2ecf20Sopenharmony_ci		nval = ucontrol->value.enumerated.item[0] - 1;
21098c2ecf20Sopenharmony_ci		val &= ~(0x07 << shift);
21108c2ecf20Sopenharmony_ci		val |= nval << shift;
21118c2ecf20Sopenharmony_ci	} else if (nval == 3) {
21128c2ecf20Sopenharmony_ci		nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
21138c2ecf20Sopenharmony_ci		val &= ~(0x08 << shift);
21148c2ecf20Sopenharmony_ci		val |= nval << shift;
21158c2ecf20Sopenharmony_ci	}
21168c2ecf20Sopenharmony_ci	change = val != old_val;
21178c2ecf20Sopenharmony_ci	if (change)
21188c2ecf20Sopenharmony_ci		outw(val, ICEMT(ice, ROUTE_SPDOUT));
21198c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
21208c2ecf20Sopenharmony_ci	return change;
21218c2ecf20Sopenharmony_ci}
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route = {
21248c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
21258c2ecf20Sopenharmony_ci	.name = "H/W Playback Route",
21268c2ecf20Sopenharmony_ci	.info = snd_ice1712_pro_route_info,
21278c2ecf20Sopenharmony_ci	.get = snd_ice1712_pro_route_analog_get,
21288c2ecf20Sopenharmony_ci	.put = snd_ice1712_pro_route_analog_put,
21298c2ecf20Sopenharmony_ci};
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route = {
21328c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
21338c2ecf20Sopenharmony_ci	.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",
21348c2ecf20Sopenharmony_ci	.info = snd_ice1712_pro_route_info,
21358c2ecf20Sopenharmony_ci	.get = snd_ice1712_pro_route_spdif_get,
21368c2ecf20Sopenharmony_ci	.put = snd_ice1712_pro_route_spdif_put,
21378c2ecf20Sopenharmony_ci	.count = 2,
21388c2ecf20Sopenharmony_ci};
21398c2ecf20Sopenharmony_ci
21408c2ecf20Sopenharmony_ci
21418c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_volume_rate_info(struct snd_kcontrol *kcontrol,
21428c2ecf20Sopenharmony_ci					    struct snd_ctl_elem_info *uinfo)
21438c2ecf20Sopenharmony_ci{
21448c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
21458c2ecf20Sopenharmony_ci	uinfo->count = 1;
21468c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
21478c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 255;
21488c2ecf20Sopenharmony_ci	return 0;
21498c2ecf20Sopenharmony_ci}
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_volume_rate_get(struct snd_kcontrol *kcontrol,
21528c2ecf20Sopenharmony_ci					   struct snd_ctl_elem_value *ucontrol)
21538c2ecf20Sopenharmony_ci{
21548c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_RATE));
21578c2ecf20Sopenharmony_ci	return 0;
21588c2ecf20Sopenharmony_ci}
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_volume_rate_put(struct snd_kcontrol *kcontrol,
21618c2ecf20Sopenharmony_ci					   struct snd_ctl_elem_value *ucontrol)
21628c2ecf20Sopenharmony_ci{
21638c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
21648c2ecf20Sopenharmony_ci	int change;
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
21678c2ecf20Sopenharmony_ci	change = inb(ICEMT(ice, MONITOR_RATE)) != ucontrol->value.integer.value[0];
21688c2ecf20Sopenharmony_ci	outb(ucontrol->value.integer.value[0], ICEMT(ice, MONITOR_RATE));
21698c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
21708c2ecf20Sopenharmony_ci	return change;
21718c2ecf20Sopenharmony_ci}
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_mixer_pro_volume_rate = {
21748c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
21758c2ecf20Sopenharmony_ci	.name = "Multi Track Volume Rate",
21768c2ecf20Sopenharmony_ci	.info = snd_ice1712_pro_volume_rate_info,
21778c2ecf20Sopenharmony_ci	.get = snd_ice1712_pro_volume_rate_get,
21788c2ecf20Sopenharmony_ci	.put = snd_ice1712_pro_volume_rate_put
21798c2ecf20Sopenharmony_ci};
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_peak_info(struct snd_kcontrol *kcontrol,
21828c2ecf20Sopenharmony_ci				     struct snd_ctl_elem_info *uinfo)
21838c2ecf20Sopenharmony_ci{
21848c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
21858c2ecf20Sopenharmony_ci	uinfo->count = 22;
21868c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
21878c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 255;
21888c2ecf20Sopenharmony_ci	return 0;
21898c2ecf20Sopenharmony_ci}
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_cistatic int snd_ice1712_pro_peak_get(struct snd_kcontrol *kcontrol,
21928c2ecf20Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
21938c2ecf20Sopenharmony_ci{
21948c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
21958c2ecf20Sopenharmony_ci	int idx;
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
21988c2ecf20Sopenharmony_ci	for (idx = 0; idx < 22; idx++) {
21998c2ecf20Sopenharmony_ci		outb(idx, ICEMT(ice, MONITOR_PEAKINDEX));
22008c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[idx] = inb(ICEMT(ice, MONITOR_PEAKDATA));
22018c2ecf20Sopenharmony_ci	}
22028c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
22038c2ecf20Sopenharmony_ci	return 0;
22048c2ecf20Sopenharmony_ci}
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ice1712_mixer_pro_peak = {
22078c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
22088c2ecf20Sopenharmony_ci	.name = "Multi Track Peak",
22098c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
22108c2ecf20Sopenharmony_ci	.info = snd_ice1712_pro_peak_info,
22118c2ecf20Sopenharmony_ci	.get = snd_ice1712_pro_peak_get
22128c2ecf20Sopenharmony_ci};
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_ci/*
22158c2ecf20Sopenharmony_ci *
22168c2ecf20Sopenharmony_ci */
22178c2ecf20Sopenharmony_ci
22188c2ecf20Sopenharmony_ci/*
22198c2ecf20Sopenharmony_ci * list of available boards
22208c2ecf20Sopenharmony_ci */
22218c2ecf20Sopenharmony_cistatic const struct snd_ice1712_card_info *card_tables[] = {
22228c2ecf20Sopenharmony_ci	snd_ice1712_hoontech_cards,
22238c2ecf20Sopenharmony_ci	snd_ice1712_delta_cards,
22248c2ecf20Sopenharmony_ci	snd_ice1712_ews_cards,
22258c2ecf20Sopenharmony_ci	NULL,
22268c2ecf20Sopenharmony_ci};
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_cistatic unsigned char snd_ice1712_read_i2c(struct snd_ice1712 *ice,
22298c2ecf20Sopenharmony_ci					  unsigned char dev,
22308c2ecf20Sopenharmony_ci					  unsigned char addr)
22318c2ecf20Sopenharmony_ci{
22328c2ecf20Sopenharmony_ci	long t = 0x10000;
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci	outb(addr, ICEREG(ice, I2C_BYTE_ADDR));
22358c2ecf20Sopenharmony_ci	outb(dev & ~ICE1712_I2C_WRITE, ICEREG(ice, I2C_DEV_ADDR));
22368c2ecf20Sopenharmony_ci	while (t-- > 0 && (inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_BUSY)) ;
22378c2ecf20Sopenharmony_ci	return inb(ICEREG(ice, I2C_DATA));
22388c2ecf20Sopenharmony_ci}
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_cistatic int snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
22418c2ecf20Sopenharmony_ci				   const char *modelname)
22428c2ecf20Sopenharmony_ci{
22438c2ecf20Sopenharmony_ci	int dev = ICE_I2C_EEPROM_ADDR;	/* I2C EEPROM device address */
22448c2ecf20Sopenharmony_ci	unsigned int i, size;
22458c2ecf20Sopenharmony_ci	const struct snd_ice1712_card_info * const *tbl, *c;
22468c2ecf20Sopenharmony_ci
22478c2ecf20Sopenharmony_ci	if (!modelname || !*modelname) {
22488c2ecf20Sopenharmony_ci		ice->eeprom.subvendor = 0;
22498c2ecf20Sopenharmony_ci		if ((inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_EEPROM) != 0)
22508c2ecf20Sopenharmony_ci			ice->eeprom.subvendor = (snd_ice1712_read_i2c(ice, dev, 0x00) << 0) |
22518c2ecf20Sopenharmony_ci				(snd_ice1712_read_i2c(ice, dev, 0x01) << 8) |
22528c2ecf20Sopenharmony_ci				(snd_ice1712_read_i2c(ice, dev, 0x02) << 16) |
22538c2ecf20Sopenharmony_ci				(snd_ice1712_read_i2c(ice, dev, 0x03) << 24);
22548c2ecf20Sopenharmony_ci		if (ice->eeprom.subvendor == 0 ||
22558c2ecf20Sopenharmony_ci		    ice->eeprom.subvendor == (unsigned int)-1) {
22568c2ecf20Sopenharmony_ci			/* invalid subvendor from EEPROM, try the PCI subststem ID instead */
22578c2ecf20Sopenharmony_ci			u16 vendor, device;
22588c2ecf20Sopenharmony_ci			pci_read_config_word(ice->pci, PCI_SUBSYSTEM_VENDOR_ID, &vendor);
22598c2ecf20Sopenharmony_ci			pci_read_config_word(ice->pci, PCI_SUBSYSTEM_ID, &device);
22608c2ecf20Sopenharmony_ci			ice->eeprom.subvendor = ((unsigned int)swab16(vendor) << 16) | swab16(device);
22618c2ecf20Sopenharmony_ci			if (ice->eeprom.subvendor == 0 || ice->eeprom.subvendor == (unsigned int)-1) {
22628c2ecf20Sopenharmony_ci				dev_err(ice->card->dev,
22638c2ecf20Sopenharmony_ci					"No valid ID is found\n");
22648c2ecf20Sopenharmony_ci				return -ENXIO;
22658c2ecf20Sopenharmony_ci			}
22668c2ecf20Sopenharmony_ci		}
22678c2ecf20Sopenharmony_ci	}
22688c2ecf20Sopenharmony_ci	for (tbl = card_tables; *tbl; tbl++) {
22698c2ecf20Sopenharmony_ci		for (c = *tbl; c->subvendor; c++) {
22708c2ecf20Sopenharmony_ci			if (modelname && c->model && !strcmp(modelname, c->model)) {
22718c2ecf20Sopenharmony_ci				dev_info(ice->card->dev,
22728c2ecf20Sopenharmony_ci					 "Using board model %s\n", c->name);
22738c2ecf20Sopenharmony_ci				ice->eeprom.subvendor = c->subvendor;
22748c2ecf20Sopenharmony_ci			} else if (c->subvendor != ice->eeprom.subvendor)
22758c2ecf20Sopenharmony_ci				continue;
22768c2ecf20Sopenharmony_ci			if (!c->eeprom_size || !c->eeprom_data)
22778c2ecf20Sopenharmony_ci				goto found;
22788c2ecf20Sopenharmony_ci			/* if the EEPROM is given by the driver, use it */
22798c2ecf20Sopenharmony_ci			dev_dbg(ice->card->dev, "using the defined eeprom..\n");
22808c2ecf20Sopenharmony_ci			ice->eeprom.version = 1;
22818c2ecf20Sopenharmony_ci			ice->eeprom.size = c->eeprom_size + 6;
22828c2ecf20Sopenharmony_ci			memcpy(ice->eeprom.data, c->eeprom_data, c->eeprom_size);
22838c2ecf20Sopenharmony_ci			goto read_skipped;
22848c2ecf20Sopenharmony_ci		}
22858c2ecf20Sopenharmony_ci	}
22868c2ecf20Sopenharmony_ci	dev_warn(ice->card->dev, "No matching model found for ID 0x%x\n",
22878c2ecf20Sopenharmony_ci	       ice->eeprom.subvendor);
22888c2ecf20Sopenharmony_ci
22898c2ecf20Sopenharmony_ci found:
22908c2ecf20Sopenharmony_ci	ice->eeprom.size = snd_ice1712_read_i2c(ice, dev, 0x04);
22918c2ecf20Sopenharmony_ci	if (ice->eeprom.size < 6)
22928c2ecf20Sopenharmony_ci		ice->eeprom.size = 32; /* FIXME: any cards without the correct size? */
22938c2ecf20Sopenharmony_ci	else if (ice->eeprom.size > 32) {
22948c2ecf20Sopenharmony_ci		dev_err(ice->card->dev,
22958c2ecf20Sopenharmony_ci			"invalid EEPROM (size = %i)\n", ice->eeprom.size);
22968c2ecf20Sopenharmony_ci		return -EIO;
22978c2ecf20Sopenharmony_ci	}
22988c2ecf20Sopenharmony_ci	ice->eeprom.version = snd_ice1712_read_i2c(ice, dev, 0x05);
22998c2ecf20Sopenharmony_ci	if (ice->eeprom.version != 1) {
23008c2ecf20Sopenharmony_ci		dev_err(ice->card->dev, "invalid EEPROM version %i\n",
23018c2ecf20Sopenharmony_ci			   ice->eeprom.version);
23028c2ecf20Sopenharmony_ci		/* return -EIO; */
23038c2ecf20Sopenharmony_ci	}
23048c2ecf20Sopenharmony_ci	size = ice->eeprom.size - 6;
23058c2ecf20Sopenharmony_ci	for (i = 0; i < size; i++)
23068c2ecf20Sopenharmony_ci		ice->eeprom.data[i] = snd_ice1712_read_i2c(ice, dev, i + 6);
23078c2ecf20Sopenharmony_ci
23088c2ecf20Sopenharmony_ci read_skipped:
23098c2ecf20Sopenharmony_ci	ice->eeprom.gpiomask = ice->eeprom.data[ICE_EEP1_GPIO_MASK];
23108c2ecf20Sopenharmony_ci	ice->eeprom.gpiostate = ice->eeprom.data[ICE_EEP1_GPIO_STATE];
23118c2ecf20Sopenharmony_ci	ice->eeprom.gpiodir = ice->eeprom.data[ICE_EEP1_GPIO_DIR];
23128c2ecf20Sopenharmony_ci
23138c2ecf20Sopenharmony_ci	return 0;
23148c2ecf20Sopenharmony_ci}
23158c2ecf20Sopenharmony_ci
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci
23188c2ecf20Sopenharmony_cistatic int snd_ice1712_chip_init(struct snd_ice1712 *ice)
23198c2ecf20Sopenharmony_ci{
23208c2ecf20Sopenharmony_ci	outb(ICE1712_RESET | ICE1712_NATIVE, ICEREG(ice, CONTROL));
23218c2ecf20Sopenharmony_ci	udelay(200);
23228c2ecf20Sopenharmony_ci	outb(ICE1712_NATIVE, ICEREG(ice, CONTROL));
23238c2ecf20Sopenharmony_ci	udelay(200);
23248c2ecf20Sopenharmony_ci	if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DMX6FIRE &&
23258c2ecf20Sopenharmony_ci	    !ice->dxr_enable)
23268c2ecf20Sopenharmony_ci		/*  Set eeprom value to limit active ADCs and DACs to 6;
23278c2ecf20Sopenharmony_ci		 *  Also disable AC97 as no hardware in standard 6fire card/box
23288c2ecf20Sopenharmony_ci		 *  Note: DXR extensions are not currently supported
23298c2ecf20Sopenharmony_ci		 */
23308c2ecf20Sopenharmony_ci		ice->eeprom.data[ICE_EEP1_CODEC] = 0x3a;
23318c2ecf20Sopenharmony_ci	pci_write_config_byte(ice->pci, 0x60, ice->eeprom.data[ICE_EEP1_CODEC]);
23328c2ecf20Sopenharmony_ci	pci_write_config_byte(ice->pci, 0x61, ice->eeprom.data[ICE_EEP1_ACLINK]);
23338c2ecf20Sopenharmony_ci	pci_write_config_byte(ice->pci, 0x62, ice->eeprom.data[ICE_EEP1_I2SID]);
23348c2ecf20Sopenharmony_ci	pci_write_config_byte(ice->pci, 0x63, ice->eeprom.data[ICE_EEP1_SPDIF]);
23358c2ecf20Sopenharmony_ci	if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24 &&
23368c2ecf20Sopenharmony_ci	    ice->eeprom.subvendor != ICE1712_SUBDEVICE_STAUDIO_ADCIII) {
23378c2ecf20Sopenharmony_ci		ice->gpio.write_mask = ice->eeprom.gpiomask;
23388c2ecf20Sopenharmony_ci		ice->gpio.direction = ice->eeprom.gpiodir;
23398c2ecf20Sopenharmony_ci		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK,
23408c2ecf20Sopenharmony_ci				  ice->eeprom.gpiomask);
23418c2ecf20Sopenharmony_ci		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
23428c2ecf20Sopenharmony_ci				  ice->eeprom.gpiodir);
23438c2ecf20Sopenharmony_ci		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA,
23448c2ecf20Sopenharmony_ci				  ice->eeprom.gpiostate);
23458c2ecf20Sopenharmony_ci	} else {
23468c2ecf20Sopenharmony_ci		ice->gpio.write_mask = 0xc0;
23478c2ecf20Sopenharmony_ci		ice->gpio.direction = 0xff;
23488c2ecf20Sopenharmony_ci		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, 0xc0);
23498c2ecf20Sopenharmony_ci		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, 0xff);
23508c2ecf20Sopenharmony_ci		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA,
23518c2ecf20Sopenharmony_ci				  ICE1712_STDSP24_CLOCK_BIT);
23528c2ecf20Sopenharmony_ci	}
23538c2ecf20Sopenharmony_ci	snd_ice1712_write(ice, ICE1712_IREG_PRO_POWERDOWN, 0);
23548c2ecf20Sopenharmony_ci	if (!(ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97)) {
23558c2ecf20Sopenharmony_ci		outb(ICE1712_AC97_WARM, ICEREG(ice, AC97_CMD));
23568c2ecf20Sopenharmony_ci		udelay(100);
23578c2ecf20Sopenharmony_ci		outb(0, ICEREG(ice, AC97_CMD));
23588c2ecf20Sopenharmony_ci		udelay(200);
23598c2ecf20Sopenharmony_ci		snd_ice1712_write(ice, ICE1712_IREG_CONSUMER_POWERDOWN, 0);
23608c2ecf20Sopenharmony_ci	}
23618c2ecf20Sopenharmony_ci	snd_ice1712_set_pro_rate(ice, 48000, 1);
23628c2ecf20Sopenharmony_ci	/* unmask used interrupts */
23638c2ecf20Sopenharmony_ci	outb(((ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) == 0 ?
23648c2ecf20Sopenharmony_ci	      ICE1712_IRQ_MPU2 : 0) |
23658c2ecf20Sopenharmony_ci	     ((ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97) ?
23668c2ecf20Sopenharmony_ci	      ICE1712_IRQ_PBKDS | ICE1712_IRQ_CONCAP | ICE1712_IRQ_CONPBK : 0),
23678c2ecf20Sopenharmony_ci	     ICEREG(ice, IRQMASK));
23688c2ecf20Sopenharmony_ci	outb(0x00, ICEMT(ice, IRQ));
23698c2ecf20Sopenharmony_ci
23708c2ecf20Sopenharmony_ci	return 0;
23718c2ecf20Sopenharmony_ci}
23728c2ecf20Sopenharmony_ci
23738c2ecf20Sopenharmony_ciint snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice)
23748c2ecf20Sopenharmony_ci{
23758c2ecf20Sopenharmony_ci	int err;
23768c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!ice->pcm_pro))
23798c2ecf20Sopenharmony_ci		return -EIO;
23808c2ecf20Sopenharmony_ci	err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice));
23818c2ecf20Sopenharmony_ci	if (err < 0)
23828c2ecf20Sopenharmony_ci		return err;
23838c2ecf20Sopenharmony_ci	kctl->id.device = ice->pcm_pro->device;
23848c2ecf20Sopenharmony_ci	err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice));
23858c2ecf20Sopenharmony_ci	if (err < 0)
23868c2ecf20Sopenharmony_ci		return err;
23878c2ecf20Sopenharmony_ci	kctl->id.device = ice->pcm_pro->device;
23888c2ecf20Sopenharmony_ci	err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice));
23898c2ecf20Sopenharmony_ci	if (err < 0)
23908c2ecf20Sopenharmony_ci		return err;
23918c2ecf20Sopenharmony_ci	kctl->id.device = ice->pcm_pro->device;
23928c2ecf20Sopenharmony_ci	err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice));
23938c2ecf20Sopenharmony_ci	if (err < 0)
23948c2ecf20Sopenharmony_ci		return err;
23958c2ecf20Sopenharmony_ci	kctl->id.device = ice->pcm_pro->device;
23968c2ecf20Sopenharmony_ci	ice->spdif.stream_ctl = kctl;
23978c2ecf20Sopenharmony_ci	return 0;
23988c2ecf20Sopenharmony_ci}
23998c2ecf20Sopenharmony_ci
24008c2ecf20Sopenharmony_ci
24018c2ecf20Sopenharmony_cistatic int snd_ice1712_build_controls(struct snd_ice1712 *ice)
24028c2ecf20Sopenharmony_ci{
24038c2ecf20Sopenharmony_ci	int err;
24048c2ecf20Sopenharmony_ci
24058c2ecf20Sopenharmony_ci	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_eeprom, ice));
24068c2ecf20Sopenharmony_ci	if (err < 0)
24078c2ecf20Sopenharmony_ci		return err;
24088c2ecf20Sopenharmony_ci	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_internal_clock, ice));
24098c2ecf20Sopenharmony_ci	if (err < 0)
24108c2ecf20Sopenharmony_ci		return err;
24118c2ecf20Sopenharmony_ci	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_internal_clock_default, ice));
24128c2ecf20Sopenharmony_ci	if (err < 0)
24138c2ecf20Sopenharmony_ci		return err;
24148c2ecf20Sopenharmony_ci
24158c2ecf20Sopenharmony_ci	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_rate_locking, ice));
24168c2ecf20Sopenharmony_ci	if (err < 0)
24178c2ecf20Sopenharmony_ci		return err;
24188c2ecf20Sopenharmony_ci	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_rate_reset, ice));
24198c2ecf20Sopenharmony_ci	if (err < 0)
24208c2ecf20Sopenharmony_ci		return err;
24218c2ecf20Sopenharmony_ci
24228c2ecf20Sopenharmony_ci	if (ice->num_total_dacs > 0) {
24238c2ecf20Sopenharmony_ci		struct snd_kcontrol_new tmp = snd_ice1712_mixer_pro_analog_route;
24248c2ecf20Sopenharmony_ci		tmp.count = ice->num_total_dacs;
24258c2ecf20Sopenharmony_ci		err = snd_ctl_add(ice->card, snd_ctl_new1(&tmp, ice));
24268c2ecf20Sopenharmony_ci		if (err < 0)
24278c2ecf20Sopenharmony_ci			return err;
24288c2ecf20Sopenharmony_ci	}
24298c2ecf20Sopenharmony_ci
24308c2ecf20Sopenharmony_ci	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_spdif_route, ice));
24318c2ecf20Sopenharmony_ci	if (err < 0)
24328c2ecf20Sopenharmony_ci		return err;
24338c2ecf20Sopenharmony_ci
24348c2ecf20Sopenharmony_ci	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_volume_rate, ice));
24358c2ecf20Sopenharmony_ci	if (err < 0)
24368c2ecf20Sopenharmony_ci		return err;
24378c2ecf20Sopenharmony_ci	return snd_ctl_add(ice->card,
24388c2ecf20Sopenharmony_ci			   snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice));
24398c2ecf20Sopenharmony_ci}
24408c2ecf20Sopenharmony_ci
24418c2ecf20Sopenharmony_cistatic int snd_ice1712_free(struct snd_ice1712 *ice)
24428c2ecf20Sopenharmony_ci{
24438c2ecf20Sopenharmony_ci	if (!ice->port)
24448c2ecf20Sopenharmony_ci		goto __hw_end;
24458c2ecf20Sopenharmony_ci	/* mask all interrupts */
24468c2ecf20Sopenharmony_ci	outb(ICE1712_MULTI_CAPTURE | ICE1712_MULTI_PLAYBACK, ICEMT(ice, IRQ));
24478c2ecf20Sopenharmony_ci	outb(0xff, ICEREG(ice, IRQMASK));
24488c2ecf20Sopenharmony_ci	/* --- */
24498c2ecf20Sopenharmony_ci__hw_end:
24508c2ecf20Sopenharmony_ci	if (ice->irq >= 0)
24518c2ecf20Sopenharmony_ci		free_irq(ice->irq, ice);
24528c2ecf20Sopenharmony_ci
24538c2ecf20Sopenharmony_ci	if (ice->port)
24548c2ecf20Sopenharmony_ci		pci_release_regions(ice->pci);
24558c2ecf20Sopenharmony_ci	snd_ice1712_akm4xxx_free(ice);
24568c2ecf20Sopenharmony_ci	pci_disable_device(ice->pci);
24578c2ecf20Sopenharmony_ci	kfree(ice->spec);
24588c2ecf20Sopenharmony_ci	kfree(ice);
24598c2ecf20Sopenharmony_ci	return 0;
24608c2ecf20Sopenharmony_ci}
24618c2ecf20Sopenharmony_ci
24628c2ecf20Sopenharmony_cistatic int snd_ice1712_dev_free(struct snd_device *device)
24638c2ecf20Sopenharmony_ci{
24648c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = device->device_data;
24658c2ecf20Sopenharmony_ci	return snd_ice1712_free(ice);
24668c2ecf20Sopenharmony_ci}
24678c2ecf20Sopenharmony_ci
24688c2ecf20Sopenharmony_cistatic int snd_ice1712_create(struct snd_card *card,
24698c2ecf20Sopenharmony_ci			      struct pci_dev *pci,
24708c2ecf20Sopenharmony_ci			      const char *modelname,
24718c2ecf20Sopenharmony_ci			      int omni,
24728c2ecf20Sopenharmony_ci			      int cs8427_timeout,
24738c2ecf20Sopenharmony_ci			      int dxr_enable,
24748c2ecf20Sopenharmony_ci			      struct snd_ice1712 **r_ice1712)
24758c2ecf20Sopenharmony_ci{
24768c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice;
24778c2ecf20Sopenharmony_ci	int err;
24788c2ecf20Sopenharmony_ci	static const struct snd_device_ops ops = {
24798c2ecf20Sopenharmony_ci		.dev_free =	snd_ice1712_dev_free,
24808c2ecf20Sopenharmony_ci	};
24818c2ecf20Sopenharmony_ci
24828c2ecf20Sopenharmony_ci	*r_ice1712 = NULL;
24838c2ecf20Sopenharmony_ci
24848c2ecf20Sopenharmony_ci	/* enable PCI device */
24858c2ecf20Sopenharmony_ci	err = pci_enable_device(pci);
24868c2ecf20Sopenharmony_ci	if (err < 0)
24878c2ecf20Sopenharmony_ci		return err;
24888c2ecf20Sopenharmony_ci	/* check, if we can restrict PCI DMA transfers to 28 bits */
24898c2ecf20Sopenharmony_ci	if (dma_set_mask(&pci->dev, DMA_BIT_MASK(28)) < 0 ||
24908c2ecf20Sopenharmony_ci	    dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(28)) < 0) {
24918c2ecf20Sopenharmony_ci		dev_err(card->dev,
24928c2ecf20Sopenharmony_ci			"architecture does not support 28bit PCI busmaster DMA\n");
24938c2ecf20Sopenharmony_ci		pci_disable_device(pci);
24948c2ecf20Sopenharmony_ci		return -ENXIO;
24958c2ecf20Sopenharmony_ci	}
24968c2ecf20Sopenharmony_ci
24978c2ecf20Sopenharmony_ci	ice = kzalloc(sizeof(*ice), GFP_KERNEL);
24988c2ecf20Sopenharmony_ci	if (ice == NULL) {
24998c2ecf20Sopenharmony_ci		pci_disable_device(pci);
25008c2ecf20Sopenharmony_ci		return -ENOMEM;
25018c2ecf20Sopenharmony_ci	}
25028c2ecf20Sopenharmony_ci	ice->omni = omni ? 1 : 0;
25038c2ecf20Sopenharmony_ci	if (cs8427_timeout < 1)
25048c2ecf20Sopenharmony_ci		cs8427_timeout = 1;
25058c2ecf20Sopenharmony_ci	else if (cs8427_timeout > 1000)
25068c2ecf20Sopenharmony_ci		cs8427_timeout = 1000;
25078c2ecf20Sopenharmony_ci	ice->cs8427_timeout = cs8427_timeout;
25088c2ecf20Sopenharmony_ci	ice->dxr_enable = dxr_enable;
25098c2ecf20Sopenharmony_ci	spin_lock_init(&ice->reg_lock);
25108c2ecf20Sopenharmony_ci	mutex_init(&ice->gpio_mutex);
25118c2ecf20Sopenharmony_ci	mutex_init(&ice->i2c_mutex);
25128c2ecf20Sopenharmony_ci	mutex_init(&ice->open_mutex);
25138c2ecf20Sopenharmony_ci	ice->gpio.set_mask = snd_ice1712_set_gpio_mask;
25148c2ecf20Sopenharmony_ci	ice->gpio.get_mask = snd_ice1712_get_gpio_mask;
25158c2ecf20Sopenharmony_ci	ice->gpio.set_dir = snd_ice1712_set_gpio_dir;
25168c2ecf20Sopenharmony_ci	ice->gpio.get_dir = snd_ice1712_get_gpio_dir;
25178c2ecf20Sopenharmony_ci	ice->gpio.set_data = snd_ice1712_set_gpio_data;
25188c2ecf20Sopenharmony_ci	ice->gpio.get_data = snd_ice1712_get_gpio_data;
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci	ice->spdif.cs8403_bits =
25218c2ecf20Sopenharmony_ci		ice->spdif.cs8403_stream_bits = (0x01 |	/* consumer format */
25228c2ecf20Sopenharmony_ci						 0x10 |	/* no emphasis */
25238c2ecf20Sopenharmony_ci						 0x20);	/* PCM encoder/decoder */
25248c2ecf20Sopenharmony_ci	ice->card = card;
25258c2ecf20Sopenharmony_ci	ice->pci = pci;
25268c2ecf20Sopenharmony_ci	ice->irq = -1;
25278c2ecf20Sopenharmony_ci	pci_set_master(pci);
25288c2ecf20Sopenharmony_ci	/* disable legacy emulation */
25298c2ecf20Sopenharmony_ci	pci_write_config_word(ice->pci, 0x40, 0x807f);
25308c2ecf20Sopenharmony_ci	pci_write_config_word(ice->pci, 0x42, 0x0006);
25318c2ecf20Sopenharmony_ci	snd_ice1712_proc_init(ice);
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_ci	card->private_data = ice;
25348c2ecf20Sopenharmony_ci
25358c2ecf20Sopenharmony_ci	err = pci_request_regions(pci, "ICE1712");
25368c2ecf20Sopenharmony_ci	if (err < 0) {
25378c2ecf20Sopenharmony_ci		kfree(ice);
25388c2ecf20Sopenharmony_ci		pci_disable_device(pci);
25398c2ecf20Sopenharmony_ci		return err;
25408c2ecf20Sopenharmony_ci	}
25418c2ecf20Sopenharmony_ci	ice->port = pci_resource_start(pci, 0);
25428c2ecf20Sopenharmony_ci	ice->ddma_port = pci_resource_start(pci, 1);
25438c2ecf20Sopenharmony_ci	ice->dmapath_port = pci_resource_start(pci, 2);
25448c2ecf20Sopenharmony_ci	ice->profi_port = pci_resource_start(pci, 3);
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_ci	if (request_irq(pci->irq, snd_ice1712_interrupt, IRQF_SHARED,
25478c2ecf20Sopenharmony_ci			KBUILD_MODNAME, ice)) {
25488c2ecf20Sopenharmony_ci		dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
25498c2ecf20Sopenharmony_ci		snd_ice1712_free(ice);
25508c2ecf20Sopenharmony_ci		return -EIO;
25518c2ecf20Sopenharmony_ci	}
25528c2ecf20Sopenharmony_ci
25538c2ecf20Sopenharmony_ci	ice->irq = pci->irq;
25548c2ecf20Sopenharmony_ci	card->sync_irq = ice->irq;
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_ci	if (snd_ice1712_read_eeprom(ice, modelname) < 0) {
25578c2ecf20Sopenharmony_ci		snd_ice1712_free(ice);
25588c2ecf20Sopenharmony_ci		return -EIO;
25598c2ecf20Sopenharmony_ci	}
25608c2ecf20Sopenharmony_ci	if (snd_ice1712_chip_init(ice) < 0) {
25618c2ecf20Sopenharmony_ci		snd_ice1712_free(ice);
25628c2ecf20Sopenharmony_ci		return -EIO;
25638c2ecf20Sopenharmony_ci	}
25648c2ecf20Sopenharmony_ci
25658c2ecf20Sopenharmony_ci	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops);
25668c2ecf20Sopenharmony_ci	if (err < 0) {
25678c2ecf20Sopenharmony_ci		snd_ice1712_free(ice);
25688c2ecf20Sopenharmony_ci		return err;
25698c2ecf20Sopenharmony_ci	}
25708c2ecf20Sopenharmony_ci
25718c2ecf20Sopenharmony_ci	*r_ice1712 = ice;
25728c2ecf20Sopenharmony_ci	return 0;
25738c2ecf20Sopenharmony_ci}
25748c2ecf20Sopenharmony_ci
25758c2ecf20Sopenharmony_ci
25768c2ecf20Sopenharmony_ci/*
25778c2ecf20Sopenharmony_ci *
25788c2ecf20Sopenharmony_ci * Registration
25798c2ecf20Sopenharmony_ci *
25808c2ecf20Sopenharmony_ci */
25818c2ecf20Sopenharmony_ci
25828c2ecf20Sopenharmony_cistatic struct snd_ice1712_card_info no_matched;
25838c2ecf20Sopenharmony_ci
25848c2ecf20Sopenharmony_cistatic int snd_ice1712_probe(struct pci_dev *pci,
25858c2ecf20Sopenharmony_ci			     const struct pci_device_id *pci_id)
25868c2ecf20Sopenharmony_ci{
25878c2ecf20Sopenharmony_ci	static int dev;
25888c2ecf20Sopenharmony_ci	struct snd_card *card;
25898c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice;
25908c2ecf20Sopenharmony_ci	int pcm_dev = 0, err;
25918c2ecf20Sopenharmony_ci	const struct snd_ice1712_card_info * const *tbl, *c;
25928c2ecf20Sopenharmony_ci
25938c2ecf20Sopenharmony_ci	if (dev >= SNDRV_CARDS)
25948c2ecf20Sopenharmony_ci		return -ENODEV;
25958c2ecf20Sopenharmony_ci	if (!enable[dev]) {
25968c2ecf20Sopenharmony_ci		dev++;
25978c2ecf20Sopenharmony_ci		return -ENOENT;
25988c2ecf20Sopenharmony_ci	}
25998c2ecf20Sopenharmony_ci
26008c2ecf20Sopenharmony_ci	err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
26018c2ecf20Sopenharmony_ci			   0, &card);
26028c2ecf20Sopenharmony_ci	if (err < 0)
26038c2ecf20Sopenharmony_ci		return err;
26048c2ecf20Sopenharmony_ci
26058c2ecf20Sopenharmony_ci	strcpy(card->driver, "ICE1712");
26068c2ecf20Sopenharmony_ci	strcpy(card->shortname, "ICEnsemble ICE1712");
26078c2ecf20Sopenharmony_ci
26088c2ecf20Sopenharmony_ci	err = snd_ice1712_create(card, pci, model[dev], omni[dev],
26098c2ecf20Sopenharmony_ci		cs8427_timeout[dev], dxr_enable[dev], &ice);
26108c2ecf20Sopenharmony_ci	if (err < 0) {
26118c2ecf20Sopenharmony_ci		snd_card_free(card);
26128c2ecf20Sopenharmony_ci		return err;
26138c2ecf20Sopenharmony_ci	}
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ci	for (tbl = card_tables; *tbl; tbl++) {
26168c2ecf20Sopenharmony_ci		for (c = *tbl; c->subvendor; c++) {
26178c2ecf20Sopenharmony_ci			if (c->subvendor == ice->eeprom.subvendor) {
26188c2ecf20Sopenharmony_ci				ice->card_info = c;
26198c2ecf20Sopenharmony_ci				strcpy(card->shortname, c->name);
26208c2ecf20Sopenharmony_ci				if (c->driver) /* specific driver? */
26218c2ecf20Sopenharmony_ci					strcpy(card->driver, c->driver);
26228c2ecf20Sopenharmony_ci				if (c->chip_init) {
26238c2ecf20Sopenharmony_ci					err = c->chip_init(ice);
26248c2ecf20Sopenharmony_ci					if (err < 0) {
26258c2ecf20Sopenharmony_ci						snd_card_free(card);
26268c2ecf20Sopenharmony_ci						return err;
26278c2ecf20Sopenharmony_ci					}
26288c2ecf20Sopenharmony_ci				}
26298c2ecf20Sopenharmony_ci				goto __found;
26308c2ecf20Sopenharmony_ci			}
26318c2ecf20Sopenharmony_ci		}
26328c2ecf20Sopenharmony_ci	}
26338c2ecf20Sopenharmony_ci	c = &no_matched;
26348c2ecf20Sopenharmony_ci __found:
26358c2ecf20Sopenharmony_ci
26368c2ecf20Sopenharmony_ci	err = snd_ice1712_pcm_profi(ice, pcm_dev++);
26378c2ecf20Sopenharmony_ci	if (err < 0) {
26388c2ecf20Sopenharmony_ci		snd_card_free(card);
26398c2ecf20Sopenharmony_ci		return err;
26408c2ecf20Sopenharmony_ci	}
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_ci	if (ice_has_con_ac97(ice)) {
26438c2ecf20Sopenharmony_ci		err = snd_ice1712_pcm(ice, pcm_dev++);
26448c2ecf20Sopenharmony_ci		if (err < 0) {
26458c2ecf20Sopenharmony_ci			snd_card_free(card);
26468c2ecf20Sopenharmony_ci			return err;
26478c2ecf20Sopenharmony_ci		}
26488c2ecf20Sopenharmony_ci	}
26498c2ecf20Sopenharmony_ci
26508c2ecf20Sopenharmony_ci	err = snd_ice1712_ac97_mixer(ice);
26518c2ecf20Sopenharmony_ci	if (err < 0) {
26528c2ecf20Sopenharmony_ci		snd_card_free(card);
26538c2ecf20Sopenharmony_ci		return err;
26548c2ecf20Sopenharmony_ci	}
26558c2ecf20Sopenharmony_ci
26568c2ecf20Sopenharmony_ci	err = snd_ice1712_build_controls(ice);
26578c2ecf20Sopenharmony_ci	if (err < 0) {
26588c2ecf20Sopenharmony_ci		snd_card_free(card);
26598c2ecf20Sopenharmony_ci		return err;
26608c2ecf20Sopenharmony_ci	}
26618c2ecf20Sopenharmony_ci
26628c2ecf20Sopenharmony_ci	if (c->build_controls) {
26638c2ecf20Sopenharmony_ci		err = c->build_controls(ice);
26648c2ecf20Sopenharmony_ci		if (err < 0) {
26658c2ecf20Sopenharmony_ci			snd_card_free(card);
26668c2ecf20Sopenharmony_ci			return err;
26678c2ecf20Sopenharmony_ci		}
26688c2ecf20Sopenharmony_ci	}
26698c2ecf20Sopenharmony_ci
26708c2ecf20Sopenharmony_ci	if (ice_has_con_ac97(ice)) {
26718c2ecf20Sopenharmony_ci		err = snd_ice1712_pcm_ds(ice, pcm_dev++);
26728c2ecf20Sopenharmony_ci		if (err < 0) {
26738c2ecf20Sopenharmony_ci			snd_card_free(card);
26748c2ecf20Sopenharmony_ci			return err;
26758c2ecf20Sopenharmony_ci		}
26768c2ecf20Sopenharmony_ci	}
26778c2ecf20Sopenharmony_ci
26788c2ecf20Sopenharmony_ci	if (!c->no_mpu401) {
26798c2ecf20Sopenharmony_ci		err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
26808c2ecf20Sopenharmony_ci			ICEREG(ice, MPU1_CTRL),
26818c2ecf20Sopenharmony_ci			c->mpu401_1_info_flags |
26828c2ecf20Sopenharmony_ci			MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
26838c2ecf20Sopenharmony_ci			-1, &ice->rmidi[0]);
26848c2ecf20Sopenharmony_ci		if (err < 0) {
26858c2ecf20Sopenharmony_ci			snd_card_free(card);
26868c2ecf20Sopenharmony_ci			return err;
26878c2ecf20Sopenharmony_ci		}
26888c2ecf20Sopenharmony_ci		if (c->mpu401_1_name)
26898c2ecf20Sopenharmony_ci			/*  Preferred name available in card_info */
26908c2ecf20Sopenharmony_ci			snprintf(ice->rmidi[0]->name,
26918c2ecf20Sopenharmony_ci				 sizeof(ice->rmidi[0]->name),
26928c2ecf20Sopenharmony_ci				 "%s %d", c->mpu401_1_name, card->number);
26938c2ecf20Sopenharmony_ci
26948c2ecf20Sopenharmony_ci		if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) {
26958c2ecf20Sopenharmony_ci			/*  2nd port used  */
26968c2ecf20Sopenharmony_ci			err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
26978c2ecf20Sopenharmony_ci				ICEREG(ice, MPU2_CTRL),
26988c2ecf20Sopenharmony_ci				c->mpu401_2_info_flags |
26998c2ecf20Sopenharmony_ci				MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
27008c2ecf20Sopenharmony_ci				-1, &ice->rmidi[1]);
27018c2ecf20Sopenharmony_ci
27028c2ecf20Sopenharmony_ci			if (err < 0) {
27038c2ecf20Sopenharmony_ci				snd_card_free(card);
27048c2ecf20Sopenharmony_ci				return err;
27058c2ecf20Sopenharmony_ci			}
27068c2ecf20Sopenharmony_ci			if (c->mpu401_2_name)
27078c2ecf20Sopenharmony_ci				/*  Preferred name available in card_info */
27088c2ecf20Sopenharmony_ci				snprintf(ice->rmidi[1]->name,
27098c2ecf20Sopenharmony_ci					 sizeof(ice->rmidi[1]->name),
27108c2ecf20Sopenharmony_ci					 "%s %d", c->mpu401_2_name,
27118c2ecf20Sopenharmony_ci					 card->number);
27128c2ecf20Sopenharmony_ci		}
27138c2ecf20Sopenharmony_ci	}
27148c2ecf20Sopenharmony_ci
27158c2ecf20Sopenharmony_ci	snd_ice1712_set_input_clock_source(ice, 0);
27168c2ecf20Sopenharmony_ci
27178c2ecf20Sopenharmony_ci	sprintf(card->longname, "%s at 0x%lx, irq %i",
27188c2ecf20Sopenharmony_ci		card->shortname, ice->port, ice->irq);
27198c2ecf20Sopenharmony_ci
27208c2ecf20Sopenharmony_ci	err = snd_card_register(card);
27218c2ecf20Sopenharmony_ci	if (err < 0) {
27228c2ecf20Sopenharmony_ci		snd_card_free(card);
27238c2ecf20Sopenharmony_ci		return err;
27248c2ecf20Sopenharmony_ci	}
27258c2ecf20Sopenharmony_ci	pci_set_drvdata(pci, card);
27268c2ecf20Sopenharmony_ci	dev++;
27278c2ecf20Sopenharmony_ci	return 0;
27288c2ecf20Sopenharmony_ci}
27298c2ecf20Sopenharmony_ci
27308c2ecf20Sopenharmony_cistatic void snd_ice1712_remove(struct pci_dev *pci)
27318c2ecf20Sopenharmony_ci{
27328c2ecf20Sopenharmony_ci	struct snd_card *card = pci_get_drvdata(pci);
27338c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = card->private_data;
27348c2ecf20Sopenharmony_ci
27358c2ecf20Sopenharmony_ci	if (ice->card_info && ice->card_info->chip_exit)
27368c2ecf20Sopenharmony_ci		ice->card_info->chip_exit(ice);
27378c2ecf20Sopenharmony_ci	snd_card_free(card);
27388c2ecf20Sopenharmony_ci}
27398c2ecf20Sopenharmony_ci
27408c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
27418c2ecf20Sopenharmony_cistatic int snd_ice1712_suspend(struct device *dev)
27428c2ecf20Sopenharmony_ci{
27438c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
27448c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = card->private_data;
27458c2ecf20Sopenharmony_ci
27468c2ecf20Sopenharmony_ci	if (!ice->pm_suspend_enabled)
27478c2ecf20Sopenharmony_ci		return 0;
27488c2ecf20Sopenharmony_ci
27498c2ecf20Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
27508c2ecf20Sopenharmony_ci
27518c2ecf20Sopenharmony_ci	snd_ac97_suspend(ice->ac97);
27528c2ecf20Sopenharmony_ci
27538c2ecf20Sopenharmony_ci	spin_lock_irq(&ice->reg_lock);
27548c2ecf20Sopenharmony_ci	ice->pm_saved_is_spdif_master = is_spdif_master(ice);
27558c2ecf20Sopenharmony_ci	ice->pm_saved_spdif_ctrl = inw(ICEMT(ice, ROUTE_SPDOUT));
27568c2ecf20Sopenharmony_ci	ice->pm_saved_route = inw(ICEMT(ice, ROUTE_PSDOUT03));
27578c2ecf20Sopenharmony_ci	spin_unlock_irq(&ice->reg_lock);
27588c2ecf20Sopenharmony_ci
27598c2ecf20Sopenharmony_ci	if (ice->pm_suspend)
27608c2ecf20Sopenharmony_ci		ice->pm_suspend(ice);
27618c2ecf20Sopenharmony_ci	return 0;
27628c2ecf20Sopenharmony_ci}
27638c2ecf20Sopenharmony_ci
27648c2ecf20Sopenharmony_cistatic int snd_ice1712_resume(struct device *dev)
27658c2ecf20Sopenharmony_ci{
27668c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
27678c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice = card->private_data;
27688c2ecf20Sopenharmony_ci	int rate;
27698c2ecf20Sopenharmony_ci
27708c2ecf20Sopenharmony_ci	if (!ice->pm_suspend_enabled)
27718c2ecf20Sopenharmony_ci		return 0;
27728c2ecf20Sopenharmony_ci
27738c2ecf20Sopenharmony_ci	if (ice->cur_rate)
27748c2ecf20Sopenharmony_ci		rate = ice->cur_rate;
27758c2ecf20Sopenharmony_ci	else
27768c2ecf20Sopenharmony_ci		rate = PRO_RATE_DEFAULT;
27778c2ecf20Sopenharmony_ci
27788c2ecf20Sopenharmony_ci	if (snd_ice1712_chip_init(ice) < 0) {
27798c2ecf20Sopenharmony_ci		snd_card_disconnect(card);
27808c2ecf20Sopenharmony_ci		return -EIO;
27818c2ecf20Sopenharmony_ci	}
27828c2ecf20Sopenharmony_ci
27838c2ecf20Sopenharmony_ci	ice->cur_rate = rate;
27848c2ecf20Sopenharmony_ci
27858c2ecf20Sopenharmony_ci	if (ice->pm_resume)
27868c2ecf20Sopenharmony_ci		ice->pm_resume(ice);
27878c2ecf20Sopenharmony_ci
27888c2ecf20Sopenharmony_ci	if (ice->pm_saved_is_spdif_master) {
27898c2ecf20Sopenharmony_ci		/* switching to external clock via SPDIF */
27908c2ecf20Sopenharmony_ci		spin_lock_irq(&ice->reg_lock);
27918c2ecf20Sopenharmony_ci		outb(inb(ICEMT(ice, RATE)) | ICE1712_SPDIF_MASTER,
27928c2ecf20Sopenharmony_ci			ICEMT(ice, RATE));
27938c2ecf20Sopenharmony_ci		spin_unlock_irq(&ice->reg_lock);
27948c2ecf20Sopenharmony_ci		snd_ice1712_set_input_clock_source(ice, 1);
27958c2ecf20Sopenharmony_ci	} else {
27968c2ecf20Sopenharmony_ci		/* internal on-card clock */
27978c2ecf20Sopenharmony_ci		snd_ice1712_set_pro_rate(ice, rate, 1);
27988c2ecf20Sopenharmony_ci		snd_ice1712_set_input_clock_source(ice, 0);
27998c2ecf20Sopenharmony_ci	}
28008c2ecf20Sopenharmony_ci
28018c2ecf20Sopenharmony_ci	outw(ice->pm_saved_spdif_ctrl, ICEMT(ice, ROUTE_SPDOUT));
28028c2ecf20Sopenharmony_ci	outw(ice->pm_saved_route, ICEMT(ice, ROUTE_PSDOUT03));
28038c2ecf20Sopenharmony_ci
28048c2ecf20Sopenharmony_ci	snd_ac97_resume(ice->ac97);
28058c2ecf20Sopenharmony_ci
28068c2ecf20Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
28078c2ecf20Sopenharmony_ci	return 0;
28088c2ecf20Sopenharmony_ci}
28098c2ecf20Sopenharmony_ci
28108c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(snd_ice1712_pm, snd_ice1712_suspend, snd_ice1712_resume);
28118c2ecf20Sopenharmony_ci#define SND_VT1712_PM_OPS	&snd_ice1712_pm
28128c2ecf20Sopenharmony_ci#else
28138c2ecf20Sopenharmony_ci#define SND_VT1712_PM_OPS	NULL
28148c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
28158c2ecf20Sopenharmony_ci
28168c2ecf20Sopenharmony_cistatic struct pci_driver ice1712_driver = {
28178c2ecf20Sopenharmony_ci	.name = KBUILD_MODNAME,
28188c2ecf20Sopenharmony_ci	.id_table = snd_ice1712_ids,
28198c2ecf20Sopenharmony_ci	.probe = snd_ice1712_probe,
28208c2ecf20Sopenharmony_ci	.remove = snd_ice1712_remove,
28218c2ecf20Sopenharmony_ci	.driver = {
28228c2ecf20Sopenharmony_ci		.pm = SND_VT1712_PM_OPS,
28238c2ecf20Sopenharmony_ci	},
28248c2ecf20Sopenharmony_ci};
28258c2ecf20Sopenharmony_ci
28268c2ecf20Sopenharmony_cimodule_pci_driver(ice1712_driver);
2827