162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ALSA driver for ICEnsemble VT1724 (Envy24HT) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Lowlevel functions for ESI Juli@ cards 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz> 862306a36Sopenharmony_ci * 2008 Pavel Hofman <dustin@seznam.cz> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include <sound/core.h> 1762306a36Sopenharmony_ci#include <sound/tlv.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "ice1712.h" 2062306a36Sopenharmony_ci#include "envy24ht.h" 2162306a36Sopenharmony_ci#include "juli.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct juli_spec { 2462306a36Sopenharmony_ci struct ak4114 *ak4114; 2562306a36Sopenharmony_ci unsigned int analog:1; 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * chip addresses on I2C bus 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci#define AK4114_ADDR 0x20 /* S/PDIF receiver */ 3262306a36Sopenharmony_ci#define AK4358_ADDR 0x22 /* DAC */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Juli does not use the standard ICE1724 clock scheme. Juli's ice1724 chip is 3662306a36Sopenharmony_ci * supplied by external clock provided by Xilinx array and MK73-1 PLL frequency 3762306a36Sopenharmony_ci * multiplier. Actual frequency is set by ice1724 GPIOs hooked to the Xilinx. 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * The clock circuitry is supplied by the two ice1724 crystals. This 4062306a36Sopenharmony_ci * arrangement allows to generate independent clock signal for AK4114's input 4162306a36Sopenharmony_ci * rate detection circuit. As a result, Juli, unlike most other 4262306a36Sopenharmony_ci * ice1724+ak4114-based cards, detects spdif input rate correctly. 4362306a36Sopenharmony_ci * This fact is applied in the driver, allowing to modify PCM stream rate 4462306a36Sopenharmony_ci * parameter according to the actual input rate. 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * Juli uses the remaining three stereo-channels of its DAC to optionally 4762306a36Sopenharmony_ci * monitor analog input, digital input, and digital output. The corresponding 4862306a36Sopenharmony_ci * I2S signals are routed by Xilinx, controlled by GPIOs. 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * The master mute is implemented using output muting transistors (GPIO) in 5162306a36Sopenharmony_ci * combination with smuting the DAC. 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * The card itself has no HW master volume control, implemented using the 5462306a36Sopenharmony_ci * vmaster control. 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * TODO: 5762306a36Sopenharmony_ci * researching and fixing the input monitors 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * GPIO pins 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci#define GPIO_FREQ_MASK (3<<0) 6462306a36Sopenharmony_ci#define GPIO_FREQ_32KHZ (0<<0) 6562306a36Sopenharmony_ci#define GPIO_FREQ_44KHZ (1<<0) 6662306a36Sopenharmony_ci#define GPIO_FREQ_48KHZ (2<<0) 6762306a36Sopenharmony_ci#define GPIO_MULTI_MASK (3<<2) 6862306a36Sopenharmony_ci#define GPIO_MULTI_4X (0<<2) 6962306a36Sopenharmony_ci#define GPIO_MULTI_2X (1<<2) 7062306a36Sopenharmony_ci#define GPIO_MULTI_1X (2<<2) /* also external */ 7162306a36Sopenharmony_ci#define GPIO_MULTI_HALF (3<<2) 7262306a36Sopenharmony_ci#define GPIO_INTERNAL_CLOCK (1<<4) /* 0 = external, 1 = internal */ 7362306a36Sopenharmony_ci#define GPIO_CLOCK_MASK (1<<4) 7462306a36Sopenharmony_ci#define GPIO_ANALOG_PRESENT (1<<5) /* RO only: 0 = present */ 7562306a36Sopenharmony_ci#define GPIO_RXMCLK_SEL (1<<7) /* must be 0 */ 7662306a36Sopenharmony_ci#define GPIO_AK5385A_CKS0 (1<<8) 7762306a36Sopenharmony_ci#define GPIO_AK5385A_DFS1 (1<<9) 7862306a36Sopenharmony_ci#define GPIO_AK5385A_DFS0 (1<<10) 7962306a36Sopenharmony_ci#define GPIO_DIGOUT_MONITOR (1<<11) /* 1 = active */ 8062306a36Sopenharmony_ci#define GPIO_DIGIN_MONITOR (1<<12) /* 1 = active */ 8162306a36Sopenharmony_ci#define GPIO_ANAIN_MONITOR (1<<13) /* 1 = active */ 8262306a36Sopenharmony_ci#define GPIO_AK5385A_CKS1 (1<<14) /* must be 0 */ 8362306a36Sopenharmony_ci#define GPIO_MUTE_CONTROL (1<<15) /* output mute, 1 = muted */ 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define GPIO_RATE_MASK (GPIO_FREQ_MASK | GPIO_MULTI_MASK | \ 8662306a36Sopenharmony_ci GPIO_CLOCK_MASK) 8762306a36Sopenharmony_ci#define GPIO_AK5385A_MASK (GPIO_AK5385A_CKS0 | GPIO_AK5385A_DFS0 | \ 8862306a36Sopenharmony_ci GPIO_AK5385A_DFS1 | GPIO_AK5385A_CKS1) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define JULI_PCM_RATE (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ 9162306a36Sopenharmony_ci SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ 9262306a36Sopenharmony_ci SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ 9362306a36Sopenharmony_ci SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \ 9462306a36Sopenharmony_ci SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define GPIO_RATE_16000 (GPIO_FREQ_32KHZ | GPIO_MULTI_HALF | \ 9762306a36Sopenharmony_ci GPIO_INTERNAL_CLOCK) 9862306a36Sopenharmony_ci#define GPIO_RATE_22050 (GPIO_FREQ_44KHZ | GPIO_MULTI_HALF | \ 9962306a36Sopenharmony_ci GPIO_INTERNAL_CLOCK) 10062306a36Sopenharmony_ci#define GPIO_RATE_24000 (GPIO_FREQ_48KHZ | GPIO_MULTI_HALF | \ 10162306a36Sopenharmony_ci GPIO_INTERNAL_CLOCK) 10262306a36Sopenharmony_ci#define GPIO_RATE_32000 (GPIO_FREQ_32KHZ | GPIO_MULTI_1X | \ 10362306a36Sopenharmony_ci GPIO_INTERNAL_CLOCK) 10462306a36Sopenharmony_ci#define GPIO_RATE_44100 (GPIO_FREQ_44KHZ | GPIO_MULTI_1X | \ 10562306a36Sopenharmony_ci GPIO_INTERNAL_CLOCK) 10662306a36Sopenharmony_ci#define GPIO_RATE_48000 (GPIO_FREQ_48KHZ | GPIO_MULTI_1X | \ 10762306a36Sopenharmony_ci GPIO_INTERNAL_CLOCK) 10862306a36Sopenharmony_ci#define GPIO_RATE_64000 (GPIO_FREQ_32KHZ | GPIO_MULTI_2X | \ 10962306a36Sopenharmony_ci GPIO_INTERNAL_CLOCK) 11062306a36Sopenharmony_ci#define GPIO_RATE_88200 (GPIO_FREQ_44KHZ | GPIO_MULTI_2X | \ 11162306a36Sopenharmony_ci GPIO_INTERNAL_CLOCK) 11262306a36Sopenharmony_ci#define GPIO_RATE_96000 (GPIO_FREQ_48KHZ | GPIO_MULTI_2X | \ 11362306a36Sopenharmony_ci GPIO_INTERNAL_CLOCK) 11462306a36Sopenharmony_ci#define GPIO_RATE_176400 (GPIO_FREQ_44KHZ | GPIO_MULTI_4X | \ 11562306a36Sopenharmony_ci GPIO_INTERNAL_CLOCK) 11662306a36Sopenharmony_ci#define GPIO_RATE_192000 (GPIO_FREQ_48KHZ | GPIO_MULTI_4X | \ 11762306a36Sopenharmony_ci GPIO_INTERNAL_CLOCK) 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* 12062306a36Sopenharmony_ci * Initial setup of the conversion array GPIO <-> rate 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistatic const unsigned int juli_rates[] = { 12362306a36Sopenharmony_ci 16000, 22050, 24000, 32000, 12462306a36Sopenharmony_ci 44100, 48000, 64000, 88200, 12562306a36Sopenharmony_ci 96000, 176400, 192000, 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic const unsigned int gpio_vals[] = { 12962306a36Sopenharmony_ci GPIO_RATE_16000, GPIO_RATE_22050, GPIO_RATE_24000, GPIO_RATE_32000, 13062306a36Sopenharmony_ci GPIO_RATE_44100, GPIO_RATE_48000, GPIO_RATE_64000, GPIO_RATE_88200, 13162306a36Sopenharmony_ci GPIO_RATE_96000, GPIO_RATE_176400, GPIO_RATE_192000, 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list juli_rates_info = { 13562306a36Sopenharmony_ci .count = ARRAY_SIZE(juli_rates), 13662306a36Sopenharmony_ci .list = juli_rates, 13762306a36Sopenharmony_ci .mask = 0, 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int get_gpio_val(int rate) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci int i; 14362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(juli_rates); i++) 14462306a36Sopenharmony_ci if (juli_rates[i] == rate) 14562306a36Sopenharmony_ci return gpio_vals[i]; 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void juli_ak4114_write(void *private_data, unsigned char reg, 15062306a36Sopenharmony_ci unsigned char val) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci snd_vt1724_write_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR, 15362306a36Sopenharmony_ci reg, val); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic unsigned char juli_ak4114_read(void *private_data, unsigned char reg) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data, 15962306a36Sopenharmony_ci AK4114_ADDR, reg); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* 16362306a36Sopenharmony_ci * If SPDIF capture and slaved to SPDIF-IN, setting runtime rate 16462306a36Sopenharmony_ci * to the external rate 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_cistatic void juli_spdif_in_open(struct snd_ice1712 *ice, 16762306a36Sopenharmony_ci struct snd_pcm_substream *substream) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct juli_spec *spec = ice->spec; 17062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 17162306a36Sopenharmony_ci int rate; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || 17462306a36Sopenharmony_ci !ice->is_spdif_master(ice)) 17562306a36Sopenharmony_ci return; 17662306a36Sopenharmony_ci rate = snd_ak4114_external_rate(spec->ak4114); 17762306a36Sopenharmony_ci if (rate >= runtime->hw.rate_min && rate <= runtime->hw.rate_max) { 17862306a36Sopenharmony_ci runtime->hw.rate_min = rate; 17962306a36Sopenharmony_ci runtime->hw.rate_max = rate; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* 18462306a36Sopenharmony_ci * AK4358 section 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void juli_akm_lock(struct snd_akm4xxx *ak, int chip) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic void juli_akm_unlock(struct snd_akm4xxx *ak, int chip) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void juli_akm_write(struct snd_akm4xxx *ak, int chip, 19662306a36Sopenharmony_ci unsigned char addr, unsigned char data) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct snd_ice1712 *ice = ak->private_data[0]; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (snd_BUG_ON(chip)) 20162306a36Sopenharmony_ci return; 20262306a36Sopenharmony_ci snd_vt1724_write_i2c(ice, AK4358_ADDR, addr, data); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/* 20662306a36Sopenharmony_ci * change the rate of envy24HT, AK4358, AK5385 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_cistatic void juli_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci unsigned char old, tmp, ak4358_dfs; 21162306a36Sopenharmony_ci unsigned int ak5385_pins, old_gpio, new_gpio; 21262306a36Sopenharmony_ci struct snd_ice1712 *ice = ak->private_data[0]; 21362306a36Sopenharmony_ci struct juli_spec *spec = ice->spec; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (rate == 0) /* no hint - S/PDIF input is master or the new spdif 21662306a36Sopenharmony_ci input rate undetected, simply return */ 21762306a36Sopenharmony_ci return; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* adjust DFS on codecs */ 22062306a36Sopenharmony_ci if (rate > 96000) { 22162306a36Sopenharmony_ci ak4358_dfs = 2; 22262306a36Sopenharmony_ci ak5385_pins = GPIO_AK5385A_DFS1 | GPIO_AK5385A_CKS0; 22362306a36Sopenharmony_ci } else if (rate > 48000) { 22462306a36Sopenharmony_ci ak4358_dfs = 1; 22562306a36Sopenharmony_ci ak5385_pins = GPIO_AK5385A_DFS0; 22662306a36Sopenharmony_ci } else { 22762306a36Sopenharmony_ci ak4358_dfs = 0; 22862306a36Sopenharmony_ci ak5385_pins = 0; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci /* AK5385 first, since it requires cold reset affecting both codecs */ 23162306a36Sopenharmony_ci old_gpio = ice->gpio.get_data(ice); 23262306a36Sopenharmony_ci new_gpio = (old_gpio & ~GPIO_AK5385A_MASK) | ak5385_pins; 23362306a36Sopenharmony_ci /* dev_dbg(ice->card->dev, "JULI - ak5385 set_rate_val: new gpio 0x%x\n", 23462306a36Sopenharmony_ci new_gpio); */ 23562306a36Sopenharmony_ci ice->gpio.set_data(ice, new_gpio); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* cold reset */ 23862306a36Sopenharmony_ci old = inb(ICEMT1724(ice, AC97_CMD)); 23962306a36Sopenharmony_ci outb(old | VT1724_AC97_COLD, ICEMT1724(ice, AC97_CMD)); 24062306a36Sopenharmony_ci udelay(1); 24162306a36Sopenharmony_ci outb(old & ~VT1724_AC97_COLD, ICEMT1724(ice, AC97_CMD)); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* AK4358 */ 24462306a36Sopenharmony_ci /* set new value, reset DFS */ 24562306a36Sopenharmony_ci tmp = snd_akm4xxx_get(ak, 0, 2); 24662306a36Sopenharmony_ci snd_akm4xxx_reset(ak, 1); 24762306a36Sopenharmony_ci tmp = snd_akm4xxx_get(ak, 0, 2); 24862306a36Sopenharmony_ci tmp &= ~(0x03 << 4); 24962306a36Sopenharmony_ci tmp |= ak4358_dfs << 4; 25062306a36Sopenharmony_ci snd_akm4xxx_set(ak, 0, 2, tmp); 25162306a36Sopenharmony_ci snd_akm4xxx_reset(ak, 0); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* reinit ak4114 */ 25462306a36Sopenharmony_ci snd_ak4114_reinit(spec->ak4114); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci#define AK_DAC(xname, xch) { .name = xname, .num_channels = xch } 25862306a36Sopenharmony_ci#define PCM_VOLUME "PCM Playback Volume" 25962306a36Sopenharmony_ci#define MONITOR_AN_IN_VOLUME "Monitor Analog In Volume" 26062306a36Sopenharmony_ci#define MONITOR_DIG_IN_VOLUME "Monitor Digital In Volume" 26162306a36Sopenharmony_ci#define MONITOR_DIG_OUT_VOLUME "Monitor Digital Out Volume" 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic const struct snd_akm4xxx_dac_channel juli_dac[] = { 26462306a36Sopenharmony_ci AK_DAC(PCM_VOLUME, 2), 26562306a36Sopenharmony_ci AK_DAC(MONITOR_AN_IN_VOLUME, 2), 26662306a36Sopenharmony_ci AK_DAC(MONITOR_DIG_OUT_VOLUME, 2), 26762306a36Sopenharmony_ci AK_DAC(MONITOR_DIG_IN_VOLUME, 2), 26862306a36Sopenharmony_ci}; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic const struct snd_akm4xxx akm_juli_dac = { 27262306a36Sopenharmony_ci .type = SND_AK4358, 27362306a36Sopenharmony_ci .num_dacs = 8, /* DAC1 - analog out 27462306a36Sopenharmony_ci DAC2 - analog in monitor 27562306a36Sopenharmony_ci DAC3 - digital out monitor 27662306a36Sopenharmony_ci DAC4 - digital in monitor 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci .ops = { 27962306a36Sopenharmony_ci .lock = juli_akm_lock, 28062306a36Sopenharmony_ci .unlock = juli_akm_unlock, 28162306a36Sopenharmony_ci .write = juli_akm_write, 28262306a36Sopenharmony_ci .set_rate_val = juli_akm_set_rate_val 28362306a36Sopenharmony_ci }, 28462306a36Sopenharmony_ci .dac_info = juli_dac, 28562306a36Sopenharmony_ci}; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci#define juli_mute_info snd_ctl_boolean_mono_info 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int juli_mute_get(struct snd_kcontrol *kcontrol, 29062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 29362306a36Sopenharmony_ci unsigned int val; 29462306a36Sopenharmony_ci val = ice->gpio.get_data(ice) & (unsigned int) kcontrol->private_value; 29562306a36Sopenharmony_ci if (kcontrol->private_value == GPIO_MUTE_CONTROL) 29662306a36Sopenharmony_ci /* val 0 = signal on */ 29762306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (val) ? 0 : 1; 29862306a36Sopenharmony_ci else 29962306a36Sopenharmony_ci /* val 1 = signal on */ 30062306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (val) ? 1 : 0; 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int juli_mute_put(struct snd_kcontrol *kcontrol, 30562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 30862306a36Sopenharmony_ci unsigned int old_gpio, new_gpio; 30962306a36Sopenharmony_ci old_gpio = ice->gpio.get_data(ice); 31062306a36Sopenharmony_ci if (ucontrol->value.integer.value[0]) { 31162306a36Sopenharmony_ci /* unmute */ 31262306a36Sopenharmony_ci if (kcontrol->private_value == GPIO_MUTE_CONTROL) { 31362306a36Sopenharmony_ci /* 0 = signal on */ 31462306a36Sopenharmony_ci new_gpio = old_gpio & ~GPIO_MUTE_CONTROL; 31562306a36Sopenharmony_ci /* un-smuting DAC */ 31662306a36Sopenharmony_ci snd_akm4xxx_write(ice->akm, 0, 0x01, 0x01); 31762306a36Sopenharmony_ci } else 31862306a36Sopenharmony_ci /* 1 = signal on */ 31962306a36Sopenharmony_ci new_gpio = old_gpio | 32062306a36Sopenharmony_ci (unsigned int) kcontrol->private_value; 32162306a36Sopenharmony_ci } else { 32262306a36Sopenharmony_ci /* mute */ 32362306a36Sopenharmony_ci if (kcontrol->private_value == GPIO_MUTE_CONTROL) { 32462306a36Sopenharmony_ci /* 1 = signal off */ 32562306a36Sopenharmony_ci new_gpio = old_gpio | GPIO_MUTE_CONTROL; 32662306a36Sopenharmony_ci /* smuting DAC */ 32762306a36Sopenharmony_ci snd_akm4xxx_write(ice->akm, 0, 0x01, 0x03); 32862306a36Sopenharmony_ci } else 32962306a36Sopenharmony_ci /* 0 = signal off */ 33062306a36Sopenharmony_ci new_gpio = old_gpio & 33162306a36Sopenharmony_ci ~((unsigned int) kcontrol->private_value); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci /* dev_dbg(ice->card->dev, 33462306a36Sopenharmony_ci "JULI - mute/unmute: control_value: 0x%x, old_gpio: 0x%x, " 33562306a36Sopenharmony_ci "new_gpio 0x%x\n", 33662306a36Sopenharmony_ci (unsigned int)ucontrol->value.integer.value[0], old_gpio, 33762306a36Sopenharmony_ci new_gpio); */ 33862306a36Sopenharmony_ci if (old_gpio != new_gpio) { 33962306a36Sopenharmony_ci ice->gpio.set_data(ice, new_gpio); 34062306a36Sopenharmony_ci return 1; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci /* no change */ 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic const struct snd_kcontrol_new juli_mute_controls[] = { 34762306a36Sopenharmony_ci { 34862306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 34962306a36Sopenharmony_ci .name = "Master Playback Switch", 35062306a36Sopenharmony_ci .info = juli_mute_info, 35162306a36Sopenharmony_ci .get = juli_mute_get, 35262306a36Sopenharmony_ci .put = juli_mute_put, 35362306a36Sopenharmony_ci .private_value = GPIO_MUTE_CONTROL, 35462306a36Sopenharmony_ci }, 35562306a36Sopenharmony_ci /* Although the following functionality respects the succint NDA'd 35662306a36Sopenharmony_ci * documentation from the card manufacturer, and the same way of 35762306a36Sopenharmony_ci * operation is coded in OSS Juli driver, only Digital Out monitor 35862306a36Sopenharmony_ci * seems to work. Surprisingly, Analog input monitor outputs Digital 35962306a36Sopenharmony_ci * output data. The two are independent, as enabling both doubles 36062306a36Sopenharmony_ci * volume of the monitor sound. 36162306a36Sopenharmony_ci * 36262306a36Sopenharmony_ci * Checking traces on the board suggests the functionality described 36362306a36Sopenharmony_ci * by the manufacturer is correct - I2S from ADC and AK4114 36462306a36Sopenharmony_ci * go to ICE as well as to Xilinx, I2S inputs of DAC2,3,4 (the monitor 36562306a36Sopenharmony_ci * inputs) are fed from Xilinx. 36662306a36Sopenharmony_ci * 36762306a36Sopenharmony_ci * I even checked traces on board and coded a support in driver for 36862306a36Sopenharmony_ci * an alternative possibility - the unused I2S ICE output channels 36962306a36Sopenharmony_ci * switched to HW-IN/SPDIF-IN and providing the monitoring signal to 37062306a36Sopenharmony_ci * the DAC - to no avail. The I2S outputs seem to be unconnected. 37162306a36Sopenharmony_ci * 37262306a36Sopenharmony_ci * The windows driver supports the monitoring correctly. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_ci { 37562306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 37662306a36Sopenharmony_ci .name = "Monitor Analog In Switch", 37762306a36Sopenharmony_ci .info = juli_mute_info, 37862306a36Sopenharmony_ci .get = juli_mute_get, 37962306a36Sopenharmony_ci .put = juli_mute_put, 38062306a36Sopenharmony_ci .private_value = GPIO_ANAIN_MONITOR, 38162306a36Sopenharmony_ci }, 38262306a36Sopenharmony_ci { 38362306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 38462306a36Sopenharmony_ci .name = "Monitor Digital Out Switch", 38562306a36Sopenharmony_ci .info = juli_mute_info, 38662306a36Sopenharmony_ci .get = juli_mute_get, 38762306a36Sopenharmony_ci .put = juli_mute_put, 38862306a36Sopenharmony_ci .private_value = GPIO_DIGOUT_MONITOR, 38962306a36Sopenharmony_ci }, 39062306a36Sopenharmony_ci { 39162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 39262306a36Sopenharmony_ci .name = "Monitor Digital In Switch", 39362306a36Sopenharmony_ci .info = juli_mute_info, 39462306a36Sopenharmony_ci .get = juli_mute_get, 39562306a36Sopenharmony_ci .put = juli_mute_put, 39662306a36Sopenharmony_ci .private_value = GPIO_DIGIN_MONITOR, 39762306a36Sopenharmony_ci }, 39862306a36Sopenharmony_ci}; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic const char * const follower_vols[] = { 40162306a36Sopenharmony_ci PCM_VOLUME, 40262306a36Sopenharmony_ci MONITOR_AN_IN_VOLUME, 40362306a36Sopenharmony_ci MONITOR_DIG_IN_VOLUME, 40462306a36Sopenharmony_ci MONITOR_DIG_OUT_VOLUME, 40562306a36Sopenharmony_ci NULL 40662306a36Sopenharmony_ci}; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic 40962306a36Sopenharmony_ciDECLARE_TLV_DB_SCALE(juli_master_db_scale, -6350, 50, 1); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic int juli_add_controls(struct snd_ice1712 *ice) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci struct juli_spec *spec = ice->spec; 41462306a36Sopenharmony_ci int err; 41562306a36Sopenharmony_ci unsigned int i; 41662306a36Sopenharmony_ci struct snd_kcontrol *vmaster; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci err = snd_ice1712_akm4xxx_build_controls(ice); 41962306a36Sopenharmony_ci if (err < 0) 42062306a36Sopenharmony_ci return err; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(juli_mute_controls); i++) { 42362306a36Sopenharmony_ci err = snd_ctl_add(ice->card, 42462306a36Sopenharmony_ci snd_ctl_new1(&juli_mute_controls[i], ice)); 42562306a36Sopenharmony_ci if (err < 0) 42662306a36Sopenharmony_ci return err; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci /* Create virtual master control */ 42962306a36Sopenharmony_ci vmaster = snd_ctl_make_virtual_master("Master Playback Volume", 43062306a36Sopenharmony_ci juli_master_db_scale); 43162306a36Sopenharmony_ci if (!vmaster) 43262306a36Sopenharmony_ci return -ENOMEM; 43362306a36Sopenharmony_ci err = snd_ctl_add(ice->card, vmaster); 43462306a36Sopenharmony_ci if (err < 0) 43562306a36Sopenharmony_ci return err; 43662306a36Sopenharmony_ci err = snd_ctl_add_followers(ice->card, vmaster, follower_vols); 43762306a36Sopenharmony_ci if (err < 0) 43862306a36Sopenharmony_ci return err; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* only capture SPDIF over AK4114 */ 44162306a36Sopenharmony_ci return snd_ak4114_build(spec->ak4114, NULL, 44262306a36Sopenharmony_ci ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/* 44662306a36Sopenharmony_ci * suspend/resume 44762306a36Sopenharmony_ci * */ 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 45062306a36Sopenharmony_cistatic int juli_resume(struct snd_ice1712 *ice) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct snd_akm4xxx *ak = ice->akm; 45362306a36Sopenharmony_ci struct juli_spec *spec = ice->spec; 45462306a36Sopenharmony_ci /* akm4358 un-reset, un-mute */ 45562306a36Sopenharmony_ci snd_akm4xxx_reset(ak, 0); 45662306a36Sopenharmony_ci /* reinit ak4114 */ 45762306a36Sopenharmony_ci snd_ak4114_resume(spec->ak4114); 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int juli_suspend(struct snd_ice1712 *ice) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct snd_akm4xxx *ak = ice->akm; 46462306a36Sopenharmony_ci struct juli_spec *spec = ice->spec; 46562306a36Sopenharmony_ci /* akm4358 reset and soft-mute */ 46662306a36Sopenharmony_ci snd_akm4xxx_reset(ak, 1); 46762306a36Sopenharmony_ci snd_ak4114_suspend(spec->ak4114); 46862306a36Sopenharmony_ci return 0; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci#endif 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/* 47362306a36Sopenharmony_ci * initialize the chip 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic inline int juli_is_spdif_master(struct snd_ice1712 *ice) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci return (ice->gpio.get_data(ice) & GPIO_INTERNAL_CLOCK) ? 0 : 1; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic unsigned int juli_get_rate(struct snd_ice1712 *ice) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci int i; 48462306a36Sopenharmony_ci unsigned char result; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci result = ice->gpio.get_data(ice) & GPIO_RATE_MASK; 48762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(gpio_vals); i++) 48862306a36Sopenharmony_ci if (gpio_vals[i] == result) 48962306a36Sopenharmony_ci return juli_rates[i]; 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci/* setting new rate */ 49462306a36Sopenharmony_cistatic void juli_set_rate(struct snd_ice1712 *ice, unsigned int rate) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci unsigned int old, new; 49762306a36Sopenharmony_ci unsigned char val; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci old = ice->gpio.get_data(ice); 50062306a36Sopenharmony_ci new = (old & ~GPIO_RATE_MASK) | get_gpio_val(rate); 50162306a36Sopenharmony_ci /* dev_dbg(ice->card->dev, "JULI - set_rate: old %x, new %x\n", 50262306a36Sopenharmony_ci old & GPIO_RATE_MASK, 50362306a36Sopenharmony_ci new & GPIO_RATE_MASK); */ 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci ice->gpio.set_data(ice, new); 50662306a36Sopenharmony_ci /* switching to external clock - supplied by external circuits */ 50762306a36Sopenharmony_ci val = inb(ICEMT1724(ice, RATE)); 50862306a36Sopenharmony_ci outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE)); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic inline unsigned char juli_set_mclk(struct snd_ice1712 *ice, 51262306a36Sopenharmony_ci unsigned int rate) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci /* no change in master clock */ 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci/* setting clock to external - SPDIF */ 51962306a36Sopenharmony_cistatic int juli_set_spdif_clock(struct snd_ice1712 *ice, int type) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci unsigned int old; 52262306a36Sopenharmony_ci old = ice->gpio.get_data(ice); 52362306a36Sopenharmony_ci /* external clock (= 0), multiply 1x, 48kHz */ 52462306a36Sopenharmony_ci ice->gpio.set_data(ice, (old & ~GPIO_RATE_MASK) | GPIO_MULTI_1X | 52562306a36Sopenharmony_ci GPIO_FREQ_48KHZ); 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci/* Called when ak4114 detects change in the input SPDIF stream */ 53062306a36Sopenharmony_cistatic void juli_ak4114_change(struct ak4114 *ak4114, unsigned char c0, 53162306a36Sopenharmony_ci unsigned char c1) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct snd_ice1712 *ice = ak4114->change_callback_private; 53462306a36Sopenharmony_ci int rate; 53562306a36Sopenharmony_ci if (ice->is_spdif_master(ice) && c1) { 53662306a36Sopenharmony_ci /* only for SPDIF master mode, rate was changed */ 53762306a36Sopenharmony_ci rate = snd_ak4114_external_rate(ak4114); 53862306a36Sopenharmony_ci /* dev_dbg(ice->card->dev, "ak4114 - input rate changed to %d\n", 53962306a36Sopenharmony_ci rate); */ 54062306a36Sopenharmony_ci juli_akm_set_rate_val(ice->akm, rate); 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic int juli_init(struct snd_ice1712 *ice) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci static const unsigned char ak4114_init_vals[] = { 54762306a36Sopenharmony_ci /* AK4117_REG_PWRDN */ AK4114_RST | AK4114_PWN | 54862306a36Sopenharmony_ci AK4114_OCKS0 | AK4114_OCKS1, 54962306a36Sopenharmony_ci /* AK4114_REQ_FORMAT */ AK4114_DIF_I24I2S, 55062306a36Sopenharmony_ci /* AK4114_REG_IO0 */ AK4114_TX1E, 55162306a36Sopenharmony_ci /* AK4114_REG_IO1 */ AK4114_EFH_1024 | AK4114_DIT | 55262306a36Sopenharmony_ci AK4114_IPS(1), 55362306a36Sopenharmony_ci /* AK4114_REG_INT0_MASK */ 0, 55462306a36Sopenharmony_ci /* AK4114_REG_INT1_MASK */ 0 55562306a36Sopenharmony_ci }; 55662306a36Sopenharmony_ci static const unsigned char ak4114_init_txcsb[] = { 55762306a36Sopenharmony_ci 0x41, 0x02, 0x2c, 0x00, 0x00 55862306a36Sopenharmony_ci }; 55962306a36Sopenharmony_ci int err; 56062306a36Sopenharmony_ci struct juli_spec *spec; 56162306a36Sopenharmony_ci struct snd_akm4xxx *ak; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci spec = kzalloc(sizeof(*spec), GFP_KERNEL); 56462306a36Sopenharmony_ci if (!spec) 56562306a36Sopenharmony_ci return -ENOMEM; 56662306a36Sopenharmony_ci ice->spec = spec; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci err = snd_ak4114_create(ice->card, 56962306a36Sopenharmony_ci juli_ak4114_read, 57062306a36Sopenharmony_ci juli_ak4114_write, 57162306a36Sopenharmony_ci ak4114_init_vals, ak4114_init_txcsb, 57262306a36Sopenharmony_ci ice, &spec->ak4114); 57362306a36Sopenharmony_ci if (err < 0) 57462306a36Sopenharmony_ci return err; 57562306a36Sopenharmony_ci /* callback for codecs rate setting */ 57662306a36Sopenharmony_ci spec->ak4114->change_callback = juli_ak4114_change; 57762306a36Sopenharmony_ci spec->ak4114->change_callback_private = ice; 57862306a36Sopenharmony_ci /* AK4114 in Juli can detect external rate correctly */ 57962306a36Sopenharmony_ci spec->ak4114->check_flags = 0; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci#if 0 58262306a36Sopenharmony_ci/* 58362306a36Sopenharmony_ci * it seems that the analog doughter board detection does not work reliably, so 58462306a36Sopenharmony_ci * force the analog flag; it should be very rare (if ever) to come at Juli@ 58562306a36Sopenharmony_ci * used without the analog daughter board 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_ci spec->analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1; 58862306a36Sopenharmony_ci#else 58962306a36Sopenharmony_ci spec->analog = 1; 59062306a36Sopenharmony_ci#endif 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (spec->analog) { 59362306a36Sopenharmony_ci dev_info(ice->card->dev, "juli@: analog I/O detected\n"); 59462306a36Sopenharmony_ci ice->num_total_dacs = 2; 59562306a36Sopenharmony_ci ice->num_total_adcs = 2; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); 59862306a36Sopenharmony_ci ak = ice->akm; 59962306a36Sopenharmony_ci if (!ak) 60062306a36Sopenharmony_ci return -ENOMEM; 60162306a36Sopenharmony_ci ice->akm_codecs = 1; 60262306a36Sopenharmony_ci err = snd_ice1712_akm4xxx_init(ak, &akm_juli_dac, NULL, ice); 60362306a36Sopenharmony_ci if (err < 0) 60462306a36Sopenharmony_ci return err; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* juli is clocked by Xilinx array */ 60862306a36Sopenharmony_ci ice->hw_rates = &juli_rates_info; 60962306a36Sopenharmony_ci ice->is_spdif_master = juli_is_spdif_master; 61062306a36Sopenharmony_ci ice->get_rate = juli_get_rate; 61162306a36Sopenharmony_ci ice->set_rate = juli_set_rate; 61262306a36Sopenharmony_ci ice->set_mclk = juli_set_mclk; 61362306a36Sopenharmony_ci ice->set_spdif_clock = juli_set_spdif_clock; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci ice->spdif.ops.open = juli_spdif_in_open; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 61862306a36Sopenharmony_ci ice->pm_resume = juli_resume; 61962306a36Sopenharmony_ci ice->pm_suspend = juli_suspend; 62062306a36Sopenharmony_ci ice->pm_suspend_enabled = 1; 62162306a36Sopenharmony_ci#endif 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci return 0; 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci/* 62862306a36Sopenharmony_ci * Juli@ boards don't provide the EEPROM data except for the vendor IDs. 62962306a36Sopenharmony_ci * hence the driver needs to sets up it properly. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic const unsigned char juli_eeprom[] = { 63362306a36Sopenharmony_ci [ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, 1xADC, 1xDACs, 63462306a36Sopenharmony_ci SPDIF in */ 63562306a36Sopenharmony_ci [ICE_EEP2_ACLINK] = 0x80, /* I2S */ 63662306a36Sopenharmony_ci [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */ 63762306a36Sopenharmony_ci [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ 63862306a36Sopenharmony_ci [ICE_EEP2_GPIO_DIR] = 0x9f, /* 5, 6:inputs; 7, 4-0 outputs*/ 63962306a36Sopenharmony_ci [ICE_EEP2_GPIO_DIR1] = 0xff, 64062306a36Sopenharmony_ci [ICE_EEP2_GPIO_DIR2] = 0x7f, 64162306a36Sopenharmony_ci [ICE_EEP2_GPIO_MASK] = 0x60, /* 5, 6: locked; 7, 4-0 writable */ 64262306a36Sopenharmony_ci [ICE_EEP2_GPIO_MASK1] = 0x00, /* 0-7 writable */ 64362306a36Sopenharmony_ci [ICE_EEP2_GPIO_MASK2] = 0x7f, 64462306a36Sopenharmony_ci [ICE_EEP2_GPIO_STATE] = GPIO_FREQ_48KHZ | GPIO_MULTI_1X | 64562306a36Sopenharmony_ci GPIO_INTERNAL_CLOCK, /* internal clock, multiple 1x, 48kHz*/ 64662306a36Sopenharmony_ci [ICE_EEP2_GPIO_STATE1] = 0x00, /* unmuted */ 64762306a36Sopenharmony_ci [ICE_EEP2_GPIO_STATE2] = 0x00, 64862306a36Sopenharmony_ci}; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/* entry point */ 65162306a36Sopenharmony_cistruct snd_ice1712_card_info snd_vt1724_juli_cards[] = { 65262306a36Sopenharmony_ci { 65362306a36Sopenharmony_ci .subvendor = VT1724_SUBDEVICE_JULI, 65462306a36Sopenharmony_ci .name = "ESI Juli@", 65562306a36Sopenharmony_ci .model = "juli", 65662306a36Sopenharmony_ci .chip_init = juli_init, 65762306a36Sopenharmony_ci .build_controls = juli_add_controls, 65862306a36Sopenharmony_ci .eeprom_size = sizeof(juli_eeprom), 65962306a36Sopenharmony_ci .eeprom_data = juli_eeprom, 66062306a36Sopenharmony_ci }, 66162306a36Sopenharmony_ci { } /* terminator */ 66262306a36Sopenharmony_ci}; 663