diff options
Diffstat (limited to 'sound/aoa/codecs')
| -rw-r--r-- | sound/aoa/codecs/Kconfig | 16 | ||||
| -rw-r--r-- | sound/aoa/codecs/Makefile | 4 | ||||
| -rw-r--r-- | sound/aoa/codecs/onyx.c (renamed from sound/aoa/codecs/snd-aoa-codec-onyx.c) | 161 | ||||
| -rw-r--r-- | sound/aoa/codecs/onyx.h (renamed from sound/aoa/codecs/snd-aoa-codec-onyx.h) | 1 | ||||
| -rw-r--r-- | sound/aoa/codecs/tas-basstreble.h | 134 | ||||
| -rw-r--r-- | sound/aoa/codecs/tas-gain-table.h (renamed from sound/aoa/codecs/snd-aoa-codec-tas-gain-table.h) | 0 | ||||
| -rw-r--r-- | sound/aoa/codecs/tas.c (renamed from sound/aoa/codecs/snd-aoa-codec-tas.c) | 567 | ||||
| -rw-r--r-- | sound/aoa/codecs/tas.h (renamed from sound/aoa/codecs/snd-aoa-codec-tas.h) | 8 | ||||
| -rw-r--r-- | sound/aoa/codecs/toonie.c (renamed from sound/aoa/codecs/snd-aoa-codec-toonie.c) | 22 |
9 files changed, 657 insertions, 256 deletions
diff --git a/sound/aoa/codecs/Kconfig b/sound/aoa/codecs/Kconfig index 90cf58f6863..0c68e32834c 100644 --- a/sound/aoa/codecs/Kconfig +++ b/sound/aoa/codecs/Kconfig @@ -1,23 +1,16 @@ config SND_AOA_ONYX tristate "support Onyx chip" - depends on SND_AOA + select I2C + select I2C_POWERMAC ---help--- This option enables support for the Onyx (pcm3052) codec chip found in the latest Apple machines (most of those with digital audio output). -#config SND_AOA_TOPAZ -# tristate "support Topaz chips" -# depends on SND_AOA -# ---help--- -# This option enables support for the Topaz (CS84xx) -# codec chips found in the latest Apple machines, -# these chips do the digital input and output on -# some PowerMacs. - config SND_AOA_TAS tristate "support TAS chips" - depends on SND_AOA + select I2C + select I2C_POWERMAC ---help--- This option enables support for the tas chips found in a lot of Apple Machines, especially @@ -25,7 +18,6 @@ config SND_AOA_TAS config SND_AOA_TOONIE tristate "support Toonie chip" - depends on SND_AOA ---help--- This option enables support for the toonie codec found in the Mac Mini. If you have a Mac Mini and diff --git a/sound/aoa/codecs/Makefile b/sound/aoa/codecs/Makefile index 31cbe68fd42..c3ee77fc4b2 100644 --- a/sound/aoa/codecs/Makefile +++ b/sound/aoa/codecs/Makefile @@ -1,3 +1,7 @@ +snd-aoa-codec-onyx-objs := onyx.o +snd-aoa-codec-tas-objs := tas.o +snd-aoa-codec-toonie-objs := toonie.o + obj-$(CONFIG_SND_AOA_ONYX) += snd-aoa-codec-onyx.o obj-$(CONFIG_SND_AOA_TAS) += snd-aoa-codec-tas.o obj-$(CONFIG_SND_AOA_TOONIE) += snd-aoa-codec-toonie.o diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.c b/sound/aoa/codecs/onyx.c index 0b7650788f1..401107b85d3 100644 --- a/sound/aoa/codecs/snd-aoa-codec-onyx.c +++ b/sound/aoa/codecs/onyx.c @@ -33,11 +33,12 @@ */ #include <linux/delay.h> #include <linux/module.h> +#include <linux/slab.h> MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa"); -#include "snd-aoa-codec-onyx.h" +#include "onyx.h" #include "../aoa.h" #include "../soundbus/soundbus.h" @@ -47,7 +48,7 @@ MODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa"); struct onyx { /* cache registers 65 to 80, they are write-only! */ u8 cache[16]; - struct i2c_client i2c; + struct i2c_client *i2c; struct aoa_codec codec; u32 initialised:1, spdif_locked:1, @@ -72,7 +73,7 @@ static int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value) *value = onyx->cache[reg-FIRSTREGISTER]; return 0; } - v = i2c_smbus_read_byte_data(&onyx->i2c, reg); + v = i2c_smbus_read_byte_data(onyx->i2c, reg); if (v < 0) return -1; *value = (u8)v; @@ -84,7 +85,7 @@ static int onyx_write_register(struct onyx *onyx, u8 reg, u8 value) { int result; - result = i2c_smbus_write_byte_data(&onyx->i2c, reg, value); + result = i2c_smbus_write_byte_data(onyx->i2c, reg, value); if (!result) onyx->cache[reg-FIRSTREGISTER] = value; return result; @@ -138,6 +139,13 @@ static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol, struct onyx *onyx = snd_kcontrol_chip(kcontrol); s8 l, r; + if (ucontrol->value.integer.value[0] < -128 + VOLUME_RANGE_SHIFT || + ucontrol->value.integer.value[0] > -1 + VOLUME_RANGE_SHIFT) + return -EINVAL; + if (ucontrol->value.integer.value[1] < -128 + VOLUME_RANGE_SHIFT || + ucontrol->value.integer.value[1] > -1 + VOLUME_RANGE_SHIFT) + return -EINVAL; + mutex_lock(&onyx->mutex); onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l); onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r); @@ -206,6 +214,9 @@ static int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol, struct onyx *onyx = snd_kcontrol_chip(kcontrol); u8 v, n; + if (ucontrol->value.integer.value[0] < 3 + INPUTGAIN_RANGE_SHIFT || + ucontrol->value.integer.value[0] > 28 + INPUTGAIN_RANGE_SHIFT) + return -EINVAL; mutex_lock(&onyx->mutex); onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); n = v; @@ -230,7 +241,7 @@ static struct snd_kcontrol_new inputgain_control = { static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Line-In", "Microphone" }; + static const char * const texts[] = { "Line-In", "Microphone" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -272,6 +283,8 @@ static void onyx_set_capture_source(struct onyx *onyx, int mic) static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + if (ucontrol->value.enumerated.item[0] > 1) + return -EINVAL; onyx_set_capture_source(snd_kcontrol_chip(kcontrol), ucontrol->value.enumerated.item[0]); return 1; @@ -280,7 +293,7 @@ static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol, static struct snd_kcontrol_new capture_source_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* If we name this 'Input Source', it properly shows up in - * alsamixer as a selection, * but it's shown under the + * alsamixer as a selection, * but it's shown under the * 'Playback' category. * If I name it 'Capture Source', it shows up in strange * ways (two bools of which one can be selected at a @@ -297,15 +310,7 @@ static struct snd_kcontrol_new capture_source_control = { .put = onyx_snd_capture_source_put, }; -static int onyx_snd_mute_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define onyx_snd_mute_info snd_ctl_boolean_stereo_info static int onyx_snd_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -359,15 +364,7 @@ static struct snd_kcontrol_new mute_control = { }; -static int onyx_snd_single_bit_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; -} +#define onyx_snd_single_bit_info snd_ctl_boolean_mono_info #define FLAG_POLARITY_INVERT 1 #define FLAG_SPDIFLOCK 2 @@ -481,7 +478,7 @@ static int onyx_spdif_mask_get(struct snd_kcontrol *kcontrol, ucontrol->value.iec958.status[3] = 0x3f; ucontrol->value.iec958.status[4] = 0x0f; - + return 0; } @@ -661,7 +658,7 @@ static struct transfer_info onyx_transfers[] = { .tag = 2, }, #ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE -Once alsa gets supports for this kind of thing we can add it... + /* Once alsa gets supports for this kind of thing we can add it... */ { /* digital compressed output */ .formats = SNDRV_PCM_FMTBIT_COMPRESSED_16BE, @@ -686,7 +683,7 @@ static int onyx_usable(struct codec_info_item *cii, onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); spdif_enabled = !!(v & ONYX_SPDIF_ENABLE); onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); - analog_enabled = + analog_enabled = (v & (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT)) != (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT); mutex_unlock(&onyx->mutex); @@ -713,7 +710,7 @@ static int onyx_prepare(struct codec_info_item *cii, if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) { /* mute and lock analog output */ onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); - if (onyx_write_register(onyx + if (onyx_write_register(onyx, ONYX_REG_DAC_CONTROL, v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT)) goto out_unlock; @@ -825,7 +822,16 @@ static int onyx_resume(struct codec_info_item *cii) int err = -ENXIO; mutex_lock(&onyx->mutex); - /* take codec out of suspend */ + + /* reset codec */ + onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); + msleep(1); + onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1); + msleep(1); + onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); + msleep(1); + + /* take codec out of suspend (if it still is after reset) */ if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v)) goto out_unlock; onyx_write_register(onyx, ONYX_REG_CONTROL, v & ~(ONYX_ADPSV | ONYX_DAPSV)); @@ -877,13 +883,13 @@ static int onyx_init_codec(struct aoa_codec *codec) msleep(1); onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); msleep(1); - + if (onyx_register_init(onyx)) { printk(KERN_ERR PFX "failed to initialise onyx registers\n"); return -ENODEV; } - if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, onyx, &ops)) { + if (aoa_snd_device_new(SNDRV_DEV_CODEC, onyx, &ops)) { printk(KERN_ERR PFX "failed to create onyx snd device!\n"); return -ENODEV; } @@ -991,12 +997,10 @@ static void onyx_exit_codec(struct aoa_codec *codec) onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx); } -static struct i2c_driver onyx_driver; - -static int onyx_create(struct i2c_adapter *adapter, - struct device_node *node, - int addr) +static int onyx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) { + struct device_node *node = client->dev.of_node; struct onyx *onyx; u8 dummy; @@ -1006,108 +1010,57 @@ static int onyx_create(struct i2c_adapter *adapter, return -ENOMEM; mutex_init(&onyx->mutex); - onyx->i2c.driver = &onyx_driver; - onyx->i2c.adapter = adapter; - onyx->i2c.addr = addr & 0x7f; - strlcpy(onyx->i2c.name, "onyx audio codec", I2C_NAME_SIZE-1); - - if (i2c_attach_client(&onyx->i2c)) { - printk(KERN_ERR PFX "failed to attach to i2c\n"); - goto fail; - } + onyx->i2c = client; + i2c_set_clientdata(client, onyx); /* we try to read from register ONYX_REG_CONTROL * to check if the codec is present */ if (onyx_read_register(onyx, ONYX_REG_CONTROL, &dummy) != 0) { - i2c_detach_client(&onyx->i2c); printk(KERN_ERR PFX "failed to read control register\n"); goto fail; } - strlcpy(onyx->codec.name, "onyx", MAX_CODEC_NAME_LEN-1); + strlcpy(onyx->codec.name, "onyx", MAX_CODEC_NAME_LEN); onyx->codec.owner = THIS_MODULE; onyx->codec.init = onyx_init_codec; onyx->codec.exit = onyx_exit_codec; onyx->codec.node = of_node_get(node); if (aoa_codec_register(&onyx->codec)) { - i2c_detach_client(&onyx->i2c); goto fail; } printk(KERN_DEBUG PFX "created and attached onyx instance\n"); return 0; fail: kfree(onyx); - return -EINVAL; + return -ENODEV; } -static int onyx_i2c_attach(struct i2c_adapter *adapter) +static int onyx_i2c_remove(struct i2c_client *client) { - struct device_node *busnode, *dev = NULL; - struct pmac_i2c_bus *bus; + struct onyx *onyx = i2c_get_clientdata(client); - bus = pmac_i2c_adapter_to_bus(adapter); - if (bus == NULL) - return -ENODEV; - busnode = pmac_i2c_get_bus_node(bus); - - while ((dev = of_get_next_child(busnode, dev)) != NULL) { - if (device_is_compatible(dev, "pcm3052")) { - u32 *addr; - printk(KERN_DEBUG PFX "found pcm3052\n"); - addr = (u32 *) get_property(dev, "reg", NULL); - if (!addr) - return -ENODEV; - return onyx_create(adapter, dev, (*addr)>>1); - } - } - - /* if that didn't work, try desperate mode for older - * machines that have stuff missing from the device tree */ - - if (!device_is_compatible(busnode, "k2-i2c")) - return -ENODEV; - - printk(KERN_DEBUG PFX "found k2-i2c, checking if onyx chip is on it\n"); - /* probe both possible addresses for the onyx chip */ - if (onyx_create(adapter, NULL, 0x46) == 0) - return 0; - return onyx_create(adapter, NULL, 0x47); -} - -static int onyx_i2c_detach(struct i2c_client *client) -{ - struct onyx *onyx = container_of(client, struct onyx, i2c); - int err; - - if ((err = i2c_detach_client(client))) - return err; aoa_codec_unregister(&onyx->codec); of_node_put(onyx->codec.node); - if (onyx->codec_info) - kfree(onyx->codec_info); + kfree(onyx->codec_info); kfree(onyx); return 0; } +static const struct i2c_device_id onyx_i2c_id[] = { + { "MAC,pcm3052", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c,onyx_i2c_id); + static struct i2c_driver onyx_driver = { .driver = { .name = "aoa_codec_onyx", .owner = THIS_MODULE, }, - .attach_adapter = onyx_i2c_attach, - .detach_client = onyx_i2c_detach, + .probe = onyx_i2c_probe, + .remove = onyx_i2c_remove, + .id_table = onyx_i2c_id, }; -static int __init onyx_init(void) -{ - return i2c_add_driver(&onyx_driver); -} - -static void __exit onyx_exit(void) -{ - i2c_del_driver(&onyx_driver); -} - -module_init(onyx_init); -module_exit(onyx_exit); +module_i2c_driver(onyx_driver); diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.h b/sound/aoa/codecs/onyx.h index aeedda77369..ffd20254ff7 100644 --- a/sound/aoa/codecs/snd-aoa-codec-onyx.h +++ b/sound/aoa/codecs/onyx.h @@ -9,7 +9,6 @@ #define __SND_AOA_CODEC_ONYX_H #include <stddef.h> #include <linux/i2c.h> -#include <linux/i2c-dev.h> #include <asm/pmac_low_i2c.h> #include <asm/prom.h> diff --git a/sound/aoa/codecs/tas-basstreble.h b/sound/aoa/codecs/tas-basstreble.h new file mode 100644 index 00000000000..69b61136fd5 --- /dev/null +++ b/sound/aoa/codecs/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-gain-table.h b/sound/aoa/codecs/tas-gain-table.h index 4cfa6757715..4cfa6757715 100644 --- a/sound/aoa/codecs/snd-aoa-codec-tas-gain-table.h +++ b/sound/aoa/codecs/tas-gain-table.h diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/tas.c index 2e39ff6ee34..cf3c6303b7e 100644 --- a/sound/aoa/codecs/snd-aoa-codec-tas.c +++ b/sound/aoa/codecs/tas.c @@ -61,33 +61,46 @@ */ #include <stddef.h> #include <linux/i2c.h> -#include <linux/i2c-dev.h> #include <asm/pmac_low_i2c.h> #include <asm/prom.h> #include <linux/delay.h> #include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> + MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("tas codec driver for snd-aoa"); -#include "snd-aoa-codec-tas.h" -#include "snd-aoa-codec-tas-gain-table.h" +#include "tas.h" +#include "tas-gain-table.h" +#include "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; + struct i2c_client *i2c; + 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; + /* protects hardware access against concurrency from + * userspace when hitting controls and during + * codec init/suspend/resume */ + struct mutex mtx; }; +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); @@ -96,9 +109,47 @@ static struct tas *codec_to_tas(struct aoa_codec *codec) static inline int tas_write_reg(struct tas *tas, u8 reg, u8 len, u8 *data) { if (len == 1) - return i2c_smbus_write_byte_data(&tas->i2c, reg, *data); + return i2c_smbus_write_byte_data(tas->i2c, reg, *data); else - 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_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) @@ -113,8 +164,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 @@ -186,8 +237,10 @@ static int tas_snd_vol_get(struct snd_kcontrol *kcontrol, { struct tas *tas = snd_kcontrol_chip(kcontrol); + mutex_lock(&tas->mtx); ucontrol->value.integer.value[0] = tas->cached_volume_l; ucontrol->value.integer.value[1] = tas->cached_volume_r; + mutex_unlock(&tas->mtx); return 0; } @@ -196,13 +249,25 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol, { struct tas *tas = snd_kcontrol_chip(kcontrol); + if (ucontrol->value.integer.value[0] < 0 || + ucontrol->value.integer.value[0] > 177) + return -EINVAL; + if (ucontrol->value.integer.value[1] < 0 || + ucontrol->value.integer.value[1] > 177) + return -EINVAL; + + mutex_lock(&tas->mtx); if (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]) { + mutex_unlock(&tas->mtx); return 0; + } 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); + mutex_unlock(&tas->mtx); return 1; } @@ -215,23 +280,17 @@ static struct snd_kcontrol_new volume_control = { .put = tas_snd_vol_put, }; -static int tas_snd_mute_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define tas_snd_mute_info snd_ctl_boolean_stereo_info static int tas_snd_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct tas *tas = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = !tas->muted_l; - ucontrol->value.integer.value[1] = !tas->muted_r; + mutex_lock(&tas->mtx); + ucontrol->value.integer.value[0] = !tas->mute_l; + ucontrol->value.integer.value[1] = !tas->mute_r; + mutex_unlock(&tas->mtx); return 0; } @@ -240,13 +299,18 @@ 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]) + mutex_lock(&tas->mtx); + if (tas->mute_l == !ucontrol->value.integer.value[0] + && tas->mute_r == !ucontrol->value.integer.value[1]) { + mutex_unlock(&tas->mtx); 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); + mutex_unlock(&tas->mtx); return 1; } @@ -275,8 +339,10 @@ static int tas_snd_mixer_get(struct snd_kcontrol *kcontrol, struct tas *tas = snd_kcontrol_chip(kcontrol); int idx = kcontrol->private_value; + mutex_lock(&tas->mtx); ucontrol->value.integer.value[0] = tas->mixer_l[idx]; ucontrol->value.integer.value[1] = tas->mixer_r[idx]; + mutex_unlock(&tas->mtx); return 0; } @@ -287,14 +353,19 @@ static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol, struct tas *tas = snd_kcontrol_chip(kcontrol); int idx = kcontrol->private_value; + mutex_lock(&tas->mtx); if (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]) { + mutex_unlock(&tas->mtx); return 0; + } 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); + mutex_unlock(&tas->mtx); return 1; } @@ -309,9 +380,101 @@ 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); + + mutex_lock(&tas->mtx); + ucontrol->value.integer.value[0] = tas->drc_range; + mutex_unlock(&tas->mtx); + 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 (ucontrol->value.integer.value[0] < 0 || + ucontrol->value.integer.value[0] > TAS3004_DRC_MAX) + return -EINVAL; + + mutex_lock(&tas->mtx); + if (tas->drc_range == ucontrol->value.integer.value[0]) { + mutex_unlock(&tas->mtx); + return 0; + } + + tas->drc_range = ucontrol->value.integer.value[0]; + if (tas->hw_enabled) + tas3004_set_drc(tas); + mutex_unlock(&tas->mtx); + 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, +}; + +#define tas_snd_drc_switch_info snd_ctl_boolean_mono_info + +static int tas_snd_drc_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + + mutex_lock(&tas->mtx); + ucontrol->value.integer.value[0] = tas->drc_enabled; + mutex_unlock(&tas->mtx); + 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); + + mutex_lock(&tas->mtx); + if (tas->drc_enabled == ucontrol->value.integer.value[0]) { + mutex_unlock(&tas->mtx); + return 0; + } + + tas->drc_enabled = !!ucontrol->value.integer.value[0]; + if (tas->hw_enabled) + tas3004_set_drc(tas); + mutex_unlock(&tas->mtx); + 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) { @@ -331,7 +494,9 @@ static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol, { struct tas *tas = snd_kcontrol_chip(kcontrol); + mutex_lock(&tas->mtx); ucontrol->value.enumerated.item[0] = !!(tas->acr & TAS_ACR_INPUT_B); + mutex_unlock(&tas->mtx); return 0; } @@ -339,14 +504,29 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct tas *tas = snd_kcontrol_chip(kcontrol); - int oldacr = tas->acr; + int oldacr; + + if (ucontrol->value.enumerated.item[0] > 1) + return -EINVAL; + mutex_lock(&tas->mtx); + oldacr = tas->acr; - tas->acr &= ~TAS_ACR_INPUT_B; + /* + * Despite what the data sheet says in one place, the + * TAS_ACR_B_MONAUREAL bit forces mono output even when + * input A (line in) is selected. + */ + tas->acr &= ~(TAS_ACR_INPUT_B | TAS_ACR_B_MONAUREAL); if (ucontrol->value.enumerated.item[0]) - tas->acr |= TAS_ACR_INPUT_B; - if (oldacr == tas->acr) + tas->acr |= TAS_ACR_INPUT_B | TAS_ACR_B_MONAUREAL | + TAS_ACR_B_MON_SEL_RIGHT; + if (oldacr == tas->acr) { + mutex_unlock(&tas->mtx); 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); + mutex_unlock(&tas->mtx); return 1; } @@ -370,19 +550,118 @@ 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); + + mutex_lock(&tas->mtx); + ucontrol->value.integer.value[0] = tas->treble; + mutex_unlock(&tas->mtx); + 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 (ucontrol->value.integer.value[0] < TAS3004_TREBLE_MIN || + ucontrol->value.integer.value[0] > TAS3004_TREBLE_MAX) + return -EINVAL; + mutex_lock(&tas->mtx); + if (tas->treble == ucontrol->value.integer.value[0]) { + mutex_unlock(&tas->mtx); + return 0; + } + + tas->treble = ucontrol->value.integer.value[0]; + if (tas->hw_enabled) + tas_set_treble(tas); + mutex_unlock(&tas->mtx); + 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); + + mutex_lock(&tas->mtx); + ucontrol->value.integer.value[0] = tas->bass; + mutex_unlock(&tas->mtx); + 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 (ucontrol->value.integer.value[0] < TAS3004_BASS_MIN || + ucontrol->value.integer.value[0] > TAS3004_BASS_MAX) + return -EINVAL; + mutex_lock(&tas->mtx); + if (tas->bass == ucontrol->value.integer.value[0]) { + mutex_unlock(&tas->mtx); + return 0; + } + + tas->bass = ucontrol->value.integer.value[0]; + if (tas->hw_enabled) + tas_set_bass(tas); + mutex_unlock(&tas->mtx); + 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[] = { { /* input */ - .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE | - SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE, + .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_BE, .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .transfer_in = 1, }, { /* output */ - .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE | - SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE, + .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_BE, .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .transfer_in = 0, }, @@ -399,49 +678,99 @@ 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; + goto outerr; + + tas->acr |= TAS_ACR_ANALOG_PDOWN; + if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr)) + goto outerr; tmp = 0; if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp)) - return -ENODEV; + goto outerr; + + 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)) + goto outerr; + + return 0; + outerr: + return -ENODEV; +} +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 */ + mutex_lock(&tas->mtx); + 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); + mutex_unlock(&tas->mtx); + break; + default: + /* doesn't happen as of now */ + return -EINVAL; + } return 0; } +#ifdef CONFIG_PM /* we are controlled via i2c and assume that is always up * If that wasn't the case, we'd have to suspend once * our i2c device is suspended, and then take note of that! */ static int tas_suspend(struct tas *tas) { + mutex_lock(&tas->mtx); + tas->hw_enabled = 0; tas->acr |= TAS_ACR_ANALOG_PDOWN; tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); + mutex_unlock(&tas->mtx); return 0; } static int tas_resume(struct tas *tas) { /* reset codec */ + mutex_lock(&tas->mtx); tas_reset_init(tas); tas_set_volume(tas); tas_set_mixer(tas); + tas->hw_enabled = 1; + mutex_unlock(&tas->mtx); return 0; } -#ifdef CONFIG_PM static int _tas_suspend(struct codec_info_item *cii, pm_message_t state) { return tas_suspend(cii->codec_data); @@ -451,7 +780,10 @@ static int _tas_resume(struct codec_info_item *cii) { return tas_resume(cii->codec_data); } -#endif +#else /* CONFIG_PM */ +#define _tas_suspend NULL +#define _tas_resume NULL +#endif /* CONFIG_PM */ static struct codec_info tas_codec_info = { .transfers = tas_transfers, @@ -463,10 +795,9 @@ static struct codec_info tas_codec_info = { .bus_factor = 64, .owner = THIS_MODULE, .usable = tas_usable, -#ifdef CONFIG_PM + .switch_clock = tas_switch_clock, .suspend = _tas_suspend, .resume = _tas_resume, -#endif }; static int tas_init_codec(struct aoa_codec *codec) @@ -479,10 +810,14 @@ static int tas_init_codec(struct aoa_codec *codec) return -EINVAL; } + mutex_lock(&tas->mtx); if (tas_reset_init(tas)) { printk(KERN_ERR PFX "tas failed to initialise\n"); + mutex_unlock(&tas->mtx); return -ENXIO; } + tas->hw_enabled = 1; + mutex_unlock(&tas->mtx); if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev, aoa_get_card(), @@ -491,7 +826,7 @@ static int tas_init_codec(struct aoa_codec *codec) return -ENODEV; } - if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, tas, &ops)) { + if (aoa_snd_device_new(SNDRV_DEV_CODEC, tas, &ops)) { printk(KERN_ERR PFX "failed to create tas snd device!\n"); return -ENODEV; } @@ -515,6 +850,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); @@ -530,14 +881,12 @@ static void tas_exit_codec(struct aoa_codec *codec) return; tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas); } - -static struct i2c_driver tas_driver; -static int tas_create(struct i2c_adapter *adapter, - struct device_node *node, - int addr) +static int tas_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) { + struct device_node *node = client->dev.of_node; struct tas *tas; tas = kzalloc(sizeof(struct tas), GFP_KERNEL); @@ -545,110 +894,62 @@ static int tas_create(struct i2c_adapter *adapter, if (!tas) return -ENOMEM; - tas->i2c.driver = &tas_driver; - tas->i2c.adapter = adapter; - tas->i2c.addr = addr; - strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE-1); + mutex_init(&tas->mtx); + tas->i2c = client; + i2c_set_clientdata(client, tas); - if (i2c_attach_client(&tas->i2c)) { - printk(KERN_ERR PFX "failed to attach to i2c\n"); - goto fail; - } + /* seems that half is a saner default */ + tas->drc_range = TAS3004_DRC_MAX / 2; - strlcpy(tas->codec.name, "tas", MAX_CODEC_NAME_LEN-1); + strlcpy(tas->codec.name, "tas", MAX_CODEC_NAME_LEN); tas->codec.owner = THIS_MODULE; tas->codec.init = tas_init_codec; tas->codec.exit = tas_exit_codec; tas->codec.node = of_node_get(node); if (aoa_codec_register(&tas->codec)) { - goto detach; + goto fail; } - 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", + (unsigned int)client->addr, node->full_name); return 0; - detach: - i2c_detach_client(&tas->i2c); fail: + mutex_destroy(&tas->mtx); kfree(tas); return -EINVAL; } -static int tas_i2c_attach(struct i2c_adapter *adapter) +static int tas_i2c_remove(struct i2c_client *client) { - struct device_node *busnode, *dev = NULL; - struct pmac_i2c_bus *bus; - - bus = pmac_i2c_adapter_to_bus(adapter); - if (bus == NULL) - return -ENODEV; - busnode = pmac_i2c_get_bus_node(bus); - - while ((dev = of_get_next_child(busnode, dev)) != NULL) { - if (device_is_compatible(dev, "tas3004")) { - u32 *addr; - printk(KERN_DEBUG PFX "found tas3004\n"); - addr = (u32 *) get_property(dev, "reg", NULL); - if (!addr) - continue; - return tas_create(adapter, dev, ((*addr) >> 1) & 0x7f); - } - /* older machines have no 'codec' node with a 'compatible' - * property that says 'tas3004', they just have a 'deq' - * node without any such property... */ - if (strcmp(dev->name, "deq") == 0) { - u32 *_addr, addr; - printk(KERN_DEBUG PFX "found 'deq' node\n"); - _addr = (u32 *) get_property(dev, "i2c-address", NULL); - if (!_addr) - continue; - addr = ((*_addr) >> 1) & 0x7f; - /* now, if the address doesn't match any of the two - * that a tas3004 can have, we cannot handle this. - * I doubt it ever happens but hey. */ - if (addr != 0x34 && addr != 0x35) - continue; - return tas_create(adapter, dev, addr); - } - } - return -ENODEV; -} - -static int tas_i2c_detach(struct i2c_client *client) -{ - struct tas *tas = container_of(client, struct tas, i2c); - int err; + struct tas *tas = i2c_get_clientdata(client); u8 tmp = TAS_ACR_ANALOG_PDOWN; - if ((err = i2c_detach_client(client))) - return err; aoa_codec_unregister(&tas->codec); of_node_put(tas->codec.node); /* power down codec chip */ tas_write_reg(tas, TAS_REG_ACR, 1, &tmp); + mutex_destroy(&tas->mtx); kfree(tas); return 0; } +static const struct i2c_device_id tas_i2c_id[] = { + { "MAC,tas3004", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c,tas_i2c_id); + static struct i2c_driver tas_driver = { .driver = { .name = "aoa_codec_tas", .owner = THIS_MODULE, }, - .attach_adapter = tas_i2c_attach, - .detach_client = tas_i2c_detach, + .probe = tas_i2c_probe, + .remove = tas_i2c_remove, + .id_table = tas_i2c_id, }; -static int __init tas_init(void) -{ - return i2c_add_driver(&tas_driver); -} - -static void __exit tas_exit(void) -{ - i2c_del_driver(&tas_driver); -} - -module_init(tas_init); -module_exit(tas_exit); +module_i2c_driver(tas_driver); diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.h b/sound/aoa/codecs/tas.h index daf81f45d83..ae177e3466e 100644 --- a/sound/aoa/codecs/snd-aoa-codec-tas.h +++ b/sound/aoa/codecs/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 */ diff --git a/sound/aoa/codecs/snd-aoa-codec-toonie.c b/sound/aoa/codecs/toonie.c index bcc555647e7..7e8c3417cd8 100644 --- a/sound/aoa/codecs/snd-aoa-codec-toonie.c +++ b/sound/aoa/codecs/toonie.c @@ -11,6 +11,7 @@ */ #include <linux/delay.h> #include <linux/module.h> +#include <linux/slab.h> MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("toonie codec driver for snd-aoa"); @@ -51,6 +52,13 @@ static struct transfer_info toonie_transfers[] = { {} }; +static int toonie_usable(struct codec_info_item *cii, + struct transfer_info *ti, + struct transfer_info *out) +{ + return 1; +} + #ifdef CONFIG_PM static int toonie_suspend(struct codec_info_item *cii, pm_message_t state) { @@ -69,6 +77,7 @@ static struct codec_info toonie_codec_info = { .sysclock_factor = 256, .bus_factor = 64, .owner = THIS_MODULE, + .usable = toonie_usable, #ifdef CONFIG_PM .suspend = toonie_suspend, .resume = toonie_resume, @@ -79,19 +88,20 @@ static int toonie_init_codec(struct aoa_codec *codec) { struct toonie *toonie = codec_to_toonie(codec); - if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, toonie, &ops)) { - printk(KERN_ERR PFX "failed to create toonie snd device!\n"); - return -ENODEV; - } - /* nothing connected? what a joke! */ if (toonie->codec.connected != 1) return -ENOTCONN; + if (aoa_snd_device_new(SNDRV_DEV_CODEC, toonie, &ops)) { + printk(KERN_ERR PFX "failed to create toonie snd device!\n"); + return -ENODEV; + } + if (toonie->codec.soundbus_dev->attach_codec(toonie->codec.soundbus_dev, aoa_get_card(), &toonie_codec_info, toonie)) { printk(KERN_ERR PFX "error creating toonie pcm\n"); + snd_device_free(aoa_get_card(), toonie); return -ENODEV; } @@ -122,7 +132,7 @@ static int __init toonie_init(void) toonie->codec.owner = THIS_MODULE; toonie->codec.init = toonie_init_codec; toonie->codec.exit = toonie_exit_codec; - + if (aoa_codec_register(&toonie->codec)) { kfree(toonie); return -EINVAL; |
