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.

TLV320AIC3100: tlv320aic3100 24mhz driver support

Part Number: TLV320AIC3100


I have used AIC3100 with nxp imx8mm,i have integrated codec driver of AIC3100 with linux kernel. Now codec register successfully, but when i try to play wav file using tinyalsa it gives error as "Error Playing File". 

Device tree configuration:

sound-tlv320aic31xx {
+ compatible = "simple-audio-card";
+ simple-audio-card,name = "tlv320aic31xx-codec";
+ simple-audio-card,format = "i2s";
+ simple-audio-card,frame-master = <&cpudai2>;
+ simple-audio-card,bitclock-master = <&cpudai2>;
+ /*simple-audio-card,mclk-fs=<256>;*/
+ simple-audio-card,widgets =
+ "Headphone", "Headphone Jack";
+ simple-audio-card,routing =
+ "Headphone Jack", "HPL",
+ "Headphone Jack", "HPR";
+
+ cpudai2:simple-audio-card,cpu {
+ sound-dai = <&sai2>;
+ };
+
+ simple-audio-card,codec {
+ sound-dai = <&tlv320aic31xx>;
+ clocks = <&codec_osc>;
+ };
+ };
+
+ clocks {
+ codec_osc: aud_mclk {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ clock-output-names = "codec-mclk";
+ };
+ };

SAI2 clock:

@@ -118,8 +194,9 @@ &sai2 {
pinctrl-0 = <&pinctrl_sai2>;
assigned-clocks = <&clk IMX8MM_CLK_SAI2>;
assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>;

 assigned-clock-rates = <24576000>;
- status = "disabled";
+ status = "okay";
};

Codec was able to detect in I2C node:

tlv320aic31xx:codec@18 {
+ compatible = "ti,tlv320aic3100";
+ reg = <0x18>;
+ #sound-dai-cells = <0>;
+ /* Regulators */
+ AVDD-supply = <&reg_3p3v>;
+ HPVDD-supply = <&reg_3p3v>;
+ SPRVDD-supply = <&reg_5v>;
+ SPLVDD-supply = <&reg_5v>;
+ DVDD-supply = <&reg_1p8v>;
+ IOVDD-supply = <&reg_1p8v>;
+
+ ai31xx-micbias-vg = <1>;
+ };

Issue:

root@picoimx8mm:/home/ubuntu# dmesg | grep tlv
[ 40.059531] tlv320aic31xx-codec 1-0018: aic31xx_set_dai_sysclk: Unsupported frequency 24576000
[ 40.088026] tlv320aic31xx-codec 1-0018: ASoC: error at snd_soc_dai_set_sysclk on tlv320aic31xx-hifi: -22
[ 40.121946] tlv320aic31xx-codec 1-0018: simple-card: set_sysclk error
[ 40.138078] 30020000.sai-tlv320aic31xx-hifi: ASoC: error at snd_soc_link_init on 30020000.sai-tlv320aic31xx-hifi: -22
[ 40.168804] asoc-simple-card: probe of sound-tlv320aic31xx failed with error -22
root@picoimx8mm:/home/ubuntu#

Externally feeding the 24MHZ oscillator to the codec MCLK, but the codec driver was not supporting.

sound/soc/codecs/tlv320aic31xx.c (No 24Mz support)

