LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* 440gx dma transfer with no data change at dest
From: John Lange @ 2006-05-26 20:58 UTC (permalink / raw)
  To: linuxppc-embedded

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

Hi Kevin

 

I saw a posting from March where you were asking about problems doing
memory to memory DMA transfers with the ppc4xx_dma module of Linux.  I'm
curious if you figured out that issue.  Would you be kind enough to
share how you fixed the issue you were having?  I would sure appreciate
a snippet of working example code if it's not proprietary.

 

Best regards,

John

 


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

^ permalink raw reply

* Re: [snd] looking for layout-ids
From: Benjamin Herrenschmidt @ 2006-05-26 21:41 UTC (permalink / raw)
  To: Eddy Petrişor; +Cc: linuxppc-dev list, Johannes Berg, debian-powerpc
In-Reply-To: <60381eeb0605260902p4420757ai3669aba711122014@mail.gmail.com>

On Fri, 2006-05-26 at 19:02 +0300, Eddy Petrişor wrote:
> On 5/25/06, Johannes Berg <johannes@sipsolutions.net> wrote:
> > Hey,
> 
> Hi,
> 
> > [1] to find your layout-id, execute the following:
> >
> >   find /proc/device-tree/ -name layout-id | xargs hexdump -e '1/4 "0x%x\n"'
> >
> > If you get no output, you have no layout-id property. If you do get
> 
> What are the plans for machines without layout-id, like the 5,2 model?
> Will there be any alsa/aoa support at some point?

There will be, but let's get the ones with layout-id first since
snd-powermac should work on the older ones.

Ben.

^ permalink raw reply

* Re: [RFC] snd-aoa and interrupts (headphone detection etc)
From: Benjamin Herrenschmidt @ 2006-05-26 21:45 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linuxppc-dev list
In-Reply-To: <1148055359.6228.20.camel@johannes.berg>


> The problematic part is things that the codec must control. Say we want
> line-in detection to automatically switch to line-in if microphone is
> selected (does anyone ever want this?). Then the problem is that the
> interrupt arrives at the GPIO layer, and I can easily make it seen in
> the fabric too. However, then propagating it to the codec is a bit
> harder. Or we don't have it in the fabric but have the codec register
> for that interrupt (through our GPIO layer). This is the first option.

No. I don't think the codecs should mess with the GPIOs directly. The
policy should be implemented in the fabric. If in some case you need to
change some codec settings, then the codec shall provide an accessor for
doing so.

> The second option is changing the whole in-/output control code that we
> have and moving it from the codec to the fabric layer. The fabric
> already knows what in- and outputs a codec has (in order to know what is
> connected), hence if we added a codec driver function to turn on/off any
> in- or output we could have the fabric control this. But then we'd also
> have to make known to the codec which of those are mutually exclusive,
> and generally make it more complicated.

We don't have to make known to the codec, we just turn on/off the right
ones.

> I currently favour the first option, the codec driver can know when it
> makes sense to try registering the interrupt (if it isn't present it
> fails anyway) and then do the appropriate stuff (possibly giving the
> user a choice).

I disagree :) But then, it's your code :)

Ben.

^ permalink raw reply

* Howto allocate non-cacheable memory
From: Muneeswaran P - TLS, Chennai @ 2006-05-27  5:38 UTC (permalink / raw)
  To: linuxppc-embedded

Hi,

How to allocate non-cacheable kernel memory ?

Pls help me.


	I am writing device driver (kernel 2.6 ) for PCI-X card.

	Pls clarify the following doubts:
	1. DPRAM is memory mapped to PCI-X card.
	2. I have to transfer data from main memory to DPRAM using bus
master concept and vice-versa.
	3. How to make this memory area as non-cacheable one ?=20
	4. Whether DMA mapping (pci_map_single() call ) should be done for
both DPRAM and main memory ?
	   4.a) I will get the physical address of DPRAM memory (memory
mapped to PCI-X card) from BAR-0 register. Can i use this physical =
addres
directly for data transer using bus master (without pci_map_single() =
call.)
?
	   4.b)  How to make main memory area as non-cacheable one ?


Regards,
Munees.
DISCLAIMER=20
The contents of this e-mail and any attachment(s) are confidential and =
intended for the=20

named recipient(s) only. It shall not attach any liability on the =
originator or HCL or its=20

affiliates. Any views or opinions presented in this email are solely =
those of the author and=20

may not necessarily reflect the opinions of HCL or its affiliates. Any =
form of reproduction,=20

dissemination, copying, disclosure, modification, distribution and / or =
publication of this=20

message without the prior written consent of the author of this e-mail =
is strictly=20

prohibited. If you have received this email in error please delete it =
and notify the sender=20

immediately. Before opening any mail and attachments please check them =
for viruses and=20

defect.

^ permalink raw reply

* [PATCH, 2.6.17-rc5-git3] ieee80211softmac_io.c: fix warning "defined but not used"
From: Toralf Förster @ 2006-05-27  9:04 UTC (permalink / raw)
  To: John W. Linville; +Cc: netdev, linuxppc-dev

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

Got this compiler warning and Johannes Berg <johannes@sipsolutions.net> wrote:

Yeah, known 'bug', we have that code there but never use it. Feel free
to submit a patch (to John Linville, CC netdev and softmac-dev) to
remove it.
 
Signed-off-by: Toralf Foerster <toralf.foerster@gmx.de>

--- src/net/ieee80211/softmac/ieee80211softmac_io.c_orig	2006-05-27 10:50:56.000000000 +0200
+++ src/net/ieee80211/softmac/ieee80211softmac_io.c	2006-05-27 10:51:45.000000000 +0200
@@ -440,47 +440,3 @@
 	return 0;
 }
 
-
-/* Create an rts/cts frame */
-static u32
-ieee80211softmac_rts_cts(struct ieee80211_hdr_2addr **pkt,
-	struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net, 
-	u32 type)
-{
-	/* Allocate Packet */
-	(*pkt) = kmalloc(IEEE80211_2ADDR_LEN, GFP_ATOMIC);	
-	memset(*pkt, 0, IEEE80211_2ADDR_LEN);
-	if((*pkt) == NULL)
-		return 0;
-	ieee80211softmac_hdr_2addr(mac, (*pkt), type, net->bssid);
-	return IEEE80211_2ADDR_LEN;
-}
-
-
-/* Sends a control packet */
-static int
-ieee80211softmac_send_ctl_frame(struct ieee80211softmac_device *mac,
-	struct ieee80211softmac_network *net, u32 type, u32 arg)
-{
-	void *pkt = NULL;
-	u32 pkt_size = 0;
-	
-	switch(type) {
-	case IEEE80211_STYPE_RTS:
-	case IEEE80211_STYPE_CTS:
-		pkt_size = ieee80211softmac_rts_cts((struct ieee80211_hdr_2addr **)(&pkt), mac, net, type);
-		break;
-	default:
-		printkl(KERN_DEBUG PFX "Unsupported Control Frame type: %i\n", type);
-		return -EINVAL;
-	}
-
-	if(pkt_size == 0)
-		return -ENOMEM;
-	
-	/* Send the packet to the ieee80211 layer for tx */
-	ieee80211_tx_frame(mac->ieee, (struct ieee80211_hdr *) pkt, pkt_size);
-
-	kfree(pkt);
-	return 0;
-}

-- 
MfG/Sincerely
Toralf Förster

[-- Attachment #2: Type: application/pgp-signature, Size: 191 bytes --]

^ permalink raw reply

* Re: [PATCH] force 64bit mode in system_reset_fwnmi for broken POWER4 firmware
From: Olaf Hering @ 2006-05-27 11:34 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev
In-Reply-To: <17526.59069.781220.922878@cargo.ozlabs.ibm.com>

 On Fri, May 26, Paul Mackeras wrote:

> Olaf Hering writes:
> 
> > According to this change for EXCEPTION_PROLOG_COMMON, I get still into
> > decremeter_common, but its not fatal anymore because the cpu is now in
> > 64bit mode and the stack is forced to PACAKSAVE(r13).
> > 
> >         subi    r1,r1,INT_FRAME_SIZE;   /* alloc frame on kernel stack  */ \
> >         beq-    1f;                                                        \
> >         ld      r1,PACAKSAVE(r13);      /* kernel stack to use          */ \
> > -1:     cmpdi   cr1,r1,0;               /* check if r1 is in userspace  */ \
> > +1:                                     \
> > +       cmpdi   cr1,r29,0x42;           \
> 
> Ummm, what's r29 supposed to have in it here?
> 
> > +       bne     cr1,2f;                 \
> > +       li      r29,2f@l;               \
> 
> And why are we setting it?
> 
> Does it look like the SRR0 and SRR1 values are correct when we get
> this problem occurring?  Is it just the MSR that is bogus?

I looked into this again, my debug patch was wrong. I cant rely on the
hello32, r29 has to be set in the system_reset path. This is the
register dump. It shows that the cpus are in 32bit mode, and that
system_reset_fwnmi calls system_reset_common correctly.


0:mon> r
R00 = 000000001000036c   R16 = 0000000000000042
R01 = 00000000ffe96ad0   R17 = 0000000000000042
R02 = 000000001009b470   R18 = 0000000000000042
R03 = 0000000000000009   R19 = 0000000000000042
R04 = 000000001002228c   R20 = 0000000000000042
R05 = 0000000040042082   R21 = 0000000000000042
R06 = 0000000000004000   R22 = 0000000000000042
R07 = 0000000010008af0   R23 = 0000000000000042
R08 = 0000000000000000   R24 = 0000000000000042
R09 = 0000000000000000   R25 = 0000000000000042
R10 = 8000000000001032   R26 = 0000000000000042
R11 = 00000000ffe96a50   R27 = 0000000000000042
R12 = 0000000020000082   R28 = 0000000000000042
R13 = 000000001009a410   R29 = 0000000000003220
R14 = 0000000000000042   R30 = 0000000000001002
R15 = 0000000000000042   R31 = a000000000001032
pc  = 00000000100003b4
lr  = 000000001000036c
msr = 000000000000d032   cr  = 20000082
ctr = 0000000000032ddc   xer = 00000000000fffff   trap =  100
0:mon> c1
1:mon> r
R00 = 000000001000036c   R16 = 0000000000000042
R01 = 00000000ffe96ad0   R17 = 0000000000000042
R02 = 000000001009b470   R18 = 0000000000000042
R03 = 0000000000000007   R19 = 0000000000000042
R04 = 000000001002228c   R20 = 0000000000000042
R05 = 0000000040022082   R21 = 0000000000000042
R06 = 0000000000004000   R22 = 0000000000000042
R07 = 0000000010008af0   R23 = 0000000000000042
R08 = 0000000000000000   R24 = 0000000000000042
R09 = 0000000000000000   R25 = 0000000000000042
R10 = 8000000000001032   R26 = 0000000000000042
R11 = 00000000ffe96a50   R27 = 0000000000000042
R12 = 0000000020000082   R28 = 0000000000000042
R13 = 000000001009a410   R29 = 0000000000003220
R14 = 0000000000000042   R30 = 0000000000001002
R15 = 0000000000000042   R31 = a000000000001032
pc  = 00000000100003b4
lr  = 000000001000036c
msr = 000000000000d032   cr  = 20000082
ctr = 0000000000032ddc   xer = 00000000000fffff   trap =  100




Index: linux-2.6/arch/powerpc/kernel/head_64.S
===================================================================
--- linux-2.6.orig/arch/powerpc/kernel/head_64.S
+++ linux-2.6/arch/powerpc/kernel/head_64.S
@@ -211,6 +211,31 @@ exception_marker:
 	ori	reg,reg,(label)@l;	/* virt addr of handler ... */
 #endif
 
+#define EXCEPTION_PROLOG_PSERIES_BROKEN_POWER4_FIRMWARE(area, label)				\
+	mfspr	r13,SPRN_SPRG3;		/* get paca address into r13 */	\
+	std	r9,area+EX_R9(r13);	/* save r9 - r12 */		\
+	std	r10,area+EX_R10(r13);					\
+	std	r11,area+EX_R11(r13);					\
+	std	r12,area+EX_R12(r13);					\
+	mfspr	r9,SPRN_SPRG1;						\
+	std	r9,area+EX_R13(r13);					\
+	mfcr	r9;							\
+	clrrdi	r12,r13,32;		/* get high part of &label */	\
+	mfmsr	r10;							\
+	mr	r30,r10;						\
+	li	r11,5;			/* MSR_SF_LG|MSR_ISF_LG */	\
+	rldicr	r11,r11,61,2;		/* (5 << 61) */	\
+	or	r10,r10,r11;						\
+	mfspr	r11,SPRN_SRR0;		/* save SRR0 */			\
+	LOAD_HANDLER(r12,label)						\
+	ori	r10,r10,MSR_IR|MSR_DR|MSR_RI;				\
+	mtspr	SPRN_SRR0,r12;						\
+	mfspr	r12,SPRN_SRR1;		/* and SRR1 */			\
+	mr	r31,r10;						\
+	mtspr	SPRN_SRR1,r10;						\
+	rfid;								\
+	b	.	/* prevent speculative execution */
+
 #define EXCEPTION_PROLOG_PSERIES(area, label)				\
 	mfspr	r13,SPRN_SPRG3;		/* get paca address into r13 */	\
 	std	r9,area+EX_R9(r13);	/* save r9 - r12 */		\
@@ -269,7 +294,12 @@ exception_marker:
 	subi	r1,r1,INT_FRAME_SIZE;	/* alloc frame on kernel stack	*/ \
 	beq-	1f;							   \
 	ld	r1,PACAKSAVE(r13);	/* kernel stack to use		*/ \
-1:	cmpdi	cr1,r1,0;		/* check if r1 is in userspace	*/ \
+1:				\
+	cmpdi   cr1,r29,0x42;	\
+	bne     cr1,2f;		\
+	li      r29,2f@l;	\
+2: ;				\
+	cmpdi	cr1,r1,0;		/* check if r1 is in userspace	*/ \
 	bge-	cr1,bad_stack;		/* abort if it is		*/ \
 	std	r9,_CCR(r1);		/* save CR in stackframe	*/ \
 	std	r11,_NIP(r1);		/* save SRR0 in stackframe	*/ \
@@ -600,14 +630,15 @@ slb_miss_user_pseries:
 system_reset_fwnmi:
 	HMT_MEDIUM
 	mtspr	SPRN_SPRG1,r13		/* save r13 */
-	EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, system_reset_common)
+	li	r29,0x42
+	EXCEPTION_PROLOG_PSERIES_BROKEN_POWER4_FIRMWARE(PACA_EXGEN, system_reset_common)
 
 	.globl machine_check_fwnmi
       .align 7
 machine_check_fwnmi:
 	HMT_MEDIUM
 	mtspr	SPRN_SPRG1,r13		/* save r13 */
-	EXCEPTION_PROLOG_PSERIES(PACA_EXMC, machine_check_common)
+	EXCEPTION_PROLOG_PSERIES_BROKEN_POWER4_FIRMWARE(PACA_EXMC, machine_check_common)
 
 #ifdef CONFIG_PPC_ISERIES
 /***  ISeries-LPAR interrupt handlers ***/

^ permalink raw reply

* please pull powerpc.git 'merge' branch
From: Paul Mackerras @ 2006-05-27 13:17 UTC (permalink / raw)
  To: torvalds; +Cc: linuxppc-dev

Linus,

Please do a pull from the "merge" branch of

git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc.git

There are 3 commits there: an update to MAINTAINERS, a fix for the
"Maple" PPC970FX evaluation boards, and a fix for the Freescale
embedded 8272 processor that is apparently needed for the console to
work on those chips.  These fixes won't affect any other platforms and
are safe to go in for 2.6.17 IMO.

Thanks,
Paul.

 MAINTAINERS                             |    4 +--
 arch/powerpc/kernel/prom_init.c         |   48 +++++++++++++++++++++++++++++--
 arch/ppc/platforms/mpc8272ads_setup.c   |   10 +++---
 arch/ppc/syslib/pq2_devices.c           |   16 +++++-----
 arch/ppc/syslib/pq2_sys.c               |    8 +++--
 drivers/serial/cpm_uart/cpm_uart_core.c |    8 +++--
 drivers/serial/cpm_uart/cpm_uart_cpm2.c |    2 +
 7 files changed, 70 insertions(+), 26 deletions(-)


commit 54f4ee183aea859eb09f141dad3fc3c6f4fe0446
Author: Hollis Blanchard <hollisb@us.ibm.com>
Date:   Thu May 25 16:36:53 2006 -0500

    [PATCH] powerpc: fix RTC/NVRAM accesses on Maple
    
    Due to a firmware device tree bug, RTC and NVRAM accesses (including
    halt/reboot) on Maple have been broken since January, when an untested
    build fix went in. This code patches the device tree in Linux.
    
    Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
    Signed-off-by: Segher Boessenkool <segher@kernel.crashing.org>
    Signed-off-by: Paul Mackerras <paulus@samba.org>

commit 8e30a9a299ca30b6c4072c2182238d5f5dd1590d
Author: Vitaly Bordug <vbordug@ru.mvista.com>
Date:   Wed May 24 21:40:18 2006 +0400

    [PATCH] ppc32 CPM_UART: various fixes for pq2 uart users
    
    This fixes various odd things that missed update together with cpm_uart
    platform_device move. Unified resources names, restructurisation, etc.
    Also, addressed issue with recent phys/virt translation rework. Being
    cache-coherent, CPM2's do alloc_bootmem() for the console stuff, and it was
    used to treat console buffer descriptor mapping 1:1 (as in CPM1 case),
    which is definitely wrong.
    
    Signed-off-by: Vitaly Bordug <vbordug@ru.mvista.com>
    Signed-off-by: Paul Mackerras <paulus@samba.org>

commit 6d923f98fe0f31c174ace92f8b680d0d153663aa
Author: Arthur Othieno <apgo@patchbomb.org>
Date:   Fri May 19 06:22:23 2006 -0400

    [PATCH] powerpc: linuxppc64.org no more
    
    http://linuxppc64.org has long been a redirect to the canonical
    http://penguinppc.org/ppc64/ -- update all instances accordingly,
    as ACKed by Hollis:
    
    On Wed, Jan 18, 2006 at 09:48:08AM -0600, Hollis Blanchard wrote:
    > On Wed, 2006-01-18 at 13:07 +0100, Olaf Hering wrote:
    > > On Wed, Jan 18, Arthur Othieno wrote:
    > > >
    > > > What about the s/linuxppc64\.org/penguinppc\.org/g case? Or is
    > > > penguinppc64.org preferable? Or am I just taking it too far? ;)
    > >
    > > They are redirected on DNS or HTTP level.
    >
    > HTTP level, but that doesn't answer his question.
    >
    > As the maintainer of that site, I would prefer to remove the
    > linuxppc64.org reference.
    
    Signed-off-by: Arthur Othieno <apgo@patchbomb.org>
    Signed-off-by: Paul Mackerras <paulus@samba.org>

^ permalink raw reply

* Re: [PATCH] make ams work with latest kernels
From: Pavel Machek @ 2006-05-26 17:39 UTC (permalink / raw)
  To: Stelian Pop
  Cc: Andrew Morton, linuxppc-dev list, Johannes Berg,
	Linux Kernel list
In-Reply-To: <1148465069.6723.26.camel@localhost.localdomain>

Hi!

> From: Stelian Pop <stelian@popies.net>
> 
> This driver provides support for the Apple Motion Sensor (ams),
> which provides an accelerometer and other misc. data.
> Some Apple PowerBooks (the series the PowerBook5,6 falls into,
> later ones have a slightly different one the driver doesn't handle)
> are supported. The accelerometer data is readable via sysfs.
> 
> This driver also provides an absolute input class device, allowing
> the laptop to act as a pinball machine-esque joystick.

Is it useful to have /sysfs interface when we already have same data
available as joystick?

-- 
Thanks for all the (sleeping) penguins.

^ permalink raw reply

* Re: [PATCH] make ams work with latest kernels
From: Johannes Berg @ 2006-05-27 20:06 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Andrew Morton, Linux Kernel list, linuxppc-dev list
In-Reply-To: <20060526173908.GA3357@ucw.cz>

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

On Fri, 2006-05-26 at 17:39 +0000, Pavel Machek wrote:

> > This driver also provides an absolute input class device, allowing
> > the laptop to act as a pinball machine-esque joystick.
> 
> Is it useful to have /sysfs interface when we already have same data
> available as joystick?

Might be useful if you need to turn off the "joystick" because X gets
confused with it. Other than that, no.

johannes

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

^ permalink raw reply

* Re: [PATCH] make ams work with latest kernels
From: Stelian Pop @ 2006-05-27 20:31 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Andrew Morton, linuxppc-dev list, Linux Kernel list, Pavel Machek
In-Reply-To: <1148760414.16989.59.camel@johannes.berg>

Le samedi 27 mai 2006 à 22:06 +0200, Johannes Berg a écrit :
> On Fri, 2006-05-26 at 17:39 +0000, Pavel Machek wrote:
> 
> > > This driver also provides an absolute input class device, allowing
> > > the laptop to act as a pinball machine-esque joystick.
> > 
> > Is it useful to have /sysfs interface when we already have same data
> > available as joystick?
> 
> Might be useful if you need to turn off the "joystick" because X gets
> confused with it. Other than that, no.

/sysfs interface also exports the 'z' coordinate, the input interface
does not.

Stelian.
-- 
Stelian Pop <stelian@popies.net>

^ permalink raw reply

* Re: [RFC] snd-aoa and interrupts (headphone detection etc)
From: Johannes Berg @ 2006-05-27 20:41 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev list
In-Reply-To: <1148679918.8089.166.camel@localhost.localdomain>

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

On Sat, 2006-05-27 at 07:45 +1000, Benjamin Herrenschmidt wrote:
> > The problematic part is things that the codec must control. Say we want
> > line-in detection to automatically switch to line-in if microphone is
> > selected (does anyone ever want this?). Then the problem is that the
> > interrupt arrives at the GPIO layer, and I can easily make it seen in
> > the fabric too. However, then propagating it to the codec is a bit
> > harder. Or we don't have it in the fabric but have the codec register
> > for that interrupt (through our GPIO layer). This is the first option.
> 
> No. I don't think the codecs should mess with the GPIOs directly. The
> policy should be implemented in the fabric. If in some case you need to
> change some codec settings, then the codec shall provide an accessor for
> doing so.

Yeah. That was my second option mostly.

> > The second option is changing the whole in-/output control code that we
> > have and moving it from the codec to the fabric layer. The fabric
> > already knows what in- and outputs a codec has (in order to know what is
> > connected), hence if we added a codec driver function to turn on/off any
> > in- or output we could have the fabric control this. But then we'd also
> > have to make known to the codec which of those are mutually exclusive,
> > and generally make it more complicated.
> 
> We don't have to make known to the codec, we just turn on/off the right
> ones.

Hmm, ok, good point, then we don't even need the complication of telling
the codec what is connected to it. Maybe that simplifies the code in
total... But the codecs will still need hardcoded numbers for the
various in- and outputs that the fabric needs to know.

I'll think about this a bit more, maybe experiment with the code a
bit :)

johannes

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

^ permalink raw reply

* Re: [PATCH] make ams work with latest kernels
From: Pavel Machek @ 2006-05-27 20:34 UTC (permalink / raw)
  To: Stelian Pop
  Cc: Andrew Morton, linuxppc-dev list, Johannes Berg,
	Linux Kernel list
In-Reply-To: <1148761919.3132.1.camel@deep-space-9.dsnet>

On So 27-05-06 22:31:58, Stelian Pop wrote:
> Le samedi 27 mai 2006 ? 22:06 +0200, Johannes Berg a écrit :
> > On Fri, 2006-05-26 at 17:39 +0000, Pavel Machek wrote:
> > 
> > > > This driver also provides an absolute input class device, allowing
> > > > the laptop to act as a pinball machine-esque joystick.
> > > 
> > > Is it useful to have /sysfs interface when we already have same data
> > > available as joystick?
> > 
> > Might be useful if you need to turn off the "joystick" because X gets
> > confused with it. Other than that, no.
> 
> /sysfs interface also exports the 'z' coordinate, the input interface
> does not.

Can that be fixed? z coordinate should be useful for input, too.

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply

* Tutorial on PPC exception
From: jeanwelly @ 2006-05-28  3:18 UTC (permalink / raw)
  To: linuxppc-embedded@ozlabs.org


SGkgQWxsLA0KRG9lcyBhbnlib2R5IGtub3cgaG93IHRvIGdldCB0aGUgZnVsbCBleGNlcHRpb25z
IG9mIHRoZSBQUEM/IFdoaWNoIHR1dG9yaWFsIGNvdWxkIGJlIGZldGNoZWQgYXMgYSByZWZlcmVu
Y2U/DQoNCkkgZ290IGFuIGV4Y2VwdGlvbiBmcm9tIHRoZSBoYXJkd2FyZSwgYnV0IEkgZG9uJ3Qg
a25vdyB0aGUgZGV0YWlsZWQgbWVhbmluZ3MsIGJlY3Vhc2UgaXQgd2FzIHRha2VuIGFzIGFuICJ1
bmtub3duIGV4cGVjdGlvbiIgYnkgbXkgc3lzdGVtLiBQZXJzb25hbGx5LCBJIHRoaW5rIGFuIGV4
Y2VwdGlvbiBpcyBhbiBodyBiZWhhcmlvdXIsIHNvIEkgZ3Vlc3MgdGhlcmUgc2hvdWxkIGJlIGFu
IHRoaXMga2luZCBvZiBkb2N1bWVudC4NCg0KVGhhbmsgeW91ISAJCQkJDQoNCqGhoaGhoaGhoaGh
oaGhoaFqZWFud2VsbHkNCqGhoaGhoaGhoaGhoaGhoaFqZWFud2VsbHlAMTI2LmNvbQ0KoaGhoaGh
oaGhoaGhoaGhoaGhoaEyMDA2LTA1LTI4DQo=

^ permalink raw reply

* [PATCH] powerpc: add dmesg command to xmon
From: Olaf Hering @ 2006-05-28 10:46 UTC (permalink / raw)
  To: linuxppc-dev


Based on work by Linas Vepstas:
dump dmesg buffer in xmon, with a new 'D' command.
Use kallsyms to lookup the symbols to avoid touching generic code in printk.c

Signed-off-by: Olaf Hering <olh@suse.de>

---
 arch/powerpc/xmon/xmon.c |  112 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)

Index: linux-2.6/arch/powerpc/xmon/xmon.c
===================================================================
--- linux-2.6.orig/arch/powerpc/xmon/xmon.c
+++ linux-2.6/arch/powerpc/xmon/xmon.c
@@ -143,6 +143,7 @@ void dump_segments(void);
 static void symbol_lookup(void);
 static void xmon_print_symbol(unsigned long address, const char *mid,
 			      const char *after);
+static void xmon_show_dmesg(void);
 static const char *getvecname(unsigned long vec);
 
 extern int print_insn_powerpc(unsigned long, unsigned long, int);
@@ -192,6 +193,7 @@ Commands:\n\
   df	dump float values\n\
   dd	dump double values\n\
   dr	dump stream of raw bytes\n\
+  D	show dmesg (printk) buffer\n\
   e	print exception information\n\
   f	flush cache\n\
   la	lookup symbol+offset of specified address\n\
@@ -781,6 +783,9 @@ cmds(struct pt_regs *excp)
 		case 'd':
 			dump();
 			break;
+		case 'D':
+			xmon_show_dmesg();
+			break;
 		case 'l':
 			symbol_lookup();
 			break;
@@ -2488,6 +2493,113 @@ static void xmon_print_symbol(unsigned l
 	printf("%s", after);
 }
 
+#ifdef CONFIG_KALLSYMS
+static const char *xmon_log_buf;
+static int xmon_log_buf_len;
+static unsigned long xmon_log_end, xmon_logged_chars;
+
+static int xmon_init_dmesg(void)
+{
+	const char **p;
+	const int *i;
+	const unsigned long *l;
+
+	if (!xmon_log_buf && setjmp(bus_error_jmp) == 0) {
+		catch_memory_errors = 1;
+		sync();
+
+		p = (const char **)kallsyms_lookup_name("log_buf");
+		if (p) {
+			xmon_log_buf = *p;
+			sync();
+			__delay(200);
+		}
+
+		i = (const int *)kallsyms_lookup_name("log_buf_len");
+		if (i) {
+			xmon_log_buf_len = *i;
+			sync();
+			__delay(200);
+		}
+
+		l = (const unsigned long *)kallsyms_lookup_name("log_end");
+		if (l) {
+			xmon_log_end = *l;
+			sync();
+			__delay(200);
+		}
+
+		l = (const unsigned long *)kallsyms_lookup_name("logged_chars");
+		if (l) {
+			xmon_logged_chars = *l;
+			sync();
+			__delay(200);
+		}
+
+	}
+	catch_memory_errors = 0;
+	return !!xmon_log_buf;
+}
+#else
+static inline int xmon_init_dmesg(void)
+{
+	return 0;
+}
+#endif
+
+static void xmon_show_dmesg(void)
+{
+	if (xmon_init_dmesg()) {
+		const char *p_start, *p_end, *l_start, *l_end, *start, *end;
+		char c;
+
+		p_start = xmon_log_buf;
+		p_end = p_start + xmon_log_buf_len;
+		l_start = p_start + xmon_log_end - (xmon_logged_chars < xmon_log_buf_len ? xmon_logged_chars : xmon_log_buf_len);
+		l_end = p_start + xmon_log_end;
+		if (l_start == l_end)
+			return;
+		start = p_start + (l_start - p_start) % xmon_log_buf_len;
+		end = p_start + (l_end - p_start) % xmon_log_buf_len;
+		c = '\0';
+		while (1) {
+			const char *p;
+			int chars = 0;
+			if (!*start) {
+				while (!*start) {
+					start++;
+					if (start < p_start)
+						start = p_end - 1;
+					else if (start >= p_end)
+						start = p_start;
+					if (start == end)
+						break;
+				}
+				if (start == end)
+					break;
+			}
+			p = start;
+			while (*start && chars < 200) {
+				c = *start;
+				chars++;
+				start++;
+				if (start < p_start)
+					start = p_end - 1;
+				else if (start >= p_end)
+					start = p_start;
+				if (start == end)
+					break;
+			}
+			if (chars)
+				printf("%.*s", chars, p);
+			if (start == end)
+				break;
+		}
+		if (c != '\n')
+			printf("\n");
+	}
+}
+
 #ifdef CONFIG_PPC64
 static void dump_slb(void)
 {

^ permalink raw reply

* Re: [PATCH] powerpc: add dmesg command to xmon
From: Arnd Bergmann @ 2006-05-28 12:04 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Olaf Hering
In-Reply-To: <20060528104621.GA7126@suse.de>

T24gU3VuZGF5IDI4IE1heSAyMDA2IDEyOjQ2LCBPbGFmIEhlcmluZyB3cm90ZToKPiArc3RhdGlj
IGludCB4bW9uX2luaXRfZG1lc2codm9pZCkKPiArewo+ICugoKCgoKCgY29uc3QgY2hhciAqKnA7
Cj4gK6CgoKCgoKBjb25zdCBpbnQgKmk7Cj4gK6CgoKCgoKBjb25zdCB1bnNpZ25lZCBsb25nICps
Owo+ICsKPiAroKCgoKCgoGlmICgheG1vbl9sb2dfYnVmICYmIHNldGptcChidXNfZXJyb3Jfam1w
KSA9PSAwKSB7Cj4gK6CgoKCgoKCgoKCgoKCgoGNhdGNoX21lbW9yeV9lcnJvcnMgPSAxOwo+ICug
oKCgoKCgoKCgoKCgoKBzeW5jKCk7Cj4gKwo+ICugoKCgoKCgoKCgoKCgoKBwID0gKGNvbnN0IGNo
YXIgKiopa2FsbHN5bXNfbG9va3VwX25hbWUoImxvZ19idWYiKTsKPiAroKCgoKCgoKCgoKCgoKCg
aWYgKHApIHsKPiAroKCgoKCgoKCgoKCgoKCgoKCgoKCgoKB4bW9uX2xvZ19idWYgPSAqcDsKPiAr
oKCgoKCgoKCgoKCgoKCgoKCgoKCgoKBzeW5jKCk7Cj4gK6CgoKCgoKCgoKCgoKCgoKCgoKCgoKCg
X19kZWxheSgyMDApOwo+ICugoKCgoKCgoKCgoKCgoKB9Cj4gKwo+ICugoKCgoKCgoKCgoKCgoKBp
ID0gKGNvbnN0IGludCAqKWthbGxzeW1zX2xvb2t1cF9uYW1lKCJsb2dfYnVmX2xlbiIpOwo+ICug
oKCgoKCgoKCgoKCgoKBpZiAoaSkgewo+ICugoKCgoKCgoKCgoKCgoKCgoKCgoKCgoHhtb25fbG9n
X2J1Zl9sZW4gPSAqaTsKPiAroKCgoKCgoKCgoKCgoKCgoKCgoKCgoKBzeW5jKCk7Cj4gK6CgoKCg
oKCgoKCgoKCgoKCgoKCgoKCgX19kZWxheSgyMDApOwo+ICugoKCgoKCgoKCgoKCgoKB9Cj4gKwo+
ICugoKCgoKCgoKCgoKCgoKBsID0gKGNvbnN0IHVuc2lnbmVkIGxvbmcgKilrYWxsc3ltc19sb29r
dXBfbmFtZSgibG9nX2VuZCIpOwo+ICugoKCgoKCgoKCgoKCgoKBpZiAobCkgewo+ICugoKCgoKCg
oKCgoKCgoKCgoKCgoKCgoHhtb25fbG9nX2VuZCA9ICpsOwo+ICugoKCgoKCgoKCgoKCgoKCgoKCg
oKCgoHN5bmMoKTsKPiAroKCgoKCgoKCgoKCgoKCgoKCgoKCgoKBfX2RlbGF5KDIwMCk7Cj4gK6Cg
oKCgoKCgoKCgoKCgoH0KPiArCj4gK6CgoKCgoKCgoKCgoKCgoGwgPSAoY29uc3QgdW5zaWduZWQg
bG9uZyAqKWthbGxzeW1zX2xvb2t1cF9uYW1lKCJsb2dnZWRfY2hhcnMiKTsKPiAroKCgoKCgoKCg
oKCgoKCgaWYgKGwpIHsKPiAroKCgoKCgoKCgoKCgoKCgoKCgoKCgoKB4bW9uX2xvZ2dlZF9jaGFy
cyA9ICpsOwo+ICugoKCgoKCgoKCgoKCgoKCgoKCgoKCgoHN5bmMoKTsKPiAroKCgoKCgoKCgoKCg
oKCgoKCgoKCgoKBfX2RlbGF5KDIwMCk7Cj4gK6CgoKCgoKCgoKCgoKCgoH0KPiArCj4gK6CgoKCg
oKB9Cj4gK6CgoKCgoKBjYXRjaF9tZW1vcnlfZXJyb3JzID0gMDsKPiAroKCgoKCgoHJldHVybiAh
IXhtb25fbG9nX2J1ZjsKClRoaXMgd2hvbGUgZnVuY3Rpb24gbG9va3MgZmlzaHkgdG8gbWUuIEl0
IHNlZW1zIHVubmVjZXNzYXJpbHkgcnVkZSB0byBtZSB0bwp1c2Uga2FsbHN5bXNfbG9va3VwX25h
bWUgaW4gb3JkZXIgdG8gZ2V0IGF0IGEgc3RhdGljIHZhcmlhYmxlIGZyb20gYW5vdGhlcgpmaWxl
LiBDYW4ndCB5b3UgaW5zdGVhZCBhZGQgYSBnbG9iYWwgZnVuY3Rpb24gdG8ga2VybmVsL3ByaW50
ay5jIHRvIHJldHVybgp0aGUgYnVmZmVyPwoKQWxzbywgd2hhdCdzIHRoZSBwdXJwb3NlIG9mIHRo
ZSBzeW5jL19fZGVsYXkgY29kZSBpbiBoZXJlPyBJZiB5b3UgdXNlIHRoYXQKdG8gY2hlY2sgZm9y
IGEgcG9zc2libGUgbWFjaGluZSBjaGVjayB0aGF0IG1heSBoYXZlIGhhcHBlbmVkLCB3aHkgbm90
IGluCnRoZSBjYXNlIHdoZXJlIHRoZSBsb29rdXAgZmFpbHM/CgoJQXJuZCA8PjwK

^ permalink raw reply

* Re: [PATCH] powerpc: add dmesg command to xmon
From: Olaf Hering @ 2006-05-28 12:07 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linuxppc-dev
In-Reply-To: <200605281404.26070.arnd@arndb.de>

 On Sun, May 28, Arnd Bergmann wrote:

> This whole function looks fishy to me. It seems unnecessarily rude to me to
> use kallsyms_lookup_name in order to get at a static variable from another
> file. Can't you instead add a global function to kernel/printk.c to return
> the buffer?

Its possible, but it wont end up in Linus tree.

> Also, what's the purpose of the sync/__delay code in here? If you use that
> to check for a possible machine check that may have happened, why not in
> the case where the lookup fails?

I just copied the other usages. Accessing the dmesg buffer itself may
need similar checks.

^ permalink raw reply

* Re: Howto allocate non-cacheable memory
From: Arnd Bergmann @ 2006-05-28 15:36 UTC (permalink / raw)
  To: linuxppc-embedded; +Cc: Muneeswaran P - TLS, Chennai
In-Reply-To: <012301731C23DE4DBFE035647C68F1CC0331C84F@sindhu.ctd.hcltech.com>

On Saturday 27 May 2006 07:38, Muneeswaran P - TLS, Chennai wrote:
> =A0=A0=A0=A0=A0=A0=A0=A0Pls clarify the following doubts:
> =A0=A0=A0=A0=A0=A0=A0=A01. DPRAM is memory mapped to PCI-X card.
> =A0=A0=A0=A0=A0=A0=A0=A02. I have to transfer data from main memory to DP=
RAM using bus
> master concept and vice-versa.
> =A0=A0=A0=A0=A0=A0=A0=A03. How to make this memory area as non-cacheable =
one ?=20
> =A0=A0=A0=A0=A0=A0=A0=A04. Whether DMA mapping (pci_map_single() call ) s=
hould be done for
> both DPRAM and main memory ?
> =A0=A0=A0=A0=A0=A0=A0=A0 =A0 4.a) I will get the physical address of DPRA=
M memory (memory
> mapped to PCI-X card) from BAR-0 register. Can i use this physical addres
> directly for data transer using bus master (without pci_map_single() call=
=2E)
> ?
> =A0=A0=A0=A0=A0=A0=A0=A0 =A0 4.b) =A0How to make main memory area as non-=
cacheable one ?

