16387f866SBrian Austin /* 26387f866SBrian Austin * cs35l35.c -- CS35L35 ALSA SoC audio driver 36387f866SBrian Austin * 46387f866SBrian Austin * Copyright 2017 Cirrus Logic, Inc. 56387f866SBrian Austin * 66387f866SBrian Austin * Author: Brian Austin <brian.austin@cirrus.com> 76387f866SBrian Austin * 86387f866SBrian Austin * This program is free software; you can redistribute it and/or modify 96387f866SBrian Austin * it under the terms of the GNU General Public License version 2 as 106387f866SBrian Austin * published by the Free Software Foundation. 116387f866SBrian Austin * 126387f866SBrian Austin */ 136387f866SBrian Austin 146387f866SBrian Austin #include <linux/module.h> 156387f866SBrian Austin #include <linux/moduleparam.h> 166387f866SBrian Austin #include <linux/version.h> 176387f866SBrian Austin #include <linux/kernel.h> 186387f866SBrian Austin #include <linux/init.h> 196387f866SBrian Austin #include <linux/delay.h> 206387f866SBrian Austin #include <linux/i2c.h> 216387f866SBrian Austin #include <linux/slab.h> 226387f866SBrian Austin #include <linux/platform_device.h> 236387f866SBrian Austin #include <linux/regulator/consumer.h> 246387f866SBrian Austin #include <linux/gpio/consumer.h> 256387f866SBrian Austin #include <linux/of_device.h> 266387f866SBrian Austin #include <linux/of_gpio.h> 276387f866SBrian Austin #include <linux/regmap.h> 286387f866SBrian Austin #include <sound/core.h> 296387f866SBrian Austin #include <sound/pcm.h> 306387f866SBrian Austin #include <sound/pcm_params.h> 316387f866SBrian Austin #include <sound/soc.h> 326387f866SBrian Austin #include <sound/soc-dapm.h> 336387f866SBrian Austin #include <linux/gpio.h> 346387f866SBrian Austin #include <sound/initval.h> 356387f866SBrian Austin #include <sound/tlv.h> 366387f866SBrian Austin #include <sound/cs35l35.h> 376387f866SBrian Austin #include <linux/of_irq.h> 386387f866SBrian Austin #include <linux/completion.h> 396387f866SBrian Austin 406387f866SBrian Austin #include "cs35l35.h" 416387f866SBrian Austin 426387f866SBrian Austin /* 436387f866SBrian Austin * Some fields take zero as a valid value so use a high bit flag that won't 446387f866SBrian Austin * get written to the device to mark those. 456387f866SBrian Austin */ 466387f866SBrian Austin #define CS35L35_VALID_PDATA 0x80000000 476387f866SBrian Austin 486387f866SBrian Austin static const struct reg_default cs35l35_reg[] = { 496387f866SBrian Austin {CS35L35_PWRCTL1, 0x01}, 506387f866SBrian Austin {CS35L35_PWRCTL2, 0x11}, 516387f866SBrian Austin {CS35L35_PWRCTL3, 0x00}, 526387f866SBrian Austin {CS35L35_CLK_CTL1, 0x04}, 536387f866SBrian Austin {CS35L35_CLK_CTL2, 0x10}, 546387f866SBrian Austin {CS35L35_CLK_CTL3, 0xCF}, 556387f866SBrian Austin {CS35L35_SP_FMT_CTL1, 0x20}, 566387f866SBrian Austin {CS35L35_SP_FMT_CTL2, 0x00}, 576387f866SBrian Austin {CS35L35_SP_FMT_CTL3, 0x02}, 586387f866SBrian Austin {CS35L35_MAG_COMP_CTL, 0x00}, 596387f866SBrian Austin {CS35L35_AMP_INP_DRV_CTL, 0x01}, 606387f866SBrian Austin {CS35L35_AMP_DIG_VOL_CTL, 0x12}, 616387f866SBrian Austin {CS35L35_AMP_DIG_VOL, 0x00}, 626387f866SBrian Austin {CS35L35_ADV_DIG_VOL, 0x00}, 636387f866SBrian Austin {CS35L35_PROTECT_CTL, 0x06}, 646387f866SBrian Austin {CS35L35_AMP_GAIN_AUD_CTL, 0x13}, 656387f866SBrian Austin {CS35L35_AMP_GAIN_PDM_CTL, 0x00}, 666387f866SBrian Austin {CS35L35_AMP_GAIN_ADV_CTL, 0x00}, 676387f866SBrian Austin {CS35L35_GPI_CTL, 0x00}, 686387f866SBrian Austin {CS35L35_BST_CVTR_V_CTL, 0x00}, 696387f866SBrian Austin {CS35L35_BST_PEAK_I, 0x07}, 706387f866SBrian Austin {CS35L35_BST_RAMP_CTL, 0x85}, 716387f866SBrian Austin {CS35L35_BST_CONV_COEF_1, 0x24}, 726387f866SBrian Austin {CS35L35_BST_CONV_COEF_2, 0x24}, 736387f866SBrian Austin {CS35L35_BST_CONV_SLOPE_COMP, 0x47}, 746387f866SBrian Austin {CS35L35_BST_CONV_SW_FREQ, 0x04}, 756387f866SBrian Austin {CS35L35_CLASS_H_CTL, 0x0B}, 766387f866SBrian Austin {CS35L35_CLASS_H_HEADRM_CTL, 0x0B}, 776387f866SBrian Austin {CS35L35_CLASS_H_RELEASE_RATE, 0x08}, 786387f866SBrian Austin {CS35L35_CLASS_H_FET_DRIVE_CTL, 0x41}, 796387f866SBrian Austin {CS35L35_CLASS_H_VP_CTL, 0xC5}, 806387f866SBrian Austin {CS35L35_VPBR_CTL, 0x0A}, 816387f866SBrian Austin {CS35L35_VPBR_VOL_CTL, 0x09}, 826387f866SBrian Austin {CS35L35_VPBR_TIMING_CTL, 0x6A}, 836387f866SBrian Austin {CS35L35_VPBR_MODE_VOL_CTL, 0x40}, 846387f866SBrian Austin {CS35L35_SPKR_MON_CTL, 0xC0}, 856387f866SBrian Austin {CS35L35_IMON_SCALE_CTL, 0x30}, 866387f866SBrian Austin {CS35L35_AUDIN_RXLOC_CTL, 0x00}, 876387f866SBrian Austin {CS35L35_ADVIN_RXLOC_CTL, 0x80}, 886387f866SBrian Austin {CS35L35_VMON_TXLOC_CTL, 0x00}, 896387f866SBrian Austin {CS35L35_IMON_TXLOC_CTL, 0x80}, 906387f866SBrian Austin {CS35L35_VPMON_TXLOC_CTL, 0x04}, 916387f866SBrian Austin {CS35L35_VBSTMON_TXLOC_CTL, 0x84}, 926387f866SBrian Austin {CS35L35_VPBR_STATUS_TXLOC_CTL, 0x04}, 936387f866SBrian Austin {CS35L35_ZERO_FILL_LOC_CTL, 0x00}, 946387f866SBrian Austin {CS35L35_AUDIN_DEPTH_CTL, 0x0F}, 956387f866SBrian Austin {CS35L35_SPKMON_DEPTH_CTL, 0x0F}, 966387f866SBrian Austin {CS35L35_SUPMON_DEPTH_CTL, 0x0F}, 976387f866SBrian Austin {CS35L35_ZEROFILL_DEPTH_CTL, 0x00}, 986387f866SBrian Austin {CS35L35_MULT_DEV_SYNCH1, 0x02}, 996387f866SBrian Austin {CS35L35_MULT_DEV_SYNCH2, 0x80}, 1006387f866SBrian Austin {CS35L35_PROT_RELEASE_CTL, 0x00}, 1016387f866SBrian Austin {CS35L35_DIAG_MODE_REG_LOCK, 0x00}, 1026387f866SBrian Austin {CS35L35_DIAG_MODE_CTL_1, 0x40}, 1036387f866SBrian Austin {CS35L35_DIAG_MODE_CTL_2, 0x00}, 1046387f866SBrian Austin {CS35L35_INT_MASK_1, 0xFF}, 1056387f866SBrian Austin {CS35L35_INT_MASK_2, 0xFF}, 1066387f866SBrian Austin {CS35L35_INT_MASK_3, 0xFF}, 1076387f866SBrian Austin {CS35L35_INT_MASK_4, 0xFF}, 1086387f866SBrian Austin 1096387f866SBrian Austin }; 1106387f866SBrian Austin 1116387f866SBrian Austin static bool cs35l35_volatile_register(struct device *dev, unsigned int reg) 1126387f866SBrian Austin { 1136387f866SBrian Austin switch (reg) { 1146387f866SBrian Austin case CS35L35_INT_STATUS_1: 1156387f866SBrian Austin case CS35L35_INT_STATUS_2: 1166387f866SBrian Austin case CS35L35_INT_STATUS_3: 1176387f866SBrian Austin case CS35L35_INT_STATUS_4: 1186387f866SBrian Austin case CS35L35_PLL_STATUS: 1196387f866SBrian Austin case CS35L35_OTP_TRIM_STATUS: 1206387f866SBrian Austin return true; 1216387f866SBrian Austin default: 1226387f866SBrian Austin return false; 1236387f866SBrian Austin } 1246387f866SBrian Austin } 1256387f866SBrian Austin 1266387f866SBrian Austin static bool cs35l35_readable_register(struct device *dev, unsigned int reg) 1276387f866SBrian Austin { 1286387f866SBrian Austin switch (reg) { 1296387f866SBrian Austin case CS35L35_DEVID_AB ... CS35L35_PWRCTL3: 1306387f866SBrian Austin case CS35L35_CLK_CTL1 ... CS35L35_SP_FMT_CTL3: 1316387f866SBrian Austin case CS35L35_MAG_COMP_CTL ... CS35L35_AMP_GAIN_AUD_CTL: 1326387f866SBrian Austin case CS35L35_AMP_GAIN_PDM_CTL ... CS35L35_BST_PEAK_I: 1336387f866SBrian Austin case CS35L35_BST_RAMP_CTL ... CS35L35_BST_CONV_SW_FREQ: 1346387f866SBrian Austin case CS35L35_CLASS_H_CTL ... CS35L35_CLASS_H_VP_CTL: 1356387f866SBrian Austin case CS35L35_CLASS_H_STATUS: 1366387f866SBrian Austin case CS35L35_VPBR_CTL ... CS35L35_VPBR_MODE_VOL_CTL: 1376387f866SBrian Austin case CS35L35_VPBR_ATTEN_STATUS: 1386387f866SBrian Austin case CS35L35_SPKR_MON_CTL: 1396387f866SBrian Austin case CS35L35_IMON_SCALE_CTL ... CS35L35_ZEROFILL_DEPTH_CTL: 1406387f866SBrian Austin case CS35L35_MULT_DEV_SYNCH1 ... CS35L35_PROT_RELEASE_CTL: 1416387f866SBrian Austin case CS35L35_DIAG_MODE_REG_LOCK ... CS35L35_DIAG_MODE_CTL_2: 1426387f866SBrian Austin case CS35L35_INT_MASK_1 ... CS35L35_PLL_STATUS: 1436387f866SBrian Austin case CS35L35_OTP_TRIM_STATUS: 1446387f866SBrian Austin return true; 1456387f866SBrian Austin default: 1466387f866SBrian Austin return false; 1476387f866SBrian Austin } 1486387f866SBrian Austin } 1496387f866SBrian Austin 1506387f866SBrian Austin static bool cs35l35_precious_register(struct device *dev, unsigned int reg) 1516387f866SBrian Austin { 1526387f866SBrian Austin switch (reg) { 1536387f866SBrian Austin case CS35L35_INT_STATUS_1: 1546387f866SBrian Austin case CS35L35_INT_STATUS_2: 1556387f866SBrian Austin case CS35L35_INT_STATUS_3: 1566387f866SBrian Austin case CS35L35_INT_STATUS_4: 1576387f866SBrian Austin case CS35L35_PLL_STATUS: 1586387f866SBrian Austin case CS35L35_OTP_TRIM_STATUS: 1596387f866SBrian Austin return true; 1606387f866SBrian Austin default: 1616387f866SBrian Austin return false; 1626387f866SBrian Austin } 1636387f866SBrian Austin } 1646387f866SBrian Austin 1656387f866SBrian Austin static int cs35l35_sdin_event(struct snd_soc_dapm_widget *w, 1666387f866SBrian Austin struct snd_kcontrol *kcontrol, int event) 1676387f866SBrian Austin { 1686387f866SBrian Austin struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 1696387f866SBrian Austin struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec); 1706387f866SBrian Austin int ret = 0; 1716387f866SBrian Austin 1726387f866SBrian Austin switch (event) { 1736387f866SBrian Austin case SND_SOC_DAPM_PRE_PMU: 1746387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1, 1756387f866SBrian Austin CS35L35_MCLK_DIS_MASK, 1766387f866SBrian Austin 0 << CS35L35_MCLK_DIS_SHIFT); 1776387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1, 1786387f866SBrian Austin CS35L35_DISCHG_FILT_MASK, 1796387f866SBrian Austin 0 << CS35L35_DISCHG_FILT_SHIFT); 1806387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1, 1816387f866SBrian Austin CS35L35_PDN_ALL_MASK, 0); 1826387f866SBrian Austin break; 1836387f866SBrian Austin case SND_SOC_DAPM_POST_PMD: 1846387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1, 1856387f866SBrian Austin CS35L35_DISCHG_FILT_MASK, 1866387f866SBrian Austin 1 << CS35L35_DISCHG_FILT_SHIFT); 1876387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1, 1886387f866SBrian Austin CS35L35_PDN_ALL_MASK, 1); 1896387f866SBrian Austin 1906387f866SBrian Austin reinit_completion(&cs35l35->pdn_done); 1916387f866SBrian Austin 1926387f866SBrian Austin ret = wait_for_completion_timeout(&cs35l35->pdn_done, 1936387f866SBrian Austin msecs_to_jiffies(100)); 1946387f866SBrian Austin if (ret == 0) { 1956387f866SBrian Austin dev_err(codec->dev, "TIMEOUT PDN_DONE did not complete in 100ms\n"); 1966387f866SBrian Austin ret = -ETIMEDOUT; 1976387f866SBrian Austin } 1986387f866SBrian Austin 1996387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1, 2006387f866SBrian Austin CS35L35_MCLK_DIS_MASK, 2016387f866SBrian Austin 1 << CS35L35_MCLK_DIS_SHIFT); 2026387f866SBrian Austin break; 2036387f866SBrian Austin default: 2046387f866SBrian Austin dev_err(codec->dev, "Invalid event = 0x%x\n", event); 2056387f866SBrian Austin ret = -EINVAL; 2066387f866SBrian Austin } 2076387f866SBrian Austin return ret; 2086387f866SBrian Austin } 2096387f866SBrian Austin 2106387f866SBrian Austin static int cs35l35_main_amp_event(struct snd_soc_dapm_widget *w, 2116387f866SBrian Austin struct snd_kcontrol *kcontrol, int event) 2126387f866SBrian Austin { 2136387f866SBrian Austin struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 2146387f866SBrian Austin struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec); 2156387f866SBrian Austin unsigned int reg[4]; 2166387f866SBrian Austin int i; 2176387f866SBrian Austin 2186387f866SBrian Austin switch (event) { 2196387f866SBrian Austin case SND_SOC_DAPM_PRE_PMU: 2206387f866SBrian Austin if (cs35l35->pdata.bst_pdn_fet_on) 2216387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, 2226387f866SBrian Austin CS35L35_PDN_BST_MASK, 2236387f866SBrian Austin 0 << CS35L35_PDN_BST_FETON_SHIFT); 2246387f866SBrian Austin else 2256387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, 2266387f866SBrian Austin CS35L35_PDN_BST_MASK, 2276387f866SBrian Austin 0 << CS35L35_PDN_BST_FETOFF_SHIFT); 2286387f866SBrian Austin break; 2296387f866SBrian Austin case SND_SOC_DAPM_POST_PMU: 2306387f866SBrian Austin usleep_range(5000, 5100); 2316387f866SBrian Austin /* If in PDM mode we must use VP for Voltage control */ 2326387f866SBrian Austin if (cs35l35->pdm_mode) 2336387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 2346387f866SBrian Austin CS35L35_BST_CVTR_V_CTL, 2356387f866SBrian Austin CS35L35_BST_CTL_MASK, 2366387f866SBrian Austin 0 << CS35L35_BST_CTL_SHIFT); 2376387f866SBrian Austin 2386387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL, 2396387f866SBrian Austin CS35L35_AMP_MUTE_MASK, 0); 2406387f866SBrian Austin 2416387f866SBrian Austin for (i = 0; i < 2; i++) 2426387f866SBrian Austin regmap_bulk_read(cs35l35->regmap, CS35L35_INT_STATUS_1, 2436387f866SBrian Austin ®, ARRAY_SIZE(reg)); 2446387f866SBrian Austin 2456387f866SBrian Austin break; 2466387f866SBrian Austin case SND_SOC_DAPM_PRE_PMD: 2476387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL, 2486387f866SBrian Austin CS35L35_AMP_MUTE_MASK, 2496387f866SBrian Austin 1 << CS35L35_AMP_MUTE_SHIFT); 2506387f866SBrian Austin if (cs35l35->pdata.bst_pdn_fet_on) 2516387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, 2526387f866SBrian Austin CS35L35_PDN_BST_MASK, 2536387f866SBrian Austin 1 << CS35L35_PDN_BST_FETON_SHIFT); 2546387f866SBrian Austin else 2556387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, 2566387f866SBrian Austin CS35L35_PDN_BST_MASK, 2576387f866SBrian Austin 1 << CS35L35_PDN_BST_FETOFF_SHIFT); 2586387f866SBrian Austin break; 2596387f866SBrian Austin case SND_SOC_DAPM_POST_PMD: 2606387f866SBrian Austin usleep_range(5000, 5100); 2616387f866SBrian Austin /* 2626387f866SBrian Austin * If PDM mode we should switch back to pdata value 2636387f866SBrian Austin * for Voltage control when we go down 2646387f866SBrian Austin */ 2656387f866SBrian Austin if (cs35l35->pdm_mode) 2666387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 2676387f866SBrian Austin CS35L35_BST_CVTR_V_CTL, 2686387f866SBrian Austin CS35L35_BST_CTL_MASK, 2696387f866SBrian Austin cs35l35->pdata.bst_vctl 2706387f866SBrian Austin << CS35L35_BST_CTL_SHIFT); 2716387f866SBrian Austin 2726387f866SBrian Austin break; 2736387f866SBrian Austin default: 2746387f866SBrian Austin dev_err(codec->dev, "Invalid event = 0x%x\n", event); 2756387f866SBrian Austin } 2766387f866SBrian Austin return 0; 2776387f866SBrian Austin } 2786387f866SBrian Austin 2796387f866SBrian Austin static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1); 2806387f866SBrian Austin static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0); 2816387f866SBrian Austin 2826387f866SBrian Austin static const struct snd_kcontrol_new cs35l35_aud_controls[] = { 2836387f866SBrian Austin SOC_SINGLE_SX_TLV("Digital Audio Volume", CS35L35_AMP_DIG_VOL, 2846387f866SBrian Austin 0, 0x34, 0xE4, dig_vol_tlv), 2856387f866SBrian Austin SOC_SINGLE_TLV("Analog Audio Volume", CS35L35_AMP_GAIN_AUD_CTL, 0, 19, 0, 2866387f866SBrian Austin amp_gain_tlv), 2876387f866SBrian Austin SOC_SINGLE_TLV("PDM Volume", CS35L35_AMP_GAIN_PDM_CTL, 0, 19, 0, 2886387f866SBrian Austin amp_gain_tlv), 2896387f866SBrian Austin }; 2906387f866SBrian Austin 2916387f866SBrian Austin static const struct snd_kcontrol_new cs35l35_adv_controls[] = { 2926387f866SBrian Austin SOC_SINGLE_SX_TLV("Digital Advisory Volume", CS35L35_ADV_DIG_VOL, 2936387f866SBrian Austin 0, 0x34, 0xE4, dig_vol_tlv), 2946387f866SBrian Austin SOC_SINGLE_TLV("Analog Advisory Volume", CS35L35_AMP_GAIN_ADV_CTL, 0, 19, 0, 2956387f866SBrian Austin amp_gain_tlv), 2966387f866SBrian Austin }; 2976387f866SBrian Austin 2986387f866SBrian Austin static const struct snd_soc_dapm_widget cs35l35_dapm_widgets[] = { 2996387f866SBrian Austin SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L35_PWRCTL3, 1, 1, 3006387f866SBrian Austin cs35l35_sdin_event, SND_SOC_DAPM_PRE_PMU | 3016387f866SBrian Austin SND_SOC_DAPM_POST_PMD), 3026387f866SBrian Austin SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L35_PWRCTL3, 2, 1), 3036387f866SBrian Austin 3046387f866SBrian Austin SND_SOC_DAPM_OUTPUT("SPK"), 3056387f866SBrian Austin 3066387f866SBrian Austin SND_SOC_DAPM_INPUT("VP"), 3076387f866SBrian Austin SND_SOC_DAPM_INPUT("VBST"), 3086387f866SBrian Austin SND_SOC_DAPM_INPUT("ISENSE"), 3096387f866SBrian Austin SND_SOC_DAPM_INPUT("VSENSE"), 3106387f866SBrian Austin 3116387f866SBrian Austin SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L35_PWRCTL2, 7, 1), 3126387f866SBrian Austin SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L35_PWRCTL2, 6, 1), 3136387f866SBrian Austin SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L35_PWRCTL3, 3, 1), 3146387f866SBrian Austin SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L35_PWRCTL3, 4, 1), 3156387f866SBrian Austin SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L35_PWRCTL2, 5, 1), 3166387f866SBrian Austin 3176387f866SBrian Austin SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L35_PWRCTL2, 0, 1, NULL, 0, 3186387f866SBrian Austin cs35l35_main_amp_event, SND_SOC_DAPM_PRE_PMU | 3196387f866SBrian Austin SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU | 3206387f866SBrian Austin SND_SOC_DAPM_PRE_PMD), 3216387f866SBrian Austin }; 3226387f866SBrian Austin 3236387f866SBrian Austin static const struct snd_soc_dapm_route cs35l35_audio_map[] = { 3246387f866SBrian Austin {"VPMON ADC", NULL, "VP"}, 3256387f866SBrian Austin {"VBSTMON ADC", NULL, "VBST"}, 3266387f866SBrian Austin {"IMON ADC", NULL, "ISENSE"}, 3276387f866SBrian Austin {"VMON ADC", NULL, "VSENSE"}, 3286387f866SBrian Austin {"SDOUT", NULL, "IMON ADC"}, 3296387f866SBrian Austin {"SDOUT", NULL, "VMON ADC"}, 3306387f866SBrian Austin {"SDOUT", NULL, "VBSTMON ADC"}, 3316387f866SBrian Austin {"SDOUT", NULL, "VPMON ADC"}, 3326387f866SBrian Austin {"AMP Capture", NULL, "SDOUT"}, 3336387f866SBrian Austin 3346387f866SBrian Austin {"SDIN", NULL, "AMP Playback"}, 3356387f866SBrian Austin {"CLASS H", NULL, "SDIN"}, 3366387f866SBrian Austin {"Main AMP", NULL, "CLASS H"}, 3376387f866SBrian Austin {"SPK", NULL, "Main AMP"}, 3386387f866SBrian Austin }; 3396387f866SBrian Austin 3406387f866SBrian Austin static int cs35l35_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) 3416387f866SBrian Austin { 3426387f866SBrian Austin struct snd_soc_codec *codec = codec_dai->codec; 3436387f866SBrian Austin struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec); 3446387f866SBrian Austin 3456387f866SBrian Austin switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 3466387f866SBrian Austin case SND_SOC_DAIFMT_CBM_CFM: 3476387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1, 3486387f866SBrian Austin CS35L35_MS_MASK, 1 << CS35L35_MS_SHIFT); 3496387f866SBrian Austin cs35l35->slave_mode = false; 3506387f866SBrian Austin break; 3516387f866SBrian Austin case SND_SOC_DAIFMT_CBS_CFS: 3526387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1, 3536387f866SBrian Austin CS35L35_MS_MASK, 0 << CS35L35_MS_SHIFT); 3546387f866SBrian Austin cs35l35->slave_mode = true; 3556387f866SBrian Austin break; 3566387f866SBrian Austin default: 3576387f866SBrian Austin return -EINVAL; 3586387f866SBrian Austin } 3596387f866SBrian Austin 3606387f866SBrian Austin switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 3616387f866SBrian Austin case SND_SOC_DAIFMT_I2S: 3626387f866SBrian Austin cs35l35->i2s_mode = true; 3636387f866SBrian Austin cs35l35->pdm_mode = false; 3646387f866SBrian Austin break; 3656387f866SBrian Austin case SND_SOC_DAIFMT_PDM: 3666387f866SBrian Austin cs35l35->pdm_mode = true; 3676387f866SBrian Austin cs35l35->i2s_mode = false; 3686387f866SBrian Austin break; 3696387f866SBrian Austin default: 3706387f866SBrian Austin return -EINVAL; 3716387f866SBrian Austin } 3726387f866SBrian Austin 3736387f866SBrian Austin return 0; 3746387f866SBrian Austin } 3756387f866SBrian Austin 3766387f866SBrian Austin struct cs35l35_sysclk_config { 3776387f866SBrian Austin int sysclk; 3786387f866SBrian Austin int srate; 3796387f866SBrian Austin u8 clk_cfg; 3806387f866SBrian Austin }; 3816387f866SBrian Austin 3826387f866SBrian Austin static struct cs35l35_sysclk_config cs35l35_clk_ctl[] = { 3836387f866SBrian Austin 3846387f866SBrian Austin /* SYSCLK, Sample Rate, Serial Port Cfg */ 3856387f866SBrian Austin {5644800, 44100, 0x00}, 3866387f866SBrian Austin {5644800, 88200, 0x40}, 3876387f866SBrian Austin {6144000, 48000, 0x10}, 3886387f866SBrian Austin {6144000, 96000, 0x50}, 3896387f866SBrian Austin {11289600, 44100, 0x01}, 3906387f866SBrian Austin {11289600, 88200, 0x41}, 3916387f866SBrian Austin {11289600, 176400, 0x81}, 3926387f866SBrian Austin {12000000, 44100, 0x03}, 3936387f866SBrian Austin {12000000, 48000, 0x13}, 3946387f866SBrian Austin {12000000, 88200, 0x43}, 3956387f866SBrian Austin {12000000, 96000, 0x53}, 3966387f866SBrian Austin {12000000, 176400, 0x83}, 3976387f866SBrian Austin {12000000, 192000, 0x93}, 3986387f866SBrian Austin {12288000, 48000, 0x11}, 3996387f866SBrian Austin {12288000, 96000, 0x51}, 4006387f866SBrian Austin {12288000, 192000, 0x91}, 4016387f866SBrian Austin {13000000, 44100, 0x07}, 4026387f866SBrian Austin {13000000, 48000, 0x17}, 4036387f866SBrian Austin {13000000, 88200, 0x47}, 4046387f866SBrian Austin {13000000, 96000, 0x57}, 4056387f866SBrian Austin {13000000, 176400, 0x87}, 4066387f866SBrian Austin {13000000, 192000, 0x97}, 4076387f866SBrian Austin {22579200, 44100, 0x02}, 4086387f866SBrian Austin {22579200, 88200, 0x42}, 4096387f866SBrian Austin {22579200, 176400, 0x82}, 4106387f866SBrian Austin {24000000, 44100, 0x0B}, 4116387f866SBrian Austin {24000000, 48000, 0x1B}, 4126387f866SBrian Austin {24000000, 88200, 0x4B}, 4136387f866SBrian Austin {24000000, 96000, 0x5B}, 4146387f866SBrian Austin {24000000, 176400, 0x8B}, 4156387f866SBrian Austin {24000000, 192000, 0x9B}, 4166387f866SBrian Austin {24576000, 48000, 0x12}, 4176387f866SBrian Austin {24576000, 96000, 0x52}, 4186387f866SBrian Austin {24576000, 192000, 0x92}, 4196387f866SBrian Austin {26000000, 44100, 0x0F}, 4206387f866SBrian Austin {26000000, 48000, 0x1F}, 4216387f866SBrian Austin {26000000, 88200, 0x4F}, 4226387f866SBrian Austin {26000000, 96000, 0x5F}, 4236387f866SBrian Austin {26000000, 176400, 0x8F}, 4246387f866SBrian Austin {26000000, 192000, 0x9F}, 4256387f866SBrian Austin }; 4266387f866SBrian Austin 4276387f866SBrian Austin static int cs35l35_get_clk_config(int sysclk, int srate) 4286387f866SBrian Austin { 4296387f866SBrian Austin int i; 4306387f866SBrian Austin 4316387f866SBrian Austin for (i = 0; i < ARRAY_SIZE(cs35l35_clk_ctl); i++) { 4326387f866SBrian Austin if (cs35l35_clk_ctl[i].sysclk == sysclk && 4336387f866SBrian Austin cs35l35_clk_ctl[i].srate == srate) 4346387f866SBrian Austin return cs35l35_clk_ctl[i].clk_cfg; 4356387f866SBrian Austin } 4366387f866SBrian Austin return -EINVAL; 4376387f866SBrian Austin } 4386387f866SBrian Austin 4396387f866SBrian Austin static int cs35l35_hw_params(struct snd_pcm_substream *substream, 4406387f866SBrian Austin struct snd_pcm_hw_params *params, 4416387f866SBrian Austin struct snd_soc_dai *dai) 4426387f866SBrian Austin { 4436387f866SBrian Austin struct snd_soc_codec *codec = dai->codec; 4446387f866SBrian Austin struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec); 4456387f866SBrian Austin struct classh_cfg *classh = &cs35l35->pdata.classh_algo; 4466387f866SBrian Austin int srate = params_rate(params); 4476387f866SBrian Austin int ret = 0; 4486387f866SBrian Austin u8 sp_sclks; 4496387f866SBrian Austin int audin_format; 4506387f866SBrian Austin int errata_chk; 4516387f866SBrian Austin 4526387f866SBrian Austin int clk_ctl = cs35l35_get_clk_config(cs35l35->sysclk, srate); 4536387f866SBrian Austin 4546387f866SBrian Austin if (clk_ctl < 0) { 4556387f866SBrian Austin dev_err(codec->dev, "Invalid CLK:Rate %d:%d\n", 4566387f866SBrian Austin cs35l35->sysclk, srate); 4576387f866SBrian Austin return -EINVAL; 4586387f866SBrian Austin } 4596387f866SBrian Austin 4606387f866SBrian Austin ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL2, 4616387f866SBrian Austin CS35L35_CLK_CTL2_MASK, clk_ctl); 4626387f866SBrian Austin if (ret != 0) { 4636387f866SBrian Austin dev_err(codec->dev, "Failed to set port config %d\n", ret); 4646387f866SBrian Austin return ret; 4656387f866SBrian Austin } 4666387f866SBrian Austin 4676387f866SBrian Austin /* 4686387f866SBrian Austin * Rev A0 Errata 4696387f866SBrian Austin * When configured for the weak-drive detection path (CH_WKFET_DIS = 0) 4706387f866SBrian Austin * the Class H algorithm does not enable weak-drive operation for 4716387f866SBrian Austin * nonzero values of CH_WKFET_DELAY if SP_RATE = 01 or 10 4726387f866SBrian Austin */ 4736387f866SBrian Austin errata_chk = clk_ctl & CS35L35_SP_RATE_MASK; 4746387f866SBrian Austin 4756387f866SBrian Austin if (classh->classh_wk_fet_disable == 0x00 && 4766387f866SBrian Austin (errata_chk == 0x01 || errata_chk == 0x03)) { 4776387f866SBrian Austin ret = regmap_update_bits(cs35l35->regmap, 4786387f866SBrian Austin CS35L35_CLASS_H_FET_DRIVE_CTL, 4796387f866SBrian Austin CS35L35_CH_WKFET_DEL_MASK, 4806387f866SBrian Austin 0 << CS35L35_CH_WKFET_DEL_SHIFT); 4816387f866SBrian Austin if (ret != 0) { 4826387f866SBrian Austin dev_err(codec->dev, "Failed to set fet config %d\n", 4836387f866SBrian Austin ret); 4846387f866SBrian Austin return ret; 4856387f866SBrian Austin } 4866387f866SBrian Austin } 4876387f866SBrian Austin 4886387f866SBrian Austin /* 4896387f866SBrian Austin * You can pull more Monitor data from the SDOUT pin than going to SDIN 4906387f866SBrian Austin * Just make sure your SCLK is fast enough to fill the frame 4916387f866SBrian Austin */ 4926387f866SBrian Austin if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 4936387f866SBrian Austin switch (params_width(params)) { 4946387f866SBrian Austin case 8: 4956387f866SBrian Austin audin_format = CS35L35_SDIN_DEPTH_8; 4966387f866SBrian Austin break; 4976387f866SBrian Austin case 16: 4986387f866SBrian Austin audin_format = CS35L35_SDIN_DEPTH_16; 4996387f866SBrian Austin break; 5006387f866SBrian Austin case 24: 5016387f866SBrian Austin audin_format = CS35L35_SDIN_DEPTH_24; 5026387f866SBrian Austin break; 5036387f866SBrian Austin default: 5046387f866SBrian Austin dev_err(codec->dev, "Unsupported Width %d\n", 5056387f866SBrian Austin params_width(params)); 5066387f866SBrian Austin return -EINVAL; 5076387f866SBrian Austin } 5086387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 5096387f866SBrian Austin CS35L35_AUDIN_DEPTH_CTL, 5106387f866SBrian Austin CS35L35_AUDIN_DEPTH_MASK, 5116387f866SBrian Austin audin_format << 5126387f866SBrian Austin CS35L35_AUDIN_DEPTH_SHIFT); 5136387f866SBrian Austin if (cs35l35->pdata.stereo) { 5146387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 5156387f866SBrian Austin CS35L35_AUDIN_DEPTH_CTL, 5166387f866SBrian Austin CS35L35_ADVIN_DEPTH_MASK, 5176387f866SBrian Austin audin_format << 5186387f866SBrian Austin CS35L35_ADVIN_DEPTH_SHIFT); 5196387f866SBrian Austin } 5206387f866SBrian Austin } 5216387f866SBrian Austin 5226387f866SBrian Austin if (cs35l35->i2s_mode) { 5236387f866SBrian Austin /* We have to take the SCLK to derive num sclks 5246387f866SBrian Austin * to configure the CLOCK_CTL3 register correctly 5256387f866SBrian Austin */ 5266387f866SBrian Austin if ((cs35l35->sclk / srate) % 4) { 5276387f866SBrian Austin dev_err(codec->dev, "Unsupported sclk/fs ratio %d:%d\n", 5286387f866SBrian Austin cs35l35->sclk, srate); 5296387f866SBrian Austin return -EINVAL; 5306387f866SBrian Austin } 5316387f866SBrian Austin sp_sclks = ((cs35l35->sclk / srate) / 4) - 1; 5326387f866SBrian Austin 5336387f866SBrian Austin /* Only certain ratios are supported in I2S Slave Mode */ 5346387f866SBrian Austin if (cs35l35->slave_mode) { 5356387f866SBrian Austin switch (sp_sclks) { 5366387f866SBrian Austin case CS35L35_SP_SCLKS_32FS: 5376387f866SBrian Austin case CS35L35_SP_SCLKS_48FS: 5386387f866SBrian Austin case CS35L35_SP_SCLKS_64FS: 5396387f866SBrian Austin break; 5406387f866SBrian Austin default: 5416387f866SBrian Austin dev_err(codec->dev, "ratio not supported\n"); 5426387f866SBrian Austin return -EINVAL; 5436387f866SBrian Austin }; 5446387f866SBrian Austin } else { 5456387f866SBrian Austin /* Only certain ratios supported in I2S MASTER Mode */ 5466387f866SBrian Austin switch (sp_sclks) { 5476387f866SBrian Austin case CS35L35_SP_SCLKS_32FS: 5486387f866SBrian Austin case CS35L35_SP_SCLKS_64FS: 5496387f866SBrian Austin break; 5506387f866SBrian Austin default: 5516387f866SBrian Austin dev_err(codec->dev, "ratio not supported\n"); 5526387f866SBrian Austin return -EINVAL; 5536387f866SBrian Austin }; 5546387f866SBrian Austin } 5556387f866SBrian Austin ret = regmap_update_bits(cs35l35->regmap, 5566387f866SBrian Austin CS35L35_CLK_CTL3, 5576387f866SBrian Austin CS35L35_SP_SCLKS_MASK, sp_sclks << 5586387f866SBrian Austin CS35L35_SP_SCLKS_SHIFT); 5596387f866SBrian Austin if (ret != 0) { 5606387f866SBrian Austin dev_err(codec->dev, "Failed to set fsclk %d\n", ret); 5616387f866SBrian Austin return ret; 5626387f866SBrian Austin } 5636387f866SBrian Austin } 5646387f866SBrian Austin 5656387f866SBrian Austin return ret; 5666387f866SBrian Austin } 5676387f866SBrian Austin 5686387f866SBrian Austin static const unsigned int cs35l35_src_rates[] = { 5696387f866SBrian Austin 44100, 48000, 88200, 96000, 176400, 192000 5706387f866SBrian Austin }; 5716387f866SBrian Austin 5726387f866SBrian Austin static const struct snd_pcm_hw_constraint_list cs35l35_constraints = { 5736387f866SBrian Austin .count = ARRAY_SIZE(cs35l35_src_rates), 5746387f866SBrian Austin .list = cs35l35_src_rates, 5756387f866SBrian Austin }; 5766387f866SBrian Austin 5776387f866SBrian Austin static int cs35l35_pcm_startup(struct snd_pcm_substream *substream, 5786387f866SBrian Austin struct snd_soc_dai *dai) 5796387f866SBrian Austin { 5806387f866SBrian Austin struct snd_soc_codec *codec = dai->codec; 5816387f866SBrian Austin struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec); 5826387f866SBrian Austin 5836387f866SBrian Austin if (!substream->runtime) 5846387f866SBrian Austin return 0; 5856387f866SBrian Austin 5866387f866SBrian Austin snd_pcm_hw_constraint_list(substream->runtime, 0, 5876387f866SBrian Austin SNDRV_PCM_HW_PARAM_RATE, &cs35l35_constraints); 5886387f866SBrian Austin 5896387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL, 5906387f866SBrian Austin CS35L35_PDM_MODE_MASK, 5916387f866SBrian Austin 0 << CS35L35_PDM_MODE_SHIFT); 5926387f866SBrian Austin 5936387f866SBrian Austin return 0; 5946387f866SBrian Austin } 5956387f866SBrian Austin 5966387f866SBrian Austin static const unsigned int cs35l35_pdm_rates[] = { 5976387f866SBrian Austin 44100, 48000, 88200, 96000 5986387f866SBrian Austin }; 5996387f866SBrian Austin 6006387f866SBrian Austin static const struct snd_pcm_hw_constraint_list cs35l35_pdm_constraints = { 6016387f866SBrian Austin .count = ARRAY_SIZE(cs35l35_pdm_rates), 6026387f866SBrian Austin .list = cs35l35_pdm_rates, 6036387f866SBrian Austin }; 6046387f866SBrian Austin 6056387f866SBrian Austin static int cs35l35_pdm_startup(struct snd_pcm_substream *substream, 6066387f866SBrian Austin struct snd_soc_dai *dai) 6076387f866SBrian Austin { 6086387f866SBrian Austin struct snd_soc_codec *codec = dai->codec; 6096387f866SBrian Austin struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec); 6106387f866SBrian Austin 6116387f866SBrian Austin if (!substream->runtime) 6126387f866SBrian Austin return 0; 6136387f866SBrian Austin 6146387f866SBrian Austin snd_pcm_hw_constraint_list(substream->runtime, 0, 6156387f866SBrian Austin SNDRV_PCM_HW_PARAM_RATE, 6166387f866SBrian Austin &cs35l35_pdm_constraints); 6176387f866SBrian Austin 6186387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL, 6196387f866SBrian Austin CS35L35_PDM_MODE_MASK, 6206387f866SBrian Austin 1 << CS35L35_PDM_MODE_SHIFT); 6216387f866SBrian Austin 6226387f866SBrian Austin return 0; 6236387f866SBrian Austin } 6246387f866SBrian Austin 6256387f866SBrian Austin static int cs35l35_dai_set_sysclk(struct snd_soc_dai *dai, 6266387f866SBrian Austin int clk_id, unsigned int freq, int dir) 6276387f866SBrian Austin { 6286387f866SBrian Austin struct snd_soc_codec *codec = dai->codec; 6296387f866SBrian Austin struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec); 6306387f866SBrian Austin 6316387f866SBrian Austin /* Need the SCLK Frequency regardless of sysclk source for I2S */ 6326387f866SBrian Austin cs35l35->sclk = freq; 6336387f866SBrian Austin 6346387f866SBrian Austin return 0; 6356387f866SBrian Austin } 6366387f866SBrian Austin 6376387f866SBrian Austin static const struct snd_soc_dai_ops cs35l35_ops = { 6386387f866SBrian Austin .startup = cs35l35_pcm_startup, 6396387f866SBrian Austin .set_fmt = cs35l35_set_dai_fmt, 6406387f866SBrian Austin .hw_params = cs35l35_hw_params, 6416387f866SBrian Austin .set_sysclk = cs35l35_dai_set_sysclk, 6426387f866SBrian Austin }; 6436387f866SBrian Austin 6446387f866SBrian Austin static const struct snd_soc_dai_ops cs35l35_pdm_ops = { 6456387f866SBrian Austin .startup = cs35l35_pdm_startup, 6466387f866SBrian Austin .set_fmt = cs35l35_set_dai_fmt, 6476387f866SBrian Austin .hw_params = cs35l35_hw_params, 6486387f866SBrian Austin }; 6496387f866SBrian Austin 6506387f866SBrian Austin static struct snd_soc_dai_driver cs35l35_dai[] = { 6516387f866SBrian Austin { 6526387f866SBrian Austin .name = "cs35l35-pcm", 6536387f866SBrian Austin .id = 0, 6546387f866SBrian Austin .playback = { 6556387f866SBrian Austin .stream_name = "AMP Playback", 6566387f866SBrian Austin .channels_min = 1, 6576387f866SBrian Austin .channels_max = 8, 6586387f866SBrian Austin .rates = SNDRV_PCM_RATE_KNOT, 6596387f866SBrian Austin .formats = CS35L35_FORMATS, 6606387f866SBrian Austin }, 6616387f866SBrian Austin .capture = { 6626387f866SBrian Austin .stream_name = "AMP Capture", 6636387f866SBrian Austin .channels_min = 1, 6646387f866SBrian Austin .channels_max = 8, 6656387f866SBrian Austin .rates = SNDRV_PCM_RATE_KNOT, 6666387f866SBrian Austin .formats = CS35L35_FORMATS, 6676387f866SBrian Austin }, 6686387f866SBrian Austin .ops = &cs35l35_ops, 6696387f866SBrian Austin .symmetric_rates = 1, 6706387f866SBrian Austin }, 6716387f866SBrian Austin { 6726387f866SBrian Austin .name = "cs35l35-pdm", 6736387f866SBrian Austin .id = 1, 6746387f866SBrian Austin .playback = { 6756387f866SBrian Austin .stream_name = "PDM Playback", 6766387f866SBrian Austin .channels_min = 1, 6776387f866SBrian Austin .channels_max = 2, 6786387f866SBrian Austin .rates = SNDRV_PCM_RATE_KNOT, 6796387f866SBrian Austin .formats = CS35L35_FORMATS, 6806387f866SBrian Austin }, 6816387f866SBrian Austin .ops = &cs35l35_pdm_ops, 6826387f866SBrian Austin }, 6836387f866SBrian Austin }; 6846387f866SBrian Austin 6856387f866SBrian Austin static int cs35l35_codec_set_sysclk(struct snd_soc_codec *codec, 6866387f866SBrian Austin int clk_id, int source, unsigned int freq, 6876387f866SBrian Austin int dir) 6886387f866SBrian Austin { 6896387f866SBrian Austin struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec); 6906387f866SBrian Austin int clksrc; 6916387f866SBrian Austin int ret = 0; 6926387f866SBrian Austin 6936387f866SBrian Austin switch (clk_id) { 6946387f866SBrian Austin case 0: 6956387f866SBrian Austin clksrc = CS35L35_CLK_SOURCE_MCLK; 6966387f866SBrian Austin break; 6976387f866SBrian Austin case 1: 6986387f866SBrian Austin clksrc = CS35L35_CLK_SOURCE_SCLK; 6996387f866SBrian Austin break; 7006387f866SBrian Austin case 2: 7016387f866SBrian Austin clksrc = CS35L35_CLK_SOURCE_PDM; 7026387f866SBrian Austin break; 7036387f866SBrian Austin default: 7046387f866SBrian Austin dev_err(codec->dev, "Invalid CLK Source\n"); 7056387f866SBrian Austin return -EINVAL; 7066387f866SBrian Austin }; 7076387f866SBrian Austin 7086387f866SBrian Austin switch (freq) { 7096387f866SBrian Austin case 5644800: 7106387f866SBrian Austin case 6144000: 7116387f866SBrian Austin case 11289600: 7126387f866SBrian Austin case 12000000: 7136387f866SBrian Austin case 12288000: 7146387f866SBrian Austin case 13000000: 7156387f866SBrian Austin case 22579200: 7166387f866SBrian Austin case 24000000: 7176387f866SBrian Austin case 24576000: 7186387f866SBrian Austin case 26000000: 7196387f866SBrian Austin cs35l35->sysclk = freq; 7206387f866SBrian Austin break; 7216387f866SBrian Austin default: 7226387f866SBrian Austin dev_err(codec->dev, "Invalid CLK Frequency Input : %d\n", freq); 7236387f866SBrian Austin return -EINVAL; 7246387f866SBrian Austin } 7256387f866SBrian Austin 7266387f866SBrian Austin ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1, 7276387f866SBrian Austin CS35L35_CLK_SOURCE_MASK, 7286387f866SBrian Austin clksrc << CS35L35_CLK_SOURCE_SHIFT); 7296387f866SBrian Austin if (ret != 0) { 7306387f866SBrian Austin dev_err(codec->dev, "Failed to set sysclk %d\n", ret); 7316387f866SBrian Austin return ret; 7326387f866SBrian Austin } 7336387f866SBrian Austin 7346387f866SBrian Austin return ret; 7356387f866SBrian Austin } 7366387f866SBrian Austin 7376387f866SBrian Austin static int cs35l35_codec_probe(struct snd_soc_codec *codec) 7386387f866SBrian Austin { 7396387f866SBrian Austin struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec); 7406387f866SBrian Austin struct classh_cfg *classh = &cs35l35->pdata.classh_algo; 7416387f866SBrian Austin struct monitor_cfg *monitor_config = &cs35l35->pdata.mon_cfg; 7426387f866SBrian Austin int ret; 7436387f866SBrian Austin 7446387f866SBrian Austin cs35l35->codec = codec; 7456387f866SBrian Austin 7466387f866SBrian Austin /* Set Platform Data */ 7476387f866SBrian Austin if (cs35l35->pdata.bst_vctl) 7486387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_BST_CVTR_V_CTL, 7496387f866SBrian Austin CS35L35_BST_CTL_MASK, 7506387f866SBrian Austin cs35l35->pdata.bst_vctl); 7516387f866SBrian Austin 7526387f866SBrian Austin if (cs35l35->pdata.bst_ipk) 7536387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_BST_PEAK_I, 7546387f866SBrian Austin CS35L35_BST_IPK_MASK, 7556387f866SBrian Austin cs35l35->pdata.bst_ipk << 7566387f866SBrian Austin CS35L35_BST_IPK_SHIFT); 7576387f866SBrian Austin 7586387f866SBrian Austin if (cs35l35->pdata.gain_zc) 7596387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL, 7606387f866SBrian Austin CS35L35_AMP_GAIN_ZC_MASK, 7616387f866SBrian Austin cs35l35->pdata.gain_zc << 7626387f866SBrian Austin CS35L35_AMP_GAIN_ZC_SHIFT); 7636387f866SBrian Austin 7646387f866SBrian Austin if (cs35l35->pdata.aud_channel) 7656387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 7666387f866SBrian Austin CS35L35_AUDIN_RXLOC_CTL, 7676387f866SBrian Austin CS35L35_AUD_IN_LR_MASK, 7686387f866SBrian Austin cs35l35->pdata.aud_channel << 7696387f866SBrian Austin CS35L35_AUD_IN_LR_SHIFT); 7706387f866SBrian Austin 7716387f866SBrian Austin if (cs35l35->pdata.stereo) { 7726387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 7736387f866SBrian Austin CS35L35_ADVIN_RXLOC_CTL, 7746387f866SBrian Austin CS35L35_ADV_IN_LR_MASK, 7756387f866SBrian Austin cs35l35->pdata.adv_channel << 7766387f866SBrian Austin CS35L35_ADV_IN_LR_SHIFT); 7776387f866SBrian Austin if (cs35l35->pdata.shared_bst) 7786387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_CLASS_H_CTL, 7796387f866SBrian Austin CS35L35_CH_STEREO_MASK, 7806387f866SBrian Austin 1 << CS35L35_CH_STEREO_SHIFT); 7816387f866SBrian Austin ret = snd_soc_add_codec_controls(codec, cs35l35_adv_controls, 7826387f866SBrian Austin ARRAY_SIZE(cs35l35_adv_controls)); 7836387f866SBrian Austin if (ret) 7846387f866SBrian Austin return ret; 7856387f866SBrian Austin } 7866387f866SBrian Austin 7876387f866SBrian Austin if (cs35l35->pdata.sp_drv_str) 7886387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1, 7896387f866SBrian Austin CS35L35_SP_DRV_MASK, 7906387f866SBrian Austin cs35l35->pdata.sp_drv_str << 7916387f866SBrian Austin CS35L35_SP_DRV_SHIFT); 7926387f866SBrian Austin 7936387f866SBrian Austin if (classh->classh_algo_enable) { 7946387f866SBrian Austin if (classh->classh_bst_override) 7956387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 7966387f866SBrian Austin CS35L35_CLASS_H_CTL, 7976387f866SBrian Austin CS35L35_CH_BST_OVR_MASK, 7986387f866SBrian Austin classh->classh_bst_override << 7996387f866SBrian Austin CS35L35_CH_BST_OVR_SHIFT); 8006387f866SBrian Austin if (classh->classh_bst_max_limit) 8016387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8026387f866SBrian Austin CS35L35_CLASS_H_CTL, 8036387f866SBrian Austin CS35L35_CH_BST_LIM_MASK, 8046387f866SBrian Austin classh->classh_bst_max_limit << 8056387f866SBrian Austin CS35L35_CH_BST_LIM_SHIFT); 8066387f866SBrian Austin if (classh->classh_mem_depth) 8076387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8086387f866SBrian Austin CS35L35_CLASS_H_CTL, 8096387f866SBrian Austin CS35L35_CH_MEM_DEPTH_MASK, 8106387f866SBrian Austin classh->classh_mem_depth << 8116387f866SBrian Austin CS35L35_CH_MEM_DEPTH_SHIFT); 8126387f866SBrian Austin if (classh->classh_headroom) 8136387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8146387f866SBrian Austin CS35L35_CLASS_H_HEADRM_CTL, 8156387f866SBrian Austin CS35L35_CH_HDRM_CTL_MASK, 8166387f866SBrian Austin classh->classh_headroom << 8176387f866SBrian Austin CS35L35_CH_HDRM_CTL_SHIFT); 8186387f866SBrian Austin if (classh->classh_release_rate) 8196387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8206387f866SBrian Austin CS35L35_CLASS_H_RELEASE_RATE, 8216387f866SBrian Austin CS35L35_CH_REL_RATE_MASK, 8226387f866SBrian Austin classh->classh_release_rate << 8236387f866SBrian Austin CS35L35_CH_REL_RATE_SHIFT); 8246387f866SBrian Austin if (classh->classh_wk_fet_disable) 8256387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8266387f866SBrian Austin CS35L35_CLASS_H_FET_DRIVE_CTL, 8276387f866SBrian Austin CS35L35_CH_WKFET_DIS_MASK, 8286387f866SBrian Austin classh->classh_wk_fet_disable << 8296387f866SBrian Austin CS35L35_CH_WKFET_DIS_SHIFT); 8306387f866SBrian Austin if (classh->classh_wk_fet_delay) 8316387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8326387f866SBrian Austin CS35L35_CLASS_H_FET_DRIVE_CTL, 8336387f866SBrian Austin CS35L35_CH_WKFET_DEL_MASK, 8346387f866SBrian Austin classh->classh_wk_fet_delay << 8356387f866SBrian Austin CS35L35_CH_WKFET_DEL_SHIFT); 8366387f866SBrian Austin if (classh->classh_wk_fet_thld) 8376387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8386387f866SBrian Austin CS35L35_CLASS_H_FET_DRIVE_CTL, 8396387f866SBrian Austin CS35L35_CH_WKFET_THLD_MASK, 8406387f866SBrian Austin classh->classh_wk_fet_thld << 8416387f866SBrian Austin CS35L35_CH_WKFET_THLD_SHIFT); 8426387f866SBrian Austin if (classh->classh_vpch_auto) 8436387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8446387f866SBrian Austin CS35L35_CLASS_H_VP_CTL, 8456387f866SBrian Austin CS35L35_CH_VP_AUTO_MASK, 8466387f866SBrian Austin classh->classh_vpch_auto << 8476387f866SBrian Austin CS35L35_CH_VP_AUTO_SHIFT); 8486387f866SBrian Austin if (classh->classh_vpch_rate) 8496387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8506387f866SBrian Austin CS35L35_CLASS_H_VP_CTL, 8516387f866SBrian Austin CS35L35_CH_VP_RATE_MASK, 8526387f866SBrian Austin classh->classh_vpch_rate << 8536387f866SBrian Austin CS35L35_CH_VP_RATE_SHIFT); 8546387f866SBrian Austin if (classh->classh_vpch_man) 8556387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8566387f866SBrian Austin CS35L35_CLASS_H_VP_CTL, 8576387f866SBrian Austin CS35L35_CH_VP_MAN_MASK, 8586387f866SBrian Austin classh->classh_vpch_man << 8596387f866SBrian Austin CS35L35_CH_VP_MAN_SHIFT); 8606387f866SBrian Austin } 8616387f866SBrian Austin 8626387f866SBrian Austin if (monitor_config->is_present) { 8636387f866SBrian Austin if (monitor_config->vmon_specs) { 8646387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8656387f866SBrian Austin CS35L35_SPKMON_DEPTH_CTL, 8666387f866SBrian Austin CS35L35_VMON_DEPTH_MASK, 8676387f866SBrian Austin monitor_config->vmon_dpth << 8686387f866SBrian Austin CS35L35_VMON_DEPTH_SHIFT); 8696387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8706387f866SBrian Austin CS35L35_VMON_TXLOC_CTL, 8716387f866SBrian Austin CS35L35_MON_TXLOC_MASK, 8726387f866SBrian Austin monitor_config->vmon_loc << 8736387f866SBrian Austin CS35L35_MON_TXLOC_SHIFT); 8746387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8756387f866SBrian Austin CS35L35_VMON_TXLOC_CTL, 8766387f866SBrian Austin CS35L35_MON_FRM_MASK, 8776387f866SBrian Austin monitor_config->vmon_frm << 8786387f866SBrian Austin CS35L35_MON_FRM_SHIFT); 8796387f866SBrian Austin } 8806387f866SBrian Austin if (monitor_config->imon_specs) { 8816387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8826387f866SBrian Austin CS35L35_SPKMON_DEPTH_CTL, 8836387f866SBrian Austin CS35L35_IMON_DEPTH_MASK, 8846387f866SBrian Austin monitor_config->imon_dpth << 8856387f866SBrian Austin CS35L35_IMON_DEPTH_SHIFT); 8866387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8876387f866SBrian Austin CS35L35_IMON_TXLOC_CTL, 8886387f866SBrian Austin CS35L35_MON_TXLOC_MASK, 8896387f866SBrian Austin monitor_config->imon_loc << 8906387f866SBrian Austin CS35L35_MON_TXLOC_SHIFT); 8916387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8926387f866SBrian Austin CS35L35_IMON_TXLOC_CTL, 8936387f866SBrian Austin CS35L35_MON_FRM_MASK, 8946387f866SBrian Austin monitor_config->imon_frm << 8956387f866SBrian Austin CS35L35_MON_FRM_SHIFT); 8966387f866SBrian Austin } 8976387f866SBrian Austin if (monitor_config->vpmon_specs) { 8986387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 8996387f866SBrian Austin CS35L35_SUPMON_DEPTH_CTL, 9006387f866SBrian Austin CS35L35_VPMON_DEPTH_MASK, 9016387f866SBrian Austin monitor_config->vpmon_dpth << 9026387f866SBrian Austin CS35L35_VPMON_DEPTH_SHIFT); 9036387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 9046387f866SBrian Austin CS35L35_VPMON_TXLOC_CTL, 9056387f866SBrian Austin CS35L35_MON_TXLOC_MASK, 9066387f866SBrian Austin monitor_config->vpmon_loc << 9076387f866SBrian Austin CS35L35_MON_TXLOC_SHIFT); 9086387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 9096387f866SBrian Austin CS35L35_VPMON_TXLOC_CTL, 9106387f866SBrian Austin CS35L35_MON_FRM_MASK, 9116387f866SBrian Austin monitor_config->vpmon_frm << 9126387f866SBrian Austin CS35L35_MON_FRM_SHIFT); 9136387f866SBrian Austin } 9146387f866SBrian Austin if (monitor_config->vbstmon_specs) { 9156387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 9166387f866SBrian Austin CS35L35_SUPMON_DEPTH_CTL, 9176387f866SBrian Austin CS35L35_VBSTMON_DEPTH_MASK, 9186387f866SBrian Austin monitor_config->vpmon_dpth << 9196387f866SBrian Austin CS35L35_VBSTMON_DEPTH_SHIFT); 9206387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 9216387f866SBrian Austin CS35L35_VBSTMON_TXLOC_CTL, 9226387f866SBrian Austin CS35L35_MON_TXLOC_MASK, 9236387f866SBrian Austin monitor_config->vbstmon_loc << 9246387f866SBrian Austin CS35L35_MON_TXLOC_SHIFT); 9256387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 9266387f866SBrian Austin CS35L35_VBSTMON_TXLOC_CTL, 9276387f866SBrian Austin CS35L35_MON_FRM_MASK, 9286387f866SBrian Austin monitor_config->vbstmon_frm << 9296387f866SBrian Austin CS35L35_MON_FRM_SHIFT); 9306387f866SBrian Austin } 9316387f866SBrian Austin if (monitor_config->vpbrstat_specs) { 9326387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 9336387f866SBrian Austin CS35L35_SUPMON_DEPTH_CTL, 9346387f866SBrian Austin CS35L35_VPBRSTAT_DEPTH_MASK, 9356387f866SBrian Austin monitor_config->vpbrstat_dpth << 9366387f866SBrian Austin CS35L35_VPBRSTAT_DEPTH_SHIFT); 9376387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 9386387f866SBrian Austin CS35L35_VPBR_STATUS_TXLOC_CTL, 9396387f866SBrian Austin CS35L35_MON_TXLOC_MASK, 9406387f866SBrian Austin monitor_config->vpbrstat_loc << 9416387f866SBrian Austin CS35L35_MON_TXLOC_SHIFT); 9426387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 9436387f866SBrian Austin CS35L35_VPBR_STATUS_TXLOC_CTL, 9446387f866SBrian Austin CS35L35_MON_FRM_MASK, 9456387f866SBrian Austin monitor_config->vpbrstat_frm << 9466387f866SBrian Austin CS35L35_MON_FRM_SHIFT); 9476387f866SBrian Austin } 9486387f866SBrian Austin if (monitor_config->zerofill_specs) { 9496387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 9506387f866SBrian Austin CS35L35_SUPMON_DEPTH_CTL, 9516387f866SBrian Austin CS35L35_ZEROFILL_DEPTH_MASK, 9526387f866SBrian Austin monitor_config->zerofill_dpth << 9536387f866SBrian Austin CS35L35_ZEROFILL_DEPTH_SHIFT); 9546387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 9556387f866SBrian Austin CS35L35_ZERO_FILL_LOC_CTL, 9566387f866SBrian Austin CS35L35_MON_TXLOC_MASK, 9576387f866SBrian Austin monitor_config->zerofill_loc << 9586387f866SBrian Austin CS35L35_MON_TXLOC_SHIFT); 9596387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 9606387f866SBrian Austin CS35L35_ZERO_FILL_LOC_CTL, 9616387f866SBrian Austin CS35L35_MON_FRM_MASK, 9626387f866SBrian Austin monitor_config->zerofill_frm << 9636387f866SBrian Austin CS35L35_MON_FRM_SHIFT); 9646387f866SBrian Austin } 9656387f866SBrian Austin } 9666387f866SBrian Austin 9676387f866SBrian Austin return ret; 9686387f866SBrian Austin } 9696387f866SBrian Austin 9706387f866SBrian Austin static struct snd_soc_codec_driver soc_codec_dev_cs35l35 = { 9716387f866SBrian Austin .probe = cs35l35_codec_probe, 9726387f866SBrian Austin .set_sysclk = cs35l35_codec_set_sysclk, 9736387f866SBrian Austin .component_driver = { 9746387f866SBrian Austin .dapm_widgets = cs35l35_dapm_widgets, 9756387f866SBrian Austin .num_dapm_widgets = ARRAY_SIZE(cs35l35_dapm_widgets), 9766387f866SBrian Austin 9776387f866SBrian Austin .dapm_routes = cs35l35_audio_map, 9786387f866SBrian Austin .num_dapm_routes = ARRAY_SIZE(cs35l35_audio_map), 9796387f866SBrian Austin 9806387f866SBrian Austin .controls = cs35l35_aud_controls, 9816387f866SBrian Austin .num_controls = ARRAY_SIZE(cs35l35_aud_controls), 9826387f866SBrian Austin }, 9836387f866SBrian Austin 9846387f866SBrian Austin }; 9856387f866SBrian Austin 9866387f866SBrian Austin static struct regmap_config cs35l35_regmap = { 9876387f866SBrian Austin .reg_bits = 8, 9886387f866SBrian Austin .val_bits = 8, 9896387f866SBrian Austin 9906387f866SBrian Austin .max_register = CS35L35_MAX_REGISTER, 9916387f866SBrian Austin .reg_defaults = cs35l35_reg, 9926387f866SBrian Austin .num_reg_defaults = ARRAY_SIZE(cs35l35_reg), 9936387f866SBrian Austin .volatile_reg = cs35l35_volatile_register, 9946387f866SBrian Austin .readable_reg = cs35l35_readable_register, 9956387f866SBrian Austin .precious_reg = cs35l35_precious_register, 9966387f866SBrian Austin .cache_type = REGCACHE_RBTREE, 9976387f866SBrian Austin }; 9986387f866SBrian Austin 9996387f866SBrian Austin static irqreturn_t cs35l35_irq(int irq, void *data) 10006387f866SBrian Austin { 10016387f866SBrian Austin struct cs35l35_private *cs35l35 = data; 10026387f866SBrian Austin struct snd_soc_codec *codec = cs35l35->codec; 10036387f866SBrian Austin unsigned int sticky1, sticky2, sticky3, sticky4; 10046387f866SBrian Austin unsigned int mask1, mask2, mask3, mask4, current1; 10056387f866SBrian Austin 10066387f866SBrian Austin /* ack the irq by reading all status registers */ 10076387f866SBrian Austin regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_4, &sticky4); 10086387f866SBrian Austin regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_3, &sticky3); 10096387f866SBrian Austin regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_2, &sticky2); 10106387f866SBrian Austin regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &sticky1); 10116387f866SBrian Austin 10126387f866SBrian Austin regmap_read(cs35l35->regmap, CS35L35_INT_MASK_4, &mask4); 10136387f866SBrian Austin regmap_read(cs35l35->regmap, CS35L35_INT_MASK_3, &mask3); 10146387f866SBrian Austin regmap_read(cs35l35->regmap, CS35L35_INT_MASK_2, &mask2); 10156387f866SBrian Austin regmap_read(cs35l35->regmap, CS35L35_INT_MASK_1, &mask1); 10166387f866SBrian Austin 10176387f866SBrian Austin /* Check to see if unmasked bits are active */ 10186387f866SBrian Austin if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3) 10196387f866SBrian Austin && !(sticky4 & ~mask4)) 10206387f866SBrian Austin return IRQ_NONE; 10216387f866SBrian Austin 10226387f866SBrian Austin if (sticky2 & CS35L35_PDN_DONE) 10236387f866SBrian Austin complete(&cs35l35->pdn_done); 10246387f866SBrian Austin 10256387f866SBrian Austin /* read the current values */ 10266387f866SBrian Austin regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, ¤t1); 10276387f866SBrian Austin 10286387f866SBrian Austin /* handle the interrupts */ 10296387f866SBrian Austin if (sticky1 & CS35L35_CAL_ERR) { 10306387f866SBrian Austin dev_crit(codec->dev, "Calibration Error\n"); 10316387f866SBrian Austin 10326387f866SBrian Austin /* error is no longer asserted; safe to reset */ 10336387f866SBrian Austin if (!(current1 & CS35L35_CAL_ERR)) { 10346387f866SBrian Austin pr_debug("%s : Cal error release\n", __func__); 10356387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 10366387f866SBrian Austin CS35L35_PROT_RELEASE_CTL, 10376387f866SBrian Austin CS35L35_CAL_ERR_RLS, 0); 10386387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 10396387f866SBrian Austin CS35L35_PROT_RELEASE_CTL, 10406387f866SBrian Austin CS35L35_CAL_ERR_RLS, 10416387f866SBrian Austin CS35L35_CAL_ERR_RLS); 10426387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 10436387f866SBrian Austin CS35L35_PROT_RELEASE_CTL, 10446387f866SBrian Austin CS35L35_CAL_ERR_RLS, 0); 10456387f866SBrian Austin } 10466387f866SBrian Austin } 10476387f866SBrian Austin 10486387f866SBrian Austin if (sticky1 & CS35L35_AMP_SHORT) { 10496387f866SBrian Austin dev_crit(codec->dev, "AMP Short Error\n"); 10506387f866SBrian Austin /* error is no longer asserted; safe to reset */ 10516387f866SBrian Austin if (!(current1 & CS35L35_AMP_SHORT)) { 10526387f866SBrian Austin dev_dbg(codec->dev, "Amp short error release\n"); 10536387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 10546387f866SBrian Austin CS35L35_PROT_RELEASE_CTL, 10556387f866SBrian Austin CS35L35_SHORT_RLS, 0); 10566387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 10576387f866SBrian Austin CS35L35_PROT_RELEASE_CTL, 10586387f866SBrian Austin CS35L35_SHORT_RLS, 10596387f866SBrian Austin CS35L35_SHORT_RLS); 10606387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 10616387f866SBrian Austin CS35L35_PROT_RELEASE_CTL, 10626387f866SBrian Austin CS35L35_SHORT_RLS, 0); 10636387f866SBrian Austin } 10646387f866SBrian Austin } 10656387f866SBrian Austin 10666387f866SBrian Austin if (sticky1 & CS35L35_OTW) { 10676387f866SBrian Austin dev_warn(codec->dev, "Over temperature warning\n"); 10686387f866SBrian Austin 10696387f866SBrian Austin /* error is no longer asserted; safe to reset */ 10706387f866SBrian Austin if (!(current1 & CS35L35_OTW)) { 10716387f866SBrian Austin dev_dbg(codec->dev, "Over temperature warn release\n"); 10726387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 10736387f866SBrian Austin CS35L35_PROT_RELEASE_CTL, 10746387f866SBrian Austin CS35L35_OTW_RLS, 0); 10756387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 10766387f866SBrian Austin CS35L35_PROT_RELEASE_CTL, 10776387f866SBrian Austin CS35L35_OTW_RLS, 10786387f866SBrian Austin CS35L35_OTW_RLS); 10796387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 10806387f866SBrian Austin CS35L35_PROT_RELEASE_CTL, 10816387f866SBrian Austin CS35L35_OTW_RLS, 0); 10826387f866SBrian Austin } 10836387f866SBrian Austin } 10846387f866SBrian Austin 10856387f866SBrian Austin if (sticky1 & CS35L35_OTE) { 10866387f866SBrian Austin dev_crit(codec->dev, "Over temperature error\n"); 10876387f866SBrian Austin /* error is no longer asserted; safe to reset */ 10886387f866SBrian Austin if (!(current1 & CS35L35_OTE)) { 10896387f866SBrian Austin dev_dbg(codec->dev, "Over temperature error release\n"); 10906387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 10916387f866SBrian Austin CS35L35_PROT_RELEASE_CTL, 10926387f866SBrian Austin CS35L35_OTE_RLS, 0); 10936387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 10946387f866SBrian Austin CS35L35_PROT_RELEASE_CTL, 10956387f866SBrian Austin CS35L35_OTE_RLS, 10966387f866SBrian Austin CS35L35_OTE_RLS); 10976387f866SBrian Austin regmap_update_bits(cs35l35->regmap, 10986387f866SBrian Austin CS35L35_PROT_RELEASE_CTL, 10996387f866SBrian Austin CS35L35_OTE_RLS, 0); 11006387f866SBrian Austin } 11016387f866SBrian Austin } 11026387f866SBrian Austin 11036387f866SBrian Austin if (sticky3 & CS35L35_BST_HIGH) { 11046387f866SBrian Austin dev_crit(codec->dev, "VBST error: powering off!\n"); 11056387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, 11066387f866SBrian Austin CS35L35_PDN_AMP, CS35L35_PDN_AMP); 11076387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1, 11086387f866SBrian Austin CS35L35_PDN_ALL, CS35L35_PDN_ALL); 11096387f866SBrian Austin } 11106387f866SBrian Austin 11116387f866SBrian Austin if (sticky3 & CS35L35_LBST_SHORT) { 11126387f866SBrian Austin dev_crit(codec->dev, "LBST error: powering off!\n"); 11136387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, 11146387f866SBrian Austin CS35L35_PDN_AMP, CS35L35_PDN_AMP); 11156387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1, 11166387f866SBrian Austin CS35L35_PDN_ALL, CS35L35_PDN_ALL); 11176387f866SBrian Austin } 11186387f866SBrian Austin 11196387f866SBrian Austin if (sticky2 & CS35L35_VPBR_ERR) 11206387f866SBrian Austin dev_dbg(codec->dev, "Error: Reactive Brownout\n"); 11216387f866SBrian Austin 11226387f866SBrian Austin if (sticky4 & CS35L35_VMON_OVFL) 11236387f866SBrian Austin dev_dbg(codec->dev, "Error: VMON overflow\n"); 11246387f866SBrian Austin 11256387f866SBrian Austin if (sticky4 & CS35L35_IMON_OVFL) 11266387f866SBrian Austin dev_dbg(codec->dev, "Error: IMON overflow\n"); 11276387f866SBrian Austin 11286387f866SBrian Austin return IRQ_HANDLED; 11296387f866SBrian Austin } 11306387f866SBrian Austin 11316387f866SBrian Austin 11326387f866SBrian Austin static int cs35l35_handle_of_data(struct i2c_client *i2c_client, 11336387f866SBrian Austin struct cs35l35_platform_data *pdata) 11346387f866SBrian Austin { 11356387f866SBrian Austin struct device_node *np = i2c_client->dev.of_node; 11366387f866SBrian Austin struct device_node *classh, *signal_format; 11376387f866SBrian Austin struct classh_cfg *classh_config = &pdata->classh_algo; 11386387f866SBrian Austin struct monitor_cfg *monitor_config = &pdata->mon_cfg; 11396387f866SBrian Austin unsigned int val32 = 0; 11406387f866SBrian Austin u8 monitor_array[3]; 11416387f866SBrian Austin int ret = 0; 11426387f866SBrian Austin 11436387f866SBrian Austin if (!np) 11446387f866SBrian Austin return 0; 11456387f866SBrian Austin 11466387f866SBrian Austin pdata->bst_pdn_fet_on = of_property_read_bool(np, 11476387f866SBrian Austin "cirrus,boost-pdn-fet-on"); 11486387f866SBrian Austin 11496387f866SBrian Austin ret = of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val32); 11506387f866SBrian Austin if (ret >= 0) { 11516387f866SBrian Austin if (val32 < 2600 || val32 > 9000) { 11526387f866SBrian Austin dev_err(&i2c_client->dev, 11536387f866SBrian Austin "Invalid Boost Voltage %d mV\n", val32); 11546387f866SBrian Austin return -EINVAL; 11556387f866SBrian Austin } 11566387f866SBrian Austin pdata->bst_vctl = ((val32 - 2600) / 100) + 1; 11576387f866SBrian Austin } 11586387f866SBrian Austin 11596387f866SBrian Austin ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val32); 11606387f866SBrian Austin if (ret >= 0) { 11616387f866SBrian Austin if (val32 < 1680 || val32 > 4480) { 11626387f866SBrian Austin dev_err(&i2c_client->dev, 11636387f866SBrian Austin "Invalid Boost Peak Current %u mA\n", val32); 11646387f866SBrian Austin return -EINVAL; 11656387f866SBrian Austin } 11666387f866SBrian Austin 11676387f866SBrian Austin pdata->bst_ipk = (val32 - 1680) / 110; 11686387f866SBrian Austin } 11696387f866SBrian Austin 11706387f866SBrian Austin if (of_property_read_u32(np, "cirrus,sp-drv-strength", &val32) >= 0) 11716387f866SBrian Austin pdata->sp_drv_str = val32; 11726387f866SBrian Austin 11736387f866SBrian Austin pdata->stereo = of_property_read_bool(np, "cirrus,stereo-config"); 11746387f866SBrian Austin 11756387f866SBrian Austin if (pdata->stereo) { 11766387f866SBrian Austin ret = of_property_read_u32(np, "cirrus,audio-channel", &val32); 11776387f866SBrian Austin if (ret >= 0) 11786387f866SBrian Austin pdata->aud_channel = val32; 11796387f866SBrian Austin 11806387f866SBrian Austin ret = of_property_read_u32(np, "cirrus,advisory-channel", 11816387f866SBrian Austin &val32); 11826387f866SBrian Austin if (ret >= 0) 11836387f866SBrian Austin pdata->adv_channel = val32; 11846387f866SBrian Austin 11856387f866SBrian Austin pdata->shared_bst = of_property_read_bool(np, 11866387f866SBrian Austin "cirrus,shared-boost"); 11876387f866SBrian Austin } 11886387f866SBrian Austin 11896387f866SBrian Austin pdata->gain_zc = of_property_read_bool(np, "cirrus,amp-gain-zc"); 11906387f866SBrian Austin 11916387f866SBrian Austin classh = of_get_child_by_name(np, "cirrus,classh-internal-algo"); 11926387f866SBrian Austin classh_config->classh_algo_enable = classh ? true : false; 11936387f866SBrian Austin 11946387f866SBrian Austin if (classh_config->classh_algo_enable) { 11956387f866SBrian Austin classh_config->classh_bst_override = 11966387f866SBrian Austin of_property_read_bool(np, "cirrus,classh-bst-overide"); 11976387f866SBrian Austin 11986387f866SBrian Austin ret = of_property_read_u32(classh, 11996387f866SBrian Austin "cirrus,classh-bst-max-limit", 12006387f866SBrian Austin &val32); 12016387f866SBrian Austin if (ret >= 0) { 12026387f866SBrian Austin val32 |= CS35L35_VALID_PDATA; 12036387f866SBrian Austin classh_config->classh_bst_max_limit = val32; 12046387f866SBrian Austin } 12056387f866SBrian Austin 12066387f866SBrian Austin ret = of_property_read_u32(classh, 12076387f866SBrian Austin "cirrus,classh-bst-max-limit", 12086387f866SBrian Austin &val32); 12096387f866SBrian Austin if (ret >= 0) { 12106387f866SBrian Austin val32 |= CS35L35_VALID_PDATA; 12116387f866SBrian Austin classh_config->classh_bst_max_limit = val32; 12126387f866SBrian Austin } 12136387f866SBrian Austin 12146387f866SBrian Austin ret = of_property_read_u32(classh, "cirrus,classh-mem-depth", 12156387f866SBrian Austin &val32); 12166387f866SBrian Austin if (ret >= 0) { 12176387f866SBrian Austin val32 |= CS35L35_VALID_PDATA; 12186387f866SBrian Austin classh_config->classh_mem_depth = val32; 12196387f866SBrian Austin } 12206387f866SBrian Austin 12216387f866SBrian Austin ret = of_property_read_u32(classh, "cirrus,classh-release-rate", 12226387f866SBrian Austin &val32); 12236387f866SBrian Austin if (ret >= 0) 12246387f866SBrian Austin classh_config->classh_release_rate = val32; 12256387f866SBrian Austin 12266387f866SBrian Austin ret = of_property_read_u32(classh, "cirrus,classh-headroom", 12276387f866SBrian Austin &val32); 12286387f866SBrian Austin if (ret >= 0) { 12296387f866SBrian Austin val32 |= CS35L35_VALID_PDATA; 12306387f866SBrian Austin classh_config->classh_headroom = val32; 12316387f866SBrian Austin } 12326387f866SBrian Austin 12336387f866SBrian Austin ret = of_property_read_u32(classh, 12346387f866SBrian Austin "cirrus,classh-wk-fet-disable", 12356387f866SBrian Austin &val32); 12366387f866SBrian Austin if (ret >= 0) 12376387f866SBrian Austin classh_config->classh_wk_fet_disable = val32; 12386387f866SBrian Austin 12396387f866SBrian Austin ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-delay", 12406387f866SBrian Austin &val32); 12416387f866SBrian Austin if (ret >= 0) { 12426387f866SBrian Austin val32 |= CS35L35_VALID_PDATA; 12436387f866SBrian Austin classh_config->classh_wk_fet_delay = val32; 12446387f866SBrian Austin } 12456387f866SBrian Austin 12466387f866SBrian Austin ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-thld", 12476387f866SBrian Austin &val32); 12486387f866SBrian Austin if (ret >= 0) 12496387f866SBrian Austin classh_config->classh_wk_fet_thld = val32; 12506387f866SBrian Austin 12516387f866SBrian Austin ret = of_property_read_u32(classh, "cirrus,classh-vpch-auto", 12526387f866SBrian Austin &val32); 12536387f866SBrian Austin if (ret >= 0) { 12546387f866SBrian Austin val32 |= CS35L35_VALID_PDATA; 12556387f866SBrian Austin classh_config->classh_vpch_auto = val32; 12566387f866SBrian Austin } 12576387f866SBrian Austin 12586387f866SBrian Austin ret = of_property_read_u32(classh, "cirrus,classh-vpch-rate", 12596387f866SBrian Austin &val32); 12606387f866SBrian Austin if (ret >= 0) { 12616387f866SBrian Austin val32 |= CS35L35_VALID_PDATA; 12626387f866SBrian Austin classh_config->classh_vpch_rate = val32; 12636387f866SBrian Austin } 12646387f866SBrian Austin 12656387f866SBrian Austin ret = of_property_read_u32(classh, "cirrus,classh-vpch-man", 12666387f866SBrian Austin &val32); 12676387f866SBrian Austin if (ret >= 0) 12686387f866SBrian Austin classh_config->classh_vpch_man = val32; 12696387f866SBrian Austin } 12706387f866SBrian Austin of_node_put(classh); 12716387f866SBrian Austin 12726387f866SBrian Austin /* frame depth location */ 12736387f866SBrian Austin signal_format = of_get_child_by_name(np, "cirrus,monitor-signal-format"); 12746387f866SBrian Austin monitor_config->is_present = signal_format ? true : false; 12756387f866SBrian Austin if (monitor_config->is_present) { 12766387f866SBrian Austin ret = of_property_read_u8_array(signal_format, "cirrus,imon", 12776387f866SBrian Austin monitor_array, ARRAY_SIZE(monitor_array)); 12786387f866SBrian Austin if (!ret) { 12796387f866SBrian Austin monitor_config->imon_specs = true; 12806387f866SBrian Austin monitor_config->imon_dpth = monitor_array[0]; 12816387f866SBrian Austin monitor_config->imon_loc = monitor_array[1]; 12826387f866SBrian Austin monitor_config->imon_frm = monitor_array[2]; 12836387f866SBrian Austin } 12846387f866SBrian Austin ret = of_property_read_u8_array(signal_format, "cirrus,vmon", 12856387f866SBrian Austin monitor_array, ARRAY_SIZE(monitor_array)); 12866387f866SBrian Austin if (!ret) { 12876387f866SBrian Austin monitor_config->vmon_specs = true; 12886387f866SBrian Austin monitor_config->vmon_dpth = monitor_array[0]; 12896387f866SBrian Austin monitor_config->vmon_loc = monitor_array[1]; 12906387f866SBrian Austin monitor_config->vmon_frm = monitor_array[2]; 12916387f866SBrian Austin } 12926387f866SBrian Austin ret = of_property_read_u8_array(signal_format, "cirrus,vpmon", 12936387f866SBrian Austin monitor_array, ARRAY_SIZE(monitor_array)); 12946387f866SBrian Austin if (!ret) { 12956387f866SBrian Austin monitor_config->vpmon_specs = true; 12966387f866SBrian Austin monitor_config->vpmon_dpth = monitor_array[0]; 12976387f866SBrian Austin monitor_config->vpmon_loc = monitor_array[1]; 12986387f866SBrian Austin monitor_config->vpmon_frm = monitor_array[2]; 12996387f866SBrian Austin } 13006387f866SBrian Austin ret = of_property_read_u8_array(signal_format, "cirrus,vbstmon", 13016387f866SBrian Austin monitor_array, ARRAY_SIZE(monitor_array)); 13026387f866SBrian Austin if (!ret) { 13036387f866SBrian Austin monitor_config->vbstmon_specs = true; 13046387f866SBrian Austin monitor_config->vbstmon_dpth = monitor_array[0]; 13056387f866SBrian Austin monitor_config->vbstmon_loc = monitor_array[1]; 13066387f866SBrian Austin monitor_config->vbstmon_frm = monitor_array[2]; 13076387f866SBrian Austin } 13086387f866SBrian Austin ret = of_property_read_u8_array(signal_format, "cirrus,vpbrstat", 13096387f866SBrian Austin monitor_array, ARRAY_SIZE(monitor_array)); 13106387f866SBrian Austin if (!ret) { 13116387f866SBrian Austin monitor_config->vpbrstat_specs = true; 13126387f866SBrian Austin monitor_config->vpbrstat_dpth = monitor_array[0]; 13136387f866SBrian Austin monitor_config->vpbrstat_loc = monitor_array[1]; 13146387f866SBrian Austin monitor_config->vpbrstat_frm = monitor_array[2]; 13156387f866SBrian Austin } 13166387f866SBrian Austin ret = of_property_read_u8_array(signal_format, "cirrus,zerofill", 13176387f866SBrian Austin monitor_array, ARRAY_SIZE(monitor_array)); 13186387f866SBrian Austin if (!ret) { 13196387f866SBrian Austin monitor_config->zerofill_specs = true; 13206387f866SBrian Austin monitor_config->zerofill_dpth = monitor_array[0]; 13216387f866SBrian Austin monitor_config->zerofill_loc = monitor_array[1]; 13226387f866SBrian Austin monitor_config->zerofill_frm = monitor_array[2]; 13236387f866SBrian Austin } 13246387f866SBrian Austin } 13256387f866SBrian Austin of_node_put(signal_format); 13266387f866SBrian Austin 13276387f866SBrian Austin return 0; 13286387f866SBrian Austin } 13296387f866SBrian Austin 13306387f866SBrian Austin /* Errata Rev A0 */ 13316387f866SBrian Austin static const struct reg_sequence cs35l35_errata_patch[] = { 13326387f866SBrian Austin 13336387f866SBrian Austin { 0x7F, 0x99 }, 13346387f866SBrian Austin { 0x00, 0x99 }, 13356387f866SBrian Austin { 0x52, 0x22 }, 13366387f866SBrian Austin { 0x04, 0x14 }, 13376387f866SBrian Austin { 0x6D, 0x44 }, 13386387f866SBrian Austin { 0x24, 0x10 }, 13396387f866SBrian Austin { 0x58, 0xC4 }, 13406387f866SBrian Austin { 0x00, 0x98 }, 13416387f866SBrian Austin { 0x18, 0x08 }, 13426387f866SBrian Austin { 0x00, 0x00 }, 13436387f866SBrian Austin { 0x7F, 0x00 }, 13446387f866SBrian Austin }; 13456387f866SBrian Austin 13466387f866SBrian Austin static int cs35l35_i2c_probe(struct i2c_client *i2c_client, 13476387f866SBrian Austin const struct i2c_device_id *id) 13486387f866SBrian Austin { 13496387f866SBrian Austin struct cs35l35_private *cs35l35; 13506387f866SBrian Austin struct cs35l35_platform_data *pdata = 13516387f866SBrian Austin dev_get_platdata(&i2c_client->dev); 13526387f866SBrian Austin int i; 13536387f866SBrian Austin int ret; 13546387f866SBrian Austin unsigned int devid = 0; 13556387f866SBrian Austin unsigned int reg; 13566387f866SBrian Austin 13576387f866SBrian Austin cs35l35 = devm_kzalloc(&i2c_client->dev, 13586387f866SBrian Austin sizeof(struct cs35l35_private), 13596387f866SBrian Austin GFP_KERNEL); 13606387f866SBrian Austin if (!cs35l35) 13616387f866SBrian Austin return -ENOMEM; 13626387f866SBrian Austin 13636387f866SBrian Austin i2c_set_clientdata(i2c_client, cs35l35); 13646387f866SBrian Austin cs35l35->regmap = devm_regmap_init_i2c(i2c_client, &cs35l35_regmap); 13656387f866SBrian Austin if (IS_ERR(cs35l35->regmap)) { 13666387f866SBrian Austin ret = PTR_ERR(cs35l35->regmap); 13676387f866SBrian Austin dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); 13686387f866SBrian Austin goto err; 13696387f866SBrian Austin } 13706387f866SBrian Austin 13716387f866SBrian Austin for (i = 0; i < ARRAY_SIZE(cs35l35_supplies); i++) 13726387f866SBrian Austin cs35l35->supplies[i].supply = cs35l35_supplies[i]; 13736387f866SBrian Austin cs35l35->num_supplies = ARRAY_SIZE(cs35l35_supplies); 13746387f866SBrian Austin 13756387f866SBrian Austin ret = devm_regulator_bulk_get(&i2c_client->dev, 13766387f866SBrian Austin cs35l35->num_supplies, 13776387f866SBrian Austin cs35l35->supplies); 13786387f866SBrian Austin if (ret != 0) { 13796387f866SBrian Austin dev_err(&i2c_client->dev, 13806387f866SBrian Austin "Failed to request core supplies: %d\n", 13816387f866SBrian Austin ret); 13826387f866SBrian Austin return ret; 13836387f866SBrian Austin } 13846387f866SBrian Austin 13856387f866SBrian Austin if (pdata) { 13866387f866SBrian Austin cs35l35->pdata = *pdata; 13876387f866SBrian Austin } else { 13886387f866SBrian Austin pdata = devm_kzalloc(&i2c_client->dev, 13896387f866SBrian Austin sizeof(struct cs35l35_platform_data), 13906387f866SBrian Austin GFP_KERNEL); 13916387f866SBrian Austin if (!pdata) 13926387f866SBrian Austin return -ENOMEM; 13936387f866SBrian Austin if (i2c_client->dev.of_node) { 13946387f866SBrian Austin ret = cs35l35_handle_of_data(i2c_client, pdata); 13956387f866SBrian Austin if (ret != 0) 13966387f866SBrian Austin return ret; 13976387f866SBrian Austin 13986387f866SBrian Austin } 13996387f866SBrian Austin cs35l35->pdata = *pdata; 14006387f866SBrian Austin } 14016387f866SBrian Austin 14026387f866SBrian Austin ret = regulator_bulk_enable(cs35l35->num_supplies, 14036387f866SBrian Austin cs35l35->supplies); 14046387f866SBrian Austin if (ret != 0) { 14056387f866SBrian Austin dev_err(&i2c_client->dev, 14066387f866SBrian Austin "Failed to enable core supplies: %d\n", 14076387f866SBrian Austin ret); 14086387f866SBrian Austin return ret; 14096387f866SBrian Austin } 14106387f866SBrian Austin 14116387f866SBrian Austin /* returning NULL can be valid if in stereo mode */ 14126387f866SBrian Austin cs35l35->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, 14136387f866SBrian Austin "reset", GPIOD_OUT_LOW); 14146387f866SBrian Austin if (IS_ERR(cs35l35->reset_gpio)) { 14156387f866SBrian Austin ret = PTR_ERR(cs35l35->reset_gpio); 14166387f866SBrian Austin if (ret == -EBUSY) { 14176387f866SBrian Austin dev_info(&i2c_client->dev, 14186387f866SBrian Austin "Reset line busy, assuming shared reset\n"); 14196387f866SBrian Austin cs35l35->reset_gpio = NULL; 14206387f866SBrian Austin } else { 14216387f866SBrian Austin dev_err(&i2c_client->dev, 14226387f866SBrian Austin "Failed to get reset GPIO: %d\n", ret); 14236387f866SBrian Austin goto err; 14246387f866SBrian Austin } 14256387f866SBrian Austin } 14266387f866SBrian Austin 14276387f866SBrian Austin gpiod_set_value_cansleep(cs35l35->reset_gpio, 1); 14286387f866SBrian Austin 14296387f866SBrian Austin init_completion(&cs35l35->pdn_done); 14306387f866SBrian Austin 14316387f866SBrian Austin ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL, 14326387f866SBrian Austin cs35l35_irq, IRQF_ONESHOT | IRQF_TRIGGER_LOW, 14336387f866SBrian Austin "cs35l35", cs35l35); 14346387f866SBrian Austin if (ret != 0) { 14356387f866SBrian Austin dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret); 14366387f866SBrian Austin goto err; 14376387f866SBrian Austin } 14386387f866SBrian Austin /* initialize codec */ 14396387f866SBrian Austin ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_AB, ®); 14406387f866SBrian Austin 14416387f866SBrian Austin devid = (reg & 0xFF) << 12; 14426387f866SBrian Austin ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_CD, ®); 14436387f866SBrian Austin devid |= (reg & 0xFF) << 4; 14446387f866SBrian Austin ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_E, ®); 14456387f866SBrian Austin devid |= (reg & 0xF0) >> 4; 14466387f866SBrian Austin 14476387f866SBrian Austin if (devid != CS35L35_CHIP_ID) { 14486387f866SBrian Austin dev_err(&i2c_client->dev, 14496387f866SBrian Austin "CS35L35 Device ID (%X). Expected ID %X\n", 14506387f866SBrian Austin devid, CS35L35_CHIP_ID); 14516387f866SBrian Austin ret = -ENODEV; 14526387f866SBrian Austin goto err; 14536387f866SBrian Austin } 14546387f866SBrian Austin 14556387f866SBrian Austin ret = regmap_read(cs35l35->regmap, CS35L35_REV_ID, ®); 14566387f866SBrian Austin if (ret < 0) { 14576387f866SBrian Austin dev_err(&i2c_client->dev, "Get Revision ID failed: %d\n", ret); 14586387f866SBrian Austin goto err; 14596387f866SBrian Austin } 14606387f866SBrian Austin 14616387f866SBrian Austin ret = regmap_register_patch(cs35l35->regmap, cs35l35_errata_patch, 14626387f866SBrian Austin ARRAY_SIZE(cs35l35_errata_patch)); 14636387f866SBrian Austin if (ret < 0) { 14646387f866SBrian Austin dev_err(&i2c_client->dev, "Failed to apply errata patch: %d\n", 14656387f866SBrian Austin ret); 14666387f866SBrian Austin goto err; 14676387f866SBrian Austin } 14686387f866SBrian Austin 14696387f866SBrian Austin dev_info(&i2c_client->dev, 14706387f866SBrian Austin "Cirrus Logic CS35L35 (%x), Revision: %02X\n", devid, 14716387f866SBrian Austin ret & 0xFF); 14726387f866SBrian Austin 14736387f866SBrian Austin /* Set the INT Masks for critical errors */ 14746387f866SBrian Austin regmap_write(cs35l35->regmap, CS35L35_INT_MASK_1, 14756387f866SBrian Austin CS35L35_INT1_CRIT_MASK); 14766387f866SBrian Austin regmap_write(cs35l35->regmap, CS35L35_INT_MASK_2, 14776387f866SBrian Austin CS35L35_INT2_CRIT_MASK); 14786387f866SBrian Austin regmap_write(cs35l35->regmap, CS35L35_INT_MASK_3, 14796387f866SBrian Austin CS35L35_INT3_CRIT_MASK); 14806387f866SBrian Austin regmap_write(cs35l35->regmap, CS35L35_INT_MASK_4, 14816387f866SBrian Austin CS35L35_INT4_CRIT_MASK); 14826387f866SBrian Austin 14836387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, 14846387f866SBrian Austin CS35L35_PWR2_PDN_MASK, 14856387f866SBrian Austin CS35L35_PWR2_PDN_MASK); 14866387f866SBrian Austin 14876387f866SBrian Austin if (cs35l35->pdata.bst_pdn_fet_on) 14886387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, 14896387f866SBrian Austin CS35L35_PDN_BST_MASK, 14906387f866SBrian Austin 1 << CS35L35_PDN_BST_FETON_SHIFT); 14916387f866SBrian Austin else 14926387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, 14936387f866SBrian Austin CS35L35_PDN_BST_MASK, 14946387f866SBrian Austin 1 << CS35L35_PDN_BST_FETOFF_SHIFT); 14956387f866SBrian Austin 14966387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL3, 14976387f866SBrian Austin CS35L35_PWR3_PDN_MASK, 14986387f866SBrian Austin CS35L35_PWR3_PDN_MASK); 14996387f866SBrian Austin 15006387f866SBrian Austin regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL, 15016387f866SBrian Austin CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT); 15026387f866SBrian Austin 15036387f866SBrian Austin ret = snd_soc_register_codec(&i2c_client->dev, 15046387f866SBrian Austin &soc_codec_dev_cs35l35, cs35l35_dai, 15056387f866SBrian Austin ARRAY_SIZE(cs35l35_dai)); 15066387f866SBrian Austin if (ret < 0) { 15076387f866SBrian Austin dev_err(&i2c_client->dev, 15086387f866SBrian Austin "Failed to register codec: %d\n", ret); 15096387f866SBrian Austin goto err; 15106387f866SBrian Austin } 15116387f866SBrian Austin 1512*1bb06adaSCharles Keepax return 0; 1513*1bb06adaSCharles Keepax 15146387f866SBrian Austin err: 15156387f866SBrian Austin regulator_bulk_disable(cs35l35->num_supplies, 15166387f866SBrian Austin cs35l35->supplies); 15176387f866SBrian Austin gpiod_set_value_cansleep(cs35l35->reset_gpio, 0); 15186387f866SBrian Austin 15196387f866SBrian Austin return ret; 15206387f866SBrian Austin } 15216387f866SBrian Austin 15226387f866SBrian Austin static int cs35l35_i2c_remove(struct i2c_client *client) 15236387f866SBrian Austin { 15246387f866SBrian Austin snd_soc_unregister_codec(&client->dev); 15256387f866SBrian Austin return 0; 15266387f866SBrian Austin } 15276387f866SBrian Austin 15286387f866SBrian Austin static const struct of_device_id cs35l35_of_match[] = { 15296387f866SBrian Austin {.compatible = "cirrus,cs35l35"}, 15306387f866SBrian Austin {}, 15316387f866SBrian Austin }; 15326387f866SBrian Austin MODULE_DEVICE_TABLE(of, cs35l35_of_match); 15336387f866SBrian Austin 15346387f866SBrian Austin static const struct i2c_device_id cs35l35_id[] = { 15356387f866SBrian Austin {"cs35l35", 0}, 15366387f866SBrian Austin {} 15376387f866SBrian Austin }; 15386387f866SBrian Austin 15396387f866SBrian Austin MODULE_DEVICE_TABLE(i2c, cs35l35_id); 15406387f866SBrian Austin 15416387f866SBrian Austin static struct i2c_driver cs35l35_i2c_driver = { 15426387f866SBrian Austin .driver = { 15436387f866SBrian Austin .name = "cs35l35", 15446387f866SBrian Austin .of_match_table = cs35l35_of_match, 15456387f866SBrian Austin }, 15466387f866SBrian Austin .id_table = cs35l35_id, 15476387f866SBrian Austin .probe = cs35l35_i2c_probe, 15486387f866SBrian Austin .remove = cs35l35_i2c_remove, 15496387f866SBrian Austin }; 15506387f866SBrian Austin 15516387f866SBrian Austin module_i2c_driver(cs35l35_i2c_driver); 15526387f866SBrian Austin 15536387f866SBrian Austin MODULE_DESCRIPTION("ASoC CS35L35 driver"); 15546387f866SBrian Austin MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>"); 15556387f866SBrian Austin MODULE_LICENSE("GPL"); 1556