LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: Commits added to powerpc.git master and powerpc-next branches
From: Jochen Friedrich @ 2008-07-02 12:35 UTC (permalink / raw)
  To: Kumar Gala; +Cc: linuxppc-dev, Paul Mackerras
In-Reply-To: <AF0B4FF7-06C8-4AF8-B1C2-23697326BDDB@kernel.crashing.org>

Hi Kumar,

>> any chance to get this one into 2.6.27?
>>
>> [POWERPC] Add i2c pins to dts and board setup
>>  http://patchwork.ozlabs.org/linuxppc/patch?person=1023&id=18603
> 
> Will we always want defconfig updates to enable the driver?

Good question. Maybe not, as the i2c bus seems to be unused on these
boards unless someone connects some i2c devices.

I'll resubmit just the booting-without-of part then.

Thanks,
Jochen

^ permalink raw reply

* [git pull] Please pull powerpc.git merge branch
From: Paul Mackerras @ 2008-07-02 12:41 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linuxppc-dev, akpm, linux-kernel

Linus,

Please pull from the 'merge' branch of

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

to get two more small bug-fixes for powerpc, as listed below.

Thanks,
Paul.

 arch/powerpc/boot/Makefile          |    3 ++-
 arch/powerpc/kernel/legacy_serial.c |    5 +++++
 2 files changed, 7 insertions(+), 1 deletions(-)

John Linn (2):
      powerpc/bootwrapper: update for initrd with simpleImage
      powerpc/legacy_serial: Bail if reg-offset/shift properties are present

^ permalink raw reply

* Re: [PATCH]: [MPC5200] (v2) Add ATA DMA support
From: Tim Yamin @ 2008-07-02 12:48 UTC (permalink / raw)
  To: Grant Likely; +Cc: linuxppc-dev
In-Reply-To: <20080701234943.GA16391@secretlab.ca>

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

Hi Grant,

Thanks for the feedback. New version is attached.

> Is this a bug fix?  If so, please put it into a separate patch.

I suppose so, yes. If Ethernet has higher priority than ATA, you can
get a deadlock if you try and download a large file over a LAN to
disk, for example. But given that nothing other than this patch uses
BestComm for ATA do you have any specific reason to split it out into
another patch?

> Good, it can be turned off.  Do you think there is any risk to existing
> ATA users with this patch applied if this is turned off?

With the new version, the only risk if this is turned off is the
change I've made to bestcomm.h. Other than that, you should get no
risk because none of the new
code is executed (mwdma_mask and udma_mask are set to 0 if the option
is turned off).

> Can you find any way to avoid this?  This could be a performance drain.

Previous code had this, so I kept it. Things do seem to work OK
without it, so I've removed it...

> Is there any way to turn on/off DMA at runtime instead of CONFIG time?

You could use libata.dma=0 to force DMA off even if it's enabled at CONFIG time.

>>       priv->ipb_period = 1000000000 / (ipb_freq / 1000);
>>       priv->ata_regs = ata_regs;
>> +     priv->ata_regs_pa = (struct mpc52xx_ata __iomem *) res_mem.start;
>
> I'm not fond of this.  First off, it is *not* __iomem.  It is physical
> address.  It would be better to use the offset_of macro to add an offset
> to the physical base address.  Doing it this way forces you to cast and
> sidestep the compile time checks for incorrect dereferences.

I'm afraid I'm not quite sure what you have in mind here, could you
please provide a pointer?

Thanks,

Tim

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 1050-mpc5200-add-ATA-DMA.patch --]
[-- Type: text/x-patch; name=1050-mpc5200-add-ATA-DMA.patch, Size: 22904 bytes --]

This patch adds MDMA/UDMA support (using BestComm for DMA) on the MPC5200
platform.

Based heavily on previous work by Freescale (Bernard Kuhn, John Rigby)
and Domen Puncer.

Using a SanDisk Extreme IV CF card I get read speeds of approximately
26.70 MB/sec.

The BestComm ATA task priority was changed to maximum in bestcomm_priv.h;
this fixes a deadlock issue I was experiencing when heavy DMA was
occuring on both the ATA and Ethernet BestComm tasks, e.g. when
downloading a large file over a LAN to disk.

There's also what I believe to be a hardware bug if you have high levels
of BestComm ATA DMA activity along with heavy LocalPlus Bus activity;
the address bus seems to sometimes get corrupted with ATA commands while
the LocalPlus Bus operation is still active (i.e. Chip Select is asserted).

I've asked Freescale about this but have not received a reply yet -- if
anybody from Freescale has any ideas please contact me; I can supply some
analyzer traces if needed. Therefore, for now, do not enable DMA if you
need reliable LocalPlus Bus unless you do a fixup in your driver as
follows:

Locking example:

        while (test_and_set_bit(0, &pata_mpc52xx_ata_dma_lock) != 0)
        {
                struct bcom_task_2 *tsk = pata_mpc52xx_ata_dma_task;

                if(bcom_buffer_done_2(tsk))
                        return 1;
        }

	return 0;

(Save the return value to `flags`)

Unlocking example:

        if(flags == 0)
                clear_bit(0, &pata_mpc52xx_ata_dma_lock);

Comments and testing would of course be very welcome.

Thanks,

Signed-off-by: Tim Yamin <plasm@roo.me.uk>

diff -urp linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/ata.h linux-2.6.26-rc6-ata/arch/powerpc/sysdev/bestcomm/ata.h
--- linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/ata.h	2008-04-17 03:49:44.000000000 +0100
+++ linux-2.6.26-rc6-ata/arch/powerpc/sysdev/bestcomm/ata.h	2008-07-02 12:48:14.000000000 +0100
@@ -16,8 +16,8 @@
 
 struct bcom_ata_bd {
 	u32	status;
-	u32	dst_pa;
 	u32	src_pa;
+	u32	dst_pa;
 };
 
 extern struct bcom_task *
diff -urp linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm.c linux-2.6.26-rc6-ata/arch/powerpc/sysdev/bestcomm/bestcomm.c
--- linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm.c	2008-04-17 03:49:44.000000000 +0100
+++ linux-2.6.26-rc6-ata/arch/powerpc/sysdev/bestcomm/bestcomm.c	2008-07-02 12:48:14.000000000 +0100
@@ -330,11 +330,16 @@ bcom_engine_init(void)
 	/* Init 'always' initiator */
 	out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS);
 
+	/* If ATA DMA is enabled, always turn prefetch off (it breaks things) */
+#ifndef CONFIG_PATA_MPC52xx_DMA
 	/* Disable COMM Bus Prefetch on the original 5200; it's broken */
 	if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR) {
+#endif
 		regval = in_be16(&bcom_eng->regs->PtdCntrl);
 		out_be16(&bcom_eng->regs->PtdCntrl,  regval | 1);
+#ifndef CONFIG_PATA_MPC52xx_DMA
 	}
+#endif
 
 	/* Init lock */
 	spin_lock_init(&bcom_eng->lock);
diff -urp linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm.h linux-2.6.26-rc6-ata/arch/powerpc/sysdev/bestcomm/bestcomm.h
--- linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm.h	2008-04-17 03:49:44.000000000 +0100
+++ linux-2.6.26-rc6-ata/arch/powerpc/sysdev/bestcomm/bestcomm.h	2008-07-02 12:48:14.000000000 +0100
@@ -140,15 +140,29 @@ bcom_queue_full(struct bcom_task *tsk)
 }
 
 /**
+ * bcom_get_bd - Get a BD from the queue
+ * @tsk: The BestComm task structure
+ * index: Index of the BD to fetch
+ */
+static inline struct bcom_bd
+*bcom_get_bd(struct bcom_task *tsk, unsigned int index)
+{
+	return ((void *) tsk->bd) + (index * tsk->bd_size);
+}
+
+/**
  * bcom_buffer_done - Checks if a BestComm 
  * @tsk: The BestComm task structure
  */
 static inline int
 bcom_buffer_done(struct bcom_task *tsk)
 {
+	struct bcom_bd *bd;
 	if (bcom_queue_empty(tsk))
 		return 0;
-	return !(tsk->bd[tsk->outdex].status & BCOM_BD_READY);
+
+	bd = bcom_get_bd(tsk, tsk->outdex);
+	return !(bd->status & BCOM_BD_READY);
 }
 
 /**
@@ -160,16 +174,21 @@ bcom_buffer_done(struct bcom_task *tsk)
 static inline struct bcom_bd *
 bcom_prepare_next_buffer(struct bcom_task *tsk)
 {
-	tsk->bd[tsk->index].status = 0;	/* cleanup last status */
-	return &tsk->bd[tsk->index];
+	struct bcom_bd *bd;
+
+	bd = bcom_get_bd(tsk, tsk->index);
+	bd->status = 0;	/* cleanup last status */
+	return bd;
 }
 
 static inline void
 bcom_submit_next_buffer(struct bcom_task *tsk, void *cookie)
 {
+	struct bcom_bd *bd = bcom_get_bd(tsk, tsk->index);
+
 	tsk->cookie[tsk->index] = cookie;
 	mb();	/* ensure the bd is really up-to-date */
-	tsk->bd[tsk->index].status |= BCOM_BD_READY;
+	bd->status |= BCOM_BD_READY;
 	tsk->index = _bcom_next_index(tsk);
 	if (tsk->flags & BCOM_FLAGS_ENABLE_TASK)
 		bcom_enable(tsk);
@@ -179,10 +198,12 @@ static inline void *
 bcom_retrieve_buffer(struct bcom_task *tsk, u32 *p_status, struct bcom_bd **p_bd)
 {
 	void *cookie = tsk->cookie[tsk->outdex];
+	struct bcom_bd *bd = bcom_get_bd(tsk, tsk->outdex);
+
 	if (p_status)
-		*p_status = tsk->bd[tsk->outdex].status;
+		*p_status = bd->status;
 	if (p_bd)
-		*p_bd = &tsk->bd[tsk->outdex];
+		*p_bd = bd;
 	tsk->outdex = _bcom_next_outdex(tsk);
 	return cookie;
 }
diff -urp linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h linux-2.6.26-rc6-ata/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
--- linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h	2008-04-17 03:49:44.000000000 +0100
+++ linux-2.6.26-rc6-ata/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h	2008-07-02 12:48:14.000000000 +0100
@@ -198,8 +198,8 @@ struct bcom_task_header {
 #define BCOM_IPR_SCTMR_1	2
 #define BCOM_IPR_FEC_RX		6
 #define BCOM_IPR_FEC_TX		5
-#define BCOM_IPR_ATA_RX		4
-#define BCOM_IPR_ATA_TX		3
+#define BCOM_IPR_ATA_RX		7
+#define BCOM_IPR_ATA_TX		7
 #define BCOM_IPR_SCPCI_RX	2
 #define BCOM_IPR_SCPCI_TX	2
 #define BCOM_IPR_PSC3_RX	2
diff -urp linux-2.6.26-rc6/drivers/ata/Kconfig linux-2.6.26-rc6-ata/drivers/ata/Kconfig
--- linux-2.6.26-rc6/drivers/ata/Kconfig	2008-07-02 12:51:27.000000000 +0100
+++ linux-2.6.26-rc6-ata/drivers/ata/Kconfig	2008-07-02 12:47:15.000000000 +0100
@@ -462,6 +462,15 @@ config PATA_MPC52xx
 
 	  If unsure, say N.
 
+config PATA_MPC52xx_DMA
+	tristate "Freescale MPC52xx SoC internal IDE DMA"
+	depends on PATA_MPC52xx
+	help
+	  This option enables support for DMA on the MPC52xx SoC PATA
+	  controller.
+
+	  If unsure, say N.
+
 config PATA_MPIIX
 	tristate "Intel PATA MPIIX support"
 	depends on PCI
diff -urp linux-2.6.26-rc6/drivers/ata/pata_mpc52xx.c linux-2.6.26-rc6-ata/drivers/ata/pata_mpc52xx.c
--- linux-2.6.26-rc6/drivers/ata/pata_mpc52xx.c	2008-07-02 12:51:27.000000000 +0100
+++ linux-2.6.26-rc6-ata/drivers/ata/pata_mpc52xx.c	2008-07-02 12:47:14.000000000 +0100
@@ -6,6 +6,9 @@
  * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
  * Copyright (C) 2003 Mipsys - Benjamin Herrenschmidt
  *
+ * UDMA support based on patches by Freescale (Bernard Kuhn, John Rigby),
+ * Domen Puncer and Tim Yamin.
+ *
  * This file is licensed under the terms of the GNU General Public License
  * version 2. This program is licensed "as is" without any warranty of any
  * kind, whether express or implied.
@@ -17,28 +20,47 @@
 #include <linux/delay.h>
 #include <linux/libata.h>
 
+#include <asm/cacheflush.h>
 #include <asm/types.h>
 #include <asm/prom.h>
 #include <asm/of_platform.h>
 #include <asm/mpc52xx.h>
 
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/bestcomm_priv.h>
+#include <sysdev/bestcomm/ata.h>
 
 #define DRV_NAME	"mpc52xx_ata"
 #define DRV_VERSION	"0.1.2"
 
-
 /* Private structures used by the driver */
 struct mpc52xx_ata_timings {
 	u32	pio1;
 	u32	pio2;
+	u32	mdma1;
+	u32	mdma2;
+	u32	udma1;
+	u32	udma2;
+	u32	udma3;
+	u32	udma4;
+	u32	udma5;
+	int	using_udma;
 };
 
 struct mpc52xx_ata_priv {
 	unsigned int			ipb_period;
 	struct mpc52xx_ata __iomem *	ata_regs;
+	struct mpc52xx_ata		*ata_regs_pa;
 	int				ata_irq;
 	struct mpc52xx_ata_timings	timings[2];
 	int				csel;
+
+	/* DMA */
+	struct bcom_task		*dmatsk;
+	const struct udmaspec		*udmaspec;
+	const struct mdmaspec		*mdmaspec;
+	int 				mpc52xx_ata_dma_last_write;
+	int				waiting_for_dma;
 };
 
 
@@ -53,6 +75,95 @@ static const int ataspec_ta[5]    = { 35
 
 #define CALC_CLKCYC(c,v) ((((v)+(c)-1)/(c)))
 
+/* ======================================================================== */
+
+/* ATAPI-4 MDMA specs (in clocks) */
+struct mdmaspec {
+	u32 t0M;
+	u32 td;
+	u32 th;
+	u32 tj;
+	u32 tkw;
+	u32 tm;
+	u32 tn;
+};
+
+static const struct mdmaspec mdmaspec66[3] = {
+	{ .t0M = 32, .td = 15, .th = 2, .tj = 2, .tkw = 15, .tm = 4, .tn = 1 },
+	{ .t0M = 10, .td = 6, .th = 1, .tj = 1, .tkw = 4, .tm = 2, .tn = 1 },
+	{ .t0M = 8, .td = 5, .th = 1, .tj = 1, .tkw = 2, .tm = 2, .tn = 1 },
+};
+
+static const struct mdmaspec mdmaspec132[3] = {
+	{ .t0M = 64, .td = 29, .th = 3, .tj = 3, .tkw = 29, .tm = 7, .tn = 2 },
+	{ .t0M = 20, .td = 11, .th = 2, .tj = 1, .tkw = 7, .tm = 4, .tn = 1 },
+	{ .t0M = 16, .td = 10, .th = 2, .tj = 1, .tkw = 4, .tm = 4, .tn = 1 },
+};
+
+/* ATAPI-4 UDMA specs (in clocks) */
+struct udmaspec {
+	u32 tcyc;
+	u32 t2cyc;
+	u32 tds;
+	u32 tdh;
+	u32 tdvs;
+	u32 tdvh;
+	u32 tfs;
+	u32 tli;
+	u32 tmli;
+	u32 taz;
+	u32 tzah;
+	u32 tenv;
+	u32 tsr;
+	u32 trfs;
+	u32 trp;
+	u32 tack;
+	u32 tss;
+};
+
+static const struct udmaspec udmaspec66[6] = {
+	{ .tcyc = 8, .t2cyc = 16, .tds = 1, .tdh = 1, .tdvs = 5, .tdvh = 1,
+	  .tfs = 16, .tli = 10, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
+	  .tsr = 3, .trfs = 5, .trp = 11, .tack = 2, .tss = 4 },
+	{ .tcyc = 5, .t2cyc = 11, .tds = 1, .tdh = 1, .tdvs = 4, .tdvh = 1,
+	  .tfs = 14, .tli = 10, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
+	  .tsr = 2, .trfs = 5, .trp = 9, .tack = 2, .tss = 4 },
+	{ .tcyc = 4, .t2cyc = 8, .tds = 1, .tdh = 1, .tdvs = 3, .tdvh = 1,
+	  .tfs = 12, .tli = 10, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
+	  .tsr = 2, .trfs = 4, .trp = 7, .tack = 2, .tss = 4 },
+	{ .tcyc = 3, .t2cyc = 6, .tds = 1, .tdh = 1, .tdvs = 2, .tdvh = 1,
+	  .tfs = 9, .tli = 7, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
+	  .tsr = 2, .trfs = 4, .trp = 7, .tack = 2, .tss = 4 },
+	{ .tcyc = 2, .t2cyc = 4, .tds = 1, .tdh = 1, .tdvs = 1, .tdvh = 1,
+	  .tfs = 8, .tli = 8, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
+	  .tsr = 2, .trfs = 4, .trp = 7, .tack = 2, .tss = 4 },
+	{ .tcyc = 2, .t2cyc = 2, .tds = 1, .tdh = 1, .tdvs = 1, .tdvh = 1,
+	  .tfs = 6, .tli = 5, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
+	  .tsr = 2, .trfs = 4, .trp = 6, .tack = 2, .tss = 4 },
+};
+
+static const struct udmaspec udmaspec132[6] = {
+	{ .tcyc = 15, .t2cyc = 31, .tds = 2, .tdh = 1, .tdvs = 10, .tdvh = 1,
+	  .tfs = 30, .tli = 20, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
+	  .tsr = 7, .trfs = 10, .trp = 22, .tack = 3, .tss = 7 },
+	{ .tcyc = 10, .t2cyc = 21, .tds = 2, .tdh = 1, .tdvs = 7, .tdvh = 1,
+	  .tfs = 27, .tli = 20, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
+	  .tsr = 4, .trfs = 10, .trp = 17, .tack = 3, .tss = 7 },
+	{ .tcyc = 6, .t2cyc = 12, .tds = 1, .tdh = 1, .tdvs = 5, .tdvh = 1,
+	  .tfs = 23, .tli = 20, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
+	  .tsr = 3, .trfs = 8, .trp = 14, .tack = 3, .tss = 7 },
+	{ .tcyc = 7, .t2cyc = 12, .tds = 1, .tdh = 1, .tdvs = 3, .tdvh = 1,
+	  .tfs = 15, .tli = 13, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
+	  .tsr = 3, .trfs = 8, .trp = 14, .tack = 3, .tss = 7 },
+	{ .tcyc = 2, .t2cyc = 5, .tds = 0, .tdh = 0, .tdvs = 1, .tdvh = 1,
+	  .tfs = 16, .tli = 14, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
+	  .tsr = 2, .trfs = 7, .trp = 13, .tack = 2, .tss = 6 },
+	{ .tcyc = 3, .t2cyc = 6, .tds = 1, .tdh = 1, .tdvs = 1, .tdvh = 1,
+	  .tfs = 12, .tli = 10, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
+	  .tsr = 3, .trfs = 7, .trp = 12, .tack = 3, .tss = 7 },
+};
+
+/* ======================================================================== */
 
 /* Bit definitions inside the registers */
 #define MPC52xx_ATA_HOSTCONF_SMR	0x80000000UL /* State machine reset */
@@ -66,6 +165,7 @@ static const int ataspec_ta[5]    = { 35
 #define MPC52xx_ATA_HOSTSTAT_WERR	0x01000000UL /* Write Error */
 
 #define MPC52xx_ATA_FIFOSTAT_EMPTY	0x01 /* FIFO Empty */
+#define MPC52xx_ATA_FIFOSTAT_ERROR	0x40 /* FIFO Error */
 
 #define MPC52xx_ATA_DMAMODE_WRITE	0x01 /* Write DMA */
 #define MPC52xx_ATA_DMAMODE_READ	0x02 /* Read DMA */
@@ -75,6 +175,8 @@ static const int ataspec_ta[5]    = { 35
 #define MPC52xx_ATA_DMAMODE_FR		0x20 /* FIFO Reset */
 #define MPC52xx_ATA_DMAMODE_HUT		0x40 /* Host UDMA burst terminate */
 
+#define MAX_DMA_BUFFERS 128
+#define MAX_DMA_BUFFER_SIZE 0x20000u
 
 /* Structure of the hardware registers */
 struct mpc52xx_ata {
@@ -133,6 +235,9 @@ struct mpc52xx_ata {
 	u8  reserved21[2];
 };
 
+/* BestComm locking */
+unsigned long pata_mpc52xx_ata_dma_lock;
+struct bcom_task *pata_mpc52xx_ata_dma_task;
 
 /* ======================================================================== */
 /* Aux fns                                                                  */
@@ -165,6 +270,41 @@ mpc52xx_ata_compute_pio_timings(struct m
 	return 0;
 }
 
+static int
+mpc52xx_ata_compute_mdma_timings(struct mpc52xx_ata_priv *priv, int dev, int speed)
+{
+	struct mpc52xx_ata_timings *timing = &priv->timings[dev];
+	const struct mdmaspec *s = &priv->mdmaspec[speed];
+
+	if (speed < 0 || speed > 2)
+		return -EINVAL;
+
+	timing->mdma1 = (s->t0M << 24) | (s->td << 16) | (s->tkw << 8) | (s->tm);
+	timing->mdma2 = (s->th << 24) | (s->tj << 16) | (s->tn << 8);
+	timing->using_udma = 0;
+
+	return 0;
+}
+
+static int
+mpc52xx_ata_compute_udma_timings(struct mpc52xx_ata_priv *priv, int dev, int speed)
+{
+	struct mpc52xx_ata_timings *timing = &priv->timings[dev];
+	const struct udmaspec *s = &priv->udmaspec[speed];
+
+	if (speed < 0 || speed > 2)
+		return -EINVAL;
+
+	timing->udma1 = (s->t2cyc << 24) | (s->tcyc << 16) | (s->tds << 8) | (s->tdh);
+	timing->udma2 = (s->tdvs << 24) | (s->tdvh << 16) | (s->tfs << 8) | (s->tli);
+	timing->udma3 = (s->tmli << 24) | (s->taz << 16) | (s->tenv << 8) | (s->tsr);
+	timing->udma4 = (s->tss << 24) | (s->trfs << 16) | (s->trp << 8) | (s->tack);
+	timing->udma5 = (s->tzah << 24);
+	timing->using_udma = 1;
+
+	return 0;
+}
+
 static void
 mpc52xx_ata_apply_timings(struct mpc52xx_ata_priv *priv, int device)
 {
@@ -173,14 +313,13 @@ mpc52xx_ata_apply_timings(struct mpc52xx
 
 	out_be32(&regs->pio1,  timing->pio1);
 	out_be32(&regs->pio2,  timing->pio2);
-	out_be32(&regs->mdma1, 0);
-	out_be32(&regs->mdma2, 0);
-	out_be32(&regs->udma1, 0);
-	out_be32(&regs->udma2, 0);
-	out_be32(&regs->udma3, 0);
-	out_be32(&regs->udma4, 0);
-	out_be32(&regs->udma5, 0);
-
+	out_be32(&regs->mdma1, timing->mdma1);
+	out_be32(&regs->mdma2, timing->mdma2);
+	out_be32(&regs->udma1, timing->udma1);
+	out_be32(&regs->udma2, timing->udma2);
+	out_be32(&regs->udma3, timing->udma3);
+	out_be32(&regs->udma4, timing->udma4);
+	out_be32(&regs->udma5, timing->udma5);
 	priv->csel = device;
 }
 
@@ -245,6 +384,29 @@ mpc52xx_ata_set_piomode(struct ata_port 
 	mpc52xx_ata_apply_timings(priv, adev->devno);
 }
 static void
+mpc52xx_ata_set_dmamode(struct ata_port *ap, struct ata_device *adev)
+{
+	struct mpc52xx_ata_priv *priv = ap->host->private_data;
+	int rv;
+
+	if (adev->dma_mode >= XFER_UDMA_0) {
+		int dma = adev->dma_mode - XFER_UDMA_0;
+		rv = mpc52xx_ata_compute_udma_timings(priv, adev->devno, dma);
+	} else {
+		int dma = adev->dma_mode - XFER_MW_DMA_0;
+		rv = mpc52xx_ata_compute_mdma_timings(priv, adev->devno, dma);
+	}
+
+	if (rv) {
+		printk(KERN_ERR DRV_NAME
+			": Trying to select invalid DMA mode %d\n",
+			adev->dma_mode);
+		return;
+	}
+
+	mpc52xx_ata_apply_timings(priv, adev->devno);
+}
+static void
 mpc52xx_ata_dev_select(struct ata_port *ap, unsigned int device)
 {
 	struct mpc52xx_ata_priv *priv = ap->host->private_data;
@@ -255,16 +416,190 @@ mpc52xx_ata_dev_select(struct ata_port *
 	ata_sff_dev_select(ap,device);
 }
 
+static int
+mpc52xx_ata_build_dmatable(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct mpc52xx_ata_priv *priv = ap->host->private_data;
+	struct mpc52xx_ata *regs_pa = priv->ata_regs_pa;
+	unsigned int read = !(qc->tf.flags & ATA_TFLAG_WRITE), si;
+	struct scatterlist *sg;
+	int count = 0;
+
+	if (read)
+		bcom_ata_rx_prepare(priv->dmatsk);
+	else
+		bcom_ata_tx_prepare(priv->dmatsk);
+
+	for_each_sg(qc->sg, sg, qc->n_elem, si) {
+		dma_addr_t cur_addr = sg_dma_address(sg);
+		u32 cur_len = sg_dma_len(sg);
+
+		while (cur_len) {
+			unsigned int tc = min(cur_len, MAX_DMA_BUFFER_SIZE);
+			struct bcom_ata_bd *bd = (struct bcom_ata_bd *) bcom_prepare_next_buffer(priv->dmatsk);
+
+			if (read) {
+				bd->status = tc;
+				bd->src_pa = (__force u32) &regs_pa->fifo_data;
+				bd->dst_pa = (__force u32) cur_addr;
+			} else {
+				bd->status = tc;
+				bd->src_pa = (__force u32) cur_addr;
+				bd->dst_pa = (__force u32) &regs_pa->fifo_data;
+			}
+
+			bcom_submit_next_buffer(priv->dmatsk, NULL);
+
+			cur_addr += tc;
+			cur_len -= tc;
+			count++;
+
+			if (count > MAX_DMA_BUFFERS) {
+				printk(KERN_ALERT "%s: %i dma table"
+					"too small\n", __func__, __LINE__);
+				goto use_pio_instead;
+			}
+		}
+	}
+	return 1;
+
+use_pio_instead:
+	bcom_ata_reset_bd(priv->dmatsk);
+	return 0;
+}
+
+static void
+mpc52xx_bmdma_setup(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct mpc52xx_ata_priv *priv = ap->host->private_data;
+	struct mpc52xx_ata __iomem *regs = priv->ata_regs;
+
+	unsigned int read = !(qc->tf.flags & ATA_TFLAG_WRITE);
+	u8 dma_mode;
+
+	if (!mpc52xx_ata_build_dmatable(qc))
+		printk(KERN_ALERT "%s: %i, return 1?\n", __func__, __LINE__);
+
+	/* Check FIFO is OK... */
+	if(in_8(&priv->ata_regs->fifo_status) & MPC52xx_ATA_FIFOSTAT_ERROR)
+		printk(KERN_ALERT "%s: FIFO error detected: 0x%02x!\n",
+			__func__, in_8(&priv->ata_regs->fifo_status));
+
+	if (read) {
+		dma_mode = MPC52xx_ATA_DMAMODE_IE | MPC52xx_ATA_DMAMODE_READ |
+				MPC52xx_ATA_DMAMODE_FE;
+
+		/* Setup FIFO if direction changed */
+		if (priv->mpc52xx_ata_dma_last_write != 0) {
+			priv->mpc52xx_ata_dma_last_write = 0;
+
+			/* Configure FIFO with granularity to 7 */
+			out_8(&regs->fifo_control, 7);
+			out_be16(&regs->fifo_alarm, 128);
+
+			/* Set FIFO Reset bit (FR) */
+			out_8(&regs->dma_mode, MPC52xx_ATA_DMAMODE_FR);
+		}
+	} else {
+		dma_mode = MPC52xx_ATA_DMAMODE_IE | MPC52xx_ATA_DMAMODE_WRITE;
+
+		/* Setup FIFO if direction changed */
+		if (priv->mpc52xx_ata_dma_last_write != 1) {
+			priv->mpc52xx_ata_dma_last_write = 1;
+
+			/* Configure FIFO with granularity to 4 */
+			out_8(&regs->fifo_control, 4);
+			out_be16(&regs->fifo_alarm, 128);
+		}
+	}
+
+	if (priv->timings[qc->dev->devno].using_udma)
+		dma_mode |= MPC52xx_ATA_DMAMODE_UDMA;
+
+	out_8(&regs->dma_mode, dma_mode);
+	priv->waiting_for_dma = ATA_DMA_ACTIVE;
+
+	ata_wait_idle(ap);
+	ap->ops->sff_exec_command(ap, &qc->tf);
+}
+
+static void
+mpc52xx_bmdma_start(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct mpc52xx_ata_priv *priv = ap->host->private_data;
+
+	/* LocalBus lock */
+	while (test_and_set_bit(0, &pata_mpc52xx_ata_dma_lock) != 0)
+		;
+
+	bcom_set_task_auto_start(priv->dmatsk->tasknum, priv->dmatsk->tasknum);
+	bcom_enable(priv->dmatsk);
+}
+
+static void
+mpc52xx_bmdma_stop(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct mpc52xx_ata_priv *priv = ap->host->private_data;
+
+	bcom_disable(priv->dmatsk);
+	bcom_ata_reset_bd(priv->dmatsk);
+
+	/* LocalBus unlock*/
+	clear_bit(0, &pata_mpc52xx_ata_dma_lock);
+
+	priv->waiting_for_dma = 0;
+
+	/* Check FIFO is OK... */
+	if(in_8(&priv->ata_regs->fifo_status) & MPC52xx_ATA_FIFOSTAT_ERROR)
+		printk(KERN_ALERT "%s: FIFO error detected: 0x%02x!\n",
+			__func__, in_8(&priv->ata_regs->fifo_status));
+}
+
+static u8
+mpc52xx_bmdma_status(struct ata_port *ap)
+{
+	struct mpc52xx_ata_priv *priv = ap->host->private_data;
+
+	/* Check FIFO is OK... */
+	if(in_8(&priv->ata_regs->fifo_status) & MPC52xx_ATA_FIFOSTAT_ERROR) {
+		printk(KERN_ALERT "%s: FIFO error detected: 0x%02x!\n",
+			__func__, in_8(&priv->ata_regs->fifo_status));
+		return priv->waiting_for_dma | ATA_DMA_ERR;
+	}
+
+	return priv->waiting_for_dma;
+}
+
+static irqreturn_t
+mpc52xx_ata_task_irq(int irq, void *vpriv)
+{
+	struct mpc52xx_ata_priv *priv = vpriv;
+	priv->waiting_for_dma |= ATA_DMA_INTR;
+
+	return IRQ_HANDLED;
+}
+
 static struct scsi_host_template mpc52xx_ata_sht = {
 	ATA_PIO_SHT(DRV_NAME),
 };
 
 static struct ata_port_operations mpc52xx_ata_port_ops = {
 	.inherits		= &ata_sff_port_ops,
-	.sff_dev_select		= mpc52xx_ata_dev_select,
-	.cable_detect		= ata_cable_40wire,
+
 	.set_piomode		= mpc52xx_ata_set_piomode,
-	.post_internal_cmd	= ATA_OP_NULL,
+	.set_dmamode		= mpc52xx_ata_set_dmamode,
+	.sff_dev_select		= mpc52xx_ata_dev_select,
+
+	.bmdma_setup		= mpc52xx_bmdma_setup,
+	.bmdma_start		= mpc52xx_bmdma_start,
+	.bmdma_stop		= mpc52xx_bmdma_stop,
+	.bmdma_status		= mpc52xx_bmdma_status,
+
+	.qc_prep		= ata_noop_qc_prep,
 };
 
 static int __devinit
@@ -281,9 +615,14 @@ mpc52xx_ata_init_one(struct device *dev,
 
 	ap = host->ports[0];
 	ap->flags		|= ATA_FLAG_SLAVE_POSS;
-	ap->pio_mask		= 0x1f;	/* Up to PIO4 */
-	ap->mwdma_mask		= 0x00;	/* No MWDMA   */
-	ap->udma_mask		= 0x00;	/* No UDMA    */
+	ap->pio_mask		= ATA_PIO4;	/* Up to PIO4 */
+#ifdef CONFIG_PATA_MPC52xx_DMA
+	ap->mwdma_mask		= ATA_MWDMA2;	/* Up to MWDMA2 */
+	ap->udma_mask		= ATA_UDMA2;	/* Up to UDMA2 */
+#else
+	ap->mwdma_mask		= 0x00;		/* No MWDMA */
+	ap->udma_mask		= 0x00;		/* No UDMA */
+#endif
 	ap->ops			= &mpc52xx_ata_port_ops;
 	host->private_data	= priv;
 
@@ -333,7 +672,7 @@ mpc52xx_ata_probe(struct of_device *op, 
 	int ata_irq;
 	struct mpc52xx_ata __iomem *ata_regs;
 	struct mpc52xx_ata_priv *priv;
-	int rv;
+	int rv, ret, task_irq;
 
 	/* Get ipb frequency */
 	ipb_freq = mpc52xx_find_ipb_freq(op->node);
@@ -389,8 +728,34 @@ mpc52xx_ata_probe(struct of_device *op, 
 
 	priv->ipb_period = 1000000000 / (ipb_freq / 1000);
 	priv->ata_regs = ata_regs;
+	priv->ata_regs_pa = (struct mpc52xx_ata *) res_mem.start;
 	priv->ata_irq = ata_irq;
 	priv->csel = -1;
+	priv->mpc52xx_ata_dma_last_write = -1;
+
+	if (ipb_freq/1000000 == 66) {
+		priv->mdmaspec = mdmaspec66;
+		priv->udmaspec = udmaspec66;
+	} else {
+		priv->mdmaspec = mdmaspec132;
+		priv->udmaspec = udmaspec132;
+	}
+
+	pata_mpc52xx_ata_dma_lock = 0;
+	priv->dmatsk = bcom_ata_init(MAX_DMA_BUFFERS, MAX_DMA_BUFFER_SIZE);
+	pata_mpc52xx_ata_dma_task = priv->dmatsk;
+	if (!priv->dmatsk) {
+		printk(KERN_ALERT "%s: %i\n", __func__, __LINE__);
+		rv = -ENOMEM;
+		goto err;
+	}
+
+	task_irq = bcom_get_task_irq(priv->dmatsk);
+	ret = request_irq(task_irq, &mpc52xx_ata_task_irq, IRQF_DISABLED,
+				"ATA task", priv);
+	if (ret)
+		printk(KERN_ALERT "%s: request_irq failed with: "
+					"%i\n", __func__, ret);
 
 	/* Init the hw */
 	rv = mpc52xx_ata_hw_init(priv);

^ permalink raw reply

* powerpc: Remove old dump_task_* functions
From: Michael Neuling @ 2008-07-02 12:51 UTC (permalink / raw)
  To: paulus; +Cc: linuxppc-dev, Roland McGrath

Since Roland's ptrace cleanup starting
f65255e8d51ecbc6c9eef20d39e0377d19b658ca, the dump_task_* functions are
no longer being used.

Signed-off-by: Michael Neuling <mikey@neuling.org>
---
Paulus: please add to you 2.6.27 tree.  

 arch/powerpc/kernel/process.c |   83 ------------------------------------------
 include/asm-powerpc/elf.h     |   20 ----------
 2 files changed, 103 deletions(-)

Index: linux-2.6-ozlabs/arch/powerpc/kernel/process.c
===================================================================
--- linux-2.6-ozlabs.orig/arch/powerpc/kernel/process.c
+++ linux-2.6-ozlabs/arch/powerpc/kernel/process.c
@@ -105,29 +105,6 @@ void enable_kernel_fp(void)
 }
 EXPORT_SYMBOL(enable_kernel_fp);
 
-int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs)
-{
-#ifdef CONFIG_VSX
-	int i;
-	elf_fpreg_t *reg;
-#endif
-
-	if (!tsk->thread.regs)
-		return 0;
-	flush_fp_to_thread(current);
-
-#ifdef CONFIG_VSX
-	reg = (elf_fpreg_t *)fpregs;
-	for (i = 0; i < ELF_NFPREG - 1; i++, reg++)
-		*reg = tsk->thread.TS_FPR(i);
-	memcpy(reg, &tsk->thread.fpscr, sizeof(elf_fpreg_t));
-#else
-	memcpy(fpregs, &tsk->thread.TS_FPR(0), sizeof(*fpregs));
-#endif
-
-	return 1;
-}
-
 #ifdef CONFIG_ALTIVEC
 void enable_kernel_altivec(void)
 {
@@ -161,35 +138,6 @@ void flush_altivec_to_thread(struct task
 		preempt_enable();
 	}
 }
-
-int dump_task_altivec(struct task_struct *tsk, elf_vrregset_t *vrregs)
-{
-	/* ELF_NVRREG includes the VSCR and VRSAVE which we need to save
-	 * separately, see below */
-	const int nregs = ELF_NVRREG - 2;
-	elf_vrreg_t *reg;
-	u32 *dest;
-
-	if (tsk == current)
-		flush_altivec_to_thread(tsk);
-
-	reg = (elf_vrreg_t *)vrregs;
-
-	/* copy the 32 vr registers */
-	memcpy(reg, &tsk->thread.vr[0], nregs * sizeof(*reg));
-	reg += nregs;
-
-	/* copy the vscr */
-	memcpy(reg, &tsk->thread.vscr, sizeof(*reg));
-	reg++;
-
-	/* vrsave is stored in the high 32bit slot of the final 128bits */
-	memset(reg, 0, sizeof(*reg));
-	dest = (u32 *)reg;
-	*dest = tsk->thread.vrsave;
-
-	return 1;
-}
 #endif /* CONFIG_ALTIVEC */
 
 #ifdef CONFIG_VSX
@@ -224,29 +172,6 @@ void flush_vsx_to_thread(struct task_str
 		preempt_enable();
 	}
 }
-
-/*
- * This dumps the lower half 64bits of the first 32 VSX registers.
- * This needs to be called with dump_task_fp and dump_task_altivec to
- * get all the VSX state.
- */
-int dump_task_vsx(struct task_struct *tsk, elf_vrreg_t *vrregs)
-{
-	elf_vrreg_t *reg;
-	double buf[32];
-	int i;
-
-	if (tsk == current)
-		flush_vsx_to_thread(tsk);
-
-	reg = (elf_vrreg_t *)vrregs;
-
-	for (i = 0; i < 32 ; i++)
-		buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET];
-	memcpy(reg, buf, sizeof(buf));
-
-	return 1;
-}
 #endif /* CONFIG_VSX */
 
 #ifdef CONFIG_SPE
@@ -279,14 +204,6 @@ void flush_spe_to_thread(struct task_str
 		preempt_enable();
 	}
 }
-
-int dump_spe(struct pt_regs *regs, elf_vrregset_t *evrregs)
-{
-	flush_spe_to_thread(current);
-	/* We copy u32 evr[32] + u64 acc + u32 spefscr -> 35 */
-	memcpy(evrregs, &current->thread.evr[0], sizeof(u32) * 35);
-	return 1;
-}
 #endif /* CONFIG_SPE */
 
 #ifndef CONFIG_SMP
Index: linux-2.6-ozlabs/include/asm-powerpc/elf.h
===================================================================
--- linux-2.6-ozlabs.orig/include/asm-powerpc/elf.h
+++ linux-2.6-ozlabs/include/asm-powerpc/elf.h
@@ -204,28 +204,8 @@ static inline void ppc_elf_core_copy_reg
 }
 #define ELF_CORE_COPY_REGS(gregs, regs) ppc_elf_core_copy_regs(gregs, regs);
 
-static inline int dump_task_regs(struct task_struct *tsk,
-				 elf_gregset_t *elf_regs)
-{
-	struct pt_regs *regs = tsk->thread.regs;
-	if (regs)
-		ppc_elf_core_copy_regs(*elf_regs, regs);
-
-	return 1;
-}
-#define ELF_CORE_COPY_TASK_REGS(tsk, elf_regs) dump_task_regs(tsk, elf_regs)
-
-extern int dump_task_fpu(struct task_struct *, elf_fpregset_t *); 
-#define ELF_CORE_COPY_FPREGS(tsk, elf_fpregs) dump_task_fpu(tsk, elf_fpregs)
-
 typedef elf_vrregset_t elf_fpxregset_t;
 
-#ifdef CONFIG_ALTIVEC
-extern int dump_task_altivec(struct task_struct *, elf_vrregset_t *vrregs);
-#define ELF_CORE_COPY_XFPREGS(tsk, regs) dump_task_altivec(tsk, regs)
-#define ELF_CORE_XFPREG_TYPE NT_PPC_VMX
-#endif
-
 /* ELF_HWCAP yields a mask that user programs can use to figure out what
    instruction set this cpu supports.  This could be done in userspace,
    but it's not easy, and we've already done it here.  */

^ permalink raw reply

* Re: High Memory problem on 2.4.22 Linux, 2GB ppc card
From: Ruksen INANIR @ 2008-07-02 13:32 UTC (permalink / raw)
  To: linuxppc-embedded
In-Reply-To: <4868A5C0.5080409@gmail.com>



I want to change the MAX_LOW_MEM to 1520 MB, what other settings do i 
need to change? I do not want to use HIGHMEM because the Fiber channel 
does not support highmem. The card is 2 GB ppc. 1520 MB is enough. At 
the moment card works with 1456 MB MAX_LOW_MEM.

Thanks





Ruksen INANIR wrote:
>
> Hi all,
>   I am working on a PPC Motorola card, which runs Linux 2.4.22.  The 
> card has 2 GB onboard memory. But with use 1456 MB of  the available 
> memory.  To increase the memory capacity i  need  to  increase the 
> used memory at least 64 MB. With CONFIG_HIGHMEM option all of the 2 GB 
> memory can be used but, but the fc driver on the card has no high 
> memory support, so this caused problems.
>     Then i saw that the memory is limited to 1456 by
>   #define MAX_LOW_MEM        0x5B000000  setting in pgtable.c.  I 
> tried to increase it by 64 MB but i could only increase the value of 
> MAX_LOW_MEM by 47 MB (1503 MB) without CONFIG_HIGHMEM option.
>
>   1)Is there any other setting that i should set other than     
> MAX_LOW_MEM to increase the usable memory to 1520 ?
>   2)I knew that the addressable physical memory without high memory 
> option was 1 G.  How was it possible to address 1503 MB without 
> CONFIG_HIGHMEM ? What is the max usable memory for 2.4.22 kernel?
>
>   Thanks,
>   Rinanir.

^ permalink raw reply

* Re: [i2c] [PATCH 1/2] Convert i2c-mpc from a platform driver into a of_platform driver, V4
From: Wolfram Sang @ 2008-07-02 13:33 UTC (permalink / raw)
  To: Jean Delvare; +Cc: Linuxppc-dev, i2c
In-Reply-To: <20080701181444.505c1544@hyperion.delvare>

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

Hi Jean,

On Tue, Jul 01, 2008 at 06:14:44PM +0200, Jean Delvare wrote:

> Applied, after fixing the patch so that it applies, fixing it again so
> that it is correct in the polling case, and fixing it again to make it
> pass checkpatch.pl.
So, it cannot be applied directly? :(

Could you repost the correct version perhaps? I'd like to test it, but
can't find it at http://khali.linux-fr.org/devel/linux-2.6/jdelvare-i2c/
(because it is scheduled for 2.6.27?).

All the best,

   Wolfram

-- 
  Dipl.-Ing. Wolfram Sang | http://www.pengutronix.de
 Pengutronix - Linux Solutions for Science and Industry

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

^ permalink raw reply

* Re: High Memory problem on 2.4.22 Linux, 2GB ppc card
From: Ruksen INANIR @ 2008-07-02 13:33 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <4868A571.2030504@gmail.com>



I want to change the MAX_LOW_MEM to 1520 MB, what other settings do i 
need to change?  I do not want to use HIGHMEM because the Fiber channel 
does not support highmem. The card is 2 GB ppc. 1520 MB is enough. At 
the moment card works with 1456 MB MAX_LOW_MEM. but setting the value 
MAX_LOW_MEM to 1520 results problems. I believe there should be other 
settings.

Thanks


Ruksen INANIR wrote:
>
> Hi all,
>    I am working on a PPC Motorola card, which runs Linux 2.4.22.  The 
> card has 2 GB onboard memory. But with use 1456 MB of  the available 
> memory.  To increase the memory capacity i  need  to  increase the 
> used memory at least 64 MB. With CONFIG_HIGHMEM option all of the 2 GB 
> memory can be used but, but the fc driver on the card has no high 
> memory support, so this caused problems.
>      Then i saw that the memory is limited to 1456 by
>    #define MAX_LOW_MEM        0x5B000000  setting in pgtable.c.  I 
> tried to increase it by 64 MB but i could only increase the value of 
> MAX_LOW_MEM by 47 MB (1503 MB) without CONFIG_HIGHMEM option.
>
>    1)Is there any other setting that i should set other than     
> MAX_LOW_MEM to increase the usable memory to 1520 ?
>    2)I knew that the addressable physical memory without high memory 
> option was 1 G.  How was it possible to address 1503 MB without 
> CONFIG_HIGHMEM ? What is the max usable memory for 2.4.22 kernel?
>
>    Thanks,
>    Rinanir.

^ permalink raw reply

* RE: Graphic Card on Freescale  MPC837x-rdb
From: Bizhan Gholikhamseh (bgholikh) @ 2008-07-02 13:34 UTC (permalink / raw)
  To: Anatolij Gustschin; +Cc: linuxppc-dev, linuxppc-embedded
In-Reply-To: <4861F59C.302@denx.de>

> -----Original Message-----
> From: Anatolij Gustschin [mailto:agust@denx.de]=20
> Sent: Wednesday, June 25, 2008 12:37 AM
> To: Bizhan Gholikhamseh (bgholikh)
> Cc: linuxppc-embedded@ozlabs.org; linuxppc-dev@ozlabs.org
> Subject: Re: Graphic Card on Freescale MPC837x-rdb
>=20
> Bizhan Gholikhamseh (bgholikh) wrote:
> > HI all,
> > Has anyone tried using a Graphic card on Freescale=20
> MPC837x-rdb board?=20
> > If so I appreciate any hints and information that I can use.
>=20
> take a look at these patches and info in the readme under:
> http://www.scitechsoft.com/ftp/snap/linux/videoboot/
>=20
> it is pretty easy to get them working with older ATI Cards=20
> (R2xx) on powerpc.
>=20
Hi
Do I need to change the DT file for add-on graphic cards?

Regards,
Bizhan=20

> Best regards,
> Anatolij
>=20

^ permalink raw reply

* Re: [alsa-devel] [PATCH 1/3] ALSA SoC: Add OpenFirmware helper for matching bus and codec drivers
From: Jon Smirl @ 2008-07-02 13:50 UTC (permalink / raw)
  To: Grant Likely; +Cc: liam.girdwood, alsa-devel, broonie, timur, linuxppc-dev
In-Reply-To: <20080701235330.16923.67218.stgit@trillian.secretlab.ca>

On 7/1/08, Grant Likely <grant.likely@secretlab.ca> wrote:
> From: Grant Likely <grant.likely@secretlab.ca>
>
>  Simple utility layer for creating ASoC machine instances based on data
>  in the OpenFirmware device tree.  OF aware platform drivers and codec
>  drivers register themselves with this framework and the framework
>  automatically instantiates a machine driver.
>
>  This is most likely temporary glue code to work around limitations in
>  the ASoC v1 framework.  I expect ASoC v2 won't need this.
>  ---
>
>   sound/soc/Kconfig  |    6 ++
>   sound/soc/Makefile |    1
>   sound/soc/soc-of.c |  171 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 178 insertions(+), 0 deletions(-)
>
>  diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
>  index 18f28ac..c5736e5 100644
>  --- a/sound/soc/Kconfig
>  +++ b/sound/soc/Kconfig
>  @@ -23,6 +23,12 @@ config SND_SOC
>           This ASoC audio support can also be built as a module.  If so, the module
>           will be called snd-soc-core.
>
>  +config SND_SOC_OF
>  +       tristate "OF helpers for SoC audio support"
>  +       depends on SND_SOC
>  +       ---help---
>  +         Add support for OpenFirmware device tree descriptions of sound device
>  +
>   # All the supported Soc's
>   source "sound/soc/at91/Kconfig"
>   source "sound/soc/pxa/Kconfig"
>  diff --git a/sound/soc/Makefile b/sound/soc/Makefile
>  index 782db21..191c2e5 100644
>  --- a/sound/soc/Makefile
>  +++ b/sound/soc/Makefile
>  @@ -2,3 +2,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o
>
>   obj-$(CONFIG_SND_SOC)  += snd-soc-core.o
>   obj-$(CONFIG_SND_SOC)  += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ omap/
>  +obj-$(CONFIG_SND_SOC_OF)       += soc-of.o
>  diff --git a/sound/soc/soc-of.c b/sound/soc/soc-of.c
>  new file mode 100644
>  index 0000000..9694979
>  --- /dev/null
>  +++ b/sound/soc/soc-of.c
>  @@ -0,0 +1,171 @@
>  +/*
>  + * OF helpers for ALSA SoC Layer
>  + *
>  + * Copyright (C) 2008, Secret Lab Technologies Ltd.
>  + */
>  +
>  +#include <linux/module.h>
>  +#include <linux/moduleparam.h>
>  +#include <linux/init.h>
>  +#include <linux/delay.h>
>  +#include <linux/pm.h>
>  +#include <linux/bitops.h>
>  +#include <linux/platform_device.h>
>  +#include <linux/of.h>
>  +#include <sound/core.h>
>  +#include <sound/pcm.h>
>  +#include <sound/pcm_params.h>
>  +#include <sound/soc.h>
>  +#include <sound/soc-of.h>
>  +#include <sound/initval.h>
>  +
>  +MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
>  +MODULE_LICENSE("GPL");
>  +MODULE_DESCRIPTION("ALSA SoC OpenFirmware bindings");
>  +
>  +DEFINE_MUTEX(of_snd_soc_mutex);
>  +LIST_HEAD(of_snd_soc_device_list);
>  +static int of_snd_soc_next_index;
>  +
>  +struct of_snd_soc_device {
>  +       int id;
>  +       struct list_head list;
>  +       struct snd_soc_device device;
>  +       struct snd_soc_machine machine;
>  +       struct snd_soc_dai_link dai_link;
>  +       struct platform_device *pdev;
>  +       struct device_node *platform_node;
>  +       struct device_node *codec_node;
>  +};
>  +
>  +static struct snd_soc_ops of_snd_soc_ops = {
>  +};
>  +
>  +static struct of_snd_soc_device *
>  +of_snd_soc_get_device(struct device_node *codec_node)
>  +{
>  +       struct of_snd_soc_device *of_soc;
>  +
>  +       list_for_each_entry(of_soc, &of_snd_soc_device_list, list) {
>  +               if (of_soc->codec_node == codec_node)
>  +                       return of_soc;
>  +       }
>  +
>  +       of_soc = kzalloc(sizeof(struct of_snd_soc_device), GFP_KERNEL);
>  +       if (!of_soc)
>  +               return NULL;
>  +
>  +       /* Initialize the structure and add it to the global list */
>  +       of_soc->codec_node = codec_node;
>  +       of_soc->id = of_snd_soc_next_index++;
>  +       of_soc->machine.dai_link = &of_soc->dai_link;
>  +       of_soc->machine.num_links = 1;
>  +       of_soc->device.machine = &of_soc->machine;
>  +       of_soc->dai_link.ops = &of_snd_soc_ops;
>  +       list_add(&of_soc->list, &of_snd_soc_device_list);
>  +
>  +       return of_soc;
>  +}
>  +
>  +static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc)
>  +{
>  +       struct platform_device *pdev;
>  +       int rc;
>  +
>  +       /* Only register the device if both the codec and platform have
>  +        * been registered */
>  +       if ((!of_soc->device.codec_data) || (!of_soc->platform_node))
>  +               return;
>  +
>  +       pr_info("platform<-->codec match achieved; registering machine\n");
>  +
>  +       pdev = platform_device_alloc("soc-audio", of_soc->id);
>  +       if (!pdev) {
>  +               pr_err("of_soc: platform_device_alloc() failed\n");
>  +               return;
>  +       }
>  +
>  +       pdev->dev.platform_data = of_soc;
>  +       platform_set_drvdata(pdev, &of_soc->device);
>  +       of_soc->device.dev = &pdev->dev;
>  +
>  +       /* The ASoC device is complete; register it */
>  +       rc = platform_device_add(pdev);
>  +       if (rc) {
>  +               pr_err("of_soc: platform_device_add() failed\n");
>  +               return;
>  +       }

Is there a driver for this device?  Is another file missing?
sof_of.h is missing too.

>  +
>  +}
>  +
>  +int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev,
>  +                             void *codec_data, struct snd_soc_codec_dai *dai,
>  +                             struct device_node *node)
>  +{
>  +       struct of_snd_soc_device *of_soc;
>  +       int rc = 0;
>  +
>  +       pr_info("registering ASoC codec driver: %s\n", node->full_name);
>  +
>  +       mutex_lock(&of_snd_soc_mutex);
>  +       of_soc = of_snd_soc_get_device(node);
>  +       if (!of_soc) {
>  +               rc = -ENOMEM;
>  +               goto out;
>  +       }
>  +
>  +       /* Store the codec data */
>  +       of_soc->device.codec_data = codec_data;
>  +       of_soc->device.codec_dev = codec_dev;
>  +       of_soc->dai_link.name = node->name;
>  +       of_soc->dai_link.stream_name = node->name;
>  +       of_soc->dai_link.codec_dai = dai;
>  +
>  +       /* Now try to register the SoC device */
>  +       of_snd_soc_register_device(of_soc);
>  +
>  + out:
>  +       mutex_unlock(&of_snd_soc_mutex);
>  +       return rc;
>  +}
>  +EXPORT_SYMBOL_GPL(of_snd_soc_register_codec);
>  +
>  +int of_snd_soc_register_platform(struct snd_soc_platform *platform,
>  +                                struct device_node *node,
>  +                                struct snd_soc_cpu_dai *cpu_dai)
>  +{
>  +       struct of_snd_soc_device *of_soc;
>  +       struct device_node *codec_node;
>  +       const phandle *handle;
>  +       int len, rc = 0;
>  +
>  +       pr_info("registering ASoC platform driver: %s\n", node->full_name);
>  +
>  +       handle = of_get_property(node, "codec-handle", &len);
>  +       if (!handle || len < sizeof(handle))
>  +               return -ENODEV;
>  +       codec_node = of_find_node_by_phandle(*handle);
>  +       if (!codec_node)
>  +               return -ENODEV;
>  +       pr_info("looking for codec: %s\n", codec_node->full_name);
>  +
>  +       mutex_lock(&of_snd_soc_mutex);
>  +       of_soc = of_snd_soc_get_device(codec_node);
>  +       if (!of_soc) {
>  +               rc = -ENOMEM;
>  +               goto out;
>  +       }
>  +
>  +       of_soc->platform_node = node;
>  +       of_soc->dai_link.cpu_dai = cpu_dai;
>  +       of_soc->device.platform = platform;
>  +       of_soc->machine.name = of_soc->dai_link.cpu_dai->name;
>  +
>  +       /* Now try to register the SoC device */
>  +       of_snd_soc_register_device(of_soc);
>  +
>  + out:
>  +       mutex_unlock(&of_snd_soc_mutex);
>  +       return rc;
>  +}
>  +EXPORT_SYMBOL_GPL(of_snd_soc_register_platform);
>
>  _______________________________________________
>  Alsa-devel mailing list
>  Alsa-devel@alsa-project.org
>  http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>


-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply

* Re: [alsa-devel] [PATCH 2/3] ALSA SoC: Add mpc5200-psc I2S driver
From: Jon Smirl @ 2008-07-02 13:51 UTC (permalink / raw)
  To: Grant Likely; +Cc: liam.girdwood, alsa-devel, broonie, timur, linuxppc-dev
In-Reply-To: <20080701235335.16923.43253.stgit@trillian.secretlab.ca>

DMA, needs to be split out. Efika is AC97 on the MPC5200 and needs to
share DMA code. The new Phytec pcm030 baseboard is AC97 too.

What does the device tree look like?


On 7/1/08, Grant Likely <grant.likely@secretlab.ca> wrote:
> From: Grant Likely <grant.likely@secretlab.ca>
>
>  This is an I2S bus driver for the MPC5200 PSC device.  It is probably
>  will not be merged as-is because it uses v1 of the ASoC API, but I want
>  to get it out there for comments.
>  ---
>
>   sound/soc/fsl/Kconfig           |    6
>   sound/soc/fsl/Makefile          |    2
>   sound/soc/fsl/mpc5200_psc_i2s.c |  899 +++++++++++++++++++++++++++++++++++++++
>   3 files changed, 907 insertions(+), 0 deletions(-)
>
>  diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
>  index 257101f..5daa8d3 100644
>  --- a/sound/soc/fsl/Kconfig
>  +++ b/sound/soc/fsl/Kconfig
>  @@ -17,4 +17,10 @@ config SND_SOC_MPC8610_HPCD
>         help
>           Say Y if you want to enable audio on the Freescale MPC8610 HPCD.
>
>  +config SND_SOC_MPC5200_I2S
>  +       bool "Freescale MPC5200 PSC in I2S mode driver"
>  +       depends on SND_SOC && PPC_MPC52xx
>  +       help
>  +         Say Y here to support the MPC5200 PSCs in I2S mode.
>  +
>   endmenu
>  diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
>  index 62f680a..98729a1 100644
>  --- a/sound/soc/fsl/Makefile
>  +++ b/sound/soc/fsl/Makefile
>  @@ -4,3 +4,5 @@ obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o
>   # MPC8610 Platform Support
>   obj-$(CONFIG_SND_SOC_MPC8610) += fsl_ssi.o fsl_dma.o
>
>  +obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o
>  +
>  diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c
>  new file mode 100644
>  index 0000000..81d0933
>  --- /dev/null
>  +++ b/sound/soc/fsl/mpc5200_psc_i2s.c
>  @@ -0,0 +1,899 @@
>  +/*
>  + * Freescale MPC5200 PSC in I2S mode
>  + * ALSA SoC Digital Audio Interface (DAI) driver
>  + *
>  + * Copyright (C) 2008 Secret Lab Technologies Ltd.
>  + */
>  +
>  +#include <linux/init.h>
>  +#include <linux/module.h>
>  +#include <linux/interrupt.h>
>  +#include <linux/device.h>
>  +#include <linux/delay.h>
>  +#include <linux/of_device.h>
>  +#include <linux/of_platform.h>
>  +#include <linux/dma-mapping.h>
>  +
>  +#include <sound/core.h>
>  +#include <sound/pcm.h>
>  +#include <sound/pcm_params.h>
>  +#include <sound/initval.h>
>  +#include <sound/soc.h>
>  +#include <sound/soc-of.h>
>  +
>  +#include <sysdev/bestcomm/bestcomm.h>
>  +#include <sysdev/bestcomm/gen_bd.h>
>  +#include <asm/mpc52xx_psc.h>
>  +
>  +MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
>  +MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
>  +MODULE_LICENSE("GPL");
>  +
>  +/**
>  + * PSC_I2S_RATES: sample rates supported by the I2S
>  + *
>  + * This driver currently only supports the PSC running in I2S slave mode,
>  + * which means the codec determines the sample rate.  Therefore, we tell
>  + * ALSA that we support all rates and let the codec driver decide what rates
>  + * are really supported.
>  + */
>  +#define PSC_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
>  +                       SNDRV_PCM_RATE_CONTINUOUS)
>  +
>  +/**
>  + * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
>  + */
>  +#define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
>  +                        SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE | \
>  +                        SNDRV_PCM_FMTBIT_S32_BE)
>  +
>  +/**
>  + * psc_i2s_stream - Data specific to a single stream (playback or capture)
>  + * @active:            flag indicating if the stream is active
>  + * @psc_i2s:           pointer back to parent psc_i2s data structure
>  + * @bcom_task:         bestcomm task structure
>  + * @irq:               irq number for bestcomm task
>  + * @period_start:      physical address of start of DMA region
>  + * @period_end:                physical address of end of DMA region
>  + * @period_next_pt:    physical address of next DMA buffer to enqueue
>  + * @period_bytes:      size of DMA period in bytes
>  + */
>  +struct psc_i2s_stream {
>  +       int active;
>  +       struct psc_i2s *psc_i2s;
>  +       struct bcom_task *bcom_task;
>  +       int irq;
>  +       struct snd_pcm_substream *stream;
>  +       dma_addr_t period_start;
>  +       dma_addr_t period_end;
>  +       dma_addr_t period_next_pt;
>  +       dma_addr_t period_current_pt;
>  +       int period_bytes;
>  +};
>  +
>  +/**
>  + * psc_i2s - Private driver data
>  + * @name: short name for this device ("PSC0", "PSC1", etc)
>  + * @psc_regs: pointer to the PSC's registers
>  + * @fifo_regs: pointer to the PSC's FIFO registers
>  + * @irq: IRQ of this PSC
>  + * @dev: struct device pointer
>  + * @playback: the number of playback streams opened
>  + * @capture: the number of capture streams opened
>  + * @dai: the CPU DAI for this device
>  + * @playback_stream: Playback stream context data
>  + * @capture_stream: Capture stream context data
>  + */
>  +struct psc_i2s {
>  +       char name[32];
>  +       struct mpc52xx_psc __iomem *psc_regs;
>  +       struct mpc52xx_psc_fifo __iomem *fifo_regs;
>  +       unsigned int irq;
>  +       struct device *dev;
>  +       struct snd_soc_cpu_dai dai;
>  +       spinlock_t lock;
>  +
>  +       /* per-stream data */
>  +       struct psc_i2s_stream playback_stream;
>  +       struct psc_i2s_stream capture_stream;
>  +
>  +       /* Statistics */
>  +       struct {
>  +               int overrun_count;
>  +               int underrun_count;
>  +       } stats;
>  +};
>  +
>  +/*
>  + * Interrupt handlers
>  + */
>  +static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s)
>  +{
>  +       struct psc_i2s *psc_i2s = _psc_i2s;
>  +       struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
>  +       u16 imr;
>  +       u16 isr;
>  +
>  +       isr = in_be16(&regs->mpc52xx_psc_isr);
>  +       imr = in_be16(&regs->mpc52xx_psc_imr);
>  +
>  +       /* Playback underrun error */
>  +       if (isr & imr & MPC52xx_PSC_IMR_TXEMP)
>  +               psc_i2s->stats.underrun_count++;
>  +
>  +       /* Capture overrun error */
>  +       if (isr & imr & MPC52xx_PSC_IMR_ORERR)
>  +               psc_i2s->stats.overrun_count++;
>  +
>  +       out_8(&regs->command, 4 << 4);  /* reset the error status */
>  +
>  +       return IRQ_HANDLED;
>  +}
>  +
>  +/**
>  + * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer
>  + * @s: pointer to stream private data structure
>  + *
>  + * Enqueues another audio period buffer into the bestcomm queue.
>  + *
>  + * Note: The routine must only be called when there is space available in
>  + * the queue.  Otherwise the enqueue will fail and the audio ring buffer
>  + * will get out of sync
>  + */
>  +static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s)
>  +{
>  +       struct bcom_bd *bd;
>  +
>  +       /* Prepare and enqueue the next buffer descriptor */
>  +       bd = bcom_prepare_next_buffer(s->bcom_task);
>  +       bd->status = s->period_bytes;
>  +       bd->data[0] = s->period_next_pt;
>  +       bcom_submit_next_buffer(s->bcom_task, NULL);
>  +
>  +       /* Update for next period */
>  +       s->period_next_pt += s->period_bytes;
>  +       if (s->period_next_pt >= s->period_end)
>  +               s->period_next_pt = s->period_start;
>  +}
>  +
>  +/* Bestcomm DMA irq handler */
>  +static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream)
>  +{
>  +       struct psc_i2s_stream *s = _psc_i2s_stream;
>  +
>  +       //spin_lock(&s->psc_i2s->lock);
>  +
>  +       /* For each finished period, dequeue the completed period buffer
>  +        * and enqueue a new one in it's place. */
>  +       while (bcom_buffer_done(s->bcom_task)) {
>  +               bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
>  +               s->period_current_pt += s->period_bytes;
>  +               if (s->period_current_pt >= s->period_end)
>  +                       s->period_current_pt = s->period_start;
>  +               psc_i2s_bcom_enqueue_next_buffer(s);
>  +               bcom_enable(s->bcom_task);
>  +       }
>  +
>  +       //spin_unlock(&s->psc_i2s->lock);
>  +
>  +       /* If the stream is active, then also inform the PCM middle layer
>  +        * of the period finished event. */
>  +       if (s->active)
>  +               snd_pcm_period_elapsed(s->stream);
>  +
>  +       return IRQ_HANDLED;
>  +}
>  +
>  +/**
>  + * psc_i2s_startup: create a new substream
>  + *
>  + * This is the first function called when a stream is opened.
>  + *
>  + * If this is the first stream open, then grab the IRQ and program most of
>  + * the PSC registers.
>  + */
>  +static int psc_i2s_startup(struct snd_pcm_substream *substream)
>  +{
>  +       int playback_irq, capture_irq, rc;
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
>  +       struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
>  +       struct mpc52xx_psc_fifo __iomem *fiforegs = psc_i2s->fifo_regs;
>  +
>  +       dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream);
>  +
>  +       /* Disable all interrupts and reset the PSC */
>  +       out_be16(&regs->mpc52xx_psc_imr, 0);
>  +       out_8(&regs->command, 3 << 4); /* reset transmitter */
>  +       out_8(&regs->command, 2 << 4); /* reset receiver */
>  +       out_8(&regs->command, 1 << 4); /* reset mode */
>  +       out_8(&regs->command, 4 << 4); /* reset error */
>  +
>  +       /* Default to CODEC8 mode */
>  +       out_be32(&regs->sicr,
>  +                MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S |
>  +                MPC52xx_PSC_SICR_CLKPOL | MPC52xx_PSC_SICR_SIM_CODEC_8);
>  +
>  +       /* First write: RxRdy (FIFO Alarm) generates receive FIFO interrupt */
>  +       /* Second write to mode: register Normal mode for non loopback */
>  +       out_8(&regs->mode, 0);
>  +       out_8(&regs->mode, 0);
>  +
>  +       /* Set the TX and RX fifo alarm thresholds */
>  +       out_be16(&fiforegs->rfalarm, 0x100);    /* set RFALARM level */
>  +       out_8(&fiforegs->rfcntl, 0x4);          /* set RFGRAN level (bytes) */
>  +       out_be16(&fiforegs->tfalarm, 0x100);    /* set TFALARM level */
>  +       out_8(&fiforegs->tfcntl, 0x7);          /* set TFGRAN level (bytes*4) */
>  +
>  +       /* Setup the IRQs */
>  +       playback_irq = bcom_get_task_irq(psc_i2s->playback_stream.bcom_task);
>  +       capture_irq = bcom_get_task_irq(psc_i2s->capture_stream.bcom_task);
>  +       rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED,
>  +                        "psc-i2s-status", psc_i2s);
>  +       rc |= request_irq(capture_irq, &psc_i2s_bcom_irq, IRQF_SHARED,
>  +                         "psc-i2s-capture", &psc_i2s->capture_stream);
>  +       rc |= request_irq(playback_irq, &psc_i2s_bcom_irq, IRQF_SHARED,
>  +                         "psc-i2s-playback", &psc_i2s->playback_stream);
>  +       if (rc) {
>  +               free_irq(psc_i2s->irq, psc_i2s);
>  +               free_irq(capture_irq, &psc_i2s->capture_stream);
>  +               free_irq(playback_irq, &psc_i2s->playback_stream);
>  +               return -ENODEV;
>  +       }
>  +
>  +       return 0;
>  +}
>  +
>  +static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
>  +                                struct snd_pcm_hw_params *params)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
>  +       u32 sicr;
>  +
>  +       dev_dbg(psc_i2s->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
>  +               " periods=%i buffer_size=%i  buffer_bytes=%i\n",
>  +               __FUNCTION__, substream, params_period_size(params),
>  +               params_period_bytes(params), params_periods(params),
>  +               params_buffer_size(params), params_buffer_bytes(params));
>  +
>  +       sicr = MPC52xx_PSC_SICR_DTS1 |
>  +              MPC52xx_PSC_SICR_I2S | MPC52xx_PSC_SICR_CLKPOL;
>  +       switch (params_format(params)) {
>  +        case SNDRV_PCM_FORMAT_S8:
>  +               sicr |= MPC52xx_PSC_SICR_SIM_CODEC_8;
>  +               break;
>  +        case SNDRV_PCM_FORMAT_S16_BE:
>  +               sicr |= MPC52xx_PSC_SICR_SIM_CODEC_16;
>  +               break;
>  +        case SNDRV_PCM_FORMAT_S24_BE:
>  +               sicr |= MPC52xx_PSC_SICR_SIM_CODEC_24;
>  +               break;
>  +        case SNDRV_PCM_FORMAT_S32_BE:
>  +               sicr |= MPC52xx_PSC_SICR_SIM_CODEC_32;
>  +               break;
>  +        default:
>  +               dev_dbg(psc_i2s->dev, "invalid format\n");
>  +               return -EINVAL;
>  +       }
>  +       out_be32(&psc_i2s->psc_regs->sicr, sicr);
>  +
>  +       //rc = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
>  +       //if (rc) {
>  +       //      dev_err(psc_i2s->dev, "could not allocate dma buffer\n");
>  +       //      return rc;
>  +       //}
>  +
>  +       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
>  +
>  +       return 0;
>  +}
>  +
>  +static int psc_i2s_hw_free(struct snd_pcm_substream *substream)
>  +{
>  +       //return snd_pcm_lib_free_pages(substream);
>  +       snd_pcm_set_runtime_buffer(substream, NULL);
>  +       return 0;
>  +}
>  +
>  +/**
>  + * psc_i2s_trigger: start and stop the DMA transfer.
>  + *
>  + * This function is called by ALSA to start, stop, pause, and resume the DMA
>  + * transfer of data.
>  + */
>  +static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
>  +       struct snd_pcm_runtime *runtime = substream->runtime;
>  +       struct psc_i2s_stream *s;
>  +       struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
>  +       u16 imr;
>  +       u8 psc_cmd;
>  +       long flags;
>  +
>  +       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
>  +               s = &psc_i2s->capture_stream;
>  +       else
>  +               s = &psc_i2s->playback_stream;
>  +
>  +       dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)"
>  +               " stream_id=%i\n",
>  +               substream, cmd, substream->pstr->stream);
>  +
>  +       switch (cmd) {
>  +        case SNDRV_PCM_TRIGGER_START:
>  +               s->period_bytes = frames_to_bytes(runtime,
>  +                                                 runtime->period_size);
>  +               s->period_start = virt_to_phys(runtime->dma_area);
>  +               s->period_end = s->period_start +
>  +                               (s->period_bytes * runtime->periods);
>  +               s->period_next_pt = s->period_start;
>  +               s->period_current_pt = s->period_start;
>  +               s->active = 1;
>  +
>  +               /* First; reset everything */
>  +               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
>  +                       out_8(&regs->command, MPC52xx_PSC_RST_RX);
>  +                       out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
>  +               } else {
>  +                       out_8(&regs->command, MPC52xx_PSC_RST_TX);
>  +                       out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
>  +               }
>  +
>  +               /* Next, fill up the bestcomm bd queue and enable DMA.
>  +                * This will begin filling the PSC's fifo. */
>  +               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
>  +                       bcom_gen_bd_rx_reset(s->bcom_task);
>  +               else
>  +                       bcom_gen_bd_tx_reset(s->bcom_task);
>  +               while (!bcom_queue_full(s->bcom_task))
>  +                       psc_i2s_bcom_enqueue_next_buffer(s);
>  +               bcom_enable(s->bcom_task);
>  +
>  +               /* Update interrupt enable settings.  This must be done
>  +                * before the PSC is enabled so that TX underrun events
>  +                * are not missed. */
>  +               imr = 0;
>  +               if (psc_i2s->playback_stream.active)
>  +                       imr |= MPC52xx_PSC_IMR_TXEMP;
>  +               if (psc_i2s->capture_stream.active)
>  +                       imr |= MPC52xx_PSC_IMR_ORERR;
>  +               out_be16(&regs->isr_imr.imr, imr);
>  +
>  +               /* Due to errata in the i2s mode; need to line up enabling
>  +                * the transmitter with a transition on the frame sync
>  +                * line */
>  +
>  +               spin_lock_irqsave(&psc_i2s->lock, flags);
>  +               /* first make sure it is low */
>  +               while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) != 0);
>  +               /* then wait for the transition to high */
>  +               while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) == 0);
>  +               /* Finally, enable the PSC.
>  +                * Receiver must always be enabled; even when we only want
>  +                * transmit.  (see 15.3.2.3 of MPC5200B User's Guide) */
>  +               psc_cmd = MPC52xx_PSC_RX_ENABLE;
>  +               if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK)
>  +                       psc_cmd |= MPC52xx_PSC_TX_ENABLE;
>  +               out_8(&regs->command, psc_cmd);
>  +               spin_unlock_irqrestore(&psc_i2s->lock, flags);
>  +
>  +               break;
>  +
>  +        case SNDRV_PCM_TRIGGER_STOP:
>  +               /* Turn off the PSC */
>  +               s->active = 0;
>  +               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
>  +                       if (!psc_i2s->playback_stream.active) {
>  +                               out_8(&regs->command, 2 << 4);  /* reset rx */
>  +                               out_8(&regs->command, 3 << 4);  /* reset tx */
>  +                               out_8(&regs->command, 4 << 4);  /* reset err */
>  +                       }
>  +               } else {
>  +                       out_8(&regs->command, 3 << 4);  /* reset tx */
>  +                       out_8(&regs->command, 4 << 4);  /* reset err */
>  +                       if (!psc_i2s->capture_stream.active)
>  +                               out_8(&regs->command, 2 << 4);  /* reset rx */
>  +               }
>  +
>  +               bcom_disable(s->bcom_task);
>  +               while (!bcom_queue_empty(s->bcom_task))
>  +                       bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
>  +
>  +               break;
>  +
>  +        default:
>  +               dev_dbg(psc_i2s->dev, "invalid command\n");
>  +               return -EINVAL;
>  +       }
>  +
>  +       /* Update interrupt enable settings */
>  +       imr = 0;
>  +       if (psc_i2s->playback_stream.active) imr |= MPC52xx_PSC_IMR_TXEMP;
>  +       if (psc_i2s->capture_stream.active) imr |= MPC52xx_PSC_IMR_ORERR;
>  +       out_be16(&regs->isr_imr.imr, imr);
>  +
>  +       return 0;
>  +}
>  +
>  +/**
>  + * psc_i2s_shutdown: shutdown the data transfer on a stream
>  + *
>  + * Shutdown the PSC if there are no other substreams open.
>  + */
>  +static void psc_i2s_shutdown(struct snd_pcm_substream *substream)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
>  +
>  +       dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream);
>  +
>  +       /*
>  +        * If this is the last active substream, disable the PSC and release
>  +        * the IRQ.
>  +        */
>  +       if (!psc_i2s->playback_stream.active &&
>  +           !psc_i2s->capture_stream.active) {
>  +               /* TODO: shut off channels */
>  +               free_irq(psc_i2s->irq, psc_i2s);
>  +               free_irq(bcom_get_task_irq(psc_i2s->capture_stream.bcom_task),
>  +                        &psc_i2s->capture_stream);
>  +               free_irq(bcom_get_task_irq(psc_i2s->playback_stream.bcom_task),
>  +                        &psc_i2s->playback_stream);
>  +       }
>  +}
>  +
>  +/**
>  + * psc_i2s_set_sysclk: set the clock frequency and direction
>  + *
>  + * This function is called by the machine driver to tell us what the clock
>  + * frequency and direction are.
>  + *
>  + * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN),
>  + * and we don't care about the frequency.  Return an error if the direction
>  + * is not SND_SOC_CLOCK_IN.
>  + *
>  + * @clk_id: reserved, should be zero
>  + * @freq: the frequency of the given clock ID, currently ignored
>  + * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master)
>  + */
>  +static int psc_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
>  +                             int clk_id, unsigned int freq, int dir)
>  +{
>  +       struct psc_i2s *psc_i2s = cpu_dai->private_data;
>  +       dev_dbg(psc_i2s->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n",
>  +                               cpu_dai, dir);
>  +       return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL;
>  +}
>  +
>  +/**
>  + * psc_i2s_set_fmt: set the serial format.
>  + *
>  + * This function is called by the machine driver to tell us what serial
>  + * format to use.
>  + *
>  + * This driver only supports I2S mode.  Return an error if the format is
>  + * not SND_SOC_DAIFMT_I2S.
>  + *
>  + * @format: one of SND_SOC_DAIFMT_xxx
>  + */
>  +static int psc_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int format)
>  +{
>  +       struct psc_i2s *psc_i2s = cpu_dai->private_data;
>  +       dev_dbg(psc_i2s->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n",
>  +                               cpu_dai, format);
>  +       return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
>  +}
>  +
>  +/* ---------------------------------------------------------------------
>  + * ALSA SoC Bindings
>  + *
>  + * - Digital Audio Interface (DAI) template
>  + * - create/destroy dai hooks
>  + */
>  +
>  +/**
>  + * psc_i2s_dai_template: template CPU Digital Audio Interface
>  + */
>  +static struct snd_soc_cpu_dai psc_i2s_dai_template = {
>  +       .type = SND_SOC_DAI_I2S,
>  +       .playback = {
>  +               .channels_min = 2,
>  +               .channels_max = 2,
>  +               .rates = PSC_I2S_RATES,
>  +               .formats = PSC_I2S_FORMATS,
>  +       },
>  +       .capture = {
>  +               .channels_min = 2,
>  +               .channels_max = 2,
>  +               .rates = PSC_I2S_RATES,
>  +               .formats = PSC_I2S_FORMATS,
>  +       },
>  +       .ops = {
>  +               .startup = psc_i2s_startup,
>  +               .hw_params = psc_i2s_hw_params,
>  +               .hw_free = psc_i2s_hw_free,
>  +               .shutdown = psc_i2s_shutdown,
>  +               .trigger = psc_i2s_trigger,
>  +       },
>  +       .dai_ops = {
>  +               .set_sysclk = psc_i2s_set_sysclk,
>  +               .set_fmt = psc_i2s_set_fmt,
>  +       },
>  +};
>  +
>  +/* ---------------------------------------------------------------------
>  + * The PSC I2S 'ASoC platform' driver
>  + *
>  + * Can be referenced by an 'ASoC machine' driver
>  + * This driver only deals with the audio bus; it doesn't have any
>  + * interaction with the attached codec
>  + */
>  +
>  +static const struct snd_pcm_hardware psc_i2s_pcm_hardware = {
>  +       .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
>  +               SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER,
>  +       .formats = SNDRV_PCM_FMTBIT_S8 |SNDRV_PCM_FMTBIT_S16_BE |
>  +                  SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
>  +       .rate_min = 8000,
>  +       .rate_max = 48000,
>  +       .channels_min = 2,
>  +       .channels_max = 2,
>  +       .period_bytes_max       = 1024 * 1024,
>  +       .period_bytes_min       = 32,
>  +       .period_bytes_max       = 1024 * 1024,
>  +       .periods_min            = 2,
>  +       .periods_max            = 256,
>  +       .buffer_bytes_max       = 2 * 1024 * 1024,
>  +       .fifo_size              = 0,
>  +};
>  +
>  +static unsigned int psc_i2s_fixed_rates[] = {
>  +       8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
>  +};
>  +
>  +static struct snd_pcm_hw_constraint_list psc_i2s_constraints_rates = {
>  +       .count = ARRAY_SIZE(psc_i2s_fixed_rates),
>  +       .list = psc_i2s_fixed_rates,
>  +       .mask = 0,
>  +};
>  +
>  +static int psc_i2s_pcm_open(struct snd_pcm_substream *substream)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
>  +       struct psc_i2s_stream *s;
>  +       int rc;
>  +
>  +       dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream);
>  +
>  +       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
>  +               s = &psc_i2s->capture_stream;
>  +       else
>  +               s = &psc_i2s->playback_stream;
>  +
>  +       snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware);
>  +
>  +       rc = snd_pcm_hw_constraint_integer(substream->runtime,
>  +                                          SNDRV_PCM_HW_PARAM_PERIODS);
>  +       if (rc < 0) {
>  +               dev_err(psc_i2s->dev, "invalid buffer size\n");
>  +               return rc;
>  +       }
>  +       rc = snd_pcm_hw_constraint_list(substream->runtime, 0,
>  +                                       SNDRV_PCM_HW_PARAM_RATE,
>  +                                       &psc_i2s_constraints_rates);
>  +       if (rc < 0) {
>  +               dev_err(psc_i2s->dev, "invalid rate\n");
>  +               return rc;
>  +       }
>  +
>  +       s->stream = substream;
>  +       return 0;
>  +}
>  +
>  +static int psc_i2s_pcm_close(struct snd_pcm_substream * substream)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
>  +       struct psc_i2s_stream *s;
>  +
>  +       dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream);
>  +
>  +       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
>  +               s = &psc_i2s->capture_stream;
>  +       else
>  +               s = &psc_i2s->playback_stream;
>  +
>  +       s->stream = NULL;
>  +       return 0;
>  +}
>  +
>  +static snd_pcm_uframes_t
>  +psc_i2s_pcm_pointer(struct snd_pcm_substream *substream)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
>  +       struct psc_i2s_stream *s;
>  +       dma_addr_t count;
>  +
>  +       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
>  +               s = &psc_i2s->capture_stream;
>  +       else
>  +               s = &psc_i2s->playback_stream;
>  +
>  +       /*FIXME: count = s->sdma->bd[s->sdma->outdex].data - s->period_start;*/
>  +       count = s->period_current_pt - s->period_start;
>  +
>  +       return bytes_to_frames(substream->runtime, count);
>  +}
>  +
>  +static struct snd_pcm_ops psc_i2s_pcm_ops = {
>  +       .open           = psc_i2s_pcm_open,
>  +       .close          = psc_i2s_pcm_close,
>  +       .ioctl          = snd_pcm_lib_ioctl,
>  +       .pointer        = psc_i2s_pcm_pointer,
>  +};
>  +
>  +static u64 psc_i2s_pcm_dmamask = 0xffffffff;
>  +static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
>  +                          struct snd_pcm *pcm)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = pcm->private_data;
>  +       size_t size = psc_i2s_pcm_hardware.buffer_bytes_max;
>  +       int rc = 0;
>  +
>  +       dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n",
>  +               card, dai, pcm);
>  +
>  +       if (!card->dev->dma_mask)
>  +               card->dev->dma_mask = &psc_i2s_pcm_dmamask;
>  +       if (!card->dev->coherent_dma_mask)
>  +               card->dev->coherent_dma_mask = 0xffffffff;
>  +
>  +       rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
>  +                                &pcm->streams[0].substream->dma_buffer);
>  +       if (rc) {
>  +               dev_err(card->dev, "Cannot alloc playback DMA buffer\n");
>  +               return -ENOMEM;
>  +       }
>  +
>  +       rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
>  +                                &pcm->streams[1].substream->dma_buffer);
>  +       if (rc) {
>  +               snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer);
>  +               dev_err(card->dev, "Can't allocate capture DMA buffer\n");
>  +               return -ENOMEM;
>  +       }
>  +
>  +       return 0;
>  +}
>  +
>  +static void psc_i2s_pcm_free(struct snd_pcm *pcm)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = pcm->private_data;
>  +       struct snd_pcm_substream *substream;
>  +       int stream;
>  +
>  +       dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=%p)\n", pcm);
>  +
>  +       for (stream = 0; stream < 2; stream++) {
>  +               substream = pcm->streams[stream].substream;
>  +               if (substream) {
>  +                       snd_dma_free_pages(&substream->dma_buffer);
>  +                       substream->dma_buffer.area = NULL;
>  +                       substream->dma_buffer.addr = 0;
>  +               }
>  +       }
>  +}
>  +
>  +struct snd_soc_platform psc_i2s_pcm_soc_platform = {
>  +       .name           = "mpc5200-psc-audio",
>  +       .pcm_ops        = &psc_i2s_pcm_ops,
>  +       .pcm_new        = &psc_i2s_pcm_new,
>  +       .pcm_free       = &psc_i2s_pcm_free,
>  +};
>  +
>  +/* ---------------------------------------------------------------------
>  + * Sysfs attributes for debugging
>  + */
>  +
>  +static ssize_t psc_i2s_status_show(struct device *dev,
>  +                          struct device_attribute *attr, char *buf)
>  +{
>  +       struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
>  +
>  +       return sprintf(buf, "status=%.4x sicr=%.8x rfnum=%i rfstat=0x%.4x tfnum=%i tfstat=0x%.4x\n",
>  +                       in_be16(&psc_i2s->psc_regs->sr_csr.status),
>  +                       in_be32(&psc_i2s->psc_regs->sicr),
>  +                       in_be16(&psc_i2s->fifo_regs->rfnum) & 0x1ff,
>  +                       in_be16(&psc_i2s->fifo_regs->rfstat),
>  +                       in_be16(&psc_i2s->fifo_regs->tfnum) & 0x1ff,
>  +                       in_be16(&psc_i2s->fifo_regs->tfstat));
>  +}
>  +
>  +static int * psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s,
>  +                                  const char *name)
>  +{
>  +       if (strcmp(name, "playback_underrun") == 0)
>  +               return &psc_i2s->stats.underrun_count;
>  +       if (strcmp(name, "capture_overrun") == 0)
>  +               return &psc_i2s->stats.overrun_count;
>  +
>  +       return NULL;
>  +}
>  +
>  +static ssize_t psc_i2s_stat_show(struct device *dev,
>  +                                struct device_attribute *attr, char *buf)
>  +{
>  +       struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
>  +       int *attrib;
>  +
>  +       attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
>  +       if (!attrib)
>  +               return 0;
>  +
>  +       return sprintf(buf, "%i\n", *attrib);
>  +}
>  +
>  +static ssize_t psc_i2s_stat_store(struct device *dev,
>  +                                 struct device_attribute *attr,
>  +                                 const char *buf,
>  +                                 size_t count)
>  +{
>  +       struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
>  +       int *attrib;
>  +
>  +       attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
>  +       if (!attrib)
>  +               return 0;
>  +
>  +       *attrib = simple_strtoul(buf, NULL, 0);
>  +       return count;
>  +}
>  +
>  +DEVICE_ATTR(status, 0644, psc_i2s_status_show, NULL);
>  +DEVICE_ATTR(playback_underrun, 0644, psc_i2s_stat_show,psc_i2s_stat_store);
>  +DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show, psc_i2s_stat_store);
>  +
>  +/* ---------------------------------------------------------------------
>  + * OF platform bus binding code:
>  + * - Probe/remove operations
>  + * - OF device match table
>  + */
>  +static int __devinit psc_i2s_of_probe(struct of_device *op,
>  +                                     const struct of_device_id *match)
>  +{
>  +       phys_addr_t fifo;
>  +       struct psc_i2s *psc_i2s;
>  +       struct resource res;
>  +       int size, psc_id, irq, rc;
>  +       const __be32 *prop;
>  +       void __iomem *regs;
>  +
>  +       dev_dbg(&op->dev, "probing psc i2s device\n");
>  +
>  +       /* Get the PSC ID */
>  +       prop = of_get_property(op->node, "cell-index", &size);
>  +       if (!prop || size < sizeof *prop)
>  +               return -ENODEV;
>  +       psc_id = be32_to_cpu(*prop);
>  +
>  +       /* Fetch the registers and IRQ of the PSC */
>  +       irq = irq_of_parse_and_map(op->node, 0);
>  +       if (of_address_to_resource(op->node, 0, &res)) {
>  +               dev_err(&op->dev, "Missing reg property\n");
>  +               return -ENODEV;
>  +       }
>  +       regs = ioremap(res.start, 1 + res.end - res.start);
>  +       if (!regs) {
>  +               dev_err(&op->dev, "Could not map registers\n");
>  +               return -ENODEV;
>  +       }
>  +
>  +       /* Allocate and initialize the driver private data */
>  +       psc_i2s = kzalloc(sizeof *psc_i2s, GFP_KERNEL);
>  +       if (!psc_i2s) {
>  +               iounmap(regs);
>  +               return -ENOMEM;
>  +       }
>  +       spin_lock_init(&psc_i2s->lock);
>  +       psc_i2s->irq = irq;
>  +       psc_i2s->psc_regs = regs;
>  +       psc_i2s->fifo_regs = regs + sizeof *psc_i2s->psc_regs;
>  +       psc_i2s->dev = &op->dev;
>  +       psc_i2s->playback_stream.psc_i2s = psc_i2s;
>  +       psc_i2s->capture_stream.psc_i2s = psc_i2s;
>  +       snprintf(psc_i2s->name, sizeof psc_i2s->name, "PSC%u", psc_id+1);
>  +
>  +       /* Fill out the CPU DAI structure */
>  +       memcpy(&psc_i2s->dai, &psc_i2s_dai_template, sizeof psc_i2s->dai);
>  +       psc_i2s->dai.private_data = psc_i2s;
>  +       psc_i2s->dai.name = psc_i2s->name;
>  +       psc_i2s->dai.id = psc_id;
>  +
>  +       /* Find the address of the fifo data registers and setup the
>  +        * DMA tasks */
>  +       fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32);
>  +       psc_i2s->capture_stream.bcom_task =
>  +               bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512);
>  +       psc_i2s->playback_stream.bcom_task =
>  +               bcom_psc_gen_bd_tx_init(psc_id, 10, fifo);
>  +       if (!psc_i2s->capture_stream.bcom_task ||
>  +           !psc_i2s->playback_stream.bcom_task) {
>  +               dev_err(&op->dev, "Could not allocate bestcomm tasks\n");
>  +               iounmap(regs);
>  +               kfree(psc_i2s);
>  +               return -ENODEV;
>  +       }
>  +
>  +       /* Save what we've done so it can be found again later */
>  +       dev_set_drvdata(&op->dev, psc_i2s);
>  +
>  +       /* Register the SYSFS files */
>  +       rc = device_create_file(psc_i2s->dev, &dev_attr_status);
>  +       rc = device_create_file(psc_i2s->dev, &dev_attr_capture_overrun);
>  +       rc = device_create_file(psc_i2s->dev, &dev_attr_playback_underrun);
>  +       if (rc)
>  +               dev_info(psc_i2s->dev, "error creating sysfs files\n");
>  +
>  +       /* Tell the ASoC OF helpers about it */
>  +       of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node,
>  +                                    &psc_i2s->dai);
>  +
>  +       return 0;
>  +}
>  +
>  +static int __devexit psc_i2s_of_remove(struct of_device *op)
>  +{
>  +       struct psc_i2s *psc_i2s = dev_get_drvdata(&op->dev);
>  +
>  +       dev_dbg(&op->dev, "psc_i2s_remove()\n");
>  +
>  +       bcom_gen_bd_rx_release(psc_i2s->capture_stream.bcom_task);
>  +       bcom_gen_bd_tx_release(psc_i2s->playback_stream.bcom_task);
>  +
>  +       iounmap(psc_i2s->psc_regs);
>  +       iounmap(psc_i2s->fifo_regs);
>  +       kfree(psc_i2s);
>  +       dev_set_drvdata(&op->dev, NULL);
>  +
>  +       return 0;
>  +}
>  +
>  +/* Match table for of_platform binding */
>  +static struct of_device_id psc_i2s_match[] __devinitdata = {
>  +       { .compatible = "fsl,mpc5200-psc-i2s", },
>  +       {}
>  +};
>  +MODULE_DEVICE_TABLE(of, psc_i2s_match);
>  +
>  +static struct of_platform_driver psc_i2s_driver = {
>  +       .match_table = psc_i2s_match,
>  +       .probe = psc_i2s_of_probe,
>  +       .remove = __devexit_p(psc_i2s_of_remove),
>  +       .driver = {
>  +               .name = "mpc5200-psc-i2s",
>  +               .owner = THIS_MODULE,
>  +       },
>  +};
>  +
>  +/* ---------------------------------------------------------------------
>  + * Module setup and teardown; simply register the of_platform driver
>  + * for the PSC in I2S mode.
>  + */
>  +static int __init psc_i2s_init(void)
>  +{
>  +       return of_register_platform_driver(&psc_i2s_driver);
>  +}
>  +module_init(psc_i2s_init);
>  +
>  +static void __exit psc_i2s_exit(void)
>  +{
>  +       of_unregister_platform_driver(&psc_i2s_driver);
>  +}
>  +module_exit(psc_i2s_exit);
>  +
>  +
>
>  _______________________________________________
>  Alsa-devel mailing list
>  Alsa-devel@alsa-project.org
>  http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>


-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply

* Re: [alsa-devel] [PATCH 3/3] ALSA SoC: Add Texas Instruments TLV320AIC26 codec driver
From: Jon Smirl @ 2008-07-02 13:52 UTC (permalink / raw)
  To: Grant Likely; +Cc: liam.girdwood, alsa-devel, broonie, timur, linuxppc-dev
In-Reply-To: <20080701235340.16923.48024.stgit@trillian.secretlab.ca>

On 7/1/08, Grant Likely <grant.likely@secretlab.ca> wrote:
> From: Grant Likely <grant.likely@secretlab.ca>
>
>  ASoC Codec driver for the TLV320AIC26 device.  This driver uses the ASoC
>  v1 API, so I don't expect it to get merged as-is, but I want to get it
>  out there for review.
>  ---
>
>   sound/soc/codecs/Kconfig       |    4
>   sound/soc/codecs/Makefile      |    2
>   sound/soc/codecs/tlv320aic26.c |  630 ++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 636 insertions(+), 0 deletions(-)
>
>  diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
>  index 3903ab7..96c7bfe 100644
>  --- a/sound/soc/codecs/Kconfig
>  +++ b/sound/soc/codecs/Kconfig
>  @@ -41,6 +41,10 @@ config SND_SOC_CS4270_VD33_ERRATA
>         bool
>         depends on SND_SOC_CS4270
>
>  +config SND_SOC_TLV320AIC26
>  +       tristate "TI TLB320AIC26 Codec support"
>  +       depends on SND_SOC && SPI
>  +
>   config SND_SOC_TLV320AIC3X
>         tristate
>         depends on SND_SOC && I2C
>  diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
>  index 4e1314c..ec0cd93 100644
>  --- a/sound/soc/codecs/Makefile
>  +++ b/sound/soc/codecs/Makefile
>  @@ -5,6 +5,7 @@ snd-soc-wm8753-objs := wm8753.o
>   snd-soc-wm9712-objs := wm9712.o
>   snd-soc-wm9713-objs := wm9713.o
>   snd-soc-cs4270-objs := cs4270.o
>  +snd-soc-tlv320aic26-objs := tlv320aic26.o
>   snd-soc-tlv320aic3x-objs := tlv320aic3x.o
>
>   obj-$(CONFIG_SND_SOC_AC97_CODEC)       += snd-soc-ac97.o
>  @@ -14,4 +15,5 @@ obj-$(CONFIG_SND_SOC_WM8753)  += snd-soc-wm8753.o
>   obj-$(CONFIG_SND_SOC_WM9712)   += snd-soc-wm9712.o
>   obj-$(CONFIG_SND_SOC_WM9713)   += snd-soc-wm9713.o
>   obj-$(CONFIG_SND_SOC_CS4270)   += snd-soc-cs4270.o
>  +obj-$(CONFIG_SND_SOC_TLV320AIC26)      += snd-soc-tlv320aic26.o
>   obj-$(CONFIG_SND_SOC_TLV320AIC3X)      += snd-soc-tlv320aic3x.o
>  diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
>  new file mode 100644
>  index 0000000..aee1dbc
>  --- /dev/null
>  +++ b/sound/soc/codecs/tlv320aic26.c
>  @@ -0,0 +1,630 @@
>  +/*
>  + * Texas Instruments TLV320AIC26 low power audio CODEC
>  + * ALSA SoC CODEC driver
>  + *
>  + * Copyright (C) 2008 Secret Lab Technologies Ltd.
>  + */
>  +
>  +#include <linux/module.h>
>  +#include <linux/moduleparam.h>
>  +#include <linux/init.h>
>  +#include <linux/delay.h>
>  +#include <linux/pm.h>
>  +#include <linux/device.h>
>  +#include <linux/sysfs.h>
>  +#include <linux/spi/spi.h>
>  +#include <sound/core.h>
>  +#include <sound/pcm.h>
>  +#include <sound/pcm_params.h>
>  +#include <sound/soc.h>
>  +#include <sound/soc-dapm.h>
>  +#include <sound/soc-of.h>
>  +#include <sound/initval.h>
>  +
>  +MODULE_DESCRIPTION("ASoC TLV320AIC26 codec driver");
>  +MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
>  +MODULE_LICENSE("GPL");
>  +
>  +/* AIC26 Registers */
>  +#define AIC26_READ_COMMAND_WORD(addr)  ((1 << 15) | (addr << 5))
>  +#define AIC26_WRITE_COMMAND_WORD(addr) ((0 << 15) | (addr << 5))
>  +#define AIC26_PAGE_ADDR(page, offset)  ((page << 6) | offset)
>  +#define AIC26_NUM_REGS                 AIC26_PAGE_ADDR(3, 0)
>  +#define AIC26_REG_CACHE_SIZE           (0x20) /* only page 2 cached */
>  +#define AIC26_REG_IS_CACHED(addr)      ((addr & ~0x1f) == (2 << 6))
>  +#define AIC26_REG_CACHE_ADDR(addr)     (addr & 0x1f)
>  +
>  +/* Page 0: Auxillary data registers */
>  +#define AIC26_REG_BAT1                 AIC26_PAGE_ADDR(0, 0x05)
>  +#define AIC26_REG_BAT2                 AIC26_PAGE_ADDR(0, 0x06)
>  +#define AIC26_REG_AUX                  AIC26_PAGE_ADDR(0, 0x07)
>  +#define AIC26_REG_TEMP1                        AIC26_PAGE_ADDR(0, 0x09)
>  +#define AIC26_REG_TEMP2                        AIC26_PAGE_ADDR(0, 0x0A)
>  +
>  +/* Page 1: Auxillary control registers */
>  +#define AIC26_REG_AUX_ADC              AIC26_PAGE_ADDR(1, 0x00)
>  +#define AIC26_REG_STATUS               AIC26_PAGE_ADDR(1, 0x01)
>  +#define AIC26_REG_REFERENCE            AIC26_PAGE_ADDR(1, 0x03)
>  +#define AIC26_REG_RESET                        AIC26_PAGE_ADDR(1, 0x04)
>  +
>  +/* Page 2: Audio control registers */
>  +#define AIC26_REG_AUDIO_CTRL1          AIC26_PAGE_ADDR(2, 0x00)
>  +#define AIC26_REG_ADC_GAIN             AIC26_PAGE_ADDR(2, 0x01)
>  +#define AIC26_REG_DAC_GAIN             AIC26_PAGE_ADDR(2, 0x02)
>  +#define AIC26_REG_SIDETONE             AIC26_PAGE_ADDR(2, 0x03)
>  +#define AIC26_REG_AUDIO_CTRL2          AIC26_PAGE_ADDR(2, 0x04)
>  +#define AIC26_REG_POWER_CTRL           AIC26_PAGE_ADDR(2, 0x05)
>  +#define AIC26_REG_AUDIO_CTRL3          AIC26_PAGE_ADDR(2, 0x06)
>  +
>  +#define AIC26_REG_FILTER_COEFF_L_N0    AIC26_PAGE_ADDR(2, 0x07)
>  +#define AIC26_REG_FILTER_COEFF_L_N1    AIC26_PAGE_ADDR(2, 0x08)
>  +#define AIC26_REG_FILTER_COEFF_L_N2    AIC26_PAGE_ADDR(2, 0x09)
>  +#define AIC26_REG_FILTER_COEFF_L_N3    AIC26_PAGE_ADDR(2, 0x0A)
>  +#define AIC26_REG_FILTER_COEFF_L_N4    AIC26_PAGE_ADDR(2, 0x0B)
>  +#define AIC26_REG_FILTER_COEFF_L_N5    AIC26_PAGE_ADDR(2, 0x0C)
>  +#define AIC26_REG_FILTER_COEFF_L_D1    AIC26_PAGE_ADDR(2, 0x0D)
>  +#define AIC26_REG_FILTER_COEFF_L_D2    AIC26_PAGE_ADDR(2, 0x0E)
>  +#define AIC26_REG_FILTER_COEFF_L_D4    AIC26_PAGE_ADDR(2, 0x0F)
>  +#define AIC26_REG_FILTER_COEFF_L_D5    AIC26_PAGE_ADDR(2, 0x10)
>  +#define AIC26_REG_FILTER_COEFF_R_N0    AIC26_PAGE_ADDR(2, 0x11)
>  +#define AIC26_REG_FILTER_COEFF_R_N1    AIC26_PAGE_ADDR(2, 0x12)
>  +#define AIC26_REG_FILTER_COEFF_R_N2    AIC26_PAGE_ADDR(2, 0x13)
>  +#define AIC26_REG_FILTER_COEFF_R_N3    AIC26_PAGE_ADDR(2, 0x14)
>  +#define AIC26_REG_FILTER_COEFF_R_N4    AIC26_PAGE_ADDR(2, 0x15)
>  +#define AIC26_REG_FILTER_COEFF_R_N5    AIC26_PAGE_ADDR(2, 0x16)
>  +#define AIC26_REG_FILTER_COEFF_R_D1    AIC26_PAGE_ADDR(2, 0x17)
>  +#define AIC26_REG_FILTER_COEFF_R_D2    AIC26_PAGE_ADDR(2, 0x18)
>  +#define AIC26_REG_FILTER_COEFF_R_D4    AIC26_PAGE_ADDR(2, 0x19)
>  +#define AIC26_REG_FILTER_COEFF_R_D5    AIC26_PAGE_ADDR(2, 0x1A)
>  +
>  +#define AIC26_REG_PLL_PROG1            AIC26_PAGE_ADDR(2, 0x1B)
>  +#define AIC26_REG_PLL_PROG2            AIC26_PAGE_ADDR(2, 0x1C)
>  +#define AIC26_REG_AUDIO_CTRL4          AIC26_PAGE_ADDR(2, 0x1D)
>  +#define AIC26_REG_AUDIO_CTRL5          AIC26_PAGE_ADDR(2, 0x1E)
>  +
>  +#define AIC26_RATES    (SNDRV_PCM_RATE_8000  | SNDRV_PCM_RATE_11025 |\
>  +                        SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
>  +                        SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
>  +                        SNDRV_PCM_RATE_48000)
>  +#define AIC26_FORMATS  (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_BE |\
>  +                        SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
>  +
>  +/* fsref dividers; used in register 'Audio Control 1' */
>  +enum aic26_divisors {
>  +       AIC26_DIV_1     = 0,
>  +       AIC26_DIV_1_5   = 1,
>  +       AIC26_DIV_2     = 2,
>  +       AIC26_DIV_3     = 3,
>  +       AIC26_DIV_4     = 4,
>  +       AIC26_DIV_5     = 5,
>  +       AIC26_DIV_5_5   = 6,
>  +       AIC26_DIV_6     = 7,
>  +};
>  +
>  +/* Digital data format */
>  +enum aic26_datfm {
>  +       AIC26_DATFM_I2S         = 0 << 8,
>  +       AIC26_DATFM_DSP         = 1 << 8,
>  +       AIC26_DATFM_RIGHTJ      = 2 << 8, /* right justified */
>  +       AIC26_DATFM_LEFTJ       = 3 << 8, /* left justified */
>  +};
>  +
>  +/* Sample word length in bits; used in register 'Audio Control 1' */
>  +enum aic26_wlen {
>  +       AIC26_WLEN_16   = 0 << 10,
>  +       AIC26_WLEN_20   = 1 << 10,
>  +       AIC26_WLEN_24   = 2 << 10,
>  +       AIC26_WLEN_32   = 3 << 10,
>  +};
>  +
>  +/* AIC26 driver private data */
>  +struct aic26 {
>  +       struct spi_device *spi;
>  +       struct snd_soc_codec codec;
>  +       u16 reg_cache[AIC26_REG_CACHE_SIZE];    /* shadow registers */
>  +       int master;
>  +       int datfm;
>  +       int mclk;
>  +
>  +       /* Keyclick parameters */
>  +       int keyclick_amplitude;
>  +       int keyclick_freq;
>  +       int keyclick_len;
>  +};
>  +
>  +/* ---------------------------------------------------------------------
>  + * Register access routines
>  + */
>  +static unsigned int aic26_reg_read(struct snd_soc_codec *codec,
>  +                                  unsigned int reg)
>  +{
>  +       struct aic26 *aic26 = codec->private_data;
>  +       u16 *cache = codec->reg_cache;
>  +       u16 cmd, value;
>  +       u8 buffer[2];
>  +       int rc;
>  +
>  +       if ((reg < 0) || (reg >= AIC26_NUM_REGS)) {
>  +               WARN_ON_ONCE(1);
>  +               return 0;
>  +       }
>  +
>  +       /* Do SPI transfer; first 16bits are command; remaining is
>  +        * register contents */
>  +       cmd = AIC26_READ_COMMAND_WORD(reg);
>  +       buffer[0] = (cmd >> 8) & 0xff;
>  +       buffer[1] = cmd & 0xff;
>  +       rc = spi_write_then_read(aic26->spi, buffer, 2, buffer, 2);
>  +       if (rc) {
>  +               dev_err(&aic26->spi->dev, "AIC26 reg read error\n");
>  +               return -EIO;
>  +       }
>  +       value = (buffer[0] << 8) | buffer[1];
>  +
>  +       /* Update the cache before returning with the value */
>  +       if (AIC26_REG_IS_CACHED(reg))
>  +               cache[AIC26_REG_CACHE_ADDR(reg)] = value;
>  +       return value;
>  +}
>  +
>  +static unsigned int aic26_reg_read_cache(struct snd_soc_codec *codec,
>  +                                        unsigned int reg)
>  +{
>  +       u16 *cache = codec->reg_cache;
>  +
>  +       if ((reg < 0) || (reg >= AIC26_NUM_REGS)) {
>  +               WARN_ON_ONCE(1);
>  +               return 0;
>  +       }
>  +
>  +       if (AIC26_REG_IS_CACHED(reg))
>  +               return cache[AIC26_REG_CACHE_ADDR(reg)];
>  +
>  +       return aic26_reg_read(codec, reg);
>  +}
>  +
>  +static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg,
>  +                          unsigned int value)
>  +{
>  +       struct aic26 *aic26 = codec->private_data;
>  +       u16 *cache = codec->reg_cache;
>  +       u16 cmd;
>  +       u8 buffer[4];
>  +       int rc;
>  +
>  +       if ((reg < 0) || (reg >= AIC26_NUM_REGS)) {
>  +               WARN_ON_ONCE(1);
>  +               return -EINVAL;
>  +       }
>  +
>  +       /* Do SPI transfer; first 16bits are command; remaining is data
>  +        * to write into register */
>  +       cmd = AIC26_WRITE_COMMAND_WORD(reg);
>  +       buffer[0] = (cmd >> 8) & 0xff;
>  +       buffer[1] = cmd & 0xff;
>  +       buffer[2] = value >> 8;
>  +       buffer[3] = value;
>  +       rc = spi_write(aic26->spi, buffer, 4);
>  +       if (rc) {
>  +               dev_err(&aic26->spi->dev, "AIC26 reg read error\n");
>  +               return -EIO;
>  +       }
>  +
>  +       /* update cache before returning */
>  +       if (AIC26_REG_IS_CACHED(reg))
>  +               cache[AIC26_REG_CACHE_ADDR(reg)] = value;
>  +       return 0;
>  +}
>  +
>  +/* ---------------------------------------------------------------------
>  + * Digital Audio Interface Operations
>  + */
>  +static int aic26_hw_params(struct snd_pcm_substream *substream,
>  +                          struct snd_pcm_hw_params *params)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct snd_soc_device *socdev = rtd->socdev;
>  +       struct snd_soc_codec *codec = socdev->codec;
>  +       struct aic26 *aic26 = codec->private_data;
>  +       int fsref, divisor, wlen, pval, jval, dval, qval;
>  +       u16 reg;
>  +
>  +       dev_dbg(&aic26->spi->dev, "aic26_hw_params(substream=%p, params=%p)\n",
>  +               substream, params);
>  +       dev_dbg(&aic26->spi->dev, "rate=%i format=%i\n", params_rate(params),
>  +               params_format(params));
>  +
>  +       switch (params_rate(params)) {
>  +        case 8000: fsref = 48000; divisor = AIC26_DIV_6; break;
>  +        case 11025: fsref = 44100; divisor = AIC26_DIV_4; break;
>  +        case 12000: fsref = 48000; divisor = AIC26_DIV_4; break;
>  +        case 16000: fsref = 48000; divisor = AIC26_DIV_3; break;
>  +        case 22050: fsref = 44100; divisor = AIC26_DIV_2; break;
>  +        case 24000: fsref = 48000; divisor = AIC26_DIV_2; break;
>  +        case 32000: fsref = 48000; divisor = AIC26_DIV_1_5; break;
>  +        case 44100: fsref = 44100; divisor = AIC26_DIV_1; break;
>  +        case 48000: fsref = 48000; divisor = AIC26_DIV_1; break;
>  +        default: dev_dbg(&aic26->spi->dev, "bad rate\n"); return -EINVAL;
>  +       }
>  +
>  +       /* select data word length */
>  +       switch (params_format(params)) {
>  +        case SNDRV_PCM_FORMAT_S8: wlen = AIC26_WLEN_16; break;
>  +        case SNDRV_PCM_FORMAT_S16_BE: wlen = AIC26_WLEN_16; break;
>  +        case SNDRV_PCM_FORMAT_S24_BE: wlen = AIC26_WLEN_24; break;
>  +        case SNDRV_PCM_FORMAT_S32_BE: wlen = AIC26_WLEN_32; break;
>  +        default: dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL;
>  +       }
>  +
>  +       /* Configure PLL */
>  +       pval = 1;
>  +       jval = (fsref == 44100) ? 7 : 8;
>  +       dval = (fsref == 44100) ? 5264 : 1920;
>  +       qval = 0;
>  +       reg = 0x8000 | qval << 11 | pval << 8 | jval << 2;
>  +       aic26_reg_write(codec, AIC26_REG_PLL_PROG1, reg);
>  +       reg = dval << 2;
>  +       aic26_reg_write(codec, AIC26_REG_PLL_PROG2, reg);
>  +
>  +       /* Power up CODEC */
>  +       aic26_reg_write(codec, AIC26_REG_POWER_CTRL, 0);
>  +
>  +       /* Audio Control 3 (master mode, fsref rate) */
>  +       reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL3);
>  +       reg &= ~0xf800;
>  +       if (aic26->master)
>  +               reg |= 0x0800;
>  +       if (fsref == 48000)
>  +               reg |= 0x2000;
>  +       aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
>  +
>  +       /* Audio Control 1 (FSref divisor) */
>  +       reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL1);
>  +       reg &= ~0x0fff;
>  +       reg |= wlen | aic26->datfm | (divisor << 3) | divisor;
>  +       aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL1, reg);
>  +
>  +       return 0;
>  +}
>  +
>  +/**
>  + * aic26_mute - Mute control to reduce noise when changing audio format
>  + */
>  +static int aic26_mute(struct snd_soc_codec_dai *dai, int mute)
>  +{
>  +       struct snd_soc_codec *codec = dai->codec;
>  +       struct aic26 *aic26 = codec->private_data;
>  +       u16 reg = aic26_reg_read_cache(codec, AIC26_REG_DAC_GAIN);
>  +
>  +       dev_dbg(&aic26->spi->dev, "aic26_mute(dai=%p, mute=%i)\n",
>  +               dai, mute);
>  +
>  +       if (mute)
>  +               reg |= 0x8080;
>  +       else
>  +               reg &= ~0x8080;
>  +       aic26_reg_write(codec, AIC26_REG_DAC_GAIN, reg);
>  +
>  +       return 0;
>  +}
>  +
>  +static int aic26_set_sysclk(struct snd_soc_codec_dai *codec_dai,
>  +                           int clk_id, unsigned int freq, int dir)
>  +{
>  +       struct snd_soc_codec *codec = codec_dai->codec;
>  +       struct aic26 *aic26 = codec->private_data;
>  +
>  +       dev_dbg(&aic26->spi->dev, "aic26_set_sysclk(dai=%p, clk_id==%i,"
>  +               " freq=%i, dir=%i)\n",
>  +               codec_dai, clk_id, freq, dir);
>  +
>  +       /* MCLK needs to fall between 2MHz and 50 MHz */
>  +       if ((freq < 2000000) || (freq > 50000000))
>  +               return -EINVAL;
>  +
>  +       aic26->mclk = freq;
>  +       return 0;
>  +}
>  +
>  +static int aic26_set_fmt(struct snd_soc_codec_dai *codec_dai, unsigned int fmt)
>  +{
>  +       struct snd_soc_codec *codec = codec_dai->codec;
>  +       struct aic26 *aic26 = codec->private_data;
>  +
>  +       dev_dbg(&aic26->spi->dev, "aic26_set_fmt(dai=%p, fmt==%i)\n",
>  +               codec_dai, fmt);
>  +
>  +       /* set master/slave audio interface */
>  +       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
>  +        case SND_SOC_DAIFMT_CBM_CFM: aic26->master = 1; break;
>  +        //case SND_SOC_DAIFMT_CBS_CFS: aic26->master = 0; break;
>  +        default: dev_dbg(&aic26->spi->dev, "bad master\n"); return -EINVAL;
>  +       }
>  +
>  +       /* interface format */
>  +       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
>  +        case SND_SOC_DAIFMT_I2S: aic26->datfm = AIC26_DATFM_I2S; break;
>  +        case SND_SOC_DAIFMT_DSP_A: aic26->datfm = AIC26_DATFM_DSP; break;
>  +        case SND_SOC_DAIFMT_RIGHT_J: aic26->datfm = AIC26_DATFM_RIGHTJ; break;
>  +        case SND_SOC_DAIFMT_LEFT_J: aic26->datfm = AIC26_DATFM_LEFTJ; break;
>  +        default: dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL;
>  +       }
>  +
>  +       return 0;
>  +}
>  +
>  +/* ---------------------------------------------------------------------
>  + * Digital Audio Interface Definition
>  + */
>  +struct snd_soc_codec_dai aic26_dai = {
>  +       .name = "tlv320aic26",
>  +       .playback = {
>  +               .stream_name = "Playback",
>  +               .channels_min = 2,
>  +               .channels_max = 2,
>  +               .rates = AIC26_RATES,
>  +               .formats = AIC26_FORMATS,
>  +       },
>  +       .capture = {
>  +               .stream_name = "Capture",
>  +               .channels_min = 2,
>  +               .channels_max = 2,
>  +               .rates = AIC26_RATES,
>  +               .formats = AIC26_FORMATS,
>  +       },
>  +       .ops = {
>  +               .hw_params = aic26_hw_params,
>  +       },
>  +       .dai_ops = {
>  +               .digital_mute = aic26_mute,
>  +               .set_sysclk = aic26_set_sysclk,
>  +               .set_fmt = aic26_set_fmt,
>  +       },
>  +};
>  +EXPORT_SYMBOL_GPL(aic26_dai);
>  +
>  +/* ---------------------------------------------------------------------
>  + * ALSA controls
>  + */
>  +static const char *aic26_capture_src_text[] = {"Mic", "Aux"};
>  +static const struct soc_enum aic26_capture_src_enum =
>  +       SOC_ENUM_SINGLE(AIC26_REG_AUDIO_CTRL1, 12,2, aic26_capture_src_text);
>  +
>  +static const struct snd_kcontrol_new aic26_snd_controls[] = {
>  +       /* Output */
>  +       SOC_DOUBLE("PCM Playback Volume", AIC26_REG_DAC_GAIN, 8, 0, 0x7f, 1),
>  +       SOC_DOUBLE("PCM Playback Switch", AIC26_REG_DAC_GAIN, 15, 7, 1, 1),
>  +       SOC_SINGLE("PCM Capture Volume", AIC26_REG_ADC_GAIN, 8, 0x7f, 0),
>  +       SOC_SINGLE("PCM Capture Mute", AIC26_REG_ADC_GAIN, 15, 1, 1),
>  +       SOC_ENUM("Capture Source", aic26_capture_src_enum),
>  +};
>  +
>  +/* ---------------------------------------------------------------------
>  + * SoC CODEC portion of driver: probe and release routines
>  + */
>  +static int aic26_probe(struct platform_device *pdev)
>  +{
>  +       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
>  +       struct snd_soc_codec *codec;
>  +       struct snd_kcontrol *kcontrol;
>  +       struct aic26 *aic26;
>  +       int i, ret, err;
>  +
>  +       dev_info(&pdev->dev, "Probing AIC26 SoC CODEC driver\n");
>  +       dev_dbg(&pdev->dev, "socdev=%p\n", socdev);
>  +       dev_dbg(&pdev->dev, "codec_data=%p\n", socdev->codec_data);
>  +
>  +       /* Fetch the relevant aic26 private data here (it's already been
>  +        * stored in the .codec pointer) */
>  +       aic26 = socdev->codec_data;
>  +       if (aic26 == NULL) {
>  +               dev_err(&pdev->dev, "aic26: missing codec pointer\n");
>  +               return -ENODEV;
>  +       }
>  +       codec = &aic26->codec;
>  +       socdev->codec = codec;
>  +
>  +       dev_dbg(&pdev->dev, "Registering PCMs, dev=%p, socdev->dev=%p\n",
>  +               &pdev->dev, socdev->dev);
>  +       /* register pcms */
>  +       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
>  +       if (ret < 0) {
>  +               dev_err(&pdev->dev, "aic26: failed to create pcms\n");
>  +               return -ENODEV;
>  +       }
>  +
>  +       /* register controls */
>  +       dev_dbg(&pdev->dev, "Registering controls\n");
>  +       for (i = 0; i < ARRAY_SIZE(aic26_snd_controls); i++) {
>  +               kcontrol = snd_soc_cnew(&aic26_snd_controls[i], codec, NULL);
>  +               err = snd_ctl_add(codec->card, kcontrol);
>  +               WARN_ON(err < 0);
>  +       }
>  +
>  +       /* CODEC is setup, we can register the card now */
>  +       dev_dbg(&pdev->dev, "Registering card\n");
>  +       ret = snd_soc_register_card(socdev);
>  +       if (ret < 0) {
>  +               dev_err(&pdev->dev, "aic26: failed to register card\n");
>  +               goto card_err;
>  +       }
>  +       return 0;
>  +
>  + card_err:
>  +       snd_soc_free_pcms(socdev);
>  +       return ret;
>  +}
>  +
>  +static int aic26_remove(struct platform_device *pdev)
>  +{
>  +       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
>  +       snd_soc_free_pcms(socdev);
>  +       return 0;
>  +}
>  +
>  +struct snd_soc_codec_device aic26_soc_codec_dev = {
>  +       .probe = aic26_probe,
>  +       .remove = aic26_remove,
>  +};
>  +
>  +/* ---------------------------------------------------------------------
>  + * SPI device portion of driver: sysfs files for debugging
>  + */
>  +
>  +static ssize_t aic26_regs_show(struct device *dev,
>  +                               struct device_attribute *attr, char *buf)
>  +{
>  +       struct aic26 *aic26 = dev_get_drvdata(dev);
>  +       char *idx = buf;
>  +       int cache_flag, addr, page, i, reg;
>  +
>  +       cache_flag = (strcmp(attr->attr.name, "regs_cache") == 0);
>  +
>  +       for (page = 0; page < 3; page++) {
>  +               for (i = 0; i < 0x20; i++) {
>  +                       addr = AIC26_PAGE_ADDR(page, i);
>  +                       if (i % 8 == 0)
>  +                               idx += sprintf(idx, "%i:%.2i:", page,i);
>  +                       if (cache_flag)
>  +                               reg = aic26_reg_read_cache(&aic26->codec, addr);
>  +                       else
>  +                               reg = aic26_reg_read(&aic26->codec, addr);
>  +                       idx += sprintf(idx, " %.4x", reg);
>  +                       if (i % 8 == 7)
>  +                               idx += sprintf(idx, "\n");
>  +               }
>  +       }
>  +       return idx - buf;
>  +}
>  +
>  +static ssize_t aic26_keyclick_show(struct device *dev,
>  +                                  struct device_attribute *attr, char *buf)
>  +{
>  +       struct aic26 *aic26 = dev_get_drvdata(dev);
>  +       int val, amp, freq, len;
>  +
>  +       val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2);
>  +       amp = (val >> 12) & 0x7;
>  +       freq = (125 << ((val >> 8) & 0x7)) >> 1;
>  +       len = 2 * (1 +((val >> 8) & 0xf));
>  +
>  +       return sprintf(buf, "amp=%x freq=%iHz len=%iclks\n", amp, freq, len);
>  +}
>  +
>  +/* Any write to the keyclick attribute will trigger the keyclick */
>  +static ssize_t aic26_keyclick_set(struct device *dev,
>  +                                 struct device_attribute *attr,
>  +                                 const char *buf, size_t count)
>  +{
>  +       struct aic26 *aic26 = dev_get_drvdata(dev);
>  +       int val;
>  +
>  +       val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2);
>  +       val |= 0x8000;
>  +       aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL2, val);
>  +
>  +       return count;
>  +}
>  +
>  +DEVICE_ATTR(regs, 0644, aic26_regs_show, NULL);
>  +DEVICE_ATTR(regs_cache, 0644, aic26_regs_show, NULL);
>  +DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set);
>  +
>  +/* ---------------------------------------------------------------------
>  + * SPI device portion of driver: probe and release routines and SPI
>  + *                              driver registration.
>  + */
>  +static int aic26_spi_probe(struct spi_device *spi)
>  +{
>  +       struct aic26 *aic26;
>  +       int rc, i, reg;
>  +
>  +       dev_dbg(&spi->dev, "probing tlv320aic26 spi device\n");
>  +
>  +       /* Allocate driver data */
>  +       aic26 = kzalloc(sizeof *aic26, GFP_KERNEL);
>  +       if (!aic26)
>  +               return -ENOMEM;
>  +
>  +       /* Initialize the driver data */
>  +       aic26->spi = spi;
>  +       dev_set_drvdata(&spi->dev, aic26);
>  +
>  +       /* Setup what we can in the codec structure so that the register
>  +        * access functions will work as expected.  More will be filled
>  +        * out when it is probed by the SoC CODEC part of this driver */
>  +       aic26->codec.private_data = aic26;
>  +       aic26->codec.name = "aic26";
>  +       aic26->codec.owner = THIS_MODULE;
>  +       aic26->codec.dai = &aic26_dai;
>  +       aic26->codec.num_dai = 1;
>  +       aic26->codec.read = aic26_reg_read;
>  +       aic26->codec.write = aic26_reg_write;
>  +       aic26->master = 1;
>  +       mutex_init(&aic26->codec.mutex);
>  +       INIT_LIST_HEAD(&aic26->codec.dapm_widgets);
>  +       INIT_LIST_HEAD(&aic26->codec.dapm_paths);
>  +       aic26->codec.reg_cache_size = sizeof(aic26->reg_cache);
>  +       aic26->codec.reg_cache = aic26->reg_cache;
>  +
>  +       /* Reset the codec to power on defaults */
>  +       aic26_reg_write(&aic26->codec, AIC26_REG_RESET, 0xBB00);
>  +
>  +       /* Power up CODEC */
>  +       aic26_reg_write(&aic26->codec, AIC26_REG_POWER_CTRL, 0);
>  +
>  +       /* Audio Control 3 (master mode, fsref rate) */
>  +       reg = aic26_reg_read(&aic26->codec, AIC26_REG_AUDIO_CTRL3);
>  +       reg &= ~0xf800;
>  +       reg |= 0x0800; /* set master mode */
>  +       aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL3, reg);
>  +
>  +       /* Fill page 2 register cache */
>  +       for (i = 0; i < ARRAY_SIZE(aic26->reg_cache); i++)
>  +               aic26_reg_read(&aic26->codec, AIC26_PAGE_ADDR(2, i));
>  +
>  +       /* Register the sysfs files for debugging */
>  +       /* Create SysFS files */
>  +       rc = device_create_file(&spi->dev, &dev_attr_regs);
>  +       rc |= device_create_file(&spi->dev, &dev_attr_regs_cache);
>  +       rc |= device_create_file(&spi->dev, &dev_attr_keyclick);
>  +       if (rc)
>  +               dev_info(&spi->dev, "error creating sysfs files\n");
>  +
>  +       /* Tell the of_soc helper about this codec */
>  +       of_snd_soc_register_codec(&aic26_soc_codec_dev, aic26, &aic26_dai,
>  +                                 spi->dev.archdata.of_node);

