* How to find out what a process has been doing on a PPC-440 system
From: Kallol Biswas @ 2006-06-02 0:36 UTC (permalink / raw)
To: External List
Hi,
I have a list of processes running in a linux system (2.6.11 kernel).
One of the processes invokes send() system call and does not return.
The command "ps" shows that the process is in "RUNNING" state. With an ICE
I could dump each process structures and all the fields, but can't find a way to get the stack trace for each process. Not sure how contexts are saved on ppc-4xx kernel. It seems that they are saved in kernel stack. How do we
get the top of the stack from the task_struct pointers? How to find out
where in kernel send() blocks (not sleeping/scheduled)?
Kallol
^ permalink raw reply
* Re: [PATCH] use msleep() for RTAS delays
From: John Rose @ 2006-06-01 22:25 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: External List, Paul Mackerras
In-Reply-To: <1149177349.9812.16.camel@sinatra.austin.ibm.com>
> I don't mind writing this up. The previous patch fixes a reported bug
> with minimal reorg. If possible, I'd like to restructure on top of
> that.
On second thought, why not reorg now! I'll have a patch shortly.
Thanks-
John
^ permalink raw reply
* RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
From: Benjamin Herrenschmidt @ 2006-06-01 22:06 UTC (permalink / raw)
To: Alexandre Bounine; +Cc: linuxppc-dev list, Paul Mackerras, Yang Xin-Xin-r48390
In-Reply-To: <8A1F97E8A7ACE847B1DB69DFDCBC6E807D633A@caribou.pc.tundra.com>
On Thu, 2006-06-01 at 16:45 -0400, Alexandre Bounine wrote:
> All differences in the Tsi108/109 PIC code from the standard MPIC are caused by the HW behavior. The Tsi108/109 PIC looks like standard OpenPIC but, in fact, is different in registers mapping and behavior. Its logic is close but not exactly as MPIC.
>
> Here are replies on comments to the code:
>
> 1.Why do you have to check if its a LEVEL irq?
>
> Check for LEVEL irqs is required in the ack/end pair to enable nested interrupt servicing and
> does not hang when core (local) interrupts are re-enabled in the ISR. Otherwise we have to use
> SA_INTERRUPT flag for all level signaled interrupts.
Can you be more precise about what exactly happens and why ? Unless you
EOI handling is broken of course, there should be no need to do anything
other than a single eoi in end(), period.
> 2. if the PIC works like other openpic's you dont need an 'ack' we
> handle it via 'end'
>
> Tsi108/109 needs it.
What for ? Please, give the low level details.
> 3. why the changes to where we do mpic_eoi for TSI108?
> The Tsi108 PIC requires EOI for spurious interrupt (as all other interrupt sources).
Ok, that is acceptable.
> The do_IRQ() does not call end routine for spurious interrupts.
>
> The MPIC code patch for Tsi108/109 demonstrates HW differences and why we originally considered having separate code for Tsi108 pic.
Please tell me more about the HW differences :)
Ben.
>
>
> -----Original Message-----
> From: Kumar Gala [mailto:galak@kernel.crashing.org]
> Sent: Tuesday, May 30, 2006 3:18 PM
> To: Zang Roy-r61911
> Cc: Benjamin Herrenschmidt; linuxppc-dev list; Yang Xin-Xin-r48390; Paul
> Mackerras; Alexandre Bounine
> Subject: Re: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
>
>
>
> On May 29, 2006, at 10:28 PM, Zang Roy-r61911 wrote:
>
> >>
> >> On Wed, 2006-05-17 at 18:14 +0800, Zang Roy-r61911 wrote:
> >>> Add Tundra Semiconductor tsi108 host bridge interrupt
> >> controller support.
> >>
> >> It looks a bit like an hacked up MPIC... Is it different
> >> enough to justify a separate driver ? Or would it be possible
> >> to define a TSI108 flag to pass the current mpic driver and
> >> add the necessary bits to it ?
> >>
> >
> > Tsi108 implementation of MPIC has many differences form the
> > original one, the following
> > code implements it with mpic. Any comment? The following patch is
> > just based on
> > my previous send out patches.
>
> In the future please provide it against the base, much easier to read.
>
> > Integrate Tundra Semiconductor tsi108 host bridge interrupt controller
> > to mpic arch.
> >
> > Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> >
>
> [snip]
>
> >
> > --- linux-2.6.17-rc4-tun/arch/powerpc/sysdev/mpic.c 2006-03-20
> > 00:53:29.000000000 -0500
> > +++ linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/mpic.c 2006-05-29
> > 16:07:03.000000000 -0400
> > @@ -81,7 +81,7 @@ static inline void _mpic_write(unsigned
> > static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi)
> > {
> > unsigned int be = (mpic->flags & MPIC_BIG_ENDIAN) != 0;
> > - unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
> > + unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi *
> > MPIC_GREG_IPI_STRIDE);
> >
> > if (mpic->flags & MPIC_BROKEN_IPI)
> > be = !be;
> > @@ -90,7 +90,7 @@ static inline u32 _mpic_ipi_read(struct
> >
> > static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int
> > ipi, u32 value)
> > {
> > - unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
> > + unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi *
> > MPIC_GREG_IPI_STRIDE);
> >
> > _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset,
> > value);
> > }
> > @@ -393,7 +393,11 @@ static inline struct mpic * mpic_from_ir
> > static inline void mpic_eoi(struct mpic *mpic)
> > {
> > mpic_cpu_write(MPIC_CPU_EOI, 0);
> > +#ifndef CONFIG_TSI108_BRIDGE
> > (void)mpic_cpu_read(MPIC_CPU_WHOAMI);
> > +#else
> > + (void)mpic_cpu_read(MPIC_CPU_OUTPUT);
> > +#endif
> > }
> >
> > #ifdef CONFIG_SMP
> > @@ -514,9 +518,26 @@ static void mpic_end_irq(unsigned int ir
> > }
> > #endif /* CONFIG_MPIC_BROKEN_U3 */
> >
> > +#ifdef CONFIG_TSI108_BRIDGE
> > + if ((irq_desc[irq].status & IRQ_LEVEL) != 0)
> > +#endif
>
> Why do you have to check if its a LEVEL irq?
>
> > mpic_eoi(mpic);
> > }
> >
> > +#ifdef CONFIG_TSI108_BRIDGE
> > +static void mpic_ack_irq(unsigned int irq)
> > +{
> > + struct mpic *mpic = mpic_from_irq(irq);
> > +
> > +#ifdef DEBUG_IRQ
> > + DBG("%s: ack_irq: %d\n", mpic->name, irq);
> > +#endif
> > +
> > + if ((irq_desc[irq].status & IRQ_LEVEL) == 0)
> > + mpic_eoi(mpic);
> > +}
> > +#endif /* CONFIG_TSI108_BRIDGE */
> > +
>
> if the PIC works like other openpic's you dont need an 'ack' we
> handle it via 'end'
>
> > #ifdef CONFIG_SMP
> >
> > static void mpic_enable_ipi(unsigned int irq)
> > @@ -596,6 +617,9 @@ struct mpic * __init mpic_alloc(unsigned
> > mpic->hc_irq.enable = mpic_enable_irq;
> > mpic->hc_irq.disable = mpic_disable_irq;
> > mpic->hc_irq.end = mpic_end_irq;
> > +#ifdef CONFIG_TSI108_BRIDGE
> > + mpic->hc_irq.ack = mpic_ack_irq;
> > +#endif
> > if (flags & MPIC_PRIMARY)
> > mpic->hc_irq.set_affinity = mpic_set_affinity;
> > #ifdef CONFIG_SMP
> > @@ -955,8 +979,13 @@ void mpic_send_ipi(unsigned int ipi_no,
> > DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no);
> > #endif
> >
> > +#ifndef CONFIG_TSI108_BRIDGE
> > mpic_cpu_write(MPIC_CPU_IPI_DISPATCH_0 + ipi_no * 0x10,
> > mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
> > +#else /* CONFIG_TSI108_BRIDGE */
> > + mpic_write(mpic->gregs, MPIC_CPU_IPI_DISPATCH,
> > + mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
> > +#endif /* !CONFIG_TSI108_BRIDGE */
> > }
> >
> > int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs)
> > @@ -972,11 +1001,20 @@ int mpic_get_one_irq(struct mpic *mpic,
> > DBG("%s: cascading ...\n", mpic->name);
> > #endif
> > irq = mpic->cascade(regs, mpic->cascade_data);
> > +#ifdef DEBUG_LOW
> > + DBG("%s: cascaded irq: %d\n", mpic->name, irq);
> > +#endif
> > +#ifndef CONFIG_TSI108_BRIDGE
> > mpic_eoi(mpic);
> > +#endif
> > return irq;
> > }
> > - if (unlikely(irq == MPIC_VEC_SPURRIOUS))
> > + if (unlikely(irq == MPIC_VEC_SPURRIOUS)) {
> > +#ifdef CONFIG_TSI108_BRIDGE
> > + mpic_eoi(mpic);
> > +#endif
> > return -1;
> > + }
>
> why the changes to where we do mpic_eoi for TSI108?
>
> > if (irq < MPIC_VEC_IPI_0) {
> > #ifdef DEBUG_IRQ
> > DBG("%s: irq %d\n", mpic->name, irq + mpic->irq_offset);
>
>
>
> [snip]
^ permalink raw reply
* Re: Collecting hypervisor call stats
From: Benjamin Herrenschmidt @ 2006-06-01 21:57 UTC (permalink / raw)
To: Arnd Bergmann; +Cc: linuxppc-dev, Paul Mackerras
In-Reply-To: <200606012014.20467.arnd.bergmann@de.ibm.com>
On Thu, 2006-06-01 at 20:14 +0200, Arnd Bergmann wrote:
> On Thursday 01 June 2006 07:26, Benjamin Herrenschmidt wrote:
> >
> > > We need to get a timestamp before and after the call. mftb should do
> > > the trick. Also, I'd prefer to have the code that stuffs the values
> > > into the array be C. So, the decision is to have the assembly code
> > > call out to the C routine -OR- create wrappers for the assembly routines.
> > > I much prefer C wrappers to touching the assembly.
> >
> > Argh... yet another use of mftb for which I'll need a cell specific
> > workaround
>
> I guess for statistics, we should rather avoid that overhead (and
> document the fact that we did on purpose).
Well.. if we can at one point "detect" the totally bogus value (the
timestamp going massively backward) and fix it up there, ok. Might be an
option.
> How fast is mftb really? Would that be significant compared to the
> time spent in a fast hcall?
Not sure, I would expect a few dozens cycles at least though...
Ben.
^ permalink raw reply
* SII3124-2
From: Rune Torgersen @ 2006-06-01 21:10 UTC (permalink / raw)
To: linuxppc-dev
Has anybody been successful in getting a SII3124-2 based SATA controller
to work under PPC?
I have a eval board that I tried on two different freescale boards (a
MPC8266ADS board and a MPC8560ADS board).
Kernel 2.6.16.16.
Here is the relevant output from the kernel.
ata1: SATA max UDMA/100 cmd 0xD1010000 ctl 0x0 bmdma 0x0 irq 115
ata2: SATA max UDMA/100 cmd 0xD1012000 ctl 0x0 bmdma 0x0 irq 115
ata3: SATA max UDMA/100 cmd 0xD1014000 ctl 0x0 bmdma 0x0 irq 115
ata4: SATA max UDMA/100 cmd 0xD1016000 ctl 0x0 bmdma 0x0 irq 115
ata1: SATA link down (SStatus 0)
scsi0 : sata_sil24
ata2: SATA link up 3.0 Gbps (SStatus 123)
sata_sil24 ata2: SRST failed, disabling port
scsi1 : sata_sil24
ata3: SATA link down (SStatus 0)
scsi2 : sata_sil24
ata4: SATA link down (SStatus 0)
scsi3 : sata_sil24
I added debug output to see the content of the Command Error register.
It is set to 26 which according to the datasheet for the 3124-1 (I am
running a -2), is PLDCMDERRORMASTERABORT, "A PCI Master Abort occurred
while the SiI3124 was fetching a Port Request Block (PRB) from host
memory."
^ permalink raw reply
* Re: [PATCH] yaboot: enable boot from iscsi target via ethernet devices on js20.
From: Doug Maxey @ 2006-06-01 20:56 UTC (permalink / raw)
Cc: yaboot-devel, Linux PowerPC List
In-Reply-To: <200605162040.k4GKeUsY007712@falcon10.austin.ibm.com>
On Tue, 16 May 2006 15:40:30 CDT, Doug Maxey wrote:
>
>On Tue, 16 May 2006 16:20:47 EDT, Paul Nasrat wrote:
>>On Fri, 2006-04-28 at 01:05 -0500, Doug Maxey wrote:
>>> Certain levels of JS20 firmware will allow the system to boot from an
>>> iscsi target. System OFW accomplishes this by setting up a virtual
>>> disk device with parameters. These parameters, when passed back to
>>> OFW by yaboot, directs the FW to use virtual device over the ethernet
>>> port that will then access iscsi target as a block device. This patch
>>> extracts those parameters from the property of the virtual device and
>>> passes them back to OFW to indicate the kernel is to be retrieved via
>>> the iscsi protocol.
>>>
>>> Signed-off-by: Doug Maxey <dwm@austin.ibm.com>
>>
>>Sorry for the delay in getting back to you - a few initial questions:
>
>np. been on vacation the last 10 days. Switzerland is nice, and Milan
>is cool. :)
>
>>
>>> diff --git a/second/file.c b/second/file.c
>>
>>> @@ -185,16 +188,45 @@ parse_device_path(char *imagepath, char
>>>
>>> if (!imagepath)
>>> return 0;
>>> +
>>> + /*
>>> + * Do preliminary checking for an iscsi device; it may appear as
>>> + * pure a network device (device_type == "network") if this is
>>> + * ISWI. This is the case on IBM systems doing an iscsi OFW
>>> + * boot.
>>> + */
>>> + if (strstr(imagepath, ",iscsi"))
>>
>>Is the , always guaranteed to be there - eg if I have boot
>>eth1:iscsi,ISCSIARGS won't this check fail.
>
>Yes, with the above command line this would fail.
>
>My point of reference are the bindings that we cannot yet talk about
>here, yet. The device args would always be followed by a comma. I suppose
>that we could just reference the string "iscsi", but then some wag
>would want to create some other property that included "iscsi" as a
>substring. Maybe append a comma?
Any preferences on this?
I have another, more radical solution.
Adding a parser that understands the full device path and that can
return the elements neatly packaged. Film at 11.
>
>
>>
>>> diff --git a/second/prom.c b/second/prom.c
>>> index 5ec06b8..9bc5415 100644
>>> --- a/second/prom.c
>>> +++ b/second/prom.c
>>> @@ -174,6 +174,9 @@ prom_get_devtype (char *device)
>>> int result;
>>> char tmp[64];
>>>
>>> + if (strstr(device, ",iscsi"))
>>> + device = strcpy(tmp, "/vdevice/gscsi/disk");
>>> +
>>
>>Ditto here.
>
>likewise. Maybe make it a #define so it would be common.
>
>>
>>Paul
>>
++doug
^ permalink raw reply
* RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
From: Alexandre Bounine @ 2006-06-01 20:45 UTC (permalink / raw)
To: Kumar Gala, Zang Roy-r61911
Cc: Yang Xin-Xin-r48390, Paul Mackerras, linuxppc-dev list
All differences in the Tsi108/109 PIC code from the standard MPIC are =
caused by the HW behavior. The Tsi108/109 PIC looks like standard =
OpenPIC but, in fact, is different in registers mapping and behavior. =
Its logic is close but not exactly as MPIC. =20
Here are replies on comments to the code:
1.Why do you have to check if its a LEVEL irq?
Check for LEVEL irqs is required in the ack/end pair to enable nested =
interrupt servicing and
does not hang when core (local) interrupts are re-enabled in the ISR. =
Otherwise we have to use=20
SA_INTERRUPT flag for all level signaled interrupts.
2. if the PIC works like other openpic's you dont need an 'ack' we =20
handle it via 'end'
Tsi108/109 needs it.
3. why the changes to where we do mpic_eoi for TSI108?
The Tsi108 PIC requires EOI for spurious interrupt (as all other =
interrupt sources).
The do_IRQ() does not call end routine for spurious interrupts. =20
The MPIC code patch for Tsi108/109 demonstrates HW differences and why =
we originally considered having separate code for Tsi108 pic.
Regards,
Alex.
=20
-----Original Message-----
From: Kumar Gala [mailto:galak@kernel.crashing.org]
Sent: Tuesday, May 30, 2006 3:18 PM
To: Zang Roy-r61911
Cc: Benjamin Herrenschmidt; linuxppc-dev list; Yang Xin-Xin-r48390; Paul
Mackerras; Alexandre Bounine
Subject: Re: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
On May 29, 2006, at 10:28 PM, Zang Roy-r61911 wrote:
>>
>> On Wed, 2006-05-17 at 18:14 +0800, Zang Roy-r61911 wrote:
>>> Add Tundra Semiconductor tsi108 host bridge interrupt
>> controller support.
>>
>> It looks a bit like an hacked up MPIC... Is it different
>> enough to justify a separate driver ? Or would it be possible
>> to define a TSI108 flag to pass the current mpic driver and
>> add the necessary bits to it ?
>>
>
> Tsi108 implementation of MPIC has many differences form the =20
> original one, the following
> code implements it with mpic. Any comment? The following patch is =20
> just based on
> my previous send out patches.
In the future please provide it against the base, much easier to read.
> Integrate Tundra Semiconductor tsi108 host bridge interrupt controller
> to mpic arch.
>
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
>
[snip]
>
> --- linux-2.6.17-rc4-tun/arch/powerpc/sysdev/mpic.c 2006-03-20 =20
> 00:53:29.000000000 -0500
> +++ linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/mpic.c 2006-05-29 =20
> 16:07:03.000000000 -0400
> @@ -81,7 +81,7 @@ static inline void _mpic_write(unsigned
> static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi)
> {
> unsigned int be =3D (mpic->flags & MPIC_BIG_ENDIAN) !=3D 0;
> - unsigned int offset =3D MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
> + unsigned int offset =3D MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * =20
> MPIC_GREG_IPI_STRIDE);
>
> if (mpic->flags & MPIC_BROKEN_IPI)
> be =3D !be;
> @@ -90,7 +90,7 @@ static inline u32 _mpic_ipi_read(struct
>
> static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int =20
> ipi, u32 value)
> {
> - unsigned int offset =3D MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
> + unsigned int offset =3D MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * =20
> MPIC_GREG_IPI_STRIDE);
>
> _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset, =20
> value);
> }
> @@ -393,7 +393,11 @@ static inline struct mpic * mpic_from_ir
> static inline void mpic_eoi(struct mpic *mpic)
> {
> mpic_cpu_write(MPIC_CPU_EOI, 0);
> +#ifndef CONFIG_TSI108_BRIDGE
> (void)mpic_cpu_read(MPIC_CPU_WHOAMI);
> +#else
> + (void)mpic_cpu_read(MPIC_CPU_OUTPUT);
> +#endif
> }
>
> #ifdef CONFIG_SMP
> @@ -514,9 +518,26 @@ static void mpic_end_irq(unsigned int ir
> }
> #endif /* CONFIG_MPIC_BROKEN_U3 */
>
> +#ifdef CONFIG_TSI108_BRIDGE
> + if ((irq_desc[irq].status & IRQ_LEVEL) !=3D 0)
> +#endif
Why do you have to check if its a LEVEL irq?
> mpic_eoi(mpic);
> }
>
> +#ifdef CONFIG_TSI108_BRIDGE
> +static void mpic_ack_irq(unsigned int irq)
> +{
> + struct mpic *mpic =3D mpic_from_irq(irq);
> +
> +#ifdef DEBUG_IRQ
> + DBG("%s: ack_irq: %d\n", mpic->name, irq);
> +#endif
> +
> + if ((irq_desc[irq].status & IRQ_LEVEL) =3D=3D 0)
> + mpic_eoi(mpic);
> +}
> +#endif /* CONFIG_TSI108_BRIDGE */
> +
if the PIC works like other openpic's you dont need an 'ack' we =20
handle it via 'end'
> #ifdef CONFIG_SMP
>
> static void mpic_enable_ipi(unsigned int irq)
> @@ -596,6 +617,9 @@ struct mpic * __init mpic_alloc(unsigned
> mpic->hc_irq.enable =3D mpic_enable_irq;
> mpic->hc_irq.disable =3D mpic_disable_irq;
> mpic->hc_irq.end =3D mpic_end_irq;
> +#ifdef CONFIG_TSI108_BRIDGE
> + mpic->hc_irq.ack =3D mpic_ack_irq;
> +#endif
> if (flags & MPIC_PRIMARY)
> mpic->hc_irq.set_affinity =3D mpic_set_affinity;
> #ifdef CONFIG_SMP
> @@ -955,8 +979,13 @@ void mpic_send_ipi(unsigned int ipi_no,
> DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no);
> #endif
>
> +#ifndef CONFIG_TSI108_BRIDGE
> mpic_cpu_write(MPIC_CPU_IPI_DISPATCH_0 + ipi_no * 0x10,
> mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
> +#else /* CONFIG_TSI108_BRIDGE */
> + mpic_write(mpic->gregs, MPIC_CPU_IPI_DISPATCH,
> + mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
> +#endif /* !CONFIG_TSI108_BRIDGE */
> }
>
> int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs)
> @@ -972,11 +1001,20 @@ int mpic_get_one_irq(struct mpic *mpic,
> DBG("%s: cascading ...\n", mpic->name);
> #endif
> irq =3D mpic->cascade(regs, mpic->cascade_data);
> +#ifdef DEBUG_LOW
> + DBG("%s: cascaded irq: %d\n", mpic->name, irq);
> +#endif
> +#ifndef CONFIG_TSI108_BRIDGE
> mpic_eoi(mpic);
> +#endif
> return irq;
> }
> - if (unlikely(irq =3D=3D MPIC_VEC_SPURRIOUS))
> + if (unlikely(irq =3D=3D MPIC_VEC_SPURRIOUS)) {
> +#ifdef CONFIG_TSI108_BRIDGE
> + mpic_eoi(mpic);
> +#endif
> return -1;
> + }
why the changes to where we do mpic_eoi for TSI108?
> if (irq < MPIC_VEC_IPI_0) {
> #ifdef DEBUG_IRQ
> DBG("%s: irq %d\n", mpic->name, irq + mpic->irq_offset);
[snip]
^ permalink raw reply
* Re: Pinned TLB entries with 2.6 linux kernel on PPC4xx
From: Matt Porter @ 2006-06-01 20:53 UTC (permalink / raw)
To: Chris Dumoulin, linuxppc-embedded
In-Reply-To: <20060601195129.GA15262@gate.ebshome.net>
On Thu, Jun 01, 2006 at 12:51:29PM -0700, Eugene Surovegin wrote:
> On Thu, Jun 01, 2006 at 03:29:37PM -0400, Chris Dumoulin wrote:
> > Does the idea of creating pinned TLB entries (ones that will never be
> > overwritten) make sense for a PPC4xx (specifically PPC405) 2.6 linux
> > kernel? If so, how would this be accomplished?
>
> 44x kernel already pins some TLB entries, 40x may use this approach to
> increase performance (I use this in my internal 2.4 tree
> quite successfully).
>
> Old 2.4 trees (linuxppc-2.4 or devel_2_4) have TLB pinning support
> for 40x, you can look at the implementation there.
The partial kernel lowmem pinning for ppc405 was deprecated in favor
of having all of kernel lowmem covered by large pages and then large
TLBs loaded on tlb misses. This is regarding 2.6, of course.
It can also be extended to handle arbitrary areas other than kernel
lowmem.
-Matt
^ permalink raw reply
* Re: memset hangs part way through; called from early_init
From: Wolfgang Denk @ 2006-06-01 20:32 UTC (permalink / raw)
To: Chris Dumoulin; +Cc: linuxppc-embedded
In-Reply-To: <200606011700.k51H0WJw014425@www-webmail1.magma.ca>
In message <200606011700.k51H0WJw014425@www-webmail1.magma.ca> you wrote:
>
> Currently, the board hangs in the early_init function. Specifically, at the following line:
> memset_io(PTRRELOC(&__bss_start), 0, _end - __bss_start);
...
> Any ideas would be appreciated.
See http://www.denx.de/wiki/view/DULG/LinuxCrashesRandomly
Best regards,
Wolfgang Denk
--
Software Engineering: Embedded and Realtime Systems, Embedded Linux
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
A princess should not be afraid -- not with a brave knight to protect
her.
-- McCoy, "Shore Leave", stardate 3025.3
^ permalink raw reply
* Re: Pinned TLB entries with 2.6 linux kernel on PPC4xx
From: Eugene Surovegin @ 2006-06-01 19:51 UTC (permalink / raw)
To: Chris Dumoulin; +Cc: linuxppc-embedded
In-Reply-To: <447F4021.5030109@ics-ltd.com>
On Thu, Jun 01, 2006 at 03:29:37PM -0400, Chris Dumoulin wrote:
> Does the idea of creating pinned TLB entries (ones that will never be
> overwritten) make sense for a PPC4xx (specifically PPC405) 2.6 linux
> kernel? If so, how would this be accomplished?
44x kernel already pins some TLB entries, 40x may use this approach to
increase performance (I use this in my internal 2.4 tree
quite successfully).
Old 2.4 trees (linuxppc-2.4 or devel_2_4) have TLB pinning support
for 40x, you can look at the implementation there.
--
Eugene
^ permalink raw reply
* Pinned TLB entries with 2.6 linux kernel on PPC4xx
From: Chris Dumoulin @ 2006-06-01 19:29 UTC (permalink / raw)
To: linuxppc-embedded
Does the idea of creating pinned TLB entries (ones that will never be
overwritten) make sense for a PPC4xx (specifically PPC405) 2.6 linux
kernel? If so, how would this be accomplished?
Regards,
Chris
--
*--Christopher Dumoulin--*
Software Team Leader
<http://ics-ltd.com/>
<http://ics-ltd.com/>
Interactive Circuits and Systems Ltd.
5430 Canotek Road
Ottawa, ON
K1J 9G2
(613)749-9241
1-800-267-9794 (USA only)
------------------------------------------------------------------------
This e-mail is private and confidential and is for the addressee only.
If misdirected, please notify us by telephone and confirm that it has
been deleted from your system and any hard copies destroyed. You are
strictly prohibited from using, printing, distributing or disseminating
it or any information contained in it save to the intended recipient.
^ permalink raw reply
* Re: Collecting hypervisor call stats
From: Arnd Bergmann @ 2006-06-01 18:14 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Paul Mackerras
In-Reply-To: <1149139576.28307.28.camel@localhost.localdomain>
On Thursday 01 June 2006 07:26, Benjamin Herrenschmidt wrote:
>=20
> > We need to get a timestamp before and after the call. =A0mftb should do
> > the trick. =A0Also, I'd prefer to have the code that stuffs the values
> > into the array be C. =A0So, the decision is to have the assembly code
> > call out to the C routine -OR- create wrappers for the assembly routine=
s.
> > I much prefer C wrappers to touching the assembly.
>=20
> Argh... yet another use of mftb for which I'll need a cell specific
> workaround=20
I guess for statistics, we should rather avoid that overhead (and
document the fact that we did on purpose).
How fast is mftb really? Would that be significant compared to the
time spent in a fast hcall?
Arnd <><
^ permalink raw reply
* Re: [PATCH] use msleep() for RTAS delays
From: Arnd Bergmann @ 2006-06-01 18:09 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Paul Mackerras
In-Reply-To: <17534.32164.268580.217501@cargo.ozlabs.ibm.com>
On Thursday 01 June 2006 07:39, Paul Mackerras wrote:
>
> > rtas_call_waitbusy(...);
>
> That's a good idea...
Do we also need an atomic version of that? Maybe there are
places where we actually need to call a slow rtas call
under a spinlock.
At the very least, rtas_call_waitbusy() should have a
might_sleep() in it to find those places.
Arnd <><
^ permalink raw reply
* Re: Linux 2.4 Kernel on Xilinx Virtex4 FX100's PPC
From: Peter Ryser @ 2006-06-01 18:03 UTC (permalink / raw)
To: Grant Likely; +Cc: Anantharaman Chetan-W16155, linuxppc-embedded
In-Reply-To: <528646bc0606011008o35096b43p42cc6aa9c0002f8c@mail.gmail.com>
It's a little bit more complicated than that but your statement is
basically correct.
- Peter
Grant Likely wrote:
> On 6/1/06, Peter Ryser <peter.ryser@xilinx.com> wrote:
>
>> There are some silicon issues on the PPC405 in V4 with PVR 0x20011430
>> which are documented in Xilinx solution record 20658. All these issues
>> are fixed in silicon where the PPC405 has a PVR of 0x20011470.
>>
>> Said that it's not true that the caches cannot be used in silicon with
>> PVR 0x20011430. The problem is a corner case which does not show in
>> typical designs.
>
>
> If I understand correctly, the cache issue only shows up with RAM
> attached to the OPB (instead of PLB). Is that correct?
>
> Cheers,
> g.
>
>
^ permalink raw reply
* Re: Linux 2.4 Kernel on Xilinx Virtex4 FX100's PPC
From: Peter Ryser @ 2006-06-01 16:59 UTC (permalink / raw)
To: Anantharaman Chetan-W16155; +Cc: linuxppc-embedded
In-Reply-To: <EDF27F298D4B03498AE0C249A941DFF803D6E8@de01exm68.ds.mot.com>
We have Linux up and running on the Virtex-4 FX100. The PVR for the
PPC405 in all FX100 is 0x20011470.
- Peter
Anantharaman Chetan-W16155 wrote:
>Was the port done on a FX100 FPGA? Also, what PVR number does the PPC405
>indicate?
>Thanks,
>Chetan
>
>-----Original Message-----
>From: glikely@gmail.com [mailto:glikely@gmail.com] On Behalf Of Grant
>Likely
>Sent: Wednesday, May 31, 2006 2:35 PM
>To: Anantharaman Chetan-W16155
>Cc: linuxppc-embedded@ozlabs.org
>Subject: Re: Linux 2.4 Kernel on Xilinx Virtex4 FX100's PPC
>
>On 5/31/06, Anantharaman Chetan-W16155
><Chetan.S.Anantharaman@motorola.com> wrote:
>
>
>>Has anyone successfully ported a Linux 2.4 Kernel on a Xilinx Virtex-4
>>
>>
>FX
>
>
>>series FPGA's, PPC405 processor?
>>
>>
>
>Linux on the V4-FX is well supported. Xilinx has an app node
>describing how to modify the linuxppc-2.4 tree to work on the V4-FX.
>You can find out how to get the tree here:
>
>http://www.penguinppc.org/kernel/
>I've got both 2.4 & 2.6 happily running on my ML403 board here.
>
>Cheers,
>g.
>
>
>
^ permalink raw reply
* Re: Linux 2.4 Kernel on Xilinx Virtex4 FX100's PPC
From: Grant Likely @ 2006-06-01 17:08 UTC (permalink / raw)
To: Peter Ryser; +Cc: Anantharaman Chetan-W16155, linuxppc-embedded
In-Reply-To: <447F1E48.10808@xilinx.com>
On 6/1/06, Peter Ryser <peter.ryser@xilinx.com> wrote:
> There are some silicon issues on the PPC405 in V4 with PVR 0x20011430
> which are documented in Xilinx solution record 20658. All these issues
> are fixed in silicon where the PPC405 has a PVR of 0x20011470.
>
> Said that it's not true that the caches cannot be used in silicon with
> PVR 0x20011430. The problem is a corner case which does not show in
> typical designs.
If I understand correctly, the cache issue only shows up with RAM
attached to the OPB (instead of PLB). Is that correct?
Cheers,
g.
^ permalink raw reply
* Re: Linux 2.4 Kernel on Xilinx Virtex4 FX100's PPC
From: Peter Ryser @ 2006-06-01 17:05 UTC (permalink / raw)
To: Aidan Williams; +Cc: Anantharaman Chetan-W16155, linuxppc-embedded
In-Reply-To: <447E1725.4010908@nicta.com.au>
There are some silicon issues on the PPC405 in V4 with PVR 0x20011430=20
which are documented in Xilinx solution record 20658. All these issues=20
are fixed in silicon where the PPC405 has a PVR of 0x20011470.
Said that it's not true that the caches cannot be used in silicon with=20
PVR 0x20011430. The problem is a corner case which does not show in=20
typical designs.
- Peter
Aidan Williams wrote:
>Anantharaman Chetan-W16155 wrote:
> =20
>
>>Has anyone successfully ported a Linux 2.4 Kernel on a Xilinx Virtex-4=20
>>FX series FPGA=92s, PPC405 processor?
>>
>> =20
>>
>
>Yes, see=20
>http://ozlabs.org/pipermail/linuxppc-embedded/2006-April/022583.html
>
>Note that there are silicon bugs that prevent caches being used in some=20
>chips.
>
> =20
>
>>More specifically, the FX100 FPGA?
>> =20
>>
>
>I don't have one of those, sorry.
>
>- aidan
>
>_______________________________________________
>Linuxppc-embedded mailing list
>Linuxppc-embedded@ozlabs.org
>https://ozlabs.org/mailman/listinfo/linuxppc-embedded
>
>
> =20
>
^ permalink raw reply
* memset hangs part way through; called from early_init
From: Chris Dumoulin @ 2006-06-01 17:00 UTC (permalink / raw)
To: linuxppc-embedded
Hi All,
I'm running a V2Pro-based development board, and I'm working on a linux 2.6.15 port.
Currently, the board hangs in the early_init function. Specifically, at the following line:
memset_io(PTRRELOC(&__bss_start), 0, _end - __bss_start);
This memset_io() call eventually calls memset() (from arch/ppc/lib/string.S) to zero the
region of memory. Using a BDI2000, I'm able to step through the assembly and see that
within memset, it enters a two-instruction loop where it is writing zeros to consecutive
memory locations. Everything is going fine, but after 10 or 20 iterations of the loop
(it's not consistent), everything blows up.
GDB reports the following:
------------------------------
(gdb) stepi
Program received signal SIG110, Real-time event 110.
0xc000b3e8 in memset ()
(gdb)
------------------------------
The BDI2000 reports the following:
------------------------------
*** MMU: address translation for 0xC000B3E8 failed
*** MMU: address translation for 0xC000B3E4 failed
*** MMU: address translation for 0xC000B3E8 failed
*** MMU: address translation for 0xC000B3E4 failed
*** MMU: address translation for 0xC000B3B0 failed
*** MMU: address translation for 0xDEADBEEF failed
*** MMU: address translation for 0xDEADBEEB failed
*** MMU: address translation for 0xDEADBEEF failed
*** MMU: address translation for 0xDEADBEEB failed
*** MMU: address translation for 0x00000000 failed
*** MMU: address translation for 0xDEADBEEF failed
*** MMU: address translation for 0xDEADBEEB failed
*** MMU: address translation for 0xDEADBEEF failed
*** MMU: address translation for 0xDEADBEEB failed
*** MMU: address translation for 0x00000000 failed
------------------------------
Here is my BDI2000 configuration file:
------------------------------
[INIT]
[TARGET]
JTAGCLOCK 0 ;use 16 MHz JTAG clock
CPUTYPE 405 ;the used target CPU type
BDIMODE AGENT ;the BDI working mode (LOADONLY | AGENT)
BREAKMODE HARD ;SOFT or HARD, HARD uses PPC hardware breakpoint
STEPMODE HWBP ;JTAG or HWBP, HWPB uses one or two hardware breakpoints
MMU XLAT 0xC0000000 ;enable virtual address mode
PTBASE 0x000000f0 ;address where kernel/user stores pointer to page table
STARTUP RESET
[HOST]
IP 192.9.200.213
FILE ppcboot.bin
FORMAT BIN 0xfff80000 ;0x200000
START 0xfff80000 ; 0x200000
LOAD MANUAL ;load code MANUAL or AUTO after reset
DEBUGPORT 2001
DUMP dump.bin ;Linux: dump.bin must already exist and public writable
[FLASH]
WORKSPACE 0xffffd000
CHIPTYPE STRATAX16
BUSWIDTH 16
CHIPSIZE 0x400000
[REGS]
IDCR1 0x010 0x011 ;MEMCFGADR and MEMCFGDATA
IDCR2 0x012 0x013 ;EBCCFGADR and EBCCFGDATA
IDCR3 0x014 0x015 ;KIAR and KIDR
FILE reg405gp.def
------------------------------
Any ideas would be appreciated.
Regards,
Chris Dumoulin
^ permalink raw reply
* Re: [PATCH] use msleep() for RTAS delays
From: John Rose @ 2006-06-01 15:55 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: External List, Paul Mackerras
In-Reply-To: <1149139866.28307.32.camel@localhost.localdomain>
Hi Ben-
> What about putting this whole thing in a helper ? There is a few more
> things I've seen floating around implementing the exact same logic (for
> example Jake's MSI patches).
I don't mind writing this up. The previous patch fixes a reported bug
with minimal reorg. If possible, I'd like to restructure on top of
that.
> Or something inside rtas_call
>
> rtas_call_waitbusy(...);
Certain RTAS calls expect identical inputs between successive calls,
while others (ibm,change-msi) expect modified "sequence" inputs, etc.
Given this inconsistency, we probably can't handle busy rcs inside
rtas_call().
Thanks-
John
^ permalink raw reply
* Re: Can't get CoralP drivers to work (fixed)
From: jourdan @ 2006-06-01 15:32 UTC (permalink / raw)
To: Wolfgang Denk; +Cc: linuxppc-embedded
In-Reply-To: <20060530203221.236923525D9@atlas.denx.de>
Hello guys
I had time to dig into the kernel code and I discovered the origin of my
problem. As I said in a previous post, we use a custom processor card
based on a mpc8270. In the initialisation of our board was missing the
following line :
conswitchp = &dummy_con;
That's all !
Now it works fine.
I still have a little problem : video output signals seems to be very low.
I have to set the maximum contrast and luminosity to see something. I
tried on different monitors, the problem is the same.
The board is configured to use the rgb analog signals from the coral, not
from the dac.
Are there any jumpers or register to tweak the video signal levels ? I
can't find it in the hardware manual...
^ permalink raw reply
* [RFC 6/8] snd-aoa: add layout-id fabric
From: Johannes Berg @ 2006-06-01 11:58 UTC (permalink / raw)
To: alsa-devel; +Cc: linuxppc-dev
In-Reply-To: <20060601115844.343214000@sipsolutions.net>
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/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SND_AOA_FABRIC_LAYOUT) += snd-aoa-fabric-layout.o
--- /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/snd-aoa-fabric-layout.c
@@ -0,0 +1,889 @@
+/*
+ * 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-60");
+MODULE_ALIAS("sound-layout-61");
+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 */
+};
+
+/* onyx on machines with real line-out */
+static struct codec_connection onyx_connections_reallineout[] = {
+ {
+ .connected = CC_SPEAKERS | CC_LINEOUT,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_DIGITALOUT,
+ .codec_bit = 1,
+ },
+ {
+ .connected = CC_LINEIN,
+ .codec_bit = 2,
+ },
+ { /* TBD: is this really present on PowerMac9,1? */
+ .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 */,
+ },
+ },
+ /* PowerMac9,1 */
+ { .layout_id = 60,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_reallineout,
+ },
+ },
+ /* PowerMac9,1 */
+ { .layout_id = 61,
+ .codecs[0] = {
+ .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 layout_dev_ptr {
+ struct layout_dev *ptr;
+};
+
+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;
+
+ /* we need these for headphone/lineout detection */
+ struct snd_kcontrol *headphone_ctrl;
+ struct snd_kcontrol *lineout_ctrl;
+ struct snd_kcontrol *speaker_ctrl;
+ struct snd_kcontrol *headphone_detected_ctrl;
+ struct snd_kcontrol *lineout_detected_ctrl;
+
+ struct layout_dev_ptr selfptr_headphone;
+ struct layout_dev_ptr selfptr_lineout;
+
+ u32 have_lineout_detect:1,
+ have_headphone_detect:1,
+ switch_on_headphone:1,
+ switch_on_lineout:1;
+};
+
+static LIST_HEAD(layouts_list);
+static int layouts_list_items;
+/* this can go away but only if we allow multiple cards,
+ * make the fabric handle all the card stuff, etc... */
+static struct layout_dev *layout_device;
+
+static int control_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+#define 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 detect_choice_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
+
+ switch (kcontrol->private_value) {
+ case 0:
+ ucontrol->value.integer.value[0] = ldev->switch_on_headphone;
+ break;
+ case 1:
+ ucontrol->value.integer.value[0] = ldev->switch_on_lineout;
+ break;
+ default:
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int detect_choice_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
+
+ switch (kcontrol->private_value) {
+ case 0:
+ ldev->switch_on_headphone = !!ucontrol->value.integer.value[0];
+ break;
+ case 1:
+ ldev->switch_on_lineout = !!ucontrol->value.integer.value[0];
+ break;
+ default:
+ return -ENODEV;
+ }
+ return 1;
+}
+
+static struct snd_kcontrol_new headphone_detect_choice = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphone Detect Autoswitch",
+ .info = control_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .get = detect_choice_get,
+ .put = detect_choice_put,
+ .private_value = 0,
+};
+
+static struct snd_kcontrol_new lineout_detect_choice = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line-Out Detect Autoswitch",
+ .info = control_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .get = detect_choice_get,
+ .put = detect_choice_put,
+ .private_value = 1,
+};
+
+static int detected_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
+ int v;
+
+ switch (kcontrol->private_value) {
+ case 0:
+ v = ldev->gpio.methods->get_detect(&ldev->gpio,
+ AOA_NOTIFY_HEADPHONE);
+ break;
+ case 1:
+ v = ldev->gpio.methods->get_detect(&ldev->gpio,
+ AOA_NOTIFY_LINE_OUT);
+ break;
+ default:
+ return -ENODEV;
+ }
+ ucontrol->value.integer.value[0] = v;
+ return 0;
+}
+
+static struct snd_kcontrol_new headphone_detected = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphone Detected",
+ .info = control_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .get = detected_get,
+ .private_value = 0,
+};
+
+static struct snd_kcontrol_new lineout_detected = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line-Out Detected",
+ .info = control_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .get = detected_get,
+ .private_value = 1,
+};
+
+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_notify(void *data)
+{
+ struct layout_dev_ptr *dptr = data;
+ struct layout_dev *ldev;
+ int v, update;
+ struct snd_kcontrol *detected, *c;
+ struct snd_card *card = aoa_get_card();
+
+ ldev = dptr->ptr;
+ if (data == &ldev->selfptr_headphone) {
+ v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_HEADPHONE);
+ detected = ldev->headphone_detected_ctrl;
+ update = ldev->switch_on_headphone;
+ if (update) {
+ ldev->gpio.methods->set_speakers(&ldev->gpio, !v);
+ ldev->gpio.methods->set_headphone(&ldev->gpio, v);
+ ldev->gpio.methods->set_lineout(&ldev->gpio, 0);
+ }
+ } else if (data == &ldev->selfptr_lineout) {
+ v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_LINE_OUT);
+ detected = ldev->lineout_detected_ctrl;
+ update = ldev->switch_on_lineout;
+ if (update) {
+ ldev->gpio.methods->set_speakers(&ldev->gpio, !v);
+ ldev->gpio.methods->set_headphone(&ldev->gpio, 0);
+ ldev->gpio.methods->set_lineout(&ldev->gpio, v);
+ }
+ } else
+ return;
+
+ if (detected)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &detected->id);
+ if (update) {
+ c = ldev->headphone_ctrl;
+ if (c)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
+ c = ldev->speaker_ctrl;
+ if (c)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
+ c = ldev->lineout_ctrl;
+ if (c)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
+ }
+}
+
+static void layout_attached_codec(struct aoa_codec *codec)
+{
+ struct codec_connection *cc;
+ struct snd_kcontrol *ctl;
+ int headphones, lineout;
+ struct layout_dev *ldev = layout_device;
+
+ /* need to add this codec to our codec array! */
+
+ cc = codec->fabric_data;
+
+ headphones = codec->gpio->methods->get_detect(codec->gpio,
+ AOA_NOTIFY_HEADPHONE);
+ lineout = codec->gpio->methods->get_detect(codec->gpio,
+ AOA_NOTIFY_LINE_OUT);
+
+ while (cc->connected) {
+ if (cc->connected & CC_SPEAKERS) {
+ if (headphones <= 0 && lineout <= 0)
+ ldev->gpio.methods->set_speakers(codec->gpio, 1);
+ ctl = snd_ctl_new1(&speakers_ctl, codec->gpio);
+ ldev->speaker_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ }
+ if (cc->connected & CC_HEADPHONE) {
+ if (headphones == 1)
+ ldev->gpio.methods->set_headphone(codec->gpio, 1);
+ ctl = snd_ctl_new1(&headphone_ctl, codec->gpio);
+ ldev->headphone_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ ldev->have_headphone_detect =
+ !ldev->gpio.methods
+ ->set_notify(&ldev->gpio,
+ AOA_NOTIFY_HEADPHONE,
+ layout_notify,
+ &ldev->selfptr_headphone);
+ if (ldev->have_headphone_detect) {
+ ctl = snd_ctl_new1(&headphone_detect_choice,
+ ldev);
+ aoa_snd_ctl_add(ctl);
+ ctl = snd_ctl_new1(&headphone_detected,
+ ldev);
+ ldev->headphone_detected_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ }
+ }
+ if (cc->connected & CC_LINEOUT) {
+ if (lineout == 1)
+ ldev->gpio.methods->set_lineout(codec->gpio, 1);
+ 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));
+ ldev->lineout_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ ldev->have_lineout_detect =
+ !ldev->gpio.methods
+ ->set_notify(&ldev->gpio,
+ AOA_NOTIFY_LINE_OUT,
+ layout_notify,
+ &ldev->selfptr_lineout);
+ if (ldev->have_lineout_detect) {
+ ctl = snd_ctl_new1(&lineout_detect_choice,
+ ldev);
+ if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
+ strlcpy(ctl->id.name,
+ "Headphone Detect Autoswitch",
+ sizeof(ctl->id.name));
+ aoa_snd_ctl_add(ctl);
+ ctl = snd_ctl_new1(&lineout_detected,
+ ldev);
+ if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
+ strlcpy(ctl->id.name,
+ "Headphone Detected",
+ sizeof(ctl->id.name));
+ ldev->lineout_detected_ctrl = ctl;
+ 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;
+
+ /* hm, currently we can only have one ... */
+ if (layout_device)
+ return -ENODEV;
+
+ /* 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;
+
+ layout_device = ldev;
+ ldev->sdev = sdev;
+ ldev->sound = sound;
+ ldev->layout = layout;
+ ldev->gpio.node = sound->parent;
+ ldev->gpio.methods = pmf_gpio_methods;
+ ldev->selfptr_headphone.ptr = ldev;
+ ldev->selfptr_lineout.ptr = ldev;
+ 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";
+ }
+
+ ldev->gpio.methods->init(&ldev->gpio);
+
+ 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;
+ }
+
+ use_layout(layout);
+ ldev->switch_on_headphone = 1;
+ ldev->switch_on_lineout = 1;
+ return 0;
+ outlistdel:
+ /* we won't be using these then... */
+ ldev->gpio.methods->exit(&ldev->gpio);
+ /* 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);
+ layout_device = NULL;
+ if (ldev) kfree(ldev);
+ return -ENODEV;
+}
+
+static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
+{
+ struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+ 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->set_notify(&ldev->gpio,
+ AOA_NOTIFY_HEADPHONE,
+ NULL,
+ NULL);
+ ldev->gpio.methods->set_notify(&ldev->gpio,
+ AOA_NOTIFY_LINE_OUT,
+ NULL,
+ NULL);
+
+ ldev->gpio.methods->exit(&ldev->gpio);
+ layout_device = NULL;
+ 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 0/8] snd-aoa: add snd-aoa
From: Johannes Berg @ 2006-06-01 11:58 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 and even implements headphone/line-out
detection now.
One thing that still needs to be done is fix this on the mac-mini where
apparently some things are missing from the device-tree, and on the
PowerMac7,2/7,3 where the device tree has a bug that prevents the i2sbus
module from working properly.
Other than that, it replaces snd-powermac for all layout-id machines, and
handles many more already, so once those two issues are fixed I'll also add
a patch to this patchset that removes support for any machines with a
layout-id from snd-powermac to allow snd-aoa and snd-powermac to coexist
nicely.
Any comments are appreciated.
--
^ permalink raw reply
* [RFC 8/8] snd-aoa: wire up aoa in sound/
From: Johannes Berg @ 2006-06-01 11:58 UTC (permalink / raw)
To: alsa-devel; +Cc: linuxppc-dev
In-Reply-To: <20060601115844.343214000@sipsolutions.net>
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/8] snd-aoa: add aoa header files
From: Johannes Berg @ 2006-06-01 11:58 UTC (permalink / raw)
To: alsa-devel; +Cc: linuxppc-dev
In-Reply-To: <20060601115844.343214000@sipsolutions.net>
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,81 @@
+/*
+ * 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 <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <asm/prom.h>
+
+typedef void (*notify_func_t)(void *data);
+
+enum notify_type {
+ AOA_NOTIFY_HEADPHONE,
+ AOA_NOTIFY_LINE_IN,
+ AOA_NOTIFY_LINE_OUT,
+};
+
+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);
+
+ /* use this to be notified of any events. The notification
+ * function is passed the data, and is called in process
+ * context by the use of schedule_work.
+ * The interface for it is that setting a function to NULL
+ * removes it, and they return 0 if the operation succeeded,
+ * and -EBUSY if the notification is already assigned by
+ * someone else. */
+ int (*set_notify)(struct gpio_runtime *rt,
+ enum notify_type type,
+ notify_func_t notify,
+ void *data);
+ /* returns 0 if not plugged in, 1 if plugged in
+ * or a negative error code */
+ int (*get_detect)(struct gpio_runtime *rt,
+ enum notify_type type);
+};
+
+struct gpio_notification {
+ notify_func_t notify;
+ void *data;
+ void *gpio_private;
+ struct work_struct work;
+ struct mutex mutex;
+};
+
+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;
+ struct gpio_notification headphone_notify;
+ struct gpio_notification line_in_notify;
+ struct gpio_notification line_out_notify;
+};
+
+#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 3/8] snd-aoa: add soundbus
From: Johannes Berg @ 2006-06-01 11:58 UTC (permalink / raw)
To: alsa-devel; +Cc: linuxppc-dev
In-Reply-To: <20060601115844.343214000@sipsolutions.net>
This patch adds the mostly generic soundbus that will be used for all the
sound busses Apple machines have and provides just the hooks for automatic
driver loading etc.
--- /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/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
+};
--- /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);
--
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox