Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Texas Instruments PCM186x Universal Audio ADC
0004  *
0005  * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com
0006  *  Andreas Dannenberg <dannenberg@ti.com>
0007  *  Andrew F. Davis <afd@ti.com>
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <linux/moduleparam.h>
0012 #include <linux/init.h>
0013 #include <linux/delay.h>
0014 #include <linux/pm.h>
0015 #include <linux/pm_runtime.h>
0016 #include <linux/regulator/consumer.h>
0017 #include <linux/regmap.h>
0018 #include <linux/slab.h>
0019 #include <sound/core.h>
0020 #include <sound/pcm.h>
0021 #include <sound/pcm_params.h>
0022 #include <sound/soc.h>
0023 #include <sound/jack.h>
0024 #include <sound/initval.h>
0025 #include <sound/tlv.h>
0026 
0027 #include "pcm186x.h"
0028 
0029 static const char * const pcm186x_supply_names[] = {
0030     "avdd",     /* Analog power supply. Connect to 3.3-V supply. */
0031     "dvdd",     /* Digital power supply. Connect to 3.3-V supply. */
0032     "iovdd",    /* I/O power supply. Connect to 3.3-V or 1.8-V. */
0033 };
0034 #define PCM186x_NUM_SUPPLIES ARRAY_SIZE(pcm186x_supply_names)
0035 
0036 struct pcm186x_priv {
0037     struct regmap *regmap;
0038     struct regulator_bulk_data supplies[PCM186x_NUM_SUPPLIES];
0039     unsigned int sysclk;
0040     unsigned int tdm_offset;
0041     bool is_tdm_mode;
0042     bool is_provider_mode;
0043 };
0044 
0045 static const DECLARE_TLV_DB_SCALE(pcm186x_pga_tlv, -1200, 50, 0);
0046 
0047 static const struct snd_kcontrol_new pcm1863_snd_controls[] = {
0048     SOC_DOUBLE_R_S_TLV("ADC Capture Volume", PCM186X_PGA_VAL_CH1_L,
0049                PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0,
0050                pcm186x_pga_tlv),
0051 };
0052 
0053 static const struct snd_kcontrol_new pcm1865_snd_controls[] = {
0054     SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", PCM186X_PGA_VAL_CH1_L,
0055                PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0,
0056                pcm186x_pga_tlv),
0057     SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", PCM186X_PGA_VAL_CH2_L,
0058                PCM186X_PGA_VAL_CH2_R, 0, -24, 80, 7, 0,
0059                pcm186x_pga_tlv),
0060 };
0061 
0062 static const unsigned int pcm186x_adc_input_channel_sel_value[] = {
0063     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0064     0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0065     0x10, 0x20, 0x30
0066 };
0067 
0068 static const char * const pcm186x_adcl_input_channel_sel_text[] = {
0069     "No Select",
0070     "VINL1[SE]",                    /* Default for ADC1L */
0071     "VINL2[SE]",                    /* Default for ADC2L */
0072     "VINL2[SE] + VINL1[SE]",
0073     "VINL3[SE]",
0074     "VINL3[SE] + VINL1[SE]",
0075     "VINL3[SE] + VINL2[SE]",
0076     "VINL3[SE] + VINL2[SE] + VINL1[SE]",
0077     "VINL4[SE]",
0078     "VINL4[SE] + VINL1[SE]",
0079     "VINL4[SE] + VINL2[SE]",
0080     "VINL4[SE] + VINL2[SE] + VINL1[SE]",
0081     "VINL4[SE] + VINL3[SE]",
0082     "VINL4[SE] + VINL3[SE] + VINL1[SE]",
0083     "VINL4[SE] + VINL3[SE] + VINL2[SE]",
0084     "VINL4[SE] + VINL3[SE] + VINL2[SE] + VINL1[SE]",
0085     "{VIN1P, VIN1M}[DIFF]",
0086     "{VIN4P, VIN4M}[DIFF]",
0087     "{VIN1P, VIN1M}[DIFF] + {VIN4P, VIN4M}[DIFF]"
0088 };
0089 
0090 static const char * const pcm186x_adcr_input_channel_sel_text[] = {
0091     "No Select",
0092     "VINR1[SE]",                    /* Default for ADC1R */
0093     "VINR2[SE]",                    /* Default for ADC2R */
0094     "VINR2[SE] + VINR1[SE]",
0095     "VINR3[SE]",
0096     "VINR3[SE] + VINR1[SE]",
0097     "VINR3[SE] + VINR2[SE]",
0098     "VINR3[SE] + VINR2[SE] + VINR1[SE]",
0099     "VINR4[SE]",
0100     "VINR4[SE] + VINR1[SE]",
0101     "VINR4[SE] + VINR2[SE]",
0102     "VINR4[SE] + VINR2[SE] + VINR1[SE]",
0103     "VINR4[SE] + VINR3[SE]",
0104     "VINR4[SE] + VINR3[SE] + VINR1[SE]",
0105     "VINR4[SE] + VINR3[SE] + VINR2[SE]",
0106     "VINR4[SE] + VINR3[SE] + VINR2[SE] + VINR1[SE]",
0107     "{VIN2P, VIN2M}[DIFF]",
0108     "{VIN3P, VIN3M}[DIFF]",
0109     "{VIN2P, VIN2M}[DIFF] + {VIN3P, VIN3M}[DIFF]"
0110 };
0111 
0112 static const struct soc_enum pcm186x_adc_input_channel_sel[] = {
0113     SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_L, 0,
0114                   PCM186X_ADC_INPUT_SEL_MASK,
0115                   ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text),
0116                   pcm186x_adcl_input_channel_sel_text,
0117                   pcm186x_adc_input_channel_sel_value),
0118     SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_R, 0,
0119                   PCM186X_ADC_INPUT_SEL_MASK,
0120                   ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text),
0121                   pcm186x_adcr_input_channel_sel_text,
0122                   pcm186x_adc_input_channel_sel_value),
0123     SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_L, 0,
0124                   PCM186X_ADC_INPUT_SEL_MASK,
0125                   ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text),
0126                   pcm186x_adcl_input_channel_sel_text,
0127                   pcm186x_adc_input_channel_sel_value),
0128     SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_R, 0,
0129                   PCM186X_ADC_INPUT_SEL_MASK,
0130                   ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text),
0131                   pcm186x_adcr_input_channel_sel_text,
0132                   pcm186x_adc_input_channel_sel_value),
0133 };
0134 
0135 static const struct snd_kcontrol_new pcm186x_adc_mux_controls[] = {
0136     SOC_DAPM_ENUM("ADC1 Left Input", pcm186x_adc_input_channel_sel[0]),
0137     SOC_DAPM_ENUM("ADC1 Right Input", pcm186x_adc_input_channel_sel[1]),
0138     SOC_DAPM_ENUM("ADC2 Left Input", pcm186x_adc_input_channel_sel[2]),
0139     SOC_DAPM_ENUM("ADC2 Right Input", pcm186x_adc_input_channel_sel[3]),
0140 };
0141 
0142 static const struct snd_soc_dapm_widget pcm1863_dapm_widgets[] = {
0143     SND_SOC_DAPM_INPUT("VINL1"),
0144     SND_SOC_DAPM_INPUT("VINR1"),
0145     SND_SOC_DAPM_INPUT("VINL2"),
0146     SND_SOC_DAPM_INPUT("VINR2"),
0147     SND_SOC_DAPM_INPUT("VINL3"),
0148     SND_SOC_DAPM_INPUT("VINR3"),
0149     SND_SOC_DAPM_INPUT("VINL4"),
0150     SND_SOC_DAPM_INPUT("VINR4"),
0151 
0152     SND_SOC_DAPM_MUX("ADC Left Capture Source", SND_SOC_NOPM, 0, 0,
0153              &pcm186x_adc_mux_controls[0]),
0154     SND_SOC_DAPM_MUX("ADC Right Capture Source", SND_SOC_NOPM, 0, 0,
0155              &pcm186x_adc_mux_controls[1]),
0156 
0157     /*
0158      * Put the codec into SLEEP mode when not in use, allowing the
0159      * Energysense mechanism to operate.
0160      */
0161     SND_SOC_DAPM_ADC("ADC", "HiFi Capture", PCM186X_POWER_CTRL, 1,  1),
0162 };
0163 
0164 static const struct snd_soc_dapm_widget pcm1865_dapm_widgets[] = {
0165     SND_SOC_DAPM_INPUT("VINL1"),
0166     SND_SOC_DAPM_INPUT("VINR1"),
0167     SND_SOC_DAPM_INPUT("VINL2"),
0168     SND_SOC_DAPM_INPUT("VINR2"),
0169     SND_SOC_DAPM_INPUT("VINL3"),
0170     SND_SOC_DAPM_INPUT("VINR3"),
0171     SND_SOC_DAPM_INPUT("VINL4"),
0172     SND_SOC_DAPM_INPUT("VINR4"),
0173 
0174     SND_SOC_DAPM_MUX("ADC1 Left Capture Source", SND_SOC_NOPM, 0, 0,
0175              &pcm186x_adc_mux_controls[0]),
0176     SND_SOC_DAPM_MUX("ADC1 Right Capture Source", SND_SOC_NOPM, 0, 0,
0177              &pcm186x_adc_mux_controls[1]),
0178     SND_SOC_DAPM_MUX("ADC2 Left Capture Source", SND_SOC_NOPM, 0, 0,
0179              &pcm186x_adc_mux_controls[2]),
0180     SND_SOC_DAPM_MUX("ADC2 Right Capture Source", SND_SOC_NOPM, 0, 0,
0181              &pcm186x_adc_mux_controls[3]),
0182 
0183     /*
0184      * Put the codec into SLEEP mode when not in use, allowing the
0185      * Energysense mechanism to operate.
0186      */
0187     SND_SOC_DAPM_ADC("ADC1", "HiFi Capture 1", PCM186X_POWER_CTRL, 1,  1),
0188     SND_SOC_DAPM_ADC("ADC2", "HiFi Capture 2", PCM186X_POWER_CTRL, 1,  1),
0189 };
0190 
0191 static const struct snd_soc_dapm_route pcm1863_dapm_routes[] = {
0192     { "ADC Left Capture Source", NULL, "VINL1" },
0193     { "ADC Left Capture Source", NULL, "VINR1" },
0194     { "ADC Left Capture Source", NULL, "VINL2" },
0195     { "ADC Left Capture Source", NULL, "VINR2" },
0196     { "ADC Left Capture Source", NULL, "VINL3" },
0197     { "ADC Left Capture Source", NULL, "VINR3" },
0198     { "ADC Left Capture Source", NULL, "VINL4" },
0199     { "ADC Left Capture Source", NULL, "VINR4" },
0200 
0201     { "ADC", NULL, "ADC Left Capture Source" },
0202 
0203     { "ADC Right Capture Source", NULL, "VINL1" },
0204     { "ADC Right Capture Source", NULL, "VINR1" },
0205     { "ADC Right Capture Source", NULL, "VINL2" },
0206     { "ADC Right Capture Source", NULL, "VINR2" },
0207     { "ADC Right Capture Source", NULL, "VINL3" },
0208     { "ADC Right Capture Source", NULL, "VINR3" },
0209     { "ADC Right Capture Source", NULL, "VINL4" },
0210     { "ADC Right Capture Source", NULL, "VINR4" },
0211 
0212     { "ADC", NULL, "ADC Right Capture Source" },
0213 };
0214 
0215 static const struct snd_soc_dapm_route pcm1865_dapm_routes[] = {
0216     { "ADC1 Left Capture Source", NULL, "VINL1" },
0217     { "ADC1 Left Capture Source", NULL, "VINR1" },
0218     { "ADC1 Left Capture Source", NULL, "VINL2" },
0219     { "ADC1 Left Capture Source", NULL, "VINR2" },
0220     { "ADC1 Left Capture Source", NULL, "VINL3" },
0221     { "ADC1 Left Capture Source", NULL, "VINR3" },
0222     { "ADC1 Left Capture Source", NULL, "VINL4" },
0223     { "ADC1 Left Capture Source", NULL, "VINR4" },
0224 
0225     { "ADC1", NULL, "ADC1 Left Capture Source" },
0226 
0227     { "ADC1 Right Capture Source", NULL, "VINL1" },
0228     { "ADC1 Right Capture Source", NULL, "VINR1" },
0229     { "ADC1 Right Capture Source", NULL, "VINL2" },
0230     { "ADC1 Right Capture Source", NULL, "VINR2" },
0231     { "ADC1 Right Capture Source", NULL, "VINL3" },
0232     { "ADC1 Right Capture Source", NULL, "VINR3" },
0233     { "ADC1 Right Capture Source", NULL, "VINL4" },
0234     { "ADC1 Right Capture Source", NULL, "VINR4" },
0235 
0236     { "ADC1", NULL, "ADC1 Right Capture Source" },
0237 
0238     { "ADC2 Left Capture Source", NULL, "VINL1" },
0239     { "ADC2 Left Capture Source", NULL, "VINR1" },
0240     { "ADC2 Left Capture Source", NULL, "VINL2" },
0241     { "ADC2 Left Capture Source", NULL, "VINR2" },
0242     { "ADC2 Left Capture Source", NULL, "VINL3" },
0243     { "ADC2 Left Capture Source", NULL, "VINR3" },
0244     { "ADC2 Left Capture Source", NULL, "VINL4" },
0245     { "ADC2 Left Capture Source", NULL, "VINR4" },
0246 
0247     { "ADC2", NULL, "ADC2 Left Capture Source" },
0248 
0249     { "ADC2 Right Capture Source", NULL, "VINL1" },
0250     { "ADC2 Right Capture Source", NULL, "VINR1" },
0251     { "ADC2 Right Capture Source", NULL, "VINL2" },
0252     { "ADC2 Right Capture Source", NULL, "VINR2" },
0253     { "ADC2 Right Capture Source", NULL, "VINL3" },
0254     { "ADC2 Right Capture Source", NULL, "VINR3" },
0255     { "ADC2 Right Capture Source", NULL, "VINL4" },
0256     { "ADC2 Right Capture Source", NULL, "VINR4" },
0257 
0258     { "ADC2", NULL, "ADC2 Right Capture Source" },
0259 };
0260 
0261 static int pcm186x_hw_params(struct snd_pcm_substream *substream,
0262                  struct snd_pcm_hw_params *params,
0263                  struct snd_soc_dai *dai)
0264 {
0265     struct snd_soc_component *component = dai->component;
0266     struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
0267     unsigned int rate = params_rate(params);
0268     snd_pcm_format_t format = params_format(params);
0269     unsigned int width = params_width(params);
0270     unsigned int channels = params_channels(params);
0271     unsigned int div_lrck;
0272     unsigned int div_bck;
0273     u8 tdm_tx_sel = 0;
0274     u8 pcm_cfg = 0;
0275 
0276     dev_dbg(component->dev, "%s() rate=%u format=0x%x width=%u channels=%u\n",
0277         __func__, rate, format, width, channels);
0278 
0279     switch (width) {
0280     case 16:
0281         pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_16 <<
0282               PCM186X_PCM_CFG_RX_WLEN_SHIFT |
0283               PCM186X_PCM_CFG_TX_WLEN_16 <<
0284               PCM186X_PCM_CFG_TX_WLEN_SHIFT;
0285         break;
0286     case 20:
0287         pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_20 <<
0288               PCM186X_PCM_CFG_RX_WLEN_SHIFT |
0289               PCM186X_PCM_CFG_TX_WLEN_20 <<
0290               PCM186X_PCM_CFG_TX_WLEN_SHIFT;
0291         break;
0292     case 24:
0293         pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_24 <<
0294               PCM186X_PCM_CFG_RX_WLEN_SHIFT |
0295               PCM186X_PCM_CFG_TX_WLEN_24 <<
0296               PCM186X_PCM_CFG_TX_WLEN_SHIFT;
0297         break;
0298     case 32:
0299         pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_32 <<
0300               PCM186X_PCM_CFG_RX_WLEN_SHIFT |
0301               PCM186X_PCM_CFG_TX_WLEN_32 <<
0302               PCM186X_PCM_CFG_TX_WLEN_SHIFT;
0303         break;
0304     default:
0305         return -EINVAL;
0306     }
0307 
0308     snd_soc_component_update_bits(component, PCM186X_PCM_CFG,
0309                 PCM186X_PCM_CFG_RX_WLEN_MASK |
0310                 PCM186X_PCM_CFG_TX_WLEN_MASK,
0311                 pcm_cfg);
0312 
0313     div_lrck = width * channels;
0314 
0315     if (priv->is_tdm_mode) {
0316         /* Select TDM transmission data */
0317         switch (channels) {
0318         case 2:
0319             tdm_tx_sel = PCM186X_TDM_TX_SEL_2CH;
0320             break;
0321         case 4:
0322             tdm_tx_sel = PCM186X_TDM_TX_SEL_4CH;
0323             break;
0324         case 6:
0325             tdm_tx_sel = PCM186X_TDM_TX_SEL_6CH;
0326             break;
0327         default:
0328             return -EINVAL;
0329         }
0330 
0331         snd_soc_component_update_bits(component, PCM186X_TDM_TX_SEL,
0332                     PCM186X_TDM_TX_SEL_MASK, tdm_tx_sel);
0333 
0334         /* In DSP/TDM mode, the LRCLK divider must be 256 */
0335         div_lrck = 256;
0336 
0337         /* Configure 1/256 duty cycle for LRCK */
0338         snd_soc_component_update_bits(component, PCM186X_PCM_CFG,
0339                     PCM186X_PCM_CFG_TDM_LRCK_MODE,
0340                     PCM186X_PCM_CFG_TDM_LRCK_MODE);
0341     }
0342 
0343     /* Only configure clock dividers in provider mode. */
0344     if (priv->is_provider_mode) {
0345         div_bck = priv->sysclk / (div_lrck * rate);
0346 
0347         dev_dbg(component->dev,
0348             "%s() master_clk=%u div_bck=%u div_lrck=%u\n",
0349             __func__, priv->sysclk, div_bck, div_lrck);
0350 
0351         snd_soc_component_write(component, PCM186X_BCK_DIV, div_bck - 1);
0352         snd_soc_component_write(component, PCM186X_LRK_DIV, div_lrck - 1);
0353     }
0354 
0355     return 0;
0356 }
0357 
0358 static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format)
0359 {
0360     struct snd_soc_component *component = dai->component;
0361     struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
0362     u8 clk_ctrl = 0;
0363     u8 pcm_cfg = 0;
0364 
0365     dev_dbg(component->dev, "%s() format=0x%x\n", __func__, format);
0366 
0367     switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
0368     case SND_SOC_DAIFMT_CBP_CFP:
0369         if (!priv->sysclk) {
0370             dev_err(component->dev, "operating in provider mode requires sysclock to be configured\n");
0371             return -EINVAL;
0372         }
0373         clk_ctrl |= PCM186X_CLK_CTRL_MST_MODE;
0374         priv->is_provider_mode = true;
0375         break;
0376     case SND_SOC_DAIFMT_CBC_CFC:
0377         priv->is_provider_mode = false;
0378         break;
0379     default:
0380         dev_err(component->dev, "Invalid DAI master/slave interface\n");
0381         return -EINVAL;
0382     }
0383 
0384     /* set interface polarity */
0385     switch (format & SND_SOC_DAIFMT_INV_MASK) {
0386     case SND_SOC_DAIFMT_NB_NF:
0387         break;
0388     default:
0389         dev_err(component->dev, "Inverted DAI clocks not supported\n");
0390         return -EINVAL;
0391     }
0392 
0393     /* set interface format */
0394     switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
0395     case SND_SOC_DAIFMT_I2S:
0396         pcm_cfg = PCM186X_PCM_CFG_FMT_I2S;
0397         break;
0398     case SND_SOC_DAIFMT_LEFT_J:
0399         pcm_cfg = PCM186X_PCM_CFG_FMT_LEFTJ;
0400         break;
0401     case SND_SOC_DAIFMT_DSP_A:
0402         priv->tdm_offset += 1;
0403         fallthrough;
0404         /* DSP_A uses the same basic config as DSP_B
0405          * except we need to shift the TDM output by one BCK cycle
0406          */
0407     case SND_SOC_DAIFMT_DSP_B:
0408         priv->is_tdm_mode = true;
0409         pcm_cfg = PCM186X_PCM_CFG_FMT_TDM;
0410         break;
0411     default:
0412         dev_err(component->dev, "Invalid DAI format\n");
0413         return -EINVAL;
0414     }
0415 
0416     snd_soc_component_update_bits(component, PCM186X_CLK_CTRL,
0417                 PCM186X_CLK_CTRL_MST_MODE, clk_ctrl);
0418 
0419     snd_soc_component_write(component, PCM186X_TDM_TX_OFFSET, priv->tdm_offset);
0420 
0421     snd_soc_component_update_bits(component, PCM186X_PCM_CFG,
0422                 PCM186X_PCM_CFG_FMT_MASK, pcm_cfg);
0423 
0424     return 0;
0425 }
0426 
0427 static int pcm186x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
0428                 unsigned int rx_mask, int slots, int slot_width)
0429 {
0430     struct snd_soc_component *component = dai->component;
0431     struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
0432     unsigned int first_slot, last_slot, tdm_offset;
0433 
0434     dev_dbg(component->dev,
0435         "%s() tx_mask=0x%x rx_mask=0x%x slots=%d slot_width=%d\n",
0436         __func__, tx_mask, rx_mask, slots, slot_width);
0437 
0438     if (!tx_mask) {
0439         dev_err(component->dev, "tdm tx mask must not be 0\n");
0440         return -EINVAL;
0441     }
0442 
0443     first_slot = __ffs(tx_mask);
0444     last_slot = __fls(tx_mask);
0445 
0446     if (last_slot - first_slot != hweight32(tx_mask) - 1) {
0447         dev_err(component->dev, "tdm tx mask must be contiguous\n");
0448         return -EINVAL;
0449     }
0450 
0451     tdm_offset = first_slot * slot_width;
0452 
0453     if (tdm_offset > 255) {
0454         dev_err(component->dev, "tdm tx slot selection out of bounds\n");
0455         return -EINVAL;
0456     }
0457 
0458     priv->tdm_offset = tdm_offset;
0459 
0460     return 0;
0461 }
0462 
0463 static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
0464                   unsigned int freq, int dir)
0465 {
0466     struct snd_soc_component *component = dai->component;
0467     struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
0468 
0469     dev_dbg(component->dev, "%s() clk_id=%d freq=%u dir=%d\n",
0470         __func__, clk_id, freq, dir);
0471 
0472     priv->sysclk = freq;
0473 
0474     return 0;
0475 }
0476 
0477 static const struct snd_soc_dai_ops pcm186x_dai_ops = {
0478     .set_sysclk = pcm186x_set_dai_sysclk,
0479     .set_tdm_slot = pcm186x_set_tdm_slot,
0480     .set_fmt = pcm186x_set_fmt,
0481     .hw_params = pcm186x_hw_params,
0482 };
0483 
0484 static struct snd_soc_dai_driver pcm1863_dai = {
0485     .name = "pcm1863-aif",
0486     .capture = {
0487          .stream_name = "Capture",
0488          .channels_min = 1,
0489          .channels_max = 2,
0490          .rates = PCM186X_RATES,
0491          .formats = PCM186X_FORMATS,
0492      },
0493     .ops = &pcm186x_dai_ops,
0494 };
0495 
0496 static struct snd_soc_dai_driver pcm1865_dai = {
0497     .name = "pcm1865-aif",
0498     .capture = {
0499          .stream_name = "Capture",
0500          .channels_min = 1,
0501          .channels_max = 4,
0502          .rates = PCM186X_RATES,
0503          .formats = PCM186X_FORMATS,
0504      },
0505     .ops = &pcm186x_dai_ops,
0506 };
0507 
0508 static int pcm186x_power_on(struct snd_soc_component *component)
0509 {
0510     struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
0511     int ret = 0;
0512 
0513     ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
0514                     priv->supplies);
0515     if (ret)
0516         return ret;
0517 
0518     regcache_cache_only(priv->regmap, false);
0519     ret = regcache_sync(priv->regmap);
0520     if (ret) {
0521         dev_err(component->dev, "Failed to restore cache\n");
0522         regcache_cache_only(priv->regmap, true);
0523         regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
0524                        priv->supplies);
0525         return ret;
0526     }
0527 
0528     snd_soc_component_update_bits(component, PCM186X_POWER_CTRL,
0529                 PCM186X_PWR_CTRL_PWRDN, 0);
0530 
0531     return 0;
0532 }
0533 
0534 static int pcm186x_power_off(struct snd_soc_component *component)
0535 {
0536     struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
0537 
0538     snd_soc_component_update_bits(component, PCM186X_POWER_CTRL,
0539                 PCM186X_PWR_CTRL_PWRDN, PCM186X_PWR_CTRL_PWRDN);
0540 
0541     regcache_cache_only(priv->regmap, true);
0542 
0543     return regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
0544                      priv->supplies);
0545 }
0546 
0547 static int pcm186x_set_bias_level(struct snd_soc_component *component,
0548                   enum snd_soc_bias_level level)
0549 {
0550     dev_dbg(component->dev, "## %s: %d -> %d\n", __func__,
0551         snd_soc_component_get_bias_level(component), level);
0552 
0553     switch (level) {
0554     case SND_SOC_BIAS_ON:
0555         break;
0556     case SND_SOC_BIAS_PREPARE:
0557         break;
0558     case SND_SOC_BIAS_STANDBY:
0559         if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
0560             pcm186x_power_on(component);
0561         break;
0562     case SND_SOC_BIAS_OFF:
0563         pcm186x_power_off(component);
0564         break;
0565     }
0566 
0567     return 0;
0568 }
0569 
0570 static struct snd_soc_component_driver soc_codec_dev_pcm1863 = {
0571     .set_bias_level     = pcm186x_set_bias_level,
0572     .controls       = pcm1863_snd_controls,
0573     .num_controls       = ARRAY_SIZE(pcm1863_snd_controls),
0574     .dapm_widgets       = pcm1863_dapm_widgets,
0575     .num_dapm_widgets   = ARRAY_SIZE(pcm1863_dapm_widgets),
0576     .dapm_routes        = pcm1863_dapm_routes,
0577     .num_dapm_routes    = ARRAY_SIZE(pcm1863_dapm_routes),
0578     .idle_bias_on       = 1,
0579     .use_pmdown_time    = 1,
0580     .endianness     = 1,
0581 };
0582 
0583 static struct snd_soc_component_driver soc_codec_dev_pcm1865 = {
0584     .set_bias_level     = pcm186x_set_bias_level,
0585     .controls       = pcm1865_snd_controls,
0586     .num_controls       = ARRAY_SIZE(pcm1865_snd_controls),
0587     .dapm_widgets       = pcm1865_dapm_widgets,
0588     .num_dapm_widgets   = ARRAY_SIZE(pcm1865_dapm_widgets),
0589     .dapm_routes        = pcm1865_dapm_routes,
0590     .num_dapm_routes    = ARRAY_SIZE(pcm1865_dapm_routes),
0591     .suspend_bias_off   = 1,
0592     .idle_bias_on       = 1,
0593     .use_pmdown_time    = 1,
0594     .endianness     = 1,
0595 };
0596 
0597 static bool pcm186x_volatile(struct device *dev, unsigned int reg)
0598 {
0599     switch (reg) {
0600     case PCM186X_PAGE:
0601     case PCM186X_DEVICE_STATUS:
0602     case PCM186X_FSAMPLE_STATUS:
0603     case PCM186X_DIV_STATUS:
0604     case PCM186X_CLK_STATUS:
0605     case PCM186X_SUPPLY_STATUS:
0606     case PCM186X_MMAP_STAT_CTRL:
0607     case PCM186X_MMAP_ADDRESS:
0608         return true;
0609     }
0610 
0611     return false;
0612 }
0613 
0614 static const struct regmap_range_cfg pcm186x_range = {
0615     .name = "Pages",
0616     .range_max = PCM186X_MAX_REGISTER,
0617     .selector_reg = PCM186X_PAGE,
0618     .selector_mask = 0xff,
0619     .window_len = PCM186X_PAGE_LEN,
0620 };
0621 
0622 const struct regmap_config pcm186x_regmap = {
0623     .reg_bits = 8,
0624     .val_bits = 8,
0625 
0626     .volatile_reg = pcm186x_volatile,
0627 
0628     .ranges = &pcm186x_range,
0629     .num_ranges = 1,
0630 
0631     .max_register = PCM186X_MAX_REGISTER,
0632 
0633     .cache_type = REGCACHE_RBTREE,
0634 };
0635 EXPORT_SYMBOL_GPL(pcm186x_regmap);
0636 
0637 int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq,
0638           struct regmap *regmap)
0639 {
0640     struct pcm186x_priv *priv;
0641     int i, ret;
0642 
0643     priv = devm_kzalloc(dev, sizeof(struct pcm186x_priv), GFP_KERNEL);
0644     if (!priv)
0645         return -ENOMEM;
0646 
0647     dev_set_drvdata(dev, priv);
0648     priv->regmap = regmap;
0649 
0650     for (i = 0; i < ARRAY_SIZE(priv->supplies); i++)
0651         priv->supplies[i].supply = pcm186x_supply_names[i];
0652 
0653     ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),
0654                       priv->supplies);
0655     if (ret) {
0656         dev_err(dev, "failed to request supplies: %d\n", ret);
0657         return ret;
0658     }
0659 
0660     ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
0661                     priv->supplies);
0662     if (ret) {
0663         dev_err(dev, "failed enable supplies: %d\n", ret);
0664         return ret;
0665     }
0666 
0667     /* Reset device registers for a consistent power-on like state */
0668     ret = regmap_write(regmap, PCM186X_PAGE, PCM186X_RESET);
0669     if (ret) {
0670         dev_err(dev, "failed to write device: %d\n", ret);
0671         return ret;
0672     }
0673 
0674     ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
0675                      priv->supplies);
0676     if (ret) {
0677         dev_err(dev, "failed disable supplies: %d\n", ret);
0678         return ret;
0679     }
0680 
0681     switch (type) {
0682     case PCM1865:
0683     case PCM1864:
0684         ret = devm_snd_soc_register_component(dev, &soc_codec_dev_pcm1865,
0685                          &pcm1865_dai, 1);
0686         break;
0687     case PCM1863:
0688     case PCM1862:
0689     default:
0690         ret = devm_snd_soc_register_component(dev, &soc_codec_dev_pcm1863,
0691                          &pcm1863_dai, 1);
0692     }
0693     if (ret) {
0694         dev_err(dev, "failed to register CODEC: %d\n", ret);
0695         return ret;
0696     }
0697 
0698     return 0;
0699 }
0700 EXPORT_SYMBOL_GPL(pcm186x_probe);
0701 
0702 MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
0703 MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
0704 MODULE_DESCRIPTION("PCM186x Universal Audio ADC driver");
0705 MODULE_LICENSE("GPL v2");