linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* Lite5200B I2S driver
@ 2006-12-12 15:47 Pedro Luis D. L.
  2006-12-12 19:22 ` Sylvain Munaut
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Pedro Luis D. L. @ 2006-12-12 15:47 UTC (permalink / raw)
  To: linuxppc-embedded

Hi all,

I´m working with a Lite5200B EVB, trying to atach an audio output to a PCM 
1680 from Texas Instruments.
At this moment I am using the patched 2.6.16.11-rt18 kernel that comes with 
Freescale BSP.
I´ve tried to develop an I2S driver for the platform with no results. I´m 
afraid that my driver devolping skills are still too low.

Anybody knows a working I2S driver for this board that can throw any 
audiooutput through PSCs?

Any help or indication would be apreciated.

Pedro Dominguez.

_________________________________________________________________
Acepta el reto MSN Premium: Protección para tus hijos en internet. 
Descárgalo y pruébalo 2 meses gratis. 
http://join.msn.com?XAPID=1697&DI=1055&HL=Footer_mailsenviados_proteccioninfantil

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Lite5200B I2S driver
  2006-12-12 15:47 Lite5200B I2S driver Pedro Luis D. L.
@ 2006-12-12 19:22 ` Sylvain Munaut
  2006-12-13  9:31   ` Pedro Luis D. L.
  2006-12-13  9:10 ` Roman Fietze
  2006-12-17  8:03 ` Grant Likely
  2 siblings, 1 reply; 13+ messages in thread
From: Sylvain Munaut @ 2006-12-12 19:22 UTC (permalink / raw)
  To: Pedro Luis D. L.; +Cc: linuxppc-embedded

Pedro Luis D. L. wrote:
> Hi all,
>
> I´m working with a Lite5200B EVB, trying to atach an audio output to a PCM 
> 1680 from Texas Instruments.
> At this moment I am using the patched 2.6.16.11-rt18 kernel that comes with 
> Freescale BSP.
> I´ve tried to develop an I2S driver for the platform with no results. I´m 
> afraid that my driver devolping skills are still too low.
>
> Anybody knows a working I2S driver for this board that can throw any 
> audiooutput through PSCs?
>
> Any help or indication would be apreciated
I've never seen one.

I'm currently working on a AC97 driver, maybe once it's done you can use
it as a base.
It's pretty early so don't excepect anything before a while ;)

I'm pretty sure some part could be shared between the two ...


    Sylvain

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Lite5200B I2S driver
  2006-12-12 15:47 Lite5200B I2S driver Pedro Luis D. L.
  2006-12-12 19:22 ` Sylvain Munaut
@ 2006-12-13  9:10 ` Roman Fietze
  2006-12-13  9:39   ` Pedro Luis D. L.
  2006-12-17  8:03 ` Grant Likely
  2 siblings, 1 reply; 13+ messages in thread
From: Roman Fietze @ 2006-12-13  9:10 UTC (permalink / raw)
  To: linuxppc-embedded

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

Hi Pedro,

On Tuesday 12 December 2006 16:47, Pedro Luis D. L. wrote:

> I´m working with a Lite5200B EVB, trying to atach an audio output to
> a PCM 1680 from Texas Instruments.

I've got a fully working GPL ALSA driver for the MPC5200 PSC's using
I2S as well as the ALSA controls for an UDA1380 codec connected via
I2C. This code is written for ALSA 1.0.8 and the 2.4.25 from Wolfgang.

I'm sorry not to be able to provide a patchset for either ALSA nor the
2.6. If you are interested in this code I will post it here.

A question to the mailing pro's: should I post source as attachments
or inlined?


Roman

-- 
Roman Fietze              Telemotive AG Büro Mühlhausen
Breitwiesen                            73347 Mühlhausen
Tel.: +49(0)7335/18493-45      http://www.telemotive.de


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

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Lite5200B I2S driver
  2006-12-12 19:22 ` Sylvain Munaut
@ 2006-12-13  9:31   ` Pedro Luis D. L.
  0 siblings, 0 replies; 13+ messages in thread
From: Pedro Luis D. L. @ 2006-12-13  9:31 UTC (permalink / raw)
  To: linuxppc-embedded


Sylvain Munaut wrote:

>Pedro Luis D. L. wrote:
> > Hi all,
> >
> > I´m working with a Lite5200B EVB, trying to atach an audio output to a 
>PCM
> > 1680 from Texas Instruments.
> > At this moment I am using the patched 2.6.16.11-rt18 kernel that comes 
>with
> > Freescale BSP.
> > I´ve tried to develop an I2S driver for the platform with no results. 
>I´m
> > afraid that my driver devolping skills are still too low.
> >
> > Anybody knows a working I2S driver for this board that can throw any
> > audiooutput through PSCs?
> >
> > Any help or indication would be apreciated
>I've never seen one.
>
>I'm currently working on a AC97 driver, maybe once it's done you can use
>it as a base.
>It's pretty early so don't excepect anything before a while ;)
>
>I'm pretty sure some part could be shared between the two ...
>
>
>     Sylvain

That would be a nice point to start.

Thx.

       Pedro.

_________________________________________________________________
Acepta el reto MSN Premium: Correos más divertidos con fotos y textos 
increíbles en MSN Premium. Descárgalo y pruébalo 2 meses gratis. 
http://join.msn.com?XAPID=1697&DI=1055&HL=Footer_mailsenviados_correosmasdivertidos

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Lite5200B I2S driver
  2006-12-13  9:10 ` Roman Fietze
@ 2006-12-13  9:39   ` Pedro Luis D. L.
  2006-12-13 12:56     ` Roman Fietze
                       ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Pedro Luis D. L. @ 2006-12-13  9:39 UTC (permalink / raw)
  To: linuxppc-embedded





------------------------------------------------------------------

Todas mis chorradas alemanas en:

http://carcadiz.blogspot.com

------------------------------------------------------------------
On Wed, 13 Dec 2006, Roman Fietze wrote:

>Hi Pedro,
>
>On Tuesday 12 December 2006 16:47, Pedro Luis D. L. wrote:
>
> > I´m working with a Lite5200B EVB, trying to atach an audio output to
> > a PCM 1680 from Texas Instruments.
>
>I've got a fully working GPL ALSA driver for the MPC5200 PSC's using
>I2S as well as the ALSA controls for an UDA1380 codec connected via
>I2C. This code is written for ALSA 1.0.8 and the 2.4.25 from Wolfgang.
>
>I'm sorry not to be able to provide a patchset for either ALSA nor the
>2.6. If you are interested in this code I will post it here.
>
>A question to the mailing pro's: should I post source as attachments
>or inlined?
>
>
>Roman

Hi Roman,

for sure. I´m interested in the code. Maybe I can adapt this code or migrate 
the application to 2.4 from Wolfgang. At this moment, I´m quite focus on 
make something sounds through the audio board.

Thx.

   Pedro.

_________________________________________________________________
Moda para esta temporada. Ponte al día de todas las tendencias. 
http://www.msn.es/Mujer/moda/default.asp

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Lite5200B I2S driver
  2006-12-13  9:39   ` Pedro Luis D. L.
@ 2006-12-13 12:56     ` Roman Fietze
  2006-12-18 13:47       ` Stefan Strobl
  2006-12-13 12:58     ` Roman Fietze
  2006-12-13 13:15     ` Roman Fietze
  2 siblings, 1 reply; 13+ messages in thread
From: Roman Fietze @ 2006-12-13 12:56 UTC (permalink / raw)
  To: linuxppc-embedded


[-- Attachment #1.1: Type: text/plain, Size: 1742 bytes --]

Hello Pedro,

On Wednesday 13 December 2006 10:39, Pedro Luis D. L. wrote:

> for sure. I´m interested in the code. Maybe I can adapt this code or
> migrate the application to 2.4 from Wolfgang. At this moment, I´m
> quite focus on make something sounds through the audio board.

See what you can achive with the code in the attachments of the two
posts. They are

alsa/alsa-kernel/i2c/mpc5200-uda1380.c   ALSA PCM driver
alsa/alsa-kernel/ppc/uda1380.[hc]        ALSA Codec Controls


And in the second post the kernel patch.

linux/include/asm-ppc/mpc5xxx.h          MPC5200 constants
arch/ppc/5xxx_io/snd-psc.c               Kernel side of ALSA sound driver

I'va also attached mpc5xxx.h, I think I used some additional constants
which I added there. I had to separate the ALSA and kernel parts of
the PCM driver, so I could more easily manage it.

The driver uses some modified bestcomm code, because the original
bestcomm code from Freescale is, ahem, not very well designed, one of
the reasons for the new design in the 2.6. The changes were done in
the code image 2. They are a more generic approach for the PSC tasks,
so I can more easily switch PSC's. You should easily be able to modify
the kernel part of the driver, to use the bestcomm code from
Wolfgang's 2.4.25 git kernel tree. If not, please contact me and I
will send you the sources of my bestcomm/ subdirectory, or anybody
else of course.

One day, when we have the 2.6 running, I will try to get the code
upstream to ALSA and the powerpc kernel.


Roman

-- 
Roman Fietze              Telemotive AG Büro Mühlhausen
Breitwiesen                            73347 Mühlhausen
Tel.: +49(0)7335/18493-45      http://www.telemotive.de


[-- Attachment #1.2: uda1380.c --]
[-- Type: text/x-csrc, Size: 23641 bytes --]

/*
 * Sound driver for Philips uda1380 - i2c related stuff
 *
 * Copyright (C) 2005 Roman Fietze <roman.fietze@telemotive.de>
 * Copyright (C) 2004 Giorgio Padrin
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file COPYING in the main directory of this archive for
 * more details.
 *
 * based on code by:
 *
 * 2004-03	Mattew Reimer 
 * Copyright (C) 2003 Christian Pellegrin
 * Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V.
 * Copyright (c) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
 * Copyright (c) 2002 Hewlett-Packard Company
 * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
 */

#undef UDA1380_DEBUG
// #define UDA1380_DEBUG

#include <sound/driver.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/soundcard.h>
#include <linux/i2c.h>

#include <sound/core.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <sound/info.h>
#include "uda1380.h"

#include <asm/uaccess.h>

static int debug = -1;


#if defined(UDA1380_DEBUG)
#define PRDEBUG(level, format, x... )  do {if (level <= debug) printk(KERN_INFO format, ## x );} while (0)
static inline void DBGDELAY(int _level, unsigned long _ms)
{
	if (_ms && _level <= debug)
	{
		wait_queue_head_t wait;
		init_waitqueue_head(&wait);
		interruptible_sleep_on_timeout(&wait, (HZ * _ms) / 1000UL);
	}
}
#else
#define PRDEBUG(level, format, x... )	do {} while(0)
#define DBGDELAY(t)			do {} while(0)
#endif


static struct i2c_driver uda1380_driver;

static struct uda1380_reg_info {
	unsigned short num;
	unsigned short default_value;
} uda1380_reg_info[] = {
	{
		EMCS_REG,
		EMCS_REG_SC_256FS | EMCS_REG_DAC_USE_WSPLL | EMCS_REG_ADC_USE_WSPLL |
		EMCS_REG_PLL_25TO50 | EMCS_REG_EN_ADC | EMCS_REG_EN_DEC | EMCS_REG_EN_DAC | EMCS_REG_EN_INT
	},
	{
		I2S_REG,
		(FMT_I2S << I2S_REG_SFORO_SHIFT) | (FMT_I2S << I2S_REG_SFORI_SHIFT) |
		I2S_REG_SEL_DECIMATOR | I2S_REG_SIM_SLAVE
	},
	{
		PWR_REG,
		PWR_REG_PON_PLL |
		PWR_REG_PON_DAC | PWR_REG_PON_BIAS | PWR_REG_PON_HP |
		PWR_REG_EN_AVC | PWR_REG_PON_AVC | PWR_REG_PON_LNA | PWR_REG_PON_ADCL | PWR_REG_PON_ADCR |
		PWR_REG_PON_PGAL | PWR_REG_PON_PGAR
	},
	{
		AMIX_REG,
		AMIX_AVCL(0x3F) | AMIX_AVCR(0x3F)
	},
	{
		MASTER_VOL_REG,
		MASTER_VCL(0) | MASTER_VCR(0)
	},
	{
		MIXER_VOL_REG,
		MIXER_VC_CH1(00) | MIXER_VC_CH2(0xff)
	},
	{
		MBT_REG,
		0x00
	},
	{
		MMCDM_REG,
		MMCDM_MASTER_MUTE| MMCDM_CHANNEL2_MUTE
	},
	{
		MIXER_CTL_REG,
		0x00
	},
	{
		DEC_VOL_REG,
		DEC_VCL(0) | DEC_VCR(0)
	},
	{
		DPM_REG,
		DPM_MUTE_ADC
	},
	{
		DEC_ADC_REG,
		0x050c
	},
	{
		DEC_AGC_REG,
		0x00
	}
};

typedef unsigned int uda1380_enumerated_t;
typedef long uda1380_value_t;

typedef struct _uda1380_lr_value
{
	uda1380_value_t left;
	uda1380_value_t right;
} uda1380_lr_value_t;


typedef struct _uda1380
{
	uint16_t regs[REG_MAX];

	int mic_enable;

	int mod_cnt;

	uda1380_enumerated_t deemp;
	uda1380_enumerated_t strength;
	uda1380_value_t playback_mute;
	uda1380_value_t mic_gain;
	uda1380_value_t mic_sw;
	uda1380_value_t line_mute;
	uda1380_lr_value_t line_gain;
	uda1380_lr_value_t master_playback_volume;
	uda1380_lr_value_t analog_playback_volume;
	uda1380_lr_value_t pcm_capture_volume;
	uda1380_lr_value_t bass;
	uda1380_lr_value_t treble;

	struct i2c_client client;
} uda1380_t;


#if defined(UDA1380_DEBUG) && 0
static void uda1380_dump(int level, const uda1380_t *uda1380)
{
	PRDEBUG(level, "uda1380: volume=%u bass=%u treble=%u line=%u mic=%u\n",
		uda1380->volume, uda1380->bass, uda1380->treble, uda1380->line, uda1380->mic);
	PRDEBUG(level, "uda1380: mod_cnt=%d client=%p\n",
		uda1380->mod_cnt, &uda1380->client);
}
#endif


static int uda1380_write_reg(struct i2c_client *client, uda1380_t *uda1380, int regaddr)
{
	uint8_t buffer[3];
	int r;

	PRDEBUG(3, "%s(): [%u] = %04x\n", __FUNCTION__, regaddr, uda1380->regs[regaddr]);

	buffer[0] = regaddr;
	buffer[1] = uda1380->regs[regaddr] >> 8;
	buffer[2] = uda1380->regs[regaddr] & 0xff;

	r = i2c_master_send(client, buffer, 3);
	if (r == 3)
		r = 0;
	else if (r >= 0)
		r = -EIO;
		
	return r;
}


void uda1380_configure(struct i2c_client *client, long rate)
{
	uda1380_t *uda1380 = container_of(client, uda1380_t, client);
	uint16_t pll;

	PRDEBUG(2, "%s(%p,%ld) uda1380=%p\n", __FUNCTION__, client, rate, uda1380);

	if (!rate) {
		PRDEBUG(2, "%s: rate set to default of 44100\n", __FUNCTION__);
		rate = 44100;
	}

	if (rate > 50000)
		pll = EMCS_REG_PLL_50TO100;
	if (rate >= 25000)
		pll = EMCS_REG_PLL_25TO50;
	else if (rate >= 12000)
		pll = EMCS_REG_PLL_12TO25;
	else
		pll = EMCS_REG_PLL_6TO12;
	uda1380->regs[EMCS_REG] &= ~(EMCS_REG_SC_MASK | EMCS_REG_PLL_MASK);
	uda1380->regs[EMCS_REG] |= EMCS_REG_SC_256FS;
	uda1380->regs[EMCS_REG] |= pll;
	uda1380_write_reg(client, uda1380, EMCS_REG);
	PRDEBUG(3, "%s: %u\n", __FUNCTION__, __LINE__);

	uda1380->regs[I2S_REG] = ((uda1380->regs[I2S_REG] & ~(I2S_REG_SFORO_MASK | I2S_REG_SFORI_MASK)) |
				  (FMT_I2S << I2S_REG_SFORO_SHIFT) | (FMT_I2S << I2S_REG_SFORI_SHIFT));
	uda1380_write_reg(client, uda1380, I2S_REG);
	PRDEBUG(3, "%s: %u\n", __FUNCTION__, __LINE__);

	uda1380->regs[PWR_REG] &= ~PWR_REG_PON_PLL;
	uda1380_write_reg(client, uda1380, PWR_REG);
	PRDEBUG(3, "%s: %u\n", __FUNCTION__, __LINE__);
	mdelay(5);

	uda1380->regs[PWR_REG] |= PWR_REG_PON_PLL;
	uda1380_write_reg(client, uda1380, PWR_REG);
	PRDEBUG(3, "%s: %u\n", __FUNCTION__, __LINE__);
	mdelay(5);
}


int uda1380_mute(struct i2c_client *client, int mute) 
{
	int ret;
	uda1380_t *uda1380 = container_of(client, uda1380_t, client);

	PRDEBUG(2, "%s(client=%p,mute=%d) uda1380=%p\n", __FUNCTION__, client, mute, uda1380);
	
	ret = (uda1380->regs[MMCDM_REG] & MMCDM_MASTER_MUTE) > 0;

	if (mute)
		uda1380->regs[MMCDM_REG] |= MMCDM_MASTER_MUTE;
	else
		uda1380->regs[MMCDM_REG] &= ~MMCDM_MASTER_MUTE;

	uda1380_write_reg(client, uda1380, MMCDM_REG);
	PRDEBUG(3, "%s: %u\n", __FUNCTION__, __LINE__);

	return ret;
}


/* The private value of the controls. */
enum
{
	UDAC_MASTER_PLAYBACK_VOLUME,
	UDAC_MASTER_PLAYBACK_MUTE,
	UDAC_ANALOG_PLAYBACK_VOLUME,
	UDAC_STRENGTH,
	UDAC_BASS,
	UDAC_TREBLE,
	UDAC_DEEMP,
	UDAC_PCM_CAPTURE_VOLUME,
	UDAC_MIC_GAIN,
	UDAC_LINE_GAIN,
	UDAC_LINE_MUTE,
	UDAC_MIC_SW,
	UDAC_LAST
};


static int 
snd_uda1380_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
	static const char *strengths[]	= {"minimum", "flat", "maximum"};
	static const char *deemps[]	= {"off", "32kHz", "44.1kHz", "48kHz", "96kHz"};

	PRDEBUG(4, "%s(%p,%p) private_value=%ld\n",
		__FUNCTION__,
		kcontrol, uinfo,
		kcontrol->private_value);

	switch (kcontrol->private_value) {
	case UDAC_MASTER_PLAYBACK_VOLUME:
		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
		uinfo->count = 2;
		uinfo->value.integer.min = 0;
		uinfo->value.integer.max = 0xFF;
		return 0;
	case UDAC_PCM_CAPTURE_VOLUME:
		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
		uinfo->count = 2;
		uinfo->value.integer.min = -128;
		uinfo->value.integer.max = 0x30;
		return 0;
	case UDAC_MASTER_PLAYBACK_MUTE:
	case UDAC_LINE_MUTE:
		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
		uinfo->count = 1;
		uinfo->value.integer.min = 0;
		uinfo->value.integer.max = 1;
		return 0;
	case UDAC_ANALOG_PLAYBACK_VOLUME:
		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
		uinfo->count = 2;
		uinfo->value.integer.min = 0;
		uinfo->value.integer.max = 0x3F;
		return 0;
	case UDAC_BASS:
		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
		uinfo->count = 2;
		uinfo->value.integer.min = 0;
		uinfo->value.integer.max = 0x0F;
		return 0;
	case UDAC_TREBLE:
		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
		uinfo->count = 2;
		uinfo->value.integer.min = 0;
		uinfo->value.integer.max = 0x03;
		return 0;
	case UDAC_STRENGTH:
		uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
		uinfo->count = 1;
		uinfo->value.enumerated.items = ARRAY_SIZE(strengths);
		if (uinfo->value.enumerated.item >= ARRAY_SIZE(strengths))
			uinfo->value.enumerated.item = ARRAY_SIZE(strengths) - 1;
		strncpy(uinfo->value.enumerated.name,
			strengths[uinfo->value.enumerated.item],
			sizeof(uinfo->value.enumerated.name));
		return 0;
	case UDAC_LINE_GAIN:
		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
		uinfo->count = 2;
		uinfo->value.integer.min = 0;
		uinfo->value.integer.max = 8;
		return 0;
	case UDAC_MIC_GAIN:
		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
		uinfo->count = 1;
		uinfo->value.integer.min = 0;
		uinfo->value.integer.max = 15;
		return 0;
	case UDAC_DEEMP:
		uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
		uinfo->count = 1;
		uinfo->value.enumerated.items = ARRAY_SIZE(deemps);
		if (uinfo->value.enumerated.item >= ARRAY_SIZE(deemps))
			uinfo->value.enumerated.item = ARRAY_SIZE(deemps) - 1;
		strncpy(uinfo->value.enumerated.name,
			deemps[uinfo->value.enumerated.item],
			sizeof(uinfo->value.enumerated.name));
		return 0;
	case UDAC_MIC_SW:
		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
		uinfo->count = 1;
		uinfo->value.integer.min = 0;
		uinfo->value.integer.max = 1;
		return 0;
	}
	printk(KERN_WARNING "%s called with bad private info: %ld\n", __FUNCTION__, kcontrol->private_value);
	return -EINVAL;
}


#if defined(UDA1380_DEBUG)
static const char *kontrol_value_names[] = {
	"MASTER_PLAYBACK_VOLUME",
	"MASTER_PLAYBACK_MUTE",
	"ANALOG_PLAYBACK_VOLUME",
	"STRENGTH",
	"BASS",
	"TREBLE",
	"DEEMP",
	"PCM_CAPTURE_VOLUME",
	"MIC_GAIN",
	"LINE_GAIN",
	"LINE_MUTE",
	"MIC_SW",
	"LAST",

	"<INVALID>"
};
#endif


static int 
snd_uda1380_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
	uda1380_t *uda1380 = snd_kcontrol_chip(kcontrol);
	struct i2c_client *client = &uda1380->client;
	uint8_t newregnum;
	uint16_t val;

#if defined(UDA1380_DEBUG)
	if (4 <= debug)
	{
		size_t i = kcontrol->private_value;
		if (i >= ARRAY_SIZE(kontrol_value_names))
			i = ARRAY_SIZE(kontrol_value_names) - 1;
		PRDEBUG(4, "%s(%p,%p) value=%s int_values=%ld/%ld uda1380=%p client=%p\n",
			__FUNCTION__,
			kcontrol, ucontrol,
			kontrol_value_names[i],
			ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
			uda1380, client);
	}
#endif

	switch (kcontrol->private_value)
	{
	case UDAC_STRENGTH:
		switch (uda1380->strength = ucontrol->value.integer.value[0])
		{
		case 0:
			val = 1;
			break;
		case 2:
			val = 3;
			break;
		default:
			val = 0;
			break;
		}

		uda1380->regs[MBT_REG] &= ~(MBT_MODE_MASK);
		uda1380->regs[MBT_REG] |= val << MBT_MODE_SHIFT;
		newregnum = MBT_REG;
		break;

	case UDAC_DEEMP:
		val = uda1380->deemp = ucontrol->value.enumerated.item[0];
		if (val > 4)
			val = 4;
		uda1380->regs[MMCDM_REG] &= ~(MMCDM_CHANNEL1_DEEMPHASIS_MASK | MMCDM_CHANNEL2_DEEMPHASIS_MASK);
		uda1380->regs[MMCDM_REG] |= (val << MMCDM_CHANNEL2_DEEMPHASIS_SHIFT) | (val << MMCDM_CHANNEL1_DEEMPHASIS_SHIFT);
		newregnum = MMCDM_REG;
		return 0;

	case UDAC_MASTER_PLAYBACK_MUTE:
		if ((uda1380->playback_mute = ucontrol->value.integer.value[0]))
			uda1380->regs[MMCDM_REG] &= ~MMCDM_MASTER_MUTE;
		else
			uda1380->regs[MMCDM_REG] |= MMCDM_MASTER_MUTE;
		newregnum = MMCDM_REG;
		break;

	case UDAC_MIC_GAIN:
		uda1380->mic_gain = ucontrol->value.integer.value[0] & 0x0F;
		uda1380->regs[DEC_ADC_REG] &= ~DEC_ADC_VGA_CTRL_MASK;
		uda1380->regs[DEC_ADC_REG] |= DEC_ADC_VGA_CTRL(uda1380->mic_gain);
		if (uda1380->mic_gain)
			uda1380->regs[DEC_ADC_REG] |= DEC_ADC_SEL_LNA | DEC_ADC_SEL_MIC;
		else
			uda1380->regs[DEC_ADC_REG] &= ~ (DEC_ADC_SEL_LNA | DEC_ADC_SEL_MIC);
		newregnum = DEC_ADC_REG;		
		break;

	case UDAC_MIC_SW:
		if ((uda1380->mic_sw = ucontrol->value.integer.value[0]))
			uda1380->regs[DEC_ADC_REG] |= DEC_ADC_SEL_LNA | DEC_ADC_SEL_MIC;
		else
			uda1380->regs[DEC_ADC_REG] &= ~ (DEC_ADC_SEL_LNA | DEC_ADC_SEL_MIC);
		newregnum = DEC_ADC_REG;
		break;

	case UDAC_LINE_MUTE:
		if ((uda1380->line_mute = ucontrol->value.integer.value[0]))
			uda1380->regs[DPM_REG] &= ~DPM_MUTE_ADC;
		else
			uda1380->regs[DPM_REG] |= DPM_MUTE_ADC;
		newregnum = DPM_REG;		
		break;

	case UDAC_MASTER_PLAYBACK_VOLUME:
		uda1380->master_playback_volume.left = ucontrol->value.integer.value[0] & 0xFF;
		uda1380->master_playback_volume.right = ucontrol->value.integer.value[1] & 0xFF;
		uda1380->regs[MASTER_VOL_REG] = (MASTER_VCL(0xFF - uda1380->master_playback_volume.left) |
						 MASTER_VCR(0xFF - uda1380->master_playback_volume.right));
		newregnum = MASTER_VOL_REG;
		break;

	case UDAC_ANALOG_PLAYBACK_VOLUME:
		uda1380->analog_playback_volume.left = ucontrol->value.integer.value[0] & 0x3F;
		uda1380->analog_playback_volume.right = ucontrol->value.integer.value[1] & 0x3F;
		uda1380->regs[AMIX_REG] = (AMIX_AVCL(0x3F - uda1380->analog_playback_volume.left) |
					   AMIX_AVCR(0x3F - uda1380->analog_playback_volume.right));
		newregnum = AMIX_REG;
		break;

	case UDAC_PCM_CAPTURE_VOLUME:
		uda1380->pcm_capture_volume.left = ucontrol->value.integer.value[0];
		if (uda1380->pcm_capture_volume.left < -128)
			uda1380->pcm_capture_volume.left = -128;
		else if (uda1380->pcm_capture_volume.left > 0x30)
			uda1380->pcm_capture_volume.left = 0x30;

		uda1380->pcm_capture_volume.right = ucontrol->value.integer.value[1];
		if (uda1380->pcm_capture_volume.right < -128)
			uda1380->pcm_capture_volume.right = -128;
		else if (uda1380->pcm_capture_volume.right > 0x30)
			uda1380->pcm_capture_volume.right = 0x30;

		uda1380->regs[DEC_VOL_REG] &= ~(DEC_VCL_MASK | DEC_VCR_MASK);
		val = uda1380->pcm_capture_volume.left & 0xFF;
		uda1380->regs[DEC_VOL_REG] |= DEC_VCL(val);
		val = uda1380->pcm_capture_volume.right & 0xFF;
		uda1380->regs[DEC_VOL_REG] |= DEC_VCR(val);
		newregnum = DEC_VOL_REG;		
		break;

	case UDAC_BASS:
		uda1380->bass.left = ucontrol->value.integer.value[0] & 0x0F;
		uda1380->bass.right = ucontrol->value.integer.value[1] & 0x0F;
		uda1380->regs[MBT_REG] &= ~(MBT_BBL_MASK | MBT_BBR_MASK);
		uda1380->regs[MBT_REG] |= MBT_BBL(uda1380->bass.left) | MBT_BBR(uda1380->bass.right);
		newregnum = MBT_REG;
		break;

	case UDAC_TREBLE:
		uda1380->treble.left = ucontrol->value.integer.value[0] & 0x03;
		uda1380->treble.right = ucontrol->value.integer.value[1] & 0x03;
		uda1380->regs[MBT_REG] &= ~(MBT_TRL_MASK | MBT_TRR_MASK);
		uda1380->regs[MBT_REG] |= MBT_TRL(uda1380->treble.left) | MBT_TRR(uda1380->treble.right);
		newregnum = MBT_REG;
		break;

	case UDAC_LINE_GAIN:
		uda1380->line_gain.left = ucontrol->value.integer.value[0] & 0x0F;
		uda1380->line_gain.right = ucontrol->value.integer.value[1] & 0x0F;
		uda1380->regs[DPM_REG] &= ~(DPM_GAINL_MASK | DPM_GAINR_MASK);
		uda1380->regs[DPM_REG] |= DPM_GAINL(uda1380->line_gain.left) | DPM_GAINR(uda1380->line_gain.right);
		newregnum = DPM_REG;		
		break;		

	default:
		return -EINVAL;
	}

	return uda1380_write_reg(client, uda1380, newregnum);
}


static int 
snd_uda1380_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
	uda1380_t *uda1380 = snd_kcontrol_chip(kcontrol);

	PRDEBUG(4, "%s(%p,%p) value=%ld uda1380=%p\n",
		__FUNCTION__,
		kcontrol, ucontrol,
		kcontrol->private_value,
		uda1380);

	switch (kcontrol->private_value)
	{
	case UDAC_STRENGTH:
		ucontrol->value.enumerated.item[0] = uda1380->strength;
		return 0;
	case UDAC_DEEMP:
		ucontrol->value.enumerated.item[0] = uda1380->deemp;
		return 0;
	case UDAC_MASTER_PLAYBACK_MUTE:
		ucontrol->value.enumerated.item[0] = uda1380->playback_mute;
		return 0;
	case UDAC_MIC_GAIN:
		ucontrol->value.integer.value[0] = uda1380->mic_gain;
		return 0;
	case UDAC_MIC_SW:
		ucontrol->value.integer.value[0] = uda1380->mic_sw;
		return 0;
	case UDAC_LINE_MUTE:
		ucontrol->value.integer.value[0] = uda1380->line_mute;
		return 0;
	case UDAC_MASTER_PLAYBACK_VOLUME:
		ucontrol->value.integer.value[0] = uda1380->master_playback_volume.left;
		ucontrol->value.integer.value[1] = uda1380->master_playback_volume.right;
		return 0;
	case UDAC_ANALOG_PLAYBACK_VOLUME:
		ucontrol->value.integer.value[0] = uda1380->analog_playback_volume.left;
		ucontrol->value.integer.value[1] = uda1380->analog_playback_volume.right;
		return 0;
	case UDAC_PCM_CAPTURE_VOLUME:
		ucontrol->value.integer.value[0] = uda1380->pcm_capture_volume.left;
		ucontrol->value.integer.value[1] = uda1380->pcm_capture_volume.right;
		return 0;
	case UDAC_BASS:
		ucontrol->value.integer.value[0] = uda1380->bass.left;
		ucontrol->value.integer.value[1] = uda1380->bass.right;
		return 0;
	case UDAC_TREBLE:
		ucontrol->value.integer.value[0] = uda1380->treble.left;
		ucontrol->value.integer.value[1] = uda1380->treble.right;
		return 0;
	case UDAC_LINE_GAIN:
		ucontrol->value.integer.value[0] = uda1380->line_gain.left;
		ucontrol->value.integer.value[1] = uda1380->line_gain.right;
		return 0;
	}
	printk(KERN_WARNING "%s called with bad private info: %ld\n", __FUNCTION__, kcontrol->private_value);
	return -EINVAL;
}


#define UDA1380(xname, pvt_value) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
  .name = xname, \
  .info = snd_uda1380_info, \
  .get = snd_uda1380_get, \
  .put = snd_uda1380_put, \
  .private_value = pvt_value \
}


static snd_kcontrol_new_t snd_uda1380_controls[] =
{
	UDA1380("Master Playback Volume",	UDAC_MASTER_PLAYBACK_VOLUME),
	UDA1380("Master Playback Switch",	UDAC_MASTER_PLAYBACK_MUTE),
	UDA1380("Line Bypass Playback Volume",	UDAC_ANALOG_PLAYBACK_VOLUME),
	UDA1380("Tone Control - Strenght",	UDAC_STRENGTH),
	UDA1380("Tone Control - Bass",		UDAC_BASS),
	UDA1380("Tone Control - Treble",	UDAC_TREBLE),
	UDA1380("De-emphasis",			UDAC_DEEMP),
	UDA1380("Capture Volume",		UDAC_PCM_CAPTURE_VOLUME),
	UDA1380("Mic Capture Gain",		UDAC_MIC_GAIN),
	UDA1380("Line Capture Volume",		UDAC_LINE_GAIN),
	UDA1380("Line Capture Switch",		UDAC_LINE_MUTE),
	UDA1380("Mic Capture Switch",		UDAC_MIC_SW),
};


/* initialization stuff */

static struct i2c_client client_template = {
	name: "(unset)",
	flags:  I2C_CLIENT_ALLOW_USE,
	driver: &uda1380_driver
};


static int uda1380_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int zero_or_minus_one)		
{
	uda1380_t *uda1380;
	struct i2c_client *client;
	int i;

	PRDEBUG(2,
		"%s(adap=%p,addr=%02x,flags=%u,zomo=%d)\n",
		__FUNCTION__,
		adap, addr, flags, zero_or_minus_one);

	uda1380 = kmalloc(sizeof(*uda1380), GFP_KERNEL);
	if (!uda1380)
		return -ENODEV;

	PRDEBUG(2, "%s: uda1380=%p created\n", __FUNCTION__, uda1380);

	memset(uda1380, 0, sizeof(*uda1380));

	/* set default values of registers */
	for (i = 0; i < ARRAY_SIZE(uda1380_reg_info); i++)
	{
		PRDEBUG(3, "%s: reg[%u] = 0x%04x\n",
			__FUNCTION__,
			uda1380_reg_info[i].num, uda1380_reg_info[i].default_value);

		uda1380->regs[uda1380_reg_info[i].num] = uda1380_reg_info[i].default_value;
	}

	client = &uda1380->client;
	memcpy(client, &client_template, sizeof(*client));
	client->adapter = adap;
	client->addr = addr;
	strcpy(client->name, "uda1380");

	i2c_attach_client(client);

	return 0;
}


static int uda1380_detach_client(struct i2c_client *client)
{
	uda1380_t *uda1380 = container_of(client, uda1380_t, client);

	PRDEBUG(2, "%s(%p) uda1380=%p\n", __FUNCTION__, client, uda1380);
	i2c_detach_client(client);
	PRDEBUG(2, "%s: freeing %p\n", __FUNCTION__, uda1380);
	kfree(uda1380);
	PRDEBUG(2, "%s: freed %p\n", __FUNCTION__, uda1380);
	return 0;
}


#define UDA1380_I2C_DEFAULT_ADDRESS     (0x30U >> 1)

/* Addresses to scan */
static unsigned short normal_i2c[] = 	{UDA1380_I2C_DEFAULT_ADDRESS, I2C_CLIENT_END};
static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
static unsigned short probe[]        =	{ I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short probe_range[]  =	{ I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore[]       =	{ I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore_range[] =	{ I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short force[]        =	{ I2C_CLIENT_END, I2C_CLIENT_END };

static struct i2c_client_address_data addr_data = {
	normal_i2c, normal_i2c_range, 
	probe, probe_range, 
	ignore, ignore_range, 
	force
};

static int uda1380_attach_adapter(struct i2c_adapter *adap)
{
	PRDEBUG(2, "%s(adap=%p)\n", __FUNCTION__, adap);
	return i2c_probe(adap, &addr_data, uda1380_attach);
}

static void uda1380_inc_use(struct i2c_client *client)
{
	PRDEBUG(2, "%s:\n", __FUNCTION__);
	MOD_INC_USE_COUNT;
}


static void uda1380_dec_use(struct i2c_client *client)
{
	PRDEBUG(2, "%s:\n", __FUNCTION__);
	MOD_DEC_USE_COUNT;
}


static struct i2c_driver uda1380_driver =
{
	.name		= "UDA1380",
	.id		= I2C_DRIVERID_UDA1380,
	.flags		= I2C_DF_NOTIFY,
	.attach_adapter	= uda1380_attach_adapter,
	.detach_client	= uda1380_detach_client,
	.inc_use	= uda1380_inc_use,
	.dec_use	= uda1380_dec_use
};


int snd_chip_uda1380_mixer_new(snd_card_t *_card, struct i2c_client **_client, int _mic_enable)
{
	size_t i;
	size_t n = 0;
	int err;
	struct i2c_client *client;
	uda1380_t *uda1380;

	MOD_INC_USE_COUNT;

	PRDEBUG(2, "%s(card=%p,_client=%p)\n", __FUNCTION__, _card, _client);
        
	snd_assert(_card != NULL, return -EINVAL);

	client = i2c_get_client(I2C_DRIVERID_UDA1380, I2C_ALGO_MPC5xxx, NULL);
	*_client = client;

	if (!client)
	{
		printk(KERN_ERR "uda1380: error: cannot get i2c client\n");
		MOD_DEC_USE_COUNT;
		return -ENODEV;
	}

	uda1380 = container_of(client, uda1380_t, client);
	uda1380->mic_enable = _mic_enable;
 
	PRDEBUG(1, "%s: i2c_get_client returned client=%p uda1380=%p\n", __FUNCTION__, client, uda1380);

	for (i = 0; i < ARRAY_SIZE(snd_uda1380_controls); i++) {
		if ((snd_uda1380_controls[i].private_value != UDAC_MIC_SW &&
		     snd_uda1380_controls[i].private_value != UDAC_MIC_GAIN) ||
		    _mic_enable)
		{
			if ((err = snd_ctl_add(_card, snd_ctl_new1(&snd_uda1380_controls[i], uda1380))) < 0)
			{
				MOD_DEC_USE_COUNT;
				return err;
			}
			n++;
		}
	}

	PRDEBUG(2, "added %u controls\n", n);
	strncpy(_card->mixername, "UDA1380 Mixer", sizeof(_card->mixername));

	return 0;
}


void snd_chip_uda1380_mixer_del(snd_card_t *_card, struct i2c_client *_client)
{
	PRDEBUG(2, "%s(%p,%p)\n", __FUNCTION__, _card, _client);
	MOD_DEC_USE_COUNT;
}


static int __init uda1380_init(void)
{
	PRDEBUG(2, "%s\n", __FUNCTION__);
#if defined(UDA1380_DEBUG)
	printk(KERN_INFO "%s: debug=%d\n", __FUNCTION__, debug);
#else
	if (debug != -1)
		printk(KERN_WARNING "%s: compiled without debug support\n", __FUNCTION__);
#endif
	return i2c_add_driver(&uda1380_driver);
}

static void __exit uda1380_exit(void)
{
	PRDEBUG(2, "%s\n", __FUNCTION__);
	i2c_del_driver(&uda1380_driver);
}


module_init(uda1380_init);
module_exit(uda1380_exit);

EXPORT_SYMBOL(uda1380_mute);
EXPORT_SYMBOL(uda1380_configure);
EXPORT_SYMBOL(snd_chip_uda1380_mixer_new);
EXPORT_SYMBOL(snd_chip_uda1380_mixer_del);

MODULE_AUTHOR("Giorgio Padrin");
MODULE_DESCRIPTION("Philips UDA1380 codec driver");
MODULE_LICENSE("GPL");

#if defined(UDA1380_DEBUG)
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "debug level (0-6)");
#endif

[-- Attachment #1.3: uda1380.h --]
[-- Type: text/x-chdr, Size: 6146 bytes --]

/*
 * Sound driver for Philips uda1380 - i2c related stuff
 *
 * Copyright (C) 2005 Roman Fietze <roman.fietze@telemotive.de>
 * Copyright (C) 2004 Giorgio Padrin
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file COPYING in the main directory of this archive for
 * more details.
 *
 * based on code by:
 * 
 * Copyright (c) 2002 Hewlett-Packard Company
 * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
 */

#ifndef _UDA1380_H_
#define _UDA1380_H_

#include <sound/driver.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <sound/info.h>
#include <linux/i2c.h>

#ifndef container_of
#define container_of    list_entry
#endif

#define FMT_I2S		0
#define FMT_LSB16	1
#define FMT_LSB18	2
#define FMT_LSB20	3
#define FMT_MSB		5


#define REG_MAX 0x24

#define EMCS_REG			0x00

#define EMCS_REG_EN_ADC			(1 << 11)
#define EMCS_REG_EN_DEC			(1 << 10)
#define EMCS_REG_EN_DAC			(1 <<  9)
#define EMCS_REG_EN_INT			(1 <<  8)
#define EMCS_REG_ADC_USE_WSPLL		(1 <<  5)
#define EMCS_REG_ADC_USE_SYSCLK		(0 <<  5)
#define EMCS_REG_DAC_USE_WSPLL		(1 <<  4)
#define EMCS_REG_DAC_USE_SYSCLK		(0 <<  4)

#define EMCS_REG_SC_MASK		(3 <<  2)
#define EMCS_REG_SC_768FS		(3 <<  2)
#define EMCS_REG_SC_512FS		(2 <<  2)
#define EMCS_REG_SC_384FS		(1 <<  2)
#define EMCS_REG_SC_256FS		(0 <<  2)

#define EMCS_REG_PLL_MASK		(3 <<  0)
#define EMCS_REG_PLL_6TO12		(0 <<  0)
#define EMCS_REG_PLL_12TO25		(1 <<  0)
#define EMCS_REG_PLL_25TO50		(2 <<  0)
#define EMCS_REG_PLL_50TO100		(3 <<  0)

#define I2S_REG				0x01

#define I2S_REG_SFORO_MASK		(7 << 0)
#define I2S_REG_SFORO_SHIFT		0
#define I2S_REG_SFORI_MASK		(7 << 8)
#define I2S_REG_SFORI_SHIFT		8
#define I2S_REG_SEL_DECIMATOR		(0 << 6)
#define I2S_REG_SEL_MIXER		(1 << 6)
#define I2S_REG_SIM_SLAVE		(0 << 4)
#define I2S_REG_SIM_MASTER		(1 << 4)

#define PWR_REG				0x02

#define PWR_REG_PON_PLL			(1 << 15)
#define PWR_REG_PON_HP			(1 << 13)
#define PWR_REG_PON_DAC			(1 << 10)
#define PWR_REG_PON_BIAS		(1 << 8)
#define PWR_REG_EN_AVC			(1 << 7)
#define PWR_REG_PON_AVC			(1 << 6)
#define PWR_REG_PON_LNA			(1 << 4)
#define PWR_REG_PON_PGAL		(1 << 3)
#define PWR_REG_PON_ADCL		(1 << 2)
#define PWR_REG_PON_PGAR		(1 << 1)
#define PWR_REG_PON_ADCR		(1 << 0)

/* analog mixer */
#define AMIX_REG			0x03

#define AMIX_AVCL(x)			(((x) & 0x3f) << 8)
#define AMIX_AVCL_MASK			(0x3f << 8)
#define AMIX_AVCR(x)			(((x) & 0x3f) << 0)
#define AMIX_AVCR_MASK			(0x3f << 0)

/* digital master volume control register */
#define MASTER_VOL_REG			0x10

#define MASTER_VCL(x)			(((x) & 0xff) << 8)
#define MASTER_VCL_MASK			(0xff << 8)
#define MASTER_VCR(x)			(((x) & 0xff) << 0)
#define MASTER_VCR_MASK			(0xff << 0)

/* digital mixer control register */
#define MIXER_VOL_REG			0x11

#define MIXER_VC_CH2(x)			(((x) & 0xff) << 8)
#define MIXER_VC_CH2_MASK		(0xff << 8)
#define MIXER_VC_CH1(x)			(((x) & 0xff) << 0)
#define MIXER_VC_CH1_MASK		(0xff << 0)

/* mode, bass boost, treble */
#define MBT_REG				0x12
#define MBT_MODE_MASK			(3 << 14)
#define MBT_MODE_FLAT			(0 << 14)
#define MBT_MODE_MIN			(1 << 14)
#define MBT_MODE_MAX			(3 << 14)
#define MBT_MODE_SHIFT			(14)
#define MBT_TRL_WIDTH			2
#define MBT_TRL_MASK			(3 << 12)
#define MBT_TRL(x)			((x) << 12)
#define MBT_TRR_MASK			(3 << 4)
#define MBT_TRR(x)			((x) << 4)
#define MBT_BBL_WIDTH			4
#define MBT_BBL_MASK			(0xf << 8)
#define MBT_BBL(x)			((x) << 8)
#define MBT_BBR_MASK			(0xf << 0)
#define MBT_BBR(x)			((x) << 0)

/* channel2 is from decimation filter */
/* channel1 is from digital data from I2S */

#define MMCDM_REG			0x13
#define MMCDM_MASTER_MUTE		(1 << 14)
#define MMCDM_CHANNEL2_MUTE		(1 << 11)
#define MMCDM_CHANNEL2_DEEMPHASIS_OFF	(0 << 8)
#define MMCDM_CHANNEL2_DEEMPHASIS_32KHZ	(1 << 8)
#define MMCDM_CHANNEL2_DEEMPHASIS_44KHZ	(2 << 8)
#define MMCDM_CHANNEL2_DEEMPHASIS_48KHZ	(3 << 8)
#define MMCDM_CHANNEL2_DEEMPHASIS_96KHZ	(4 << 8)
#define MMCDM_CHANNEL2_DEEMPHASIS_MASK	(7 << 8)
#define MMCDM_CHANNEL2_DEEMPHASIS_SHIFT	(8)
#define MMCDM_CHANNEL1_MUTE		(1 << 3)
#define MMCDM_CHANNEL1_DEEMPHASIS_32KHZ	(1 << 0)
#define MMCDM_CHANNEL1_DEEMPHASIS_44KHZ	(2 << 0)
#define MMCDM_CHANNEL1_DEEMPHASIS_48KHZ	(3 << 0)
#define MMCDM_CHANNEL1_DEEMPHASIS_96KHZ	(4 << 0)
#define MMCDM_CHANNEL1_DEEMPHASIS_MASK	(7 << 0)
#define MMCDM_CHANNEL1_DEEMPHASIS_SHIFT	(0)

#define MIXER_CTL_REG			0x14

#define MIXER_CTL_DAC_POLARITY_INVERT	(1 << 15)
#define MIXER_CTL_SEL_5TH_ORDER_NS	(1 << 14)
#define MIXER_CTL_MIX_POS	        (1 << 13)
#define MIXER_CTL_MIX		        (1 << 12)
#define MIXER_CTL_SILENCE	        (1 << 7)
#define MIXER_CTL_SDET_ON	        (1 << 6)
#define MIXER_CTL_SDET_VALUE_MASK       (3 << 4)
#define MIXER_CTL_SDET_VALUE_SHIFT	4
#define MIXER_CTL_OVERSAMPLING_MASK     (3 << 0)
#define MIXER_CTL_OVERSAMPLING_SHIFT    0

#define DEC_VOL_REG			0x20

#define DEC_VCL(x)	(((x) & 0xff) << 8)
#define DEC_VCL_MASK	(0xff << 8)
#define DEC_VCR(x)	(((x) & 0xff) << 0)
#define DEC_VCR_MASK	(0xff << 0)

#define DPM_REG				0x21

#define DPM_MUTE_ADC	(1 << 15)
#define DPM_GAINL(x)	(((x) & 0xf) << 8)
#define DPM_GAINL_MASK	(0xf << 8)
#define DPM_GAINR(x)	(((x) & 0xf) << 0)
#define DPM_GAINR_MASK	(0xf << 0)

#define DEC_ADC_REG			0x22

#define DEC_ADC_POL_INV			(1 << 12)
#define DEC_ADC_VGA_CTRL(x)		((x) << 8)
#define DEC_ADC_VGA_CTRL_MASK		(0xf << 8)
#define DEC_ADC_SEL_LNA			(1 << 3)
#define DEC_ADC_SEL_MIC			(1 << 2)
#define DEC_ADC_SKIP_DCFIL		(1 << 1)
#define DEC_ADC_EN_DCFIL		(1 << 0)

#define DEC_AGC_REG			0x23

#define DEC_AGC_EN			(1 << 0)


int snd_chip_uda1380_mixer_new(snd_card_t *card, struct i2c_client **clnt, int _mic_enable);
void snd_chip_uda1380_mixer_del(snd_card_t *card, struct i2c_client *clnt);

// int uda1380_configure(struct i2c_client *client, struct uda1380_cfg *conf);
void uda1380_configure(struct i2c_client *client, long rate);
int uda1380_mute(struct i2c_client *clnt, int mute);

#endif /* _UDA1380_H_ */

[-- Attachment #1.4: mpc5200-uda1380.c --]
[-- Type: text/x-csrc, Size: 23735 bytes --]

/*
  Copyright (C) 2005 Roman Fietze, Telemotive AG

  Audio driver for the FreeScale MPC5200 PSC in I2S codec
  mode. Optionally using I2C to an UDA1380 to control the Codec.

  This code is released under GPL (GNU Public License) with absolutely
  no warranty. Please see http://www.gnu.org/ for a complete discussion
  of the GPL.

  based on code by:

  Copyright (C) 2003 Christian Pellegrin
  Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V.
  Copyright (c) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
  Copyright (c) 2002 Hewlett-Packard Company
  Copyright (c) 2000 Nicolas Pitre <nico@cam.org>

*/

/*
  mpc5200-uda1380.c
*/


#include <linux/module.h>
#include <sound/driver.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/sound.h>
#include <linux/soundcard.h>
#include <linux/i2c.h>
#include <linux/kmod.h>

#include <asm/semaphore.h>
#include <asm/uaccess.h>

#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include "../i2c/uda1380.h"

#include <asm/mpc5xxx_snd.h>

#undef SND_MPC5200_DEBUG
// #define SND_MPC5200_DEBUG

// #undef SND_MPC5200_SIMPLE_PROC
#define SND_MPC5200_SIMPLE_PROC

#if defined(SND_MPC5200_DEBUG)
#define PRDEBUG(level, format, x... )  do {if (level <= debug) printk(KERN_INFO format, ## x );} while (0)
#else
#define PRDEBUG(level, format, x... )	do {} while(0)
#endif


enum
{
	CMD_EXTSW	/* optional */
};


typedef enum _stream_id
{
	PLAYBACK	= 0,	/* playback stream index */
	CAPTURE,		/* capture stream index */

	NUM_STREAMS		/* every "card" or PSC can have up to two (snd_pcm_sub)streams */
} stream_id_t;

typedef struct _mpc5200_uda1380
{
	snd_card_t *card;
	snd_pcm_t *pcm;
	int cardid;
	struct i2c_client *uda1380;	/* optional, no mixer if NULL */
	int extsw;
	snd_info_entry_t *extsw_proc_entry;
#if !defined(SND_MPC5200_SIMPLE_PROC)
	wait_queue_head_t extsw_wait;
#endif
	long samplerate;
	snd_mpc5xxx_i2s_card_t *i2s_card;
	snd_mpc5xxx_i2s_substream_t *i2s_substreams[NUM_STREAMS];
} mpc5200_uda1380_t;

static const char DRIVER_KERNEL_NAME[] = "snd-mpc5200";
static const char DRIVER_NAME[] = "MPC5xxx I2S UDA1380";

static int debug = -1;

static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
static int uda[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
static int slave[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
static int spi[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
static int mic[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
static int extsw[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
static int loopback[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
static snd_card_t *cards[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* initialize to NULL */
static const int allowed[SNDRV_CARDS] = {0, 1, 1, 1, 0, 0, 1, 0}; /* allow PSC 1,2,3,6 */

#if !defined(SND_MPC5200_SIMPLE_PROC)
static char snd_mpc5200_extsw_data[2] = {'0', '\n'};
#endif

static void snd_mpc5200_uda1380_set_samplerate(mpc5200_uda1380_t *chip, long rate)
{
	struct i2c_client *uda1380 = chip->uda1380;
	int mute;

	PRDEBUG(1,
		"%s(id=%d,rate=%ld) chip->samplerate=%ld\n",
		__FUNCTION__,
		chip->cardid, rate,
		chip->samplerate);

	if (rate == chip->samplerate)
		return;

	mute = uda1380_mute(uda1380, 1);

	chip->samplerate = rate;
	udelay(125);

	uda1380_configure(uda1380, chip->samplerate = rate);
	uda1380_mute(uda1380, mute);

	PRDEBUG(1, "%s done\n", __FUNCTION__);
}


static const snd_pcm_hardware_t snd_mpc5200_playback_capture =
{
	.info			= (SNDRV_PCM_INFO_INTERLEAVED |
				   SNDRV_PCM_INFO_BLOCK_TRANSFER |
				   SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
	.formats		= SNDRV_PCM_FMTBIT_S16_BE,
	.rates			= SNDRV_PCM_RATE_CONTINUOUS,
	.rate_min		= SND_PSC_AUDIO_RATE_MIN,
	.rate_max		= SND_PSC_AUDIO_RATE_MAX,
	.channels_min		= 2,
	.channels_max		= 2,
	.buffer_bytes_max	= SND_PSC_MAX_BUFFER_SIZE,
	.period_bytes_min	= SND_PSC_MIN_PERIOD_SIZE,
	.period_bytes_max	= SND_PSC_MAX_PERIOD_SIZE,
	.periods_min		= 1,
	.periods_max		= 1024,
	.fifo_size		= 0,
};

#if 0
static const snd_pcm_hardware_t snd_mpc5200_playback =
{
	.info			= (SNDRV_PCM_INFO_INTERLEAVED |
				   SNDRV_PCM_INFO_BLOCK_TRANSFER |
				   SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
	.formats		= SNDRV_PCM_FMTBIT_S16_BE,
	.rates			= SNDRV_PCM_RATE_CONTINUOUS,
	.rate_min		= SND_PSC_AUDIO_RATE_MIN,
	.rate_max		= SND_PSC_AUDIO_RATE_MAX,
	.channels_min		= 2,
	.channels_max		= 2,
	.buffer_bytes_max	= SND_PSC_MAX_BUFFER_SIZE,
	.period_bytes_min	= SND_PSC_MIN_PERIOD_SIZE,
	.period_bytes_max	= SND_PSC_MAX_PERIOD_SIZE,
	.periods_min		= 1,
	.periods_max		= 1024,
	.fifo_size		= 0,
};
#endif

static int snd_card_mpc5200_playback_open(snd_pcm_substream_t *substream)
{
	mpc5200_uda1380_t *chip = snd_pcm_substream_chip(substream);
	snd_pcm_runtime_t *runtime = substream->runtime;
	int err;

	PRDEBUG(1, "%s\n", __FUNCTION__);
	runtime->hw = snd_mpc5200_playback_capture;

	err = snd_psc_substream_open(chip->i2s_substreams[PLAYBACK], substream);
	if (err < 0)
		return err;

        if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
                return err;

	return 0;
}


static int snd_card_mpc5200_playback_close(snd_pcm_substream_t *substream)
{
	mpc5200_uda1380_t *chip = snd_pcm_substream_chip(substream);
	PRDEBUG(1, "%s\n", __FUNCTION__);
	return snd_psc_substream_close(chip->i2s_substreams[PLAYBACK]);
}


static int snd_card_mpc5200_playback_ioctl(snd_pcm_substream_t *substream,
					   unsigned int cmd,
					   void *arg)
{
	PRDEBUG(1, "%s(cmd=%d)\n", __FUNCTION__, cmd);
	return snd_pcm_lib_ioctl(substream, cmd, arg);
}


static int snd_card_mpc5200_playback_hw_params(snd_pcm_substream_t *substream,
					       snd_pcm_hw_params_t *hw_params)
{
	mpc5200_uda1380_t *chip = snd_pcm_substream_chip(substream);
	snd_pcm_runtime_t *runtime = substream->runtime;
	int err;

	err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
	{
		if (err < 0)
			printk(KERN_ERR "%s: memory allocation failed\n", DRIVER_NAME);
		return err;
	}

	/* set requested samplerate on an optional UDA1380 */
	if (chip->uda1380)
		snd_mpc5200_uda1380_set_samplerate(chip, runtime->rate);

	return snd_psc_substream_params(chip->i2s_substreams[PLAYBACK],
					runtime->rate,
					runtime->channels,
					snd_pcm_format_width(runtime->format),
					snd_pcm_lib_buffer_bytes(substream),
					snd_pcm_lib_period_bytes(substream));
}


static int snd_card_mpc5200_playback_prepare(snd_pcm_substream_t *substream)
{
	mpc5200_uda1380_t *chip = snd_pcm_substream_chip(substream);
	snd_pcm_runtime_t *runtime = substream->runtime;

	/* set requested samplerate on an optional UDA1380 */
	if (chip->uda1380)
		snd_mpc5200_uda1380_set_samplerate(chip, runtime->rate);

	return snd_psc_substream_params(chip->i2s_substreams[PLAYBACK],
					runtime->rate,
					runtime->channels,
					snd_pcm_format_width(runtime->format),
					snd_pcm_lib_buffer_bytes(substream),
					snd_pcm_lib_period_bytes(substream));
}


static int snd_card_mpc5200_playback_trigger(snd_pcm_substream_t *substream, int cmd)
{
	mpc5200_uda1380_t *chip = snd_pcm_substream_chip(substream);

	switch (cmd)
	{
	case SNDRV_PCM_TRIGGER_START:
		PRDEBUG(1, "%s(%p,START)\n", __FUNCTION__, substream);
		return snd_psc_substream_start(chip->i2s_substreams[PLAYBACK],
					       substream->runtime->dma_addr);

	case SNDRV_PCM_TRIGGER_STOP:
		PRDEBUG(1, "%s(%p,STOP)\n", __FUNCTION__, substream);
		return snd_psc_substream_stop(chip->i2s_substreams[PLAYBACK]);
	}

	return -EINVAL;
}


static snd_pcm_uframes_t snd_card_mpc5200_playback_pointer(snd_pcm_substream_t *substream)
{
	mpc5200_uda1380_t *chip = snd_pcm_substream_chip(substream);
	size_t ret = snd_psc_get_substream_pos(chip->i2s_substreams[PLAYBACK]);
	PRDEBUG(3, "%s returns %u\n", __FUNCTION__, ret);
	return bytes_to_frames(substream->runtime, ret);
}


static void snd_card_mpc5200_playback_capture_irq(void *_substream)
{
	snd_pcm_substream_t *substream = _substream;
	PRDEBUG(3, "%s(%p)\n", __FUNCTION__, substream);
	snd_pcm_period_elapsed(substream);
	return IRQ_HANDLED;
}


static int snd_card_mpc5200_capture_open(snd_pcm_substream_t *substream)
{
	mpc5200_uda1380_t *chip = snd_pcm_substream_chip(substream);
	snd_pcm_runtime_t *runtime = substream->runtime;
	int err;

	PRDEBUG(1, "%s\n", __FUNCTION__);

	runtime->hw = snd_mpc5200_playback_capture;

	err = snd_psc_substream_open(chip->i2s_substreams[CAPTURE], substream);
	if (err < 0)
		return err;

        if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
                return err;

	return 0;
}


static int snd_card_mpc5200_capture_close(snd_pcm_substream_t *substream)
{
	mpc5200_uda1380_t *chip = snd_pcm_substream_chip(substream);
	PRDEBUG(1, "%s:\n", __FUNCTION__);
	return snd_psc_substream_close(chip->i2s_substreams[CAPTURE]);
}


static int snd_card_mpc5200_capture_ioctl(snd_pcm_substream_t *substream,
					  unsigned int cmd, void *arg)
{
	PRDEBUG(1, "%s:\n", __FUNCTION__);
	return snd_pcm_lib_ioctl(substream, cmd, arg);
}


static int snd_card_mpc5200_capture_hw_params(snd_pcm_substream_t *substream,
					      snd_pcm_hw_params_t *hw_params)
{
	mpc5200_uda1380_t *chip = snd_pcm_substream_chip(substream);
	snd_pcm_runtime_t *runtime = substream->runtime;
	int err;

	err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
	{
		if (err < 0)
			printk(KERN_ERR "%s: memory allocation failed\n", DRIVER_NAME);
		return err;
	}

	/* set requested samplerate on an optional UDA1380 */
	if (chip->uda1380)
		snd_mpc5200_uda1380_set_samplerate(chip, runtime->rate);

	return snd_psc_substream_params(chip->i2s_substreams[CAPTURE],
					runtime->rate,
					runtime->channels,
					snd_pcm_format_width(runtime->format),
					snd_pcm_lib_buffer_bytes(substream),
					snd_pcm_lib_period_bytes(substream));
}


static int snd_card_mpc5200_capture_prepare(snd_pcm_substream_t *substream)
{
	mpc5200_uda1380_t *chip = snd_pcm_substream_chip(substream);
	snd_pcm_runtime_t *runtime = substream->runtime;

	/* set requested samplerate on an optional UDA1380 */
	if (chip->uda1380)
		snd_mpc5200_uda1380_set_samplerate(chip, runtime->rate);

	return snd_psc_substream_params(chip->i2s_substreams[CAPTURE],
					runtime->rate,
					runtime->channels,
					snd_pcm_format_width(runtime->format),
					snd_pcm_lib_buffer_bytes(substream),
					snd_pcm_lib_period_bytes(substream));
}


static int snd_card_mpc5200_capture_trigger(snd_pcm_substream_t *substream, int cmd)
{
	mpc5200_uda1380_t *chip = snd_pcm_substream_chip(substream);

	switch (cmd)
	{
	case SNDRV_PCM_TRIGGER_START:
		PRDEBUG(1, "%s(%p,START)\n", __FUNCTION__, substream);
		return snd_psc_substream_start(chip->i2s_substreams[CAPTURE],
					       substream->runtime->dma_addr);

	case SNDRV_PCM_TRIGGER_STOP:
		PRDEBUG(1, "%s(%p,STOP)\n", __FUNCTION__, substream);
		return snd_psc_substream_stop(chip->i2s_substreams[CAPTURE]);
	}

	return -EINVAL;
}


static snd_pcm_uframes_t snd_card_mpc5200_capture_pointer(snd_pcm_substream_t *substream)
{
	mpc5200_uda1380_t *chip = snd_pcm_substream_chip(substream);
	size_t ret = snd_psc_get_substream_pos(chip->i2s_substreams[CAPTURE]);
	PRDEBUG(3, "%s returns %u\n", __FUNCTION__, ret);
	return bytes_to_frames(substream->runtime, ret);
}


static int snd_mpc5200_hw_free(snd_pcm_substream_t *substream)
{

	PRDEBUG(1, "%s\n", __FUNCTION__);
	return snd_pcm_lib_free_pages(substream);
}


static snd_pcm_ops_t snd_card_mpc5200_playback_ops =
{
	.open			= snd_card_mpc5200_playback_open,
	.close			= snd_card_mpc5200_playback_close,
	.ioctl			= snd_card_mpc5200_playback_ioctl,
	.hw_params	        = snd_card_mpc5200_playback_hw_params,
	.hw_free	        = snd_mpc5200_hw_free,
	.prepare		= snd_card_mpc5200_playback_prepare,
	.trigger		= snd_card_mpc5200_playback_trigger,
	.pointer		= snd_card_mpc5200_playback_pointer,
};


static snd_pcm_ops_t snd_card_mpc5200_capture_ops =
{
	.open			= snd_card_mpc5200_capture_open,
	.close			= snd_card_mpc5200_capture_close,
	.ioctl			= snd_card_mpc5200_capture_ioctl,
	.hw_params	        = snd_card_mpc5200_capture_hw_params,
	.hw_free	        = snd_mpc5200_hw_free,
	.prepare		= snd_card_mpc5200_capture_prepare,
	.trigger		= snd_card_mpc5200_capture_trigger,
	.pointer		= snd_card_mpc5200_capture_pointer,
};


static void snd_mpc5200_pcm_free(snd_pcm_t *pcm)
{
	mpc5200_uda1380_t *chip = snd_pcm_chip(pcm);

	snd_psc_substream_free(chip->i2s_substreams[PLAYBACK]);
	chip->i2s_substreams[PLAYBACK] = NULL;

	snd_psc_substream_free(chip->i2s_substreams[CAPTURE]);
	chip->i2s_substreams[CAPTURE] = NULL;
}


static int __init snd_mpc5200_pcm_create(mpc5200_uda1380_t *chip, int cardid)
{
	static char pcm_name[] = "PSC?_I2S_PCM";
	static char playback_name[] = "PSC?_I2S_PLAYBACK";
	static char capture_name[] = "PSC?_I2S_CAPTURE";
	int err;

	pcm_name[3] = '0' + cardid;
	playback_name[3] = '0' + cardid;
	capture_name[3] = '0' + cardid;

	PRDEBUG(1, "%s\n", __FUNCTION__);

	// audio power is turned on and a clock is set at the same time so this gives us a default
	// starting rate
	chip->samplerate = 0;

	if ((err = snd_pcm_new(chip->card, pcm_name, 0, 1, 1, &(chip->pcm))) < 0)
		return err;

	snd_pcm_set_ops(chip->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_mpc5200_playback_ops);
	snd_pcm_set_ops(chip->pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_mpc5200_capture_ops);
	snd_pcm_lib_preallocate_pages_for_all(chip->pcm, SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(), 0x10000, 0x10000);

	chip->pcm->private_data = chip;
	chip->pcm->private_free = snd_mpc5200_pcm_free;
	chip->pcm->info_flags = 0;

	/* setup playback and capture */
	strncpy(chip->pcm->name, pcm_name, sizeof(chip->pcm->name));

	/* setup DMA structures */
	err = snd_psc_substream_create(chip->i2s_substreams + PLAYBACK,
				       chip->i2s_card,
				       playback_name,
				       PLAYBACK,
				       snd_card_mpc5200_playback_capture_irq);
	if (err < 0)
		return err;

	err = snd_psc_substream_create(chip->i2s_substreams + CAPTURE,
				       chip->i2s_card,
				       capture_name,
				       CAPTURE,
				       snd_card_mpc5200_playback_capture_irq);
	if (err < 0)
		return err;

	return 0;
}


static int
snd_mpc5200_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
	static const char *extsws[]	= {"Line", "Ext.Mic"};

	PRDEBUG(2, "%s(%p,%p)\n", __FUNCTION__, kcontrol, uinfo);

	switch (kcontrol->private_value)
	{
	case CMD_EXTSW:
		uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
		uinfo->count = 1;
		uinfo->value.enumerated.items = ARRAY_SIZE(extsws);
		if (uinfo->value.enumerated.item >= ARRAY_SIZE(extsws))
			uinfo->value.enumerated.item = ARRAY_SIZE(extsws) - 1;
		strncpy(uinfo->value.enumerated.name,
			extsws[uinfo->value.enumerated.item],
			sizeof(uinfo->value.enumerated.name));
		return 0;
	}


	printk(KERN_WARNING "%s called with bad private info: %ld\n", __FUNCTION__, kcontrol->private_value);
	return -EINVAL;
}


static int
snd_mpc5200_put_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
	mpc5200_uda1380_t *chip = snd_kcontrol_chip(kcontrol);

	PRDEBUG(2, "%s(%p,%p) value=%lu chip=%p\n",
		__FUNCTION__,
		kcontrol, ucontrol,
		kcontrol->private_value,
		chip);

	switch (kcontrol->private_value)
	{
	case CMD_EXTSW:
		if (!extsw[chip->cardid])
			ucontrol->value.enumerated.item[0] = 0;
		chip->extsw = ucontrol->value.enumerated.item[0];
		PRDEBUG(2, "%s: extsw=%d\n", __FUNCTION__, chip->extsw);
#if !defined(SND_MPC5200_SIMPLE_PROC)
		if (chip->extsw_proc_entry)
		{
			snd_mpc5200_extsw_data[0] = chip->extsw ? '1' : '0';
			wake_up(&chip->extsw_wait);
		}
#endif
		return 0;
	}

	printk(KERN_WARNING "%s called with bad private info: %ld\n", __FUNCTION__, kcontrol->private_value);
	return -EINVAL;
}


static int
snd_mpc5200_get_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
	mpc5200_uda1380_t *chip = snd_kcontrol_chip(kcontrol);

	PRDEBUG(3, "%s(%p,%p) private_value=%ld\n",
		__FUNCTION__,
		kcontrol, ucontrol,
		kcontrol->private_value);

	switch (kcontrol->private_value)
	{
	case CMD_EXTSW:
		ucontrol->value.enumerated.item[0] = chip->extsw;
		PRDEBUG(2, "%s: extsw=%d\n", __FUNCTION__, chip->extsw);
		return 0;
	}
	printk(KERN_WARNING "%s called with bad private info: %ld\n", __FUNCTION__, kcontrol->private_value);
	return -EINVAL;
}


#define MPC5200(xname, pvt_value) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
  .name = xname, \
  .info = snd_mpc5200_info, \
  .get = snd_mpc5200_get_single, \
  .put = snd_mpc5200_put_single, \
  .private_value = pvt_value \
}


/* The name "Capture Source" seems to confuse ALSA. Name it differently. */
static snd_kcontrol_new_t snd_mpc5200_extsw_controls = MPC5200("Line MUX", CMD_EXTSW);


#if defined(SND_MPC5200_SIMPLE_PROC)

static void snd_mpc5200_extsw_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
{
	mpc5200_uda1380_t *chip = entry->private_data;

	PRDEBUG(2, "%s(%p,%p) cs=%d\n", __FUNCTION__, entry, buffer, chip->extsw);
	snd_iprintf(buffer, "%d\n", chip->extsw);
}

#else

static unsigned int snd_mpc5200_extsw_poll(snd_info_entry_t *entry,
					   void *private,
					   struct file *file,
					   poll_table *wait)
{
	mpc5200_uda1380_t *chip = entry->private_data;

	PRDEBUG(2, "%s(%p,%p,%p,%p)\n",
		__FUNCTION__,
		entry, private, file, wait);

	poll_wait(file, &chip->extsw_wait, wait);
	PRDEBUG(2, "%s: return from poll_wait\n", __FUNCTION__);

	return POLLIN | POLLRDNORM;
}


static long snd_mpc5200_extsw_read(snd_info_entry_t *entry,
				   void *private,
				   struct file *file,
				   char *buf, unsigned long count, unsigned long pos)
{
	mpc5200_uda1380_t *chip = entry->private_data;
	unsigned long size = count;

	PRDEBUG(2, "%s(%p,%p,%p,%p,%lu,%lu) cs=%d\n",
		__FUNCTION__,
		entry, private, file, buf, count, pos,
		chip->extsw);

	if (pos + size > sizeof(snd_mpc5200_extsw_data))
		size = sizeof(snd_mpc5200_extsw_data) - pos;
	if (copy_to_user(buf, snd_mpc5200_extsw_data + pos, size))
		return -EFAULT;

	return size;
}


static long long snd_mpc5200_extsw_llseek(snd_info_entry_t *entry, void *private,
					  struct file *file, long long offset, int orig)
{
	PRDEBUG(2, "%s(%p,%p,%p,%Lu,%d)\n",
		__FUNCTION__,
		entry, private, file, offset, orig);

	if (orig == 0 && offset >= 0)
		return file->f_pos = offset;
	else
		return -EINVAL;
}


static struct snd_info_entry_ops snd_mpc5200_extsw_ops =
{
	.read = snd_mpc5200_extsw_read,
	.poll = snd_mpc5200_extsw_poll,
	.llseek = snd_mpc5200_extsw_llseek
};

#endif


static void snd_mpc5200_card_free(snd_card_t *card)
{
	mpc5200_uda1380_t *chip = card->private_data;

	PRDEBUG(1, "%s(%p) chip=%p\n", __FUNCTION__, card, chip);

	if (chip->uda1380)
		snd_chip_uda1380_mixer_del(card, chip->uda1380);

	snd_psc_card_free(chip->i2s_card);
	kfree(chip);
	card->private_data = NULL;

#ifdef SND_DELAY_ON_EXIT
	snd_psc_delay(__FUNCTION__, 4000, 1);
#endif

	PRDEBUG(1, "%s done\n", __FUNCTION__);
}


static int __init snd_mpc5200_card_create(int cardid)
{
	snd_card_t *card;
	mpc5200_uda1380_t *chip;
	int err;

	PRDEBUG(1, "%s(%d)\n", __FUNCTION__, cardid);

	/* register the soundcard */
	card = snd_card_new(index[cardid], id[cardid], THIS_MODULE, 0);
	if (card == NULL)
		return -ENOMEM;

	PRDEBUG(2, "%s: card=%p created\n", __FUNCTION__, card);

	chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
	if (chip == NULL)
	{
		snd_card_free(card);
		return -ENOMEM;
	}

	PRDEBUG(2, "%s: chip=%p created\n", __FUNCTION__, chip);

	card->private_data = chip;
	card->private_free = snd_mpc5200_card_free;

	chip->card = card;
	chip->cardid = cardid;

#if !defined(SND_MPC5200_SIMPLE_PROC)
	init_waitqueue_head(&chip->extsw_wait);
#endif

	if (uda[cardid])
	{
		PRDEBUG(2, "%s: card %u: checking for UDA1380\n", __FUNCTION__, cardid);

		// mixer
		err = snd_chip_uda1380_mixer_new(chip->card, &chip->uda1380, mic[cardid]);
		if (err < 0)
		{
			printk(KERN_ERR "%s: cannot create or find mixer for card id=%d!\n", DRIVER_NAME, cardid);
			snd_card_free(card);
			return err;
		}
		else
		{
			// init mixer with default values
			uda1380_configure(chip->uda1380, chip->samplerate = SND_PSC_AUDIO_RATE_DEFAULT);
		}

		snprintf(card->mixername, sizeof(card->mixername), "MPC5xxx_UDA_PSC%u", cardid);

		if (extsw[cardid])
		{
			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_mpc5200_extsw_controls, chip))) < 0)
				return err;

			if ((err = snd_card_proc_new(card, "extsw", &chip->extsw_proc_entry)) < 0)
				return err;

#if defined(SND_MPC5200_SIMPLE_PROC)
			snd_info_set_text_ops(chip->extsw_proc_entry, chip, 64, snd_mpc5200_extsw_read);
#else
			chip->extsw_proc_entry->content = SNDRV_INFO_CONTENT_DATA;
			chip->extsw_proc_entry->private_data = chip;
			chip->extsw_proc_entry->c.ops = &snd_mpc5200_extsw_ops;
			chip->extsw_proc_entry->size = 8;
			chip->extsw_proc_entry->mode = S_IFREG | S_IRUGO;
#endif
		}

		snprintf(card->longname, sizeof(card->longname),
			 "Telemotive Board with I2S on PSC%u + Philips UDA1380", cardid);
	}
	else
	{
		chip->uda1380 = NULL;
		chip->extsw_proc_entry = NULL;
		snprintf(card->longname, sizeof(card->longname),
			 "Telemotive Board with I2S on PSC%u", cardid);
	}

	err = snd_psc_card_create(&chip->i2s_card, cardid,
				  slave[cardid], spi[cardid], loopback[cardid]);
	if (err < 0)
	{
		snd_card_free(card);
		return err;
	}

	if ((err = snd_mpc5200_pcm_create(chip, cardid)) < 0)
	{
		snd_card_free(card);
		return err;
	}

	strcpy(card->driver, "mpc5200-uda1380");
	snprintf(card->shortname, sizeof(card->shortname), "PSC%u", cardid);

	if ((err = snd_card_register(card)) < 0)
	{
		snd_card_free(card);
		return err;
	}

	cards[cardid] = card;

	return err;
}


static int __init snd_mpc5200_init(void)
{
	int err;
	size_t i;

	PRDEBUG(1, "%s\n", __FUNCTION__);

#if !defined(SND_MPC5200_DEBUG)
	if (debug != -1)
		printk(KERN_WARNING "%s: compiled without debug support\n", __FUNCTION__);
#endif

	/* be lazy */
	for (i = 0; i < SNDRV_CARDS; i++)
	{
		if (allowed[i] && enable[i])
		{
			err = snd_mpc5200_card_create(i);
			if (err < 0)
				return err;
		}
	}

	return 0;
}


static void __exit snd_mpc5200_exit(void)
{
	size_t cardid;

	PRDEBUG(1, "%s\n", __FUNCTION__);

	for (cardid = 0; cardid < SNDRV_CARDS; cardid++)
	{
		PRDEBUG(3, "%s: cardid=%u\n", __FUNCTION__, cardid);

		if (cards[cardid])
		{
			snd_card_free(cards[cardid]);
			cards[cardid] = NULL;
#ifdef SND_DELAY_ON_EXIT
			snd_psc_delay(__FUNCTION__, 4000, 1);
#endif
		}

		PRDEBUG(3, "%s: cardid=%u done\n", __FUNCTION__, cardid);
	}

	PRDEBUG(1, "%s exit\n", __FUNCTION__);
}


module_init(snd_mpc5200_init);
module_exit(snd_mpc5200_exit);

MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "debug level (0-6)");

module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for MPC5xxx soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for MPC5xxx soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable MPC5xxx soundcard.");
module_param_array(uda, bool, NULL, 0444);
MODULE_PARM_DESC(uda, "I2C adress of UDA1380 for MPC5xxx soundcard.");
module_param_array(slave, bool, NULL, 0444);
MODULE_PARM_DESC(slave, "Run I2S port in slave mode.");
module_param_array(spi, bool, NULL, 0444);
MODULE_PARM_DESC(spi, "Use SPI instead of I2S to communicate via the PSC.");
module_param_array(mic, bool, NULL, 0444);
MODULE_PARM_DESC(mic, "Microphone connected [0=no Mic,1=Mic].");
module_param_array(extsw, bool, NULL, 0444);
MODULE_PARM_DESC(extsw, "Enable external capture switch via GPIO.");
module_param_array(loopback, bool, NULL, 0444);
MODULE_PARM_DESC(loopback, "Enable MPC5xxx local loopback mode.");

MODULE_LICENSE("GPL");

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

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Lite5200B I2S driver
  2006-12-13  9:39   ` Pedro Luis D. L.
  2006-12-13 12:56     ` Roman Fietze
@ 2006-12-13 12:58     ` Roman Fietze
  2006-12-13 13:15     ` Roman Fietze
  2 siblings, 0 replies; 13+ messages in thread
From: Roman Fietze @ 2006-12-13 12:58 UTC (permalink / raw)
  To: linuxppc-embedded


[-- Attachment #1.1: Type: text/plain, Size: 244 bytes --]

Hello Pedro,

Here the kernel part of the driver.


Roman

-- 
Roman Fietze              Telemotive AG Büro Mühlhausen
Breitwiesen                            73347 Mühlhausen
Tel.: +49(0)7335/18493-45      http://www.telemotive.de


[-- Attachment #1.2: mpc5xxx.h --]
[-- Type: text/x-chdr, Size: 47498 bytes --]

/*
 * include/asm-ppc/mpc5xxx.h
 *
 * Prototypes, etc. for the Motorola MPC5xxx embedded cpu chips
 *
 * Author: Dale Farnsworth <dfarnsworth@mvista.com>
 *
 * 2003 (c) MontaVista, Software, Inc.  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.
 */
#ifndef __ASM_MPC5XXX_H
#define __ASM_MPC5XXX_H

#define MPC5xxx_FEC_DEBUG	0
#define MPC5xxx_SDMA_DEBUG	2

#ifndef __ASSEMBLY__
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/config.h>

#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/machdep.h>
#endif /* __ASSEMBLY */

#define MPC5xxx_MBAR		0xf0000000
#define MPC5xxx_MBAR_SIZE	0x10000000
#define MPC5xxx_SDRAM_START	(MPC5xxx_MBAR + 0x0034)
#define MPC5xxx_SDRAM_CONFIG_0	(MPC5xxx_MBAR + 0x0034)
#define MPC5xxx_SDRAM_STOP	(MPC5xxx_MBAR + 0x0038)
#define MPC5xxx_SDRAM_CONFIG_1	(MPC5xxx_MBAR + 0x0038)
#define MPC5xxx_ADREN		(MPC5xxx_MBAR + 0x0054)
#define MPC5xxx_CDM		(MPC5xxx_MBAR + 0x0200)
#define MPC5xxx_SFTRST		(MPC5xxx_MBAR + 0x0220)
#define MPC5xxx_SFTRST_BIT	0x01000000
#define MPC5xxx_CSC		(MPC5xxx_MBAR + 0x0300)
#define MPC5xxx_CS0CFG		(MPC5xxx_MBAR + 0x0300)
#define MPC5xxx_INTR		(MPC5xxx_MBAR + 0x0500)
#define MPC5xxx_GPT		(MPC5xxx_MBAR + 0x0600) /* General Purpose Timers 0..7 */
#define MPC5xxx_GPT0		(MPC5xxx_MBAR + 0x0600) /* General Purpose Timers 0..7 */
#define MPC5xxx_GPT1		(MPC5xxx_MBAR + 0x0610)
#define MPC5xxx_GPT2		(MPC5xxx_MBAR + 0x0620)
#define MPC5xxx_GPT3		(MPC5xxx_MBAR + 0x0630)
#define MPC5xxx_GPT4		(MPC5xxx_MBAR + 0x0640)
#define MPC5xxx_GPT5		(MPC5xxx_MBAR + 0x0650)
#define MPC5xxx_GPT6		(MPC5xxx_MBAR + 0x0660)
#define MPC5xxx_GPT7		(MPC5xxx_MBAR + 0x0670)
#define MPC5xxx_MSCAN1		(MPC5xxx_MBAR + 0x0900)	/* MSCAN Module 1 */
#define MPC5xxx_MSCAN2		(MPC5xxx_MBAR + 0x0980)	/* MSCAN Module 2 */
#define MPC5xxx_GPIO		(MPC5xxx_MBAR + 0x0b00)
#define MPC5xxx_WU_GPIO         (MPC5xxx_MBAR + 0x0c00)
#define MPC5xxx_PCI		(MPC5xxx_MBAR + 0x0d00)
#define MPC5xxx_SPI		(MPC5xxx_MBAR + 0x0f00)
#define MPC5xxx_USB_OHCI	(MPC5xxx_MBAR + 0x1000)
#define MPC5xxx_SDMA		(MPC5xxx_MBAR + 0x1200)
#define MPC5xxx_XLB		(MPC5xxx_MBAR + 0x1f00)
#define MPC5xxx_PSC1		(MPC5xxx_MBAR + 0x2000)
#define MPC5xxx_PSC2		(MPC5xxx_MBAR + 0x2200)
#define MPC5xxx_PSC3		(MPC5xxx_MBAR + 0x2400)
#define MPC5xxx_PSC4		(MPC5xxx_MBAR + 0x2600)
#define MPC5xxx_PSC5		(MPC5xxx_MBAR + 0x2800)
#define MPC5xxx_PSC6		(MPC5xxx_MBAR + 0x2C00)
#define MPC5xxx_FEC		(MPC5xxx_MBAR + 0x3000)
#define MPC5xxx_PCI_MCTI	(MPC5xxx_MBAR + 0x3800)
#define MPC5xxx_ATA		(MPC5xxx_MBAR + 0x3a00)
#define MPC5xxx_I2C1		(MPC5xxx_MBAR + 0x3d00)
#define MPC5xxx_I2C_MICR	(MPC5xxx_MBAR + 0x3d20)
#define MPC5xxx_I2C2		(MPC5xxx_MBAR + 0x3d40)
#ifdef CONFIG_MPC5100
#define MPC5xxx_SRAM		(MPC5xxx_MBAR + 0x4000)
#define MPC5xxx_SRAM_SIZE	(8*1024)
#define MPC5xxx_SDMA_MAX_TASKS	8
#elif defined(CONFIG_MPC5200)
#define MPC5xxx_SRAM		(MPC5xxx_MBAR + 0x8000)
#define MPC5xxx_SRAM_SIZE	(16*1024)
#define MPC5xxx_SDMA_MAX_TASKS		16
#define MPC5xxx_SDMA_MAX_INITIATORS	32
#endif

/* Bit definitions for PCI window configuration regsiers.
 *
 * Attention: here Motorola/FreeScale suddenly counts bits from right to left as all the others
 * do! */
#define	MPC5xxx_INIT_WINDOW_DISABLE		(0U << 0)
#define	MPC5xxx_INIT_WINDOW_ENABLE		(1U << 0)

#define	MPC5xxx_INIT_WINDOW_READ		(0U << 1)
#define	MPC5xxx_INIT_WINDOW_READ_LINE		(1U << 1)
#define	MPC5xxx_INIT_WINDOW_READ_MULTIPLE	(2U << 1)

#define MPC5xxx_INIT_WINDOW_MEM			(0U << 3)
#define MPC5xxx_INIT_WINDOW_IO			(1U << 3)

/* Interrupt priority register bits */
#define MPC5xxx_PERPRI_MASK(x)		(0xF0000000U >> (((x) & 0x07U) * (uint32_t)4))
#define MPC5xxx_PERPRI_PRI(x,p)		(((p) & 0x0FU) << ((7UL - ((x) & 0x07U)) * (uint32_t)4))
#define MPC5xxx_PERPRI_REG(x)		((&((struct mpc5xxx_intr *)MPC5xxx_INTR)->per_pri1) + ((x) / 8U))
#define MPC5xxx_PERPRI_SET(x,p)		do{*MPC5xxx_PERPRI_REG(x)=(*MPC5xxx_PERPRI_REG(x)&~MPC5xxx_PERPRI_MASK(x))|MPC5xxx_PERPRI_PRI((x),(p));} while(0)


/* GPT General Purpose Timers registers */

/* GPT Counter Input Register */
#define MPC5xxx_GPT_CIR_PRESCALE(prescale)	((uint32_t)((prescale) & 0xFFFFU) << 16)
#define MPC5xxx_GPT_CIR_COUNT(count)		((count) & 0xFFFFU)

/* GPT PWM Configuration Register */
#define MPC5xxx_GPT_PWMCR_WIDTH(width)		((uint32_t)((width) & 0xFFFFU) << 16)
#define MPC5xxx_GPT_PWMCR_PWMOP			(1U << 16)
#define MPC5xxx_GPT_PWMCR_LOAD			(1U << 0)

/* GPT Enable and Mode Select Register */
#define MPC5xxx_GPT_EMSR_WDEN			(1U << 15)
#define MPC5xxx_GPT_EMSR_CE			(1U << 12)
#define MPC5xxx_GPT_EMSR_OPEN_DRAIN		(1U << 9)
#define MPC5xxx_GPT_EMSR_GPIO(x)		(((x) & 0x03U) << 4)
#define MPC5xxx_GPT_EMSR_GPIO_MASK		MPC5xxx_GPT_EMSR_GPIO(3)
#define MPC5xxx_GPT_EMSR_GPIO_INPUT		MPC5xxx_GPT_EMSR_GPIO(0)
#define MPC5xxx_GPT_EMSR_GPIO_OUTPUT_0		MPC5xxx_GPT_EMSR_GPIO(2)
#define MPC5xxx_GPT_EMSR_GPIO_OUTPUT_1		MPC5xxx_GPT_EMSR_GPIO(3)
#define MPC5xxx_GPT_EMSR_TIMER_MS(x)		(((x) & 0x07U) << 0)
#define MPC5xxx_GPT_EMSR_TIMER_DISABLED		MPC5xxx_GPT_EMSR_TIMER_MS(0)
#define MPC5xxx_GPT_EMSR_TIMER_INPUT_CAPTURE	MPC5xxx_GPT_EMSR_TIMER_MS(1)
#define MPC5xxx_GPT_EMSR_TIMER_OUTPUT_COMPARE	MPC5xxx_GPT_EMSR_TIMER_MS(2)
#define MPC5xxx_GPT_EMSR_TIMER_PWM		MPC5xxx_GPT_EMSR_TIMER_MS(3)
#define MPC5xxx_GPT_EMSR_TIMER_GPIO		MPC5xxx_GPT_EMSR_TIMER_MS(4)
#define MPC5xxx_GPT_EMSR_TIMER_PIN  	    	(1U << 8)


/* MSCAN control register 0 (CANCTL0) bits */
#define MPC5xxx_MSCAN_RXFRM			(1U << 7)
#define MPC5xxx_MSCAN_RXACT			(1U << 6)
#define MPC5xxx_MSCAN_CSWAI			(1U << 5)
#define MPC5xxx_MSCAN_SYNCH			(1U << 4)
#define MPC5xxx_MSCAN_TIME			(1U << 3)
#define MPC5xxx_MSCAN_WUPE			(1U << 2)
#define MPC5xxx_MSCAN_SLPRQ			(1U << 1)
#define MPC5xxx_MSCAN_INITRQ			(1U << 0)

/* MSCAN control register 1 (CANCTL1) bits */
#define MPC5xxx_MSCAN_CANE			(1U << 7)
#define MPC5xxx_MSCAN_CLKSRC			(1U << 6)
#define MPC5xxx_MSCAN_LOOPB			(1U << 5)
#define MPC5xxx_MSCAN_LISTEN			(1U << 4)
#define MPC5xxx_MSCAN_WUPM			(1U << 2)
#define MPC5xxx_MSCAN_SLPAK			(1U << 1)
#define MPC5xxx_MSCAN_INITAK			(1U << 0)

/* MSCAN receiver flag register (CANRFLG) bits */
#define MPC5xxx_MSCAN_WUPIF			(1U << 7)
#define MPC5xxx_MSCAN_CSCIF			(1U << 6)
#define MPC5xxx_MSCAN_RSTAT1			(1U << 5)
#define MPC5xxx_MSCAN_RSTAT0			(1U << 4)
#define MPC5xxx_MSCAN_TSTAT1			(1U << 3)
#define MPC5xxx_MSCAN_TSTAT0			(1U << 2)
#define MPC5xxx_MSCAN_OVRIF			(1U << 1)
#define MPC5xxx_MSCAN_RXF			(1U << 0)

/* MSCAN receiver interrupt enable register (CANRIER) bits */
#define MPC5xxx_MSCAN_WUPIE			(1U << 7)
#define MPC5xxx_MSCAN_CSCIE			(1U << 6)
#define MPC5xxx_MSCAN_RSTATE1			(1U << 5)
#define MPC5xxx_MSCAN_RSTATE0			(1U << 4)
#define MPC5xxx_MSCAN_TSTATE1			(1U << 3)
#define MPC5xxx_MSCAN_TSTATE0			(1U << 2)
#define MPC5xxx_MSCAN_OVRIE			(1U << 1)
#define MPC5xxx_MSCAN_RXFIE			(1U << 0)

/* MSCAN transmitter flag register (CANTFLG) bits */
#define MPC5xxx_MSCAN_TXE2			(1U << 2)
#define MPC5xxx_MSCAN_TXE1			(1U << 1)
#define MPC5xxx_MSCAN_TXE0			(1U << 0)
#define MPC5xxx_MSCAN_TXE			(MPC5xxx_MSCAN_TXE2 | MPC5xxx_MSCAN_TXE1 | MPC5xxx_MSCAN_TXE0)

/* MSCAN transmitter interrupt enable register (CANTIER) bits */
#define MPC5xxx_MSCAN_TXIE2			MPC5xxx_MSCAN_TXE2
#define MPC5xxx_MSCAN_TXIE1			MPC5xxx_MSCAN_TXE1
#define MPC5xxx_MSCAN_TXIE0			MPC5xxx_MSCAN_TXIE0
#define MPC5xxx_MSCAN_TXIE			MPC5xxx_MSCAN_TXE

/* MSCAN transmitter message abort request (CANTARQ) bits */
#define MPC5xxx_MSCAN_ABTRQ2			MPC5xxx_MSCAN_TXE2
#define MPC5xxx_MSCAN_ABTRQ1			MPC5xxx_MSCAN_TXE1
#define MPC5xxx_MSCAN_ABTRQ0			MPC5xxx_MSCAN_TXE0

/* MSCAN transmitter message abort ack (CANTAAK) bits */
#define MPC5xxx_MSCAN_ABTAK2			MPC5xxx_MSCAN_TXE2
#define MPC5xxx_MSCAN_ABTAK1			MPC5xxx_MSCAN_TXE1
#define MPC5xxx_MSCAN_ABTAK0			MPC5xxx_MSCAN_TXE0

/* MSCAN transmit buffer selection (CANTBSEL) bits */
#define MPC5xxx_MSCAN_TX2			MPC5xxx_MSCAN_TXE2
#define MPC5xxx_MSCAN_TX1			MPC5xxx_MSCAN_TXE1
#define MPC5xxx_MSCAN_TX0			MPC5xxx_MSCAN_TXE0

/* MSCAN ID acceptance control register (CANIDAC) bits */
#define MPC5xxx_MSCAN_IDAM1			(1U << 5)
#define MPC5xxx_MSCAN_IDAM0			(1U << 4)
#define MPC5xxx_MSCAN_IDHIT2			MPC5xxx_MSCAN_TXE2
#define MPC5xxx_MSCAN_IDHIT1			MPC5xxx_MSCAN_TXE1
#define MPC5xxx_MSCAN_IDHIT0			MPC5xxx_MSCAN_TXE0


/* SDMA bits */
#define MPC5xxx_SDMA_IPR_HOLD			(1U << 7)


/* XLB bits */
#define MPC5xxx_XLB_CONFIG_PLDIS		(1U << 31)
#define MPC5xxx_XLB_CONFIG_USE_WWF		(1U << 14)


/* CDM bits */

/* CDM clock enable register bits */
#define MPC5xxx_CDM_CER_PSC6			(1U << 4)
#define MPC5xxx_CDM_CER_PSC1			(1U << 5)
#define MPC5xxx_CDM_CER_PSC2			(1U << 6)
#define MPC5xxx_CDM_CER_PSC345			(1U << 7)
#define MPC5xxx_CDM_PCI_CLK			(1U << 18)


/* GPIO port_config bits	*/
#define MPC5xxx_GPIO_PC_PSC1(x)			(((x) & 0x0FU) << 0)
#define MPC5xxx_GPIO_PC_PSC1_MASK		MPC5xxx_GPIO_PC_PSC1(0xFU)
#define MPC5xxx_GPIO_PC_PSC1_CODEC1_USB2	MPC5xxx_GPIO_PC_PSC1(0x1U)
#define MPC5xxx_GPIO_PC_PSC1_UART_NO_CD		MPC5xxx_GPIO_PC_PSC1(0x4U)
#define MPC5xxx_GPIO_PC_PSC1_UART_CD		MPC5xxx_GPIO_PC_PSC1(0x5U)
#define MPC5xxx_GPIO_PC_PSC1_CODEC1		MPC5xxx_GPIO_PC_PSC1(0x6U)
#define MPC5xxx_GPIO_PC_PSC1_CODEC1_MCLK	MPC5xxx_GPIO_PC_PSC1(0x7U)

#define MPC5xxx_GPIO_PC_PSC2(x)			(((x) & 0x0FU) << 4)
#define MPC5xxx_GPIO_PC_PSC2_MASK		MPC5xxx_GPIO_PC_PSC2(0xFU)
#define MPC5xxx_GPIO_PC_PSC2_GPIO		MPC5xxx_GPIO_PC_PSC3(0x0U)
#define MPC5xxx_GPIO_PC_PSC2_CAN		MPC5xxx_GPIO_PC_PSC2(0x1U)
#define MPC5xxx_GPIO_PC_PSC2_AC97		MPC5xxx_GPIO_PC_PSC2(0x2U)
#define MPC5xxx_GPIO_PC_PSC2_UART_NO_CD		MPC5xxx_GPIO_PC_PSC2(0x4U)
#define MPC5xxx_GPIO_PC_PSC2_UART_CD		MPC5xxx_GPIO_PC_PSC2(0x5U)
#define MPC5xxx_GPIO_PC_PSC2_CODEC2		MPC5xxx_GPIO_PC_PSC2(0x6U)
#define MPC5xxx_GPIO_PC_PSC2_CODEC2_MCLK	MPC5xxx_GPIO_PC_PSC2(0x7U)

#define MPC5xxx_GPIO_PC_PSC3(x)			(((x) & 0x0FU) << 8)
#define MPC5xxx_GPIO_PC_PSC3_MASK		MPC5xxx_GPIO_PC_PSC3(0xFU)
#define MPC5xxx_GPIO_PC_PSC3_GPIO		MPC5xxx_GPIO_PC_PSC3(0x0U)
#define MPC5xxx_GPIO_PC_PSC3_USB2		MPC5xxx_GPIO_PC_PSC3(0x1U)
#define MPC5xxx_GPIO_PC_PSC3_UART_NO_CD		MPC5xxx_GPIO_PC_PSC3(0x4U)
#define MPC5xxx_GPIO_PC_PSC3_UART_CD		MPC5xxx_GPIO_PC_PSC3(0x5U)
#define MPC5xxx_GPIO_PC_PSC3_CODEC3		MPC5xxx_GPIO_PC_PSC3(0x6U)
#define MPC5xxx_GPIO_PC_PSC3_CODEC3_MCLK	MPC5xxx_GPIO_PC_PSC3(0x7U)
#define MPC5xxx_GPIO_PC_PSC3_SPI		MPC5xxx_GPIO_PC_PSC3(0x8U)

#define MPC5xxx_GPIO_PC_USB(x)			(((x) & 0x03U) << 12)
#define MPC5xxx_GPIO_PC_USB_MASK		MPC5xxx_GPIO_PC_USB(0x3U)
#define MPC5xxx_GPIO_PC_USB_GPIO		MPC5xxx_GPIO_PC_USB(0x0U)
#define MPC5xxx_GPIO_PC_USB_USB			MPC5xxx_GPIO_PC_USB(0x1U)
#define MPC5xxx_GPIO_PC_USB_UARTS		MPC5xxx_GPIO_PC_USB(0x2U)

#define MPC5xxx_GPIO_PC_USB_SE			(1U << 14)
#define MPC5xxx_GPIO_PC_PCI_DIS			(1U << 15)

#define MPC5xxx_GPIO_PC_PSC6(x)			(((x) & 0x07U) << 20)
#define MPC5xxx_GPIO_PC_PSC6_MASK		MPC5xxx_GPIO_PC_PSC6(0x7U)
#define MPC5xxx_GPIO_PC_PSC6_UART_NO_CD		MPC5xxx_GPIO_PC_PSC6(0x5U)
#define MPC5xxx_GPIO_PC_PSC6_CODEC6		MPC5xxx_GPIO_PC_PSC6(0x7U)

#define MPC5xxx_GPIO_PC_IR_USB_CLK		(1U << 23)

#define MPC5xxx_GPIO_PC_ATA(x)			(((x) & 0x03U) << 24)
#define MPC5xxx_GPIO_PC_ATA_MASK		MPC5xxx_GPIO_PC_PSC2(0x3U)
#define MPC5xxx_GPIO_PC_ATA_NONE		MPC5xxx_GPIO_PC_PSC2(0x0U)
#define MPC5xxx_GPIO_PC_ATA_CSB45		MPC5xxx_GPIO_PC_PSC2(0x1U)
#define MPC5xxx_GPIO_PC_ATA_I2C2		MPC5xxx_GPIO_PC_PSC2(0x2U)
#define MPC5xxx_GPIO_PC_ATA_TMR01		MPC5xxx_GPIO_PC_PSC2(0x3U)


#define MPC5xxx_GPIO_PC_ALT(x)			(((x) & 0x03U) << 28)
#define MPC5xxx_GPIO_PC_ALT_MASK		MPC5xxx_GPIO_PC_ALT(0x3U)
#define MPC5xxx_GPIO_PC_ALT_NONE		MPC5xxx_GPIO_PC_ALT(0x0U)
#define MPC5xxx_GPIO_PC_ALT_CAN			MPC5xxx_GPIO_PC_ALT(0x1U)
#define MPC5xxx_GPIO_PC_ALT_SPI			MPC5xxx_GPIO_PC_ALT(0x2U)
#define MPC5xxx_GPIO_PC_ALT_BOTH		MPC5xxx_GPIO_PC_ALT(0x3U)


/* GPIO Simple GPIO bits, bit numbers are pin numbers, Valid for
 * simple_gpioe, simple_ode, simple_ddr, simple_dvo and simple_ival in
 * mpc5xxx_gpio.
 */
#define MPC5xxx_GPIO_BIT_PSC2_2			(1U << 6)
#define MPC5xxx_GPIO_BIT_PSC3_6			(1U << 12)
#define MPC5xxx_GPIO_BIT_PSC3_7			(1U << 13)
#define MPC5xxx_GPIO_BIT_USB1_0			(1U << 16)

/* GPIO Simple Interrupt bits
 */
#define MPC5xxx_GPIO_SINT_0			(1U << 0)
#define MPC5xxx_GPIO_SINT_1			(1U << 1)
#define MPC5xxx_GPIO_SINT_2			(1U << 2)
#define MPC5xxx_GPIO_SINT_3			(1U << 3)


/* Wakeup GPIO bits
 */
#define MPC5xxx_WU_GPIO_0			(1U << 0)
#define MPC5xxx_WU_GPIO_2			(1U << 2)
#define MPC5xxx_WU_GPIO_6			(1U << 6)
#define MPC5xxx_WU_GPIO_7			(1U << 7)


/* PSC definitions */

#define MPC5xxx_PSC_FIFO_SIZE		(512U)

/* PSC CTUR bits		*/
#define MPC5xxx_PSC_CTUR_CODEC(x)	((x) & 0xFFU)

/* PSC CTLR bits		*/
#define MPC5xxx_PSC_CTLR_CODEC(x)	((x) & 0xFFU)

/* PSC CCR bits			*/
#define MPC5xxx_PSC_CCR_CODEC(fsd,bcd)	((((uint16_t)(fsd) & 0xFFU) << 8) | ((bcd) & 0xFFU))

/* PSC MCLOCK			*/
#define MPC5xxx_PSC_MCLOCK_ENABLE	(1U << 15)
#define MPC5xxx_PSC_MCLOCK_MCLKDIV(x)	((uint16_t)(x) & 0x1FFU)


/* PSC SICR bits		*/
#define MPC5xxx_PSC_SICR_DTS1_AFTER	(1UL << 29)
#define MPC5xxx_PSC_SICR_SHDIR_LSB	(1UL << 28)

#define MPC5xxx_PSC_SICR_SIM(x)		(((uint32_t)(x) & 0x0FU) << 24)
#define MPC5xxx_PSC_SICR_SIM_UART	MPC5xxx_PSC_SICR_SIM(0U)
#define MPC5xxx_PSC_SICR_SIM_CODEC_8	MPC5xxx_PSC_SICR_SIM(1U)
#define MPC5xxx_PSC_SICR_SIM_CODEC_16	MPC5xxx_PSC_SICR_SIM(2U)
#define MPC5xxx_PSC_SICR_SIM_AC97	MPC5xxx_PSC_SICR_SIM(3U)
#define MPC5xxx_PSC_SICR_SIM_SIR	MPC5xxx_PSC_SICR_SIM(4U)
#define MPC5xxx_PSC_SICR_SIM_MIR	MPC5xxx_PSC_SICR_SIM(5U)
#define MPC5xxx_PSC_SICR_SIM_FIR	MPC5xxx_PSC_SICR_SIM(6U)
#define MPC5xxx_PSC_SICR_SIM_CODEC_24	MPC5xxx_PSC_SICR_SIM(7U)
#define MPC5xxx_PSC_SICR_SIM_UART_DCD	MPC5xxx_PSC_SICR_SIM(8U)
#define MPC5xxx_PSC_SICR_SIM_SIR_DCD	MPC5xxx_PSC_SICR_SIM(12U)
#define MPC5xxx_PSC_SICR_SIM_CODEC_32	MPC5xxx_PSC_SICR_SIM(15U)

#define MPC5xxx_PSC_SICR_GENCLK_INT	(1UL << 23)
#define MPC5xxx_PSC_SICR_MULTIWD	(1UL << 22)
#define MPC5xxx_PSC_SICR_CLKPOL_RISE	(1UL << 21)
#define MPC5xxx_PSC_SICR_SYNCPOL_RISE	(1UL << 20)

#define MPC5xxx_PSC_SICR_SPI		(1UL << 15)
#define MPC5xxx_PSC_SICR_MSTR		(1UL << 14)
#define MPC5xxx_PSC_SICR_CPOL		(1UL << 13)
#define MPC5xxx_PSC_SICR_CPHA		(1UL << 12)
#define MPC5xxx_PSC_SICR_USEEOF		(1UL << 11)


/* SPI control1 bits		*/
#define MPC5xxx_SPI_CONTROL1_LSBFE	(1U << 0)
#define MPC5xxx_SPI_CONTROL1_SSOE	(1U << 1)
#define MPC5xxx_SPI_CONTROL1_CPHA	(1U << 2)
#define MPC5xxx_SPI_CONTROL1_CPOL	(1U << 3)
#define MPC5xxx_SPI_CONTROL1_MSTR	(1U << 4)
#define MPC5xxx_SPI_CONTROL1_SPE	(1U << 6)
#define MPC5xxx_SPI_CONTROL1_SPIE	(1U << 7)

/* SPI control2 bits		*/
#define MPC5xxx_SPI_CONTROL2_SPC0	(1U << 0)
#define MPC5xxx_SPI_CONTROL2_SPISWAI	(1U << 1)

/* SPI baud bits		*/
#define MPC5xxx_SPI_BAUD_SPR(x)		(((x) & 0x07U) << 0)
#define MPC5xxx_SPI_BAUD_SPPR(x)	(((x) & 0x07U) << 4)

/* SPI status bits		*/
#define MPC5xxx_SPI_STATUS_MODF		(1U << 4)
#define MPC5xxx_SPI_STATUS_WCOL		(1U << 6)
#define MPC5xxx_SPI_STATUS_SPIF		(1U << 7)


/* I2Cn address register */
#define MPC5xxx_I2C_ADDR(x)	(((x) & 0x7FU) << 25)

/* I2Cn frequency divider register possible values */
#define MPC5xxx_I2C_100HZ	0x1F	/* 100HZ Clock */

/* I2Cn control register bits */
#define MPC5xxx_I2C_EN		0x80	/* Enable I2Cn module */
#define MPC5xxx_I2C_IEN		0x40	/* Enable interrupt from I2Cn module */
#define MPC5xxx_I2C_STA		0x20	/* Master/Slave mode select */
#define MPC5xxx_I2C_TX		0x10	/* Transmit/Receive mode select */
#define MPC5xxx_I2C_TXAK	0x08	/* Transmit ack enable */
#define MPC5xxx_I2C_RSTA	0x04	/* Repeat start */
#define MPC5xxx_I2C_INIT_MASK	(MPC5xxx_I2C_EN | MPC5xxx_I2C_STA | MPC5xxx_I2C_TX | MPC5xxx_I2C_RSTA)

/* I2Cn status register bits */
#define MPC5xxx_I2C_CF		0x80	/* Data transferring */
#define MPC5xxx_I2C_AAS		0x40	/* Addressed as slave */
#define MPC5xxx_I2C_BB		0x20	/* Bus busy */
#define MPC5xxx_I2C_AL		0x10	/* Arbitration lost */
#define MPC5xxx_I2C_SRW		0x04	/* Slave Read/Write */
#define MPC5xxx_I2C_IF		0x02	/* I2Cn Interrupt */
#define MPC5xxx_I2C_RXAK	0x01	/* Receive ack */


/* Programmable Serial Controller (PSC) status register bits */
#define MPC5xxx_PSC_SR_CDE	0x0080
#define MPC5xxx_PSC_SR_RXRDY	0x0100
#define MPC5xxx_PSC_SR_RXFULL	0x0200
#define MPC5xxx_PSC_SR_TXRDY	0x0400
#define MPC5xxx_PSC_SR_TXEMP	0x0800
#define MPC5xxx_PSC_SR_OE	0x1000
#define MPC5xxx_PSC_SR_PE	0x2000
#define MPC5xxx_PSC_SR_FE	0x4000
#define MPC5xxx_PSC_SR_RB	0x8000

/* PSC Command values */
#define MPC5xxx_PSC_RX_ENABLE		0x0001
#define MPC5xxx_PSC_RX_DISABLE		0x0002
#define MPC5xxx_PSC_TX_ENABLE		0x0004
#define MPC5xxx_PSC_TX_DISABLE		0x0008
#define MPC5xxx_PSC_SEL_MODE_REG_1	0x0010
#define MPC5xxx_PSC_RST_RX		0x0020
#define MPC5xxx_PSC_RST_TX		0x0030
#define MPC5xxx_PSC_RST_ERR_STAT	0x0040
#define MPC5xxx_PSC_RST_BRK_CHG_INT	0x0050
#define MPC5xxx_PSC_START_BRK		0x0060
#define MPC5xxx_PSC_STOP_BRK		0x0070

/* PSC FIFO status bits */
#define MPC5xxx_PSC_FIFO_ERR		(1U << 6)
#define MPC5xxx_PSC_FIFO_UF		(1U << 5)
#define MPC5xxx_PSC_FIFO_OF		(1U << 4)
#define MPC5xxx_PSC_FIFO_FR		(1U << 3)
#define MPC5xxx_PSC_FIFO_FULL		(1U << 2)
#define MPC5xxx_PSC_FIFO_ALARM		(1U << 1)
#define MPC5xxx_PSC_FIFO_EMPTY		(1U << 0)

/* Programmable Serial Controller (PSC) interrupt status register bits */
#define MPC5xxx_PSC_ISR_DEOF		(1U << 7)
#define MPC5xxx_PSC_ISR_TXRDY		(1U << 8)
#define MPC5xxx_PSC_ISR_RXRDY		(1U << 9)
#define MPC5xxx_PSC_ISR_DB		(1U << 10)
#define MPC5xxx_PSC_ISR_URERR		(1U << 11)
#define MPC5xxx_PSC_ISR_ORERR		(1U << 12)
#define MPC5xxx_PSC_ISR_IPC		(1U << 15)

/* PSC interrupt mask bits */
#define MPC5xxx_PSC_IMR_TXRDY		MPC5xxx_PSC_ISR_TXRDY
#define MPC5xxx_PSC_IMR_RXRDY		MPC5xxx_PSC_ISR_RXRDY
#define MPC5xxx_PSC_IMR_DB		MPC5xxx_PSC_ISR_DB
#define MPC5xxx_PSC_IMR_URERR		MPC5xxx_PSC_ISR_URERR
#define MPC5xxx_PSC_IMR_ORERR		MPC5xxx_PSC_ISR_ORERR
#define MPC5xxx_PSC_IMR_IPC		MPC5xxx_PSC_ISR_IPC

/* PSC input port change bit */
#define MPC5xxx_PSC_CTS			0x01
#define MPC5xxx_PSC_DCD			0x02
#define MPC5xxx_PSC_D_CTS		0x10
#define MPC5xxx_PSC_D_DCD		0x20

/* PSC mode fields */

/* MR1 */
#define MPC5xxx_PSC_MODE_5_BITS		0x00
#define MPC5xxx_PSC_MODE_6_BITS		0x01
#define MPC5xxx_PSC_MODE_7_BITS		0x02
#define MPC5xxx_PSC_MODE_8_BITS		0x03
#define MPC5xxx_PSC_MODE_PAREVEN	0x00
#define MPC5xxx_PSC_MODE_PARODD		0x04
#define MPC5xxx_PSC_MODE_PARFORCE	0x08
#define MPC5xxx_PSC_MODE_PARNONE	0x10
#define MPC5xxx_PSC_MODE_RESERVED	0x20
#define MPC5xxx_PSC_MODE_FFULL		0x40
#define MPC5xxx_PSC_MODE_RXRTS		0x80

/* MR2 */
#define MPC5xxx_PSC_MODE2_TXCTS			(1U << 4)
#define MPC5xxx_PSC_MODE2_TXRTS			(1U << 5)
#define MPC5xxx_PSC_MODE2_SB(n)			(((n) & 0x0FU) << 0)
#define MPC5xxx_PSC_MODE2_ONE_STOP_5_BITS	MPC5xxx_PSC_MODE2_SB(0)
#define MPC5xxx_PSC_MODE2_ONE_STOP		MPC5xxx_PSC_MODE2_SB(7)
#define MPC5xxx_PSC_MODE2_TWO_STOP		MPC5xxx_PSC_MODE2_SB(15)


#define MPC5xxx_PSC_RFNUM_MASK		0x01ff

/* Memory allocation block size */
#define MPC5xxx_SDRAM_UNIT	0x8000		/* 32K byte */

#define MPC5xxx_CRIT_IRQ_NUM	4
#define MPC5xxx_MAIN_IRQ_NUM	17
#define MPC5xxx_SDMA_IRQ_NUM	17
#define MPC5xxx_PERP_IRQ_NUM	22

#define MPC5xxx_CRIT_IRQ_BASE	0
#define MPC5xxx_MAIN_IRQ_BASE	(MPC5xxx_CRIT_IRQ_BASE + MPC5xxx_CRIT_IRQ_NUM)
#define MPC5xxx_SDMA_IRQ_BASE	(MPC5xxx_MAIN_IRQ_BASE + MPC5xxx_MAIN_IRQ_NUM)
#define MPC5xxx_PERP_IRQ_BASE	(MPC5xxx_SDMA_IRQ_BASE + MPC5xxx_SDMA_IRQ_NUM)

#define MPC5xxx_IRQ0			(MPC5xxx_CRIT_IRQ_BASE + 0)
#define MPC5xxx_SLICE_TIMER_0_IRQ	(MPC5xxx_CRIT_IRQ_BASE + 1)
#define MPC5xxx_HI_INT_IRQ		(MPC5xxx_CRIT_IRQ_BASE + 2)
#define MPC5xxx_CCS_IRQ			(MPC5xxx_CRIT_IRQ_BASE + 3)

#define MPC5xxx_IRQ1			(MPC5xxx_MAIN_IRQ_BASE + 1)
#define MPC5xxx_IRQ2			(MPC5xxx_MAIN_IRQ_BASE + 2)
#define MPC5xxx_IRQ3			(MPC5xxx_MAIN_IRQ_BASE + 3)

#define MPC5xxx_SDMA_IRQ		(MPC5xxx_PERP_IRQ_BASE + 0)
#define MPC5xxx_PSC1_IRQ		(MPC5xxx_PERP_IRQ_BASE + 1)
#define MPC5xxx_PSC2_IRQ		(MPC5xxx_PERP_IRQ_BASE + 2)
#define MPC5xxx_PSC3_IRQ		(MPC5xxx_PERP_IRQ_BASE + 3)
#define MPC5xxx_PSC6_IRQ		(MPC5xxx_PERP_IRQ_BASE + 4)
#define MPC5xxx_IRDA_IRQ		(MPC5xxx_PSC6_IRQ)
#define MPC5xxx_FEC_IRQ			(MPC5xxx_PERP_IRQ_BASE + 5)
#define MPC5xxx_USB_IRQ			(MPC5xxx_PERP_IRQ_BASE + 6)
#define MPC5xxx_ATA_IRQ			(MPC5xxx_PERP_IRQ_BASE + 7)
#define MPC5xxx_PCI_CNTRL_IRQ		(MPC5xxx_PERP_IRQ_BASE + 8)
#define MPC5xxx_PCI_SCIRX_IRQ		(MPC5xxx_PERP_IRQ_BASE + 9)
#define MPC5xxx_PCI_SCITX_IRQ		(MPC5xxx_PERP_IRQ_BASE + 10)
#define MPC5xxx_PSC4_IRQ		(MPC5xxx_PERP_IRQ_BASE + 11)
#define MPC5xxx_PSC5_IRQ		(MPC5xxx_PERP_IRQ_BASE + 12)
#define MPC5xxx_SPI_MODF_IRQ		(MPC5xxx_PERP_IRQ_BASE + 13)
#define MPC5xxx_SPI_SPIF_IRQ		(MPC5xxx_PERP_IRQ_BASE + 14)
#define MPC5xxx_I2C1_IRQ		(MPC5xxx_PERP_IRQ_BASE + 15)
#define MPC5xxx_I2C2_IRQ		(MPC5xxx_PERP_IRQ_BASE + 16)
#define MPC5xxx_CAN1_IRQ		(MPC5xxx_PERP_IRQ_BASE + 17)
#define MPC5xxx_CAN2_IRQ		(MPC5xxx_PERP_IRQ_BASE + 18)
#define MPC5xxx_IR_RX_IRQ		(MPC5xxx_PERP_IRQ_BASE + 19)
#define MPC5xxx_IR_TX_IRQ		(MPC5xxx_PERP_IRQ_BASE + 20)
#define MPC5xxx_XLB_ARB_IRQ		(MPC5xxx_PERP_IRQ_BASE + 21)

#define PCI_WINDOW_TRANSLATION(proc_start, proc_end, pci_start, pci_end) \
	((((proc_start >> 24) & 0xff) << 24) | \
	((((proc_end - proc_start) >> 24) & 0xff) << 16) | \
	(((pci_start >> 24) & 0xff) << 8))

#define PCI_WINDOW_CONTROL(win0, win1, win2)	(((win0) << 24) | ((win1) << 16) | ((win2) << 8))

#ifndef __ASSEMBLY__

#define MPC5xxx_PCI_FIFO_SIZE		(512U)

struct mpc5xxx_pci {
	volatile uint32_t	idr;		/* PCI + 0x00 */
	volatile uint32_t	scr;		/* PCI + 0x04 */
	volatile uint32_t	ccrir;		/* PCI + 0x08 */
	volatile uint32_t	cr1;		/* PCI + 0x0C */
	volatile uint32_t	bar0;		/* PCI + 0x10 */
	volatile uint32_t	bar1;		/* PCI + 0x14 */
	volatile uint8_t	reserved1[16];	/* PCI + 0x18 */
	volatile uint32_t	ccpr;		/* PCI + 0x28 */
	volatile uint32_t	sid;		/* PCI + 0x2C */
	volatile uint32_t	erbar;		/* PCI + 0x30 */
	volatile uint32_t	cpr;		/* PCI + 0x34 */
	volatile uint8_t	reserved2[4];	/* PCI + 0x38 */
	volatile uint32_t	cr2;		/* PCI + 0x3C */
	volatile uint8_t	reserved3[32];	/* PCI + 0x40 */
	volatile uint32_t	gscr;		/* PCI + 0x60 */
	volatile uint32_t	tbatr0;		/* PCI + 0x64 */
	volatile uint32_t	tbatr1;		/* PCI + 0x68 */
	volatile uint32_t	tcr;		/* PCI + 0x6C */
	volatile uint32_t	iw0btar;	/* PCI + 0x70 */
	volatile uint32_t	iw1btar;	/* PCI + 0x74 */
	volatile uint32_t	iw2btar;	/* PCI + 0x78 */
	volatile uint8_t	reserved4[4];	/* PCI + 0x7C */
	volatile uint32_t	iwcr;		/* PCI + 0x80 */
	volatile uint32_t	icr;		/* PCI + 0x84 */
	volatile uint32_t	isr;		/* PCI + 0x88 */
	volatile uint32_t	arb;		/* PCI + 0x8C */
	volatile uint8_t	reserved5[104];	/* PCI + 0x90 */
	volatile uint32_t	car;		/* PCI + 0xF8 */
	volatile uint8_t	reserved6[4];	/* PCI + 0xFC */
};


struct mpc5xxx_pci_mcti {
	volatile uint16_t	pcitpsr;	/* PCI_MCTI + 0x00 */
	volatile uint16_t	reservedtpsr;	/* PCI_MCTI + 0x02 */
	volatile uint32_t	pcitsar;	/* PCI_MCTI + 0x04 */
	volatile uint32_t	pcittcr;	/* PCI_MCTI + 0x08 */
	volatile uint16_t	pciter;		/* PCI_MCTI + 0x0C */
	volatile uint16_t	reservedter;	/* PCI_MCTI + 0x0E */
	volatile uint32_t	pcitnar;	/* PCI_MCTI + 0x10 */
	volatile uint32_t	pcitlwr;	/* PCI_MCTI + 0x14 */
	volatile uint16_t	pcitdcr_bd;	/* PCI_MCTI + 0x18 */
	volatile uint16_t	pcitdcr_pd;	/* PCI_MCTI + 0x1A */
	volatile uint16_t	pcitsr;		/* PCI_MCTI + 0x1C */
	volatile uint16_t	reservedtsr;	/* PCI_MCTI + 0x1A */
	volatile uint32_t	reservedt[8];	/* PCI_MCTI + 0x20 */
	volatile uint32_t	pcitfdr;	/* PCI_MCTI + 0x40 */
	volatile uint16_t	pcitfsr;	/* PCI_MCTI + 0x44 */
	volatile uint16_t	reservedtfsr;	/* PCI_MCTI + 0x46 */
	volatile uint16_t	pcitfcr;	/* PCI_MCTI + 0x48 */
	volatile uint16_t	reservedtfcr;	/* PCI_MCTI + 0x4A */
	volatile uint32_t	pcitfar;	/* PCI_MCTI + 0x4C */
	volatile uint32_t	pcitfrpr;	/* PCI_MCTI + 0x50 */
	volatile uint32_t	pcitfwpr;	/* PCI_MCTI + 0x54 */

	volatile uint32_t	reserved[10];	/* PCI_MCTI + 0x58 */

	volatile uint16_t	pcirpsr;	/* PCI_MCTI + 0x80 */
	volatile uint16_t	reservedrpsr;	/* PCI_MCTI + 0x82 */
	volatile uint32_t	pcirsar;	/* PCI_MCTI + 0x84 */
	volatile uint32_t	pcirtcr;	/* PCI_MCTI + 0x88 */
	volatile uint16_t	pcirer;		/* PCI_MCTI + 0x8C */
	volatile uint16_t	reservedrer;	/* PCI_MCTI + 0x8E */
	volatile uint32_t	pcirnar;	/* PCI_MCTI + 0x90 */
	volatile uint32_t	pcirlwr;	/* PCI_MCTI + 0x94 */
	volatile uint16_t	pcirdcr_bd;	/* PCI_MCTI + 0x98 */
	volatile uint16_t	pcirdcr_pd;	/* PCI_MCTI + 0x9A */
	volatile uint16_t	pcirsr;		/* PCI_MCTI + 0x9C */
	volatile uint16_t	reservedrsr;	/* PCI_MCTI + 0x9C */
	volatile uint32_t	reservedr[8];	/* PCI_MCTI + 0xA0 */
	volatile uint32_t	pcirfdr;	/* PCI_MCTI + 0xC0 */
	volatile uint16_t	pcirfsr;	/* PCI_MCTI + 0xC4 */
	volatile uint16_t	reservedrfsr;	/* PCI_MCTI + 0xC6 */
	volatile uint16_t	pcirfcr;	/* PCI_MCTI + 0xC8 */
	volatile uint16_t	reservedrfcr;	/* PCI_MCTI + 0xCA */
	volatile uint32_t	pcirfar;	/* PCI_MCTI + 0xCC */
	volatile uint32_t	pcirfrpr;	/* PCI_MCTI + 0xD0 */
	volatile uint32_t	pcirfwpr;	/* PCI_MCTI + 0xD4 */
};

struct mpc5xxx_psc {
	volatile uint8_t	mode;		/* PSC + 0x00 */
	volatile uint8_t	reserved0[3];
	union {					/* PSC + 0x04 */
		volatile uint16_t	status;
		volatile uint16_t	clock_select;
	} sr_csr;
#define mpc5xxx_psc_status	sr_csr.status
#define mpc5xxx_psc_clock_select sr_csr.clock_select
	volatile uint16_t	reserved1;
	volatile uint8_t	command;	/* PSC + 0x08 */
	volatile uint8_t	reserved2[3];
	union {					/* PSC + 0x0c */
		volatile uint8_t	buffer_8;
		volatile uint16_t	buffer_16;
		volatile uint32_t	buffer_32;
	} buffer;
#define mpc5xxx_psc_buffer_8	buffer.buffer_8
#define mpc5xxx_psc_buffer_16	buffer.buffer_16
#define mpc5xxx_psc_buffer_32	buffer.buffer_32
	union {					/* PSC + 0x10 */
		volatile uint8_t	ipcr;
		volatile uint8_t	acr;
	} ipcr_acr;
#define mpc5xxx_psc_ipcr		ipcr_acr.ipcr
#define mpc5xxx_psc_acr		ipcr_acr.acr
	volatile uint8_t	reserved3[3];
	union {					/* PSC + 0x14 */
		volatile uint16_t	isr;
		volatile uint16_t	imr;
	} isr_imr;
#define mpc5xxx_psc_isr		isr_imr.isr
#define mpc5xxx_psc_imr		isr_imr.imr
	volatile uint16_t	reserved4;
	volatile uint8_t	ctur;		/* PSC + 0x18 */
	volatile uint8_t	reserved5[3];
	volatile uint8_t	ctlr;		/* PSC + 0x1c */
	volatile uint8_t	reserved6[3];
	volatile uint16_t	ccr;		/* PSC + 0x20 */
	volatile uint8_t	reserved7[14];
	volatile uint8_t	ivr;		/* PSC + 0x30 */
	volatile uint8_t	reserved8[3];
	volatile uint8_t	ip;		/* PSC + 0x34 */
	volatile uint8_t	reserved9[3];
	volatile uint8_t	op1;		/* PSC + 0x38 */
	volatile uint8_t	reserved10[3];
	volatile uint8_t	op0;		/* PSC + 0x3c */
	volatile uint8_t	reserved11[3];
	volatile uint32_t	sicr;		/* PSC + 0x40 */
	volatile uint8_t	ircr1;		/* PSC + 0x44 */
	volatile uint8_t	reserved13[3];
	volatile uint8_t	ircr2;		/* PSC + 0x44 */
	volatile uint8_t	reserved14[3];
	volatile uint8_t	irsdr;		/* PSC + 0x4c */
	volatile uint8_t	reserved15[3];
	volatile uint8_t	irmdr;		/* PSC + 0x50 */
	volatile uint8_t	reserved16[3];
	volatile uint8_t	irfdr;		/* PSC + 0x54 */
	volatile uint8_t	reserved17[3];
	volatile uint16_t	rfnum;		/* PSC + 0x58 */
	volatile uint16_t	reserved18;
	volatile uint16_t	tfnum;		/* PSC + 0x5c */
	volatile uint16_t	reserved19;
	volatile uint32_t	rfdata;		/* PSC + 0x60 */
	volatile uint16_t	rfstat;		/* PSC + 0x64 */
	volatile uint16_t	reserved20;
	volatile uint8_t	rfcntl;		/* PSC + 0x68 */
	volatile uint8_t	reserved21[5];
	volatile uint16_t	rfalarm;	/* PSC + 0x6e */
	volatile uint16_t	reserved22;
	volatile uint16_t	rfrptr;		/* PSC + 0x72 */
	volatile uint16_t	reserved23;
	volatile uint16_t	rfwptr;		/* PSC + 0x76 */
	volatile uint16_t	reserved24;
	volatile uint16_t	rflrfptr;	/* PSC + 0x7a */
	volatile uint16_t	reserved25;
	volatile uint16_t	rflwfptr;	/* PSC + 0x7e */
	volatile uint32_t	tfdata;		/* PSC + 0x80 */
	volatile uint16_t	tfstat;		/* PSC + 0x84 */
	volatile uint16_t	reserved26;
	volatile uint8_t	tfcntl;		/* PSC + 0x88 */
	volatile uint8_t	reserved27[5];
	volatile uint16_t	tfalarm;	/* PSC + 0x8e */
	volatile uint16_t	reserved28;
	volatile uint16_t	tfrptr;		/* PSC + 0x92 */
	volatile uint16_t	reserved29;
	volatile uint16_t	tfwptr;		/* PSC + 0x96 */
	volatile uint16_t	reserved30;
	volatile uint16_t	tflrfptr;	/* PSC + 0x9a */
	volatile uint16_t	reserved31;
	volatile uint16_t	tflwfptr;	/* PSC + 0x9e */
};

#define PSC_ADDRESS(num)	((struct mpc5xxx_psc *)(MPC5xxx_PSC1 + (((num) - 1) * 0x200)))

struct mpc5xxx_spi {
	volatile uint8_t	control1;	/* SPI + 0x00 */
	volatile uint8_t	control2;	/* SPI + 0x01 */
	volatile uint8_t	reserved0[2];	/* SPI + 0x02 */
	volatile uint8_t	baud;		/* SPI + 0x04 */
	volatile uint8_t	status;		/* SPI + 0x05 */
	volatile uint8_t	reserved1[3];	/* SPI + 0x06 */
	volatile uint8_t	data;		/* SPI + 0x09 */
	volatile uint8_t	reserved2[3];	/* SPI + 0x0a */
	volatile uint8_t	port;		/* SPI + 0x0d */
	volatile uint8_t	reserved3[2];	/* SPI + 0x0e */
	volatile uint8_t	dir;		/* SPI + 0x10 */
};

struct mpc5xxx_intr {
	volatile uint32_t	per_mask;	/* INTR + 0x00 */
	volatile uint32_t	per_pri1;	/* INTR + 0x04 */
	volatile uint32_t	per_pri2;	/* INTR + 0x08 */
	volatile uint32_t	per_pri3;	/* INTR + 0x0c */
	volatile uint32_t	ctrl;		/* INTR + 0x10 */
	volatile uint32_t	main_mask;	/* INTR + 0x14 */
	volatile uint32_t	main_pri1;	/* INTR + 0x18 */
	volatile uint32_t	main_pri2;	/* INTR + 0x1c */
	volatile uint32_t	reserved1;	/* INTR + 0x20 */
	volatile uint32_t	enc_status;	/* INTR + 0x24 */
	volatile uint32_t	crit_status;	/* INTR + 0x28 */
	volatile uint32_t	main_status;	/* INTR + 0x2c */
	volatile uint32_t	per_status;	/* INTR + 0x30 */
	volatile uint32_t	reserved2;	/* INTR + 0x34 */
	volatile uint32_t	per_error;	/* INTR + 0x38 */
};

struct mpc5xxx_gpio {
	volatile uint32_t	port_config;	/* GPIO + 0x00 */
	volatile uint32_t	simple_gpioe;	/* GPIO + 0x04 */
	volatile uint32_t	simple_ode;	/* GPIO + 0x08 */
	volatile uint32_t	simple_ddr;	/* GPIO + 0x0c */
	volatile uint32_t	simple_dvo;	/* GPIO + 0x10 */
	volatile uint32_t	simple_ival;	/* GPIO + 0x14 */
	volatile uint8_t	outo_gpioe;	/* GPIO + 0x18 */
	volatile uint8_t	reserved1[3];	/* GPIO + 0x19 */
	volatile uint8_t	outo_dvo;	/* GPIO + 0x1c */
	volatile uint8_t	reserved2[3];	/* GPIO + 0x1d */
	volatile uint8_t	sint_gpioe;	/* GPIO + 0x20 */
	volatile uint8_t	reserved3[3];	/* GPIO + 0x21 */
	volatile uint8_t	sint_ode;	/* GPIO + 0x24 */
	volatile uint8_t	reserved4[3];	/* GPIO + 0x25 */
	volatile uint8_t	sint_ddr;	/* GPIO + 0x28 */
	volatile uint8_t	reserved5[3];	/* GPIO + 0x29 */
	volatile uint8_t	sint_dvo;	/* GPIO + 0x2c */
	volatile uint8_t	reserved6[3];	/* GPIO + 0x2d */
	volatile uint8_t	sint_inten;	/* GPIO + 0x30 */
	volatile uint8_t	reserved7[3];	/* GPIO + 0x31 */
	volatile uint16_t	sint_itype;	/* GPIO + 0x34 */
	volatile uint16_t	reserved8;	/* GPIO + 0x36 */
	volatile uint8_t	gpio_control;	/* GPIO + 0x38 */
	volatile uint8_t	reserved9[3];	/* GPIO + 0x39 */
	volatile uint8_t	sint_istat;	/* GPIO + 0x3c */
	volatile uint8_t	sint_ival;	/* GPIO + 0x3d */
	volatile uint8_t	bus_errs;	/* GPIO + 0x3e */
	volatile uint8_t	reserved10;	/* GPIO + 0x3f */
};

struct mpc5xxx_wu_gpio {
	volatile u8 wu_gpioe;		/* WU_GPIO + 0x00 */
	volatile u8 reserved1[3];	/* WU_GPIO + 0x01 */
	volatile u8 wu_ode;		/* WU_GPIO + 0x04 */
	volatile u8 reserved2[3];	/* WU_GPIO + 0x05 */
	volatile u8 wu_ddr;		/* WU_GPIO + 0x08 */
	volatile u8 reserved3[3];	/* WU_GPIO + 0x09 */
	volatile u8 wu_dvo;		/* WU_GPIO + 0x0c */
	volatile u8 reserved4[3];	/* WU_GPIO + 0x0d */
	volatile u8 wu_inten;		/* WU_GPIO + 0x10 */
	volatile u8 reserved5[3];	/* WU_GPIO + 0x11 */
	volatile u8 wu_individual;	/* WU_GPIO + 0x14 */
	volatile u8 reserved6[3];	/* WU_GPIO + 0x15 */
	volatile u16 wu_itype;	    	/* WU_GPIO + 0x18 */
	volatile u16 reserved7;		/* WU_GPIO + 0x1A */
	volatile u8 gpio_control;	/* WU_GPIO + 0x1C */
	volatile u8 reserved8[3];	/* WU_GPIO + 0x1D */
	volatile u8 wu_ival;		/* WU_GPIO + 0x20 */
	volatile u8 reserved9[3];	/* WU_GPIO + 0x21 */
	volatile u8 wu_istat;		/* WU_GPIO + 0x24 */
	volatile u8 reserved10[3];	/* WU_GPIO + 0x25 */
};

struct mpc5xxx_gpt {
	volatile uint32_t	emsr;		/* GPT + 0x00 */
	volatile uint32_t	cir;		/* GPT + 0x04 */
	volatile uint32_t	pwmcr;		/* GPT + 0x08 */
	volatile uint32_t	sr;		/* GPT + 0x0c */
};


struct mpc5xxx_sdma {
	volatile uint32_t	taskBar;	/* SDMA + 0x00 */
	volatile uint32_t	currentPointer;	/* SDMA + 0x04 */
	volatile uint32_t	endPointer;	/* SDMA + 0x08 */
	volatile uint32_t	variablePointer;/* SDMA + 0x0c */

	volatile uint8_t	IntVect1;	/* SDMA + 0x10 */
	volatile uint8_t	IntVect2;	/* SDMA + 0x11 */
	volatile uint16_t	PtdCntrl;	/* SDMA + 0x12 */

	volatile uint32_t	IntPend;	/* SDMA + 0x14 */
	volatile uint32_t	IntMask;	/* SDMA + 0x18 */

#if 0
	volatile uint16_t	tcr_0;		/* SDMA + 0x1c */
	volatile uint16_t	tcr_1;		/* SDMA + 0x1e */
	volatile uint16_t	tcr_2;		/* SDMA + 0x20 */
	volatile uint16_t	tcr_3;		/* SDMA + 0x22 */
	volatile uint16_t	tcr_4;		/* SDMA + 0x24 */
	volatile uint16_t	tcr_5;		/* SDMA + 0x26 */
	volatile uint16_t	tcr_6;		/* SDMA + 0x28 */
	volatile uint16_t	tcr_7;		/* SDMA + 0x2a */
	volatile uint16_t	tcr_8;		/* SDMA + 0x2c */
	volatile uint16_t	tcr_9;		/* SDMA + 0x2e */
	volatile uint16_t	tcr_a;		/* SDMA + 0x30 */
	volatile uint16_t	tcr_b;		/* SDMA + 0x32 */
	volatile uint16_t	tcr_c;		/* SDMA + 0x34 */
	volatile uint16_t	tcr_d;		/* SDMA + 0x36 */
	volatile uint16_t	tcr_e;		/* SDMA + 0x38 */
	volatile uint16_t	tcr_f;		/* SDMA + 0x3a */
#else
	volatile uint16_t	tcr[MPC5xxx_SDMA_MAX_TASKS]; /* SDMA + 0x1c..0x3a */
#endif

#if 0
	volatile uint8_t	IPR0;		/* SDMA + 0x3c */
	volatile uint8_t	IPR1;		/* SDMA + 0x3d */
	volatile uint8_t	IPR2;		/* SDMA + 0x3e */
	volatile uint8_t	IPR3;		/* SDMA + 0x3f */
	volatile uint8_t	IPR4;		/* SDMA + 0x40 */
	volatile uint8_t	IPR5;		/* SDMA + 0x41 */
	volatile uint8_t	IPR6;		/* SDMA + 0x42 */
	volatile uint8_t	IPR7;		/* SDMA + 0x43 */
	volatile uint8_t	IPR8;		/* SDMA + 0x44 */
	volatile uint8_t	IPR9;		/* SDMA + 0x45 */
	volatile uint8_t	IPR10;		/* SDMA + 0x46 */
	volatile uint8_t	IPR11;		/* SDMA + 0x47 */
	volatile uint8_t	IPR12;		/* SDMA + 0x48 */
	volatile uint8_t	IPR13;		/* SDMA + 0x49 */
	volatile uint8_t	IPR14;		/* SDMA + 0x4a */
	volatile uint8_t	IPR15;		/* SDMA + 0x4b */
	volatile uint8_t	IPR16;		/* SDMA + 0x4c */
	volatile uint8_t	IPR17;		/* SDMA + 0x4d */
	volatile uint8_t	IPR18;		/* SDMA + 0x4e */
	volatile uint8_t	IPR19;		/* SDMA + 0x4f */
	volatile uint8_t	IPR20;		/* SDMA + 0x50 */
	volatile uint8_t	IPR21;		/* SDMA + 0x51 */
	volatile uint8_t	IPR22;		/* SDMA + 0x52 */
	volatile uint8_t	IPR23;		/* SDMA + 0x53 */
	volatile uint8_t	IPR24;		/* SDMA + 0x54 */
	volatile uint8_t	IPR25;		/* SDMA + 0x55 */
	volatile uint8_t	IPR26;		/* SDMA + 0x56 */
	volatile uint8_t	IPR27;		/* SDMA + 0x57 */
	volatile uint8_t	IPR28;		/* SDMA + 0x58 */
	volatile uint8_t	IPR29;		/* SDMA + 0x59 */
	volatile uint8_t	IPR30;		/* SDMA + 0x5a */
	volatile uint8_t	IPR31;		/* SDMA + 0x5b */
#else
	volatile uint8_t	IPR[MPC5xxx_SDMA_MAX_INITIATORS]; /* SDMA + 0x3c..0x5b */
#endif

	volatile uint32_t	res1;		/* SDMA + 0x5c */
#ifdef CONFIG_MPC5100
	volatile uint32_t	res2;		/* SDMA + 0x60 */
	volatile uint32_t	res3;		/* SDMA + 0x64 */
#else
	volatile uint32_t	task_size0;	/* SDMA + 0x60 */
	volatile uint32_t	task_size1;	/* SDMA + 0x64 */
#endif
	volatile uint32_t	MDEDebug;	/* SDMA + 0x68 */
	volatile uint32_t	ADSDebug;	/* SDMA + 0x6c */
	volatile uint32_t	Value1;		/* SDMA + 0x70 */
	volatile uint32_t	Value2;		/* SDMA + 0x74 */
	volatile uint32_t	Control;	/* SDMA + 0x78 */
	volatile uint32_t	Status;		/* SDMA + 0x7c */
	volatile uint32_t	EU00;		/* SDMA + 0x80 */
	volatile uint32_t	EU01;		/* SDMA + 0x84 */
	volatile uint32_t	EU02;		/* SDMA + 0x88 */
	volatile uint32_t	EU03;		/* SDMA + 0x8c */
	volatile uint32_t	EU04;		/* SDMA + 0x90 */
	volatile uint32_t	EU05;		/* SDMA + 0x94 */
	volatile uint32_t	EU06;		/* SDMA + 0x98 */
	volatile uint32_t	EU07;		/* SDMA + 0x9c */
	volatile uint32_t	EU10;		/* SDMA + 0xa0 */
	volatile uint32_t	EU11;		/* SDMA + 0xa4 */
	volatile uint32_t	EU12;		/* SDMA + 0xa8 */
	volatile uint32_t	EU13;		/* SDMA + 0xac */
	volatile uint32_t	EU14;		/* SDMA + 0xb0 */
	volatile uint32_t	EU15;		/* SDMA + 0xb4 */
	volatile uint32_t	EUINT16_T;		/* SDMA + 0xb8 */
	volatile uint32_t	EU17;		/* SDMA + 0xbc */
	volatile uint32_t	EU20;		/* SDMA + 0xc0 */
	volatile uint32_t	EU21;		/* SDMA + 0xc4 */
	volatile uint32_t	EU22;		/* SDMA + 0xc8 */
	volatile uint32_t	EU23;		/* SDMA + 0xcc */
	volatile uint32_t	EU24;		/* SDMA + 0xd0 */
	volatile uint32_t	EU25;		/* SDMA + 0xd4 */
	volatile uint32_t	EU26;		/* SDMA + 0xd8 */
	volatile uint32_t	EU27;		/* SDMA + 0xdc */
	volatile uint32_t	EU30;		/* SDMA + 0xe0 */
	volatile uint32_t	EU31;		/* SDMA + 0xe4 */
	volatile uint32_t	EUINT32_T;		/* SDMA + 0xe8 */
	volatile uint32_t	EU33;		/* SDMA + 0xec */
	volatile uint32_t	EU34;		/* SDMA + 0xf0 */
	volatile uint32_t	EU35;		/* SDMA + 0xf4 */
	volatile uint32_t	EU36;		/* SDMA + 0xf8 */
	volatile uint32_t	EU37;		/* SDMA + 0xfc */
};

struct mscan_buffer {
	volatile uint8_t  idr[0x8];	/* 0x00 */
	volatile uint8_t  dsr[0x10];	/* 0x08 */
	volatile uint8_t  dlr;		/* 0x18 */
	volatile uint8_t  tbpr;		/* 0x19 */	/* This register is not applicable for receive buffers */
	volatile uint16_t rsrv1;	/* 0x1A */
	volatile uint8_t  tsrh;		/* 0x1C */
	volatile uint8_t  tsrl;		/* 0x1D */
	volatile uint16_t rsrv2;	/* 0x1E */
};

struct mpc5xxx_mscan {
	volatile uint8_t  canctl0;	/* MSCAN + 0x00 */
	volatile uint8_t  canctl1;	/* MSCAN + 0x01 */
	volatile uint16_t rsrv1;	/* MSCAN + 0x02 */
	volatile uint8_t  canbtr0;	/* MSCAN + 0x04 */
	volatile uint8_t  canbtr1;	/* MSCAN + 0x05 */
	volatile uint16_t rsrv2;	/* MSCAN + 0x06 */
	volatile uint8_t  canrflg;	/* MSCAN + 0x08 */
	volatile uint8_t  canrier;	/* MSCAN + 0x09 */
	volatile uint16_t rsrv3;	/* MSCAN + 0x0A */
	volatile uint8_t  cantflg;	/* MSCAN + 0x0C */
	volatile uint8_t  cantier;	/* MSCAN + 0x0D */
	volatile uint16_t rsrv4;	/* MSCAN + 0x0E */
	volatile uint8_t  cantarq;	/* MSCAN + 0x10 */
	volatile uint8_t  cantaak;	/* MSCAN + 0x11 */
	volatile uint16_t rsrv5;	/* MSCAN + 0x12 */
	volatile uint8_t  cantbsel;	/* MSCAN + 0x14 */
	volatile uint8_t  canidac;	/* MSCAN + 0x15 */
	volatile uint16_t rsrv6[3];	/* MSCAN + 0x16 */
	volatile uint8_t  canrxerr;	/* MSCAN + 0x1C */
	volatile uint8_t  cantxerr;	/* MSCAN + 0x1D */
	volatile uint16_t rsrv7;	/* MSCAN + 0x1E */
	volatile uint8_t  canidar0;	/* MSCAN + 0x20 */
	volatile uint8_t  canidar1;	/* MSCAN + 0x21 */
	volatile uint16_t rsrv8;	/* MSCAN + 0x22 */
	volatile uint8_t  canidar2;	/* MSCAN + 0x24 */
	volatile uint8_t  canidar3;	/* MSCAN + 0x25 */
	volatile uint16_t rsrv9;	/* MSCAN + 0x26 */
	volatile uint8_t  canidmr0;	/* MSCAN + 0x28 */
	volatile uint8_t  canidmr1;	/* MSCAN + 0x29 */
	volatile uint16_t rsrv10;	/* MSCAN + 0x2A */
	volatile uint8_t  canidmr2;	/* MSCAN + 0x2C */
	volatile uint8_t  canidmr3;	/* MSCAN + 0x2D */
	volatile uint16_t rsrv11;	/* MSCAN + 0x2E */
	volatile uint8_t  canidar4;	/* MSCAN + 0x30 */
	volatile uint8_t  canidar5;	/* MSCAN + 0x31 */
	volatile uint16_t rsrv12;	/* MSCAN + 0x32 */
	volatile uint8_t  canidar6;	/* MSCAN + 0x34 */
	volatile uint8_t  canidar7;	/* MSCAN + 0x35 */
	volatile uint16_t rsrv13;	/* MSCAN + 0x36 */
	volatile uint8_t  canidmr4;	/* MSCAN + 0x38 */
	volatile uint8_t  canidmr5;	/* MSCAN + 0x39 */
	volatile uint16_t rsrv14;	/* MSCAN + 0x3A */
	volatile uint8_t  canidmr6;	/* MSCAN + 0x3C */
	volatile uint8_t  canidmr7;	/* MSCAN + 0x3D */
	volatile uint16_t rsrv15;	/* MSCAN + 0x3E */

	struct mscan_buffer canrxfg;	/* MSCAN + 0x40 */    /* Foreground receive buffer */
	struct mscan_buffer cantxfg;	/* MSCAN + 0x60 */    /* Foreground transmit buffer */
};

struct mpc5xxx_i2c {
	volatile uint32_t madr;		/* I2Cn + 0x00 */
	volatile uint32_t mfdr;		/* I2Cn + 0x04 */
	volatile uint32_t mcr;		/* I2Cn + 0x08 */
	volatile uint32_t msr;		/* I2Cn + 0x0C */
	volatile uint32_t mdr;		/* I2Cn + 0x10 */
};

struct mpc5xxx_xlb {
	volatile uint8_t  reserved[0x40];
	volatile uint32_t config;		/* XLB + 0x40 */
	volatile uint32_t version;		/* XLB + 0x44 */
	volatile uint32_t status;		/* XLB + 0x48 */
	volatile uint32_t int_enable;		/* XLB + 0x4c */
	volatile uint32_t addr_capture;		/* XLB + 0x50 */
	volatile uint32_t bus_sig_capture;	/* XLB + 0x54 */
	volatile uint32_t addr_timeout;		/* XLB + 0x58 */
	volatile uint32_t data_timeout;		/* XLB + 0x5c */
	volatile uint32_t bus_act_timeout;	/* XLB + 0x60 */
	volatile uint32_t master_pri_enable;	/* XLB + 0x64 */
	volatile uint32_t master_priority;	/* XLB + 0x68 */
	volatile uint32_t base_address;		/* XLB + 0x6c */
	volatile uint32_t snoop_window;		/* XLB + 0x70 */
};

struct mpc5xxx_cdm {
	volatile uint32_t jtag_id;		/* MBAR_CDM + 0x00  reg0 read only */
	volatile uint32_t rstcfg;		/* MBAR_CDM + 0x04  reg1 read only */
	volatile uint32_t breadcrumb;		/* MBAR_CDM + 0x08  reg2 */

	volatile uint8_t  ddr_mode;		/* MBAR_CDM + 0x0c  reg3 byte0 read only */
	volatile uint8_t  xlb_clk_sel;		/* MBAR_CDM + 0x0d  reg3 byte1 read only */
	volatile uint8_t  ipb_clk_sel;		/* MBAR_CDM + 0x0e  reg3 byte2 */
	volatile uint8_t  pci_clk_sel;		/* MBAR_CDM + 0x0f  reg3 byte3 */

	volatile uint8_t  ext_48mhz_en;		/* MBAR_CDM + 0x10  reg4 byte0 */
	volatile uint8_t  fd_enable;		/* MBAR_CDM + 0x11  reg4 byte1 */
	volatile uint16_t fd_counters;		/* MBAR_CDM + 0x12  reg4 byte2,3 */

	volatile uint32_t clk_enables;		/* MBAR_CDM + 0x14  reg5 */

	volatile uint8_t  osc_disable;		/* MBAR_CDM + 0x18  reg6 byte0 */
	volatile uint8_t  reserved0[3];		/* MBAR_CDM + 0x19  reg6 byte1,2,3 */

	volatile uint8_t  ccs_sleep_enable;	/* MBAR_CDM + 0x1c  reg7 byte0 */
	volatile uint8_t  osc_sleep_enable;	/* MBAR_CDM + 0x1d  reg7 byte1 */
	volatile uint8_t  reserved1;		/* MBAR_CDM + 0x1e  reg7 byte2 */
	volatile uint8_t  ccs_qreq_test;	/* MBAR_CDM + 0x1f  reg7 byte3 */

	volatile uint8_t  soft_reset;		/* MBAR_CDM + 0x20  uint8_t byte0 */
	volatile uint8_t  no_ckstp;		/* MBAR_CDM + 0x21  uint8_t byte0 */
	volatile uint8_t  reserved2[2];		/* MBAR_CDM + 0x22  uint8_t byte1,2,3 */

	volatile uint8_t  pll_lock;		/* MBAR_CDM + 0x24  reg9 byte0 */
	volatile uint8_t  pll_looselock;	/* MBAR_CDM + 0x25  reg9 byte1 */
	volatile uint8_t  pll_sm_lockwin;	/* MBAR_CDM + 0x26  reg9 byte2 */
	volatile uint8_t  reserved3;		/* MBAR_CDM + 0x27  reg9 byte3 */

	volatile uint16_t reserved4;		/* MBAR_CDM + 0x28  reg10 byte0,1 */
	volatile uint16_t mclken_div_psc1;	/* MBAR_CDM + 0x2a  reg10 byte2,3 */

	volatile uint16_t reserved5;		/* MBAR_CDM + 0x2c  reg11 byte0,1 */
	volatile uint16_t mclken_div_psc2;	/* MBAR_CDM + 0x2e  reg11 byte2,3 */

	volatile uint16_t reserved6;		/* MBAR_CDM + 0x30  reg12 byte0,1 */
	volatile uint16_t mclken_div_psc3;	/* MBAR_CDM + 0x32  reg12 byte2,3 */

	volatile uint16_t reserved7;		/* MBAR_CDM + 0x34  reg13 byte0,1 */
	volatile uint16_t mclken_div_psc6;	/* MBAR_CDM + 0x36  reg13 byte2,3 */
};

#define MPC5xxx_SDMA_VAR_SIZE		256
#define MPC5xxx_SDMA_VAR_ALIGN_SHIFT	8
#define MPC5xxx_SDMA_FDT_SIZE		128
#define MPC5xxx_SDMA_FDT_ALIGN_SHIFT	8
#define MPC5xxx_SDMA_CSAVE_SIZE		256
#define MPC5xxx_SDMA_CSAVE_ALIGN_SHIFT	8

#define MPC5xxx_SDMA_TASK_ENABLE		0x8000

/* Function to read the current timebase of the 603e core */
static inline uint64_t readTBx(void)
{
	/* "bind" result to the registers r3 & r4 (because of 64bit int,
	   result will use 2 registers). 
	*/
	register uint64_t result asm("r3");
	__asm__("1:   	          \n\t"
		"mftbu %%r3       \n\t"	// get TBU
		"mftb  %%r4       \n\t"	// get TBL
		"mftbu %%r5       \n\t"	// get TBU once again
		"cmpw  %%r5, %%r3 \n\t"	// see if 'old' = 'new'
		"bne 1b		\n\t"
		: : : "r3", "r4", "r5");

        /* The current value of the timebase is now stored r3 & r4 (TBU & TBL).
           Because of assigning result to that registers, their value can be
           returned as a one  64bit uint by just returning result. 
        */
	return result;
}

extern int mpc5xxx_bestcomm_init(void);
extern void mpc5xxx_get_sysclocks(uint32_t *_fsystem, uint32_t *_xlb, uint32_t *_ipb, uint32_t *_pci);


static inline uint16_t
mpc5xxx_sdma_task_status(size_t task)
{
	struct mpc5xxx_sdma *sdma = (struct mpc5xxx_sdma *)MPC5xxx_SDMA;
	return in_be16(sdma->tcr + task);
}


static inline void
mpc5xxx_sdma_enable_task(size_t task)
{
	struct mpc5xxx_sdma *sdma = (struct mpc5xxx_sdma *)MPC5xxx_SDMA;
	uint16_t val16 = in_be16(sdma->tcr + task);
	val16 |= MPC5xxx_SDMA_TASK_ENABLE;
	out_be16(sdma->tcr + task, val16);
}


static inline void
mpc5xxx_sdma_disable_task(size_t task)
{
	struct mpc5xxx_sdma *sdma = (struct mpc5xxx_sdma *)MPC5xxx_SDMA;
	uint16_t val16 = in_be16(sdma->tcr + task);
	val16 &= ~MPC5xxx_SDMA_TASK_ENABLE;
	out_be16(sdma->tcr + task, val16);
}

static inline void
mpc5xxx_sdma_set_initator_priority(size_t initiator, uint8_t value)
{
	struct mpc5xxx_sdma *sdma = (struct mpc5xxx_sdma *)MPC5xxx_SDMA;
	out_8(sdma->IPR + initiator, value);
}

void mpc5xxx_init_irq(void);
int mpc5xxx_get_irq(struct pt_regs *regs);
void mpc5xxx_pci_init_windows(uint8_t win0_ctrl, uint32_t win0_translation,
			      uint8_t win1_ctrl, uint32_t win1_translation,
			      uint8_t win2_ctrl, uint32_t win2_translation);

enum mpc5xxx_gpio_t
{
	MPC5XXX_GPIO_INVALID,
	MPC5XXX_GPIO_PSC2_2,
	MPC5XXX_GPIO_PSC3_6,
	MPC5XXX_GPIO_PSC3_7,
	MPC5XXX_GPIO_TIMER5,
	MPC5XXX_GPIO_TIMER7
};

extern int mpc5xxx_config_gpio(enum mpc5xxx_gpio_t port, int output, int opendrain);
extern int mpc5xxx_set_gpio(enum mpc5xxx_gpio_t port, int value);
extern int mpc5xxx_get_gpio(enum mpc5xxx_gpio_t port);


/* System Version Register Definitions */
#define MPC52XX_SVR(major,minor)	((uint32_t)(0x80110000UL | ((major)<<4) | (minor)))
#define MPC52XX_SVR_1_0			MPC52XX_SVR(1,0)
#define MPC52XX_SVR_1_1			MPC52XX_SVR(1,1)
#define MPC52XX_SVR_1_2			MPC52XX_SVR(1,2)
#define MPC52XX_SVR_2_2			MPC52XX_SVR(2,2)
extern uint32_t mpc52xx_get_system_version(void);


#if defined(CONFIG_GLACIER)
#include <platforms/glacier.h>
#elif defined(CONFIG_ICECUBE)
#include <platforms/icecube.h>
#elif defined(CONFIG_TELEMOTIVE_PPC)
#include <platforms/telemotive.h>
#elif defined(CONFIG_PM520)
#include <platforms/pm520.h>
#elif defined(CONFIG_PP01)
#include <platforms/pp01.h>
#elif defined(CONFIG_TOP5200)
#include <platforms/top5200.h>
#elif defined(CONFIG_TQM5200)
#include <platforms/tqm5200.h>
#elif defined(CONFIG_INKA4X0)
#include <platforms/inka4x0.h>
#endif

#endif /* __ASSEMBLY__ */

#endif /* __ASM_MPC5XXX_H */

[-- Attachment #1.3: snd-psc.c --]
[-- Type: text/x-csrc, Size: 28275 bytes --]

/************************************************************************
 **
 ** snd_psc.c
 **
 ** Derived from i2s.c. Provides some functions to support an ALSA
 ** driver module providing playback and capture on an I2S PSC
 ** connection.
 **
 ** This file is subject to the terms and conditions of the GNU General Public
 ** License.  See the file COPYING in the main directory of this archive for
 ** more details.
 **
 ** Copyright 2005 (C) Roman Fietze (roman.fietze@telemotive.de)
 **
 ************************************************************************/


#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>

#include <linux/types.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/smp_lock.h>
#include <linux/crc32.h>
#include <linux/vmalloc.h>

#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/delay.h>

#include <linux/ctype.h>

#include <linux/init.h>

#include <mgt5200/mgt5200.h>
#include <mgt5200/sdma.h>

#ifdef CONFIG_UBOOT
#include <asm/ppcboot.h>
#endif

#include <bestcomm_api.h>
#include <dma_image.capi.h>

#include <asm/mpc5xxx.h>
#include <asm/mpc5xxx_snd.h>

#undef SND_PSC_DEBUG
// #define SND_PSC_DEBUG

#undef SND_PSC_CYCLIC_STATUS
// #define SND_PSC_CYCLIC_STATUS

#if !defined(CONFIG_BESTCOMM_IMAGE2)
#error Can only be used with BestComm Image 2!
#endif

#if defined(SND_PSC_DEBUG)
#define PRDEBUG(level, format, x... )  do {if (level <= debug) printk(KERN_INFO format, ## x );} while (0)
#else
#define PRDEBUG(level, format, x... )	do {} while(0)
#endif

static int debug = -1;

#if defined(SND_PSC_DEBUG)
static int fixed = 0;
static struct mpc5xxx_psc *status_psc = NULL;
#endif

#ifdef SND_PSC_CYCLIC_STATUS
# define SND_PSC_STATUS_TIMER_PERIOD	(HZ/2)
struct timer_list status_timer;
#endif

enum snd_psc_trace_tokens
{
	ST_ASSIGN
};

#if SND_PSC_TRACE
static int lognum = -1;
static const char *snd_psc_token_text[] =
{
	"ASSIGN"
};

static inline void
snd_psc_add_timestamp(snd_mpc5xxx_i2s_substream_t *_s, enum snd_psc_trace_tokens t)
{
	trace_add_timestamp(_s->trace_handle, t);
}
#else
static inline void
snd_psc_add_timestamp(snd_mpc5xxx_i2s_substream_t *_s, enum snd_psc_trace_tokens t)
{
}
#endif



/* SICR as found in the old I2S driver */
#define SND_PSC_I2S_SLAVE_SICR	((uint32_t)(MPC5xxx_PSC_SICR_DTS1_AFTER | \
				MPC5xxx_PSC_SICR_SIM_CODEC_32 | \
				MPC5xxx_PSC_SICR_CLKPOL_RISE))

#define SND_PSC_I2S_MASTER_SICR	(SND_PSC_I2S_SLAVE_SICR | MPC5xxx_PSC_SICR_GENCLK_INT)

#define SND_PSC_SPI_SICR	((uint32_t)(MPC5xxx_PSC_SICR_SPI | \
				MPC5xxx_PSC_SICR_GENCLK_INT | \
				MPC5xxx_PSC_SICR_SHDIR_LSB | \
				MPC5xxx_PSC_SICR_CPHA | \
				MPC5xxx_PSC_SICR_MULTIWD | \
				MPC5xxx_PSC_SICR_SIM_CODEC_16 | \
				MPC5xxx_PSC_SICR_MSTR))

/* The used IRQs, PSC and DMA channel. */
#define sdma		((struct mpc5xxx_sdma *)MPC5xxx_SDMA)

typedef TaskId (*snd_psc_tx_setup_func_t)(TASK_GEN_TX_BD_api_t *taskapi, TaskSetupParamSet_t *param);
typedef TaskId (*snd_psc_rx_setup_func_t)(TASK_GEN_RX_BD_api_t *taskapi, TaskSetupParamSet_t *param);

typedef struct _snd_psc_card_setup
{
	volatile uint16_t * mclken_div;		/* pointer to mclk divider register */
	uint32_t config_mask;			/* GPIO port config mask */
	uint32_t config_set;			/* GPIO port config bits */
	uint32_t clken;				/* CDM clock enable bit mask */
	int irq[2];				/* in BestComm mode both are used, w/o BestComm only the first one */
} snd_psc_card_setup_t;


#define MPC5xxx_CDM_P	((struct mpc5xxx_cdm *)MPC5xxx_CDM)

#define MPC5xxx_CDM_CER_PSC3		MPC5xxx_CDM_CER_PSC345

#define SND_PSC_MK_DIV(psc)		&MPC5xxx_CDM_P->mclken_div_psc ## psc
#define SND_PSC_MK_MASK(psc)		MPC5xxx_GPIO_PC_PSC ## psc ## _MASK
#define SND_PSC_MK_CODEC(psc)		MPC5xxx_GPIO_PC_PSC ## psc ## _CODEC ## psc
#define SND_PSC_MK_CLKEN(psc)		MPC5xxx_CDM_CER_PSC ## psc
#define SND_PSC_MK_PSC_IRQ(psc)		MPC5xxx_PSC ## psc ## _IRQ
#define SND_PSC_MK_TASK_IRQNUM(psc,d)	(MPC5xxx_SDMA_IRQ_BASE + TASK_GEN_## d ## X ## psc ## _BD)

#define SND_PSC_SETUP_UNDEFINED		{NULL,0UL,0UL,0UL,{-1,-1}}

#define SND_PSC_SETUP(psc)		{SND_PSC_MK_DIV(psc),SND_PSC_MK_MASK(psc),SND_PSC_MK_CODEC(psc),SND_PSC_MK_CLKEN(psc),\
					 {SND_PSC_MK_TASK_IRQNUM(psc, T),SND_PSC_MK_TASK_IRQNUM(psc, R)}}

static snd_psc_card_setup_t snd_psc_setups[8] =
{
	SND_PSC_SETUP_UNDEFINED,
	SND_PSC_SETUP(1),
	SND_PSC_SETUP(2),
	SND_PSC_SETUP(3),
	SND_PSC_SETUP_UNDEFINED,
	SND_PSC_SETUP_UNDEFINED,
	SND_PSC_SETUP(6),
	SND_PSC_SETUP_UNDEFINED
};


#define SND_PSC_NUM_BDS		(4)

#define SND_PSC_TFALARM		(0x100)
#define SND_PSC_TFCNTL		(4)
#define SND_PSC_RFALARM		(0x100)
#define SND_PSC_RFCNTL		(4)


/* delay in ms */
static inline void
snd_psc_delay(const char *text, unsigned long delay, int interruptible)
{
	if (delay)
	{
		wait_queue_head_t wait;
		init_waitqueue_head(&wait);
		if (interruptible)
			interruptible_sleep_on_timeout(&wait, (HZ * delay) / 1000UL);
		else
			sleep_on_timeout(&wait, (HZ * delay) / 1000UL);
	}
}


static inline void psc_tx_enable(struct mpc5xxx_psc *psc)
{
	PRDEBUG(4, "  psc->command=%02x\n", MPC5xxx_PSC_TX_ENABLE);
	psc->command = MPC5xxx_PSC_TX_ENABLE;
}


static inline void psc_tx_disable(struct mpc5xxx_psc *psc)
{
	PRDEBUG(4, "  psc->command=%02x\n", MPC5xxx_PSC_TX_DISABLE);
	psc->command = MPC5xxx_PSC_TX_DISABLE;
}


static inline void psc_rx_enable(struct mpc5xxx_psc *psc)
{
	PRDEBUG(4, "  psc->command=%02x\n", MPC5xxx_PSC_RX_ENABLE);
	psc->command = MPC5xxx_PSC_RX_ENABLE;
}


static inline void psc_rx_disable(struct mpc5xxx_psc *psc)
{
	PRDEBUG(4, "  psc->command=%02x\n", MPC5xxx_PSC_RX_DISABLE);
	psc->command = MPC5xxx_PSC_RX_DISABLE;
}


static inline void psc_tx_rx_enable(struct mpc5xxx_psc *psc)
{
	PRDEBUG(4, "  psc->command=%02x\n", MPC5xxx_PSC_RX_ENABLE | MPC5xxx_PSC_TX_ENABLE);
	psc->command = MPC5xxx_PSC_RX_ENABLE | MPC5xxx_PSC_TX_ENABLE;
}


static inline void psc_tx_rx_disable(struct mpc5xxx_psc *psc)
{
	PRDEBUG(4, "  psc->command=%02x\n", MPC5xxx_PSC_RX_DISABLE | MPC5xxx_PSC_TX_DISABLE);
	psc->command = MPC5xxx_PSC_RX_DISABLE | MPC5xxx_PSC_TX_DISABLE;
}


static inline void psc_reset(struct mpc5xxx_psc *psc)
{
	PRDEBUG(4, "  psc->command=%02x\n", MPC5xxx_PSC_RST_RX);
	psc->command = MPC5xxx_PSC_RST_RX;
	PRDEBUG(4, "  psc->command=%02x\n", MPC5xxx_PSC_RST_TX);
	psc->command = MPC5xxx_PSC_RST_TX;
	PRDEBUG(4, "  psc->command=%02x\n", MPC5xxx_PSC_SEL_MODE_REG_1);
	psc->command = MPC5xxx_PSC_SEL_MODE_REG_1;
	PRDEBUG(4, "  psc->command=%02x\n", MPC5xxx_PSC_RST_ERR_STAT);
	psc->command = MPC5xxx_PSC_RST_ERR_STAT;
}


#ifdef SND_PSC_DEBUG
static int snd_psc_tx_rx_status_dump(int level, char *buf, char locationid)
{
	int len = 0;

	if (status_psc)
	{
		static char qbits[] = "oudrt";
		static char sbits[] = "outfr";
		static char tfsbits[] = "xuorfae";
		static char rfsbits[] = "xuorfae";

		static uint16_t lastq = 0xFFFFU;
		static uint16_t lasts = 0xFFFFU;
		static uint16_t lasttfs = 0xFFFFU;
		static uint16_t lastrfs = 0xFFFFU;

		int i;

		uint16_t q = status_psc->mpc5xxx_psc_isr;

		if (q != lastq)
		{
			char *pqbits = qbits + 4;

			lastq = q;

			q >>= 8;
			for (i = 0; i < 5; i++)
			{
				if (q & 1)
					*pqbits = toupper(*pqbits);
				else
					*pqbits = tolower(*pqbits);

				q >>= 1;
				pqbits--;
			}
		}

		uint16_t s = status_psc->mpc5xxx_psc_status;

		if (s != lasts)
		{
			char *psbits = sbits + 4;

			lasts = s;

			s >>= 8;
			for (i = 0; i < 5; i++)
			{
				if (s & 1)
					*psbits = toupper(*psbits);
				else
					*psbits = tolower(*psbits);

				s >>= 1;
				psbits--;
			}
		}

		uint16_t tfs = status_psc->tfstat;

		if (tfs != lasttfs)
		{
			char *ptfsbits = tfsbits + 6;

			lasttfs = tfs;

			for (i = 0; i < 7; i++)
			{
				if (tfs & 1)
					*ptfsbits = toupper(*ptfsbits);
				else
					*ptfsbits = tolower(*ptfsbits);

				tfs >>= 1;
				ptfsbits--;
			}
		}

		uint16_t rfs = status_psc->rfstat;

		if (rfs != lastrfs)
		{
			char *prfsbits = rfsbits + 6;

			lastrfs = rfs;

			for (i = 0; i < 7; i++)
			{
				if (rfs & 1)
					*prfsbits = toupper(*prfsbits);
				else
					*prfsbits = tolower(*prfsbits);

				rfs >>= 1;
				prfsbits--;
			}
		}

		if (buf)
		{
			/* proc fs output */
			len = sprintf(buf,
				      "ISR: %s %04x | SR: %s %04x | tx: %s %02x n=%3u w=%3u r=%3u | rx: %s %02x n=%3u w=%3u r=%3u\n",
				      qbits,
				      lastq,
				      sbits,
				      lasts,
				      tfsbits,
				      lasttfs,
				      status_psc->tfnum,
				      status_psc->tfwptr,
				      status_psc->tfrptr,
				      rfsbits,
				      lastrfs,
				      status_psc->rfnum,
				      status_psc->rfwptr,
				      status_psc->rfrptr);
		}
		else
		{
			/* printk output */
			PRDEBUG(level, "PSC(%c): | ISR: %s %04x | SR: %s %04x | tx: %s %02x n=%3u w=%3u r=%3u | rx: %s %02x n=%3u w=%3u r=%3u\n",
				locationid,
				qbits,
				lastq,
				sbits,
				lasts,
				tfsbits,
				lasttfs,
				status_psc->tfnum,
				status_psc->tfwptr,
				status_psc->tfrptr,
				rfsbits,
				lastrfs,
				status_psc->rfnum,
				status_psc->rfwptr,
				status_psc->rfrptr);
		}
	}
	else
	{
		if (buf)
			len = sprintf(buf, "%s: PSC not yet defined!\n", __FUNCTION__);
		else
			PRDEBUG(level, "%s: PSC not yet defined!\n", __FUNCTION__);
	}

	return (len);
}
#endif


static inline void snd_psc_tx_rx_status(int level, char locationid)
{
#ifdef SND_PSC_DEBUG
	if (debug >= level)
		snd_psc_tx_rx_status_dump(level, 0, locationid);
#endif
}


#if defined(SND_PSC_DEBUG)
static void snd_psc_dump_buffer(int level, char id, const void *_pc, size_t n)
{
	if (debug >= level)
	{
		const uint8_t *pc = _pc;
		size_t col = 0;
		while (n--)
		{
			if (col == 0)
				printk(KERN_INFO "%c: %p:", id, pc);

			printk(" %02x", *pc);
			if (col == 15)
			{
				col = 0;
				printk("\n");
			}
			else
			{
				col++;
			}

			pc++;
		}

		if (col != 0)
			printk("\n");
	}
}
#else
static inline void snd_psc_dump_buffer(int level, char id, const void *pc, size_t n)
{
}
#endif


static irqreturn_t snd_psc_irq(int irq, void *dev_id, struct pt_regs *regs)
{
	snd_mpc5xxx_i2s_substream_t *s = dev_id;

	if (s->dma_started)
	{
		PRDEBUG(5, "%s(%d,%p,...) name=%s\n", __FUNCTION__, irq, dev_id, s->name);

		snd_mpc5xxx_i2s_card_t *card = s->card;
		size_t num = 0;
		size_t tc;
		static size_t maxnum = 0;
		TaskBD1_t *pbd;
		BDIdx bdidx;

		TaskIntClear(s->task_id);

		tc = s->period_size;
		if (tc > SND_PSC_MAX_PERIOD_SIZE)
			tc = SND_PSC_MAX_PERIOD_SIZE;

		spin_lock(&card->lock);
		pbd = (TaskBD1_t *)TaskGetBD(s->task_id, s->bdidx);

		while (!(pbd->Status & SDMA_BD_MASK_READY))
		{
			s->bdidx = TaskBDRelease(s->task_id);
			s->irqpos += tc;
			s->irqpos %= s->buffer_size;
			s->processed += tc;
			num++;
			PRDEBUG(5, "%s: %s: %u:  bdidx=%d processed=%u dmapos=%u irqpos=%u\n",
				__FUNCTION__, s->name, num, s->bdidx, s->processed, s->dmapos, s->irqpos);
#if defined(SND_PSC_DEBUG)
			if (fixed && tc >= 4 * sizeof(uint16_t))
			{
				static const uint16_t valid[4] = {0x55AAU, 0x0C0CU, 0x55AAU, 0x0C0CU};
				const uint16_t *pdata = phys_to_virt(s->dma_addr + s->dmapos);
				PRDEBUG(2, "%s: pdata=%p %04x %04x %04x %04x %04x ...\n",
					__FUNCTION__,
					pdata,
					pdata[0], pdata[1], pdata[2], pdata[3], pdata[4]);

				if (memcmp(valid, pdata, sizeof(valid)))
				{
					PRDEBUG(1, "%s: data mismatch for fixed data WAV sample\n", __FUNCTION__);
					snd_psc_dump_buffer(1, 'F', pdata, tc);
				}
			}
#endif
			bdidx = TaskBDAssign(s->task_id, (void *)(s->dma_addr + s->dmapos), NULL, tc, 0);
			if (bdidx < 0)
				printk(KERN_ERR "snd-psc: %s: error %d assigning BD\n", s->name, bdidx);
			s->dmapos += tc;
			s->dmapos %= s->buffer_size;
			pbd = (TaskBD1_t *)TaskGetBD(s->task_id, s->bdidx);
		}

		if (s->processed >= s->period_size)
		{
			s->processed %= s->period_size;
			spin_unlock(&card->lock);
			PRDEBUG(5, "%s: handler\n", __FUNCTION__);
			s->handler(s->stream);
		}
		else
		{
			spin_unlock(&card->lock);
		}

		if (num > maxnum)
			maxnum = num;
		if (num != 1)
			PRDEBUG(1, "%s: num=%u maxnum=%u\n", __FUNCTION__, num, maxnum);
	}
}


size_t snd_psc_get_substream_pos(snd_mpc5xxx_i2s_substream_t *_s)
{
	PRDEBUG(5, "%s(%s) return %u\n", __FUNCTION__, _s->name, _s->irqpos);
	return _s->irqpos;
}


int snd_psc_substream_open(snd_mpc5xxx_i2s_substream_t *_s, void *_stream)
{
	snd_mpc5xxx_i2s_card_t *card = _s->card;
	unsigned long flags;
	PRDEBUG(1, "%s(%s,%p)\n", __FUNCTION__, _s->name, _stream);
	spin_lock_irqsave(&card->lock, flags);
	_s->stream = _stream;
	_s->dma_addr = 0;
	spin_unlock_irqrestore(&card->lock, flags);
	return 0;
}


int snd_psc_substream_params(snd_mpc5xxx_i2s_substream_t *_s,
			     uint32_t _rate, size_t _channels, size_t _bits,
			     size_t _buffer_size, size_t _period_size)
{
	snd_mpc5xxx_i2s_card_t *card = _s->card;
	const uint32_t bitrate = _rate * _channels * _bits;
	int ret = -EINVAL;
	unsigned long flags;

	PRDEBUG(1, "%s(%s,%u,%u,%u,%u,%u) bitrate=%uHz\n",
		__FUNCTION__,
		_s->name, _rate, _channels, _bits, _buffer_size, _period_size,
		bitrate);

	spin_lock_irqsave(&card->lock, flags);

	// In slave mode this wouldn't work:
	// if (card->rate != _rate || card->channels != _channels || card->bits != _bits)

	// so we always go in here until we understand why :)
	if (1)
	{
		spin_unlock_irqrestore(&card->lock, flags);

		PRDEBUG(1, "%s: changing some parameters\n", __FUNCTION__);
		PRDEBUG(1, "%s: card: old: rate=%u channels=%u bits=%u\n", __FUNCTION__, card->rate, card->channels, card->bits);

		struct mpc5xxx_psc *psc = PSC_ADDRESS(card->pscnum);
		struct mpc5xxx_gpio * const gpio = (struct mpc5xxx_gpio *)MPC5xxx_GPIO;
		const snd_psc_card_setup_t *setup = snd_psc_setups + card->pscnum;

		uint32_t mclk;
		uint32_t mclkdiv;
		uint32_t bitclkdiv;
		uint32_t testmclkdiv;
		uint32_t testbitclkdiv;
		uint32_t maxerr;
		uint32_t currerr;
		uint32_t btmp;
		uint8_t ctur;
		uint8_t ctlr;
		uint16_t ccr;
		uint32_t sicr;
		uint32_t port_config;

#ifdef CONFIG_UBOOT
		extern unsigned char __res[];
		const bd_t * const bd = (bd_t *)__res;
		uint32_t fsystem = 0;

		if (MPC5xxx_CDM_P->rstcfg & ( 1 << 5))
			fsystem = bd->bi_busfreq * 8;
		else
			fsystem = bd->bi_busfreq * 4;

		PRDEBUG(3, "%s: cdm->rstcfg = 0x%08x fsystem=%uHz\n", __FUNCTION__, MPC5xxx_CDM_P->rstcfg, fsystem);
#else
		return -EINVAL;
#endif

		const uint32_t MCLKDIV_MAX	= (1U << 9);
		const uint32_t BITCLKDIV_MAX	= (1U << 8);

		PRDEBUG(4, "  psc->command=%02x\n", MPC5xxx_PSC_TX_DISABLE | MPC5xxx_PSC_RX_DISABLE);

		spin_lock_irqsave(&card->lock, flags);

		psc_tx_rx_disable(psc);
		psc_reset(psc);

		PRDEBUG(4, "  psc->mode=0\n");
		psc->mode = 0;

		PRDEBUG(4, "snd-psc: %s\n", __FUNCTION__);

		if (!card->slave)
		{
			/* Calculate CDM MCLK, PSC CCR BitClkDiv from bitrate. As well
			 * calculate PSC CCR FrameSyncDiv from mclk and check that it
			 * is not too large.
			 */
			maxerr = UINT32_MAX;

			for (testmclkdiv = 1; ret && testmclkdiv < MCLKDIV_MAX; testmclkdiv++)
			{
				mclk = fsystem / (testmclkdiv + 1);
				PRDEBUG(5, "snd-psc: mclk=%u\n", mclk);
				if (mclk > bitrate)
				{
					for (testbitclkdiv = 1; ret && testbitclkdiv < BITCLKDIV_MAX; testbitclkdiv++)
					{
						btmp = mclk / (testbitclkdiv + 1);
						if (btmp != bitrate)
						{
							if (btmp > bitrate)
								currerr = btmp - bitrate;
							else
								currerr = bitrate - btmp;

							if (currerr < maxerr)
							{
								maxerr = currerr;
								mclkdiv = testmclkdiv;
								bitclkdiv = testbitclkdiv;

								PRDEBUG(5,
									"snd-psc: currerr=%u mclkdiv=%u bitclkdiv=%u\n",
									currerr, testmclkdiv, testbitclkdiv);
							}
						}
						else
						{
							maxerr = 0;
							mclkdiv = testmclkdiv;
							bitclkdiv = testbitclkdiv;
							ret = 0;
						}
					}
				}
			}

			if (maxerr < bitrate / 10)
				ret = 0;

			PRDEBUG(1,
				"snd-psc: maxerr=%u mclkdiv=%u bitclkdiv=%u bitrate=%u resulting br=%u\n",
				maxerr, mclkdiv, bitclkdiv, bitrate, fsystem / (mclkdiv+1) / (bitclkdiv+1));

			PRDEBUG(1, "snd-psc: mclkdiv=%u, bitclkdiv=%u, ret=%d\n", mclkdiv, bitclkdiv, ret);

			if (ret < 0)
			{
				printk(KERN_ERR
				       "%s: cannot find correct clock dividers for a sample rate of %u.\n",
				       __FUNCTION__,
				       _rate);
				return ret;
			}
		}

		if (card->spi)
		{
			const uint32_t ipb = bd->bi_ipbfreq;
			const uint16_t dtl = 2;
			const uint32_t ct = (dtl * ipb) / 1000000U;
			ctur = MPC5xxx_PSC_CTUR_CODEC(ct >> 8);		/* DTL high byte */
			ctlr = MPC5xxx_PSC_CTLR_CODEC(ct);		/* DTL low byte */
			ccr = MPC5xxx_PSC_CCR_CODEC(0, bitclkdiv);	/* DSCKL as low as possible: TBR */
			sicr = SND_PSC_SPI_SICR;
		}
		else
		{
			ctur = MPC5xxx_PSC_CTUR_CODEC(8 * sizeof(uint16_t) - 1);
			ctlr = 0;
			ccr = MPC5xxx_PSC_CCR_CODEC(2 * 8 * sizeof(uint16_t) - 1, bitclkdiv);
			sicr = card->slave ? SND_PSC_I2S_SLAVE_SICR : SND_PSC_I2S_MASTER_SICR;
		}

		PRDEBUG(4, "  cdm->mclken_div_psc%u=%08x\n", card->pscnum,
			MPC5xxx_PSC_MCLOCK_ENABLE | MPC5xxx_PSC_MCLOCK_MCLKDIV(mclkdiv));
		*(setup->mclken_div) = MPC5xxx_PSC_MCLOCK_ENABLE | MPC5xxx_PSC_MCLOCK_MCLKDIV(mclkdiv);
		PRDEBUG(4, "  cdm->clk_enables|=%08x\n", setup->clken);
		MPC5xxx_CDM_P->clk_enables |= setup->clken;
		PRDEBUG(4, "  psc->ctur=%02x\n", ctur);
		psc->ctur = ctur;
		PRDEBUG(4, "  psc->ctlr=%02x\n", ctlr);
		psc->ctlr = ctlr;
		PRDEBUG(4, "  psc->ccr=%04x\n", ccr);
		psc->ccr = ccr;
		port_config = gpio->port_config;
		port_config = (port_config & ~setup->config_mask) | setup->config_set;
		PRDEBUG(4, "  gpio->port_config = %08x\n", port_config);
		gpio->port_config = port_config;
		PRDEBUG(4, "  psc->tfstat=%04x\n", MPC5xxx_PSC_FIFO_ERR | MPC5xxx_PSC_FIFO_UF | MPC5xxx_PSC_FIFO_OF);
		psc->tfstat = MPC5xxx_PSC_FIFO_ERR | MPC5xxx_PSC_FIFO_UF | MPC5xxx_PSC_FIFO_OF;	// reset some status bits
		PRDEBUG(4, "  psc->rfstat=%04x\n", MPC5xxx_PSC_FIFO_ERR | MPC5xxx_PSC_FIFO_UF | MPC5xxx_PSC_FIFO_OF);
		psc->rfstat = MPC5xxx_PSC_FIFO_ERR | MPC5xxx_PSC_FIFO_UF | MPC5xxx_PSC_FIFO_OF;
		PRDEBUG(4, "  psc->tfalarm=%d\n", SND_PSC_TFALARM);
		psc->tfalarm = SND_PSC_TFALARM;		/* alarm threshold level */
		PRDEBUG(4, "  psc->rfalarm=%d\n", SND_PSC_RFALARM);
		psc->rfalarm = SND_PSC_RFALARM;
		PRDEBUG(4, "  psc->tfcntl=%d\n", SND_PSC_TFCNTL);
		psc->tfcntl = SND_PSC_TFCNTL;		/* granularity */
		PRDEBUG(4, "  psc->rfcntl=%d\n", SND_PSC_RFCNTL);
		psc->rfcntl = SND_PSC_RFCNTL;
		PRDEBUG(4, "  psc->imr=0\n");
		psc->mpc5xxx_psc_imr = 0;	/* disable interrupts */
		PRDEBUG(1, "%s: %s: sicr=%08x\n", __FUNCTION__, _s->name, sicr);
		psc->sicr = sicr;

		if (card->loopback)
		{
			volatile int val;
			printk(KERN_INFO "snd-psc: ************** local loop back mode enabled on PSC%u\n", card->pscnum);
			val = psc->mode;	/* to switch from mode1 to mode2 */
			val++;			/* ??? short delay ??? */
			psc->mode = 0x80;
		}

#if defined(SND_PSC_DEBUG)
		if (fixed)
			snd_psc_tx_rx_status(1, 'P');
#endif

		/* reset PSC again */
		psc_reset(psc);
		if (card->psc_started)
			psc_tx_rx_enable(psc);

		card->rate = _rate;
		card->channels = _channels;
		card->bits = _bits;
	}

	_s->buffer_size = _buffer_size;
	_s->period_size = _period_size;

	spin_unlock_irqrestore(&card->lock, flags);

	return 0;
}


int snd_psc_substream_start(snd_mpc5xxx_i2s_substream_t *_s, dma_addr_t _dma_addr)
{
	snd_mpc5xxx_i2s_card_t *card = _s->card;
	unsigned long flags;

	PRDEBUG(1, "snd-psc: %s(s=%s,dma_addr=%08x) id=%d\n", __FUNCTION__, _s->name, _dma_addr, _s->id);

	spin_lock_irqsave(&card->lock, flags);

	if (!_s->dma_started)
	{
		struct mpc5xxx_psc *psc = PSC_ADDRESS(card->pscnum);
		size_t i;
		size_t tc;
		BDIdx bdidx;

		card->streams[_s->id] = _s;
		_s->processed = 0;

		_s->dma_addr = _dma_addr;
		_s->dmapos = _s->irqpos = 0;

		if (_s->id)
		{
			uint32_t tmp;

			/* empty RX FIFO to remove noise */
			while (psc->rfnum >= sizeof(uint32_t))
				tmp = psc->mpc5xxx_psc_buffer_32;
		}

		tc = _s->period_size;
		if (tc > SND_PSC_MAX_PERIOD_SIZE)
			tc = SND_PSC_MAX_PERIOD_SIZE;

		bdidx = TaskBDReset(_s->task_id);
		if (bdidx < 0)
			printk(KERN_ERR "snd-psc: %s: error %d resetting BDs\n", _s->name, bdidx);

		for (i = 0; i < SND_PSC_NUM_BDS; i++)
		{
			PRDEBUG(3, "%s: assign BD at dma_addr[%u] (%08x)\n",
				__FUNCTION__,
				_s->dmapos, _s->dma_addr + _s->dmapos);
			bdidx = TaskBDAssign(_s->task_id, (void *)(_s->dma_addr + _s->dmapos), NULL, tc, 0);
			if (bdidx < 0)
				printk(KERN_ERR "snd-psc: %s: error %d assigning BD\n", _s->name, bdidx);
			_s->dmapos += tc;
			_s->dmapos %= _s->buffer_size;
		}

		if (!card->psc_started)
			psc_tx_rx_enable(psc);
		card->psc_started |= 1 << _s->id;

#if defined(SND_PSC_DEBUG)
		if (fixed)
			snd_psc_tx_rx_status(1, 'S');
#endif

		TaskStart(_s->task_id, TASK_AUTOSTART_ENABLE, _s->task_id, TASK_INTERRUPT_ENABLE);

		_s->dma_started = 1;
	}

	spin_unlock_irqrestore(&card->lock, flags);

	return 0;
}


int snd_psc_substream_stop(snd_mpc5xxx_i2s_substream_t *_s)
{
	snd_mpc5xxx_i2s_card_t *card = _s->card;
	unsigned long flags;

	PRDEBUG(3, "snd-psc: %s(%s)\n", __FUNCTION__, _s->name);

	spin_lock_irqsave(&card->lock, flags);
	if (_s->dma_started)
	{
		struct mpc5xxx_psc *psc = PSC_ADDRESS(card->pscnum);

		_s->dma_started = 0;
		card->streams[_s->id] = NULL;

		/* stop the tasks */
		card->psc_started &= ~(1 << _s->id);
		if (!card->psc_started)
			psc_tx_rx_disable(psc);

		TaskStop(_s->task_id);
		TaskBDReset(_s->task_id);
	}

	spin_unlock_irqrestore(&card->lock, flags);

	return 0;
}


/* Initialize as much as possible of the BestComm init
 * structures.
 */
int snd_psc_substream_create(snd_mpc5xxx_i2s_substream_t **_s,
			     snd_mpc5xxx_i2s_card_t *_card,
			     const char *_name,
			     int _id,			/* 0..1 */
			     snd_psc_irq_handler_t _handler)
{
	struct mpc5xxx_psc *psc = PSC_ADDRESS(_card->pscnum);
	TaskSetupParamSet_t setup;
	int ret;

	snd_mpc5xxx_i2s_substream_t *s = kmalloc(sizeof(snd_mpc5xxx_i2s_substream_t), GFP_KERNEL);
	*_s = s;
	if (s == NULL)
		return -ENOMEM;

	/* first of all, disable all PSC interrupts */
	psc->mpc5xxx_psc_imr = 0;

	PRDEBUG(1, "snd-psc: %s(s=%p,card=%p,name=%s,id=%d,handler=%p) substream=%p\n",
		__FUNCTION__,
		_s, _card,
		_name, _id, _handler,
		s);

	s->id = _id;
	s->card = _card;
	s->stream = NULL;
	strncpy(s->name, _name, sizeof(s->name));
	s->task_id = 0;
	s->bdidx = 0;
	s->irq = snd_psc_setups[_card->pscnum].irq[_id];
	s->dmapos = s->irqpos = 0;
	s->dma_started = 0;
	s->processed = 0;
	s->handler = _handler;
	s->stream = NULL;

#if SND_PSC_TRACE
	trace_create(&s->trace_handle, lognum, snd_psc_token_text, ARRAY_SIZE(snd_psc_token_text), SND_PSC_TRACE);
#endif

	memset(&setup, 0, sizeof(setup));
	/* setup.Initiator will be setup by the task setup function automatically */
	setup.NumBD = SND_PSC_NUM_BDS;
	setup.Size.MaxBuf = SND_PSC_MAX_PERIOD_SIZE;
	setup.StartAddrSrc = setup.StartAddrDst = 0;
	setup.SzSrc = setup.SzDst = sizeof(uint32_t);

	PRDEBUG(4, "%s: pscnum=%u Initiator=%u\n", __FUNCTION__, _card->pscnum, setup.Initiator);

	if (!_id)
	{
		/* task to transmit data */
		setup.StartAddrDst = (uint32_t)&(psc->mpc5xxx_psc_buffer_32);
		setup.IncrSrc = sizeof(uint32_t);
		setup.IncrDst = 0;
		PRDEBUG(3, "%s: setup TX\n", __FUNCTION__);
		s->task_id = TaskSetup_TASK_GEN_TX_BD(_card->pscnum, &setup, 0);
	}
	else
	{
		/* task to receive data */
		setup.StartAddrSrc = (uint32_t)&(psc->mpc5xxx_psc_buffer_32);
		setup.IncrSrc = 0;
		setup.IncrDst = sizeof(uint32_t);
		PRDEBUG(3, "%s: setup RX\n", __FUNCTION__);
		s->task_id = TaskSetup_TASK_GEN_RX_BD(_card->pscnum, &setup, 0);
	}

	if (s->task_id < 0)
	{
		printk(KERN_ERR "snd-psc: cannot setup BestComm task: err=%d\n", s->task_id);
		kfree(s);
		*_s = NULL;
		return -EFAULT;
	}

	PRDEBUG(3, "%s: pscnum=%u _s->task_id=%u, request IRQ%d\n", __FUNCTION__, _card->pscnum, s->task_id, s->irq);

	if ((ret = request_irq(s->irq, snd_psc_irq, SA_INTERRUPT, s->name, s)))
	{
		printk(KERN_ERR
		       "%s: PSC DMA irq (%d) allocation failed, error %d\n",
		       s->name,
		       s->irq,
		       -ret);

		kfree(s);
		*_s = NULL;
		return ret;
	}

	return 0;
}


int snd_psc_substream_close(snd_mpc5xxx_i2s_substream_t *_s)
{
	PRDEBUG(1, "%s(%s)\n", __FUNCTION__, _s->name);
	return 0;
}


int snd_psc_substream_free(snd_mpc5xxx_i2s_substream_t *_s)
{
	struct mpc5xxx_psc *psc = PSC_ADDRESS(_s->card->pscnum);

	PRDEBUG(1, "%s(%p)\n", __FUNCTION__, _s);

	/* stop the PSC */
	psc_tx_disable(psc);
	psc_rx_disable(psc);

	psc_reset(psc);

	free_irq(_s->irq, _s);

#if SND_PSC_TRACE
	trace_dump(_s->trace_handle);
	trace_free(_s->trace_handle);
#endif

	return 0;
}


#ifdef SND_PSC_CYCLIC_STATUS
static void snd_psc_status_timer_interrupt(unsigned long arg)
{
	snd_psc_tx_rx_status(1, 'T');

	if (status_psc)
		mod_timer(&status_timer, jiffies + SND_PSC_STATUS_TIMER_PERIOD);
}
#endif


int snd_psc_card_create(snd_mpc5xxx_i2s_card_t **_card,
			uint16_t _pscnum,
			int _slave, int _spi, int _loopback)
{
	static const char *request_names[8] =
		{
			"invalid_PSC",
			"snd_psc_PS1",
			"snd_psc_PS2",
			"snd_psc_PS3",
			"invalid_PSC",
			"invalid_PSC",
			"snd_psc_PS6",
			"invalid_PSC"
		};

	size_t i;
	struct mpc5xxx_psc *psc = PSC_ADDRESS(_pscnum);

	snd_mpc5xxx_i2s_card_t *card = kmalloc(sizeof(snd_mpc5xxx_i2s_card_t), GFP_KERNEL);
	*_card = card;
	if (card == NULL)
	{
		return -ENOMEM;
	}

	MOD_INC_USE_COUNT;

#if defined(SND_PSC_DEBUG)
	status_psc = psc;
#else
	if (debug != -1)
		printk(KERN_WARNING "%s: compiled without debug support\n", __FUNCTION__);
#endif

	PRDEBUG(1,
		"%s(card=%p,pscnum=%u,slave=%d,spi=%d,loopback=%d) card=%p\n",
		__FUNCTION__,
		_card, _pscnum, _slave, _spi, _loopback, card);

	/* Can't fail */
	request_region((unsigned long)psc, sizeof(struct mpc5xxx_psc), request_names[_pscnum]);
	snprintf(card->name, sizeof(card->name), "snd_psc_psc%u", _pscnum);
	card->pscnum = _pscnum;
	card->rate = 0;
	card->channels = 0;
	card->bits = 0;
	card->slave = _slave;
	card->spi = _spi;
	card->loopback = _loopback;
	spin_lock_init(&card->lock);
	for (i = 0; i < SND_PSC_MAX_STREAMS; i++)
		card->streams[i] = NULL;

	card->psc_started = 0;
	psc->mpc5xxx_psc_imr = 0;

#ifdef SND_PSC_CYCLIC_STATUS
	/* Start timer interrupt to get transfer status every second. */
	PRDEBUG(1, "snd-psc: starting cyclic status\n");
	init_timer(&status_timer);
	status_timer.function = snd_psc_status_timer_interrupt;
	status_timer.data = 0;
	add_timer(&status_timer);
#endif

	return 0;
}


void snd_psc_card_free(snd_mpc5xxx_i2s_card_t *_card)
{
	PRDEBUG(1, "%s(%p)\n", __FUNCTION__, _card);

	if (_card)
	{
		struct mpc5xxx_psc *psc = PSC_ADDRESS(_card->pscnum);
		release_region((unsigned long)psc, sizeof(struct mpc5xxx_psc));
	}

#ifdef SND_PSC_CYCLIC_STATUS
	if (status_psc)
	{
		PRDEBUG(1, "snd-psc: stopping cyclic status\n");
		status_psc = NULL;
		del_timer_sync(&status_timer);
	}
#endif

	MOD_DEC_USE_COUNT;
}


static int __init snd_psc_init(void)
{
	PRDEBUG(1, "%s() debug=%d fixed=%d\n", __FUNCTION__, debug, fixed);
	printk(KERN_INFO "MPC5200 PSC I2S Audio DMA Driver\n");
	return 0;
}


static void __exit snd_psc_exit(void)
{
	PRDEBUG(1, "%s\n", __FUNCTION__);
}


module_init(snd_psc_init);
module_exit(snd_psc_exit);

MODULE_DESCRIPTION("Driver for the MPC5200 PSC in CODEC SND_PSC mode.");
MODULE_AUTHOR("Roman Fietze, Telemotive AG, roman.fietze@telemotive.de");
MODULE_SUPPORTED_DEVICE("MPC5200 PSC SND_PSC Codec");

MODULE_LICENSE("GPL");

#if defined(SND_PSC_DEBUG)
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "debug level (0-6)");
MODULE_PARM(fixed, "i");
MODULE_PARM_DESC(fixed, "check fixed data stream (0-1)");
#endif

#if SND_PSC_TRACE
MODULE_PARM(lognum, "i");
MODULE_PARM_DESC(lognum, "Log file number (/var/log/tmproto-<num>) (0-99)");
#endif

EXPORT_SYMBOL(snd_psc_card_create);
EXPORT_SYMBOL(snd_psc_card_free);

EXPORT_SYMBOL(snd_psc_substream_create);
EXPORT_SYMBOL(snd_psc_substream_open);
EXPORT_SYMBOL(snd_psc_substream_params);
EXPORT_SYMBOL(snd_psc_substream_start);
EXPORT_SYMBOL(snd_psc_get_substream_pos);
EXPORT_SYMBOL(snd_psc_substream_stop);
EXPORT_SYMBOL(snd_psc_substream_close);
EXPORT_SYMBOL(snd_psc_substream_free);

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

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Lite5200B I2S driver
  2006-12-13  9:39   ` Pedro Luis D. L.
  2006-12-13 12:56     ` Roman Fietze
  2006-12-13 12:58     ` Roman Fietze
@ 2006-12-13 13:15     ` Roman Fietze
  2006-12-13 14:37       ` Pedro Luis D. L.
  2 siblings, 1 reply; 13+ messages in thread
From: Roman Fietze @ 2006-12-13 13:15 UTC (permalink / raw)
  To: linuxppc-embedded


[-- Attachment #1.1: Type: text/plain, Size: 259 bytes --]

Hello Pedro,

I just discovered one important, but missing file.


Roman

-- 
Roman Fietze              Telemotive AG Büro Mühlhausen
Breitwiesen                            73347 Mühlhausen
Tel.: +49(0)7335/18493-45      http://www.telemotive.de


[-- Attachment #1.2: mpc5xxx_snd.h --]
[-- Type: text/x-chdr, Size: 3069 bytes --]

/*
 * mpc5xxx_snd.h
 *
 * Definitions to use the PSC in I2S codec mode with the BestComm for
 * an ALSA sound driver.
 */

#ifndef _MPC5XXX_SND_H
#define _MPC5XXX_SND_H

#include <linux/kernel.h>
#include <linux/interrupt.h>

#define SND_PSC_TRACE	(0)
// #define SND_PSC_TRACE		(1000)

// #undef SND_PSC_STATISTICS
#define SND_PSC_STATISTICS

/* Some needed or useful constants. */
#define SND_PSC_AUDIO_RATE_MIN		(8000)
#define SND_PSC_AUDIO_RATE_DEFAULT	(44100)
#define SND_PSC_AUDIO_RATE_MAX		(48000)
#define SND_PSC_MAX_BUFFER_SIZE		(0x40000)
#define SND_PSC_MIN_PERIOD_SIZE		(1024)
#define SND_PSC_MAX_PERIOD_SIZE		(4096)


#if SND_PSC_TRACE
# if defined(MODULE)
#  if !defined(CONFIG_TRACE) && !defined(CONFIG_TRACE_MODULE)
#   warning "Need CONFIG_TRACE or CONFIG_TRACE_MODULE, disable trace."
#   undef SND_PSC_TRACE
#   define SND_PSC_TRACE	(0)
#  endif
# elif !defined(CONFIG_TRACE)
#   warning "Need CONFIG_TRACE, disable trace."
#   undef SND_PSC_TRACE
#   define SND_PSC_TRACE	(0)
# endif
#endif

#if SND_PSC_TRACE
# include <linux/trace.h>
#endif

#define SND_PSC_MAX_STREAMS	(2)

typedef void (*snd_psc_irq_handler_t)(void *_stream);

typedef struct _snd_mpc5xxx_i2s_substream
{
	char name[40];

	int id;
	struct _snd_mpc5xxx_i2s_card *card;

	int dma_started;
	int8_t task_id;
	int32_t bdidx;			/* BestComms current BDIdx */
	int irq;
	dma_addr_t dma_addr;
	size_t dmapos;
	size_t irqpos;
	size_t processed;		/* processed since last call of xxx_pointer() */
	size_t buffer_size;
	size_t period_size;

	snd_psc_irq_handler_t handler;
	void *stream;

#if SND_PSC_TRACE
	trace_handle_t *trace_handle;
#endif

} snd_mpc5xxx_i2s_substream_t;


typedef struct _snd_mpc5xxx_i2s_card
{
	char name[20];
	uint16_t pscnum;	/* identical to cardid in ALSA driver */
	int loopback;
	uint32_t rate;
	size_t channels;
	size_t bits;
	int slave;		/* run I2S codec in clock slave mode */
	int spi;		/* run SPI codec instead of I2S codec */

	spinlock_t lock;

	int psc_started;	/* bit mask for started substreams */

	snd_mpc5xxx_i2s_substream_t *streams[SND_PSC_MAX_STREAMS];
} snd_mpc5xxx_i2s_card_t;


int snd_psc_card_create(snd_mpc5xxx_i2s_card_t **_card,
			uint16_t _pscnum,
			int _slave, int _spi, int _loopback);

void snd_psc_card_free(snd_mpc5xxx_i2s_card_t *_card);

int snd_psc_substream_create(snd_mpc5xxx_i2s_substream_t **_s,
			     snd_mpc5xxx_i2s_card_t *_card,
			     const char *_name,
			     int _id,			/* 0..1 */
			     snd_psc_irq_handler_t _handler);

int snd_psc_substream_open(snd_mpc5xxx_i2s_substream_t *_s, void *_stream);

int snd_psc_substream_params(snd_mpc5xxx_i2s_substream_t *_s,
			     uint32_t _rate, size_t _channels, size_t _bits,
			     size_t _buffer_size, size_t _period_size);

int snd_psc_substream_start(snd_mpc5xxx_i2s_substream_t *_s, dma_addr_t _dma_addr);

size_t snd_psc_get_substream_pos(snd_mpc5xxx_i2s_substream_t *_s);
int snd_psc_substream_stop(snd_mpc5xxx_i2s_substream_t *_s);
int snd_psc_substream_close(snd_mpc5xxx_i2s_substream_t *_s);
int snd_psc_substream_free(snd_mpc5xxx_i2s_substream_t *_s);

#endif

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

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Lite5200B I2S driver
  2006-12-13 13:15     ` Roman Fietze
@ 2006-12-13 14:37       ` Pedro Luis D. L.
  0 siblings, 0 replies; 13+ messages in thread
From: Pedro Luis D. L. @ 2006-12-13 14:37 UTC (permalink / raw)
  To: linuxppc-embedded

On Wed, 13 Dec 2006, Roman Fietze wrote:

>Hello Pedro,
>
>I just discovered one important, but missing file.
>
>
>Roman
>

Hello Roman,

Thank you very much for the code. I´ll see what can I do.

   Pedro.

_________________________________________________________________
Dale rienda suelta a tu tiempo libre. Mil ideas para exprimir tu ocio con 
MSN Entretenimiento. http://entretenimiento.msn.es/

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Lite5200B I2S driver
  2006-12-12 15:47 Lite5200B I2S driver Pedro Luis D. L.
  2006-12-12 19:22 ` Sylvain Munaut
  2006-12-13  9:10 ` Roman Fietze
@ 2006-12-17  8:03 ` Grant Likely
  2 siblings, 0 replies; 13+ messages in thread
From: Grant Likely @ 2006-12-17  8:03 UTC (permalink / raw)
  To: Pedro Luis D. L.; +Cc: linuxppc-embedded

On 12/12/06, Pedro Luis D. L. <carcadiz@hotmail.com> wrote:
> Hi all,
>
> I=B4m working with a Lite5200B EVB, trying to atach an audio output to a =
PCM
> 1680 from Texas Instruments.
> At this moment I am using the patched 2.6.16.11-rt18 kernel that comes wi=
th
> Freescale BSP.
> I=B4ve tried to develop an I2S driver for the platform with no results. I=
=B4m
> afraid that my driver devolping skills are still too low.
>
> Anybody knows a working I2S driver for this board that can throw any
> audiooutput through PSCs?

I've just pushed a driver for the mpc52xx psc + AIC26 codec out to the
'lite5200' branch of my git tree on http://git.secretlab.ca.  I've
just pushed it out to my tree so that those who are interested can
take a look at it.  But it is *not* complete.  Here's a non-exhaustive
list of known issues with it.

1. not ported to arch/powerpc
2. doesn't use the SPI infrastructure
3. I had to rip out some board specific code for manipulating SPI chip
selects (in order to release it)
4. Poor coding convention in places

It works quite well on one of my targets; but I was not able to
release that code as is; hence the broken driver.  I'll be working on
this code to get it into shape for inclusion into mainline.  Most of
the work will probably revolve around doing a proper SPI connection
and adapting the bestcomm code to whatever Sylvain has brewing in his
tree.  The move to arch/powerpc will also have a fair impact.

Cheers,
g.

--=20
Grant Likely, B.Sc. P.Eng.
Secret Lab Technologies Ltd.
grant.likely@secretlab.ca
(403) 399-0195

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Lite5200B I2S driver
  2006-12-13 12:56     ` Roman Fietze
@ 2006-12-18 13:47       ` Stefan Strobl
  2006-12-18 14:46         ` Roman Fietze
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Strobl @ 2006-12-18 13:47 UTC (permalink / raw)
  To: linuxppc-embedded

Hello Roman

I'm interested in your code also though I haven't had a chance to look 
at it yet. I am also having a MPC5200 based board running Wolfgang's 
kernel 2.4.25.

Roman Fietze wrote:
 > The driver uses some modified bestcomm code, because the original
 > bestcomm code from Freescale is, ahem, not very well designed, one of
 > the reasons for the new design in the 2.6. The changes were done in
 > the code image 2. They are a more generic approach for the PSC tasks,
 > so I can more easily switch PSC's. You should easily be able to modify
 > the kernel part of the driver, to use the bestcomm code from
 > Wolfgang's 2.4.25 git kernel tree. If not, please contact me and I
 > will send you the sources of my bestcomm/ subdirectory, or anybody
 > else of course.

What I am wondering is, how many DMA tasks do you run besides Audio?
I am already using DMA for IDE and FEC so Audio would be my third DMA 
task. I remember that it used to be difficult to get more than two tasks 
running at the same time.

Would you send me or post your bestcomm sources since I'm not that 
proficient in kernel programming.

Thanks
Stefan

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Lite5200B I2S driver
  2006-12-18 13:47       ` Stefan Strobl
@ 2006-12-18 14:46         ` Roman Fietze
  2006-12-18 15:10           ` Stefan Strobl
  0 siblings, 1 reply; 13+ messages in thread
From: Roman Fietze @ 2006-12-18 14:46 UTC (permalink / raw)
  To: linuxppc-embedded

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

Hello Stefan,

On Monday 18 December 2006 14:47, Stefan Strobl wrote:

> What I am wondering is, how many DMA tasks do you run besides Audio?
> I am already using DMA for IDE and FEC so Audio would be my third
> DMA task. I remember that it used to be difficult to get more than
> two tasks running at the same time.

As of my curret knowledge it is definitely a problem to run IDE-DMA
together with any access to the LocalPlus on the old B2 mask of the
MPC5200. We did not have problems using the BestComm concurrently for
IDE, FEC and the PSC (I2S+SPI codec mode) on either mask.


> Would you send me or post your bestcomm sources since I'm not that
> proficient in kernel programming.

No problem, I will mail you the same tar fiel I sent to Wolfgang. This
includes the BestComm code and once more the drivers. It's derived
work and GPL'd.


Roman

-- 
Roman Fietze              Telemotive AG Büro Mühlhausen
Breitwiesen                            73347 Mühlhausen
Tel.: +49(0)7335/18493-45      http://www.telemotive.de


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

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Lite5200B I2S driver
  2006-12-18 14:46         ` Roman Fietze
@ 2006-12-18 15:10           ` Stefan Strobl
  0 siblings, 0 replies; 13+ messages in thread
From: Stefan Strobl @ 2006-12-18 15:10 UTC (permalink / raw)
  To: linuxppc-embedded

Hello Roman

Roman Fietze wrote:
> As of my curret knowledge it is definitely a problem to run IDE-DMA
> together with any access to the LocalPlus on the old B2 mask of the
> MPC5200. We did not have problems using the BestComm concurrently for
> IDE, FEC and the PSC (I2S+SPI codec mode) on either mask.

That's good news for me.

> No problem, I will mail you the same tar fiel I sent to Wolfgang. This
> includes the BestComm code and once more the drivers. It's derived
> work and GPL'd.

Thanks a lot. I'm looking forward to trying that out. I'll send you a 
note once I've got it run, but it'll take some time until I have a 
hardware to work on.

Stefan

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2006-12-18 15:15 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-12-12 15:47 Lite5200B I2S driver Pedro Luis D. L.
2006-12-12 19:22 ` Sylvain Munaut
2006-12-13  9:31   ` Pedro Luis D. L.
2006-12-13  9:10 ` Roman Fietze
2006-12-13  9:39   ` Pedro Luis D. L.
2006-12-13 12:56     ` Roman Fietze
2006-12-18 13:47       ` Stefan Strobl
2006-12-18 14:46         ` Roman Fietze
2006-12-18 15:10           ` Stefan Strobl
2006-12-13 12:58     ` Roman Fietze
2006-12-13 13:15     ` Roman Fietze
2006-12-13 14:37       ` Pedro Luis D. L.
2006-12-17  8:03 ` Grant Likely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).