kernel_samsung_a53x/sound/soc/codecs/tas2562/tas2562-codec.c

1759 lines
52 KiB
C
Raw Normal View History

2024-06-15 16:02:09 -03:00
/*
* =============================================================================
* Copyright (c) 2016 Texas Instruments Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details.
*
* File:
* tas2562-codec.c
*
* Description:
* ALSA SoC driver for Texas Instruments TAS2562 High Performance 4W Smart
* Amplifier
*
* =============================================================================
*/
#ifdef CONFIG_TAS2562_CODEC
#define DEBUG 5
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/firmware.h>
#include <linux/regmap.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <linux/version.h>
#include "tas2562.h"
#include "tas2562-bigdata.h"
#ifdef CONFIG_TAS25XX_ALGO
#include <sound/smart_amp.h>
#include "tas25xx-calib.h"
#endif /*CONFIG_TAS25XX_ALGO*/
#define TAS2562_MDELAY 0xFFFFFFFE
#define TAS2562_MSLEEP 0xFFFFFFFD
#define TAS2562_IVSENSER_ENABLE 1
#define TAS2562_IVSENSER_DISABLE 0
/* #define TAS2558_CODEC */
static char p_icn_threshold[] = {0x00, 0x01, 0x2f, 0x2c};
static char p_icn_hysteresis[] = {0x00, 0x01, 0x5d, 0xc0};
static char const *iv_enable_text[] = {"Off", "On"};
static int tas2562iv_enable;
static int mb_mute;
static const struct soc_enum tas2562_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(iv_enable_text), iv_enable_text),
};
static int tas2562_set_fmt(struct tas2562_priv *p_tas2562,
unsigned int fmt);
static int tas2562_i2c_load_data(struct tas2562_priv *p_tas2562,
enum channel chn, unsigned int *p_data);
static int tas2562_system_mute_ctrl_get(struct snd_kcontrol *pKcontrol,
struct snd_ctl_elem_value *pValue);
static int tas2562_system_mute_ctrl_put(struct snd_kcontrol *pKcontrol,
struct snd_ctl_elem_value *pValue);
static int tas2562_mute_ctrl_get(struct snd_kcontrol *pKcontrol,
struct snd_ctl_elem_value *pValue);
static int tas2562_mute_ctrl_put(struct snd_kcontrol *pKcontrol,
struct snd_ctl_elem_value *pValue);
static unsigned int p_tas2562_classh_d_data[] = {
/* reg address size values */
TAS2562_CLASSHHEADROOM, 0x4, 0x09, 0x99, 0x99, 0x9a,
TAS2562_CLASSHHYSTERESIS, 0x4, 0x0, 0x0, 0x0, 0x0,
TAS2562_CLASSHMTCT, 0x4, 0xb, 0x0, 0x0, 0x0,
TAS2562_VBATFILTER, 0x1, 0x38,
TAS2562_CLASSHRELEASETIMER, 0x1, 0x3c,
TAS2562_BOOSTSLOPE, 0x1, 0x78,
TAS2562_TESTPAGECONFIGURATION, 0x1, 0xd,
TAS2562_CLASSDCONFIGURATION3, 0x1, 0x8e,
TAS2562_CLASSDCONFIGURATION2, 0x1, 0x49,
TAS2562_CLASSDCONFIGURATION4, 0x1, 0x21,
TAS2562_CLASSDCONFIGURATION1, 0x1, 0x80,
TAS2562_EFFICIENCYCONFIGURATION, 0x1, 0xc1,
0xFFFFFFFF, 0xFFFFFFFF
};
static unsigned int p_tas2562_emphasis_filter_48kHZ[] = {
/* Program the first order filter as an FIR filter */
TAS2562_REG(0x00, 0x2, 0x70), 0xc, 0x7f, 0xfc, 0xb9, 0x23, 0x80,
0x89, 0x79, 0x1A, 0x00, 0x00, 0x00, 0x00,
/* Set post processing gain = 7.33/30 */
TAS2562_REG(0x00, 0x3, 0x50), 0x8, 0xfd, 0xf7, 0xbc, 0xc0, 0xfd,
0xde, 0x65, 0x55,
/* Program the biquad feedback coefficients with pole = 16Hz */
TAS2562_REG(0x00, 0x4, 0x40), 0xc, 0x04, 0x00, 0x00, 0x00, 0xc0,
0x01, 0xc0, 0x60, 0x1f, 0xfe, 0x48, 0xd1,
/* Program the biquad feedforward coefficients */
TAS2562_REG(0x00, 0x4, 0x4c), 0xc, 0x7b, 0xfb, 0xfe, 0x62, 0x84,
0x04, 0x01, 0x9d, 0x00, 0x00, 0x00, 0x00,
0xFFFFFFFF, 0xFFFFFFFF
};
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
static unsigned int tas2562_codec_read(struct snd_soc_component *codec,
unsigned int reg)
{
unsigned int value = 0;
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
p_tas2562->read (p_tas2562, channel_left, reg, &value);
dev_dbg(p_tas2562->dev, "%s, reg=%d, value=%d", __func__, reg, value);
return value;
}
#else
static unsigned int tas2562_codec_read(struct snd_soc_codec *codec,
unsigned int reg)
{
unsigned int value = 0;
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
p_tas2562->read (p_tas2562, channel_left, reg, &value);
dev_dbg(p_tas2562->dev, "%s, reg=%d, value=%d", __func__, reg, value);
return value;
}
#endif
static int tas2562_iv_enable(struct tas2562_priv *p_tas2562, int enable)
{
int n_result;
if (enable) {
pr_debug("%s:tas2562iv_enable\n", __func__);
n_result = p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_POWERCONTROL,
TAS2562_POWERCONTROL_ISNSPOWER_MASK |
TAS2562_POWERCONTROL_VSNSPOWER_MASK,
TAS2562_POWERCONTROL_VSNSPOWER_ACTIVE |
TAS2562_POWERCONTROL_ISNSPOWER_ACTIVE);
} else {
pr_debug("%s:tas2562iv_disable\n", __func__);
n_result = p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_POWERCONTROL,
TAS2562_POWERCONTROL_ISNSPOWER_MASK |
TAS2562_POWERCONTROL_VSNSPOWER_MASK,
TAS2562_POWERCONTROL_VSNSPOWER_POWEREDDOWN |
TAS2562_POWERCONTROL_ISNSPOWER_POWEREDDOWN);
}
tas2562iv_enable = enable;
return n_result;
}
static int tas2562iv_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec
= snd_soc_kcontrol_component(kcontrol);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
#endif
struct tas2562_priv *p_tas2562 = NULL;
int iv_enable = 0, n_result = 0;
if (codec == NULL) {
pr_err("%s:codec is NULL\n", __func__);
return 0;
}
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
if (p_tas2562 == NULL) {
pr_err("%s:p_tas2562 is NULL\n", __func__);
return 0;
}
iv_enable = ucontrol->value.integer.value[0];
n_result = tas2562_iv_enable(p_tas2562, iv_enable);
pr_debug("%s: tas2562iv_enable = %d\n", __func__, tas2562iv_enable);
return n_result;
}
static int tas2562iv_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
#endif
struct tas2562_priv *p_tas2562 = NULL;
int ret, value;
if (codec == NULL) {
pr_err("%s:codec is NULL\n", __func__);
return 0;
}
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
if (p_tas2562 == NULL) {
pr_err("%s:p_tas2562 is NULL\n", __func__);
return 0;
}
ret = p_tas2562->read(p_tas2562, channel_left,
TAS2562_POWERCONTROL, &value);
if (ret < 0)
dev_err(p_tas2562->dev, "can't get ivsensor state %s, L=%d\n",
__func__, __LINE__);
else if (((value & TAS2562_POWERCONTROL_ISNSPOWER_MASK)
== TAS2562_POWERCONTROL_ISNSPOWER_ACTIVE)
&& ((value & TAS2562_POWERCONTROL_VSNSPOWER_MASK)
== TAS2562_POWERCONTROL_VSNSPOWER_ACTIVE)) {
ucontrol->value.integer.value[0] = TAS2562_IVSENSER_ENABLE;
} else {
ucontrol->value.integer.value[0] = TAS2562_IVSENSER_DISABLE;
}
tas2562iv_enable = ucontrol->value.integer.value[0];
dev_info(p_tas2562->dev, "value: 0x%x, tas2562iv_enable %d\n",
value, tas2562iv_enable);
return 0;
}
static const struct snd_kcontrol_new tas2562_controls[] = {
SOC_ENUM_EXT("TAS2562 IVSENSE ENABLE", tas2562_enum[0],
tas2562iv_get, tas2562iv_put),
};
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
static int tas2562_codec_write(struct snd_soc_component *codec,
unsigned int reg, unsigned int value)
{
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
dev_dbg(p_tas2562->dev, "%s: %d, %d", __func__, reg, value);
p_tas2562->write (p_tas2562, channel_both, reg, value);
return 0;
}
#else
static int tas2562_codec_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value)
{
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
dev_dbg(p_tas2562->dev, "%s: %d, %d", __func__, reg, value);
p_tas2562->write (p_tas2562, channel_both, reg, value);
return 0;
}
#endif
static int tas2562_i2c_load_data(struct tas2562_priv *p_tas2562,
enum channel chn, unsigned int *p_data)
{
unsigned int n_register;
unsigned int *n_data;
unsigned char buf[128];
unsigned int n_length = 0;
unsigned int i = 0;
unsigned int n_size = 0;
int n_result = 0;
do {
n_register = p_data[n_length];
n_size = p_data[n_length + 1];
n_data = &p_data[n_length + 2];
if (n_register == TAS2562_MSLEEP) {
msleep(n_data[0]);
dev_dbg(p_tas2562->dev, "%s, msleep = %d\n",
__func__, n_data[0]);
} else if (n_register == TAS2562_MDELAY) {
msleep(n_data[0]);
dev_dbg(p_tas2562->dev, "%s, mdelay = %d\n",
__func__, n_data[0]);
} else {
if (n_register != 0xFFFFFFFF) {
if (n_size > 128) {
dev_err(p_tas2562->dev,
"%s, Line=%d, invalid size, maximum is 128 bytes!\n",
__func__, __LINE__);
break;
}
if (n_size > 1) {
for (i = 0; i < n_size; i++)
buf[i] = (unsigned char)n_data[i];
n_result = p_tas2562->bulk_write(p_tas2562, chn,
n_register, buf, n_size);
if (n_result < 0)
break;
} else if (n_size == 1) {
n_result = p_tas2562->write(p_tas2562,
chn,
n_register, n_data[0]);
if (n_result < 0)
break;
} else {
dev_err(p_tas2562->dev,
"%s, Line=%d,invalid size, minimum is 1 bytes!\n",
__func__, __LINE__);
}
}
}
n_length = n_length + 2 + p_data[n_length + 1];
} while (n_register != 0xFFFFFFFF);
return n_result;
}
#ifdef CODEC_PM
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
static int tas2562_codec_suspend(struct snd_soc_component *codec)
{
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
int ret = 0;
mutex_lock(&p_tas2562->codec_lock);
dev_dbg(p_tas2562->dev, "%s\n", __func__);
p_tas2562->runtime_suspend(p_tas2562);
mutex_unlock(&p_tas2562->codec_lock);
return ret;
}
static int tas2562_codec_resume(struct snd_soc_component *codec)
{
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
int ret = 0;
mutex_lock(&p_tas2562->codec_lock);
dev_dbg(p_tas2562->dev, "%s\n", __func__);
p_tas2562->runtime_resume(p_tas2562);
mutex_unlock(&p_tas2562->codec_lock);
return ret;
}
#else
static int tas2562_codec_suspend(struct snd_soc_codec *codec)
{
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
mutex_lock(&p_tas2562->codec_lock);
dev_dbg(p_tas2562->dev, "%s\n", __func__);
p_tas2562->runtime_suspend(p_tas2562);
mutex_unlock(&p_tas2562->codec_lock);
return ret;
}
static int tas2562_codec_resume(struct snd_soc_codec *codec)
{
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
mutex_lock(&p_tas2562->codec_lock);
dev_dbg(p_tas2562->dev, "%s\n", __func__);
p_tas2562->runtime_resume(p_tas2562);
mutex_unlock(&p_tas2562->codec_lock);
return ret;
}
#endif
#endif
static int irq_tas2562_power_up(struct tas2562_priv *p_tas2562,
enum channel chn)
{
int n_result = 0;
/*Added Interrupt Disable to fix TDM clock error*/
n_result = p_tas2562->write(p_tas2562, channel_both,
TAS2562_INTERRUPTMASKREG0,
TAS2562_INTERRUPTMASKREG0_DISABLE);
n_result = p_tas2562->write(p_tas2562, channel_both,
TAS2562_INTERRUPTMASKREG1,
TAS2562_INTERRUPTMASKREG1_DISABLE);
/* Clear latched IRQ before power on */
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_INTERRUPTCONFIGURATION,
TAS2562_INTERRUPTCONFIGURATION_LTCHINTCLEAR_MASK,
TAS2562_INTERRUPTCONFIGURATION_LTCHINTCLEAR);
/*Power up sequence*/
n_result = p_tas2562->update_bits(p_tas2562,
chn, TAS2562_POWERCONTROL,
TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK |
TAS2562_POWERCONTROL_ISNSPOWER_MASK |
TAS2562_POWERCONTROL_VSNSPOWER_MASK,
TAS2562_POWERCONTROL_OPERATIONALMODE10_ACTIVE |
TAS2562_POWERCONTROL_ISNSPOWER_ACTIVE |
TAS2562_POWERCONTROL_VSNSPOWER_ACTIVE);
dev_info(p_tas2562->dev, "set ICN to -80dB\n");
n_result = p_tas2562->bulk_write(p_tas2562, chn,
TAS2562_ICN_THRESHOLD_REG,
p_icn_threshold,
sizeof(p_icn_threshold));
n_result = p_tas2562->bulk_write(p_tas2562, chn,
TAS2562_ICN_HYSTERESIS_REG,
p_icn_hysteresis,
sizeof(p_icn_hysteresis));
p_tas2562->write(p_tas2562, channel_both,
TAS2562_INTERRUPTMASKREG0, 0xf8);
p_tas2562->write(p_tas2562, channel_both,
TAS2562_INTERRUPTMASKREG1, 0xb1);
return n_result;
}
int tas2562_determine_interrupt(struct tas2562_priv *p_tas2562, int nDevInt1Status, int nDevInt2Status)
{
int temp_err_code = 0;
if (((nDevInt1Status & 0x7) != 0)
|| ((nDevInt2Status & 0x0f) != 0)) {
/* in case of INT_CLK, INT_OC, INT_OT,
* INT_OVLT, INT_UVLT, INT_BO
*/
if (nDevInt1Status &
TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_INTERRUPT) {
dev_err(p_tas2562->dev, "TDM clock error!\n");
temp_err_code |= ERROR_CLOCK;
}
if (nDevInt1Status &
TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_INTERRUPT) {
dev_err(p_tas2562->dev, "SPK over current!\n");
temp_err_code |= ERROR_OVER_CURRENT;
}
if (nDevInt1Status &
TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_INTERRUPT) {
dev_err(p_tas2562->dev, "die over temperature!\n");
temp_err_code |= ERROR_DIE_OVERTEMP;
}
if (nDevInt2Status &
TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_INTERRUPT) {
dev_err(p_tas2562->dev, "SPK over voltage!\n");
temp_err_code |= ERROR_OVER_VOLTAGE;
}
if (nDevInt2Status &
TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_INTERRUPT) {
dev_err(p_tas2562->dev, "SPK under voltage!\n");
temp_err_code |= ERROR_UNDER_VOLTAGE;
}
if (nDevInt2Status &
TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_INTERRUPT) {
dev_err(p_tas2562->dev, "brownout!\n");
temp_err_code |= ERROR_BROWNOUT;
}
}
return temp_err_code;
}
int tas2562_enable_emphasis_filter(struct tas2562_priv *p_tas2562, int ch,
int samplerate)
{
int n_result = 0;
if (samplerate == 48000) {
dev_info(p_tas2562->dev, "Emphasis Filter Enabled\n");
n_result = tas2562_i2c_load_data(p_tas2562, ch,
p_tas2562_emphasis_filter_48kHZ);
}
return n_result;
}
static int tas2562_set_power_state(struct tas2562_priv *p_tas2562,
enum channel chn, int state)
{
int n_result = 0;
int i = 0;
int nDevInt1Status = 0, nDevInt2Status = 0, temp_err_code = 0;
if ((p_tas2562->mb_mute) && (state == TAS2562_POWER_ACTIVE))
state = TAS2562_POWER_MUTE;
dev_info(p_tas2562->dev, "set power state: %d\n", state);
switch (state) {
case TAS2562_POWER_ACTIVE:
/* if set format was not called by asoc, then set it default */
if (p_tas2562->mn_asi_format == 0)
p_tas2562->mn_asi_format = SND_SOC_DAIFMT_CBS_CFS
| SND_SOC_DAIFMT_IB_NF
| SND_SOC_DAIFMT_I2S;
n_result = tas2562_set_fmt(p_tas2562, p_tas2562->mn_asi_format);
if (n_result < 0)
return n_result;
#ifdef CONFIG_TAS25XX_ALGO
tas25xx_send_algo_calibration();
tas25xx_set_iv_bit_fomat(p_tas2562->mn_iv_width,
p_tas2562->mn_vbat, 1);
#endif
/*Added Interrupt Disable to fix TDM clock error*/
n_result = p_tas2562->write(p_tas2562, channel_both,
TAS2562_INTERRUPTMASKREG0,
TAS2562_INTERRUPTMASKREG0_DISABLE);
n_result = p_tas2562->write(p_tas2562, channel_both,
TAS2562_INTERRUPTMASKREG1,
TAS2562_INTERRUPTMASKREG1_DISABLE);
/*Power up sequence*/
if (p_tas2562->dac_mute == 0) {
n_result = p_tas2562->update_bits(p_tas2562,
chn, TAS2562_POWERCONTROL,
TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK |
TAS2562_POWERCONTROL_ISNSPOWER_MASK |
TAS2562_POWERCONTROL_VSNSPOWER_MASK,
TAS2562_POWERCONTROL_OPERATIONALMODE10_ACTIVE |
TAS2562_POWERCONTROL_ISNSPOWER_ACTIVE |
TAS2562_POWERCONTROL_VSNSPOWER_ACTIVE);
} else {
n_result = p_tas2562->update_bits(p_tas2562,
chn, TAS2562_POWERCONTROL,
TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK |
TAS2562_POWERCONTROL_ISNSPOWER_MASK |
TAS2562_POWERCONTROL_VSNSPOWER_MASK,
TAS2562_POWERCONTROL_OPERATIONALMODE10_MUTE |
TAS2562_POWERCONTROL_ISNSPOWER_ACTIVE |
TAS2562_POWERCONTROL_VSNSPOWER_ACTIVE);
}
if (p_tas2562->mn_iv_width == 8)
tas2562_enable_emphasis_filter(p_tas2562,
channel_both,
p_tas2562->mn_sampling_rate);
dev_info(p_tas2562->dev, "set ICN to -80dB\n");
n_result = p_tas2562->bulk_write(p_tas2562, chn,
TAS2562_ICN_THRESHOLD_REG,
p_icn_threshold,
sizeof(p_icn_threshold));
n_result = p_tas2562->bulk_write(p_tas2562, chn,
TAS2562_ICN_HYSTERESIS_REG,
p_icn_hysteresis,
sizeof(p_icn_hysteresis));
/* Schedule routine enable interrupt after 100ms */
p_tas2562->mb_power_up = true;
p_tas2562->mn_power_state = TAS2562_POWER_ACTIVE;
schedule_delayed_work(&p_tas2562->init_work,
msecs_to_jiffies(200));
break;
case TAS2562_POWER_MUTE:
n_result = p_tas2562->update_bits(p_tas2562, chn,
TAS2562_POWERCONTROL,
TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK |
TAS2562_POWERCONTROL_ISNSPOWER_MASK |
TAS2562_POWERCONTROL_VSNSPOWER_MASK,
TAS2562_POWERCONTROL_OPERATIONALMODE10_MUTE |
TAS2562_POWERCONTROL_VSNSPOWER_ACTIVE |
TAS2562_POWERCONTROL_ISNSPOWER_ACTIVE);
p_tas2562->mb_power_up = true;
p_tas2562->mn_power_state = TAS2562_POWER_MUTE;
break;
case TAS2562_POWER_SHUTDOWN:
/*Included IV Sense Power Down*/
n_result = p_tas2562->update_bits(p_tas2562, chn,
TAS2562_POWERCONTROL,
TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK |
TAS2562_POWERCONTROL_ISNSPOWER_MASK |
TAS2562_POWERCONTROL_VSNSPOWER_MASK,
TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN |
TAS2562_POWERCONTROL_VSNSPOWER_POWEREDDOWN |
TAS2562_POWERCONTROL_ISNSPOWER_POWEREDDOWN);
p_tas2562->mb_power_up = false;
p_tas2562->mn_power_state = TAS2562_POWER_SHUTDOWN;
p_tas2562->enable_irq(p_tas2562, false);
msleep(20);
/* Added for Big Data
* Seperate Loop inorder not to delay the stop
* Since register are latched, value will be stored even after shutdown
*/
if (p_tas2562->valid_irq_gpio == 0) {
for (i = 0; i < p_tas2562->mn_channels; i++) {
if (chn & (i+1)) {
n_result = p_tas2562->read(p_tas2562, i+1,
TAS2562_LATCHEDINTERRUPTREG0, &nDevInt1Status);
n_result = p_tas2562->read(p_tas2562, i+1,
TAS2562_LATCHEDINTERRUPTREG1, &nDevInt2Status);
temp_err_code =
tas2562_determine_interrupt(p_tas2562, nDevInt1Status,
nDevInt2Status);
tas2562_update_bigdata(i+1, temp_err_code);
}
}
}
#ifdef CONFIG_TAS25XX_ALGO
tas25xx_update_big_data();
#endif
break;
default:
dev_err(p_tas2562->dev, "wrong power state setting %d\n",
state);
}
return n_result;
}
static int tas2562_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
switch (event) {
case SND_SOC_DAPM_POST_PMU:
dev_info(p_tas2562->dev, "SND_SOC_DAPM_POST_PMU\n");
break;
case SND_SOC_DAPM_PRE_PMD:
dev_info(p_tas2562->dev, "SND_SOC_DAPM_PRE_PMD\n");
break;
}
return 0;
}
static const struct snd_soc_dapm_widget tas2562_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("Voltage Sense", "ASI1 Capture", 1,
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("Current Sense", "ASI1 Capture", 0,
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2562_dac_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_OUTPUT("OUT"),
SND_SOC_DAPM_SIGGEN("VMON"),
SND_SOC_DAPM_SIGGEN("IMON")
};
static const struct snd_soc_dapm_route tas2562_audio_map[] = {
{"DAC", NULL, "ASI1"},
{"OUT", NULL, "DAC"},
{"Voltage Sense", NULL, "VMON"},
{"Current Sense", NULL, "IMON"}
};
static int tas2562_get_left_speaker_switch(struct snd_kcontrol *pKcontrol,
struct snd_ctl_elem_value *p_u_control)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec
= snd_soc_kcontrol_component(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
dev_info(p_tas2562->dev, "%s, p_u_control = %ld\n",
__func__, p_u_control->value.integer.value[0]);
p_u_control->value.integer.value[0] = p_tas2562->spk_l_control;
return 0;
}
static int tas2562_set_left_speaker_switch(struct snd_kcontrol *pKcontrol,
struct snd_ctl_elem_value *p_u_control)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
dev_info(p_tas2562->dev, "%s, spk_l_control = %d\n",
__func__, p_tas2562->spk_l_control);
p_tas2562->spk_l_control = p_u_control->value.integer.value[0];
return 0;
}
static int tas2562_get_right_speaker_switch(
struct snd_kcontrol *pKcontrol, struct snd_ctl_elem_value *p_u_control)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
dev_info(p_tas2562->dev, "%s, p_u_control = %ld\n",
__func__, p_u_control->value.integer.value[0]);
p_u_control->value.integer.value[0] = p_tas2562->spk_r_control;
return 0;
}
static int tas2562_set_right_speaker_switch(
struct snd_kcontrol *pKcontrol, struct snd_ctl_elem_value *p_u_control)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
dev_info(p_tas2562->dev, "%s, spk_r_control = %d\n",
__func__, p_tas2562->spk_r_control);
p_tas2562->spk_r_control = p_u_control->value.integer.value[0];
return 0;
}
static int tas2562_mute(struct snd_soc_dai *dai, int mute, int direction)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = dai->component;
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = dai->codec;
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
enum channel chn;
dev_dbg(p_tas2562->dev, "%s, direction %d, mute %d\n", __func__, direction, mute);
mutex_lock(&p_tas2562->codec_lock);
if ((p_tas2562->spk_l_control == 1)
&& (p_tas2562->spk_r_control == 1)
&& (p_tas2562->mn_channels == 2))
chn = channel_both;
else if (p_tas2562->spk_l_control == 1)
chn = channel_left;
else if ((p_tas2562->spk_r_control == 1)
&& (p_tas2562->mn_channels == 2))
chn = channel_right;
else
chn = channel_left;
if (direction == 0) { /* Playback */
if (mute) {
tas2562_set_power_state(p_tas2562,
channel_both, TAS2562_POWER_SHUTDOWN);
} else {
tas2562_set_power_state(p_tas2562, chn, TAS2562_POWER_ACTIVE);
}
}
mutex_unlock(&p_tas2562->codec_lock);
return 0;
}
static int tas2562_iv_slot_config(struct tas2562_priv *p_tas2562)
{
int ret = 0;
dev_info(p_tas2562->dev, "%s, %d\n", __func__,
p_tas2562->mn_slot_width);
if (p_tas2562->mn_channels == 2) {
if (p_tas2562->mn_slot_width == 16) {
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG5, 0xff, 0x41);
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG6, 0xff, 0x40);
p_tas2562->update_bits(p_tas2562, channel_right,
TAS2562_TDMCONFIGURATIONREG5, 0xff, 0x43);
p_tas2562->update_bits(p_tas2562, channel_right,
TAS2562_TDMCONFIGURATIONREG6, 0xff, 0x42);
} else { /*24Bit or 32Bit*/
if (p_tas2562->mn_iv_width == 8) {
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG2,
TAS2562_TDMCONFIGURATIONREG2_IVMONLEN76_MASK,
TAS2562_TDMCONFIGURATIONREG2_IVMONLEN76_8BITS);
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG5, 0xff, 0x41);
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG6, 0xff, 0x40);
p_tas2562->update_bits(p_tas2562, channel_right,
TAS2562_TDMCONFIGURATIONREG5, 0xff, 0x45);
p_tas2562->update_bits(p_tas2562, channel_right,
TAS2562_TDMCONFIGURATIONREG6, 0xff, 0x44);
if (p_tas2562->mn_vbat) {
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG7, 0xff, 0x42);
p_tas2562->update_bits(p_tas2562, channel_right,
TAS2562_TDMCONFIGURATIONREG7, 0xff, 0x46);
}
} else { /* 12Bit or 16Bit No VBat can be supported */
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG5, 0xff, 0x42);
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG6, 0xff, 0x40);
p_tas2562->update_bits(p_tas2562, channel_right,
TAS2562_TDMCONFIGURATIONREG5, 0xff, 0x46);
p_tas2562->update_bits(p_tas2562, channel_right,
TAS2562_TDMCONFIGURATIONREG6, 0xff, 0x44);
}
}
} else if ((p_tas2562->mn_channels == 1)
&& (p_tas2562->mn_slot_width == 32)) {
if (p_tas2562->mn_iv_width == 16) {
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG2,
TAS2562_TDMCONFIGURATIONREG2_IVLEN76_MASK,
TAS2562_TDMCONFIGURATIONREG2_IVLENCFG76_16BITS);
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG5, 0xff, 0x44);
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG6, 0xff, 0x40);
} else if (p_tas2562->mn_iv_width == 12) {
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG2,
TAS2562_TDMCONFIGURATIONREG2_IVLEN76_MASK,
TAS2562_TDMCONFIGURATIONREG2_IVLENCFG76_12BITS);
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG5, 0xff, 0x41);
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG6, 0xff, 0x40);
if (p_tas2562->mn_vbat == 1)
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG7, 0xff, 0x44);
}
} else if ((p_tas2562->mn_channels == 1)
&& (p_tas2562->mn_slot_width == 16)) {
if (p_tas2562->mn_iv_width == 16) {
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG2,
TAS2562_TDMCONFIGURATIONREG2_IVLEN76_MASK,
TAS2562_TDMCONFIGURATIONREG2_IVLENCFG76_16BITS);
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG5, 0xff, 0x42);
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG6, 0xff, 0x40);
} else if (p_tas2562->mn_iv_width == 12) {
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG2,
TAS2562_TDMCONFIGURATIONREG2_IVLEN76_MASK,
TAS2562_TDMCONFIGURATIONREG2_IVLENCFG76_12BITS);
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG5, 0xff, 0x41);
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG6, 0xff, 0x40);
if (p_tas2562->mn_vbat == 1)
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG7, 0xff, 0x43);
}
} else {
dev_err(p_tas2562->dev, "%s, wrong params, %d\n",
__func__, p_tas2562->mn_slot_width);
}
return ret;
}
static int tas2562_iv_bitwidth_config(struct tas2562_priv *p_tas2562)
{
int ret = 0;
if ((p_tas2562->mn_channels == 2) && (p_tas2562->mn_slot_width == 16)) {
ret = p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG2,
TAS2562_TDMCONFIGURATIONREG2_IVMONLEN76_MASK,
TAS2562_TDMCONFIGURATIONREG2_IVMONLEN76_8BITS);
}
return ret;
}
static int tas2562_set_slot(struct tas2562_priv *p_tas2562,
int slot_width)
{
int ret = 0;
switch (slot_width) {
case 16:
ret = p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG2,
TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_MASK,
TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_16BITS);
break;
case 24:
ret = p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG2,
TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_MASK,
TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_24BITS);
break;
case 32:
ret = p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG2,
TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_MASK,
TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_32BITS);
break;
case 0:
/* Do not change slot width */
break;
default:
dev_err(p_tas2562->dev, "slot width not supported");
ret = -EINVAL;
}
if (ret >= 0)
p_tas2562->mn_slot_width = slot_width;
return ret;
}
static int tas2562_set_bitwidth(struct tas2562_priv *p_tas2562,
int bitwidth)
{
int slot_width_tmp = 16;
dev_info(p_tas2562->dev, "%s %d\n", __func__, bitwidth);
switch (bitwidth) {
case SNDRV_PCM_FORMAT_S16_LE:
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG2,
TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_MASK,
TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_16BITS);
p_tas2562->mn_ch_size = 16;
slot_width_tmp = 16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG2,
TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_MASK,
TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_24BITS);
p_tas2562->mn_ch_size = 24;
slot_width_tmp = 32;
break;
case SNDRV_PCM_FORMAT_S32_LE:
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG2,
TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_MASK,
TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_32BITS);
p_tas2562->mn_ch_size = 32;
slot_width_tmp = 32;
break;
default:
dev_info(p_tas2562->dev, "Not supported params format\n");
}
tas2562_set_slot(p_tas2562, slot_width_tmp);
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG2,
TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MASK,
TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MONO_LEFT);
p_tas2562->update_bits(p_tas2562, channel_right,
TAS2562_TDMCONFIGURATIONREG2,
TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MASK,
TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MONO_RIGHT);
tas2562_iv_slot_config(p_tas2562);
tas2562_iv_bitwidth_config(p_tas2562);
dev_info(p_tas2562->dev, "mn_ch_size: %d\n", p_tas2562->mn_ch_size);
p_tas2562->mn_pcm_format = bitwidth;
return 0;
}
static int tas2562_set_samplerate(struct tas2562_priv *p_tas2562,
int samplerate)
{
switch (samplerate) {
case 48000:
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG0,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_MASK,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_48KHZ);
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG0,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_MASK,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_44_1_48KHZ);
break;
case 44100:
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG0,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_MASK,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_44_1KHZ);
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG0,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_MASK,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_44_1_48KHZ);
break;
case 96000:
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG0,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_MASK,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_48KHZ);
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG0,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_MASK,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_88_2_96KHZ);
break;
case 88200:
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG0,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_MASK,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_44_1KHZ);
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG0,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_MASK,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_88_2_96KHZ);
break;
case 19200:
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG0,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_MASK,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_48KHZ);
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG0,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_MASK,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_176_4_192KHZ);
break;
case 17640:
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG0,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_MASK,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_44_1KHZ);
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG0,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_MASK,
TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_176_4_192KHZ);
break;
default:
dev_info(p_tas2562->dev, "%s, unsupported sample rate, %d\n",
__func__, samplerate);
}
p_tas2562->mn_sampling_rate = samplerate;
return 0;
}
static int tas2562_system_mute_ctrl_get(struct snd_kcontrol *pKcontrol,
struct snd_ctl_elem_value *pValue)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec
= snd_soc_kcontrol_component(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
pValue->value.integer.value[0] = p_tas2562->mb_mute;
dev_dbg(p_tas2562->dev, "%s = %d\n",
__func__, p_tas2562->mb_mute);
return 0;
}
static int tas2562_system_mute_ctrl_put(struct snd_kcontrol *pKcontrol,
struct snd_ctl_elem_value *pValue)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
mb_mute = pValue->value.integer.value[0];
dev_dbg(p_tas2562->dev, "%s = %d\n", __func__, mb_mute);
p_tas2562->mb_mute = !!mb_mute;
return 0;
}
static int tas2562_mute_ctrl_get(struct snd_kcontrol *pKcontrol,
struct snd_ctl_elem_value *pValue)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
pValue->value.integer.value[0] = p_tas2562->mb_mute;
if ((p_tas2562->mb_power_up == true)
&& (p_tas2562->mn_power_state == TAS2562_POWER_ACTIVE))
pValue->value.integer.value[0] = 0;
else
pValue->value.integer.value[0] = 1;
dev_dbg(p_tas2562->dev, "%s = %ld\n",
__func__, pValue->value.integer.value[0]);
return 0;
}
static int tas2562_mute_ctrl_put(struct snd_kcontrol *pKcontrol,
struct snd_ctl_elem_value *pValue)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
enum channel chn;
int mute = pValue->value.integer.value[0];
dev_dbg(p_tas2562->dev, "%s, %d\n", __func__, mute);
mutex_lock(&p_tas2562->codec_lock);
if ((p_tas2562->spk_l_control == 1)
&& (p_tas2562->spk_r_control == 1)
&& (p_tas2562->mn_channels == 2))
chn = channel_both;
else if (p_tas2562->spk_l_control == 1)
chn = channel_left;
else if ((p_tas2562->spk_r_control == 1)
&& (p_tas2562->mn_channels == 2))
chn = channel_right;
else
chn = channel_left;
if (mute) {
tas2562_set_power_state(p_tas2562,
chn, TAS2562_POWER_MUTE);
} else {
tas2562_set_power_state(p_tas2562, chn, TAS2562_POWER_ACTIVE);
}
mutex_unlock(&p_tas2562->codec_lock);
return 0;
}
static int tas2562_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = dai->component;
struct tas2562_priv *p_tas2562
= snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = dai->codec;
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
/* int blr_clk_ratio; */
int n_result = 0;
dev_dbg(p_tas2562->dev, "%s, format: %d\n", __func__,
params_format(params));
mutex_lock(&p_tas2562->codec_lock);
n_result = tas2562_set_bitwidth(p_tas2562,
params_format(params));
if (n_result < 0) {
dev_info(p_tas2562->dev, "set bitwidth failed, %d\n", n_result);
goto ret;
}
dev_info(p_tas2562->dev, "%s, sample rate: %d\n", __func__,
params_rate(params));
n_result = tas2562_set_samplerate(p_tas2562,
params_rate(params));
ret:
mutex_unlock(&p_tas2562->codec_lock);
return n_result;
}
static int tas2562_set_fmt(struct tas2562_priv *p_tas2562,
unsigned int fmt)
{
u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
int ret = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
asi_cfg_1 = 0x00;
break;
default:
dev_err(p_tas2562->dev, "ASI format master is not found\n");
ret = -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
dev_info(p_tas2562->dev, "INV format: NBNF\n");
asi_cfg_1 |= TAS2562_TDMCONFIGURATIONREG1_RXEDGE_RISING;
break;
case SND_SOC_DAIFMT_IB_NF:
dev_info(p_tas2562->dev, "INV format: IBNF\n");
asi_cfg_1 |= TAS2562_TDMCONFIGURATIONREG1_RXEDGE_FALLING;
break;
default:
dev_err(p_tas2562->dev, "ASI format Inverse is not found\n");
ret = -EINVAL;
}
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_TDMCONFIGURATIONREG1,
TAS2562_TDMCONFIGURATIONREG1_RXEDGE_MASK,
asi_cfg_1);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case (SND_SOC_DAIFMT_I2S):
tdm_rx_start_slot = 1;
break;
case (SND_SOC_DAIFMT_DSP_A):
case (SND_SOC_DAIFMT_DSP_B):
tdm_rx_start_slot = 1;
break;
case (SND_SOC_DAIFMT_LEFT_J):
tdm_rx_start_slot = 0;
break;
default:
dev_err(p_tas2562->dev, "DAI Format is not found, fmt=0x%x\n", fmt);
ret = -EINVAL;
break;
}
p_tas2562->update_bits(p_tas2562, channel_left,
TAS2562_TDMCONFIGURATIONREG1,
TAS2562_TDMCONFIGURATIONREG1_RXOFFSET51_MASK,
(tdm_rx_start_slot <<
TAS2562_TDMCONFIGURATIONREG1_RXOFFSET51_SHIFT));
if (p_tas2562->mn_channels == 2) {
p_tas2562->update_bits(p_tas2562, channel_right,
TAS2562_TDMCONFIGURATIONREG1,
TAS2562_TDMCONFIGURATIONREG1_RXOFFSET51_MASK,
(tdm_rx_start_slot <<
TAS2562_TDMCONFIGURATIONREG1_RXOFFSET51_SHIFT));
}
p_tas2562->mn_asi_format = fmt;
return 0;
}
static int tas2562_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = dai->component;
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = dai->codec;
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
int ret = 0;
dev_dbg(p_tas2562->dev, "%s, format=0x%x\n", __func__, fmt);
ret = tas2562_set_fmt(p_tas2562, fmt);
return ret;
}
static int tas2562_set_dai_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width)
{
int ret = 0;
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = dai->component;
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = dai->codec;
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
dev_dbg(p_tas2562->dev, "%s, tx_mask:%d, rx_mask:%d",
__func__, tx_mask, rx_mask);
dev_dbg(p_tas2562->dev, "%s, slots:%d,slot_width:%d",
__func__, slots, slot_width);
ret = tas2562_set_slot(p_tas2562, slot_width);
return ret;
}
static struct snd_soc_dai_ops tas2562_dai_ops = {
.mute_stream = tas2562_mute,
.hw_params = tas2562_hw_params,
.set_fmt = tas2562_set_dai_fmt,
.set_tdm_slot = tas2562_set_dai_tdm_slot,
};
#define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S32_LE)
#define TAS2562_RATES (SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000 \
SNDRV_PCM_RATE_88200 |\
SNDRV_PCM_RATE_96000 |\
SNDRV_PCM_RATE_176400 |\
SNDRV_PCM_RATE_192000)
static struct snd_soc_dai_driver tas2562_dai_driver[] = {
{
.name = "tas2562 ASI1",
.id = 0,
.playback = {
.stream_name = "ASI1 Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = TAS2562_FORMATS,
},
.capture = {
.stream_name = "ASI1 Capture",
.channels_min = 0,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = TAS2562_FORMATS,
},
.ops = &tas2562_dai_ops,
.symmetric_rates = 1,
},
};
static int tas2562_load_init(struct tas2562_priv *p_tas2562)
{
int ret;
#ifdef TAS2558_CODEC
/* Max voltage to 9V */
ret = p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_BOOSTCONFIGURATION2,
TAS2562_BOOSTCONFIGURATION2_BOOSTMAXVOLTAGE_MASK,
0x7);
ret = p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_PLAYBACKCONFIGURATIONREG0,
TAS2562_PLAYBACKCONFIGURATIONREG0_AMPLIFIERLEVEL51_MASK,
0xd << 1);
if (ret < 0)
return ret;
#endif
ret = p_tas2562->write(p_tas2562, channel_both,
TAS2562_MISCCONFIGURATIONREG0, 0xcf);
if (ret < 0)
return ret;
#ifdef CONFIG_PLATFORM_EXYNOS
if (p_tas2562->mn_channels == 2) {
ret = p_tas2562->write(p_tas2562, channel_both,
TAS2562_TDMConfigurationReg4, 0x13);
if (ret < 0)
return ret;
} else {
ret = p_tas2562->write(p_tas2562, channel_both,
TAS2562_TDMConfigurationReg4, 0x03);
if (ret < 0)
return ret;
}
#else
if (p_tas2562->mn_channels == 2) {
ret = p_tas2562->write(p_tas2562, channel_both,
TAS2562_TDMConfigurationReg4, 0x11);
if (ret < 0)
return ret;
} else {
ret = p_tas2562->write(p_tas2562, channel_both,
TAS2562_TDMConfigurationReg4, 0x01);
if (ret < 0)
return ret;
}
#endif
ret = p_tas2562->write(p_tas2562, channel_both,
TAS2562_CLOCKCONFIGURATION, 0x0c);
if (ret < 0)
return ret;
ret = tas2562_i2c_load_data(p_tas2562, channel_both,
p_tas2562_classh_d_data);
/* Increase the clock halt timer to 838ms to avoid
TDM Clock errors during playback start/stop */
ret = p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_INTERRUPTCONFIGURATION,
TAS2562_CLOCK_HALT_TIMER_MASK,
TAS2562_CLOCK_HALT_838MS);
return ret;
}
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
static int tas2562_codec_probe(struct snd_soc_component *codec)
{
int ret;
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
ret = snd_soc_add_component_controls(codec, tas2562_controls,
ARRAY_SIZE(tas2562_controls));
if (ret < 0) {
pr_err("%s: add_codec_controls failed, err %d\n",
__func__, ret);
return ret;
}
tas2562_load_init(p_tas2562);
tas2562_iv_enable(p_tas2562, 1);
dev_dbg(p_tas2562->dev, "%s\n", __func__);
return 0;
}
#else
static int tas2562_codec_probe(struct snd_soc_codec *codec)
{
int ret;
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
ret = snd_soc_add_codec_controls(codec, tas2562_controls,
ARRAY_SIZE(tas2562_controls));
if (ret < 0) {
pr_err("%s: add_codec_controls failed, err %d\n",
__func__, ret);
return ret;
}
tas2562_load_init(p_tas2562);
tas2562_iv_enable(p_tas2562, 1);
dev_dbg(p_tas2562->dev, "%s\n", __func__);
return 0;
}
#endif
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
static void tas2562_codec_remove(struct snd_soc_component *codec)
{
}
#else
static int tas2562_codec_remove(struct snd_soc_codec *codec)
{
return 0;
}
#endif
static DECLARE_TLV_DB_SCALE(tas2562_digital_tlv, 1100, 50, 0);
static const char * const speaker_switch_text[] = {"Off", "On"};
static const struct soc_enum spk_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(speaker_switch_text),
speaker_switch_text),
};
static const char * const icn_sw_text[] = {"Enable", "Disable"};
static const struct soc_enum icn_sw_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(icn_sw_text),
icn_sw_text),
};
static int tas2562_get_icn_switch(struct snd_kcontrol *pKcontrol,
struct snd_ctl_elem_value *p_u_control)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec
= snd_soc_kcontrol_component(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
dev_info(p_tas2562->dev, "%s, icn_sw = %ld\n",
__func__, p_u_control->value.integer.value[0]);
p_u_control->value.integer.value[0] = p_tas2562->icn_sw;
return 0;
}
static int tas2562_set_icn_switch(struct snd_kcontrol *pKcontrol,
struct snd_ctl_elem_value *p_u_control)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec
= snd_soc_kcontrol_component(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
p_tas2562->icn_sw = p_u_control->value.integer.value[0];
if (p_tas2562->icn_sw == 0) {
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_ICN_SW_REG,
TAS2562_ICN_SW_MASK,
TAS2562_ICN_SW_ENABLE);
dev_info(p_tas2562->dev, "%s: ICN Enable!\n", __func__);
} else {
p_tas2562->update_bits(p_tas2562, channel_both,
TAS2562_ICN_SW_REG,
TAS2562_ICN_SW_MASK,
TAS2562_ICN_SW_DISABLE);
dev_info(p_tas2562->dev, "%s: ICN Disable!\n", __func__);
}
return 0;
}
static int tas2562_dac_mute_ctrl_get(struct snd_kcontrol *pKcontrol,
struct snd_ctl_elem_value *pValue)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
pValue->value.integer.value[0] = p_tas2562->dac_mute;
dev_dbg(p_tas2562->dev, "%s = %ld\n",
__func__, pValue->value.integer.value[0]);
return 0;
}
static int tas2562_dac_mute_ctrl_put(struct snd_kcontrol *pKcontrol,
struct snd_ctl_elem_value *pValue)
{
int n_result = 0;
enum channel chn;
int mute = pValue->value.integer.value[0];
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_component_get_drvdata(codec);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec);
#endif
dev_dbg(p_tas2562->dev, "%s, %d\n", __func__, mute);
mutex_lock(&p_tas2562->codec_lock);
if ((p_tas2562->spk_l_control == 1)
&& (p_tas2562->spk_r_control == 1)
&& (p_tas2562->mn_channels == 2))
chn = channel_both;
else if (p_tas2562->spk_l_control == 1)
chn = channel_left;
else if ((p_tas2562->spk_r_control == 1)
&& (p_tas2562->mn_channels == 2))
chn = channel_right;
else
chn = channel_left;
if (p_tas2562->mn_power_state == TAS2562_POWER_ACTIVE) {
if (mute) {
n_result = p_tas2562->update_bits(p_tas2562, chn,
TAS2562_POWERCONTROL,
TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK,
TAS2562_POWERCONTROL_OPERATIONALMODE10_MUTE);
} else {
n_result = p_tas2562->update_bits(p_tas2562, chn,
TAS2562_POWERCONTROL,
TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK,
TAS2562_POWERCONTROL_OPERATIONALMODE10_ACTIVE);
}
}
p_tas2562->dac_mute = mute;
mutex_unlock(&p_tas2562->codec_lock);
return n_result;
}
static const struct snd_kcontrol_new tas2562_snd_controls[] = {
SOC_SINGLE_TLV("Amp Output Level", TAS2562_PLAYBACKCONFIGURATIONREG0,
1, 0x16, 0, tas2562_digital_tlv),
SOC_SINGLE_EXT("SmartPA System Mute", SND_SOC_NOPM, 0, 0x0001, 0,
tas2562_system_mute_ctrl_get,
tas2562_system_mute_ctrl_put),
SOC_SINGLE_EXT("SmartPA Mute", SND_SOC_NOPM, 0, 0x0001, 0,
tas2562_mute_ctrl_get, tas2562_mute_ctrl_put),
SOC_SINGLE_EXT("TAS2562 DAC Mute", SND_SOC_NOPM, 0, 0x0001, 0,
tas2562_dac_mute_ctrl_get, tas2562_dac_mute_ctrl_put),
SOC_ENUM_EXT("TAS2562 Left Speaker Switch", spk_enum[0],
tas2562_get_left_speaker_switch,
tas2562_set_left_speaker_switch),
SOC_ENUM_EXT("TAS2562 Right Speaker Switch", spk_enum[0],
tas2562_get_right_speaker_switch,
tas2562_set_right_speaker_switch),
SOC_ENUM_EXT("TAS2562 ICN Switch", icn_sw_enum[0],
tas2562_get_icn_switch,
tas2562_set_icn_switch),
};
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
static const struct snd_soc_component_driver soc_codec_driver_tas2562 = {
.probe = tas2562_codec_probe,
.remove = tas2562_codec_remove,
.read = tas2562_codec_read,
.write = tas2562_codec_write,
#ifdef CODEC_PM
.suspend = tas2562_codec_suspend,
.resume = tas2562_codec_resume,
#endif
.controls = tas2562_snd_controls,
.num_controls = ARRAY_SIZE(tas2562_snd_controls),
.dapm_widgets = tas2562_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(tas2562_dapm_widgets),
.dapm_routes = tas2562_audio_map,
.num_dapm_routes = ARRAY_SIZE(tas2562_audio_map),
};
#else
static struct snd_soc_codec_driver soc_codec_driver_tas2562 = {
.probe = tas2562_codec_probe,
.remove = tas2562_codec_remove,
.read = tas2562_codec_read,
.write = tas2562_codec_write,
#ifdef CODEC_PM
.suspend = tas2562_codec_suspend,
.resume = tas2562_codec_resume,
#endif
.component_driver = {
.controls = tas2562_snd_controls,
.num_controls = ARRAY_SIZE(tas2562_snd_controls),
.dapm_widgets = tas2562_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(tas2562_dapm_widgets),
.dapm_routes = tas2562_audio_map,
.num_dapm_routes = ARRAY_SIZE(tas2562_audio_map),
},
};
#endif
int tas2562_register_codec(struct tas2562_priv *p_tas2562)
{
int n_result = 0;
dev_info(p_tas2562->dev, "%s, enter\n", __func__);
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
n_result = devm_snd_soc_register_component(p_tas2562->dev,
&soc_codec_driver_tas2562,
tas2562_dai_driver, ARRAY_SIZE(tas2562_dai_driver));
#else
n_result = snd_soc_register_codec(p_tas2562->dev,
&soc_codec_driver_tas2562,
tas2562_dai_driver, ARRAY_SIZE(tas2562_dai_driver));
#endif
#ifdef CONFIG_TAS25XX_ALGO
smartamp_add_algo(p_tas2562->mn_channels);
tas_calib_init();
#endif /*CONFIG_TAS25XX_ALGO*/
return n_result;
}
int tas2562_deregister_codec(struct tas2562_priv *p_tas2562)
{
#ifdef CONFIG_TAS25XX_ALGO
smartamp_remove_algo();
#endif /*CONFIG_TAS25XX_ALGO*/
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
snd_soc_unregister_component(p_tas2562->dev);
#else
snd_soc_unregister_codec(p_tas2562->dev);
#endif
return 0;
}
void tas2562_load_config(struct tas2562_priv *p_tas2562)
{
int ret = 0;
p_tas2562->hw_reset(p_tas2562);
msleep(20);
p_tas2562->write(p_tas2562, channel_both, TAS2562_SOFTWARERESET,
TAS2562_SOFTWARERESET_SOFTWARERESET_RESET);
msleep(20);
ret = tas2562_iv_slot_config(p_tas2562);
if (ret < 0)
goto end;
tas2562_load_init(p_tas2562);
tas2562_iv_enable(p_tas2562, tas2562iv_enable);
ret = tas2562_set_slot(p_tas2562, p_tas2562->mn_slot_width);
if (ret < 0)
goto end;
ret = tas2562_set_fmt(p_tas2562, p_tas2562->mn_asi_format);
if (ret < 0)
goto end;
ret = tas2562_set_bitwidth(p_tas2562, p_tas2562->mn_pcm_format);
if (ret < 0)
goto end;
ret = tas2562_set_samplerate(p_tas2562, p_tas2562->mn_sampling_rate);
if (ret < 0)
goto end;
ret = irq_tas2562_power_up(p_tas2562, channel_both);
if (ret < 0)
goto end;
end:
/* power up failed, restart later */
if (ret < 0)
schedule_delayed_work(&p_tas2562->irq_work,
msecs_to_jiffies(1000));
}
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_DESCRIPTION("TAS2562 ALSA SOC Smart Amplifier driver");
MODULE_LICENSE("GPL v2");
#endif /* CONFIG_TAS2562_CODEC */