audio: add tas5707 codec driver

PD#144051: audio: add codec driver

Change-Id: I2290cf5684e0a22e7dd8aede9aa4f67570d39860
Signed-off-by: Xing Wang <xing.wang@amlogic.com>
This commit is contained in:
Xing Wang
2017-05-10 17:47:17 +08:00
committed by Jianxin Pan
parent 262832b709
commit a33fcadc70
9 changed files with 960 additions and 8 deletions

View File

@@ -0,0 +1,18 @@
Texas Instruments TAS5701 Audio amplifier
The TAS5701 serial control bus communicates through the I2C protocol only. The
serial bus is also used for periodic codec fault checking/reporting during
audio playback. For more product information please see the links below:
Required properties:
- compatible : "ti,tas5707"
- reg : I2C slave address
Example:
tas5707: tas5707@6c {
status = "okay";
compatible = "ti,tas5707";
reg = <0x6c>;
};

View File

@@ -13881,3 +13881,12 @@ F: drivers/amlogic/pm/lgcy_early_suspend.c
AMLOGIC MESON8B ADD SKT DTS
M: Yun Cai <yun.cai@amlogic.com>
F: arch/arm/boot/dts/amlogic/meson8b_skt.dts
AMLOGIC Audio codec driver
M: Xing Wang <xing.wang@amlogic.com>
F: arch/arm64/configs/meson64_defconfig
F: include/sound/tas57xx.h
F: sound/soc/codecs/amlogic/Kconfig
F: sound/soc/codecs/amlogic/Makefile
F: sound/soc/codecs/amlogic/tas5707.c
F: sound/soc/codecs/amlogic/tas5707.h

View File

@@ -334,6 +334,7 @@ CONFIG_AMLOGIC_SND_SOC_CODECS=y
CONFIG_AMLOGIC_SND_CODEC_DUMMY_CODEC=y
CONFIG_AMLOGIC_SND_CODEC_PCM2BT=y
CONFIG_AMLOGIC_SND_CODEC_AMLT9015=y
CONFIG_AMLOGIC_SND_SOC_TAS5707=y
CONFIG_AMLOGIC_SND_SOC=y
CONFIG_AMLOGIC_SND_SPLIT_MODE=y
CONFIG_UHID=y

61
include/sound/tas57xx.h Normal file
View File

@@ -0,0 +1,61 @@
#ifndef _TAS57xx_H
#define _TAS57xx_H
#include <sound/soc.h>
#include <linux/gpio/consumer.h>
#define TAS57XX_EQ_REGS 20
#define TAS57XX_EQ_BQS 9
#define TAS57XX_EQ_CHNLS 2
#define TAS57XX_EQ_BYTES (TAS57XX_EQ_REGS * TAS57XX_EQ_BQS * TAS57XX_EQ_CHNLS)
#ifndef NAME_SIZE
#define NAME_SIZE 32
#endif
struct tas57xx_reg_cfg {
const char *reg_data;
};
struct tas57xx_eq_cfg {
char name[NAME_SIZE];
char *regs;
int reg_bytes;
};
struct tas57xx_platform_data {
int (*init_func)(void);
int (*early_suspend_func)(void);
int (*suspend_func)(void);
int (*resume_func)(void);
int (*late_resume_func)(void);
char *custom_init_value_table;
int init_value_table_len;
char *init_regs;
int num_init_regs;
char *custom_drc1_table;
int custom_drc1_table_len;
char *custom_drc1_tko_table;
int custom_drc1_tko_table_len;
char *custom_drc2_table;
int custom_drc2_table_len;
char *custom_drc2_tko_table;
int custom_drc2_tko_table_len;
int num_eq_cfgs;
struct tas57xx_eq_cfg *eq_cfgs;
char *custom_sub_bq_table;
int custom_sub_bq_table_len;
unsigned int custom_master_vol;
int eq_enable;
int drc_enable;
int enable_ch1_drc;
int enable_ch2_drc;
int enable_hardmute;
int i2c_addr;
int reset_pin;
int phone_pin;
int scan_pin;
};
#endif

View File

@@ -440,5 +440,6 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o
obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o
# Amlogic
obj-$(CONFIG_AMLOGIC_SND_SOC_CODECS) += amlogic/

View File

@@ -39,12 +39,25 @@ config AMLOGIC_SND_CODEC_AMLT9015
this codec is internal
config AMLOGIC_SND_CODEC_PMU3
bool "Amlogic Audio AML PMU3 codec"
depends on AMLOGIC_SND_SOC_CODECS
default n
help
Amlogic Audio codec,
AML PMU3 codec,
AML PMU3 codec,
this codec is internal
bool "Amlogic Audio AML PMU3 codec"
depends on AMLOGIC_SND_SOC_CODECS
default n
help
Amlogic Audio codec,
AML PMU3 codec,
AML PMU3 codec,
this codec is internal
# Amlogic add Third part codec
config AMLOGIC_SND_SOC_TAS5707
bool "Texas Instruments TAS5707 amplifier"
depends on AMLOGIC_SND_SOC_CODECS
depends on I2C
default n
help
Enable support for Texas Instruments TAS5707 CODEC.
Select this if your TAS5707 is connected via an I2C bus.
Enable support for Texas Instruments TAS5707 CODEC.
Select this if your TAS5707 is connected via an I2C bus.
#endif #AMLOGIC_SND_SOC_CODECS

View File

@@ -4,8 +4,14 @@ snd-soc-pcm2bt-objs := pcm2bt.o
snd-soc-aml_t9015-objs := aml_codec_t9015.o
snd-soc-pmu3-objs := aml_pmu3.o
#Third part codecs
snd-soc-tas5707-objs := tas5707.o
# Amlogic
obj-$(CONFIG_AMLOGIC_SND_CODEC_DUMMY_CODEC) += snd-soc-dummy_codec.o
obj-$(CONFIG_AMLOGIC_SND_CODEC_PCM2BT) += snd-soc-pcm2bt.o
obj-$(CONFIG_AMLOGIC_SND_CODEC_AMLT9015) += snd-soc-aml_t9015.o
obj-$(CONFIG_AMLOGIC_SND_CODEC_PMU3) += snd-soc-pmu3.o
#Third part codecs
obj-$(CONFIG_AMLOGIC_SND_SOC_TAS5707) += snd-soc-tas5707.o

View File

