es18xx.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,223 行 · 第 1/5 页
C
2,223 行
/* * Driver for generic ESS AudioDrive ES18xx soundcards * Copyright (c) by Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de> * Copyright (c) by Abramo Bagnara <abramo@alsa-project.org> * * * 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, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *//* GENERAL NOTES: * * BUGS: * - There are pops (we can't delay in trigger function, cause midlevel * often need to trigger down and then up very quickly). * Any ideas? * - Support for 16 bit DMA seems to be broken. I've no hardware to tune it. *//* * ES1868 NOTES: * - The chip has one half duplex pcm (with very limited full duplex support). * * - Duplex stereophonic sound is impossible. * - Record and playback must share the same frequency rate. * * - The driver use dma2 for playback and dma1 for capture. *//* * ES1869 NOTES: * * - there are a first full duplex pcm and a second playback only pcm * (incompatible with first pcm capture) * * - there is support for the capture volume and ESS Spatializer 3D effect. * * - contrarily to some pages in DS_1869.PDF the rates can be set * independently. * * BUGS: * * - There is a major trouble I noted: * * using both channel for playback stereo 16 bit samples at 44100 Hz * the second pcm (Audio1) DMA slows down irregularly and sound is garbled. * * The same happens using Audio1 for captureing. * * The Windows driver does not suffer of this (although it use Audio1 * only for captureing). I'm unable to discover why. * */#include <sound/driver.h>#include <asm/io.h>#include <asm/dma.h>#include <linux/init.h>#include <linux/pm.h>#include <linux/slab.h>#include <linux/pnp.h>#include <linux/isapnp.h>#include <linux/moduleparam.h>#include <sound/core.h>#include <sound/control.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include <sound/mpu401.h>#include <sound/opl3.h>#define SNDRV_LEGACY_AUTO_PROBE#define SNDRV_LEGACY_FIND_FREE_IRQ#define SNDRV_LEGACY_FIND_FREE_DMA#include <sound/initval.h>#define PFX "es18xx: "struct _snd_es18xx { unsigned long port; /* port of ESS chip */ unsigned long mpu_port; /* MPU-401 port of ESS chip */ unsigned long fm_port; /* FM port */ unsigned long ctrl_port; /* Control port of ESS chip */ struct resource *res_port; struct resource *res_mpu_port; struct resource *res_ctrl_port; int irq; /* IRQ number of ESS chip */ int dma1; /* DMA1 */ int dma2; /* DMA2 */ unsigned short version; /* version of ESS chip */ int caps; /* Chip capabilities */ unsigned short audio2_vol; /* volume level of audio2 */ unsigned short active; /* active channel mask */ unsigned int dma1_size; unsigned int dma2_size; unsigned int dma1_shift; unsigned int dma2_shift; snd_card_t *card; snd_pcm_t *pcm; snd_pcm_substream_t *playback_a_substream; snd_pcm_substream_t *capture_a_substream; snd_pcm_substream_t *playback_b_substream; snd_rawmidi_t *rmidi; snd_kcontrol_t *hw_volume; snd_kcontrol_t *hw_switch; snd_kcontrol_t *master_volume; snd_kcontrol_t *master_switch; spinlock_t reg_lock; spinlock_t mixer_lock; spinlock_t ctrl_lock;#ifdef CONFIG_PM unsigned char pm_reg;#endif};#define AUDIO1_IRQ 0x01#define AUDIO2_IRQ 0x02#define HWV_IRQ 0x04#define MPU_IRQ 0x08#define ES18XX_PCM2 0x0001 /* Has two useable PCM */#define ES18XX_SPATIALIZER 0x0002 /* Has 3D Spatializer */#define ES18XX_RECMIX 0x0004 /* Has record mixer */#define ES18XX_DUPLEX_MONO 0x0008 /* Has mono duplex only */#define ES18XX_DUPLEX_SAME 0x0010 /* Playback and record must share the same rate */#define ES18XX_NEW_RATE 0x0020 /* More precise rate setting */#define ES18XX_AUXB 0x0040 /* AuxB mixer control */#define ES18XX_HWV 0x0080 /* Has hardware volume */#define ES18XX_MONO 0x0100 /* Mono_in mixer control */#define ES18XX_I2S 0x0200 /* I2S mixer control */#define ES18XX_MUTEREC 0x0400 /* Record source can be muted */#define ES18XX_CONTROL 0x0800 /* Has control ports *//* Power Management */#define ES18XX_PM 0x07#define ES18XX_PM_GPO0 0x01#define ES18XX_PM_GPO1 0x02#define ES18XX_PM_PDR 0x04#define ES18XX_PM_ANA 0x08#define ES18XX_PM_FM 0x020#define ES18XX_PM_SUS 0x080typedef struct _snd_es18xx es18xx_t;/* Lowlevel */#define DAC1 0x01#define ADC1 0x02#define DAC2 0x04#define MILLISECOND 10000static int snd_es18xx_dsp_command(es18xx_t *chip, unsigned char val){ int i; for(i = MILLISECOND; i; i--) if ((inb(chip->port + 0x0C) & 0x80) == 0) { outb(val, chip->port + 0x0C); return 0; } snd_printk("dsp_command: timeout (0x%x)\n", val); return -EINVAL;}static int snd_es18xx_dsp_get_byte(es18xx_t *chip){ int i; for(i = MILLISECOND/10; i; i--) if (inb(chip->port + 0x0C) & 0x40) return inb(chip->port + 0x0A); snd_printk("dsp_get_byte failed: 0x%lx = 0x%x!!!\n", chip->port + 0x0A, inb(chip->port + 0x0A)); return -ENODEV;}#undef REG_DEBUGstatic int snd_es18xx_write(es18xx_t *chip, unsigned char reg, unsigned char data){ unsigned long flags; int ret; spin_lock_irqsave(&chip->reg_lock, flags); ret = snd_es18xx_dsp_command(chip, reg); if (ret < 0) goto end; ret = snd_es18xx_dsp_command(chip, data); end: spin_unlock_irqrestore(&chip->reg_lock, flags);#ifdef REG_DEBUG snd_printk("Reg %02x set to %02x\n", reg, data);#endif return ret;}static int snd_es18xx_read(es18xx_t *chip, unsigned char reg){ unsigned long flags; int ret, data; spin_lock_irqsave(&chip->reg_lock, flags); ret = snd_es18xx_dsp_command(chip, 0xC0); if (ret < 0) goto end; ret = snd_es18xx_dsp_command(chip, reg); if (ret < 0) goto end; data = snd_es18xx_dsp_get_byte(chip); ret = data;#ifdef REG_DEBUG snd_printk("Reg %02x now is %02x (%d)\n", reg, data, ret);#endif end: spin_unlock_irqrestore(&chip->reg_lock, flags); return ret;}/* Return old value */static int snd_es18xx_bits(es18xx_t *chip, unsigned char reg, unsigned char mask, unsigned char val){ int ret; unsigned char old, new, oval; unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); ret = snd_es18xx_dsp_command(chip, 0xC0); if (ret < 0) goto end; ret = snd_es18xx_dsp_command(chip, reg); if (ret < 0) goto end; ret = snd_es18xx_dsp_get_byte(chip); if (ret < 0) { goto end; } old = ret; oval = old & mask; if (val != oval) { ret = snd_es18xx_dsp_command(chip, reg); if (ret < 0) goto end; new = (old & ~mask) | (val & mask); ret = snd_es18xx_dsp_command(chip, new); if (ret < 0) goto end;#ifdef REG_DEBUG snd_printk("Reg %02x was %02x, set to %02x (%d)\n", reg, old, new, ret);#endif } ret = oval; end: spin_unlock_irqrestore(&chip->reg_lock, flags); return ret;}inline void snd_es18xx_mixer_write(es18xx_t *chip, unsigned char reg, unsigned char data){ unsigned long flags; spin_lock_irqsave(&chip->mixer_lock, flags); outb(reg, chip->port + 0x04); outb(data, chip->port + 0x05); spin_unlock_irqrestore(&chip->mixer_lock, flags);#ifdef REG_DEBUG snd_printk("Mixer reg %02x set to %02x\n", reg, data);#endif}inline int snd_es18xx_mixer_read(es18xx_t *chip, unsigned char reg){ unsigned long flags; int data; spin_lock_irqsave(&chip->mixer_lock, flags); outb(reg, chip->port + 0x04); data = inb(chip->port + 0x05); spin_unlock_irqrestore(&chip->mixer_lock, flags);#ifdef REG_DEBUG snd_printk("Mixer reg %02x now is %02x\n", reg, data);#endif return data;}/* Return old value */static inline int snd_es18xx_mixer_bits(es18xx_t *chip, unsigned char reg, unsigned char mask, unsigned char val){ unsigned char old, new, oval; unsigned long flags; spin_lock_irqsave(&chip->mixer_lock, flags); outb(reg, chip->port + 0x04); old = inb(chip->port + 0x05); oval = old & mask; if (val != oval) { new = (old & ~mask) | (val & mask); outb(new, chip->port + 0x05);#ifdef REG_DEBUG snd_printk("Mixer reg %02x was %02x, set to %02x\n", reg, old, new);#endif } spin_unlock_irqrestore(&chip->mixer_lock, flags); return oval;}static inline int snd_es18xx_mixer_writable(es18xx_t *chip, unsigned char reg, unsigned char mask){ int old, expected, new; unsigned long flags; spin_lock_irqsave(&chip->mixer_lock, flags); outb(reg, chip->port + 0x04); old = inb(chip->port + 0x05); expected = old ^ mask; outb(expected, chip->port + 0x05); new = inb(chip->port + 0x05); spin_unlock_irqrestore(&chip->mixer_lock, flags);#ifdef REG_DEBUG snd_printk("Mixer reg %02x was %02x, set to %02x, now is %02x\n", reg, old, expected, new);#endif return expected == new;}static int snd_es18xx_reset(es18xx_t *chip){ int i; outb(0x03, chip->port + 0x06); inb(chip->port + 0x06); outb(0x00, chip->port + 0x06); for(i = 0; i < MILLISECOND && !(inb(chip->port + 0x0E) & 0x80); i++); if (inb(chip->port + 0x0A) != 0xAA) return -1; return 0;}static int snd_es18xx_reset_fifo(es18xx_t *chip){ outb(0x02, chip->port + 0x06); inb(chip->port + 0x06); outb(0x00, chip->port + 0x06); return 0;}static ratnum_t new_clocks[2] = { { .num = 793800, .den_min = 1, .den_max = 128, .den_step = 1, }, { .num = 768000, .den_min = 1, .den_max = 128, .den_step = 1, }};static snd_pcm_hw_constraint_ratnums_t new_hw_constraints_clocks = { .nrats = 2, .rats = new_clocks,};static ratnum_t old_clocks[2] = { { .num = 795444, .den_min = 1, .den_max = 128, .den_step = 1, }, { .num = 397722, .den_min = 1, .den_max = 128, .den_step = 1, }};static snd_pcm_hw_constraint_ratnums_t old_hw_constraints_clocks = { .nrats = 2, .rats = old_clocks,};static void snd_es18xx_rate_set(es18xx_t *chip, snd_pcm_substream_t *substream, int mode){ unsigned int bits, div0; snd_pcm_runtime_t *runtime = substream->runtime; if (chip->caps & ES18XX_NEW_RATE) { if (runtime->rate_num == new_clocks[0].num) bits = 128 - runtime->rate_den; else bits = 256 - runtime->rate_den; } else { if (runtime->rate_num == old_clocks[0].num) bits = 256 - runtime->rate_den; else bits = 128 - runtime->rate_den; } /* set filter register */ div0 = 256 - 7160000*20/(8*82*runtime->rate); if ((chip->caps & ES18XX_PCM2) && mode == DAC2) { snd_es18xx_mixer_write(chip, 0x70, bits); snd_es18xx_mixer_write(chip, 0x72, div0); } else { snd_es18xx_write(chip, 0xA1, bits); snd_es18xx_write(chip, 0xA2, div0); }}static int snd_es18xx_playback_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params){ es18xx_t *chip = snd_pcm_substream_chip(substream); int shift, err; shift = 0; if (params_channels(hw_params) == 2) shift++; if (snd_pcm_format_width(params_format(hw_params)) == 16) shift++; if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) { if ((chip->caps & ES18XX_DUPLEX_MONO) && (chip->capture_a_substream) && params_channels(hw_params) != 1) { _snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?