Commit 6a4f5787 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Linus Torvalds

[PATCH] aoa: tas: fix initialisation/reset

This patch fixes the initialisation and reset of the tas codec.  The tas will
often reset if the i2s clocks go away so it needs to be completely
re-initialised when clocks come back.

Also, this patch adds some code for DRC that will be exploited later to add a
DRC control again, fixing a regression over snd-powermac.
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 14b42963
...@@ -75,19 +75,24 @@ MODULE_DESCRIPTION("tas codec driver for snd-aoa"); ...@@ -75,19 +75,24 @@ MODULE_DESCRIPTION("tas codec driver for snd-aoa");
#include "../aoa.h" #include "../aoa.h"
#include "../soundbus/soundbus.h" #include "../soundbus/soundbus.h"
#define PFX "snd-aoa-codec-tas: " #define PFX "snd-aoa-codec-tas: "
struct tas { struct tas {
struct aoa_codec codec; struct aoa_codec codec;
struct i2c_client i2c; struct i2c_client i2c;
u32 muted_l:1, muted_r:1, u32 mute_l:1, mute_r:1 ,
controls_created:1; controls_created:1 ,
drc_enabled:1,
hw_enabled:1;
u8 cached_volume_l, cached_volume_r; u8 cached_volume_l, cached_volume_r;
u8 mixer_l[3], mixer_r[3]; u8 mixer_l[3], mixer_r[3];
u8 acr; u8 acr;
int drc_range;
}; };
static int tas_reset_init(struct tas *tas);
static struct tas *codec_to_tas(struct aoa_codec *codec) static struct tas *codec_to_tas(struct aoa_codec *codec)
{ {
return container_of(codec, struct tas, 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) ...@@ -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); 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) static void tas_set_volume(struct tas *tas)
{ {
u8 block[6]; u8 block[6];
...@@ -113,8 +140,8 @@ static void tas_set_volume(struct tas *tas) ...@@ -113,8 +140,8 @@ static void tas_set_volume(struct tas *tas)
if (left > 177) left = 177; if (left > 177) left = 177;
if (right > 177) right = 177; if (right > 177) right = 177;
if (tas->muted_l) left = 0; if (tas->mute_l) left = 0;
if (tas->muted_r) right = 0; if (tas->mute_r) right = 0;
/* analysing the volume and mixer tables shows /* analysing the volume and mixer tables shows
* that they are similar enough when we shift * that they are similar enough when we shift
...@@ -202,6 +229,7 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol, ...@@ -202,6 +229,7 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol,
tas->cached_volume_l = ucontrol->value.integer.value[0]; tas->cached_volume_l = ucontrol->value.integer.value[0];
tas->cached_volume_r = ucontrol->value.integer.value[1]; tas->cached_volume_r = ucontrol->value.integer.value[1];
if (tas->hw_enabled)
tas_set_volume(tas); tas_set_volume(tas);
return 1; return 1;
} }
...@@ -230,8 +258,8 @@ static int tas_snd_mute_get(struct snd_kcontrol *kcontrol, ...@@ -230,8 +258,8 @@ static int tas_snd_mute_get(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = !tas->muted_l; ucontrol->value.integer.value[0] = !tas->mute_l;
ucontrol->value.integer.value[1] = !tas->muted_r; ucontrol->value.integer.value[1] = !tas->mute_r;
return 0; return 0;
} }
...@@ -240,12 +268,13 @@ static int tas_snd_mute_put(struct snd_kcontrol *kcontrol, ...@@ -240,12 +268,13 @@ static int tas_snd_mute_put(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
if (tas->muted_l == !ucontrol->value.integer.value[0] if (tas->mute_l == !ucontrol->value.integer.value[0]
&& tas->muted_r == !ucontrol->value.integer.value[1]) && tas->mute_r == !ucontrol->value.integer.value[1])
return 0; return 0;
tas->muted_l = !ucontrol->value.integer.value[0]; tas->mute_l = !ucontrol->value.integer.value[0];
tas->muted_r = !ucontrol->value.integer.value[1]; tas->mute_r = !ucontrol->value.integer.value[1];
if (tas->hw_enabled)
tas_set_volume(tas); tas_set_volume(tas);
return 1; return 1;
} }
...@@ -294,6 +323,7 @@ static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol, ...@@ -294,6 +323,7 @@ static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol,
tas->mixer_l[idx] = ucontrol->value.integer.value[0]; tas->mixer_l[idx] = ucontrol->value.integer.value[0];
tas->mixer_r[idx] = ucontrol->value.integer.value[1]; tas->mixer_r[idx] = ucontrol->value.integer.value[1];
if (tas->hw_enabled)
tas_set_mixer(tas); tas_set_mixer(tas);
return 1; return 1;
} }
...@@ -346,6 +376,7 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol, ...@@ -346,6 +376,7 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
tas->acr |= TAS_ACR_INPUT_B; tas->acr |= TAS_ACR_INPUT_B;
if (oldacr == tas->acr) if (oldacr == tas->acr)
return 0; return 0;
if (tas->hw_enabled)
tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
return 1; return 1;
} }
...@@ -399,26 +430,66 @@ static int tas_usable(struct codec_info_item *cii, ...@@ -399,26 +430,66 @@ static int tas_usable(struct codec_info_item *cii,
static int tas_reset_init(struct tas *tas) static int tas_reset_init(struct tas *tas)
{ {
u8 tmp; 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); 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); 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); tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
msleep(1); msleep(10);
tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
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;
tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT; tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT;
if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp)) if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp))
return -ENODEV; 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; tmp = 0;
if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp)) if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp))
return -ENODEV; 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; return 0;
} }
...@@ -427,6 +498,7 @@ static int tas_reset_init(struct tas *tas) ...@@ -427,6 +498,7 @@ static int tas_reset_init(struct tas *tas)
* our i2c device is suspended, and then take note of that! */ * our i2c device is suspended, and then take note of that! */
static int tas_suspend(struct tas *tas) static int tas_suspend(struct tas *tas)
{ {
tas->hw_enabled = 0;
tas->acr |= TAS_ACR_ANALOG_PDOWN; tas->acr |= TAS_ACR_ANALOG_PDOWN;
tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
return 0; return 0;
...@@ -438,6 +510,7 @@ static int tas_resume(struct tas *tas) ...@@ -438,6 +510,7 @@ static int tas_resume(struct tas *tas)
tas_reset_init(tas); tas_reset_init(tas);
tas_set_volume(tas); tas_set_volume(tas);
tas_set_mixer(tas); tas_set_mixer(tas);
tas->hw_enabled = 1;
return 0; return 0;
} }
...@@ -463,6 +536,7 @@ static struct codec_info tas_codec_info = { ...@@ -463,6 +536,7 @@ static struct codec_info tas_codec_info = {
.bus_factor = 64, .bus_factor = 64,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.usable = tas_usable, .usable = tas_usable,
.switch_clock = tas_switch_clock,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.suspend = _tas_suspend, .suspend = _tas_suspend,
.resume = _tas_resume, .resume = _tas_resume,
...@@ -483,6 +557,7 @@ static int tas_init_codec(struct aoa_codec *codec) ...@@ -483,6 +557,7 @@ static int tas_init_codec(struct aoa_codec *codec)
printk(KERN_ERR PFX "tas failed to initialise\n"); printk(KERN_ERR PFX "tas failed to initialise\n");
return -ENXIO; return -ENXIO;
} }
tas->hw_enabled = 1;
if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev, if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev,
aoa_get_card(), aoa_get_card(),
...@@ -548,6 +623,7 @@ static int tas_create(struct i2c_adapter *adapter, ...@@ -548,6 +623,7 @@ static int tas_create(struct i2c_adapter *adapter,
tas->i2c.driver = &tas_driver; tas->i2c.driver = &tas_driver;
tas->i2c.adapter = adapter; tas->i2c.adapter = adapter;
tas->i2c.addr = addr; tas->i2c.addr = addr;
tas->drc_range = TAS3004_DRC_MAX;
strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE-1); strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE-1);
if (i2c_attach_client(&tas->i2c)) { if (i2c_attach_client(&tas->i2c)) {
...@@ -564,7 +640,9 @@ static int tas_create(struct i2c_adapter *adapter, ...@@ -564,7 +640,9 @@ static int tas_create(struct i2c_adapter *adapter,
if (aoa_codec_register(&tas->codec)) { if (aoa_codec_register(&tas->codec)) {
goto detach; 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; return 0;
detach: detach:
i2c_detach_client(&tas->i2c); i2c_detach_client(&tas->i2c);
......
...@@ -44,4 +44,12 @@ ...@@ -44,4 +44,12 @@
#define TAS_REG_LEFT_BIQUAD6 0x10 #define TAS_REG_LEFT_BIQUAD6 0x10
#define TAS_REG_RIGHT_BIQUAD6 0x19 #define TAS_REG_RIGHT_BIQUAD6 0x19
#define TAS_REG_LEFT_LOUDNESS 0x21
#define TAS_REG_RIGHT_LOUDNESS 0x22
#define TAS_REG_LEFT_LOUDNESS_GAIN 0x23
#define TAS_REG_RIGHT_LOUDNESS_GAIN 0x24
#define TAS3001_DRC_MAX 0x5f
#define TAS3004_DRC_MAX 0xef
#endif /* __SND_AOA_CODECTASH */ #endif /* __SND_AOA_CODECTASH */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment