18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *   ALSA driver for ICEnsemble VT1724 (Envy24HT)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *   Lowlevel functions for ESI Maya44 cards
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *	Copyright (c) 2009 Takashi Iwai <tiwai@suse.de>
88c2ecf20Sopenharmony_ci *	Based on the patches by Rainer Zimmermann <mail@lightshed.de>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <sound/core.h>
148c2ecf20Sopenharmony_ci#include <sound/control.h>
158c2ecf20Sopenharmony_ci#include <sound/pcm.h>
168c2ecf20Sopenharmony_ci#include <sound/tlv.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "ice1712.h"
198c2ecf20Sopenharmony_ci#include "envy24ht.h"
208c2ecf20Sopenharmony_ci#include "maya44.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* WM8776 register indexes */
238c2ecf20Sopenharmony_ci#define WM8776_REG_HEADPHONE_L		0x00
248c2ecf20Sopenharmony_ci#define WM8776_REG_HEADPHONE_R		0x01
258c2ecf20Sopenharmony_ci#define WM8776_REG_HEADPHONE_MASTER	0x02
268c2ecf20Sopenharmony_ci#define WM8776_REG_DAC_ATTEN_L		0x03
278c2ecf20Sopenharmony_ci#define WM8776_REG_DAC_ATTEN_R		0x04
288c2ecf20Sopenharmony_ci#define WM8776_REG_DAC_ATTEN_MASTER	0x05
298c2ecf20Sopenharmony_ci#define WM8776_REG_DAC_PHASE		0x06
308c2ecf20Sopenharmony_ci#define WM8776_REG_DAC_CONTROL		0x07
318c2ecf20Sopenharmony_ci#define WM8776_REG_DAC_MUTE		0x08
328c2ecf20Sopenharmony_ci#define WM8776_REG_DAC_DEEMPH		0x09
338c2ecf20Sopenharmony_ci#define WM8776_REG_DAC_IF_CONTROL	0x0a
348c2ecf20Sopenharmony_ci#define WM8776_REG_ADC_IF_CONTROL	0x0b
358c2ecf20Sopenharmony_ci#define WM8776_REG_MASTER_MODE_CONTROL	0x0c
368c2ecf20Sopenharmony_ci#define WM8776_REG_POWERDOWN		0x0d
378c2ecf20Sopenharmony_ci#define WM8776_REG_ADC_ATTEN_L		0x0e
388c2ecf20Sopenharmony_ci#define WM8776_REG_ADC_ATTEN_R		0x0f
398c2ecf20Sopenharmony_ci#define WM8776_REG_ADC_ALC1		0x10
408c2ecf20Sopenharmony_ci#define WM8776_REG_ADC_ALC2		0x11
418c2ecf20Sopenharmony_ci#define WM8776_REG_ADC_ALC3		0x12
428c2ecf20Sopenharmony_ci#define WM8776_REG_ADC_NOISE_GATE	0x13
438c2ecf20Sopenharmony_ci#define WM8776_REG_ADC_LIMITER		0x14
448c2ecf20Sopenharmony_ci#define WM8776_REG_ADC_MUX		0x15
458c2ecf20Sopenharmony_ci#define WM8776_REG_OUTPUT_MUX		0x16
468c2ecf20Sopenharmony_ci#define WM8776_REG_RESET		0x17
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define WM8776_NUM_REGS			0x18
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* clock ratio identifiers for snd_wm8776_set_rate() */
518c2ecf20Sopenharmony_ci#define WM8776_CLOCK_RATIO_128FS	0
528c2ecf20Sopenharmony_ci#define WM8776_CLOCK_RATIO_192FS	1
538c2ecf20Sopenharmony_ci#define WM8776_CLOCK_RATIO_256FS	2
548c2ecf20Sopenharmony_ci#define WM8776_CLOCK_RATIO_384FS	3
558c2ecf20Sopenharmony_ci#define WM8776_CLOCK_RATIO_512FS	4
568c2ecf20Sopenharmony_ci#define WM8776_CLOCK_RATIO_768FS	5
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cienum { WM_VOL_HP, WM_VOL_DAC, WM_VOL_ADC, WM_NUM_VOLS };
598c2ecf20Sopenharmony_cienum { WM_SW_DAC, WM_SW_BYPASS, WM_NUM_SWITCHES };
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistruct snd_wm8776 {
628c2ecf20Sopenharmony_ci	unsigned char addr;
638c2ecf20Sopenharmony_ci	unsigned short regs[WM8776_NUM_REGS];
648c2ecf20Sopenharmony_ci	unsigned char volumes[WM_NUM_VOLS][2];
658c2ecf20Sopenharmony_ci	unsigned int switch_bits;
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistruct snd_maya44 {
698c2ecf20Sopenharmony_ci	struct snd_ice1712 *ice;
708c2ecf20Sopenharmony_ci	struct snd_wm8776 wm[2];
718c2ecf20Sopenharmony_ci	struct mutex mutex;
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/* write the given register and save the data to the cache */
768c2ecf20Sopenharmony_cistatic void wm8776_write(struct snd_ice1712 *ice, struct snd_wm8776 *wm,
778c2ecf20Sopenharmony_ci			 unsigned char reg, unsigned short val)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	/*
808c2ecf20Sopenharmony_ci	 * WM8776 registers are up to 9 bits wide, bit 8 is placed in the LSB
818c2ecf20Sopenharmony_ci	 * of the address field
828c2ecf20Sopenharmony_ci	 */
838c2ecf20Sopenharmony_ci	snd_vt1724_write_i2c(ice, wm->addr,
848c2ecf20Sopenharmony_ci			     (reg << 1) | ((val >> 8) & 1),
858c2ecf20Sopenharmony_ci			     val & 0xff);
868c2ecf20Sopenharmony_ci	wm->regs[reg] = val;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/*
908c2ecf20Sopenharmony_ci * update the given register with and/or mask and save the data to the cache
918c2ecf20Sopenharmony_ci */
928c2ecf20Sopenharmony_cistatic int wm8776_write_bits(struct snd_ice1712 *ice, struct snd_wm8776 *wm,
938c2ecf20Sopenharmony_ci			     unsigned char reg,
948c2ecf20Sopenharmony_ci			     unsigned short mask, unsigned short val)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	val |= wm->regs[reg] & ~mask;
978c2ecf20Sopenharmony_ci	if (val != wm->regs[reg]) {
988c2ecf20Sopenharmony_ci		wm8776_write(ice, wm, reg, val);
998c2ecf20Sopenharmony_ci		return 1;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci	return 0;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/*
1068c2ecf20Sopenharmony_ci * WM8776 volume controls
1078c2ecf20Sopenharmony_ci */
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistruct maya_vol_info {
1108c2ecf20Sopenharmony_ci	unsigned int maxval;		/* volume range: 0..maxval */
1118c2ecf20Sopenharmony_ci	unsigned char regs[2];		/* left and right registers */
1128c2ecf20Sopenharmony_ci	unsigned short mask;		/* value mask */
1138c2ecf20Sopenharmony_ci	unsigned short offset;		/* zero-value offset */
1148c2ecf20Sopenharmony_ci	unsigned short mute;		/* mute bit */
1158c2ecf20Sopenharmony_ci	unsigned short update;		/* update bits */
1168c2ecf20Sopenharmony_ci	unsigned char mux_bits[2];	/* extra bits for ADC mute */
1178c2ecf20Sopenharmony_ci};
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic const struct maya_vol_info vol_info[WM_NUM_VOLS] = {
1208c2ecf20Sopenharmony_ci	[WM_VOL_HP] = {
1218c2ecf20Sopenharmony_ci		.maxval = 80,
1228c2ecf20Sopenharmony_ci		.regs = { WM8776_REG_HEADPHONE_L, WM8776_REG_HEADPHONE_R },
1238c2ecf20Sopenharmony_ci		.mask = 0x7f,
1248c2ecf20Sopenharmony_ci		.offset = 0x30,
1258c2ecf20Sopenharmony_ci		.mute = 0x00,
1268c2ecf20Sopenharmony_ci		.update = 0x180,	/* update and zero-cross enable */
1278c2ecf20Sopenharmony_ci	},
1288c2ecf20Sopenharmony_ci	[WM_VOL_DAC] = {
1298c2ecf20Sopenharmony_ci		.maxval = 255,
1308c2ecf20Sopenharmony_ci		.regs = { WM8776_REG_DAC_ATTEN_L, WM8776_REG_DAC_ATTEN_R },
1318c2ecf20Sopenharmony_ci		.mask = 0xff,
1328c2ecf20Sopenharmony_ci		.offset = 0x01,
1338c2ecf20Sopenharmony_ci		.mute = 0x00,
1348c2ecf20Sopenharmony_ci		.update = 0x100,	/* zero-cross enable */
1358c2ecf20Sopenharmony_ci	},
1368c2ecf20Sopenharmony_ci	[WM_VOL_ADC] = {
1378c2ecf20Sopenharmony_ci		.maxval = 91,
1388c2ecf20Sopenharmony_ci		.regs = { WM8776_REG_ADC_ATTEN_L, WM8776_REG_ADC_ATTEN_R },
1398c2ecf20Sopenharmony_ci		.mask = 0xff,
1408c2ecf20Sopenharmony_ci		.offset = 0xa5,
1418c2ecf20Sopenharmony_ci		.mute = 0xa5,
1428c2ecf20Sopenharmony_ci		.update = 0x100,	/* update */
1438c2ecf20Sopenharmony_ci		.mux_bits = { 0x80, 0x40 }, /* ADCMUX bits */
1448c2ecf20Sopenharmony_ci	},
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci/*
1488c2ecf20Sopenharmony_ci * dB tables
1498c2ecf20Sopenharmony_ci */
1508c2ecf20Sopenharmony_ci/* headphone output: mute, -73..+6db (1db step) */
1518c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_hp, -7400, 100, 1);
1528c2ecf20Sopenharmony_ci/* DAC output: mute, -127..0db (0.5db step) */
1538c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_dac, -12750, 50, 1);
1548c2ecf20Sopenharmony_ci/* ADC gain: mute, -21..+24db (0.5db step) */
1558c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_adc, -2100, 50, 1);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic int maya_vol_info(struct snd_kcontrol *kcontrol,
1588c2ecf20Sopenharmony_ci			 struct snd_ctl_elem_info *uinfo)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	unsigned int idx = kcontrol->private_value;
1618c2ecf20Sopenharmony_ci	const struct maya_vol_info *vol = &vol_info[idx];
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
1648c2ecf20Sopenharmony_ci	uinfo->count = 2;
1658c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
1668c2ecf20Sopenharmony_ci	uinfo->value.integer.max = vol->maxval;
1678c2ecf20Sopenharmony_ci	return 0;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic int maya_vol_get(struct snd_kcontrol *kcontrol,
1718c2ecf20Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
1748c2ecf20Sopenharmony_ci	struct snd_wm8776 *wm =
1758c2ecf20Sopenharmony_ci		&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
1768c2ecf20Sopenharmony_ci	unsigned int idx = kcontrol->private_value;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
1798c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = wm->volumes[idx][0];
1808c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[1] = wm->volumes[idx][1];
1818c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
1828c2ecf20Sopenharmony_ci	return 0;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int maya_vol_put(struct snd_kcontrol *kcontrol,
1868c2ecf20Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
1898c2ecf20Sopenharmony_ci	struct snd_wm8776 *wm =
1908c2ecf20Sopenharmony_ci		&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
1918c2ecf20Sopenharmony_ci	unsigned int idx = kcontrol->private_value;
1928c2ecf20Sopenharmony_ci	const struct maya_vol_info *vol = &vol_info[idx];
1938c2ecf20Sopenharmony_ci	unsigned int val, data;
1948c2ecf20Sopenharmony_ci	int ch, changed = 0;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
1978c2ecf20Sopenharmony_ci	for (ch = 0; ch < 2; ch++) {
1988c2ecf20Sopenharmony_ci		val = ucontrol->value.integer.value[ch];
1998c2ecf20Sopenharmony_ci		if (val > vol->maxval)
2008c2ecf20Sopenharmony_ci			val = vol->maxval;
2018c2ecf20Sopenharmony_ci		if (val == wm->volumes[idx][ch])
2028c2ecf20Sopenharmony_ci			continue;
2038c2ecf20Sopenharmony_ci		if (!val)
2048c2ecf20Sopenharmony_ci			data = vol->mute;
2058c2ecf20Sopenharmony_ci		else
2068c2ecf20Sopenharmony_ci			data = (val - 1) + vol->offset;
2078c2ecf20Sopenharmony_ci		data |= vol->update;
2088c2ecf20Sopenharmony_ci		changed |= wm8776_write_bits(chip->ice, wm, vol->regs[ch],
2098c2ecf20Sopenharmony_ci					     vol->mask | vol->update, data);
2108c2ecf20Sopenharmony_ci		if (vol->mux_bits[ch])
2118c2ecf20Sopenharmony_ci			wm8776_write_bits(chip->ice, wm, WM8776_REG_ADC_MUX,
2128c2ecf20Sopenharmony_ci					  vol->mux_bits[ch],
2138c2ecf20Sopenharmony_ci					  val ? 0 : vol->mux_bits[ch]);
2148c2ecf20Sopenharmony_ci		wm->volumes[idx][ch] = val;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
2178c2ecf20Sopenharmony_ci	return changed;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci/*
2218c2ecf20Sopenharmony_ci * WM8776 switch controls
2228c2ecf20Sopenharmony_ci */
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci#define COMPOSE_SW_VAL(idx, reg, mask)	((idx) | ((reg) << 8) | ((mask) << 16))
2258c2ecf20Sopenharmony_ci#define GET_SW_VAL_IDX(val)	((val) & 0xff)
2268c2ecf20Sopenharmony_ci#define GET_SW_VAL_REG(val)	(((val) >> 8) & 0xff)
2278c2ecf20Sopenharmony_ci#define GET_SW_VAL_MASK(val)	(((val) >> 16) & 0xff)
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci#define maya_sw_info	snd_ctl_boolean_mono_info
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic int maya_sw_get(struct snd_kcontrol *kcontrol,
2328c2ecf20Sopenharmony_ci		       struct snd_ctl_elem_value *ucontrol)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
2358c2ecf20Sopenharmony_ci	struct snd_wm8776 *wm =
2368c2ecf20Sopenharmony_ci		&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
2378c2ecf20Sopenharmony_ci	unsigned int idx = GET_SW_VAL_IDX(kcontrol->private_value);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = (wm->switch_bits >> idx) & 1;
2408c2ecf20Sopenharmony_ci	return 0;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int maya_sw_put(struct snd_kcontrol *kcontrol,
2448c2ecf20Sopenharmony_ci		       struct snd_ctl_elem_value *ucontrol)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
2478c2ecf20Sopenharmony_ci	struct snd_wm8776 *wm =
2488c2ecf20Sopenharmony_ci		&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
2498c2ecf20Sopenharmony_ci	unsigned int idx = GET_SW_VAL_IDX(kcontrol->private_value);
2508c2ecf20Sopenharmony_ci	unsigned int mask, val;
2518c2ecf20Sopenharmony_ci	int changed;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
2548c2ecf20Sopenharmony_ci	mask = 1 << idx;
2558c2ecf20Sopenharmony_ci	wm->switch_bits &= ~mask;
2568c2ecf20Sopenharmony_ci	val = ucontrol->value.integer.value[0];
2578c2ecf20Sopenharmony_ci	if (val)
2588c2ecf20Sopenharmony_ci		wm->switch_bits |= mask;
2598c2ecf20Sopenharmony_ci	mask = GET_SW_VAL_MASK(kcontrol->private_value);
2608c2ecf20Sopenharmony_ci	changed = wm8776_write_bits(chip->ice, wm,
2618c2ecf20Sopenharmony_ci				    GET_SW_VAL_REG(kcontrol->private_value),
2628c2ecf20Sopenharmony_ci				    mask, val ? mask : 0);
2638c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
2648c2ecf20Sopenharmony_ci	return changed;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci/*
2688c2ecf20Sopenharmony_ci * GPIO pins (known ones for maya44)
2698c2ecf20Sopenharmony_ci */
2708c2ecf20Sopenharmony_ci#define GPIO_PHANTOM_OFF	2
2718c2ecf20Sopenharmony_ci#define GPIO_MIC_RELAY		4
2728c2ecf20Sopenharmony_ci#define GPIO_SPDIF_IN_INV	5
2738c2ecf20Sopenharmony_ci#define GPIO_MUST_BE_0		7
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci/*
2768c2ecf20Sopenharmony_ci * GPIO switch controls
2778c2ecf20Sopenharmony_ci */
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci#define COMPOSE_GPIO_VAL(shift, inv)	((shift) | ((inv) << 8))
2808c2ecf20Sopenharmony_ci#define GET_GPIO_VAL_SHIFT(val)		((val) & 0xff)
2818c2ecf20Sopenharmony_ci#define GET_GPIO_VAL_INV(val)		(((val) >> 8) & 1)
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic int maya_set_gpio_bits(struct snd_ice1712 *ice, unsigned int mask,
2848c2ecf20Sopenharmony_ci			      unsigned int bits)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	unsigned int data;
2878c2ecf20Sopenharmony_ci	data = snd_ice1712_gpio_read(ice);
2888c2ecf20Sopenharmony_ci	if ((data & mask) == bits)
2898c2ecf20Sopenharmony_ci		return 0;
2908c2ecf20Sopenharmony_ci	snd_ice1712_gpio_write(ice, (data & ~mask) | bits);
2918c2ecf20Sopenharmony_ci	return 1;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci#define maya_gpio_sw_info	snd_ctl_boolean_mono_info
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic int maya_gpio_sw_get(struct snd_kcontrol *kcontrol,
2978c2ecf20Sopenharmony_ci			    struct snd_ctl_elem_value *ucontrol)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
3008c2ecf20Sopenharmony_ci	unsigned int shift = GET_GPIO_VAL_SHIFT(kcontrol->private_value);
3018c2ecf20Sopenharmony_ci	unsigned int val;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	val = (snd_ice1712_gpio_read(chip->ice) >> shift) & 1;
3048c2ecf20Sopenharmony_ci	if (GET_GPIO_VAL_INV(kcontrol->private_value))
3058c2ecf20Sopenharmony_ci		val = !val;
3068c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = val;
3078c2ecf20Sopenharmony_ci	return 0;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic int maya_gpio_sw_put(struct snd_kcontrol *kcontrol,
3118c2ecf20Sopenharmony_ci			    struct snd_ctl_elem_value *ucontrol)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
3148c2ecf20Sopenharmony_ci	unsigned int shift = GET_GPIO_VAL_SHIFT(kcontrol->private_value);
3158c2ecf20Sopenharmony_ci	unsigned int val, mask;
3168c2ecf20Sopenharmony_ci	int changed;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
3198c2ecf20Sopenharmony_ci	mask = 1 << shift;
3208c2ecf20Sopenharmony_ci	val = ucontrol->value.integer.value[0];
3218c2ecf20Sopenharmony_ci	if (GET_GPIO_VAL_INV(kcontrol->private_value))
3228c2ecf20Sopenharmony_ci		val = !val;
3238c2ecf20Sopenharmony_ci	val = val ? mask : 0;
3248c2ecf20Sopenharmony_ci	changed = maya_set_gpio_bits(chip->ice, mask, val);
3258c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
3268c2ecf20Sopenharmony_ci	return changed;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci/*
3308c2ecf20Sopenharmony_ci * capture source selection
3318c2ecf20Sopenharmony_ci */
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci/* known working input slots (0-4) */
3348c2ecf20Sopenharmony_ci#define MAYA_LINE_IN	1	/* in-2 */
3358c2ecf20Sopenharmony_ci#define MAYA_MIC_IN	3	/* in-4 */
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic void wm8776_select_input(struct snd_maya44 *chip, int idx, int line)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	wm8776_write_bits(chip->ice, &chip->wm[idx], WM8776_REG_ADC_MUX,
3408c2ecf20Sopenharmony_ci			  0x1f, 1 << line);
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic int maya_rec_src_info(struct snd_kcontrol *kcontrol,
3448c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_info *uinfo)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	static const char * const texts[] = { "Line", "Mic" };
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic int maya_rec_src_get(struct snd_kcontrol *kcontrol,
3528c2ecf20Sopenharmony_ci			    struct snd_ctl_elem_value *ucontrol)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
3558c2ecf20Sopenharmony_ci	int sel;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (snd_ice1712_gpio_read(chip->ice) & (1 << GPIO_MIC_RELAY))
3588c2ecf20Sopenharmony_ci		sel = 1;
3598c2ecf20Sopenharmony_ci	else
3608c2ecf20Sopenharmony_ci		sel = 0;
3618c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = sel;
3628c2ecf20Sopenharmony_ci	return 0;
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic int maya_rec_src_put(struct snd_kcontrol *kcontrol,
3668c2ecf20Sopenharmony_ci			    struct snd_ctl_elem_value *ucontrol)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
3698c2ecf20Sopenharmony_ci	int sel = ucontrol->value.enumerated.item[0];
3708c2ecf20Sopenharmony_ci	int changed;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
3738c2ecf20Sopenharmony_ci	changed = maya_set_gpio_bits(chip->ice, 1 << GPIO_MIC_RELAY,
3748c2ecf20Sopenharmony_ci				     sel ? (1 << GPIO_MIC_RELAY) : 0);
3758c2ecf20Sopenharmony_ci	wm8776_select_input(chip, 0, sel ? MAYA_MIC_IN : MAYA_LINE_IN);
3768c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
3778c2ecf20Sopenharmony_ci	return changed;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci/*
3818c2ecf20Sopenharmony_ci * Maya44 routing switch settings have different meanings than the standard
3828c2ecf20Sopenharmony_ci * ice1724 switches as defined in snd_vt1724_pro_route_info (ice1724.c).
3838c2ecf20Sopenharmony_ci */
3848c2ecf20Sopenharmony_cistatic int maya_pb_route_info(struct snd_kcontrol *kcontrol,
3858c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_info *uinfo)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	static const char * const texts[] = {
3888c2ecf20Sopenharmony_ci		"PCM Out", /* 0 */
3898c2ecf20Sopenharmony_ci		"Input 1", "Input 2", "Input 3", "Input 4"
3908c2ecf20Sopenharmony_ci	};
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic int maya_pb_route_shift(int idx)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	static const unsigned char shift[10] =
3988c2ecf20Sopenharmony_ci		{ 8, 20, 0, 3, 11, 23, 14, 26, 17, 29 };
3998c2ecf20Sopenharmony_ci	return shift[idx % 10];
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic int maya_pb_route_get(struct snd_kcontrol *kcontrol,
4038c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_value *ucontrol)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
4068c2ecf20Sopenharmony_ci	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
4078c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] =
4088c2ecf20Sopenharmony_ci		snd_ice1724_get_route_val(chip->ice, maya_pb_route_shift(idx));
4098c2ecf20Sopenharmony_ci	return 0;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic int maya_pb_route_put(struct snd_kcontrol *kcontrol,
4138c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_value *ucontrol)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
4168c2ecf20Sopenharmony_ci	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
4178c2ecf20Sopenharmony_ci	return snd_ice1724_put_route_val(chip->ice,
4188c2ecf20Sopenharmony_ci					 ucontrol->value.enumerated.item[0],
4198c2ecf20Sopenharmony_ci					 maya_pb_route_shift(idx));
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci/*
4248c2ecf20Sopenharmony_ci * controls to be added
4258c2ecf20Sopenharmony_ci */
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new maya_controls[] = {
4288c2ecf20Sopenharmony_ci	{
4298c2ecf20Sopenharmony_ci		.name = "Crossmix Playback Volume",
4308c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4318c2ecf20Sopenharmony_ci		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
4328c2ecf20Sopenharmony_ci			SNDRV_CTL_ELEM_ACCESS_TLV_READ,
4338c2ecf20Sopenharmony_ci		.info = maya_vol_info,
4348c2ecf20Sopenharmony_ci		.get = maya_vol_get,
4358c2ecf20Sopenharmony_ci		.put = maya_vol_put,
4368c2ecf20Sopenharmony_ci		.tlv = { .p = db_scale_hp },
4378c2ecf20Sopenharmony_ci		.private_value = WM_VOL_HP,
4388c2ecf20Sopenharmony_ci		.count = 2,
4398c2ecf20Sopenharmony_ci	},
4408c2ecf20Sopenharmony_ci	{
4418c2ecf20Sopenharmony_ci		.name = "PCM Playback Volume",
4428c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4438c2ecf20Sopenharmony_ci		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
4448c2ecf20Sopenharmony_ci			SNDRV_CTL_ELEM_ACCESS_TLV_READ,
4458c2ecf20Sopenharmony_ci		.info = maya_vol_info,
4468c2ecf20Sopenharmony_ci		.get = maya_vol_get,
4478c2ecf20Sopenharmony_ci		.put = maya_vol_put,
4488c2ecf20Sopenharmony_ci		.tlv = { .p = db_scale_dac },
4498c2ecf20Sopenharmony_ci		.private_value = WM_VOL_DAC,
4508c2ecf20Sopenharmony_ci		.count = 2,
4518c2ecf20Sopenharmony_ci	},
4528c2ecf20Sopenharmony_ci	{
4538c2ecf20Sopenharmony_ci		.name = "Line Capture Volume",
4548c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4558c2ecf20Sopenharmony_ci		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
4568c2ecf20Sopenharmony_ci			SNDRV_CTL_ELEM_ACCESS_TLV_READ,
4578c2ecf20Sopenharmony_ci		.info = maya_vol_info,
4588c2ecf20Sopenharmony_ci		.get = maya_vol_get,
4598c2ecf20Sopenharmony_ci		.put = maya_vol_put,
4608c2ecf20Sopenharmony_ci		.tlv = { .p = db_scale_adc },
4618c2ecf20Sopenharmony_ci		.private_value = WM_VOL_ADC,
4628c2ecf20Sopenharmony_ci		.count = 2,
4638c2ecf20Sopenharmony_ci	},
4648c2ecf20Sopenharmony_ci	{
4658c2ecf20Sopenharmony_ci		.name = "PCM Playback Switch",
4668c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4678c2ecf20Sopenharmony_ci		.info = maya_sw_info,
4688c2ecf20Sopenharmony_ci		.get = maya_sw_get,
4698c2ecf20Sopenharmony_ci		.put = maya_sw_put,
4708c2ecf20Sopenharmony_ci		.private_value = COMPOSE_SW_VAL(WM_SW_DAC,
4718c2ecf20Sopenharmony_ci						WM8776_REG_OUTPUT_MUX, 0x01),
4728c2ecf20Sopenharmony_ci		.count = 2,
4738c2ecf20Sopenharmony_ci	},
4748c2ecf20Sopenharmony_ci	{
4758c2ecf20Sopenharmony_ci		.name = "Bypass Playback Switch",
4768c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4778c2ecf20Sopenharmony_ci		.info = maya_sw_info,
4788c2ecf20Sopenharmony_ci		.get = maya_sw_get,
4798c2ecf20Sopenharmony_ci		.put = maya_sw_put,
4808c2ecf20Sopenharmony_ci		.private_value = COMPOSE_SW_VAL(WM_SW_BYPASS,
4818c2ecf20Sopenharmony_ci						WM8776_REG_OUTPUT_MUX, 0x04),
4828c2ecf20Sopenharmony_ci		.count = 2,
4838c2ecf20Sopenharmony_ci	},
4848c2ecf20Sopenharmony_ci	{
4858c2ecf20Sopenharmony_ci		.name = "Capture Source",
4868c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4878c2ecf20Sopenharmony_ci		.info = maya_rec_src_info,
4888c2ecf20Sopenharmony_ci		.get = maya_rec_src_get,
4898c2ecf20Sopenharmony_ci		.put = maya_rec_src_put,
4908c2ecf20Sopenharmony_ci	},
4918c2ecf20Sopenharmony_ci	{
4928c2ecf20Sopenharmony_ci		.name = "Mic Phantom Power Switch",
4938c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4948c2ecf20Sopenharmony_ci		.info = maya_gpio_sw_info,
4958c2ecf20Sopenharmony_ci		.get = maya_gpio_sw_get,
4968c2ecf20Sopenharmony_ci		.put = maya_gpio_sw_put,
4978c2ecf20Sopenharmony_ci		.private_value = COMPOSE_GPIO_VAL(GPIO_PHANTOM_OFF, 1),
4988c2ecf20Sopenharmony_ci	},
4998c2ecf20Sopenharmony_ci	{
5008c2ecf20Sopenharmony_ci		.name = "SPDIF Capture Switch",
5018c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5028c2ecf20Sopenharmony_ci		.info = maya_gpio_sw_info,
5038c2ecf20Sopenharmony_ci		.get = maya_gpio_sw_get,
5048c2ecf20Sopenharmony_ci		.put = maya_gpio_sw_put,
5058c2ecf20Sopenharmony_ci		.private_value = COMPOSE_GPIO_VAL(GPIO_SPDIF_IN_INV, 1),
5068c2ecf20Sopenharmony_ci	},
5078c2ecf20Sopenharmony_ci	{
5088c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5098c2ecf20Sopenharmony_ci		.name = "H/W Playback Route",
5108c2ecf20Sopenharmony_ci		.info = maya_pb_route_info,
5118c2ecf20Sopenharmony_ci		.get = maya_pb_route_get,
5128c2ecf20Sopenharmony_ci		.put = maya_pb_route_put,
5138c2ecf20Sopenharmony_ci		.count = 4,  /* FIXME: do controls 5-9 have any meaning? */
5148c2ecf20Sopenharmony_ci	},
5158c2ecf20Sopenharmony_ci};
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic int maya44_add_controls(struct snd_ice1712 *ice)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	int err, i;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(maya_controls); i++) {
5228c2ecf20Sopenharmony_ci		err = snd_ctl_add(ice->card, snd_ctl_new1(&maya_controls[i],
5238c2ecf20Sopenharmony_ci							  ice->spec));
5248c2ecf20Sopenharmony_ci		if (err < 0)
5258c2ecf20Sopenharmony_ci			return err;
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci	return 0;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci/*
5328c2ecf20Sopenharmony_ci * initialize a wm8776 chip
5338c2ecf20Sopenharmony_ci */
5348c2ecf20Sopenharmony_cistatic void wm8776_init(struct snd_ice1712 *ice,
5358c2ecf20Sopenharmony_ci			struct snd_wm8776 *wm, unsigned int addr)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	static const unsigned short inits_wm8776[] = {
5388c2ecf20Sopenharmony_ci		0x02, 0x100, /* R2: headphone L+R muted + update */
5398c2ecf20Sopenharmony_ci		0x05, 0x100, /* R5: DAC output L+R muted + update */
5408c2ecf20Sopenharmony_ci		0x06, 0x000, /* R6: DAC output phase normal */
5418c2ecf20Sopenharmony_ci		0x07, 0x091, /* R7: DAC enable zero cross detection,
5428c2ecf20Sopenharmony_ci				normal output */
5438c2ecf20Sopenharmony_ci		0x08, 0x000, /* R8: DAC soft mute off */
5448c2ecf20Sopenharmony_ci		0x09, 0x000, /* R9: no deemph, DAC zero detect disabled */
5458c2ecf20Sopenharmony_ci		0x0a, 0x022, /* R10: DAC I2C mode, std polarities, 24bit */
5468c2ecf20Sopenharmony_ci		0x0b, 0x022, /* R11: ADC I2C mode, std polarities, 24bit,
5478c2ecf20Sopenharmony_ci				highpass filter enabled */
5488c2ecf20Sopenharmony_ci		0x0c, 0x042, /* R12: ADC+DAC slave, ADC+DAC 44,1kHz */
5498c2ecf20Sopenharmony_ci		0x0d, 0x000, /* R13: all power up */
5508c2ecf20Sopenharmony_ci		0x0e, 0x100, /* R14: ADC left muted,
5518c2ecf20Sopenharmony_ci				enable zero cross detection */
5528c2ecf20Sopenharmony_ci		0x0f, 0x100, /* R15: ADC right muted,
5538c2ecf20Sopenharmony_ci				enable zero cross detection */
5548c2ecf20Sopenharmony_ci			     /* R16: ALC...*/
5558c2ecf20Sopenharmony_ci		0x11, 0x000, /* R17: disable ALC */
5568c2ecf20Sopenharmony_ci			     /* R18: ALC...*/
5578c2ecf20Sopenharmony_ci			     /* R19: noise gate...*/
5588c2ecf20Sopenharmony_ci		0x15, 0x000, /* R21: ADC input mux init, mute all inputs */
5598c2ecf20Sopenharmony_ci		0x16, 0x001, /* R22: output mux, select DAC */
5608c2ecf20Sopenharmony_ci		0xff, 0xff
5618c2ecf20Sopenharmony_ci	};
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	const unsigned short *ptr;
5648c2ecf20Sopenharmony_ci	unsigned char reg;
5658c2ecf20Sopenharmony_ci	unsigned short data;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	wm->addr = addr;
5688c2ecf20Sopenharmony_ci	/* enable DAC output; mute bypass, aux & all inputs */
5698c2ecf20Sopenharmony_ci	wm->switch_bits = (1 << WM_SW_DAC);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	ptr = inits_wm8776;
5728c2ecf20Sopenharmony_ci	while (*ptr != 0xff) {
5738c2ecf20Sopenharmony_ci		reg = *ptr++;
5748c2ecf20Sopenharmony_ci		data = *ptr++;
5758c2ecf20Sopenharmony_ci		wm8776_write(ice, wm, reg, data);
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci/*
5818c2ecf20Sopenharmony_ci * change the rate on the WM8776 codecs.
5828c2ecf20Sopenharmony_ci * this assumes that the VT17xx's rate is changed by the calling function.
5838c2ecf20Sopenharmony_ci * NOTE: even though the WM8776's are running in slave mode and rate
5848c2ecf20Sopenharmony_ci * selection is automatic, we need to call snd_wm8776_set_rate() here
5858c2ecf20Sopenharmony_ci * to make sure some flags are set correctly.
5868c2ecf20Sopenharmony_ci */
5878c2ecf20Sopenharmony_cistatic void set_rate(struct snd_ice1712 *ice, unsigned int rate)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	struct snd_maya44 *chip = ice->spec;
5908c2ecf20Sopenharmony_ci	unsigned int ratio, adc_ratio, val;
5918c2ecf20Sopenharmony_ci	int i;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	switch (rate) {
5948c2ecf20Sopenharmony_ci	case 192000:
5958c2ecf20Sopenharmony_ci		ratio = WM8776_CLOCK_RATIO_128FS;
5968c2ecf20Sopenharmony_ci		break;
5978c2ecf20Sopenharmony_ci	case 176400:
5988c2ecf20Sopenharmony_ci		ratio = WM8776_CLOCK_RATIO_128FS;
5998c2ecf20Sopenharmony_ci		break;
6008c2ecf20Sopenharmony_ci	case 96000:
6018c2ecf20Sopenharmony_ci		ratio = WM8776_CLOCK_RATIO_256FS;
6028c2ecf20Sopenharmony_ci		break;
6038c2ecf20Sopenharmony_ci	case 88200:
6048c2ecf20Sopenharmony_ci		ratio = WM8776_CLOCK_RATIO_384FS;
6058c2ecf20Sopenharmony_ci		break;
6068c2ecf20Sopenharmony_ci	case 48000:
6078c2ecf20Sopenharmony_ci		ratio = WM8776_CLOCK_RATIO_512FS;
6088c2ecf20Sopenharmony_ci		break;
6098c2ecf20Sopenharmony_ci	case 44100:
6108c2ecf20Sopenharmony_ci		ratio = WM8776_CLOCK_RATIO_512FS;
6118c2ecf20Sopenharmony_ci		break;
6128c2ecf20Sopenharmony_ci	case 32000:
6138c2ecf20Sopenharmony_ci		ratio = WM8776_CLOCK_RATIO_768FS;
6148c2ecf20Sopenharmony_ci		break;
6158c2ecf20Sopenharmony_ci	case 0:
6168c2ecf20Sopenharmony_ci		/* no hint - S/PDIF input is master, simply return */
6178c2ecf20Sopenharmony_ci		return;
6188c2ecf20Sopenharmony_ci	default:
6198c2ecf20Sopenharmony_ci		snd_BUG();
6208c2ecf20Sopenharmony_ci		return;
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	/*
6248c2ecf20Sopenharmony_ci	 * this currently sets the same rate for ADC and DAC, but limits
6258c2ecf20Sopenharmony_ci	 * ADC rate to 256X (96kHz). For 256X mode (96kHz), this sets ADC
6268c2ecf20Sopenharmony_ci	 * oversampling to 64x, as recommended by WM8776 datasheet.
6278c2ecf20Sopenharmony_ci	 * Setting the rate is not really necessary in slave mode.
6288c2ecf20Sopenharmony_ci	 */
6298c2ecf20Sopenharmony_ci	adc_ratio = ratio;
6308c2ecf20Sopenharmony_ci	if (adc_ratio < WM8776_CLOCK_RATIO_256FS)
6318c2ecf20Sopenharmony_ci		adc_ratio = WM8776_CLOCK_RATIO_256FS;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	val = adc_ratio;
6348c2ecf20Sopenharmony_ci	if (adc_ratio == WM8776_CLOCK_RATIO_256FS)
6358c2ecf20Sopenharmony_ci		val |= 8;
6368c2ecf20Sopenharmony_ci	val |= ratio << 4;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
6398c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++)
6408c2ecf20Sopenharmony_ci		wm8776_write_bits(ice, &chip->wm[i],
6418c2ecf20Sopenharmony_ci				  WM8776_REG_MASTER_MODE_CONTROL,
6428c2ecf20Sopenharmony_ci				  0x180, val);
6438c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci/*
6478c2ecf20Sopenharmony_ci * supported sample rates (to override the default one)
6488c2ecf20Sopenharmony_ci */
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_cistatic const unsigned int rates[] = {
6518c2ecf20Sopenharmony_ci	32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000
6528c2ecf20Sopenharmony_ci};
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci/* playback rates: 32..192 kHz */
6558c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list dac_rates = {
6568c2ecf20Sopenharmony_ci	.count = ARRAY_SIZE(rates),
6578c2ecf20Sopenharmony_ci	.list = rates,
6588c2ecf20Sopenharmony_ci	.mask = 0
6598c2ecf20Sopenharmony_ci};
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci/*
6638c2ecf20Sopenharmony_ci * chip addresses on I2C bus
6648c2ecf20Sopenharmony_ci */
6658c2ecf20Sopenharmony_cistatic const unsigned char wm8776_addr[2] = {
6668c2ecf20Sopenharmony_ci	0x34, 0x36, /* codec 0 & 1 */
6678c2ecf20Sopenharmony_ci};
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci/*
6708c2ecf20Sopenharmony_ci * initialize the chip
6718c2ecf20Sopenharmony_ci */
6728c2ecf20Sopenharmony_cistatic int maya44_init(struct snd_ice1712 *ice)
6738c2ecf20Sopenharmony_ci{
6748c2ecf20Sopenharmony_ci	int i;
6758c2ecf20Sopenharmony_ci	struct snd_maya44 *chip;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
6788c2ecf20Sopenharmony_ci	if (!chip)
6798c2ecf20Sopenharmony_ci		return -ENOMEM;
6808c2ecf20Sopenharmony_ci	mutex_init(&chip->mutex);
6818c2ecf20Sopenharmony_ci	chip->ice = ice;
6828c2ecf20Sopenharmony_ci	ice->spec = chip;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	/* initialise codecs */
6858c2ecf20Sopenharmony_ci	ice->num_total_dacs = 4;
6868c2ecf20Sopenharmony_ci	ice->num_total_adcs = 4;
6878c2ecf20Sopenharmony_ci	ice->akm_codecs = 0;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
6908c2ecf20Sopenharmony_ci		wm8776_init(ice, &chip->wm[i], wm8776_addr[i]);
6918c2ecf20Sopenharmony_ci		wm8776_select_input(chip, i, MAYA_LINE_IN);
6928c2ecf20Sopenharmony_ci	}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	/* set card specific rates */
6958c2ecf20Sopenharmony_ci	ice->hw_rates = &dac_rates;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	/* register change rate notifier */
6988c2ecf20Sopenharmony_ci	ice->gpio.set_pro_rate = set_rate;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	/* RDMA1 (2nd input channel) is used for ADC by default */
7018c2ecf20Sopenharmony_ci	ice->force_rdma1 = 1;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	/* have an own routing control */
7048c2ecf20Sopenharmony_ci	ice->own_routing = 1;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	return 0;
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci/*
7118c2ecf20Sopenharmony_ci * Maya44 boards don't provide the EEPROM data except for the vendor IDs.
7128c2ecf20Sopenharmony_ci * hence the driver needs to sets up it properly.
7138c2ecf20Sopenharmony_ci */
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cistatic const unsigned char maya44_eeprom[] = {
7168c2ecf20Sopenharmony_ci	[ICE_EEP2_SYSCONF]     = 0x45,
7178c2ecf20Sopenharmony_ci		/* clock xin1=49.152MHz, mpu401, 2 stereo ADCs+DACs */
7188c2ecf20Sopenharmony_ci	[ICE_EEP2_ACLINK]      = 0x80,
7198c2ecf20Sopenharmony_ci		/* I2S */
7208c2ecf20Sopenharmony_ci	[ICE_EEP2_I2S]         = 0xf8,
7218c2ecf20Sopenharmony_ci		/* vol, 96k, 24bit, 192k */
7228c2ecf20Sopenharmony_ci	[ICE_EEP2_SPDIF]       = 0xc3,
7238c2ecf20Sopenharmony_ci		/* enable spdif out, spdif out supp, spdif-in, ext spdif out */
7248c2ecf20Sopenharmony_ci	[ICE_EEP2_GPIO_DIR]    = 0xff,
7258c2ecf20Sopenharmony_ci	[ICE_EEP2_GPIO_DIR1]   = 0xff,
7268c2ecf20Sopenharmony_ci	[ICE_EEP2_GPIO_DIR2]   = 0xff,
7278c2ecf20Sopenharmony_ci	[ICE_EEP2_GPIO_MASK]   = 0/*0x9f*/,
7288c2ecf20Sopenharmony_ci	[ICE_EEP2_GPIO_MASK1]  = 0/*0xff*/,
7298c2ecf20Sopenharmony_ci	[ICE_EEP2_GPIO_MASK2]  = 0/*0x7f*/,
7308c2ecf20Sopenharmony_ci	[ICE_EEP2_GPIO_STATE]  = (1 << GPIO_PHANTOM_OFF) |
7318c2ecf20Sopenharmony_ci			(1 << GPIO_SPDIF_IN_INV),
7328c2ecf20Sopenharmony_ci	[ICE_EEP2_GPIO_STATE1] = 0x00,
7338c2ecf20Sopenharmony_ci	[ICE_EEP2_GPIO_STATE2] = 0x00,
7348c2ecf20Sopenharmony_ci};
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci/* entry point */
7378c2ecf20Sopenharmony_cistruct snd_ice1712_card_info snd_vt1724_maya44_cards[] = {
7388c2ecf20Sopenharmony_ci	{
7398c2ecf20Sopenharmony_ci		.subvendor = VT1724_SUBDEVICE_MAYA44,
7408c2ecf20Sopenharmony_ci		.name = "ESI Maya44",
7418c2ecf20Sopenharmony_ci		.model = "maya44",
7428c2ecf20Sopenharmony_ci		.chip_init = maya44_init,
7438c2ecf20Sopenharmony_ci		.build_controls = maya44_add_controls,
7448c2ecf20Sopenharmony_ci		.eeprom_size = sizeof(maya44_eeprom),
7458c2ecf20Sopenharmony_ci		.eeprom_data = maya44_eeprom,
7468c2ecf20Sopenharmony_ci	},
7478c2ecf20Sopenharmony_ci	{ } /* terminator */
7488c2ecf20Sopenharmony_ci};
749