diff options
Diffstat (limited to 'sound/aoa/codecs')
-rw-r--r-- | sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h | 134 | ||||
-rw-r--r-- | sound/aoa/codecs/snd-aoa-codec-tas.c | 331 | ||||
-rw-r--r-- | sound/aoa/codecs/snd-aoa-codec-tas.h | 8 |
3 files changed, 448 insertions, 25 deletions
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h b/sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h new file mode 100644 index 000000000000..69b61136fd54 --- /dev/null +++ b/sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h @@ -0,0 +1,134 @@ +/* + * This file is only included exactly once! + * + * The tables here are derived from the tas3004 datasheet, + * modulo typo corrections and some smoothing... + */ + +#define TAS3004_TREBLE_MIN 0 +#define TAS3004_TREBLE_MAX 72 +#define TAS3004_BASS_MIN 0 +#define TAS3004_BASS_MAX 72 +#define TAS3004_TREBLE_ZERO 36 +#define TAS3004_BASS_ZERO 36 + +static u8 tas3004_treble_table[] = { + 150, /* -18 dB */ + 149, + 148, + 147, + 146, + 145, + 144, + 143, + 142, + 141, + 140, + 139, + 138, + 137, + 136, + 135, + 134, + 133, + 132, + 131, + 130, + 129, + 128, + 127, + 126, + 125, + 124, + 123, + 122, + 121, + 120, + 119, + 118, + 117, + 116, + 115, + 114, /* 0 dB */ + 113, + 112, + 111, + 109, + 108, + 107, + 105, + 104, + 103, + 101, + 99, + 98, + 96, + 93, + 91, + 89, + 86, + 83, + 81, + 77, + 74, + 71, + 67, + 63, + 59, + 54, + 49, + 44, + 38, + 32, + 26, + 19, + 10, + 4, + 2, + 1, /* +18 dB */ +}; + +static inline u8 tas3004_treble(int idx) +{ + return tas3004_treble_table[idx]; +} + +/* I only save the difference here to the treble table + * so that the binary is smaller... + * I have also ignored completely differences of + * +/- 1 + */ +static s8 tas3004_bass_diff_to_treble[] = { + 2, /* 7 dB, offset 50 */ + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 3, + 4, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 14, + 13, + 8, + 1, /* 18 dB */ +}; + +static inline u8 tas3004_bass(int idx) +{ + u8 result = tas3004_treble_table[idx]; + + if (idx >= 50) + result += tas3004_bass_diff_to_treble[idx-50]; + return result; +} diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/snd-aoa-codec-tas.c index 2e39ff6ee349..16c0b6b0a805 100644 --- a/sound/aoa/codecs/snd-aoa-codec-tas.c +++ b/sound/aoa/codecs/snd-aoa-codec-tas.c @@ -72,22 +72,29 @@ MODULE_DESCRIPTION("tas codec driver for snd-aoa"); #include "snd-aoa-codec-tas.h" #include "snd-aoa-codec-tas-gain-table.h" +#include "snd-aoa-codec-tas-basstreble.h" #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 bass, treble; 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 +108,44 @@ 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_treble(struct tas *tas) +{ + u8 tmp; + + tmp = tas3004_treble(tas->treble); + tas_write_reg(tas, TAS_REG_TREBLE, 1, &tmp); +} + +static void tas_set_bass(struct tas *tas) +{ + u8 tmp; + + tmp = tas3004_bass(tas->bass); + tas_write_reg(tas, TAS_REG_BASS, 1, &tmp); +} + static void tas_set_volume(struct tas *tas) { u8 block[6]; @@ -113,8 +158,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 +247,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 +276,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 +286,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 +341,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; } @@ -309,9 +357,93 @@ static struct snd_kcontrol_new n##_control = { \ .private_value = idx, \ } -MIXER_CONTROL(pcm1, "PCM1", 0); +MIXER_CONTROL(pcm1, "PCM", 0); MIXER_CONTROL(monitor, "Monitor", 2); +static int tas_snd_drc_range_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = TAS3004_DRC_MAX; + return 0; +} + +static int tas_snd_drc_range_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tas->drc_range; + return 0; +} + +static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + + if (tas->drc_range == ucontrol->value.integer.value[0]) + return 0; + + tas->drc_range = ucontrol->value.integer.value[0]; + if (tas->hw_enabled) + tas3004_set_drc(tas); + return 1; +} + +static struct snd_kcontrol_new drc_range_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DRC Range", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = tas_snd_drc_range_info, + .get = tas_snd_drc_range_get, + .put = tas_snd_drc_range_put, +}; + +static int tas_snd_drc_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int tas_snd_drc_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tas->drc_enabled; + return 0; +} + +static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + + if (tas->drc_enabled == ucontrol->value.integer.value[0]) + return 0; + + tas->drc_enabled = ucontrol->value.integer.value[0]; + if (tas->hw_enabled) + tas3004_set_drc(tas); + return 1; +} + +static struct snd_kcontrol_new drc_switch_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DRC Range Switch", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = tas_snd_drc_switch_info, + .get = tas_snd_drc_switch_get, + .put = tas_snd_drc_switch_put, +}; + static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -346,7 +478,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; } @@ -370,6 +503,89 @@ static struct snd_kcontrol_new capture_source_control = { .put = tas_snd_capture_source_put, }; +static int tas_snd_treble_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = TAS3004_TREBLE_MIN; + uinfo->value.integer.max = TAS3004_TREBLE_MAX; + return 0; +} + +static int tas_snd_treble_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tas->treble; + return 0; +} + +static int tas_snd_treble_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + + if (tas->treble == ucontrol->value.integer.value[0]) + return 0; + + tas->treble = ucontrol->value.integer.value[0]; + if (tas->hw_enabled) + tas_set_treble(tas); + return 1; +} + +static struct snd_kcontrol_new treble_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Treble", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = tas_snd_treble_info, + .get = tas_snd_treble_get, + .put = tas_snd_treble_put, +}; + +static int tas_snd_bass_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = TAS3004_BASS_MIN; + uinfo->value.integer.max = TAS3004_BASS_MAX; + return 0; +} + +static int tas_snd_bass_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tas->bass; + return 0; +} + +static int tas_snd_bass_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + + if (tas->bass == ucontrol->value.integer.value[0]) + return 0; + + tas->bass = ucontrol->value.integer.value[0]; + if (tas->hw_enabled) + tas_set_bass(tas); + return 1; +} + +static struct snd_kcontrol_new bass_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Bass", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = tas_snd_bass_info, + .get = tas_snd_bass_get, + .put = tas_snd_bass_put, +}; static struct transfer_info tas_transfers[] = { { @@ -399,26 +615,67 @@ 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 */ + tas->treble = TAS3004_TREBLE_ZERO; + tas->bass = TAS3004_BASS_ZERO; + tas_set_treble(tas); + tas_set_bass(tas); + + 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 +684,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 +696,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 +722,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 +743,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(), @@ -515,6 +776,22 @@ static int tas_init_codec(struct aoa_codec *codec) if (err) goto error; + err = aoa_snd_ctl_add(snd_ctl_new1(&drc_range_control, tas)); + if (err) + goto error; + + err = aoa_snd_ctl_add(snd_ctl_new1(&drc_switch_control, tas)); + if (err) + goto error; + + err = aoa_snd_ctl_add(snd_ctl_new1(&treble_control, tas)); + if (err) + goto error; + + err = aoa_snd_ctl_add(snd_ctl_new1(&bass_control, tas)); + if (err) + goto error; + return 0; error: tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas); @@ -548,6 +825,8 @@ static int tas_create(struct i2c_adapter *adapter, tas->i2c.driver = &tas_driver; tas->i2c.adapter = adapter; tas->i2c.addr = addr; + /* seems that half is a saner default */ + tas->drc_range = TAS3004_DRC_MAX / 2; strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE-1); if (i2c_attach_client(&tas->i2c)) { @@ -564,7 +843,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); diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.h b/sound/aoa/codecs/snd-aoa-codec-tas.h index daf81f45d83a..ae177e3466e6 100644 --- a/sound/aoa/codecs/snd-aoa-codec-tas.h +++ b/sound/aoa/codecs/snd-aoa-codec-tas.h @@ -44,4 +44,12 @@ #define TAS_REG_LEFT_BIQUAD6 0x10 #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 */ |