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

📄 tvaudio.c

📁 This program is free software you can redistribute it and/or modify it under the terms of the GNU Ge
💻 C
📖 第 1 页 / 共 4 页
字号:
		.checkit    = tda9874a_checkit,
		.initialize = tda9874a_initialize,
		.insmodopt  = &tda9874a,
		.addr_lo    = I2C_TDA9874 >> 1,
		.addr_hi    = I2C_TDA9874 >> 1,

		.getmode    = tda9874a_getmode,
		.setmode    = tda9874a_setmode,
		.checkmode  = generic_checkmode,
	},
	{
		.name       = "tda9850",
		.id         = I2C_DRIVERID_TDA9850,
		.insmodopt  = &tda9850,
		.addr_lo    = I2C_TDA985x_L >> 1,
		.addr_hi    = I2C_TDA985x_H >> 1,
		.registers  = 11,

		.getmode    = tda985x_getmode,
		.setmode    = tda985x_setmode,

		.init       = { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } }
	},
	{
		.name       = "tda9855",
		.id         = I2C_DRIVERID_TDA9855,
		.insmodopt  = &tda9855,
		.addr_lo    = I2C_TDA985x_L >> 1,
		.addr_hi    = I2C_TDA985x_H >> 1,
		.registers  = 11,
		.flags      = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE,

		.leftreg    = TDA9855_VL,
		.rightreg   = TDA9855_VR,
		.bassreg    = TDA9855_BA,
		.treblereg  = TDA9855_TR,
		.volfunc    = tda9855_volume,
		.bassfunc   = tda9855_bass,
		.treblefunc = tda9855_treble,

		.getmode    = tda985x_getmode,
		.setmode    = tda985x_setmode,

		.init       = { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2,
				    TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT,
				    TDA985x_STEREO | TDA9855_LINEAR | TDA9855_TZCM | TDA9855_VZCM,
				    0x07, 0x10, 0x10, 0x03 }}
	},
	{
		.name       = "tea6300",
		.id         = I2C_DRIVERID_TEA6300,
		.insmodopt  = &tea6300,
		.addr_lo    = I2C_TEA6300 >> 1,
		.addr_hi    = I2C_TEA6300 >> 1,
		.registers  = 6,
		.flags      = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL,

		.leftreg    = TEA6300_VR,
		.rightreg   = TEA6300_VL,
		.bassreg    = TEA6300_BA,
		.treblereg  = TEA6300_TR,
		.volfunc    = tea6300_shift10,
		.bassfunc   = tea6300_shift12,
		.treblefunc = tea6300_shift12,

		.inputreg   = TEA6300_S,
		.inputmap   = { TEA6300_S_SA, TEA6300_S_SB, TEA6300_S_SC },
		.inputmute  = TEA6300_S_GMU,
	},
	{
		.name       = "tea6420",
		.id         = I2C_DRIVERID_TEA6420,
		.insmodopt  = &tea6420,
		.addr_lo    = I2C_TEA6420 >> 1,
		.addr_hi    = I2C_TEA6420 >> 1,
		.registers  = 1,
		.flags      = CHIP_HAS_INPUTSEL,

		.inputreg   = -1,
		.inputmap   = { TEA6420_S_SA, TEA6420_S_SB, TEA6420_S_SC },
		.inputmute  = TEA6300_S_GMU,
	},
	{
		.name       = "tda8425",
		.id         = I2C_DRIVERID_TDA8425,
		.insmodopt  = &tda8425,
		.addr_lo    = I2C_TDA8425 >> 1,
		.addr_hi    = I2C_TDA8425 >> 1,
		.registers  = 9,
		.flags      = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL,

		.leftreg    = TDA8425_VL,
		.rightreg   = TDA8425_VR,
		.bassreg    = TDA8425_BA,
		.treblereg  = TDA8425_TR,
		.volfunc    = tda8425_shift10,
		.bassfunc   = tda8425_shift12,
		.treblefunc = tda8425_shift12,

		.inputreg   = TDA8425_S1,
		.inputmap   = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 },
		.inputmute  = TDA8425_S1_OFF,

		.setmode    = tda8425_setmode,
		.initialize = tda8425_initialize,
	},
	{
		.name       = "pic16c54 (PV951)",
		.id         = I2C_DRIVERID_PIC16C54_PV951,
		.insmodopt  = &pic16c54,
		.addr_lo    = I2C_PIC16C54 >> 1,
		.addr_hi    = I2C_PIC16C54>> 1,
		.registers  = 2,
		.flags      = CHIP_HAS_INPUTSEL,

		.inputreg   = PIC16C54_REG_MISC,
		.inputmap   = {PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_TUNER,
			     PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE,
			     PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE,
			     PIC16C54_MISC_SND_MUTE,PIC16C54_MISC_SND_MUTE,
			     PIC16C54_MISC_SND_NOTMUTE},
		.inputmute  = PIC16C54_MISC_SND_MUTE,
	},
	{
		.name       = "ta8874z",
		.id         = -1,
		//.id         = I2C_DRIVERID_TA8874Z,
		.checkit    = ta8874z_checkit,
		.insmodopt  = &ta8874z,
		.addr_lo    = I2C_TDA9840 >> 1,
		.addr_hi    = I2C_TDA9840 >> 1,
		.registers  = 2,

		.getmode    = ta8874z_getmode,
		.setmode    = ta8874z_setmode,
		.checkmode  = generic_checkmode,

	        .init       = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}},
	},
	{ .name = NULL } /* EOF */
};


