diff options
Diffstat (limited to 'sound/aoa')
| -rw-r--r-- | sound/aoa/Kconfig | 11 | ||||
| -rw-r--r-- | sound/aoa/aoa-gpio.h | 4 | ||||
| -rw-r--r-- | sound/aoa/aoa.h | 6 | ||||
| -rw-r--r-- | sound/aoa/codecs/Kconfig | 12 | ||||
| -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 (renamed from sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h) | 0 | ||||
| -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) | 184 | ||||
| -rw-r--r-- | sound/aoa/codecs/tas.h (renamed from sound/aoa/codecs/snd-aoa-codec-tas.h) | 0 | ||||
| -rw-r--r-- | sound/aoa/codecs/toonie.c (renamed from sound/aoa/codecs/snd-aoa-codec-toonie.c) | 5 | ||||
| -rw-r--r-- | sound/aoa/core/Makefile | 8 | ||||
| -rw-r--r-- | sound/aoa/core/alsa.c (renamed from sound/aoa/core/snd-aoa-alsa.c) | 17 | ||||
| -rw-r--r-- | sound/aoa/core/alsa.h (renamed from sound/aoa/core/snd-aoa-alsa.h) | 2 | ||||
| -rw-r--r-- | sound/aoa/core/core.c (renamed from sound/aoa/core/snd-aoa-core.c) | 6 | ||||
| -rw-r--r-- | sound/aoa/core/gpio-feature.c (renamed from sound/aoa/core/snd-aoa-gpio-feature.c) | 57 | ||||
| -rw-r--r-- | sound/aoa/core/gpio-pmf.c (renamed from sound/aoa/core/snd-aoa-gpio-pmf.c) | 37 | ||||
| -rw-r--r-- | sound/aoa/fabrics/Kconfig | 1 | ||||
| -rw-r--r-- | sound/aoa/fabrics/Makefile | 2 | ||||
| -rw-r--r-- | sound/aoa/fabrics/layout.c (renamed from sound/aoa/fabrics/snd-aoa-fabric-layout.c) | 137 | ||||
| -rw-r--r-- | sound/aoa/soundbus/Kconfig | 1 | ||||
| -rw-r--r-- | sound/aoa/soundbus/core.c | 85 | ||||
| -rw-r--r-- | sound/aoa/soundbus/i2sbus/Makefile | 2 | ||||
| -rw-r--r-- | sound/aoa/soundbus/i2sbus/control.c (renamed from sound/aoa/soundbus/i2sbus/i2sbus-control.c) | 3 | ||||
| -rw-r--r-- | sound/aoa/soundbus/i2sbus/core.c (renamed from sound/aoa/soundbus/i2sbus/i2sbus-core.c) | 110 | ||||
| -rw-r--r-- | sound/aoa/soundbus/i2sbus/i2sbus.h | 12 | ||||
| -rw-r--r-- | sound/aoa/soundbus/i2sbus/interface.h (renamed from sound/aoa/soundbus/i2sbus/i2sbus-interface.h) | 0 | ||||
| -rw-r--r-- | sound/aoa/soundbus/i2sbus/pcm.c (renamed from sound/aoa/soundbus/i2sbus/i2sbus-pcm.c) | 425 | ||||
| -rw-r--r-- | sound/aoa/soundbus/soundbus.h | 6 | ||||
| -rw-r--r-- | sound/aoa/soundbus/sysfs.c | 7 |
31 files changed, 645 insertions, 661 deletions
diff --git a/sound/aoa/Kconfig b/sound/aoa/Kconfig index 5d5813cec4c..c081e18b954 100644 --- a/sound/aoa/Kconfig +++ b/sound/aoa/Kconfig @@ -1,18 +1,17 @@ -menu "Apple Onboard Audio driver" - depends on SND!=n && PPC_PMAC - -config SND_AOA +menuconfig SND_AOA tristate "Apple Onboard Audio driver" - depends on SND + depends on PPC_PMAC select SND_PCM ---help--- This option enables the new driver for the various Apple Onboard Audio components. +if SND_AOA + source "sound/aoa/fabrics/Kconfig" source "sound/aoa/codecs/Kconfig" source "sound/aoa/soundbus/Kconfig" -endmenu +endif # SND_AOA diff --git a/sound/aoa/aoa-gpio.h b/sound/aoa/aoa-gpio.h index 3a61f311557..6065b0344e2 100644 --- a/sound/aoa/aoa-gpio.h +++ b/sound/aoa/aoa-gpio.h @@ -34,10 +34,12 @@ struct gpio_methods { void (*set_headphone)(struct gpio_runtime *rt, int on); void (*set_speakers)(struct gpio_runtime *rt, int on); void (*set_lineout)(struct gpio_runtime *rt, int on); + void (*set_master)(struct gpio_runtime *rt, int on); int (*get_headphone)(struct gpio_runtime *rt); int (*get_speakers)(struct gpio_runtime *rt); int (*get_lineout)(struct gpio_runtime *rt); + int (*get_master)(struct gpio_runtime *rt); void (*set_hw_reset)(struct gpio_runtime *rt, int on); @@ -59,10 +61,10 @@ struct gpio_methods { }; struct gpio_notification { + struct delayed_work work; notify_func_t notify; void *data; void *gpio_private; - struct work_struct work; struct mutex mutex; }; diff --git a/sound/aoa/aoa.h b/sound/aoa/aoa.h index 378ef1e9879..34c668f2779 100644 --- a/sound/aoa/aoa.h +++ b/sound/aoa/aoa.h @@ -10,8 +10,6 @@ #define __AOA_H #include <asm/prom.h> #include <linux/module.h> -/* So apparently there's a reason for requiring driver.h to be included first! */ -#include <sound/driver.h> #include <sound/core.h> #include <sound/asound.h> #include <sound/control.h> @@ -99,7 +97,7 @@ struct aoa_fabric { * that are not assigned yet are passed to the fabric * again for reconsideration. */ extern int -aoa_fabric_register(struct aoa_fabric *fabric); +aoa_fabric_register(struct aoa_fabric *fabric, struct device *dev); /* it is vital to call this when the fabric exits! * When calling, the remove_codec will be called @@ -118,7 +116,7 @@ struct aoa_card { struct snd_card *alsa_card; }; -extern int aoa_snd_device_new(snd_device_type_t type, +extern int aoa_snd_device_new(enum snd_device_type type, void * device_data, struct snd_device_ops * ops); extern struct snd_card *aoa_get_card(void); extern int aoa_snd_ctl_add(struct snd_kcontrol* control); diff --git a/sound/aoa/codecs/Kconfig b/sound/aoa/codecs/Kconfig index d5fbd6016e9..0c68e32834c 100644 --- a/sound/aoa/codecs/Kconfig +++ b/sound/aoa/codecs/Kconfig @@ -1,6 +1,5 @@ config SND_AOA_ONYX tristate "support Onyx chip" - depends on SND_AOA select I2C select I2C_POWERMAC ---help--- @@ -8,18 +7,8 @@ config SND_AOA_ONYX 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--- @@ -29,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/snd-aoa-codec-tas-basstreble.h b/sound/aoa/codecs/tas-basstreble.h index 69b61136fd5..69b61136fd5 100644 --- a/sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h +++ b/sound/aoa/codecs/tas-basstreble.h 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 2ef55a17917..cf3c6303b7e 100644 --- a/sound/aoa/codecs/snd-aoa-codec-tas.c +++ b/sound/aoa/codecs/tas.c @@ -61,20 +61,20 @@ */ #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 "snd-aoa-codec-tas-basstreble.h" +#include "tas.h" +#include "tas-gain-table.h" +#include "tas-basstreble.h" #include "../aoa.h" #include "../soundbus/soundbus.h" @@ -83,7 +83,7 @@ MODULE_DESCRIPTION("tas codec driver for snd-aoa"); struct tas { struct aoa_codec codec; - struct i2c_client i2c; + struct i2c_client *i2c; u32 mute_l:1, mute_r:1 , controls_created:1 , drc_enabled:1, @@ -109,9 +109,9 @@ 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) @@ -249,6 +249,13 @@ 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]) { @@ -273,15 +280,7 @@ 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) @@ -410,6 +409,10 @@ static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol, { 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); @@ -432,15 +435,7 @@ static struct snd_kcontrol_new drc_range_control = { .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; -} +#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) @@ -464,7 +459,7 @@ static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol, return 0; } - tas->drc_enabled = ucontrol->value.integer.value[0]; + tas->drc_enabled = !!ucontrol->value.integer.value[0]; if (tas->hw_enabled) tas3004_set_drc(tas); mutex_unlock(&tas->mtx); @@ -511,12 +506,20 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol, struct tas *tas = snd_kcontrol_chip(kcontrol); 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; + 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; @@ -573,6 +576,9 @@ static int tas_snd_treble_put(struct snd_kcontrol *kcontrol, { 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); @@ -621,6 +627,9 @@ static int tas_snd_bass_put(struct snd_kcontrol *kcontrol, { 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); @@ -646,15 +655,13 @@ static struct snd_kcontrol_new bass_control = { 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, }, @@ -686,8 +693,7 @@ static int tas_reset_init(struct tas *tas) if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp)) goto outerr; - tas->acr |= TAS_ACR_ANALOG_PDOWN | TAS_ACR_B_MONAUREAL | - TAS_ACR_B_MON_SEL_RIGHT; + tas->acr |= TAS_ACR_ANALOG_PDOWN; if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr)) goto outerr; @@ -739,6 +745,7 @@ static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock 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! */ @@ -764,7 +771,6 @@ static int tas_resume(struct tas *tas) return 0; } -#ifdef CONFIG_PM static int _tas_suspend(struct codec_info_item *cii, pm_message_t state) { return tas_suspend(cii->codec_data); @@ -774,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, @@ -787,10 +796,8 @@ static struct codec_info tas_codec_info = { .owner = THIS_MODULE, .usable = tas_usable, .switch_clock = tas_switch_clock, -#ifdef CONFIG_PM .suspend = _tas_suspend, .resume = _tas_resume, -#endif }; static int tas_init_codec(struct aoa_codec *codec) @@ -819,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; } @@ -874,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); @@ -890,87 +895,36 @@ static int tas_create(struct i2c_adapter *adapter, return -ENOMEM; mutex_init(&tas->mtx); - tas->i2c.driver = &tas_driver; - tas->i2c.adapter = adapter; - tas->i2c.addr = addr; + tas->i2c = client; + i2c_set_clientdata(client, tas); + /* 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)) { - printk(KERN_ERR PFX "failed to attach to i2c\n"); - goto fail; - } - 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: tas found, addr 0x%02x on %s\n", - addr, node->full_name); + (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); @@ -982,24 +936,20 @@ static int tas_i2c_detach(struct i2c_client *client) 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 ae177e3466e..ae177e3466e 100644 --- a/sound/aoa/codecs/snd-aoa-codec-tas.h +++ b/sound/aoa/codecs/tas.h diff --git a/sound/aoa/codecs/snd-aoa-codec-toonie.c b/sound/aoa/codecs/toonie.c index 3c7d1d8a9a6..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"); @@ -91,7 +92,7 @@ static int toonie_init_codec(struct aoa_codec *codec) if (toonie->codec.connected != 1) return -ENOTCONN; - if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, toonie, &ops)) { + if (aoa_snd_device_new(SNDRV_DEV_CODEC, toonie, &ops)) { printk(KERN_ERR PFX "failed to create toonie snd device!\n"); return -ENODEV; } @@ -131,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; diff --git a/sound/aoa/core/Makefile b/sound/aoa/core/Makefile index 62dc7287f66..a1596e88c71 100644 --- a/sound/aoa/core/Makefile +++ b/sound/aoa/core/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_SND_AOA) += snd-aoa.o -snd-aoa-objs := snd-aoa-core.o \ - snd-aoa-alsa.o \ - snd-aoa-gpio-pmf.o \ - snd-aoa-gpio-feature.o +snd-aoa-objs := core.o \ + alsa.o \ + gpio-pmf.o \ + gpio-feature.o diff --git a/sound/aoa/core/snd-aoa-alsa.c b/sound/aoa/core/alsa.c index b42fdea77ed..4a7e4e6b746 100644 --- a/sound/aoa/core/snd-aoa-alsa.c +++ b/sound/aoa/core/alsa.c @@ -6,7 +6,7 @@ * GPL v2, can be found in COPYING. */ #include <linux/module.h> -#include "snd-aoa-alsa.h" +#include "alsa.h" static int index = -1; module_param(index, int, 0444); @@ -14,7 +14,7 @@ MODULE_PARM_DESC(index, "index for AOA sound card."); static struct aoa_card *aoa_card; -int aoa_alsa_init(char *name, struct module *mod) +int aoa_alsa_init(char *name, struct module *mod, struct device *dev) { struct snd_card *alsa_card; int err; @@ -23,9 +23,10 @@ int aoa_alsa_init(char *name, struct module *mod) /* cannot be EEXIST due to usage in aoa_fabric_register */ return -EBUSY; - alsa_card = snd_card_new(index, name, mod, sizeof(struct aoa_card)); - if (!alsa_card) - return -ENOMEM; + err = snd_card_new(dev, index, name, mod, sizeof(struct aoa_card), + &alsa_card); + if (err < 0) + return err; aoa_card = alsa_card->private_data; aoa_card->alsa_card = alsa_card; strlcpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver)); @@ -58,12 +59,12 @@ void aoa_alsa_cleanup(void) } } -int aoa_snd_device_new(snd_device_type_t type, - void * device_data, struct snd_device_ops * ops) +int aoa_snd_device_new(enum snd_device_type type, + void * device_data, struct snd_device_ops * ops) { struct snd_card *card = aoa_get_card(); int err; - + if (!card) return -ENOMEM; err = snd_device_new(card, type, device_data, ops); diff --git a/sound/aoa/core/snd-aoa-alsa.h b/sound/aoa/core/alsa.h index 660d2f1793b..9669e4489ca 100644 --- a/sound/aoa/core/snd-aoa-alsa.h +++ b/sound/aoa/core/alsa.h @@ -10,7 +10,7 @@ #define __SND_AOA_ALSA_H #include "../aoa.h" -extern int aoa_alsa_init(char *name, struct module *mod); +extern int aoa_alsa_init(char *name, struct module *mod, struct device *dev); extern void aoa_alsa_cleanup(void); #endif /* __SND_AOA_ALSA_H */ diff --git a/sound/aoa/core/snd-aoa-core.c b/sound/aoa/core/core.c index ecd2d8263f2..10bec6c6138 100644 --- a/sound/aoa/core/snd-aoa-core.c +++ b/sound/aoa/core/core.c @@ -10,7 +10,7 @@ #include <linux/module.h> #include <linux/list.h> #include "../aoa.h" -#include "snd-aoa-alsa.h" +#include "alsa.h" MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver"); MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); @@ -82,7 +82,7 @@ void aoa_codec_unregister(struct aoa_codec *codec) } EXPORT_SYMBOL_GPL(aoa_codec_unregister); -int aoa_fabric_register(struct aoa_fabric *new_fabric) +int aoa_fabric_register(struct aoa_fabric *new_fabric, struct device *dev) { struct aoa_codec *c; int err; @@ -98,7 +98,7 @@ int aoa_fabric_register(struct aoa_fabric *new_fabric) if (!new_fabric) return -EINVAL; - err = aoa_alsa_init(new_fabric->name, new_fabric->owner); + err = aoa_alsa_init(new_fabric->name, new_fabric->owner, dev); if (err) return err; diff --git a/sound/aoa/core/snd-aoa-gpio-feature.c b/sound/aoa/core/gpio-feature.c index 7c26089527f..f34153962d0 100644 --- a/sound/aoa/core/snd-aoa-gpio-feature.c +++ b/sound/aoa/core/gpio-feature.c @@ -5,16 +5,17 @@ * * GPL v2, can be found in COPYING. * - * This file contains the GPIO control routines for + * This file contains the GPIO control routines for * direct (through feature calls) access to the GPIO * registers. */ -#include <asm/pmac_feature.h> +#include <linux/of_irq.h> #include <linux/interrupt.h> +#include <asm/pmac_feature.h> #include "../aoa.h" -/* TODO: these are 20 global variables +/* TODO: these are lots of global variables * that aren't used on most machines... * Move them into a dynamically allocated * structure and use that. @@ -23,6 +24,7 @@ /* these are the GPIO numbers (register addresses as offsets into * the GPIO space) */ static int headphone_mute_gpio; +static int master_mute_gpio; static int amp_mute_gpio; static int lineout_mute_gpio; static int hw_reset_gpio; @@ -32,6 +34,7 @@ static int linein_detect_gpio; /* see the SWITCH_GPIO macro */ static int headphone_mute_gpio_activestate; +static int master_mute_gpio_activestate; static int amp_mute_gpio_activestate; static int lineout_mute_gpio_activestate; static int hw_reset_gpio_activestate; @@ -55,7 +58,7 @@ static struct device_node *get_gpio(char *name, int *gpioactiveptr) { struct device_node *np, *gpio; - u32 *reg; + const u32 *reg; const char *audio_gpio; *gpioptr = -1; @@ -71,7 +74,7 @@ static struct device_node *get_gpio(char *name, if (!gpio) return NULL; while ((np = of_get_next_child(gpio, np))) { - audio_gpio = get_property(np, "audio-gpio", NULL); + audio_gpio = of_get_property(np, "audio-gpio", NULL); if (!audio_gpio) continue; if (strcmp(audio_gpio, name) == 0) @@ -84,7 +87,7 @@ static struct device_node *get_gpio(char *name, return NULL; } - reg = (u32 *)get_property(np, "reg", NULL); + reg = of_get_property(np, "reg", NULL); if (!reg) return NULL; @@ -96,7 +99,7 @@ static struct device_node *get_gpio(char *name, if (*gpioptr < 0x50) *gpioptr += 0x50; - reg = (u32 *)get_property(np, "audio-gpio-active-state", NULL); + reg = of_get_property(np, "audio-gpio-active-state", NULL); if (!reg) /* Apple seems to default to 1, but * that doesn't seem right at least on most @@ -156,6 +159,7 @@ static int ftr_gpio_get_##name(struct gpio_runtime *rt) \ FTR_GPIO(headphone, 0); FTR_GPIO(amp, 1); FTR_GPIO(lineout, 2); +FTR_GPIO(master, 3); static void ftr_gpio_set_hw_reset(struct gpio_runtime *rt, int on) { @@ -172,6 +176,8 @@ static void ftr_gpio_set_hw_reset(struct gpio_runtime *rt, int on) hw_reset_gpio, v); } +static struct gpio_methods methods; + static void ftr_gpio_all_amps_off(struct gpio_runtime *rt) { int saved; @@ -181,6 +187,8 @@ static void ftr_gpio_all_amps_off(struct gpio_runtime *rt) ftr_gpio_set_headphone(rt, 0); ftr_gpio_set_amp(rt, 0); ftr_gpio_set_lineout(rt, 0); + if (methods.set_master) + ftr_gpio_set_master(rt, 0); rt->implementation_private = saved; } @@ -193,11 +201,14 @@ static void ftr_gpio_all_amps_restore(struct gpio_runtime *rt) ftr_gpio_set_headphone(rt, (s>>0)&1); ftr_gpio_set_amp(rt, (s>>1)&1); ftr_gpio_set_lineout(rt, (s>>2)&1); + if (methods.set_master) + ftr_gpio_set_master(rt, (s>>3)&1); } -static void ftr_handle_notify(void *data) +static void ftr_handle_notify(struct work_struct *work) { - struct gpio_notification *notif = data; + struct gpio_notification *notif = + container_of(work, struct gpio_notification, work.work); mutex_lock(¬if->mutex); if (notif->notify) @@ -230,6 +241,12 @@ static void ftr_gpio_init(struct gpio_runtime *rt) get_gpio("hw-reset", "audio-hw-reset", &hw_reset_gpio, &hw_reset_gpio_activestate); + if (get_gpio("master-mute", NULL, + &master_mute_gpio, + &master_mute_gpio_activestate)) { + methods.set_master = ftr_gpio_set_master; + methods.get_master = ftr_gpio_get_master; + } headphone_detect_node = get_gpio("headphone-detect", NULL, &headphone_detect_gpio, @@ -253,12 +270,9 @@ static void ftr_gpio_init(struct gpio_runtime *rt) ftr_gpio_all_amps_off(rt); rt->implementation_private = 0; - INIT_WORK(&rt->headphone_notify.work, ftr_handle_notify, - &rt->headphone_notify); - INIT_WORK(&rt->line_in_notify.work, ftr_handle_notify, - &rt->line_in_notify); - INIT_WORK(&rt->line_out_notify.work, ftr_handle_notify, - &rt->line_out_notify); + INIT_DELAYED_WORK(&rt->headphone_notify.work, ftr_handle_notify); + INIT_DELAYED_WORK(&rt->line_in_notify.work, ftr_handle_notify); + INIT_DELAYED_WORK(&rt->line_out_notify.work, ftr_handle_notify); mutex_init(&rt->headphone_notify.mutex); mutex_init(&rt->line_in_notify.mutex); mutex_init(&rt->line_out_notify.mutex); @@ -274,22 +288,19 @@ static void ftr_gpio_exit(struct gpio_runtime *rt) free_irq(linein_detect_irq, &rt->line_in_notify); if (rt->line_out_notify.gpio_private) free_irq(lineout_detect_irq, &rt->line_out_notify); - cancel_delayed_work(&rt->headphone_notify.work); - cancel_delayed_work(&rt->line_in_notify.work); - cancel_delayed_work(&rt->line_out_notify.work); - flush_scheduled_work(); + cancel_delayed_work_sync(&rt->headphone_notify.work); + cancel_delayed_work_sync(&rt->line_in_notify.work); + cancel_delayed_work_sync(&rt->line_out_notify.work); mutex_destroy(&rt->headphone_notify.mutex); mutex_destroy(&rt->line_in_notify.mutex); mutex_destroy(&rt->line_out_notify.mutex); } -static irqreturn_t ftr_handle_notify_irq(int xx, - void *data, - struct pt_regs *regs) +static irqreturn_t ftr_handle_notify_irq(int xx, void *data) { struct gpio_notification *notif = data; - schedule_work(¬if->work); + schedule_delayed_work(¬if->work, 0); return IRQ_HANDLED; } diff --git a/sound/aoa/core/snd-aoa-gpio-pmf.c b/sound/aoa/core/gpio-pmf.c index 2836c321839..c8d8a1a6f96 100644 --- a/sound/aoa/core/snd-aoa-gpio-pmf.c +++ b/sound/aoa/core/gpio-pmf.c @@ -6,6 +6,7 @@ * GPL v2, can be found in COPYING. */ +#include <linux/slab.h> #include <asm/pmac_feature.h> #include <asm/pmac_pfunc.h> #include "../aoa.h" @@ -69,9 +70,10 @@ static void pmf_gpio_all_amps_restore(struct gpio_runtime *rt) pmf_gpio_set_lineout(rt, (s>>2)&1); } -static void pmf_handle_notify(void *data) +static void pmf_handle_notify(struct work_struct *work) { - struct gpio_notification *notif = data; + struct gpio_notification *notif = + container_of(work, struct gpio_notification, work.work); mutex_lock(¬if->mutex); if (notif->notify) @@ -83,12 +85,9 @@ static void pmf_gpio_init(struct gpio_runtime *rt) { pmf_gpio_all_amps_off(rt); rt->implementation_private = 0; - INIT_WORK(&rt->headphone_notify.work, pmf_handle_notify, - &rt->headphone_notify); - INIT_WORK(&rt->line_in_notify.work, pmf_handle_notify, - &rt->line_in_notify); - INIT_WORK(&rt->line_out_notify.work, pmf_handle_notify, - &rt->line_out_notify); + INIT_DELAYED_WORK(&rt->headphone_notify.work, pmf_handle_notify); + INIT_DELAYED_WORK(&rt->line_in_notify.work, pmf_handle_notify); + INIT_DELAYED_WORK(&rt->line_out_notify.work, pmf_handle_notify); mutex_init(&rt->headphone_notify.mutex); mutex_init(&rt->line_in_notify.mutex); mutex_init(&rt->line_out_notify.mutex); @@ -108,28 +107,24 @@ static void pmf_gpio_exit(struct gpio_runtime *rt) /* make sure no work is pending before freeing * all things */ - cancel_delayed_work(&rt->headphone_notify.work); - cancel_delayed_work(&rt->line_in_notify.work); - cancel_delayed_work(&rt->line_out_notify.work); - flush_scheduled_work(); + cancel_delayed_work_sync(&rt->headphone_notify.work); + cancel_delayed_work_sync(&rt->line_in_notify.work); + cancel_delayed_work_sync(&rt->line_out_notify.work); mutex_destroy(&rt->headphone_notify.mutex); mutex_destroy(&rt->line_in_notify.mutex); mutex_destroy(&rt->line_out_notify.mutex); - if (rt->headphone_notify.gpio_private) - kfree(rt->headphone_notify.gpio_private); - if (rt->line_in_notify.gpio_private) - kfree(rt->line_in_notify.gpio_private); - if (rt->line_out_notify.gpio_private) - kfree(rt->line_out_notify.gpio_private); + kfree(rt->headphone_notify.gpio_private); + kfree(rt->line_in_notify.gpio_private); + kfree(rt->line_out_notify.gpio_private); } static void pmf_handle_notify_irq(void *data) { struct gpio_notification *notif = data; - schedule_work(¬if->work); + schedule_delayed_work(¬if->work, 0); } static int pmf_set_notify(struct gpio_runtime *rt, @@ -184,6 +179,10 @@ static int pmf_set_notify(struct gpio_runtime *rt, if (!old && notify) { irq_client = kzalloc(sizeof(struct pmf_irq_client), GFP_KERNEL); + if (!irq_client) { + err = -ENOMEM; + goto out_unlock; + } irq_client->data = notif; irq_client->handler = pmf_handle_notify_irq; irq_client->owner = THIS_MODULE; diff --git a/sound/aoa/fabrics/Kconfig b/sound/aoa/fabrics/Kconfig index c3bc7705c86..3ca475a886b 100644 --- a/sound/aoa/fabrics/Kconfig +++ b/sound/aoa/fabrics/Kconfig @@ -1,6 +1,5 @@ config SND_AOA_FABRIC_LAYOUT tristate "layout-id fabric" - depends SND_AOA select SND_AOA_SOUNDBUS select SND_AOA_SOUNDBUS_I2S ---help--- diff --git a/sound/aoa/fabrics/Makefile b/sound/aoa/fabrics/Makefile index 55fc5e7e52c..da37c10eca5 100644 --- a/sound/aoa/fabrics/Makefile +++ b/sound/aoa/fabrics/Makefile @@ -1 +1,3 @@ +snd-aoa-fabric-layout-objs += layout.o + obj-$(CONFIG_SND_AOA_FABRIC_LAYOUT) += snd-aoa-fabric-layout.o diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/layout.c index 172eb95476c..9dc5806d23d 100644 --- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c +++ b/sound/aoa/fabrics/layout.c @@ -1,19 +1,18 @@ /* - * Apple Onboard Audio driver -- layout fabric + * Apple Onboard Audio driver -- layout/machine id fabric * - * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net> * * GPL v2, can be found in COPYING. * * - * This fabric module looks for sound codecs - * based on the layout-id property in the device tree. - * + * This fabric module looks for sound codecs based on the + * layout-id or device-id property in the device tree. */ - #include <asm/prom.h> #include <linux/list.h> #include <linux/module.h> +#include <linux/slab.h> #include "../aoa.h" #include "../soundbus/soundbus.h" @@ -63,10 +62,10 @@ struct codec_connect_info { #define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0) struct layout { - unsigned int layout_id; + unsigned int layout_id, device_id; struct codec_connect_info codecs[MAX_CODECS_PER_BUS]; int flags; - + /* if busname is not assigned, we use 'Master' below, * so that our layout table doesn't need to be filled * too much. @@ -111,6 +110,11 @@ MODULE_ALIAS("sound-layout-96"); MODULE_ALIAS("sound-layout-98"); MODULE_ALIAS("sound-layout-100"); +MODULE_ALIAS("aoa-device-id-14"); +MODULE_ALIAS("aoa-device-id-22"); +MODULE_ALIAS("aoa-device-id-35"); +MODULE_ALIAS("aoa-device-id-44"); + /* onyx with all but microphone connected */ static struct codec_connection onyx_connections_nomic[] = { { @@ -358,6 +362,13 @@ static struct layout layouts[] = { .connections = tas_connections_nolineout, }, }, + /* PowerBook6,5 */ + { .device_id = 44, + .codecs[0] = { + .name = "tas", + .connections = tas_connections_all, + }, + }, /* PowerBook6,7 */ { .layout_id = 80, .codecs[0] = { @@ -518,6 +529,27 @@ static struct layout layouts[] = { .connections = onyx_connections_noheadphones, }, }, + /* PowerMac3,4 */ + { .device_id = 14, + .codecs[0] = { + .name = "tas", + .connections = tas_connections_noline, + }, + }, + /* PowerMac3,6 */ + { .device_id = 22, + .codecs[0] = { + .name = "tas", + .connections = tas_connections_all, + }, + }, + /* PowerBook5,2 */ + { .device_id = 35, + .codecs[0] = { + .name = "tas", + .connections = tas_connections_all, + }, + }, {} }; @@ -526,7 +558,7 @@ static struct layout *find_layout_by_id(unsigned int id) struct layout *l; l = layouts; - while (l->layout_id) { + while (l->codecs[0].name) { if (l->layout_id == id) return l; l++; @@ -534,6 +566,19 @@ static struct layout *find_layout_by_id(unsigned int id) return NULL; } +static struct layout *find_layout_by_device(unsigned int id) +{ + struct layout *l; + + l = layouts; + while (l->codecs[0].name) { + if (l->device_id == id) + return l; + l++; + } + return NULL; +} + static void use_layout(struct layout *l) { int i; @@ -564,6 +609,7 @@ struct layout_dev { struct snd_kcontrol *headphone_ctrl; struct snd_kcontrol *lineout_ctrl; struct snd_kcontrol *speaker_ctrl; + struct snd_kcontrol *master_ctrl; struct snd_kcontrol *headphone_detected_ctrl; struct snd_kcontrol *lineout_detected_ctrl; @@ -582,15 +628,7 @@ static int layouts_list_items; * make the fabric handle all the card stuff, etc... */ static struct layout_dev *layout_device; -static int control_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 control_info snd_ctl_boolean_mono_info #define AMP_CONTROL(n, description) \ static int n##_control_get(struct snd_kcontrol *kcontrol, \ @@ -606,9 +644,9 @@ static int n##_control_put(struct snd_kcontrol *kcontrol, \ struct snd_ctl_elem_value *ucontrol) \ { \ struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \ - if (gpio->methods && gpio->methods->get_##n) \ + if (gpio->methods && gpio->methods->set_##n) \ gpio->methods->set_##n(gpio, \ - ucontrol->value.integer.value[0]); \ + !!ucontrol->value.integer.value[0]); \ return 1; \ } \ static struct snd_kcontrol_new n##_ctl = { \ @@ -623,6 +661,7 @@ static struct snd_kcontrol_new n##_ctl = { \ AMP_CONTROL(headphone, "Headphone Switch"); AMP_CONTROL(speakers, "Speakers Switch"); AMP_CONTROL(lineout, "Line-Out Switch"); +AMP_CONTROL(master, "Master Switch"); static int detect_choice_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -724,7 +763,7 @@ static int check_codec(struct aoa_codec *codec, struct layout_dev *ldev, struct codec_connect_info *cci) { - u32 *ref; + const u32 *ref; char propname[32]; struct codec_connection *cc; @@ -732,13 +771,13 @@ static int check_codec(struct aoa_codec *codec, if (codec->node && (strcmp(codec->node->name, "codec") == 0)) { snprintf(propname, sizeof(propname), "platform-%s-codec-ref", codec->name); - ref = (u32*)get_property(ldev->sound, propname, NULL); + ref = of_get_property(ldev->sound, propname, NULL); if (!ref) { printk(KERN_INFO "snd-aoa-fabric-layout: " "required property %s not present\n", propname); return -ENODEV; } - if (*ref != codec->node->linux_phandle) { + if (*ref != codec->node->phandle) { printk(KERN_INFO "snd-aoa-fabric-layout: " "%s doesn't match!\n", propname); return -ENODEV; @@ -863,6 +902,11 @@ static void layout_attached_codec(struct aoa_codec *codec) lineout = codec->gpio->methods->get_detect(codec->gpio, AOA_NOTIFY_LINE_OUT); + if (codec->gpio->methods->set_master) { + ctl = snd_ctl_new1(&master_ctl, codec->gpio); + ldev->master_ctrl = ctl; + aoa_snd_ctl_add(ctl); + } while (cc->connected) { if (cc->connected & CC_SPEAKERS) { if (headphones <= 0 && lineout <= 0) @@ -946,8 +990,8 @@ static struct aoa_fabric layout_fabric = { static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) { struct device_node *sound = NULL; - unsigned int *layout_id; - struct layout *layout; + const unsigned int *id; + struct layout *layout = NULL; struct layout_dev *ldev = NULL; int err; @@ -956,19 +1000,22 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) return -ENODEV; /* by breaking out we keep a reference */ - while ((sound = of_get_next_child(sdev->ofdev.node, sound))) { + while ((sound = of_get_next_child(sdev->ofdev.dev.of_node, sound))) { if (sound->type && strcasecmp(sound->type, "soundchip") == 0) break; } - if (!sound) return -ENODEV; + if (!sound) + return -ENODEV; - layout_id = (unsigned int *) get_property(sound, "layout-id", NULL); - if (!layout_id) - goto outnodev; - printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d\n", - *layout_id); + id = of_get_property(sound, "layout-id", NULL); + if (id) { + layout = find_layout_by_id(*id); + } else { + id = of_get_property(sound, "device-id", NULL); + if (id) + layout = find_layout_by_device(*id); + } - layout = find_layout_by_id(*layout_id); if (!layout) { printk(KERN_ERR "snd-aoa-fabric-layout: unknown layout\n"); goto outnodev; @@ -984,6 +1031,7 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) ldev->layout = layout; ldev->gpio.node = sound->parent; switch (layout->layout_id) { + case 0: /* anything with device_id, not layout_id */ case 41: /* that unknown machine no one seems to have */ case 51: /* PowerBook5,4 */ case 58: /* Mac Mini */ @@ -998,7 +1046,7 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) } ldev->selfptr_headphone.ptr = ldev; ldev->selfptr_lineout.ptr = ldev; - sdev->ofdev.dev.driver_data = ldev; + dev_set_drvdata(&sdev->ofdev.dev, ldev); list_add(&ldev->list, &layouts_list); layouts_list_items++; @@ -1014,7 +1062,7 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) ldev->gpio.methods->init(&ldev->gpio); - err = aoa_fabric_register(&layout_fabric); + err = aoa_fabric_register(&layout_fabric, &sdev->ofdev.dev); if (err && err != -EALREADY) { printk(KERN_INFO "snd-aoa-fabric-layout: can't use," " another fabric is active!\n"); @@ -1033,16 +1081,16 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) sdev->pcmid = -1; list_del(&ldev->list); layouts_list_items--; + kfree(ldev); outnodev: - if (sound) of_node_put(sound); + of_node_put(sound); layout_device = NULL; - if (ldev) kfree(ldev); return -ENODEV; } static int aoa_fabric_layout_remove(struct soundbus_dev *sdev) { - struct layout_dev *ldev = sdev->ofdev.dev.driver_data; + struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev); int i; for (i=0; i<MAX_CODECS_PER_BUS; i++) { @@ -1075,9 +1123,7 @@ static int aoa_fabric_layout_remove(struct soundbus_dev *sdev) #ifdef CONFIG_PM static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state) { - struct layout_dev *ldev = sdev->ofdev.dev.driver_data; - - printk("aoa_fabric_layout_suspend()\n"); + struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev); if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off) ldev->gpio.methods->all_amps_off(&ldev->gpio); @@ -1087,11 +1133,9 @@ static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t sta static int aoa_fabric_layout_resume(struct soundbus_dev *sdev) { - struct layout_dev *ldev = sdev->ofdev.dev.driver_data; + struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev); - printk("aoa_fabric_layout_resume()\n"); - - if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off) + if (ldev->gpio.methods && ldev->gpio.methods->all_amps_restore) ldev->gpio.methods->all_amps_restore(&ldev->gpio); return 0; @@ -1107,6 +1151,9 @@ static struct soundbus_driver aoa_soundbus_driver = { .suspend = aoa_fabric_layout_suspend, .resume = aoa_fabric_layout_resume, #endif + .driver = { + .owner = THIS_MODULE, + } }; static int __init aoa_fabric_layout_init(void) diff --git a/sound/aoa/soundbus/Kconfig b/sound/aoa/soundbus/Kconfig index 7368b7ddfe0..839d1137b9b 100644 --- a/sound/aoa/soundbus/Kconfig +++ b/sound/aoa/soundbus/Kconfig @@ -1,6 +1,5 @@ config SND_AOA_SOUNDBUS tristate "Apple Soundbus support" - depends on SOUND select SND_PCM ---help--- This option enables the generic driver for the soundbus diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c index 47b3e3768df..7487eb76e03 100644 --- a/sound/aoa/soundbus/core.c +++ b/sound/aoa/soundbus/core.c @@ -56,14 +56,13 @@ static int soundbus_probe(struct device *dev) } -static int soundbus_uevent(struct device *dev, char **envp, int num_envp, - char *buffer, int buffer_size) +static int soundbus_uevent(struct device *dev, struct kobj_uevent_env *env) { struct soundbus_dev * soundbus_dev; - struct of_device * of; - char *scratch, *compat, *compat2; - int i = 0; - int length, cplen, cplen2, seen = 0; + struct platform_device * of; + const char *compat; + int retval = 0; + int cplen, seen = 0; if (!dev) return -ENODEV; @@ -75,63 +74,35 @@ static int soundbus_uevent(struct device *dev, char **envp, int num_envp, of = &soundbus_dev->ofdev; /* stuff we want to pass to /sbin/hotplug */ - envp[i++] = scratch = buffer; - length = scnprintf (scratch, buffer_size, "OF_NAME=%s", of->node->name); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "OF_TYPE=%s", of->node->type); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; + retval = add_uevent_var(env, "OF_NAME=%s", of->dev.of_node->name); + if (retval) + return retval; + + retval = add_uevent_var(env, "OF_TYPE=%s", of->dev.of_node->type); + if (retval) + return retval; /* Since the compatible field can contain pretty much anything * it's not really legal to split it out with commas. We split it * up using a number of environment variables instead. */ - compat = (char *) get_property(of->node, "compatible", &cplen); - compat2 = compat; - cplen2= cplen; + compat = of_get_property(of->dev.of_node, "compatible", &cplen); while (compat && cplen > 0) { - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, - "OF_COMPATIBLE_%d=%s", seen, compat); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - length = strlen (compat) + 1; - compat += length; - cplen -= length; - seen++; + int tmp = env->buflen; + retval = add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat); + if (retval) + return retval; + compat += env->buflen - tmp; + cplen -= env->buflen - tmp; + seen += 1; } - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "OF_COMPATIBLE_N=%d", seen); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "MODALIAS=%s", - soundbus_dev->modalias); - - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; + retval = add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen); + if (retval) + return retval; + retval = add_uevent_var(env, "MODALIAS=%s", soundbus_dev->modalias); - envp[i] = NULL; - - return 0; + return retval; } static int soundbus_device_remove(struct device *dev) @@ -179,8 +150,6 @@ static int soundbus_device_resume(struct device * dev) #endif /* CONFIG_PM */ -extern struct device_attribute soundbus_dev_attrs[]; - static struct bus_type soundbus_bus_type = { .name = "aoa-soundbus", .probe = soundbus_probe, @@ -200,14 +169,14 @@ int soundbus_add_one(struct soundbus_dev *dev) /* sanity checks */ if (!dev->attach_codec || - !dev->ofdev.node || + !dev->ofdev.dev.of_node || dev->pcmname || dev->pcmid != -1) { printk(KERN_ERR "soundbus: adding device failed sanity check!\n"); return -EINVAL; } - snprintf(dev->ofdev.dev.bus_id, BUS_ID_SIZE, "soundbus:%x", ++devcount); + dev_set_name(&dev->ofdev.dev, "soundbus:%x", ++devcount); dev->ofdev.dev.bus = &soundbus_bus_type; return of_device_register(&dev->ofdev); } diff --git a/sound/aoa/soundbus/i2sbus/Makefile b/sound/aoa/soundbus/i2sbus/Makefile index e57a5cf6565..1b949b2a402 100644 --- a/sound/aoa/soundbus/i2sbus/Makefile +++ b/sound/aoa/soundbus/i2sbus/Makefile @@ -1,2 +1,2 @@ obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += snd-aoa-i2sbus.o -snd-aoa-i2sbus-objs := i2sbus-core.o i2sbus-pcm.o i2sbus-control.o +snd-aoa-i2sbus-objs := core.o pcm.o control.o diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-control.c b/sound/aoa/soundbus/i2sbus/control.c index 87beb4ad4d6..4dc9b49c02c 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus-control.c +++ b/sound/aoa/soundbus/i2sbus/control.c @@ -8,6 +8,7 @@ #include <linux/kernel.h> #include <linux/delay.h> +#include <linux/slab.h> #include <asm/io.h> #include <asm/prom.h> @@ -41,7 +42,7 @@ int i2sbus_control_add_dev(struct i2sbus_control *c, { struct device_node *np; - np = i2sdev->sound.ofdev.node; + np = i2sdev->sound.ofdev.dev.of_node; i2sdev->enable = pmf_find_function(np, "enable"); i2sdev->cell_enable = pmf_find_function(np, "cell-enable"); i2sdev->clock_enable = pmf_find_function(np, "clock-enable"); diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-core.c b/sound/aoa/soundbus/i2sbus/core.c index 23190aa6bc7..467836057ee 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus-core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -1,17 +1,19 @@ /* * i2sbus driver * - * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net> * * GPL v2, can be found in COPYING. */ #include <linux/module.h> +#include <linux/slab.h> #include <linux/pci.h> #include <linux/interrupt.h> #include <linux/dma-mapping.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> -#include <sound/driver.h> #include <sound/core.h> #include <asm/macio.h> @@ -23,9 +25,6 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); MODULE_DESCRIPTION("Apple Soundbus: I2S support"); -/* for auto-loading, declare that we handle this weird - * string that macio puts into the relevant device */ -MODULE_ALIAS("of:Ni2sTi2sC"); static int force; module_param(force, int, 0444); @@ -37,12 +36,14 @@ static struct of_device_id i2sbus_match[] = { { } }; +MODULE_DEVICE_TABLE(of, i2sbus_match); + static int alloc_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev, struct dbdma_command_mem *r, int numcmds) { - /* one more for rounding */ - r->size = (numcmds+1) * sizeof(struct dbdma_cmd); + /* one more for rounding, one for branch back, one for stop command */ + r->size = (numcmds + 3) * sizeof(struct dbdma_cmd); /* We use the PCI APIs for now until the generic one gets fixed * enough or until we get some macio-specific versions */ @@ -66,7 +67,7 @@ static void free_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev, struct dbdma_command_mem *r) { if (!r->space) return; - + dma_free_coherent(&macio_get_pci_dev(i2sdev->macio)->dev, r->size, r->space, r->bus_addr); } @@ -93,7 +94,7 @@ static void i2sbus_release_dev(struct device *dev) kfree(i2sdev); } -static irqreturn_t i2sbus_bus_intr(int irq, void *devid, struct pt_regs *regs) +static irqreturn_t i2sbus_bus_intr(int irq, void *devid) { struct i2sbus_dev *dev = devid; u32 intreg; @@ -122,7 +123,7 @@ static int i2sbus_get_and_fixup_rsrc(struct device_node *np, int index, { struct device_node *parent; int pindex, rc = -ENXIO; - u32 *reg; + const u32 *reg; /* Machines with layout 76 and 36 (K2 based) have a weird device * tree what we need to special case. @@ -141,7 +142,7 @@ static int i2sbus_get_and_fixup_rsrc(struct device_node *np, int index, rc = of_address_to_resource(parent, pindex, res); if (rc) goto bail; - reg = (u32 *)get_property(np, "reg", NULL); + reg = of_get_property(np, "reg", NULL); if (reg == NULL) { rc = -ENXIO; goto bail; @@ -161,12 +162,11 @@ static int i2sbus_add_dev(struct macio_dev *macio, struct i2sbus_dev *dev; struct device_node *child = NULL, *sound = NULL; struct resource *r; - int i, layout = 0, rlen; + int i, layout = 0, rlen, ok = force; static const char *rnames[] = { "i2sbus: %s (control)", "i2sbus: %s (tx)", "i2sbus: %s (rx)" }; - static irqreturn_t (*ints[])(int irq, void *devid, - struct pt_regs *regs) = { + static irq_handler_t ints[] = { i2sbus_bus_intr, i2sbus_tx_intr, i2sbus_rx_intr @@ -189,13 +189,26 @@ static int i2sbus_add_dev(struct macio_dev *macio, } } if (i == 1) { - u32 *layout_id; - layout_id = (u32*) get_property(sound, "layout-id", NULL); - if (layout_id) { - layout = *layout_id; + const u32 *id = of_get_property(sound, "layout-id", NULL); + + if (id) { + layout = *id; snprintf(dev->sound.modalias, 32, "sound-layout-%d", layout); - force = 1; + ok = 1; + } else { + id = of_get_property(sound, "device-id", NULL); + /* + * We probably cannot handle all device-id machines, + * so restrict to those we do handle for now. + */ + if (id && (*id == 22 || *id == 14 || *id == 35 || + *id == 44)) { + snprintf(dev->sound.modalias, 32, + "aoa-device-id-%d", *id); + ok = 1; + layout = -1; + } } } /* for the time being, until we can handle non-layout-id @@ -204,16 +217,16 @@ static int i2sbus_add_dev(struct macio_dev *macio, * When there are two i2s busses and only one has a layout-id, * then this depends on the order, but that isn't important * either as the second one in that case is just a modem. */ - if (!force) { + if (!ok) { kfree(dev); return -ENODEV; } mutex_init(&dev->lock); spin_lock_init(&dev->low_lock); - dev->sound.ofdev.node = np; - dev->sound.ofdev.dma_mask = macio->ofdev.dma_mask; - dev->sound.ofdev.dev.dma_mask = &dev->sound.ofdev.dma_mask; + dev->sound.ofdev.archdata.dma_mask = macio->ofdev.archdata.dma_mask; + dev->sound.ofdev.dev.of_node = np; + dev->sound.ofdev.dev.dma_mask = &dev->sound.ofdev.archdata.dma_mask; dev->sound.ofdev.dev.parent = &macio->ofdev.dev; dev->sound.ofdev.dev.release = i2sbus_release_dev; dev->sound.attach_codec = i2sbus_attach_codec; @@ -250,10 +263,9 @@ static int i2sbus_add_dev(struct macio_dev *macio, * but request_resource doesn't know about parents and * contained resources... */ - dev->allocated_resource[i] = + dev->allocated_resource[i] = request_mem_region(dev->resources[i].start, - dev->resources[i].end - - dev->resources[i].start + 1, + resource_size(&dev->resources[i]), dev->rnames[i]); if (!dev->allocated_resource[i]) { printk(KERN_ERR "i2sbus: failed to claim resource %d!\n", i); @@ -262,19 +274,19 @@ static int i2sbus_add_dev(struct macio_dev *macio, } r = &dev->resources[aoa_resource_i2smmio]; - rlen = r->end - r->start + 1; + rlen = resource_size(r); if (rlen < sizeof(struct i2s_interface_regs)) goto err; dev->intfregs = ioremap(r->start, rlen); r = &dev->resources[aoa_resource_txdbdma]; - rlen = r->end - r->start + 1; + rlen = resource_size(r); if (rlen < sizeof(struct dbdma_regs)) goto err; dev->out.dbdma = ioremap(r->start, rlen); r = &dev->resources[aoa_resource_rxdbdma]; - rlen = r->end - r->start + 1; + rlen = resource_size(r); if (rlen < sizeof(struct dbdma_regs)) goto err; dev->in.dbdma = ioremap(r->start, rlen); @@ -336,9 +348,9 @@ static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match) return -ENODEV; } - while ((np = of_get_next_child(dev->ofdev.node, np))) { - if (device_is_compatible(np, "i2sbus") || - device_is_compatible(np, "i2s-modem")) { + while ((np = of_get_next_child(dev->ofdev.dev.of_node, np))) { + if (of_device_is_compatible(np, "i2sbus") || + of_device_is_compatible(np, "i2s-modem")) { got += i2sbus_add_dev(dev, control, np); } } @@ -349,14 +361,14 @@ static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match) return -ENODEV; } - dev->ofdev.dev.driver_data = control; + dev_set_drvdata(&dev->ofdev.dev, control); return 0; } static int i2sbus_remove(struct macio_dev* dev) { - struct i2sbus_control *control = dev->ofdev.dev.driver_data; + struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev); struct i2sbus_dev *i2sdev, *tmp; list_for_each_entry_safe(i2sdev, tmp, &control->list, item) @@ -368,7 +380,7 @@ static int i2sbus_remove(struct macio_dev* dev) #ifdef CONFIG_PM static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state) { - struct i2sbus_control *control = dev->ofdev.dev.driver_data; + struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev); struct codec_info_item *cii; struct i2sbus_dev* i2sdev; int err, ret = 0; @@ -378,11 +390,8 @@ static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state) if (i2sdev->sound.pcm) { /* Suspend PCM streams */ snd_pcm_suspend_all(i2sdev->sound.pcm); - /* Probably useless as we handle - * power transitions ourselves */ - snd_power_change_state(i2sdev->sound.pcm->card, - SNDRV_CTL_POWER_D3hot); } + /* Notify codecs */ list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { err = 0; @@ -391,18 +400,25 @@ static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state) if (err) ret = err; } + + /* wait until streams are stopped */ + i2sbus_wait_for_stop_both(i2sdev); } + return ret; } static int i2sbus_resume(struct macio_dev* dev) { - struct i2sbus_control *control = dev->ofdev.dev.driver_data; + struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev); struct codec_info_item *cii; struct i2sbus_dev* i2sdev; int err, ret = 0; list_for_each_entry(i2sdev, &control->list, item) { + /* reset i2s bus format etc. */ + i2sbus_pcm_prepare_both(i2sdev); + /* Notify codecs so they can re-initialize */ list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { err = 0; @@ -411,12 +427,6 @@ static int i2sbus_resume(struct macio_dev* dev) if (err) ret = err; } - /* Notify Alsa */ - if (i2sdev->sound.pcm) { - /* Same comment as above, probably useless */ - snd_power_change_state(i2sdev->sound.pcm->card, - SNDRV_CTL_POWER_D0); - } } return ret; @@ -429,9 +439,11 @@ static int i2sbus_shutdown(struct macio_dev* dev) } static struct macio_driver i2sbus_drv = { - .name = "soundbus-i2s", - .owner = THIS_MODULE, - .match_table = i2sbus_match, + .driver = { + .name = "soundbus-i2s", + .owner = THIS_MODULE, + .of_match_table = i2sbus_match, + }, .probe = i2sbus_probe, .remove = i2sbus_remove, #ifdef CONFIG_PM diff --git a/sound/aoa/soundbus/i2sbus/i2sbus.h b/sound/aoa/soundbus/i2sbus/i2sbus.h index 0c69d209be5..befefd99e27 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus.h +++ b/sound/aoa/soundbus/i2sbus/i2sbus.h @@ -10,6 +10,7 @@ #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/mutex.h> +#include <linux/completion.h> #include <sound/pcm.h> @@ -17,7 +18,7 @@ #include <asm/pmac_feature.h> #include <asm/dbdma.h> -#include "i2sbus-interface.h" +#include "interface.h" #include "../soundbus.h" struct i2sbus_control { @@ -34,6 +35,7 @@ struct dbdma_command_mem { void *space; int size; u32 running:1; + u32 stopping:1; }; struct pcm_info { @@ -45,6 +47,7 @@ struct pcm_info { u32 frame_count; struct dbdma_command_mem dbdma_ring; volatile struct dbdma_regs __iomem *dbdma; + struct completion *stop_completion; }; enum { @@ -97,9 +100,12 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, extern void i2sbus_detach_codec(struct soundbus_dev *dev, void *data); extern irqreturn_t -i2sbus_tx_intr(int irq, void *devid, struct pt_regs *regs); +i2sbus_tx_intr(int irq, void *devid); extern irqreturn_t -i2sbus_rx_intr(int irq, void *devid, struct pt_regs *regs); +i2sbus_rx_intr(int irq, void *devid); + +extern void i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev); +extern void i2sbus_pcm_prepare_both(struct i2sbus_dev *i2sdev); /* control specific functions */ extern int i2sbus_control_init(struct macio_dev* dev, diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-interface.h b/sound/aoa/soundbus/i2sbus/interface.h index c6b5f5452d2..c6b5f5452d2 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus-interface.h +++ b/sound/aoa/soundbus/i2sbus/interface.h diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c b/sound/aoa/soundbus/i2sbus/pcm.c index 3049015a04f..7b74a4ba75f 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c +++ b/sound/aoa/soundbus/i2sbus/pcm.c @@ -8,12 +8,11 @@ #include <asm/io.h> #include <linux/delay.h> -/* So apparently there's a reason for requiring driver.h - * to be included first, even if I don't know it... */ -#include <sound/driver.h> +#include <linux/slab.h> #include <sound/core.h> #include <asm/macio.h> #include <linux/pci.h> +#include <linux/module.h> #include "../soundbus.h" #include "i2sbus.h" @@ -125,7 +124,8 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in) } /* bus dependent stuff */ hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME; + SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_JOINT_DUPLEX; CHECK_RATE(5512); CHECK_RATE(8000); @@ -179,7 +179,7 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in) */ if (other->active) { /* FIXME: is this guaranteed by the alsa api? */ - hw->formats &= (1ULL << i2sdev->format); + hw->formats &= pcm_format_to_bits(i2sdev->format); /* see above, restrict rates to the one we already have */ hw->rate_min = i2sdev->rate; hw->rate_max = i2sdev->rate; @@ -193,6 +193,12 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in) hw->period_bytes_max = 16384; hw->periods_min = 3; hw->periods_max = MAX_DBDMA_COMMANDS; + err = snd_pcm_hw_constraint_integer(pi->substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) { + result = err; + goto out_unlock; + } list_for_each_entry(cii, &sdev->codec_list, list) { if (cii->codec->open) { err = cii->codec->open(cii, pi->substream); @@ -245,18 +251,78 @@ static int i2sbus_pcm_close(struct i2sbus_dev *i2sdev, int in) return err; } +static void i2sbus_wait_for_stop(struct i2sbus_dev *i2sdev, + struct pcm_info *pi) +{ + unsigned long flags; + struct completion done; + long timeout; + + spin_lock_irqsave(&i2sdev->low_lock, flags); + if (pi->dbdma_ring.stopping) { + init_completion(&done); + pi->stop_completion = &done; + spin_unlock_irqrestore(&i2sdev->low_lock, flags); + timeout = wait_for_completion_timeout(&done, HZ); + spin_lock_irqsave(&i2sdev->low_lock, flags); + pi->stop_completion = NULL; + if (timeout == 0) { + /* timeout expired, stop dbdma forcefully */ + printk(KERN_ERR "i2sbus_wait_for_stop: timed out\n"); + /* make sure RUN, PAUSE and S0 bits are cleared */ + out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16); + pi->dbdma_ring.stopping = 0; + timeout = 10; + while (in_le32(&pi->dbdma->status) & ACTIVE) { + if (--timeout <= 0) + break; + udelay(1); + } + } + } + spin_unlock_irqrestore(&i2sdev->low_lock, flags); +} + +#ifdef CONFIG_PM +void i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev) +{ + struct pcm_info *pi; + + get_pcm_info(i2sdev, 0, &pi, NULL); + i2sbus_wait_for_stop(i2sdev, pi); + get_pcm_info(i2sdev, 1, &pi, NULL); + i2sbus_wait_for_stop(i2sdev, pi); +} +#endif + static int i2sbus_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); } -static int i2sbus_hw_free(struct snd_pcm_substream *substream) +static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in) { + struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); + struct pcm_info *pi; + + get_pcm_info(i2sdev, in, &pi, NULL); + if (pi->dbdma_ring.stopping) + i2sbus_wait_for_stop(i2sdev, pi); snd_pcm_lib_free_pages(substream); return 0; } +static int i2sbus_playback_hw_free(struct snd_pcm_substream *substream) +{ + return i2sbus_hw_free(substream, 0); +} + +static int i2sbus_record_hw_free(struct snd_pcm_substream *substream) +{ + return i2sbus_hw_free(substream, 1); +} + static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) { /* whee. Hard work now. The user has selected a bitrate @@ -264,7 +330,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) * I2S controller appropriately. */ struct snd_pcm_runtime *runtime; struct dbdma_cmd *command; - int i, periodsize; + int i, periodsize, nperiods; dma_addr_t offset; struct bus_info bi; struct codec_info_item *cii; @@ -274,6 +340,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) struct pcm_info *pi, *other; int cnt; int result = 0; + unsigned int cmd, stopaddr; mutex_lock(&i2sdev->lock); @@ -283,6 +350,13 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) result = -EBUSY; goto out_unlock; } + if (pi->dbdma_ring.stopping) + i2sbus_wait_for_stop(i2sdev, pi); + + if (!pi->substream || !pi->substream->runtime) { + result = -EINVAL; + goto out_unlock; + } runtime = pi->substream->runtime; pi->active = 1; @@ -297,24 +371,43 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) i2sdev->rate = runtime->rate; periodsize = snd_pcm_lib_period_bytes(pi->substream); + nperiods = pi->substream->runtime->periods; pi->current_period = 0; /* generate dbdma command ring first */ command = pi->dbdma_ring.cmds; + memset(command, 0, (nperiods + 2) * sizeof(struct dbdma_cmd)); + + /* commands to DMA to/from the ring */ + /* + * For input, we need to do a graceful stop; if we abort + * the DMA, we end up with leftover bytes that corrupt + * the next recording. To do this we set the S0 status + * bit and wait for the DMA controller to stop. Each + * command has a branch condition to + * make it branch to a stop command if S0 is set. + * On input we also need to wait for the S7 bit to be + * set before turning off the DMA controller. + * In fact we do the graceful stop for output as well. + */ offset = runtime->dma_addr; - for (i = 0; i < pi->substream->runtime->periods; - i++, command++, offset += periodsize) { - memset(command, 0, sizeof(struct dbdma_cmd)); - command->command = - cpu_to_le16((in ? INPUT_MORE : OUTPUT_MORE) | INTR_ALWAYS); + cmd = (in? INPUT_MORE: OUTPUT_MORE) | BR_IFSET | INTR_ALWAYS; + stopaddr = pi->dbdma_ring.bus_cmd_start + + (nperiods + 1) * sizeof(struct dbdma_cmd); + for (i = 0; i < nperiods; i++, command++, offset += periodsize) { + command->command = cpu_to_le16(cmd); + command->cmd_dep = cpu_to_le32(stopaddr); command->phy_addr = cpu_to_le32(offset); command->req_count = cpu_to_le16(periodsize); - command->xfer_status = cpu_to_le16(0); } - /* last one branches back to first */ - command--; - command->command |= cpu_to_le16(BR_ALWAYS); + + /* branch back to beginning of ring */ + command->command = cpu_to_le16(DBDMA_NOP | BR_ALWAYS); command->cmd_dep = cpu_to_le32(pi->dbdma_ring.bus_cmd_start); + command++; + + /* set stop command */ + command->command = cpu_to_le16(DBDMA_STOP); /* ok, let's set the serial format and stuff */ switch (runtime->format) { @@ -435,16 +528,18 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) return result; } -static struct dbdma_cmd STOP_CMD = { - .command = __constant_cpu_to_le16(DBDMA_STOP), -}; +#ifdef CONFIG_PM +void i2sbus_pcm_prepare_both(struct i2sbus_dev *i2sdev) +{ + i2sbus_pcm_prepare(i2sdev, 0); + i2sbus_pcm_prepare(i2sdev, 1); +} +#endif static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd) { struct codec_info_item *cii; struct pcm_info *pi; - int timeout; - struct dbdma_cmd tmp; int result = 0; unsigned long flags; @@ -464,92 +559,50 @@ static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd) cii->codec->start(cii, pi->substream); pi->dbdma_ring.running = 1; - /* reset dma engine */ - out_le32(&pi->dbdma->control, - 0 | (RUN | PAUSE | FLUSH | WAKE) << 16); - timeout = 100; - while (in_le32(&pi->dbdma->status) & RUN && timeout--) - udelay(1); - if (timeout <= 0) { - printk(KERN_ERR - "i2sbus: error waiting for dma reset\n"); - result = -ENXIO; - goto out_unlock; + if (pi->dbdma_ring.stopping) { + /* Clear the S0 bit, then see if we stopped yet */ + out_le32(&pi->dbdma->control, 1 << 16); + if (in_le32(&pi->dbdma->status) & ACTIVE) { + /* possible race here? */ + udelay(10); + if (in_le32(&pi->dbdma->status) & ACTIVE) { + pi->dbdma_ring.stopping = 0; + goto out_unlock; /* keep running */ + } + } } + /* make sure RUN, PAUSE and S0 bits are cleared */ + out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16); + + /* set branch condition select register */ + out_le32(&pi->dbdma->br_sel, (1 << 16) | 1); + /* write dma command buffer address to the dbdma chip */ out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start); - /* post PCI write */ - mb(); - (void)in_le32(&pi->dbdma->status); - - /* change first command to STOP */ - tmp = *pi->dbdma_ring.cmds; - *pi->dbdma_ring.cmds = STOP_CMD; - - /* set running state, remember that the first command is STOP */ - out_le32(&pi->dbdma->control, RUN | (RUN << 16)); - timeout = 100; - /* wait for STOP to be executed */ - while (in_le32(&pi->dbdma->status) & ACTIVE && timeout--) - udelay(1); - if (timeout <= 0) { - printk(KERN_ERR "i2sbus: error waiting for dma stop\n"); - result = -ENXIO; - goto out_unlock; - } - /* again, write dma command buffer address to the dbdma chip, - * this time of the first real command */ - *pi->dbdma_ring.cmds = tmp; - out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start); - /* post write */ - mb(); - (void)in_le32(&pi->dbdma->status); - - /* reset dma engine again */ - out_le32(&pi->dbdma->control, - 0 | (RUN | PAUSE | FLUSH | WAKE) << 16); - timeout = 100; - while (in_le32(&pi->dbdma->status) & RUN && timeout--) - udelay(1); - if (timeout <= 0) { - printk(KERN_ERR - "i2sbus: error waiting for dma reset\n"); - result = -ENXIO; - goto out_unlock; - } - /* wake up the chip with the next descriptor */ - out_le32(&pi->dbdma->control, - (RUN | WAKE) | ((RUN | WAKE) << 16)); - /* get the frame count */ + /* initialize the frame count and current period */ + pi->current_period = 0; pi->frame_count = in_le32(&i2sdev->intfregs->frame_count); + /* set the DMA controller running */ + out_le32(&pi->dbdma->control, (RUN << 16) | RUN); + /* off you go! */ break; + case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: if (!pi->dbdma_ring.running) { result = -EALREADY; goto out_unlock; } + pi->dbdma_ring.running = 0; - /* turn off all relevant bits */ - out_le32(&pi->dbdma->control, - (RUN | WAKE | FLUSH | PAUSE) << 16); - { - /* FIXME: move to own function */ - int timeout = 5000; - while ((in_le32(&pi->dbdma->status) & RUN) - && --timeout > 0) - udelay(1); - if (!timeout) - printk(KERN_ERR - "i2sbus: timed out turning " - "off dbdma engine!\n"); - } + /* Set the S0 bit to make the DMA branch to the stop cmd */ + out_le32(&pi->dbdma->control, (1 << 16) | 1); + pi->dbdma_ring.stopping = 1; - pi->dbdma_ring.running = 0; list_for_each_entry(cii, &i2sdev->sound.codec_list, list) if (cii->codec->stop) cii->codec->stop(cii, pi->substream); @@ -574,81 +627,93 @@ static snd_pcm_uframes_t i2sbus_pcm_pointer(struct i2sbus_dev *i2sdev, int in) fc = in_le32(&i2sdev->intfregs->frame_count); fc = fc - pi->frame_count; - return (bytes_to_frames(pi->substream->runtime, - pi->current_period * - snd_pcm_lib_period_bytes(pi->substream)) - + fc) % pi->substream->runtime->buffer_size; + if (fc >= pi->substream->runtime->buffer_size) + fc %= pi->substream->runtime->buffer_size; + return fc; } static inline void handle_interrupt(struct i2sbus_dev *i2sdev, int in) { struct pcm_info *pi; - u32 fc; - u32 delta; + u32 fc, nframes; + u32 status; + int timeout, i; + int dma_stopped = 0; + struct snd_pcm_runtime *runtime; spin_lock(&i2sdev->low_lock); get_pcm_info(i2sdev, in, &pi, NULL); - - if (!pi->dbdma_ring.running) { - /* there was still an interrupt pending - * while we stopped. or maybe another - * processor (not the one that was stopping - * the DMA engine) was spinning above - * waiting for the lock. */ + if (!pi->dbdma_ring.running && !pi->dbdma_ring.stopping) goto out_unlock; - } - fc = in_le32(&i2sdev->intfregs->frame_count); - /* a counter overflow does not change the calculation. */ - delta = fc - pi->frame_count; - - /* update current_period */ - while (delta >= pi->substream->runtime->period_size) { - pi->current_period++; - delta = delta - pi->substream->runtime->period_size; - } - - if (unlikely(delta)) { - /* Some interrupt came late, so check the dbdma. - * This special case exists to syncronize the frame_count with - * the dbdma transfer, but is hit every once in a while. */ - int period; - - period = (in_le32(&pi->dbdma->cmdptr) - - pi->dbdma_ring.bus_cmd_start) - / sizeof(struct dbdma_cmd); - pi->current_period = pi->current_period - % pi->substream->runtime->periods; - - while (pi->current_period != period) { - pi->current_period++; - pi->current_period %= pi->substream->runtime->periods; - /* Set delta to zero, as the frame_count value is too - * high (otherwise the code path will not be executed). - * This corrects the fact that the frame_count is too - * low at the beginning due to buffering. */ - delta = 0; + i = pi->current_period; + runtime = pi->substream->runtime; + while (pi->dbdma_ring.cmds[i].xfer_status) { + if (le16_to_cpu(pi->dbdma_ring.cmds[i].xfer_status) & BT) + /* + * BT is the branch taken bit. If it took a branch + * it is because we set the S0 bit to make it + * branch to the stop command. + */ + dma_stopped = 1; + pi->dbdma_ring.cmds[i].xfer_status = 0; + + if (++i >= runtime->periods) { + i = 0; + pi->frame_count += runtime->buffer_size; } + pi->current_period = i; + + /* + * Check the frame count. The DMA tends to get a bit + * ahead of the frame counter, which confuses the core. + */ + fc = in_le32(&i2sdev->intfregs->frame_count); + nframes = i * runtime->period_size; + if (fc < pi->frame_count + nframes) + pi->frame_count = fc - nframes; } - pi->frame_count = fc - delta; - pi->current_period %= pi->substream->runtime->periods; + if (dma_stopped) { + timeout = 1000; + for (;;) { + status = in_le32(&pi->dbdma->status); + if (!(status & ACTIVE) && (!in || (status & 0x80))) + break; + if (--timeout <= 0) { + printk(KERN_ERR "i2sbus: timed out " + "waiting for DMA to stop!\n"); + break; + } + udelay(1); + } + + /* Turn off DMA controller, clear S0 bit */ + out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16); + + pi->dbdma_ring.stopping = 0; + if (pi->stop_completion) + complete(pi->stop_completion); + } + if (!pi->dbdma_ring.running) + goto out_unlock; spin_unlock(&i2sdev->low_lock); /* may call _trigger again, hence needs to be unlocked */ snd_pcm_period_elapsed(pi->substream); return; + out_unlock: spin_unlock(&i2sdev->low_lock); } -irqreturn_t i2sbus_tx_intr(int irq, void *devid, struct pt_regs *regs) +irqreturn_t i2sbus_tx_intr(int irq, void *devid) { handle_interrupt((struct i2sbus_dev *)devid, 0); return IRQ_HANDLED; } -irqreturn_t i2sbus_rx_intr(int irq, void *devid, struct pt_regs * regs) +irqreturn_t i2sbus_rx_intr(int irq, void *devid) { handle_interrupt((struct i2sbus_dev *)devid, 1); return IRQ_HANDLED; @@ -718,7 +783,7 @@ static struct snd_pcm_ops i2sbus_playback_ops = { .close = i2sbus_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = i2sbus_hw_params, - .hw_free = i2sbus_hw_free, + .hw_free = i2sbus_playback_hw_free, .prepare = i2sbus_playback_prepare, .trigger = i2sbus_playback_trigger, .pointer = i2sbus_playback_pointer, @@ -788,7 +853,7 @@ static struct snd_pcm_ops i2sbus_record_ops = { .close = i2sbus_record_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = i2sbus_hw_params, - .hw_free = i2sbus_hw_free, + .hw_free = i2sbus_record_hw_free, .prepare = i2sbus_record_prepare, .trigger = i2sbus_record_trigger, .pointer = i2sbus_record_pointer, @@ -812,7 +877,6 @@ static void i2sbus_private_free(struct snd_pcm *pcm) module_put(THIS_MODULE); } -/* FIXME: this function needs an error handling strategy with labels */ int i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, struct codec_info *ci, void *data) @@ -880,41 +944,31 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, if (!cii->sdev) { printk(KERN_DEBUG "i2sbus: failed to get soundbus dev reference\n"); - kfree(cii); - return -ENODEV; + err = -ENODEV; + goto out_free_cii; } if (!try_module_get(THIS_MODULE)) { printk(KERN_DEBUG "i2sbus: failed to get module reference!\n"); - soundbus_dev_put(dev); - kfree(cii); - return -EBUSY; + err = -EBUSY; + goto out_put_sdev; } if (!try_module_get(ci->owner)) { printk(KERN_DEBUG "i2sbus: failed to get module reference to codec owner!\n"); - module_put(THIS_MODULE); - soundbus_dev_put(dev); - kfree(cii); - return -EBUSY; + err = -EBUSY; + goto out_put_this_module; } if (!dev->pcm) { - err = snd_pcm_new(card, - dev->pcmname, - dev->pcmid, - 0, - 0, + err = snd_pcm_new(card, dev->pcmname, dev->pcmid, 0, 0, &dev->pcm); if (err) { printk(KERN_DEBUG "i2sbus: failed to create pcm\n"); - kfree(cii); - module_put(ci->owner); - soundbus_dev_put(dev); - module_put(THIS_MODULE); - return err; + goto out_put_ci_module; } + dev->pcm->dev = &dev->ofdev.dev; } /* ALSA yet again sucks. @@ -926,20 +980,12 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, /* eh? */ printk(KERN_ERR "Can't attach same bus to different cards!\n"); - module_put(ci->owner); - kfree(cii); - soundbus_dev_put(dev); - module_put(THIS_MODULE); - return -EINVAL; - } - if ((err = - snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 1))) { - module_put(ci->owner); - kfree(cii); - soundbus_dev_put(dev); - module_put(THIS_MODULE); - return err; + err = -EINVAL; + goto out_put_ci_module; } + err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 1); + if (err) + goto out_put_ci_module; snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, &i2sbus_playback_ops); i2sdev->out.created = 1; @@ -949,20 +995,12 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, if (dev->pcm->card != card) { printk(KERN_ERR "Can't attach same bus to different cards!\n"); - module_put(ci->owner); - kfree(cii); - soundbus_dev_put(dev); - module_put(THIS_MODULE); - return -EINVAL; - } - if ((err = - snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1))) { - module_put(ci->owner); - kfree(cii); - soundbus_dev_put(dev); - module_put(THIS_MODULE); - return err; + err = -EINVAL; + goto out_put_ci_module; } + err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1); + if (err) + goto out_put_ci_module; snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, &i2sbus_record_ops); i2sdev->in.created = 1; @@ -977,11 +1015,7 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, err = snd_device_register(card, dev->pcm); if (err) { printk(KERN_ERR "i2sbus: error registering new pcm\n"); - module_put(ci->owner); - kfree(cii); - soundbus_dev_put(dev); - module_put(THIS_MODULE); - return err; + goto out_put_ci_module; } /* no errors any more, so let's add this to our list */ list_add(&cii->list, &dev->codec_list); @@ -996,6 +1030,15 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, 64 * 1024, 64 * 1024); return 0; + out_put_ci_module: + module_put(ci->owner); + out_put_this_module: + module_put(THIS_MODULE); + out_put_sdev: + soundbus_dev_put(dev); + out_free_cii: + kfree(cii); + return err; } void i2sbus_detach_codec(struct soundbus_dev *dev, void *data) diff --git a/sound/aoa/soundbus/soundbus.h b/sound/aoa/soundbus/soundbus.h index 5c27297835d..adecbf36f4f 100644 --- a/sound/aoa/soundbus/soundbus.h +++ b/sound/aoa/soundbus/soundbus.h @@ -8,7 +8,7 @@ #ifndef __SOUNDBUS_H #define __SOUNDBUS_H -#include <asm/of_device.h> +#include <linux/of_device.h> #include <sound/pcm.h> #include <linux/list.h> @@ -141,7 +141,7 @@ struct soundbus_dev { struct list_head onbuslist; /* the of device it represents */ - struct of_device ofdev; + struct platform_device ofdev; /* what modules go by */ char modalias[32]; @@ -199,4 +199,6 @@ struct soundbus_driver { extern int soundbus_register_driver(struct soundbus_driver *drv); extern void soundbus_unregister_driver(struct soundbus_driver *drv); +extern struct device_attribute soundbus_dev_attrs[]; + #endif /* __SOUNDBUS_H */ diff --git a/sound/aoa/soundbus/sysfs.c b/sound/aoa/soundbus/sysfs.c index d31f8146952..e0980b5c2cd 100644 --- a/sound/aoa/soundbus/sysfs.c +++ b/sound/aoa/soundbus/sysfs.c @@ -1,4 +1,3 @@ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/stat.h> /* FIX UP */ @@ -10,14 +9,14 @@ field##_show (struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ struct soundbus_dev *mdev = to_soundbus_device (dev); \ - return sprintf (buf, format_string, mdev->ofdev.node->field); \ + return sprintf (buf, format_string, mdev->ofdev.dev.of_node->field); \ } static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { struct soundbus_dev *sdev = to_soundbus_device(dev); - struct of_device *of = &sdev->ofdev; + struct platform_device *of = &sdev->ofdev; int length; if (*sdev->modalias) { @@ -26,7 +25,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, length = strlen(buf); } else { length = sprintf(buf, "of:N%sT%s\n", - of->node->name, of->node->type); + of->dev.of_node->name, of->dev.of_node->type); } return length; |
