aboutsummaryrefslogtreecommitdiff
path: root/sound/aoa
diff options
context:
space:
mode:
Diffstat (limited to 'sound/aoa')
-rw-r--r--sound/aoa/Kconfig11
-rw-r--r--sound/aoa/aoa-gpio.h4
-rw-r--r--sound/aoa/aoa.h6
-rw-r--r--sound/aoa/codecs/Kconfig12
-rw-r--r--sound/aoa/codecs/Makefile4
-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/Makefile8
-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/Kconfig1
-rw-r--r--sound/aoa/fabrics/Makefile2
-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/Kconfig1
-rw-r--r--sound/aoa/soundbus/core.c85
-rw-r--r--sound/aoa/soundbus/i2sbus/Makefile2
-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.h12
-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.h6
-rw-r--r--sound/aoa/soundbus/sysfs.c7
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(&notif->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(&notif->work);
+ schedule_delayed_work(&notif->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(&notif->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(&notif->work);
+ schedule_delayed_work(&notif->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;