* Re: 2.6.23-rc2-mm2 build error on MIPS and ARM
From: Sam Ravnborg @ 2007-08-11 11:35 UTC (permalink / raw)
To: Martin Schwidefsky
Cc: linuxppc-dev, Andrew Morton, Mathieu Desnoyers, linux-kernel
In-Reply-To: <1186829697.3930.4.camel@localhost>
On Sat, Aug 11, 2007 at 12:54:57PM +0200, Martin Schwidefsky wrote:
> On Sat, 2007-08-11 at 11:56 +0200, Sam Ravnborg wrote:
> > > That attached patch should fix it for arm and mips. I'll try a few more
> > > architectures until I'm fed up with compiling cross-compilers..
> > http://userweb.kernel.org/~akpm/cross-compilers/
>
> Thanks for the hint but I prefer to compile them on my own (naming
> scheme, installation target, 4.1.2, ..). The first one is the hardest,
> for the following ones it is just a matter how fast you machine is.
> I'll do one cross-compiler every 15 minutes right now (without glibc).
Should dive into this myself one day.
But for now crosstool has saved my day.
Sam
^ permalink raw reply
* my kernel start info prompt: "Badness at arch/ppc/kernel/dma-mapping.c:345", inside the mail is the detail
From: poorbeyond @ 2007-08-11 14:13 UTC (permalink / raw)
To: Linux-ppc mail list
[-- Attachment #1: Type: text/plain, Size: 3628 bytes --]
my cpu is mpc860, kernel is 2.6.20.14, use arch/ppc.
the attachment is my kernel config file, is something wrong?
the following is kernel start info:
^_^=>bootm 300000
## Booting image at 00300000 ...
Image Name: Linux-2.6.20.14
Image Type: PowerPC Linux Kernel Image (gzip compressed)
Data Size: 840127 Bytes = 820.4 kB
Load Address: 00000000
Entry Point: 00000000
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK
## Current stack ends at 0x01D5DB10 => set upper limit to 0x00800000
No initrd
## Transferring control to Linux (at address 00000000) ...
bootm1
Linux version 2.6.20.14 (root@localhost.localdomain) (gcc version 4.0.0 (DENX ELDK 4.0 4.0.0)) #1 PREEMPT Sat Aug 11 19:58:59 CST 2007
I2C/SPI/SMC1 microcode patch installed.
Zone PFN ranges:
DMA 0 -> 2048
Normal 2048 -> 2048
early_node_map[1] active PFN ranges
0: 0 -> 2048
Built 1 zonelists. Total pages: 2032
Kernel command line:
PID hash table entries: 32 (order: 5, 128 bytes)
Decrementer Frequency = 187500000/60
Warning: real time clock seems stuck!
cpm_uart: console: compat mode
Dentry cache hash table entries: 1024 (order: 0, 4096 bytes)
Inode-cache hash table entries: 1024 (order: 0, 4096 bytes)
Memory: 6220k available (1264k kernel code, 536k data, 84k init, 0k highmem)
Mount-cache hash table entries: 512
Zq001: start_kernel
------------[ cut here ]------------
Badness at arch/ppc/kernel/dma-mapping.c:345
Call Trace:
[00205E60] [00008DB0] (unreliable)
[00205E90] [000A38D8]
[00205EA0] [00003D28]
[00205ED0] [0000308C]
[00205F90] [001A4600]
[00205FA0] [000022DC]
[00205FF0] [00004B40]
NET: Registered protocol family 16
Generic PHY: Registered new driver
NET: Registered protocol family 2
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 256 (order: 0, 4096 bytes)
TCP bind hash table entries: 128 (order: -1, 2560 bytes)
TCP: Hash tables configured (established 256 bind 128)
TCP reno registered
audit: initializing netlink socket (disabled)
audit(4294836223.222:1): initialized
audit: cannot initialize inotify handle
io scheduler noop registered (default)
Serial: CPM driver $Revision: 0.02 $
cpm_uart: WARNING: no UART devices found on platform bus!
cpm_uart: the driver will guess configuration, but this mode is no longer supported.
ttyCPM0 at MMIO 0xff000a80 (irq = 20) is a CPM UART
RAMDISK driver initialized: 16 RAM disks of 16384K size 1024 blocksize
LXT970: Registered new driver
LXT971: Registered new driver
fs_enet.c:v1.0 (Aug 8, 2005)
------------[ cut here ]------------
kernel BUG at arch/ppc/kernel/dma-mapping.c:233!
Oops: Exception in kernel mode, sig: 5 [#1]
PREEMPT
NIP: 00005644 LR: 000055F8 CTR: 00000000
REGS: 00205e90 TRAP: 0700 Not tainted (2.6.20.14)
MSR: 00029032 <EE,ME,IR,DR> CR: 55222253 XER: A000247F
TASK = 00202ba0[1] 'swapper' THREAD: 00204000
GPR00: 00400165 00205F40 00202BA0 001DFBA4 00000000 00000BDA 0025BFFC 00000000
GPR08: 00162648 00000001 00401000 00400164 00000000 41082205 02000000 00000001
GPR16: 00000000 01FFFA54 FFFFFFFF 00000000 00800000 007FFF00 01FFAA14 00205F78
GPR24: 005FF000 00160000 00000000 001DFB80 00001000 001D8000 00223F60 001DFB60
Call Trace:
[00205F40] [000055C8] (unreliable)
[00205F70] [001A51C4]
[00205FA0] [000022DC]
[00205FF0] [00004B40]
Instruction dump:
38632658 481348c5 7fc3f378 480521fd 7fe3fb78 7f44d378 48042929 48000078
801d0000 5400066e 3160ffff 7d2b0110 <0f090000> 38000400 7d20f828 7d290378
Kernel panic - not syncing: Attempted to kill init!
<0>Rebooting in 180 seconds..
poorbeyond
2007-08-11
[-- Attachment #2: Type: text/html, Size: 5253 bytes --]
^ permalink raw reply
* Re: Is it possible to move Kernel Virtual Space to 0xE0000000
From: John Reiser @ 2007-08-11 15:22 UTC (permalink / raw)
To: linuxppc-embedded
In-Reply-To: <f807f08a0708110414p2b9fc7c0s94dd9d10214296d5@mail.gmail.com>
> Giving Kernel virtual space to 0xD0000000 and it boots without any problems,
> however when I increase it more to 0xE0000000 it throws Program Exception. I
> will be glad if someone helps me out with refernces for Linux Porting on
> MPCs.
Search for:
linux "4G/4G split"
This work was active a couple years ago on x86. The kernel patches
are not trivial, and they cost about 15% in performance of system calls.
But they do provide almost a full 32-bits of address space for user mode.
The problem of "not enough user address space" also is solvable with
money. Get a 64-bit machine. It costs more per board but saves
development time, so costs less in the long run unless you are making
more than 10,000 systems.
--
^ permalink raw reply
* Re: [PATCHv2 1/2] Xilinx ML403 AC97 Controller Reference device driver
From: Joachim Förster @ 2007-08-11 16:23 UTC (permalink / raw)
To: Takashi Iwai
Cc: alsa-devel, Stephen Neuendorffer, Lorenz Kolb, linuxppc-embedded
In-Reply-To: <s5hsl6sws0s.wl%tiwai@suse.de>
Hi Takashi, Grant, Stephen,
I tried to correct the issues you pointed out and below you'll find a
new version of my patch. Maybe important changes:
- I moved to sound/drivers, so there will be less problems as soon as
somebody confirms that it works on MicroBlaze, too.
- I removed the inline stuff from pcm-indirect2.h and split it into a .c
file and a .h file.
@Stephen: Wolfgang Reissnegger's changes don't exist in any
"(un)official" branches, so I thought I stay with XILINX_VIRTEX -
anyway, it's a very small change to make - so we might do this later. Or
did I overlook something?
From: Joachim Foerster <JOFT@gmx.de>
Add ALSA support for the opb_ac97_controller_ref_v1_00_a ip core found
in Xilinx' ML403 reference design.
Known issue: Currently this driver hits a WARN_ON_ONCE(1) statement in
kernel/irq/resend.c (line 70). According to Linus
(http://lkml.org/lkml/2007/8/5/5) this may be ignored, right? I haven't
had a look into this "problem" yet.
(Patch for Linus' master branch, date 2007/08/08)
This patchset _will_ be published on
http://www.esic-solutions.com/support.html soon (like the first version
of the driver (tar file), but this may take some time ...).
Signed-off-by: Joachim Foerster <JOFT@gmx.de>
---
sound/drivers/Kconfig | 12 +
sound/drivers/Makefile | 2 +
sound/drivers/ml403-ac97cr.c | 1353 +++++++++++++++++++++++++++++++++++++++++
sound/drivers/pcm-indirect2.c | 591 ++++++++++++++++++
sound/drivers/pcm-indirect2.h | 140 +++++
5 files changed, 2098 insertions(+), 0 deletions(-)
create mode 100644 sound/drivers/ml403-ac97cr.c
create mode 100644 sound/drivers/pcm-indirect2.c
create mode 100644 sound/drivers/pcm-indirect2.h
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 83529b0..6716adf 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -120,4 +120,16 @@ config SND_PORTMAN2X4
To compile this driver as a module, choose M here: the module
will be called snd-portman2x4.
+config SND_ML403_AC97CR
+ tristate "Xilinx ML403 AC97 Controller Reference"
+ depends on SND && XILINX_VIRTEX
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for the
+ opb_ac97_controller_ref_v1_00_a ip core found in Xilinx' ML403
+ reference design.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-ml403_ac97cr.
+
endmenu
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index 0411264..c5a9471 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -9,6 +9,7 @@ snd-mts64-objs := mts64.o
snd-portman2x4-objs := portman2x4.o
snd-serial-u16550-objs := serial-u16550.o
snd-virmidi-objs := virmidi.o
+snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
@@ -17,5 +18,6 @@ obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
obj-$(CONFIG_SND_MTS64) += snd-mts64.o
obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
+obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o
obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/
diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c
new file mode 100644
index 0000000..2222315
--- /dev/null
+++ b/sound/drivers/ml403-ac97cr.c
@@ -0,0 +1,1353 @@
+/*
+ * ALSA driver for Xilinx ML403 AC97 Controller Reference
+ * IP: opb_ac97_controller_ref_v1_00_a (EDK 8.1i)
+ * IP: opb_ac97_controller_ref_v1_00_a (EDK 9.1i)
+ *
+ * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Some notes / status of this driver:
+ *
+ * - Don't wonder about some strange implementations of things - especially the
+ * (heavy) shadowing of codec registers, with which I tried to reduce read
+ * accesses to a minimum, because after a variable amount of accesses, the AC97
+ * controller doesn't raise the register access finished bit anymore ...
+ *
+ * - Capture support works - basically, but after ~30s (with rates > ~20kHz)
+ * ALSA stops reading captured samples from the intermediate buffer and
+ * therefore a overrun happens - ATM I don't know what's wrong.
+ *
+ * - Playback support seems to be pretty stable - no issues here.
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+
+#include <linux/platform_device.h>
+
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+
+/* HZ */
+#include <linux/param.h>
+/* jiffies, time_*() */
+#include <linux/jiffies.h>
+/* schedule_timeout*() */
+#include <linux/sched.h>
+/* spin_lock*() */
+#include <linux/spinlock.h>
+/* struct mutex, mutex_init(), mutex_*lock() */
+#include <linux/mutex.h>
+
+/* snd_printk(), snd_printd() */
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/ac97_codec.h>
+
+#include "pcm-indirect2.h"
+
+
+#define SND_ML403_AC97CR_DRIVER "ml403-ac97cr"
+
+MODULE_AUTHOR("Joachim Foerster <JOFT@gmx.de>");
+MODULE_DESCRIPTION("Xilinx ML403 AC97 Controller Reference");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Xilinx,ML403 AC97 Controller Reference}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for ML403 AC97 Controller Reference.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for ML403 AC97 Controller Reference.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable this ML403 AC97 Controller Reference.");
+
+/* Special feature options */
+/*#define CODEC_WRITE_CHECK_RAF*/ /* don't return after a write to a codec
+ * register, while RAF bit is not set
+ */
+/* Debug options for code which may be removed completely in a final version */
+#ifdef CONFIG_SND_DEBUG
+/*#define CODEC_STAT*/ /* turn on some minimal "statistics"
+ * about codec register usage
+ */
+#define SND_PCM_INDIRECT2_STAT /* turn on some "statistics" about the
+ * process of copying bytes from the
+ * intermediate buffer to the hardware
+ * fifo and the other way round
+ */
+#endif
+
+/* Definition of a "level/facility dependent" printk(); may be removed
+ * completely in a final version
+ */
+#undef PDEBUG
+#ifdef CONFIG_SND_DEBUG
+/* "facilities" for PDEBUG */
+#define UNKNOWN (1<<0)
+#define CODEC_SUCCESS (1<<1)
+#define CODEC_FAKE (1<<2)
+#define INIT_INFO (1<<3)
+#define INIT_FAILURE (1<<4)
+#define WORK_INFO (1<<5)
+#define WORK_FAILURE (1<<6)
+
+#define PDEBUG_FACILITIES (UNKNOWN | INIT_FAILURE | WORK_FAILURE)
+
+#define PDEBUG(fac, fmt, args...) do { \
+ if (fac & PDEBUG_FACILITIES) \
+ snd_printd(KERN_DEBUG SND_ML403_AC97CR_DRIVER ": " \
+ fmt, ##args); \
+ } while (0)
+#else
+#define PDEBUG(fac, fmt, args...) /* nothing */
+#endif
+
+
+
+/* Defines for "waits"/timeouts (portions of HZ=250 on arch/ppc by default) */
+#define CODEC_TIMEOUT_ON_INIT 5 /* timeout for checking for codec
+ * readiness (after insmod)
+ */
+#ifndef CODEC_WRITE_CHECK_RAF
+#define CODEC_WAIT_AFTER_WRITE 100 /* general, static wait after a write
+ * access to a codec register, may be
+ * 0 to completely remove wait
+ */
+#else
+#define CODEC_TIMEOUT_AFTER_WRITE 5 /* timeout after a write access to a
+ * codec register, if RAF bit is used
+ */
+#endif
+#define CODEC_TIMEOUT_AFTER_READ 5 /* timeout after a read access to a
+ * codec register (checking RAF bit)
+ */
+
+/* Infrastructure for codec register shadowing */
+#define LM4550_REG_OK (1<<0) /* register exists */
+#define LM4550_REG_DONEREAD (1<<1) /* read register once, value should be
+ * the same currently in the register
+ */
+#define LM4550_REG_NOSAVE (1<<2) /* values written to this register will
+ * not be saved in the register
+ */
+#define LM4550_REG_NOSHADOW (1<<3) /* don't do register shadowing, use plain
+ * hardware access
+ */
+#define LM4550_REG_READONLY (1<<4) /* register is read only */
+#define LM4550_REG_FAKEPROBE (1<<5) /* fake write _and_ read actions during
+ * probe() correctly
+ */
+#define LM4550_REG_FAKEREAD (1<<6) /* fake read access, always return
+ * default value
+ */
+#define LM4550_REG_ALLFAKE (LM4550_REG_FAKEREAD | LM4550_REG_FAKEPROBE)
+
+struct lm4550_reg {
+ u16 value;
+ u16 flag;
+ u16 wmask;
+ u16 def;
+};
+
+struct lm4550_reg lm4550_regfile[64] = {
+ [AC97_RESET / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_NOSAVE \
+ | LM4550_REG_FAKEREAD,
+ .def = 0x0D50},
+ [AC97_MASTER / 2] = {.flag = LM4550_REG_OK
+ | LM4550_REG_FAKEPROBE,
+ .wmask = 0x9F1F,
+ .def = 0x8000},
+ [AC97_HEADPHONE / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .wmask = 0x9F1F,
+ .def = 0x8000},
+ [AC97_MASTER_MONO / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .wmask = 0x801F,
+ .def = 0x8000},
+ [AC97_PC_BEEP / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .wmask = 0x801E,
+ .def = 0x0},
+ [AC97_PHONE / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .wmask = 0x801F,
+ .def = 0x8008},
+ [AC97_MIC / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .wmask = 0x805F,
+ .def = 0x8008},
+ [AC97_LINE / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .wmask = 0x9F1F,
+ .def = 0x8808},
+ [AC97_CD / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .wmask = 0x9F1F,
+ .def = 0x8808},
+ [AC97_VIDEO / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .wmask = 0x9F1F,
+ .def = 0x8808},
+ [AC97_AUX / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .wmask = 0x9F1F,
+ .def = 0x8808},
+ [AC97_PCM / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .wmask = 0x9F1F,
+ .def = 0x8008},
+ [AC97_REC_SEL / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .wmask = 0x707,
+ .def = 0x0},
+ [AC97_REC_GAIN / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .wmask = 0x8F0F,
+ .def = 0x8000},
+ [AC97_GENERAL_PURPOSE / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .def = 0x0,
+ .wmask = 0xA380},
+ [AC97_3D_CONTROL / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEREAD \
+ | LM4550_REG_READONLY,
+ .def = 0x0101},
+ [AC97_POWERDOWN / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_NOSHADOW \
+ | LM4550_REG_NOSAVE,
+ .wmask = 0xFF00},
+ /* may not write ones to
+ * REF/ANL/DAC/ADC bits
+ * FIXME: Is this ok?
+ */
+ [AC97_EXTENDED_ID / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEREAD \
+ | LM4550_REG_READONLY,
+ .def = 0x0201}, /* primary codec */
+ [AC97_EXTENDED_STATUS / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_NOSHADOW \
+ | LM4550_REG_NOSAVE,
+ .wmask = 0x1},
+ [AC97_PCM_FRONT_DAC_RATE / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .def = 0xBB80,
+ .wmask = 0xFFFF},
+ [AC97_PCM_LR_ADC_RATE / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_FAKEPROBE,
+ .def = 0xBB80,
+ .wmask = 0xFFFF},
+ [AC97_VENDOR_ID1 / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_READONLY \
+ | LM4550_REG_FAKEREAD,
+ .def = 0x4E53},
+ [AC97_VENDOR_ID2 / 2] = {.flag = LM4550_REG_OK \
+ | LM4550_REG_READONLY \
+ | LM4550_REG_FAKEREAD,
+ .def = 0x4350}
+};
+
+#define LM4550_RF_OK(reg) (lm4550_regfile[reg / 2].flag & LM4550_REG_OK)
+
+static void lm4550_regfile_init(void)
+{
+ int i;
+ for (i = 0; i < 64; i++)
+ if (lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE)
+ lm4550_regfile[i].value = lm4550_regfile[i].def;
+}
+
+static void lm4550_regfile_write_values_after_init(struct snd_ac97 *ac97)
+{
+ int i;
+ for (i = 0; i < 64; i++)
+ if ((lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE) &&
+ (lm4550_regfile[i].value != lm4550_regfile[i].def)) {
+ PDEBUG(CODEC_FAKE, "lm4550_regfile_write_values_after_"
+ "init(): reg=0x%x value=0x%x / %d is different "
+ "from def=0x%x / %d\n",
+ i, lm4550_regfile[i].value,
+ lm4550_regfile[i].value, lm4550_regfile[i].def,
+ lm4550_regfile[i].def);
+ snd_ac97_write(ac97, i * 2, lm4550_regfile[i].value);
+ lm4550_regfile[i].flag |= LM4550_REG_DONEREAD;
+ }
+}
+
+
+/* direct registers */
+#define CR_REG(ml403_ac97cr, x) ((ml403_ac97cr)->port + CR_REG_##x)
+
+#define CR_REG_PLAYFIFO 0x00
+#define CR_PLAYDATA(a) ((a) & 0xFFFF)
+
+#define CR_REG_RECFIFO 0x04
+#define CR_RECDATA(a) ((a) & 0xFFFF)
+
+#define CR_REG_STATUS 0x08
+#define CR_RECOVER (1<<7)
+#define CR_PLAYUNDER (1<<6)
+#define CR_CODECREADY (1<<5)
+#define CR_RAF (1<<4)
+#define CR_RECEMPTY (1<<3)
+#define CR_RECFULL (1<<2)
+#define CR_PLAYHALF (1<<1)
+#define CR_PLAYFULL (1<<0)
+
+#define CR_REG_RESETFIFO 0x0C
+#define CR_RECRESET (1<<1)
+#define CR_PLAYRESET (1<<0)
+
+#define CR_REG_CODEC_ADDR 0x10
+/* UG082 says:
+ * #define CR_CODEC_ADDR(a) ((a) << 1)
+ * #define CR_CODEC_READ (1<<0)
+ * #define CR_CODEC_WRITE (0<<0)
+ */
+/* RefDesign example says: */
+#define CR_CODEC_ADDR(a) ((a) << 0)
+#define CR_CODEC_READ (1<<7)
+#define CR_CODEC_WRITE (0<<7)
+
+#define CR_REG_CODEC_DATAREAD 0x14
+#define CR_CODEC_DATAREAD(v) ((v) & 0xFFFF)
+
+#define CR_REG_CODEC_DATAWRITE 0x18
+#define CR_CODEC_DATAWRITE(v) ((v) & 0xFFFF)
+
+#define CR_FIFO_SIZE 32
+
+struct snd_ml403_ac97cr {
+ /* lock for access to (controller) registers */
+ spinlock_t reg_lock;
+ /* mutex for the whole sequence of accesses to (controller) registers
+ * which affect codec registers
+ */
+ struct mutex cdc_mutex;
+
+ int irq; /* for playback */
+ int enable_irq; /* for playback */
+
+ int capture_irq;
+ int enable_capture_irq;
+
+ struct resource *res_port;
+ void *port;
+
+ struct snd_ac97 *ac97;
+ int ac97_fake;
+#ifdef CODEC_STAT
+ int ac97_read;
+ int ac97_write;
+#endif
+
+ struct platform_device *pfdev;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+ struct snd_pcm_indirect2 ind_rec; /* for playback */
+ struct snd_pcm_indirect2 capture_ind2_rec;
+};
+
+static struct snd_pcm_hardware snd_ml403_ac97cr_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_BE,
+ .rates = (SNDRV_PCM_RATE_CONTINUOUS |
+ SNDRV_PCM_RATE_8000_48000),
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = CR_FIFO_SIZE/2,
+ .period_bytes_max = (64*1024),
+ .periods_min = 2,
+ .periods_max = (128*1024)/(CR_FIFO_SIZE/2),
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware snd_ml403_ac97cr_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_BE,
+ .rates = (SNDRV_PCM_RATE_CONTINUOUS |
+ SNDRV_PCM_RATE_8000_48000),
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = CR_FIFO_SIZE/2,
+ .period_bytes_max = (64*1024),
+ .periods_min = 2,
+ .periods_max = (128*1024)/(CR_FIFO_SIZE/2),
+ .fifo_size = 0,
+};
+
+static size_t
+snd_ml403_ac97cr_playback_ind2_zero(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+ int copied_words = 0;
+ u32 full = 0;
+
+ ml403_ac97cr = snd_pcm_substream_chip(substream);
+
+ spin_lock(&ml403_ac97cr->reg_lock);
+ while ((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
+ CR_PLAYFULL)) != CR_PLAYFULL) {
+ out_be32(CR_REG(ml403_ac97cr, PLAYFIFO), 0);
+ copied_words++;
+ }
+ rec->hw_ready = 0;
+ spin_unlock(&ml403_ac97cr->reg_lock);
+
+ return (size_t) (copied_words * 2);
+}
+
+static size_t
+snd_ml403_ac97cr_playback_ind2_copy(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec,
+ size_t bytes)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+ u16 *src;
+ int copied_words = 0;
+ u32 full = 0;
+
+ ml403_ac97cr = snd_pcm_substream_chip(substream);
+ src = (u16 *)(substream->runtime->dma_area + rec->sw_data);
+
+ spin_lock(&ml403_ac97cr->reg_lock);
+ while (((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
+ CR_PLAYFULL)) != CR_PLAYFULL) && (bytes > 1)) {
+ out_be32(CR_REG(ml403_ac97cr, PLAYFIFO),
+ CR_PLAYDATA(src[copied_words]));
+ copied_words++;
+ bytes = bytes - 2;
+ }
+ if (full != CR_PLAYFULL)
+ rec->hw_ready = 1;
+ else
+ rec->hw_ready = 0;
+ spin_unlock(&ml403_ac97cr->reg_lock);
+
+ return (size_t) (copied_words * 2);
+}
+
+static size_t
+snd_ml403_ac97cr_capture_ind2_null(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+ int copied_words = 0;
+ u32 empty = 0;
+
+ ml403_ac97cr = snd_pcm_substream_chip(substream);
+
+ spin_lock(&ml403_ac97cr->reg_lock);
+ while ((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
+ CR_RECEMPTY)) != CR_RECEMPTY) {
+ volatile u32 trash;
+
+ trash = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr, RECFIFO)));
+ /* Hmmmm, really necessary? Don't want call to in_be32()
+ * to be optimised away!
+ */
+ trash++;
+ copied_words++;
+ }
+ rec->hw_ready = 0;
+ spin_unlock(&ml403_ac97cr->reg_lock);
+
+ return (size_t) (copied_words * 2);
+}
+
+static size_t
+snd_ml403_ac97cr_capture_ind2_copy(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec, size_t bytes)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+ u16 *dst;
+ int copied_words = 0;
+ u32 empty = 0;
+
+ ml403_ac97cr = snd_pcm_substream_chip(substream);
+ dst = (u16 *)(substream->runtime->dma_area + rec->sw_data);
+
+ spin_lock(&ml403_ac97cr->reg_lock);
+ while (((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
+ CR_RECEMPTY)) != CR_RECEMPTY) && (bytes > 1)) {
+ dst[copied_words] = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr,
+ RECFIFO)));
+ copied_words++;
+ bytes = bytes - 2;
+ }
+ if (empty != CR_RECEMPTY)
+ rec->hw_ready = 1;
+ else
+ rec->hw_ready = 0;
+ spin_unlock(&ml403_ac97cr->reg_lock);
+
+ return (size_t) (copied_words * 2);
+}
+
+static snd_pcm_uframes_t
+snd_ml403_ac97cr_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+ struct snd_pcm_indirect2 *ind2_rec = NULL;
+
+ ml403_ac97cr = snd_pcm_substream_chip(substream);
+
+ if (substream == ml403_ac97cr->playback_substream)
+ ind2_rec = &ml403_ac97cr->ind_rec;
+ if (substream == ml403_ac97cr->capture_substream)
+ ind2_rec = &ml403_ac97cr->capture_ind2_rec;
+
+ if (ind2_rec != NULL)
+ return snd_pcm_indirect2_pointer(substream, ind2_rec);
+ return (snd_pcm_uframes_t) 0;
+}
+
+static int
+snd_ml403_ac97cr_pcm_playback_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+ int err = 0;
+
+ ml403_ac97cr = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ PDEBUG(WORK_INFO, "trigger(playback): START\n");
+ ml403_ac97cr->ind_rec.hw_ready = 1;
+
+ /* clear play FIFO */
+ out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_PLAYRESET);
+
+ /* enable play irq */
+ ml403_ac97cr->enable_irq = 1;
+ enable_irq(ml403_ac97cr->irq);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ PDEBUG(WORK_INFO, "trigger(playback): STOP\n");
+ ml403_ac97cr->ind_rec.hw_ready = 0;
+#ifdef SND_PCM_INDIRECT2_STAT
+ snd_pcm_indirect2_stat(substream, &ml403_ac97cr->ind_rec);
+#endif
+ /* disable play irq */
+ disable_irq_nosync(ml403_ac97cr->irq);
+ ml403_ac97cr->enable_irq = 0;
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ PDEBUG(WORK_INFO, "trigger(playback): (done)\n");
+ return err;
+}
+
+static int
+snd_ml403_ac97cr_pcm_capture_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+ int err = 0;
+
+ ml403_ac97cr = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ PDEBUG(WORK_INFO, "trigger(capture): START\n");
+ ml403_ac97cr->capture_ind2_rec.hw_ready = 0;
+
+ /* clear record FIFO */
+ out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_RECRESET);
+
+ /* enable record irq */
+ ml403_ac97cr->enable_capture_irq = 1;
+ enable_irq(ml403_ac97cr->capture_irq);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ PDEBUG(WORK_INFO, "trigger(capture): STOP\n");
+ ml403_ac97cr->capture_ind2_rec.hw_ready = 0;
+#ifdef SND_PCM_INDIRECT2_STAT
+ snd_pcm_indirect2_stat(substream,
+ &ml403_ac97cr->capture_ind2_rec);
+#endif
+ /* disable capture irq */
+ disable_irq_nosync(ml403_ac97cr->capture_irq);
+ ml403_ac97cr->enable_capture_irq = 0;
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ PDEBUG(WORK_INFO, "trigger(capture): (done)\n");
+ return err;
+}
+
+static int
+snd_ml403_ac97cr_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+ struct snd_pcm_runtime *runtime;
+
+ ml403_ac97cr = snd_pcm_substream_chip(substream);
+ runtime = substream->runtime;
+
+ PDEBUG(WORK_INFO,
+ "prepare(): period_bytes=%d, minperiod_bytes=%d\n",
+ snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2);
+
+ /* set sampling rate */
+ snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_FRONT_DAC_RATE,
+ runtime->rate);
+ PDEBUG(WORK_INFO, "prepare(): rate=%d\n", runtime->rate);
+
+ /* init struct for intermediate buffer */
+ memset(&ml403_ac97cr->ind_rec, 0,
+ sizeof(struct snd_pcm_indirect2));
+ ml403_ac97cr->ind_rec.hw_buffer_size = CR_FIFO_SIZE;
+ ml403_ac97cr->ind_rec.sw_buffer_size =
+ snd_pcm_lib_buffer_bytes(substream);
+ ml403_ac97cr->ind_rec.min_periods = -1;
+ ml403_ac97cr->ind_rec.min_multiple =
+ snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2);
+ PDEBUG(WORK_INFO, "prepare(): hw_buffer_size=%d, "
+ "sw_buffer_size=%d, min_multiple=%d\n",
+ CR_FIFO_SIZE, ml403_ac97cr->ind_rec.sw_buffer_size,
+ ml403_ac97cr->ind_rec.min_multiple);
+ return 0;
+}
+
+static int
+snd_ml403_ac97cr_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+ struct snd_pcm_runtime *runtime;
+
+ ml403_ac97cr = snd_pcm_substream_chip(substream);
+ runtime = substream->runtime;
+
+ PDEBUG(WORK_INFO,
+ "prepare(capture): period_bytes=%d, minperiod_bytes=%d\n",
+ snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2);
+
+ /* set sampling rate */
+ snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_LR_ADC_RATE,
+ runtime->rate);
+ PDEBUG(WORK_INFO, "prepare(capture): rate=%d\n", runtime->rate);
+
+ /* init struct for intermediate buffer */
+ memset(&ml403_ac97cr->capture_ind2_rec, 0,
+ sizeof(struct snd_pcm_indirect2));
+ ml403_ac97cr->capture_ind2_rec.hw_buffer_size = CR_FIFO_SIZE;
+ ml403_ac97cr->capture_ind2_rec.sw_buffer_size =
+ snd_pcm_lib_buffer_bytes(substream);
+ ml403_ac97cr->capture_ind2_rec.min_multiple =
+ snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2);
+ PDEBUG(WORK_INFO, "prepare(capture): hw_buffer_size=%d, "
+ "sw_buffer_size=%d, min_multiple=%d\n", CR_FIFO_SIZE,
+ ml403_ac97cr->capture_ind2_rec.sw_buffer_size,
+ ml403_ac97cr->capture_ind2_rec.min_multiple);
+ return 0;
+}
+
+static int snd_ml403_ac97cr_hw_free(struct snd_pcm_substream *substream)
+{
+ PDEBUG(WORK_INFO, "hw_free()\n");
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int
+snd_ml403_ac97cr_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ PDEBUG(WORK_INFO, "hw_params(): desired buffer bytes=%d, desired "
+ "period bytes=%d\n",
+ params_buffer_bytes(hw_params), params_period_bytes(hw_params));
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int snd_ml403_ac97cr_playback_open(struct snd_pcm_substream *substream)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+ struct snd_pcm_runtime *runtime;
+
+ ml403_ac97cr = snd_pcm_substream_chip(substream);
+ runtime = substream->runtime;
+
+ PDEBUG(WORK_INFO, "open(playback)\n");
+ ml403_ac97cr->playback_substream = substream;
+ runtime->hw = snd_ml403_ac97cr_playback;
+
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ CR_FIFO_SIZE / 2);
+ return 0;
+}
+
+static int snd_ml403_ac97cr_capture_open(struct snd_pcm_substream *substream)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+ struct snd_pcm_runtime *runtime;
+
+ ml403_ac97cr = snd_pcm_substream_chip(substream);
+ runtime = substream->runtime;
+
+ PDEBUG(WORK_INFO, "open(capture)\n");
+ ml403_ac97cr->capture_substream = substream;
+ runtime->hw = snd_ml403_ac97cr_capture;
+
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ CR_FIFO_SIZE / 2);
+ return 0;
+}
+
+static int snd_ml403_ac97cr_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+
+ ml403_ac97cr = snd_pcm_substream_chip(substream);
+
+ PDEBUG(WORK_INFO, "close(playback)\n");
+ ml403_ac97cr->playback_substream = NULL;
+ return 0;
+}
+
+static int snd_ml403_ac97cr_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+
+ ml403_ac97cr = snd_pcm_substream_chip(substream);
+
+ PDEBUG(WORK_INFO, "close(capture)\n");
+ ml403_ac97cr->capture_substream = NULL;
+ return 0;
+}
+
+static struct snd_pcm_ops snd_ml403_ac97cr_playback_ops = {
+ .open = snd_ml403_ac97cr_playback_open,
+ .close = snd_ml403_ac97cr_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ml403_ac97cr_hw_params,
+ .hw_free = snd_ml403_ac97cr_hw_free,
+ .prepare = snd_ml403_ac97cr_pcm_playback_prepare,
+ .trigger = snd_ml403_ac97cr_pcm_playback_trigger,
+ .pointer = snd_ml403_ac97cr_pcm_pointer,
+};
+
+static struct snd_pcm_ops snd_ml403_ac97cr_capture_ops = {
+ .open = snd_ml403_ac97cr_capture_open,
+ .close = snd_ml403_ac97cr_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ml403_ac97cr_hw_params,
+ .hw_free = snd_ml403_ac97cr_hw_free,
+ .prepare = snd_ml403_ac97cr_pcm_capture_prepare,
+ .trigger = snd_ml403_ac97cr_pcm_capture_trigger,
+ .pointer = snd_ml403_ac97cr_pcm_pointer,
+};
+
+static irqreturn_t snd_ml403_ac97cr_irq(int irq, void *dev_id)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+ struct platform_device *pfdev;
+ int cmp_irq;
+
+ ml403_ac97cr = (struct snd_ml403_ac97cr *)dev_id;
+ if (ml403_ac97cr == NULL)
+ return IRQ_NONE;
+
+ pfdev = ml403_ac97cr->pfdev;
+
+ /* playback interrupt */
+ cmp_irq = platform_get_irq(pfdev, 0);
+ if (irq == cmp_irq) {
+ if (ml403_ac97cr->enable_irq)
+ snd_pcm_indirect2_playback_interrupt(
+ ml403_ac97cr->playback_substream,
+ &ml403_ac97cr->ind_rec,
+ snd_ml403_ac97cr_playback_ind2_copy,
+ snd_ml403_ac97cr_playback_ind2_zero);
+ else
+ goto __disable_irq;
+ } else {
+ /* record interrupt */
+ cmp_irq = platform_get_irq(pfdev, 1);
+ if (irq == cmp_irq) {
+ if (ml403_ac97cr->enable_capture_irq)
+ snd_pcm_indirect2_capture_interrupt(
+ ml403_ac97cr->capture_substream,
+ &ml403_ac97cr->capture_ind2_rec,
+ snd_ml403_ac97cr_capture_ind2_copy,
+ snd_ml403_ac97cr_capture_ind2_null);
+ else
+ goto __disable_irq;
+ } else
+ return IRQ_NONE;
+ }
+ return IRQ_HANDLED;
+
+__disable_irq:
+ PDEBUG(INIT_INFO, "irq(): irq %d is meant to be disabled! So, now try "
+ "to disable it _really_!\n", irq);
+ disable_irq_nosync(irq);
+ return IRQ_HANDLED;
+}
+
+static unsigned short
+snd_ml403_ac97cr_codec_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
+#ifdef CODEC_STAT
+ u32 stat;
+ u32 rafaccess = 0;
+#endif
+ unsigned long end_time;
+ u16 value = 0;
+
+ if (!LM4550_RF_OK(reg)) {
+ snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+ "access to unknown/unused codec register 0x%x "
+ "ignored!\n", reg);
+ return 0;
+ }
+ /* check if we can fake/answer this access from our shadow register */
+ if ((lm4550_regfile[reg / 2].flag &
+ (LM4550_REG_DONEREAD | LM4550_REG_ALLFAKE)) &&
+ !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) {
+ if (lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEREAD) {
+ PDEBUG(CODEC_FAKE, "codec_read(): faking read from "
+ "reg=0x%x, val=0x%x / %d\n",
+ reg, lm4550_regfile[reg / 2].def,
+ lm4550_regfile[reg / 2].def);
+ return lm4550_regfile[reg / 2].def;
+ } else if ((lm4550_regfile[reg / 2].flag &
+ LM4550_REG_FAKEPROBE) &&
+ ml403_ac97cr->ac97_fake) {
+ PDEBUG(CODEC_FAKE, "codec_read(): faking read from "
+ "reg=0x%x, val=0x%x / %d (probe)\n",
+ reg, lm4550_regfile[reg / 2].value,
+ lm4550_regfile[reg / 2].value);
+ return lm4550_regfile[reg / 2].value;
+ } else {
+#ifdef CODEC_STAT
+ PDEBUG(CODEC_FAKE, "codec_read(): read access "
+ "answered by shadow register 0x%x (value=0x%x "
+ "/ %d) (cw=%d cr=%d)\n",
+ reg, lm4550_regfile[reg / 2].value,
+ lm4550_regfile[reg / 2].value,
+ ml403_ac97cr->ac97_write,
+ ml403_ac97cr->ac97_read);
+#else
+ PDEBUG(CODEC_FAKE, "codec_read(): read access "
+ "answered by shadow register 0x%x (value=0x%x "
+ "/ %d)\n",
+ reg, lm4550_regfile[reg / 2].value,
+ lm4550_regfile[reg / 2].value);
+#endif
+ return lm4550_regfile[reg / 2].value;
+ }
+ }
+ /* if we are here, we _have_ to access the codec really, no faking */
+ if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0)
+ return 0;
+#ifdef CODEC_STAT
+ ml403_ac97cr->ac97_read++;
+#endif
+ spin_lock(&ml403_ac97cr->reg_lock);
+ out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR),
+ CR_CODEC_ADDR(reg) | CR_CODEC_READ);
+ spin_unlock(&ml403_ac97cr->reg_lock);
+ end_time = jiffies + (HZ / CODEC_TIMEOUT_AFTER_READ);
+ do {
+ spin_lock(&ml403_ac97cr->reg_lock);
+#ifdef CODEC_STAT
+ rafaccess++;
+ stat = in_be32(CR_REG(ml403_ac97cr, STATUS));
+ if ((stat & CR_RAF) == CR_RAF) {
+ value = CR_CODEC_DATAREAD(
+ in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
+ PDEBUG(CODEC_SUCCESS, "codec_read(): (done) reg=0x%x, "
+ "value=0x%x / %d (STATUS=0x%x)\n",
+ reg, value, value, stat);
+#else
+ if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) &
+ CR_RAF) == CR_RAF) {
+ value = CR_CODEC_DATAREAD(
+ in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
+ PDEBUG(CODEC_SUCCESS, "codec_read(): (done) "
+ "reg=0x%x, value=0x%x / %d\n",
+ reg, value, value);
+#endif
+ lm4550_regfile[reg / 2].value = value;
+ lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
+ spin_unlock(&ml403_ac97cr->reg_lock);
+ mutex_unlock(&ml403_ac97cr->cdc_mutex);
+ return value;
+ }
+ spin_unlock(&ml403_ac97cr->reg_lock);
+ schedule_timeout_uninterruptible(1);
+ } while (time_after(end_time, jiffies));
+ /* read the DATAREAD register anyway, see comment below */
+ spin_lock(&ml403_ac97cr->reg_lock);
+ value =
+ CR_CODEC_DATAREAD(in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
+ spin_unlock(&ml403_ac97cr->reg_lock);
+#ifdef CODEC_STAT
+ snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+ "timeout while codec read! "
+ "(reg=0x%x, last STATUS=0x%x, DATAREAD=0x%x / %d, %d) "
+ "(cw=%d, cr=%d)\n",
+ reg, stat, value, value, rafaccess,
+ ml403_ac97cr->ac97_write, ml403_ac97cr->ac97_read);
+#else
+ snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+ "timeout while codec read! "
+ "(reg=0x%x, DATAREAD=0x%x / %d)\n",
+ reg, value, value);
+#endif
+ /* BUG: This is PURE speculation! But after _most_ read timeouts the
+ * value in the register is ok!
+ */
+ lm4550_regfile[reg / 2].value = value;
+ lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
+ mutex_unlock(&ml403_ac97cr->cdc_mutex);
+ return value;
+}
+
+static void
+snd_ml403_ac97cr_codec_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
+
+#ifdef CODEC_STAT
+ u32 stat;
+ u32 rafaccess = 0;
+#endif
+#ifdef CODEC_WRITE_CHECK_RAF
+ unsigned long end_time;
+#endif
+
+ if (!LM4550_RF_OK(reg)) {
+ snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+ "access to unknown/unused codec register 0x%x "
+ "ignored!\n", reg);
+ return;
+ }
+ if (lm4550_regfile[reg / 2].flag & LM4550_REG_READONLY) {
+ snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+ "write access to read only codec register 0x%x "
+ "ignored!\n", reg);
+ return;
+ }
+ if ((val & lm4550_regfile[reg / 2].wmask) != val) {
+ snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+ "write access to codec register 0x%x "
+ "with bad value 0x%x / %d!\n",
+ reg, val, val);
+ val = val & lm4550_regfile[reg / 2].wmask;
+ }
+ if (((lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEPROBE) &&
+ ml403_ac97cr->ac97_fake) &&
+ !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) {
+ PDEBUG(CODEC_FAKE, "codec_write(): faking write to reg=0x%x, "
+ "val=0x%x / %d\n", reg, val, val);
+ lm4550_regfile[reg / 2].value = (val &
+ lm4550_regfile[reg / 2].wmask);
+ return;
+ }
+ if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0)
+ return;
+#ifdef CODEC_STAT
+ ml403_ac97cr->ac97_write++;
+#endif
+ spin_lock(&ml403_ac97cr->reg_lock);
+ out_be32(CR_REG(ml403_ac97cr, CODEC_DATAWRITE),
+ CR_CODEC_DATAWRITE(val));
+ out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR),
+ CR_CODEC_ADDR(reg) | CR_CODEC_WRITE);
+ spin_unlock(&ml403_ac97cr->reg_lock);
+#ifdef CODEC_WRITE_CHECK_RAF
+ /* check CR_CODEC_RAF bit to see if write access to register is done;
+ * loop until bit is set or timeout happens
+ */
+ end_time = jiffies + HZ / CODEC_TIMEOUT_AFTER_WRITE;
+ do {
+ spin_lock(&ml403_ac97cr->reg_lock);
+#ifdef CODEC_STAT
+ rafaccess++;
+ stat = in_be32(CR_REG(ml403_ac97cr, STATUS))
+ if ((stat & CR_RAF) == CR_RAF) {
+#else
+ if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) &
+ CR_RAF) == CR_RAF) {
+#endif
+ PDEBUG(CODEC_SUCCESS, "codec_write(): (done) "
+ "reg=0x%x, value=%d / 0x%x\n",
+ reg, val, val);
+ if (!(lm4550_regfile[reg / 2].flag &
+ LM4550_REG_NOSHADOW) &&
+ !(lm4550_regfile[reg / 2].flag &
+ LM4550_REG_NOSAVE))
+ lm4550_regfile[reg / 2].value = val;
+ lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
+ spin_unlock(&ml403_ac97cr->reg_lock);
+ mutex_unlock(&ml403_ac97cr->cdc_mutex);
+ return;
+ }
+ spin_unlock(&ml403_ac97cr->reg_lock);
+ schedule_timeout_uninterruptible(1);
+ } while (time_after(end_time, jiffies));
+#ifdef CODEC_STAT
+ snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+ "timeout while codec write "
+ "(reg=0x%x, val=0x%x / %d, last STATUS=0x%x, %d) "
+ "(cw=%d, cr=%d)\n",
+ reg, val, val, stat, rafaccess, ml403_ac97cr->ac97_write,
+ ml403_ac97cr->ac97_read);
+#else
+ snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+ "timeout while codec write (reg=0x%x, val=0x%x / %d)\n",
+ reg, val, val);
+#endif
+#else /* CODEC_WRITE_CHECK_RAF */
+#if CODEC_WAIT_AFTER_WRITE > 0
+ /* officially, in AC97 spec there is no possibility for a AC97
+ * controller to determine, if write access is done or not - so: How
+ * is Xilinx able to provide a RAF bit for write access?
+ * => very strange, thus just don't check RAF bit (compare with
+ * Xilinx's example app in EDK 8.1i) and wait
+ */
+ schedule_timeout_uninterruptible(HZ / CODEC_WAIT_AFTER_WRITE);
+#endif
+ PDEBUG(CODEC_SUCCESS, "codec_write(): (done) "
+ "reg=0x%x, value=%d / 0x%x (no RAF check)\n",
+ reg, val, val);
+#endif
+ mutex_unlock(&ml403_ac97cr->cdc_mutex);
+ return;
+}
+
+static int __devinit
+snd_ml403_ac97cr_chip_init(struct snd_ml403_ac97cr *ml403_ac97cr)
+{
+ unsigned long end_time;
+ PDEBUG(INIT_INFO, "chip_init():\n");
+ end_time = jiffies + HZ / CODEC_TIMEOUT_ON_INIT;
+ do {
+ if (in_be32(CR_REG(ml403_ac97cr, STATUS)) & CR_CODECREADY) {
+ /* clear both hardware FIFOs */
+ out_be32(CR_REG(ml403_ac97cr, RESETFIFO),
+ CR_RECRESET | CR_PLAYRESET);
+ PDEBUG(INIT_INFO, "chip_init(): (done)\n");
+ return 0;
+ }
+ schedule_timeout_uninterruptible(1);
+ } while (time_after(end_time, jiffies));
+ snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
+ "timeout while waiting for codec, "
+ "not ready!\n");
+ return -EBUSY;
+}
+
+static int snd_ml403_ac97cr_free(struct snd_ml403_ac97cr *ml403_ac97cr)
+{
+ PDEBUG(INIT_INFO, "free():\n");
+ /* irq release */
+ if (ml403_ac97cr->irq >= 0)
+ free_irq(ml403_ac97cr->irq, ml403_ac97cr);
+ if (ml403_ac97cr->capture_irq >= 0)
+ free_irq(ml403_ac97cr->capture_irq, ml403_ac97cr);
+ /* give back "port" */
+ if (ml403_ac97cr->port != NULL)
+ iounmap(ml403_ac97cr->port);
+ kfree(ml403_ac97cr);
+ PDEBUG(INIT_INFO, "free(): (done)\n");
+ return 0;
+}
+
+static int snd_ml403_ac97cr_dev_free(struct snd_device *snddev)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr = snddev->device_data;
+ PDEBUG(INIT_INFO, "dev_free():\n");
+ return snd_ml403_ac97cr_free(ml403_ac97cr);
+}
+
+static int __devinit
+snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev,
+ struct snd_ml403_ac97cr **rml403_ac97cr)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr;
+ int err;
+ static struct snd_device_ops ops = {
+ .dev_free = snd_ml403_ac97cr_dev_free,
+ };
+ struct resource *resource;
+ int irq;
+
+ *rml403_ac97cr = NULL;
+ ml403_ac97cr = kzalloc(sizeof(*ml403_ac97cr), GFP_KERNEL);
+ if (ml403_ac97cr == NULL)
+ return -ENOMEM;
+ spin_lock_init(&ml403_ac97cr->reg_lock);
+ mutex_init(&ml403_ac97cr->cdc_mutex);
+ ml403_ac97cr->card = card;
+ ml403_ac97cr->pfdev = pfdev;
+ ml403_ac97cr->irq = -1;
+ ml403_ac97cr->enable_irq = 0;
+ ml403_ac97cr->capture_irq = -1;
+ ml403_ac97cr->enable_capture_irq = 0;
+ ml403_ac97cr->port = NULL;
+ ml403_ac97cr->res_port = NULL;
+
+ PDEBUG(INIT_INFO, "Trying to reserve resources now ...\n");
+ resource = platform_get_resource(pfdev, IORESOURCE_MEM, 0);
+ /* get "port" */
+ ml403_ac97cr->port = ioremap_nocache(resource->start,
+ (resource->end) -
+ (resource->start) + 1);
+ if (ml403_ac97cr->port == NULL) {
+ snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
+ "unable to remap memory region (%x to %x)\n",
+ resource->start, resource->end);
+ snd_ml403_ac97cr_free(ml403_ac97cr);
+ return -EBUSY;
+ }
+ snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
+ "remap controller memory region to "
+ "0x%x done\n", (unsigned int)ml403_ac97cr->port);
+ /* get irq */
+ irq = platform_get_irq(pfdev, 0);
+ if (request_irq(irq, snd_ml403_ac97cr_irq, IRQF_DISABLED,
+ pfdev->dev.bus_id, (void *)ml403_ac97cr)) {
+ snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
+ "unable to grab IRQ %d\n",
+ irq);
+ snd_ml403_ac97cr_free(ml403_ac97cr);
+ return -EBUSY;
+ }
+ ml403_ac97cr->irq = irq;
+ snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
+ "request (playback) irq %d done\n",
+ ml403_ac97cr->irq);
+ irq = platform_get_irq(pfdev, 1);
+ if (request_irq(irq, snd_ml403_ac97cr_irq, IRQF_DISABLED,
+ pfdev->dev.bus_id, (void *)ml403_ac97cr)) {
+ snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
+ "unable to grab IRQ %d\n",
+ irq);
+ snd_ml403_ac97cr_free(ml403_ac97cr);
+ return -EBUSY;
+ }
+ ml403_ac97cr->capture_irq = irq;
+ snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
+ "request (capture) irq %d done\n",
+ ml403_ac97cr->capture_irq);
+
+ err = snd_ml403_ac97cr_chip_init(ml403_ac97cr);
+ if (err < 0) {
+ snd_ml403_ac97cr_free(ml403_ac97cr);
+ return err;
+ }
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ml403_ac97cr, &ops);
+ if (err < 0) {
+ PDEBUG(INIT_FAILURE, "probe(): snd_device_new() failed!\n");
+ snd_ml403_ac97cr_free(ml403_ac97cr);
+ return err;
+ }
+
+ snd_card_set_dev(card, &pfdev->dev);
+
+ *rml403_ac97cr = ml403_ac97cr;
+ return 0;
+}
+
+static void snd_ml403_ac97cr_mixer_free(struct snd_ac97 *ac97)
+{
+ struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
+ PDEBUG(INIT_INFO, "mixer_free():\n");
+ ml403_ac97cr->ac97 = NULL;
+ PDEBUG(INIT_INFO, "mixer_free(): (done)\n");
+}
+
+static int __devinit
+snd_ml403_ac97cr_mixer(struct snd_ml403_ac97cr *ml403_ac97cr)
+{
+ struct snd_ac97_bus *bus;
+ struct snd_ac97_template ac97;
+ int err;
+ static struct snd_ac97_bus_ops ops = {
+ .write = snd_ml403_ac97cr_codec_write,
+ .read = snd_ml403_ac97cr_codec_read,
+ };
+ PDEBUG(INIT_INFO, "mixer():\n");
+ err = snd_ac97_bus(ml403_ac97cr->card, 0, &ops, NULL, &bus);
+ if (err < 0)
+ return err;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ml403_ac97cr->ac97_fake = 1;
+ lm4550_regfile_init();
+#ifdef CODEC_STAT
+ ml403_ac97cr->ac97_read = 0;
+ ml403_ac97cr->ac97_write = 0;
+#endif
+ ac97.private_data = ml403_ac97cr;
+ ac97.private_free = snd_ml403_ac97cr_mixer_free;
+ ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM |
+ AC97_SCAP_NO_SPDIF;
+ err = snd_ac97_mixer(bus, &ac97, &ml403_ac97cr->ac97);
+ ml403_ac97cr->ac97_fake = 0;
+ lm4550_regfile_write_values_after_init(ml403_ac97cr->ac97);
+ PDEBUG(INIT_INFO, "mixer(): (done) snd_ac97_mixer()=%d\n", err);
+ return err;
+}
+
+static int __devinit
+snd_ml403_ac97cr_pcm(struct snd_ml403_ac97cr *ml403_ac97cr, int device,
+ struct snd_pcm **rpcm)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ err = snd_pcm_new(ml403_ac97cr->card, "ML403AC97CR/1", device, 1, 1,
+ &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_ml403_ac97cr_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_ml403_ac97cr_capture_ops);
+ pcm->private_data = ml403_ac97cr;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "ML403AC97CR DAC/ADC");
+ ml403_ac97cr->pcm = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL),
+ 64 * 1024,
+ 128 * 1024);
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+
+static int __devinit snd_ml403_ac97cr_probe(struct platform_device *pfdev)
+{
+ struct snd_card *card;
+ struct snd_ml403_ac97cr *ml403_ac97cr = NULL;
+ int err;
+ int dev = pfdev->id;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev])
+ return -ENOENT;
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+ err = snd_ml403_ac97cr_create(card, pfdev, &ml403_ac97cr);
+ if (err < 0) {
+ PDEBUG(INIT_FAILURE, "probe(): create failed!\n");
+ snd_card_free(card);
+ return err;
+ }
+ PDEBUG(INIT_INFO, "probe(): create done\n");
+ card->private_data = ml403_ac97cr;
+ err = snd_ml403_ac97cr_mixer(ml403_ac97cr);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ PDEBUG(INIT_INFO, "probe(): mixer done\n");
+ err = snd_ml403_ac97cr_pcm(ml403_ac97cr, 0, NULL);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ PDEBUG(INIT_INFO, "probe(): PCM done\n");
+ strcpy(card->driver, SND_ML403_AC97CR_DRIVER);
+ strcpy(card->shortname, "ML403 AC97 Controller Reference");
+ sprintf(card->longname, "%s %s at 0x%lx, irq %i & %i, device %i",
+ card->shortname, card->driver,
+ (unsigned long)ml403_ac97cr->port, ml403_ac97cr->irq,
+ ml403_ac97cr->capture_irq, dev + 1);
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ platform_set_drvdata(pfdev, card);
+ PDEBUG(INIT_INFO, "probe(): (done)\n");
+ return 0;
+}
+
+static int snd_ml403_ac97cr_remove(struct platform_device *pfdev)
+{
+ snd_card_free(platform_get_drvdata(pfdev));
+ platform_set_drvdata(pfdev, NULL);
+ return 0;
+}
+
+static struct platform_driver snd_ml403_ac97cr_driver = {
+ .probe = snd_ml403_ac97cr_probe,
+ .remove = snd_ml403_ac97cr_remove,
+ .driver = {
+ .name = SND_ML403_AC97CR_DRIVER,
+ },
+};
+
+static int __init alsa_card_ml403_ac97cr_init(void)
+{
+ return platform_driver_register(&snd_ml403_ac97cr_driver);
+}
+
+static void __exit alsa_card_ml403_ac97cr_exit(void)
+{
+ platform_driver_unregister(&snd_ml403_ac97cr_driver);
+}
+
+module_init(alsa_card_ml403_ac97cr_init)
+module_exit(alsa_card_ml403_ac97cr_exit)
diff --git a/sound/drivers/pcm-indirect2.c b/sound/drivers/pcm-indirect2.c
new file mode 100644
index 0000000..a36215a
--- /dev/null
+++ b/sound/drivers/pcm-indirect2.c
@@ -0,0 +1,591 @@
+/*
+ * Helper functions for indirect PCM data transfer to a simple FIFO in
+ * hardware (small, no possibility to read "hardware io position",
+ * updating position done by interrupt, ...)
+ *
+ * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de>
+ *
+ * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by
+ *
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ * Jaroslav Kysela <perex@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* #dependency of sound/core.h# */
+#include <sound/driver.h>
+/* snd_printk/d() */
+#include <sound/core.h>
+/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t
+ * snd_pcm_period_elapsed() */
+#include <sound/pcm.h>
+
+#include "pcm-indirect2.h"
+
+#ifdef SND_PCM_INDIRECT2_STAT
+/* jiffies */
+#include <linux/jiffies.h>
+
+void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int i;
+ int j;
+ int k;
+ int seconds = (rec->lastbytetime - rec->firstbytetime) / HZ;
+
+ snd_printk(KERN_DEBUG "STAT: mul_elapsed: %u, mul_elapsed_real: %d, "
+ "irq_occured: %d\n",
+ rec->mul_elapsed, rec->mul_elapsed_real, rec->irq_occured);
+ snd_printk(KERN_DEBUG "STAT: min_multiple: %d (irqs/period)\n",
+ rec->min_multiple);
+ snd_printk(KERN_DEBUG "STAT: firstbytetime: %lu, lastbytetime: %lu, "
+ "firstzerotime: %lu\n",
+ rec->firstbytetime, rec->lastbytetime, rec->firstzerotime);
+ snd_printk(KERN_DEBUG "STAT: bytes2hw: %u Bytes => (by runtime->rate) "
+ "length: %d s\n",
+ rec->bytes2hw, rec->bytes2hw / 2 / 2 / runtime->rate);
+ snd_printk(KERN_DEBUG "STAT: (by measurement) length: %d => "
+ "rate: %d Bytes/s = %d Frames/s|Hz\n",
+ seconds, rec->bytes2hw / seconds,
+ rec->bytes2hw / 2 / 2 / seconds);
+ snd_printk(KERN_DEBUG
+ "STAT: zeros2hw: %u = %d ms ~ %d * %d zero copies\n",
+ rec->zeros2hw, ((rec->zeros2hw / 2 / 2) * 1000) /
+ runtime->rate,
+ rec->zeros2hw / (rec->hw_buffer_size / 2),
+ (rec->hw_buffer_size / 2));
+ snd_printk(KERN_DEBUG "STAT: pointer_calls: %u, lastdifftime: %u\n",
+ rec->pointer_calls, rec->lastdifftime);
+ snd_printk(KERN_DEBUG "STAT: sw_io: %d, sw_data: %d\n", rec->sw_io,
+ rec->sw_data);
+ snd_printk(KERN_DEBUG "STAT: byte_sizes[]:\n");
+ k = 0;
+ for (j = 0; j < 8; j++) {
+ for (i = j * 8; i < (j + 1) * 8; i++)
+ if (rec->byte_sizes[i] != 0) {
+ snd_printk(KERN_DEBUG "%u: %u",
+ i, rec->byte_sizes[i]);
+ k++;
+ }
+ if (((k % 8) == 0) && (k != 0)) {
+ snd_printk(KERN_DEBUG "\n");
+ k = 0;
+ }
+ }
+ snd_printk(KERN_DEBUG "\n");
+ snd_printk(KERN_DEBUG "STAT: zero_sizes[]:\n");
+ for (j = 0; j < 8; j++) {
+ k = 0;
+ for (i = j * 8; i < (j + 1) * 8; i++)
+ if (rec->zero_sizes[i] != 0)
+ snd_printk(KERN_DEBUG "%u: %u",
+ i, rec->zero_sizes[i]);
+ else
+ k++;
+ if (!k)
+ snd_printk(KERN_DEBUG "\n");
+ }
+ snd_printk(KERN_DEBUG "\n");
+ snd_printk(KERN_DEBUG "STAT: min_adds[]:\n");
+ for (j = 0; j < 8; j++) {
+ if (rec->min_adds[j] != 0)
+ snd_printk(KERN_DEBUG "%u: %u", j, rec->min_adds[j]);
+ }
+ snd_printk(KERN_DEBUG "\n");
+ snd_printk(KERN_DEBUG "STAT: mul_adds[]:\n");
+ for (j = 0; j < 8; j++) {
+ if (rec->mul_adds[j] != 0)
+ snd_printk(KERN_DEBUG "%u: %u", j, rec->mul_adds[j]);
+ }
+ snd_printk(KERN_DEBUG "\n");
+ snd_printk(KERN_DEBUG
+ "STAT: zero_times_saved: %d, zero_times_notsaved: %d\n",
+ rec->zero_times_saved, rec->zero_times_notsaved);
+ /* snd_printk(KERN_DEBUG "STAT: zero_times[]\n");
+ i = 0;
+ for (j = 0; j < 3750; j++) {
+ if (rec->zero_times[j] != 0) {
+ snd_printk(KERN_DEBUG "%u: %u", j, rec->zero_times[j]);
+ i++;
+ }
+ if (((i % 8) == 0) && (i != 0))
+ snd_printk(KERN_DEBUG "\n");
+ }
+ snd_printk(KERN_DEBUG "\n"); */
+ return;
+}
+#endif
+
+/*
+ * _internal_ helper function for playback/capture transfer function
+ */
+static void
+snd_pcm_indirect2_increase_min_periods(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec,
+ int isplay, int iscopy,
+ unsigned int bytes)
+{
+ if (rec->min_periods >= 0) {
+ if (iscopy) {
+ rec->sw_io += bytes;
+ if (rec->sw_io >= rec->sw_buffer_size)
+ rec->sw_io -= rec->sw_buffer_size;
+ } else if (isplay) {
+ /* If application does not write data in multiples of
+ * a period, move sw_data to the next correctly aligned
+ * position, so that sw_io can converge to it (in the
+ * next step).
+ */
+ if (!rec->check_alignment) {
+ if (rec->bytes2hw %
+ snd_pcm_lib_period_bytes(substream)) {
+ unsigned bytes2hw_aligned =
+ (1 +
+ (rec->bytes2hw /
+ snd_pcm_lib_period_bytes
+ (substream))) *
+ snd_pcm_lib_period_bytes
+ (substream);
+ rec->sw_data =
+ bytes2hw_aligned %
+ rec->sw_buffer_size;
+#ifdef SND_PCM_INDIRECT2_STAT
+ snd_printk(KERN_DEBUG
+ "STAT: @re-align: aligned "
+ "bytes2hw to next period "
+ "size boundary: %d "
+ "(instead of %d)\n",
+ bytes2hw_aligned,
+ rec->bytes2hw);
+ snd_printk(KERN_DEBUG
+ "STAT: @re-align: sw_data "
+ "moves to: %d\n",
+ rec->sw_data);
+#endif
+ }
+ rec->check_alignment = 1;
+ }
+ /* We are at the end and are copying zeros into the
+ * fifo.
+ * Now, we have to make sure that sw_io is increased
+ * until the position of sw_data: Filling the fifo with
+ * the first zeros means, the last bytes were played.
+ */
+ if (rec->sw_io != rec->sw_data) {
+ unsigned int diff;
+ if (rec->sw_data > rec->sw_io)
+ diff = rec->sw_data - rec->sw_io;
+ else
+ diff = (rec->sw_buffer_size -
+ rec->sw_io) +
+ rec->sw_data;
+ if (bytes >= diff)
+ rec->sw_io = rec->sw_data;
+ else {
+ rec->sw_io += bytes;
+ if (rec->sw_io >= rec->sw_buffer_size)
+ rec->sw_io -=
+ rec->sw_buffer_size;
+ }
+ }
+ }
+ rec->min_period_count += bytes;
+ if (rec->min_period_count >= (rec->hw_buffer_size / 2)) {
+ rec->min_periods += (rec->min_period_count /
+ (rec->hw_buffer_size / 2));
+#ifdef SND_PCM_INDIRECT2_STAT
+ if ((rec->min_period_count /
+ (rec->hw_buffer_size / 2)) > 7)
+ snd_printk(KERN_DEBUG
+ "STAT: more than 7 (%d) min_adds "
+ "at once - too big to save!\n",
+ (rec->min_period_count /
+ (rec->hw_buffer_size / 2)));
+ else
+ rec->min_adds[(rec->min_period_count /
+ (rec->hw_buffer_size / 2))]++;
+#endif
+ rec->min_period_count = (rec->min_period_count %
+ (rec->hw_buffer_size / 2));
+ }
+ } else if (isplay && iscopy)
+ rec->min_periods = 0;
+}
+
+/*
+ * helper function for playback/capture pointer callback
+ */
+snd_pcm_uframes_t
+snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec)
+{
+#ifdef SND_PCM_INDIRECT2_STAT
+ rec->pointer_calls++;
+#endif
+ return bytes_to_frames(substream->runtime, rec->sw_io);
+}
+
+/*
+ * _internal_ helper function for playback interrupt callback
+ */
+static void
+snd_pcm_indirect2_playback_transfer(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec,
+ snd_pcm_indirect2_copy_t copy,
+ snd_pcm_indirect2_zero_t zero)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
+
+ /* runtime->control->appl_ptr: position where ALSA will write next time
+ * rec->appl_ptr: position where ALSA was last time
+ * diff: obviously ALSA wrote that much bytes into the intermediate
+ * buffer since we checked last time
+ */
+ snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
+
+ if (diff) {
+#ifdef SND_PCM_INDIRECT2_STAT
+ rec->lastdifftime = jiffies;
+#endif
+ if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
+ diff += runtime->boundary;
+ /* number of bytes "added" by ALSA increases the number of
+ * bytes which are ready to "be transfered to HW"/"played"
+ * Then, set rec->appl_ptr to not count bytes twice next time.
+ */
+ rec->sw_ready += (int)frames_to_bytes(runtime, diff);
+ rec->appl_ptr = appl_ptr;
+ }
+ if (rec->hw_ready && (rec->sw_ready <= 0)) {
+ unsigned int bytes;
+
+#ifdef SND_PCM_INDIRECT2_STAT
+ if (rec->firstzerotime == 0) {
+ rec->firstzerotime = jiffies;
+ snd_printk(KERN_DEBUG
+ "STAT: @firstzerotime: mul_elapsed: %d, "
+ "min_period_count: %d\n",
+ rec->mul_elapsed, rec->min_period_count);
+ snd_printk(KERN_DEBUG
+ "STAT: @firstzerotime: sw_io: %d, "
+ "sw_data: %d, appl_ptr: %u\n",
+ rec->sw_io, rec->sw_data,
+ (unsigned int)appl_ptr);
+ }
+ if ((jiffies - rec->firstzerotime) < 3750) {
+ rec->zero_times[(jiffies - rec->firstzerotime)]++;
+ rec->zero_times_saved++;
+ } else
+ rec->zero_times_notsaved++;
+#endif
+ bytes = zero(substream, rec);
+
+#ifdef SND_PCM_INDIRECT2_STAT
+ rec->zeros2hw += bytes;
+ if (bytes < 64)
+ rec->zero_sizes[bytes]++;
+ else
+ snd_printk(KERN_DEBUG
+ "STAT: %d zero Bytes copied to hardware at "
+ "once - too big to save!\n",
+ bytes);
+#endif
+ snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 0,
+ bytes);
+ return;
+ }
+ while (rec->hw_ready && (rec->sw_ready > 0)) {
+ /* sw_to_end: max. number of bytes that can be read/take from
+ * the current position (sw_data) in _one_ step
+ */
+ unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
+
+ /* bytes: number of bytes we have available (for reading) */
+ unsigned int bytes = rec->sw_ready;
+
+ if (sw_to_end < bytes)
+ bytes = sw_to_end;
+ if (!bytes)
+ break;
+
+#ifdef SND_PCM_INDIRECT2_STAT
+ if (rec->firstbytetime == 0)
+ rec->firstbytetime = jiffies;
+ rec->lastbytetime = jiffies;
+#endif
+ /* copy bytes from intermediate buffer position sw_data to the
+ * HW and return number of bytes actually written
+ * Furthermore, set hw_ready to 0, if the fifo isn't empty
+ * now => more could be transfered to fifo
+ */
+ bytes = copy(substream, rec, bytes);
+ rec->bytes2hw += bytes;
+
+#ifdef SND_PCM_INDIRECT2_STAT
+ if (bytes < 64)
+ rec->byte_sizes[bytes]++;
+ else
+ snd_printk(KERN_DEBUG
+ "STAT: %d Bytes copied to hardware at once "
+ "- too big to save!\n",
+ bytes);
+#endif
+ /* increase sw_data by the number of actually written bytes
+ * (= number of taken bytes from intermediate buffer)
+ */
+ rec->sw_data += bytes;
+ if (rec->sw_data == rec->sw_buffer_size)
+ rec->sw_data = 0;
+ /* now sw_data is the position where ALSA is going to write
+ * in the intermediate buffer next time = position we are going
+ * to read from next time
+ */
+
+ snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 1,
+ bytes);
+
+ /* we read bytes from intermediate buffer, so we need to say
+ * that the number of bytes ready for transfer are decreased
+ * now
+ */
+ rec->sw_ready -= bytes;
+ }
+ return;
+}
+
+/*
+ * helper function for playback interrupt routine
+ */
+void
+snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec,
+ snd_pcm_indirect2_copy_t copy,
+ snd_pcm_indirect2_zero_t zero)
+{
+#ifdef SND_PCM_INDIRECT2_STAT
+ rec->irq_occured++;
+#endif
+ /* hardware played some bytes, so there is room again (in fifo) */
+ rec->hw_ready = 1;
+
+ /* don't call ack() now, instead call transfer() function directly
+ * (normally called by ack() )
+ */
+ snd_pcm_indirect2_playback_transfer(substream, rec, copy, zero);
+
+ if (rec->min_periods >= rec->min_multiple) {
+#ifdef SND_PCM_INDIRECT2_STAT
+ if ((rec->min_periods / rec->min_multiple) > 7)
+ snd_printk(KERN_DEBUG
+ "STAT: more than 7 (%d) mul_adds - too big "
+ "to save!\n",
+ (rec->min_periods / rec->min_multiple));
+ else
+ rec->mul_adds[(rec->min_periods /
+ rec->min_multiple)]++;
+ rec->mul_elapsed_real += (rec->min_periods /
+ rec->min_multiple);
+ rec->mul_elapsed++;
+#endif
+ rec->min_periods = 0;
+ snd_pcm_period_elapsed(substream);
+ }
+}
+
+/*
+ * _internal_ helper function for capture interrupt callback
+ */
+static void
+snd_pcm_indirect2_capture_transfer(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec,
+ snd_pcm_indirect2_copy_t copy,
+ snd_pcm_indirect2_zero_t null)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
+ snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
+
+ if (diff) {
+#ifdef SND_PCM_INDIRECT2_STAT
+ rec->lastdifftime = jiffies;
+#endif
+ if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
+ diff += runtime->boundary;
+ rec->sw_ready -= frames_to_bytes(runtime, diff);
+ rec->appl_ptr = appl_ptr;
+ }
+ /* if hardware has something, but the intermediate buffer is full
+ * => skip contents of buffer
+ */
+ if (rec->hw_ready && (rec->sw_ready >= (int)rec->sw_buffer_size)) {
+ unsigned int bytes;
+
+#ifdef SND_PCM_INDIRECT2_STAT
+ if (rec->firstzerotime == 0) {
+ rec->firstzerotime = jiffies;
+ snd_printk(KERN_DEBUG "STAT: (capture) "
+ "@firstzerotime: mul_elapsed: %d, "
+ "min_period_count: %d\n",
+ rec->mul_elapsed, rec->min_period_count);
+ snd_printk(KERN_DEBUG "STAT: (capture) "
+ "@firstzerotime: sw_io: %d, sw_data: %d, "
+ "appl_ptr: %u\n",
+ rec->sw_io, rec->sw_data,
+ (unsigned int)appl_ptr);
+ }
+ if ((jiffies - rec->firstzerotime) < 3750) {
+ rec->zero_times[(jiffies - rec->firstzerotime)]++;
+ rec->zero_times_saved++;
+ } else
+ rec->zero_times_notsaved++;
+#endif
+ bytes = null(substream, rec);
+
+#ifdef SND_PCM_INDIRECT2_STAT
+ rec->zeros2hw += bytes;
+ if (bytes < 64)
+ rec->zero_sizes[bytes]++;
+ else
+ snd_printk(KERN_DEBUG
+ "STAT: (capture) %d zero Bytes copied to "
+ "hardware at once - too big to save!\n",
+ bytes);
+#endif
+ snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 0,
+ bytes);
+ /* report an overrun */
+ rec->sw_io = SNDRV_PCM_POS_XRUN;
+ return;
+ }
+ while (rec->hw_ready && (rec->sw_ready < (int)rec->sw_buffer_size)) {
+ /* sw_to_end: max. number of bytes that we can write to the
+ * intermediate buffer (until it's end)
+ */
+ size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
+
+ /* bytes: max. number of bytes, which may be copied to the
+ * intermediate buffer without overflow (in _one_ step)
+ */
+ size_t bytes = rec->sw_buffer_size - rec->sw_ready;
+
+ /* limit number of bytes (for transfer) by available room in
+ * the intermediate buffer
+ */
+ if (sw_to_end < bytes)
+ bytes = sw_to_end;
+ if (!bytes)
+ break;
+
+#ifdef SND_PCM_INDIRECT2_STAT
+ if (rec->firstbytetime == 0)
+ rec->firstbytetime = jiffies;
+ rec->lastbytetime = jiffies;
+#endif
+ /* copy bytes from the intermediate buffer (position sw_data)
+ * to the HW at most and return number of bytes actually copied
+ * from HW
+ * Furthermore, set hw_ready to 0, if the fifo is empty now.
+ */
+ bytes = copy(substream, rec, bytes);
+ rec->bytes2hw += bytes;
+
+#ifdef SND_PCM_INDIRECT2_STAT
+ if (bytes < 64)
+ rec->byte_sizes[bytes]++;
+ else
+ snd_printk(KERN_DEBUG
+ "STAT: (capture) %d Bytes copied to "
+ "hardware at once - too big to save!\n",
+ bytes);
+#endif
+ /* increase sw_data by the number of actually copied bytes from
+ * HW
+ */
+ rec->sw_data += bytes;
+ if (rec->sw_data == rec->sw_buffer_size)
+ rec->sw_data = 0;
+
+ snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 1,
+ bytes);
+
+ /* number of bytes in the intermediate buffer, which haven't
+ * been fetched by ALSA yet.
+ */
+ rec->sw_ready += bytes;
+ }
+ return;
+}
+
+/*
+ * helper function for capture interrupt routine
+ */
+void
+snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec,
+ snd_pcm_indirect2_copy_t copy,
+ snd_pcm_indirect2_zero_t null)
+{
+#ifdef SND_PCM_INDIRECT2_STAT
+ rec->irq_occured++;
+#endif
+ /* hardware recorded some bytes, so there is something to read from the
+ * record fifo:
+ */
+ rec->hw_ready = 1;
+
+ /* don't call ack() now, instead call transfer() function directly
+ * (normally called by ack() )
+ */
+ snd_pcm_indirect2_capture_transfer(substream, rec, copy, null);
+
+ if (rec->min_periods >= rec->min_multiple) {
+
+#ifdef SND_PCM_INDIRECT2_STAT
+ if ((rec->min_periods / rec->min_multiple) > 7)
+ snd_printk(KERN_DEBUG
+ "STAT: more than 7 (%d) mul_adds - "
+ "too big to save!\n",
+ (rec->min_periods / rec->min_multiple));
+ else
+ rec->mul_adds[(rec->min_periods /
+ rec->min_multiple)]++;
+ rec->mul_elapsed_real += (rec->min_periods /
+ rec->min_multiple);
+ rec->mul_elapsed++;
+
+ if (!(rec->mul_elapsed % 4)) {
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int appl_ptr =
+ frames_to_bytes(runtime,
+ (unsigned int)runtime->control->
+ appl_ptr) % rec->sw_buffer_size;
+ int diff = rec->sw_data - appl_ptr;
+ if (diff < 0)
+ diff += rec->sw_buffer_size;
+ snd_printk(KERN_DEBUG
+ "STAT: mul_elapsed: %d, sw_data: %u, "
+ "appl_ptr (bytes): %u, diff: %d\n",
+ rec->mul_elapsed, rec->sw_data, appl_ptr,
+ diff);
+ }
+#endif
+ rec->min_periods = 0;
+ snd_pcm_period_elapsed(substream);
+ }
+}
diff --git a/sound/drivers/pcm-indirect2.h b/sound/drivers/pcm-indirect2.h
new file mode 100644
index 0000000..859cd7a
--- /dev/null
+++ b/sound/drivers/pcm-indirect2.h
@@ -0,0 +1,140 @@
+/*
+ * Helper functions for indirect PCM data transfer to a simple FIFO in
+ * hardware (small, no possibility to read "hardware io position",
+ * updating position done by interrupt, ...)
+ *
+ * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de>
+ *
+ * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by
+ *
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ * Jaroslav Kysela <perex@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SOUND_PCM_INDIRECT2_H
+#define __SOUND_PCM_INDIRECT2_H
+
+/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t */
+#include <sound/pcm.h>
+
+/* Debug options for code which may be removed completely in a final version */
+#ifdef CONFIG_SND_DEBUG
+#define SND_PCM_INDIRECT2_STAT /* turn on some "statistics" about the
+ * process of copying bytes from the
+ * intermediate buffer to the hardware
+ * fifo and the other way round
+ */
+#endif
+
+struct snd_pcm_indirect2 {
+ unsigned int hw_buffer_size; /* Byte size of hardware buffer */
+ int hw_ready; /* playback: 1 = hw fifo has room left,
+ * 0 = hw fifo is full
+ */
+ unsigned int min_multiple;
+ int min_periods; /* counts number of min. periods until
+ * min_multiple is reached
+ */
+ int min_period_count; /* counts bytes to count number of
+ * min. periods
+ */
+
+ unsigned int sw_buffer_size; /* Byte size of software buffer */
+
+ /* sw_data: position in intermediate buffer, where we will read (or
+ * write) from/to next time (to transfer data to/from HW)
+ */
+ unsigned int sw_data; /* Offset to next dst (or src) in sw
+ * ring buffer
+ */
+ /* easiest case (playback):
+ * sw_data is nearly the same as ~ runtime->control->appl_ptr, with the
+ * exception that sw_data is "behind" by the number if bytes ALSA wrote
+ * to the intermediate buffer last time.
+ * A call to ack() callback synchronizes both indirectly.
+ */
+
+ /* We have no real sw_io pointer here. Usually sw_io is pointing to the
+ * current playback/capture position _inside_ the hardware. Devices
+ * with plain FIFOs often have no possibility to publish this position.
+ * So we say: if sw_data is updated, that means bytes were copied to
+ * the hardware, we increase sw_io by that amount, because there have
+ * to be as much bytes which were played. So sw_io will stay behind
+ * sw_data all the time and has to converge to sw_data at the end of
+ * playback.
+ */
+ unsigned int sw_io; /* Current software pointer in bytes */
+
+ /* sw_ready: number of bytes ALSA copied to the intermediate buffer, so
+ * it represents the number of bytes which wait for transfer to the HW
+ */
+ int sw_ready; /* Bytes ready to be transferred to/from hw */
+
+ /* appl_ptr: last known position of ALSA (where ALSA is going to write
+ * next time into the intermediate buffer
+ */
+ snd_pcm_uframes_t appl_ptr; /* Last seen appl_ptr */
+
+ unsigned int bytes2hw;
+ int check_alignment;
+
+#ifdef SND_PCM_INDIRECT2_STAT
+ unsigned int zeros2hw;
+ unsigned int mul_elapsed;
+ unsigned int mul_elapsed_real;
+ unsigned long firstbytetime;
+ unsigned long lastbytetime;
+ unsigned long firstzerotime;
+ unsigned int byte_sizes[64];
+ unsigned int zero_sizes[64];
+ unsigned int min_adds[8];
+ unsigned int mul_adds[8];
+ unsigned int zero_times[3750]; /* = 15s */
+ unsigned int zero_times_saved;
+ unsigned int zero_times_notsaved;
+ unsigned int irq_occured;
+ unsigned int pointer_calls;
+ unsigned int lastdifftime;
+#endif
+};
+
+typedef size_t(*snd_pcm_indirect2_copy_t) (struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec,
+ size_t bytes);
+typedef size_t(*snd_pcm_indirect2_zero_t) (struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec);
+
+#ifdef SND_PCM_INDIRECT2_STAT
+void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec);
+#endif
+
+snd_pcm_uframes_t
+snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec);
+void
+snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec,
+ snd_pcm_indirect2_copy_t copy,
+ snd_pcm_indirect2_zero_t zero);
+void
+snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect2 *rec,
+ snd_pcm_indirect2_copy_t copy,
+ snd_pcm_indirect2_zero_t null);
+
+#endif /* __SOUND_PCM_INDIRECT2_H */
--
1.5.2.4
^ permalink raw reply related
* Re: [PATCHv2 2/2] [VIRTEX] Register AC97 Controller Reference with the platform bus
From: Joachim Förster @ 2007-08-11 16:24 UTC (permalink / raw)
To: Takashi Iwai
Cc: alsa-devel, Stephen Neuendorffer, Lorenz Kolb, linuxppc-embedded
In-Reply-To: <s5hsl6sws0s.wl%tiwai@suse.de>
Hi Grant, Takashi, Stephen,
I tried to correct the issues here, too. But unfortunately Xilinx EDK
uses such long #define macro names. I guess we have to leave it the way
it is?
From: Joachim Foerster <JOFT@gmx.de>
(Patch for Linus' master branch, date 2007/08/08)
Signed-off-by: Joachim Foerster <JOFT@gmx.de>
---
arch/ppc/syslib/virtex_devices.c | 28 ++++++++++++++++++++++++++++
1 files changed, 28 insertions(+), 0 deletions(-)
diff --git a/arch/ppc/syslib/virtex_devices.c b/arch/ppc/syslib/virtex_devices.c
index 62a9495..d0b04d6 100644
--- a/arch/ppc/syslib/virtex_devices.c
+++ b/arch/ppc/syslib/virtex_devices.c
@@ -121,6 +121,29 @@
}, \
}
+#define XPAR_AC97_CONTROLLER_REFERENCE(num) { \
+ .name = "ml403-ac97cr", \
+ .id = num, \
+ .num_resources = 3, \
+ .resource = (struct resource[]) { \
+ { \
+ .start = XPAR_OPB_AC97_CONTROLLER_REF_##num##_BASEADDR, \
+ .end = XPAR_OPB_AC97_CONTROLLER_REF_##num##_HIGHADDR, \
+ .flags = IORESOURCE_MEM, \
+ }, \
+ { \
+ .start = XPAR_OPB_INTC_0_OPB_AC97_CONTROLLER_REF_##num##_PLAYBACK_INTERRUPT_INTR, \
+ .end = XPAR_OPB_INTC_0_OPB_AC97_CONTROLLER_REF_##num##_PLAYBACK_INTERRUPT_INTR, \
+ .flags = IORESOURCE_IRQ, \
+ }, \
+ { \
+ .start = XPAR_OPB_INTC_0_OPB_AC97_CONTROLLER_REF_##num##_RECORD_INTERRUPT_INTR, \
+ .end = XPAR_OPB_INTC_0_OPB_AC97_CONTROLLER_REF_##num##_RECORD_INTERRUPT_INTR, \
+ .flags = IORESOURCE_IRQ, \
+ }, \
+ }, \
+}
+
/* UART 8250 driver platform data table */
struct plat_serial8250_port virtex_serial_platform_data[] = {
#if defined(XPAR_UARTNS550_0_BASEADDR)
@@ -221,6 +244,11 @@ struct platform_device virtex_platform_devices[] = {
#if defined(XPAR_TFT_3_BASEADDR)
XPAR_TFT(3),
#endif
+
+ /* AC97 Controller Reference instances */
+#if defined(XPAR_OPB_AC97_CONTROLLER_REF_0_BASEADDR)
+ XPAR_AC97_CONTROLLER_REFERENCE(0),
+#endif
};
/* Early serial support functions */
--
1.5.2.4
^ permalink raw reply related
* Oops on boot with 2.6.23-rc2-g2c9d365c
From: Guido Guenther @ 2007-08-11 22:42 UTC (permalink / raw)
To: linuxppc-dev
Hi,
I'm seeing the following oops on a Powerbook 6,1 with
2.6.23-rc2-g2c9d365c (and also with earlier 2.6.23, 2.6.22 is fine) and
am quiet clueless. Any ideas before ist start bisecting?
-- Guido
Registering PowerMac CPU frequency driver
Low: 533 Mhz, High: 867 Mhz, Boot: 866 Mhz
------------[ cut here ]------------
Badness at kernel/irq/resend.c:70
NIP: c0050b2c LR: c0050b00 CTR: c0016738
REGS: cff43c30 TRAP: 0700 Not tainted (2.6.23-rc2-g2c9d365c)
MSR: 00021032 <ME,IR,DR> CR: 48002084 XER: 20000000
TASK = cff41830[1] 'swapper' THREAD: cff42000
GPR00: 00000001 cff43ce0 cff41830 00000178 0000002f 80000000 00000000 0000002f
GPR08: 000186a0 c02a0000 4048002f 00000000 00082208 00000000 00000000 00000000
GPR16: 00000000 00000000 00000000 00000000 00000000 41400000 01668460 00000000
GPR24: c0273000 c02a0000 c02a0000 00000000 00000000 0000002f 00024008 c0279fb8
NIP [c0050b2c] check_irq_resend+0x5c/0xc0
LR [c0050b00] check_irq_resend+0x30/0xc0
Call Trace:
[cff43ce0] [c0050b00] check_irq_resend+0x30/0xc0 (unreliable)
[cff43d00] [c0050658] enable_irq+0x84/0xa8
[cff43d10] [c014ab54] pmu_resume+0x80/0xc4
[cff43d20] [c00219d4] pmu_set_cpu_speed+0x19c/0x1b4
[cff43db0] [c002157c] do_set_cpu_speed+0xb4/0x160
[cff43dd0] [c002176c] pmac_cpufreq_target+0x48/0x6c
[cff43df0] [c01719f8] __cpufreq_driver_target+0x38/0x50
[cff43e00] [c017384c] cpufreq_governor_userspace+0x1c8/0x224
[cff43e20] [c0171dd4] __cpufreq_governor+0x64/0xbc
[cff43e40] [c01728bc] __cpufreq_set_policy+0x174/0x19c
[cff43e60] [c01735b0] cpufreq_add_dev+0x258/0x2f8
[cff43f20] [c013d9a4] sysdev_driver_register+0x78/0xe8
[cff43f40] [c01732dc] cpufreq_register_driver+0xa4/0x120
[cff43f50] [c025880c] pmac_cpufreq_setup+0x66c/0x688
[cff43f80] [c024a1e4] kernel_init+0xc8/0x284
[cff43ff0] [c001084c] kernel_thread+0x44/0x60
Instruction dump:
4e800421 801f0000 3d20c005 57cb052a 39290eb8 2f0b0400 7f804800 419e0030
3d20c02a 80098508 7c000034 5400d97e <0f000000> 2f800000 41be0048 38000001
io scheduler noop registered (default)
io scheduler cfq registered
^ permalink raw reply
* Re: [PATCH] remove gratuitous reads from pasemi pci config space methods
From: Olof Johansson @ 2007-08-11 23:57 UTC (permalink / raw)
To: Nathan Lynch; +Cc: linuxppc-dev, Paul Mackerras
In-Reply-To: <11866954472463-git-send-email-ntl@pobox.com>
On Thu, Aug 09, 2007 at 04:37:27PM -0500, Nathan Lynch wrote:
> The pasemi pci configuration space write method reads the written
> location immediately after the write is performed, presumably in order
> to flush the write. However, configuration space writes are not
> allowed to be posted, making these reads gratuitous. Furthermore,
> this behavior potentially causes us to violate the PCI PM spec when
> changing between e.g. D0 and D3 states, because a delay of up to 10ms
> may be required before the OS accesses configuration space after the
> write which initiates the transition.
>
> Remove the unnecessary reads from pa_pxp_write_config.
Thanks!
> Signed-off-by: Nathan Lynch <ntl@pobox.com>
Acked-by: Olof Johansson <olof@lixom.net>
-Olof
^ permalink raw reply
* Re: [RFC/PATCH] remove gratuitous reads from maple pci config space methods
From: Benjamin Herrenschmidt @ 2007-08-11 23:41 UTC (permalink / raw)
To: Segher Boessenkool
Cc: linuxppc-dev, Nathan Lynch, Paul Mackerras, David Gibson
In-Reply-To: <6681fdad128303fc62c86c2345aa33a4@kernel.crashing.org>
> It should be fine on PowerMac as well -- all G5s use U3/U4,
> the workaround is for certain older Apple bridge chips.
Yeah, remove them. The workaround that is needed afaik is really only
the one that reads back the -address- before accessing the data register
(and I think it's sill needed on U3), but it's unrelated to what that
patch changes.
As I said on IRC, I suspect those reads come from misguided attempts on
my part to avoid the processor itself posting those writes, since we
want config space access to be synchronous all the way. In that case,
however, a sync will do the job just fine to ensure the previous write
did hit the bridge.
Cheers,
Ben.
^ permalink raw reply
* no words output in console on 852t board with kernel 2.6.15
From: 徐 运 @ 2007-08-12 2:50 UTC (permalink / raw)
To: linuxppc-embedded
Hello all,
I try to run linux 2.6.15 on a 852T custom board but can't see any words
by console.
I have located the problem with leds and see that the program hangs in
function dma_alloc_coherent of cpm_uart_allocbuf in file cpm_uart_cpm1.c.
Is there anyone who has already seen similar problem or can give some
advise?
my kernel configure bases on TQM850L_defconfig and use smc1 as uart .
Thanks
xuyun00
...............
_________________________________________________________________
享用世界上最大的电子邮件系统― MSN Hotmail。 http://www.hotmail.com
^ permalink raw reply
* Re: [PATCH v3 1/2] [POWERPC] MPC832x_RDB: update dts to use spi, register mmc_spi stub
From: David Gibson @ 2007-08-12 8:16 UTC (permalink / raw)
To: Segher Boessenkool; +Cc: linuxppc-dev
In-Reply-To: <5cad6d634a7cb6c8c37adbb15db8dfc2@kernel.crashing.org>
On Fri, Aug 10, 2007 at 10:42:51PM +0200, Segher Boessenkool wrote:
> >>> We should co-ordinate better on this, if it's to become a
> >>> convention...
> >>
> >> That means we shouldn't coordinate on this, right?
> >
> > Heh. Either one is kind of ugly, I'll grant you.
> >
> > But, many SoCs do have a notion of device "number", which is relevant
> > for programming other general control registers in places. We need to
> > encode it somehow, and it would be good to have a consistent way of
> > doing it.
>
> I'm not convinced there isn't a more direct way to represent
> the relevant relationships.
>
> Either way, we don't have enough experience with this stuff
> yet to know what works well and what doesn't (at least, I
> don't, and I haven't seen any evidence that others do); so
> I'd prefer to keep this in per-device bindings for now; it
> should be there anyhow, but once we do have experience with
> it we could do some recommendation.
Well of course it will remain in the per-device bindings. But just
because these are in different per-device bindings doesn't mean we
can't *try* to use consistent property names for similar things...
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
^ permalink raw reply
* [patch 10/10] m68k/mac: Make mac_hid_mouse_emulate_buttons() declaration visible
From: Geert Uytterhoeven @ 2007-08-12 9:41 UTC (permalink / raw)
To: Linus Torvalds, Andrew Morton
Cc: linux-input, linuxppc-dev, linux-m68k, Dmitry Torokhov,
linux-kernel
In-Reply-To: <20070812094050.522858761@mail.of.borg>
From: Geert Uytterhoeven <geert@linux-m68k.org>
m68k/mac: Make mac_hid_mouse_emulate_buttons() declaration visible
drivers/char/keyboard.c: In function 'kbd_keycode':
drivers/char/keyboard.c:1142: error: implicit declaration of function 'mac_hid_mouse_emulate_buttons'
The forward declaration of mac_hid_mouse_emulate_buttons() is not visible on
m68k because it's hidden in the middle of a big #ifdef block.
Move it to <linux/kbd_kern.h>, correct the type of the second parameter, and
include <linux/kbd_kern.h> where needed.
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
drivers/char/keyboard.c | 4 ----
drivers/macintosh/mac_hid.c | 1 +
include/linux/kbd_kern.h | 3 +++
3 files changed, 4 insertions(+), 4 deletions(-)
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -1022,10 +1022,6 @@ static const unsigned short x86_keycodes
308,310,313,314,315,317,318,319,320,357,322,323,324,325,276,330,
332,340,365,342,343,344,345,346,356,270,341,368,369,370,371,372 };
-#ifdef CONFIG_MAC_EMUMOUSEBTN
-extern int mac_hid_mouse_emulate_buttons(int, int, int);
-#endif /* CONFIG_MAC_EMUMOUSEBTN */
-
#ifdef CONFIG_SPARC
static int sparc_l1_a_state = 0;
extern void sun_do_break(void);
--- a/drivers/macintosh/mac_hid.c
+++ b/drivers/macintosh/mac_hid.c
@@ -13,6 +13,7 @@
#include <linux/sysctl.h>
#include <linux/input.h>
#include <linux/module.h>
+#include <linux/kbd_kern.h>
static struct input_dev *emumousebtn;
--- a/include/linux/kbd_kern.h
+++ b/include/linux/kbd_kern.h
@@ -161,4 +161,7 @@ static inline void con_schedule_flip(str
schedule_delayed_work(&t->buf.work, 0);
}
+/* mac_hid.c */
+extern int mac_hid_mouse_emulate_buttons(int, unsigned int, int);
+
#endif
--
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [PATCH v3 1/2] [POWERPC] MPC832x_RDB: update dts to use spi, register mmc_spi stub
From: Segher Boessenkool @ 2007-08-12 11:36 UTC (permalink / raw)
To: David Gibson; +Cc: linuxppc-dev
In-Reply-To: <20070812081604.GA6458@localhost.localdomain>
>>> But, many SoCs do have a notion of device "number", which is relevant
>>> for programming other general control registers in places. We need
>>> to
>>> encode it somehow, and it would be good to have a consistent way of
>>> doing it.
>>
>> I'm not convinced there isn't a more direct way to represent
>> the relevant relationships.
>>
>> Either way, we don't have enough experience with this stuff
>> yet to know what works well and what doesn't (at least, I
>> don't, and I haven't seen any evidence that others do); so
>> I'd prefer to keep this in per-device bindings for now; it
>> should be there anyhow, but once we do have experience with
>> it we could do some recommendation.
>
> Well of course it will remain in the per-device bindings. But just
> because these are in different per-device bindings doesn't mean we
> can't *try* to use consistent property names for similar things...
Sure, if those properties do exactly the same thing, identical
names are a good thing. Until we're reasonably certain that this
is a good way to do things I'd rather people experiment a bit
instead of all cluster around a random thing that would be labeled
"de facto standard".
:-)
Segher
^ permalink raw reply
* Re: 2.6.23-rc2-mm2 build error on MIPS and ARM
From: Mathieu Desnoyers @ 2007-08-12 13:35 UTC (permalink / raw)
To: Martin Schwidefsky; +Cc: linuxppc-dev, Andrew Morton, linux-kernel
In-Reply-To: <1186825800.32314.2.camel@localhost>
* Martin Schwidefsky (schwidefsky@de.ibm.com) wrote:
> On Fri, 2007-08-10 at 22:58 -0400, Mathieu Desnoyers wrote:
> > I got the following errors when building 2.6.23-rc2-mm2 on both mips and
> > arm. Both errors are very much alike.
>
> That attached patch should fix it for arm and mips. I'll try a few more
> architectures until I'm fed up with compiling cross-compilers..
>
It fixes the problem for me, thanks!
Tested-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
Mathieu
> --
> blue skies,
> Martin.
>
> "Reality continues to ruin my life." - Calvin.
> ---
> Subject: [PATCH] move mm_struct and vm_area_struct, compile fix.
>
> From: Martin Schwidefsky <schwidefsky@de.ibm.com>
>
> Include <asm/page.h> in <linux/mm_types.h> to make mips and arm
> compile again after mm_struct and vm_area_struct have been moved.
>
> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
> ---
>
> include/linux/mm_types.h | 1 +
> 1 file changed, 1 insertion(+)
>
> diff -urpN linux-2.6/include/linux/mm_types.h
> linux-2.6-patched/include/linux/mm_types.h
> --- linux-2.6/include/linux/mm_types.h 2007-08-11 11:30:10.000000000
> +0200
> +++ linux-2.6-patched/include/linux/mm_types.h 2007-08-11
> 11:32:42.000000000 +0200
> @@ -11,6 +11,7 @@
> #include <linux/rwsem.h>
> #include <linux/completion.h>
> #include <asm/mmu.h>
> +#include <asm/page.h>
>
> struct address_space;
>
>
>
--
Mathieu Desnoyers
Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
^ permalink raw reply
* basic and stupid question on wait_event and wake_up
From: Ming Liu @ 2007-08-12 13:57 UTC (permalink / raw)
To: Linuxppc-embedded
Dear all,
I am reading LDD(V3) chapter 6 on the topic of wait_event(queue, condition)
and wake_up(queue) functions. I am quite confused on the sayings. One is
"Until condition evaluates to a true value, the process continues to
sleep", which looks like that 'condition' is the one who wake up the
process from its sleeping. However the other saying is "The basic function
that wakes up sleeping processes is called wake_up, and wake_up wakes up
all processes waiting on the given queue" So who is the exact one to wake
the sleeping process up at all, condition or wake_up? From my
understanding, if the condition becomes true, then the sleeping process
will leave its sleeping status and wake up. Then what's the use of wake_up
function?
My senario could be described as: in my char device driver, I use one ioctl
command to initiate a DMA transfer. After all related registers are
initiated, this process will be put to sleep for saving CPU cycles. In the
interrupt handler which is for a DMA_done, I wake that process up and
resume its following executing. With this method, in my application program
if I release a DMA initiation command, it is a Blocking operation and it
will wait until the DMA transfer is done.
Perhaps my question is quite simple or basic. Thanks for any explanation
and comment on this topic in my senario from you experts. Thanks a lot.
Br
Ming
_________________________________________________________________
与联机的朋友进行交流,请使用 MSN Messenger: http://messenger.msn.com/cn
^ permalink raw reply
* Re: pci in arch/powerpc vs arch/ppc
From: Benjamin Herrenschmidt @ 2007-08-11 23:28 UTC (permalink / raw)
To: Segher Boessenkool; +Cc: linuxppc-dev, Alexandros Kostopoulos
In-Reply-To: <2b043cf24b0f5ddda06fcf1f01cb62f3@kernel.crashing.org>
On Thu, 2007-08-09 at 17:56 +0200, Segher Boessenkool wrote:
> > It means the bus on which legacy I/O ports can be found. It's a fairly
> > broken concept; each host bridge should really be treated as a
> > completely separate entity, and if something like a VGA card has legacy
> > I/O ports that need to be used, they should be looked for on the same
> > PCI bus as the card itself. Legacy ISA ports should be discovered
> > through the device tree (or platform devices, or whatever) that
> > explicitly state which PCI-to-ISA bridge they're under.
>
> Currently, Linux does not allow multiple PCI domains to use
> overlapping legacy I/O ranges. Yeah it's a pain.
I have a plan I exposed a little while ago to handle that. We need that
for VGA cards among others anyway.
The idea is basically a call around the lines of
pci_convert_legacy_resource(struct resource *r);
You fill up the resource with flags = MEM/IO and start/end being your
legacy range, and it returns a "fixed" resource that you can use with
inX/outX, or whatever else.
Haven't had time to code something up, and we need to provide a default
impl. for all archs too ... but feel free to volunteer and beat me to
it :-)
Ben.
^ permalink raw reply
* Error: unsupported relocation against CSRR0
From: mike zheng @ 2007-08-12 21:04 UTC (permalink / raw)
To: linuxppc-dev, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 998 bytes --]
Hi All,
I have "unsupported relocation against" problem, while compiling entry.S in
2.4 Kernel against e500 core. Does anyone know what is the problem? Is
there any option I missed?
Thanks in advance,
MikeZ
/kernel-> powerpc-linux-gnuspe-gcc -m32 -Wp,-MD,arch/ppc/kernel/.entry.o.d
-nostdinc -isystem
/opt/mtwk/usr/local/gcc-3_4-e500-glibc-2.3.4-dp/powerpc-linux-gnuspe/lib/gcc/powerpc-linux-gnuspe/3.4.3/include
-D__KERNEL__ -Iinclude -Iarch/ppc -D__ASSEMBLY__ -Iarch/ppc -Wa,-me500 -c
-o arch/ppc/kernel/entry.o arch/ppc/kernel/entry.S
arch/ppc/kernel/entry.S: Assembler messages:
entry.S:819: Error: unsupported relocation against CSRR0
entry.S:820: Error: unsupported relocation against CSRR1
entry.S:888: Error: unsupported relocation against MCSRR0
entry.S:889: Error: unsupported relocation against MCSRR1
entry.S:909: Error: unsupported relocation against CSRR0
entry.S:911: Error: unsupported relocation against CSRR1
entry.S
819 mtspr CSRR0, r11
820 mtspr CSRR1, r12
[-- Attachment #2: Type: text/html, Size: 1820 bytes --]
^ permalink raw reply
* Re: Oops on boot with 2.6.23-rc2-g2c9d365c
From: Benjamin Herrenschmidt @ 2007-08-12 21:05 UTC (permalink / raw)
To: Guido Guenther; +Cc: linuxppc-dev
In-Reply-To: <20070811224241.GA9770@bogon.ms20.nix>
On Sun, 2007-08-12 at 00:42 +0200, Guido Guenther wrote:
> Hi,
> I'm seeing the following oops on a Powerbook 6,1 with
> 2.6.23-rc2-g2c9d365c (and also with earlier 2.6.23, 2.6.22 is fine) and
> am quiet clueless. Any ideas before ist start bisecting?
I think it's a bogus WARN_ON, just remove it.
Ben.
> -- Guido
>
> Registering PowerMac CPU frequency driver
> Low: 533 Mhz, High: 867 Mhz, Boot: 866 Mhz
> ------------[ cut here ]------------
> Badness at kernel/irq/resend.c:70
> NIP: c0050b2c LR: c0050b00 CTR: c0016738
> REGS: cff43c30 TRAP: 0700 Not tainted (2.6.23-rc2-g2c9d365c)
> MSR: 00021032 <ME,IR,DR> CR: 48002084 XER: 20000000
> TASK = cff41830[1] 'swapper' THREAD: cff42000
> GPR00: 00000001 cff43ce0 cff41830 00000178 0000002f 80000000 00000000 0000002f
> GPR08: 000186a0 c02a0000 4048002f 00000000 00082208 00000000 00000000 00000000
> GPR16: 00000000 00000000 00000000 00000000 00000000 41400000 01668460 00000000
> GPR24: c0273000 c02a0000 c02a0000 00000000 00000000 0000002f 00024008 c0279fb8
> NIP [c0050b2c] check_irq_resend+0x5c/0xc0
> LR [c0050b00] check_irq_resend+0x30/0xc0
> Call Trace:
> [cff43ce0] [c0050b00] check_irq_resend+0x30/0xc0 (unreliable)
> [cff43d00] [c0050658] enable_irq+0x84/0xa8
> [cff43d10] [c014ab54] pmu_resume+0x80/0xc4
> [cff43d20] [c00219d4] pmu_set_cpu_speed+0x19c/0x1b4
> [cff43db0] [c002157c] do_set_cpu_speed+0xb4/0x160
> [cff43dd0] [c002176c] pmac_cpufreq_target+0x48/0x6c
> [cff43df0] [c01719f8] __cpufreq_driver_target+0x38/0x50
> [cff43e00] [c017384c] cpufreq_governor_userspace+0x1c8/0x224
> [cff43e20] [c0171dd4] __cpufreq_governor+0x64/0xbc
> [cff43e40] [c01728bc] __cpufreq_set_policy+0x174/0x19c
> [cff43e60] [c01735b0] cpufreq_add_dev+0x258/0x2f8
> [cff43f20] [c013d9a4] sysdev_driver_register+0x78/0xe8
> [cff43f40] [c01732dc] cpufreq_register_driver+0xa4/0x120
> [cff43f50] [c025880c] pmac_cpufreq_setup+0x66c/0x688
> [cff43f80] [c024a1e4] kernel_init+0xc8/0x284
> [cff43ff0] [c001084c] kernel_thread+0x44/0x60
> Instruction dump:
> 4e800421 801f0000 3d20c005 57cb052a 39290eb8 2f0b0400 7f804800 419e0030
> 3d20c02a 80098508 7c000034 5400d97e <0f000000> 2f800000 41be0048 38000001
> io scheduler noop registered (default)
> io scheduler cfq registered
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
^ permalink raw reply
* [PATCH] via-pmu: Fix typo in printk
From: Michael Buesch @ 2007-08-12 20:38 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
This fixes a typo in a printk message.
Signed-off-by: Michael Buesch <mb@bu3sch.de>
Index: linux-2.6/drivers/macintosh/via-pmu.c
===================================================================
--- linux-2.6.orig/drivers/macintosh/via-pmu.c 2007-05-26 19:56:27.000000000 +0200
+++ linux-2.6/drivers/macintosh/via-pmu.c 2007-08-12 22:36:27.000000000 +0200
@@ -410,7 +410,7 @@ static int __init via_pmu_start(void)
irq = irq_of_parse_and_map(vias, 0);
if (irq == NO_IRQ) {
- printk(KERN_ERR "via-pmu: can't map interruptn");
+ printk(KERN_ERR "via-pmu: can't map interrupt\n");
return -ENODEV;
}
if (request_irq(irq, via_pmu_interrupt, 0, "VIA-PMU", (void *)0)) {
--
Greetings Michael.
^ permalink raw reply
* [PATCH, RFC] wake up from a serial port
From: Guennadi Liakhovetski @ 2007-08-12 22:27 UTC (permalink / raw)
To: linux-serial, linuxppc-dev
A number of Linkstation models from Buffalo Technology with PPC, ARM, and
also MIPS (I think) CPUs have a power-management controller connected to a
UART. Among other things that chip controlls power and reset buttons.
Working on a standby support for one of these systems (ppc mpc8241 based),
the only suitable wakeup source there is the power button, which means, I
have to configure one of the two system UARTs to not be suspendsd. Using
the device_*_wakeup API doesn't quite work because both serial ports share
one device. The below patch proposes a new port flag UPF_MAY_WAKEUP to
configure such UARTs. It also adds support for a new "can-wakeup" serial
node property to the legacy_serial driver.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c
index cea8045..888d9bb 100644
--- a/arch/powerpc/kernel/legacy_serial.c
+++ b/arch/powerpc/kernel/legacy_serial.c
@@ -51,6 +51,9 @@ static int __init add_legacy_port(struct device_node *np, int want_index,
/* get default speed if present */
spd = of_get_property(np, "current-speed", NULL);
+ if (of_find_property(np, "can-wakeup", NULL))
+ flags |= UPF_MAY_WAKEUP;
+
/* If we have a location index, then try to use it */
if (want_index >= 0 && want_index < MAX_LEGACY_SERIAL_PORTS)
index = want_index;
diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index 0b3ec38..77dd552 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -130,6 +130,7 @@ struct uart_8250_port {
unsigned char mcr_mask; /* mask of user bits */
unsigned char mcr_force; /* mask of forced bits */
unsigned char lsr_break_flag;
+ char suspended;
/*
* We provide a per-port pm hook.
@@ -2680,8 +2681,14 @@ static int serial8250_suspend(struct platform_device *dev, pm_message_t state)
for (i = 0; i < UART_NR; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
- if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev)
- uart_suspend_port(&serial8250_reg, &up->port);
+ if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) {
+ if (up->port.flags & UPF_MAY_WAKEUP)
+ enable_irq_wake(up->port.irq);
+ else {
+ uart_suspend_port(&serial8250_reg, &up->port);
+ up->suspended = 1;
+ }
+ }
}
return 0;
@@ -2694,8 +2701,13 @@ static int serial8250_resume(struct platform_device *dev)
for (i = 0; i < UART_NR; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
- if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev)
- serial8250_resume_port(i);
+ if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) {
+ if (up->suspended) {
+ serial8250_resume_port(i);
+ up->suspended = 0;
+ } else
+ disable_irq_wake(up->port.irq);
+ }
}
return 0;
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 773d8d8..d585967 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -272,6 +272,7 @@ struct uart_port {
#define UPF_LOW_LATENCY ((__force upf_t) (1 << 13))
#define UPF_BUGGY_UART ((__force upf_t) (1 << 14))
#define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))
+#define UPF_MAY_WAKEUP ((__force upf_t) (1 << 17))
#define UPF_CONS_FLOW ((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ ((__force upf_t) (1 << 24))
#define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))
^ permalink raw reply related
* Re: Error: unsupported relocation against CSRR0
From: David Gibson @ 2007-08-13 0:49 UTC (permalink / raw)
To: mike zheng; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <5c9cd53b0708121404p5e2b8311l5a20ec481a6bed59@mail.gmail.com>
On Sun, Aug 12, 2007 at 05:04:35PM -0400, mike zheng wrote:
> Hi All,
>
> I have "unsupported relocation against" problem, while compiling entry.S in
> 2.4 Kernel against e500 core. Does anyone know what is the problem? Is
> there any option I missed?
Probably means you haven't included the file with the #defines for
these SPR values.
> /kernel-> powerpc-linux-gnuspe-gcc -m32 -Wp,-MD,arch/ppc/kernel/.entry.o.d
> -nostdinc -isystem
> /opt/mtwk/usr/local/gcc-3_4-e500-glibc-2.3.4-dp/powerpc-linux-gnuspe/lib/gcc/powerpc-linux-gnuspe/3.4.3/include
> -D__KERNEL__ -Iinclude -Iarch/ppc -D__ASSEMBLY__ -Iarch/ppc -Wa,-me500 -c
> -o arch/ppc/kernel/entry.o arch/ppc/kernel/entry.S
> arch/ppc/kernel/entry.S: Assembler messages:
>
> entry.S:819: Error: unsupported relocation against CSRR0
> entry.S:820: Error: unsupported relocation against CSRR1
> entry.S:888: Error: unsupported relocation against MCSRR0
> entry.S:889: Error: unsupported relocation against MCSRR1
> entry.S:909: Error: unsupported relocation against CSRR0
> entry.S:911: Error: unsupported relocation against CSRR1
>
>
>
> entry.S
>
> 819 mtspr CSRR0, r11
> 820 mtspr CSRR1, r12
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
^ permalink raw reply
* Re: DTC 1.0.0 Release
From: David Gibson @ 2007-08-13 0:50 UTC (permalink / raw)
To: Olaf Hering; +Cc: linuxppc-dev, Jon Loeliger
In-Reply-To: <20070810212129.GA16948@aepfle.de>
On Fri, Aug 10, 2007 at 11:21:29PM +0200, Olaf Hering wrote:
> On Thu, Aug 09, Jon Loeliger wrote:
>
> > As usual, please let me know of any issues with it.
>
> tests/truncated_property.c:45: warning: 'err' is used uninitialized
> in this function
Looks good. If you had a Signed-off-by, I'd add an Acked-by...
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
^ permalink raw reply
* Re: Device tree aware EMAC driver
From: David Gibson @ 2007-08-13 1:05 UTC (permalink / raw)
To: Segher Boessenkool; +Cc: LinuxPPC-dev
In-Reply-To: <7af683d3d78a51373d071b126c51b53a@kernel.crashing.org>
On Fri, Aug 10, 2007 at 10:39:31PM +0200, Segher Boessenkool wrote:
> > * In drivers/net/ibm_newemac/Makefile, I think the preferred method is
> > to use ibm_newemac-y rather than ibm_newemac-objs.
>
> I thought it was the other way around, so I checked with the
> Kbuild maintainer, and indeed you are correct.
Changed accordingly.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
^ permalink raw reply
* Re: [ PATCH ] PowerPC cascade UIC IRQ handler fix.
From: David Gibson @ 2007-08-13 1:08 UTC (permalink / raw)
To: Benjamin Herrenschmidt, linuxppc-dev, Valentine Barshak
In-Reply-To: <20070803062346.GA14456@localhost.localdomain>
On Fri, Aug 03, 2007 at 04:23:46PM +1000, David Gibson wrote:
> On Fri, Aug 03, 2007 at 02:57:05PM +1000, David Gibson wrote:
> > On Fri, Aug 03, 2007 at 11:18:09AM +1000, Benjamin Herrenschmidt wrote:
> > > On Thu, 2007-08-02 at 13:48 +1000, David Gibson wrote:
> > > > On Mon, Jul 30, 2007 at 08:35:17PM +0400, Valentine Barshak wrote:
> > > > > PPC44x cascade UIC irq handler fix.
> > > > >
> > > > > According to PPC44x UM, if an interrupt is configured as level-sensitive,
> > > > > and a clear is attempted on the UIC_SR, the UIC_SR field is not
> > > > > cleared if the incoming interrupt signal is at the asserted polarity.
> > > > > This causes us to enter a cascade handler twice, since we first ack
> > > > > parent UIC interrupt and ack child UIC one after that.
> > > > > The patch checks child UIC msr value and returns IRQ_HANDLED
> > > > > if there're no pending interrupts. Otherwise we get a kernel panic
> > > > > with a "Fatal exception in interrupt" (illegal vector).
> > > > > The patch also fixes status flags.
> > > > >
> > > > > Signed-off-by: Valentine Barshak <vbarshak@ru.mvista.com>
> > > >
> > > > Hrm... This doesn't seem like the right fix to me. Instead, I think
> > > > the cascaded IRQ handler should ack the interrupt on the child first.
> > > > I'm a little surprised it doesn't at the moment.
> > >
> > > Well, we certainly do also need to make the code more solid vs.
> > > spurrious interrupts.
> >
> > Actually that's true. The suggested patch is a good improvement for
> > general robustness, but doesn't actually address the real problem.
> >
> > > The main thing is, if the cascade is a level interrupt, it should
> > > probably use a smarter cascade handler that masks it, handle the child
> > > interrupts, then unmasks it.
> >
> > We already have that, since I just use setup_irq() to set up a cascade
> > handler, rather than a custom flow handler.
> >
> > The problem is that the standard handle_level_irq() flow handler acks
> > before the ISR is called, whereas because of this UIC behaviour, we
> > need to ack after the ISR has cleared the interrupt in the source.
> > This is not specific to cascades, but is a problem for all
> > level-triggered interrupts (i.e. basically everything).
> >
> > I think it means we must currently be getting a whole lot of spurious
> > interrupts - will do some investigation in a moment.
> >
> > To fix this either we'll need a custom flow handler for UIC, or we can
> > use the standard one, but clear the UIC_SR bits from the ->unmask()
> > callback for level interrupts. I'm not entirely sure if the latter
> > approach is safe - I *think* it is, but I could do with more
> > convincing.
>
> Ok, here's a patch which fixes up the flow handling on the UIC. It
> needs some polish yet, but should be ok to test. Valentine, can you
> test this on your setup, *without* your original proposed patch.
> Eventually, for robustness, we'll want something like your original
> patch as well for robustness, but in the meantime leaving it out
> should tell us if my patch is actually having the intended effect.
Valentine, it would be really helpful if you could test this on the
problem you observed with the cascade interrupt. Any word on this?
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
^ permalink raw reply
* Re: DTC 1.0.0 Release Coming?
From: David Gibson @ 2007-08-12 9:26 UTC (permalink / raw)
To: Geoff Levand; +Cc: linuxppc-dev, Jon Loeliger, Paul Mackerras
In-Reply-To: <46BD1272.9060107@am.sony.com>
On Fri, Aug 10, 2007 at 06:35:46PM -0700, Geoff Levand wrote:
> Paul Mackerras wrote:
> > David Gibson writes:
> >
> >> We decided that since a formal dtc release was imminent, it would be
> >> simpler to make dtc a new kernel build requirement, rather than
> >> integrate the substantial blob of dtc code into the kernel tree and
> >> then have to deal with the maintenance / synchronization issues
> >> between the in-kernel and upstream versions.
> >
> > Um, what I thought we decided was to ship a pre-built .dtb for ps3
> > (once its dts settles down), and make dtc a kernel build requirement
> > only for embedded platforms.
>
> We could also ship a generated .S file (dtc -O asm -o ps3-dt.S ps3.dts),
> which would be easier to maintain in the source tree than a binary file,
> then use something like this in the wrapper script:
>
> ${CROSS}gcc -c -o ${platform}-dt.o ${platform}-dt.S
> ${CROSS}objcopy -O binary ${platform}-dt.o ${platform}.dtb
>
> Untested, but it seems like it would work.
Yes, I'm aware of that option and am considering it.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
^ permalink raw reply
* Re: DTC 1.0.0 Release Coming?
From: David Gibson @ 2007-08-12 9:25 UTC (permalink / raw)
To: Paul Mackerras; +Cc: linuxppc-dev, Jon Loeliger
In-Reply-To: <18109.2103.507694.281225@cargo.ozlabs.ibm.com>
On Sat, Aug 11, 2007 at 10:52:07AM +1000, Paul Mackerras wrote:
> David Gibson writes:
>
> > We decided that since a formal dtc release was imminent, it would be
> > simpler to make dtc a new kernel build requirement, rather than
> > integrate the substantial blob of dtc code into the kernel tree and
> > then have to deal with the maintenance / synchronization issues
> > between the in-kernel and upstream versions.
>
> Um, what I thought we decided was to ship a pre-built .dtb for ps3
> (once its dts settles down), and make dtc a kernel build requirement
> only for embedded platforms.
Oh, yes, sorry forgot that detail.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox