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

📄 msp3400-driver.c

📁 V4l driver for DVB HD
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Programming the mspx4xx sound processor family * * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org> * * what works and what doesn't: * *  AM-Mono *      Support for Hauppauge cards added (decoding handled by tuner) added by *      Frederic Crozat <fcrozat@mail.dotcom.fr> * *  FM-Mono *      should work. The stereo modes are backward compatible to FM-mono, *      therefore FM-Mono should be allways available. * *  FM-Stereo (B/G, used in germany) *      should work, with autodetect * *  FM-Stereo (satellite) *      should work, no autodetect (i.e. default is mono, but you can *      switch to stereo -- untested) * *  NICAM (B/G, L , used in UK, Scandinavia, Spain and France) *      should work, with autodetect. Support for NICAM was added by *      Pekka Pietikainen <pp@netppl.fi> * * TODO: *   - better SAT support * * 980623  Thomas Sailer (sailer@ife.ee.ethz.ch) *         using soundcore instead of OSS * * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. */#include "compat.h"#include <linux/kernel.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/i2c.h>#include <linux/videodev.h>#include <linux/videodev2.h>#include <media/v4l2-common.h>#include <media/tvaudio.h>#include <media/msp3400.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)#include <linux/kthread.h>#include <linux/suspend.h>#endif#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)#include "i2c-compat.h"#endif#include "msp3400-driver.h"/* ---------------------------------------------------------------------- */MODULE_DESCRIPTION("device driver for msp34xx TV sound processor");MODULE_AUTHOR("Gerd Knorr");MODULE_LICENSE("GPL");/* module parameters */static int opmode   = OPMODE_AUTO;int msp_debug;		 /* msp_debug output */int msp_once;		 /* no continous stereo monitoring */int msp_amsound;	 /* hard-wire AM sound at 6.5 Hz (france),			    the autoscan seems work well only with FM... */int msp_standard = 1;    /* Override auto detect of audio msp_standard, if needed. */int msp_dolby;int msp_stereo_thresh = 0x190; /* a2 threshold for stereo/bilingual					(msp34xxg only) 0x00a0-0x03c0 */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)/* read-only */module_param(opmode,           int, 0444);/* read-write */module_param_named(once,msp_once,                      bool, 0644);module_param_named(debug,msp_debug,                    int,  0644);module_param_named(stereo_threshold,msp_stereo_thresh, int,  0644);module_param_named(standard,msp_standard,              int,  0644);module_param_named(amsound,msp_amsound,                bool, 0644);module_param_named(dolby,msp_dolby,                    bool, 0644);#elseMODULE_PARM(opmode,               "i");MODULE_PARM(msp_once,             "i");MODULE_PARM(msp_debug,            "i");MODULE_PARM(msp_stereo_thresh,    "i");MODULE_PARM(msp_standard,         "i");MODULE_PARM(msp_amsound,          "i");MODULE_PARM(msp_dolby,            "i");#endifMODULE_PARM_DESC(opmode, "Forces a MSP3400 opmode. 0=Manual, 1=Autodetect, 2=Autodetect and autoselect");MODULE_PARM_DESC(once, "No continuous stereo monitoring");MODULE_PARM_DESC(debug, "Enable debug messages [0-3]");MODULE_PARM_DESC(stereo_threshold, "Sets signal threshold to activate stereo");MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect");MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan");MODULE_PARM_DESC(dolby, "Activates Dolby processsing");/* ---------------------------------------------------------------------- *//* control subaddress */#define I2C_MSP_CONTROL 0x00/* demodulator unit subaddress */#define I2C_MSP_DEM     0x10/* DSP unit subaddress */#define I2C_MSP_DSP     0x12/* Addresses to scan */static unsigned short normal_i2c[] = { 0x80 >> 1, 0x88 >> 1, I2C_CLIENT_END };#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };#endifI2C_CLIENT_INSMOD;/* ----------------------------------------------------------------------- *//* functions for talking to the MSP3400C Sound processor                   */int msp_reset(struct i2c_client *client){	/* reset and read revision code */	static u8 reset_off[3] = { I2C_MSP_CONTROL, 0x80, 0x00 };	static u8 reset_on[3]  = { I2C_MSP_CONTROL, 0x00, 0x00 };	static u8 write[3]     = { I2C_MSP_DSP + 1, 0x00, 0x1e };	u8 read[2];	struct i2c_msg reset[2] = {		{ client->addr, I2C_M_IGNORE_NAK, 3, reset_off },		{ client->addr, I2C_M_IGNORE_NAK, 3, reset_on  },	};	struct i2c_msg test[2] = {		{ client->addr, 0,        3, write },		{ client->addr, I2C_M_RD, 2, read  },	};	v4l_dbg(3, msp_debug, client, "msp_reset\n");	if (i2c_transfer(client->adapter, &reset[0], 1) != 1 ||	    i2c_transfer(client->adapter, &reset[1], 1) != 1 ||	    i2c_transfer(client->adapter, test, 2) != 2) {		v4l_err(client, "chip reset failed\n");		return -1;	}	return 0;}static int msp_read(struct i2c_client *client, int dev, int addr){	int err, retval;	u8 write[3];	u8 read[2];	struct i2c_msg msgs[2] = {		{ client->addr, 0,        3, write },		{ client->addr, I2C_M_RD, 2, read  }	};	write[0] = dev + 1;	write[1] = addr >> 8;	write[2] = addr & 0xff;	for (err = 0; err < 3; err++) {		if (i2c_transfer(client->adapter, msgs, 2) == 2)			break;		v4l_warn(client, "I/O error #%d (read 0x%02x/0x%02x)\n", err,		       dev, addr);		current->state = TASK_INTERRUPTIBLE;		schedule_timeout(msecs_to_jiffies(10));	}	if (err == 3) {		v4l_warn(client, "giving up, resetting chip. Sound will go off, sorry folks :-|\n");		msp_reset(client);		return -1;	}	retval = read[0] << 8 | read[1];	v4l_dbg(3, msp_debug, client, "msp_read(0x%x, 0x%x): 0x%x\n", dev, addr, retval);	return retval;}int msp_read_dem(struct i2c_client *client, int addr){	return msp_read(client, I2C_MSP_DEM, addr);}int msp_read_dsp(struct i2c_client *client, int addr){	return msp_read(client, I2C_MSP_DSP, addr);}static int msp_write(struct i2c_client *client, int dev, int addr, int val){	int err;	u8 buffer[5];	buffer[0] = dev;	buffer[1] = addr >> 8;	buffer[2] = addr &  0xff;	buffer[3] = val  >> 8;	buffer[4] = val  &  0xff;	v4l_dbg(3, msp_debug, client, "msp_write(0x%x, 0x%x, 0x%x)\n", dev, addr, val);	for (err = 0; err < 3; err++) {		if (i2c_master_send(client, buffer, 5) == 5)			break;		v4l_warn(client, "I/O error #%d (write 0x%02x/0x%02x)\n", err,		       dev, addr);		current->state = TASK_INTERRUPTIBLE;		schedule_timeout(msecs_to_jiffies(10));	}	if (err == 3) {		v4l_warn(client, "giving up, resetting chip. Sound will go off, sorry folks :-|\n");		msp_reset(client);		return -1;	}	return 0;}int msp_write_dem(struct i2c_client *client, int addr, int val){	return msp_write(client, I2C_MSP_DEM, addr, val);}int msp_write_dsp(struct i2c_client *client, int addr, int val){	return msp_write(client, I2C_MSP_DSP, addr, val);}/* ----------------------------------------------------------------------- * * bits  9  8  5 - SCART DSP input Select: *       0  0  0 - SCART 1 to DSP input (reset position) *       0  1  0 - MONO to DSP input *       1  0  0 - SCART 2 to DSP input *       1  1  1 - Mute DSP input * * bits 11 10  6 - SCART 1 Output Select: *       0  0  0 - undefined (reset position) *       0  1  0 - SCART 2 Input to SCART 1 Output (for devices with 2 SCARTS) *       1  0  0 - MONO input to SCART 1 Output *       1  1  0 - SCART 1 DA to SCART 1 Output *       0  0  1 - SCART 2 DA to SCART 1 Output *       0  1  1 - SCART 1 Input to SCART 1 Output *       1  1  1 - Mute SCART 1 Output * * bits 13 12  7 - SCART 2 Output Select (for devices with 2 Output SCART): *       0  0  0 - SCART 1 DA to SCART 2 Output (reset position) *       0  1  0 - SCART 1 Input to SCART 2 Output *       1  0  0 - MONO input to SCART 2 Output *       0  0  1 - SCART 2 DA to SCART 2 Output *       0  1  1 - SCART 2 Input to SCART 2 Output *       1  1  0 - Mute SCART 2 Output * * Bits 4 to 0 should be zero. * ----------------------------------------------------------------------- */static int scarts[3][9] = {       /* MASK   IN1     IN2     IN3     IN4     IN1_DA  IN2_DA  MONO    MUTE   */	/* SCART DSP Input select */       { 0x0320, 0x0000, 0x0200, 0x0300, 0x0020, -1,     -1,     0x0100, 0x0320 },	/* SCART1 Output select */       { 0x0c40, 0x0440, 0x0400, 0x0000, 0x0840, 0x0c00, 0x0040, 0x0800, 0x0c40 },	/* SCART2 Output select */       { 0x3080, 0x1000, 0x1080, 0x2080, 0x3080, 0x0000, 0x0080, 0x2000, 0x3000 },};static char *scart_names[] = {       "in1", "in2", "in3", "in4", "in1 da", "in2 da", "mono", "mute"};void msp_set_scart(struct i2c_client *client, int in, int out){	struct msp_state *state = i2c_get_clientdata(client);	state->in_scart = in;	if (in >= 0 && in <= 7 && out >= 0 && out <= 2) {		if (-1 == scarts[out][in + 1])			return;		state->acb &= ~scarts[out][0];		state->acb |=  scarts[out][in + 1];	} else		state->acb = 0xf60; /* Mute Input and SCART 1 Output */	v4l_dbg(1, msp_debug, client, "scart switch: %s => %d (ACB=0x%04x)\n",						scart_names[in], out, state->acb);	msp_write_dsp(client, 0x13, state->acb);	/* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */	if (state->has_i2s_conf)		msp_write_dem(client, 0x40, state->i2s_mode);}void msp_set_audio(struct i2c_client *client){	struct msp_state *state = i2c_get_clientdata(client);	int bal = 0, bass, treble, loudness;	int val = 0;	int reallymuted = state->muted | state->scan_in_progress;	if (!reallymuted)		val = (state->volume * 0x7f / 65535) << 8;	v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",		state->muted ? "on" : "off", state->scan_in_progress ? "yes" : "no",		state->volume);	msp_write_dsp(client, 0x0000, val);	msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1));	if (state->has_scart2_out_volume)		msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1));	if (state->has_headphones)		msp_write_dsp(client, 0x0006, val);	if (!state->has_sound_processing)		return;	if (val)		bal = (u8)((state->balance / 256) - 128);	bass = ((state->bass - 32768) * 0x60 / 65535) << 8;	treble = ((state->treble - 32768) * 0x60 / 65535) << 8;	loudness = state->loudness ? ((5 * 4) << 8) : 0;	v4l_dbg(1, msp_debug, client, "balance=%d bass=%d treble=%d loudness=%d\n",		state->balance, state->bass, state->treble, state->loudness);	msp_write_dsp(client, 0x0001, bal << 8);	msp_write_dsp(client, 0x0002, bass);	msp_write_dsp(client, 0x0003, treble);	msp_write_dsp(client, 0x0004, loudness);	if (!state->has_headphones)		return;	msp_write_dsp(client, 0x0030, bal << 8);	msp_write_dsp(client, 0x0031, bass);	msp_write_dsp(client, 0x0032, treble);	msp_write_dsp(client, 0x0033, loudness);}/* ------------------------------------------------------------------------ */#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)static void msp_setup_thread(struct msp_state *state){	daemonize();	exit_files(current);	reparent_to_init();	spin_lock_irq(SIGMASK_LOCK(current));	sigfillset(&current->blocked);	spin_unlock_irq(SIGMASK_LOCK(current));	strcpy(current->comm, "msp3400");	state->kthread = current;	if (state->notify != NULL)		up(state->notify);}#endifstatic void msp_wake_thread(struct i2c_client *client){	struct msp_state *state = i2c_get_clientdata(client);	if (NULL == state->kthread)		return;	state->watch_stereo = 0;	state->restart = 1;	wake_up_interruptible(&state->wq);}int msp_sleep(struct msp_state *state, int timeout){	DECLARE_WAITQUEUE(wait, current);	add_wait_queue(&state->wq, &wait);#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)	if (!(state->rmmod || signal_pending(current))) {#else	if (!kthread_should_stop()) {#endif		if (timeout < 0) {			set_current_state(TASK_INTERRUPTIBLE);			schedule();		} else {#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)			schedule_timeout_interruptible						(msecs_to_jiffies(timeout));#else			set_current_state(TASK_INTERRUPTIBLE);			schedule_timeout(msecs_to_jiffies(timeout));

⌨️ 快捷键说明

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