@@ -0,0 +1,751 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/tas57xx.h>
#include <linux/amlogic/aml_gpio_consumer.h>
#include "tas5707.h"
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
static void tas5707_early_suspend(struct early_suspend *h);
static void tas5707_late_resume(struct early_suspend *h);
#endif
#define TAS5707_RATES (SNDRV_PCM_RATE_8000 | \
SNDRV_PCM_RATE_11025 | \
SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000)
#define TAS5707_FORMATS \
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
SNDRV_PCM_FMTBIT_S32_LE)
/* Power-up register defaults */
struct reg_default tas5707_reg_defaults[TAS5707_REGISTER_COUNT] = {
{0x00, 0x6c},
{0x01, 0x70},
{0x02, 0x00},
{0x03, 0xA0},
{0x04, 0x05},
{0x05, 0x40},
{0x06, 0x00},
{0x07, 0xFF},
{0x08, 0x30},
{0x09, 0x30},
{0x0A, 0xFF},
{0x0B, 0x00},
{0x0C, 0x00},
{0x0D, 0x00},
{0x0E, 0x91},
{0x10, 0x00},
{0x11, 0x02},
{0x12, 0xAC},
{0x13, 0x54},
{0x14, 0xAC},
{0x15, 0x54},
{0x16, 0x00},
{0x17, 0x00},
{0x18, 0x00},
{0x19, 0x00},
{0x1A, 0x30},
{0x1B, 0x0F},
{0x1C, 0x82},
{0x1D, 0x02}
};
static unsigned int tas5707_EQ_table_length = 280;
static unsigned int tas5707_EQ_table[280] = {
/*0x29---ch1_bq[0]*/
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0x2A---ch1_bq[1]*/
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0x2B---ch1_bq[2]*/
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0x2C---ch1_bq[3]*/
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0x2D---ch1_bq[4]*/
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0x2E---ch1_bq[5]*/
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0x2F---ch1_bq[6]*/
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0x30---ch2_bq[0]*/
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0x31---ch2_bq[1]*/
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0x32---ch2_bq[2]*/
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0x33---ch2_bq[3]*/
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0x34---ch2_bq[4]*/
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0x35---ch2_bq[5]*/
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0x36---ch2_bq[6]*/
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static unsigned int tas5707_drc1_table_length = 24;
static unsigned int tas5707_drc1_table[24] = {
/* 0x3A drc1_ae */
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x3B drc1_aa */
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x3C drc1_ad */
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static unsigned int tas5707_drc1_tko_length = 12;
static unsigned int tas5707_drc1_tko_table[12] = {
0xFD, 0xA2, 0x14, 0x90, /*0x40---drc1_t*/
0x03, 0x84, 0x21, 0x09, /*0x41---drc1_k*/
0x00, 0x08, 0x42, 0x10, /*0x42---drc1_o*/
};
/* codec private data */
struct tas5707_priv {
struct regmap *regmap;
struct snd_soc_codec *codec;
struct tas57xx_platform_data *pdata;
/*Platform provided EQ configuration */
int num_eq_conf_texts;
const char **eq_conf_texts;
int eq_cfg;
struct soc_enum eq_conf_enum;
unsigned char Ch1_vol;
unsigned char Ch2_vol;
unsigned char master_vol;
unsigned int mclk;
#ifdef CONFIG_HAS_EARLYSUSPEND
struct early_suspend early_suspend;
#endif
};
static int tas5707_set_EQ_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
static int tas5707_get_EQ_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
static int tas5707_set_DRC_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
static int tas5707_get_DRC_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12700, 50, 1);
static const DECLARE_TLV_DB_SCALE(chvol_tlv, -10300, 50, 1);
static const struct snd_kcontrol_new tas5707_snd_controls[] = {
SOC_SINGLE_TLV("Master Volume", DDX_MASTER_VOLUME, 0,
0xff, 1, mvol_tlv),
SOC_SINGLE_TLV("Ch1 Volume", DDX_CHANNEL1_VOL, 0,
0xff, 1, chvol_tlv),
SOC_SINGLE_TLV("Ch2 Volume", DDX_CHANNEL2_VOL, 0,
0xff, 1, chvol_tlv),
SOC_SINGLE("Ch1 Switch", DDX_SOFT_MUTE, 0, 1, 1),
SOC_SINGLE("Ch2 Switch", DDX_SOFT_MUTE, 1, 1, 1),
SOC_SINGLE_RANGE("Fine Master Volume", DDX_CHANNEL3_VOL, 0,
0x80, 0x83, 0),
SOC_SINGLE_BOOL_EXT("Set EQ Enable", 0,
tas5707_get_EQ_enum, tas5707_set_EQ_enum),
SOC_SINGLE_BOOL_EXT("Set DRC Enable", 0,
tas5707_get_DRC_enum, tas5707_set_DRC_enum),
};
static int tas5707_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct tas5707_priv *tas5707 = snd_soc_codec_get_drvdata(codec);
tas5707->mclk = freq;
/* 0x74 = 512fs; 0x6c = 256fs */
if (freq == 512 * 48000)
snd_soc_write(codec, DDX_CLOCK_CTL, 0x74);
else
snd_soc_write(codec, DDX_CLOCK_CTL, 0x6c);
return 0;
}
static int tas5707_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
case SND_SOC_DAIFMT_RIGHT_J:
case SND_SOC_DAIFMT_LEFT_J:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
break;
default:
return -EINVAL;
}
return 0;
}
static int tas5707_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
unsigned int rate;
rate = params_rate(params);
pr_debug("rate: %u\n", rate);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S24_LE:
case SNDRV_PCM_FORMAT_S24_BE:
pr_debug("24bit\n");
/* fall through */
case SNDRV_PCM_FORMAT_S32_LE:
case SNDRV_PCM_FORMAT_S20_3LE:
case SNDRV_PCM_FORMAT_S20_3BE:
pr_debug("20bit\n");
break;
case SNDRV_PCM_FORMAT_S16_LE:
case SNDRV_PCM_FORMAT_S16_BE:
pr_debug("16bit\n");
break;
default:
return -EINVAL;
}
return 0;
}
static int tas5707_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
pr_debug("level = %d\n", level);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
/* Full power on */
break;
case SND_SOC_BIAS_STANDBY:
break;
case SND_SOC_BIAS_OFF:
/* The chip runs through the power down sequence for us. */
break;
}
codec->component.dapm.bias_level = level;
return 0;
}
static const struct snd_soc_dai_ops tas5707_dai_ops = {
.hw_params = tas5707_hw_params,
.set_sysclk = tas5707_set_dai_sysclk,
.set_fmt = tas5707_set_dai_fmt,
};
static struct snd_soc_dai_driver tas5707_dai = {
.name = "tas5707",
.playback = {
.stream_name = "HIFI Playback",
.channels_min = 2,
.channels_max = 8,
.rates = TAS5707_RATES,
.formats = TAS5707_FORMATS,
},
.ops = &tas5707_dai_ops,
};
static int tas5707_set_master_vol(struct snd_soc_codec *codec)
{
struct tas57xx_platform_data *pdata = dev_get_platdata(codec->dev);
/* using user BSP defined master vol config; */
if (pdata && pdata->custom_master_vol) {
pr_debug("tas5707_set_master_vol::%d\n",
pdata->custom_master_vol);
snd_soc_write(codec, DDX_MASTER_VOLUME,
(0xff - pdata->custom_master_vol));
} else {
snd_soc_write(codec, DDX_MASTER_VOLUME, 0x69);
}
return 0;
}
/* tas5707 DRC for channel L/R */
static int tas5707_set_drc1(struct snd_soc_codec *codec)
{
struct tas5707_priv *tas5707 = snd_soc_codec_get_drvdata(codec);
int i = 0, j = 0;
unsigned int *p = NULL;
u8 tas5707_drc1_table_tmp[8];
u8 tas5707_drc1_tko_table_tmp[4];
p = &tas5707_drc1_table[0];
for (i = 0; i < 3; i++) {
for (j = 0; j < 8; j++)
tas5707_drc1_table_tmp[j] = p[i * 8 + j];
regmap_raw_write(tas5707->regmap, DDX_DRC1_AE + i,
tas5707_drc1_table_tmp, 8);
/* for (j = 0; j < 8; j++)
* pr_info("TAS5707_drc1_table[%d][%d]: %x\n",
* i, j, tas5707_drc1_table_tmp[j]);
*/
}
p = &tas5707_drc1_tko_table[0];
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++)
tas5707_drc1_tko_table_tmp[j] = p[i * 4 + j];
regmap_raw_write(tas5707->regmap, DDX_DRC1_T + i,
tas5707_drc1_tko_table_tmp, 4);
/* for (j = 0; j < 4; j++)
* pr_info("tas5707_drc1_tko_table[%d][%d]: %x\n",
* i, j, tas5707_drc1_tko_table_tmp[j]);
*/
}
return 0;
}
static int tas5707_set_drc(struct snd_soc_codec *codec)
{
struct tas5707_priv *tas5707 = snd_soc_codec_get_drvdata(codec);
char drc_mask = 0;
u8 tas5707_drc_ctl_table[] = { 0x00, 0x00, 0x00, 0x00 };
regmap_raw_write(tas5707->regmap, DDX_DRC_CTL,
tas5707_drc_ctl_table, 4);
drc_mask |= 0x01;
tas5707_drc_ctl_table[3] = drc_mask;
tas5707_set_drc1(codec);
regmap_raw_write(tas5707->regmap, DDX_DRC_CTL,
tas5707_drc_ctl_table, 4);
return 0;
}
static int tas5707_set_eq_biquad(struct snd_soc_codec *codec)
{
struct tas5707_priv *tas5707 = snd_soc_codec_get_drvdata(codec);
int i = 0, j = 0, k = 0;
unsigned int *p = NULL;
u8 addr;
u8 tas5707_bq_table[20];
p = &tas5707_EQ_table[0];
for (i = 0; i < 2; i++) {
for (j = 0; j < 7; j++) {
addr = (DDX_CH1_BQ_0 + i * 7 + j);
for (k = 0; k < 20; k++)
tas5707_bq_table[k] =
p[i * 7 * 20 + j * 20 + k];
regmap_raw_write(tas5707->regmap, addr,
tas5707_bq_table, 20);
/* for (k = 0; k < 20; k++)
* pr_info("tas5707_bq_table[%d]: %x\n",
* k, tas5707_bq_table[k]);
*/
}
}
return 0;
}
static int tas5707_set_eq(struct snd_soc_codec *codec)
{
struct tas5707_priv *tas5707 = snd_soc_codec_get_drvdata(codec);
u8 tas5707_eq_ctl_table[] = { 0x00, 0x00, 0x00, 0x80 };
regmap_raw_write(tas5707->regmap, DDX_BANKSWITCH_AND_EQCTL,
tas5707_eq_ctl_table, 4);
tas5707_set_eq_biquad(codec);
tas5707_eq_ctl_table[3] &= 0x7F;
regmap_raw_write(tas5707->regmap, DDX_BANKSWITCH_AND_EQCTL,
tas5707_eq_ctl_table, 4);
return 0;
}
static bool EQ_enum_value = 1;
static int tas5707_set_EQ_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct tas5707_priv *tas5707 = snd_soc_codec_get_drvdata(codec);
u8 tas5707_eq_ctl_table[] = { 0x00, 0x00, 0x00, 0x80 };
EQ_enum_value = ucontrol->value.integer.value[0];
if (EQ_enum_value == 1)
tas5707_set_eq(codec);
else
regmap_raw_write(tas5707->regmap,
DDX_BANKSWITCH_AND_EQCTL,
tas5707_eq_ctl_table, 4);
return 0;
}
static int tas5707_get_EQ_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = EQ_enum_value;
return 0;
}
static bool DRC_enum_value = 1;
static int tas5707_set_DRC_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct tas5707_priv *tas5707 = snd_soc_codec_get_drvdata(codec);
u8 tas5707_drc_ctl_table[] = { 0x00, 0x00, 0x00, 0x00 };
DRC_enum_value = ucontrol->value.integer.value[0];
if (DRC_enum_value == 1)
tas5707_set_drc(codec);
else
regmap_raw_write(tas5707->regmap, DDX_DRC_CTL,
tas5707_drc_ctl_table, 4);
return 0;
}
static int tas5707_get_DRC_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = DRC_enum_value;
return 0;
}
static int reset_tas5707_GPIO(struct snd_soc_codec *codec)
{
struct tas5707_priv *tas5707 = snd_soc_codec_get_drvdata(codec);
struct tas57xx_platform_data *pdata = tas5707->pdata;
int ret = devm_gpio_request_one(codec->dev, pdata->reset_pin,
GPIOF_OUT_INIT_LOW,
"tas5707-reset-pin");
if (ret < 0)
return -1;
gpio_direction_output(pdata->reset_pin, GPIOF_OUT_INIT_LOW);
udelay(1000);
gpio_direction_output(pdata->reset_pin, GPIOF_OUT_INIT_HIGH);
mdelay(15);
return 0;
}
static int tas5707_init(struct snd_soc_codec *codec)
{
unsigned char burst_data[][4] = {
{ 0x00, 0x01, 0x77, 0x72 },
{ 0x00, 0x00, 0x42, 0x03 },
{ 0x01, 0x02, 0x13, 0x45 },
};
struct tas5707_priv *tas5707 = snd_soc_codec_get_drvdata(codec);
reset_tas5707_GPIO(codec);
dev_info(codec->dev, "tas5707_init!\n");
snd_soc_write(codec, DDX_OSC_TRIM, 0x00);
msleep(50);
snd_soc_write(codec, DDX_CLOCK_CTL, 0x6c);
snd_soc_write(codec, DDX_SYS_CTL_1, 0xa0);
snd_soc_write(codec, DDX_SERIAL_DATA_INTERFACE, 0x05);
snd_soc_write(codec, DDX_BKND_ERR, 0x02);
regmap_raw_write(tas5707->regmap, DDX_INPUT_MUX, burst_data[0], 4);
regmap_raw_write(tas5707->regmap, DDX_CH4_SOURCE_SELECT,
burst_data[1], 4);
regmap_raw_write(tas5707->regmap, DDX_PWM_MUX, burst_data[2], 4);
/*drc */
tas5707_set_drc(codec);
/*eq */
tas5707_set_eq(codec);
snd_soc_write(codec, DDX_VOLUME_CONFIG, 0xD1);
snd_soc_write(codec, DDX_SYS_CTL_2, 0x84);
snd_soc_write(codec, DDX_START_STOP_PERIOD, 0x95);
snd_soc_write(codec, DDX_PWM_SHUTDOWN_GROUP, 0x30);
snd_soc_write(codec, DDX_MODULATION_LIMIT, 0x02);
/*normal operation */
if ((tas5707_set_master_vol(codec)) < 0)
dev_err(codec->dev, "fail to set tas5707 master vol!\n");
snd_soc_write(codec, DDX_CHANNEL1_VOL, tas5707->Ch1_vol);
snd_soc_write(codec, DDX_CHANNEL2_VOL, tas5707->Ch2_vol);
snd_soc_write(codec, DDX_SOFT_MUTE, 0x00);
snd_soc_write(codec, DDX_CHANNEL3_VOL, 0x80);
return 0;
}
static int tas5707_probe(struct snd_soc_codec *codec)
{
#ifdef CONFIG_HAS_EARLYSUSPEND
tas5707->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
tas5707->early_suspend.suspend = tas5707_early_suspend;
tas5707->early_suspend.resume = tas5707_late_resume;
tas5707->early_suspend.param = codec;
register_early_suspend(&(tas5707->early_suspend));
#endif
tas5707_init(codec);
return 0;
}
static int tas5707_remove(struct snd_soc_codec *codec)
{
#ifdef CONFIG_HAS_EARLYSUSPEND
struct tas5707_priv *tas5707 = snd_soc_codec_get_drvdata(codec);
unregister_early_suspend(&(tas5707->early_suspend));
#endif
return 0;
}
#ifdef CONFIG_PM
static int tas5707_suspend(struct snd_soc_codec *codec)
{
struct tas5707_priv *tas5707 = snd_soc_codec_get_drvdata(codec);
struct tas57xx_platform_data *pdata = dev_get_platdata(codec->dev);
dev_info(codec->dev, "tas5707_suspend!\n");
if (pdata && pdata->suspend_func)
pdata->suspend_func();
/*save volume */
tas5707->Ch1_vol = snd_soc_read(codec, DDX_CHANNEL1_VOL);
tas5707->Ch2_vol = snd_soc_read(codec, DDX_CHANNEL2_VOL);
tas5707->master_vol = snd_soc_read(codec, DDX_MASTER_VOLUME);
tas5707_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int tas5707_resume(struct snd_soc_codec *codec)
{
struct tas5707_priv *tas5707 = snd_soc_codec_get_drvdata(codec);
struct tas57xx_platform_data *pdata = dev_get_platdata(codec->dev);
dev_info(codec->dev, "tas5707_resume!\n");
if (pdata && pdata->resume_func)
pdata->resume_func();
tas5707_init(codec);
snd_soc_write(codec, DDX_CHANNEL1_VOL, tas5707->Ch1_vol);
snd_soc_write(codec, DDX_CHANNEL2_VOL, tas5707->Ch2_vol);
snd_soc_write(codec, DDX_MASTER_VOLUME, tas5707->master_vol);
tas5707_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
#else
#define tas5707_suspend NULL
#define tas5707_resume NULL
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
static void tas5707_early_suspend(struct early_suspend *h)
{
}
static void tas5707_late_resume(struct early_suspend *h)
{
}
#endif
static const struct snd_soc_dapm_widget tas5707_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "HIFI Playback", SND_SOC_NOPM, 0, 0),
};
static const struct snd_soc_codec_driver soc_codec_dev_tas5707 = {
.probe = tas5707_probe,
.remove = tas5707_remove,
.suspend = tas5707_suspend,
.resume = tas5707_resume,
.set_bias_level = tas5707_set_bias_level,
.component_driver = {
.controls = tas5707_snd_controls,
.num_controls = ARRAY_SIZE(tas5707_snd_controls),
.dapm_widgets = tas5707_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(tas5707_dapm_widgets),
}
};
static const struct regmap_config tas5707_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = TAS5707_REGISTER_COUNT,
.reg_defaults = tas5707_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tas5707_reg_defaults),
.cache_type = REGCACHE_RBTREE,
};
static int tas5707_parse_dt(
struct tas5707_priv *tas5707,
struct device_node *np)
{
int ret = 0;
int reset_pin = 0;
reset_pin = of_get_named_gpio(np, "reset_pin", 0);
if (reset_pin < 0) {
pr_err("%s fail to get reset pin from dts!\n", __func__);
ret = -1;
} else {
tas5707->pdata->reset_pin = reset_pin;
pr_info("%s pdata->reset_pin = %d!\n", __func__,
tas5707->pdata->reset_pin);
}
return ret;
}
static int tas5707_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct tas5707_priv *tas5707;
struct tas57xx_platform_data *pdata;
int ret;
tas5707 = devm_kzalloc(&i2c->dev, sizeof(struct tas5707_priv),
GFP_KERNEL);
if (!tas5707)
return -ENOMEM;
tas5707->regmap = devm_regmap_init_i2c(i2c, &tas5707_regmap);
if (IS_ERR(tas5707->regmap)) {
ret = PTR_ERR(tas5707->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
return ret;
}
i2c_set_clientdata(i2c, tas5707);
pdata = devm_kzalloc(&i2c->dev,
sizeof(struct tas57xx_platform_data),
GFP_KERNEL);
if (!pdata) {
pr_err("%s failed to kzalloc for tas5707 pdata\n", __func__);
return -ENOMEM;
}
tas5707->pdata = pdata;
tas5707_parse_dt(tas5707, i2c->dev.of_node);
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_tas5707,
&tas5707_dai, 1);
if (ret != 0)
dev_err(&i2c->dev, "Failed to register codec (%d)\n", ret);
return ret;
}
static int tas5707_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static const struct i2c_device_id tas5707_i2c_id[] = {
{ "tas5707", 0 },
{}
};
static const struct of_device_id tas5707_of_id[] = {
{.compatible = "ti,tas5707",},
{ /* senitel */ }
};
MODULE_DEVICE_TABLE(of, tas5707_of_id);
static struct i2c_driver tas5707_i2c_driver = {
.driver = {
.name = "tas5707",
.of_match_table = tas5707_of_id,
.owner = THIS_MODULE,
},
.probe = tas5707_i2c_probe,
.remove = tas5707_i2c_remove,
.id_table = tas5707_i2c_id,
};
module_i2c_driver(tas5707_i2c_driver);
module_param_array(tas5707_EQ_table,
uint, &tas5707_EQ_table_length, 0664);
MODULE_PARM_DESC(tas5707_EQ_table,
"An array of tas5707 EQ param");
module_param_array(tas5707_drc1_table,
uint, &tas5707_drc1_table_length, 0664);
MODULE_PARM_DESC(tas5707_drc1_table,
"An array of tas5707 DRC table param");
module_param_array(tas5707_drc1_tko_table,
uint, &tas5707_drc1_tko_length, 0664);
MODULE_PARM_DESC(tas5707_drc1_tko_table,
"An array of tas5707 DRC tko table param");
MODULE_DESCRIPTION("ASoC Tas5707 driver");
MODULE_AUTHOR("AML MM team");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,92 @@
#ifndef _TAS5707_H
#define _TAS5707_H
#define DDX_I2C_ADDR 0x36
#define DDX_CLOCK_CTL 0x00
#define DDX_DEVICE_ID 0x01
#define DDX_ERROR_STATUS 0x02
#define DDX_SYS_CTL_1 0x03
#define DDX_SERIAL_DATA_INTERFACE 0x04
#define DDX_SYS_CTL_2 0x05
#define DDX_SOFT_MUTE 0x06
#define DDX_MASTER_VOLUME 0x07
#define DDX_CHANNEL1_VOL 0x08
#define DDX_CHANNEL2_VOL 0x09
#define DDX_CHANNEL3_VOL 0x0A
#define DDX_VOLUME_CONFIG 0x0E
#define DDX_MODULATION_LIMIT 0x10
#define DDX_IC_DELAY_CHANNEL_1 0x11
#define DDX_IC_DELAY_CHANNEL_2 0x12
#define DDX_IC_DELAY_CHANNEL_3 0x13
#define DDX_IC_DELAY_CHANNEL_4 0x14
#define DDX_PWM_SHUTDOWN_GROUP 0x19
#define DDX_START_STOP_PERIOD 0x1A
#define DDX_OSC_TRIM 0x1B
#define DDX_BKND_ERR 0x1C
#define DDX_NUM_BYTE_REG 0x1D
#define DDX_INPUT_MUX 0x20
#define DDX_CH4_SOURCE_SELECT 0x21
#define DDX_PWM_MUX 0x25
#define DDX_CH1_BQ_0 0x29
#define DDX_CH1_BQ_1 0x2A
#define DDX_CH1_BQ_2 0x2B
#define DDX_CH1_BQ_3 0x2C
#define DDX_CH1_BQ_4 0x2D
#define DDX_CH1_BQ_5 0x2E
#define DDX_CH1_BQ_6 0x2F
#define DDX_CH2_BQ_0 0x30
#define DDX_CH2_BQ_1 0x31
#define DDX_CH2_BQ_2 0x32
#define DDX_CH2_BQ_3 0x33
#define DDX_CH2_BQ_4 0x34
#define DDX_CH2_BQ_5 0x35
#define DDX_CH2_BQ_6 0x36
#define DDX_DRC1_AE 0x3A
#define DDX_DRC1_AA 0x3B
#define DDX_DRC1_AD 0x3C
#define DDX_DRC2_AE 0x3D
#define DDX_DRC2_AA 0x3E
#define DDX_DRC2_AD 0x3F
#define DDX_DRC1_T 0x40
#define DDX_DRC1_K 0x41
#define DDX_DRC1_O 0x42
#define DDX_DRC2_T 0x43
#define DDX_DRC2_K 0x44
#define DDX_DRC2_O 0x45
#define DDX_DRC_CTL 0x46
#define DDX_BANKSWITCH_AND_EQCTL 0x50
#define DDX_CH_1_OUTPUT_MIXER 0x51
#define DDX_CH_2_OUTPUT_MIXER 0x52
#define DDX_CH_1_INPUT_MIXER 0x53
#define DDX_CH_2_INPUT_MIXER 0x54
#define DDX_CH_3_INPUT_MIXER 0x55
#define DDX_OUTPUT_POST_SCALE 0x56
#define DDX_OUTPUT_PRE_SCALE 0x57
#define DDX_CH1_BQ_7 0x58
#define DDX_CH1_BQ_8 0x59
#define DDX_SUBCHANNEL_BQ_0 0x5A
#define DDX_SUBCHANNEL_BQ_1 0x5B
#define DDX_CH2_BQ_7 0x5C
#define DDX_CH2_BQ_8 0x5D
#define DDX_PSEUDO_CH2_BQ_0 0x5E
#define DDX_CH_4_OUTPUT_MIXER 0x60
#define DDX_CH_4_INPUT_MIXER 0x61
#define DDX_CH_IDF_POST_SCALE 0x62
#define DDX_CH_DEV_ADDR_ENABLE 0xF8
#define DDX_CH_DEV_ADDR_UPDATE 0xF9
#define DDX_DRC_BYTES (8)
#define DDX_BQ_BYTES (20)
#define TAS5707_REGISTER_COUNT 0xFF
#endif