summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorYour Name <Bernhard.Guillon@opensimpad.org>2009-01-09 22:46:34 +0100
committerYour Name <Bernhard.Guillon@opensimpad.org>2009-01-09 22:46:34 +0100
commitdd18dcefdd237e1d02a7be0385bf8c2a7793fdc1 (patch)
tree4dc0f90ae5a7ad743a62e5ea86a195d8e3cec77c /packages
parent1c8891902d4ae73126be38ca8d40faa42d4bd5cd (diff)
add simpad sound driver update defconfig bump linux_2.6.24 PR
The sound driver patch is the work of Thomas Schätzlein. Thanks for this patch!
Diffstat (limited to 'packages')
-rw-r--r--packages/linux/linux-2.6.24/simpad/defconfig60
-rw-r--r--packages/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-audio.patch1622
-rw-r--r--packages/linux/linux_2.6.24.bb5
3 files changed, 1683 insertions, 4 deletions
diff --git a/packages/linux/linux-2.6.24/simpad/defconfig b/packages/linux/linux-2.6.24/simpad/defconfig
index fb88e108fb..24e98f8fa7 100644
--- a/packages/linux/linux-2.6.24/simpad/defconfig
+++ b/packages/linux/linux-2.6.24/simpad/defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.24
-# Mon Mar 3 22:58:54 2008
+# Fri Jan 9 09:35:22 2009
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
@@ -1274,7 +1274,63 @@ CONFIG_FONT_SUN8x16=y
#
# Sound
#
-# CONFIG_SOUND is not set
+CONFIG_SOUND=y
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+# CONFIG_SND_SEQUENCER is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# ALSA ARM devices
+#
+CONFIG_SND_SA11XX_UCB1X00=y
+
+#
+# USB devices
+#
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+
+#
+# PCMCIA devices
+#
+# CONFIG_SND_VXPOCKET is not set
+# CONFIG_SND_PDAUDIOCF is not set
+
+#
+# System on Chip audio support
+#
+# CONFIG_SND_SOC is not set
+
+#
+# SoC Audio support for SuperH
+#
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
# CONFIG_HID_SUPPORT is not set
CONFIG_HID=m
CONFIG_USB_SUPPORT=y
diff --git a/packages/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-audio.patch b/packages/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-audio.patch
new file mode 100644
index 0000000000..4f471b5fb4
--- /dev/null
+++ b/packages/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-audio.patch
@@ -0,0 +1,1622 @@
+Index: linux-2.6.24/sound/arm/sa11xx-ucb1x00.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.24/sound/arm/sa11xx-ucb1x00.c 2008-12-25 23:23:38.000000000 +0100
+@@ -0,0 +1,829 @@
++/*
++ * Driver for Philips UCB1300 on Siemens Simpad
++ * Copyright (C) 2008 Thomas Schätzlein <thomas@pnxs.de>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License.
++ *
++ */
++
++#include <sound/driver.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/err.h>
++#include <linux/platform_device.h>
++#include <linux/errno.h>
++#include <linux/ioctl.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++
++#ifdef CONFIG_PM
++#include <linux/pm.h>
++#endif
++
++#include <asm/dma.h>
++
++#include <sound/core.h>
++#include <sound/control.h>
++#include <sound/pcm.h>
++#include <sound/initval.h>
++#include <sound/info.h>
++
++#include <linux/mfd/ucb1x00.h>
++
++#undef DEBUG_MODE
++#undef DEBUG_FUNCTION_NAMES
++
++#define AUDIO_RATE_DEFAULT 44100
++
++MODULE_AUTHOR("Thomas Schätzlein <thomas@pnxs.de>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("SA1100/SA1111 + UCB1300 driver for ALSA");
++MODULE_SUPPORTED_DEVICE("{{UCB1x00,Simpad}}");
++
++static char *id; /* ID for this card */
++
++module_param(id, charp, 0444);
++MODULE_PARM_DESC(id, "ID string for SA1100/SA1111 + UCB1x00 soundcard.");
++
++struct audio_stream {
++ char *id; /* identification string */
++ int stream_id; /* numeric identification */
++ dma_device_t dma_dev; /* device identifier for DMA */
++ dma_regs_t *dma_regs; /* points to our DMA registers */
++ unsigned int active:1; /* we are using this stream for transfer now */
++ int period; /* current transfer period */
++ int periods; /* current count of periods registerd in the DMA engine */
++ unsigned int old_offset;
++ spinlock_t dma_lock; /* for locking in DMA operations (see dma-sa1100.c in the kernel) */
++ struct snd_pcm_substream *stream;
++};
++
++struct sa11xx_ucb1x00 {
++ struct snd_card* card;
++ struct snd_pcm* pcm;
++ struct ucb1x00_dev* ucb1x00;
++ struct device* pdev;
++ long samplerate;
++ struct audio_stream s[2]; /* playback & capture */
++};
++
++#if 0
++static unsigned int rates[] = {
++ 8000, 10666, 10985, 14647,
++ 16000, 21970, 22050, 24000,
++ 29400, 32000, 44100, 48000,
++};
++
++static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
++ .count = ARRAY_SIZE(rates),
++ .list = rates,
++ .mask = 0,
++};
++#endif
++
++#define UCB1x00_SINGLE(xname, where, reg, shift, mask, invert) \
++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ucb1x00_info_single, \
++ .get = snd_ucb1x00_get_single, .put = snd_ucb1x00_put_single, \
++ .private_value = where | (reg << 5) | (shift << 9) | (mask << 13) | (invert << 19) \
++}
++
++#define UCB1x00_SINGLE_WHERE(value) (value & 31)
++#define UCB1x00_SINGLE_REG(value) ((value >> 5) & 15)
++#define UCB1x00_SINGLE_SHIFT(value) ((value >> 9) & 15)
++#define UCB1x00_SINGLE_MASK(value) ((value >> 13) & 63)
++#define UCB1x00_SINGLE_INV(value) ((value >> 19) & 1)
++
++static int snd_ucb1x00_info_single(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_info *uinfo)
++{
++ int mask = UCB1x00_SINGLE_MASK(kcontrol->private_value);
++
++// printk(KERN_INFO "snd_ucb1x00_info_single called\n");
++
++ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
++ uinfo->count = 1;
++ uinfo->value.integer.min = 0;
++ uinfo->value.integer.max = mask;
++ return 0;
++}
++
++static int snd_ucb1x00_get_single(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct sa11xx_ucb1x00* chip = snd_kcontrol_chip(kcontrol);
++ struct ucb1x00 *ucb = chip->ucb1x00->ucb;
++ unsigned int retval;
++
++ int reg = UCB1x00_SINGLE_REG(kcontrol->private_value);
++ int shift = UCB1x00_SINGLE_SHIFT(kcontrol->private_value);
++ int mask = UCB1x00_SINGLE_MASK(kcontrol->private_value);
++ int invert= UCB1x00_SINGLE_INV(kcontrol->private_value);
++
++// printk(KERN_INFO "snd_ucb1x00_get_single called\n");
++
++ retval = (ucb1x00_reg_read(ucb, reg) >> shift) & mask;
++
++// printk(KERN_INFO "ucb1x00_reg_read reg=%02X value=%08X\n", reg, retval);
++
++ ucontrol->value.integer.value[0] = retval;
++ if (invert) {
++ ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
++ }
++
++ return 0;
++}
++
++static int snd_ucb1x00_put_single(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct sa11xx_ucb1x00* chip = snd_kcontrol_chip(kcontrol);
++ struct ucb1x00 *ucb = chip->ucb1x00->ucb;
++ int reg = UCB1x00_SINGLE_REG(kcontrol->private_value);
++ int shift = UCB1x00_SINGLE_SHIFT(kcontrol->private_value);
++ int mask = UCB1x00_SINGLE_MASK(kcontrol->private_value);
++ int invert= UCB1x00_SINGLE_INV(kcontrol->private_value);
++ unsigned short val;
++ unsigned int regval;
++
++ val = (ucontrol->value.integer.value[0] & mask);
++ if (invert)
++ val = mask - val;
++
++// printk(KERN_INFO "snd_ucb1x00_put_single called val=%d\n", val);
++
++ regval = ucb1x00_reg_read(ucb, reg);
++ regval &= ~(mask << shift);
++ regval |= val << shift;
++// printk(KERN_INFO "snd_ucb1x00_put_single write regval=%08X to reg %d\n", regval, reg);
++ ucb1x00_reg_write(ucb, reg, regval);
++ return 0;
++}
++
++enum ucb1x00_config {
++ CMD_OMUTE = 0,
++ CMD_VOLUME,
++ CMD_IGAIN,
++ CMD_IMUTE,
++ CMD_LOOPBACK,
++ CMD_CLIP,
++ CMD_OENA,
++};
++
++static struct snd_kcontrol_new snd_ucb1x00_controls[] = {
++ UCB1x00_SINGLE("Master Playback Switch", CMD_OMUTE, UCB_AC_B, 13, 0x01, 1),
++ UCB1x00_SINGLE("Master Playback Volume", CMD_VOLUME, UCB_AC_B, 0, 0x1F, 1),
++
++ UCB1x00_SINGLE("Input Gain", CMD_IGAIN, UCB_AC_A, 7, 0x1F, 1),
++// UCB1x00_SINGLE("Input Mute", CMD_IMUTE, UCB_AC_B, 14, 1, 1),
++// UCB1x00_SINGLE("Output Enabled", CMD_OENA, UCB_AC_B, 15, 1, 1),
++
++ UCB1x00_SINGLE("Audio Loopback", CMD_LOOPBACK, UCB_AC_B, 8, 1, 0),
++};
++
++static void sa11xx_ucb1x00_set_samplerate(struct sa11xx_ucb1x00 *sa11xx_ucb1x00, long rate)
++{
++ struct ucb1x00 *ucb = sa11xx_ucb1x00->ucb1x00->ucb;
++ unsigned int div_rate = ucb1x00_clkrate(ucb) / 32;
++ unsigned int div;
++ unsigned int reg;
++
++// printk(KERN_INFO "sa11xx_ucb1x00_set_samplerate called rate=%d div_rate=%d\n", rate, div_rate);
++
++ div = (div_rate + (rate / 2)) / rate;
++ if (div < 6) {
++ div = 6;
++ }
++ if (div > 127) {
++ div = 127;
++ }
++/*
++ printk(KERN_INFO "ucb=%08X\n", ucb);
++ printk(KERN_INFO "ucb1x00_set_audio_divisor(%d)\n", div*32);
++ printk(KERN_INFO "ucb1x00_reg_write(reg=%d, div=%d)\n", UCB_AC_A, div);
++*/
++ reg = ucb1x00_reg_read(ucb, UCB_AC_B);
++ reg &= ~(UCB_AC_B_IN_ENA | UCB_AC_B_OUT_ENA);
++ ucb1x00_reg_write(ucb, UCB_AC_B, reg);
++
++ // ucb1x00_reg_write(ucb, UCB_AC_B, 0);
++ ucb1x00_set_audio_divisor(ucb, div*32); /* set divisor in MCP */
++
++ reg = ucb1x00_reg_read(ucb, UCB_AC_A);
++ reg &= ~(0x1F);
++ reg |= div;
++ ucb1x00_reg_write(ucb, UCB_AC_A, reg); /* set divisor in UCB1x00 */
++
++ reg = ucb1x00_reg_read(ucb, UCB_AC_B);
++ reg |= UCB_AC_B_IN_ENA | UCB_AC_B_OUT_ENA;
++ ucb1x00_reg_write(ucb, UCB_AC_B, reg);
++
++ sa11xx_ucb1x00->samplerate = div_rate / div;
++
++// printk(KERN_INFO "sa11xx_ucb1x00_set_samplerate done\n");
++}
++
++/* HW init and shutdown */
++static void sa11xx_ucb1x00_audio_init(struct sa11xx_ucb1x00 *sa11xx_ucb1x00)
++{
++ //unsigned long flags;
++
++ /* Setup DMA stuff */
++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_PLAYBACK].id = "UCB1x00 out";
++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = SNDRV_PCM_STREAM_PLAYBACK;
++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev = sa11xx_ucb1x00->ucb1x00->ucb->mcp->dma_audio_wr;
++
++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_CAPTURE].id = "UCB1x00 in";
++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = SNDRV_PCM_STREAM_CAPTURE;
++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev = sa11xx_ucb1x00->ucb1x00->ucb->mcp->dma_audio_rd;
++}
++
++#if 0
++static void sa11xx_ucb1x00_audio_shutdown(struct sa11xx_ucb1x00 *sa11xx_ucb1x00)
++{
++ /* mute on */
++// set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
++
++ /* disable the audio power and all signals leading to the audio chip */
++// l3_close(sa11xx_uda1341->uda1341);
++// Ser4SSCR0 = 0;
++// clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
++
++ /* power off and mute off */
++ /* FIXME - is muting off necesary??? */
++
++// clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
++// clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
++}
++#endif
++
++/* DMA stuff */
++
++/*
++ * these are the address and sizes used to fill the xmit buffer
++ * so we can get a clock in record only mode
++ */
++//#define FORCE_CLOCK_ADDR (dma_addr_t)FLUSH_BASE_PHYS
++//#define FORCE_CLOCK_SIZE 4096 // was 2048
++
++// FIXME Why this value exactly - wrote comment
++#define DMA_BUF_SIZE 8176 /* <= MAX_DMA_SIZE from asm/arch-sa1100/dma.h */
++
++static int audio_dma_request(struct audio_stream *s, void (*callback)(void *))
++{
++ int ret;
++
++// printk(KERN_INFO "audio_dma_request stream=%08X\n", s);
++
++ ret = sa1100_request_dma(s->dma_dev, s->id, callback, s, &s->dma_regs);
++ if (ret < 0)
++ printk(KERN_ERR "unable to grab audio dma 0x%x\n", s->dma_dev);
++ return ret;
++}
++
++static void audio_dma_free(struct audio_stream *s)
++{
++// printk(KERN_INFO "audio_dma_free stream=%08X\n", s);
++ sa1100_free_dma(s->dma_regs);
++ s->dma_regs = 0;
++}
++
++static u_int audio_get_dma_pos(struct audio_stream *s)
++{
++ struct snd_pcm_substream *substream = s->stream;
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ unsigned int offset;
++ unsigned long flags;
++ dma_addr_t addr;
++
++ // this must be called w/ interrupts locked out see dma-sa1100.c in the kernel
++ spin_lock_irqsave(&s->dma_lock, flags);
++ addr = sa1100_get_dma_pos((s)->dma_regs);
++ offset = addr - runtime->dma_addr;
++ spin_unlock_irqrestore(&s->dma_lock, flags);
++
++// printk(KERN_INFO "audio_get_dma_pos real_offset=%d\n", offset);
++
++ offset = bytes_to_frames(runtime,offset);
++ if (offset >= runtime->buffer_size)
++ offset = 0;
++
++// printk(KERN_INFO "audio_get_dma_pos stream=%08X pos=%d\n", s, offset);
++
++ return offset;
++}
++
++/*
++ * this stops the dma and clears the dma ptrs
++ */
++static void audio_stop_dma(struct audio_stream *s)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&s->dma_lock, flags);
++ s->active = 0;
++ s->period = 0;
++ /* this stops the dma channel and clears the buffer ptrs */
++ sa1100_clear_dma(s->dma_regs);
++ spin_unlock_irqrestore(&s->dma_lock, flags);
++}
++
++static void audio_process_dma(struct audio_stream *s)
++{
++ struct snd_pcm_substream *substream = s->stream;
++ struct snd_pcm_runtime *runtime;
++ unsigned int dma_size;
++ unsigned int offset;
++ int ret;
++
++// printk(KERN_ERR "audio_process_dma called periods=%d period=%d\n", s->periods, s->period);
++
++ /* must be set here - only valid for running streams, not for forced_clock dma fills */
++ runtime = substream->runtime;
++ while (s->active && s->periods < runtime->periods) {
++ dma_size = frames_to_bytes(runtime, runtime->period_size);
++ if (s->old_offset) {
++ /* a little trick, we need resume from old position */
++ offset = frames_to_bytes(runtime, s->old_offset - 1);
++ s->old_offset = 0;
++ s->periods = 0;
++ s->period = offset / dma_size;
++ offset %= dma_size;
++ dma_size = dma_size - offset;
++ if (!dma_size)
++ continue; /* special case */
++ } else {
++ offset = dma_size * s->period;
++ snd_assert(dma_size <= DMA_BUF_SIZE, );
++ }
++// printk(KERN_INFO "start addr=%08X size=%d\n", runtime->dma_addr + offset, dma_size);
++ ret = sa1100_start_dma((s)->dma_regs, runtime->dma_addr + offset, dma_size);
++ if (ret) {
++// printk(KERN_ERR "audio_process_dma: cannot queue DMA buffer (%i)\n", ret);
++ return;
++ }
++
++ s->period++;
++ s->period %= runtime->periods;
++ s->periods++;
++ }
++}
++
++static void audio_dma_callback(void *data)
++{
++ struct audio_stream *s = data;
++
++// printk(KERN_INFO "audio_dma_callback called stream=%08X\n", s);
++
++ /*
++ * If we are getting a callback for an active stream then we inform
++ * the PCM middle layer we've finished a period
++ */
++ if (s->active) {
++// printk(KERN_INFO "audio_dma_callback period elapsed\n");
++ snd_pcm_period_elapsed(s->stream);
++ }
++
++ spin_lock(&s->dma_lock);
++ if (s->periods > 0) {
++ s->periods--;
++ }
++ audio_process_dma(s);
++ spin_unlock(&s->dma_lock);
++}
++
++static void sa11xx_ucb1x00_enable_audio(struct ucb1x00* ucb)
++{
++ uint32_t reg;
++ reg = ucb1x00_reg_read(ucb, UCB_AC_B);
++ reg |= UCB_AC_B_OUT_ENA;
++ ucb1x00_reg_write(ucb, UCB_AC_B, reg);
++}
++
++static void sa11xx_ucb1x00_disable_audio(struct ucb1x00* ucb)
++{
++ uint32_t reg;
++ reg = ucb1x00_reg_read(ucb, UCB_AC_B);
++ reg &= ~(UCB_AC_B_OUT_ENA);
++ ucb1x00_reg_write(ucb, UCB_AC_B, reg);
++}
++
++/* PCM setting */
++
++/* trigger & timer */
++static int snd_sa11xx_ucb1x00_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream);
++ int stream_id = substream->pstr->stream;
++ struct audio_stream *s = &chip->s[stream_id];
++ int err = 0;
++
++// printk(KERN_INFO "snd_sa11xx_ucb1x00_trigger called\n");
++
++ /* note local interrupts are already disabled in the midlevel code */
++ spin_lock(&s->dma_lock);
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ /* requested stream startup */
++ s->active = 1;
++ sa11xx_ucb1x00_enable_audio(chip->ucb1x00->ucb);
++ audio_process_dma(s);
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ /* requested stream shutdown */
++ sa11xx_ucb1x00_disable_audio(chip->ucb1x00->ucb);
++ audio_stop_dma(s);
++ break;
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ s->active = 0;
++ sa1100_stop_dma(s->dma_regs);
++ s->old_offset = audio_get_dma_pos(s) + 1;
++#ifdef HH_VERSION
++ sa1100_dma_flush_all(s->dma_regs);
++#else
++ //FIXME - DMA API
++#endif
++ s->periods = 0;
++ break;
++ case SNDRV_PCM_TRIGGER_RESUME:
++ s->active = 1;
++ audio_process_dma(s);
++ break;
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ sa1100_stop_dma(s->dma_regs);
++ s->active = 0;
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ s->active = 1;
++ if (s->old_offset) {
++ audio_process_dma(s);
++ break;
++ }
++ sa1100_resume_dma(s->dma_regs);
++ break;
++ default:
++ err = -EINVAL;
++ break;
++ }
++ spin_unlock(&s->dma_lock);
++ return err;
++}
++
++static int snd_sa11xx_ucb1x00_prepare(struct snd_pcm_substream *substream)
++{
++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream);
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct audio_stream *s = &chip->s[substream->pstr->stream];
++
++// printk(KERN_INFO "snd_sa11xx_ucb1x00_prepare called\n");
++
++ /* set requested samplerate */
++ sa11xx_ucb1x00_set_samplerate(chip, runtime->rate);
++
++ s->period = 0;
++ s->periods = 0;
++
++ return 0;
++}
++
++static snd_pcm_uframes_t snd_sa11xx_ucb1x00_pointer(struct snd_pcm_substream *substream)
++{
++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream);
++// printk(KERN_INFO "snd_sa11xx_ucb1x00_pointer called\n");
++ return audio_get_dma_pos(&chip->s[substream->pstr->stream]);
++}
++
++static struct snd_pcm_hardware snd_sa11xx_ucb1x00_hw =
++{
++ .info = (SNDRV_PCM_INFO_INTERLEAVED |
++ SNDRV_PCM_INFO_BLOCK_TRANSFER |
++ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
++ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,
++/* .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
++ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
++ SNDRV_PCM_RATE_KNOT),*/
++ .rate_min = 5000,
++ .rate_max = 48000,
++ .channels_min = 1,
++ .channels_max = 1,
++ .buffer_bytes_max = 64*1024,
++ .period_bytes_min = 4096,
++ .period_bytes_max = DMA_BUF_SIZE,
++ .periods_min = 2,
++ .periods_max = 255,
++ .fifo_size = 0,
++};
++
++#if 0
++static struct snd_pcm_hardware snd_sa11xx_ucb1x00_playback =
++{
++ .info = (SNDRV_PCM_INFO_INTERLEAVED |
++ SNDRV_PCM_INFO_BLOCK_TRANSFER |
++ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
++ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,
++ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
++ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
++ SNDRV_PCM_RATE_KNOT),
++ .rate_min = 8000,
++ .rate_max = 48000,
++ .channels_min = 2,
++ .channels_max = 2,
++ .buffer_bytes_max = 64*1024,
++ .period_bytes_min = 128,
++ .period_bytes_max = DMA_BUF_SIZE,
++ .periods_min = 2,
++ .periods_max = 8,
++ .fifo_size = 0,
++};
++#endif
++
++static int snd_card_sa11xx_ucb1x00_open(struct snd_pcm_substream *substream)
++{
++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream);
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ int stream_id = substream->pstr->stream;
++ //int err;
++
++ chip->s[stream_id].stream = substream;
++
++// printk(KERN_INFO "snd_card_sa11xx_ucb1x00_open called substream=%08X\n", substream);
++
++ runtime->hw = snd_sa11xx_ucb1x00_hw;
++ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
++ }
++ else {
++ }
++/*
++ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) {
++ return err;
++ }
++*/
++/* if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0) {
++ return err;
++ }
++*/
++ return 0;
++}
++
++static int snd_card_sa11xx_ucb1x00_close(struct snd_pcm_substream *substream)
++{
++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream);
++
++ chip->s[substream->pstr->stream].stream = NULL;
++// printk(KERN_INFO "snd_card_sa11xx_ucb1x00_close called substream=%08X\n", substream);
++ return 0;
++}
++
++/* HW params & free */
++
++static int snd_sa11xx_ucb1x00_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *hw_params)
++{
++// struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream);
++// struct ucb1x00* ucb = chip->ucb1x00->ucb;
++// printk(KERN_INFO "snd_sa11xx_ucb1x00_hw_params called substream=%08X\n", substream);
++
++
++ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
++}
++
++static int snd_sa11xx_ucb1x00_hw_free(struct snd_pcm_substream *substream)
++{
++// struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream);
++// struct ucb1x00* ucb = chip->ucb1x00->ucb;
++// printk(KERN_INFO "snd_sa11xx_ucb1x00_hw_free called substream=%08X\n", substream);
++
++ return snd_pcm_lib_free_pages(substream);
++}
++
++static struct snd_pcm_ops snd_card_sa11xx_ucb1x00_playback_ops = {
++ .open = snd_card_sa11xx_ucb1x00_open,
++ .close = snd_card_sa11xx_ucb1x00_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = snd_sa11xx_ucb1x00_hw_params,
++ .hw_free = snd_sa11xx_ucb1x00_hw_free,
++ .prepare = snd_sa11xx_ucb1x00_prepare,
++ .trigger = snd_sa11xx_ucb1x00_trigger,
++ .pointer = snd_sa11xx_ucb1x00_pointer,
++};
++
++static struct snd_pcm_ops snd_card_sa11xx_ucb1x00_capture_ops = {
++ .open = snd_card_sa11xx_ucb1x00_open,
++ .close = snd_card_sa11xx_ucb1x00_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = snd_sa11xx_ucb1x00_hw_params,
++ .hw_free = snd_sa11xx_ucb1x00_hw_free,
++ .prepare = snd_sa11xx_ucb1x00_prepare,
++ .trigger = snd_sa11xx_ucb1x00_trigger,
++ .pointer = snd_sa11xx_ucb1x00_pointer,
++};
++
++static int __init snd_card_sa11xx_ucb1x00_pcm(struct sa11xx_ucb1x00 *sa11xx_ucb1x00, int device)
++{
++ struct snd_pcm *pcm;
++ int err;
++
++ if ((err = snd_pcm_new(sa11xx_ucb1x00->card, "UCB1x00 PCM", device, 1, 1, &pcm)) < 0) {
++ return err;
++ }
++
++ sa11xx_ucb1x00_audio_init(sa11xx_ucb1x00);
++
++
++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_sa11xx_ucb1x00_playback_ops);
++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_sa11xx_ucb1x00_capture_ops);
++ pcm->private_data = sa11xx_ucb1x00;
++ pcm->info_flags = 0;
++ strcpy(pcm->name, "UCB1x00 PCM");
++
++ err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
++ NULL, 64*1024, 64*1024);
++// printk(KERN_INFO "snd_pcm_lib_preallocate_pages_for_all returns %d\n", err);
++
++
++ /* setup DMA controller */
++ audio_dma_request(&sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_PLAYBACK], audio_dma_callback);
++ audio_dma_request(&sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_CAPTURE], audio_dma_callback);
++
++ sa11xx_ucb1x00->pcm = pcm;
++
++ return 0;
++}
++
++#if 0
++#ifdef CONFIG_PM
++
++static int snd_sa11xx_ucb1x00_suspend(struct platform_device *devptr,
++ pm_message_t state)
++{
++ struct snd_card *card = platform_get_drvdata(devptr);
++ struct sa11xx_ucb1x00 *chip = card->private_data;
++
++ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
++ snd_pcm_suspend_all(chip->pcm);
++#ifdef HH_VERSION
++ sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach);
++ sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);
++#else
++ //FIXME
++#endif
++ l3_command(chip->ucb1x00, CMD_SUSPEND, NULL);
++ sa11xx_uda1341_audio_shutdown(chip);
++
++ return 0;
++}
++
++static int snd_sa11xx_ucb1x00_resume(struct platform_device *devptr)
++{
++ struct snd_card *card = platform_get_drvdata(devptr);
++ struct sa11xx_ucb1x00 *chip = card->private_data;
++
++ sa11xx_ucb1x00_audio_init(chip);
++ l3_command(chip->ucb1x00, CMD_RESUME, NULL);
++#ifdef HH_VERSION
++ sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach);
++ sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);
++#else
++ //FIXME
++#endif
++ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
++ return 0;
++}
++#endif /* COMFIG_PM */
++#endif
++
++void snd_sa11xx_ucb1x00_free(struct snd_card *card)
++{
++ struct sa11xx_ucb1x00 *chip = card->private_data;
++
++// printk(KERN_INFO "snd_sa11xx_ucb1x00_free called\n");
++
++ if (&chip->s[SNDRV_PCM_STREAM_PLAYBACK]) {
++ audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]);
++ }
++ if (&chip->s[SNDRV_PCM_STREAM_CAPTURE]) {
++ audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
++ }
++}
++
++static int ucb1x00_dev_free(struct snd_device *device)
++{
++ return 0;
++}
++
++static int snd_chip_ucb1x00_mixer_new(struct snd_card *card)
++{
++ static struct snd_device_ops ops = {
++ .dev_free = ucb1x00_dev_free,
++ };
++
++ struct sa11xx_ucb1x00 *chip = card->private_data;
++ int idx;
++ int err;
++
++ snd_assert(card != NULL, return -EINVAL);
++
++ for (idx = 0; idx < ARRAY_SIZE(snd_ucb1x00_controls); idx++) {
++ if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ucb1x00_controls[idx], chip))) < 0) {
++ return err;
++ }
++ }
++
++ if ((err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops)) < 0) {
++ return err;
++ }
++
++ strcpy(card->mixername, "UCB1x00 Mixer");
++ // ((struct ucb1x00_dev *)clnt->driver_data)->card = card;
++
++ return 0;
++}
++
++static int __init sa11xx_ucb1x00_audio_add(struct ucb1x00_dev* dev)
++{
++ int err;
++ struct snd_card *card;
++ struct sa11xx_ucb1x00 *chip;
++
++ /* register the soundcard */
++ card = snd_card_new(-1, id, THIS_MODULE, sizeof(struct sa11xx_ucb1x00));
++ if (card == NULL) {
++ return -ENOMEM;
++ }
++
++ ucb1x00_enable(dev->ucb);
++
++ chip = card->private_data;
++ spin_lock_init(&chip->s[0].dma_lock);
++ spin_lock_init(&chip->s[1].dma_lock);
++
++ card->private_free = snd_sa11xx_ucb1x00_free;
++ chip->card = card;
++ chip->samplerate = AUDIO_RATE_DEFAULT;
++ chip->ucb1x00 = dev;
++ chip->pdev = &dev->ucb->mcp->attached_device;
++ dev->priv = chip;
++
++ // mixer
++ if ((err = snd_chip_ucb1x00_mixer_new(card)))
++ goto nodev;
++
++ // PCM
++ if ((err = snd_card_sa11xx_ucb1x00_pcm(chip, 0)) < 0)
++ goto nodev;
++
++ strcpy(card->driver, "UCB1x00");
++ strcpy(card->shortname, "Simpad UCB1x00");
++ sprintf(card->longname, "Siemens Simpad with Philips UCB1x00");
++
++ snd_card_set_dev(card, chip->pdev);
++
++ if ((err = snd_card_register(card)) == 0) {
++ printk( KERN_INFO "Simpad audio support initialized 2\n" );
++ return 0;
++ }
++
++ nodev:
++ snd_card_free(card);
++ return err;
++}
++
++static void sa11xx_ucb1x00_audio_remove(struct ucb1x00_dev* dev)
++{
++ struct sa11xx_ucb1x00* chip = dev->priv;
++ printk(KERN_INFO "sa11xx_ucb1x00_audio_remove called\n");
++ ucb1x00_disable(dev->ucb);
++ snd_card_free(chip->card);
++}
++
++static int sa11xx_ucb1x00_audio_resume(struct ucb1x00_dev* dev)
++{
++ printk(KERN_INFO "sa11xx_ucb1x00_audio_resume called\n");
++ return 0;
++}
++
++/* ---- GOOD ----------------------------------------------------- */
++
++static struct ucb1x00_driver sa11xx_ucb1x00_audio_driver = {
++ .add = sa11xx_ucb1x00_audio_add,
++ .remove = sa11xx_ucb1x00_audio_remove,
++ .resume = sa11xx_ucb1x00_audio_resume,
++};
++
++static int __init sa11xx_ucb1x00_init(void)
++{
++ return ucb1x00_register_driver(&sa11xx_ucb1x00_audio_driver);
++}
++
++static void __exit sa11xx_ucb1x00_exit(void)
++{
++ ucb1x00_unregister_driver(&sa11xx_ucb1x00_audio_driver);
++}
++
++module_init(sa11xx_ucb1x00_init);
++module_exit(sa11xx_ucb1x00_exit);
++
+Index: linux-2.6.24/drivers/mfd/ucb1x00.h
+===================================================================
+--- linux-2.6.24.orig/drivers/mfd/ucb1x00.h 2008-12-23 22:00:03.000000000 +0100
++++ /dev/null 1970-01-01 00:00:00.000000000 +0000
+@@ -1,255 +0,0 @@
+-/*
+- * linux/drivers/mfd/ucb1x00.h
+- *
+- * Copyright (C) 2001 Russell King, All Rights Reserved.
+- *
+- * 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.
+- */
+-#ifndef UCB1200_H
+-#define UCB1200_H
+-
+-#define UCB_IO_DATA 0x00
+-#define UCB_IO_DIR 0x01
+-
+-#define UCB_IO_0 (1 << 0)
+-#define UCB_IO_1 (1 << 1)
+-#define UCB_IO_2 (1 << 2)
+-#define UCB_IO_3 (1 << 3)
+-#define UCB_IO_4 (1 << 4)
+-#define UCB_IO_5 (1 << 5)
+-#define UCB_IO_6 (1 << 6)
+-#define UCB_IO_7 (1 << 7)
+-#define UCB_IO_8 (1 << 8)
+-#define UCB_IO_9 (1 << 9)
+-
+-#define UCB_IE_RIS 0x02
+-#define UCB_IE_FAL 0x03
+-#define UCB_IE_STATUS 0x04
+-#define UCB_IE_CLEAR 0x04
+-#define UCB_IE_ADC (1 << 11)
+-#define UCB_IE_TSPX (1 << 12)
+-#define UCB_IE_TSMX (1 << 13)
+-#define UCB_IE_TCLIP (1 << 14)
+-#define UCB_IE_ACLIP (1 << 15)
+-
+-#define UCB_IRQ_TSPX 12
+-
+-#define UCB_TC_A 0x05
+-#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */
+-#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */
+-
+-#define UCB_TC_B 0x06
+-#define UCB_TC_B_VOICE_ENA (1 << 3)
+-#define UCB_TC_B_CLIP (1 << 4)
+-#define UCB_TC_B_ATT (1 << 6)
+-#define UCB_TC_B_SIDE_ENA (1 << 11)
+-#define UCB_TC_B_MUTE (1 << 13)
+-#define UCB_TC_B_IN_ENA (1 << 14)
+-#define UCB_TC_B_OUT_ENA (1 << 15)
+-
+-#define UCB_AC_A 0x07
+-#define UCB_AC_B 0x08
+-#define UCB_AC_B_LOOP (1 << 8)
+-#define UCB_AC_B_MUTE (1 << 13)
+-#define UCB_AC_B_IN_ENA (1 << 14)
+-#define UCB_AC_B_OUT_ENA (1 << 15)
+-
+-#define UCB_TS_CR 0x09
+-#define UCB_TS_CR_TSMX_POW (1 << 0)
+-#define UCB_TS_CR_TSPX_POW (1 << 1)
+-#define UCB_TS_CR_TSMY_POW (1 << 2)
+-#define UCB_TS_CR_TSPY_POW (1 << 3)
+-#define UCB_TS_CR_TSMX_GND (1 << 4)
+-#define UCB_TS_CR_TSPX_GND (1 << 5)
+-#define UCB_TS_CR_TSMY_GND (1 << 6)
+-#define UCB_TS_CR_TSPY_GND (1 << 7)
+-#define UCB_TS_CR_MODE_INT (0 << 8)
+-#define UCB_TS_CR_MODE_PRES (1 << 8)
+-#define UCB_TS_CR_MODE_POS (2 << 8)
+-#define UCB_TS_CR_BIAS_ENA (1 << 11)
+-#define UCB_TS_CR_TSPX_LOW (1 << 12)
+-#define UCB_TS_CR_TSMX_LOW (1 << 13)
+-
+-#define UCB_ADC_CR 0x0a
+-#define UCB_ADC_SYNC_ENA (1 << 0)
+-#define UCB_ADC_VREFBYP_CON (1 << 1)
+-#define UCB_ADC_INP_TSPX (0 << 2)
+-#define UCB_ADC_INP_TSMX (1 << 2)
+-#define UCB_ADC_INP_TSPY (2 << 2)
+-#define UCB_ADC_INP_TSMY (3 << 2)
+-#define UCB_ADC_INP_AD0 (4 << 2)
+-#define UCB_ADC_INP_AD1 (5 << 2)
+-#define UCB_ADC_INP_AD2 (6 << 2)
+-#define UCB_ADC_INP_AD3 (7 << 2)
+-#define UCB_ADC_EXT_REF (1 << 5)
+-#define UCB_ADC_START (1 << 7)
+-#define UCB_ADC_ENA (1 << 15)
+-
+-#define UCB_ADC_DATA 0x0b
+-#define UCB_ADC_DAT_VAL (1 << 15)
+-#define UCB_ADC_DAT(x) (((x) & 0x7fe0) >> 5)
+-
+-#define UCB_ID 0x0c
+-#define UCB_ID_1200 0x1004
+-#define UCB_ID_1300 0x1005
+-#define UCB_ID_TC35143 0x9712
+-
+-#define UCB_MODE 0x0d
+-#define UCB_MODE_DYN_VFLAG_ENA (1 << 12)
+-#define UCB_MODE_AUD_OFF_CAN (1 << 13)
+-
+-#include "mcp.h"
+-
+-struct ucb1x00_irq {
+- void *devid;
+- void (*fn)(int, void *);
+-};
+-
+-struct ucb1x00 {
+- spinlock_t lock;
+- struct mcp *mcp;
+- unsigned int irq;
+- struct semaphore adc_sem;
+- spinlock_t io_lock;
+- u16 id;
+- u16 io_dir;
+- u16 io_out;
+- u16 adc_cr;
+- u16 irq_fal_enbl;
+- u16 irq_ris_enbl;
+- struct ucb1x00_irq irq_handler[16];
+- struct class_device cdev;
+- struct list_head node;
+- struct list_head devs;
+-};
+-
+-struct ucb1x00_driver;
+-
+-struct ucb1x00_dev {
+- struct list_head dev_node;
+- struct list_head drv_node;
+- struct ucb1x00 *ucb;
+- struct ucb1x00_driver *drv;
+- void *priv;
+-};
+-
+-struct ucb1x00_driver {
+- struct list_head node;
+- struct list_head devs;
+- int (*add)(struct ucb1x00_dev *dev);