msp3400-driver.c

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

C
1,275
字号
			break;		case AUDIO_EXTERN_1:			/* IN1 is often used for external input ... */			state->mode = MSP_MODE_EXTERN;			scart       = SCART_IN1;			break;		case AUDIO_EXTERN_2:			/* ... sometimes it is IN2 through ;) */			state->mode = MSP_MODE_EXTERN;			scart       = SCART_IN2;			break;		case AUDIO_TUNER:			state->mode = -1;			break;		}		if (scart) {			state->rxsubchans = V4L2_TUNER_SUB_STEREO;			state->audmode = V4L2_TUNER_MODE_STEREO;			msp_set_scart(client, scart, 0);			msp_write_dsp(client, 0x000d, 0x1900);		}		if (sarg->capability == V4L2_AUDCAP_STEREO) {			state->audmode = V4L2_TUNER_MODE_STEREO;		} else {			state->audmode &= ~V4L2_TUNER_MODE_STEREO;		}		msp_any_set_audmode(client, state->audmode);		msp_wake_thread(client);		break;	}	case VIDIOC_G_TUNER:	{		struct v4l2_tuner *vt = arg;		if (state->radio)			break;		if (state->opmode == OPMODE_AUTOSELECT)			msp_any_detect_stereo(client);		vt->audmode    = state->audmode;		vt->rxsubchans = state->rxsubchans;		vt->capability = V4L2_TUNER_CAP_STEREO |			V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;		break;	}	case VIDIOC_S_TUNER:	{		struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;		if (state->radio)			break;		/* only set audmode */		if (vt->audmode != -1 && vt->audmode != 0)			msp_any_set_audmode(client, vt->audmode);		break;	}	case VIDIOC_G_AUDOUT:	{		struct v4l2_audioout *a = (struct v4l2_audioout *)arg;		int idx = a->index;		memset(a, 0, sizeof(*a));		switch (idx) {		case 0:			strcpy(a->name, "Scart1 Out");			break;		case 1:			strcpy(a->name, "Scart2 Out");			break;		case 2:			strcpy(a->name, "I2S Out");			break;		default:			return -EINVAL;		}		break;	}	case VIDIOC_S_AUDOUT:	{		struct v4l2_audioout *a = (struct v4l2_audioout *)arg;		if (a->index < 0 || a->index > 2)			return -EINVAL;		v4l_dbg(1, msp_debug, client, "Setting audio out on msp34xx to input %i\n", a->index);		msp_set_scart(client, state->in_scart, a->index + 1);		break;	}	case VIDIOC_INT_I2S_CLOCK_FREQ:	{		u32 *a = (u32 *)arg;		v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", *a);		switch (*a) {			case 1024000:				state->i2s_mode = 0;				break;			case 2048000:				state->i2s_mode = 1;				break;			default:				return -EINVAL;		}		break;	}	case VIDIOC_QUERYCTRL:	{		struct v4l2_queryctrl *qc = arg;		int i;		for (i = 0; i < ARRAY_SIZE(msp_qctrl_std); i++)			if (qc->id && qc->id == msp_qctrl_std[i].id) {				memcpy(qc, &msp_qctrl_std[i], sizeof(*qc));				return 0;			}		if (!state->has_sound_processing)			return -EINVAL;		for (i = 0; i < ARRAY_SIZE(msp_qctrl_sound_processing); i++)			if (qc->id && qc->id == msp_qctrl_sound_processing[i].id) {				memcpy(qc, &msp_qctrl_sound_processing[i], sizeof(*qc));				return 0;			}		return -EINVAL;	}	case VIDIOC_G_CTRL:		return msp_get_ctrl(client, arg);	case VIDIOC_S_CTRL:		return msp_set_ctrl(client, arg);	case VIDIOC_LOG_STATUS:	{		const char *p;		if (state->opmode == OPMODE_AUTOSELECT)			msp_any_detect_stereo(client);		v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",				client->name, state->rev1, state->rev2);		v4l_info(client, "Audio:    volume %d%s\n",				state->volume, state->muted ? " (muted)" : "");		if (state->has_sound_processing) {			v4l_info(client, "Audio:    balance %d bass %d treble %d loudness %s\n",					state->balance, state->bass, state->treble,					state->loudness ? "on" : "off");		}		switch (state->mode) {		case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;		case MSP_MODE_FM_RADIO: p = "FM Radio"; break;		case MSP_MODE_FM_TERRA: p = "Terrestial FM-mono + FM-stereo"; break;		case MSP_MODE_FM_SAT: p = "Satellite FM-mono"; break;		case MSP_MODE_FM_NICAM1: p = "NICAM/FM (B/G, D/K)"; break;		case MSP_MODE_FM_NICAM2: p = "NICAM/FM (I)"; break;		case MSP_MODE_AM_NICAM: p = "NICAM/AM (L)"; break;		case MSP_MODE_BTSC: p = "BTSC"; break;		case MSP_MODE_EXTERN: p = "External input"; break;		default: p = "unknown"; break;		}		if (state->opmode == OPMODE_MANUAL) {			v4l_info(client, "Mode:     %s (%s%s)\n", p,				(state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",				(state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");		} else {			v4l_info(client, "Mode:     %s\n", p);			v4l_info(client, "Standard: %s (%s%s)\n",				msp_standard_std_name(state->std),				(state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",				(state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");		}		v4l_info(client, "ACB:      0x%04x\n", state->acb);		break;	}	default:		/* unknown */		return -EINVAL;	}	return 0;}static int msp_suspend(struct device * dev, pm_message_t state){	struct i2c_client *client = container_of(dev, struct i2c_client, dev);	v4l_dbg(1, msp_debug, client, "suspend\n");	msp_reset(client);	return 0;}static int msp_resume(struct device * dev){	struct i2c_client *client = container_of(dev, struct i2c_client, dev);	v4l_dbg(1, msp_debug, client, "resume\n");	msp_wake_thread(client);	return 0;}/* ----------------------------------------------------------------------- */static struct i2c_driver i2c_driver;static int msp_attach(struct i2c_adapter *adapter, int address, int kind){	struct i2c_client *client;	struct msp_state *state;	int (*thread_func)(void *data) = NULL;	int msp_hard;	int msp_family;	int msp_revision;	int msp_product, msp_prod_hi, msp_prod_lo;	int msp_rom;	client = kmalloc(sizeof(*client), GFP_KERNEL);	if (client == NULL)		return -ENOMEM;	memset(client, 0, sizeof(*client));	client->addr = address;	client->adapter = adapter;	client->driver = &i2c_driver;	snprintf(client->name, sizeof(client->name) - 1, "msp3400");	if (msp_reset(client) == -1) {		v4l_dbg(1, msp_debug, client, "msp3400 not found\n");		kfree(client);		return -1;	}	state = kmalloc(sizeof(*state), GFP_KERNEL);	if (state == NULL) {		kfree(client);		return -ENOMEM;	}	i2c_set_clientdata(client, state);	memset(state, 0, sizeof(*state));	state->v4l2_std = V4L2_STD_NTSC;	state->volume = 58880;	/* 0db gain */	state->balance = 32768;	/* 0db gain */	state->bass = 32768;	state->treble = 32768;	state->loudness = 0;	state->input = -1;	state->muted = 0;	state->i2s_mode = 0;	init_waitqueue_head(&state->wq);	state->rev1 = msp_read_dsp(client, 0x1e);	if (state->rev1 != -1)		state->rev2 = msp_read_dsp(client, 0x1f);	v4l_dbg(1, msp_debug, client, "rev1=0x%04x, rev2=0x%04x\n", state->rev1, state->rev2);	if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) {		v4l_dbg(1, msp_debug, client, "not an msp3400 (cannot read chip version)\n");		kfree(state);		kfree(client);		return -1;	}	msp_set_audio(client);	msp_family = ((state->rev1 >> 4) & 0x0f) + 3;	msp_product = (state->rev2 >> 8) & 0xff;	msp_prod_hi = msp_product / 10;	msp_prod_lo = msp_product % 10;	msp_revision = (state->rev1 & 0x0f) + '@';	msp_hard = ((state->rev1 >> 8) & 0xff) + '@';	msp_rom = state->rev2 & 0x1f;	snprintf(client->name, sizeof(client->name), "MSP%d4%02d%c-%c%d",			msp_family, msp_product,			msp_revision, msp_hard, msp_rom);	/* Has NICAM support: all mspx41x and mspx45x products have NICAM */	state->has_nicam = msp_prod_hi == 1 || msp_prod_hi == 5;	/* Has radio support: was added with revision G */	state->has_radio = msp_revision >= 'G';	/* Has headphones output: not for stripped down products */	state->has_headphones = msp_prod_lo < 5;	/* Has scart4 input: not in pre D revisions, not in stripped D revs */	state->has_scart4 = msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5);	/* Has scart2 and scart3 inputs and scart2 output: not in stripped	   down products of the '3' family */	state->has_scart23_in_scart2_out = msp_family >= 4 || msp_prod_lo < 5;	/* Has scart2 a volume control? Not in pre-D revisions. */	state->has_scart2_out_volume = msp_revision > 'C' && state->has_scart23_in_scart2_out;	/* Has a configurable i2s out? */	state->has_i2s_conf = msp_revision >= 'G' && msp_prod_lo < 7;	/* Has subwoofer output: not in pre-D revs and not in stripped down products */	state->has_subwoofer = msp_revision >= 'D' && msp_prod_lo < 5;	/* Has soundprocessing (bass/treble/balance/loudness/equalizer): not in	   stripped down products */	state->has_sound_processing = msp_prod_lo < 7;	/* Has Virtual Dolby Surround: only in msp34x1 */	state->has_virtual_dolby_surround = msp_revision == 'G' && msp_prod_lo == 1;	/* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */	state->has_dolby_pro_logic = msp_revision == 'G' && msp_prod_lo == 2;	state->opmode = opmode;	if (state->opmode == OPMODE_AUTO) {		/* MSP revision G and up have both autodetect and autoselect */		if (msp_revision >= 'G')			state->opmode = OPMODE_AUTOSELECT;		/* MSP revision D and up have autodetect */		else if (msp_revision >= 'D')			state->opmode = OPMODE_AUTODETECT;		else			state->opmode = OPMODE_MANUAL;	}	/* hello world :-) */	v4l_info(client, "%s found @ 0x%x (%s)\n", client->name, address << 1, adapter->name);	v4l_info(client, "%s ", client->name);	if (state->has_nicam && state->has_radio)		printk("supports nicam and radio, ");	else if (state->has_nicam)		printk("supports nicam, ");	else if (state->has_radio)		printk("supports radio, ");	printk("mode is ");	/* version-specific initialization */	switch (state->opmode) {	case OPMODE_MANUAL:		printk("manual");		thread_func = msp3400c_thread;		break;	case OPMODE_AUTODETECT:		printk("autodetect");		thread_func = msp3410d_thread;		break;	case OPMODE_AUTOSELECT:		printk("autodetect and autoselect");		thread_func = msp34xxg_thread;		break;	}	printk("\n");	/* startup control thread if needed */	if (thread_func) {		state->kthread = kthread_run(thread_func, client, "msp34xx");		if (state->kthread == NULL)			v4l_warn(client, "kernel_thread() failed\n");		msp_wake_thread(client);	}	/* done */	i2c_attach_client(client);	return 0;}static int msp_probe(struct i2c_adapter *adapter){	if (adapter->class & I2C_CLASS_TV_ANALOG)		return i2c_probe(adapter, &addr_data, msp_attach);	return 0;}static int msp_detach(struct i2c_client *client){	struct msp_state *state = i2c_get_clientdata(client);	int err;	/* shutdown control thread */	if (state->kthread) {		state->restart = 1;		kthread_stop(state->kthread);	}	msp_reset(client);	err = i2c_detach_client(client);	if (err) {		return err;	}	kfree(state);	kfree(client);	return 0;}/* ----------------------------------------------------------------------- *//* i2c implementation */static struct i2c_driver i2c_driver = {	.id             = I2C_DRIVERID_MSP3400,	.attach_adapter = msp_probe,	.detach_client  = msp_detach,	.command        = msp_command,	.driver = {		.name    = "msp3400",		.suspend = msp_suspend,		.resume  = msp_resume,	},};static int __init msp3400_init_module(void){	return i2c_add_driver(&i2c_driver);}static void __exit msp3400_cleanup_module(void){	i2c_del_driver(&i2c_driver);}module_init(msp3400_init_module);module_exit(msp3400_cleanup_module);/* * Overrides for Emacs so that we follow Linus's tabbing style. * --------------------------------------------------------------------------- * Local variables: * c-basic-offset: 8 * End: */

⌨️ 快捷键说明

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