brintos

brintos / linux-shallow public Read only

0
0
Text · 25.9 KiB · da52afe Raw
972 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// CS530x CODEC driver4//5// Copyright (C) 2024 Cirrus Logic, Inc. and6//                    Cirrus Logic International Semiconductor Ltd.7 8#include <sound/core.h>9#include <linux/delay.h>10#include <linux/i2c.h>11#include <linux/init.h>12#include <sound/initval.h>13#include <linux/module.h>14#include <sound/pcm.h>15#include <sound/pcm_params.h>16#include <linux/pm.h>17#include <linux/property.h>18#include <linux/slab.h>19#include <sound/soc.h>20#include <sound/tlv.h>21 22#include "cs530x.h"23 24#define CS530X_MAX_ADC_CH	825#define CS530X_MIN_ADC_CH	226 27static const char *cs530x_supply_names[CS530X_NUM_SUPPLIES] = {28	"vdd-a",29	"vdd-io",30};31 32static const struct reg_default cs530x_reg_defaults[] = {33	{ CS530X_CLK_CFG_0, 0x30 },34	{ CS530X_CLK_CFG_1, 0x0001 },35	{ CS530X_CHIP_ENABLE, 0 },36	{ CS530X_ASP_CFG, 0 },37	{ CS530X_SIGNAL_PATH_CFG, 0 },38	{ CS530X_IN_ENABLES, 0 },39	{ CS530X_IN_RAMP_SUM, 0x0022 },40	{ CS530X_IN_FILTER, 0 },41	{ CS530X_IN_HIZ, 0 },42	{ CS530X_IN_INV, 0 },43	{ CS530X_IN_VOL_CTRL1_0, 0x8000 },44	{ CS530X_IN_VOL_CTRL1_1, 0x8000 },45	{ CS530X_IN_VOL_CTRL2_0, 0x8000 },46	{ CS530X_IN_VOL_CTRL2_1, 0x8000 },47	{ CS530X_IN_VOL_CTRL3_0, 0x8000 },48	{ CS530X_IN_VOL_CTRL3_1, 0x8000 },49	{ CS530X_IN_VOL_CTRL4_0, 0x8000 },50	{ CS530X_IN_VOL_CTRL4_1, 0x8000 },51	{ CS530X_PAD_FN, 0 },52	{ CS530X_PAD_LVL, 0 },53};54 55static bool cs530x_read_and_write_regs(unsigned int reg)56{57	switch (reg) {58	case CS530X_CLK_CFG_0:59	case CS530X_CLK_CFG_1:60	case CS530X_CHIP_ENABLE:61	case CS530X_ASP_CFG:62	case CS530X_SIGNAL_PATH_CFG:63	case CS530X_IN_ENABLES:64	case CS530X_IN_RAMP_SUM:65	case CS530X_IN_FILTER:66	case CS530X_IN_HIZ:67	case CS530X_IN_INV:68	case CS530X_IN_VOL_CTRL1_0:69	case CS530X_IN_VOL_CTRL1_1:70	case CS530X_IN_VOL_CTRL2_0:71	case CS530X_IN_VOL_CTRL2_1:72	case CS530X_IN_VOL_CTRL3_0:73	case CS530X_IN_VOL_CTRL3_1:74	case CS530X_IN_VOL_CTRL4_0:75	case CS530X_IN_VOL_CTRL4_1:76	case CS530X_PAD_FN:77	case CS530X_PAD_LVL:78		return true;79	default:80		return false;81	}82}83 84static bool cs530x_readable_register(struct device *dev, unsigned int reg)85{86	switch (reg) {87	case CS530X_DEVID:88	case CS530X_REVID:89		return true;90	default:91		return cs530x_read_and_write_regs(reg);92	}93}94 95static bool cs530x_writeable_register(struct device *dev, unsigned int reg)96{97	switch (reg) {98	case CS530X_SW_RESET:99	case CS530X_IN_VOL_CTRL5:100		return true;101	default:102		return cs530x_read_and_write_regs(reg);103	}104}105 106static int cs530x_put_volsw_vu(struct snd_kcontrol *kcontrol,107				struct snd_ctl_elem_value *ucontrol)108{109	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);110	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);111	struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);112	struct regmap *regmap = cs530x->regmap;113	int ret;114 115	snd_soc_dapm_mutex_lock(dapm);116 117	ret = snd_soc_put_volsw(kcontrol, ucontrol);118	if (ret)119		goto volsw_err;120 121	/* Write IN_VU bit for the volume change to take effect */122	regmap_write(regmap, CS530X_IN_VOL_CTRL5, CS530X_IN_VU);123 124volsw_err:125	snd_soc_dapm_mutex_unlock(dapm);126 127	return ret;128}129 130static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1270, 50, 0);131 132static const char * const cs530x_in_filter_text[] = {133	"Min Phase Slow Roll-off",134	"Min Phase Fast Roll-off",135	"Linear Phase Slow Roll-off",136	"Linear Phase Fast Roll-off",137};138 139static SOC_ENUM_SINGLE_DECL(cs530x_in_filter_enum, CS530X_IN_FILTER,140			    CS530X_IN_FILTER_SHIFT,141			    cs530x_in_filter_text);142 143static const char * const cs530x_in_4ch_sum_text[] = {144	"None",145	"Groups of 2",146	"Groups of 4",147};148 149static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch4_enum, CS530X_IN_RAMP_SUM,150			    CS530X_IN_SUM_MODE_SHIFT,151			    cs530x_in_4ch_sum_text);152 153static const struct snd_kcontrol_new cs530x_in_sum_4ch_controls[] = {154SOC_ENUM("IN Sum Select", cs530x_in_sum_ch4_enum),155};156 157static const char * const cs530x_in_8ch_sum_text[] = {158	"None",159	"Groups of 2",160	"Groups of 4",161	"Groups of 8",162};163 164static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch8_enum, CS530X_IN_RAMP_SUM,165			    CS530X_IN_SUM_MODE_SHIFT,166			    cs530x_in_8ch_sum_text);167 168static const struct snd_kcontrol_new cs530x_in_sum_8ch_controls[] = {169SOC_ENUM("IN Sum Select", cs530x_in_sum_ch8_enum),170};171 172 173static const char * const cs530x_vol_ramp_text[] = {174	"0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",175	"15ms/6dB", "30ms/6dB",176};177 178static SOC_ENUM_SINGLE_DECL(cs530x_ramp_inc_enum, CS530X_IN_RAMP_SUM,179			    CS530X_RAMP_RATE_INC_SHIFT,180			    cs530x_vol_ramp_text);181 182static SOC_ENUM_SINGLE_DECL(cs530x_ramp_dec_enum, CS530X_IN_RAMP_SUM,183			    CS530X_RAMP_RATE_DEC_SHIFT,184			    cs530x_vol_ramp_text);185 186static const struct snd_kcontrol_new cs530x_in_1_to_2_controls[] = {187SOC_SINGLE_EXT_TLV("IN1 Volume", CS530X_IN_VOL_CTRL1_0, 0, 255, 1,188		    snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),189SOC_SINGLE_EXT_TLV("IN2 Volume", CS530X_IN_VOL_CTRL1_1, 0, 255, 1,190		    snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),191 192SOC_ENUM("IN DEC Filter Select", cs530x_in_filter_enum),193SOC_ENUM("Input Ramp Up", cs530x_ramp_inc_enum),194SOC_ENUM("Input Ramp Down", cs530x_ramp_dec_enum),195 196SOC_SINGLE("ADC1 Invert Switch", CS530X_IN_INV, CS530X_IN1_INV_SHIFT, 1, 0),197SOC_SINGLE("ADC2 Invert Switch", CS530X_IN_INV, CS530X_IN2_INV_SHIFT, 1, 0),198};199 200static const struct snd_kcontrol_new cs530x_in_3_to_4_controls[] = {201SOC_SINGLE_EXT_TLV("IN3 Volume", CS530X_IN_VOL_CTRL2_0, 0, 255, 1,202		    snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),203SOC_SINGLE_EXT_TLV("IN4 Volume", CS530X_IN_VOL_CTRL2_1, 0, 255, 1,204		    snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),205 206SOC_SINGLE("ADC3 Invert Switch", CS530X_IN_INV, CS530X_IN3_INV_SHIFT, 1, 0),207SOC_SINGLE("ADC4 Invert Switch", CS530X_IN_INV, CS530X_IN4_INV_SHIFT, 1, 0),208};209 210static const struct snd_kcontrol_new cs530x_in_5_to_8_controls[] = {211SOC_SINGLE_EXT_TLV("IN5 Volume", CS530X_IN_VOL_CTRL3_0, 0, 255, 1,212		    snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),213SOC_SINGLE_EXT_TLV("IN6 Volume", CS530X_IN_VOL_CTRL3_1, 0, 255, 1,214		    snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),215SOC_SINGLE_EXT_TLV("IN7 Volume", CS530X_IN_VOL_CTRL4_0, 0, 255, 1,216		    snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),217SOC_SINGLE_EXT_TLV("IN8 Volume", CS530X_IN_VOL_CTRL4_1, 0, 255, 1,218		    snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),219 220SOC_SINGLE("ADC5 Invert Switch", CS530X_IN_INV, CS530X_IN5_INV_SHIFT, 1, 0),221SOC_SINGLE("ADC6 Invert Switch", CS530X_IN_INV, CS530X_IN6_INV_SHIFT, 1, 0),222SOC_SINGLE("ADC7 Invert Switch", CS530X_IN_INV, CS530X_IN7_INV_SHIFT, 1, 0),223SOC_SINGLE("ADC8 Invert Switch", CS530X_IN_INV, CS530X_IN8_INV_SHIFT, 1, 0),224};225 226static int cs530x_adc_event(struct snd_soc_dapm_widget *w,227			     struct snd_kcontrol *kcontrol, int event)228{229	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);230	struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);231	struct regmap *regmap = cs530x->regmap;232 233	switch (event) {234	case SND_SOC_DAPM_PRE_PMU:235		cs530x->adc_pairs_count++;236		break;237	case SND_SOC_DAPM_POST_PMU:238		regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 +239				 (w->shift * 2), CS530X_IN_MUTE);240		regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 +241				 ((w->shift+1) * 2), CS530X_IN_MUTE);242 243		cs530x->adc_pairs_count--;244		if (!cs530x->adc_pairs_count) {245			usleep_range(1000, 1100);246			return regmap_write(regmap, CS530X_IN_VOL_CTRL5,247					    CS530X_IN_VU);248		}249		break;250	case SND_SOC_DAPM_PRE_PMD:251		regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 +252			       (w->shift * 2), CS530X_IN_MUTE);253		regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 +254			       ((w->shift+1) * 2), CS530X_IN_MUTE);255		return regmap_write(regmap, CS530X_IN_VOL_CTRL5,256				    CS530X_IN_VU);257	default:258		return -EINVAL;259	}260 261	return 0;262}263 264static const struct snd_kcontrol_new adc12_ctrl =265	SOC_DAPM_SINGLE_VIRT("Switch", 1);266 267static const struct snd_kcontrol_new adc34_ctrl =268	SOC_DAPM_SINGLE_VIRT("Switch", 1);269 270static const struct snd_kcontrol_new adc56_ctrl =271	SOC_DAPM_SINGLE_VIRT("Switch", 1);272 273static const struct snd_kcontrol_new adc78_ctrl =274	SOC_DAPM_SINGLE_VIRT("Switch", 1);275 276static const struct snd_kcontrol_new in_hpf_ctrl =277	SOC_DAPM_SINGLE_VIRT("Switch", 1);278 279/* General DAPM widgets for all devices */280static const struct snd_soc_dapm_widget cs530x_gen_dapm_widgets[] = {281SND_SOC_DAPM_SUPPLY("Global Enable", CS530X_CHIP_ENABLE, 0, 0, NULL, 0),282};283 284/* ADC's Channels 1 and 2 plus generic ADC DAPM events */285static const struct snd_soc_dapm_widget cs530x_adc_ch12_dapm_widgets[] = {286SND_SOC_DAPM_INPUT("IN1"),287SND_SOC_DAPM_INPUT("IN2"),288SND_SOC_DAPM_ADC_E("ADC1", NULL, CS530X_IN_ENABLES, 0, 0,289		   cs530x_adc_event,290		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |291		   SND_SOC_DAPM_PRE_PMU),292SND_SOC_DAPM_ADC("ADC2", NULL, CS530X_IN_ENABLES, 1, 0),293SND_SOC_DAPM_SWITCH("ADC12 Enable", SND_SOC_NOPM, 0, 0, &adc12_ctrl),294SND_SOC_DAPM_SWITCH("IN HPF", CS530X_IN_FILTER, CS530X_IN_HPF_EN_SHIFT,295		    0, &in_hpf_ctrl),296};297 298/* ADC's Channels 3 and 4 */299static const struct snd_soc_dapm_widget cs530x_adc_ch34_dapm_widgets[] = {300SND_SOC_DAPM_INPUT("IN3"),301SND_SOC_DAPM_INPUT("IN4"),302SND_SOC_DAPM_ADC_E("ADC3", NULL, CS530X_IN_ENABLES, 2, 0,303		   cs530x_adc_event,304		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |305		   SND_SOC_DAPM_PRE_PMU),306SND_SOC_DAPM_ADC("ADC4", NULL, CS530X_IN_ENABLES, 3, 0),307SND_SOC_DAPM_SWITCH("ADC34 Enable", SND_SOC_NOPM, 0, 0, &adc34_ctrl),308};309 310/* ADC's Channels 5 to 8 */311static const struct snd_soc_dapm_widget cs530x_adc_ch58_dapm_widgets[] = {312SND_SOC_DAPM_INPUT("IN5"),313SND_SOC_DAPM_INPUT("IN6"),314SND_SOC_DAPM_INPUT("IN7"),315SND_SOC_DAPM_INPUT("IN8"),316SND_SOC_DAPM_ADC_E("ADC5", NULL, CS530X_IN_ENABLES, 4, 0,317		   cs530x_adc_event,318		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |319		   SND_SOC_DAPM_PRE_PMU),320SND_SOC_DAPM_ADC("ADC6", NULL, CS530X_IN_ENABLES, 5, 0),321SND_SOC_DAPM_SWITCH("ADC56 Enable", SND_SOC_NOPM, 0, 0, &adc56_ctrl),322SND_SOC_DAPM_ADC_E("ADC7", NULL, CS530X_IN_ENABLES, 6, 0,323		   cs530x_adc_event,324		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |325		   SND_SOC_DAPM_PRE_PMU),326SND_SOC_DAPM_ADC("ADC8", NULL, CS530X_IN_ENABLES, 7, 0),327SND_SOC_DAPM_SWITCH("ADC78 Enable", SND_SOC_NOPM, 0, 0, &adc78_ctrl),328};329 330static const struct snd_soc_dapm_route adc_ch1_2_routes[] = {331	{ "ADC1", NULL, "Global Enable" },332	{ "ADC2", NULL, "Global Enable" },333 334	{ "ADC12 Enable", "Switch", "IN1" },335	{ "ADC12 Enable", "Switch", "IN2" },336	{ "ADC1", NULL, "ADC12 Enable" },337	{ "ADC2", NULL, "ADC12 Enable" },338	{ "IN HPF", "Switch", "ADC1" },339	{ "IN HPF", "Switch", "ADC2" },340 341	{ "AIF Capture", NULL, "IN HPF" },342	{ "AIF Capture", NULL, "ADC1" },343	{ "AIF Capture", NULL, "ADC2" },344};345 346static const struct snd_soc_dapm_route adc_ch3_4_routes[] = {347	{ "ADC3", NULL, "Global Enable" },348	{ "ADC4", NULL, "Global Enable" },349 350	{ "ADC34 Enable", "Switch", "IN3" },351	{ "ADC34 Enable", "Switch", "IN4" },352	{ "ADC3", NULL, "ADC34 Enable" },353	{ "ADC4", NULL, "ADC34 Enable" },354	{ "IN HPF", "Switch", "ADC3" },355	{ "IN HPF", "Switch", "ADC4" },356 357	{ "AIF Capture", NULL, "ADC3" },358	{ "AIF Capture", NULL, "ADC4" },359};360 361static const struct snd_soc_dapm_route adc_ch5_8_routes[] = {362	{ "ADC5", NULL, "Global Enable" },363	{ "ADC6", NULL, "Global Enable" },364	{ "ADC7", NULL, "Global Enable" },365	{ "ADC8", NULL, "Global Enable" },366 367	{ "ADC56 Enable", "Switch", "IN5" },368	{ "ADC56 Enable", "Switch", "IN6" },369	{ "ADC5", NULL, "ADC56 Enable" },370	{ "ADC6", NULL, "ADC56 Enable" },371	{ "IN HPF", "Switch", "ADC5" },372	{ "IN HPF", "Switch", "ADC6" },373 374	{ "AIF Capture", NULL, "ADC5" },375	{ "AIF Capture", NULL, "ADC6" },376 377	{ "ADC78 Enable", "Switch", "IN7" },378	{ "ADC78 Enable", "Switch", "IN8" },379	{ "ADC7", NULL, "ADC78 Enable" },380	{ "ADC8", NULL, "ADC78 Enable" },381	{ "IN HPF", "Switch", "ADC7" },382	{ "IN HPF", "Switch", "ADC8" },383 384	{ "AIF Capture", NULL, "ADC7" },385	{ "AIF Capture", NULL, "ADC8" },386};387 388static void cs530x_add_12_adc_widgets(struct snd_soc_component *component)389{390	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);391 392	snd_soc_add_component_controls(component,393				       cs530x_in_1_to_2_controls,394				       ARRAY_SIZE(cs530x_in_1_to_2_controls));395 396	snd_soc_dapm_new_controls(dapm, cs530x_adc_ch12_dapm_widgets,397				  ARRAY_SIZE(cs530x_adc_ch12_dapm_widgets));398 399	snd_soc_dapm_add_routes(dapm, adc_ch1_2_routes,400				ARRAY_SIZE(adc_ch1_2_routes));401}402 403static void cs530x_add_34_adc_widgets(struct snd_soc_component *component)404{405	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);406 407	snd_soc_add_component_controls(component,408				       cs530x_in_3_to_4_controls,409				       ARRAY_SIZE(cs530x_in_3_to_4_controls));410 411	snd_soc_dapm_new_controls(dapm, cs530x_adc_ch34_dapm_widgets,412				  ARRAY_SIZE(cs530x_adc_ch34_dapm_widgets));413 414	snd_soc_dapm_add_routes(dapm, adc_ch3_4_routes,415				ARRAY_SIZE(adc_ch3_4_routes));416}417 418static int cs530x_set_bclk(struct snd_soc_component *component, const int freq)419{420	struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);421	struct regmap *regmap = cs530x->regmap;422	unsigned int bclk_val;423 424	switch (freq) {425	case 2822400:426	case 3072000:427		bclk_val = CS530X_BCLK_2P822_3P072;428		break;429	case 5644800:430	case 6144000:431		bclk_val = CS530X_BCLK_5P6448_6P144;432		break;433	case 11289600:434	case 12288000:435		bclk_val = CS530X_BCLK_11P2896_12P288;436		break;437	case 22579200:438	case 24576000:439		bclk_val = CS530X_BCLK_24P5792_24P576;440		break;441	default:442		dev_err(component->dev, "Invalid BCLK frequency %d\n", freq);443		return -EINVAL;444	}445 446	dev_dbg(component->dev, "BCLK frequency is %d\n", freq);447 448	return regmap_update_bits(regmap, CS530X_ASP_CFG,449				  CS530X_ASP_BCLK_FREQ_MASK, bclk_val);450}451 452static int cs530x_set_pll_refclk(struct snd_soc_component *component,453				  const unsigned int freq)454{455	struct cs530x_priv *priv = snd_soc_component_get_drvdata(component);456	struct regmap *regmap = priv->regmap;457	unsigned int refclk;458 459	switch (freq) {460	case 2822400:461	case 3072000:462		refclk = CS530X_REFCLK_2P822_3P072;463		break;464	case 5644800:465	case 6144000:466		refclk = CS530X_REFCLK_5P6448_6P144;467		break;468	case 11289600:469	case 12288000:470		refclk = CS530X_REFCLK_11P2896_12P288;471		break;472	case 22579200:473	case 24576000:474		refclk = CS530X_REFCLK_24P5792_24P576;475		break;476	default:477		dev_err(component->dev, "Invalid PLL refclk %d\n", freq);478		return -EINVAL;479	}480 481	return regmap_update_bits(regmap, CS530X_CLK_CFG_0,482				  CS530X_PLL_REFCLK_FREQ_MASK, refclk);483}484 485static int cs530x_hw_params(struct snd_pcm_substream *substream,486			    struct snd_pcm_hw_params *params,487			    struct snd_soc_dai *dai)488{489	struct snd_soc_component *component = dai->component;490	struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);491	struct regmap *regmap = cs530x->regmap;492	int ret = 0, fs = params_rate(params), bclk;493	unsigned int fs_val;494 495 496	switch (fs) {497	case 32000:498		fs_val = CS530X_FS_32K;499		break;500	case 44100:501	case 48000:502		fs_val = CS530X_FS_48K_44P1K;503		break;504	case 88200:505	case 96000:506		fs_val = CS530X_FS_96K_88P2K;507		break;508	case 176400:509	case 192000:510		fs_val = CS530X_FS_192K_176P4K;511		break;512	case 356800:513	case 384000:514		fs_val = CS530X_FS_384K_356P8K;515		break;516	case 705600:517	case 768000:518		fs_val = CS530X_FS_768K_705P6K;519		break;520	default:521		dev_err(component->dev, "Invalid sample rate %d\n", fs);522		return -EINVAL;523	}524 525	cs530x->fs = fs;526	regmap_update_bits(regmap, CS530X_CLK_CFG_1,527			   CS530X_SAMPLE_RATE_MASK, fs_val);528 529 530	if (regmap_test_bits(regmap, CS530X_SIGNAL_PATH_CFG,531			     CS530X_TDM_EN_MASK)) {532		dev_dbg(component->dev, "Configuring for %d %d bit TDM slots\n",533			cs530x->tdm_slots, cs530x->tdm_width);534		bclk = snd_soc_tdm_params_to_bclk(params,535						  cs530x->tdm_width,536						  cs530x->tdm_slots,537						  1);538	} else {539		bclk = snd_soc_params_to_bclk(params);540	}541 542	if (!regmap_test_bits(regmap, CS530X_CLK_CFG_0,543			     CS530X_PLL_REFCLK_SRC_MASK)) {544		ret = cs530x_set_pll_refclk(component, bclk);545		if (ret)546			return ret;547	}548 549	return cs530x_set_bclk(component, bclk);550}551 552static int cs530x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)553{554	struct snd_soc_component *component = dai->component;555	struct cs530x_priv *priv = snd_soc_component_get_drvdata(component);556	struct regmap *regmap = priv->regmap;557	unsigned int asp_fmt, asp_cfg = 0;558 559	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {560	case SND_SOC_DAIFMT_CBS_CFS:561		break;562	case SND_SOC_DAIFMT_CBM_CFM:563		asp_cfg = CS530X_ASP_PRIMARY;564		break;565	default:566		return -EINVAL;567	}568 569	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {570	case SND_SOC_DAIFMT_DSP_A:571		asp_fmt = CS530X_ASP_FMT_DSP_A;572		break;573	case SND_SOC_DAIFMT_I2S:574		asp_fmt = CS530X_ASP_FMT_I2S;575		break;576	case SND_SOC_DAIFMT_LEFT_J:577		asp_fmt = CS530X_ASP_FMT_LJ;578		break;579	default:580		return -EINVAL;581	}582 583	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {584	case SND_SOC_DAIFMT_NB_NF:585		break;586	case SND_SOC_DAIFMT_IB_IF:587		asp_cfg |= CS530X_ASP_BCLK_INV;588		break;589	default:590		return -EINVAL;591	}592 593	regmap_update_bits(regmap, CS530X_ASP_CFG,594			   CS530X_ASP_PRIMARY | CS530X_ASP_BCLK_INV,595			   asp_cfg);596 597	return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG,598				  CS530X_ASP_FMT_MASK, asp_fmt);599}600 601static bool cs530x_check_mclk_freq(struct snd_soc_component *component,602				   const unsigned int freq)603{604	switch (freq) {605	case 24576000:606	case 22579200:607	case 12288000:608	case 11289600:609		return true;610	default:611		dev_err(component->dev, "Invalid MCLK %d\n", freq);612		return false;613	}614}615 616static int cs530x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,617				 unsigned int rx_mask, int slots, int slot_width)618{619	struct snd_soc_component *component = dai->component;620	struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);621	struct regmap *regmap = cs530x->regmap;622	unsigned int val;623 624	switch (tx_mask) {625	case CS530X_0_1_TDM_SLOT_MASK:626	case CS530X_0_3_TDM_SLOT_MASK:627	case CS530X_0_7_TDM_SLOT_MASK:628		val = CS530X_0_7_TDM_SLOT_VAL;629		break;630	case CS530X_2_3_TDM_SLOT_MASK:631		val = CS530X_2_3_TDM_SLOT_VAL;632		break;633	case CS530X_4_5_TDM_SLOT_MASK:634	case CS530X_4_7_TDM_SLOT_MASK:635		val = CS530X_4_7_TDM_SLOT_VAL;636		break;637	case CS530X_6_7_TDM_SLOT_MASK:638		val = CS530X_6_7_TDM_SLOT_VAL;639		break;640	case CS530X_8_9_TDM_SLOT_MASK:641	case CS530X_8_11_TDM_SLOT_MASK:642	case CS530X_8_15_TDM_SLOT_MASK:643		val = CS530X_8_15_TDM_SLOT_VAL;644		break;645	case CS530X_10_11_TDM_SLOT_MASK:646		val = CS530X_10_11_TDM_SLOT_VAL;647		break;648	case CS530X_12_13_TDM_SLOT_MASK:649	case CS530X_12_15_TDM_SLOT_MASK:650		val = CS530X_12_15_TDM_SLOT_VAL;651		break;652	case CS530X_14_15_TDM_SLOT_MASK:653		val = CS530X_14_15_TDM_SLOT_VAL;654		break;655	default:656		dev_err(component->dev, "Invalid TX slot(s) 0x%x\n", tx_mask);657		return -EINVAL;658	}659 660	cs530x->tdm_width = slot_width;661	cs530x->tdm_slots = slots;662 663	return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG,664				  CS530X_ASP_TDM_SLOT_MASK,665				  val << CS530X_ASP_TDM_SLOT_SHIFT);666}667 668static const struct snd_soc_dai_ops cs530x_dai_ops = {669	.set_fmt = cs530x_set_fmt,670	.hw_params = cs530x_hw_params,671	.set_tdm_slot = cs530x_set_tdm_slot,672};673 674static const struct snd_soc_dai_driver cs530x_dai = {675	.name = "cs530x-dai",676	.capture = {677		.stream_name = "AIF Capture",678		.channels_min = 2,679		.channels_max = 8,680		.rates = SNDRV_PCM_RATE_KNOT,681		.formats = SNDRV_PCM_FMTBIT_S32_LE,682	},683	.ops = &cs530x_dai_ops,684	.symmetric_rate = 1,685	.symmetric_sample_bits = 1,686};687 688static int cs530x_set_pll(struct snd_soc_component *component, int pll_id,689			   int source, unsigned int freq_in,690			   unsigned int freq_out)691{692	struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);693	struct regmap *regmap = cs530x->regmap;694	unsigned int sysclk_src;695	int ret;696 697	regmap_read(regmap, CS530X_CLK_CFG_0, &sysclk_src);698 699	/* Check if the source is the PLL  */700	if ((sysclk_src & CS530X_SYSCLK_SRC_MASK) == 0)701		return 0;702 703	switch (source) {704	case CS530X_PLL_SRC_MCLK:705		if (!cs530x_check_mclk_freq(component, freq_in))706			return -EINVAL;707 708		ret = cs530x_set_pll_refclk(component, freq_in);709		if (ret)710			return ret;711 712		break;713	case CS530X_PLL_SRC_BCLK:714		break;715	default:716		dev_err(component->dev, "Invalid PLL source %d\n", source);717		return -EINVAL;718	}719 720	return regmap_update_bits(regmap, CS530X_CLK_CFG_0,721				  CS530X_PLL_REFCLK_SRC_MASK, source);722}723 724static int cs530x_component_probe(struct snd_soc_component *component)725{726	struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);727	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);728	int num_widgets;729 730	snd_soc_dapm_new_controls(dapm, cs530x_gen_dapm_widgets,731				  ARRAY_SIZE(cs530x_gen_dapm_widgets));732 733	switch (cs530x->devtype) {734	case CS5302:735		cs530x_add_12_adc_widgets(component);736		break;737	case CS5304:738		cs530x_add_12_adc_widgets(component);739		cs530x_add_34_adc_widgets(component);740 741		num_widgets = ARRAY_SIZE(cs530x_in_sum_4ch_controls);742		snd_soc_add_component_controls(component,743					       cs530x_in_sum_4ch_controls,744					       num_widgets);745		break;746 747	case CS5308:748		cs530x_add_12_adc_widgets(component);749		cs530x_add_34_adc_widgets(component);750 751		num_widgets = ARRAY_SIZE(cs530x_in_5_to_8_controls);752		snd_soc_add_component_controls(component,753					       cs530x_in_5_to_8_controls,754					       num_widgets);755 756		num_widgets = ARRAY_SIZE(cs530x_in_sum_8ch_controls);757		snd_soc_add_component_controls(component,758					       cs530x_in_sum_8ch_controls,759					       num_widgets);760 761		num_widgets = ARRAY_SIZE(cs530x_adc_ch58_dapm_widgets);762		snd_soc_dapm_new_controls(dapm, cs530x_adc_ch58_dapm_widgets,763					  num_widgets);764 765		snd_soc_dapm_add_routes(dapm, adc_ch5_8_routes,766					ARRAY_SIZE(adc_ch5_8_routes));767		break;768	default:769		dev_err(component->dev, "Invalid device type %d\n",770			cs530x->devtype);771		return -EINVAL;772	}773 774	return 0;775}776 777static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id,778				int source, unsigned int freq, int dir)779{780	struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);781	struct regmap *regmap = cs530x->regmap;782 783	switch (source) {784	case CS530X_SYSCLK_SRC_MCLK:785		if (freq != 24560000 && freq != 22572000) {786			dev_err(component->dev, "Invalid MCLK source rate %d\n",787				freq);788			return -EINVAL;789		}790 791		cs530x->mclk_rate = freq;792		break;793	case CS530X_SYSCLK_SRC_PLL:794		break;795	default:796		dev_err(component->dev, "Invalid clock id %d\n", clk_id);797		return -EINVAL;798	}799 800	return regmap_update_bits(regmap, CS530X_CLK_CFG_0,801				  CS530X_SYSCLK_SRC_MASK,802				  source << CS530X_SYSCLK_SRC_SHIFT);803}804 805static const struct snd_soc_component_driver soc_component_dev_cs530x = {806	.probe			= cs530x_component_probe,807	.set_sysclk		= cs530x_set_sysclk,808	.set_pll		= cs530x_set_pll,809	.endianness		= 1,810};811 812const struct regmap_config cs530x_regmap = {813	.reg_bits = 16,814	.val_bits = 16,815 816	.max_register = CS530X_MAX_REGISTER,817	.readable_reg = cs530x_readable_register,818	.writeable_reg = cs530x_writeable_register,819 820	.cache_type = REGCACHE_MAPLE,821	.reg_defaults = cs530x_reg_defaults,822	.num_reg_defaults = ARRAY_SIZE(cs530x_reg_defaults),823};824EXPORT_SYMBOL_NS_GPL(cs530x_regmap, SND_SOC_CS530X);825 826static int cs530x_check_device_id(struct cs530x_priv *cs530x)827{828	struct device *dev = cs530x->dev;829	unsigned int dev_id, rev;830	int ret;831 832	ret = regmap_read(cs530x->regmap, CS530X_DEVID, &dev_id);833	if (ret)834		return dev_err_probe(dev, ret, "Can't read device ID\n");835 836	ret = regmap_read(cs530x->regmap, CS530X_REVID, &rev);837	if (ret)838		return dev_err_probe(dev, ret, "Can't read REV ID\n");839 840	dev_dbg(dev, "Device ID 0x%x Rev ID 0x%x\n", dev_id, rev);841 842	switch (dev_id) {843	case CS530X_2CH_ADC_DEV_ID:844		cs530x->num_adcs = 2;845		break;846	case CS530X_4CH_ADC_DEV_ID:847		cs530x->num_adcs = 4;848		break;849	case CS530X_8CH_ADC_DEV_ID:850		cs530x->num_adcs = 8;851		break;852	default:853		return dev_err_probe(dev, -EINVAL, "Invalid device ID 0x%x\n",854				     dev_id);855	}856 857	return 0;858}859 860static int cs530x_parse_device_properties(struct cs530x_priv *cs530x)861{862	struct regmap *regmap = cs530x->regmap;863	struct device *dev = cs530x->dev;864	unsigned int val = 0;865 866	switch (cs530x->num_adcs) {867	case 8:868		if (device_property_read_bool(dev, "cirrus,in-hiz-pin78"))869			val = CS530X_IN78_HIZ;870 871		if (device_property_read_bool(dev, "cirrus,in-hiz-pin56"))872			val |= CS530X_IN56_HIZ;873 874		fallthrough;875	case 4:876		if (device_property_read_bool(dev, "cirrus,in-hiz-pin34"))877			val |= CS530X_IN34_HIZ;878 879		fallthrough;880	case 2:881		if (device_property_read_bool(dev, "cirrus,in-hiz-pin12"))882			val |= CS530X_IN12_HIZ;883 884		return regmap_set_bits(regmap, CS530X_IN_HIZ, val);885	default:886		return dev_err_probe(dev, -EINVAL,887				     "Invalid number of adcs %d\n",888				     cs530x->num_adcs);889	}890}891 892int cs530x_probe(struct cs530x_priv *cs530x)893{894	struct device *dev = cs530x->dev;895	int ret, i;896 897	cs530x->dev_dai = devm_kmemdup(dev, &cs530x_dai,898					sizeof(*(cs530x->dev_dai)),899					GFP_KERNEL);900	if (!cs530x->dev_dai)901		return -ENOMEM;902 903	for (i = 0; i < ARRAY_SIZE(cs530x->supplies); i++)904		cs530x->supplies[i].supply = cs530x_supply_names[i];905 906	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs530x->supplies),907				      cs530x->supplies);908	if (ret != 0)909		return dev_err_probe(dev, ret, "Failed to request supplies");910 911	ret = regulator_bulk_enable(ARRAY_SIZE(cs530x->supplies),912				    cs530x->supplies);913	if (ret != 0)914		return dev_err_probe(dev, ret, "Failed to enable supplies");915 916	cs530x->reset_gpio = devm_gpiod_get_optional(dev, "reset",917						      GPIOD_OUT_HIGH);918	if (IS_ERR(cs530x->reset_gpio)) {919		ret = dev_err_probe(dev, PTR_ERR(cs530x->reset_gpio),920			      "Reset gpio not available\n");921		goto err_regulator;922	}923 924	if (cs530x->reset_gpio) {925		usleep_range(2000, 2100);926		gpiod_set_value_cansleep(cs530x->reset_gpio, 0);927	}928 929	usleep_range(5000, 5100);930	ret = cs530x_check_device_id(cs530x);931	if (ret)932		goto err_reset;933 934	if (!cs530x->reset_gpio) {935		ret = regmap_write(cs530x->regmap, CS530X_SW_RESET,936				   CS530X_SW_RST_VAL);937		if (ret) {938			dev_err_probe(dev, ret, "Soft Reset Failed\n");939			goto err_reset;940		}941	}942 943	ret = cs530x_parse_device_properties(cs530x);944	if (ret)945		goto err_reset;946 947	cs530x->dev_dai->capture.channels_max = cs530x->num_adcs;948 949	ret = devm_snd_soc_register_component(dev,950			&soc_component_dev_cs530x, cs530x->dev_dai, 1);951	if (ret) {952		dev_err_probe(dev, ret, "Can't register cs530x component\n");953		goto err_reset;954	}955 956	return 0;957 958err_reset:959	gpiod_set_value_cansleep(cs530x->reset_gpio, 1);960 961err_regulator:962	regulator_bulk_disable(ARRAY_SIZE(cs530x->supplies),963			       cs530x->supplies);964 965	return ret;966}967EXPORT_SYMBOL_NS_GPL(cs530x_probe, SND_SOC_CS530X);968 969MODULE_DESCRIPTION("CS530X CODEC Driver");970MODULE_AUTHOR("Paul Handrigan <paulha@opensource.cirrus.com>");971MODULE_LICENSE("GPL");972