This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TAS2110: Linux driver implementation.

Part Number: TAS2110
Other Parts Discussed in Thread: TAS2563, TAS2505, TAS2562, TAS2557, TAS2558

Hello,

I have custom carrier board for Jetson Nano (kernel 4.9.253) with TAS2110 audio amplifier. Firstly I made test manually - play I2S sound and configure via I2C using script, so hardware is fine. I want to implement driver for this IC, so I use driver from your website: https://git.ti.com/cgit/lpaa-android-drivers/tas2505-linux-driver/. I know that it is driver for TAS2563, but I read that it is replacement for TAS2110.
I work with Nvidia support during added this driver to Kernel (https://forums.developer.nvidia.com/t/tas2557-tas2563-implement-driver-from-kernel-source/218181). They said that on Kernel side everything looks fine and probably fault is in driver source.
Can you said me if this driver will work with TAS2110 amplifier? If not, can you said what I must change or can you send other driver?

Best regards,
Mateusz


  • Hi Mateus,

    Not sure if you linked the correct driver, as that one is for TAS2505 but then you mention TAS2563, please confirm.
    You may try with either of these:

    Best regards,
    -Ivan Salazar
    Applications Engineer

  • Hi Mateusz

    Kindly refer to this thread TAS2110: TAS2110 Linux Driver - Audio forum - Audio - TI E2E support forums

    Both tas2557 and tas2563 are dsp-inside chip which need firmware. I think tas2562 or tas2558 driver code is more suitable for tas2110.

    Good luck.

    Shenghao

  • I am using the TAS2110 now, but in the future it will probably be tas2563. I use the driver for TAS2505 because it is only available for Linux on your git (the rest of the drivers are for Android).

  • Driver for Android can be used for Linux, because both use the Linux kernel

  • Thanks for reply.
    I compiled driver for TAS2562 without error, but I have problem with register codec. In dmesg log I see:
    [ 1.478687] tegra-asoc: sound: failed to set tas2562 sysclk!
    [ 1.484433] tegra-asoc: sound: ASoC: failed to init tas2562-playback: -524
    [ 1.491381] tegra-asoc: sound: ASoC: failed to instantiate card -524
    [ 1.498653] usb 1-3: new high-speed USB device number 2 using tegra-xusb
    [ 1.501418] tegra-asoc: sound: snd_soc_register_card failed (-524)
    [ 1.507772] tegra-asoc:: probe of sound failed with error -524
    full dmesg log:

    dmesg_full.txt
    I checked files /sys/kernel/debug/asoc/*

    sys_kernel_debug_asoc_components.txt
    $ sudo cat /sys/kernel/debug/asoc/components
    tegra210-ope.1
    tegra210-ope.0
    tegra210-mvc.1
    tegra210-mvc.0
    tegra210-afc.5
    tegra210-afc.4
    tegra210-afc.3
    tegra210-afc.2
    tegra210-afc.1
    tegra210-afc.0
    tegra210-sfc.3
    tegra210-sfc.2
    tegra210-sfc.1
    tegra210-sfc.0
    tegra210-mixer
    tegra210-adx.1
    tegra210-adx.0
    tegra210-amx.1
    tegra210-amx.0
    tegra210-dmic.1
    tegra210-dmic.0
    tegra210-i2s.2
    tegra210-admaif
    tegra210-axbar
    tas2562.2-004c
    7.spdif-dit.7
    6.spdif-dit.6
    5.spdif-dit.5
    4.spdif-dit.4
    3.spdif-dit.3
    2.spdif-dit.2
    1.spdif-dit.1
    0.spdif-dit.0
    snd-soc-dummy
    
    sys_kernel_debug_asoc_dais.txt
    $ sudo cat /sys/kernel/debug/asoc/dais
    OPE OUT
    OPE IN
    OPE OUT
    OPE IN
    MVC OUT
    MVC IN
    MVC OUT
    MVC IN
    AFC OUT
    AFC IN
    AFC OUT
    AFC IN
    AFC OUT
    AFC IN
    AFC OUT
    AFC IN
    AFC OUT
    AFC IN
    AFC OUT
    AFC IN
    DAP
    CIF
    DAP
    CIF
    DAP
    CIF
    DAP
    CIF
    TX5
    TX4
    TX3
    TX2
    TX1
    RX10
    RX9
    RX8
    RX7
    RX6
    RX5
    RX4
    RX3
    RX2
    RX1
    IN
    OUT4
    OUT3
    OUT2
    OUT1
    IN
    OUT4
    OUT3
    OUT2
    OUT1
    OUT
    IN4
    IN3
    IN2
    IN1
    OUT
    IN4
    IN3
    IN2
    IN1
    DAP
    CIF
    DAP
    CIF
    DAP
    CIF
    ADMAIF10 CIF
    ADMAIF9 CIF
    ADMAIF8 CIF
    ADMAIF7 CIF
    ADMAIF6 CIF
    ADMAIF5 CIF
    ADMAIF4 CIF
    ADMAIF3 CIF
    ADMAIF2 CIF
    ADMAIF1 CIF
    ADMAIF10 FIFO
    ADMAIF9 FIFO
    ADMAIF8 FIFO
    ADMAIF7 FIFO
    ADMAIF6 FIFO
    ADMAIF5 FIFO
    ADMAIF4 FIFO
    ADMAIF3 FIFO
    ADMAIF2 FIFO
    ADMAIF1 FIFO
    ADMAIF10
    ADMAIF9
    ADMAIF8
    ADMAIF7
    ADMAIF6
    ADMAIF5
    ADMAIF4
    ADMAIF3
    ADMAIF2
    ADMAIF1
    ADX2
    ADX2-4
    ADX2-3
    ADX2-2
    ADX2-1
    ADX1
    ADX1-4
    ADX1-3
    ADX1-2
    ADX1-1
    AMX2-4
    AMX2-3
    AMX2-2
    AMX2-1
    AMX2
    AMX1-4
    AMX1-3
    AMX1-2
    AMX1-1
    AMX1
    DMIC3
    DMIC2
    DMIC1
    IQC2-2
    IQC2-1
    IQC1-2
    IQC1-1
    MVC2
    MVC1
    SPKPROT1
    OPE2
    OPE1
    AFC6
    AFC5
    AFC4
    AFC3
    AFC2
    AFC1
    MIXER1-10
    MIXER1-9
    MIXER1-8
    MIXER1-7
    MIXER1-6
    MIXER1-5
    MIXER1-4
    MIXER1-3
    MIXER1-2
    MIXER1-1
    SFC4
    SFC3
    SFC2
    SFC1
    I2S5
    I2S4
    I2S3
    I2S2
    I2S1
    ADMAIF10
    ADMAIF9
    ADMAIF8
    ADMAIF7
    ADMAIF6
    ADMAIF5
    ADMAIF4
    ADMAIF3
    ADMAIF2
    ADMAIF1
    tas2562 ASI1
    dit-hifi
    dit-hifi
    dit-hifi
    dit-hifi
    dit-hifi
    dit-hifi
    dit-hifi
    dit-hifi
    snd-soc-dummy-dai
    
    sys_kernel_debug_asoc_platforms.txt
    $ sudo cat /sys/kernel/debug/asoc/platforms
    tegra210-admaif
    snd-soc-dummy
    


    Above error is call from below function:

    static int tegra_machine_tas2562_init(struct snd_soc_pcm_runtime *rtd) {
    struct device *dev = rtd->card->dev;
    int err;

    err = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 12288000,
    SND_SOC_CLOCK_IN);
    if (err) {
    dev_err(dev, "failed to set tas2562 sysclk!\n");
    return err;
    }

    return 0;
    }

    When I used tas2505 driver with similar settings (only change tas2505 to tas 2562), I didn't have any problem.
    Do you have any idea what is wrong?

  • Hi Mateus,

    We'll check your error descriptions and provide further comments as soon as possible

    Best regards,
    -Ivan Salazar
    Applications Engineer

  • I found mistake in device tree and now I see sound card in system

    sudo cat /proc/asound/cards 
     0 [tegrahda       ]: tegra-hda - tegra-hda
                          tegra-hda at 0x70038000 irq 82
     1 [tegrasndt210ref]: tegra-snd-t210r - tegra-snd-t210ref-mobile-TAS2xx
                          tegra-snd-t210ref-mobile-TAS2xxx_AMP
    

    When I play sound then I2S and I2C is quiet (only mclk was 11.3MHz).

    During boot on UART I had:
    [ 1.400695] tas2562 2-004c: Driver ID: 1.0.1
    [ 1.429716] tas2562 2-004c: gpio up !!
    [ 1.467630] tas2562 2-004c: tas2562_codec_probe!!!

    I checked files /sys/kernel/debug/asoc/*

    cat_sys_kernel_debug_asoc_codecs.txt
    sudo cat /sys/kernel/debug/asoc/codecs
    tegra210-ope.1
    tegra210-ope.0
    tegra210-mvc.1
    tegra210-mvc.0
    tegra210-afc.5
    tegra210-afc.4
    tegra210-afc.3
    tegra210-afc.2
    tegra210-afc.1
    tegra210-afc.0
    tegra210-sfc.3
    tegra210-sfc.2
    tegra210-sfc.1
    tegra210-sfc.0
    tegra210-mixer
    tegra210-adx.1
    tegra210-adx.0
    tegra210-amx.1
    tegra210-amx.0
    tegra210-dmic.1
    tegra210-dmic.0
    tegra210-i2s.2
    tegra210-admaif
    tegra210-axbar
    tas2562.2-004c
    7.spdif-dit.7
    6.spdif-dit.6
    5.spdif-dit.5
    4.spdif-dit.4
    3.spdif-dit.3
    2.spdif-dit.2
    1.spdif-dit.1
    0.spdif-dit.0
    snd-soc-dummy
    
    
    
    cat_sys_kernel_debug_asoc_dais.txt
    sudo cat /sys/kernel/debug/asoc/dais
    OPE OUT
    OPE IN
    OPE OUT
    OPE IN
    MVC OUT
    MVC IN
    MVC OUT
    MVC IN
    AFC OUT
    AFC IN
    AFC OUT
    AFC IN
    AFC OUT
    AFC IN
    AFC OUT
    AFC IN
    AFC OUT
    AFC IN
    AFC OUT
    AFC IN
    DAP
    CIF
    DAP
    CIF
    DAP
    CIF
    DAP
    CIF
    TX5
    TX4
    TX3
    TX2
    TX1
    RX10
    RX9
    RX8
    RX7
    RX6
    RX5
    RX4
    RX3
    RX2
    RX1
    IN
    OUT4
    OUT3
    OUT2
    OUT1
    IN
    OUT4
    OUT3
    OUT2
    OUT1
    OUT
    IN4
    IN3
    IN2
    IN1
    OUT
    IN4
    IN3
    IN2
    IN1
    DAP
    CIF
    DAP
    CIF
    DAP
    CIF
    ADMAIF10 CIF
    ADMAIF9 CIF
    ADMAIF8 CIF
    ADMAIF7 CIF
    ADMAIF6 CIF
    ADMAIF5 CIF
    ADMAIF4 CIF
    ADMAIF3 CIF
    ADMAIF2 CIF
    ADMAIF1 CIF
    ADMAIF10 FIFO
    ADMAIF9 FIFO
    ADMAIF8 FIFO
    ADMAIF7 FIFO
    ADMAIF6 FIFO
    ADMAIF5 FIFO
    ADMAIF4 FIFO
    ADMAIF3 FIFO
    ADMAIF2 FIFO
    ADMAIF1 FIFO
    ADMAIF10
    ADMAIF9
    ADMAIF8
    ADMAIF7
    ADMAIF6
    ADMAIF5
    ADMAIF4
    ADMAIF3
    ADMAIF2
    ADMAIF1
    ADX2
    ADX2-4
    ADX2-3
    ADX2-2
    ADX2-1
    ADX1
    ADX1-4
    ADX1-3
    ADX1-2
    ADX1-1
    AMX2-4
    AMX2-3
    AMX2-2
    AMX2-1
    AMX2
    AMX1-4
    AMX1-3
    AMX1-2
    AMX1-1
    AMX1
    DMIC3
    DMIC2
    DMIC1
    IQC2-2
    IQC2-1
    IQC1-2
    IQC1-1
    MVC2
    MVC1
    SPKPROT1
    OPE2
    OPE1
    AFC6
    AFC5
    AFC4
    AFC3
    AFC2
    AFC1
    MIXER1-10
    MIXER1-9
    MIXER1-8
    MIXER1-7
    MIXER1-6
    MIXER1-5
    MIXER1-4
    MIXER1-3
    MIXER1-2
    MIXER1-1
    SFC4
    SFC3
    SFC2
    SFC1
    I2S5
    I2S4
    I2S3
    I2S2
    I2S1
    ADMAIF10
    ADMAIF9
    ADMAIF8
    ADMAIF7
    ADMAIF6
    ADMAIF5
    ADMAIF4
    ADMAIF3
    ADMAIF2
    ADMAIF1
    tas2562 ASI1
    dit-hifi
    dit-hifi
    dit-hifi
    dit-hifi
    dit-hifi
    dit-hifi
    dit-hifi
    dit-hifi
    snd-soc-dummy-dai
    
    

    Below I attach dmesg log and source file:

    sound_card_lit.txt
    #--- sudo zcat /proc/config.gz | egrep -i 'tas2562  ---#
    CONFIG_SND_SOC_TAS2562=y
    CONFIG_TAS2562_REGMAP=y
    CONFIG_TAS2562_CODEC=y
    #--- sudo cat /proc/asound/cards  ---#
     0 [tegrahda       ]: tegra-hda - tegra-hda
                          tegra-hda at 0x70038000 irq 82
     1 [tegrasndt210ref]: tegra-snd-t210r - tegra-snd-t210ref-mobile-TAS2xx
                          tegra-snd-t210ref-mobile-TAS2xxx_AMP
    #--- sudo zcat /proc/config.gz | grep CONFIG_SND_SOC_SPDIF  ---#
    CONFIG_SND_SOC_SPDIF=y
    
    

    tas2562.h
    tas2562-codec.c
    /*
    ** =============================================================================
    ** 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 1
    #include <linux/module.h>
    #include <linux/moduleparam.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <linux/pm.h>
    #include <linux/i2c.h>
    #include <linux/gpio.h>
    #include <linux/regulator/consumer.h>
    #include <linux/firmware.h>
    #include <linux/regmap.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    #include <linux/slab.h>
    #include <sound/core.h>
    #include <sound/pcm.h>
    #include <sound/pcm_params.h>
    #include <sound/soc.h>
    #include <sound/initval.h>
    #include <sound/tlv.h>
    
    #include "tas2562.h"
    
    #define TAS2562_MDELAY 0xFFFFFFFE
    #define TAS2562_MSLEEP 0xFFFFFFFD
    
    static char const *iv_enable_text[] = {"Off", "On"};
    static int tas2562iv_enable = 1;
    static int mbMute;
    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 *pTAS2562, unsigned int fmt);
    
    static int tas2562_i2c_load_data(struct tas2562_priv *pTAS2562, unsigned int *pData);
    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 *pTAS2562 = snd_soc_codec_get_drvdata(codec);
    	int nResult = 0;
    	unsigned int value = 0;
    
    	nResult = pTAS2562->read(pTAS2562, reg, &value);
    
    	if (nResult < 0)
    		dev_err(pTAS2562->dev, "%s, ERROR, reg=0x%x, E=%d\n",
    			__func__, reg, nResult);
    	else
    		dev_dbg(pTAS2562->dev, "%s, reg: 0x%x, value: 0x%x\n",
    				__func__, reg, value);
    
    	if (nResult >= 0)
    		return value;
    	else
    		return nResult;
    }
    
    static int tas2562_iv_enable(struct tas2562_priv *pTAS2562, int enable)
    {
    	int nResult;
    
    	if (enable) {
    		pr_debug("%s: tas2562iv_enable \n", __func__);
    		nResult = pTAS2562->update_bits(pTAS2562, 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__);
    		nResult = pTAS2562->update_bits(pTAS2562, TAS2562_PowerControl,
    			TAS2562_PowerControl_ISNSPower_Mask |
    			TAS2562_PowerControl_VSNSPower_Mask,
    			TAS2562_PowerControl_VSNSPower_PoweredDown |
    			TAS2562_PowerControl_ISNSPower_PoweredDown);
    	}
    	tas2562iv_enable = enable;
    
    	return nResult;
    }
    
    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 *pTAS2562 = snd_soc_codec_get_drvdata(codec);
    	int iv_enable = 0, nResult = 0;
    
        if (codec == NULL) {
    		pr_err("%s: codec is NULL \n",  __func__);
    		return 0;
        }
    
        iv_enable = ucontrol->value.integer.value[0];
    
    	nResult = tas2562_iv_enable(pTAS2562, iv_enable);
    
    	pr_debug("%s: tas2562iv_enable = %d\n", __func__, tas2562iv_enable);
    
    	return nResult;
    }
    
    static int tas2562iv_get(struct snd_kcontrol *kcontrol,
    				  struct snd_ctl_elem_value *ucontrol)
    {
       ucontrol->value.integer.value[0] = 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 *pTAS2562 = snd_soc_codec_get_drvdata(codec);
    
    	int nResult = 0;
    
    	nResult = pTAS2562->write(pTAS2562, reg, value);
    	if (nResult < 0)
    		dev_err(pTAS2562->dev, "%s, ERROR, reg=0x%x, E=%d\n",
    			__func__, reg, nResult);
    	else
    		dev_dbg(pTAS2562->dev, "%s, reg: 0x%x, 0x%x\n",
    			__func__, reg, value);
    
    	return nResult;
    
    }
    static int tas2562_i2c_load_data(struct tas2562_priv *pTAS2562, unsigned int *pData)
    {
    	unsigned int nRegister;
    	unsigned int *nData;
    	unsigned char Buf[128];
    	unsigned int nLength = 0;
    	unsigned int i = 0;
    	unsigned int nSize = 0;
    	int nResult = 0;
    	do {
    		nRegister = pData[nLength];
    		nSize = pData[nLength + 1];
    		nData = &pData[nLength + 2];
    		if (nRegister == TAS2562_MSLEEP) {
    			msleep(nData[0]);
    			dev_dbg(pTAS2562->dev, "%s, msleep = %d\n",
    				__func__, nData[0]);
    		} else if (nRegister == TAS2562_MDELAY) {
    			mdelay(nData[0]);
    			dev_dbg(pTAS2562->dev, "%s, mdelay = %d\n",
    				__func__, nData[0]);
    		} else {
    			if (nRegister != 0xFFFFFFFF) {
    				if (nSize > 128) {
    					dev_err(pTAS2562->dev,
    						"%s, Line=%d, invalid size, maximum is 128 bytes!\n",
    						__func__, __LINE__);
    					break;
    				}
    				if (nSize > 1) {
    					for (i = 0; i < nSize; i++)
    						Buf[i] = (unsigned char)nData[i];
    					nResult = pTAS2562->bulk_write(pTAS2562, nRegister, Buf, nSize);
    					if (nResult < 0)
    						break;
    				} else if (nSize == 1) {
    					nResult = pTAS2562->write(pTAS2562, nRegister, nData[0]);
    					if (nResult < 0)
    						break;
    				} else {
    					dev_err(pTAS2562->dev,
    						"%s, Line=%d,invalid size, minimum is 1 bytes!\n",
    						__func__, __LINE__);
    				}
    			}
    		}
    		nLength = nLength + 2 + pData[nLength + 1];
    	} while (nRegister != 0xFFFFFFFF);
    	return nResult;
    }
    static int tas2562_codec_suspend(struct snd_soc_codec *codec)
    {
    	struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec);
    	int ret = 0;
    
    	mutex_lock(&pTAS2562->codec_lock);
    
    	dev_dbg(pTAS2562->dev, "%s\n", __func__);
    	pTAS2562->runtime_suspend(pTAS2562);
    
    	mutex_unlock(&pTAS2562->codec_lock);
    	return ret;
    }
    
    static int tas2562_codec_resume(struct snd_soc_codec *codec)
    {
    	struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec);
    	int ret = 0;
    
    	mutex_lock(&pTAS2562->codec_lock);
    
    	dev_dbg(pTAS2562->dev, "%s\n", __func__);
    	pTAS2562->runtime_resume(pTAS2562);
    
    	mutex_unlock(&pTAS2562->codec_lock);
    	return ret;
    }
    
    static const struct snd_kcontrol_new tas2562_asi_controls[] = {
    	SOC_DAPM_SINGLE("Left", TAS2562_TDMConfigurationReg2,
    		4, 1, 0),
    	SOC_DAPM_SINGLE("Right", TAS2562_TDMConfigurationReg2,
    		4, 2, 0),
    	SOC_DAPM_SINGLE("LeftRightDiv2", TAS2562_TDMConfigurationReg2,
    		4, 3, 0),
    };
    
    static int tas2562_set_power_state(struct tas2562_priv *pTAS2562, int state)
    {
    	int nResult = 0;
    	/*unsigned int nValue;*/
    	int irqreg;
    
    	if ((pTAS2562->mbMute) && (state == TAS2562_POWER_ACTIVE))
    		state = TAS2562_POWER_MUTE;
    	dev_err(pTAS2562->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(pTAS2562->mnASIFormat == 0)
    			pTAS2562->mnASIFormat = SND_SOC_DAIFMT_CBS_CFS
    				| SND_SOC_DAIFMT_IB_NF
    				| SND_SOC_DAIFMT_I2S;
    		nResult = tas2562_set_fmt(pTAS2562, pTAS2562->mnASIFormat);
    		if (nResult < 0)
    			return nResult;
    
    //Clear latched IRQ before power on
    
    		pTAS2562->update_bits(pTAS2562, TAS2562_InterruptConfiguration,
    					TAS2562_InterruptConfiguration_LTCHINTClear_Mask,
    					TAS2562_InterruptConfiguration_LTCHINTClear);
    
    		pTAS2562->read(pTAS2562, TAS2562_LatchedInterruptReg0, &irqreg);
    			dev_info(pTAS2562->dev, "IRQ reg is: %s %d, %d\n", __func__, irqreg, __LINE__);
    
    		pTAS2562->mbPowerUp = true;
    		pTAS2562->mnPowerState = TAS2562_POWER_ACTIVE;
    		schedule_delayed_work(&pTAS2562->irq_work, msecs_to_jiffies(10));
    		break;
    
    	case TAS2562_POWER_MUTE:
    		nResult = pTAS2562->update_bits(pTAS2562, 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);
    			pTAS2562->mbPowerUp = true;
    			pTAS2562->mnPowerState = TAS2562_POWER_MUTE;
    		break;
    
    	case TAS2562_POWER_SHUTDOWN:
    		pTAS2562->enableIRQ(pTAS2562, false);
    		if (hrtimer_active(&pTAS2562->mtimer))
    		{
    			dev_info(pTAS2562->dev, "cancel timer\n");
    			hrtimer_cancel(&pTAS2562->mtimer);
    		}
    
    		nResult = pTAS2562->update_bits(pTAS2562, TAS2562_PowerControl,
    			TAS2562_PowerControl_OperationalMode10_Mask,
    			TAS2562_PowerControl_OperationalMode10_Shutdown);
    			pTAS2562->mbPowerUp = false;
    			pTAS2562->mnPowerState = TAS2562_POWER_SHUTDOWN;
    		msleep(20);
    
    		break;
    
    	default:
    		dev_err(pTAS2562->dev, "wrong power state setting %d\n", state);
    
    	}
    
    	return nResult;
    }
    
    
    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 *pTAS2562 = snd_soc_codec_get_drvdata(codec);
    
    	switch (event) {
    	case SND_SOC_DAPM_POST_PMU:
    		tas2562_set_power_state(pTAS2562, TAS2562_POWER_ACTIVE);
    		break;
    	case SND_SOC_DAPM_PRE_PMD:
    		tas2562_set_power_state(pTAS2562, TAS2562_POWER_SHUTDOWN);
    		break;
    
    	}
    	return 0;
    
    }
    
    static const struct snd_soc_dapm_widget tas2562_dapm_widgets[] = {
    	SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
    	SND_SOC_DAPM_AIF_OUT("Voltage Sense", "ASI1 Capture",  1, TAS2562_PowerControl, 2, 1),
    	SND_SOC_DAPM_AIF_OUT("Current Sense", "ASI1 Capture",  0, TAS2562_PowerControl, 3, 1),
    	SND_SOC_DAPM_MIXER("ASI1 Sel",
    		TAS2562_TDMConfigurationReg2, 4, 0,
    		&tas2562_asi_controls[0],
    		ARRAY_SIZE(tas2562_asi_controls)),
    	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[] = {
    	{"ASI1 Sel", "Left", "ASI1"},
    	{"ASI1 Sel", "Right", "ASI1"},
    	{"ASI1 Sel", "LeftRightDiv2", "ASI1"},
    	{"DAC", NULL, "ASI1 Sel"},
    	{"OUT", NULL, "DAC"},
    	/*{"VMON", NULL, "Voltage Sense"},
    	{"IMON", NULL, "Current Sense"},*/
    	{"Voltage Sense", NULL, "VMON"},
    	{"Current Sense", NULL, "IMON"},
    };
    
    
    static int tas2562_mute(struct snd_soc_dai *dai, int mute)
    {
    	struct snd_soc_codec *codec = dai->codec;
    	struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec);
    
    	dev_dbg(pTAS2562->dev, "%s, %d \n", __func__, mute);
    
    	mutex_lock(&pTAS2562->codec_lock);
    	if (mute) {
    		tas2562_set_power_state(pTAS2562, TAS2562_POWER_SHUTDOWN);
    	} else {
    		tas2562_set_power_state(pTAS2562, TAS2562_POWER_ACTIVE);
    	}
    	mutex_unlock(&pTAS2562->codec_lock);
    	return 0;
    }
    
    static int tas2562_slot_config(struct snd_soc_codec *codec, struct tas2562_priv *pTAS2562, int blr_clk_ratio)
    {
    	int ret = 0;
    		pTAS2562->update_bits(pTAS2562,
    			TAS2562_TDMConfigurationReg5, 0xff, 0x42);
    
    		pTAS2562->update_bits(pTAS2562,
    			TAS2562_TDMConfigurationReg6, 0xff, 0x40);
    
    	return ret;
    }
    
    static int tas2562_set_slot(struct snd_soc_codec *codec, int slot_width)
    {
    	int ret = 0;
    	struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec);
    
    	switch (slot_width) {
    	case 16:
    	ret = pTAS2562->update_bits(pTAS2562,
    		TAS2562_TDMConfigurationReg2,
    		TAS2562_TDMConfigurationReg2_RXSLEN10_Mask,
    		TAS2562_TDMConfigurationReg2_RXSLEN10_16Bits);
    	break;
    
    	case 24:
    	ret = pTAS2562->update_bits(pTAS2562,
    		TAS2562_TDMConfigurationReg2,
    		TAS2562_TDMConfigurationReg2_RXSLEN10_Mask,
    		TAS2562_TDMConfigurationReg2_RXSLEN10_24Bits);
    	break;
    
    	case 32:
    	ret = pTAS2562->update_bits(pTAS2562,
    		TAS2562_TDMConfigurationReg2,
    		TAS2562_TDMConfigurationReg2_RXSLEN10_Mask,
    		TAS2562_TDMConfigurationReg2_RXSLEN10_32Bits);
    	break;
    
    	case 0:
    	/* Do not change slot width */
    	break;
    
    	default:
    		dev_err(pTAS2562->dev, "slot width not supported");
    		ret = -EINVAL;
    	}
    
    	if (ret >= 0)
    		pTAS2562->mnSlot_width = slot_width;
    
    	return ret;
    }
    
    static int tas2562_set_bitwidth(struct tas2562_priv *pTAS2562, int bitwidth)
    {
    	int slot_width_tmp = 16;
    	dev_info(pTAS2562->dev, "%s %d\n", __func__, bitwidth);
    
    	switch (bitwidth) {
    	case SNDRV_PCM_FORMAT_S16_LE:
    			pTAS2562->update_bits(pTAS2562,
    			TAS2562_TDMConfigurationReg2,
    			TAS2562_TDMConfigurationReg2_RXWLEN32_Mask,
    			TAS2562_TDMConfigurationReg2_RXWLEN32_16Bits);
    			pTAS2562->mnCh_size = 16;
    			if (pTAS2562->mnSlot_width == 0)
    				slot_width_tmp = 16;
    		break;
    	case SNDRV_PCM_FORMAT_S24_LE:
    			pTAS2562->update_bits(pTAS2562,
    			TAS2562_TDMConfigurationReg2,
    			TAS2562_TDMConfigurationReg2_RXWLEN32_Mask,
    			TAS2562_TDMConfigurationReg2_RXWLEN32_24Bits);
    			pTAS2562->mnCh_size = 24;
    			if (pTAS2562->mnSlot_width == 0)
    				slot_width_tmp = 32;
    		break;
    	case SNDRV_PCM_FORMAT_S32_LE:
    			pTAS2562->update_bits(pTAS2562,
    			TAS2562_TDMConfigurationReg2,
    			TAS2562_TDMConfigurationReg2_RXWLEN32_Mask,
    			TAS2562_TDMConfigurationReg2_RXWLEN32_32Bits);
    			pTAS2562->mnCh_size = 32;
    			if (pTAS2562->mnSlot_width == 0)
    				slot_width_tmp = 32;
    		break;
    
    	default:
    		dev_info(pTAS2562->dev, "Not supported params format\n");
    	}
    
    	/* If machine driver did not call set slot width */
    	if (pTAS2562->mnSlot_width == 0)
    		tas2562_set_slot(pTAS2562->codec, slot_width_tmp);
    
    	dev_info(pTAS2562->dev, "mnCh_size: %d\n", pTAS2562->mnCh_size);
    	pTAS2562->mnPCMFormat = bitwidth;
    
    	return 0;
    }
    
    static int tas2562_set_samplerate(struct tas2562_priv *pTAS2562, int samplerate)
    {
    	switch (samplerate) {
    	case 48000:
    			pTAS2562->update_bits(pTAS2562,
    				TAS2562_TDMConfigurationReg0,
    				TAS2562_TDMConfigurationReg0_SAMPRATERAMP_Mask,
    				TAS2562_TDMConfigurationReg0_SAMPRATERAMP_48KHz);
    			pTAS2562->update_bits(pTAS2562,
    				TAS2562_TDMConfigurationReg0,
    				TAS2562_TDMConfigurationReg0_SAMPRATE31_Mask,
    				TAS2562_TDMConfigurationReg0_SAMPRATE31_44_1_48kHz);
    			break;
    	case 44100:
    			pTAS2562->update_bits(pTAS2562,
    				TAS2562_TDMConfigurationReg0,
    				TAS2562_TDMConfigurationReg0_SAMPRATERAMP_Mask,
    				TAS2562_TDMConfigurationReg0_SAMPRATERAMP_44_1KHz);
    			pTAS2562->update_bits(pTAS2562,
    				TAS2562_TDMConfigurationReg0,
    				TAS2562_TDMConfigurationReg0_SAMPRATE31_Mask,
    				TAS2562_TDMConfigurationReg0_SAMPRATE31_44_1_48kHz);
    			break;
    	case 96000:
    			pTAS2562->update_bits(pTAS2562,
    				TAS2562_TDMConfigurationReg0,
    				TAS2562_TDMConfigurationReg0_SAMPRATERAMP_Mask,
    				TAS2562_TDMConfigurationReg0_SAMPRATERAMP_48KHz);
    			pTAS2562->update_bits(pTAS2562,
    				TAS2562_TDMConfigurationReg0,
    				TAS2562_TDMConfigurationReg0_SAMPRATE31_Mask,
    				TAS2562_TDMConfigurationReg0_SAMPRATE31_88_2_96kHz);
    			break;
    	case 88200:
    			pTAS2562->update_bits(pTAS2562,
    				TAS2562_TDMConfigurationReg0,
    				TAS2562_TDMConfigurationReg0_SAMPRATERAMP_Mask,
    				TAS2562_TDMConfigurationReg0_SAMPRATERAMP_44_1KHz);
    			pTAS2562->update_bits(pTAS2562,
    				TAS2562_TDMConfigurationReg0,
    				TAS2562_TDMConfigurationReg0_SAMPRATE31_Mask,
    				TAS2562_TDMConfigurationReg0_SAMPRATE31_88_2_96kHz);
    			break;
    	case 19200:
    			pTAS2562->update_bits(pTAS2562,
    				TAS2562_TDMConfigurationReg0,
    				TAS2562_TDMConfigurationReg0_SAMPRATERAMP_Mask,
    				TAS2562_TDMConfigurationReg0_SAMPRATERAMP_48KHz);
    			pTAS2562->update_bits(pTAS2562,
    				TAS2562_TDMConfigurationReg0,
    				TAS2562_TDMConfigurationReg0_SAMPRATE31_Mask,
    				TAS2562_TDMConfigurationReg0_SAMPRATE31_176_4_192kHz);
    			break;
    	case 17640:
    			pTAS2562->update_bits(pTAS2562,
    				TAS2562_TDMConfigurationReg0,
    				TAS2562_TDMConfigurationReg0_SAMPRATERAMP_Mask,
    				TAS2562_TDMConfigurationReg0_SAMPRATERAMP_44_1KHz);
    			pTAS2562->update_bits(pTAS2562,
    				TAS2562_TDMConfigurationReg0,
    				TAS2562_TDMConfigurationReg0_SAMPRATE31_Mask,
    				TAS2562_TDMConfigurationReg0_SAMPRATE31_176_4_192kHz);
    			break;
    	default:
    			dev_info(pTAS2562->dev, "%s, unsupported sample rate, %d\n", __func__, samplerate);
    
    	}
    
    	pTAS2562->mnSamplingRate = samplerate;
    	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 *pTAS2562 = snd_soc_codec_get_drvdata(codec);
    
    	pValue->value.integer.value[0] = pTAS2562->mbMute;
    	dev_dbg(pTAS2562->dev, "tas2562_mute_ctrl_get = %d\n",
    		pTAS2562->mbMute);
    
    	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 *pTAS2562 = snd_soc_codec_get_drvdata(codec);
    
    	mbMute = pValue->value.integer.value[0];
    
    	dev_dbg(pTAS2562->dev, "tas2562_mute_ctrl_put = %d\n", mbMute);
    
    	pTAS2562->mbMute = !!mbMute;
    
    	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 *pTAS2562 = snd_soc_codec_get_drvdata(codec);
    	int blr_clk_ratio;
    	int ret = 0;
    
    	dev_dbg(pTAS2562->dev, "%s, format: %d\n", __func__,
    		params_format(params));
    
    	mutex_lock(&pTAS2562->codec_lock);
    
    	ret = tas2562_set_bitwidth(pTAS2562, params_format(params));
    	if(ret < 0)
    	{
    		dev_info(pTAS2562->dev, "set bitwidth failed, %d\n", ret);
    		goto ret;
    	}
    
    	blr_clk_ratio = params_channels(params) * pTAS2562->mnCh_size;
    	dev_info(pTAS2562->dev, "blr_clk_ratio: %d\n", blr_clk_ratio);
    	if(blr_clk_ratio != 0)
    		tas2562_slot_config(pTAS2562->codec, pTAS2562, blr_clk_ratio);
    
    	dev_info(pTAS2562->dev, "%s, sample rate: %d\n", __func__,
    		params_rate(params));
    
    	ret = tas2562_set_samplerate(pTAS2562, params_rate(params));
    
    ret:
    	mutex_unlock(&pTAS2562->codec_lock);
    	return ret;
    }
    
    static int tas2562_set_fmt(struct tas2562_priv *pTAS2562, 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(pTAS2562->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(pTAS2562->dev, "INV format: NBNF\n");
    		asi_cfg_1 |= TAS2562_TDMConfigurationReg1_RXEDGE_Rising;
    		break;
    	case SND_SOC_DAIFMT_IB_NF:
    		dev_info(pTAS2562->dev, "INV format: IBNF\n");
    		asi_cfg_1 |= TAS2562_TDMConfigurationReg1_RXEDGE_Falling;
    		break;
    	default:
    		dev_err(pTAS2562->dev, "ASI format Inverse is not found\n");
    		ret = -EINVAL;
    	}
    
    	pTAS2562->update_bits(pTAS2562, 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(pTAS2562->dev, "DAI Format is not found, fmt=0x%x\n", fmt);
    	ret = -EINVAL;
    		break;
    	}
    
    	pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg1,
    		TAS2562_TDMConfigurationReg1_RXOFFSET51_Mask,
    		(tdm_rx_start_slot << TAS2562_TDMConfigurationReg1_RXOFFSET51_Shift));
    
    	pTAS2562->mnASIFormat = 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 *pTAS2562 = snd_soc_codec_get_drvdata(codec);
    	int ret = 0;
    
    	dev_dbg(pTAS2562->dev, "%s, format=0x%x\n", __func__, fmt);
    
    	ret = tas2562_set_fmt(pTAS2562, 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 *pTAS2562 = snd_soc_codec_get_drvdata(codec);
    
    	dev_dbg(pTAS2562->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(codec, 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   = 1,
    			.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 *pTAS2562)
    {
    	int ret;
    
    #ifdef TAS2558_CODEC
    /* Max voltage to 9V */
    	ret = pTAS2562->update_bits(pTAS2562, TAS2562_BoostConfiguration2,
    					TAS2562_BoostConfiguration2_BoostMaxVoltage_Mask,
    					0x7);
    	if(ret < 0)
    		return ret;
    
            ret = pTAS2562->update_bits(pTAS2562, TAS2562_PlaybackConfigurationReg0,
                                            TAS2562_PlaybackConfigurationReg0_AmplifierLevel51_Mask,
                                            0xd << 1);
            if(ret < 0)
                    return ret;
    
    #endif
    
    	ret = pTAS2562->write(pTAS2562, TAS2562_MiscConfigurationReg0, 0xcf);
    	if(ret < 0)
    		return ret;
    	ret = pTAS2562->write(pTAS2562, TAS2562_TDMConfigurationReg4, 0x01);
    	if(ret < 0)
    		return ret;
    	ret = pTAS2562->write(pTAS2562, TAS2562_ClockConfiguration, 0x0c);
    	if(ret < 0)
    		return ret;
    	ret = tas2562_i2c_load_data(pTAS2562, p_tas2562_classH_D_data);
    
    	return ret;
    }
    
    static int tas2562_codec_probe(struct snd_soc_codec *codec)
    {
    	int ret;
    	struct tas2562_priv *pTAS2562 = 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(pTAS2562);
    	tas2562_iv_enable(pTAS2562, 1);
    	pTAS2562->codec = codec;
    	dev_err(pTAS2562->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 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 Mute", SND_SOC_NOPM, 0, 0x0001, 0,
    			tas2562_mute_ctrl_get, tas2562_mute_ctrl_put),
    };
    
    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 *pTAS2562)
    {
    	int nResult = 0;
    
    	dev_info(pTAS2562->dev, "%s, enter\n", __func__);
    	nResult = snd_soc_register_codec(pTAS2562->dev,
    		&soc_codec_driver_tas2562,
    		tas2562_dai_driver, ARRAY_SIZE(tas2562_dai_driver));
    	return nResult;
    }
    
    int tas2562_deregister_codec(struct tas2562_priv *pTAS2562)
    {
    	snd_soc_unregister_codec(pTAS2562->dev);
    
    	return 0;
    }
    
    void tas2562_LoadConfig(struct tas2562_priv *pTAS2562)
    {
    	int ret = 0;
    
    	if (hrtimer_active(&pTAS2562->mtimer))
    	{
    		dev_info(pTAS2562->dev, "cancel timer\n");
    		hrtimer_cancel(&pTAS2562->mtimer);
    	} else
    		dev_info(pTAS2562->dev, "timer not active\n");
    
    	pTAS2562->hw_reset(pTAS2562);
    	msleep(2);
    	pTAS2562->write(pTAS2562, TAS2562_SoftwareReset,
    			TAS2562_SoftwareReset_SoftwareReset_Reset);
    	msleep(3);
    
    	tas2562_load_init(pTAS2562);
    	tas2562_iv_enable(pTAS2562, tas2562iv_enable);
    
    	ret = tas2562_set_slot(pTAS2562->codec, pTAS2562->mnSlot_width);
    	if (ret < 0)
    		goto end;
    
    	ret = tas2562_set_fmt(pTAS2562, pTAS2562->mnASIFormat);
    	if (ret < 0)
    		goto end;
    
    	ret = tas2562_set_bitwidth(pTAS2562, pTAS2562->mnPCMFormat);
    	if (ret < 0)
    		goto end;
    
    	ret = tas2562_set_samplerate(pTAS2562, pTAS2562->mnSamplingRate);
    	if (ret < 0)
    		goto end;
    
    	ret = tas2562_set_power_state(pTAS2562, pTAS2562->mnPowerState);
    	if (ret < 0)
    		goto end;
    
    end:
    /* power up failed, restart later */
    	if (ret < 0)
    		schedule_delayed_work(&pTAS2562->irq_work,
    				msecs_to_jiffies(1000));
    }
    
    MODULE_AUTHOR("Texas Instruments Inc.");
    MODULE_DESCRIPTION("TAS2562 ALSA SOC Smart Amplifier driver");
    MODULE_LICENSE("GPL");
    #endif /* CONFIG_TAS2562_CODEC */
    
    tas2562-codec.h
    tas2562-misc.c
    /*
    ** =============================================================================
    ** 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 1
    #include <linux/module.h>
    #include <linux/moduleparam.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <linux/pm.h>
    #include <linux/i2c.h>
    #include <linux/gpio.h>
    #include <linux/regulator/consumer.h>
    #include <linux/firmware.h>
    #include <linux/regmap.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    #include <linux/slab.h>
    #include <linux/syscalls.h>
    #include <linux/fcntl.h>
    #include <linux/miscdevice.h>
    #include <linux/uaccess.h>
    
    #include "tas2562.h"
    #include "tas2562-misc.h"
    #include <linux/dma-mapping.h>
    
    static int g_logEnable = 1;
    static struct tas2562_priv *g_tas2562;
    
    static int tas2562_file_open(struct inode *inode, struct file *file)
    {
    	struct tas2562_priv *pTAS2562 = g_tas2562;
    
    	if (!try_module_get(THIS_MODULE))
    		return -ENODEV;
    
    	file->private_data = (void *)pTAS2562;
    
    	if (g_logEnable)
    		dev_info(pTAS2562->dev, "%s\n", __func__);
    	return 0;
    }
    
    static int tas2562_file_release(struct inode *inode, struct file *file)
    {
    	struct tas2562_priv *pTAS2562 = (struct tas2562_priv *)file->private_data;
    
    	if (g_logEnable)
    		dev_info(pTAS2562->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 *pTAS2562 = (struct tas2562_priv *)file->private_data;
    	int ret = 0;
    	unsigned int nValue = 0;
    	unsigned char value = 0;
    	unsigned char *p_kBuf = NULL;
    
    	mutex_lock(&pTAS2562->file_lock);
    
    	switch (pTAS2562->mnDBGCmd) {
    	case TIAUDIO_CMD_REG_READ:
    	{
    		if (g_logEnable)
    			dev_info(pTAS2562->dev,
    				"TIAUDIO_CMD_REG_READ: current_reg = 0x%x, count=%d\n",
    				pTAS2562->mnCurrentReg, (int)count);
    		if (count == 1) {
    			ret = pTAS2562->read(pTAS2562, pTAS2562->mnCurrentReg, &nValue);
    			if (ret < 0) {
    				dev_err(pTAS2562->dev, "dev read fail %d\n", ret);
    				break;
    			}
    
    			value = (u8)nValue;
    			if (g_logEnable)
    				dev_info(pTAS2562->dev, "TIAUDIO_CMD_REG_READ: nValue=0x%x, value=0x%x\n",
    					nValue, value);
    			ret = copy_to_user(buf, &value, 1);
    			if (ret != 0) {
    				/* Failed to copy all the data, exit */
    				dev_err(pTAS2562->dev, "copy to user fail %d\n", ret);
    			}
    		} else if (count > 1) {
    			p_kBuf = kzalloc(count, GFP_KERNEL);
    			if (p_kBuf != NULL) {
    				ret = pTAS2562->bulk_read(pTAS2562, pTAS2562->mnCurrentReg, p_kBuf, count);
    				if (ret < 0) {
    					dev_err(pTAS2562->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(pTAS2562->dev, "copy to user fail %d\n", ret);
    					}
    				}
    
    				kfree(p_kBuf);
    			} else {
    				dev_err(pTAS2562->dev, "read no mem\n");
    			}
    		}
    	}
    	break;
    	}
    	pTAS2562->mnDBGCmd = 0;
    
    	mutex_unlock(&pTAS2562->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 *pTAS2562 = (struct tas2562_priv *)file->private_data;
    	int ret = 0;
    	unsigned char *p_kBuf = NULL;
    	unsigned int reg = 0;
    	unsigned int len = 0;
    
    	mutex_lock(&pTAS2562->file_lock);
    
    	p_kBuf = kzalloc(count, GFP_KERNEL);
    	if (p_kBuf == NULL) {
    		dev_err(pTAS2562->dev, "write no mem\n");
    		goto err;
    	}
    
    	ret = copy_from_user(p_kBuf, buf, count);
    	if (ret != 0) {
    		dev_err(pTAS2562->dev, "copy_from_user failed.\n");
    		goto err;
    	}
    
    	pTAS2562->mnDBGCmd = p_kBuf[0];
    	switch (pTAS2562->mnDBGCmd) {
    	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 = pTAS2562->write(pTAS2562, reg, p_kBuf[5]);
    			if (g_logEnable)
    				dev_info(pTAS2562->dev,
    					"TIAUDIO_CMD_REG_WITE, Reg=0x%x, Val=0x%x\n",
    					reg, p_kBuf[5]);
    		} else {
    			ret = pTAS2562->bulk_write(pTAS2562, reg, &p_kBuf[5], len);
    		}
    	} else {
    		dev_err(pTAS2562->dev, "%s, write len fail, count=%d.\n",
    			__func__, (int)count);
    	}
    	pTAS2562->mnDBGCmd = 0;
    	break;
    
    	case TIAUDIO_CMD_REG_READ:
    	if (count == 5) {
    		pTAS2562->mnCurrentReg = ((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_logEnable) {
    			dev_info(pTAS2562->dev,
    				"TIAUDIO_CMD_REG_READ, whole=0x%x\n",
    				pTAS2562->mnCurrentReg);
    		}
    	} else {
    		dev_err(pTAS2562->dev, "read len fail.\n");
    	}
    	break;
    	}
    err:
    	if (p_kBuf != NULL)
    		kfree(p_kBuf);
    
    	mutex_unlock(&pTAS2562->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 *pTAS2562)
    {
    	int ret = 0;
    
    	g_tas2562 = pTAS2562;
    	ret = misc_register(&tas2562_misc);
    	if (ret) {
    		dev_err(pTAS2562->dev, "TAS2562 misc fail: %d\n", ret);
    	}
    
    	dev_info(pTAS2562->dev, "%s, leave\n", __func__);
    
    	return ret;
    }
    
    int tas2562_deregister_misc(struct tas2562_priv *pTAS2562)
    {
    	misc_deregister(&tas2562_misc);
    	return 0;
    }
    
    MODULE_AUTHOR("Texas Instruments Inc.");
    MODULE_DESCRIPTION("TAS2562 Misc Smart Amplifier driver");
    MODULE_LICENSE("GPL");
    
    tas2562-misc.h
    tas2562-regmap.c
    /*
     * 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 1
    #include <linux/module.h>
    #include <linux/moduleparam.h>
    #include <linux/err.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <linux/pm.h>
    #include <linux/i2c.h>
    #include <linux/gpio.h>
    #include <linux/regulator/consumer.h>
    #include <linux/firmware.h>
    #include <linux/regmap.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    #include <linux/slab.h>
    #include <sound/soc.h>
    #include <linux/interrupt.h>
    #include <linux/irq.h>
    
    #include "tas2562.h"
    #include "tas2562-codec.h"
    #include "tas2562-misc.h"
    
    static char pICN[] = {0x00, 0x03, 0x46, 0xdc};
    
    static int tas2562_change_book_page(struct tas2562_priv *pTAS2562,
    	int book, int page)
    {
    	int nResult = 0;
    
    	if ((pTAS2562->mnCurrentBook == book)
    		&& (pTAS2562->mnCurrentPage == page))
    		goto end;
    
    	if (pTAS2562->mnCurrentBook != book) {
    		nResult = regmap_write(pTAS2562->regmap, TAS2562_BOOKCTL_PAGE, 0);
    		if (nResult < 0) {
    			dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n",
    				__func__, __LINE__, nResult);
    			goto end;
    		}
    		pTAS2562->mnCurrentPage = 0;
    		nResult = regmap_write(pTAS2562->regmap, TAS2562_BOOKCTL_REG, book);
    		if (nResult < 0) {
    			dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n",
    				__func__, __LINE__, nResult);
    			goto end;
    		}
    		pTAS2562->mnCurrentBook = book;
    	}
    
    	if (pTAS2562->mnCurrentPage != page) {
    		nResult = regmap_write(pTAS2562->regmap, TAS2562_BOOKCTL_PAGE, page);
    		if (nResult < 0) {
    			dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n",
    				__func__, __LINE__, nResult);
    			goto end;
    		}
    		pTAS2562->mnCurrentPage = page;
    	}
    
    end:
    	return nResult;
    }
    
    static int tas2562_dev_read(struct tas2562_priv *pTAS2562,
    	unsigned int reg, unsigned int *pValue)
    {
    	int nResult = 0;
    
    	mutex_lock(&pTAS2562->dev_lock);
    
    	nResult = tas2562_change_book_page(pTAS2562,
    		TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg));
    	if (nResult < 0)
    		goto end;
    
    	nResult = regmap_read(pTAS2562->regmap, TAS2562_PAGE_REG(reg), pValue);
    	if (nResult < 0)
    		dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n",
    			__func__, __LINE__, nResult);
    	else
    		dev_dbg(pTAS2562->dev, "%s: BOOK:PAGE:REG %u:%u:%u\n", __func__,
    			TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg),
    			TAS2562_PAGE_REG(reg));
    
    end:
    	mutex_unlock(&pTAS2562->dev_lock);
    	return nResult;
    }
    
    static int tas2562_dev_write(struct tas2562_priv *pTAS2562,
    	unsigned int reg, unsigned int value)
    {
    	int nResult = 0;
    
    	mutex_lock(&pTAS2562->dev_lock);
    
    	nResult = tas2562_change_book_page(pTAS2562,
    		TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg));
    	if (nResult < 0)
    		goto end;
    
    	nResult = regmap_write(pTAS2562->regmap, TAS2562_PAGE_REG(reg),
    			value);
    	if (nResult < 0)
    		dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n",
    			__func__, __LINE__, nResult);
    	else
    		dev_dbg(pTAS2562->dev, "%s: BOOK:PAGE:REG %u:%u:%u, VAL: 0x%02x\n",
    			__func__, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg),
    			TAS2562_PAGE_REG(reg), value);
    
    end:
    	mutex_unlock(&pTAS2562->dev_lock);
    	return nResult;
    }
    
    static int tas2562_dev_bulk_write(struct tas2562_priv *pTAS2562,
    	unsigned int reg, unsigned char *pData, unsigned int nLength)
    {
    	int nResult = 0;
    
    	mutex_lock(&pTAS2562->dev_lock);
    
    	nResult = tas2562_change_book_page(pTAS2562,
    		TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg));
    	if (nResult < 0)
    		goto end;
    
    	nResult = regmap_bulk_write(pTAS2562->regmap,
    		TAS2562_PAGE_REG(reg), pData, nLength);
    	if (nResult < 0)
    		dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n",
    			__func__, __LINE__, nResult);
    	else
    		dev_dbg(pTAS2562->dev, "%s: BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n",
    			__func__, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg),
    			TAS2562_PAGE_REG(reg), nLength);
    
    end:
    	mutex_unlock(&pTAS2562->dev_lock);
    	return nResult;
    }
    
    static int tas2562_dev_bulk_read(struct tas2562_priv *pTAS2562,
    	unsigned int reg, unsigned char *pData, unsigned int nLength)
    {
    	int nResult = 0;
    
    	mutex_lock(&pTAS2562->dev_lock);
    
    	nResult = tas2562_change_book_page(pTAS2562,
    		TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg));
    	if (nResult < 0)
    		goto end;
    
    	nResult = regmap_bulk_read(pTAS2562->regmap,
    	TAS2562_PAGE_REG(reg), pData, nLength);
    	if (nResult < 0)
    		dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n",
    			__func__, __LINE__, nResult);
    	else
    		dev_dbg(pTAS2562->dev, "%s: BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n",
    			__func__, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg),
    			TAS2562_PAGE_REG(reg), nLength);
    end:
    	mutex_unlock(&pTAS2562->dev_lock);
    	return nResult;
    }
    
    static int tas2562_dev_update_bits(struct tas2562_priv *pTAS2562,
    	unsigned int reg, unsigned int mask, unsigned int value)
    {
    	int nResult = 0;
    
    	mutex_lock(&pTAS2562->dev_lock);
    	nResult = tas2562_change_book_page(pTAS2562,
    		TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg));
    	if (nResult < 0)
    		goto end;
    
    	nResult = regmap_update_bits(pTAS2562->regmap,
    	TAS2562_PAGE_REG(reg), mask, value);
    	if (nResult < 0)
    		dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n",
    			__func__, __LINE__, nResult);
    	else
    		dev_dbg(pTAS2562->dev, "%s: BOOK:PAGE:REG %u:%u:%u, mask: 0x%x, val=0x%x\n",
    			__func__, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg),
    			TAS2562_PAGE_REG(reg), mask, value);
    end:
    	mutex_unlock(&pTAS2562->dev_lock);
    	return nResult;
    }
    
    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 *pTAS2562)
    {
    	if (gpio_is_valid(pTAS2562->mnResetGPIO)) {
    		gpio_direction_output(pTAS2562->mnResetGPIO, 0);
    		msleep(5);
    		gpio_direction_output(pTAS2562->mnResetGPIO, 1);
    		msleep(2);
    	}
    	dev_err(pTAS2562->dev, "gpio up !!\n");
    
    	pTAS2562->mnCurrentBook = -1;
    	pTAS2562->mnCurrentPage = -1;
    }
    
    void tas2562_enableIRQ(struct tas2562_priv *pTAS2562, bool enable)
    {
    	if (enable) {
    		if (pTAS2562->mbIRQEnable)
    			return;
    
    		if (gpio_is_valid(pTAS2562->mnIRQGPIO))
    			enable_irq(pTAS2562->mnIRQ);
    
    		schedule_delayed_work(&pTAS2562->irq_work, msecs_to_jiffies(10));
    
    		pTAS2562->mbIRQEnable = true;
    	} else {
    		if (gpio_is_valid(pTAS2562->mnIRQGPIO))
    			disable_irq_nosync(pTAS2562->mnIRQ);
    		pTAS2562->mbIRQEnable = false;
    	}
    }
    
    static void irq_work_routine(struct work_struct *work)
    {
    	struct tas2562_priv *pTAS2562 =
    		container_of(work, struct tas2562_priv, irq_work.work);
    	unsigned int nDevInt1Status = 0, nDevInt2Status = 0;
    	int nCounter = 2;
    	int nResult = 0;
    	int irqreg;
    
    	dev_info(pTAS2562->dev, "%s\n", __func__);
    #ifdef CONFIG_TAS2562_CODEC
    	mutex_lock(&pTAS2562->codec_lock);
    #endif
    
    	if (pTAS2562->mbRuntimeSuspend) {
    		dev_info(pTAS2562->dev, "%s, Runtime Suspended\n", __func__);
    		goto end;
    	}
    
    	if (pTAS2562->mnPowerState == TAS2562_POWER_SHUTDOWN) {
    		dev_info(pTAS2562->dev, "%s, device not powered\n", __func__);
    		goto end;
    	}
    
    	nResult = tas2562_dev_write(pTAS2562, TAS2562_InterruptMaskReg0,
    				TAS2562_InterruptMaskReg0_Disable);
    	if (nResult < 0)
    		goto reload;
    	nResult = tas2562_dev_write(pTAS2562, TAS2562_InterruptMaskReg1,
    				TAS2562_InterruptMaskReg1_Disable);
    	if (nResult < 0)
    		goto reload;
    
    	nResult = tas2562_dev_read(pTAS2562, TAS2562_LatchedInterruptReg0, &nDevInt1Status);
    	if (nResult >= 0)
    		nResult = tas2562_dev_read(pTAS2562, TAS2562_LatchedInterruptReg1, &nDevInt2Status);
    	else
    		goto reload;
    
    	dev_dbg(pTAS2562->dev, "IRQ status : 0x%x, 0x%x\n",
    			nDevInt1Status, nDevInt2Status);
    
    	if (((nDevInt1Status & 0x7) != 0) || ((nDevInt2Status & 0x0f) != 0)) {
    		/* in case of INT_OC, INT_OT, INT_OVLT, INT_UVLT, INT_BO */
    
    		if (nDevInt1Status & TAS2562_LatchedInterruptReg0_OCEFlagSticky_Interrupt) {
    			pTAS2562->mnErrCode |= ERROR_OVER_CURRENT;
    			dev_err(pTAS2562->dev, "SPK over current!\n");
    		} else
    			pTAS2562->mnErrCode &= ~ERROR_OVER_CURRENT;
    
    		if (nDevInt1Status & TAS2562_LatchedInterruptReg0_OTEFlagSticky_Interrupt) {
    			pTAS2562->mnErrCode |= ERROR_DIE_OVERTEMP;
    			dev_err(pTAS2562->dev, "die over temperature!\n");
    		} else
    			pTAS2562->mnErrCode &= ~ERROR_DIE_OVERTEMP;
    
    		if (nDevInt2Status & TAS2562_LatchedInterruptReg1_VBATOVLOSticky_Interrupt) {
    			pTAS2562->mnErrCode |= ERROR_OVER_VOLTAGE;
    			dev_err(pTAS2562->dev, "SPK over voltage!\n");
    		} else
    			pTAS2562->mnErrCode &= ~ERROR_UNDER_VOLTAGE;
    
    		if (nDevInt2Status & TAS2562_LatchedInterruptReg1_VBATUVLOSticky_Interrupt) {
    			pTAS2562->mnErrCode |= ERROR_UNDER_VOLTAGE;
    			dev_err(pTAS2562->dev, "SPK under voltage!\n");
    		} else
    			pTAS2562->mnErrCode &= ~ERROR_UNDER_VOLTAGE;
    
    		if (nDevInt2Status & TAS2562_LatchedInterruptReg1_BrownOutFlagSticky_Interrupt) {
    			pTAS2562->mnErrCode |= ERROR_BROWNOUT;
    			dev_err(pTAS2562->dev, "brownout!\n");
    		} else
    			pTAS2562->mnErrCode &= ~ERROR_BROWNOUT;
    
    		dev_err(pTAS2562->dev, "before goto reload\n");
    		goto reload;
    	} else {
    		nCounter = 2;
    
    		while (nCounter > 0) {
    			nResult = tas2562_dev_read(pTAS2562, TAS2562_PowerControl, &nDevInt1Status);
    			if (nResult < 0)
    				goto reload;
    
    			if ((nDevInt1Status & TAS2562_PowerControl_OperationalMode10_Mask)
    				!= TAS2562_PowerControl_OperationalMode10_Shutdown)
    				break;
    
    			pTAS2562->read(pTAS2562, TAS2562_LatchedInterruptReg0, &irqreg);
    			dev_info(pTAS2562->dev, "IRQ reg is: %s %d, %d\n", __func__, irqreg, __LINE__);
    
    			nResult = pTAS2562->update_bits(pTAS2562, TAS2562_PowerControl,
    				TAS2562_PowerControl_OperationalMode10_Mask,
    				TAS2562_PowerControl_OperationalMode10_Active);
    			if (nResult < 0)
    				goto reload;
    
    			dev_info(pTAS2562->dev, "set ICN to -80dB\n");
    			nResult = pTAS2562->bulk_write(pTAS2562, TAS2562_ICN_REG, pICN, 4);
    
    			pTAS2562->read(pTAS2562, TAS2562_LatchedInterruptReg0, &irqreg);
    			dev_info(pTAS2562->dev, "IRQ reg is: %s, %d, %d\n", __func__, irqreg, __LINE__);
    
    			nCounter--;
    			if (nCounter > 0) {
    				/* in case check pow status just after power on TAS2562 */
    				dev_dbg(pTAS2562->dev, "PowSts B: 0x%x, check again after 10ms\n",
    					nDevInt1Status);
    				msleep(10);
    			}
    		}
    
    		if ((nDevInt1Status & TAS2562_PowerControl_OperationalMode10_Mask)
    			== TAS2562_PowerControl_OperationalMode10_Shutdown) {
    			dev_err(pTAS2562->dev, "%s, Critical ERROR REG[0x%x] = 0x%x\n",
    				__func__,
    				TAS2562_PowerControl,
    				nDevInt1Status);
    			pTAS2562->mnErrCode |= ERROR_CLASSD_PWR;
    			goto reload;
    		}
    		pTAS2562->mnErrCode &= ~ERROR_CLASSD_PWR;
    	}
    
    	nResult = tas2562_dev_write(pTAS2562, TAS2562_InterruptMaskReg0, 0xf8);
    	if (nResult < 0)
    		goto reload;
    
    	nResult = tas2562_dev_write(pTAS2562, TAS2562_InterruptMaskReg1, 0xb1);
    	if (nResult < 0)
    		goto reload;
    
    	goto end;
    
    reload:
    	/* hardware reset and reload */
    	nResult = -1;
    	tas2562_LoadConfig(pTAS2562);
    
    end:
    	if (nResult >= 0) {
    		tas2562_enableIRQ(pTAS2562, true);
    	    dev_info(pTAS2562->dev, "enable IRQ\n");
    	}
    
    	nResult = gpio_get_value(pTAS2562->mnIRQGPIO);
    	dev_info(pTAS2562->dev, "%s, irq GPIO state: %d\n", __func__, nResult);
    
    	if (!hrtimer_active(&pTAS2562->mtimer)) {
    		dev_dbg(pTAS2562->dev, "%s, start timer\n", __func__);
    		hrtimer_start(&pTAS2562->mtimer,
    			ns_to_ktime((u64)CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL);
    	} else
    		dev_info(pTAS2562->dev, "timer not active\n");
    
    #ifdef CONFIG_TAS2562_CODEC
    	mutex_unlock(&pTAS2562->codec_lock);
    #endif
    }
    
    static enum hrtimer_restart timer_func(struct hrtimer *timer)
    {
    	struct tas2562_priv *pTAS2562 = container_of(timer,
    		struct tas2562_priv, mtimer);
    
    	if (pTAS2562->mbPowerUp) {
    		if (!delayed_work_pending(&pTAS2562->irq_work))
    			schedule_delayed_work(&pTAS2562->irq_work,
    				msecs_to_jiffies(20));
    	}
    
    	return HRTIMER_NORESTART;
    }
    
    static irqreturn_t tas2562_irq_handler(int irq, void *dev_id)
    {
    	struct tas2562_priv *pTAS2562 = (struct tas2562_priv *)dev_id;
    
    	tas2562_enableIRQ(pTAS2562, false);
    	/* get IRQ status after 100 ms */
    	schedule_delayed_work(&pTAS2562->irq_work, msecs_to_jiffies(100));
    	return IRQ_HANDLED;
    }
    
    static int tas2562_runtime_suspend(struct tas2562_priv *pTAS2562)
    {
    	dev_dbg(pTAS2562->dev, "%s\n", __func__);
    
    	pTAS2562->mbRuntimeSuspend = true;
    
    	if (hrtimer_active(&pTAS2562->mtimer)) {
    		dev_dbg(pTAS2562->dev, "cancel timer\n");
    		hrtimer_cancel(&pTAS2562->mtimer);
    	}
    
    	if (delayed_work_pending(&pTAS2562->irq_work)) {
    		dev_dbg(pTAS2562->dev, "cancel IRQ work\n");
    		cancel_delayed_work_sync(&pTAS2562->irq_work);
    	}
    
    	return 0;
    }
    
    static int tas2562_runtime_resume(struct tas2562_priv *pTAS2562)
    {
    	dev_dbg(pTAS2562->dev, "%s\n", __func__);
    
    	if (pTAS2562->mbPowerUp) {
    		if (!hrtimer_active(&pTAS2562->mtimer)) {
    		dev_dbg(pTAS2562->dev, "%s, start check timer\n", __func__);
    			hrtimer_start(&pTAS2562->mtimer,
    				ns_to_ktime((u64)CHECK_PERIOD * NSEC_PER_MSEC),
    				HRTIMER_MODE_REL);
    		}
    	}
    
    	pTAS2562->mbRuntimeSuspend = false;
    
    	return 0;
    }
    
    static int tas2562_parse_dt(struct device *dev, struct tas2562_priv *pTAS2562)
    {
    	struct device_node *np = dev->of_node;
    	int rc = 0, ret = 0;
    
    //	u32 debounceInfo[2] = { 0, 0 };
    	rc = of_property_read_u32(np, "ti,asi-format", &pTAS2562->mnASIFormat);
    	if (rc) {
    		dev_err(pTAS2562->dev, "Looking up %s property in node %s failed %d\n",
    			"ti,asi-format", np->full_name, rc);
    	} else {
    		dev_dbg(pTAS2562->dev, "ti,asi-format=%d",
    			pTAS2562->mnASIFormat);
    	}
    
    	pTAS2562->mnResetGPIO = of_get_named_gpio(np, "ti,reset-gpio", 0);
    	if (!gpio_is_valid(pTAS2562->mnResetGPIO)) {
    		dev_err(pTAS2562->dev, "Looking up %s property in node %s failed %d\n",
    			"ti,reset-gpio", np->full_name, pTAS2562->mnResetGPIO);
    	} else {
    		dev_dbg(pTAS2562->dev, "ti,reset-gpio=%d",
    			pTAS2562->mnResetGPIO);
    	}
    
    	pTAS2562->mnIRQGPIO = of_get_named_gpio(np, "ti,irq-gpio", 0);
    	if (!gpio_is_valid(pTAS2562->mnIRQGPIO)) {
    		dev_err(pTAS2562->dev, "Looking up %s property in node %s failed %d\n",
    			"ti,irq-gpio", np->full_name, pTAS2562->mnIRQGPIO);
    	} else {
    		dev_dbg(pTAS2562->dev, "ti,irq-gpio=%d", pTAS2562->mnIRQGPIO);
    	}
    
    	return ret;
    }
    
    static int tas2562_i2c_probe(struct i2c_client *pClient,
    			const struct i2c_device_id *id)
    {
    	struct tas2562_priv *pTAS2562;
    	int nResult;
    
    	dev_err(&pClient->dev, "Driver ID: %s\n", TAS2562_DRIVER_ID);
    	dev_info(&pClient->dev, "%s enter\n", __func__);
    
    	pTAS2562 = devm_kzalloc(&pClient->dev,
    		sizeof(struct tas2562_priv), GFP_KERNEL);
    	if (pTAS2562 == NULL) {
    		dev_err(&pClient->dev, "failed to get i2c device\n");
    		nResult = -ENOMEM;
    		goto err;
    	}
    
    	pTAS2562->dev = &pClient->dev;
    	i2c_set_clientdata(pClient, pTAS2562);
    	dev_set_drvdata(&pClient->dev, pTAS2562);
    
    	pTAS2562->regmap = devm_regmap_init_i2c(pClient, &tas2562_i2c_regmap);
    	if (IS_ERR(pTAS2562->regmap)) {
    		nResult = PTR_ERR(pTAS2562->regmap);
    		dev_err(&pClient->dev, "Failed to allocate register map: %d\n",
    					nResult);
    		goto err;
    	}
    
    	if (pClient->dev.of_node)
    		tas2562_parse_dt(&pClient->dev, pTAS2562);
    
    	if (gpio_is_valid(pTAS2562->mnResetGPIO)) {
    		nResult = gpio_request(pTAS2562->mnResetGPIO, "TAS2562_RESET");
    		if (nResult) {
    			dev_err(pTAS2562->dev, "%s: Failed to request gpio %d\n",
    				__func__, pTAS2562->mnResetGPIO);
    			nResult = -EINVAL;
    			goto err;
    		}
    		tas2562_hw_reset(pTAS2562);
    	}
    
    	pTAS2562->read = tas2562_dev_read;
    	pTAS2562->write = tas2562_dev_write;
    	pTAS2562->bulk_read = tas2562_dev_bulk_read;
    	pTAS2562->bulk_write = tas2562_dev_bulk_write;
    	pTAS2562->update_bits = tas2562_dev_update_bits;
    	pTAS2562->hw_reset = tas2562_hw_reset;
    	pTAS2562->enableIRQ = tas2562_enableIRQ;
    	//pTAS2562->clearIRQ = tas2562_clearIRQ;
    	pTAS2562->runtime_suspend = tas2562_runtime_suspend;
    	pTAS2562->runtime_resume = tas2562_runtime_resume;
    	pTAS2562->mnPowerState = TAS2562_POWER_SHUTDOWN;
    
    	mutex_init(&pTAS2562->dev_lock);
    
    	/* Reset the chip */
    	nResult = tas2562_dev_write(pTAS2562, TAS2562_SoftwareReset, 0x01);
    	if (nResult < 0) {
    		dev_err(&pClient->dev, "I2c fail, %d\n", nResult);
    		goto err;
    	}
    
    	if (gpio_is_valid(pTAS2562->mnIRQGPIO)) {
    		nResult = gpio_request(pTAS2562->mnIRQGPIO, "TAS2562-IRQ");
    		if (nResult < 0) {
    			dev_err(pTAS2562->dev, "%s: GPIO %d request error\n",
    				__func__, pTAS2562->mnIRQGPIO);
    			goto err;
    		}
    		gpio_direction_input(pTAS2562->mnIRQGPIO);
    		nResult = gpio_get_value(pTAS2562->mnIRQGPIO);
    		dev_info(pTAS2562->dev, "irq GPIO state: %d\n", nResult);
    
    		pTAS2562->mnIRQ = gpio_to_irq(pTAS2562->mnIRQGPIO);
    		dev_dbg(pTAS2562->dev, "irq = %d\n", pTAS2562->mnIRQ);
    		INIT_DELAYED_WORK(&pTAS2562->irq_work, irq_work_routine);
    		nResult = request_threaded_irq(pTAS2562->mnIRQ, tas2562_irq_handler,
    				NULL, IRQF_TRIGGER_LOW|IRQF_ONESHOT,
    				pClient->name, pTAS2562);
    		if (nResult < 0) {
    			dev_err(pTAS2562->dev,
    				"request_irq failed, %d\n", nResult);
    			goto err;
    		}
    		disable_irq_nosync(pTAS2562->mnIRQ);
    	}
    
    #ifdef CONFIG_TAS2562_CODEC
    	mutex_init(&pTAS2562->codec_lock);
    	nResult = tas2562_register_codec(pTAS2562);
    	if (nResult < 0) {
    		dev_err(pTAS2562->dev,
    			"register codec failed, %d\n", nResult);
    		goto err;
    	}
    #endif
    
    #ifdef CONFIG_TAS2562_MISC
    	mutex_init(&pTAS2562->file_lock);
    	nResult = tas2562_register_misc(pTAS2562);
    	if (nResult < 0) {
    		dev_err(pTAS2562->dev,
    			"register codec failed, %d\n", nResult);
    		goto err;
    	}
    #endif
    
    	hrtimer_init(&pTAS2562->mtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    	pTAS2562->mtimer.function = timer_func;
    
    err:
    	return nResult;
    }
    
    static int tas2562_i2c_remove(struct i2c_client *pClient)
    {
    	struct tas2562_priv *pTAS2562 = i2c_get_clientdata(pClient);
    
    	dev_info(pTAS2562->dev, "%s\n", __func__);
    
    #ifdef CONFIG_TAS2562_CODEC
    	tas2562_deregister_codec(pTAS2562);
    	mutex_destroy(&pTAS2562->codec_lock);
    #endif
    
    #ifdef CONFIG_TAS2562_MISC
    	tas2562_deregister_misc(pTAS2562);
    	mutex_destroy(&pTAS2562->file_lock);
    #endif
    
    	if (gpio_is_valid(pTAS2562->mnResetGPIO))
    		gpio_free(pTAS2562->mnResetGPIO);
    	if (gpio_is_valid(pTAS2562->mnIRQGPIO))
    		gpio_free(pTAS2562->mnIRQGPIO);
    
    	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");
    #endif
    
    tegra_machine_driver_mobile.c
    /*
     * tegra_machine_driver_mobile.c - Tegra ASoC Machine driver for mobile
     *
     * Copyright (c) 2017-2021 NVIDIA CORPORATION.  All rights reserved.
     *
     * This program is free software; you can redistribute it and/or modify it
     * under the terms and conditions of the GNU General Public License,
     * version 2, as published by the Free Software Foundation.
     *
     * This program is distributed in the hope 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.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     */
    
    #include <linux/module.h>
    #include <linux/platform_device.h>
    #include <linux/of_platform.h>
    #include <linux/input.h>
    #include <linux/gpio.h>
    #include <linux/pm_runtime.h>
    #include <sound/core.h>
    #include <sound/jack.h>
    #include <sound/pcm.h>
    #include <sound/pcm_params.h>
    #include <sound/soc.h>
    #include <dt-bindings/sound/tas2552.h>
    #include "rt5659.h"
    #include "sgtl5000.h"
    #include "tas2562.h"
    #include "tegra_asoc_machine_alt.h"
    #include "tegra210_xbar_alt.h"
    
    #define DRV_NAME "tegra-asoc:"
    
    #define DEBUG 1
    
    #define PARAMS(sformat, channels)		\
    	{					\
    		.formats = sformat,		\
    		.rate_min = 48000,		\
    		.rate_max = 48000,		\
    		.channels_min = channels,	\
    		.channels_max = channels,	\
    	}
    
    /* t210 soc data */
    static const struct tegra_machine_soc_data soc_data_tegra210 = {
    	.admaif_dai_link_start		= TEGRA210_DAI_LINK_ADMAIF1,
    	.admaif_dai_link_end		= TEGRA210_DAI_LINK_ADMAIF10,
    #if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT)
    	.adsp_pcm_dai_link_start	= TEGRA210_DAI_LINK_ADSP_PCM1,
    	.adsp_pcm_dai_link_end		= TEGRA210_DAI_LINK_ADSP_PCM2,
    	.adsp_compr_dai_link_start	= TEGRA210_DAI_LINK_ADSP_COMPR1,
    	.adsp_compr_dai_link_end	= TEGRA210_DAI_LINK_ADSP_COMPR2,
    #endif
    	.sfc_dai_link			= TEGRA210_DAI_LINK_SFC1_RX,
    
    	.write_idle_bias_off_state	= false,
    
    	.ahub_links			= tegra210_xbar_dai_links,
    	.num_ahub_links			= TEGRA210_XBAR_DAI_LINKS,
    	.ahub_confs			= tegra210_xbar_codec_conf,
    	.num_ahub_confs			= TEGRA210_XBAR_CODEC_CONF,
    };
    
    /* t186 soc data */
    static const struct tegra_machine_soc_data soc_data_tegra186 = {
    	.admaif_dai_link_start		= TEGRA186_DAI_LINK_ADMAIF1,
    	.admaif_dai_link_end		= TEGRA186_DAI_LINK_ADMAIF10,
    #if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT)
    	.adsp_pcm_dai_link_start	= TEGRA186_DAI_LINK_ADSP_PCM1,
    	.adsp_pcm_dai_link_end		= TEGRA186_DAI_LINK_ADSP_PCM2,
    	.adsp_compr_dai_link_start	= TEGRA186_DAI_LINK_ADSP_COMPR1,
    	.adsp_compr_dai_link_end	= TEGRA186_DAI_LINK_ADSP_COMPR2,
    #endif
    	.sfc_dai_link			= TEGRA186_DAI_LINK_SFC1_RX,
    
    	.write_idle_bias_off_state	= true,
    
    	.ahub_links			= tegra186_xbar_dai_links,
    	.num_ahub_links			= TEGRA186_XBAR_DAI_LINKS,
    	.ahub_confs			= tegra186_xbar_codec_conf,
    	.num_ahub_confs			= TEGRA186_XBAR_CODEC_CONF,
    };
    
    static const char * const tegra_machine_srate_text[] = {
    	"None",
    	"8kHz",
    	"16kHz",
    	"44kHz",
    	"48kHz",
    	"11kHz",
    	"22kHz",
    	"24kHz",
    	"32kHz",
    	"88kHz",
    	"96kHz",
    	"176kHz",
    	"192kHz",
    };
    
    static const char * const tegra_machine_format_text[] = {
    	"None",
    	"16",
    	"32",
    };
    
    static const struct soc_enum tegra_machine_codec_rate =
    	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tegra_machine_srate_text),
    			    tegra_machine_srate_text);
    
    static const struct soc_enum tegra_machine_codec_format =
    	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tegra_machine_format_text),
    			    tegra_machine_format_text);
    
    static const int tegra_machine_srate_values[] = {
    	0,
    	8000,
    	16000,
    	44100,
    	48000,
    	11025,
    	22050,
    	24000,
    	32000,
    	88200,
    	96000,
    	176400,
    	192000,
    };
    
    static const struct snd_soc_dapm_widget tegra_machine_dapm_widgets[] = {
    	SND_SOC_DAPM_SPK("x Int Spk", NULL),
    	SND_SOC_DAPM_HP("x Headphone Jack", NULL),
    	SND_SOC_DAPM_MIC("x Int Mic", NULL),
    	SND_SOC_DAPM_MIC("x Mic Jack", NULL),
    
    	SND_SOC_DAPM_SPK("d1 Headphone", NULL),
    	SND_SOC_DAPM_SPK("d2 Headphone", NULL),
    	SND_SOC_DAPM_SPK("d3 Headphone", NULL),
    
    	SND_SOC_DAPM_HP("w Headphone", NULL),
    	SND_SOC_DAPM_HP("x Headphone", NULL),
    	SND_SOC_DAPM_HP("y Headphone", NULL),
    	SND_SOC_DAPM_HP("z Headphone", NULL),
    	SND_SOC_DAPM_HP("l Headphone", NULL),
    	SND_SOC_DAPM_HP("m Headphone", NULL),
    	SND_SOC_DAPM_HP("n Headphone", NULL),
    	SND_SOC_DAPM_HP("o Headphone", NULL),
    	SND_SOC_DAPM_HP("s Headphone", NULL),
    
    	SND_SOC_DAPM_MIC("Int Mic", NULL),
    	SND_SOC_DAPM_MIC("w Mic", NULL),
    	SND_SOC_DAPM_MIC("x Mic", NULL),
    	SND_SOC_DAPM_MIC("y Mic", NULL),
    	SND_SOC_DAPM_MIC("z Mic", NULL),
    	SND_SOC_DAPM_MIC("l Mic", NULL),
    	SND_SOC_DAPM_MIC("m Mic", NULL),
    	SND_SOC_DAPM_MIC("n Mic", NULL),
    	SND_SOC_DAPM_MIC("o Mic", NULL),
    	SND_SOC_DAPM_MIC("a Mic", NULL),
    	SND_SOC_DAPM_MIC("b Mic", NULL),
    	SND_SOC_DAPM_MIC("c Mic", NULL),
    	SND_SOC_DAPM_MIC("d Mic", NULL),
    	SND_SOC_DAPM_MIC("s Mic", NULL),
    
    	SND_SOC_DAPM_LINE("x Line Out", NULL),
    	SND_SOC_DAPM_LINE("y Line Out", NULL),
    	SND_SOC_DAPM_LINE("x Line In", NULL),
    	SND_SOC_DAPM_LINE("y Line In", NULL),
    };
    
    static struct snd_soc_pcm_stream tegra_machine_asrc_link_params[] = {
    	PARAMS(SNDRV_PCM_FMTBIT_S32_LE, 8),
    	PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2),
    	PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2),
    	PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2),
    	PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2),
    	PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2),
    };
    
    static int tegra_machine_codec_get_rate(struct snd_kcontrol *kcontrol,
    					struct snd_ctl_elem_value *ucontrol)
    {
    	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
    	struct tegra_machine *machine = snd_soc_card_get_drvdata(card);
    
    	ucontrol->value.integer.value[0] = machine->rate_via_kcontrol;
    
    	return 0;
    }
    
    static int tegra_machine_codec_put_rate(struct snd_kcontrol *kcontrol,
    					struct snd_ctl_elem_value *ucontrol)
    {
    	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
    	struct tegra_machine *machine = snd_soc_card_get_drvdata(card);
    
    	/* set the rate control flag */
    	machine->rate_via_kcontrol = ucontrol->value.integer.value[0];
    
    	return 0;
    }
    
    static int tegra_machine_codec_get_format(struct snd_kcontrol *kcontrol,
    	struct snd_ctl_elem_value *ucontrol)
    {
    	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
    	struct tegra_machine *machine = snd_soc_card_get_drvdata(card);
    
    	ucontrol->value.integer.value[0] = machine->fmt_via_kcontrol;
    
    	return 0;
    }
    
    static int tegra_machine_codec_put_format(struct snd_kcontrol *kcontrol,
    	struct snd_ctl_elem_value *ucontrol)
    {
    	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
    	struct tegra_machine *machine = snd_soc_card_get_drvdata(card);
    
    	/* set the format control flag */
    	machine->fmt_via_kcontrol = ucontrol->value.integer.value[0];
    
    	return 0;
    }
    
    static int tegra_machine_set_params(struct snd_soc_card *card,
    				    struct tegra_machine *machine,
    				    unsigned int rate,
    				    unsigned int channels,
    				    u64 formats)
    {
    	unsigned int mask = (1 << channels) - 1;
    	struct snd_soc_pcm_runtime *rtd;
    	int idx = 0, err = 0;
    	u64 format_k;
    
    	int num_of_dai_links = machine->soc_data->num_ahub_links +
    			       machine->num_codec_links;
    
    	format_k = (machine->fmt_via_kcontrol == 2) ?
    			(1ULL << SNDRV_PCM_FORMAT_S32_LE) : formats;
    
    	/* update dai link hw_params */
    	list_for_each_entry(rtd, &card->rtd_list, list) {
    		if (rtd->dai_link->params) {
    			struct snd_soc_pcm_stream *dai_params;
    
    			dai_params =
    			  (struct snd_soc_pcm_stream *)
    			  rtd->dai_link->params;
    
    			dai_params->rate_min = rate;
    			dai_params->channels_min = channels;
    			dai_params->formats = format_k;
    
    			if ((idx >= machine->soc_data->num_ahub_links)
    				&& (idx < num_of_dai_links)) {
    				unsigned int fmt;
    
    				/* TODO: why below overrite is needed */
    				dai_params->formats = formats;
    
    				fmt = rtd->dai_link->dai_fmt;
    				fmt &= SND_SOC_DAIFMT_FORMAT_MASK;
    
    				/* set TDM slot mask */
    				if (fmt == SND_SOC_DAIFMT_DSP_A ||
    				    fmt == SND_SOC_DAIFMT_DSP_B) {
    					err = snd_soc_dai_set_tdm_slot(
    							rtd->cpu_dai, mask,
    							mask, 0, 0);
    					if (err < 0) {
    						dev_err(card->dev,
    						"%s cpu DAI slot mask not set\n",
    						rtd->cpu_dai->name);
    						return err;
    					}
    				}
    			}
    		}
    		idx++;
    	}
    	return 0;
    }
    
    static int tegra_machine_dai_init(struct snd_soc_pcm_runtime *runtime,
    				  unsigned int rate, unsigned int channels,
    				  u64 formats)
    {
    	struct snd_soc_card *card = runtime->card;
    	struct tegra_machine *machine = snd_soc_card_get_drvdata(card);
    	struct snd_soc_pcm_stream *dai_params;
    	unsigned int aud_mclk, srate;
    	int err, sample_size;
    	struct snd_soc_pcm_runtime *rtd;
    
    	srate = (machine->rate_via_kcontrol) ?
    			tegra_machine_srate_values[machine->rate_via_kcontrol] :
    			rate;
    
    	switch (formats) {
    	case SNDRV_PCM_FORMAT_S8:
    		sample_size = 8;
    		break;
    	case SNDRV_PCM_FORMAT_S16_LE:
    		sample_size = 16;
    		break;
    	case SNDRV_PCM_FORMAT_S24_LE:
    	/*
    	 * I2S bit clock is derived from PLLA_OUT0 and size of
    	 * 24 bits results in fractional value and the clock
    	 * is not accurate with this. To have integer clock
    	 * division below is used. It means there are additional
    	 * bit clocks (8 cycles) which are ignored. Codec picks
    	 * up data for other channel when LRCK signal toggles.
    	 */
    	case SNDRV_PCM_FORMAT_S32_LE:
    		sample_size = 32;
    		break;
    	default:
    		pr_err("Wrong format!\n");
    		return -EINVAL;
    	}
    	formats = 1ULL << formats;
    
    	err = tegra_alt_asoc_utils_set_rate(&machine->audio_clock, srate,
    						channels, sample_size, 0, 0);
    	if (err < 0) {
    		dev_err(card->dev, "Can't configure clocks\n");
    		return err;
    	}
    
    	aud_mclk = machine->audio_clock.set_aud_mclk_rate;
    
    	pr_debug("pll_a_out0 = %u Hz, aud_mclk = %u Hz, sample rate = %u Hz\n",
    		 machine->audio_clock.set_pll_out_rate, aud_mclk, srate);
    
    	err = tegra_machine_set_params(card, machine, srate, channels, formats);
    	if (err < 0)
    		return err;
    
    	rtd = snd_soc_get_pcm_runtime(card, "tas2562-playback");
        if (rtd) {
            dai_params = (struct snd_soc_pcm_stream *)rtd->dai_link->params;
    
    		dai_params->formats = (machine->fmt_via_kcontrol == 2) ? (1ULL << SNDRV_PCM_FORMAT_S32_LE) : formats;
    /*		err = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 12288000, SND_SOC_CLOCK_IN);
    		if (err < 0) {
    			dev_err(card->dev, "codec_dai clock not set\n");
    			return err;
    		}
    */
        }
    	return 0;
    }
    
    static int tegra_machine_pcm_hw_params(struct snd_pcm_substream *substream,
    					struct snd_pcm_hw_params *params)
    {
    	struct snd_soc_pcm_runtime *rtd = substream->private_data;
    	struct snd_soc_card *card = rtd->card;
    	int err;
    
    	err = tegra_machine_dai_init(rtd, params_rate(params),
    				     params_channels(params),
    				     params_format(params));
    	if (err < 0) {
    		dev_err(card->dev, "Failed dai init\n");
    		return err;
    	}
    
    	return 0;
    }
    
    static int tegra_machine_pcm_startup(struct snd_pcm_substream *substream)
    {
    	struct snd_soc_pcm_runtime *rtd = substream->private_data;
    	struct tegra_machine *machine = snd_soc_card_get_drvdata(rtd->card);
    
    	tegra_alt_asoc_utils_clk_enable(&machine->audio_clock);
    
    	return 0;
    }
    
    static void tegra_machine_pcm_shutdown(struct snd_pcm_substream *substream)
    {
    	struct snd_soc_pcm_runtime *rtd = substream->private_data;
    	struct tegra_machine *machine = snd_soc_card_get_drvdata(rtd->card);
    
    	tegra_alt_asoc_utils_clk_disable(&machine->audio_clock);
    }
    
    static int tegra_machine_suspend_pre(struct snd_soc_card *card)
    {
    	struct snd_soc_pcm_runtime *rtd;
    
    	/* DAPM dai link stream work for non pcm links */
    	list_for_each_entry(rtd, &card->rtd_list, list) {
    		if (rtd->dai_link->params)
    			INIT_DELAYED_WORK(&rtd->delayed_work, NULL);
    	}
    
    	return 0;
    }
    
    #if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT)
    static int tegra_machine_compr_startup(struct snd_compr_stream *cstream)
    {
    	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
    	struct snd_soc_card *card = rtd->card;
    	struct tegra_machine *machine = snd_soc_card_get_drvdata(card);
    
    	tegra_alt_asoc_utils_clk_enable(&machine->audio_clock);
    
    	return 0;
    }
    
    static void tegra_machine_compr_shutdown(struct snd_compr_stream *cstream)
    {
    	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
    	struct snd_soc_card *card = rtd->card;
    	struct tegra_machine *machine = snd_soc_card_get_drvdata(card);
    
    	tegra_alt_asoc_utils_clk_disable(&machine->audio_clock);
    }
    
    static int tegra_machine_compr_set_params(struct snd_compr_stream *cstream)
    {
    	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
    	struct snd_soc_card *card = rtd->card;
    	struct snd_soc_platform *platform = rtd->platform;
    	struct snd_codec codec_params;
    	int err;
    
    	if (platform->driver->compr_ops &&
    		platform->driver->compr_ops->get_params) {
    		err = platform->driver->compr_ops->get_params(cstream,
    			&codec_params);
    		if (err < 0) {
    			dev_err(card->dev, "Failed to get compr params\n");
    			return err;
    		}
    	} else {
    		dev_err(card->dev, "compr ops not set\n");
    		return -EINVAL;
    	}
    
    	err = tegra_machine_dai_init(rtd, codec_params.sample_rate,
    				     codec_params.ch_out,
    				     SNDRV_PCM_FORMAT_S16_LE);
    	if (err < 0) {
    		dev_err(card->dev, "Failed dai init\n");
    		return err;
    	}
    
    	return 0;
    }
    #endif
    
    static int tegra_machine_respeaker_init(struct snd_soc_pcm_runtime *rtd)
    {
    	struct device *dev = rtd->card->dev;
    	int err;
    
    	/* ac108 codec driver hardcodes the freq as 24000000
    	 * and source as PLL irrespective of args passed through
    	 * this callback
    	 */
    	err = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 24000000,
    				     SND_SOC_CLOCK_IN);
    	if (err) {
    		dev_err(dev, "failed to set ac108 sysclk!\n");
    		return err;
    	}
    
    	return 0;
    }
    
    static int tegra_machine_fepi_init(struct snd_soc_pcm_runtime *rtd)
    {
    	struct device *dev = rtd->card->dev;
    	int err;
    
    	err = snd_soc_dai_set_sysclk(rtd->codec_dai, SGTL5000_SYSCLK, 12288000,
    				     SND_SOC_CLOCK_IN);
    	if (err) {
    		dev_err(dev, "failed to set sgtl5000 sysclk!\n");
    		return err;
    	}
    
    	return 0;
    }
    
    static int tegra_machine_tas2562_init(struct snd_soc_pcm_runtime *rtd) {
    /*       struct device *dev = rtd->card->dev;
          int err;
    
     	err = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 12288000,
                                        SND_SOC_CLOCK_IN);
           if (err) {
                   dev_err(dev, "failed to set tas2562 sysclk!!!\n");
                   return err;
           }
    */
           return 0;
    }
    
    
    static int tegra_machine_rt565x_init(struct snd_soc_pcm_runtime *rtd)
    {
    	struct snd_soc_card *card = rtd->card;
    	struct snd_soc_jack *jack;
    	int err;
    
    	jack = devm_kzalloc(card->dev, sizeof(struct snd_soc_jack), GFP_KERNEL);
    	if (!jack)
    		return -ENOMEM;
    
    	err = snd_soc_card_jack_new(card, "Headset Jack", SND_JACK_HEADSET,
    				    jack, NULL, 0);
    	if (err) {
    		dev_err(card->dev, "Headset Jack creation failed %d\n", err);
    		return err;
    	}
    
    	err = tegra_machine_add_codec_jack_control(card, rtd, jack);
    	if (err) {
    		dev_err(card->dev, "Failed to add jack control: %d\n", err);
    		return err;
    	}
    
    	err = rt5659_set_jack_detect(rtd->codec, jack);
    	if (err) {
    		dev_err(card->dev, "Failed to set jack for RT565x: %d\n", err);
    		return err;
    	}
    
    	/* single button supporting play/pause */
    	snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA);
    
    	/* multiple buttons supporting play/pause and volume up/down */
    	snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_MEDIA);
    	snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
    	snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
    
    	snd_soc_dapm_sync(&card->dapm);
    
    	return 0;
    }
    
    static int codec_init(struct tegra_machine *machine)
    {
    	struct snd_soc_dai_link *dai_links = machine->asoc->dai_links;
    	unsigned int num_links = machine->asoc->num_links, i;
    
    	if (!dai_links || !num_links)
    		return -EINVAL;
    
    	for (i = 0; i < num_links; i++) {
    		if (!dai_links[i].name)
    			continue;
    
    		if (strstr(dai_links[i].name, "rt565x-playback") ||
    		    strstr(dai_links[i].name, "rt565x-codec-sysclk-bclk1"))
    			dai_links[i].init = tegra_machine_rt565x_init;
    		else if (strstr(dai_links[i].name, "fe-pi-audio-z-v2"))
    			dai_links[i].init = tegra_machine_fepi_init;
    		else if (strstr(dai_links[i].name, "respeaker-4-mic-array"))
    			dai_links[i].init = tegra_machine_respeaker_init;
    		else if (strstr(dai_links[i].name, "tas2562-playback"))
    			dai_links[i].init = tegra_machine_tas2562_init;
    	}
    
    	return 0;
    }
    
    static struct snd_soc_ops tegra_machine_pcm_ops = {
    	.hw_params	= tegra_machine_pcm_hw_params,
    	.startup	= tegra_machine_pcm_startup,
    	.shutdown	= tegra_machine_pcm_shutdown,
    };
    
    #if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT)
    static struct snd_soc_compr_ops tegra_machine_compr_ops = {
    	.set_params	= tegra_machine_compr_set_params,
    	.startup	= tegra_machine_compr_startup,
    	.shutdown	= tegra_machine_compr_shutdown,
    };
    #endif
    
    static void set_dai_ops(struct tegra_machine *machine)
    {
    	int i;
    
    	/* set ADMAIF dai_ops */
    	for (i = machine->soc_data->admaif_dai_link_start;
    	     i <= machine->soc_data->admaif_dai_link_end; i++)
    		machine->asoc->dai_links[i].ops = &tegra_machine_pcm_ops;
    #if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT)
    	/* set ADSP PCM/COMPR */
    	for (i = machine->soc_data->adsp_pcm_dai_link_start;
    	     i <= machine->soc_data->adsp_pcm_dai_link_end; i++)
    		machine->asoc->dai_links[i].ops = &tegra_machine_pcm_ops;
    	/* set ADSP COMPR */
    	for (i = machine->soc_data->adsp_compr_dai_link_start;
    	     i <= machine->soc_data->adsp_compr_dai_link_end; i++)
    		machine->asoc->dai_links[i].compr_ops =
    			&tegra_machine_compr_ops;
    #endif
    #if IS_ENABLED(CONFIG_SND_SOC_TEGRA186_ASRC_ALT)
    	if (!(of_machine_is_compatible("nvidia,tegra210")  ||
    		of_machine_is_compatible("nvidia,tegra210b01"))) {
    		/* set ASRC params. The default is 2 channels */
    		for (i = 0; i < 6; i++) {
    			int tx = TEGRA186_DAI_LINK_ASRC1_TX1 + i;
    			int rx = TEGRA186_DAI_LINK_ASRC1_RX1 + i;
    
    			machine->asoc->dai_links[tx].params =
    				&tegra_machine_asrc_link_params[i];
    			machine->asoc->dai_links[rx].params =
    				&tegra_machine_asrc_link_params[i];
    		}
    	}
    #endif
    }
    
    static int add_dai_links(struct platform_device *pdev)
    {
    	struct snd_soc_card *card = platform_get_drvdata(pdev);
    	struct tegra_machine *machine = snd_soc_card_get_drvdata(card);
    	int ret;
    
    	machine->asoc = devm_kzalloc(&pdev->dev, sizeof(*machine->asoc),
    				     GFP_KERNEL);
    	if (!machine->asoc)
    		return -ENOMEM;
    
    	ret = tegra_asoc_populate_dai_links(pdev);
    	if (ret < 0)
    		return ret;
    
    	ret = tegra_asoc_populate_codec_confs(pdev);
    	if (ret < 0)
    		return ret;
    
    	ret = codec_init(machine);
    	if (ret < 0)
    		return ret;
    
    	set_dai_ops(machine);
    
    	return 0;
    }
    
    static const struct snd_kcontrol_new tegra_machine_controls[] = {
    	SOC_ENUM_EXT("codec-x rate", tegra_machine_codec_rate,
    		tegra_machine_codec_get_rate, tegra_machine_codec_put_rate),
    	SOC_ENUM_EXT("codec-x format", tegra_machine_codec_format,
    		tegra_machine_codec_get_format, tegra_machine_codec_put_format),
    };
    
    static struct snd_soc_card snd_soc_tegra_card = {
    	.owner = THIS_MODULE,
    	.controls = tegra_machine_controls,
    	.num_controls = ARRAY_SIZE(tegra_machine_controls),
    	.dapm_widgets = tegra_machine_dapm_widgets,
    	.num_dapm_widgets = ARRAY_SIZE(tegra_machine_dapm_widgets),
    	.suspend_pre = tegra_machine_suspend_pre,
    	.fully_routed = true,
    };
    
    /* structure to match device tree node */
    static const struct of_device_id tegra_machine_of_match[] = {
    	{ .compatible = "nvidia,tegra-audio-t186ref-mobile-rt565x",
    		.data = &soc_data_tegra186 },
    	{ .compatible = "nvidia,tegra-audio-t210ref-mobile-rt565x",
    		.data = &soc_data_tegra210 },
    	{ .compatible = "nvidia,tegra-audio-mystique",
    		.data = &soc_data_tegra186 },
    	{},
    };
    
    static int tegra_machine_driver_probe(struct platform_device *pdev)
    {
    	struct device_node *np = pdev->dev.of_node;
    	struct snd_soc_card *card = &snd_soc_tegra_card;
    	struct tegra_machine *machine;
    	int ret = 0;
    	const struct of_device_id *match;
    
    	card->dev = &pdev->dev;
    	/* parse card name first to log errors with proper device name */
    	ret = snd_soc_of_parse_card_name(card, "nvidia,model");
    	if (ret)
    		return ret;
    
    	match = of_match_device(tegra_machine_of_match, &pdev->dev);
    	if (!match) {
    		dev_err(&pdev->dev, "Error: No device match found\n");
    		return -ENODEV;
    	}
    
    	if (!np) {
    		dev_err(&pdev->dev, "No DT node for tegra machine driver");
    		return -ENODEV;
    	}
    
    	machine = devm_kzalloc(&pdev->dev, sizeof(*machine), GFP_KERNEL);
    	if (!machine)
    		return -ENOMEM;
    
    	machine->soc_data = (struct tegra_machine_soc_data *)match->data;
    	if (!machine->soc_data)
    		return -EINVAL;
    
    	platform_set_drvdata(pdev, card);
    	snd_soc_card_set_drvdata(card, machine);
    
    	if (machine->soc_data->write_cdev1_state)
    		machine->audio_clock.clk_cdev1_state = 0;
    
    	if (machine->soc_data->write_idle_bias_off_state)
    		card->dapm.idle_bias_off = true;
    
    	ret = snd_soc_of_parse_audio_routing(card,
    				"nvidia,audio-routing");
    	if (ret)
    		return ret;
    
    	memset(&machine->audio_clock, 0, sizeof(machine->audio_clock));
    	if (of_property_read_u32(np, "mclk-fs",
    				 &machine->audio_clock.mclk_scale) < 0)
    		dev_dbg(&pdev->dev, "Missing property mclk-fs\n");
    
    	if (of_property_read_bool(np, "fixed-pll")) {
    		machine->audio_clock.fixed_pll = true;
    		dev_info(&pdev->dev, "PLL configuration is fixed from DT\n");
    	}
    
    	tegra_machine_dma_set_mask(pdev);
    
    	ret = add_dai_links(pdev);
    	if (ret < 0)
    		goto cleanup_asoc;
    
    	ret = tegra_alt_asoc_utils_init(&machine->audio_clock,
    					&pdev->dev,
    					card);
    	if (ret < 0)
    		goto cleanup_asoc;
    
    	ret = snd_soc_register_card(card);
    	if (ret) {
    		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
    			ret);
    		goto cleanup_asoc;
    	}
    
    	tegra_machine_add_i2s_codec_controls(card,
    					machine->soc_data->num_ahub_links +
    					machine->num_codec_links);
    
    	return 0;
    cleanup_asoc:
    	release_asoc_phandles(machine);
    	return ret;
    }
    
    static int tegra_machine_driver_remove(struct platform_device *pdev)
    {
    	struct snd_soc_card *card = platform_get_drvdata(pdev);
    
    	snd_soc_unregister_card(card);
    
    	return 0;
    }
    
    #if CONFIG_PM
    static void tegra_asoc_machine_resume(struct device *dev)
    {
    	WARN_ON(snd_soc_resume(dev));
    }
    #else
    #define tegra_asoc_machine_resume NULL
    #endif
    
    static const struct dev_pm_ops tegra_asoc_machine_pm_ops = {
    	.prepare = snd_soc_suspend,
    	.complete = tegra_asoc_machine_resume,
    	.poweroff = snd_soc_poweroff,
    };
    
    static struct platform_driver tegra_asoc_machine_driver = {
    	.driver = {
    		.name = DRV_NAME,
    		.owner = THIS_MODULE,
    		.pm = &tegra_asoc_machine_pm_ops,
    		.of_match_table = tegra_machine_of_match,
    	},
    	.probe = tegra_machine_driver_probe,
    	.remove = tegra_machine_driver_remove,
    };
    module_platform_driver(tegra_asoc_machine_driver);
    
    MODULE_AUTHOR("Mohan Kumar <mkumard@nvidia.com>, Sameer Pujar <spujar@nvidia.com>");
    MODULE_DESCRIPTION("Tegra ASoC machine driver");
    MODULE_LICENSE("GPL");
    MODULE_ALIAS("platform:" DRV_NAME);
    MODULE_DEVICE_TABLE(of, tegra_machine_of_match);
    

    Could you check again what is incorrect set?

  • Hi Mateus,

    During playback, I2S signal must work. It is correct the I2C is quiet. I think there is wrong setting from the platform side, as you know, tas2110 is a slave device, it depends on the correct clk from master side. Kindly consult the platform vendor.

    BR

    Shenghao Ding