You need pci_map_single() for all bus-master accesses from the PCI-X card
and ioremap() to map the card's registers and/or memory into your kernel
address space as cache-inhibited.

Depending on what your adapter does, you might want to map memory areas
on the card with __ioremap(addr, size, _PAGE_NO_CACHE) instead of
ioremap(addr, size) in order to get a non-guarded mapping.

	Arnd <><

^ permalink raw reply

* Re: [PATCH] powerpc: add dmesg command to xmon
From: Arnd Bergmann @ 2006-05-28 16:12 UTC (permalink / raw)
  To: Olaf Hering; +Cc: linuxppc-dev
In-Reply-To: <20060528120748.GA7608@suse.de>

On Sunday 28 May 2006 14:07, Olaf Hering wrote:
> > This whole function looks fishy to me. It seems unnecessarily rude to me to
> > use kallsyms_lookup_name in order to get at a static variable from another
> > file. Can't you instead add a global function to kernel/printk.c to return
> > the buffer?
> 
> Its possible, but it wont end up in Linus tree.

Hmm, so the idea is that because Linus doesn't like kernel debuggers, we just
obfuscate the code for any improvements to xmon and hope he doesn't notice?

Don't get me wrong, I think having dmesg in xmon is a good idea, but this is
probably not the best strategy of getting it in there.

	Arnd <><

^ permalink raw reply

* Re: Tutorial on PPC exception
From: Mark Chambers @ 2006-05-28 18:15 UTC (permalink / raw)
  To: jeanwelly, linuxppc-embedded
In-Reply-To: <200605281118066622530@126.com>



> Hi All,
> Does anybody know how to get the full exceptions of the PPC? Which 
> tutorial could be fetched as a reference?
>

Freescale has complete documentation for their processors available at 
www.freescale.com

Mark C. 

^ permalink raw reply

* [RFC 7/7] snd-aoa: wire up aoa in sound/
From: Johannes Berg @ 2006-05-28 19:00 UTC (permalink / raw)
  To: alsa-devel; +Cc: linuxppc-dev
In-Reply-To: <20060528190026.754474000@johannes.berg>

This patch adds the necessary Kconfig and Makefile hooks to make aoa
buildable inside the kernel tree.

--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -58,6 +58,8 @@ source "sound/pci/Kconfig"
 
 source "sound/ppc/Kconfig"
 
+source "sound/aoa/Kconfig"
+
 source "sound/arm/Kconfig"
 
 source "sound/mips/Kconfig"
--- a/sound/Makefile
+++ b/sound/Makefile
@@ -4,7 +4,7 @@ #
 obj-$(CONFIG_SOUND) += soundcore.o
 obj-$(CONFIG_SOUND_PRIME) += oss/
 obj-$(CONFIG_DMASOUND) += oss/
-obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/
+obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ aoa/
 
 ifeq ($(CONFIG_SND),y)
   obj-y += last.o

--

^ permalink raw reply

* [RFC 1/7] snd-aoa: add aoa header files
From: Johannes Berg @ 2006-05-28 19:00 UTC (permalink / raw)
  To: alsa-devel; +Cc: linuxppc-dev
In-Reply-To: <20060528190026.754474000@johannes.berg>

This patch adds header files to use for communication between the various
parts of aoa.

--- /dev/null
+++ b/sound/aoa/aoa-gpio.h
@@ -0,0 +1,44 @@
+/*
+ * Apple Onboard Audio GPIO definitions
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#ifndef __AOA_GPIO_H
+#define __AOA_GPIO_H
+#include <asm/prom.h>
+
+struct gpio_runtime;
+struct gpio_methods {
+	/* for initialisation/de-initialisation of the GPIO layer */
+	void (*init)(struct gpio_runtime *rt);
+	void (*exit)(struct gpio_runtime *rt);
+
+	/* turn off headphone, speakers, lineout */
+	void (*all_amps_off)(struct gpio_runtime *rt);
+	/* turn headphone, speakers, lineout back to previous setting */
+	void (*all_amps_restore)(struct gpio_runtime *rt);
+
+	void (*set_headphone)(struct gpio_runtime *rt, int on);
+	void (*set_speakers)(struct gpio_runtime *rt, int on);
+	void (*set_lineout)(struct gpio_runtime *rt, int on);
+
+	int (*get_headphone)(struct gpio_runtime *rt);
+	int (*get_speakers)(struct gpio_runtime *rt);
+	int (*get_lineout)(struct gpio_runtime *rt);
+
+	void (*set_hw_reset)(struct gpio_runtime *rt, int on);
+};
+
+struct gpio_runtime {
+	/* to be assigned by fabric */
+	struct device_node *node;
+	/* since everyone needs this pointer anyway... */
+	struct gpio_methods *methods;
+	/* to be used by the gpio implementation */
+	int implementation_private;
+};
+
+#endif /* __AOA_GPIO_H */
--- /dev/null
+++ b/sound/aoa/aoa.h
@@ -0,0 +1,130 @@
+/*
+ * Apple Onboard Audio definitions
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#ifndef __AOA_H
+#define __AOA_H
+#include <asm/prom.h>
+#include <linux/module.h>
+/* So apparently there's a reason for requiring driver.h to be included first! */
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/asound.h>
+#include <sound/control.h>
+#include "aoa-gpio.h"
+#include "soundbus/soundbus.h"
+
+#define MAX_CODEC_NAME_LEN	32
+
+struct aoa_codec {
+	char	name[MAX_CODEC_NAME_LEN];
+
+	struct module *owner;
+
+	/* called when the fabric wants to init this codec.
+	 * Do alsa card manipulations from here. */
+	int (*init)(struct aoa_codec *codec);
+
+	/* called when the fabric is done with the codec.
+	 * The alsa card will be cleaned up so don't bother. */
+	void (*exit)(struct aoa_codec *codec);
+
+	/* May be NULL, but can be used by the fabric.
+	 * Refcounting is the codec driver's responsibility */
+	struct device_node *node;
+
+	/* assigned by fabric before init() is called, points
+	 * to the soundbus device. Cannot be NULL. */
+	struct soundbus_dev *soundbus_dev;
+
+	/* assigned by the fabric before init() is called, points
+	 * to the fabric's gpio runtime record for the relevant
+	 * device. */
+	struct gpio_runtime *gpio;
+
+	/* assigned by the fabric before init() is called, contains
+	 * a codec specific bitmask of what outputs and inputs are
+	 * actually connected */
+	u32 connected;
+
+	/* data the fabric can associate with this structure */
+	void *fabric_data;
+
+	/* private! */
+	struct list_head list;
+	struct aoa_fabric *fabric;
+};
+
+/* return 0 on success */
+extern int
+aoa_codec_register(struct aoa_codec *codec);
+extern void
+aoa_codec_unregister(struct aoa_codec *codec);
+
+#define MAX_LAYOUT_NAME_LEN	32
+
+struct aoa_fabric {
+	char	name[MAX_LAYOUT_NAME_LEN];
+
+	struct module *owner;
+
+	/* once codecs register, they are passed here after.
+	 * They are of course not initialised, since the
+	 * fabric is responsible for initialising some fields
+	 * in the codec structure! */
+	int (*found_codec)(struct aoa_codec *codec);
+	/* called for each codec when it is removed,
+	 * also in the case that aoa_fabric_unregister
+	 * is called and all codecs are removed
+	 * from this fabric.
+	 * Also called if found_codec returned 0 but
+	 * the codec couldn't initialise. */
+	void (*remove_codec)(struct aoa_codec *codec);
+	/* If found_codec returned 0, and the codec
+	 * could be initialised, this is called. */
+	void (*attached_codec)(struct aoa_codec *codec);
+};
+
+/* return 0 on success, -EEXIST if another fabric is
+ * registered, -EALREADY if the same fabric is registered.
+ * Passing NULL can be used to test for the presence
+ * of another fabric, if -EALREADY is returned there is
+ * no other fabric present.
+ * In the case that the function returns -EALREADY
+ * and the fabric passed is not NULL, all codecs
+ * that are not assigned yet are passed to the fabric
+ * again for reconsideration. */
+extern int
+aoa_fabric_register(struct aoa_fabric *fabric);
+
+/* it is vital to call this when the fabric exits!
+ * When calling, the remove_codec will be called
+ * for all codecs, unless it is NULL. */
+extern void
+aoa_fabric_unregister(struct aoa_fabric *fabric);
+
+/* if for some reason you want to get rid of a codec
+ * before the fabric is removed, use this.
+ * Note that remove_codec is called for it! */
+extern void
+aoa_fabric_unlink_codec(struct aoa_codec *codec);
+
+/* alsa help methods */
+struct aoa_card {
+	struct snd_card *alsa_card;
+};
+        
+extern int aoa_snd_device_new(snd_device_type_t type,
+	void * device_data, struct snd_device_ops * ops);
+extern struct snd_card *aoa_get_card(void);
+extern int aoa_snd_ctl_add(struct snd_kcontrol* control);
+
+/* GPIO stuff */
+extern struct gpio_methods *pmf_gpio_methods;
+/* extern struct gpio_methods *map_gpio_methods; */
+
+#endif /* __AOA_H */

--

^ permalink raw reply

* [RFC 2/7] snd-aoa: add aoa core
From: Johannes Berg @ 2006-05-28 19:00 UTC (permalink / raw)
  To: alsa-devel; +Cc: linuxppc-dev
In-Reply-To: <20060528190026.754474000@johannes.berg>

This patch adds the core of aoa, in itself pretty useless, but providing
useful functions to other modules.

--- /dev/null
+++ b/sound/aoa/core/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_SND_AOA) += snd-aoa.o
+snd-aoa-objs := snd-aoa-core.o \
+		snd-aoa-alsa.o \
+		snd-aoa-gpio-pmf.o
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-alsa.c
@@ -0,0 +1,91 @@
+/*
+ * Apple Onboard Audio Alsa helpers
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#include <linux/module.h>
+#include "snd-aoa-alsa.h"
+
+static struct aoa_card *aoa_card;
+
+int aoa_alsa_init(char *name, struct module *mod)
+{
+	struct snd_card *alsa_card;
+	int err;
+
+	if (aoa_card)
+		/* cannot be EEXIST due to usage in aoa_fabric_register */
+		return -EBUSY;
+
+	alsa_card = snd_card_new(-1, name, mod, sizeof(struct aoa_card));
+	if (!alsa_card)
+		return -ENOMEM;
+	aoa_card = alsa_card->private_data;
+	aoa_card->alsa_card = alsa_card;
+	strncpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver));
+	strncpy(alsa_card->shortname, name, sizeof(alsa_card->shortname));
+	strncpy(alsa_card->longname, name, sizeof(alsa_card->longname));
+	strncpy(alsa_card->mixername, name, sizeof(alsa_card->mixername));
+	err = snd_card_register(aoa_card->alsa_card);
+	if (err < 0) {
+		printk(KERN_ERR "snd-aoa: couldn't register alsa card\n");
+		snd_card_free(aoa_card->alsa_card);
+		aoa_card = NULL;
+		return err;
+	}
+	return 0;
+}
+
+struct snd_card *aoa_get_card(void)
+{
+	if (aoa_card)
+		return aoa_card->alsa_card;
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(aoa_get_card);
+
+void aoa_alsa_cleanup(void)
+{
+	if (aoa_card) {
+		snd_card_free(aoa_card->alsa_card);
+		aoa_card = NULL;
+	}
+}
+
+int aoa_snd_device_new(snd_device_type_t type,
+        void * device_data, struct snd_device_ops * ops)
+{
+	struct snd_card *card = aoa_get_card();
+	int err;
+	
+	if (!card) return -ENOMEM;
+
+	err = snd_device_new(card, type, device_data, ops);
+	if (err) {
+		printk(KERN_ERR "snd-aoa: failed to create snd device (%d)\n", err);
+		return err;
+	}
+	err = snd_device_register(card, device_data);
+	if (err) {
+		printk(KERN_ERR "snd-aoa: failed to register snd device (%d)\n", err);
+		printk(KERN_ERR "snd-aoa: have you forgotten the dev_register callback?\n");
+		snd_device_free(card, device_data);
+	}
+	return err;
+}
+EXPORT_SYMBOL_GPL(aoa_snd_device_new);
+
+int aoa_snd_ctl_add(struct snd_kcontrol* control)
+{
+	int err;
+
+	if (!aoa_card) return -ENODEV;
+
+	err = snd_ctl_add(aoa_card->alsa_card, control);
+	if (err)
+		printk(KERN_ERR "snd-aoa: failed to add alsa control (%d)\n", err);
+	return err;
+}
+EXPORT_SYMBOL_GPL(aoa_snd_ctl_add);
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-alsa.h
@@ -0,0 +1,17 @@
+/*
+ * Apple Onboard Audio Alsa private helpers
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#ifndef __SND_AOA_ALSA_H
+#define __SND_AOA_ALSA_H
+/* FIXME */
+#include "../aoa.h"
+
+extern int aoa_alsa_init(char *name, struct module *mod);
+extern void aoa_alsa_cleanup(void);
+
+#endif /* __SND_AOA_ALSA_H */
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-core.c
@@ -0,0 +1,153 @@
+/*
+ * Apple Onboard Audio driver core
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include "../aoa.h"
+#include "snd-aoa-alsa.h"
+
+MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver");
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+
+/* We allow only one fabric. This simplifies things,
+ * and more don't really make that much sense */
+static struct aoa_fabric *fabric;
+static LIST_HEAD(codec_list);
+
+static void attach_codec_to_fabric(struct aoa_codec *c)
+{
+	int err;
+
+	if (!try_module_get(c->owner))
+		return;
+	/* found_codec has to be assigned */
+	err = -ENOENT;
+	if (fabric->found_codec)
+		err = fabric->found_codec(c);
+	if (err) {
+		module_put(c->owner);
+		printk("snd-aoa: fabric didn't like codec %s\n", c->name);
+		return;
+	}
+	c->fabric = fabric;
+
+	err = 0;
+	if (c->init)
+		err = c->init(c);
+	if (err) {
+		printk("snd-aoa: codec %s didn't init\n", c->name);
+		c->fabric = NULL;
+		if (fabric->remove_codec)
+			fabric->remove_codec(c);
+		module_put(c->owner);
+		return;
+	}
+	if (fabric->attached_codec)
+		fabric->attached_codec(c);
+}
+
+int aoa_codec_register(struct aoa_codec *codec)
+{
+	list_add(&codec->list, &codec_list);
+	if (fabric)
+		attach_codec_to_fabric(codec);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(aoa_codec_register);
+
+void aoa_codec_unregister(struct aoa_codec *codec)
+{
+	list_del(&codec->list);
+	if (codec->fabric && codec->exit)
+		codec->exit(codec);
+	if (fabric && fabric->remove_codec)
+		fabric->remove_codec(codec);
+	codec->fabric = NULL;
+	module_put(codec->owner);
+}
+EXPORT_SYMBOL_GPL(aoa_codec_unregister);
+
+int aoa_fabric_register(struct aoa_fabric *new_fabric)
+{
+	struct aoa_codec *c;
+	int err;
+
+	/* allow querying for presence of fabric
+	 * (i.e. do this test first!) */
+	if (new_fabric == fabric) {
+		err = -EALREADY;
+		goto attach;
+	}
+	if (fabric)
+		return -EEXIST;
+	if (!new_fabric)
+		return -EINVAL;
+
+	err = aoa_alsa_init(new_fabric->name, new_fabric->owner);
+	if (err) {
+		return err;
+	}
+	fabric = new_fabric;
+
+ attach:
+	list_for_each_entry(c, &codec_list, list) {
+		if (c->fabric != fabric)
+			attach_codec_to_fabric(c);
+	}
+	return err;
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_register);
+
+void aoa_fabric_unregister(struct aoa_fabric *old_fabric)
+{
+	struct aoa_codec *c;
+
+	if (fabric != old_fabric)
+		return;
+
+	list_for_each_entry(c, &codec_list, list) {
+		if (c->fabric)
+			aoa_fabric_unlink_codec(c);
+	}
+
+	aoa_alsa_cleanup();
+
+	fabric = NULL;
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_unregister);
+
+void aoa_fabric_unlink_codec(struct aoa_codec *codec)
+{
+	if (!codec->fabric) {
+		printk(KERN_ERR "snd-aoa: fabric unassigned in aoa_fabric_unlink_codec\n");
+		dump_stack();
+		return;
+	}
+	if (codec->exit)
+		codec->exit(codec);
+	if (codec->fabric->remove_codec)
+		codec->fabric->remove_codec(codec);
+	codec->fabric = NULL;
+	module_put(codec->owner);
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec);
+
+static int __init aoa_init(void)
+{
+	return 0;
+}
+
+static void __exit aoa_exit(void)
+{
+	aoa_alsa_cleanup();
+}
+
+module_init(aoa_init);
+module_exit(aoa_exit);
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-gpio-pmf.c
@@ -0,0 +1,91 @@
+/*
+ * Apple Onboard Audio pmf GPIOs
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <asm/pmac_feature.h>
+#include <asm/pmac_pfunc.h>
+#include "../aoa.h"
+
+#define PMF_GPIO(name, bit)					\
+static void pmf_gpio_set_##name(struct gpio_runtime *rt, int on)\
+{								\
+	struct pmf_args args = { .count = 1, .u[0].v = !on };	\
+								\
+	if (unlikely(!rt)) return;				\
+	pmf_call_function(rt->node, #name "-mute", &args);	\
+	rt->implementation_private &= ~(1<<bit);		\
+	rt->implementation_private |= (!!on << bit);		\
+}								\
+static int pmf_gpio_get_##name(struct gpio_runtime *rt)		\
+{								\
+	if (unlikely(!rt)) return 0;				\
+	return (rt->implementation_private>>bit)&1;		\
+}
+
+PMF_GPIO(headphone, 0);
+PMF_GPIO(amp, 1);
+PMF_GPIO(lineout, 2);
+
+static void pmf_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
+{
+	struct pmf_args args = { .count = 1, .u[0].v = !!on };
+
+	if (unlikely(!rt)) return;
+	pmf_call_function(rt->node, "hw-reset", &args);
+}
+
+static void pmf_gpio_all_amps_off(struct gpio_runtime *rt)
+{
+	int saved;
+
+	if (unlikely(!rt)) return;
+	saved = rt->implementation_private;
+	pmf_gpio_set_headphone(rt, 0);
+	pmf_gpio_set_amp(rt, 0);
+	pmf_gpio_set_lineout(rt, 0);
+	rt->implementation_private = saved;
+}
+
+static void pmf_gpio_all_amps_restore(struct gpio_runtime *rt)
+{
+	int s;
+
+	if (unlikely(!rt)) return;
+	s = rt->implementation_private;
+	pmf_gpio_set_headphone(rt, (s>>0)&1);
+	pmf_gpio_set_amp(rt, (s>>1)&1);
+	pmf_gpio_set_lineout(rt, (s>>2)&1);
+}
+
+static void pmf_gpio_init(struct gpio_runtime *rt)
+{
+	pmf_gpio_all_amps_off(rt);
+	rt->implementation_private = 0;
+}
+
+static void pmf_gpio_exit(struct gpio_runtime *rt)
+{
+	pmf_gpio_all_amps_off(rt);
+	rt->implementation_private = 0;
+}
+
+static struct gpio_methods methods = {
+	.init			= pmf_gpio_init,
+	.exit			= pmf_gpio_exit,
+	.all_amps_off		= pmf_gpio_all_amps_off,
+	.all_amps_restore	= pmf_gpio_all_amps_restore,
+	.set_headphone		= pmf_gpio_set_headphone,
+	.set_speakers		= pmf_gpio_set_amp,
+	.set_lineout		= pmf_gpio_set_lineout,
+	.set_hw_reset		= pmf_gpio_set_hw_reset,
+	.get_headphone		= pmf_gpio_get_headphone,
+	.get_speakers		= pmf_gpio_get_amp,
+	.get_lineout		= pmf_gpio_get_lineout,
+};
+
+struct gpio_methods *pmf_gpio_methods = &methods;
+EXPORT_SYMBOL_GPL(pmf_gpio_methods);

--

^ permalink raw reply

* [RFC 5/7] snd-aoa: add layout-id fabric
From: Johannes Berg @ 2006-05-28 19:00 UTC (permalink / raw)
  To: alsa-devel; +Cc: linuxppc-dev
In-Reply-To: <20060528190026.754474000@johannes.berg>

The 'fabric' is the thing that pulls all of snd-aoa together, this patch
adds the 'layout' or 'layout-id' fabric that keys off the 'layout-id'
property in the device-tree to pull in the proper modules. It itself loads
if an i2sbus is present with a layout-id (exported in modalias) that it can
use.

--- /dev/null
+++ b/sound/aoa/fabrics/Kconfig
@@ -0,0 +1,12 @@
+config SND_AOA_FABRIC_LAYOUT
+	tristate "layout-id fabric"
+	depends SND_AOA
+	select SND_AOA_SOUNDBUS
+	select SND_AOA_SOUNDBUS_I2S
+	---help---
+	This enables the layout-id fabric for the Apple Onboard
+	Audio driver, the module holding it all together
+	based on the device-tree's layout-id property.
+	
+	If you are unsure and have a later Apple machine,
+	compile it as a module.
--- /dev/null
+++ b/sound/aoa/fabrics/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SND_AOA_FABRIC_LAYOUT) += snd-aoa-fabric-layout.o
--- /dev/null
+++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c
@@ -0,0 +1,605 @@
+/*
+ * Apple Onboard Audio driver -- layout fabric
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ *
+ *
+ * This fabric module looks for sound codecs
+ * based on the layout-id property in the device tree.
+ *
+ */
+
+#include <asm/prom.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include "../aoa.h"
+#include "../soundbus/soundbus.h"
+
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Layout-ID fabric for snd-aoa");
+
+#define MAX_CODECS_PER_BUS	2
+
+/* These are the connections the layout fabric
+ * knows about. It doesn't really care about the
+ * input ones, but I thought I'd separate them
+ * to give them proper names. The thing is that
+ * Apple usually will distinguish the active output
+ * by GPIOs, while the active input is set directly
+ * on the codec. Hence we here tell the codec what
+ * we think is connected. This information is hard-
+ * coded below ... */
+#define CC_SPEAKERS	(1<<0)
+#define CC_HEADPHONE	(1<<1)
+#define CC_LINEOUT	(1<<2)
+#define CC_DIGITALOUT	(1<<3)
+#define CC_LINEIN	(1<<4)
+#define CC_MICROPHONE	(1<<5)
+#define CC_DIGITALIN	(1<<6)
+/* pretty bogus but users complain...
+ * This is a flag saying that the LINEOUT
+ * should be renamed to HEADPHONE.
+ * be careful with input detection! */
+#define CC_LINEOUT_LABELLED_HEADPHONE	(1<<7)
+
+struct codec_connection {
+	/* CC_ flags from above */
+	int connected;
+	/* codec dependent bit to be set in the aoa_codec.connected field.
+	 * This intentionally doesn't have any generic flags because the
+	 * fabric has to know the codec anyway and all codecs might have
+	 * different connectors */
+	int codec_bit;
+};
+
+struct codec_connect_info {
+	char *name;
+	struct codec_connection *connections;
+};
+
+#define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF	(1<<0)
+
+struct layout {
+	unsigned int layout_id;
+	struct codec_connect_info codecs[MAX_CODECS_PER_BUS];
+	int flags;
+	
+	/* if busname is not assigned, we use 'Master' below,
+	 * so that our layout table doesn't need to be filled
+	 * too much.
+	 * We only assign these two if we expect to find more
+	 * than one soundbus, i.e. on those machines with
+	 * multiple layout-ids */
+	char *busname;
+	int pcmid;
+};
+
+MODULE_ALIAS("sound-layout-82");
+MODULE_ALIAS("sound-layout-45");
+MODULE_ALIAS("sound-layout-64");
+MODULE_ALIAS("sound-layout-65");
+MODULE_ALIAS("sound-layout-68");
+MODULE_ALIAS("sound-layout-69");
+MODULE_ALIAS("sound-layout-70");
+MODULE_ALIAS("sound-layout-72");
+MODULE_ALIAS("sound-layout-80");
+MODULE_ALIAS("sound-layout-86");
+MODULE_ALIAS("sound-layout-84");
+MODULE_ALIAS("sound-layout-92");
+
+/* onyx with all but microphone connected */
+static struct codec_connection onyx_connections_nomic[] = {
+	{
+		.connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
+		.codec_bit = 0,
+	},
+	{
+		.connected = CC_DIGITALOUT,
+		.codec_bit = 1,
+	},
+	{
+		.connected = CC_LINEIN,
+		.codec_bit = 2,
+	},
+	{} /* terminate array by .connected == 0 */
+};
+
+/* onyx on machines without headphone */
+static struct codec_connection onyx_connections_noheadphones[] = {
+	{
+		.connected = CC_SPEAKERS | CC_LINEOUT |
+			     CC_LINEOUT_LABELLED_HEADPHONE,
+		.codec_bit = 0,
+	},
+	{
+		.connected = CC_DIGITALOUT,
+		.codec_bit = 1,
+	},
+	/* FIXME: are these correct? probably not for all the machines
+	 * below ... If not this will need separating. */
+	{
+		.connected = CC_LINEIN,
+		.codec_bit = 2,
+	},
+	{
+		.connected = CC_MICROPHONE,
+		.codec_bit = 3,
+	},
+	{} /* terminate array by .connected == 0 */
+};
+
+/* tas on machines without line out */
+static struct codec_connection tas_connections_nolineout[] = {
+	{
+		.connected = CC_SPEAKERS | CC_HEADPHONE,
+		.codec_bit = 0,
+	},
+	{
+		.connected = CC_LINEIN,
+		.codec_bit = 2,
+	},
+	{
+		.connected = CC_MICROPHONE,
+		.codec_bit = 3,
+	},
+	{} /* terminate array by .connected == 0 */
+};
+
+/* tas on machines with neither line out nor line in */
+static struct codec_connection tas_connections_noline[] = {
+	{
+		.connected = CC_SPEAKERS | CC_HEADPHONE,
+		.codec_bit = 0,
+	},
+	{
+		.connected = CC_MICROPHONE,
+		.codec_bit = 3,
+	},
+	{} /* terminate array by .connected == 0 */
+};
+
+static struct layout layouts[] = {
+	/* last PowerBooks (15" Oct 2005) */
+	{ .layout_id = 82,
+	  .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
+	  .codecs[0] = {
+		.name = "onyx",
+		.connections = onyx_connections_noheadphones,
+	  },
+	  .codecs[1] = {
+		.name = "topaz",
+		.connections = NULL /* TBD */,
+	  },
+	},
+	/* PowerBook5,7 */
+	{ .layout_id = 64,
+	  .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
+	  .codecs[0] = {
+		.name = "onyx",
+		.connections = onyx_connections_noheadphones,
+	  },
+	},
+	/* PowerBook5,7 */
+	{ .layout_id = 65,
+	  .codecs[0] = {
+		.name = "topaz",
+		.connections = NULL, /* TBD */
+	  },
+	},
+	/* PowerBook5,9 [17" Oct 2005] */
+	{ .layout_id = 84,
+	  .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
+	  .codecs[0] = {
+		.name = "onyx",
+		.connections = onyx_connections_noheadphones,
+	  },
+	  .codecs[1] = {
+		.name = "topaz",
+		.connections = NULL /* TBD */,
+	  },
+	},
+	/* PowerMac8,1 */
+	{ .layout_id = 45,
+	  .codecs[0] = {
+		.name = "onyx",
+		.connections = onyx_connections_noheadphones,
+	  },
+	  .codecs[1] = {
+		.name = "topaz",
+		.connections = NULL /* TBD */,
+	  },
+	},
+	/* Quad PowerMac (analog in, analog/digital out) */
+	{ .layout_id = 68,
+	  .codecs[0] = {
+		.name = "onyx",
+		.connections = onyx_connections_nomic,
+	  },
+	},
+	/* Quad PowerMac (digital in) */
+	{ .layout_id = 69,
+	  .codecs[0] = {
+		.name = "topaz",
+		.connections = NULL /* TBD */,
+	  },
+	  .busname = "digital in", .pcmid = 1 },
+	/* Early 2005 PowerBook */
+	{ .layout_id = 70,
+	  .codecs[0] = {
+		.name = "tas",
+		.connections = tas_connections_nolineout,
+	  },
+	},
+	/* PowerBook6,7 */
+	{ .layout_id = 80,
+	  .codecs[0] = {
+		.name = "tas",
+		.connections = tas_connections_noline,
+	  },
+	},
+	/* PowerBook6,8 */
+	{ .layout_id = 72,
+	  .codecs[0] = {
+		.name = "tas",
+		.connections = tas_connections_nolineout,
+	  },
+	},
+	/* PowerMac8,2 */
+	{ .layout_id = 86,
+	  .codecs[0] = {
+		.name = "onyx",
+		.connections = onyx_connections_nomic, /* ??? */
+	  },
+	  .codecs[1] = {
+		.name = "topaz",
+		.connections = NULL /* TBD */,
+	  },
+	},
+	/* PowerBook6,7 */
+	{ .layout_id = 92,
+	  .codecs[0] = {
+		.name = "tas",
+		.connections = tas_connections_nolineout,
+	  },
+	},
+	{}
+};
+
+static struct layout *find_layout_by_id(unsigned int id)
+{
+	struct layout *l;
+
+	l = layouts;
+	while (l->layout_id) {
+		if (l->layout_id == id)
+			return l;
+		l++;
+	}
+	return NULL;
+}
+
+static void use_layout(struct layout *l)
+{
+	int i;
+
+	for (i=0; i<MAX_CODECS_PER_BUS; i++) {
+		if (l->codecs[i].name) {
+			request_module("snd-aoa-codec-%s", l->codecs[i].name);
+		}
+	}
+	/* now we wait for the codecs to call us back */
+}
+
+struct layout_dev {
+	struct list_head list;
+	struct soundbus_dev *sdev;
+	struct device_node *sound;
+	struct aoa_codec *codecs[MAX_CODECS_PER_BUS];
+	struct layout *layout;
+	struct gpio_runtime gpio;
+};
+
+static LIST_HEAD(layouts_list);
+static int layouts_list_items;
+
+static int control_info(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+#define AMP_CONTROL(n, description)					\
+static int n##_control_get(struct snd_kcontrol *kcontrol,		\
+			   struct snd_ctl_elem_value *ucontrol)		\
+{									\
+	struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol);	\
+	if (gpio->methods && gpio->methods->get_##n)			\
+		ucontrol->value.integer.value[0] =			\
+			gpio->methods->get_##n(gpio);			\
+	return 0;							\
+}									\
+static int n##_control_put(struct snd_kcontrol *kcontrol,		\
+			   struct snd_ctl_elem_value *ucontrol)		\
+{									\
+	struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol);	\
+	if (gpio->methods && gpio->methods->get_##n)			\
+		gpio->methods->set_##n(gpio,				\
+			ucontrol->value.integer.value[0]);		\
+	return 1;							\
+}									\
+static struct snd_kcontrol_new n##_ctl = {				\
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,				\
+	.name = description,						\
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,                      \
+	.info = control_info,						\
+	.get = n##_control_get,						\
+	.put = n##_control_put,						\
+}
+
+AMP_CONTROL(headphone, "Headphone Switch");
+AMP_CONTROL(speakers, "Speakers Switch");
+AMP_CONTROL(lineout, "Line-out Switch");
+
+static int check_codec(struct aoa_codec *codec, struct layout_dev *ldev, struct codec_connect_info *cci)
+{
+	u32 *ref;
+	char propname[32];
+	struct codec_connection *cc;
+
+	if (codec->node) {
+		snprintf(propname, sizeof(propname), "platform-%s-codec-ref", codec->name);
+		ref = (u32*)get_property(ldev->sound, propname, NULL);
+		/* if the codec has a node, we require a reference */
+		if (!ref) {
+			printk(KERN_INFO "snd-aoa-fabric-layout: required property %s not present\n", propname);
+			return -ENODEV;
+		}
+		if (*ref != codec->node->linux_phandle) {
+			printk(KERN_INFO "snd-aoa-fabric-layout: %s doesn't match!\n", propname);
+			return -ENODEV;
+		}
+	} else {
+		if (layouts_list_items != 1) {
+			printk(KERN_INFO "snd-aoa-fabric-layout: more than one soundbus, but no references. eek!\n");
+			return -ENODEV;
+		}
+	}
+	codec->soundbus_dev = ldev->sdev;
+	codec->gpio = &ldev->gpio;
+
+	cc = cci->connections;
+	if (!cc)
+		return -EINVAL;
+
+	printk(KERN_INFO "snd-aoa-fabric-layout: can use this codec\n");
+
+	codec->connected = 0;
+	codec->fabric_data = cc;
+
+	while (cc->connected) {
+		codec->connected |= 1<<cc->codec_bit;
+		cc++;
+	}
+
+	return 0;
+}
+
+static int layout_found_codec(struct aoa_codec *codec)
+{
+	struct layout_dev *ldev;
+	int i;
+
+	list_for_each_entry(ldev, &layouts_list, list) {
+		for (i=0; i<MAX_CODECS_PER_BUS; i++) {
+			if (!ldev->layout->codecs[i].name)
+				continue;
+			if (strcmp(ldev->layout->codecs[i].name, codec->name) == 0) {
+				if (check_codec(codec, ldev, &ldev->layout->codecs[i]) == 0)
+					return 0;
+			}
+		}
+	}
+	return -ENODEV;
+}
+
+static void layout_remove_codec(struct aoa_codec *codec)
+{
+	int i;
+	/* here remove the codec from the layout dev's
+	 * codec reference */
+
+	codec->soundbus_dev = NULL;
+	codec->gpio = NULL;
+	for (i=0; i<MAX_CODECS_PER_BUS; i++) {
+	}
+}
+
+static void layout_attached_codec(struct aoa_codec *codec)
+{
+	struct codec_connection *cc;
+	struct snd_kcontrol *ctl;
+
+	/* need to add this codec to our codec array! */
+
+	cc = codec->fabric_data;
+
+	while (cc->connected) {
+		if (cc->connected & CC_SPEAKERS)
+			aoa_snd_ctl_add(snd_ctl_new1(&speakers_ctl, codec->gpio));
+		if (cc->connected & CC_HEADPHONE)
+			aoa_snd_ctl_add(snd_ctl_new1(&headphone_ctl, codec->gpio));
+		if (cc->connected & CC_LINEOUT) {
+			ctl = snd_ctl_new1(&lineout_ctl, codec->gpio);
+			if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
+				strlcpy(ctl->id.name,
+					"Headphone Switch", sizeof(ctl->id.name));
+			aoa_snd_ctl_add(ctl);
+		}
+		cc++;
+	}
+}
+static struct aoa_fabric layout_fabric = {
+	.name = "SoundByLayout",
+	.owner = THIS_MODULE,
+	.found_codec = layout_found_codec,
+	.remove_codec = layout_remove_codec,
+	.attached_codec = layout_attached_codec,
+};
+
+static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
+{
+	struct device_node *sound = NULL;
+	unsigned int *layout_id;
+	struct layout *layout;
+	struct layout_dev *ldev = NULL;
+	int err;
+
+	/* by breaking out we keep a reference */
+	while ((sound = of_get_next_child(sdev->ofdev.node, sound))) {
+		if (sound->type && strcasecmp(sound->type, "soundchip") == 0)
+			break;
+	}
+	if (!sound) return -ENODEV;
+
+	layout_id = (unsigned int *) get_property(sound, "layout-id", NULL);
+	if (!layout_id)
+		goto outnodev;
+	printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d ", *layout_id);
+
+	layout = find_layout_by_id(*layout_id);
+	if (!layout) {
+		printk("(no idea how to handle)\n");
+		goto outnodev;
+	}
+
+	ldev = kzalloc(sizeof(struct layout_dev), GFP_KERNEL);
+	if (!ldev)
+		goto outnodev;
+
+	ldev->sdev = sdev;
+	ldev->sound = sound;
+	ldev->layout = layout;
+	ldev->gpio.node = sound->parent;
+	ldev->gpio.methods = pmf_gpio_methods;
+	sdev->ofdev.dev.driver_data = ldev;
+
+	printk("(using)\n");
+	list_add(&ldev->list, &layouts_list);
+	layouts_list_items++;
+
+	/* assign these before registering ourselves, so
+	 * callbacks that are done during registration
+	 * already have the values */
+	sdev->pcmid = ldev->layout->pcmid;
+	if (ldev->layout->busname) {
+		sdev->pcmname = ldev->layout->busname;
+	} else {
+		sdev->pcmname = "Master";
+	}
+
+	err = aoa_fabric_register(&layout_fabric);
+	if (err && err != -EALREADY) {
+		printk(KERN_INFO "snd-aoa-fabric-layout: can't use,"
+				 " another fabric is active!\n");
+		goto outlistdel;
+	}
+
+	ldev->gpio.methods->init(&ldev->gpio);
+	use_layout(layout);
+	return 0;
+ outlistdel:
+	/* reset if we didn't use it */
+	sdev->pcmname = NULL;
+	sdev->pcmid = -1;
+	list_del(&ldev->list);
+	layouts_list_items--;
+ outnodev:
+ 	if (sound) of_node_put(sound);
+ 	if (ldev) kfree(ldev);
+	return -ENODEV;
+}
+
+static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
+{
+	struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+	int i;
+
+	for (i=0; i<MAX_CODECS_PER_BUS; i++) {
+		if (ldev->codecs[i]) {
+			aoa_fabric_unlink_codec(ldev->codecs[i]);
+		}
+		ldev->codecs[i] = NULL;
+	}
+	list_del(&ldev->list);
+	layouts_list_items--;
+	of_node_put(ldev->sound);
+
+	ldev->gpio.methods->exit(&ldev->gpio);
+	kfree(ldev);
+	sdev->pcmid = -1;
+	sdev->pcmname = NULL;
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state)
+{
+	struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+
+	printk("aoa_fabric_layout_suspend()\n");
+
+	if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
+		ldev->gpio.methods->all_amps_off(&ldev->gpio);
+
+	return 0;
+}
+
+static int aoa_fabric_layout_resume(struct soundbus_dev *sdev)
+{
+	struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+
+	printk("aoa_fabric_layout_resume()\n");
+
+	if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
+		ldev->gpio.methods->all_amps_restore(&ldev->gpio);
+
+	return 0;
+}
+#endif
+
+static struct soundbus_driver aoa_soundbus_driver = {
+	.name = "snd_aoa_soundbus_drv",
+	.owner = THIS_MODULE,
+	.probe = aoa_fabric_layout_probe,
+	.remove = aoa_fabric_layout_remove,
+#ifdef CONFIG_PM
+	.suspend = aoa_fabric_layout_suspend,
+	.resume = aoa_fabric_layout_resume,
+#endif
+};
+
+static int __init aoa_fabric_layout_init(void)
+{
+	int err;
+
+	err = soundbus_register_driver(&aoa_soundbus_driver);
+	if (err)
+		return err;
+	return 0;
+}
+
+static void __exit aoa_fabric_layout_exit(void)
+{
+	soundbus_unregister_driver(&aoa_soundbus_driver);
+	aoa_fabric_unregister(&layout_fabric);
+}
+
+module_init(aoa_fabric_layout_init);
+module_exit(aoa_fabric_layout_exit);

