?? sa11xx-uda1341.c
字號:
/* * Driver for Philips UDA1341TS on Compaq iPAQ H3600 soundcard * Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License. * * History: * * 2002-03-13 Tomas Kasparek initial release - based on h3600-uda1341.c from OSS * 2002-03-20 Tomas Kasparek playback over ALSA is working * 2002-03-28 Tomas Kasparek playback over OSS emulation is working * 2002-03-29 Tomas Kasparek basic capture is working (native ALSA) * 2002-03-29 Tomas Kasparek capture is working (OSS emulation) * 2002-04-04 Tomas Kasparek better rates handling (allow non-standard rates) * 2003-02-14 Brian Avery fixed full duplex mode, other updates * 2003-02-20 Tomas Kasparek merged updates by Brian (except HAL) * 2003-04-19 Jaroslav Kysela recoded DMA stuff to follow 2.4.18rmk3-hh24 kernel * working suspend and resume * 2003-04-28 Tomas Kasparek updated work by Jaroslav to compile it under 2.5.x again * merged HAL layer (patches from Brian) *//* $Id: sa11xx-uda1341.c,v 1.27 2005/12/07 09:13:42 cladisch Exp $ *//***************************************************************************************************** To understand what Alsa Drivers should be doing look at "Writing an Alsa Driver" by Takashi Iwai* available in the Alsa doc section on the website * * A few notes to make things clearer. The UDA1341 is hooked up to Serial port 4 on the SA1100.* We are using SSP mode to talk to the UDA1341. The UDA1341 bit & wordselect clocks are generated* by this UART. Unfortunately, the clock only runs if the transmit buffer has something in it.* So, if we are just recording, we feed the transmit DMA stream a bunch of 0x0000 so that the* transmit buffer is full and the clock keeps going. The zeroes come from FLUSH_BASE_PHYS which* is a mem loc that always decodes to 0's w/ no off chip access.** Some alsa terminology:* frame => num_channels * sample_size e.g stereo 16 bit is 2 * 16 = 32 bytes* period => the least number of bytes that will generate an interrupt e.g. we have a 1024 byte* buffer and 4 periods in the runtime structure this means we'll get an int every 256* bytes or 4 times per buffer.* A number of the sizes are in frames rather than bytes, use frames_to_bytes and* bytes_to_frames to convert. The easiest way to tell the units is to look at the* type i.e. runtime-> buffer_size is in frames and its type is snd_pcm_uframes_t* * Notes about the pointer fxn:* The pointer fxn needs to return the offset into the dma buffer in frames.* Interrupts must be blocked before calling the dma_get_pos fxn to avoid race with interrupts.** Notes about pause/resume* Implementing this would be complicated so it's skipped. The problem case is:* A full duplex connection is going, then play is paused. At this point you need to start xmitting* 0's to keep the record active which means you cant just freeze the dma and resume it later you'd* need to save off the dma info, and restore it properly on a resume. Yeach!** Notes about transfer methods:* The async write calls fail. I probably need to implement something else to support them?* ***************************************************************************************************/#include <linux/config.h>#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/hardware.h>#include <asm/arch/h3600.h>#include <asm/mach-types.h>#include <asm/dma.h>#ifdef CONFIG_H3600_HAL#include <asm/semaphore.h>#include <asm/uaccess.h>#include <asm/arch/h3600_hal.h>#endif#include <sound/core.h>#include <sound/pcm.h>#include <sound/initval.h>#include <linux/l3/l3.h>#undef DEBUG_MODE#undef DEBUG_FUNCTION_NAMES#include <sound/uda1341.h>/* * FIXME: Is this enough as autodetection of 2.4.X-rmkY-hhZ kernels? * We use DMA stuff from 2.4.18-rmk3-hh24 here to be able to compile this * module for Familiar 0.6.1 */#ifdef CONFIG_H3600_HAL#define HH_VERSION 1#endif/* {{{ Type definitions */MODULE_AUTHOR("Tomas Kasparek <tomas.kasparek@seznam.cz>");MODULE_LICENSE("GPL");MODULE_DESCRIPTION("SA1100/SA1111 + UDA1341TS driver for ALSA");MODULE_SUPPORTED_DEVICE("{{UDA1341,iPAQ H3600 UDA1341TS}}");static char *id = NULL; /* ID for this card */module_param(id, charp, 0444);MODULE_PARM_DESC(id, "ID string for SA1100/SA1111 + UDA1341TS soundcard.");struct audio_stream { char *id; /* identification string */ int stream_id; /* numeric identification */ dma_device_t dma_dev; /* device identifier for DMA */#ifdef HH_VERSION dmach_t dmach; /* dma channel identification */#else dma_regs_t *dma_regs; /* points to our DMA registers */#endif 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 */ int tx_spin; /* are we recoding - flag used to do DMA trans. for sync */ 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_uda1341 { struct snd_card *card; struct l3_client *uda1341; struct snd_pcm *pcm; long samplerate; struct audio_stream s[2]; /* playback & capture */};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,};static struct platform_device *device;/* }}} *//* {{{ Clock and sample rate stuff *//* * Stop-gap solution until rest of hh.org HAL stuff is merged. */#define GPIO_H3600_CLK_SET0 GPIO_GPIO (12)#define GPIO_H3600_CLK_SET1 GPIO_GPIO (13)#ifdef CONFIG_SA1100_H3XXX#define clr_sa11xx_uda1341_egpio(x) clr_h3600_egpio(x)#define set_sa11xx_uda1341_egpio(x) set_h3600_egpio(x)#else#error This driver could serve H3x00 handhelds only!#endifstatic void sa11xx_uda1341_set_audio_clock(long val){ switch (val) { case 24000: case 32000: case 48000: /* 00: 12.288 MHz */ GPCR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1; break; case 22050: case 29400: case 44100: /* 01: 11.2896 MHz */ GPSR = GPIO_H3600_CLK_SET0; GPCR = GPIO_H3600_CLK_SET1; break; case 8000: case 10666: case 16000: /* 10: 4.096 MHz */ GPCR = GPIO_H3600_CLK_SET0; GPSR = GPIO_H3600_CLK_SET1; break; case 10985: case 14647: case 21970: /* 11: 5.6245 MHz */ GPSR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1; break; }}static void sa11xx_uda1341_set_samplerate(struct sa11xx_uda1341 *sa11xx_uda1341, long rate){ int clk_div = 0; int clk=0; /* We don't want to mess with clocks when frames are in flight */ Ser4SSCR0 &= ~SSCR0_SSE; /* wait for any frame to complete */ udelay(125); /* * We have the following clock sources: * 4.096 MHz, 5.6245 MHz, 11.2896 MHz, 12.288 MHz * Those can be divided either by 256, 384 or 512. * This makes up 12 combinations for the following samplerates... */ if (rate >= 48000) rate = 48000; else if (rate >= 44100) rate = 44100; else if (rate >= 32000) rate = 32000; else if (rate >= 29400) rate = 29400; else if (rate >= 24000) rate = 24000; else if (rate >= 22050) rate = 22050; else if (rate >= 21970) rate = 21970; else if (rate >= 16000) rate = 16000; else if (rate >= 14647) rate = 14647; else if (rate >= 10985) rate = 10985; else if (rate >= 10666) rate = 10666; else rate = 8000; /* Set the external clock generator */#ifdef CONFIG_H3600_HAL h3600_audio_clock(rate);#else sa11xx_uda1341_set_audio_clock(rate);#endif /* Select the clock divisor */ switch (rate) { case 8000: case 10985: case 22050: case 24000: clk = F512; clk_div = SSCR0_SerClkDiv(16); break; case 16000: case 21970: case 44100: case 48000: clk = F256; clk_div = SSCR0_SerClkDiv(8); break; case 10666: case 14647: case 29400: case 32000: clk = F384; clk_div = SSCR0_SerClkDiv(12); break; } /* FMT setting should be moved away when other FMTs are added (FIXME) */ l3_command(sa11xx_uda1341->uda1341, CMD_FORMAT, (void *)LSB16); l3_command(sa11xx_uda1341->uda1341, CMD_FS, (void *)clk); Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE; sa11xx_uda1341->samplerate = rate;}/* }}} *//* {{{ HW init and shutdown */static void sa11xx_uda1341_audio_init(struct sa11xx_uda1341 *sa11xx_uda1341){ unsigned long flags; /* Setup DMA stuff */ sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].id = "UDA1341 out"; sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = SNDRV_PCM_STREAM_PLAYBACK; sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev = DMA_Ser4SSPWr; sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].id = "UDA1341 in"; sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = SNDRV_PCM_STREAM_CAPTURE; sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev = DMA_Ser4SSPRd; /* Initialize the UDA1341 internal state */ /* Setup the uarts */ local_irq_save(flags); GAFR |= (GPIO_SSP_CLK); GPDR &= ~(GPIO_SSP_CLK); Ser4SSCR0 = 0; Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(8); Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk; Ser4SSCR0 |= SSCR0_SSE; local_irq_restore(flags); /* Enable the audio power */#ifdef CONFIG_H3600_HAL h3600_audio_power(AUDIO_RATE_DEFAULT);#else clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET); set_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON); set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);#endif /* Wait for the UDA1341 to wake up */ mdelay(1); //FIXME - was removed by Perex - Why? /* Initialize the UDA1341 internal state */ l3_open(sa11xx_uda1341->uda1341); /* external clock configuration (after l3_open - regs must be initialized */ sa11xx_uda1341_set_samplerate(sa11xx_uda1341, sa11xx_uda1341->samplerate); /* Wait for the UDA1341 to wake up */ set_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET); mdelay(1); /* make the left and right channels unswapped (flip the WS latch) */ Ser4SSDR = 0;#ifdef CONFIG_H3600_HAL h3600_audio_mute(0);#else clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE); #endif }static void sa11xx_uda1341_audio_shutdown(struct sa11xx_uda1341 *sa11xx_uda1341){ /* mute on */#ifdef CONFIG_H3600_HAL h3600_audio_mute(1);#else set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);#endif /* 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??? */#ifdef CONFIG_H3600_HAL h3600_audio_power(0); h3600_audio_mute(0);#else clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON); clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);#endif }/* }}} *//* {{{ DMA staff *//* * 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 */#ifdef HH_VERSIONstatic int audio_dma_request(struct audio_stream *s, void (*callback)(void *, int)){ int ret; ret = sa1100_request_dma(&s->dmach, s->id, s->dma_dev); if (ret < 0) { printk(KERN_ERR "unable to grab audio dma 0x%x\n", s->dma_dev); return ret; } sa1100_dma_set_callback(s->dmach, callback); return 0;}static inline void audio_dma_free(struct audio_stream *s){ sa1100_free_dma(s->dmach); s->dmach = -1;}#elsestatic int audio_dma_request(struct audio_stream *s, void (*callback)(void *)){ int ret; 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){ sa1100_free_dma(s->dma_regs); s->dma_regs = 0;}#endifstatic 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);#ifdef HH_VERSION sa1100_dma_get_current(s->dmach, NULL, &addr);#else addr = sa1100_get_dma_pos((s)->dma_regs);#endif offset = addr - runtime->dma_addr; spin_unlock_irqrestore(&s->dma_lock, flags); offset = bytes_to_frames(runtime,offset); if (offset >= runtime->buffer_size) offset = 0; 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 */#ifdef HH_VERSION sa1100_dma_flush_all(s->dmach);#else sa1100_clear_dma(s->dma_regs); #endif 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; /* we are requested to process synchronization DMA transfer */ if (s->tx_spin) { snd_assert(s->stream_id == SNDRV_PCM_STREAM_PLAYBACK, return); /* fill the xmit dma buffers and return */#ifdef HH_VERSION sa1100_dma_set_spin(s->dmach, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE);#else while (1) { ret = sa1100_start_dma(s->dma_regs, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE); if (ret) return; }#endif return; } /* 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;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -