From patchwork Tue Jul 30 12:09:35 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Preston X-Patchwork-Id: 11065697 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id AE39F1399 for ; Tue, 30 Jul 2019 12:32:13 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9D4432878F for ; Tue, 30 Jul 2019 12:32:13 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 91447287B6; Tue, 30 Jul 2019 12:32:13 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id DBA9B28784 for ; Tue, 30 Jul 2019 12:32:12 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id CF91F17E4; Tue, 30 Jul 2019 14:13:28 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz CF91F17E4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1564488858; bh=RC6T/49vIMAIXYnzSdeWRG+OmHqPcVMYUMO3mRM/+HE=; h=From:To:Date:In-Reply-To:References:Cc:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=LlSOzORUPDnjy5km7ntuCVhmqWZS6wz1nY8NWm5k3d2Sod2AdnFolQ3p4wxV12Q9+ AwYtK0KNkTcmR9Ig0KWnul39x+3Q2d/h+wFzmcAfNBiYB/NDttdPLT+tZgRaF90FQz 9+jHKkM7fKCCoU1jFWXZc4yc9uVshVxRfslAU3m8= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id A0E5DF805A8; Tue, 30 Jul 2019 14:10:35 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa1.perex.cz (Postfix, from userid 50401) id 160A2F805A9; Tue, 30 Jul 2019 14:10:34 +0200 (CEST) Received: from imap1.codethink.co.uk (imap1.codethink.co.uk [176.9.8.82]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id B3DBAF8058C for ; Tue, 30 Jul 2019 14:10:31 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz B3DBAF8058C Received: from [167.98.27.226] (helo=ct-lt-1124.office.codethink.co.uk) by imap1.codethink.co.uk with esmtpsa (Exim 4.84_2 #1 (Debian)) id 1hsQwq-0003E4-86; Tue, 30 Jul 2019 13:09:44 +0100 From: Thomas Preston To: Liam Girdwood , Mark Brown , Rob Herring , Mark Rutland , Jaroslav Kysela , Takashi Iwai , Charles Keepax , Jerome Brunet , Srinivas Kandagatla , Marco Felsch , Paul Cercueil , Kirill Marinushkin , Cheng-Yi Chiang , Kuninori Morimoto , Vinod Koul , Annaliese McDermond , Thomas Preston , alsa-devel@alsa-project.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Date: Tue, 30 Jul 2019 13:09:35 +0100 Message-Id: <20190730120937.16271-2-thomas.preston@codethink.co.uk> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190730120937.16271-1-thomas.preston@codethink.co.uk> References: <20190730120937.16271-1-thomas.preston@codethink.co.uk> MIME-Version: 1.0 Cc: Patrick Glaser , Rob Duncan , Nate Case Subject: [alsa-devel] [PATCH v2 1/3] dt-bindings: ASoC: Add TDA7802 amplifier X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Thomas Preston Cc: Patrick Glaser Cc: Rob Duncan Cc: Nate Case --- .../devicetree/bindings/sound/tda7802.txt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/tda7802.txt diff --git a/Documentation/devicetree/bindings/sound/tda7802.txt b/Documentation/devicetree/bindings/sound/tda7802.txt new file mode 100644 index 000000000000..f80aaf4f1ba0 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tda7802.txt @@ -0,0 +1,26 @@ +ST TDA7802 audio processor + +This device supports I2C only. + +Required properties: + +- compatible : "st,tda7802" +- reg : the I2C address of the device +- enable-supply : a regulator spec for the PLLen pin + +Optional properties: + +- st,gain-ch13 : gain for channels 1 and 3 (range: 1-4) +- st,gain-ch24 : gain for channels 2 and 3 (range: 1-4) +- st,diagnostic-mode-ch13 : diagnotic mode for channels 1 and 3 + values: "Speaker" (default), "Booster" +- st,diagnostic-mode-ch24 : diagnotic mode for channels 2 and 4 + values: "Speaker" (default), "Booster" + +Example: + +amp: tda7802@6c { + compatible = "st,tda7802"; + reg = <0x6c>; + enable-supply = <&_enable_reg>; +}; From patchwork Tue Jul 30 12:09:36 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Preston X-Patchwork-Id: 11065701 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DF7C01399 for ; Tue, 30 Jul 2019 12:37:14 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CCA3523B24 for ; Tue, 30 Jul 2019 12:37:14 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C087628770; Tue, 30 Jul 2019 12:37:14 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 620B123B24 for ; Tue, 30 Jul 2019 12:37:13 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id C010717B2; Tue, 30 Jul 2019 14:12:00 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz C010717B2 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1564488770; bh=+rsDYnXuVyZbIlutATpf4LvoY/ZFgFfih4+dqmGyTVY=; h=From:To:Date:In-Reply-To:References:Cc:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=G6PrjpStPhGU3+j/e7KlpBikNeysi6GRC6PpwBtP2TwF8nS9s26/PVjc7TzeiKbBk eq+wE9EgsV4UgntzeQzry/ZWQslAKuYgJ9vrASVtEQkUh3/9ndkbcskqa9vVC8lZrb aUwV+Iup6toQMIfKP/ZHCzvJ3eB3uUO1ifGk5bM0= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 5F2B8F80533; Tue, 30 Jul 2019 14:10:14 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa1.perex.cz (Postfix, from userid 50401) id F11E1F804CF; Tue, 30 Jul 2019 14:10:10 +0200 (CEST) Received: from imap1.codethink.co.uk (imap1.codethink.co.uk [176.9.8.82]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id C8205F804CA for ; Tue, 30 Jul 2019 14:10:06 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz C8205F804CA Received: from [167.98.27.226] (helo=ct-lt-1124.office.codethink.co.uk) by imap1.codethink.co.uk with esmtpsa (Exim 4.84_2 #1 (Debian)) id 1hsQwq-0003E4-W7; Tue, 30 Jul 2019 13:09:45 +0100 From: Thomas Preston To: Liam Girdwood , Mark Brown , Rob Herring , Mark Rutland , Jaroslav Kysela , Takashi Iwai , Charles Keepax , Jerome Brunet , Srinivas Kandagatla , Marco Felsch , Paul Cercueil , Kirill Marinushkin , Cheng-Yi Chiang , Kuninori Morimoto , Vinod Koul , Annaliese McDermond , Thomas Preston , alsa-devel@alsa-project.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Date: Tue, 30 Jul 2019 13:09:36 +0100 Message-Id: <20190730120937.16271-3-thomas.preston@codethink.co.uk> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190730120937.16271-1-thomas.preston@codethink.co.uk> References: <20190730120937.16271-1-thomas.preston@codethink.co.uk> MIME-Version: 1.0 Cc: Patrick Glaser , Rob Duncan , Nate Case Subject: [alsa-devel] [PATCH v2 2/3] ASoC: Add codec driver for ST TDA7802 X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" X-Virus-Scanned: ClamAV using ClamSMTP Add an I2C based codec driver for ST TDA7802 amplifier. The amplifier supports 4 audio channels but can support up to 16 with multiple devices. Signed-off-by: Thomas Preston Cc: Patrick Glaser Cc: Rob Duncan Cc: Nate Case --- Changes since v1: - Use ALSA kcontrol interface to expose device controls to userland - Gain - Channel diagnostic mode - Impedance efficiency optimiser. I decided against setting this as a DT property since it seems like something that can be changed on the fly. - Add regmap default values - Channel unmute by default is added in a downstream patch. - I'm not sure if I should keep this since they're all zero, although there are other drivers will all-zero reg_defaults. - I believe the "//" style is used for SPDX headers in normal C source files. https://lwn.net/Articles/739183/ - Drop the "enable" sysfs device attribute. - Don't set TDM format using magic numbers. - Set sample rate using hw_params. - Remove unecessary defines. - Use DAPM to handle AMP_ON. - Cosmetic fixups sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tda7802.c | 509 +++++++++++++++++++++++++++++++++++++ 3 files changed, 517 insertions(+) create mode 100644 sound/soc/codecs/tda7802.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9f89a5346299..7e3117eab735 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -182,6 +182,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TAS5720 if I2C select SND_SOC_TAS6424 if I2C select SND_SOC_TDA7419 if I2C + select SND_SOC_TDA7802 if I2C select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -1121,6 +1122,11 @@ config SND_SOC_TDA7419 depends on I2C select REGMAP_I2C +config SND_SOC_TDA7802 + tristate "ST TDA7802 audio processor" + depends on I2C + select REGMAP_I2C + config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 5b4bb8cf4325..31dec8930e98 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -194,6 +194,7 @@ snd-soc-tas571x-objs := tas571x.o snd-soc-tas5720-objs := tas5720.o snd-soc-tas6424-objs := tas6424.o snd-soc-tda7419-objs := tda7419.o +snd-soc-tda7802-objs := tda7802.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o @@ -474,6 +475,7 @@ obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o +obj-$(CONFIG_SND_SOC_TDA7802) += snd-soc-tda7802.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o diff --git a/sound/soc/codecs/tda7802.c b/sound/soc/codecs/tda7802.c new file mode 100644 index 000000000000..0f82a88bc1a4 --- /dev/null +++ b/sound/soc/codecs/tda7802.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * tda7802.c -- codec driver for ST TDA7802 + * + * Copyright (C) 2016-2019 Tesla Motors, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ENABLE_DELAY_MS 10 + +#define TDA7802_IB0 0x00 +#define TDA7802_IB1 0x01 +#define TDA7802_IB2 0x02 +#define TDA7802_IB3 0x03 +#define TDA7802_IB4 0x04 +#define TDA7802_IB5 0x05 + +#define TDA7802_DB0 0x10 +#define TDA7802_DB5 0x15 + +#define IB2_DIGITAL_MUTE_DISABLED (1 << 2) + +#define IB3_SAMPLE_RATE_SHIFT 6 +#define IB3_SAMPLE_RATE_MASK (3 << IB3_SAMPLE_RATE_SHIFT) +#define IB3_SAMPLE_RATE_44_KHZ (0 << IB3_SAMPLE_RATE_SHIFT) +#define IB3_SAMPLE_RATE_48_KHZ (1 << IB3_SAMPLE_RATE_SHIFT) +#define IB3_SAMPLE_RATE_96_KHZ (2 << IB3_SAMPLE_RATE_SHIFT) +#define IB3_SAMPLE_RATE_192_KHZ (3 << IB3_SAMPLE_RATE_SHIFT) +#define IB3_FORMAT_SHIFT 3 +#define IB3_FORMAT_MASK (7 << IB3_FORMAT_SHIFT) +#define IB3_FORMAT_I2S (0 << IB3_FORMAT_SHIFT) +#define IB3_FORMAT_TDM4 (1 << IB3_FORMAT_SHIFT) +#define IB3_FORMAT_TDM8_DEV1 (2 << IB3_FORMAT_SHIFT) +#define IB3_FORMAT_TDM8_DEV2 (3 << IB3_FORMAT_SHIFT) +#define IB3_FORMAT_TDM16_DEV1 (4 << IB3_FORMAT_SHIFT) +#define IB3_FORMAT_TDM16_DEV2 (5 << IB3_FORMAT_SHIFT) +#define IB3_FORMAT_TDM16_DEV3 (6 << IB3_FORMAT_SHIFT) +#define IB3_FORMAT_TDM16_DEV4 (7 << IB3_FORMAT_SHIFT) + +enum tda7802_type { + tda7802_base, +}; + +struct tda7802_priv { + struct i2c_client *i2c; + struct regmap *regmap; + struct regulator *enable_reg; +}; + +static const struct reg_default tda7802_reg[] = { + { TDA7802_IB0, 0x0 }, + { TDA7802_IB1, 0x0 }, + { TDA7802_IB2, 0x0 }, + { TDA7802_IB3, 0x0 }, + { TDA7802_IB4, 0x0 }, + { TDA7802_IB5, 0x0 }, +}; + +static bool tda7802_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TDA7802_IB0 ... TDA7802_IB5: + case TDA7802_DB0 ... TDA7802_DB5: + return true; + default: + return false; + } +} + +static bool tda7802_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TDA7802_DB0 ... TDA7802_DB5: + return true; + default: + return false; + } +} + +static bool tda7802_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TDA7802_IB0 ... TDA7802_IB5: + return true; + default: + return false; + } +} + +static const struct regmap_config tda7802_regmap_config = { + .val_bits = 8, + .reg_bits = 8, + .max_register = TDA7802_DB5, + .use_single_read = 1, + .use_single_write = 1, + + .readable_reg = tda7802_readable_reg, + .volatile_reg = tda7802_volatile_reg, + .writeable_reg = tda7802_writeable_reg, + + .reg_defaults = tda7802_reg, + .num_reg_defaults = ARRAY_SIZE(tda7802_reg), + .cache_type = REGCACHE_RBTREE, +}; + +static int tda7802_digital_mute(struct snd_soc_dai *dai, int mute) +{ + const u8 mute_disabled = mute ? 0 : IB2_DIGITAL_MUTE_DISABLED; + struct device *dev = dai->dev; + int err; + + dev_dbg(dev, "%s mute=%d\n", __func__, mute); + + err = snd_soc_component_update_bits(dai->component, TDA7802_IB2, + IB2_DIGITAL_MUTE_DISABLED, mute_disabled); + if (err < 0) + dev_err(dev, "Cannot mute amp %d\n", err); + + return err; +} + +static int tda7802_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct device *dev = dai->dev; + u8 tdm_format; + int ret; + + dev_dbg(dai->dev, "%s tx %x, rx %x, slots %d, slot_width %d\n", + __func__, tx_mask, rx_mask, slots, slot_width); + + switch (slots) { + case 4: + tdm_format = IB3_FORMAT_TDM4; + break; + case 8: + switch (tx_mask) { + case 0x000f: + tdm_format = IB3_FORMAT_TDM8_DEV1; + break; + case 0x00f0: + tdm_format = IB3_FORMAT_TDM8_DEV2; + break; + default: + dev_err(dev, + "Failed to set tdm fmt, slots %d, tx_mask %x\n", + slots, tx_mask); + return -ENOTSUPP; + } + break; + case 16: + switch (tx_mask) { + case 0x000f: + tdm_format = IB3_FORMAT_TDM16_DEV1; + break; + case 0x00f0: + tdm_format = IB3_FORMAT_TDM16_DEV2; + break; + case 0x0f00: + tdm_format = IB3_FORMAT_TDM16_DEV3; + break; + case 0xf000: + tdm_format = IB3_FORMAT_TDM16_DEV4; + break; + default: + dev_err(dev, + "Failed to set tdm fmt, slots %d, tx_mask %x\n", + slots, tx_mask); + return -ENOTSUPP; + } + break; + default: + dev_err(dev, "Failed to set %d slots, supported: 4, 8, 16\n", + slots); + return -ENOTSUPP; + } + + ret = snd_soc_component_update_bits(dai->component, TDA7802_IB3, + IB3_FORMAT_MASK, tdm_format); + if (ret < 0) { + dev_err(dev, "Failed to write IB3 config %d\n", ret); + return ret; + } + + return 0; +} + +static int tda7802_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int err; + u8 val; + + dev_dbg(dai->dev, "%s rate %d\n", __func__, params_rate(params)); + + switch (params_rate(params)) { + case 44100: + val = IB3_SAMPLE_RATE_44_KHZ; + break; + case 48000: + val = IB3_SAMPLE_RATE_48_KHZ; + break; + case 96000: + val = IB3_SAMPLE_RATE_96_KHZ; + break; + case 192000: + val = IB3_SAMPLE_RATE_192_KHZ; + break; + default: + return -EINVAL; + } + + err = snd_soc_component_update_bits(dai->component, TDA7802_IB3, + IB3_SAMPLE_RATE_MASK, val); + if (err < 0) + dev_err(dai->dev, "Could not set hw_params, %d\n", err); + + return err; +} + +static const struct snd_soc_dai_ops tda7802_dai_ops = { + .digital_mute = tda7802_digital_mute, + .hw_params = tda7802_hw_params, + .set_tdm_slot = tda7802_set_tdm_slot, +}; + +static struct snd_soc_dai_driver tda7802_dai_driver = { + .name = "tda7802", + .playback = { + .stream_name = "Playback", + .channels_min = 4, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &tda7802_dai_ops, +}; + +static int tda7802_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + const struct tda7802_priv *tda7802 = + snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm_context = + snd_soc_component_get_dapm(component); + const enum snd_soc_bias_level oldlevel = + snd_soc_dapm_get_bias_level(dapm_context); + int err = 0; + + dev_dbg(component->dev, "%s level %d\n", __func__, level); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + err = regulator_enable(tda7802->enable_reg); + if (err < 0) { + dev_err(component->dev, "Could not enable.\n"); + return err; + } + dev_dbg(component->dev, "Regulator enabled\n"); + msleep(ENABLE_DELAY_MS); + + if (oldlevel == SND_SOC_BIAS_OFF) { + dev_dbg(component->dev, "Syncing regcache\n"); + err = regcache_sync(component->regmap); + if (err < 0) + dev_err(component->dev, + "Could not sync regcache, %d\n", err); + } + break; + case SND_SOC_BIAS_OFF: + regcache_mark_dirty(component->regmap); + err = regulator_disable(tda7802->enable_reg); + if (err < 0) + dev_err(component->dev, "Could not disable.\n"); + break; + } + + return err; +} + +static const char * const amp_mode_str[] = { + "High Efficiency", + "Standard Class AB", +}; + +static SOC_ENUM_SINGLE_DECL(ch1_amp_mode, TDA7802_IB0, 0, amp_mode_str); +static SOC_ENUM_SINGLE_DECL(ch2_amp_mode, TDA7802_IB0, 1, amp_mode_str); +static SOC_ENUM_SINGLE_DECL(ch3_amp_mode, TDA7802_IB0, 2, amp_mode_str); +static SOC_ENUM_SINGLE_DECL(ch4_amp_mode, TDA7802_IB0, 3, amp_mode_str); + +static const char * const zopt_str[] = { + "2ohm", + "4ohm", +}; + +static SOC_ENUM_SINGLE_DECL(zopt_ch24, TDA7802_IB1, 7, zopt_str); +static SOC_ENUM_SINGLE_DECL(zopt_ch13, TDA7802_IB2, 0, zopt_str); + +static const char * const diag_timing_str[] = { + "default", + "x2", + "x4", + "x8", +}; + +static SOC_ENUM_SINGLE_DECL(diag_timing, TDA7802_IB1, 5, diag_timing_str); + +static const char * const mute_time_str[] = { + "1.45ms", + "5.8ms", + "11.6ms", + "23.2ms", + "46.4ms", + "92.8ms", + "185.6ms", + "371.2ms", +}; + +static SOC_ENUM_SINGLE_DECL(mute_time, TDA7802_IB2, 5, mute_time_str); + +static const char * const automute_threshold_str[] = { + "5.3V", + "7.3V", +}; + +static SOC_ENUM_SINGLE_DECL(automute_threshold, TDA7802_IB2, 1, + automute_threshold_str); + +static const char * const ac_diag_threshold_str[] = { + "High", + "Low", +}; + +static SOC_ENUM_SINGLE_DECL(ac_diag_threshold, TDA7802_IB3, 4, + ac_diag_threshold_str); + +static const char * const ch_diag_mode_str[] = { + "Speaker", + "Boosted", +}; + +static SOC_ENUM_SINGLE_DECL(diag_mode_ch13, TDA7802_IB4, 2, ch_diag_mode_str); +static SOC_ENUM_SINGLE_DECL(diag_mode_ch24, TDA7802_IB4, 1, ch_diag_mode_str); + +static const char * const temp_warn_str[] = { + "TW1", + "TW2", + "TW3", + "TW4", + "None", +}; + +static SOC_ENUM_SINGLE_DECL(temp_warn, TDA7802_IB5, 5, temp_warn_str); + +static const char * const clip_detect_str[] = { + "2%", + "5%", + "10%", + "None", +}; + +static SOC_ENUM_SINGLE_DECL(clip_detect_ch13, TDA7802_IB5, 3, clip_detect_str); +static SOC_ENUM_SINGLE_DECL(clip_detect_ch24, TDA7802_IB5, 1, clip_detect_str); + +static const struct snd_kcontrol_new tda7802_snd_controls[] = { + SOC_SINGLE("Channel 4 Tristate", TDA7802_IB0, 7, 1, 0), + SOC_SINGLE("Channel 3 Tristate", TDA7802_IB0, 6, 1, 0), + SOC_SINGLE("Channel 2 Tristate", TDA7802_IB0, 5, 1, 0), + SOC_SINGLE("Channel 1 Tristate", TDA7802_IB0, 4, 1, 0), + SOC_ENUM("Channel 4 Amplifier Mode", ch4_amp_mode), + SOC_ENUM("Channel 3 Amplifier Mode", ch3_amp_mode), + SOC_ENUM("Channel 2 Amplifier Mode", ch2_amp_mode), + SOC_ENUM("Channel 1 Amplifier Mode", ch1_amp_mode), + + /* Impedance (Z) efficiency optimiser */ + SOC_ENUM("Z efficiency opt channels 2 & 4", zopt_ch24), + SOC_ENUM("Z efficiency opt channels 1 & 3", zopt_ch13), + + SOC_ENUM("Long diag config timing", diag_timing), + SOC_SINGLE_RANGE("Gain channels 1 & 3", TDA7802_IB1, 3, 0, 3, 0), + SOC_SINGLE_RANGE("Gain channels 2 & 4", TDA7802_IB1, 1, 0, 3, 0), + SOC_SINGLE("Digital gain boost +6db", TDA7802_IB1, 0, 1, 0), + + /* Mute settings */ + SOC_ENUM("Mute time", mute_time), + SOC_SINGLE("Unmute channels 1 & 3", TDA7802_IB2, 4, 1, 0), + SOC_SINGLE("Unmute channels 2 & 4", TDA7802_IB2, 3, 1, 0), + SOC_ENUM("Automute threshold", automute_threshold), + + SOC_SINGLE("High pass filter enable", TDA7802_IB3, 0, 1, 0), + + /* Interactive diagnostics */ + SOC_SINGLE("Noise gating func enable", TDA7802_IB4, 7, 1, 1), + SOC_SINGLE("CDdiag: short fault", TDA7802_IB4, 6, 1, 1), + SOC_SINGLE("CDdiag: offset", TDA7802_IB4, 5, 1, 1), + SOC_ENUM("CDdiag: temperature warning", temp_warn), + SOC_ENUM("AC diag current threshold", ac_diag_threshold), + SOC_SINGLE("AC diag enable", TDA7802_IB4, 3, 1, 0), + SOC_ENUM("Diag mode channels 1 & 3", diag_mode_ch13), + SOC_ENUM("Diag mode channels 2 & 4", diag_mode_ch24), + SOC_SINGLE("Diag mode enable", TDA7802_IB4, 0, 1, 0), + + SOC_ENUM("Clipping detect channels 1 & 3", clip_detect_ch13), + SOC_ENUM("Clipping detect channels 2 & 4", clip_detect_ch24), +}; + +static const struct snd_soc_dapm_widget tda7802_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("AIFIN", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC", NULL, TDA7802_IB5, 0, 0), + SND_SOC_DAPM_OUTPUT("SPK"), +}; + +static const struct snd_soc_dapm_route tda7802_dapm_routes[] = { + { "AIFIN", NULL, "Playback" }, + { "DAC", NULL, "AIFIN" }, + { "SPK", NULL, "DAC" }, +}; + +static const struct snd_soc_component_driver tda7802_component_driver = { + .set_bias_level = tda7802_set_bias_level, + .idle_bias_on = 1, + .suspend_bias_off = 1, + .controls = tda7802_snd_controls, + .num_controls = ARRAY_SIZE(tda7802_snd_controls), + .dapm_widgets = tda7802_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tda7802_dapm_widgets), + .dapm_routes = tda7802_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tda7802_dapm_routes), +}; + +static int tda7802_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct tda7802_priv *tda7802; + int err; + + dev_dbg(dev, "%s addr=0x%02hx, id %p\n", __func__, i2c->addr, id); + + tda7802 = devm_kmalloc(dev, sizeof(*tda7802), GFP_KERNEL); + if (!tda7802) + return -ENOMEM; + + i2c_set_clientdata(i2c, tda7802); + tda7802->i2c = i2c; + + tda7802->enable_reg = devm_regulator_get(dev, "enable"); + if (IS_ERR(tda7802->enable_reg)) { + dev_err(dev, "Failed to get enable regulator\n"); + return PTR_ERR(tda7802->enable_reg); + } + + tda7802->regmap = devm_regmap_init_i2c(tda7802->i2c, + &tda7802_regmap_config); + if (IS_ERR(tda7802->regmap)) + return PTR_ERR(tda7802->regmap); + + err = devm_snd_soc_register_component(dev, &tda7802_component_driver, + &tda7802_dai_driver, 1); + if (err < 0) + dev_err(dev, "Failed to register codec: %d\n", err); + return err; +} + +#ifdef CONFIG_OF +static const struct of_device_id tda7802_of_match[] = { + { .compatible = "st,tda7802" }, + { }, +}; +MODULE_DEVICE_TABLE(of, tda7802_of_match); +#endif + +static const struct i2c_device_id tda7802_i2c_id[] = { + { "tda7802", tda7802_base }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, tda7802_i2c_id); + +static struct i2c_driver tda7802_i2c_driver = { + .driver = { + .name = "tda7802-codec", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tda7802_of_match), + }, + .probe = tda7802_i2c_probe, + .id_table = tda7802_i2c_id, +}; +module_i2c_driver(tda7802_i2c_driver); + +MODULE_DESCRIPTION("ASoC ST TDA7802 driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Rob Duncan "); +MODULE_AUTHOR("Thomas Preston "); From patchwork Tue Jul 30 12:09:37 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Preston X-Patchwork-Id: 11065919 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 79EAE1395 for ; Tue, 30 Jul 2019 14:37:15 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 68DF327861 for ; Tue, 30 Jul 2019 14:37:15 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5C61D287E8; Tue, 30 Jul 2019 14:37:15 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 040A6287C9 for ; Tue, 30 Jul 2019 14:37:14 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 161D917AD; Tue, 30 Jul 2019 14:10:11 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 161D917AD DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1564488661; bh=g7NGJhmYGFzjsLFqSpZTagwSE+ki0sw4c7jrIej8jtM=; h=From:To:Date:In-Reply-To:References:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=AyZwrQVL40D+sqghA4aisCRm425AuvH6k2NvIFWgwlFvd4iCLVC5BVnQnldb6Rbed XZLbkVvC+SV//wbK/qvuKPHikPrl60VNmi1T2Q9NlQQ3Dr4lDlsOCrOncL9D0wOkfE cEGw4d+BmMmpN5/ZjBzYOrl3K+uJvNQ3DaWwP8c4= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 722E6F80482; Tue, 30 Jul 2019 14:10:10 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa1.perex.cz (Postfix, from userid 50401) id 54914F80482; Tue, 30 Jul 2019 14:10:08 +0200 (CEST) Received: from imap1.codethink.co.uk (imap1.codethink.co.uk [176.9.8.82]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 1AFC2F800BF for ; Tue, 30 Jul 2019 14:10:04 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 1AFC2F800BF Received: from [167.98.27.226] (helo=ct-lt-1124.office.codethink.co.uk) by imap1.codethink.co.uk with esmtpsa (Exim 4.84_2 #1 (Debian)) id 1hsQwr-0003E4-LE; Tue, 30 Jul 2019 13:09:45 +0100 From: Thomas Preston To: Liam Girdwood , Mark Brown , Rob Herring , Mark Rutland , Jaroslav Kysela , Takashi Iwai , Charles Keepax , Jerome Brunet , Srinivas Kandagatla , Marco Felsch , Paul Cercueil , Kirill Marinushkin , Cheng-Yi Chiang , Kuninori Morimoto , Vinod Koul , Annaliese McDermond , Thomas Preston , alsa-devel@alsa-project.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Date: Tue, 30 Jul 2019 13:09:37 +0100 Message-Id: <20190730120937.16271-4-thomas.preston@codethink.co.uk> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190730120937.16271-1-thomas.preston@codethink.co.uk> References: <20190730120937.16271-1-thomas.preston@codethink.co.uk> MIME-Version: 1.0 Subject: [alsa-devel] [PATCH v2 3/3] ASoC: TDA7802: Add turn-on diagnostic routine X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" X-Virus-Scanned: ClamAV using ClamSMTP Add a debugfs device node which initiates the turn-on diagnostic routine feature of the TDA7802 amplifier. The four status registers (one per channel) are returned. Signed-off-by: Thomas Preston --- Changes since v1: - Rename speaker-test to (turn-on) diagnostics - Move turn-on diagnostic to debugfs as there is no standard ALSA interface for this kind of routine. sound/soc/codecs/tda7802.c | 186 ++++++++++++++++++++++++++++++++++++- 1 file changed, 185 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/tda7802.c b/sound/soc/codecs/tda7802.c index 0f82a88bc1a4..74436212241d 100644 --- a/sound/soc/codecs/tda7802.c +++ b/sound/soc/codecs/tda7802.c @@ -5,6 +5,7 @@ * Copyright (C) 2016-2019 Tesla Motors, Inc. */ +#include #include #include #include @@ -26,6 +27,7 @@ #define TDA7802_IB5 0x05 #define TDA7802_DB0 0x10 +#define TDA7802_DB1 0x11 #define TDA7802_DB5 0x15 #define IB2_DIGITAL_MUTE_DISABLED (1 << 2) @@ -47,6 +49,17 @@ #define IB3_FORMAT_TDM16_DEV3 (6 << IB3_FORMAT_SHIFT) #define IB3_FORMAT_TDM16_DEV4 (7 << IB3_FORMAT_SHIFT) +#define IB4_DIAG_MODE_ENABLE (1 << 0) + +#define IB5_AMPLIFIER_ON (1 << 0) + +#define DB0_STARTUP_DIAG_STATUS (1 << 6) + +#define DIAGNOSTIC_POLL_PERIOD_US 5000 +#define DIAGNOSTIC_TIMEOUT_US 5000000 +#define DIAGNOSTIC_SETTLE_MS 20 +#define NUM_IB 6 + enum tda7802_type { tda7802_base, }; @@ -55,6 +68,12 @@ struct tda7802_priv { struct i2c_client *i2c; struct regmap *regmap; struct regulator *enable_reg; + struct dentry *debugfs; + struct mutex diagnostic_mutex; +}; + +struct reg_update { + unsigned int reg, mask, val; }; static const struct reg_default tda7802_reg[] = { @@ -113,6 +132,19 @@ static const struct regmap_config tda7802_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +/* The following bits need to be updated before diagnostics mode is set. */ +static const struct reg_update diagnostic_state[NUM_IB] = { + { TDA7802_IB0, 0, 0 }, + { TDA7802_IB1, 0, 0 }, + { TDA7802_IB2, + IB2_CH13_UNMUTED | IB2_CH24_UNMUTED | IB2_DIGITAL_MUTE_DISABLED, + IB2_CH13_UNMUTED | IB2_CH24_UNMUTED | IB2_DIGITAL_MUTE_DISABLED, + }, + { TDA7802_IB3, 0, 0 }, + { TDA7802_IB4, IB4_DIAG_MODE_ENABLE, 0 }, + { TDA7802_IB5, IB5_AMPLIFIER_ON, 0 }, +}; + static int tda7802_digital_mute(struct snd_soc_dai *dai, int mute) { const u8 mute_disabled = mute ? 0 : IB2_DIGITAL_MUTE_DISABLED; @@ -414,7 +446,6 @@ static const struct snd_kcontrol_new tda7802_snd_controls[] = { SOC_SINGLE("AC diag enable", TDA7802_IB4, 3, 1, 0), SOC_ENUM("Diag mode channels 1 & 3", diag_mode_ch13), SOC_ENUM("Diag mode channels 2 & 4", diag_mode_ch24), - SOC_SINGLE("Diag mode enable", TDA7802_IB4, 0, 1, 0), SOC_ENUM("Clipping detect channels 1 & 3", clip_detect_ch13), SOC_ENUM("Clipping detect channels 2 & 4", clip_detect_ch24), @@ -432,7 +463,160 @@ static const struct snd_soc_dapm_route tda7802_dapm_routes[] = { { "SPK", NULL, "DAC" }, }; +static int tda7802_bulk_update(struct regmap *map, struct reg_update *update, + size_t update_count) +{ + int i, err; + + for (i = 0; i < update_count; i++) { + err = regmap_update_bits(map, update[i].reg, update[i].mask, + update[i].val); + if (err < 0) + return err; + } + + return i; +} + +static int run_turn_on_diagnostic(struct tda7802_priv *tda7802, u8 *status) +{ + struct device *dev = &tda7802->i2c->dev; + int err_status, err; + unsigned int val; + u8 state[NUM_IB]; + + dev_dbg(dev, "%s\n", __func__); + + /* Save state and prepare for diagnostic */ + err = regmap_bulk_read(tda7802->regmap, TDA7802_IB0, state, + ARRAY_SIZE(state)); + if (err < 0) { + dev_err(dev, "Could not save device state, %d\n", err); + return err; + } + + err = tda7802_bulk_update(tda7802->regmap, diagnostic_state, + ARRAY_SIZE(diagnostic_state)); + if (err < 0) { + dev_err(dev, "Could not prepare for diagnostics, %d\n", err); + goto diagnostic_restore; + } + + /* We must wait 20ms for device to settle, otherwise diagnostics will + * not start and regmap poll will timeout. + */ + msleep(DIAGNOSTIC_SETTLE_MS); + + /* Turn on diagnostic */ + err = regmap_update_bits(tda7802->regmap, TDA7802_IB4, + IB4_DIAG_MODE_ENABLE, IB4_DIAG_MODE_ENABLE); + if (err < 0) { + dev_err(dev, "Could not enable diagnostic mode, %d\n", err); + goto diagnostic_restore; + } + + /* Wait until DB0_STARTUP_DIAG_STATUS is set, then read status */ + err_status = regmap_read_poll_timeout(tda7802->regmap, TDA7802_DB0, val, + val & DB0_STARTUP_DIAG_STATUS, + DIAGNOSTIC_POLL_PERIOD_US, + DIAGNOSTIC_TIMEOUT_US); + if (err_status < 0) { + dev_err(dev, "Diagnostic did not complete, err %d, reg %x", + err_status, val); + goto diagnostic_restore; + } + + err = regmap_bulk_read(tda7802->regmap, TDA7802_DB1, status, 4); + if (err < 0) { + dev_err(dev, "Could not read channel status, %d\n", err); + goto diagnostic_restore; + } + +diagnostic_restore: + err = regmap_bulk_write(tda7802->regmap, TDA7802_IB0, state, + ARRAY_SIZE(state)); + if (err < 0) + dev_err(dev, "Could not restore state, %d\n", err); + + if (err_status < 0) + return err_status; + else + return err; +} + +static int tda7802_diagnostic_show(struct seq_file *f, void *p) +{ + char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + struct tda7802_priv *tda7802 = f->private; + u8 status[4] = { 0 }; + int i, err; + + mutex_lock(&tda7802->diagnostic_mutex); + err = run_turn_on_diagnostic(tda7802, status); + mutex_unlock(&tda7802->diagnostic_mutex); + if (err < 0) + return err; + + for (i = 0; i < ARRAY_SIZE(status); i++) + seq_printf(f, "%02x: %02x\n", TDA7802_DB1+i, status[i]); + + return 0; +} + +static int tda7802_diagnostic_open(struct inode *inode, struct file *file) +{ + return single_open(file, tda7802_diagnostic_show, inode->i_private); +} + +static const struct file_operations tda7802_diagnostic_fops = { + .open = tda7802_diagnostic_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int tda7802_probe(struct snd_soc_component *component) +{ + struct tda7802_priv *tda7802 = snd_soc_component_get_drvdata(component); + struct device *dev = &tda7802->i2c->dev; + int err; + + tda7802->debugfs = debugfs_create_dir(dev_name(dev), NULL); + if (IS_ERR_OR_NULL(tda7802->debugfs)) { + dev_info(dev, + "Failed to create debugfs node, err %ld\n", + PTR_ERR(tda7802->debugfs)); + return 0; + } + + mutex_init(&tda7802->diagnostic_mutex); + err = debugfs_create_file("diagnostic", 0444, tda7802->debugfs, tda7802, + &tda7802_diagnostic_fops); + if (err < 0) { + dev_err(dev, + "debugfs: Failed to create diagnostic node, err %d\n", + err); + goto cleanup_diagnostic; + } + + return 0; + +cleanup_diagnostic: + mutex_destroy(&tda7802->diagnostic_mutex); + return err; +} + +static void tda7802_remove(struct snd_soc_component *component) +{ + struct tda7802_priv *tda7802 = snd_soc_component_get_drvdata(component); + + debugfs_remove_recursive(tda7802->debugfs); + mutex_destroy(&tda7802->diagnostic_mutex); +} + static const struct snd_soc_component_driver tda7802_component_driver = { + .probe = tda7802_probe, + .remove = tda7802_remove, .set_bias_level = tda7802_set_bias_level, .idle_bias_on = 1, .suspend_bias_off = 1,