11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * ALSA driver for ICEnsemble VT1724 (Envy24HT) 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Lowlevel functions for ESI Juli@ cards 61da177e4SLinus Torvalds * 7c1017a4cSJaroslav Kysela * Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz> 8d16be8edSPavel Hofman * 2008 Pavel Hofman <dustin@seznam.cz> 91da177e4SLinus Torvalds */ 101da177e4SLinus Torvalds 111da177e4SLinus Torvalds #include <linux/delay.h> 121da177e4SLinus Torvalds #include <linux/interrupt.h> 131da177e4SLinus Torvalds #include <linux/init.h> 141da177e4SLinus Torvalds #include <linux/slab.h> 15338e17d3SJoey Pabalinas #include <linux/string.h> 161da177e4SLinus Torvalds #include <sound/core.h> 17d16be8edSPavel Hofman #include <sound/tlv.h> 181da177e4SLinus Torvalds 191da177e4SLinus Torvalds #include "ice1712.h" 201da177e4SLinus Torvalds #include "envy24ht.h" 211da177e4SLinus Torvalds #include "juli.h" 22cc67b7f7SVedran Miletic 237cda8ba9STakashi Iwai struct juli_spec { 247cda8ba9STakashi Iwai struct ak4114 *ak4114; 257cda8ba9STakashi Iwai unsigned int analog:1; 267cda8ba9STakashi Iwai }; 277cda8ba9STakashi Iwai 281da177e4SLinus Torvalds /* 291da177e4SLinus Torvalds * chip addresses on I2C bus 301da177e4SLinus Torvalds */ 311da177e4SLinus Torvalds #define AK4114_ADDR 0x20 /* S/PDIF receiver */ 321da177e4SLinus Torvalds #define AK4358_ADDR 0x22 /* DAC */ 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds /* 35d16be8edSPavel Hofman * Juli does not use the standard ICE1724 clock scheme. Juli's ice1724 chip is 36d16be8edSPavel Hofman * supplied by external clock provided by Xilinx array and MK73-1 PLL frequency 37d16be8edSPavel Hofman * multiplier. Actual frequency is set by ice1724 GPIOs hooked to the Xilinx. 38d16be8edSPavel Hofman * 39d16be8edSPavel Hofman * The clock circuitry is supplied by the two ice1724 crystals. This 40d16be8edSPavel Hofman * arrangement allows to generate independent clock signal for AK4114's input 41d16be8edSPavel Hofman * rate detection circuit. As a result, Juli, unlike most other 42d16be8edSPavel Hofman * ice1724+ak4114-based cards, detects spdif input rate correctly. 43d16be8edSPavel Hofman * This fact is applied in the driver, allowing to modify PCM stream rate 44d16be8edSPavel Hofman * parameter according to the actual input rate. 45d16be8edSPavel Hofman * 46d16be8edSPavel Hofman * Juli uses the remaining three stereo-channels of its DAC to optionally 47d16be8edSPavel Hofman * monitor analog input, digital input, and digital output. The corresponding 48d16be8edSPavel Hofman * I2S signals are routed by Xilinx, controlled by GPIOs. 49d16be8edSPavel Hofman * 50d16be8edSPavel Hofman * The master mute is implemented using output muting transistors (GPIO) in 51d16be8edSPavel Hofman * combination with smuting the DAC. 52d16be8edSPavel Hofman * 53d16be8edSPavel Hofman * The card itself has no HW master volume control, implemented using the 54d16be8edSPavel Hofman * vmaster control. 55d16be8edSPavel Hofman * 56d16be8edSPavel Hofman * TODO: 57d16be8edSPavel Hofman * researching and fixing the input monitors 58d16be8edSPavel Hofman */ 59d16be8edSPavel Hofman 60d16be8edSPavel Hofman /* 611da177e4SLinus Torvalds * GPIO pins 621da177e4SLinus Torvalds */ 631da177e4SLinus Torvalds #define GPIO_FREQ_MASK (3<<0) 641da177e4SLinus Torvalds #define GPIO_FREQ_32KHZ (0<<0) 651da177e4SLinus Torvalds #define GPIO_FREQ_44KHZ (1<<0) 661da177e4SLinus Torvalds #define GPIO_FREQ_48KHZ (2<<0) 671da177e4SLinus Torvalds #define GPIO_MULTI_MASK (3<<2) 681da177e4SLinus Torvalds #define GPIO_MULTI_4X (0<<2) 691da177e4SLinus Torvalds #define GPIO_MULTI_2X (1<<2) 701da177e4SLinus Torvalds #define GPIO_MULTI_1X (2<<2) /* also external */ 711da177e4SLinus Torvalds #define GPIO_MULTI_HALF (3<<2) 72d16be8edSPavel Hofman #define GPIO_INTERNAL_CLOCK (1<<4) /* 0 = external, 1 = internal */ 73d16be8edSPavel Hofman #define GPIO_CLOCK_MASK (1<<4) 741da177e4SLinus Torvalds #define GPIO_ANALOG_PRESENT (1<<5) /* RO only: 0 = present */ 751da177e4SLinus Torvalds #define GPIO_RXMCLK_SEL (1<<7) /* must be 0 */ 761da177e4SLinus Torvalds #define GPIO_AK5385A_CKS0 (1<<8) 77d16be8edSPavel Hofman #define GPIO_AK5385A_DFS1 (1<<9) 78d16be8edSPavel Hofman #define GPIO_AK5385A_DFS0 (1<<10) 791da177e4SLinus Torvalds #define GPIO_DIGOUT_MONITOR (1<<11) /* 1 = active */ 801da177e4SLinus Torvalds #define GPIO_DIGIN_MONITOR (1<<12) /* 1 = active */ 811da177e4SLinus Torvalds #define GPIO_ANAIN_MONITOR (1<<13) /* 1 = active */ 82d16be8edSPavel Hofman #define GPIO_AK5385A_CKS1 (1<<14) /* must be 0 */ 83d16be8edSPavel Hofman #define GPIO_MUTE_CONTROL (1<<15) /* output mute, 1 = muted */ 84d16be8edSPavel Hofman 85d16be8edSPavel Hofman #define GPIO_RATE_MASK (GPIO_FREQ_MASK | GPIO_MULTI_MASK | \ 86d16be8edSPavel Hofman GPIO_CLOCK_MASK) 87d16be8edSPavel Hofman #define GPIO_AK5385A_MASK (GPIO_AK5385A_CKS0 | GPIO_AK5385A_DFS0 | \ 88d16be8edSPavel Hofman GPIO_AK5385A_DFS1 | GPIO_AK5385A_CKS1) 89d16be8edSPavel Hofman 90d16be8edSPavel Hofman #define JULI_PCM_RATE (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ 91d16be8edSPavel Hofman SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ 92d16be8edSPavel Hofman SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ 93d16be8edSPavel Hofman SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \ 94d16be8edSPavel Hofman SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) 95d16be8edSPavel Hofman 96d16be8edSPavel Hofman #define GPIO_RATE_16000 (GPIO_FREQ_32KHZ | GPIO_MULTI_HALF | \ 97d16be8edSPavel Hofman GPIO_INTERNAL_CLOCK) 98d16be8edSPavel Hofman #define GPIO_RATE_22050 (GPIO_FREQ_44KHZ | GPIO_MULTI_HALF | \ 99d16be8edSPavel Hofman GPIO_INTERNAL_CLOCK) 100d16be8edSPavel Hofman #define GPIO_RATE_24000 (GPIO_FREQ_48KHZ | GPIO_MULTI_HALF | \ 101d16be8edSPavel Hofman GPIO_INTERNAL_CLOCK) 102d16be8edSPavel Hofman #define GPIO_RATE_32000 (GPIO_FREQ_32KHZ | GPIO_MULTI_1X | \ 103d16be8edSPavel Hofman GPIO_INTERNAL_CLOCK) 104d16be8edSPavel Hofman #define GPIO_RATE_44100 (GPIO_FREQ_44KHZ | GPIO_MULTI_1X | \ 105d16be8edSPavel Hofman GPIO_INTERNAL_CLOCK) 106d16be8edSPavel Hofman #define GPIO_RATE_48000 (GPIO_FREQ_48KHZ | GPIO_MULTI_1X | \ 107d16be8edSPavel Hofman GPIO_INTERNAL_CLOCK) 108d16be8edSPavel Hofman #define GPIO_RATE_64000 (GPIO_FREQ_32KHZ | GPIO_MULTI_2X | \ 109d16be8edSPavel Hofman GPIO_INTERNAL_CLOCK) 110d16be8edSPavel Hofman #define GPIO_RATE_88200 (GPIO_FREQ_44KHZ | GPIO_MULTI_2X | \ 111d16be8edSPavel Hofman GPIO_INTERNAL_CLOCK) 112d16be8edSPavel Hofman #define GPIO_RATE_96000 (GPIO_FREQ_48KHZ | GPIO_MULTI_2X | \ 113d16be8edSPavel Hofman GPIO_INTERNAL_CLOCK) 114d16be8edSPavel Hofman #define GPIO_RATE_176400 (GPIO_FREQ_44KHZ | GPIO_MULTI_4X | \ 115d16be8edSPavel Hofman GPIO_INTERNAL_CLOCK) 116d16be8edSPavel Hofman #define GPIO_RATE_192000 (GPIO_FREQ_48KHZ | GPIO_MULTI_4X | \ 117d16be8edSPavel Hofman GPIO_INTERNAL_CLOCK) 118d16be8edSPavel Hofman 119d16be8edSPavel Hofman /* 120d16be8edSPavel Hofman * Initial setup of the conversion array GPIO <-> rate 121d16be8edSPavel Hofman */ 122965f19beSTakashi Iwai static const unsigned int juli_rates[] = { 123d16be8edSPavel Hofman 16000, 22050, 24000, 32000, 124d16be8edSPavel Hofman 44100, 48000, 64000, 88200, 125d16be8edSPavel Hofman 96000, 176400, 192000, 126d16be8edSPavel Hofman }; 127d16be8edSPavel Hofman 128965f19beSTakashi Iwai static const unsigned int gpio_vals[] = { 129d16be8edSPavel Hofman GPIO_RATE_16000, GPIO_RATE_22050, GPIO_RATE_24000, GPIO_RATE_32000, 130d16be8edSPavel Hofman GPIO_RATE_44100, GPIO_RATE_48000, GPIO_RATE_64000, GPIO_RATE_88200, 131d16be8edSPavel Hofman GPIO_RATE_96000, GPIO_RATE_176400, GPIO_RATE_192000, 132d16be8edSPavel Hofman }; 133d16be8edSPavel Hofman 134965f19beSTakashi Iwai static const struct snd_pcm_hw_constraint_list juli_rates_info = { 135d16be8edSPavel Hofman .count = ARRAY_SIZE(juli_rates), 136d16be8edSPavel Hofman .list = juli_rates, 137d16be8edSPavel Hofman .mask = 0, 138d16be8edSPavel Hofman }; 139d16be8edSPavel Hofman 140d16be8edSPavel Hofman static int get_gpio_val(int rate) 141d16be8edSPavel Hofman { 142d16be8edSPavel Hofman int i; 143d16be8edSPavel Hofman for (i = 0; i < ARRAY_SIZE(juli_rates); i++) 144d16be8edSPavel Hofman if (juli_rates[i] == rate) 145d16be8edSPavel Hofman return gpio_vals[i]; 146d16be8edSPavel Hofman return 0; 147d16be8edSPavel Hofman } 1481da177e4SLinus Torvalds 149cc67b7f7SVedran Miletic static void juli_ak4114_write(void *private_data, unsigned char reg, 150cc67b7f7SVedran Miletic unsigned char val) 1511da177e4SLinus Torvalds { 152cc67b7f7SVedran Miletic snd_vt1724_write_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR, 153cc67b7f7SVedran Miletic reg, val); 1541da177e4SLinus Torvalds } 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds static unsigned char juli_ak4114_read(void *private_data, unsigned char reg) 1571da177e4SLinus Torvalds { 158cc67b7f7SVedran Miletic return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data, 159cc67b7f7SVedran Miletic AK4114_ADDR, reg); 1601da177e4SLinus Torvalds } 1611da177e4SLinus Torvalds 162d16be8edSPavel Hofman /* 163d16be8edSPavel Hofman * If SPDIF capture and slaved to SPDIF-IN, setting runtime rate 164d16be8edSPavel Hofman * to the external rate 165d16be8edSPavel Hofman */ 166c93f5a1eSTakashi Iwai static void juli_spdif_in_open(struct snd_ice1712 *ice, 167c93f5a1eSTakashi Iwai struct snd_pcm_substream *substream) 168c93f5a1eSTakashi Iwai { 169c93f5a1eSTakashi Iwai struct juli_spec *spec = ice->spec; 170c93f5a1eSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 171c93f5a1eSTakashi Iwai int rate; 172c93f5a1eSTakashi Iwai 173d16be8edSPavel Hofman if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || 174d16be8edSPavel Hofman !ice->is_spdif_master(ice)) 175c93f5a1eSTakashi Iwai return; 176c93f5a1eSTakashi Iwai rate = snd_ak4114_external_rate(spec->ak4114); 177c93f5a1eSTakashi Iwai if (rate >= runtime->hw.rate_min && rate <= runtime->hw.rate_max) { 178c93f5a1eSTakashi Iwai runtime->hw.rate_min = rate; 179c93f5a1eSTakashi Iwai runtime->hw.rate_max = rate; 180c93f5a1eSTakashi Iwai } 181c93f5a1eSTakashi Iwai } 182c93f5a1eSTakashi Iwai 1831da177e4SLinus Torvalds /* 1841da177e4SLinus Torvalds * AK4358 section 1851da177e4SLinus Torvalds */ 1861da177e4SLinus Torvalds 187ab0c7d72STakashi Iwai static void juli_akm_lock(struct snd_akm4xxx *ak, int chip) 1881da177e4SLinus Torvalds { 1891da177e4SLinus Torvalds } 1901da177e4SLinus Torvalds 191ab0c7d72STakashi Iwai static void juli_akm_unlock(struct snd_akm4xxx *ak, int chip) 1921da177e4SLinus Torvalds { 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds 195ab0c7d72STakashi Iwai static void juli_akm_write(struct snd_akm4xxx *ak, int chip, 1961da177e4SLinus Torvalds unsigned char addr, unsigned char data) 1971da177e4SLinus Torvalds { 198ab0c7d72STakashi Iwai struct snd_ice1712 *ice = ak->private_data[0]; 1991da177e4SLinus Torvalds 200da3cec35STakashi Iwai if (snd_BUG_ON(chip)) 201da3cec35STakashi Iwai return; 2021da177e4SLinus Torvalds snd_vt1724_write_i2c(ice, AK4358_ADDR, addr, data); 2031da177e4SLinus Torvalds } 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds /* 206d16be8edSPavel Hofman * change the rate of envy24HT, AK4358, AK5385 2071da177e4SLinus Torvalds */ 208ab0c7d72STakashi Iwai static void juli_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) 2091da177e4SLinus Torvalds { 210d16be8edSPavel Hofman unsigned char old, tmp, ak4358_dfs; 211d16be8edSPavel Hofman unsigned int ak5385_pins, old_gpio, new_gpio; 212d16be8edSPavel Hofman struct snd_ice1712 *ice = ak->private_data[0]; 213d16be8edSPavel Hofman struct juli_spec *spec = ice->spec; 2141da177e4SLinus Torvalds 215d16be8edSPavel Hofman if (rate == 0) /* no hint - S/PDIF input is master or the new spdif 216d16be8edSPavel Hofman input rate undetected, simply return */ 2171da177e4SLinus Torvalds return; 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds /* adjust DFS on codecs */ 220d16be8edSPavel Hofman if (rate > 96000) { 221d16be8edSPavel Hofman ak4358_dfs = 2; 222d16be8edSPavel Hofman ak5385_pins = GPIO_AK5385A_DFS1 | GPIO_AK5385A_CKS0; 223d16be8edSPavel Hofman } else if (rate > 48000) { 224d16be8edSPavel Hofman ak4358_dfs = 1; 225d16be8edSPavel Hofman ak5385_pins = GPIO_AK5385A_DFS0; 226d16be8edSPavel Hofman } else { 227d16be8edSPavel Hofman ak4358_dfs = 0; 228d16be8edSPavel Hofman ak5385_pins = 0; 229d16be8edSPavel Hofman } 230d16be8edSPavel Hofman /* AK5385 first, since it requires cold reset affecting both codecs */ 231d16be8edSPavel Hofman old_gpio = ice->gpio.get_data(ice); 232d16be8edSPavel Hofman new_gpio = (old_gpio & ~GPIO_AK5385A_MASK) | ak5385_pins; 2336dfb5affSTakashi Iwai /* dev_dbg(ice->card->dev, "JULI - ak5385 set_rate_val: new gpio 0x%x\n", 234d16be8edSPavel Hofman new_gpio); */ 235d16be8edSPavel Hofman ice->gpio.set_data(ice, new_gpio); 2361da177e4SLinus Torvalds 237d16be8edSPavel Hofman /* cold reset */ 238d16be8edSPavel Hofman old = inb(ICEMT1724(ice, AC97_CMD)); 239d16be8edSPavel Hofman outb(old | VT1724_AC97_COLD, ICEMT1724(ice, AC97_CMD)); 240d16be8edSPavel Hofman udelay(1); 241d16be8edSPavel Hofman outb(old & ~VT1724_AC97_COLD, ICEMT1724(ice, AC97_CMD)); 242d16be8edSPavel Hofman 243d16be8edSPavel Hofman /* AK4358 */ 244d16be8edSPavel Hofman /* set new value, reset DFS */ 2451da177e4SLinus Torvalds tmp = snd_akm4xxx_get(ak, 0, 2); 2461da177e4SLinus Torvalds snd_akm4xxx_reset(ak, 1); 2471da177e4SLinus Torvalds tmp = snd_akm4xxx_get(ak, 0, 2); 2481da177e4SLinus Torvalds tmp &= ~(0x03 << 4); 249d16be8edSPavel Hofman tmp |= ak4358_dfs << 4; 2501da177e4SLinus Torvalds snd_akm4xxx_set(ak, 0, 2, tmp); 2511da177e4SLinus Torvalds snd_akm4xxx_reset(ak, 0); 252d16be8edSPavel Hofman 253d16be8edSPavel Hofman /* reinit ak4114 */ 254d16be8edSPavel Hofman snd_ak4114_reinit(spec->ak4114); 2551da177e4SLinus Torvalds } 2561da177e4SLinus Torvalds 257d16be8edSPavel Hofman #define AK_DAC(xname, xch) { .name = xname, .num_channels = xch } 258d16be8edSPavel Hofman #define PCM_VOLUME "PCM Playback Volume" 259d16be8edSPavel Hofman #define MONITOR_AN_IN_VOLUME "Monitor Analog In Volume" 260d16be8edSPavel Hofman #define MONITOR_DIG_IN_VOLUME "Monitor Digital In Volume" 261d16be8edSPavel Hofman #define MONITOR_DIG_OUT_VOLUME "Monitor Digital Out Volume" 262d16be8edSPavel Hofman 263d16be8edSPavel Hofman static const struct snd_akm4xxx_dac_channel juli_dac[] = { 264d16be8edSPavel Hofman AK_DAC(PCM_VOLUME, 2), 265d16be8edSPavel Hofman AK_DAC(MONITOR_AN_IN_VOLUME, 2), 266d16be8edSPavel Hofman AK_DAC(MONITOR_DIG_OUT_VOLUME, 2), 267d16be8edSPavel Hofman AK_DAC(MONITOR_DIG_IN_VOLUME, 2), 268d16be8edSPavel Hofman }; 269d16be8edSPavel Hofman 270d16be8edSPavel Hofman 2713135432eSBhumika Goyal static const struct snd_akm4xxx akm_juli_dac = { 2721da177e4SLinus Torvalds .type = SND_AK4358, 273d16be8edSPavel Hofman .num_dacs = 8, /* DAC1 - analog out 274d16be8edSPavel Hofman DAC2 - analog in monitor 275d16be8edSPavel Hofman DAC3 - digital out monitor 276d16be8edSPavel Hofman DAC4 - digital in monitor 277d16be8edSPavel Hofman */ 2781da177e4SLinus Torvalds .ops = { 2791da177e4SLinus Torvalds .lock = juli_akm_lock, 2801da177e4SLinus Torvalds .unlock = juli_akm_unlock, 2811da177e4SLinus Torvalds .write = juli_akm_write, 2821da177e4SLinus Torvalds .set_rate_val = juli_akm_set_rate_val 283d16be8edSPavel Hofman }, 284d16be8edSPavel Hofman .dac_info = juli_dac, 2851da177e4SLinus Torvalds }; 2861da177e4SLinus Torvalds 287d16be8edSPavel Hofman #define juli_mute_info snd_ctl_boolean_mono_info 288d16be8edSPavel Hofman 289d16be8edSPavel Hofman static int juli_mute_get(struct snd_kcontrol *kcontrol, 290d16be8edSPavel Hofman struct snd_ctl_elem_value *ucontrol) 291d16be8edSPavel Hofman { 292d16be8edSPavel Hofman struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 293d16be8edSPavel Hofman unsigned int val; 294d16be8edSPavel Hofman val = ice->gpio.get_data(ice) & (unsigned int) kcontrol->private_value; 295d16be8edSPavel Hofman if (kcontrol->private_value == GPIO_MUTE_CONTROL) 296d16be8edSPavel Hofman /* val 0 = signal on */ 297d16be8edSPavel Hofman ucontrol->value.integer.value[0] = (val) ? 0 : 1; 298d16be8edSPavel Hofman else 299d16be8edSPavel Hofman /* val 1 = signal on */ 300d16be8edSPavel Hofman ucontrol->value.integer.value[0] = (val) ? 1 : 0; 301d16be8edSPavel Hofman return 0; 302d16be8edSPavel Hofman } 303d16be8edSPavel Hofman 304d16be8edSPavel Hofman static int juli_mute_put(struct snd_kcontrol *kcontrol, 305d16be8edSPavel Hofman struct snd_ctl_elem_value *ucontrol) 306d16be8edSPavel Hofman { 307d16be8edSPavel Hofman struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 308d16be8edSPavel Hofman unsigned int old_gpio, new_gpio; 309d16be8edSPavel Hofman old_gpio = ice->gpio.get_data(ice); 310d16be8edSPavel Hofman if (ucontrol->value.integer.value[0]) { 311d16be8edSPavel Hofman /* unmute */ 312d16be8edSPavel Hofman if (kcontrol->private_value == GPIO_MUTE_CONTROL) { 313d16be8edSPavel Hofman /* 0 = signal on */ 314d16be8edSPavel Hofman new_gpio = old_gpio & ~GPIO_MUTE_CONTROL; 315d16be8edSPavel Hofman /* un-smuting DAC */ 316d16be8edSPavel Hofman snd_akm4xxx_write(ice->akm, 0, 0x01, 0x01); 317d16be8edSPavel Hofman } else 318d16be8edSPavel Hofman /* 1 = signal on */ 319d16be8edSPavel Hofman new_gpio = old_gpio | 320d16be8edSPavel Hofman (unsigned int) kcontrol->private_value; 321d16be8edSPavel Hofman } else { 322d16be8edSPavel Hofman /* mute */ 323d16be8edSPavel Hofman if (kcontrol->private_value == GPIO_MUTE_CONTROL) { 324d16be8edSPavel Hofman /* 1 = signal off */ 325d16be8edSPavel Hofman new_gpio = old_gpio | GPIO_MUTE_CONTROL; 326d16be8edSPavel Hofman /* smuting DAC */ 327d16be8edSPavel Hofman snd_akm4xxx_write(ice->akm, 0, 0x01, 0x03); 328d16be8edSPavel Hofman } else 329d16be8edSPavel Hofman /* 0 = signal off */ 330d16be8edSPavel Hofman new_gpio = old_gpio & 331d16be8edSPavel Hofman ~((unsigned int) kcontrol->private_value); 332d16be8edSPavel Hofman } 3336dfb5affSTakashi Iwai /* dev_dbg(ice->card->dev, 334e2ea7cfcSTakashi Iwai "JULI - mute/unmute: control_value: 0x%x, old_gpio: 0x%x, " 335e2ea7cfcSTakashi Iwai "new_gpio 0x%x\n", 336d16be8edSPavel Hofman (unsigned int)ucontrol->value.integer.value[0], old_gpio, 337d16be8edSPavel Hofman new_gpio); */ 338d16be8edSPavel Hofman if (old_gpio != new_gpio) { 339d16be8edSPavel Hofman ice->gpio.set_data(ice, new_gpio); 340d16be8edSPavel Hofman return 1; 341d16be8edSPavel Hofman } 342d16be8edSPavel Hofman /* no change */ 343d16be8edSPavel Hofman return 0; 344d16be8edSPavel Hofman } 345d16be8edSPavel Hofman 346b4e5e707STakashi Iwai static const struct snd_kcontrol_new juli_mute_controls[] = { 347d16be8edSPavel Hofman { 348d16be8edSPavel Hofman .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 349d16be8edSPavel Hofman .name = "Master Playback Switch", 350d16be8edSPavel Hofman .info = juli_mute_info, 351d16be8edSPavel Hofman .get = juli_mute_get, 352d16be8edSPavel Hofman .put = juli_mute_put, 353d16be8edSPavel Hofman .private_value = GPIO_MUTE_CONTROL, 354d16be8edSPavel Hofman }, 355d16be8edSPavel Hofman /* Although the following functionality respects the succint NDA'd 356d16be8edSPavel Hofman * documentation from the card manufacturer, and the same way of 357d16be8edSPavel Hofman * operation is coded in OSS Juli driver, only Digital Out monitor 358d16be8edSPavel Hofman * seems to work. Surprisingly, Analog input monitor outputs Digital 359d16be8edSPavel Hofman * output data. The two are independent, as enabling both doubles 360d16be8edSPavel Hofman * volume of the monitor sound. 361d16be8edSPavel Hofman * 362d16be8edSPavel Hofman * Checking traces on the board suggests the functionality described 363d16be8edSPavel Hofman * by the manufacturer is correct - I2S from ADC and AK4114 364d16be8edSPavel Hofman * go to ICE as well as to Xilinx, I2S inputs of DAC2,3,4 (the monitor 365d16be8edSPavel Hofman * inputs) are fed from Xilinx. 366d16be8edSPavel Hofman * 367d16be8edSPavel Hofman * I even checked traces on board and coded a support in driver for 36806fe9fb4SDirk Hohndel * an alternative possibility - the unused I2S ICE output channels 369d16be8edSPavel Hofman * switched to HW-IN/SPDIF-IN and providing the monitoring signal to 370d16be8edSPavel Hofman * the DAC - to no avail. The I2S outputs seem to be unconnected. 371d16be8edSPavel Hofman * 372d16be8edSPavel Hofman * The windows driver supports the monitoring correctly. 373d16be8edSPavel Hofman */ 374d16be8edSPavel Hofman { 375d16be8edSPavel Hofman .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 376d16be8edSPavel Hofman .name = "Monitor Analog In Switch", 377d16be8edSPavel Hofman .info = juli_mute_info, 378d16be8edSPavel Hofman .get = juli_mute_get, 379d16be8edSPavel Hofman .put = juli_mute_put, 380d16be8edSPavel Hofman .private_value = GPIO_ANAIN_MONITOR, 381d16be8edSPavel Hofman }, 382d16be8edSPavel Hofman { 383d16be8edSPavel Hofman .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 384d16be8edSPavel Hofman .name = "Monitor Digital Out Switch", 385d16be8edSPavel Hofman .info = juli_mute_info, 386d16be8edSPavel Hofman .get = juli_mute_get, 387d16be8edSPavel Hofman .put = juli_mute_put, 388d16be8edSPavel Hofman .private_value = GPIO_DIGOUT_MONITOR, 389d16be8edSPavel Hofman }, 390d16be8edSPavel Hofman { 391d16be8edSPavel Hofman .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 392d16be8edSPavel Hofman .name = "Monitor Digital In Switch", 393d16be8edSPavel Hofman .info = juli_mute_info, 394d16be8edSPavel Hofman .get = juli_mute_get, 395d16be8edSPavel Hofman .put = juli_mute_put, 396d16be8edSPavel Hofman .private_value = GPIO_DIGIN_MONITOR, 397d16be8edSPavel Hofman }, 398d16be8edSPavel Hofman }; 399d16be8edSPavel Hofman 4009ab0cb30STakashi Iwai static const char * const follower_vols[] = { 401d16be8edSPavel Hofman PCM_VOLUME, 402d16be8edSPavel Hofman MONITOR_AN_IN_VOLUME, 403d16be8edSPavel Hofman MONITOR_DIG_IN_VOLUME, 404d16be8edSPavel Hofman MONITOR_DIG_OUT_VOLUME, 405d16be8edSPavel Hofman NULL 406d16be8edSPavel Hofman }; 407d16be8edSPavel Hofman 408e23e7a14SBill Pemberton static 409d16be8edSPavel Hofman DECLARE_TLV_DB_SCALE(juli_master_db_scale, -6350, 50, 1); 410d16be8edSPavel Hofman 411e23e7a14SBill Pemberton static struct snd_kcontrol *ctl_find(struct snd_card *card, 412d16be8edSPavel Hofman const char *name) 413d16be8edSPavel Hofman { 414338e17d3SJoey Pabalinas struct snd_ctl_elem_id sid = {0}; 415338e17d3SJoey Pabalinas 416338e17d3SJoey Pabalinas strlcpy(sid.name, name, sizeof(sid.name)); 417d16be8edSPavel Hofman sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 418d16be8edSPavel Hofman return snd_ctl_find_id(card, &sid); 419d16be8edSPavel Hofman } 420d16be8edSPavel Hofman 4219ab0cb30STakashi Iwai static void add_followers(struct snd_card *card, 422a2af050fSTakashi Iwai struct snd_kcontrol *master, 423f16a4e96STakashi Iwai const char * const *list) 424d16be8edSPavel Hofman { 425d16be8edSPavel Hofman for (; *list; list++) { 4269ab0cb30STakashi Iwai struct snd_kcontrol *follower = ctl_find(card, *list); 4279ab0cb30STakashi Iwai /* dev_dbg(card->dev, "add_followers - %s\n", *list); */ 4289ab0cb30STakashi Iwai if (follower) { 4299ab0cb30STakashi Iwai /* dev_dbg(card->dev, "follower %s found\n", *list); */ 4309ab0cb30STakashi Iwai snd_ctl_add_follower(master, follower); 431d16be8edSPavel Hofman } 432d16be8edSPavel Hofman } 433d16be8edSPavel Hofman } 434d16be8edSPavel Hofman 435e23e7a14SBill Pemberton static int juli_add_controls(struct snd_ice1712 *ice) 4361da177e4SLinus Torvalds { 4377cda8ba9STakashi Iwai struct juli_spec *spec = ice->spec; 438fdd4bb49STakashi Iwai int err; 439d16be8edSPavel Hofman unsigned int i; 440d16be8edSPavel Hofman struct snd_kcontrol *vmaster; 441d16be8edSPavel Hofman 442fdd4bb49STakashi Iwai err = snd_ice1712_akm4xxx_build_controls(ice); 443fdd4bb49STakashi Iwai if (err < 0) 444fdd4bb49STakashi Iwai return err; 445d16be8edSPavel Hofman 446d16be8edSPavel Hofman for (i = 0; i < ARRAY_SIZE(juli_mute_controls); i++) { 447d16be8edSPavel Hofman err = snd_ctl_add(ice->card, 448d16be8edSPavel Hofman snd_ctl_new1(&juli_mute_controls[i], ice)); 449d16be8edSPavel Hofman if (err < 0) 450d16be8edSPavel Hofman return err; 451d16be8edSPavel Hofman } 452d16be8edSPavel Hofman /* Create virtual master control */ 453d16be8edSPavel Hofman vmaster = snd_ctl_make_virtual_master("Master Playback Volume", 454d16be8edSPavel Hofman juli_master_db_scale); 455d16be8edSPavel Hofman if (!vmaster) 456d16be8edSPavel Hofman return -ENOMEM; 4579ab0cb30STakashi Iwai add_followers(ice->card, vmaster, follower_vols); 458d16be8edSPavel Hofman err = snd_ctl_add(ice->card, vmaster); 459d16be8edSPavel Hofman if (err < 0) 460d16be8edSPavel Hofman return err; 461d16be8edSPavel Hofman 462fdd4bb49STakashi Iwai /* only capture SPDIF over AK4114 */ 463387417b5SSudip Mukherjee return snd_ak4114_build(spec->ak4114, NULL, 464d16be8edSPavel Hofman ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); 4651da177e4SLinus Torvalds } 4661da177e4SLinus Torvalds 4671da177e4SLinus Torvalds /* 46850d40f18SAleksey Kunitskiy * suspend/resume 46950d40f18SAleksey Kunitskiy * */ 47050d40f18SAleksey Kunitskiy 471c7561cd8STakashi Iwai #ifdef CONFIG_PM_SLEEP 47250d40f18SAleksey Kunitskiy static int juli_resume(struct snd_ice1712 *ice) 47350d40f18SAleksey Kunitskiy { 47450d40f18SAleksey Kunitskiy struct snd_akm4xxx *ak = ice->akm; 47550d40f18SAleksey Kunitskiy struct juli_spec *spec = ice->spec; 47650d40f18SAleksey Kunitskiy /* akm4358 un-reset, un-mute */ 47750d40f18SAleksey Kunitskiy snd_akm4xxx_reset(ak, 0); 47850d40f18SAleksey Kunitskiy /* reinit ak4114 */ 4791293617cSTakashi Iwai snd_ak4114_resume(spec->ak4114); 48050d40f18SAleksey Kunitskiy return 0; 48150d40f18SAleksey Kunitskiy } 48250d40f18SAleksey Kunitskiy 48350d40f18SAleksey Kunitskiy static int juli_suspend(struct snd_ice1712 *ice) 48450d40f18SAleksey Kunitskiy { 48550d40f18SAleksey Kunitskiy struct snd_akm4xxx *ak = ice->akm; 4861293617cSTakashi Iwai struct juli_spec *spec = ice->spec; 48750d40f18SAleksey Kunitskiy /* akm4358 reset and soft-mute */ 48850d40f18SAleksey Kunitskiy snd_akm4xxx_reset(ak, 1); 4891293617cSTakashi Iwai snd_ak4114_suspend(spec->ak4114); 49050d40f18SAleksey Kunitskiy return 0; 49150d40f18SAleksey Kunitskiy } 49250d40f18SAleksey Kunitskiy #endif 49350d40f18SAleksey Kunitskiy 49450d40f18SAleksey Kunitskiy /* 4951da177e4SLinus Torvalds * initialize the chip 4961da177e4SLinus Torvalds */ 497d16be8edSPavel Hofman 498d16be8edSPavel Hofman static inline int juli_is_spdif_master(struct snd_ice1712 *ice) 499d16be8edSPavel Hofman { 500d16be8edSPavel Hofman return (ice->gpio.get_data(ice) & GPIO_INTERNAL_CLOCK) ? 0 : 1; 501d16be8edSPavel Hofman } 502d16be8edSPavel Hofman 503d16be8edSPavel Hofman static unsigned int juli_get_rate(struct snd_ice1712 *ice) 504d16be8edSPavel Hofman { 505d16be8edSPavel Hofman int i; 506d16be8edSPavel Hofman unsigned char result; 507d16be8edSPavel Hofman 508d16be8edSPavel Hofman result = ice->gpio.get_data(ice) & GPIO_RATE_MASK; 509d16be8edSPavel Hofman for (i = 0; i < ARRAY_SIZE(gpio_vals); i++) 510d16be8edSPavel Hofman if (gpio_vals[i] == result) 511d16be8edSPavel Hofman return juli_rates[i]; 512d16be8edSPavel Hofman return 0; 513d16be8edSPavel Hofman } 514d16be8edSPavel Hofman 515d16be8edSPavel Hofman /* setting new rate */ 516d16be8edSPavel Hofman static void juli_set_rate(struct snd_ice1712 *ice, unsigned int rate) 517d16be8edSPavel Hofman { 518d16be8edSPavel Hofman unsigned int old, new; 519d16be8edSPavel Hofman unsigned char val; 520d16be8edSPavel Hofman 521d16be8edSPavel Hofman old = ice->gpio.get_data(ice); 522d16be8edSPavel Hofman new = (old & ~GPIO_RATE_MASK) | get_gpio_val(rate); 5236dfb5affSTakashi Iwai /* dev_dbg(ice->card->dev, "JULI - set_rate: old %x, new %x\n", 524d16be8edSPavel Hofman old & GPIO_RATE_MASK, 525d16be8edSPavel Hofman new & GPIO_RATE_MASK); */ 526d16be8edSPavel Hofman 527d16be8edSPavel Hofman ice->gpio.set_data(ice, new); 528d16be8edSPavel Hofman /* switching to external clock - supplied by external circuits */ 529d16be8edSPavel Hofman val = inb(ICEMT1724(ice, RATE)); 530d16be8edSPavel Hofman outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE)); 531d16be8edSPavel Hofman } 532d16be8edSPavel Hofman 533d16be8edSPavel Hofman static inline unsigned char juli_set_mclk(struct snd_ice1712 *ice, 534d16be8edSPavel Hofman unsigned int rate) 535d16be8edSPavel Hofman { 536d16be8edSPavel Hofman /* no change in master clock */ 537d16be8edSPavel Hofman return 0; 538d16be8edSPavel Hofman } 539d16be8edSPavel Hofman 540d16be8edSPavel Hofman /* setting clock to external - SPDIF */ 5411ff97cb9SPavel Hofman static int juli_set_spdif_clock(struct snd_ice1712 *ice, int type) 542d16be8edSPavel Hofman { 543d16be8edSPavel Hofman unsigned int old; 544d16be8edSPavel Hofman old = ice->gpio.get_data(ice); 545d16be8edSPavel Hofman /* external clock (= 0), multiply 1x, 48kHz */ 546d16be8edSPavel Hofman ice->gpio.set_data(ice, (old & ~GPIO_RATE_MASK) | GPIO_MULTI_1X | 547d16be8edSPavel Hofman GPIO_FREQ_48KHZ); 5481ff97cb9SPavel Hofman return 0; 549d16be8edSPavel Hofman } 550d16be8edSPavel Hofman 551d16be8edSPavel Hofman /* Called when ak4114 detects change in the input SPDIF stream */ 552d16be8edSPavel Hofman static void juli_ak4114_change(struct ak4114 *ak4114, unsigned char c0, 553d16be8edSPavel Hofman unsigned char c1) 554d16be8edSPavel Hofman { 555d16be8edSPavel Hofman struct snd_ice1712 *ice = ak4114->change_callback_private; 556d16be8edSPavel Hofman int rate; 557d16be8edSPavel Hofman if (ice->is_spdif_master(ice) && c1) { 558d16be8edSPavel Hofman /* only for SPDIF master mode, rate was changed */ 559d16be8edSPavel Hofman rate = snd_ak4114_external_rate(ak4114); 5606dfb5affSTakashi Iwai /* dev_dbg(ice->card->dev, "ak4114 - input rate changed to %d\n", 561d16be8edSPavel Hofman rate); */ 562d16be8edSPavel Hofman juli_akm_set_rate_val(ice->akm, rate); 563d16be8edSPavel Hofman } 564d16be8edSPavel Hofman } 565d16be8edSPavel Hofman 566e23e7a14SBill Pemberton static int juli_init(struct snd_ice1712 *ice) 5671da177e4SLinus Torvalds { 568517400cbSTakashi Iwai static const unsigned char ak4114_init_vals[] = { 569cc67b7f7SVedran Miletic /* AK4117_REG_PWRDN */ AK4114_RST | AK4114_PWN | 570cc67b7f7SVedran Miletic AK4114_OCKS0 | AK4114_OCKS1, 5711da177e4SLinus Torvalds /* AK4114_REQ_FORMAT */ AK4114_DIF_I24I2S, 5721da177e4SLinus Torvalds /* AK4114_REG_IO0 */ AK4114_TX1E, 573cc67b7f7SVedran Miletic /* AK4114_REG_IO1 */ AK4114_EFH_1024 | AK4114_DIT | 574cc67b7f7SVedran Miletic AK4114_IPS(1), 5751da177e4SLinus Torvalds /* AK4114_REG_INT0_MASK */ 0, 5761da177e4SLinus Torvalds /* AK4114_REG_INT1_MASK */ 0 5771da177e4SLinus Torvalds }; 578517400cbSTakashi Iwai static const unsigned char ak4114_init_txcsb[] = { 5791da177e4SLinus Torvalds 0x41, 0x02, 0x2c, 0x00, 0x00 5801da177e4SLinus Torvalds }; 5811da177e4SLinus Torvalds int err; 5827cda8ba9STakashi Iwai struct juli_spec *spec; 583ab0c7d72STakashi Iwai struct snd_akm4xxx *ak; 5841da177e4SLinus Torvalds 5857cda8ba9STakashi Iwai spec = kzalloc(sizeof(*spec), GFP_KERNEL); 5867cda8ba9STakashi Iwai if (!spec) 5877cda8ba9STakashi Iwai return -ENOMEM; 5887cda8ba9STakashi Iwai ice->spec = spec; 5897cda8ba9STakashi Iwai 5901da177e4SLinus Torvalds err = snd_ak4114_create(ice->card, 5911da177e4SLinus Torvalds juli_ak4114_read, 5921da177e4SLinus Torvalds juli_ak4114_write, 5931da177e4SLinus Torvalds ak4114_init_vals, ak4114_init_txcsb, 5947cda8ba9STakashi Iwai ice, &spec->ak4114); 5951da177e4SLinus Torvalds if (err < 0) 5961da177e4SLinus Torvalds return err; 597d16be8edSPavel Hofman /* callback for codecs rate setting */ 598d16be8edSPavel Hofman spec->ak4114->change_callback = juli_ak4114_change; 599d16be8edSPavel Hofman spec->ak4114->change_callback_private = ice; 600d16be8edSPavel Hofman /* AK4114 in Juli can detect external rate correctly */ 601d16be8edSPavel Hofman spec->ak4114->check_flags = 0; 6021da177e4SLinus Torvalds 603fd6715e5SJaroslav Kysela #if 0 604cc67b7f7SVedran Miletic /* 605cc67b7f7SVedran Miletic * it seems that the analog doughter board detection does not work reliably, so 606cc67b7f7SVedran Miletic * force the analog flag; it should be very rare (if ever) to come at Juli@ 607cc67b7f7SVedran Miletic * used without the analog daughter board 608cc67b7f7SVedran Miletic */ 6097cda8ba9STakashi Iwai spec->analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1; 610fd6715e5SJaroslav Kysela #else 6117cda8ba9STakashi Iwai spec->analog = 1; 612fd6715e5SJaroslav Kysela #endif 6131da177e4SLinus Torvalds 6147cda8ba9STakashi Iwai if (spec->analog) { 6156dfb5affSTakashi Iwai dev_info(ice->card->dev, "juli@: analog I/O detected\n"); 6161da177e4SLinus Torvalds ice->num_total_dacs = 2; 6171da177e4SLinus Torvalds ice->num_total_adcs = 2; 6181da177e4SLinus Torvalds 619cc67b7f7SVedran Miletic ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); 620cc67b7f7SVedran Miletic ak = ice->akm; 6211da177e4SLinus Torvalds if (!ak) 6221da177e4SLinus Torvalds return -ENOMEM; 6231da177e4SLinus Torvalds ice->akm_codecs = 1; 624cc67b7f7SVedran Miletic err = snd_ice1712_akm4xxx_init(ak, &akm_juli_dac, NULL, ice); 625cc67b7f7SVedran Miletic if (err < 0) 6261da177e4SLinus Torvalds return err; 6271da177e4SLinus Torvalds } 6281da177e4SLinus Torvalds 629d16be8edSPavel Hofman /* juli is clocked by Xilinx array */ 630d16be8edSPavel Hofman ice->hw_rates = &juli_rates_info; 631d16be8edSPavel Hofman ice->is_spdif_master = juli_is_spdif_master; 632d16be8edSPavel Hofman ice->get_rate = juli_get_rate; 633d16be8edSPavel Hofman ice->set_rate = juli_set_rate; 634d16be8edSPavel Hofman ice->set_mclk = juli_set_mclk; 635d16be8edSPavel Hofman ice->set_spdif_clock = juli_set_spdif_clock; 636d16be8edSPavel Hofman 637c93f5a1eSTakashi Iwai ice->spdif.ops.open = juli_spdif_in_open; 63850d40f18SAleksey Kunitskiy 639c7561cd8STakashi Iwai #ifdef CONFIG_PM_SLEEP 64050d40f18SAleksey Kunitskiy ice->pm_resume = juli_resume; 64150d40f18SAleksey Kunitskiy ice->pm_suspend = juli_suspend; 64250d40f18SAleksey Kunitskiy ice->pm_suspend_enabled = 1; 64350d40f18SAleksey Kunitskiy #endif 64450d40f18SAleksey Kunitskiy 6451da177e4SLinus Torvalds return 0; 6461da177e4SLinus Torvalds } 6471da177e4SLinus Torvalds 6481da177e4SLinus Torvalds 6491da177e4SLinus Torvalds /* 6501da177e4SLinus Torvalds * Juli@ boards don't provide the EEPROM data except for the vendor IDs. 6511da177e4SLinus Torvalds * hence the driver needs to sets up it properly. 6521da177e4SLinus Torvalds */ 6531da177e4SLinus Torvalds 654f16a4e96STakashi Iwai static const unsigned char juli_eeprom[] = { 655d16be8edSPavel Hofman [ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, 1xADC, 1xDACs, 656d16be8edSPavel Hofman SPDIF in */ 657189bc171STakashi Iwai [ICE_EEP2_ACLINK] = 0x80, /* I2S */ 658189bc171STakashi Iwai [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */ 659189bc171STakashi Iwai [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ 660d16be8edSPavel Hofman [ICE_EEP2_GPIO_DIR] = 0x9f, /* 5, 6:inputs; 7, 4-0 outputs*/ 661189bc171STakashi Iwai [ICE_EEP2_GPIO_DIR1] = 0xff, 662189bc171STakashi Iwai [ICE_EEP2_GPIO_DIR2] = 0x7f, 663d16be8edSPavel Hofman [ICE_EEP2_GPIO_MASK] = 0x60, /* 5, 6: locked; 7, 4-0 writable */ 664d16be8edSPavel Hofman [ICE_EEP2_GPIO_MASK1] = 0x00, /* 0-7 writable */ 665189bc171STakashi Iwai [ICE_EEP2_GPIO_MASK2] = 0x7f, 666d16be8edSPavel Hofman [ICE_EEP2_GPIO_STATE] = GPIO_FREQ_48KHZ | GPIO_MULTI_1X | 667d16be8edSPavel Hofman GPIO_INTERNAL_CLOCK, /* internal clock, multiple 1x, 48kHz*/ 668d16be8edSPavel Hofman [ICE_EEP2_GPIO_STATE1] = 0x00, /* unmuted */ 669189bc171STakashi Iwai [ICE_EEP2_GPIO_STATE2] = 0x00, 6701da177e4SLinus Torvalds }; 6711da177e4SLinus Torvalds 6721da177e4SLinus Torvalds /* entry point */ 673e23e7a14SBill Pemberton struct snd_ice1712_card_info snd_vt1724_juli_cards[] = { 6741da177e4SLinus Torvalds { 6751da177e4SLinus Torvalds .subvendor = VT1724_SUBDEVICE_JULI, 6761da177e4SLinus Torvalds .name = "ESI Juli@", 6771da177e4SLinus Torvalds .model = "juli", 6781da177e4SLinus Torvalds .chip_init = juli_init, 6791da177e4SLinus Torvalds .build_controls = juli_add_controls, 6801da177e4SLinus Torvalds .eeprom_size = sizeof(juli_eeprom), 6811da177e4SLinus Torvalds .eeprom_data = juli_eeprom, 6821da177e4SLinus Torvalds }, 6831da177e4SLinus Torvalds { } /* terminator */ 6841da177e4SLinus Torvalds }; 685