/* * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that * setting the gain below -100 dB (register value <0x7) is effectively a MUTE * as per device datasheet.
*/ static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0);
/* clock masters */ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_CBC_CFC: break; default:
dev_err(component->dev, "Invalid DAI clocking\n"); return -EINVAL;
}
/* signal polarity */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; default:
dev_err(component->dev, "Invalid DAI clock signal polarity\n"); return -EINVAL;
}
/* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S:
serial_format |= TAS6424_SAP_I2S; break; case SND_SOC_DAIFMT_DSP_A:
serial_format |= TAS6424_SAP_DSP; break; case SND_SOC_DAIFMT_DSP_B: /* * We can use the fact that the TAS6424 does not care about the * LRCLK duty cycle during TDM to receive DSP_B formatted data * in LEFTJ mode (no delaying of the 1st data bit).
*/
serial_format |= TAS6424_SAP_LEFTJ; break; case SND_SOC_DAIFMT_LEFT_J:
serial_format |= TAS6424_SAP_LEFTJ; break; default:
dev_err(component->dev, "Invalid DAI interface format\n"); return -EINVAL;
}
if (!tx_mask || !rx_mask) return 0; /* nothing needed to disable TDM mode */
/* * Determine the first slot and last slot that is being requested so * we'll be able to more easily enforce certain constraints as the * TAS6424's TDM interface is not fully configurable.
*/
first_slot = __ffs(tx_mask);
last_slot = __fls(rx_mask);
if (last_slot - first_slot != 4) {
dev_err(component->dev, "tdm mask must cover 4 contiguous slots\n"); return -EINVAL;
}
switch (first_slot) { case 0:
sap_tdm_slot_last = false; break; case 4:
sap_tdm_slot_last = true; break; default:
dev_err(component->dev, "tdm mask must start at slot 0 or 4\n"); return -EINVAL;
}
ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies),
tas6424->supplies); if (ret < 0) {
dev_err(component->dev, "failed to disable supplies: %d\n", ret); return ret;
}
return 0;
}
staticint tas6424_power_on(struct snd_soc_component *component)
{ struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component); int ret;
u8 chan_states; int no_auto_diags = 0; unsignedint reg_val;
if (!regmap_read(tas6424->regmap, TAS6424_DC_DIAG_CTRL1, ®_val))
no_auto_diags = reg_val & TAS6424_LDGBYPASS_MASK;
ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies),
tas6424->supplies); if (ret < 0) {
dev_err(component->dev, "failed to enable supplies: %d\n", ret); return ret;
}
regcache_cache_only(tas6424->regmap, false);
ret = regcache_sync(tas6424->regmap); if (ret < 0) {
dev_err(component->dev, "failed to sync regcache: %d\n", ret); return ret;
}
if (tas6424->mute_gpio) {
gpiod_set_value_cansleep(tas6424->mute_gpio, 0); /* * channels are muted via the mute pin. Don't also mute * them via the registers so that subsequent register * access is not necessary to un-mute the channels
*/
chan_states = TAS6424_ALL_STATE_PLAY;
} else {
chan_states = TAS6424_ALL_STATE_MUTE;
}
snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, chan_states);
/* any time we come out of HIZ, the output channels automatically run DC * load diagnostics if autodiagnotics are enabled. wait here until this * completes.
*/ if (!no_auto_diags)
msleep(230);
switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
tas6424_power_on(component); break; case SND_SOC_BIAS_OFF:
tas6424_power_off(component); break;
}
ret = regmap_read(tas6424->regmap, TAS6424_CHANNEL_FAULT, ®); if (ret < 0) {
dev_err(dev, "failed to read CHANNEL_FAULT register: %d\n", ret); goto out;
}
if (!reg) {
tas6424->last_cfault = reg; goto check_global_fault1_reg;
}
/* * Only flag errors once for a given occurrence. This is needed as * the TAS6424 will take time clearing the fault condition internally * during which we don't want to bombard the system with the same * error message over and over.
*/ if ((reg & TAS6424_FAULT_OC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH1))
dev_crit(dev, "experienced a channel 1 overcurrent fault\n");
if ((reg & TAS6424_FAULT_OC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH2))
dev_crit(dev, "experienced a channel 2 overcurrent fault\n");
if ((reg & TAS6424_FAULT_OC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH3))
dev_crit(dev, "experienced a channel 3 overcurrent fault\n");
if ((reg & TAS6424_FAULT_OC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH4))
dev_crit(dev, "experienced a channel 4 overcurrent fault\n");
if ((reg & TAS6424_FAULT_DC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH1))
dev_crit(dev, "experienced a channel 1 DC fault\n");
if ((reg & TAS6424_FAULT_DC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH2))
dev_crit(dev, "experienced a channel 2 DC fault\n");
if ((reg & TAS6424_FAULT_DC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH3))
dev_crit(dev, "experienced a channel 3 DC fault\n");
if ((reg & TAS6424_FAULT_DC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH4))
dev_crit(dev, "experienced a channel 4 DC fault\n");
/* Store current fault1 value so we can detect any changes next time */
tas6424->last_cfault = reg;
check_global_fault1_reg:
ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT1, ®); if (ret < 0) {
dev_err(dev, "failed to read GLOB_FAULT1 register: %d\n", ret); goto out;
}
/* * Ignore any clock faults as there is no clean way to check for them. * We would need to start checking for those faults *after* the SAIF * stream has been setup, and stop checking *before* the stream is * stopped to avoid any false-positives. However there are no * appropriate hooks to monitor these events.
*/
reg &= TAS6424_FAULT_PVDD_OV |
TAS6424_FAULT_VBAT_OV |
TAS6424_FAULT_PVDD_UV |
TAS6424_FAULT_VBAT_UV;
if (!reg) {
tas6424->last_fault1 = reg; goto check_global_fault2_reg;
}
if ((reg & TAS6424_FAULT_PVDD_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_OV))
dev_crit(dev, "experienced a PVDD overvoltage fault\n");
if ((reg & TAS6424_FAULT_VBAT_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_OV))
dev_crit(dev, "experienced a VBAT overvoltage fault\n");
if ((reg & TAS6424_FAULT_PVDD_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_UV))
dev_crit(dev, "experienced a PVDD undervoltage fault\n");
if ((reg & TAS6424_FAULT_VBAT_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_UV))
dev_crit(dev, "experienced a VBAT undervoltage fault\n");
/* Store current fault1 value so we can detect any changes next time */
tas6424->last_fault1 = reg;
check_global_fault2_reg:
ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT2, ®); if (ret < 0) {
dev_err(dev, "failed to read GLOB_FAULT2 register: %d\n", ret); goto out;
}
if ((reg & TAS6424_WARN_VDD_UV) && !(tas6424->last_warn & TAS6424_WARN_VDD_UV))
dev_warn(dev, "experienced a VDD under voltage condition\n");
if ((reg & TAS6424_WARN_VDD_POR) && !(tas6424->last_warn & TAS6424_WARN_VDD_POR))
dev_warn(dev, "experienced a VDD POR condition\n");
if ((reg & TAS6424_WARN_VDD_OTW) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW))
dev_warn(dev, "experienced a global overtemp warning\n");
if ((reg & TAS6424_WARN_VDD_OTW_CH1) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH1))
dev_warn(dev, "experienced an overtemp warning on CH1\n");
if ((reg & TAS6424_WARN_VDD_OTW_CH2) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH2))
dev_warn(dev, "experienced an overtemp warning on CH2\n");
if ((reg & TAS6424_WARN_VDD_OTW_CH3) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH3))
dev_warn(dev, "experienced an overtemp warning on CH3\n");
if ((reg & TAS6424_WARN_VDD_OTW_CH4) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH4))
dev_warn(dev, "experienced an overtemp warning on CH4\n");
/* Store current warn value so we can detect any changes next time */
tas6424->last_warn = reg;
/* Clear any warnings by toggling the CLEAR_FAULT control bit */
ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3,
TAS6424_CLEAR_FAULT, TAS6424_CLEAR_FAULT); if (ret < 0)
dev_err(dev, "failed to write MISC_CTRL3 register: %d\n", ret);
ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3,
TAS6424_CLEAR_FAULT, 0); if (ret < 0)
dev_err(dev, "failed to write MISC_CTRL3 register: %d\n", ret);
out: /* Schedule the next fault check at the specified interval */
schedule_delayed_work(&tas6424->fault_check_work,
msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL));
}
staticbool tas6424_is_writable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case TAS6424_MODE_CTRL: case TAS6424_MISC_CTRL1: case TAS6424_MISC_CTRL2: case TAS6424_SAP_CTRL: case TAS6424_CH_STATE_CTRL: case TAS6424_CH1_VOL_CTRL: case TAS6424_CH2_VOL_CTRL: case TAS6424_CH3_VOL_CTRL: case TAS6424_CH4_VOL_CTRL: case TAS6424_DC_DIAG_CTRL1: case TAS6424_DC_DIAG_CTRL2: case TAS6424_DC_DIAG_CTRL3: case TAS6424_PIN_CTRL: case TAS6424_AC_DIAG_CTRL1: case TAS6424_MISC_CTRL3: case TAS6424_CLIP_CTRL: case TAS6424_CLIP_WINDOW: case TAS6424_CLIP_WARN: case TAS6424_CBC_STAT: case TAS6424_MISC_CTRL4: returntrue; default: returnfalse;
}
}
staticbool tas6424_is_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case TAS6424_DC_LOAD_DIAG_REP12: case TAS6424_DC_LOAD_DIAG_REP34: case TAS6424_DC_LOAD_DIAG_REPLO: case TAS6424_CHANNEL_STATE: case TAS6424_CHANNEL_FAULT: case TAS6424_GLOB_FAULT1: case TAS6424_GLOB_FAULT2: case TAS6424_WARN: case TAS6424_AC_LOAD_DIAG_REP1: case TAS6424_AC_LOAD_DIAG_REP2: case TAS6424_AC_LOAD_DIAG_REP3: case TAS6424_AC_LOAD_DIAG_REP4: returntrue; default: returnfalse;
}
}
tas6424->regmap = devm_regmap_init_i2c(client, &tas6424_regmap_config); if (IS_ERR(tas6424->regmap)) {
ret = PTR_ERR(tas6424->regmap);
dev_err(dev, "unable to allocate register map: %d\n", ret); return ret;
}
/* * Get control of the standby pin and set it LOW to take the codec * out of the stand-by mode. * Note: The actual pin polarity is taken care of in the GPIO lib * according the polarity specified in the DTS.
*/
tas6424->standby_gpio = devm_gpiod_get_optional(dev, "standby",
GPIOD_OUT_LOW); if (IS_ERR(tas6424->standby_gpio)) { if (PTR_ERR(tas6424->standby_gpio) == -EPROBE_DEFER) return -EPROBE_DEFER;
dev_info(dev, "failed to get standby GPIO: %ld\n",
PTR_ERR(tas6424->standby_gpio));
tas6424->standby_gpio = NULL;
}
/* * Get control of the mute pin and set it HIGH in order to start with * all the output muted. * Note: The actual pin polarity is taken care of in the GPIO lib * according the polarity specified in the DTS.
*/
tas6424->mute_gpio = devm_gpiod_get_optional(dev, "mute",
GPIOD_OUT_HIGH); if (IS_ERR(tas6424->mute_gpio)) { if (PTR_ERR(tas6424->mute_gpio) == -EPROBE_DEFER) return -EPROBE_DEFER;
dev_info(dev, "failed to get nmute GPIO: %ld\n",
PTR_ERR(tas6424->mute_gpio));
tas6424->mute_gpio = NULL;
}
for (i = 0; i < ARRAY_SIZE(tas6424->supplies); i++)
tas6424->supplies[i].supply = tas6424_supply_names[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(tas6424->supplies),
tas6424->supplies); if (ret) {
dev_err(dev, "unable to request supplies: %d\n", ret); return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies),
tas6424->supplies); if (ret) {
dev_err(dev, "unable to enable supplies: %d\n", ret); return ret;
}
/* Reset device to establish well-defined startup state */
ret = regmap_update_bits(tas6424->regmap, TAS6424_MODE_CTRL,
TAS6424_RESET, TAS6424_RESET); if (ret) {
dev_err(dev, "unable to reset device: %d\n", ret); goto disable_regs;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.