mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00: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:
parent
849c83fe49
commit
9fa6a693ad
15 changed files with 779 additions and 2874 deletions
37
include/sound/tas2781-comlib-i2c.h
Normal file
37
include/sound/tas2781-comlib-i2c.h
Normal file
|
@ -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__ */
|
|
@ -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;
|
||||
|
|
|
@ -27,12 +27,10 @@
|
|||
|
||||
#include <sound/hda_codec.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tas2781-dsp.h>
|
||||
#include <sound/tas2781.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/tas2781-tlv.h>
|
||||
|
||||
#include "tas2781-spi.h"
|
||||
|
||||
#include "hda_local.h"
|
||||
#include "hda_auto_parser.h"
|
||||
#include "hda_component.h"
|
||||
|
@ -40,6 +38,17 @@
|
|||
#include "hda_generic.h"
|
||||
#include "tas2781_hda.h"
|
||||
|
||||
#define TASDEVICE_RANGE_MAX_SIZE (256 * 128)
|
||||
#define TASDEVICE_WIN_LEN 128
|
||||
#define TAS2781_SPI_MAX_FREQ (4 * HZ_PER_MHZ)
|
||||
/* Flag of calibration registers address. */
|
||||
#define TASDEVICE_CALIBRATION_REG_ADDRESS BIT(7)
|
||||
#define TASDEV_UEFI_CALI_REG_ADDR_FLG BIT(7)
|
||||
|
||||
/* System Reset Check Register */
|
||||
#define TAS2781_REG_CLK_CONFIG TASDEVICE_REG(0x0, 0x0, 0x5c)
|
||||
#define TAS2781_REG_CLK_CONFIG_RESET 0x19
|
||||
|
||||
struct tas2781_hda {
|
||||
struct tasdevice_priv *priv;
|
||||
struct acpi_device *dacpi;
|
||||
|
@ -52,12 +61,12 @@ struct tas2781_hda {
|
|||
static const struct regmap_range_cfg tasdevice_ranges[] = {
|
||||
{
|
||||
.range_min = 0,
|
||||
.range_max = TASDEVICE_MAX_SIZE,
|
||||
.range_max = TASDEVICE_RANGE_MAX_SIZE,
|
||||
.selector_reg = TASDEVICE_PAGE_SELECT,
|
||||
.selector_mask = GENMASK(7, 0),
|
||||
.selector_shift = 0,
|
||||
.window_start = 0,
|
||||
.window_len = TASDEVICE_MAX_PAGE,
|
||||
.window_len = TASDEVICE_WIN_LEN,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -65,39 +74,19 @@ static const struct regmap_config tasdevice_regmap = {
|
|||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.zero_flag_mask = true,
|
||||
.read_flag_mask = 0x01,
|
||||
.reg_shift = -1,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
.ranges = tasdevice_ranges,
|
||||
.num_ranges = ARRAY_SIZE(tasdevice_ranges),
|
||||
.max_register = TASDEVICE_MAX_SIZE,
|
||||
.max_register = TASDEVICE_RANGE_MAX_SIZE,
|
||||
};
|
||||
|
||||
static int tasdevice_spi_switch_book(struct tasdevice_priv *tas_priv, int reg)
|
||||
static int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, unsigned int reg, unsigned int *val)
|
||||
{
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
|
||||
if (tas_priv->cur_book != TASDEVICE_BOOK_ID(reg)) {
|
||||
int ret = regmap_write(map, TASDEVICE_BOOKCTL_REG,
|
||||
TASDEVICE_BOOK_ID(reg));
|
||||
if (ret < 0) {
|
||||
dev_err(tas_priv->dev, "Switch Book E=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
tas_priv->cur_book = TASDEVICE_BOOK_ID(reg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv,
|
||||
unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
int ret;
|
||||
|
||||
ret = tasdevice_spi_switch_book(tas_priv, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* In our TAS2781 SPI mode, if read from other book (not book 0),
|
||||
* or read from page number larger than 1 in book 0, one more byte
|
||||
|
@ -106,11 +95,11 @@ int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv,
|
|||
if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) {
|
||||
unsigned char data[2];
|
||||
|
||||
ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1,
|
||||
ret = tasdevice_dev_bulk_read(tas_priv, chn, reg,
|
||||
data, sizeof(data));
|
||||
*val = data[1];
|
||||
} else {
|
||||
ret = regmap_read(map, TASDEVICE_PAGE_REG(reg) | 1, val);
|
||||
ret = tasdevice_dev_read(tas_priv, chn, reg, val);
|
||||
}
|
||||
if (ret < 0)
|
||||
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
|
||||
|
@ -118,71 +107,25 @@ int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int tasdevice_spi_dev_write(struct tasdevice_priv *tas_priv,
|
||||
unsigned int reg,
|
||||
unsigned int value)
|
||||
static int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, unsigned int reg, unsigned char *data,
|
||||
unsigned int len)
|
||||
{
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
int ret;
|
||||
|
||||
ret = tasdevice_spi_switch_book(tas_priv, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(map, TASDEVICE_PAGE_REG(reg), value);
|
||||
if (ret < 0)
|
||||
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tasdevice_spi_dev_bulk_write(struct tasdevice_priv *tas_priv,
|
||||
unsigned int reg,
|
||||
unsigned char *data,
|
||||
unsigned int len)
|
||||
{
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
int ret;
|
||||
|
||||
ret = tasdevice_spi_switch_book(tas_priv, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_write(map, TASDEVICE_PAGE_REG(reg), data, len);
|
||||
if (ret < 0)
|
||||
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv,
|
||||
unsigned int reg,
|
||||
unsigned char *data,
|
||||
unsigned int len)
|
||||
{
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
int ret;
|
||||
|
||||
ret = tasdevice_spi_switch_book(tas_priv, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (len > TASDEVICE_MAX_PAGE)
|
||||
len = TASDEVICE_MAX_PAGE;
|
||||
/*
|
||||
* In our TAS2781 SPI mode, if read from other book (not book 0),
|
||||
* or read from page number larger than 1 in book 0, one more byte
|
||||
* read is needed, and first byte is a dummy byte, need to be ignored.
|
||||
*/
|
||||
if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) {
|
||||
unsigned char buf[TASDEVICE_MAX_PAGE+1];
|
||||
unsigned char buf[TASDEVICE_WIN_LEN + 1];
|
||||
|
||||
ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1, buf,
|
||||
len + 1);
|
||||
ret = tasdevice_dev_bulk_read(tas_priv, chn, reg,
|
||||
buf, len + 1);
|
||||
memcpy(data, buf + 1, len);
|
||||
} else {
|
||||
ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1, data,
|
||||
len);
|
||||
ret = tasdevice_dev_bulk_read(tas_priv, chn, reg, data, len);
|
||||
}
|
||||
if (ret < 0)
|
||||
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
|
||||
|
@ -190,31 +133,55 @@ int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tas_priv,
|
||||
unsigned int reg,
|
||||
unsigned int mask,
|
||||
unsigned int value)
|
||||
static int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tas_priv,
|
||||
unsigned short chn, unsigned int reg, unsigned int mask,
|
||||
unsigned int value)
|
||||
{
|
||||
struct regmap *map = tas_priv->regmap;
|
||||
int ret, val;
|
||||
|
||||
/*
|
||||
* In our TAS2781 SPI mode, read/write was masked in last bit of
|
||||
* address, it cause regmap_update_bits() not work as expected.
|
||||
*/
|
||||
ret = tasdevice_spi_dev_read(tas_priv, reg, &val);
|
||||
ret = tasdevice_dev_read(tas_priv, chn, reg, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = regmap_write(map, TASDEVICE_PAGE_REG(reg),
|
||||
(val & ~mask) | (mask & value));
|
||||
|
||||
ret = tasdevice_dev_write(tas_priv, chn, TASDEVICE_PAGE_REG(reg),
|
||||
(val & ~mask) | (mask & value));
|
||||
if (ret < 0)
|
||||
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tasdevice_spi_change_chn_book(struct tasdevice_priv *p,
|
||||
unsigned short chn, int book)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (chn == p->index) {
|
||||
struct tasdevice *tasdev = &p->tasdevice[chn];
|
||||
struct regmap *map = p->regmap;
|
||||
|
||||
if (tasdev->cur_book != book) {
|
||||
ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book);
|
||||
if (ret < 0)
|
||||
dev_err(p->dev, "%s, E=%d\n", __func__, ret);
|
||||
else
|
||||
tasdev->cur_book = book;
|
||||
}
|
||||
} else {
|
||||
ret = -EXDEV;
|
||||
dev_dbg(p->dev, "Not error, %s ignore channel(%d)\n",
|
||||
__func__, chn);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tas2781_spi_reset(struct tasdevice_priv *tas_dev)
|
||||
{
|
||||
int ret;
|
||||
|
@ -223,12 +190,13 @@ static void tas2781_spi_reset(struct tasdevice_priv *tas_dev)
|
|||
gpiod_set_value_cansleep(tas_dev->reset, 0);
|
||||
fsleep(800);
|
||||
gpiod_set_value_cansleep(tas_dev->reset, 1);
|
||||
} else {
|
||||
ret = tasdevice_dev_write(tas_dev, tas_dev->index,
|
||||
TASDEVICE_REG_SWRESET, TASDEVICE_REG_SWRESET_RESET);
|
||||
if (ret < 0)
|
||||
dev_err(tas_dev->dev, "dev sw-reset fail, %d\n", ret);
|
||||
fsleep(1000);
|
||||
}
|
||||
ret = tasdevice_spi_dev_write(tas_dev, TAS2781_REG_SWRESET,
|
||||
TAS2781_REG_SWRESET_RESET);
|
||||
if (ret < 0)
|
||||
dev_err(tas_dev->dev, "dev sw-reset fail, %d\n", ret);
|
||||
fsleep(1000);
|
||||
}
|
||||
|
||||
static int tascodec_spi_init(struct tasdevice_priv *tas_priv,
|
||||
|
@ -245,7 +213,7 @@ static int tascodec_spi_init(struct tasdevice_priv *tas_priv,
|
|||
|
||||
scnprintf(tas_priv->rca_binaryname,
|
||||
sizeof(tas_priv->rca_binaryname), "%sRCA%d.bin",
|
||||
tas_priv->dev_name, tas_priv->index);
|
||||
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,
|
||||
|
@ -260,26 +228,22 @@ static int tascodec_spi_init(struct tasdevice_priv *tas_priv,
|
|||
|
||||
static void tasdevice_spi_init(struct tasdevice_priv *tas_priv)
|
||||
{
|
||||
tas_priv->cur_prog = -1;
|
||||
tas_priv->cur_conf = -1;
|
||||
tas_priv->tasdevice[tas_priv->index].cur_book = -1;
|
||||
tas_priv->tasdevice[tas_priv->index].cur_conf = -1;
|
||||
tas_priv->tasdevice[tas_priv->index].cur_prog = -1;
|
||||
|
||||
tas_priv->cur_book = -1;
|
||||
tas_priv->cur_prog = -1;
|
||||
tas_priv->cur_conf = -1;
|
||||
tas_priv->isspi = true;
|
||||
|
||||
/* Store default registers address for calibration data. */
|
||||
tas_priv->cali_reg_array[0] = TASDEVICE_REG(0, 0x17, 0x74);
|
||||
tas_priv->cali_reg_array[1] = TASDEVICE_REG(0, 0x18, 0x0c);
|
||||
tas_priv->cali_reg_array[2] = TASDEVICE_REG(0, 0x18, 0x14);
|
||||
tas_priv->cali_reg_array[3] = TASDEVICE_REG(0, 0x13, 0x70);
|
||||
tas_priv->cali_reg_array[4] = TASDEVICE_REG(0, 0x18, 0x7c);
|
||||
tas_priv->update_bits = tasdevice_spi_dev_update_bits;
|
||||
tas_priv->change_chn_book = tasdevice_spi_change_chn_book;
|
||||
tas_priv->dev_read = tasdevice_spi_dev_read;
|
||||
tas_priv->dev_bulk_read = tasdevice_spi_dev_bulk_read;
|
||||
|
||||
mutex_init(&tas_priv->codec_lock);
|
||||
}
|
||||
|
||||
static int tasdevice_spi_amp_putvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol,
|
||||
struct soc_mixer_control *mc)
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
|
||||
{
|
||||
unsigned int invert = mc->invert;
|
||||
unsigned char mask;
|
||||
|
@ -290,7 +254,8 @@ static int tasdevice_spi_amp_putvol(struct tasdevice_priv *tas_priv,
|
|||
mask <<= mc->shift;
|
||||
val = clamp(invert ? max - ucontrol->value.integer.value[0] :
|
||||
ucontrol->value.integer.value[0], 0, max);
|
||||
ret = tasdevice_spi_dev_update_bits(tas_priv,
|
||||
|
||||
ret = tasdevice_spi_dev_update_bits(tas_priv, tas_priv->index,
|
||||
mc->reg, mask, (unsigned int)(val << mc->shift));
|
||||
if (ret)
|
||||
dev_err(tas_priv->dev, "set AMP vol error in dev %d\n",
|
||||
|
@ -300,16 +265,14 @@ static int tasdevice_spi_amp_putvol(struct tasdevice_priv *tas_priv,
|
|||
}
|
||||
|
||||
static int tasdevice_spi_amp_getvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol,
|
||||
struct soc_mixer_control *mc)
|
||||
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, val;
|
||||
|
||||
/* Read the primary device */
|
||||
ret = tasdevice_spi_dev_read(tas_priv, mc->reg, &val);
|
||||
ret = tasdevice_spi_dev_read(tas_priv, tas_priv->index, mc->reg, &val);
|
||||
if (ret) {
|
||||
dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__);
|
||||
return ret;
|
||||
|
@ -324,9 +287,8 @@ static int tasdevice_spi_amp_getvol(struct tasdevice_priv *tas_priv,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int tasdevice_spi_digital_putvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol,
|
||||
struct soc_mixer_control *mc)
|
||||
static int tasdevice_spi_digital_putvol(struct tasdevice_priv *p,
|
||||
struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
|
||||
{
|
||||
unsigned int invert = mc->invert;
|
||||
int max = mc->max;
|
||||
|
@ -334,26 +296,23 @@ static int tasdevice_spi_digital_putvol(struct tasdevice_priv *tas_priv,
|
|||
|
||||
val = clamp(invert ? max - ucontrol->value.integer.value[0] :
|
||||
ucontrol->value.integer.value[0], 0, max);
|
||||
ret = tasdevice_spi_dev_write(tas_priv, mc->reg, (unsigned int)val);
|
||||
ret = tasdevice_dev_write(p, p->index, mc->reg, (unsigned int)val);
|
||||
if (ret)
|
||||
dev_err(tas_priv->dev, "set digital vol err in dev %d\n",
|
||||
tas_priv->index);
|
||||
dev_err(p->dev, "set digital vol err in dev %d\n", p->index);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tasdevice_spi_digital_getvol(struct tasdevice_priv *tas_priv,
|
||||
struct snd_ctl_elem_value *ucontrol,
|
||||
struct soc_mixer_control *mc)
|
||||
static int tasdevice_spi_digital_getvol(struct tasdevice_priv *p,
|
||||
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_spi_dev_read(tas_priv, mc->reg, &val);
|
||||
ret = tasdevice_spi_dev_read(p, p->index, mc->reg, &val);
|
||||
if (ret) {
|
||||
dev_err(tas_priv->dev, "%s, get digital vol err\n", __func__);
|
||||
dev_err(p->dev, "%s, get digital vol err\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -392,7 +351,7 @@ static int tas2781_read_acpi(struct tas2781_hda *tas_hda,
|
|||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
nval = ret;
|
||||
p->ndev = nval = ret;
|
||||
|
||||
ret = device_property_read_u32_array(physdev, property, values, nval);
|
||||
if (ret)
|
||||
|
@ -435,14 +394,17 @@ err:
|
|||
static void tas2781_hda_playback_hook(struct device *dev, int action)
|
||||
{
|
||||
struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
|
||||
struct tasdevice_priv *tas_priv = tas_hda->priv;
|
||||
|
||||
if (action == HDA_GEN_PCM_ACT_OPEN) {
|
||||
pm_runtime_get_sync(dev);
|
||||
guard(mutex)(&tas_hda->priv->codec_lock);
|
||||
tasdevice_spi_tuning_switch(tas_hda->priv, 0);
|
||||
guard(mutex)(&tas_priv->codec_lock);
|
||||
if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK)
|
||||
tasdevice_tuning_switch(tas_hda->priv, 0);
|
||||
} else if (action == HDA_GEN_PCM_ACT_CLOSE) {
|
||||
guard(mutex)(&tas_hda->priv->codec_lock);
|
||||
tasdevice_spi_tuning_switch(tas_hda->priv, 1);
|
||||
guard(mutex)(&tas_priv->codec_lock);
|
||||
if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK)
|
||||
tasdevice_tuning_switch(tas_priv, 1);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
@ -589,6 +551,7 @@ static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol,
|
|||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
|
||||
guard(mutex)(&tas_priv->codec_lock);
|
||||
return tasdevice_spi_digital_getvol(tas_priv, ucontrol, mc);
|
||||
}
|
||||
|
||||
|
@ -599,6 +562,7 @@ static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
|
|||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
|
||||
guard(mutex)(&tas_priv->codec_lock);
|
||||
return tasdevice_spi_amp_getvol(tas_priv, ucontrol, mc);
|
||||
}
|
||||
|
||||
|
@ -609,7 +573,7 @@ static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol,
|
|||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
|
||||
/* The check of the given value is in tasdevice_digital_putvol. */
|
||||
guard(mutex)(&tas_priv->codec_lock);
|
||||
return tasdevice_spi_digital_putvol(tas_priv, ucontrol, mc);
|
||||
}
|
||||
|
||||
|
@ -620,7 +584,7 @@ static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
|
|||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
|
||||
/* The check of the given value is in tasdevice_amp_putvol. */
|
||||
guard(mutex)(&tas_priv->codec_lock);
|
||||
return tasdevice_spi_amp_putvol(tas_priv, ucontrol, mc);
|
||||
}
|
||||
|
||||
|
@ -725,31 +689,117 @@ static const struct snd_kcontrol_new tas2781_dsp_conf_ctrl[] = {
|
|||
|
||||
static void tas2781_apply_calib(struct tasdevice_priv *tas_priv)
|
||||
{
|
||||
int i, rc;
|
||||
struct calidata *cali_data = &tas_priv->cali_data;
|
||||
struct cali_reg *r = &cali_data->cali_reg_array;
|
||||
unsigned char *data = cali_data->data;
|
||||
unsigned int *tmp_val = (unsigned int *)data;
|
||||
unsigned int cali_reg[TASDEV_CALIB_N] = {
|
||||
TASDEVICE_REG(0, 0x17, 0x74),
|
||||
TASDEVICE_REG(0, 0x18, 0x0c),
|
||||
TASDEVICE_REG(0, 0x18, 0x14),
|
||||
TASDEVICE_REG(0, 0x13, 0x70),
|
||||
TASDEVICE_REG(0, 0x18, 0x7c),
|
||||
};
|
||||
unsigned int crc, oft;
|
||||
unsigned char *buf;
|
||||
int i, j, k, l;
|
||||
|
||||
/*
|
||||
* If no calibration data exist in tasdevice_priv *tas_priv,
|
||||
* calibration apply will be ignored, and use default values
|
||||
* in firmware binary, which was loaded during firmware download.
|
||||
*/
|
||||
if (tas_priv->cali_data[0] == 0)
|
||||
return;
|
||||
/*
|
||||
* Calibration data was saved in tasdevice_priv *tas_priv as:
|
||||
* unsigned int cali_data[CALIB_MAX];
|
||||
* and every data (in 4 bytes) will be saved in register which in
|
||||
* book 0, and page number in page_array[], offset was saved in
|
||||
* rgno_array[].
|
||||
*/
|
||||
for (i = 0; i < CALIB_MAX; i++) {
|
||||
rc = tasdevice_spi_dev_bulk_write(tas_priv,
|
||||
tas_priv->cali_reg_array[i],
|
||||
(unsigned char *)&tas_priv->cali_data[i], 4);
|
||||
if (rc < 0)
|
||||
dev_err(tas_priv->dev,
|
||||
"chn %d calib %d bulk_wr err = %d\n",
|
||||
tas_priv->index, i, rc);
|
||||
if (tmp_val[0] == 2781) {
|
||||
/*
|
||||
* New features were added in calibrated Data V3:
|
||||
* 1. Added calibration registers address define in
|
||||
* a node, marked as Device id == 0x80.
|
||||
* New features were added in calibrated Data V2:
|
||||
* 1. Added some the fields to store the link_id and
|
||||
* uniqie_id for multi-link solutions
|
||||
* 2. Support flexible number of devices instead of
|
||||
* fixed one in V1.
|
||||
* Layout of calibrated data V2 in UEFI(total 256 bytes):
|
||||
* ChipID (2781, 4 bytes)
|
||||
* Data-Group-Sum (4 bytes)
|
||||
* TimeStamp of Calibration (4 bytes)
|
||||
* for (i = 0; i < Data-Group-Sum; i++) {
|
||||
* if (Data type != 0x80) (4 bytes)
|
||||
* Calibrated Data of Device #i (20 bytes)
|
||||
* else
|
||||
* Calibration registers address (5*4 = 20 bytes)
|
||||
* # V2: No reg addr in data grp section.
|
||||
* # V3: Normally the last grp is the reg addr.
|
||||
* }
|
||||
* CRC (4 bytes)
|
||||
* Reserved (the rest)
|
||||
*/
|
||||
crc = crc32(~0, data, (3 + tmp_val[1] * 6) * 4) ^ ~0;
|
||||
|
||||
if (crc != tmp_val[3 + tmp_val[1] * 6]) {
|
||||
cali_data->total_sz = 0;
|
||||
dev_err(tas_priv->dev, "%s: CRC error\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
for (j = 0, k = 0; j < tmp_val[1]; j++) {
|
||||
oft = j * 6 + 3;
|
||||
if (tmp_val[oft] == TASDEV_UEFI_CALI_REG_ADDR_FLG) {
|
||||
for (i = 0; i < TASDEV_CALIB_N; i++) {
|
||||
buf = &data[(oft + i + 1) * 4];
|
||||
cali_reg[i] = TASDEVICE_REG(buf[1],
|
||||
buf[2], buf[3]);
|
||||
}
|
||||
} else {
|
||||
l = j * (cali_data->cali_dat_sz_per_dev + 1);
|
||||
if (k >= tas_priv->ndev || l > oft * 4) {
|
||||
dev_err(tas_priv->dev,
|
||||
"%s: dev sum error\n",
|
||||
__func__);
|
||||
cali_data->total_sz = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
data[l] = k;
|
||||
for (i = 0; i < TASDEV_CALIB_N * 4; i++)
|
||||
data[l + i] = data[4 * oft + i];
|
||||
k++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Calibration data is in V1 format.
|
||||
* struct cali_data {
|
||||
* char cali_data[20];
|
||||
* }
|
||||
*
|
||||
* struct {
|
||||
* struct cali_data cali_data[4];
|
||||
* int TimeStamp of Calibration (4 bytes)
|
||||
* int CRC (4 bytes)
|
||||
* } ueft;
|
||||
*/
|
||||
crc = crc32(~0, data, 84) ^ ~0;
|
||||
if (crc != tmp_val[21]) {
|
||||
cali_data->total_sz = 0;
|
||||
dev_err(tas_priv->dev, "%s: V1 CRC error\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
for (j = tas_priv->ndev - 1; j >= 0; j--) {
|
||||
l = j * (cali_data->cali_dat_sz_per_dev + 1);
|
||||
for (i = TASDEV_CALIB_N * 4; i > 0 ; i--)
|
||||
data[l + i] = data[tas_priv->index * 5 + i];
|
||||
data[l+i] = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (tas_priv->dspbin_typ == TASDEV_BASIC) {
|
||||
r->r0_reg = cali_reg[0];
|
||||
r->invr0_reg = cali_reg[1];
|
||||
r->r0_low_reg = cali_reg[2];
|
||||
r->pow_reg = cali_reg[3];
|
||||
r->tlimit_reg = cali_reg[4];
|
||||
}
|
||||
|
||||
tas_priv->is_user_space_calidata = true;
|
||||
cali_data->total_sz =
|
||||
tas_priv->ndev * (cali_data->cali_dat_sz_per_dev + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -769,93 +819,34 @@ static int tas2781_save_calibration(struct tasdevice_priv *tas_priv)
|
|||
EFI_GUID(0x02f9af02, 0x7734, 0x4233,
|
||||
0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3);
|
||||
static efi_char16_t efi_name[] = TASDEVICE_CALIBRATION_DATA_NAME;
|
||||
unsigned char data[TASDEVICE_CALIBRATION_DATA_SIZE], *buf;
|
||||
unsigned int attr, crc, offset, *tmp_val;
|
||||
struct calidata *cali_data = &tas_priv->cali_data;
|
||||
unsigned long total_sz = 0;
|
||||
unsigned int attr, size;
|
||||
efi_status_t status;
|
||||
unsigned char *data;
|
||||
|
||||
tas_priv->cali_data[0] = 0;
|
||||
status = efi.get_variable(efi_name, &efi_guid, &attr, &total_sz, data);
|
||||
cali_data->cali_dat_sz_per_dev = 20;
|
||||
size = tas_priv->ndev * (cali_data->cali_dat_sz_per_dev + 1);
|
||||
status = efi.get_variable(efi_name, &efi_guid, &attr, &total_sz, NULL);
|
||||
cali_data->total_sz = total_sz > size ? total_sz : size;
|
||||
if (status == EFI_BUFFER_TOO_SMALL) {
|
||||
if (total_sz > TASDEVICE_CALIBRATION_DATA_SIZE)
|
||||
/* Allocate data buffer of data_size bytes */
|
||||
data = tas_priv->cali_data.data = devm_kzalloc(tas_priv->dev,
|
||||
tas_priv->cali_data.total_sz, GFP_KERNEL);
|
||||
if (!data) {
|
||||
tas_priv->cali_data.total_sz = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Get variable contents into buffer */
|
||||
status = efi.get_variable(efi_name, &efi_guid, &attr,
|
||||
&total_sz, data);
|
||||
&tas_priv->cali_data.total_sz, data);
|
||||
}
|
||||
if (status != EFI_SUCCESS)
|
||||
if (status != EFI_SUCCESS) {
|
||||
tas_priv->cali_data.total_sz = 0;
|
||||
return status;
|
||||
|
||||
tmp_val = (unsigned int *)data;
|
||||
if (tmp_val[0] == 2781) {
|
||||
/*
|
||||
* New features were added in calibrated Data V3:
|
||||
* 1. Added calibration registers address define in
|
||||
* a node, marked as Device id == 0x80.
|
||||
* New features were added in calibrated Data V2:
|
||||
* 1. Added some the fields to store the link_id and
|
||||
* uniqie_id for multi-link solutions
|
||||
* 2. Support flexible number of devices instead of
|
||||
* fixed one in V1.
|
||||
* Layout of calibrated data V2 in UEFI(total 256 bytes):
|
||||
* ChipID (2781, 4 bytes)
|
||||
* Device-Sum (4 bytes)
|
||||
* TimeStamp of Calibration (4 bytes)
|
||||
* for (i = 0; i < Device-Sum; i++) {
|
||||
* Device #i index_info () {
|
||||
* SDW link id (2bytes)
|
||||
* SDW unique_id (2bytes)
|
||||
* } // if Device number is 0x80, mean it's
|
||||
* calibration registers address.
|
||||
* Calibrated Data of Device #i (20 bytes)
|
||||
* }
|
||||
* CRC (4 bytes)
|
||||
* Reserved (the rest)
|
||||
*/
|
||||
crc = crc32(~0, data, (3 + tmp_val[1] * 6) * 4) ^ ~0;
|
||||
|
||||
if (crc != tmp_val[3 + tmp_val[1] * 6])
|
||||
return 0;
|
||||
|
||||
for (int j = 0; j < tmp_val[1]; j++) {
|
||||
offset = j * 6 + 3;
|
||||
if (tmp_val[offset] == tas_priv->index) {
|
||||
for (int i = 0; i < CALIB_MAX; i++)
|
||||
tas_priv->cali_data[i] =
|
||||
tmp_val[offset + i + 1];
|
||||
} else if (tmp_val[offset] ==
|
||||
TASDEVICE_CALIBRATION_REG_ADDRESS) {
|
||||
for (int i = 0; i < CALIB_MAX; i++) {
|
||||
buf = &data[(offset + i + 1) * 4];
|
||||
tas_priv->cali_reg_array[i] =
|
||||
TASDEVICE_REG(buf[1], buf[2],
|
||||
buf[3]);
|
||||
}
|
||||
}
|
||||
tas_priv->apply_calibration(tas_priv);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Calibration data is in V1 format.
|
||||
* struct cali_data {
|
||||
* char cali_data[20];
|
||||
* }
|
||||
*
|
||||
* struct {
|
||||
* struct cali_data cali_data[4];
|
||||
* int TimeStamp of Calibration (4 bytes)
|
||||
* int CRC (4 bytes)
|
||||
* } ueft;
|
||||
*/
|
||||
crc = crc32(~0, data, 84) ^ ~0;
|
||||
if (crc == tmp_val[21]) {
|
||||
for (int i = 0; i < CALIB_MAX; i++)
|
||||
tas_priv->cali_data[i] =
|
||||
tmp_val[tas_priv->index * 5 + i];
|
||||
tas_priv->apply_calibration(tas_priv);
|
||||
}
|
||||
}
|
||||
|
||||
tas_priv->apply_calibration(tas_priv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -883,7 +874,7 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context)
|
|||
pm_runtime_get_sync(tas_priv->dev);
|
||||
guard(mutex)(&tas_priv->codec_lock);
|
||||
|
||||
ret = tasdevice_spi_rca_parser(tas_priv, fmw);
|
||||
ret = tasdevice_rca_parser(tas_priv, fmw);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@ -910,12 +901,12 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context)
|
|||
}
|
||||
}
|
||||
|
||||
tasdevice_spi_dsp_remove(tas_priv);
|
||||
tasdevice_dsp_remove(tas_priv);
|
||||
|
||||
tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
|
||||
scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%08X-%01d.bin",
|
||||
codec->core.subsystem_id, tas_priv->index);
|
||||
ret = tasdevice_spi_dsp_parser(tas_priv);
|
||||
ret = tasdevice_dsp_parser(tas_priv);
|
||||
if (ret) {
|
||||
dev_err(tas_priv->dev, "dspfw load %s error\n",
|
||||
tas_priv->coef_binaryname);
|
||||
|
@ -945,26 +936,27 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* Perform AMP reset before firmware download. */
|
||||
tas_priv->rcabin.profile_cfg_id = TAS2781_PRE_POST_RESET_CFG;
|
||||
tas2781_spi_reset(tas_priv);
|
||||
tas_priv->rcabin.profile_cfg_id = 0;
|
||||
|
||||
tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
|
||||
ret = tasdevice_spi_dev_read(tas_priv, TAS2781_REG_CLK_CONFIG, &val);
|
||||
ret = tas_priv->dev_read(tas_priv, tas_priv->index,
|
||||
TAS2781_REG_CLK_CONFIG, &val);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (val == TAS2781_REG_CLK_CONFIG_RESET)
|
||||
ret = tasdevice_spi_prmg_load(tas_priv, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(tas_priv->dev, "FW download failed = %d\n", ret);
|
||||
goto out;
|
||||
if (val == TAS2781_REG_CLK_CONFIG_RESET) {
|
||||
ret = tasdevice_prmg_load(tas_priv, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(tas_priv->dev, "FW download failed = %d\n",
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (tas_priv->fmw->nr_programs > 0)
|
||||
tas_priv->cur_prog = 0;
|
||||
tas_priv->tasdevice[tas_priv->index].cur_prog = 0;
|
||||
if (tas_priv->fmw->nr_configurations > 0)
|
||||
tas_priv->cur_conf = 0;
|
||||
tas_priv->tasdevice[tas_priv->index].cur_conf = 0;
|
||||
|
||||
/*
|
||||
* If calibrated data occurs error, dsp will still works with default
|
||||
|
@ -1017,9 +1009,10 @@ static void tas2781_hda_unbind(struct device *dev, struct device *master,
|
|||
{
|
||||
struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
|
||||
struct hda_component_parent *parent = master_data;
|
||||
struct tasdevice_priv *tas_priv = tas_hda->priv;
|
||||
struct hda_component *comp;
|
||||
|
||||
comp = hda_component_from_index(parent, tas_hda->priv->index);
|
||||
comp = hda_component_from_index(parent, tas_priv->index);
|
||||
if (comp && (comp->dev == dev)) {
|
||||
comp->dev = NULL;
|
||||
memset(comp->name, 0, sizeof(comp->name));
|
||||
|
@ -1028,8 +1021,8 @@ static void tas2781_hda_unbind(struct device *dev, struct device *master,
|
|||
|
||||
tas2781_hda_remove_controls(tas_hda);
|
||||
|
||||
tasdevice_spi_config_info_remove(tas_hda->priv);
|
||||
tasdevice_spi_dsp_remove(tas_hda->priv);
|
||||
tasdevice_config_info_remove(tas_priv);
|
||||
tasdevice_dsp_remove(tas_priv);
|
||||
|
||||
tas_hda->priv->fw_state = TASDEVICE_DSP_FW_PENDING;
|
||||
}
|
||||
|
@ -1124,14 +1117,16 @@ static void tas2781_hda_spi_remove(struct spi_device *spi)
|
|||
static int tas2781_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
|
||||
struct tasdevice_priv *tas_priv = tas_hda->priv;
|
||||
|
||||
guard(mutex)(&tas_hda->priv->codec_lock);
|
||||
guard(mutex)(&tas_priv->codec_lock);
|
||||
|
||||
if (tas_hda->priv->playback_started)
|
||||
tasdevice_spi_tuning_switch(tas_hda->priv, 1);
|
||||
if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK
|
||||
&& tas_priv->playback_started)
|
||||
tasdevice_tuning_switch(tas_priv, 1);
|
||||
|
||||
tas_hda->priv->cur_book = -1;
|
||||
tas_hda->priv->cur_conf = -1;
|
||||
tas_priv->tasdevice[tas_priv->index].cur_book = -1;
|
||||
tas_priv->tasdevice[tas_priv->index].cur_conf = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1139,11 +1134,13 @@ static int tas2781_runtime_suspend(struct device *dev)
|
|||
static int tas2781_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
|
||||
struct tasdevice_priv *tas_priv = tas_hda->priv;
|
||||
|
||||
guard(mutex)(&tas_hda->priv->codec_lock);
|
||||
guard(mutex)(&tas_priv->codec_lock);
|
||||
|
||||
if (tas_hda->priv->playback_started)
|
||||
tasdevice_spi_tuning_switch(tas_hda->priv, 0);
|
||||
if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK
|
||||
&& tas_priv->playback_started)
|
||||
tasdevice_tuning_switch(tas_priv, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1151,6 +1148,7 @@ static int tas2781_runtime_resume(struct device *dev)
|
|||
static int tas2781_system_suspend(struct device *dev)
|
||||
{
|
||||
struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
|
||||
struct tasdevice_priv *tas_priv = tas_hda->priv;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_force_suspend(dev);
|
||||
|
@ -1158,8 +1156,9 @@ static int tas2781_system_suspend(struct device *dev)
|
|||
return ret;
|
||||
|
||||
/* Shutdown chip before system suspend */
|
||||
if (tas_hda->priv->playback_started)
|
||||
tasdevice_spi_tuning_switch(tas_hda->priv, 1);
|
||||
if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK
|
||||
&& tas_priv->playback_started)
|
||||
tasdevice_tuning_switch(tas_priv, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1167,32 +1166,34 @@ static int tas2781_system_suspend(struct device *dev)
|
|||
static int tas2781_system_resume(struct device *dev)
|
||||
{
|
||||
struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
|
||||
struct tasdevice_priv *tas_priv = tas_hda->priv;
|
||||
int ret, val;
|
||||
|
||||
ret = pm_runtime_force_resume(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
guard(mutex)(&tas_hda->priv->codec_lock);
|
||||
ret = tasdevice_spi_dev_read(tas_hda->priv, TAS2781_REG_CLK_CONFIG,
|
||||
&val);
|
||||
guard(mutex)(&tas_priv->codec_lock);
|
||||
ret = tas_priv->dev_read(tas_priv, tas_priv->index,
|
||||
TAS2781_REG_CLK_CONFIG, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (val == TAS2781_REG_CLK_CONFIG_RESET) {
|
||||
tas_hda->priv->cur_book = -1;
|
||||
tas_hda->priv->cur_conf = -1;
|
||||
tas_hda->priv->cur_prog = -1;
|
||||
tas_priv->tasdevice[tas_priv->index].cur_book = -1;
|
||||
tas_priv->tasdevice[tas_priv->index].cur_conf = -1;
|
||||
tas_priv->tasdevice[tas_priv->index].cur_prog = -1;
|
||||
|
||||
ret = tasdevice_spi_prmg_load(tas_hda->priv, 0);
|
||||
ret = tasdevice_prmg_load(tas_priv, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(tas_hda->priv->dev,
|
||||
dev_err(tas_priv->dev,
|
||||
"FW download failed = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
|
||||
|
||||
if (tas_hda->priv->playback_started)
|
||||
tasdevice_spi_tuning_switch(tas_hda->priv, 0);
|
||||
if (tas_priv->playback_started)
|
||||
tasdevice_tuning_switch(tas_priv, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -1229,3 +1230,4 @@ module_spi_driver(tas2781_hda_spi_driver);
|
|||
MODULE_DESCRIPTION("TAS2781 HDA SPI Driver");
|
||||
MODULE_AUTHOR("Baojun, Xu, <baojun.xug@ti.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS("SND_SOC_TAS2781_FMWLIB");
|
||||
|
|
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
|
||||
|
|
375
sound/soc/codecs/tas2781-comlib-i2c.c
Normal file
375
sound/soc/codecs/tas2781-comlib-i2c.c
Normal file
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue