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