msp3400.c

来自「底层驱动开发」· C语言 代码 · 共 1,864 行 · 第 1/4 页

C
1,864
字号
		       msp_init_data[type].dfp_src);	msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e,		       msp_init_data[type].dfp_matrix);	if (HAVE_NICAM(msp)) {		/* nicam prescale */		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0010, 0x5a00); /* was: 0x3000 */	}}static int best_audio_mode(int rxsubchans){	if (rxsubchans & V4L2_TUNER_SUB_STEREO)		return V4L2_TUNER_MODE_STEREO;	if (rxsubchans & V4L2_TUNER_SUB_LANG1)		return V4L2_TUNER_MODE_LANG1;	if (rxsubchans & V4L2_TUNER_SUB_LANG2)		return V4L2_TUNER_MODE_LANG2;	return V4L2_TUNER_MODE_MONO;}/* turn on/off nicam + stereo */static void msp3400c_set_audmode(struct i2c_client *client, int audmode){	static char *strmode[16] = {#if __GNUC__ >= 3		[ 0 ... 15 ]               = "invalid",#endif		[ V4L2_TUNER_MODE_MONO   ] = "mono",		[ V4L2_TUNER_MODE_STEREO ] = "stereo",		[ V4L2_TUNER_MODE_LANG1  ] = "lang1",		[ V4L2_TUNER_MODE_LANG2  ] = "lang2",	};	struct msp3400c *msp = i2c_get_clientdata(client);	int nicam=0; /* channel source: FM/AM or nicam */	int src=0;	BUG_ON(msp->opmode == OPMODE_SIMPLER);	msp->audmode = audmode;	/* switch demodulator */	switch (msp->mode) {	case MSP_MODE_FM_TERRA:		dprintk(KERN_DEBUG "msp3400: FM setstereo: %s\n",			strmode[audmode]);		msp3400c_setcarrier(client,msp->second,msp->main);		switch (audmode) {		case V4L2_TUNER_MODE_STEREO:			msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3001);			break;		case V4L2_TUNER_MODE_MONO:		case V4L2_TUNER_MODE_LANG1:		case V4L2_TUNER_MODE_LANG2:			msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3000);			break;		}		break;	case MSP_MODE_FM_SAT:		dprintk(KERN_DEBUG "msp3400: SAT setstereo: %s\n",			strmode[audmode]);		switch (audmode) {		case V4L2_TUNER_MODE_MONO:			msp3400c_setcarrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5));			break;		case V4L2_TUNER_MODE_STEREO:			msp3400c_setcarrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02));			break;		case V4L2_TUNER_MODE_LANG1:			msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02));			break;		case V4L2_TUNER_MODE_LANG2:			msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02));			break;		}		break;	case MSP_MODE_FM_NICAM1:	case MSP_MODE_FM_NICAM2:	case MSP_MODE_AM_NICAM:		dprintk(KERN_DEBUG "msp3400: NICAM setstereo: %s\n",			strmode[audmode]);		msp3400c_setcarrier(client,msp->second,msp->main);		if (msp->nicam_on)			nicam=0x0100;		break;	case MSP_MODE_BTSC:		dprintk(KERN_DEBUG "msp3400: BTSC setstereo: %s\n",			strmode[audmode]);		nicam=0x0300;		break;	case MSP_MODE_EXTERN:		dprintk(KERN_DEBUG "msp3400: extern setstereo: %s\n",			strmode[audmode]);		nicam = 0x0200;		break;	case MSP_MODE_FM_RADIO:		dprintk(KERN_DEBUG "msp3400: FM-Radio setstereo: %s\n",			strmode[audmode]);		break;	default:		dprintk(KERN_DEBUG "msp3400: mono setstereo\n");		return;	}	/* switch audio */	switch (audmode) {	case V4L2_TUNER_MODE_STEREO:		src = 0x0020 | nicam;		break;	case V4L2_TUNER_MODE_MONO:		if (msp->mode == MSP_MODE_AM_NICAM) {			dprintk("msp3400: switching to AM mono\n");			/* AM mono decoding is handled by tuner, not MSP chip */			/* SCART switching control register */			msp3400c_set_scart(client,SCART_MONO,0);			src = 0x0200;			break;		}	case V4L2_TUNER_MODE_LANG1:		src = 0x0000 | nicam;		break;	case V4L2_TUNER_MODE_LANG2:		src = 0x0010 | nicam;		break;	}	dprintk(KERN_DEBUG		"msp3400: setstereo final source/matrix = 0x%x\n", src);	if (dolby) {		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,0x0520);		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,0x0620);		msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src);		msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src);	} else {		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,src);		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,src);		msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src);		msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src);	}}static voidmsp3400c_print_mode(struct msp3400c *msp){	if (msp->main == msp->second) {		printk(KERN_DEBUG "msp3400: mono sound carrier: %d.%03d MHz\n",		       msp->main/910000,(msp->main/910)%1000);	} else {		printk(KERN_DEBUG "msp3400: main sound carrier: %d.%03d MHz\n",		       msp->main/910000,(msp->main/910)%1000);	}	if (msp->mode == MSP_MODE_FM_NICAM1 ||	    msp->mode == MSP_MODE_FM_NICAM2)		printk(KERN_DEBUG "msp3400: NICAM/FM carrier   : %d.%03d MHz\n",		       msp->second/910000,(msp->second/910)%1000);	if (msp->mode == MSP_MODE_AM_NICAM)		printk(KERN_DEBUG "msp3400: NICAM/AM carrier   : %d.%03d MHz\n",		       msp->second/910000,(msp->second/910)%1000);	if (msp->mode == MSP_MODE_FM_TERRA &&	    msp->main != msp->second) {		printk(KERN_DEBUG "msp3400: FM-stereo carrier : %d.%03d MHz\n",		       msp->second/910000,(msp->second/910)%1000);	}}/* ----------------------------------------------------------------------- */struct REGISTER_DUMP {	int   addr;	char *name;};static intautodetect_stereo(struct i2c_client *client){	struct msp3400c *msp = i2c_get_clientdata(client);	int val;	int rxsubchans = msp->rxsubchans;	int newnicam   = msp->nicam_on;	int update = 0;	switch (msp->mode) {	case MSP_MODE_FM_TERRA:		val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x18);		if (val > 32767)			val -= 65536;		dprintk(KERN_DEBUG			"msp34xx: stereo detect register: %d\n",val);		if (val > 4096) {			rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;		} else if (val < -4096) {			rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;		} else {			rxsubchans = V4L2_TUNER_SUB_MONO;		}		newnicam = 0;		break;	case MSP_MODE_FM_NICAM1:	case MSP_MODE_FM_NICAM2:	case MSP_MODE_AM_NICAM:		val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x23);		dprintk(KERN_DEBUG			"msp34xx: nicam sync=%d, mode=%d\n",			val & 1, (val & 0x1e) >> 1);		if (val & 1) {			/* nicam synced */			switch ((val & 0x1e) >> 1)  {			case 0:			case 8:				rxsubchans = V4L2_TUNER_SUB_STEREO;				break;			case 1:			case 9:				rxsubchans = V4L2_TUNER_SUB_MONO					| V4L2_TUNER_SUB_LANG1;				break;			case 2:			case 10:				rxsubchans = V4L2_TUNER_SUB_MONO					| V4L2_TUNER_SUB_LANG1					| V4L2_TUNER_SUB_LANG2;				break;			default:				rxsubchans = V4L2_TUNER_SUB_MONO;				break;			}			newnicam=1;		} else {			newnicam = 0;			rxsubchans = V4L2_TUNER_SUB_MONO;		}		break;	case MSP_MODE_BTSC:		val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x200);		dprintk(KERN_DEBUG			"msp3410: status=0x%x (pri=%s, sec=%s, %s%s%s)\n",			val,			(val & 0x0002) ? "no"     : "yes",			(val & 0x0004) ? "no"     : "yes",			(val & 0x0040) ? "stereo" : "mono",			(val & 0x0080) ? ", nicam 2nd mono" : "",			(val & 0x0100) ? ", bilingual/SAP"  : "");		rxsubchans = V4L2_TUNER_SUB_MONO;		if (val & 0x0040) rxsubchans |= V4L2_TUNER_SUB_STEREO;		if (val & 0x0100) rxsubchans |= V4L2_TUNER_SUB_LANG1;		break;	}	if (rxsubchans != msp->rxsubchans) {		update = 1;		dprintk(KERN_DEBUG "msp34xx: watch: rxsubchans %d => %d\n",			msp->rxsubchans,rxsubchans);		msp->rxsubchans = rxsubchans;	}	if (newnicam != msp->nicam_on) {		update = 1;		dprintk(KERN_DEBUG "msp34xx: watch: nicam %d => %d\n",			msp->nicam_on,newnicam);		msp->nicam_on = newnicam;	}	return update;}/* * A kernel thread for msp3400 control -- we don't want to block the * in the ioctl while doing the sound carrier & stereo detect */static int msp34xx_sleep(struct msp3400c *msp, int timeout){	DECLARE_WAITQUEUE(wait, current);	add_wait_queue(&msp->wq, &wait);	if (!kthread_should_stop()) {		if (timeout < 0) {			set_current_state(TASK_INTERRUPTIBLE);			schedule();		} else {			set_current_state(TASK_INTERRUPTIBLE);			schedule_timeout(msecs_to_jiffies(timeout));		}	}	remove_wait_queue(&msp->wq, &wait);	try_to_freeze();	return msp->restart;}/* stereo/multilang monitoring */static void watch_stereo(struct i2c_client *client){	struct msp3400c *msp = i2c_get_clientdata(client);	if (autodetect_stereo(client))		msp3400c_set_audmode(client,best_audio_mode(msp->rxsubchans));	if (once)		msp->watch_stereo = 0;}static int msp3400c_thread(void *data){	struct i2c_client *client = data;	struct msp3400c *msp = i2c_get_clientdata(client);	struct CARRIER_DETECT *cd;	int count, max1,max2,val1,val2, val,this;	printk("msp3400: kthread started\n");	for (;;) {		d2printk("msp3400: thread: sleep\n");		msp34xx_sleep(msp,-1);		d2printk("msp3400: thread: wakeup\n");	restart:		dprintk("msp3410: thread: restart scan\n");		msp->restart = 0;		if (kthread_should_stop())			break;		if (VIDEO_MODE_RADIO == msp->norm ||		    MSP_MODE_EXTERN  == msp->mode) {			/* no carrier scan, just unmute */			printk("msp3400: thread: no carrier scan\n");			msp3400c_setvolume(client, msp->muted,					   msp->volume, msp->balance);			continue;		}		/* mute */		msp3400c_setvolume(client, msp->muted, 0, 0);		msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ );		val1 = val2 = 0;		max1 = max2 = -1;		msp->watch_stereo = 0;		/* some time for the tuner to sync */		if (msp34xx_sleep(msp,200))			goto restart;		/* carrier detect pass #1 -- main carrier */		cd = carrier_detect_main; count = CARRIER_COUNT(carrier_detect_main);		if (amsound && (msp->norm == VIDEO_MODE_SECAM)) {			/* autodetect doesn't work well with AM ... */			max1 = 3;			count = 0;			dprintk("msp3400: AM sound override\n");		}		for (this = 0; this < count; this++) {			msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo);			if (msp34xx_sleep(msp,100))				goto restart;			val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b);			if (val > 32767)				val -= 65536;			if (val1 < val)				val1 = val, max1 = this;			dprintk("msp3400: carrier1 val: %5d / %s\n", val,cd[this].name);		}		/* carrier detect pass #2 -- second (stereo) carrier */		switch (max1) {		case 1: /* 5.5 */			cd = carrier_detect_55;			count = CARRIER_COUNT(carrier_detect_55);			break;		case 3: /* 6.5 */			cd = carrier_detect_65;			count = CARRIER_COUNT(carrier_detect_65);			break;		case 0: /* 4.5 */		case 2: /* 6.0 */		default:			cd = NULL; count = 0;			break;		}		if (amsound && (msp->norm == VIDEO_MODE_SECAM)) {			/* autodetect doesn't work well with AM ... */			cd = NULL; count = 0; max2 = 0;		}		for (this = 0; this < count; this++) {			msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo);			if (msp34xx_sleep(msp,100))				goto restart;			val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b);			if (val > 32767)				val -= 65536;			if (val2 < val)				val2 = val, max2 = this;			dprintk("msp3400: carrier2 val: %5d / %s\n", val,cd[this].name);		}		/* programm the msp3400 according to the results */		msp->main   = carrier_detect_main[max1].cdo;		switch (max1) {		case 1: /* 5.5 */			if (max2 == 0) {				/* B/G FM-stereo */				msp->second = carrier_detect_55[max2].cdo;				msp3400c_setmode(client, MSP_MODE_FM_TERRA);				msp->nicam_on = 0;				msp3400c_set_audmode(client, V4L2_TUNER_MODE_MONO);				msp->watch_stereo = 1;			} else if (max2 == 1 && HAVE_NICAM(msp)) {				/* B/G NICAM */				msp->second = carrier_detect_55[max2].cdo;				msp3400c_setmode(client, MSP_MODE_FM_NICAM1);				msp->nicam_on = 1;				msp3400c_setcarrier(client, msp->second, msp->main);				msp->watch_stereo = 1;			} else {				goto no_second;			}			break;		case 2: /* 6.0 */			/* PAL I NICAM */			msp->second = MSP_CARRIER(6.552);			msp3400c_setmode(client, MSP_MODE_FM_NICAM2);			msp->nicam_on = 1;			msp3400c_setcarrier(client, msp->second, msp->main);			msp->watch_stereo = 1;			break;		case 3: /* 6.5 */			if (max2 == 1 || max2 == 2) {				/* D/K FM-stereo */				msp->second = carrier_detect_65[max2].cdo;				msp3400c_setmode(client, MSP_MODE_FM_TERRA);				msp->nicam_on = 0;				msp3400c_set_audmode(client, V4L2_TUNER_MODE_MONO);				msp->watch_stereo = 1;			} else if (max2 == 0 &&				   msp->norm == VIDEO_MODE_SECAM) {				/* L NICAM or AM-mono */				msp->second = carrier_detect_65[max2].cdo;				msp3400c_setmode(client, MSP_MODE_AM_NICAM);				msp->nicam_on = 0;				msp3400c_set_audmode(client, V4L2_TUNER_MODE_MONO);				msp3400c_setcarrier(client, msp->second, msp->main);				/* volume prescale for SCART (AM mono input) */				msp3400c_write(client,I2C_MSP3400C_DFP, 0x000d, 0x1900);				msp->watch_stereo = 1;			} else if (max2 == 0 && HAVE_NICAM(msp)) {				/* D/K NICAM */				msp->second = carrier_detect_65[max2].cdo;				msp3400c_setmode(client, MSP_MODE_FM_NICAM1);				msp->nicam_on = 1;				msp3400c_setcarrier(client, msp->second, msp->main);				msp->watch_stereo = 1;			} else {				goto no_second;			}			break;		case 0: /* 4.5 */		default:		no_second:			msp->second = carrier_detect_main[max1].cdo;			msp3400c_setmode(client, MSP_MODE_FM_TERRA);			msp->nicam_on = 0;			msp3400c_setcarrier(client, msp->second, msp->main);			msp->rxsubchans = V4L2_TUNER_SUB_MONO;			msp3400c_set_audmode(client, V4L2_TUNER_MODE_MONO);			break;		}		/* unmute */		msp3400c_setvolume(client, msp->muted,

⌨️ 快捷键说明

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