msp3400-kthreads.c

来自「linux2.6.16版本」· C语言 代码 · 共 1,011 行 · 第 1/2 页

C
1,011
字号
		for (this = 0; this < count; this++) {			msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo);			if (msp_sleep(state,100))				goto restart;			val = msp_read_dsp(client, 0x1b);			if (val > 32767)				val -= 65536;			if (val1 < val)				val1 = val, max1 = this;			v4l_dbg(1, msp_debug, client, "carrier1 val: %5d / %s\n", val,cd[this].name);		}		/* carrier detect pass #2 -- second (stereo) carrier */		switch (max1) {		case 1: /* 5.5 */			cd = msp3400c_carrier_detect_55;			count = ARRAY_SIZE(msp3400c_carrier_detect_55);			break;		case 3: /* 6.5 */			cd = msp3400c_carrier_detect_65;			count = ARRAY_SIZE(msp3400c_carrier_detect_65);			break;		case 0: /* 4.5 */		case 2: /* 6.0 */		default:			cd = NULL;			count = 0;			break;		}		if (msp_amsound && (state->v4l2_std & V4L2_STD_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 (msp_sleep(state,100))				goto restart;			val = msp_read_dsp(client, 0x1b);			if (val > 32767)				val -= 65536;			if (val2 < val)				val2 = val, max2 = this;			v4l_dbg(1, msp_debug, client, "carrier2 val: %5d / %s\n", val,cd[this].name);		}		/* program the msp3400 according to the results */		state->main   = msp3400c_carrier_detect_main[max1].cdo;		switch (max1) {		case 1: /* 5.5 */			if (max2 == 0) {				/* B/G FM-stereo */				state->second = msp3400c_carrier_detect_55[max2].cdo;				msp3400c_setmode(client, MSP_MODE_FM_TERRA);				state->nicam_on = 0;				msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO);				state->watch_stereo = 1;			} else if (max2 == 1 && state->has_nicam) {				/* B/G NICAM */				state->second = msp3400c_carrier_detect_55[max2].cdo;				msp3400c_setmode(client, MSP_MODE_FM_NICAM1);				state->nicam_on = 1;				msp3400c_setcarrier(client, state->second, state->main);				state->watch_stereo = 1;			} else {				goto no_second;			}			break;		case 2: /* 6.0 */			/* PAL I NICAM */			state->second = MSP_CARRIER(6.552);			msp3400c_setmode(client, MSP_MODE_FM_NICAM2);			state->nicam_on = 1;			msp3400c_setcarrier(client, state->second, state->main);			state->watch_stereo = 1;			break;		case 3: /* 6.5 */			if (max2 == 1 || max2 == 2) {				/* D/K FM-stereo */				state->second = msp3400c_carrier_detect_65[max2].cdo;				msp3400c_setmode(client, MSP_MODE_FM_TERRA);				state->nicam_on = 0;				msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO);				state->watch_stereo = 1;			} else if (max2 == 0 && (state->v4l2_std & V4L2_STD_SECAM)) {				/* L NICAM or AM-mono */				state->second = msp3400c_carrier_detect_65[max2].cdo;				msp3400c_setmode(client, MSP_MODE_AM_NICAM);				state->nicam_on = 0;				msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO);				msp3400c_setcarrier(client, state->second, state->main);				/* volume prescale for SCART (AM mono input) */				msp_write_dsp(client, 0x000d, 0x1900);				state->watch_stereo = 1;			} else if (max2 == 0 && state->has_nicam) {				/* D/K NICAM */				state->second = msp3400c_carrier_detect_65[max2].cdo;				msp3400c_setmode(client, MSP_MODE_FM_NICAM1);				state->nicam_on = 1;				msp3400c_setcarrier(client, state->second, state->main);				state->watch_stereo = 1;			} else {				goto no_second;			}			break;		case 0: /* 4.5 */		default:		no_second:			state->second = msp3400c_carrier_detect_main[max1].cdo;			msp3400c_setmode(client, MSP_MODE_FM_TERRA);			state->nicam_on = 0;			msp3400c_setcarrier(client, state->second, state->main);			state->rxsubchans = V4L2_TUNER_SUB_MONO;			msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO);			break;		}		/* unmute */		msp_set_audio(client);		if (msp_debug)			msp3400c_print_mode(client);		/* monitor tv audio mode */		while (state->watch_stereo) {			if (msp_sleep(state,5000))				goto restart;			watch_stereo(client);		}	}	v4l_dbg(1, msp_debug, client, "thread: exit\n");	return 0;}int msp3410d_thread(void *data){	struct i2c_client *client = data;	struct msp_state *state = i2c_get_clientdata(client);	int val, i, std;	v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n");	for (;;) {		v4l_dbg(2, msp_debug, client, "msp3410 thread: sleep\n");		msp_sleep(state,-1);		v4l_dbg(2, msp_debug, client, "msp3410 thread: wakeup\n");	restart:		v4l_dbg(1, msp_debug, client, "thread: restart scan\n");		state->restart = 0;		if (kthread_should_stop())			break;		if (state->mode == MSP_MODE_EXTERN) {			/* no carrier scan needed, just unmute */			v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n");			msp_set_audio(client);			continue;		}		/* put into sane state (and mute) */		msp_reset(client);		/* some time for the tuner to sync */		if (msp_sleep(state,200))			goto restart;		/* start autodetect */		if (state->radio)			std = 0x40;		else			std = (state->v4l2_std & V4L2_STD_NTSC) ? 0x20 : 1;		state->watch_stereo = 0;		if (msp_debug)			v4l_dbg(1, msp_debug, client, "setting standard: %s (0x%04x)\n",			       msp_standard_std_name(std), std);		if (std != 1) {			/* programmed some specific mode */			val = std;		} else {			/* triggered autodetect */			msp_write_dem(client, 0x20, std);			for (;;) {				if (msp_sleep(state, 100))					goto restart;				/* check results */				val = msp_read_dem(client, 0x7e);				if (val < 0x07ff)					break;				v4l_dbg(1, msp_debug, client, "detection still in progress\n");			}		}		for (i = 0; msp_stdlist[i].name != NULL; i++)			if (msp_stdlist[i].retval == val)				break;		v4l_dbg(1, msp_debug, client, "current standard: %s (0x%04x)\n",			msp_standard_std_name(val), val);		state->main   = msp_stdlist[i].main;		state->second = msp_stdlist[i].second;		state->std = val;		if (msp_amsound && !state->radio && (state->v4l2_std & V4L2_STD_SECAM) &&				(val != 0x0009)) {			/* autodetection has failed, let backup */			v4l_dbg(1, msp_debug, client, "autodetection failed,"				" switching to backup standard: %s (0x%04x)\n",				msp_stdlist[8].name ? msp_stdlist[8].name : "unknown",val);			val = 0x0009;			msp_write_dem(client, 0x20, val);		}		/* set various prescales */		msp_write_dsp(client, 0x0d, 0x1900); /* scart */		msp_write_dsp(client, 0x0e, 0x2403); /* FM */		msp_write_dsp(client, 0x10, 0x5a00); /* nicam */		/* set stereo */		switch (val) {		case 0x0008: /* B/G NICAM */		case 0x000a: /* I NICAM */			if (val == 0x0008)				state->mode = MSP_MODE_FM_NICAM1;			else				state->mode = MSP_MODE_FM_NICAM2;			/* just turn on stereo */			state->rxsubchans = V4L2_TUNER_SUB_STEREO;			state->nicam_on = 1;			state->watch_stereo = 1;			msp3400c_setstereo(client,V4L2_TUNER_MODE_STEREO);			break;		case 0x0009:			state->mode = MSP_MODE_AM_NICAM;			state->rxsubchans = V4L2_TUNER_SUB_MONO;			state->nicam_on = 1;			msp3400c_setstereo(client,V4L2_TUNER_MODE_MONO);			state->watch_stereo = 1;			break;		case 0x0020: /* BTSC */			/* just turn on stereo */			state->mode = MSP_MODE_BTSC;			state->rxsubchans = V4L2_TUNER_SUB_STEREO;			state->nicam_on = 0;			state->watch_stereo = 1;			msp3400c_setstereo(client,V4L2_TUNER_MODE_STEREO);			break;		case 0x0040: /* FM radio */			state->mode = MSP_MODE_FM_RADIO;			state->rxsubchans = V4L2_TUNER_SUB_STEREO;			state->audmode = V4L2_TUNER_MODE_STEREO;			state->nicam_on = 0;			state->watch_stereo = 0;			/* not needed in theory if we have radio, but			   short programming enables carrier mute */			msp3400c_setmode(client, MSP_MODE_FM_RADIO);			msp3400c_setcarrier(client, MSP_CARRIER(10.7),					    MSP_CARRIER(10.7));			/* scart routing */			msp_set_scart(client,SCART_IN2,0);			/* msp34xx does radio decoding */			msp_write_dsp(client, 0x08, 0x0020);			msp_write_dsp(client, 0x09, 0x0020);			msp_write_dsp(client, 0x0b, 0x0020);			break;		case 0x0003:		case 0x0004:		case 0x0005:			state->mode = MSP_MODE_FM_TERRA;			state->rxsubchans = V4L2_TUNER_SUB_MONO;			state->audmode = V4L2_TUNER_MODE_MONO;			state->nicam_on = 0;			state->watch_stereo = 1;			break;		}		/* unmute, restore misc registers */		msp_set_audio(client);		msp_write_dsp(client, 0x13, state->acb);		if (state->has_i2s_conf)			msp_write_dem(client, 0x40, state->i2s_mode);		/* monitor tv audio mode */		while (state->watch_stereo) {			if (msp_sleep(state,5000))				goto restart;			watch_stereo(client);		}	}	v4l_dbg(1, msp_debug, client, "thread: exit\n");	return 0;}/* ----------------------------------------------------------------------- *//* msp34xxG + (autoselect no-thread)                                       *//* this one uses both automatic standard detection and automatic sound     *//* select which are available in the newer G versions                      *//* struct msp: only norm, acb and source are really used in this mode      *//* set the same 'source' for the loudspeaker, scart and quasi-peak detector * the value for source is the same as bit 15:8 of DSP registers 0x08, * 0x0a and 0x0c: 0=mono, 1=stereo or A|B, 2=SCART, 3=stereo or A, 4=stereo or B * * this function replaces msp3400c_setstereo */static void msp34xxg_set_source(struct i2c_client *client, int source){	struct msp_state *state = i2c_get_clientdata(client);	/* fix matrix mode to stereo and let the msp choose what	 * to output according to 'source', as recommended	 * for MONO (source==0) downmixing set bit[7:0] to 0x30	 */	int value = (source & 0x07) << 8 | (source == 0 ? 0x30 : 0x20);	v4l_dbg(1, msp_debug, client, "set source to %d (0x%x)\n", source, value);	/* Loudspeaker Output */	msp_write_dsp(client, 0x08, value);	/* SCART1 DA Output */	msp_write_dsp(client, 0x0a, value);	/* Quasi-peak detector */	msp_write_dsp(client, 0x0c, value);	/*	 * set identification threshold. Personally, I	 * I set it to a higher value that the default	 * of 0x190 to ignore noisy stereo signals.	 * this needs tuning. (recommended range 0x00a0-0x03c0)	 * 0x7f0 = forced mono mode	 */	/* a2 threshold for stereo/bilingual */	msp_write_dem(client, 0x22, msp_stereo_thresh);	state->source = source;}/* (re-)initialize the msp34xxg, according to the current norm in state->norm * return 0 if it worked, -1 if it failed */static int msp34xxg_reset(struct i2c_client *client){	struct msp_state *state = i2c_get_clientdata(client);	int modus, std;	if (msp_reset(client))		return -1;	/* make sure that input/output is muted (paranoid mode) */	/* ACB, mute DSP input, mute SCART 1 */	if (msp_write_dsp(client, 0x13, 0x0f20))		return -1;	if (state->has_i2s_conf)		msp_write_dem(client, 0x40, state->i2s_mode);	/* step-by-step initialisation, as described in the manual */	modus = msp_modus(client);	if (state->radio)		std = 0x40;	else		std = (state->v4l2_std & V4L2_STD_NTSC) ? 0x20 : 1;	modus &= ~0x03; /* STATUS_CHANGE = 0 */	modus |= 0x01;  /* AUTOMATIC_SOUND_DETECTION = 1 */	if (msp_write_dem(client, 0x30, modus))		return -1;	if (msp_write_dem(client, 0x20, std))		return -1;	/* write the dsps that may have an influence on	   standard/audio autodetection right now */	msp34xxg_set_source(client, state->source);	/* AM/FM Prescale [15:8] 75khz deviation */	if (msp_write_dsp(client, 0x0e, 0x3000))		return -1;	/* NICAM Prescale 9db gain (as recommended) */	if (msp_write_dsp(client, 0x10, 0x5a00))		return -1;	return 0;}int msp34xxg_thread(void *data){	struct i2c_client *client = data;	struct msp_state *state = i2c_get_clientdata(client);	int val, std, i;	v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n");	state->source = 1; /* default */	for (;;) {		v4l_dbg(2, msp_debug, client, "msp34xxg thread: sleep\n");		msp_sleep(state, -1);		v4l_dbg(2, msp_debug, client, "msp34xxg thread: wakeup\n");	restart:		v4l_dbg(1, msp_debug, client, "thread: restart scan\n");		state->restart = 0;		if (kthread_should_stop())			break;		/* setup the chip*/		msp34xxg_reset(client);		std = msp_standard;		if (std != 0x01)			goto unmute;		/* watch autodetect */		v4l_dbg(1, msp_debug, client, "triggered autodetect, waiting for result\n");		for (i = 0; i < 10; i++) {			if (msp_sleep(state, 100))				goto restart;			/* check results */			val = msp_read_dem(client, 0x7e);			if (val < 0x07ff) {				std = val;				break;			}			v4l_dbg(2, msp_debug, client, "detection still in progress\n");		}		if (std == 1) {			v4l_dbg(1, msp_debug, client, "detection still in progress after 10 tries. giving up.\n");			continue;		}	unmute:		state->std = std;		v4l_dbg(1, msp_debug, client, "current standard: %s (0x%04x)\n",			msp_standard_std_name(std), std);		/* unmute: dispatch sound to scart output, set scart volume */		msp_set_audio(client);		/* restore ACB */		if (msp_write_dsp(client, 0x13, state->acb))			return -1;		msp_write_dem(client, 0x40, state->i2s_mode);	}	v4l_dbg(1, msp_debug, client, "thread: exit\n");	return 0;}void msp34xxg_detect_stereo(struct i2c_client *client){	struct msp_state *state = i2c_get_clientdata(client);	int status = msp_read_dem(client, 0x0200);	int is_bilingual = status & 0x100;	int is_stereo = status & 0x40;	state->rxsubchans = 0;	if (is_stereo)		state->rxsubchans |= V4L2_TUNER_SUB_STEREO;	else		state->rxsubchans |= V4L2_TUNER_SUB_MONO;	if (is_bilingual) {		state->rxsubchans |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;		/* I'm supposed to check whether it's SAP or not		 * and set only LANG2/SAP in this case. Yet, the MSP		 * does a lot of work to hide this and handle everything		 * the same way. I don't want to work around it so unless		 * this is a problem, I'll handle SAP just like lang1/lang2.		 */	}	v4l_dbg(1, msp_debug, client, "status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n",		status, is_stereo, is_bilingual, state->rxsubchans);}void msp34xxg_set_audmode(struct i2c_client *client, int audmode){	struct msp_state *state = i2c_get_clientdata(client);	int source;	switch (audmode) {	case V4L2_TUNER_MODE_MONO:		source = 0; /* mono only */		break;	case V4L2_TUNER_MODE_STEREO:		source = 1; /* stereo or A|B, see comment in msp34xxg_get_v4l2_stereo() */		/* problem: that could also mean 2 (scart input) */		break;	case V4L2_TUNER_MODE_LANG1:		source = 3; /* stereo or A */		break;	case V4L2_TUNER_MODE_LANG2:		source = 4; /* stereo or B */		break;	default:		audmode = 0;		source  = 1;		break;	}	state->audmode = audmode;	msp34xxg_set_source(client, source);}

⌨️ 快捷键说明

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