I've been using asoc-v2 so it may not be possible to make this arch
independent in asoc-v1.  Maybe try porting this to asoc-v2 and see if
a bunch of the registration complexity disappears. Most of soc-of.c
should be unnecessary.

Liam, when is asoc-v2 going into mainline?


>  +
>  +       dev_dbg(&spi->dev, "SPI device initialized\n");
>  +       return 0;
>  +}
>  +
>  +static int aic26_spi_remove(struct spi_device *spi)
>  +{
>  +       struct aic26 *aic26 = dev_get_drvdata(&spi->dev);
>  +
>  +       kfree(aic26);
>  +
>  +       return 0;
>  +}
>  +
>  +static struct spi_driver aic26_spi = {
>  +       .driver = {
>  +               .name = "tlv320aic26",
>  +               .owner = THIS_MODULE,
>  +       },
>  +       .probe = aic26_spi_probe,
>  +       .remove = aic26_spi_remove,
>  +};
>  +
>  +static int __init aic26_init(void)
>  +{
>  +       return spi_register_driver(&aic26_spi);
>  +}
>  +module_init(aic26_init);
>  +
>  +static void __exit aic26_exit(void)
>  +{
>  +       spi_unregister_driver(&aic26_spi);
>  +}
>  +module_exit(aic26_exit);
>
>  _______________________________________________
>  Alsa-devel mailing list
>  Alsa-devel@alsa-project.org
>  http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>


-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply

* Re: [PATCH] powerpc/85xx: Add support for MPC8536DS
From: Chen Gong @ 2008-07-02 14:04 UTC (permalink / raw)
  To: Stephen Rothwell; +Cc: linuxppc-dev
In-Reply-To: <20080702170814.de282293.sfr@canb.auug.org.au>

Stephen Rothwell 写道:
> Hi Kumar,
> 
> On Wed, 2 Jul 2008 02:01:10 -0500 (CDT) Kumar Gala <galak@kernel.crashing.org> wrote:
>> +void __init mpc8536_ds_pic_init(void)
>> +{
>> +	struct mpic *mpic;
>> +	struct resource r;
>> +	struct device_node *np = NULL;
> 
> You don't need to initialise this.
> 
>> +	np = of_find_node_by_type(np, "open-pic");
>> +
> 
> Extra blank line.
> 
>> +	if (np == NULL) {
>> +		printk(KERN_ERR "Could not find open-pic node\n");
>> +		return;
>> +	}
>> +
>> +	if (of_address_to_resource(np, 0, &r)) {
>> +		printk(KERN_ERR "Failed to map mpic register space\n");
>> +		of_node_put(np);
>> +		return;
>> +	}
>> +
>> +	mpic = mpic_alloc(np, r.start,
>> +			  MPIC_PRIMARY | MPIC_WANTS_RESET |
>> +			  MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS,
>> +			0, 256, " OpenPIC  ");
>> +	BUG_ON(mpic == NULL);
>> +
>> +	mpic_init(mpic);
> 
> You leak a reference to np here.

It looks like there are same problem in other mpc85xx_xx.c files

> 
>> +static struct of_device_id mpc8536_ds_ids[] = {
> 
> __initdata, please.
> 
> 
> 
> ------------------------------------------------------------------------
> 
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev

^ permalink raw reply

* RE: [PATCH] powerpc: Xilinx: add dts file for ML507 board
From: John Linn @ 2008-07-02 14:10 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev, Stephen Neuendorffer
In-Reply-To: <20080701205301.1acdf087@zod.rchland.ibm.com>

Hi Josh,

It's looking like I need to update this anyway for some changes to our
reference system I just learned about.

I'll talk with Stephen about dts-v1 as he does most of the dts
generation work.

Thanks,
John

-----Original Message-----
From: Josh Boyer [mailto:jwboyer@linux.vnet.ibm.com] =

Sent: Tuesday, July 01, 2008 6:53 PM
To: John Linn
Cc: linuxppc-dev@ozlabs.org; grant.likely@secretlab.ca; John Linn
Subject: Re: [PATCH] powerpc: Xilinx: add dts file for ML507 board

On Tue, 1 Jul 2008 16:02:54 -0700
John Linn <john.linn@xilinx.com> wrote:

> This new file adds support for the ML507 board which
> has a Virtex 5 FXT FPGA with a 440.

I haven't looked this over thoroughly yet, but an immediate question
comes to mind.  Any way this can get converted to dts-v1 before it's
pulled in?  All of the in-tree DTS files should be dts-v1 now, so I'd
like to have new ones go in as that.

(Also CC'ing the proper list)

josh


This email and any attachments are intended for the sole use of the named r=
ecipient(s) and contain(s) confidential information that may be proprietary=
, privileged or copyrighted under applicable law. If you are not the intend=
ed recipient, do not read, copy, or forward this email message or any attac=
hments. Delete this email message and any attachments immediately.

^ permalink raw reply

* Re: [PATCH v2 1/2] powerpc: Fix building of feature-fixup tests on ppc32
From: Kumar Gala @ 2008-07-02 14:31 UTC (permalink / raw)
  To: michael; +Cc: linuxppc-dev
In-Reply-To: <1214991302.7436.7.camel@localhost>


On Jul 2, 2008, at 4:35 AM, Michael Ellerman wrote:

> On Tue, 2008-07-01 at 10:16 -0500, Kumar Gala wrote:
>> We need to use PPC_LCMPI otherwise we get compile errors like:
>>
>> arch/powerpc/lib/feature-fixups-test.S: Assembler messages:
>> arch/powerpc/lib/feature-fixups-test.S:142: Error: Unrecognized  
>> opcode: `cmpdi'
>> arch/powerpc/lib/feature-fixups-test.S:149: Error: Unrecognized  
>> opcode: `cmpdi'
>> arch/powerpc/lib/feature-fixups-test.S:164: Error: Unrecognized  
>> opcode: `cmpdi'
>
> Sorry, I definitely built (and booted) a 32-bit config, so I'm not  
> sure
> how I missed that. What toolchain are you using?

gcc-4.3.0 from fedora 9 on a G5.

- k

^ permalink raw reply

* Re: [PATCH 1/2] Convert i2c-mpc from a platform driver into  a of_platform driver, V4
From: Jean Delvare @ 2008-07-02 14:36 UTC (permalink / raw)
  To: Wolfram Sang; +Cc: Linuxppc-dev, i2c
In-Reply-To: <20080702133343.GC4253@pengutronix.de>

On Wed, 2 Jul 2008 15:33:43 +0200, Wolfram Sang wrote:
> Hi Jean,
> 
> On Tue, Jul 01, 2008 at 06:14:44PM +0200, Jean Delvare wrote:
> 
> > Applied, after fixing the patch so that it applies, fixing it again so
> > that it is correct in the polling case, and fixing it again to make it
> > pass checkpatch.pl.
> So, it cannot be applied directly? :(
> 
> Could you repost the correct version perhaps? I'd like to test it, but
> can't find it at http://khali.linux-fr.org/devel/linux-2.6/jdelvare-i2c/
> (because it is scheduled for 2.6.27?).

It's at:
http://khali.linux-fr.org/devel/linux-2.6/jdelvare-i2c/i2c-mpc-convert-to-an-of_platform-driver.patch

It was simply a couple days since I had updated this public directory,
sorry.

-- 
Jean Delvare

^ permalink raw reply

* Please pull linux-2.6-mpc52xx.git for 2.6.26
From: Grant Likely @ 2008-07-02 14:37 UTC (permalink / raw)
  To: linuxppc-dev, paulus

Hi Paul,

Here is one more last minute 2.6.26 bug fix.

The following changes since commit 1702b52092e9a6d05398d3f9581ddc050ef00d06:
  Linus Torvalds (1):
        Merge git://git.kernel.org/.../mchehab/v4l-dvb

are available in the git repository at:

  git://git.secretlab.ca/git/linux-2.6-mpc52xx.git for-2.6.26

Tim Yamin (1):
      powerpc/mpc5200: Fix lite5200b suspend/resume

 arch/powerpc/platforms/52xx/lite5200_pm.c |   14 +++++++++++++-
 1 files changed, 13 insertions(+), 1 deletions(-)

^ permalink raw reply

* [PATCH] powerpc/85xx: minor fixes for MPC85xx DS board port
From: Kumar Gala @ 2008-07-02 15:03 UTC (permalink / raw)
  To: linuxppc-dev

These issues were reported by Stephen Rothwell for another 85xx board
port and pointed out by Chen Gong as issues in the DS port.

* mpic OF node reference counting was off
* of_device_id struct should be marked as __initdata

Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
in my powerpc-next tree.

- k

 arch/powerpc/platforms/85xx/mpc85xx_ds.c |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ds.c b/arch/powerpc/platforms/85xx/mpc85xx_ds.c
index b010dc9..8b2d63d 100644
--- a/arch/powerpc/platforms/85xx/mpc85xx_ds.c
+++ b/arch/powerpc/platforms/85xx/mpc85xx_ds.c
@@ -58,14 +58,13 @@ void __init mpc85xx_ds_pic_init(void)
 {
 	struct mpic *mpic;
 	struct resource r;
-	struct device_node *np = NULL;
+	struct device_node *np;
 #ifdef CONFIG_PPC_I8259
 	struct device_node *cascade_node = NULL;
 	int cascade_irq;
 #endif

 	np = of_find_node_by_type(np, "open-pic");
-
 	if (np == NULL) {
 		printk(KERN_ERR "Could not find open-pic node\n");
 		return;
@@ -82,6 +81,7 @@ void __init mpc85xx_ds_pic_init(void)
 			  MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS,
 			0, 256, " OpenPIC  ");
 	BUG_ON(mpic == NULL);
+	of_node_put(np);

 	mpic_init(mpic);

@@ -185,7 +185,7 @@ static int __init mpc8544_ds_probe(void)
 	}
 }

-static struct of_device_id mpc85xxds_ids[] = {
+static struct of_device_id __initdata mpc85xxds_ids[] = {
 	{ .type = "soc", },
 	{ .compatible = "soc", },
 	{},
-- 
1.5.5.1

^ permalink raw reply related

* Re: [PATCH] powerpc/85xx: Add support for MPC8536DS
From: Kumar Gala @ 2008-07-02 15:03 UTC (permalink / raw)
  To: Chen Gong; +Cc: Stephen Rothwell, linuxppc-dev
In-Reply-To: <486B8AFE.7060303@gmail.com>


On Jul 2, 2008, at 9:04 AM, Chen Gong wrote:

> Stephen Rothwell =D0=B4=B5=C0:
>> Hi Kumar,
>>
>> On Wed, 2 Jul 2008 02:01:10 -0500 (CDT) Kumar Gala =
<galak@kernel.crashing.org=20
>> > wrote:
>>> +void __init mpc8536_ds_pic_init(void)
>>> +{
>>> +	struct mpic *mpic;
>>> +	struct resource r;
>>> +	struct device_node *np =3D NULL;
>>
>> You don't need to initialise this.
>>
>>> +	np =3D of_find_node_by_type(np, "open-pic");
>>> +
>>
>> Extra blank line.
>>
>>> +	if (np =3D=3D NULL) {
>>> +		printk(KERN_ERR "Could not find open-pic node\n");
>>> +		return;
>>> +	}
>>> +
>>> +	if (of_address_to_resource(np, 0, &r)) {
>>> +		printk(KERN_ERR "Failed to map mpic register space\n");
>>> +		of_node_put(np);
>>> +		return;
>>> +	}
>>> +
>>> +	mpic =3D mpic_alloc(np, r.start,
>>> +			  MPIC_PRIMARY | MPIC_WANTS_RESET |
>>> +			  MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS,
>>> +			0, 256, " OpenPIC  ");
>>> +	BUG_ON(mpic =3D=3D NULL);
>>> +
>>> +	mpic_init(mpic);
>>
>> You leak a reference to np here.
>
> It looks like there are same problem in other mpc85xx_xx.c files

agreed for the mpc85xx_ds.c, I dont see the _cds or _ads having these =20=

issues.

- k

^ permalink raw reply

* Re: [PATCH v2] Change the default link address for pSeries zImage kernels.
From: Olaf Hering @ 2008-07-02 15:04 UTC (permalink / raw)
  To: Tony Breeds; +Cc: linuxppc-dev, Paul Mackerras
In-Reply-To: <4ca08ab3df251d90440c962ef15ead1a72d75f05.1214276944.git.tony@bakeyournoodle.com>

On Tue, Jun 24, Tony Breeds wrote:

> Currently we set the start of the .text section to be 4Mb for pSeries.
> In situations where the zImage is > 8Mb we'll fail to boot (due to
> overlapping with OF).  Move .text in a zImage from 4MB to 64MB (well past OF).
> 
> We still will not be able to load large zImage unless we also move OF,
> to that end, add a note to the zImage ELF to move OF to 32Mb.  If this
> is the very first kernel booted then we'll need to move OF manually by
> setting real-base.

Setting real-base to what?

What currently happens with a large boot file is:
Firmware loads the zImage at load-base, finds that the ELF file is too
large to fit into the memory window and stops.

With your patch, firmware loads 12566528 bytes, and starts the zImage.
The result is a truncated file, the initrd will be corrupted, kernel
panic in populate_rootfs().

The only system where firmware relocates itself from 12MB to 32MB is a
p640 with firmware version NAN04194.
All other systems seem to ignore the NOTE section, real-base remains at
0xc00000

So I do not think your patch is a real improvement,
clear error vs. silent corruption.

Do you happen to know how to automate the changing the value of
real-base? The addnote change has appearently no effect on recent
systems.

Olaf

^ permalink raw reply

* Re: Commits added to powerpc.git master and powerpc-next branches
From: Kumar Gala @ 2008-07-02 15:10 UTC (permalink / raw)
  To: Jochen Friedrich; +Cc: linuxppc-dev, Paul Mackerras
In-Reply-To: <486B7619.4070603@scram.de>


On Jul 2, 2008, at 7:35 AM, Jochen Friedrich wrote:

> Hi Kumar,
>
>>> any chance to get this one into 2.6.27?
>>>
>>> [POWERPC] Add i2c pins to dts and board setup
>>> http://patchwork.ozlabs.org/linuxppc/patch?person=1023&id=18603
>>
>> Will we always want defconfig updates to enable the driver?
>
> Good question. Maybe not, as the i2c bus seems to be unused on these
> boards unless someone connects some i2c devices.
>
> I'll resubmit just the booting-without-of part then.

Actually, if that's the case I'd prefer to leave the patch is as and  
just make a note about the fact that the I2C bus isn't connected to  
anything on the board in the commit message.  The code is a good  
example of setting everything else up and I'd like to have that in the  
tree.

- k

^ permalink raw reply

* Re: [alsa-devel] [PATCH 2/3] ALSA SoC: Add mpc5200-psc I2S driver
From: Jon Smirl @ 2008-07-02 15:19 UTC (permalink / raw)
  To: Grant Likely; +Cc: liam.girdwood, alsa-devel, broonie, timur, linuxppc-dev
In-Reply-To: <20080701235335.16923.43253.stgit@trillian.secretlab.ca>

On 7/1/08, Grant Likely <grant.likely@secretlab.ca> wrote:
> From: Grant Likely <grant.likely@secretlab.ca>
>
>  This is an I2S bus driver for the MPC5200 PSC device.  It is probably
>  will not be merged as-is because it uses v1 of the ASoC API, but I want
>  to get it out there for comments.
>  ---
>
>   sound/soc/fsl/Kconfig           |    6
>   sound/soc/fsl/Makefile          |    2
>   sound/soc/fsl/mpc5200_psc_i2s.c |  899 +++++++++++++++++++++++++++++++++++++++
>   3 files changed, 907 insertions(+), 0 deletions(-)
>
>  diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
>  index 257101f..5daa8d3 100644
>  --- a/sound/soc/fsl/Kconfig
>  +++ b/sound/soc/fsl/Kconfig
>  @@ -17,4 +17,10 @@ config SND_SOC_MPC8610_HPCD
>         help
>           Say Y if you want to enable audio on the Freescale MPC8610 HPCD.
>
>  +config SND_SOC_MPC5200_I2S
>  +       bool "Freescale MPC5200 PSC in I2S mode driver"
>  +       depends on SND_SOC && PPC_MPC52xx
>  +       help
>  +         Say Y here to support the MPC5200 PSCs in I2S mode.
>  +
>   endmenu
>  diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
>  index 62f680a..98729a1 100644
>  --- a/sound/soc/fsl/Makefile
>  +++ b/sound/soc/fsl/Makefile
>  @@ -4,3 +4,5 @@ obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o
>   # MPC8610 Platform Support
>   obj-$(CONFIG_SND_SOC_MPC8610) += fsl_ssi.o fsl_dma.o
>
>  +obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o
>  +
>  diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c
>  new file mode 100644
>  index 0000000..81d0933
>  --- /dev/null
>  +++ b/sound/soc/fsl/mpc5200_psc_i2s.c
>  @@ -0,0 +1,899 @@
>  +/*
>  + * Freescale MPC5200 PSC in I2S mode
>  + * ALSA SoC Digital Audio Interface (DAI) driver
>  + *
>  + * Copyright (C) 2008 Secret Lab Technologies Ltd.
>  + */
>  +
>  +#include <linux/init.h>
>  +#include <linux/module.h>
>  +#include <linux/interrupt.h>
>  +#include <linux/device.h>
>  +#include <linux/delay.h>
>  +#include <linux/of_device.h>
>  +#include <linux/of_platform.h>
>  +#include <linux/dma-mapping.h>
>  +
>  +#include <sound/core.h>
>  +#include <sound/pcm.h>
>  +#include <sound/pcm_params.h>
>  +#include <sound/initval.h>
>  +#include <sound/soc.h>
>  +#include <sound/soc-of.h>
>  +
>  +#include <sysdev/bestcomm/bestcomm.h>
>  +#include <sysdev/bestcomm/gen_bd.h>
>  +#include <asm/mpc52xx_psc.h>
>  +
>  +MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
>  +MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
>  +MODULE_LICENSE("GPL");
>  +
>  +/**
>  + * PSC_I2S_RATES: sample rates supported by the I2S
>  + *
>  + * This driver currently only supports the PSC running in I2S slave mode,
>  + * which means the codec determines the sample rate.  Therefore, we tell
>  + * ALSA that we support all rates and let the codec driver decide what rates
>  + * are really supported.
>  + */
>  +#define PSC_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
>  +                       SNDRV_PCM_RATE_CONTINUOUS)
>  +
>  +/**
>  + * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
>  + */
>  +#define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
>  +                        SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE | \
>  +                        SNDRV_PCM_FMTBIT_S32_BE)
>  +
>  +/**
>  + * psc_i2s_stream - Data specific to a single stream (playback or capture)
>  + * @active:            flag indicating if the stream is active
>  + * @psc_i2s:           pointer back to parent psc_i2s data structure
>  + * @bcom_task:         bestcomm task structure
>  + * @irq:               irq number for bestcomm task
>  + * @period_start:      physical address of start of DMA region
>  + * @period_end:                physical address of end of DMA region
>  + * @period_next_pt:    physical address of next DMA buffer to enqueue
>  + * @period_bytes:      size of DMA period in bytes
>  + */
>  +struct psc_i2s_stream {
>  +       int active;
>  +       struct psc_i2s *psc_i2s;
>  +       struct bcom_task *bcom_task;
>  +       int irq;
>  +       struct snd_pcm_substream *stream;
>  +       dma_addr_t period_start;
>  +       dma_addr_t period_end;
>  +       dma_addr_t period_next_pt;
>  +       dma_addr_t period_current_pt;
>  +       int period_bytes;
>  +};
>  +
>  +/**
>  + * psc_i2s - Private driver data
>  + * @name: short name for this device ("PSC0", "PSC1", etc)
>  + * @psc_regs: pointer to the PSC's registers
>  + * @fifo_regs: pointer to the PSC's FIFO registers
>  + * @irq: IRQ of this PSC
>  + * @dev: struct device pointer
>  + * @playback: the number of playback streams opened
>  + * @capture: the number of capture streams opened
>  + * @dai: the CPU DAI for this device
>  + * @playback_stream: Playback stream context data
>  + * @capture_stream: Capture stream context data
>  + */
>  +struct psc_i2s {
>  +       char name[32];
>  +       struct mpc52xx_psc __iomem *psc_regs;
>  +       struct mpc52xx_psc_fifo __iomem *fifo_regs;
>  +       unsigned int irq;
>  +       struct device *dev;
>  +       struct snd_soc_cpu_dai dai;
>  +       spinlock_t lock;
>  +
>  +       /* per-stream data */
>  +       struct psc_i2s_stream playback_stream;
>  +       struct psc_i2s_stream capture_stream;
>  +
>  +       /* Statistics */
>  +       struct {
>  +               int overrun_count;
>  +               int underrun_count;
>  +       } stats;
>  +};
>  +
>  +/*
>  + * Interrupt handlers
>  + */
>  +static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s)
>  +{
>  +       struct psc_i2s *psc_i2s = _psc_i2s;
>  +       struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
>  +       u16 imr;
>  +       u16 isr;
>  +
>  +       isr = in_be16(&regs->mpc52xx_psc_isr);
>  +       imr = in_be16(&regs->mpc52xx_psc_imr);
>  +
>  +       /* Playback underrun error */
>  +       if (isr & imr & MPC52xx_PSC_IMR_TXEMP)
>  +               psc_i2s->stats.underrun_count++;
>  +
>  +       /* Capture overrun error */
>  +       if (isr & imr & MPC52xx_PSC_IMR_ORERR)
>  +               psc_i2s->stats.overrun_count++;
>  +
>  +       out_8(&regs->command, 4 << 4);  /* reset the error status */
>  +
>  +       return IRQ_HANDLED;
>  +}
>  +
>  +/**
>  + * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer
>  + * @s: pointer to stream private data structure
>  + *
>  + * Enqueues another audio period buffer into the bestcomm queue.
>  + *
>  + * Note: The routine must only be called when there is space available in
>  + * the queue.  Otherwise the enqueue will fail and the audio ring buffer
>  + * will get out of sync
>  + */
>  +static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s)
>  +{
>  +       struct bcom_bd *bd;
>  +
>  +       /* Prepare and enqueue the next buffer descriptor */
>  +       bd = bcom_prepare_next_buffer(s->bcom_task);
>  +       bd->status = s->period_bytes;
>  +       bd->data[0] = s->period_next_pt;
>  +       bcom_submit_next_buffer(s->bcom_task, NULL);
>  +
>  +       /* Update for next period */
>  +       s->period_next_pt += s->period_bytes;
>  +       if (s->period_next_pt >= s->period_end)
>  +               s->period_next_pt = s->period_start;
>  +}
>  +
>  +/* Bestcomm DMA irq handler */
>  +static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream)
>  +{
>  +       struct psc_i2s_stream *s = _psc_i2s_stream;
>  +
>  +       //spin_lock(&s->psc_i2s->lock);
>  +
>  +       /* For each finished period, dequeue the completed period buffer
>  +        * and enqueue a new one in it's place. */
>  +       while (bcom_buffer_done(s->bcom_task)) {
>  +               bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
>  +               s->period_current_pt += s->period_bytes;
>  +               if (s->period_current_pt >= s->period_end)
>  +                       s->period_current_pt = s->period_start;
>  +               psc_i2s_bcom_enqueue_next_buffer(s);
>  +               bcom_enable(s->bcom_task);
>  +       }
>  +
>  +       //spin_unlock(&s->psc_i2s->lock);
>  +
>  +       /* If the stream is active, then also inform the PCM middle layer
>  +        * of the period finished event. */
>  +       if (s->active)
>  +               snd_pcm_period_elapsed(s->stream);
>  +
>  +       return IRQ_HANDLED;
>  +}
>  +
>  +/**
>  + * psc_i2s_startup: create a new substream
>  + *
>  + * This is the first function called when a stream is opened.
>  + *
>  + * If this is the first stream open, then grab the IRQ and program most of
>  + * the PSC registers.
>  + */
>  +static int psc_i2s_startup(struct snd_pcm_substream *substream)
>  +{
>  +       int playback_irq, capture_irq, rc;
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
>  +       struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
>  +       struct mpc52xx_psc_fifo __iomem *fiforegs = psc_i2s->fifo_regs;
>  +
>  +       dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream);
>  +
>  +       /* Disable all interrupts and reset the PSC */
>  +       out_be16(&regs->mpc52xx_psc_imr, 0);
>  +       out_8(&regs->command, 3 << 4); /* reset transmitter */
>  +       out_8(&regs->command, 2 << 4); /* reset receiver */
>  +       out_8(&regs->command, 1 << 4); /* reset mode */
>  +       out_8(&regs->command, 4 << 4); /* reset error */
>  +
>  +       /* Default to CODEC8 mode */
>  +       out_be32(&regs->sicr,
>  +                MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S |
>  +                MPC52xx_PSC_SICR_CLKPOL | MPC52xx_PSC_SICR_SIM_CODEC_8);
>  +
>  +       /* First write: RxRdy (FIFO Alarm) generates receive FIFO interrupt */
>  +       /* Second write to mode: register Normal mode for non loopback */
>  +       out_8(&regs->mode, 0);
>  +       out_8(&regs->mode, 0);
>  +
>  +       /* Set the TX and RX fifo alarm thresholds */
>  +       out_be16(&fiforegs->rfalarm, 0x100);    /* set RFALARM level */
>  +       out_8(&fiforegs->rfcntl, 0x4);          /* set RFGRAN level (bytes) */
>  +       out_be16(&fiforegs->tfalarm, 0x100);    /* set TFALARM level */
>  +       out_8(&fiforegs->tfcntl, 0x7);          /* set TFGRAN level (bytes*4) */
>  +
>  +       /* Setup the IRQs */
>  +       playback_irq = bcom_get_task_irq(psc_i2s->playback_stream.bcom_task);
>  +       capture_irq = bcom_get_task_irq(psc_i2s->capture_stream.bcom_task);
>  +       rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED,
>  +                        "psc-i2s-status", psc_i2s);
>  +       rc |= request_irq(capture_irq, &psc_i2s_bcom_irq, IRQF_SHARED,
>  +                         "psc-i2s-capture", &psc_i2s->capture_stream);
>  +       rc |= request_irq(playback_irq, &psc_i2s_bcom_irq, IRQF_SHARED,
>  +                         "psc-i2s-playback", &psc_i2s->playback_stream);
>  +       if (rc) {
>  +               free_irq(psc_i2s->irq, psc_i2s);
>  +               free_irq(capture_irq, &psc_i2s->capture_stream);
>  +               free_irq(playback_irq, &psc_i2s->playback_stream);
>  +               return -ENODEV;
>  +       }
>  +
>  +       return 0;
>  +}
>  +
>  +static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
>  +                                struct snd_pcm_hw_params *params)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
>  +       u32 sicr;
>  +
>  +       dev_dbg(psc_i2s->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
>  +               " periods=%i buffer_size=%i  buffer_bytes=%i\n",
>  +               __FUNCTION__, substream, params_period_size(params),
>  +               params_period_bytes(params), params_periods(params),
>  +               params_buffer_size(params), params_buffer_bytes(params));
>  +
>  +       sicr = MPC52xx_PSC_SICR_DTS1 |
>  +              MPC52xx_PSC_SICR_I2S | MPC52xx_PSC_SICR_CLKPOL;
>  +       switch (params_format(params)) {
>  +        case SNDRV_PCM_FORMAT_S8:
>  +               sicr |= MPC52xx_PSC_SICR_SIM_CODEC_8;
>  +               break;
>  +        case SNDRV_PCM_FORMAT_S16_BE:
>  +               sicr |= MPC52xx_PSC_SICR_SIM_CODEC_16;
>  +               break;
>  +        case SNDRV_PCM_FORMAT_S24_BE:
>  +               sicr |= MPC52xx_PSC_SICR_SIM_CODEC_24;
>  +               break;
>  +        case SNDRV_PCM_FORMAT_S32_BE:
>  +               sicr |= MPC52xx_PSC_SICR_SIM_CODEC_32;
>  +               break;
>  +        default:
>  +               dev_dbg(psc_i2s->dev, "invalid format\n");
>  +               return -EINVAL;
>  +       }
>  +       out_be32(&psc_i2s->psc_regs->sicr, sicr);
>  +
>  +       //rc = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
>  +       //if (rc) {
>  +       //      dev_err(psc_i2s->dev, "could not allocate dma buffer\n");
>  +       //      return rc;
>  +       //}
>  +
>  +       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
>  +
>  +       return 0;
>  +}
>  +
>  +static int psc_i2s_hw_free(struct snd_pcm_substream *substream)
>  +{
>  +       //return snd_pcm_lib_free_pages(substream);
>  +       snd_pcm_set_runtime_buffer(substream, NULL);
>  +       return 0;
>  +}
>  +
>  +/**
>  + * psc_i2s_trigger: start and stop the DMA transfer.
>  + *
>  + * This function is called by ALSA to start, stop, pause, and resume the DMA
>  + * transfer of data.
>  + */
>  +static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
>  +       struct snd_pcm_runtime *runtime = substream->runtime;
>  +       struct psc_i2s_stream *s;
>  +       struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
>  +       u16 imr;
>  +       u8 psc_cmd;
>  +       long flags;
>  +
>  +       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
>  +               s = &psc_i2s->capture_stream;
>  +       else
>  +               s = &psc_i2s->playback_stream;
>  +
>  +       dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)"
>  +               " stream_id=%i\n",
>  +               substream, cmd, substream->pstr->stream);
>  +
>  +       switch (cmd) {
>  +        case SNDRV_PCM_TRIGGER_START:
>  +               s->period_bytes = frames_to_bytes(runtime,
>  +                                                 runtime->period_size);
>  +               s->period_start = virt_to_phys(runtime->dma_area);
>  +               s->period_end = s->period_start +
>  +                               (s->period_bytes * runtime->periods);
>  +               s->period_next_pt = s->period_start;
>  +               s->period_current_pt = s->period_start;
>  +               s->active = 1;
>  +
>  +               /* First; reset everything */
>  +               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
>  +                       out_8(&regs->command, MPC52xx_PSC_RST_RX);
>  +                       out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
>  +               } else {
>  +                       out_8(&regs->command, MPC52xx_PSC_RST_TX);
>  +                       out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
>  +               }
>  +
>  +               /* Next, fill up the bestcomm bd queue and enable DMA.
>  +                * This will begin filling the PSC's fifo. */
>  +               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
>  +                       bcom_gen_bd_rx_reset(s->bcom_task);
>  +               else
>  +                       bcom_gen_bd_tx_reset(s->bcom_task);
>  +               while (!bcom_queue_full(s->bcom_task))
>  +                       psc_i2s_bcom_enqueue_next_buffer(s);
>  +               bcom_enable(s->bcom_task);
>  +
>  +               /* Update interrupt enable settings.  This must be done
>  +                * before the PSC is enabled so that TX underrun events
>  +                * are not missed. */
>  +               imr = 0;
>  +               if (psc_i2s->playback_stream.active)
>  +                       imr |= MPC52xx_PSC_IMR_TXEMP;
>  +               if (psc_i2s->capture_stream.active)
>  +                       imr |= MPC52xx_PSC_IMR_ORERR;
>  +               out_be16(&regs->isr_imr.imr, imr);
>  +
>  +               /* Due to errata in the i2s mode; need to line up enabling
>  +                * the transmitter with a transition on the frame sync
>  +                * line */
>  +
>  +               spin_lock_irqsave(&psc_i2s->lock, flags);
>  +               /* first make sure it is low */
>  +               while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) != 0);

Could this be moved to the front of the routine, to increase parallelism?

Once you detect the 0, it will be a fixed interval before the 1
happens. Might as well overlap the computations.

>  +               /* then wait for the transition to high */
>  +               while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) == 0);
>  +               /* Finally, enable the PSC.
>  +                * Receiver must always be enabled; even when we only want
>  +                * transmit.  (see 15.3.2.3 of MPC5200B User's Guide) */
>  +               psc_cmd = MPC52xx_PSC_RX_ENABLE;
>  +               if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK)
>  +                       psc_cmd |= MPC52xx_PSC_TX_ENABLE;
>  +               out_8(&regs->command, psc_cmd);
>  +               spin_unlock_irqrestore(&psc_i2s->lock, flags);
>  +
>  +               break;
>  +
>  +        case SNDRV_PCM_TRIGGER_STOP:
>  +               /* Turn off the PSC */
>  +               s->active = 0;
>  +               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
>  +                       if (!psc_i2s->playback_stream.active) {
>  +                               out_8(&regs->command, 2 << 4);  /* reset rx */
>  +                               out_8(&regs->command, 3 << 4);  /* reset tx */
>  +                               out_8(&regs->command, 4 << 4);  /* reset err */
>  +                       }
>  +               } else {
>  +                       out_8(&regs->command, 3 << 4);  /* reset tx */
>  +                       out_8(&regs->command, 4 << 4);  /* reset err */
>  +                       if (!psc_i2s->capture_stream.active)
>  +                               out_8(&regs->command, 2 << 4);  /* reset rx */
>  +               }
>  +
>  +               bcom_disable(s->bcom_task);
>  +               while (!bcom_queue_empty(s->bcom_task))
>  +                       bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
>  +
>  +               break;
>  +
>  +        default:
>  +               dev_dbg(psc_i2s->dev, "invalid command\n");
>  +               return -EINVAL;
>  +       }
>  +
>  +       /* Update interrupt enable settings */
>  +       imr = 0;
>  +       if (psc_i2s->playback_stream.active) imr |= MPC52xx_PSC_IMR_TXEMP;
>  +       if (psc_i2s->capture_stream.active) imr |= MPC52xx_PSC_IMR_ORERR;
>  +       out_be16(&regs->isr_imr.imr, imr);
>  +
>  +       return 0;
>  +}
>  +
>  +/**
>  + * psc_i2s_shutdown: shutdown the data transfer on a stream
>  + *
>  + * Shutdown the PSC if there are no other substreams open.
>  + */
>  +static void psc_i2s_shutdown(struct snd_pcm_substream *substream)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
>  +
>  +       dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream);
>  +
>  +       /*
>  +        * If this is the last active substream, disable the PSC and release
>  +        * the IRQ.
>  +        */
>  +       if (!psc_i2s->playback_stream.active &&
>  +           !psc_i2s->capture_stream.active) {
>  +               /* TODO: shut off channels */
>  +               free_irq(psc_i2s->irq, psc_i2s);
>  +               free_irq(bcom_get_task_irq(psc_i2s->capture_stream.bcom_task),
>  +                        &psc_i2s->capture_stream);
>  +               free_irq(bcom_get_task_irq(psc_i2s->playback_stream.bcom_task),
>  +                        &psc_i2s->playback_stream);
>  +       }
>  +}
>  +
>  +/**
>  + * psc_i2s_set_sysclk: set the clock frequency and direction
>  + *
>  + * This function is called by the machine driver to tell us what the clock
>  + * frequency and direction are.
>  + *
>  + * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN),
>  + * and we don't care about the frequency.  Return an error if the direction
>  + * is not SND_SOC_CLOCK_IN.
>  + *
>  + * @clk_id: reserved, should be zero
>  + * @freq: the frequency of the given clock ID, currently ignored
>  + * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master)
>  + */
>  +static int psc_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
>  +                             int clk_id, unsigned int freq, int dir)
>  +{
>  +       struct psc_i2s *psc_i2s = cpu_dai->private_data;
>  +       dev_dbg(psc_i2s->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n",
>  +                               cpu_dai, dir);
>  +       return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL;
>  +}
>  +
>  +/**
>  + * psc_i2s_set_fmt: set the serial format.
>  + *
>  + * This function is called by the machine driver to tell us what serial
>  + * format to use.
>  + *
>  + * This driver only supports I2S mode.  Return an error if the format is
>  + * not SND_SOC_DAIFMT_I2S.
>  + *
>  + * @format: one of SND_SOC_DAIFMT_xxx
>  + */
>  +static int psc_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int format)
>  +{
>  +       struct psc_i2s *psc_i2s = cpu_dai->private_data;
>  +       dev_dbg(psc_i2s->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n",
>  +                               cpu_dai, format);
>  +       return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
>  +}
>  +
>  +/* ---------------------------------------------------------------------
>  + * ALSA SoC Bindings
>  + *
>  + * - Digital Audio Interface (DAI) template
>  + * - create/destroy dai hooks
>  + */
>  +
>  +/**
>  + * psc_i2s_dai_template: template CPU Digital Audio Interface
>  + */
>  +static struct snd_soc_cpu_dai psc_i2s_dai_template = {
>  +       .type = SND_SOC_DAI_I2S,
>  +       .playback = {
>  +               .channels_min = 2,
>  +               .channels_max = 2,
>  +               .rates = PSC_I2S_RATES,
>  +               .formats = PSC_I2S_FORMATS,
>  +       },
>  +       .capture = {
>  +               .channels_min = 2,
>  +               .channels_max = 2,
>  +               .rates = PSC_I2S_RATES,
>  +               .formats = PSC_I2S_FORMATS,
>  +       },
>  +       .ops = {
>  +               .startup = psc_i2s_startup,
>  +               .hw_params = psc_i2s_hw_params,
>  +               .hw_free = psc_i2s_hw_free,
>  +               .shutdown = psc_i2s_shutdown,
>  +               .trigger = psc_i2s_trigger,
>  +       },
>  +       .dai_ops = {
>  +               .set_sysclk = psc_i2s_set_sysclk,
>  +               .set_fmt = psc_i2s_set_fmt,
>  +       },
>  +};
>  +
>  +/* ---------------------------------------------------------------------
>  + * The PSC I2S 'ASoC platform' driver
>  + *
>  + * Can be referenced by an 'ASoC machine' driver
>  + * This driver only deals with the audio bus; it doesn't have any
>  + * interaction with the attached codec
>  + */
>  +
>  +static const struct snd_pcm_hardware psc_i2s_pcm_hardware = {
>  +       .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
>  +               SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER,
>  +       .formats = SNDRV_PCM_FMTBIT_S8 |SNDRV_PCM_FMTBIT_S16_BE |
>  +                  SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
>  +       .rate_min = 8000,
>  +       .rate_max = 48000,
>  +       .channels_min = 2,
>  +       .channels_max = 2,
>  +       .period_bytes_max       = 1024 * 1024,
>  +       .period_bytes_min       = 32,
>  +       .period_bytes_max       = 1024 * 1024,
>  +       .periods_min            = 2,
>  +       .periods_max            = 256,
>  +       .buffer_bytes_max       = 2 * 1024 * 1024,
>  +       .fifo_size              = 0,
>  +};
>  +
>  +static unsigned int psc_i2s_fixed_rates[] = {
>  +       8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
>  +};
>  +
>  +static struct snd_pcm_hw_constraint_list psc_i2s_constraints_rates = {
>  +       .count = ARRAY_SIZE(psc_i2s_fixed_rates),
>  +       .list = psc_i2s_fixed_rates,
>  +       .mask = 0,
>  +};
>  +
>  +static int psc_i2s_pcm_open(struct snd_pcm_substream *substream)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
>  +       struct psc_i2s_stream *s;
>  +       int rc;
>  +
>  +       dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream);
>  +
>  +       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
>  +               s = &psc_i2s->capture_stream;
>  +       else
>  +               s = &psc_i2s->playback_stream;
>  +
>  +       snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware);
>  +
>  +       rc = snd_pcm_hw_constraint_integer(substream->runtime,
>  +                                          SNDRV_PCM_HW_PARAM_PERIODS);
>  +       if (rc < 0) {
>  +               dev_err(psc_i2s->dev, "invalid buffer size\n");
>  +               return rc;
>  +       }
>  +       rc = snd_pcm_hw_constraint_list(substream->runtime, 0,
>  +                                       SNDRV_PCM_HW_PARAM_RATE,
>  +                                       &psc_i2s_constraints_rates);
>  +       if (rc < 0) {
>  +               dev_err(psc_i2s->dev, "invalid rate\n");
>  +               return rc;
>  +       }
>  +
>  +       s->stream = substream;
>  +       return 0;
>  +}
>  +
>  +static int psc_i2s_pcm_close(struct snd_pcm_substream * substream)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
>  +       struct psc_i2s_stream *s;
>  +
>  +       dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream);
>  +
>  +       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
>  +               s = &psc_i2s->capture_stream;
>  +       else
>  +               s = &psc_i2s->playback_stream;
>  +
>  +       s->stream = NULL;
>  +       return 0;
>  +}
>  +
>  +static snd_pcm_uframes_t
>  +psc_i2s_pcm_pointer(struct snd_pcm_substream *substream)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>  +       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
>  +       struct psc_i2s_stream *s;
>  +       dma_addr_t count;
>  +
>  +       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
>  +               s = &psc_i2s->capture_stream;
>  +       else
>  +               s = &psc_i2s->playback_stream;
>  +
>  +       /*FIXME: count = s->sdma->bd[s->sdma->outdex].data - s->period_start;*/
>  +       count = s->period_current_pt - s->period_start;
>  +
>  +       return bytes_to_frames(substream->runtime, count);
>  +}
>  +
>  +static struct snd_pcm_ops psc_i2s_pcm_ops = {
>  +       .open           = psc_i2s_pcm_open,
>  +       .close          = psc_i2s_pcm_close,
>  +       .ioctl          = snd_pcm_lib_ioctl,
>  +       .pointer        = psc_i2s_pcm_pointer,
>  +};
>  +
>  +static u64 psc_i2s_pcm_dmamask = 0xffffffff;
>  +static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
>  +                          struct snd_pcm *pcm)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = pcm->private_data;
>  +       size_t size = psc_i2s_pcm_hardware.buffer_bytes_max;
>  +       int rc = 0;
>  +
>  +       dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n",
>  +               card, dai, pcm);
>  +
>  +       if (!card->dev->dma_mask)
>  +               card->dev->dma_mask = &psc_i2s_pcm_dmamask;
>  +       if (!card->dev->coherent_dma_mask)
>  +               card->dev->coherent_dma_mask = 0xffffffff;
>  +
>  +       rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
>  +                                &pcm->streams[0].substream->dma_buffer);
>  +       if (rc) {
>  +               dev_err(card->dev, "Cannot alloc playback DMA buffer\n");
>  +               return -ENOMEM;
>  +       }
>  +
>  +       rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
>  +                                &pcm->streams[1].substream->dma_buffer);
>  +       if (rc) {
>  +               snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer);
>  +               dev_err(card->dev, "Can't allocate capture DMA buffer\n");
>  +               return -ENOMEM;
>  +       }
>  +
>  +       return 0;
>  +}
>  +
>  +static void psc_i2s_pcm_free(struct snd_pcm *pcm)
>  +{
>  +       struct snd_soc_pcm_runtime *rtd = pcm->private_data;
>  +       struct snd_pcm_substream *substream;
>  +       int stream;
>  +
>  +       dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=%p)\n", pcm);
>  +
>  +       for (stream = 0; stream < 2; stream++) {
>  +               substream = pcm->streams[stream].substream;
>  +               if (substream) {
>  +                       snd_dma_free_pages(&substream->dma_buffer);
>  +                       substream->dma_buffer.area = NULL;
>  +                       substream->dma_buffer.addr = 0;
>  +               }
>  +       }
>  +}
>  +
>  +struct snd_soc_platform psc_i2s_pcm_soc_platform = {
>  +       .name           = "mpc5200-psc-audio",
>  +       .pcm_ops        = &psc_i2s_pcm_ops,
>  +       .pcm_new        = &psc_i2s_pcm_new,
>  +       .pcm_free       = &psc_i2s_pcm_free,
>  +};
>  +
>  +/* ---------------------------------------------------------------------
>  + * Sysfs attributes for debugging
>  + */
>  +
>  +static ssize_t psc_i2s_status_show(struct device *dev,
>  +                          struct device_attribute *attr, char *buf)
>  +{
>  +       struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
>  +
>  +       return sprintf(buf, "status=%.4x sicr=%.8x rfnum=%i rfstat=0x%.4x tfnum=%i tfstat=0x%.4x\n",
>  +                       in_be16(&psc_i2s->psc_regs->sr_csr.status),
>  +                       in_be32(&psc_i2s->psc_regs->sicr),
>  +                       in_be16(&psc_i2s->fifo_regs->rfnum) & 0x1ff,
>  +                       in_be16(&psc_i2s->fifo_regs->rfstat),
>  +                       in_be16(&psc_i2s->fifo_regs->tfnum) & 0x1ff,
>  +                       in_be16(&psc_i2s->fifo_regs->tfstat));
>  +}
>  +
>  +static int * psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s,
>  +                                  const char *name)
>  +{
>  +       if (strcmp(name, "playback_underrun") == 0)
>  +               return &psc_i2s->stats.underrun_count;
>  +       if (strcmp(name, "capture_overrun") == 0)
>  +               return &psc_i2s->stats.overrun_count;
>  +
>  +       return NULL;
>  +}
>  +
>  +static ssize_t psc_i2s_stat_show(struct device *dev,
>  +                                struct device_attribute *attr, char *buf)
>  +{
>  +       struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
>  +       int *attrib;
>  +
>  +       attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
>  +       if (!attrib)
>  +               return 0;
>  +
>  +       return sprintf(buf, "%i\n", *attrib);
>  +}
>  +
>  +static ssize_t psc_i2s_stat_store(struct device *dev,
>  +                                 struct device_attribute *attr,
>  +                                 const char *buf,
>  +                                 size_t count)
>  +{
>  +       struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
>  +       int *attrib;
>  +
>  +       attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
>  +       if (!attrib)
>  +               return 0;
>  +
>  +       *attrib = simple_strtoul(buf, NULL, 0);
>  +       return count;
>  +}
>  +
>  +DEVICE_ATTR(status, 0644, psc_i2s_status_show, NULL);
>  +DEVICE_ATTR(playback_underrun, 0644, psc_i2s_stat_show,psc_i2s_stat_store);
>  +DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show, psc_i2s_stat_store);
>  +
>  +/* ---------------------------------------------------------------------
>  + * OF platform bus binding code:
>  + * - Probe/remove operations
>  + * - OF device match table
>  + */
>  +static int __devinit psc_i2s_of_probe(struct of_device *op,
>  +                                     const struct of_device_id *match)
>  +{
>  +       phys_addr_t fifo;
>  +       struct psc_i2s *psc_i2s;
>  +       struct resource res;
>  +       int size, psc_id, irq, rc;
>  +       const __be32 *prop;
>  +       void __iomem *regs;
>  +
>  +       dev_dbg(&op->dev, "probing psc i2s device\n");
>  +
>  +       /* Get the PSC ID */
>  +       prop = of_get_property(op->node, "cell-index", &size);
>  +       if (!prop || size < sizeof *prop)
>  +               return -ENODEV;
>  +       psc_id = be32_to_cpu(*prop);
>  +
>  +       /* Fetch the registers and IRQ of the PSC */
>  +       irq = irq_of_parse_and_map(op->node, 0);
>  +       if (of_address_to_resource(op->node, 0, &res)) {
>  +               dev_err(&op->dev, "Missing reg property\n");
>  +               return -ENODEV;
>  +       }
>  +       regs = ioremap(res.start, 1 + res.end - res.start);
>  +       if (!regs) {
>  +               dev_err(&op->dev, "Could not map registers\n");
>  +               return -ENODEV;
>  +       }
>  +
>  +       /* Allocate and initialize the driver private data */
>  +       psc_i2s = kzalloc(sizeof *psc_i2s, GFP_KERNEL);
>  +       if (!psc_i2s) {
>  +               iounmap(regs);
>  +               return -ENOMEM;
>  +       }
>  +       spin_lock_init(&psc_i2s->lock);
>  +       psc_i2s->irq = irq;
>  +       psc_i2s->psc_regs = regs;
>  +       psc_i2s->fifo_regs = regs + sizeof *psc_i2s->psc_regs;
>  +       psc_i2s->dev = &op->dev;
>  +       psc_i2s->playback_stream.psc_i2s = psc_i2s;
>  +       psc_i2s->capture_stream.psc_i2s = psc_i2s;
>  +       snprintf(psc_i2s->name, sizeof psc_i2s->name, "PSC%u", psc_id+1);
>  +
>  +       /* Fill out the CPU DAI structure */
>  +       memcpy(&psc_i2s->dai, &psc_i2s_dai_template, sizeof psc_i2s->dai);
>  +       psc_i2s->dai.private_data = psc_i2s;
>  +       psc_i2s->dai.name = psc_i2s->name;
>  +       psc_i2s->dai.id = psc_id;
>  +
>  +       /* Find the address of the fifo data registers and setup the
>  +        * DMA tasks */
>  +       fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32);
>  +       psc_i2s->capture_stream.bcom_task =
>  +               bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512);
>  +       psc_i2s->playback_stream.bcom_task =
>  +               bcom_psc_gen_bd_tx_init(psc_id, 10, fifo);
>  +       if (!psc_i2s->capture_stream.bcom_task ||
>  +           !psc_i2s->playback_stream.bcom_task) {
>  +               dev_err(&op->dev, "Could not allocate bestcomm tasks\n");
>  +               iounmap(regs);
>  +               kfree(psc_i2s);
>  +               return -ENODEV;
>  +       }
>  +
>  +       /* Save what we've done so it can be found again later */
>  +       dev_set_drvdata(&op->dev, psc_i2s);
>  +
>  +       /* Register the SYSFS files */
>  +       rc = device_create_file(psc_i2s->dev, &dev_attr_status);
>  +       rc = device_create_file(psc_i2s->dev, &dev_attr_capture_overrun);
>  +       rc = device_create_file(psc_i2s->dev, &dev_attr_playback_underrun);
>  +       if (rc)
>  +               dev_info(psc_i2s->dev, "error creating sysfs files\n");
>  +
>  +       /* Tell the ASoC OF helpers about it */
>  +       of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node,
>  +                                    &psc_i2s->dai);
>  +
>  +       return 0;
>  +}
>  +
>  +static int __devexit psc_i2s_of_remove(struct of_device *op)
>  +{
>  +       struct psc_i2s *psc_i2s = dev_get_drvdata(&op->dev);
>  +
>  +       dev_dbg(&op->dev, "psc_i2s_remove()\n");
>  +
>  +       bcom_gen_bd_rx_release(psc_i2s->capture_stream.bcom_task);
>  +       bcom_gen_bd_tx_release(psc_i2s->playback_stream.bcom_task);
>  +
>  +       iounmap(psc_i2s->psc_regs);
>  +       iounmap(psc_i2s->fifo_regs);
>  +       kfree(psc_i2s);
>  +       dev_set_drvdata(&op->dev, NULL);
>  +
>  +       return 0;
>  +}
>  +
>  +/* Match table for of_platform binding */
>  +static struct of_device_id psc_i2s_match[] __devinitdata = {
>  +       { .compatible = "fsl,mpc5200-psc-i2s", },
>  +       {}
>  +};
>  +MODULE_DEVICE_TABLE(of, psc_i2s_match);
>  +
>  +static struct of_platform_driver psc_i2s_driver = {
>  +       .match_table = psc_i2s_match,
>  +       .probe = psc_i2s_of_probe,
>  +       .remove = __devexit_p(psc_i2s_of_remove),
>  +       .driver = {
>  +               .name = "mpc5200-psc-i2s",
>  +               .owner = THIS_MODULE,
>  +       },
>  +};
>  +
>  +/* ---------------------------------------------------------------------
>  + * Module setup and teardown; simply register the of_platform driver
>  + * for the PSC in I2S mode.
>  + */
>  +static int __init psc_i2s_init(void)
>  +{
>  +       return of_register_platform_driver(&psc_i2s_driver);
>  +}
>  +module_init(psc_i2s_init);
>  +
>  +static void __exit psc_i2s_exit(void)
>  +{
>  +       of_unregister_platform_driver(&psc_i2s_driver);
>  +}
>  +module_exit(psc_i2s_exit);
>  +
>  +
>
>  _______________________________________________
>  Alsa-devel mailing list
>  Alsa-devel@alsa-project.org
>  http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>


-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply

* Re: [PATCH] powerpc/85xx: Add support for MPC8536DS
From: Chen Gong @ 2008-07-02 15:27 UTC (permalink / raw)
  To: Kumar Gala; +Cc: Stephen Rothwell, linuxppc-dev
In-Reply-To: <F2A6FF1A-CF7F-4B2A-94B3-4C0E0693CB21@kernel.crashing.org>

Kumar Gala 写道:
> 
> On Jul 2, 2008, at 9:04 AM, Chen Gong wrote:
> 
>> Stephen Rothwell 写道:
>>> Hi Kumar,
>>>
>>> On Wed, 2 Jul 2008 02:01:10 -0500 (CDT) Kumar Gala 
>>> <galak@kernel.crashing.org> wrote:
>>>> +void __init mpc8536_ds_pic_init(void)
>>>> +{
>>>> +    struct mpic *mpic;
>>>> +    struct resource r;
>>>> +    struct device_node *np = NULL;
>>>
>>> You don't need to initialise this.
>>>
>>>> +    np = of_find_node_by_type(np, "open-pic");
>>>> +
>>>
>>> Extra blank line.
>>>
>>>> +    if (np == NULL) {
>>>> +        printk(KERN_ERR "Could not find open-pic node\n");
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    if (of_address_to_resource(np, 0, &r)) {
>>>> +        printk(KERN_ERR "Failed to map mpic register space\n");
>>>> +        of_node_put(np);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    mpic = mpic_alloc(np, r.start,
>>>> +              MPIC_PRIMARY | MPIC_WANTS_RESET |
>>>> +              MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS,
>>>> +            0, 256, " OpenPIC  ");
>>>> +    BUG_ON(mpic == NULL);
>>>> +
>>>> +    mpic_init(mpic);
>>>
>>> You leak a reference to np here.
>>
>> It looks like there are same problem in other mpc85xx_xx.c files
> 
> agreed for the mpc85xx_ds.c, I dont see the _cds or _ads having these 
> issues.

Yes, maybe I should write as mpc8xxx_xx.c, because it seems there is
the same problem in mpc8610_hpcd.c mpc86xx_hpcn.c etc.
> 
> - k
> 
> 

^ permalink raw reply

* Re: [alsa-devel] [PATCH 1/3] ALSA SoC: Add OpenFirmware helper for matching bus and codec drivers
From: Jon Smirl @ 2008-07-02 15:27 UTC (permalink / raw)
  To: Grant Likely; +Cc: liam.girdwood, alsa-devel, broonie, timur, linuxppc-dev
In-Reply-To: <20080701235330.16923.67218.stgit@trillian.secretlab.ca>

On 7/1/08, Grant Likely <grant.likely@secretlab.ca> wrote:
> From: Grant Likely <grant.likely@secretlab.ca>
>
>  Simple utility layer for creating ASoC machine instances based on data
>  in the OpenFirmware device tree.  OF aware platform drivers and codec
>  drivers register themselves with this framework and the framework
>  automatically instantiates a machine driver.
>
>  This is most likely temporary glue code to work around limitations in
>  the ASoC v1 framework.  I expect ASoC v2 won't need this.
>  ---
>
>   sound/soc/Kconfig  |    6 ++
>   sound/soc/Makefile |    1
>   sound/soc/soc-of.c |  171 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 178 insertions(+), 0 deletions(-)
>
>  diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
>  index 18f28ac..c5736e5 100644
>  --- a/sound/soc/Kconfig
>  +++ b/sound/soc/Kconfig
>  @@ -23,6 +23,12 @@ config SND_SOC
>           This ASoC audio support can also be built as a module.  If so, the module
>           will be called snd-soc-core.
>
>  +config SND_SOC_OF
>  +       tristate "OF helpers for SoC audio support"
>  +       depends on SND_SOC
>  +       ---help---
>  +         Add support for OpenFirmware device tree descriptions of sound device
>  +
>   # All the supported Soc's
>   source "sound/soc/at91/Kconfig"
>   source "sound/soc/pxa/Kconfig"
>  diff --git a/sound/soc/Makefile b/sound/soc/Makefile
>  index 782db21..191c2e5 100644
>  --- a/sound/soc/Makefile
>  +++ b/sound/soc/Makefile
>  @@ -2,3 +2,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o
>
>   obj-$(CONFIG_SND_SOC)  += snd-soc-core.o
>   obj-$(CONFIG_SND_SOC)  += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ omap/
>  +obj-$(CONFIG_SND_SOC_OF)       += soc-of.o
>  diff --git a/sound/soc/soc-of.c b/sound/soc/soc-of.c
>  new file mode 100644
>  index 0000000..9694979
>  --- /dev/null
>  +++ b/sound/soc/soc-of.c
>  @@ -0,0 +1,171 @@
>  +/*
>  + * OF helpers for ALSA SoC Layer
>  + *
>  + * Copyright (C) 2008, Secret Lab Technologies Ltd.
>  + */
>  +
>  +#include <linux/module.h>
>  +#include <linux/moduleparam.h>
>  +#include <linux/init.h>
>  +#include <linux/delay.h>
>  +#include <linux/pm.h>
>  +#include <linux/bitops.h>
>  +#include <linux/platform_device.h>
>  +#include <linux/of.h>
>  +#include <sound/core.h>
>  +#include <sound/pcm.h>
>  +#include <sound/pcm_params.h>
>  +#include <sound/soc.h>
>  +#include <sound/soc-of.h>
>  +#include <sound/initval.h>
>  +
>  +MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
>  +MODULE_LICENSE("GPL");
>  +MODULE_DESCRIPTION("ALSA SoC OpenFirmware bindings");
>  +
>  +DEFINE_MUTEX(of_snd_soc_mutex);
>  +LIST_HEAD(of_snd_soc_device_list);
>  +static int of_snd_soc_next_index;
>  +
>  +struct of_snd_soc_device {
>  +       int id;
>  +       struct list_head list;
>  +       struct snd_soc_device device;
>  +       struct snd_soc_machine machine;
>  +       struct snd_soc_dai_link dai_link;
>  +       struct platform_device *pdev;
>  +       struct device_node *platform_node;
>  +       struct device_node *codec_node;
>  +};
>  +
>  +static struct snd_soc_ops of_snd_soc_ops = {
>  +};
>  +
>  +static struct of_snd_soc_device *
>  +of_snd_soc_get_device(struct device_node *codec_node)
>  +{
>  +       struct of_snd_soc_device *of_soc;
>  +
>  +       list_for_each_entry(of_soc, &of_snd_soc_device_list, list) {
>  +               if (of_soc->codec_node == codec_node)
>  +                       return of_soc;
>  +       }
>  +
>  +       of_soc = kzalloc(sizeof(struct of_snd_soc_device), GFP_KERNEL);
>  +       if (!of_soc)
>  +               return NULL;
>  +
>  +       /* Initialize the structure and add it to the global list */
>  +       of_soc->codec_node = codec_node;
>  +       of_soc->id = of_snd_soc_next_index++;
>  +       of_soc->machine.dai_link = &of_soc->dai_link;
>  +       of_soc->machine.num_links = 1;
>  +       of_soc->device.machine = &of_soc->machine;
>  +       of_soc->dai_link.ops = &of_snd_soc_ops;
>  +       list_add(&of_soc->list, &of_snd_soc_device_list);
>  +
>  +       return of_soc;
>  +}

Isn't this performing the same basic function as this code (except for
spi)? Should this list be maintained in alsa or should there be an
equivalent for searching the spi bus? If you follow the link to the
codec node and get it's parent you know which bus to search. But it
might be simpler to just search the buses sequentially for the node.

static int of_dev_node_match(struct device *dev, void *data)
{
        return dev->archdata.of_node == data;
}

struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
{
	struct device *dev;
	
	dev = bus_find_device(&i2c_bus_type, NULL, node,
					 of_dev_node_match);
	if (!dev)
		return NULL;
		
	return to_i2c_client(dev);
}
EXPORT_SYMBOL(of_find_i2c_device_by_node);



>  +
>  +static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc)
>  +{
>  +       struct platform_device *pdev;
>  +       int rc;
>  +
>  +       /* Only register the device if both the codec and platform have
>  +        * been registered */
>  +       if ((!of_soc->device.codec_data) || (!of_soc->platform_node))
>  +               return;
>  +
>  +       pr_info("platform<-->codec match achieved; registering machine\n");
>  +
>  +       pdev = platform_device_alloc("soc-audio", of_soc->id);
>  +       if (!pdev) {
>  +               pr_err("of_soc: platform_device_alloc() failed\n");
>  +               return;
>  +       }
>  +
>  +       pdev->dev.platform_data = of_soc;
>  +       platform_set_drvdata(pdev, &of_soc->device);
>  +       of_soc->device.dev = &pdev->dev;
>  +
>  +       /* The ASoC device is complete; register it */
>  +       rc = platform_device_add(pdev);
>  +       if (rc) {
>  +               pr_err("of_soc: platform_device_add() failed\n");
>  +               return;
>  +       }
>  +
>  +}
>  +
>  +int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev,
>  +                             void *codec_data, struct snd_soc_codec_dai *dai,
>  +                             struct device_node *node)
>  +{
>  +       struct of_snd_soc_device *of_soc;
>  +       int rc = 0;
>  +
>  +       pr_info("registering ASoC codec driver: %s\n", node->full_name);
>  +
>  +       mutex_lock(&of_snd_soc_mutex);
>  +       of_soc = of_snd_soc_get_device(node);
>  +       if (!of_soc) {
>  +               rc = -ENOMEM;
>  +               goto out;
>  +       }
>  +
>  +       /* Store the codec data */
>  +       of_soc->device.codec_data = codec_data;
>  +       of_soc->device.codec_dev = codec_dev;
>  +       of_soc->dai_link.name = node->name;
>  +       of_soc->dai_link.stream_name = node->name;
>  +       of_soc->dai_link.codec_dai = dai;
>  +
>  +       /* Now try to register the SoC device */
>  +       of_snd_soc_register_device(of_soc);
>  +
>  + out:
>  +       mutex_unlock(&of_snd_soc_mutex);
>  +       return rc;
>  +}
>  +EXPORT_SYMBOL_GPL(of_snd_soc_register_codec);
>  +
>  +int of_snd_soc_register_platform(struct snd_soc_platform *platform,
>  +                                struct device_node *node,
>  +                                struct snd_soc_cpu_dai *cpu_dai)
>  +{
>  +       struct of_snd_soc_device *of_soc;
>  +       struct device_node *codec_node;
>  +       const phandle *handle;
>  +       int len, rc = 0;
>  +
>  +       pr_info("registering ASoC platform driver: %s\n", node->full_name);
>  +
>  +       handle = of_get_property(node, "codec-handle", &len);
>  +       if (!handle || len < sizeof(handle))
>  +               return -ENODEV;
>  +       codec_node = of_find_node_by_phandle(*handle);
>  +       if (!codec_node)
>  +               return -ENODEV;
>  +       pr_info("looking for codec: %s\n", codec_node->full_name);
>  +
>  +       mutex_lock(&of_snd_soc_mutex);
>  +       of_soc = of_snd_soc_get_device(codec_node);
>  +       if (!of_soc) {
>  +               rc = -ENOMEM;
>  +               goto out;
>  +       }
>  +
>  +       of_soc->platform_node = node;
>  +       of_soc->dai_link.cpu_dai = cpu_dai;
>  +       of_soc->device.platform = platform;
>  +       of_soc->machine.name = of_soc->dai_link.cpu_dai->name;
>  +
>  +       /* Now try to register the SoC device */
>  +       of_snd_soc_register_device(of_soc);
>  +
>  + out:
>  +       mutex_unlock(&of_snd_soc_mutex);
>  +       return rc;
>  +}
>  +EXPORT_SYMBOL_GPL(of_snd_soc_register_platform);
>
>  _______________________________________________
>  Alsa-devel mailing list
>  Alsa-devel@alsa-project.org
>  http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>


-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply

* Re: patches for 2.6.27...
From: Scott Wood @ 2008-07-02 15:41 UTC (permalink / raw)
  To: Kumar Gala; +Cc: linuxppc-dev@ozlabs.org list
In-Reply-To: <068935A1-11BE-4AEB-9C0E-E1AB53F1618E@kernel.crashing.org>

On Wed, Jul 02, 2008 at 02:56:16AM -0500, Kumar Gala wrote:
> Please point out any patches that have been posted but havent made it  
> into a git tree related to Freescale chips.
> 
> I know there are probably a slew of CPM patches that need to get into  
> the tree.

Could you apply the 83xx power management patchset (the gianfar bits had
been acked by Jeff long ago)?

-Scott

^ permalink raw reply

* Re: [alsa-devel] [PATCH 1/3] ALSA SoC: Add OpenFirmware helper for matching bus and codec drivers
From: Grant Likely @ 2008-07-02 15:48 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: liam.girdwood, alsa-devel, broonie, timur, linuxppc-dev
In-Reply-To: <s5hiqvod37w.wl%tiwai@suse.de>

On Wed, Jul 02, 2008 at 11:50:43AM +0200, Takashi Iwai wrote:
> At Tue, 01 Jul 2008 17:53:30 -0600,
> Grant Likely wrote:
> > diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
> > index 18f28ac..c5736e5 100644
> > --- a/sound/soc/Kconfig
> > +++ b/sound/soc/Kconfig
> > @@ -23,6 +23,12 @@ config SND_SOC
> >  	  This ASoC audio support can also be built as a module.  If so, the module
> >  	  will be called snd-soc-core.
> >  
> > +config SND_SOC_OF
> > +	tristate "OF helpers for SoC audio support"
> > +	depends on SND_SOC
> > +	---help---
> > +	  Add support for OpenFirmware device tree descriptions of sound device
> > +
> 
> This is a helper module and not necessarily manually selectable.
> Better to make the other driver selecting this.

Yes, you're right.  I hadn't put too much thought into the Kconfig stuff
for this patch because, at the moment, I'm viewing this as a temporary
solution until ASoCv2 is merged.

Which raises another question: Liam and Mark, what is your opinion on
merging this driver?  Is it something that you would merge with the v1
API and then rework it when v2 is merged?  Or would you rather it be
kept out until v2 is ready?  I haven't even looked at the v2 API yet, so
I don't know how much rework is involved.

Cheers,
g.

^ 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