📄 i2s.patch
字号:
Index: linux-2.6.11-rc5/include/asm-mips/mach-au1x00/au1xxx_psc.h===================================================================--- linux-2.6.11-rc5.orig/include/asm-mips/mach-au1x00/au1xxx_psc.h+++ linux-2.6.11-rc5/include/asm-mips/mach-au1x00/au1xxx_psc.h@@ -230,6 +230,7 @@ typedef struct psc_i2s { #define PSC_I2SCFG_DD_DISABLE (1 << 27) #define PSC_I2SCFG_DE_ENABLE (1 << 26)+#define PSC_I2SCFG_WS_MASK (0x3F<<16) #define PSC_I2SCFG_SET_WS(x) (((((x) / 2) - 1) & 0x7f) << 16) #define PSC_I2SCFG_WI (1 << 15) Index: linux-2.6.11-rc5/sound/oss/au1550_i2s.c===================================================================--- linux-2.6.11-rc5.orig/sound/oss/au1550_i2s.c+++ linux-2.6.11-rc5/sound/oss/au1550_i2s.c@@ -4,21 +4,35 @@ * * Copyright 2004 Embedded Edge, LLC * dan@embeddededge.com- * Copyright 2005 Matt Porter <mporter@kernel.crashing.org> *- * Mostly copied from the au1550_psc.c driver and some from the- * PowerMac dbdma driver.- * We assume the processor can do memory coherent DMA.+ * Modified for platform independence -- 5/2/2005+ * Chris Gray (chris.gray@amd.com) *- * WM8731 mixer support, codec framework, cleanup, and 2.6 port- * Matt Porter <mporter@kernel.crashing.org>+ * This module was reworked to support some of the various hw+ * platforms that use Au1550/Au1200 and I2S codecs. Most of+ * the Wolfson CODECs I have encountred do not implement readable+ * registers. Because of this, I implemented local copy of hw+ * mixer settings. Another drawback is that I2S will often mux+ * inputs to ADC. *- * The SMBus (I2C) is required for the control of the + * Anyway, because of the limitations and "funkiness" of I2S, I+ * created another file for platform specific implementation. I + * I gave it the name au1550_i2s_hw.c, for the lack of creativity.+ *+ * Mostly copied from the au1550_psc.c.+ *+ * The SMBus (I2C) is required for the control of the codec. It * appears at I2C address 0x36 (I2C binary 0011011). The Pb1550 * uses the Wolfson WM8731 codec, which is controlled over the I2C.+ * + * Pb/Db1550 Pb/Db1200 Notes: * It's connected to a 12MHz clock, so we can only reliably support- * 96KHz, 48KHz, 32KHz, and 8KHz data rates. Variable rate audio is- * unsupported, we currently force it to 48KHz.+ * 96KHz, 48KHz, 32KHz, and 8KHz data rates. The framework for variable+ * rate audio is in place, but we currently force it to 48KHz. This+ * is only true of the PSC is running as the I2S Master.+ * In the au1550_i2s_hw.c file I configure the CODEC to run as + * I2S Master while the Au1xxx device is slave running in slave mode+ * (SERCLK). We can now support any sample rate the CODEC supports. * * 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@@ -40,15 +54,15 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. *+ * */+ #include <linux/version.h> #include <linux/module.h> #include <linux/string.h> #include <linux/ioport.h> #include <linux/sched.h> #include <linux/delay.h>-#include <linux/fs.h>-#include <linux/hardirq.h> #include <linux/sound.h> #include <linux/slab.h> #include <linux/soundcard.h>@@ -59,220 +73,78 @@ #include <linux/proc_fs.h> #include <linux/spinlock.h> #include <linux/smp_lock.h>-+#include <linux/hardirq.h> #include <asm/io.h> #include <asm/uaccess.h>-#include <asm/hardirq.h>- #include <asm/mach-au1x00/au1000.h> #include <asm/mach-au1x00/au1xxx_psc.h> #include <asm/mach-au1x00/au1xxx_dbdma.h>-#include <asm/mach-pb1x00/pb1550.h> -#undef OSS_DOCUMENTED_MIXER_SEMANTICS+/*Power Management*/ -#define AU1550_MODULE_NAME "Au1550 I2S Audio"-#define PFX AU1550_MODULE_NAME+#ifdef CONFIG_PM+#include <asm/mach-au1x00/au1xxx_pm.h>+#endif -/* Define this if you want to try running at the 44.1 KHz rate.- * It's just a little off, I think it's actually 44117 or something.- * I did this for debugging, since many programs, including this- * driver, will try to upsample from 44.1 to 48 KHz.- * Seems to work well, we'll just leave it this way.- */-#define TRY_441KHz+#ifdef CONFIG_MIPS_PB1550+#include <asm/mach-pb1x00/pb1550.h>+#endif -#ifdef TRY_441KHz-#define SAMP_RATE 44100-#else-#define SAMP_RATE 48000+#ifdef CONFIG_MIPS_DB1550+#include <asm/mach-db1x00/db1x00.h>+#endif++#ifdef CONFIG_MIPS_PB1200+#include <asm/mach-pb1x00/pb1200.h> #endif +#ifdef CONFIG_MIPS_DB1200+#include <asm/mach-db1x00/db1200.h>+#endif++#include "au1550_i2s.h"++#undef OSS_DOCUMENTED_MIXER_SEMANTICS++static int vra = 0;+ /* The number of DBDMA ring descriptors to allocate. No sense making * this too large....if you can't keep up with a few you aren't likely * to be able to with lots of them, either.+ *+ * CTG -- by cranking these up I was able to play 88200 correctly. 4/26/05 */-#define NUM_DBDMA_DESCRIPTORS 4+#define NUM_DBDMA_DESCRIPTORS 16+#define MIN_Q_COUNT 4 -#define pr_error(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)+MODULE_PARM(vra, "i");+MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it"); -static void-au1550_delay(int msec)-{- unsigned long tmo;- signed long tmo2;+/*Power Management*/ - if (in_interrupt())- return;+#ifdef CONFIG_PM - tmo = jiffies + (msec * HZ) / 1000;- for (;;) {- tmo2 = tmo - jiffies;- if (tmo2 <= 0)- break;- schedule_timeout(tmo2);- }-} /*- * Codec framework. If somebody supports another codec, they- * should hopefully be able to define another struct i2s_codec- * definition, and #ifdef the support for it and the WM8731 so- * they can be selected via a CONFIG option. For now, we just- * hardcode WM8731_CODEC.+ * This will enable the device/codec to be powered up when write() or read()+ * is called. If this is not defined, the driver will return -EBUSY. */-#define i2s_supported_mixer(CODEC,FOO) ((FOO >= 0) && \- (FOO < SOUND_MIXER_NRDEVICES) && \- (CODEC)->supported_mixers & (1<<FOO) )--struct i2s_codec {- int modcnt;- int supported_mixers;- int stereo_mixers;- int record_sources;- unsigned int mixer_state[SOUND_MIXER_NRDEVICES];- void *data;- int (*set_mixer) (struct i2s_codec *codec, unsigned int oss_mixer, unsigned int val);- void (*init_codec) (struct i2s_codec *codec);-};+#define WAKE_ON_ACCESS 1+ -#define WM8731_CODEC-#ifdef WM8731_CODEC /*- * WM8731 codec support+ * Support two nodes in this driver, ADC and DAC. They are seperate in regards to+ * power management control. But the driver needs to check both when determining+ * the overall power management of the CODEC. If ADC is being told to turn off,+ * and the DAC is already off... then we can safely turn off everything else in + * the CODEC. */-#define WM8731_SUPPORTED_MASK (WM8731_STEREO_MASK|WM8731_RECORD_MASK)-#define WM8731_STEREO_MASK (SOUND_MASK_VOLUME|SOUND_MASK_LINE)-#define WM8731_RECORD_MASK (SOUND_MASK_MIC|SOUND_MASK_LINE)--static struct codec_data {- u16 audio_path;-} wm8731_data;--static void-wm8731_wrcodec(u8 ctlreg, u8 val)+typedef struct {- int rcnt;- extern int pb1550_wm_codec_write(u8 addr, u8 reg, u8 val);-- /* The codec is a write only device, with a 16-bit control/data- * word. Although it is written as two bytes on the I2C, the- * format is actually 7 bits of register and 9 bits of data.- * The ls bit of the first byte is the ms bit of the data.- */- rcnt = 0;- while ((pb1550_wm_codec_write((0x36 >> 1), ctlreg, val) != 1) - && (rcnt < 50)) {- rcnt++;- }-}--static int-wm8731_set_mixer(struct i2s_codec *codec, unsigned int oss_mixer, unsigned int val)-{- unsigned int lvol, rvol;- struct codec_data *cdata = (struct codec_data *)codec->data;-- switch (oss_mixer) {- case SOUND_MIXER_VOLUME:- /* normalize OSS range to fit codec volume control */- lvol = ((((val & 0x7f00) >> 8) * 0x60) / 0x64) + 0x1f;- rvol = (((val & 0x7f) * 0x60) / 0x64) + 0x1f;- lvol |= 0x80;- rvol |= 0x80;- wm8731_wrcodec(0x04, lvol);- au1550_delay(10);- wm8731_wrcodec(0x06, rvol);- au1550_delay(10);- codec->mixer_state[oss_mixer] = val;- break;- case SOUND_MIXER_LINE:- /* normalize OSS range to fit codec line control */- lvol = ((((val & 0x7f00) >> 8) * 0x1f) / 0x64);- rvol = (((val & 0x7f) * 0x1f) / 0x64);- if (!(val & 0x1f00))- lvol |= 0x80;- else- lvol &= ~0x80;- if (!(val & 0x001f))- rvol |= 0x80;- else- rvol &= ~0x80;- wm8731_wrcodec(0x00, lvol);- au1550_delay(10);- wm8731_wrcodec(0x02, rvol);- au1550_delay(10);- codec->mixer_state[oss_mixer] = val;- break;- case SOUND_MIXER_MIC:- if (!val)- cdata->audio_path |= 0x02;- else {- if (val >= 0x32)- cdata->audio_path |= 0x01;- else- cdata->audio_path &= ~0x01;- cdata->audio_path &= ~0x02;- }- wm8731_wrcodec(0x08, cdata->audio_path);- au1550_delay(10);- codec->mixer_state[oss_mixer] = val;- break;- case SOUND_MIXER_RECSRC:- if (val & SOUND_MASK_LINE)- cdata->audio_path &= ~0x04;- else- cdata->audio_path |= 0x04;- wm8731_wrcodec(0x08, cdata->audio_path);- au1550_delay(10);- codec->mixer_state[oss_mixer] = val;- break;- default:- return -EINVAL;- }-- return 0;-}--void-wm8731_init_codec(struct i2s_codec *codec)-{- struct codec_data *cdata = (struct codec_data *)codec->data;-- wm8731_wrcodec(0x1e, 0x00); /* Reset */- au1550_delay(200);- wm8731_wrcodec(0x0c, 0x00); /* Power up everything */- au1550_delay(10);- wm8731_wrcodec(0x12, 0x00); /* Deactivate codec */- au1550_delay(10);- cdata->audio_path = 0x10;- /* Select DAC outputs to line out */- wm8731_wrcodec(0x08, cdata->audio_path);- au1550_delay(10);- wm8731_wrcodec(0x0a, 0x00); /* Disable output mute */- au1550_delay(10);- wm8731_wrcodec(0x0e, 0x02); /* Set slave, 16-bit, I2S modes */- au1550_delay(10);- wm8731_wrcodec(0x10, 0x01); /* 12MHz (USB), 250fs */- au1550_delay(10);- wm8731_wrcodec(0x12, 0x01); /* Activate codec */- au1550_delay(10);-- codec->set_mixer(codec, SOUND_MIXER_VOLUME, 0x5050);- codec->set_mixer(codec, SOUND_MIXER_LINE, 0x0000);- codec->set_mixer(codec, SOUND_MIXER_MIC, 0x00);- codec->mixer_state[SOUND_MIXER_RECSRC] = SOUND_MIXER_LINE;-}--static struct i2s_codec au1550_i2s_codec = {- .supported_mixers = WM8731_SUPPORTED_MASK,- .stereo_mixers = WM8731_STEREO_MASK,- .record_sources = WM8731_RECORD_MASK,- .init_codec = &wm8731_init_codec,- .set_mixer = &wm8731_set_mixer,- .data = &wm8731_data,-};-#endif /* WM8731_CODEC */+ spinlock_t lock; /* Used to block on state transitions */+ au1xxx_power_dev_t *dev; /* Power Managers device structure */+} pm_state;+#endif static struct au1550_state { /* soundcore stuff */@@ -284,8 +156,13 @@ static struct au1550_state { struct semaphore sem; mode_t open_mode; wait_queue_head_t open_wait;- volatile psc_i2s_t *psc_addr;- struct i2s_codec *codec;+ int no_vra;+ struct i2s_hw * i2s;++ int level_line;+ int level_mic;+ int level_left;+ int level_right; struct dmabuf { u32 dmanr;@@ -321,12 +198,16 @@ static struct au1550_state { unsigned ossfragshift; int ossmaxfrags; unsigned subdivision;-- /* Mixer stuff */- int dev_mixer;+#ifdef CONFIG_PM+ pm_state pm;+#endif } dma_dac, dma_adc; } au1550_state; +static void powerup_adc(struct au1550_state *s); +static void powerup_dac(struct au1550_state *s);++ static unsigned ld2(unsigned int x) {@@ -353,18 +234,202 @@ ld2(unsigned int x) return r; } +static void+au1550_delay(int msec)+{+ unsigned long tmo;+ signed long tmo2;++ if (in_interrupt())+ return;++ tmo = jiffies + (msec * HZ) / 1000;+ for (;;) {+ tmo2 = tmo - jiffies;+ if (tmo2 <= 0)+ break;+ schedule_timeout(tmo2);+ }+}++static int+au1550_open_mixdev(struct inode *inode, struct file *file)+{+ file->private_data = &au1550_state;+ return 0;+}++static int+au1550_release_mixdev(struct inode *inode, struct file *file)+{+ return 0;+}++static int+au1550_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)+{+ struct au1550_state *s = (struct au1550_state *)file->private_data;++ int i, val = 0;++ if (cmd == SOUND_MIXER_INFO) {+ mixer_info info;+ strncpy(info.id, s->i2s->name, sizeof(info.id));+ strncpy(info.name, s->i2s->name, sizeof(info.name));+ info.modify_counter = 0;+ if (copy_to_user((void *)arg, &info, sizeof(info)))+ return -EFAULT;+ return 0;+ }+ if (cmd == SOUND_OLD_MIXER_INFO) {+ _old_mixer_info info;+ strncpy(info.id, s->i2s->name, sizeof(info.id));+ strncpy(info.name, s->i2s->name, sizeof(info.name));+ if (copy_to_user((void *)arg, &info, sizeof(info)))+ return -EFAULT;+ return 0;+ }++ if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))+ return -EINVAL;++ if (cmd == OSS_GETVERSION)+ return put_user(SOUND_VERSION, (int *)arg);++ if (_SIOC_DIR(cmd) == _SIOC_READ) {+ switch (_IOC_NR(cmd)) {+ case SOUND_MIXER_RECSRC: /* give them the current record src */+ + + if (!s->i2s->input_mask){+ val=0;+ }+ else+ {+ val=s->i2s->get_recsrc();+ }+ + break;++ case SOUND_MIXER_DEVMASK: /* give them the supported mixers */+ val = s->i2s->output_mask | s->i2s->input_mask;+ break;++ case SOUND_MIXER_RECMASK: + /* Arg contains a bit for each supported recording + * source */+ val = s->i2s->input_mask;+ break;++ case SOUND_MIXER_STEREODEVS: + /* Mixer channels supporting stereo */+ val = s->i2s->output_mask;+ break;++ case SOUND_MIXER_CAPS:+ val = SOUND_CAP_EXCL_INPUT;+ break;++ default: /* read a specific mixer */+ i = _IOC_NR(cmd);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -