LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 00/12] snd-aoa: add snd-aoa
From: Johannes Berg @ 2006-06-07 13:09 UTC (permalink / raw)
  To: alsa-devel; +Cc: linuxppc-dev, netstar

The following patches would add snd-aoa to the Linux source tree. I'm
posting them here now for some last round of review before I finally
post them with a Signed-Off-By and Changelog for inclusion. As you
previously asked about this, I don't think it makes sense to commit
these patches one by one, but the first one removing layout-id support
from snd-powermac might be worth singling out.
Please let me know either way so that I provide either one or two
changelog entries.

snd-aoa currently handles all Apple machines that have a layout-id
property in the sound node in the device-tree and doesn't attempt to
handle any others yet, a bunch of layout-ids are untested by users,
support for those was added based on the Info.plist file OSX has.

The only thing that still needs to be done is getting it to work on the
PowerMac7,2/7,3 where the device tree has a bug that apparently prevents
the i2sbus module from working properly.

Also, the DRC feature of the tas3004 codec should be exposed like
snd-powermac exposes it, so there'll probably be one more round of
patches before I will finally ask for inclusion.

Other than that, it replaces snd-powermac for all layout-id machines, and
handles many more already, so I have also added a patch to snd-powermac
to this patchset that removes support for any machines with a layout-id
it, allowing snd-aoa to take over for those machines even if both are
compiled in. On affected machines, snd-powermac will also give the hint
that snd-aoa should be used instead, but if snd-aoa is compiled as modules
it will load automatically.

Thanks for previous review, I hope I have incorporated all suggestions.
Over the previous version, the major change is adding the file
core/snd-aoa-gpio-feature.c which contains GPIO access routines for
those machine that don't have platform functions for it.

Any further comments are appreciated.

For the curious (like me :) ) here's a diffstat for the whole series:
 sound/Kconfig                                   |    2 
 sound/Makefile                                  |    2 
 sound/aoa/Kconfig                               |   17 
 sound/aoa/Makefile                              |    4 
 sound/aoa/aoa-gpio.h                            |   81 +
 sound/aoa/aoa.h                                 |  131 ++
 sound/aoa/codecs/Kconfig                        |   32 
 sound/aoa/codecs/Makefile                       |    3 
 sound/aoa/codecs/snd-aoa-codec-onyx.c           | 1113 ++++++++++++++++++++++++
 sound/aoa/codecs/snd-aoa-codec-onyx.h           |   76 +
 sound/aoa/codecs/snd-aoa-codec-tas-gain-table.h |  209 ++++
 sound/aoa/codecs/snd-aoa-codec-tas.c            |  653 ++++++++++++++
 sound/aoa/codecs/snd-aoa-codec-tas.h            |   47 +
 sound/aoa/codecs/snd-aoa-codec-toonie.c         |  140 +++
 sound/aoa/core/Makefile                         |    5 
 sound/aoa/core/snd-aoa-alsa.c                   |   98 ++
 sound/aoa/core/snd-aoa-alsa.h                   |   16 
 sound/aoa/core/snd-aoa-core.c                   |  156 +++
 sound/aoa/core/snd-aoa-gpio-feature.c           |  392 ++++++++
 sound/aoa/core/snd-aoa-gpio-pmf.c               |  246 +++++
 sound/aoa/fabrics/Kconfig                       |   12 
 sound/aoa/fabrics/Makefile                      |    1 
 sound/aoa/fabrics/snd-aoa-fabric-layout.c       | 1096 +++++++++++++++++++++++
 sound/aoa/soundbus/Kconfig                      |   14 
 sound/aoa/soundbus/Makefile                     |    3 
 sound/aoa/soundbus/core.c                       |  250 +++++
 sound/aoa/soundbus/i2sbus/Makefile              |    2 
 sound/aoa/soundbus/i2sbus/i2sbus-control.c      |  192 ++++
 sound/aoa/soundbus/i2sbus/i2sbus-control.h      |   37 
 sound/aoa/soundbus/i2sbus/i2sbus-core.c         |  387 ++++++++
 sound/aoa/soundbus/i2sbus/i2sbus-interface.h    |  187 ++++
 sound/aoa/soundbus/i2sbus/i2sbus-pcm.c          | 1017 +++++++++++++++++++++
 sound/aoa/soundbus/i2sbus/i2sbus.h              |  112 ++
 sound/aoa/soundbus/soundbus.h                   |  202 ++++
 sound/aoa/soundbus/sysfs.c                      |   43 
 sound/ppc/Makefile                              |    2 
 sound/ppc/pmac.c                                |   44 
 sound/ppc/pmac.h                                |    3 
 sound/ppc/powermac.c                            |    7 
 sound/ppc/toonie.c                              |  378 --------
 40 files changed, 6987 insertions(+), 425 deletions(-)


--

^ permalink raw reply

* [RFC 05/12] snd-aoa: add i2sbus
From: Johannes Berg @ 2006-06-07 13:09 UTC (permalink / raw)
  To: alsa-devel; +Cc: linuxppc-dev, netstar
In-Reply-To: <20060607130909.584205000@sipsolutions.net>

This patch adds the required files for i2sbus (the Makefile hook and the
Kconfig item were already added in the previous patch). i2sbus is the module
that handles the i2s busses on newer Apple machines, currently only handling
those machines that have all the required information in the device tree.

