162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ALSA SoC Texas Instruments TAS6424 Quad-Channel Audio Amplifier 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016-2017 Texas Instruments Incorporated - https://www.ti.com/ 662306a36Sopenharmony_ci * Author: Andreas Dannenberg <dannenberg@ti.com> 762306a36Sopenharmony_ci * Andrew F. Davis <afd@ti.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/i2c.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <sound/pcm.h> 2162306a36Sopenharmony_ci#include <sound/pcm_params.h> 2262306a36Sopenharmony_ci#include <sound/soc.h> 2362306a36Sopenharmony_ci#include <sound/soc-dapm.h> 2462306a36Sopenharmony_ci#include <sound/tlv.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "tas6424.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* Define how often to check (and clear) the fault status register (in ms) */ 2962306a36Sopenharmony_ci#define TAS6424_FAULT_CHECK_INTERVAL 200 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic const char * const tas6424_supply_names[] = { 3262306a36Sopenharmony_ci "dvdd", /* Digital power supply. Connect to 3.3-V supply. */ 3362306a36Sopenharmony_ci "vbat", /* Supply used for higher voltage analog circuits. */ 3462306a36Sopenharmony_ci "pvdd", /* Class-D amp output FETs supply. */ 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci#define TAS6424_NUM_SUPPLIES ARRAY_SIZE(tas6424_supply_names) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct tas6424_data { 3962306a36Sopenharmony_ci struct device *dev; 4062306a36Sopenharmony_ci struct regmap *regmap; 4162306a36Sopenharmony_ci struct regulator_bulk_data supplies[TAS6424_NUM_SUPPLIES]; 4262306a36Sopenharmony_ci struct delayed_work fault_check_work; 4362306a36Sopenharmony_ci unsigned int last_cfault; 4462306a36Sopenharmony_ci unsigned int last_fault1; 4562306a36Sopenharmony_ci unsigned int last_fault2; 4662306a36Sopenharmony_ci unsigned int last_warn; 4762306a36Sopenharmony_ci struct gpio_desc *standby_gpio; 4862306a36Sopenharmony_ci struct gpio_desc *mute_gpio; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that 5362306a36Sopenharmony_ci * setting the gain below -100 dB (register value <0x7) is effectively a MUTE 5462306a36Sopenharmony_ci * as per device datasheet. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic const struct snd_kcontrol_new tas6424_snd_controls[] = { 5962306a36Sopenharmony_ci SOC_SINGLE_TLV("Speaker Driver CH1 Playback Volume", 6062306a36Sopenharmony_ci TAS6424_CH1_VOL_CTRL, 0, 0xff, 0, dac_tlv), 6162306a36Sopenharmony_ci SOC_SINGLE_TLV("Speaker Driver CH2 Playback Volume", 6262306a36Sopenharmony_ci TAS6424_CH2_VOL_CTRL, 0, 0xff, 0, dac_tlv), 6362306a36Sopenharmony_ci SOC_SINGLE_TLV("Speaker Driver CH3 Playback Volume", 6462306a36Sopenharmony_ci TAS6424_CH3_VOL_CTRL, 0, 0xff, 0, dac_tlv), 6562306a36Sopenharmony_ci SOC_SINGLE_TLV("Speaker Driver CH4 Playback Volume", 6662306a36Sopenharmony_ci TAS6424_CH4_VOL_CTRL, 0, 0xff, 0, dac_tlv), 6762306a36Sopenharmony_ci SOC_SINGLE_STROBE("Auto Diagnostics Switch", TAS6424_DC_DIAG_CTRL1, 6862306a36Sopenharmony_ci TAS6424_LDGBYPASS_SHIFT, 1), 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int tas6424_dac_event(struct snd_soc_dapm_widget *w, 7262306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 7562306a36Sopenharmony_ci struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci dev_dbg(component->dev, "%s() event=0x%0x\n", __func__, event); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (event & SND_SOC_DAPM_POST_PMU) { 8062306a36Sopenharmony_ci /* Observe codec shutdown-to-active time */ 8162306a36Sopenharmony_ci msleep(12); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Turn on TAS6424 periodic fault checking/handling */ 8462306a36Sopenharmony_ci tas6424->last_fault1 = 0; 8562306a36Sopenharmony_ci tas6424->last_fault2 = 0; 8662306a36Sopenharmony_ci tas6424->last_warn = 0; 8762306a36Sopenharmony_ci schedule_delayed_work(&tas6424->fault_check_work, 8862306a36Sopenharmony_ci msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL)); 8962306a36Sopenharmony_ci } else if (event & SND_SOC_DAPM_PRE_PMD) { 9062306a36Sopenharmony_ci /* Disable TAS6424 periodic fault checking/handling */ 9162306a36Sopenharmony_ci cancel_delayed_work_sync(&tas6424->fault_check_work); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget tas6424_dapm_widgets[] = { 9862306a36Sopenharmony_ci SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0), 9962306a36Sopenharmony_ci SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas6424_dac_event, 10062306a36Sopenharmony_ci SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), 10162306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("OUT") 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic const struct snd_soc_dapm_route tas6424_audio_map[] = { 10562306a36Sopenharmony_ci { "DAC", NULL, "DAC IN" }, 10662306a36Sopenharmony_ci { "OUT", NULL, "DAC" }, 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int tas6424_hw_params(struct snd_pcm_substream *substream, 11062306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 11162306a36Sopenharmony_ci struct snd_soc_dai *dai) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 11462306a36Sopenharmony_ci unsigned int rate = params_rate(params); 11562306a36Sopenharmony_ci unsigned int width = params_width(params); 11662306a36Sopenharmony_ci u8 sap_ctrl = 0; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci dev_dbg(component->dev, "%s() rate=%u width=%u\n", __func__, rate, width); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci switch (rate) { 12162306a36Sopenharmony_ci case 44100: 12262306a36Sopenharmony_ci sap_ctrl |= TAS6424_SAP_RATE_44100; 12362306a36Sopenharmony_ci break; 12462306a36Sopenharmony_ci case 48000: 12562306a36Sopenharmony_ci sap_ctrl |= TAS6424_SAP_RATE_48000; 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci case 96000: 12862306a36Sopenharmony_ci sap_ctrl |= TAS6424_SAP_RATE_96000; 12962306a36Sopenharmony_ci break; 13062306a36Sopenharmony_ci default: 13162306a36Sopenharmony_ci dev_err(component->dev, "unsupported sample rate: %u\n", rate); 13262306a36Sopenharmony_ci return -EINVAL; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci switch (width) { 13662306a36Sopenharmony_ci case 16: 13762306a36Sopenharmony_ci sap_ctrl |= TAS6424_SAP_TDM_SLOT_SZ_16; 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci case 24: 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci default: 14262306a36Sopenharmony_ci dev_err(component->dev, "unsupported sample width: %u\n", width); 14362306a36Sopenharmony_ci return -EINVAL; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci snd_soc_component_update_bits(component, TAS6424_SAP_CTRL, 14762306a36Sopenharmony_ci TAS6424_SAP_RATE_MASK | 14862306a36Sopenharmony_ci TAS6424_SAP_TDM_SLOT_SZ_16, 14962306a36Sopenharmony_ci sap_ctrl); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int tas6424_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 15762306a36Sopenharmony_ci u8 serial_format = 0; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci dev_dbg(component->dev, "%s() fmt=0x%0x\n", __func__, fmt); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* clock masters */ 16262306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 16362306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBC_CFC: 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci default: 16662306a36Sopenharmony_ci dev_err(component->dev, "Invalid DAI clocking\n"); 16762306a36Sopenharmony_ci return -EINVAL; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* signal polarity */ 17162306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 17262306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 17362306a36Sopenharmony_ci break; 17462306a36Sopenharmony_ci default: 17562306a36Sopenharmony_ci dev_err(component->dev, "Invalid DAI clock signal polarity\n"); 17662306a36Sopenharmony_ci return -EINVAL; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* interface format */ 18062306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 18162306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 18262306a36Sopenharmony_ci serial_format |= TAS6424_SAP_I2S; 18362306a36Sopenharmony_ci break; 18462306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 18562306a36Sopenharmony_ci serial_format |= TAS6424_SAP_DSP; 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * We can use the fact that the TAS6424 does not care about the 19062306a36Sopenharmony_ci * LRCLK duty cycle during TDM to receive DSP_B formatted data 19162306a36Sopenharmony_ci * in LEFTJ mode (no delaying of the 1st data bit). 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci serial_format |= TAS6424_SAP_LEFTJ; 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 19662306a36Sopenharmony_ci serial_format |= TAS6424_SAP_LEFTJ; 19762306a36Sopenharmony_ci break; 19862306a36Sopenharmony_ci default: 19962306a36Sopenharmony_ci dev_err(component->dev, "Invalid DAI interface format\n"); 20062306a36Sopenharmony_ci return -EINVAL; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci snd_soc_component_update_bits(component, TAS6424_SAP_CTRL, 20462306a36Sopenharmony_ci TAS6424_SAP_FMT_MASK, serial_format); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int tas6424_set_dai_tdm_slot(struct snd_soc_dai *dai, 21062306a36Sopenharmony_ci unsigned int tx_mask, unsigned int rx_mask, 21162306a36Sopenharmony_ci int slots, int slot_width) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 21462306a36Sopenharmony_ci unsigned int first_slot, last_slot; 21562306a36Sopenharmony_ci bool sap_tdm_slot_last; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci dev_dbg(component->dev, "%s() tx_mask=%d rx_mask=%d\n", __func__, 21862306a36Sopenharmony_ci tx_mask, rx_mask); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (!tx_mask || !rx_mask) 22162306a36Sopenharmony_ci return 0; /* nothing needed to disable TDM mode */ 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* 22462306a36Sopenharmony_ci * Determine the first slot and last slot that is being requested so 22562306a36Sopenharmony_ci * we'll be able to more easily enforce certain constraints as the 22662306a36Sopenharmony_ci * TAS6424's TDM interface is not fully configurable. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci first_slot = __ffs(tx_mask); 22962306a36Sopenharmony_ci last_slot = __fls(rx_mask); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (last_slot - first_slot != 4) { 23262306a36Sopenharmony_ci dev_err(component->dev, "tdm mask must cover 4 contiguous slots\n"); 23362306a36Sopenharmony_ci return -EINVAL; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci switch (first_slot) { 23762306a36Sopenharmony_ci case 0: 23862306a36Sopenharmony_ci sap_tdm_slot_last = false; 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci case 4: 24162306a36Sopenharmony_ci sap_tdm_slot_last = true; 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci default: 24462306a36Sopenharmony_ci dev_err(component->dev, "tdm mask must start at slot 0 or 4\n"); 24562306a36Sopenharmony_ci return -EINVAL; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci snd_soc_component_update_bits(component, TAS6424_SAP_CTRL, TAS6424_SAP_TDM_SLOT_LAST, 24962306a36Sopenharmony_ci sap_tdm_slot_last ? TAS6424_SAP_TDM_SLOT_LAST : 0); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return 0; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int tas6424_mute(struct snd_soc_dai *dai, int mute, int direction) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 25762306a36Sopenharmony_ci struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component); 25862306a36Sopenharmony_ci unsigned int val; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci dev_dbg(component->dev, "%s() mute=%d\n", __func__, mute); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (tas6424->mute_gpio) { 26362306a36Sopenharmony_ci gpiod_set_value_cansleep(tas6424->mute_gpio, mute); 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (mute) 26862306a36Sopenharmony_ci val = TAS6424_ALL_STATE_MUTE; 26962306a36Sopenharmony_ci else 27062306a36Sopenharmony_ci val = TAS6424_ALL_STATE_PLAY; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, val); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic int tas6424_power_off(struct snd_soc_component *component) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component); 28062306a36Sopenharmony_ci int ret; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, TAS6424_ALL_STATE_HIZ); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci regcache_cache_only(tas6424->regmap, true); 28562306a36Sopenharmony_ci regcache_mark_dirty(tas6424->regmap); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), 28862306a36Sopenharmony_ci tas6424->supplies); 28962306a36Sopenharmony_ci if (ret < 0) { 29062306a36Sopenharmony_ci dev_err(component->dev, "failed to disable supplies: %d\n", ret); 29162306a36Sopenharmony_ci return ret; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic int tas6424_power_on(struct snd_soc_component *component) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component); 30062306a36Sopenharmony_ci int ret; 30162306a36Sopenharmony_ci u8 chan_states; 30262306a36Sopenharmony_ci int no_auto_diags = 0; 30362306a36Sopenharmony_ci unsigned int reg_val; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (!regmap_read(tas6424->regmap, TAS6424_DC_DIAG_CTRL1, ®_val)) 30662306a36Sopenharmony_ci no_auto_diags = reg_val & TAS6424_LDGBYPASS_MASK; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies), 30962306a36Sopenharmony_ci tas6424->supplies); 31062306a36Sopenharmony_ci if (ret < 0) { 31162306a36Sopenharmony_ci dev_err(component->dev, "failed to enable supplies: %d\n", ret); 31262306a36Sopenharmony_ci return ret; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci regcache_cache_only(tas6424->regmap, false); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ret = regcache_sync(tas6424->regmap); 31862306a36Sopenharmony_ci if (ret < 0) { 31962306a36Sopenharmony_ci dev_err(component->dev, "failed to sync regcache: %d\n", ret); 32062306a36Sopenharmony_ci return ret; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (tas6424->mute_gpio) { 32462306a36Sopenharmony_ci gpiod_set_value_cansleep(tas6424->mute_gpio, 0); 32562306a36Sopenharmony_ci /* 32662306a36Sopenharmony_ci * channels are muted via the mute pin. Don't also mute 32762306a36Sopenharmony_ci * them via the registers so that subsequent register 32862306a36Sopenharmony_ci * access is not necessary to un-mute the channels 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_ci chan_states = TAS6424_ALL_STATE_PLAY; 33162306a36Sopenharmony_ci } else { 33262306a36Sopenharmony_ci chan_states = TAS6424_ALL_STATE_MUTE; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, chan_states); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* any time we come out of HIZ, the output channels automatically run DC 33762306a36Sopenharmony_ci * load diagnostics if autodiagnotics are enabled. wait here until this 33862306a36Sopenharmony_ci * completes. 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci if (!no_auto_diags) 34162306a36Sopenharmony_ci msleep(230); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int tas6424_set_bias_level(struct snd_soc_component *component, 34762306a36Sopenharmony_ci enum snd_soc_bias_level level) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci dev_dbg(component->dev, "%s() level=%d\n", __func__, level); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci switch (level) { 35262306a36Sopenharmony_ci case SND_SOC_BIAS_ON: 35362306a36Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 35462306a36Sopenharmony_ci break; 35562306a36Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 35662306a36Sopenharmony_ci if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) 35762306a36Sopenharmony_ci tas6424_power_on(component); 35862306a36Sopenharmony_ci break; 35962306a36Sopenharmony_ci case SND_SOC_BIAS_OFF: 36062306a36Sopenharmony_ci tas6424_power_off(component); 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic struct snd_soc_component_driver soc_codec_dev_tas6424 = { 36862306a36Sopenharmony_ci .set_bias_level = tas6424_set_bias_level, 36962306a36Sopenharmony_ci .controls = tas6424_snd_controls, 37062306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(tas6424_snd_controls), 37162306a36Sopenharmony_ci .dapm_widgets = tas6424_dapm_widgets, 37262306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(tas6424_dapm_widgets), 37362306a36Sopenharmony_ci .dapm_routes = tas6424_audio_map, 37462306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(tas6424_audio_map), 37562306a36Sopenharmony_ci .use_pmdown_time = 1, 37662306a36Sopenharmony_ci .endianness = 1, 37762306a36Sopenharmony_ci}; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic const struct snd_soc_dai_ops tas6424_speaker_dai_ops = { 38062306a36Sopenharmony_ci .hw_params = tas6424_hw_params, 38162306a36Sopenharmony_ci .set_fmt = tas6424_set_dai_fmt, 38262306a36Sopenharmony_ci .set_tdm_slot = tas6424_set_dai_tdm_slot, 38362306a36Sopenharmony_ci .mute_stream = tas6424_mute, 38462306a36Sopenharmony_ci .no_capture_mute = 1, 38562306a36Sopenharmony_ci}; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic struct snd_soc_dai_driver tas6424_dai[] = { 38862306a36Sopenharmony_ci { 38962306a36Sopenharmony_ci .name = "tas6424-amplifier", 39062306a36Sopenharmony_ci .playback = { 39162306a36Sopenharmony_ci .stream_name = "Playback", 39262306a36Sopenharmony_ci .channels_min = 1, 39362306a36Sopenharmony_ci .channels_max = 4, 39462306a36Sopenharmony_ci .rates = TAS6424_RATES, 39562306a36Sopenharmony_ci .formats = TAS6424_FORMATS, 39662306a36Sopenharmony_ci }, 39762306a36Sopenharmony_ci .ops = &tas6424_speaker_dai_ops, 39862306a36Sopenharmony_ci }, 39962306a36Sopenharmony_ci}; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void tas6424_fault_check_work(struct work_struct *work) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct tas6424_data *tas6424 = container_of(work, struct tas6424_data, 40462306a36Sopenharmony_ci fault_check_work.work); 40562306a36Sopenharmony_ci struct device *dev = tas6424->dev; 40662306a36Sopenharmony_ci unsigned int reg; 40762306a36Sopenharmony_ci int ret; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci ret = regmap_read(tas6424->regmap, TAS6424_CHANNEL_FAULT, ®); 41062306a36Sopenharmony_ci if (ret < 0) { 41162306a36Sopenharmony_ci dev_err(dev, "failed to read CHANNEL_FAULT register: %d\n", ret); 41262306a36Sopenharmony_ci goto out; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (!reg) { 41662306a36Sopenharmony_ci tas6424->last_cfault = reg; 41762306a36Sopenharmony_ci goto check_global_fault1_reg; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* 42162306a36Sopenharmony_ci * Only flag errors once for a given occurrence. This is needed as 42262306a36Sopenharmony_ci * the TAS6424 will take time clearing the fault condition internally 42362306a36Sopenharmony_ci * during which we don't want to bombard the system with the same 42462306a36Sopenharmony_ci * error message over and over. 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_OC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH1)) 42762306a36Sopenharmony_ci dev_crit(dev, "experienced a channel 1 overcurrent fault\n"); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_OC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH2)) 43062306a36Sopenharmony_ci dev_crit(dev, "experienced a channel 2 overcurrent fault\n"); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_OC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH3)) 43362306a36Sopenharmony_ci dev_crit(dev, "experienced a channel 3 overcurrent fault\n"); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_OC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH4)) 43662306a36Sopenharmony_ci dev_crit(dev, "experienced a channel 4 overcurrent fault\n"); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_DC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH1)) 43962306a36Sopenharmony_ci dev_crit(dev, "experienced a channel 1 DC fault\n"); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_DC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH2)) 44262306a36Sopenharmony_ci dev_crit(dev, "experienced a channel 2 DC fault\n"); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_DC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH3)) 44562306a36Sopenharmony_ci dev_crit(dev, "experienced a channel 3 DC fault\n"); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_DC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH4)) 44862306a36Sopenharmony_ci dev_crit(dev, "experienced a channel 4 DC fault\n"); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Store current fault1 value so we can detect any changes next time */ 45162306a36Sopenharmony_ci tas6424->last_cfault = reg; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cicheck_global_fault1_reg: 45462306a36Sopenharmony_ci ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT1, ®); 45562306a36Sopenharmony_ci if (ret < 0) { 45662306a36Sopenharmony_ci dev_err(dev, "failed to read GLOB_FAULT1 register: %d\n", ret); 45762306a36Sopenharmony_ci goto out; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* 46162306a36Sopenharmony_ci * Ignore any clock faults as there is no clean way to check for them. 46262306a36Sopenharmony_ci * We would need to start checking for those faults *after* the SAIF 46362306a36Sopenharmony_ci * stream has been setup, and stop checking *before* the stream is 46462306a36Sopenharmony_ci * stopped to avoid any false-positives. However there are no 46562306a36Sopenharmony_ci * appropriate hooks to monitor these events. 46662306a36Sopenharmony_ci */ 46762306a36Sopenharmony_ci reg &= TAS6424_FAULT_PVDD_OV | 46862306a36Sopenharmony_ci TAS6424_FAULT_VBAT_OV | 46962306a36Sopenharmony_ci TAS6424_FAULT_PVDD_UV | 47062306a36Sopenharmony_ci TAS6424_FAULT_VBAT_UV; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (!reg) { 47362306a36Sopenharmony_ci tas6424->last_fault1 = reg; 47462306a36Sopenharmony_ci goto check_global_fault2_reg; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_PVDD_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_OV)) 47862306a36Sopenharmony_ci dev_crit(dev, "experienced a PVDD overvoltage fault\n"); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_VBAT_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_OV)) 48162306a36Sopenharmony_ci dev_crit(dev, "experienced a VBAT overvoltage fault\n"); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_PVDD_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_UV)) 48462306a36Sopenharmony_ci dev_crit(dev, "experienced a PVDD undervoltage fault\n"); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_VBAT_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_UV)) 48762306a36Sopenharmony_ci dev_crit(dev, "experienced a VBAT undervoltage fault\n"); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* Store current fault1 value so we can detect any changes next time */ 49062306a36Sopenharmony_ci tas6424->last_fault1 = reg; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cicheck_global_fault2_reg: 49362306a36Sopenharmony_ci ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT2, ®); 49462306a36Sopenharmony_ci if (ret < 0) { 49562306a36Sopenharmony_ci dev_err(dev, "failed to read GLOB_FAULT2 register: %d\n", ret); 49662306a36Sopenharmony_ci goto out; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci reg &= TAS6424_FAULT_OTSD | 50062306a36Sopenharmony_ci TAS6424_FAULT_OTSD_CH1 | 50162306a36Sopenharmony_ci TAS6424_FAULT_OTSD_CH2 | 50262306a36Sopenharmony_ci TAS6424_FAULT_OTSD_CH3 | 50362306a36Sopenharmony_ci TAS6424_FAULT_OTSD_CH4; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (!reg) { 50662306a36Sopenharmony_ci tas6424->last_fault2 = reg; 50762306a36Sopenharmony_ci goto check_warn_reg; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_OTSD) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD)) 51162306a36Sopenharmony_ci dev_crit(dev, "experienced a global overtemp shutdown\n"); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_OTSD_CH1) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH1)) 51462306a36Sopenharmony_ci dev_crit(dev, "experienced an overtemp shutdown on CH1\n"); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_OTSD_CH2) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH2)) 51762306a36Sopenharmony_ci dev_crit(dev, "experienced an overtemp shutdown on CH2\n"); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_OTSD_CH3) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH3)) 52062306a36Sopenharmony_ci dev_crit(dev, "experienced an overtemp shutdown on CH3\n"); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if ((reg & TAS6424_FAULT_OTSD_CH4) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH4)) 52362306a36Sopenharmony_ci dev_crit(dev, "experienced an overtemp shutdown on CH4\n"); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* Store current fault2 value so we can detect any changes next time */ 52662306a36Sopenharmony_ci tas6424->last_fault2 = reg; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cicheck_warn_reg: 52962306a36Sopenharmony_ci ret = regmap_read(tas6424->regmap, TAS6424_WARN, ®); 53062306a36Sopenharmony_ci if (ret < 0) { 53162306a36Sopenharmony_ci dev_err(dev, "failed to read WARN register: %d\n", ret); 53262306a36Sopenharmony_ci goto out; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci reg &= TAS6424_WARN_VDD_UV | 53662306a36Sopenharmony_ci TAS6424_WARN_VDD_POR | 53762306a36Sopenharmony_ci TAS6424_WARN_VDD_OTW | 53862306a36Sopenharmony_ci TAS6424_WARN_VDD_OTW_CH1 | 53962306a36Sopenharmony_ci TAS6424_WARN_VDD_OTW_CH2 | 54062306a36Sopenharmony_ci TAS6424_WARN_VDD_OTW_CH3 | 54162306a36Sopenharmony_ci TAS6424_WARN_VDD_OTW_CH4; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (!reg) { 54462306a36Sopenharmony_ci tas6424->last_warn = reg; 54562306a36Sopenharmony_ci goto out; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if ((reg & TAS6424_WARN_VDD_UV) && !(tas6424->last_warn & TAS6424_WARN_VDD_UV)) 54962306a36Sopenharmony_ci dev_warn(dev, "experienced a VDD under voltage condition\n"); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if ((reg & TAS6424_WARN_VDD_POR) && !(tas6424->last_warn & TAS6424_WARN_VDD_POR)) 55262306a36Sopenharmony_ci dev_warn(dev, "experienced a VDD POR condition\n"); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if ((reg & TAS6424_WARN_VDD_OTW) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW)) 55562306a36Sopenharmony_ci dev_warn(dev, "experienced a global overtemp warning\n"); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if ((reg & TAS6424_WARN_VDD_OTW_CH1) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH1)) 55862306a36Sopenharmony_ci dev_warn(dev, "experienced an overtemp warning on CH1\n"); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if ((reg & TAS6424_WARN_VDD_OTW_CH2) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH2)) 56162306a36Sopenharmony_ci dev_warn(dev, "experienced an overtemp warning on CH2\n"); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if ((reg & TAS6424_WARN_VDD_OTW_CH3) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH3)) 56462306a36Sopenharmony_ci dev_warn(dev, "experienced an overtemp warning on CH3\n"); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if ((reg & TAS6424_WARN_VDD_OTW_CH4) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH4)) 56762306a36Sopenharmony_ci dev_warn(dev, "experienced an overtemp warning on CH4\n"); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* Store current warn value so we can detect any changes next time */ 57062306a36Sopenharmony_ci tas6424->last_warn = reg; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* Clear any warnings by toggling the CLEAR_FAULT control bit */ 57362306a36Sopenharmony_ci ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3, 57462306a36Sopenharmony_ci TAS6424_CLEAR_FAULT, TAS6424_CLEAR_FAULT); 57562306a36Sopenharmony_ci if (ret < 0) 57662306a36Sopenharmony_ci dev_err(dev, "failed to write MISC_CTRL3 register: %d\n", ret); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3, 57962306a36Sopenharmony_ci TAS6424_CLEAR_FAULT, 0); 58062306a36Sopenharmony_ci if (ret < 0) 58162306a36Sopenharmony_ci dev_err(dev, "failed to write MISC_CTRL3 register: %d\n", ret); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ciout: 58462306a36Sopenharmony_ci /* Schedule the next fault check at the specified interval */ 58562306a36Sopenharmony_ci schedule_delayed_work(&tas6424->fault_check_work, 58662306a36Sopenharmony_ci msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL)); 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic const struct reg_default tas6424_reg_defaults[] = { 59062306a36Sopenharmony_ci { TAS6424_MODE_CTRL, 0x00 }, 59162306a36Sopenharmony_ci { TAS6424_MISC_CTRL1, 0x32 }, 59262306a36Sopenharmony_ci { TAS6424_MISC_CTRL2, 0x62 }, 59362306a36Sopenharmony_ci { TAS6424_SAP_CTRL, 0x04 }, 59462306a36Sopenharmony_ci { TAS6424_CH_STATE_CTRL, 0x55 }, 59562306a36Sopenharmony_ci { TAS6424_CH1_VOL_CTRL, 0xcf }, 59662306a36Sopenharmony_ci { TAS6424_CH2_VOL_CTRL, 0xcf }, 59762306a36Sopenharmony_ci { TAS6424_CH3_VOL_CTRL, 0xcf }, 59862306a36Sopenharmony_ci { TAS6424_CH4_VOL_CTRL, 0xcf }, 59962306a36Sopenharmony_ci { TAS6424_DC_DIAG_CTRL1, 0x00 }, 60062306a36Sopenharmony_ci { TAS6424_DC_DIAG_CTRL2, 0x11 }, 60162306a36Sopenharmony_ci { TAS6424_DC_DIAG_CTRL3, 0x11 }, 60262306a36Sopenharmony_ci { TAS6424_PIN_CTRL, 0xff }, 60362306a36Sopenharmony_ci { TAS6424_AC_DIAG_CTRL1, 0x00 }, 60462306a36Sopenharmony_ci { TAS6424_MISC_CTRL3, 0x00 }, 60562306a36Sopenharmony_ci { TAS6424_CLIP_CTRL, 0x01 }, 60662306a36Sopenharmony_ci { TAS6424_CLIP_WINDOW, 0x14 }, 60762306a36Sopenharmony_ci { TAS6424_CLIP_WARN, 0x00 }, 60862306a36Sopenharmony_ci { TAS6424_CBC_STAT, 0x00 }, 60962306a36Sopenharmony_ci { TAS6424_MISC_CTRL4, 0x40 }, 61062306a36Sopenharmony_ci}; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic bool tas6424_is_writable_reg(struct device *dev, unsigned int reg) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci switch (reg) { 61562306a36Sopenharmony_ci case TAS6424_MODE_CTRL: 61662306a36Sopenharmony_ci case TAS6424_MISC_CTRL1: 61762306a36Sopenharmony_ci case TAS6424_MISC_CTRL2: 61862306a36Sopenharmony_ci case TAS6424_SAP_CTRL: 61962306a36Sopenharmony_ci case TAS6424_CH_STATE_CTRL: 62062306a36Sopenharmony_ci case TAS6424_CH1_VOL_CTRL: 62162306a36Sopenharmony_ci case TAS6424_CH2_VOL_CTRL: 62262306a36Sopenharmony_ci case TAS6424_CH3_VOL_CTRL: 62362306a36Sopenharmony_ci case TAS6424_CH4_VOL_CTRL: 62462306a36Sopenharmony_ci case TAS6424_DC_DIAG_CTRL1: 62562306a36Sopenharmony_ci case TAS6424_DC_DIAG_CTRL2: 62662306a36Sopenharmony_ci case TAS6424_DC_DIAG_CTRL3: 62762306a36Sopenharmony_ci case TAS6424_PIN_CTRL: 62862306a36Sopenharmony_ci case TAS6424_AC_DIAG_CTRL1: 62962306a36Sopenharmony_ci case TAS6424_MISC_CTRL3: 63062306a36Sopenharmony_ci case TAS6424_CLIP_CTRL: 63162306a36Sopenharmony_ci case TAS6424_CLIP_WINDOW: 63262306a36Sopenharmony_ci case TAS6424_CLIP_WARN: 63362306a36Sopenharmony_ci case TAS6424_CBC_STAT: 63462306a36Sopenharmony_ci case TAS6424_MISC_CTRL4: 63562306a36Sopenharmony_ci return true; 63662306a36Sopenharmony_ci default: 63762306a36Sopenharmony_ci return false; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic bool tas6424_is_volatile_reg(struct device *dev, unsigned int reg) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci switch (reg) { 64462306a36Sopenharmony_ci case TAS6424_DC_LOAD_DIAG_REP12: 64562306a36Sopenharmony_ci case TAS6424_DC_LOAD_DIAG_REP34: 64662306a36Sopenharmony_ci case TAS6424_DC_LOAD_DIAG_REPLO: 64762306a36Sopenharmony_ci case TAS6424_CHANNEL_STATE: 64862306a36Sopenharmony_ci case TAS6424_CHANNEL_FAULT: 64962306a36Sopenharmony_ci case TAS6424_GLOB_FAULT1: 65062306a36Sopenharmony_ci case TAS6424_GLOB_FAULT2: 65162306a36Sopenharmony_ci case TAS6424_WARN: 65262306a36Sopenharmony_ci case TAS6424_AC_LOAD_DIAG_REP1: 65362306a36Sopenharmony_ci case TAS6424_AC_LOAD_DIAG_REP2: 65462306a36Sopenharmony_ci case TAS6424_AC_LOAD_DIAG_REP3: 65562306a36Sopenharmony_ci case TAS6424_AC_LOAD_DIAG_REP4: 65662306a36Sopenharmony_ci return true; 65762306a36Sopenharmony_ci default: 65862306a36Sopenharmony_ci return false; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic const struct regmap_config tas6424_regmap_config = { 66362306a36Sopenharmony_ci .reg_bits = 8, 66462306a36Sopenharmony_ci .val_bits = 8, 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci .writeable_reg = tas6424_is_writable_reg, 66762306a36Sopenharmony_ci .volatile_reg = tas6424_is_volatile_reg, 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci .max_register = TAS6424_MAX, 67062306a36Sopenharmony_ci .reg_defaults = tas6424_reg_defaults, 67162306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(tas6424_reg_defaults), 67262306a36Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 67362306a36Sopenharmony_ci}; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_OF) 67662306a36Sopenharmony_cistatic const struct of_device_id tas6424_of_ids[] = { 67762306a36Sopenharmony_ci { .compatible = "ti,tas6424", }, 67862306a36Sopenharmony_ci { }, 67962306a36Sopenharmony_ci}; 68062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tas6424_of_ids); 68162306a36Sopenharmony_ci#endif 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic int tas6424_i2c_probe(struct i2c_client *client) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct device *dev = &client->dev; 68662306a36Sopenharmony_ci struct tas6424_data *tas6424; 68762306a36Sopenharmony_ci int ret; 68862306a36Sopenharmony_ci int i; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci tas6424 = devm_kzalloc(dev, sizeof(*tas6424), GFP_KERNEL); 69162306a36Sopenharmony_ci if (!tas6424) 69262306a36Sopenharmony_ci return -ENOMEM; 69362306a36Sopenharmony_ci dev_set_drvdata(dev, tas6424); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci tas6424->dev = dev; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci tas6424->regmap = devm_regmap_init_i2c(client, &tas6424_regmap_config); 69862306a36Sopenharmony_ci if (IS_ERR(tas6424->regmap)) { 69962306a36Sopenharmony_ci ret = PTR_ERR(tas6424->regmap); 70062306a36Sopenharmony_ci dev_err(dev, "unable to allocate register map: %d\n", ret); 70162306a36Sopenharmony_ci return ret; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* 70562306a36Sopenharmony_ci * Get control of the standby pin and set it LOW to take the codec 70662306a36Sopenharmony_ci * out of the stand-by mode. 70762306a36Sopenharmony_ci * Note: The actual pin polarity is taken care of in the GPIO lib 70862306a36Sopenharmony_ci * according the polarity specified in the DTS. 70962306a36Sopenharmony_ci */ 71062306a36Sopenharmony_ci tas6424->standby_gpio = devm_gpiod_get_optional(dev, "standby", 71162306a36Sopenharmony_ci GPIOD_OUT_LOW); 71262306a36Sopenharmony_ci if (IS_ERR(tas6424->standby_gpio)) { 71362306a36Sopenharmony_ci if (PTR_ERR(tas6424->standby_gpio) == -EPROBE_DEFER) 71462306a36Sopenharmony_ci return -EPROBE_DEFER; 71562306a36Sopenharmony_ci dev_info(dev, "failed to get standby GPIO: %ld\n", 71662306a36Sopenharmony_ci PTR_ERR(tas6424->standby_gpio)); 71762306a36Sopenharmony_ci tas6424->standby_gpio = NULL; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* 72162306a36Sopenharmony_ci * Get control of the mute pin and set it HIGH in order to start with 72262306a36Sopenharmony_ci * all the output muted. 72362306a36Sopenharmony_ci * Note: The actual pin polarity is taken care of in the GPIO lib 72462306a36Sopenharmony_ci * according the polarity specified in the DTS. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci tas6424->mute_gpio = devm_gpiod_get_optional(dev, "mute", 72762306a36Sopenharmony_ci GPIOD_OUT_HIGH); 72862306a36Sopenharmony_ci if (IS_ERR(tas6424->mute_gpio)) { 72962306a36Sopenharmony_ci if (PTR_ERR(tas6424->mute_gpio) == -EPROBE_DEFER) 73062306a36Sopenharmony_ci return -EPROBE_DEFER; 73162306a36Sopenharmony_ci dev_info(dev, "failed to get nmute GPIO: %ld\n", 73262306a36Sopenharmony_ci PTR_ERR(tas6424->mute_gpio)); 73362306a36Sopenharmony_ci tas6424->mute_gpio = NULL; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tas6424->supplies); i++) 73762306a36Sopenharmony_ci tas6424->supplies[i].supply = tas6424_supply_names[i]; 73862306a36Sopenharmony_ci ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(tas6424->supplies), 73962306a36Sopenharmony_ci tas6424->supplies); 74062306a36Sopenharmony_ci if (ret) { 74162306a36Sopenharmony_ci dev_err(dev, "unable to request supplies: %d\n", ret); 74262306a36Sopenharmony_ci return ret; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies), 74662306a36Sopenharmony_ci tas6424->supplies); 74762306a36Sopenharmony_ci if (ret) { 74862306a36Sopenharmony_ci dev_err(dev, "unable to enable supplies: %d\n", ret); 74962306a36Sopenharmony_ci return ret; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* Reset device to establish well-defined startup state */ 75362306a36Sopenharmony_ci ret = regmap_update_bits(tas6424->regmap, TAS6424_MODE_CTRL, 75462306a36Sopenharmony_ci TAS6424_RESET, TAS6424_RESET); 75562306a36Sopenharmony_ci if (ret) { 75662306a36Sopenharmony_ci dev_err(dev, "unable to reset device: %d\n", ret); 75762306a36Sopenharmony_ci goto disable_regs; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci INIT_DELAYED_WORK(&tas6424->fault_check_work, tas6424_fault_check_work); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci ret = devm_snd_soc_register_component(dev, &soc_codec_dev_tas6424, 76362306a36Sopenharmony_ci tas6424_dai, ARRAY_SIZE(tas6424_dai)); 76462306a36Sopenharmony_ci if (ret < 0) { 76562306a36Sopenharmony_ci dev_err(dev, "unable to register codec: %d\n", ret); 76662306a36Sopenharmony_ci goto disable_regs; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return 0; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cidisable_regs: 77262306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), tas6424->supplies); 77362306a36Sopenharmony_ci return ret; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic void tas6424_i2c_remove(struct i2c_client *client) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci struct device *dev = &client->dev; 77962306a36Sopenharmony_ci struct tas6424_data *tas6424 = dev_get_drvdata(dev); 78062306a36Sopenharmony_ci int ret; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci cancel_delayed_work_sync(&tas6424->fault_check_work); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* put the codec in stand-by */ 78562306a36Sopenharmony_ci if (tas6424->standby_gpio) 78662306a36Sopenharmony_ci gpiod_set_value_cansleep(tas6424->standby_gpio, 1); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), 78962306a36Sopenharmony_ci tas6424->supplies); 79062306a36Sopenharmony_ci if (ret < 0) 79162306a36Sopenharmony_ci dev_err(dev, "unable to disable supplies: %d\n", ret); 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic const struct i2c_device_id tas6424_i2c_ids[] = { 79562306a36Sopenharmony_ci { "tas6424", 0 }, 79662306a36Sopenharmony_ci { } 79762306a36Sopenharmony_ci}; 79862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tas6424_i2c_ids); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic struct i2c_driver tas6424_i2c_driver = { 80162306a36Sopenharmony_ci .driver = { 80262306a36Sopenharmony_ci .name = "tas6424", 80362306a36Sopenharmony_ci .of_match_table = of_match_ptr(tas6424_of_ids), 80462306a36Sopenharmony_ci }, 80562306a36Sopenharmony_ci .probe = tas6424_i2c_probe, 80662306a36Sopenharmony_ci .remove = tas6424_i2c_remove, 80762306a36Sopenharmony_ci .id_table = tas6424_i2c_ids, 80862306a36Sopenharmony_ci}; 80962306a36Sopenharmony_cimodule_i2c_driver(tas6424_i2c_driver); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ciMODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>"); 81262306a36Sopenharmony_ciMODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 81362306a36Sopenharmony_ciMODULE_DESCRIPTION("TAS6424 Audio amplifier driver"); 81462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 815