10c516b4fSNicolin Chen /* 20c516b4fSNicolin Chen * Cirrus Logic CS42448/CS42888 Audio CODEC Digital Audio Interface (DAI) driver 30c516b4fSNicolin Chen * 40c516b4fSNicolin Chen * Copyright (C) 2014 Freescale Semiconductor, Inc. 50c516b4fSNicolin Chen * 60c516b4fSNicolin Chen * Author: Nicolin Chen <Guangyu.Chen@freescale.com> 70c516b4fSNicolin Chen * 80c516b4fSNicolin Chen * This file is licensed under the terms of the GNU General Public License 90c516b4fSNicolin Chen * version 2. This program is licensed "as is" without any warranty of any 100c516b4fSNicolin Chen * kind, whether express or implied. 110c516b4fSNicolin Chen */ 120c516b4fSNicolin Chen 130c516b4fSNicolin Chen #include <linux/clk.h> 140c516b4fSNicolin Chen #include <linux/delay.h> 150c516b4fSNicolin Chen #include <linux/module.h> 167cda6223SShengjiu Wang #include <linux/gpio/consumer.h> 170c516b4fSNicolin Chen #include <linux/pm_runtime.h> 180c516b4fSNicolin Chen #include <linux/regulator/consumer.h> 190c516b4fSNicolin Chen #include <sound/pcm_params.h> 200c516b4fSNicolin Chen #include <sound/soc.h> 210c516b4fSNicolin Chen #include <sound/tlv.h> 220c516b4fSNicolin Chen 230c516b4fSNicolin Chen #include "cs42xx8.h" 240c516b4fSNicolin Chen 250c516b4fSNicolin Chen #define CS42XX8_NUM_SUPPLIES 4 260c516b4fSNicolin Chen static const char *const cs42xx8_supply_names[CS42XX8_NUM_SUPPLIES] = { 270c516b4fSNicolin Chen "VA", 280c516b4fSNicolin Chen "VD", 290c516b4fSNicolin Chen "VLS", 300c516b4fSNicolin Chen "VLC", 310c516b4fSNicolin Chen }; 320c516b4fSNicolin Chen 330c516b4fSNicolin Chen #define CS42XX8_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ 340c516b4fSNicolin Chen SNDRV_PCM_FMTBIT_S20_3LE | \ 350c516b4fSNicolin Chen SNDRV_PCM_FMTBIT_S24_LE | \ 360c516b4fSNicolin Chen SNDRV_PCM_FMTBIT_S32_LE) 370c516b4fSNicolin Chen 380c516b4fSNicolin Chen /* codec private data */ 390c516b4fSNicolin Chen struct cs42xx8_priv { 400c516b4fSNicolin Chen struct regulator_bulk_data supplies[CS42XX8_NUM_SUPPLIES]; 410c516b4fSNicolin Chen const struct cs42xx8_driver_data *drvdata; 420c516b4fSNicolin Chen struct regmap *regmap; 430c516b4fSNicolin Chen struct clk *clk; 440c516b4fSNicolin Chen 450c516b4fSNicolin Chen bool slave_mode; 460c516b4fSNicolin Chen unsigned long sysclk; 471f1e60c9SZidan Wang u32 tx_channels; 48bfe95dfaSS.j. Wang struct gpio_desc *gpiod_reset; 4948dfd37aSShengjiu Wang u32 rate[2]; 500c516b4fSNicolin Chen }; 510c516b4fSNicolin Chen 520c516b4fSNicolin Chen /* -127.5dB to 0dB with step of 0.5dB */ 530c516b4fSNicolin Chen static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); 540c516b4fSNicolin Chen /* -64dB to 24dB with step of 0.5dB */ 550c516b4fSNicolin Chen static const DECLARE_TLV_DB_SCALE(adc_tlv, -6400, 50, 0); 560c516b4fSNicolin Chen 570c516b4fSNicolin Chen static const char *const cs42xx8_adc_single[] = { "Differential", "Single-Ended" }; 580c516b4fSNicolin Chen static const char *const cs42xx8_szc[] = { "Immediate Change", "Zero Cross", 590c516b4fSNicolin Chen "Soft Ramp", "Soft Ramp on Zero Cross" }; 600c516b4fSNicolin Chen 610c516b4fSNicolin Chen static const struct soc_enum adc1_single_enum = 620c516b4fSNicolin Chen SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 4, 2, cs42xx8_adc_single); 630c516b4fSNicolin Chen static const struct soc_enum adc2_single_enum = 640c516b4fSNicolin Chen SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 3, 2, cs42xx8_adc_single); 650c516b4fSNicolin Chen static const struct soc_enum adc3_single_enum = 660c516b4fSNicolin Chen SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 2, 2, cs42xx8_adc_single); 670c516b4fSNicolin Chen static const struct soc_enum dac_szc_enum = 680c516b4fSNicolin Chen SOC_ENUM_SINGLE(CS42XX8_TXCTL, 5, 4, cs42xx8_szc); 690c516b4fSNicolin Chen static const struct soc_enum adc_szc_enum = 700c516b4fSNicolin Chen SOC_ENUM_SINGLE(CS42XX8_TXCTL, 0, 4, cs42xx8_szc); 710c516b4fSNicolin Chen 720c516b4fSNicolin Chen static const struct snd_kcontrol_new cs42xx8_snd_controls[] = { 730c516b4fSNicolin Chen SOC_DOUBLE_R_TLV("DAC1 Playback Volume", CS42XX8_VOLAOUT1, 740c516b4fSNicolin Chen CS42XX8_VOLAOUT2, 0, 0xff, 1, dac_tlv), 750c516b4fSNicolin Chen SOC_DOUBLE_R_TLV("DAC2 Playback Volume", CS42XX8_VOLAOUT3, 760c516b4fSNicolin Chen CS42XX8_VOLAOUT4, 0, 0xff, 1, dac_tlv), 770c516b4fSNicolin Chen SOC_DOUBLE_R_TLV("DAC3 Playback Volume", CS42XX8_VOLAOUT5, 780c516b4fSNicolin Chen CS42XX8_VOLAOUT6, 0, 0xff, 1, dac_tlv), 790c516b4fSNicolin Chen SOC_DOUBLE_R_TLV("DAC4 Playback Volume", CS42XX8_VOLAOUT7, 800c516b4fSNicolin Chen CS42XX8_VOLAOUT8, 0, 0xff, 1, dac_tlv), 810c516b4fSNicolin Chen SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", CS42XX8_VOLAIN1, 820c516b4fSNicolin Chen CS42XX8_VOLAIN2, 0, -0x80, 0x30, 7, 0, adc_tlv), 830c516b4fSNicolin Chen SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", CS42XX8_VOLAIN3, 840c516b4fSNicolin Chen CS42XX8_VOLAIN4, 0, -0x80, 0x30, 7, 0, adc_tlv), 850c516b4fSNicolin Chen SOC_DOUBLE("DAC1 Invert Switch", CS42XX8_DACINV, 0, 1, 1, 0), 860c516b4fSNicolin Chen SOC_DOUBLE("DAC2 Invert Switch", CS42XX8_DACINV, 2, 3, 1, 0), 870c516b4fSNicolin Chen SOC_DOUBLE("DAC3 Invert Switch", CS42XX8_DACINV, 4, 5, 1, 0), 880c516b4fSNicolin Chen SOC_DOUBLE("DAC4 Invert Switch", CS42XX8_DACINV, 6, 7, 1, 0), 890c516b4fSNicolin Chen SOC_DOUBLE("ADC1 Invert Switch", CS42XX8_ADCINV, 0, 1, 1, 0), 900c516b4fSNicolin Chen SOC_DOUBLE("ADC2 Invert Switch", CS42XX8_ADCINV, 2, 3, 1, 0), 910c516b4fSNicolin Chen SOC_SINGLE("ADC High-Pass Filter Switch", CS42XX8_ADCCTL, 7, 1, 1), 920c516b4fSNicolin Chen SOC_SINGLE("DAC De-emphasis Switch", CS42XX8_ADCCTL, 5, 1, 0), 930c516b4fSNicolin Chen SOC_ENUM("ADC1 Single Ended Mode Switch", adc1_single_enum), 940c516b4fSNicolin Chen SOC_ENUM("ADC2 Single Ended Mode Switch", adc2_single_enum), 950c516b4fSNicolin Chen SOC_SINGLE("DAC Single Volume Control Switch", CS42XX8_TXCTL, 7, 1, 0), 960c516b4fSNicolin Chen SOC_ENUM("DAC Soft Ramp & Zero Cross Control Switch", dac_szc_enum), 970c516b4fSNicolin Chen SOC_SINGLE("DAC Auto Mute Switch", CS42XX8_TXCTL, 4, 1, 0), 980c516b4fSNicolin Chen SOC_SINGLE("Mute ADC Serial Port Switch", CS42XX8_TXCTL, 3, 1, 0), 990c516b4fSNicolin Chen SOC_SINGLE("ADC Single Volume Control Switch", CS42XX8_TXCTL, 2, 1, 0), 1000c516b4fSNicolin Chen SOC_ENUM("ADC Soft Ramp & Zero Cross Control Switch", adc_szc_enum), 1010c516b4fSNicolin Chen }; 1020c516b4fSNicolin Chen 1030c516b4fSNicolin Chen static const struct snd_kcontrol_new cs42xx8_adc3_snd_controls[] = { 1040c516b4fSNicolin Chen SOC_DOUBLE_R_S_TLV("ADC3 Capture Volume", CS42XX8_VOLAIN5, 1050c516b4fSNicolin Chen CS42XX8_VOLAIN6, 0, -0x80, 0x30, 7, 0, adc_tlv), 1060c516b4fSNicolin Chen SOC_DOUBLE("ADC3 Invert Switch", CS42XX8_ADCINV, 4, 5, 1, 0), 1070c516b4fSNicolin Chen SOC_ENUM("ADC3 Single Ended Mode Switch", adc3_single_enum), 1080c516b4fSNicolin Chen }; 1090c516b4fSNicolin Chen 1100c516b4fSNicolin Chen static const struct snd_soc_dapm_widget cs42xx8_dapm_widgets[] = { 1110c516b4fSNicolin Chen SND_SOC_DAPM_DAC("DAC1", "Playback", CS42XX8_PWRCTL, 1, 1), 1120c516b4fSNicolin Chen SND_SOC_DAPM_DAC("DAC2", "Playback", CS42XX8_PWRCTL, 2, 1), 1130c516b4fSNicolin Chen SND_SOC_DAPM_DAC("DAC3", "Playback", CS42XX8_PWRCTL, 3, 1), 1140c516b4fSNicolin Chen SND_SOC_DAPM_DAC("DAC4", "Playback", CS42XX8_PWRCTL, 4, 1), 1150c516b4fSNicolin Chen 1160c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT1L"), 1170c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT1R"), 1180c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT2L"), 1190c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT2R"), 1200c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT3L"), 1210c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT3R"), 1220c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT4L"), 1230c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT4R"), 1240c516b4fSNicolin Chen 1250c516b4fSNicolin Chen SND_SOC_DAPM_ADC("ADC1", "Capture", CS42XX8_PWRCTL, 5, 1), 1260c516b4fSNicolin Chen SND_SOC_DAPM_ADC("ADC2", "Capture", CS42XX8_PWRCTL, 6, 1), 1270c516b4fSNicolin Chen 1280c516b4fSNicolin Chen SND_SOC_DAPM_INPUT("AIN1L"), 1290c516b4fSNicolin Chen SND_SOC_DAPM_INPUT("AIN1R"), 1300c516b4fSNicolin Chen SND_SOC_DAPM_INPUT("AIN2L"), 1310c516b4fSNicolin Chen SND_SOC_DAPM_INPUT("AIN2R"), 1320c516b4fSNicolin Chen 1330c516b4fSNicolin Chen SND_SOC_DAPM_SUPPLY("PWR", CS42XX8_PWRCTL, 0, 1, NULL, 0), 1340c516b4fSNicolin Chen }; 1350c516b4fSNicolin Chen 1360c516b4fSNicolin Chen static const struct snd_soc_dapm_widget cs42xx8_adc3_dapm_widgets[] = { 1370c516b4fSNicolin Chen SND_SOC_DAPM_ADC("ADC3", "Capture", CS42XX8_PWRCTL, 7, 1), 1380c516b4fSNicolin Chen 1390c516b4fSNicolin Chen SND_SOC_DAPM_INPUT("AIN3L"), 1400c516b4fSNicolin Chen SND_SOC_DAPM_INPUT("AIN3R"), 1410c516b4fSNicolin Chen }; 1420c516b4fSNicolin Chen 1430c516b4fSNicolin Chen static const struct snd_soc_dapm_route cs42xx8_dapm_routes[] = { 1440c516b4fSNicolin Chen /* Playback */ 1450c516b4fSNicolin Chen { "AOUT1L", NULL, "DAC1" }, 1460c516b4fSNicolin Chen { "AOUT1R", NULL, "DAC1" }, 1470c516b4fSNicolin Chen { "DAC1", NULL, "PWR" }, 1480c516b4fSNicolin Chen 1490c516b4fSNicolin Chen { "AOUT2L", NULL, "DAC2" }, 1500c516b4fSNicolin Chen { "AOUT2R", NULL, "DAC2" }, 1510c516b4fSNicolin Chen { "DAC2", NULL, "PWR" }, 1520c516b4fSNicolin Chen 1530c516b4fSNicolin Chen { "AOUT3L", NULL, "DAC3" }, 1540c516b4fSNicolin Chen { "AOUT3R", NULL, "DAC3" }, 1550c516b4fSNicolin Chen { "DAC3", NULL, "PWR" }, 1560c516b4fSNicolin Chen 1570c516b4fSNicolin Chen { "AOUT4L", NULL, "DAC4" }, 1580c516b4fSNicolin Chen { "AOUT4R", NULL, "DAC4" }, 1590c516b4fSNicolin Chen { "DAC4", NULL, "PWR" }, 1600c516b4fSNicolin Chen 1610c516b4fSNicolin Chen /* Capture */ 1620c516b4fSNicolin Chen { "ADC1", NULL, "AIN1L" }, 1630c516b4fSNicolin Chen { "ADC1", NULL, "AIN1R" }, 1640c516b4fSNicolin Chen { "ADC1", NULL, "PWR" }, 1650c516b4fSNicolin Chen 1660c516b4fSNicolin Chen { "ADC2", NULL, "AIN2L" }, 1670c516b4fSNicolin Chen { "ADC2", NULL, "AIN2R" }, 1680c516b4fSNicolin Chen { "ADC2", NULL, "PWR" }, 1690c516b4fSNicolin Chen }; 1700c516b4fSNicolin Chen 1710c516b4fSNicolin Chen static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = { 1720c516b4fSNicolin Chen /* Capture */ 1730c516b4fSNicolin Chen { "ADC3", NULL, "AIN3L" }, 1740c516b4fSNicolin Chen { "ADC3", NULL, "AIN3R" }, 1750c516b4fSNicolin Chen { "ADC3", NULL, "PWR" }, 1760c516b4fSNicolin Chen }; 1770c516b4fSNicolin Chen 1780c516b4fSNicolin Chen struct cs42xx8_ratios { 17948dfd37aSShengjiu Wang unsigned int mfreq; 18048dfd37aSShengjiu Wang unsigned int min_mclk; 18148dfd37aSShengjiu Wang unsigned int max_mclk; 18248dfd37aSShengjiu Wang unsigned int ratio[3]; 1830c516b4fSNicolin Chen }; 1840c516b4fSNicolin Chen 18548dfd37aSShengjiu Wang /* 18648dfd37aSShengjiu Wang * According to reference mannual, define the cs42xx8_ratio struct 18748dfd37aSShengjiu Wang * MFreq2 | MFreq1 | MFreq0 | Description | SSM | DSM | QSM | 18848dfd37aSShengjiu Wang * 0 | 0 | 0 |1.029MHz to 12.8MHz | 256 | 128 | 64 | 18948dfd37aSShengjiu Wang * 0 | 0 | 1 |1.536MHz to 19.2MHz | 384 | 192 | 96 | 19048dfd37aSShengjiu Wang * 0 | 1 | 0 |2.048MHz to 25.6MHz | 512 | 256 | 128 | 19148dfd37aSShengjiu Wang * 0 | 1 | 1 |3.072MHz to 38.4MHz | 768 | 384 | 192 | 19248dfd37aSShengjiu Wang * 1 | x | x |4.096MHz to 51.2MHz |1024 | 512 | 256 | 19348dfd37aSShengjiu Wang */ 1940c516b4fSNicolin Chen static const struct cs42xx8_ratios cs42xx8_ratios[] = { 19548dfd37aSShengjiu Wang { 0, 1029000, 12800000, {256, 128, 64} }, 19648dfd37aSShengjiu Wang { 2, 1536000, 19200000, {384, 192, 96} }, 19748dfd37aSShengjiu Wang { 4, 2048000, 25600000, {512, 256, 128} }, 19848dfd37aSShengjiu Wang { 6, 3072000, 38400000, {768, 384, 192} }, 19948dfd37aSShengjiu Wang { 8, 4096000, 51200000, {1024, 512, 256} }, 2000c516b4fSNicolin Chen }; 2010c516b4fSNicolin Chen 2020c516b4fSNicolin Chen static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai, 2030c516b4fSNicolin Chen int clk_id, unsigned int freq, int dir) 2040c516b4fSNicolin Chen { 20599a9f452SKuninori Morimoto struct snd_soc_component *component = codec_dai->component; 20699a9f452SKuninori Morimoto struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); 2070c516b4fSNicolin Chen 2080c516b4fSNicolin Chen cs42xx8->sysclk = freq; 2090c516b4fSNicolin Chen 2100c516b4fSNicolin Chen return 0; 2110c516b4fSNicolin Chen } 2120c516b4fSNicolin Chen 2130c516b4fSNicolin Chen static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai, 2140c516b4fSNicolin Chen unsigned int format) 2150c516b4fSNicolin Chen { 21699a9f452SKuninori Morimoto struct snd_soc_component *component = codec_dai->component; 21799a9f452SKuninori Morimoto struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); 2180c516b4fSNicolin Chen u32 val; 2190c516b4fSNicolin Chen 2200c516b4fSNicolin Chen /* Set DAI format */ 2210c516b4fSNicolin Chen switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { 2220c516b4fSNicolin Chen case SND_SOC_DAIFMT_LEFT_J: 2230c516b4fSNicolin Chen val = CS42XX8_INTF_DAC_DIF_LEFTJ | CS42XX8_INTF_ADC_DIF_LEFTJ; 2240c516b4fSNicolin Chen break; 2250c516b4fSNicolin Chen case SND_SOC_DAIFMT_I2S: 2260c516b4fSNicolin Chen val = CS42XX8_INTF_DAC_DIF_I2S | CS42XX8_INTF_ADC_DIF_I2S; 2270c516b4fSNicolin Chen break; 2280c516b4fSNicolin Chen case SND_SOC_DAIFMT_RIGHT_J: 2290c516b4fSNicolin Chen val = CS42XX8_INTF_DAC_DIF_RIGHTJ | CS42XX8_INTF_ADC_DIF_RIGHTJ; 2300c516b4fSNicolin Chen break; 231689dc643SShengjiu Wang case SND_SOC_DAIFMT_DSP_A: 232689dc643SShengjiu Wang val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM; 233689dc643SShengjiu Wang break; 2340c516b4fSNicolin Chen default: 23599a9f452SKuninori Morimoto dev_err(component->dev, "unsupported dai format\n"); 2360c516b4fSNicolin Chen return -EINVAL; 2370c516b4fSNicolin Chen } 2380c516b4fSNicolin Chen 2390c516b4fSNicolin Chen regmap_update_bits(cs42xx8->regmap, CS42XX8_INTF, 2400c516b4fSNicolin Chen CS42XX8_INTF_DAC_DIF_MASK | 2410c516b4fSNicolin Chen CS42XX8_INTF_ADC_DIF_MASK, val); 2420c516b4fSNicolin Chen 2430c516b4fSNicolin Chen /* Set master/slave audio interface */ 2440c516b4fSNicolin Chen switch (format & SND_SOC_DAIFMT_MASTER_MASK) { 2450c516b4fSNicolin Chen case SND_SOC_DAIFMT_CBS_CFS: 2460c516b4fSNicolin Chen cs42xx8->slave_mode = true; 2470c516b4fSNicolin Chen break; 2480c516b4fSNicolin Chen case SND_SOC_DAIFMT_CBM_CFM: 2490c516b4fSNicolin Chen cs42xx8->slave_mode = false; 2500c516b4fSNicolin Chen break; 2510c516b4fSNicolin Chen default: 25299a9f452SKuninori Morimoto dev_err(component->dev, "unsupported master/slave mode\n"); 2530c516b4fSNicolin Chen return -EINVAL; 2540c516b4fSNicolin Chen } 2550c516b4fSNicolin Chen 2560c516b4fSNicolin Chen return 0; 2570c516b4fSNicolin Chen } 2580c516b4fSNicolin Chen 2590c516b4fSNicolin Chen static int cs42xx8_hw_params(struct snd_pcm_substream *substream, 2600c516b4fSNicolin Chen struct snd_pcm_hw_params *params, 2610c516b4fSNicolin Chen struct snd_soc_dai *dai) 2620c516b4fSNicolin Chen { 26399a9f452SKuninori Morimoto struct snd_soc_component *component = dai->component; 26499a9f452SKuninori Morimoto struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); 2650c516b4fSNicolin Chen bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 26648dfd37aSShengjiu Wang u32 ratio[2]; 26748dfd37aSShengjiu Wang u32 rate[2]; 26848dfd37aSShengjiu Wang u32 fm[2]; 26948dfd37aSShengjiu Wang u32 i, val, mask; 27048dfd37aSShengjiu Wang bool condition1, condition2; 2710c516b4fSNicolin Chen 2721f1e60c9SZidan Wang if (tx) 2731f1e60c9SZidan Wang cs42xx8->tx_channels = params_channels(params); 2741f1e60c9SZidan Wang 27548dfd37aSShengjiu Wang rate[tx] = params_rate(params); 27648dfd37aSShengjiu Wang rate[!tx] = cs42xx8->rate[!tx]; 27748dfd37aSShengjiu Wang 27848dfd37aSShengjiu Wang ratio[tx] = rate[tx] > 0 ? cs42xx8->sysclk / rate[tx] : 0; 27948dfd37aSShengjiu Wang ratio[!tx] = rate[!tx] > 0 ? cs42xx8->sysclk / rate[!tx] : 0; 28048dfd37aSShengjiu Wang 28148dfd37aSShengjiu Wang /* Get functional mode for tx and rx according to rate */ 28248dfd37aSShengjiu Wang for (i = 0; i < 2; i++) { 28348dfd37aSShengjiu Wang if (cs42xx8->slave_mode) { 28448dfd37aSShengjiu Wang fm[i] = CS42XX8_FM_AUTO; 28548dfd37aSShengjiu Wang } else { 28648dfd37aSShengjiu Wang if (rate[i] < 50000) { 28748dfd37aSShengjiu Wang fm[i] = CS42XX8_FM_SINGLE; 28848dfd37aSShengjiu Wang } else if (rate[i] > 50000 && rate[i] < 100000) { 28948dfd37aSShengjiu Wang fm[i] = CS42XX8_FM_DOUBLE; 29048dfd37aSShengjiu Wang } else if (rate[i] > 100000 && rate[i] < 200000) { 29148dfd37aSShengjiu Wang fm[i] = CS42XX8_FM_QUAD; 29248dfd37aSShengjiu Wang } else { 29348dfd37aSShengjiu Wang dev_err(component->dev, 29448dfd37aSShengjiu Wang "unsupported sample rate\n"); 29548dfd37aSShengjiu Wang return -EINVAL; 29648dfd37aSShengjiu Wang } 29748dfd37aSShengjiu Wang } 29848dfd37aSShengjiu Wang } 29948dfd37aSShengjiu Wang 3000c516b4fSNicolin Chen for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { 30148dfd37aSShengjiu Wang /* Is the ratio[tx] valid ? */ 30248dfd37aSShengjiu Wang condition1 = ((fm[tx] == CS42XX8_FM_AUTO) ? 30348dfd37aSShengjiu Wang (cs42xx8_ratios[i].ratio[0] == ratio[tx] || 30448dfd37aSShengjiu Wang cs42xx8_ratios[i].ratio[1] == ratio[tx] || 30548dfd37aSShengjiu Wang cs42xx8_ratios[i].ratio[2] == ratio[tx]) : 30648dfd37aSShengjiu Wang (cs42xx8_ratios[i].ratio[fm[tx]] == ratio[tx])) && 30748dfd37aSShengjiu Wang cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk && 30848dfd37aSShengjiu Wang cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk; 30948dfd37aSShengjiu Wang 31048dfd37aSShengjiu Wang if (!ratio[tx]) 31148dfd37aSShengjiu Wang condition1 = true; 31248dfd37aSShengjiu Wang 31348dfd37aSShengjiu Wang /* Is the ratio[!tx] valid ? */ 31448dfd37aSShengjiu Wang condition2 = ((fm[!tx] == CS42XX8_FM_AUTO) ? 31548dfd37aSShengjiu Wang (cs42xx8_ratios[i].ratio[0] == ratio[!tx] || 31648dfd37aSShengjiu Wang cs42xx8_ratios[i].ratio[1] == ratio[!tx] || 31748dfd37aSShengjiu Wang cs42xx8_ratios[i].ratio[2] == ratio[!tx]) : 31848dfd37aSShengjiu Wang (cs42xx8_ratios[i].ratio[fm[!tx]] == ratio[!tx])); 31948dfd37aSShengjiu Wang 32048dfd37aSShengjiu Wang if (!ratio[!tx]) 32148dfd37aSShengjiu Wang condition2 = true; 32248dfd37aSShengjiu Wang 32348dfd37aSShengjiu Wang /* 32448dfd37aSShengjiu Wang * Both ratio[tx] and ratio[!tx] is valid, then we get 32548dfd37aSShengjiu Wang * a proper MFreq. 32648dfd37aSShengjiu Wang */ 32748dfd37aSShengjiu Wang if (condition1 && condition2) 3280c516b4fSNicolin Chen break; 3290c516b4fSNicolin Chen } 3300c516b4fSNicolin Chen 3310c516b4fSNicolin Chen if (i == ARRAY_SIZE(cs42xx8_ratios)) { 33299a9f452SKuninori Morimoto dev_err(component->dev, "unsupported sysclk ratio\n"); 3330c516b4fSNicolin Chen return -EINVAL; 3340c516b4fSNicolin Chen } 3350c516b4fSNicolin Chen 33648dfd37aSShengjiu Wang cs42xx8->rate[tx] = params_rate(params); 3370c516b4fSNicolin Chen 33848dfd37aSShengjiu Wang mask = CS42XX8_FUNCMOD_MFREQ_MASK; 33948dfd37aSShengjiu Wang val = cs42xx8_ratios[i].mfreq; 3400c516b4fSNicolin Chen 3410c516b4fSNicolin Chen regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD, 3420c516b4fSNicolin Chen CS42XX8_FUNCMOD_xC_FM_MASK(tx) | mask, 34348dfd37aSShengjiu Wang CS42XX8_FUNCMOD_xC_FM(tx, fm[tx]) | val); 3440c516b4fSNicolin Chen 3450c516b4fSNicolin Chen return 0; 3460c516b4fSNicolin Chen } 3470c516b4fSNicolin Chen 34848dfd37aSShengjiu Wang static int cs42xx8_hw_free(struct snd_pcm_substream *substream, 34948dfd37aSShengjiu Wang struct snd_soc_dai *dai) 35048dfd37aSShengjiu Wang { 35148dfd37aSShengjiu Wang struct snd_soc_component *component = dai->component; 35248dfd37aSShengjiu Wang struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); 35348dfd37aSShengjiu Wang bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 35448dfd37aSShengjiu Wang 35548dfd37aSShengjiu Wang /* Clear stored rate */ 35648dfd37aSShengjiu Wang cs42xx8->rate[tx] = 0; 35748dfd37aSShengjiu Wang 35848dfd37aSShengjiu Wang regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD, 35948dfd37aSShengjiu Wang CS42XX8_FUNCMOD_xC_FM_MASK(tx), 36048dfd37aSShengjiu Wang CS42XX8_FUNCMOD_xC_FM(tx, CS42XX8_FM_AUTO)); 36148dfd37aSShengjiu Wang return 0; 36248dfd37aSShengjiu Wang } 36348dfd37aSShengjiu Wang 36403c0f1b5SKuninori Morimoto static int cs42xx8_mute(struct snd_soc_dai *dai, int mute, int direction) 3650c516b4fSNicolin Chen { 36699a9f452SKuninori Morimoto struct snd_soc_component *component = dai->component; 36799a9f452SKuninori Morimoto struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); 3681f1e60c9SZidan Wang u8 dac_unmute = cs42xx8->tx_channels ? 3691f1e60c9SZidan Wang ~((0x1 << cs42xx8->tx_channels) - 1) : 0; 3700c516b4fSNicolin Chen 3711f1e60c9SZidan Wang regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, 3721f1e60c9SZidan Wang mute ? CS42XX8_DACMUTE_ALL : dac_unmute); 3730c516b4fSNicolin Chen 3740c516b4fSNicolin Chen return 0; 3750c516b4fSNicolin Chen } 3760c516b4fSNicolin Chen 3770c516b4fSNicolin Chen static const struct snd_soc_dai_ops cs42xx8_dai_ops = { 3780c516b4fSNicolin Chen .set_fmt = cs42xx8_set_dai_fmt, 3790c516b4fSNicolin Chen .set_sysclk = cs42xx8_set_dai_sysclk, 3800c516b4fSNicolin Chen .hw_params = cs42xx8_hw_params, 38148dfd37aSShengjiu Wang .hw_free = cs42xx8_hw_free, 38203c0f1b5SKuninori Morimoto .mute_stream = cs42xx8_mute, 38303c0f1b5SKuninori Morimoto .no_capture_mute = 1, 3840c516b4fSNicolin Chen }; 3850c516b4fSNicolin Chen 3860c516b4fSNicolin Chen static struct snd_soc_dai_driver cs42xx8_dai = { 3870c516b4fSNicolin Chen .playback = { 3880c516b4fSNicolin Chen .stream_name = "Playback", 3890c516b4fSNicolin Chen .channels_min = 1, 3900c516b4fSNicolin Chen .channels_max = 8, 3910c516b4fSNicolin Chen .rates = SNDRV_PCM_RATE_8000_192000, 3920c516b4fSNicolin Chen .formats = CS42XX8_FORMATS, 3930c516b4fSNicolin Chen }, 3940c516b4fSNicolin Chen .capture = { 3950c516b4fSNicolin Chen .stream_name = "Capture", 3960c516b4fSNicolin Chen .channels_min = 1, 3970c516b4fSNicolin Chen .rates = SNDRV_PCM_RATE_8000_192000, 3980c516b4fSNicolin Chen .formats = CS42XX8_FORMATS, 3990c516b4fSNicolin Chen }, 4000c516b4fSNicolin Chen .ops = &cs42xx8_dai_ops, 4010c516b4fSNicolin Chen }; 4020c516b4fSNicolin Chen 4030c516b4fSNicolin Chen static const struct reg_default cs42xx8_reg[] = { 4040c516b4fSNicolin Chen { 0x02, 0x00 }, /* Power Control */ 4050c516b4fSNicolin Chen { 0x03, 0xF0 }, /* Functional Mode */ 4060c516b4fSNicolin Chen { 0x04, 0x46 }, /* Interface Formats */ 4070c516b4fSNicolin Chen { 0x05, 0x00 }, /* ADC Control & DAC De-Emphasis */ 4080c516b4fSNicolin Chen { 0x06, 0x10 }, /* Transition Control */ 4090c516b4fSNicolin Chen { 0x07, 0x00 }, /* DAC Channel Mute */ 4100c516b4fSNicolin Chen { 0x08, 0x00 }, /* Volume Control AOUT1 */ 4110c516b4fSNicolin Chen { 0x09, 0x00 }, /* Volume Control AOUT2 */ 4120c516b4fSNicolin Chen { 0x0a, 0x00 }, /* Volume Control AOUT3 */ 4130c516b4fSNicolin Chen { 0x0b, 0x00 }, /* Volume Control AOUT4 */ 4140c516b4fSNicolin Chen { 0x0c, 0x00 }, /* Volume Control AOUT5 */ 4150c516b4fSNicolin Chen { 0x0d, 0x00 }, /* Volume Control AOUT6 */ 4160c516b4fSNicolin Chen { 0x0e, 0x00 }, /* Volume Control AOUT7 */ 4170c516b4fSNicolin Chen { 0x0f, 0x00 }, /* Volume Control AOUT8 */ 4180c516b4fSNicolin Chen { 0x10, 0x00 }, /* DAC Channel Invert */ 4190c516b4fSNicolin Chen { 0x11, 0x00 }, /* Volume Control AIN1 */ 4200c516b4fSNicolin Chen { 0x12, 0x00 }, /* Volume Control AIN2 */ 4210c516b4fSNicolin Chen { 0x13, 0x00 }, /* Volume Control AIN3 */ 4220c516b4fSNicolin Chen { 0x14, 0x00 }, /* Volume Control AIN4 */ 4230c516b4fSNicolin Chen { 0x15, 0x00 }, /* Volume Control AIN5 */ 4240c516b4fSNicolin Chen { 0x16, 0x00 }, /* Volume Control AIN6 */ 4250c516b4fSNicolin Chen { 0x17, 0x00 }, /* ADC Channel Invert */ 4260c516b4fSNicolin Chen { 0x18, 0x00 }, /* Status Control */ 4270c516b4fSNicolin Chen { 0x1a, 0x00 }, /* Status Mask */ 4280c516b4fSNicolin Chen { 0x1b, 0x00 }, /* MUTEC Pin Control */ 4290c516b4fSNicolin Chen }; 4300c516b4fSNicolin Chen 4310c516b4fSNicolin Chen static bool cs42xx8_volatile_register(struct device *dev, unsigned int reg) 4320c516b4fSNicolin Chen { 4330c516b4fSNicolin Chen switch (reg) { 4340c516b4fSNicolin Chen case CS42XX8_STATUS: 4350c516b4fSNicolin Chen return true; 4360c516b4fSNicolin Chen default: 4370c516b4fSNicolin Chen return false; 4380c516b4fSNicolin Chen } 4390c516b4fSNicolin Chen } 4400c516b4fSNicolin Chen 4410c516b4fSNicolin Chen static bool cs42xx8_writeable_register(struct device *dev, unsigned int reg) 4420c516b4fSNicolin Chen { 4430c516b4fSNicolin Chen switch (reg) { 4440c516b4fSNicolin Chen case CS42XX8_CHIPID: 4450c516b4fSNicolin Chen case CS42XX8_STATUS: 4460c516b4fSNicolin Chen return false; 4470c516b4fSNicolin Chen default: 4480c516b4fSNicolin Chen return true; 4490c516b4fSNicolin Chen } 4500c516b4fSNicolin Chen } 4510c516b4fSNicolin Chen 4520c516b4fSNicolin Chen const struct regmap_config cs42xx8_regmap_config = { 4530c516b4fSNicolin Chen .reg_bits = 8, 4540c516b4fSNicolin Chen .val_bits = 8, 4550c516b4fSNicolin Chen 4560c516b4fSNicolin Chen .max_register = CS42XX8_LASTREG, 4570c516b4fSNicolin Chen .reg_defaults = cs42xx8_reg, 4580c516b4fSNicolin Chen .num_reg_defaults = ARRAY_SIZE(cs42xx8_reg), 4590c516b4fSNicolin Chen .volatile_reg = cs42xx8_volatile_register, 4600c516b4fSNicolin Chen .writeable_reg = cs42xx8_writeable_register, 4610c516b4fSNicolin Chen .cache_type = REGCACHE_RBTREE, 4620c516b4fSNicolin Chen }; 4630c516b4fSNicolin Chen EXPORT_SYMBOL_GPL(cs42xx8_regmap_config); 4640c516b4fSNicolin Chen 46599a9f452SKuninori Morimoto static int cs42xx8_component_probe(struct snd_soc_component *component) 4660c516b4fSNicolin Chen { 46799a9f452SKuninori Morimoto struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); 46899a9f452SKuninori Morimoto struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 4690c516b4fSNicolin Chen 4700c516b4fSNicolin Chen switch (cs42xx8->drvdata->num_adcs) { 4710c516b4fSNicolin Chen case 3: 47299a9f452SKuninori Morimoto snd_soc_add_component_controls(component, cs42xx8_adc3_snd_controls, 4730c516b4fSNicolin Chen ARRAY_SIZE(cs42xx8_adc3_snd_controls)); 4740c516b4fSNicolin Chen snd_soc_dapm_new_controls(dapm, cs42xx8_adc3_dapm_widgets, 4750c516b4fSNicolin Chen ARRAY_SIZE(cs42xx8_adc3_dapm_widgets)); 4760c516b4fSNicolin Chen snd_soc_dapm_add_routes(dapm, cs42xx8_adc3_dapm_routes, 4770c516b4fSNicolin Chen ARRAY_SIZE(cs42xx8_adc3_dapm_routes)); 4780c516b4fSNicolin Chen break; 4790c516b4fSNicolin Chen default: 4800c516b4fSNicolin Chen break; 4810c516b4fSNicolin Chen } 4820c516b4fSNicolin Chen 4830c516b4fSNicolin Chen /* Mute all DAC channels */ 4840c516b4fSNicolin Chen regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, CS42XX8_DACMUTE_ALL); 4850c516b4fSNicolin Chen 4860c516b4fSNicolin Chen return 0; 4870c516b4fSNicolin Chen } 4880c516b4fSNicolin Chen 48999a9f452SKuninori Morimoto static const struct snd_soc_component_driver cs42xx8_driver = { 49099a9f452SKuninori Morimoto .probe = cs42xx8_component_probe, 4910c516b4fSNicolin Chen .controls = cs42xx8_snd_controls, 4920c516b4fSNicolin Chen .num_controls = ARRAY_SIZE(cs42xx8_snd_controls), 4930c516b4fSNicolin Chen .dapm_widgets = cs42xx8_dapm_widgets, 4940c516b4fSNicolin Chen .num_dapm_widgets = ARRAY_SIZE(cs42xx8_dapm_widgets), 4950c516b4fSNicolin Chen .dapm_routes = cs42xx8_dapm_routes, 4960c516b4fSNicolin Chen .num_dapm_routes = ARRAY_SIZE(cs42xx8_dapm_routes), 49799a9f452SKuninori Morimoto .use_pmdown_time = 1, 49899a9f452SKuninori Morimoto .endianness = 1, 4990c516b4fSNicolin Chen }; 5000c516b4fSNicolin Chen 5010c516b4fSNicolin Chen const struct cs42xx8_driver_data cs42448_data = { 5020c516b4fSNicolin Chen .name = "cs42448", 5030c516b4fSNicolin Chen .num_adcs = 3, 5040c516b4fSNicolin Chen }; 5050c516b4fSNicolin Chen EXPORT_SYMBOL_GPL(cs42448_data); 5060c516b4fSNicolin Chen 5070c516b4fSNicolin Chen const struct cs42xx8_driver_data cs42888_data = { 5080c516b4fSNicolin Chen .name = "cs42888", 5090c516b4fSNicolin Chen .num_adcs = 2, 5100c516b4fSNicolin Chen }; 5110c516b4fSNicolin Chen EXPORT_SYMBOL_GPL(cs42888_data); 5120c516b4fSNicolin Chen 513*e5afc867SPeter Bergin int cs42xx8_probe(struct device *dev, struct regmap *regmap, struct cs42xx8_driver_data *drvdata) 5140c516b4fSNicolin Chen { 5150c516b4fSNicolin Chen struct cs42xx8_priv *cs42xx8; 5160c516b4fSNicolin Chen int ret, val, i; 5170c516b4fSNicolin Chen 518d375d0abSAxel Lin if (IS_ERR(regmap)) { 519d375d0abSAxel Lin ret = PTR_ERR(regmap); 520d375d0abSAxel Lin dev_err(dev, "failed to allocate regmap: %d\n", ret); 521d375d0abSAxel Lin return ret; 522d375d0abSAxel Lin } 523d375d0abSAxel Lin 5240c516b4fSNicolin Chen cs42xx8 = devm_kzalloc(dev, sizeof(*cs42xx8), GFP_KERNEL); 5250c516b4fSNicolin Chen if (cs42xx8 == NULL) 5260c516b4fSNicolin Chen return -ENOMEM; 5270c516b4fSNicolin Chen 5280c516b4fSNicolin Chen dev_set_drvdata(dev, cs42xx8); 5290c516b4fSNicolin Chen 530*e5afc867SPeter Bergin cs42xx8->regmap = regmap; 5310c516b4fSNicolin Chen 532*e5afc867SPeter Bergin cs42xx8->drvdata = drvdata; 5330c516b4fSNicolin Chen 534bfe95dfaSS.j. Wang cs42xx8->gpiod_reset = devm_gpiod_get_optional(dev, "reset", 535bfe95dfaSS.j. Wang GPIOD_OUT_HIGH); 536bfe95dfaSS.j. Wang if (IS_ERR(cs42xx8->gpiod_reset)) 537bfe95dfaSS.j. Wang return PTR_ERR(cs42xx8->gpiod_reset); 538bfe95dfaSS.j. Wang 539bfe95dfaSS.j. Wang gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 0); 540bfe95dfaSS.j. Wang 5410c516b4fSNicolin Chen cs42xx8->clk = devm_clk_get(dev, "mclk"); 5420c516b4fSNicolin Chen if (IS_ERR(cs42xx8->clk)) { 5430c516b4fSNicolin Chen dev_err(dev, "failed to get the clock: %ld\n", 5440c516b4fSNicolin Chen PTR_ERR(cs42xx8->clk)); 5450c516b4fSNicolin Chen return -EINVAL; 5460c516b4fSNicolin Chen } 5470c516b4fSNicolin Chen 5480c516b4fSNicolin Chen cs42xx8->sysclk = clk_get_rate(cs42xx8->clk); 5490c516b4fSNicolin Chen 5500c516b4fSNicolin Chen for (i = 0; i < ARRAY_SIZE(cs42xx8->supplies); i++) 5510c516b4fSNicolin Chen cs42xx8->supplies[i].supply = cs42xx8_supply_names[i]; 5520c516b4fSNicolin Chen 5530c516b4fSNicolin Chen ret = devm_regulator_bulk_get(dev, 5540c516b4fSNicolin Chen ARRAY_SIZE(cs42xx8->supplies), cs42xx8->supplies); 5550c516b4fSNicolin Chen if (ret) { 5560c516b4fSNicolin Chen dev_err(dev, "failed to request supplies: %d\n", ret); 5570c516b4fSNicolin Chen return ret; 5580c516b4fSNicolin Chen } 5590c516b4fSNicolin Chen 5600c516b4fSNicolin Chen ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies), 5610c516b4fSNicolin Chen cs42xx8->supplies); 5620c516b4fSNicolin Chen if (ret) { 5630c516b4fSNicolin Chen dev_err(dev, "failed to enable supplies: %d\n", ret); 5640c516b4fSNicolin Chen return ret; 5650c516b4fSNicolin Chen } 5660c516b4fSNicolin Chen 5670c516b4fSNicolin Chen /* Make sure hardware reset done */ 5680c516b4fSNicolin Chen msleep(5); 5690c516b4fSNicolin Chen 5700c516b4fSNicolin Chen /* Validate the chip ID */ 57106b4b813SAxel Lin ret = regmap_read(cs42xx8->regmap, CS42XX8_CHIPID, &val); 57206b4b813SAxel Lin if (ret < 0) { 57306b4b813SAxel Lin dev_err(dev, "failed to get device ID, ret = %d", ret); 5740c516b4fSNicolin Chen goto err_enable; 5750c516b4fSNicolin Chen } 5760c516b4fSNicolin Chen 5770c516b4fSNicolin Chen /* The top four bits of the chip ID should be 0000 */ 57806b4b813SAxel Lin if (((val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4) != 0x00) { 5790c516b4fSNicolin Chen dev_err(dev, "unmatched chip ID: %d\n", 58006b4b813SAxel Lin (val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4); 5810c516b4fSNicolin Chen ret = -EINVAL; 5820c516b4fSNicolin Chen goto err_enable; 5830c516b4fSNicolin Chen } 5840c516b4fSNicolin Chen 5850c516b4fSNicolin Chen dev_info(dev, "found device, revision %X\n", 5860c516b4fSNicolin Chen val & CS42XX8_CHIPID_REV_ID_MASK); 5870c516b4fSNicolin Chen 5880c516b4fSNicolin Chen cs42xx8_dai.name = cs42xx8->drvdata->name; 5890c516b4fSNicolin Chen 5900c516b4fSNicolin Chen /* Each adc supports stereo input */ 5910c516b4fSNicolin Chen cs42xx8_dai.capture.channels_max = cs42xx8->drvdata->num_adcs * 2; 5920c516b4fSNicolin Chen 59399a9f452SKuninori Morimoto ret = devm_snd_soc_register_component(dev, &cs42xx8_driver, &cs42xx8_dai, 1); 5940c516b4fSNicolin Chen if (ret) { 59599a9f452SKuninori Morimoto dev_err(dev, "failed to register component:%d\n", ret); 5960c516b4fSNicolin Chen goto err_enable; 5970c516b4fSNicolin Chen } 5980c516b4fSNicolin Chen 5990c516b4fSNicolin Chen regcache_cache_only(cs42xx8->regmap, true); 6000c516b4fSNicolin Chen 6010c516b4fSNicolin Chen err_enable: 6020c516b4fSNicolin Chen regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies), 6030c516b4fSNicolin Chen cs42xx8->supplies); 6040c516b4fSNicolin Chen 6050c516b4fSNicolin Chen return ret; 6060c516b4fSNicolin Chen } 6070c516b4fSNicolin Chen EXPORT_SYMBOL_GPL(cs42xx8_probe); 6080c516b4fSNicolin Chen 609641d334bSRafael J. Wysocki #ifdef CONFIG_PM 6100c516b4fSNicolin Chen static int cs42xx8_runtime_resume(struct device *dev) 6110c516b4fSNicolin Chen { 6120c516b4fSNicolin Chen struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev); 6130c516b4fSNicolin Chen int ret; 6140c516b4fSNicolin Chen 6150c516b4fSNicolin Chen ret = clk_prepare_enable(cs42xx8->clk); 6160c516b4fSNicolin Chen if (ret) { 6170c516b4fSNicolin Chen dev_err(dev, "failed to enable mclk: %d\n", ret); 6180c516b4fSNicolin Chen return ret; 6190c516b4fSNicolin Chen } 6200c516b4fSNicolin Chen 621bfe95dfaSS.j. Wang gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 0); 622bfe95dfaSS.j. Wang 6230c516b4fSNicolin Chen ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies), 6240c516b4fSNicolin Chen cs42xx8->supplies); 6250c516b4fSNicolin Chen if (ret) { 6260c516b4fSNicolin Chen dev_err(dev, "failed to enable supplies: %d\n", ret); 6270c516b4fSNicolin Chen goto err_clk; 6280c516b4fSNicolin Chen } 6290c516b4fSNicolin Chen 6300c516b4fSNicolin Chen /* Make sure hardware reset done */ 6310c516b4fSNicolin Chen msleep(5); 6320c516b4fSNicolin Chen 6330c516b4fSNicolin Chen regcache_cache_only(cs42xx8->regmap, false); 634ad6eecbfSS.j. Wang regcache_mark_dirty(cs42xx8->regmap); 6350c516b4fSNicolin Chen 6360c516b4fSNicolin Chen ret = regcache_sync(cs42xx8->regmap); 6370c516b4fSNicolin Chen if (ret) { 6380c516b4fSNicolin Chen dev_err(dev, "failed to sync regmap: %d\n", ret); 6390c516b4fSNicolin Chen goto err_bulk; 6400c516b4fSNicolin Chen } 6410c516b4fSNicolin Chen 6420c516b4fSNicolin Chen return 0; 6430c516b4fSNicolin Chen 6440c516b4fSNicolin Chen err_bulk: 6450c516b4fSNicolin Chen regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies), 6460c516b4fSNicolin Chen cs42xx8->supplies); 6470c516b4fSNicolin Chen err_clk: 6480c516b4fSNicolin Chen clk_disable_unprepare(cs42xx8->clk); 6490c516b4fSNicolin Chen 6500c516b4fSNicolin Chen return ret; 6510c516b4fSNicolin Chen } 6520c516b4fSNicolin Chen 6530c516b4fSNicolin Chen static int cs42xx8_runtime_suspend(struct device *dev) 6540c516b4fSNicolin Chen { 6550c516b4fSNicolin Chen struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev); 6560c516b4fSNicolin Chen 6570c516b4fSNicolin Chen regcache_cache_only(cs42xx8->regmap, true); 6580c516b4fSNicolin Chen 6590c516b4fSNicolin Chen regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies), 6600c516b4fSNicolin Chen cs42xx8->supplies); 6610c516b4fSNicolin Chen 662bfe95dfaSS.j. Wang gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 1); 663bfe95dfaSS.j. Wang 6640c516b4fSNicolin Chen clk_disable_unprepare(cs42xx8->clk); 6650c516b4fSNicolin Chen 6660c516b4fSNicolin Chen return 0; 6670c516b4fSNicolin Chen } 6680c516b4fSNicolin Chen #endif 6690c516b4fSNicolin Chen 6700c516b4fSNicolin Chen const struct dev_pm_ops cs42xx8_pm = { 671b429ca49SShengjiu Wang SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 672b429ca49SShengjiu Wang pm_runtime_force_resume) 6730c516b4fSNicolin Chen SET_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL) 6740c516b4fSNicolin Chen }; 6750c516b4fSNicolin Chen EXPORT_SYMBOL_GPL(cs42xx8_pm); 6760c516b4fSNicolin Chen 6770c516b4fSNicolin Chen MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec Driver"); 6780c516b4fSNicolin Chen MODULE_AUTHOR("Freescale Semiconductor, Inc."); 6790c516b4fSNicolin Chen MODULE_LICENSE("GPL"); 680