From 720af089a66ca7e516a594df0c4d0ad2ecfce139 Mon Sep 17 00:00:00 2001 From: liaohj Date: Mon, 5 Aug 2019 10:21:41 +0800 Subject: [PATCH] sound/codecs:add ti2558 driver sofiar use smart pa ti2558, add kernel drivers. Change-Id: I02b490c17e96224d2fd9510d3e214900a4172b4a Reviewed-on: https://gerrit.mot.com/1396120 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Yuesheng Huang Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- sound/soc/codecs/Android.mk | 6 + sound/soc/codecs/Kbuild | 8 + sound/soc/codecs/tas2562-codec.c | 1098 ++++++++++++++++++++++++++++ sound/soc/codecs/tas2562-codec.h | 31 + sound/soc/codecs/tas2562-misc.c | 259 +++++++ sound/soc/codecs/tas2562-misc.h | 38 + sound/soc/codecs/tas2562-regmap.c | 1135 +++++++++++++++++++++++++++++ sound/soc/codecs/tas2562.h | 655 +++++++++++++++++ sound/soc/codecs/tas2562.txt | 45 ++ 9 files changed, 3275 insertions(+) create mode 100644 sound/soc/codecs/tas2562-codec.c create mode 100644 sound/soc/codecs/tas2562-codec.h create mode 100644 sound/soc/codecs/tas2562-misc.c create mode 100644 sound/soc/codecs/tas2562-misc.h create mode 100644 sound/soc/codecs/tas2562-regmap.c create mode 100644 sound/soc/codecs/tas2562.h create mode 100644 sound/soc/codecs/tas2562.txt diff --git a/sound/soc/codecs/Android.mk b/sound/soc/codecs/Android.mk index 4a8df2c8e806..09d8e05112bf 100644 --- a/sound/soc/codecs/Android.mk +++ b/sound/soc/codecs/Android.mk @@ -24,3 +24,9 @@ LOCAL_MODULE := tas2560_calib.ko LOCAL_MODULE_TAGS := optional LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := tas2562.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/sound/soc/codecs/Kbuild b/sound/soc/codecs/Kbuild index 33d5ce390629..a7d85a299b74 100644 --- a/sound/soc/codecs/Kbuild +++ b/sound/soc/codecs/Kbuild @@ -19,6 +19,11 @@ ifneq (,$(filter channel%, $(TARGET_PRODUCT))) EXTRA_CFLAGS += -DCONFIG_TAS2560_REGMAP -DCONFIG_TAS2560_CODEC -DCONFIG_TAS2560_MISC -DCONFIG_SND_SOC_TAS2560 endif +ifneq (,$(filter sofiar%, $(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_TAS2562_REGMAP -DCONFIG_TAS2562_CODEC -DCONFIG_SND_SOC_TAS2562 -DTAS2558_CODEC +endif + + ifeq ($(KDIR),) PLMA_HOSTSW_DIR=$(realpath -f $(srctree)/$(src)) else @@ -49,3 +54,6 @@ obj-m += tas2560_algo.o tas2560_calib-objs = tas2560-calib.o obj-m += tas2560_calib.o + +tas2562-objs = tas2562-codec.o tas2562-regmap.o +obj-m += tas2562.o diff --git a/sound/soc/codecs/tas2562-codec.c b/sound/soc/codecs/tas2562-codec.c new file mode 100644 index 000000000000..b18220ded9c3 --- /dev/null +++ b/sound/soc/codecs/tas2562-codec.c @@ -0,0 +1,1098 @@ +/* + * ============================================================================= + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2562.h" + +#define TAS2562_MDELAY 0xFFFFFFFE +#define TAS2562_MSLEEP 0xFFFFFFFD +#define TAS2562_IVSENSER_ENABLE 1 +#define TAS2562_IVSENSER_DISABLE 0 +/* #define TAS2558_CODEC */ + +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 tas2562_codec_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + dev_err(p_tas2562->dev, "Should not get here\n"); + + return 0; +} + +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) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + int iv_enable = 0, n_result = 0; + + if (codec == NULL) { + pr_err("%s:codec 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) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + int ret, value; + + 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_err(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), +}; + +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_err(p_tas2562->dev, "Should not get here\n"); + + return 0; +} + +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; +} + +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; +} + +static int tas2562_set_power_state(struct tas2562_priv *p_tas2562, + enum channel chn, int state) +{ + int n_result = 0; + int irqreg; + /*unsigned int n_value;*/ + + if ((p_tas2562->mb_mute) && (state == TAS2562_POWER_ACTIVE)) + state = TAS2562_POWER_MUTE; + dev_err(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; + +/* Clear latched IRQ before power on */ + + p_tas2562->update_bits(p_tas2562, chn, + TAS2562_INTERRUPTCONFIGURATION, + TAS2562_INTERRUPTCONFIGURATION_LTCHINTCLEAR_MASK, + TAS2562_INTERRUPTCONFIGURATION_LTCHINTCLEAR); + + p_tas2562->read(p_tas2562, channel_left, + TAS2562_LATCHEDINTERRUPTREG0, &irqreg); + dev_info(p_tas2562->dev, "IRQ reg is: %s %d, %d\n", + __func__, irqreg, __LINE__); + p_tas2562->read(p_tas2562, channel_right, + TAS2562_LATCHEDINTERRUPTREG0, &irqreg); + dev_info(p_tas2562->dev, "IRQ reg is: %s %d, %d\n", + __func__, irqreg, __LINE__); + + + p_tas2562->mb_power_up = true; + p_tas2562->mn_power_state = TAS2562_POWER_ACTIVE; + schedule_delayed_work(&p_tas2562->irq_work, + msecs_to_jiffies(10)); + 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: + + n_result = p_tas2562->update_bits(p_tas2562, chn, + TAS2562_POWERCONTROL, + TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK, + TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN); + p_tas2562->mb_power_up = false; + p_tas2562->mn_power_state = TAS2562_POWER_SHUTDOWN; + msleep(20); + p_tas2562->enable_irq(p_tas2562, false); + + 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) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + 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_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) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + 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) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + 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) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + 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) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + 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) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + enum channel chn; + + 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, + 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) { + 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)) { + 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_channels == 1) + && (p_tas2562->mn_slot_width == 16)) { + 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 { + dev_err(p_tas2562->dev, "%s, wrong params, %d\n", + __func__, p_tas2562->mn_slot_width); + } + + 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); + + 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) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + 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) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + 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) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + 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) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + 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) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + /* 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 + p_tas2562->mn_slot_width) << + 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) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + 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; + struct snd_soc_codec *codec = dai->codec; + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(p_tas2562->dev, "%s, tx_mask:%d, rx_mask:%d, slots:%d, slot_width:%d", + __func__, tx_mask, rx_mask, slots, slot_width); + + ret = tas2562_set_slot(p_tas2562, slot_width); + + return ret; +} + +static struct snd_soc_dai_ops tas2562_dai_ops = { + .digital_mute = 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; + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_TDMConfigurationReg4, 0x01); + if (ret < 0) + return ret; + 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); + + return ret; +} + +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_err(p_tas2562->dev, "%s\n", __func__); + + return 0; +} + +static int tas2562_codec_remove(struct snd_soc_codec *codec) +{ + return 0; +} + +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 struct snd_kcontrol_new tas2562_snd_controls[] = { + SOC_SINGLE_TLV("Amp Output Level", TAS2562_PLAYBACKCONFIGURATIONREG0, + 0, 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_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), +}; + +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, + .suspend = tas2562_codec_suspend, + .resume = tas2562_codec_resume, + .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), + }, +}; + +int tas2562_register_codec(struct tas2562_priv *p_tas2562) +{ + int n_result = 0; + + dev_info(p_tas2562->dev, "%s, enter\n", __func__); + n_result = snd_soc_register_codec(p_tas2562->dev, + &soc_codec_driver_tas2562, + tas2562_dai_driver, ARRAY_SIZE(tas2562_dai_driver)); + return n_result; +} + +int tas2562_deregister_codec(struct tas2562_priv *p_tas2562) +{ + snd_soc_unregister_codec(p_tas2562->dev); + + 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 = tas2562_set_power_state(p_tas2562, channel_both, + p_tas2562->mn_power_state); + 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 */ diff --git a/sound/soc/codecs/tas2562-codec.h b/sound/soc/codecs/tas2562-codec.h new file mode 100644 index 000000000000..7b4447208b7d --- /dev/null +++ b/sound/soc/codecs/tas2562-codec.h @@ -0,0 +1,31 @@ +/* + * ============================================================================= + * 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.h + * + * Description: + * header file for tas2562-codec.c + * + * ============================================================================= + */ + +#ifndef _TAS2562_CODEC_H +#define _TAS2562_CODEC_H + +#include "tas2562.h" + +int tas2562_register_codec(struct tas2562_priv *p_tas2562); +int tas2562_deregister_codec(struct tas2562_priv *p_tas2562); +int tas2562_load_config(struct tas2562_priv *p_tas2562); + +#endif /* _TAS2562_CODEC_H */ diff --git a/sound/soc/codecs/tas2562-misc.c b/sound/soc/codecs/tas2562-misc.c new file mode 100644 index 000000000000..79677aaea4d3 --- /dev/null +++ b/sound/soc/codecs/tas2562-misc.c @@ -0,0 +1,259 @@ +/* + * ============================================================================= + * 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-misc.c + * + * Description: + * misc driver for Texas Instruments + * TAS2562 High Performance 4W Smart Amplifier + * + * ============================================================================= + */ + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2562.h" +#include "tas2562-misc.h" +#include + +static int g_log_enable = 1; +static struct tas2562_priv *g_tas2562; + +static int tas2562_file_open(struct inode *inode, struct file *file) +{ + struct tas2562_priv *p_tas2562 = g_tas2562; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + file->private_data = (void *)p_tas2562; + + if (g_log_enable) + dev_info(p_tas2562->dev, "%s\n", __func__); + return 0; +} + +static int tas2562_file_release(struct inode *inode, struct file *file) +{ + struct tas2562_priv *p_tas2562 = + (struct tas2562_priv *)file->private_data; + + if (g_log_enable) + dev_info(p_tas2562->dev, "%s\n", __func__); + + file->private_data = (void *)NULL; + module_put(THIS_MODULE); + + return 0; +} + +static ssize_t tas2562_file_read(struct file *file, + char *buf, size_t count, loff_t *ppos) +{ + struct tas2562_priv *p_tas2562 = + (struct tas2562_priv *)file->private_data; + int ret = 0; + unsigned int n_value = 0; + unsigned char value = 0; + unsigned char *p_kbuf = NULL; + + mutex_lock(&p_tas2562->file_lock); + + switch (p_tas2562->mn_dbg_cmd) { + case TIAUDIO_CMD_REG_READ: + { + if (g_log_enable) + dev_info(p_tas2562->dev, + "TIAUDIO_CMD_REG_READ: current_reg = 0x%x, count=%d\n", + p_tas2562->mn_current_reg, (int)count); + if (count == 1) { + ret = p_tas2562->read(p_tas2562, + p_tas2562->mn_current_reg, &n_value); + if (ret < 0) { + dev_err(p_tas2562->dev, + "dev read fail %d\n", ret); + break; + } + + value = (u8)n_value; + if (g_log_enable) + dev_info(p_tas2562->dev, "TIAUDIO_CMD_REG_READ: n_value=0x%x, value=0x%x\n", + n_value, value); + ret = copy_to_user(buf, &value, 1); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(p_tas2562->dev, + "copy to user fail %d\n", ret); + } + } else if (count > 1) { + p_kbuf = kzalloc(count, GFP_KERNEL); + if (p_kbuf != NULL) { + ret = p_tas2562->bulk_read(p_tas2562, + p_tas2562->mn_current_reg, p_kbuf, count); + if (ret < 0) { + dev_err(p_tas2562->dev, + "dev bulk read fail %d\n", ret); + } else { + ret = copy_to_user(buf, p_kbuf, count); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(p_tas2562->dev, + "copy to user fail %d\n", ret); + } + } + + kfree(p_kbuf); + } else { + dev_err(p_tas2562->dev, "read no mem\n"); + } + } + } + break; + } + p_tas2562->mn_dbg_cmd = 0; + + mutex_unlock(&p_tas2562->file_lock); + return count; +} + +static ssize_t tas2562_file_write(struct file *file, + const char *buf, size_t count, loff_t *ppos) +{ + struct tas2562_priv *p_tas2562 = + (struct tas2562_priv *)file->private_data; + int ret = 0; + unsigned char *p_kbuf = NULL; + unsigned int reg = 0; + unsigned int len = 0; + + mutex_lock(&p_tas2562->file_lock); + + p_kbuf = kzalloc(count, GFP_KERNEL); + if (p_kbuf == NULL) { + /* dev_err(p_tas2562->dev, "write no mem\n"); */ + goto err; + } + + ret = copy_from_user(p_kbuf, buf, count); + if (ret != 0) { + dev_err(p_tas2562->dev, "copy_from_user failed.\n"); + goto err; + } + + p_tas2562->mn_dbg_cmd = p_kbuf[0]; + switch (p_tas2562->mn_dbg_cmd) { + case TIAUDIO_CMD_REG_WITE: + if (count > 5) { + reg = ((unsigned int)p_kbuf[1] << 24) + + ((unsigned int)p_kbuf[2] << 16) + + ((unsigned int)p_kbuf[3] << 8) + + (unsigned int)p_kbuf[4]; + len = count - 5; + if (len == 1) { + ret = p_tas2562->write(p_tas2562, reg, p_kbuf[5]); + if (g_log_enable) + dev_info(p_tas2562->dev, + "TIAUDIO_CMD_REG_WITE, Reg=0x%x, Val=0x%x\n", + reg, p_kbuf[5]); + } else { + ret = p_tas2562->bulk_write(p_tas2562, + reg, &p_kbuf[5], len); + } + } else { + dev_err(p_tas2562->dev, "%s, write len fail, count=%d.\n", + __func__, (int)count); + } + p_tas2562->mn_dbg_cmd = 0; + break; + + case TIAUDIO_CMD_REG_READ: + if (count == 5) { + p_tas2562->mn_current_reg = ((unsigned int)p_kbuf[1] << 24) + + ((unsigned int)p_kbuf[2] << 16) + + ((unsigned int)p_kbuf[3] << 8) + + (unsigned int)p_kbuf[4]; + if (g_log_enable) { + dev_info(p_tas2562->dev, + "TIAUDIO_CMD_REG_READ, whole=0x%x\n", + p_tas2562->mn_current_reg); + } + } else { + dev_err(p_tas2562->dev, "read len fail.\n"); + } + break; + } +err: + if (p_kbuf != NULL) + kfree(p_kbuf); + + mutex_unlock(&p_tas2562->file_lock); + + return count; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .read = tas2562_file_read, + .write = tas2562_file_write, + .unlocked_ioctl = NULL, + .open = tas2562_file_open, + .release = tas2562_file_release, +}; + +#define MODULE_NAME "tas2562" +static struct miscdevice tas2562_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = MODULE_NAME, + .fops = &fops, +}; + +int tas2562_register_misc(struct tas2562_priv *p_tas2562) +{ + int ret = 0; + + g_tas2562 = p_tas2562; + ret = misc_register(&tas2562_misc); + if (ret) + dev_err(p_tas2562->dev, "TAS2562 misc fail: %d\n", ret); + + dev_info(p_tas2562->dev, "%s, leave\n", __func__); + + return ret; +} + +int tas2562_deregister_misc(struct tas2562_priv *p_tas2562) +{ + misc_deregister(&tas2562_misc); + return 0; +} + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TAS2562 Misc Smart Amplifier driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas2562-misc.h b/sound/soc/codecs/tas2562-misc.h new file mode 100644 index 000000000000..18fa8c391204 --- /dev/null +++ b/sound/soc/codecs/tas2562-misc.h @@ -0,0 +1,38 @@ +/* + * ============================================================================= + * 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-misc.h + * + * Description: + * header file for tas2562-misc.c + * + * ============================================================================= + */ + +#ifndef _TAS2562_MISC_H +#define _TAS2562_MISC_H + +#define TIAUDIO_CMD_REG_WITE 1 +#define TIAUDIO_CMD_REG_READ 2 +#define TIAUDIO_CMD_DEBUG_ON 3 +#define TIAUDIO_CMD_CALIBRATION 7 +#define TIAUDIO_CMD_SAMPLERATE 8 +#define TIAUDIO_CMD_BITRATE 9 +#define TIAUDIO_CMD_DACVOLUME 10 +#define TIAUDIO_CMD_SPEAKER 11 + +int tas2562_register_misc(struct tas2562_priv *p_tas2562); +int tas2562_deregister_misc(struct tas2562_priv *p_tas2562); + +#endif /* _TAS2562_MISC_H */ diff --git a/sound/soc/codecs/tas2562-regmap.c b/sound/soc/codecs/tas2562-regmap.c new file mode 100644 index 000000000000..61de704919e6 --- /dev/null +++ b/sound/soc/codecs/tas2562-regmap.c @@ -0,0 +1,1135 @@ +/* + * ALSA SoC Texas Instruments TAS2562 High Performance 4W Smart Amplifier + * + * Copyright (C) 2016 Texas Instruments, Inc. + * + * Author: saiprasad + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ +#ifdef CONFIG_TAS2562_REGMAP + +#define DEBUG 5 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2562.h" +#include "tas2562-codec.h" +#include "tas2562-misc.h" +static char p_icn[] = {0x00, 0x03, 0x46, 0xdc}; + +static int tas2562_regmap_write(struct tas2562_priv *p_tas2562, + unsigned int reg, unsigned int value) +{ + int nResult = 0; + int retry_count = TAS2562_I2C_RETRY_COUNT; + + if(p_tas2562->i2c_suspend) + return ERROR_I2C_SUSPEND; + + while(retry_count--) + { + nResult = regmap_write(p_tas2562->regmap, reg, + value); + if (nResult >= 0) + break; + msleep(20); + } + if(retry_count == -1) + return ERROR_I2C_FAILED; + else + return 0; +} + +static int tas2562_regmap_bulk_write(struct tas2562_priv *p_tas2562, + unsigned int reg, unsigned char *pData, unsigned int nLength) +{ + int nResult = 0; + int retry_count = TAS2562_I2C_RETRY_COUNT; + + if(p_tas2562->i2c_suspend) + return ERROR_I2C_SUSPEND; + + while(retry_count --) + { + nResult = regmap_bulk_write(p_tas2562->regmap, reg, + pData, nLength); + if (nResult >= 0) + break; + msleep(20); + } + if(retry_count == -1) + return ERROR_I2C_FAILED; + else + return 0; +} + +static int tas2562_regmap_read(struct tas2562_priv *p_tas2562, + unsigned int reg, unsigned int *value) +{ + int nResult = 0; + int retry_count = TAS2562_I2C_RETRY_COUNT; + + if(p_tas2562->i2c_suspend) + return ERROR_I2C_SUSPEND; + + while(retry_count --) + { + nResult = regmap_read(p_tas2562->regmap, reg, + value); + if (nResult >= 0) + break; + msleep(20); + } + if(retry_count == -1) + return ERROR_I2C_FAILED; + else + return 0; +} + +static int tas2562_regmap_bulk_read(struct tas2562_priv *p_tas2562, + unsigned int reg, unsigned char *pData, unsigned int nLength) +{ + int nResult = 0; + int retry_count = TAS2562_I2C_RETRY_COUNT; + + if(p_tas2562->i2c_suspend) + return ERROR_I2C_SUSPEND; + + while(retry_count --) + { + nResult = regmap_bulk_read(p_tas2562->regmap, reg, + pData, nLength); + if (nResult >= 0) + break; + msleep(20); + } + if(retry_count == -1) + return ERROR_I2C_FAILED; + else + return 0; +} + +static int tas2562_regmap_update_bits(struct tas2562_priv *p_tas2562, + unsigned int reg, unsigned int mask, unsigned int value) +{ + int nResult = 0; + int retry_count = TAS2562_I2C_RETRY_COUNT; + + if(p_tas2562->i2c_suspend) + return ERROR_I2C_SUSPEND; + + while(retry_count--) + { + nResult = regmap_update_bits(p_tas2562->regmap, reg, + mask, value); + if (nResult >= 0) + break; + msleep(20); + } + if(retry_count == -1) + return ERROR_I2C_FAILED; + else + return 0; +} + +static int tas2562_change_book_page(struct tas2562_priv *p_tas2562, + enum channel chn, + int book, int page) +{ + int n_result = 0; + + + if ((chn&channel_left) || (p_tas2562->mn_channels == 1)) { + p_tas2562->client->addr = p_tas2562->mn_l_addr; + if (p_tas2562->mn_l_current_book != book) { + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_BOOKCTL_PAGE, 0); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + goto end; + } + p_tas2562->mn_l_current_page = 0; + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_BOOKCTL_REG, book); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + goto end; + } + p_tas2562->mn_l_current_book = book; + } + + if (p_tas2562->mn_l_current_page != page) { + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_BOOKCTL_PAGE, page); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + goto end; + } + p_tas2562->mn_l_current_page = page; + } + } + + if ((chn&channel_right) && (p_tas2562->mn_channels == 2)) { + p_tas2562->client->addr = p_tas2562->mn_r_addr; + if (p_tas2562->mn_r_current_book != book) { + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_BOOKCTL_PAGE, 0); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + goto end; + } + p_tas2562->mn_r_current_page = 0; + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_BOOKCTL_REG, book); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + goto end; + } + p_tas2562->mn_r_current_book = book; + } + + if (p_tas2562->mn_r_current_page != page) { + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_BOOKCTL_PAGE, page); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + goto end; + } + p_tas2562->mn_r_current_page = page; + } + } + +end: + return n_result; +} + +static int tas2562_dev_read(struct tas2562_priv *p_tas2562, + enum channel chn, + unsigned int reg, unsigned int *pValue) +{ + int n_result = 0; + + mutex_lock(&p_tas2562->dev_lock); + + n_result = tas2562_change_book_page(p_tas2562, chn, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); + if (n_result < 0) + goto end; + + if ((chn == channel_left) || (p_tas2562->mn_channels == 1)) + p_tas2562->client->addr = p_tas2562->mn_l_addr; + else if (chn == channel_right) + p_tas2562->client->addr = p_tas2562->mn_r_addr; + else + dev_err(p_tas2562->dev, "%s, wrong channel number\n", __func__); + + n_result = tas2562_regmap_read(p_tas2562, + TAS2562_PAGE_REG(reg), pValue); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, + "%s: chn:%x:BOOK:PAGE:REG %u:%u:%u,0x%x\n", __func__, + p_tas2562->client->addr, TAS2562_BOOK_ID(reg), + TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), *pValue); + +end: + mutex_unlock(&p_tas2562->dev_lock); + return n_result; +} + +static int tas2562_dev_write(struct tas2562_priv *p_tas2562, enum channel chn, + unsigned int reg, unsigned int value) +{ + int n_result = 0; + + mutex_lock(&p_tas2562->dev_lock); + + n_result = tas2562_change_book_page(p_tas2562, chn, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); + if (n_result < 0) + goto end; + + if ((chn&channel_left) || (p_tas2562->mn_channels == 1)) { + p_tas2562->client->addr = p_tas2562->mn_l_addr; + + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_PAGE_REG(reg), value); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, + "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, VAL: 0x%02x\n", + __func__, p_tas2562->client->addr, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), value); + } + + if ((chn&channel_right) && (p_tas2562->mn_channels == 2)) { + p_tas2562->client->addr = p_tas2562->mn_r_addr; + + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_PAGE_REG(reg), + value); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, VAL: 0x%02x\n", + __func__, p_tas2562->client->addr, + TAS2562_BOOK_ID(reg), + TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), value); + } + +end: + mutex_unlock(&p_tas2562->dev_lock); + return n_result; +} + +static int tas2562_dev_bulk_write(struct tas2562_priv *p_tas2562, + enum channel chn, + unsigned int reg, unsigned char *p_data, unsigned int n_length) +{ + int n_result = 0; + + mutex_lock(&p_tas2562->dev_lock); + + n_result = tas2562_change_book_page(p_tas2562, chn, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); + if (n_result < 0) + goto end; + + if ((chn&channel_left) || (p_tas2562->mn_channels == 1)) { + p_tas2562->client->addr = p_tas2562->mn_l_addr; + n_result = tas2562_regmap_bulk_write(p_tas2562, + TAS2562_PAGE_REG(reg), p_data, n_length); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n", + __func__, p_tas2562->client->addr, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), n_length); + } + + if ((chn&channel_right) && (p_tas2562->mn_channels == 2)) { + p_tas2562->client->addr = p_tas2562->mn_r_addr; + n_result = tas2562_regmap_bulk_write(p_tas2562, + TAS2562_PAGE_REG(reg), p_data, n_length); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, "%s: %x:BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n", + __func__, p_tas2562->client->addr, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), n_length); + } + +end: + mutex_unlock(&p_tas2562->dev_lock); + return n_result; +} + +static int tas2562_dev_bulk_read(struct tas2562_priv *p_tas2562, + enum channel chn, + unsigned int reg, unsigned char *p_data, unsigned int n_length) +{ + int n_result = 0; + + mutex_lock(&p_tas2562->dev_lock); + + if ((chn == channel_left) || (p_tas2562->mn_channels == 1)) + p_tas2562->client->addr = p_tas2562->mn_l_addr; + else if (chn == channel_right) + p_tas2562->client->addr = p_tas2562->mn_r_addr; + else + dev_err(p_tas2562->dev, "%s, wrong channel number\n", __func__); + + n_result = tas2562_change_book_page(p_tas2562, chn, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); + if (n_result < 0) + goto end; + + n_result = tas2562_regmap_bulk_read(p_tas2562, + TAS2562_PAGE_REG(reg), p_data, n_length); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n", + __func__, p_tas2562->client->addr, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), n_length); +end: + mutex_unlock(&p_tas2562->dev_lock); + return n_result; +} + +static int tas2562_dev_update_bits(struct tas2562_priv *p_tas2562, + enum channel chn, + unsigned int reg, unsigned int mask, unsigned int value) +{ + int n_result = 0; + + mutex_lock(&p_tas2562->dev_lock); + n_result = tas2562_change_book_page(p_tas2562, chn, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); + if (n_result < 0) + goto end; + + if ((chn&channel_left) || (p_tas2562->mn_channels == 1)) { + p_tas2562->client->addr = p_tas2562->mn_l_addr; + n_result = tas2562_regmap_update_bits(p_tas2562, + TAS2562_PAGE_REG(reg), mask, value); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, + "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, mask: 0x%x, val: 0x%x\n", + __func__, p_tas2562->client->addr, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), mask, value); + } + + if ((chn&channel_right) && (p_tas2562->mn_channels == 2)) { + p_tas2562->client->addr = p_tas2562->mn_r_addr; + n_result = tas2562_regmap_update_bits(p_tas2562, + TAS2562_PAGE_REG(reg), mask, value); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, + "%s:chn%x:BOOK:PAGE:REG %u:%u:%u,mask: 0x%x, val: 0x%x\n", + __func__, p_tas2562->client->addr, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), mask, value); + } + +end: + mutex_unlock(&p_tas2562->dev_lock); + return n_result; +} + +static bool tas2562_volatile(struct device *dev, unsigned int reg) +{ + return true; +} + +static bool tas2562_writeable(struct device *dev, unsigned int reg) +{ + return true; +} +static const struct regmap_config tas2562_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = tas2562_writeable, + .volatile_reg = tas2562_volatile, + .cache_type = REGCACHE_NONE, + .max_register = 1 * 128, +}; + + +static void tas2562_hw_reset(struct tas2562_priv *p_tas2562) +{ + if (gpio_is_valid(p_tas2562->mn_reset_gpio)) { + gpio_direction_output(p_tas2562->mn_reset_gpio, 0); + gpio_direction_output(p_tas2562->mn_reset_gpio2, 0); + msleep(20); + gpio_direction_output(p_tas2562->mn_reset_gpio, 1); + gpio_direction_output(p_tas2562->mn_reset_gpio2, 1); + msleep(20); + } + dev_err(p_tas2562->dev, "gpio up !!\n"); + + p_tas2562->mn_l_current_book = -1; + p_tas2562->mn_l_current_page = -1; + p_tas2562->mn_r_current_book = -1; + p_tas2562->mn_r_current_page = -1; +} + +void tas2562_enable_irq(struct tas2562_priv *p_tas2562, bool enable) +{ + static int irq1_enabled = 0; + static int irq2_enabled = 0; + + dev_info(p_tas2562->dev, "%s: irq enable %d\n", __func__, enable); + if (enable) { + if (p_tas2562->mb_irq_eable) + return; + + if (gpio_is_valid(p_tas2562->mn_irq_gpio) && irq1_enabled == 0) { + enable_irq(p_tas2562->mn_irq); + irq1_enabled = 1; + } + if (gpio_is_valid(p_tas2562->mn_irq_gpio2) && irq2_enabled == 0) { + enable_irq(p_tas2562->mn_irq2); + irq2_enabled = 1; + } + + p_tas2562->mb_irq_eable = true; + } else { + if (gpio_is_valid(p_tas2562->mn_irq_gpio) && irq1_enabled == 1) { + disable_irq_nosync(p_tas2562->mn_irq); + irq1_enabled = 0; + } + if (gpio_is_valid(p_tas2562->mn_irq_gpio2) && irq2_enabled == 1) { + disable_irq_nosync(p_tas2562->mn_irq2); + irq2_enabled = 0; + } + p_tas2562->mb_irq_eable = false; + } +} + +static void irq_work_routine(struct work_struct *work) +{ + struct tas2562_priv *p_tas2562 = + container_of(work, struct tas2562_priv, irq_work.work); + unsigned int nDevInt1Status = 0, nDevInt2Status = 0, + nDevInt3Status = 0, nDevInt4Status = 0; + int n_counter = 2; + int n_result = 0; + int irqreg; + enum channel chn; + + dev_info(p_tas2562->dev, "%s\n", __func__); +#ifdef CONFIG_TAS2562_CODEC + mutex_lock(&p_tas2562->codec_lock); +#endif + tas2562_enable_irq(p_tas2562, false); + + if (p_tas2562->mb_runtime_suspend) { + dev_info(p_tas2562->dev, "%s, Runtime Suspended\n", __func__); + goto end; + } + + if (p_tas2562->mn_power_state == TAS2562_POWER_SHUTDOWN) { + dev_info(p_tas2562->dev, "%s, device not powered\n", __func__); + goto end; + } + + 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); + + if (n_result < 0) + goto reload; + + 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 (chn & channel_left) + n_result = p_tas2562->read(p_tas2562, channel_left, + TAS2562_LATCHEDINTERRUPTREG0, &nDevInt1Status); + if (n_result >= 0) + n_result = p_tas2562->read(p_tas2562, channel_left, + TAS2562_LATCHEDINTERRUPTREG1, &nDevInt2Status); + else + goto reload; + + if (chn & channel_right) + n_result = p_tas2562->read(p_tas2562, channel_right, + TAS2562_LATCHEDINTERRUPTREG0, &nDevInt3Status); + if (n_result >= 0) + n_result = p_tas2562->read(p_tas2562, channel_right, + TAS2562_LATCHEDINTERRUPTREG1, &nDevInt4Status); + else + goto reload; + + dev_dbg(p_tas2562->dev, "IRQ status : 0x%x, 0x%x, 0x%x, 0x%x\n", + nDevInt3Status, nDevInt4Status, + nDevInt3Status, nDevInt4Status); + + if (((nDevInt1Status & 0x7) != 0) + || ((nDevInt2Status & 0x0f) != 0) || + ((nDevInt3Status & 0x7) != 0) + || ((nDevInt4Status & 0x0f) != 0)) { +/* in case of INT_CLK, INT_OC, INT_OT, + * INT_OVLT, INT_UVLT, INT_BO + */ + + if ((nDevInt1Status & + TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_INTERRUPT) || + (nDevInt3Status & + TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_INTERRUPT)) { + p_tas2562->mn_err_code |= ERROR_CLOCK; + dev_err(p_tas2562->dev, "TDM clock error!\n"); + } else + p_tas2562->mn_err_code &= ~ERROR_OVER_CURRENT; + + if ((nDevInt1Status & + TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_INTERRUPT) || + (nDevInt3Status & + TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_INTERRUPT)) { + p_tas2562->mn_err_code |= ERROR_OVER_CURRENT; + dev_err(p_tas2562->dev, "SPK over current!\n"); + } else + p_tas2562->mn_err_code &= ~ERROR_OVER_CURRENT; + + if ((nDevInt1Status & + TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_INTERRUPT) || + (nDevInt3Status & + TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_INTERRUPT)) { + p_tas2562->mn_err_code |= ERROR_DIE_OVERTEMP; + dev_err(p_tas2562->dev, "die over temperature!\n"); + } else + p_tas2562->mn_err_code &= ~ERROR_DIE_OVERTEMP; + + if ((nDevInt2Status & + TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_INTERRUPT) || + (nDevInt4Status & + TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_INTERRUPT)) { + p_tas2562->mn_err_code |= ERROR_OVER_VOLTAGE; + dev_err(p_tas2562->dev, "SPK over voltage!\n"); + } else + p_tas2562->mn_err_code &= ~ERROR_UNDER_VOLTAGE; + + if ((nDevInt2Status & + TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_INTERRUPT) || + (nDevInt4Status & + TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_INTERRUPT)) { + p_tas2562->mn_err_code |= ERROR_UNDER_VOLTAGE; + dev_err(p_tas2562->dev, "SPK under voltage!\n"); + } else + p_tas2562->mn_err_code &= ~ERROR_UNDER_VOLTAGE; + + if ((nDevInt2Status & + TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_INTERRUPT) || + (nDevInt4Status & + TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_INTERRUPT)) { + p_tas2562->mn_err_code |= ERROR_BROWNOUT; + dev_err(p_tas2562->dev, "brownout!\n"); + } else + p_tas2562->mn_err_code &= ~ERROR_BROWNOUT; + + goto reload; + } else { + n_counter = 2; + + while (n_counter > 0) { + if (chn & channel_left) + n_result = p_tas2562->read(p_tas2562, channel_left, + TAS2562_POWERCONTROL, &nDevInt1Status); + if (n_result < 0) + goto reload; + if (chn && channel_right) + n_result = p_tas2562->read(p_tas2562, channel_right, + TAS2562_POWERCONTROL, &nDevInt3Status); + if (n_result < 0) + goto reload; + + if ((nDevInt1Status + & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) + != TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) { + /* If only left should be power on */ + if (chn == channel_left) + break; + /* If both should be power on */ + if ((nDevInt3Status + & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) + != + TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) + break; + } + /*If only right should be power on */ + else if (chn == channel_right) { + if ((nDevInt3Status + & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) + != + TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) + break; + } + + p_tas2562->read(p_tas2562, channel_left, + TAS2562_LATCHEDINTERRUPTREG0, &irqreg); + dev_info(p_tas2562->dev, "IRQ reg is: %s %d, %d\n", + __func__, irqreg, __LINE__); + p_tas2562->read(p_tas2562, channel_right, + TAS2562_LATCHEDINTERRUPTREG0, &irqreg); + dev_info(p_tas2562->dev, "IRQ reg is: %s %d, %d\n", + __func__, irqreg, __LINE__); + + n_result = p_tas2562->update_bits(p_tas2562, + chn, TAS2562_POWERCONTROL, + TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK, + TAS2562_POWERCONTROL_OPERATIONALMODE10_ACTIVE); + if (n_result < 0) + goto reload; + + dev_info(p_tas2562->dev, "set ICN to -80dB\n"); + n_result = p_tas2562->bulk_write(p_tas2562, chn, + TAS2562_ICN_REG, p_icn, 4); + + p_tas2562->read(p_tas2562, channel_left, + TAS2562_LATCHEDINTERRUPTREG0, &irqreg); + dev_info(p_tas2562->dev, "IRQ reg is: %s, %d, %d\n", + __func__, irqreg, __LINE__); + p_tas2562->read(p_tas2562, channel_right, + TAS2562_LATCHEDINTERRUPTREG0, &irqreg); + dev_info(p_tas2562->dev, "IRQ reg is: %s %d, %d\n", + __func__, irqreg, __LINE__); + + n_counter--; + if (n_counter > 0) { + /* in case check pow status just after power on TAS2562 */ + dev_dbg(p_tas2562->dev, "PowSts B: 0x%x, check again after 10ms\n", + nDevInt1Status); + msleep(20); + } + } + + if ((((nDevInt1Status + & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) + == TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) + && (chn & channel_left)) + || (((nDevInt3Status + & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) + == TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) + && (chn & channel_right))) { + dev_err(p_tas2562->dev, "%s, Critical ERROR REG[0x%x] = 0x%x\n", + __func__, + TAS2562_POWERCONTROL, + nDevInt1Status); + p_tas2562->mn_err_code |= ERROR_CLASSD_PWR; + goto reload; + } + p_tas2562->mn_err_code &= ~ERROR_CLASSD_PWR; + } + + n_result = p_tas2562->write(p_tas2562, channel_left, + TAS2562_INTERRUPTMASKREG0, 0xf8); + if (n_result < 0) + goto reload; + + n_result = p_tas2562->write(p_tas2562, channel_left, + TAS2562_INTERRUPTMASKREG1, 0xb1); + if (n_result < 0) + goto reload; + + goto end; + +reload: + /* hardware reset and reload */ + tas2562_load_config(p_tas2562); + +end: + tas2562_enable_irq(p_tas2562, true); +#ifdef CONFIG_TAS2562_CODEC + mutex_unlock(&p_tas2562->codec_lock); +#endif +} + +static void init_work_routine(struct work_struct *work) +{ + struct tas2562_priv *p_tas2562 = + container_of(work, struct tas2562_priv, init_work.work); + int nResult = 0; + //int irqreg; + //dev_info(p_tas2562->dev, "%s\n", __func__); +#ifdef CONFIG_TAS2562_CODEC + mutex_lock(&p_tas2562->codec_lock); +#endif + + p_tas2562->update_bits(p_tas2562, channel_both, TAS2562_POWERCONTROL, + TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK, + TAS2562_POWERCONTROL_OPERATIONALMODE10_ACTIVE); + + //dev_info(p_tas2562->dev, "set ICN to -80dB\n"); + nResult = p_tas2562->bulk_write(p_tas2562, channel_both, TAS2562_ICN_REG, p_icn, 4); + + nResult = gpio_get_value(p_tas2562->mn_irq_gpio); + //dev_info(p_tas2562->dev, "%s, irq GPIO state: %d\n", __func__, nResult); + +#ifdef CONFIG_TAS2562_CODEC + mutex_unlock(&p_tas2562->codec_lock); +#endif +} + +static irqreturn_t tas2562_irq_handler(int irq, void *dev_id) +{ + struct tas2562_priv *p_tas2562 = (struct tas2562_priv *)dev_id; + + /* get IRQ status after 100 ms */ + schedule_delayed_work(&p_tas2562->irq_work, msecs_to_jiffies(100)); + return IRQ_HANDLED; +} + +static int tas2562_runtime_suspend(struct tas2562_priv *p_tas2562) +{ + dev_dbg(p_tas2562->dev, "%s\n", __func__); + + p_tas2562->mb_runtime_suspend = true; + + if (delayed_work_pending(&p_tas2562->irq_work)) { + dev_dbg(p_tas2562->dev, "cancel IRQ work\n"); + cancel_delayed_work_sync(&p_tas2562->irq_work); + } + + return 0; +} + +static int tas2562_runtime_resume(struct tas2562_priv *p_tas2562) +{ + dev_dbg(p_tas2562->dev, "%s\n", __func__); + + p_tas2562->mb_runtime_suspend = false; + + return 0; +} + +static int tas2562_parse_dt(struct device *dev, struct tas2562_priv *p_tas2562) +{ + struct device_node *np = dev->of_node; + int rc = 0, ret = 0; + + rc = of_property_read_u32(np, "ti,asi-format", + &p_tas2562->mn_asi_format); + if (rc) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,asi-format", np->full_name, rc); + } else { + dev_dbg(p_tas2562->dev, "ti,asi-format=%d", + p_tas2562->mn_asi_format); + } + + rc = of_property_read_u32(np, "ti,channels", &p_tas2562->mn_channels); + if (rc) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,channels", np->full_name, rc); + } else { + dev_dbg(p_tas2562->dev, "ti,channels=%d", + p_tas2562->mn_channels); + } + + rc = of_property_read_u32(np, "ti,left-channel", &p_tas2562->mn_l_addr); + if (rc) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,left-channel", np->full_name, rc); + } else { + dev_dbg(p_tas2562->dev, "ti,left-channel=0x%x", + p_tas2562->mn_l_addr); + } + + rc = of_property_read_u32(np, "ti,right-channel", + &p_tas2562->mn_r_addr); + if (rc) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,right-channel", np->full_name, rc); + } else { + dev_dbg(p_tas2562->dev, "ti,right-channel=0x%x", + p_tas2562->mn_r_addr); + } + + p_tas2562->mn_reset_gpio = of_get_named_gpio(np, "ti,reset-gpio", 0); + if (!gpio_is_valid(p_tas2562->mn_reset_gpio)) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,reset-gpio", np->full_name, + p_tas2562->mn_reset_gpio); + } else { + dev_dbg(p_tas2562->dev, "ti,reset-gpio=%d", + p_tas2562->mn_reset_gpio); + } + + p_tas2562->mn_reset_gpio2 = of_get_named_gpio(np, "ti,reset-gpio2", 0); + if (!gpio_is_valid(p_tas2562->mn_reset_gpio2)) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,reset-gpio2", np->full_name, + p_tas2562->mn_reset_gpio2); + } else { + dev_dbg(p_tas2562->dev, "ti,reset-gpio2=%d", + p_tas2562->mn_reset_gpio2); + } + + p_tas2562->mn_irq_gpio = of_get_named_gpio(np, "ti,irq-gpio", 0); + if (!gpio_is_valid(p_tas2562->mn_irq_gpio)) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,irq-gpio", np->full_name, p_tas2562->mn_irq_gpio); + } else { + dev_dbg(p_tas2562->dev, "ti,irq-gpio=%d", + p_tas2562->mn_irq_gpio); + } + + p_tas2562->mn_irq_gpio2 = of_get_named_gpio(np, "ti,irq-gpio2", 0); + if (!gpio_is_valid(p_tas2562->mn_irq_gpio2)) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,irq-gpio2", np->full_name, p_tas2562->mn_irq_gpio2); + } else { + dev_dbg(p_tas2562->dev, "ti,irq-gpio2=%d", + p_tas2562->mn_irq_gpio2); + } + + return ret; +} + +static int tas2562_i2c_probe(struct i2c_client *p_client, + const struct i2c_device_id *id) +{ + struct tas2562_priv *p_tas2562; + int n_result; + + dev_err(&p_client->dev, "Driver ID: %s\n", TAS2562_DRIVER_ID); + dev_info(&p_client->dev, "%s enter\n", __func__); + + p_tas2562 = devm_kzalloc(&p_client->dev, + sizeof(struct tas2562_priv), GFP_KERNEL); + if (p_tas2562 == NULL) { + /* dev_err(&p_client->dev, "failed to get i2c device\n"); */ + n_result = -ENOMEM; + goto err; + } + + p_tas2562->client = p_client; + p_tas2562->dev = &p_client->dev; + i2c_set_clientdata(p_client, p_tas2562); + dev_set_drvdata(&p_client->dev, p_tas2562); + + p_tas2562->regmap = devm_regmap_init_i2c(p_client, &tas2562_i2c_regmap); + if (IS_ERR(p_tas2562->regmap)) { + n_result = PTR_ERR(p_tas2562->regmap); + dev_err(&p_client->dev, "Failed to allocate register map: %d\n", + n_result); + goto err; + } + + if (p_client->dev.of_node) + tas2562_parse_dt(&p_client->dev, p_tas2562); + + if (gpio_is_valid(p_tas2562->mn_reset_gpio)) { + n_result = gpio_request(p_tas2562->mn_reset_gpio, + "TAS2562_RESET"); + if (n_result) { + dev_err(p_tas2562->dev, "%s: Failed to request gpio %d\n", + __func__, p_tas2562->mn_reset_gpio); + n_result = -EINVAL; + goto err; + } + tas2562_hw_reset(p_tas2562); + } + + if (gpio_is_valid(p_tas2562->mn_reset_gpio2)) { + n_result = gpio_request(p_tas2562->mn_reset_gpio2, + "TAS2562_RESET2"); + if (n_result) { + dev_err(p_tas2562->dev, "%s: Failed to request gpio %d\n", + __func__, p_tas2562->mn_reset_gpio2); + n_result = -EINVAL; + goto err; + } + tas2562_hw_reset(p_tas2562); + } + + p_tas2562->read = tas2562_dev_read; + p_tas2562->write = tas2562_dev_write; + p_tas2562->bulk_read = tas2562_dev_bulk_read; + p_tas2562->bulk_write = tas2562_dev_bulk_write; + p_tas2562->update_bits = tas2562_dev_update_bits; + p_tas2562->hw_reset = tas2562_hw_reset; + p_tas2562->enable_irq = tas2562_enable_irq; + p_tas2562->runtime_suspend = tas2562_runtime_suspend; + p_tas2562->runtime_resume = tas2562_runtime_resume; + p_tas2562->mn_power_state = TAS2562_POWER_SHUTDOWN; + p_tas2562->spk_l_control = 1; + + mutex_init(&p_tas2562->dev_lock); + + dev_info(&p_client->dev, "Before SW reset\n"); + /* Reset the chip */ + n_result = tas2562_dev_write(p_tas2562, channel_both, + TAS2562_SOFTWARERESET, 0x01); + if (n_result < 0) { + dev_err(&p_client->dev, "I2c fail, %d\n", n_result); + goto err; + } + dev_info(&p_client->dev, "After SW reset\n"); + + if (gpio_is_valid(p_tas2562->mn_irq_gpio)) { + n_result = gpio_request(p_tas2562->mn_irq_gpio, "TAS2562-IRQ"); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s: GPIO %d request error\n", + __func__, p_tas2562->mn_irq_gpio); + goto err; + } + gpio_direction_input(p_tas2562->mn_irq_gpio); + tas2562_dev_write(p_tas2562, channel_both, + TAS2562_MISCCONFIGURATIONREG0, 0xce); + + p_tas2562->mn_irq = gpio_to_irq(p_tas2562->mn_irq_gpio); + dev_info(p_tas2562->dev, "irq = %d\n", p_tas2562->mn_irq); + INIT_DELAYED_WORK(&p_tas2562->irq_work, irq_work_routine); + n_result = request_threaded_irq(p_tas2562->mn_irq, + tas2562_irq_handler, + NULL, IRQF_TRIGGER_FALLING|IRQF_ONESHOT, + p_client->name, p_tas2562); + if (n_result < 0) { + dev_err(p_tas2562->dev, + "request_irq failed, %d\n", n_result); + goto err; + } + disable_irq_nosync(p_tas2562->mn_irq); + } + if (gpio_is_valid(p_tas2562->mn_irq_gpio2) && + (p_tas2562->mn_channels == 2)) { + n_result = gpio_request(p_tas2562->mn_irq_gpio2, + "TAS2562-IRQ2"); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s: GPIO %d request error\n", + __func__, p_tas2562->mn_irq_gpio2); + goto err; + } + gpio_direction_input(p_tas2562->mn_irq_gpio2); + tas2562_dev_write(p_tas2562, channel_both, + TAS2562_MISCCONFIGURATIONREG0, 0xce); + + p_tas2562->mn_irq2 = gpio_to_irq(p_tas2562->mn_irq_gpio2); + dev_info(p_tas2562->dev, "irq = %d\n", p_tas2562->mn_irq2); + INIT_DELAYED_WORK(&p_tas2562->irq_work, irq_work_routine); + n_result = request_threaded_irq(p_tas2562->mn_irq2, + tas2562_irq_handler, + NULL, IRQF_TRIGGER_FALLING|IRQF_ONESHOT, + p_client->name, p_tas2562); + if (n_result < 0) { + dev_err(p_tas2562->dev, + "request_irq failed, %d\n", n_result); + goto err; + } + disable_irq_nosync(p_tas2562->mn_irq2); + } + tas2562_enable_irq(p_tas2562, true); + INIT_DELAYED_WORK(&p_tas2562->init_work, init_work_routine); + +#ifdef CONFIG_TAS2562_CODEC + mutex_init(&p_tas2562->codec_lock); + n_result = tas2562_register_codec(p_tas2562); + if (n_result < 0) { + dev_err(p_tas2562->dev, + "register codec failed, %d\n", n_result); + goto err; + } +#endif + +#ifdef CONFIG_TAS2562_MISC + mutex_init(&p_tas2562->file_lock); + n_result = tas2562_register_misc(p_tas2562); + if (n_result < 0) { + dev_err(p_tas2562->dev, + "register codec failed, %d\n", n_result); + goto err; + } +#endif + +err: + return n_result; +} + +static int tas2562_i2c_remove(struct i2c_client *p_client) +{ + struct tas2562_priv *p_tas2562 = i2c_get_clientdata(p_client); + + dev_info(p_tas2562->dev, "%s\n", __func__); + +#ifdef CONFIG_TAS2562_CODEC + tas2562_deregister_codec(p_tas2562); + mutex_destroy(&p_tas2562->codec_lock); +#endif + +#ifdef CONFIG_TAS2562_MISC + tas2562_deregister_misc(p_tas2562); + mutex_destroy(&p_tas2562->file_lock); +#endif + + if (gpio_is_valid(p_tas2562->mn_reset_gpio)) + gpio_free(p_tas2562->mn_reset_gpio); + if (gpio_is_valid(p_tas2562->mn_irq_gpio)) + gpio_free(p_tas2562->mn_irq_gpio); + if (gpio_is_valid(p_tas2562->mn_reset_gpio2)) + gpio_free(p_tas2562->mn_reset_gpio2); + if (gpio_is_valid(p_tas2562->mn_irq_gpio2)) + gpio_free(p_tas2562->mn_irq_gpio2); + + return 0; +} + + +static const struct i2c_device_id tas2562_i2c_id[] = { + { "tas2562", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas2562_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id tas2562_of_match[] = { + { .compatible = "ti,tas2562" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tas2562_of_match); +#endif + + +static struct i2c_driver tas2562_i2c_driver = { + .driver = { + .name = "tas2562", + .owner = THIS_MODULE, +#if defined(CONFIG_OF) + .of_match_table = of_match_ptr(tas2562_of_match), +#endif + }, + .probe = tas2562_i2c_probe, + .remove = tas2562_i2c_remove, + .id_table = tas2562_i2c_id, +}; + +module_i2c_driver(tas2562_i2c_driver); + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TAS2562 I2C Smart Amplifier driver"); +MODULE_LICENSE("GPL v2"); +#endif diff --git a/sound/soc/codecs/tas2562.h b/sound/soc/codecs/tas2562.h new file mode 100644 index 000000000000..01d148fe8b86 --- /dev/null +++ b/sound/soc/codecs/tas2562.h @@ -0,0 +1,655 @@ + +#ifndef __TAS2562_ +#define __TAS2562_ + +#define TAS2562_DRIVER_ID "2.1.0" + +/* Page Control Register */ +#define TAS2562_PAGECTL_REG 0 + +/* Book Control Register (available in page0 of each book) */ +#define TAS2562_BOOKCTL_PAGE 0 +#define TAS2562_BOOKCTL_REG 127 + +#define TAS2562_REG(book, page, reg) (((book * 256 * 128) + \ + (page * 128)) + reg) + +#define TAS2562_BOOK_ID(reg) (reg / (256 * 128)) + +#define TAS2562_PAGE_ID(reg) ((reg % (256 * 128)) / 128) + +#define TAS2562_BOOK_REG(reg) (reg % (256 * 128)) + +#define TAS2562_PAGE_REG(reg) ((reg % (256 * 128)) % 128) + + + /* Software Reset */ +#define TAS2562_SOFTWARERESET TAS2562_REG(0x0, 0x0, 0x01) +#define TAS2562_SOFTWARERESET_SOFTWARERESET_MASK (0x1 << 0), +#define TAS2562_SOFTWARERESET_SOFTWARERESET_DONTRESET (0x0 << 0) +#define TAS2562_SOFTWARERESET_SOFTWARERESET_RESET (0x1 << 0) + + /* Power Control */ +#define TAS2562_POWERCONTROL TAS2562_REG(0x0, 0x0, 0x02) +#define TAS2562_POWERCONTROL_ISNSPOWER_MASK (0x1 << 3) +#define TAS2562_POWERCONTROL_ISNSPOWER_ACTIVE (0x0 << 3) +#define TAS2562_POWERCONTROL_ISNSPOWER_POWEREDDOWN (0x1 << 3) +#define TAS2562_POWERCONTROL_VSNSPOWER_MASK (0x1 << 2) +#define TAS2562_POWERCONTROL_VSNSPOWER_ACTIVE (0x0 << 2) +#define TAS2562_POWERCONTROL_VSNSPOWER_POWEREDDOWN (0x1 << 2) +#define TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK (0x3 << 0) +#define TAS2562_POWERCONTROL_OPERATIONALMODE10_ACTIVE (0x0 << 0) +#define TAS2562_POWERCONTROL_OPERATIONALMODE10_MUTE (0x1 << 0) +#define TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN (0x2 << 0) + + /* data format */ +#define TAS2562_DATAFORMAT_SHIFT 2 +#define TAS2562_DATAFORMAT_I2S 0x0 +#define TAS2562_DATAFORMAT_DSP 0x1 +#define TAS2562_DATAFORMAT_RIGHT_J 0x2 +#define TAS2562_DATAFORMAT_LEFT_J 0x3 + +#define TAS2562_DAI_FMT_MASK (0x7 << TAS2562_DATAFORMAT_SHIFT) + + /* Playback Configuration Reg0 */ +#define TAS2562_PLAYBACKCONFIGURATIONREG0 TAS2562_REG(0x0, 0x0, 0x03) +#define TAS2562_PLAYBACKCONFIGURATIONREG0_DCBLOCKER_MASK (0x1 << 6) +#define TAS2562_PLAYBACKCONFIGURATIONREG0_DCBLOCKER_ENABLED (0x0 << 6) +#define TAS2562_PLAYBACKCONFIGURATIONREG0_DCBLOCKER_DISABLED (0x1 << 6) +#define TAS2562_PLAYBACKCONFIGURATIONREG0_AMPLIFIERLEVEL51_MASK (0x1f << 1) + + /* Misc Configuration Reg0 */ +#define TAS2562_MISCCONFIGURATIONREG0 TAS2562_REG(0x0, 0x0, 0x04) +#define TAS2562_MISCCONFIGURATIONREG0_CPPGRETRY_MASK (0x1 << 7) +#define TAS2562_MISCCONFIGURATIONREG0_CPPGRETRY_DONOTRETRY (0x0 << 7) +#define TAS2562_MISCCONFIGURATIONREG0_CPPGRETRY_RETRY (0x1 << 7) +#define TAS2562_MISCCONFIGURATIONREG0_VBATPRORETRY_MASK (0x1 << 6) +#define TAS2562_MISCCONFIGURATIONREG0_VBATPRORETRY_DONOTRETRY (0x0 << 6) +#define TAS2562_MISCCONFIGURATIONREG0_VBATPRORETRY_RETRY (0x1 << 6) +#define TAS2562_MISCCONFIGURATIONREG0_OCERETRY_MASK (0x1 << 5) +#define TAS2562_MISCCONFIGURATIONREG0_OCERETRY_DONOTRETRY (0x0 << 5) +#define TAS2562_MISCCONFIGURATIONREG0_OCERETRY_RETRY (0x1 << 5) +#define TAS2562_MISCCONFIGURATIONREG0_OTERETRY_MASK (0x1 << 4) +#define TAS2562_MISCCONFIGURATIONREG0_OTERETRY_DONOTRETRY (0x0 << 4) +#define TAS2562_MISCCONFIGURATIONREG0_OTERETRY_RETRY (0x1 << 4) +#define TAS2562_MISCCONFIGURATIONREG0_IRQZPULL_MASK (0x1 << 3) +#define TAS2562_MISCCONFIGURATIONREG0_IRQZPULL_DISABLED (0x0 << 3) +#define TAS2562_MISCCONFIGURATIONREG0_IRQZPULL_ENABLED (0x1 << 3) +#define TAS2562_MISCCONFIGURATIONREG0_AMPSS_MASK (0x1 << 2) +#define TAS2562_MISCCONFIGURATIONREG0_AMPSS_DISABLED (0x0 << 2) +#define TAS2562_MISCCONFIGURATIONREG0_AMPSS_ENABLED (0x1 << 2) + + /* TDM Configuration Reg0 */ +#define TAS2562_TDMCONFIGURATIONREG0 TAS2562_REG(0x0, 0x0, 0x06) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_MASK (0x1 << 5) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_48KHZ (0x0 << 5) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_44_1KHZ (0x1 << 5) +#define TAS2562_TDMCONFIGURATIONREG0_DETECTSAMPRATE_MASK (0x1 << 4) +#define TAS2562_TDMCONFIGURATIONREG0_DETECTSAMPRATE_DISABLED (0x1 << 4) +#define TAS2562_TDMCONFIGURATIONREG0_DETECTSAMPRATE_ENABLED (0x0 << 4) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_MASK (0x7 << 1) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_14_7_16KHZ (0x1 << 1) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_44_1_48KHZ (0x4 << 1) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_88_2_96KHZ (0x5 << 1) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_176_4_192KHZ (0x6 << 1) +#define TAS2562_TDMCONFIGURATIONREG0_FRAMESTART_MASK (0x1 << 0) +#define TAS2562_TDMCONFIGURATIONREG0_FRAMESTART_LOWTOHIGH (0x0 << 0) +#define TAS2562_TDMCONFIGURATIONREG0_FRAMESTART_HIGHTOLOW (0x1 << 0) + + /* TDM Configuration Reg1 */ +#define TAS2562_TDMCONFIGURATIONREG1 TAS2562_REG(0x0, 0x0, 0x07) +#define TAS2562_TDMCONFIGURATIONREG1_RXJUSTIFY_MASK (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG1_RXJUSTIFY_LEFT (0x0 << 6) +#define TAS2562_TDMCONFIGURATIONREG1_RXJUSTIFY_RIGHT (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG1_RXOFFSET51_MASK (0x1f << 1) +#define TAS2562_TDMCONFIGURATIONREG1_RXOFFSET51_SHIFT (1) +#define TAS2562_TDMCONFIGURATIONREG1_RXEDGE_MASK (0x1 << 0) +#define TAS2562_TDMCONFIGURATIONREG1_RXEDGE_RISING (0x0 << 0) +#define TAS2562_TDMCONFIGURATIONREG1_RXEDGE_FALLING (0x1 << 0) + + /* TDM Configuration Reg2 */ +#define TAS2562_TDMCONFIGURATIONREG2 TAS2562_REG(0x0, 0x0, 0x08) +#define TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MASK (0x3 << 4) +#define TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MONO_I2C (0x0 << 4) +#define TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MONO_LEFT (0x1 << 4) +#define TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MONO_RIGHT (0x2 << 4) +#define TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_STEREO_DOWNMIX (0x3 << 4) +#define TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_MASK (0x3 << 2) +#define TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_16BITS (0x0 << 2) +#define TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_20BITS (0x1 << 2) +#define TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_24BITS (0x2 << 2) +#define TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_32BITS (0x3 << 2) +#define TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_MASK (0x3 << 0) +#define TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_16BITS (0x0 << 0) +#define TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_24BITS (0x1 << 0) +#define TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_32BITS (0x2 << 0) + + /* TDM Configuration Reg3 */ +#define TAS2562_TDMCONFIGURATIONREG3 TAS2562_REG(0x0, 0x0, 0x09) +#define TAS2562_TDMCONFIGURATIONREG3_RXSLOTRIGHT74_MASK (0xf << 4) +#define TAS2562_TDMCONFIGURATIONREG3_RXSLOTLeft30_Mask (0xf << 0) + + /* TDM Configuration Reg4 */ +#define TAS2562_TDMConfigurationReg4 TAS2562_REG(0x0, 0x0, 0x0A) +#define TAS2562_TDMCONFIGURATIONREG4_TXKEEPER_MASK (0x1 << 5) +#define TAS2562_TDMCONFIGURATIONREG4_TXKEEPER_DISABLE (0x0 << 5) +#define TAS2562_TDMCONFIGURATIONREG4_TXKEEPER_ENABLE (0x1 << 5) +#define TAS2562_TDMCONFIGURATIONREG4_TXFILL_MASK (0x1 << 4) +#define TAS2562_TDMCONFIGURATIONREG4_TXFILL_TRANSMIT0 (0x0 << 4) +#define TAS2562_TDMCONFIGURATIONREG4_TXFILL_TRANSMITHIZ (0x1 << 4) +#define TAS2562_TDMCONFIGURATIONREG4_TXOFFSET31_MASK (0x7 << 1) +#define TAS2562_TDMCONFIGURATIONREG4_TXEDGE_MASK (0x1 << 0) +#define TAS2562_TDMCONFIGURATIONREG4_TXEDGE_RISING (0x0 << 0) +#define TAS2562_TDMCONFIGURATIONREG4_TXEDGE_FALLING (0x1 << 0) + + /* TDM Configuration Reg5 */ +#define TAS2562_TDMCONFIGURATIONREG5 TAS2562_REG(0x0, 0x0, 0x0B) +#define TAS2562_TDMCONFIGURATIONREG5_VSNSTX_MASK (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG5_VSNSTX_DISABLE (0x0 << 6), +#define TAS2562_TDMCONFIGURATIONREG5_VSNSTX_ENABLE (0x1 << 6), +#define TAS2562_TDMCONFIGURATIONREG5_VSNSSLOT50_MASK (0x3f << 0) + + /* TDM Configuration Reg6 */ +#define TAS2562_TDMCONFIGURATIONREG6 TAS2562_REG(0x0, 0x0, 0x0C) +#define TAS2562_TDMCONFIGURATIONREG6_ISNSTX_MASK (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG6_ISNSTX_DISABLE (0x0 << 6), +#define TAS2562_TDMCONFIGURATIONREG6_ISNSTX_ENABLE (0x1 << 6), +#define TAS2562_TDMCONFIGURATIONREG6_ISNSSLOT50_MASK (0x3f << 0) + + /* TDM Configuration Reg7 */ +#define TAS2562_TDMCONFIGURATIONREG7 TAS2562_REG(0x0, 0x0, 0x0D) +#define TAS2562_TDMCONFIGURATIONREG7_VBATSLEN_MASK (0x1 << 7) +#define TAS2562_TDMCONFIGURATIONREG7_VBATSLEN_8BITS (0x0 << 7) +#define TAS2562_TDMCONFIGURATIONREG7_VBATSLEN_16BITS (0x1 << 7) +#define TAS2562_TDMCONFIGURATIONREG7_VBATTX_MASK (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG7_VBATTX_DISABLE (0x0 << 6) +#define TAS2562_TDMCONFIGURATIONREG7_VBATTX_ENABLE (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG7_VBATSLOT50_MASK (0x3f << 0) + + /* TDM Configuration Reg8 */ +#define TAS2562_TDMCONFIGURATIONREG8 TAS2562_REG(0x0, 0x0, 0x0E) +#define TAS2562_TDMCONFIGURATIONREG8_TEMPTX_MASK (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG8_TEMPTX_DISABLE (0x0 << 6) +#define TAS2562_TDMCONFIGURATIONREG8_TEMPTX_ENABLE (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG8_TEMPSLOT50_MASK (0x3f << 0) + + /* TDM Configuration Reg9 */ +#define TAS2562_TDMCONFIGURATIONREG9 TAS2562_REG(0x0, 0x0, 0x0F) +#define TAS2562_TDMCONFIGURATIONREG9_GAINTX_MASK (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG9_GAINTX_DISABLE (0x0 << 6) +#define TAS2562_TDMCONFIGURATIONREG9_GAINTX_ENABLE (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG9_GAINSLOT50_MASK (0x3f << 0) + + /* Limiter Configuration Reg0 */ +#define TAS2562_LIMITERCONFIGURATIONREG0 TAS2562_REG(0x0, 0x0, 0x12) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKST54_MASK (0x3 << 4) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKST54_1 (0x2 << 4) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKST54_2 (0x3 << 4) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKST54_0_25 (0x0 << 4) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKST54_0_5 (0x1 << 4) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_MASK (0x7 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_5 (0x0 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_10 (0x1 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_20 (0x2 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_40 (0x3 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_80 (0x4 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_160 (0x5 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_320 (0x6 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_640 (0x7 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMEN_MASK (0x1 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMEN_DISABLED (0x0 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMEN_ENABLED (0x1 << 0) + + /* Limiter Configuration Reg1 */ +#define TAS2562_LIMITERCONFIGURATIONREG1 TAS2562_REG(0x0, 0x0, 0x13) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSST76_MASK (0x3 << 6) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSST76_1 (0x2 << 6) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSST76_2 (0x3 << 6) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSST76_0_25 (0x0 << 6) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSST76_0_5 (0x1 << 6) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_MASK (0x7 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_10 (0x0 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_50 (0x1 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_100 (0x2 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_250 (0x3 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_500 (0x4 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_750 (0x5 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_1000 (0x6 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_1500 (0x7 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_MASK (0x7 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_0 (0x0 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_10 (0x1 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_25 (0x2 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_50 (0x3 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_100 (0x4 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_250 (0x5 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_500 (0x6 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_1000 (0x7 << 0) + + /* Brown Out Prevention Reg0 */ +#define TAS2562_BROWNOUTPREVENTIONREG0 TAS2562_REG(0x0, 0x0, 0x14) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPSDEN_MASK (0x1 << 4) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPSDEN_DISABLED (0x0 << 4) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPSDEN_ENABLED (0x1 << 4) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPHLDCLR_MASK (0x1 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPHLDCLR_DONTCLEAR (0x0 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPHLDCLR_CLEAR (0x1 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPINFHLD_MASK (0x1 << 2) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPINFHLD_USEHOLDTIME (0x0 << 2) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPINFHLD_HOLDUNTILCLEARED (0x1 << 2) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPMUTE_MASK (0x1 << 1) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPMUTE_DONOTMUTE (0x0 << 1) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPMUTE_MUTE (0x1 << 1) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPEN_MASK (0x1 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPEN_DISABLED (0x0 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPEN_ENABLED (0x1 << 0) + + /* Brown Out Prevention Reg1 */ +#define TAS2562_BROWNOUTPREVENTIONREG1 TAS2562_REG(0x0, 0x0, 0x15) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_MASK (0x7 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_1 (0x0 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_2 (0x1 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_4 (0x2 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_8 (0x3 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_16 (0x4 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_32 (0x5 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_64 (0x6 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_128 (0x7 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKST43_MASK (0x3 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKST43_1 (0x1 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKST43_2 (0x3 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKST43_0_5 (0x0 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKST43_1_5 (0x2 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_MASK (0x7 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_0 (0x0 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_10 (0x1 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_25 (0x2 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_50 (0x3 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_100 (0x4 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_250 (0x5 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_500 (0x6 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_1000 (0x7 << 0) + +/* Interrupt Mask Reg0 */ +#define TAS2562_INTERRUPTMASKREG0 TAS2562_REG(0x0, 0x0, 0x1A) +#define TAS2562_INTERRUPTMASKREG0_LIMMUTEINTMASK_MASK (0x1 << 7) +#define TAS2562_INTERRUPTMASKREG0_LIMMUTEINTMASK_UNMASK (0x0 << 7) +#define TAS2562_INTERRUPTMASKREG0_LIMMUTEINTMASK_DISABLE (0x1 << 7) +#define TAS2562_INTERRUPTMASKREG0_LIMINFHLDINTMASK_MASK (0x1 << 6) +#define TAS2562_INTERRUPTMASKREG0_LIMINFHLDINTMASK_UNMASK (0x0 << 6) +#define TAS2562_INTERRUPTMASKREG0_LIMINFHLDINTMASK_DISABLE (0x1 << 6) +#define TAS2562_INTERRUPTMASKREG0_LIMMAXATNINTMASK_MASK (0x1 << 5) +#define TAS2562_INTERRUPTMASKREG0_LIMMAXATNINTMASK_UNMASK (0x0 << 5) +#define TAS2562_INTERRUPTMASKREG0_LIMMAXATNINTMASK_DISABLE (0x1 << 5) +#define TAS2562_INTERRUPTMASKREG0_VBATLESSTHANINFINTMASK_MASK (0x1 << 4) +#define TAS2562_INTERRUPTMASKREG0_VBATLESSTHANINFINTMASK_UNMASK (0x0 << 4) +#define TAS2562_INTERRUPTMASKREG0_VBATLESSTHANINFINTMASK_DISABLE (0x1 << 4) +#define TAS2562_INTERRUPTMASKREG0_LIMACTIVEFLAGINTMASK_MASK (0x1 << 3) +#define TAS2562_INTERRUPTMASKREG0_LIMACTIVEFLAGINTMASK_UNMASK (0x0 << 3) +#define TAS2562_INTERRUPTMASKREG0_LIMACTIVEFLAGINTMASK_DISABLE (0x1 << 3) +#define TAS2562_INTERRUPTMASKREG0_TDMCLOCKERRORINTMASK_MASK (0x1 << 2) +#define TAS2562_INTERRUPTMASKREG0_TDMCLOCKERRORINTMASK_UNMASK (0x0 << 2) +#define TAS2562_INTERRUPTMASKREG0_TDMCLOCKERRORINTMASK_DISABLE (0x1 << 2) +#define TAS2562_INTERRUPTMASKREG0_OCEINTMASK_MASK (0x1 << 1) +#define TAS2562_INTERRUPTMASKREG0_OCEINTMASK_UNMASK (0x0 << 1) +#define TAS2562_INTERRUPTMASKREG0_OCEINTMASK_DISABLE (0x1 << 1) +#define TAS2562_INTERRUPTMASKREG0_OTEINTMASK_MASK (0x1 << 0) +#define TAS2562_INTERRUPTMASKREG0_OTEINTMASK_UNMASK (0x0 << 0) +#define TAS2562_INTERRUPTMASKREG0_OTEINTMASK_DISABLE (0x1 << 0) +#define TAS2562_INTERRUPTMASKREG0_DISABLE 0xff + + /* Interrupt Mask Reg1 */ +#define TAS2562_INTERRUPTMASKREG1 TAS2562_REG(0x0, 0x0, 0x1B) +#define TAS2562_INTERRUPTMASKREG1_DSPOUTPUTINTMASK_MASK (0x1 << 7) +#define TAS2562_INTERRUPTMASKREG1_DSPOUTPUTINTMASK_UNMASK (0x0 << 7) +#define TAS2562_INTERRUPTMASKREG1_DSPOUTPUTINTMASK_DISABLE (0x1 << 7) +#define TAS2562_INTERRUPTMASKREG1_CRCINTMASK_MASK (0x1 << 6) +#define TAS2562_INTERRUPTMASKREG1_CRCINTMASK_UNMASK (0x0 << 6) +#define TAS2562_INTERRUPTMASKREG1_CRCINTMASK_DISABLE (0x1 << 6) +#define TAS2562_INTERRUPTMASKREG1_VBATOVLOINTMASK_MASK (0x1 << 2) +#define TAS2562_INTERRUPTMASKREG1_VBATOVLOINTMASK_UNMASK (0x0 << 2) +#define TAS2562_INTERRUPTMASKREG1_VBATOVLOINTMASK_DISABLE (0x1 << 2) +#define TAS2562_INTERRUPTMASKREG1_VBATUVLOINTMASK_MASK (0x1 << 1) +#define TAS2562_INTERRUPTMASKREG1_VBATUVLOINTMASK_UNMASK (0x0 << 1) +#define TAS2562_INTERRUPTMASKREG1_VBATUVLOINTMASK_DISABLE (0x1 << 1) +#define TAS2562_INTERRUPTMASKREG1_BROWNOUTFLAGINTMASK_MASK (0x1 << 0) +#define TAS2562_INTERRUPTMASKREG1_BROWNOUTFLAGINTMASK_UNMASK (0x0 << 0) +#define TAS2562_INTERRUPTMASKREG1_BROWNOUTFLAGINTMASK_DISABLE (0x1 << 0) +#define TAS2562_INTERRUPTMASKREG1_DISABLE 0xff + + /* Interrupt Mask Reg2 */ +#define TAS2562_INTERRUPTMASKREG2 TAS2562_REG(0x0, 0x0, 0x1C) +#define TAS2562_INTERRUPTMASKREG2_DACLKINTMASK_MASK (0x1 << 7) +#define TAS2562_INTERRUPTMASKREG2_DACLKINTMASK_UNMASK (0x0 << 7) +#define TAS2562_INTERRUPTMASKREG2_DACLKINTMASK_DISABLE (0x1 << 7) +#define TAS2562_INTERRUPTMASKREG2_BSTCLKINTMASK_MASK (0x1 << 6) +#define TAS2562_INTERRUPTMASKREG2_BSTCLKINTMASK_UNMASK (0x0 << 6) +#define TAS2562_INTERRUPTMASKREG2_BSTCLKINTMASK_DISABLE (0x1 << 6) +#define TAS2562_INTERRUPTMASKREG2_VBATPORCLKINTMASK_MASK (0x1 << 5) +#define TAS2562_INTERRUPTMASKREG2_VBATPORCLKINTMASK_UNMASK (0x0 << 5) +#define TAS2562_INTERRUPTMASKREG2_VBATPORCLKINTMASK_DISABLE (0x1 << 5) +#define TAS2562_INTERRUPTMASKREG2_PLLOCKINTMASK_MASK (0x1 << 4) +#define TAS2562_INTERRUPTMASKREG2_PLLOCKINTMASK_UNMASK (0x0 << 4) +#define TAS2562_INTERRUPTMASKREG2_PLLOCKINTMASK_DISABLE (0x1 << 4) +#define TAS2562_INTERRUPTMASKREG2_DCDETECTINTMASK_MASK (0x1 << 3) +#define TAS2562_INTERRUPTMASKREG2_DCDETECTINTMASK_UNMASK (0x0 << 3) +#define TAS2562_INTERRUPTMASKREG2_DCDETECTINTMASK_DISABLE (0x1 << 3) + + /* Live-Interrupt Reg0 */ +#define TAS2562_LIVEINTERRUPTREG0 TAS2562_REG(0x0, 0x0, 0x1F) +#define TAS2562_LIVEINTERRUPTREG0_LIMMUTE_MASK (0x1 << 7) +#define TAS2562_LIVEINTERRUPTREG0_LIMMUTE_NOINTERRUPT (0x0 << 7) +#define TAS2562_LIVEINTERRUPTREG0_LIMMUTE_INTERRUPT (0x1 << 7) +#define TAS2562_LIVEINTERRUPTREG0_LIMINFHLD_MASK (0x1 << 6) +#define TAS2562_LIVEINTERRUPTREG0_LIMINFHLD_NOINTERRUPT (0x0 << 6) +#define TAS2562_LIVEINTERRUPTREG0_LIMINFHLD_INTERRUPT (0x1 << 6) +#define TAS2562_LIVEINTERRUPTREG0_LIMMAXATN_MASK (0x1 << 5) +#define TAS2562_LIVEINTERRUPTREG0_LIMMAXATN_NOINTERRUPT (0x0 << 5) +#define TAS2562_LIVEINTERRUPTREG0_LIMMAXATN_INTERRUPT (0x1 << 5) +#define TAS2562_LIVEINTERRUPTREG0_VBATLESSTHANINF_MASK (0x1 << 4) +#define TAS2562_LIVEINTERRUPTREG0_VBATLESSTHANINF_NOINTERRUPT (0x0 << 4) +#define TAS2562_LIVEINTERRUPTREG0_VBATLESSTHANINF_INTERRUPT (0x1 << 4) +#define TAS2562_LIVEINTERRUPTREG0_LIMACTIVEFLAG_MASK (0x1 << 3) +#define TAS2562_LIVEINTERRUPTREG0_LIMACTIVEFLAG_NOINTERRUPT (0x0 << 3) +#define TAS2562_LIVEINTERRUPTREG0_LIMACTIVEFLAG_INTERRUPT (0x1 << 3) +#define TAS2562_LIVEINTERRUPTREG0_TDMCLOCKERROR_MASK (0x1 << 2) +#define TAS2562_LIVEINTERRUPTREG0_TDMCLOCKERROR_NOINTERRUPT (0x0 << 2) +#define TAS2562_LIVEINTERRUPTREG0_TDMCLOCKERROR_INTERRUPT (0x1 << 2) +#define TAS2562_LIVEINTERRUPTREG0_OCEFLAG_MASK (0x1 << 1) +#define TAS2562_LIVEINTERRUPTREG0_OCEFLAG_NOINTERRUPT (0x0 << 1) +#define TAS2562_LIVEINTERRUPTREG0_OCEFLAG_INTERRUPT (0x1 << 1) +#define TAS2562_LIVEINTERRUPTREG0_OTEFLAG_MASK (0x1 << 0) +#define TAS2562_LIVEINTERRUPTREG0_OTEFLAG_NOINTERRUPT (0x0 << 0) +#define TAS2562_LIVEINTERRUPTREG0_OTEFLAG_INTERRUPT (0x1 << 0) + + /* Live-Interrupt Reg1 */ +#define TAS2562_LIVEINTERRUPTREG1 TAS2562_REG(0x0, 0x0, 0x20) +#define TAS2562_LIVEINTERRUPTREG1_DSPINTOUTPUT_MASK (0x1 << 7) +#define TAS2562_LIVEINTERRUPTREG1_DSPINTOUTPUT_NOINTERRUPT (0x0 << 7) +#define TAS2562_LIVEINTERRUPTREG1_DSPINTOUTPUT_INTERRUPT (0x1 << 7) +#define TAS2562_LIVEINTERRUPTREG1_OTPCRC_MASK (0x1 << 6) +#define TAS2562_LIVEINTERRUPTREG1_OTPCRC_NOINTERRUPT (0x0 << 6) +#define TAS2562_LIVEINTERRUPTREG1_OTPCRC_INTERRUPT (0x1 << 6) +#define TAS2562_LIVEINTERRUPTREG1_BROWNOUTFLAG_MASK (0x1 << 1) +#define TAS2562_LIVEINTERRUPTREG1_BROWNOUTFLAG_NOINTERRUPT (0x0 << 1) +#define TAS2562_LIVEINTERRUPTREG1_BROWNOUTFLAG_INTERRUPT (0x1 << 1) +#define TAS2562_LIVEINTERRUPTREG1_BROWNOUTDETECTED_MASK (0x1 << 1) +#define TAS2562_LIVEINTERRUPTREG1_BROWNOUTDETECTED_NOINTERRUPT (0x0 << 1) +#define TAS2562_LIVEINTERRUPTREG1_BROWNOUTDETECTED_INTERRUPT (0x1 << 1) + + /* Latched-Interrupt Reg0 */ +#define TAS2562_LATCHEDINTERRUPTREG0 TAS2562_REG(0x0, 0x0, 0x24) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMMUTESTICKY_MASK (0x1 << 7) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMMUTESTICKY_NOINTERRUPT (0x0 << 7) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMMUTESTICKY_INTERRUPT (0x1 << 7) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMINFHLDSTICKY_MASK (0x1 << 6) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMINFHLDSTICKY_NOINTERRUPT (0x0 << 6) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMINFHLDSTICKY_INTERRUPT (0x1 << 6) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMMAXATNSTICKY_MASK (0x1 << 5) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMMAXATNSTICKY_NOINTERRUPT (0x0 << 5) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMMAXATNSTICKY_INTERRUPT (0x1 << 5) +#define TAS2562_LATCHEDINTERRUPTREG0_VBATLESSTHANINFSTICKY_MASK (0x1 << 4) +#define TAS2562_LATCHEDINTERRUPTREG0_VBATLESSTHANINFSTICKY_NOINTERRUPT \ + (0x0 << 4) +#define TAS2562_LATCHEDINTERRUPTREG0_VBATLESSTHANINFSTICKY_INTERRUPT (0x1 << 4) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMACTIVEFLAGSTICKY_MASK (0x1 << 3) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMACTIVEFLAGSTICKY_NOINTERRUPT (0x0 << 3) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMACTIVEFLAGSTICKY_INTERRUPT (0x1 << 3) +#define TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_MASK (0x1 << 2) +#define TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_NOINTERRUPT (0x0 << 2) +#define TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_INTERRUPT (0x1 << 2) +#define TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_MASK (0x1 << 1) +#define TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_NOINTERRUPT (0x0 << 1) +#define TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_INTERRUPT (0x1 << 1) +#define TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_MASK (0x1 << 0) +#define TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_NOINTERRUPT (0x0 << 0) +#define TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_INTERRUPT (0x1 << 0) + + /* Latched-Interrupt Reg1 */ +#define TAS2562_LATCHEDINTERRUPTREG1 TAS2562_REG(0x0, 0x0, 0x25) +#define TAS2562_LATCHEDINTERRUPTREG1_PDMAUDDATAINVALIDSTICKY_MASK (0x1 << 7) +#define TAS2562_LATCHEDINTERRUPTREG1_PDMAUDDATAINVALIDSTICKY_NOINTERRUPT \ + (0x0 << 7) +#define TAS2562_LATCHEDINTERRUPTREG1_PDMAUDDATAINVALIDSTICKY_INTERRUPT \ + (0x1 << 7) +#define TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_MASK (0x1 << 3) +#define TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_NOINTERRUPT (0x0 << 3) +#define TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_INTERRUPT (0x1 << 3) +#define TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_MASK (0x1 << 2) +#define TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_NOINTERRUPT (0x0 << 2) +#define TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_INTERRUPT (0x1 << 2) +#define TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_MASK (0x1 << 1) +#define TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_NOINTERRUPT (0x0 << 1) +#define TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_INTERRUPT (0x1 << 1) +#define TAS2562_LATCHEDINTERRUPTREG1_PDMCLOCKERRORSTICKY_MASK (0x1 << 0) +#define TAS2562_LATCHEDINTERRUPTREG1_PDMCLOCKERRORSTICKY_NOINTERRUPT (0x0 << 0) +#define TAS2562_LATCHEDINTERRUPTREG1_PDMCLOCKERRORSTICKY_INTERRUPT (0x1 << 0) + + /* VBAT MSB */ +#define TAS2562_VBATMSB TAS2562_REG(0x0, 0x0, 0x2A) +#define TAS2562_VBATMSB_VBATMSB70_MASK (0xff << 0) + + /* VBAT LSB */ +#define TAS2562_VBATLSB TAS2562_REG(0x0, 0x0, 0x2B) +#define TAS2562_VBATLSB_VBATLSB74_MASK (0xf << 4) + + /* TEMP */ +#define TAS2562_TEMP TAS2562_REG(0x0, 0x0, 0x2C) +#define TAS2562_TEMP_TEMPMSB70_MASK (0xff << 0) + + + /* Interrupt Configuration */ +#define TAS2562_INTERRUPTCONFIGURATION TAS2562_REG(0x0, 0x0, 0x30) +#define TAS2562_INTERRUPTCONFIGURATION_LTCHINTCLEAR_MASK (0x1 << 2) +#define TAS2562_INTERRUPTCONFIGURATION_LTCHINTCLEAR (0x1 << 2) +#define TAS2562_INTERRUPTCONFIGURATION_PININTCONFIG10_MASK (0x3 << 0) +#define TAS2562_INTERRUPTCONFIGURATION_PININTCONFIG10_ASSERTONLIVEINTERRUPTS \ + (0x0 << 0) +#define \ +TAS2562_INTERRUPTCONFIGURATION_PININTCONFIG10_ASSERTONLATCHEDINTERRUPTS \ + (0x1 << 0) +#define \ +TAS2562_INTERRUPTCONFIGURATION_PININTCONFIG10_ASSERT2MSONLIVEINTERRUPTS \ + (0x2 << 0) +#define \ +TAS2562_INTERRUPTCONFIGURATION_PININTCONFIG10_ASSERT2MSONLATCHEDINTERRUPTS \ + (0x3 << 0) + + /* Digital Input Pin Pull Down */ +#define TAS2562_DIGITALINPUTPINPULLDOWN TAS2562_REG(0x0, 0x0, 0x31) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSDOUT_MASK (0x1 << 7) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSDOUT_DISABLED (0x0 << 7) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSDOUT_ENABLED (0x1 << 7) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSDIN_MASK (0x1 << 6) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSDIN_DISABLED (0x0 << 6) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSDIN_ENABLED (0x1 << 6) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNFSYNC_MASK (0x1 << 5) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNFSYNC_DISABLED (0x0 << 5) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNFSYNC_ENABLED (0x1 << 5) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSBCLK_MASK (0x1 << 4) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSBCLK_DISABLED (0x0 << 4) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSBCLK_ENABLED (0x1 << 4) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMD0_MASK (0x1 << 3) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMD0_DISABLED (0x0 << 3) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMD0_ENABLED (0x1 << 3) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMD1_MASK (0x1 << 2) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMD1_DISABLED (0x0 << 2) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMD1_ENABLED (0x1 << 2) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMCK0_MASK (0x1 << 1) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMCK0_DISABLED (0x0 << 1) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMCK0_ENABLED (0x1 << 1) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMCK1_MASK (0x1 << 0) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMCK1_DISABLED (0x0 << 0) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMCK1_ENABLED (0x1 << 0) + + /* Misc IRQ */ +#define TAS2562_MISCIRQ TAS2562_REG(0x0, 0x0, 0x32) +#define TAS2562_MISCIRQ_IRQZREQD_MASK (0x1 << 7) +#define TAS2562_MISCIRQ_IRQZREQD_ACTIVEHIGH (0x0 << 7) +#define TAS2562_MISCIRQ_IRQZREQD_ACTIVELOW (0x1 << 7) +#define TAS2562_MISCIRQ_IRQZBITBANG_MASK (0x1 << 0) +#define TAS2562_MISCIRQ_IRQZBITBANG_IRQZINPUTBUF0 (0x0 << 0) +#define TAS2562_MISCIRQ_IRQZBITBANG_IRQZINPUTBUF1 (0x1 << 0) + +#define TAS2562_BOOSTCONFIGURATION2 TAS2562_REG(0x0, 0x0, 0x34) +#define TAS2562_BOOSTCONFIGURATION2_BOOSTMAXVOLTAGE_MASK (0x0f << 0) + +#define TAS2562_BOOSTSLOPE TAS2562_REG(0x0, 0x0, 0x35) +#define TAS2562_BOOSTSLOPE_MASK (0x3 << 2) +#define TAS2562_BOOSTSLOPE_3AV (0x1 << 2) +#define TAS2562_BOOSTSLOPE_2AV (0x2 << 2) + + /* Clock Configuration */ +#define TAS2562_CLOCKCONFIGURATION TAS2562_REG(0x0, 0x0, 0x38) +#define TAS2562_CLOCKCONFIGURATION_SBCLKTOFS52_MASK (0xf << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_16 (0x0 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_24 (0x1 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_32 (0x2 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_48 (0x3 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_64 (0x4 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_96 (0x5 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_128 (0x6 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_192 (0x7 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_256 (0x8 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_384 (0x9 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_512 (0xa << 2) +#define TAS2562_CLOCKCONFIGURATION_DISCLKRATEDETECT10_MASK (0x3 << 0) +#define TAS2562_CLOCKCONFIGURATION_DISCLKRATEDETECT10_DISABLED (0x1 << 0) +#define TAS2562_CLOCKCONFIGURATION_DISCLKRATEDETECT10_ENABLED (0x0 << 0) + +#define TAS2562_VBATFILTER TAS2562_REG(0x0, 0x0, 0x3b) +#define TAS2562_CLASSHRELEASETIMER TAS2562_REG(0x0, 0x0, 0x3c) + +#define TAS2562_ICN_REG TAS2562_REG(0x0, 0x2, 0x64) + +#define TAS2562_TESTPAGECONFIGURATION TAS2562_REG(0x0, 0xfd, 0xd) +#define TAS2562_CLASSDCONFIGURATION1 TAS2562_REG(0x0, 0xfd, 0x19) +#define TAS2562_CLASSDCONFIGURATION2 TAS2562_REG(0x0, 0xfd, 0x32) +#define TAS2562_CLASSDCONFIGURATION3 TAS2562_REG(0x0, 0xfd, 0x33) +#define TAS2562_CLASSDCONFIGURATION4 TAS2562_REG(0x0, 0xfd, 0x3f) +#define TAS2562_EFFICIENCYCONFIGURATION TAS2562_REG(0x0, 0xfd, 0x5f) + +#define TAS2562_CLASSHHEADROOM TAS2562_REG(0x64, 0x7, 0x48) +#define TAS2562_CLASSHHYSTERESIS TAS2562_REG(0x64, 0x7, 0x4c) +#define TAS2562_CLASSHMTCT TAS2562_REG(0x64, 0x5, 0x4c) + + /* Revision and PG ID */ +#define TAS2562_REVISIONANDPGID TAS2562_REG(0x0, 0x0, 0x7D) +#define TAS2562_REVISIONANDPGID_REVISIONID74_MASK (0xf << 4) +#define TAS2562_REVISIONANDPGID_PGID30_MASK (0xf << 0) + + /* I2C Checksum */ +#define TAS2562_I2CCHECKSUM TAS2562_REG(0x0, 0x0, 0x7E) +#define TAS2562_I2CCHECKSUM_I2CCHECKSUM70_MASK (0xff << 0) + + /* Book */ +#define TAS2562_BOOK TAS2562_REG(0x0, 0x0, 0x7F) +#define TAS2562_BOOK_BOOK70_MASK (0xff << 0) + +#define TAS2562_POWER_ACTIVE 0 +#define TAS2562_POWER_MUTE 1 +#define TAS2562_POWER_SHUTDOWN 2 + +#define ERROR_NONE 0x0000000 +#define ERROR_PLL_ABSENT 0x0000000 +#define ERROR_DEVA_I2C_COMM 0x0000000 +#define ERROR_DEVB_I2C_COMM 0x0000000 +#define ERROR_CLOCK 0x0000000 +#define ERROR_YRAM_CRCCHK 0x0000001 +#define ERROR_OVER_CURRENT 0x0000002 +#define ERROR_DIE_OVERTEMP 0x0000004 +#define ERROR_OVER_VOLTAGE 0x0000008 +#define ERROR_UNDER_VOLTAGE 0x0000010 +#define ERROR_BROWNOUT 0x0000020 +#define ERROR_CLASSD_PWR 0x0000040 +#define ERROR_FAILSAFE 0x4000000 + +#define CHECK_PERIOD 5000 /* 5 second */ + +#define TAS2562_I2C_RETRY_COUNT 3 +#define ERROR_I2C_SUSPEND -1 +#define ERROR_I2C_FAILED -2 + +struct tas2562_register { +int book; +int page; +int reg; +}; + +struct tas2562_dai_cfg { +unsigned int dai_fmt; +unsigned int tdm_delay; +}; + +enum channel { + channel_left = 0x01, + channel_right = 0x02, + channel_both = channel_left|channel_right +}; + +struct tas2562_priv { +struct device *dev; +struct i2c_client *client; +struct regmap *regmap; +struct mutex dev_lock; +struct delayed_work irq_work; +struct delayed_work init_work; +struct hrtimer mtimer; +bool mb_power_up; +int mn_power_state; +int mn_l_current_book; +int mn_l_current_page; +int mn_r_current_book; +int mn_r_current_page; +int mn_l_addr; +int mn_r_addr; +int mn_asi_format; +int mn_reset_gpio; +int mn_irq_gpio; +int mn_irq; +int mn_reset_gpio2; +int mn_irq_gpio2; +int mn_irq2; +bool mb_irq_eable; +int mn_sampling_rate; +int mn_frame_size; +int mn_ppg; +int mn_ch_size; +int mn_slot_width; +int mn_pcm_format; +bool mb_mute; +bool i2c_suspend; +int mn_channels; +int spk_l_control; +int spk_r_control; +int (*read)(struct tas2562_priv *p_tas2562, enum channel chn, + unsigned int reg, unsigned int *pValue); +int (*write)(struct tas2562_priv *p_tas2562, enum channel chn, + unsigned int reg, unsigned int Value); +int (*bulk_read)(struct tas2562_priv *p_tas2562, enum channel chn, + unsigned int reg, unsigned char *p_data, unsigned int len); +int (*bulk_write)(struct tas2562_priv *p_tas2562, enum channel chn, + unsigned int reg, unsigned char *p_data, unsigned int len); +int (*update_bits)(struct tas2562_priv *p_tas2562, enum channel chn, + unsigned int reg, unsigned int mask, unsigned int value); +void (*hw_reset)(struct tas2562_priv *p_tas2562); +void (*clear_irq)(struct tas2562_priv *p_tas2562); +void (*enable_irq)(struct tas2562_priv *p_tas2562, bool enable); + /* device is working, but system is suspended */ +int (*runtime_suspend)(struct tas2562_priv *p_tas2562); +int (*runtime_resume)(struct tas2562_priv *p_tas2562); +bool mb_runtime_suspend; + +unsigned int mn_err_code; +#ifdef CONFIG_TAS2562_CODEC +struct mutex codec_lock; +#endif + +#ifdef CONFIG_TAS2562_MISC +int mn_dbg_cmd; +int mn_current_reg; +struct mutex file_lock; +#endif +}; + +#endif /* __TAS2562_ */ diff --git a/sound/soc/codecs/tas2562.txt b/sound/soc/codecs/tas2562.txt new file mode 100644 index 000000000000..ddb490b24191 --- /dev/null +++ b/sound/soc/codecs/tas2562.txt @@ -0,0 +1,45 @@ +Texas Instruments TAS2562 Smart PA + +Required properties: + + - compatible: Should contain "ti,tas2562". + - reg: The i2c address. Should contain <0x4c>, <0x4d>, + <0x4e>, or <0x4f>. + +Optional properties: + + - ti,left-channel: The i2c address of left device. + + - ti,right-channel: The i2c address of right device. + It can be NULL for mono design. + + + - ti,channels: This property is channels number from 1 to 2. + If given 1, it is mono design. + If given 2, it is stereo design. + + - ti,reset-gpio: Reset GPIO number of left device. + + - ti,reset-gpio2: Reset GPIO number of right device. + It can be NULL for mono design. + + - irq-gpio: IRQ GPIO number of left device. + + - irq-gpio2: IRQ GPIO number of right device. + It can be NULL for mono design. + +Examples: + + tas2562:tas2562@4c { + #sound-dai-cells = <0>; + compatible = "ti,tas2562"; + reg = <0x4c>; + ti,left-channel = <0x4c>; + ti,right-channel = <0x4d>; + ti,channels = <2>; /* channel number */ + ti,reset-gpio = <&gpio1 17 0>; + ti,reset-gpio2 = <&gpio1 16 0>; + ti,irq-gpio = <&gpio1 15 0>; + ti,irq-gpio2 = <&gpio1 14 0>; + status = "ok"; + }; \ No newline at end of file