aboutsummaryrefslogtreecommitdiff
path: root/sound/aoa/soundbus
diff options
context:
space:
mode:
Diffstat (limited to 'sound/aoa/soundbus')
-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
10 files changed, 341 insertions, 310 deletions
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;