brintos

brintos / linux-shallow public Read only

0
0
Text · 20.2 KiB · 451a8fd Raw
705 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Texas Instruments PCM186x Universal Audio ADC4 *5 * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com6 *	Andreas Dannenberg <dannenberg@ti.com>7 *	Andrew F. Davis <afd@ti.com>8 */9 10#include <linux/module.h>11#include <linux/moduleparam.h>12#include <linux/init.h>13#include <linux/delay.h>14#include <linux/pm.h>15#include <linux/regulator/consumer.h>16#include <linux/regmap.h>17#include <linux/slab.h>18#include <sound/core.h>19#include <sound/pcm.h>20#include <sound/pcm_params.h>21#include <sound/soc.h>22#include <sound/jack.h>23#include <sound/initval.h>24#include <sound/tlv.h>25 26#include "pcm186x.h"27 28static const char * const pcm186x_supply_names[] = {29	"avdd",		/* Analog power supply. Connect to 3.3-V supply. */30	"dvdd",		/* Digital power supply. Connect to 3.3-V supply. */31	"iovdd",	/* I/O power supply. Connect to 3.3-V or 1.8-V. */32};33#define PCM186x_NUM_SUPPLIES ARRAY_SIZE(pcm186x_supply_names)34 35struct pcm186x_priv {36	struct regmap *regmap;37	struct regulator_bulk_data supplies[PCM186x_NUM_SUPPLIES];38	unsigned int sysclk;39	unsigned int tdm_offset;40	bool is_tdm_mode;41	bool is_provider_mode;42};43 44static const DECLARE_TLV_DB_SCALE(pcm186x_pga_tlv, -1200, 50, 0);45 46static const struct snd_kcontrol_new pcm1863_snd_controls[] = {47	SOC_DOUBLE_R_S_TLV("ADC Capture Volume", PCM186X_PGA_VAL_CH1_L,48			   PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0,49			   pcm186x_pga_tlv),50};51 52static const struct snd_kcontrol_new pcm1865_snd_controls[] = {53	SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", PCM186X_PGA_VAL_CH1_L,54			   PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0,55			   pcm186x_pga_tlv),56	SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", PCM186X_PGA_VAL_CH2_L,57			   PCM186X_PGA_VAL_CH2_R, 0, -24, 80, 7, 0,58			   pcm186x_pga_tlv),59};60 61static const unsigned int pcm186x_adc_input_channel_sel_value[] = {62	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,63	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,64	0x10, 0x20, 0x3065};66 67static const char * const pcm186x_adcl_input_channel_sel_text[] = {68	"No Select",69	"VINL1[SE]",					/* Default for ADC1L */70	"VINL2[SE]",					/* Default for ADC2L */71	"VINL2[SE] + VINL1[SE]",72	"VINL3[SE]",73	"VINL3[SE] + VINL1[SE]",74	"VINL3[SE] + VINL2[SE]",75	"VINL3[SE] + VINL2[SE] + VINL1[SE]",76	"VINL4[SE]",77	"VINL4[SE] + VINL1[SE]",78	"VINL4[SE] + VINL2[SE]",79	"VINL4[SE] + VINL2[SE] + VINL1[SE]",80	"VINL4[SE] + VINL3[SE]",81	"VINL4[SE] + VINL3[SE] + VINL1[SE]",82	"VINL4[SE] + VINL3[SE] + VINL2[SE]",83	"VINL4[SE] + VINL3[SE] + VINL2[SE] + VINL1[SE]",84	"{VIN1P, VIN1M}[DIFF]",85	"{VIN4P, VIN4M}[DIFF]",86	"{VIN1P, VIN1M}[DIFF] + {VIN4P, VIN4M}[DIFF]"87};88 89static const char * const pcm186x_adcr_input_channel_sel_text[] = {90	"No Select",91	"VINR1[SE]",					/* Default for ADC1R */92	"VINR2[SE]",					/* Default for ADC2R */93	"VINR2[SE] + VINR1[SE]",94	"VINR3[SE]",95	"VINR3[SE] + VINR1[SE]",96	"VINR3[SE] + VINR2[SE]",97	"VINR3[SE] + VINR2[SE] + VINR1[SE]",98	"VINR4[SE]",99	"VINR4[SE] + VINR1[SE]",100	"VINR4[SE] + VINR2[SE]",101	"VINR4[SE] + VINR2[SE] + VINR1[SE]",102	"VINR4[SE] + VINR3[SE]",103	"VINR4[SE] + VINR3[SE] + VINR1[SE]",104	"VINR4[SE] + VINR3[SE] + VINR2[SE]",105	"VINR4[SE] + VINR3[SE] + VINR2[SE] + VINR1[SE]",106	"{VIN2P, VIN2M}[DIFF]",107	"{VIN3P, VIN3M}[DIFF]",108	"{VIN2P, VIN2M}[DIFF] + {VIN3P, VIN3M}[DIFF]"109};110 111static const struct soc_enum pcm186x_adc_input_channel_sel[] = {112	SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_L, 0,113			      PCM186X_ADC_INPUT_SEL_MASK,114			      ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text),115			      pcm186x_adcl_input_channel_sel_text,116			      pcm186x_adc_input_channel_sel_value),117	SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_R, 0,118			      PCM186X_ADC_INPUT_SEL_MASK,119			      ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text),120			      pcm186x_adcr_input_channel_sel_text,121			      pcm186x_adc_input_channel_sel_value),122	SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_L, 0,123			      PCM186X_ADC_INPUT_SEL_MASK,124			      ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text),125			      pcm186x_adcl_input_channel_sel_text,126			      pcm186x_adc_input_channel_sel_value),127	SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_R, 0,128			      PCM186X_ADC_INPUT_SEL_MASK,129			      ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text),130			      pcm186x_adcr_input_channel_sel_text,131			      pcm186x_adc_input_channel_sel_value),132};133 134static const struct snd_kcontrol_new pcm186x_adc_mux_controls[] = {135	SOC_DAPM_ENUM("ADC1 Left Input", pcm186x_adc_input_channel_sel[0]),136	SOC_DAPM_ENUM("ADC1 Right Input", pcm186x_adc_input_channel_sel[1]),137	SOC_DAPM_ENUM("ADC2 Left Input", pcm186x_adc_input_channel_sel[2]),138	SOC_DAPM_ENUM("ADC2 Right Input", pcm186x_adc_input_channel_sel[3]),139};140 141static const struct snd_soc_dapm_widget pcm1863_dapm_widgets[] = {142	SND_SOC_DAPM_INPUT("VINL1"),143	SND_SOC_DAPM_INPUT("VINR1"),144	SND_SOC_DAPM_INPUT("VINL2"),145	SND_SOC_DAPM_INPUT("VINR2"),146	SND_SOC_DAPM_INPUT("VINL3"),147	SND_SOC_DAPM_INPUT("VINR3"),148	SND_SOC_DAPM_INPUT("VINL4"),149	SND_SOC_DAPM_INPUT("VINR4"),150 151	SND_SOC_DAPM_MUX("ADC Left Capture Source", SND_SOC_NOPM, 0, 0,152			 &pcm186x_adc_mux_controls[0]),153	SND_SOC_DAPM_MUX("ADC Right Capture Source", SND_SOC_NOPM, 0, 0,154			 &pcm186x_adc_mux_controls[1]),155 156	/*157	 * Put the codec into SLEEP mode when not in use, allowing the158	 * Energysense mechanism to operate.159	 */160	SND_SOC_DAPM_ADC("ADC", "HiFi Capture", PCM186X_POWER_CTRL, 1,  1),161};162 163static const struct snd_soc_dapm_widget pcm1865_dapm_widgets[] = {164	SND_SOC_DAPM_INPUT("VINL1"),165	SND_SOC_DAPM_INPUT("VINR1"),166	SND_SOC_DAPM_INPUT("VINL2"),167	SND_SOC_DAPM_INPUT("VINR2"),168	SND_SOC_DAPM_INPUT("VINL3"),169	SND_SOC_DAPM_INPUT("VINR3"),170	SND_SOC_DAPM_INPUT("VINL4"),171	SND_SOC_DAPM_INPUT("VINR4"),172 173	SND_SOC_DAPM_MUX("ADC1 Left Capture Source", SND_SOC_NOPM, 0, 0,174			 &pcm186x_adc_mux_controls[0]),175	SND_SOC_DAPM_MUX("ADC1 Right Capture Source", SND_SOC_NOPM, 0, 0,176			 &pcm186x_adc_mux_controls[1]),177	SND_SOC_DAPM_MUX("ADC2 Left Capture Source", SND_SOC_NOPM, 0, 0,178			 &pcm186x_adc_mux_controls[2]),179	SND_SOC_DAPM_MUX("ADC2 Right Capture Source", SND_SOC_NOPM, 0, 0,180			 &pcm186x_adc_mux_controls[3]),181 182	/*183	 * Put the codec into SLEEP mode when not in use, allowing the184	 * Energysense mechanism to operate.185	 */186	SND_SOC_DAPM_ADC("ADC1", "HiFi Capture 1", PCM186X_POWER_CTRL, 1,  1),187	SND_SOC_DAPM_ADC("ADC2", "HiFi Capture 2", PCM186X_POWER_CTRL, 1,  1),188};189 190static const struct snd_soc_dapm_route pcm1863_dapm_routes[] = {191	{ "ADC Left Capture Source", NULL, "VINL1" },192	{ "ADC Left Capture Source", NULL, "VINR1" },193	{ "ADC Left Capture Source", NULL, "VINL2" },194	{ "ADC Left Capture Source", NULL, "VINR2" },195	{ "ADC Left Capture Source", NULL, "VINL3" },196	{ "ADC Left Capture Source", NULL, "VINR3" },197	{ "ADC Left Capture Source", NULL, "VINL4" },198	{ "ADC Left Capture Source", NULL, "VINR4" },199 200	{ "ADC", NULL, "ADC Left Capture Source" },201 202	{ "ADC Right Capture Source", NULL, "VINL1" },203	{ "ADC Right Capture Source", NULL, "VINR1" },204	{ "ADC Right Capture Source", NULL, "VINL2" },205	{ "ADC Right Capture Source", NULL, "VINR2" },206	{ "ADC Right Capture Source", NULL, "VINL3" },207	{ "ADC Right Capture Source", NULL, "VINR3" },208	{ "ADC Right Capture Source", NULL, "VINL4" },209	{ "ADC Right Capture Source", NULL, "VINR4" },210 211	{ "ADC", NULL, "ADC Right Capture Source" },212};213 214static const struct snd_soc_dapm_route pcm1865_dapm_routes[] = {215	{ "ADC1 Left Capture Source", NULL, "VINL1" },216	{ "ADC1 Left Capture Source", NULL, "VINR1" },217	{ "ADC1 Left Capture Source", NULL, "VINL2" },218	{ "ADC1 Left Capture Source", NULL, "VINR2" },219	{ "ADC1 Left Capture Source", NULL, "VINL3" },220	{ "ADC1 Left Capture Source", NULL, "VINR3" },221	{ "ADC1 Left Capture Source", NULL, "VINL4" },222	{ "ADC1 Left Capture Source", NULL, "VINR4" },223 224	{ "ADC1", NULL, "ADC1 Left Capture Source" },225 226	{ "ADC1 Right Capture Source", NULL, "VINL1" },227	{ "ADC1 Right Capture Source", NULL, "VINR1" },228	{ "ADC1 Right Capture Source", NULL, "VINL2" },229	{ "ADC1 Right Capture Source", NULL, "VINR2" },230	{ "ADC1 Right Capture Source", NULL, "VINL3" },231	{ "ADC1 Right Capture Source", NULL, "VINR3" },232	{ "ADC1 Right Capture Source", NULL, "VINL4" },233	{ "ADC1 Right Capture Source", NULL, "VINR4" },234 235	{ "ADC1", NULL, "ADC1 Right Capture Source" },236 237	{ "ADC2 Left Capture Source", NULL, "VINL1" },238	{ "ADC2 Left Capture Source", NULL, "VINR1" },239	{ "ADC2 Left Capture Source", NULL, "VINL2" },240	{ "ADC2 Left Capture Source", NULL, "VINR2" },241	{ "ADC2 Left Capture Source", NULL, "VINL3" },242	{ "ADC2 Left Capture Source", NULL, "VINR3" },243	{ "ADC2 Left Capture Source", NULL, "VINL4" },244	{ "ADC2 Left Capture Source", NULL, "VINR4" },245 246	{ "ADC2", NULL, "ADC2 Left Capture Source" },247 248	{ "ADC2 Right Capture Source", NULL, "VINL1" },249	{ "ADC2 Right Capture Source", NULL, "VINR1" },250	{ "ADC2 Right Capture Source", NULL, "VINL2" },251	{ "ADC2 Right Capture Source", NULL, "VINR2" },252	{ "ADC2 Right Capture Source", NULL, "VINL3" },253	{ "ADC2 Right Capture Source", NULL, "VINR3" },254	{ "ADC2 Right Capture Source", NULL, "VINL4" },255	{ "ADC2 Right Capture Source", NULL, "VINR4" },256 257	{ "ADC2", NULL, "ADC2 Right Capture Source" },258};259 260static int pcm186x_hw_params(struct snd_pcm_substream *substream,261			     struct snd_pcm_hw_params *params,262			     struct snd_soc_dai *dai)263{264	struct snd_soc_component *component = dai->component;265	struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);266	unsigned int rate = params_rate(params);267	snd_pcm_format_t format = params_format(params);268	unsigned int width = params_width(params);269	unsigned int channels = params_channels(params);270	unsigned int div_lrck;271	unsigned int div_bck;272	u8 tdm_tx_sel = 0;273	u8 pcm_cfg = 0;274 275	dev_dbg(component->dev, "%s() rate=%u format=0x%x width=%u channels=%u\n",276		__func__, rate, format, width, channels);277 278	switch (width) {279	case 16:280		pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_16 <<281			  PCM186X_PCM_CFG_RX_WLEN_SHIFT |282			  PCM186X_PCM_CFG_TX_WLEN_16 <<283			  PCM186X_PCM_CFG_TX_WLEN_SHIFT;284		break;285	case 20:286		pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_20 <<287			  PCM186X_PCM_CFG_RX_WLEN_SHIFT |288			  PCM186X_PCM_CFG_TX_WLEN_20 <<289			  PCM186X_PCM_CFG_TX_WLEN_SHIFT;290		break;291	case 24:292		pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_24 <<293			  PCM186X_PCM_CFG_RX_WLEN_SHIFT |294			  PCM186X_PCM_CFG_TX_WLEN_24 <<295			  PCM186X_PCM_CFG_TX_WLEN_SHIFT;296		break;297	case 32:298		pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_32 <<299			  PCM186X_PCM_CFG_RX_WLEN_SHIFT |300			  PCM186X_PCM_CFG_TX_WLEN_32 <<301			  PCM186X_PCM_CFG_TX_WLEN_SHIFT;302		break;303	default:304		return -EINVAL;305	}306 307	snd_soc_component_update_bits(component, PCM186X_PCM_CFG,308			    PCM186X_PCM_CFG_RX_WLEN_MASK |309			    PCM186X_PCM_CFG_TX_WLEN_MASK,310			    pcm_cfg);311 312	div_lrck = width * channels;313 314	if (priv->is_tdm_mode) {315		/* Select TDM transmission data */316		switch (channels) {317		case 2:318			tdm_tx_sel = PCM186X_TDM_TX_SEL_2CH;319			break;320		case 4:321			tdm_tx_sel = PCM186X_TDM_TX_SEL_4CH;322			break;323		case 6:324			tdm_tx_sel = PCM186X_TDM_TX_SEL_6CH;325			break;326		default:327			return -EINVAL;328		}329 330		snd_soc_component_update_bits(component, PCM186X_TDM_TX_SEL,331				    PCM186X_TDM_TX_SEL_MASK, tdm_tx_sel);332 333		/* In DSP/TDM mode, the LRCLK divider must be 256 */334		div_lrck = 256;335 336		/* Configure 1/256 duty cycle for LRCK */337		snd_soc_component_update_bits(component, PCM186X_PCM_CFG,338				    PCM186X_PCM_CFG_TDM_LRCK_MODE,339				    PCM186X_PCM_CFG_TDM_LRCK_MODE);340	}341 342	/* Only configure clock dividers in provider mode. */343	if (priv->is_provider_mode) {344		div_bck = priv->sysclk / (div_lrck * rate);345 346		dev_dbg(component->dev,347			"%s() master_clk=%u div_bck=%u div_lrck=%u\n",348			__func__, priv->sysclk, div_bck, div_lrck);349 350		snd_soc_component_write(component, PCM186X_BCK_DIV, div_bck - 1);351		snd_soc_component_write(component, PCM186X_LRK_DIV, div_lrck - 1);352	}353 354	return 0;355}356 357static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format)358{359	struct snd_soc_component *component = dai->component;360	struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);361	u8 clk_ctrl = 0;362	u8 pcm_cfg = 0;363 364	dev_dbg(component->dev, "%s() format=0x%x\n", __func__, format);365 366	switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {367	case SND_SOC_DAIFMT_CBP_CFP:368		if (!priv->sysclk) {369			dev_err(component->dev, "operating in provider mode requires sysclock to be configured\n");370			return -EINVAL;371		}372		clk_ctrl |= PCM186X_CLK_CTRL_MST_MODE;373		priv->is_provider_mode = true;374		break;375	case SND_SOC_DAIFMT_CBC_CFC:376		priv->is_provider_mode = false;377		break;378	default:379		dev_err(component->dev, "Invalid DAI master/slave interface\n");380		return -EINVAL;381	}382 383	/* set interface polarity */384	switch (format & SND_SOC_DAIFMT_INV_MASK) {385	case SND_SOC_DAIFMT_NB_NF:386		break;387	default:388		dev_err(component->dev, "Inverted DAI clocks not supported\n");389		return -EINVAL;390	}391 392	/* set interface format */393	switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {394	case SND_SOC_DAIFMT_I2S:395		pcm_cfg = PCM186X_PCM_CFG_FMT_I2S;396		break;397	case SND_SOC_DAIFMT_LEFT_J:398		pcm_cfg = PCM186X_PCM_CFG_FMT_LEFTJ;399		break;400	case SND_SOC_DAIFMT_DSP_A:401		priv->tdm_offset += 1;402		fallthrough;403		/* DSP_A uses the same basic config as DSP_B404		 * except we need to shift the TDM output by one BCK cycle405		 */406	case SND_SOC_DAIFMT_DSP_B:407		priv->is_tdm_mode = true;408		pcm_cfg = PCM186X_PCM_CFG_FMT_TDM;409		break;410	default:411		dev_err(component->dev, "Invalid DAI format\n");412		return -EINVAL;413	}414 415	snd_soc_component_update_bits(component, PCM186X_CLK_CTRL,416			    PCM186X_CLK_CTRL_MST_MODE, clk_ctrl);417 418	snd_soc_component_write(component, PCM186X_TDM_TX_OFFSET, priv->tdm_offset);419 420	snd_soc_component_update_bits(component, PCM186X_PCM_CFG,421			    PCM186X_PCM_CFG_FMT_MASK, pcm_cfg);422 423	return 0;424}425 426static int pcm186x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,427				unsigned int rx_mask, int slots, int slot_width)428{429	struct snd_soc_component *component = dai->component;430	struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);431	unsigned int first_slot, last_slot, tdm_offset;432 433	dev_dbg(component->dev,434		"%s() tx_mask=0x%x rx_mask=0x%x slots=%d slot_width=%d\n",435		__func__, tx_mask, rx_mask, slots, slot_width);436 437	if (!tx_mask) {438		dev_err(component->dev, "tdm tx mask must not be 0\n");439		return -EINVAL;440	}441 442	first_slot = __ffs(tx_mask);443	last_slot = __fls(tx_mask);444 445	if (last_slot - first_slot != hweight32(tx_mask) - 1) {446		dev_err(component->dev, "tdm tx mask must be contiguous\n");447		return -EINVAL;448	}449 450	tdm_offset = first_slot * slot_width;451 452	if (tdm_offset > 255) {453		dev_err(component->dev, "tdm tx slot selection out of bounds\n");454		return -EINVAL;455	}456 457	priv->tdm_offset = tdm_offset;458 459	return 0;460}461 462static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,463				  unsigned int freq, int dir)464{465	struct snd_soc_component *component = dai->component;466	struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);467 468	dev_dbg(component->dev, "%s() clk_id=%d freq=%u dir=%d\n",469		__func__, clk_id, freq, dir);470 471	priv->sysclk = freq;472 473	return 0;474}475 476static const struct snd_soc_dai_ops pcm186x_dai_ops = {477	.set_sysclk = pcm186x_set_dai_sysclk,478	.set_tdm_slot = pcm186x_set_tdm_slot,479	.set_fmt = pcm186x_set_fmt,480	.hw_params = pcm186x_hw_params,481};482 483static struct snd_soc_dai_driver pcm1863_dai = {484	.name = "pcm1863-aif",485	.capture = {486		 .stream_name = "Capture",487		 .channels_min = 1,488		 .channels_max = 2,489		 .rates = PCM186X_RATES,490		 .formats = PCM186X_FORMATS,491	 },492	.ops = &pcm186x_dai_ops,493};494 495static struct snd_soc_dai_driver pcm1865_dai = {496	.name = "pcm1865-aif",497	.capture = {498		 .stream_name = "Capture",499		 .channels_min = 1,500		 .channels_max = 4,501		 .rates = PCM186X_RATES,502		 .formats = PCM186X_FORMATS,503	 },504	.ops = &pcm186x_dai_ops,505};506 507static int pcm186x_power_on(struct snd_soc_component *component)508{509	struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);510	int ret = 0;511 512	ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),513				    priv->supplies);514	if (ret)515		return ret;516 517	regcache_cache_only(priv->regmap, false);518	ret = regcache_sync(priv->regmap);519	if (ret) {520		dev_err(component->dev, "Failed to restore cache\n");521		regcache_cache_only(priv->regmap, true);522		regulator_bulk_disable(ARRAY_SIZE(priv->supplies),523				       priv->supplies);524		return ret;525	}526 527	snd_soc_component_update_bits(component, PCM186X_POWER_CTRL,528			    PCM186X_PWR_CTRL_PWRDN, 0);529 530	return 0;531}532 533static int pcm186x_power_off(struct snd_soc_component *component)534{535	struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);536 537	snd_soc_component_update_bits(component, PCM186X_POWER_CTRL,538			    PCM186X_PWR_CTRL_PWRDN, PCM186X_PWR_CTRL_PWRDN);539 540	regcache_cache_only(priv->regmap, true);541 542	return regulator_bulk_disable(ARRAY_SIZE(priv->supplies),543				     priv->supplies);544}545 546static int pcm186x_set_bias_level(struct snd_soc_component *component,547				  enum snd_soc_bias_level level)548{549	dev_dbg(component->dev, "## %s: %d -> %d\n", __func__,550		snd_soc_component_get_bias_level(component), level);551 552	switch (level) {553	case SND_SOC_BIAS_ON:554		break;555	case SND_SOC_BIAS_PREPARE:556		break;557	case SND_SOC_BIAS_STANDBY:558		if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)559			pcm186x_power_on(component);560		break;561	case SND_SOC_BIAS_OFF:562		pcm186x_power_off(component);563		break;564	}565 566	return 0;567}568 569static struct snd_soc_component_driver soc_codec_dev_pcm1863 = {570	.set_bias_level		= pcm186x_set_bias_level,571	.controls		= pcm1863_snd_controls,572	.num_controls		= ARRAY_SIZE(pcm1863_snd_controls),573	.dapm_widgets		= pcm1863_dapm_widgets,574	.num_dapm_widgets	= ARRAY_SIZE(pcm1863_dapm_widgets),575	.dapm_routes		= pcm1863_dapm_routes,576	.num_dapm_routes	= ARRAY_SIZE(pcm1863_dapm_routes),577	.idle_bias_on		= 1,578	.use_pmdown_time	= 1,579	.endianness		= 1,580};581 582static struct snd_soc_component_driver soc_codec_dev_pcm1865 = {583	.set_bias_level		= pcm186x_set_bias_level,584	.controls		= pcm1865_snd_controls,585	.num_controls		= ARRAY_SIZE(pcm1865_snd_controls),586	.dapm_widgets		= pcm1865_dapm_widgets,587	.num_dapm_widgets	= ARRAY_SIZE(pcm1865_dapm_widgets),588	.dapm_routes		= pcm1865_dapm_routes,589	.num_dapm_routes	= ARRAY_SIZE(pcm1865_dapm_routes),590	.suspend_bias_off	= 1,591	.idle_bias_on		= 1,592	.use_pmdown_time	= 1,593	.endianness		= 1,594};595 596static bool pcm186x_volatile(struct device *dev, unsigned int reg)597{598	switch (reg) {599	case PCM186X_PAGE:600	case PCM186X_DEVICE_STATUS:601	case PCM186X_FSAMPLE_STATUS:602	case PCM186X_DIV_STATUS:603	case PCM186X_CLK_STATUS:604	case PCM186X_SUPPLY_STATUS:605	case PCM186X_MMAP_STAT_CTRL:606	case PCM186X_MMAP_ADDRESS:607		return true;608	}609 610	return false;611}612 613static const struct regmap_range_cfg pcm186x_range = {614	.name = "Pages",615	.range_max = PCM186X_MAX_REGISTER,616	.selector_reg = PCM186X_PAGE,617	.selector_mask = 0xff,618	.window_len = PCM186X_PAGE_LEN,619};620 621const struct regmap_config pcm186x_regmap = {622	.reg_bits = 8,623	.val_bits = 8,624 625	.volatile_reg = pcm186x_volatile,626 627	.ranges = &pcm186x_range,628	.num_ranges = 1,629 630	.max_register = PCM186X_MAX_REGISTER,631 632	.cache_type = REGCACHE_RBTREE,633};634EXPORT_SYMBOL_GPL(pcm186x_regmap);635 636int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq,637		  struct regmap *regmap)638{639	struct pcm186x_priv *priv;640	int i, ret;641 642	priv = devm_kzalloc(dev, sizeof(struct pcm186x_priv), GFP_KERNEL);643	if (!priv)644		return -ENOMEM;645 646	dev_set_drvdata(dev, priv);647	priv->regmap = regmap;648 649	for (i = 0; i < ARRAY_SIZE(priv->supplies); i++)650		priv->supplies[i].supply = pcm186x_supply_names[i];651 652	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),653				      priv->supplies);654	if (ret) {655		dev_err(dev, "failed to request supplies: %d\n", ret);656		return ret;657	}658 659	ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),660				    priv->supplies);661	if (ret) {662		dev_err(dev, "failed enable supplies: %d\n", ret);663		return ret;664	}665 666	/* Reset device registers for a consistent power-on like state */667	ret = regmap_write(regmap, PCM186X_PAGE, PCM186X_RESET);668	if (ret) {669		dev_err(dev, "failed to write device: %d\n", ret);670		return ret;671	}672 673	ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies),674				     priv->supplies);675	if (ret) {676		dev_err(dev, "failed disable supplies: %d\n", ret);677		return ret;678	}679 680	switch (type) {681	case PCM1865:682	case PCM1864:683		ret = devm_snd_soc_register_component(dev, &soc_codec_dev_pcm1865,684					     &pcm1865_dai, 1);685		break;686	case PCM1863:687	case PCM1862:688	default:689		ret = devm_snd_soc_register_component(dev, &soc_codec_dev_pcm1863,690					     &pcm1863_dai, 1);691	}692	if (ret) {693		dev_err(dev, "failed to register CODEC: %d\n", ret);694		return ret;695	}696 697	return 0;698}699EXPORT_SYMBOL_GPL(pcm186x_probe);700 701MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");702MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");703MODULE_DESCRIPTION("PCM186x Universal Audio ADC driver");704MODULE_LICENSE("GPL v2");705