|
@@ -75,19 +75,24 @@ MODULE_DESCRIPTION("tas codec driver for snd-aoa");
|
|
|
#include "../aoa.h"
|
|
|
#include "../soundbus/soundbus.h"
|
|
|
|
|
|
-
|
|
|
#define PFX "snd-aoa-codec-tas: "
|
|
|
|
|
|
+
|
|
|
struct tas {
|
|
|
struct aoa_codec codec;
|
|
|
struct i2c_client i2c;
|
|
|
- u32 muted_l:1, muted_r:1,
|
|
|
- controls_created:1;
|
|
|
+ u32 mute_l:1, mute_r:1 ,
|
|
|
+ controls_created:1 ,
|
|
|
+ drc_enabled:1,
|
|
|
+ hw_enabled:1;
|
|
|
u8 cached_volume_l, cached_volume_r;
|
|
|
u8 mixer_l[3], mixer_r[3];
|
|
|
u8 acr;
|
|
|
+ int drc_range;
|
|
|
};
|
|
|
|
|
|
+static int tas_reset_init(struct tas *tas);
|
|
|
+
|
|
|
static struct tas *codec_to_tas(struct aoa_codec *codec)
|
|
|
{
|
|
|
return container_of(codec, struct tas, codec);
|
|
@@ -101,6 +106,28 @@ static inline int tas_write_reg(struct tas *tas, u8 reg, u8 len, u8 *data)
|
|
|
return i2c_smbus_write_i2c_block_data(&tas->i2c, reg, len, data);
|
|
|
}
|
|
|
|
|
|
+static void tas3004_set_drc(struct tas *tas)
|
|
|
+{
|
|
|
+ unsigned char val[6];
|
|
|
+
|
|
|
+ if (tas->drc_enabled)
|
|
|
+ val[0] = 0x50; /* 3:1 above threshold */
|
|
|
+ else
|
|
|
+ val[0] = 0x51; /* disabled */
|
|
|
+ val[1] = 0x02; /* 1:1 below threshold */
|
|
|
+ if (tas->drc_range > 0xef)
|
|
|
+ val[2] = 0xef;
|
|
|
+ else if (tas->drc_range < 0)
|
|
|
+ val[2] = 0x00;
|
|
|
+ else
|
|
|
+ val[2] = tas->drc_range;
|
|
|
+ val[3] = 0xb0;
|
|
|
+ val[4] = 0x60;
|
|
|
+ val[5] = 0xa0;
|
|
|
+
|
|
|
+ tas_write_reg(tas, TAS_REG_DRC, 6, val);
|
|
|
+}
|
|
|
+
|
|
|
static void tas_set_volume(struct tas *tas)
|
|
|
{
|
|
|
u8 block[6];
|
|
@@ -113,8 +140,8 @@ static void tas_set_volume(struct tas *tas)
|
|
|
if (left > 177) left = 177;
|
|
|
if (right > 177) right = 177;
|
|
|
|
|
|
- if (tas->muted_l) left = 0;
|
|
|
- if (tas->muted_r) right = 0;
|
|
|
+ if (tas->mute_l) left = 0;
|
|
|
+ if (tas->mute_r) right = 0;
|
|
|
|
|
|
/* analysing the volume and mixer tables shows
|
|
|
* that they are similar enough when we shift
|
|
@@ -202,7 +229,8 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol,
|
|
|
|
|
|
tas->cached_volume_l = ucontrol->value.integer.value[0];
|
|
|
tas->cached_volume_r = ucontrol->value.integer.value[1];
|
|
|
- tas_set_volume(tas);
|
|
|
+ if (tas->hw_enabled)
|
|
|
+ tas_set_volume(tas);
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
@@ -230,8 +258,8 @@ static int tas_snd_mute_get(struct snd_kcontrol *kcontrol,
|
|
|
{
|
|
|
struct tas *tas = snd_kcontrol_chip(kcontrol);
|
|
|
|
|
|
- ucontrol->value.integer.value[0] = !tas->muted_l;
|
|
|
- ucontrol->value.integer.value[1] = !tas->muted_r;
|
|
|
+ ucontrol->value.integer.value[0] = !tas->mute_l;
|
|
|
+ ucontrol->value.integer.value[1] = !tas->mute_r;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -240,13 +268,14 @@ static int tas_snd_mute_put(struct snd_kcontrol *kcontrol,
|
|
|
{
|
|
|
struct tas *tas = snd_kcontrol_chip(kcontrol);
|
|
|
|
|
|
- if (tas->muted_l == !ucontrol->value.integer.value[0]
|
|
|
- && tas->muted_r == !ucontrol->value.integer.value[1])
|
|
|
+ if (tas->mute_l == !ucontrol->value.integer.value[0]
|
|
|
+ && tas->mute_r == !ucontrol->value.integer.value[1])
|
|
|
return 0;
|
|
|
|
|
|
- tas->muted_l = !ucontrol->value.integer.value[0];
|
|
|
- tas->muted_r = !ucontrol->value.integer.value[1];
|
|
|
- tas_set_volume(tas);
|
|
|
+ tas->mute_l = !ucontrol->value.integer.value[0];
|
|
|
+ tas->mute_r = !ucontrol->value.integer.value[1];
|
|
|
+ if (tas->hw_enabled)
|
|
|
+ tas_set_volume(tas);
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
@@ -294,7 +323,8 @@ static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol,
|
|
|
tas->mixer_l[idx] = ucontrol->value.integer.value[0];
|
|
|
tas->mixer_r[idx] = ucontrol->value.integer.value[1];
|
|
|
|
|
|
- tas_set_mixer(tas);
|
|
|
+ if (tas->hw_enabled)
|
|
|
+ tas_set_mixer(tas);
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
@@ -346,7 +376,8 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
|
|
|
tas->acr |= TAS_ACR_INPUT_B;
|
|
|
if (oldacr == tas->acr)
|
|
|
return 0;
|
|
|
- tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
|
|
|
+ if (tas->hw_enabled)
|
|
|
+ tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
@@ -399,26 +430,66 @@ static int tas_usable(struct codec_info_item *cii,
|
|
|
static int tas_reset_init(struct tas *tas)
|
|
|
{
|
|
|
u8 tmp;
|
|
|
+
|
|
|
+ tas->codec.gpio->methods->all_amps_off(tas->codec.gpio);
|
|
|
+ msleep(5);
|
|
|
tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
|
|
|
- msleep(1);
|
|
|
+ msleep(5);
|
|
|
tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 1);
|
|
|
- msleep(1);
|
|
|
+ msleep(20);
|
|
|
tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
|
|
|
- msleep(1);
|
|
|
-
|
|
|
- tas->acr &= ~TAS_ACR_ANALOG_PDOWN;
|
|
|
- tas->acr |= TAS_ACR_B_MONAUREAL | TAS_ACR_B_MON_SEL_RIGHT;
|
|
|
- if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
|
|
|
- return -ENODEV;
|
|
|
+ msleep(10);
|
|
|
+ tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
|
|
|
|
|
|
tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT;
|
|
|
if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp))
|
|
|
return -ENODEV;
|
|
|
|
|
|
+ tas->acr |= TAS_ACR_ANALOG_PDOWN | TAS_ACR_B_MONAUREAL |
|
|
|
+ TAS_ACR_B_MON_SEL_RIGHT;
|
|
|
+ if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
tmp = 0;
|
|
|
if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp))
|
|
|
return -ENODEV;
|
|
|
|
|
|
+ tas3004_set_drc(tas);
|
|
|
+
|
|
|
+ /* Set treble & bass to 0dB */
|
|
|
+ tmp = 114;
|
|
|
+ tas_write_reg(tas, TAS_REG_TREBLE, 1, &tmp);
|
|
|
+ tas_write_reg(tas, TAS_REG_BASS, 1, &tmp);
|
|
|
+
|
|
|
+ tas->acr &= ~TAS_ACR_ANALOG_PDOWN;
|
|
|
+ if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock)
|
|
|
+{
|
|
|
+ struct tas *tas = cii->codec_data;
|
|
|
+
|
|
|
+ switch(clock) {
|
|
|
+ case CLOCK_SWITCH_PREPARE_SLAVE:
|
|
|
+ /* Clocks are going away, mute mute mute */
|
|
|
+ tas->codec.gpio->methods->all_amps_off(tas->codec.gpio);
|
|
|
+ tas->hw_enabled = 0;
|
|
|
+ break;
|
|
|
+ case CLOCK_SWITCH_SLAVE:
|
|
|
+ /* Clocks are back, re-init the codec */
|
|
|
+ tas_reset_init(tas);
|
|
|
+ tas_set_volume(tas);
|
|
|
+ tas_set_mixer(tas);
|
|
|
+ tas->hw_enabled = 1;
|
|
|
+ tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ /* doesn't happen as of now */
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -427,6 +498,7 @@ static int tas_reset_init(struct tas *tas)
|
|
|
* our i2c device is suspended, and then take note of that! */
|
|
|
static int tas_suspend(struct tas *tas)
|
|
|
{
|
|
|
+ tas->hw_enabled = 0;
|
|
|
tas->acr |= TAS_ACR_ANALOG_PDOWN;
|
|
|
tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
|
|
|
return 0;
|
|
@@ -438,6 +510,7 @@ static int tas_resume(struct tas *tas)
|
|
|
tas_reset_init(tas);
|
|
|
tas_set_volume(tas);
|
|
|
tas_set_mixer(tas);
|
|
|
+ tas->hw_enabled = 1;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -463,6 +536,7 @@ static struct codec_info tas_codec_info = {
|
|
|
.bus_factor = 64,
|
|
|
.owner = THIS_MODULE,
|
|
|
.usable = tas_usable,
|
|
|
+ .switch_clock = tas_switch_clock,
|
|
|
#ifdef CONFIG_PM
|
|
|
.suspend = _tas_suspend,
|
|
|
.resume = _tas_resume,
|
|
@@ -483,6 +557,7 @@ static int tas_init_codec(struct aoa_codec *codec)
|
|
|
printk(KERN_ERR PFX "tas failed to initialise\n");
|
|
|
return -ENXIO;
|
|
|
}
|
|
|
+ tas->hw_enabled = 1;
|
|
|
|
|
|
if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev,
|
|
|
aoa_get_card(),
|
|
@@ -548,6 +623,7 @@ static int tas_create(struct i2c_adapter *adapter,
|
|
|
tas->i2c.driver = &tas_driver;
|
|
|
tas->i2c.adapter = adapter;
|
|
|
tas->i2c.addr = addr;
|
|
|
+ tas->drc_range = TAS3004_DRC_MAX;
|
|
|
strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE-1);
|
|
|
|
|
|
if (i2c_attach_client(&tas->i2c)) {
|
|
@@ -564,7 +640,9 @@ static int tas_create(struct i2c_adapter *adapter,
|
|
|
if (aoa_codec_register(&tas->codec)) {
|
|
|
goto detach;
|
|
|
}
|
|
|
- printk(KERN_DEBUG "snd-aoa-codec-tas: created and attached tas instance\n");
|
|
|
+ printk(KERN_DEBUG
|
|
|
+ "snd-aoa-codec-tas: tas found, addr 0x%02x on %s\n",
|
|
|
+ addr, node->full_name);
|
|
|
return 0;
|
|
|
detach:
|
|
|
i2c_detach_client(&tas->i2c);
|