--

^ permalink raw reply

* [RFC 3/7] snd-aoa: add soundbus and i2sbus
From: Johannes Berg @ 2006-05-28 19:00 UTC (permalink / raw)
  To: alsa-devel; +Cc: linuxppc-dev
In-Reply-To: <20060528190026.754474000@johannes.berg>

This patch adds the mostly generic soundbus that will be used for older
things (davbus comes to mind) as well as adding i2sbus, the module handling
the i2s bus on Apple mac-io chips.

--- /dev/null
+++ b/sound/aoa/soundbus/Kconfig
@@ -0,0 +1,14 @@
+config SND_AOA_SOUNDBUS
+	tristate "Apple Soundbus support"
+	depends on SOUND && SND_PCM && EXPERIMENTAL
+	---help---
+	This option enables the generic driver for the soundbus
+	support on Apple machines.
+	
+	It is required for the sound bus implementations.
+
+config SND_AOA_SOUNDBUS_I2S
+	tristate "I2S bus support"
+	depends on SND_AOA_SOUNDBUS && PCI
+	---help---
+	This option enables support for Apple I2S busses.
--- /dev/null
+++ b/sound/aoa/soundbus/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_SND_AOA_SOUNDBUS) += snd-aoa-soundbus.o
+snd-aoa-soundbus-objs := core.o sysfs.o
+obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += i2sbus/
--- /dev/null
+++ b/sound/aoa/soundbus/core.c
@@ -0,0 +1,250 @@
+/*
+ * soundbus
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <linux/module.h>
+#include "soundbus.h"
+
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Apple Soundbus");
+
+struct soundbus_dev *soundbus_dev_get(struct soundbus_dev *dev)
+{
+	struct device *tmp;
+
+	if (!dev)
+		return NULL;
+	tmp = get_device(&dev->ofdev.dev);
+	if (tmp)
+		return to_soundbus_device(tmp);
+	else
+		return NULL;
+}
+EXPORT_SYMBOL_GPL(soundbus_dev_get);
+
+void soundbus_dev_put(struct soundbus_dev *dev)
+{
+	if (dev)
+		put_device(&dev->ofdev.dev);
+}
+EXPORT_SYMBOL_GPL(soundbus_dev_put);
+
+static int soundbus_probe(struct device *dev)
+{
+	int error = -ENODEV;
+	struct soundbus_driver *drv;
+	struct soundbus_dev *soundbus_dev;
+
+	drv = to_soundbus_driver(dev->driver);
+	soundbus_dev = to_soundbus_device(dev);
+
+	if (!drv->probe)
+		return error;
+
+	soundbus_dev_get(soundbus_dev);
+
+	error = drv->probe(soundbus_dev);
+	if (error)
+		soundbus_dev_put(soundbus_dev);
+
+	return error;
+}
+
+
+static int soundbus_uevent(struct device *dev, char **envp, int num_envp,
+			   char *buffer, int buffer_size)
+{
+	struct soundbus_dev * soundbus_dev;
+	struct of_device * of;
+	char *scratch, *compat, *compat2;
+	int i = 0;
+	int length, cplen, cplen2, seen = 0;
+
+	if (!dev)
+		return -ENODEV;
+
+	soundbus_dev = to_soundbus_device(dev);
+	if (!soundbus_dev)
+		return -ENODEV;
+
+	of = &soundbus_dev->ofdev;
+
+	/* stuff we want to pass to /sbin/hotplug */
+	envp[i++] = scratch = buffer;
+	length = scnprintf (scratch, buffer_size, "OF_NAME=%s", of->node->name);
+	++length;
+	buffer_size -= length;
+	if ((buffer_size <= 0) || (i >= num_envp))
+		return -ENOMEM;
+	scratch += length;
+
+	envp[i++] = scratch;
+	length = scnprintf (scratch, buffer_size, "OF_TYPE=%s", of->node->type);
+	++length;
+	buffer_size -= length;
+	if ((buffer_size <= 0) || (i >= num_envp))
+		return -ENOMEM;
+	scratch += length;
+
+	/* Since the compatible field can contain pretty much anything
+	 * it's not really legal to split it out with commas. We split it
+	 * up using a number of environment variables instead. */
+
+	compat = (char *) get_property(of->node, "compatible", &cplen);
+	compat2 = compat;
+	cplen2= cplen;
+	while (compat && cplen > 0) {
+		envp[i++] = scratch;
+		length = scnprintf (scratch, buffer_size,
+				     "OF_COMPATIBLE_%d=%s", seen, compat);
+		++length;
+		buffer_size -= length;
+		if ((buffer_size <= 0) || (i >= num_envp))
+			return -ENOMEM;
+		scratch += length;
+		length = strlen (compat) + 1;
+		compat += length;
+		cplen -= length;
+		seen++;
+	}
+
+	envp[i++] = scratch;
+	length = scnprintf (scratch, buffer_size, "OF_COMPATIBLE_N=%d", seen);
+	++length;
+	buffer_size -= length;
+	if ((buffer_size <= 0) || (i >= num_envp))
+		return -ENOMEM;
+	scratch += length;
+
+	envp[i++] = scratch;
+	length = scnprintf (scratch, buffer_size, "MODALIAS=%s",
+			soundbus_dev->modalias);
+
+	buffer_size -= length;
+	if ((buffer_size <= 0) || (i >= num_envp))
+		return -ENOMEM;
+
+	envp[i] = NULL;
+
+	return 0;
+}
+
+static int soundbus_device_remove(struct device *dev)
+{
+	struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
+	struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
+
+	if (dev->driver && drv->remove)
+		drv->remove(soundbus_dev);
+	soundbus_dev_put(soundbus_dev);
+
+	return 0;
+}
+
+static void soundbus_device_shutdown(struct device *dev)
+{
+	struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
+	struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
+
+	if (dev->driver && drv->shutdown)
+		drv->shutdown(soundbus_dev);
+}
+
+#ifdef CONFIG_PM
+
+static int soundbus_device_suspend(struct device *dev, pm_message_t state)
+{
+	struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
+	struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
+
+	if (dev->driver && drv->suspend)
+		return drv->suspend(soundbus_dev, state);
+	return 0;
+}
+
+static int soundbus_device_resume(struct device * dev)
+{
+	struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
+	struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
+
+	if (dev->driver && drv->resume)
+		return drv->resume(soundbus_dev);
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
+extern struct device_attribute soundbus_dev_attrs[];
+
+static struct bus_type soundbus_bus_type = {
+	.name		= "aoa-soundbus",
+	.probe		= soundbus_probe,
+	.uevent		= soundbus_uevent,
+	.remove		= soundbus_device_remove,
+	.shutdown	= soundbus_device_shutdown,
+#ifdef CONFIG_PM
+	.suspend	= soundbus_device_suspend,
+	.resume		= soundbus_device_resume,
+#endif
+	.dev_attrs	= soundbus_dev_attrs,
+};
+
+static int __init soundbus_init(void)
+{
+	return bus_register(&soundbus_bus_type);
+}
+
+static void __exit soundbus_exit(void)
+{
+	bus_unregister(&soundbus_bus_type);
+}
+
+int soundbus_add_one(struct soundbus_dev *dev)
+{
+	static int devcount;
+
+	/* sanity checks */
+	if (!dev->attach_codec ||
+	    !dev->ofdev.node ||
+	    dev->pcmname ||
+	    dev->pcmid != -1) {
+		printk(KERN_ERR "soundbus: adding device failed sanity check!\n");
+		return -EINVAL;
+	}
+
+	snprintf(dev->ofdev.dev.bus_id, BUS_ID_SIZE, "soundbus:%x", ++devcount);
+	dev->ofdev.dev.bus = &soundbus_bus_type;
+	return of_device_register(&dev->ofdev);
+}
+EXPORT_SYMBOL_GPL(soundbus_add_one);
+
+void soundbus_remove_one(struct soundbus_dev *dev)
+{
+	of_device_unregister(&dev->ofdev);
+}
+EXPORT_SYMBOL_GPL(soundbus_remove_one);
+
+int soundbus_register_driver(struct soundbus_driver *drv)
+{
+	/* initialize common driver fields */
+	drv->driver.name = drv->name;
+	drv->driver.bus = &soundbus_bus_type;
+
+	/* register with core */
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(soundbus_register_driver);
+
+void soundbus_unregister_driver(struct soundbus_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(soundbus_unregister_driver);
+
+module_init(soundbus_init);
+module_exit(soundbus_exit);
--- /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,213 @@
+/*
+ * 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;
+	/* HACK HACK HACK
+	 * And one that doesn't even work to boot.
+	 * We should be using the feature calls for this, but
+	 * since no one else uses the registers we currently hit
+	 * this is fine for now. */
+	(*c)->rsrc.end = (*c)->rsrc.start + sizeof(struct i2s_control_regs);
+	(*c)->allocated_rsrc =
+		request_mem_region((*c)->rsrc.start,
+				   (*c)->rsrc.end - (*c)->rsrc.start + 1,
+				   "i2s control");
+	if (!(*c)->allocated_rsrc) {
+		printk(KERN_WARNING "i2sbus: !!!!! WARNING !!!!!\n");
+		printk(KERN_WARNING "i2sbus: control: couldn't allocate resource\n");
+		printk(KERN_WARNING "i2sbus: going ahead anyway\n");
+		/* we should not ignore this error, but
+		 * some other device (via-pmu?) claims our memory region...
+		 * Until we somehow handle that we ignore the error */
+		/*goto err;*/
+	}
+	(*c)->controlregs = ioremap((*c)->rsrc.start,
+				    (*c)->rsrc.end - (*c)->rsrc.start + 1);
+	if (!(*c)->controlregs)
+		goto release;
+
+	return 0;
+ release:
+ 	if ((*c)->allocated_rsrc)
+ 		release_resource((*c)->allocated_rsrc);
+ err:
+	kfree(*c);
+	*c = NULL;
+	return -ENODEV;
+}
+
+void i2sbus_control_destroy(struct i2sbus_control *c)
+{
+	iounmap(c->controlregs);
+	if (c->allocated_rsrc)
+		release_resource(c->allocated_rsrc);
+	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;
+}
--- /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-core.c
@@ -0,0 +1,370 @@
+/*
+ * 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 = pci_alloc_consistent(macio_get_pci_dev(i2sdev->macio),
+					r->size,
+					&r->bus_addr);
+
+	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;
+	
+	pci_free_consistent(macio_get_pci_dev(i2sdev->macio),
+			    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);
+
+	printk(KERN_INFO "i2sbus: interrupt, intr reg is 0x%x!\n", intreg);
+
+	/* acknowledge interrupt reasons */
+	out_le32(&dev->intfregs->intr_ctl, intreg);
+
+	spin_unlock(&dev->low_lock);
+
+	return IRQ_HANDLED;
+}
+
+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);
+		}
+	}
+
+	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-pcm.c
@@ -0,0 +1,1008 @@
+/*
+ * 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 == 0) {
+		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 == 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 == 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;
+	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;
+
+	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;
+				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 transfers, 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 + 1) % 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 is to correct the fact that the frame_count is too low at the beginning
+			 * due to the dbdma's buffer. */
+			delta = 0;
+		}
+	}
+
+	pi->frame_count = fc - delta;
+	pi->current_period = 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");
+	}
+
+	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 */
+	/* FIXME FIXME FIXME: If this fails, we BUG() when the alsa layer
+	 * later tries to allocate memory. Apparently we should be setting
+	 * some device pointer for that ...
+	 */
+	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.h
@@ -0,0 +1,115 @@
+/*
+ * 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 "i2sbus-interface.h"
+#include "i2sbus-control.h"
+/* FIXME */
+#include "../soundbus.h"
+/* should go away, see comment in i2sbus_add_dev */
+#include <asm/prom.h>
+
+struct i2sbus_control {
+	volatile struct i2s_control_regs __iomem *controlregs;
+	struct resource rsrc;
+	struct resource *allocated_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/soundbus.h
@@ -0,0 +1,202 @@
+/*
+ * soundbus generic definitions
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#ifndef __SOUNDBUS_H
+#define __SOUNDBUS_H
+
+#include <asm/of_device.h>
+#include <sound/pcm.h>
+#include <linux/list.h>
+
+
+/* When switching from master to slave or the other way around,
+ * you don't want to have the codec chip acting as clock source
+ * while the bus still is.
+ * More importantly, while switch from slave to master, you need
+ * to turn off the chip's master function first, but then there's
+ * no clock for a while and other chips might reset, so we notify
+ * their drivers after having switched.
+ * The constants here are codec-point of view, so when we switch
+ * the soundbus to master we tell the codec we're going to switch
+ * and give it CLOCK_SWITCH_PREPARE_SLAVE!
+ */
+enum clock_switch {
+	CLOCK_SWITCH_PREPARE_SLAVE,
+	CLOCK_SWITCH_PREPARE_MASTER,
+	CLOCK_SWITCH_SLAVE,
+	CLOCK_SWITCH_MASTER,
+	CLOCK_SWITCH_NOTIFY,
+};
+
+/* information on a transfer the codec can take */
+struct transfer_info {
+	u64 formats;		/* SNDRV_PCM_FMTBIT_* */
+	unsigned int rates;	/* SNDRV_PCM_RATE_* */
+	/* flags */
+	u32 transfer_in:1, /* input = 1, output = 0 */
+	    must_be_clock_source:1;
+	/* for codecs to distinguish among their TIs */
+	int tag;
+};
+
+struct codec_info_item {
+	struct codec_info *codec;
+	void *codec_data;
+	struct soundbus_dev *sdev;
+	/* internal, to be used by the soundbus provider */
+	struct list_head list;
+};
+
+/* for prepare, where the codecs need to know
+ * what we're going to drive the bus with */
+struct bus_info {
+	/* see below */
+	int sysclock_factor;
+	int bus_factor;
+};
+
+/* information on the codec itself, plus function pointers */
+struct codec_info {
+	/* the module this lives in */
+	struct module *owner;
+
+	/* supported transfer possibilities, array terminated by
+	 * formats or rates being 0. */
+	struct transfer_info *transfers;
+
+	/* Master clock speed factor
+	 * to be used (master clock speed = sysclock_factor * sampling freq)
+	 * Unused if the soundbus provider has no such notion.
+	 */
+	int sysclock_factor;
+
+	/* Bus factor, bus clock speed = bus_factor * sampling freq)
+	 * Unused if the soundbus provider has no such notion.
+	 */
+	int bus_factor;
+
+	/* operations */
+	/* clock switching, see above */
+	int (*switch_clock)(struct codec_info_item *cii,
+			    enum clock_switch clock);
+
+	/* called for each transfer_info when the user
+	 * opens the pcm device to determine what the
+	 * hardware can support at this point in time.
+	 * That can depend on other user-switchable controls.
+	 * Return 1 if usable, 0 if not.
+	 * out points to another instance of a transfer_info
+	 * which is initialised to the values in *ti, and
+	 * it's format and rate values can be modified by
+	 * the callback if it is necessary to further restrict
+	 * the formats that can be used at the moment, for
+	 * example when one codec has multiple logical codec
+	 * info structs for multiple inputs.
+	 */
+	int (*usable)(struct codec_info_item *cii,
+		      struct transfer_info *ti,
+		      struct transfer_info *out);
+
+	/* called when pcm stream is opened, probably not implemented
+	 * most of the time since it isn't too useful */
+	int (*open)(struct codec_info_item *cii,
+		    struct snd_pcm_substream *substream);
+
+	/* called when the pcm stream is closed, at this point
+	 * the user choices can all be unlocked (see below) */
+	int (*close)(struct codec_info_item *cii,
+		     struct snd_pcm_substream *substream);
+
+	/* if the codec must forbid some user choices because
+	 * they are not valid with the substream/transfer info,
+	 * it must do so here. Example: no digital output for
+	 * incompatible framerate, say 8KHz, on Onyx.
+	 * If the selected stuff in the substream is NOT
+	 * compatible, you have to reject this call! */
+	int (*prepare)(struct codec_info_item *cii,
+		       struct bus_info *bi,
+		       struct snd_pcm_substream *substream);
+
+	/* start() is called before data is pushed to the codec.
+	 * Note that start() must be atomic! */
+	int (*start)(struct codec_info_item *cii,
+		     struct snd_pcm_substream *substream);
+
+	/* stop() is called after data is no longer pushed to the codec.
+	 * Note that stop() must be atomic! */
+	int (*stop)(struct codec_info_item *cii,
+		    struct snd_pcm_substream *substream);
+
+	int (*suspend)(struct codec_info_item *cii, pm_message_t state);
+	int (*resume)(struct codec_info_item *cii);
+};
+
+/* information on a soundbus device */
+struct soundbus_dev {
+	/* the bus it belongs to */
+	struct list_head onbuslist;
+
+	/* the of device it represents */
+	struct of_device ofdev;
+
+	/* what modules go by */
+	char modalias[32];
+
+	/* These fields must be before attach_codec can be called.
+	 * They should be set by the owner of the alsa card object
+	 * that is needed, and whoever sets them must make sure
+	 * that they are unique within that alsa card object. */
+	char *pcmname;
+	int pcmid;
+
+	/* this is assigned by the soundbus provider in attach_codec */
+	struct snd_pcm *pcm;
+
+	/* operations */
+	/* attach a codec to this soundbus, give the alsa
+	 * card object the PCMs for this soundbus should be in.
+	 * The 'data' pointer must be unique, it is used as the
+	 * key for detach_codec(). */
+	int (*attach_codec)(struct soundbus_dev *dev, struct snd_card *card,
+			    struct codec_info *ci, void *data);
+	void (*detach_codec)(struct soundbus_dev *dev, void *data);
+	/* TODO: suspend/resume */
+
+	/* private for the soundbus provider */
+	struct list_head codec_list;
+	u32 have_out:1, have_in:1;
+};
+#define to_soundbus_device(d) container_of(d, struct soundbus_dev, ofdev.dev)
+#define of_to_soundbus_device(d) container_of(d, struct soundbus_dev, ofdev)
+
+extern int soundbus_add_one(struct soundbus_dev *dev);
+extern void soundbus_remove_one(struct soundbus_dev *dev);
+
+extern struct soundbus_dev *soundbus_dev_get(struct soundbus_dev *dev);
+extern void soundbus_dev_put(struct soundbus_dev *dev);
+
+struct soundbus_driver {
+	char *name;
+	struct module *owner;
+
+	/* we don't implement any matching at all */
+
+	int	(*probe)(struct soundbus_dev* dev);
+	int	(*remove)(struct soundbus_dev* dev);
+
+	int	(*suspend)(struct soundbus_dev* dev, pm_message_t state);
+	int	(*resume)(struct soundbus_dev* dev);
+	int	(*shutdown)(struct soundbus_dev* dev);
+
+	struct device_driver driver;
+};
+#define to_soundbus_driver(drv) container_of(drv,struct soundbus_driver, driver)
+
+extern int soundbus_register_driver(struct soundbus_driver *drv);
+extern void soundbus_unregister_driver(struct soundbus_driver *drv);
+
+#endif /* __SOUNDBUS_H */
--- /dev/null
+++ b/sound/aoa/soundbus/sysfs.c
@@ -0,0 +1,67 @@
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+/* FIX UP */
+#include "soundbus.h"
+
+#define soundbus_config_of_attr(field, format_string)			\
+static ssize_t								\
+field##_show (struct device *dev, struct device_attribute *attr,	\
+              char *buf)						\
+{									\
+	struct soundbus_dev *mdev = to_soundbus_device (dev);		\
+	return sprintf (buf, format_string, mdev->ofdev.node->field);	\
+}
+
+static ssize_t
+compatible_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct of_device *of;
+	char *compat;
+	int cplen;
+	int length = 0;
+
+	of = &to_soundbus_device (dev)->ofdev;
+	compat = (char *) get_property(of->node, "compatible", &cplen);
+	if (!compat) {
+		*buf = '\0';
+		return 0;
+	}
+	while (cplen > 0) {
+		int l;
+		length += sprintf (buf, "%s\n", compat);
+		buf += length;
+		l = strlen (compat) + 1;
+		compat += l;
+		cplen -= l;
+	}
+
+	return length;
+}
+
+static ssize_t modalias_show (struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct soundbus_dev *sdev = to_soundbus_device(dev);
+	struct of_device *of = &sdev->ofdev;
+	int length;
+
+	if (strlen(sdev->modalias)) {
+		length = snprintf (buf, 34, "%s\n", sdev->modalias);
+	} else {
+		length = sprintf (buf, "of:N%sT%s\n", of->node->name, of->node->type);
+	}
+
+	return length;
+}
+
+soundbus_config_of_attr (name, "%s\n");
+soundbus_config_of_attr (type, "%s\n");
+
+struct device_attribute soundbus_dev_attrs[] = {
+	__ATTR_RO(name),
+	__ATTR_RO(type),
+	__ATTR_RO(compatible),
+	__ATTR_RO(modalias),
+	__ATTR_NULL
+};

--

^ permalink raw reply

* [RFC 0/7] snd-aoa: add snd-aoa
From: Johannes Berg @ 2006-05-28 19:00 UTC (permalink / raw)
  To: alsa-devel; +Cc: linuxppc-dev

The following patches would add snd-aoa to the Linux source tree. I'm
posting them here in the spirit of getting review fairly early, it works
fine for those machines it handles but has some rough edges like not
providing headphone detection at the moment.

There are plans to fix this, but they might be influenced by your comments
so I'm holding off there a bit. One thing that will probably be done is
moving all the alsa controls for various inputs from the codecs to the
fabric, the fabric needs to know these anyway and code can be simplified and
prepared for headphone/... detection if the codecs simply provide hooks to
turn on/off the various in- and outputs.

Any comments are appreciated.

Thanks,
johannes

^ 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