--- /dev/null
+++ b/sound/aoa/soundbus/i2sbus/i2sbus-control.h
@@ -0,0 +1,37 @@
+/*
+ * i2sbus driver -- bus register definitions
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#ifndef __I2SBUS_CONTROLREGS_H
+#define __I2SBUS_CONTROLREGS_H
+
+/* i2s control registers, at least what we know about them */
+
+#define __PAD(m,n) u8 __pad##m[n]
+#define _PAD(line, n) __PAD(line, n)
+#define PAD(n) _PAD(__LINE__, (n))
+struct i2s_control_regs {
+	PAD(0x38);
+	__le32 fcr0;		/* 0x38 (unknown) */
+	__le32 cell_control;	/* 0x3c (fcr1) */
+	__le32 fcr2;		/* 0x40 (unknown) */
+	__le32 fcr3;		/* 0x44 (fcr3) */
+	__le32 clock_control;	/* 0x48 (unknown) */
+	PAD(4);
+	/* total size: 0x50 bytes */
+}  __attribute__((__packed__));
+
+#define CTRL_CLOCK_CELL_0_ENABLE	(1<<10)
+#define CTRL_CLOCK_CLOCK_0_ENABLE	(1<<12)
+#define CTRL_CLOCK_SWRESET_0		(1<<11)
+#define CTRL_CLOCK_INTF_0_ENABLE	(1<<13)
+
+#define CTRL_CLOCK_CELL_1_ENABLE	(1<<17)
+#define CTRL_CLOCK_CLOCK_1_ENABLE	(1<<18)
+#define CTRL_CLOCK_SWRESET_1		(1<<19)
+#define CTRL_CLOCK_INTF_1_ENABLE	(1<<20)
+
+#endif /* __I2SBUS_CONTROLREGS_H */
--- /dev/null
+++ b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c
@@ -0,0 +1,1017 @@
+/*
+ * i2sbus driver -- pcm routines
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#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 <sound/core.h>
+#include <asm/macio.h>
+#include <linux/pci.h>
+#include "../soundbus.h"
+#include "i2sbus.h"
+
+static inline void get_pcm_info(struct i2sbus_dev *i2sdev, int in,
+				struct pcm_info **pi, struct pcm_info **other)
+{
+	if (in) {
+		if (pi)
+			*pi = &i2sdev->in;
+		if (other)
+			*other = &i2sdev->out;
+	} else {
+		if (pi)
+			*pi = &i2sdev->out;
+		if (other)
+			*other = &i2sdev->in;
+	}
+}
+
+static int clock_and_divisors(int mclk, int sclk, int rate, int *out)
+{
+	/* sclk must be derived from mclk! */
+	if (mclk % sclk)
+		return -1;
+	/* derive sclk register value */
+	if (i2s_sf_sclkdiv(mclk / sclk, out))
+		return -1;
+
+	if (I2S_CLOCK_SPEED_18MHz % (rate * mclk) == 0) {
+		if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_18MHz / rate / mclk, out)) {
+			*out |= I2S_SF_CLOCK_SOURCE_18MHz;
+			return 0;
+		}
+	}
+	if (I2S_CLOCK_SPEED_45MHz % (rate * mclk) == 0) {
+		if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_45MHz / rate / mclk, out)) {
+			*out |= I2S_SF_CLOCK_SOURCE_45MHz;
+			return 0;
+		}
+	}
+	if (I2S_CLOCK_SPEED_49MHz % (rate * mclk) == 0) {
+		if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_49MHz / rate / mclk, out)) {
+			*out |= I2S_SF_CLOCK_SOURCE_49MHz;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+#define CHECK_RATE(rate)						\
+	do { if (rates & SNDRV_PCM_RATE_ ##rate) {			\
+		int dummy;						\
+		if (clock_and_divisors(sysclock_factor,			\
+				       bus_factor, rate, &dummy))	\
+			rates &= ~SNDRV_PCM_RATE_ ##rate;		\
+	} } while (0)
+
+static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
+{
+	struct pcm_info *pi, *other;
+	struct soundbus_dev *sdev;
+	int masks_inited = 0, err;
+	struct codec_info_item *cii, *rev;
+	struct snd_pcm_hardware *hw;
+	u64 formats = 0;
+	unsigned int rates = 0;
+	struct transfer_info v;
+	int result = 0;
+	int bus_factor = 0, sysclock_factor = 0;
+	int found_this;
+
+	mutex_lock(&i2sdev->lock);
+
+	get_pcm_info(i2sdev, in, &pi, &other);
+
+	hw = &pi->substream->runtime->hw;
+	sdev = &i2sdev->sound;
+
+	if (pi->active) {
+		/* alsa messed up */
+		result = -EBUSY;
+		goto out_unlock;
+	}
+
+	/* we now need to assign the hw */
+	list_for_each_entry(cii, &sdev->codec_list, list) {
+		struct transfer_info *ti = cii->codec->transfers;
+		bus_factor = cii->codec->bus_factor;
+		sysclock_factor = cii->codec->sysclock_factor;
+		while (ti->formats && ti->rates) {
+			v = *ti;
+			if (ti->transfer_in == in
+			    && cii->codec->usable(cii, ti, &v)) {
+				if (masks_inited) {
+					formats &= v.formats;
+					rates &= v.rates;
+				} else {
+					formats = v.formats;
+					rates = v.rates;
+					masks_inited = 1;
+				}
+			}
+			ti++;
+		}
+	}
+	if (!masks_inited || !bus_factor || !sysclock_factor) {
+		result = -ENODEV;
+		goto out_unlock;
+	}
+	/* bus dependent stuff */
+	hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+		   SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME;
+
+	CHECK_RATE(5512);
+	CHECK_RATE(8000);
+	CHECK_RATE(11025);
+	CHECK_RATE(16000);
+	CHECK_RATE(22050);
+	CHECK_RATE(32000);
+	CHECK_RATE(44100);
+	CHECK_RATE(48000);
+	CHECK_RATE(64000);
+	CHECK_RATE(88200);
+	CHECK_RATE(96000);
+	CHECK_RATE(176400);
+	CHECK_RATE(192000);
+	hw->rates = rates;
+
+	/* well. the codec might want 24 bits only, and we'll
+	 * ever only transfer 24 bits, but they are top-aligned!
+	 * So for alsa, we claim that we're doing full 32 bit
+	 * while in reality we'll ignore the lower 8 bits of
+	 * that when doing playback (they're transferred as 0
+	 * as far as I know, no codecs we have are 32-bit capable
+	 * so I can't really test) and when doing recording we'll
+	 * always have those lower 8 bits recorded as 0 */
+	if (formats & SNDRV_PCM_FMTBIT_S24_BE)
+		formats |= SNDRV_PCM_FMTBIT_S32_BE;
+	if (formats & SNDRV_PCM_FMTBIT_U24_BE)
+		formats |= SNDRV_PCM_FMTBIT_U32_BE;
+	/* now mask off what we can support. I suppose we could
+	 * also support S24_3LE and some similar formats, but I
+	 * doubt there's a codec that would be able to use that,
+	 * so we don't support it here. */
+	hw->formats = formats & (SNDRV_PCM_FMTBIT_S16_BE |
+				 SNDRV_PCM_FMTBIT_U16_BE |
+				 SNDRV_PCM_FMTBIT_S32_BE |
+				 SNDRV_PCM_FMTBIT_U32_BE);
+
+	/* we need to set the highest and lowest rate possible.
+	 * These are the highest and lowest rates alsa can
+	 * support properly in its bitfield.
+	 * Below, we'll use that to restrict to the rate
+	 * currently in use (if any). */
+	hw->rate_min = 5512;
+	hw->rate_max = 192000;
+	/* if the other stream is active, then we can only
+	 * support what it is currently using.
+	 * FIXME: I lied. This comment is wrong. We can support
+	 * anything that works with the same serial format, ie.
+	 * when recording 24 bit sound we can well play 16 bit
+	 * sound at the same time iff using the same transfer mode.
+	 */
+	if (other->active) {
+		/* FIXME: is this guaranteed by the alsa api? */
+		hw->formats &= (1ULL << i2sdev->format);
+		/* see above, restrict rates to the one we already have */
+		hw->rate_min = i2sdev->rate;
+		hw->rate_max = i2sdev->rate;
+	}
+
+	hw->channels_min = 2;
+	hw->channels_max = 2;
+	/* these are somewhat arbitrary */
+	hw->buffer_bytes_max = 131072;
+	hw->period_bytes_min = 256;
+	hw->period_bytes_max = 16384;
+	hw->periods_min = 3;
+	hw->periods_max = MAX_DBDMA_COMMANDS;
+	list_for_each_entry(cii, &sdev->codec_list, list) {
+		if (cii->codec->open) {
+			err = cii->codec->open(cii, pi->substream);
+			if (err) {
+				result = err;
+				/* unwind */
+				found_this = 0;
+				list_for_each_entry_reverse(rev,
+				    &sdev->codec_list, list) {
+					if (found_this && rev->codec->close) {
+						rev->codec->close(rev,
+								pi->substream);
+					}
+					if (rev == cii)
+						found_this = 1;
+				}
+				goto out_unlock;
+			}
+		}
+	}
+
+ out_unlock:
+	mutex_unlock(&i2sdev->lock);
+	return result;
+}
+
+#undef CHECK_RATE
+
+static int i2sbus_pcm_close(struct i2sbus_dev *i2sdev, int in)
+{
+	struct codec_info_item *cii;
+	struct pcm_info *pi;
+	int err = 0, tmp;
+
+	mutex_lock(&i2sdev->lock);
+
+	get_pcm_info(i2sdev, in, &pi, NULL);
+
+	list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
+		if (cii->codec->close) {
+			tmp = cii->codec->close(cii, pi->substream);
+			if (tmp)
+				err = tmp;
+		}
+	}
+
+	pi->substream = NULL;
+	pi->active = 0;
+	mutex_unlock(&i2sdev->lock);
+	return err;
+}
+
+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)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
+{
+	/* whee. Hard work now. The user has selected a bitrate
+	 * and bit format, so now we have to program our
+	 * I2S controller appropriately. */
+	struct snd_pcm_runtime *runtime;
+	struct dbdma_cmd *command;
+	int i, periodsize;
+	dma_addr_t offset;
+	struct bus_info bi;
+	struct codec_info_item *cii;
+	int sfr = 0;		/* serial format register */
+	int dws = 0;		/* data word sizes reg */
+	int input_16bit;
+	struct pcm_info *pi, *other;
+	int cnt;
+	int result = 0;
+
+	mutex_lock(&i2sdev->lock);
+
+	get_pcm_info(i2sdev, in, &pi, &other);
+
+	if (pi->dbdma_ring.running) {
+		result = -EBUSY;
+		goto out_unlock;
+	}
+
+	runtime = pi->substream->runtime;
+	pi->active = 1;
+	if (other->active &&
+	    ((i2sdev->format != runtime->format)
+	     || (i2sdev->rate != runtime->rate))) {
+		result = -EINVAL;
+		goto out_unlock;
+	}
+
+	i2sdev->format = runtime->format;
+	i2sdev->rate = runtime->rate;
+
+	periodsize = snd_pcm_lib_period_bytes(pi->substream);
+	pi->current_period = 0;
+
+	/* generate dbdma command ring first */
+	command = pi->dbdma_ring.cmds;
+	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);
+		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);
+	command->cmd_dep = cpu_to_le32(pi->dbdma_ring.bus_cmd_start);
+
+	/* ok, let's set the serial format and stuff */
+	switch (runtime->format) {
+	/* 16 bit formats */
+	case SNDRV_PCM_FORMAT_S16_BE:
+	case SNDRV_PCM_FORMAT_U16_BE:
+		/* FIXME: if we add different bus factors we need to
+		 * do more here!! */
+		bi.bus_factor = 0;
+		list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
+			bi.bus_factor = cii->codec->bus_factor;
+			break;
+		}
+		if (!bi.bus_factor) {
+			result = -ENODEV;
+			goto out_unlock;
+		}
+		input_16bit = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S32_BE:
+	case SNDRV_PCM_FORMAT_U32_BE:
+		/* force 64x bus speed, otherwise the data cannot be
+		 * transferred quickly enough! */
+		bi.bus_factor = 64;
+		input_16bit = 0;
+		break;
+	default:
+		result = -EINVAL;
+		goto out_unlock;
+	}
+	/* we assume all sysclocks are the same! */
+	list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
+		bi.sysclock_factor = cii->codec->sysclock_factor;
+		break;
+	}
+
+	if (clock_and_divisors(bi.sysclock_factor,
+			       bi.bus_factor,
+			       runtime->rate,
+			       &sfr) < 0) {
+		result = -EINVAL;
+		goto out_unlock;
+	}
+	switch (bi.bus_factor) {
+	case 32:
+		sfr |= I2S_SF_SERIAL_FORMAT_I2S_32X;
+		break;
+	case 64:
+		sfr |= I2S_SF_SERIAL_FORMAT_I2S_64X;
+		break;
+	}
+	/* FIXME: THIS ASSUMES MASTER ALL THE TIME */
+	sfr |= I2S_SF_SCLK_MASTER;
+
+	list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
+		int err = 0;
+		if (cii->codec->prepare)
+			err = cii->codec->prepare(cii, &bi, pi->substream);
+		if (err) {
+			result = err;
+			goto out_unlock;
+		}
+	}
+	/* codecs are fine with it, so set our clocks */
+	if (input_16bit)
+		dws =	(2 << I2S_DWS_NUM_CHANNELS_IN_SHIFT) |
+			(2 << I2S_DWS_NUM_CHANNELS_OUT_SHIFT) |
+			I2S_DWS_DATA_IN_16BIT | I2S_DWS_DATA_OUT_16BIT;
+	else
+		dws =	(2 << I2S_DWS_NUM_CHANNELS_IN_SHIFT) |
+			(2 << I2S_DWS_NUM_CHANNELS_OUT_SHIFT) |
+			I2S_DWS_DATA_IN_24BIT | I2S_DWS_DATA_OUT_24BIT;
+
+	/* early exit if already programmed correctly */
+	/* not locking these is fine since we touch them only in this function */
+	if (in_le32(&i2sdev->intfregs->serial_format) == sfr
+	 && in_le32(&i2sdev->intfregs->data_word_sizes) == dws)
+		goto out_unlock;
+
+	/* let's notify the codecs about clocks going away.
+	 * For now we only do mastering on the i2s cell... */
+	list_for_each_entry(cii, &i2sdev->sound.codec_list, list)
+		if (cii->codec->switch_clock)
+			cii->codec->switch_clock(cii, CLOCK_SWITCH_PREPARE_SLAVE);
+
+	i2sbus_control_enable(i2sdev->control, i2sdev);
+	i2sbus_control_cell(i2sdev->control, i2sdev, 1);
+
+	out_le32(&i2sdev->intfregs->intr_ctl, I2S_PENDING_CLOCKS_STOPPED);
+
+	i2sbus_control_clock(i2sdev->control, i2sdev, 0);
+
+	msleep(1);
+
+	/* wait for clock stopped. This can apparently take a while... */
+	cnt = 100;
+	while (cnt-- &&
+	    !(in_le32(&i2sdev->intfregs->intr_ctl) & I2S_PENDING_CLOCKS_STOPPED)) {
+		msleep(5);
+	}
+	out_le32(&i2sdev->intfregs->intr_ctl, I2S_PENDING_CLOCKS_STOPPED);
+
+	/* not locking these is fine since we touch them only in this function */
+	out_le32(&i2sdev->intfregs->serial_format, sfr);
+	out_le32(&i2sdev->intfregs->data_word_sizes, dws);
+
+        i2sbus_control_enable(i2sdev->control, i2sdev);
+        i2sbus_control_cell(i2sdev->control, i2sdev, 1);
+        i2sbus_control_clock(i2sdev->control, i2sdev, 1);
+	msleep(1);
+
+	list_for_each_entry(cii, &i2sdev->sound.codec_list, list)
+		if (cii->codec->switch_clock)
+			cii->codec->switch_clock(cii, CLOCK_SWITCH_SLAVE);
+
+ out_unlock:
+	mutex_unlock(&i2sdev->lock);
+	return result;
+}
+
+static struct dbdma_cmd STOP_CMD = {
+	.command = __constant_cpu_to_le16(DBDMA_STOP),
+};
+
+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;
+
+	spin_lock_irqsave(&i2sdev->low_lock, flags);
+
+	get_pcm_info(i2sdev, in, &pi, NULL);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (pi->dbdma_ring.running) {
+			result = -EALREADY;
+			goto out_unlock;
+		}
+		list_for_each_entry(cii, &i2sdev->sound.codec_list, list)
+			if (cii->codec->start)
+				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;
+		}
+
+		/* 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  */
+		pi->frame_count = in_le32(&i2sdev->intfregs->frame_count);
+
+		/* off you go! */
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		if (!pi->dbdma_ring.running) {
+			result = -EALREADY;
+			goto out_unlock;
+		}
+
+		/* 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");
+		}
+
+		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);
+		break;
+	default:
+		result = -EINVAL;
+		goto out_unlock;
+	}
+
+ out_unlock:
+	spin_unlock_irqrestore(&i2sdev->low_lock, flags);
+	return result;
+}
+
+static snd_pcm_uframes_t i2sbus_pcm_pointer(struct i2sbus_dev *i2sdev, int in)
+{
+	struct pcm_info *pi;
+	u32 fc;
+
+	get_pcm_info(i2sdev, in, &pi, NULL);
+
+	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;
+}
+
+static inline void handle_interrupt(struct i2sbus_dev *i2sdev, int in)
+{
+	struct pcm_info *pi;
+	u32 fc;
+	u32 delta;
+
+	spin_lock(&i2sdev->low_lock);
+	get_pcm_info(i2sdev, in, &pi, NULL);
+	if (!pi->substream) {
+		printk(KERN_INFO "i2sbus: got %s irq while not active!\n",
+		       in ? "rx" : "tx");
+		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;
+		}
+	}
+
+	pi->frame_count = fc - delta;
+	pi->current_period %= pi->substream->runtime->periods;
+
+	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)
+{
+	handle_interrupt((struct i2sbus_dev *)devid, 0);
+	return IRQ_HANDLED;
+}
+
+irqreturn_t i2sbus_rx_intr(int irq, void *devid, struct pt_regs * regs)
+{
+	handle_interrupt((struct i2sbus_dev *)devid, 1);
+	return IRQ_HANDLED;
+}
+
+static int i2sbus_playback_open(struct snd_pcm_substream *substream)
+{
+	struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+	if (!i2sdev)
+		return -EINVAL;
+	i2sdev->out.substream = substream;
+	return i2sbus_pcm_open(i2sdev, 0);
+}
+
+static int i2sbus_playback_close(struct snd_pcm_substream *substream)
+{
+	struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+	int err;
+
+	if (!i2sdev)
+		return -EINVAL;
+	if (i2sdev->out.substream != substream)
+		return -EINVAL;
+	err = i2sbus_pcm_close(i2sdev, 0);
+	if (!err)
+		i2sdev->out.substream = NULL;
+	return err;
+}
+
+static int i2sbus_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+	if (!i2sdev)
+		return -EINVAL;
+	if (i2sdev->out.substream != substream)
+		return -EINVAL;
+	return i2sbus_pcm_prepare(i2sdev, 0);
+}
+
+static int i2sbus_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+	if (!i2sdev)
+		return -EINVAL;
+	if (i2sdev->out.substream != substream)
+		return -EINVAL;
+	return i2sbus_pcm_trigger(i2sdev, 0, cmd);
+}
+
+static snd_pcm_uframes_t i2sbus_playback_pointer(struct snd_pcm_substream
+						 *substream)
+{
+	struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+	if (!i2sdev)
+		return -EINVAL;
+	if (i2sdev->out.substream != substream)
+		return 0;
+	return i2sbus_pcm_pointer(i2sdev, 0);
+}
+
+static struct snd_pcm_ops i2sbus_playback_ops = {
+	.open =		i2sbus_playback_open,
+	.close =	i2sbus_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	i2sbus_hw_params,
+	.hw_free =	i2sbus_hw_free,
+	.prepare =	i2sbus_playback_prepare,
+	.trigger =	i2sbus_playback_trigger,
+	.pointer =	i2sbus_playback_pointer,
+};
+
+static int i2sbus_record_open(struct snd_pcm_substream *substream)
+{
+	struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+	if (!i2sdev)
+		return -EINVAL;
+	i2sdev->in.substream = substream;
+	return i2sbus_pcm_open(i2sdev, 1);
+}
+
+static int i2sbus_record_close(struct snd_pcm_substream *substream)
+{
+	struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+	int err;
+
+	if (!i2sdev)
+		return -EINVAL;
+	if (i2sdev->in.substream != substream)
+		return -EINVAL;
+	err = i2sbus_pcm_close(i2sdev, 1);
+	if (!err)
+		i2sdev->in.substream = NULL;
+	return err;
+}
+
+static int i2sbus_record_prepare(struct snd_pcm_substream *substream)
+{
+	struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+	if (!i2sdev)
+		return -EINVAL;
+	if (i2sdev->in.substream != substream)
+		return -EINVAL;
+	return i2sbus_pcm_prepare(i2sdev, 1);
+}
+
+static int i2sbus_record_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+	if (!i2sdev)
+		return -EINVAL;
+	if (i2sdev->in.substream != substream)
+		return -EINVAL;
+	return i2sbus_pcm_trigger(i2sdev, 1, cmd);
+}
+
+static snd_pcm_uframes_t i2sbus_record_pointer(struct snd_pcm_substream
+					       *substream)
+{
+	struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+	if (!i2sdev)
+		return -EINVAL;
+	if (i2sdev->in.substream != substream)
+		return 0;
+	return i2sbus_pcm_pointer(i2sdev, 1);
+}
+
+static struct snd_pcm_ops i2sbus_record_ops = {
+	.open =		i2sbus_record_open,
+	.close =	i2sbus_record_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	i2sbus_hw_params,
+	.hw_free =	i2sbus_hw_free,
+	.prepare =	i2sbus_record_prepare,
+	.trigger =	i2sbus_record_trigger,
+	.pointer =	i2sbus_record_pointer,
+};
+
+static void i2sbus_private_free(struct snd_pcm *pcm)
+{
+	struct i2sbus_dev *i2sdev = snd_pcm_chip(pcm);
+	struct codec_info_item *p, *tmp;
+
+	i2sdev->sound.pcm = NULL;
+	i2sdev->out.created = 0;
+	i2sdev->in.created = 0;
+	list_for_each_entry_safe(p, tmp, &i2sdev->sound.codec_list, list) {
+		printk(KERN_ERR "i2sbus: a codec didn't unregister!\n");
+		list_del(&p->list);
+		module_put(p->codec->owner);
+		kfree(p);
+	}
+	soundbus_dev_put(&i2sdev->sound);
+	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)
+{
+	int err, in = 0, out = 0;
+	struct transfer_info *tmp;
+	struct i2sbus_dev *i2sdev = soundbus_dev_to_i2sbus_dev(dev);
+	struct codec_info_item *cii;
+
+	if (!dev->pcmname || dev->pcmid == -1) {
+		printk(KERN_ERR "i2sbus: pcm name and id must be set!\n");
+		return -EINVAL;
+	}
+
+	list_for_each_entry(cii, &dev->codec_list, list) {
+		if (cii->codec_data == data)
+			return -EALREADY;
+	}
+
+	if (!ci->transfers || !ci->transfers->formats
+	    || !ci->transfers->rates || !ci->usable)
+		return -EINVAL;
+
+	/* we currently code the i2s transfer on the clock, and support only
+	 * 32 and 64 */
+	if (ci->bus_factor != 32 && ci->bus_factor != 64)
+		return -EINVAL;
+
+	/* If you want to fix this, you need to keep track of what transport infos
+	 * are to be used, which codecs they belong to, and then fix all the
+	 * sysclock/busclock stuff above to depend on which is usable */
+	list_for_each_entry(cii, &dev->codec_list, list) {
+		if (cii->codec->sysclock_factor != ci->sysclock_factor) {
+			printk(KERN_DEBUG
+			       "cannot yet handle multiple different sysclocks!\n");
+			return -EINVAL;
+		}
+		if (cii->codec->bus_factor != ci->bus_factor) {
+			printk(KERN_DEBUG
+			       "cannot yet handle multiple different bus clocks!\n");
+			return -EINVAL;
+		}
+	}
+
+	tmp = ci->transfers;
+	while (tmp->formats && tmp->rates) {
+		if (tmp->transfer_in)
+			in = 1;
+		else
+			out = 1;
+		tmp++;
+	}
+
+	cii = kzalloc(sizeof(struct codec_info_item), GFP_KERNEL);
+	if (!cii) {
+		printk(KERN_DEBUG "i2sbus: failed to allocate cii\n");
+		return -ENOMEM;
+	}
+
+	/* use the private data to point to the codec info */
+	cii->sdev = soundbus_dev_get(dev);
+	cii->codec = ci;
+	cii->codec_data = data;
+
+	if (!cii->sdev) {
+		printk(KERN_DEBUG
+		       "i2sbus: failed to get soundbus dev reference\n");
+		kfree(cii);
+		return -ENODEV;
+	}
+
+	if (!try_module_get(THIS_MODULE)) {
+		printk(KERN_DEBUG "i2sbus: failed to get module reference!\n");
+		soundbus_dev_put(dev);
+		kfree(cii);
+		return -EBUSY;
+	}
+
+	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;
+	}
+
+	if (!dev->pcm) {
+		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;
+		}
+	}
+
+	/* ALSA yet again sucks.
+	 * If it is ever fixed, remove this line. See below. */
+	out = in = 1;
+
+	if (!i2sdev->out.created && out) {
+		if (dev->pcm->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;
+		}
+		snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK,
+				&i2sbus_playback_ops);
+		i2sdev->out.created = 1;
+	}
+
+	if (!i2sdev->in.created && in) {
+		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;
+		}
+		snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE,
+				&i2sbus_record_ops);
+		i2sdev->in.created = 1;
+	}
+
+	/* so we have to register the pcm after adding any substream
+	 * to it because alsa doesn't create the devices for the
+	 * substreams when we add them later.
+	 * Therefore, force in and out on both busses (above) and
+	 * register the pcm now instead of just after creating it.
+	 */
+	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;
+	}
+	/* no errors any more, so let's add this to our list */
+	list_add(&cii->list, &dev->codec_list);
+
+	dev->pcm->private_data = i2sdev;
+	dev->pcm->private_free = i2sbus_private_free;
+
+	/* well, we really should support scatter/gather DMA */
+	snd_pcm_lib_preallocate_pages_for_all(
+		dev->pcm, SNDRV_DMA_TYPE_DEV,
+		snd_dma_pci_data(macio_get_pci_dev(i2sdev->macio)),
+		64 * 1024, 64 * 1024);
+
+	return 0;
+}
+
+void i2sbus_detach_codec(struct soundbus_dev *dev, void *data)
+{
+	struct codec_info_item *cii = NULL, *i;
+
+	list_for_each_entry(i, &dev->codec_list, list) {
+		if (i->codec_data == data) {
+			cii = i;
+			break;
+		}
+	}
+	if (cii) {
+		list_del(&cii->list);
+		module_put(cii->codec->owner);
+		kfree(cii);
+	}
+	/* no more codecs, but still a pcm? */
+	if (list_empty(&dev->codec_list) && dev->pcm) {
+		/* the actual cleanup is done by the callback above! */
+		snd_device_free(dev->pcm->card, dev->pcm);
+	}
+}
--- /dev/null
+++ b/sound/aoa/soundbus/i2sbus/i2sbus-core.c
@@ -0,0 +1,387 @@
+/*
+ * i2sbus driver
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <linux/module.h>
+#include <asm/macio.h>
+#include <asm/dbdma.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <linux/dma-mapping.h>
+#include "../soundbus.h"
+#include "i2sbus.h"
+
+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 struct of_device_id i2sbus_match[] = {
+	{ .name = "i2s" },
+	{ }
+};
+
+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);
+	/* We use the PCI APIs for now until the generic one gets fixed
+	 * enough or until we get some macio-specific versions
+	 */
+	r->space = dma_alloc_coherent(
+			&macio_get_pci_dev(i2sdev->macio)->dev,
+			r->size,
+			&r->bus_addr,
+			GFP_KERNEL);
+
+	if (!r->space) return -ENOMEM;
+
+	memset(r->space, 0, r->size);
+	r->cmds = (void*)DBDMA_ALIGN(r->space);
+	r->bus_cmd_start = r->bus_addr +
+			   (dma_addr_t)((char*)r->cmds - (char*)r->space);
+
+	return 0;
+}
+
+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);
+}
+
+static void i2sbus_release_dev(struct device *dev)
+{
+	struct i2sbus_dev *i2sdev;
+	int i;
+
+	i2sdev = container_of(dev, struct i2sbus_dev, sound.ofdev.dev);
+
+ 	if (i2sdev->intfregs) iounmap(i2sdev->intfregs);
+ 	if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma);
+ 	if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma);
+	for (i=0;i<3;i++)
+		if (i2sdev->allocated_resource[i])
+			release_resource(i2sdev->allocated_resource[i]);
+	free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring);
+	free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring);
+	for (i=0;i<3;i++)
+		free_irq(i2sdev->interrupts[i], i2sdev);
+	i2sbus_control_remove_dev(i2sdev->control, i2sdev);
+	mutex_destroy(&i2sdev->lock);
+	kfree(i2sdev);
+}
+
+static irqreturn_t i2sbus_bus_intr(int irq, void *devid, struct pt_regs *regs)
+{
+	struct i2sbus_dev *dev = devid;
+	u32 intreg;
+
+	spin_lock(&dev->low_lock);
+	intreg = in_le32(&dev->intfregs->intr_ctl);
+
+	/* acknowledge interrupt reasons */
+	out_le32(&dev->intfregs->intr_ctl, intreg);
+
+	spin_unlock(&dev->low_lock);
+
+	return IRQ_HANDLED;
+}
+
+static int force;
+module_param(force, int, 0444);
+MODULE_PARM_DESC(force, "Force loading i2sbus even when"
+			" no layout-id property is present");
+
+/* FIXME: look at device node refcounting */
+static int i2sbus_add_dev(struct macio_dev *macio,
+			  struct i2sbus_control *control,
+			  struct device_node *np)
+{
+	struct i2sbus_dev *dev;
+	struct device_node *child = NULL, *sound = NULL;
+	int i;
+	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) = {
+		i2sbus_bus_intr,
+		i2sbus_tx_intr,
+		i2sbus_rx_intr
+	};
+
+	if (strlen(np->name) != 5)
+		return 0;
+	if (strncmp(np->name, "i2s-", 4))
+		return 0;
+
+	if (np->n_intrs != 3)
+		return 0;
+
+	dev = kzalloc(sizeof(struct i2sbus_dev), GFP_KERNEL);
+	if (!dev)
+		return 0;
+
+	i = 0;
+	while ((child = of_get_next_child(np, child))) {
+		if (strcmp(child->name, "sound") == 0) {
+			i++;
+			sound = child;
+		}
+	}
+	if (i == 1) {
+		u32 *layout_id;
+		layout_id = (u32*) get_property(sound, "layout-id", NULL);
+		if (layout_id) {
+			snprintf(dev->sound.modalias, 32,
+				 "sound-layout-%d", *layout_id);
+			force = 1;
+		}
+	}
+	/* for the time being, until we can handle non-layout-id
+	 * things in some fabric, refuse to attach if there is no
+	 * layout-id property or we haven't been forced to attach.
+	 * 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) {
+		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.dev.parent = &macio->ofdev.dev;
+	dev->sound.ofdev.dev.release = i2sbus_release_dev;
+	dev->sound.attach_codec = i2sbus_attach_codec;
+	dev->sound.detach_codec = i2sbus_detach_codec;
+	dev->sound.pcmid = -1;
+	dev->macio = macio;
+	dev->control = control;
+	dev->bus_number = np->name[4] - 'a';
+	INIT_LIST_HEAD(&dev->sound.codec_list);
+
+	for (i=0;i<3;i++) {
+		dev->interrupts[i] = -1;
+		snprintf(dev->rnames[i], sizeof(dev->rnames[i]), rnames[i], np->name);
+	}
+	for (i=0;i<3;i++) {
+		if (request_irq(np->intrs[i].line, ints[i], 0, dev->rnames[i], dev))
+			goto err;
+		dev->interrupts[i] = np->intrs[i].line;
+	}
+
+	for (i=0;i<3;i++) {
+		if (of_address_to_resource(np, i, &dev->resources[i]))
+			goto err;
+		/* if only we could use our resource dev->resources[i]...
+		 * but request_resource doesn't know about parents and
+		 * contained resources... */
+		dev->allocated_resource[i] = 
+			request_mem_region(dev->resources[i].start,
+					   dev->resources[i].end -
+					   dev->resources[i].start + 1,
+					   dev->rnames[i]);
+		if (!dev->allocated_resource[i]) {
+			printk(KERN_ERR "i2sbus: failed to claim resource %d!\n", i);
+			goto err;
+		}
+	}
+	/* should do sanity checking here about length of them */
+	dev->intfregs = ioremap(dev->resources[0].start,
+				dev->resources[0].end-dev->resources[0].start+1);
+	dev->out.dbdma = ioremap(dev->resources[1].start,
+			 	 dev->resources[1].end-dev->resources[1].start+1);
+	dev->in.dbdma = ioremap(dev->resources[2].start,
+				dev->resources[2].end-dev->resources[2].start+1);
+	if (!dev->intfregs || !dev->out.dbdma || !dev->in.dbdma)
+		goto err;
+
+	if (alloc_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring,
+					MAX_DBDMA_COMMANDS))
+		goto err;
+	if (alloc_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring,
+					MAX_DBDMA_COMMANDS))
+		goto err;
+
+	if (i2sbus_control_add_dev(dev->control, dev)) {
+		printk(KERN_ERR "i2sbus: control layer didn't like bus\n");
+		goto err;
+	}
+
+	if (soundbus_add_one(&dev->sound)) {
+		printk(KERN_DEBUG "i2sbus: device registration error!\n");
+		goto err;
+	}
+
+	/* enable this cell */
+	i2sbus_control_cell(dev->control, dev, 1);
+	i2sbus_control_enable(dev->control, dev);
+	i2sbus_control_clock(dev->control, dev, 1);
+
+	return 1;
+ err:
+	for (i=0;i<3;i++)
+		if (dev->interrupts[i] != -1)
+			free_irq(dev->interrupts[i], dev);
+	free_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring);
+	free_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring);
+	if (dev->intfregs) iounmap(dev->intfregs);
+	if (dev->out.dbdma) iounmap(dev->out.dbdma);
+	if (dev->in.dbdma) iounmap(dev->in.dbdma);
+	for (i=0;i<3;i++)
+		if (dev->allocated_resource[i])
+			release_resource(dev->allocated_resource[i]);
+	mutex_destroy(&dev->lock);
+	kfree(dev);
+	return 0;
+}
+
+static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match)
+{
+	struct device_node *np = NULL;
+	int got = 0, err;
+	struct i2sbus_control *control = NULL;
+
+	err = i2sbus_control_init(dev, &control);
+	if (err)
+		return err;
+	if (!control) {
+		printk(KERN_ERR "i2sbus_control_init API breakage\n");
+		return -ENODEV;
+	}
+
+	while ((np = of_get_next_child(dev->ofdev.node, np))) {
+		if (device_is_compatible(np, "i2sbus") ||
+		    device_is_compatible(np, "i2s-modem")) {
+			got += i2sbus_add_dev(dev, control, np);
+		}
+	}
+
+	if (!got) {
+		/* found none, clean up */
+		i2sbus_control_destroy(control);
+		return -ENODEV;
+	}
+
+	dev->ofdev.dev.driver_data = control;
+
+	return 0;
+}
+
+static int i2sbus_remove(struct macio_dev* dev)
+{
+	struct i2sbus_control *control = dev->ofdev.dev.driver_data;
+	struct i2sbus_dev *i2sdev, *tmp;
+
+	list_for_each_entry_safe(i2sdev, tmp, &control->list, item)
+		soundbus_remove_one(&i2sdev->sound);
+
+	return 0;
+}
+
+#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 codec_info_item *cii;
+	struct i2sbus_dev* i2sdev;
+	int err, ret = 0;
+
+	list_for_each_entry(i2sdev, &control->list, item) {
+		/* Notify Alsa */
+		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;
+			if (cii->codec->suspend)
+				err = cii->codec->suspend(cii, state);
+			if (err)
+				ret = err;
+		}
+	}
+	return ret;
+}
+
+static int i2sbus_resume(struct macio_dev* dev)
+{
+	struct i2sbus_control *control = dev->ofdev.dev.driver_data;
+	struct codec_info_item *cii;
+	struct i2sbus_dev* i2sdev;
+	int err, ret = 0;
+
+	list_for_each_entry(i2sdev, &control->list, item) {
+		/* Notify codecs so they can re-initialize */
+		list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
+			err = 0;
+			if (cii->codec->resume)
+				err = cii->codec->resume(cii);
+			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;
+}
+#endif /* CONFIG_PM */
+
+static int i2sbus_shutdown(struct macio_dev* dev)
+{
+	return 0;
+}
+
+static struct macio_driver i2sbus_drv = {
+	.name = "soundbus-i2s",
+	.owner = THIS_MODULE,
+	.match_table = i2sbus_match,
+	.probe = i2sbus_probe,
+	.remove = i2sbus_remove,
+#ifdef CONFIG_PM
+	.suspend = i2sbus_suspend,
+	.resume = i2sbus_resume,
+#endif
+	.shutdown = i2sbus_shutdown,
+};
+
+static int __init soundbus_i2sbus_init(void)
+{
+	return macio_register_driver(&i2sbus_drv);
+}
+
+static void __exit soundbus_i2sbus_exit(void)
+{
+	macio_unregister_driver(&i2sbus_drv);
+}
+
+module_init(soundbus_i2sbus_init);
+module_exit(soundbus_i2sbus_exit);
--- /dev/null
+++ b/sound/aoa/soundbus/i2sbus/i2sbus-interface.h
@@ -0,0 +1,187 @@
+/*
+ * i2sbus driver -- interface register definitions
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#ifndef __I2SBUS_INTERFACE_H
+#define __I2SBUS_INTERFACE_H
+
+/* i2s bus control registers, at least what we know about them */
+
+#define __PAD(m,n) u8 __pad##m[n]
+#define _PAD(line, n) __PAD(line, n)
+#define PAD(n) _PAD(__LINE__, (n))
+struct i2s_interface_regs {
+	__le32 intr_ctl;	/* 0x00 */
+	PAD(12);
+	__le32 serial_format;	/* 0x10 */
+	PAD(12);
+	__le32 codec_msg_out;	/* 0x20 */
+	PAD(12);
+	__le32 codec_msg_in;	/* 0x30 */
+	PAD(12);
+	__le32 frame_count;	/* 0x40 */
+	PAD(12);
+	__le32 frame_match;	/* 0x50 */
+	PAD(12);
+	__le32 data_word_sizes;	/* 0x60 */
+	PAD(12);
+	__le32 peak_level_sel;	/* 0x70 */
+	PAD(12);
+	__le32 peak_level_in0;	/* 0x80 */
+	PAD(12);
+	__le32 peak_level_in1;	/* 0x90 */
+	PAD(12);
+	/* total size: 0x100 bytes */
+}  __attribute__((__packed__));
+
+/* interrupt register is just a bitfield with
+ * interrupt enable and pending bits */
+#define I2S_REG_INTR_CTL		0x00
+#	define I2S_INT_FRAME_COUNT		(1<<31)
+#	define I2S_PENDING_FRAME_COUNT		(1<<30)
+#	define I2S_INT_MESSAGE_FLAG		(1<<29)
+#	define I2S_PENDING_MESSAGE_FLAG		(1<<28)
+#	define I2S_INT_NEW_PEAK			(1<<27)
+#	define I2S_PENDING_NEW_PEAK		(1<<26)
+#	define I2S_INT_CLOCKS_STOPPED		(1<<25)
+#	define I2S_PENDING_CLOCKS_STOPPED	(1<<24)
+#	define I2S_INT_EXTERNAL_SYNC_ERROR	(1<<23)
+#	define I2S_PENDING_EXTERNAL_SYNC_ERROR	(1<<22)
+#	define I2S_INT_EXTERNAL_SYNC_OK		(1<<21)
+#	define I2S_PENDING_EXTERNAL_SYNC_OK	(1<<20)
+#	define I2S_INT_NEW_SAMPLE_RATE		(1<<19)
+#	define I2S_PENDING_NEW_SAMPLE_RATE	(1<<18)
+#	define I2S_INT_STATUS_FLAG		(1<<17)
+#	define I2S_PENDING_STATUS_FLAG		(1<<16)
+
+/* serial format register is more interesting :)
+ * It contains:
+ *  - clock source
+ *  - MClk divisor
+ *  - SClk divisor
+ *  - SClk master flag
+ *  - serial format (sony, i2s 64x, i2s 32x, dav, silabs)
+ *  - external sample frequency interrupt (don't understand)
+ *  - external sample frequency
+ */
+#define I2S_REG_SERIAL_FORMAT		0x10
+/* clock source. You get either 18.432, 45.1584 or 49.1520 MHz */
+#	define I2S_SF_CLOCK_SOURCE_SHIFT	30
+#	define I2S_SF_CLOCK_SOURCE_MASK		(3<<I2S_SF_CLOCK_SOURCE_SHIFT)
+#	define I2S_SF_CLOCK_SOURCE_18MHz	(0<<I2S_SF_CLOCK_SOURCE_SHIFT)
+#	define I2S_SF_CLOCK_SOURCE_45MHz	(1<<I2S_SF_CLOCK_SOURCE_SHIFT)
+#	define I2S_SF_CLOCK_SOURCE_49MHz	(2<<I2S_SF_CLOCK_SOURCE_SHIFT)
+/* also, let's define the exact clock speeds here, in Hz */
+#define I2S_CLOCK_SPEED_18MHz	18432000
+#define I2S_CLOCK_SPEED_45MHz	45158400
+#define I2S_CLOCK_SPEED_49MHz	49152000
+/* MClk is the clock that drives the codec, usually called its 'system clock'.
+ * It is derived by taking only every 'divisor' tick of the clock.
+ */
+#	define I2S_SF_MCLKDIV_SHIFT		24
+#	define I2S_SF_MCLKDIV_MASK		(0x1F<<I2S_SF_MCLKDIV_SHIFT)
+#	define I2S_SF_MCLKDIV_1			(0x14<<I2S_SF_MCLKDIV_SHIFT)
+#	define I2S_SF_MCLKDIV_3			(0x13<<I2S_SF_MCLKDIV_SHIFT)
+#	define I2S_SF_MCLKDIV_5			(0x12<<I2S_SF_MCLKDIV_SHIFT)
+#	define I2S_SF_MCLKDIV_14		(0x0E<<I2S_SF_MCLKDIV_SHIFT)
+#	define I2S_SF_MCLKDIV_OTHER(div)	(((div/2-1)<<I2S_SF_MCLKDIV_SHIFT)&I2S_SF_MCLKDIV_MASK)
+static inline int i2s_sf_mclkdiv(int div, int *out)
+{
+	int d;
+
+	switch(div) {
+	case 1: *out |= I2S_SF_MCLKDIV_1; return 0;
+	case 3: *out |= I2S_SF_MCLKDIV_3; return 0;
+	case 5: *out |= I2S_SF_MCLKDIV_5; return 0;
+	case 14: *out |= I2S_SF_MCLKDIV_14; return 0;
+	default:
+		if (div%2) return -1;
+		d = div/2-1;
+		if (d == 0x14 || d == 0x13 || d == 0x12 || d == 0x0E)
+			return -1;
+		*out |= I2S_SF_MCLKDIV_OTHER(div);
+		return 0;
+	}
+}
+/* SClk is the clock that drives the i2s wire bus. Note that it is
+ * derived from the MClk above by taking only every 'divisor' tick
+ * of MClk.
+ */
+#	define I2S_SF_SCLKDIV_SHIFT		20
+#	define I2S_SF_SCLKDIV_MASK		(0xF<<I2S_SF_SCLKDIV_SHIFT)
+#	define I2S_SF_SCLKDIV_1			(8<<I2S_SF_SCLKDIV_SHIFT)
+#	define I2S_SF_SCLKDIV_3			(9<<I2S_SF_SCLKDIV_SHIFT)
+#	define I2S_SF_SCLKDIV_OTHER(div)	(((div/2-1)<<I2S_SF_SCLKDIV_SHIFT)&I2S_SF_SCLKDIV_MASK)
+static inline int i2s_sf_sclkdiv(int div, int *out)
+{
+	int d;
+
+	switch(div) {
+	case 1: *out |= I2S_SF_SCLKDIV_1; return 0;
+	case 3: *out |= I2S_SF_SCLKDIV_3; return 0;
+	default:
+		if (div%2) return -1;
+		d = div/2-1;
+		if (d == 8 || d == 9) return -1;
+		*out |= I2S_SF_SCLKDIV_OTHER(div);
+		return 0;
+	}
+}
+#	define I2S_SF_SCLK_MASTER		(1<<19)
+/* serial format is the way the data is put to the i2s wire bus */
+#	define I2S_SF_SERIAL_FORMAT_SHIFT	16
+#	define I2S_SF_SERIAL_FORMAT_MASK	(7<<I2S_SF_SERIAL_FORMAT_SHIFT)
+#	define I2S_SF_SERIAL_FORMAT_SONY	(0<<I2S_SF_SERIAL_FORMAT_SHIFT)
+#	define I2S_SF_SERIAL_FORMAT_I2S_64X	(1<<I2S_SF_SERIAL_FORMAT_SHIFT)
+#	define I2S_SF_SERIAL_FORMAT_I2S_32X	(2<<I2S_SF_SERIAL_FORMAT_SHIFT)
+#	define I2S_SF_SERIAL_FORMAT_I2S_DAV	(4<<I2S_SF_SERIAL_FORMAT_SHIFT)
+#	define I2S_SF_SERIAL_FORMAT_I2S_SILABS	(5<<I2S_SF_SERIAL_FORMAT_SHIFT)
+/* unknown */
+#	define I2S_SF_EXT_SAMPLE_FREQ_INT_SHIFT	12
+#	define I2S_SF_EXT_SAMPLE_FREQ_INT_MASK	(0xF<<I2S_SF_SAMPLE_FREQ_INT_SHIFT)
+/* probably gives external frequency? */
+#	define I2S_SF_EXT_SAMPLE_FREQ_MASK	0xFFF
+
+/* used to send codec messages, but how isn't clear */
+#define I2S_REG_CODEC_MSG_OUT		0x20
+
+/* used to receive codec messages, but how isn't clear */
+#define I2S_REG_CODEC_MSG_IN		0x30
+
+/* frame count reg isn't clear to me yet, but probably useful */
+#define I2S_REG_FRAME_COUNT		0x40
+
+/* program to some value, and get interrupt if frame count reaches it */
+#define I2S_REG_FRAME_MATCH		0x50
+
+/* this register describes how the bus transfers data */
+#define I2S_REG_DATA_WORD_SIZES		0x60
+/* number of interleaved input channels */
+#	define I2S_DWS_NUM_CHANNELS_IN_SHIFT	24
+#	define I2S_DWS_NUM_CHANNELS_IN_MASK	(0x1F<<I2S_DWS_NUM_CHANNELS_IN_SHIFT)
+/* word size of input data */
+#	define I2S_DWS_DATA_IN_SIZE_SHIFT	16
+#	define I2S_DWS_DATA_IN_16BIT		(0<<I2S_DWS_DATA_IN_SIZE_SHIFT)
+#	define I2S_DWS_DATA_IN_24BIT		(3<<I2S_DWS_DATA_IN_SIZE_SHIFT)
+/* number of interleaved output channels */
+#	define I2S_DWS_NUM_CHANNELS_OUT_SHIFT	8
+#	define I2S_DWS_NUM_CHANNELS_OUT_MASK	(0x1F<<I2S_DWS_NUM_CHANNELS_OUT_SHIFT)
+/* word size of output data */
+#	define I2S_DWS_DATA_OUT_SIZE_SHIFT	0
+#	define I2S_DWS_DATA_OUT_16BIT		(0<<I2S_DWS_DATA_OUT_SIZE_SHIFT)
+#	define I2S_DWS_DATA_OUT_24BIT		(3<<I2S_DWS_DATA_OUT_SIZE_SHIFT)
+
+
+/* unknown */
+#define I2S_REG_PEAK_LEVEL_SEL		0x70
+
+/* unknown */
+#define I2S_REG_PEAK_LEVEL_IN0		0x80
+
+/* unknown */
+#define I2S_REG_PEAK_LEVEL_IN1		0x90
+
+#endif /* __I2SBUS_INTERFACE_H */
--- /dev/null
+++ b/sound/aoa/soundbus/i2sbus/i2sbus.h
@@ -0,0 +1,112 @@
+/*
+ * i2sbus driver -- private definitions
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#ifndef __I2SBUS_H
+#define __I2SBUS_H
+#include <asm/dbdma.h>
+#include <linux/interrupt.h>
+#include <sound/pcm.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <asm/prom.h>
+#include "i2sbus-interface.h"
+#include "i2sbus-control.h"
+#include "../soundbus.h"
+
+struct i2sbus_control {
+	volatile struct i2s_control_regs __iomem *controlregs;
+	struct resource rsrc;
+	struct list_head list;
+};
+
+#define MAX_DBDMA_COMMANDS	32
+
+struct dbdma_command_mem {
+	dma_addr_t bus_addr;
+	dma_addr_t bus_cmd_start;
+	struct dbdma_cmd *cmds;
+	void *space;
+	int size;
+	u32 running:1;
+};
+
+struct pcm_info {
+	u32 created:1, /* has this direction been created with alsa? */
+	    active:1;  /* is this stream active? */
+	/* runtime information */
+	struct snd_pcm_substream *substream;
+	int current_period;
+	u32 frame_count;
+	struct dbdma_command_mem dbdma_ring;
+	volatile struct dbdma_regs __iomem *dbdma;
+};
+
+struct i2sbus_dev {
+	struct soundbus_dev sound;
+	struct macio_dev *macio;
+	struct i2sbus_control *control;
+	volatile struct i2s_interface_regs __iomem *intfregs;
+
+	struct resource resources[3];
+	struct resource *allocated_resource[3];
+	int interrupts[3];
+	char rnames[3][32];
+
+	/* info about currently active substreams */
+	struct pcm_info out, in;
+	snd_pcm_format_t format;
+	unsigned int rate;
+
+	/* list for a single controller */
+	struct list_head item;
+	/* number of bus on controller */
+	int bus_number;
+	/* for use by control layer */
+	struct pmf_function *enable,
+			    *cell_enable,
+			    *cell_disable,
+			    *clock_enable,
+			    *clock_disable;
+
+	/* locks */
+	/* spinlock for low-level interrupt locking */
+	spinlock_t low_lock;
+	/* mutex for high-level consistency */
+	struct mutex lock;
+};
+
+#define soundbus_dev_to_i2sbus_dev(sdev) \
+		container_of(sdev, struct i2sbus_dev, sound)
+
+/* pcm specific functions */
+extern int
+i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
+		    struct codec_info *ci, void *data);
+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);
+extern irqreturn_t
+i2sbus_rx_intr(int irq, void *devid, struct pt_regs *regs);
+
+/* control specific functions */
+extern int i2sbus_control_init(struct macio_dev* dev,
+			       struct i2sbus_control **c);
+extern void i2sbus_control_destroy(struct i2sbus_control *c);
+extern int i2sbus_control_add_dev(struct i2sbus_control *c,
+				  struct i2sbus_dev *i2sdev);
+extern void i2sbus_control_remove_dev(struct i2sbus_control *c,
+				      struct i2sbus_dev *i2sdev);
+extern int i2sbus_control_enable(struct i2sbus_control *c,
+				 struct i2sbus_dev *i2sdev);
+extern int i2sbus_control_cell(struct i2sbus_control *c,
+			       struct i2sbus_dev *i2sdev,
+			       int enable);
+extern int i2sbus_control_clock(struct i2sbus_control *c,
+				struct i2sbus_dev *i2sdev,
+				int enable);
+#endif /* __I2SBUS_H */
--- /dev/null
+++ b/sound/aoa/soundbus/i2sbus/Makefile
@@ -0,0 +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
--- /dev/null
+++ b/sound/aoa/soundbus/i2sbus/i2sbus-control.c
@@ -0,0 +1,192 @@
+/*
+ * i2sbus driver -- bus control routines
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <asm/prom.h>
+#include <asm/macio.h>
+#include <asm/pmac_feature.h>
+#include <asm/pmac_pfunc.h>
+#include "i2sbus.h"
+
+int i2sbus_control_init(struct macio_dev* dev, struct i2sbus_control **c)
+{
+	*c = kzalloc(sizeof(struct i2sbus_control), GFP_KERNEL);
+	if (!*c)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&(*c)->list);
+
+	if (of_address_to_resource(dev->ofdev.node, 0, &(*c)->rsrc))
+		goto err;
+	/* we really should be using feature calls instead of mapping
+	 * these registers. It's safe for now since no one else is
+	 * touching them... */
+	(*c)->controlregs = ioremap((*c)->rsrc.start,
+				    sizeof(struct i2s_control_regs));
+	if (!(*c)->controlregs)
+		goto err;
+
+	return 0;
+ err:
+	kfree(*c);
+	*c = NULL;
+	return -ENODEV;
+}
+
+void i2sbus_control_destroy(struct i2sbus_control *c)
+{
+	iounmap(c->controlregs);
+	kfree(c);
+}
+
+/* this is serialised externally */
+int i2sbus_control_add_dev(struct i2sbus_control *c,
+			   struct i2sbus_dev *i2sdev)
+{
+	struct device_node *np;
+
+	np = i2sdev->sound.ofdev.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");
+	i2sdev->cell_disable = pmf_find_function(np, "cell-disable");
+	i2sdev->clock_disable = pmf_find_function(np, "clock-disable");
+
+	/* if the bus number is not 0 or 1 we absolutely need to use
+	 * the platform functions -- there's nothing in Darwin that
+	 * would allow seeing a system behind what the FCRs are then,
+	 * and I don't want to go parsing a bunch of platform functions
+	 * by hand to try finding a system... */
+	if (i2sdev->bus_number != 0 && i2sdev->bus_number != 1 &&
+	    (!i2sdev->enable ||
+	     !i2sdev->cell_enable || !i2sdev->clock_enable ||
+	     !i2sdev->cell_disable || !i2sdev->clock_disable)) {
+		pmf_put_function(i2sdev->enable);
+		pmf_put_function(i2sdev->cell_enable);
+		pmf_put_function(i2sdev->clock_enable);
+		pmf_put_function(i2sdev->cell_disable);
+		pmf_put_function(i2sdev->clock_disable);
+		return -ENODEV;
+	}
+
+	list_add(&i2sdev->item, &c->list);
+
+	return 0;
+}
+
+void i2sbus_control_remove_dev(struct i2sbus_control *c,
+			       struct i2sbus_dev *i2sdev)
+{
+	/* this is serialised externally */
+	list_del(&i2sdev->item);
+	if (list_empty(&c->list))
+		i2sbus_control_destroy(c);
+}
+
+int i2sbus_control_enable(struct i2sbus_control *c,
+			  struct i2sbus_dev *i2sdev)
+{
+	struct pmf_args args = { .count = 0 };
+	int cc;
+
+	if (i2sdev->enable)
+		return pmf_call_one(i2sdev->enable, &args);
+
+	switch (i2sdev->bus_number) {
+	case 0:
+		cc = in_le32(&c->controlregs->cell_control);
+		out_le32(&c->controlregs->cell_control, cc | CTRL_CLOCK_INTF_0_ENABLE);
+		break;
+	case 1:
+		cc = in_le32(&c->controlregs->cell_control);
+		out_le32(&c->controlregs->cell_control, cc | CTRL_CLOCK_INTF_1_ENABLE);
+		break;
+	default:
+		return -ENODEV;
+	}
+	return 0;
+}
+
+int i2sbus_control_cell(struct i2sbus_control *c,
+			struct i2sbus_dev *i2sdev,
+			int enable)
+{
+	struct pmf_args args = { .count = 0 };
+	int cc;
+
+	switch (enable) {
+	case 0:
+		if (i2sdev->cell_disable)
+			return pmf_call_one(i2sdev->cell_disable, &args);
+		break;
+	case 1:
+		if (i2sdev->cell_enable)
+			return pmf_call_one(i2sdev->cell_enable, &args);
+		break;
+	default:
+		printk(KERN_ERR "i2sbus: INVALID CELL ENABLE VALUE\n");
+		return -ENODEV;
+	}
+	switch (i2sdev->bus_number) {
+	case 0:
+		cc = in_le32(&c->controlregs->cell_control);
+		cc &= ~CTRL_CLOCK_CELL_0_ENABLE;
+		cc |= enable * CTRL_CLOCK_CELL_0_ENABLE;
+		out_le32(&c->controlregs->cell_control, cc);
+		break;
+	case 1:
+		cc = in_le32(&c->controlregs->cell_control);
+		cc &= ~CTRL_CLOCK_CELL_1_ENABLE;
+		cc |= enable * CTRL_CLOCK_CELL_1_ENABLE;
+		out_le32(&c->controlregs->cell_control, cc);
+		break;
+	default:
+		return -ENODEV;
+	}
+	return 0;
+}
+
+int i2sbus_control_clock(struct i2sbus_control *c,
+			 struct i2sbus_dev *i2sdev,
+			 int enable)
+{
+	struct pmf_args args = { .count = 0 };
+	int cc;
+
+	switch (enable) {
+	case 0:
+		if (i2sdev->clock_disable)
+			return pmf_call_one(i2sdev->clock_disable, &args);
+		break;
+	case 1:
+		if (i2sdev->clock_enable)
+			return pmf_call_one(i2sdev->clock_enable, &args);
+		break;
+	default:
+		printk(KERN_ERR "i2sbus: INVALID CLOCK ENABLE VALUE\n");
+		return -ENODEV;
+	}
+	switch (i2sdev->bus_number) {
+	case 0:
+		cc = in_le32(&c->controlregs->cell_control);
+		cc &= ~CTRL_CLOCK_CLOCK_0_ENABLE;
+		cc |= enable * CTRL_CLOCK_CLOCK_0_ENABLE;
+		out_le32(&c->controlregs->cell_control, cc);
+		break;
+	case 1:
+		cc = in_le32(&c->controlregs->cell_control);
+		cc &= ~CTRL_CLOCK_CLOCK_1_ENABLE;
+		cc |= enable * CTRL_CLOCK_CLOCK_1_ENABLE;
+		out_le32(&c->controlregs->cell_control, cc);
+		break;
+	default:
+		return -ENODEV;
+	}
+	return 0;
+}

--

^ permalink raw reply

* [RFC 11/12] snd-aoa: add Kconfig and Makefile
From: Johannes Berg @ 2006-06-07 13:09 UTC (permalink / raw)
  To: alsa-devel; +Cc: linuxppc-dev, netstar
In-Reply-To: <20060607130909.584205000@sipsolutions.net>

This patch adds the simple Kconfig and Makefile files for aoa.

--- /dev/null
+++ b/sound/aoa/Kconfig
@@ -0,0 +1,17 @@
+menu "Apple Onboard Audio driver"
+	depends on SND != n && PPC
+
+config SND_AOA
+	tristate "Apple Onboard Audio driver"
+	depends on SOUND && SND_PCM && EXPERIMENTAL
+	---help---
+	This option enables the new driver for the various
+	Apple Onboard Audio components.
+
+source "sound/aoa/fabrics/Kconfig"
+
+source "sound/aoa/codecs/Kconfig"
+
+source "sound/aoa/soundbus/Kconfig"
+
+endmenu
--- /dev/null
+++ b/sound/aoa/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_SND_AOA) += core/
+obj-$(CONFIG_SND_AOA) += codecs/
+obj-$(CONFIG_SND_AOA) += fabrics/
+obj-$(CONFIG_SND_AOA_SOUNDBUS) += soundbus/

--

^ permalink raw reply

* [RFC 08/12] snd-aoa: add toonie codec
From: Johannes Berg @ 2006-06-07 13:09 UTC (permalink / raw)
  To: alsa-devel; +Cc: linuxppc-dev, netstar
In-Reply-To: <20060607130909.584205000@sipsolutions.net>

This adds support for the 'toonie' codec which is just a codec
without any mixer/volume control functionality and is used on
the mac mini.

--- /dev/null
+++ b/sound/aoa/codecs/snd-aoa-codec-toonie.c
@@ -0,0 +1,140 @@
+/*
+ * Apple Onboard Audio driver for Toonie codec
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ *
+ *
+ * This is a driver for the toonie codec chip. This chip is present
+ * on the Mac Mini and is nothing but a DAC.
+ */
+#include <linux/delay.h>
+#include <linux/module.h>
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("toonie codec driver for snd-aoa");
+
+#include "../aoa.h"
+#include "../soundbus/soundbus.h"
+
+
+#define PFX "snd-aoa-codec-toonie: "
+
+struct toonie {
+	struct aoa_codec	codec;
+};
+#define codec_to_toonie(c) container_of(c, struct toonie, codec)
+
+static int toonie_dev_register(struct snd_device *dev)
+{
+	return 0;
+}
+
+static struct snd_device_ops ops = {
+	.dev_register = toonie_dev_register,
+};
+
+static struct transfer_info toonie_transfers[] = {
+	/* This thing *only* has analog output,
+	 * the rates are taken from Info.plist
+	 * from Darwin. */
+	{
+		.formats = SNDRV_PCM_FMTBIT_S16_BE |
+			   SNDRV_PCM_FMTBIT_S24_BE,
+		.rates = SNDRV_PCM_RATE_32000 |
+			 SNDRV_PCM_RATE_44100 |
+			 SNDRV_PCM_RATE_48000 |
+			 SNDRV_PCM_RATE_88200 |
+			 SNDRV_PCM_RATE_96000,
+	},
+	{}
+};
+
+#ifdef CONFIG_PM
+static int toonie_suspend(struct codec_info_item *cii, pm_message_t state)
+{
+	/* can we turn it off somehow? */
+	return 0;
+}
+
+static int toonie_resume(struct codec_info_item *cii)
+{
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct codec_info toonie_codec_info = {
+	.transfers = toonie_transfers,
+	.sysclock_factor = 256,
+	.bus_factor = 64,
+	.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+	.suspend = toonie_suspend,
+	.resume = toonie_resume,
+#endif
+};
+
+static int toonie_init_codec(struct aoa_codec *codec)
+{
+	struct toonie *toonie = codec_to_toonie(codec);
+
+	if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, toonie, &ops)) {
+		printk(KERN_ERR PFX "failed to create toonie snd device!\n");
+		return -ENODEV;
+	}
+
+	/* nothing connected? what a joke! */
+	if (toonie->codec.connected != 1)
+		return -ENOTCONN;
+
+	if (toonie->codec.soundbus_dev->attach_codec(toonie->codec.soundbus_dev,
+						     aoa_get_card(),
+						     &toonie_codec_info, toonie)) {
+		printk(KERN_ERR PFX "error creating toonie pcm\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void toonie_exit_codec(struct aoa_codec *codec)
+{
+	struct toonie *toonie = codec_to_toonie(codec);
+
+	if (!toonie->codec.soundbus_dev) {
+		printk(KERN_ERR PFX "toonie_exit_codec called without soundbus_dev!\n");
+		return;
+	}
+	toonie->codec.soundbus_dev->detach_codec(toonie->codec.soundbus_dev, toonie);
+}
+
+static struct toonie *toonie;
+
+static int __init toonie_init(void)
+{
+	toonie = kzalloc(sizeof(struct toonie), GFP_KERNEL);
+
+	if (!toonie)
+		return -ENOMEM;
+
+	strlcpy(toonie->codec.name, "toonie", sizeof(toonie->codec.name));
+	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;
+	}
+
+	return 0;
+}
+
+static void __exit toonie_exit(void)
+{
+	kfree(toonie);
+}
+
+module_init(toonie_init);
+module_exit(toonie_exit);

--

^ permalink raw reply

* Re: CPM_UART should allocate DPRAM for SMCx parameter RAM on MPC82xx
From: Vitaly Bordug @ 2006-06-07 13:31 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linuxppc-embedded
In-Reply-To: <200606071434.08905.laurent.pinchart@tbox.biz>

On Wed, 7 Jun 2006 14:34:08 +0200
Laurent Pinchart <laurent.pinchart@tbox.biz> wrote:

> Hi everybody,
> 
> the move to platform devices introduced a bug in the CPM_UART SMC driver.
> 
> Unlike SCC and FCC, the SMC parameter ram is not at a fixed location. A 
> pointer to the parameter ram is instead stored at PROFF_SMCx_BASE.
> 
> The SMC platform device resources (in arch/ppc/syslib/pq2_devices.c) reserves 
> the memory resource PROFF_SMCx_BASE - PROFF_SMCx_BASE+1. The CPM UART driver 
> considers that value as the SMC parameter ram offset. It should instead 
> allocate 64 bytes (on a 64 bytes boundary) of DPRAM for its parameter ram, 
> and store the offset at PROFF_SMCx_BASE. 
> 
> I'm not sure how to fix the problem as it seems that the platform device 
> support is not complete yet (cpm_uart_init_portdesc is still used to 
> initialize the console). A possible workaround is to set the pram resource to 
> 0x0000-0x003f and 0x0040-0x007f instead of 0x87fc-0x87fd and 0x88fc-0x88fd 
> for SMC1 and SMC2. This will not work if cpm_uart_init_portdesc is not 
> called.
> 

No, pdev support for cpm uart is complete and works for every board I have handy, both 8xx and 82xx.
8xx use SMC stuff and are ok, so I suppose the issue got inside because this is first case we head with combination of pq2/smc. 

init_portedsc is called in compatibility mode only, when driver was
unable to locate platform device (early_uart_get_pdev call). The
offsets are definitely odd - we should count the proper values (as it
was done for 8xx - offset from immr where pram will be located that
is). So, pq2_devices should be fixed, as well as bsp code should
provide early_uart_get_pdev call (see 8272 as reference).

-- 
Sincerely, 
Vitaly

^ permalink raw reply

* Re: CPM_UART should allocate DPRAM for SMCx parameter RAM on MPC82xx
From: Laurent Pinchart @ 2006-06-07 13:39 UTC (permalink / raw)
  To: Vitaly Bordug; +Cc: linuxppc-embedded
In-Reply-To: <20060607173107.53d0ef59@vitb.ru.mvista.com>

> > the move to platform devices introduced a bug in the CPM_UART SMC drive=
r.
> >
> > Unlike SCC and FCC, the SMC parameter ram is not at a fixed location. A
> > pointer to the parameter ram is instead stored at PROFF_SMCx_BASE.
> >
> > The SMC platform device resources (in arch/ppc/syslib/pq2_devices.c)
> > reserves the memory resource PROFF_SMCx_BASE - PROFF_SMCx_BASE+1. The C=
PM
> > UART driver considers that value as the SMC parameter ram offset. It
> > should instead allocate 64 bytes (on a 64 bytes boundary) of DPRAM for
> > its parameter ram, and store the offset at PROFF_SMCx_BASE.
> >
> > I'm not sure how to fix the problem as it seems that the platform device
> > support is not complete yet (cpm_uart_init_portdesc is still used to
> > initialize the console). A possible workaround is to set the pram
> > resource to 0x0000-0x003f and 0x0040-0x007f instead of 0x87fc-0x87fd and
> > 0x88fc-0x88fd for SMC1 and SMC2. This will not work if
> > cpm_uart_init_portdesc is not called.
>
> No, pdev support for cpm uart is complete and works for every board I have
> handy, both 8xx and 82xx. 8xx use SMC stuff and are ok, so I suppose the
> issue got inside because this is first case we head with combination of
> pq2/smc.
>
> init_portedsc is called in compatibility mode only, when driver was
> unable to locate platform device (early_uart_get_pdev call). The
> offsets are definitely odd - we should count the proper values (as it
> was done for 8xx - offset from immr where pram will be located that
> is). So, pq2_devices should be fixed, as well as bsp code should
> provide early_uart_get_pdev call (see 8272 as reference).

=46rom my understanding, SMC pram should be allocated, and its offset shoul=
d be=20
stored at PROFF_SMCx_BASE. pq2_devices returns the PRFF_SMCx_BASE resource,=
=20
and the CPM UART driver should use cpm_dpalloc to allocate the parameter RA=
M.=20
Is that right ? If so, where should I allocate memory (and where should I=20
free it) in the CPM UART driver ?

Laurent Pinchart

^ permalink raw reply

* Re: CPM_UART should allocate DPRAM for SMCx parameter RAM on MPC82xx
From: Vitaly Bordug @ 2006-06-07 13:49 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linuxppc-embedded
In-Reply-To: <200606071539.57394.laurent.pinchart@tbox.biz>

On Wed, 7 Jun 2006 15:39:57 +0200
Laurent Pinchart <laurent.pinchart@tbox.biz> wrote:

> > > the move to platform devices introduced a bug in the CPM_UART SMC driver.
> > >
> > > Unlike SCC and FCC, the SMC parameter ram is not at a fixed location. A
> > > pointer to the parameter ram is instead stored at PROFF_SMCx_BASE.
> > >
> > > The SMC platform device resources (in arch/ppc/syslib/pq2_devices.c)
> > > reserves the memory resource PROFF_SMCx_BASE - PROFF_SMCx_BASE+1. The CPM
> > > UART driver considers that value as the SMC parameter ram offset. It
> > > should instead allocate 64 bytes (on a 64 bytes boundary) of DPRAM for
> > > its parameter ram, and store the offset at PROFF_SMCx_BASE.
> > >
> > > I'm not sure how to fix the problem as it seems that the platform device
> > > support is not complete yet (cpm_uart_init_portdesc is still used to
> > > initialize the console). A possible workaround is to set the pram
> > > resource to 0x0000-0x003f and 0x0040-0x007f instead of 0x87fc-0x87fd and
> > > 0x88fc-0x88fd for SMC1 and SMC2. This will not work if
> > > cpm_uart_init_portdesc is not called.
> >
> > No, pdev support for cpm uart is complete and works for every board I have
> > handy, both 8xx and 82xx. 8xx use SMC stuff and are ok, so I suppose the
> > issue got inside because this is first case we head with combination of
> > pq2/smc.
> >
> > init_portedsc is called in compatibility mode only, when driver was
> > unable to locate platform device (early_uart_get_pdev call). The
> > offsets are definitely odd - we should count the proper values (as it
> > was done for 8xx - offset from immr where pram will be located that
> > is). So, pq2_devices should be fixed, as well as bsp code should
> > provide early_uart_get_pdev call (see 8272 as reference).
> 
> From my understanding, SMC pram should be allocated, and its offset should be 
> stored at PROFF_SMCx_BASE. pq2_devices returns the PRFF_SMCx_BASE resource, 
> and the CPM UART driver should use cpm_dpalloc to allocate the parameter RAM. 
> Is that right ? If so, where should I allocate memory (and where should I 
> free it) in the CPM UART driver ?
> 
As I said, SMC offsets relative to pram things are screwed for PQ2.
That needs to be fixed. It is assumed, that for each SMC ready-for-use offset should be defined in pram resource (as for 8xx). There is no need to dpalloc/dpfree because the idea is that pram thing should be fixed to prevent confusion. So the odd offset for SMC devices in pq2_devices.c should be replaced with correct values (which should be dpram_start+PROFF_SMCx - have a look at 8xx as reference)

-- 
Sincerely, 
Vitaly

^ permalink raw reply

* Re: *** PROBABLY SPAM *** Re: CPM_UART should allocate DPRAM for SMCx parameter RAM on MPC82xx
From: Laurent Pinchart @ 2006-06-07 14:10 UTC (permalink / raw)
  To: Vitaly Bordug; +Cc: linuxppc-embedded
In-Reply-To: <20060607174931.766eda5d@vitb.ru.mvista.com>

> As I said, SMC offsets relative to pram things are screwed for PQ2.
> That needs to be fixed. It is assumed, that for each SMC ready-for-use
> offset should be defined in pram resource (as for 8xx). There is no need to
> dpalloc/dpfree because the idea is that pram thing should be fixed to
> prevent confusion. So the odd offset for SMC devices in pq2_devices.c
> should be replaced with correct values (which should be
> dpram_start+PROFF_SMCx - have a look at 8xx as reference)

Ok. dpram[PROFF_SMCx_BASE] must still be set to the values returned by 
platform_get_resource_byname(). Where should that be done ?

Laurent Pinchart

^ permalink raw reply

* Re: [PATCH] dtc: add setting of physical boot cpu
From: Jon Loeliger @ 2006-06-07 14:49 UTC (permalink / raw)
  To: Michael Neuling; +Cc: linuxppc-dev
In-Reply-To: <20060530223309.9AB7667B3E@ozlabs.org>

So, like, the other day Michael Neuling mumbled:
> dtc always sets the physical boot CPU to 0xfeedbeef.  Add a -b option to
> set this.    Also add warnings when using the wrong property with the
> wrong blob version.  
> 
> Signed-off-by: Michael Neuling <mikey@neuling.org>

Applied and pushed out to:

     http://www.jdl.com/git_repos

Sorry for the hang time.

Thanks,
jdl

^ permalink raw reply

* Re: [PATCH 0/5] Sizing zones and holes in an architecture independent manner V7
From: Andi Kleen @ 2006-06-07 15:20 UTC (permalink / raw)
  To: Mel Gorman
  Cc: Andrew Morton, davej, tony.luck, linux-mm, linux-kernel,
	bob.picco, linuxppc-dev
In-Reply-To: <Pine.LNX.4.64.0606071118230.20653@skynet.skynet.ie>


> Ok, while true, I'm not sure how it affects performance. The only "real" 
> value affected by present_pages is the number of patches that are 
> allocated in batches to the per-cpu allocator.

It affects the low/high water marks in the VM zone balancer.

Especially for the 16MB DMA zone it can make a difference if you
account 4MB kernel in there or not.

-Andi

^ permalink raw reply

* Re: [RFC 07/12] snd-aoa: add tas codec
From: Johannes Berg @ 2006-06-07 15:42 UTC (permalink / raw)
  To: alsa-devel; +Cc: linuxppc-dev, netstar
In-Reply-To: <20060607130914.735290000@sipsolutions.net>

[-- Attachment #1: Type: text/plain, Size: 643 bytes --]

> +		/* 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;
> +			printk(KERN_DEBUG PFX "found 'deq' node\n");
> +			addr = (u32 *) get_property(dev, "i2c-address", NULL);
> +			if (!addr)
> +				continue;
> +			/* 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)

And I fixed the obvious bug here now.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 793 bytes --]

^ permalink raw reply

* Linux kernel 2.6 on IBM RS/6000 7025-F40
From: Christophe Simon @ 2006-06-07 15:30 UTC (permalink / raw)
  To: linuxppc-dev

Hi there,

Did someone succeeded in compiling a running 2.6 kernel on an IBM RS/6000 
7025-F40 (PReP arch) ? I have one of these machines, and it runs with a 
2.4.22 kernel built using the instructions gathered on 
http://www.solinno.co.uk/7043-140/

It seems that this machine is quite problematic.

I tried to compile my own kernel with a stock 2.6.16.19, and after that 
2.6.7 patched with the files I found for the 2.6.7 (at 
http://www.solinno.co.uk/7043-140/files/2.6.7/), but the system refuses to 
boot with those twoo kernels  (the machine reboots or freezes before writing 
any piece of information on the console...).

The site seems not to move anymore, and I'd like to have a 2.6 kernel 
running because of his better performances ans extended functionalities, and 
I'd like not to be stalled on 2.4.22 kernel...

I crawled the web hours and hours (Google is my friend, but it didn't help 
me...) and I didn't find anything...

Could someone help me !

Thanks a lot !

Christophe SIMON

_________________________________________________________________
Windows Live Messenger : venez tester la version bêta ! 
http://www.ideas.live.com/programpage.aspx?versionId=0eccd94b-eb48-497c-8e60-c6313f7ebb73

^ permalink raw reply

* Re: [PATCH 0/5] Sizing zones and holes in an architecture independent manner V7
From: Mel Gorman @ 2006-06-07 16:25 UTC (permalink / raw)
  To: Andi Kleen
  Cc: Andrew Morton, davej, tony.luck, linux-mm, linux-kernel,
	bob.picco, linuxppc-dev
In-Reply-To: <200606071720.22242.ak@suse.de>

On Wed, 7 Jun 2006, Andi Kleen wrote:

>
>> Ok, while true, I'm not sure how it affects performance. The only "real"
>> value affected by present_pages is the number of patches that are
>> allocated in batches to the per-cpu allocator.
>
> It affects the low/high water marks in the VM zone balancer.
>

ok, that's true. The watermarks would be higher if memmap and the kernel 
image is not taken into account. Arguably, the same applies to bootmem 
allocations. This hits all architectures *except* x86_64.

> Especially for the 16MB DMA zone it can make a difference if you
> account 4MB kernel in there or not.
>

I'm guessing it's a difficult-to-trigger problem or it would have been 
reported for other arches. Assuming it can be triggered and that is what 
was causing your problems, it's still worth fixing in the arch-independent 
code rather than burying it in arch/somewhere, right?

-- 
Mel Gorman
Part-time Phd Student                          Linux Technology Center
University of Limerick                         IBM Dublin Software Lab

^ permalink raw reply

* Re: CPM_UART should allocate DPRAM for SMCx parameter RAM on MPC82xx
From: Dan Malek @ 2006-06-07 16:34 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linuxppc-embedded
In-Reply-To: <200606071434.08905.laurent.pinchart@tbox.biz>


On Jun 7, 2006, at 8:34 AM, Laurent Pinchart wrote:

> I'm not sure how to fix the problem

They way this _used_ to work is the DPRAM memory allocator
knew the SMCs had allocated the lower 128 bytes of DPRAM and
it never would allocate this space to anyone else (and it should not
free it either).  The constant #defined values for the SMC base
address would work just fine and could be assigned as part of
the SMC uart initialization.  Due to the alignment restrictions and
no reason to relocate this space anywhere else, this still seems
to be the most efficient method.


	-- Dan

^ permalink raw reply

* Re: [PATCH 2/3] powerpc: Implement support for setting little-endian mode via prctl
From: Olof Johansson @ 2006-06-07 17:33 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: akpm, linuxppc-dev
In-Reply-To: <17542.28368.426967.127506@cargo.ozlabs.ibm.com>

Hi,

On Wed, Jun 07, 2006 at 04:14:40PM +1000, Paul Mackerras wrote:

> @@ -136,6 +141,7 @@ #define CPU_FTR_COHERENT_ICACHE		ASM_CON
>  #define CPU_FTR_LOCKLESS_TLBIE		ASM_CONST(0x0)
>  #define CPU_FTR_MMCRA_SIHV		ASM_CONST(0x0)
>  #define CPU_FTR_CI_LARGE_PAGE		ASM_CONST(0x0)
> +#define CPU_FTR_PAUSE_ZERO              ASM_CONST(0x0)
>  #define CPU_FTR_PURR			ASM_CONST(0x0)
>  #endif

This seems to have snuck in from something else. It's not used anywhere.



-Olof

^ permalink raw reply

* Re: [Alsa-devel] [RFC 03/12] snd-aoa: add aoa core
From: Takashi Iwai @ 2006-06-07 18:13 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linuxppc-dev, alsa-devel, netstar
In-Reply-To: <20060607130911.997152000@sipsolutions.net>

At Wed, 07 Jun 2006 15:09:12 +0200,
Johannes Berg wrote:
> 
> --- /dev/null
> +++ b/sound/aoa/core/snd-aoa-gpio-pmf.c
(snip)
> +void pmf_handle_notify_irq(void *data)
> +{

Missing static.


> --- /dev/null
> +++ b/sound/aoa/core/snd-aoa-gpio-feature.c
(snip)
> +static struct device_node *get_gpio(
> +	char *name, char *altname, int *gpioptr, int *gpioactiveptr)

Unconventional style.

> +irqreturn_t ftr_handle_notify_irq(int xx, void *data, struct pt_regs *regs)
> +{

Missing static.

> +static int ftr_set_notify(struct gpio_runtime *rt,
> +			  enum notify_type type,
> +			  notify_func_t notify,
> +			  void *data)
(snip)
> +	if (old && !notify) {
> +		free_irq(irq, notif);
> +	}
> +	if (!old && notify) {
> +		request_irq(irq, ftr_handle_notify_irq, 0, name, notif);
> +	}

Remove braces.  Any error check from request_irq()?


Takashi

^ permalink raw reply

* Re: [Alsa-devel] [RFC 00/12] snd-aoa: add snd-aoa
From: Takashi Iwai @ 2006-06-07 18:22 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linuxppc-dev, alsa-devel, netstar
In-Reply-To: <20060607130909.584205000@sipsolutions.net>

At Wed, 07 Jun 2006 15:09:09 +0200,
Johannes Berg wrote:
> 
> The following patches would add snd-aoa to the Linux source tree. I'm
> posting them here now for some last round of review before I finally
> post them with a Signed-Off-By and Changelog for inclusion. As you
> previously asked about this, I don't think it makes sense to commit
> these patches one by one, but the first one removing layout-id support
> from snd-powermac might be worth singling out.

Yes, I'll apply the patch to snd-powermac separately.

> Please let me know either way so that I provide either one or two
> changelog entries.

Both for snd-aoa addition and for dropping layout-id support from
snd-powermac, please.


thanks,

Takashi

^ permalink raw reply

* Re: [Alsa-devel] [RFC 05/12] snd-aoa: add i2sbus
From: Takashi Iwai @ 2006-06-07 18:44 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linuxppc-dev, alsa-devel, netstar
In-Reply-To: <20060607130913.414650000@sipsolutions.net>

At Wed, 07 Jun 2006 15:09:14 +0200,
Johannes Berg wrote:
> 
> --- /dev/null
> +++ b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c
> +static int clock_and_divisors(int mclk, int sclk, int rate, int *out)
> +{
> +	/* sclk must be derived from mclk! */
> +	if (mclk % sclk)
> +		return -1;
> +	/* derive sclk register value */
> +	if (i2s_sf_sclkdiv(mclk / sclk, out))
> +		return -1;
> +
> +	if (I2S_CLOCK_SPEED_18MHz % (rate * mclk) == 0) {
> +		if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_18MHz / rate / mclk, out)) {

I'd use "I2S_CLOCK_SPEED_18MHZ / (rate * mclk)"


> --- /dev/null
> +++ b/sound/aoa/soundbus/i2sbus/i2sbus-core.c
> +static void i2sbus_release_dev(struct device *dev)
> +{
> +	struct i2sbus_dev *i2sdev;
> +	int i;
> +
> +	i2sdev = container_of(dev, struct i2sbus_dev, sound.ofdev.dev);
> +
> + 	if (i2sdev->intfregs) iounmap(i2sdev->intfregs);
> + 	if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma);
> + 	if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma);
> +	for (i=0;i<3;i++)
> +		if (i2sdev->allocated_resource[i])
> +			release_resource(i2sdev->allocated_resource[i]);

release_resource() doesn't kfree the record returned from
request_mem_region().  ALSA has a helper function
release_and_free_resource() that also does NULL check and kfree:

	for (i = 0; i < 3; i++)
		release_and_free_resource(i2sdev->allocated_resource[i]);

> +/* FIXME: look at device node refcounting */
> +static int i2sbus_add_dev(struct macio_dev *macio,
> +			  struct i2sbus_control *control,
> +			  struct device_node *np)
> +{
(snip)
> +	for (i=0;i<3;i++)
> +		if (dev->allocated_resource[i])
> +			release_resource(dev->allocated_resource[i]);

Ditto.


Takashi

^ permalink raw reply

* Re: [Alsa-devel] [RFC 11/12] snd-aoa: add Kconfig and Makefile
From: Takashi Iwai @ 2006-06-07 18:50 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linuxppc-dev, alsa-devel, netstar
In-Reply-To: <20060607130917.996988000@sipsolutions.net>

At Wed, 07 Jun 2006 15:09:20 +0200,
Johannes Berg wrote:
> 
> This patch adds the simple Kconfig and Makefile files for aoa.
> 
> --- /dev/null
> +++ b/sound/aoa/Kconfig
> @@ -0,0 +1,17 @@
> +menu "Apple Onboard Audio driver"
> +	depends on SND != n && PPC
> +
> +config SND_AOA
> +	tristate "Apple Onboard Audio driver"
> +	depends on SOUND && SND_PCM && EXPERIMENTAL

Let's remove EXPERIMENTAL.  Or keep snd-powermac untouched.


Takashi

^ permalink raw reply

* [PATCH 0/3] PPC64 PCIe support
From: Jake Moilanen @ 2006-06-07 21:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Paul Mackerras

Here are the three patches for PCIe/MSI on pSeries for 2.6.18.

Since MSI is a requirement for PCIe, I have put these in one batch.

This is enough to probe, find, configure, and run basic I/O to PCIe
enabled devices.  However, these patches do not include support for
PCIe hotplug, nor multiple MSI vectors.

There is a supplemental patches which will be needed in the
future.  Specifically adding a PCIe bit for
ibm,client-architecture-support.  To my knowledge this bit has not been
defined yet.

^ permalink raw reply

* [PATCH 1/3] PCIe device_type pciex
From: Jake Moilanen @ 2006-06-07 21:05 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: paulus
In-Reply-To: <20060607160212.1203d9ea.moilanen@austin.ibm.com>

This adds support to recognize the PCIe device_type "pciex" and made
the portdrv buildable.

Signed-off-by: Jake Moilanen <moilanen@austin.ibm.com>


Index: 2.6/arch/powerpc/Kconfig
===================================================================
--- 2.6.orig/arch/powerpc/Kconfig	2006-05-31 15:33:57.000000000 -0500
+++ 2.6/arch/powerpc/Kconfig	2006-05-31 15:34:10.000000000 -0500
@@ -849,6 +849,8 @@
 
 endchoice
 
+source "drivers/pci/pcie/Kconfig"
+
 source "drivers/pci/Kconfig"
 
 source "drivers/pcmcia/Kconfig"
Index: 2.6/arch/powerpc/kernel/pci_64.c
===================================================================
--- 2.6.orig/arch/powerpc/kernel/pci_64.c	2006-05-31 15:33:57.000000000 -0500
+++ 2.6/arch/powerpc/kernel/pci_64.c	2006-05-31 15:34:10.000000000 -0500
@@ -396,7 +396,7 @@
 
 	dev->current_state = 4;		/* unknown power state */
 
-	if (!strcmp(type, "pci")) {
+	if (!strcmp(type, "pci") || !strcmp(type, "pciex")) {
 		/* a PCI-PCI bridge */
 		dev->hdr_type = PCI_HEADER_TYPE_BRIDGE;
 		dev->rom_base_reg = PCI_ROM_ADDRESS1;
Index: 2.6/arch/powerpc/kernel/rtas_pci.c
===================================================================
--- 2.6.orig/arch/powerpc/kernel/rtas_pci.c	2006-05-31 15:33:57.000000000 -0500
+++ 2.6/arch/powerpc/kernel/rtas_pci.c	2006-05-31 15:34:10.000000000 -0500
@@ -313,7 +313,9 @@
 	for (node = of_get_next_child(root, NULL);
 	     node != NULL;
 	     node = of_get_next_child(root, node)) {
-		if (node->type == NULL || strcmp(node->type, "pci") != 0)
+
+		if (node->type == NULL || (strcmp(node->type, "pci") != 0 &&
+					   strcmp(node->type, "pciex") != 0))
 			continue;
 
 		phb = pcibios_alloc_controller(node);

^ permalink raw reply

* [PATCH 2/3] MSI power abstraction
From: Jake Moilanen @ 2006-06-07 21:15 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: paulus
In-Reply-To: <20060607160212.1203d9ea.moilanen@austin.ibm.com>

Instead of trying to make PPC64 MSI fit in a Intel-centric MSI layer, a
simple short-term solution is to hook the pci_{en/dis}able_msi() calls
and make a machdep call.

The rest of the MSI functions are superfluous for what is needed at this
time.  Many of which can have machdep calls added as needed.

Ben and Michael Ellerman are looking into rewrite the MSI layer to be
more generic.  However, in the meantime this works as a interim
solution.

Signed-off-by: Jake Moilanen <moilanen@austin.ibm.com>

Index: 2.6/arch/powerpc/kernel/irq.c
===================================================================
--- 2.6.orig/arch/powerpc/kernel/irq.c	2006-06-07 15:23:41.000000000 -0500
+++ 2.6/arch/powerpc/kernel/irq.c	2006-06-07 15:29:32.000000000 -0500
@@ -47,6 +47,7 @@
 #include <linux/cpumask.h>
 #include <linux/profile.h>
 #include <linux/bitops.h>
+#include <linux/pci.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -436,6 +437,30 @@
 }
 EXPORT_SYMBOL(do_softirq);
 
+#ifdef CONFIG_PCI_MSI
+int pci_enable_msi(struct pci_dev * pdev)
+{
+	if (ppc_md.enable_msi)
+		return ppc_md.enable_msi(pdev);
+	else
+		return -1;
+}
+
+void pci_disable_msi(struct pci_dev * pdev)
+{
+	if (ppc_md.disable_msi)
+		ppc_md.disable_msi(pdev);
+}
+
+void pci_scan_msi_device(struct pci_dev *dev) {}
+int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) {return -1;}
+void pci_disable_msix(struct pci_dev *dev) {}
+void msi_remove_pci_irq_vectors(struct pci_dev *dev) {}
+void disable_msi_mode(struct pci_dev *dev, int pos, int type) {}
+void pci_no_msi(void) {}
+
+#endif
+
 #ifdef CONFIG_PPC64
 static int __init setup_noirqdistrib(char *str)
 {
Index: 2.6/include/asm-powerpc/machdep.h
===================================================================
--- 2.6.orig/include/asm-powerpc/machdep.h	2006-06-07 15:23:41.000000000 -0500
+++ 2.6/include/asm-powerpc/machdep.h	2006-06-07 15:24:48.000000000 -0500
@@ -238,6 +238,11 @@
 	 */
 	void (*machine_kexec)(struct kimage *image);
 #endif /* CONFIG_KEXEC */
+
+#ifdef CONFIG_PCI_MSI
+	int (*enable_msi)(struct pci_dev *pdev);
+	void (*disable_msi)(struct pci_dev *pdev);
+#endif /* CONFIG_PCI_MSI */
 };
 
 extern void power4_idle(void);

^ permalink raw reply

* [PATCH 3/3] RTAS MSI
From: Jake Moilanen @ 2006-06-07 21:25 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: paulus
In-Reply-To: <20060607160212.1203d9ea.moilanen@austin.ibm.com>

This provides support for the pSeries RTAS interfaces for MSI.  

It follows the altix conventions, and is based on top of the altix patch.

Signed-off-by: Jake Moilanen <moilanen@austin.ibm.com>

Index: 2.6/drivers/pci/Makefile
===================================================================
--- 2.6.orig/drivers/pci/Makefile	2006-06-07 15:29:55.000000000 -0500
+++ 2.6/drivers/pci/Makefile	2006-06-07 15:59:28.000000000 -0500
@@ -26,7 +26,14 @@
 obj-$(CONFIG_PPC64) += setup-bus.o
 obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
 obj-$(CONFIG_X86_VISWS) += setup-irq.o
-obj-$(CONFIG_PCI_MSI) += msi.o
+
+msiobj-$(CONFIG_X86) += msi.o msi-apic.o msi-intel.o
+msiobj-$(CONFIG_IA64) += msi.o msi-apic.o msi-intel.o
+msiobj-$(CONFIG_IA64_GENERIC) += msi.o msi-altix.o
+msiobj-$(CONFIG_IA64_SGI_SN2) += msi.o msi-altix.o
+msiobj-$(CONFIG_PPC_PSERIES) += msi-rtas.o
+
+obj-$(CONFIG_PCI_MSI) += $(msiobj-y)
 
 #
 # ACPI Related PCI FW Functions
Index: 2.6/drivers/pci/msi-rtas.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ 2.6/drivers/pci/msi-rtas.c	2006-06-07 15:59:59.000000000 -0500
@@ -0,0 +1,162 @@
+/*
+ * Jake Moilanen <moilanen@austin.ibm.com>
+ * Copyright (C) 2006 IBM
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/irq.h>
+#include <asm/rtas.h>
+#include <asm/hw_irq.h>
+
+int rtas_enable_msi(struct pci_dev* pdev)
+{
+	static int seq_num = 1;
+	int i;
+	int rc;
+	int query_token = rtas_token("ibm,query-interrupt-source-number");
+	int devfn;
+	int busno;
+	u32 *reg;
+	int reglen;
+	int ret[3];
+	int dummy;
+	unsigned int virq;
+	unsigned int addr;
+	unsigned long buid = -1;
+	unsigned long wait_time;
+	struct device_node * dn;
+
+	BUG_ON(!pdev);
+
+	dn = pci_device_to_OF_node(pdev);
+
+	if (!of_find_property(dn, "ibm,req#msi", &dummy))
+		return -ENOENT;
+
+	reg = (u32 *) get_property(dn, "reg", &reglen);
+	if (reg == NULL || reglen < 20)
+		return -ENXIO;
+
+	devfn = (reg[0] >> 8) & 0xff;
+	busno = (reg[0] >> 16) & 0xff;
+
+	buid = get_phb_buid(dn->parent);
+	addr = (busno << 16) | (devfn << 8);
+
+	while (1) {
+		rc = rtas_call(rtas_token("ibm,change-msi"), 6, 3, ret, addr,
+			       buid >> 32, buid & 0xffffffff,
+			       0, 0, seq_num);
+
+		if (!rc)
+			break;
+		else if (rc == RTAS_BUSY)
+			udelay(1);
+		else if (rtas_is_extended_busy(rc)) {
+			wait_time = rtas_extended_busy_delay_time(rc);
+			udelay(wait_time * 1000);
+		} else {
+			printk(KERN_WARNING "error[%d]: getting the number of "
+			       "MSI interrupts for %s\n", rc, dn->name);
+			return -EIO;
+		}
+
+		seq_num = ret[1];
+	}
+
+	/* Return if there's no MSI interrupts */
+	if (!ret[0])
+		return -ENOENT;
+
+	dn->n_intrs = ret[0];
+
+	dn->intrs = kmalloc(dn->n_intrs * sizeof(*(dn->intrs)), GFP_KERNEL);
+	if (!dn->intrs) {
+		printk(KERN_WARNING "rtas_enable_msi: can't allocate space\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < dn->n_intrs; i++) {
+		rc = rtas_call(query_token, 4, 3, ret, addr,
+			       buid >> 32, buid & 0xffffffff, i);
+
+		if (!rc) {
+			virq = virt_irq_create_mapping(ret[0]);
+
+			dn->intrs[i].line = irq_offset_up(virq);
+			dn->intrs[i].sense = ret[1];
+		} else {
+			printk(KERN_WARNING "error[%d]: query-interrupt-source-number for %s\n",
+			       rc, dn->name);
+		}
+	}
+
+	/* Just give the first vector out for now */
+	pdev->irq = dn->intrs[0].line;
+
+	return 0;
+}
+
+void rtas_disable_msi(struct pci_dev* pdev)
+{
+	static int seq_num = 1;
+	struct device_node * dn;
+	int rc;
+	int devfn;
+	int busno;
+	u32 *reg;
+	int reglen;
+	int ret[3];
+	int dummy;
+	unsigned int addr;
+	unsigned long buid = -1;
+	unsigned long wait_time;
+
+	BUG_ON(!pdev);
+
+	dn = pci_device_to_OF_node(pdev);
+
+	if (!of_find_property(dn, "ibm,req#msi", &dummy))
+		return;
+
+	reg = (u32 *) get_property(dn, "reg", &reglen);
+	if (reg == NULL || reglen < 20)
+		return;
+
+	devfn = (reg[0] >> 8) & 0xff;
+	busno = (reg[0] >> 16) & 0xff;
+
+	buid = get_phb_buid(dn->parent);
+	addr = (busno << 16) | (devfn << 8);
+
+	while (1) {
+		rc = rtas_call(rtas_token("ibm,change-msi"), 6, 3, ret, addr,
+			       buid >> 32, buid & 0xffffffff,
+			       2, 0, seq_num);
+
+		if (!rc)
+			break;
+		else if (rc == RTAS_BUSY)
+			udelay(1);
+		else if (rtas_is_extended_busy(rc)) {
+			wait_time = rtas_extended_busy_delay_time(rc);
+			udelay(wait_time * 1000);
+		} else {
+			printk(KERN_WARNING "error[%d]: setting the number of "
+			       "MSI interrupts for %s\n", rc, dn->name);
+			return;
+		}
+
+		seq_num = ret[1];
+	}
+
+	dn->n_intrs = 0;
+
+	kfree(dn->intrs);
+}
Index: 2.6/drivers/pci/Kconfig
===================================================================
--- 2.6.orig/drivers/pci/Kconfig	2006-06-07 15:29:55.000000000 -0500
+++ 2.6/drivers/pci/Kconfig	2006-06-07 15:59:28.000000000 -0500
@@ -4,7 +4,7 @@
 config PCI_MSI
 	bool "Message Signaled Interrupts (MSI and MSI-X)"
 	depends on PCI
-	depends on (X86_LOCAL_APIC && X86_IO_APIC) || IA64
+	depends on (X86_LOCAL_APIC && X86_IO_APIC) || IA64 || PPC_PSERIES
 	help
 	   This allows device drivers to enable MSI (Message Signaled
 	   Interrupts).  Message Signaled Interrupts enable a device to
Index: 2.6/arch/powerpc/platforms/pseries/setup.c
===================================================================
--- 2.6.orig/arch/powerpc/platforms/pseries/setup.c	2006-06-07 15:29:55.000000000 -0500
+++ 2.6/arch/powerpc/platforms/pseries/setup.c	2006-06-07 15:59:28.000000000 -0500
@@ -78,6 +78,8 @@
 #endif
 
 extern void find_udbg_vterm(void);
+extern int rtas_enable_msi(struct pci_dev* pdev);
+extern void rtas_disable_msi(struct pci_dev * pdev);
 
 int fwnmi_active;  /* TRUE if an FWNMI handler is present */
 
@@ -205,6 +207,10 @@
 	} else {
 		ppc_md.init_IRQ       = xics_init_IRQ;
 		ppc_md.get_irq        = xics_get_irq;
+#ifdef CONFIG_PCI_MSI
+		ppc_md.enable_msi	= rtas_enable_msi;
+		ppc_md.disable_msi	= rtas_disable_msi;
+#endif
 	}
 
 #ifdef CONFIG_SMP

^ permalink raw reply

* Re: [PATCH 2/3] powerpc: Implement support for setting little-endian mode via prctl
From: Paul Mackerras @ 2006-06-07 21:41 UTC (permalink / raw)
  To: Olof Johansson; +Cc: akpm, linuxppc-dev
In-Reply-To: <20060607173320.GB5661@pb15.lixom.net>

Olof Johansson writes:

> >  #define CPU_FTR_LOCKLESS_TLBIE		ASM_CONST(0x0)
> >  #define CPU_FTR_MMCRA_SIHV		ASM_CONST(0x0)
> >  #define CPU_FTR_CI_LARGE_PAGE		ASM_CONST(0x0)
> > +#define CPU_FTR_PAUSE_ZERO              ASM_CONST(0x0)
> >  #define CPU_FTR_PURR			ASM_CONST(0x0)
> >  #endif
> 
> This seems to have snuck in from something else. It's not used anywhere.

Yes, early on I noticed that this one was missing from the dummy
definitions for 32-bit and cleaned it up as I went past.  I'll remove
that hunk.

Paul.

^ permalink raw reply

* MPC5200B SPI PSC3 Problem
From: Trueskew @ 2006-06-07 22:21 UTC (permalink / raw)
  To: linuxppc-embedded

[-- Attachment #1: Type: text/plain, Size: 10899 bytes --]

We have an AIC26 codec connected to our Lite5200B platform via J21.  I've
used MPC5200BUG to configure PSC3 as an SPI master, along with some samples
I've found online (including here) and some I received from Freescale.
Although the transfers seem to be working as expected (please see the output
file at the end of this message), I get only 0xffff back.  This occurs
whether or not the device is attached to J21.  A scope shows that PSC3_8
(SPI_SS) and PSC3_9 (SPI_CLK) are low at all times, and I'm concerned I'm
still doing something wrong with respect to enabling SPI over PSC3.  
 
I'm including my driver initialization code, my write code, and output
showing the write behavior.  If someone could comment on it, I would greatly
appreciate it... or if someone flat out has code to do this, I'd be happy to
take it from you.  I've seen a few variations, but whether I use them
directly or modify them as I need to, I can't seem to get past this.  Of
course, any other suggestions are welcome.
 
Thanks.
Sal
 
---------------------------------------------------------------------------
Initialization Code
---------------------------------------------------------------------------
#define GPIO_PSC3_PORT_CONFIG_MASK 0x00000f00
#ifdef SPI_USE_MCLK
   #define      GPIO_PSC3_PORT_CONFIG 0x00000700  /* PSC3 mode with mclk */
#else  /* SPI_USE_MCLK */
   #define      GPIO_PSC3_PORT_CONFIG 0x00000600  /* PSC3 mode */
#endif /* SPI_USE_MCLK */
 
#define       CDM_PSC3_MCLK_ENABLE 0x00000080
#define       CDM_PSC3_MCLK_CONFIG 0x8020      /* Divide Fvco ftom 528 to 
                                                  16Mhz */
 
#define        PSC3_SICR_REG_VALUE 0x0280f000  /* 16-bit select Codec SPI 
                                                  master mode, msb first, 
                                                  UseEOF=1. GenClk=1, SIM, 
                                                  CPOL and CPHA are 
                                                  function input */  
...                                               
   /* Select the Pin-Muxing for PSC3 Codec mode */
   gpio = (struct mpc52xx_gpio *) ioremap(MPC52xx_GPIO,
     sizeof(struct mpc52xx_gpio));
   if(gpio)
   {
      port_config = gpio->port_config;
      port_config &= ~GPIO_PSC3_PORT_CONFIG_MASK;
      port_config |= GPIO_PSC3_PORT_CONFIG;
      gpio->port_config = port_config;
      iounmap(gpio);
   }
   else
   {
      return(-1);
   }
 
#ifdef SPI_USE_MCLK
   /* PSC clock enable */
   g_pCDM->clk_enables |= CDM_PSC3_MCLK_ENABLE;
   g_pCDM->mclken_div_psc3 = CDM_PSC3_MCLK_CONFIG;
#endif /* SPI_USE_MCLK */
 
   /* Disable rx and tx */
   g_pPSC->command = MPC52xx_PSC_RST_RX;
   g_pPSC->command = MPC52xx_PSC_RST_TX;
   g_pPSC->command = MPC52xx_PSC_SEL_MODE_REG_1;
   g_pPSC->command = MPC52xx_PSC_RST_ERR_STAT;
   g_pPSC->command = MPC52xx_PSC_RX_DISABLE | MPC52xx_PSC_TX_DISABLE;  
 
   g_pPSC->mode = 0;
   g_pPSC->sicr = PSC3_SICR_REG_VALUE;
 
   #ifdef SPI_USE_MCLK
   g_pPSC->ccr=0x0703;       /* set SCK and DSCKL delay */
   #else  /* SPI_USE_MCLK */
   g_pPSC->ccr=0x0003;       /* set SCK and DSCKL delay must be > 2 */
   #endif /* SPI_USE_MCLK */
 
   g_pPSC->ctur=0x00;        /* Set DTL delay 2us */
   g_pPSC->ctlr=0x84;
 
   g_pPSC->rfalarm=100;      /* Alarm values taken from SPI example sample
*/
   g_pPSC->tfalarm=1;
 
   g_pPSC->rfcntl &= 0xf8;       /* 0 byte granularity */
   g_pPSC->tfcntl = 1;
 
   /* Enable rx & tx */
   g_pPSC->command = MPC52xx_PSC_RST_RX;
   g_pPSC->command = MPC52xx_PSC_RST_TX;
   g_pPSC->command = MPC52xx_PSC_SEL_MODE_REG_1;
   g_pPSC->command = MPC52xx_PSC_RST_ERR_STAT;
   g_pPSC->command = MPC52xx_PSC_RX_ENABLE | MPC52xx_PSC_TX_ENABLE;  
 
---------------------------------------------------------------------------
Write code
---------------------------------------------------------------------------
static int mpc52xx_spi_transfer(u16 *p_usBuffer, u16 p_usCount)
{
   u16 usIndex, usTemp;
 
   printk("------------------------------------------------------\n");
   printk("Entry:         psc=%x status=%04x tfstat=%04x rfstat=%04x
mode=%02x\n   tfnum %3d  rfnum %3d\n",
 
(int)g_pPSC,g_pPSC->mpc52xx_psc_status,g_pPSC->tfstat,g_pPSC->tfstat,g_pPSC-
>mode,
     g_pPSC->tfnum, g_pPSC->rfnum);
   g_pPSC->command = MPC52xx_PSC_RST_RX;
   g_pPSC->command = MPC52xx_PSC_RST_TX;
   g_pPSC->command = MPC52xx_PSC_SEL_MODE_REG_1;
   g_pPSC->command = MPC52xx_PSC_RST_ERR_STAT;
   g_pPSC->command = MPC52xx_PSC_RX_ENABLE | MPC52xx_PSC_TX_ENABLE;
   printk("TX-RX Enable:  psc=%x status=%04x tfstat=%04x rfstat=%04x
mode=%02x\n   tfnum %3d  rfnum %3d\n",
 
(int)g_pPSC,g_pPSC->mpc52xx_psc_status,g_pPSC->tfstat,g_pPSC->tfstat,g_pPSC-
>mode,
     g_pPSC->tfnum, g_pPSC->rfnum);
 
   /* Clean out the read FIFO */
   usIndex = 0;
   while(g_pPSC->mpc52xx_psc_status & MPC52xx_PSC_SR_RXRDY)
   {
      usTemp = g_pPSC->mpc52xx_psc_buffer_16;
      printk("Flushing Rx FIFO: psc=%x status=%04x tfstat=%04x rfstat=%04x
mode=%02x\n   tfnum %3d  rfnum %3d\n",
 
(int)g_pPSC,g_pPSC->mpc52xx_psc_status,g_pPSC->tfstat,g_pPSC->tfstat,g_pPSC-
>mode,
        g_pPSC->tfnum, g_pPSC->rfnum);
      udelay(100000);
      udelay(100000);
      udelay(100000);
      udelay(100000);
      udelay(100000);
      usIndex++;
      if(usIndex == 10) return(-1);
   }
 
   /* Send out the buffer */
   g_pPSC->command = MPC52xx_PSC_RX_ENABLE | MPC52xx_PSC_TX_DISABLE;
   for(usIndex=0; usIndex<p_usCount; usIndex++)
   {
      printk("Sending %04x:  psc=%x status=%04x tfstat=%04x rfstat=%04x
mode=%02x\n   tfnum %3d  rfnum %3d\n",
        p_usBuffer[usIndex],
(int)g_pPSC,g_pPSC->mpc52xx_psc_status,g_pPSC->tfstat,g_pPSC->tfstat,g_pPSC-
>mode,
        g_pPSC->tfnum, g_pPSC->rfnum);
      g_pPSC->mpc52xx_psc_buffer_16 = p_usBuffer[usIndex];
      printk("Sent:          psc=%x status=%04x tfstat=%04x rfstat=%04x
mode=%02x\n   tfnum %3d  rfnum %3d\n",
 
(int)g_pPSC,g_pPSC->mpc52xx_psc_status,g_pPSC->tfstat,g_pPSC->tfstat,g_pPSC-
>mode,
        g_pPSC->tfnum, g_pPSC->rfnum);
   }
   g_pPSC->command = MPC52xx_PSC_RX_ENABLE | MPC52xx_PSC_TX_ENABLE;
   usTemp = 0;
   while(g_pPSC->tfnum)
   {
      printk("TFNUM Wait:   psc=%x status=%04x tfstat=%04x rfstat=%04x
mode=%02x\n   tfnum %3d  rfnum %3d\n",
 
(int)g_pPSC,g_pPSC->mpc52xx_psc_status,g_pPSC->tfstat,g_pPSC->tfstat,g_pPSC-
>mode,
        g_pPSC->tfnum, g_pPSC->rfnum);
      udelay(100000);
      udelay(100000);
      udelay(100000);
      udelay(100000);
      udelay(100000);
      usTemp++;
      if(usTemp == 10) return(-1);
   };
   printk("TxRDY:         psc=%x status=%04x tfstat=%04x rfstat=%04x
mode=%02x\n   tfnum %3d  rfnum %3d\n",
 
(int)g_pPSC,g_pPSC->mpc52xx_psc_status,g_pPSC->tfstat,g_pPSC->tfstat,g_pPSC-
>mode,
     g_pPSC->tfnum, g_pPSC->rfnum);
   for(usIndex=0; usIndex<p_usCount; usIndex++)
   {
      usTemp = 0;
      while(!(g_pPSC->mpc52xx_psc_status & MPC52xx_PSC_SR_RXRDY))
      {
         printk("RxRDY Wait:    psc=%x status=%04x tfstat=%04x rfstat=%04x
mode=%02x\n   tfnum %3d  rfnum %3d\n",
 
(int)g_pPSC,g_pPSC->mpc52xx_psc_status,g_pPSC->tfstat,g_pPSC->tfstat,g_pPSC-
>mode,
           g_pPSC->tfnum, g_pPSC->rfnum);
         udelay(100000);
         udelay(100000);
         udelay(100000);
         udelay(100000);
         udelay(100000);
         usTemp++;
         if(usTemp == 10) return(-1);
      };
      printk("RxRDY:         psc=%x status=%04x tfstat=%04x rfstat=%04x
mode=%02x\n   tfnum %3d  rfnum %3d\n",
 
(int)g_pPSC,g_pPSC->mpc52xx_psc_status,g_pPSC->tfstat,g_pPSC->tfstat,g_pPSC-
>mode,
        g_pPSC->tfnum, g_pPSC->rfnum);
      p_usBuffer[usIndex] = g_pPSC->mpc52xx_psc_buffer_16;
      printk("  Received %04x\n", p_usBuffer[usIndex]);
   }
 
   printk("EXIT:          psc=%x status=%04x tfstat=%04x rfstat=%04x
mode=%02x\n   tfnum %3d  rfnum %3d\n",
 
(int)g_pPSC,g_pPSC->mpc52xx_psc_status,g_pPSC->tfstat,g_pPSC->tfstat,g_pPSC-
>mode,
     g_pPSC->tfnum, g_pPSC->rfnum);
   return(usIndex);
}
 

---------------------------------------------------------------------------
Output
---------------------------------------------------------------------------
The lines below are a capture of register settings from my driver,
along with output lines when trying to write 16 bit words to our
device.  This output is consistent whether the device is connected
or not (via J21).  Basic operation, delimited by "------...---":
 
  - Disable TX, Enable RX (despite the "TX-RX Enable" heading)
  - Send word 1
  - Send word 2
  - Enable TX, Enable RX
  - Wait for tfnum == 0
  - Receive data
 
port_config=91051624 sicr=0280f000 clk_enables=00ffffff div_psc3=800f
------------------------------------------------------
Entry:         psc=f0002400 status=0400 tfstat=0003 rfstat=0003 mode=33
   tfnum   0  rfnum   0
TX-RX Enable:  psc=f0002400 status=0400 tfstat=0003 rfstat=0003 mode=33
   tfnum   0  rfnum   0
Sending 0880:  psc=f0002400 status=0400 tfstat=0003 rfstat=0003 mode=07
   tfnum   0  rfnum   0
Sent:          psc=f0002400 status=0000 tfstat=0002 rfstat=0002 mode=07
   tfnum   2  rfnum   0
Sending bb00:  psc=f0002400 status=0000 tfstat=0002 rfstat=0002 mode=07
   tfnum   2  rfnum   0
Sent:          psc=f0002400 status=0000 tfstat=0002 rfstat=0002 mode=07
   tfnum   4  rfnum   0
TFNUM Wait:   psc=f0002400 status=0000 tfstat=0002 rfstat=0002 mode=07
   tfnum   2  rfnum   0
TxRDY:         psc=f0002400 status=0500 tfstat=0003 rfstat=0003 mode=07
   tfnum   0  rfnum   4
RxRDY:         psc=f0002400 status=0500 tfstat=0003 rfstat=0003 mode=07
   tfnum   0  rfnum   4
  Received ffff
RxRDY:         psc=f0002400 status=0500 tfstat=0003 rfstat=0003 mode=07
   tfnum   0  rfnum   2
  Received ffff
EXIT:          psc=f0002400 status=0400 tfstat=0003 rfstat=0003 mode=07
   tfnum   0  rfnum   0
------------------------------------------------------
Entry:         psc=f0002400 status=0400 tfstat=0003 rfstat=0003 mode=07
   tfnum   0  rfnum   0
TX-RX Enable:  psc=f0002400 status=0400 tfstat=0003 rfstat=0003 mode=33
   tfnum   0  rfnum   0
Sending 8820:  psc=f0002400 status=0400 tfstat=0003 rfstat=0003 mode=07
   tfnum   0  rfnum   0
Sent:          psc=f0002400 status=0000 tfstat=0002 rfstat=0002 mode=07
   tfnum   2  rfnum   0
Sending 0000:  psc=f0002400 status=0000 tfstat=0002 rfstat=0002 mode=07
   tfnum   2  rfnum   0
Sent:          psc=f0002400 status=0000 tfstat=0002 rfstat=0002 mode=07
   tfnum   4  rfnum   0
TFNUM Wait:   psc=f0002400 status=0000 tfstat=0002 rfstat=0002 mode=07
   tfnum   2  rfnum   0
TxRDY:         psc=f0002400 status=0500 tfstat=0003 rfstat=0003 mode=07
   tfnum   0  rfnum   4
RxRDY:         psc=f0002400 status=0500 tfstat=0003 rfstat=0003 mode=07
   tfnum   0  rfnum   4
  Received ffff
RxRDY:         psc=f0002400 status=0500 tfstat=0003 rfstat=0003 mode=07
   tfnum   0  rfnum   2
  Received ffff
EXIT:          psc=f0002400 status=0400 tfstat=0003 rfstat=0003 mode=07
   tfnum   0  rfnum   0

[-- Attachment #2: Type: text/html, Size: 20391 bytes --]

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox