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

📄 msp3400.c

📁 This program is free software you can redistribute it and/or modify it under the terms of the GNU Ge
💻 C
📖 第 1 页 / 共 4 页
字号:

done:
	dprintk(KERN_DEBUG "msp3410: thread: exit\n");
	msp->active = 0;
	msp->thread = NULL;

	if(msp->notify != NULL)
		up(msp->notify);
	return 0;
}

/* ----------------------------------------------------------------------- */

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
static int msp_attach(struct i2c_adapter *adap, int addr, int kind);
#else
static int msp_attach(struct i2c_adapter *adap, int addr,
		      unsigned short flags, int kind);
#endif
static int msp_detach(struct i2c_client *client);
static int msp_probe(struct i2c_adapter *adap);
static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg);

static struct i2c_driver driver = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
	.owner          = THIS_MODULE,
#endif
        .name           = "i2c msp3400 driver",
        .id             = I2C_DRIVERID_MSP3400,
        .flags          = I2C_DF_NOTIFY,
        .attach_adapter = msp_probe,
        .detach_client  = msp_detach,
        .command        = msp_command,
};

static struct i2c_client client_template = 
{
	I2C_DEVNAME("(unset)"),
	.flags     = I2C_CLIENT_ALLOW_USE,
        .driver    = &driver,
};

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
static int msp_attach(struct i2c_adapter *adap, int addr, int kind)
#else
static int msp_attach(struct i2c_adapter *adap, int addr,
		      unsigned short flags, int kind)
#endif
{
	DECLARE_MUTEX_LOCKED(sem);
	struct msp3400c *msp;
        struct i2c_client *c;
	int i, rc;

        client_template.adapter = adap;
        client_template.addr = addr;

        if (-1 == msp3400c_reset(&client_template)) {
                dprintk("msp3400: no chip found\n");
                return -1;
        }
	
        if (NULL == (c = kmalloc(sizeof(struct i2c_client),GFP_KERNEL)))
                return -ENOMEM;
        memcpy(c,&client_template,sizeof(struct i2c_client));
	if (NULL == (msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL))) {
		kfree(c);
		return -ENOMEM;
	}
	
	memset(msp,0,sizeof(struct msp3400c));
	msp->left   = 65535;
	msp->right  = 65535;
	msp->bass   = 32768;
	msp->treble = 32768;
	msp->input  = -1;
	msp->muted  = 1;
	for (i = 0; i < DFP_COUNT; i++)
		msp->dfp_regs[i] = -1;

	i2c_set_clientdata(c, msp);
	init_waitqueue_head(&msp->wq);

	if (-1 == msp3400c_reset(c)) {
		kfree(msp);
		kfree(c);
		dprintk("msp3400: no chip found\n");
		return -1;
	}
    
	msp->rev1 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1e);
	if (-1 != msp->rev1)
		msp->rev2 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1f);
	if ((-1 == msp->rev1) || (0 == msp->rev1 && 0 == msp->rev2)) {
		kfree(msp);
		kfree(c);
		printk("msp3400: error while reading chip version\n");
		return -1;
	}

#if 0
	/* this will turn on a 1kHz beep - might be useful for debugging... */
	msp3400c_write(c,I2C_MSP3400C_DFP, 0x0014, 0x1040);
#endif
	msp3400c_setvolume(c,msp->muted,msp->left,msp->right);

	snprintf(c->name, sizeof(c->name), "MSP34%02d%c-%c%d",
		 (msp->rev2>>8)&0xff, (msp->rev1&0xff)+'@',
		 ((msp->rev1>>8)&0xff)+'@', msp->rev2&0x1f);

	if (simple == -1) {
		/* default mode */
		msp->simple = HAVE_SIMPLE(msp);
	} else {
		/* use insmod option */
		msp->simple = simple;
	}

	/* timer for stereo checking */
	init_timer(&msp->wake_stereo);
	msp->wake_stereo.function = msp3400c_stereo_wake;
	msp->wake_stereo.data     = (unsigned long)msp;

	/* hello world :-) */
	printk(KERN_INFO "msp34xx: init: chip=%s",i2c_clientname(c));
	if (HAVE_NICAM(msp))
		printk(" +nicam");
	if (HAVE_SIMPLE(msp))
		printk(" +simple");
	if (HAVE_RADIO(msp))
		printk(" +radio");
	printk("\n");

	/* startup control thread */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
	MOD_INC_USE_COUNT;
#endif
	msp->notify = &sem;
	rc = kernel_thread(msp->simple ? msp3410d_thread : msp3400c_thread,
			   (void *)c, 0);
	if (rc < 0)
		printk(KERN_WARNING "msp34xx: kernel_thread() failed\n");
	else
		down(&sem);
	msp->notify = NULL;
	wake_up_interruptible(&msp->wq);

	/* update our own array */
	for (i = 0; i < MSP3400_MAX; i++) {
		if (NULL == msps[i]) {
			msps[i] = c;
			break;
		}
	}
	
	/* done */
        i2c_attach_client(c);
	return 0;
}

static int msp_detach(struct i2c_client *client)
{
	DECLARE_MUTEX_LOCKED(sem);
	struct msp3400c *msp  = i2c_get_clientdata(client);
	int i;
	
	/* shutdown control thread */
	del_timer(&msp->wake_stereo);
	if (msp->thread) 
	{
		msp->notify = &sem;
		msp->rmmod = 1;
		wake_up_interruptible(&msp->wq);
		down(&sem);
		msp->notify = NULL;
	}
    	msp3400c_reset(client);

        /* update our own array */
	for (i = 0; i < MSP3400_MAX; i++) {
		if (client == msps[i]) {
			msps[i] = NULL;
			break;
		}
	}

	i2c_detach_client(client);
	kfree(msp);
	kfree(client);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
	MOD_DEC_USE_COUNT;
#endif
	return 0;
}

static int msp_probe(struct i2c_adapter *adap)
{
#ifdef I2C_ADAP_CLASS_TV_ANALOG
	if (adap->class & I2C_ADAP_CLASS_TV_ANALOG)
		return i2c_probe(adap, &addr_data, msp_attach);
#else
	if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848))
		return i2c_probe(adap, &addr_data, msp_attach);
#endif
	return 0;
}

static void msp_wake_thread(struct i2c_client *client)
{
	struct msp3400c *msp  = i2c_get_clientdata(client);

	msp3400c_setvolume(client,msp->muted,0,0);
	msp->watch_stereo=0;
	del_timer(&msp->wake_stereo);
	if (msp->active)
		msp->restart = 1;
	wake_up_interruptible(&msp->wq);
}

static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
	struct msp3400c *msp  = i2c_get_clientdata(client);
        __u16           *sarg = arg;
#if 0
	int             *iarg = (int*)arg;
#endif

	switch (cmd) {

	case AUDC_SET_INPUT:
		/* scart switching
		     - IN1 is often used for external input
		     - Hauppauge uses IN2 for the radio */
		dprintk(KERN_DEBUG "msp34xx: AUDC_SET_INPUT(%d)\n",*sarg);
		if (*sarg == msp->input)
			break;
		msp->input = *sarg;
		switch (*sarg) {
		case AUDIO_RADIO:
			msp->mode   = MSP_MODE_FM_RADIO;
			msp->stereo = VIDEO_SOUND_STEREO;
			msp3400c_set_scart(client,SCART_IN2,0);
			msp3400c_write(client,I2C_MSP3400C_DFP,0x000d,0x1900);
			msp3400c_setstereo(client,msp->stereo);
			break;
		case AUDIO_EXTERN:
			msp->mode   = MSP_MODE_EXTERN;
			msp->stereo = VIDEO_SOUND_STEREO;
			msp3400c_set_scart(client,SCART_IN1,0);
			msp3400c_write(client,I2C_MSP3400C_DFP,0x000d,0x1900);
			msp3400c_setstereo(client,msp->stereo);
			break;
		case AUDIO_TUNER:
			msp->mode   = -1;
			msp_wake_thread(client);
			break;
		default:
			if (*sarg & AUDIO_MUTE)
				msp3400c_set_scart(client,SCART_MUTE,0);
			break;
		}
		if (msp->active)
			msp->restart = 1;
		break;

	case AUDC_SET_RADIO:
		dprintk(KERN_DEBUG "msp34xx: AUDC_SET_RADIO\n");
		msp->norm = VIDEO_MODE_RADIO;
		msp->watch_stereo=0;
		del_timer(&msp->wake_stereo);
		dprintk(KERN_DEBUG "msp34xx: switching to radio mode\n");
		if (msp->simple) {
			/* the thread will do for us */
			msp_wake_thread(client);
		} else {
			/* set msp3400 to FM radio mode */
			msp3400c_setmode(client,MSP_MODE_FM_RADIO);
			msp3400c_setcarrier(client, MSP_CARRIER(10.7),MSP_CARRIER(10.7));
			msp3400c_setvolume(client,msp->muted,msp->left,msp->right);			
		}
		if (msp->active)
			msp->restart = 1;
		break;

#if 1
	/* work-in-progress:  hook to control the DFP registers */
	case MSP_SET_DFPREG:
	{
		struct msp_dfpreg *r = arg;
		unsigned int i;

		if (r->reg < 0 || r->reg >= DFP_COUNT)
			return -EINVAL;
		for (i = 0; i < ARRAY_SIZE(bl_dfp); i++)
			if (r->reg == bl_dfp[i])
				return -EINVAL;
		msp->dfp_regs[r->reg] = r->value;
		msp3400c_write(client,I2C_MSP3400C_DFP,r->reg,r->value);
		return 0;
	}
	case MSP_GET_DFPREG:
	{
		struct msp_dfpreg *r = arg;

		if (r->reg < 0 || r->reg >= DFP_COUNT)
			return -EINVAL;
		r->value = msp3400c_read(client,I2C_MSP3400C_DFP,r->reg);
		return 0;
	}
#endif

	/* --- v4l ioctls --- */
	/* take care: bttv does userspace copying, we'll get a
	   kernel pointer here... */
	case VIDIOCGAUDIO:
	{
		struct video_audio *va = arg;

		dprintk(KERN_DEBUG "msp34xx: VIDIOCGAUDIO\n");
		va->flags |= VIDEO_AUDIO_VOLUME |
			VIDEO_AUDIO_BASS |
			VIDEO_AUDIO_TREBLE |
			VIDEO_AUDIO_MUTABLE;
		if (msp->muted)
			va->flags |= VIDEO_AUDIO_MUTE;
		va->volume=max(msp->left,msp->right);
		va->balance=(32768*min(msp->left,msp->right))/
			(va->volume ? va->volume : 1);
		va->balance=(msp->left<msp->right)?
			(65535-va->balance) : va->balance;
		if (0 == va->volume)
			va->balance = 32768;
		va->bass = msp->bass;
		va->treble = msp->treble;

		if (msp->norm != VIDEO_MODE_RADIO) {
			autodetect_stereo(client);
			va->mode = msp->stereo;
		}
		break;
	}
	case VIDIOCSAUDIO:
	{
		struct video_audio *va = arg;

		dprintk(KERN_DEBUG "msp34xx: VIDIOCSAUDIO\n");
		msp->muted = (va->flags & VIDEO_AUDIO_MUTE);
		msp->left = (min(65536 - va->balance,32768) *
			     va->volume) / 32768;
		msp->right = (min(va->balance,(__u16)32768) *
			      va->volume) / 32768;
		msp->bass = va->bass;
		msp->treble = va->treble;
		msp3400c_setvolume(client,msp->muted,msp->left,msp->right);
		msp3400c_setbass(client,msp->bass);
		msp3400c_settreble(client,msp->treble);

		if (va->mode != 0 && msp->norm != VIDEO_MODE_RADIO) {
			msp->watch_stereo=0;
			del_timer(&msp->wake_stereo);
			msp->stereo = va->mode & 0x0f;
			msp3400c_setstereo(client,va->mode & 0x0f);
		}
		break;
	}
	case VIDIOCSCHAN:
	{
		struct video_channel *vc = arg;
		
		dprintk(KERN_DEBUG "msp34xx: VIDIOCSCHAN\n");
		msp->norm = vc->norm;
		break;
	}
	case VIDIOCSFREQ:
	{
		/* new channel -- kick audio carrier scan */
		dprintk(KERN_DEBUG "msp34xx: VIDIOCSFREQ\n");
		msp_wake_thread(client);
		break;
	}

	default:
		/* nothing */
		break;
	}
	return 0;
}

/* ----------------------------------------------------------------------- */

static int msp3400_init_module(void)
{
	i2c_add_driver(&driver);
	return 0;
}

static void msp3400_cleanup_module(void)
{
	i2c_del_driver(&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 + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -