📄 ch7013.c
字号:
/** * @file ch7013.c * @brief Source file for CH7013B tvout driver. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright (C) 2006 Sitek Hengke Electronics Wuxi. * http://www.ihanker.com * Modification history: * * Nov. 29, 2006. Gary * Initial version of this file. * */#include <linux/kernel.h>#include <linux/module.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/i2c.h>#include <linux/i2c-algo-bit.h>#include <linux/i2c-id.h>#include <linux/poll.h>#include <linux/miscdevice.h>#include <linux/proc_fs.h>#include <linux/delay.h>#include <asm/types.h>#include <asm/hardware.h>#include <asm/arch/clock.h>#include "mx2fb.h"#include "ch7013.h"#define MODULE_NAME "tvout"static int g_tvout_major = 0;static lcdc_regs_t g_lcdc_regs;/************************************************************************************************* i2c related*************************************************************************************************///CH7013 i2c addr//ADDR Serial Address Selected// 1 111 0101 = 75h //high: 0xEA(write), 0xEB(read)// 0 111 0110 = 76h // low: 0xEC(write), 0xED(read)#define CH7013_MXC_I2C_FLAG_POLLING 0x02 /* if set, is polling mode; else is interrupt mode */#define CH7013_I2C_ADDR 0x76 //0xECstatic int ch7013_i2c_attach (struct i2c_adapter * adap);static struct i2c_driver ch7013_i2c_driver = { .driver = { .owner = THIS_MODULE, .name = "ch7013 driver", }, .attach_adapter = &ch7013_i2c_attach, .detach_client = NULL,};static struct i2c_client ch7013_i2c_client = { .name = "ch7013 I2C dev", .addr = CH7013_I2C_ADDR, .driver = &ch7013_i2c_driver, .flags = 0,};static int ch7013_i2c_attach(struct i2c_adapter *adap){ printk("ch7013_i2c_attach begin!\n"); if (memcmp(adap->name, "MXC I2C Adapter", 15) != 0 ) { printk("memcmp adapter name failed\n"); return -ENODEV; } ch7013_i2c_client.adapter = adap; if (i2c_attach_client(&ch7013_i2c_client)) { printk("ch7013: i2c_attach_client() failed.\n"); //i2c_del_driver(&ch7013_i2c_driver); return -1; } return 0;}/*! * @brief Function to read one byte from TV encoder registers on the i2c bus * @param client I2C client structure * @param reg The register number/CH7013 register address * @param value Pointer to buffer to receive the read data/register value */static int ch7013_i2c_read(unsigned char reg, unsigned char *buf){ struct i2c_msg msg[2]; int ret; msg[0].addr = ch7013_i2c_client.addr; msg[0].len = 1; msg[0].buf = ® msg[0].flags = CH7013_MXC_I2C_FLAG_POLLING; msg[1].addr = ch7013_i2c_client.addr; msg[1].len = 2; msg[1].buf = buf; msg[1].flags = I2C_M_RD | CH7013_MXC_I2C_FLAG_POLLING; ret = i2c_transfer(ch7013_i2c_client.adapter, msg, 2); if (ret >= 0) return 0; return ret;}/*! * @brief Function to write one byte to a TV encoder register on the i2c bus * @param client I2C client structure * @param reg The register number/CH7013 register address * @param value The value to write */ static int ch7013_i2c_write(unsigned char reg, unsigned char data){ struct i2c_msg msg[2]; int ret; msg[0].addr = ch7013_i2c_client.addr; msg[0].len = 1; //msg[0].buf[0] = reg; msg[0].buf = ® msg[0].flags = 0; msg[1].addr = ch7013_i2c_client.addr; msg[1].len = 2; //msg[1].buf[0] = data; msg[1].buf = &data; msg[1].flags = 0; ret = i2c_transfer(ch7013_i2c_client.adapter, msg, 2); if (ret >= 0) return 0; return ret;}void ch7013_i2c_init(void){ /* check for deferred I2C registration */ int err; if (err = i2c_add_driver(&ch7013_i2c_driver)) printk("ch7013: driver registration failed\n"); return ;}void ch7013_i2c_cleanup(void){ i2c_detach_client(&ch7013_i2c_client ); i2c_del_driver(&ch7013_i2c_driver);}/************************************************************************************************* End of i2c related*************************************************************************************************////////////////////////////////////////////////////////////////////////TVOUT CH7013 Part////////////////////////////////////////////////////////////////////static int ch7013_i2c_program(struct i2c_client *client);static void ch7013_hw_init(void);static void ch7013_hw_exit(void);static void ch7013_save_lcdc(void);static ch7013_fixed_table_t ctable[] ={ {0x60, 0x2A, 0x50, 0x74, 0x31, 0x81, 0x18, 0x2D, 0x40, 0x0D, 0x14, 0x300AE7C4, 0x10, 0x00}, //display mode 13 {0x61, 0x2A, 0x50, 0x44, 0x31, 0x81, 0x36, 0x2C, 0x40, 0x04, 0x09, 0x266F1FD0, 0x10, 0x00}, //display mode 14 {0x69, 0x2A, 0x50, 0x6A, 0x30, 0x81, 0x1F, 0xF9, 0x40, 0x3F, 0x6E, 0x25249249, 0x00, 0x00}, //display mode 16 {0x6A, 0x2A, 0x50, 0x64, 0x30, 0x81, 0x2D, 0xFD, 0x40, 0x3F, 0x7E, 0x20800000, 0x00, 0x00}, //display mode 17};static int ch7013_i2c_program(struct i2c_client *client){ int idx; unsigned char chip_ver; u8 reg; idx = 3; ch7013_i2c_write(0x0E, 0x00); //soft reset //mdelay(1); ch7013_i2c_write(0x0E, 0x0B); //all are active //mdelay(1); reg = 0x25; ch7013_i2c_read(reg, &chip_ver); printk("Chip version ID(0x22)= 0x%X\n", chip_ver); ch7013_i2c_write(0x00, ctable[idx].DMR); //display mode ch7013_i2c_write(0x01, ctable[idx].FFR); //flicker filter settings ch7013_i2c_write(0x04, 0x00); //Input data format 16bit 565 ch7013_i2c_write(0x06, ctable[idx].CM); //clock mode ch7013_i2c_write(0x07, ctable[idx].SAV); //start of active video register ch7013_i2c_write( 0x08, ctable[idx].PO); //position overflow ch7013_i2c_write( 0x09, ctable[idx].BLR); //black level ch7013_i2c_write( 0x0A, ctable[idx].HPR); //horizontal position ch7013_i2c_write( 0x0B, ctable[idx].VPR); //vertical position ch7013_i2c_write( 0x13, ctable[idx].MNE); //PLLM and PLLN ch7013_i2c_write( 0x14, ctable[idx].PLLM); ch7013_i2c_write( 0x15, ctable[idx].PLLN); ch7013_i2c_write( 0x20, ctable[idx].PLLC); //PLL control ch7013_i2c_write( 0x21, ctable[idx].CIVC); //turn off CIV ch7013_i2c_write( 0x18, (ctable[idx].FSCI & 0xF0000000) >> 28); ch7013_i2c_write( 0x19, (ctable[idx].FSCI & 0x0F000000) >> 24); ch7013_i2c_write( 0x1A, (ctable[idx].FSCI & 0x00F00000) >> 20); ch7013_i2c_write( 0x1B, (ctable[idx].FSCI & 0x000F0000) >> 16); ch7013_i2c_write( 0x1C, (ctable[idx].FSCI & 0x0000F000) >> 12); ch7013_i2c_write( 0x1D, (ctable[idx].FSCI & 0x00000F00) >> 8); ch7013_i2c_write( 0x1E, (ctable[idx].FSCI & 0x000000F0) >> 4); ch7013_i2c_write( 0x1F, ctable[idx].FSCI & 0x0000000F); return 0;}//This function config LCDC registers to fit TVOUT output.static void ch7013_set_lcdc(void){ unsigned int val; unsigned int reg; /* Size register */ __raw_writel(0x128001E0, LCDC_REG(LCDC_LSR)); // refer to CONFIG_FB_64_TFTLCD /* Virtual page width register */ __raw_writel(0x140, LCDC_REG(LCDC_LVPWR)); // refer to CONFIG_FB_64_TFTLCD //mxc_set_clocks_div(LCDC_CLK, 1); reg = __raw_readl(IO_ADDRESS(0x10027000) + 0x1c); reg = (reg & (~(0x3f << 16))); __raw_writel(reg, IO_ADDRESS(0x10027000) + 0x1c); /* Panel configuration register */ __raw_writel(0xFA380089, LCDC_REG(LCDC_LPCR)); //__raw_writel(0xFAE000C1, LCDC_REG(LCDC_LPCR)); // refer to CONFIG_FB_64_TFTLCD /* Horizontal and vertical configuration register */ val = (13<<26) | (25<<8) | (98); printk("LHCR: 0x%x\n", val); __raw_writel(val, LCDC_REG(LCDC_LHCR)); printk("LHCR = 0x%x\n",(*(unsigned long *)(LCDC_REG(LCDC_LHCR)))); //__raw_writel(0xB0002008, LCDC_REG(LCDC_LHCR)); //val = (2<<26) | (46<<8) | (54); val = (2<<26) | (43<<8) | (43); printk("LVCR: 0x%x\n", val); __raw_writel(val, LCDC_REG(LCDC_LVCR)); //__raw_writel(0x0200110A, LCDC_REG(LCDC_LVCR)); /* Sharp configuration register */ //__raw_writel(0x04120300, LCDC_REG(LCDC_LSCR)); /* Refresh mode control reigster */ __raw_writel(0x00000000, LCDC_REG(LCDC_LRMCR)); /* PWM contrast control register */ __raw_writel(0x00A903FF, LCDC_REG(LCDC_LPCCR)); /* DMA control register */ __raw_writel(0x00030008, LCDC_REG(LCDC_LDCR));}/** * @brief Save the register values/status that might be changed by tvout */static void ch7013_save_lcdc(void){ //Save lcdc registers g_lcdc_regs.LSSAR = __raw_readl(LCDC_REG(LCDC_LSSAR)); g_lcdc_regs.LSR = __raw_readl(LCDC_REG(LCDC_LSR)); g_lcdc_regs.LVPWR = __raw_readl(LCDC_REG(LCDC_LVPWR)); g_lcdc_regs.LPCR = __raw_readl(LCDC_REG(LCDC_LPCR)); g_lcdc_regs.LHCR = __raw_readl(LCDC_REG(LCDC_LHCR)); g_lcdc_regs.LVCR = __raw_readl(LCDC_REG(LCDC_LVCR)); g_lcdc_regs.LSCR = __raw_readl(LCDC_REG(LCDC_LSSAR)); g_lcdc_regs.LPCCR = __raw_readl(LCDC_REG(LCDC_LPCCR)); g_lcdc_regs.LRMCR = __raw_readl(LCDC_REG(LCDC_LRMCR)); g_lcdc_regs.LDCR = __raw_readl(LCDC_REG(LCDC_LDCR)); g_lcdc_regs.LGWDCR = __raw_readl(LCDC_REG(LCDC_LGWDCR)); }static void ch7013_hw_init(void){ ch7013_i2c_init(); printk("ch7013_i2c_init over!\n"); ch7013_save_lcdc();; printk("ch7013_save_lcdc over!\n"); ch7013_set_lcdc(); printk("ch7013_set_lcdc over!\n"); mdelay(10); ch7013_i2c_program(&ch7013_i2c_client); printk("ch7013_i2c_program over!\n");}static void ch7013_hw_exit(void){ ch7013_i2c_cleanup();}static int ch7013_open(struct inode * inode, struct file * filp){ return 0;}static int ch7013_release(struct inode * inode, struct file * filp){ return 0;}static int ch7013_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg){ int ret = 0; return ret;}struct file_operations g_tvout_fops = { owner: THIS_MODULE, ioctl: ch7013_ioctl, open: ch7013_open, release: ch7013_release,};static int __init ch7013_init(void){ printk("MX27 CH7013 tvout driver.\n"); g_tvout_major = register_chrdev(0, MODULE_NAME, &g_tvout_fops); if (g_tvout_major < 0) { printk("Unable to register %s driver.\n",MODULE_NAME); remove_proc_entry(MODULE_NAME, NULL); return -ENODEV; } else { printk("/dev/tvout major device number is: %d\n", g_tvout_major); } ch7013_hw_init(); return 0;}static void __exit ch7013_exit(void){ ch7013_hw_exit(); if (g_tvout_major > 0) { unregister_chrdev(g_tvout_major, MODULE_NAME); }}module_init(ch7013_init);module_exit(ch7013_exit);MODULE_DESCRIPTION("MX27 TVOUT CH7013 Driver ");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -