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 + -
显示快捷键?