/* ---------------------------------------------------------------------- */
/* i2c registration                                                       */

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
static int chip_attach(struct i2c_adapter *adap, int addr, int kind)
#else
static int chip_attach(struct i2c_adapter *adap, int addr,
		       unsigned short flags, int kind)
#endif
{
	struct CHIPSTATE *chip;
	struct CHIPDESC  *desc;
	int rc;

	chip = kmalloc(sizeof(*chip),GFP_KERNEL);
	if (!chip)
		return -ENOMEM;
	memset(chip,0,sizeof(*chip));
	memcpy(&chip->c,&client_template,sizeof(struct i2c_client));
        chip->c.adapter = adap;
        chip->c.addr = addr;
	i2c_set_clientdata(&chip->c, chip);

	/* find description for the chip */
	dprintk("tvaudio: chip found @ i2c-addr=0x%x\n", addr<<1);
	for (desc = chiplist; desc->name != NULL; desc++) {
		if (0 == *(desc->insmodopt))
			continue;
		if (addr < desc->addr_lo ||
		    addr > desc->addr_hi)
			continue;
		if (desc->checkit && !desc->checkit(chip))
			continue;
		break;
	}
	if (desc->name == NULL) {
		dprintk("tvaudio: no matching chip description found\n");
		return -EIO;
	}
	printk("tvaudio: found %s @ 0x%x\n", desc->name, addr<<1);
	dprintk("tvaudio: matches:%s%s%s.\n",
		(desc->flags & CHIP_HAS_VOLUME)     ? " volume"      : "",
		(desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "",
		(desc->flags & CHIP_HAS_INPUTSEL)   ? " audiomux"    : "");

	/* fill required data structures */
	strcpy(i2c_clientname(&chip->c),desc->name);
	chip->type = desc-chiplist;
	chip->shadow.count = desc->registers+1;
        chip->prevmode = -1;
	/* register */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
	MOD_INC_USE_COUNT;
#endif
	i2c_attach_client(&chip->c);

	/* initialization  */
	if (desc->initialize != NULL)
		desc->initialize(chip);
	else
		chip_cmd(chip,"init",&desc->init);

	if (desc->flags & CHIP_HAS_VOLUME) {
		chip->left   = desc->leftinit   ? desc->leftinit   : 65535;
		chip->right  = desc->rightinit  ? desc->rightinit  : 65535;
		chip_write(chip,desc->leftreg,desc->volfunc(chip->left));
		chip_write(chip,desc->rightreg,desc->volfunc(chip->right));
	}
	if (desc->flags & CHIP_HAS_BASSTREBLE) {
		chip->treble = desc->trebleinit ? desc->trebleinit : 32768;
		chip->bass   = desc->bassinit   ? desc->bassinit   : 32768;
		chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass));
		chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble));
	}

	if (desc->checkmode) {
		/* start async thread */
		DECLARE_MUTEX_LOCKED(sem);
		chip->notify = &sem;
		init_timer(&chip->wt);
		chip->wt.function = chip_thread_wake;
		chip->wt.data     = (unsigned long)chip;
		init_waitqueue_head(&chip->wq);
		rc = kernel_thread(chip_thread,(void *)chip,0);
		if (rc < 0)
			printk(KERN_WARNING "%s: kernel_thread() failed\n",
			       i2c_clientname(&chip->c));
		else
			down(&sem);
		chip->notify = NULL;
		wake_up_interruptible(&chip->wq);
	}
	return 0;
}

static int chip_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, chip_attach);
#else
	switch (adap->id) {
	case I2C_ALGO_BIT | I2C_HW_B_BT848:
	case I2C_ALGO_BIT | I2C_HW_B_RIVA:
	case I2C_ALGO_SAA7134:
		return i2c_probe(adap, &addr_data, chip_attach);
	}
#endif
	return 0;
}

static int chip_detach(struct i2c_client *client)
{
	struct CHIPSTATE *chip = i2c_get_clientdata(client);

	del_timer(&chip->wt);
	if (NULL != chip->thread) {
		/* shutdown async thread */
		DECLARE_MUTEX_LOCKED(sem);
		chip->notify = &sem;
		chip->done = 1;
		wake_up_interruptible(&chip->wq);
		down(&sem);
		chip->notify = NULL;
	}
	
	i2c_detach_client(&chip->c);
	kfree(chip);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
	MOD_DEC_USE_COUNT;
#endif
	return 0;
}

/* ---------------------------------------------------------------------- */
/* video4linux interface                                                  */

static int chip_command(struct i2c_client *client,
			unsigned int cmd, void *arg)
{
        __u16 *sarg = arg;
	struct CHIPSTATE *chip = i2c_get_clientdata(client);
	struct CHIPDESC  *desc = chiplist + chip->type;

	dprintk("%s: chip_command 0x%x\n",i2c_clientname(&chip->c),cmd);

	switch (cmd) {
	case AUDC_SET_INPUT:
		if (desc->flags & CHIP_HAS_INPUTSEL) {
			if (*sarg & 0x80)
				chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask);
			else
				chip_write_masked(chip,desc->inputreg,desc->inputmap[*sarg],desc->inputmask);
		}
		break;

	case AUDC_SET_RADIO:
		dprintk(KERN_DEBUG "tvaudio: AUDC_SET_RADIO\n");
		chip->norm = VIDEO_MODE_RADIO;
		chip->watch_stereo = 0;
		/* del_timer(&chip->wt); */
		break;

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

		if (desc->flags & CHIP_HAS_VOLUME) {
			va->flags  |= VIDEO_AUDIO_VOLUME;
			va->volume  = max(chip->left,chip->right);
			va->balance = (32768*min(chip->left,chip->right))/
				(va->volume ? va->volume : 1);
		}
		if (desc->flags & CHIP_HAS_BASSTREBLE) {
			va->flags |= VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE;
			va->bass   = chip->bass;
			va->treble = chip->treble;
		}
		if (chip->norm != VIDEO_MODE_RADIO) {
			if (desc->getmode)
				va->mode = desc->getmode(chip);
			else
				va->mode = VIDEO_SOUND_MONO;
		}
		break;
	}

	case VIDIOCSAUDIO:
	{
		struct video_audio *va = arg;
		
		if (desc->flags & CHIP_HAS_VOLUME) {
			chip->left = (min(65536 - va->balance,32768) *
				      va->volume) / 32768;
			chip->right = (min(va->balance,(__u16)32768) *
				       va->volume) / 32768;
			chip_write(chip,desc->leftreg,desc->volfunc(chip->left));
			chip_write(chip,desc->rightreg,desc->volfunc(chip->right));
		}
		if (desc->flags & CHIP_HAS_BASSTREBLE) {
			chip->bass = va->bass;
			chip->treble = va->treble;
			chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass));
			chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble));
		}
		if (desc->setmode && va->mode) {
			chip->watch_stereo = 0;
			/* del_timer(&chip->wt); */
			chip->mode = va->mode;
			desc->setmode(chip,va->mode);
		}
		break;
	}
	case VIDIOCSCHAN:
	{
		struct video_channel *vc = arg;
		
		dprintk(KERN_DEBUG "tvaudio: VIDIOCSCHAN\n");
		chip->norm = vc->norm;
		break;
	}
	case VIDIOCSFREQ:
	{
	    	chip->mode = 0; /* automatic */
		if (desc->checkmode) {
			desc->setmode(chip,VIDEO_SOUND_MONO);
		    	if (chip->prevmode != VIDEO_SOUND_MONO)
		    		chip->prevmode = -1; /* reset previous mode */
			mod_timer(&chip->wt, jiffies+2*HZ);
			/* the thread will call checkmode() later */
		}
	}
	}
	return 0;
}


static struct i2c_driver driver = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,54)
	.owner           = THIS_MODULE,
#endif
        .name            = "generic i2c audio driver",
        .id              = I2C_DRIVERID_TVAUDIO,
        .flags           = I2C_DF_NOTIFY,
        .attach_adapter  = chip_probe,
        .detach_client   = chip_detach,
        .command         = chip_command,
};

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

static int audiochip_init_module(void)
{
	struct CHIPDESC  *desc;
	printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n");
	printk(KERN_INFO "tvaudio: known chips: ");
	for (desc = chiplist; desc->name != NULL; desc++)
		printk("%s%s", (desc == chiplist) ? "" : ",",desc->name);
	printk("\n");
	i2c_add_driver(&driver);
	return 0;
}

static void audiochip_cleanup_module(void)
{
	i2c_del_driver(&driver);
}

module_init(audiochip_init_module);
module_exit(audiochip_cleanup_module);

/*
 * Local variables:
 * c-basic-offset: 8
 * End:
 */

⌨️ 快捷键说明

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