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