📄 ch7005c.c
字号:
return 0;
err_out_kfree_encoder:
kfree(encoder);
err_out_kfree_client:
kfree(client);
err_out:
return err;
}
/* Called when a new I2C bus found */
static int ch7005c_attach(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, ch7005c_detect);
}
/* Called on exit */
static int ch7005c_detach(struct i2c_client *client)
{
int err;
if ((err = i2c_detach_client(client)))
return err;
kfree(client->data);
kfree(client);
return 0;
}
/* Interface to the world */
static int ch7005c_command(struct i2c_client *client,
unsigned int cmd, void *arg)
{
struct ch7005c *dev = (struct ch7005c *)(client->data);
switch (cmd) {
#ifdef READ_REGISTERS
case READ_REGISTERS: /* for debugging */
{
return ch7005c_read_block(client, arg); /* bytes read */
}
case WRITE_REGISTERS: /* for debugging */
{
ch7005c_write_block(client, arg, NR_REGISTER);
return NR_REGISTER;
}
case GET_NR_OF_REGISTERS: /* for debugging */
{
return NR_REGISTER;
}
#endif /* READ_REGISTERS */
case ENCODER_GET_CAPABILITIES:
{
struct video_encoder_capability *cap = arg;
cap->flags = VIDEO_ENCODER_PAL | VIDEO_ENCODER_NTSC
| VIDEO_ENCODER_PAL_M | VIDEO_ENCODER_NTSC_J;
cap->inputs = 1;
cap->outputs = 2;
DPRINTK("ENCODER_GET_CAPABILITIES\n");
break;
}
case ENCODER_SET_NORM: /* SET_DISP_MODE may override this! */
{
int norm = *(int*)arg;
unsigned char blr, idf;
idf = dev->reg[IDF];
switch (norm) {
case VIDEO_ENCODER_PAL:
blr = 105;
idf |= 0x40;
break;
case VIDEO_ENCODER_NTSC:
case VIDEO_ENCODER_PAL_M:
blr = 127;
idf &= ~0x40;
break;
case VIDEO_ENCODER_NTSC_J:
blr = 100;
idf |= 0x40;
break;
default:
DPRINTK("invalid norm: %d\n", norm);
return -EINVAL;
}
dev->norm = norm;
ch7005c_write(client, BLR, blr);
ch7005c_write(client, IDF, idf);
DPRINTK("setting BLR: %#x, IDF: %#x\n", blr. idf);
break;
}
case ENCODER_SET_INPUT:
{
int input = *(int*)arg;
if (input != 0) {
return -EINVAL;
}
break;
}
case ENCODER_SET_OUTPUT:
{
int output = *(int*)arg;
unsigned char pmr;
switch (output) {
case 0:
dev->scart_enable = 0;
break;
case 1:
dev->scart_enable = 0x10;
break;
default:
return -EINVAL;
}
pmr = (dev->reg[PMR] & 0xef) | dev->scart_enable;
ch7005c_write(client, PMR, pmr);
DPRINTK("output set: %d (scart enable: %d)\n", output, dev->scart_enable);
break;
}
case ENCODER_ENABLE_OUTPUT:
{
int enable = *(int*)arg;
unsigned char pmr;
if (enable)
pmr = 0x0b; /* Normal (on) */
else
pmr = 0x0c; /* Full power down */
pmr |= dev->scart_enable;
ch7005c_write(client, PMR, pmr);
dev->enable = enable;
DPRINTK("output enable: %d (pmr: %#x)\n", enable, pmr);
break;
}
case ENCODER_SET_CONFIG: /* TODO: PLLC settings */
{
struct ch7005c_config *cfg = (struct ch7005c_config*)arg;
unsigned char cm, idf, spr, civc;
if (((cfg->xcm == 0) &&
(cfg->input_fmt == CH7005C_8_RGB_MPX_24)) ||
((cfg->xcm == 1) &&
(cfg->input_fmt == CH7005C_16_RGB_16 ||
cfg->input_fmt == CH7005C_16_YCRCB_24 ||
cfg->input_fmt == CH7005C_15_RGB_15 ||
cfg->input_fmt == CH7005C_8_RGB_MPX_24)) ||
((cfg->xcm == 2) &&
(cfg->input_fmt != CH7005C_8_RGB_MPX_24))) {
DPRINTK("input data format is not valid!\n");
return -EINVAL;
}
if ((cfg->des == CH7005C_DES_ENABLE) &&
(cfg->input_fmt != CH7005C_16_YCRCB_24) &&
(cfg->input_fmt != CH7005C_8_YCRCB_MPX_24)) {
DPRINTK("des value is not valid!\n");
return -EINVAL;
}
cm = (cfg->clock_mode << 7) |
(cfg->clock_mode << 6) |
(cfg->mcp << 4) |
(cfg->xcm << 2) |
(cfg->pcm);
idf = (dev->reg[IDF] & 0xd0) |
(cfg->rgb_bypass << 5) |
(cfg->input_fmt);
spr = (cfg->des << 3) |
(cfg->sync_direction << 2) |
(cfg->vert_sync_pol << 1) |
(cfg->horiz_sync_pol);
civc = (dev->reg[CIVC] & 0xfe) |
((~cfg->clock_mode) & 0x01);
ch7005c_write(client, CM, cm);
ch7005c_write(client, IDF, idf);
ch7005c_write(client, SPR, spr);
ch7005c_write(client, CIVC, civc);
break;
}
case ENCODER_SET_DISP_MODE:
{
int mode = *(int*)arg;
unsigned char dmr, mne, m, n, pllc;
__u32 f = 0;
if (mode < 0 || mode > 28) {
DPRINTK("invalid mode: %d\n", mode);
return -EINVAL;
}
dmr = display_modes[mode];
switch (dev->norm) {
case VIDEO_ENCODER_PAL:
case VIDEO_ENCODER_NTSC:
f = fsci[mode];
break;
case VIDEO_ENCODER_PAL_M:
case VIDEO_ENCODER_NTSC_J:
dmr |= 0x10;
f = fsci_mj[mode];
break;
default:
DPRINTK("internal error, norm: %d\n", dev->norm);
return -EINVAL;
}
m = pll_m[mode];
n = pll_n[mode];
mne = ((n & 0x300) >> 7) | ((m & 0x100) >> 8);
pllc = (dev->reg[PLLC] & 0xef) | (pllcap[mode] << 4);
ch7005c_write(client, DMR, dmr);
ch7005c_write(client, MNE, mne);
ch7005c_write(client, PLLM, m & 0xff);
ch7005c_write(client, PLLN, n & 0xff);
ch7005c_write(client, PLLC, pllc);
ch7005c_write(client, FSCI0, f & 0x0f);
f >>= 4;
ch7005c_write(client, FSCI1, f & 0x0f);
f >>= 4;
ch7005c_write(client, FSCI2, f & 0x0f);
f >>= 4;
ch7005c_write(client, FSCI3, f & 0x0f);
f >>= 4;
ch7005c_write(client, FSCI4, f & 0x0f);
f >>= 4;
ch7005c_write(client, FSCI5, f & 0x0f);
f >>= 4;
ch7005c_write(client, FSCI6, f & 0x0f);
f >>= 4;
ch7005c_write(client, FSCI7, f & 0x0f);
DPRINTK("new mode: %d (dmr: %#x, mne: %#x, m: %#x, n: %#x, pllc: %#x)\n", mode, dmr, mne, m, n, pllc);
break;
}
case ENCODER_SET_FLICKER_FILTER:
{
struct ch7005c_filter *filter = (struct ch7005c_filter*)arg;
int ft, fy, fc;
unsigned char ffr;
ft = filter->text_enhancement;
fy = filter->luma;
fc = filter->chroma;
if (ft < CH7005C_FILTER_MIN ||
ft > CH7005C_FILTER_MAX ||
fy < CH7005C_FILTER_MIN ||
fy > CH7005C_FILTER_MAX ||
fc < CH7005C_FILTER_MIN ||
fc > CH7005C_DOTCRAWL_RED) {
DPRINTK("invalid filter mode\n");
return -EINVAL;
}
ffr = (fc << 4) |
(fy << 2) |
(2 - ft);
ch7005c_write(client, FFR, ffr);
DPRINTK("flicker filter: %#x\n", ffr);
break;
}
case ENCODER_SET_POS: /* TODO: verify values better, DS/BCO handling */
{
struct ch7005c_pos *pos = (struct ch7005c_pos*)arg;
unsigned char po;
if (pos->start_active_video > 511 ||
pos->horiz_pos > 511 ||
pos->vert_pos > 511) {
DPRINTK("invalid position! \n");
return -EINVAL;
}
po = ((pos->start_active_video & 0x100) >> 6) |
((pos->horiz_pos & 0x100) >> 7) |
((pos->vert_pos & 0x100) >> 8);
ch7005c_write(client, SAV, pos->start_active_video & 0xff);
ch7005c_write(client, HPR, pos->horiz_pos & 0xff);
ch7005c_write(client, VPR, pos->vert_pos & 0xff);
ch7005c_write(client, PO, po);
DPRINTK("position set\n");
break;
}
case ENCODER_SET_CONTRAST:
{
int cont = *(int*)arg;
if (cont < 0 || cont > 7) {
DPRINTK("invalid contrast: %d\n", cont);
return -EINVAL;
}
ch7005c_write(client, CE, cont);
DPRINTK("contrast adjusted to: %d\n", cont);
break;
}
case ENCODER_DETECT_CONNECTIONS:
{
int *conn = arg;
if (!dev->enable) {
DPRINTK("ENCODER_DETECT_CONNECTIONS is not possible with disabled device!\n");
return -EINVAL;
}
ch7005c_write(client, CDR, 0x01);
/* udelay(1); */
ch7005c_write(client, CDR, 0x00);
*conn = (~ch7005c_read(client, CDR)) & 0x0e;
DPRINTK("detected connections: %#x\n", *conn);
break;
}
default:
{
DPRINTK("unknown command!\n");
return -EINVAL;
}
}
return 0;
}
static void ch7005c_inc_use(struct i2c_client *client)
{
MOD_INC_USE_COUNT;
}
static void ch7005c_dec_use(struct i2c_client *client)
{
MOD_DEC_USE_COUNT;
}
/*----------------------------------------------------------------------
* Modularization
*----------------------------------------------------------------------*/
MODULE_AUTHOR("Ferenc Bakonyi <fero@drama.obuda.kando.hu>");
MODULE_DESCRIPTION("CH7005C driver");
MODULE_LICENSE("GPL");
int __init init_ch7005c(void)
{
return i2c_add_driver(&i2c_driver_ch7005c);
}
void __exit cleanup_ch7005c(void)
{
i2c_del_driver(&i2c_driver_ch7005c);
}
module_init(init_ch7005c);
module_exit(cleanup_ch7005c);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -