mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
ALSA: hda/tas2781: Remove tas2781_spi_fwlib.c and leverage SND_SOC_TAS2781_FMWLIB
Most codes in tas2781_spi_fwlib.c are same as tas2781-fmwlib.c, mainly for firmware parsing, only differece is the register reading, bit update and book switching in i2c and spi. The main purpose of this patch is for code cleaup and arrange the shared part for i2c and spi. Signed-off-by: Shenghao Ding <shenghao-ding@ti.com> Acked-by: Mark Brown <broonie@kernel.org> Link: https://patch.msgid.link/20250429111055.567-1-shenghao-ding@ti.com Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
committed by
Takashi Iwai
parent
849c83fe49
commit
9fa6a693ad
@@ -0,0 +1,37 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
//
|
||||
// ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier
|
||||
//
|
||||
// Copyright (C) 2025 Texas Instruments Incorporated
|
||||
// https://www.ti.com
|
||||
//
|
||||
// The TAS2563/TAS2781 driver implements a flexible and configurable
|
||||
// algo coefficient setting for one, two, or even multiple
|
||||
// TAS2563/TAS2781 chips.
|
||||
//
|
||||
// Author: Shenghao Ding <shenghao-ding@ti.com>
|
||||
//
|
||||
|
||||
#ifndef __TAS2781_COMLIB_I2C_H__
|
||||
#define __TAS2781_COMLIB_I2C_H__
|
||||
|
||||
void tasdevice_reset(struct tasdevice_priv *tas_dev);
|
||||
int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
|
||||
struct module *module,
|
||||
void (*cont)(const struct firmware *fw, void *context));
|
||||
struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c);
|
||||
int tasdevice_init(struct tasdevice_priv *tas_priv);
|
||||
int tasdev_chn_switch(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn);
|
||||
int tasdevice_dev_update_bits(
|
||||
struct tasdevice_priv *tasdevice, unsigned short chn,
|
||||
unsigned int reg, unsigned int mask, unsigned int value);
|
||||
int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
|
||||
int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
|
||||
int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
|
||||
int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
|
||||
#endif /* __TAS2781_COMLIB_I2C_H__ */
|
||||
+19
-26
@@ -32,6 +32,8 @@
|
||||
SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
#define TASDEVICE_CRC8_POLYNOMIAL 0x4d
|
||||
|
||||
/* PAGE Control Register (available in page0 of each book) */
|
||||
#define TASDEVICE_PAGE_SELECT 0x00
|
||||
#define TASDEVICE_BOOKCTL_PAGE 0x00
|
||||
@@ -193,6 +195,7 @@ struct tasdevice_priv {
|
||||
bool force_fwload_status;
|
||||
bool playback_started;
|
||||
bool isacpi;
|
||||
bool isspi;
|
||||
bool is_user_space_calidata;
|
||||
unsigned int global_addr;
|
||||
|
||||
@@ -210,41 +213,31 @@ struct tasdevice_priv {
|
||||
int (*tasdevice_load_block)(struct tasdevice_priv *tas_priv,
|
||||
struct tasdev_blk *block);
|
||||
|
||||
int (*change_chn_book)(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, int book);
|
||||
int (*update_bits)(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, unsigned int reg, unsigned int mask,
|
||||
unsigned int value);
|
||||
int (*dev_read)(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, unsigned int reg, unsigned int *value);
|
||||
int (*dev_bulk_read)(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, unsigned int reg, unsigned char *p_data,
|
||||
unsigned int n_length);
|
||||
int (*save_calibration)(struct tasdevice_priv *tas_priv);
|
||||
void (*apply_calibration)(struct tasdevice_priv *tas_priv);
|
||||
};
|
||||
|
||||
void tasdevice_reset(struct tasdevice_priv *tas_dev);
|
||||
int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
|
||||
struct module *module,
|
||||
void (*cont)(const struct firmware *fw, void *context));
|
||||
struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c);
|
||||
int tasdevice_init(struct tasdevice_priv *tas_priv);
|
||||
void tasdevice_remove(struct tasdevice_priv *tas_priv);
|
||||
int tasdevice_save_calibration(struct tasdevice_priv *tas_priv);
|
||||
void tasdevice_apply_calibration(struct tasdevice_priv *tas_priv);
|
||||
int tasdev_chn_switch(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn);
|
||||
int tasdevice_dev_read(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, unsigned int reg, unsigned int *value);
|
||||
int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, unsigned int reg, unsigned char *p_data,
|
||||
unsigned int n_length);
|
||||
int tasdevice_dev_write(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, unsigned int reg, unsigned int value);
|
||||
int tasdevice_dev_bulk_write(
|
||||
struct tasdevice_priv *tas_priv, unsigned short chn,
|
||||
unsigned int reg, unsigned char *p_data, unsigned int n_length);
|
||||
int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, unsigned int reg, unsigned char *p_data,
|
||||
unsigned int n_length);
|
||||
int tasdevice_dev_update_bits(
|
||||
struct tasdevice_priv *tasdevice, unsigned short chn,
|
||||
unsigned int reg, unsigned int mask, unsigned int value);
|
||||
int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
|
||||
int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
|
||||
int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
|
||||
int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
|
||||
|
||||
void tasdevice_remove(struct tasdevice_priv *tas_priv);
|
||||
int tasdevice_save_calibration(struct tasdevice_priv *tas_priv);
|
||||
void tasdevice_apply_calibration(struct tasdevice_priv *tas_priv);
|
||||
#endif /* __TAS2781_H__ */
|
||||
|
||||
@@ -186,7 +186,7 @@ config SND_HDA_SCODEC_TAS2781_I2C
|
||||
depends on ACPI
|
||||
depends on EFI
|
||||
depends on SND_SOC
|
||||
select SND_SOC_TAS2781_COMLIB
|
||||
select SND_SOC_TAS2781_COMLIB_I2C
|
||||
select SND_SOC_TAS2781_FMWLIB
|
||||
select CRC32
|
||||
help
|
||||
@@ -202,6 +202,8 @@ config SND_HDA_SCODEC_TAS2781_SPI
|
||||
depends on ACPI
|
||||
depends on EFI
|
||||
depends on SND_SOC
|
||||
select SND_SOC_TAS2781_FMWLIB
|
||||
select CRC8
|
||||
select CRC32
|
||||
help
|
||||
Say Y or M here to include TAS2781 SPI HD-audio side codec support
|
||||
|
||||
@@ -39,7 +39,7 @@ snd-hda-scodec-cs35l56-i2c-y := cs35l56_hda_i2c.o
|
||||
snd-hda-scodec-cs35l56-spi-y := cs35l56_hda_spi.o
|
||||
snd-hda-scodec-component-y := hda_component.o
|
||||
snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o
|
||||
snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o tas2781_spi_fwlib.o
|
||||
snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o
|
||||
|
||||
# common driver
|
||||
obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
//
|
||||
// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
|
||||
//
|
||||
// Copyright (C) 2024 Texas Instruments Incorporated
|
||||
// https://www.ti.com
|
||||
//
|
||||
// The TAS2781 driver implements a flexible and configurable
|
||||
// algo coefficient setting for TAS2781 chips.
|
||||
//
|
||||
// Author: Baojun Xu <baojun.xu@ti.com>
|
||||
//
|
||||
|
||||
#ifndef __TAS2781_SPI_H__
|
||||
#define __TAS2781_SPI_H__
|
||||
|
||||
#define TASDEVICE_RATES \
|
||||
(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
|
||||
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200)
|
||||
|
||||
#define TASDEVICE_FORMATS \
|
||||
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
#define TASDEVICE_MAX_BOOK_NUM 256
|
||||
#define TASDEVICE_MAX_PAGE 256
|
||||
|
||||
#define TASDEVICE_MAX_SIZE (TASDEVICE_MAX_BOOK_NUM * TASDEVICE_MAX_PAGE)
|
||||
|
||||
/* PAGE Control Register (available in page0 of each book) */
|
||||
#define TASDEVICE_PAGE_SELECT 0x00
|
||||
#define TASDEVICE_BOOKCTL_PAGE 0x00
|
||||
#define TASDEVICE_BOOKCTL_REG GENMASK(7, 1)
|
||||
#define TASDEVICE_BOOK_ID(reg) (((reg) & GENMASK(24, 16)) >> 16)
|
||||
#define TASDEVICE_PAGE_ID(reg) (((reg) & GENMASK(15, 8)) >> 8)
|
||||
#define TASDEVICE_REG_ID(reg) (((reg) & GENMASK(7, 1)) >> 1)
|
||||
#define TASDEVICE_PAGE_REG(reg) ((reg) & GENMASK(15, 1))
|
||||
#define TASDEVICE_REG(book, page, reg) \
|
||||
(((book) << 16) | ((page) << 8) | ((reg) << 1))
|
||||
|
||||
/* Software Reset */
|
||||
#define TAS2781_REG_SWRESET TASDEVICE_REG(0x0, 0x0, 0x01)
|
||||
#define TAS2781_REG_SWRESET_RESET BIT(0)
|
||||
|
||||
/* System Reset Check Register */
|
||||
#define TAS2781_REG_CLK_CONFIG TASDEVICE_REG(0x0, 0x0, 0x5c)
|
||||
#define TAS2781_REG_CLK_CONFIG_RESET (0x19)
|
||||
#define TAS2781_PRE_POST_RESET_CFG 3
|
||||
|
||||
/* Block Checksum */
|
||||
#define TASDEVICE_CHECKSUM 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
|
||||
|
||||
#define TAS2781_SPI_MAX_FREQ (4 * HZ_PER_MHZ)
|
||||
|
||||
#define TASDEVICE_CRC8_POLYNOMIAL 0x4d
|
||||
#define TASDEVICE_SPEAKER_CALIBRATION_SIZE 20
|
||||
|
||||
/* Flag of calibration registers address. */
|
||||
#define TASDEVICE_CALIBRATION_REG_ADDRESS BIT(7)
|
||||
|
||||
#define TASDEVICE_CALIBRATION_DATA_NAME L"CALI_DATA"
|
||||
#define TASDEVICE_CALIBRATION_DATA_SIZE 256
|
||||
|
||||
enum calib_data {
|
||||
R0_VAL = 0,
|
||||
INV_R0,
|
||||
R0LOW,
|
||||
POWER,
|
||||
TLIM,
|
||||
CALIB_MAX
|
||||
};
|
||||
|
||||
struct tasdevice_priv {
|
||||
struct tasdevice_fw *cali_data_fmw;
|
||||
struct tasdevice_rca rcabin;
|
||||
struct tasdevice_fw *fmw;
|
||||
struct gpio_desc *reset;
|
||||
struct mutex codec_lock;
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
|
||||
unsigned char crc8_lkp_tbl[CRC8_TABLE_SIZE];
|
||||
unsigned char coef_binaryname[64];
|
||||
unsigned char rca_binaryname[64];
|
||||
unsigned char dev_name[32];
|
||||
|
||||
bool force_fwload_status;
|
||||
bool playback_started;
|
||||
bool is_loading;
|
||||
bool is_loaderr;
|
||||
unsigned int cali_reg_array[CALIB_MAX];
|
||||
unsigned int cali_data[CALIB_MAX];
|
||||
unsigned int err_code;
|
||||
void *codec;
|
||||
int cur_book;
|
||||
int cur_prog;
|
||||
int cur_conf;
|
||||
int fw_state;
|
||||
int index;
|
||||
int irq;
|
||||
|
||||
int (*fw_parse_variable_header)(struct tasdevice_priv *tas_priv,
|
||||
const struct firmware *fmw,
|
||||
int offset);
|
||||
int (*fw_parse_program_data)(struct tasdevice_priv *tas_priv,
|
||||
struct tasdevice_fw *tas_fmw,
|
||||
const struct firmware *fmw, int offset);
|
||||
int (*fw_parse_configuration_data)(struct tasdevice_priv *tas_priv,
|
||||
struct tasdevice_fw *tas_fmw,
|
||||
const struct firmware *fmw,
|
||||
int offset);
|
||||
int (*tasdevice_load_block)(struct tasdevice_priv *tas_priv,
|
||||
struct tasdev_blk *block);
|
||||
|
||||
int (*save_calibration)(struct tasdevice_priv *tas_priv);
|
||||
void (*apply_calibration)(struct tasdevice_priv *tas_priv);
|
||||
};
|
||||
|
||||
int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv,
|
||||
unsigned int reg, unsigned int *value);
|
||||
int tasdevice_spi_dev_write(struct tasdevice_priv *tas_priv,
|
||||
unsigned int reg, unsigned int value);
|
||||
int tasdevice_spi_dev_bulk_write(struct tasdevice_priv *tas_priv,
|
||||
unsigned int reg, unsigned char *p_data,
|
||||
unsigned int n_length);
|
||||
int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv,
|
||||
unsigned int reg, unsigned char *p_data,
|
||||
unsigned int n_length);
|
||||
int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tasdevice,
|
||||
unsigned int reg, unsigned int mask,
|
||||
unsigned int value);
|
||||
|
||||
void tasdevice_spi_select_cfg_blk(void *context, int conf_no,
|
||||
unsigned char block_type);
|
||||
void tasdevice_spi_config_info_remove(void *context);
|
||||
int tasdevice_spi_dsp_parser(void *context);
|
||||
int tasdevice_spi_rca_parser(void *context, const struct firmware *fmw);
|
||||
void tasdevice_spi_dsp_remove(void *context);
|
||||
void tasdevice_spi_calbin_remove(void *context);
|
||||
int tasdevice_spi_select_tuningprm_cfg(void *context, int prm, int cfg_no,
|
||||
int rca_conf_no);
|
||||
int tasdevice_spi_prmg_load(void *context, int prm_no);
|
||||
int tasdevice_spi_prmg_calibdata_load(void *context, int prm_no);
|
||||
void tasdevice_spi_tuning_switch(void *context, int state);
|
||||
int tas2781_spi_load_calibration(void *context, char *file_name,
|
||||
unsigned short i);
|
||||
#endif /* __TAS2781_SPI_H__ */
|
||||
@@ -9,6 +9,9 @@
|
||||
|
||||
#include <sound/asound.h>
|
||||
|
||||
#define TASDEVICE_CALIBRATION_DATA_NAME L"CALI_DATA"
|
||||
#define TASDEV_CALIB_N 5
|
||||
|
||||
/*
|
||||
* No standard control callbacks for SNDRV_CTL_ELEM_IFACE_CARD
|
||||
* Define two controls, one is Volume control callbacks, the other is
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <sound/hda_codec.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tas2781.h>
|
||||
#include <sound/tas2781-comlib-i2c.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/tas2781-tlv.h>
|
||||
|
||||
@@ -34,15 +35,6 @@
|
||||
|
||||
#define TASDEVICE_SPEAKER_CALIBRATION_SIZE 20
|
||||
|
||||
enum calib_data {
|
||||
R0_VAL = 0,
|
||||
INV_R0,
|
||||
R0LOW,
|
||||
POWER,
|
||||
TLIM,
|
||||
CALIB_MAX
|
||||
};
|
||||
|
||||
#define TAS2563_MAX_CHANNELS 4
|
||||
|
||||
#define TAS2563_CAL_POWER TASDEVICE_REG(0, 0x0d, 0x3c)
|
||||
@@ -50,17 +42,15 @@ enum calib_data {
|
||||
#define TAS2563_CAL_INVR0 TASDEVICE_REG(0, 0x0f, 0x40)
|
||||
#define TAS2563_CAL_R0_LOW TASDEVICE_REG(0, 0x0f, 0x48)
|
||||
#define TAS2563_CAL_TLIM TASDEVICE_REG(0, 0x10, 0x14)
|
||||
#define TAS2563_CAL_N 5
|
||||
#define TAS2563_CAL_DATA_SIZE 4
|
||||
#define TAS2563_CAL_CH_SIZE 20
|
||||
#define TAS2563_CAL_ARRAY_SIZE 80
|
||||
|
||||
static unsigned int cal_regs[TAS2563_CAL_N] = {
|
||||
static unsigned int cal_regs[TASDEV_CALIB_N] = {
|
||||
TAS2563_CAL_POWER, TAS2563_CAL_R0, TAS2563_CAL_INVR0,
|
||||
TAS2563_CAL_R0_LOW, TAS2563_CAL_TLIM,
|
||||
};
|
||||
|
||||
|
||||
struct tas2781_hda {
|
||||
struct device *dev;
|
||||
struct tasdevice_priv *priv;
|
||||
@@ -475,7 +465,7 @@ static void tas2563_apply_calib(struct tasdevice_priv *tas_priv)
|
||||
int ret;
|
||||
|
||||
for (int i = 0; i < tas_priv->ndev; i++) {
|
||||
for (int j = 0; j < TAS2563_CAL_N; ++j) {
|
||||
for (int j = 0; j < TASDEV_CALIB_N; ++j) {
|
||||
data = cpu_to_be32(
|
||||
*(uint32_t *)&tas_priv->cali_data.data[offset]);
|
||||
ret = tasdevice_dev_bulk_write(tas_priv, i, cal_regs[j],
|
||||
@@ -493,7 +483,7 @@ static int tas2563_save_calibration(struct tasdevice_priv *tas_priv)
|
||||
static efi_guid_t efi_guid = EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc,
|
||||
0x09, 0x43, 0xa3, 0xf4, 0x31, 0x0a, 0x92);
|
||||
|
||||
static efi_char16_t *efi_vars[TAS2563_MAX_CHANNELS][TAS2563_CAL_N] = {
|
||||
static efi_char16_t *efi_vars[TAS2563_MAX_CHANNELS][TASDEV_CALIB_N] = {
|
||||
{ L"Power_1", L"R0_1", L"InvR0_1", L"R0_Low_1", L"TLim_1" },
|
||||
{ L"Power_2", L"R0_2", L"InvR0_2", L"R0_Low_2", L"TLim_2" },
|
||||
{ L"Power_3", L"R0_3", L"InvR0_3", L"R0_Low_3", L"TLim_3" },
|
||||
@@ -511,7 +501,7 @@ static int tas2563_save_calibration(struct tasdevice_priv *tas_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
for (int i = 0; i < tas_priv->ndev; ++i) {
|
||||
for (int j = 0; j < TAS2563_CAL_N; ++j) {
|
||||
for (int j = 0; j < TASDEV_CALIB_N; ++j) {
|
||||
status = efi.get_variable(efi_vars[i][j],
|
||||
&efi_guid, &attr, &max_size,
|
||||
&tas_priv->cali_data.data[offset]);
|
||||
@@ -535,7 +525,7 @@ static void tas2781_apply_calib(struct tasdevice_priv *tas_priv)
|
||||
{
|
||||
struct calidata *cali_data = &tas_priv->cali_data;
|
||||
struct cali_reg *r = &cali_data->cali_reg_array;
|
||||
unsigned int cali_reg[CALIB_MAX] = {
|
||||
unsigned int cali_reg[TASDEV_CALIB_N] = {
|
||||
TASDEVICE_REG(0, 0x17, 0x74),
|
||||
TASDEVICE_REG(0, 0x18, 0x0c),
|
||||
TASDEVICE_REG(0, 0x18, 0x14),
|
||||
@@ -555,7 +545,7 @@ static void tas2781_apply_calib(struct tasdevice_priv *tas_priv)
|
||||
}
|
||||
|
||||
for (i = 0; i < tas_priv->ndev; i++) {
|
||||
for (j = 0; j < CALIB_MAX; j++) {
|
||||
for (j = 0; j < TASDEV_CALIB_N; j++) {
|
||||
data = cpu_to_be32(
|
||||
*(uint32_t *)&tas_priv->cali_data.data[oft]);
|
||||
rc = tasdevice_dev_bulk_write(tas_priv, i,
|
||||
@@ -578,7 +568,7 @@ static int tas2781_save_calibration(struct tasdevice_priv *tas_priv)
|
||||
{
|
||||
efi_guid_t efi_guid = EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d,
|
||||
0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3);
|
||||
static efi_char16_t efi_name[] = L"CALI_DATA";
|
||||
static efi_char16_t efi_name[] = TASDEVICE_CALIBRATION_DATA_NAME;
|
||||
unsigned int attr, crc;
|
||||
unsigned int *tmp_val;
|
||||
efi_status_t status;
|
||||
|
||||
+273
-271
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -260,6 +260,7 @@ config SND_SOC_ALL_CODECS
|
||||
imply SND_SOC_TAS2770
|
||||
imply SND_SOC_TAS2780
|
||||
imply SND_SOC_TAS2781_COMLIB
|
||||
imply SND_SOC_TAS2781_COMLIB_I2C
|
||||
imply SND_SOC_TAS2781_FMWLIB
|
||||
imply SND_SOC_TAS2781_I2C
|
||||
imply SND_SOC_TAS5086
|
||||
@@ -1991,20 +1992,23 @@ config SND_SOC_TAS2780
|
||||
digital input mono Class-D audio power amplifiers.
|
||||
|
||||
config SND_SOC_TAS2781_COMLIB
|
||||
tristate
|
||||
|
||||
config SND_SOC_TAS2781_COMLIB_I2C
|
||||
depends on I2C
|
||||
select CRC8
|
||||
select REGMAP_I2C
|
||||
tristate
|
||||
|
||||
config SND_SOC_TAS2781_FMWLIB
|
||||
depends on SND_SOC_TAS2781_COMLIB
|
||||
select SND_SOC_TAS2781_COMLIB
|
||||
tristate
|
||||
default n
|
||||
|
||||
config SND_SOC_TAS2781_I2C
|
||||
tristate "Texas Instruments TAS2781 speaker amplifier based on I2C"
|
||||
depends on I2C
|
||||
select SND_SOC_TAS2781_COMLIB
|
||||
select SND_SOC_TAS2781_COMLIB_I2C
|
||||
select SND_SOC_TAS2781_FMWLIB
|
||||
help
|
||||
Enable support for Texas Instruments TAS2781 Smart Amplifier
|
||||
|
||||
@@ -305,6 +305,7 @@ snd-soc-tas6424-y := tas6424.o
|
||||
snd-soc-tda7419-y := tda7419.o
|
||||
snd-soc-tas2770-y := tas2770.o
|
||||
snd-soc-tas2781-comlib-y := tas2781-comlib.o
|
||||
snd-soc-tas2781-comlib-i2c-y := tas2781-comlib-i2c.o
|
||||
snd-soc-tas2781-fmwlib-y := tas2781-fmwlib.o
|
||||
snd-soc-tas2781-i2c-y := tas2781-i2c.o
|
||||
snd-soc-tfa9879-y := tfa9879.o
|
||||
@@ -713,6 +714,7 @@ 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_COMLIB) += snd-soc-tas2781-comlib.o
|
||||
obj-$(CONFIG_SND_SOC_TAS2781_COMLIB_I2C) += snd-soc-tas2781-comlib-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_TAS2781_FMWLIB) += snd-soc-tas2781-fmwlib.o
|
||||
obj-$(CONFIG_SND_SOC_TAS2781_I2C) += snd-soc-tas2781-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers based on I2C
|
||||
//
|
||||
// Copyright 2025 Texas Instruments, Inc.
|
||||
//
|
||||
// Author: Shenghao Ding <shenghao-ding@ti.com>
|
||||
|
||||
#include <linux/crc8.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tas2781.h>
|
||||
#include <sound/tas2781-comlib-i2c.h>
|
||||
|
||||
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_NONE,
|
||||
.ranges = tasdevice_ranges,
|
||||
.num_ranges = ARRAY_SIZE(tasdevice_ranges),
|
||||
.max_register = 256 * 128,
|
||||
};
|
||||
|
||||
static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, int book)
|
||||
{
|
||||
struct i2c_client *client = (struct i2c_client *)tas_priv->client;
|
||||
int ret = 0;
|
||||
|
||||
if (chn < tas_priv->ndev) {
|
||||
struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
|
||||
if (client->addr != tasdev->dev_addr) {
|
||||
client->addr = tasdev->dev_addr;
|
||||
/* All tas2781s share the same regmap, clear the page
|
||||
* inside regmap once switching to another tas2781.
|
||||
* Register 0 at any pages and any books inside tas2781
|
||||
* is the same one for page-switching.
|
||||
*/
|
||||
ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(tas_priv->dev, "%s, E=%d channel:%d\n",
|
||||
__func__, ret, chn);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (tasdev->cur_book != book) {
|
||||
ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book);
|
||||
if (ret < 0) {
|
||||
dev_err(tas_priv->dev, "%s, E=%d\n",
|
||||
__func__, ret);
|
||||
goto out;
|
||||
}
|
||||
tasdev->cur_book = book;
|
||||
}
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
|
||||
chn);
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tasdev_chn_switch(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn)
|
||||
{
|
||||
struct i2c_client *client = (struct i2c_client *)tas_priv->client;
|
||||
struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
int ret;
|
||||
|
||||
if (client->addr != tasdev->dev_addr) {
|
||||
client->addr = tasdev->dev_addr;
|
||||
/* All devices share the same regmap, clear the page
|
||||
* inside regmap once switching to another device.
|
||||
* Register 0 at any pages and any books inside tas2781
|
||||
* is the same one for page-switching.
|
||||
*/
|
||||
ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdev_chn_switch);
|
||||
|
||||
int tasdevice_dev_update_bits(
|
||||
struct tasdevice_priv *tas_priv, unsigned short chn,
|
||||
unsigned int reg, unsigned int mask, unsigned int value)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (chn < tas_priv->ndev) {
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
|
||||
ret = tas_priv->change_chn_book(tas_priv, chn,
|
||||
TASDEVICE_BOOK_ID(reg));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_update_bits(map, TASDEVICE_PGRG(reg),
|
||||
mask, value);
|
||||
if (ret < 0)
|
||||
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
|
||||
} else {
|
||||
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
|
||||
chn);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_dev_update_bits);
|
||||
|
||||
struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c)
|
||||
{
|
||||
struct tasdevice_priv *tas_priv;
|
||||
|
||||
tas_priv = devm_kzalloc(&i2c->dev, sizeof(*tas_priv), GFP_KERNEL);
|
||||
if (!tas_priv)
|
||||
return NULL;
|
||||
tas_priv->dev = &i2c->dev;
|
||||
tas_priv->client = (void *)i2c;
|
||||
|
||||
return tas_priv;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_kzalloc);
|
||||
|
||||
int tasdevice_init(struct tasdevice_priv *tas_priv)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
tas_priv->regmap = devm_regmap_init_i2c(tas_priv->client,
|
||||
&tasdevice_regmap);
|
||||
if (IS_ERR(tas_priv->regmap)) {
|
||||
ret = PTR_ERR(tas_priv->regmap);
|
||||
dev_err(tas_priv->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
tas_priv->cur_prog = -1;
|
||||
tas_priv->cur_conf = -1;
|
||||
tas_priv->isspi = false;
|
||||
|
||||
for (i = 0; i < tas_priv->ndev; i++) {
|
||||
tas_priv->tasdevice[i].cur_book = -1;
|
||||
tas_priv->tasdevice[i].cur_prog = -1;
|
||||
tas_priv->tasdevice[i].cur_conf = -1;
|
||||
}
|
||||
|
||||
tas_priv->update_bits = tasdevice_dev_update_bits;
|
||||
tas_priv->change_chn_book = tasdevice_change_chn_book;
|
||||
tas_priv->dev_read = tasdevice_dev_read;
|
||||
tas_priv->dev_bulk_read = tasdevice_dev_bulk_read;
|
||||
|
||||
mutex_init(&tas_priv->codec_lock);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_init);
|
||||
|
||||
static int tasdevice_clamp(int val, int max, unsigned int invert)
|
||||
{
|
||||
if (val > max)
|
||||
val = max;
|
||||
if (invert)
|
||||
val = max - val;
|
||||
if (val < 0)
|
||||
val = 0;
|
||||
return val;
|
||||
}
|
||||
|
||||
int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
|
||||
{
|
||||
unsigned int invert = mc->invert;
|
||||
unsigned char mask;
|
||||
int max = mc->max;
|
||||
int err_cnt = 0;
|
||||
int val, i, ret;
|
||||
|
||||
mask = (1 << fls(max)) - 1;
|
||||
mask <<= mc->shift;
|
||||
val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
|
||||
for (i = 0; i < tas_priv->ndev; i++) {
|
||||
ret = tasdevice_dev_update_bits(tas_priv, i,
|
||||
mc->reg, mask, (unsigned int)(val << mc->shift));
|
||||
if (!ret)
|
||||
continue;
|
||||
err_cnt++;
|
||||
dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", i);
|
||||
}
|
||||
|
||||
/* All the devices set error, return 0 */
|
||||
return (err_cnt == tas_priv->ndev) ? 0 : 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_amp_putvol);
|
||||
|
||||
int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
|
||||
{
|
||||
unsigned int invert = mc->invert;
|
||||
unsigned char mask = 0;
|
||||
int max = mc->max;
|
||||
int ret = 0;
|
||||
int val;
|
||||
|
||||
/* Read the primary device */
|
||||
ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val);
|
||||
if (ret) {
|
||||
dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
mask = (1 << fls(max)) - 1;
|
||||
mask <<= mc->shift;
|
||||
val = (val & mask) >> mc->shift;
|
||||
val = tasdevice_clamp(val, max, invert);
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_amp_getvol);
|
||||
|
||||
int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
|
||||
{
|
||||
unsigned int invert = mc->invert;
|
||||
int max = mc->max;
|
||||
int ret, val;
|
||||
|
||||
/* Read the primary device as the whole */
|
||||
ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val);
|
||||
if (ret) {
|
||||
dev_err(tas_priv->dev, "%s, get digital vol error\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
val = tasdevice_clamp(val, max, invert);
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_digital_getvol);
|
||||
|
||||
int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
|
||||
{
|
||||
unsigned int invert = mc->invert;
|
||||
int max = mc->max;
|
||||
int err_cnt = 0;
|
||||
int ret;
|
||||
int val, i;
|
||||
|
||||
val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
|
||||
|
||||
for (i = 0; i < tas_priv->ndev; i++) {
|
||||
ret = tasdevice_dev_write(tas_priv, i, mc->reg,
|
||||
(unsigned int)val);
|
||||
if (!ret)
|
||||
continue;
|
||||
err_cnt++;
|
||||
dev_err(tas_priv->dev,
|
||||
"set digital vol err in dev %d\n", i);
|
||||
}
|
||||
|
||||
/* All the devices set error, return 0 */
|
||||
return (err_cnt == tas_priv->ndev) ? 0 : 1;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_digital_putvol);
|
||||
|
||||
void tasdevice_reset(struct tasdevice_priv *tas_dev)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
if (tas_dev->reset) {
|
||||
gpiod_set_value_cansleep(tas_dev->reset, 0);
|
||||
usleep_range(500, 1000);
|
||||
gpiod_set_value_cansleep(tas_dev->reset, 1);
|
||||
} else {
|
||||
for (i = 0; i < tas_dev->ndev; i++) {
|
||||
ret = tasdevice_dev_write(tas_dev, i,
|
||||
TASDEVICE_REG_SWRESET,
|
||||
TASDEVICE_REG_SWRESET_RESET);
|
||||
if (ret < 0)
|
||||
dev_err(tas_dev->dev,
|
||||
"dev %d swreset fail, %d\n",
|
||||
i, ret);
|
||||
}
|
||||
}
|
||||
usleep_range(1000, 1050);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_reset);
|
||||
|
||||
int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
|
||||
struct module *module,
|
||||
void (*cont)(const struct firmware *fw, void *context))
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Codec Lock Hold to ensure that codec_probe and firmware parsing and
|
||||
* loading do not simultaneously execute.
|
||||
*/
|
||||
mutex_lock(&tas_priv->codec_lock);
|
||||
|
||||
if (tas_priv->name_prefix)
|
||||
scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin",
|
||||
tas_priv->name_prefix, tas_priv->dev_name,
|
||||
tas_priv->ndev);
|
||||
else
|
||||
scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin",
|
||||
tas_priv->dev_name, tas_priv->ndev);
|
||||
crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL);
|
||||
tas_priv->codec = codec;
|
||||
ret = request_firmware_nowait(module, FW_ACTION_UEVENT,
|
||||
tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv,
|
||||
cont);
|
||||
if (ret)
|
||||
dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n",
|
||||
ret);
|
||||
|
||||
/* Codec Lock Release*/
|
||||
mutex_unlock(&tas_priv->codec_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tascodec_init);
|
||||
|
||||
void tasdevice_remove(struct tasdevice_priv *tas_priv)
|
||||
{
|
||||
mutex_destroy(&tas_priv->codec_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_remove);
|
||||
|
||||
MODULE_DESCRIPTION("TAS2781 common library for I2C");
|
||||
MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -2,14 +2,14 @@
|
||||
//
|
||||
// TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers
|
||||
//
|
||||
// Copyright 2023 - 2024 Texas Instruments, Inc.
|
||||
// Copyright 2023 - 2025 Texas Instruments, Inc.
|
||||
//
|
||||
// Author: Shenghao Ding <shenghao-ding@ti.com>
|
||||
|
||||
#include <linux/crc8.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
@@ -17,103 +17,8 @@
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tas2781.h>
|
||||
|
||||
#define TASDEVICE_CRC8_POLYNOMIAL 0x4d
|
||||
|
||||
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_NONE,
|
||||
.ranges = tasdevice_ranges,
|
||||
.num_ranges = ARRAY_SIZE(tasdevice_ranges),
|
||||
.max_register = 256 * 128,
|
||||
};
|
||||
|
||||
static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, int book)
|
||||
{
|
||||
struct i2c_client *client = (struct i2c_client *)tas_priv->client;
|
||||
int ret = 0;
|
||||
|
||||
if (chn < tas_priv->ndev) {
|
||||
struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
|
||||
if (client->addr != tasdev->dev_addr) {
|
||||
client->addr = tasdev->dev_addr;
|
||||
/* All tas2781s share the same regmap, clear the page
|
||||
* inside regmap once switching to another tas2781.
|
||||
* Register 0 at any pages and any books inside tas2781
|
||||
* is the same one for page-switching.
|
||||
*/
|
||||
ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(tas_priv->dev, "%s, E=%d channel:%d\n",
|
||||
__func__, ret, chn);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (tasdev->cur_book != book) {
|
||||
ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book);
|
||||
if (ret < 0) {
|
||||
dev_err(tas_priv->dev, "%s, E=%d\n",
|
||||
__func__, ret);
|
||||
goto out;
|
||||
}
|
||||
tasdev->cur_book = book;
|
||||
}
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
|
||||
chn);
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tasdev_chn_switch(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn)
|
||||
{
|
||||
struct i2c_client *client = (struct i2c_client *)tas_priv->client;
|
||||
struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
int ret;
|
||||
|
||||
if (client->addr != tasdev->dev_addr) {
|
||||
client->addr = tasdev->dev_addr;
|
||||
/* All devices share the same regmap, clear the page
|
||||
* inside regmap once switching to another device.
|
||||
* Register 0 at any pages and any books inside tas2781
|
||||
* is the same one for page-switching.
|
||||
*/
|
||||
ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdev_chn_switch);
|
||||
|
||||
int tasdevice_dev_read(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, unsigned int reg, unsigned int *val)
|
||||
{
|
||||
@@ -122,7 +27,7 @@ int tasdevice_dev_read(struct tasdevice_priv *tas_priv,
|
||||
if (chn < tas_priv->ndev) {
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
|
||||
ret = tasdevice_change_chn_book(tas_priv, chn,
|
||||
ret = tas_priv->change_chn_book(tas_priv, chn,
|
||||
TASDEVICE_BOOK_ID(reg));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@@ -141,6 +46,32 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_dev_read);
|
||||
|
||||
int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, unsigned int reg, unsigned char *data,
|
||||
unsigned int len)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (chn < tas_priv->ndev) {
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
|
||||
ret = tas_priv->change_chn_book(tas_priv, chn,
|
||||
TASDEVICE_BOOK_ID(reg));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len);
|
||||
if (ret < 0)
|
||||
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
|
||||
} else
|
||||
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
|
||||
chn);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read);
|
||||
|
||||
int tasdevice_dev_write(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, unsigned int reg, unsigned int value)
|
||||
{
|
||||
@@ -149,7 +80,7 @@ int tasdevice_dev_write(struct tasdevice_priv *tas_priv,
|
||||
if (chn < tas_priv->ndev) {
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
|
||||
ret = tasdevice_change_chn_book(tas_priv, chn,
|
||||
ret = tas_priv->change_chn_book(tas_priv, chn,
|
||||
TASDEVICE_BOOK_ID(reg));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@@ -179,7 +110,7 @@ int tasdevice_dev_bulk_write(
|
||||
if (chn < tas_priv->ndev) {
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
|
||||
ret = tasdevice_change_chn_book(tas_priv, chn,
|
||||
ret = tas_priv->change_chn_book(tas_priv, chn,
|
||||
TASDEVICE_BOOK_ID(reg));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@@ -199,160 +130,20 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_write);
|
||||
|
||||
int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, unsigned int reg, unsigned char *data,
|
||||
unsigned int len)
|
||||
int tasdevice_save_calibration(struct tasdevice_priv *tas_priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (chn < tas_priv->ndev) {
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
|
||||
ret = tasdevice_change_chn_book(tas_priv, chn,
|
||||
TASDEVICE_BOOK_ID(reg));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len);
|
||||
if (ret < 0)
|
||||
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
|
||||
} else
|
||||
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
|
||||
chn);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
if (tas_priv->save_calibration)
|
||||
return tas_priv->save_calibration(tas_priv);
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read);
|
||||
EXPORT_SYMBOL_GPL(tasdevice_save_calibration);
|
||||
|
||||
int tasdevice_dev_update_bits(
|
||||
struct tasdevice_priv *tas_priv, unsigned short chn,
|
||||
unsigned int reg, unsigned int mask, unsigned int value)
|
||||
void tasdevice_apply_calibration(struct tasdevice_priv *tas_priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (chn < tas_priv->ndev) {
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
|
||||
ret = tasdevice_change_chn_book(tas_priv, chn,
|
||||
TASDEVICE_BOOK_ID(reg));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_update_bits(map, TASDEVICE_PGRG(reg),
|
||||
mask, value);
|
||||
if (ret < 0)
|
||||
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
|
||||
} else {
|
||||
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
|
||||
chn);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
if (tas_priv->apply_calibration && tas_priv->cali_data.total_sz)
|
||||
tas_priv->apply_calibration(tas_priv);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_dev_update_bits);
|
||||
|
||||
struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c)
|
||||
{
|
||||
struct tasdevice_priv *tas_priv;
|
||||
|
||||
tas_priv = devm_kzalloc(&i2c->dev, sizeof(*tas_priv), GFP_KERNEL);
|
||||
if (!tas_priv)
|
||||
return NULL;
|
||||
tas_priv->dev = &i2c->dev;
|
||||
tas_priv->client = (void *)i2c;
|
||||
|
||||
return tas_priv;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_kzalloc);
|
||||
|
||||
void tasdevice_reset(struct tasdevice_priv *tas_dev)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
if (tas_dev->reset) {
|
||||
gpiod_set_value_cansleep(tas_dev->reset, 0);
|
||||
usleep_range(500, 1000);
|
||||
gpiod_set_value_cansleep(tas_dev->reset, 1);
|
||||
} else {
|
||||
for (i = 0; i < tas_dev->ndev; i++) {
|
||||
ret = tasdevice_dev_write(tas_dev, i,
|
||||
TASDEVICE_REG_SWRESET,
|
||||
TASDEVICE_REG_SWRESET_RESET);
|
||||
if (ret < 0)
|
||||
dev_err(tas_dev->dev,
|
||||
"dev %d swreset fail, %d\n",
|
||||
i, ret);
|
||||
}
|
||||
}
|
||||
usleep_range(1000, 1050);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_reset);
|
||||
|
||||
int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
|
||||
struct module *module,
|
||||
void (*cont)(const struct firmware *fw, void *context))
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Codec Lock Hold to ensure that codec_probe and firmware parsing and
|
||||
* loading do not simultaneously execute.
|
||||
*/
|
||||
mutex_lock(&tas_priv->codec_lock);
|
||||
|
||||
if (tas_priv->name_prefix)
|
||||
scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin",
|
||||
tas_priv->name_prefix, tas_priv->dev_name,
|
||||
tas_priv->ndev);
|
||||
else
|
||||
scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin",
|
||||
tas_priv->dev_name, tas_priv->ndev);
|
||||
crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL);
|
||||
tas_priv->codec = codec;
|
||||
ret = request_firmware_nowait(module, FW_ACTION_UEVENT,
|
||||
tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv,
|
||||
cont);
|
||||
if (ret)
|
||||
dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n",
|
||||
ret);
|
||||
|
||||
/* Codec Lock Release*/
|
||||
mutex_unlock(&tas_priv->codec_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tascodec_init);
|
||||
|
||||
int tasdevice_init(struct tasdevice_priv *tas_priv)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
tas_priv->regmap = devm_regmap_init_i2c(tas_priv->client,
|
||||
&tasdevice_regmap);
|
||||
if (IS_ERR(tas_priv->regmap)) {
|
||||
ret = PTR_ERR(tas_priv->regmap);
|
||||
dev_err(tas_priv->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
tas_priv->cur_prog = -1;
|
||||
tas_priv->cur_conf = -1;
|
||||
|
||||
for (i = 0; i < tas_priv->ndev; i++) {
|
||||
tas_priv->tasdevice[i].cur_book = -1;
|
||||
tas_priv->tasdevice[i].cur_prog = -1;
|
||||
tas_priv->tasdevice[i].cur_conf = -1;
|
||||
}
|
||||
|
||||
mutex_init(&tas_priv->codec_lock);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_init);
|
||||
EXPORT_SYMBOL_GPL(tasdevice_apply_calibration);
|
||||
|
||||
static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog)
|
||||
{
|
||||
@@ -434,143 +225,6 @@ void tasdevice_dsp_remove(void *context)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_dsp_remove);
|
||||
|
||||
void tasdevice_remove(struct tasdevice_priv *tas_priv)
|
||||
{
|
||||
mutex_destroy(&tas_priv->codec_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_remove);
|
||||
|
||||
int tasdevice_save_calibration(struct tasdevice_priv *tas_priv)
|
||||
{
|
||||
if (tas_priv->save_calibration)
|
||||
return tas_priv->save_calibration(tas_priv);
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_save_calibration);
|
||||
|
||||
void tasdevice_apply_calibration(struct tasdevice_priv *tas_priv)
|
||||
{
|
||||
if (tas_priv->apply_calibration && tas_priv->cali_data.total_sz)
|
||||
tas_priv->apply_calibration(tas_priv);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_apply_calibration);
|
||||
|
||||
static int tasdevice_clamp(int val, int max, unsigned int invert)
|
||||
{
|
||||
if (val > max)
|
||||
val = max;
|
||||
if (invert)
|
||||
val = max - val;
|
||||
if (val < 0)
|
||||
val = 0;
|
||||
return val;
|
||||
}
|
||||
|
||||
int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
|
||||
{
|
||||
unsigned int invert = mc->invert;
|
||||
unsigned char mask;
|
||||
int max = mc->max;
|
||||
int err_cnt = 0;
|
||||
int val, i, ret;
|
||||
|
||||
mask = (1 << fls(max)) - 1;
|
||||
mask <<= mc->shift;
|
||||
val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
|
||||
for (i = 0; i < tas_priv->ndev; i++) {
|
||||
ret = tasdevice_dev_update_bits(tas_priv, i,
|
||||
mc->reg, mask, (unsigned int)(val << mc->shift));
|
||||
if (!ret)
|
||||
continue;
|
||||
err_cnt++;
|
||||
dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", i);
|
||||
}
|
||||
|
||||
/* All the devices set error, return 0 */
|
||||
return (err_cnt == tas_priv->ndev) ? 0 : 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_amp_putvol);
|
||||
|
||||
int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
|
||||
{
|
||||
unsigned int invert = mc->invert;
|
||||
unsigned char mask = 0;
|
||||
int max = mc->max;
|
||||
int ret = 0;
|
||||
int val;
|
||||
|
||||
/* Read the primary device */
|
||||
ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val);
|
||||
if (ret) {
|
||||
dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
mask = (1 << fls(max)) - 1;
|
||||
mask <<= mc->shift;
|
||||
val = (val & mask) >> mc->shift;
|
||||
val = tasdevice_clamp(val, max, invert);
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_amp_getvol);
|
||||
|
||||
int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
|
||||
{
|
||||
unsigned int invert = mc->invert;
|
||||
int max = mc->max;
|
||||
int err_cnt = 0;
|
||||
int ret;
|
||||
int val, i;
|
||||
|
||||
val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
|
||||
|
||||
for (i = 0; i < tas_priv->ndev; i++) {
|
||||
ret = tasdevice_dev_write(tas_priv, i, mc->reg,
|
||||
(unsigned int)val);
|
||||
if (!ret)
|
||||
continue;
|
||||
err_cnt++;
|
||||
dev_err(tas_priv->dev,
|
||||
"set digital vol err in dev %d\n", i);
|
||||
}
|
||||
|
||||
/* All the devices set error, return 0 */
|
||||
return (err_cnt == tas_priv->ndev) ? 0 : 1;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_digital_putvol);
|
||||
|
||||
int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
|
||||
{
|
||||
unsigned int invert = mc->invert;
|
||||
int max = mc->max;
|
||||
int ret, val;
|
||||
|
||||
/* Read the primary device as the whole */
|
||||
ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val);
|
||||
if (ret) {
|
||||
dev_err(tas_priv->dev, "%s, get digital vol error\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
val = tasdevice_clamp(val, max, invert);
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tasdevice_digital_getvol);
|
||||
|
||||
MODULE_DESCRIPTION("TAS2781 common library");
|
||||
MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -803,8 +803,13 @@ static int tasdevice_process_block(void *context, unsigned char *data,
|
||||
chn = idx - 1;
|
||||
chnend = idx;
|
||||
} else {
|
||||
chn = 0;
|
||||
chnend = tas_priv->ndev;
|
||||
if (tas_priv->isspi) {
|
||||
chn = tas_priv->index;
|
||||
chnend = chn + 1;
|
||||
} else {
|
||||
chn = 0;
|
||||
chnend = tas_priv->ndev;
|
||||
}
|
||||
}
|
||||
|
||||
for (; chn < chnend; chn++) {
|
||||
@@ -896,7 +901,7 @@ static int tasdevice_process_block(void *context, unsigned char *data,
|
||||
is_err = true;
|
||||
break;
|
||||
}
|
||||
rc = tasdevice_dev_update_bits(tas_priv, chn,
|
||||
rc = tas_priv->update_bits(tas_priv, chn,
|
||||
TASDEVICE_REG(data[subblk_offset + 2],
|
||||
data[subblk_offset + 3],
|
||||
data[subblk_offset + 4]),
|
||||
@@ -1461,7 +1466,7 @@ static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice,
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = tasdevice_dev_bulk_read(tasdevice, chn,
|
||||
ret = tasdevice->dev_bulk_read(tasdevice, chn,
|
||||
TASDEVICE_REG(book, page, crc_data.offset),
|
||||
nBuf1, crc_data.len);
|
||||
if (ret < 0)
|
||||
@@ -1511,7 +1516,7 @@ static int do_singlereg_checksum(struct tasdevice_priv *tasdevice,
|
||||
in = check_yram(&crc_data, book, page, reg, 1);
|
||||
if (!in)
|
||||
goto end;
|
||||
ret = tasdevice_dev_read(tasdevice, chl,
|
||||
ret = tasdevice->dev_read(tasdevice, chl,
|
||||
TASDEVICE_REG(book, page, reg), &nData1);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
@@ -1615,7 +1620,7 @@ static int tasdev_block_chksum(struct tasdevice_priv *tas_priv,
|
||||
unsigned int nr_value;
|
||||
int ret;
|
||||
|
||||
ret = tasdevice_dev_read(tas_priv, chn, TASDEVICE_CHECKSUM_REG,
|
||||
ret = tas_priv->dev_read(tas_priv, chn, TASDEVICE_CHECKSUM_REG,
|
||||
&nr_value);
|
||||
if (ret < 0) {
|
||||
dev_err(tas_priv->dev, "%s: Chn %d\n", __func__, chn);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tas2781.h>
|
||||
#include <sound/tas2781-comlib-i2c.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/tas2563-tlv.h>
|
||||
#include <sound/tas2781-tlv.h>
|
||||
|
||||
Reference in New Issue
Block a user