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
December 2022
- 130 participants
- 294 discussions
[PATCH V7 5/5] ASoC: dt-bindings: Add schema for "awinic,aw883xx"
by wangweidong.aï¼ awinic.com 22 Dec '22
by wangweidong.aï¼ awinic.com 22 Dec '22
22 Dec '22
From: Weidong Wang <wangweidong.a(a)awinic.com>
Add a DT schema for describing Awinic AW883xx audio amplifiers. They are
controlled using I2C.
Signed-off-by: Weidong Wang <wangweidong.a(a)awinic.com>
---
.../bindings/sound/awinic,aw883xx.yaml | 49 +++++++++++++++++++
1 file changed, 49 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/awinic,aw883xx.yaml
diff --git a/Documentation/devicetree/bindings/sound/awinic,aw883xx.yaml b/Documentation/devicetree/bindings/sound/awinic,aw883xx.yaml
new file mode 100644
index 000000000000..af4e0e27f8f7
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/awinic,aw883xx.yaml
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/awinic,aw883xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Awinic AW883xx Smart Audio Amplifier
+
+maintainers:
+ - Stephan Gerhold <stephan(a)gerhold.net>
+
+description:
+ The Awinic AW883XX is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier with an integrated 10.25V
+ smart boost convert.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: awinic,aw883xx_smartpa
+
+ reg:
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+
+ sound-name-prefix: true
+
+required:
+ - compatible
+ - reg
+ - reset-gpios
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ audio-codec@34 {
+ compatible = "awinic,aw883xx_smartpa";
+ reg = <0x34>;
+ reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
+ };
+ };
--
2.38.1
3
2
alsa-project/alsa-utils pull request #184 was opened from juimonen:
Add support in nhlt plugin for:
- ssp blob ver 1.5
- ssp auxiliary data
Request URL : https://github.com/alsa-project/alsa-utils/pull/184
Patch URL : https://github.com/alsa-project/alsa-utils/pull/184.patch
Repository URL: https://github.com/alsa-project/alsa-utils
1
0
The following changes since commit 9472382db38452df15f9f2f74b1dff34848e56b2:
Add SOF panic dump support for AMD platform. (2022-12-05 17:28:07 +0000)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git tags/asoc-v6.2-3
for you to fetch changes up to a12a383e59ce486abd719b6bda33c353a3b385e7:
ASoC: lochnagar: Fix unused lochnagar_of_match warning (2022-12-15 13:53:00 +0000)
----------------------------------------------------------------
ASoC: Updates for v6.2
Some more small fixes and board quirks that came in since my last
update, the main one being the fixes from Kai for issues around the
attempts to get kexec working well on SOF based systems.
----------------------------------------------------------------
Bard Liao (1):
ASoC: Intel: sof_realtek_common: set ret = 0 as initial value
Brent Lu (1):
ASoC: Intel: sof_rt5682: add jsl_rt5682 board config
Cezary Rojewski (22):
ALSA: hda: Allow for compress stream to hdac_ext_stream assignment
ALSA: hda: Prepare for compress stream support
ALSA: hda: Interrupt servicing and BDL setup for compress streams
ASoC: Intel: avs: Introduce avs_log_buffer_status_locked()
ASoC: Intel: avs: Drop fifo_lock
ASoC: Intel: avs: Introduce debug-context aware helpers
ASoC: Intel: avs: Make enable_logs() dependent on DEBUG_FS
ASoC: Intel: avs: Drop usage of debug members in non-debug code
ASoC: Intel: avs: Add data probing requests
ASoC: Intel: avs: Probe compress operations
ASoC: Intel: avs: Data probing soc-component
ASoC: Intel: avs: Add probe machine board
ASoC: Intel: avs: Probing and firmware tracing over debugfs
ASoC: Intel: avs: Gather remaining logs on strace_release()
ASoC: Intel: avs: Allow for dumping FW_REGS area
ASoC: Intel: avs: Allow for dumping debug window snapshot
ASoC: Intel: Skylake: Update pipe_config_idx before filling BE params
ASoC: Intel: Skylake: Remove skl_tplg_is_multi_fmt()
ASoC: Intel: Skylake: Drop pipe_config_idx
ASoC: Intel: Skylake: Introduce single place for pipe-config selection
ASoC: Intel: Skylake: Fix driver hang during shutdown
ASoC: Intel: Skylake: Use SG allocation for SKL-based firmware load
Chancel Liu (1):
ASoC: soc-pcm.c: Clear DAIs parameters after stream_active is updated
Gongjun Song (2):
ASoC: Intel: sof_sdw: use common helpers for all Realtek amps
ASoC: Intel: soc-acpi: update codec addr on 0C11/0C4F product
Hans de Goede (2):
ASoC: Intel: bytcr_rt5640: Add quirk for the Advantech MICA-071 tablet
ASoC: rt5670: Remove unbalanced pm_runtime_put()
Jiapeng Chong (1):
ASoC: codecs: wcd-clsh: Remove the unused function
Kai Vehmanen (2):
ASoC: SOF: Intel: pci-tgl: unblock S5 entry if DMA stop has failed"
ASoC: SOF: Revert: "core: unregister clients and machine drivers in .shutdown"
Krzysztof Kozlowski (1):
ASoC: lochnagar: Fix unused lochnagar_of_match warning
Marek Szyprowski (1):
ASoC: wm8994: Fix potential deadlock
Marek Vasut (1):
ASoC: dt-bindings: fsl-sai: Reinstate i.MX93 SAI compatible string
Mark Brown (4):
ASoC: Intel: avs: Data probing and fw logging
ASoC: Intel: Skylake: Topology and shutdown fixes
ASoC: Intel: boards: updates for SOF boards
ASoC: SOF: remove unregister calls from shutdown
Moises Cardona (1):
ASoC: Intel: Add HP Stream 8 to bytcr_rt5640.c
Wang Jingjin (2):
ASoC: rockchip: pdm: Add missing clk_disable_unprepare() in rockchip_pdm_runtime_resume()
ASoC: rockchip: spdif: Add missing clk_disable_unprepare() in rk_spdif_runtime_resume()
Wang Yufen (3):
ASoC: mediatek: mt8173-rt5650-rt5514: fix refcount leak in mt8173_rt5650_rt5514_dev_probe()
ASoC: audio-graph-card: fix refcount leak of cpu_ep in __graph_for_each_link()
ASoC: mediatek: mt8183: fix refcount leak in mt8183_mt6358_ts3a227_max98357_dev_probe()
YC Hung (2):
ASoC: mediatek: mt8195: add sof be ops to check audio active
ASoC: SOF: mediatek: initialize panic_info to zero
Yang Yingliang (1):
ASoC: sof_es8336: fix possible use-after-free in sof_es8336_remove()
ye xingchen (1):
ASoC: imx-audmux: use sysfs_emit() to instead of scnprintf()
.../devicetree/bindings/sound/fsl,sai.yaml | 1 +
include/sound/hdaudio_ext.h | 2 +
sound/hda/ext/hdac_ext_stream.c | 41 ++
sound/hda/hdac_controller.c | 4 +-
sound/hda/hdac_stream.c | 51 ++-
sound/soc/codecs/lochnagar-sc.c | 2 +-
sound/soc/codecs/rt5670.c | 2 -
sound/soc/codecs/wcd-clsh-v2.c | 6 -
sound/soc/codecs/wm8994.c | 5 +
sound/soc/fsl/imx-audmux.c | 3 +-
sound/soc/generic/audio-graph-card.c | 4 +-
sound/soc/intel/Kconfig | 1 +
sound/soc/intel/avs/Makefile | 4 +
sound/soc/intel/avs/apl.c | 24 +-
sound/soc/intel/avs/avs.h | 80 +++-
sound/soc/intel/avs/board_selection.c | 33 ++
sound/soc/intel/avs/boards/Kconfig | 8 +
sound/soc/intel/avs/boards/Makefile | 2 +
sound/soc/intel/avs/boards/probe.c | 64 +++
sound/soc/intel/avs/core.c | 2 +
sound/soc/intel/avs/debugfs.c | 436 +++++++++++++++++++++
sound/soc/intel/avs/ipc.c | 2 +-
sound/soc/intel/avs/messages.c | 104 ++++-
sound/soc/intel/avs/messages.h | 53 +++
sound/soc/intel/avs/pcm.c | 6 +-
sound/soc/intel/avs/probes.c | 313 +++++++++++++++
sound/soc/intel/avs/registers.h | 3 +-
sound/soc/intel/avs/skl.c | 17 +-
sound/soc/intel/avs/utils.c | 22 --
sound/soc/intel/boards/Makefile | 3 +-
sound/soc/intel/boards/bytcr_rt5640.c | 25 ++
sound/soc/intel/boards/sof_es8336.c | 2 +-
sound/soc/intel/boards/sof_realtek_common.c | 3 +-
sound/soc/intel/boards/sof_rt5682.c | 6 +
sound/soc/intel/boards/sof_sdw.c | 10 +-
sound/soc/intel/boards/sof_sdw_common.h | 20 +-
sound/soc/intel/boards/sof_sdw_rt1316.c | 239 -----------
sound/soc/intel/boards/sof_sdw_rt1318.c | 120 ------
.../boards/{sof_sdw_rt1308.c => sof_sdw_rt_amp.c} | 139 +++++--
sound/soc/intel/common/soc-acpi-intel-jsl-match.c | 5 +
sound/soc/intel/common/soc-acpi-intel-rpl-match.c | 2 +-
sound/soc/intel/skylake/skl-sst-cldma.c | 27 +-
sound/soc/intel/skylake/skl-topology.c | 73 ++--
sound/soc/intel/skylake/skl-topology.h | 1 -
sound/soc/intel/skylake/skl.c | 5 +-
sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c | 7 +-
.../mt8183/mt8183-mt6358-ts3a227-max98357.c | 14 +-
sound/soc/mediatek/mt8195/mt8195-mt6359.c | 30 ++
sound/soc/rockchip/rockchip_pdm.c | 1 +
sound/soc/rockchip/rockchip_spdif.c | 1 +
sound/soc/soc-pcm.c | 20 +-
sound/soc/sof/core.c | 9 -
sound/soc/sof/intel/hda-dsp.c | 72 ++++
sound/soc/sof/intel/hda.h | 1 +
sound/soc/sof/intel/tgl.c | 2 +-
sound/soc/sof/mediatek/mtk-adsp-common.c | 2 +-
56 files changed, 1519 insertions(+), 615 deletions(-)
create mode 100644 sound/soc/intel/avs/boards/probe.c
create mode 100644 sound/soc/intel/avs/debugfs.c
create mode 100644 sound/soc/intel/avs/probes.c
delete mode 100644 sound/soc/intel/boards/sof_sdw_rt1316.c
delete mode 100644 sound/soc/intel/boards/sof_sdw_rt1318.c
rename sound/soc/intel/boards/{sof_sdw_rt1308.c => sof_sdw_rt_amp.c} (59%)
2
1
[PATCH] ALSA: usb-audio: Add new quirk FIXED_RATE for JBL Quantum810 Wireless
by Jaroslav Kysela 22 Dec '22
by Jaroslav Kysela 22 Dec '22
22 Dec '22
It seems that the firmware is broken and does not accept
the UAC_EP_CS_ATTR_SAMPLE_RATE URB. There is only one rate (48000Hz)
available in the descriptors for the output endpoint.
Create a new quirk QUIRK_FLAG_FIXED_RATE to skip the rate setup
when only one rate is available (fixed).
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=216798
Signed-off-by: Jaroslav Kysela <perex(a)perex.cz>
---
sound/usb/card.h | 1 +
sound/usb/endpoint.c | 16 ++++++++++------
sound/usb/endpoint.h | 3 ++-
sound/usb/implicit.c | 6 +++++-
sound/usb/implicit.h | 2 +-
sound/usb/pcm.c | 36 +++++++++++++++++++++++++++++++++---
sound/usb/pcm.h | 2 ++
sound/usb/quirks.c | 2 ++
sound/usb/usbaudio.h | 4 ++++
9 files changed, 60 insertions(+), 12 deletions(-)
diff --git a/sound/usb/card.h b/sound/usb/card.h
index 40061550105a..6ec95b2edf86 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -131,6 +131,7 @@ struct snd_usb_endpoint {
bool lowlatency_playback; /* low-latency playback mode */
bool need_setup; /* (re-)need for hw_params? */
bool need_prepare; /* (re-)need for prepare? */
+ bool fixed_rate; /* skip rate setup */
/* for hw constraints */
const struct audioformat *cur_audiofmt;
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 4aaf0784940b..419302e2057e 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -769,7 +769,8 @@ struct snd_usb_endpoint *
snd_usb_endpoint_open(struct snd_usb_audio *chip,
const struct audioformat *fp,
const struct snd_pcm_hw_params *params,
- bool is_sync_ep)
+ bool is_sync_ep,
+ bool fixed_rate)
{
struct snd_usb_endpoint *ep;
int ep_num = is_sync_ep ? fp->sync_ep : fp->endpoint;
@@ -825,6 +826,7 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
ep->implicit_fb_sync = fp->implicit_fb;
ep->need_setup = true;
ep->need_prepare = true;
+ ep->fixed_rate = fixed_rate;
usb_audio_dbg(chip, " channels=%d, rate=%d, format=%s, period_bytes=%d, periods=%d, implicit_fb=%d\n",
ep->cur_channels, ep->cur_rate,
@@ -1413,11 +1415,13 @@ static int init_sample_rate(struct snd_usb_audio *chip,
if (clock && !clock->need_setup)
return 0;
- err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, rate);
- if (err < 0) {
- if (clock)
- clock->rate = 0; /* reset rate */
- return err;
+ if (!ep->fixed_rate) {
+ err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, rate);
+ if (err < 0) {
+ if (clock)
+ clock->rate = 0; /* reset rate */
+ return err;
+ }
}
if (clock)
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index e67ea28faa54..924f4351588c 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -14,7 +14,8 @@ struct snd_usb_endpoint *
snd_usb_endpoint_open(struct snd_usb_audio *chip,
const struct audioformat *fp,
const struct snd_pcm_hw_params *params,
- bool is_sync_ep);
+ bool is_sync_ep,
+ bool fixed_rate);
void snd_usb_endpoint_close(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep);
int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c
index f3e8484b3d9c..41ac7185b42b 100644
--- a/sound/usb/implicit.c
+++ b/sound/usb/implicit.c
@@ -15,6 +15,7 @@
#include "usbaudio.h"
#include "card.h"
#include "helper.h"
+#include "pcm.h"
#include "implicit.h"
enum {
@@ -455,7 +456,8 @@ const struct audioformat *
snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
const struct audioformat *target,
const struct snd_pcm_hw_params *params,
- int stream)
+ int stream,
+ bool *fixed_rate)
{
struct snd_usb_substream *subs;
const struct audioformat *fp, *sync_fmt = NULL;
@@ -483,6 +485,8 @@ snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
}
}
+ if (fixed_rate)
+ *fixed_rate = snd_usb_pcm_has_fixed_rate(subs);
return sync_fmt;
}
diff --git a/sound/usb/implicit.h b/sound/usb/implicit.h
index ccb415a0ea86..7f1577b6c4d3 100644
--- a/sound/usb/implicit.h
+++ b/sound/usb/implicit.h
@@ -9,6 +9,6 @@ const struct audioformat *
snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
const struct audioformat *target,
const struct snd_pcm_hw_params *params,
- int stream);
+ int stream, bool *fixed_rate);
#endif /* __USBAUDIO_IMPLICIT_H */
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 9557bd4d1bbc..99a66d0ef5b2 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -157,6 +157,31 @@ find_substream_format(struct snd_usb_substream *subs,
true, subs);
}
+bool snd_usb_pcm_has_fixed_rate(struct snd_usb_substream *subs)
+{
+ const struct audioformat *fp;
+ struct snd_usb_audio *chip = subs->stream->chip;
+ int rate = -1;
+
+ if (!(chip->quirk_flags & QUIRK_FLAG_FIXED_RATE))
+ return false;
+ list_for_each_entry(fp, &subs->fmt_list, list) {
+ if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
+ return false;
+ if (fp->nr_rates < 1)
+ continue;
+ if (fp->nr_rates > 1)
+ return false;
+ if (rate < 0) {
+ rate = fp->rate_table[0];
+ continue;
+ }
+ if (rate != fp->rate_table[0])
+ return false;
+ }
+ return true;
+}
+
static int init_pitch_v1(struct snd_usb_audio *chip, int ep)
{
struct usb_device *dev = chip->dev;
@@ -450,12 +475,14 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
struct snd_usb_audio *chip = subs->stream->chip;
const struct audioformat *fmt;
const struct audioformat *sync_fmt;
+ bool fixed_rate, sync_fixed_rate;
int ret;
ret = snd_media_start_pipeline(subs);
if (ret)
return ret;
+ fixed_rate = snd_usb_pcm_has_fixed_rate(subs);
fmt = find_substream_format(subs, hw_params);
if (!fmt) {
usb_audio_dbg(chip,
@@ -469,7 +496,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
if (fmt->implicit_fb) {
sync_fmt = snd_usb_find_implicit_fb_sync_format(chip, fmt,
hw_params,
- !substream->stream);
+ !substream->stream,
+ &sync_fixed_rate);
if (!sync_fmt) {
usb_audio_dbg(chip,
"cannot find sync format: ep=0x%x, iface=%d:%d, format=%s, rate=%d, channels=%d\n",
@@ -482,6 +510,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
}
} else {
sync_fmt = fmt;
+ sync_fixed_rate = fixed_rate;
}
ret = snd_usb_lock_shutdown(chip);
@@ -499,7 +528,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
close_endpoints(chip, subs);
}
- subs->data_endpoint = snd_usb_endpoint_open(chip, fmt, hw_params, false);
+ subs->data_endpoint = snd_usb_endpoint_open(chip, fmt, hw_params, false, fixed_rate);
if (!subs->data_endpoint) {
ret = -EINVAL;
goto unlock;
@@ -508,7 +537,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
if (fmt->sync_ep) {
subs->sync_endpoint = snd_usb_endpoint_open(chip, sync_fmt,
hw_params,
- fmt == sync_fmt);
+ fmt == sync_fmt,
+ sync_fixed_rate);
if (!subs->sync_endpoint) {
ret = -EINVAL;
goto unlock;
diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h
index 493a4e34d78d..388fe2ba346d 100644
--- a/sound/usb/pcm.h
+++ b/sound/usb/pcm.h
@@ -6,6 +6,8 @@ void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
int snd_usb_pcm_suspend(struct snd_usb_stream *as);
int snd_usb_pcm_resume(struct snd_usb_stream *as);
+bool snd_usb_pcm_has_fixed_rate(struct snd_usb_substream *as);
+
int snd_usb_init_pitch(struct snd_usb_audio *chip,
const struct audioformat *fmt);
void snd_usb_preallocate_buffer(struct snd_usb_substream *subs);
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 58b37bfc885c..3d13fdf7590c 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2152,6 +2152,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x0525, 0xa4ad, /* Hamedal C20 usb camero */
QUIRK_FLAG_IFACE_SKIP_CLOSE),
+ DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */
+ QUIRK_FLAG_FIXED_RATE),
/* Vendor matches */
VENDOR_FLG(0x045e, /* MS Lifecam */
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 2aba508a4831..f5a8dca66457 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -175,6 +175,9 @@ extern bool snd_usb_skip_validation;
* QUIRK_FLAG_FORCE_IFACE_RESET
* Force an interface reset whenever stopping & restarting a stream
* (e.g. after xrun)
+ * QUIRK_FLAG_FIXED_RATE
+ * Do not set PCM rate (frequency) when only one rate is available
+ * for the given endpoint.
*/
#define QUIRK_FLAG_GET_SAMPLE_RATE (1U << 0)
@@ -198,5 +201,6 @@ extern bool snd_usb_skip_validation;
#define QUIRK_FLAG_SKIP_IMPLICIT_FB (1U << 18)
#define QUIRK_FLAG_IFACE_SKIP_CLOSE (1U << 19)
#define QUIRK_FLAG_FORCE_IFACE_RESET (1U << 20)
+#define QUIRK_FLAG_FIXED_RATE (1U << 21)
#endif /* __USBAUDIO_H */
--
2.38.1
2
1
[PATCH] ALSA: azt3328: Remove the unused function snd_azf3328_codec_outl()
by Jiapeng Chong 22 Dec '22
by Jiapeng Chong 22 Dec '22
22 Dec '22
The function snd_azf3328_codec_outl is defined in the azt3328.c file, but
not called elsewhere, so remove this unused function.
sound/pci/azt3328.c:367:1: warning: unused function 'snd_azf3328_codec_outl'.
Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=3432
Reported-by: Abaci Robot <abaci(a)linux.alibaba.com>
Signed-off-by: Jiapeng Chong <jiapeng.chong(a)linux.alibaba.com>
---
sound/pci/azt3328.c | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 7f329dfc5404..0c6754bf9455 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -363,15 +363,6 @@ snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg)
return inw(codec->io_base + reg);
}
-static inline void
-snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
- unsigned reg,
- u32 value
-)
-{
- outl(value, codec->io_base + reg);
-}
-
static inline void
snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec,
unsigned reg, const void *buffer, int count
--
2.20.1.7.g153144c
2
1
This is a question that Cezary would need to answer, as setting the
format was simply brought over from the da7219 AVS machine driver.
1
0
1
0
alsa-project/alsa-ucm-conf issue #256 was opened from lordhoto:
With the current ALC4080 UCM2 configuration, sound is completely broken on my MSI MPG X570S Carbon Max Wifi Mainboard with an ALC4080. Regardless of whether I use the "HiFi 2.0 channels", "HiFi 5.1 channels" or "HiFi 7.1 channels" configuration on the front headphone output, the audio is sometimes "stuck" and not playing at all.
This causes all sorts of issues: when you play a YouTube video it might not even start, after a while sound might play for a bit and then suddenly stop again. Even in Gnome's audio settings dialog when testing left/right the sound sometimes just does not play at all.
I'm using pulseaudio 16.1. When I insert my old ALSA UCM2 configuration, the sound works flawlessly. I suppose there is something wrong with the current configuration, but I can't figure out what. Are there any logs or similar I might look into?
Issue URL : https://github.com/alsa-project/alsa-ucm-conf/issues/256
Repository URL: https://github.com/alsa-project/alsa-ucm-conf
1
0
[PATCH V2 1/4] ASoC: amd: ps: implement api to retrieve acp device config
by Vijendar Mukunda 21 Dec '22
by Vijendar Mukunda 21 Dec '22
21 Dec '22
Implement API to retrieve acp device config and calculate platform
device count and dev mask for platform device node creation.
Currently for DMIC configuration, mask and dev count are calculated.
Same api will be used to extend support for different ACP device
configurations.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda(a)amd.com>
---
v1->v2:
- used ACP_DMIC_DEV macro instead of hard coded value
- changed is_dmic_dev variable initialization
- changed debug level
sound/soc/amd/ps/acp63.h | 7 +++++++
sound/soc/amd/ps/pci-ps.c | 37 +++++++++++++++++++++++++++++++++++--
2 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index 5e7f9c1c1b0e..b015e845e5fa 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -54,6 +54,11 @@
/* time in ms for runtime suspend delay */
#define ACP_SUSPEND_DELAY_MS 2000
+#define ACP63_DMIC_ADDR 2
+#define ACP63_PDM_MODE_DEVS 3
+#define ACP63_PDM_DEV_MASK 1
+#define ACP_DMIC_DEV 2
+
enum acp_config {
ACP_CONFIG_0 = 0,
ACP_CONFIG_1,
@@ -102,4 +107,6 @@ struct acp63_dev_data {
struct resource *res;
bool acp63_audio_mode;
struct platform_device *pdev[ACP63_DEVS];
+ u16 pdev_mask;
+ u16 pdev_count;
};
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index 489f01a20699..9066a625e501 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -132,6 +132,38 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
return IRQ_NONE;
}
+void get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63_dev_data *acp_data)
+{
+ struct acpi_device *dmic_dev;
+ const union acpi_object *obj;
+ bool is_dmic_dev = false;
+
+ dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
+ if (dmic_dev) {
+ if (!acpi_dev_get_property(dmic_dev, "acp-audio-device-type",
+ ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == ACP_DMIC_DEV)
+ is_dmic_dev = true;
+ }
+
+ switch (config) {
+ case ACP_CONFIG_0:
+ case ACP_CONFIG_1:
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ case ACP_CONFIG_9:
+ case ACP_CONFIG_15:
+ dev_dbg(&pci->dev, "Audio Mode %d\n", config);
+ break;
+ default:
+ if (is_dmic_dev) {
+ acp_data->pdev_mask = ACP63_PDM_DEV_MASK;
+ acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
+ }
+ break;
+ }
+}
+
static int snd_acp63_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@@ -183,6 +215,7 @@ static int snd_acp63_probe(struct pci_dev *pci,
if (ret)
goto release_regions;
val = acp63_readl(adata->acp63_base + ACP_PIN_CONFIG);
+ get_acp63_device_config(val, pci, adata);
switch (val) {
case ACP_CONFIG_0:
case ACP_CONFIG_1:
@@ -195,14 +228,14 @@ static int snd_acp63_probe(struct pci_dev *pci,
default:
/* Checking DMIC hardware*/
- adev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), 0x02, 0);
+ adev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP_DMIC_ADDR, 0);
if (!adev)
break;
if (!acpi_dev_get_property(adev, "acp-audio-device-type",
ACPI_TYPE_INTEGER, &obj) &&
- obj->integer.value == 2) {
+ obj->integer.value == ACP_DMIC_DEV) {
adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL);
if (!adata->res) {
ret = -ENOMEM;
--
2.34.1
3
8
The TAS2781 driver implements a flexible and configurable register setting
for one, two, even multiple TAS2781 chips. All the register setting are in
a bin file. Almost no specific register setting can be found in the code.
Signed-off-by: Kevin Lu <luminlong(a)139.com>
---
sound/soc/codecs/Kconfig | 13 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/tas2781-dsp.c | 2483 ++++++++++++++++++++++++++++++++
sound/soc/codecs/tas2781-dsp.h | 213 +++
sound/soc/codecs/tas2781-i2c.c | 2143 +++++++++++++++++++++++++++
sound/soc/codecs/tas2781.h | 208 +++
6 files changed, 5062 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 7022e6286..31d2d9594 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -222,6 +222,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
@@ -1573,6 +1574,18 @@ 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
+ 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
+ register setting, for one, two, even multiple TAS2781 chips.
+ All the register setting are in a bin file. Almost no specific
+ register setting can be found in the code.
+
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 9170ee144..088fed9e7 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -354,6 +354,7 @@ snd-soc-tas2552-objs := tas2552.o
snd-soc-tas2562-objs := tas2562.o
snd-soc-tas2764-objs := tas2764.o
snd-soc-tas2780-objs := tas2780.o
+snd-soc-tas2781-objs := tas2781-i2c.o tas2781-dsp.o
# Mux
snd-soc-simple-mux-objs := simple-mux.o
@@ -604,6 +605,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 000000000..56c7efd30
--- /dev/null
+++ b/sound/soc/codecs/tas2781-dsp.c
@@ -0,0 +1,2483 @@
+// 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 register setting
+ * for one, two, even multiple TAS2781 chips. All the register setting are in
+ * a bin file. Almost no specific register setting can be found in the code.
+ *
+ * Author: Shenghao Ding <shenghao-ding(a)ti.com>
+ * Kevin Lu <kevin-lu(a)ti.com>
+ */
+
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/string.h>
+#include <linux/crc8.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+
+#include "tas2781.h"
+
+#define TAS2781_CAL_BIN_PATH ("/lib/firmware/")
+
+#define ERROR_PRAM_CRCCHK (0x0000000)
+#define ERROR_YRAM_CRCCHK (0x0000001)
+#define BINFILEDOCVER (0)
+#define DRVFWVER (1)
+#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 TYCRC {
+ unsigned char mnOffset;
+ unsigned char mnLen;
+};
+
+const unsigned int BinFileformatVerInfo[][2] = {
+ {0x100, 0x100},
+ {0x110, 0x200},
+ {0x200, 0x300},
+ {0x210, 0x310},
+ {0x230, 0x320},
+ {0x300, 0x400}
+};
+
+const char *devicefamily[1] = {
+ "TAS Devices" };
+
+const char *devicelist[TASDEVICE_DSP_TAS_MAX_DEVICE] = {
+ "TAS2555",
+ "TAS2555 Stereo",
+ "TAS2557 Mono",
+ "TAS2557 Dual Mono",
+ "TAS2559",
+ "TAS2563",
+ NULL,
+ "TAS2563 Dual Mono",
+ "TAS2563 Quad",
+ "TAS2563 2.1",
+ "TAS2781",
+ "TAS2781 Stereo",
+ "TAS2781 2.1",
+ "TAS2781 Quad"
+};
+
+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 TFirmware *pFirmware,
+ struct TBlock *pBlock, const struct firmware *pFW, int offset)
+{
+ const unsigned char *pData = pFW->data;
+
+ if (offset + 4 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pBlock->mnType = SMS_HTONL(pData[offset],
+ pData[offset + 1], pData[offset + 2],
+ pData[offset + 3]);
+ offset += 4;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: mbPChkSumPresent error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pBlock->mbPChkSumPresent = pData[offset];
+ offset++;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: mnPChkSum error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pBlock->mnPChkSum = pData[offset];
+ offset++;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: mbYChkSumPresent error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pBlock->mbYChkSumPresent = pData[offset];
+ offset++;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: mnYChkSum error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pBlock->mnYChkSum = pData[offset];
+ offset++;
+
+ if (offset + 4 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: blk_size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pBlock->blk_size = SMS_HTONL(pData[offset],
+ pData[offset + 1], pData[offset + 2],
+ pData[offset + 3]);
+ offset += 4;
+
+ if (offset + 4 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: nSublocks error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pBlock->nSublocks = SMS_HTONL(pData[offset],
+ pData[offset + 1], pData[offset + 2], pData[offset + 3]);
+ offset += 4;
+
+ pBlock->mpData = kzalloc(pBlock->blk_size, GFP_KERNEL);
+ if (pBlock->mpData == NULL) {
+ offset = -1;
+ goto out;
+ }
+ memcpy(pBlock->mpData, &pData[offset], pBlock->blk_size);
+ offset += pBlock->blk_size;
+out:
+ return offset;
+}
+
+static int fw_parse_data_kernel(struct TFirmware *pFirmware,
+ struct TData *pImageData, const struct firmware *pFW, int offset)
+{
+ const unsigned char *pData = pFW->data;
+ unsigned int nBlock;
+
+ if (offset + 4 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pImageData->mnBlocks = SMS_HTONL(pData[offset],
+ pData[offset + 1], pData[offset + 2], pData[offset + 3]);
+ offset += 4;
+
+ pImageData->mpBlocks =
+ kcalloc(pImageData->mnBlocks, sizeof(struct TBlock),
+ GFP_KERNEL);
+ if (pImageData->mpBlocks == NULL) {
+ dev_err(pFirmware->dev, "%s: FW memory failed!\n", __func__);
+ goto out;
+ }
+
+ for (nBlock = 0; nBlock < pImageData->mnBlocks; nBlock++) {
+ offset = fw_parse_block_data_kernel(pFirmware,
+ &(pImageData->mpBlocks[nBlock]), pFW, offset);
+ if (offset < 0) {
+ offset = -1;
+ goto out;
+ }
+ }
+out:
+ return offset;
+}
+
+int fw_parse_program_data_kernel(struct tasdevice_priv *tas_dev,
+ struct TFirmware *pFirmware,
+ const struct firmware *pFW, int offset)
+{
+ struct TProgram *pProgram;
+ const unsigned char *buf = pFW->data;
+ unsigned int nProgram = 0;
+
+ for (nProgram = 0; nProgram < pFirmware->mnPrograms; nProgram++) {
+ pProgram = &(pFirmware->mpPrograms[nProgram]);
+ if (offset + 64 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mpName error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ memcpy(pProgram->mpName, &buf[offset], 64);
+ offset += 64;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnAppMode error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pProgram->mnAppMode = buf[offset];
+ offset++;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnPDMI2SMode error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pProgram->mnPDMI2SMode = buf[offset];
+ offset++;
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnISnsPD error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pProgram->mnISnsPD = buf[offset];
+ offset++;
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnVSnsPD error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pProgram->mnVSnsPD = buf[offset];
+ offset++;
+ //skip 3-byte reserved
+ offset += 3;
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnPowerLDG error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pProgram->mnPowerLDG = buf[offset];
+ offset++;
+
+ offset = fw_parse_data_kernel(pFirmware, &(pProgram->mData),
+ pFW, offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+int fw_parse_configuration_data_kernel(
+ struct tasdevice_priv *tas_dev,
+ struct TFirmware *pFirmware, const struct firmware *pFW, int offset)
+{
+ const unsigned char *pData = pFW->data;
+
+ unsigned int nConfiguration;
+ struct TConfiguration *pConfiguration;
+
+ for (nConfiguration = 0; nConfiguration < pFirmware->mnConfigurations;
+ nConfiguration++) {
+ pConfiguration =
+ &(pFirmware->mpConfigurations[nConfiguration]);
+ if (offset + 64 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mpName error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ memcpy(pConfiguration->mpName, &pData[offset], 64);
+ offset += 64;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev,
+ "%s: mnDevice_orientation error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pConfiguration->mnDevice_orientation = pData[offset];
+ offset++;
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnDevices error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pConfiguration->mnDevices = pData[offset + 1];
+ offset += 1;
+
+ if (offset + 2 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mProgram error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pConfiguration->mProgram = SMS_HTONS(pData[offset],
+ pData[offset + 1]);
+ offset += 2;
+
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnSamplingRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pConfiguration->mnSamplingRate = SMS_HTONL(pData[offset],
+ pData[offset + 1], pData[offset + 2],
+ pData[offset + 3]);
+ offset += 4;
+
+ if (offset + 2 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnPLLSrc error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pConfiguration->mnPLLSrc = SMS_HTONS(pData[offset],
+ pData[offset + 1]);
+ offset += 2;
+
+ if (offset + 2 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnFsRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pConfiguration->mnFsRate = SMS_HTONS(pData[offset],
+ pData[offset + 1]);
+ offset += 2;
+
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnPLLSrcRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pConfiguration->mnPLLSrcRate = SMS_HTONL(pData[offset],
+ pData[offset + 1], pData[offset + 2],
+ pData[offset + 3]);
+ offset += 4;
+ offset = fw_parse_data_kernel(pFirmware,
+ &(pConfiguration->mData), pFW, offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+int fw_parse_variable_header_kernel(struct tasdevice_priv *tas_dev,
+ const struct firmware *pFW, int offset)
+{
+ struct TFirmware *pFirmware = tas_dev->mpFirmware;
+ struct tasdevice_dspfw_hdr *pFw_hdr = &(pFirmware->fw_hdr);
+ const unsigned char *buf = pFW->data;
+ struct TProgram *pProgram;
+ struct TConfiguration *pConfiguration;
+ unsigned int nProgram = 0, nConfiguration = 0;
+ unsigned short maxConf = TASDEVICE_MAXCONFIG_NUM_KERNEL;
+
+ if (offset + 2 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFw_hdr->mnDeviceFamily = SMS_HTONS(buf[offset], buf[offset + 1]);
+ if (pFw_hdr->mnDeviceFamily != 0) {
+ dev_err(tas_dev->dev, "ERROR:%s:not TAS device\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+ if (offset + 2 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnDevice error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFw_hdr->mnDevice = SMS_HTONS(buf[offset], buf[offset + 1]);
+ if (pFw_hdr->mnDevice >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ pFw_hdr->mnDevice == 6) {
+ dev_err(tas_dev->dev, "ERROR:%s: not support device %d\n",
+ __func__, pFw_hdr->mnDevice);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+ pFw_hdr->ndev = deviceNumber[pFw_hdr->mnDevice];
+
+ if (pFw_hdr->ndev != tas_dev->ndev) {
+ dev_err(tas_dev->dev,
+ "%s: ndev(%u) in dspbin dismatch ndev(%u) in DTS\n",
+ __func__, pFw_hdr->ndev, tas_dev->ndev);
+ offset = -1;
+ goto out;
+ }
+
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnPrograms error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFirmware->mnPrograms = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+
+ if (pFirmware->mnPrograms == 0 || pFirmware->mnPrograms >
+ 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 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mpPrograms error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ pFirmware->mpPrograms =
+ kcalloc(pFirmware->mnPrograms,
+ sizeof(struct TProgram), GFP_KERNEL);
+ if (pFirmware->mpPrograms == NULL) {
+ dev_err(tas_dev->dev, "%s: mpPrograms memory failed!\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+
+ for (nProgram = 0; nProgram < pFirmware->mnPrograms; nProgram++) {
+ pProgram = &(pFirmware->mpPrograms[nProgram]);
+ pProgram->prog_size = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ pFirmware->cfg_start_offset += pProgram->prog_size;
+ offset += 4;
+ }
+ offset += (4 * (5 - nProgram));
+
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnConfigurations error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pFirmware->mnConfigurations = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ maxConf = (pFw_hdr->ndev >= 4) ?
+ TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS :
+ TASDEVICE_MAXCONFIG_NUM_KERNEL;
+ if (pFirmware->mnConfigurations == 0 ||
+ pFirmware->mnConfigurations > maxConf) {
+ dev_err(tas_dev->dev, "%s: mnConfigurations is invalid\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+
+ if (offset + 4 * maxConf > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mpConfigurations error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+
+ pFirmware->mpConfigurations = kcalloc(pFirmware->mnConfigurations,
+ sizeof(struct TConfiguration), GFP_KERNEL);
+ if (pFirmware->mpConfigurations == NULL) {
+ offset = -1;
+ goto out;
+ }
+
+ for (nConfiguration = 0; nConfiguration < pFirmware->mnPrograms;
+ nConfiguration++) {
+ pConfiguration =
+ &(pFirmware->mpConfigurations[nConfiguration]);
+ pConfiguration->cfg_size = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ }
+
+ offset += (4 * (maxConf - nConfiguration));
+ pFirmware->prog_start_offset = offset;
+ pFirmware->cfg_start_offset += offset;
+out:
+ return offset;
+}
+
+int tasdevice_load_block_kernel(struct tasdevice_priv *pTAS2781,
+ struct TBlock *pBlock)
+{
+ int nResult = 0;
+
+ unsigned char *pData = pBlock->mpData;
+ unsigned int i = 0, length = 0;
+ const unsigned int blk_size = pBlock->blk_size;
+ unsigned char dev_idx = 0;
+ struct tasdevice_dspfw_hdr *pFw_hdr = &(pTAS2781->mpFirmware->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *pFw_fixed_hdr = &(pFw_hdr->mnFixedHdr);
+
+ if (pFw_fixed_hdr->mnPPCVersion >= PPC3_VERSION) {
+ switch (pBlock->mnType) {
+ 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(pTAS2781->dev,
+ "%s: TAS2781 load block: Other Type = 0x%02x\n",
+ __func__, pBlock->mnType);
+ break;
+ }
+ } else {
+ switch (pBlock->mnType) {
+ 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(pTAS2781->dev,
+ "%s: TAS2781 load block: Other Type = 0x%02x\n",
+ __func__,
+ pBlock->mnType);
+ break;
+ }
+ }
+
+ for (i = 0; i < pBlock->nSublocks; i++) {
+ int rc = tasdevice_process_block(pTAS2781, pData + length,
+ dev_idx, blk_size - length);
+ if (rc < 0) {
+ dev_err(pTAS2781->dev,
+ "%s: ERROR:%u %u sublock write error\n",
+ __func__, length, blk_size);
+ break;
+ }
+ length += (unsigned int)rc;
+ if (blk_size < length) {
+ dev_err(pTAS2781->dev,
+ "%s: ERROR:%u %u out of memory\n",
+ __func__, length, blk_size);
+ break;
+ }
+ }
+
+ return nResult;
+}
+
+int fw_parse_variable_header_git(struct tasdevice_priv *tas_dev,
+ const struct firmware *pFW, int offset)
+{
+ const unsigned char *buf = pFW->data;
+ struct TFirmware *pFirmware = tas_dev->mpFirmware;
+ struct tasdevice_dspfw_hdr *pFw_hdr = &(pFirmware->fw_hdr);
+ int i = strlen((char *)&buf[offset]);
+
+ i++;
+
+ if (offset + i > pFW->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ pFw_hdr->mpDescription = kmemdup(&buf[offset], i, GFP_KERNEL);
+ if (pFw_hdr->mpDescription == NULL)
+ goto out;
+ offset += i;
+
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFw_hdr->mnDeviceFamily = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (pFw_hdr->mnDeviceFamily != 0) {
+ dev_err(tas_dev->dev, "ERROR:%s: not TAS device\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFw_hdr->mnDevice = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (pFw_hdr->mnDevice >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ pFw_hdr->mnDevice == 6) {
+ dev_err(tas_dev->dev, "ERROR:%s: not support device %d\n",
+ __func__, pFw_hdr->mnDevice);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ pFw_hdr->ndev = deviceNumber[pFw_hdr->mnDevice];
+ if (pFw_hdr->ndev != tas_dev->ndev) {
+ dev_err(tas_dev->dev,
+ "%s: ndev(%u) in dspbin dismatch ndev(%u) in DTS\n",
+ __func__, pFw_hdr->ndev,
+ tas_dev->ndev);
+ offset = -1;
+ }
+
+out:
+ return offset;
+}
+
+int fw_parse_variable_header_cal(struct tasdevice_priv *tas_dev,
+ struct TFirmware *pCalFirmware, const struct firmware *pFW, int offset)
+{
+ const unsigned char *buf = pFW->data;
+ struct tasdevice_dspfw_hdr *pFw_hdr = &(pCalFirmware->fw_hdr);
+ int i = strlen((char *)&buf[offset]);
+
+ i++;
+
+ if (offset + i > pFW->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ pFw_hdr->mpDescription = kmemdup(&buf[offset], i, GFP_KERNEL);
+ if (pFw_hdr->mpDescription == NULL)
+ goto out;
+ offset += i;
+
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnDeviceFamily error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFw_hdr->mnDeviceFamily = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (pFw_hdr->mnDeviceFamily != 0) {
+ dev_err(tas_dev->dev, "ERROR:%s: not TAS device\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnDevice error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFw_hdr->mnDevice = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (pFw_hdr->mnDevice >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ pFw_hdr->mnDevice == 6) {
+ dev_err(tas_dev->dev, "ERROR:%s: not support device %d\n",
+ __func__, pFw_hdr->mnDevice);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ pFw_hdr->ndev = deviceNumber[pFw_hdr->mnDevice];
+ if (pFw_hdr->ndev != 1) {
+ dev_err(tas_dev->dev,
+ "%s: calbin must be 1, but currently ndev(%u)\n",
+ __func__, pFw_hdr->ndev);
+ offset = -1;
+ }
+
+out:
+ return offset;
+}
+
+static inline void tas2781_clear_Calfirmware(struct TFirmware
+ *mpCalFirmware)
+{
+ int i = 0;
+ unsigned int nBlock = 0;
+
+ if (mpCalFirmware->mpCalibrations) {
+ struct TCalibration *pCalibration;
+
+ for (i = 0; i < mpCalFirmware->mnCalibrations; i++) {
+ pCalibration = &(mpCalFirmware->mpCalibrations[i]);
+ if (pCalibration) {
+ struct TData *pImageData =
+ &(pCalibration->mData);
+
+ if (pImageData->mpBlocks) {
+ struct TBlock *pBlock;
+
+ for (nBlock = 0; nBlock <
+ pImageData->mnBlocks;
+ nBlock++) {
+ pBlock =
+ &(pImageData->mpBlocks[nBlock]);
+ kfree(pBlock->mpData);
+ }
+ kfree(pImageData->mpBlocks);
+ }
+ kfree(pCalibration->mpDescription);
+ }
+ }
+ kfree(mpCalFirmware->mpCalibrations);
+ }
+ kfree(mpCalFirmware);
+}
+
+static int fw_parse_block_data(struct TFirmware *pFirmware,
+ struct TBlock *pBlock, const struct firmware *pFW, int offset)
+{
+ unsigned char *pData = (unsigned char *)pFW->data;
+ int n;
+
+ if (offset + 4 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: mnType error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pBlock->mnType = SMS_HTONL(pData[offset],
+ pData[offset + 1], pData[offset + 2], pData[offset + 3]);
+ offset += 4;
+
+ if (pFirmware->fw_hdr.mnFixedHdr.mnDriverVersion >=
+ PPC_DRIVER_CRCCHK) {
+ if (offset + 1 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: mbPChkSumPresent error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pBlock->mbPChkSumPresent = pData[offset];
+ offset++;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: mnPChkSum error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pBlock->mnPChkSum = pData[offset];
+ offset++;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: mbYChkSumPresent error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pBlock->mbYChkSumPresent = pData[offset];
+ offset++;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: mnYChkSum error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pBlock->mnYChkSum = pData[offset];
+ offset++;
+ } else {
+ pBlock->mbPChkSumPresent = 0;
+ pBlock->mbYChkSumPresent = 0;
+ }
+ if (offset + 4 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: mnCommands error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pBlock->mnCommands = SMS_HTONL(pData[offset],
+ pData[offset + 1], pData[offset + 2], pData[offset + 3]);
+ offset += 4;
+
+ n = pBlock->mnCommands * 4;
+ if (offset + n > pFW->size) {
+ dev_err(pFirmware->dev,
+ "%s: File Size(%u) error offset = %d n = %d\n",
+ __func__, pFW->size, offset, n);
+ offset = -1;
+ goto out;
+ }
+ pBlock->mpData = kmemdup(&pData[offset], n, GFP_KERNEL);
+ if (pBlock->mpData == NULL) {
+ offset = -1;
+ goto out;
+ }
+ offset += n;
+out:
+ return offset;
+}
+
+static int fw_parse_data(struct TFirmware *pFirmware,
+ struct TData *pImageData, const struct firmware *pFW, int offset)
+{
+ const unsigned char *pData = (unsigned char *)pFW->data;
+ int n = 0;
+ unsigned int nBlock;
+
+ if (offset + 64 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: mpName error\n", __func__);
+ n = -1;
+ goto out;
+ }
+ memcpy(pImageData->mpName, &pData[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&pData[offset]);
+ n++;
+ if (offset + n > pFW->size) {
+ dev_err(pFirmware->dev, "%s: mpDescription error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pImageData->mpDescription = kmemdup(pData, n, GFP_KERNEL);
+ if (pImageData->mpDescription == NULL)
+ goto out;
+ offset += n;
+
+ if (offset + 2 > pFW->size) {
+ dev_err(pFirmware->dev, "%s: mnBlocks error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pImageData->mnBlocks = SMS_HTONS(pData[offset], pData[offset + 1]);
+ offset += 2;
+
+ pImageData->mpBlocks =
+ kcalloc(pImageData->mnBlocks, sizeof(struct TBlock),
+ GFP_KERNEL);
+ if (pImageData->mpBlocks == NULL) {
+ dev_err(pFirmware->dev, "%s: FW memory failed!\n", __func__);
+ goto out;
+ }
+ for (nBlock = 0; nBlock < pImageData->mnBlocks; nBlock++) {
+ offset = fw_parse_block_data(pFirmware,
+ &(pImageData->mpBlocks[nBlock]), pFW, offset);
+ if (offset < 0) {
+ offset = -1;
+ goto out;
+ }
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_calibration_data(struct tasdevice_priv *tas_dev,
+ struct TFirmware *pFirmware, const struct firmware *pFW, int offset)
+{
+ unsigned char *pData = (unsigned char *)pFW->data;
+ unsigned int n = 0;
+ unsigned int nCalibration = 0;
+ struct TCalibration *pCalibration = NULL;
+
+ if (offset + 2 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnCalibrations error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFirmware->mnCalibrations = SMS_HTONS(pData[offset],
+ pData[offset + 1]);
+ offset += 2;
+
+ if (pFirmware->mnCalibrations != 1) {
+ dev_err(tas_dev->dev,
+ "%s: only support one calibraiton(%d)!\n",
+ __func__, pFirmware->mnCalibrations);
+ goto out;
+ }
+
+ pFirmware->mpCalibrations =
+ kcalloc(pFirmware->mnCalibrations, sizeof(struct TCalibration),
+ GFP_KERNEL);
+ if (pFirmware->mpCalibrations == NULL) {
+ dev_err(tas_dev->dev, "%s: mpCalibrations memory failed!\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ for (nCalibration = 0; nCalibration < pFirmware->mnCalibrations;
+ nCalibration++) {
+ if (offset + 64 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mpCalibrations error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pCalibration = &(pFirmware->mpCalibrations[nCalibration]);
+ memcpy(pCalibration->mpName, &pData[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&pData[offset]);
+ n++;
+ if (offset + n > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mpDescription error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pCalibration->mpDescription = kmemdup(&pData[offset], n,
+ GFP_KERNEL);
+ if (pCalibration->mpDescription == NULL) {
+ offset = -1;
+ goto out;
+ }
+ offset += n;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev,
+ "%s: mnProgram error, offset = %d\n", __func__,
+ offset);
+ offset = -1;
+ goto out;
+ }
+ pCalibration->mnProgram = pData[offset];
+ offset++;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev,
+ "%s: mnConfiguration error, offset = %d\n",
+ __func__,
+ offset);
+ offset = -1;
+ goto out;
+ }
+ pCalibration->mnConfiguration = pData[offset];
+ offset++;
+
+ offset = fw_parse_data(pFirmware, &(pCalibration->mData), pFW,
+ offset);
+ if (offset < 0)
+ goto out;
+ }
+
+out:
+
+ return offset;
+}
+
+static int fw_parse_program_data(struct tasdevice_priv *tas_dev,
+ struct TFirmware *pFirmware, const struct firmware *pFW, int offset)
+{
+ struct TProgram *pProgram;
+ unsigned char *buf = (unsigned char *)pFW->data;
+ int nProgram = 0;
+
+ if (offset + 2 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFirmware->mnPrograms = SMS_HTONS(buf[offset], buf[offset + 1]);
+ offset += 2;
+
+ if (pFirmware->mnPrograms == 0) {
+ dev_err(tas_dev->dev, "%s: mnPrograms is null, maybe calbin\n",
+ __func__);
+ //Do not "offset = -1;", because of calbin
+ goto out;
+ }
+
+ pFirmware->mpPrograms =
+ kcalloc(pFirmware->mnPrograms, sizeof(struct TProgram),
+ GFP_KERNEL);
+ if (pFirmware->mpPrograms == NULL) {
+ offset = -1;
+ goto out;
+ }
+ for (nProgram = 0; nProgram < pFirmware->mnPrograms; nProgram++) {
+ int n = 0;
+
+ pProgram = &(pFirmware->mpPrograms[nProgram]);
+ if (offset + 64 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mpName error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ memcpy(pProgram->mpName, &buf[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&buf[offset]);
+ n++;
+ if (offset + n > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mpDescription error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pProgram->mpDescription = kmemdup(&buf[offset], n, GFP_KERNEL);
+ if (pProgram->mpDescription == NULL) {
+ offset = -1;
+ goto out;
+ }
+
+ offset += n;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnAppMode error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pProgram->mnAppMode = buf[offset];
+ offset++;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnPDMI2SMode error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pProgram->mnPDMI2SMode = buf[offset];
+ offset++;
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnISnsPD error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pProgram->mnISnsPD = buf[offset];
+ offset++;
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnVSnsPD error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pProgram->mnVSnsPD = buf[offset];
+ offset++;
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnPowerLDG error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pProgram->mnPowerLDG = buf[offset];
+ offset++;
+
+ offset = fw_parse_data(pFirmware, &(pProgram->mData), pFW,
+ offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_configuration_data(
+ struct tasdevice_priv *tas_dev,
+ struct TFirmware *pFirmware,
+ const struct firmware *pFW, int offset)
+{
+ unsigned char *pData = (unsigned char *)pFW->data;
+ int n;
+ unsigned int nConfiguration;
+ struct TConfiguration *pConfiguration;
+
+ if (offset + 2 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFirmware->mnConfigurations = SMS_HTONS(pData[offset],
+ pData[offset + 1]);
+ offset += 2;
+
+ if (pFirmware->mnConfigurations == 0) {
+ dev_err(tas_dev->dev, "%s: mnConfigurations is zero\n",
+ __func__);
+ //Do not "offset = -1;", because of calbin
+ goto out;
+ }
+ pFirmware->mpConfigurations =
+ kcalloc(pFirmware->mnConfigurations,
+ sizeof(struct TConfiguration), GFP_KERNEL);
+
+ for (nConfiguration = 0; nConfiguration < pFirmware->mnConfigurations;
+ nConfiguration++) {
+ pConfiguration =
+ &(pFirmware->mpConfigurations[nConfiguration]);
+ if (offset + 64 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ memcpy(pConfiguration->mpName, &pData[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&pData[offset]);
+ n++;
+ if (offset + n > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mpDescription error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pConfiguration->mpDescription = kmemdup(&pData[offset], n,
+ GFP_KERNEL);
+
+ if (pConfiguration->mpDescription == NULL) {
+ dev_err(tas_dev->dev, "%s: FW memory failed!\n",
+ __func__);
+ goto out;
+ }
+ offset += n;
+ if (offset + 2 > pFW->size) {
+ dev_err(tas_dev->dev,
+ "%s: mnDevice_orientation error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pConfiguration->mnDevice_orientation = pData[offset];
+
+ pConfiguration->mnDevices = pData[offset + 1];
+ offset += 2;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mProgram error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pConfiguration->mProgram = pData[offset];
+ offset++;
+
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnSamplingRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pConfiguration->mnSamplingRate = SMS_HTONL(pData[offset],
+ pData[offset + 1], pData[offset + 2],
+ pData[offset + 3]);
+ offset += 4;
+
+ if (offset + 1 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnPLLSrc error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pConfiguration->mnPLLSrc = pData[offset];
+ offset++;
+
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnPLLSrcRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pConfiguration->mnPLLSrcRate = SMS_HTONL(pData[offset],
+ pData[offset + 1], pData[offset + 2],
+ pData[offset + 3]);
+ offset += 4;
+
+ if (offset + 2 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnFsRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ pConfiguration->mnFsRate = SMS_HTONS(pData[offset],
+ pData[offset + 1]);
+ offset += 2;
+
+ offset = fw_parse_data(pFirmware, &(pConfiguration->mData),
+ pFW, offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_header(struct tasdevice_priv *tas_dev,
+ struct TFirmware *pFirmware, const struct firmware *pFW, int offset)
+{
+ struct tasdevice_dspfw_hdr *pFw_hdr = &(pFirmware->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *pFw_fixed_hdr = &(pFw_hdr->mnFixedHdr);
+ const unsigned char *buf = (unsigned char *)pFW->data;
+ int i = 0;
+ unsigned char pMagicNumber[] = { 0x35, 0x35, 0x35, 0x32 };
+
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ if (memcmp(&buf[offset], pMagicNumber, 4)) {
+ dev_err(tas_dev->dev, "%s: Magic number doesn't match",
+ __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ pFw_fixed_hdr->mnMagicNumber = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnFWSize error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFw_fixed_hdr->mnFWSize = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ if (pFw_fixed_hdr->mnFWSize != pFW->size) {
+ dev_err(tas_dev->dev, "File size not match, %d %d", pFW->size,
+ pFw_fixed_hdr->mnFWSize);
+ offset = -1;
+ goto out;
+ }
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnChecksum error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFw_fixed_hdr->mnChecksum = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnPPCVersion error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFw_fixed_hdr->mnPPCVersion = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnFWVersion error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFw_fixed_hdr->mnFWVersion = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnDriverVersion error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ pFw_fixed_hdr->mnDriverVersion = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mnTimeStamp error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ for (i = 0; i < sizeof(BinFileformatVerInfo) /
+ sizeof(BinFileformatVerInfo[0]); i++) {
+ if (BinFileformatVerInfo[i][DRVFWVER] ==
+ pFw_fixed_hdr->mnDriverVersion) {
+ pFw_hdr->mnBinFileDocVer =
+ BinFileformatVerInfo[i][BINFILEDOCVER];
+ break;
+ }
+ }
+ pFw_fixed_hdr->mnTimeStamp = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 64 > pFW->size) {
+ dev_err(tas_dev->dev, "%s: mpDDCName error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ memcpy(pFw_fixed_hdr->mpDDCName, &buf[offset], 64);
+ offset += 64;
+
+ out:
+ return offset;
+}
+
+static const unsigned char crc8_lookup_table[CRC8_TABLE_SIZE] = {
+ 0x00, 0x4D, 0x9A, 0xD7, 0x79, 0x34, 0xE3, 0xAE,
+ 0xF2, 0xBF, 0x68, 0x25, 0x8B, 0xC6, 0x11, 0x5C,
+ 0xA9, 0xE4, 0x33, 0x7E, 0xD0, 0x9D, 0x4A, 0x07,
+ 0x5B, 0x16, 0xC1, 0x8C, 0x22, 0x6F, 0xB8, 0xF5,
+ 0x1F, 0x52, 0x85, 0xC8, 0x66, 0x2B, 0xFC, 0xB1,
+ 0xED, 0xA0, 0x77, 0x3A, 0x94, 0xD9, 0x0E, 0x43,
+ 0xB6, 0xFB, 0x2C, 0x61, 0xCF, 0x82, 0x55, 0x18,
+ 0x44, 0x09, 0xDE, 0x93, 0x3D, 0x70, 0xA7, 0xEA,
+ 0x3E, 0x73, 0xA4, 0xE9, 0x47, 0x0A, 0xDD, 0x90,
+ 0xCC, 0x81, 0x56, 0x1B, 0xB5, 0xF8, 0x2F, 0x62,
+ 0x97, 0xDA, 0x0D, 0x40, 0xEE, 0xA3, 0x74, 0x39,
+ 0x65, 0x28, 0xFF, 0xB2, 0x1C, 0x51, 0x86, 0xCB,
+ 0x21, 0x6C, 0xBB, 0xF6, 0x58, 0x15, 0xC2, 0x8F,
+ 0xD3, 0x9E, 0x49, 0x04, 0xAA, 0xE7, 0x30, 0x7D,
+ 0x88, 0xC5, 0x12, 0x5F, 0xF1, 0xBC, 0x6B, 0x26,
+ 0x7A, 0x37, 0xE0, 0xAD, 0x03, 0x4E, 0x99, 0xD4,
+ 0x7C, 0x31, 0xE6, 0xAB, 0x05, 0x48, 0x9F, 0xD2,
+ 0x8E, 0xC3, 0x14, 0x59, 0xF7, 0xBA, 0x6D, 0x20,
+ 0xD5, 0x98, 0x4F, 0x02, 0xAC, 0xE1, 0x36, 0x7B,
+ 0x27, 0x6A, 0xBD, 0xF0, 0x5E, 0x13, 0xC4, 0x89,
+ 0x63, 0x2E, 0xF9, 0xB4, 0x1A, 0x57, 0x80, 0xCD,
+ 0x91, 0xDC, 0x0B, 0x46, 0xE8, 0xA5, 0x72, 0x3F,
+ 0xCA, 0x87, 0x50, 0x1D, 0xB3, 0xFE, 0x29, 0x64,
+ 0x38, 0x75, 0xA2, 0xEF, 0x41, 0x0C, 0xDB, 0x96,
+ 0x42, 0x0F, 0xD8, 0x95, 0x3B, 0x76, 0xA1, 0xEC,
+ 0xB0, 0xFD, 0x2A, 0x67, 0xC9, 0x84, 0x53, 0x1E,
+ 0xEB, 0xA6, 0x71, 0x3C, 0x92, 0xDF, 0x08, 0x45,
+ 0x19, 0x54, 0x83, 0xCE, 0x60, 0x2D, 0xFA, 0xB7,
+ 0x5D, 0x10, 0xC7, 0x8A, 0x24, 0x69, 0xBE, 0xF3,
+ 0xAF, 0xE2, 0x35, 0x78, 0xD6, 0x9B, 0x4C, 0x01,
+ 0xF4, 0xB9, 0x6E, 0x23, 0x8D, 0xC0, 0x17, 0x5A,
+ 0x06, 0x4B, 0x9C, 0xD1, 0x7F, 0x32, 0xE5, 0xA8
+};
+
+static int isInPageYRAM(struct tasdevice_priv *pTAS2781,
+ struct TYCRC *pCRCData,
+ unsigned char nBook, unsigned char nPage,
+ unsigned char nReg, unsigned char len)
+{
+ int nResult = 0;
+
+ if (nBook == TAS2781_YRAM_BOOK1) {
+ if (nPage == TAS2781_YRAM1_PAGE) {
+ if (nReg >= TAS2781_YRAM1_START_REG) {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = len;
+ nResult = 1;
+ } else if ((nReg + len) > TAS2781_YRAM1_START_REG) {
+ pCRCData->mnOffset = TAS2781_YRAM1_START_REG;
+ pCRCData->mnLen =
+ len - (TAS2781_YRAM1_START_REG - nReg);
+ nResult = 1;
+ } else
+ nResult = 0;
+ } else if (nPage == TAS2781_YRAM3_PAGE) {
+ if (nReg > TAS2781_YRAM3_END_REG) {
+ nResult = 0;
+ } else if (nReg >= TAS2781_YRAM3_START_REG) {
+ if ((nReg + len) > TAS2781_YRAM3_END_REG) {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen =
+ TAS2781_YRAM3_END_REG - nReg + 1;
+ nResult = 1;
+ } else {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = len;
+ nResult = 1;
+ }
+ } else {
+ if ((nReg + (len-1)) <
+ TAS2781_YRAM3_START_REG)
+ nResult = 0;
+ else {
+ pCRCData->mnOffset =
+ TAS2781_YRAM3_START_REG;
+ pCRCData->mnLen =
+ len - (TAS2781_YRAM3_START_REG - nReg);
+ nResult = 1;
+ }
+ }
+ }
+ } else if (nBook ==
+ TAS2781_YRAM_BOOK2) {
+ if (nPage == TAS2781_YRAM5_PAGE) {
+ if (nReg > TAS2781_YRAM5_END_REG) {
+ nResult = 0;
+ } else if (nReg >= TAS2781_YRAM5_START_REG) {
+ if ((nReg + len) > TAS2781_YRAM5_END_REG) {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen =
+ TAS2781_YRAM5_END_REG - nReg + 1;
+ nResult = 1;
+ } else {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = len;
+ nResult = 1;
+ }
+ } else {
+ if ((nReg + (len-1)) <
+ TAS2781_YRAM5_START_REG)
+ nResult = 0;
+ else {
+ pCRCData->mnOffset =
+ TAS2781_YRAM5_START_REG;
+ pCRCData->mnLen =
+ len - (TAS2781_YRAM5_START_REG - nReg);
+ nResult = 1;
+ }
+ }
+ }
+ } else
+ nResult = 0;
+
+ return nResult;
+}
+
+static int isInBlockYRAM(struct tasdevice_priv *pTAS2781,
+ struct TYCRC *pCRCData,
+ unsigned char nBook, unsigned char nPage,
+ unsigned char nReg, unsigned char len)
+{
+ int nResult = 0;
+
+ if (nBook == TAS2781_YRAM_BOOK1) {
+ if (nPage < TAS2781_YRAM2_START_PAGE)
+ nResult = 0;
+ else if (nPage <= TAS2781_YRAM2_END_PAGE) {
+ if (nReg > TAS2781_YRAM2_END_REG)
+ nResult = 0;
+ else if (nReg >= TAS2781_YRAM2_START_REG) {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = len;
+ nResult = 1;
+ } else {
+ if ((nReg + (len-1)) <
+ TAS2781_YRAM2_START_REG)
+ nResult = 0;
+ else {
+ pCRCData->mnOffset =
+ TAS2781_YRAM2_START_REG;
+ pCRCData->mnLen =
+ nReg + len - TAS2781_YRAM2_START_REG;
+ nResult = 1;
+ }
+ }
+ } else
+ nResult = 0;
+ } else if (nBook ==
+ TAS2781_YRAM_BOOK2) {
+ if (nPage < TAS2781_YRAM4_START_PAGE)
+ nResult = 0;
+ else if (nPage <= TAS2781_YRAM4_END_PAGE) {
+ if (nReg > TAS2781_YRAM2_END_REG)
+ nResult = 0;
+ else if (nReg >= TAS2781_YRAM2_START_REG) {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = len;
+ nResult = 1;
+ } else {
+ if ((nReg + (len-1))
+ < TAS2781_YRAM2_START_REG)
+ nResult = 0;
+ else {
+ pCRCData->mnOffset =
+ TAS2781_YRAM2_START_REG;
+ pCRCData->mnLen =
+ nReg + len - TAS2781_YRAM2_START_REG;
+ nResult = 1;
+ }
+ }
+ } else
+ nResult = 0;
+ } else
+ nResult = 0;
+
+ return nResult;
+}
+
+static int isYRAM(struct tasdevice_priv *pTAS2781, struct TYCRC *pCRCData,
+ unsigned char nBook, unsigned char nPage,
+ unsigned char nReg, unsigned char len)
+{
+ int nResult = 0;
+
+ nResult = isInPageYRAM(pTAS2781, pCRCData, nBook, nPage, nReg, len);
+ if (nResult == 0)
+ nResult = isInBlockYRAM(pTAS2781, pCRCData, nBook,
+ nPage, nReg, len);
+
+ return nResult;
+}
+
+/*
+ * crc8-calculate a crc8 over the given input data.
+ *
+ * table: crc table used for calculation.
+ * pdata: pointer to data buffer.
+ * nbytes: number of bytes in data buffer.
+ * crc: previous returned crc8 value.
+ */
+static u8 ti_crc8(const u8 table[CRC8_TABLE_SIZE], u8 *pdata,
+ size_t nbytes, u8 crc)
+{
+ /*loop over the buffer data */
+ while (nbytes-- > 0)
+ crc = table[(crc ^ *(pdata += 1)) & 0xff];
+
+ return crc;
+}
+
+static int doSingleRegCheckSum(struct tasdevice_priv *pTAS2781,
+ enum channel chl,
+ unsigned char nBook, unsigned char nPage,
+ unsigned char nReg, unsigned char nValue)
+{
+ int nResult = 0;
+ struct TYCRC sCRCData;
+ unsigned int nData1 = 0;
+
+ if ((nBook == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (nPage == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (nReg >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+ && (nReg <= (TASDEVICE_PAGE_REG(
+ TAS2781_SA_COEFF_SWAP_REG) + 4))) {
+ /*DSP swap command, pass */
+ nResult = 0;
+ goto end;
+ }
+
+ nResult = isYRAM(pTAS2781, &sCRCData, nBook, nPage, nReg, 1);
+ if (nResult == 1) {
+ nResult = pTAS2781->read(pTAS2781, chl,
+ TASDEVICE_REG(nBook, nPage, nReg), &nData1);
+ if (nResult < 0)
+ goto end;
+
+ if (nData1 != nValue) {
+ dev_err(pTAS2781->dev,
+ "error2, B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n",
+ nBook, nPage, nReg,
+ nValue, nData1);
+ nResult = -EAGAIN;
+ pTAS2781->tasdevice[chl].mnErrCode |=
+ ERROR_YRAM_CRCCHK;
+ goto end;
+ }
+
+ if (nData1 != nValue) {
+ dev_err(pTAS2781->dev,
+ "error2, B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n",
+ nBook, nPage, nReg,
+ nValue, nData1);
+ nResult = -EAGAIN;
+ goto end;
+ }
+
+ nResult = ti_crc8(crc8_lookup_table, &nValue, 1, 0);
+ }
+
+end:
+ return nResult;
+}
+
+static int doMultiRegCheckSum(struct tasdevice_priv *pTAS2781,
+ enum channel chn, unsigned char nBook, unsigned char nPage,
+ unsigned char nReg, unsigned int len)
+{
+ int nResult = 0, i = 0;
+ unsigned char nCRCChkSum = 0;
+ unsigned char nBuf1[128] = {0};
+ struct TYCRC TCRCData;
+
+ if ((nReg + len-1) > 127) {
+ nResult = -EINVAL;
+ dev_err(pTAS2781->dev, "firmware error\n");
+ goto end;
+ }
+
+ if ((nBook == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (nPage == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (nReg == TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+ && (len == 4)) {
+ /*DSP swap command, pass */
+ nResult = 0;
+ goto end;
+ }
+
+ nResult = isYRAM(pTAS2781, &TCRCData, nBook, nPage, nReg, len);
+ dev_info(pTAS2781->dev,
+ "isYRAM: nBook 0x%x, nPage 0x%x, nReg 0x%x\n",
+ nBook, nPage, nReg);
+ dev_info(pTAS2781->dev,
+ "isYRAM: TCRCData.mnLen 0x%x, len 0x%x, nResult %d\n",
+ TCRCData.mnLen, len, nResult);
+ dev_info(pTAS2781->dev, "TCRCData.mnOffset %x\n", TCRCData.mnOffset);
+ if (nResult == 1) {
+ if (len == 1) {
+ dev_err(pTAS2781->dev, "firmware error\n");
+ nResult = -EINVAL;
+ goto end;
+ } else {
+ nResult = pTAS2781->bulk_read(pTAS2781, chn,
+ TASDEVICE_REG(nBook, nPage, TCRCData.mnOffset),
+ nBuf1, TCRCData.mnLen);
+ if (nResult < 0)
+ goto end;
+
+ for (i = 0; i < TCRCData.mnLen; i++) {
+ if ((nBook == TASDEVICE_BOOK_ID(
+ TAS2781_SA_COEFF_SWAP_REG))
+ && (nPage == TASDEVICE_PAGE_ID(
+ TAS2781_SA_COEFF_SWAP_REG))
+ && ((i + TCRCData.mnOffset)
+ >= TASDEVICE_PAGE_REG(
+ TAS2781_SA_COEFF_SWAP_REG))
+ && ((i + TCRCData.mnOffset)
+ <= (TASDEVICE_PAGE_REG(
+ TAS2781_SA_COEFF_SWAP_REG)
+ + 4))) {
+ /*DSP swap command, bypass */
+ continue;
+ } else
+ nCRCChkSum +=
+ ti_crc8(crc8_lookup_table, &nBuf1[i],
+ 1, 0);
+ }
+
+ nResult = nCRCChkSum;
+ }
+ }
+
+end:
+ return nResult;
+}
+
+static int tasdevice_load_block(struct tasdevice_priv *tas_dev,
+ struct TBlock *pBlock)
+{
+ int nResult = 0;
+ unsigned int nCommand = 0;
+ unsigned char nBook = 0;
+ unsigned char nPage = 0;
+ unsigned char nOffset = 0;
+ unsigned char nData = 0;
+ unsigned int nLength = 0;
+ unsigned int nSleep = 0;
+ unsigned char nCRCChkSum = 0;
+ unsigned int nValue = 0;
+ int nRetry = 6;
+ unsigned char *pData = pBlock->mpData;
+ int chn = 0, chnend = 0;
+
+ dev_info(tas_dev->dev,
+ "TAS2781 load block: Type = %d, commands = %d\n",
+ pBlock->mnType, pBlock->mnCommands);
+ switch (pBlock->mnType) {
+ 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_info(tas_dev->dev,
+ "TAS2781 load block: Other Type = 0x%02x\n",
+ pBlock->mnType);
+ break;
+ }
+
+ for (; chn < chnend; chn++) {
+ if (tas_dev->tasdevice[chn].bLoading == false)
+ continue;
+start:
+ if (pBlock->mbPChkSumPresent) {
+ nResult = tas_dev->write(tas_dev, chn,
+ TASDEVICE_I2CChecksum, 0);
+ if (nResult < 0)
+ goto end;
+ }
+
+ if (pBlock->mbYChkSumPresent)
+ nCRCChkSum = 0;
+
+ nCommand = 0;
+
+ while (nCommand < pBlock->mnCommands) {
+ pData = pBlock->mpData + nCommand * 4;
+
+ nBook = pData[0];
+ nPage = pData[1];
+ nOffset = pData[2];
+ nData = pData[3];
+
+ nCommand++;
+
+ if (nOffset <= 0x7F) {
+ nResult = tas_dev->write(tas_dev, chn,
+ TASDEVICE_REG(nBook, nPage, nOffset),
+ nData);
+ if (nResult < 0)
+ goto end;
+ if (pBlock->mbYChkSumPresent) {
+ nResult = doSingleRegCheckSum(tas_dev,
+ chn, nBook, nPage, nOffset,
+ nData);
+ if (nResult < 0)
+ goto check;
+ nCRCChkSum += (unsigned char)nResult;
+ }
+ } else if (nOffset == 0x81) {
+ nSleep = (nBook << 8) + nPage;
+ msleep(nSleep);
+ } else if (nOffset == 0x85) {
+ pData += 4;
+ nLength = (nBook << 8) + nPage;
+ nBook = pData[0];
+ nPage = pData[1];
+ nOffset = pData[2];
+ if (nLength > 1) {
+ nResult = tas_dev->bulk_write(tas_dev,
+ chn, TASDEVICE_REG(nBook,
+ nPage, nOffset), pData + 3,
+ nLength);
+ if (nResult < 0)
+ goto end;
+ if (pBlock->mbYChkSumPresent) {
+ nResult = doMultiRegCheckSum(
+ tas_dev, chn, nBook,
+ nPage, nOffset,
+ nLength);
+ if (nResult < 0)
+ goto check;
+ nCRCChkSum +=
+ (unsigned char)nResult;
+ }
+ } else {
+ nResult = tas_dev->write(tas_dev, chn,
+ TASDEVICE_REG(nBook, nPage,
+ nOffset),
+ pData[3]);
+ if (nResult < 0)
+ goto end;
+ if (pBlock->mbYChkSumPresent) {
+ nResult = doSingleRegCheckSum(
+ tas_dev, chn, nBook,
+ nPage, nOffset,
+ pData[3]);
+ if (nResult < 0)
+ goto check;
+ nCRCChkSum +=
+ (unsigned char)nResult;
+ }
+ }
+
+ nCommand++;
+ if (nLength >= 2)
+ nCommand += ((nLength - 2) / 4) + 1;
+ }
+ }
+ if (pBlock->mbPChkSumPresent) {
+ nResult = tas_dev->read(tas_dev, chn,
+ TASDEVICE_I2CChecksum, &nValue);
+ if (nResult < 0) {
+ dev_err(tas_dev->dev, "%s: Channel %d\n",
+ __func__, chn);
+ goto check;
+ }
+ if ((nValue&0xff) != pBlock->mnPChkSum) {
+ dev_err(tas_dev->dev,
+ "Block PChkSum Channel %d Error: FW = 0x%x, Reg = 0x%x\n",
+ chn, pBlock->mnPChkSum, (nValue&0xff));
+ nResult = -EAGAIN;
+ tas_dev->tasdevice[chn].mnErrCode |=
+ ERROR_PRAM_CRCCHK;
+ goto check;
+ }
+ nResult = 0;
+ tas_dev->tasdevice[chn].mnErrCode &=
+ ~ERROR_PRAM_CRCCHK;
+ dev_info(tas_dev->dev, "Block[0x%02x] PChkSum match\n",
+ pBlock->mnType);
+ }
+
+ if (pBlock->mbYChkSumPresent) {
+ //TBD, open it when FW ready
+ dev_err(tas_dev->dev, "Block YChkSum: FW = 0x%x, YCRC = 0x%x\n",
+ pBlock->mnYChkSum,
+ nCRCChkSum);
+
+ tas_dev->tasdevice[chn].mnErrCode &=
+ ~ERROR_YRAM_CRCCHK;
+ nResult = 0;
+ dev_info(tas_dev->dev,
+ "Block[0x%x] YChkSum match\n", pBlock->mnType);
+ }
+check:
+ if (nResult == -EAGAIN) {
+ nRetry--;
+ if (nRetry > 0)
+ goto start;
+ else {
+ if ((pBlock->mnType == MAIN_ALL_DEVICES)
+ || (pBlock->mnType == MAIN_DEVICE_A)
+ || (pBlock->mnType == MAIN_DEVICE_B)
+ || (pBlock->mnType == MAIN_DEVICE_C)
+ || (pBlock->mnType == MAIN_DEVICE_D)) {
+ tas_dev->tasdevice[chn].mnCurrentProgram
+ = -1;
+ } else {
+ tas_dev->tasdevice[chn].mnCurrentConfiguration
+ = -1;
+ }
+ nRetry = 6;
+ }
+ }
+ }
+end:
+ if (nResult < 0) {
+ dev_err(tas_dev->dev, "Block (%d) load error\n",
+ pBlock->mnType);
+ }
+ return nResult;
+}
+
+
+static int tasdevice_load_data(struct tasdevice_priv *tas_dev,
+ struct TData *pData)
+{
+ int nResult = 0;
+ unsigned int nBlock = 0;
+ struct TBlock *pBlock = NULL;
+
+ dev_info(tas_dev->dev, "%s: TAS2781 load data: %s, Blocks = %d\n",
+ __func__,
+ pData->mpName, pData->mnBlocks);
+
+ for (nBlock = 0; nBlock < pData->mnBlocks; nBlock++) {
+ pBlock = &(pData->mpBlocks[nBlock]);
+ nResult = tas_dev->tasdevice_load_block(tas_dev, pBlock);
+ if (nResult < 0)
+ break;
+ }
+
+ return nResult;
+}
+
+static int tasdevice_load_calibrated_data(
+ struct tasdevice_priv *tas_dev, struct TData *pData)
+{
+ int nResult = 0;
+ unsigned int nBlock = 0;
+ struct TBlock *pBlock = NULL;
+
+ dev_info(tas_dev->dev, "%s: TAS2781 load data: %s, Blocks = %d\n",
+ __func__,
+ pData->mpName, pData->mnBlocks);
+
+ for (nBlock = 0; nBlock < pData->mnBlocks; nBlock++) {
+ pBlock = &(pData->mpBlocks[nBlock]);
+ nResult = tasdevice_load_block(tas_dev, pBlock);
+ if (nResult < 0)
+ break;
+ }
+
+ return nResult;
+}
+
+int tas2781_load_calibration(void *pContext,
+ char *pFileName, enum channel i)
+{
+ int ret = 0, nSize = 0, offset = 0;
+ loff_t pos = 0;
+ struct file *filp = NULL;
+ struct firmware FW;
+ const struct firmware *fw_entry = NULL;
+ char *data = NULL;
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *)pContext;
+ struct Ttasdevice *pTasdev = &(tas_dev->tasdevice[i]);
+ struct TFirmware *mpCalFirmware = NULL;
+ char pHint[256];
+
+ ret = request_firmware(&fw_entry, pFileName, tas_dev->dev);
+ if (!ret) {
+ if (!fw_entry->size) {
+ dev_err(tas_dev->dev,
+ "%s: file read error: size = %d\n",
+ __func__, fw_entry->size);
+ goto out;
+ }
+ FW.size = fw_entry->size;
+ FW.data = fw_entry->data;
+ dev_info(tas_dev->dev,
+ "%s: file = %s, file size %zd\n",
+ __func__, pFileName, fw_entry->size);
+ } else {
+ dev_info(tas_dev->dev,
+ "%s: Request firmware failed, try flip_open()\n",
+ __func__);
+
+ scnprintf(pHint, sizeof(pHint), "%s%s\n",
+ TAS2781_CAL_BIN_PATH, pFileName);
+ filp = filp_open(pHint, O_RDONLY, 664);
+ if (!IS_ERR_OR_NULL(filp)) {
+ FW.size = i_size_read(file_inode(filp));
+ dev_info(tas_dev->dev,
+ "%s: file = %s, file size %ld\n",
+ __func__, pHint, (long)FW.size);
+ data = kmalloc(FW.size, GFP_KERNEL);
+ if (data == NULL) {
+ dev_err(tas_dev->dev, "%s: malloc error\n",
+ __func__);
+ goto out;
+ }
+ nSize = (int)kernel_read(filp, data, FW.size, &pos);
+ if (!nSize) {
+ dev_err(tas_dev->dev,
+ "%s: file read error: size = %d\n",
+ __func__, nSize);
+ goto out;
+ }
+ dev_info(tas_dev->dev, "read filed nSize = %d\n",
+ nSize);
+ FW.data = data;
+ } else {
+ dev_err(tas_dev->dev,
+ "%s: cannot open calibration file: %s\n",
+ __func__, pHint);
+ goto out;
+ }
+ }
+
+ mpCalFirmware = pTasdev->mpCalFirmware = kcalloc(1,
+ sizeof(struct TFirmware), GFP_KERNEL);
+ if (pTasdev->mpCalFirmware == NULL) {
+ dev_err(tas_dev->dev, "%s: FW memory failed!\n", __func__);
+ ret = -1;
+ goto out;
+ }
+ mpCalFirmware->dev = tas_dev->dev;
+ offset = fw_parse_header(tas_dev, mpCalFirmware, &FW, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev, "%s: fw_parse_header EXIT!\n", __func__);
+ goto out;
+ }
+ offset = fw_parse_variable_header_cal(tas_dev, mpCalFirmware, &FW,
+ 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, mpCalFirmware, &FW, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev, "%s: fw_parse_program_data EXIT!\n",
+ __func__);
+ goto out;
+ }
+ offset = fw_parse_configuration_data(tas_dev, mpCalFirmware, &FW,
+ offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev,
+ "%s: fw_parse_configuration_data EXIT!\n", __func__);
+ goto out;
+ }
+ offset = fw_parse_calibration_data(tas_dev,
+ mpCalFirmware, &FW, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev, "%s: fw_parse_calibration_data EXIT!\n",
+ __func__);
+ goto out;
+ }
+ pTasdev->mbCalibrationLoaded = true;
+out:
+ if (!IS_ERR_OR_NULL(filp)) {
+ filp_close(filp, NULL);
+ kfree(data);
+ }
+ if (fw_entry) {
+ release_firmware(fw_entry);
+ fw_entry = NULL;
+ }
+ return ret;
+}
+
+int tasdevice_dspfw_ready(const void *pVoid, void *pContext)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) pContext;
+ const struct firmware *pFW = (const struct firmware *)pVoid;
+ struct TFirmware *pFirmware = NULL;
+ struct tasdevice_fw_fixed_hdr *pFw_fixed_hdr = NULL;
+ int offset = 0, ret = 0;
+
+ if (!pFW || !pFW->data) {
+ dev_err(tas_dev->dev, "%s: Failed to read firmware %s\n",
+ __func__, tas_dev->dsp_binaryname);
+ ret = -1;
+ goto out;
+ }
+
+ tas_dev->mpFirmware = kcalloc(1,
+ sizeof(struct TFirmware), GFP_KERNEL);
+ if (tas_dev->mpFirmware == NULL) {
+ ret = -1;
+ goto out;
+ }
+ pFirmware = tas_dev->mpFirmware;
+ pFirmware->dev = tas_dev->dev;
+ offset = fw_parse_header(tas_dev, pFirmware, pFW, offset);
+
+ if (offset == -1)
+ goto out;
+ pFw_fixed_hdr = &(pFirmware->fw_hdr.mnFixedHdr);
+ switch (pFw_fixed_hdr->mnDriverVersion) {
+ 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;
+ pFirmware->bKernelFormat = true;
+ 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;
+ pFirmware->bKernelFormat = false;
+ break;
+ default:
+ if (pFw_fixed_hdr->mnDriverVersion == 0x100) {
+ if (pFw_fixed_hdr->mnPPCVersion >= 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 (pFw_fixed_hdr->mnPPCVersion) {
+ 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 Current:0x%02x\n",
+ __func__, PPC3_VERSION,
+ pFw_fixed_hdr->mnPPCVersion);
+ offset = -1;
+ break;
+ }
+ }
+ } else {
+ dev_err(tas_dev->dev,
+ "%s: DriverVersion must be 0x0, 0x230 or above 0x230:0x%02x\n",
+ __func__,
+ pFw_fixed_hdr->mnDriverVersion);
+ offset = -1;
+ }
+ break;
+ }
+
+ offset = tas_dev->fw_parse_variable_header(tas_dev, pFW, offset);
+ if (offset == -1)
+ goto out;
+
+ offset = tas_dev->fw_parse_program_data(tas_dev, pFirmware, pFW,
+ offset);
+ if (offset < 0) {
+ ret = -1;
+ goto out;
+ }
+ offset = tas_dev->fw_parse_configuration_data(tas_dev,
+ pFirmware, pFW, offset);
+ if (offset < 0)
+ ret = -1;
+
+out:
+ return ret;
+}
+
+void tasdevice_calbin_remove(void *pContext)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) pContext;
+ struct Ttasdevice *pTasdev = NULL;
+ int i = 0;
+
+ if (tas_dev) {
+ for (i = 0; i < tas_dev->ndev; i++) {
+ pTasdev = &(tas_dev->tasdevice[i]);
+ if (pTasdev->mpCalFirmware) {
+ tas2781_clear_Calfirmware(
+ pTasdev->mpCalFirmware);
+ pTasdev->mpCalFirmware = NULL;
+ }
+ }
+ }
+}
+
+void tasdevice_dsp_remove(void *pContext)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) pContext;
+ int i = 0;
+
+ if (tas_dev) {
+ if (tas_dev->mpFirmware) {
+ struct TFirmware *pFirmware = tas_dev->mpFirmware;
+
+ if (pFirmware->mpPrograms) {
+ struct TProgram *pProgram;
+
+ for (i = 0; i < pFirmware->mnPrograms; i++) {
+ pProgram = &(pFirmware->mpPrograms[i]);
+ if (pProgram) {
+ struct TData *pImageData =
+ &(pProgram->mData);
+
+ if (pImageData->mpBlocks) {
+ struct TBlock *pBlock;
+ unsigned int nBlock;
+
+ for (nBlock = 0;
+ nBlock <
+ pImageData->mnBlocks;
+ nBlock++) {
+ pBlock =
+ &(pImageData->mpBlocks[nBlock]);
+ kfree(pBlock->mpData);
+ }
+ kfree(pImageData->mpBlocks);
+ }
+ kfree(pProgram->mpDescription);
+ }
+ }
+ kfree(pFirmware->mpPrograms);
+ }
+
+ if (pFirmware->mpConfigurations) {
+ struct TConfiguration *pConfig;
+
+ for (i = 0; i < pFirmware->mnConfigurations;
+ i++) {
+ pConfig =
+ &(pFirmware->mpConfigurations[i]);
+ if (pConfig) {
+ struct TData *pImageData =
+ &(pConfig->mData);
+
+ if (pImageData->mpBlocks) {
+ struct TBlock *pBlock;
+ unsigned int nBlock;
+
+ for (nBlock = 0;
+ nBlock <
+ pImageData->mnBlocks;
+ nBlock++) {
+ pBlock =
+ &(pImageData->mpBlocks[nBlock]);
+ kfree(pBlock->mpData);
+ }
+ kfree(pImageData->mpBlocks);
+ }
+ kfree(pConfig->mpDescription);
+ }
+ }
+ kfree(pFirmware->mpConfigurations);
+ }
+ kfree(pFirmware->fw_hdr.mpDescription);
+ kfree(pFirmware);
+ tas_dev->mpFirmware = NULL;
+ }
+ }
+}
+
+void tasdevice_select_tuningprm_cfg(void *pContext, int prm_no,
+ int cfg_no, int regbin_conf_no)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) pContext;
+ struct tasdevice_regbin *regbin = &(tas_dev->mtRegbin);
+ struct tasdevice_config_info **cfg_info = regbin->cfg_info;
+ struct TFirmware *pFirmware = tas_dev->mpFirmware;
+ struct TConfiguration *pConfigurations = NULL;
+ struct TProgram *pProgram = NULL;
+ int i = 0;
+ int status = 0;
+
+ if (pFirmware == NULL) {
+ dev_err(tas_dev->dev, "%s: Firmware is NULL\n", __func__);
+ goto out;
+ }
+
+ if (cfg_no >= pFirmware->mnConfigurations) {
+ dev_err(tas_dev->dev,
+ "%s: cfg(%d) is not in range of conf %u\n",
+ __func__, cfg_no, pFirmware->mnConfigurations);
+ goto out;
+ }
+
+ if (prm_no >= pFirmware->mnPrograms || prm_no == 1) {
+ dev_err(tas_dev->dev,
+ "%s: prm(%d) is not in range of Programs %u\n",
+ __func__, prm_no, pFirmware->mnPrograms);
+ goto out;
+ }
+
+ if (regbin_conf_no > regbin->ncfgs || regbin_conf_no < 0 ||
+ cfg_info == NULL) {
+ dev_err(tas_dev->dev,
+ "conf_no:%d should be in range from 0 to %u\n",
+ regbin_conf_no, regbin->ncfgs-1);
+ goto out;
+ } else {
+ dev_info(tas_dev->dev, "%s: regbin_profile_conf_id = %d\n",
+ __func__, regbin_conf_no);
+ }
+
+ tas_dev->mnCurrentConfiguration = cfg_no;
+ tas_dev->mnCurrentProgram = prm_no;
+
+ pConfigurations = &(pFirmware->mpConfigurations[cfg_no]);
+ for (i = 0; i < tas_dev->ndev; i++) {
+ if (cfg_info[regbin_conf_no]->active_dev & (1 << i)) {
+ if (tas_dev->tasdevice[i].mnCurrentProgram != prm_no) {
+ tas_dev->tasdevice[i].mnCurrentConfiguration
+ = -1;
+ tas_dev->tasdevice[i].bLoading = true;
+ status++;
+ }
+ } else
+ tas_dev->tasdevice[i].bLoading = false;
+ tas_dev->tasdevice[i].bLoaderr = false;
+ }
+
+ if (status) {
+ pProgram = &(pFirmware->mpPrograms[prm_no]);
+ tasdevice_load_data(tas_dev, &(pProgram->mData));
+ for (i = 0; i < tas_dev->ndev; i++) {
+ if (tas_dev->tasdevice[i].bLoaderr == true)
+ continue;
+ else if (tas_dev->tasdevice[i].bLoaderr == false
+ && tas_dev->tasdevice[i].bLoading == true) {
+ struct TFirmware *pCalFirmware =
+ tas_dev->tasdevice[i].mpCalFirmware;
+
+ if (pCalFirmware) {
+ struct TCalibration *pCalibration =
+ pCalFirmware->mpCalibrations;
+
+ if (pCalibration)
+ tasdevice_load_calibrated_data(
+ tas_dev,
+ &(pCalibration->mData));
+ }
+ tas_dev->tasdevice[i].mnCurrentProgram
+ = prm_no;
+ }
+ }
+ }
+
+ if (tas_dev->mbCalibrationLoaded == false) {
+ for (i = 0; i < tas_dev->ndev; i++)
+ tas_dev->set_calibration(tas_dev, i, 0x100);
+ tas_dev->mbCalibrationLoaded = true;
+ /* No wise to reload calibrationdata everytime,
+ * this code will work once even if calibrated
+ * data still failed to be got
+ */
+ }
+
+ status = 0;
+ for (i = 0; i < tas_dev->ndev; i++) {
+ dev_info(tas_dev->dev, "%s,fun %d,%d,%d\n", __func__,
+ tas_dev->tasdevice[i].mnCurrentConfiguration,
+ cfg_info[regbin_conf_no]->active_dev,
+ tas_dev->tasdevice[i].bLoaderr);
+ if (tas_dev->tasdevice[i].mnCurrentConfiguration != cfg_no
+ && (cfg_info[regbin_conf_no]->active_dev & (1 << i))
+ && (tas_dev->tasdevice[i].bLoaderr == false)) {
+ status++;
+ tas_dev->tasdevice[i].bLoading = true;
+ } else
+ tas_dev->tasdevice[i].bLoading = false;
+ }
+
+ if (status) {
+ status = 0;
+ tasdevice_load_data(tas_dev, &(pConfigurations->mData));
+ for (i = 0; i < tas_dev->ndev; i++) {
+ if (tas_dev->tasdevice[i].bLoaderr == true) {
+ status |= 1 << (i + 4);
+ continue;
+ } else if (tas_dev->tasdevice[i].bLoaderr == false
+ && tas_dev->tasdevice[i].bLoading == true) {
+ tas_dev->tasdevice[i].mnCurrentConfiguration
+ = cfg_no;
+ tas_dev->tasdevice[i].bDSPBypass = false;
+ }
+ }
+ } else {
+ dev_err(tas_dev->dev,
+ "%s: No device is in active in conf %d\n",
+ __func__, regbin_conf_no);
+ }
+
+ status |= cfg_info[regbin_conf_no]->active_dev;
+ dev_info(tas_dev->dev, "%s: DSP mode: load status is %08x\n",
+ __func__, status);
+out:
+ return;
+}
+
+int tas2781_set_calibration(void *pContext, enum channel i,
+ int nCalibration)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) pContext;
+ int nResult = 0;
+ struct Ttasdevice *pTasdev = &(tas_dev->tasdevice[i]);
+ struct TFirmware *pCalFirmware = pTasdev->mpCalFirmware;
+
+ if ((!tas_dev->mpFirmware->mpPrograms)
+ || (!tas_dev->mpFirmware->mpConfigurations)) {
+ dev_err(tas_dev->dev, "%s, Firmware not loaded\n\r", __func__);
+ nResult = 0;
+ goto out;
+ }
+
+ if (nCalibration == 0xFF || (nCalibration == 0x100
+ && pTasdev->mbCalibrationLoaded == false)) {
+ if (pCalFirmware) {
+ pTasdev->mbCalibrationLoaded = false;
+ tas2781_clear_Calfirmware(pCalFirmware);
+ pCalFirmware = NULL;
+ }
+
+ scnprintf(tas_dev->cal_binaryname[i], 64, "%s_cal_0x%02x.bin",
+ tas_dev->dev_name, tas_dev->tasdevice[i].mnDevAddr);
+ nResult = tas2781_load_calibration(tas_dev,
+ tas_dev->cal_binaryname[i], i);
+ if (nResult != 0) {
+ dev_err(tas_dev->dev,
+ "%s: load %s error, no-side effect for playback\n",
+ __func__, tas_dev->cal_binaryname[i]);
+ nResult = 0;
+ }
+ }
+ pTasdev->bLoading = true;
+ pTasdev->bLoaderr = false;
+
+ if (pCalFirmware) {
+ struct TCalibration *pCalibration =
+ pCalFirmware->mpCalibrations;
+
+ if (pCalibration)
+ tasdevice_load_calibrated_data(tas_dev,
+ &(pCalibration->mData));
+ } else
+ dev_err(tas_dev->dev,
+ "%s: No calibrated data for device %d\n", __func__, i);
+
+out:
+ return nResult;
+}
diff --git a/sound/soc/codecs/tas2781-dsp.h b/sound/soc/codecs/tas2781-dsp.h
new file mode 100644
index 000000000..f7b689ad6
--- /dev/null
+++ b/sound/soc/codecs/tas2781-dsp.h
@@ -0,0 +1,213 @@
+/* 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 register setting
+ * for one, two, even multiple TAS2781 chips. All the register setting are in
+ * a bin file. Almost no specific register setting can be found in the code.
+ *
+ * Author: Shenghao Ding <shenghao-ding(a)ti.com>
+ * 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 REGBIN_CONFIGID_BYPASS_ALL (0)
+#define TASDEVICE_DEVICE_SUM (8)
+#define TASDEVICE_CONFIG_SUM (64)
+
+enum channel {
+ TopLeftChn = 0x00,
+ TopRightChn = 0x01,
+ BottomLeftChn = 0x02,
+ BottomRightChn = 0x03,
+ MaxChn,
+};
+
+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 mnMagicNumber;
+ unsigned int mnFWSize;
+ unsigned int mnChecksum;
+ unsigned int mnPPCVersion;
+ unsigned int mnFWVersion;
+ unsigned int mnDriverVersion;
+ unsigned int mnTimeStamp;
+ char mpDDCName[64];
+};
+
+struct tasdevice_dspfw_hdr {
+ struct tasdevice_fw_fixed_hdr mnFixedHdr;
+ unsigned int mnBinFileDocVer;
+ char *mpDescription;
+ unsigned short mnDeviceFamily;
+ unsigned short mnDevice;
+ unsigned char ndev;
+};
+
+struct TBlock {
+ unsigned int mnType;
+ unsigned char mbPChkSumPresent;
+ unsigned char mnPChkSum;
+ unsigned char mbYChkSumPresent;
+ unsigned char mnYChkSum;
+ unsigned int mnCommands;
+ unsigned int blk_size;
+ unsigned int nSublocks;
+ unsigned char *mpData;
+};
+
+struct TData {
+ char mpName[64];
+ char *mpDescription;
+ unsigned int mnBlocks;
+ struct TBlock *mpBlocks;
+};
+struct TProgram {
+ unsigned int prog_size;
+ char mpName[64];
+ char *mpDescription;
+ unsigned char mnAppMode;
+ unsigned char mnPDMI2SMode;
+ unsigned char mnISnsPD;
+ unsigned char mnVSnsPD;
+ unsigned char mnPowerLDG;
+ struct TData mData;
+};
+
+struct TConfiguration {
+ unsigned int cfg_size;
+ char mpName[64];
+ char *mpDescription;
+ unsigned char mnDevice_orientation;
+ unsigned char mnDevices;
+ unsigned int mProgram;
+ unsigned int mnSamplingRate;
+ unsigned short mnPLLSrc;
+ unsigned int mnPLLSrcRate;
+ unsigned int mnFsRate;
+ struct TData mData;
+};
+
+struct TCalibration {
+ char mpName[64];
+ char *mpDescription;
+ unsigned int mnProgram;
+ unsigned int mnConfiguration;
+ struct TData mData;
+};
+
+struct TFirmware {
+ struct tasdevice_dspfw_hdr fw_hdr;
+ unsigned int prog_start_offset;
+ unsigned short mnPrograms;
+ struct TProgram *mpPrograms;
+ unsigned int cfg_start_offset;
+ unsigned short mnConfigurations;
+ struct TConfiguration *mpConfigurations;
+ unsigned short mnCalibrations;
+ struct TCalibration *mpCalibrations;
+ bool bKernelFormat;
+ 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_regbin_hdr {
+ unsigned int img_sz;
+ unsigned int checksum;
+ unsigned int binary_version_num;
+ unsigned int drv_fw_version;
+ unsigned int timestamp;
+ 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 tasdevice_block_data {
+ unsigned char dev_idx;
+ unsigned char block_type;
+ unsigned short yram_checksum;
+ unsigned int block_size;
+ unsigned int nSublocks;
+ unsigned char *regdata;
+};
+
+struct tasdevice_config_info {
+ char mpName[64];
+ unsigned int nblocks;
+ unsigned int real_nblocks;
+ unsigned char active_dev;
+ struct tasdevice_block_data **blk_data;
+};
+
+struct tasdevice_regbin {
+ struct tasdevice_regbin_hdr fw_hdr;
+ int ncfgs;
+ struct tasdevice_config_info **cfg_info;
+ int profile_cfg_id;
+};
+
+int tasdevice_dspfw_ready(const void *pVoid, void *pContext);
+void tasdevice_dsp_remove(void *pContext);
+void tasdevice_calbin_remove(void *pContext);
+int tas2781_load_calibration(void *tas_dev, char *pFileName,
+ enum channel i);
+int tas2781_set_calibration(void *pContext, enum channel i,
+ int nCalibration);
+void tasdevice_select_tuningprm_cfg(void *pContext, int prm,
+ int cfg_no, int regbin_conf_no);
+
+#endif
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
new file mode 100644
index 000000000..3ee254777
--- /dev/null
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -0,0 +1,2143 @@
+// 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 register setting
+ * for one, two, even multiple TAS2781 chips. All the register setting are in
+ * a bin file. Almost no specific register setting can be found in the code.
+ *
+ * Author: Shenghao Ding <shenghao-ding(a)ti.com>
+ * Kevin Lu <kevin-lu(a)ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/of_gpio.h>
+#include <linux/interrupt.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include "tas2781.h"
+
+/* max. length of a alsa mixer control name */
+#define MAX_CONTROL_NAME (48)
+#define TASDEVICE_CLK_DIR_IN (0)
+#define TASDEVICE_CLK_DIR_OUT (1)
+
+#define TASDEVICE_IRQ_DET_TIMEOUT (30000)
+#define TASDEVICE_IRQ_DET_CNT_LIMIT (500)
+#define TAS2781_REG_INT_LTCH0 TASDEVICE_REG(0X0, 0x0, 0x49)
+#define TAS2781_REG_INT_LTCH1 TASDEVICE_REG(0X0, 0x0, 0x4A)
+#define TAS2781_REG_INT_LTCH1_0 TASDEVICE_REG(0X0, 0x0, 0x4B)
+#define TAS2781_REG_INT_LTCH2 TASDEVICE_REG(0X0, 0x0, 0x4F)
+#define TAS2781_REG_INT_LTCH3 TASDEVICE_REG(0X0, 0x0, 0x50)
+#define TAS2781_REG_INT_LTCH4 TASDEVICE_REG(0X0, 0x0, 0x51)
+
+const char *blocktype[5] = {
+ "COEFF",
+ "POST_POWER_UP",
+ "PRE_SHUTDOWN",
+ "PRE_POWER_UP",
+ "POST_SHUTDOWN"
+};
+
+static const char * const dts_dev_addr_tag[] = {
+ "ti,topleft-channel",
+ "ti,topright-channel",
+ "ti,bottomleft-channel",
+ "ti,bottomright-channel"
+};
+
+static const char *dts_rst_tag = "ti,%s-gpio%d";
+
+static const char *dts_glb_addr_tag = "ti,global-address";
+
+static const struct i2c_device_id tasdevice_id[] = {
+ { "audev", GENERAL_AUDEV },
+ { "tas2781", TAS2781 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tasdevice_id);
+
+static bool tasdevice_volatile(struct device *dev, unsigned int reg)
+{
+ return true;
+}
+
+static bool tasdevice_writeable(struct device *dev, unsigned int reg)
+{
+ return true;
+}
+
+static const struct regmap_config tasdevice_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = tasdevice_writeable,
+ .volatile_reg = tasdevice_volatile,
+ .cache_type = REGCACHE_FLAT,
+ .max_register = 1 * 128,
+};
+
+static const struct of_device_id tasdevice_of_match[] = {
+ { .compatible = "ti,audev" },
+ { .compatible = "ti,tas2781" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, tasdevice_of_match);
+
+int tasdevice_process_block(void *pContext,
+ unsigned char *data, unsigned char dev_idx, int sublocksize)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *)pContext;
+ unsigned char subblk_typ = data[1];
+ int subblk_offset = 2;
+ int chn = 0, chnend = 0;
+ int rc = 0;
+ int blktyp = dev_idx & 0xC0, idx = dev_idx & 0x3F;
+ bool bError = false;
+
+ if (idx) {
+ chn = idx-1;
+ chnend = idx;
+ } else {
+ if (tas_dev->glb_addr.mnDevAddr) {
+ chn = tas_dev->ndev;
+ chnend = tas_dev->ndev + 1;
+ } else {
+ chn = 0;
+ chnend = tas_dev->ndev;
+ }
+ }
+
+ for (; chn < chnend; chn++) {
+ if (tas_dev->glb_addr.mnDevAddr == 0 &&
+ tas_dev->tasdevice[chn].bLoading == false)
+ continue;
+
+ bError = 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 memory\n");
+ bError = 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) {
+ bError = 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,
+ "process_block: BURST Out of memory\n");
+ bError = true;
+ break;
+ }
+ if (len % 4) {
+ dev_err(tas_dev->dev,
+ "process_block: Burst len(%u) can be divided by 4\n",
+ 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) {
+ bError = true;
+ dev_err(tas_dev->dev,
+ "process_block: bulk_write error = %d\n",
+ 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,
+ "process_block: deley Out of memory\n");
+ bError = 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,
+ "process_block: bit write Out of memory\n");
+ bError = 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) {
+ bError = true;
+ dev_err(tas_dev->dev,
+ "process_block: update_bits error = %d\n", rc);
+ }
+ subblk_offset += 6;
+ break;
+ default:
+ break;
+ };
+ if (bError == true && blktyp != 0) {
+ if (blktyp == 0x80) {
+ tas_dev->tasdevice[chn].mnCurrentProgram = -1;
+ tas_dev->tasdevice[chn].mnCurrentConfiguration =
+ -1;
+ } else
+ tas_dev->tasdevice[chn].mnCurrentConfiguration =
+ -1;
+ }
+ }
+ return subblk_offset;
+}
+
+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_regbin *regbin = &(tas_dev->mtRegbin);
+ struct tasdevice_config_info **cfg_info = regbin->cfg_info;
+ int j = 0, k = 0, chn = 0, chnend = 0;
+
+ if (conf_no >= regbin->ncfgs || conf_no < 0 || NULL == cfg_info) {
+ dev_err(tas_dev->dev,
+ "conf_no should be not more than %u\n",
+ regbin->ncfgs);
+ goto out;
+ } else
+ dev_info(tas_dev->dev,
+ "select_cfg_blk: profile_conf_id = %d\n",
+ conf_no);
+
+ 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;
+ dev_info(tas_dev->dev,
+ "select_cfg_blk: conf %d, block type:%s\t device idx = 0x%02x\n",
+ conf_no, blocktype[cfg_info[conf_no]->blk_data[j]
+ ->block_type-1], cfg_info[conf_no]->blk_data[j]
+ ->dev_idx);
+
+ for (k = 0; k < (int)cfg_info[conf_no]->blk_data[j]
+ ->nSublocks; 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].bLoading = 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,
+ "select_cfg_blk: ERROR:%u %u out of memory\n",
+ 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,
+ "select_cfg_blk: ERROR: %u %u size is not same\n",
+ length,
+ cfg_info[conf_no]->blk_data[j]->block_size);
+
+ }
+
+out:
+ return;
+}
+
+static struct tasdevice_config_info *tasdevice_add_config(
+ void *pContext, unsigned char *config_data,
+ unsigned int config_size)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *)pContext;
+ struct tasdevice_config_info *cfg_info = NULL;
+ int config_offset = 0, i = 0;
+
+ cfg_info = kzalloc(
+ sizeof(struct tasdevice_config_info), GFP_KERNEL);
+ if (!cfg_info)
+ goto out;
+
+ if (tas_dev->mtRegbin.fw_hdr.binary_version_num >= 0x105) {
+ if (config_offset + 64 > (int)config_size) {
+ dev_err(tas_dev->dev,
+ "add config: Out of memory\n");
+ goto out;
+ }
+ memcpy(cfg_info->mpName, &config_data[config_offset], 64);
+ config_offset += 64;
+ }
+
+ if (config_offset + 4 > (int)config_size) {
+ dev_err(tas_dev->dev,
+ "add config: Out of memory\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 tasdevice_block_data *),
+ GFP_KERNEL);
+ if (!cfg_info->blk_data) {
+ dev_err(tas_dev->dev,
+ "add config: blk_data alloc failed!\n");
+ 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 memory: i = %d nblocks = %u!\n",
+ i, cfg_info->nblocks);
+ break;
+ }
+ cfg_info->blk_data[i] = kzalloc(
+ sizeof(struct tasdevice_block_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]->nSublocks =
+ 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 == 0) {
+ dev_err(tas_dev->dev,
+ "add config: regdata alloc failed!\n");
+ goto out;
+ }
+ if (config_offset + cfg_info->blk_data[i]->block_size
+ > config_size) {
+ dev_err(tas_dev->dev,
+ "add config: block_size Out of memory: i = %d nblocks = %u!\n",
+ 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;
+}
+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;
+ unsigned int val;
+ 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;
+ }
+ val = (val > mc->max) ? mc->max : val;
+ val = mc->invert ? mc->max - val : val;
+ 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;
+ unsigned int val;
+ int i, ret = 0;
+
+ val = ucontrol->value.integer.value[0];
+ val = (val > mc->max) ? mc->max : val;
+ val = mc->invert ? mc->max - val : val;
+ val = (val < 0) ? 0 : val;
+ if (tas_dev->tas2781_set_global != NULL) {
+ ret = tasdevice_dev_write(tas_dev, tas_dev->ndev,
+ mc->reg, val);
+ if (ret)
+ dev_err(tas_dev->dev,
+ "%s, set digital vol error in global mode\n",
+ __func__);
+ } else {
+ for (i = 0; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_write(tas_dev, i,
+ mc->reg, val);
+ if (ret)
+ dev_err(tas_dev->dev,
+ "%s, set digital vol error in device %d\n",
+ __func__, i);
+ }
+ }
+
+ return ret;
+}
+
+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;
+ unsigned int val;
+ unsigned char mask = 0;
+ int ret = 0;
+
+ /* 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;
+ val = (val > mc->max) ? mc->max : val;
+ val = mc->invert ? mc->max - val : val;
+ 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;
+ unsigned int val;
+ int i, ret = 0;
+ unsigned char mask = 0;
+
+ mask = (1 << fls(mc->max)) - 1;
+ mask <<= mc->shift;
+ val = ucontrol->value.integer.value[0];
+ val = (val > mc->max) ? mc->max : val;
+ val = mc->invert ? mc->max - val : val;
+ val = (val < 0) ? 0 : val;
+ for (i = 0; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_update_bits(tas_dev, i,
+ mc->reg,
+ mask,
+ val << mc->shift);
+ if (ret)
+ dev_err(tas_dev->dev,
+ "%s, set AMP vol error in device %d\n",
+ __func__, i);
+ }
+
+ return ret;
+}
+
+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 Control", 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 *p_tasdevice =
+ snd_soc_component_get_drvdata(codec);
+
+ /* Codec Lock Hold*/
+ mutex_lock(&p_tasdevice->codec_lock);
+ p_tasdevice->mtRegbin.profile_cfg_id =
+ ucontrol->value.integer.value[0];
+ /* Codec Lock Release*/
+ mutex_unlock(&p_tasdevice->codec_lock);
+
+ return 0;
+}
+
+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 *p_tasdevice =
+ snd_soc_component_get_drvdata(codec);
+ struct TFirmware *Tfw = p_tasdevice->mpFirmware;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ /* Codec Lock Hold*/
+ mutex_lock(&p_tasdevice->codec_lock);
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = (int)Tfw->mnPrograms;
+ /* Codec Lock Release*/
+ mutex_unlock(&p_tasdevice->codec_lock);
+ 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 *p_tasdevice =
+ snd_soc_component_get_drvdata(codec);
+ struct TFirmware *Tfw = p_tasdevice->mpFirmware;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ /* Codec Lock Hold*/
+ mutex_lock(&p_tasdevice->codec_lock);
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = (int)Tfw->mnConfigurations - 1;
+ /* Codec Lock Release*/
+ mutex_unlock(&p_tasdevice->codec_lock);
+
+ 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 *p_tasdevice =
+ snd_soc_component_get_drvdata(codec);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ /* Codec Lock Hold*/
+ mutex_lock(&p_tasdevice->codec_lock);
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = max(0, p_tasdevice->mtRegbin.ncfgs);
+ /* Codec Lock Release*/
+ mutex_unlock(&p_tasdevice->codec_lock);
+ 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 *p_tasdevice
+ = snd_soc_component_get_drvdata(codec);
+
+ /* Codec Lock Hold*/
+ mutex_lock(&p_tasdevice->codec_lock);
+ ucontrol->value.integer.value[0] =
+ p_tasdevice->mtRegbin.profile_cfg_id;
+ /* Codec Lock Release*/
+ mutex_unlock(&p_tasdevice->codec_lock);
+
+ 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,
+ MAX_CONTROL_NAME, GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ scnprintf(name, MAX_CONTROL_NAME, "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);
+
+ mutex_lock(&pTAS2781->codec_lock);
+ pValue->value.integer.value[0] = pTAS2781->mnCurrentProgram;
+ mutex_unlock(&pTAS2781->codec_lock);
+ 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];
+
+ mutex_lock(&pTAS2781->codec_lock);
+ pTAS2781->mnCurrentProgram = nProgram;
+ mutex_unlock(&pTAS2781->codec_lock);
+ return 0;
+}
+
+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);
+
+ mutex_lock(&pTAS2781->codec_lock);
+ pValue->value.integer.value[0] = pTAS2781->mnCurrentConfiguration;
+ mutex_unlock(&pTAS2781->codec_lock);
+ 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 *pTAS2781 = snd_soc_component_get_drvdata(codec);
+ unsigned int nConfiguration = pValue->value.integer.value[0];
+
+ mutex_lock(&pTAS2781->codec_lock);
+ pTAS2781->mnCurrentConfiguration = nConfiguration;
+ mutex_unlock(&pTAS2781->codec_lock);
+ return 0;
+}
+
+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,
+ MAX_CONTROL_NAME, GFP_KERNEL);
+ configuration_name = devm_kzalloc(tas_dev->dev,
+ MAX_CONTROL_NAME, GFP_KERNEL);
+ if (!program_name || !configuration_name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ scnprintf(program_name, MAX_CONTROL_NAME, "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, MAX_CONTROL_NAME, "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;
+}
+
+void tasdevice_regbin_ready(const struct firmware *pFW,
+ void *pContext)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *) pContext;
+ struct tasdevice_regbin *regbin = NULL;
+ struct tasdevice_regbin_hdr *fw_hdr = NULL;
+ struct tasdevice_config_info **cfg_info = NULL;
+ const struct firmware *fw_entry = NULL;
+ unsigned char *buf = NULL;
+ int offset = 0, i = 0;
+ unsigned int total_config_sz = 0;
+ int ret = 0;
+
+ if (tas_dev == NULL) {
+ dev_err(tas_dev->dev,
+ "tasdev: regbin_ready: handle is NULL\n");
+ return;
+ }
+ mutex_lock(&tas_dev->codec_lock);
+ regbin = &(tas_dev->mtRegbin);
+ fw_hdr = &(regbin->fw_hdr);
+ if (unlikely(!pFW) || unlikely(!pFW->data)) {
+ dev_err(tas_dev->dev,
+ "Failed to read %s, no side - effect on driver running\n",
+ tas_dev->regbin_binaryname);
+ ret = -1;
+ goto out;
+ }
+ buf = (unsigned char *)pFW->data;
+
+ dev_info(tas_dev->dev, "tasdev: regbin_ready start\n");
+ fw_hdr->img_sz = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (fw_hdr->img_sz != pFW->size) {
+ dev_err(tas_dev->dev,
+ "File size not match, %d %u", (int)pFW->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 += 4;
+ fw_hdr->timestamp = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ 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) from Regbin and ndev(%u) from DTS does 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,
+ "regbin_ready: Out of Memory!\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;
+ dev_info(tas_dev->dev, "nconfig = %u\n", fw_hdr->nconfig);
+ 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];
+ }
+ dev_info(tas_dev->dev,
+ "img_sz = %u total_config_sz = %u offset = %d\n",
+ fw_hdr->img_sz, total_config_sz, offset);
+ 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;
+ dev_err(tas_dev->dev, "nconfig Memory alloc failed!\n");
+ goto out;
+ }
+ regbin->cfg_info = cfg_info;
+ regbin->ncfgs = 0;
+ for (i = 0; i < (int)fw_hdr->nconfig; i++) {
+ cfg_info[i] = tasdevice_add_config(pContext, &buf[offset],
+ fw_hdr->config_size[i]);
+ if (!cfg_info[i]) {
+ ret = -1;
+ dev_err(tas_dev->dev,
+ "add_config Memory alloc failed!\n");
+ break;
+ }
+ offset += (int)fw_hdr->config_size[i];
+ regbin->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->dsp_binaryname, 64, "%s_dsp.bin",
+ tas_dev->dev_name);
+ ret = request_firmware(&fw_entry, tas_dev->dsp_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->dsp_binaryname);
+ goto out;
+ }
+ tasdevice_dsp_create_control(tas_dev);
+
+ tas_dev->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+ tas_dev->mbCalibrationLoaded = 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].mnDevAddr);
+ 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 for playback\n",
+ __func__,
+ tas_dev->cal_binaryname[i]);
+ ret = 0;
+ tas_dev->mbCalibrationLoaded = false;
+ }
+ }
+
+out:
+ mutex_unlock(&tas_dev->codec_lock);
+ if (pFW)
+ release_firmware(pFW);
+ dev_info(tas_dev->dev, "Firmware init complete\n");
+}
+
+void tasdevice_config_info_remove(void *pContext)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *) pContext;
+ struct tasdevice_regbin *regbin = &(tas_dev->mtRegbin);
+ struct tasdevice_config_info **cfg_info = regbin->cfg_info;
+ int i = 0, j = 0;
+
+ mutex_lock(&tas_dev->dev_lock);
+ if (cfg_info) {
+ for (i = 0; i < regbin->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);
+ }
+ mutex_unlock(&tas_dev->dev_lock);
+}
+
+static int tasdevice_dac_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);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dev_info(tas_dev->dev, "SND_SOC_DAPM_POST_PMU\n");
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ dev_info(tas_dev->dev, "SND_SOC_DAPM_PRE_PMD\n");
+ break;
+ }
+
+ 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("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0,
+ tasdevice_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON")
+};
+
+static const struct snd_soc_dapm_route tasdevice_audio_map[] = {
+ {"DAC", NULL, "ASI"},
+ {"OUT", NULL, "DAC"},
+ {"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;
+
+ dev_info(tas_dev->dev, "%s: %s\n",
+ __func__, substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ "Playback":"Capture");
+
+ 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;
+ }
+ dev_info(tas_dev->dev,
+ "%s: BCLK rate = %d Channel = %d Sample rate = %u slot width = %u\n",
+ __func__, bclk_rate, params_channels(params),
+ fsrate, slot_width);
+out:
+ return rc;
+}
+
+static void tasdevice_enable_irq(
+ struct tasdevice_priv *tas_dev, bool enable)
+{
+ struct irq_desc *desc = NULL;
+
+ if (enable) {
+ if (tas_dev->mIrqInfo.mb_irq_enable)
+ return;
+ if (gpio_is_valid(tas_dev->mIrqInfo.mn_irq_gpio)) {
+ desc = irq_to_desc(tas_dev->mIrqInfo.mn_irq);
+ if (desc && desc->depth > 0)
+ enable_irq(tas_dev->mIrqInfo.mn_irq);
+ else
+ dev_info(tas_dev->dev,
+ "### irq already enabled\n");
+ }
+ tas_dev->mIrqInfo.mb_irq_enable = true;
+ } else {
+ if (gpio_is_valid(tas_dev->mIrqInfo.mn_irq_gpio))
+ disable_irq_nosync(tas_dev->mIrqInfo.mn_irq);
+ tas_dev->mIrqInfo.mb_irq_enable = false;
+ }
+}
+
+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);
+
+ dev_info(tas_dev->dev,
+ "%s: clk_id = %d, freq = %u, CLK direction %s\n",
+ __func__, clk_id, freq,
+ dir == TASDEVICE_CLK_DIR_OUT ? "OUT":"IN");
+
+ return 0;
+}
+
+static void powercontrol_routine(struct work_struct *work)
+{
+ struct tasdevice_priv *tas_dev =
+ container_of(work, struct tasdevice_priv,
+ powercontrol_work.work);
+ struct TFirmware *pFw = NULL;
+ int profile_cfg_id = 0;
+
+ mutex_lock(&tas_dev->codec_lock);
+ /*mnCurrentProgram != 0 is dsp mode or tuning mode*/
+ if (tas_dev->mnCurrentProgram) {
+ /*bypass all in regbin is profile id 0*/
+ profile_cfg_id = REGBIN_CONFIGID_BYPASS_ALL;
+ } else {
+ profile_cfg_id = tas_dev->mtRegbin.profile_cfg_id;
+ pFw = tas_dev->mpFirmware;
+ dev_info(tas_dev->dev, "%s: %s\n", __func__,
+ pFw->mpConfigurations[tas_dev->mnCurrentConfiguration]
+ .mpName);
+ tasdevice_select_tuningprm_cfg(tas_dev,
+ tas_dev->mnCurrentProgram,
+ tas_dev->mnCurrentConfiguration,
+ profile_cfg_id);
+ if (tas_dev->tas2781_set_global != NULL)
+ tas_dev->tas2781_set_global(tas_dev);
+ }
+ tasdevice_select_cfg_blk(tas_dev, profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+
+ if (tas_dev->chip_id != GENERAL_AUDEV)
+ tasdevice_enable_irq(tas_dev, true);
+ mutex_unlock(&tas_dev->codec_lock);
+}
+
+static void tasdevice_set_power_state(
+ struct tasdevice_priv *tas_dev, int state)
+{
+ switch (state) {
+ case 0:
+ schedule_delayed_work(&tas_dev->powercontrol_work,
+ msecs_to_jiffies(20));
+ break;
+ default:
+ if (!(tas_dev->pstream || tas_dev->cstream)) {
+ if (tas_dev->chip_id != GENERAL_AUDEV)
+ tasdevice_enable_irq(tas_dev, false);
+ tasdevice_select_cfg_blk(tas_dev,
+ tas_dev->mtRegbin.profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_SHUTDOWN);
+ }
+ break;
+ }
+}
+
+static int tasdevice_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+
+ /* Codec Lock Hold */
+ mutex_lock(&tas_dev->codec_lock);
+
+ if (mute) {
+ /* stop DSP only when both playback and capture streams
+ * are deactivated
+ */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tas_dev->pstream = 0;
+ else
+ tas_dev->cstream = 0;
+ if (tas_dev->pstream != 0 || tas_dev->cstream != 0)
+ goto out;
+ } else {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tas_dev->pstream = 1;
+ else
+ tas_dev->cstream = 1;
+
+ }
+ tasdevice_set_power_state(tas_dev, mute);
+out:
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_dev->codec_lock);
+
+ 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,
+ .mute_stream = tasdevice_mute,
+};
+
+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 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);
+
+ tas_dev->codec = codec;
+ scnprintf(tas_dev->regbin_binaryname, 64, "%s_regbin.bin",
+ tas_dev->dev_name);
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+ tas_dev->regbin_binaryname, tas_dev->dev, GFP_KERNEL, tas_dev,
+ tasdevice_regbin_ready);
+ if (ret)
+ dev_err(tas_dev->dev,
+ "%s: request_firmware_nowait error:0x%08x\n",
+ __func__, ret);
+
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_dev->codec_lock);
+
+ if (tas_dev->tas2781_reset != NULL)
+ tas_dev->tas2781_reset(tas_dev);
+ if (tas_dev->tas2781_set_global != NULL)
+ tas_dev->tas2781_set_global(tas_dev);
+
+ return ret;
+}
+
+static void tasdevice_deinit(void *pContext)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) pContext;
+
+ 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);
+
+ return;
+
+}
+
+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 void irq_work_routine(struct work_struct *pWork)
+{
+ struct tasdevice_priv *tas_dev =
+ container_of(pWork, struct tasdevice_priv,
+ mIrqInfo.irq_work.work);
+
+ mutex_lock(&tas_dev->codec_lock);
+ if (tas_dev->mb_runtime_suspend) {
+ dev_info(tas_dev->dev, "%s, Runtime Suspended\n", __func__);
+ goto end;
+ }
+ /*Logical Layer IRQ function, return is ignored*/
+ if (tas_dev->irq_work_func)
+ tas_dev->irq_work_func(tas_dev);
+ else
+ dev_info(tas_dev->dev,
+ "%s, irq_work_func is NULL\n", __func__);
+end:
+ mutex_unlock(&tas_dev->codec_lock);
+}
+
+static void tas2781_irq_work_func(struct tasdevice_priv *tas_dev)
+{
+ int rc = 0;
+ unsigned int reg_val = 0, array_size = 0, i = 0, ndev = 0;
+ unsigned int int_reg_array[] = {
+ TAS2781_REG_INT_LTCH0,
+ TAS2781_REG_INT_LTCH1,
+ TAS2781_REG_INT_LTCH1_0,
+ TAS2781_REG_INT_LTCH2,
+ TAS2781_REG_INT_LTCH3,
+ TAS2781_REG_INT_LTCH4};
+
+ tasdevice_enable_irq(tas_dev, false);
+
+ array_size = ARRAY_SIZE(int_reg_array);
+
+ for (ndev = 0; ndev < tas_dev->ndev; ndev++) {
+ for (i = 0; i < array_size; i++) {
+ rc = tasdevice_dev_read(tas_dev,
+ ndev, int_reg_array[i], ®_val);
+ if (!rc)
+ dev_info(tas_dev->dev,
+ "INT STATUS REG 0x%04x=0x%02x\n",
+ int_reg_array[i], reg_val);
+ else
+ dev_err(tas_dev->dev,
+ "Read Reg 0x%04x error(rc=%d)\n",
+ int_reg_array[i], rc);
+ }
+ }
+
+}
+
+static irqreturn_t tasdevice_irq_handler(int irq,
+ void *dev_id)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *)dev_id;
+;
+ /* get IRQ status after 100 ms */
+ schedule_delayed_work(&tas_dev->mIrqInfo.irq_work,
+ msecs_to_jiffies(100));
+ return IRQ_HANDLED;
+}
+
+int tasdevice_parse_dt(struct tasdevice_priv *tas_dev)
+{
+ struct device_node *np = tas_dev->dev->of_node;
+ int rc = 0, i = 0, ndev = 0;
+ char buf[32];
+
+
+ for (i = 0, ndev = 0; i < MaxChn; i++) {
+ rc = of_property_read_u32(np, dts_dev_addr_tag[i],
+ &(tas_dev->tasdevice[ndev].mnDevAddr));
+ if (rc)
+ dev_err(tas_dev->dev,
+ "Looking up %s property in node %s failed %d\n",
+ dts_dev_addr_tag[i],
+ np->full_name, rc);
+ else {
+ dev_dbg(tas_dev->dev, "%s=0x%02x", dts_dev_addr_tag[i],
+ tas_dev->tasdevice[ndev].mnDevAddr);
+ ndev++;
+ }
+ }
+
+ rc = of_property_read_u32(np, dts_glb_addr_tag,
+ &(tas_dev->glb_addr.mnDevAddr));
+ if (rc) {
+ dev_err(tas_dev->dev,
+ "Looking up %s property in node %s failed %d\n",
+ dts_glb_addr_tag, np->full_name, rc);
+ tas_dev->glb_addr.mnDevAddr = 0;
+ }
+ tas_dev->ndev = (ndev == 0) ? 1 : ndev;
+
+ for (i = 0, ndev = 0; i < tas_dev->ndev; i++) {
+ scnprintf(buf, sizeof(buf), dts_rst_tag, "reset", i);
+ tas_dev->tasdevice[ndev].mnResetGpio = of_get_named_gpio(np,
+ buf, 0);
+ if (gpio_is_valid(tas_dev->tasdevice[ndev].mnResetGpio)) {
+ rc = gpio_request(tas_dev->tasdevice[ndev].mnResetGpio,
+ buf);
+ if (!rc) {
+ gpio_direction_output(
+ tas_dev->tasdevice[ndev].mnResetGpio,
+ 1);
+ dev_info(tas_dev->dev, "%s = %d", buf,
+ tas_dev->tasdevice[ndev].mnResetGpio);
+ ndev++;
+ } else
+ dev_err(tas_dev->dev,
+ "%s: Failed to request dev[%d] gpio %d\n",
+ __func__, ndev,
+ tas_dev->tasdevice[ndev].mnResetGpio);
+ } else
+ dev_err(tas_dev->dev,
+ "Looking up %s property in node %s failed %d\n",
+ buf, np->full_name,
+ tas_dev->tasdevice[ndev].mnResetGpio);
+ }
+ strcpy(tas_dev->dev_name, tasdevice_id[tas_dev->chip_id].name);
+ if (tas_dev->chip_id != GENERAL_AUDEV) {
+ tas_dev->mIrqInfo.mn_irq_gpio = of_get_named_gpio(np,
+ "ti,irq-gpio", 0);
+ if (gpio_is_valid(tas_dev->mIrqInfo.mn_irq_gpio)) {
+ dev_dbg(tas_dev->dev, "irq-gpio = %d",
+ tas_dev->mIrqInfo.mn_irq_gpio);
+ INIT_DELAYED_WORK(&tas_dev->mIrqInfo.irq_work,
+ irq_work_routine);
+
+ rc = gpio_request(tas_dev->mIrqInfo.mn_irq_gpio,
+ "AUDEV-IRQ");
+ if (!rc) {
+ gpio_direction_input(
+ tas_dev->mIrqInfo.mn_irq_gpio);
+
+ tas_dev->mIrqInfo.mn_irq =
+ gpio_to_irq(
+ tas_dev->mIrqInfo.mn_irq_gpio);
+ dev_info(tas_dev->dev,
+ "irq = %d\n",
+ tas_dev->mIrqInfo.mn_irq);
+
+ rc = request_threaded_irq(
+ tas_dev->mIrqInfo.mn_irq,
+ tasdevice_irq_handler,
+ NULL, IRQF_TRIGGER_FALLING|
+ IRQF_ONESHOT,
+ SMARTAMP_MODULE_NAME, tas_dev);
+ if (!rc)
+ disable_irq_nosync(
+ tas_dev->mIrqInfo.mn_irq);
+ else
+ dev_err(tas_dev->dev,
+ "request_irq failed, %d\n",
+ rc);
+ } else
+ dev_err(tas_dev->dev,
+ "%s: GPIO %d request error\n",
+ __func__,
+ tas_dev->mIrqInfo.mn_irq_gpio);
+ } else
+ dev_err(tas_dev->dev,
+ "Looking up irq-gpio property in node %s failed %d\n",
+ np->full_name,
+ tas_dev->mIrqInfo.mn_irq_gpio);
+ }
+
+ if (tas_dev->chip_id != GENERAL_AUDEV && rc == 0) {
+ if (tas_dev->chip_id == TAS2781)
+ tas_dev->irq_work_func = tas2781_irq_work_func;
+ else
+ dev_info(tas_dev->dev, "%s: No match irq_work_func\n",
+ __func__);
+ }
+
+ return 0;
+}
+
+static int tasdevice_regmap_write(
+ struct tasdevice_priv *tas_dev,
+ unsigned int reg, unsigned int value)
+{
+ int nResult = 0;
+ int retry_count = TASDEVICE_RETRY_COUNT;
+
+ while (retry_count--) {
+ nResult = regmap_write(tas_dev->regmap, reg,
+ value);
+ if (nResult >= 0)
+ break;
+ usleep_range(5000, 5050);
+ }
+ if (retry_count == -1)
+ return TASDEVICE_ERROR_FAILED;
+ else
+ return 0;
+}
+
+static int tasdevice_regmap_bulk_write(
+ struct tasdevice_priv *tas_dev, unsigned int reg,
+ unsigned char *pData, unsigned int nLength)
+{
+ int nResult = 0;
+ int retry_count = TASDEVICE_RETRY_COUNT;
+
+ while (retry_count--) {
+ nResult = regmap_bulk_write(tas_dev->regmap, reg,
+ pData, nLength);
+ if (nResult >= 0)
+ break;
+ usleep_range(5000, 5050);
+ }
+ if (retry_count == -1)
+ return TASDEVICE_ERROR_FAILED;
+ else
+ return 0;
+}
+
+static int tasdevice_regmap_read(
+ struct tasdevice_priv *tas_dev,
+ unsigned int reg, unsigned int *value)
+{
+ int nResult = 0;
+ int retry_count = TASDEVICE_RETRY_COUNT;
+
+ while (retry_count--) {
+ nResult = regmap_read(tas_dev->regmap, reg,
+ value);
+ if (nResult >= 0)
+ break;
+ usleep_range(5000, 5050);
+ }
+ if (retry_count == -1)
+ return TASDEVICE_ERROR_FAILED;
+ else
+ return 0;
+}
+
+static int tasdevice_regmap_bulk_read(
+ struct tasdevice_priv *tas_dev, unsigned int reg,
+ unsigned char *pData, unsigned int nLength)
+{
+ int nResult = 0;
+ int retry_count = TASDEVICE_RETRY_COUNT;
+
+ while (retry_count--) {
+ nResult = regmap_bulk_read(tas_dev->regmap, reg,
+ pData, nLength);
+ if (nResult >= 0)
+ break;
+ usleep_range(5000, 5050);
+ }
+ if (retry_count == -1)
+ return TASDEVICE_ERROR_FAILED;
+ else
+ return 0;
+}
+
+static int tasdevice_regmap_update_bits(
+ struct tasdevice_priv *tas_dev, unsigned int reg,
+ unsigned int mask, unsigned int value)
+{
+ int nResult = 0;
+ int retry_count = TASDEVICE_RETRY_COUNT;
+
+ while (retry_count--) {
+ nResult = regmap_update_bits(tas_dev->regmap, reg,
+ mask, value);
+ if (nResult >= 0)
+ break;
+ usleep_range(5000, 5050);
+ }
+ if (retry_count == -1)
+ return TASDEVICE_ERROR_FAILED;
+ else
+ return 0;
+}
+
+static int tasdevice_change_chn_book_page(
+ struct tasdevice_priv *tas_dev, enum channel chn,
+ int book, int page)
+{
+ int n_result = 0;
+ struct i2c_client *pClient =
+ (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.mnBkPg.mnBook = -1;
+ tas_dev->glb_addr.mnBkPg.mnPage = -1;
+ }
+ pClient->addr = tas_dev->tasdevice[chn].mnDevAddr;
+ if (tas_dev->tasdevice[chn].mnBkPg.mnBook != book) {
+ n_result = tasdevice_regmap_write(tas_dev,
+ TASDEVICE_BOOKCTL_PAGE, 0);
+ if (n_result < 0) {
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ goto out;
+ }
+ tas_dev->tasdevice[chn].mnBkPg.mnPage = 0;
+ n_result = tasdevice_regmap_write(tas_dev,
+ TASDEVICE_BOOKCTL_REG, book);
+ if (n_result < 0) {
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ goto out;
+ }
+ tas_dev->tasdevice[chn].mnBkPg.mnBook = book;
+ }
+
+ if (tas_dev->tasdevice[chn].mnBkPg.mnPage != page) {
+ n_result = tasdevice_regmap_write(tas_dev,
+ TASDEVICE_BOOKCTL_PAGE, page);
+ if (n_result < 0) {
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ goto out;
+ }
+ tas_dev->tasdevice[chn].mnBkPg.mnPage = page;
+ }
+ } 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].mnBkPg.mnBook
+ = -1;
+ tas_dev->tasdevice[i].mnBkPg.mnPage
+ = -1;
+ }
+ pClient->addr = tas_dev->glb_addr.mnDevAddr;
+ if (tas_dev->glb_addr.mnBkPg.mnBook != book) {
+ n_result = tasdevice_regmap_write(tas_dev,
+ TASDEVICE_BOOKCTL_PAGE, 0);
+ if (n_result < 0) {
+ dev_err(tas_dev->dev,
+ "%s, 0ERROR, E=%d\n",
+ __func__, n_result);
+ goto out;
+ }
+ tas_dev->glb_addr.mnBkPg.mnPage = 0;
+ n_result = tasdevice_regmap_write(tas_dev,
+ TASDEVICE_BOOKCTL_REG, book);
+ if (n_result < 0) {
+ dev_err(tas_dev->dev,
+ "%s, book%xERROR, E=%d\n",
+ __func__, book, n_result);
+ goto out;
+ }
+ tas_dev->glb_addr.mnBkPg.mnBook = book;
+ }
+
+ if (tas_dev->glb_addr.mnBkPg.mnPage != page) {
+ n_result = tasdevice_regmap_write(tas_dev,
+ TASDEVICE_BOOKCTL_PAGE, page);
+ if (n_result < 0) {
+ dev_err(tas_dev->dev,
+ "%s, page%xERROR, E=%d\n",
+ __func__, page, n_result);
+ goto out;
+ }
+ tas_dev->glb_addr.mnBkPg.mnPage = page;
+ }
+ tas_dev->glb_addr.ref_cnt++;
+ } else
+ dev_err(tas_dev->dev,
+ "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+
+out:
+ return n_result;
+}
+
+int tasdevice_dev_read(struct tasdevice_priv *tas_dev,
+ enum channel chn, unsigned int reg, unsigned int *pValue)
+{
+ int n_result = 0;
+
+ mutex_lock(&tas_dev->dev_lock);
+ if (chn < tas_dev->ndev) {
+ n_result = tasdevice_change_chn_book_page(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg));
+ if (n_result < 0)
+ goto out;
+
+ n_result = tasdevice_regmap_read(tas_dev,
+ TASDEVICE_PAGE_REG(reg), pValue);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR,E=%d\n",
+ __func__, n_result);
+ else
+ dev_dbg(tas_dev->dev,
+ "%s: chn:0x%02x:BOOK:PAGE:REG 0x%02x:0x%02x:0x%02x, 0x%02x\n",
+ __func__,
+ tas_dev->tasdevice[chn].mnDevAddr,
+ TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg),
+ TASDEVICE_PAGE_REG(reg), *pValue);
+ } else {
+
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+ }
+
+out:
+ mutex_unlock(&tas_dev->dev_lock);
+ return n_result;
+}
+
+int tasdevice_dev_write(struct tasdevice_priv *tas_dev,
+ enum channel chn, unsigned int reg, unsigned int value)
+{
+ int n_result = 0;
+
+ mutex_lock(&tas_dev->dev_lock);
+ if (chn <= tas_dev->ndev) {
+ n_result = tasdevice_change_chn_book_page(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg));
+ if (n_result < 0)
+ goto out;
+
+ n_result = tasdevice_regmap_write(tas_dev,
+ TASDEVICE_PAGE_REG(reg), value);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ else {
+ dev_dbg(tas_dev->dev,
+ "%s: %s-0x%02x:BOOK:PAGE:REG 0x%02x:0x%02x:0x%02x, VAL: 0x%02x\n",
+ __func__, (chn == tas_dev->ndev)?"glb":"chn",
+ (chn == tas_dev->ndev) ?
+ tas_dev->glb_addr.mnDevAddr
+ : tas_dev->tasdevice[chn].mnDevAddr,
+ TASDEVICE_BOOK_ID(reg),
+ TASDEVICE_PAGE_ID(reg),
+ TASDEVICE_PAGE_REG(reg), value);
+ }
+ } else
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+out:
+ mutex_unlock(&tas_dev->dev_lock);
+ return n_result;
+}
+
+int tasdevice_dev_bulk_write(
+ struct tasdevice_priv *tas_dev, enum channel chn,
+ unsigned int reg, unsigned char *p_data,
+ unsigned int n_length)
+{
+ int n_result = 0;
+
+ mutex_lock(&tas_dev->dev_lock);
+ if (chn <= tas_dev->ndev) {
+ n_result = tasdevice_change_chn_book_page(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg));
+ if (n_result < 0)
+ goto out;
+
+ n_result = tasdevice_regmap_bulk_write(tas_dev,
+ TASDEVICE_PAGE_REG(reg), p_data, n_length);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ else {
+ dev_dbg(tas_dev->dev,
+ "%s: %s-0x%02x:BOOK:PAGE:REG 0x%02x:0x%02x: 0x%02x, len: 0x%02x\n",
+ __func__,
+ (chn == tas_dev->ndev)?"glb":"chn",
+ (chn == tas_dev->ndev) ?
+ tas_dev->glb_addr.mnDevAddr
+ : tas_dev->tasdevice[chn].mnDevAddr,
+ TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg),
+ TASDEVICE_PAGE_REG(reg), n_length);
+ }
+ } else
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+out:
+ mutex_unlock(&tas_dev->dev_lock);
+ return n_result;
+}
+
+int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_dev,
+ enum channel chn, unsigned int reg, unsigned char *p_data,
+ unsigned int n_length)
+{
+ int n_result = 0;
+
+ mutex_lock(&tas_dev->dev_lock);
+ if (chn < tas_dev->ndev) {
+ n_result = tasdevice_change_chn_book_page(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg));
+ if (n_result < 0)
+ goto out;
+
+ n_result = tasdevice_regmap_bulk_read(tas_dev,
+ TASDEVICE_PAGE_REG(reg), p_data, n_length);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ else
+ dev_dbg(tas_dev->dev,
+ "%s: chn0x%02x:BOOK:PAGE:REG 0x%02x:0x%02x: 0x%02x, len: 0x%02x\n",
+ __func__,
+ tas_dev->tasdevice[chn].mnDevAddr,
+ TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg),
+ TASDEVICE_PAGE_REG(reg), n_length);
+ } else
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+
+out:
+ mutex_unlock(&tas_dev->dev_lock);
+ return n_result;
+}
+
+int tasdevice_dev_update_bits(
+ struct tasdevice_priv *tas_dev, enum channel chn,
+ unsigned int reg, unsigned int mask, unsigned int value)
+{
+ int n_result = 0;
+ struct i2c_client *pClient =
+ (struct i2c_client *)tas_dev->client;
+
+ mutex_lock(&tas_dev->dev_lock);
+ if (chn < tas_dev->ndev) {
+ n_result = tasdevice_change_chn_book_page(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg));
+ if (n_result < 0)
+ goto out;
+ pClient->addr = tas_dev->tasdevice[chn].mnDevAddr;
+ n_result = tasdevice_regmap_update_bits(tas_dev,
+ TASDEVICE_PAGE_REG(reg), mask, value);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ else
+ dev_dbg(tas_dev->dev,
+ "%s: chn0x%02x:BOOK:PAGE:REG 0x%02x:0x%02x:0x%02x, mask: 0x%02x, val: 0x%02x\n",
+ __func__, tas_dev->tasdevice[chn].mnDevAddr,
+ TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg),
+ TASDEVICE_PAGE_REG(reg), mask, value);
+ } else {
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+ n_result = -1;
+ }
+
+out:
+ mutex_unlock(&tas_dev->dev_lock);
+ return n_result;
+}
+
+static void tas2781_reset(struct tasdevice_priv *tas_dev)
+{
+ int ret = 0;
+ int i = 0;
+
+ for (; 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, "%s: chn %d reset fail, %d\n",
+ __func__, i, ret);
+ continue;
+ }
+ }
+ usleep_range(1000, 1050);
+}
+
+static void tas2781_set_global(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_MASK, 0x02);
+ if (ret < 0) {
+ dev_err(tas_dev->dev, "%s: chn %d set global fail, %d\n",
+ __func__, i, ret);
+ continue;
+ }
+ }
+}
+
+static int tasdevice_init(struct tasdevice_priv *tas_dev)
+{
+ int nResult = 0, i = 0;
+
+ tas_dev->mnCurrentProgram = -1;
+ tas_dev->mnCurrentConfiguration = -1;
+
+ for (i = 0; i < tas_dev->ndev; i++) {
+ tas_dev->tasdevice[i].mnBkPg.mnBook = -1;
+ tas_dev->tasdevice[i].mnBkPg.mnPage = -1;
+ tas_dev->tasdevice[i].mnCurrentProgram = -1;
+ tas_dev->tasdevice[i].mnCurrentConfiguration = -1;
+ }
+ mutex_init(&tas_dev->dev_lock);
+ tas_dev->read = tasdevice_dev_read;
+ tas_dev->write = tasdevice_dev_write;
+ tas_dev->bulk_read = tasdevice_dev_bulk_read;
+ tas_dev->bulk_write = tasdevice_dev_bulk_write;
+ tas_dev->set_calibration = tas2781_set_calibration;
+ tas_dev->tas2781_reset = tas2781_reset;
+ if (tas_dev->glb_addr.mnDevAddr != 0
+ && tas_dev->glb_addr.mnDevAddr < 0x7F)
+ tas_dev->tas2781_set_global = tas2781_set_global;
+ dev_set_drvdata(tas_dev->dev, tas_dev);
+
+ INIT_DELAYED_WORK(&tas_dev->powercontrol_work,
+ powercontrol_routine);
+
+ mutex_init(&tas_dev->codec_lock);
+ nResult = devm_snd_soc_register_component(tas_dev->dev,
+ &soc_codec_driver_tasdevice,
+ tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver));
+ if (nResult)
+ dev_err(tas_dev->dev, "%s: codec register error:0x%08x\n",
+ __func__, nResult);
+
+ INIT_DELAYED_WORK(&tas_dev->mIrqInfo.irq_work, irq_work_routine);
+ tas_dev->mIrqInfo.mb_irq_enable = false;
+
+ dev_info(tas_dev->dev, "i2c register success\n");
+
+ return nResult;
+}
+
+void tasdevice_remove(struct tasdevice_priv *tas_dev)
+{
+ int i = 0;
+
+ for (i = 0; i < tas_dev->mtRstGPIOs.ndev; i++) {
+ if (gpio_is_valid(tas_dev->mtRstGPIOs.mnResetGpio[i]))
+ gpio_free(tas_dev->mtRstGPIOs.mnResetGpio[i]);
+ }
+
+ for (i = 0; i < tas_dev->ndev; i++) {
+ if (gpio_is_valid(tas_dev->tasdevice[i].mnIRQGPIO))
+ gpio_free(tas_dev->tasdevice[i].mnIRQGPIO);
+ }
+
+ if (delayed_work_pending(&tas_dev->mIrqInfo.irq_work)) {
+ dev_info(tas_dev->dev, "cancel IRQ work\n");
+ cancel_delayed_work(&tas_dev->mIrqInfo.irq_work);
+ }
+ cancel_delayed_work_sync(&tas_dev->mIrqInfo.irq_work);
+
+ mutex_destroy(&tas_dev->dev_lock);
+ mutex_destroy(&tas_dev->codec_lock);
+ snd_soc_unregister_component(tas_dev->dev);
+}
+
+static int tasdevice_pm_suspend(struct device *dev)
+{
+ struct tasdevice_priv *tas_dev = dev_get_drvdata(dev);
+
+ if (!tas_dev) {
+ dev_err(tas_dev->dev, "%s: drvdata is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&tas_dev->codec_lock);
+
+ tas_dev->mb_runtime_suspend = true;
+
+ if (tas_dev->chip_id != GENERAL_AUDEV) {
+ if (delayed_work_pending(&tas_dev->mIrqInfo.irq_work)) {
+ dev_dbg(tas_dev->dev, "cancel IRQ work\n");
+ cancel_delayed_work_sync(&tas_dev->mIrqInfo.irq_work);
+ }
+ }
+ mutex_unlock(&tas_dev->codec_lock);
+ return 0;
+}
+
+static int tasdevice_pm_resume(struct device *dev)
+{
+ struct tasdevice_priv *tas_dev = dev_get_drvdata(dev);
+
+ if (!tas_dev)
+ return -EINVAL;
+
+ mutex_lock(&tas_dev->codec_lock);
+ tas_dev->mb_runtime_suspend = false;
+ mutex_unlock(&tas_dev->codec_lock);
+ return 0;
+}
+
+const struct dev_pm_ops tasdevice_pm_ops = {
+ .suspend = tasdevice_pm_suspend,
+ .resume = tasdevice_pm_resume
+};
+
+static int tasdevice_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct tasdevice_priv *tas_dev = NULL;
+ int ret = 0;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
+ dev_err(&i2c->dev,
+ "%s: I2C check failed\n", __func__);
+ ret = -ENODEV;
+ goto out;
+ }
+
+ tas_dev = devm_kzalloc(&i2c->dev, sizeof(*tas_dev), GFP_KERNEL);
+ if (!tas_dev) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ tas_dev->dev = &i2c->dev;
+ tas_dev->client = (void *)i2c;
+ tas_dev->chip_id = id->driver_data;
+
+ 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);
+ return ret;
+ }
+ 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 *pClient)
+{
+ struct tasdevice_priv *tas_dev = i2c_get_clientdata(pClient);
+
+ if (tas_dev)
+ tasdevice_remove(tas_dev);
+
+}
+
+static struct i2c_driver tasdevice_i2c_driver = {
+ .driver = {
+ .name = "tas2781-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tasdevice_of_match),
+ .pm = &tasdevice_pm_ops,
+ },
+ .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 000000000..db4ec752b
--- /dev/null
+++ b/sound/soc/codecs/tas2781.h
@@ -0,0 +1,208 @@
+/* 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 register setting
+ * for one, two, even multiple TAS2781 chips. All the register setting are in
+ * a bin file. Almost no specific register setting can be found in the code.
+ *
+ * Author: Shenghao Ding <shenghao-ding(a)ti.com>
+ * Kevin Lu <kevin-lu(a)ti.com>
+ */
+
+#ifndef __TAS2781_H__
+#define __TAS2781_H__
+
+#include "tas2781-dsp.h"
+
+#define SMARTAMP_MODULE_NAME ("tas2781")
+#define MAX_LENGTH (128)
+
+#define TASDEVICE_RETRY_COUNT (3)
+#define TASDEVICE_ERROR_FAILED (-2)
+
+#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_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 (0x1 << 0)
+
+ /* Enable Global addresses */
+#define TAS2871_MISC_CFG2 TASDEVICE_REG(0x0, 0X0, 0x07)
+#define TAS2871_GLOBAL_MASK (0x1 << 1)
+
+#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))
+
+ /*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 {
+ GENERAL_AUDEV = 0,
+ TAS2781 = 1,
+};
+
+struct smartpa_gpio_info {
+ unsigned char ndev;
+ int mnResetGpio[MaxChn];
+};
+
+#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 Tbookpage {
+ unsigned char mnBook;
+ unsigned char mnPage;
+};
+
+struct Ttasdevice {
+ unsigned int mnDevAddr;
+ unsigned int mnErrCode;
+ short mnCurrentProgram;
+ short mnCurrentConfiguration;
+ short mnCurrentRegConf;
+ int mnIRQGPIO;
+ int mnResetGpio;
+ int mn_irq;
+ int PowerStatus;
+ bool bDSPBypass;
+ bool bIrq_enabled;
+ bool bLoading;
+ bool bLoaderr;
+ bool mbCalibrationLoaded;
+ struct Tbookpage mnBkPg;
+ struct TFirmware *mpCalFirmware;
+};
+
+struct global_addr {
+ struct Tbookpage mnBkPg;
+ unsigned int mnDevAddr;
+ int ref_cnt;
+};
+
+struct tas_control {
+ struct snd_kcontrol_new *tasdevice_profile_controls;
+ int nr_controls;
+};
+
+struct tasdevice_irqinfo {
+ int mn_irq_gpio;
+ int mn_irq;
+ struct delayed_work irq_work;
+ bool mb_irq_enable;
+};
+
+struct tasdevice_priv {
+ struct device *dev;
+ void *client;
+ struct regmap *regmap;
+ struct mutex codec_lock;
+ struct mutex dev_lock;
+ struct Ttasdevice tasdevice[MaxChn];
+ struct TFirmware *mpFirmware;
+ struct tasdevice_regbin mtRegbin;
+ struct smartpa_gpio_info mtRstGPIOs;
+ struct tasdevice_irqinfo mIrqInfo;
+ struct tas_control tas_ctrl;
+ struct global_addr glb_addr;
+ int mnCurrentProgram;
+ int mnCurrentConfiguration;
+ unsigned int chip_id;
+ int (*read)(struct tasdevice_priv *tas_dev, enum channel chn,
+ unsigned int reg, unsigned int *pValue);
+ int (*write)(struct tasdevice_priv *tas_dev, enum channel chn,
+ unsigned int reg, unsigned int Value);
+ int (*bulk_read)(struct tasdevice_priv *tas_dev, enum channel chn,
+ unsigned int reg, unsigned char *pData, unsigned int len);
+ int (*bulk_write)(struct tasdevice_priv *tas_dev, enum channel chn,
+ unsigned int reg, unsigned char *pData, unsigned int len);
+ int (*set_calibration)(void *tas_dev, enum channel chl,
+ int calibration);
+ void (*tas2781_reset)(struct tasdevice_priv *tas_dev);
+ void (*tas2781_set_global)(struct tasdevice_priv *tas_dev);
+ int (*fw_parse_variable_header)(struct tasdevice_priv *tas_dev,
+ const struct firmware *pFW, int offset);
+ int (*fw_parse_program_data)(struct tasdevice_priv *tas_dev,
+ struct TFirmware *pFirmware,
+ const struct firmware *pFW, int offset);
+ int (*fw_parse_configuration_data)(struct tasdevice_priv *tas_dev,
+ struct TFirmware *pFirmware,
+ const struct firmware *pFW, int offset);
+ int (*tasdevice_load_block)(struct tasdevice_priv *tas_dev,
+ struct TBlock *pBlock);
+ int (*fw_parse_calibration_data)(struct tasdevice_priv *tas_dev,
+ struct TFirmware *pFirmware,
+ const struct firmware *pFW, int offset);
+ void (*irq_work_func)(struct tasdevice_priv *tas_dev);
+ int fw_state;
+ unsigned int magic_num;
+ unsigned char ndev;
+ unsigned char dev_name[32];
+ unsigned char regbin_binaryname[64];
+ unsigned char dsp_binaryname[64];
+ unsigned char cal_binaryname[MaxChn][64];
+ bool mb_runtime_suspend;
+ struct delayed_work powercontrol_work;
+ void *codec;
+ int sysclk;
+ int pstream;
+ int cstream;
+ bool mbCalibrationLoaded;
+};
+
+int tasdevice_dev_read(struct tasdevice_priv *pPcmdev,
+ enum channel chn, unsigned int reg, unsigned int *pValue);
+int tasdevice_process_block(void *pContext,
+ unsigned char *data, unsigned char dev_idx, int sublocksize);
+int tasdevice_dev_write(struct tasdevice_priv *pPcmdev,
+ enum channel chn, unsigned int reg, unsigned int value);
+
+int tasdevice_dev_bulk_write(
+ struct tasdevice_priv *pPcmdev, enum channel chn,
+ unsigned int reg, unsigned char *p_data, unsigned int n_length);
+
+int tasdevice_dev_bulk_read(struct tasdevice_priv *pPcmdev,
+ enum channel chn, unsigned int reg, unsigned char *p_data,
+ unsigned int n_length);
+
+int tasdevice_dev_update_bits(
+ struct tasdevice_priv *pPcmdev, enum channel chn,
+ unsigned int reg, unsigned int mask, unsigned int value);
+
+#endif /*__TAS2781_H__ */
--
2.17.1
3
7