Alsa-devel
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
March 2023
- 147 participants
- 432 discussions
Re: [PATCH v2 1/7] dt-bindings: misc: Add the Lantiq PEF2466 E1/T1/J1 framer
by Rob Herring 22 Mar '23
by Rob Herring 22 Mar '23
22 Mar '23
On Mon, Mar 20, 2023 at 10:46:19AM +0100, Herve Codina via Alsa-devel wrote:
> Received: by alsa1.perex.cz (Postfix, from userid 50401) id 16494F8027B;
> Mon, 20 Mar 2023 10:46:37 +0100 (CET)
> X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on alsa1.perex.cz
> X-Spam-Level:
> X-Spam-Status: No, score=-5.2 required=5.0 tests=DKIM_SIGNED,DKIM_VALID,
> DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_HI,SPF_HELO_NONE,SPF_PASS,
> URIBL_BLOCKED shortcircuit=no autolearn=ham autolearn_force=no
> version=3.4.6
> Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net
> [217.70.183.198]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384
> (256/256 bits)) (No client certificate requested) by alsa1.perex.cz
> (Postfix) with ESMTPS id 3FF5FF80105 for <alsa-devel(a)alsa-project.org>;
> Mon, 20 Mar 2023 10:46:22 +0100 (CET)
> DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 3FF5FF80105
> Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key,
> unprotected) header.d=bootlin.com header.i=(a)bootlin.com
> header.a=rsa-sha256 header.s=gm1 header.b=m4O7nLC1
> Received: (Authenticated sender: herve.codina(a)bootlin.com) by
> mail.gandi.net (Postfix) with ESMTPSA id 40453C0009; Mon, 20 Mar 2023
> 09:46:20 +0000 (UTC)
> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1;
> t=1679305582;
> h=from:from:reply-to:subject:subject:date:date:message-id:message-id:
> to:to:cc:cc:mime-version:mime-version:content-type:content-type:
> content-transfer-encoding:content-transfer-encoding:
> in-reply-to:in-reply-to:references:references;
> bh=Ieu9Fv38se4lD4z/BVXUHLrVJL9Tx5iKWZgvO8X+VoY=;
> b=m4O7nLC1LPZDOI5eM/hmgqouxdkin2veA6CvJhT9kU9rGQALB3ya2fuybMfDvrkTqqBjEd
> j6DAxXMgOKgwuUfEsZsp3BFJpoii00hSaf0r2uIbnnGcUrDGVQqUQVEqv51O6VBqnrViQk
> PstlJM0lcE9R/AFASd5D/HQGoYYyRY+NKT7xt8g1Ax23Yk/tUG59LXku/skn/4faSLodnU
> vV2ng3VMUcoLuvSMJtdYY3hrXEWqUrW1ZogxAFHJNiKuyOELmqZGmNo4B4yAFOEcqqyano
> /f4m/7BtT7X1wwPvGu29gg+0aOFrGQq5kb4UNrMoriSQyKnxPRha8zL3J2Jckw==
> Date: Mon, 20 Mar 2023 10:46:19 +0100
> From: Herve Codina <herve.codina(a)bootlin.com>
> To: Krzysztof Kozlowski <krzysztof.kozlowski(a)linaro.org>
> Subject: Re: [PATCH v2 1/7] dt-bindings: misc: Add the Lantiq PEF2466
> E1/T1/J1 framer
> Message-ID: <20230320104619.468a304b(a)bootlin.com>
> In-Reply-To: <96b01241-d57d-a460-4a8b-9e83eaab24ae(a)linaro.org>
> References: <20230316122741.577663-1-herve.codina(a)bootlin.com>
> <20230316122741.577663-2-herve.codina(a)bootlin.com>
> <96b01241-d57d-a460-4a8b-9e83eaab24ae(a)linaro.org>
> Organization: Bootlin
> X-Mailer: Claws Mail 4.1.1 (GTK 3.24.37; x86_64-redhat-linux-gnu)
> MIME-Version: 1.0
> Content-Type: text/plain; charset=UTF-8
> Content-Transfer-Encoding: quoted-printable
> Message-ID-Hash: AJZF4VHU24ASVVBCPRMLJCDG4ZDX55LB
> X-Message-ID-Hash: AJZF4VHU24ASVVBCPRMLJCDG4ZDX55LB
> X-MailFrom: herve.codina(a)bootlin.com
> X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency;
> loop; banned-address; member-moderation;
> header-match-alsa-devel.alsa-project.org-0;
> header-match-alsa-devel.alsa-project.org-1; nonmember-moderation;
> administrivia; implicit-dest; max-recipients; max-size; news-moderation;
> no-subject; digests; suspicious-header
> CC: Rob Herring <robh+dt(a)kernel.org>, Krzysztof Kozlowski
> <krzysztof.kozlowski+dt(a)linaro.org>, Liam Girdwood <lgirdwood(a)gmail.com>,
> Mark Brown <broonie(a)kernel.org>, Derek Kiernan <derek.kiernan(a)xilinx.com>,
> Dragan Cvetic <dragan.cvetic(a)xilinx.com>, Arnd Bergmann <arnd(a)arndb.de>,
> Greg Kroah-Hartman <gregkh(a)linuxfoundation.org>, Takashi Iwai
> <tiwai(a)suse.com>, linux-kernel(a)vger.kernel.org,
> devicetree(a)vger.kernel.org, alsa-devel(a)alsa-project.org, Christophe Leroy
> <christophe.leroy(a)csgroup.eu>, Thomas Petazzoni
> <thomas.petazzoni(a)bootlin.com>
> X-Mailman-Version: 3.3.8
> Precedence: list
> List-Id: "Alsa-devel mailing list for ALSA developers -
> http://www.alsa-project.org" <alsa-devel.alsa-project.org>
> Archived-At: <https://mailman.alsa-project.org/hyperkitty/list/alsa-devel@alsa-project.or…>
> List-Archive: <https://mailman.alsa-project.org/hyperkitty/list/alsa-devel@alsa-project.or…>
> List-Help: <mailto:alsa-devel-request@alsa-project.org?subject=help>
> List-Owner: <mailto:alsa-devel-owner@alsa-project.org>
> List-Post: <mailto:alsa-devel@alsa-project.org>
> List-Subscribe: <mailto:alsa-devel-join@alsa-project.org>
> List-Unsubscribe: <mailto:alsa-devel-leave@alsa-project.org>
The alsa-devel list doesn't seem to like your emails. The archives
(lore) has 2 copies with the 2nd having the original headers in the
body. I'm seeing this recently on other senders too. Best I can tell is
you sent this as quoted-printable.
Rob
2
3
Hi,
Speaker-webcam eMeet C980 Pro does not play, outputs only silent noise.
The digital volume seems to be down.
dmesg:
[22406.805176] input: HD Webcam eMeet C980 Pro: HD We as
/devices/pci0000:00/0000:00:01.2/0000:02:00.0/usb1/1-7/1-7.1/1-7.1:1.0/input/input27
[22406.829048] usb 1-7.1: current rate 16000 is different from the
runtime rate 48000
[22406.933044] usb 1-7.1: Warning! Unlikely big volume range (=10240),
cval->res is probably wrong.
[22406.933048] usb 1-7.1: [5] FU [PCM Playback Volume] ch = 1, val =
-7168/3072/1
[22407.001043] usb 1-7.1: Warning! Unlikely big volume range (=3072),
cval->res is probably wrong.
[22407.001047] usb 1-7.1: [6] FU [Headset Capture Volume] ch = 1, val =
-1536/1536/1
amixer contents:
.....
numid=3,iface=MIXER,name='PCM Playback Switch'
; type=BOOLEAN,access=rw------,values=1
: values=on
numid=4,iface=MIXER,name='PCM Playback Volume'
; type=INTEGER,access=rw---R--,values=1,min=0,max=10240,step=0
: values=10240
| dBminmax-min=-28.00dB,max=12.00dB
numid=5,iface=MIXER,name='Headset Capture Switch'
; type=BOOLEAN,access=rw------,values=1
: values=on
numid=6,iface=MIXER,name='Headset Capture Volume'
; type=INTEGER,access=rw---R--,values=1,min=0,max=3072,step=0
: values=3072
| dBminmax-min=-6.00dB,max=6.00dB
.....
I tried playing with csetting numid=4 to various values, no effect.
Please what should be the correct min,max,res in volume_control_quirks
for the 'PCM Playback Volume' control for such device?
Capture seems to be OK (maybe the range should be adjusted too for the
complete range of control)
Also, the device does not seem to get the current rate correctly, but
IIUC that should not have any adverse effect (not marked with
QUIRK_FLAG_GET_SAMPLE_RATE yet):
[22407.178045] usb 1-7.1: current rate 16000 is different from the
runtime rate 48000
[22407.199046] usb 1-7.1: current rate 16000 is different from the
runtime rate 48000
[22407.220045] usb 1-7.1: current rate 16000 is different from the
runtime rate 48000
[22407.282044] usb 1-7.1: current rate 16000 is different from the
runtime rate 48000
[22811.235851] usb 1-7.1: current rate 16000 is different from the
runtime rate 48000
[22829.335494] usb 1-7.1: current rate 16000 is different from the
runtime rate 48000
cat /proc/asound/card2/stream0
SHENZHEN EMEET TECHNOLOGY CO., LTD. HD Webcam eMeet C980 Pro at
usb-0000:02:00. : USB Audio
Playback:
Status: Stop
Interface 3
Altset 1
Format: S16_LE
Channels: 1
Endpoint: 0x01 (1 OUT) (ADAPTIVE)
Rates: 48000
Data packet interval: 1000 us
Bits: 16
Channel map: FL
Capture:
Status: Stop
Interface 4
Altset 1
Format: S16_LE
Channels: 1
Endpoint: 0x83 (3 IN) (SYNC)
Rates: 16000
Data packet interval: 1000 us
Bits: 16
alsa-info dump:
http://alsa-project.org/db/?f=7af42635d88e858818932578aa8445810916a947
lsusb -v -d 328f:1912 attached.
Thank you very much for any hints.
Best regards,
Pavel.
2
2
22 Mar '23
> > > This introduces a DT property but there's no documentation for it,
but I
> > > don't see why we'd want this in the bindings - the driver should be
able
> > > to tell from the input clock rate and required output/internal clocks
if
> > > it needs to divide MCLK.
>
> > The problem here is that I have no knowledge what is the maximum MCLK
> > that the codec accepts. According to the datasheet the maximum supported
> > frequency of MCLK is 51.2 Mhz. But this doesn't seem to be the case in
> > practice since a MCLK of 48Mhz causes noises in the sound output.
> > The idea to divide the MCLK by 2 was proposed by a Everest Semiconductor
> > engineer.
> > So I don't know how to make this generic enough to be activated from the
> > codec driver.
>
> The usual constraint would be that MCLK can be at most some multiple of
> LRCLK or something similar (are all the other dividers in the chip set
> sensibly for the full scale MCLK?). In any case you're clearly aware of
> a specific case where it needs to be divided down which can be
> identified even if you're concerned about dividing down for other cases.
In practice no dt property is needed. We support 48M mclk, based on enabling
MCLK divided by 2 for mclk greater than 24M . You may want code like this:
/* enabling mclk divided by 2 for mclk > 24M */
if (es8316->sysclk > 24000000)
mclk_div_option = true;
1
0
[PATCH] ASoC: simple-card: add comment to indicate don't remove platforms
by Kuninori Morimoto 22 Mar '23
by Kuninori Morimoto 22 Mar '23
22 Mar '23
Basically CPU and Platform are different Component, but if CPU is using
soc-generic-dmaengine-pcm, same dev will be shared between CPU and
Platform, and Simple Card had been supporting it.
When we focus to clean up Simple Card driver, we tend to remove platforms
if no Platform was selected, but it is wrong because of above reasons.
This patch adds comment why we shouldn't remove platforms.
In case of CPU is not using soc-generic-dmaengine-pcm, CPU and Platform
will be duplicated, but it will be ignored by snd_soc_rtd_add_component().
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx(a)renesas.com>
---
sound/soc/generic/audio-graph-card.c | 13 ++++++++++++-
sound/soc/generic/audio-graph-card2.c | 17 +++++++++++++++++
sound/soc/generic/simple-card.c | 19 ++++++++++++++++++-
3 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index d788f5f23a8a..4e85536a1b26 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -613,10 +613,16 @@ static int graph_count_noml(struct asoc_simple_priv *priv,
return -EINVAL;
}
+ /*
+ * DON'T REMOVE platforms
+ * see
+ * simple-card.c :: simple_count_noml()
+ */
li->num[li->link].cpus = 1;
- li->num[li->link].codecs = 1;
li->num[li->link].platforms = 1;
+ li->num[li->link].codecs = 1;
+
li->link += 1; /* 1xCPU-Codec */
dev_dbg(dev, "Count As Normal\n");
@@ -637,6 +643,11 @@ static int graph_count_dpcm(struct asoc_simple_priv *priv,
}
if (li->cpu) {
+ /*
+ * DON'T REMOVE platforms
+ * see
+ * simple-card.c :: simple_count_noml()
+ */
li->num[li->link].cpus = 1;
li->num[li->link].platforms = 1;
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c
index 259544f64df9..d145b74d41e4 100644
--- a/sound/soc/generic/audio-graph-card2.c
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -1046,8 +1046,14 @@ static int graph_count_normal(struct asoc_simple_priv *priv,
* => lnk: port { endpoint { .. }; };
* };
*/
+ /*
+ * DON'T REMOVE platforms
+ * see
+ * simple-card.c :: simple_count_noml()
+ */
li->num[li->link].cpus =
li->num[li->link].platforms = graph_counter(cpu_port);
+
li->num[li->link].codecs = graph_counter(codec_port);
of_node_put(cpu_ep);
@@ -1079,6 +1085,11 @@ static int graph_count_dpcm(struct asoc_simple_priv *priv,
*/
if (asoc_graph_is_ports0(lnk)) {
+ /*
+ * DON'T REMOVE platforms
+ * see
+ * simple-card.c :: simple_count_noml()
+ */
li->num[li->link].cpus = graph_counter(rport); /* FE */
li->num[li->link].platforms = graph_counter(rport);
} else {
@@ -1113,8 +1124,14 @@ static int graph_count_c2c(struct asoc_simple_priv *priv,
* };
* };
*/
+ /*
+ * DON'T REMOVE platforms
+ * see
+ * simple-card.c :: simple_count_noml()
+ */
li->num[li->link].cpus =
li->num[li->link].platforms = graph_counter(codec0);
+
li->num[li->link].codecs = graph_counter(codec1);
of_node_put(ports);
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index e98932c16754..fc044978e057 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -509,10 +509,22 @@ static int simple_count_noml(struct asoc_simple_priv *priv,
return -EINVAL;
}
+ /*
+ * DON'T REMOVE platforms
+ *
+ * Some CPU might be using soc-generic-dmaengine-pcm. This means CPU and Platform
+ * are different Component, but are sharing same component->dev.
+ * Simple Card had been supported it without special Platform selection.
+ * We need platforms here.
+ *
+ * In case of no Platform, it will be Platform == CPU, but Platform will be
+ * ignored by snd_soc_rtd_add_component().
+ */
li->num[li->link].cpus = 1;
- li->num[li->link].codecs = 1;
li->num[li->link].platforms = 1;
+ li->num[li->link].codecs = 1;
+
li->link += 1;
return 0;
@@ -531,6 +543,11 @@ static int simple_count_dpcm(struct asoc_simple_priv *priv,
}
if (li->cpu) {
+ /*
+ * DON'T REMOVE platforms
+ * see
+ * simple_count_noml()
+ */
li->num[li->link].cpus = 1;
li->num[li->link].platforms = 1;
--
2.25.1
1
1
Create tas2781 driver.
Signed-off-by: Shenghao Ding <13916275206(a)139.com>
---
Changes in v5:
- fixed indentation issues
- correct header file in alphabetical order
- remove useless initialization
- Add comments for lock
| Reported-by: kernel test robot <lkp(a)intel.com>
| Link:
| https://lore.kernel.org/oe-kbuild-all/202303210435.ij3d6YHB-lkp@intel.
| com/
- remove unused is_set_glb_mode
- remove unused dts_dev_addr_tag
| Reported-by: kernel test robot <lkp(a)intel.com>
| Link:
| https://lore.kernel.org/oe-kbuild-all/202303210419.sTqbh3R4-lkp@intel.
| com/
- fixed Using plain integer as NULL pointer
Changes to be committed:
modified: sound/soc/codecs/Kconfig
modified: sound/soc/codecs/Makefile
new file: sound/soc/codecs/tas2781-dsp.c
new file: sound/soc/codecs/tas2781-dsp.h
new file: sound/soc/codecs/tas2781-i2c.c
new file: sound/soc/codecs/tas2781.h
---
sound/soc/codecs/Kconfig | 12 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/tas2781-dsp.c | 2152 ++++++++++++++++++++++++++++++++
sound/soc/codecs/tas2781-dsp.h | 178 +++
sound/soc/codecs/tas2781-i2c.c | 1665 ++++++++++++++++++++++++
sound/soc/codecs/tas2781.h | 169 +++
6 files changed, 4178 insertions(+)
create mode 100644 sound/soc/codecs/tas2781-dsp.c
create mode 100644 sound/soc/codecs/tas2781-dsp.h
create mode 100644 sound/soc/codecs/tas2781-i2c.c
create mode 100644 sound/soc/codecs/tas2781.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 07747565c3b5..e1afe1ced5d7 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -229,6 +229,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_TAS2764
imply SND_SOC_TAS2770
imply SND_SOC_TAS2780
+ imply SND_SOC_TAS2781
imply SND_SOC_TAS5086
imply SND_SOC_TAS571X
imply SND_SOC_TAS5720
@@ -1637,6 +1638,17 @@ config SND_SOC_TAS2780
Enable support for Texas Instruments TAS2780 high-efficiency
digital input mono Class-D audio power amplifiers.
+config SND_SOC_TAS2781
+ tristate "Texas Instruments TAS2781 speaker amplifier"
+ depends on I2C
+ select REGMAP_I2C
+ select CRC8
+ help
+ Enable support for Texas Instruments TAS2781 Smart Amplifier
+ Digital input mono Class-D and DSP-inside audio power amplifiers.
+ Note the TAS2781 driver implements a flexible and configurable
+ algo coff setting, for one, two, even multiple TAS2781 chips.
+
config SND_SOC_TAS5086
tristate "Texas Instruments TAS5086 speaker amplifier"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index f1ca18f7946c..5559b9e9cc17 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -262,6 +262,7 @@ snd-soc-tas5805m-objs := tas5805m.o
snd-soc-tas6424-objs := tas6424.o
snd-soc-tda7419-objs := tda7419.o
snd-soc-tas2770-objs := tas2770.o
+snd-soc-tas2781-objs := tas2781-i2c.o tas2781-dsp.o
snd-soc-tfa9879-objs := tfa9879.o
snd-soc-tfa989x-objs := tfa989x.o
snd-soc-tlv320adc3xxx-objs := tlv320adc3xxx.o
@@ -619,6 +620,7 @@ obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o
obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o
obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o
+obj-$(CONFIG_SND_SOC_TAS2781) += snd-soc-tas2781.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
diff --git a/sound/soc/codecs/tas2781-dsp.c b/sound/soc/codecs/tas2781-dsp.c
new file mode 100644
index 000000000000..22c256237335
--- /dev/null
+++ b/sound/soc/codecs/tas2781-dsp.c
@@ -0,0 +1,2152 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+//
+// Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2781 driver implements a flexible and configurable algo coff setting
+// for one, two, even multiple TAS2781 chips.
+//
+// Author: Shenghao Ding <shenghao-ding(a)ti.com>
+// Author: Kevin Lu <kevin-lu(a)ti.com>
+//
+
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "tas2781.h"
+
+#define ERROR_PRAM_CRCCHK 0x0000000
+#define ERROR_YRAM_CRCCHK 0x0000001
+#define PPC_DRIVER_CRCCHK 0x00000200
+
+#define TAS2781_SA_COEFF_SWAP_REG TASDEVICE_REG(0, 0x35, 0x2c)
+#define TAS2781_YRAM_BOOK1 140
+#define TAS2781_YRAM1_PAGE 42
+#define TAS2781_YRAM1_START_REG 88
+
+#define TAS2781_YRAM2_START_PAGE 43
+#define TAS2781_YRAM2_END_PAGE 49
+#define TAS2781_YRAM2_START_REG 8
+#define TAS2781_YRAM2_END_REG 127
+
+#define TAS2781_YRAM3_PAGE 50
+#define TAS2781_YRAM3_START_REG 8
+#define TAS2781_YRAM3_END_REG 27
+
+/*should not include B0_P53_R44-R47 */
+#define TAS2781_YRAM_BOOK2 0
+#define TAS2781_YRAM4_START_PAGE 50
+#define TAS2781_YRAM4_END_PAGE 60
+
+#define TAS2781_YRAM5_PAGE 61
+#define TAS2781_YRAM5_START_REG 8
+#define TAS2781_YRAM5_END_REG 27
+
+#define TASDEVICE_MAXPROGRAM_NUM_KERNEL 5
+#define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS 64
+#define TASDEVICE_MAXCONFIG_NUM_KERNEL 10
+#define MAIN_ALL_DEVICES_1X 0x01
+#define MAIN_DEVICE_A_1X 0x02
+#define MAIN_DEVICE_B_1X 0x03
+#define MAIN_DEVICE_C_1X 0x04
+#define MAIN_DEVICE_D_1X 0x05
+#define COEFF_DEVICE_A_1X 0x12
+#define COEFF_DEVICE_B_1X 0x13
+#define COEFF_DEVICE_C_1X 0x14
+#define COEFF_DEVICE_D_1X 0x15
+#define PRE_DEVICE_A_1X 0x22
+#define PRE_DEVICE_B_1X 0x23
+#define PRE_DEVICE_C_1X 0x24
+#define PRE_DEVICE_D_1X 0x25
+
+struct tas_crc {
+ unsigned char offset;
+ unsigned char len;
+};
+
+const char deviceNumber[TASDEVICE_DSP_TAS_MAX_DEVICE] = {
+ 1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4
+};
+
+static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->type = SMS_HTONL(data[offset],
+ data[offset + 1], data[offset + 2],
+ data[offset + 3]);
+ offset += 4;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: PChkSumPresent error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->is_pchksum_present = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnPChkSum error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->pchksum = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: YChkSumPresent error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->is_ychksum_present = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnYChkSum error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->ychksum = data[offset];
+ offset++;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: blk_size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->blk_size = SMS_HTONL(data[offset], data[offset + 1],
+ data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->nr_subblocks = SMS_HTONL(data[offset], data[offset + 1],
+ data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ block->data = kzalloc(block->blk_size, GFP_KERNEL);
+ if (block->data == NULL) {
+ offset = -1;
+ goto out;
+ }
+ memcpy(block->data, &data[offset], block->blk_size);
+ offset += block->blk_size;
+out:
+ return offset;
+}
+
+static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw,
+ struct tasdevice_data *img_data, const struct firmware *fmw,
+ int offset)
+{
+ const unsigned char *data = fmw->data;
+ unsigned int nr_block;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ img_data->nr_blk = SMS_HTONL(data[offset],
+ data[offset + 1], data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ img_data->dev_blks =
+ kcalloc(img_data->nr_blk, sizeof(struct tasdev_blk),
+ GFP_KERNEL);
+ if (img_data->dev_blks == NULL) {
+ dev_err(tas_fmw->dev, "%s: FW memory failed!\n", __func__);
+ goto out;
+ }
+
+ for (nr_block = 0; nr_block < img_data->nr_blk; nr_block++) {
+ offset = fw_parse_block_data_kernel(tas_fmw,
+ &(img_data->dev_blks[nr_block]), fmw, offset);
+ if (offset < 0) {
+ offset = -1;
+ goto out;
+ }
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_program_data_kernel(
+ struct tasdevice_priv *tas_dev, struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset)
+{
+ struct tasdevice_prog *program;
+ unsigned int nr_program;
+
+ for (nr_program = 0; nr_program < tas_fmw->nr_programs; nr_program++) {
+ program = &(tas_fmw->programs[nr_program]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpName error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 64;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: AppMode error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "PDMI2SMode err\n");
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: ISnsPD error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: VSnsPD error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ //skip 3-byte reserved
+ offset += 3;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: PowerLDG err\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_configuration_data_kernel(
+ struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+ unsigned int nr_conf;
+ struct tasdevice_config *config;
+
+ for (nr_conf = 0; nr_conf < tas_fmw->nr_configurations; nr_conf++) {
+ config = &(tas_fmw->configs[nr_conf]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpName error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ memcpy(config->name, &data[offset], 64);
+ offset += 64;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "orientation err\n");
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: Devices error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 1;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: Program error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "SamplingRate err\n");
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: PLLSrc error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: FsRate error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: PLLRate err\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ offset = fw_parse_data_kernel(tas_fmw,
+ &(config->dev_data), fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_variable_header_kernel(
+ struct tasdevice_priv *tas_dev, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ const unsigned char *buf = fmw->data;
+ struct tasdevice_prog *program;
+ struct tasdevice_config *config;
+ unsigned int nr_program, nr_configs;
+ unsigned short max_confs;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device_family = SMS_HTONS(buf[offset], buf[offset + 1]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_dev->dev, "ERROR:%s:not TAS device\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnDevice error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device = SMS_HTONS(buf[offset], buf[offset + 1]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_dev->dev, "Not support dev %d\n", fw_hdr->device);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+ fw_hdr->ndev = deviceNumber[fw_hdr->device];
+
+ if (fw_hdr->ndev != tas_dev->ndev) {
+ dev_err(tas_dev->dev,
+ "%s: ndev(%u) in dspbin dismatch ndev(%u) in DTS\n",
+ __func__, fw_hdr->ndev, tas_dev->ndev);
+ offset = -1;
+ goto out;
+ }
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPrograms error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->nr_programs = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+
+ if (tas_fmw->nr_programs == 0 || tas_fmw->nr_programs >
+ TASDEVICE_MAXPROGRAM_NUM_KERNEL) {
+ dev_err(tas_dev->dev, "%s: mnPrograms is invalid\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ if (offset + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpPrograms error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ tas_fmw->programs = kcalloc(tas_fmw->nr_programs,
+ sizeof(struct tasdevice_prog), GFP_KERNEL);
+ if (tas_fmw->programs == NULL) {
+ offset = -1;
+ goto out;
+ }
+
+ for (nr_program = 0; nr_program < tas_fmw->nr_programs; nr_program++) {
+ program = &(tas_fmw->programs[nr_program]);
+ program->prog_size = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ }
+ offset += (4 * (5 - nr_program));
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: Configurations error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->nr_configurations = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ max_confs = (fw_hdr->ndev >= 4) ?
+ TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS :
+ TASDEVICE_MAXCONFIG_NUM_KERNEL;
+ if (tas_fmw->nr_configurations == 0 ||
+ tas_fmw->nr_configurations > max_confs) {
+ dev_err(tas_dev->dev, "%s: Conf is invalid\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ if (offset + 4 * max_confs > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpConfigurations err\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ tas_fmw->configs = kcalloc(tas_fmw->nr_configurations,
+ sizeof(struct tasdevice_config), GFP_KERNEL);
+ if (tas_fmw->configs == NULL) {
+ offset = -1;
+ goto out;
+ }
+
+ for (nr_configs = 0; nr_configs < tas_fmw->nr_programs;
+ nr_configs++) {
+ config =
+ &(tas_fmw->configs[nr_configs]);
+ config->cfg_size = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ }
+
+ offset += (4 * (max_confs - nr_configs));
+out:
+ return offset;
+}
+
+static int tasdevice_load_block_kernel(
+ struct tasdevice_priv *tasdevice, struct tasdev_blk *block)
+{
+ unsigned char *data = block->data;
+ unsigned int i, length;
+ const unsigned int blk_size = block->blk_size;
+ unsigned char dev_idx = 0;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tasdevice->fmw->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+
+ if (fw_fixed_hdr->ppcver >= PPC3_VERSION) {
+ switch (block->type) {
+ case MAIN_ALL_DEVICES_1X:
+ dev_idx = 0|0x80;
+ break;
+ case MAIN_DEVICE_A_1X:
+ dev_idx = 1|0x80;
+ break;
+ case COEFF_DEVICE_A_1X:
+ case PRE_DEVICE_A_1X:
+ dev_idx = 1|0xC0;
+ break;
+ case MAIN_DEVICE_B_1X:
+ dev_idx = 2|0x80;
+ break;
+ case COEFF_DEVICE_B_1X:
+ case PRE_DEVICE_B_1X:
+ dev_idx = 2|0xC0;
+ break;
+ case MAIN_DEVICE_C_1X:
+ dev_idx = 3|0x80;
+ break;
+ case COEFF_DEVICE_C_1X:
+ case PRE_DEVICE_C_1X:
+ dev_idx = 3|0xC0;
+ break;
+ case MAIN_DEVICE_D_1X:
+ dev_idx = 4|0x80;
+ break;
+ case COEFF_DEVICE_D_1X:
+ case PRE_DEVICE_D_1X:
+ dev_idx = 4|0xC0;
+ break;
+ default:
+ dev_info(tasdevice->dev,
+ "%s: load block: Other Type = 0x%02x\n",
+ __func__, block->type);
+ break;
+ }
+ } else {
+ switch (block->type) {
+ case MAIN_ALL_DEVICES:
+ dev_idx = 0|0x80;
+ break;
+ case MAIN_DEVICE_A:
+ dev_idx = 1|0x80;
+ break;
+ case COEFF_DEVICE_A:
+ case PRE_DEVICE_A:
+ dev_idx = 1|0xC0;
+ break;
+ case MAIN_DEVICE_B:
+ dev_idx = 2|0x80;
+ break;
+ case COEFF_DEVICE_B:
+ case PRE_DEVICE_B:
+ dev_idx = 2|0xC0;
+ break;
+ case MAIN_DEVICE_C:
+ dev_idx = 3|0x80;
+ break;
+ case COEFF_DEVICE_C:
+ case PRE_DEVICE_C:
+ dev_idx = 3|0xC0;
+ break;
+ case MAIN_DEVICE_D:
+ dev_idx = 4|0x80;
+ break;
+ case COEFF_DEVICE_D:
+ case PRE_DEVICE_D:
+ dev_idx = 4|0xC0;
+ break;
+ default:
+ dev_info(tasdevice->dev,
+ "%s: load block: Other Type = 0x%02x\n",
+ __func__, block->type);
+ break;
+ }
+ }
+
+ for (i = 0, length = 0; i < block->nr_subblocks; i++) {
+ int rc = tasdevice_process_block(tasdevice, data + length,
+ dev_idx, blk_size - length);
+ if (rc < 0) {
+ dev_err(tasdevice->dev,
+ "%s: ERROR:%u %u sublock write error\n",
+ __func__, length, blk_size);
+ break;
+ }
+ length += (unsigned int)rc;
+ if (blk_size < length) {
+ dev_err(tasdevice->dev,
+ "%s: ERROR:%u %u out of bounary\n",
+ __func__, length, blk_size);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int fw_parse_variable_header_git(struct tasdevice_priv
+ *tas_dev, const struct firmware *fmw, int offset)
+{
+ const unsigned char *buf = fmw->data;
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ int len = strlen((char *)&buf[offset]);
+
+ len++;
+
+ if (offset + len > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ offset += len;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device_family = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_dev->dev, "ERROR:%s: not TAS device\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_dev->dev, "Not support dev %d\n", fw_hdr->device);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->ndev = deviceNumber[fw_hdr->device];
+ if (fw_hdr->ndev != tas_dev->ndev) {
+ dev_err(tas_dev->dev,
+ "%s: ndev(%u) in dspbin dismatch ndev(%u) in DTS\n",
+ __func__, fw_hdr->ndev,
+ tas_dev->ndev);
+ offset = -1;
+ }
+
+out:
+ return offset;
+}
+
+static int fw_parse_variable_hdr_cal(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ const unsigned char *buf = fmw->data;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ int len = strlen((char *)&buf[offset]);
+
+ len++;
+
+ if (offset + len > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ offset += len;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnDeviceFamily error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device_family = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_dev->dev, "ERROR:%s: not TAS device\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnDevice error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_dev->dev, "Not support dev %d\n", fw_hdr->device);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->ndev = deviceNumber[fw_hdr->device];
+ if (fw_hdr->ndev != 1) {
+ dev_err(tas_dev->dev,
+ "%s: calbin must be 1, but currently ndev(%u)\n",
+ __func__, fw_hdr->ndev);
+ offset = -1;
+ }
+
+out:
+ return offset;
+}
+
+static inline void tas2781_clear_calfirmware(struct tasdevice_fw
+ *tas_fmw)
+{
+ int i;
+ unsigned int blks;
+
+ if (tas_fmw->calibrations) {
+ struct tasdevice_calibration *calibration;
+
+ for (i = 0; i < tas_fmw->nr_calibrations; i++) {
+ calibration = &(tas_fmw->calibrations[i]);
+ if (calibration) {
+ struct tasdevice_data *im =
+ &(calibration->dev_data);
+
+ if (im->dev_blks) {
+ struct tasdev_blk *block;
+
+ for (blks = 0; blks < im->nr_blk;
+ blks++) {
+ block = &(im->dev_blks[blks]);
+ kfree(block->data);
+ }
+ kfree(im->dev_blks);
+ }
+ }
+ }
+ kfree(tas_fmw->calibrations);
+ }
+ kfree(tas_fmw);
+}
+
+static int fw_parse_block_data(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ int n;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnType error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->type = SMS_HTONL(data[offset], data[offset + 1],
+ data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >=
+ PPC_DRIVER_CRCCHK) {
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "PChkSumPresent error\n");
+ offset = -1;
+ goto out;
+ }
+ block->is_pchksum_present = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: PChkSum error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->pchksum = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "YChkSumPresent error\n");
+ offset = -1;
+ goto out;
+ }
+ block->is_ychksum_present = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: YChkSum error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->ychksum = data[offset];
+ offset++;
+ } else {
+ block->is_pchksum_present = 0;
+ block->is_ychksum_present = 0;
+ }
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnCommands error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->nr_cmds = SMS_HTONL(data[offset], data[offset + 1],
+ data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ n = block->nr_cmds * 4;
+ if (offset + n > fmw->size) {
+ dev_err(tas_fmw->dev,
+ "%s: File Size(%lu) error offset = %d n = %d\n",
+ __func__, (unsigned long)fmw->size, offset, n);
+ offset = -1;
+ goto out;
+ }
+ block->data = kmemdup(&data[offset], n, GFP_KERNEL);
+ if (block->data == NULL) {
+ offset = -1;
+ goto out;
+ }
+ offset += n;
+out:
+ return offset;
+}
+
+static int fw_parse_data(struct tasdevice_fw *tas_fmw,
+ struct tasdevice_data *img_data, const struct firmware *fmw,
+ int offset)
+{
+ const unsigned char *data = (unsigned char *)fmw->data;
+ int n;
+ unsigned int nr_block;
+
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mpName error\n", __func__);
+ n = -1;
+ goto out;
+ }
+ memcpy(img_data->name, &data[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n++;
+ if (offset + n > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mpDescription error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += n;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnBlocks error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ img_data->nr_blk = SMS_HTONS(data[offset], data[offset + 1]);
+ offset += 2;
+
+ img_data->dev_blks =
+ kcalloc(img_data->nr_blk, sizeof(struct tasdev_blk),
+ GFP_KERNEL);
+ if (img_data->dev_blks == NULL) {
+ dev_err(tas_fmw->dev, "%s: FW memory failed!\n", __func__);
+ goto out;
+ }
+ for (nr_block = 0; nr_block < img_data->nr_blk; nr_block++) {
+ offset = fw_parse_block_data(tas_fmw,
+ &(img_data->dev_blks[nr_block]), fmw, offset);
+ if (offset < 0) {
+ offset = -1;
+ goto out;
+ }
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_calibration_data(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ unsigned int n;
+ unsigned int nr_calibration = 0;
+ struct tasdevice_calibration *calibration;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnCalibrations error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->nr_calibrations = SMS_HTONS(data[offset], data[offset + 1]);
+ offset += 2;
+
+ if (tas_fmw->nr_calibrations != 1) {
+ dev_err(tas_dev->dev,
+ "%s: only support one calibraiton(%d)!\n",
+ __func__, tas_fmw->nr_calibrations);
+ goto out;
+ }
+
+ tas_fmw->calibrations =
+ kcalloc(tas_fmw->nr_calibrations,
+ sizeof(struct tasdevice_calibration), GFP_KERNEL);
+ if (tas_fmw->calibrations == NULL) {
+ dev_err(tas_dev->dev, "pCalibrations mem failed!\n");
+ offset = -1;
+ goto out;
+ }
+ for (nr_calibration = 0; nr_calibration < tas_fmw->nr_calibrations;
+ nr_calibration++) {
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "pCalibrations error\n");
+ offset = -1;
+ goto out;
+ }
+ calibration = &(tas_fmw->calibrations[nr_calibration]);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n++;
+ if (offset + n > fmw->size) {
+ dev_err(tas_dev->dev, "pDescription err\n");
+ offset = -1;
+ goto out;
+ }
+ offset += n;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: Prog err, offset = %d\n",
+ __func__, offset);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: Conf err, offset = %d\n",
+ __func__, offset);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ offset = fw_parse_data(tas_fmw, &(calibration->dev_data), fmw,
+ offset);
+ if (offset < 0)
+ goto out;
+ }
+
+out:
+
+ return offset;
+}
+
+static int fw_parse_program_data(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_prog *program;
+ unsigned char *buf = (unsigned char *)fmw->data;
+ int nr_program;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->nr_programs = SMS_HTONS(buf[offset], buf[offset + 1]);
+ offset += 2;
+
+ if (tas_fmw->nr_programs == 0) {
+ /*Not error in calibration Data file, return directly*/
+ dev_err(tas_dev->dev, "%s: Programs is null, maybe calbin\n",
+ __func__);
+ goto out;
+ }
+
+ tas_fmw->programs =
+ kcalloc(tas_fmw->nr_programs, sizeof(struct tasdevice_prog),
+ GFP_KERNEL);
+ if (tas_fmw->programs == NULL) {
+ offset = -1;
+ goto out;
+ }
+ for (nr_program = 0; nr_program < tas_fmw->nr_programs; nr_program++) {
+ int n = 0;
+
+ program = &(tas_fmw->programs[nr_program]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpName error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 64;
+
+ n = strlen((char *)&buf[offset]);
+ n++;
+ if (offset + n > fmw->size) {
+ dev_err(tas_dev->dev, "Description err\n");
+ offset = -1;
+ goto out;
+ }
+
+ offset += n;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: AppMode err\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "PDMI2SMode err\n");
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: ISnsPD error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: VSnsPD error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: PowerLDG err\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ offset = fw_parse_data(tas_fmw, &(program->dev_data), fmw,
+ offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_configuration_data(
+ struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ int n;
+ unsigned int n_configs;
+ struct tasdevice_config *config;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->nr_configurations = SMS_HTONS(data[offset], data[offset + 1]);
+ offset += 2;
+
+ if (tas_fmw->nr_configurations == 0) {
+ dev_err(tas_dev->dev, "%s: Conf is zero\n", __func__);
+ //Do not "offset = -1;", because of calbin
+ goto out;
+ }
+ tas_fmw->configs =
+ kcalloc(tas_fmw->nr_configurations,
+ sizeof(struct tasdevice_config), GFP_KERNEL);
+
+ for (n_configs = 0; n_configs < tas_fmw->nr_configurations;
+ n_configs++) {
+ config =
+ &(tas_fmw->configs[n_configs]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size err\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ memcpy(config->name, &data[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n++;
+ if (offset + n > fmw->size) {
+ dev_err(tas_dev->dev, "Description err\n");
+ offset = -1;
+ goto out;
+ }
+
+ offset += n;
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "Device_orientation err\n");
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: Program err\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "SampleRate err\n");
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: PLLSrc err\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: PLLRate err\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnFsRate err\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ offset = fw_parse_data(tas_fmw, &(config->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_header(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+ const unsigned char *buf = (unsigned char *)fmw->data;
+ const unsigned char magic_number[] = { 0x35, 0x35, 0x35, 0x32 };
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ if (memcmp(&buf[offset], magic_number, 4)) {
+ dev_err(tas_dev->dev, "%s: Magic num NOT match\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnFWSize error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_fixed_hdr->fwsize = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ if (fw_fixed_hdr->fwsize != fmw->size) {
+ dev_err(tas_dev->dev, "File size not match, %lu %u",
+ (unsigned long)fmw->size, fw_fixed_hdr->fwsize);
+ offset = -1;
+ goto out;
+ }
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnChecksum error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPPCVersion error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_fixed_hdr->ppcver = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnFWVersion error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnDriverVersion error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_fixed_hdr->drv_ver = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnTimeStamp error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpDDCName error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 64;
+
+ out:
+ return offset;
+}
+
+// Return Code:
+// 1 -- the registers are in the inpage yram
+// 0 -- the registers are NOT in the inpage yram
+static int check_inpage_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ int ret;
+
+ if (book == TAS2781_YRAM_BOOK1) {
+ if (page == TAS2781_YRAM1_PAGE) {
+ if (reg >= TAS2781_YRAM1_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ } else if ((reg + len) > TAS2781_YRAM1_START_REG) {
+ cd->offset = TAS2781_YRAM1_START_REG;
+ cd->len =
+ len - (TAS2781_YRAM1_START_REG - reg);
+ ret = 1;
+ } else
+ ret = 0;
+ } else if (page == TAS2781_YRAM3_PAGE) {
+ if (reg > TAS2781_YRAM3_END_REG) {
+ ret = 0;
+ } else if (reg >= TAS2781_YRAM3_START_REG) {
+ if ((reg + len) > TAS2781_YRAM3_END_REG) {
+ cd->offset = reg;
+ cd->len =
+ TAS2781_YRAM3_END_REG - reg + 1;
+ ret = 1;
+ } else {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ }
+ } else {
+ if ((reg + (len-1)) <
+ TAS2781_YRAM3_START_REG)
+ ret = 0;
+ else {
+ cd->offset =
+ TAS2781_YRAM3_START_REG;
+ cd->len =
+ len - (TAS2781_YRAM3_START_REG - reg);
+ ret = 1;
+ }
+ }
+ }
+ } else if (book ==
+ TAS2781_YRAM_BOOK2) {
+ if (page == TAS2781_YRAM5_PAGE) {
+ if (reg > TAS2781_YRAM5_END_REG) {
+ ret = 0;
+ } else if (reg >= TAS2781_YRAM5_START_REG) {
+ if ((reg + len) > TAS2781_YRAM5_END_REG) {
+ cd->offset = reg;
+ cd->len =
+ TAS2781_YRAM5_END_REG - reg + 1;
+ ret = 1;
+ } else {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ }
+ } else {
+ if ((reg + (len-1)) <
+ TAS2781_YRAM5_START_REG)
+ ret = 0;
+ else {
+ cd->offset =
+ TAS2781_YRAM5_START_REG;
+ cd->len =
+ len - (TAS2781_YRAM5_START_REG - reg);
+ ret = 1;
+ }
+ }
+ }
+ } else
+ ret = 0;
+
+ return ret;
+}
+
+// Return Code:
+// 1 -- the registers are in the inblock yram
+// 0 -- the registers are NOT in the inblock yram
+static int check_inblock_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ int ret;
+
+ if (book == TAS2781_YRAM_BOOK1) {
+ if (page < TAS2781_YRAM2_START_PAGE)
+ ret = 0;
+ else if (page <= TAS2781_YRAM2_END_PAGE) {
+ if (reg > TAS2781_YRAM2_END_REG)
+ ret = 0;
+ else if (reg >= TAS2781_YRAM2_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ } else {
+ if ((reg + (len-1)) <
+ TAS2781_YRAM2_START_REG)
+ ret = 0;
+ else {
+ cd->offset =
+ TAS2781_YRAM2_START_REG;
+ cd->len =
+ reg + len - TAS2781_YRAM2_START_REG;
+ ret = 1;
+ }
+ }
+ } else
+ ret = 0;
+ } else if (book ==
+ TAS2781_YRAM_BOOK2) {
+ if (page < TAS2781_YRAM4_START_PAGE)
+ ret = 0;
+ else if (page <= TAS2781_YRAM4_END_PAGE) {
+ if (reg > TAS2781_YRAM2_END_REG)
+ ret = 0;
+ else if (reg >= TAS2781_YRAM2_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ } else {
+ if ((reg + (len-1))
+ < TAS2781_YRAM2_START_REG)
+ ret = 0;
+ else {
+ cd->offset =
+ TAS2781_YRAM2_START_REG;
+ cd->len =
+ reg + len - TAS2781_YRAM2_START_REG;
+ ret = 1;
+ }
+ }
+ } else
+ ret = 0;
+ } else
+ ret = 0;
+
+ return ret;
+}
+
+static int check_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ int ret;
+
+ ret = check_inpage_yram(cd, book, page, reg, len);
+ if (ret == 0)
+ ret = check_inblock_yram(cd, book,
+ page, reg, len);
+
+ return ret;
+}
+
+static int do_singlereg_checksum(struct tasdevice_priv *tasdevice,
+ enum channel chl, unsigned char book, unsigned char page,
+ unsigned char reg, unsigned char val)
+{
+ int ret;
+ struct tas_crc crc_data;
+ unsigned int nData1;
+
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg <= (TASDEVICE_PAGE_REG(
+ TAS2781_SA_COEFF_SWAP_REG) + 4))) {
+ /*DSP swap command, pass */
+ ret = 0;
+ goto end;
+ }
+
+ ret = check_yram(&crc_data, book, page, reg, 1);
+ if (ret == 1) {
+ ret = tasdevice_dev_read(tasdevice, chl,
+ TASDEVICE_REG(book, page, reg), &nData1);
+ if (ret < 0)
+ goto end;
+
+ if (nData1 != val) {
+ dev_err(tasdevice->dev,
+ "B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n",
+ book, page, reg,
+ val, nData1);
+ ret = -EAGAIN;
+ tasdevice->tasdevice[chl].err_code |=
+ ERROR_YRAM_CRCCHK;
+ ret = -EAGAIN;
+ goto end;
+ }
+
+ ret = crc8(tasdevice->crc8_lkp_tbl, &val, 1, 0);
+ }
+
+end:
+ return ret;
+}
+
+static int do_multireg_checksum(struct tasdevice_priv *tasdevice,
+ enum channel chn, unsigned char book, unsigned char page,
+ unsigned char reg, unsigned int len)
+{
+ int ret, i;
+ unsigned char crc_chksum = 0;
+ unsigned char nBuf1[128];
+ struct tas_crc crc_data;
+
+ if ((reg + len-1) > 127) {
+ ret = -EINVAL;
+ dev_err(tasdevice->dev, "firmware error\n");
+ goto end;
+ }
+
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg == TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+ && (len == 4)) {
+ /*DSP swap command, pass */
+ ret = 0;
+ goto end;
+ }
+
+ ret = check_yram(&crc_data, book, page, reg, len);
+ if (ret == 1) {
+ if (len == 1) {
+ dev_err(tasdevice->dev, "firmware error\n");
+ ret = -EINVAL;
+ goto end;
+ } else {
+ ret = tasdevice_dev_bulk_read(tasdevice, chn,
+ TASDEVICE_REG(book, page, crc_data.offset),
+ nBuf1, crc_data.len);
+ if (ret < 0)
+ goto end;
+
+ for (i = 0; i < crc_data.len; i++) {
+ if ((book == TASDEVICE_BOOK_ID(
+ TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(
+ TAS2781_SA_COEFF_SWAP_REG))
+ && ((i + crc_data.offset)
+ >= TASDEVICE_PAGE_REG(
+ TAS2781_SA_COEFF_SWAP_REG))
+ && ((i + crc_data.offset)
+ <= (TASDEVICE_PAGE_REG(
+ TAS2781_SA_COEFF_SWAP_REG)
+ + 4))) {
+ /*DSP swap command, bypass */
+ continue;
+ } else
+ crc_chksum +=
+ crc8(tasdevice->crc8_lkp_tbl,
+ &nBuf1[i], 1, 0);
+ }
+
+ ret = crc_chksum;
+ }
+ }
+
+end:
+ return ret;
+}
+
+static int tasdevice_load_block(struct tasdevice_priv *tas_dev,
+ struct tasdev_blk *block)
+{
+ int ret = 0;
+ unsigned int nr_cmds;
+ unsigned char book;
+ unsigned char page;
+ unsigned char offset;
+ unsigned char val;
+ unsigned int len;
+ unsigned int sleep_dur;
+ unsigned char crc_chksum = 0;
+ unsigned int nr_value;
+ int nr_retry = 6;
+ unsigned char *data = block->data;
+ int chn = 0, chnend = 0;
+
+ switch (block->type) {
+ case MAIN_ALL_DEVICES:
+ chn = 0;
+ chnend = tas_dev->ndev;
+ break;
+ case MAIN_DEVICE_A:
+ case COEFF_DEVICE_A:
+ case PRE_DEVICE_A:
+ chn = 0;
+ chnend = 1;
+ break;
+ case MAIN_DEVICE_B:
+ case COEFF_DEVICE_B:
+ case PRE_DEVICE_B:
+ chn = 1;
+ chnend = 2;
+ break;
+ case MAIN_DEVICE_C:
+ case COEFF_DEVICE_C:
+ case PRE_DEVICE_C:
+ chn = 2;
+ chnend = 3;
+ break;
+ case MAIN_DEVICE_D:
+ case COEFF_DEVICE_D:
+ case PRE_DEVICE_D:
+ chn = 3;
+ chnend = 4;
+ break;
+ default:
+ dev_dbg(tas_dev->dev, "load blk: Other Type = 0x%02x\n",
+ block->type);
+ break;
+ }
+
+ for (; chn < chnend; chn++) {
+ if (tas_dev->tasdevice[chn].is_loading == false)
+ continue;
+start:
+ if (block->is_pchksum_present) {
+ ret = tasdevice_dev_write(tas_dev, chn,
+ TASDEVICE_I2CChecksum, 0);
+ if (ret < 0)
+ goto end;
+ }
+
+ if (block->is_ychksum_present)
+ crc_chksum = 0;
+
+ nr_cmds = 0;
+
+ while (nr_cmds < block->nr_cmds) {
+ data = block->data + nr_cmds * 4;
+
+ book = data[0];
+ page = data[1];
+ offset = data[2];
+ val = data[3];
+
+ nr_cmds++;
+
+ if (offset <= 0x7F) {
+ ret = tasdevice_dev_write(tas_dev, chn,
+ TASDEVICE_REG(book, page, offset),
+ val);
+ if (ret < 0)
+ goto end;
+ if (block->is_ychksum_present) {
+ ret = do_singlereg_checksum(tas_dev,
+ chn, book, page, offset,
+ val);
+ if (ret < 0)
+ goto check;
+ crc_chksum += (unsigned char)ret;
+ }
+ continue;
+ }
+ if (offset == 0x81) {
+ sleep_dur = (book << 8) + page;
+ msleep(sleep_dur);
+ continue;
+ }
+ if (offset == 0x85) {
+ data += 4;
+ len = (book << 8) + page;
+ book = data[0];
+ page = data[1];
+ offset = data[2];
+ if (len > 1) {
+ ret = tasdevice_dev_bulk_write(
+ tas_dev, chn,
+ TASDEVICE_REG(book, page,
+ offset), data + 3, len);
+ if (ret < 0)
+ goto end;
+ if (!block->is_ychksum_present)
+ goto next;
+ ret = do_multireg_checksum(
+ tas_dev, chn, book,
+ page, offset,
+ len);
+ if (ret < 0)
+ goto check;
+ crc_chksum +=
+ (unsigned char)ret;
+ } else {
+ ret = tasdevice_dev_write(tas_dev,
+ chn,
+ TASDEVICE_REG(book, page,
+ offset),
+ data[3]);
+ if (ret < 0)
+ goto end;
+ if (!block->is_ychksum_present)
+ goto next;
+ ret = do_singlereg_checksum(
+ tas_dev, chn, book,
+ page, offset,
+ data[3]);
+ if (ret < 0)
+ goto check;
+ crc_chksum +=
+ (unsigned char)ret;
+ }
+next:
+ nr_cmds++;
+ if (len >= 2)
+ nr_cmds += ((len - 2) / 4) + 1;
+ }
+ }
+ if (block->is_pchksum_present) {
+ ret = tasdevice_dev_read(tas_dev, chn,
+ TASDEVICE_I2CChecksum, &nr_value);
+ if (ret < 0) {
+ dev_err(tas_dev->dev, "%s: Chn %d\n", __func__,
+ chn);
+ goto check;
+ }
+ if ((nr_value & 0xff) != block->pchksum) {
+ dev_err(tas_dev->dev, "Blk PChkSum Chn %d ",
+ chn);
+ dev_err(tas_dev->dev,
+ "FW = 0x%x, Reg = 0x%x\n",
+ block->pchksum,
+ (nr_value & 0xff));
+ ret = -EAGAIN;
+ tas_dev->tasdevice[chn].err_code |=
+ ERROR_PRAM_CRCCHK;
+ goto check;
+ }
+ ret = 0;
+ tas_dev->tasdevice[chn].err_code &=
+ ~ERROR_PRAM_CRCCHK;
+ }
+
+ if (block->is_ychksum_present) {
+ //TBD, open it when FW ready
+ dev_err(tas_dev->dev,
+ "Block YChkSum: FW = 0x%x, YCRC = 0x%x\n",
+ block->ychksum, crc_chksum);
+
+ tas_dev->tasdevice[chn].err_code &=
+ ~ERROR_YRAM_CRCCHK;
+ ret = 0;
+ }
+check:
+ if (ret == -EAGAIN) {
+ nr_retry--;
+ if (nr_retry > 0)
+ goto start;
+ else {
+ if ((block->type == MAIN_ALL_DEVICES)
+ || (block->type == MAIN_DEVICE_A)
+ || (block->type == MAIN_DEVICE_B)
+ || (block->type == MAIN_DEVICE_C)
+ || (block->type == MAIN_DEVICE_D))
+ tas_dev->tasdevice[chn].cur_prog = -1;
+ else
+ tas_dev->tasdevice[chn].cur_conf = -1;
+ nr_retry = 6;
+ }
+ }
+ }
+end:
+ if (ret < 0)
+ dev_err(tas_dev->dev, "Block (%d) load error\n", block->type);
+
+ return ret;
+}
+
+
+static int tasdevice_load_data(struct tasdevice_priv *tas_dev,
+ struct tasdevice_data *dev_data)
+{
+ int ret = 0;
+ unsigned int nr_block;
+ struct tasdev_blk *block;
+
+ for (nr_block = 0; nr_block < dev_data->nr_blk; nr_block++) {
+ block = &(dev_data->dev_blks[nr_block]);
+ ret = tas_dev->tasdevice_load_block(tas_dev, block);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static int tasdevice_load_calibrated_data(
+ struct tasdevice_priv *tas_dev, struct tasdevice_data *dev_data)
+{
+ int ret = 0;
+ unsigned int nr_block;
+ struct tasdev_blk *block;
+
+ for (nr_block = 0; nr_block < dev_data->nr_blk; nr_block++) {
+ block = &(dev_data->dev_blks[nr_block]);
+ ret = tasdevice_load_block(tas_dev, block);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+int tas2781_load_calibration(void *context,
+ char *file_name, enum channel i)
+{
+ int ret, offset = 0;
+ struct firmware fmw;
+ const struct firmware *fw_entry;
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *)context;
+ struct tasdevice *tasdev = &(tas_dev->tasdevice[i]);
+ struct tasdevice_fw *tas_fmw;
+
+ ret = request_firmware(&fw_entry, file_name, tas_dev->dev);
+ if (!ret) {
+ if (!fw_entry->size) {
+ dev_err(tas_dev->dev,
+ "%s: file read error: size = %lu\n",
+ __func__, (unsigned long)fw_entry->size);
+ goto out;
+ }
+ fmw.size = fw_entry->size;
+ fmw.data = fw_entry->data;
+ } else {
+ dev_info(tas_dev->dev,
+ "%s: Request firmware %s failed\n",
+ __func__, file_name);
+ goto out;
+ }
+
+ tas_fmw = tasdev->cali_data_fmw = kcalloc(1,
+ sizeof(struct tasdevice_fw), GFP_KERNEL);
+ if (tasdev->cali_data_fmw == NULL) {
+ dev_err(tas_dev->dev, "%s: FW memory failed!\n", __func__);
+ ret = -1;
+ goto out;
+ }
+ tas_fmw->dev = tas_dev->dev;
+ offset = fw_parse_header(tas_dev, tas_fmw, &fmw, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev, "%s: fw_parse_header EXIT!\n", __func__);
+ goto out;
+ }
+ offset = fw_parse_variable_hdr_cal(tas_dev, tas_fmw, &fmw, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev,
+ "%s: fw_parse_variable_header_cal EXIT!\n", __func__);
+ goto out;
+ }
+ offset = fw_parse_program_data(tas_dev, tas_fmw, &fmw, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev, "fw_parse_program_data EXIT!\n");
+ goto out;
+ }
+ offset = fw_parse_configuration_data(tas_dev, tas_fmw, &fmw,
+ offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev, "fw_parse_configuration_data EXIT!\n");
+ goto out;
+ }
+ offset = fw_parse_calibration_data(tas_dev,
+ tas_fmw, &fmw, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev, "fw_parse_calibration_data EXIT!\n");
+ goto out;
+ }
+ tasdev->is_calibrated_data_loaded = true;
+out:
+ if (fw_entry) {
+ release_firmware(fw_entry);
+ fw_entry = NULL;
+ }
+ return ret;
+}
+
+int tasdevice_dspfw_ready(const struct firmware *fmw, void *context)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ struct tasdevice_fw *tas_fmw;
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr;
+ int offset = 0, ret = 0;
+
+ if (!fmw || !fmw->data) {
+ dev_err(tas_dev->dev, "%s: Failed to read firmware %s\n",
+ __func__, tas_dev->coef_binaryname);
+ ret = -1;
+ goto out;
+ }
+
+ tas_dev->fmw = kcalloc(1,
+ sizeof(struct tasdevice_fw), GFP_KERNEL);
+ if (tas_dev->fmw == NULL) {
+ ret = -1;
+ goto out;
+ }
+ tas_fmw = tas_dev->fmw;
+ tas_fmw->dev = tas_dev->dev;
+ offset = fw_parse_header(tas_dev, tas_fmw, fmw, offset);
+
+ if (offset == -1)
+ goto out;
+ fw_fixed_hdr = &(tas_fmw->fw_hdr.fixed_hdr);
+ switch (fw_fixed_hdr->drv_ver) {
+ case 0x301:
+ case 0x302:
+ case 0x502:
+ tas_dev->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_dev->fw_parse_program_data =
+ fw_parse_program_data_kernel;
+ tas_dev->fw_parse_configuration_data =
+ fw_parse_configuration_data_kernel;
+ tas_dev->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ break;
+ case 0x202:
+ case 0x400:
+ tas_dev->fw_parse_variable_header =
+ fw_parse_variable_header_git;
+ tas_dev->fw_parse_program_data =
+ fw_parse_program_data;
+ tas_dev->fw_parse_configuration_data =
+ fw_parse_configuration_data;
+ tas_dev->tasdevice_load_block =
+ tasdevice_load_block;
+ break;
+ default:
+ if (fw_fixed_hdr->drv_ver == 0x100) {
+ if (fw_fixed_hdr->ppcver >= PPC3_VERSION) {
+ tas_dev->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_dev->fw_parse_program_data =
+ fw_parse_program_data_kernel;
+ tas_dev->fw_parse_configuration_data =
+ fw_parse_configuration_data_kernel;
+ tas_dev->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ tas_dev->fw_parse_calibration_data = NULL;
+ } else {
+ switch (fw_fixed_hdr->ppcver) {
+ case 0x00:
+ tas_dev->fw_parse_variable_header =
+ fw_parse_variable_header_git;
+ tas_dev->fw_parse_program_data =
+ fw_parse_program_data;
+ tas_dev->fw_parse_configuration_data =
+ fw_parse_configuration_data;
+ tas_dev->fw_parse_calibration_data =
+ fw_parse_calibration_data;
+ tas_dev->tasdevice_load_block =
+ tasdevice_load_block;
+ break;
+ default:
+ dev_err(tas_dev->dev,
+ "%s: PPCVersion must be 0x0 or 0x%02x",
+ __func__, PPC3_VERSION);
+ dev_err(tas_dev->dev, " Current:0x%02x\n",
+ fw_fixed_hdr->ppcver);
+ offset = -1;
+ break;
+ }
+ }
+ } else {
+ dev_err(tas_dev->dev,
+ "DriverVersion must be 0x0, 0x230 or above 0x230 ");
+ dev_err(tas_dev->dev, "current is 0x%02x\n",
+ fw_fixed_hdr->drv_ver);
+ offset = -1;
+ }
+ break;
+ }
+
+ offset = tas_dev->fw_parse_variable_header(tas_dev, fmw, offset);
+ if (offset == -1)
+ goto out;
+
+ offset = tas_dev->fw_parse_program_data(tas_dev, tas_fmw, fmw,
+ offset);
+ if (offset < 0) {
+ ret = -1;
+ goto out;
+ }
+ offset = tas_dev->fw_parse_configuration_data(tas_dev,
+ tas_fmw, fmw, offset);
+ if (offset < 0)
+ ret = -1;
+
+out:
+ return ret;
+}
+
+void tasdevice_calbin_remove(void *context)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ struct tasdevice *tasdev;
+ int i;
+
+ if (tas_dev) {
+ for (i = 0; i < tas_dev->ndev; i++) {
+ tasdev = &(tas_dev->tasdevice[i]);
+ if (tasdev->cali_data_fmw) {
+ tas2781_clear_calfirmware(
+ tasdev->cali_data_fmw);
+ tasdev->cali_data_fmw = NULL;
+ }
+ }
+ }
+}
+
+static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog)
+{
+ struct tasdevice_data *im;
+ unsigned int nr_blk;
+ struct tasdev_blk *blk;
+
+ if (prog) {
+ im = &(prog->dev_data);
+
+ if (!im->dev_blks)
+ return;
+
+ for (nr_blk = 0; nr_blk < im->nr_blk; nr_blk++) {
+ blk = &(im->dev_blks[nr_blk]);
+ kfree(blk->data);
+ }
+ kfree(im->dev_blks);
+ }
+}
+
+static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog,
+ unsigned short nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++)
+ tasdev_dsp_prog_blk_remove(&prog[i]);
+ kfree(prog);
+}
+
+static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg)
+{
+ struct tasdevice_data *im;
+ unsigned int nr_blk;
+ struct tasdev_blk *blk;
+
+ if (cfg) {
+ im = &(cfg->dev_data);
+
+ if (!im->dev_blks)
+ return;
+
+ for (nr_blk = 0; nr_blk < im->nr_blk; nr_blk++) {
+ blk = &(im->dev_blks[nr_blk]);
+ kfree(blk->data);
+ }
+ kfree(im->dev_blks);
+ }
+}
+
+static void tasdev_dsp_cfg_remove(struct tasdevice_config *config,
+ unsigned short nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++)
+ tasdev_dsp_cfg_blk_remove(&config[i]);
+ kfree(config);
+}
+
+void tasdevice_dsp_remove(void *context)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+
+ if (tas_dev->fmw) {
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+
+ if (tas_fmw->programs)
+ tasdev_dsp_prog_remove(tas_fmw->programs,
+ tas_fmw->nr_programs);
+ if (tas_fmw->configs)
+ tasdev_dsp_cfg_remove(tas_fmw->configs,
+ tas_fmw->nr_configurations);
+ kfree(tas_fmw);
+ tas_dev->fmw = NULL;
+ }
+}
+
+static int tas2781_set_calibration(void *context, enum channel i,
+ int n_calibration)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ int ret = 0;
+ struct tasdevice *tasdevice = &(tas_dev->tasdevice[i]);
+ struct tasdevice_fw *cal_fmw = tasdevice->cali_data_fmw;
+
+ if ((!tas_dev->fmw->programs) || (!tas_dev->fmw->configs)) {
+ dev_err(tas_dev->dev, "%s, Firmware not loaded\n\r", __func__);
+ ret = 0;
+ goto out;
+ }
+
+ if (n_calibration == 0xFF || (n_calibration == 0x100
+ && tasdevice->is_calibrated_data_loaded == false)) {
+ if (cal_fmw) {
+ tasdevice->is_calibrated_data_loaded = false;
+ tas2781_clear_calfirmware(cal_fmw);
+ cal_fmw = NULL;
+ }
+
+ scnprintf(tas_dev->cal_binaryname[i], 64, "%s_cal_0x%02x.bin",
+ tas_dev->dev_name, tas_dev->tasdevice[i].dev_addr);
+ ret = tas2781_load_calibration(tas_dev,
+ tas_dev->cal_binaryname[i], i);
+ if (ret != 0) {
+ dev_err(tas_dev->dev,
+ "%s: load %s error, no-side effect\n",
+ __func__, tas_dev->cal_binaryname[i]);
+ ret = 0;
+ }
+ }
+ tasdevice->is_loading = true;
+ tasdevice->is_loaderr = false;
+
+ if (cal_fmw) {
+ struct tasdevice_calibration *calibration =
+ cal_fmw->calibrations;
+
+ if (calibration)
+ tasdevice_load_calibrated_data(tas_dev,
+ &(calibration->dev_data));
+ } else
+ dev_err(tas_dev->dev, "%s: No calibrated data for dev %d\n",
+ __func__, i);
+
+out:
+ return ret;
+}
+
+int tasdevice_select_tuningprm_cfg(void *context, int prm_no,
+ int cfg_no, int rca_conf_no)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ struct tasdevice_rca *rca = &(tas_dev->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+ struct tasdevice_config *conf;
+ struct tasdevice_prog *program;
+ int i;
+ int status;
+ int prog_status;
+
+ if (!tas_fmw) {
+ dev_err(tas_dev->dev, "%s: Firmware is NULL\n", __func__);
+ goto out;
+ }
+
+ if (cfg_no >= tas_fmw->nr_configurations) {
+ dev_err(tas_dev->dev,
+ "%s: cfg(%d) is not in range of conf %u\n",
+ __func__, cfg_no, tas_fmw->nr_configurations);
+ goto out;
+ }
+
+ if (prm_no >= tas_fmw->nr_programs) {
+ dev_err(tas_dev->dev,
+ "%s: prm(%d) is not in range of Programs %u\n",
+ __func__, prm_no, tas_fmw->nr_programs);
+ goto out;
+ }
+
+ if (rca_conf_no > rca->ncfgs || rca_conf_no <= 0 ||
+ cfg_info == NULL) {
+ dev_err(tas_dev->dev,
+ "conf_no:%d should be in range from 0 to %u\n",
+ rca_conf_no, rca->ncfgs-1);
+ goto out;
+ }
+
+ conf = &(tas_fmw->configs[cfg_no]);
+ for (i = 0, prog_status = 0; i < tas_dev->ndev; i++) {
+ if (cfg_info[rca_conf_no]->active_dev & (1 << i)) {
+ if (tas_dev->tasdevice[i].cur_prog != prm_no) {
+ tas_dev->tasdevice[i].cur_conf = -1;
+ tas_dev->tasdevice[i].is_loading = true;
+ prog_status++;
+ }
+ } else
+ tas_dev->tasdevice[i].is_loading = false;
+ tas_dev->tasdevice[i].is_loaderr = false;
+ }
+
+ if (prog_status) {
+ program = &(tas_fmw->programs[prm_no]);
+ tasdevice_load_data(tas_dev, &(program->dev_data));
+ for (i = 0; i < tas_dev->ndev; i++) {
+ if (tas_dev->tasdevice[i].is_loaderr == true)
+ continue;
+ else if (tas_dev->tasdevice[i].is_loaderr == false
+ && tas_dev->tasdevice[i].is_loading == true) {
+ struct tasdevice_fw *cal_fmw =
+ tas_dev->tasdevice[i].cali_data_fmw;
+
+ if (cal_fmw) {
+ struct tasdevice_calibration
+ *calibration =
+ cal_fmw->calibrations;
+
+ if (calibration)
+ tasdevice_load_calibrated_data(
+ tas_dev,
+ &(calibration->dev_data));
+ }
+ tas_dev->tasdevice[i].cur_prog = prm_no;
+ }
+ }
+ }
+
+ if (tas_dev->is_glb_calibrated_data_loaded == false) {
+ for (i = 0; i < tas_dev->ndev; i++)
+ tas2781_set_calibration(tas_dev, i, 0x100);
+ tas_dev->is_glb_calibrated_data_loaded = true;
+ /* Unwise to reload calibrationdata everytime,
+ * this code will work once even if calibrated
+ * data still failed to be got
+ */
+ }
+
+ for (i = 0, status = 0; i < tas_dev->ndev; i++) {
+ if (tas_dev->tasdevice[i].cur_conf != cfg_no
+ && (cfg_info[rca_conf_no]->active_dev & (1 << i))
+ && (tas_dev->tasdevice[i].is_loaderr == false)) {
+ status++;
+ tas_dev->tasdevice[i].is_loading = true;
+ } else
+ tas_dev->tasdevice[i].is_loading = false;
+ }
+
+ if (status) {
+ status = 0;
+ tasdevice_load_data(tas_dev, &(conf->dev_data));
+ for (i = 0; i < tas_dev->ndev; i++) {
+ if (tas_dev->tasdevice[i].is_loaderr == true) {
+ status |= 1 << (i + 4);
+ continue;
+ } else if (tas_dev->tasdevice[i].is_loaderr == false
+ && tas_dev->tasdevice[i].is_loading == true)
+ tas_dev->tasdevice[i].cur_conf = cfg_no;
+ }
+ } else
+ dev_err(tas_dev->dev,
+ "%s: No device is in active in conf %d\n",
+ __func__, rca_conf_no);
+
+ status |= cfg_info[rca_conf_no]->active_dev;
+out:
+ return prog_status;
+}
diff --git a/sound/soc/codecs/tas2781-dsp.h b/sound/soc/codecs/tas2781-dsp.h
new file mode 100644
index 000000000000..26a6f5796349
--- /dev/null
+++ b/sound/soc/codecs/tas2781-dsp.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+//
+// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+//
+// Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2781 driver implements a flexible and configurable algo coff setting
+// for one, two, even multiple TAS2781 chips.
+//
+// Author: Shenghao Ding <shenghao-ding(a)ti.com>
+// Author: Kevin Lu <kevin-lu(a)ti.com>
+//
+
+#ifndef __TASDEVICE_DSP_H__
+#define __TASDEVICE_DSP_H__
+
+#define MAIN_ALL_DEVICES 0x0d
+#define MAIN_DEVICE_A 0x01
+#define MAIN_DEVICE_B 0x08
+#define MAIN_DEVICE_C 0x10
+#define MAIN_DEVICE_D 0x14
+#define COEFF_DEVICE_A 0x03
+#define COEFF_DEVICE_B 0x0a
+#define COEFF_DEVICE_C 0x11
+#define COEFF_DEVICE_D 0x15
+#define PRE_DEVICE_A 0x04
+#define PRE_DEVICE_B 0x0b
+#define PRE_DEVICE_C 0x12
+#define PRE_DEVICE_D 0x16
+
+#define PPC3_VERSION 0x4100
+#define RCA_CONFIGID_BYPASS_ALL 0
+#define TASDEVICE_DEVICE_SUM 8
+#define TASDEVICE_CONFIG_SUM 64
+
+enum channel {
+ top_left_Chn,
+ top_right_chn,
+ bottom_left_Chn,
+ bottom_right_chn,
+ max_chn,
+};
+
+enum tasdevice_dsp_dev_idx {
+ TASDEVICE_DSP_TAS_2555 = 0,
+ TASDEVICE_DSP_TAS_2555_STEREO,
+ TASDEVICE_DSP_TAS_2557_MONO,
+ TASDEVICE_DSP_TAS_2557_DUAL_MONO,
+ TASDEVICE_DSP_TAS_2559,
+ TASDEVICE_DSP_TAS_2563,
+ TASDEVICE_DSP_TAS_2563_DUAL_MONO = 7,
+ TASDEVICE_DSP_TAS_2563_QUAD,
+ TASDEVICE_DSP_TAS_2563_21,
+ TASDEVICE_DSP_TAS_2781,
+ TASDEVICE_DSP_TAS_2781_DUAL_MONO,
+ TASDEVICE_DSP_TAS_2781_21,
+ TASDEVICE_DSP_TAS_2781_QUAD,
+ TASDEVICE_DSP_TAS_MAX_DEVICE
+};
+
+struct tasdevice_fw_fixed_hdr {
+ unsigned int fwsize;
+ unsigned int ppcver;
+ unsigned int drv_ver;
+};
+
+struct tasdevice_dspfw_hdr {
+ struct tasdevice_fw_fixed_hdr fixed_hdr;
+ unsigned short device_family;
+ unsigned short device;
+ unsigned char ndev;
+};
+
+struct tasdev_blk {
+ unsigned int type;
+ unsigned char is_pchksum_present;
+ unsigned char pchksum;
+ unsigned char is_ychksum_present;
+ unsigned char ychksum;
+ unsigned int nr_cmds;
+ unsigned int blk_size;
+ unsigned int nr_subblocks;
+ unsigned char *data;
+};
+
+struct tasdevice_data {
+ char name[64];
+ unsigned int nr_blk;
+ struct tasdev_blk *dev_blks;
+};
+struct tasdevice_prog {
+ unsigned int prog_size;
+ struct tasdevice_data dev_data;
+};
+
+struct tasdevice_config {
+ unsigned int cfg_size;
+ char name[64];
+ struct tasdevice_data dev_data;
+};
+
+struct tasdevice_calibration {
+ struct tasdevice_data dev_data;
+};
+
+struct tasdevice_fw {
+ struct tasdevice_dspfw_hdr fw_hdr;
+ unsigned short nr_programs;
+ struct tasdevice_prog *programs;
+ unsigned short nr_configurations;
+ struct tasdevice_config *configs;
+ unsigned short nr_calibrations;
+ struct tasdevice_calibration *calibrations;
+ struct device *dev;
+};
+
+enum tasdevice_dsp_fw_state {
+ TASDEVICE_DSP_FW_NONE = 0,
+ TASDEVICE_DSP_FW_PENDING,
+ TASDEVICE_DSP_FW_FAIL,
+ TASDEVICE_DSP_FW_ALL_OK,
+};
+
+enum tasdevice_bin_blk_type {
+ TASDEVICE_BIN_BLK_COEFF = 1,
+ TASDEVICE_BIN_BLK_POST_POWER_UP,
+ TASDEVICE_BIN_BLK_PRE_SHUTDOWN,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP,
+ TASDEVICE_BIN_BLK_POST_SHUTDOWN
+};
+
+struct tasdevice_rca_hdr {
+ unsigned int img_sz;
+ unsigned int checksum;
+ unsigned int binary_version_num;
+ unsigned int drv_fw_version;
+ unsigned char plat_type;
+ unsigned char dev_family;
+ unsigned char reserve;
+ unsigned char ndev;
+ unsigned char devs[TASDEVICE_DEVICE_SUM];
+ unsigned int nconfig;
+ unsigned int config_size[TASDEVICE_CONFIG_SUM];
+};
+
+struct tasdev_blk_data {
+ unsigned char dev_idx;
+ unsigned char block_type;
+ unsigned short yram_checksum;
+ unsigned int block_size;
+ unsigned int n_subblks;
+ unsigned char *regdata;
+};
+
+struct tasdevice_config_info {
+ unsigned int nblocks;
+ unsigned int real_nblocks;
+ unsigned char active_dev;
+ struct tasdev_blk_data **blk_data;
+};
+
+struct tasdevice_rca {
+ struct tasdevice_rca_hdr fw_hdr;
+ int ncfgs;
+ struct tasdevice_config_info **cfg_info;
+ int profile_cfg_id;
+};
+
+int tasdevice_dspfw_ready(const struct firmware *fmw, void *context);
+void tasdevice_dsp_remove(void *context);
+void tasdevice_calbin_remove(void *context);
+int tas2781_load_calibration(void *tas_dev, char *filename,
+ enum channel i);
+int tasdevice_select_tuningprm_cfg(void *context, int prm,
+ int cfg_no, int rca_conf_no);
+
+#endif
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
new file mode 100644
index 000000000000..5c4af6b8069c
--- /dev/null
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -0,0 +1,1665 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+//
+// Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2781 driver implements a flexible and configurable algo coff setting
+// for one, two, even multiple TAS2781 chips.
+//
+// Author: Shenghao Ding <shenghao-ding(a)ti.com>
+// Author: Kevin Lu <kevin-lu(a)ti.com>
+//
+
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "tas2781.h"
+
+#define TASDEVICE_CRC8_POLYNOMIAL 0x4d
+
+const char *blocktype[5] = {
+ "COEFF",
+ "POST_POWER_UP",
+ "PRE_SHUTDOWN",
+ "PRE_POWER_UP",
+ "POST_SHUTDOWN"
+};
+
+static const struct i2c_device_id tasdevice_id[] = {
+ { "tas2781", TAS2781 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tasdevice_id);
+
+static const struct regmap_range_cfg tasdevice_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 256 * 128,
+ .selector_reg = TASDEVICE_PAGE_SELECT,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config tasdevice_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .ranges = tasdevice_ranges,
+ .num_ranges = ARRAY_SIZE(tasdevice_ranges),
+ .max_register = 256 * 128,
+};
+
+static const struct of_device_id tasdevice_of_match[] = {
+ { .compatible = "ti,tas2781" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, tasdevice_of_match);
+
+int tasdevice_process_block(void *context,
+ unsigned char *data, unsigned char dev_idx, int sublocksize)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *)context;
+ unsigned char subblk_typ = data[1];
+ int subblk_offset;
+ int chn, chnend;
+ int rc;
+ int blktyp = dev_idx & 0xC0;
+ int idx = dev_idx & 0x3F;
+ bool is_err = false;
+
+ if (idx) {
+ chn = idx-1;
+ chnend = idx;
+ } else {
+ if (tas_dev->set_global_mode) {
+ chn = tas_dev->ndev;
+ chnend = tas_dev->ndev + 1;
+ } else {
+ chn = 0;
+ chnend = tas_dev->ndev;
+ }
+ }
+
+ for (; chn < chnend; chn++) {
+ if (!tas_dev->set_global_mode &&
+ tas_dev->tasdevice[chn].is_loading == false)
+ continue;
+
+ is_err = false;
+ subblk_offset = 2;
+ switch (subblk_typ) {
+ case TASDEVICE_CMD_SING_W: {
+ int i = 0;
+ unsigned short len = SMS_HTONS(data[2], data[3]);
+
+ subblk_offset += 2;
+ if (subblk_offset + 4 * len > sublocksize) {
+ dev_err(tas_dev->dev,
+ "process_block: Out of bounary\n");
+ is_err = true;
+ break;
+ }
+
+ for (i = 0; i < len; i++) {
+ rc = tasdevice_dev_write(tas_dev, chn,
+ TASDEVICE_REG(data[subblk_offset],
+ data[subblk_offset + 1],
+ data[subblk_offset + 2]),
+ data[subblk_offset + 3]);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_dev->dev,
+ "process_block: single write error\n");
+ }
+ subblk_offset += 4;
+ }
+ }
+ break;
+ case TASDEVICE_CMD_BURST: {
+ unsigned short len =
+ SMS_HTONS(data[2], data[3]);
+
+ subblk_offset += 2;
+ if (subblk_offset + 4 + len > sublocksize) {
+ dev_err(tas_dev->dev,
+ "%s: BST Out of bounary\n",
+ __func__);
+ is_err = true;
+ break;
+ }
+ if (len % 4) {
+ dev_err(tas_dev->dev,
+ "%s:Bst-len(%u)not div by 4\n",
+ __func__, len);
+ break;
+ }
+
+ rc = tasdevice_dev_bulk_write(tas_dev, chn,
+ TASDEVICE_REG(data[subblk_offset],
+ data[subblk_offset + 1],
+ data[subblk_offset + 2]),
+ &(data[subblk_offset + 4]),
+ len);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_dev->dev,
+ "%s: bulk_write error = %d\n",
+ __func__, rc);
+ }
+ subblk_offset += (len + 4);
+ }
+ break;
+ case TASDEVICE_CMD_DELAY: {
+ unsigned short delay_time = 0;
+
+ if (subblk_offset + 2 > sublocksize) {
+ dev_err(tas_dev->dev,
+ "%s: deley Out of bounary\n",
+ __func__);
+ is_err = true;
+ break;
+ }
+ delay_time = SMS_HTONS(data[2], data[3]);
+ usleep_range(delay_time*1000, delay_time*1000);
+ subblk_offset += 2;
+ }
+ break;
+ case TASDEVICE_CMD_FIELD_W:
+ if (subblk_offset + 6 > sublocksize) {
+ dev_err(tas_dev->dev,
+ "%s: bit write Out of bounary\n",
+ __func__);
+ is_err = true;
+ break;
+ }
+ rc = tasdevice_dev_update_bits(tas_dev, chn,
+ TASDEVICE_REG(data[subblk_offset + 2],
+ data[subblk_offset + 3],
+ data[subblk_offset + 4]),
+ data[subblk_offset + 1],
+ data[subblk_offset + 5]);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_dev->dev,
+ "%s: update_bits error = %d\n",
+ __func__, rc);
+ }
+ subblk_offset += 6;
+ break;
+ default:
+ break;
+ };
+ if (is_err == true && blktyp != 0) {
+ if (blktyp == 0x80) {
+ tas_dev->tasdevice[chn].cur_prog = -1;
+ tas_dev->tasdevice[chn].cur_conf = -1;
+ } else
+ tas_dev->tasdevice[chn].cur_conf = -1;
+ }
+ }
+ return subblk_offset;
+}
+
+static void tasdevice_select_cfg_blk(void *pContext, int conf_no,
+ unsigned char block_type)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *) pContext;
+ struct tasdevice_rca *rca = &(tas_dev->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ int j, k, chn, chnend;
+
+ if (conf_no >= rca->ncfgs || conf_no < 0 || NULL == cfg_info) {
+ dev_err(tas_dev->dev,
+ "conf_no should be not more than %u\n",
+ rca->ncfgs);
+ goto out;
+ }
+
+ for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) {
+ unsigned int length = 0, rc = 0;
+
+ if (block_type > 5 || block_type < 2) {
+ dev_err(tas_dev->dev,
+ "ERROR!!!block_type should be in range from 2 to 5\n");
+ goto out;
+ }
+ if (block_type != cfg_info[conf_no]->blk_data[j]->block_type)
+ continue;
+
+ for (k = 0; k < (int)cfg_info[conf_no]->blk_data[j]->n_subblks;
+ k++) {
+ if (cfg_info[conf_no]->blk_data[j]->dev_idx) {
+ chn =
+ cfg_info[conf_no]->blk_data[j]->dev_idx
+ - 1;
+ chnend =
+ cfg_info[conf_no]->blk_data[j]->dev_idx;
+ } else {
+ chn = 0;
+ chnend = tas_dev->ndev;
+ }
+ for (; chn < chnend; chn++)
+ tas_dev->tasdevice[chn].is_loading = true;
+
+ rc = tasdevice_process_block(tas_dev,
+ cfg_info[conf_no]->blk_data[j]->regdata +
+ length,
+ cfg_info[conf_no]->blk_data[j]->dev_idx,
+ cfg_info[conf_no]->blk_data[j]->block_size -
+ length);
+ length += rc;
+ if (cfg_info[conf_no]->blk_data[j]->block_size <
+ length) {
+ dev_err(tas_dev->dev,
+ "%s: ERROR:%u %u out of bounary\n",
+ __func__, length,
+ cfg_info[conf_no]->blk_data[j]
+ ->block_size);
+ break;
+ }
+ }
+ if (length != cfg_info[conf_no]->blk_data[j]->block_size)
+ dev_err(tas_dev->dev,
+ "%s: %u %u size is not same\n",
+ __func__, length,
+ cfg_info[conf_no]->blk_data[j]->block_size);
+
+ }
+
+out:
+ return;
+}
+
+static struct tasdevice_config_info *tasdevice_add_config(
+ void *context, unsigned char *config_data,
+ unsigned int config_size)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *)context;
+ struct tasdevice_config_info *cfg_info = NULL;
+ int config_offset = 0, i;
+
+ cfg_info = kzalloc(
+ sizeof(struct tasdevice_config_info), GFP_KERNEL);
+ if (!cfg_info)
+ goto out;
+
+ if (tas_dev->rcabin.fw_hdr.binary_version_num >= 0x105) {
+ if (config_offset + 64 > (int)config_size) {
+ dev_err(tas_dev->dev, "add config: Out of bounary\n");
+ goto out;
+ }
+ config_offset += 64;
+ }
+
+ if (config_offset + 4 > (int)config_size) {
+ dev_err(tas_dev->dev, "add config: Out of bounary\n");
+ goto out;
+ }
+ cfg_info->nblocks =
+ SMS_HTONL(config_data[config_offset],
+ config_data[config_offset + 1],
+ config_data[config_offset + 2], config_data[config_offset + 3]);
+ config_offset += 4;
+
+ cfg_info->blk_data = kcalloc(
+ cfg_info->nblocks, sizeof(struct tasdev_blk_data *),
+ GFP_KERNEL);
+ if (!cfg_info->blk_data)
+ goto out;
+
+ cfg_info->real_nblocks = 0;
+ for (i = 0; i < (int)cfg_info->nblocks; i++) {
+ if (config_offset + 12 > config_size) {
+ dev_err(tas_dev->dev,
+ "add config: Out of bounary: i = %d nblocks = %u!\n",
+ i, cfg_info->nblocks);
+ break;
+ }
+ cfg_info->blk_data[i] = kzalloc(
+ sizeof(struct tasdev_blk_data), GFP_KERNEL);
+ if (!cfg_info->blk_data[i])
+ break;
+
+ cfg_info->blk_data[i]->dev_idx = config_data[config_offset];
+ config_offset++;
+
+ cfg_info->blk_data[i]->block_type = config_data[config_offset];
+ config_offset++;
+
+ if (cfg_info->blk_data[i]->block_type ==
+ TASDEVICE_BIN_BLK_PRE_POWER_UP) {
+ if (cfg_info->blk_data[i]->dev_idx == 0)
+ cfg_info->active_dev = 1;
+ else
+ cfg_info->active_dev =
+ 1 <<
+ (cfg_info->blk_data[i]->dev_idx - 1);
+
+ }
+ cfg_info->blk_data[i]->yram_checksum =
+ SMS_HTONS(config_data[config_offset],
+ config_data[config_offset + 1]);
+ config_offset += 2;
+ cfg_info->blk_data[i]->block_size =
+ SMS_HTONL(config_data[config_offset],
+ config_data[config_offset + 1],
+ config_data[config_offset + 2],
+ config_data[config_offset + 3]);
+ config_offset += 4;
+
+ cfg_info->blk_data[i]->n_subblks =
+ SMS_HTONL(config_data[config_offset],
+ config_data[config_offset + 1],
+ config_data[config_offset + 2],
+ config_data[config_offset + 3]);
+
+ config_offset += 4;
+ cfg_info->blk_data[i]->regdata = kzalloc(
+ cfg_info->blk_data[i]->block_size, GFP_KERNEL);
+ if (cfg_info->blk_data[i]->regdata == NULL)
+ goto out;
+
+ if (config_offset + cfg_info->blk_data[i]->block_size
+ > config_size) {
+ dev_err(tas_dev->dev,
+ "%s: block_size Out of bounary: i = %d blks = %u!\n",
+ __func__, i, cfg_info->nblocks);
+ break;
+ }
+ memcpy(cfg_info->blk_data[i]->regdata,
+ &config_data[config_offset],
+ cfg_info->blk_data[i]->block_size);
+ config_offset += cfg_info->blk_data[i]->block_size;
+ cfg_info->real_nblocks += 1;
+ }
+out:
+ return cfg_info;
+}
+
+/* tas2781 contain book and page two-level regmap, especially book switching
+ * will set the register BXXP00R7F, after switching to the correct book,
+ * then leverage the mechanism for paging to access the register. Custom
+ * control is primarily for regmap booking, paging depends on internal
+ * regmap mechanism
+ */
+static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int val;
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int ret = 0;
+
+ /* Read the primary device as the whole */
+ ret = tasdevice_dev_read(tas_dev, 0, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_dev->dev, "%s, get digital vol error\n", __func__);
+ goto out;
+ }
+
+ if (val > max)
+ val = max;
+ if (invert)
+ val = max - val;
+ if (val < 0)
+ val = 0;
+ ucontrol->value.integer.value[0] = val;
+
+out:
+ return ret;
+}
+
+static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int val, i;
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int err_cnt = 0;
+ int ret = 1;
+
+ val = ucontrol->value.integer.value[0];
+ if (val > max)
+ val = max;
+ if (invert)
+ val = max - val;
+ if (val < 0)
+ val = 0;
+
+ if (!tas_dev->set_global_mode) {
+ ret = tasdevice_dev_write(tas_dev, tas_dev->ndev,
+ mc->reg, (unsigned int)val);
+ if (ret)
+ dev_err(tas_dev->dev, "%s, error in global mode\n",
+ __func__);
+ }
+
+ if (tas_dev->set_global_mode == NULL || ret == 1) {
+ for (i = 0; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_write(tas_dev, i, mc->reg,
+ (unsigned int)val);
+ if (ret) {
+ err_cnt++;
+ dev_err(tas_dev->dev,
+ "set digital vol err in dev %d\n", i);
+ }
+ }
+ }
+ /* All the devices set error, return 0*/
+ return (err_cnt == tas_dev->ndev) ? 0 : 1;
+}
+
+static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int val;
+ unsigned char mask = 0;
+ int max = mc->max;
+ int ret = 0;
+ unsigned int invert = mc->invert;
+
+ /* Read the primary device */
+ ret = tasdevice_dev_read(tas_dev, 0, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__);
+ goto out;
+ }
+
+ mask = (1 << fls(mc->max)) - 1;
+ mask <<= mc->shift;
+ val = (val & mask) >> mc->shift;
+ if (val > max)
+ val = max;
+ if (invert)
+ val = max - val;
+ if (val < 0)
+ val = 0;
+ ucontrol->value.integer.value[0] = val;
+
+out:
+ return ret;
+}
+
+static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int val;
+ int i, ret;
+ unsigned char mask;
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+ int err_cnt = 0;
+
+ mask = (1 << fls(mc->max)) - 1;
+ mask <<= mc->shift;
+ val = ucontrol->value.integer.value[0];
+ if (val > max)
+ val = max;
+ if (invert)
+ val = max - val;
+ if (val < 0)
+ val = 0;
+ for (i = 0; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_update_bits(tas_dev, i,
+ mc->reg, mask, (unsigned int)(val << mc->shift));
+ if (ret) {
+ err_cnt++;
+ dev_err(tas_dev->dev, "set AMP vol error in dev %d\n",
+ i);
+ }
+ }
+
+ /* All the devices set error, return 0*/
+ return (err_cnt == tas_dev->ndev) ? 0 : 1;
+}
+
+static const DECLARE_TLV_DB_SCALE(dvc_tlv, -10000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(amp_vol_tlv, 1100, 50, 0);
+
+static const struct snd_kcontrol_new tas2781_snd_controls[] = {
+ SOC_SINGLE_RANGE_EXT_TLV("Amp Gain Volume", TAS2781_AMP_LEVEL,
+ 1, 0, 20, 0, tas2781_amp_getvol,
+ tas2781_amp_putvol, amp_vol_tlv),
+ SOC_SINGLE_RANGE_EXT_TLV("Digital Volume", TAS2781_DVC_LVL,
+ 0, 0, 200, 1, tas2781_digital_getvol,
+ tas2781_digital_putvol, dvc_tlv),
+};
+
+static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+
+ tas_dev->rcabin.profile_cfg_id =
+ ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static int tasdevice_info_programs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ struct tasdevice_fw *tas_fw = tas_dev->fmw;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = (int)tas_fw->nr_programs;
+
+ return 0;
+}
+
+static int tasdevice_info_configurations(
+ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ struct tasdevice_fw *tas_fw = tas_dev->fmw;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = (int)tas_fw->nr_configurations - 1;
+
+
+ return 0;
+}
+
+static int tasdevice_info_profile(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+
+ uinfo->count = 1;
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = max(0, tas_dev->rcabin.ncfgs);
+
+ return 0;
+}
+
+static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev
+ = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] =
+ tas_dev->rcabin.profile_cfg_id;
+
+ return 0;
+}
+
+static int tasdevice_create_controls(struct tasdevice_priv *tas_dev)
+{
+ int nr_controls = 1, ret = 0, mix_index = 0;
+ char *name = NULL;
+ struct snd_kcontrol_new *tasdevice_profile_controls = NULL;
+
+ tasdevice_profile_controls = devm_kzalloc(tas_dev->dev,
+ nr_controls * sizeof(tasdevice_profile_controls[0]),
+ GFP_KERNEL);
+ if (tasdevice_profile_controls == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Create a mixer item for selecting the active profile */
+ name = devm_kzalloc(tas_dev->dev,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "TASDEVICE Profile id");
+ tasdevice_profile_controls[mix_index].name = name;
+ tasdevice_profile_controls[mix_index].iface =
+ SNDRV_CTL_ELEM_IFACE_MIXER;
+ tasdevice_profile_controls[mix_index].info =
+ tasdevice_info_profile;
+ tasdevice_profile_controls[mix_index].get =
+ tasdevice_get_profile_id;
+ tasdevice_profile_controls[mix_index].put =
+ tasdevice_set_profile_id;
+ mix_index++;
+
+ ret = snd_soc_add_component_controls(tas_dev->codec,
+ tasdevice_profile_controls,
+ nr_controls < mix_index ? nr_controls : mix_index);
+
+ tas_dev->tas_ctrl.nr_controls =
+ nr_controls < mix_index ? nr_controls : mix_index;
+out:
+ return ret;
+}
+
+static int tasdevice_program_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(pKcontrol);
+ struct tasdevice_priv *pTAS2781 = snd_soc_component_get_drvdata(codec);
+
+ pValue->value.integer.value[0] = pTAS2781->cur_prog;
+
+ return 0;
+}
+
+static int tasdevice_program_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(pKcontrol);
+ struct tasdevice_priv *pTAS2781 = snd_soc_component_get_drvdata(codec);
+ unsigned int nProgram = pValue->value.integer.value[0];
+
+ pTAS2781->cur_prog = nProgram;
+
+ return 1;
+}
+
+static int tasdevice_configuration_get(
+ struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(pKcontrol);
+ struct tasdevice_priv *pTAS2781 = snd_soc_component_get_drvdata(codec);
+
+ pValue->value.integer.value[0] = pTAS2781->cur_conf;
+
+ return 0;
+}
+
+static int tasdevice_configuration_put(
+ struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(pKcontrol);
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+ unsigned int n_configuration = pValue->value.integer.value[0];
+
+ tas_dev->cur_conf = n_configuration;
+
+ return 1;
+}
+
+static int tasdevice_dsp_create_control(
+ struct tasdevice_priv *tas_dev)
+{
+ int nr_controls = 2, ret = 0, mix_index = 0;
+ char *program_name = NULL;
+ char *configuration_name = NULL;
+ struct snd_kcontrol_new *tasdevice_dsp_controls = NULL;
+
+ tasdevice_dsp_controls = devm_kzalloc(tas_dev->dev,
+ nr_controls * sizeof(tasdevice_dsp_controls[0]),
+ GFP_KERNEL);
+ if (tasdevice_dsp_controls == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Create a mixer item for selecting the active profile */
+ program_name = devm_kzalloc(tas_dev->dev,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL);
+ configuration_name = devm_kzalloc(tas_dev->dev,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL);
+ if (!program_name || !configuration_name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ scnprintf(program_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "Program");
+ tasdevice_dsp_controls[mix_index].name = program_name;
+ tasdevice_dsp_controls[mix_index].iface =
+ SNDRV_CTL_ELEM_IFACE_MIXER;
+ tasdevice_dsp_controls[mix_index].info =
+ tasdevice_info_programs;
+ tasdevice_dsp_controls[mix_index].get =
+ tasdevice_program_get;
+ tasdevice_dsp_controls[mix_index].put =
+ tasdevice_program_put;
+ mix_index++;
+
+ scnprintf(configuration_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ "Configuration");
+ tasdevice_dsp_controls[mix_index].name = configuration_name;
+ tasdevice_dsp_controls[mix_index].iface =
+ SNDRV_CTL_ELEM_IFACE_MIXER;
+ tasdevice_dsp_controls[mix_index].info =
+ tasdevice_info_configurations;
+ tasdevice_dsp_controls[mix_index].get =
+ tasdevice_configuration_get;
+ tasdevice_dsp_controls[mix_index].put =
+ tasdevice_configuration_put;
+ mix_index++;
+
+ ret = snd_soc_add_component_controls(tas_dev->codec,
+ tasdevice_dsp_controls,
+ nr_controls < mix_index ? nr_controls : mix_index);
+
+ tas_dev->tas_ctrl.nr_controls += nr_controls;
+out:
+ return ret;
+}
+
+static void tasdevice_rca_ready(const struct firmware *fmw,
+ void *context)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *) context;
+ struct tasdevice_rca *rca;
+ struct tasdevice_rca_hdr *fw_hdr;
+ struct tasdevice_config_info **cfg_info = NULL;
+ const struct firmware *fw_entry;
+ unsigned char *buf;
+ int offset = 0;
+ int i, ret;
+ unsigned int total_config_sz = 0;
+
+ mutex_lock(&tas_dev->codec_lock);
+ rca = &(tas_dev->rcabin);
+ fw_hdr = &(rca->fw_hdr);
+ if (!fmw || !fmw->data) {
+ dev_err(tas_dev->dev,
+ "Failed to read %s, no side - effect on driver running\n",
+ tas_dev->rca_binaryname);
+ ret = -1;
+ goto out;
+ }
+ buf = (unsigned char *)fmw->data;
+
+ fw_hdr->img_sz = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (fw_hdr->img_sz != fmw->size) {
+ dev_err(tas_dev->dev,
+ "File size not match, %d %u", (int)fmw->size,
+ fw_hdr->img_sz);
+ ret = -1;
+ goto out;
+ }
+
+ fw_hdr->checksum = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ fw_hdr->binary_version_num = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->binary_version_num < 0x103) {
+ dev_err(tas_dev->dev,
+ "File version 0x%04x is too low",
+ fw_hdr->binary_version_num);
+ ret = -1;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->drv_fw_version = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 8;
+ fw_hdr->plat_type = buf[offset];
+ offset += 1;
+ fw_hdr->dev_family = buf[offset];
+ offset += 1;
+ fw_hdr->reserve = buf[offset];
+ offset += 1;
+ fw_hdr->ndev = buf[offset];
+ offset += 1;
+ if (fw_hdr->ndev != tas_dev->ndev) {
+ dev_err(tas_dev->dev,
+ "ndev(%u) fro rcabin and ndev(%u) fro DTS NOT match\n",
+ fw_hdr->ndev, tas_dev->ndev);
+ ret = -1;
+ goto out;
+ }
+ if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) {
+ dev_err(tas_dev->dev, "rca_ready: Out of bounary!\n");
+ ret = -1;
+ goto out;
+ }
+
+ for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++)
+ fw_hdr->devs[i] = buf[offset];
+
+ fw_hdr->nconfig = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+
+ for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) {
+ fw_hdr->config_size[i] = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ total_config_sz += fw_hdr->config_size[i];
+ }
+
+ if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) {
+ dev_err(tas_dev->dev, "Bin file error!\n");
+ ret = -1;
+ goto out;
+ }
+ cfg_info = kcalloc(fw_hdr->nconfig,
+ sizeof(struct tasdevice_config_info *),
+ GFP_KERNEL);
+
+ if (!cfg_info) {
+ ret = -1;
+ goto out;
+ }
+ rca->cfg_info = cfg_info;
+ rca->ncfgs = 0;
+ for (i = 0; i < (int)fw_hdr->nconfig; i++) {
+ cfg_info[i] = tasdevice_add_config(context, &buf[offset],
+ fw_hdr->config_size[i]);
+ if (!cfg_info[i]) {
+ ret = -1;
+ break;
+ }
+ offset += (int)fw_hdr->config_size[i];
+ rca->ncfgs += 1;
+ }
+ tasdevice_create_controls(tas_dev);
+
+ tasdevice_dsp_remove(tas_dev);
+ tasdevice_calbin_remove(tas_dev);
+ tas_dev->fw_state = TASDEVICE_DSP_FW_PENDING;
+ scnprintf(tas_dev->coef_binaryname, 64, "%s_coef.bin",
+ tas_dev->dev_name);
+ ret = request_firmware(&fw_entry, tas_dev->coef_binaryname,
+ tas_dev->dev);
+ if (!ret) {
+ ret = tasdevice_dspfw_ready(fw_entry, tas_dev);
+ release_firmware(fw_entry);
+ fw_entry = NULL;
+ } else {
+ tas_dev->fw_state = TASDEVICE_DSP_FW_FAIL;
+ dev_err(tas_dev->dev, "%s: load %s error\n", __func__,
+ tas_dev->coef_binaryname);
+ goto out;
+ }
+ tasdevice_dsp_create_control(tas_dev);
+
+ tas_dev->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+ tas_dev->is_glb_calibrated_data_loaded = true;
+ for (i = 0; i < tas_dev->ndev; i++) {
+ scnprintf(tas_dev->cal_binaryname[i], 64, "%s_cal_0x%02x.bin",
+ tas_dev->dev_name, tas_dev->tasdevice[i].dev_addr);
+ ret = tas2781_load_calibration(tas_dev,
+ tas_dev->cal_binaryname[i], i);
+ if (ret != 0) {
+ dev_err(tas_dev->dev,
+ "%s: load %s error, no-side effect\n",
+ __func__, tas_dev->cal_binaryname[i]);
+ ret = 0;
+ tas_dev->is_glb_calibrated_data_loaded = false;
+ }
+ }
+
+out:
+ mutex_unlock(&tas_dev->codec_lock);
+ if (fmw)
+ release_firmware(fmw);
+}
+
+static void tasdevice_config_info_remove(void *context)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *) context;
+ struct tasdevice_rca *rca = &(tas_dev->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ int i, j;
+
+ if (cfg_info) {
+ for (i = 0; i < rca->ncfgs; i++) {
+ if (cfg_info[i]) {
+ for (j = 0; j < (int)cfg_info[i]->real_nblocks;
+ j++) {
+ kfree(
+ cfg_info[i]->blk_data[j]->regdata);
+ kfree(cfg_info[i]->blk_data[j]);
+ }
+ kfree(cfg_info[i]->blk_data);
+ kfree(cfg_info[i]);
+ }
+ }
+ kfree(cfg_info);
+ }
+}
+
+static void tasdevice_tuning_switch(
+ struct tasdevice_priv *tas_dev, int state)
+{
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+ int profile_cfg_id = 0;
+
+ if (state == 0) {
+ if (tas_fmw) {
+ if (tas_dev->cur_prog >= tas_fmw->nr_programs)
+ /*bypass all in rca is profile id 0*/
+ profile_cfg_id = RCA_CONFIGID_BYPASS_ALL;
+ else {
+ /*dsp mode or tuning mode*/
+ profile_cfg_id =
+ tas_dev->rcabin.profile_cfg_id;
+
+ tasdevice_select_tuningprm_cfg(tas_dev,
+ tas_dev->cur_prog,
+ tas_dev->cur_conf,
+ profile_cfg_id);
+ if (tas_dev->set_global_mode != NULL)
+ tas_dev->set_global_mode(tas_dev);
+ }
+ } else
+ profile_cfg_id = RCA_CONFIGID_BYPASS_ALL;
+
+ tasdevice_select_cfg_blk(tas_dev, profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+ } else
+ tasdevice_select_cfg_blk(tas_dev,
+ tas_dev->rcabin.profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_SHUTDOWN);
+}
+
+static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+ int state = 1;
+
+ /* Codec Lock Hold */
+ mutex_lock(&tas_dev->codec_lock);
+ if (event == SND_SOC_DAPM_PRE_PMD)
+ state = 0;
+ tasdevice_tuning_switch(tas_dev, state);
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_dev->codec_lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM,
+ 0, 0, tasdevice_dapm_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_INPUT("DMIC")
+};
+
+static const struct snd_soc_dapm_route tasdevice_audio_map[] = {
+ {"SPK", NULL, "ASI"},
+ {"OUT", NULL, "SPK"},
+ {"ASI OUT", NULL, "DMIC"}
+};
+
+static int tasdevice_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+ int ret = 0;
+
+ if (tas_dev->fw_state != TASDEVICE_DSP_FW_ALL_OK) {
+ dev_err(tas_dev->dev, "DSP bin file not loaded\n");
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int tasdevice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct tasdevice_priv *tas_dev = snd_soc_dai_get_drvdata(dai);
+ unsigned int fsrate;
+ unsigned int slot_width;
+ int bclk_rate;
+ int rc = 0;
+
+ fsrate = params_rate(params);
+ switch (fsrate) {
+ case 48000:
+ break;
+ case 44100:
+ break;
+ default:
+ dev_err(tas_dev->dev, "%s: incorrect sample rate = %u\n",
+ __func__, fsrate);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ slot_width = params_width(params);
+ switch (slot_width) {
+ case 16:
+ break;
+ case 20:
+ break;
+ case 24:
+ break;
+ case 32:
+ break;
+ default:
+ dev_err(tas_dev->dev, "%s: incorrect slot width = %u\n",
+ __func__, slot_width);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0) {
+ dev_err(tas_dev->dev, "%s: incorrect bclk rate = %d\n",
+ __func__, bclk_rate);
+ rc = bclk_rate;
+ goto out;
+ }
+out:
+ return rc;
+}
+
+static int tasdevice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct tasdevice_priv *tas_dev = snd_soc_dai_get_drvdata(codec_dai);
+
+ tas_dev->sysclk = freq;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tasdevice_dai_ops = {
+ .startup = tasdevice_startup,
+ .hw_params = tasdevice_hw_params,
+ .set_sysclk = tasdevice_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver tasdevice_dai_driver[] = {
+ {
+ .name = "tas2781_codec",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TASDEVICE_RATES,
+ .formats = TASDEVICE_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TASDEVICE_RATES,
+ .formats = TASDEVICE_FORMATS,
+ },
+ .ops = &tasdevice_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static void tas2781_reset(struct tasdevice_priv *tas_dev)
+{
+ int ret = 0;
+ int i = 0;
+
+ if (tas_dev->reset) {
+ gpiod_set_value_cansleep(tas_dev->reset, 0);
+ usleep_range(500, 1000);
+ gpiod_set_value_cansleep(tas_dev->reset, 1);
+ } else {
+
+ for (i = 0; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_write(tas_dev, i,
+ TAS2871_REG_SWRESET,
+ TAS2871_REG_SWRESET_RESET);
+ if (ret < 0)
+ dev_err(tas_dev->dev,
+ "dev %d swreset fail, %d\n",
+ i, ret);
+ }
+ }
+ usleep_range(1000, 1050);
+}
+
+static int tasdevice_codec_probe(
+ struct snd_soc_component *codec)
+{
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ /* Codec Lock Hold */
+ mutex_lock(&tas_dev->codec_lock);
+
+ crc8_populate_msb(tas_dev->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL);
+ tas_dev->codec = codec;
+ scnprintf(tas_dev->rca_binaryname, 64, "%s_rca.bin",
+ tas_dev->dev_name);
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+ tas_dev->rca_binaryname, tas_dev->dev, GFP_KERNEL, tas_dev,
+ tasdevice_rca_ready);
+ if (ret)
+ dev_err(tas_dev->dev, "request_firmware_nowait err:0x%08x\n",
+ ret);
+
+ tas2781_reset(tas_dev);
+ if (tas_dev->set_global_mode != NULL)
+ tas_dev->set_global_mode(tas_dev);
+
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_dev->codec_lock);
+
+ return ret;
+}
+
+static void tasdevice_deinit(void *context)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+
+ tasdevice_config_info_remove(tas_dev);
+ tasdevice_dsp_remove(tas_dev);
+ tasdevice_calbin_remove(tas_dev);
+ tas_dev->fw_state = TASDEVICE_DSP_FW_PENDING;
+}
+
+static void tasdevice_codec_remove(
+ struct snd_soc_component *codec)
+{
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ /* Codec Lock Hold */
+ mutex_lock(&tas_dev->codec_lock);
+ tasdevice_deinit(tas_dev);
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_dev->codec_lock);
+}
+
+static const struct snd_soc_component_driver
+ soc_codec_driver_tasdevice = {
+ .probe = tasdevice_codec_probe,
+ .remove = tasdevice_codec_remove,
+ .controls = tas2781_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2781_snd_controls),
+ .dapm_widgets = tasdevice_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tasdevice_dapm_widgets),
+ .dapm_routes = tasdevice_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tasdevice_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static int tasdevice_parse_dt(struct tasdevice_priv *tas_dev)
+{
+ struct device_node *np = tas_dev->dev->of_node;
+ int rc, i, ndev;
+ unsigned int dev_addrs[max_chn];
+ struct i2c_client *client = (struct i2c_client *)tas_dev->client;
+
+ if (tas_dev->isacpi) {
+ ndev = device_property_read_u32_array(&client->dev,
+ "ti,audio-slots", NULL, 0);
+ if (ndev <= 0) {
+ ndev = 1;
+ dev_addrs[0] = client->addr;
+ } else {
+ ndev = (ndev < ARRAY_SIZE(dev_addrs))
+ ? ndev : ARRAY_SIZE(dev_addrs);
+ ndev = device_property_read_u32_array(&client->dev,
+ "ti,audio-slots", dev_addrs, ndev);
+ }
+
+ tas_dev->irq_info.irq_gpio =
+ acpi_dev_gpio_irq_get(ACPI_COMPANION(&client->dev), 0);
+ } else {
+ ndev = of_property_read_variable_u32_array(np,
+ "ti,audio-slots", dev_addrs, 0, ARRAY_SIZE(dev_addrs));
+ if (ndev <= 0) {
+ ndev = 1;
+ dev_addrs[0] = client->addr;
+ }
+ tas_dev->irq_info.irq_gpio = of_irq_get(np, 0);
+ }
+ tas_dev->ndev = ndev;
+ for (i = 0; i < ndev; i++)
+ tas_dev->tasdevice[i].dev_addr = dev_addrs[i];
+
+ if (ndev > 1) {
+ rc = of_property_read_u32(np, "ti,broadcast-addr",
+ &(tas_dev->glb_addr.dev_addr));
+ if (rc) {
+ dev_err(tas_dev->dev, "Looking up node %s failed %d\n",
+ np->full_name, rc);
+ tas_dev->glb_addr.dev_addr = 0;
+ }
+ }
+
+ tas_dev->ndev = ndev;
+
+ tas_dev->reset = devm_gpiod_get_optional(&client->dev,
+ "reset-gpios", GPIOD_OUT_HIGH);
+ if (IS_ERR(tas_dev->reset))
+ dev_err(tas_dev->dev, "%s ERROR: Can't get reset GPIO\n",
+ __func__);
+
+ strcpy(tas_dev->dev_name, tasdevice_id[tas_dev->chip_id].name);
+
+ tas_dev->irq_info.irq_gpio = of_get_named_gpio(np,
+ "interrupts", 0);
+ if (gpio_is_valid(tas_dev->irq_info.irq_gpio)) {
+ rc = gpio_request(tas_dev->irq_info.irq_gpio,
+ "AUDEV-IRQ");
+ if (!rc) {
+ gpio_direction_input(
+ tas_dev->irq_info.irq_gpio);
+
+ tas_dev->irq_info.irq =
+ gpio_to_irq(tas_dev->irq_info.irq_gpio);
+ } else
+ dev_err(tas_dev->dev, "%s: GPIO %d request error\n",
+ __func__, tas_dev->irq_info.irq_gpio);
+ } else
+ dev_err(tas_dev->dev,
+ "Looking up irq-gpio property in node %s failed %d\n",
+ np->full_name, tas_dev->irq_info.irq_gpio);
+
+ return 0;
+}
+
+static int tasdevice_change_chn_book(struct tasdevice_priv *tas_dev,
+ enum channel chn, int book)
+{
+ int ret = 0;
+ struct i2c_client *client =
+ (struct i2c_client *)tas_dev->client;
+
+ if (chn < tas_dev->ndev) {
+ if (tas_dev->glb_addr.ref_cnt != 0) {
+ tas_dev->glb_addr.ref_cnt = 0;
+ tas_dev->glb_addr.cur_book = -1;
+ }
+ client->addr = tas_dev->tasdevice[chn].dev_addr;
+ if (tas_dev->tasdevice[chn].cur_book != book) {
+ ret = regmap_write(tas_dev->regmap,
+ TASDEVICE_BOOKCTL_REG, book);
+ if (ret < 0) {
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, ret);
+ goto out;
+ }
+ tas_dev->tasdevice[chn].cur_book = book;
+ }
+ } else if (chn == tas_dev->ndev) {
+ int i = 0;
+
+ if (tas_dev->glb_addr.ref_cnt == 0)
+ for (i = 0; i < tas_dev->ndev; i++)
+ tas_dev->tasdevice[i].cur_book = -1;
+ client->addr = tas_dev->glb_addr.dev_addr;
+ if (tas_dev->glb_addr.cur_book != book) {
+ ret = regmap_write(tas_dev->regmap,
+ TASDEVICE_BOOKCTL_PAGE, 0);
+ if (ret < 0) {
+ dev_err(tas_dev->dev,
+ "%s, 0ERROR, E=%d\n",
+ __func__, ret);
+ goto out;
+ }
+ ret = regmap_write(tas_dev->regmap,
+ TASDEVICE_BOOKCTL_REG, book);
+ if (ret < 0) {
+ dev_err(tas_dev->dev,
+ "%s, book%xERROR, E=%d\n",
+ __func__, book, ret);
+ goto out;
+ }
+ tas_dev->glb_addr.cur_book = book;
+ }
+
+ tas_dev->glb_addr.ref_cnt++;
+ } else
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+
+out:
+ return ret;
+}
+
+int tasdevice_dev_read(struct tasdevice_priv *tas_dev,
+ enum channel chn, unsigned int reg, unsigned int *val)
+{
+ int ret = 0;
+
+ if (chn < tas_dev->ndev) {
+ ret = tasdevice_change_chn_book(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_read(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), val);
+ if (ret < 0)
+ dev_err(tas_dev->dev, "%s, ERROR,E=%d\n",
+ __func__, ret);
+ } else {
+
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+ }
+
+out:
+ return ret;
+}
+
+int tasdevice_dev_write(struct tasdevice_priv *tas_dev,
+ enum channel chn, unsigned int reg, unsigned int value)
+{
+ int ret = 0;
+
+ if (chn <= tas_dev->ndev) {
+ ret = tasdevice_change_chn_book(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_write(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), value);
+ if (ret < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, ret);
+ } else
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+out:
+ return ret;
+}
+
+int tasdevice_dev_bulk_write(
+ struct tasdevice_priv *tas_dev, enum channel chn,
+ unsigned int reg, unsigned char *data,
+ unsigned int len)
+{
+ int ret = 0;
+
+ if (chn <= tas_dev->ndev) {
+ ret = tasdevice_change_chn_book(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_write(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), data, len);
+ if (ret < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n", __func__,
+ ret);
+ } else
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+out:
+ return ret;
+}
+
+int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_dev,
+ enum channel chn, unsigned int reg, unsigned char *data,
+ unsigned int len)
+{
+ int ret = 0;
+
+ if (chn < tas_dev->ndev) {
+ ret = tasdevice_change_chn_book(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_read(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), data, len);
+ if (ret < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, ret);
+ } else
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+
+out:
+ return ret;
+}
+
+int tasdevice_dev_update_bits(
+ struct tasdevice_priv *tas_dev, enum channel chn,
+ unsigned int reg, unsigned int mask, unsigned int value)
+{
+ int ret = 0;
+ struct i2c_client *client =
+ (struct i2c_client *)tas_dev->client;
+
+ if (chn < tas_dev->ndev) {
+ ret = tasdevice_change_chn_book(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+ client->addr = tas_dev->tasdevice[chn].dev_addr;
+ ret = regmap_update_bits(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), mask, value);
+ if (ret < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, ret);
+ } else {
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+ ret = -1;
+ }
+
+out:
+ return ret;
+}
+
+static void tas2781_set_global_mode(struct tasdevice_priv *tas_dev)
+{
+ int i = 0;
+ int ret = 0;
+
+ for (; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_update_bits(tas_dev, i,
+ TAS2871_MISC_CFG2, TAS2871_GLOBAL_ADDR_MASK,
+ TAS2871_GLOBAL_ADDR_ENABLE);
+ if (ret < 0) {
+ dev_err(tas_dev->dev, "%s: chn %d set glb fail, %d\n",
+ __func__, i, ret);
+ continue;
+ }
+ }
+}
+
+static int tasdevice_init(struct tasdevice_priv *tas_dev)
+{
+ int ret, i;
+
+ tas_dev->cur_prog = -1;
+ tas_dev->cur_conf = -1;
+
+ for (i = 0; i < tas_dev->ndev; i++) {
+ tas_dev->tasdevice[i].cur_book = -1;
+ tas_dev->tasdevice[i].cur_prog = -1;
+ tas_dev->tasdevice[i].cur_conf = -1;
+ }
+
+ if (tas_dev->glb_addr.dev_addr != 0
+ && tas_dev->glb_addr.dev_addr < 0x7F)
+ tas_dev->set_global_mode = tas2781_set_global_mode;
+ dev_set_drvdata(tas_dev->dev, tas_dev);
+
+ mutex_init(&tas_dev->codec_lock);
+ ret = devm_snd_soc_register_component(tas_dev->dev,
+ &soc_codec_driver_tasdevice,
+ tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver));
+ if (ret) {
+ dev_err(tas_dev->dev, "%s: codec register error:0x%08x\n",
+ __func__, ret);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static void tasdevice_remove(struct tasdevice_priv *tas_dev)
+{
+ if (gpio_is_valid(tas_dev->irq_info.irq_gpio))
+ gpio_free(tas_dev->irq_info.irq_gpio);
+
+ mutex_destroy(&tas_dev->codec_lock);
+}
+
+static int tasdevice_i2c_probe(struct i2c_client *i2c)
+{
+ struct tasdevice_priv *tas_dev = NULL;
+ int ret;
+ const struct acpi_device_id *acpi_id;
+ const struct i2c_device_id *id = i2c_match_id(tasdevice_id, i2c);
+
+ tas_dev = devm_kzalloc(&i2c->dev, sizeof(*tas_dev), GFP_KERNEL);
+ if (!tas_dev) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (ACPI_HANDLE(&i2c->dev)) {
+ acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table,
+ &i2c->dev);
+ if (!acpi_id) {
+ dev_err(&i2c->dev, "No driver data\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ tas_dev->chip_id = acpi_id->driver_data;
+ tas_dev->isacpi = true;
+ } else {
+ tas_dev->chip_id = (id != NULL) ? id->driver_data : 0;
+ tas_dev->isacpi = false;
+ }
+
+ tas_dev->dev = &i2c->dev;
+ tas_dev->client = (void *)i2c;
+
+ if (i2c->dev.of_node)
+ ret = tasdevice_parse_dt(tas_dev);
+ else {
+ dev_err(tas_dev->dev, "No DTS info\n");
+ goto out;
+ }
+
+ tas_dev->regmap = devm_regmap_init_i2c(i2c,
+ &tasdevice_regmap);
+ if (IS_ERR(tas_dev->regmap)) {
+ ret = PTR_ERR(tas_dev->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ goto out;
+ }
+ ret = tasdevice_init(tas_dev);
+
+out:
+ if (ret < 0 && tas_dev != NULL)
+ tasdevice_remove(tas_dev);
+ return ret;
+
+}
+
+static void tasdevice_i2c_remove(struct i2c_client *client)
+{
+ struct tasdevice_priv *tas_dev = i2c_get_clientdata(client);
+
+ tasdevice_remove(tas_dev);
+
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id tasdevice_acpi_match[] = {
+ { "TAS2781", TAS2781 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match);
+#endif
+
+static struct i2c_driver tasdevice_i2c_driver = {
+ .driver = {
+ .name = "tas2781-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tasdevice_of_match),
+ .acpi_match_table = ACPI_PTR(tasdevice_acpi_match),
+ },
+ .probe = tasdevice_i2c_probe,
+ .remove = tasdevice_i2c_remove,
+ .id_table = tasdevice_id,
+};
+
+module_i2c_driver(tasdevice_i2c_driver);
+
+MODULE_AUTHOR("Shenghao Ding <shenghao-ding(a)ti.com>");
+MODULE_AUTHOR("Kevin Lu <kevin-lu(a)ti.com>");
+MODULE_DESCRIPTION("ASoC TAS2781 Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2781.h b/sound/soc/codecs/tas2781.h
new file mode 100644
index 000000000000..383a330df865
--- /dev/null
+++ b/sound/soc/codecs/tas2781.h
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+//
+// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+//
+// Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2781 driver implements a flexible and configurable algo coff setting
+// for one, two, even multiple TAS2781 chips.
+//
+// Author: Shenghao Ding <shenghao-ding(a)ti.com>
+// Author: Kevin Lu <kevin-lu(a)ti.com>
+//
+
+#ifndef __TAS2781_H__
+#define __TAS2781_H__
+
+#include "tas2781-dsp.h"
+
+#define TAS2781_DRV_VER 1 /* version number */
+#define SMARTAMP_MODULE_NAME "tas2781"
+#define TASDEVICE_RATES (SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_88200)
+#define TASDEVICE_MAX_CHANNELS 8
+
+#define TASDEVICE_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+/*PAGE Control Register (available in page0 of each book) */
+#define TASDEVICE_PAGE_SELECT 0x00
+#define TASDEVICE_BOOKCTL_PAGE 0x00
+#define TASDEVICE_BOOKCTL_REG 127
+#define TASDEVICE_BOOK_ID(reg) (reg / (256 * 128))
+#define TASDEVICE_PAGE_ID(reg) ((reg % (256 * 128)) / 128)
+#define TASDEVICE_PAGE_REG(reg) ((reg % (256 * 128)) % 128)
+#define TASDEVICE_PGRG(reg) ((reg % (256 * 128)))
+#define TASDEVICE_REG(book, page, reg) (((book * 256 * 128) + \
+ (page * 128)) + reg)
+
+/*Software Reset */
+#define TAS2871_REG_SWRESET TASDEVICE_REG(0x0, 0X0, 0x02)
+#define TAS2871_REG_SWRESET_RESET BIT(0)
+
+/* Enable Global addresses */
+#define TAS2871_MISC_CFG2 TASDEVICE_REG(0x0, 0X0, 0x07)
+#define TAS2871_GLOBAL_ADDR_MASK BIT(1)
+#define TAS2871_GLOBAL_ADDR_ENABLE BIT(1)
+
+/*I2C Checksum */
+#define TASDEVICE_I2CChecksum TASDEVICE_REG(0x0, 0x0, 0x7E)
+
+/* Volume control */
+#define TAS2781_DVC_LVL TASDEVICE_REG(0x0, 0x0, 0x1A)
+#define TAS2781_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x03)
+#define TAS2781_AMP_LEVEL_MASK GENMASK(5, 1)
+
+#define TASDEVICE_CMD_SING_W 0x1
+#define TASDEVICE_CMD_BURST 0x2
+#define TASDEVICE_CMD_DELAY 0x3
+#define TASDEVICE_CMD_FIELD_W 0x4
+
+enum audio_device {
+ TAS2781 = 0,
+};
+
+#define SMS_HTONS(a, b) ((((a)&0x00FF)<<8) | ((b)&0x00FF))
+#define SMS_HTONL(a, b, c, d) ((((a)&0x000000FF)<<24) | \
+ (((b)&0x000000FF)<<16) | (((c)&0x000000FF)<<8) | \
+ ((d)&0x000000FF))
+
+struct tasdevice {
+ unsigned int dev_addr;
+ unsigned int err_code;
+ short cur_prog;
+ short cur_conf;
+ bool is_loading;
+ bool is_loaderr;
+ bool is_calibrated_data_loaded;
+ unsigned char cur_book;
+ struct tasdevice_fw *cali_data_fmw;
+};
+
+/*
+ * This item is used to store the generic i2c address of
+ * all the tas2781 devices for I2C broadcast during the multi-device
+ * writes, useless in mono case.
+ */
+struct global_addr {
+ unsigned char cur_book;
+ unsigned int dev_addr;
+ int ref_cnt;
+};
+
+struct tas_control {
+ struct snd_kcontrol_new *tasdevice_profile_controls;
+ int nr_controls;
+};
+
+struct tasdevice_irqinfo {
+ int irq_gpio;
+ int irq;
+};
+
+struct tasdevice_priv {
+ struct device *dev;
+ void *client;
+ struct regmap *regmap;
+ /* Lock for serialising dsp processing on this mutex */
+ struct mutex codec_lock;
+ struct gpio_desc *reset;
+ struct tasdevice tasdevice[max_chn];
+ struct tasdevice_fw *fmw;
+ struct tasdevice_rca rcabin;
+ struct tasdevice_irqinfo irq_info;
+ struct tas_control tas_ctrl;
+ struct global_addr glb_addr;
+ bool is_glb_calibrated_data_loaded;
+ bool isacpi;
+ int cur_prog;
+ int cur_conf;
+ unsigned int chip_id;
+ void (*set_global_mode)(struct tasdevice_priv *tas_dev);
+ int (*fw_parse_variable_header)(struct tasdevice_priv *tas_dev,
+ const struct firmware *fmw, int offset);
+ int (*fw_parse_program_data)(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset);
+ int (*fw_parse_configuration_data)(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset);
+ int (*tasdevice_load_block)(struct tasdevice_priv *tas_dev,
+ struct tasdev_blk *pBlock);
+ int (*fw_parse_calibration_data)(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset);
+ int fw_state;
+ unsigned int magic_num;
+ unsigned char ndev;
+ unsigned char dev_name[32];
+ unsigned char rca_binaryname[64];
+ unsigned char coef_binaryname[64];
+ unsigned char cal_binaryname[max_chn][64];
+ unsigned char crc8_lkp_tbl[CRC8_TABLE_SIZE];
+ void *codec;
+ unsigned int sysclk;
+};
+
+int tasdevice_dev_read(struct tasdevice_priv *tasdevice,
+ enum channel chn, unsigned int reg, unsigned int *value);
+int tasdevice_process_block(void *context,
+ unsigned char *data, unsigned char dev_idx, int sublocksize);
+int tasdevice_dev_write(struct tasdevice_priv *tasdevice,
+ enum channel chn, unsigned int reg, unsigned int value);
+
+int tasdevice_dev_bulk_write(
+ struct tasdevice_priv *tasdevice, enum channel chn,
+ unsigned int reg, unsigned char *p_data, unsigned int n_length);
+
+int tasdevice_dev_bulk_read(struct tasdevice_priv *tasdevice,
+ enum channel chn, unsigned int reg, unsigned char *p_data,
+ unsigned int n_length);
+
+int tasdevice_dev_update_bits(
+ struct tasdevice_priv *tasdevice, enum channel chn,
+ unsigned int reg, unsigned int mask, unsigned int value);
+
+#endif /*__TAS2781_H__ */
--
2.34.1
4
3
[PATCH 000/173] ALSA/ASoC: Convert to platform remove callback returning void
by Uwe Kleine-König 21 Mar '23
by Uwe Kleine-König 21 Mar '23
21 Mar '23
Hello,
this series adapts the platform drivers below sound/ to use the .remove_new()
callback. Compared to the traditional .remove() callback .remove_new() returns
no value. This is a good thing because the driver core doesn't (and cannot)
cope for errors during remove. The only effect of a non-zero return value in
.remove() is that the driver core emits a warning. The device is removed anyhow
and an early return from .remove() usually yields a resource leak.
By changing the remove callback to return void driver authors cannot
reasonably assume any more that there is some kind of cleanup later.
The first two patches simplify a driver each to return zero unconditionally,
and then all drivers are trivially converted to .remove_new().
There are nearly no interdependencies in this patch set---only 1 <- 11 and
2 <- 16. So even if some individual problems are found (I don't expect that),
the other patches can (and from my POV should) still be applied.
Best regards
Uwe
Uwe Kleine-König (173):
ALSA: sh: aica: Drop if blocks with always false condition
ASoC: amd: acp: rembrandt: Drop if blocks with always false condition
ALSA: pxa2xx: Convert to platform remove callback returning void
ALSA: atmel: ac97: Convert to platform remove callback returning void
ALSA: mts64: Convert to platform remove callback returning void
ALSA: portman2x4: Convert to platform remove callback returning void
ALSA: mips/hal2: Convert to platform remove callback returning void
ALSA: mips/sgio2audio: Convert to platform remove callback returning
void
ALSA: hda/tegra: Convert to platform remove callback returning void
ALSA: ppc/powermac: Convert to platform remove callback returning void
ALSA: sh: aica: Convert to platform remove callback returning void
ALSA: sh_dac_audio: Convert to platform remove callback returning void
ASoC: adi: axi-i2s: Convert to platform remove callback returning void
ASoC: adi: axi-spdif: Convert to platform remove callback returning
void
ASoC: amd: acp-pcm-dma: Convert to platform remove callback returning
void
ASoC: amd: acp: rembrandt: Convert to platform remove callback
returning void
ASoC: amd: acp: renoir: Convert to platform remove callback returning
void
ASoC: amd: ps: Convert to platform remove callback returning void
ASoC: amd: raven: acp3x-pcm-dma: Convert to platform remove callback
returning void
ASoC: amd: raven: acp3x-pdm-dma: Convert to platform remove callback
returning void
ASoC: amd: vangogh: acp5x-pcm-dma: Convert to platform remove callback
returning void
ASoC: amd: yc: acp6x-pdm-dma: Convert to platform remove callback
returning void
ASoC: apple: mca: Convert to platform remove callback returning void
ASoC: atmel: atmel-i2s: Convert to platform remove callback returning
void
ASoC: atmel: atmel_wm8904: Convert to platform remove callback
returning void
ASoC: atmel: mchp-i2s-mcc: Convert to platform remove callback
returning void
ASoC: atmel: mchp-pdmc: Convert to platform remove callback returning
void
ASoC: atmel: mchp-spdifrx: Convert to platform remove callback
returning void
ASoC: atmel: mchp-spdiftx: Convert to platform remove callback
returning void
ASoC: atmel: mikroe-proto: Convert to platform remove callback
returning void
ASoC: atmel: sam9g20_wm8731: Convert to platform remove callback
returning void
ASoC: atmel: sam9x5_wm8731: Convert to platform remove callback
returning void
ASoC: atmel: tse850-pcm5142: Convert to platform remove callback
returning void
ASoC: au1x: ac97c: Convert to platform remove callback returning void
ASoC: au1x: i2sc: Convert to platform remove callback returning void
ASoC: au1x: psc-ac97: Convert to platform remove callback returning
void
ASoC: au1x: psc-i2s: Convert to platform remove callback returning
void
ASoC: bcm: bcm63xx-i2s-whistler: Convert to platform remove callback
returning void
ASoC: bcm: cygnus-ssp: Convert to platform remove callback returning
void
ASoC: cirrus: edb93xx: Convert to platform remove callback returning
void
ASoC: cirrus: ep93xx-i2s: Convert to platform remove callback
returning void
ASoC: codecs: cs47l15: Convert to platform remove callback returning
void
ASoC: codecs: cs47l24: Convert to platform remove callback returning
void
ASoC: codecs: cs47l35: Convert to platform remove callback returning
void
ASoC: codecs: cs47l85: Convert to platform remove callback returning
void
ASoC: codecs: cs47l90: Convert to platform remove callback returning
void
ASoC: codecs: cs47l92: Convert to platform remove callback returning
void
ASoC: codecs: inno_rk3036: Convert to platform remove callback
returning void
ASoC: codecs: lpass-rx-macro: Convert to platform remove callback
returning void
ASoC: codecs: lpass-tx-macro: Convert to platform remove callback
returning void
ASoC: codecs: lpass-va-macro: Convert to platform remove callback
returning void
ASoC: codecs: lpass-wsa-macro: Convert to platform remove callback
returning void
ASoC: codecs: msm8916-wcd-analog: Convert to platform remove callback
returning void
ASoC: codecs: msm8916-wcd-digital: Convert to platform remove callback
returning void
ASoC: codecs: rk817_codec: Convert to platform remove callback
returning void
ASoC: codecs: wcd938x: Convert to platform remove callback returning
void
ASoC: codecs: wm5102: Convert to platform remove callback returning
void
ASoC: codecs: wm5110: Convert to platform remove callback returning
void
ASoC: codecs: wm8994: Convert to platform remove callback returning
void
ASoC: codecs: wm8997: Convert to platform remove callback returning
void
ASoC: codecs: wm8998: Convert to platform remove callback returning
void
ASoC: dwc: dwc-i2s: Convert to platform remove callback returning void
ASoC: fsl: eukrea-tlv320: Convert to platform remove callback
returning void
ASoC: fsl: fsl_asrc: Convert to platform remove callback returning
void
ASoC: fsl: fsl_aud2htx: Convert to platform remove callback returning
void
ASoC: fsl: fsl_audmix: Convert to platform remove callback returning
void
ASoC: fsl: fsl_dma: Convert to platform remove callback returning void
ASoC: fsl: fsl_easrc: Convert to platform remove callback returning
void
ASoC: fsl: fsl_esai: Convert to platform remove callback returning
void
ASoC: fsl: fsl_mqs: Convert to platform remove callback returning void
ASoC: fsl: fsl_rpmsg: Convert to platform remove callback returning
void
ASoC: fsl: fsl_sai: Convert to platform remove callback returning void
ASoC: fsl: fsl_spdif: Convert to platform remove callback returning
void
ASoC: fsl: fsl_ssi: Convert to platform remove callback returning void
ASoC: fsl: fsl_xcvr: Convert to platform remove callback returning
void
ASoC: fsl: imx-audmux: Convert to platform remove callback returning
void
ASoC: fsl: imx-pcm-rpmsg: Convert to platform remove callback
returning void
ASoC: fsl: imx-sgtl5000: Convert to platform remove callback returning
void
ASoC: fsl: mpc5200_psc_ac97: Convert to platform remove callback
returning void
ASoC: fsl: mpc5200_psc_i2s: Convert to platform remove callback
returning void
ASoC: fsl: mpc8610_hpcd: Convert to platform remove callback returning
void
ASoC: fsl: p1022_ds: Convert to platform remove callback returning
void
ASoC: fsl: p1022_rdk: Convert to platform remove callback returning
void
ASoC: fsl: pcm030-audio-fabric: Convert to platform remove callback
returning void
ASoC: generic: test-component: Convert to platform remove callback
returning void
ASoC: img: img-i2s-in: Convert to platform remove callback returning
void
ASoC: img: img-i2s-out: Convert to platform remove callback returning
void
ASoC: img: img-parallel-out: Convert to platform remove callback
returning void
ASoC: img: img-spdif-in: Convert to platform remove callback returning
void
ASoC: img: img-spdif-out: Convert to platform remove callback
returning void
ASoC: img: pistachio-internal-dac: Convert to platform remove callback
returning void
ASoC: Intel: sst-mfld-platform-pcm: Convert to platform remove
callback returning void
ASoC: Intel: sst: Convert to platform remove callback returning void
ASoC: Intel: bytcht_es8316: Convert to platform remove callback
returning void
ASoC: Intel: bytcr_rt5640: Convert to platform remove callback
returning void
ASoC: Intel: boards: bytcr_rt5651: Convert to platform remove callback
returning void
ASoC: Intel: bytcr_wm5102: Convert to platform remove callback
returning void
ASoC: Intel: cht_bsw_max98090_ti: Convert to platform remove callback
returning void
ASoC: Intel: sof_es8336: Convert to platform remove callback returning
void
ASoC: Intel: sof_pcm512x: Convert to platform remove callback
returning void
ASoC: Intel: sof_sdw: Convert to platform remove callback returning
void
ASoC: Intel: sof_wm8804: Convert to platform remove callback returning
void
ASoC: Intel: catpt: Convert to platform remove callback returning void
ASoC: Intel: skl-ssp-clk: Convert to platform remove callback
returning void
ASoC: kirkwood: kirkwood-i2s: Convert to platform remove callback
returning void
ASoC: mediatek: mtk-btcvsd: Convert to platform remove callback
returning void
ASoC: mediatek: mt2701-afe-pcm: Convert to platform remove callback
returning void
ASoC: mediatek: mt6797-afe-pcm: Convert to platform remove callback
returning void
ASoC: mediatek: mt8173-afe-pcm: Convert to platform remove callback
returning void
ASoC: mediatek: mt8183-afe-pcm: Convert to platform remove callback
returning void
ASoC: mediatek: mt8188-afe-pcm: Convert to platform remove callback
returning void
ASoC: mediatek: mt8192-afe-pcm: Convert to platform remove callback
returning void
ASoC: mediatek: mt8195-afe-pcm: Convert to platform remove callback
returning void
ASoC: meson: aiu: Convert to platform remove callback returning void
ASoC: mxs: mxs-sgtl5000: Convert to platform remove callback returning
void
ASoC: pxa: mmp-sspa: Convert to platform remove callback returning
void
ASoC: pxa: pxa2xx-ac97: Convert to platform remove callback returning
void
ASoC: qcom: qdsp6: Convert to platform remove callback returning void
ASoC: rockchip: rockchip_i2s: Convert to platform remove callback
returning void
ASoC: rockchip: rockchip_i2s_tdm: Convert to platform remove callback
returning void
ASoC: rockchip: rockchip_pdm: Convert to platform remove callback
returning void
ASoC: rockchip: rockchip_rt5645: Convert to platform remove callback
returning void
ASoC: rockchip: rockchip_spdif: Convert to platform remove callback
returning void
ASoC: samsung: arndale: Convert to platform remove callback returning
void
ASoC: samsung: i2s: Convert to platform remove callback returning void
ASoC: samsung: odroid: Convert to platform remove callback returning
void
ASoC: samsung: pcm: Convert to platform remove callback returning void
ASoC: samsung: snow: Convert to platform remove callback returning
void
ASoC: samsung: spdif: Convert to platform remove callback returning
void
ASoC: sh: fsi: Convert to platform remove callback returning void
ASoC: sh: hac: Convert to platform remove callback returning void
ASoC: sh: rcar: Convert to platform remove callback returning void
ASoC: sh: rz-ssi: Convert to platform remove callback returning void
ASoC: sh: siu_dai: Convert to platform remove callback returning void
ASoC: sprd: sprd-mcdt: Convert to platform remove callback returning
void
ASoC: stm: stm32_adfsdm: Convert to platform remove callback returning
void
ASoC: stm: stm32_i2s: Convert to platform remove callback returning
void
ASoC: stm: stm32_sai_sub: Convert to platform remove callback
returning void
ASoC: stm: stm32_spdifrx: Convert to platform remove callback
returning void
ASoC: sunxi: sun4i-codec: Convert to platform remove callback
returning void
ASoC: sunxi: sun4i-i2s: Convert to platform remove callback returning
void
ASoC: sunxi: sun4i-spdif: Convert to platform remove callback
returning void
ASoC: sunxi: sun50i-dmic: Convert to platform remove callback
returning void
ASoC: sunxi: sun8i-codec: Convert to platform remove callback
returning void
ASoC: tegra: tegra186_asrc: Convert to platform remove callback
returning void
ASoC: tegra: tegra186_dspk: Convert to platform remove callback
returning void
ASoC: tegra: tegra20_ac97: Convert to platform remove callback
returning void
ASoC: tegra: tegra20_i2s: Convert to platform remove callback
returning void
ASoC: tegra: tegra210_admaif: Convert to platform remove callback
returning void
ASoC: tegra: tegra210_adx: Convert to platform remove callback
returning void
ASoC: tegra: tegra210_ahub: Convert to platform remove callback
returning void
ASoC: tegra: tegra210_amx: Convert to platform remove callback
returning void
ASoC: tegra: tegra210_dmic: Convert to platform remove callback
returning void
ASoC: tegra: tegra210_i2s: Convert to platform remove callback
returning void
ASoC: tegra: tegra210_mixer: Convert to platform remove callback
returning void
ASoC: tegra: tegra210_mvc: Convert to platform remove callback
returning void
ASoC: tegra: tegra210_ope: Convert to platform remove callback
returning void
ASoC: tegra: tegra210_sfc: Convert to platform remove callback
returning void
ASoC: tegra: tegra30_ahub: Convert to platform remove callback
returning void
ASoC: tegra: tegra30_i2s: Convert to platform remove callback
returning void
ASoC: ti: ams-delta: Convert to platform remove callback returning
void
ASoC: ti: davinci-i2s: Convert to platform remove callback returning
void
ASoC: ti: davinci-mcasp: Convert to platform remove callback returning
void
ASoC: ti: omap-hdmi: Convert to platform remove callback returning
void
ASoC: ti: omap-mcbsp: Convert to platform remove callback returning
void
ASoC: uniphier: evea: Convert to platform remove callback returning
void
ASoC: ux500: mop500: Convert to platform remove callback returning
void
ASoC: ux500: ux500_msp_dai: Convert to platform remove callback
returning void
ASoC: xilinx: xlnx_formatter_pcm: Convert to platform remove callback
returning void
ASoC: xilinx: xlnx_spdif: Convert to platform remove callback
returning void
ASoC: xtensa: xtfpga-i2s: Convert to platform remove callback
returning void
ALSA: sparc/cs4231: Convert to platform remove callback returning void
ALSA: sparc/dbri: Convert to platform remove callback returning void
sound/arm/pxa2xx-ac97.c | 6 ++----
sound/atmel/ac97c.c | 6 ++----
sound/drivers/mts64.c | 6 ++----
sound/drivers/portman2x4.c | 6 ++----
sound/mips/hal2.c | 5 ++---
sound/mips/sgio2audio.c | 5 ++---
sound/pci/hda/hda_tegra.c | 6 ++----
sound/ppc/powermac.c | 5 ++---
sound/sh/aica.c | 7 ++-----
sound/sh/sh_dac_audio.c | 5 ++---
sound/soc/adi/axi-i2s.c | 6 ++----
sound/soc/adi/axi-spdif.c | 6 ++----
sound/soc/amd/acp-pcm-dma.c | 6 ++----
sound/soc/amd/acp/acp-rembrandt.c | 13 +++----------
sound/soc/amd/acp/acp-renoir.c | 5 ++---
sound/soc/amd/ps/ps-pdm-dma.c | 5 ++---
sound/soc/amd/raven/acp3x-pcm-dma.c | 5 ++---
sound/soc/amd/renoir/acp3x-pdm-dma.c | 5 ++---
sound/soc/amd/vangogh/acp5x-pcm-dma.c | 5 ++---
sound/soc/amd/yc/acp6x-pdm-dma.c | 5 ++---
sound/soc/apple/mca.c | 5 ++---
sound/soc/atmel/atmel-i2s.c | 6 ++----
sound/soc/atmel/atmel_wm8904.c | 6 ++----
sound/soc/atmel/mchp-i2s-mcc.c | 6 ++----
sound/soc/atmel/mchp-pdmc.c | 6 ++----
sound/soc/atmel/mchp-spdifrx.c | 6 ++----
sound/soc/atmel/mchp-spdiftx.c | 6 ++----
sound/soc/atmel/mikroe-proto.c | 6 ++----
sound/soc/atmel/sam9g20_wm8731.c | 6 ++----
sound/soc/atmel/sam9x5_wm8731.c | 6 ++----
sound/soc/atmel/tse850-pcm5142.c | 6 ++----
sound/soc/au1x/ac97c.c | 6 ++----
sound/soc/au1x/i2sc.c | 6 ++----
sound/soc/au1x/psc-ac97.c | 6 ++----
sound/soc/au1x/psc-i2s.c | 6 ++----
sound/soc/bcm/bcm63xx-i2s-whistler.c | 5 ++---
sound/soc/bcm/cygnus-ssp.c | 6 ++----
sound/soc/cirrus/edb93xx.c | 6 ++----
sound/soc/cirrus/ep93xx-i2s.c | 5 ++---
sound/soc/codecs/cs47l15.c | 6 ++----
sound/soc/codecs/cs47l24.c | 6 ++----
sound/soc/codecs/cs47l35.c | 6 ++----
sound/soc/codecs/cs47l85.c | 6 ++----
sound/soc/codecs/cs47l90.c | 6 ++----
sound/soc/codecs/cs47l92.c | 6 ++----
sound/soc/codecs/inno_rk3036.c | 6 ++----
sound/soc/codecs/lpass-rx-macro.c | 6 ++----
sound/soc/codecs/lpass-tx-macro.c | 6 ++----
sound/soc/codecs/lpass-va-macro.c | 6 ++----
sound/soc/codecs/lpass-wsa-macro.c | 6 ++----
sound/soc/codecs/msm8916-wcd-analog.c | 6 ++----
sound/soc/codecs/msm8916-wcd-digital.c | 6 ++----
sound/soc/codecs/rk817_codec.c | 6 ++----
sound/soc/codecs/wcd938x.c | 6 ++----
sound/soc/codecs/wm5102.c | 6 ++----
sound/soc/codecs/wm5110.c | 6 ++----
sound/soc/codecs/wm8994.c | 6 ++----
sound/soc/codecs/wm8997.c | 6 ++----
sound/soc/codecs/wm8998.c | 6 ++----
sound/soc/dwc/dwc-i2s.c | 5 ++---
sound/soc/fsl/eukrea-tlv320.c | 6 ++----
sound/soc/fsl/fsl_asrc.c | 6 ++----
sound/soc/fsl/fsl_aud2htx.c | 6 ++----
sound/soc/fsl/fsl_audmix.c | 6 ++----
sound/soc/fsl/fsl_dma.c | 6 ++----
sound/soc/fsl/fsl_easrc.c | 6 ++----
sound/soc/fsl/fsl_esai.c | 6 ++----
sound/soc/fsl/fsl_mqs.c | 5 ++---
sound/soc/fsl/fsl_rpmsg.c | 6 ++----
sound/soc/fsl/fsl_sai.c | 6 ++----
sound/soc/fsl/fsl_spdif.c | 6 ++----
sound/soc/fsl/fsl_ssi.c | 6 ++----
sound/soc/fsl/fsl_xcvr.c | 5 ++---
sound/soc/fsl/imx-audmux.c | 6 ++----
sound/soc/fsl/imx-pcm-rpmsg.c | 6 ++----
sound/soc/fsl/imx-sgtl5000.c | 6 ++----
sound/soc/fsl/mpc5200_psc_ac97.c | 5 ++---
sound/soc/fsl/mpc5200_psc_i2s.c | 5 ++---
sound/soc/fsl/mpc8610_hpcd.c | 6 ++----
sound/soc/fsl/p1022_ds.c | 6 ++----
sound/soc/fsl/p1022_rdk.c | 6 ++----
sound/soc/fsl/pcm030-audio-fabric.c | 6 ++----
sound/soc/generic/test-component.c | 6 ++----
sound/soc/img/img-i2s-in.c | 6 ++----
sound/soc/img/img-i2s-out.c | 6 ++----
sound/soc/img/img-parallel-out.c | 6 ++----
sound/soc/img/img-spdif-in.c | 6 ++----
sound/soc/img/img-spdif-out.c | 6 ++----
sound/soc/img/pistachio-internal-dac.c | 6 ++----
sound/soc/intel/atom/sst-mfld-platform-pcm.c | 5 ++---
sound/soc/intel/atom/sst/sst_acpi.c | 5 ++---
sound/soc/intel/boards/bytcht_es8316.c | 5 ++---
sound/soc/intel/boards/bytcr_rt5640.c | 5 ++---
sound/soc/intel/boards/bytcr_rt5651.c | 5 ++---
sound/soc/intel/boards/bytcr_wm5102.c | 5 ++---
sound/soc/intel/boards/cht_bsw_max98090_ti.c | 6 ++----
sound/soc/intel/boards/sof_es8336.c | 6 ++----
sound/soc/intel/boards/sof_pcm512x.c | 6 ++----
sound/soc/intel/boards/sof_sdw.c | 6 ++----
sound/soc/intel/boards/sof_wm8804.c | 5 ++---
sound/soc/intel/catpt/device.c | 6 ++----
sound/soc/intel/skylake/skl-ssp-clk.c | 6 ++----
sound/soc/kirkwood/kirkwood-i2s.c | 6 ++----
sound/soc/mediatek/common/mtk-btcvsd.c | 5 ++---
sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 6 ++----
sound/soc/mediatek/mt6797/mt6797-afe-pcm.c | 6 ++----
sound/soc/mediatek/mt8173/mt8173-afe-pcm.c | 5 ++---
sound/soc/mediatek/mt8183/mt8183-afe-pcm.c | 6 ++----
sound/soc/mediatek/mt8188/mt8188-afe-pcm.c | 6 ++----
sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 5 ++---
sound/soc/mediatek/mt8195/mt8195-afe-pcm.c | 5 ++---
sound/soc/meson/aiu.c | 6 ++----
sound/soc/mxs/mxs-sgtl5000.c | 6 ++----
sound/soc/pxa/mmp-sspa.c | 7 +++----
sound/soc/pxa/pxa2xx-ac97.c | 5 ++---
sound/soc/qcom/qdsp6/q6routing.c | 6 ++----
sound/soc/rockchip/rockchip_i2s.c | 6 ++----
sound/soc/rockchip/rockchip_i2s_tdm.c | 6 ++----
sound/soc/rockchip/rockchip_pdm.c | 6 ++----
sound/soc/rockchip/rockchip_rt5645.c | 6 ++----
sound/soc/rockchip/rockchip_spdif.c | 6 ++----
sound/soc/samsung/arndale.c | 5 ++---
sound/soc/samsung/i2s.c | 8 +++-----
sound/soc/samsung/odroid.c | 6 ++----
sound/soc/samsung/pcm.c | 6 ++----
sound/soc/samsung/snow.c | 6 ++----
sound/soc/samsung/spdif.c | 6 ++----
sound/soc/sh/fsi.c | 6 ++----
sound/soc/sh/hac.c | 5 ++---
sound/soc/sh/rcar/core.c | 6 ++----
sound/soc/sh/rz-ssi.c | 6 ++----
sound/soc/sh/siu_dai.c | 5 ++---
sound/soc/sprd/sprd-mcdt.c | 6 ++----
sound/soc/stm/stm32_adfsdm.c | 6 ++----
sound/soc/stm/stm32_i2s.c | 6 ++----
sound/soc/stm/stm32_sai_sub.c | 6 ++----
sound/soc/stm/stm32_spdifrx.c | 6 ++----
sound/soc/sunxi/sun4i-codec.c | 6 ++----
sound/soc/sunxi/sun4i-i2s.c | 6 ++----
sound/soc/sunxi/sun4i-spdif.c | 6 ++----
sound/soc/sunxi/sun50i-dmic.c | 6 ++----
sound/soc/sunxi/sun8i-codec.c | 6 ++----
sound/soc/tegra/tegra186_asrc.c | 6 ++----
sound/soc/tegra/tegra186_dspk.c | 6 ++----
sound/soc/tegra/tegra20_ac97.c | 6 ++----
sound/soc/tegra/tegra20_i2s.c | 6 ++----
sound/soc/tegra/tegra210_admaif.c | 6 ++----
sound/soc/tegra/tegra210_adx.c | 6 ++----
sound/soc/tegra/tegra210_ahub.c | 6 ++----
sound/soc/tegra/tegra210_amx.c | 6 ++----
sound/soc/tegra/tegra210_dmic.c | 6 ++----
sound/soc/tegra/tegra210_i2s.c | 6 ++----
sound/soc/tegra/tegra210_mixer.c | 6 ++----
sound/soc/tegra/tegra210_mvc.c | 6 ++----
sound/soc/tegra/tegra210_ope.c | 6 ++----
sound/soc/tegra/tegra210_sfc.c | 6 ++----
sound/soc/tegra/tegra30_ahub.c | 6 ++----
sound/soc/tegra/tegra30_i2s.c | 6 ++----
sound/soc/ti/ams-delta.c | 5 ++---
sound/soc/ti/davinci-i2s.c | 6 ++----
sound/soc/ti/davinci-mcasp.c | 6 ++----
sound/soc/ti/omap-hdmi.c | 5 ++---
sound/soc/ti/omap-mcbsp.c | 6 ++----
sound/soc/uniphier/evea.c | 6 ++----
sound/soc/ux500/mop500.c | 6 ++----
sound/soc/ux500/ux500_msp_dai.c | 6 ++----
sound/soc/xilinx/xlnx_formatter_pcm.c | 5 ++---
sound/soc/xilinx/xlnx_spdif.c | 5 ++---
sound/soc/xtensa/xtfpga-i2s.c | 5 ++---
sound/sparc/cs4231.c | 6 ++----
sound/sparc/dbri.c | 6 ++----
171 files changed, 345 insertions(+), 654 deletions(-)
base-commit: fe15c26ee26efa11741a7b632e9f23b01aca4cc6
--
2.39.2
17
226
21 Mar '23
From: "Vlad.Karpovich" <vkarpovi(a)opensource.cirrus.com>
Adds device tree configuration for cs35l45 GPIOs
Signed-off-by: Vlad Karpovich <vkarpovi(a)opensource.cirrus.com>
---
include/dt-bindings/sound/cs35l45.h | 57 +++++++++++++++++++++++++++++
sound/soc/codecs/cs35l45-tables.c | 14 +++++++
sound/soc/codecs/cs35l45.c | 56 ++++++++++++++++++++++++++++
sound/soc/codecs/cs35l45.h | 27 +++++++++++++-
4 files changed, 152 insertions(+), 2 deletions(-)
diff --git a/include/dt-bindings/sound/cs35l45.h b/include/dt-bindings/sound/cs35l45.h
index 076da4b2c28d..25386af18445 100644
--- a/include/dt-bindings/sound/cs35l45.h
+++ b/include/dt-bindings/sound/cs35l45.h
@@ -17,4 +17,61 @@
#define CS35L45_ASP_TX_HIZ_UNUSED 0x1
#define CS35L45_ASP_TX_HIZ_DISABLED 0x2
+/*
+ * Optional GPIOX Sub-nodes:
+ * The cs35l45 node can have up to three "cirrus,gpio-ctrlX" ('X' = [1,2,3])
+ * sub-nodes for configuring the GPIO pins.
+ *
+ * - gpio-dir : GPIO pin direction. Valid only when 'gpio-ctrl'
+ * is 1.
+ * 0 = Output
+ * 1 = Input (Default)
+ *
+ * - gpio-lvl : GPIO level. Valid only when 'gpio-ctrl' is 1 and 'gpio-dir' is 0.
+ *
+ * 0 = Low (Default)
+ * 1 = High
+ *
+ * - gpio-op-cfg : GPIO output configuration. Valid only when 'gpio-ctrl' is 1
+ * and 'gpio-dir' is 0.
+ *
+ * 0 = CMOS (Default)
+ * 1 = Open Drain
+ *
+ * - gpio-pol : GPIO output polarity select. Valid only when 'gpio-ctrl' is 1
+ * and 'gpio-dir' is 0.
+ *
+ * 0 = Non-inverted, Active High (Default)
+ * 1 = Inverted, Active Low
+ *
+ * - gpio-invert : Defines the polarity of the GPIO pin if configured
+ * as input.
+ *
+ * 0 = Not inverted (Default)
+ * 1 = Inverted
+ *
+ * - gpio-ctrl : Defines the function of the GPIO pin.
+ *
+ * GPIO1:
+ * 0 = High impedance input (Default)
+ * 1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+ * 2 = Pin acts as MDSYNC, direction controlled by MDSYNC
+ * 3-7 = Reserved
+ *
+ * GPIO2:
+ * 0 = High impedance input (Default)
+ * 1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+ * 2 = Pin acts as open drain INT
+ * 3 = Reserved
+ * 4 = Pin acts as push-pull output INT. Active low.
+ * 5 = Pin acts as push-pull output INT. Active high.
+ * 6,7 = Reserved
+ *
+ * GPIO3:
+ * 0 = High impedance input (Default)
+ * 1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+ * 2-7 = Reserved
+ */
+#define CS35L45_NUM_GPIOS 0x3
+
#endif /* DT_CS35L45_H */
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
index 5a2c2e684ef9..bca7e830821f 100644
--- a/sound/soc/codecs/cs35l45-tables.c
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -43,6 +43,9 @@ EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, SND_SOC_CS35L45_TABLES);
static const struct reg_default cs35l45_defaults[] = {
{ CS35L45_BLOCK_ENABLES, 0x00003323 },
{ CS35L45_BLOCK_ENABLES2, 0x00000010 },
+ { CS35L45_SYNC_GPIO1, 0x00000007 },
+ { CS35L45_INTB_GPIO2_MCLK_REF, 0x00000005 },
+ { CS35L45_GPIO3, 0x00000005 },
{ CS35L45_REFCLK_INPUT, 0x00000510 },
{ CS35L45_GLOBAL_SAMPLE_RATE, 0x00000003 },
{ CS35L45_ASP_ENABLES1, 0x00000000 },
@@ -61,6 +64,9 @@ static const struct reg_default cs35l45_defaults[] = {
{ CS35L45_ASPTX4_INPUT, 0x00000028 },
{ CS35L45_ASPTX5_INPUT, 0x00000048 },
{ CS35L45_AMP_PCM_CONTROL, 0x00100000 },
+ { CS35L45_GPIO1_CTRL1, 0x81000001 },
+ { CS35L45_GPIO2_CTRL1, 0x81000001 },
+ { CS35L45_GPIO3_CTRL1, 0x81000001 },
};
static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
@@ -72,6 +78,9 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
case CS35L45_BLOCK_ENABLES:
case CS35L45_BLOCK_ENABLES2:
case CS35L45_ERROR_RELEASE:
+ case CS35L45_SYNC_GPIO1:
+ case CS35L45_INTB_GPIO2_MCLK_REF:
+ case CS35L45_GPIO3:
case CS35L45_REFCLK_INPUT:
case CS35L45_GLOBAL_SAMPLE_RATE:
case CS35L45_ASP_ENABLES1:
@@ -92,6 +101,10 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
case CS35L45_AMP_PCM_CONTROL:
case CS35L45_AMP_PCM_HPF_TST:
case CS35L45_IRQ1_EINT_4:
+ case CS35L45_GPIO_STATUS1:
+ case CS35L45_GPIO1_CTRL1:
+ case CS35L45_GPIO2_CTRL1:
+ case CS35L45_GPIO3_CTRL1:
return true;
default:
return false;
@@ -107,6 +120,7 @@ static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg)
case CS35L45_ERROR_RELEASE:
case CS35L45_AMP_PCM_HPF_TST: /* not cachable */
case CS35L45_IRQ1_EINT_4:
+ case CS35L45_GPIO_STATUS1:
return true;
default:
return false;
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
index d15b3b77c7eb..901f3647fbda 100644
--- a/sound/soc/codecs/cs35l45.c
+++ b/sound/soc/codecs/cs35l45.c
@@ -536,7 +536,63 @@ static int __maybe_unused cs35l45_runtime_resume(struct device *dev)
static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
{
+ struct device_node *node = cs35l45->dev->of_node;
+ unsigned int gpio_regs[] = {CS35L45_GPIO1_CTRL1, CS35L45_GPIO2_CTRL1,
+ CS35L45_GPIO3_CTRL1};
+ unsigned int pad_regs[] = {CS35L45_SYNC_GPIO1,
+ CS35L45_INTB_GPIO2_MCLK_REF, CS35L45_GPIO3};
+ struct device_node *child;
unsigned int val;
+ char of_name[32];
+ int ret, i;
+
+ if (!node)
+ return 0;
+
+ for (i = 0; i < CS35L45_NUM_GPIOS; i++) {
+ sprintf(of_name, "cirrus,gpio-ctrl%d", i + 1);
+ child = of_get_child_by_name(node, of_name);
+ if (!child)
+ continue;
+
+ ret = of_property_read_u32(child, "gpio-dir", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_DIR_MASK,
+ val << CS35L45_GPIO_DIR_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-lvl", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_LVL_MASK,
+ val << CS35L45_GPIO_LVL_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-op-cfg", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_OP_CFG_MASK,
+ val << CS35L45_GPIO_OP_CFG_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-pol", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_POL_MASK,
+ val << CS35L45_GPIO_POL_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-ctrl", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, pad_regs[i],
+ CS35L45_GPIO_CTRL_MASK,
+ val << CS35L45_GPIO_CTRL_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-invert", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, pad_regs[i],
+ CS35L45_GPIO_INVERT_MASK,
+ val << CS35L45_GPIO_INVERT_SHIFT);
+
+ of_node_put(child);
+ }
if (device_property_read_u32(cs35l45->dev,
"cirrus,asp-sdout-hiz-ctrl", &val) == 0) {
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
index 53fe9d2b7b15..f3a54fc57d53 100644
--- a/sound/soc/codecs/cs35l45.h
+++ b/sound/soc/codecs/cs35l45.h
@@ -14,6 +14,7 @@
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <dt-bindings/sound/cs35l45.h>
#define CS35L45_DEVID 0x00000000
#define CS35L45_REVID 0x00000004
@@ -24,6 +25,9 @@
#define CS35L45_BLOCK_ENABLES 0x00002018
#define CS35L45_BLOCK_ENABLES2 0x0000201C
#define CS35L45_ERROR_RELEASE 0x00002034
+#define CS35L45_SYNC_GPIO1 0x00002430
+#define CS35L45_INTB_GPIO2_MCLK_REF 0x00002434
+#define CS35L45_GPIO3 0x00002438
#define CS35L45_REFCLK_INPUT 0x00002C04
#define CS35L45_GLOBAL_SAMPLE_RATE 0x00002C0C
#define CS35L45_BOOST_CCM_CFG 0x00003808
@@ -48,8 +52,11 @@
#define CS35L45_AMP_PCM_CONTROL 0x00007000
#define CS35L45_AMP_PCM_HPF_TST 0x00007004
#define CS35L45_IRQ1_EINT_4 0x0000E01C
-#define CS35L45_LASTREG 0x0000E01C
-
+#define CS35L45_GPIO_STATUS1 0x0000F000
+#define CS35L45_GPIO1_CTRL1 0x0000F008
+#define CS35L45_GPIO2_CTRL1 0x0000F00C
+#define CS35L45_GPIO3_CTRL1 0x0000F010
+#define CS35L45_LASTREG 0x0000F010
/* SFT_RESET */
#define CS35L45_SOFT_RESET_TRIGGER 0x5A000000
@@ -165,6 +172,22 @@
#define CS35L45_OTP_BOOT_DONE_STS_MASK BIT(1)
#define CS35L45_OTP_BUSY_MASK BIT(0)
+/* GPIOX_CTRL1 */
+#define CS35L45_GPIO_DIR_SHIFT 31
+#define CS35L45_GPIO_DIR_MASK BIT(31)
+#define CS35L45_GPIO_LVL_SHIFT 15
+#define CS35L45_GPIO_LVL_MASK BIT(15)
+#define CS35L45_GPIO_OP_CFG_SHIFT 14
+#define CS35L45_GPIO_OP_CFG_MASK BIT(14)
+#define CS35L45_GPIO_POL_SHIFT 12
+#define CS35L45_GPIO_POL_MASK BIT(12)
+
+/* SYNC_GPIO1, INTB_GPIO2_MCLK_REF, GPIO3 */
+#define CS35L45_GPIO_CTRL_SHIFT 20
+#define CS35L45_GPIO_CTRL_MASK GENMASK(22, 20)
+#define CS35L45_GPIO_INVERT_SHIFT 19
+#define CS35L45_GPIO_INVERT_MASK BIT(19)
+
/* Mixer sources */
#define CS35L45_PCM_SRC_MASK 0x7F
#define CS35L45_PCM_SRC_ZERO 0x00
--
2.25.1
2
6
21 Mar '23
The CS35L56 is a high-performance boosted mono audio amplifier.
Supported control interfaces are I2C, SPI or SoundWire.
Supported audio interfaces are I2S/TDM or SoundWire.
The CS35L56 has a self-booting firmware in ROM, with the ability
to patch the firmware and/or apply tunings.
Patches #1 to #7 add support to cs_dsp and wm_adsp for self-booting
firmware and the ability to apply a .bin file without having to
apply a .wmfw.
Changes since v2:
- Fix Kconfig to prevent the driver being built-in if the SoundWire
core is a module.
Changes since v1:
- Check for error when getting pm_runtime in cs35l56_irq().
- Use SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK instead of the old define.
- Re-word two comments in cs35l56-sdw.c.
- Add an comment explaining the wait for init_completion in
cs35l56_dsp_work().
- Omit patches to Intel machine driver. These will be done as a
separate thread.
Richard Fitzgerald (1):
ASoC: cs35l56: Add driver for Cirrus Logic CS35L56
Simon Trimmer (7):
firmware: cs_dsp: Introduce no_core_startstop for self-booting DSPs
ASoC: wm_adsp: Use no_core_startstop to prevent creating preload
control
firmware: cs_dsp: Support DSPs that don't require firmware download
ASoC: wm_adsp: Support DSPs that don't require firmware download
ASoC: wm_adsp: Expose the DSP boot work actions as wm_adsp_power_up()
ASoC: wm_adsp: Add support for loading bin files without wmfw
ASoC: wm_adsp: Simplify the logging of requested firmware files
MAINTAINERS | 1 +
drivers/firmware/cirrus/cs_dsp.c | 17 +-
include/linux/firmware/cirrus/cs_dsp.h | 1 +
include/sound/cs35l56.h | 266 +++++
sound/soc/codecs/Kconfig | 40 +
sound/soc/codecs/Makefile | 10 +
sound/soc/codecs/cs35l56-i2c.c | 83 ++
sound/soc/codecs/cs35l56-sdw.c | 528 +++++++++
sound/soc/codecs/cs35l56-shared.c | 390 +++++++
sound/soc/codecs/cs35l56-spi.c | 81 ++
sound/soc/codecs/cs35l56.c | 1461 ++++++++++++++++++++++++
sound/soc/codecs/cs35l56.h | 77 ++
sound/soc/codecs/wm_adsp.c | 63 +-
sound/soc/codecs/wm_adsp.h | 3 +
14 files changed, 3005 insertions(+), 16 deletions(-)
create mode 100644 include/sound/cs35l56.h
create mode 100644 sound/soc/codecs/cs35l56-i2c.c
create mode 100644 sound/soc/codecs/cs35l56-sdw.c
create mode 100644 sound/soc/codecs/cs35l56-shared.c
create mode 100644 sound/soc/codecs/cs35l56-spi.c
create mode 100644 sound/soc/codecs/cs35l56.c
create mode 100644 sound/soc/codecs/cs35l56.h
--
2.30.2
2
9
[bug report] ASoC: SOF: ipc4-control: Add support for bytes control get and put
by Dan Carpenter 21 Mar '23
by Dan Carpenter 21 Mar '23
21 Mar '23
Hello Peter Ujfalusi,
The patch a062c8899fed: "ASoC: SOF: ipc4-control: Add support for
bytes control get and put" from Mar 13, 2023, leads to the following
Smatch static checker warning:
sound/soc/sof/ipc4-control.c:436 sof_ipc4_widget_kcontrol_setup()
warn: iterator used outside loop: 'scontrol'
sound/soc/sof/ipc4-control.c
411 static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
412 {
413 struct snd_sof_control *scontrol;
414 int ret = 0;
415
416 list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
417 if (scontrol->comp_id == swidget->comp_id) {
418 switch (scontrol->info_type) {
419 case SND_SOC_TPLG_CTL_VOLSW:
420 case SND_SOC_TPLG_CTL_VOLSW_SX:
421 case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
422 ret = sof_ipc4_set_volume_data(sdev, swidget,
423 scontrol, false);
424 break;
425 case SND_SOC_TPLG_CTL_BYTES:
426 ret = sof_ipc4_set_get_bytes_data(sdev, scontrol,
427 true, false);
428 break;
This breaks out of the switch statement and not the loop. So that means
that it will continue through the loop and ret is reset.
429 default:
430 break;
431 }
432 }
433 }
434
435 if (ret < 0)
--> 436 dev_err(sdev->dev, "kcontrol %d set up failed for widget %s\n",
437 scontrol->comp_id, swidget->widget->name);
^^^^^^^^^^^^^^^^^
"scontrol" cannot be a valid pointer at this stage. It is always an
offset off the &sdev->kcontrol_list because of the above issue.
438
439 return ret;
440 }
regards,
dan carpenter
2
1
21 Mar '23
There are 8 cs35l41 speaker amplifier connected to TDM
in my Xiaomi Mi Pad 5 Pro tablet. In this case, it's necessary
to set 12288000 (48000 * 32 * 8) clk freq for it.
rate=48000, slot_width=32, slots=8.
Signed-off-by: Jianhua Lu <lujianhua000(a)gmail.com>
---
sound/soc/codecs/cs35l41.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
index 9ec01a5f09ca..6ac501f008ec 100644
--- a/sound/soc/codecs/cs35l41.c
+++ b/sound/soc/codecs/cs35l41.c
@@ -150,6 +150,7 @@ static const struct cs35l41_fs_mon_config cs35l41_fs_mon[] = {
{ 5644800, 16, 24 },
{ 6000000, 16, 24 },
{ 6144000, 16, 24 },
+ { 12288000, 0, 0 },
};
static int cs35l41_get_fs_mon_config_index(int freq)
--
2.39.2
3
2