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

📄 saa7191.c

📁 底层驱动开发
💻 C
字号:
/* *  saa7191.c - Philips SAA7191 video decoder driver * *  Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> *  Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi> * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License version 2 as *  published by the Free Software Foundation. */#include <linux/module.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/fs.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/sched.h>#include <linux/videodev.h>#include <linux/video_decoder.h>#include <linux/i2c.h>#include "saa7191.h"#define SAA7191_MODULE_VERSION "0.0.3"MODULE_DESCRIPTION("Philips SAA7191 video decoder driver");MODULE_VERSION(SAA7191_MODULE_VERSION);MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");MODULE_LICENSE("GPL");#define VINO_ADAPTER	(I2C_ALGO_SGI | I2C_HW_SGI_VINO)struct saa7191 {	struct i2c_client *client;	/* the register values are stored here as the actual	 * I2C-registers are write-only */	unsigned char reg[25];	unsigned char norm;	unsigned char input;};static struct i2c_driver i2c_driver_saa7191;static const unsigned char initseq[] = {	0,	/* Subaddress */	0x50,	/* SAA7191_REG_IDEL */	0x30,	/* SAA7191_REG_HSYB */	0x00,	/* SAA7191_REG_HSYS */	0xe8,	/* SAA7191_REG_HCLB */	0xb6,	/* SAA7191_REG_HCLS */	0xf4,	/* SAA7191_REG_HPHI */	0x01,	/* SAA7191_REG_LUMA - chrominance trap active (CVBS) */	0x00,	/* SAA7191_REG_HUEC */	0xf8,	/* SAA7191_REG_CKTQ */	0xf8,	/* SAA7191_REG_CKTS */	0x90,	/* SAA7191_REG_PLSE */	0x90,	/* SAA7191_REG_SESE */	0x00,	/* SAA7191_REG_GAIN */	0x0c,	/* SAA7191_REG_STDC - not SECAM, slow time constant */	0x78,	/* SAA7191_REG_IOCK - chrominance from CVBS, GPSW1 & 2 off */	0x99,	/* SAA7191_REG_CTL3 - automatic field detection */	0x00,	/* SAA7191_REG_CTL4 */	0x2c,	/* SAA7191_REG_CHCV */	0x00,	/* unused */	0x00,	/* unused */	0x34,	/* SAA7191_REG_HS6B */	0x0a,	/* SAA7191_REG_HS6S */	0xf4,	/* SAA7191_REG_HC6B */	0xce,	/* SAA7191_REG_HC6S */	0xf4,	/* SAA7191_REG_HP6I */};/* SAA7191 register handling */static unsigned char saa7191_read_reg(struct i2c_client *client,				      unsigned char reg){	return ((struct saa7191 *)i2c_get_clientdata(client))->reg[reg];}static int saa7191_read_status(struct i2c_client *client,			       unsigned char *value){	int ret;	ret = i2c_master_recv(client, value, 1);	if (ret < 0) {		printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed");		return ret;	}	return 0;}static int saa7191_write_reg(struct i2c_client *client, unsigned char reg,			     unsigned char value){	((struct saa7191 *)i2c_get_clientdata(client))->reg[reg] = value;	return i2c_smbus_write_byte_data(client, reg, value);}/* the first byte of data must be the first subaddress number (register) */static int saa7191_write_block(struct i2c_client *client,			       unsigned char length, unsigned char *data){	int i;	int ret;	struct saa7191 *decoder = (struct saa7191 *)i2c_get_clientdata(client);	for (i = 0; i < (length - 1); i++) {		decoder->reg[data[0] + i] = data[i + 1];	}	ret = i2c_master_send(client, data, length);	if (ret < 0) {		printk(KERN_ERR "SAA7191: saa7191_write_block(): "		       "write failed");		return ret;	}	return 0;}/* Helper functions */static int saa7191_set_input(struct i2c_client *client, int input){	unsigned char luma = saa7191_read_reg(client, SAA7191_REG_LUMA);	unsigned char iock = saa7191_read_reg(client, SAA7191_REG_IOCK);	int err;	switch (input) {	case SAA7191_INPUT_COMPOSITE: /* Set Composite input */		iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1			  | SAA7191_IOCK_GPSW2);		/* Chrominance trap active */		luma &= ~SAA7191_LUMA_BYPS;		break;	case SAA7191_INPUT_SVIDEO: /* Set S-Video input */		iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2;		/* Chrominance trap bypassed */		luma |= SAA7191_LUMA_BYPS;		break;	default:		return -EINVAL;	}	err = saa7191_write_reg(client, SAA7191_REG_LUMA, luma);	if (err)		return -EIO;	err = saa7191_write_reg(client, SAA7191_REG_IOCK, iock);	if (err)		return -EIO;	return 0;}static int saa7191_set_norm(struct i2c_client *client, int norm){	struct saa7191 *decoder = i2c_get_clientdata(client);	unsigned char stdc = saa7191_read_reg(client, SAA7191_REG_STDC);	unsigned char ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3);	unsigned char chcv = saa7191_read_reg(client, SAA7191_REG_CHCV);	int err;	switch(norm) {	case SAA7191_NORM_AUTO: {		unsigned char status;		// does status depend on current norm ?		if (saa7191_read_status(client, &status))			return -EIO;		stdc &= ~SAA7191_STDC_SECS;		ctl3 &= ~SAA7191_CTL3_FSEL;		ctl3 |= SAA7191_CTL3_AUFD;		chcv = (status & SAA7191_STATUS_FIDT)			       ? SAA7191_CHCV_NTSC : SAA7191_CHCV_PAL;		break;	}	case SAA7191_NORM_PAL:		stdc &= ~SAA7191_STDC_SECS;		ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);		chcv = SAA7191_CHCV_PAL;		break;	case SAA7191_NORM_NTSC:		stdc &= ~SAA7191_STDC_SECS;		ctl3 &= ~SAA7191_CTL3_AUFD;		ctl3 |= SAA7191_CTL3_FSEL;		chcv = SAA7191_CHCV_NTSC;		break;	case SAA7191_NORM_SECAM:		stdc |= SAA7191_STDC_SECS;		ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);		chcv = SAA7191_CHCV_PAL;		break;	default:		return -EINVAL;	}	err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3);	if (err)		return -EIO;	err = saa7191_write_reg(client, SAA7191_REG_STDC, stdc);	if (err)		return -EIO;	err = saa7191_write_reg(client, SAA7191_REG_CHCV, chcv);	if (err)		return -EIO;	decoder->norm = norm;	return 0;}static int saa7191_get_controls(struct i2c_client *client,				struct saa7191_control *ctrl){	unsigned char hue = saa7191_read_reg(client, SAA7191_REG_HUEC);	unsigned char stdc = saa7191_read_reg(client, SAA7191_REG_STDC);	if (hue < 0x80) {		hue += 0x80;	} else {		hue -= 0x80;	}	ctrl->hue = hue;	ctrl->vtrc = (stdc & SAA7191_STDC_VTRC)		? SAA7191_VALUE_ENABLED : SAA7191_VALUE_DISABLED;	return 0;}static int saa7191_set_controls(struct i2c_client *client,				struct saa7191_control *ctrl){	int err;	if (ctrl->hue >= 0) {		unsigned char hue = ctrl->hue & 0xff;		if (hue < 0x80) {			hue += 0x80;		} else {			hue -= 0x80;		}		err = saa7191_write_reg(client, SAA7191_REG_HUEC, hue);		if (err)			return -EIO;	}	if (ctrl->vtrc >= 0) {		unsigned char stdc =			saa7191_read_reg(client, SAA7191_REG_STDC);		if (ctrl->vtrc) {			stdc |= SAA7191_STDC_VTRC;		} else {			stdc &= ~SAA7191_STDC_VTRC;		}		err = saa7191_write_reg(client, SAA7191_REG_STDC, stdc);		if (err)			return -EIO;	}	return 0;}/* I2C-interface */static int saa7191_attach(struct i2c_adapter *adap, int addr, int kind){	int err = 0;	struct saa7191 *decoder;	struct i2c_client *client;	printk(KERN_INFO "Philips SAA7191 driver version %s\n",	       SAA7191_MODULE_VERSION);	client = kmalloc(sizeof(*client), GFP_KERNEL);	if (!client)		return -ENOMEM;	decoder = kmalloc(sizeof(*decoder), GFP_KERNEL);	if (!decoder) {		err = -ENOMEM;		goto out_free_client;	}	memset(client, 0, sizeof(struct i2c_client));	memset(decoder, 0, sizeof(struct saa7191));	client->addr = addr;	client->adapter = adap;	client->driver = &i2c_driver_saa7191;	client->flags = 0;	strcpy(client->name, "saa7191 client");	i2c_set_clientdata(client, decoder);	decoder->client = client;	err = i2c_attach_client(client);	if (err)		goto out_free_decoder;	decoder->input = SAA7191_INPUT_COMPOSITE;	decoder->norm = SAA7191_NORM_AUTO;	err = saa7191_write_block(client, sizeof(initseq),				  (unsigned char *)initseq);	if (err) {		printk(KERN_ERR "SAA7191 initialization failed\n");		goto out_detach_client;	}	printk(KERN_INFO "SAA7191 initialized\n");	return 0;out_detach_client:	i2c_detach_client(client);out_free_decoder:	kfree(decoder);out_free_client:	kfree(client);	return err;}static int saa7191_probe(struct i2c_adapter *adap){	/* Always connected to VINO */	if (adap->id == VINO_ADAPTER)		return saa7191_attach(adap, SAA7191_ADDR, 0);	/* Feel free to add probe here :-) */	return -ENODEV;}static int saa7191_detach(struct i2c_client *client){	struct saa7191 *decoder = i2c_get_clientdata(client);	i2c_detach_client(client);	kfree(decoder);	kfree(client);	return 0;}static int saa7191_command(struct i2c_client *client, unsigned int cmd,			   void *arg){	struct saa7191 *decoder = i2c_get_clientdata(client);	switch (cmd) {	case DECODER_GET_CAPABILITIES: {		struct video_decoder_capability *cap = arg;		cap->flags  = VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC |			      VIDEO_DECODER_SECAM | VIDEO_DECODER_AUTO;		cap->inputs = (client->adapter->id == VINO_ADAPTER) ? 2 : 1;		cap->outputs = 1;		break;	}	case DECODER_GET_STATUS: {		int *iarg = arg;		unsigned char status;		int res = 0;		if (saa7191_read_status(client, &status)) {			return -EIO;		}		if ((status & SAA7191_STATUS_HLCK) == 0)			res |= DECODER_STATUS_GOOD;		if (status & SAA7191_STATUS_CODE)			res |= DECODER_STATUS_COLOR;		switch (decoder->norm) {		case SAA7191_NORM_NTSC:			res |= DECODER_STATUS_NTSC;			break;		case SAA7191_NORM_PAL:			res |= DECODER_STATUS_PAL;			break;		case SAA7191_NORM_SECAM:			res |= DECODER_STATUS_SECAM;			break;		case SAA7191_NORM_AUTO:		default:			if (status & SAA7191_STATUS_FIDT)				res |= DECODER_STATUS_NTSC;			else				res |= DECODER_STATUS_PAL;			break;		}		*iarg = res;		break;	}	case DECODER_SET_NORM: {		int *iarg = arg;		switch (*iarg) {		case VIDEO_MODE_AUTO:			return saa7191_set_norm(client, SAA7191_NORM_AUTO);		case VIDEO_MODE_PAL:			return saa7191_set_norm(client, SAA7191_NORM_PAL);		case VIDEO_MODE_NTSC:			return saa7191_set_norm(client, SAA7191_NORM_NTSC);		case VIDEO_MODE_SECAM:			return saa7191_set_norm(client, SAA7191_NORM_SECAM);		default:			return -EINVAL;		}		break;	}	case DECODER_SET_INPUT:	{		int *iarg = arg;		switch (client->adapter->id) {		case VINO_ADAPTER:			return saa7191_set_input(client, *iarg);		default:			if (*iarg != 0)				return -EINVAL;		}		break;	}	case DECODER_SET_OUTPUT: {		int *iarg = arg;		/* not much choice of outputs */		if (*iarg != 0)			return -EINVAL;		break;	}	case DECODER_ENABLE_OUTPUT: {		/* Always enabled */		break;	}	case DECODER_SET_PICTURE: {		struct video_picture *pic = arg;		unsigned val;		int err;		val = (pic->hue >> 8) - 0x80;		err = saa7191_write_reg(client, SAA7191_REG_HUEC, val);		if (err)			return -EIO;		break;	}	case DECODER_SAA7191_GET_STATUS: {		struct saa7191_status *status = arg;		unsigned char status_reg;		if (saa7191_read_status(client, &status_reg))			return -EIO;		status->signal = ((status_reg & SAA7191_STATUS_HLCK) == 0)			? SAA7191_VALUE_ENABLED : SAA7191_VALUE_DISABLED;		status->ntsc = (status_reg & SAA7191_STATUS_FIDT)			? SAA7191_VALUE_ENABLED : SAA7191_VALUE_DISABLED;		status->color = (status_reg & SAA7191_STATUS_CODE)			? SAA7191_VALUE_ENABLED : SAA7191_VALUE_DISABLED;		status->input = decoder->input;		status->norm = decoder->norm;	}	case DECODER_SAA7191_SET_NORM: {		int *norm = arg;		return saa7191_set_norm(client, *norm);	}	case DECODER_SAA7191_GET_CONTROLS: {		struct saa7191_control *ctrl = arg;		return saa7191_get_controls(client, ctrl);	}	case DECODER_SAA7191_SET_CONTROLS: {		struct saa7191_control *ctrl = arg;		return saa7191_set_controls(client, ctrl);	}	default:		return -EINVAL;	}	return 0;}static struct i2c_driver i2c_driver_saa7191 = {	.owner		= THIS_MODULE,	.name 		= "saa7191",	.id 		= I2C_DRIVERID_SAA7191,	.flags 		= I2C_DF_NOTIFY,	.attach_adapter = saa7191_probe,	.detach_client 	= saa7191_detach,	.command 	= saa7191_command};static int saa7191_init(void){	return i2c_add_driver(&i2c_driver_saa7191);}static void saa7191_exit(void){	i2c_del_driver(&i2c_driver_saa7191);}module_init(saa7191_init);module_exit(saa7191_exit);

⌨️ 快捷键说明

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