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.
Hello,
I have custom carrier board for Jetson Nano (kernel 4.9.253) with TAS2110 audio amplifier. Firstly I made test manually - play I2S sound and configure via I2C using script, so hardware is fine. I want to implement driver for this IC, so I use driver from your website: https://git.ti.com/cgit/lpaa-android-drivers/tas2505-linux-driver/. I know that it is driver for TAS2563, but I read that it is replacement for TAS2110.
I work with Nvidia support during added this driver to Kernel (https://forums.developer.nvidia.com/t/tas2557-tas2563-implement-driver-from-kernel-source/218181). They said that on Kernel side everything looks fine and probably fault is in driver source.
Can you said me if this driver will work with TAS2110 amplifier? If not, can you said what I must change or can you send other driver?
Best regards,
Mateusz
Hi Mateus,
Not sure if you linked the correct driver, as that one is for TAS2505 but then you mention TAS2563, please confirm.
You may try with either of these:
Best regards,
-Ivan Salazar
Applications Engineer
Hi Mateusz
Kindly refer to this thread TAS2110: TAS2110 Linux Driver - Audio forum - Audio - TI E2E support forums
Both tas2557 and tas2563 are dsp-inside chip which need firmware. I think tas2562 or tas2558 driver code is more suitable for tas2110.
Good luck.
Shenghao
I am using the TAS2110 now, but in the future it will probably be tas2563. I use the driver for TAS2505 because it is only available for Linux on your git (the rest of the drivers are for Android).
Thanks for reply.
I compiled driver for TAS2562 without error, but I have problem with register codec. In dmesg log I see:
[ 1.478687] tegra-asoc: sound: failed to set tas2562 sysclk!
[ 1.484433] tegra-asoc: sound: ASoC: failed to init tas2562-playback: -524
[ 1.491381] tegra-asoc: sound: ASoC: failed to instantiate card -524
[ 1.498653] usb 1-3: new high-speed USB device number 2 using tegra-xusb
[ 1.501418] tegra-asoc: sound: snd_soc_register_card failed (-524)
[ 1.507772] tegra-asoc:: probe of sound failed with error -524
full dmesg log:
dmesg_full.txt
I checked files /sys/kernel/debug/asoc/*
$ sudo cat /sys/kernel/debug/asoc/components tegra210-ope.1 tegra210-ope.0 tegra210-mvc.1 tegra210-mvc.0 tegra210-afc.5 tegra210-afc.4 tegra210-afc.3 tegra210-afc.2 tegra210-afc.1 tegra210-afc.0 tegra210-sfc.3 tegra210-sfc.2 tegra210-sfc.1 tegra210-sfc.0 tegra210-mixer tegra210-adx.1 tegra210-adx.0 tegra210-amx.1 tegra210-amx.0 tegra210-dmic.1 tegra210-dmic.0 tegra210-i2s.2 tegra210-admaif tegra210-axbar tas2562.2-004c 7.spdif-dit.7 6.spdif-dit.6 5.spdif-dit.5 4.spdif-dit.4 3.spdif-dit.3 2.spdif-dit.2 1.spdif-dit.1 0.spdif-dit.0 snd-soc-dummy
$ sudo cat /sys/kernel/debug/asoc/dais OPE OUT OPE IN OPE OUT OPE IN MVC OUT MVC IN MVC OUT MVC IN AFC OUT AFC IN AFC OUT AFC IN AFC OUT AFC IN AFC OUT AFC IN AFC OUT AFC IN AFC OUT AFC IN DAP CIF DAP CIF DAP CIF DAP CIF TX5 TX4 TX3 TX2 TX1 RX10 RX9 RX8 RX7 RX6 RX5 RX4 RX3 RX2 RX1 IN OUT4 OUT3 OUT2 OUT1 IN OUT4 OUT3 OUT2 OUT1 OUT IN4 IN3 IN2 IN1 OUT IN4 IN3 IN2 IN1 DAP CIF DAP CIF DAP CIF ADMAIF10 CIF ADMAIF9 CIF ADMAIF8 CIF ADMAIF7 CIF ADMAIF6 CIF ADMAIF5 CIF ADMAIF4 CIF ADMAIF3 CIF ADMAIF2 CIF ADMAIF1 CIF ADMAIF10 FIFO ADMAIF9 FIFO ADMAIF8 FIFO ADMAIF7 FIFO ADMAIF6 FIFO ADMAIF5 FIFO ADMAIF4 FIFO ADMAIF3 FIFO ADMAIF2 FIFO ADMAIF1 FIFO ADMAIF10 ADMAIF9 ADMAIF8 ADMAIF7 ADMAIF6 ADMAIF5 ADMAIF4 ADMAIF3 ADMAIF2 ADMAIF1 ADX2 ADX2-4 ADX2-3 ADX2-2 ADX2-1 ADX1 ADX1-4 ADX1-3 ADX1-2 ADX1-1 AMX2-4 AMX2-3 AMX2-2 AMX2-1 AMX2 AMX1-4 AMX1-3 AMX1-2 AMX1-1 AMX1 DMIC3 DMIC2 DMIC1 IQC2-2 IQC2-1 IQC1-2 IQC1-1 MVC2 MVC1 SPKPROT1 OPE2 OPE1 AFC6 AFC5 AFC4 AFC3 AFC2 AFC1 MIXER1-10 MIXER1-9 MIXER1-8 MIXER1-7 MIXER1-6 MIXER1-5 MIXER1-4 MIXER1-3 MIXER1-2 MIXER1-1 SFC4 SFC3 SFC2 SFC1 I2S5 I2S4 I2S3 I2S2 I2S1 ADMAIF10 ADMAIF9 ADMAIF8 ADMAIF7 ADMAIF6 ADMAIF5 ADMAIF4 ADMAIF3 ADMAIF2 ADMAIF1 tas2562 ASI1 dit-hifi dit-hifi dit-hifi dit-hifi dit-hifi dit-hifi dit-hifi dit-hifi snd-soc-dummy-dai
$ sudo cat /sys/kernel/debug/asoc/platforms tegra210-admaif snd-soc-dummy
static int tegra_machine_tas2562_init(struct snd_soc_pcm_runtime *rtd) {
struct device *dev = rtd->card->dev;
int err;
err = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 12288000,
SND_SOC_CLOCK_IN);
if (err) {
dev_err(dev, "failed to set tas2562 sysclk!\n");
return err;
}
return 0;
}
When I used tas2505 driver with similar settings (only change tas2505 to tas 2562), I didn't have any problem.
Do you have any idea what is wrong?
Hi Mateus,
We'll check your error descriptions and provide further comments as soon as possible
Best regards,
-Ivan Salazar
Applications Engineer
I found mistake in device tree and now I see sound card in system
sudo cat /proc/asound/cards
0 [tegrahda ]: tegra-hda - tegra-hda
tegra-hda at 0x70038000 irq 82
1 [tegrasndt210ref]: tegra-snd-t210r - tegra-snd-t210ref-mobile-TAS2xx
tegra-snd-t210ref-mobile-TAS2xxx_AMP
When I play sound then I2S and I2C is quiet (only mclk was 11.3MHz).
During boot on UART I had:
[ 1.400695] tas2562 2-004c: Driver ID: 1.0.1
[ 1.429716] tas2562 2-004c: gpio up !!
[ 1.467630] tas2562 2-004c: tas2562_codec_probe!!!
I checked files /sys/kernel/debug/asoc/*
sudo cat /sys/kernel/debug/asoc/codecs tegra210-ope.1 tegra210-ope.0 tegra210-mvc.1 tegra210-mvc.0 tegra210-afc.5 tegra210-afc.4 tegra210-afc.3 tegra210-afc.2 tegra210-afc.1 tegra210-afc.0 tegra210-sfc.3 tegra210-sfc.2 tegra210-sfc.1 tegra210-sfc.0 tegra210-mixer tegra210-adx.1 tegra210-adx.0 tegra210-amx.1 tegra210-amx.0 tegra210-dmic.1 tegra210-dmic.0 tegra210-i2s.2 tegra210-admaif tegra210-axbar tas2562.2-004c 7.spdif-dit.7 6.spdif-dit.6 5.spdif-dit.5 4.spdif-dit.4 3.spdif-dit.3 2.spdif-dit.2 1.spdif-dit.1 0.spdif-dit.0 snd-soc-dummy
sudo cat /sys/kernel/debug/asoc/dais OPE OUT OPE IN OPE OUT OPE IN MVC OUT MVC IN MVC OUT MVC IN AFC OUT AFC IN AFC OUT AFC IN AFC OUT AFC IN AFC OUT AFC IN AFC OUT AFC IN AFC OUT AFC IN DAP CIF DAP CIF DAP CIF DAP CIF TX5 TX4 TX3 TX2 TX1 RX10 RX9 RX8 RX7 RX6 RX5 RX4 RX3 RX2 RX1 IN OUT4 OUT3 OUT2 OUT1 IN OUT4 OUT3 OUT2 OUT1 OUT IN4 IN3 IN2 IN1 OUT IN4 IN3 IN2 IN1 DAP CIF DAP CIF DAP CIF ADMAIF10 CIF ADMAIF9 CIF ADMAIF8 CIF ADMAIF7 CIF ADMAIF6 CIF ADMAIF5 CIF ADMAIF4 CIF ADMAIF3 CIF ADMAIF2 CIF ADMAIF1 CIF ADMAIF10 FIFO ADMAIF9 FIFO ADMAIF8 FIFO ADMAIF7 FIFO ADMAIF6 FIFO ADMAIF5 FIFO ADMAIF4 FIFO ADMAIF3 FIFO ADMAIF2 FIFO ADMAIF1 FIFO ADMAIF10 ADMAIF9 ADMAIF8 ADMAIF7 ADMAIF6 ADMAIF5 ADMAIF4 ADMAIF3 ADMAIF2 ADMAIF1 ADX2 ADX2-4 ADX2-3 ADX2-2 ADX2-1 ADX1 ADX1-4 ADX1-3 ADX1-2 ADX1-1 AMX2-4 AMX2-3 AMX2-2 AMX2-1 AMX2 AMX1-4 AMX1-3 AMX1-2 AMX1-1 AMX1 DMIC3 DMIC2 DMIC1 IQC2-2 IQC2-1 IQC1-2 IQC1-1 MVC2 MVC1 SPKPROT1 OPE2 OPE1 AFC6 AFC5 AFC4 AFC3 AFC2 AFC1 MIXER1-10 MIXER1-9 MIXER1-8 MIXER1-7 MIXER1-6 MIXER1-5 MIXER1-4 MIXER1-3 MIXER1-2 MIXER1-1 SFC4 SFC3 SFC2 SFC1 I2S5 I2S4 I2S3 I2S2 I2S1 ADMAIF10 ADMAIF9 ADMAIF8 ADMAIF7 ADMAIF6 ADMAIF5 ADMAIF4 ADMAIF3 ADMAIF2 ADMAIF1 tas2562 ASI1 dit-hifi dit-hifi dit-hifi dit-hifi dit-hifi dit-hifi dit-hifi dit-hifi snd-soc-dummy-dai
Below I attach dmesg log and source file:
#--- sudo zcat /proc/config.gz | egrep -i 'tas2562 ---# CONFIG_SND_SOC_TAS2562=y CONFIG_TAS2562_REGMAP=y CONFIG_TAS2562_CODEC=y #--- sudo cat /proc/asound/cards ---# 0 [tegrahda ]: tegra-hda - tegra-hda tegra-hda at 0x70038000 irq 82 1 [tegrasndt210ref]: tegra-snd-t210r - tegra-snd-t210ref-mobile-TAS2xx tegra-snd-t210ref-mobile-TAS2xxx_AMP #--- sudo zcat /proc/config.gz | grep CONFIG_SND_SOC_SPDIF ---# CONFIG_SND_SOC_SPDIF=y
/* ** ============================================================================= ** Copyright (c) 2016 Texas Instruments Inc. ** ** This program is free software; you can redistribute it and/or modify it under ** the terms of the GNU General Public License as published by the Free Software ** Foundation; version 2. ** ** This program is distributed in the hope that it will be useful, but WITHOUT ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ** FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details. ** ** File: ** tas2562-codec.c ** ** Description: ** ALSA SoC driver for Texas Instruments TAS2562 High Performance 4W Smart ** Amplifier ** ** ============================================================================= */ #ifdef CONFIG_TAS2562_CODEC #define DEBUG 1 #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> #include <linux/gpio.h> #include <linux/regulator/consumer.h> #include <linux/firmware.h> #include <linux/regmap.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/initval.h> #include <sound/tlv.h> #include "tas2562.h" #define TAS2562_MDELAY 0xFFFFFFFE #define TAS2562_MSLEEP 0xFFFFFFFD static char const *iv_enable_text[] = {"Off", "On"}; static int tas2562iv_enable = 1; static int mbMute; static const struct soc_enum tas2562_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(iv_enable_text), iv_enable_text), }; static int tas2562_set_fmt(struct tas2562_priv *pTAS2562, unsigned int fmt); static int tas2562_i2c_load_data(struct tas2562_priv *pTAS2562, unsigned int *pData); static int tas2562_mute_ctrl_get(struct snd_kcontrol *pKcontrol, struct snd_ctl_elem_value *pValue); static int tas2562_mute_ctrl_put(struct snd_kcontrol *pKcontrol, struct snd_ctl_elem_value *pValue); static unsigned int p_tas2562_classH_D_data[] = { /* reg address size values */ TAS2562_ClassHHeadroom, 0x4, 0x09, 0x99, 0x99, 0x9a, TAS2562_ClassHHysteresis, 0x4, 0x0, 0x0, 0x0, 0x0, TAS2562_ClassHMtct, 0x4, 0xb, 0x0, 0x0, 0x0, TAS2562_VBatFilter, 0x1, 0x38, TAS2562_ClassHReleaseTimer, 0x1, 0x3c, TAS2562_BoostSlope, 0x1, 0x78, TAS2562_TestPageConfiguration, 0x1, 0xd, TAS2562_ClassDConfiguration3, 0x1, 0x8e, TAS2562_ClassDConfiguration2, 0x1, 0x49, TAS2562_ClassDConfiguration4, 0x1, 0x21, TAS2562_ClassDConfiguration1, 0x1, 0x80, TAS2562_EfficiencyConfiguration, 0x1, 0xc1, 0xFFFFFFFF, 0xFFFFFFFF }; static unsigned int tas2562_codec_read(struct snd_soc_codec *codec, unsigned int reg) { struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec); int nResult = 0; unsigned int value = 0; nResult = pTAS2562->read(pTAS2562, reg, &value); if (nResult < 0) dev_err(pTAS2562->dev, "%s, ERROR, reg=0x%x, E=%d\n", __func__, reg, nResult); else dev_dbg(pTAS2562->dev, "%s, reg: 0x%x, value: 0x%x\n", __func__, reg, value); if (nResult >= 0) return value; else return nResult; } static int tas2562_iv_enable(struct tas2562_priv *pTAS2562, int enable) { int nResult; if (enable) { pr_debug("%s: tas2562iv_enable \n", __func__); nResult = pTAS2562->update_bits(pTAS2562, TAS2562_PowerControl, TAS2562_PowerControl_ISNSPower_Mask | TAS2562_PowerControl_VSNSPower_Mask, TAS2562_PowerControl_VSNSPower_Active | TAS2562_PowerControl_ISNSPower_Active); } else { pr_debug("%s: tas2562iv_disable \n", __func__); nResult = pTAS2562->update_bits(pTAS2562, TAS2562_PowerControl, TAS2562_PowerControl_ISNSPower_Mask | TAS2562_PowerControl_VSNSPower_Mask, TAS2562_PowerControl_VSNSPower_PoweredDown | TAS2562_PowerControl_ISNSPower_PoweredDown); } tas2562iv_enable = enable; return nResult; } static int tas2562iv_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec); int iv_enable = 0, nResult = 0; if (codec == NULL) { pr_err("%s: codec is NULL \n", __func__); return 0; } iv_enable = ucontrol->value.integer.value[0]; nResult = tas2562_iv_enable(pTAS2562, iv_enable); pr_debug("%s: tas2562iv_enable = %d\n", __func__, tas2562iv_enable); return nResult; } static int tas2562iv_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { ucontrol->value.integer.value[0] = tas2562iv_enable; return 0; } static const struct snd_kcontrol_new tas2562_controls[] = { SOC_ENUM_EXT("TAS2562 IVSENSE ENABLE", tas2562_enum[0], tas2562iv_get, tas2562iv_put), }; static int tas2562_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec); int nResult = 0; nResult = pTAS2562->write(pTAS2562, reg, value); if (nResult < 0) dev_err(pTAS2562->dev, "%s, ERROR, reg=0x%x, E=%d\n", __func__, reg, nResult); else dev_dbg(pTAS2562->dev, "%s, reg: 0x%x, 0x%x\n", __func__, reg, value); return nResult; } static int tas2562_i2c_load_data(struct tas2562_priv *pTAS2562, unsigned int *pData) { unsigned int nRegister; unsigned int *nData; unsigned char Buf[128]; unsigned int nLength = 0; unsigned int i = 0; unsigned int nSize = 0; int nResult = 0; do { nRegister = pData[nLength]; nSize = pData[nLength + 1]; nData = &pData[nLength + 2]; if (nRegister == TAS2562_MSLEEP) { msleep(nData[0]); dev_dbg(pTAS2562->dev, "%s, msleep = %d\n", __func__, nData[0]); } else if (nRegister == TAS2562_MDELAY) { mdelay(nData[0]); dev_dbg(pTAS2562->dev, "%s, mdelay = %d\n", __func__, nData[0]); } else { if (nRegister != 0xFFFFFFFF) { if (nSize > 128) { dev_err(pTAS2562->dev, "%s, Line=%d, invalid size, maximum is 128 bytes!\n", __func__, __LINE__); break; } if (nSize > 1) { for (i = 0; i < nSize; i++) Buf[i] = (unsigned char)nData[i]; nResult = pTAS2562->bulk_write(pTAS2562, nRegister, Buf, nSize); if (nResult < 0) break; } else if (nSize == 1) { nResult = pTAS2562->write(pTAS2562, nRegister, nData[0]); if (nResult < 0) break; } else { dev_err(pTAS2562->dev, "%s, Line=%d,invalid size, minimum is 1 bytes!\n", __func__, __LINE__); } } } nLength = nLength + 2 + pData[nLength + 1]; } while (nRegister != 0xFFFFFFFF); return nResult; } static int tas2562_codec_suspend(struct snd_soc_codec *codec) { struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec); int ret = 0; mutex_lock(&pTAS2562->codec_lock); dev_dbg(pTAS2562->dev, "%s\n", __func__); pTAS2562->runtime_suspend(pTAS2562); mutex_unlock(&pTAS2562->codec_lock); return ret; } static int tas2562_codec_resume(struct snd_soc_codec *codec) { struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec); int ret = 0; mutex_lock(&pTAS2562->codec_lock); dev_dbg(pTAS2562->dev, "%s\n", __func__); pTAS2562->runtime_resume(pTAS2562); mutex_unlock(&pTAS2562->codec_lock); return ret; } static const struct snd_kcontrol_new tas2562_asi_controls[] = { SOC_DAPM_SINGLE("Left", TAS2562_TDMConfigurationReg2, 4, 1, 0), SOC_DAPM_SINGLE("Right", TAS2562_TDMConfigurationReg2, 4, 2, 0), SOC_DAPM_SINGLE("LeftRightDiv2", TAS2562_TDMConfigurationReg2, 4, 3, 0), }; static int tas2562_set_power_state(struct tas2562_priv *pTAS2562, int state) { int nResult = 0; /*unsigned int nValue;*/ int irqreg; if ((pTAS2562->mbMute) && (state == TAS2562_POWER_ACTIVE)) state = TAS2562_POWER_MUTE; dev_err(pTAS2562->dev, "set power state: %d\n", state); switch (state) { case TAS2562_POWER_ACTIVE: //if set format was not called by asoc, then set it default if(pTAS2562->mnASIFormat == 0) pTAS2562->mnASIFormat = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_I2S; nResult = tas2562_set_fmt(pTAS2562, pTAS2562->mnASIFormat); if (nResult < 0) return nResult; //Clear latched IRQ before power on pTAS2562->update_bits(pTAS2562, TAS2562_InterruptConfiguration, TAS2562_InterruptConfiguration_LTCHINTClear_Mask, TAS2562_InterruptConfiguration_LTCHINTClear); pTAS2562->read(pTAS2562, TAS2562_LatchedInterruptReg0, &irqreg); dev_info(pTAS2562->dev, "IRQ reg is: %s %d, %d\n", __func__, irqreg, __LINE__); pTAS2562->mbPowerUp = true; pTAS2562->mnPowerState = TAS2562_POWER_ACTIVE; schedule_delayed_work(&pTAS2562->irq_work, msecs_to_jiffies(10)); break; case TAS2562_POWER_MUTE: nResult = pTAS2562->update_bits(pTAS2562, TAS2562_PowerControl, TAS2562_PowerControl_OperationalMode10_Mask | TAS2562_PowerControl_ISNSPower_Mask | TAS2562_PowerControl_VSNSPower_Mask, TAS2562_PowerControl_OperationalMode10_Mute | TAS2562_PowerControl_VSNSPower_Active | TAS2562_PowerControl_ISNSPower_Active); pTAS2562->mbPowerUp = true; pTAS2562->mnPowerState = TAS2562_POWER_MUTE; break; case TAS2562_POWER_SHUTDOWN: pTAS2562->enableIRQ(pTAS2562, false); if (hrtimer_active(&pTAS2562->mtimer)) { dev_info(pTAS2562->dev, "cancel timer\n"); hrtimer_cancel(&pTAS2562->mtimer); } nResult = pTAS2562->update_bits(pTAS2562, TAS2562_PowerControl, TAS2562_PowerControl_OperationalMode10_Mask, TAS2562_PowerControl_OperationalMode10_Shutdown); pTAS2562->mbPowerUp = false; pTAS2562->mnPowerState = TAS2562_POWER_SHUTDOWN; msleep(20); break; default: dev_err(pTAS2562->dev, "wrong power state setting %d\n", state); } return nResult; } static int tas2562_dac_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec); switch (event) { case SND_SOC_DAPM_POST_PMU: tas2562_set_power_state(pTAS2562, TAS2562_POWER_ACTIVE); break; case SND_SOC_DAPM_PRE_PMD: tas2562_set_power_state(pTAS2562, TAS2562_POWER_SHUTDOWN); break; } return 0; } static const struct snd_soc_dapm_widget tas2562_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("Voltage Sense", "ASI1 Capture", 1, TAS2562_PowerControl, 2, 1), SND_SOC_DAPM_AIF_OUT("Current Sense", "ASI1 Capture", 0, TAS2562_PowerControl, 3, 1), SND_SOC_DAPM_MIXER("ASI1 Sel", TAS2562_TDMConfigurationReg2, 4, 0, &tas2562_asi_controls[0], ARRAY_SIZE(tas2562_asi_controls)), SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2562_dac_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_OUTPUT("OUT"), SND_SOC_DAPM_SIGGEN("VMON"), SND_SOC_DAPM_SIGGEN("IMON") }; static const struct snd_soc_dapm_route tas2562_audio_map[] = { {"ASI1 Sel", "Left", "ASI1"}, {"ASI1 Sel", "Right", "ASI1"}, {"ASI1 Sel", "LeftRightDiv2", "ASI1"}, {"DAC", NULL, "ASI1 Sel"}, {"OUT", NULL, "DAC"}, /*{"VMON", NULL, "Voltage Sense"}, {"IMON", NULL, "Current Sense"},*/ {"Voltage Sense", NULL, "VMON"}, {"Current Sense", NULL, "IMON"}, }; static int tas2562_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec); dev_dbg(pTAS2562->dev, "%s, %d \n", __func__, mute); mutex_lock(&pTAS2562->codec_lock); if (mute) { tas2562_set_power_state(pTAS2562, TAS2562_POWER_SHUTDOWN); } else { tas2562_set_power_state(pTAS2562, TAS2562_POWER_ACTIVE); } mutex_unlock(&pTAS2562->codec_lock); return 0; } static int tas2562_slot_config(struct snd_soc_codec *codec, struct tas2562_priv *pTAS2562, int blr_clk_ratio) { int ret = 0; pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg5, 0xff, 0x42); pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg6, 0xff, 0x40); return ret; } static int tas2562_set_slot(struct snd_soc_codec *codec, int slot_width) { int ret = 0; struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec); switch (slot_width) { case 16: ret = pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg2, TAS2562_TDMConfigurationReg2_RXSLEN10_Mask, TAS2562_TDMConfigurationReg2_RXSLEN10_16Bits); break; case 24: ret = pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg2, TAS2562_TDMConfigurationReg2_RXSLEN10_Mask, TAS2562_TDMConfigurationReg2_RXSLEN10_24Bits); break; case 32: ret = pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg2, TAS2562_TDMConfigurationReg2_RXSLEN10_Mask, TAS2562_TDMConfigurationReg2_RXSLEN10_32Bits); break; case 0: /* Do not change slot width */ break; default: dev_err(pTAS2562->dev, "slot width not supported"); ret = -EINVAL; } if (ret >= 0) pTAS2562->mnSlot_width = slot_width; return ret; } static int tas2562_set_bitwidth(struct tas2562_priv *pTAS2562, int bitwidth) { int slot_width_tmp = 16; dev_info(pTAS2562->dev, "%s %d\n", __func__, bitwidth); switch (bitwidth) { case SNDRV_PCM_FORMAT_S16_LE: pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg2, TAS2562_TDMConfigurationReg2_RXWLEN32_Mask, TAS2562_TDMConfigurationReg2_RXWLEN32_16Bits); pTAS2562->mnCh_size = 16; if (pTAS2562->mnSlot_width == 0) slot_width_tmp = 16; break; case SNDRV_PCM_FORMAT_S24_LE: pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg2, TAS2562_TDMConfigurationReg2_RXWLEN32_Mask, TAS2562_TDMConfigurationReg2_RXWLEN32_24Bits); pTAS2562->mnCh_size = 24; if (pTAS2562->mnSlot_width == 0) slot_width_tmp = 32; break; case SNDRV_PCM_FORMAT_S32_LE: pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg2, TAS2562_TDMConfigurationReg2_RXWLEN32_Mask, TAS2562_TDMConfigurationReg2_RXWLEN32_32Bits); pTAS2562->mnCh_size = 32; if (pTAS2562->mnSlot_width == 0) slot_width_tmp = 32; break; default: dev_info(pTAS2562->dev, "Not supported params format\n"); } /* If machine driver did not call set slot width */ if (pTAS2562->mnSlot_width == 0) tas2562_set_slot(pTAS2562->codec, slot_width_tmp); dev_info(pTAS2562->dev, "mnCh_size: %d\n", pTAS2562->mnCh_size); pTAS2562->mnPCMFormat = bitwidth; return 0; } static int tas2562_set_samplerate(struct tas2562_priv *pTAS2562, int samplerate) { switch (samplerate) { case 48000: pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg0, TAS2562_TDMConfigurationReg0_SAMPRATERAMP_Mask, TAS2562_TDMConfigurationReg0_SAMPRATERAMP_48KHz); pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg0, TAS2562_TDMConfigurationReg0_SAMPRATE31_Mask, TAS2562_TDMConfigurationReg0_SAMPRATE31_44_1_48kHz); break; case 44100: pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg0, TAS2562_TDMConfigurationReg0_SAMPRATERAMP_Mask, TAS2562_TDMConfigurationReg0_SAMPRATERAMP_44_1KHz); pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg0, TAS2562_TDMConfigurationReg0_SAMPRATE31_Mask, TAS2562_TDMConfigurationReg0_SAMPRATE31_44_1_48kHz); break; case 96000: pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg0, TAS2562_TDMConfigurationReg0_SAMPRATERAMP_Mask, TAS2562_TDMConfigurationReg0_SAMPRATERAMP_48KHz); pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg0, TAS2562_TDMConfigurationReg0_SAMPRATE31_Mask, TAS2562_TDMConfigurationReg0_SAMPRATE31_88_2_96kHz); break; case 88200: pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg0, TAS2562_TDMConfigurationReg0_SAMPRATERAMP_Mask, TAS2562_TDMConfigurationReg0_SAMPRATERAMP_44_1KHz); pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg0, TAS2562_TDMConfigurationReg0_SAMPRATE31_Mask, TAS2562_TDMConfigurationReg0_SAMPRATE31_88_2_96kHz); break; case 19200: pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg0, TAS2562_TDMConfigurationReg0_SAMPRATERAMP_Mask, TAS2562_TDMConfigurationReg0_SAMPRATERAMP_48KHz); pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg0, TAS2562_TDMConfigurationReg0_SAMPRATE31_Mask, TAS2562_TDMConfigurationReg0_SAMPRATE31_176_4_192kHz); break; case 17640: pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg0, TAS2562_TDMConfigurationReg0_SAMPRATERAMP_Mask, TAS2562_TDMConfigurationReg0_SAMPRATERAMP_44_1KHz); pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg0, TAS2562_TDMConfigurationReg0_SAMPRATE31_Mask, TAS2562_TDMConfigurationReg0_SAMPRATE31_176_4_192kHz); break; default: dev_info(pTAS2562->dev, "%s, unsupported sample rate, %d\n", __func__, samplerate); } pTAS2562->mnSamplingRate = samplerate; return 0; } static int tas2562_mute_ctrl_get(struct snd_kcontrol *pKcontrol, struct snd_ctl_elem_value *pValue) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec); pValue->value.integer.value[0] = pTAS2562->mbMute; dev_dbg(pTAS2562->dev, "tas2562_mute_ctrl_get = %d\n", pTAS2562->mbMute); return 0; } static int tas2562_mute_ctrl_put(struct snd_kcontrol *pKcontrol, struct snd_ctl_elem_value *pValue) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec); mbMute = pValue->value.integer.value[0]; dev_dbg(pTAS2562->dev, "tas2562_mute_ctrl_put = %d\n", mbMute); pTAS2562->mbMute = !!mbMute; return 0; } static int tas2562_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec); int blr_clk_ratio; int ret = 0; dev_dbg(pTAS2562->dev, "%s, format: %d\n", __func__, params_format(params)); mutex_lock(&pTAS2562->codec_lock); ret = tas2562_set_bitwidth(pTAS2562, params_format(params)); if(ret < 0) { dev_info(pTAS2562->dev, "set bitwidth failed, %d\n", ret); goto ret; } blr_clk_ratio = params_channels(params) * pTAS2562->mnCh_size; dev_info(pTAS2562->dev, "blr_clk_ratio: %d\n", blr_clk_ratio); if(blr_clk_ratio != 0) tas2562_slot_config(pTAS2562->codec, pTAS2562, blr_clk_ratio); dev_info(pTAS2562->dev, "%s, sample rate: %d\n", __func__, params_rate(params)); ret = tas2562_set_samplerate(pTAS2562, params_rate(params)); ret: mutex_unlock(&pTAS2562->codec_lock); return ret; } static int tas2562_set_fmt(struct tas2562_priv *pTAS2562, unsigned int fmt) { u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0; int ret = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: asi_cfg_1 = 0x00; break; default: dev_err(pTAS2562->dev, "ASI format master is not found\n"); ret = -EINVAL; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: dev_info(pTAS2562->dev, "INV format: NBNF\n"); asi_cfg_1 |= TAS2562_TDMConfigurationReg1_RXEDGE_Rising; break; case SND_SOC_DAIFMT_IB_NF: dev_info(pTAS2562->dev, "INV format: IBNF\n"); asi_cfg_1 |= TAS2562_TDMConfigurationReg1_RXEDGE_Falling; break; default: dev_err(pTAS2562->dev, "ASI format Inverse is not found\n"); ret = -EINVAL; } pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg1, TAS2562_TDMConfigurationReg1_RXEDGE_Mask, asi_cfg_1); switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case (SND_SOC_DAIFMT_I2S): tdm_rx_start_slot = 1; break; case (SND_SOC_DAIFMT_DSP_A): case (SND_SOC_DAIFMT_DSP_B): tdm_rx_start_slot = 1; break; case (SND_SOC_DAIFMT_LEFT_J): tdm_rx_start_slot = 0; break; default: dev_err(pTAS2562->dev, "DAI Format is not found, fmt=0x%x\n", fmt); ret = -EINVAL; break; } pTAS2562->update_bits(pTAS2562, TAS2562_TDMConfigurationReg1, TAS2562_TDMConfigurationReg1_RXOFFSET51_Mask, (tdm_rx_start_slot << TAS2562_TDMConfigurationReg1_RXOFFSET51_Shift)); pTAS2562->mnASIFormat = fmt; return 0; } static int tas2562_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_codec *codec = dai->codec; struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec); int ret = 0; dev_dbg(pTAS2562->dev, "%s, format=0x%x\n", __func__, fmt); ret = tas2562_set_fmt(pTAS2562, fmt); return ret; } static int tas2562_set_dai_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { int ret = 0; struct snd_soc_codec *codec = dai->codec; struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec); dev_dbg(pTAS2562->dev, "%s, tx_mask:%d, rx_mask:%d, slots:%d, slot_width:%d", __func__, tx_mask, rx_mask, slots, slot_width); ret = tas2562_set_slot(codec, slot_width); return ret; } static struct snd_soc_dai_ops tas2562_dai_ops = { .digital_mute = tas2562_mute, .hw_params = tas2562_hw_params, .set_fmt = tas2562_set_dai_fmt, .set_tdm_slot = tas2562_set_dai_tdm_slot, }; #define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) #define TAS2562_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 \ SNDRV_PCM_RATE_88200 |\ SNDRV_PCM_RATE_96000 |\ SNDRV_PCM_RATE_176400 |\ SNDRV_PCM_RATE_192000\ ) static struct snd_soc_dai_driver tas2562_dai_driver[] = { { .name = "tas2562 ASI1", .id = 0, .playback = { .stream_name = "ASI1 Playback", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, .formats = TAS2562_FORMATS, }, .capture = { .stream_name = "ASI1 Capture", .channels_min = 0, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, .formats = TAS2562_FORMATS, }, .ops = &tas2562_dai_ops, .symmetric_rates = 1, }, }; static int tas2562_load_init(struct tas2562_priv *pTAS2562) { int ret; #ifdef TAS2558_CODEC /* Max voltage to 9V */ ret = pTAS2562->update_bits(pTAS2562, TAS2562_BoostConfiguration2, TAS2562_BoostConfiguration2_BoostMaxVoltage_Mask, 0x7); if(ret < 0) return ret; ret = pTAS2562->update_bits(pTAS2562, TAS2562_PlaybackConfigurationReg0, TAS2562_PlaybackConfigurationReg0_AmplifierLevel51_Mask, 0xd << 1); if(ret < 0) return ret; #endif ret = pTAS2562->write(pTAS2562, TAS2562_MiscConfigurationReg0, 0xcf); if(ret < 0) return ret; ret = pTAS2562->write(pTAS2562, TAS2562_TDMConfigurationReg4, 0x01); if(ret < 0) return ret; ret = pTAS2562->write(pTAS2562, TAS2562_ClockConfiguration, 0x0c); if(ret < 0) return ret; ret = tas2562_i2c_load_data(pTAS2562, p_tas2562_classH_D_data); return ret; } static int tas2562_codec_probe(struct snd_soc_codec *codec) { int ret; struct tas2562_priv *pTAS2562 = snd_soc_codec_get_drvdata(codec); ret = snd_soc_add_codec_controls(codec, tas2562_controls, ARRAY_SIZE(tas2562_controls)); if (ret < 0) { pr_err("%s: add_codec_controls failed, err %d\n", __func__, ret); return ret; } tas2562_load_init(pTAS2562); tas2562_iv_enable(pTAS2562, 1); pTAS2562->codec = codec; dev_err(pTAS2562->dev, "%s!!!\n", __func__); return 0; } static int tas2562_codec_remove(struct snd_soc_codec *codec) { return 0; } static DECLARE_TLV_DB_SCALE(tas2562_digital_tlv, 1100, 50, 0); static const struct snd_kcontrol_new tas2562_snd_controls[] = { SOC_SINGLE_TLV("Amp Output Level", TAS2562_PlaybackConfigurationReg0, 0, 0x16, 0, tas2562_digital_tlv), SOC_SINGLE_EXT("SmartPA Mute", SND_SOC_NOPM, 0, 0x0001, 0, tas2562_mute_ctrl_get, tas2562_mute_ctrl_put), }; static struct snd_soc_codec_driver soc_codec_driver_tas2562 = { .probe = tas2562_codec_probe, .remove = tas2562_codec_remove, .read = tas2562_codec_read, .write = tas2562_codec_write, .suspend = tas2562_codec_suspend, .resume = tas2562_codec_resume, .component_driver = { .controls = tas2562_snd_controls, .num_controls = ARRAY_SIZE(tas2562_snd_controls), .dapm_widgets = tas2562_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(tas2562_dapm_widgets), .dapm_routes = tas2562_audio_map, .num_dapm_routes = ARRAY_SIZE(tas2562_audio_map), }, }; int tas2562_register_codec(struct tas2562_priv *pTAS2562) { int nResult = 0; dev_info(pTAS2562->dev, "%s, enter\n", __func__); nResult = snd_soc_register_codec(pTAS2562->dev, &soc_codec_driver_tas2562, tas2562_dai_driver, ARRAY_SIZE(tas2562_dai_driver)); return nResult; } int tas2562_deregister_codec(struct tas2562_priv *pTAS2562) { snd_soc_unregister_codec(pTAS2562->dev); return 0; } void tas2562_LoadConfig(struct tas2562_priv *pTAS2562) { int ret = 0; if (hrtimer_active(&pTAS2562->mtimer)) { dev_info(pTAS2562->dev, "cancel timer\n"); hrtimer_cancel(&pTAS2562->mtimer); } else dev_info(pTAS2562->dev, "timer not active\n"); pTAS2562->hw_reset(pTAS2562); msleep(2); pTAS2562->write(pTAS2562, TAS2562_SoftwareReset, TAS2562_SoftwareReset_SoftwareReset_Reset); msleep(3); tas2562_load_init(pTAS2562); tas2562_iv_enable(pTAS2562, tas2562iv_enable); ret = tas2562_set_slot(pTAS2562->codec, pTAS2562->mnSlot_width); if (ret < 0) goto end; ret = tas2562_set_fmt(pTAS2562, pTAS2562->mnASIFormat); if (ret < 0) goto end; ret = tas2562_set_bitwidth(pTAS2562, pTAS2562->mnPCMFormat); if (ret < 0) goto end; ret = tas2562_set_samplerate(pTAS2562, pTAS2562->mnSamplingRate); if (ret < 0) goto end; ret = tas2562_set_power_state(pTAS2562, pTAS2562->mnPowerState); if (ret < 0) goto end; end: /* power up failed, restart later */ if (ret < 0) schedule_delayed_work(&pTAS2562->irq_work, msecs_to_jiffies(1000)); } MODULE_AUTHOR("Texas Instruments Inc."); MODULE_DESCRIPTION("TAS2562 ALSA SOC Smart Amplifier driver"); MODULE_LICENSE("GPL"); #endif /* CONFIG_TAS2562_CODEC */
/* ** ============================================================================= ** Copyright (c) 2016 Texas Instruments Inc. ** ** This program is free software; you can redistribute it and/or modify it under ** the terms of the GNU General Public License as published by the Free Software ** Foundation; version 2. ** ** This program is distributed in the hope that it will be useful, but WITHOUT ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ** File: ** tas2562-misc.c ** ** Description: ** misc driver for Texas Instruments TAS2562 High Performance 4W Smart Amplifier ** ** ============================================================================= */ #define DEBUG 1 #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> #include <linux/gpio.h> #include <linux/regulator/consumer.h> #include <linux/firmware.h> #include <linux/regmap.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/slab.h> #include <linux/syscalls.h> #include <linux/fcntl.h> #include <linux/miscdevice.h> #include <linux/uaccess.h> #include "tas2562.h" #include "tas2562-misc.h" #include <linux/dma-mapping.h> static int g_logEnable = 1; static struct tas2562_priv *g_tas2562; static int tas2562_file_open(struct inode *inode, struct file *file) { struct tas2562_priv *pTAS2562 = g_tas2562; if (!try_module_get(THIS_MODULE)) return -ENODEV; file->private_data = (void *)pTAS2562; if (g_logEnable) dev_info(pTAS2562->dev, "%s\n", __func__); return 0; } static int tas2562_file_release(struct inode *inode, struct file *file) { struct tas2562_priv *pTAS2562 = (struct tas2562_priv *)file->private_data; if (g_logEnable) dev_info(pTAS2562->dev, "%s\n", __func__); file->private_data = (void *)NULL; module_put(THIS_MODULE); return 0; } static ssize_t tas2562_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) { struct tas2562_priv *pTAS2562 = (struct tas2562_priv *)file->private_data; int ret = 0; unsigned int nValue = 0; unsigned char value = 0; unsigned char *p_kBuf = NULL; mutex_lock(&pTAS2562->file_lock); switch (pTAS2562->mnDBGCmd) { case TIAUDIO_CMD_REG_READ: { if (g_logEnable) dev_info(pTAS2562->dev, "TIAUDIO_CMD_REG_READ: current_reg = 0x%x, count=%d\n", pTAS2562->mnCurrentReg, (int)count); if (count == 1) { ret = pTAS2562->read(pTAS2562, pTAS2562->mnCurrentReg, &nValue); if (ret < 0) { dev_err(pTAS2562->dev, "dev read fail %d\n", ret); break; } value = (u8)nValue; if (g_logEnable) dev_info(pTAS2562->dev, "TIAUDIO_CMD_REG_READ: nValue=0x%x, value=0x%x\n", nValue, value); ret = copy_to_user(buf, &value, 1); if (ret != 0) { /* Failed to copy all the data, exit */ dev_err(pTAS2562->dev, "copy to user fail %d\n", ret); } } else if (count > 1) { p_kBuf = kzalloc(count, GFP_KERNEL); if (p_kBuf != NULL) { ret = pTAS2562->bulk_read(pTAS2562, pTAS2562->mnCurrentReg, p_kBuf, count); if (ret < 0) { dev_err(pTAS2562->dev, "dev bulk read fail %d\n", ret); } else { ret = copy_to_user(buf, p_kBuf, count); if (ret != 0) { /* Failed to copy all the data, exit */ dev_err(pTAS2562->dev, "copy to user fail %d\n", ret); } } kfree(p_kBuf); } else { dev_err(pTAS2562->dev, "read no mem\n"); } } } break; } pTAS2562->mnDBGCmd = 0; mutex_unlock(&pTAS2562->file_lock); return count; } static ssize_t tas2562_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { struct tas2562_priv *pTAS2562 = (struct tas2562_priv *)file->private_data; int ret = 0; unsigned char *p_kBuf = NULL; unsigned int reg = 0; unsigned int len = 0; mutex_lock(&pTAS2562->file_lock); p_kBuf = kzalloc(count, GFP_KERNEL); if (p_kBuf == NULL) { dev_err(pTAS2562->dev, "write no mem\n"); goto err; } ret = copy_from_user(p_kBuf, buf, count); if (ret != 0) { dev_err(pTAS2562->dev, "copy_from_user failed.\n"); goto err; } pTAS2562->mnDBGCmd = p_kBuf[0]; switch (pTAS2562->mnDBGCmd) { case TIAUDIO_CMD_REG_WITE: if (count > 5) { reg = ((unsigned int)p_kBuf[1] << 24) + ((unsigned int)p_kBuf[2] << 16) + ((unsigned int)p_kBuf[3] << 8) + (unsigned int)p_kBuf[4]; len = count - 5; if (len == 1) { ret = pTAS2562->write(pTAS2562, reg, p_kBuf[5]); if (g_logEnable) dev_info(pTAS2562->dev, "TIAUDIO_CMD_REG_WITE, Reg=0x%x, Val=0x%x\n", reg, p_kBuf[5]); } else { ret = pTAS2562->bulk_write(pTAS2562, reg, &p_kBuf[5], len); } } else { dev_err(pTAS2562->dev, "%s, write len fail, count=%d.\n", __func__, (int)count); } pTAS2562->mnDBGCmd = 0; break; case TIAUDIO_CMD_REG_READ: if (count == 5) { pTAS2562->mnCurrentReg = ((unsigned int)p_kBuf[1] << 24) + ((unsigned int)p_kBuf[2] << 16) + ((unsigned int)p_kBuf[3] << 8) + (unsigned int)p_kBuf[4]; if (g_logEnable) { dev_info(pTAS2562->dev, "TIAUDIO_CMD_REG_READ, whole=0x%x\n", pTAS2562->mnCurrentReg); } } else { dev_err(pTAS2562->dev, "read len fail.\n"); } break; } err: if (p_kBuf != NULL) kfree(p_kBuf); mutex_unlock(&pTAS2562->file_lock); return count; } static const struct file_operations fops = { .owner = THIS_MODULE, .read = tas2562_file_read, .write = tas2562_file_write, .unlocked_ioctl = NULL, .open = tas2562_file_open, .release = tas2562_file_release, }; #define MODULE_NAME "tas2562" static struct miscdevice tas2562_misc = { .minor = MISC_DYNAMIC_MINOR, .name = MODULE_NAME, .fops = &fops, }; int tas2562_register_misc(struct tas2562_priv *pTAS2562) { int ret = 0; g_tas2562 = pTAS2562; ret = misc_register(&tas2562_misc); if (ret) { dev_err(pTAS2562->dev, "TAS2562 misc fail: %d\n", ret); } dev_info(pTAS2562->dev, "%s, leave\n", __func__); return ret; } int tas2562_deregister_misc(struct tas2562_priv *pTAS2562) { misc_deregister(&tas2562_misc); return 0; } MODULE_AUTHOR("Texas Instruments Inc."); MODULE_DESCRIPTION("TAS2562 Misc Smart Amplifier driver"); MODULE_LICENSE("GPL");
/* * ALSA SoC Texas Instruments TAS2562 High Performance 4W Smart Amplifier * * Copyright (C) 2016 Texas Instruments, Inc. * * Author: saiprasad * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * */ #ifdef CONFIG_TAS2562_REGMAP #define DEBUG 1 #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/err.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> #include <linux/gpio.h> #include <linux/regulator/consumer.h> #include <linux/firmware.h> #include <linux/regmap.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/slab.h> #include <sound/soc.h> #include <linux/interrupt.h> #include <linux/irq.h> #include "tas2562.h" #include "tas2562-codec.h" #include "tas2562-misc.h" static char pICN[] = {0x00, 0x03, 0x46, 0xdc}; static int tas2562_change_book_page(struct tas2562_priv *pTAS2562, int book, int page) { int nResult = 0; if ((pTAS2562->mnCurrentBook == book) && (pTAS2562->mnCurrentPage == page)) goto end; if (pTAS2562->mnCurrentBook != book) { nResult = regmap_write(pTAS2562->regmap, TAS2562_BOOKCTL_PAGE, 0); if (nResult < 0) { dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, nResult); goto end; } pTAS2562->mnCurrentPage = 0; nResult = regmap_write(pTAS2562->regmap, TAS2562_BOOKCTL_REG, book); if (nResult < 0) { dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, nResult); goto end; } pTAS2562->mnCurrentBook = book; } if (pTAS2562->mnCurrentPage != page) { nResult = regmap_write(pTAS2562->regmap, TAS2562_BOOKCTL_PAGE, page); if (nResult < 0) { dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, nResult); goto end; } pTAS2562->mnCurrentPage = page; } end: return nResult; } static int tas2562_dev_read(struct tas2562_priv *pTAS2562, unsigned int reg, unsigned int *pValue) { int nResult = 0; mutex_lock(&pTAS2562->dev_lock); nResult = tas2562_change_book_page(pTAS2562, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); if (nResult < 0) goto end; nResult = regmap_read(pTAS2562->regmap, TAS2562_PAGE_REG(reg), pValue); if (nResult < 0) dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, nResult); else dev_dbg(pTAS2562->dev, "%s: BOOK:PAGE:REG %u:%u:%u\n", __func__, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), TAS2562_PAGE_REG(reg)); end: mutex_unlock(&pTAS2562->dev_lock); return nResult; } static int tas2562_dev_write(struct tas2562_priv *pTAS2562, unsigned int reg, unsigned int value) { int nResult = 0; mutex_lock(&pTAS2562->dev_lock); nResult = tas2562_change_book_page(pTAS2562, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); if (nResult < 0) goto end; nResult = regmap_write(pTAS2562->regmap, TAS2562_PAGE_REG(reg), value); if (nResult < 0) dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, nResult); else dev_dbg(pTAS2562->dev, "%s: BOOK:PAGE:REG %u:%u:%u, VAL: 0x%02x\n", __func__, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), TAS2562_PAGE_REG(reg), value); end: mutex_unlock(&pTAS2562->dev_lock); return nResult; } static int tas2562_dev_bulk_write(struct tas2562_priv *pTAS2562, unsigned int reg, unsigned char *pData, unsigned int nLength) { int nResult = 0; mutex_lock(&pTAS2562->dev_lock); nResult = tas2562_change_book_page(pTAS2562, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); if (nResult < 0) goto end; nResult = regmap_bulk_write(pTAS2562->regmap, TAS2562_PAGE_REG(reg), pData, nLength); if (nResult < 0) dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, nResult); else dev_dbg(pTAS2562->dev, "%s: BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n", __func__, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), TAS2562_PAGE_REG(reg), nLength); end: mutex_unlock(&pTAS2562->dev_lock); return nResult; } static int tas2562_dev_bulk_read(struct tas2562_priv *pTAS2562, unsigned int reg, unsigned char *pData, unsigned int nLength) { int nResult = 0; mutex_lock(&pTAS2562->dev_lock); nResult = tas2562_change_book_page(pTAS2562, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); if (nResult < 0) goto end; nResult = regmap_bulk_read(pTAS2562->regmap, TAS2562_PAGE_REG(reg), pData, nLength); if (nResult < 0) dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, nResult); else dev_dbg(pTAS2562->dev, "%s: BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n", __func__, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), TAS2562_PAGE_REG(reg), nLength); end: mutex_unlock(&pTAS2562->dev_lock); return nResult; } static int tas2562_dev_update_bits(struct tas2562_priv *pTAS2562, unsigned int reg, unsigned int mask, unsigned int value) { int nResult = 0; mutex_lock(&pTAS2562->dev_lock); nResult = tas2562_change_book_page(pTAS2562, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); if (nResult < 0) goto end; nResult = regmap_update_bits(pTAS2562->regmap, TAS2562_PAGE_REG(reg), mask, value); if (nResult < 0) dev_err(pTAS2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, nResult); else dev_dbg(pTAS2562->dev, "%s: BOOK:PAGE:REG %u:%u:%u, mask: 0x%x, val=0x%x\n", __func__, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), TAS2562_PAGE_REG(reg), mask, value); end: mutex_unlock(&pTAS2562->dev_lock); return nResult; } static bool tas2562_volatile(struct device *dev, unsigned int reg) { return true; } static bool tas2562_writeable(struct device *dev, unsigned int reg) { return true; } static const struct regmap_config tas2562_i2c_regmap = { .reg_bits = 8, .val_bits = 8, .writeable_reg = tas2562_writeable, .volatile_reg = tas2562_volatile, .cache_type = REGCACHE_NONE, .max_register = 1 * 128, }; static void tas2562_hw_reset(struct tas2562_priv *pTAS2562) { if (gpio_is_valid(pTAS2562->mnResetGPIO)) { gpio_direction_output(pTAS2562->mnResetGPIO, 0); msleep(5); gpio_direction_output(pTAS2562->mnResetGPIO, 1); msleep(2); } dev_err(pTAS2562->dev, "gpio up !!\n"); pTAS2562->mnCurrentBook = -1; pTAS2562->mnCurrentPage = -1; } void tas2562_enableIRQ(struct tas2562_priv *pTAS2562, bool enable) { if (enable) { if (pTAS2562->mbIRQEnable) return; if (gpio_is_valid(pTAS2562->mnIRQGPIO)) enable_irq(pTAS2562->mnIRQ); schedule_delayed_work(&pTAS2562->irq_work, msecs_to_jiffies(10)); pTAS2562->mbIRQEnable = true; } else { if (gpio_is_valid(pTAS2562->mnIRQGPIO)) disable_irq_nosync(pTAS2562->mnIRQ); pTAS2562->mbIRQEnable = false; } } static void irq_work_routine(struct work_struct *work) { struct tas2562_priv *pTAS2562 = container_of(work, struct tas2562_priv, irq_work.work); unsigned int nDevInt1Status = 0, nDevInt2Status = 0; int nCounter = 2; int nResult = 0; int irqreg; dev_info(pTAS2562->dev, "%s\n", __func__); #ifdef CONFIG_TAS2562_CODEC mutex_lock(&pTAS2562->codec_lock); #endif if (pTAS2562->mbRuntimeSuspend) { dev_info(pTAS2562->dev, "%s, Runtime Suspended\n", __func__); goto end; } if (pTAS2562->mnPowerState == TAS2562_POWER_SHUTDOWN) { dev_info(pTAS2562->dev, "%s, device not powered\n", __func__); goto end; } nResult = tas2562_dev_write(pTAS2562, TAS2562_InterruptMaskReg0, TAS2562_InterruptMaskReg0_Disable); if (nResult < 0) goto reload; nResult = tas2562_dev_write(pTAS2562, TAS2562_InterruptMaskReg1, TAS2562_InterruptMaskReg1_Disable); if (nResult < 0) goto reload; nResult = tas2562_dev_read(pTAS2562, TAS2562_LatchedInterruptReg0, &nDevInt1Status); if (nResult >= 0) nResult = tas2562_dev_read(pTAS2562, TAS2562_LatchedInterruptReg1, &nDevInt2Status); else goto reload; dev_dbg(pTAS2562->dev, "IRQ status : 0x%x, 0x%x\n", nDevInt1Status, nDevInt2Status); if (((nDevInt1Status & 0x7) != 0) || ((nDevInt2Status & 0x0f) != 0)) { /* in case of INT_OC, INT_OT, INT_OVLT, INT_UVLT, INT_BO */ if (nDevInt1Status & TAS2562_LatchedInterruptReg0_OCEFlagSticky_Interrupt) { pTAS2562->mnErrCode |= ERROR_OVER_CURRENT; dev_err(pTAS2562->dev, "SPK over current!\n"); } else pTAS2562->mnErrCode &= ~ERROR_OVER_CURRENT; if (nDevInt1Status & TAS2562_LatchedInterruptReg0_OTEFlagSticky_Interrupt) { pTAS2562->mnErrCode |= ERROR_DIE_OVERTEMP; dev_err(pTAS2562->dev, "die over temperature!\n"); } else pTAS2562->mnErrCode &= ~ERROR_DIE_OVERTEMP; if (nDevInt2Status & TAS2562_LatchedInterruptReg1_VBATOVLOSticky_Interrupt) { pTAS2562->mnErrCode |= ERROR_OVER_VOLTAGE; dev_err(pTAS2562->dev, "SPK over voltage!\n"); } else pTAS2562->mnErrCode &= ~ERROR_UNDER_VOLTAGE; if (nDevInt2Status & TAS2562_LatchedInterruptReg1_VBATUVLOSticky_Interrupt) { pTAS2562->mnErrCode |= ERROR_UNDER_VOLTAGE; dev_err(pTAS2562->dev, "SPK under voltage!\n"); } else pTAS2562->mnErrCode &= ~ERROR_UNDER_VOLTAGE; if (nDevInt2Status & TAS2562_LatchedInterruptReg1_BrownOutFlagSticky_Interrupt) { pTAS2562->mnErrCode |= ERROR_BROWNOUT; dev_err(pTAS2562->dev, "brownout!\n"); } else pTAS2562->mnErrCode &= ~ERROR_BROWNOUT; dev_err(pTAS2562->dev, "before goto reload\n"); goto reload; } else { nCounter = 2; while (nCounter > 0) { nResult = tas2562_dev_read(pTAS2562, TAS2562_PowerControl, &nDevInt1Status); if (nResult < 0) goto reload; if ((nDevInt1Status & TAS2562_PowerControl_OperationalMode10_Mask) != TAS2562_PowerControl_OperationalMode10_Shutdown) break; pTAS2562->read(pTAS2562, TAS2562_LatchedInterruptReg0, &irqreg); dev_info(pTAS2562->dev, "IRQ reg is: %s %d, %d\n", __func__, irqreg, __LINE__); nResult = pTAS2562->update_bits(pTAS2562, TAS2562_PowerControl, TAS2562_PowerControl_OperationalMode10_Mask, TAS2562_PowerControl_OperationalMode10_Active); if (nResult < 0) goto reload; dev_info(pTAS2562->dev, "set ICN to -80dB\n"); nResult = pTAS2562->bulk_write(pTAS2562, TAS2562_ICN_REG, pICN, 4); pTAS2562->read(pTAS2562, TAS2562_LatchedInterruptReg0, &irqreg); dev_info(pTAS2562->dev, "IRQ reg is: %s, %d, %d\n", __func__, irqreg, __LINE__); nCounter--; if (nCounter > 0) { /* in case check pow status just after power on TAS2562 */ dev_dbg(pTAS2562->dev, "PowSts B: 0x%x, check again after 10ms\n", nDevInt1Status); msleep(10); } } if ((nDevInt1Status & TAS2562_PowerControl_OperationalMode10_Mask) == TAS2562_PowerControl_OperationalMode10_Shutdown) { dev_err(pTAS2562->dev, "%s, Critical ERROR REG[0x%x] = 0x%x\n", __func__, TAS2562_PowerControl, nDevInt1Status); pTAS2562->mnErrCode |= ERROR_CLASSD_PWR; goto reload; } pTAS2562->mnErrCode &= ~ERROR_CLASSD_PWR; } nResult = tas2562_dev_write(pTAS2562, TAS2562_InterruptMaskReg0, 0xf8); if (nResult < 0) goto reload; nResult = tas2562_dev_write(pTAS2562, TAS2562_InterruptMaskReg1, 0xb1); if (nResult < 0) goto reload; goto end; reload: /* hardware reset and reload */ nResult = -1; tas2562_LoadConfig(pTAS2562); end: if (nResult >= 0) { tas2562_enableIRQ(pTAS2562, true); dev_info(pTAS2562->dev, "enable IRQ\n"); } nResult = gpio_get_value(pTAS2562->mnIRQGPIO); dev_info(pTAS2562->dev, "%s, irq GPIO state: %d\n", __func__, nResult); if (!hrtimer_active(&pTAS2562->mtimer)) { dev_dbg(pTAS2562->dev, "%s, start timer\n", __func__); hrtimer_start(&pTAS2562->mtimer, ns_to_ktime((u64)CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL); } else dev_info(pTAS2562->dev, "timer not active\n"); #ifdef CONFIG_TAS2562_CODEC mutex_unlock(&pTAS2562->codec_lock); #endif } static enum hrtimer_restart timer_func(struct hrtimer *timer) { struct tas2562_priv *pTAS2562 = container_of(timer, struct tas2562_priv, mtimer); if (pTAS2562->mbPowerUp) { if (!delayed_work_pending(&pTAS2562->irq_work)) schedule_delayed_work(&pTAS2562->irq_work, msecs_to_jiffies(20)); } return HRTIMER_NORESTART; } static irqreturn_t tas2562_irq_handler(int irq, void *dev_id) { struct tas2562_priv *pTAS2562 = (struct tas2562_priv *)dev_id; tas2562_enableIRQ(pTAS2562, false); /* get IRQ status after 100 ms */ schedule_delayed_work(&pTAS2562->irq_work, msecs_to_jiffies(100)); return IRQ_HANDLED; } static int tas2562_runtime_suspend(struct tas2562_priv *pTAS2562) { dev_dbg(pTAS2562->dev, "%s\n", __func__); pTAS2562->mbRuntimeSuspend = true; if (hrtimer_active(&pTAS2562->mtimer)) { dev_dbg(pTAS2562->dev, "cancel timer\n"); hrtimer_cancel(&pTAS2562->mtimer); } if (delayed_work_pending(&pTAS2562->irq_work)) { dev_dbg(pTAS2562->dev, "cancel IRQ work\n"); cancel_delayed_work_sync(&pTAS2562->irq_work); } return 0; } static int tas2562_runtime_resume(struct tas2562_priv *pTAS2562) { dev_dbg(pTAS2562->dev, "%s\n", __func__); if (pTAS2562->mbPowerUp) { if (!hrtimer_active(&pTAS2562->mtimer)) { dev_dbg(pTAS2562->dev, "%s, start check timer\n", __func__); hrtimer_start(&pTAS2562->mtimer, ns_to_ktime((u64)CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL); } } pTAS2562->mbRuntimeSuspend = false; return 0; } static int tas2562_parse_dt(struct device *dev, struct tas2562_priv *pTAS2562) { struct device_node *np = dev->of_node; int rc = 0, ret = 0; // u32 debounceInfo[2] = { 0, 0 }; rc = of_property_read_u32(np, "ti,asi-format", &pTAS2562->mnASIFormat); if (rc) { dev_err(pTAS2562->dev, "Looking up %s property in node %s failed %d\n", "ti,asi-format", np->full_name, rc); } else { dev_dbg(pTAS2562->dev, "ti,asi-format=%d", pTAS2562->mnASIFormat); } pTAS2562->mnResetGPIO = of_get_named_gpio(np, "ti,reset-gpio", 0); if (!gpio_is_valid(pTAS2562->mnResetGPIO)) { dev_err(pTAS2562->dev, "Looking up %s property in node %s failed %d\n", "ti,reset-gpio", np->full_name, pTAS2562->mnResetGPIO); } else { dev_dbg(pTAS2562->dev, "ti,reset-gpio=%d", pTAS2562->mnResetGPIO); } pTAS2562->mnIRQGPIO = of_get_named_gpio(np, "ti,irq-gpio", 0); if (!gpio_is_valid(pTAS2562->mnIRQGPIO)) { dev_err(pTAS2562->dev, "Looking up %s property in node %s failed %d\n", "ti,irq-gpio", np->full_name, pTAS2562->mnIRQGPIO); } else { dev_dbg(pTAS2562->dev, "ti,irq-gpio=%d", pTAS2562->mnIRQGPIO); } return ret; } static int tas2562_i2c_probe(struct i2c_client *pClient, const struct i2c_device_id *id) { struct tas2562_priv *pTAS2562; int nResult; dev_err(&pClient->dev, "Driver ID: %s\n", TAS2562_DRIVER_ID); dev_info(&pClient->dev, "%s enter\n", __func__); pTAS2562 = devm_kzalloc(&pClient->dev, sizeof(struct tas2562_priv), GFP_KERNEL); if (pTAS2562 == NULL) { dev_err(&pClient->dev, "failed to get i2c device\n"); nResult = -ENOMEM; goto err; } pTAS2562->dev = &pClient->dev; i2c_set_clientdata(pClient, pTAS2562); dev_set_drvdata(&pClient->dev, pTAS2562); pTAS2562->regmap = devm_regmap_init_i2c(pClient, &tas2562_i2c_regmap); if (IS_ERR(pTAS2562->regmap)) { nResult = PTR_ERR(pTAS2562->regmap); dev_err(&pClient->dev, "Failed to allocate register map: %d\n", nResult); goto err; } if (pClient->dev.of_node) tas2562_parse_dt(&pClient->dev, pTAS2562); if (gpio_is_valid(pTAS2562->mnResetGPIO)) { nResult = gpio_request(pTAS2562->mnResetGPIO, "TAS2562_RESET"); if (nResult) { dev_err(pTAS2562->dev, "%s: Failed to request gpio %d\n", __func__, pTAS2562->mnResetGPIO); nResult = -EINVAL; goto err; } tas2562_hw_reset(pTAS2562); } pTAS2562->read = tas2562_dev_read; pTAS2562->write = tas2562_dev_write; pTAS2562->bulk_read = tas2562_dev_bulk_read; pTAS2562->bulk_write = tas2562_dev_bulk_write; pTAS2562->update_bits = tas2562_dev_update_bits; pTAS2562->hw_reset = tas2562_hw_reset; pTAS2562->enableIRQ = tas2562_enableIRQ; //pTAS2562->clearIRQ = tas2562_clearIRQ; pTAS2562->runtime_suspend = tas2562_runtime_suspend; pTAS2562->runtime_resume = tas2562_runtime_resume; pTAS2562->mnPowerState = TAS2562_POWER_SHUTDOWN; mutex_init(&pTAS2562->dev_lock); /* Reset the chip */ nResult = tas2562_dev_write(pTAS2562, TAS2562_SoftwareReset, 0x01); if (nResult < 0) { dev_err(&pClient->dev, "I2c fail, %d\n", nResult); goto err; } if (gpio_is_valid(pTAS2562->mnIRQGPIO)) { nResult = gpio_request(pTAS2562->mnIRQGPIO, "TAS2562-IRQ"); if (nResult < 0) { dev_err(pTAS2562->dev, "%s: GPIO %d request error\n", __func__, pTAS2562->mnIRQGPIO); goto err; } gpio_direction_input(pTAS2562->mnIRQGPIO); nResult = gpio_get_value(pTAS2562->mnIRQGPIO); dev_info(pTAS2562->dev, "irq GPIO state: %d\n", nResult); pTAS2562->mnIRQ = gpio_to_irq(pTAS2562->mnIRQGPIO); dev_dbg(pTAS2562->dev, "irq = %d\n", pTAS2562->mnIRQ); INIT_DELAYED_WORK(&pTAS2562->irq_work, irq_work_routine); nResult = request_threaded_irq(pTAS2562->mnIRQ, tas2562_irq_handler, NULL, IRQF_TRIGGER_LOW|IRQF_ONESHOT, pClient->name, pTAS2562); if (nResult < 0) { dev_err(pTAS2562->dev, "request_irq failed, %d\n", nResult); goto err; } disable_irq_nosync(pTAS2562->mnIRQ); } #ifdef CONFIG_TAS2562_CODEC mutex_init(&pTAS2562->codec_lock); nResult = tas2562_register_codec(pTAS2562); if (nResult < 0) { dev_err(pTAS2562->dev, "register codec failed, %d\n", nResult); goto err; } #endif #ifdef CONFIG_TAS2562_MISC mutex_init(&pTAS2562->file_lock); nResult = tas2562_register_misc(pTAS2562); if (nResult < 0) { dev_err(pTAS2562->dev, "register codec failed, %d\n", nResult); goto err; } #endif hrtimer_init(&pTAS2562->mtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); pTAS2562->mtimer.function = timer_func; err: return nResult; } static int tas2562_i2c_remove(struct i2c_client *pClient) { struct tas2562_priv *pTAS2562 = i2c_get_clientdata(pClient); dev_info(pTAS2562->dev, "%s\n", __func__); #ifdef CONFIG_TAS2562_CODEC tas2562_deregister_codec(pTAS2562); mutex_destroy(&pTAS2562->codec_lock); #endif #ifdef CONFIG_TAS2562_MISC tas2562_deregister_misc(pTAS2562); mutex_destroy(&pTAS2562->file_lock); #endif if (gpio_is_valid(pTAS2562->mnResetGPIO)) gpio_free(pTAS2562->mnResetGPIO); if (gpio_is_valid(pTAS2562->mnIRQGPIO)) gpio_free(pTAS2562->mnIRQGPIO); return 0; } static const struct i2c_device_id tas2562_i2c_id[] = { { "tas2562", 0}, { } }; MODULE_DEVICE_TABLE(i2c, tas2562_i2c_id); #if defined(CONFIG_OF) static const struct of_device_id tas2562_of_match[] = { { .compatible = "ti,tas2562" }, {}, }; MODULE_DEVICE_TABLE(of, tas2562_of_match); #endif static struct i2c_driver tas2562_i2c_driver = { .driver = { .name = "tas2562", .owner = THIS_MODULE, #if defined(CONFIG_OF) .of_match_table = of_match_ptr(tas2562_of_match), #endif }, .probe = tas2562_i2c_probe, .remove = tas2562_i2c_remove, .id_table = tas2562_i2c_id, }; module_i2c_driver(tas2562_i2c_driver); MODULE_AUTHOR("Texas Instruments Inc."); MODULE_DESCRIPTION("TAS2562 I2C Smart Amplifier driver"); MODULE_LICENSE("GPL"); #endif
/* * tegra_machine_driver_mobile.c - Tegra ASoC Machine driver for mobile * * Copyright (c) 2017-2021 NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/of_platform.h> #include <linux/input.h> #include <linux/gpio.h> #include <linux/pm_runtime.h> #include <sound/core.h> #include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <dt-bindings/sound/tas2552.h> #include "rt5659.h" #include "sgtl5000.h" #include "tas2562.h" #include "tegra_asoc_machine_alt.h" #include "tegra210_xbar_alt.h" #define DRV_NAME "tegra-asoc:" #define DEBUG 1 #define PARAMS(sformat, channels) \ { \ .formats = sformat, \ .rate_min = 48000, \ .rate_max = 48000, \ .channels_min = channels, \ .channels_max = channels, \ } /* t210 soc data */ static const struct tegra_machine_soc_data soc_data_tegra210 = { .admaif_dai_link_start = TEGRA210_DAI_LINK_ADMAIF1, .admaif_dai_link_end = TEGRA210_DAI_LINK_ADMAIF10, #if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT) .adsp_pcm_dai_link_start = TEGRA210_DAI_LINK_ADSP_PCM1, .adsp_pcm_dai_link_end = TEGRA210_DAI_LINK_ADSP_PCM2, .adsp_compr_dai_link_start = TEGRA210_DAI_LINK_ADSP_COMPR1, .adsp_compr_dai_link_end = TEGRA210_DAI_LINK_ADSP_COMPR2, #endif .sfc_dai_link = TEGRA210_DAI_LINK_SFC1_RX, .write_idle_bias_off_state = false, .ahub_links = tegra210_xbar_dai_links, .num_ahub_links = TEGRA210_XBAR_DAI_LINKS, .ahub_confs = tegra210_xbar_codec_conf, .num_ahub_confs = TEGRA210_XBAR_CODEC_CONF, }; /* t186 soc data */ static const struct tegra_machine_soc_data soc_data_tegra186 = { .admaif_dai_link_start = TEGRA186_DAI_LINK_ADMAIF1, .admaif_dai_link_end = TEGRA186_DAI_LINK_ADMAIF10, #if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT) .adsp_pcm_dai_link_start = TEGRA186_DAI_LINK_ADSP_PCM1, .adsp_pcm_dai_link_end = TEGRA186_DAI_LINK_ADSP_PCM2, .adsp_compr_dai_link_start = TEGRA186_DAI_LINK_ADSP_COMPR1, .adsp_compr_dai_link_end = TEGRA186_DAI_LINK_ADSP_COMPR2, #endif .sfc_dai_link = TEGRA186_DAI_LINK_SFC1_RX, .write_idle_bias_off_state = true, .ahub_links = tegra186_xbar_dai_links, .num_ahub_links = TEGRA186_XBAR_DAI_LINKS, .ahub_confs = tegra186_xbar_codec_conf, .num_ahub_confs = TEGRA186_XBAR_CODEC_CONF, }; static const char * const tegra_machine_srate_text[] = { "None", "8kHz", "16kHz", "44kHz", "48kHz", "11kHz", "22kHz", "24kHz", "32kHz", "88kHz", "96kHz", "176kHz", "192kHz", }; static const char * const tegra_machine_format_text[] = { "None", "16", "32", }; static const struct soc_enum tegra_machine_codec_rate = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tegra_machine_srate_text), tegra_machine_srate_text); static const struct soc_enum tegra_machine_codec_format = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tegra_machine_format_text), tegra_machine_format_text); static const int tegra_machine_srate_values[] = { 0, 8000, 16000, 44100, 48000, 11025, 22050, 24000, 32000, 88200, 96000, 176400, 192000, }; static const struct snd_soc_dapm_widget tegra_machine_dapm_widgets[] = { SND_SOC_DAPM_SPK("x Int Spk", NULL), SND_SOC_DAPM_HP("x Headphone Jack", NULL), SND_SOC_DAPM_MIC("x Int Mic", NULL), SND_SOC_DAPM_MIC("x Mic Jack", NULL), SND_SOC_DAPM_SPK("d1 Headphone", NULL), SND_SOC_DAPM_SPK("d2 Headphone", NULL), SND_SOC_DAPM_SPK("d3 Headphone", NULL), SND_SOC_DAPM_HP("w Headphone", NULL), SND_SOC_DAPM_HP("x Headphone", NULL), SND_SOC_DAPM_HP("y Headphone", NULL), SND_SOC_DAPM_HP("z Headphone", NULL), SND_SOC_DAPM_HP("l Headphone", NULL), SND_SOC_DAPM_HP("m Headphone", NULL), SND_SOC_DAPM_HP("n Headphone", NULL), SND_SOC_DAPM_HP("o Headphone", NULL), SND_SOC_DAPM_HP("s Headphone", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL), SND_SOC_DAPM_MIC("w Mic", NULL), SND_SOC_DAPM_MIC("x Mic", NULL), SND_SOC_DAPM_MIC("y Mic", NULL), SND_SOC_DAPM_MIC("z Mic", NULL), SND_SOC_DAPM_MIC("l Mic", NULL), SND_SOC_DAPM_MIC("m Mic", NULL), SND_SOC_DAPM_MIC("n Mic", NULL), SND_SOC_DAPM_MIC("o Mic", NULL), SND_SOC_DAPM_MIC("a Mic", NULL), SND_SOC_DAPM_MIC("b Mic", NULL), SND_SOC_DAPM_MIC("c Mic", NULL), SND_SOC_DAPM_MIC("d Mic", NULL), SND_SOC_DAPM_MIC("s Mic", NULL), SND_SOC_DAPM_LINE("x Line Out", NULL), SND_SOC_DAPM_LINE("y Line Out", NULL), SND_SOC_DAPM_LINE("x Line In", NULL), SND_SOC_DAPM_LINE("y Line In", NULL), }; static struct snd_soc_pcm_stream tegra_machine_asrc_link_params[] = { PARAMS(SNDRV_PCM_FMTBIT_S32_LE, 8), PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), }; static int tegra_machine_codec_get_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct tegra_machine *machine = snd_soc_card_get_drvdata(card); ucontrol->value.integer.value[0] = machine->rate_via_kcontrol; return 0; } static int tegra_machine_codec_put_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct tegra_machine *machine = snd_soc_card_get_drvdata(card); /* set the rate control flag */ machine->rate_via_kcontrol = ucontrol->value.integer.value[0]; return 0; } static int tegra_machine_codec_get_format(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct tegra_machine *machine = snd_soc_card_get_drvdata(card); ucontrol->value.integer.value[0] = machine->fmt_via_kcontrol; return 0; } static int tegra_machine_codec_put_format(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct tegra_machine *machine = snd_soc_card_get_drvdata(card); /* set the format control flag */ machine->fmt_via_kcontrol = ucontrol->value.integer.value[0]; return 0; } static int tegra_machine_set_params(struct snd_soc_card *card, struct tegra_machine *machine, unsigned int rate, unsigned int channels, u64 formats) { unsigned int mask = (1 << channels) - 1; struct snd_soc_pcm_runtime *rtd; int idx = 0, err = 0; u64 format_k; int num_of_dai_links = machine->soc_data->num_ahub_links + machine->num_codec_links; format_k = (machine->fmt_via_kcontrol == 2) ? (1ULL << SNDRV_PCM_FORMAT_S32_LE) : formats; /* update dai link hw_params */ list_for_each_entry(rtd, &card->rtd_list, list) { if (rtd->dai_link->params) { struct snd_soc_pcm_stream *dai_params; dai_params = (struct snd_soc_pcm_stream *) rtd->dai_link->params; dai_params->rate_min = rate; dai_params->channels_min = channels; dai_params->formats = format_k; if ((idx >= machine->soc_data->num_ahub_links) && (idx < num_of_dai_links)) { unsigned int fmt; /* TODO: why below overrite is needed */ dai_params->formats = formats; fmt = rtd->dai_link->dai_fmt; fmt &= SND_SOC_DAIFMT_FORMAT_MASK; /* set TDM slot mask */ if (fmt == SND_SOC_DAIFMT_DSP_A || fmt == SND_SOC_DAIFMT_DSP_B) { err = snd_soc_dai_set_tdm_slot( rtd->cpu_dai, mask, mask, 0, 0); if (err < 0) { dev_err(card->dev, "%s cpu DAI slot mask not set\n", rtd->cpu_dai->name); return err; } } } } idx++; } return 0; } static int tegra_machine_dai_init(struct snd_soc_pcm_runtime *runtime, unsigned int rate, unsigned int channels, u64 formats) { struct snd_soc_card *card = runtime->card; struct tegra_machine *machine = snd_soc_card_get_drvdata(card); struct snd_soc_pcm_stream *dai_params; unsigned int aud_mclk, srate; int err, sample_size; struct snd_soc_pcm_runtime *rtd; srate = (machine->rate_via_kcontrol) ? tegra_machine_srate_values[machine->rate_via_kcontrol] : rate; switch (formats) { case SNDRV_PCM_FORMAT_S8: sample_size = 8; break; case SNDRV_PCM_FORMAT_S16_LE: sample_size = 16; break; case SNDRV_PCM_FORMAT_S24_LE: /* * I2S bit clock is derived from PLLA_OUT0 and size of * 24 bits results in fractional value and the clock * is not accurate with this. To have integer clock * division below is used. It means there are additional * bit clocks (8 cycles) which are ignored. Codec picks * up data for other channel when LRCK signal toggles. */ case SNDRV_PCM_FORMAT_S32_LE: sample_size = 32; break; default: pr_err("Wrong format!\n"); return -EINVAL; } formats = 1ULL << formats; err = tegra_alt_asoc_utils_set_rate(&machine->audio_clock, srate, channels, sample_size, 0, 0); if (err < 0) { dev_err(card->dev, "Can't configure clocks\n"); return err; } aud_mclk = machine->audio_clock.set_aud_mclk_rate; pr_debug("pll_a_out0 = %u Hz, aud_mclk = %u Hz, sample rate = %u Hz\n", machine->audio_clock.set_pll_out_rate, aud_mclk, srate); err = tegra_machine_set_params(card, machine, srate, channels, formats); if (err < 0) return err; rtd = snd_soc_get_pcm_runtime(card, "tas2562-playback"); if (rtd) { dai_params = (struct snd_soc_pcm_stream *)rtd->dai_link->params; dai_params->formats = (machine->fmt_via_kcontrol == 2) ? (1ULL << SNDRV_PCM_FORMAT_S32_LE) : formats; /* err = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 12288000, SND_SOC_CLOCK_IN); if (err < 0) { dev_err(card->dev, "codec_dai clock not set\n"); return err; } */ } return 0; } static int tegra_machine_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; int err; err = tegra_machine_dai_init(rtd, params_rate(params), params_channels(params), params_format(params)); if (err < 0) { dev_err(card->dev, "Failed dai init\n"); return err; } return 0; } static int tegra_machine_pcm_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct tegra_machine *machine = snd_soc_card_get_drvdata(rtd->card); tegra_alt_asoc_utils_clk_enable(&machine->audio_clock); return 0; } static void tegra_machine_pcm_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct tegra_machine *machine = snd_soc_card_get_drvdata(rtd->card); tegra_alt_asoc_utils_clk_disable(&machine->audio_clock); } static int tegra_machine_suspend_pre(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; /* DAPM dai link stream work for non pcm links */ list_for_each_entry(rtd, &card->rtd_list, list) { if (rtd->dai_link->params) INIT_DELAYED_WORK(&rtd->delayed_work, NULL); } return 0; } #if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT) static int tegra_machine_compr_startup(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_card *card = rtd->card; struct tegra_machine *machine = snd_soc_card_get_drvdata(card); tegra_alt_asoc_utils_clk_enable(&machine->audio_clock); return 0; } static void tegra_machine_compr_shutdown(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_card *card = rtd->card; struct tegra_machine *machine = snd_soc_card_get_drvdata(card); tegra_alt_asoc_utils_clk_disable(&machine->audio_clock); } static int tegra_machine_compr_set_params(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_card *card = rtd->card; struct snd_soc_platform *platform = rtd->platform; struct snd_codec codec_params; int err; if (platform->driver->compr_ops && platform->driver->compr_ops->get_params) { err = platform->driver->compr_ops->get_params(cstream, &codec_params); if (err < 0) { dev_err(card->dev, "Failed to get compr params\n"); return err; } } else { dev_err(card->dev, "compr ops not set\n"); return -EINVAL; } err = tegra_machine_dai_init(rtd, codec_params.sample_rate, codec_params.ch_out, SNDRV_PCM_FORMAT_S16_LE); if (err < 0) { dev_err(card->dev, "Failed dai init\n"); return err; } return 0; } #endif static int tegra_machine_respeaker_init(struct snd_soc_pcm_runtime *rtd) { struct device *dev = rtd->card->dev; int err; /* ac108 codec driver hardcodes the freq as 24000000 * and source as PLL irrespective of args passed through * this callback */ err = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 24000000, SND_SOC_CLOCK_IN); if (err) { dev_err(dev, "failed to set ac108 sysclk!\n"); return err; } return 0; } static int tegra_machine_fepi_init(struct snd_soc_pcm_runtime *rtd) { struct device *dev = rtd->card->dev; int err; err = snd_soc_dai_set_sysclk(rtd->codec_dai, SGTL5000_SYSCLK, 12288000, SND_SOC_CLOCK_IN); if (err) { dev_err(dev, "failed to set sgtl5000 sysclk!\n"); return err; } return 0; } static int tegra_machine_tas2562_init(struct snd_soc_pcm_runtime *rtd) { /* struct device *dev = rtd->card->dev; int err; err = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 12288000, SND_SOC_CLOCK_IN); if (err) { dev_err(dev, "failed to set tas2562 sysclk!!!\n"); return err; } */ return 0; } static int tegra_machine_rt565x_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct snd_soc_jack *jack; int err; jack = devm_kzalloc(card->dev, sizeof(struct snd_soc_jack), GFP_KERNEL); if (!jack) return -ENOMEM; err = snd_soc_card_jack_new(card, "Headset Jack", SND_JACK_HEADSET, jack, NULL, 0); if (err) { dev_err(card->dev, "Headset Jack creation failed %d\n", err); return err; } err = tegra_machine_add_codec_jack_control(card, rtd, jack); if (err) { dev_err(card->dev, "Failed to add jack control: %d\n", err); return err; } err = rt5659_set_jack_detect(rtd->codec, jack); if (err) { dev_err(card->dev, "Failed to set jack for RT565x: %d\n", err); return err; } /* single button supporting play/pause */ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA); /* multiple buttons supporting play/pause and volume up/down */ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_MEDIA); snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); snd_soc_dapm_sync(&card->dapm); return 0; } static int codec_init(struct tegra_machine *machine) { struct snd_soc_dai_link *dai_links = machine->asoc->dai_links; unsigned int num_links = machine->asoc->num_links, i; if (!dai_links || !num_links) return -EINVAL; for (i = 0; i < num_links; i++) { if (!dai_links[i].name) continue; if (strstr(dai_links[i].name, "rt565x-playback") || strstr(dai_links[i].name, "rt565x-codec-sysclk-bclk1")) dai_links[i].init = tegra_machine_rt565x_init; else if (strstr(dai_links[i].name, "fe-pi-audio-z-v2")) dai_links[i].init = tegra_machine_fepi_init; else if (strstr(dai_links[i].name, "respeaker-4-mic-array")) dai_links[i].init = tegra_machine_respeaker_init; else if (strstr(dai_links[i].name, "tas2562-playback")) dai_links[i].init = tegra_machine_tas2562_init; } return 0; } static struct snd_soc_ops tegra_machine_pcm_ops = { .hw_params = tegra_machine_pcm_hw_params, .startup = tegra_machine_pcm_startup, .shutdown = tegra_machine_pcm_shutdown, }; #if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT) static struct snd_soc_compr_ops tegra_machine_compr_ops = { .set_params = tegra_machine_compr_set_params, .startup = tegra_machine_compr_startup, .shutdown = tegra_machine_compr_shutdown, }; #endif static void set_dai_ops(struct tegra_machine *machine) { int i; /* set ADMAIF dai_ops */ for (i = machine->soc_data->admaif_dai_link_start; i <= machine->soc_data->admaif_dai_link_end; i++) machine->asoc->dai_links[i].ops = &tegra_machine_pcm_ops; #if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT) /* set ADSP PCM/COMPR */ for (i = machine->soc_data->adsp_pcm_dai_link_start; i <= machine->soc_data->adsp_pcm_dai_link_end; i++) machine->asoc->dai_links[i].ops = &tegra_machine_pcm_ops; /* set ADSP COMPR */ for (i = machine->soc_data->adsp_compr_dai_link_start; i <= machine->soc_data->adsp_compr_dai_link_end; i++) machine->asoc->dai_links[i].compr_ops = &tegra_machine_compr_ops; #endif #if IS_ENABLED(CONFIG_SND_SOC_TEGRA186_ASRC_ALT) if (!(of_machine_is_compatible("nvidia,tegra210") || of_machine_is_compatible("nvidia,tegra210b01"))) { /* set ASRC params. The default is 2 channels */ for (i = 0; i < 6; i++) { int tx = TEGRA186_DAI_LINK_ASRC1_TX1 + i; int rx = TEGRA186_DAI_LINK_ASRC1_RX1 + i; machine->asoc->dai_links[tx].params = &tegra_machine_asrc_link_params[i]; machine->asoc->dai_links[rx].params = &tegra_machine_asrc_link_params[i]; } } #endif } static int add_dai_links(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); struct tegra_machine *machine = snd_soc_card_get_drvdata(card); int ret; machine->asoc = devm_kzalloc(&pdev->dev, sizeof(*machine->asoc), GFP_KERNEL); if (!machine->asoc) return -ENOMEM; ret = tegra_asoc_populate_dai_links(pdev); if (ret < 0) return ret; ret = tegra_asoc_populate_codec_confs(pdev); if (ret < 0) return ret; ret = codec_init(machine); if (ret < 0) return ret; set_dai_ops(machine); return 0; } static const struct snd_kcontrol_new tegra_machine_controls[] = { SOC_ENUM_EXT("codec-x rate", tegra_machine_codec_rate, tegra_machine_codec_get_rate, tegra_machine_codec_put_rate), SOC_ENUM_EXT("codec-x format", tegra_machine_codec_format, tegra_machine_codec_get_format, tegra_machine_codec_put_format), }; static struct snd_soc_card snd_soc_tegra_card = { .owner = THIS_MODULE, .controls = tegra_machine_controls, .num_controls = ARRAY_SIZE(tegra_machine_controls), .dapm_widgets = tegra_machine_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(tegra_machine_dapm_widgets), .suspend_pre = tegra_machine_suspend_pre, .fully_routed = true, }; /* structure to match device tree node */ static const struct of_device_id tegra_machine_of_match[] = { { .compatible = "nvidia,tegra-audio-t186ref-mobile-rt565x", .data = &soc_data_tegra186 }, { .compatible = "nvidia,tegra-audio-t210ref-mobile-rt565x", .data = &soc_data_tegra210 }, { .compatible = "nvidia,tegra-audio-mystique", .data = &soc_data_tegra186 }, {}, }; static int tegra_machine_driver_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct snd_soc_card *card = &snd_soc_tegra_card; struct tegra_machine *machine; int ret = 0; const struct of_device_id *match; card->dev = &pdev->dev; /* parse card name first to log errors with proper device name */ ret = snd_soc_of_parse_card_name(card, "nvidia,model"); if (ret) return ret; match = of_match_device(tegra_machine_of_match, &pdev->dev); if (!match) { dev_err(&pdev->dev, "Error: No device match found\n"); return -ENODEV; } if (!np) { dev_err(&pdev->dev, "No DT node for tegra machine driver"); return -ENODEV; } machine = devm_kzalloc(&pdev->dev, sizeof(*machine), GFP_KERNEL); if (!machine) return -ENOMEM; machine->soc_data = (struct tegra_machine_soc_data *)match->data; if (!machine->soc_data) return -EINVAL; platform_set_drvdata(pdev, card); snd_soc_card_set_drvdata(card, machine); if (machine->soc_data->write_cdev1_state) machine->audio_clock.clk_cdev1_state = 0; if (machine->soc_data->write_idle_bias_off_state) card->dapm.idle_bias_off = true; ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); if (ret) return ret; memset(&machine->audio_clock, 0, sizeof(machine->audio_clock)); if (of_property_read_u32(np, "mclk-fs", &machine->audio_clock.mclk_scale) < 0) dev_dbg(&pdev->dev, "Missing property mclk-fs\n"); if (of_property_read_bool(np, "fixed-pll")) { machine->audio_clock.fixed_pll = true; dev_info(&pdev->dev, "PLL configuration is fixed from DT\n"); } tegra_machine_dma_set_mask(pdev); ret = add_dai_links(pdev); if (ret < 0) goto cleanup_asoc; ret = tegra_alt_asoc_utils_init(&machine->audio_clock, &pdev->dev, card); if (ret < 0) goto cleanup_asoc; ret = snd_soc_register_card(card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); goto cleanup_asoc; } tegra_machine_add_i2s_codec_controls(card, machine->soc_data->num_ahub_links + machine->num_codec_links); return 0; cleanup_asoc: release_asoc_phandles(machine); return ret; } static int tegra_machine_driver_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); snd_soc_unregister_card(card); return 0; } #if CONFIG_PM static void tegra_asoc_machine_resume(struct device *dev) { WARN_ON(snd_soc_resume(dev)); } #else #define tegra_asoc_machine_resume NULL #endif static const struct dev_pm_ops tegra_asoc_machine_pm_ops = { .prepare = snd_soc_suspend, .complete = tegra_asoc_machine_resume, .poweroff = snd_soc_poweroff, }; static struct platform_driver tegra_asoc_machine_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, .pm = &tegra_asoc_machine_pm_ops, .of_match_table = tegra_machine_of_match, }, .probe = tegra_machine_driver_probe, .remove = tegra_machine_driver_remove, }; module_platform_driver(tegra_asoc_machine_driver); MODULE_AUTHOR("Mohan Kumar <mkumard@nvidia.com>, Sameer Pujar <spujar@nvidia.com>"); MODULE_DESCRIPTION("Tegra ASoC machine driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRV_NAME); MODULE_DEVICE_TABLE(of, tegra_machine_of_match);
Could you check again what is incorrect set?
Hi Mateus,
During playback, I2S signal must work. It is correct the I2C is quiet. I think there is wrong setting from the platform side, as you know, tas2110 is a slave device, it depends on the correct clk from master side. Kindly consult the platform vendor.
BR
Shenghao Ding