18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * max98926.c -- ALSA SoC MAX98926 driver 48c2ecf20Sopenharmony_ci * Copyright 2013-15 Maxim Integrated Products 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/delay.h> 78c2ecf20Sopenharmony_ci#include <linux/i2c.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/regmap.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/cdev.h> 128c2ecf20Sopenharmony_ci#include <sound/pcm.h> 138c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 148c2ecf20Sopenharmony_ci#include <sound/soc.h> 158c2ecf20Sopenharmony_ci#include <sound/tlv.h> 168c2ecf20Sopenharmony_ci#include "max98926.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic const char * const max98926_boost_voltage_txt[] = { 198c2ecf20Sopenharmony_ci "8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V", 208c2ecf20Sopenharmony_ci "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V" 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic const char *const max98926_pdm_ch_text[] = { 248c2ecf20Sopenharmony_ci "Current", "Voltage", 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const char *const max98926_hpf_cutoff_txt[] = { 288c2ecf20Sopenharmony_ci "Disable", "DC Block", "100Hz", 298c2ecf20Sopenharmony_ci "200Hz", "400Hz", "800Hz", 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic const struct reg_default max98926_reg[] = { 338c2ecf20Sopenharmony_ci { 0x0B, 0x00 }, /* IRQ Enable0 */ 348c2ecf20Sopenharmony_ci { 0x0C, 0x00 }, /* IRQ Enable1 */ 358c2ecf20Sopenharmony_ci { 0x0D, 0x00 }, /* IRQ Enable2 */ 368c2ecf20Sopenharmony_ci { 0x0E, 0x00 }, /* IRQ Clear0 */ 378c2ecf20Sopenharmony_ci { 0x0F, 0x00 }, /* IRQ Clear1 */ 388c2ecf20Sopenharmony_ci { 0x10, 0x00 }, /* IRQ Clear2 */ 398c2ecf20Sopenharmony_ci { 0x11, 0xC0 }, /* Map0 */ 408c2ecf20Sopenharmony_ci { 0x12, 0x00 }, /* Map1 */ 418c2ecf20Sopenharmony_ci { 0x13, 0x00 }, /* Map2 */ 428c2ecf20Sopenharmony_ci { 0x14, 0xF0 }, /* Map3 */ 438c2ecf20Sopenharmony_ci { 0x15, 0x00 }, /* Map4 */ 448c2ecf20Sopenharmony_ci { 0x16, 0xAB }, /* Map5 */ 458c2ecf20Sopenharmony_ci { 0x17, 0x89 }, /* Map6 */ 468c2ecf20Sopenharmony_ci { 0x18, 0x00 }, /* Map7 */ 478c2ecf20Sopenharmony_ci { 0x19, 0x00 }, /* Map8 */ 488c2ecf20Sopenharmony_ci { 0x1A, 0x04 }, /* DAI Clock Mode 1 */ 498c2ecf20Sopenharmony_ci { 0x1B, 0x00 }, /* DAI Clock Mode 2 */ 508c2ecf20Sopenharmony_ci { 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */ 518c2ecf20Sopenharmony_ci { 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */ 528c2ecf20Sopenharmony_ci { 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */ 538c2ecf20Sopenharmony_ci { 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */ 548c2ecf20Sopenharmony_ci { 0x20, 0x50 }, /* Format */ 558c2ecf20Sopenharmony_ci { 0x21, 0x00 }, /* TDM Slot Select */ 568c2ecf20Sopenharmony_ci { 0x22, 0x00 }, /* DOUT Configuration VMON */ 578c2ecf20Sopenharmony_ci { 0x23, 0x00 }, /* DOUT Configuration IMON */ 588c2ecf20Sopenharmony_ci { 0x24, 0x00 }, /* DOUT Configuration VBAT */ 598c2ecf20Sopenharmony_ci { 0x25, 0x00 }, /* DOUT Configuration VBST */ 608c2ecf20Sopenharmony_ci { 0x26, 0x00 }, /* DOUT Configuration FLAG */ 618c2ecf20Sopenharmony_ci { 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */ 628c2ecf20Sopenharmony_ci { 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */ 638c2ecf20Sopenharmony_ci { 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */ 648c2ecf20Sopenharmony_ci { 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */ 658c2ecf20Sopenharmony_ci { 0x2B, 0x02 }, /* DOUT Drive Strength */ 668c2ecf20Sopenharmony_ci { 0x2C, 0x90 }, /* Filters */ 678c2ecf20Sopenharmony_ci { 0x2D, 0x00 }, /* Gain */ 688c2ecf20Sopenharmony_ci { 0x2E, 0x02 }, /* Gain Ramping */ 698c2ecf20Sopenharmony_ci { 0x2F, 0x00 }, /* Speaker Amplifier */ 708c2ecf20Sopenharmony_ci { 0x30, 0x0A }, /* Threshold */ 718c2ecf20Sopenharmony_ci { 0x31, 0x00 }, /* ALC Attack */ 728c2ecf20Sopenharmony_ci { 0x32, 0x80 }, /* ALC Atten and Release */ 738c2ecf20Sopenharmony_ci { 0x33, 0x00 }, /* ALC Infinite Hold Release */ 748c2ecf20Sopenharmony_ci { 0x34, 0x92 }, /* ALC Configuration */ 758c2ecf20Sopenharmony_ci { 0x35, 0x01 }, /* Boost Converter */ 768c2ecf20Sopenharmony_ci { 0x36, 0x00 }, /* Block Enable */ 778c2ecf20Sopenharmony_ci { 0x37, 0x00 }, /* Configuration */ 788c2ecf20Sopenharmony_ci { 0x38, 0x00 }, /* Global Enable */ 798c2ecf20Sopenharmony_ci { 0x3A, 0x00 }, /* Boost Limiter */ 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic const struct soc_enum max98926_voltage_enum[] = { 838c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, 0, 848c2ecf20Sopenharmony_ci ARRAY_SIZE(max98926_pdm_ch_text), 858c2ecf20Sopenharmony_ci max98926_pdm_ch_text), 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new max98926_voltage_control = 898c2ecf20Sopenharmony_ci SOC_DAPM_ENUM("Route", max98926_voltage_enum); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic const struct soc_enum max98926_current_enum[] = { 928c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, 938c2ecf20Sopenharmony_ci MAX98926_PDM_SOURCE_1_SHIFT, 948c2ecf20Sopenharmony_ci ARRAY_SIZE(max98926_pdm_ch_text), 958c2ecf20Sopenharmony_ci max98926_pdm_ch_text), 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new max98926_current_control = 998c2ecf20Sopenharmony_ci SOC_DAPM_ENUM("Route", max98926_current_enum); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new max98926_mixer_controls[] = { 1028c2ecf20Sopenharmony_ci SOC_DAPM_SINGLE("PCM Single Switch", MAX98926_SPK_AMP, 1038c2ecf20Sopenharmony_ci MAX98926_INSELECT_MODE_SHIFT, 0, 0), 1048c2ecf20Sopenharmony_ci SOC_DAPM_SINGLE("PDM Single Switch", MAX98926_SPK_AMP, 1058c2ecf20Sopenharmony_ci MAX98926_INSELECT_MODE_SHIFT, 1, 0), 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new max98926_dai_controls[] = { 1098c2ecf20Sopenharmony_ci SOC_DAPM_SINGLE("Left", MAX98926_GAIN, 1108c2ecf20Sopenharmony_ci MAX98926_DAC_IN_SEL_SHIFT, 0, 0), 1118c2ecf20Sopenharmony_ci SOC_DAPM_SINGLE("Right", MAX98926_GAIN, 1128c2ecf20Sopenharmony_ci MAX98926_DAC_IN_SEL_SHIFT, 1, 0), 1138c2ecf20Sopenharmony_ci SOC_DAPM_SINGLE("LeftRight", MAX98926_GAIN, 1148c2ecf20Sopenharmony_ci MAX98926_DAC_IN_SEL_SHIFT, 2, 0), 1158c2ecf20Sopenharmony_ci SOC_DAPM_SINGLE("(Left+Right)/2 Switch", MAX98926_GAIN, 1168c2ecf20Sopenharmony_ci MAX98926_DAC_IN_SEL_SHIFT, 3, 0), 1178c2ecf20Sopenharmony_ci}; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget max98926_dapm_widgets[] = { 1208c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, 1218c2ecf20Sopenharmony_ci SND_SOC_NOPM, 0, 0), 1228c2ecf20Sopenharmony_ci SND_SOC_DAPM_DAC("Amp Enable", NULL, MAX98926_BLOCK_ENABLE, 1238c2ecf20Sopenharmony_ci MAX98926_SPK_EN_SHIFT, 0), 1248c2ecf20Sopenharmony_ci SND_SOC_DAPM_SUPPLY("Global Enable", MAX98926_GLOBAL_ENABLE, 1258c2ecf20Sopenharmony_ci MAX98926_EN_SHIFT, 0, NULL, 0), 1268c2ecf20Sopenharmony_ci SND_SOC_DAPM_SUPPLY("VI Enable", MAX98926_BLOCK_ENABLE, 1278c2ecf20Sopenharmony_ci MAX98926_ADC_IMON_EN_WIDTH | 1288c2ecf20Sopenharmony_ci MAX98926_ADC_VMON_EN_SHIFT, 1298c2ecf20Sopenharmony_ci 0, NULL, 0), 1308c2ecf20Sopenharmony_ci SND_SOC_DAPM_PGA("BST Enable", MAX98926_BLOCK_ENABLE, 1318c2ecf20Sopenharmony_ci MAX98926_BST_EN_SHIFT, 0, NULL, 0), 1328c2ecf20Sopenharmony_ci SND_SOC_DAPM_OUTPUT("BE_OUT"), 1338c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIXER("PCM Sel", MAX98926_SPK_AMP, 1348c2ecf20Sopenharmony_ci MAX98926_INSELECT_MODE_SHIFT, 0, 1358c2ecf20Sopenharmony_ci &max98926_mixer_controls[0], 1368c2ecf20Sopenharmony_ci ARRAY_SIZE(max98926_mixer_controls)), 1378c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIXER("DAI Sel", 1388c2ecf20Sopenharmony_ci MAX98926_GAIN, MAX98926_DAC_IN_SEL_SHIFT, 0, 1398c2ecf20Sopenharmony_ci &max98926_dai_controls[0], 1408c2ecf20Sopenharmony_ci ARRAY_SIZE(max98926_dai_controls)), 1418c2ecf20Sopenharmony_ci SND_SOC_DAPM_MUX("PDM CH1 Source", 1428c2ecf20Sopenharmony_ci MAX98926_DAI_CLK_DIV_N_LSBS, 1438c2ecf20Sopenharmony_ci MAX98926_PDM_CURRENT_SHIFT, 1448c2ecf20Sopenharmony_ci 0, &max98926_current_control), 1458c2ecf20Sopenharmony_ci SND_SOC_DAPM_MUX("PDM CH0 Source", 1468c2ecf20Sopenharmony_ci MAX98926_DAI_CLK_DIV_N_LSBS, 1478c2ecf20Sopenharmony_ci MAX98926_PDM_VOLTAGE_SHIFT, 1488c2ecf20Sopenharmony_ci 0, &max98926_voltage_control), 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route max98926_audio_map[] = { 1528c2ecf20Sopenharmony_ci {"VI Enable", NULL, "DAI_OUT"}, 1538c2ecf20Sopenharmony_ci {"DAI Sel", "Left", "VI Enable"}, 1548c2ecf20Sopenharmony_ci {"DAI Sel", "Right", "VI Enable"}, 1558c2ecf20Sopenharmony_ci {"DAI Sel", "LeftRight", "VI Enable"}, 1568c2ecf20Sopenharmony_ci {"DAI Sel", "LeftRightDiv2", "VI Enable"}, 1578c2ecf20Sopenharmony_ci {"PCM Sel", "PCM", "DAI Sel"}, 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci {"PDM CH1 Source", "Current", "DAI_OUT"}, 1608c2ecf20Sopenharmony_ci {"PDM CH1 Source", "Voltage", "DAI_OUT"}, 1618c2ecf20Sopenharmony_ci {"PDM CH0 Source", "Current", "DAI_OUT"}, 1628c2ecf20Sopenharmony_ci {"PDM CH0 Source", "Voltage", "DAI_OUT"}, 1638c2ecf20Sopenharmony_ci {"PCM Sel", "Analog", "PDM CH1 Source"}, 1648c2ecf20Sopenharmony_ci {"PCM Sel", "Analog", "PDM CH0 Source"}, 1658c2ecf20Sopenharmony_ci {"Amp Enable", NULL, "PCM Sel"}, 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci {"BST Enable", NULL, "Amp Enable"}, 1688c2ecf20Sopenharmony_ci {"BE_OUT", NULL, "BST Enable"}, 1698c2ecf20Sopenharmony_ci}; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic bool max98926_volatile_register(struct device *dev, unsigned int reg) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci switch (reg) { 1748c2ecf20Sopenharmony_ci case MAX98926_VBAT_DATA: 1758c2ecf20Sopenharmony_ci case MAX98926_VBST_DATA: 1768c2ecf20Sopenharmony_ci case MAX98926_LIVE_STATUS0: 1778c2ecf20Sopenharmony_ci case MAX98926_LIVE_STATUS1: 1788c2ecf20Sopenharmony_ci case MAX98926_LIVE_STATUS2: 1798c2ecf20Sopenharmony_ci case MAX98926_STATE0: 1808c2ecf20Sopenharmony_ci case MAX98926_STATE1: 1818c2ecf20Sopenharmony_ci case MAX98926_STATE2: 1828c2ecf20Sopenharmony_ci case MAX98926_FLAG0: 1838c2ecf20Sopenharmony_ci case MAX98926_FLAG1: 1848c2ecf20Sopenharmony_ci case MAX98926_FLAG2: 1858c2ecf20Sopenharmony_ci case MAX98926_VERSION: 1868c2ecf20Sopenharmony_ci return true; 1878c2ecf20Sopenharmony_ci default: 1888c2ecf20Sopenharmony_ci return false; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic bool max98926_readable_register(struct device *dev, unsigned int reg) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci switch (reg) { 1958c2ecf20Sopenharmony_ci case MAX98926_IRQ_CLEAR0: 1968c2ecf20Sopenharmony_ci case MAX98926_IRQ_CLEAR1: 1978c2ecf20Sopenharmony_ci case MAX98926_IRQ_CLEAR2: 1988c2ecf20Sopenharmony_ci case MAX98926_ALC_HOLD_RLS: 1998c2ecf20Sopenharmony_ci return false; 2008c2ecf20Sopenharmony_ci default: 2018c2ecf20Sopenharmony_ci return true; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(max98926_spk_tlv, -600, 100, 0); 2068c2ecf20Sopenharmony_cistatic DECLARE_TLV_DB_RANGE(max98926_current_tlv, 2078c2ecf20Sopenharmony_ci 0, 11, TLV_DB_SCALE_ITEM(20, 20, 0), 2088c2ecf20Sopenharmony_ci 12, 15, TLV_DB_SCALE_ITEM(320, 40, 0), 2098c2ecf20Sopenharmony_ci); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(max98926_dac_hpf_cutoff, 2128c2ecf20Sopenharmony_ci MAX98926_FILTERS, MAX98926_DAC_HPF_SHIFT, 2138c2ecf20Sopenharmony_ci max98926_hpf_cutoff_txt); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(max98926_boost_voltage, 2168c2ecf20Sopenharmony_ci MAX98926_CONFIGURATION, MAX98926_BST_VOUT_SHIFT, 2178c2ecf20Sopenharmony_ci max98926_boost_voltage_txt); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new max98926_snd_controls[] = { 2208c2ecf20Sopenharmony_ci SOC_SINGLE_TLV("Speaker Volume", MAX98926_GAIN, 2218c2ecf20Sopenharmony_ci MAX98926_SPK_GAIN_SHIFT, 2228c2ecf20Sopenharmony_ci (1<<MAX98926_SPK_GAIN_WIDTH)-1, 0, 2238c2ecf20Sopenharmony_ci max98926_spk_tlv), 2248c2ecf20Sopenharmony_ci SOC_SINGLE("Ramp Switch", MAX98926_GAIN_RAMPING, 2258c2ecf20Sopenharmony_ci MAX98926_SPK_RMP_EN_SHIFT, 1, 0), 2268c2ecf20Sopenharmony_ci SOC_SINGLE("ZCD Switch", MAX98926_GAIN_RAMPING, 2278c2ecf20Sopenharmony_ci MAX98926_SPK_ZCD_EN_SHIFT, 1, 0), 2288c2ecf20Sopenharmony_ci SOC_SINGLE("ALC Switch", MAX98926_THRESHOLD, 2298c2ecf20Sopenharmony_ci MAX98926_ALC_EN_SHIFT, 1, 0), 2308c2ecf20Sopenharmony_ci SOC_SINGLE("ALC Threshold", MAX98926_THRESHOLD, 2318c2ecf20Sopenharmony_ci MAX98926_ALC_TH_SHIFT, 2328c2ecf20Sopenharmony_ci (1<<MAX98926_ALC_TH_WIDTH)-1, 0), 2338c2ecf20Sopenharmony_ci SOC_ENUM("Boost Output Voltage", max98926_boost_voltage), 2348c2ecf20Sopenharmony_ci SOC_SINGLE_TLV("Boost Current Limit", MAX98926_BOOST_LIMITER, 2358c2ecf20Sopenharmony_ci MAX98926_BST_ILIM_SHIFT, 2368c2ecf20Sopenharmony_ci (1<<MAX98926_BST_ILIM_SHIFT)-1, 0, 2378c2ecf20Sopenharmony_ci max98926_current_tlv), 2388c2ecf20Sopenharmony_ci SOC_ENUM("DAC HPF Cutoff", max98926_dac_hpf_cutoff), 2398c2ecf20Sopenharmony_ci SOC_DOUBLE("PDM Channel One", MAX98926_DAI_CLK_DIV_N_LSBS, 2408c2ecf20Sopenharmony_ci MAX98926_PDM_CHANNEL_1_SHIFT, 2418c2ecf20Sopenharmony_ci MAX98926_PDM_CHANNEL_1_HIZ, 1, 0), 2428c2ecf20Sopenharmony_ci SOC_DOUBLE("PDM Channel Zero", MAX98926_DAI_CLK_DIV_N_LSBS, 2438c2ecf20Sopenharmony_ci MAX98926_PDM_CHANNEL_0_SHIFT, 2448c2ecf20Sopenharmony_ci MAX98926_PDM_CHANNEL_0_HIZ, 1, 0), 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic const struct { 2488c2ecf20Sopenharmony_ci int rate; 2498c2ecf20Sopenharmony_ci int sr; 2508c2ecf20Sopenharmony_ci} rate_table[] = { 2518c2ecf20Sopenharmony_ci { 2528c2ecf20Sopenharmony_ci .rate = 8000, 2538c2ecf20Sopenharmony_ci .sr = 0, 2548c2ecf20Sopenharmony_ci }, 2558c2ecf20Sopenharmony_ci { 2568c2ecf20Sopenharmony_ci .rate = 11025, 2578c2ecf20Sopenharmony_ci .sr = 1, 2588c2ecf20Sopenharmony_ci }, 2598c2ecf20Sopenharmony_ci { 2608c2ecf20Sopenharmony_ci .rate = 12000, 2618c2ecf20Sopenharmony_ci .sr = 2, 2628c2ecf20Sopenharmony_ci }, 2638c2ecf20Sopenharmony_ci { 2648c2ecf20Sopenharmony_ci .rate = 16000, 2658c2ecf20Sopenharmony_ci .sr = 3, 2668c2ecf20Sopenharmony_ci }, 2678c2ecf20Sopenharmony_ci { 2688c2ecf20Sopenharmony_ci .rate = 22050, 2698c2ecf20Sopenharmony_ci .sr = 4, 2708c2ecf20Sopenharmony_ci }, 2718c2ecf20Sopenharmony_ci { 2728c2ecf20Sopenharmony_ci .rate = 24000, 2738c2ecf20Sopenharmony_ci .sr = 5, 2748c2ecf20Sopenharmony_ci }, 2758c2ecf20Sopenharmony_ci { 2768c2ecf20Sopenharmony_ci .rate = 32000, 2778c2ecf20Sopenharmony_ci .sr = 6, 2788c2ecf20Sopenharmony_ci }, 2798c2ecf20Sopenharmony_ci { 2808c2ecf20Sopenharmony_ci .rate = 44100, 2818c2ecf20Sopenharmony_ci .sr = 7, 2828c2ecf20Sopenharmony_ci }, 2838c2ecf20Sopenharmony_ci { 2848c2ecf20Sopenharmony_ci .rate = 48000, 2858c2ecf20Sopenharmony_ci .sr = 8, 2868c2ecf20Sopenharmony_ci }, 2878c2ecf20Sopenharmony_ci}; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic void max98926_set_sense_data(struct max98926_priv *max98926) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci regmap_update_bits(max98926->regmap, 2928c2ecf20Sopenharmony_ci MAX98926_DOUT_CFG_VMON, 2938c2ecf20Sopenharmony_ci MAX98926_DAI_VMON_EN_MASK, 2948c2ecf20Sopenharmony_ci MAX98926_DAI_VMON_EN_MASK); 2958c2ecf20Sopenharmony_ci regmap_update_bits(max98926->regmap, 2968c2ecf20Sopenharmony_ci MAX98926_DOUT_CFG_IMON, 2978c2ecf20Sopenharmony_ci MAX98926_DAI_IMON_EN_MASK, 2988c2ecf20Sopenharmony_ci MAX98926_DAI_IMON_EN_MASK); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (!max98926->interleave_mode) { 3018c2ecf20Sopenharmony_ci /* set VMON slots */ 3028c2ecf20Sopenharmony_ci regmap_update_bits(max98926->regmap, 3038c2ecf20Sopenharmony_ci MAX98926_DOUT_CFG_VMON, 3048c2ecf20Sopenharmony_ci MAX98926_DAI_VMON_SLOT_MASK, 3058c2ecf20Sopenharmony_ci max98926->v_slot); 3068c2ecf20Sopenharmony_ci /* set IMON slots */ 3078c2ecf20Sopenharmony_ci regmap_update_bits(max98926->regmap, 3088c2ecf20Sopenharmony_ci MAX98926_DOUT_CFG_IMON, 3098c2ecf20Sopenharmony_ci MAX98926_DAI_IMON_SLOT_MASK, 3108c2ecf20Sopenharmony_ci max98926->i_slot); 3118c2ecf20Sopenharmony_ci } else { 3128c2ecf20Sopenharmony_ci /* enable interleave mode */ 3138c2ecf20Sopenharmony_ci regmap_update_bits(max98926->regmap, 3148c2ecf20Sopenharmony_ci MAX98926_FORMAT, 3158c2ecf20Sopenharmony_ci MAX98926_DAI_INTERLEAVE_MASK, 3168c2ecf20Sopenharmony_ci MAX98926_DAI_INTERLEAVE_MASK); 3178c2ecf20Sopenharmony_ci /* set interleave slots */ 3188c2ecf20Sopenharmony_ci regmap_update_bits(max98926->regmap, 3198c2ecf20Sopenharmony_ci MAX98926_DOUT_CFG_VBAT, 3208c2ecf20Sopenharmony_ci MAX98926_DAI_INTERLEAVE_SLOT_MASK, 3218c2ecf20Sopenharmony_ci max98926->v_slot); 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai, 3268c2ecf20Sopenharmony_ci unsigned int fmt) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 3298c2ecf20Sopenharmony_ci struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component); 3308c2ecf20Sopenharmony_ci unsigned int invert = 0; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 3358c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 3368c2ecf20Sopenharmony_ci max98926_set_sense_data(max98926); 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci default: 3398c2ecf20Sopenharmony_ci dev_err(component->dev, "DAI clock mode unsupported\n"); 3408c2ecf20Sopenharmony_ci return -EINVAL; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 3448c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 3478c2ecf20Sopenharmony_ci invert = MAX98926_DAI_WCI_MASK; 3488c2ecf20Sopenharmony_ci break; 3498c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 3508c2ecf20Sopenharmony_ci invert = MAX98926_DAI_BCI_MASK; 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 3538c2ecf20Sopenharmony_ci invert = MAX98926_DAI_BCI_MASK | MAX98926_DAI_WCI_MASK; 3548c2ecf20Sopenharmony_ci break; 3558c2ecf20Sopenharmony_ci default: 3568c2ecf20Sopenharmony_ci dev_err(component->dev, "DAI invert mode unsupported\n"); 3578c2ecf20Sopenharmony_ci return -EINVAL; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci regmap_write(max98926->regmap, 3618c2ecf20Sopenharmony_ci MAX98926_FORMAT, MAX98926_DAI_DLY_MASK); 3628c2ecf20Sopenharmony_ci regmap_update_bits(max98926->regmap, MAX98926_FORMAT, 3638c2ecf20Sopenharmony_ci MAX98926_DAI_BCI_MASK, invert); 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic int max98926_dai_hw_params(struct snd_pcm_substream *substream, 3688c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 3698c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci int dai_sr = -EINVAL; 3728c2ecf20Sopenharmony_ci int rate = params_rate(params), i; 3738c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 3748c2ecf20Sopenharmony_ci struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component); 3758c2ecf20Sopenharmony_ci int blr_clk_ratio; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci switch (params_format(params)) { 3788c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 3798c2ecf20Sopenharmony_ci regmap_update_bits(max98926->regmap, 3808c2ecf20Sopenharmony_ci MAX98926_FORMAT, 3818c2ecf20Sopenharmony_ci MAX98926_DAI_CHANSZ_MASK, 3828c2ecf20Sopenharmony_ci MAX98926_DAI_CHANSZ_16); 3838c2ecf20Sopenharmony_ci max98926->ch_size = 16; 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 3868c2ecf20Sopenharmony_ci regmap_update_bits(max98926->regmap, 3878c2ecf20Sopenharmony_ci MAX98926_FORMAT, 3888c2ecf20Sopenharmony_ci MAX98926_DAI_CHANSZ_MASK, 3898c2ecf20Sopenharmony_ci MAX98926_DAI_CHANSZ_24); 3908c2ecf20Sopenharmony_ci max98926->ch_size = 24; 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 3938c2ecf20Sopenharmony_ci regmap_update_bits(max98926->regmap, 3948c2ecf20Sopenharmony_ci MAX98926_FORMAT, 3958c2ecf20Sopenharmony_ci MAX98926_DAI_CHANSZ_MASK, 3968c2ecf20Sopenharmony_ci MAX98926_DAI_CHANSZ_32); 3978c2ecf20Sopenharmony_ci max98926->ch_size = 32; 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci default: 4008c2ecf20Sopenharmony_ci dev_dbg(component->dev, "format unsupported %d\n", 4018c2ecf20Sopenharmony_ci params_format(params)); 4028c2ecf20Sopenharmony_ci return -EINVAL; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* BCLK/LRCLK ratio calculation */ 4068c2ecf20Sopenharmony_ci blr_clk_ratio = params_channels(params) * max98926->ch_size; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci switch (blr_clk_ratio) { 4098c2ecf20Sopenharmony_ci case 32: 4108c2ecf20Sopenharmony_ci regmap_update_bits(max98926->regmap, 4118c2ecf20Sopenharmony_ci MAX98926_DAI_CLK_MODE2, 4128c2ecf20Sopenharmony_ci MAX98926_DAI_BSEL_MASK, 4138c2ecf20Sopenharmony_ci MAX98926_DAI_BSEL_32); 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci case 48: 4168c2ecf20Sopenharmony_ci regmap_update_bits(max98926->regmap, 4178c2ecf20Sopenharmony_ci MAX98926_DAI_CLK_MODE2, 4188c2ecf20Sopenharmony_ci MAX98926_DAI_BSEL_MASK, 4198c2ecf20Sopenharmony_ci MAX98926_DAI_BSEL_48); 4208c2ecf20Sopenharmony_ci break; 4218c2ecf20Sopenharmony_ci case 64: 4228c2ecf20Sopenharmony_ci regmap_update_bits(max98926->regmap, 4238c2ecf20Sopenharmony_ci MAX98926_DAI_CLK_MODE2, 4248c2ecf20Sopenharmony_ci MAX98926_DAI_BSEL_MASK, 4258c2ecf20Sopenharmony_ci MAX98926_DAI_BSEL_64); 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci default: 4288c2ecf20Sopenharmony_ci return -EINVAL; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* find the closest rate */ 4328c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rate_table); i++) { 4338c2ecf20Sopenharmony_ci if (rate_table[i].rate >= rate) { 4348c2ecf20Sopenharmony_ci dai_sr = rate_table[i].sr; 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci if (dai_sr < 0) 4398c2ecf20Sopenharmony_ci return -EINVAL; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* set DAI_SR to correct LRCLK frequency */ 4428c2ecf20Sopenharmony_ci regmap_update_bits(max98926->regmap, 4438c2ecf20Sopenharmony_ci MAX98926_DAI_CLK_MODE2, 4448c2ecf20Sopenharmony_ci MAX98926_DAI_SR_MASK, dai_sr << MAX98926_DAI_SR_SHIFT); 4458c2ecf20Sopenharmony_ci return 0; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci#define MAX98926_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ 4498c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops max98926_dai_ops = { 4528c2ecf20Sopenharmony_ci .set_fmt = max98926_dai_set_fmt, 4538c2ecf20Sopenharmony_ci .hw_params = max98926_dai_hw_params, 4548c2ecf20Sopenharmony_ci}; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver max98926_dai[] = { 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci .name = "max98926-aif1", 4598c2ecf20Sopenharmony_ci .playback = { 4608c2ecf20Sopenharmony_ci .stream_name = "HiFi Playback", 4618c2ecf20Sopenharmony_ci .channels_min = 1, 4628c2ecf20Sopenharmony_ci .channels_max = 2, 4638c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 4648c2ecf20Sopenharmony_ci .formats = MAX98926_FORMATS, 4658c2ecf20Sopenharmony_ci }, 4668c2ecf20Sopenharmony_ci .capture = { 4678c2ecf20Sopenharmony_ci .stream_name = "HiFi Capture", 4688c2ecf20Sopenharmony_ci .channels_min = 1, 4698c2ecf20Sopenharmony_ci .channels_max = 2, 4708c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 4718c2ecf20Sopenharmony_ci .formats = MAX98926_FORMATS, 4728c2ecf20Sopenharmony_ci }, 4738c2ecf20Sopenharmony_ci .ops = &max98926_dai_ops, 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci}; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int max98926_probe(struct snd_soc_component *component) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci max98926->component = component; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* Hi-Z all the slots */ 4848c2ecf20Sopenharmony_ci regmap_write(max98926->regmap, MAX98926_DOUT_HIZ_CFG4, 0xF0); 4858c2ecf20Sopenharmony_ci return 0; 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_max98926 = { 4898c2ecf20Sopenharmony_ci .probe = max98926_probe, 4908c2ecf20Sopenharmony_ci .controls = max98926_snd_controls, 4918c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(max98926_snd_controls), 4928c2ecf20Sopenharmony_ci .dapm_routes = max98926_audio_map, 4938c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(max98926_audio_map), 4948c2ecf20Sopenharmony_ci .dapm_widgets = max98926_dapm_widgets, 4958c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(max98926_dapm_widgets), 4968c2ecf20Sopenharmony_ci .idle_bias_on = 1, 4978c2ecf20Sopenharmony_ci .use_pmdown_time = 1, 4988c2ecf20Sopenharmony_ci .endianness = 1, 4998c2ecf20Sopenharmony_ci .non_legacy_dai_naming = 1, 5008c2ecf20Sopenharmony_ci}; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic const struct regmap_config max98926_regmap = { 5038c2ecf20Sopenharmony_ci .reg_bits = 8, 5048c2ecf20Sopenharmony_ci .val_bits = 8, 5058c2ecf20Sopenharmony_ci .max_register = MAX98926_VERSION, 5068c2ecf20Sopenharmony_ci .reg_defaults = max98926_reg, 5078c2ecf20Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(max98926_reg), 5088c2ecf20Sopenharmony_ci .volatile_reg = max98926_volatile_register, 5098c2ecf20Sopenharmony_ci .readable_reg = max98926_readable_register, 5108c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 5118c2ecf20Sopenharmony_ci}; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic int max98926_i2c_probe(struct i2c_client *i2c, 5148c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci int ret, reg; 5178c2ecf20Sopenharmony_ci u32 value; 5188c2ecf20Sopenharmony_ci struct max98926_priv *max98926; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci max98926 = devm_kzalloc(&i2c->dev, 5218c2ecf20Sopenharmony_ci sizeof(*max98926), GFP_KERNEL); 5228c2ecf20Sopenharmony_ci if (!max98926) 5238c2ecf20Sopenharmony_ci return -ENOMEM; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci i2c_set_clientdata(i2c, max98926); 5268c2ecf20Sopenharmony_ci max98926->regmap = devm_regmap_init_i2c(i2c, &max98926_regmap); 5278c2ecf20Sopenharmony_ci if (IS_ERR(max98926->regmap)) { 5288c2ecf20Sopenharmony_ci ret = PTR_ERR(max98926->regmap); 5298c2ecf20Sopenharmony_ci dev_err(&i2c->dev, 5308c2ecf20Sopenharmony_ci "Failed to allocate regmap: %d\n", ret); 5318c2ecf20Sopenharmony_ci goto err_out; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci if (of_property_read_bool(i2c->dev.of_node, "interleave-mode")) 5348c2ecf20Sopenharmony_ci max98926->interleave_mode = true; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) { 5378c2ecf20Sopenharmony_ci if (value > MAX98926_DAI_VMON_SLOT_1E_1F) { 5388c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "vmon slot number is wrong:\n"); 5398c2ecf20Sopenharmony_ci return -EINVAL; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci max98926->v_slot = value; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) { 5448c2ecf20Sopenharmony_ci if (value > MAX98926_DAI_IMON_SLOT_1E_1F) { 5458c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "imon slot number is wrong:\n"); 5468c2ecf20Sopenharmony_ci return -EINVAL; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci max98926->i_slot = value; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci ret = regmap_read(max98926->regmap, 5518c2ecf20Sopenharmony_ci MAX98926_VERSION, ®); 5528c2ecf20Sopenharmony_ci if (ret < 0) { 5538c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Failed to read: %x\n", reg); 5548c2ecf20Sopenharmony_ci return ret; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&i2c->dev, 5588c2ecf20Sopenharmony_ci &soc_component_dev_max98926, 5598c2ecf20Sopenharmony_ci max98926_dai, ARRAY_SIZE(max98926_dai)); 5608c2ecf20Sopenharmony_ci if (ret < 0) 5618c2ecf20Sopenharmony_ci dev_err(&i2c->dev, 5628c2ecf20Sopenharmony_ci "Failed to register component: %d\n", ret); 5638c2ecf20Sopenharmony_ci dev_info(&i2c->dev, "device version: %x\n", reg); 5648c2ecf20Sopenharmony_cierr_out: 5658c2ecf20Sopenharmony_ci return ret; 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic const struct i2c_device_id max98926_i2c_id[] = { 5698c2ecf20Sopenharmony_ci { "max98926", 0 }, 5708c2ecf20Sopenharmony_ci { } 5718c2ecf20Sopenharmony_ci}; 5728c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, max98926_i2c_id); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic const struct of_device_id max98926_of_match[] = { 5758c2ecf20Sopenharmony_ci { .compatible = "maxim,max98926", }, 5768c2ecf20Sopenharmony_ci { } 5778c2ecf20Sopenharmony_ci}; 5788c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, max98926_of_match); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic struct i2c_driver max98926_i2c_driver = { 5818c2ecf20Sopenharmony_ci .driver = { 5828c2ecf20Sopenharmony_ci .name = "max98926", 5838c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(max98926_of_match), 5848c2ecf20Sopenharmony_ci .pm = NULL, 5858c2ecf20Sopenharmony_ci }, 5868c2ecf20Sopenharmony_ci .probe = max98926_i2c_probe, 5878c2ecf20Sopenharmony_ci .id_table = max98926_i2c_id, 5888c2ecf20Sopenharmony_ci}; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cimodule_i2c_driver(max98926_i2c_driver) 5918c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC MAX98926 driver"); 5928c2ecf20Sopenharmony_ciMODULE_AUTHOR("Anish kumar <anish.kumar@maximintegrated.com>"); 5938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 594