📄 msp3400.c
字号:
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 + -