📄 ch7005c.c
字号:
/* ------------------------------------------------------------------------- */
/* ch7005c.c I2C driver for Chrontel CH7005C video encoder chips */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 2000 Ferenc Bakonyi <fero@drama.obuda.kando.hu>
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* ------------------------------------------------------------------------- */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/videodev.h>
#include "rivatv.h" /* HACK for register dump! */
#include "ch7005c.h"
#undef DPRINTK
#define DPRINTK(x...)
/*----------------------------------------------------------------------
* CH7005C I2C 'driver' driver
*----------------------------------------------------------------------*/
static int ch7005c_attach(struct i2c_adapter *adap);
static int ch7005c_detach(struct i2c_client *client);
static int ch7005c_command(struct i2c_client *client,
unsigned int cmd, void *arg);
static void ch7005c_inc_use(struct i2c_client *client);
static void ch7005c_dec_use(struct i2c_client *client);
/* CH7005C address: 0x75 */
static unsigned short normal_i2c[] = {0x75, I2C_CLIENT_END};
static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
/* Magic definition of all other I2C variables and things */
I2C_CLIENT_INSMOD;
/* Number of CH7005C registers */
#define NR_REGISTER 64
/* CH7005C register map */
#define DMR 0x00
#define FFR 0x01
#define VBW 0x03
#define IDF 0x04
#define CM 0x06
#define SAV 0x07
#define PO 0x08
#define BLR 0x09
#define HPR 0x0a
#define VPR 0x0b
#define SPR 0x0d
#define PMR 0x0e
#define CDR 0x10
#define CE 0x11
#define MNE 0x13
#define PLLM 0x14
#define PLLN 0x15
#define BCO 0x16
#define FSCI7 0x18
#define FSCI6 0x19
#define FSCI5 0x1a
#define FSCI4 0x1b /* TODO: P-OUTP */
#define FSCI3 0x1c /* TODO: DSEN */
#define FSCI2 0x1d
#define FSCI1 0x1e
#define FSCI0 0x1f
#define PLLC 0x20
#define CIVC 0x21
#define CIV 0x22
#define VID 0x25
#define TR 0x26
#define AR 0x3f
/* Initial register values */
unsigned char init_regs[] = {
0x6a, /* DMR Mode 17 (640x480, NTSC, 7/8) */
0x32, /* FFR */
0x00, /* unused */
0x00, /* VBW TODO */
0x00, /* IDF 16-bit non-multiplexed RGB (16-bit color, 565) */
0x00, /* unused */
0x10, /* CM slave */
0x00, /* SAV */
0x00, /* PO */
0x7f, /* BLR 127 for NTSC */
0x00, /* HPR */
0x00, /* VPR */
0x00, /* unused */
0x00, /* SPR syncs are inputs and active low */
0x0b, /* PMR output enabled, scart disabled */
0x00, /* unused */
0x00, /* CDR */
0x03, /* CE */
0x00, /* unused */
0x00, /* MNE */
0x41, /* PLLM BUG? or 0x3f ??? */
0x80, /* PLLN BUG? or 0x7e ??? */
0x00, /* BCO TODO: implement */
0x00, /* unused */
0x00, /* FSCI7 */
0x00, /* FSCI6 */
0x00, /* FSCI5 */
0x00, /* FSCI4 */
0x00, /* FSCI3 */
0x00, /* FSCI2 */
0x00, /* FSCI1 */
0x00, /* FSCI0 */
0x0a, /* PLLC */
0x01 /* CIVC */
};
/* CH7005C instance */
struct ch7005c {
int enable; /* output enabled */
int scart_enable; /* output is scart */
__u32 norm; /* video norm */
unsigned char reg[NR_REGISTER]; /* local copy of ch7005c's registers*/
};
/* Standard display modes */
static unsigned char display_modes[] = {
/* IR VOS SR */
0x00, /* 000 00 000 */
0x01, /* 000 00 001 */
0x08, /* 000 01 000 */
0x09, /* 000 01 001 */
0x20, /* 001 00 000 */
0x21, /* 001 00 001 */
0x28, /* 001 01 000 */
0x29, /* 001 01 001 */
0x42, /* 010 00 010 */
0x41, /* 010 00 001 */
0x48, /* 010 01 000 */
0x49, /* 010 01 001 */
0x4a, /* 010 01 010 */
0x60, /* 011 00 000 */
0x61, /* 011 00 001 */
0x63, /* 011 00 011 */
0x69, /* 011 01 001 */
0x6a, /* 011 01 010 */
0x6b, /* 011 01 011 */
0x81, /* 100 00 001 */
0x83, /* 100 00 011 */
0x84, /* 100 00 100 */
0x8b, /* 100 01 011 */
0x8c, /* 100 01 100 */
0x8d, /* 100 01 101 */
0xa1, /* 101 00 001 TODO: extra checks for IDF */
0xa9, /* 101 01 001 TODO: extra checks for IDF */
0xc1, /* 110 00 001 TODO: extra checks for IDF */
0xc9 /* 110 01 001 TODO: extra checks for IDF */
};
/* PLL M values for each display mode */
static unsigned int pll_m[] = {
13, 4, 89, 63, 26, 138, 63, 33, 61, 3, 63, 11, 89, 13, 4,
3, 63, 63, 89, 313, 33, 103, 33, 19, 89, 33, 33, 197, 2
};
/* PLL N values for each display mode */
static unsigned int pll_n[] = {
20, 9, 126, 110, 53, 339, 106, 70, 108, 9, 94, 22, 190, 20, 9,
9, 110, 126, 190, 647, 86, 284, 94, 62, 302, 31, 31, 242, 2
};
/* PLLCAP values for each display mode */
static unsigned char pllcap[] = {
1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1,
1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1
};
/* FSCI values for each display mode */
static __u32 fsci[] = {
806021060, 644816848, 763363328, 623153737, 601829058,
485346014, 574429782, 463962517, 677057690, 537347373,
646233505, 516986804, 452363454, 806021060, 644816848,
537347373, 623153737, 545259520, 508908885, 645499916,
528951320, 488262757, 521957831, 469762048, 428554851,
705268427, 569408543, 1073747879, 1073741824
};
/* FSCI values for PAL-M and NTSC-J display modes */
static __u32 fsci_mj[] = {
651209077, 520967262, 762524467, 622468953, 486236111,
392125896, 573798541, 463452668, 547015625, 434139385,
645523358, 516418687, 451866351, 651209077, 520967262,
434139385, 622468953, 544660334, 508349645, 521519134,
427355957, 394482422, 521384251, 469245826, 428083911,
569807942, 568782819, 867513766, 1072561888
};
/* For registering the I2C 'driver' driver */
static struct i2c_driver i2c_driver_ch7005c = {
name: "CH7005C",
id: I2C_DRIVERID_CH7005,
flags: I2C_DF_NOTIFY,
attach_adapter: ch7005c_attach,
detach_client: ch7005c_detach,
command: ch7005c_command,
inc_use: ch7005c_inc_use,
dec_use: ch7005c_dec_use
};
/* Template for new clients */
static struct i2c_client ch7005c_client_template = {
name: "CH7005C",
id: -1,
flags: 0,
addr: 0,
adapter: NULL,
driver: &i2c_driver_ch7005c,
data: NULL /* struct ch7005c *data */
};
/* Unique ID allocation */
static int ch7005c_id = 0;
/* Helper functions */
/* Read from a register */
static int ch7005c_read(struct i2c_client *client, unsigned char subaddr)
{
return i2c_smbus_read_byte_data(client, subaddr);
}
/* Write to a register */
static int ch7005c_write(struct i2c_client *client, unsigned char subaddr, unsigned char data)
{
struct ch7005c *dev = (struct ch7005c *)(client->data);
dev->reg[subaddr] = data;
return i2c_smbus_write_byte_data(client, subaddr, data);
}
/* Read all registers */
#ifdef READ_REGISTERS
static int ch7005c_read_block(struct i2c_client *client, unsigned char *data)
{
int i;
for (i = 0 ; i < NR_REGISTER ; i++)
data[i] = i2c_smbus_read_byte_data(client, i);
return NR_REGISTER;
}
#endif
/* Write to a register block */
static int ch7005c_write_block(struct i2c_client *client, unsigned char *data, unsigned int len)
{
struct ch7005c *dev = (struct ch7005c *)(client->data);
int i, err;
if (len > NR_REGISTER) len = NR_REGISTER;
for (i = 0 ; i < len ; i++) {
if ((err = i2c_smbus_write_byte_data(client, i, data[i])))
return err;
dev->reg[i] = data[i];
}
return 0;
}
/* I2C driver functions */
/* Called when a 'CH7005C like' device found */
static int ch7005c_detect(struct i2c_adapter *adap, int addr, unsigned short flags, int kind)
{
int err = 0;
struct i2c_client *client;
struct ch7005c *encoder;
int version;
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE_DATA |
I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
goto err_out;
client = kmalloc(sizeof(*client), GFP_KERNEL);
if (client == NULL) {
err = -ENOMEM;
goto err_out;
}
memcpy(client, &ch7005c_client_template, sizeof(*client));
client->adapter = adap;
client->addr = addr;
client->id = ch7005c_id++;
if (kind < 0) {
version = i2c_smbus_read_byte_data(client, VID);
if (version == -1)
goto err_out_kfree_client;
if (version == 0x3a) {
printk("ch7005c: video encoder chip found. Chip version: %#x\n", version);
} else {
printk("ch7005c: unknown CH700x chip found. (Maybe 7003?) Chip version: %#x\n", version);
goto err_out_kfree_client;
}
} else {
printk("ch7005c: detection skipped\n");
}
encoder = kmalloc(sizeof(struct ch7005c), GFP_KERNEL);
if (encoder == NULL) {
err = -ENOMEM;
goto err_out_kfree_client;
}
memset(encoder, 0, sizeof(struct ch7005c));
encoder->enable = 1;
encoder->scart_enable = 0;
encoder->norm = VIDEO_ENCODER_NTSC;
client->data = encoder;
if (ch7005c_write_block(client, init_regs, sizeof(init_regs)))
goto err_out_kfree_encoder;
if ((err = i2c_attach_client(client)))
goto err_out_kfree_encoder;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -