Alsa-devel
Threads by month
- ----- 2025 -----
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 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
- 16 participants
- 51350 discussions

ALSA: emux: Avoid potential array out-of-bound in snd_emux_xg_control()
by Артемий Карасев 03 Feb '23
by Артемий Карасев 03 Feb '23
03 Feb '23
In snd_emux_control() method "snd_emux_port" can be passed with
field chset.midi_mode == SNDRV_MIDI_MODE_XG. The "type" can be
MIDI_CTL_PITCHBEND or MIDI_CTL_CHAN_PRESSURE.
It may lead to accessing 'control' array of size 128 with
MIDI_CTL_PITCHBEND (0x80) or MIDI_CTL_CHAN_PRESSURE (0x82) index.
How can we prevent this? Would it be a good solution to place code like this
before call snd_emux_xg_control()?
if (type == MIDI_CTL_PITCHBEND || type == MIDI_CTL_CHAN_PRESSURE){
return;
}
Found by Linux Verification Center (linuxtesting.org) with SVACE.
1
0
Create tas2781 driver.
Signed-off-by: Shenghao Ding <13916275206(a)139.com>
---
Changes in v3:
- fixed issues reported-by: kernel test robot <lkp(a)intel.com>
- correct all the inconsistent indenting warnings.
- correct uninitialized symbol 'pwr_state'
- correct unsigned 'val' is never less than zero
- correct 'tas_dev' could be null
Changes to be committed:
modified: sound/soc/codecs/Kconfig
modified: sound/soc/codecs/Makefile
new file: sound/soc/codecs/tas2781-dsp.c
new file: sound/soc/codecs/tas2781-dsp.h
new file: sound/soc/codecs/tas2781-i2c.c
new file: sound/soc/codecs/tas2781.h
---
sound/soc/codecs/Kconfig | 12 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/tas2781-dsp.c | 2261 ++++++++++++++++++++++++++++++++
sound/soc/codecs/tas2781-dsp.h | 178 +++
sound/soc/codecs/tas2781-i2c.c | 1913 +++++++++++++++++++++++++++
sound/soc/codecs/tas2781.h | 176 +++
6 files changed, 4542 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 0f9d71490075..9016c4846d95 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -223,6 +223,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
@@ -1579,6 +1580,17 @@ config SND_SOC_TAS2780
Enable support for Texas Instruments TAS2780 high-efficiency
digital input mono Class-D audio power amplifiers.
+config SND_SOC_TAS2781
+ tristate "Texas Instruments TAS2781 speaker amplifier"
+ depends on I2C
+ select REGMAP_I2C
+ select CRC8
+ help
+ Enable support for Texas Instruments TAS2781 Smart Amplifier
+ Digital input mono Class-D and DSP-inside audio power amplifiers.
+ Note the TAS2781 driver implements a flexible and configurable
+ algo coff setting, for one, two, even multiple TAS2781 chips.
+
config SND_SOC_TAS5086
tristate "Texas Instruments TAS5086 speaker amplifier"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 71d3ce5867e4..49f912aea19d 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -355,6 +355,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
@@ -606,6 +607,7 @@ obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o
obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o
obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o
+obj-$(CONFIG_SND_SOC_TAS2781) += snd-soc-tas2781.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
diff --git a/sound/soc/codecs/tas2781-dsp.c b/sound/soc/codecs/tas2781-dsp.c
new file mode 100644
index 000000000000..615f5c24e3b3
--- /dev/null
+++ b/sound/soc/codecs/tas2781-dsp.c
@@ -0,0 +1,2261 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+ *
+ * Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+ * https://www.ti.com
+ *
+ * The TAS2781 driver implements a flexible and configurable algo coff setting
+ * for one, two, even multiple TAS2781 chips.
+ *
+ * Author: Shenghao Ding <shenghao-ding(a)ti.com>
+ * 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 ERROR_PRAM_CRCCHK 0x0000000
+#define ERROR_YRAM_CRCCHK 0x0000001
+#define BINFILE_VER 0
+#define DRV_VER 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 tas_crc {
+ unsigned char offset;
+ unsigned char len;
+};
+
+const char deviceNumber[TASDEVICE_DSP_TAS_MAX_DEVICE] = {
+ 1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4
+};
+
+static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->type = SMS_HTONL(data[offset],
+ data[offset + 1], data[offset + 2],
+ data[offset + 3]);
+ offset += 4;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mbPChkSumPresent error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ block->is_pchksum_present = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnPChkSum error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->pchksum = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mbYChkSumPresent error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ block->is_ychksum_present = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnYChkSum error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->ychksum = data[offset];
+ offset++;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: blk_size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->blk_size = SMS_HTONL(data[offset], data[offset + 1],
+ data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->n_subblocks = SMS_HTONL(data[offset], data[offset + 1],
+ data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ block->data = kzalloc(block->blk_size, GFP_KERNEL);
+ if (block->data == NULL) {
+ offset = -1;
+ goto out;
+ }
+ memcpy(block->data, &data[offset], block->blk_size);
+ offset += block->blk_size;
+out:
+ return offset;
+}
+
+static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw,
+ struct tasdevice_data *img_data, const struct firmware *fmw,
+ int offset)
+{
+ const unsigned char *data = fmw->data;
+ unsigned int n_block;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ img_data->n_blk = SMS_HTONL(data[offset],
+ data[offset + 1], data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ img_data->dev_blks =
+ kcalloc(img_data->n_blk, sizeof(struct tasdev_blk),
+ GFP_KERNEL);
+ if (img_data->dev_blks == NULL) {
+ dev_err(tas_fmw->dev, "%s: FW memory failed!\n", __func__);
+ goto out;
+ }
+
+ for (n_block = 0; n_block < img_data->n_blk; n_block++) {
+ offset = fw_parse_block_data_kernel(tas_fmw,
+ &(img_data->dev_blks[n_block]), fmw, offset);
+ if (offset < 0) {
+ offset = -1;
+ goto out;
+ }
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_program_data_kernel(
+ struct tasdevice_priv *tas_dev, struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset)
+{
+ struct tasdevice_prog *program;
+ unsigned int n_program = 0;
+
+ for (n_program = 0; n_program < tas_fmw->n_programs; n_program++) {
+ program = &(tas_fmw->programs[n_program]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpName error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 64;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnAppMode error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPDMI2SMode error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnISnsPD error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnVSnsPD error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ //skip 3-byte reserved
+ offset += 3;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPowerLDG error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_configuration_data_kernel(
+ struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+ unsigned int n_configs;
+ struct tasdevice_config *config;
+
+ for (n_configs = 0; n_configs < tas_fmw->n_configurations;
+ n_configs++) {
+ config =
+ &(tas_fmw->configs[n_configs]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpName error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ memcpy(config->name, &data[offset], 64);
+ offset += 64;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: orientation error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnDevices error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 1;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mProgram error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnSamplingRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPLLSrc error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnFsRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPLLSrcRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ offset = fw_parse_data_kernel(tas_fmw,
+ &(config->dev_data), fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_variable_header_kernel(
+ struct tasdevice_priv *tas_dev, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ const unsigned char *buf = fmw->data;
+ struct tasdevice_prog *program;
+ struct tasdevice_config *config;
+ unsigned int n_program = 0, n_configs = 0;
+ unsigned short max_confs = TASDEVICE_MAXCONFIG_NUM_KERNEL;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device_family = SMS_HTONS(buf[offset], buf[offset + 1]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_dev->dev, "ERROR:%s:not TAS device\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnDevice error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device = SMS_HTONS(buf[offset], buf[offset + 1]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_dev->dev, "ERROR:%s: not support device %d\n",
+ __func__, fw_hdr->device);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+ fw_hdr->ndev = deviceNumber[fw_hdr->device];
+
+ if (fw_hdr->ndev != tas_dev->ndev) {
+ dev_err(tas_dev->dev,
+ "%s: ndev(%u) in dspbin dismatch ndev(%u) in DTS\n",
+ __func__, fw_hdr->ndev, tas_dev->ndev);
+ offset = -1;
+ goto out;
+ }
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPrograms error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->n_programs = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+
+ if (tas_fmw->n_programs == 0 || tas_fmw->n_programs >
+ TASDEVICE_MAXPROGRAM_NUM_KERNEL) {
+ dev_err(tas_dev->dev, "%s: mnPrograms is invalid\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ if (offset + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpPrograms error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ tas_fmw->programs =
+ kcalloc(tas_fmw->n_programs,
+ sizeof(struct tasdevice_prog), GFP_KERNEL);
+ if (tas_fmw->programs == NULL) {
+ dev_err(tas_dev->dev, "%s: mpPrograms memory failed!\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+
+ for (n_program = 0; n_program < tas_fmw->n_programs; n_program++) {
+ program = &(tas_fmw->programs[n_program]);
+ program->prog_size = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ }
+ offset += (4 * (5 - n_program));
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnConfigurations error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->n_configurations = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ max_confs = (fw_hdr->ndev >= 4) ?
+ TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS :
+ TASDEVICE_MAXCONFIG_NUM_KERNEL;
+ if (tas_fmw->n_configurations == 0 ||
+ tas_fmw->n_configurations > max_confs) {
+ dev_err(tas_dev->dev, "%s: mnConfigurations is invalid\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+
+ if (offset + 4 * max_confs > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpConfigurations error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+
+ tas_fmw->configs = kcalloc(tas_fmw->n_configurations,
+ sizeof(struct tasdevice_config), GFP_KERNEL);
+ if (tas_fmw->configs == NULL) {
+ offset = -1;
+ goto out;
+ }
+
+ for (n_configs = 0; n_configs < tas_fmw->n_programs;
+ n_configs++) {
+ config =
+ &(tas_fmw->configs[n_configs]);
+ config->cfg_size = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ }
+
+ offset += (4 * (max_confs - n_configs));
+out:
+ return offset;
+}
+
+static int tasdevice_load_block_kernel(
+ struct tasdevice_priv *tasdevice, struct tasdev_blk *block)
+{
+ int ret = 0;
+
+ unsigned char *data = block->data;
+ unsigned int i = 0, length = 0;
+ const unsigned int blk_size = block->blk_size;
+ unsigned char dev_idx = 0;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tasdevice->fmw->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+
+ if (fw_fixed_hdr->ppcver >= PPC3_VERSION) {
+ switch (block->type) {
+ case MAIN_ALL_DEVICES_1X:
+ dev_idx = 0|0x80;
+ break;
+ case MAIN_DEVICE_A_1X:
+ dev_idx = 1|0x80;
+ break;
+ case COEFF_DEVICE_A_1X:
+ case PRE_DEVICE_A_1X:
+ dev_idx = 1|0xC0;
+ break;
+ case MAIN_DEVICE_B_1X:
+ dev_idx = 2|0x80;
+ break;
+ case COEFF_DEVICE_B_1X:
+ case PRE_DEVICE_B_1X:
+ dev_idx = 2|0xC0;
+ break;
+ case MAIN_DEVICE_C_1X:
+ dev_idx = 3|0x80;
+ break;
+ case COEFF_DEVICE_C_1X:
+ case PRE_DEVICE_C_1X:
+ dev_idx = 3|0xC0;
+ break;
+ case MAIN_DEVICE_D_1X:
+ dev_idx = 4|0x80;
+ break;
+ case COEFF_DEVICE_D_1X:
+ case PRE_DEVICE_D_1X:
+ dev_idx = 4|0xC0;
+ break;
+ default:
+ dev_info(tasdevice->dev,
+ "%s: load block: Other Type = 0x%02x\n",
+ __func__, block->type);
+ break;
+ }
+ } else {
+ switch (block->type) {
+ case MAIN_ALL_DEVICES:
+ dev_idx = 0|0x80;
+ break;
+ case MAIN_DEVICE_A:
+ dev_idx = 1|0x80;
+ break;
+ case COEFF_DEVICE_A:
+ case PRE_DEVICE_A:
+ dev_idx = 1|0xC0;
+ break;
+ case MAIN_DEVICE_B:
+ dev_idx = 2|0x80;
+ break;
+ case COEFF_DEVICE_B:
+ case PRE_DEVICE_B:
+ dev_idx = 2|0xC0;
+ break;
+ case MAIN_DEVICE_C:
+ dev_idx = 3|0x80;
+ break;
+ case COEFF_DEVICE_C:
+ case PRE_DEVICE_C:
+ dev_idx = 3|0xC0;
+ break;
+ case MAIN_DEVICE_D:
+ dev_idx = 4|0x80;
+ break;
+ case COEFF_DEVICE_D:
+ case PRE_DEVICE_D:
+ dev_idx = 4|0xC0;
+ break;
+ default:
+ dev_info(tasdevice->dev,
+ "%s: load block: Other Type = 0x%02x\n",
+ __func__, block->type);
+ break;
+ }
+ }
+
+ for (i = 0; i < block->n_subblocks; i++) {
+ int rc = tasdevice_process_block(tasdevice, data + length,
+ dev_idx, blk_size - length);
+ if (rc < 0) {
+ dev_err(tasdevice->dev,
+ "%s: ERROR:%u %u sublock write error\n",
+ __func__, length, blk_size);
+ break;
+ }
+ length += (unsigned int)rc;
+ if (blk_size < length) {
+ dev_err(tasdevice->dev,
+ "%s: ERROR:%u %u out of memory\n",
+ __func__, length, blk_size);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int fw_parse_variable_header_git(struct tasdevice_priv *tas_dev,
+ const struct firmware *fmw, int offset)
+{
+ const unsigned char *buf = fmw->data;
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ int i = strlen((char *)&buf[offset]);
+
+ i++;
+
+ if (offset + i > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ offset += i;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device_family = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_dev->dev, "ERROR:%s: not TAS device\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_dev->dev, "ERROR:%s: not support device %d\n",
+ __func__, fw_hdr->device);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->ndev = deviceNumber[fw_hdr->device];
+ if (fw_hdr->ndev != tas_dev->ndev) {
+ dev_err(tas_dev->dev,
+ "%s: ndev(%u) in dspbin dismatch ndev(%u) in DTS\n",
+ __func__, fw_hdr->ndev,
+ tas_dev->ndev);
+ offset = -1;
+ }
+
+out:
+ return offset;
+}
+
+static int fw_parse_variable_hdr_cal(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ const unsigned char *buf = fmw->data;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ int i = strlen((char *)&buf[offset]);
+
+ i++;
+
+ if (offset + i > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ offset += i;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnDeviceFamily error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device_family = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_dev->dev, "ERROR:%s: not TAS device\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnDevice error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_dev->dev, "ERROR:%s: not support device %d\n",
+ __func__, fw_hdr->device);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->ndev = deviceNumber[fw_hdr->device];
+ if (fw_hdr->ndev != 1) {
+ dev_err(tas_dev->dev,
+ "%s: calbin must be 1, but currently ndev(%u)\n",
+ __func__, fw_hdr->ndev);
+ offset = -1;
+ }
+
+out:
+ return offset;
+}
+
+static inline void tas2781_clear_calfirmware(struct tasdevice_fw
+ *tas_fmw)
+{
+ int i = 0;
+ unsigned int n_blk = 0;
+
+ if (tas_fmw->calibrations) {
+ struct tasdevice_calibration *calibration;
+
+ for (i = 0; i < tas_fmw->n_calibrations; i++) {
+ calibration = &(tas_fmw->calibrations[i]);
+ if (calibration) {
+ struct tasdevice_data *im =
+ &(calibration->dev_data);
+
+ if (im->dev_blks) {
+ struct tasdev_blk *block;
+
+ for (n_blk = 0; n_blk < im->n_blk;
+ n_blk++) {
+ block = &(im->dev_blks[n_blk]);
+ kfree(block->data);
+ }
+ kfree(im->dev_blks);
+ }
+ }
+ }
+ kfree(tas_fmw->calibrations);
+ }
+ kfree(tas_fmw);
+}
+
+static int fw_parse_block_data(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ int n;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnType error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->type = SMS_HTONL(data[offset], data[offset + 1],
+ data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >=
+ PPC_DRIVER_CRCCHK) {
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mbPChkSumPresent error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ block->is_pchksum_present = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnPChkSum error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ block->pchksum = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mbYChkSumPresent error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ block->is_ychksum_present = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnYChkSum error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ block->ychksum = data[offset];
+ offset++;
+ } else {
+ block->is_pchksum_present = 0;
+ block->is_ychksum_present = 0;
+ }
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnCommands error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->n_cmds = SMS_HTONL(data[offset], data[offset + 1],
+ data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ n = block->n_cmds * 4;
+ if (offset + n > fmw->size) {
+ dev_err(tas_fmw->dev,
+ "%s: File Size(%lu) error offset = %d n = %d\n",
+ __func__, (unsigned long)fmw->size, offset, n);
+ offset = -1;
+ goto out;
+ }
+ block->data = kmemdup(&data[offset], n, GFP_KERNEL);
+ if (block->data == NULL) {
+ offset = -1;
+ goto out;
+ }
+ offset += n;
+out:
+ return offset;
+}
+
+static int fw_parse_data(struct tasdevice_fw *tas_fmw,
+ struct tasdevice_data *img_data, const struct firmware *fmw,
+ int offset)
+{
+ const unsigned char *data = (unsigned char *)fmw->data;
+ int n = 0;
+ unsigned int n_block;
+
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mpName error\n", __func__);
+ n = -1;
+ goto out;
+ }
+ memcpy(img_data->name, &data[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n++;
+ if (offset + n > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mpDescription error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += n;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnBlocks error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ img_data->n_blk = SMS_HTONS(data[offset], data[offset + 1]);
+ offset += 2;
+
+ img_data->dev_blks =
+ kcalloc(img_data->n_blk, sizeof(struct tasdev_blk),
+ GFP_KERNEL);
+ if (img_data->dev_blks == NULL) {
+ dev_err(tas_fmw->dev, "%s: FW memory failed!\n", __func__);
+ goto out;
+ }
+ for (n_block = 0; n_block < img_data->n_blk; n_block++) {
+ offset = fw_parse_block_data(tas_fmw,
+ &(img_data->dev_blks[n_block]), fmw, offset);
+ if (offset < 0) {
+ offset = -1;
+ goto out;
+ }
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_calibration_data(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ unsigned int n = 0;
+ unsigned int n_calibration = 0;
+ struct tasdevice_calibration *calibration = NULL;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnCalibrations error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->n_calibrations = SMS_HTONS(data[offset], data[offset + 1]);
+ offset += 2;
+
+ if (tas_fmw->n_calibrations != 1) {
+ dev_err(tas_dev->dev,
+ "%s: only support one calibraiton(%d)!\n",
+ __func__, tas_fmw->n_calibrations);
+ goto out;
+ }
+
+ tas_fmw->calibrations =
+ kcalloc(tas_fmw->n_calibrations,
+ sizeof(struct tasdevice_calibration), GFP_KERNEL);
+ if (tas_fmw->calibrations == NULL) {
+ dev_err(tas_dev->dev, "%s: mpCalibrations memory failed!\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ for (n_calibration = 0; n_calibration < tas_fmw->n_calibrations;
+ n_calibration++) {
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpCalibrations error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ calibration = &(tas_fmw->calibrations[n_calibration]);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n++;
+ if (offset + n > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpDescription error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += n;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev,
+ "%s: mnProgram error, offset = %d\n", __func__,
+ offset);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev,
+ "%s: mnConfiguration error, offset = %d\n",
+ __func__,
+ offset);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ offset = fw_parse_data(tas_fmw, &(calibration->dev_data), fmw,
+ offset);
+ if (offset < 0)
+ goto out;
+ }
+
+out:
+
+ return offset;
+}
+
+static int fw_parse_program_data(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_prog *program;
+ unsigned char *buf = (unsigned char *)fmw->data;
+ int n_program = 0;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->n_programs = SMS_HTONS(buf[offset], buf[offset + 1]);
+ offset += 2;
+
+ if (tas_fmw->n_programs == 0) {
+ dev_err(tas_dev->dev, "%s: mnPrograms is null, maybe calbin\n",
+ __func__);
+ //Do not "offset = -1;", because of calbin
+ goto out;
+ }
+
+ tas_fmw->programs =
+ kcalloc(tas_fmw->n_programs, sizeof(struct tasdevice_prog),
+ GFP_KERNEL);
+ if (tas_fmw->programs == NULL) {
+ offset = -1;
+ goto out;
+ }
+ for (n_program = 0; n_program < tas_fmw->n_programs; n_program++) {
+ int n = 0;
+
+ program = &(tas_fmw->programs[n_program]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpName error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 64;
+
+ n = strlen((char *)&buf[offset]);
+ n++;
+ if (offset + n > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpDescription error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+
+ offset += n;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnAppMode error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPDMI2SMode error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnISnsPD error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnVSnsPD error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPowerLDG error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ offset = fw_parse_data(tas_fmw, &(program->dev_data), fmw,
+ offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_configuration_data(
+ struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ int n;
+ unsigned int n_configs;
+ struct tasdevice_config *config;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->n_configurations = SMS_HTONS(data[offset], data[offset + 1]);
+ offset += 2;
+
+ if (tas_fmw->n_configurations == 0) {
+ dev_err(tas_dev->dev, "%s: mnConfigurations is zero\n",
+ __func__);
+ //Do not "offset = -1;", because of calbin
+ goto out;
+ }
+ tas_fmw->configs =
+ kcalloc(tas_fmw->n_configurations,
+ sizeof(struct tasdevice_config), GFP_KERNEL);
+
+ for (n_configs = 0; n_configs < tas_fmw->n_configurations;
+ n_configs++) {
+ config =
+ &(tas_fmw->configs[n_configs]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ memcpy(config->name, &data[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n++;
+ if (offset + n > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpDescription error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+
+ offset += n;
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev,
+ "%s: mnDevice_orientation error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mProgram error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnSamplingRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPLLSrc error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPLLSrcRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnFsRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ offset = fw_parse_data(tas_fmw, &(config->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_header(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+ const unsigned char *buf = (unsigned char *)fmw->data;
+ const unsigned char magic_number[] = { 0x35, 0x35, 0x35, 0x32 };
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ if (memcmp(&buf[offset], magic_number, 4)) {
+ dev_err(tas_dev->dev, "%s: Magic number doesn't match",
+ __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnFWSize error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_fixed_hdr->fwsize = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ if (fw_fixed_hdr->fwsize != fmw->size) {
+ dev_err(tas_dev->dev, "File size not match, %lu %u",
+ (unsigned long)fmw->size, fw_fixed_hdr->fwsize);
+ offset = -1;
+ goto out;
+ }
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnChecksum error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPPCVersion error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_fixed_hdr->ppcver = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnFWVersion error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnDriverVersion error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_fixed_hdr->drv_ver = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnTimeStamp error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpDDCName error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 64;
+
+ out:
+ return offset;
+}
+
+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 check_inpage_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ int ret = 0;
+
+ if (book == TAS2781_YRAM_BOOK1) {
+ if (page == TAS2781_YRAM1_PAGE) {
+ if (reg >= TAS2781_YRAM1_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ } else if ((reg + len) > TAS2781_YRAM1_START_REG) {
+ cd->offset = TAS2781_YRAM1_START_REG;
+ cd->len =
+ len - (TAS2781_YRAM1_START_REG - reg);
+ ret = 1;
+ } else
+ ret = 0;
+ } else if (page == TAS2781_YRAM3_PAGE) {
+ if (reg > TAS2781_YRAM3_END_REG) {
+ ret = 0;
+ } else if (reg >= TAS2781_YRAM3_START_REG) {
+ if ((reg + len) > TAS2781_YRAM3_END_REG) {
+ cd->offset = reg;
+ cd->len =
+ TAS2781_YRAM3_END_REG - reg + 1;
+ ret = 1;
+ } else {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ }
+ } else {
+ if ((reg + (len-1)) <
+ TAS2781_YRAM3_START_REG)
+ ret = 0;
+ else {
+ cd->offset =
+ TAS2781_YRAM3_START_REG;
+ cd->len =
+ len - (TAS2781_YRAM3_START_REG - reg);
+ ret = 1;
+ }
+ }
+ }
+ } else if (book ==
+ TAS2781_YRAM_BOOK2) {
+ if (page == TAS2781_YRAM5_PAGE) {
+ if (reg > TAS2781_YRAM5_END_REG) {
+ ret = 0;
+ } else if (reg >= TAS2781_YRAM5_START_REG) {
+ if ((reg + len) > TAS2781_YRAM5_END_REG) {
+ cd->offset = reg;
+ cd->len =
+ TAS2781_YRAM5_END_REG - reg + 1;
+ ret = 1;
+ } else {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ }
+ } else {
+ if ((reg + (len-1)) <
+ TAS2781_YRAM5_START_REG)
+ ret = 0;
+ else {
+ cd->offset =
+ TAS2781_YRAM5_START_REG;
+ cd->len =
+ len - (TAS2781_YRAM5_START_REG - reg);
+ ret = 1;
+ }
+ }
+ }
+ } else
+ ret = 0;
+
+ return ret;
+}
+
+static int check_inblock_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ int ret = 0;
+
+ if (book == TAS2781_YRAM_BOOK1) {
+ if (page < TAS2781_YRAM2_START_PAGE)
+ ret = 0;
+ else if (page <= TAS2781_YRAM2_END_PAGE) {
+ if (reg > TAS2781_YRAM2_END_REG)
+ ret = 0;
+ else if (reg >= TAS2781_YRAM2_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ } else {
+ if ((reg + (len-1)) <
+ TAS2781_YRAM2_START_REG)
+ ret = 0;
+ else {
+ cd->offset =
+ TAS2781_YRAM2_START_REG;
+ cd->len =
+ reg + len - TAS2781_YRAM2_START_REG;
+ ret = 1;
+ }
+ }
+ } else
+ ret = 0;
+ } else if (book ==
+ TAS2781_YRAM_BOOK2) {
+ if (page < TAS2781_YRAM4_START_PAGE)
+ ret = 0;
+ else if (page <= TAS2781_YRAM4_END_PAGE) {
+ if (reg > TAS2781_YRAM2_END_REG)
+ ret = 0;
+ else if (reg >= TAS2781_YRAM2_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ } else {
+ if ((reg + (len-1))
+ < TAS2781_YRAM2_START_REG)
+ ret = 0;
+ else {
+ cd->offset =
+ TAS2781_YRAM2_START_REG;
+ cd->len =
+ reg + len - TAS2781_YRAM2_START_REG;
+ ret = 1;
+ }
+ }
+ } else
+ ret = 0;
+ } else
+ ret = 0;
+
+ return ret;
+}
+
+static int check_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ int ret = 0;
+
+ ret = check_inpage_yram(cd, book, page, reg, len);
+ if (ret == 0)
+ ret = check_inblock_yram(cd, book,
+ page, reg, len);
+
+ return ret;
+}
+
+static int do_singlereg_checksum(struct tasdevice_priv *tasdevice,
+ enum channel chl, unsigned char book, unsigned char page,
+ unsigned char reg, unsigned char val)
+{
+ int ret = 0;
+ struct tas_crc crc_data;
+ unsigned int nData1 = 0;
+
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg <= (TASDEVICE_PAGE_REG(
+ TAS2781_SA_COEFF_SWAP_REG) + 4))) {
+ /*DSP swap command, pass */
+ ret = 0;
+ goto end;
+ }
+
+ ret = check_yram(&crc_data, book, page, reg, 1);
+ if (ret == 1) {
+ ret = tasdevice_dev_read(tasdevice, chl,
+ TASDEVICE_REG(book, page, reg), &nData1);
+ if (ret < 0)
+ goto end;
+
+ if (nData1 != val) {
+ dev_err(tasdevice->dev,
+ "B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n",
+ book, page, reg,
+ val, nData1);
+ ret = -EAGAIN;
+ tasdevice->tasdevice[chl].err_code |=
+ ERROR_YRAM_CRCCHK;
+ ret = -EAGAIN;
+ goto end;
+ }
+
+ ret = crc8(crc8_lookup_table, &val, 1, 0);
+ }
+
+end:
+ return ret;
+}
+
+static int do_multireg_checksum(struct tasdevice_priv *tasdevice,
+ enum channel chn, unsigned char book, unsigned char page,
+ unsigned char reg, unsigned int len)
+{
+ int ret = 0, i = 0;
+ unsigned char crc_chksum = 0;
+ unsigned char nBuf1[128] = {0};
+ struct tas_crc crc_data;
+
+ if ((reg + len-1) > 127) {
+ ret = -EINVAL;
+ dev_err(tasdevice->dev, "firmware error\n");
+ goto end;
+ }
+
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg == TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+ && (len == 4)) {
+ /*DSP swap command, pass */
+ ret = 0;
+ goto end;
+ }
+
+ ret = check_yram(&crc_data, book, page, reg, len);
+ dev_info(tasdevice->dev,
+ "isYRAM: Bk 0x%02x, Pg 0x%02x, Rg 0x%02x\n",
+ book, page, reg);
+ dev_info(tasdevice->dev,
+ "isYRAM: crc_data.mnLen 0x%x, len 0x%x, nResult %d\n",
+ crc_data.len, len, ret);
+ dev_info(tasdevice->dev, "crc_data.mnOffset %x\n", crc_data.offset);
+ if (ret == 1) {
+ if (len == 1) {
+ dev_err(tasdevice->dev, "firmware error\n");
+ ret = -EINVAL;
+ goto end;
+ } else {
+ ret = tasdevice_dev_bulk_read(tasdevice, chn,
+ TASDEVICE_REG(book, page, crc_data.offset),
+ nBuf1, crc_data.len);
+ if (ret < 0)
+ goto end;
+
+ for (i = 0; i < crc_data.len; i++) {
+ if ((book == TASDEVICE_BOOK_ID(
+ TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(
+ TAS2781_SA_COEFF_SWAP_REG))
+ && ((i + crc_data.offset)
+ >= TASDEVICE_PAGE_REG(
+ TAS2781_SA_COEFF_SWAP_REG))
+ && ((i + crc_data.offset)
+ <= (TASDEVICE_PAGE_REG(
+ TAS2781_SA_COEFF_SWAP_REG)
+ + 4))) {
+ /*DSP swap command, bypass */
+ continue;
+ } else
+ crc_chksum +=
+ crc8(crc8_lookup_table,
+ &nBuf1[i], 1, 0);
+ }
+
+ ret = crc_chksum;
+ }
+ }
+
+end:
+ return ret;
+}
+
+static int tasdevice_load_block(struct tasdevice_priv *tas_dev,
+ struct tasdev_blk *block)
+{
+ int ret = 0;
+ unsigned int n_cmds = 0;
+ unsigned char book = 0;
+ unsigned char page = 0;
+ unsigned char offset = 0;
+ unsigned char val = 0;
+ unsigned int len = 0;
+ unsigned int sleep_dur = 0;
+ unsigned char crc_chksum = 0;
+ unsigned int n_value = 0;
+ int n_retry = 6;
+ unsigned char *data = block->data;
+ int chn = 0, chnend = 0;
+
+ dev_info(tas_dev->dev,
+ "load block: Type = %d, commands = %d\n",
+ block->type, block->n_cmds);
+ switch (block->type) {
+ case MAIN_ALL_DEVICES:
+ chn = 0;
+ chnend = tas_dev->ndev;
+ break;
+ case MAIN_DEVICE_A:
+ case COEFF_DEVICE_A:
+ case PRE_DEVICE_A:
+ chn = 0;
+ chnend = 1;
+ break;
+ case MAIN_DEVICE_B:
+ case COEFF_DEVICE_B:
+ case PRE_DEVICE_B:
+ chn = 1;
+ chnend = 2;
+ break;
+ case MAIN_DEVICE_C:
+ case COEFF_DEVICE_C:
+ case PRE_DEVICE_C:
+ chn = 2;
+ chnend = 3;
+ break;
+ case MAIN_DEVICE_D:
+ case COEFF_DEVICE_D:
+ case PRE_DEVICE_D:
+ chn = 3;
+ chnend = 4;
+ break;
+ default:
+ dev_info(tas_dev->dev,
+ "load block: Other Type = 0x%02x\n",
+ block->type);
+ break;
+ }
+
+ for (; chn < chnend; chn++) {
+ if (tas_dev->tasdevice[chn].is_loading == false)
+ continue;
+start:
+ if (block->is_pchksum_present) {
+ ret = tasdevice_dev_write(tas_dev, chn,
+ TASDEVICE_I2CChecksum, 0);
+ if (ret < 0)
+ goto end;
+ }
+
+ if (block->is_ychksum_present)
+ crc_chksum = 0;
+
+ n_cmds = 0;
+
+ while (n_cmds < block->n_cmds) {
+ data = block->data + n_cmds * 4;
+
+ book = data[0];
+ page = data[1];
+ offset = data[2];
+ val = data[3];
+
+ n_cmds++;
+
+ if (offset <= 0x7F) {
+ ret = tasdevice_dev_write(tas_dev, chn,
+ TASDEVICE_REG(book, page, offset),
+ val);
+ if (ret < 0)
+ goto end;
+ if (block->is_ychksum_present) {
+ ret = do_singlereg_checksum(tas_dev,
+ chn, book, page, offset,
+ val);
+ if (ret < 0)
+ goto check;
+ crc_chksum += (unsigned char)ret;
+ }
+ continue;
+ }
+ if (offset == 0x81) {
+ sleep_dur = (book << 8) + page;
+ msleep(sleep_dur);
+ continue;
+ }
+ if (offset == 0x85) {
+ data += 4;
+ len = (book << 8) + page;
+ book = data[0];
+ page = data[1];
+ offset = data[2];
+ if (len > 1) {
+ ret = tasdevice_dev_bulk_write(
+ tas_dev, chn,
+ TASDEVICE_REG(book, page,
+ offset), data + 3, len);
+ if (ret < 0)
+ goto end;
+ if (!block->is_ychksum_present)
+ goto next;
+ ret = do_multireg_checksum(
+ tas_dev, chn, book,
+ page, offset,
+ len);
+ if (ret < 0)
+ goto check;
+ crc_chksum +=
+ (unsigned char)ret;
+ } else {
+ ret = tasdevice_dev_write(tas_dev,
+ chn,
+ TASDEVICE_REG(book, page,
+ offset),
+ data[3]);
+ if (ret < 0)
+ goto end;
+ if (!block->is_ychksum_present)
+ goto next;
+ ret = do_singlereg_checksum(
+ tas_dev, chn, book,
+ page, offset,
+ data[3]);
+ if (ret < 0)
+ goto check;
+ crc_chksum +=
+ (unsigned char)ret;
+ }
+next:
+ n_cmds++;
+ if (len >= 2)
+ n_cmds += ((len - 2) / 4) + 1;
+ }
+ }
+ if (block->is_pchksum_present) {
+ ret = tasdevice_dev_read(tas_dev, chn,
+ TASDEVICE_I2CChecksum, &n_value);
+ if (ret < 0) {
+ dev_err(tas_dev->dev, "%s: Channel %d\n",
+ __func__, chn);
+ goto check;
+ }
+ if ((n_value & 0xff) != block->pchksum) {
+ dev_err(tas_dev->dev,
+ "Block PChkSum Channel %d ", chn);
+ dev_err(tas_dev->dev,
+ "FW = 0x%x, Reg = 0x%x\n",
+ block->pchksum,
+ (n_value & 0xff));
+ ret = -EAGAIN;
+ tas_dev->tasdevice[chn].err_code |=
+ ERROR_PRAM_CRCCHK;
+ goto check;
+ }
+ ret = 0;
+ tas_dev->tasdevice[chn].err_code &=
+ ~ERROR_PRAM_CRCCHK;
+ dev_info(tas_dev->dev, "Block[0x%02x] PChkSum match\n",
+ block->type);
+ }
+
+ if (block->is_ychksum_present) {
+ //TBD, open it when FW ready
+ dev_err(tas_dev->dev,
+ "Block YChkSum: FW = 0x%x, YCRC = 0x%x\n",
+ block->ychksum, crc_chksum);
+
+ tas_dev->tasdevice[chn].err_code &=
+ ~ERROR_YRAM_CRCCHK;
+ ret = 0;
+ dev_info(tas_dev->dev,
+ "Block[0x%x] YChkSum match\n", block->type);
+ }
+check:
+ if (ret == -EAGAIN) {
+ n_retry--;
+ if (n_retry > 0)
+ goto start;
+ else {
+ if ((block->type == MAIN_ALL_DEVICES)
+ || (block->type == MAIN_DEVICE_A)
+ || (block->type == MAIN_DEVICE_B)
+ || (block->type == MAIN_DEVICE_C)
+ || (block->type == MAIN_DEVICE_D))
+ tas_dev->tasdevice[chn].cur_prog = -1;
+ else
+ tas_dev->tasdevice[chn].cur_conf = -1;
+ n_retry = 6;
+ }
+ }
+ }
+end:
+ if (ret < 0)
+ dev_err(tas_dev->dev, "Block (%d) load error\n", block->type);
+
+ return ret;
+}
+
+
+static int tasdevice_load_data(struct tasdevice_priv *tas_dev,
+ struct tasdevice_data *dev_data)
+{
+ int ret = 0;
+ unsigned int n_block = 0;
+ struct tasdev_blk *block = NULL;
+
+ dev_info(tas_dev->dev, "%s: TAS2781 load data: %s, Blocks = %d\n",
+ __func__, dev_data->name, dev_data->n_blk);
+
+ for (n_block = 0; n_block < dev_data->n_blk; n_block++) {
+ block = &(dev_data->dev_blks[n_block]);
+ ret = tas_dev->tasdevice_load_block(tas_dev, block);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static int tasdevice_load_calibrated_data(
+ struct tasdevice_priv *tas_dev, struct tasdevice_data *dev_data)
+{
+ int ret = 0;
+ unsigned int n_block = 0;
+ struct tasdev_blk *block = NULL;
+
+ dev_info(tas_dev->dev, "%s: TAS2781 load data: %s, Blocks = %d\n",
+ __func__, dev_data->name, dev_data->n_blk);
+
+ for (n_block = 0; n_block < dev_data->n_blk; n_block++) {
+ block = &(dev_data->dev_blks[n_block]);
+ ret = tasdevice_load_block(tas_dev, block);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+int tas2781_load_calibration(void *context,
+ char *file_name, enum channel i)
+{
+ int ret = 0, offset = 0;
+ struct firmware fmw;
+ const struct firmware *fw_entry = NULL;
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *)context;
+ struct tasdevice *tasdev = &(tas_dev->tasdevice[i]);
+ struct tasdevice_fw *tas_fmw = NULL;
+
+ ret = request_firmware(&fw_entry, file_name, tas_dev->dev);
+ if (!ret) {
+ if (!fw_entry->size) {
+ dev_err(tas_dev->dev,
+ "%s: file read error: size = %lu\n",
+ __func__, (unsigned long)fw_entry->size);
+ goto out;
+ }
+ fmw.size = fw_entry->size;
+ fmw.data = fw_entry->data;
+ dev_info(tas_dev->dev,
+ "%s: file = %s, file size %zd\n",
+ __func__, file_name, fw_entry->size);
+ } else {
+ dev_info(tas_dev->dev,
+ "%s: Request firmware %s failed\n",
+ __func__, file_name);
+ goto out;
+ }
+
+ tas_fmw = tasdev->cali_data_fmw = kcalloc(1,
+ sizeof(struct tasdevice_fw), GFP_KERNEL);
+ if (tasdev->cali_data_fmw == NULL) {
+ dev_err(tas_dev->dev, "%s: FW memory failed!\n", __func__);
+ ret = -1;
+ goto out;
+ }
+ tas_fmw->dev = tas_dev->dev;
+ offset = fw_parse_header(tas_dev, tas_fmw, &fmw, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev, "%s: fw_parse_header EXIT!\n", __func__);
+ goto out;
+ }
+ offset = fw_parse_variable_hdr_cal(tas_dev, tas_fmw, &fmw, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev,
+ "%s: fw_parse_variable_header_cal EXIT!\n", __func__);
+ goto out;
+ }
+ offset = fw_parse_program_data(tas_dev, tas_fmw, &fmw, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev, "%s: fw_parse_program_data EXIT!\n",
+ __func__);
+ goto out;
+ }
+ offset = fw_parse_configuration_data(tas_dev, tas_fmw, &fmw,
+ 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,
+ tas_fmw, &fmw, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev, "%s: fw_parse_calibration_data EXIT!\n",
+ __func__);
+ goto out;
+ }
+ tasdev->is_calibrated_data_loaded = true;
+out:
+ if (fw_entry) {
+ release_firmware(fw_entry);
+ fw_entry = NULL;
+ }
+ return ret;
+}
+
+int tasdevice_dspfw_ready(const struct firmware *fmw, void *context)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ struct tasdevice_fw *tas_fmw = NULL;
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = NULL;
+ int offset = 0, ret = 0;
+
+ if (!fmw || !fmw->data) {
+ dev_err(tas_dev->dev, "%s: Failed to read firmware %s\n",
+ __func__, tas_dev->coef_binaryname);
+ ret = -1;
+ goto out;
+ }
+
+ tas_dev->fmw = kcalloc(1,
+ sizeof(struct tasdevice_fw), GFP_KERNEL);
+ if (tas_dev->fmw == NULL) {
+ ret = -1;
+ goto out;
+ }
+ tas_fmw = tas_dev->fmw;
+ tas_fmw->dev = tas_dev->dev;
+ offset = fw_parse_header(tas_dev, tas_fmw, fmw, offset);
+
+ if (offset == -1)
+ goto out;
+ fw_fixed_hdr = &(tas_fmw->fw_hdr.fixed_hdr);
+ switch (fw_fixed_hdr->drv_ver) {
+ case 0x301:
+ case 0x302:
+ case 0x502:
+ tas_dev->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_dev->fw_parse_program_data =
+ fw_parse_program_data_kernel;
+ tas_dev->fw_parse_configuration_data =
+ fw_parse_configuration_data_kernel;
+ tas_dev->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ break;
+ case 0x202:
+ case 0x400:
+ tas_dev->fw_parse_variable_header =
+ fw_parse_variable_header_git;
+ tas_dev->fw_parse_program_data =
+ fw_parse_program_data;
+ tas_dev->fw_parse_configuration_data =
+ fw_parse_configuration_data;
+ tas_dev->tasdevice_load_block =
+ tasdevice_load_block;
+ break;
+ default:
+ if (fw_fixed_hdr->drv_ver == 0x100) {
+ if (fw_fixed_hdr->ppcver >= PPC3_VERSION) {
+ tas_dev->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_dev->fw_parse_program_data =
+ fw_parse_program_data_kernel;
+ tas_dev->fw_parse_configuration_data =
+ fw_parse_configuration_data_kernel;
+ tas_dev->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ tas_dev->fw_parse_calibration_data = NULL;
+ } else {
+ switch (fw_fixed_hdr->ppcver) {
+ case 0x00:
+ tas_dev->fw_parse_variable_header =
+ fw_parse_variable_header_git;
+ tas_dev->fw_parse_program_data =
+ fw_parse_program_data;
+ tas_dev->fw_parse_configuration_data =
+ fw_parse_configuration_data;
+ tas_dev->fw_parse_calibration_data =
+ fw_parse_calibration_data;
+ tas_dev->tasdevice_load_block =
+ tasdevice_load_block;
+ break;
+ default:
+ dev_err(tas_dev->dev,
+ "%s: PPCVersion must be 0x0 or 0x%02x",
+ __func__, PPC3_VERSION);
+ dev_err(tas_dev->dev, " Current:0x%02x\n",
+ fw_fixed_hdr->ppcver);
+ offset = -1;
+ break;
+ }
+ }
+ } else {
+ dev_err(tas_dev->dev,
+ "%s: DriverVersion must be 0x0, 0x230 or above 0x230 ",
+ __func__);
+ dev_err(tas_dev->dev, "current is 0x%02x\n",
+ fw_fixed_hdr->drv_ver);
+ offset = -1;
+ }
+ break;
+ }
+
+ offset = tas_dev->fw_parse_variable_header(tas_dev, fmw, offset);
+ if (offset == -1)
+ goto out;
+
+ offset = tas_dev->fw_parse_program_data(tas_dev, tas_fmw, fmw,
+ offset);
+ if (offset < 0) {
+ ret = -1;
+ goto out;
+ }
+ offset = tas_dev->fw_parse_configuration_data(tas_dev,
+ tas_fmw, fmw, offset);
+ if (offset < 0)
+ ret = -1;
+
+out:
+ return ret;
+}
+
+void tasdevice_calbin_remove(void *context)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ struct tasdevice *tasdev = NULL;
+ int i = 0;
+
+ if (tas_dev) {
+ for (i = 0; i < tas_dev->ndev; i++) {
+ tasdev = &(tas_dev->tasdevice[i]);
+ if (tasdev->cali_data_fmw) {
+ tas2781_clear_calfirmware(
+ tasdev->cali_data_fmw);
+ tasdev->cali_data_fmw = NULL;
+ }
+ }
+ }
+}
+
+void tasdevice_dsp_remove(void *context)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ int i;
+ struct tasdev_blk *blk;
+ unsigned int n_blk;
+
+ if (tas_dev->fmw) {
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+
+ if (tas_fmw->programs) {
+ struct tasdevice_prog *program;
+
+ for (i = 0; i < tas_fmw->n_programs; i++) {
+ program = &(tas_fmw->programs[i]);
+ if (program) {
+ struct tasdevice_data
+ *im =
+ &(program->dev_data);
+
+ if (!im->dev_blks)
+ continue;
+ for (n_blk = 0; n_blk < im->n_blk;
+ n_blk++) {
+ blk =
+ &(im->dev_blks[n_blk]);
+ kfree(blk->data);
+ }
+ kfree(im->dev_blks);
+ }
+ }
+ kfree(tas_fmw->programs);
+ }
+
+ if (tas_fmw->configs) {
+ struct tasdevice_config *config;
+
+ for (i = 0; i < tas_fmw->n_configurations; i++) {
+ config = &(tas_fmw->configs[i]);
+ if (config) {
+ struct tasdevice_data
+ *im = &(config->dev_data);
+
+ if (!im->dev_blks)
+ continue;
+ for (n_blk = 0; n_blk < im->n_blk;
+ n_blk++) {
+ blk =
+ &(im->dev_blks[n_blk]);
+ kfree(blk->data);
+ }
+ kfree(im->dev_blks);
+ }
+ }
+ kfree(tas_fmw->configs);
+ }
+ kfree(tas_fmw);
+ tas_dev->fmw = NULL;
+ }
+}
+
+static int tas2781_set_calibration(void *context, enum channel i,
+ int n_calibration)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ int ret = 0;
+ struct tasdevice *tasdevice = &(tas_dev->tasdevice[i]);
+ struct tasdevice_fw *cal_fmw = tasdevice->cali_data_fmw;
+
+ if ((!tas_dev->fmw->programs)
+ || (!tas_dev->fmw->configs)) {
+ dev_err(tas_dev->dev, "%s, Firmware not loaded\n\r", __func__);
+ ret = 0;
+ goto out;
+ }
+
+ if (n_calibration == 0xFF || (n_calibration == 0x100
+ && tasdevice->is_calibrated_data_loaded == false)) {
+ if (cal_fmw) {
+ tasdevice->is_calibrated_data_loaded = false;
+ tas2781_clear_calfirmware(cal_fmw);
+ cal_fmw = NULL;
+ }
+
+ scnprintf(tas_dev->cal_binaryname[i], 64, "%s_cal_0x%02x.bin",
+ tas_dev->dev_name, tas_dev->tasdevice[i].dev_addr);
+ ret = tas2781_load_calibration(tas_dev,
+ tas_dev->cal_binaryname[i], i);
+ if (ret != 0) {
+ dev_err(tas_dev->dev,
+ "%s: load %s error, no-side effect\n",
+ __func__, tas_dev->cal_binaryname[i]);
+ ret = 0;
+ }
+ }
+ tasdevice->is_loading = true;
+ tasdevice->is_loaderr = false;
+
+ if (cal_fmw) {
+ struct tasdevice_calibration *calibration =
+ cal_fmw->calibrations;
+
+ if (calibration)
+ tasdevice_load_calibrated_data(tas_dev,
+ &(calibration->dev_data));
+ } else
+ dev_err(tas_dev->dev,
+ "%s: No calibrated data for device %d\n", __func__, i);
+
+out:
+ return ret;
+}
+
+int tasdevice_select_tuningprm_cfg(void *context, int prm_no,
+ int cfg_no, int rca_conf_no)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ struct tasdevice_rca *rca = &(tas_dev->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+ struct tasdevice_config *conf = NULL;
+ struct tasdevice_prog *program = NULL;
+ int i = 0;
+ int status = 0;
+ int prog_status = 0;
+
+ if (tas_fmw == NULL) {
+ dev_err(tas_dev->dev, "%s: Firmware is NULL\n", __func__);
+ goto out;
+ }
+
+ if (cfg_no >= tas_fmw->n_configurations) {
+ dev_err(tas_dev->dev,
+ "%s: cfg(%d) is not in range of conf %u\n",
+ __func__, cfg_no, tas_fmw->n_configurations);
+ goto out;
+ }
+
+ if (prm_no >= tas_fmw->n_programs) {
+ dev_err(tas_dev->dev,
+ "%s: prm(%d) is not in range of Programs %u\n",
+ __func__, prm_no, tas_fmw->n_programs);
+ goto out;
+ }
+
+ if (rca_conf_no > rca->ncfgs || rca_conf_no <= 0 ||
+ cfg_info == NULL) {
+ dev_err(tas_dev->dev,
+ "conf_no:%d should be in range from 0 to %u\n",
+ rca_conf_no, rca->ncfgs-1);
+ goto out;
+ } else {
+ dev_info(tas_dev->dev, "%s: rca_profile_conf_id = %d\n",
+ __func__, rca_conf_no);
+ }
+
+ conf = &(tas_fmw->configs[cfg_no]);
+ for (i = 0; i < tas_dev->ndev; i++) {
+ if (cfg_info[rca_conf_no]->active_dev & (1 << i)) {
+ if (tas_dev->tasdevice[i].cur_prog != prm_no) {
+ tas_dev->tasdevice[i].cur_conf = -1;
+ tas_dev->tasdevice[i].is_loading = true;
+ prog_status++;
+ }
+ } else
+ tas_dev->tasdevice[i].is_loading = false;
+ tas_dev->tasdevice[i].is_loaderr = false;
+ }
+
+ if (prog_status) {
+ program = &(tas_fmw->programs[prm_no]);
+ tasdevice_load_data(tas_dev, &(program->dev_data));
+ for (i = 0; i < tas_dev->ndev; i++) {
+ if (tas_dev->tasdevice[i].is_loaderr == true)
+ continue;
+ else if (tas_dev->tasdevice[i].is_loaderr == false
+ && tas_dev->tasdevice[i].is_loading == true) {
+ struct tasdevice_fw *cal_fmw =
+ tas_dev->tasdevice[i].cali_data_fmw;
+
+ if (cal_fmw) {
+ struct tasdevice_calibration
+ *calibration =
+ cal_fmw->calibrations;
+
+ if (calibration)
+ tasdevice_load_calibrated_data(
+ tas_dev,
+ &(calibration->dev_data));
+ }
+ tas_dev->tasdevice[i].cur_prog = prm_no;
+ }
+ }
+ }
+
+ if (tas_dev->is_glb_calibrated_data_loaded == false) {
+ for (i = 0; i < tas_dev->ndev; i++)
+ tas2781_set_calibration(tas_dev, i, 0x100);
+ tas_dev->is_glb_calibrated_data_loaded = true;
+ /* No wise to reload calibrationdata everytime,
+ * this code will work once even if calibrated
+ * data still failed to be got
+ */
+ }
+
+ for (i = 0; i < tas_dev->ndev; i++) {
+ dev_info(tas_dev->dev, "%s,fun %d,%d,%d\n", __func__,
+ tas_dev->tasdevice[i].cur_conf,
+ cfg_info[rca_conf_no]->active_dev,
+ tas_dev->tasdevice[i].is_loaderr);
+ if (tas_dev->tasdevice[i].cur_conf != cfg_no
+ && (cfg_info[rca_conf_no]->active_dev & (1 << i))
+ && (tas_dev->tasdevice[i].is_loaderr == false)) {
+ status++;
+ tas_dev->tasdevice[i].is_loading = true;
+ } else
+ tas_dev->tasdevice[i].is_loading = false;
+ }
+
+ if (status) {
+ status = 0;
+ tasdevice_load_data(tas_dev, &(conf->dev_data));
+ for (i = 0; i < tas_dev->ndev; i++) {
+ if (tas_dev->tasdevice[i].is_loaderr == true) {
+ status |= 1 << (i + 4);
+ continue;
+ } else if (tas_dev->tasdevice[i].is_loaderr == false
+ && tas_dev->tasdevice[i].is_loading == true)
+ tas_dev->tasdevice[i].cur_conf = cfg_no;
+ }
+ } else {
+ dev_err(tas_dev->dev,
+ "%s: No device is in active in conf %d\n",
+ __func__, rca_conf_no);
+ }
+
+ status |= cfg_info[rca_conf_no]->active_dev;
+ dev_info(tas_dev->dev, "%s: DSP mode: load status is %08x\n",
+ __func__, status);
+out:
+ return prog_status;
+}
diff --git a/sound/soc/codecs/tas2781-dsp.h b/sound/soc/codecs/tas2781-dsp.h
new file mode 100644
index 000000000000..639e84e0b5b4
--- /dev/null
+++ b/sound/soc/codecs/tas2781-dsp.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+ *
+ * Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+ * https://www.ti.com
+ *
+ * The TAS2781 driver implements a flexible and configurable algo coff setting
+ * for one, two, even multiple TAS2781 chips.
+ *
+ * Author: Shenghao Ding <shenghao-ding(a)ti.com>
+ * Kevin Lu <kevin-lu(a)ti.com>
+ */
+
+#ifndef __TASDEVICE_DSP_H__
+#define __TASDEVICE_DSP_H__
+
+#define MAIN_ALL_DEVICES 0x0d
+#define MAIN_DEVICE_A 0x01
+#define MAIN_DEVICE_B 0x08
+#define MAIN_DEVICE_C 0x10
+#define MAIN_DEVICE_D 0x14
+#define COEFF_DEVICE_A 0x03
+#define COEFF_DEVICE_B 0x0a
+#define COEFF_DEVICE_C 0x11
+#define COEFF_DEVICE_D 0x15
+#define PRE_DEVICE_A 0x04
+#define PRE_DEVICE_B 0x0b
+#define PRE_DEVICE_C 0x12
+#define PRE_DEVICE_D 0x16
+
+#define PPC3_VERSION 0x4100
+#define RCA_CONFIGID_BYPASS_ALL 0
+#define TASDEVICE_DEVICE_SUM 8
+#define TASDEVICE_CONFIG_SUM 64
+
+enum channel {
+ top_left_Chn,
+ top_right_chn,
+ bottom_left_Chn,
+ bottom_right_chn,
+ max_chn,
+};
+
+enum tasdevice_dsp_dev_idx {
+ TASDEVICE_DSP_TAS_2555 = 0,
+ TASDEVICE_DSP_TAS_2555_STEREO,
+ TASDEVICE_DSP_TAS_2557_MONO,
+ TASDEVICE_DSP_TAS_2557_DUAL_MONO,
+ TASDEVICE_DSP_TAS_2559,
+ TASDEVICE_DSP_TAS_2563,
+ TASDEVICE_DSP_TAS_2563_DUAL_MONO = 7,
+ TASDEVICE_DSP_TAS_2563_QUAD,
+ TASDEVICE_DSP_TAS_2563_21,
+ TASDEVICE_DSP_TAS_2781,
+ TASDEVICE_DSP_TAS_2781_DUAL_MONO,
+ TASDEVICE_DSP_TAS_2781_21,
+ TASDEVICE_DSP_TAS_2781_QUAD,
+ TASDEVICE_DSP_TAS_MAX_DEVICE
+};
+
+struct tasdevice_fw_fixed_hdr {
+ unsigned int fwsize;
+ unsigned int ppcver;
+ unsigned int drv_ver;
+};
+
+struct tasdevice_dspfw_hdr {
+ struct tasdevice_fw_fixed_hdr fixed_hdr;
+ unsigned short device_family;
+ unsigned short device;
+ unsigned char ndev;
+};
+
+struct tasdev_blk {
+ unsigned int type;
+ unsigned char is_pchksum_present;
+ unsigned char pchksum;
+ unsigned char is_ychksum_present;
+ unsigned char ychksum;
+ unsigned int n_cmds;
+ unsigned int blk_size;
+ unsigned int n_subblocks;
+ unsigned char *data;
+};
+
+struct tasdevice_data {
+ char name[64];
+ unsigned int n_blk;
+ struct tasdev_blk *dev_blks;
+};
+struct tasdevice_prog {
+ unsigned int prog_size;
+ struct tasdevice_data dev_data;
+};
+
+struct tasdevice_config {
+ unsigned int cfg_size;
+ char name[64];
+ struct tasdevice_data dev_data;
+};
+
+struct tasdevice_calibration {
+ struct tasdevice_data dev_data;
+};
+
+struct tasdevice_fw {
+ struct tasdevice_dspfw_hdr fw_hdr;
+ unsigned short n_programs;
+ struct tasdevice_prog *programs;
+ unsigned short n_configurations;
+ struct tasdevice_config *configs;
+ unsigned short n_calibrations;
+ struct tasdevice_calibration *calibrations;
+ struct device *dev;
+};
+
+enum tasdevice_dsp_fw_state {
+ TASDEVICE_DSP_FW_NONE = 0,
+ TASDEVICE_DSP_FW_PENDING,
+ TASDEVICE_DSP_FW_FAIL,
+ TASDEVICE_DSP_FW_ALL_OK,
+};
+
+enum tasdevice_bin_blk_type {
+ TASDEVICE_BIN_BLK_COEFF = 1,
+ TASDEVICE_BIN_BLK_POST_POWER_UP,
+ TASDEVICE_BIN_BLK_PRE_SHUTDOWN,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP,
+ TASDEVICE_BIN_BLK_POST_SHUTDOWN
+};
+
+struct tasdevice_rca_hdr {
+ unsigned int img_sz;
+ unsigned int checksum;
+ unsigned int binary_version_num;
+ unsigned int drv_fw_version;
+ unsigned char plat_type;
+ unsigned char dev_family;
+ unsigned char reserve;
+ unsigned char ndev;
+ unsigned char devs[TASDEVICE_DEVICE_SUM];
+ unsigned int nconfig;
+ unsigned int config_size[TASDEVICE_CONFIG_SUM];
+};
+
+struct tasdev_blk_data {
+ unsigned char dev_idx;
+ unsigned char block_type;
+ unsigned short yram_checksum;
+ unsigned int block_size;
+ unsigned int n_subblks;
+ unsigned char *regdata;
+};
+
+struct tasdevice_config_info {
+ unsigned int nblocks;
+ unsigned int real_nblocks;
+ unsigned char active_dev;
+ struct tasdev_blk_data **blk_data;
+};
+
+struct tasdevice_rca {
+ struct tasdevice_rca_hdr fw_hdr;
+ int ncfgs;
+ struct tasdevice_config_info **cfg_info;
+ int profile_cfg_id;
+};
+
+int tasdevice_dspfw_ready(const struct firmware *fmw, void *context);
+void tasdevice_dsp_remove(void *context);
+void tasdevice_calbin_remove(void *context);
+int tas2781_load_calibration(void *tas_dev, char *filename,
+ enum channel i);
+int tasdevice_select_tuningprm_cfg(void *context, int prm,
+ int cfg_no, int rca_conf_no);
+
+#endif
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
new file mode 100644
index 000000000000..2b2b8367044f
--- /dev/null
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -0,0 +1,1913 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+ *
+ * Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+ * https://www.ti.com
+ *
+ * The TAS2781 driver implements a flexible and configurable algo coff setting
+ * for one, two, even multiple TAS2781 chips.
+ *
+ * Author: Shenghao Ding <shenghao-ding(a)ti.com>
+ * 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 the 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[] = {
+ "topleft-channel",
+ "topright-channel",
+ "bottomleft-channel",
+ "bottomright-channel"
+};
+
+static const struct i2c_device_id tasdevice_id[] = {
+ { "tas2781", TAS2781 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tasdevice_id);
+
+static const struct regmap_range_cfg tasdevice_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 256 * 128,
+ .selector_reg = TASDEVICE_PAGE_SELECT,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config tasdevice_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .ranges = tasdevice_ranges,
+ .num_ranges = ARRAY_SIZE(tasdevice_ranges),
+ .max_register = 256 * 128,
+};
+
+static const struct of_device_id tasdevice_of_match[] = {
+ { .compatible = "ti,tas2781" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, tasdevice_of_match);
+
+int tasdevice_process_block(void *context,
+ unsigned char *data, unsigned char dev_idx, int sublocksize)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *)context;
+ unsigned char subblk_typ = data[1];
+ int subblk_offset = 2;
+ int chn = 0, chnend = 0;
+ int rc = 0;
+ int blktyp = dev_idx & 0xC0, idx = dev_idx & 0x3F;
+ bool is_err = false;
+
+ if (idx) {
+ chn = idx-1;
+ chnend = idx;
+ } else {
+ if (tas_dev->set_global_mode) {
+ chn = tas_dev->ndev;
+ chnend = tas_dev->ndev + 1;
+ } else {
+ chn = 0;
+ chnend = tas_dev->ndev;
+ }
+ }
+
+ for (; chn < chnend; chn++) {
+ if (tas_dev->set_global_mode == NULL &&
+ tas_dev->tasdevice[chn].is_loading == false)
+ continue;
+
+ is_err = false;
+ subblk_offset = 2;
+ switch (subblk_typ) {
+ case TASDEVICE_CMD_SING_W: {
+ int i = 0;
+ unsigned short len = SMS_HTONS(data[2], data[3]);
+
+ subblk_offset += 2;
+ if (subblk_offset + 4 * len > sublocksize) {
+ dev_err(tas_dev->dev,
+ "process_block: Out of memory\n");
+ is_err = true;
+ break;
+ }
+
+ for (i = 0; i < len; i++) {
+ rc = tasdevice_dev_write(tas_dev, chn,
+ TASDEVICE_REG(data[subblk_offset],
+ data[subblk_offset + 1],
+ data[subblk_offset + 2]),
+ data[subblk_offset + 3]);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_dev->dev,
+ "process_block: single write error\n");
+ }
+ subblk_offset += 4;
+ }
+ }
+ break;
+ case TASDEVICE_CMD_BURST: {
+ unsigned short len =
+ SMS_HTONS(data[2], data[3]);
+
+ subblk_offset += 2;
+ if (subblk_offset + 4 + len > sublocksize) {
+ dev_err(tas_dev->dev,
+ "%s: BURST Out of memory\n", __func__);
+ is_err = true;
+ break;
+ }
+ if (len % 4) {
+ dev_err(tas_dev->dev,
+ "%s:Bst-len(%u)not div by 4\n",
+ __func__, len);
+ break;
+ }
+
+ rc = tasdevice_dev_bulk_write(tas_dev, chn,
+ TASDEVICE_REG(data[subblk_offset],
+ data[subblk_offset + 1],
+ data[subblk_offset + 2]),
+ &(data[subblk_offset + 4]),
+ len);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_dev->dev,
+ "%s: bulk_write error = %d\n",
+ __func__, rc);
+ }
+ subblk_offset += (len + 4);
+ }
+ break;
+ case TASDEVICE_CMD_DELAY: {
+ unsigned short delay_time = 0;
+
+ if (subblk_offset + 2 > sublocksize) {
+ dev_err(tas_dev->dev,
+ "%s: deley Out of memory\n",
+ __func__);
+ is_err = true;
+ break;
+ }
+ delay_time = SMS_HTONS(data[2], data[3]);
+ usleep_range(delay_time*1000, delay_time*1000);
+ subblk_offset += 2;
+ }
+ break;
+ case TASDEVICE_CMD_FIELD_W:
+ if (subblk_offset + 6 > sublocksize) {
+ dev_err(tas_dev->dev,
+ "%s: bit write Out of memory\n",
+ __func__);
+ is_err = true;
+ break;
+ }
+ rc = tasdevice_dev_update_bits(tas_dev, chn,
+ TASDEVICE_REG(data[subblk_offset + 2],
+ data[subblk_offset + 3],
+ data[subblk_offset + 4]),
+ data[subblk_offset + 1],
+ data[subblk_offset + 5]);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_dev->dev,
+ "%s: update_bits error = %d\n",
+ __func__, rc);
+ }
+ subblk_offset += 6;
+ break;
+ default:
+ break;
+ };
+ if (is_err == true && blktyp != 0) {
+ if (blktyp == 0x80) {
+ tas_dev->tasdevice[chn].cur_prog = -1;
+ tas_dev->tasdevice[chn].cur_conf = -1;
+ } else
+ tas_dev->tasdevice[chn].cur_conf = -1;
+ }
+ }
+ return subblk_offset;
+}
+
+static void tasdevice_select_cfg_blk(void *pContext, int conf_no,
+ unsigned char block_type)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *) pContext;
+ struct tasdevice_rca *rca = &(tas_dev->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ int j = 0, k = 0, chn = 0, chnend = 0;
+
+ if (conf_no >= rca->ncfgs || conf_no < 0 || NULL == cfg_info) {
+ dev_err(tas_dev->dev,
+ "conf_no should be not more than %u\n",
+ rca->ncfgs);
+ goto out;
+ } 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,
+ "%s: conf %d, block type:%s\t device idx = 0x%02x\n",
+ __func__, 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]
+ ->n_subblks; k++) {
+ if (cfg_info[conf_no]->blk_data[j]->dev_idx) {
+ chn =
+ cfg_info[conf_no]->blk_data[j]->dev_idx
+ - 1;
+ chnend =
+ cfg_info[conf_no]->blk_data[j]->dev_idx;
+ } else {
+ chn = 0;
+ chnend = tas_dev->ndev;
+ }
+ for (; chn < chnend; chn++)
+ tas_dev->tasdevice[chn].is_loading = true;
+
+ rc = tasdevice_process_block(tas_dev,
+ cfg_info[conf_no]->blk_data[j]->regdata +
+ length,
+ cfg_info[conf_no]->blk_data[j]->dev_idx,
+ cfg_info[conf_no]->blk_data[j]->block_size -
+ length);
+ length += rc;
+ if (cfg_info[conf_no]->blk_data[j]->block_size <
+ length) {
+ dev_err(tas_dev->dev,
+ "%s: ERROR:%u %u out of memory\n",
+ __func__, length,
+ cfg_info[conf_no]->blk_data[j]
+ ->block_size);
+ break;
+ }
+ }
+ if (length != cfg_info[conf_no]->blk_data[j]->block_size)
+ dev_err(tas_dev->dev,
+ "%s: %u %u size is not same\n",
+ __func__, length,
+ cfg_info[conf_no]->blk_data[j]->block_size);
+
+ }
+
+out:
+ return;
+}
+
+static struct tasdevice_config_info *tasdevice_add_config(
+ void *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->rcabin.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;
+ }
+ 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 tasdev_blk_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 tasdev_blk_data), GFP_KERNEL);
+ if (!cfg_info->blk_data[i])
+ break;
+
+ cfg_info->blk_data[i]->dev_idx = config_data[config_offset];
+ config_offset++;
+
+ cfg_info->blk_data[i]->block_type = config_data[config_offset];
+ config_offset++;
+
+ if (cfg_info->blk_data[i]->block_type ==
+ TASDEVICE_BIN_BLK_PRE_POWER_UP) {
+ if (cfg_info->blk_data[i]->dev_idx == 0) {
+ cfg_info->active_dev = 1;
+ } else {
+ cfg_info->active_dev =
+ 1 <<
+ (cfg_info->blk_data[i]->dev_idx - 1);
+ }
+ }
+ cfg_info->blk_data[i]->yram_checksum =
+ SMS_HTONS(config_data[config_offset],
+ config_data[config_offset + 1]);
+ config_offset += 2;
+ cfg_info->blk_data[i]->block_size =
+ SMS_HTONL(config_data[config_offset],
+ config_data[config_offset + 1],
+ config_data[config_offset + 2],
+ config_data[config_offset + 3]);
+ config_offset += 4;
+
+ cfg_info->blk_data[i]->n_subblks =
+ SMS_HTONL(config_data[config_offset],
+ config_data[config_offset + 1],
+ config_data[config_offset + 2],
+ config_data[config_offset + 3]);
+
+ config_offset += 4;
+ cfg_info->blk_data[i]->regdata = kzalloc(
+ cfg_info->blk_data[i]->block_size, GFP_KERNEL);
+ if (cfg_info->blk_data[i]->regdata == 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,
+ "%s: block_size Out of memory: i = %d nblocks = %u!\n",
+ __func__, i,
+ cfg_info->nblocks);
+ break;
+ }
+ memcpy(cfg_info->blk_data[i]->regdata,
+ &config_data[config_offset],
+ cfg_info->blk_data[i]->block_size);
+ config_offset += cfg_info->blk_data[i]->block_size;
+ cfg_info->real_nblocks += 1;
+ }
+out:
+ return cfg_info;
+}
+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;
+ 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->set_global_mode != NULL) {
+ ret = tasdevice_dev_write(tas_dev, tas_dev->ndev,
+ mc->reg, (unsigned int)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, (unsigned int)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] = (int)val;
+
+out:
+ return ret;
+}
+
+static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int val;
+ int i, ret = 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, (unsigned int)(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->rcabin.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 tasdevice_fw *tas_fw = p_tasdevice->fmw;
+
+ 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)tas_fw->n_programs;
+ /* 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 tasdevice_fw *tas_fw = p_tasdevice->fmw;
+
+ 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)tas_fw->n_configurations - 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 = 1;
+ uinfo->value.integer.max = max(0, p_tasdevice->rcabin.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->rcabin.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->cur_prog;
+ 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->cur_prog = 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->cur_conf;
+ 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 *tas_dev = snd_soc_component_get_drvdata(codec);
+ unsigned int n_configuration = pValue->value.integer.value[0];
+
+ mutex_lock(&tas_dev->codec_lock);
+ tas_dev->cur_conf = n_configuration;
+ mutex_unlock(&tas_dev->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;
+}
+
+static void tasdevice_rca_ready(const struct firmware *fmw,
+ void *context)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *) context;
+ struct tasdevice_rca *rca = NULL;
+ struct tasdevice_rca_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;
+
+ mutex_lock(&tas_dev->codec_lock);
+ rca = &(tas_dev->rcabin);
+ fw_hdr = &(rca->fw_hdr);
+ if (unlikely(!fmw) || unlikely(!fmw->data)) {
+ dev_err(tas_dev->dev,
+ "Failed to read %s, no side - effect on driver running\n",
+ tas_dev->rca_binaryname);
+ ret = -1;
+ goto out;
+ }
+ buf = (unsigned char *)fmw->data;
+
+ dev_info(tas_dev->dev, "tasdev: rca_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 != fmw->size) {
+ dev_err(tas_dev->dev,
+ "File size not match, %d %u", (int)fmw->size,
+ fw_hdr->img_sz);
+ ret = -1;
+ goto out;
+ }
+
+ fw_hdr->checksum = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ fw_hdr->binary_version_num = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->binary_version_num < 0x103) {
+ dev_err(tas_dev->dev,
+ "File version 0x%04x is too low",
+ fw_hdr->binary_version_num);
+ ret = -1;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->drv_fw_version = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 8;
+ fw_hdr->plat_type = buf[offset];
+ offset += 1;
+ fw_hdr->dev_family = buf[offset];
+ offset += 1;
+ fw_hdr->reserve = buf[offset];
+ offset += 1;
+ fw_hdr->ndev = buf[offset];
+ offset += 1;
+ if (fw_hdr->ndev != tas_dev->ndev) {
+ dev_err(tas_dev->dev,
+ "ndev(%u) from rcabin 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,
+ "rca_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;
+ }
+ rca->cfg_info = cfg_info;
+ rca->ncfgs = 0;
+ for (i = 0; i < (int)fw_hdr->nconfig; i++) {
+ cfg_info[i] = tasdevice_add_config(context, &buf[offset],
+ fw_hdr->config_size[i]);
+ if (!cfg_info[i]) {
+ ret = -1;
+ dev_err(tas_dev->dev,
+ "add_config Memory alloc failed!\n");
+ break;
+ }
+ offset += (int)fw_hdr->config_size[i];
+ rca->ncfgs += 1;
+ }
+ tasdevice_create_controls(tas_dev);
+
+ tasdevice_dsp_remove(tas_dev);
+ tasdevice_calbin_remove(tas_dev);
+ tas_dev->fw_state = TASDEVICE_DSP_FW_PENDING;
+ scnprintf(tas_dev->coef_binaryname, 64, "%s_coef.bin",
+ tas_dev->dev_name);
+ ret = request_firmware(&fw_entry, tas_dev->coef_binaryname,
+ tas_dev->dev);
+ if (!ret) {
+ ret = tasdevice_dspfw_ready(fw_entry, tas_dev);
+ release_firmware(fw_entry);
+ fw_entry = NULL;
+ } else {
+ tas_dev->fw_state = TASDEVICE_DSP_FW_FAIL;
+ dev_err(tas_dev->dev, "%s: load %s error\n", __func__,
+ tas_dev->coef_binaryname);
+ goto out;
+ }
+ tasdevice_dsp_create_control(tas_dev);
+
+ tas_dev->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+ tas_dev->is_glb_calibrated_data_loaded = true;
+ for (i = 0; i < tas_dev->ndev; i++) {
+ scnprintf(tas_dev->cal_binaryname[i], 64, "%s_cal_0x%02x.bin",
+ tas_dev->dev_name, tas_dev->tasdevice[i].dev_addr);
+ ret = tas2781_load_calibration(tas_dev,
+ tas_dev->cal_binaryname[i], i);
+ if (ret != 0) {
+ dev_err(tas_dev->dev,
+ "%s: load %s error, no-side effect\n",
+ __func__,
+ tas_dev->cal_binaryname[i]);
+ ret = 0;
+ tas_dev->is_glb_calibrated_data_loaded = false;
+ }
+ }
+
+out:
+ mutex_unlock(&tas_dev->codec_lock);
+ if (fmw)
+ release_firmware(fmw);
+ dev_info(tas_dev->dev, "Firmware init complete\n");
+}
+
+static void tasdevice_config_info_remove(void *context)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *) context;
+ struct tasdevice_rca *rca = &(tas_dev->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ int i = 0, j = 0;
+
+ mutex_lock(&tas_dev->dev_lock);
+ if (cfg_info) {
+ for (i = 0; i < rca->ncfgs; i++) {
+ if (cfg_info[i]) {
+ for (j = 0; j < (int)cfg_info[i]->real_nblocks;
+ j++) {
+ kfree(
+ cfg_info[i]->blk_data[j]->regdata);
+ kfree(cfg_info[i]->blk_data[j]);
+ }
+ kfree(cfg_info[i]->blk_data);
+ kfree(cfg_info[i]);
+ }
+ }
+ kfree(cfg_info);
+ }
+ mutex_unlock(&tas_dev->dev_lock);
+}
+
+static void tasdevice_enable_irq(
+ struct tasdevice_priv *tas_dev, bool enable)
+{
+ if (enable) {
+ if (tas_dev->irq_info.is_irq_enable)
+ return;
+ if (gpio_is_valid(tas_dev->irq_info.irq_gpio))
+ enable_irq(tas_dev->irq_info.irq);
+ tas_dev->irq_info.is_irq_enable = true;
+ } else {
+ if (gpio_is_valid(tas_dev->irq_info.irq_gpio))
+ disable_irq_nosync(tas_dev->irq_info.irq);
+ tas_dev->irq_info.is_irq_enable = false;
+ }
+}
+
+static void tasdevice_set_power_state(
+ struct tasdevice_priv *tas_dev, int state)
+{
+ switch (state) {
+ case 0:
+ schedule_delayed_work(&tas_dev->tuning_switch_wrk,
+ msecs_to_jiffies(20));
+ break;
+ default:
+ if (!(tas_dev->pstream || tas_dev->cstream)) {
+ if (tas_dev->irq_work_func)
+ tasdevice_enable_irq(tas_dev, false);
+ tasdevice_select_cfg_blk(tas_dev,
+ tas_dev->rcabin.profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_SHUTDOWN);
+ }
+ break;
+ }
+}
+
+static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+ int stream;
+
+ /* Codec Lock Hold */
+ mutex_lock(&tas_dev->codec_lock);
+ stream = strcmp(w->name, "SPK") ? SNDRV_PCM_STREAM_PLAYBACK :
+ SNDRV_PCM_STREAM_CAPTURE;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dev_info(tas_dev->dev, "SND_SOC_DAPM_POST_PMU\n");
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tas_dev->pstream = 1;
+ else
+ tas_dev->cstream = 1;
+ tasdevice_set_power_state(tas_dev, 0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ dev_info(tas_dev->dev, "SND_SOC_DAPM_PRE_PMD\n");
+ /* 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;
+ tasdevice_set_power_state(tas_dev, 1);
+ break;
+ }
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_dev->codec_lock);
+out:
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM,
+ 0, 0, tasdevice_dapm_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_INPUT("DMIC")
+};
+
+static const struct snd_soc_dapm_route tasdevice_audio_map[] = {
+ {"SPK", NULL, "ASI"},
+ {"OUT", NULL, "SPK"},
+ {"ASI OUT", NULL, "DMIC"}
+};
+
+static int tasdevice_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+ int ret = 0;
+
+ if (tas_dev->fw_state != TASDEVICE_DSP_FW_ALL_OK) {
+ dev_err(tas_dev->dev, "DSP bin file not loaded\n");
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int tasdevice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct tasdevice_priv *tas_dev = snd_soc_dai_get_drvdata(dai);
+ unsigned int fsrate;
+ unsigned int slot_width;
+ int bclk_rate;
+ int rc = 0;
+
+ 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 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 tuning_switch_routine(struct work_struct *work)
+{
+ struct tasdevice_priv *tas_dev =
+ container_of(work, struct tasdevice_priv,
+ tuning_switch_wrk.work);
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+ int profile_cfg_id = 0;
+ int is_set_glb_mode = 0;
+
+ mutex_lock(&tas_dev->codec_lock);
+
+ if (tas_fmw) {
+ if (tas_dev->cur_prog >= tas_fmw->n_programs)
+ /*bypass all in rca is profile id 0*/
+ profile_cfg_id = RCA_CONFIGID_BYPASS_ALL;
+ else {
+ /*dsp mode or tuning mode*/
+ profile_cfg_id = tas_dev->rcabin.profile_cfg_id;
+ tas_fmw = tas_dev->fmw;
+ dev_info(tas_dev->dev, "%s: %s\n", __func__,
+ tas_fmw->configs[tas_dev->cur_conf].name);
+ is_set_glb_mode =
+ tasdevice_select_tuningprm_cfg(tas_dev,
+ tas_dev->cur_prog,
+ tas_dev->cur_conf,
+ profile_cfg_id);
+ if (tas_dev->set_global_mode != NULL)
+ tas_dev->set_global_mode(tas_dev);
+ }
+ } else
+ profile_cfg_id = RCA_CONFIGID_BYPASS_ALL;
+
+ tasdevice_select_cfg_blk(tas_dev, profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+
+ if (is_set_glb_mode && tas_dev->set_global_mode)
+ tas_dev->set_global_mode(tas_dev);
+ if (tas_dev->irq_work_func)
+ tasdevice_enable_irq(tas_dev, true);
+ mutex_unlock(&tas_dev->codec_lock);
+}
+
+static const struct snd_soc_dai_ops tasdevice_dai_ops = {
+ .startup = tasdevice_startup,
+ .hw_params = tasdevice_hw_params,
+ .set_sysclk = tasdevice_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver tasdevice_dai_driver[] = {
+ {
+ .name = "tas2781_codec",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TASDEVICE_RATES,
+ .formats = TASDEVICE_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TASDEVICE_RATES,
+ .formats = TASDEVICE_FORMATS,
+ },
+ .ops = &tasdevice_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static void tas2781_reset(struct tasdevice_priv *tas_dev)
+{
+ int ret = 0;
+ int i = 0;
+
+ 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 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->rca_binaryname, 64, "%s_rca.bin",
+ tas_dev->dev_name);
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+ tas_dev->rca_binaryname, tas_dev->dev, GFP_KERNEL, tas_dev,
+ tasdevice_rca_ready);
+ if (ret)
+ dev_err(tas_dev->dev,
+ "%s: request_firmware_nowait error:0x%08x\n",
+ __func__, ret);
+
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_dev->codec_lock);
+
+ tas2781_reset(tas_dev);
+ if (tas_dev->set_global_mode != NULL)
+ tas_dev->set_global_mode(tas_dev);
+
+ return ret;
+}
+
+static void tasdevice_deinit(void *context)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+
+ tasdevice_config_info_remove(tas_dev);
+ tasdevice_dsp_remove(tas_dev);
+ tasdevice_calbin_remove(tas_dev);
+ tas_dev->fw_state = TASDEVICE_DSP_FW_PENDING;
+}
+
+static void tasdevice_codec_remove(
+ struct snd_soc_component *codec)
+{
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ /* Codec Lock Hold */
+ mutex_lock(&tas_dev->codec_lock);
+ tasdevice_deinit(tas_dev);
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_dev->codec_lock);
+}
+
+static const struct snd_soc_component_driver
+ soc_codec_driver_tasdevice = {
+ .probe = tasdevice_codec_probe,
+ .remove = tasdevice_codec_remove,
+ .controls = tas2781_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2781_snd_controls),
+ .dapm_widgets = tasdevice_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tasdevice_dapm_widgets),
+ .dapm_routes = tasdevice_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tasdevice_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static void irq_work_routine(struct work_struct *pWork)
+{
+ struct tasdevice_priv *tas_dev =
+ container_of(pWork, struct tasdevice_priv,
+ irq_info.irq_work.work);
+
+ mutex_lock(&tas_dev->codec_lock);
+ if (tas_dev->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->irq_info.irq_work,
+ msecs_to_jiffies(100));
+ return IRQ_HANDLED;
+}
+
+static 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;
+ unsigned int dev_addrs[max_chn];
+ struct i2c_client *client = (struct i2c_client *)tas_dev->client;
+
+ ndev = of_property_read_variable_u32_array(np, "ti,audio-slots",
+ dev_addrs, 0, ARRAY_SIZE(dev_addrs));
+ if (ndev <= 0) {
+ ndev = 1;
+ dev_addrs[0] = client->addr;
+ }
+ for (i = 0; i < ndev; i++) {
+ tas_dev->tasdevice[i].dev_addr = dev_addrs[i];
+ dev_info(tas_dev->dev, "%s=0x%02x",
+ dts_dev_addr_tag[i],
+ tas_dev->tasdevice[i].dev_addr);
+ }
+ if (ndev > 1) {
+ rc = of_property_read_u32(np, "ti,broadcast-addr",
+ &(tas_dev->glb_addr.dev_addr));
+ if (rc) {
+ dev_err(tas_dev->dev,
+ "Looking up node %s failed %d\n",
+ np->full_name, rc);
+ tas_dev->glb_addr.dev_addr = 0;
+ }
+ }
+
+ tas_dev->ndev = ndev;
+
+ tas_dev->rst_gpio = of_get_named_gpio(np, "reset-gpios", i);
+ if (gpio_is_valid(tas_dev->rst_gpio)) {
+ rc = gpio_request(tas_dev->rst_gpio, "reset");
+ if (!rc) {
+ gpio_direction_output(tas_dev->rst_gpio, 1);
+ dev_info(tas_dev->dev, "reset-gpio = %d",
+ tas_dev->rst_gpio);
+ ndev++;
+ } else
+ dev_err(tas_dev->dev,
+ "%s: Failed to request gpio %d\n",
+ __func__, tas_dev->rst_gpio);
+ } else
+ dev_err(tas_dev->dev,
+ "Looking up node %s failed %d\n",
+ np->full_name,
+ tas_dev->rst_gpio);
+
+ strcpy(tas_dev->dev_name, tasdevice_id[tas_dev->chip_id].name);
+
+ tas_dev->irq_info.irq_gpio = of_get_named_gpio(np,
+ "interrupts", 0);
+ if (gpio_is_valid(tas_dev->irq_info.irq_gpio)) {
+ dev_dbg(tas_dev->dev, "interrupts gpio = %d",
+ tas_dev->irq_info.irq_gpio);
+ INIT_DELAYED_WORK(&tas_dev->irq_info.irq_work,
+ irq_work_routine);
+
+ rc = gpio_request(tas_dev->irq_info.irq_gpio,
+ "AUDEV-IRQ");
+ if (!rc) {
+ gpio_direction_input(
+ tas_dev->irq_info.irq_gpio);
+
+ tas_dev->irq_info.irq =
+ gpio_to_irq(
+ tas_dev->irq_info.irq_gpio);
+ dev_info(tas_dev->dev,
+ "irq = %d\n",
+ tas_dev->irq_info.irq);
+
+ rc = request_threaded_irq(
+ tas_dev->irq_info.irq,
+ tasdevice_irq_handler,
+ NULL, IRQF_TRIGGER_FALLING|
+ IRQF_ONESHOT,
+ SMARTAMP_MODULE_NAME, tas_dev);
+ if (!rc)
+ disable_irq_nosync(
+ tas_dev->irq_info.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->irq_info.irq_gpio);
+ } else
+ dev_err(tas_dev->dev,
+ "Looking up irq-gpio property in node %s failed %d\n",
+ np->full_name,
+ tas_dev->irq_info.irq_gpio);
+
+ if (gpio_is_valid(tas_dev->irq_info.irq_gpio) && 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_change_chn_book(struct tasdevice_priv *tas_dev,
+ enum channel chn, int book)
+{
+ 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.cur_book = -1;
+ }
+ pClient->addr = tas_dev->tasdevice[chn].dev_addr;
+ if (tas_dev->tasdevice[chn].cur_book != book) {
+ n_result = regmap_write(tas_dev->regmap,
+ TASDEVICE_BOOKCTL_PAGE, 0);
+ if (n_result < 0) {
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ goto out;
+ }
+ n_result = regmap_write(tas_dev->regmap,
+ 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].cur_book = book;
+ }
+ } else if (chn == tas_dev->ndev) {
+ int i = 0;
+
+ if (tas_dev->glb_addr.ref_cnt == 0)
+ for (i = 0; i < tas_dev->ndev; i++)
+ tas_dev->tasdevice[i].cur_book = -1;
+ pClient->addr = tas_dev->glb_addr.dev_addr;
+ if (tas_dev->glb_addr.cur_book != book) {
+ n_result = regmap_write(tas_dev->regmap,
+ TASDEVICE_BOOKCTL_PAGE, 0);
+ if (n_result < 0) {
+ dev_err(tas_dev->dev,
+ "%s, 0ERROR, E=%d\n",
+ __func__, n_result);
+ goto out;
+ }
+ n_result = regmap_write(tas_dev->regmap,
+ 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.cur_book = book;
+ }
+
+ tas_dev->glb_addr.ref_cnt++;
+ } else
+ dev_err(tas_dev->dev,
+ "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+
+out:
+ return 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(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (n_result < 0)
+ goto out;
+
+ n_result = regmap_read(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), pValue);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR,E=%d\n",
+ __func__, n_result);
+ } 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(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (n_result < 0)
+ goto out;
+
+ n_result = regmap_write(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), value);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ } 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(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (n_result < 0)
+ goto out;
+
+ n_result = regmap_bulk_write(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), p_data, n_length);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ } 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(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (n_result < 0)
+ goto out;
+
+ n_result = regmap_bulk_read(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), p_data, n_length);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ } 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(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (n_result < 0)
+ goto out;
+ pClient->addr = tas_dev->tasdevice[chn].dev_addr;
+ n_result = regmap_update_bits(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), mask, value);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ } 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_set_global_mode(struct tasdevice_priv *tas_dev)
+{
+ int i = 0;
+ int ret = 0;
+
+ for (; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_update_bits(tas_dev, i,
+ TAS2871_MISC_CFG2, TAS2871_GLOBAL_ADDR_MASK,
+ TAS2871_GLOBAL_ADDR_ENABLE);
+ if (ret < 0) {
+ dev_err(tas_dev->dev,
+ "%s: chn %d set global fail, %d\n",
+ __func__, i, ret);
+ continue;
+ }
+ }
+}
+
+static int tasdevice_init(struct tasdevice_priv *tas_dev)
+{
+ int ret = 0, i = 0;
+
+ tas_dev->cur_prog = -1;
+ tas_dev->cur_conf = -1;
+
+ for (i = 0; i < tas_dev->ndev; i++) {
+ tas_dev->tasdevice[i].cur_book = -1;
+ tas_dev->tasdevice[i].cur_prog = -1;
+ tas_dev->tasdevice[i].cur_conf = -1;
+ }
+ mutex_init(&tas_dev->dev_lock);
+ if (tas_dev->glb_addr.dev_addr != 0
+ && tas_dev->glb_addr.dev_addr < 0x7F)
+ tas_dev->set_global_mode = tas2781_set_global_mode;
+ dev_set_drvdata(tas_dev->dev, tas_dev);
+
+ INIT_DELAYED_WORK(&tas_dev->tuning_switch_wrk,
+ tuning_switch_routine);
+
+ mutex_init(&tas_dev->codec_lock);
+ ret = devm_snd_soc_register_component(tas_dev->dev,
+ &soc_codec_driver_tasdevice,
+ tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver));
+ if (ret)
+ dev_err(tas_dev->dev, "%s: codec register error:0x%08x\n",
+ __func__, ret);
+
+ INIT_DELAYED_WORK(&tas_dev->irq_info.irq_work, irq_work_routine);
+ tas_dev->irq_info.is_irq_enable = false;
+
+ dev_info(tas_dev->dev, "i2c register success\n");
+
+ return ret;
+}
+
+static void tasdevice_remove(struct tasdevice_priv *tas_dev)
+{
+ if (gpio_is_valid(tas_dev->rst_gpio))
+ gpio_free(tas_dev->rst_gpio);
+
+ if (gpio_is_valid(tas_dev->irq_info.irq_gpio)) {
+ if (delayed_work_pending(&tas_dev->irq_info.irq_work)) {
+ dev_info(tas_dev->dev, "cancel IRQ work\n");
+ cancel_delayed_work(&tas_dev->irq_info.irq_work);
+ }
+ cancel_delayed_work_sync(&tas_dev->irq_info.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);
+
+ mutex_lock(&tas_dev->codec_lock);
+ tas_dev->runtime_suspend = true;
+
+ if (tas_dev->irq_work_func) {
+ if (delayed_work_pending(&tas_dev->irq_info.irq_work)) {
+ dev_dbg(tas_dev->dev, "cancel IRQ work\n");
+ cancel_delayed_work_sync(&tas_dev->irq_info.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);
+
+ mutex_lock(&tas_dev->codec_lock);
+ tas_dev->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 000000000000..ce9d3d162aa0
--- /dev/null
+++ b/sound/soc/codecs/tas2781.h
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+ *
+ * Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+ * https://www.ti.com
+ *
+ * The TAS2781 driver implements a flexible and configurable algo coff setting
+ * for one, two, even multiple TAS2781 chips.
+ *
+ * Author: Shenghao Ding <shenghao-ding(a)ti.com>
+ * Kevin Lu <kevin-lu(a)ti.com>
+ */
+
+#ifndef __TAS2781_H__
+#define __TAS2781_H__
+
+#include "tas2781-dsp.h"
+
+#define SMARTAMP_MODULE_NAME "tas2781"
+#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_PGRG(reg) ((reg % (256 * 128)))
+#define TASDEVICE_REG(book, page, reg) (((book * 256 * 128) + \
+ (page * 128)) + reg)
+
+ /*Software Reset */
+#define TAS2871_REG_SWRESET TASDEVICE_REG(0x0, 0X0, 0x02)
+#define TAS2871_REG_SWRESET_RESET BIT(0)
+
+ /* Enable Global addresses */
+#define TAS2871_MISC_CFG2 TASDEVICE_REG(0x0, 0X0, 0x07)
+#define TAS2871_GLOBAL_ADDR_MASK BIT(1)
+#define TAS2871_GLOBAL_ADDR_ENABLE BIT(1)
+
+ /*I2C Checksum */
+#define TASDEVICE_I2CChecksum TASDEVICE_REG(0x0, 0x0, 0x7E)
+
+ /* Volume control */
+#define TAS2781_DVC_LVL TASDEVICE_REG(0x0, 0x0, 0x1A)
+#define TAS2781_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x03)
+#define TAS2781_AMP_LEVEL_MASK GENMASK(5, 1)
+
+#define TASDEVICE_CMD_SING_W 0x1
+#define TASDEVICE_CMD_BURST 0x2
+#define TASDEVICE_CMD_DELAY 0x3
+#define TASDEVICE_CMD_FIELD_W 0x4
+
+enum audio_device {
+ TAS2781 = 0,
+};
+
+#define SMS_HTONS(a, b) ((((a)&0x00FF)<<8) | ((b)&0x00FF))
+#define SMS_HTONL(a, b, c, d) ((((a)&0x000000FF)<<24) | \
+ (((b)&0x000000FF)<<16) | (((c)&0x000000FF)<<8) | \
+ ((d)&0x000000FF))
+
+struct tasdevice {
+ unsigned int dev_addr;
+ unsigned int err_code;
+ short cur_prog;
+ short cur_conf;
+ bool is_loading;
+ bool is_loaderr;
+ bool is_calibrated_data_loaded;
+ unsigned char cur_book;
+ struct tasdevice_fw *cali_data_fmw;
+};
+
+/*
+ * This item is used to store the generic i2c address of
+ * all the tas2781 devices for I2C broadcast during the multi-device
+ * writes, useless in mono case.
+ */
+struct global_addr {
+ unsigned char cur_book;
+ unsigned int dev_addr;
+ int ref_cnt;
+};
+
+struct tas_control {
+ struct snd_kcontrol_new *tasdevice_profile_controls;
+ int nr_controls;
+};
+
+struct tasdevice_irqinfo {
+ int irq_gpio;
+ int irq;
+ struct delayed_work irq_work;
+ bool is_irq_enable;
+};
+
+struct tasdevice_priv {
+ struct device *dev;
+ void *client;
+ struct regmap *regmap;
+ struct mutex codec_lock;
+ struct mutex dev_lock;
+ int rst_gpio;
+ struct tasdevice tasdevice[max_chn];
+ struct tasdevice_fw *fmw;
+ struct tasdevice_rca rcabin;
+ struct tasdevice_irqinfo irq_info;
+ struct tas_control tas_ctrl;
+ struct global_addr glb_addr;
+ int cur_prog;
+ int cur_conf;
+ unsigned int chip_id;
+ void (*set_global_mode)(struct tasdevice_priv *tas_dev);
+ int (*fw_parse_variable_header)(struct tasdevice_priv *tas_dev,
+ const struct firmware *fmw, int offset);
+ int (*fw_parse_program_data)(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset);
+ int (*fw_parse_configuration_data)(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset);
+ int (*tasdevice_load_block)(struct tasdevice_priv *tas_dev,
+ struct tasdev_blk *pBlock);
+ int (*fw_parse_calibration_data)(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset);
+ 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 rca_binaryname[64];
+ unsigned char coef_binaryname[64];
+ unsigned char cal_binaryname[max_chn][64];
+ bool runtime_suspend;
+ struct delayed_work tuning_switch_wrk;
+ void *codec;
+ int sysclk;
+ int pstream;
+ int cstream;
+ bool is_glb_calibrated_data_loaded;
+};
+
+int tasdevice_dev_read(struct tasdevice_priv *tasdevice,
+ enum channel chn, unsigned int reg, unsigned int *value);
+int tasdevice_process_block(void *context,
+ unsigned char *data, unsigned char dev_idx, int sublocksize);
+int tasdevice_dev_write(struct tasdevice_priv *tasdevice,
+ enum channel chn, unsigned int reg, unsigned int value);
+
+int tasdevice_dev_bulk_write(
+ struct tasdevice_priv *tasdevice, enum channel chn,
+ unsigned int reg, unsigned char *p_data, unsigned int n_length);
+
+int tasdevice_dev_bulk_read(struct tasdevice_priv *tasdevice,
+ enum channel chn, unsigned int reg, unsigned char *p_data,
+ unsigned int n_length);
+
+int tasdevice_dev_update_bits(
+ struct tasdevice_priv *tasdevice, enum channel chn,
+ unsigned int reg, unsigned int mask, unsigned int value);
+
+#endif /*__TAS2781_H__ */
--
2.34.1
1
0

03 Feb '23
Hi Mark
This patch-set adds R-Car Gen4 support on Renesas sound driver.
To add it, it needs some cleanups / prepares which makes
easy to review the patch.
Kuninori Morimoto (15):
ASoC: rsnd: check whether playback/capture property exists
ASoC: rsnd: fixup #endif position
ASoC: rsnd: Remove unnecessary rsnd_dbg_dai_call()
ASoC: rsnd: indicate necessary error when clock start failed
ASoC: rsnd: indicate warning once if it can't handle requested rule
ASoC: rsnd: use same debug message format on clkout
ASoC: rsnd: remove unnecessary ADG flags
ASoC: rsnd: rename clk to clkin
ASoC: rsnd: moves clkout_name to top of the file
ASoC: rsnd: use clkin/out_size
ASoC: rsnd: use array for 44.1kHz/48kHz rate handling
ASoC: rsnd: tidyup rsnd_dma_addr()
ASoC: rsnd: dma.c: tidyup rsnd_dma_probe()
ASoC: dt-bindings: renesas: add R8A779G0 V4H
ASoC: rsnd: add R-Car Gen4 Sound support
.../bindings/sound/renesas,rsnd.yaml | 4 +
sound/soc/sh/rcar/adg.c | 169 ++++++++++--------
sound/soc/sh/rcar/core.c | 66 ++++---
sound/soc/sh/rcar/dma.c | 57 +++++-
sound/soc/sh/rcar/gen.c | 70 ++++++++
sound/soc/sh/rcar/rsnd.h | 23 ++-
sound/soc/sh/rcar/ssi.c | 13 +-
sound/soc/sh/rcar/ssiu.c | 15 +-
8 files changed, 289 insertions(+), 128 deletions(-)
--
2.25.1
4
20
I am trying to only use a static /dev. I have opened an issue about this on
GitHub and got it to work only as the root user. This resembles my
experience with xorg. In that case I had to use the suid use flag. Is there
an equivalent in this situation?
2
1

[PATCH] ASoC: mchp-spdifrx: Fix uninitialized use of mr in mchp_spdifrx_hw_params()
by Nathan Chancellor 02 Feb '23
by Nathan Chancellor 02 Feb '23
02 Feb '23
Clang warns:
../sound/soc/atmel/mchp-spdifrx.c:455:3: error: variable 'mr' is uninitialized when used here [-Werror,-Wuninitialized]
mr |= SPDIFRX_MR_ENDIAN_BIG;
^~
../sound/soc/atmel/mchp-spdifrx.c:432:8: note: initialize the variable 'mr' to silence this warning
u32 mr;
^
= 0
1 error generated.
Zero initialize mr so that these bitwise OR and assignment operation
works unconditionally.
Fixes: fa09fa60385a ("ASoC: mchp-spdifrx: fix controls which rely on rsr register")
Link: https://github.com/ClangBuiltLinux/linux/issues/1797
Signed-off-by: Nathan Chancellor <nathan(a)kernel.org>
---
sound/soc/atmel/mchp-spdifrx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c
index 526dd3a17471..eb0c0ef4541e 100644
--- a/sound/soc/atmel/mchp-spdifrx.c
+++ b/sound/soc/atmel/mchp-spdifrx.c
@@ -429,7 +429,7 @@ static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
- u32 mr;
+ u32 mr = 0;
int ret;
dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
---
base-commit: 1d78f19d90d1932c83d8c96c6f86fbb06c05e29a
change-id: 20230202-mchp-spdifrx-fix-uninit-mr-80528f2024c9
Best regards,
--
Nathan Chancellor <nathan(a)kernel.org>
3
2

[PATCH] ASoC: SOF: amd: Fix for handling spurious interrupts from DSP
by V sujith kumar Reddy 02 Feb '23
by V sujith kumar Reddy 02 Feb '23
02 Feb '23
As interrupts are Level-triggered,unless and until we deassert the register
the interrupts are generated which causes spurious interrupts unhandled.
Now we deasserted the interrupt at top half which solved the below
"nobody cared" warning.
warning reported in dmesg:
irq 80: nobody cared (try booting with the "irqpoll" option)
CPU: 5 PID: 2735 Comm: irq/80-AudioDSP
Not tainted 5.15.86-15817-g4c19f3e06d49 #1 1bd3fd932cf58caacc95b0504d6ea1e3eab22289
Hardware name: Google Skyrim/Skyrim, BIOS Google_Skyrim.15303.0.0 01/03/2023
Call Trace:
<IRQ>
dump_stack_lvl+0x69/0x97
__report_bad_irq+0x3a/0xae
note_interrupt+0x1a9/0x1e3
handle_irq_event_percpu+0x4b/0x6e
handle_irq_event+0x36/0x5b
handle_fasteoi_irq+0xae/0x171
__common_interrupt+0x48/0xc4
</IRQ>
handlers:
acp_irq_handler [snd_sof_amd_acp] threaded [<000000007e089f34>] acp_irq_thread [snd_sof_amd_acp]
Disabling IRQ #80
Signed-off-by: V sujith kumar Reddy <Vsujithkumar.Reddy(a)amd.com>
---
sound/soc/sof/amd/acp.c | 36 +++++++++++++++---------------------
1 file changed, 15 insertions(+), 21 deletions(-)
diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c
index 6bd2888fbb66..d5ccd4d09278 100644
--- a/sound/soc/sof/amd/acp.c
+++ b/sound/soc/sof/amd/acp.c
@@ -318,7 +318,6 @@ static irqreturn_t acp_irq_thread(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
- unsigned int base = desc->dsp_intr_base;
unsigned int val, count = ACP_HW_SEM_RETRY_COUNT;
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat);
@@ -328,28 +327,20 @@ static irqreturn_t acp_irq_thread(int irq, void *context)
return IRQ_HANDLED;
}
- val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET);
- if (val & ACP_DSP_TO_HOST_IRQ) {
- while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset)) {
- /* Wait until acquired HW Semaphore lock or timeout */
- count--;
- if (!count) {
- dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__);
- return IRQ_NONE;
- }
+ while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset)) {
+ /* Wait until acquired HW Semaphore lock or timeout */
+ count--;
+ if (!count) {
+ dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__);
+ return IRQ_NONE;
}
-
- sof_ops(sdev)->irq_thread(irq, sdev);
- val |= ACP_DSP_TO_HOST_IRQ;
- snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET, val);
-
- /* Unlock or Release HW Semaphore */
- snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset, 0x0);
-
- return IRQ_HANDLED;
}
- return IRQ_NONE;
+ sof_ops(sdev)->irq_thread(irq, sdev);
+ /* Unlock or Release HW Semaphore */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset, 0x0);
+
+ return IRQ_HANDLED;
};
static irqreturn_t acp_irq_handler(int irq, void *dev_id)
@@ -360,8 +351,11 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id)
unsigned int val;
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET);
- if (val)
+ if (val) {
+ val |= ACP_DSP_TO_HOST_IRQ;
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET, val);
return IRQ_WAKE_THREAD;
+ }
return IRQ_NONE;
}
--
2.25.1
2
1
It's been reported that a number of laptops have a low volume
level from the digital microphone compared to Windows.
AMD offers a register that can adjust the gain for PDM which is not
configured at maximum gain by default.
To fix this change the default for all 3 drivers to raise the gain
but also offer a module parameter. The module parameter can be used
for debugging if the gain is too high on a given laptop.
This is intentionally split into multiple patches for default and
parameter so that if the default really does behave better universally
we can bring it back to stable too later.
v2->v3:
* Use clamp and clear properly
Mario Limonciello (6):
ASoC: amd: yc: Adjust the gain for PDM DMIC
ASoC: amd: yc: Add a module parameter to influence pdm_gain
ASoC: amd: renoir: Adjust the gain for PDM DMIC
ASoC: amd: renoir: Add a module parameter to influence pdm_gain
ASoC: amd: ps: Adjust the gain for PDM DMIC
ASoC: amd: ps: Add a module parameter to influence pdm_gain
sound/soc/amd/ps/acp63.h | 2 +-
sound/soc/amd/ps/ps-pdm-dma.c | 8 +++++++-
sound/soc/amd/renoir/acp3x-pdm-dma.c | 8 +++++++-
sound/soc/amd/renoir/rn_acp3x.h | 2 +-
sound/soc/amd/yc/acp6x-pdm-dma.c | 8 +++++++-
sound/soc/amd/yc/acp6x.h | 2 +-
6 files changed, 24 insertions(+), 6 deletions(-)
--
2.34.1
3
11

[PATCH][next] ASoC: codecs: aw88395: Fix spelling mistake "cersion" -> "version"
by Colin Ian King 02 Feb '23
by Colin Ian King 02 Feb '23
02 Feb '23
There are spelling mistakes in dev_err messages. Fix them.
Signed-off-by: Colin Ian King <colin.i.king(a)gmail.com>
---
sound/soc/codecs/aw88395/aw88395_lib.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c
index 34ae405bb43d..64dde972f3f0 100644
--- a/sound/soc/codecs/aw88395/aw88395_lib.c
+++ b/sound/soc/codecs/aw88395/aw88395_lib.c
@@ -890,7 +890,7 @@ int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg)
case AW88395_CFG_HDR_VER:
ret = aw_dev_load_cfg_by_hdr(aw_dev, cfg_hdr);
if (ret < 0) {
- dev_err(aw_dev->dev, "hdr_cersion[0x%x] parse failed",
+ dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed",
cfg_hdr->hdr_version);
return ret;
}
@@ -898,7 +898,7 @@ int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg)
case AW88395_CFG_HDR_VER_V1:
ret = aw_dev_load_cfg_by_hdr_v1(aw_dev, aw_cfg);
if (ret < 0) {
- dev_err(aw_dev->dev, "hdr_cersion[0x%x] parse failed",
+ dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed",
cfg_hdr->hdr_version);
return ret;
}
--
2.30.2
2
1

[PATCH v2 0/9] ASoC: SOF: core/ipc4/mtl: Add support for PCM delay reporting
by Peter Ujfalusi 02 Feb '23
by Peter Ujfalusi 02 Feb '23
02 Feb '23
Hi,
Changes since v1:
- The delay calculation updated to take into account the counter wrapping on both
ends (host and link side).
The following series adds support for the PCM delay reporting in SOF core level
and implements the needed infrastructure with IPC4 to finally enable it for MTL.
Currently this is only supported on MTL (and via IPC4), but with the
infrastructure in place it will be possible to support other platforms with
DeepBuffer.
Regards,
Peter
---
Rander Wang (9):
ASoC: SOF: add ipc4_fw_reg header file
ASoC: SOF: add fw_info_box support
ASoC: SOF: add time info structure for ipc4 path
ASoC: SOF: ipc4-pcm: allocate time info for pcm delay feature
ASoC: SOF: ipc4-pcm: add hw_params
ASoC: SOF: add delay function support in sof framework
ASoC: SOF: add get_stream_position ops for pcm delay
ASoC: SOF: Intel: mtl: add get_stream_position support
ASoC: SOF: ipc4-pcm: add delay function support
sound/soc/sof/intel/mtl.c | 14 +++
sound/soc/sof/intel/mtl.h | 6 +
sound/soc/sof/ipc4-fw-reg.h | 155 ++++++++++++++++++++++++
sound/soc/sof/ipc4-pcm.c | 232 ++++++++++++++++++++++++++++++++++++
sound/soc/sof/ipc4-priv.h | 15 +++
sound/soc/sof/ipc4.c | 3 +
sound/soc/sof/ops.h | 10 ++
sound/soc/sof/pcm.c | 13 ++
sound/soc/sof/sof-audio.h | 6 +
sound/soc/sof/sof-priv.h | 10 ++
10 files changed, 464 insertions(+)
create mode 100644 sound/soc/sof/ipc4-fw-reg.h
--
2.39.1
1
10

02 Feb '23
This reverts entire SMA1303 submission:
1. commit 1c24d12b68fa ("ASoC: dt-bindings: irondevice,sma1303.yaml: Fix about breaking the checks")
2. commit dcf6d2ef0e82 ("ASoC: Modified the schema binding and added the vendor prefixes.")
3. commit 5b28c049ff53 ("ASoC: SMA1303: Fix spelling mistake "Invald" -> "Invalid"")
4. commit 68cd394efd0f ("ASoC: The Iron Device SMA1303 is a boosted Class-D audio amplifier.")
Because the binding:
1. Was never tested,
2. Was never sent to Devicetree maintainers,
3. Is entirely broken and wrong, so it would have to be almost rewritten
from scratch,
4. It does not match the driver, IOW, the binding is fake.
We cannot accept drivers with broken bindings and make it an ABI.
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski(a)linaro.org>
---
.../bindings/sound/irondevice,sma1303.yaml | 31 -
.../devicetree/bindings/vendor-prefixes.yaml | 2 -
sound/soc/codecs/Kconfig | 7 -
sound/soc/codecs/Makefile | 2 -
sound/soc/codecs/sma1303.c | 1804 -----------------
sound/soc/codecs/sma1303.h | 609 ------
6 files changed, 2455 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml
delete mode 100644 sound/soc/codecs/sma1303.c
delete mode 100644 sound/soc/codecs/sma1303.h
diff --git a/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml b/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml
deleted file mode 100644
index 162c52606635..000000000000
--- a/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/irondevice,sma1303.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: Iron Device SMA1303 Audio Amplifier
-
-maintainers:
- - Kiseok Jo <kiseok.jo(a)irondevice.com>
-
-description:
- SMA1303 digital class-D audio amplifier with an integrated boost converter.
-
-allOf:
- - $ref: name-prefix.yaml#
-
-required:
- - compatible
- - reg
-
-additionalProperties: false
-
-examples:
- - |
- i2c_bus {
- amplifier@1e {
- compatible = "irondevice,sma1303";
- reg = <0x1e>;
- };
- };
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 1f7a519a936f..4cf1749c8f45 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -645,8 +645,6 @@ patternProperties:
description: Inverse Path
"^iom,.*":
description: Iomega Corporation
- "^irondevice,.*":
- description: Iron Device Corporation
"^isee,.*":
description: ISEE 2007 S.L.
"^isil,.*":
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 6902628f0d10..6b4ee14640ab 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -206,7 +206,6 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_SI476X
imply SND_SOC_SIMPLE_AMPLIFIER
imply SND_SOC_SIMPLE_MUX
- imply SND_SOC_SMA1303
imply SND_SOC_SPDIF
imply SND_SOC_SRC4XXX_I2C
imply SND_SOC_SSM2305
@@ -1493,12 +1492,6 @@ config SND_SOC_SIMPLE_MUX
tristate "Simple Audio Mux"
depends on GPIOLIB
-config SND_SOC_SMA1303
- tristate "Iron Device SMA1303 Audio Amplifier"
- depends on I2C
- help
- Enable support for Iron Device SMA1303 Boosted Class-D amplifier
-
config SND_SOC_SPDIF
tristate "S/PDIF CODEC"
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 89ffd06e9290..71d3ce5867e4 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -233,7 +233,6 @@ snd-soc-sigmadsp-objs := sigmadsp.o
snd-soc-sigmadsp-i2c-objs := sigmadsp-i2c.o
snd-soc-sigmadsp-regmap-objs := sigmadsp-regmap.o
snd-soc-si476x-objs := si476x.o
-snd-soc-sma1303-objs := sma1303.o
snd-soc-spdif-tx-objs := spdif_transmitter.o
snd-soc-spdif-rx-objs := spdif_receiver.o
snd-soc-src4xxx-objs := src4xxx.o
@@ -589,7 +588,6 @@ obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o
obj-$(CONFIG_SND_SOC_SIGMADSP_REGMAP) += snd-soc-sigmadsp-regmap.o
obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o
-obj-$(CONFIG_SND_SOC_SMA1303) += snd-soc-sma1303.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o
obj-$(CONFIG_SND_SOC_SRC4XXX) += snd-soc-src4xxx.o
obj-$(CONFIG_SND_SOC_SRC4XXX_I2C) += snd-soc-src4xxx-i2c.o
diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c
deleted file mode 100644
index 3d8e3900f5c3..000000000000
--- a/sound/soc/codecs/sma1303.c
+++ /dev/null
@@ -1,1804 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-//
-// sma1303.c -- sma1303 ALSA SoC Audio driver
-//
-// Copyright 2023 Iron Device Corporation
-//
-// Auther: Gyuhwa Park <gyuhwa.park(a)irondevice.com>
-// Kiseok Jo <kiseok.jo(a)irondevice.com>
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/pm.h>
-#include <linux/i2c.h>
-#include <linux/regmap.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/initval.h>
-#include <sound/tlv.h>
-#include <linux/of_device.h>
-#include <linux/slab.h>
-#include <asm/div64.h>
-
-#include "sma1303.h"
-
-#define CHECK_PERIOD_TIME 1 /* sec per HZ */
-#define MAX_CONTROL_NAME 48
-
-#define PLL_MATCH(_input_clk_name, _output_clk_name, _input_clk,\
- _post_n, _n, _vco, _p_cp)\
-{\
- .input_clk_name = _input_clk_name,\
- .output_clk_name = _output_clk_name,\
- .input_clk = _input_clk,\
- .post_n = _post_n,\
- .n = _n,\
- .vco = _vco,\
- .p_cp = _p_cp,\
-}
-
-enum sma1303_type {
- SMA1303,
-};
-
-struct sma1303_pll_match {
- char *input_clk_name;
- char *output_clk_name;
- unsigned int input_clk;
- unsigned int post_n;
- unsigned int n;
- unsigned int vco;
- unsigned int p_cp;
-};
-
-struct sma1303_priv {
- enum sma1303_type devtype;
- struct attribute_group *attr_grp;
- struct delayed_work check_fault_work;
- struct device *dev;
- struct kobject *kobj;
- struct regmap *regmap;
- struct sma1303_pll_match *pll_matches;
- bool amp_power_status;
- bool force_mute_status;
- int num_of_pll_matches;
- int retry_cnt;
- unsigned int amp_mode;
- unsigned int cur_vol;
- unsigned int format;
- unsigned int frame_size;
- unsigned int init_vol;
- unsigned int last_bclk;
- unsigned int last_ocp_val;
- unsigned int last_over_temp;
- unsigned int rev_num;
- unsigned int sys_clk_id;
- unsigned int tdm_slot_rx;
- unsigned int tdm_slot_tx;
- unsigned int tsdw_cnt;
- long check_fault_period;
- long check_fault_status;
-};
-
-static struct sma1303_pll_match sma1303_pll_matches[] = {
-PLL_MATCH("1.411MHz", "24.595MHz", 1411200, 0x07, 0xF4, 0x8B, 0x03),
-PLL_MATCH("1.536MHz", "24.576MHz", 1536000, 0x07, 0xE0, 0x8B, 0x03),
-PLL_MATCH("3.072MHz", "24.576MHz", 3072000, 0x07, 0x70, 0x8B, 0x03),
-PLL_MATCH("6.144MHz", "24.576MHz", 6144000, 0x07, 0x70, 0x8B, 0x07),
-PLL_MATCH("12.288MHz", "24.576MHz", 12288000, 0x07, 0x70, 0x8B, 0x0B),
-PLL_MATCH("19.2MHz", "24.343MHz", 19200000, 0x07, 0x47, 0x8B, 0x0A),
-PLL_MATCH("24.576MHz", "24.576MHz", 24576000, 0x07, 0x70, 0x8B, 0x0F),
-};
-
-static int sma1303_startup(struct snd_soc_component *);
-static int sma1303_shutdown(struct snd_soc_component *);
-
-static const struct reg_default sma1303_reg_def[] = {
- { 0x00, 0x80 },
- { 0x01, 0x00 },
- { 0x02, 0x00 },
- { 0x03, 0x11 },
- { 0x04, 0x17 },
- { 0x09, 0x00 },
- { 0x0A, 0x31 },
- { 0x0B, 0x98 },
- { 0x0C, 0x84 },
- { 0x0D, 0x07 },
- { 0x0E, 0x3F },
- { 0x10, 0x00 },
- { 0x11, 0x00 },
- { 0x12, 0x00 },
- { 0x14, 0x5C },
- { 0x15, 0x01 },
- { 0x16, 0x0F },
- { 0x17, 0x0F },
- { 0x18, 0x0F },
- { 0x19, 0x00 },
- { 0x1A, 0x00 },
- { 0x1B, 0x00 },
- { 0x23, 0x19 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x04 },
- { 0x33, 0x00 },
- { 0x36, 0x92 },
- { 0x37, 0x27 },
- { 0x3B, 0x5A },
- { 0x3C, 0x20 },
- { 0x3D, 0x00 },
- { 0x3E, 0x03 },
- { 0x3F, 0x0C },
- { 0x8B, 0x07 },
- { 0x8C, 0x70 },
- { 0x8D, 0x8B },
- { 0x8E, 0x6F },
- { 0x8F, 0x03 },
- { 0x90, 0x26 },
- { 0x91, 0x42 },
- { 0x92, 0xE0 },
- { 0x94, 0x35 },
- { 0x95, 0x0C },
- { 0x96, 0x42 },
- { 0x97, 0x95 },
- { 0xA0, 0x00 },
- { 0xA1, 0x3B },
- { 0xA2, 0xC8 },
- { 0xA3, 0x28 },
- { 0xA4, 0x40 },
- { 0xA5, 0x01 },
- { 0xA6, 0x41 },
- { 0xA7, 0x00 },
-};
-
-static bool sma1303_readable_register(struct device *dev, unsigned int reg)
-{
- bool result;
-
- if (reg > SMA1303_FF_DEVICE_INDEX)
- return false;
-
- switch (reg) {
- case SMA1303_00_SYSTEM_CTRL ... SMA1303_04_INPUT1_CTRL4:
- case SMA1303_09_OUTPUT_CTRL ... SMA1303_0E_MUTE_VOL_CTRL:
- case SMA1303_10_SYSTEM_CTRL1 ... SMA1303_12_SYSTEM_CTRL3:
- case SMA1303_14_MODULATOR ... SMA1303_1B_BASS_SPK7:
- case SMA1303_23_COMP_LIM1 ... SMA1303_26_COMP_LIM4:
- case SMA1303_33_SDM_CTRL ... SMA1303_34_OTP_DATA1:
- case SMA1303_36_PROTECTION ... SMA1303_38_OTP_TRM0:
- case SMA1303_3B_TEST1 ... SMA1303_3F_ATEST2:
- case SMA1303_8B_PLL_POST_N ... SMA1303_92_FDPEC_CTRL:
- case SMA1303_94_BOOST_CTRL1 ... SMA1303_97_BOOST_CTRL4:
- case SMA1303_A0_PAD_CTRL0 ... SMA1303_A7_CLK_MON:
- case SMA1303_FA_STATUS1 ... SMA1303_FB_STATUS2:
- result = true;
- break;
- case SMA1303_FF_DEVICE_INDEX:
- result = true;
- break;
- default:
- result = false;
- break;
- }
- return result;
-}
-
-static bool sma1303_writeable_register(struct device *dev, unsigned int reg)
-{
- bool result;
-
- if (reg > SMA1303_FF_DEVICE_INDEX)
- return false;
-
- switch (reg) {
- case SMA1303_00_SYSTEM_CTRL ... SMA1303_04_INPUT1_CTRL4:
- case SMA1303_09_OUTPUT_CTRL ... SMA1303_0E_MUTE_VOL_CTRL:
- case SMA1303_10_SYSTEM_CTRL1 ... SMA1303_12_SYSTEM_CTRL3:
- case SMA1303_14_MODULATOR ... SMA1303_1B_BASS_SPK7:
- case SMA1303_23_COMP_LIM1 ... SMA1303_26_COMP_LIM4:
- case SMA1303_33_SDM_CTRL:
- case SMA1303_36_PROTECTION ... SMA1303_37_SLOPE_CTRL:
- case SMA1303_3B_TEST1 ... SMA1303_3F_ATEST2:
- case SMA1303_8B_PLL_POST_N ... SMA1303_92_FDPEC_CTRL:
- case SMA1303_94_BOOST_CTRL1 ... SMA1303_97_BOOST_CTRL4:
- case SMA1303_A0_PAD_CTRL0 ... SMA1303_A7_CLK_MON:
- result = true;
- break;
- default:
- result = false;
- break;
- }
- return result;
-}
-
-static bool sma1303_volatile_register(struct device *dev, unsigned int reg)
-{
- bool result;
-
- switch (reg) {
- case SMA1303_FA_STATUS1 ... SMA1303_FB_STATUS2:
- result = true;
- break;
- case SMA1303_FF_DEVICE_INDEX:
- result = true;
- break;
- default:
- result = false;
- break;
- }
- return result;
-}
-
-static const DECLARE_TLV_DB_SCALE(sma1303_spk_tlv, -6000, 50, 0);
-
-static int sma1303_regmap_write(struct sma1303_priv *sma1303,
- unsigned int reg, unsigned int val)
-{
- int ret = 0;
- int cnt = sma1303->retry_cnt;
-
- while (cnt--) {
- ret = regmap_write(sma1303->regmap, reg, val);
- if (ret < 0) {
- dev_err(sma1303->dev,
- "Failed to write [0x%02X]\n", reg);
- } else
- break;
- }
- return ret;
-}
-
-static int sma1303_regmap_update_bits(struct sma1303_priv *sma1303,
- unsigned int reg, unsigned int mask, unsigned int val, bool *change)
-{
- int ret = 0;
- int cnt = sma1303->retry_cnt;
-
- while (cnt--) {
- ret = regmap_update_bits_check(sma1303->regmap, reg,
- mask, val, change);
- if (ret < 0) {
- dev_err(sma1303->dev,
- "Failed to update [0x%02X]\n", reg);
- } else
- break;
- }
- return ret;
-}
-
-static int sma1303_regmap_read(struct sma1303_priv *sma1303,
- unsigned int reg, unsigned int *val)
-{
- int ret = 0;
- int cnt = sma1303->retry_cnt;
-
- while (cnt--) {
- ret = regmap_read(sma1303->regmap, reg, val);
- if (ret < 0) {
- dev_err(sma1303->dev,
- "Failed to read [0x%02X]\n", reg);
- } else
- break;
- }
- return ret;
-}
-
-static const char * const sma1303_aif_in_source_text[] = {
- "Mono", "Left", "Right"};
-static const char * const sma1303_aif_out_source_text[] = {
- "Disable", "After_FmtC", "After_Mixer", "After_DSP", "After_Post",
- "Clk_PLL", "Clk_OSC"};
-
-static const struct soc_enum sma1303_aif_in_source_enum =
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_aif_in_source_text),
- sma1303_aif_in_source_text);
-static const struct soc_enum sma1303_aif_out_source_enum =
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_aif_out_source_text),
- sma1303_aif_out_source_text);
-
-static int sma1303_force_mute_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component =
- snd_soc_kcontrol_component(kcontrol);
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
-
- ucontrol->value.integer.value[0] = (int)sma1303->force_mute_status;
- dev_dbg(sma1303->dev, "%s : Force Mute %s\n", __func__,
- sma1303->force_mute_status ? "ON" : "OFF");
-
- return 0;
-}
-
-static int sma1303_force_mute_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component =
- snd_soc_kcontrol_component(kcontrol);
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
- bool change = false, val = (bool)ucontrol->value.integer.value[0];
-
- if (sma1303->force_mute_status == val)
- change = false;
- else {
- change = true;
- sma1303->force_mute_status = val;
- }
- dev_dbg(sma1303->dev, "%s : Force Mute %s\n", __func__,
- sma1303->force_mute_status ? "ON" : "OFF");
-
- return change;
-}
-
-static int sma1303_postscaler_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component =
- snd_soc_kcontrol_component(kcontrol);
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
- int val, ret;
-
- ret = sma1303_regmap_read(sma1303, SMA1303_90_POSTSCALER, &val);
- if (ret < 0)
- return -EINVAL;
-
- ucontrol->value.integer.value[0] = (val & 0x7E) >> 1;
-
- return 0;
-}
-
-static int sma1303_postscaler_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component =
- snd_soc_kcontrol_component(kcontrol);
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
- int ret, val = (int)ucontrol->value.integer.value[0];
- bool change;
-
- ret = sma1303_regmap_update_bits(sma1303,
- SMA1303_90_POSTSCALER, 0x7E, (val << 1), &change);
- if (ret < 0)
- return -EINVAL;
-
- return change;
-}
-
-static int sma1303_startup(struct snd_soc_component *component)
-{
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
- bool change = false, temp = false;
-
- sma1303_regmap_update_bits(sma1303, SMA1303_8E_PLL_CTRL,
- SMA1303_PLL_PD2_MASK, SMA1303_PLL_OPERATION2, &temp);
- if (temp == true)
- change = true;
-
- sma1303_regmap_update_bits(sma1303, SMA1303_00_SYSTEM_CTRL,
- SMA1303_POWER_MASK, SMA1303_POWER_ON, &temp);
- if (temp == true)
- change = true;
-
- if (sma1303->amp_mode == SMA1303_MONO) {
- sma1303_regmap_update_bits(sma1303,
- SMA1303_10_SYSTEM_CTRL1,
- SMA1303_SPK_MODE_MASK,
- SMA1303_SPK_MONO,
- &temp);
- if (temp == true)
- change = true;
-
- } else {
- sma1303_regmap_update_bits(sma1303,
- SMA1303_10_SYSTEM_CTRL1,
- SMA1303_SPK_MODE_MASK,
- SMA1303_SPK_STEREO,
- &temp);
- if (temp == true)
- change = true;
- }
-
- if (sma1303->check_fault_status) {
- if (sma1303->check_fault_period > 0)
- queue_delayed_work(system_freezable_wq,
- &sma1303->check_fault_work,
- sma1303->check_fault_period * HZ);
- else
- queue_delayed_work(system_freezable_wq,
- &sma1303->check_fault_work,
- CHECK_PERIOD_TIME * HZ);
- }
-
- sma1303->amp_power_status = true;
-
- return change;
-}
-
-static int sma1303_shutdown(struct snd_soc_component *component)
-{
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
- bool change = false, temp = false;
-
- cancel_delayed_work_sync(&sma1303->check_fault_work);
-
- sma1303_regmap_update_bits(sma1303, SMA1303_10_SYSTEM_CTRL1,
- SMA1303_SPK_MODE_MASK, SMA1303_SPK_OFF, &temp);
- if (temp == true)
- change = true;
-
- sma1303_regmap_update_bits(sma1303, SMA1303_00_SYSTEM_CTRL,
- SMA1303_POWER_MASK, SMA1303_POWER_OFF, &temp);
- if (temp == true)
- change = true;
- sma1303_regmap_update_bits(sma1303, SMA1303_8E_PLL_CTRL,
- SMA1303_PLL_PD2_MASK, SMA1303_PLL_PD2, &temp);
- if (temp == true)
- change = true;
-
- sma1303->amp_power_status = false;
-
- return change;
-}
-
-static int sma1303_aif_in_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_component *component =
- snd_soc_dapm_to_component(w->dapm);
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
- unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
- int ret = 0;
- bool change = false, temp = false;
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- switch (mux) {
- case 0:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_11_SYSTEM_CTRL2,
- SMA1303_MONOMIX_MASK,
- SMA1303_MONOMIX_ON,
- &change);
- sma1303->amp_mode = SMA1303_MONO;
- break;
- case 1:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_11_SYSTEM_CTRL2,
- SMA1303_MONOMIX_MASK,
- SMA1303_MONOMIX_OFF,
- &temp);
- if (temp == true)
- change = true;
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_11_SYSTEM_CTRL2,
- SMA1303_LR_DATA_SW_MASK,
- SMA1303_LR_DATA_SW_NORMAL,
- &temp);
- if (temp == true)
- change = true;
- sma1303->amp_mode = SMA1303_STEREO;
- break;
- case 2:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_11_SYSTEM_CTRL2,
- SMA1303_MONOMIX_MASK,
- SMA1303_MONOMIX_OFF,
- &temp);
- if (temp == true)
- change = true;
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_11_SYSTEM_CTRL2,
- SMA1303_LR_DATA_SW_MASK,
- SMA1303_LR_DATA_SW_NORMAL,
- &temp);
- if (temp == true)
- change = true;
- sma1303->amp_mode = SMA1303_STEREO;
- break;
- default:
- dev_err(sma1303->dev, "%s : Invalid value (%d)\n",
- __func__, mux);
- return -EINVAL;
- }
-
- dev_dbg(sma1303->dev, "%s : Source : %s\n", __func__,
- sma1303_aif_in_source_text[mux]);
- break;
- }
- if (ret < 0)
- return -EINVAL;
- return change;
-}
-
-static int sma1303_aif_out_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_component *component =
- snd_soc_dapm_to_component(w->dapm);
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
- unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
- int ret = 0;
- bool change = false, temp = false;
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- switch (mux) {
- case 0:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A3_TOP_MAN2,
- SMA1303_TEST_CLKO_EN_MASK,
- SMA1303_NORMAL_SDO,
- &temp);
- if (temp == true)
- change = true;
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_09_OUTPUT_CTRL,
- SMA1303_PORT_OUT_SEL_MASK,
- SMA1303_OUT_SEL_DISABLE,
- &temp);
- if (temp == true)
- change = true;
- break;
- case 1:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A3_TOP_MAN2,
- SMA1303_TEST_CLKO_EN_MASK,
- SMA1303_NORMAL_SDO,
- &temp);
- if (temp == true)
- change = true;
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_09_OUTPUT_CTRL,
- SMA1303_PORT_OUT_SEL_MASK,
- SMA1303_FORMAT_CONVERTER,
- &temp);
- if (temp == true)
- change = true;
- break;
- case 2:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A3_TOP_MAN2,
- SMA1303_TEST_CLKO_EN_MASK,
- SMA1303_NORMAL_SDO,
- &temp);
- if (temp == true)
- change = true;
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_09_OUTPUT_CTRL,
- SMA1303_PORT_OUT_SEL_MASK,
- SMA1303_MIXER_OUTPUT,
- &temp);
- if (temp == true)
- change = true;
- break;
- case 3:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A3_TOP_MAN2,
- SMA1303_TEST_CLKO_EN_MASK,
- SMA1303_NORMAL_SDO,
- &temp);
- if (temp == true)
- change = true;
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_09_OUTPUT_CTRL,
- SMA1303_PORT_OUT_SEL_MASK,
- SMA1303_SPEAKER_PATH,
- &temp);
- if (temp == true)
- change = true;
- break;
- case 4:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A3_TOP_MAN2,
- SMA1303_TEST_CLKO_EN_MASK,
- SMA1303_NORMAL_SDO,
- &temp);
- if (temp == true)
- change = true;
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_09_OUTPUT_CTRL,
- SMA1303_PORT_OUT_SEL_MASK,
- SMA1303_POSTSCALER_OUTPUT,
- &temp);
- if (temp == true)
- change = true;
- break;
- case 5:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A3_TOP_MAN2,
- SMA1303_TEST_CLKO_EN_MASK,
- SMA1303_CLK_OUT_SDO,
- &temp);
- if (temp == true)
- change = true;
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A3_TOP_MAN2,
- SMA1303_MON_OSC_PLL_MASK,
- SMA1303_PLL_SDO,
- &temp);
- if (temp == true)
- change = true;
- break;
- case 6:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A3_TOP_MAN2,
- SMA1303_TEST_CLKO_EN_MASK,
- SMA1303_CLK_OUT_SDO,
- &temp);
- if (temp == true)
- change = true;
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A3_TOP_MAN2,
- SMA1303_MON_OSC_PLL_MASK,
- SMA1303_OSC_SDO,
- &temp);
- if (temp == true)
- change = true;
- break;
- default:
- dev_err(sma1303->dev, "%s : Invalid value (%d)\n",
- __func__, mux);
- return -EINVAL;
- }
-
- dev_dbg(sma1303->dev, "%s : Source : %s\n", __func__,
- sma1303_aif_out_source_text[mux]);
- break;
- }
- if (ret < 0)
- return -EINVAL;
- return change;
-}
-
-static int sma1303_sdo_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_component *component =
- snd_soc_dapm_to_component(w->dapm);
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
- int ret = 0;
- bool change = false, temp = false;
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- dev_dbg(sma1303->dev,
- "%s : SND_SOC_DAPM_PRE_PMU\n", __func__);
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_09_OUTPUT_CTRL,
- SMA1303_PORT_CONFIG_MASK,
- SMA1303_OUTPUT_PORT_ENABLE,
- &temp);
- if (temp == true)
- change = true;
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A3_TOP_MAN2,
- SMA1303_SDO_OUTPUT_MASK,
- SMA1303_NORMAL_OUT,
- &temp);
- if (temp == true)
- change = true;
- break;
- case SND_SOC_DAPM_POST_PMD:
- dev_dbg(sma1303->dev,
- "%s : SND_SOC_DAPM_POST_PMD\n", __func__);
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_09_OUTPUT_CTRL,
- SMA1303_PORT_CONFIG_MASK,
- SMA1303_INPUT_PORT_ONLY,
- &temp);
- if (temp == true)
- change = true;
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A3_TOP_MAN2,
- SMA1303_SDO_OUTPUT_MASK,
- SMA1303_HIGH_Z_OUT,
- &temp);
- if (temp == true)
- change = true;
- break;
- }
- if (ret < 0)
- return -EINVAL;
- return change;
-}
-
-static int sma1303_post_scaler_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_component *component =
- snd_soc_dapm_to_component(w->dapm);
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
- int ret = 0;
- bool change = false;
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- dev_dbg(sma1303->dev,
- "%s : SND_SOC_DAPM_PRE_PMU\n", __func__);
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_90_POSTSCALER,
- SMA1303_BYP_POST_MASK,
- SMA1303_EN_POST_SCALER,
- &change);
- break;
- case SND_SOC_DAPM_POST_PMD:
- dev_dbg(sma1303->dev,
- "%s : SND_SOC_DAPM_POST_PMD\n", __func__);
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_90_POSTSCALER,
- SMA1303_BYP_POST_MASK,
- SMA1303_BYP_POST_SCALER,
- &change);
- break;
- }
- if (ret < 0)
- return -EINVAL;
- return change;
-}
-
-static int sma1303_power_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_component *component =
- snd_soc_dapm_to_component(w->dapm);
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
- int ret = 0;
-
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- dev_dbg(sma1303->dev,
- "%s : SND_SOC_DAPM_POST_PMU\n", __func__);
- ret = sma1303_startup(component);
- break;
- case SND_SOC_DAPM_PRE_PMD:
- dev_dbg(sma1303->dev,
- "%s : SND_SOC_DAPM_PRE_PMD\n", __func__);
- ret = sma1303_shutdown(component);
- break;
- }
- return ret;
-}
-
-static const struct snd_kcontrol_new sma1303_aif_in_source_control =
- SOC_DAPM_ENUM("AIF IN Source", sma1303_aif_in_source_enum);
-static const struct snd_kcontrol_new sma1303_aif_out_source_control =
- SOC_DAPM_ENUM("AIF OUT Source", sma1303_aif_out_source_enum);
-static const struct snd_kcontrol_new sma1303_sdo_control =
- SOC_DAPM_SINGLE_VIRT("Switch", 1);
-static const struct snd_kcontrol_new sma1303_post_scaler_control =
- SOC_DAPM_SINGLE_VIRT("Switch", 1);
-static const struct snd_kcontrol_new sma1303_enable_control =
- SOC_DAPM_SINGLE_VIRT("Switch", 1);
-
-static const struct snd_kcontrol_new sma1303_snd_controls[] = {
- SOC_SINGLE_TLV("Speaker Volume", SMA1303_0A_SPK_VOL,
- 0, 167, 1, sma1303_spk_tlv),
- SOC_SINGLE_BOOL_EXT("Force Mute Switch", 0,
- sma1303_force_mute_get, sma1303_force_mute_put),
- SOC_SINGLE_EXT("Postscaler Gain", SMA1303_90_POSTSCALER, 1, 0x30, 0,
- sma1303_postscaler_get, sma1303_postscaler_put),
-};
-
-static const struct snd_soc_dapm_widget sma1303_dapm_widgets[] = {
- /* platform domain */
- SND_SOC_DAPM_OUTPUT("SPK"),
- SND_SOC_DAPM_INPUT("SDO"),
-
- /* path domain */
- SND_SOC_DAPM_MUX_E("AIF IN Source", SND_SOC_NOPM, 0, 0,
- &sma1303_aif_in_source_control,
- sma1303_aif_in_event,
- SND_SOC_DAPM_PRE_PMU),
- SND_SOC_DAPM_MUX_E("AIF OUT Source", SND_SOC_NOPM, 0, 0,
- &sma1303_aif_out_source_control,
- sma1303_aif_out_event,
- SND_SOC_DAPM_PRE_PMU),
- SND_SOC_DAPM_SWITCH_E("SDO Enable", SND_SOC_NOPM, 0, 0,
- &sma1303_sdo_control,
- sma1303_sdo_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MIXER("Entry", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_SWITCH_E("Post Scaler", SND_SOC_NOPM, 0, 1,
- &sma1303_post_scaler_control,
- sma1303_post_scaler_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_OUT_DRV_E("AMP Power", SND_SOC_NOPM, 0, 0, NULL, 0,
- sma1303_power_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
- SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 1,
- &sma1303_enable_control),
-
- /* stream domain */
- SND_SOC_DAPM_AIF_IN("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
-};
-
-static const struct snd_soc_dapm_route sma1303_audio_map[] = {
- /* Playback */
- {"AIF IN Source", "Mono", "AIF IN"},
- {"AIF IN Source", "Left", "AIF IN"},
- {"AIF IN Source", "Right", "AIF IN"},
-
- {"SDO Enable", "Switch", "AIF IN"},
- {"AIF OUT Source", "Disable", "SDO Enable"},
- {"AIF OUT Source", "After_FmtC", "SDO Enable"},
- {"AIF OUT Source", "After_Mixer", "SDO Enable"},
- {"AIF OUT Source", "After_DSP", "SDO Enable"},
- {"AIF OUT Source", "After_Post", "SDO Enable"},
- {"AIF OUT Source", "Clk_PLL", "SDO Enable"},
- {"AIF OUT Source", "Clk_OSC", "SDO Enable"},
-
- {"Entry", NULL, "AIF OUT Source"},
- {"Entry", NULL, "AIF IN Source"},
-
- {"Post Scaler", "Switch", "Entry"},
- {"AMP Power", NULL, "Entry"},
- {"AMP Power", NULL, "Entry"},
-
- {"AMP Enable", "Switch", "AMP Power"},
- {"SPK", NULL, "AMP Enable"},
-
- /* Capture */
- {"AIF OUT", NULL, "AMP Enable"},
-};
-
-static int sma1303_setup_pll(struct snd_soc_component *component,
- unsigned int bclk)
-{
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
-
- int i = 0, ret = 0;
-
- dev_dbg(component->dev, "%s : BCLK = %dHz\n",
- __func__, bclk);
-
- if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_MCLK) {
- dev_dbg(component->dev, "%s : MCLK is not supported\n",
- __func__);
- } else if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_BCLK) {
- for (i = 0; i < sma1303->num_of_pll_matches; i++) {
- if (sma1303->pll_matches[i].input_clk == bclk)
- break;
- }
- if (i == sma1303->num_of_pll_matches) {
- dev_dbg(component->dev, "%s : No matching value between pll table and SCK\n",
- __func__);
- return -EINVAL;
- }
-
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A2_TOP_MAN1,
- SMA1303_PLL_PD_MASK|SMA1303_PLL_REF_CLK_MASK,
- SMA1303_PLL_OPERATION|SMA1303_PLL_SCK,
- NULL);
- }
-
- ret += sma1303_regmap_write(sma1303,
- SMA1303_8B_PLL_POST_N,
- sma1303->pll_matches[i].post_n);
-
- ret += sma1303_regmap_write(sma1303,
- SMA1303_8C_PLL_N,
- sma1303->pll_matches[i].n);
-
- ret += sma1303_regmap_write(sma1303,
- SMA1303_8D_PLL_A_SETTING,
- sma1303->pll_matches[i].vco);
-
- ret += sma1303_regmap_write(sma1303,
- SMA1303_8F_PLL_P_CP,
- sma1303->pll_matches[i].p_cp);
- if (ret < 0)
- return -EINVAL;
-
- return 0;
-}
-
-static int sma1303_dai_hw_params_amp(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
-{
- struct snd_soc_component *component = dai->component;
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
- unsigned int bclk = 0;
- int ret = 0;
-
- if (sma1303->format == SND_SOC_DAIFMT_DSP_A)
- bclk = params_rate(params) * sma1303->frame_size;
- else
- bclk = params_rate(params) * params_physical_width(params)
- * params_channels(params);
-
- dev_dbg(component->dev,
- "%s : rate = %d : bit size = %d : channel = %d\n",
- __func__, params_rate(params), params_width(params),
- params_channels(params));
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-
- if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_MCLK
- || sma1303->sys_clk_id == SMA1303_PLL_CLKIN_BCLK) {
-
- if (sma1303->last_bclk != bclk) {
- sma1303_setup_pll(component, bclk);
- sma1303->last_bclk = bclk;
- }
- }
-
- switch (params_rate(params)) {
- case 8000:
- case 12000:
- case 16000:
- case 24000:
- case 32000:
- case 44100:
- case 48000:
- case 96000:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A2_TOP_MAN1,
- SMA1303_DAC_DN_CONV_MASK,
- SMA1303_DAC_DN_CONV_DISABLE,
- NULL);
-
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_01_INPUT1_CTRL1,
- SMA1303_LEFTPOL_MASK,
- SMA1303_LOW_FIRST_CH,
- NULL);
- break;
-
- case 192000:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A2_TOP_MAN1,
- SMA1303_DAC_DN_CONV_MASK,
- SMA1303_DAC_DN_CONV_ENABLE,
- NULL);
-
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_01_INPUT1_CTRL1,
- SMA1303_LEFTPOL_MASK,
- SMA1303_HIGH_FIRST_CH,
- NULL);
- break;
-
- default:
- dev_err(component->dev, "%s not support rate : %d\n",
- __func__, params_rate(params));
-
- return -EINVAL;
- }
-
- } else {
-
- switch (params_format(params)) {
-
- case SNDRV_PCM_FORMAT_S16_LE:
- dev_dbg(component->dev,
- "%s set format SNDRV_PCM_FORMAT_S16_LE\n",
- __func__);
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A4_TOP_MAN3,
- SMA1303_SCK_RATE_MASK,
- SMA1303_SCK_32FS,
- NULL);
- break;
-
- case SNDRV_PCM_FORMAT_S24_LE:
- dev_dbg(component->dev,
- "%s set format SNDRV_PCM_FORMAT_S24_LE\n",
- __func__);
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A4_TOP_MAN3,
- SMA1303_SCK_RATE_MASK,
- SMA1303_SCK_64FS,
- NULL);
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- dev_dbg(component->dev,
- "%s set format SNDRV_PCM_FORMAT_S32_LE\n",
- __func__);
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A4_TOP_MAN3,
- SMA1303_SCK_RATE_MASK,
- SMA1303_SCK_64FS,
- NULL);
- break;
- default:
- dev_err(component->dev,
- "%s not support data bit : %d\n", __func__,
- params_format(params));
- return -EINVAL;
- }
- }
-
- switch (sma1303->format) {
- case SND_SOC_DAIFMT_I2S:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_01_INPUT1_CTRL1,
- SMA1303_I2S_MODE_MASK,
- SMA1303_STANDARD_I2S,
- NULL);
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A4_TOP_MAN3,
- SMA1303_O_FORMAT_MASK,
- SMA1303_O_FMT_I2S,
- NULL);
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_01_INPUT1_CTRL1,
- SMA1303_I2S_MODE_MASK,
- SMA1303_LJ,
- NULL);
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A4_TOP_MAN3,
- SMA1303_O_FORMAT_MASK,
- SMA1303_O_FMT_LJ,
- NULL);
- break;
- case SND_SOC_DAIFMT_RIGHT_J:
- switch (params_width(params)) {
- case 16:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_01_INPUT1_CTRL1,
- SMA1303_I2S_MODE_MASK,
- SMA1303_RJ_16BIT,
- NULL);
- break;
- case 24:
- case 32:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_01_INPUT1_CTRL1,
- SMA1303_I2S_MODE_MASK,
- SMA1303_RJ_24BIT,
- NULL);
- break;
- }
- break;
- case SND_SOC_DAIFMT_DSP_A:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_01_INPUT1_CTRL1,
- SMA1303_I2S_MODE_MASK,
- SMA1303_STANDARD_I2S,
- NULL);
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A4_TOP_MAN3,
- SMA1303_O_FORMAT_MASK,
- SMA1303_O_FMT_TDM,
- NULL);
- break;
- }
-
- switch (params_width(params)) {
- case 16:
- case 24:
- case 32:
- break;
- default:
- dev_err(component->dev,
- "%s not support data bit : %d\n", __func__,
- params_format(params));
- return -EINVAL;
- }
- if (ret < 0)
- return -EINVAL;
-
- return 0;
-}
-
-static int sma1303_dai_set_sysclk_amp(struct snd_soc_dai *dai,
- int clk_id, unsigned int freq, int dir)
-{
- struct snd_soc_component *component = dai->component;
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
-
- switch (clk_id) {
- case SMA1303_EXTERNAL_CLOCK_19_2:
- break;
- case SMA1303_EXTERNAL_CLOCK_24_576:
- break;
- case SMA1303_PLL_CLKIN_MCLK:
- break;
- case SMA1303_PLL_CLKIN_BCLK:
- break;
- default:
- dev_err(component->dev, "Invalid clk id: %d\n", clk_id);
- return -EINVAL;
- }
- sma1303->sys_clk_id = clk_id;
- return 0;
-}
-
-static int sma1303_dai_mute(struct snd_soc_dai *dai, int mute, int stream)
-{
- struct snd_soc_component *component = dai->component;
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
- int ret = 0;
-
- if (stream == SNDRV_PCM_STREAM_CAPTURE)
- return ret;
-
- if (mute) {
- dev_dbg(component->dev, "%s : %s\n", __func__, "MUTE");
-
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_0E_MUTE_VOL_CTRL,
- SMA1303_SPK_MUTE_MASK,
- SMA1303_SPK_MUTE,
- NULL);
-
- /* Need to wait time for mute slope */
- msleep(55);
- } else {
- if (!sma1303->force_mute_status) {
- dev_dbg(component->dev, "%s : %s\n",
- __func__, "UNMUTE");
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_0E_MUTE_VOL_CTRL,
- SMA1303_SPK_MUTE_MASK,
- SMA1303_SPK_UNMUTE,
- NULL);
- } else {
- dev_dbg(sma1303->dev,
- "%s : FORCE MUTE!!!\n", __func__);
- }
- }
-
- if (ret < 0)
- return -EINVAL;
- return 0;
-}
-
-static int sma1303_dai_set_fmt_amp(struct snd_soc_dai *dai,
- unsigned int fmt)
-{
- struct snd_soc_component *component = dai->component;
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
- int ret = 0;
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-
- case SND_SOC_DAIFMT_CBC_CFC:
- dev_dbg(component->dev,
- "%s : %s\n", __func__, "I2S/TDM Device mode");
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_01_INPUT1_CTRL1,
- SMA1303_CONTROLLER_DEVICE_MASK,
- SMA1303_DEVICE_MODE,
- NULL);
- break;
-
- case SND_SOC_DAIFMT_CBP_CFP:
- dev_dbg(component->dev,
- "%s : %s\n", __func__, "I2S/TDM Controller mode");
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_01_INPUT1_CTRL1,
- SMA1303_CONTROLLER_DEVICE_MASK,
- SMA1303_CONTROLLER_MODE,
- NULL);
- break;
-
- default:
- dev_err(component->dev,
- "Unsupported Controller/Device : 0x%x\n", fmt);
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-
- case SND_SOC_DAIFMT_I2S:
- case SND_SOC_DAIFMT_RIGHT_J:
- case SND_SOC_DAIFMT_LEFT_J:
- case SND_SOC_DAIFMT_DSP_A:
- case SND_SOC_DAIFMT_DSP_B:
- sma1303->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
- break;
- default:
- dev_err(component->dev,
- "Unsupported Audio Interface Format : 0x%x\n", fmt);
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-
- case SND_SOC_DAIFMT_IB_NF:
- dev_dbg(component->dev, "%s : %s\n",
- __func__, "Invert BCLK + Normal Frame");
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_01_INPUT1_CTRL1,
- SMA1303_SCK_RISING_MASK,
- SMA1303_SCK_RISING_EDGE,
- NULL);
- break;
- case SND_SOC_DAIFMT_IB_IF:
- dev_dbg(component->dev, "%s : %s\n",
- __func__, "Invert BCLK + Invert Frame");
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_01_INPUT1_CTRL1,
- SMA1303_LEFTPOL_MASK|SMA1303_SCK_RISING_MASK,
- SMA1303_HIGH_FIRST_CH|SMA1303_SCK_RISING_EDGE,
- NULL);
- break;
- case SND_SOC_DAIFMT_NB_IF:
- dev_dbg(component->dev, "%s : %s\n",
- __func__, "Normal BCLK + Invert Frame");
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_01_INPUT1_CTRL1,
- SMA1303_LEFTPOL_MASK,
- SMA1303_HIGH_FIRST_CH,
- NULL);
- break;
- case SND_SOC_DAIFMT_NB_NF:
- dev_dbg(component->dev, "%s : %s\n",
- __func__, "Normal BCLK + Normal Frame");
- break;
- default:
- dev_err(component->dev,
- "Unsupported Bit & Frameclock : 0x%x\n", fmt);
- return -EINVAL;
- }
-
- if (ret < 0)
- return -EINVAL;
- return 0;
-}
-
-static int sma1303_dai_set_tdm_slot(struct snd_soc_dai *dai,
- unsigned int tx_mask, unsigned int rx_mask,
- int slots, int slot_width)
-{
- struct snd_soc_component *component = dai->component;
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
- int ret = 0;
-
- dev_dbg(component->dev, "%s : slots = %d, slot_width - %d\n",
- __func__, slots, slot_width);
-
- sma1303->frame_size = slot_width * slots;
-
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A4_TOP_MAN3,
- SMA1303_O_FORMAT_MASK,
- SMA1303_O_FMT_TDM,
- NULL);
-
- switch (slot_width) {
- case 16:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A6_TDM2,
- SMA1303_TDM_DL_MASK,
- SMA1303_TDM_DL_16,
- NULL);
- break;
- case 32:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A6_TDM2,
- SMA1303_TDM_DL_MASK,
- SMA1303_TDM_DL_32,
- NULL);
- break;
- default:
- dev_err(component->dev, "%s not support TDM %d slot_width\n",
- __func__, slot_width);
- break;
- }
-
- switch (slots) {
- case 4:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A6_TDM2,
- SMA1303_TDM_N_SLOT_MASK,
- SMA1303_TDM_N_SLOT_4,
- NULL);
- break;
- case 8:
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A6_TDM2,
- SMA1303_TDM_N_SLOT_MASK,
- SMA1303_TDM_N_SLOT_8,
- NULL);
- break;
- default:
- dev_err(component->dev, "%s not support TDM %d slots\n",
- __func__, slots);
- break;
- }
-
- if (sma1303->tdm_slot_rx < slots)
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A5_TDM1,
- SMA1303_TDM_SLOT1_RX_POS_MASK,
- (sma1303->tdm_slot_rx) << 3,
- NULL);
- else
- dev_err(component->dev, "%s Incorrect tdm-slot-rx %d set\n",
- __func__, sma1303->tdm_slot_rx);
-
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A5_TDM1,
- SMA1303_TDM_CLK_POL_MASK,
- SMA1303_TDM_CLK_POL_RISE,
- NULL);
-
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A5_TDM1,
- SMA1303_TDM_TX_MODE_MASK,
- SMA1303_TDM_TX_MONO,
- NULL);
-
- if (sma1303->tdm_slot_tx < slots)
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_A6_TDM2,
- SMA1303_TDM_SLOT1_TX_POS_MASK,
- (sma1303->tdm_slot_tx) << 3,
- NULL);
- else
- dev_err(component->dev, "%s Incorrect tdm-slot-tx %d set\n",
- __func__, sma1303->tdm_slot_tx);
-
- if (ret < 0)
- return -EINVAL;
- return 0;
-}
-
-static const struct snd_soc_dai_ops sma1303_dai_ops_amp = {
- .set_sysclk = sma1303_dai_set_sysclk_amp,
- .set_fmt = sma1303_dai_set_fmt_amp,
- .hw_params = sma1303_dai_hw_params_amp,
- .mute_stream = sma1303_dai_mute,
- .set_tdm_slot = sma1303_dai_set_tdm_slot,
-};
-
-#define SMA1303_RATES SNDRV_PCM_RATE_8000_192000
-#define SMA1303_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE)
-
-static struct snd_soc_dai_driver sma1303_dai[] = {
- {
- .name = "sma1303-amplifier",
- .id = 0,
- .playback = {
- .stream_name = "Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = SMA1303_RATES,
- .formats = SMA1303_FORMATS,
- },
- .capture = {
- .stream_name = "Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rates = SMA1303_RATES,
- .formats = SMA1303_FORMATS,
- },
- .ops = &sma1303_dai_ops_amp,
- },
-};
-
-static void sma1303_check_fault_worker(struct work_struct *work)
-{
- struct sma1303_priv *sma1303 =
- container_of(work, struct sma1303_priv, check_fault_work.work);
- int ret = 0;
- unsigned int over_temp, ocp_val, uvlo_val;
-
- if (sma1303->tsdw_cnt)
- ret = sma1303_regmap_read(sma1303,
- SMA1303_0A_SPK_VOL, &sma1303->cur_vol);
- else
- ret = sma1303_regmap_read(sma1303,
- SMA1303_0A_SPK_VOL, &sma1303->init_vol);
-
- if (ret != 0) {
- dev_err(sma1303->dev,
- "failed to read SMA1303_0A_SPK_VOL : %d\n", ret);
- return;
- }
-
- ret = sma1303_regmap_read(sma1303, SMA1303_FA_STATUS1, &over_temp);
- if (ret != 0) {
- dev_err(sma1303->dev,
- "failed to read SMA1303_FA_STATUS1 : %d\n", ret);
- return;
- }
-
- ret = sma1303_regmap_read(sma1303, SMA1303_FB_STATUS2, &ocp_val);
- if (ret != 0) {
- dev_err(sma1303->dev,
- "failed to read SMA1303_FB_STATUS2 : %d\n", ret);
- return;
- }
-
- ret = sma1303_regmap_read(sma1303, SMA1303_FF_DEVICE_INDEX, &uvlo_val);
- if (ret != 0) {
- dev_err(sma1303->dev,
- "failed to read SMA1303_FF_DEVICE_INDEX : %d\n", ret);
- return;
- }
-
- if (~over_temp & SMA1303_OT1_OK_STATUS) {
- dev_crit(sma1303->dev,
- "%s : OT1(Over Temperature Level 1)\n", __func__);
-
- if ((sma1303->cur_vol + 6) <= 0xFF)
- sma1303_regmap_write(sma1303,
- SMA1303_0A_SPK_VOL, sma1303->cur_vol + 6);
-
- sma1303->tsdw_cnt++;
- } else if (sma1303->tsdw_cnt) {
- sma1303_regmap_write(sma1303,
- SMA1303_0A_SPK_VOL, sma1303->init_vol);
- sma1303->tsdw_cnt = 0;
- sma1303->cur_vol = sma1303->init_vol;
- }
-
- if (~over_temp & SMA1303_OT2_OK_STATUS) {
- dev_crit(sma1303->dev,
- "%s : OT2(Over Temperature Level 2)\n", __func__);
- }
- if (ocp_val & SMA1303_OCP_SPK_STATUS) {
- dev_crit(sma1303->dev,
- "%s : OCP_SPK(Over Current Protect SPK)\n", __func__);
- }
- if (ocp_val & SMA1303_OCP_BST_STATUS) {
- dev_crit(sma1303->dev,
- "%s : OCP_BST(Over Current Protect Boost)\n", __func__);
- }
- if ((ocp_val & SMA1303_CLK_MON_STATUS) && (sma1303->amp_power_status)) {
- dev_crit(sma1303->dev,
- "%s : CLK_FAULT(No clock input)\n", __func__);
- }
- if (uvlo_val & SMA1303_UVLO_BST_STATUS) {
- dev_crit(sma1303->dev,
- "%s : UVLO(Under Voltage Lock Out)\n", __func__);
- }
-
- if ((over_temp != sma1303->last_over_temp) ||
- (ocp_val != sma1303->last_ocp_val)) {
-
- dev_crit(sma1303->dev, "Please check AMP status");
- dev_dbg(sma1303->dev, "STATUS1=0x%02X : STATUS2=0x%02X\n",
- over_temp, ocp_val);
- sma1303->last_over_temp = over_temp;
- sma1303->last_ocp_val = ocp_val;
- }
-
- if (sma1303->check_fault_status) {
- if (sma1303->check_fault_period > 0)
- queue_delayed_work(system_freezable_wq,
- &sma1303->check_fault_work,
- sma1303->check_fault_period * HZ);
- else
- queue_delayed_work(system_freezable_wq,
- &sma1303->check_fault_work,
- CHECK_PERIOD_TIME * HZ);
- }
-
- if (!(~over_temp & SMA1303_OT1_OK_STATUS)
- && !(~over_temp & SMA1303_OT2_OK_STATUS)
- && !(ocp_val & SMA1303_OCP_SPK_STATUS)
- && !(ocp_val & SMA1303_OCP_BST_STATUS)
- && !(ocp_val & SMA1303_CLK_MON_STATUS)
- && !(uvlo_val & SMA1303_UVLO_BST_STATUS)) {
- }
-}
-
-static int sma1303_probe(struct snd_soc_component *component)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
-
- snd_soc_dapm_sync(dapm);
-
- return 0;
-}
-
-static void sma1303_remove(struct snd_soc_component *component)
-{
- struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
-
- cancel_delayed_work_sync(&sma1303->check_fault_work);
-}
-
-static const struct snd_soc_component_driver sma1303_component = {
- .probe = sma1303_probe,
- .remove = sma1303_remove,
- .controls = sma1303_snd_controls,
- .num_controls = ARRAY_SIZE(sma1303_snd_controls),
- .dapm_widgets = sma1303_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(sma1303_dapm_widgets),
- .dapm_routes = sma1303_audio_map,
- .num_dapm_routes = ARRAY_SIZE(sma1303_audio_map),
-};
-
-const struct regmap_config sma_i2c_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = SMA1303_FF_DEVICE_INDEX,
- .readable_reg = sma1303_readable_register,
- .writeable_reg = sma1303_writeable_register,
- .volatile_reg = sma1303_volatile_register,
-
- .cache_type = REGCACHE_NONE,
- .reg_defaults = sma1303_reg_def,
- .num_reg_defaults = ARRAY_SIZE(sma1303_reg_def),
-};
-
-static ssize_t check_fault_period_show(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
-
- return sysfs_emit(buf, "%ld\n", sma1303->check_fault_period);
-}
-
-static ssize_t check_fault_period_store(struct device *dev,
- struct device_attribute *devattr, const char *buf, size_t count)
-{
- struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
- int ret;
-
- ret = kstrtol(buf, 10, &sma1303->check_fault_period);
-
- if (ret)
- return -EINVAL;
-
- return (ssize_t)count;
-}
-
-static DEVICE_ATTR_RW(check_fault_period);
-
-static ssize_t check_fault_status_show(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
-
- return sysfs_emit(buf, "%ld\n", sma1303->check_fault_status);
-}
-
-static ssize_t check_fault_status_store(struct device *dev,
- struct device_attribute *devattr, const char *buf, size_t count)
-{
- struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
- int ret;
-
- ret = kstrtol(buf, 10, &sma1303->check_fault_status);
-
- if (ret)
- return -EINVAL;
-
- if (sma1303->check_fault_status) {
- if (sma1303->check_fault_period > 0)
- queue_delayed_work(system_freezable_wq,
- &sma1303->check_fault_work,
- sma1303->check_fault_period * HZ);
- else
- queue_delayed_work(system_freezable_wq,
- &sma1303->check_fault_work,
- CHECK_PERIOD_TIME * HZ);
- }
-
- return (ssize_t)count;
-}
-
-static DEVICE_ATTR_RW(check_fault_status);
-
-static struct attribute *sma1303_attr[] = {
- &dev_attr_check_fault_period.attr,
- &dev_attr_check_fault_status.attr,
- NULL,
-};
-
-static struct attribute_group sma1303_attr_group = {
- .attrs = sma1303_attr,
-};
-
-static int sma1303_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct sma1303_priv *sma1303;
- struct device_node *np = client->dev.of_node;
- int ret, i = 0;
- u32 value = 0;
- unsigned int device_info, status, otp_stat;
-
- sma1303 = devm_kzalloc(&client->dev,
- sizeof(struct sma1303_priv), GFP_KERNEL);
- if (!sma1303)
- return -ENOMEM;
- sma1303->dev = &client->dev;
-
- sma1303->regmap = devm_regmap_init_i2c(client, &sma_i2c_regmap);
- if (IS_ERR(sma1303->regmap)) {
- ret = PTR_ERR(sma1303->regmap);
- dev_err(&client->dev,
- "Failed to allocate register map: %d\n", ret);
-
- return ret;
- }
-
- if (np) {
- if (!of_property_read_u32(np, "i2c-retry", &value)) {
- if (value > 50 || value <= 0) {
- sma1303->retry_cnt = SMA1303_I2C_RETRY_COUNT;
- dev_dbg(&client->dev, "%s : %s\n", __func__,
- "i2c-retry out of range (up to 50)");
- } else {
- sma1303->retry_cnt = value;
- dev_dbg(&client->dev, "%s : %s = %u\n",
- __func__, "i2c-retry count", value);
- }
- } else {
- dev_dbg(&client->dev, "%s : %s = %d\n", __func__,
- "i2c-retry count", SMA1303_I2C_RETRY_COUNT);
- sma1303->retry_cnt = SMA1303_I2C_RETRY_COUNT;
- }
- if (!of_property_read_u32(np, "tdm-slot-rx", &value)) {
- dev_dbg(&client->dev,
- "tdm slot rx is '%d' from DT\n", value);
- sma1303->tdm_slot_rx = value;
- } else {
- dev_dbg(&client->dev,
- "Default setting of tdm slot rx is '0'\n");
- sma1303->tdm_slot_rx = 0;
- }
- if (!of_property_read_u32(np, "tdm-slot-tx", &value)) {
- dev_dbg(&client->dev,
- "tdm slot tx is '%u' from DT\n", value);
- sma1303->tdm_slot_tx = value;
- } else {
- dev_dbg(&client->dev,
- "Default setting of tdm slot tx is '0'\n");
- sma1303->tdm_slot_tx = 0;
- }
- if (!of_property_read_u32(np, "sys-clk-id", &value)) {
- switch (value) {
- case SMA1303_EXTERNAL_CLOCK_19_2:
- case SMA1303_EXTERNAL_CLOCK_24_576:
- case SMA1303_PLL_CLKIN_MCLK:
- dev_dbg(&client->dev, "MCLK is not supported\n");
- break;
- case SMA1303_PLL_CLKIN_BCLK:
- dev_dbg(&client->dev,
- "Take an BCLK(SCK) and covert it to an internal PLL for use\n");
- break;
- default:
- dev_err(&client->dev,
- "Invalid sys-clk-id: %u\n", value);
- return -EINVAL;
- }
- sma1303->sys_clk_id = value;
- } else {
- dev_dbg(&client->dev, "Use the internal PLL clock by default\n");
- sma1303->sys_clk_id = SMA1303_PLL_CLKIN_BCLK;
- }
- } else {
- dev_err(&client->dev,
- "device node initialization error\n");
- devm_kfree(&client->dev, sma1303);
- return -ENODEV;
- }
-
- ret = sma1303_regmap_read(sma1303,
- SMA1303_FF_DEVICE_INDEX, &device_info);
-
- if ((ret != 0) || ((device_info & 0xF8) != SMA1303_DEVICE_ID)) {
- dev_err(&client->dev, "device initialization error (%d 0x%02X)",
- ret, device_info);
- }
- dev_dbg(&client->dev, "chip version 0x%02X\n", device_info);
-
- ret += sma1303_regmap_update_bits(sma1303,
- SMA1303_00_SYSTEM_CTRL,
- SMA1303_RESETBYI2C_MASK, SMA1303_RESETBYI2C_RESET,
- NULL);
-
- ret += sma1303_regmap_read(sma1303, SMA1303_FF_DEVICE_INDEX, &status);
- sma1303->rev_num = status & SMA1303_REV_NUM_STATUS;
- if (sma1303->rev_num == SMA1303_REV_NUM_TV0)
- dev_dbg(&client->dev, "SMA1303 Trimming Version 0\n");
- else if (sma1303->rev_num == SMA1303_REV_NUM_TV1)
- dev_dbg(&client->dev, "SMA1303 Trimming Version 1\n");
-
- ret += sma1303_regmap_read(sma1303, SMA1303_FB_STATUS2, &otp_stat);
- if (ret < 0)
- dev_err(&client->dev,
- "failed to read, register: %02X, ret: %d\n",
- SMA1303_FF_DEVICE_INDEX, ret);
-
- if (((sma1303->rev_num == SMA1303_REV_NUM_TV0) &&
- ((otp_stat & 0x0E) == SMA1303_OTP_STAT_OK_0)) ||
- ((sma1303->rev_num != SMA1303_REV_NUM_TV0) &&
- ((otp_stat & 0x0C) == SMA1303_OTP_STAT_OK_1)))
- dev_dbg(&client->dev, "SMA1303 OTP Status Successful\n");
- else
- dev_dbg(&client->dev, "SMA1303 OTP Status Fail\n");
-
- for (i = 0; i < (unsigned int)ARRAY_SIZE(sma1303_reg_def); i++)
- ret += sma1303_regmap_write(sma1303,
- sma1303_reg_def[i].reg,
- sma1303_reg_def[i].def);
-
- sma1303->amp_mode = SMA1303_MONO;
- sma1303->amp_power_status = false;
- sma1303->check_fault_period = CHECK_PERIOD_TIME;
- sma1303->check_fault_status = true;
- sma1303->force_mute_status = false;
- sma1303->init_vol = 0x31;
- sma1303->cur_vol = sma1303->init_vol;
- sma1303->last_bclk = 0;
- sma1303->last_ocp_val = 0x08;
- sma1303->last_over_temp = 0xC0;
- sma1303->tsdw_cnt = 0;
-
- sma1303->dev = &client->dev;
- sma1303->kobj = &client->dev.kobj;
-
- INIT_DELAYED_WORK(&sma1303->check_fault_work,
- sma1303_check_fault_worker);
-
- i2c_set_clientdata(client, sma1303);
-
- sma1303->pll_matches = sma1303_pll_matches;
- sma1303->num_of_pll_matches =
- ARRAY_SIZE(sma1303_pll_matches);
-
- ret = devm_snd_soc_register_component(&client->dev,
- &sma1303_component, sma1303_dai, 1);
- if (ret) {
- dev_err(&client->dev, "Failed to register component");
-
- return ret;
- }
-
- sma1303->attr_grp = &sma1303_attr_group;
- ret = sysfs_create_group(sma1303->kobj, sma1303->attr_grp);
- if (ret) {
- dev_err(&client->dev,
- "failed to create attribute group [%d]\n", ret);
- sma1303->attr_grp = NULL;
- }
-
- return ret;
-}
-
-static void sma1303_i2c_remove(struct i2c_client *client)
-{
- struct sma1303_priv *sma1303 =
- (struct sma1303_priv *) i2c_get_clientdata(client);
-
- cancel_delayed_work_sync(&sma1303->check_fault_work);
-}
-
-static const struct i2c_device_id sma1303_i2c_id[] = {
- {"sma1303", 0},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, sma1303_i2c_id);
-
-static const struct of_device_id sma1303_of_match[] = {
- { .compatible = "irondevice,sma1303", },
- { }
-};
-MODULE_DEVICE_TABLE(of, sma1303_of_match);
-
-static struct i2c_driver sma1303_i2c_driver = {
- .driver = {
- .name = "sma1303",
- .of_match_table = sma1303_of_match,
- },
- .probe = sma1303_i2c_probe,
- .remove = sma1303_i2c_remove,
- .id_table = sma1303_i2c_id,
-};
-
-module_i2c_driver(sma1303_i2c_driver);
-
-MODULE_DESCRIPTION("ALSA SoC SMA1303 driver");
-MODULE_AUTHOR("Gyuhwa Park, <gyuhwa.park(a)irondevice.com>");
-MODULE_AUTHOR("Kiseok Jo, <kiseok.jo(a)irondevice.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/sma1303.h b/sound/soc/codecs/sma1303.h
deleted file mode 100644
index ae70f207adde..000000000000
--- a/sound/soc/codecs/sma1303.h
+++ /dev/null
@@ -1,609 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * sma1303.h -- sma1303 ALSA SoC Audio driver
- *
- * Copyright 2023 Iron Device Corporation
- *
- * Author: Kiseok Jo <kiseok.jo(a)irondevice.com>
- *
- */
-
-#ifndef _SMA1303_H
-#define _SMA1303_H
-
-#define SMA1303_I2C_ADDR_00 0x1e
-#define SMA1303_I2C_ADDR_01 0x3e
-#define SMA1303_I2C_ADDR_10 0x5e
-#define SMA1303_I2C_ADDR_11 0x7e
-
-#define SMA1303_EXTERNAL_CLOCK_19_2 0x00
-#define SMA1303_EXTERNAL_CLOCK_24_576 0x01
-#define SMA1303_PLL_CLKIN_MCLK 0x02
-#define SMA1303_PLL_CLKIN_BCLK 0x03
-
-#define SMA1303_MONO 0x00
-#define SMA1303_STEREO 0x01
-
-#define SMA1303_I2C_RETRY_COUNT 3
-
-/*
- * SMA1303 Register Definition
- */
-
-/* SMA1303 Register Addresses */
-#define SMA1303_00_SYSTEM_CTRL 0x00
-#define SMA1303_01_INPUT1_CTRL1 0x01
-#define SMA1303_02_INPUT1_CTRL2 0x02
-#define SMA1303_03_INPUT1_CTRL3 0x03
-#define SMA1303_04_INPUT1_CTRL4 0x04
-/* 0x05 ~ 0x08 : Reserved */
-#define SMA1303_09_OUTPUT_CTRL 0x09
-#define SMA1303_0A_SPK_VOL 0x0a
-#define SMA1303_0B_BST_TEST 0x0b
-#define SMA1303_0C_BST_TEST1 0x0c
-#define SMA1303_0D_SPK_TEST 0x0d
-#define SMA1303_0E_MUTE_VOL_CTRL 0x0e
-/* 0x0F : Reserved */
-#define SMA1303_10_SYSTEM_CTRL1 0x10
-#define SMA1303_11_SYSTEM_CTRL2 0x11
-#define SMA1303_12_SYSTEM_CTRL3 0x12
-/* 0x13 : Reserved */
-#define SMA1303_14_MODULATOR 0x14
-#define SMA1303_15_BASS_SPK1 0x15
-#define SMA1303_16_BASS_SPK2 0x16
-#define SMA1303_17_BASS_SPK3 0x17
-#define SMA1303_18_BASS_SPK4 0x18
-#define SMA1303_19_BASS_SPK5 0x19
-#define SMA1303_1A_BASS_SPK6 0x1a
-#define SMA1303_1B_BASS_SPK7 0x1b
-/* 0x1C ~ 0x22 : Reserved */
-#define SMA1303_23_COMP_LIM1 0x23
-#define SMA1303_24_COMP_LIM2 0x24
-#define SMA1303_25_COMP_LIM3 0x25
-#define SMA1303_26_COMP_LIM4 0x26
-/* 0x27 ~ 0x32 : Reserved */
-#define SMA1303_33_SDM_CTRL 0x33
-#define SMA1303_34_OTP_DATA1 0x34
-/* 0x35 : Reserved */
-#define SMA1303_36_PROTECTION 0x36
-#define SMA1303_37_SLOPE_CTRL 0x37
-#define SMA1303_38_OTP_TRM0 0x38
-/* 0x39 ~ 0x3A : Reserved */
-#define SMA1303_3B_TEST1 0x3b
-#define SMA1303_3C_TEST2 0x3c
-#define SMA1303_3D_TEST3 0x3d
-#define SMA1303_3E_ATEST1 0x3e
-#define SMA1303_3F_ATEST2 0x3f
-/* 0x40 ~ 0x8A : Reserved */
-#define SMA1303_8B_PLL_POST_N 0x8b
-#define SMA1303_8C_PLL_N 0x8c
-#define SMA1303_8D_PLL_A_SETTING 0x8d
-#define SMA1303_8E_PLL_CTRL 0x8e
-#define SMA1303_8F_PLL_P_CP 0x8f
-#define SMA1303_90_POSTSCALER 0x90
-#define SMA1303_91_CLASS_G_CTRL 0x91
-#define SMA1303_92_FDPEC_CTRL 0x92
-/* 0x93 : Reserved */
-#define SMA1303_94_BOOST_CTRL1 0x94
-#define SMA1303_95_BOOST_CTRL2 0x95
-#define SMA1303_96_BOOST_CTRL3 0x96
-#define SMA1303_97_BOOST_CTRL4 0x97
-/* 0x98 ~ 0x9F : Reserved */
-#define SMA1303_A0_PAD_CTRL0 0xa0
-#define SMA1303_A1_PAD_CTRL1 0xa1
-#define SMA1303_A2_TOP_MAN1 0xa2
-#define SMA1303_A3_TOP_MAN2 0xa3
-#define SMA1303_A4_TOP_MAN3 0xa4
-#define SMA1303_A5_TDM1 0xa5
-#define SMA1303_A6_TDM2 0xa6
-#define SMA1303_A7_CLK_MON 0xa7
-/* 0xA8 ~ 0xF9 : Reserved */
-#define SMA1303_FA_STATUS1 0xfa
-#define SMA1303_FB_STATUS2 0xfb
-/* 0xFC ~ 0xFE : Reserved */
-#define SMA1303_FF_DEVICE_INDEX 0xff
-
-/* SMA1303 Registers Bit Fields */
-
-/* SYSTEM_CTRL : 0x00 */
-#define SMA1303_RESETBYI2C_MASK (1<<1)
-#define SMA1303_RESETBYI2C_NORMAL (0<<1)
-#define SMA1303_RESETBYI2C_RESET (1<<1)
-
-#define SMA1303_POWER_MASK (1<<0)
-#define SMA1303_POWER_OFF (0<<0)
-#define SMA1303_POWER_ON (1<<0)
-
-/* INTPUT CTRL1 : 0x01 */
-#define SMA1303_CONTROLLER_DEVICE_MASK (1<<7)
-#define SMA1303_DEVICE_MODE (0<<7)
-#define SMA1303_CONTROLLER_MODE (1<<7)
-
-#define SMA1303_I2S_MODE_MASK (7<<4)
-#define SMA1303_STANDARD_I2S (0<<4)
-#define SMA1303_LJ (1<<4)
-#define SMA1303_RJ_16BIT (4<<4)
-#define SMA1303_RJ_18BIT (5<<4)
-#define SMA1303_RJ_20BIT (6<<4)
-#define SMA1303_RJ_24BIT (7<<4)
-
-#define SMA1303_LEFTPOL_MASK (1<<3)
-#define SMA1303_LOW_FIRST_CH (0<<3)
-#define SMA1303_HIGH_FIRST_CH (1<<3)
-
-#define SMA1303_SCK_RISING_MASK (1<<2)
-#define SMA1303_SCK_FALLING_EDGE (0<<2)
-#define SMA1303_SCK_RISING_EDGE (1<<2)
-
-/* INTPUT CTRL2 : 0x02 */
-#define SMA1303_IMODE_MASK (3<<6)
-#define SMA1303_I2S (0<<6)
-#define SMA1303_PCM_SHORT (1<<6)
-#define SMA1303_PCM_LONG (2<<6)
-
-#define RSMA1303_IGHT_FIRST_MASK (1<<5)
-#define SMA1303_LEFT_NORMAL (0<<5)
-#define SMA1303_RIGHT_INVERTED (1<<5)
-
-#define SMA1303_PCM_ALAW_MASK (1<<4)
-#define SMA1303_PCM_U_DECODING (0<<4)
-#define SMA1303_PCM_A_DECODING (1<<4)
-
-#define SMA1303_PCM_COMP_MASK (1<<3)
-#define SMA1303_PCM_LINEAR (0<<3)
-#define SMA1303_PCM_COMPANDING (1<<3)
-
-#define SMA1303_INPUTSEL_MASK (1<<2)
-#define SMA1303_PCM_8KHZ (0<<2)
-#define SMA1303_PCM_16KHZ (1<<2)
-
-#define SMA1303_PCM_STEREO_MASK (1<<1)
-#define SMA1303_PCM_MONO (0<<1)
-#define SMA1303_PCM_STEREO (1<<1)
-
-#define SMA1303_PCM_DL_MASK (1<<0)
-#define SMA1303_PCM_8BIT (0<<0)
-#define SMA1303_PCM_16BIT (1<<0)
-
-/* INTPUT CTRL3 : 0x03 */
-#define SMA1303_PCM_N_SLOT_MASK (15<<0)
-#define SMA1303_PCM_N_SLOT1 (0<<0)
-#define SMA1303_PCM_N_SLOT2 (1<<0)
-#define SMA1303_PCM_N_SLOT3 (2<<0)
-#define SMA1303_PCM_N_SLOT4 (3<<0)
-#define SMA1303_PCM_N_SLOT5 (4<<0)
-#define SMA1303_PCM_N_SLOT6 (5<<0)
-#define SMA1303_PCM_N_SLOT7 (6<<0)
-#define SMA1303_PCM_N_SLOT8 (7<<0)
-#define SMA1303_PCM_N_SLOT9 (8<<0)
-#define SMA1303_PCM_N_SLOT10 (9<<0)
-#define SMA1303_PCM_N_SLOT11 (10<<0)
-#define SMA1303_PCM_N_SLOT12 (11<<0)
-#define SMA1303_PCM_N_SLOT13 (12<<0)
-#define SMA1303_PCM_N_SLOT14 (13<<0)
-#define SMA1303_PCM_N_SLOT15 (14<<0)
-#define SMA1303_PCM_N_SLOT16 (15<<0)
-
-/* INTPUT CTRL4 : 0x04 */
-#define SMA1303_PCM1_SLOT_MASK (15<<4)
-#define SMA1303_PCM1_SLOT1 (0<<4)
-#define SMA1303_PCM1_SLOT2 (1<<4)
-#define SMA1303_PCM1_SLOT3 (2<<4)
-#define SMA1303_PCM1_SLOT4 (3<<4)
-#define SMA1303_PCM1_SLOT5 (4<<4)
-#define SMA1303_PCM1_SLOT6 (5<<4)
-#define SMA1303_PCM1_SLOT7 (6<<4)
-#define SMA1303_PCM1_SLOT8 (7<<4)
-#define SMA1303_PCM1_SLOT9 (8<<4)
-#define SMA1303_PCM1_SLOT10 (9<<4)
-#define SMA1303_PCM1_SLOT11 (10<<4)
-#define SMA1303_PCM1_SLOT12 (11<<4)
-#define SMA1303_PCM1_SLOT13 (12<<4)
-#define SMA1303_PCM1_SLOT14 (13<<4)
-#define SMA1303_PCM1_SLOT15 (14<<4)
-#define SMA1303_PCM1_SLOT16 (15<<4)
-
-#define SMA1303_PCM2_SLOT_MASK (15<<0)
-#define SMA1303_PCM2_SLOT1 (0<<0)
-#define SMA1303_PCM2_SLOT2 (1<<0)
-#define SMA1303_PCM2_SLOT3 (2<<0)
-#define SMA1303_PCM2_SLOT4 (3<<0)
-#define SMA1303_PCM2_SLOT5 (4<<0)
-#define SMA1303_PCM2_SLOT6 (5<<0)
-#define SMA1303_PCM2_SLOT7 (6<<0)
-#define SMA1303_PCM2_SLOT8 (7<<0)
-#define SMA1303_PCM2_SLOT9 (8<<0)
-#define SMA1303_PCM2_SLOT10 (9<<0)
-#define SMA1303_PCM2_SLOT11 (10<<0)
-#define SMA1303_PCM2_SLOT12 (11<<0)
-#define SMA1303_PCM2_SLOT13 (12<<0)
-#define SMA1303_PCM2_SLOT14 (13<<0)
-#define SMA1303_PCM2_SLOT15 (14<<0)
-#define SMA1303_PCM2_SLOT16 (15<<0)
-
-/* OUTPUT CTRL : 0x09 */
-#define SMA1303_PORT_CONFIG_MASK (3<<5)
-#define SMA1303_INPUT_PORT_ONLY (0<<5)
-#define SMA1303_OUTPUT_PORT_ENABLE (2<<5)
-
-#define SMA1303_PORT_OUT_SEL_MASK (7<<0)
-#define SMA1303_OUT_SEL_DISABLE (0<<0)
-#define SMA1303_FORMAT_CONVERTER (1<<0)
-#define SMA1303_MIXER_OUTPUT (2<<0)
-#define SMA1303_SPEAKER_PATH (3<<0)
-#define SMA1303_POSTSCALER_OUTPUT (4<<0)
-
-/* BST_TEST : 0x0B */
-#define SMA1303_BST_OFF_SLOPE_MASK (3<<6)
-#define SMA1303_BST_OFF_SLOPE_6_7ns (0<<6)
-#define SMA1303_BST_OFF_SLOPE_4_8ns (1<<6)
-#define SMA1303_BST_OFF_SLOPE_2_6ns (2<<6)
-#define SMA1303_BST_OFF_SLOPE_1_2ns (3<<6)
-
-#define SMA1303_OCP_TEST_MASK (1<<5)
-#define SMA1303_OCP_NORMAL_MODE (0<<5)
-#define SMA1303_OCP_TEST_MODE (1<<5)
-
-#define SMA1303_BST_FAST_LEBN_MASK (1<<4)
-#define SMA1303_BST_SHORT_LEB (0<<4)
-#define SMA1303_BST_LONG_LEB (1<<4)
-
-#define SMA1303_HIGH_PGAIN_MASK (1<<3)
-#define SMA1303_NORMAL_P_GAIN (0<<3)
-#define SMA1303_HIGH_P_GAIN (1<<3)
-
-#define SMA1303_VCOMP_MASK (1<<2)
-#define SMA1303_VCOMP_NORMAL_MODE (0<<2)
-#define SMA1303_VCOMP_V_MON_MODE (1<<2)
-
-#define SMA1303_PMOS_ON_MASK (1<<1)
-#define SMA1303_PMOS_NORMAL_MODE (0<<1)
-#define SMA1303_PMOS_TEST_MODE (1<<1)
-
-#define SMA1303_NMOS_ON_MASK (1<<0)
-#define SMA1303_NMOS_NORMAL_MODE (0<<0)
-#define SMA1303_NMOS_TEST_MODE (1<<0)
-
-/* BST_TEST1 : 0x0C */
-#define SMA1303_SET_OCP_H_MASK (3<<6)
-#define SMA1303_HIGH_OCP_4_5_LVL (0<<6)
-#define SMA1303_HIGH_OCP_3_2_LVL (1<<6)
-#define SMA1303_HIGH_OCP_2_1_LVL (2<<6)
-#define SMA1303_HIGH_OCP_0_9_LVL (3<<6)
-
-#define SMA1303_OCL_TEST_MASK (1<<5)
-#define SMA1303_OCL_NORMAL_MODE (0<<5)
-#define SMA1303_OCL_TEST_MODE (1<<5)
-
-#define SMA1303_LOOP_CHECK_MASK (1<<4)
-#define SMA1303_BST_LOOP_NORMAL_MODE (0<<4)
-#define SMA1303_BST_LOOP_CHECK_MODE (1<<4)
-
-#define SMA1303_EN_SH_PRT_MASK (1<<3)
-#define SMA1303_EN_SH_PRT_DISABLE (0<<3)
-#define SMA1303_EN_SH_PRT_ENABLE (1<<3)
-
-/* SPK_TEST : 0x0D */
-#define SMA1303_VREF_MON_MASK (1<<3)
-#define SMA1303_VREF_NORMAL_MODE (0<<3)
-#define SMA1303_VREF_V_MON_MODE (1<<3)
-
-#define SMA1303_SPK_OCP_DLYN_MASK (1<<2)
-#define SMA1303_SPK_OCP_LONG_DELAY (0<<2)
-#define SMA1303_SPK_OCP_NORMAL (1<<2)
-
-#define SMA1303_SPK_OFF_SLOPE_MASK (3<<0)
-#define SMA1303_SPK_OFF_SLOPE_SLOW (0<<0)
-#define SMA1303_SPK_OFF_SLOPE_FAST (3<<0)
-
-/* MUTE_VOL_CTRL : 0x0E */
-#define SMA1303_VOL_SLOPE_MASK (3<<6)
-#define SMA1303_VOL_SLOPE_OFF (0<<6)
-#define SMA1303_VOL_SLOPE_SLOW (1<<6)
-#define SMA1303_VOL_SLOPE_MID (2<<6)
-#define SMA1303_VOL_SLOPE_FAST (3<<6)
-
-#define SMA1303_MUTE_SLOPE_MASK (3<<4)
-#define SMA1303_MUTE_SLOPE_OFF (0<<4)
-#define SMA1303_MUTE_SLOPE_SLOW (1<<4)
-#define SMA1303_MUTE_SLOPE_MID (2<<4)
-#define SMA1303_MUTE_SLOPE_FAST (3<<4)
-
-#define SMA1303_SPK_MUTE_MASK (1<<0)
-#define SMA1303_SPK_UNMUTE (0<<0)
-#define SMA1303_SPK_MUTE (1<<0)
-
-/* SYSTEM_CTRL1 :0x10 */
-#define SMA1303_SPK_MODE_MASK (7<<2)
-#define SMA1303_SPK_OFF (0<<2)
-#define SMA1303_SPK_MONO (1<<2)
-#define SMA1303_SPK_STEREO (4<<2)
-
-/* SYSTEM_CTRL2 : 0x11 */
-#define SMA1303_SPK_BS_MASK (1<<6)
-#define SMA1303_SPK_BS_BYP (0<<6)
-#define SMA1303_SPK_BS_EN (1<<6)
-#define SMA1303_SPK_LIM_MASK (1<<5)
-#define SMA1303_SPK_LIM_BYP (0<<5)
-#define SMA1303_SPK_LIM_EN (1<<5)
-
-#define SMA1303_LR_DATA_SW_MASK (1<<4)
-#define SMA1303_LR_DATA_SW_NORMAL (0<<4)
-#define SMA1303_LR_DATA_SW_SWAP (1<<4)
-
-#define SMA1303_MONOMIX_MASK (1<<0)
-#define SMA1303_MONOMIX_OFF (0<<0)
-#define SMA1303_MONOMIX_ON (1<<0)
-
-/* SYSTEM_CTRL3 : 0x12 */
-#define SMA1303_INPUT_MASK (3<<6)
-#define SMA1303_INPUT_0_DB (0<<6)
-#define SMA1303_INPUT_M6_DB (1<<6)
-#define SMA1303_INPUT_M12_DB (2<<6)
-#define SMA1303_INPUT_INFI_DB (3<<6)
-#define SMA1303_INPUT_R_MASK (3<<4)
-#define SMA1303_INPUT_R_0_DB (0<<4)
-#define SMA1303_INPUT_R_M6_DB (1<<4)
-#define SMA1303_INPUT_R_M12_DB (2<<4)
-#define SMA1303_INPUT_R_INFI_DB (3<<4)
-
-/* Modulator : 0x14 */
-#define SMA1303_SPK_HYSFB_MASK (3<<6)
-#define SMA1303_HYSFB_625K (0<<6)
-#define SMA1303_HYSFB_414K (1<<6)
-#define SMA1303_HYSFB_297K (2<<6)
-#define SMA1303_HYSFB_226K (3<<6)
-#define SMA1303_SPK_BDELAY_MASK (63<<0)
-
-/* SDM CONTROL : 0x33 */
-#define SMA1303_SDM_Q_SEL_MASK (1<<2)
-#define SMA1303_QUART_SEL_1_DIV_4 (0<<2)
-#define SMA1303_QUART_SEL_1_DIV_8 (1<<2)
-
-/* OTP_DATA1 : 0x34 */
-#define SMA1303_OTP_LVL_MASK (1<<5)
-#define SMA1303_OTP_LVL_NORMAL (0<<5)
-#define SMA1303_OTP_LVL_LOW (1<<5)
-
-/* PROTECTION : 0x36 */
-#define SMA1303_EDGE_DIS_MASK (1<<7)
-#define SMA1303_EDGE_DIS_ENABLE (0<<7)
-#define SMA1303_EDGE_DIS_DISABLE (1<<7)
-
-#define SMA1303_SPK_OCP_DIS_MASK (1<<3)
-#define SMA1303_SPK_OCP_ENABLE (0<<3)
-#define SMA1303_SPK_OCP_DISABLE (1<<3)
-
-#define SMA1303_OCP_MODE_MASK (1<<2)
-#define SMA1303_AUTO_RECOVER (0<<2)
-#define SMA1303_SHUT_DOWN_PERMANENT (1<<2)
-
-#define SMA1303_OTP_MODE_MASK (3<<0)
-#define SMA1303_OTP_MODE_DISABLE (0<<0)
-#define SMA1303_IG_THR1_SHUT_THR2 (1<<0)
-#define SMA1303_REC_THR1_SHUT_THR2 (2<<0)
-#define SMA1303_SHUT_THR1_SHUT_THR2 (3<<0)
-
-/* TEST2 : 0x3C */
-#define SMA1303_SPK_HSDM_BP_MASK (1<<4)
-#define SMA1303_SPK_HSDM_ENABLE (0<<4)
-#define SMA1303_SPK_HSDM_BYPASS (1<<4)
-
-#define SMA1303_SDM_SYNC_DIS_MASK (1<<5)
-#define SMA1303_SDM_SYNC_NORMAL (0<<5)
-#define SMA1303_SDM_SYNC_DISABLE (1<<5)
-
-/* ATEST2 : 0x3F */
-#define SMA1303_SPK_OUT_FREQ_MASK (1<<2)
-#define SMA1303_SPK_OUT_FREQ_360K (0<<2)
-#define SMA1303_SPK_OUT_FREQ_410K (1<<2)
-
-#define SMA1303_LOW_POWER_MODE_MASK (1<<3)
-#define SMA1303_LOW_POWER_MODE_DISABLE (0<<3)
-#define SMA1303_LOW_POWER_MODE_ENABLE (1<<3)
-
-#define SMA1303_THERMAL_ADJUST_MASK (3<<5)
-#define SMA1303_THERMAL_150_110 (0<<5)
-#define SMA1303_THERMAL_160_120 (1<<5)
-#define SMA1303_THERMAL_140_100 (2<<5)
-
-#define SMA1303_FAST_OFF_DRIVE_SPK_MASK (1<<0)
-#define SMA1303_FAST_OFF_DRIVE_SPK_DISABLE (0<<0)
-#define SMA1303_FAST_OFF_DRIVE_SPK_ENABLE (1<<0)
-
-/* PLL_CTRL : 0x8E */
-#define SMA1303_TRM_LVL_MASK (1<<4)
-#define SMA1303_TRM_LVL_NORMAL (0<<4)
-#define SMA1303_TRM_LVL_LOW (1<<4)
-
-#define SMA1303_LOW_OCL_MODE_MASK (1<<3)
-#define SMA1303_LOW_OCL_MODE (0<<3)
-#define SMA1303_NORMAL_OCL_MODE (1<<3)
-
-#define SMA1303_PLL_PD2_MASK (7<<0)
-#define SMA1303_PLL_PD2 (7<<0)
-#define SMA1303_PLL_OPERATION2 (0<<0)
-
-/* POSTSCALER : 0x90 */
-#define SMA1303_BYP_POST_MASK (1<<0)
-#define SMA1303_EN_POST_SCALER (0<<0)
-#define SMA1303_BYP_POST_SCALER (1<<0)
-
-/* FDPEC CONTROL : 0x92 */
-#define SMA1303_FLT_VDD_GAIN_MASK (15<<4)
-#define SMA1303_FLT_VDD_GAIN_2P40 (0<<4)
-#define SMA1303_FLT_VDD_GAIN_2P45 (1<<4)
-#define SMA1303_FLT_VDD_GAIN_2P50 (2<<4)
-#define SMA1303_FLT_VDD_GAIN_2P55 (3<<4)
-#define SMA1303_FLT_VDD_GAIN_2P60 (4<<4)
-#define SMA1303_FLT_VDD_GAIN_2P65 (5<<4)
-#define SMA1303_FLT_VDD_GAIN_2P70 (6<<4)
-#define SMA1303_FLT_VDD_GAIN_2P75 (7<<4)
-#define SMA1303_FLT_VDD_GAIN_2P80 (8<<4)
-#define SMA1303_FLT_VDD_GAIN_2P85 (9<<4)
-#define SMA1303_FLT_VDD_GAIN_2P90 (10<<4)
-#define SMA1303_FLT_VDD_GAIN_2P95 (11<<4)
-#define SMA1303_FLT_VDD_GAIN_3P00 (12<<4)
-#define SMA1303_FLT_VDD_GAIN_3P05 (13<<4)
-#define SMA1303_FLT_VDD_GAIN_3P10 (14<<4)
-#define SMA1303_FLT_VDD_GAIN_3P15 (15<<4)
-
-#define SMA1303_DIS_FCHG_MASK (1<<2)
-#define SMA1303_EN_FAST_CHARGE (0<<2)
-#define SMA1303_DIS_FAST_CHARGE (1<<2)
-
-/* BOOST_CONTROL4 : 0x97 */
-#define SMA1303_TRM_VBST_MASK (7<<2)
-#define SMA1303_TRM_VBST_5P5 (0<<2)
-#define SMA1303_TRM_VBST_5P6 (1<<2)
-#define SMA1303_TRM_VBST_5P7 (2<<2)
-#define SMA1303_TRM_VBST_5P8 (3<<2)
-#define SMA1303_TRM_VBST_5P9 (4<<2)
-#define SMA1303_TRM_VBST_6P0 (5<<2)
-#define SMA1303_TRM_VBST_6P1 (6<<2)
-#define SMA1303_TRM_VBST_6P2 (7<<2)
-
-/* TOP_MAN1 : 0xA2 */
-#define SMA1303_PLL_LOCK_SKIP_MASK (1<<7)
-#define SMA1303_PLL_LOCK_ENABLE (0<<7)
-#define SMA1303_PLL_LOCK_DISABLE (1<<7)
-
-#define SMA1303_PLL_PD_MASK (1<<6)
-#define SMA1303_PLL_OPERATION (0<<6)
-#define SMA1303_PLL_PD (1<<6)
-
-#define SMA1303_PLL_DIV_MASK (3<<4)
-#define SMA1303_PLL_OUT (0<<4)
-#define SMA1303_PLL_OUT_2 (1<<4)
-#define SMA1303_PLL_OUT_4 (2<<4)
-#define SMA1303_PLL_OUT_8 (3<<4)
-
-#define SMA1303_PLL_REF_CLK_MASK (1<<3)
-#define SMA1303_PLL_REF_CLK1 (0<<3)
-#define SMA1303_PLL_SCK (1<<3)
-
-#define SMA1303_DAC_DN_CONV_MASK (1<<2)
-#define SMA1303_DAC_DN_CONV_DISABLE (0<<2)
-#define SMA1303_DAC_DN_CONV_ENABLE (1<<2)
-
-#define SMA1303_SDO_IO_MASK (1<<1)
-#define SMA1303_HIGH_Z_LRCK_H (0<<1)
-#define SMA1303_HIGH_Z_LRCK_L (1<<1)
-
-#define SMA1303_SDO_OUTPUT2_MASK (1<<0)
-#define SMA1303_SDO_NORMAL (0<<0)
-#define SMA1303_SDO_OUTPUT_ONLY (1<<0)
-
-/* TOP_MAN2 : 0xA3 */
-#define SMA1303_MON_OSC_PLL_MASK (1<<7)
-#define SMA1303_PLL_SDO (0<<7)
-#define SMA1303_OSC_SDO (1<<7)
-
-#define SMA1303_TEST_CLKO_EN_MASK (1<<6)
-#define SMA1303_NORMAL_SDO (0<<6)
-#define SMA1303_CLK_OUT_SDO (1<<6)
-
-#define SMA1303_SDO_OUTPUT_MASK (1<<3)
-#define SMA1303_NORMAL_OUT (0<<3)
-#define SMA1303_HIGH_Z_OUT (1<<3)
-
-#define SMA1303_CLOCK_MON_MASK (1<<1)
-#define SMA1303_CLOCK_MON (0<<1)
-#define SMA1303_CLOCK_NOT_MON (1<<1)
-
-#define SMA1303_OSC_PD_MASK (1<<0)
-#define SMA1303_NORMAL_OPERATION_OSC (0<<0)
-#define SMA1303_POWER_DOWN_OSC (1<<0)
-
-/* TOP_MAN3 0xA4 */
-#define SMA1303_O_FORMAT_MASK (7<<5)
-#define SMA1303_O_FMT_LJ (1<<5)
-#define SMA1303_O_FMT_I2S (2<<5)
-#define SMA1303_O_FMT_TDM (4<<5)
-
-#define SMA1303_SCK_RATE_MASK (1<<3)
-#define SMA1303_SCK_64FS (0<<3)
-#define SMA1303_SCK_32FS (2<<3)
-
-#define SMA1303_LRCK_POL_MASK (1<<0)
-#define SMA1303_L_VALID (0<<0)
-#define SMA1303_R_VALID (1<<0)
-
-/* TDM1 FORMAT : 0xA5 */
-#define SMA1303_TDM_CLK_POL_MASK (1<<7)
-#define SMA1303_TDM_CLK_POL_RISE (0<<7)
-#define SMA1303_TDM_CLK_POL_FALL (1<<7)
-
-#define SMA1303_TDM_TX_MODE_MASK (1<<6)
-#define SMA1303_TDM_TX_MONO (0<<6)
-#define SMA1303_TDM_TX_STEREO (1<<6)
-
-#define SMA1303_TDM_SLOT1_RX_POS_MASK (7<<3)
-#define SMA1303_TDM_SLOT1_RX_POS_0 (0<<3)
-#define SMA1303_TDM_SLOT1_RX_POS_1 (1<<3)
-#define SMA1303_TDM_SLOT1_RX_POS_2 (2<<3)
-#define SMA1303_TDM_SLOT1_RX_POS_3 (3<<3)
-#define SMA1303_TDM_SLOT1_RX_POS_4 (4<<3)
-#define SMA1303_TDM_SLOT1_RX_POS_5 (5<<3)
-#define SMA1303_TDM_SLOT1_RX_POS_6 (6<<3)
-#define SMA1303_TDM_SLOT1_RX_POS_7 (7<<3)
-
-#define SMA1303_TDM_SLOT2_RX_POS_MASK (7<<0)
-#define SMA1303_TDM_SLOT2_RX_POS_0 (0<<0)
-#define SMA1303_TDM_SLOT2_RX_POS_1 (1<<0)
-#define SMA1303_TDM_SLOT2_RX_POS_2 (2<<0)
-#define SMA1303_TDM_SLOT2_RX_POS_3 (3<<0)
-#define SMA1303_TDM_SLOT2_RX_POS_4 (4<<0)
-#define SMA1303_TDM_SLOT2_RX_POS_5 (5<<0)
-#define SMA1303_TDM_SLOT2_RX_POS_6 (6<<0)
-#define SMA1303_TDM_SLOT2_RX_POS_7 (7<<0)
-
-/* TDM2 FORMAT : 0xA6 */
-#define SMA1303_TDM_DL_MASK (1<<7)
-#define SMA1303_TDM_DL_16 (0<<7)
-#define SMA1303_TDM_DL_32 (1<<7)
-
-#define SMA1303_TDM_N_SLOT_MASK (1<<6)
-#define SMA1303_TDM_N_SLOT_4 (0<<6)
-#define SMA1303_TDM_N_SLOT_8 (1<<6)
-
-#define SMA1303_TDM_SLOT1_TX_POS_MASK (7<<3)
-#define SMA1303_TDM_SLOT1_TX_POS_0 (0<<3)
-#define SMA1303_TDM_SLOT1_TX_POS_1 (1<<3)
-#define SMA1303_TDM_SLOT1_TX_POS_2 (2<<3)
-#define SMA1303_TDM_SLOT1_TX_POS_3 (3<<3)
-#define SMA1303_TDM_SLOT1_TX_POS_4 (4<<3)
-#define SMA1303_TDM_SLOT1_TX_POS_5 (5<<3)
-#define SMA1303_TDM_SLOT1_TX_POS_6 (6<<3)
-#define SMA1303_TDM_SLOT1_TX_POS_7 (7<<3)
-
-#define SMA1303_TDM_SLOT2_TX_POS_MASK (7<<0)
-#define SMA1303_TDM_SLOT2_TX_POS_0 (0<<0)
-#define SMA1303_TDM_SLOT2_TX_POS_1 (1<<0)
-#define SMA1303_TDM_SLOT2_TX_POS_2 (2<<0)
-#define SMA1303_TDM_SLOT2_TX_POS_3 (3<<0)
-#define SMA1303_TDM_SLOT2_TX_POS_4 (4<<0)
-#define SMA1303_TDM_SLOT2_TX_POS_5 (5<<0)
-#define SMA1303_TDM_SLOT2_TX_POS_6 (6<<0)
-#define SMA1303_TDM_SLOT2_TX_POS_7 (7<<0)
-
-/* STATUS1 : 0xFA */
-#define SMA1303_OT1_OK_STATUS (1<<7)
-#define SMA1303_OT2_OK_STATUS (1<<6)
-
-/* STATUS2 : 0xFB */
-#define SMA1303_OCP_SPK_STATUS (1<<5)
-#define SMA1303_OCP_BST_STATUS (1<<4)
-#define SMA1303_OTP_STAT_OK_0 (5<<1)
-#define SMA1303_OTP_STAT_OK_1 (2<<2)
-
-#define SMA1303_CLK_MON_STATUS (1<<0)
-
-/* DEVICE_INFO : 0xFF */
-#define SMA1303_DEVICE_ID (2<<3)
-#define SMA1303_UVLO_BST_STATUS (1<<2)
-#define SMA1303_REV_NUM_STATUS (3<<0)
-#define SMA1303_REV_NUM_TV0 (0<<0)
-#define SMA1303_REV_NUM_TV1 (1<<0)
-
-#endif
--
2.34.1
3
9