summaryrefslogtreecommitdiff
path: root/recipes/linux/linux-2.6.31/collie/0015-ucb1x00-sound-hack-collie-specific.patch
diff options
context:
space:
mode:
authorThomas Kunze <thommycheck@gmx.de>2009-10-29 23:27:10 +0100
committerThomas Kunze <thommycheck@gmx.de>2009-10-29 23:27:10 +0100
commitbbe9ae502bf711a30197ddf26dd91bc91f4cf421 (patch)
tree20ce7efb1b0bfba6b417c4557b951c3c7bbd6aaf /recipes/linux/linux-2.6.31/collie/0015-ucb1x00-sound-hack-collie-specific.patch
parentf410685d5fd63a1360b127fbf519e1baf4500bc6 (diff)
add collie patches for 2.6.31 kernel
moved the at91 from SRC_URI to SRC_URI_append_machine for the other two machines that use this kernel. Patches conflict otherwise.
Diffstat (limited to 'recipes/linux/linux-2.6.31/collie/0015-ucb1x00-sound-hack-collie-specific.patch')
-rw-r--r--recipes/linux/linux-2.6.31/collie/0015-ucb1x00-sound-hack-collie-specific.patch899
1 files changed, 899 insertions, 0 deletions
diff --git a/recipes/linux/linux-2.6.31/collie/0015-ucb1x00-sound-hack-collie-specific.patch b/recipes/linux/linux-2.6.31/collie/0015-ucb1x00-sound-hack-collie-specific.patch
new file mode 100644
index 0000000000..29ad090e63
--- /dev/null
+++ b/recipes/linux/linux-2.6.31/collie/0015-ucb1x00-sound-hack-collie-specific.patch
@@ -0,0 +1,899 @@
+From 7e2cf8ebfd553d025184aef2c0574119e95e84a4 Mon Sep 17 00:00:00 2001
+From: Thomas Kunze <thommycheck@gmx.de>
+Date: Sat, 10 Oct 2009 21:05:21 +0200
+Subject: [PATCH 15/15] ucb1x00 sound hack (collie specific)
+
+---
+ sound/arm/Kconfig | 10 +
+ sound/arm/Makefile | 3 +
+ sound/arm/sa11xx-ucb1x00.c | 843 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 856 insertions(+), 0 deletions(-)
+ create mode 100644 sound/arm/sa11xx-ucb1x00.c
+
+diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
+index 885683a..068962e 100644
+--- a/sound/arm/Kconfig
++++ b/sound/arm/Kconfig
+@@ -11,6 +11,16 @@ menuconfig SND_ARM
+
+ if SND_ARM
+
++config SND_SA11XX_UCB1X00
++ tristate "SA11xx UCB1x00 driver (Simpad)"
++ depends on ARCH_SA1100 && SND
++ select SND_PCM
++ help
++ Say Y here if you have a Simpad handheld computer
++
++ To compile this driver as a module, choose M here: the module
++ will be called snd-sa11xx-ucb1x00.
++
+ config SND_ARMAACI
+ tristate "ARM PrimeCell PL041 AC Link support"
+ depends on ARM_AMBA
+diff --git a/sound/arm/Makefile b/sound/arm/Makefile
+index 5a549ed..48a25d0 100644
+--- a/sound/arm/Makefile
++++ b/sound/arm/Makefile
+@@ -2,6 +2,9 @@
+ # Makefile for ALSA
+ #
+
++obj-$(CONFIG_SND_SA11XX_UCB1X00) += snd-sa11xx-ucb1x00.o
++snd-sa11xx-ucb1x00-objs := sa11xx-ucb1x00.o
++
+ obj-$(CONFIG_SND_ARMAACI) += snd-aaci.o
+ snd-aaci-objs := aaci.o devdma.o
+
+diff --git a/sound/arm/sa11xx-ucb1x00.c b/sound/arm/sa11xx-ucb1x00.c
+new file mode 100644
+index 0000000..e93e7d8
+--- /dev/null
++++ b/sound/arm/sa11xx-ucb1x00.c
+@@ -0,0 +1,843 @@
++/*
++ * 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 <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
++
++/* extra register defines for collieis tc35143af */
++#define UCB_AC_A_MIC2_SEL (1 << 15)
++#define UCB_AC_A_OUT2_ENA (1 << 14)
++#define UCB_AC_B_IN_MUTE (1 << 13)
++#define UCB_AC_B_OUT_MUTE (1 << 5)
++
++
++
++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, 5, 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, 0x3F, 1),
++ UCB1x00_SINGLE("Input Mute", CMD_IMUTE, UCB_AC_B, 13, 1, 0),
++ UCB1x00_SINGLE("Output Mute", CMD_OMUTE, UCB_AC_B, 5, 1, 0),
++
++ 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);
++ ucb1x00_reg_write(ucb, UCB_AC_B, reg);
++
++ reg = ucb1x00_reg_read(ucb, UCB_AC_A);
++ reg &= ~(UCB_AC_A_OUT2_ENA);
++ ucb1x00_reg_write(ucb, UCB_AC_A, reg);
++
++ 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;
++ ucb1x00_reg_write(ucb, UCB_AC_B, reg);
++
++ reg = ucb1x00_reg_read(ucb, UCB_AC_A);
++ reg |= UCB_AC_A_OUT2_ENA;
++ ucb1x00_reg_write(ucb, UCB_AC_A, 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_BUG_ON(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_A);
++ reg |= UCB_AC_A_OUT2_ENA;
++ ucb1x00_reg_write(ucb, UCB_AC_A, reg);
++}
++
++static void sa11xx_ucb1x00_disable_audio(struct ucb1x00* ucb)
++{
++ uint32_t reg;
++ reg = ucb1x00_reg_read(ucb, UCB_AC_A);
++ reg &= ~(UCB_AC_A_OUT2_ENA);
++ ucb1x00_reg_write(ucb, UCB_AC_A, 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_BUG_ON(card == NULL);
++
++ 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 */
++ err = snd_card_create(-1, id, THIS_MODULE, sizeof(struct sa11xx_ucb1x00), &card);
++ if (err < 0)
++ return -err;
++
++
++ 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);
++
+--
+1.6.0.4
+