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