📄 tw2834.c
字号:
/*
* tw2834 - Techwell TW2834 video decoder driver version 0.0.1
*
* Copyright (c) 2006 Guangzhou Jinpeng Group Co., Ltd.
* All rights reserved.
* David Huang<huangym@gzjp.cn>
* This source code and any compilation or derivative thereof is the proprietary
* information of Guangzhou Jinpeng Group and is confidential in nature. Under no
* circumstances is this software to be exposed to or placed under an Open Source
* License of any type without the express written permission of Guangzhou
* Jinpeng Group.
*/
/*
Rev Date Author Comments
--------------------------------------------------------------------------------
1 20061023 huangym Original;
2 20061206 huangym Add TW2834 cascade mode ;
change 1x00 to cascade mode;
add tw2834_set_xpath_position;
change the device name;
3 2007.03.05 wangdy in tw2834_config.h;0x1e=0xff--->0x1e=0x7f,This is frame size CIF for Y path;
4 2007.03.09 wangdy 0x70 from 0xc0 to 0x40,for Y path no popup,this is for 2834 cscade,only this can record work well!
5 2007.04.08 wangdy Delete setting PREVIEW interface after setting one position;
6 2007.04.11 wangdy add tw2834_ioctl_sem to avoid two threads infaluting each other,
or, when split ,some channels will not PREVIEW ;
7 2007.04.25 wangdy 0x14-0x17,0x20 regs; V blank:0x09,0x49,0x89,0xd9->0x07;
8 2007.06.06 wangdy command copy data from user : copy_from_user(buf,(unsigned int *)arg,16);
It can solve the data error when transites from user space! some channels have no video problem no more!
9 2007.07.07 wangdy shutdown DAC output of tw2834;
10 2007.07.25 wangdy add ntsc_mode;midify ntsc color phase for video;
11 2007.09.03 wangdy modify get video status;
12 2007.11.28 wangdy modify 1x7d,1x84 for ntsc recode;
--------------------------------------------------------------------------------
*/
#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/miscdevice.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include "tw2834.h"
#include "tw2834_config.h"
#define I2C_NAME(x) (x)->name
#define DEVNAME "tw2834"
MODULE_DESCRIPTION("Techwell TW2834 video decoder driver");
MODULE_AUTHOR("David Huang");
MODULE_LICENSE("GPL");
static int debug =2;
static int chip_number=-1;
unsigned int height;
/* Which is the address of I2C slaover.Please modify it for your necessary! */
#define master0_addr 0x84
#define slaver1_addr 0x86
#define slaver2_addr 0x88
#define slaver3_addr 0x8a
static struct semaphore tw2834_ioctl_sem; /*2007.4.11 add to avoid two threads infaluting each other*/
static char *mode_option = "pal"; /* default mode_option is pal;*/
module_param(mode_option,charp,0);
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
#define dprintk(num, format, args...) \
do\
{ \
if (debug >= num) \
printk(format, ##args); \
} while (0)
/* ----------------------------------------------------------------------- */
int tw2834_major = TW2834_MAJOR;
int tw2834_minor = 0;
int tw2834_nr_devs = TW2834_NR_DEVS; /* number of bare tw2834 devices */
struct tw2834_dev *tw2834_devices; /* allocated in tw2834_init_module */
static struct class_simple *tw2834_class;
static struct i2c_client *tw2834_client;
struct tw2834 {
int norm;
};
static inline int tw2834_chip_config(void);
static inline void set_standard(struct i2c_client * client);
static inline void tw2834_only_master_chip(void);
static inline void tw2834_tow_chip(void);
static inline void tw2834_three_chip(void);
static inline void tw2834_four_chip(void);
static inline void tw2834_all_channel_disable(struct i2c_client * client); //
static void tw2834_set_xpath_position_table(struct i2c_client * client,int ch_no,unsigned int split_num,unsigned int to_position);
int tw2834_trim(struct tw2834_dev *dev);
static inline int tw2834_read (struct i2c_client *client, u8 vi_page,u8 reg);
static ssize_t tw2834_fileread(struct file *file, char __user *buf, size_t count, loff_t *pos);
static int tw2834_open(struct inode *inode, struct file *file);
static int tw2834_command ( struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
static struct file_operations tw2834_fops = {
.owner = THIS_MODULE,
.read = tw2834_fileread,
.open = tw2834_open,
.release = NULL,
.ioctl = tw2834_command,
};
#define REG_ADDR(x) (((x) << 1) + 1)
#define LOu8(x) ((unsigned char)((x) & 0xff))
#define HIu8(x) ((unsigned char)(((x) >> 8) & 0xff))
#define LOWORD(x) ((unsigned short int)((x) & 0xffff))
#define HIWORD(x) ((unsigned short int)(((x) >> 16) & 0xffff))
#define BUFFER_LENGTH (10)
/* ----------------------------------------------------------------------- */
static ssize_t tw2834_fileread(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
ssize_t retval = 0;
if (*pos == 0)
{
char kernBuf[10] = {0,0,0,0,0,0,0,0,0,0};
if (count > BUFFER_LENGTH)
{
count = BUFFER_LENGTH;
}
if ( copy_to_user(buf, kernBuf, count) )
{
printk(KERN_WARNING "%s: Unable to copy_to_user all data during read.\n", __FILE__);
retval = -EFAULT;
}
else
{
*pos += count;
retval = count;
}
}
return retval;
}
/* ----------------------------------------------------------------------- */
static int
tw2834_open(struct inode *inode, struct file *file)
{
int num;
struct tw2834_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct tw2834_dev, cdev);
//dprintk(1, KERN_DEBUG "major is %d,minor is %d!\n",MAJOR(inode->i_rdev),MINOR(inode->i_rdev));
num = MINOR(inode->i_rdev);
file->private_data = dev; /* for other methods */
//dprintk(1, KERN_DEBUG "This is chip[%d] addr is:(0x%02x)\n",num,dev->addr << 1);
/* now trim to 0 the length of the device if open was write-only */
if ( (file->f_flags & O_ACCMODE) == O_WRONLY) {
if (down_interruptible(&dev->sem)) return -ERESTARTSYS;
tw2834_trim(dev); /* ignore errors */
up(&dev->sem);
}
return 0; /* success */
}
static inline int
tw2834_write (struct i2c_client *client,
u8 vi_page,
u8 reg,
u8 value)
{
struct i2c_msg msg;
u8 data[3];
msg.addr = client->addr;
msg.flags = 0;
data[0] = vi_page&0x03;
data[1] = reg;
data[2] = value;
msg.buf = (char *) data;
msg.len = 3;
return i2c_transfer(client->adapter, &msg, 1);
}
static inline int
tw2834_write_block (struct i2c_client *client,
u8 vi_page,
u8 reg,
const u8 *value,
unsigned int len)
{
struct i2c_msg msg;
u8 data[32];
unsigned int copylen;
int ret = -1;
msg.addr = client->addr;
msg.flags = 0;
msg.buf = (char *)data;
data[0] = vi_page&0x03; /* The first byte is register's page */
while(len)
{
data[1] = reg; /* The second byte is register's address */
if (len <=30)
{
copylen = len;
len = 0;
}
else
{
copylen = 30;
len -=30;
}
memcpy(&data[2], value, copylen); /* Copy register's value */
value += copylen;
reg +=copylen;
msg.len = copylen + 2;
/* the tw2834 has an autoincrement function */
if ((ret = i2c_transfer(client->adapter, &msg, 1)) < 0) /* Transfer a block */
break;
}
return ret;
}
static inline int tw2834_read (struct i2c_client *client,
u8 vi_page,
u8 reg)
{
struct i2c_msg msg;
u8 data[2];
u8 rcvdata;
msg.addr = client->addr;
msg.flags = 0;
data[0] = vi_page&0x03;
data[1] = reg;
msg.buf = (char *) data;
msg.len = 2;
if( i2c_transfer(client->adapter, &msg, 1) < 0)
return -1;
if(i2c_master_recv(client, (char*)&rcvdata, 1)<0)
return -1;
return rcvdata;
}
#define HORIZON 720
#define PAL_V 576
#define NTSC_V 480
/*
* founction:position and split screen on tv
* ch_no, which channel
* split_num,split screen num:1,4,9,16
* to_position:on tv position is :0--(split_num-1)
*/
static void tw2834_set_xpath_position_table(struct i2c_client * client,
int ch_no,
unsigned int split_num,
unsigned int to_position)
{
struct tw2834 *decoder;
unsigned int subaddr;
unsigned int hscale=0;
unsigned int vscale=0;
unsigned int position;
unsigned char h_left=0;
unsigned char h_right=0;
unsigned char v_top=0;
unsigned char v_bottom=0;
unsigned char ntsc_mode=0;
if(ch_no < 0 || ch_no >4) /* error channel number */
return;
decoder = i2c_get_clientdata(client);
if(decoder->norm == PAL_STD)
{
position =(to_position)*4; /* for pal */
ntsc_mode=0;
}
else
{
position =(split_num+ to_position)*4; /* for ntsc */
ntsc_mode=1;
}
{
if(split_num == 1 )
{
hscale = hscale1[ntsc_mode];
vscale = vscale1[ntsc_mode];
h_left = preview_position_1[position++];
h_right = preview_position_1[position++];
v_top = preview_position_1[position++];
v_bottom = preview_position_1[position];
}
if(split_num == 4 )
{
hscale = hscale4[ntsc_mode];
vscale = vscale4[ntsc_mode];
h_left = preview_position_4[position++];
h_right = preview_position_4[position++];
v_top = preview_position_4[position++];
v_bottom = preview_position_4[position];
}
if(split_num == 9 )
{
hscale = hscale9[ntsc_mode];
vscale = vscale9[ntsc_mode];
h_left = preview_position_9[position++];
h_right = preview_position_9[position++];
v_top = preview_position_9[position++];
v_bottom = preview_position_9[position];
}
if(split_num == 16 )
{
hscale = hscale16[ntsc_mode];
vscale = vscale16[ntsc_mode];
h_left = preview_position_16[position++];
h_right = preview_position_16[position++];
v_top = preview_position_16[position++];
v_bottom = preview_position_16[position];
}
}
//dprintk(1, KERN_DEBUG "ch_no = %d, hscale = 0x%x, vscale = 0x%x.\n ", ch_no, hscale, vscale);
if(decoder->norm == PAL_STD)
{
tw2834_write(client, 1, 0x88, 0x20);
}
else
{
tw2834_write(client, 1, 0x88, 0x19); /* left position of tw2834 */
}
subaddr = 0x1c+ch_no*0x40;
tw2834_write(client, 0, subaddr++, HIu8(hscale)); /* HSCALE_X[15:8] */
tw2834_write(client, 0, subaddr, LOu8(hscale)); /* HSCALE_X[7:0] */
subaddr = 0x18+ch_no*0x40;
tw2834_write(client, 0, subaddr++, HIu8(vscale)); /* VSCALE_X[15:8] */
tw2834_write(client, 0, subaddr, LOu8(vscale)); /* VSCALE_X[7:0] */
subaddr = 0x30+ch_no*4; /* position */
tw2834_write(client, 1, subaddr++, h_left);
tw2834_write(client, 1, subaddr++, h_right);
tw2834_write(client, 1, subaddr++, v_top);
tw2834_write(client, 1, subaddr, v_bottom);
if(split_num == 1 )
{
subaddr = 0x03+ch_no*0x40; /* position and blank boundary */
tw2834_write(client, 0, subaddr, 0x44);
tw2834_write(client, 0, subaddr+0x06, 0x11);
subaddr = 0x14+ch_no*0x40; /* related regs:display path and record path */
tw2834_write(client, 0, subaddr++, 0x34);
tw2834_write(client, 0, subaddr++, 0x00);
tw2834_write(client, 0, subaddr++, 0x00);
tw2834_write(client, 0, subaddr, 0x10);
if(decoder->norm == PAL_STD)
{
subaddr = 0x20+ch_no*0x40;
tw2834_write(client, 0, subaddr, 0x0f);
}
else
{
subaddr = 0x20+ch_no*0x40; /* only Display path and 0x21 is record path */
tw2834_write(client, 0, subaddr, 0x07);
}
//printk("split num is 1,set fullscreen\n");
}
if(split_num == 4 )
{
if(decoder->norm == PAL_STD)
{
subaddr = 0x03+ch_no*0x40; /* position and blank boundary */
tw2834_write(client, 0, subaddr, 0x30);
tw2834_write(client, 0, subaddr+0x06, 0x07);
subaddr = 0x14+ch_no*0x40; /* related regs */
tw2834_write(client, 0, subaddr++, 0x50);
tw2834_write(client, 0, subaddr++, 0x30);
tw2834_write(client, 0, subaddr++, 0xc0);
tw2834_write(client, 0, subaddr, 0xc0);
subaddr = 0x20+ch_no*0x40;
tw2834_write(client, 0, subaddr, 0x0f);
}
else
{
subaddr = 0x03+ch_no*0x40; /* position and blank boundary */
tw2834_write(client, 0, subaddr, 0x30);
tw2834_write(client, 0, subaddr+0x06, 0x10);
subaddr = 0x14+ch_no*0x40; /* related regs */
tw2834_write(client, 0, subaddr++, 0x00);
tw2834_write(client, 0, subaddr++, 0x23);
tw2834_write(client, 0, subaddr++, 0x00);
tw2834_write(client, 0, subaddr, 0x10);
subaddr = 0x20+ch_no*0x40;
tw2834_write(client, 0, subaddr, 0x0f);
}
//printk("split num is %d\n",split_num);
}
if(split_num == 9 )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -