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> 160c516b4fSNicolin Chen #include <linux/of_device.h> 177cda6223SShengjiu Wang #include <linux/gpio/consumer.h> 180c516b4fSNicolin Chen #include <linux/pm_runtime.h> 190c516b4fSNicolin Chen #include <linux/regulator/consumer.h> 200c516b4fSNicolin Chen #include <sound/pcm_params.h> 210c516b4fSNicolin Chen #include <sound/soc.h> 220c516b4fSNicolin Chen #include <sound/tlv.h> 230c516b4fSNicolin Chen 240c516b4fSNicolin Chen #include "cs42xx8.h" 250c516b4fSNicolin Chen 260c516b4fSNicolin Chen #define CS42XX8_NUM_SUPPLIES 4 270c516b4fSNicolin Chen static const char *const cs42xx8_supply_names[CS42XX8_NUM_SUPPLIES] = { 280c516b4fSNicolin Chen "VA", 290c516b4fSNicolin Chen "VD", 300c516b4fSNicolin Chen "VLS", 310c516b4fSNicolin Chen "VLC", 320c516b4fSNicolin Chen }; 330c516b4fSNicolin Chen 340c516b4fSNicolin Chen #define CS42XX8_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ 350c516b4fSNicolin Chen SNDRV_PCM_FMTBIT_S20_3LE | \ 360c516b4fSNicolin Chen SNDRV_PCM_FMTBIT_S24_LE | \ 370c516b4fSNicolin Chen SNDRV_PCM_FMTBIT_S32_LE) 380c516b4fSNicolin Chen 390c516b4fSNicolin Chen /* codec private data */ 400c516b4fSNicolin Chen struct cs42xx8_priv { 410c516b4fSNicolin Chen struct regulator_bulk_data supplies[CS42XX8_NUM_SUPPLIES]; 420c516b4fSNicolin Chen const struct cs42xx8_driver_data *drvdata; 430c516b4fSNicolin Chen struct regmap *regmap; 440c516b4fSNicolin Chen struct clk *clk; 450c516b4fSNicolin Chen 460c516b4fSNicolin Chen bool slave_mode; 470c516b4fSNicolin Chen unsigned long sysclk; 481f1e60c9SZidan Wang u32 tx_channels; 49bfe95dfaSS.j. Wang struct gpio_desc *gpiod_reset; 5048dfd37aSShengjiu Wang u32 rate[2]; 510c516b4fSNicolin Chen }; 520c516b4fSNicolin Chen 530c516b4fSNicolin Chen /* -127.5dB to 0dB with step of 0.5dB */ 540c516b4fSNicolin Chen static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); 550c516b4fSNicolin Chen /* -64dB to 24dB with step of 0.5dB */ 560c516b4fSNicolin Chen static const DECLARE_TLV_DB_SCALE(adc_tlv, -6400, 50, 0); 570c516b4fSNicolin Chen 580c516b4fSNicolin Chen static const char *const cs42xx8_adc_single[] = { "Differential", "Single-Ended" }; 590c516b4fSNicolin Chen static const char *const cs42xx8_szc[] = { "Immediate Change", "Zero Cross", 600c516b4fSNicolin Chen "Soft Ramp", "Soft Ramp on Zero Cross" }; 610c516b4fSNicolin Chen 620c516b4fSNicolin Chen static const struct soc_enum adc1_single_enum = 630c516b4fSNicolin Chen SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 4, 2, cs42xx8_adc_single); 640c516b4fSNicolin Chen static const struct soc_enum adc2_single_enum = 650c516b4fSNicolin Chen SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 3, 2, cs42xx8_adc_single); 660c516b4fSNicolin Chen static const struct soc_enum adc3_single_enum = 670c516b4fSNicolin Chen SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 2, 2, cs42xx8_adc_single); 680c516b4fSNicolin Chen static const struct soc_enum dac_szc_enum = 690c516b4fSNicolin Chen SOC_ENUM_SINGLE(CS42XX8_TXCTL, 5, 4, cs42xx8_szc); 700c516b4fSNicolin Chen static const struct soc_enum adc_szc_enum = 710c516b4fSNicolin Chen SOC_ENUM_SINGLE(CS42XX8_TXCTL, 0, 4, cs42xx8_szc); 720c516b4fSNicolin Chen 730c516b4fSNicolin Chen static const struct snd_kcontrol_new cs42xx8_snd_controls[] = { 740c516b4fSNicolin Chen SOC_DOUBLE_R_TLV("DAC1 Playback Volume", CS42XX8_VOLAOUT1, 750c516b4fSNicolin Chen CS42XX8_VOLAOUT2, 0, 0xff, 1, dac_tlv), 760c516b4fSNicolin Chen SOC_DOUBLE_R_TLV("DAC2 Playback Volume", CS42XX8_VOLAOUT3, 770c516b4fSNicolin Chen CS42XX8_VOLAOUT4, 0, 0xff, 1, dac_tlv), 780c516b4fSNicolin Chen SOC_DOUBLE_R_TLV("DAC3 Playback Volume", CS42XX8_VOLAOUT5, 790c516b4fSNicolin Chen CS42XX8_VOLAOUT6, 0, 0xff, 1, dac_tlv), 800c516b4fSNicolin Chen SOC_DOUBLE_R_TLV("DAC4 Playback Volume", CS42XX8_VOLAOUT7, 810c516b4fSNicolin Chen CS42XX8_VOLAOUT8, 0, 0xff, 1, dac_tlv), 820c516b4fSNicolin Chen SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", CS42XX8_VOLAIN1, 830c516b4fSNicolin Chen CS42XX8_VOLAIN2, 0, -0x80, 0x30, 7, 0, adc_tlv), 840c516b4fSNicolin Chen SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", CS42XX8_VOLAIN3, 850c516b4fSNicolin Chen CS42XX8_VOLAIN4, 0, -0x80, 0x30, 7, 0, adc_tlv), 860c516b4fSNicolin Chen SOC_DOUBLE("DAC1 Invert Switch", CS42XX8_DACINV, 0, 1, 1, 0), 870c516b4fSNicolin Chen SOC_DOUBLE("DAC2 Invert Switch", CS42XX8_DACINV, 2, 3, 1, 0), 880c516b4fSNicolin Chen SOC_DOUBLE("DAC3 Invert Switch", CS42XX8_DACINV, 4, 5, 1, 0), 890c516b4fSNicolin Chen SOC_DOUBLE("DAC4 Invert Switch", CS42XX8_DACINV, 6, 7, 1, 0), 900c516b4fSNicolin Chen SOC_DOUBLE("ADC1 Invert Switch", CS42XX8_ADCINV, 0, 1, 1, 0), 910c516b4fSNicolin Chen SOC_DOUBLE("ADC2 Invert Switch", CS42XX8_ADCINV, 2, 3, 1, 0), 920c516b4fSNicolin Chen SOC_SINGLE("ADC High-Pass Filter Switch", CS42XX8_ADCCTL, 7, 1, 1), 930c516b4fSNicolin Chen SOC_SINGLE("DAC De-emphasis Switch", CS42XX8_ADCCTL, 5, 1, 0), 940c516b4fSNicolin Chen SOC_ENUM("ADC1 Single Ended Mode Switch", adc1_single_enum), 950c516b4fSNicolin Chen SOC_ENUM("ADC2 Single Ended Mode Switch", adc2_single_enum), 960c516b4fSNicolin Chen SOC_SINGLE("DAC Single Volume Control Switch", CS42XX8_TXCTL, 7, 1, 0), 970c516b4fSNicolin Chen SOC_ENUM("DAC Soft Ramp & Zero Cross Control Switch", dac_szc_enum), 980c516b4fSNicolin Chen SOC_SINGLE("DAC Auto Mute Switch", CS42XX8_TXCTL, 4, 1, 0), 990c516b4fSNicolin Chen SOC_SINGLE("Mute ADC Serial Port Switch", CS42XX8_TXCTL, 3, 1, 0), 1000c516b4fSNicolin Chen SOC_SINGLE("ADC Single Volume Control Switch", CS42XX8_TXCTL, 2, 1, 0), 1010c516b4fSNicolin Chen SOC_ENUM("ADC Soft Ramp & Zero Cross Control Switch", adc_szc_enum), 1020c516b4fSNicolin Chen }; 1030c516b4fSNicolin Chen 1040c516b4fSNicolin Chen static const struct snd_kcontrol_new cs42xx8_adc3_snd_controls[] = { 1050c516b4fSNicolin Chen SOC_DOUBLE_R_S_TLV("ADC3 Capture Volume", CS42XX8_VOLAIN5, 1060c516b4fSNicolin Chen CS42XX8_VOLAIN6, 0, -0x80, 0x30, 7, 0, adc_tlv), 1070c516b4fSNicolin Chen SOC_DOUBLE("ADC3 Invert Switch", CS42XX8_ADCINV, 4, 5, 1, 0), 1080c516b4fSNicolin Chen SOC_ENUM("ADC3 Single Ended Mode Switch", adc3_single_enum), 1090c516b4fSNicolin Chen }; 1100c516b4fSNicolin Chen 1110c516b4fSNicolin Chen static const struct snd_soc_dapm_widget cs42xx8_dapm_widgets[] = { 1120c516b4fSNicolin Chen SND_SOC_DAPM_DAC("DAC1", "Playback", CS42XX8_PWRCTL, 1, 1), 1130c516b4fSNicolin Chen SND_SOC_DAPM_DAC("DAC2", "Playback", CS42XX8_PWRCTL, 2, 1), 1140c516b4fSNicolin Chen SND_SOC_DAPM_DAC("DAC3", "Playback", CS42XX8_PWRCTL, 3, 1), 1150c516b4fSNicolin Chen SND_SOC_DAPM_DAC("DAC4", "Playback", CS42XX8_PWRCTL, 4, 1), 1160c516b4fSNicolin Chen 1170c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT1L"), 1180c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT1R"), 1190c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT2L"), 1200c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT2R"), 1210c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT3L"), 1220c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT3R"), 1230c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT4L"), 1240c516b4fSNicolin Chen SND_SOC_DAPM_OUTPUT("AOUT4R"), 1250c516b4fSNicolin Chen 1260c516b4fSNicolin Chen SND_SOC_DAPM_ADC("ADC1", "Capture", CS42XX8_PWRCTL, 5, 1), 1270c516b4fSNicolin Chen SND_SOC_DAPM_ADC("ADC2", "Capture", CS42XX8_PWRCTL, 6, 1), 1280c516b4fSNicolin Chen 1290c516b4fSNicolin Chen SND_SOC_DAPM_INPUT("AIN1L"), 1300c516b4fSNicolin Chen SND_SOC_DAPM_INPUT("AIN1R"), 1310c516b4fSNicolin Chen SND_SOC_DAPM_INPUT("AIN2L"), 1320c516b4fSNicolin Chen SND_SOC_DAPM_INPUT("AIN2R"), 1330c516b4fSNicolin Chen 1340c516b4fSNicolin Chen SND_SOC_DAPM_SUPPLY("PWR", CS42XX8_PWRCTL, 0, 1, NULL, 0), 1350c516b4fSNicolin Chen }; 1360c516b4fSNicolin Chen 1370c516b4fSNicolin Chen static const struct snd_soc_dapm_widget cs42xx8_adc3_dapm_widgets[] = { 1380c516b4fSNicolin Chen SND_SOC_DAPM_ADC("ADC3", "Capture", CS42XX8_PWRCTL, 7, 1), 1390c516b4fSNicolin Chen 1400c516b4fSNicolin Chen SND_SOC_DAPM_INPUT("AIN3L"), 1410c516b4fSNicolin Chen SND_SOC_DAPM_INPUT("AIN3R"), 1420c516b4fSNicolin Chen }; 1430c516b4fSNicolin Chen 1440c516b4fSNicolin Chen static const struct snd_soc_dapm_route cs42xx8_dapm_routes[] = { 1450c516b4fSNicolin Chen /* Playback */ 1460c516b4fSNicolin Chen { "AOUT1L", NULL, "DAC1" }, 1470c516b4fSNicolin Chen { "AOUT1R", NULL, "DAC1" }, 1480c516b4fSNicolin Chen { "DAC1", NULL, "PWR" }, 1490c516b4fSNicolin Chen 1500c516b4fSNicolin Chen { "AOUT2L", NULL, "DAC2" }, 1510c516b4fSNicolin Chen { "AOUT2R", NULL, "DAC2" }, 1520c516b4fSNicolin Chen { "DAC2", NULL, "PWR" }, 1530c516b4fSNicolin Chen 1540c516b4fSNicolin Chen { "AOUT3L", NULL, "DAC3" }, 1550c516b4fSNicolin Chen { "AOUT3R", NULL, "DAC3" }, 1560c516b4fSNicolin Chen { "DAC3", NULL, "PWR" }, 1570c516b4fSNicolin Chen 1580c516b4fSNicolin Chen { "AOUT4L", NULL, "DAC4" }, 1590c516b4fSNicolin Chen { "AOUT4R", NULL, "DAC4" }, 1600c516b4fSNicolin Chen { "DAC4", NULL, "PWR" }, 1610c516b4fSNicolin Chen 1620c516b4fSNicolin Chen /* Capture */ 1630c516b4fSNicolin Chen { "ADC1", NULL, "AIN1L" }, 1640c516b4fSNicolin Chen { "ADC1", NULL, "AIN1R" }, 1650c516b4fSNicolin Chen { "ADC1", NULL, "PWR" }, 1660c516b4fSNicolin Chen 1670c516b4fSNicolin Chen { "ADC2", NULL, "AIN2L" }, 1680c516b4fSNicolin Chen { "ADC2", NULL, "AIN2R" }, 1690c516b4fSNicolin Chen { "ADC2", NULL, "PWR" }, 1700c516b4fSNicolin Chen }; 1710c516b4fSNicolin Chen 1720c516b4fSNicolin Chen static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = { 1730c516b4fSNicolin Chen /* Capture */ 1740c516b4fSNicolin Chen { "ADC3", NULL, "AIN3L" }, 1750c516b4fSNicolin Chen { "ADC3", NULL, "AIN3R" }, 1760c516b4fSNicolin Chen { "ADC3", NULL, "PWR" }, 1770c516b4fSNicolin Chen }; 1780c516b4fSNicolin Chen 1790c516b4fSNicolin Chen struct cs42xx8_ratios { 18048dfd37aSShengjiu Wang unsigned int mfreq; 18148dfd37aSShengjiu Wang unsigned int min_mclk; 18248dfd37aSShengjiu Wang unsigned int max_mclk; 18348dfd37aSShengjiu Wang unsigned int ratio[3]; 1840c516b4fSNicolin Chen }; 1850c516b4fSNicolin Chen 18648dfd37aSShengjiu Wang /* 18748dfd37aSShengjiu Wang * According to reference mannual, define the cs42xx8_ratio struct 18848dfd37aSShengjiu Wang * MFreq2 | MFreq1 | MFreq0 | Description | SSM | DSM | QSM | 18948dfd37aSShengjiu Wang * 0 | 0 | 0 |1.029MHz to 12.8MHz | 256 | 128 | 64 | 19048dfd37aSShengjiu Wang * 0 | 0 | 1 |1.536MHz to 19.2MHz | 384 | 192 | 96 | 19148dfd37aSShengjiu Wang * 0 | 1 | 0 |2.048MHz to 25.6MHz | 512 | 256 | 128 | 19248dfd37aSShengjiu Wang * 0 | 1 | 1 |3.072MHz to 38.4MHz | 768 | 384 | 192 | 19348dfd37aSShengjiu Wang * 1 | x | x |4.096MHz to 51.2MHz |1024 | 512 | 256 | 19448dfd37aSShengjiu Wang */ 1950c516b4fSNicolin Chen static const struct cs42xx8_ratios cs42xx8_ratios[] = { 19648dfd37aSShengjiu Wang { 0, 1029000, 12800000, {256, 128, 64} }, 19748dfd37aSShengjiu Wang { 2, 1536000, 19200000, {384, 192, 96} }, 19848dfd37aSShengjiu Wang { 4, 2048000, 25600000, {512, 256, 128} }, 19948dfd37aSShengjiu Wang { 6, 3072000, 38400000, {768, 384, 192} }, 20048dfd37aSShengjiu Wang { 8, 4096000, 51200000, {1024, 512, 256} }, 2010c516b4fSNicolin Chen }; 2020c516b4fSNicolin Chen 2030c516b4fSNicolin Chen static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai, 2040c516b4fSNicolin Chen int clk_id, unsigned int freq, int dir) 2050c516b4fSNicolin Chen { 20699a9f452SKuninori Morimoto struct snd_soc_component *component = codec_dai->component; 20799a9f452SKuninori Morimoto struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); 2080c516b4fSNicolin Chen 2090c516b4fSNicolin Chen cs42xx8->sysclk = freq; 2100c516b4fSNicolin Chen 2110c516b4fSNicolin Chen return 0; 2120c516b4fSNicolin Chen } 2130c516b4fSNicolin Chen 2140c516b4fSNicolin Chen static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai, 2150c516b4fSNicolin Chen unsigned int format) 2160c516b4fSNicolin Chen { 21799a9f452SKuninori Morimoto struct snd_soc_component *component = codec_dai->component; 21899a9f452SKuninori Morimoto struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); 2190c516b4fSNicolin Chen u32 val; 2200c516b4fSNicolin Chen 2210c516b4fSNicolin Chen /* Set DAI format */ 2220c516b4fSNicolin Chen switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { 2230c516b4fSNicolin Chen case SND_SOC_DAIFMT_LEFT_J: 2240c516b4fSNicolin Chen val = CS42XX8_INTF_DAC_DIF_LEFTJ | CS42XX8_INTF_ADC_DIF_LEFTJ; 2250c516b4fSNicolin Chen break; 2260c516b4fSNicolin Chen case SND_SOC_DAIFMT_I2S: 2270c516b4fSNicolin Chen val = CS42XX8_INTF_DAC_DIF_I2S | CS42XX8_INTF_ADC_DIF_I2S; 2280c516b4fSNicolin Chen break; 2290c516b4fSNicolin Chen case SND_SOC_DAIFMT_RIGHT_J: 2300c516b4fSNicolin Chen val = CS42XX8_INTF_DAC_DIF_RIGHTJ | CS42XX8_INTF_ADC_DIF_RIGHTJ; 2310c516b4fSNicolin Chen break; 232689dc643SShengjiu Wang case SND_SOC_DAIFMT_DSP_A: 233689dc643SShengjiu Wang val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM; 234689dc643SShengjiu Wang break; 2350c516b4fSNicolin Chen default: 23699a9f452SKuninori Morimoto dev_err(component->dev, "unsupported dai format\n"); 2370c516b4fSNicolin Chen return -EINVAL; 2380c516b4fSNicolin Chen } 2390c516b4fSNicolin Chen 2400c516b4fSNicolin Chen regmap_update_bits(cs42xx8->regmap, CS42XX8_INTF, 2410c516b4fSNicolin Chen CS42XX8_INTF_DAC_DIF_MASK | 2420c516b4fSNicolin Chen CS42XX8_INTF_ADC_DIF_MASK, val); 2430c516b4fSNicolin Chen 2440c516b4fSNicolin Chen /* Set master/slave audio interface */ 2450c516b4fSNicolin Chen switch (format & SND_SOC_DAIFMT_MASTER_MASK) { 2460c516b4fSNicolin Chen case SND_SOC_DAIFMT_CBS_CFS: 2470c516b4fSNicolin Chen cs42xx8->slave_mode = true; 2480c516b4fSNicolin Chen break; 2490c516b4fSNicolin Chen case SND_SOC_DAIFMT_CBM_CFM: 2500c516b4fSNicolin Chen cs42xx8->slave_mode = false; 2510c516b4fSNicolin Chen break; 2520c516b4fSNicolin Chen default: 25399a9f452SKuninori Morimoto dev_err(component->dev, "unsupported master/slave mode\n"); 2540c516b4fSNicolin Chen return -EINVAL; 2550c516b4fSNicolin Chen } 2560c516b4fSNicolin Chen 2570c516b4fSNicolin Chen return 0; 2580c516b4fSNicolin Chen } 2590c516b4fSNicolin Chen 2600c516b4fSNicolin Chen static int cs42xx8_hw_params(struct snd_pcm_substream *substream, 2610c516b4fSNicolin Chen struct snd_pcm_hw_params *params, 2620c516b4fSNicolin Chen struct snd_soc_dai *dai) 2630c516b4fSNicolin Chen { 26499a9f452SKuninori Morimoto struct snd_soc_component *component = dai->component; 26599a9f452SKuninori Morimoto struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); 2660c516b4fSNicolin Chen bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 26748dfd37aSShengjiu Wang u32 ratio[2]; 26848dfd37aSShengjiu Wang u32 rate[2]; 26948dfd37aSShengjiu Wang u32 fm[2]; 27048dfd37aSShengjiu Wang u32 i, val, mask; 27148dfd37aSShengjiu Wang bool condition1, condition2; 2720c516b4fSNicolin Chen 2731f1e60c9SZidan Wang if (tx) 2741f1e60c9SZidan Wang cs42xx8->tx_channels = params_channels(params); 2751f1e60c9SZidan Wang 27648dfd37aSShengjiu Wang rate[tx] = params_rate(params); 27748dfd37aSShengjiu Wang rate[!tx] = cs42xx8->rate[!tx]; 27848dfd37aSShengjiu Wang 27948dfd37aSShengjiu Wang ratio[tx] = rate[tx] > 0 ? cs42xx8->sysclk / rate[tx] : 0; 28048dfd37aSShengjiu Wang ratio[!tx] = rate[!tx] > 0 ? cs42xx8->sysclk / rate[!tx] : 0; 28148dfd37aSShengjiu Wang 28248dfd37aSShengjiu Wang /* Get functional mode for tx and rx according to rate */ 28348dfd37aSShengjiu Wang for (i = 0; i < 2; i++) { 28448dfd37aSShengjiu Wang if (cs42xx8->slave_mode) { 28548dfd37aSShengjiu Wang fm[i] = CS42XX8_FM_AUTO; 28648dfd37aSShengjiu Wang } else { 28748dfd37aSShengjiu Wang if (rate[i] < 50000) { 28848dfd37aSShengjiu Wang fm[i] = CS42XX8_FM_SINGLE; 28948dfd37aSShengjiu Wang } else if (rate[i] > 50000 && rate[i] < 100000) { 29048dfd37aSShengjiu Wang fm[i] = CS42XX8_FM_DOUBLE; 29148dfd37aSShengjiu Wang } else if (rate[i] > 100000 && rate[i] < 200000) { 29248dfd37aSShengjiu Wang fm[i] = CS42XX8_FM_QUAD; 29348dfd37aSShengjiu Wang } else { 29448dfd37aSShengjiu Wang dev_err(component->dev, 29548dfd37aSShengjiu Wang "unsupported sample rate\n"); 29648dfd37aSShengjiu Wang return -EINVAL; 29748dfd37aSShengjiu Wang } 29848dfd37aSShengjiu Wang } 29948dfd37aSShengjiu Wang } 30048dfd37aSShengjiu Wang 3010c516b4fSNicolin Chen for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { 30248dfd37aSShengjiu Wang /* Is the ratio[tx] valid ? */ 30348dfd37aSShengjiu Wang condition1 = ((fm[tx] == CS42XX8_FM_AUTO) ? 30448dfd37aSShengjiu Wang (cs42xx8_ratios[i].ratio[0] == ratio[tx] || 30548dfd37aSShengjiu Wang cs42xx8_ratios[i].ratio[1] == ratio[tx] || 30648dfd37aSShengjiu Wang cs42xx8_ratios[i].ratio[2] == ratio[tx]) : 30748dfd37aSShengjiu Wang (cs42xx8_ratios[i].ratio[fm[tx]] == ratio[tx])) && 30848dfd37aSShengjiu Wang cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk && 30948dfd37aSShengjiu Wang cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk; 31048dfd37aSShengjiu Wang 31148dfd37aSShengjiu Wang if (!ratio[tx]) 31248dfd37aSShengjiu Wang condition1 = true; 31348dfd37aSShengjiu Wang 31448dfd37aSShengjiu Wang /* Is the ratio[!tx] valid ? */ 31548dfd37aSShengjiu Wang condition2 = ((fm[!tx] == CS42XX8_FM_AUTO) ? 31648dfd37aSShengjiu Wang (cs42xx8_ratios[i].ratio[0] == ratio[!tx] || 31748dfd37aSShengjiu Wang cs42xx8_ratios[i].ratio[1] == ratio[!tx] || 31848dfd37aSShengjiu Wang cs42xx8_ratios[i].ratio[2] == ratio[!tx]) : 31948dfd37aSShengjiu Wang (cs42xx8_ratios[i].ratio[fm[!tx]] == ratio[!tx])); 32048dfd37aSShengjiu Wang 32148dfd37aSShengjiu Wang if (!ratio[!tx]) 32248dfd37aSShengjiu Wang condition2 = true; 32348dfd37aSShengjiu Wang 32448dfd37aSShengjiu Wang /* 32548dfd37aSShengjiu Wang * Both ratio[tx] and ratio[!tx] is valid, then we get 32648dfd37aSShengjiu Wang * a proper MFreq. 32748dfd37aSShengjiu Wang */ 32848dfd37aSShengjiu Wang if (condition1 && condition2) 3290c516b4fSNicolin Chen break; 3300c516b4fSNicolin Chen } 3310c516b4fSNicolin Chen 3320c516b4fSNicolin Chen if (i == ARRAY_SIZE(cs42xx8_ratios)) { 33399a9f452SKuninori Morimoto dev_err(component->dev, "unsupported sysclk ratio\n"); 3340c516b4fSNicolin Chen return -EINVAL; 3350c516b4fSNicolin Chen } 3360c516b4fSNicolin Chen 33748dfd37aSShengjiu Wang cs42xx8->rate[tx] = params_rate(params); 3380c516b4fSNicolin Chen 33948dfd37aSShengjiu Wang mask = CS42XX8_FUNCMOD_MFREQ_MASK; 34048dfd37aSShengjiu Wang val = cs42xx8_ratios[i].mfreq; 3410c516b4fSNicolin Chen 3420c516b4fSNicolin Chen regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD, 3430c516b4fSNicolin Chen CS42XX8_FUNCMOD_xC_FM_MASK(tx) | mask, 34448dfd37aSShengjiu Wang CS42XX8_FUNCMOD_xC_FM(tx, fm[tx]) | val); 3450c516b4fSNicolin Chen 3460c516b4fSNicolin Chen return 0; 3470c516b4fSNicolin Chen } 3480c516b4fSNicolin Chen 34948dfd37aSShengjiu Wang static int cs42xx8_hw_free(struct snd_pcm_substream *substream, 35048dfd37aSShengjiu Wang struct snd_soc_dai *dai) 35148dfd37aSShengjiu Wang { 35248dfd37aSShengjiu Wang struct snd_soc_component *component = dai->component; 35348dfd37aSShengjiu Wang struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); 35448dfd37aSShengjiu Wang bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 35548dfd37aSShengjiu Wang 35648dfd37aSShengjiu Wang /* Clear stored rate */ 35748dfd37aSShengjiu Wang cs42xx8->rate[tx] = 0; 35848dfd37aSShengjiu Wang 35948dfd37aSShengjiu Wang regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD, 36048dfd37aSShengjiu Wang CS42XX8_FUNCMOD_xC_FM_MASK(tx), 36148dfd37aSShengjiu Wang CS42XX8_FUNCMOD_xC_FM(tx, CS42XX8_FM_AUTO)); 36248dfd37aSShengjiu Wang return 0; 36348dfd37aSShengjiu Wang } 36448dfd37aSShengjiu Wang 3650c516b4fSNicolin Chen static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute) 3660c516b4fSNicolin Chen { 36799a9f452SKuninori Morimoto struct snd_soc_component *component = dai->component; 36899a9f452SKuninori Morimoto struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); 3691f1e60c9SZidan Wang u8 dac_unmute = cs42xx8->tx_channels ? 3701f1e60c9SZidan Wang ~((0x1 << cs42xx8->tx_channels) - 1) : 0; 3710c516b4fSNicolin Chen 3721f1e60c9SZidan Wang regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, 3731f1e60c9SZidan Wang mute ? CS42XX8_DACMUTE_ALL : dac_unmute); 3740c516b4fSNicolin Chen 3750c516b4fSNicolin Chen return 0; 3760c516b4fSNicolin Chen } 3770c516b4fSNicolin Chen 3780c516b4fSNicolin Chen static const struct snd_soc_dai_ops cs42xx8_dai_ops = { 3790c516b4fSNicolin Chen .set_fmt = cs42xx8_set_dai_fmt, 3800c516b4fSNicolin Chen .set_sysclk = cs42xx8_set_dai_sysclk, 3810c516b4fSNicolin Chen .hw_params = cs42xx8_hw_params, 38248dfd37aSShengjiu Wang .hw_free = cs42xx8_hw_free, 3830c516b4fSNicolin Chen .digital_mute = cs42xx8_digital_mute, 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, 49999a9f452SKuninori Morimoto .non_legacy_dai_naming = 1, 5000c516b4fSNicolin Chen }; 5010c516b4fSNicolin Chen 5020c516b4fSNicolin Chen const struct cs42xx8_driver_data cs42448_data = { 5030c516b4fSNicolin Chen .name = "cs42448", 5040c516b4fSNicolin Chen .num_adcs = 3, 5050c516b4fSNicolin Chen }; 5060c516b4fSNicolin Chen EXPORT_SYMBOL_GPL(cs42448_data); 5070c516b4fSNicolin Chen 5080c516b4fSNicolin Chen const struct cs42xx8_driver_data cs42888_data = { 5090c516b4fSNicolin Chen .name = "cs42888", 5100c516b4fSNicolin Chen .num_adcs = 2, 5110c516b4fSNicolin Chen }; 5120c516b4fSNicolin Chen EXPORT_SYMBOL_GPL(cs42888_data); 5130c516b4fSNicolin Chen 5145e4cb7b6SAxel Lin const struct of_device_id cs42xx8_of_match[] = { 5150c516b4fSNicolin Chen { .compatible = "cirrus,cs42448", .data = &cs42448_data, }, 5160c516b4fSNicolin Chen { .compatible = "cirrus,cs42888", .data = &cs42888_data, }, 5170c516b4fSNicolin Chen { /* sentinel */ } 5180c516b4fSNicolin Chen }; 5190c516b4fSNicolin Chen MODULE_DEVICE_TABLE(of, cs42xx8_of_match); 5200c516b4fSNicolin Chen EXPORT_SYMBOL_GPL(cs42xx8_of_match); 5210c516b4fSNicolin Chen 5220c516b4fSNicolin Chen int cs42xx8_probe(struct device *dev, struct regmap *regmap) 5230c516b4fSNicolin Chen { 524d375d0abSAxel Lin const struct of_device_id *of_id; 5250c516b4fSNicolin Chen struct cs42xx8_priv *cs42xx8; 5260c516b4fSNicolin Chen int ret, val, i; 5270c516b4fSNicolin Chen 528d375d0abSAxel Lin if (IS_ERR(regmap)) { 529d375d0abSAxel Lin ret = PTR_ERR(regmap); 530d375d0abSAxel Lin dev_err(dev, "failed to allocate regmap: %d\n", ret); 531d375d0abSAxel Lin return ret; 532d375d0abSAxel Lin } 533d375d0abSAxel Lin 5340c516b4fSNicolin Chen cs42xx8 = devm_kzalloc(dev, sizeof(*cs42xx8), GFP_KERNEL); 5350c516b4fSNicolin Chen if (cs42xx8 == NULL) 5360c516b4fSNicolin Chen return -ENOMEM; 5370c516b4fSNicolin Chen 538d375d0abSAxel Lin cs42xx8->regmap = regmap; 5390c516b4fSNicolin Chen dev_set_drvdata(dev, cs42xx8); 5400c516b4fSNicolin Chen 541d375d0abSAxel Lin of_id = of_match_device(cs42xx8_of_match, dev); 5420c516b4fSNicolin Chen if (of_id) 5430c516b4fSNicolin Chen cs42xx8->drvdata = of_id->data; 5440c516b4fSNicolin Chen 5450c516b4fSNicolin Chen if (!cs42xx8->drvdata) { 5460c516b4fSNicolin Chen dev_err(dev, "failed to find driver data\n"); 5470c516b4fSNicolin Chen return -EINVAL; 5480c516b4fSNicolin Chen } 5490c516b4fSNicolin Chen 550bfe95dfaSS.j. Wang cs42xx8->gpiod_reset = devm_gpiod_get_optional(dev, "reset", 551bfe95dfaSS.j. Wang GPIOD_OUT_HIGH); 552bfe95dfaSS.j. Wang if (IS_ERR(cs42xx8->gpiod_reset)) 553bfe95dfaSS.j. Wang return PTR_ERR(cs42xx8->gpiod_reset); 554bfe95dfaSS.j. Wang 555bfe95dfaSS.j. Wang gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 0); 556bfe95dfaSS.j. Wang 5570c516b4fSNicolin Chen cs42xx8->clk = devm_clk_get(dev, "mclk"); 5580c516b4fSNicolin Chen if (IS_ERR(cs42xx8->clk)) { 5590c516b4fSNicolin Chen dev_err(dev, "failed to get the clock: %ld\n", 5600c516b4fSNicolin Chen PTR_ERR(cs42xx8->clk)); 5610c516b4fSNicolin Chen return -EINVAL; 5620c516b4fSNicolin Chen } 5630c516b4fSNicolin Chen 5640c516b4fSNicolin Chen cs42xx8->sysclk = clk_get_rate(cs42xx8->clk); 5650c516b4fSNicolin Chen 5660c516b4fSNicolin Chen for (i = 0; i < ARRAY_SIZE(cs42xx8->supplies); i++) 5670c516b4fSNicolin Chen cs42xx8->supplies[i].supply = cs42xx8_supply_names[i]; 5680c516b4fSNicolin Chen 5690c516b4fSNicolin Chen ret = devm_regulator_bulk_get(dev, 5700c516b4fSNicolin Chen ARRAY_SIZE(cs42xx8->supplies), cs42xx8->supplies); 5710c516b4fSNicolin Chen if (ret) { 5720c516b4fSNicolin Chen dev_err(dev, "failed to request supplies: %d\n", ret); 5730c516b4fSNicolin Chen return ret; 5740c516b4fSNicolin Chen } 5750c516b4fSNicolin Chen 5760c516b4fSNicolin Chen ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies), 5770c516b4fSNicolin Chen cs42xx8->supplies); 5780c516b4fSNicolin Chen if (ret) { 5790c516b4fSNicolin Chen dev_err(dev, "failed to enable supplies: %d\n", ret); 5800c516b4fSNicolin Chen return ret; 5810c516b4fSNicolin Chen } 5820c516b4fSNicolin Chen 5830c516b4fSNicolin Chen /* Make sure hardware reset done */ 5840c516b4fSNicolin Chen msleep(5); 5850c516b4fSNicolin Chen 5860c516b4fSNicolin Chen /* Validate the chip ID */ 58706b4b813SAxel Lin ret = regmap_read(cs42xx8->regmap, CS42XX8_CHIPID, &val); 58806b4b813SAxel Lin if (ret < 0) { 58906b4b813SAxel Lin dev_err(dev, "failed to get device ID, ret = %d", ret); 5900c516b4fSNicolin Chen goto err_enable; 5910c516b4fSNicolin Chen } 5920c516b4fSNicolin Chen 5930c516b4fSNicolin Chen /* The top four bits of the chip ID should be 0000 */ 59406b4b813SAxel Lin if (((val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4) != 0x00) { 5950c516b4fSNicolin Chen dev_err(dev, "unmatched chip ID: %d\n", 59606b4b813SAxel Lin (val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4); 5970c516b4fSNicolin Chen ret = -EINVAL; 5980c516b4fSNicolin Chen goto err_enable; 5990c516b4fSNicolin Chen } 6000c516b4fSNicolin Chen 6010c516b4fSNicolin Chen dev_info(dev, "found device, revision %X\n", 6020c516b4fSNicolin Chen val & CS42XX8_CHIPID_REV_ID_MASK); 6030c516b4fSNicolin Chen 6040c516b4fSNicolin Chen cs42xx8_dai.name = cs42xx8->drvdata->name; 6050c516b4fSNicolin Chen 6060c516b4fSNicolin Chen /* Each adc supports stereo input */ 6070c516b4fSNicolin Chen cs42xx8_dai.capture.channels_max = cs42xx8->drvdata->num_adcs * 2; 6080c516b4fSNicolin Chen 60999a9f452SKuninori Morimoto ret = devm_snd_soc_register_component(dev, &cs42xx8_driver, &cs42xx8_dai, 1); 6100c516b4fSNicolin Chen if (ret) { 61199a9f452SKuninori Morimoto dev_err(dev, "failed to register component:%d\n", ret); 6120c516b4fSNicolin Chen goto err_enable; 6130c516b4fSNicolin Chen } 6140c516b4fSNicolin Chen 6150c516b4fSNicolin Chen regcache_cache_only(cs42xx8->regmap, true); 6160c516b4fSNicolin Chen 6170c516b4fSNicolin Chen err_enable: 6180c516b4fSNicolin Chen regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies), 6190c516b4fSNicolin Chen cs42xx8->supplies); 6200c516b4fSNicolin Chen 6210c516b4fSNicolin Chen return ret; 6220c516b4fSNicolin Chen } 6230c516b4fSNicolin Chen EXPORT_SYMBOL_GPL(cs42xx8_probe); 6240c516b4fSNicolin Chen 625641d334bSRafael J. Wysocki #ifdef CONFIG_PM 6260c516b4fSNicolin Chen static int cs42xx8_runtime_resume(struct device *dev) 6270c516b4fSNicolin Chen { 6280c516b4fSNicolin Chen struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev); 6290c516b4fSNicolin Chen int ret; 6300c516b4fSNicolin Chen 6310c516b4fSNicolin Chen ret = clk_prepare_enable(cs42xx8->clk); 6320c516b4fSNicolin Chen if (ret) { 6330c516b4fSNicolin Chen dev_err(dev, "failed to enable mclk: %d\n", ret); 6340c516b4fSNicolin Chen return ret; 6350c516b4fSNicolin Chen } 6360c516b4fSNicolin Chen 637bfe95dfaSS.j. Wang gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 0); 638bfe95dfaSS.j. Wang 6390c516b4fSNicolin Chen ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies), 6400c516b4fSNicolin Chen cs42xx8->supplies); 6410c516b4fSNicolin Chen if (ret) { 6420c516b4fSNicolin Chen dev_err(dev, "failed to enable supplies: %d\n", ret); 6430c516b4fSNicolin Chen goto err_clk; 6440c516b4fSNicolin Chen } 6450c516b4fSNicolin Chen 6460c516b4fSNicolin Chen /* Make sure hardware reset done */ 6470c516b4fSNicolin Chen msleep(5); 6480c516b4fSNicolin Chen 6490c516b4fSNicolin Chen regcache_cache_only(cs42xx8->regmap, false); 650ad6eecbfSS.j. Wang regcache_mark_dirty(cs42xx8->regmap); 6510c516b4fSNicolin Chen 6520c516b4fSNicolin Chen ret = regcache_sync(cs42xx8->regmap); 6530c516b4fSNicolin Chen if (ret) { 6540c516b4fSNicolin Chen dev_err(dev, "failed to sync regmap: %d\n", ret); 6550c516b4fSNicolin Chen goto err_bulk; 6560c516b4fSNicolin Chen } 6570c516b4fSNicolin Chen 6580c516b4fSNicolin Chen return 0; 6590c516b4fSNicolin Chen 6600c516b4fSNicolin Chen err_bulk: 6610c516b4fSNicolin Chen regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies), 6620c516b4fSNicolin Chen cs42xx8->supplies); 6630c516b4fSNicolin Chen err_clk: 6640c516b4fSNicolin Chen clk_disable_unprepare(cs42xx8->clk); 6650c516b4fSNicolin Chen 6660c516b4fSNicolin Chen return ret; 6670c516b4fSNicolin Chen } 6680c516b4fSNicolin Chen 6690c516b4fSNicolin Chen static int cs42xx8_runtime_suspend(struct device *dev) 6700c516b4fSNicolin Chen { 6710c516b4fSNicolin Chen struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev); 6720c516b4fSNicolin Chen 6730c516b4fSNicolin Chen regcache_cache_only(cs42xx8->regmap, true); 6740c516b4fSNicolin Chen 6750c516b4fSNicolin Chen regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies), 6760c516b4fSNicolin Chen cs42xx8->supplies); 6770c516b4fSNicolin Chen 678bfe95dfaSS.j. Wang gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 1); 679bfe95dfaSS.j. Wang 6800c516b4fSNicolin Chen clk_disable_unprepare(cs42xx8->clk); 6810c516b4fSNicolin Chen 6820c516b4fSNicolin Chen return 0; 6830c516b4fSNicolin Chen } 6840c516b4fSNicolin Chen #endif 6850c516b4fSNicolin Chen 6860c516b4fSNicolin Chen const struct dev_pm_ops cs42xx8_pm = { 687*b429ca49SShengjiu Wang SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 688*b429ca49SShengjiu Wang pm_runtime_force_resume) 6890c516b4fSNicolin Chen SET_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL) 6900c516b4fSNicolin Chen }; 6910c516b4fSNicolin Chen EXPORT_SYMBOL_GPL(cs42xx8_pm); 6920c516b4fSNicolin Chen 6930c516b4fSNicolin Chen MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec Driver"); 6940c516b4fSNicolin Chen MODULE_AUTHOR("Freescale Semiconductor, Inc."); 6950c516b4fSNicolin Chen MODULE_LICENSE("GPL"); 696