18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ALSA SoC TLV320AIC23 codec driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Arun KS, <arunks@mistralsolutions.com> 68c2ecf20Sopenharmony_ci * Copyright: (C) 2008 Mistral Solutions Pvt Ltd., 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on sound/soc/codecs/wm8731.c by Richard Purdie 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Notes: 118c2ecf20Sopenharmony_ci * The AIC23 is a driver for a low power stereo audio 128c2ecf20Sopenharmony_ci * codec tlv320aic23 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * The machine layer should disable unsupported inputs/outputs by 158c2ecf20Sopenharmony_ci * snd_soc_dapm_disable_pin(codec, "LHPOUT"), etc. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/pm.h> 238c2ecf20Sopenharmony_ci#include <linux/regmap.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <sound/core.h> 268c2ecf20Sopenharmony_ci#include <sound/pcm.h> 278c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 288c2ecf20Sopenharmony_ci#include <sound/soc.h> 298c2ecf20Sopenharmony_ci#include <sound/tlv.h> 308c2ecf20Sopenharmony_ci#include <sound/initval.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "tlv320aic23.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* 358c2ecf20Sopenharmony_ci * AIC23 register cache 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_cistatic const struct reg_default tlv320aic23_reg[] = { 388c2ecf20Sopenharmony_ci { 0, 0x0097 }, 398c2ecf20Sopenharmony_ci { 1, 0x0097 }, 408c2ecf20Sopenharmony_ci { 2, 0x00F9 }, 418c2ecf20Sopenharmony_ci { 3, 0x00F9 }, 428c2ecf20Sopenharmony_ci { 4, 0x001A }, 438c2ecf20Sopenharmony_ci { 5, 0x0004 }, 448c2ecf20Sopenharmony_ci { 6, 0x0007 }, 458c2ecf20Sopenharmony_ci { 7, 0x0001 }, 468c2ecf20Sopenharmony_ci { 8, 0x0020 }, 478c2ecf20Sopenharmony_ci { 9, 0x0000 }, 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciconst struct regmap_config tlv320aic23_regmap = { 518c2ecf20Sopenharmony_ci .reg_bits = 7, 528c2ecf20Sopenharmony_ci .val_bits = 9, 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci .max_register = TLV320AIC23_RESET, 558c2ecf20Sopenharmony_ci .reg_defaults = tlv320aic23_reg, 568c2ecf20Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(tlv320aic23_reg), 578c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tlv320aic23_regmap); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic const char *rec_src_text[] = { "Line", "Mic" }; 628c2ecf20Sopenharmony_cistatic const char *deemph_text[] = {"None", "32Khz", "44.1Khz", "48Khz"}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(rec_src_enum, 658c2ecf20Sopenharmony_ci TLV320AIC23_ANLG, 2, rec_src_text); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new tlv320aic23_rec_src_mux_controls = 688c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Input Select", rec_src_enum); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(tlv320aic23_deemph, 718c2ecf20Sopenharmony_ci TLV320AIC23_DIGT, 1, deemph_text); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(out_gain_tlv, -12100, 100, 0); 748c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(input_gain_tlv, -1725, 75, 0); 758c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(sidetone_vol_tlv, -1800, 300, 0); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol, 788c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 818c2ecf20Sopenharmony_ci u16 val, reg; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci val = (ucontrol->value.integer.value[0] & 0x07); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* linear conversion to userspace 868c2ecf20Sopenharmony_ci * 000 = -6db 878c2ecf20Sopenharmony_ci * 001 = -9db 888c2ecf20Sopenharmony_ci * 010 = -12db 898c2ecf20Sopenharmony_ci * 011 = -18db (Min) 908c2ecf20Sopenharmony_ci * 100 = 0db (Max) 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci val = (val >= 4) ? 4 : (3 - val); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci reg = snd_soc_component_read(component, TLV320AIC23_ANLG) & (~0x1C0); 958c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_ANLG, reg | (val << 6)); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int snd_soc_tlv320aic23_get_volsw(struct snd_kcontrol *kcontrol, 1018c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 1048c2ecf20Sopenharmony_ci u16 val; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci val = snd_soc_component_read(component, TLV320AIC23_ANLG) & (0x1C0); 1078c2ecf20Sopenharmony_ci val = val >> 6; 1088c2ecf20Sopenharmony_ci val = (val >= 4) ? 4 : (3 - val); 1098c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = val; 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new tlv320aic23_snd_controls[] = { 1158c2ecf20Sopenharmony_ci SOC_DOUBLE_R_TLV("Digital Playback Volume", TLV320AIC23_LCHNVOL, 1168c2ecf20Sopenharmony_ci TLV320AIC23_RCHNVOL, 0, 127, 0, out_gain_tlv), 1178c2ecf20Sopenharmony_ci SOC_SINGLE("Digital Playback Switch", TLV320AIC23_DIGT, 3, 1, 1), 1188c2ecf20Sopenharmony_ci SOC_DOUBLE_R("Line Input Switch", TLV320AIC23_LINVOL, 1198c2ecf20Sopenharmony_ci TLV320AIC23_RINVOL, 7, 1, 0), 1208c2ecf20Sopenharmony_ci SOC_DOUBLE_R_TLV("Line Input Volume", TLV320AIC23_LINVOL, 1218c2ecf20Sopenharmony_ci TLV320AIC23_RINVOL, 0, 31, 0, input_gain_tlv), 1228c2ecf20Sopenharmony_ci SOC_SINGLE("Mic Input Switch", TLV320AIC23_ANLG, 1, 1, 1), 1238c2ecf20Sopenharmony_ci SOC_SINGLE("Mic Booster Switch", TLV320AIC23_ANLG, 0, 1, 0), 1248c2ecf20Sopenharmony_ci SOC_SINGLE_EXT_TLV("Sidetone Volume", TLV320AIC23_ANLG, 6, 4, 0, 1258c2ecf20Sopenharmony_ci snd_soc_tlv320aic23_get_volsw, 1268c2ecf20Sopenharmony_ci snd_soc_tlv320aic23_put_volsw, sidetone_vol_tlv), 1278c2ecf20Sopenharmony_ci SOC_ENUM("Playback De-emphasis", tlv320aic23_deemph), 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* PGA Mixer controls for Line and Mic switch */ 1318c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new tlv320aic23_output_mixer_controls[] = { 1328c2ecf20Sopenharmony_ci SOC_DAPM_SINGLE("Line Bypass Switch", TLV320AIC23_ANLG, 3, 1, 0), 1338c2ecf20Sopenharmony_ci SOC_DAPM_SINGLE("Mic Sidetone Switch", TLV320AIC23_ANLG, 5, 1, 0), 1348c2ecf20Sopenharmony_ci SOC_DAPM_SINGLE("Playback Switch", TLV320AIC23_ANLG, 4, 1, 0), 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { 1388c2ecf20Sopenharmony_ci SND_SOC_DAPM_DAC("DAC", "Playback", TLV320AIC23_PWR, 3, 1), 1398c2ecf20Sopenharmony_ci SND_SOC_DAPM_ADC("ADC", "Capture", TLV320AIC23_PWR, 2, 1), 1408c2ecf20Sopenharmony_ci SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0, 1418c2ecf20Sopenharmony_ci &tlv320aic23_rec_src_mux_controls), 1428c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIXER("Output Mixer", TLV320AIC23_PWR, 4, 1, 1438c2ecf20Sopenharmony_ci &tlv320aic23_output_mixer_controls[0], 1448c2ecf20Sopenharmony_ci ARRAY_SIZE(tlv320aic23_output_mixer_controls)), 1458c2ecf20Sopenharmony_ci SND_SOC_DAPM_PGA("Line Input", TLV320AIC23_PWR, 0, 1, NULL, 0), 1468c2ecf20Sopenharmony_ci SND_SOC_DAPM_PGA("Mic Input", TLV320AIC23_PWR, 1, 1, NULL, 0), 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci SND_SOC_DAPM_OUTPUT("LHPOUT"), 1498c2ecf20Sopenharmony_ci SND_SOC_DAPM_OUTPUT("RHPOUT"), 1508c2ecf20Sopenharmony_ci SND_SOC_DAPM_OUTPUT("LOUT"), 1518c2ecf20Sopenharmony_ci SND_SOC_DAPM_OUTPUT("ROUT"), 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci SND_SOC_DAPM_INPUT("LLINEIN"), 1548c2ecf20Sopenharmony_ci SND_SOC_DAPM_INPUT("RLINEIN"), 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci SND_SOC_DAPM_INPUT("MICIN"), 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route tlv320aic23_intercon[] = { 1608c2ecf20Sopenharmony_ci /* Output Mixer */ 1618c2ecf20Sopenharmony_ci {"Output Mixer", "Line Bypass Switch", "Line Input"}, 1628c2ecf20Sopenharmony_ci {"Output Mixer", "Playback Switch", "DAC"}, 1638c2ecf20Sopenharmony_ci {"Output Mixer", "Mic Sidetone Switch", "Mic Input"}, 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Outputs */ 1668c2ecf20Sopenharmony_ci {"RHPOUT", NULL, "Output Mixer"}, 1678c2ecf20Sopenharmony_ci {"LHPOUT", NULL, "Output Mixer"}, 1688c2ecf20Sopenharmony_ci {"LOUT", NULL, "Output Mixer"}, 1698c2ecf20Sopenharmony_ci {"ROUT", NULL, "Output Mixer"}, 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Inputs */ 1728c2ecf20Sopenharmony_ci {"Line Input", NULL, "LLINEIN"}, 1738c2ecf20Sopenharmony_ci {"Line Input", NULL, "RLINEIN"}, 1748c2ecf20Sopenharmony_ci {"Mic Input", NULL, "MICIN"}, 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* input mux */ 1778c2ecf20Sopenharmony_ci {"Capture Source", "Line", "Line Input"}, 1788c2ecf20Sopenharmony_ci {"Capture Source", "Mic", "Mic Input"}, 1798c2ecf20Sopenharmony_ci {"ADC", NULL, "Capture Source"}, 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci}; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/* AIC23 driver data */ 1848c2ecf20Sopenharmony_cistruct aic23 { 1858c2ecf20Sopenharmony_ci struct regmap *regmap; 1868c2ecf20Sopenharmony_ci int mclk; 1878c2ecf20Sopenharmony_ci int requested_adc; 1888c2ecf20Sopenharmony_ci int requested_dac; 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* 1928c2ecf20Sopenharmony_ci * Common Crystals used 1938c2ecf20Sopenharmony_ci * 11.2896 Mhz /128 = *88.2k /192 = 58.8k 1948c2ecf20Sopenharmony_ci * 12.0000 Mhz /125 = *96k /136 = 88.235K 1958c2ecf20Sopenharmony_ci * 12.2880 Mhz /128 = *96k /192 = 64k 1968c2ecf20Sopenharmony_ci * 16.9344 Mhz /128 = 132.3k /192 = *88.2k 1978c2ecf20Sopenharmony_ci * 18.4320 Mhz /128 = 144k /192 = *96k 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/* 2018c2ecf20Sopenharmony_ci * Normal BOSR 0-256/2 = 128, 1-384/2 = 192 2028c2ecf20Sopenharmony_ci * USB BOSR 0-250/2 = 125, 1-272/2 = 136 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_cistatic const int bosr_usb_divisor_table[] = { 2058c2ecf20Sopenharmony_ci 128, 125, 192, 136 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci#define LOWER_GROUP ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<6) | (1<<7)) 2088c2ecf20Sopenharmony_ci#define UPPER_GROUP ((1<<8) | (1<<9) | (1<<10) | (1<<11) | (1<<15)) 2098c2ecf20Sopenharmony_cistatic const unsigned short sr_valid_mask[] = { 2108c2ecf20Sopenharmony_ci LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 0*/ 2118c2ecf20Sopenharmony_ci LOWER_GROUP, /* Usb, bosr - 0*/ 2128c2ecf20Sopenharmony_ci LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 1*/ 2138c2ecf20Sopenharmony_ci UPPER_GROUP, /* Usb, bosr - 1*/ 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci/* 2168c2ecf20Sopenharmony_ci * Every divisor is a factor of 11*12 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci#define SR_MULT (11*12) 2198c2ecf20Sopenharmony_ci#define A(x) (SR_MULT/x) 2208c2ecf20Sopenharmony_cistatic const unsigned char sr_adc_mult_table[] = { 2218c2ecf20Sopenharmony_ci A(2), A(2), A(12), A(12), 0, 0, A(3), A(1), 2228c2ecf20Sopenharmony_ci A(2), A(2), A(11), A(11), 0, 0, 0, A(1) 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_cistatic const unsigned char sr_dac_mult_table[] = { 2258c2ecf20Sopenharmony_ci A(2), A(12), A(2), A(12), 0, 0, A(3), A(1), 2268c2ecf20Sopenharmony_ci A(2), A(11), A(2), A(11), 0, 0, 0, A(1) 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic unsigned get_score(int adc, int adc_l, int adc_h, int need_adc, 2308c2ecf20Sopenharmony_ci int dac, int dac_l, int dac_h, int need_dac) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci if ((adc >= adc_l) && (adc <= adc_h) && 2338c2ecf20Sopenharmony_ci (dac >= dac_l) && (dac <= dac_h)) { 2348c2ecf20Sopenharmony_ci int diff_adc = need_adc - adc; 2358c2ecf20Sopenharmony_ci int diff_dac = need_dac - dac; 2368c2ecf20Sopenharmony_ci return abs(diff_adc) + abs(diff_dac); 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci return UINT_MAX; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int find_rate(int mclk, u32 need_adc, u32 need_dac) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci int i, j; 2448c2ecf20Sopenharmony_ci int best_i = -1; 2458c2ecf20Sopenharmony_ci int best_j = -1; 2468c2ecf20Sopenharmony_ci int best_div = 0; 2478c2ecf20Sopenharmony_ci unsigned best_score = UINT_MAX; 2488c2ecf20Sopenharmony_ci int adc_l, adc_h, dac_l, dac_h; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci need_adc *= SR_MULT; 2518c2ecf20Sopenharmony_ci need_dac *= SR_MULT; 2528c2ecf20Sopenharmony_ci /* 2538c2ecf20Sopenharmony_ci * rates given are +/- 1/32 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci adc_l = need_adc - (need_adc >> 5); 2568c2ecf20Sopenharmony_ci adc_h = need_adc + (need_adc >> 5); 2578c2ecf20Sopenharmony_ci dac_l = need_dac - (need_dac >> 5); 2588c2ecf20Sopenharmony_ci dac_h = need_dac + (need_dac >> 5); 2598c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bosr_usb_divisor_table); i++) { 2608c2ecf20Sopenharmony_ci int base = mclk / bosr_usb_divisor_table[i]; 2618c2ecf20Sopenharmony_ci int mask = sr_valid_mask[i]; 2628c2ecf20Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(sr_adc_mult_table); 2638c2ecf20Sopenharmony_ci j++, mask >>= 1) { 2648c2ecf20Sopenharmony_ci int adc; 2658c2ecf20Sopenharmony_ci int dac; 2668c2ecf20Sopenharmony_ci int score; 2678c2ecf20Sopenharmony_ci if ((mask & 1) == 0) 2688c2ecf20Sopenharmony_ci continue; 2698c2ecf20Sopenharmony_ci adc = base * sr_adc_mult_table[j]; 2708c2ecf20Sopenharmony_ci dac = base * sr_dac_mult_table[j]; 2718c2ecf20Sopenharmony_ci score = get_score(adc, adc_l, adc_h, need_adc, 2728c2ecf20Sopenharmony_ci dac, dac_l, dac_h, need_dac); 2738c2ecf20Sopenharmony_ci if (best_score > score) { 2748c2ecf20Sopenharmony_ci best_score = score; 2758c2ecf20Sopenharmony_ci best_i = i; 2768c2ecf20Sopenharmony_ci best_j = j; 2778c2ecf20Sopenharmony_ci best_div = 0; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci score = get_score((adc >> 1), adc_l, adc_h, need_adc, 2808c2ecf20Sopenharmony_ci (dac >> 1), dac_l, dac_h, need_dac); 2818c2ecf20Sopenharmony_ci /* prefer to have a /2 */ 2828c2ecf20Sopenharmony_ci if ((score != UINT_MAX) && (best_score >= score)) { 2838c2ecf20Sopenharmony_ci best_score = score; 2848c2ecf20Sopenharmony_ci best_i = i; 2858c2ecf20Sopenharmony_ci best_j = j; 2868c2ecf20Sopenharmony_ci best_div = 1; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci return (best_j << 2) | best_i | (best_div << TLV320AIC23_CLKIN_SHIFT); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci#ifdef DEBUG 2948c2ecf20Sopenharmony_cistatic void get_current_sample_rates(struct snd_soc_component *component, int mclk, 2958c2ecf20Sopenharmony_ci u32 *sample_rate_adc, u32 *sample_rate_dac) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci int src = snd_soc_component_read(component, TLV320AIC23_SRATE); 2988c2ecf20Sopenharmony_ci int sr = (src >> 2) & 0x0f; 2998c2ecf20Sopenharmony_ci int val = (mclk / bosr_usb_divisor_table[src & 3]); 3008c2ecf20Sopenharmony_ci int adc = (val * sr_adc_mult_table[sr]) / SR_MULT; 3018c2ecf20Sopenharmony_ci int dac = (val * sr_dac_mult_table[sr]) / SR_MULT; 3028c2ecf20Sopenharmony_ci if (src & TLV320AIC23_CLKIN_HALF) { 3038c2ecf20Sopenharmony_ci adc >>= 1; 3048c2ecf20Sopenharmony_ci dac >>= 1; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci *sample_rate_adc = adc; 3078c2ecf20Sopenharmony_ci *sample_rate_dac = dac; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci#endif 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int set_sample_rate_control(struct snd_soc_component *component, int mclk, 3128c2ecf20Sopenharmony_ci u32 sample_rate_adc, u32 sample_rate_dac) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci /* Search for the right sample rate */ 3158c2ecf20Sopenharmony_ci int data = find_rate(mclk, sample_rate_adc, sample_rate_dac); 3168c2ecf20Sopenharmony_ci if (data < 0) { 3178c2ecf20Sopenharmony_ci printk(KERN_ERR "%s:Invalid rate %u,%u requested\n", 3188c2ecf20Sopenharmony_ci __func__, sample_rate_adc, sample_rate_dac); 3198c2ecf20Sopenharmony_ci return -EINVAL; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_SRATE, data); 3228c2ecf20Sopenharmony_ci#ifdef DEBUG 3238c2ecf20Sopenharmony_ci { 3248c2ecf20Sopenharmony_ci u32 adc, dac; 3258c2ecf20Sopenharmony_ci get_current_sample_rates(component, mclk, &adc, &dac); 3268c2ecf20Sopenharmony_ci printk(KERN_DEBUG "actual samplerate = %u,%u reg=%x\n", 3278c2ecf20Sopenharmony_ci adc, dac, data); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci#endif 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic int tlv320aic23_hw_params(struct snd_pcm_substream *substream, 3348c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 3358c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 3388c2ecf20Sopenharmony_ci u16 iface_reg; 3398c2ecf20Sopenharmony_ci int ret; 3408c2ecf20Sopenharmony_ci struct aic23 *aic23 = snd_soc_component_get_drvdata(component); 3418c2ecf20Sopenharmony_ci u32 sample_rate_adc = aic23->requested_adc; 3428c2ecf20Sopenharmony_ci u32 sample_rate_dac = aic23->requested_dac; 3438c2ecf20Sopenharmony_ci u32 sample_rate = params_rate(params); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 3468c2ecf20Sopenharmony_ci aic23->requested_dac = sample_rate_dac = sample_rate; 3478c2ecf20Sopenharmony_ci if (!sample_rate_adc) 3488c2ecf20Sopenharmony_ci sample_rate_adc = sample_rate; 3498c2ecf20Sopenharmony_ci } else { 3508c2ecf20Sopenharmony_ci aic23->requested_adc = sample_rate_adc = sample_rate; 3518c2ecf20Sopenharmony_ci if (!sample_rate_dac) 3528c2ecf20Sopenharmony_ci sample_rate_dac = sample_rate; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci ret = set_sample_rate_control(component, aic23->mclk, sample_rate_adc, 3558c2ecf20Sopenharmony_ci sample_rate_dac); 3568c2ecf20Sopenharmony_ci if (ret < 0) 3578c2ecf20Sopenharmony_ci return ret; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci iface_reg = snd_soc_component_read(component, TLV320AIC23_DIGT_FMT) & ~(0x03 << 2); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci switch (params_width(params)) { 3628c2ecf20Sopenharmony_ci case 16: 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci case 20: 3658c2ecf20Sopenharmony_ci iface_reg |= (0x01 << 2); 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci case 24: 3688c2ecf20Sopenharmony_ci iface_reg |= (0x02 << 2); 3698c2ecf20Sopenharmony_ci break; 3708c2ecf20Sopenharmony_ci case 32: 3718c2ecf20Sopenharmony_ci iface_reg |= (0x03 << 2); 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_DIGT_FMT, iface_reg); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream, 3808c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* set active */ 3858c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_ACTIVE, 0x0001); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic void tlv320aic23_shutdown(struct snd_pcm_substream *substream, 3918c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 3948c2ecf20Sopenharmony_ci struct aic23 *aic23 = snd_soc_component_get_drvdata(component); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* deactivate */ 3978c2ecf20Sopenharmony_ci if (!snd_soc_component_active(component)) { 3988c2ecf20Sopenharmony_ci udelay(50); 3998c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_ACTIVE, 0x0); 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 4028c2ecf20Sopenharmony_ci aic23->requested_dac = 0; 4038c2ecf20Sopenharmony_ci else 4048c2ecf20Sopenharmony_ci aic23->requested_adc = 0; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int tlv320aic23_mute(struct snd_soc_dai *dai, int mute, int direction) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 4108c2ecf20Sopenharmony_ci u16 reg; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci reg = snd_soc_component_read(component, TLV320AIC23_DIGT); 4138c2ecf20Sopenharmony_ci if (mute) 4148c2ecf20Sopenharmony_ci reg |= TLV320AIC23_DACM_MUTE; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci else 4178c2ecf20Sopenharmony_ci reg &= ~TLV320AIC23_DACM_MUTE; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_DIGT, reg); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai, 4258c2ecf20Sopenharmony_ci unsigned int fmt) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 4288c2ecf20Sopenharmony_ci u16 iface_reg; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci iface_reg = snd_soc_component_read(component, TLV320AIC23_DIGT_FMT) & (~0x03); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* set master/slave audio interface */ 4338c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 4348c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 4358c2ecf20Sopenharmony_ci iface_reg |= TLV320AIC23_MS_MASTER; 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 4388c2ecf20Sopenharmony_ci iface_reg &= ~TLV320AIC23_MS_MASTER; 4398c2ecf20Sopenharmony_ci break; 4408c2ecf20Sopenharmony_ci default: 4418c2ecf20Sopenharmony_ci return -EINVAL; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* interface format */ 4468c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 4478c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 4488c2ecf20Sopenharmony_ci iface_reg |= TLV320AIC23_FOR_I2S; 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 4518c2ecf20Sopenharmony_ci iface_reg |= TLV320AIC23_LRP_ON; 4528c2ecf20Sopenharmony_ci fallthrough; 4538c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 4548c2ecf20Sopenharmony_ci iface_reg |= TLV320AIC23_FOR_DSP; 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 4598c2ecf20Sopenharmony_ci iface_reg |= TLV320AIC23_FOR_LJUST; 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci default: 4628c2ecf20Sopenharmony_ci return -EINVAL; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_DIGT_FMT, iface_reg); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return 0; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic int tlv320aic23_set_dai_sysclk(struct snd_soc_dai *codec_dai, 4728c2ecf20Sopenharmony_ci int clk_id, unsigned int freq, int dir) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct aic23 *aic23 = snd_soc_dai_get_drvdata(codec_dai); 4758c2ecf20Sopenharmony_ci aic23->mclk = freq; 4768c2ecf20Sopenharmony_ci return 0; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic int tlv320aic23_set_bias_level(struct snd_soc_component *component, 4808c2ecf20Sopenharmony_ci enum snd_soc_bias_level level) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci u16 reg = snd_soc_component_read(component, TLV320AIC23_PWR) & 0x17f; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci switch (level) { 4858c2ecf20Sopenharmony_ci case SND_SOC_BIAS_ON: 4868c2ecf20Sopenharmony_ci /* vref/mid, osc on, dac unmute */ 4878c2ecf20Sopenharmony_ci reg &= ~(TLV320AIC23_DEVICE_PWR_OFF | TLV320AIC23_OSC_OFF | \ 4888c2ecf20Sopenharmony_ci TLV320AIC23_DAC_OFF); 4898c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_PWR, reg); 4908c2ecf20Sopenharmony_ci break; 4918c2ecf20Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 4928c2ecf20Sopenharmony_ci break; 4938c2ecf20Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 4948c2ecf20Sopenharmony_ci /* everything off except vref/vmid, */ 4958c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_PWR, 4968c2ecf20Sopenharmony_ci reg | TLV320AIC23_CLK_OFF); 4978c2ecf20Sopenharmony_ci break; 4988c2ecf20Sopenharmony_ci case SND_SOC_BIAS_OFF: 4998c2ecf20Sopenharmony_ci /* everything off, dac mute, inactive */ 5008c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_ACTIVE, 0x0); 5018c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_PWR, 0x1ff); 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci#define AIC23_RATES SNDRV_PCM_RATE_8000_96000 5088c2ecf20Sopenharmony_ci#define AIC23_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 5098c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops tlv320aic23_dai_ops = { 5128c2ecf20Sopenharmony_ci .prepare = tlv320aic23_pcm_prepare, 5138c2ecf20Sopenharmony_ci .hw_params = tlv320aic23_hw_params, 5148c2ecf20Sopenharmony_ci .shutdown = tlv320aic23_shutdown, 5158c2ecf20Sopenharmony_ci .mute_stream = tlv320aic23_mute, 5168c2ecf20Sopenharmony_ci .set_fmt = tlv320aic23_set_dai_fmt, 5178c2ecf20Sopenharmony_ci .set_sysclk = tlv320aic23_set_dai_sysclk, 5188c2ecf20Sopenharmony_ci .no_capture_mute = 1, 5198c2ecf20Sopenharmony_ci}; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver tlv320aic23_dai = { 5228c2ecf20Sopenharmony_ci .name = "tlv320aic23-hifi", 5238c2ecf20Sopenharmony_ci .playback = { 5248c2ecf20Sopenharmony_ci .stream_name = "Playback", 5258c2ecf20Sopenharmony_ci .channels_min = 2, 5268c2ecf20Sopenharmony_ci .channels_max = 2, 5278c2ecf20Sopenharmony_ci .rates = AIC23_RATES, 5288c2ecf20Sopenharmony_ci .formats = AIC23_FORMATS,}, 5298c2ecf20Sopenharmony_ci .capture = { 5308c2ecf20Sopenharmony_ci .stream_name = "Capture", 5318c2ecf20Sopenharmony_ci .channels_min = 2, 5328c2ecf20Sopenharmony_ci .channels_max = 2, 5338c2ecf20Sopenharmony_ci .rates = AIC23_RATES, 5348c2ecf20Sopenharmony_ci .formats = AIC23_FORMATS,}, 5358c2ecf20Sopenharmony_ci .ops = &tlv320aic23_dai_ops, 5368c2ecf20Sopenharmony_ci}; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic int tlv320aic23_resume(struct snd_soc_component *component) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct aic23 *aic23 = snd_soc_component_get_drvdata(component); 5418c2ecf20Sopenharmony_ci regcache_mark_dirty(aic23->regmap); 5428c2ecf20Sopenharmony_ci regcache_sync(aic23->regmap); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci return 0; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic int tlv320aic23_component_probe(struct snd_soc_component *component) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci /* Reset codec */ 5508c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_RESET, 0); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_DIGT, TLV320AIC23_DEEMP_44K); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* Unmute input */ 5558c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, TLV320AIC23_LINVOL, 5568c2ecf20Sopenharmony_ci TLV320AIC23_LIM_MUTED, TLV320AIC23_LRS_ENABLED); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, TLV320AIC23_RINVOL, 5598c2ecf20Sopenharmony_ci TLV320AIC23_LIM_MUTED, TLV320AIC23_LRS_ENABLED); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, TLV320AIC23_ANLG, 5628c2ecf20Sopenharmony_ci TLV320AIC23_BYPASS_ON | TLV320AIC23_MICM_MUTED, 5638c2ecf20Sopenharmony_ci 0); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* Default output volume */ 5668c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_LCHNVOL, 5678c2ecf20Sopenharmony_ci TLV320AIC23_DEFAULT_OUT_VOL & TLV320AIC23_OUT_VOL_MASK); 5688c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_RCHNVOL, 5698c2ecf20Sopenharmony_ci TLV320AIC23_DEFAULT_OUT_VOL & TLV320AIC23_OUT_VOL_MASK); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci snd_soc_component_write(component, TLV320AIC23_ACTIVE, 0x1); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci return 0; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_tlv320aic23 = { 5778c2ecf20Sopenharmony_ci .probe = tlv320aic23_component_probe, 5788c2ecf20Sopenharmony_ci .resume = tlv320aic23_resume, 5798c2ecf20Sopenharmony_ci .set_bias_level = tlv320aic23_set_bias_level, 5808c2ecf20Sopenharmony_ci .controls = tlv320aic23_snd_controls, 5818c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(tlv320aic23_snd_controls), 5828c2ecf20Sopenharmony_ci .dapm_widgets = tlv320aic23_dapm_widgets, 5838c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets), 5848c2ecf20Sopenharmony_ci .dapm_routes = tlv320aic23_intercon, 5858c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon), 5868c2ecf20Sopenharmony_ci .suspend_bias_off = 1, 5878c2ecf20Sopenharmony_ci .idle_bias_on = 1, 5888c2ecf20Sopenharmony_ci .use_pmdown_time = 1, 5898c2ecf20Sopenharmony_ci .endianness = 1, 5908c2ecf20Sopenharmony_ci .non_legacy_dai_naming = 1, 5918c2ecf20Sopenharmony_ci}; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ciint tlv320aic23_probe(struct device *dev, struct regmap *regmap) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct aic23 *aic23; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) 5988c2ecf20Sopenharmony_ci return PTR_ERR(regmap); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci aic23 = devm_kzalloc(dev, sizeof(struct aic23), GFP_KERNEL); 6018c2ecf20Sopenharmony_ci if (aic23 == NULL) 6028c2ecf20Sopenharmony_ci return -ENOMEM; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci aic23->regmap = regmap; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci dev_set_drvdata(dev, aic23); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci return devm_snd_soc_register_component(dev, 6098c2ecf20Sopenharmony_ci &soc_component_dev_tlv320aic23, 6108c2ecf20Sopenharmony_ci &tlv320aic23_dai, 1); 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tlv320aic23_probe); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver"); 6158c2ecf20Sopenharmony_ciMODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>"); 6168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 617