/* ADC dividers can be disabled by configuring them to 0 */
static const struct aic31xx_rate_divs aic31xx_divs[] = {
/* mclk/p rate pll: j d dosr ndac mdac aors nadc madc */
/* 8k rate */
{12000000, 8000, 8, 1920, 128, 48, 2, 128, 48, 2},
{12000000, 8000, 8, 1920, 128, 32, 3, 128, 32, 3},
{12500000, 8000, 7, 8643, 128, 48, 2, 128, 48, 2},
/* 11.025k rate */
{12000000, 11025, 7, 5264, 128, 32, 2, 128, 32, 2},
{12000000, 11025, 8, 4672, 128, 24, 3, 128, 24, 3},
{12500000, 11025, 7, 2253, 128, 32, 2, 128, 32, 2},
/* 16k rate */
{12000000, 16000, 8, 1920, 128, 24, 2, 128, 24, 2},
{12000000, 16000, 8, 1920, 128, 16, 3, 128, 16, 3},
{12500000, 16000, 7, 8643, 128, 24, 2, 128, 24, 2},
/* 22.05k rate */
{12000000, 22050, 7, 5264, 128, 16, 2, 128, 16, 2},
{12000000, 22050, 8, 4672, 128, 12, 3, 128, 12, 3},
{12500000, 22050, 7, 2253, 128, 16, 2, 128, 16, 2},
/* 32k rate */
{12000000, 32000, 8, 1920, 128, 12, 2, 128, 12, 2},
{12000000, 32000, 8, 1920, 128, 8, 3, 128, 8, 3},
{12500000, 32000, 7, 8643, 128, 12, 2, 128, 12, 2},
/* 44.1k rate */
{12000000, 44100, 7, 5264, 128, 8, 2, 128, 8, 2},
{12000000, 44100, 8, 4672, 128, 6, 3, 128, 6, 3},
{12500000, 44100, 7, 2253, 128, 8, 2, 128, 8, 2},
/* 48k rate */
{12000000, 48000, 8, 1920, 128, 8, 2, 128, 8, 2},
{12000000, 48000, 7, 6800, 96, 5, 4, 96, 5, 4},
{12500000, 48000, 7, 8643, 128, 8, 2, 128, 8, 2},
/* 88.2k rate */
{12000000, 88200, 7, 5264, 64, 8, 2, 64,

TI driver tlv320aic3100 codec driver doesn't have 24MHZ support Please support us to resolve the issue?

Regards,

SanthanaKumarS

  • Let me ask our codec expert for your request.

  • Thanks, Please support us to resolve the issue?

  • You can find your clock configuration from the GUI a shown below:

    • Click the Digital Audio Processing block

    • Click the internal clock

    • Enter your MCLK and Sampling frequency as shown, the results are shown for the PLL clock parameters

    • Configure your registers according to the above settings you choose

    Regards.

  • Hi,

    Generate the 24mhz through the software and added those patches in the driver 

    static const struct aic31xx_rate_divs aic31xx_divs[] = {
    + /* mclk/p rate pll: j d dosr ndac mdac aors nadc madc */
    + /* 8k rate */
    + {24000000, 44100, 7, 560, 128, 3, 5, 128, 3, 5},
    + {24000000, 44100, 7, 5264, 128, 4, 4, 128, 16, 4},
    };

    Console log:

    [ 40.034074] aic31xx_codec_probe
    [ 40.036874] ## aic31xx_set_dai_sysclk: clk_id = 0, freq = 24000000, dir = 0

    root@picoimx8mm:/home/ubuntu# aplay 48000_16_st.wav
    [ 304.640543] fsl-sai 30020000.sai: failed to derive required Tx rate: 1411200
    [ 304.648159] fsl-sai 30020000.sai: ASoC: error at snd_soc_dai_hw_params on 30020000.sai: -22
    [ 304.657061] 30020000.sai-tlv320aic31xx-hifi: ASoC: soc_pcm_hw_params() failed (-22)
    ALSA lib pcm_direct.c:1284:(snd1_pcm_direct_initialize_slave) unable to install hw params
    ALSA lib pcm_dmix.c:1044:(snd_pcm_dmix_open) unable to initialize slave
    aplay: main:831: audio open error: Invalid argument
    root@picoimx8mm:/home/ubuntu#

    Please help us to resolve the issue or if any patches are given to us it will appreciated.

  • Hi Kumar

    Would you be so kind set the platform MCLK to 12MHz, the driver seemed to support 12MHz?

  • Hi,

    Ok the driver support will be 12Mhz, but the codec MCLK is given by external clock 24Mhz, Can you please tell us what step we can proceed?

    Regards,
    SanthanaKumarS

  • Hi Kumar

    Would you be so kind check whether the sample rate of 48000_16_st.wav does not match the 24MHz setting?

    It seemed that the wav file is 48kHz, and the 24MHz setting only support 44.1kHz. 

  • Yes we are playing the 48khz file on the board but the bit clock and frame clock was not getting the proper one.

    Probe the codec clock

    1. MCLK = 24mhz
    2. Bit clock : 78khz
    3. Frame clock: 25khz

    Can you please support on the codec bringup?

  • Since the file is 48kHz, you should add 48000Hzm instead of 44100?

    + {24000000, 44100, 7, 560, 128, 3, 5, 128, 3, 5},
    + {24000000, 44100, 7, 5264, 128, 4, 4, 128, 16, 4},

  • Hi,

    Can you please check us the old patch, added those change in the driver file, but still the clock was not generated.

    Regards,
    SanthanaKumarS

  • Kindly offer the data based on 48000 Hz to me

  • Hi,

    Attached the picture to you.

    Measurement:

    1. MCLK = 24mhz
    2. Bit clock : 78khz
    3. Frame clock: 25khz

    Attaced the driver to you

    // SPDX-License-Identifier: GPL-2.0
    /*
     * ALSA SoC TLV320AIC31xx CODEC Driver
     *
     * Copyright (C) 2014-2017 Texas Instruments Incorporated - https://www.ti.com/
     *	Jyri Sarha <jsarha@ti.com>
     *
     * Based on ground work by: Ajit Kulkarni <x0175765@ti.com>
     *
     * The TLV320AIC31xx series of audio codecs are low-power, highly integrated
     * high performance codecs which provides a stereo DAC, a mono ADC,
     * and mono/stereo Class-D speaker driver.
     */
    
    #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/consumer.h>
    #include <linux/regulator/consumer.h>
    #include <linux/acpi.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    #include <linux/slab.h>
    #include <sound/core.h>
    #include <sound/jack.h>
    #include <sound/pcm.h>
    #include <sound/pcm_params.h>
    #include <sound/soc.h>
    #include <sound/initval.h>
    #include <sound/tlv.h>
    #include <dt-bindings/sound/tlv320aic31xx-micbias.h>
    
    #include "tlv320aic31xx.h"
    
    static int aic31xx_set_jack(struct snd_soc_component *component,
                                struct snd_soc_jack *jack, void *data);
    
    static const struct reg_default aic31xx_reg_defaults[] = {
    	{ AIC31XX_CLKMUX, 0x00 },
    	{ AIC31XX_PLLPR, 0x11 },
    	{ AIC31XX_PLLJ, 0x04 },
    	{ AIC31XX_PLLDMSB, 0x00 },
    	{ AIC31XX_PLLDLSB, 0x00 },
    	{ AIC31XX_NDAC, 0x01 },
    	{ AIC31XX_MDAC, 0x01 },
    	{ AIC31XX_DOSRMSB, 0x00 },
    	{ AIC31XX_DOSRLSB, 0x80 },
    	{ AIC31XX_NADC, 0x01 },
    	{ AIC31XX_MADC, 0x01 },
    	{ AIC31XX_AOSR, 0x80 },
    	{ AIC31XX_IFACE1, 0x00 },
    	{ AIC31XX_DATA_OFFSET, 0x00 },
    	{ AIC31XX_IFACE2, 0x00 },
    	{ AIC31XX_BCLKN, 0x01 },
    	{ AIC31XX_DACSETUP, 0x14 },
    	{ AIC31XX_DACMUTE, 0x0c },
    	{ AIC31XX_LDACVOL, 0x00 },
    	{ AIC31XX_RDACVOL, 0x00 },
    	{ AIC31XX_ADCSETUP, 0x00 },
    	{ AIC31XX_ADCFGA, 0x80 },
    	{ AIC31XX_ADCVOL, 0x00 },
    	{ AIC31XX_HPDRIVER, 0x04 },
    	{ AIC31XX_SPKAMP, 0x06 },
    	{ AIC31XX_DACMIXERROUTE, 0x00 },
    	{ AIC31XX_LANALOGHPL, 0x7f },
    	{ AIC31XX_RANALOGHPR, 0x7f },
    	{ AIC31XX_LANALOGSPL, 0x7f },
    	{ AIC31XX_RANALOGSPR, 0x7f },
    	{ AIC31XX_HPLGAIN, 0x02 },
    	{ AIC31XX_HPRGAIN, 0x02 },
    	{ AIC31XX_SPLGAIN, 0x00 },
    	{ AIC31XX_SPRGAIN, 0x00 },
    	{ AIC31XX_MICBIAS, 0x00 },
    	{ AIC31XX_MICPGA, 0x80 },
    	{ AIC31XX_MICPGAPI, 0x00 },
    	{ AIC31XX_MICPGAMI, 0x00 },
    };
    
    static bool aic31xx_volatile(struct device *dev, unsigned int reg)
    {
    	switch (reg) {
    	case AIC31XX_PAGECTL: /* regmap implementation requires this */
    	case AIC31XX_RESET: /* always clears after write */
    	case AIC31XX_OT_FLAG:
    	case AIC31XX_ADCFLAG:
    	case AIC31XX_DACFLAG1:
    	case AIC31XX_DACFLAG2:
    	case AIC31XX_OFFLAG: /* Sticky interrupt flags */
    	case AIC31XX_INTRDACFLAG: /* Sticky interrupt flags */
    	case AIC31XX_INTRADCFLAG: /* Sticky interrupt flags */
    	case AIC31XX_INTRDACFLAG2:
    	case AIC31XX_INTRADCFLAG2:
    	case AIC31XX_HSDETECT:
    		return true;
    	}
    	return false;
    }
    
    static bool aic31xx_writeable(struct device *dev, unsigned int reg)
    {
    	switch (reg) {
    	case AIC31XX_OT_FLAG:
    	case AIC31XX_ADCFLAG:
    	case AIC31XX_DACFLAG1:
    	case AIC31XX_DACFLAG2:
    	case AIC31XX_OFFLAG: /* Sticky interrupt flags */
    	case AIC31XX_INTRDACFLAG: /* Sticky interrupt flags */
    	case AIC31XX_INTRADCFLAG: /* Sticky interrupt flags */
    	case AIC31XX_INTRDACFLAG2:
    	case AIC31XX_INTRADCFLAG2:
    		return false;
    	}
    	return true;
    }
    
    static const struct regmap_range_cfg aic31xx_ranges[] = {
    	{
    		.range_min = 0,
    		.range_max = 12 * 128,
    		.selector_reg = AIC31XX_PAGECTL,
    		.selector_mask = 0xff,
    		.selector_shift = 0,
    		.window_start = 0,
    		.window_len = 128,
    	},
    };
    
    static const struct regmap_config aic31xx_i2c_regmap = {
    	.reg_bits = 8,
    	.val_bits = 8,
    	.writeable_reg = aic31xx_writeable,
    	.volatile_reg = aic31xx_volatile,
    	.reg_defaults = aic31xx_reg_defaults,
    	.num_reg_defaults = ARRAY_SIZE(aic31xx_reg_defaults),
    	.cache_type = REGCACHE_RBTREE,
    	.ranges = aic31xx_ranges,
    	.num_ranges = ARRAY_SIZE(aic31xx_ranges),
    	.max_register = 12 * 128,
    };
    
    static const char * const aic31xx_supply_names[] = {
    	"HPVDD",
    	"SPRVDD",
    	"SPLVDD",
    	"AVDD",
    	"IOVDD",
    	"DVDD",
    };
    
    #define AIC31XX_NUM_SUPPLIES ARRAY_SIZE(aic31xx_supply_names)
    
    struct aic31xx_disable_nb {
    	struct notifier_block nb;
    	struct aic31xx_priv *aic31xx;
    };
    
    struct aic31xx_priv {
    	struct snd_soc_component *component;
    	u8 i2c_regs_status;
    	struct device *dev;
    	struct regmap *regmap;
    	enum aic31xx_type codec_type;
    	struct gpio_desc *gpio_reset;
    	int micbias_vg;
    	struct aic31xx_pdata pdata;
    	struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES];
    	struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES];
    	struct snd_soc_jack *jack;
    	unsigned int sysclk;
    	u8 p_div;
    	int rate_div_line;
    	bool master_dapm_route_applied;
    	int irq;
    	u8 ocmv; /* output common-mode voltage */
    };
    
    struct aic31xx_rate_divs {
    	u32 mclk_p;
    	u32 rate;
    	u8 pll_j;
    	u16 pll_d;
    	u16 dosr;
    	u8 ndac;
    	u8 mdac;
    	u8 aosr;
    	u8 nadc;
    	u8 madc;
    };
    
    /* ADC dividers can be disabled by configuring them to 0 */
    static const struct aic31xx_rate_divs aic31xx_divs[] = {
    	/* mclk/p    rate  pll: j     d        dosr ndac mdac  aors nadc madc */
    #if 1
    	/* 8k rate */
    	{12000000,   8000,	8, 1920,	128,  48,  2,	128,  48,  2},
    	{12000000,   8000,	8, 1920,	128,  32,  3,	128,  32,  3},
    	{12500000,   8000,	7, 8643,	128,  48,  2,	128,  48,  2},
    	/* 11.025k rate */
    	{12000000,  11025,	7, 5264,	128,  32,  2,	128,  32,  2},
    	{12000000,  11025,	8, 4672,	128,  24,  3,	128,  24,  3},
    	{12500000,  11025,	7, 2253,	128,  32,  2,	128,  32,  2},
    	/* 16k rate */
    	{12000000,  16000,	8, 1920,	128,  24,  2,	128,  24,  2},
    	{12000000,  16000,	8, 1920,	128,  16,  3,	128,  16,  3},
    	{12500000,  16000,	7, 8643,	128,  24,  2,	128,  24,  2},
    	/* 22.05k rate */
    	{12000000,  22050,	7, 5264,	128,  16,  2,	128,  16,  2},
    	{12000000,  22050,	8, 4672,	128,  12,  3,	128,  12,  3},
    	{12500000,  22050,	7, 2253,	128,  16,  2,	128,  16,  2},
    	/* 32k rate */
    	{12000000,  32000,	8, 1920,	128,  12,  2,	128,  12,  2},
    	{12000000,  32000,	8, 1920,	128,   8,  3,	128,   8,  3},
    	{12500000,  32000,	7, 8643,	128,  12,  2,	128,  12,  2},
    	/* 44.1k rate */
    	{12000000,  44100,	7, 5264,	128,   8,  2,	128,   8,  2},
    	{12000000,  44100,	8, 4672,	128,   6,  3,	128,   6,  3},
    	{12500000,  44100,	7, 2253,	128,   8,  2,	128,   8,  2},
    	{24000000, 44100, 7, 560, 128, 3, 5, 128, 3, 5},
     {24000000, 44100, 7, 5264, 128, 4, 4, 128, 16, 4},
    	/* 48k rate */
    	{12000000,  48000,	8, 1920,	128,   8,  2,	128,   8,  2},
    	{12000000,  48000,	7, 6800,	 96,   5,  4,	 96,   5,  4},
    	{12500000,  48000,	7, 8643,	128,   8,  2,	128,   8,  2},
    	/* 88.2k rate */
    	{12000000,  88200,	7, 5264,	 64,   8,  2,	 64,   8,  2},
    	{12000000,  88200,	8, 4672,	 64,   6,  3,	 64,   6,  3},
    	{12500000,  88200,	7, 2253,	 64,   8,  2,	 64,   8,  2},
    	/* 96k rate */
    	{12000000,  96000,	8, 1920,	 64,   8,  2,	 64,   8,  2},
    	{12000000,  96000,	7, 6800,	 48,   5,  4,	 48,   5,  4},
    	{12500000,  96000,	7, 8643,	 64,   8,  2,	 64,   8,  2},
    	/* 176.4k rate */
    	{12000000, 176400,	7, 5264,	 32,   8,  2,	 32,   8,  2},
    	{12000000, 176400,	8, 4672,	 32,   6,  3,	 32,   6,  3},
    	{12500000, 176400,	7, 2253,	 32,   8,  2,	 32,   8,  2},
    	/* 192k rate */
    	{12000000, 192000,	8, 1920,	 32,   8,  2,	 32,   8,  2},
    	{12000000, 192000,	7, 6800,	 24,   5,  4,	 24,   5,  4},
    	{12500000, 192000,	7, 8643,	 32,   8,  2,	 32,   8,  2},
    #endif
    #if 0
    	/* mclk/p  rate  pll: j  d   dosr ndac mdac  aors nadc madc */
    	{12000000, 8000,  1, 7, 6800, 768,  5, 3, 128,	5, 18, 24},
    	{24000000, 8000,  2, 7, 6800, 768, 15, 1,  64, 45,  4, 24},
    	{25000000, 8000,  2, 7, 3728, 768, 15, 1,  64, 45,  4, 24},
    	/* 11.025k rate */
    	{12000000, 11025, 1, 7, 5264, 512,  8, 2, 128,	8,  8, 16},
    	{24000000, 11025, 2, 7, 5264, 512, 16, 1,  64, 32,  4, 16},
    	/* 16k rate */
    	{12000000, 16000, 1, 7, 6800, 384,  5, 3, 128,	5,  9, 12},
    	{24000000, 16000, 2, 7, 6800, 384, 15, 1,  64, 18,  5, 12},
    	{25000000, 16000, 2, 7, 3728, 384, 15, 1,  64, 18,  5, 12},
    	/* 22.05k rate */
    	{12000000, 22050, 1, 7, 5264, 256,  4, 4, 128,	4,  8,	8},
    	{24000000, 22050, 2, 7, 5264, 256, 16, 1,  64, 16,  4,	8},
    	{25000000, 22050, 2, 7, 2253, 256, 16, 1,  64, 16,  4,	8},
    	/* 32k rate */
    	{12000000, 32000, 1, 7, 1680, 192,  2, 7,  64,	2, 21,	6},
    	{24000000, 32000, 2, 7, 1680, 192,  7, 2,  64,	7,  6,	6},
    	/* 44.1k rate */
    	{12000000, 44100, 1, 7, 5264, 128,  2, 8, 128,	2,  8,	4},
    	{24000000, 44100, 2, 7, 5264, 128,  8, 2,  64,	8,  4,	4},
    	{25000000, 44100, 2, 7, 2253, 128,  8, 2,  64,	8,  4,	4},
    	/* 48k rate */
    	{12000000, 48000, 1, 8, 1920, 128,  2, 8, 128,	2,  8,	4},
    	{24000000, 48000, 2, 8, 1920, 128,  8, 2,  64,	8,  4,	4},
    	{25000000, 48000, 2, 7, 8643, 128,  8, 2,  64,	8,  4,	4},
    #endif
    };
    
    static const char * const ldac_in_text[] = {
    	"Off", "Left Data", "Right Data", "Mono"
    };
    
    static const char * const rdac_in_text[] = {
    	"Off", "Right Data", "Left Data", "Mono"
    };
    
    static SOC_ENUM_SINGLE_DECL(ldac_in_enum, AIC31XX_DACSETUP, 4, ldac_in_text);
    
    static SOC_ENUM_SINGLE_DECL(rdac_in_enum, AIC31XX_DACSETUP, 2, rdac_in_text);
    
    static const char * const mic_select_text[] = {
    	"Off", "FFR 10 Ohm", "FFR 20 Ohm", "FFR 40 Ohm"
    };
    
    static SOC_ENUM_SINGLE_DECL(mic1lp_p_enum, AIC31XX_MICPGAPI, 6,
    	mic_select_text);
    static SOC_ENUM_SINGLE_DECL(mic1rp_p_enum, AIC31XX_MICPGAPI, 4,
    	mic_select_text);
    static SOC_ENUM_SINGLE_DECL(mic1lm_p_enum, AIC31XX_MICPGAPI, 2,
    	mic_select_text);
    
    static SOC_ENUM_SINGLE_DECL(mic1lm_m_enum, AIC31XX_MICPGAMI, 4,
    	mic_select_text);
    
    static const char * const hp_poweron_time_text[] = {
    	"0us", "15.3us", "153us", "1.53ms", "15.3ms", "76.2ms",
    	"153ms", "304ms", "610ms", "1.22s", "3.04s", "6.1s" };
    
    static SOC_ENUM_SINGLE_DECL(hp_poweron_time_enum, AIC31XX_HPPOP, 3,
    	hp_poweron_time_text);
    
    static const char * const hp_rampup_step_text[] = {
    	"0ms", "0.98ms", "1.95ms", "3.9ms" };
    
    static SOC_ENUM_SINGLE_DECL(hp_rampup_step_enum, AIC31XX_HPPOP, 1,
    	hp_rampup_step_text);
    
    static const char * const vol_soft_step_mode_text[] = {
    	"fast", "slow", "disabled" };
    
    static SOC_ENUM_SINGLE_DECL(vol_soft_step_mode_enum, AIC31XX_DACSETUP, 0,
    	vol_soft_step_mode_text);
    
    static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6350, 50, 0);
    static const DECLARE_TLV_DB_SCALE(adc_fgain_tlv, 0, 10, 0);
    static const DECLARE_TLV_DB_SCALE(adc_cgain_tlv, -2000, 50, 0);
    static const DECLARE_TLV_DB_SCALE(mic_pga_tlv, 0, 50, 0);
    static const DECLARE_TLV_DB_SCALE(hp_drv_tlv, 0, 100, 0);
    static const DECLARE_TLV_DB_SCALE(class_D_drv_tlv, 600, 600, 0);
    static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -6350, 50, 0);
    static const DECLARE_TLV_DB_SCALE(sp_vol_tlv, -6350, 50, 0);
    
    /*
     * controls to be exported to the user space
     */
    static const struct snd_kcontrol_new common31xx_snd_controls[] = {
    	SOC_DOUBLE_R_S_TLV("DAC Playback Volume", AIC31XX_LDACVOL,
    			   AIC31XX_RDACVOL, 0, -127, 48, 7, 0, dac_vol_tlv),
    
    	SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN,
    		     AIC31XX_HPRGAIN, 2, 1, 0),
    	SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN,
    			 AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv),
    
    	SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,
    			 AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),
    
    	/* HP de-pop control: apply power not immediately but via ramp
    	 * function with these psarameters. Note that power up sequence
    	 * has to wait for this to complete; this is implemented by
    	 * polling HP driver status in aic31xx_dapm_power_event()
    	 */
    	SOC_ENUM("HP Output Driver Power-On time", hp_poweron_time_enum),
    	SOC_ENUM("HP Output Driver Ramp-up step", hp_rampup_step_enum),
    
    	SOC_ENUM("Volume Soft Stepping", vol_soft_step_mode_enum),
    };
    
    static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
    	SOC_SINGLE_TLV("ADC Fine Capture Volume", AIC31XX_ADCFGA, 4, 4, 1,
    		       adc_fgain_tlv),
    
    	SOC_SINGLE("ADC Capture Switch", AIC31XX_ADCFGA, 7, 1, 1),
    	SOC_DOUBLE_R_S_TLV("ADC Capture Volume", AIC31XX_ADCVOL, AIC31XX_ADCVOL,
    			   0, -24, 40, 6, 0, adc_cgain_tlv),
    
    	SOC_SINGLE_TLV("Mic PGA Capture Volume", AIC31XX_MICPGA, 0,
    		       119, 0, mic_pga_tlv),
    };
    
    static const struct snd_kcontrol_new aic311x_snd_controls[] = {
    	SOC_DOUBLE_R("Speaker Driver Playback Switch", AIC31XX_SPLGAIN,
    		     AIC31XX_SPRGAIN, 2, 1, 0),
    	SOC_DOUBLE_R_TLV("Speaker Driver Playback Volume", AIC31XX_SPLGAIN,
    			 AIC31XX_SPRGAIN, 3, 3, 0, class_D_drv_tlv),
    
    	SOC_DOUBLE_R_TLV("Speaker Analog Playback Volume", AIC31XX_LANALOGSPL,
    			 AIC31XX_RANALOGSPR, 0, 0x7F, 1, sp_vol_tlv),
    };
    
    static const struct snd_kcontrol_new aic310x_snd_controls[] = {
    	SOC_SINGLE("Speaker Driver Playback Switch", AIC31XX_SPLGAIN,
    		   2, 1, 0),
    	SOC_SINGLE_TLV("Speaker Driver Playback Volume", AIC31XX_SPLGAIN,
    		       3, 3, 0, class_D_drv_tlv),
    
    	SOC_SINGLE_TLV("Speaker Analog Playback Volume", AIC31XX_LANALOGSPL,
    		       0, 0x7F, 1, sp_vol_tlv),
    };
    
    static const struct snd_kcontrol_new ldac_in_control =
    	SOC_DAPM_ENUM("DAC Left Input", ldac_in_enum);
    
    static const struct snd_kcontrol_new rdac_in_control =
    	SOC_DAPM_ENUM("DAC Right Input", rdac_in_enum);
    
    static int aic31xx_wait_bits(struct aic31xx_priv *aic31xx, unsigned int reg,
    			     unsigned int mask, unsigned int wbits, int sleep,
    			     int count)
    {
    	unsigned int bits;
    	int counter = count;
    	int ret = regmap_read(aic31xx->regmap, reg, &bits);
    
    	while ((bits & mask) != wbits && counter && !ret) {
    		usleep_range(sleep, sleep * 2);
    		ret = regmap_read(aic31xx->regmap, reg, &bits);
    		counter--;
    	}
    	if ((bits & mask) != wbits) {
    		dev_err(aic31xx->dev,
    			"%s: Failed! 0x%x was 0x%x expected 0x%x (%d, 0x%x, %d us)\n",
    			__func__, reg, bits, wbits, ret, mask,
    			(count - counter) * sleep);
    		ret = -1;
    	}
    	return ret;
    }
    
    #define WIDGET_BIT(reg, shift) (((shift) << 8) | (reg))
    
    static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w,
    				    struct snd_kcontrol *kcontrol, int event)
    {
    	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
    	struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
    	unsigned int reg = AIC31XX_DACFLAG1;
    	unsigned int mask;
    	unsigned int timeout = 500 * USEC_PER_MSEC;
    
    	switch (WIDGET_BIT(w->reg, w->shift)) {
    	case WIDGET_BIT(AIC31XX_DACSETUP, 7):
    		mask = AIC31XX_LDACPWRSTATUS_MASK;
    		break;
    	case WIDGET_BIT(AIC31XX_DACSETUP, 6):
    		mask = AIC31XX_RDACPWRSTATUS_MASK;
    		break;
    	case WIDGET_BIT(AIC31XX_HPDRIVER, 7):
    		mask = AIC31XX_HPLDRVPWRSTATUS_MASK;
    		if (event == SND_SOC_DAPM_POST_PMU)
    			timeout = 7 * USEC_PER_SEC;
    		break;
    	case WIDGET_BIT(AIC31XX_HPDRIVER, 6):
    		mask = AIC31XX_HPRDRVPWRSTATUS_MASK;
    		if (event == SND_SOC_DAPM_POST_PMU)
    			timeout = 7 * USEC_PER_SEC;
    		break;
    	case WIDGET_BIT(AIC31XX_SPKAMP, 7):
    		mask = AIC31XX_SPLDRVPWRSTATUS_MASK;
    		break;
    	case WIDGET_BIT(AIC31XX_SPKAMP, 6):
    		mask = AIC31XX_SPRDRVPWRSTATUS_MASK;
    		break;
    	case WIDGET_BIT(AIC31XX_ADCSETUP, 7):
    		mask = AIC31XX_ADCPWRSTATUS_MASK;
    		reg = AIC31XX_ADCFLAG;
    		break;
    	default:
    		printk( "Unknown widget '%s' calling %s\n",
    			w->name, __func__);
    		return -EINVAL;
    	}
    
    	switch (event) {
    	case SND_SOC_DAPM_POST_PMU:
    		return aic31xx_wait_bits(aic31xx, reg, mask, mask,
    				5000, timeout / 5000);
    	case SND_SOC_DAPM_POST_PMD:
    		return aic31xx_wait_bits(aic31xx, reg, mask, 0,
    				5000, timeout / 5000);
    	default:
    		printk(
    			"Unhandled dapm widget event %d from %s\n",
    			event, w->name);
    	}
    	return 0;
    }
    
    static const struct snd_kcontrol_new aic31xx_left_output_switches[] = {
    	SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
    	SOC_DAPM_SINGLE("From MIC1LP", AIC31XX_DACMIXERROUTE, 5, 1, 0),
    	SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 4, 1, 0),
    };
    
    static const struct snd_kcontrol_new aic31xx_right_output_switches[] = {
    	SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
    	SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 1, 1, 0),
    };
    
    static const struct snd_kcontrol_new dac31xx_left_output_switches[] = {
    	SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
    	SOC_DAPM_SINGLE("From AIN1", AIC31XX_DACMIXERROUTE, 5, 1, 0),
    	SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 4, 1, 0),
    };
    
    static const struct snd_kcontrol_new dac31xx_right_output_switches[] = {
    	SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
    	SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 1, 1, 0),
    };
    
    static const struct snd_kcontrol_new p_term_mic1lp =
    	SOC_DAPM_ENUM("MIC1LP P-Terminal", mic1lp_p_enum);
    
    static const struct snd_kcontrol_new p_term_mic1rp =
    	SOC_DAPM_ENUM("MIC1RP P-Terminal", mic1rp_p_enum);
    
    static const struct snd_kcontrol_new p_term_mic1lm =
    	SOC_DAPM_ENUM("MIC1LM P-Terminal", mic1lm_p_enum);
    
    static const struct snd_kcontrol_new m_term_mic1lm =
    	SOC_DAPM_ENUM("MIC1LM M-Terminal", mic1lm_m_enum);
    
    static const struct snd_kcontrol_new aic31xx_dapm_hpl_switch =
    	SOC_DAPM_SINGLE("Switch", AIC31XX_LANALOGHPL, 7, 1, 0);
    
    static const struct snd_kcontrol_new aic31xx_dapm_hpr_switch =
    	SOC_DAPM_SINGLE("Switch", AIC31XX_RANALOGHPR, 7, 1, 0);
    
    static const struct snd_kcontrol_new aic31xx_dapm_spl_switch =
    	SOC_DAPM_SINGLE("Switch", AIC31XX_LANALOGSPL, 7, 1, 0);
    
    static const struct snd_kcontrol_new aic31xx_dapm_spr_switch =
    	SOC_DAPM_SINGLE("Switch", AIC31XX_RANALOGSPR, 7, 1, 0);
    
    static int mic_bias_event(struct snd_soc_dapm_widget *w,
    			  struct snd_kcontrol *kcontrol, int event)
    {
    	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
    	struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
    
    	switch (event) {
    	case SND_SOC_DAPM_POST_PMU:
    		/* change mic bias voltage to user defined */
    		snd_soc_component_update_bits(component, AIC31XX_MICBIAS,
    				    AIC31XX_MICBIAS_MASK,
    				    aic31xx->micbias_vg <<
    				    AIC31XX_MICBIAS_SHIFT);
    		printk( "%s: turned on\n", __func__);
    		break;
    	case SND_SOC_DAPM_PRE_PMD:
    		/* turn mic bias off */
    		snd_soc_component_update_bits(component, AIC31XX_MICBIAS,
    				    AIC31XX_MICBIAS_MASK, 0);
    		printk( "%s: turned off\n", __func__);
    		break;
    	}
    	return 0;
    }
    
    static const struct snd_soc_dapm_widget common31xx_dapm_widgets[] = {
    	SND_SOC_DAPM_AIF_IN("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
    
    	SND_SOC_DAPM_MUX("DAC Left Input",
    			 SND_SOC_NOPM, 0, 0, &ldac_in_control),
    	SND_SOC_DAPM_MUX("DAC Right Input",
    			 SND_SOC_NOPM, 0, 0, &rdac_in_control),
    	/* DACs */
    	SND_SOC_DAPM_DAC_E("DAC Left", "Left Playback",
    			   AIC31XX_DACSETUP, 7, 0, aic31xx_dapm_power_event,
    			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
    
    	SND_SOC_DAPM_DAC_E("DAC Right", "Right Playback",
    			   AIC31XX_DACSETUP, 6, 0, aic31xx_dapm_power_event,
    			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
    
    	/* HP */
    	SND_SOC_DAPM_SWITCH("HP Left", SND_SOC_NOPM, 0, 0,
    			    &aic31xx_dapm_hpl_switch),
    	SND_SOC_DAPM_SWITCH("HP Right", SND_SOC_NOPM, 0, 0,
    			    &aic31xx_dapm_hpr_switch),
    
    	/* Output drivers */
    	SND_SOC_DAPM_OUT_DRV_E("HPL Driver", AIC31XX_HPDRIVER, 7, 0,
    			       NULL, 0, aic31xx_dapm_power_event,
    			       SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
    	SND_SOC_DAPM_OUT_DRV_E("HPR Driver", AIC31XX_HPDRIVER, 6, 0,
    			       NULL, 0, aic31xx_dapm_power_event,
    			       SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
    
    	/* Mic Bias */
    	SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event,
    			    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
    
    	/* Keep BCLK/WCLK enabled even if DAC/ADC is powered down */
    	SND_SOC_DAPM_SUPPLY("Activate I2S clocks", AIC31XX_IFACE2, 2, 0,
    			    NULL, 0),
    
    	/* Outputs */
    	SND_SOC_DAPM_OUTPUT("HPL"),
    	SND_SOC_DAPM_OUTPUT("HPR"),
    };
    
    static const struct snd_soc_dapm_widget dac31xx_dapm_widgets[] = {
    	/* Inputs */
    	SND_SOC_DAPM_INPUT("AIN1"),
    	SND_SOC_DAPM_INPUT("AIN2"),
    
    	/* Output Mixers */
    	SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
    			   dac31xx_left_output_switches,
    			   ARRAY_SIZE(dac31xx_left_output_switches)),
    	SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
    			   dac31xx_right_output_switches,
    			   ARRAY_SIZE(dac31xx_right_output_switches)),
    };
    
    static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
    	/* Inputs */
    	SND_SOC_DAPM_INPUT("MIC1LP"),
    	SND_SOC_DAPM_INPUT("MIC1RP"),
    	SND_SOC_DAPM_INPUT("MIC1LM"),
    
    	/* Input Selection to MIC_PGA */
    	SND_SOC_DAPM_MUX("MIC1LP P-Terminal", SND_SOC_NOPM, 0, 0,
    			 &p_term_mic1lp),
    	SND_SOC_DAPM_MUX("MIC1RP P-Terminal", SND_SOC_NOPM, 0, 0,
    			 &p_term_mic1rp),
    	SND_SOC_DAPM_MUX("MIC1LM P-Terminal", SND_SOC_NOPM, 0, 0,
    			 &p_term_mic1lm),
    
    	/* ADC */
    	SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0,
    			   aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
    			   SND_SOC_DAPM_POST_PMD),
    
    	SND_SOC_DAPM_MUX("MIC1LM M-Terminal", SND_SOC_NOPM, 0, 0,
    			 &m_term_mic1lm),
    
    	/* Enabling & Disabling MIC Gain Ctl */
    	SND_SOC_DAPM_PGA("MIC_GAIN_CTL", AIC31XX_MICPGA,
    			 7, 1, NULL, 0),
    
    	/* Output Mixers */
    	SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
    			   aic31xx_left_output_switches,
    			   ARRAY_SIZE(aic31xx_left_output_switches)),
    	SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
    			   aic31xx_right_output_switches,
    			   ARRAY_SIZE(aic31xx_right_output_switches)),
    
    	SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
    };
    
    static const struct snd_soc_dapm_widget aic311x_dapm_widgets[] = {
    	/* AIC3111 and AIC3110 have stereo class-D amplifier */
    	SND_SOC_DAPM_OUT_DRV_E("SPL ClassD", AIC31XX_SPKAMP, 7, 0, NULL, 0,
    			       aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
    			       SND_SOC_DAPM_POST_PMD),
    	SND_SOC_DAPM_OUT_DRV_E("SPR ClassD", AIC31XX_SPKAMP, 6, 0, NULL, 0,
    			       aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
    			       SND_SOC_DAPM_POST_PMD),
    	SND_SOC_DAPM_SWITCH("Speaker Left", SND_SOC_NOPM, 0, 0,
    			    &aic31xx_dapm_spl_switch),
    	SND_SOC_DAPM_SWITCH("Speaker Right", SND_SOC_NOPM, 0, 0,
    			    &aic31xx_dapm_spr_switch),
    	SND_SOC_DAPM_OUTPUT("SPL"),
    	SND_SOC_DAPM_OUTPUT("SPR"),
    };
    
    /* AIC3100 and AIC3120 have only mono class-D amplifier */
    static const struct snd_soc_dapm_widget aic310x_dapm_widgets[] = {
    	SND_SOC_DAPM_OUT_DRV_E("SPK ClassD", AIC31XX_SPKAMP, 7, 0, NULL, 0,
    			       aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
    			       SND_SOC_DAPM_POST_PMD),
    	SND_SOC_DAPM_SWITCH("Speaker", SND_SOC_NOPM, 0, 0,
    			    &aic31xx_dapm_spl_switch),
    	SND_SOC_DAPM_OUTPUT("SPK"),
    };
    
    static const struct snd_soc_dapm_route
    common31xx_audio_map[] = {
    	/* DAC Input Routing */
    	{"DAC Left Input", "Left Data", "AIF IN"},
    	{"DAC Left Input", "Right Data", "AIF IN"},
    	{"DAC Left Input", "Mono", "AIF IN"},
    	{"DAC Right Input", "Left Data", "AIF IN"},
    	{"DAC Right Input", "Right Data", "AIF IN"},
    	{"DAC Right Input", "Mono", "AIF IN"},
    	{"DAC Left", NULL, "DAC Left Input"},
    	{"DAC Right", NULL, "DAC Right Input"},
    
    	/* HPL path */
    	{"HP Left", "Switch", "Output Left"},
    	{"HPL Driver", NULL, "HP Left"},
    	{"HPL", NULL, "HPL Driver"},
    
    	/* HPR path */
    	{"HP Right", "Switch", "Output Right"},
    	{"HPR Driver", NULL, "HP Right"},
    	{"HPR", NULL, "HPR Driver"},
    };
    
    static const struct snd_soc_dapm_route
    dac31xx_audio_map[] = {
    	/* Left Output */
    	{"Output Left", "From Left DAC", "DAC Left"},
    	{"Output Left", "From AIN1", "AIN1"},
    	{"Output Left", "From AIN2", "AIN2"},
    
    	/* Right Output */
    	{"Output Right", "From Right DAC", "DAC Right"},
    	{"Output Right", "From AIN2", "AIN2"},
    };
    
    static const struct snd_soc_dapm_route
    aic31xx_audio_map[] = {
    	/* Mic input */
    	{"MIC1LP P-Terminal", "FFR 10 Ohm", "MIC1LP"},
    	{"MIC1LP P-Terminal", "FFR 20 Ohm", "MIC1LP"},
    	{"MIC1LP P-Terminal", "FFR 40 Ohm", "MIC1LP"},
    	{"MIC1RP P-Terminal", "FFR 10 Ohm", "MIC1RP"},
    	{"MIC1RP P-Terminal", "FFR 20 Ohm", "MIC1RP"},
    	{"MIC1RP P-Terminal", "FFR 40 Ohm", "MIC1RP"},
    	{"MIC1LM P-Terminal", "FFR 10 Ohm", "MIC1LM"},
    	{"MIC1LM P-Terminal", "FFR 20 Ohm", "MIC1LM"},
    	{"MIC1LM P-Terminal", "FFR 40 Ohm", "MIC1LM"},
    
    	{"MIC1LM M-Terminal", "FFR 10 Ohm", "MIC1LM"},
    	{"MIC1LM M-Terminal", "FFR 20 Ohm", "MIC1LM"},
    	{"MIC1LM M-Terminal", "FFR 40 Ohm", "MIC1LM"},
    
    	{"MIC_GAIN_CTL", NULL, "MIC1LP P-Terminal"},
    	{"MIC_GAIN_CTL", NULL, "MIC1RP P-Terminal"},
    	{"MIC_GAIN_CTL", NULL, "MIC1LM P-Terminal"},
    	{"MIC_GAIN_CTL", NULL, "MIC1LM M-Terminal"},
    
    	{"ADC", NULL, "MIC_GAIN_CTL"},
    
    	{"AIF OUT", NULL, "ADC"},
    
    	/* Left Output */
    	{"Output Left", "From Left DAC", "DAC Left"},
    	{"Output Left", "From MIC1LP", "MIC1LP"},
    	{"Output Left", "From MIC1RP", "MIC1RP"},
    
    	/* Right Output */
    	{"Output Right", "From Right DAC", "DAC Right"},
    	{"Output Right", "From MIC1RP", "MIC1RP"},
    };
    
    static const struct snd_soc_dapm_route
    aic311x_audio_map[] = {
    	/* SP L path */
    	{"Speaker Left", "Switch", "Output Left"},
    	{"SPL ClassD", NULL, "Speaker Left"},
    	{"SPL", NULL, "SPL ClassD"},
    
    	/* SP R path */
    	{"Speaker Right", "Switch", "Output Right"},
    	{"SPR ClassD", NULL, "Speaker Right"},
    	{"SPR", NULL, "SPR ClassD"},
    };
    
    static const struct snd_soc_dapm_route
    aic310x_audio_map[] = {
    	/* SP L path */
    	{"Speaker", "Switch", "Output Left"},
    	{"SPK ClassD", NULL, "Speaker"},
    	{"SPK", NULL, "SPK ClassD"},
    };
    
    /*
     * Always connected DAPM routes for codec clock master modes.
     * If the codec is the master on the I2S bus, we need to power up components
     * to have valid DAC_CLK.
     *
     * In order to have the I2S clocks on the bus either the DACs/ADC need to be
     * enabled, or the P0/R29/D2 (Keep bclk/wclk in power down) need to be set.
     *
     * Otherwise the codec will not generate clocks on the bus.
     */
    static const struct snd_soc_dapm_route
    common31xx_cm_audio_map[] = {
    	{"HPL", NULL, "AIF IN"},
    	{"HPR", NULL, "AIF IN"},
    
    	{"AIF IN", NULL, "Activate I2S clocks"},
    };
    
    static const struct snd_soc_dapm_route
    aic31xx_cm_audio_map[] = {
    	{"AIF OUT", NULL, "MIC1LP"},
    	{"AIF OUT", NULL, "MIC1RP"},
    	{"AIF OUT", NULL, "MIC1LM"},
    
    	{"AIF OUT", NULL, "Activate I2S clocks"},
    };
    
    static int aic31xx_add_controls(struct snd_soc_component *component)
    {
    	int ret = 0;
    	struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
    
    	if (!(aic31xx->codec_type & DAC31XX_BIT))
    		ret = snd_soc_add_component_controls(
    			component, aic31xx_snd_controls,
    			ARRAY_SIZE(aic31xx_snd_controls));
    	if (ret)
    		return ret;
    
    	if (aic31xx->codec_type & AIC31XX_STEREO_CLASS_D_BIT)
    		ret = snd_soc_add_component_controls(
    			component, aic311x_snd_controls,
    			ARRAY_SIZE(aic311x_snd_controls));
    	else
    		ret = snd_soc_add_component_controls(
    			component, aic310x_snd_controls,
    			ARRAY_SIZE(aic310x_snd_controls));
    
    	return ret;
    }
    
    static int aic31xx_add_widgets(struct snd_soc_component *component)
    {
    	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
    	struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
    	int ret = 0;
    
    	if (aic31xx->codec_type & DAC31XX_BIT) {
    		ret = snd_soc_dapm_new_controls(
    			dapm, dac31xx_dapm_widgets,
    			ARRAY_SIZE(dac31xx_dapm_widgets));
    		if (ret)
    			return ret;
    
    		ret = snd_soc_dapm_add_routes(dapm, dac31xx_audio_map,
    					      ARRAY_SIZE(dac31xx_audio_map));
    		if (ret)
    			return ret;
    	} else {
    		ret = snd_soc_dapm_new_controls(
    			dapm, aic31xx_dapm_widgets,
    			ARRAY_SIZE(aic31xx_dapm_widgets));
    		if (ret)
    			return ret;
    
    		ret = snd_soc_dapm_add_routes(dapm, aic31xx_audio_map,
    					      ARRAY_SIZE(aic31xx_audio_map));
    		if (ret)
    			return ret;
    	}
    
    	if (aic31xx->codec_type & AIC31XX_STEREO_CLASS_D_BIT) {
    		ret = snd_soc_dapm_new_controls(
    			dapm, aic311x_dapm_widgets,
    			ARRAY_SIZE(aic311x_dapm_widgets));
    		if (ret)
    			return ret;
    
    		ret = snd_soc_dapm_add_routes(dapm, aic311x_audio_map,
    					      ARRAY_SIZE(aic311x_audio_map));
    		if (ret)
    			return ret;
    	} else {
    		ret = snd_soc_dapm_new_controls(
    			dapm, aic310x_dapm_widgets,
    			ARRAY_SIZE(aic310x_dapm_widgets));
    		if (ret)
    			return ret;
    
    		ret = snd_soc_dapm_add_routes(dapm, aic310x_audio_map,
    					      ARRAY_SIZE(aic310x_audio_map));
    		if (ret)
    			return ret;
    	}
    
    	return 0;
    }
    
    static int aic31xx_setup_pll(struct snd_soc_component *component,
    			     struct snd_pcm_hw_params *params)
    {
    	struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
    	int bclk_score = snd_soc_params_to_frame_size(params);
    	int mclk_p;
    	int bclk_n = 0;
    	int match = -1;
    	int i;
    
    	if (!aic31xx->sysclk || !aic31xx->p_div) {
    		printk( "Master clock not supplied\n");
    		return -EINVAL;
    	}
    	mclk_p = aic31xx->sysclk / aic31xx->p_div;
    
    	/* Use PLL as CODEC_CLKIN and DAC_CLK as BDIV_CLKIN */
    	snd_soc_component_update_bits(component, AIC31XX_CLKMUX,
    			    AIC31XX_CODEC_CLKIN_MASK, AIC31XX_CODEC_CLKIN_PLL);
    	snd_soc_component_update_bits(component, AIC31XX_IFACE2,
    			    AIC31XX_BDIVCLK_MASK, AIC31XX_DAC2BCLK);
    
    	for (i = 0; i < ARRAY_SIZE(aic31xx_divs); i++) {
    		if (aic31xx_divs[i].rate == params_rate(params) &&
    		    aic31xx_divs[i].mclk_p == mclk_p) {
    			int s =	(aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) %
    				snd_soc_params_to_frame_size(params);
    			int bn = (aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) /
    				snd_soc_params_to_frame_size(params);
    			if (s < bclk_score && bn > 0) {
    				match = i;
    				bclk_n = bn;
    				bclk_score = s;
    			}
    		}
    	}
    
    	if (match == -1) {
    		printk(
    			"%s: Sample rate (%u) and format not supported\n",
    			__func__, params_rate(params));
    		/* See bellow for details how fix this. */
    		return -EINVAL;
    	}
    	if (bclk_score != 0) {
    		dev_warn(component->dev, "Can not produce exact bitclock");
    		/* This is fine if using dsp format, but if using i2s
    		   there may be trouble. To fix the issue edit the
    		   aic31xx_divs table for your mclk and sample
    		   rate. Details can be found from:
    		   https://www.ti.com/lit/ds/symlink/tlv320aic3100.pdf
    		   Section: 5.6 CLOCK Generation and PLL
    		*/
    	}
    	i = match;
    
    	/* PLL configuration */
    	snd_soc_component_update_bits(component, AIC31XX_PLLPR, AIC31XX_PLL_MASK,
    			    (aic31xx->p_div << 4) | 0x01);
    	snd_soc_component_write(component, AIC31XX_PLLJ, aic31xx_divs[i].pll_j);
    
    	snd_soc_component_write(component, AIC31XX_PLLDMSB,
    		      aic31xx_divs[i].pll_d >> 8);
    	snd_soc_component_write(component, AIC31XX_PLLDLSB,
    		      aic31xx_divs[i].pll_d & 0xff);
    
    	/* DAC dividers configuration */
    	snd_soc_component_update_bits(component, AIC31XX_NDAC, AIC31XX_PLL_MASK,
    			    aic31xx_divs[i].ndac);
    	snd_soc_component_update_bits(component, AIC31XX_MDAC, AIC31XX_PLL_MASK,
    			    aic31xx_divs[i].mdac);
    
    	snd_soc_component_write(component, AIC31XX_DOSRMSB, aic31xx_divs[i].dosr >> 8);
    	snd_soc_component_write(component, AIC31XX_DOSRLSB, aic31xx_divs[i].dosr & 0xff);
    
    	/* ADC dividers configuration. Write reset value 1 if not used. */
    	snd_soc_component_update_bits(component, AIC31XX_NADC, AIC31XX_PLL_MASK,
    			    aic31xx_divs[i].nadc ? aic31xx_divs[i].nadc : 1);
    	snd_soc_component_update_bits(component, AIC31XX_MADC, AIC31XX_PLL_MASK,
    			    aic31xx_divs[i].madc ? aic31xx_divs[i].madc : 1);
    
    	snd_soc_component_write(component, AIC31XX_AOSR, aic31xx_divs[i].aosr);
    
    	/* Bit clock divider configuration. */
    	snd_soc_component_update_bits(component, AIC31XX_BCLKN,
    			    AIC31XX_PLL_MASK, bclk_n);
    
    	aic31xx->rate_div_line = i;
    
    	printk(
    		"pll %d.%04d/%d dosr %d n %d m %d aosr %d n %d m %d bclk_n %d\n",
    		aic31xx_divs[i].pll_j,
    		aic31xx_divs[i].pll_d,
    		aic31xx->p_div,
    		aic31xx_divs[i].dosr,
    		aic31xx_divs[i].ndac,
    		aic31xx_divs[i].mdac,
    		aic31xx_divs[i].aosr,
    		aic31xx_divs[i].nadc,
    		aic31xx_divs[i].madc,
    		bclk_n
    	);
    
    	return 0;
    }
    
    static int aic31xx_hw_params(struct snd_pcm_substream *substream,
    			     struct snd_pcm_hw_params *params,
    			     struct snd_soc_dai *dai)
    {
    	struct snd_soc_component *component = dai->component;
    	u8 data = 0;
    
    	printk( "## %s: width %d rate %d\n",
    		__func__, params_width(params),
    		params_rate(params));
    
    	switch (params_width(params)) {
    	case 16:
    		break;
    	case 20:
    		data = (AIC31XX_WORD_LEN_20BITS <<
    			AIC31XX_IFACE1_DATALEN_SHIFT);
    		break;
    	case 24:
    		data = (AIC31XX_WORD_LEN_24BITS <<
    			AIC31XX_IFACE1_DATALEN_SHIFT);
    		break;
    	case 32:
    		data = (AIC31XX_WORD_LEN_32BITS <<
    			AIC31XX_IFACE1_DATALEN_SHIFT);
    		break;
    	default:
    		printk( "%s: Unsupported width %d\n",
    			__func__, params_width(params));
    		return -EINVAL;
    	}
    
    	snd_soc_component_update_bits(component, AIC31XX_IFACE1,
    			    AIC31XX_IFACE1_DATALEN_MASK,
    			    data);
    
    	return aic31xx_setup_pll(component, params);
    }
    
    static int aic31xx_dac_mute(struct snd_soc_dai *codec_dai, int mute,
    			    int direction)
    {
    	struct snd_soc_component *component = codec_dai->component;
    
    	if (mute) {
    		snd_soc_component_update_bits(component, AIC31XX_DACMUTE,
    				    AIC31XX_DACMUTE_MASK,
    				    AIC31XX_DACMUTE_MASK);
    	} else {
    		snd_soc_component_update_bits(component, AIC31XX_DACMUTE,
    				    AIC31XX_DACMUTE_MASK, 0x0);
    	}
    
    	return 0;
    }
    
    static int aic31xx_clock_master_routes(struct snd_soc_component *component,
    				       unsigned int fmt)
    {
    	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
    	struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
    	int ret;
    
    	fmt &= SND_SOC_DAIFMT_MASTER_MASK;
    	if (fmt == SND_SOC_DAIFMT_CBS_CFS &&
    	    aic31xx->master_dapm_route_applied) {
    		/*
    		 * Remove the DAPM route(s) for codec clock master modes,
    		 * if applied
    		 */
    		ret = snd_soc_dapm_del_routes(dapm, common31xx_cm_audio_map,
    					ARRAY_SIZE(common31xx_cm_audio_map));
    		if (!ret && !(aic31xx->codec_type & DAC31XX_BIT))
    			ret = snd_soc_dapm_del_routes(dapm,
    					aic31xx_cm_audio_map,
    					ARRAY_SIZE(aic31xx_cm_audio_map));
    
    		if (ret)
    			return ret;
    
    		aic31xx->master_dapm_route_applied = false;
    	} else if (fmt != SND_SOC_DAIFMT_CBS_CFS &&
    		   !aic31xx->master_dapm_route_applied) {
    		/*
    		 * Add the needed DAPM route(s) for codec clock master modes,
    		 * if it is not done already
    		 */
    		ret = snd_soc_dapm_add_routes(dapm, common31xx_cm_audio_map,
    					ARRAY_SIZE(common31xx_cm_audio_map));
    		if (!ret && !(aic31xx->codec_type & DAC31XX_BIT))
    			ret = snd_soc_dapm_add_routes(dapm,
    					aic31xx_cm_audio_map,
    					ARRAY_SIZE(aic31xx_cm_audio_map));
    
    		if (ret)
    			return ret;
    
    		aic31xx->master_dapm_route_applied = true;
    	}
    
    	return 0;
    }
    
    static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
    			       unsigned int fmt)
    {
    	struct snd_soc_component *component = codec_dai->component;
    	u8 iface_reg1 = 0;
    	u8 iface_reg2 = 0;
    	u8 dsp_a_val = 0;
    
    	printk( "## %s: fmt = 0x%x\n", __func__, fmt);
    
    	/* set master/slave audio interface */
    	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
    	case SND_SOC_DAIFMT_CBM_CFM:
    		iface_reg1 |= AIC31XX_BCLK_MASTER | AIC31XX_WCLK_MASTER;
    		break;
    	case SND_SOC_DAIFMT_CBS_CFM:
    		iface_reg1 |= AIC31XX_WCLK_MASTER;
    		break;
    	case SND_SOC_DAIFMT_CBM_CFS:
    		iface_reg1 |= AIC31XX_BCLK_MASTER;
    		break;
    	case SND_SOC_DAIFMT_CBS_CFS:
    		break;
    	default:
    		printk( "Invalid DAI master/slave interface\n");
    		return -EINVAL;
    	}
    
    	/* signal polarity */
    	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
    	case SND_SOC_DAIFMT_NB_NF:
    		break;
    	case SND_SOC_DAIFMT_IB_NF:
    		iface_reg2 |= AIC31XX_BCLKINV_MASK;
    		break;
    	default:
    		printk( "Invalid DAI clock signal polarity\n");
    		return -EINVAL;
    	}
    
    	/* interface format */
    	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
    	case SND_SOC_DAIFMT_I2S:
    		break;
    	case SND_SOC_DAIFMT_DSP_A:
    		dsp_a_val = 0x1;
    		fallthrough;
    	case SND_SOC_DAIFMT_DSP_B:
    		/*
    		 * NOTE: This CODEC samples on the falling edge of BCLK in
    		 * DSP mode, this is inverted compared to what most DAIs
    		 * expect, so we invert for this mode
    		 */
    		iface_reg2 ^= AIC31XX_BCLKINV_MASK;
    		iface_reg1 |= (AIC31XX_DSP_MODE <<
    			       AIC31XX_IFACE1_DATATYPE_SHIFT);
    		break;
    	case SND_SOC_DAIFMT_RIGHT_J:
    		iface_reg1 |= (AIC31XX_RIGHT_JUSTIFIED_MODE <<
    			       AIC31XX_IFACE1_DATATYPE_SHIFT);
    		break;
    	case SND_SOC_DAIFMT_LEFT_J:
    		iface_reg1 |= (AIC31XX_LEFT_JUSTIFIED_MODE <<
    			       AIC31XX_IFACE1_DATATYPE_SHIFT);
    		break;
    	default:
    		printk( "Invalid DAI interface format\n");
    		return -EINVAL;
    	}
    
    	snd_soc_component_update_bits(component, AIC31XX_IFACE1,
    			    AIC31XX_IFACE1_DATATYPE_MASK |
    			    AIC31XX_IFACE1_MASTER_MASK,
    			    iface_reg1);
    	snd_soc_component_update_bits(component, AIC31XX_DATA_OFFSET,
    			    AIC31XX_DATA_OFFSET_MASK,
    			    dsp_a_val);
    	snd_soc_component_update_bits(component, AIC31XX_IFACE2,
    			    AIC31XX_BCLKINV_MASK,
    			    iface_reg2);
    
    	return aic31xx_clock_master_routes(component, fmt);
    }
    
    static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
    				  int clk_id, unsigned int freq, int dir)
    {
    	struct snd_soc_component *component = codec_dai->component;
    	struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
    	int i;
    
    	printk( "## %s: clk_id = %d, freq = %d, dir = %d\n",
    		__func__, clk_id, freq, dir);
    
    	for (i = 1; i < 8; i++)
    		if (freq / i <= 20000000)
    			break;
    	if (freq/i > 20000000) {
    		dev_err(aic31xx->dev, "%s: Too high mclk frequency %u\n",
    			__func__, freq);
    		return -EINVAL;
    	}
    	aic31xx->p_div = i;
    
    	for (i = 0; i < ARRAY_SIZE(aic31xx_divs); i++)
    		if (aic31xx_divs[i].mclk_p == freq / aic31xx->p_div)
    			break;
    	if (i == ARRAY_SIZE(aic31xx_divs)) {
    		dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
    			__func__, freq);
    		return -EINVAL;
    	}
    
    	/* set clock on MCLK, BCLK, or GPIO1 as PLL input */
    	snd_soc_component_update_bits(component, AIC31XX_CLKMUX, AIC31XX_PLL_CLKIN_MASK,
    			    clk_id << AIC31XX_PLL_CLKIN_SHIFT);
    
    	aic31xx->sysclk = freq;
    
    	return 0;
    }
    
    static int aic31xx_regulator_event(struct notifier_block *nb,
    				   unsigned long event, void *data)
    {
    	struct aic31xx_disable_nb *disable_nb =
    		container_of(nb, struct aic31xx_disable_nb, nb);
    	struct aic31xx_priv *aic31xx = disable_nb->aic31xx;
    
    	if (event & REGULATOR_EVENT_DISABLE) {
    		/*
    		 * Put codec to reset and as at least one of the
    		 * supplies was disabled.
    		 */
    		if (aic31xx->gpio_reset)
    			gpiod_set_value(aic31xx->gpio_reset, 1);
    
    		regcache_mark_dirty(aic31xx->regmap);
    		dev_dbg(aic31xx->dev, "## %s: DISABLE received\n", __func__);
    	}
    
    	return 0;
    }
    
    static int aic31xx_reset(struct aic31xx_priv *aic31xx)
    {
    	int ret = 0;
    
    	if (aic31xx->gpio_reset) {
    		gpiod_set_value(aic31xx->gpio_reset, 1);
    		ndelay(10); /* At least 10ns */
    		gpiod_set_value(aic31xx->gpio_reset, 0);
    	} else {
    		ret = regmap_write(aic31xx->regmap, AIC31XX_RESET, 1);
    	}
    	mdelay(1); /* At least 1ms */
    
    	return ret;
    }
    
    static void aic31xx_clk_on(struct snd_soc_component *component)
    {
    	struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
    	u8 mask = AIC31XX_PM_MASK;
    	u8 on = AIC31XX_PM_MASK;
    
    	printk( "codec clock -> on (rate %d)\n",
    		aic31xx_divs[aic31xx->rate_div_line].rate);
    	snd_soc_component_update_bits(component, AIC31XX_PLLPR, mask, on);
    	mdelay(10);
    	snd_soc_component_update_bits(component, AIC31XX_NDAC, mask, on);
    	snd_soc_component_update_bits(component, AIC31XX_MDAC, mask, on);
    	if (aic31xx_divs[aic31xx->rate_div_line].nadc)
    		snd_soc_component_update_bits(component, AIC31XX_NADC, mask, on);
    	if (aic31xx_divs[aic31xx->rate_div_line].madc)
    		snd_soc_component_update_bits(component, AIC31XX_MADC, mask, on);
    	snd_soc_component_update_bits(component, AIC31XX_BCLKN, mask, on);
    }
    
    static void aic31xx_clk_off(struct snd_soc_component *component)
    {
    	u8 mask = AIC31XX_PM_MASK;
    	u8 off = 0;
    
    	printk( "codec clock -> off\n");
    	snd_soc_component_update_bits(component, AIC31XX_BCLKN, mask, off);
    	snd_soc_component_update_bits(component, AIC31XX_MADC, mask, off);
    	snd_soc_component_update_bits(component, AIC31XX_NADC, mask, off);
    	snd_soc_component_update_bits(component, AIC31XX_MDAC, mask, off);
    	snd_soc_component_update_bits(component, AIC31XX_NDAC, mask, off);
    	snd_soc_component_update_bits(component, AIC31XX_PLLPR, mask, off);
    }
    
    static int aic31xx_power_on(struct snd_soc_component *component)
    {
    	struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
    	int ret;
    
    	ret = regulator_bulk_enable(ARRAY_SIZE(aic31xx->supplies),
    				    aic31xx->supplies);
    	if (ret)
    		return ret;
    
    	regcache_cache_only(aic31xx->regmap, false);
    
    	/* Reset device registers for a consistent power-on like state */
    	ret = aic31xx_reset(aic31xx);
    	if (ret < 0)
    		dev_err(aic31xx->dev, "Could not reset device: %d\n", ret);
    
    	ret = regcache_sync(aic31xx->regmap);
    	if (ret) {
    		printk(
    			"Failed to restore cache: %d\n", ret);
    		regcache_cache_only(aic31xx->regmap, true);
    		regulator_bulk_disable(ARRAY_SIZE(aic31xx->supplies),
    				       aic31xx->supplies);
    		return ret;
    	}
    
    	/*
    	 * The jack detection configuration is in the same register
    	 * that is used to report jack detect status so is volatile
    	 * and not covered by the cache sync, restore it separately.
    	 */
    	aic31xx_set_jack(component, aic31xx->jack, NULL);
    
    	return 0;
    }
    
    static void aic31xx_power_off(struct snd_soc_component *component)
    {
    	struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
    
    	regcache_cache_only(aic31xx->regmap, true);
    	regulator_bulk_disable(ARRAY_SIZE(aic31xx->supplies),
    			       aic31xx->supplies);
    }
    
    static int aic31xx_set_bias_level(struct snd_soc_component *component,
    				  enum snd_soc_bias_level level)
    {
    	printk( "## %s: %d -> %d\n", __func__,
    		snd_soc_component_get_bias_level(component), level);
    
    	switch (level) {
    	case SND_SOC_BIAS_ON:
    		break;
    	case SND_SOC_BIAS_PREPARE:
    		if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY)
    			aic31xx_clk_on(component);
    		break;
    	case SND_SOC_BIAS_STANDBY:
    		switch (snd_soc_component_get_bias_level(component)) {
    		case SND_SOC_BIAS_OFF:
    			aic31xx_power_on(component);
    			break;
    		case SND_SOC_BIAS_PREPARE:
    			aic31xx_clk_off(component);
    			break;
    		default:
    			BUG();
    		}
    		break;
    	case SND_SOC_BIAS_OFF:
    		if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY)
    			aic31xx_power_off(component);
    		break;
    	}
    
    	return 0;
    }
    
    static int aic31xx_set_jack(struct snd_soc_component *component,
    			    struct snd_soc_jack *jack, void *data)
    {
    	struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
    
    	aic31xx->jack = jack;
    
    	/* Enable/Disable jack detection */
    	regmap_write(aic31xx->regmap, AIC31XX_HSDETECT,
    		     jack ? AIC31XX_HSD_ENABLE : 0);
    
    	return 0;
    }
    
    static int aic31xx_codec_probe(struct snd_soc_component *component)
    {
    	struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
    	int i, ret;
    
    	dev_dbg(aic31xx->dev, "## %s\n", __func__);
    	printk("Zumiaic31xx->dev,## %s\n", __func__);
    
    	aic31xx->component = component;
    
    	for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) {
    		aic31xx->disable_nb[i].nb.notifier_call =
    			aic31xx_regulator_event;
    		aic31xx->disable_nb[i].aic31xx = aic31xx;
    		ret = devm_regulator_register_notifier(
    						aic31xx->supplies[i].consumer,
    						&aic31xx->disable_nb[i].nb);
    		if (ret) {
    			printk(
    				"Failed to request regulator notifier: %d\n",
    				ret);
    			return ret;
    		}
    	}
    
    	regcache_cache_only(aic31xx->regmap, true);
    	regcache_mark_dirty(aic31xx->regmap);
    
    	ret = aic31xx_add_controls(component);
    	if (ret)
    		return ret;
    
    	ret = aic31xx_add_widgets(component);
    	if (ret)
    		return ret;
    
    	/* set output common-mode voltage */
    	snd_soc_component_update_bits(component, AIC31XX_HPDRIVER,
    				      AIC31XX_HPD_OCMV_MASK,
    				      aic31xx->ocmv << AIC31XX_HPD_OCMV_SHIFT);
    
    	return 0;
    }
    
    static const struct snd_soc_component_driver soc_codec_driver_aic31xx = {
    	.probe			= aic31xx_codec_probe,
    	.set_jack		= aic31xx_set_jack,
    	.set_bias_level		= aic31xx_set_bias_level,
    	.controls		= common31xx_snd_controls,
    	.num_controls		= ARRAY_SIZE(common31xx_snd_controls),
    	.dapm_widgets		= common31xx_dapm_widgets,
    	.num_dapm_widgets	= ARRAY_SIZE(common31xx_dapm_widgets),
    	.dapm_routes		= common31xx_audio_map,
    	.num_dapm_routes	= ARRAY_SIZE(common31xx_audio_map),
    	.suspend_bias_off	= 1,
    	.idle_bias_on		= 1,
    	.use_pmdown_time	= 1,
    	.endianness		= 1,
    	.non_legacy_dai_naming	= 1,
    };
    
    static const struct snd_soc_dai_ops aic31xx_dai_ops = {
    	.hw_params	= aic31xx_hw_params,
    	.set_sysclk	= aic31xx_set_dai_sysclk,
    	.set_fmt	= aic31xx_set_dai_fmt,
    	.mute_stream	= aic31xx_dac_mute,
    	.no_capture_mute = 1,
    };
    
    static struct snd_soc_dai_driver dac31xx_dai_driver[] = {
    	{
    		.name = "tlv320dac31xx-hifi",
    		.playback = {
    			.stream_name	 = "Playback",
    			.channels_min	 = 2,
    			.channels_max	 = 2,
    			.rates		 = AIC31XX_RATES,
    			.formats	 = AIC31XX_FORMATS,
    		},
    		.ops = &aic31xx_dai_ops,
    		.symmetric_rates = 1,
    	}
    };
    
    static struct snd_soc_dai_driver aic31xx_dai_driver[] = {
    	{
    		.name = "tlv320aic31xx-hifi",
    		.playback = {
    			.stream_name	 = "Playback",
    			.channels_min	 = 2,
    			.channels_max	 = 2,
    			.rates		 = AIC31XX_RATES,
    			.formats	 = AIC31XX_FORMATS,
    		},
    		.capture = {
    			.stream_name	 = "Capture",
    			.channels_min	 = 2,
    			.channels_max	 = 2,
    			.rates		 = AIC31XX_RATES,
    			.formats	 = AIC31XX_FORMATS,
    		},
    		.ops = &aic31xx_dai_ops,
    		.symmetric_rates = 1,
    	}
    };
    
    #if defined(CONFIG_OF)
    static const struct of_device_id tlv320aic31xx_of_match[] = {
    	{ .compatible = "ti,tlv320aic310x" },
    	{ .compatible = "ti,tlv320aic311x" },
    	{ .compatible = "ti,tlv320aic3100" },
    	{ .compatible = "ti,tlv320aic3110" },
    	{ .compatible = "ti,tlv320aic3120" },
    	{ .compatible = "ti,tlv320aic3111" },
    	{ .compatible = "ti,tlv320dac3100" },
    	{ .compatible = "ti,tlv320dac3101" },
    	{},
    };
    MODULE_DEVICE_TABLE(of, tlv320aic31xx_of_match);
    #endif /* CONFIG_OF */
    
    #ifdef CONFIG_ACPI
    static const struct acpi_device_id aic31xx_acpi_match[] = {
    	{ "10TI3100", 0 },
    	{ }
    };
    MODULE_DEVICE_TABLE(acpi, aic31xx_acpi_match);
    #endif
    
    static irqreturn_t aic31xx_irq(int irq, void *data)
    {
    	struct aic31xx_priv *aic31xx = data;
    	struct device *dev = aic31xx->dev;
    	unsigned int value;
    	bool handled = false;
    	int ret;
    
    	ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG, &value);
    	if (ret) {
    		dev_err(dev, "Failed to read interrupt mask: %d\n", ret);
    		goto exit;
    	}
    
    	if (value)
    		handled = true;
    	else
    		goto read_overflow;
    
    	if (value & AIC31XX_HPLSCDETECT)
    		dev_err(dev, "Short circuit on Left output is detected\n");
    	if (value & AIC31XX_HPRSCDETECT)
    		dev_err(dev, "Short circuit on Right output is detected\n");
    	if (value & (AIC31XX_HSPLUG | AIC31XX_BUTTONPRESS)) {
    		unsigned int val;
    		int status = 0;
    
    		ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG2,
    				  &val);
    		if (ret) {
    			dev_err(dev, "Failed to read interrupt mask: %d\n",
    				ret);
    			goto exit;
    		}
    
    		if (val & AIC31XX_BUTTONPRESS)
    			status |= SND_JACK_BTN_0;
    
    		ret = regmap_read(aic31xx->regmap, AIC31XX_HSDETECT, &val);
    		if (ret) {
    			dev_err(dev, "Failed to read headset type: %d\n", ret);
    			goto exit;
    		}
    
    		switch ((val & AIC31XX_HSD_TYPE_MASK) >>
    			AIC31XX_HSD_TYPE_SHIFT) {
    		case AIC31XX_HSD_HP:
    			status |= SND_JACK_HEADPHONE;
    			break;
    		case AIC31XX_HSD_HS:
    			status |= SND_JACK_HEADSET;
    			break;
    		default:
    			break;
    		}
    
    		if (aic31xx->jack)
    			snd_soc_jack_report(aic31xx->jack, status,
    					    AIC31XX_JACK_MASK);
    	}
    	if (value & ~(AIC31XX_HPLSCDETECT |
    		      AIC31XX_HPRSCDETECT |
    		      AIC31XX_HSPLUG |
    		      AIC31XX_BUTTONPRESS))
    		dev_err(dev, "Unknown DAC interrupt flags: 0x%08x\n", value);
    
    read_overflow:
    	ret = regmap_read(aic31xx->regmap, AIC31XX_OFFLAG, &value);
    	if (ret) {
    		dev_err(dev, "Failed to read overflow flag: %d\n", ret);
    		goto exit;
    	}
    
    	if (value)
    		handled = true;
    	else
    		goto exit;
    
    	if (value & AIC31XX_DAC_OF_LEFT)
    		dev_warn(dev, "Left-channel DAC overflow has occurred\n");
    	if (value & AIC31XX_DAC_OF_RIGHT)
    		dev_warn(dev, "Right-channel DAC overflow has occurred\n");
    	if (value & AIC31XX_DAC_OF_SHIFTER)
    		dev_warn(dev, "DAC barrel shifter overflow has occurred\n");
    	if (value & AIC31XX_ADC_OF)
    		dev_warn(dev, "ADC overflow has occurred\n");
    	if (value & AIC31XX_ADC_OF_SHIFTER)
    		dev_warn(dev, "ADC barrel shifter overflow has occurred\n");
    	if (value & ~(AIC31XX_DAC_OF_LEFT |
    		      AIC31XX_DAC_OF_RIGHT |
    		      AIC31XX_DAC_OF_SHIFTER |
    		      AIC31XX_ADC_OF |
    		      AIC31XX_ADC_OF_SHIFTER))
    		dev_warn(dev, "Unknown overflow interrupt flags: 0x%08x\n", value);
    
    exit:
    	if (handled)
    		return IRQ_HANDLED;
    	else
    		return IRQ_NONE;
    }
    
    static void aic31xx_configure_ocmv(struct aic31xx_priv *priv)
    {
    	struct device *dev = priv->dev;
    	int dvdd, avdd;
    	u32 value;
    
    	if (dev->fwnode &&
    	    fwnode_property_read_u32(dev->fwnode, "ai31xx-ocmv", &value)) {
    		/* OCMV setting is forced by DT */
    		if (value <= 3) {
    			priv->ocmv = value;
    			return;
    		}
    	}
    
    	avdd = regulator_get_voltage(priv->supplies[3].consumer);
    	dvdd = regulator_get_voltage(priv->supplies[5].consumer);
    
    	if (avdd > 3600000 || dvdd > 1950000) {
    		dev_warn(dev,
    			 "Too high supply voltage(s) AVDD: %d, DVDD: %d\n",
    			 avdd, dvdd);
    	} else if (avdd == 3600000 && dvdd == 1950000) {
    		priv->ocmv = AIC31XX_HPD_OCMV_1_8V;
    	} else if (avdd >= 3300000 && dvdd >= 1800000) {
    		priv->ocmv = AIC31XX_HPD_OCMV_1_65V;
    	} else if (avdd >= 3000000 && dvdd >= 1650000) {
    		priv->ocmv = AIC31XX_HPD_OCMV_1_5V;
    	} else if (avdd >= 2700000 && dvdd >= 1525000) {
    		priv->ocmv = AIC31XX_HPD_OCMV_1_35V;
    	} else {
    		dev_warn(dev,
    			 "Invalid supply voltage(s) AVDD: %d, DVDD: %d\n",
    			 avdd, dvdd);
    	}
    }
    
    static int aic31xx_i2c_probe(struct i2c_client *i2c,
    			     const struct i2c_device_id *id)
    {
    	struct aic31xx_priv *aic31xx;
    	unsigned int micbias_value = MICBIAS_2_0V;
    	int i, ret;
    
    	dev_dbg(&i2c->dev, "## %s: %s codec_type = %d\n", __func__,
    		id->name, (int)id->driver_data);
    
    	aic31xx = devm_kzalloc(&i2c->dev, sizeof(*aic31xx), GFP_KERNEL);
    	if (!aic31xx)
    		return -ENOMEM;
    
    	aic31xx->regmap = devm_regmap_init_i2c(i2c, &aic31xx_i2c_regmap);
    	if (IS_ERR(aic31xx->regmap)) {
    		ret = PTR_ERR(aic31xx->regmap);
    		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
    			ret);
    		return ret;
    	}
    	aic31xx->dev = &i2c->dev;
    	aic31xx->irq = i2c->irq;
    
    	aic31xx->codec_type = id->driver_data;
    
    	dev_set_drvdata(aic31xx->dev, aic31xx);
    
    	fwnode_property_read_u32(aic31xx->dev->fwnode, "ai31xx-micbias-vg",
    				 &micbias_value);
    	switch (micbias_value) {
    	case MICBIAS_2_0V:
    	case MICBIAS_2_5V:
    	case MICBIAS_AVDDV:
    		aic31xx->micbias_vg = micbias_value;
    		break;
    	default:
    		dev_err(aic31xx->dev, "Bad ai31xx-micbias-vg value %d\n",
    			micbias_value);
    		aic31xx->micbias_vg = MICBIAS_2_0V;
    	}
    
    	if (dev_get_platdata(aic31xx->dev)) {
    		memcpy(&aic31xx->pdata, dev_get_platdata(aic31xx->dev), sizeof(aic31xx->pdata));
    		aic31xx->codec_type = aic31xx->pdata.codec_type;
    		aic31xx->micbias_vg = aic31xx->pdata.micbias_vg;
    	}
    
    	aic31xx->gpio_reset = devm_gpiod_get_optional(aic31xx->dev, "reset",
    						      GPIOD_OUT_LOW);
    	if (IS_ERR(aic31xx->gpio_reset)) {
    		if (PTR_ERR(aic31xx->gpio_reset) != -EPROBE_DEFER)
    			dev_err(aic31xx->dev, "not able to acquire gpio\n");
    		return PTR_ERR(aic31xx->gpio_reset);
    	}
    
    	for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++)
    		aic31xx->supplies[i].supply = aic31xx_supply_names[i];
    
    	ret = devm_regulator_bulk_get(aic31xx->dev,
    				      ARRAY_SIZE(aic31xx->supplies),
    				      aic31xx->supplies);
    	if (ret) {
    		if (ret != -EPROBE_DEFER)
    			dev_err(aic31xx->dev,
    				"Failed to request supplies: %d\n", ret);
    		return ret;
    	}
    
    	aic31xx_configure_ocmv(aic31xx);
    
    	if (aic31xx->irq > 0) {
    		regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1,
    				   AIC31XX_GPIO1_FUNC_MASK,
    				   AIC31XX_GPIO1_INT1 <<
    				   AIC31XX_GPIO1_FUNC_SHIFT);
    
    		regmap_write(aic31xx->regmap, AIC31XX_INT1CTRL,
    			     AIC31XX_HSPLUGDET |
    			     AIC31XX_BUTTONPRESSDET |
    			     AIC31XX_SC |
    			     AIC31XX_ENGINE);
    
    		ret = devm_request_threaded_irq(aic31xx->dev, aic31xx->irq,
    						NULL, aic31xx_irq,
    						IRQF_ONESHOT, "aic31xx-irq",
    						aic31xx);
    		if (ret) {
    			dev_err(aic31xx->dev, "Unable to request IRQ\n");
    			return ret;
    		}
    	}
    
    	if (aic31xx->codec_type & DAC31XX_BIT)
    		return devm_snd_soc_register_component(&i2c->dev,
    				&soc_codec_driver_aic31xx,
    				dac31xx_dai_driver,
    				ARRAY_SIZE(dac31xx_dai_driver));
    	else
    		return devm_snd_soc_register_component(&i2c->dev,
    				&soc_codec_driver_aic31xx,
    				aic31xx_dai_driver,
    				ARRAY_SIZE(aic31xx_dai_driver));
    }
    
    static const struct i2c_device_id aic31xx_i2c_id[] = {
    	{ "tlv320aic310x", AIC3100 },
    	{ "tlv320aic311x", AIC3110 },
    	{ "tlv320aic3100", AIC3100 },
    	{ "tlv320aic3110", AIC3110 },
    	{ "tlv320aic3120", AIC3120 },
    	{ "tlv320aic3111", AIC3111 },
    	{ "tlv320dac3100", DAC3100 },
    	{ "tlv320dac3101", DAC3101 },
    	{ }
    };
    MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
    
    static struct i2c_driver aic31xx_i2c_driver = {
    	.driver = {
    		.name	= "tlv320aic31xx-codec",
    		.of_match_table = of_match_ptr(tlv320aic31xx_of_match),
    		.acpi_match_table = ACPI_PTR(aic31xx_acpi_match),
    	},
    	.probe		= aic31xx_i2c_probe,
    	.id_table	= aic31xx_i2c_id,
    };
    module_i2c_driver(aic31xx_i2c_driver);
    
    MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
    MODULE_DESCRIPTION("ASoC TLV320AIC31xx CODEC Driver");
    MODULE_LICENSE("GPL v2");
    

    i2cdump -f -y 1 0x18

    0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
    00: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ?...............
    10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 ...............?
    20: 06 3e 00 00 7f 7f ff 7f 02 02 00 00 20 86 00 80 ?>..??.???.. ?.?
    30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    root@pico-imx8mm:~#

    Playing wav file:

    root@pico-imx8mm:~# aplay 44100_16_st.wav
    [ 87.580362] ## aic31xx_hw_params: width 16 rate 44100
    [ 87.585469] Zumi sysclk 24000000 p_div 2
    [ 87.589450] Zumi mclk_p 12000000
    [ 87.600295] pll 7.5264/2 dosr 128 n 4 m 4 aosr 128 n 16 m 4 bclk_n 16
    [ 87.606762] Zumi : rate = 44100 sai->bclk_ratio = 0
    [ 87.611834] Zumi : slots = 2 slot_width = 16
    [ 87.616381] 1. SKZumi ratio 0 for freq 1411200Hz based on clock 0Hz
    [ 87.622714] 1. SKZumi verid major 3minor 1
    [ 87.626930] 2. Zumi ratio 17 for freq 1411200Hz based on clock 24000000Hz

    [ 304.640543] fsl-sai 30020000.sai: failed to derive required Tx rate: 1411200
    [ 304.648159] fsl-sai 30020000.sai: ASoC: error at snd_soc_dai_hw_params on 30020000.sai: -22
    [ 304.657061] 30020000.sai-tlv320aic31xx-hifi: ASoC: soc_pcm_hw_params() failed (-22)
    ALSA lib pcm_direct.c:1284:(snd1_pcm_direct_initialize_slave) unable to install hw params
    ALSA lib pcm_dmix.c:1044:(snd_pcm_dmix_open) unable to initialize slave
    aplay: main:831: audio open error: Invalid argument
    root@picoimx8mm:/home/ubuntu#

    Attached the device tree file

    // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
    /*
     * Copyright 2020 Technexion Ltd.
     *
     * Author: Richard Hu <richard.hu@technexion.com>
     *
     */
    
    /dts-v1/;
    
    #include <dt-bindings/usb/pd.h>
    #include "imx8mm.dtsi"
    
    / {
    	reserved-memory {
    		#address-cells = <2>;
    		#size-cells = <2>;
    		ranges;
    
    		rpmsg_reserved: rpmsg@0xb8000000 {
    			no-map;
    			reg = <0 0xb8000000 0 0x400000>;
    		};
    	};
    
    	chosen {
    		stdout-path = &uart2;
    	};
    
    	bt_reg_on: btreg_on {
    		compatible = "gpio-reset";
    		pinctrl-names = "default";
    		pinctrl-0 = <&pinctrl_bt_ctrl>;
    		reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
    		reset-delay-us = <2000>;
    		reset-post-delay-ms = <40>;
    		#reset-cells = <0>;
    	};
    
    	pcie0_refclk: pcie0-refclk {
    		compatible = "fixed-clock";
    		#clock-cells = <0>;
    		clock-frequency = <100000000>;
    	};
    
    sound-tlv320aic31xx {
                 compatible = "simple-audio-card";
                 simple-audio-card,name = "tlv320aic31xx-codec";
                 simple-audio-card,format = "i2s";
                 simple-audio-card,frame-master = <&cpudai2>;
                 simple-audio-card,bitclock-master = <&cpudai2>;
                 /*simple-audio-card,mclk-fs=<256>;*/
                 simple-audio-card,widgets =
                         "Headphone", "Headphone Jack";
                 simple-audio-card,routing =
                         "Headphone Jack", "HPL",
                         "Headphone Jack", "HPR";
    
                 cpudai2:simple-audio-card,cpu {
                         sound-dai = <&sai2>;
                 };
    
                 simple-audio-card,codec {
                         sound-dai = <&tlv320aic31xx>;
                         //system-clock-frequency = <24000000>;
                         clocks = <&clk IMX8MM_CLK_SAI2_ROOT>;
                 };
            };
    
            clocks {
                    codec_osc: aud_mclk {
                            compatible = "fixed-clock";
                            #clock-cells = <0>;
                            clock-frequency = <24000000>;
                            clock-output-names = "codec-mclk";
                    };
            };
     
            reg_1p8v: regulator-1p8v {
                    compatible = "regulator-fixed";
                    regulator-name = "1P8V";
                    regulator-min-microvolt = <1800000>;
                    regulator-max-microvolt = <1800000>;
                    regulator-always-on;
            };
    
            reg_3p3v: regulator-3p3v {
                    compatible = "regulator-fixed";
                    regulator-name = "3P3V";
                    regulator-min-microvolt = <3300000>;
                    regulator-max-microvolt = <3300000>;
                    regulator-always-on;
            };
    
            reg_5v: regulator-5v {
                    compatible = "regulator-fixed";
                    regulator-name = "5V";
                    regulator-min-microvolt = <5000000>;
                    regulator-max-microvolt = <5000000>;
                    regulator-always-on;
            };
    
    	regulators {
    		compatible = "simple-bus";
    		#address-cells = <1>;
    		#size-cells = <0>;
    
    		wl_reg_on: wlreg_on {
    			compatible = "regulator-fixed";
    			pinctrl-names = "default";
    			pinctrl-0 = <&pinctrl_wifi_ctrl>;
    			regulator-name = "WL_REG_ON";
    			regulator-min-microvolt = <3300000>;
    			regulator-max-microvolt = <3300000>;
    			gpio = <&gpio1 11 GPIO_ACTIVE_HIGH>;
    			off-on-delay = <20000>;
    			startup-delay-us = <100>;
    			enable-active-high;
    		};
    	};
    };
    
    &A53_0 {
    	cpu-supply = <&buck2_reg>;
    };
    
    &fec1 {
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_fec1>;
    	phy-mode = "rgmii-id";
    	phy-handle = <&ethphy0>;
    	fsl,magic-packet;
    	status = "okay";
    
    	mdio {
    		#address-cells = <1>;
    		#size-cells = <0>;
    
    		ethphy0: ethernet-phy@0 {
    			compatible = "ethernet-phy-ieee802.3-c22";
    			reg = <1>;
    			at803x,led-act-blind-workaround;
    			at803x,eee-disabled;
    			rtl821x,aldps-disable;
    			rtl821x,clkout-disable;
    		};
    	};
    };
    
    &pcie0{
    	clocks = <&clk IMX8MM_CLK_PCIE1_ROOT>,
    		 <&clk IMX8MM_CLK_PCIE1_AUX>,
    		 <&clk IMX8MM_CLK_PCIE1_PHY>,
    		 <&pcie0_refclk>;
    	clock-names = "pcie", "pcie_aux", "pcie_phy", "pcie_bus";
    	ext_osc = <0>;
    	reserved-region = <&rpmsg_reserved>;
    	status = "disabled";
    };
    &sai3 {
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_sai3>;
    	assigned-clocks = <&clk IMX8MM_CLK_SAI3>;
    	assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>;
    	assigned-clock-rates = <24576000>;
    	status = "disabled";
    };
    
    &sai2 {
    	#sound-dai-cells = <0>;
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_sai2>;
    	/*assigned-clocks = <&clk IMX8MM_CLK_SAI2>;
    	assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>;
    	//assigned-clock-rates = <24576000>;
    	assigned-clock-rates = <24000000>;*/
            clocks = <&clk IMX8MM_CLK_SAI2_IPG>,
                                             <&clk IMX8MM_CLK_DUMMY>,
                                             <&clk IMX8MM_CLK_SAI2_ROOT>,
                                             <&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_DUMMY>;
            clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3";
    	status = "okay";
    };
    
    &sai1 {
    	pinctrl-names = "default", "dsd";
    	pinctrl-0 = <&pinctrl_sai1>;
    	assigned-clocks = <&clk IMX8MM_CLK_SAI1>;
    	assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>;
    	assigned-clock-rates = <49152000>;
    	clocks = <&clk IMX8MM_CLK_SAI1_IPG>, <&clk IMX8MM_CLK_DUMMY>,
    		<&clk IMX8MM_CLK_SAI1_ROOT>, <&clk IMX8MM_CLK_DUMMY>,
    		<&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_AUDIO_PLL1_OUT>,
    		<&clk IMX8MM_AUDIO_PLL2_OUT>;
    	clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3", "pll8k", "pll11k";
    	status = "disabled";
    };
    
    &uart1 { /* BT */
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_uart1>;
    	assigned-clocks = <&clk IMX8MM_CLK_UART1>;
    	assigned-clock-parents = <&clk IMX8MM_SYS_PLL1_80M>;
    	fsl,uart-has-rtscts;
    	resets = <&bt_reg_on>;
    	status = "okay";
    };
    
    &uart2 { /* console */
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_uart2>;
    	status = "okay";
    };
    
    &uart3 {
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_uart3>;
    	assigned-clocks = <&clk IMX8MM_CLK_UART3>;
    	assigned-clock-parents = <&clk IMX8MM_SYS_PLL1_80M>;
    	fsl,uart-has-rtscts;
    	status = "okay";
    };
    
    &usbotg1 {
    	dr_mode = "otg";
    	hnp-disable;
    	srp-disable;
    	adp-disable;
    	picophy,pre-emp-curr-control = <3>;
    	picophy,dc-vol-level-adjust = <7>;
    	status = "okay";
    };
    
    &usbotg2 {
    	dr_mode = "host";
    	picophy,pre-emp-curr-control = <3>;
    	picophy,dc-vol-level-adjust = <7>;
    	status = "okay";
    };
    
    /* WIFI SDIO */
    &usdhc1 {
    	pinctrl-names = "default", "state_100mhz", "state_200mhz";
    	pinctrl-0 = <&pinctrl_usdhc1>;
    	pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
    	pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
    	bus-width = <4>;
    	no-1-8-v;
    	vmmc-supply = <&wl_reg_on>;
    	pm-ignore-notify;
    	keep-power-in-suspend;
    	non-removable;
    	status = "okay";
    };
    
    /* SD card on CLIX connector */
    &usdhc2 {
    	pinctrl-names = "default", "state_100mhz", "state_200mhz";
    	pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>;
    	pinctrl-1 = <&pinctrl_usdhc2_100mhz>, <&pinctrl_usdhc2_gpio>;
    	pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>;
    	cd-gpios = <&gpio2 12 GPIO_ACTIVE_LOW>;
    	bus-width = <4>;
    	status = "okay";
    };
    
    /* eMMC on SOM */
    &usdhc3 {
    	pinctrl-names = "default", "state_100mhz", "state_200mhz";
    	pinctrl-0 = <&pinctrl_usdhc3>;
    	pinctrl-1 = <&pinctrl_usdhc3_100mhz>;
    	pinctrl-2 = <&pinctrl_usdhc3_200mhz>;
    	bus-width = <8>;
    	non-removable;
    	status = "okay";
    };
    
    &wdog1 {
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_wdog>;
    	fsl,ext-reset-output;
    	status = "okay";
    };
    
    &ecspi2 {
    	#address-cells = <1>;
    	#size-cells = <0>;
    	num-cs = <1>;
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_ecspi2 &pinctrl_ecspi2_cs>;
    	cs-gpios = <&gpio5 13 GPIO_ACTIVE_LOW>;
    	status = "disabled";
    };
    
    &i2c1 {
    	clock-frequency = <400000>;
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_i2c1>;
    	status = "okay";
    
    	pmic@4b {
    		compatible = "rohm,bd71847";
    		reg = <0x4b>;
    		pinctrl-0 = <&pinctrl_pmic>;
    		interrupt-parent = <&gpio1>;
    		interrupts = <3 GPIO_ACTIVE_LOW>;
    		rohm,reset-snvs-powered;
    
    		regulators {
    			buck1_reg: BUCK1 {
    				regulator-name = "BUCK1";
    				regulator-min-microvolt = <700000>;
    				regulator-max-microvolt = <1300000>;
    				regulator-boot-on;
    				regulator-always-on;
    				regulator-ramp-delay = <1250>;
    			};
    
    			buck2_reg: BUCK2 {
    				regulator-name = "BUCK2";
    				regulator-min-microvolt = <700000>;
    				regulator-max-microvolt = <1300000>;
    				regulator-boot-on;
    				regulator-always-on;
    				regulator-ramp-delay = <1250>;
    				rohm,dvs-run-voltage = <1000000>;
    				rohm,dvs-idle-voltage = <900000>;
    			};
    
    			buck3_reg: BUCK3 {
    				// BUCK5 in datasheet
    				regulator-name = "BUCK3";
    				regulator-min-microvolt = <700000>;
    				regulator-max-microvolt = <1350000>;
    				regulator-boot-on;
    				regulator-always-on;
    			};
    
    			buck4_reg: BUCK4 {
    				// BUCK6 in datasheet
    				regulator-name = "BUCK4";
    				regulator-min-microvolt = <3000000>;
    				regulator-max-microvolt = <3300000>;
    				regulator-boot-on;
    				regulator-always-on;
    			};
    
    			buck5_reg: BUCK5 {
    				// BUCK7 in datasheet
    				regulator-name = "BUCK5";
    				regulator-min-microvolt = <1605000>;
    				regulator-max-microvolt = <1995000>;
    				regulator-boot-on;
    				regulator-always-on;
    			};
    
    			buck6_reg: BUCK6 {
    				// BUCK8 in datasheet
    				regulator-name = "BUCK6";
    				regulator-min-microvolt = <800000>;
    				regulator-max-microvolt = <1400000>;
    				regulator-boot-on;
    				regulator-always-on;
    			};
    
    			ldo1_reg: LDO1 {
    				regulator-name = "LDO1";
    				regulator-min-microvolt = <1600000>;
    				regulator-max-microvolt = <1900000>;
    				regulator-boot-on;
    				regulator-always-on;
    			};
    
    			ldo2_reg: LDO2 {
    				regulator-name = "LDO2";
    				regulator-min-microvolt = <800000>;
    				regulator-max-microvolt = <900000>;
    				regulator-boot-on;
    				regulator-always-on;
    			};
    
    			ldo3_reg: LDO3 {
    				regulator-name = "LDO3";
    				regulator-min-microvolt = <1800000>;
    				regulator-max-microvolt = <3300000>;
    				regulator-boot-on;
    				regulator-always-on;
    			};
    
    			ldo4_reg: LDO4 {
    				regulator-name = "LDO4";
    				regulator-min-microvolt = <900000>;
    				regulator-max-microvolt = <1800000>;
    				regulator-boot-on;
    				regulator-always-on;
    			};
    
    			ldo6_reg: LDO6 {
    				regulator-name = "LDO6";
    				regulator-min-microvolt = <900000>;
    				regulator-max-microvolt = <1800000>;
    				regulator-boot-on;
    				regulator-always-on;
    			};
    		};
    	};
    };
    
    &i2c2 {
    	clock-frequency = <400000>;
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_i2c2>;
    	status = "okay";
    tlv320aic31xx:codec@18 {
                    compatible = "ti,tlv320aic3100";
                    reg = <0x18>;
                    #sound-dai-cells = <0>;
                    /* Regulators */
                    AVDD-supply = <&reg_3p3v>;
                    HPVDD-supply = <&reg_3p3v>;
                    SPRVDD-supply = <&reg_5v>;
                    SPLVDD-supply = <&reg_5v>;
                    DVDD-supply = <&reg_1p8v>;
                    IOVDD-supply = <&reg_1p8v>;
                    /*clocks = <&clk IMX8MM_CLK_SAI2_ROOT>;
                    clock-names = "mclk";
                    assigned-clocks = <&clk IMX8MM_CLK_SAI2_ROOT>;
                    assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>;
                    assigned-clock-rates = <24000000>;*/
                    ai31xx-micbias-vg = <1>;
            };
    
    };
    
    &i2c3 {
    	clock-frequency = <100000>;
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_i2c3>;
    	status = "okay";
    };
    
    &snvs_rtc {
    	status = "disabled";
    };
    
    &iomuxc {
    	pinctrl-names = "default";
    
    	pinctrl_fec1: fec1grp {
    		fsl,pins = <
    			MX8MM_IOMUXC_ENET_MDC_ENET1_MDC			0x3
    			MX8MM_IOMUXC_ENET_MDIO_ENET1_MDIO		0x3
    			MX8MM_IOMUXC_ENET_TD3_ENET1_RGMII_TD3		0x1f
    			MX8MM_IOMUXC_ENET_TD2_ENET1_RGMII_TD2		0x1f
    			MX8MM_IOMUXC_ENET_TD1_ENET1_RGMII_TD1		0x1f
    			MX8MM_IOMUXC_ENET_TD0_ENET1_RGMII_TD0		0x1f
    			MX8MM_IOMUXC_ENET_RD3_ENET1_RGMII_RD3		0x91
    			MX8MM_IOMUXC_ENET_RD2_ENET1_RGMII_RD2		0x91
    			MX8MM_IOMUXC_ENET_RD1_ENET1_RGMII_RD1		0x91
    			MX8MM_IOMUXC_ENET_RD0_ENET1_RGMII_RD0		0x91
    			MX8MM_IOMUXC_ENET_TXC_ENET1_RGMII_TXC		0x1f
    			MX8MM_IOMUXC_ENET_RXC_ENET1_RGMII_RXC		0x91
    			MX8MM_IOMUXC_ENET_RX_CTL_ENET1_RGMII_RX_CTL	0x91
    			MX8MM_IOMUXC_ENET_TX_CTL_ENET1_RGMII_TX_CTL	0x1f
    			MX8MM_IOMUXC_SD1_RESET_B_GPIO2_IO10	0x36	/* ENET_nRST */
    		>;
    	};
    
    	pinctrl_i2c1: i2c1grp {
    		fsl,pins = <
    			MX8MM_IOMUXC_I2C1_SCL_I2C1_SCL			0x400001c3
    			MX8MM_IOMUXC_I2C1_SDA_I2C1_SDA			0x400001c3
    		>;
    	};
    
    	pinctrl_i2c2: i2c2grp {
    		fsl,pins = <
    			MX8MM_IOMUXC_I2C2_SCL_I2C2_SCL			0x400001c3
    			MX8MM_IOMUXC_I2C2_SDA_I2C2_SDA			0x400001c3
    		>;
    	};
    
    	pinctrl_i2c3: i2c3grp {
    		fsl,pins = <
    			MX8MM_IOMUXC_I2C3_SCL_I2C3_SCL			0x400001c3
    			MX8MM_IOMUXC_I2C3_SDA_I2C3_SDA			0x400001c3
    		>;
    	};
    
    	pinctrl_pmic: pmicirq {
    		fsl,pins = <
    			MX8MM_IOMUXC_GPIO1_IO03_GPIO1_IO3		0x41
    		>;
    	};
    
    	pinctrl_sai2: sai2grp {
    		fsl,pins = <
    			MX8MM_IOMUXC_SAI2_TXFS_SAI2_TX_SYNC	0xd6
    			MX8MM_IOMUXC_SAI2_TXC_SAI2_TX_BCLK	0xd6
    			MX8MM_IOMUXC_SAI2_MCLK_SAI2_MCLK	0xd6
    			MX8MM_IOMUXC_SAI2_TXD0_SAI2_TX_DATA0	0xd6
    			MX8MM_IOMUXC_SAI2_RXD0_SAI2_RX_DATA0	0xd6
    		>;
    	};
    
    	pinctrl_sai3: sai3grp {
    		fsl,pins = <
    			MX8MM_IOMUXC_SAI3_TXFS_SAI3_TX_SYNC	0xd6
    			MX8MM_IOMUXC_SAI3_TXC_SAI3_TX_BCLK	0xd6
    			MX8MM_IOMUXC_SAI3_TXD_SAI3_TX_DATA0	0xd6
    			MX8MM_IOMUXC_SAI3_RXD_SAI3_RX_DATA0	0xd6
    		>;
    	};
    
    	/* Bluetooth audio */
    	pinctrl_sai1: sai1grp {
    		fsl,pins = <
    			MX8MM_IOMUXC_SAI1_TXFS_SAI1_TX_SYNC	0xd6
    			MX8MM_IOMUXC_SAI1_TXC_SAI1_TX_BCLK	0xd6
    			MX8MM_IOMUXC_SAI1_TXD0_SAI1_TX_DATA0	0xd6
    			MX8MM_IOMUXC_SAI1_RXD0_SAI5_RX_DATA0    0xd6
    		>;
    	};
    
    	pinctrl_uart1: uart1grp {
    		fsl,pins = <
    			MX8MM_IOMUXC_UART1_RXD_UART1_DCE_RX	0x140
    			MX8MM_IOMUXC_UART1_TXD_UART1_DCE_TX	0x140
    			MX8MM_IOMUXC_UART3_RXD_UART1_DCE_CTS_B	0x140
    			MX8MM_IOMUXC_UART3_TXD_UART1_DCE_RTS_B	0x140
    		>;
    	};
    
    	pinctrl_uart2: uart2grp {
    		fsl,pins = <
    			MX8MM_IOMUXC_UART2_RXD_UART2_DCE_RX	0x140
    			MX8MM_IOMUXC_UART2_TXD_UART2_DCE_TX	0x140
    		>;
    	};
    
    	pinctrl_uart3: uart3grp {
    		fsl,pins = <
    			MX8MM_IOMUXC_ECSPI1_SCLK_UART3_DCE_RX		0x140
    			MX8MM_IOMUXC_ECSPI1_MOSI_UART3_DCE_TX		0x140
    			MX8MM_IOMUXC_ECSPI1_SS0_UART3_DCE_RTS_B		0x140
    			MX8MM_IOMUXC_ECSPI1_MISO_UART3_DCE_CTS_B	0x140
    		>;
    	};
    
    	pinctrl_usdhc1: usdhc1grp {
    		fsl,pins = <
    			MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK		0x190
    			MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD		0x1d0
    			MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0	0x1d0
    			MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1	0x1d0
    			MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2	0x1d0
    			MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3	0x1d0
    		>;
    	};
    
    	pinctrl_usdhc1_100mhz: usdhc1grp100mhz {
    		fsl,pins = <
    			MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK		0x194
    			MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD		0x1d4
    			MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0	0x1d4
    			MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1	0x1d4
    			MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2	0x1d4
    			MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3	0x1d4
    		>;
    	};
    
    	pinctrl_usdhc1_200mhz: usdhc1grp200mhz {
    		fsl,pins = <
    			MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK		0x196
    			MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD		0x1d6
    			MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0	0x1d6
    			MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1	0x1d6
    			MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2	0x1d6
    			MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3	0x1d6
    		>;
    	};
    
    	pinctrl_usdhc2_gpio: usdhc2grpgpio {
    		fsl,pins = <
    			MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12	0x1c4
    		>;
    	};
    
    	pinctrl_usdhc2: usdhc2grp {
    		fsl,pins = <
    			MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK		0x190
    			MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD		0x1d0
    			MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0	0x1d0
    			MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1	0x1d0
    			MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2	0x1d0
    			MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3	0x1d0
    			MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT	0x1d0
    		>;
    	};
    
    	pinctrl_usdhc2_100mhz: usdhc2grp100mhz {
    		fsl,pins = <
    			MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK		0x194
    			MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD		0x1d4
    			MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0	0x1d4
    			MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1	0x1d4
    			MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2	0x1d4
    			MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3	0x1d4
    			MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT	0x1d0
    		>;
    	};
    
    	pinctrl_usdhc2_200mhz: usdhc2grp200mhz {
    		fsl,pins = <
    			MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK		0x196
    			MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD		0x1d6
    			MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0	0x1d6
    			MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1	0x1d6
    			MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2	0x1d6
    			MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3	0x1d6
    			MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT	0x1d0
    		>;
    	};
    
    	pinctrl_usdhc3: usdhc3grp {
    		fsl,pins = <
    			MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK		0x190
    			MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD		0x1d0
    			MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0		0x1d0
    			MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1		0x1d0
    			MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2		0x1d0
    			MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3		0x1d0
    			MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4		0x1d0
    			MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5		0x1d0
    			MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6		0x1d0
    			MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7		0x1d0
    			MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 		0x190
    		>;
    	};
    
    	pinctrl_usdhc3_100mhz: usdhc3grp100mhz {
    		fsl,pins = <
    			MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK		0x194
    			MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD		0x1d4
    			MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0		0x1d4
    			MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1		0x1d4
    			MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2		0x1d4
    			MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3		0x1d4
    			MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4		0x1d4
    			MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5		0x1d4
    			MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6		0x1d4
    			MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7		0x1d4
    			MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 		0x194
    		>;
    	};
    
    	pinctrl_usdhc3_200mhz: usdhc3grp200mhz {
    		fsl,pins = <
    			MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK		0x196
    			MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD		0x1d6
    			MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0		0x1d6
    			MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1		0x1d6
    			MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2		0x1d6
    			MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3		0x1d6
    			MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4		0x1d6
    			MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5		0x1d6
    			MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6		0x1d6
    			MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7		0x1d6
    			MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 		0x196
    		>;
    	};
    
    	pinctrl_wdog: wdoggrp {
    		fsl,pins = <
    			MX8MM_IOMUXC_GPIO1_IO02_WDOG1_WDOG_B	0xc6
    		>;
    	};
    
    	pinctrl_wifi_ctrl: wifi_ctrlgrp {
    		fsl,pins = <
    			MX8MM_IOMUXC_GPIO1_IO11_GPIO1_IO11	0x41 /* WL_REG_ON */
    		>;
    	};
    
    	pinctrl_bt_ctrl: bt_ctrlgrp {
    		fsl,pins = <
    			MX8MM_IOMUXC_GPIO1_IO12_GPIO1_IO12	0x41 /* BT_REG_ON */
    		>;
    	};
    
    	pinctrl_ecspi2: ecspi2grp {
    		fsl,pins = <
    			MX8MM_IOMUXC_ECSPI2_SCLK_ECSPI2_SCLK	0x82
    			MX8MM_IOMUXC_ECSPI2_MOSI_ECSPI2_MOSI	0x82
    			MX8MM_IOMUXC_ECSPI2_MISO_ECSPI2_MISO	0x82
    		>;
    	};
    
    	pinctrl_ecspi2_cs: ecspi2cs {
    		fsl,pins = <
    			MX8MM_IOMUXC_ECSPI2_SS0_GPIO5_IO13	0x16
    		>;
    	};
    };
    
    &lcdif {
    	status = "okay";
    };
    
    &vpu_g1 {
    	status = "okay";
    };
    
    &vpu_g2 {
    	status = "okay";
    };
    
    &vpu_h1 {
    	status = "okay";
    };
    
    &gpu {
    	status = "okay";
    };
    

    Please support on this issue.

  • Hi Kumar

    Per the devicetree, aic3100 is the slave, the clks(bit clock and frame clock) are generated by the sai2, which is the platform module, you may as well consult the platform vendor why the clk is wrong?

  • Hi,

    playing the file 44100Khz file with addied those changes.

    Now the measurement of clock

    1. MCLK = 24mhz (Can you please tell the codec will support external clock upto 24mhz)

    2. BCLK = 1.5mhz

    3. Frame clock = 46.9khz

    This will come 44100khz right, but why the 46.9kkhz was generating?

    Added the pll generated, Can you please tell what is PLL_OUTPUT_CLOCK, how this will lbe probe or configured in device tree?

    pico-codec-pll.xlsx

  • Hi Kumar

    As you know, both bclk and frame clk are generated by the sai2, not aic3100, you may as well consult platform vendor.

    For aic3100 in slave mode, once the bclk and frame clk can output the right clock, aic3100 will work well.

  • Thanks for your input. Now the clock was generated successfully and the frame clock is coming 44 khz as expected. Issue in the SAI clock configurations.

    The audio was not coming in the speaker, Can you please tell anything need to be configured in the attached above patches.

  • Kindly upload the ws clk and bit clk waveform measured by scope and offer the latest log. Thanks.

  • Sorry to shared the waveform but we measured the bit clock (1.5mhz) and WS clock (44.1 khz).

    Below the logs

    dmesg:

    [ 4.956073] fsl-jr-uio 30901000.jr: UIO device full name fsl-jr0 initialized
    [ 4.964432] aic31xx->dev,## aic31xx_codec_probe
    [ 4.969753] ## aic31xx_set_dai_sysclk: clk_id = 0, freq = 24000000, dir = 0
    [ 4.976814]  aic31xx_set_dai_sysclk: frequency 24000000
    [ 4.983069] ## aic31xx_set_dai_fmt: fmt = 0x4001
    [ 4.992175] ## aic31xx_set_bias_level: 0 -> 1
    [ 4.999358] tlv320aic31xx-codec 1-0018: Could not reset device: -6
    [ 5.009899] tlv320aic31xx-codec 1-0018: Unable to sync registers 0x9f-0x9f. -6
    [ 5.023365] Failed to restore cache: -6
    [ 5.782944] tlv320aic31xx-codec 1-0018: ASoC: error at snd_soc_component_update_bits on tlv320aic31xx-codec.1-0018: -16
    [ 5.783088] tlv320aic31xx-codec 1-0018: ASoC: error at snd_soc_component_update_bits on tlv320aic31xx-codec.1-0018: -16
    [ 5.952897] tlv320aic31xx-codec 1-0018: ASoC: error at snd_soc_component_update_bits on tlv320aic31xx-codec.1-0018: -16
    [ 5.953078] tlv320aic31xx-codec 1-0018: ASoC: error at snd_soc_component_update_bits on tlv320aic31xx-codec.1-0018: -16



    root@pico-imx8mm:~# aplay 44100_16_st.wav
    [ 79.242407] ## aic31xx_hw_params: width 16 rate 44100
    [ 79.247490] sysclk 24000000 p_div 2
    [ 79.251477] mclk_p 24000000 frame size 32
    [ 79.255954] pll 7.5264/2 dosr 128 n 4 m 4 aosr 128 n 4 m 4 bclk_n 16
    [ 79.262337]  rate = 44100 sai->bclk_ratio = 0
    [ 79.267400]  slots = 2 slot_width = 16
    [ 79.271946] 1. ratio 0 for freq 1411200Hz based on clock 0Hz
    [ 79.278222] 1. verid major 3minor 1
    [ 79.282417] 2.  ratio 16 for freq 1411200Hz based on clock 22579200Hz
    [ 79.289225] 3. ret 0 savesub 1411200
    [ 79.293251] storing ratio
    [ 79.296055] continue 4
    Playing WAVE '44100_16_st.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
    root@pico-imx8mm:~#

  • Pls answer my following questions

    Waveform is necessary for this issue, we will check whether there occured jitters in the clk.

    From the log, could you tell me what sai->bclk_ratio = 0 means?

    From the log, what's wrong with the i2c during probe, pls fix it?

    [ 4.999358] tlv320aic31xx-codec 1-0018: Could not reset device: -6
    [ 5.009899] tlv320aic31xx-codec 1-0018: Unable to sync registers 0x9f-0x9f. -6
    [ 5.023365] Failed to restore cache: -6
    [ 5.782944] tlv320aic31xx-codec 1-0018: ASoC: error at snd_soc_component_update_bits on tlv320aic31xx-codec.1-0018: -16
    [ 5.783088] tlv320aic31xx-codec 1-0018: ASoC: error at snd_soc_component_update_bits on tlv320aic31xx-codec.1-0018: -16
    [ 5.952897] tlv320aic31xx-codec 1-0018: ASoC: error at snd_soc_component_update_bits on tlv320aic31xx-codec.1-0018: -16
    [ 5.953078] tlv320aic31xx-codec 1-0018: ASoC: error at snd_soc_component_update_bits on tlv320aic31xx-codec.1-0018: -16

  • Hi,

    Above logs are not coming now after reset the module given by the gpio pin.

    root@pico-imx8mm:~# aplay pcm1644m.wav
    [ 947.498999] ## aic31xx_hw_params: width 16 rate 44100
    [ 947.504082] Zumi sysclk 24000000 p_div 2
    [ 947.508056] Zumi mclk_p 12000000 frame size 32
    [ 947.931576] aic31xx_setup_pll : Selected Sample rate (44100) and format supported
    [ 947.943066] pll 7.5264/2 dosr 128 n 8 m 2 aosr 128 n 8 m 2 bclk_n 8
    [ 947.949358] rate = 44100 sai->bclk_ratio = 0 /****** Printed the structure member sai->bclk_ratio *********/
    [ 947.954452] slots = 2 slot_width = 16
    [ 947.959011] Zumi ratio 0 for freq 1411200Hz based on clock 0Hz
    [ 947.965125] Zumi verid major 3minor 1
    [ 947.969156] clk rate = 22579200 ret = 0 ratio = 16 div = 1
    [ 947.974785] ratio 16 for freq 1411200Hz based on clock 22579200Hz
    [ 947.981588] ret 0 savesub 1411200
    [ 947.985607] storing ratio
    [ 947.988409] continue 4
    Playing WAVE 'pcm1644m.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Mono
    root@pico-imx8mm:~#

    Issue: Connected Speaker and microphone, but still the audio was not coming.

    Measured Wave forms

    BCLK - 1.5 mhz

    WCLK 44.1KHZ

    MCLK 24Mhz

    Below screenshot show the Speaker connection from the codec

    Before playing the audio:

    No size specified (using byte-data access)
           0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
    00: 00 00 01 66 00 11 04 00 00 00 00 01 01 00 80 80 ..?f.??....??.??
    10: 08 00 01 01 80 80 04 00 00 00 01 00 00 00 01 00 ?.?????...?...?.
    20: 00 00 00 00 80 00 00 00 00 00 00 00 00 00 00 00 ....?...........
    30: 00 00 00 02 32 12 03 02 02 11 10 00 01 04 00 14 ...?2??????.??.?
    40: 0c d8 d8 00 6f 38 00 00 00 00 00 ee 10 d8 7e e3 ???.o8.....???~?
    50: 00 00 80 00 00 00 00 00 7f 00 00 00 00 00 00 00 ..?.....?.......
    60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    root@pico-imx8mm:~#

    After playing the audio

    root@pico-imx8mm:~# i2cdump -f -y 1 0x18
    No size specified (using byte-data access)
          0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
    00: 00 00 01 66 03 21 07 14 90 00 00 08 02 00 80 80 ..?f?!???..??.??
    10: 08 00 08 02 80 80 04 00 00 00 01 00 00 00 08 00 ?.?????...?...?.
    20: 00 00 00 00 80 00 00 00 00 00 00 00 00 00 00 00 ....?...........
    30: 00 00 00 02 32 12 03 02 02 11 10 00 01 04 00 14 ...?2??????.??.?
    40: 0c d8 d8 00 6f 38 00 00 00 00 00 ee 10 d8 7e e3 ???.o8.....???~?
    50: 00 00 80 00 00 00 00 00 7f 00 00 00 00 00 00 00 ..?.....?.......
    60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    root@pico-imx8mm:~#

    Please answer us the below questions,

    1. Can you please review the schematics and the codec MCLK is feed by external oscillator?


    2. Referred the TI document "Audio Serial Interface Configurations for Audio Devices" Our connection is similar to the figure 3 document.

    Can you please review the point 1 & 2 and give me the feedback the TLV320AIC3100 codec support external MCLK? 

    If the codec doesn't support the external MCLK, then if possible the MCLK can be generated by BCLK?

    Regards,
    SanthanaKumarS

  • I have assign the review task to our hardware guy.

    • What I want to know is why the bitclk is 1.5 not 1.4112MHz? For 44100 sample rate, the bitclk must be 1.4112MHz
  • Sorry. Bit clock is coming as 1.4112 mhz only. 

  • 1. Your registers above show none of the DAC channels are powered and PLL is not enabled.

    See notes below, please revisit your register settings.

    Before playing the audio:
    
        0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  Comment
    00: 00 00 01 66 00 11 04 00 00 00 00 01 01 00 80 80 PLLCLK=MCLK, CODEC_CLKIN=MCLK, PLL is power down, P=1,R=1, J=4, D=0, NDAC=1 power down, MDAC=1 power down, DOSR=128
    10: 08 00 01 01 80 80 04 00 00 00 01 00 00 00 01 00 NADC=1 power down, MADC=1 power down, AOSR-128, 16-bit,I2S, target mode
    20: 00 00 00 00 80 00 00 00 00 00 00 00 00 00 00 00 
    30: 00 00 00 02 32 12 03 02 02 11 10 00 01 04 00 14 both Left and right DAC powered down
    40: 0c d8 d8 00 6f 38 00 00 00 00 00 ee 10 d8 7e e3 both Left and right DAC muted
    50: 00 00 80 00 00 00 00 00 7f 00 00 00 00 00 00 00 
    60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    
    
    After playing the audio:
    
        0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  Comment
    00: 00 00 01 66 03 21 07 14 90 00 00 08 02 00 80 80 PLLCLK=MCLK, CODEC_CLKIN=PLLCLK, PLL is power down, P=2,R=1, J=7, D=5264, NDAC=8 power down, MDAC=2 power down, DOSR=128
    10: 08 00 08 02 80 80 04 00 00 00 01 00 00 00 08 00 NADC=8 power down, MADC=2 power down, AOSR-128, 16-bit,I2S, target mode
    20: 00 00 00 00 80 00 00 00 00 00 00 00 00 00 00 00 
    30: 00 00 00 02 32 12 03 02 02 11 10 00 01 04 00 14 both Left and right DAC powered down
    40: 0c d8 d8 00 6f 38 00 00 00 00 00 ee 10 d8 7e e3 both Left and right DAC muted
    50: 00 00 80 00 00 00 00 00 7f 00 00 00 00 00 00 00 
    60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

    2. Using Osc is not recommended as mentioned in the apps. note. You can use BCLK, but make sure the respective clock is meeting the criteria in the table below from the clock tree.

    Regards.

  • Can you please tell how can we use bclk instead of mclk please tell the following steps. 

  • Hi,

    Based on your comments, we did some work around below it was worked now.

    Before playing the audio, running the below command

    Below configurations are selected in alsamixer

    root@pico-imx8mm:~# amixer -c 0
    Simple mixer control 'Speaker',0
    Capabilities: pswitch pswitch-joined
    Playback channels: Mono
    Mono: Playback [off]
    Simple mixer control 'Speaker Analog',0
    Capabilities: pvolume pvolume-joined
    Playback channels: Mono
    Limits: Playback 0 - 127
    Mono: Playback 0 [0%] [-63.50dB]
    Simple mixer control 'Speaker Driver',0
    Capabilities: pvolume pvolume-joined pswitch pswitch-joined
    Playback channels: Mono
    Limits: Playback 0 - 3
    Mono: Playback 0 [0%] [6.00dB] [off]
    Simple mixer control 'Mic PGA',0
    Capabilities: cvolume cvolume-joined
    Capture channels: Mono
    Limits: Capture 0 - 119
    Mono: Capture 0 [0%] [0.00dB]
    Simple mixer control 'ADC',0
    Capabilities: cvolume cvolume-joined cswitch cswitch-joined
    Capture channels: Mono
    Limits: Capture 0 - 64
    Mono: Capture 24 [38%] [-8.00dB] [off]
    Simple mixer control 'ADC Fine',0
    Capabilities: cvolume cvolume-joined
    Capture channels: Mono
    Limits: Capture 0 - 4
    Mono: Capture 4 [100%] [0.40dB]
    Simple mixer control 'DAC',0
    Capabilities: pvolume
    Playback channels: Front Left - Front Right
    Limits: Playback 0 - 175
    Mono:
    Front Left: Playback 87 [50%] [-20.00dB]
    Front Right: Playback 87 [50%] [-20.00dB]
    Simple mixer control 'DAC Left Input',0
    Capabilities: enum
    Items: 'Off' 'Left Data' 'Right Data' 'Mono'
    Item0: 'Left Data'
    Simple mixer control 'DAC Right Input',0
    Capabilities: enum
    Items: 'Off' 'Right Data' 'Left Data' 'Mono'
    Item0: 'Right Data'
    Simple mixer control 'HP Analog',0
    Capabilities: pvolume
    Playback channels: Front Left - Front Right
    Limits: Playback 0 - 127
    Mono:
    Front Left: Playback 0 [0%] [-63.50dB]
    Front Right: Playback 0 [0%] [-63.50dB]
    Simple mixer control 'HP Driver',0
    Capabilities: pvolume pswitch
    Playback channels: Front Left - Front Right
    Limits: Playback 0 - 9
    Mono:
    Front Left: Playback 0 [0%] [0.00dB] [off]
    Front Right: Playback 0 [0%] [0.00dB] [off]
    Simple mixer control 'HP Left',0
    Capabilities: pswitch pswitch-joined
    Playback channels: Mono
    Mono: Playback [off]
    Simple mixer control 'HP Output Driver Power-On time',0
    Capabilities: enum
    Items: '0us' '15.3us' '153us' '1.53ms' '15.3ms' '76.2ms' '153ms' '304ms' '610ms' '1.22s' '3.04s' '6.1s'
    Item0: '304ms'
    Simple mixer control 'HP Output Driver Ramp-up step',0
    Capabilities: enum
    Items: '0ms' '0.98ms' '1.95ms' '3.9ms'
    Item0: '3.9ms'
    Simple mixer control 'HP Right',0
    Capabilities: pswitch pswitch-joined
    Playback channels: Mono
    Mono: Playback [off]
    Simple mixer control 'MIC1LM M-Terminal',0
    Capabilities: enum
    Items: 'Off' 'FFR 10 Ohm' 'FFR 20 Ohm' 'FFR 40 Ohm'
    Item0: 'Off'
    Simple mixer control 'MIC1LM P-Terminal',0
    Capabilities: enum
    Items: 'Off' 'FFR 10 Ohm' 'FFR 20 Ohm' 'FFR 40 Ohm'
    Item0: 'Off'
    Simple mixer control 'MIC1LP P-Terminal',0
    Capabilities: enum
    Items: 'Off' 'FFR 10 Ohm' 'FFR 20 Ohm' 'FFR 40 Ohm'
    Item0: 'Off'
    Simple mixer control 'MIC1RP P-Terminal',0
    Capabilities: enum
    Items: 'Off' 'FFR 10 Ohm' 'FFR 20 Ohm' 'FFR 40 Ohm'
    Item0: 'Off'
    Simple mixer control 'Output Left From Left DAC',0
    Capabilities: pswitch pswitch-joined
    Playback channels: Mono
    Mono: Playback [off]
    Simple mixer control 'Output Left From MIC1LP',0
    Capabilities: pswitch pswitch-joined
    Playback channels: Mono
    Mono: Playback [off]
    Simple mixer control 'Output Left From MIC1RP',0
    Capabilities: pswitch pswitch-joined
    Playback channels: Mono
    Mono: Playback [off]
    Simple mixer control 'Output Right From MIC1RP',0
    Capabilities: pswitch pswitch-joined
    Playback channels: Mono
    Mono: Playback [off]
    Simple mixer control 'Output Right From Right DAC',0
    Capabilities: pswitch pswitch-joined
    Playback channels: Mono
    Mono: Playback [off]
    Simple mixer control 'Volume Soft Stepping',0
    Capabilities: enum
    Items: 'fast' 'slow' 'disabled'
    Item0: 'fast'

    Note: this above configurations are not worked for playing the audio

    The below configurations are worked to playing the audio:

    1. Running the configuration

    amixer -c 0 sset "Speaker" on
    amixer -c 0 sset "Speaker Driver" on
    amixer -c 0 sset "Output Left From Left DAC" on
    amixer -c 0 sset "Output Right From Right DAC" off
    amixer -c 0 sset "Output Left From MIC1LP" on => After running the command then the PLL goes to enabled HOW????
    [ 21.631091] ## aic31xx_set_bias_level: 1 -> 2
    [ 21.635478] codec clock -> on (rate 8000)
    [ 21.654689] Zumi aic31xx_dapm_power_event
    [ 21.659724] Zumi aic31xx_wait_bits
    [ 21.663428] ## aic31xx_set_bias_level: 2 -> 3
    Simple mixer control 'Output Left From MIC1LP',0

    amixer -c 0 sset "Output Left From MIC1RP" on

    2. After running the alsamixer to change some settings in the Speaker & DAC.

    root@pico-imx8mm:~# amixer -c 0
    Simple mixer control 'Speaker',0
    Capabilities: pswitch pswitch-joined
    Playback channels: Mono
    Mono: Playback [on]
    Simple mixer control 'Speaker Analog',0
    Capabilities: pvolume pvolume-joined
    Playback channels: Mono
    Limits: Playback 0 - 127
    Mono: Playback 112 [88%] [-7.50dB]


    Simple mixer control 'DAC',0
    Capabilities: pvolume
    Playback channels: Front Left - Front Right
    Limits: Playback 0 - 175
    Mono:
    Front Left: Playback 142 [81%] [7.50dB]
    Front Right: Playback 142 [81%] [7.50dB]

    Can you please tell us those changes should be added in kernel driver?

  • Hi Kumar,

    You won't add the changes in the driver, what you do is to select them via amixer commands.

    For audio hal, you can call amixer api to select them or add them in ~/.asoundrc.

    Asoundrc - AlsaProject (alsa-project.org)

    Here is the example on aoundrc on ti audio device.

    [FAQ] PCM2906C: How to enable Linux Volume Control when Sound Device does not have capture/record control? - Audio forum - Audio - TI E2E support forums

    .

  • Hi,

         Thanks for giving a big support to us. For adding the "amixer" command, we can use a script file to configure the codec part.

  • Hi,

    Posted the issue in the below link also please reply this

    e2e.ti.com/.../4859270

    We can able to record the audio and it working as expected but there is noise coming in the recorded file. 

    Playing the audio. & attached file having some noise coming, We are suspecting the issue as the alsamixer Settings. Can you please help out the correct settings for recording the audio & don't have issue in playing the audio.

    In the wav file, You can see every 3 or 5 secs, some noise was coming, It should not come. Can you please help us to resolve the issue by amixer settings or?

    Note: Linux driver TLV320aic3100 did not handle the microphone/Speaker settings, we are doing by setting the amixer command only.

    Below the register settings loaded the driver

    No size specified (using byte-data access)
    0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
    00: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ?...............
    10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 ...............?
    20: 06 3e 00 44 00 80 07 7f 06 06 00 00 20 86 00 b2 ?>.D.?????.. ?.?
    30: 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 @...............
    40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    root@picoimx8mm:/home/ubuntu#

    Below the amixer command is used to record the audio from the headphone.

    amixer -c 0 cset name='ADC Capture Switch' on
    amixer -c 0 sset 'ADC' 40
    amixer -c 0 sset 'Mic PGA' 50
    amixer -c 0 sset "Output Left From MIC1LP" off
    amixer -c 0 sset "Output Left From MIC1RP" off
    amixer -c 0 sset "Output Right From MIC1RP" off
    amixer -c 0 sset 'MIC1LM P-Terminal' 'FFR 10 Ohm'
    amixer -c 0 sset "DAC" 0 off
    amixer -c 0 sset 'Speaker Analog' 0 off
    amixer -c 0 sset "HP Analog" 0 off
    amixer -c 0 sset "HP Left" off
    amixer -c 0 sset "HP Right" off
    amixer -c 0 sset "HP Driver" off
    amixer -c 0 sset "Output Left From Left DAC" off
    amixer -c 0 sset "Output Right From Right DAC" off

    After given the above amixer settings command, then below attacjhed registers

    root@picoimx8mm:/home/ubuntu# ./i2cdump -f -y 1 0x18
    No size specified (using byte-data access)
    0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
    00: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ?...............
    10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 ...............?
    20: 06 3e 00 00 7f 7f 7f 7f 02 02 00 00 20 86 00 b2 ?>..??????.. ?.?
    30: 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .?..............
    40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    root@picoimx8mm:/home/ubuntu#

    arecord -f S16_LE -c2 -r44100 -d20 aud_rec.wav
    Recording WAVE 'aud_rec.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo

    After recording the audio, the below register settings are

    root@picoimx8mm:/home/ubuntu# ./i2cdump -f -y 1 0x18
    No size specified (using byte-data access)
    0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
    00: 00 00 01 66 03 21 07 14 90 00 00 08 02 00 80 80 ..?f?!???..??.??
    10: 08 00 08 02 80 80 04 00 00 00 01 00 00 00 08 00 ?.?????...?...?.
    20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    30: 00 00 00 02 32 12 03 02 02 11 10 00 01 04 00 14 ...?2??????.??.?
    40: 0c 81 81 00 6f 38 00 00 00 00 00 ee 10 d8 7e e3 ???.o8.....???~?
    50: 00 00 00 1c 00 00 00 00 7f 00 00 00 00 00 00 00 ...?....?.......
    60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    root@picoimx8mm:/home/ubuntu#

    Regards,
    SanthanaKumarS

  • I'll let our Linux expert to comment on the amixer setting.

  • It seemed buffer overflow when writing audio buffer to file. How large audio buffer to write the file? You can set a much larger buffer and check whether this issue still occur.

    Kindly use scope to measure the i2s bck and ws clk, whether ws clk is larger than 44100Hz?

    One more thing, do you have AP to record the I2S data out from our codec to check this?

  • Hi,

    While using the arecord command to capture the audio, If you are not mention the buffer size, then it should be set to maximum buffer size.

    Below the arecord command follows:

    arecord -f S16_LE -c2 -r44100 -d5 test7.wav


    " --buffer-size=#Buffer duration is # frames If no buffer time and no buffer size is given then the maximal allowed buffer time but not more than 500ms is set."

    We don't have AP to record the I2S data from the codec.

    Regards,
    SanthanaKumarS

  • You can close this thread.