⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcxhr.c

📁 linux2.6.16版本
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Driver for Digigram pcxhr compatible soundcards * * main file with alsa callbacks * * Copyright (c) 2004 by Digigram <alsa@digigram.com> * *   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 */#include <sound/driver.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/moduleparam.h>#include <sound/core.h>#include <sound/initval.h>#include <sound/info.h>#include <sound/control.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include "pcxhr.h"#include "pcxhr_mixer.h"#include "pcxhr_hwdep.h"#include "pcxhr_core.h"#define DRIVER_NAME "pcxhr"MODULE_AUTHOR("Markus Bollinger <bollinger@digigram.com>");MODULE_DESCRIPTION("Digigram " DRIVER_NAME " " PCXHR_DRIVER_VERSION_STRING);MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{Digigram," DRIVER_NAME "}}");static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;		/* Index 0-MAX */static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;		/* ID for this card */static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */static int mono[SNDRV_CARDS];					/* capture in mono only */module_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index value for Digigram " DRIVER_NAME " soundcard");module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "ID string for Digigram " DRIVER_NAME " soundcard");module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable Digigram " DRIVER_NAME " soundcard");module_param_array(mono, bool, NULL, 0444);MODULE_PARM_DESC(mono, "Mono capture mode (default is stereo)");enum {	PCI_ID_VX882HR,	PCI_ID_PCX882HR,	PCI_ID_VX881HR,	PCI_ID_PCX881HR,	PCI_ID_PCX1222HR,	PCI_ID_PCX1221HR,	PCI_ID_LAST};static struct pci_device_id pcxhr_ids[] = {	{ 0x10b5, 0x9656, 0x1369, 0xb001, 0, 0, PCI_ID_VX882HR, },   /* VX882HR */	{ 0x10b5, 0x9656, 0x1369, 0xb101, 0, 0, PCI_ID_PCX882HR, },  /* PCX882HR */	{ 0x10b5, 0x9656, 0x1369, 0xb201, 0, 0, PCI_ID_VX881HR, },   /* VX881HR */	{ 0x10b5, 0x9656, 0x1369, 0xb301, 0, 0, PCI_ID_PCX881HR, },  /* PCX881HR */	{ 0x10b5, 0x9656, 0x1369, 0xb501, 0, 0, PCI_ID_PCX1222HR, }, /* PCX1222HR */	{ 0x10b5, 0x9656, 0x1369, 0xb701, 0, 0, PCI_ID_PCX1221HR, }, /* PCX1221HR */	{ 0, }};MODULE_DEVICE_TABLE(pci, pcxhr_ids);struct board_parameters {	char* board_name;	short playback_chips;	short capture_chips;	short firmware_num;};static struct board_parameters pcxhr_board_params[] = {[PCI_ID_VX882HR] =	{ "VX882HR",   4, 4, 41, },[PCI_ID_PCX882HR] =	{ "PCX882HR",  4, 4, 41, },[PCI_ID_VX881HR] =	{ "VX881HR",   4, 4, 41, },[PCI_ID_PCX881HR] =	{ "PCX881HR",  4, 4, 41, },[PCI_ID_PCX1222HR] =	{ "PCX1222HR", 6, 1, 42, },[PCI_ID_PCX1221HR] =	{ "PCX1221HR", 6, 1, 42, },};static int pcxhr_pll_freq_register(unsigned int freq, unsigned int* pllreg,				   unsigned int* realfreq){	unsigned int reg;	if (freq < 6900 || freq > 110250)		return -EINVAL;	reg = (28224000 * 10) / freq;	reg = (reg + 5) / 10;	if (reg < 0x200)		*pllreg = reg + 0x800;	else if (reg < 0x400)		*pllreg = reg & 0x1ff;	else if (reg < 0x800) {		*pllreg = ((reg >> 1) & 0x1ff) + 0x200;		reg &= ~1;	} else {		*pllreg = ((reg >> 2) & 0x1ff) + 0x400;		reg &= ~3;	}	if (realfreq)		*realfreq = ((28224000 * 10) / reg + 5) / 10;	return 0;}#define PCXHR_FREQ_REG_MASK		0x1f#define PCXHR_FREQ_QUARTZ_48000		0x00#define PCXHR_FREQ_QUARTZ_24000		0x01#define PCXHR_FREQ_QUARTZ_12000		0x09#define PCXHR_FREQ_QUARTZ_32000		0x08#define PCXHR_FREQ_QUARTZ_16000		0x04#define PCXHR_FREQ_QUARTZ_8000		0x0c#define PCXHR_FREQ_QUARTZ_44100		0x02#define PCXHR_FREQ_QUARTZ_22050		0x0a#define PCXHR_FREQ_QUARTZ_11025		0x06#define PCXHR_FREQ_PLL			0x05#define PCXHR_FREQ_QUARTZ_192000	0x10#define PCXHR_FREQ_QUARTZ_96000		0x18#define PCXHR_FREQ_QUARTZ_176400	0x14#define PCXHR_FREQ_QUARTZ_88200		0x1c#define PCXHR_FREQ_QUARTZ_128000	0x12#define PCXHR_FREQ_QUARTZ_64000		0x1a#define PCXHR_FREQ_WORD_CLOCK		0x0f#define PCXHR_FREQ_SYNC_AES		0x0e#define PCXHR_FREQ_AES_1		0x07#define PCXHR_FREQ_AES_2		0x0b#define PCXHR_FREQ_AES_3		0x03#define PCXHR_FREQ_AES_4		0x0d#define PCXHR_MODIFY_CLOCK_S_BIT	0x04#define PCXHR_IRQ_TIMER_FREQ		92000#define PCXHR_IRQ_TIMER_PERIOD		48static int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,			       unsigned int *reg, unsigned int *freq){	unsigned int val, realfreq, pllreg;	struct pcxhr_rmh rmh;	int err;	realfreq = rate;	switch (mgr->use_clock_type) {	case PCXHR_CLOCK_TYPE_INTERNAL :	/* clock by quartz or pll */		switch (rate) {		case 48000 :	val = PCXHR_FREQ_QUARTZ_48000;	break;		case 24000 :	val = PCXHR_FREQ_QUARTZ_24000;	break;		case 12000 :	val = PCXHR_FREQ_QUARTZ_12000;	break;		case 32000 :	val = PCXHR_FREQ_QUARTZ_32000;	break;		case 16000 :	val = PCXHR_FREQ_QUARTZ_16000;	break;		case 8000 :	val = PCXHR_FREQ_QUARTZ_8000;	break;		case 44100 :	val = PCXHR_FREQ_QUARTZ_44100;	break;		case 22050 :	val = PCXHR_FREQ_QUARTZ_22050;	break;		case 11025 :	val = PCXHR_FREQ_QUARTZ_11025;	break;		case 192000 :	val = PCXHR_FREQ_QUARTZ_192000;	break;		case 96000 :	val = PCXHR_FREQ_QUARTZ_96000;	break;		case 176400 :	val = PCXHR_FREQ_QUARTZ_176400;	break;		case 88200 :	val = PCXHR_FREQ_QUARTZ_88200;	break;		case 128000 :	val = PCXHR_FREQ_QUARTZ_128000;	break;		case 64000 :	val = PCXHR_FREQ_QUARTZ_64000;	break;		default :			val = PCXHR_FREQ_PLL;			/* get the value for the pll register */			err = pcxhr_pll_freq_register(rate, &pllreg, &realfreq);			if (err)				return err;			pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);			rmh.cmd[0] |= IO_NUM_REG_GENCLK;			rmh.cmd[1]  = pllreg & MASK_DSP_WORD;			rmh.cmd[2]  = pllreg >> 24;			rmh.cmd_len = 3;			err = pcxhr_send_msg(mgr, &rmh);			if (err < 0) {				snd_printk(KERN_ERR					   "error CMD_ACCESS_IO_WRITE for PLL register : %x!\n",					   err );				return err;			}		}		break;	case PCXHR_CLOCK_TYPE_WORD_CLOCK :	val = PCXHR_FREQ_WORD_CLOCK;	break;	case PCXHR_CLOCK_TYPE_AES_SYNC :	val = PCXHR_FREQ_SYNC_AES;	break;	case PCXHR_CLOCK_TYPE_AES_1 :		val = PCXHR_FREQ_AES_1;		break;	case PCXHR_CLOCK_TYPE_AES_2 :		val = PCXHR_FREQ_AES_2;		break;	case PCXHR_CLOCK_TYPE_AES_3 :		val = PCXHR_FREQ_AES_3;		break;	case PCXHR_CLOCK_TYPE_AES_4 :		val = PCXHR_FREQ_AES_4;		break;	default : return -EINVAL;	}	*reg = val;	*freq = realfreq;	return 0;}int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate){	unsigned int val, realfreq, speed;	struct pcxhr_rmh rmh;	int err, changed;	if (rate == 0)		return 0; /* nothing to do */	err = pcxhr_get_clock_reg(mgr, rate, &val, &realfreq);	if (err)		return err;	/* codec speed modes */	if (rate < 55000)		speed = 0;	/* single speed */	else if (rate < 100000)		speed = 1;	/* dual speed */	else		speed = 2;	/* quad speed */	if (mgr->codec_speed != speed) {		pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);	/* mute outputs */		rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;		err = pcxhr_send_msg(mgr, &rmh);		if (err)			return err;		pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);	/* set speed ratio */		rmh.cmd[0] |= IO_NUM_SPEED_RATIO;		rmh.cmd[1] = speed;		rmh.cmd_len = 2;		err = pcxhr_send_msg(mgr, &rmh);		if (err)			return err;	}	/* set the new frequency */	snd_printdd("clock register : set %x\n", val);	err = pcxhr_write_io_num_reg_cont(mgr, PCXHR_FREQ_REG_MASK, val, &changed);	if (err)		return err;	mgr->sample_rate_real = realfreq;	mgr->cur_clock_type = mgr->use_clock_type;	/* unmute after codec speed modes */	if (mgr->codec_speed != speed) {		pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);	/* unmute outputs */		rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;		err = pcxhr_send_msg(mgr, &rmh);		if (err)			return err;		mgr->codec_speed = speed;			/* save new codec speed */	}	if (changed) {		pcxhr_init_rmh(&rmh, CMD_MODIFY_CLOCK);		rmh.cmd[0] |= PCXHR_MODIFY_CLOCK_S_BIT;		/* resync fifos  */		if (rate < PCXHR_IRQ_TIMER_FREQ)			rmh.cmd[1] = PCXHR_IRQ_TIMER_PERIOD;		else			rmh.cmd[1] = PCXHR_IRQ_TIMER_PERIOD * 2;		rmh.cmd[2] = rate;		rmh.cmd_len = 3;		err = pcxhr_send_msg(mgr, &rmh);		if (err)			return err;	}	snd_printdd("pcxhr_set_clock to %dHz (realfreq=%d)\n", rate, realfreq);	return 0;}int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, enum pcxhr_clock_type clock_type,			     int *sample_rate){	struct pcxhr_rmh rmh;	unsigned char reg;	int err, rate;	switch (clock_type) {	case PCXHR_CLOCK_TYPE_WORD_CLOCK :	reg = REG_STATUS_WORD_CLOCK;	break;	case PCXHR_CLOCK_TYPE_AES_SYNC :	reg = REG_STATUS_AES_SYNC;	break;	case PCXHR_CLOCK_TYPE_AES_1 :		reg = REG_STATUS_AES_1;		break;	case PCXHR_CLOCK_TYPE_AES_2 :		reg = REG_STATUS_AES_2;		break;	case PCXHR_CLOCK_TYPE_AES_3 :		reg = REG_STATUS_AES_3;		break;	case PCXHR_CLOCK_TYPE_AES_4 :		reg = REG_STATUS_AES_4;		break;	default : return -EINVAL;	}	pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);	rmh.cmd_len = 2;	rmh.cmd[0] |= IO_NUM_REG_STATUS;	if (mgr->last_reg_stat != reg) {		rmh.cmd[1]  = reg;		err = pcxhr_send_msg(mgr, &rmh);		if (err)			return err;		udelay(100);		/* wait minimum 2 sample_frames at 32kHz ! */		mgr->last_reg_stat = reg;	}	rmh.cmd[1]  = REG_STATUS_CURRENT;	err = pcxhr_send_msg(mgr, &rmh);	if (err)		return err;	switch (rmh.stat[1] & 0x0f) {	case REG_STATUS_SYNC_32000 :	rate = 32000; break;	case REG_STATUS_SYNC_44100 :	rate = 44100; break;	case REG_STATUS_SYNC_48000 :	rate = 48000; break;	case REG_STATUS_SYNC_64000 :	rate = 64000; break;	case REG_STATUS_SYNC_88200 :	rate = 88200; break;	case REG_STATUS_SYNC_96000 :	rate = 96000; break;	case REG_STATUS_SYNC_128000 :	rate = 128000; break;	case REG_STATUS_SYNC_176400 :	rate = 176400; break;	case REG_STATUS_SYNC_192000 :	rate = 192000; break;	default: rate = 0;	}	snd_printdd("External clock is at %d Hz\n", rate);	*sample_rate = rate;	return 0;}/* *  start or stop playback/capture substream */static int pcxhr_set_stream_state(struct pcxhr_stream *stream){	int err;	struct snd_pcxhr *chip;	struct pcxhr_rmh rmh;	int stream_mask, start;	if (stream->status == PCXHR_STREAM_STATUS_SCHEDULE_RUN)		start = 1;	else {		if (stream->status != PCXHR_STREAM_STATUS_SCHEDULE_STOP) {			snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state CANNOT be stopped\n");			return -EINVAL;		}		start = 0;	}	if (!stream->substream)		return -EINVAL;	stream->timer_abs_periods = 0;	stream->timer_period_frag = 0;            /* reset theoretical stream pos */	stream->timer_buf_periods = 0;	stream->timer_is_synced = 0;	stream_mask = stream->pipe->is_capture ? 1 : 1<<stream->substream->number;	pcxhr_init_rmh(&rmh, start ? CMD_START_STREAM : CMD_STOP_STREAM);	pcxhr_set_pipe_cmd_params(&rmh, stream->pipe->is_capture,				  stream->pipe->first_audio, 0, stream_mask);	chip = snd_pcm_substream_chip(stream->substream);	err = pcxhr_send_msg(chip->mgr, &rmh);	if (err)		snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state err=%x;\n", err);	stream->status = start ? PCXHR_STREAM_STATUS_STARTED : PCXHR_STREAM_STATUS_STOPPED;	return err;}#define HEADER_FMT_BASE_LIN		0xfed00000#define HEADER_FMT_BASE_FLOAT		0xfad00000#define HEADER_FMT_INTEL		0x00008000#define HEADER_FMT_24BITS		0x00004000#define HEADER_FMT_16BITS		0x00002000#define HEADER_FMT_UPTO11		0x00000200#define HEADER_FMT_UPTO32		0x00000100#define HEADER_FMT_MONO			0x00000080static int pcxhr_set_format(struct pcxhr_stream *stream){	int err, is_capture, sample_rate, stream_num;	struct snd_pcxhr *chip;	struct pcxhr_rmh rmh;	unsigned int header;	switch (stream->format) {	case SNDRV_PCM_FORMAT_U8:		header = HEADER_FMT_BASE_LIN;		break;	case SNDRV_PCM_FORMAT_S16_LE:		header = HEADER_FMT_BASE_LIN | HEADER_FMT_16BITS | HEADER_FMT_INTEL;		break;	case SNDRV_PCM_FORMAT_S16_BE:		header = HEADER_FMT_BASE_LIN | HEADER_FMT_16BITS;		break;	case SNDRV_PCM_FORMAT_S24_3LE:		header = HEADER_FMT_BASE_LIN | HEADER_FMT_24BITS | HEADER_FMT_INTEL;		break;	case SNDRV_PCM_FORMAT_S24_3BE:		header = HEADER_FMT_BASE_LIN | HEADER_FMT_24BITS;		break;	case SNDRV_PCM_FORMAT_FLOAT_LE:		header = HEADER_FMT_BASE_FLOAT | HEADER_FMT_INTEL;		break;	default:		snd_printk(KERN_ERR "error pcxhr_set_format() : unknown format\n");		return -EINVAL;	}	chip = snd_pcm_substream_chip(stream->substream);	sample_rate = chip->mgr->sample_rate;	if (sample_rate <= 32000 && sample_rate !=0) {		if (sample_rate <= 11025)			header |= HEADER_FMT_UPTO11;		else			header |= HEADER_FMT_UPTO32;	}	if (stream->channels == 1)		header |= HEADER_FMT_MONO;	is_capture = stream->pipe->is_capture;	stream_num = is_capture ? 0 : stream->substream->number;	pcxhr_init_rmh(&rmh, is_capture ? CMD_FORMAT_STREAM_IN : CMD_FORMAT_STREAM_OUT);	pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio, stream_num, 0);	if (is_capture)		rmh.cmd[0] |= 1<<12;	rmh.cmd[1] = 0;	rmh.cmd[2] = header >> 8;	rmh.cmd[3] = (header & 0xff) << 16;	rmh.cmd_len = 4;	err = pcxhr_send_msg(chip->mgr, &rmh);	if (err)		snd_printk(KERN_ERR "ERROR pcxhr_set_format err=%x;\n", err);	return err;}static int pcxhr_update_r_buffer(struct pcxhr_stream *stream){	int err, is_capture, stream_num;	struct pcxhr_rmh rmh;	struct snd_pcm_substream *subs = stream->substream;	struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);	is_capture = (subs->stream == SNDRV_PCM_STREAM_CAPTURE);	stream_num = is_capture ? 0 : subs->number;	snd_printdd("pcxhr_update_r_buffer(pcm%c%d) : addr(%p) bytes(%zx) subs(%d)\n",

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -