From 7e2cf8ebfd553d025184aef2c0574119e95e84a4 Mon Sep 17 00:00:00 2001 From: Thomas Kunze 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PM +#include +#endif + +#include + +#include +#include +#include +#include +#include + +#include + +#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 "); +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