tda9887.c

来自「V4l driver for DVB HD」· C语言 代码 · 共 991 行 · 第 1/2 页

C
991
字号
	if (t->config & TDA9887_PORT2_ACTIVE)		buf[1] &= ~cOutputPort2Inactive;	if (t->config & TDA9887_PORT2_INACTIVE)		buf[1] |= cOutputPort2Inactive;	if (t->config & TDA9887_QSS)		buf[1] |= cQSS;	if (t->config & TDA9887_INTERCARRIER)		buf[1] &= ~cQSS;	if (t->config & TDA9887_AUTOMUTE)		buf[1] |= cAutoMuteFmActive;	if (t->config & TDA9887_DEEMPHASIS_MASK) {		buf[2] &= ~0x60;		switch (t->config & TDA9887_DEEMPHASIS_MASK) {		case TDA9887_DEEMPHASIS_NONE:			buf[2] |= cDeemphasisOFF;			break;		case TDA9887_DEEMPHASIS_50:			buf[2] |= cDeemphasisON | cDeemphasis50;			break;		case TDA9887_DEEMPHASIS_75:			buf[2] |= cDeemphasisON | cDeemphasis75;			break;		}	}	if (t->config & TDA9887_TOP_SET) {		buf[2] &= ~cTopMask;		buf[2] |= (t->config >> 8) & cTopMask;	}	if ((t->config & TDA9887_INTERCARRIER_NTSC) && (t->std & V4L2_STD_NTSC))		buf[1] &= ~cQSS;	return 0;}/* ---------------------------------------------------------------------- */static char pal[] = "--";static char secam[] = "--";static char ntsc[] = "-";#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)MODULE_PARM(pal,"s");MODULE_PARM(secam,"s");MODULE_PARM(ntsc,"s");#elsemodule_param_string(pal, pal, sizeof(pal), 0644);module_param_string(secam, secam, sizeof(secam), 0644);module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);#endifstatic int tda9887_fixup_std(struct tda9887 *t){	/* get more precise norm info from insmod option */	if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {		switch (pal[0]) {		case 'b':		case 'B':		case 'g':		case 'G':		case 'h':		case 'H':		case 'n':		case 'N':			if (pal[1] == 'c' || pal[1] == 'C') {				tda9887_dbg("insmod fixup: PAL => PAL-Nc\n");				t->std = V4L2_STD_PAL_Nc;			} else {				tda9887_dbg("insmod fixup: PAL => PAL-BGHN\n");				t->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N;			}			break;		case 'i':		case 'I':			tda9887_dbg("insmod fixup: PAL => PAL-I\n");			t->std = V4L2_STD_PAL_I;			break;		case 'd':		case 'D':		case 'k':		case 'K':			tda9887_dbg("insmod fixup: PAL => PAL-DK\n");			t->std = V4L2_STD_PAL_DK;			break;		case 'm':		case 'M':			tda9887_dbg("insmod fixup: PAL => PAL-M\n");			t->std = V4L2_STD_PAL_M;			break;		case '-':			/* default parameter, do nothing */			break;		default:			tda9887_info("pal= argument not recognised\n");			break;		}	}	if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {		switch (secam[0]) {		case 'b':		case 'B':		case 'g':		case 'G':		case 'h':		case 'H':			tda9887_dbg("insmod fixup: SECAM => SECAM-BGH\n");			t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;			break;		case 'd':		case 'D':		case 'k':		case 'K':			tda9887_dbg("insmod fixup: SECAM => SECAM-DK\n");			t->std = V4L2_STD_SECAM_DK;			break;		case 'l':		case 'L':			if (secam[1] == 'c' || secam[1] == 'C') {				tda9887_dbg("insmod fixup: SECAM => SECAM-L'\n");				t->std = V4L2_STD_SECAM_LC;			} else {				tda9887_dbg("insmod fixup: SECAM => SECAM-L\n");				t->std = V4L2_STD_SECAM_L;			}			break;		case '-':			/* default parameter, do nothing */			break;		default:			tda9887_info("secam= argument not recognised\n");			break;		}	}	if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {		switch (ntsc[0]) {		case 'm':		case 'M':			tda9887_dbg("insmod fixup: NTSC => NTSC-M\n");			t->std = V4L2_STD_NTSC_M;			break;		case 'j':		case 'J':			tda9887_dbg("insmod fixup: NTSC => NTSC_M_JP\n");			t->std = V4L2_STD_NTSC_M_JP;			break;		case 'k':		case 'K':			tda9887_dbg("insmod fixup: NTSC => NTSC_M_KR\n");			t->std = V4L2_STD_NTSC_M_KR;			break;		case '-':			/* default parameter, do nothing */			break;		default:			tda9887_info("ntsc= argument not recognised\n");			break;		}	}	return 0;}static int tda9887_status(struct tda9887 *t){	unsigned char buf[1];	int rc;	memset(buf,0,sizeof(buf));	if (1 != (rc = i2c_master_recv(&t->client,buf,1)))		tda9887_info("i2c i/o error: rc == %d (should be 1)\n",rc);	dump_read_message(t, buf);	return 0;}static int tda9887_configure(struct tda9887 *t){	int rc;	memset(t->data,0,sizeof(t->data));	tda9887_set_tvnorm(t,t->data);	/* A note on the port settings:	   These settings tend to depend on the specifics of the board.	   By default they are set to inactive (bit value 1) by this driver,	   overwriting any changes made by the tvnorm. This means that it	   is the responsibility of the module using the tda9887 to set	   these values in case of changes in the tvnorm.	   In many cases port 2 should be made active (0) when selecting	   SECAM-L, and port 2 should remain inactive (1) for SECAM-L'.	   For the other standards the tda9887 application note says that	   the ports should be set to active (0), but, again, that may	   differ depending on the precise hardware configuration.	 */	t->data[1] |= cOutputPort1Inactive;	t->data[1] |= cOutputPort2Inactive;	tda9887_set_config(t,t->data);	tda9887_set_insmod(t,t->data);	if (t->mode == T_STANDBY) {		t->data[1] |= cForcedMuteAudioON;	}	tda9887_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",		t->data[1],t->data[2],t->data[3]);	if (debug > 1)		dump_write_message(t, t->data);	if (4 != (rc = i2c_master_send(&t->client,t->data,4)))		tda9887_info("i2c i/o error: rc == %d (should be 4)\n",rc);	if (debug > 2) {		msleep_interruptible(1000);		tda9887_status(t);	}	return 0;}/* ---------------------------------------------------------------------- */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)static int tda9887_attach(struct i2c_adapter *adap, int addr, int kind)#elsestatic int tda9887_attach(struct i2c_adapter *adap, int addr,			  unsigned short flags, int kind)#endif{	struct tda9887 *t;	client_template.adapter = adap;	client_template.addr    = addr;	if (NULL == (t = kzalloc(sizeof(*t), GFP_KERNEL)))		return -ENOMEM;	t->client      = client_template;	t->std         = 0;	t->radio_mode = V4L2_TUNER_MODE_STEREO;	tda9887_info("chip found @ 0x%x (%s)\n", addr<<1, adap->name);	i2c_set_clientdata(&t->client, t);	i2c_attach_client(&t->client);#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)	MOD_INC_USE_COUNT;#endif	return 0;}static int tda9887_probe(struct i2c_adapter *adap){#ifdef I2C_CLASS_TV_ANALOG	if (adap->class & I2C_CLASS_TV_ANALOG)		return i2c_probe(adap, &addr_data, tda9887_attach);#else	switch (adap->id) {	case I2C_HW_B_BT848:	case I2C_HW_B_RIVA:#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)	case I2C_HW_SAA7134:#endif		return i2c_probe(adap, &addr_data, tda9887_attach);		break;	}#endif	return 0;}static int tda9887_detach(struct i2c_client *client){	struct tda9887 *t = i2c_get_clientdata(client);	i2c_detach_client(client);	kfree(t);#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)	MOD_DEC_USE_COUNT;#endif	return 0;}#define SWITCH_V4L2	if (!t->using_v4l2 && debug) \			  tda9887_info("switching to v4l2\n"); \			  t->using_v4l2 = 1;#define CHECK_V4L2	if (t->using_v4l2) { if (debug) \			  tda9887_info("ignore v4l1 call\n"); \			  return 0; }static inttda9887_command(struct i2c_client *client, unsigned int cmd, void *arg){	struct tda9887 *t = i2c_get_clientdata(client);	switch (cmd) {	/* --- configuration --- */	case AUDC_SET_RADIO:	{		t->mode = T_RADIO;		tda9887_configure(t);		break;	}	case TUNER_SET_STANDBY:	{		t->mode = T_STANDBY;		tda9887_configure(t);		break;	}	case TDA9887_SET_CONFIG:	{		int *i = arg;		t->config = *i;		tda9887_configure(t);		break;	}	/* --- v4l ioctls --- */	/* take care: bttv does userspace copying, we'll get a	   kernel pointer here... */	case VIDIOCSCHAN:	{		static const v4l2_std_id map[] = {			[ VIDEO_MODE_PAL   ] = V4L2_STD_PAL,			[ VIDEO_MODE_NTSC  ] = V4L2_STD_NTSC_M,			[ VIDEO_MODE_SECAM ] = V4L2_STD_SECAM,			[ 4 /* bttv */     ] = V4L2_STD_PAL_M,			[ 5 /* bttv */     ] = V4L2_STD_PAL_N,			[ 6 /* bttv */     ] = V4L2_STD_NTSC_M_JP,		};		struct video_channel *vc = arg;		CHECK_V4L2;		t->mode = T_ANALOG_TV;		if (vc->norm < ARRAY_SIZE(map))			t->std = map[vc->norm];		tda9887_fixup_std(t);		tda9887_configure(t);		break;	}	case VIDIOC_S_STD:	{		v4l2_std_id *id = arg;		SWITCH_V4L2;		t->mode = T_ANALOG_TV;		t->std   = *id;		tda9887_fixup_std(t);		tda9887_configure(t);		break;	}	case VIDIOC_S_FREQUENCY:	{		struct v4l2_frequency *f = arg;		SWITCH_V4L2;		if (V4L2_TUNER_ANALOG_TV == f->type) {			if (t->mode == T_ANALOG_TV)				return 0;			t->mode = T_ANALOG_TV;		}		if (V4L2_TUNER_RADIO == f->type) {			if (t->mode == T_RADIO)				return 0;			t->mode = T_RADIO;		}		tda9887_configure(t);		break;	}	case VIDIOC_G_TUNER:	{		static int AFC_BITS_2_kHz[] = {			-12500,  -37500,  -62500,  -97500,			-112500, -137500, -162500, -187500,			187500,  162500,  137500,  112500,			97500 ,  62500,   37500 ,  12500		};		struct v4l2_tuner* tuner = arg;		if (t->mode == T_RADIO) {			__u8 reg = 0;			tuner->afc=0;			if (1 == i2c_master_recv(&t->client,&reg,1))				tuner->afc = AFC_BITS_2_kHz[(reg>>1)&0x0f];		}		break;	}	case VIDIOC_S_TUNER:	{		struct v4l2_tuner* tuner = arg;		if (t->mode == T_RADIO) {			t->radio_mode = tuner->audmode;			tda9887_configure (t);		}		break;	}	case VIDIOC_LOG_STATUS:	{		tda9887_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", t->data[1], t->data[2], t->data[3]);		break;	}	default:		/* nothing */		break;	}	return 0;}#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,14)static int tda9887_suspend(struct device * dev, pm_message_t state)#else#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)static int tda9887_suspend(struct device * dev, pm_message_t state, u32 level)#elsestatic int tda9887_suspend(struct device * dev, u32 state, u32 level)#endif#endif{	struct i2c_client *c = container_of(dev, struct i2c_client, dev);	struct tda9887 *t = i2c_get_clientdata(c);	tda9887_dbg("suspend\n");	return 0;}#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,14)static int tda9887_resume(struct device * dev)#elsestatic int tda9887_resume(struct device * dev, u32 level)#endif{	struct i2c_client *c = container_of(dev, struct i2c_client, dev);	struct tda9887 *t = i2c_get_clientdata(c);	tda9887_dbg("resume\n");	tda9887_configure(t);	return 0;}#endif/* ----------------------------------------------------------------------- */static struct i2c_driver driver = {#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))&&(LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15))	.owner          = THIS_MODULE,#endif#if LINUX_VERSION_CODE <=  KERNEL_VERSION(2,6,15)	.name           = "tda9887",	.flags          = I2C_DF_NOTIFY,#endif	.id             = I2C_DRIVERID_TDA9887,	.attach_adapter = tda9887_probe,	.detach_client  = tda9887_detach,	.command        = tda9887_command,#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)	.driver = {#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15)		.name    = "tda9887",#endif		.suspend = tda9887_suspend,		.resume  = tda9887_resume,	},#endif};static struct i2c_client client_template ={	.name      = "tda9887",#if LINUX_VERSION_CODE <=  KERNEL_VERSION(2,6,15)	.flags     = I2C_CLIENT_ALLOW_USE,#endif	.driver    = &driver,};static int __init tda9887_init_module(void){	return i2c_add_driver(&driver);}static void __exit tda9887_cleanup_module(void){	i2c_del_driver(&driver);}module_init(tda9887_init_module);module_exit(tda9887_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 + -
显示快捷键?