OSHW-DEIMOS/SOFTWARE/A64-TERES/linux-a64/drivers/misc/anx6345.c
Dimitar Gamishev f9b0e7a283 linux
2017-10-13 14:07:04 +03:00

674 lines
17 KiB
C

/*
AN6345 Module by <hehopmajieh@debian.bg> Mitko Gamishev 2016
***************************************************************
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include "anx6345.h"
#include <linux/debugfs.h>
//BIST Mode
/*#define BIST_MODE 1*/
static const unsigned short normal_i2c[] = {0x39, I2C_CLIENT_END};
static __u32 twi_id = 0;
static int i2c_num = 0;
static const unsigned short i2c_address[3] = {0x39,0x38,0x2d};
int i2c_master_reg8_recv(const struct i2c_client *client, const char reg, char *buf, int count, int scl_rate)
{
struct i2c_adapter *adap=client->adapter;
struct i2c_msg msgs[2];
int ret;
char reg_buf = reg;
msgs[0].addr = client->addr;
msgs[0].flags = client->flags;
msgs[0].len = 1;
msgs[0].buf = &reg_buf;
msgs[1].addr = client->addr;
msgs[1].flags = client->flags | I2C_M_RD;
msgs[1].len = count;
msgs[1].buf = (char *)buf;
ret = i2c_transfer(adap, msgs, 2);
return (ret == 2)? count : ret;
}
int i2c_master_reg8_send(const struct i2c_client *client, const char reg, const char *buf, int count, int scl_rate)
{
struct i2c_adapter *adap=client->adapter;
struct i2c_msg msg;
int ret;
char *tx_buf = (char *)kmalloc(count + 1, GFP_KERNEL);
if(!tx_buf)
return -ENOMEM;
tx_buf[0] = reg;
memcpy(tx_buf+1, buf, count);
msg.addr = client->addr;
msg.flags = client->flags;
msg.len = count + 1;
msg.buf = (char *)tx_buf;
ret = i2c_transfer(adap, &msg, 1);
kfree(tx_buf);
return (ret == 1) ? count : ret;
}
static int anx6345_detect(struct i2c_client *client, struct i2c_board_info *info)
{
int ret;
// dprintk(DEBUG_INIT, "%s enter \n", __func__);
struct i2c_adapter *adapter = client->adapter;
int address = client->addr;
const char *name = NULL;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
name = "anx6345";
strlcpy(info->type, name, I2C_NAME_SIZE);
}
static int anx6345_i2c_read_p0_reg(struct i2c_client *client, char reg, char *val)
{
int ret;
client->addr = DP_TX_PORT0_ADDR >> 1;
ret = i2c_master_reg8_recv(client, reg, val, 1, ANX6345_SCL_RATE) > 0? 0: -EINVAL;
if(ret < 0)
{
printk(KERN_ERR "%s>>err\n",__func__);
}
return ret;
}
static int anx6345_i2c_write_p0_reg(struct i2c_client *client, char reg, char *val)
{
int ret;
client->addr = DP_TX_PORT0_ADDR >> 1;
ret = i2c_master_reg8_send(client, reg, val, 1, ANX6345_SCL_RATE) > 0? 0: -EINVAL;
if(ret < 0)
{
printk(KERN_ERR "%s>>err\n",__func__);
}
return ret;
}
static int anx6345_i2c_read_p1_reg(struct i2c_client *client, char reg, char *val)
{
int ret;
client->addr = HDMI_TX_PORT0_ADDR >> 1;
ret = i2c_master_reg8_recv(client, reg, val, 1, ANX6345_SCL_RATE) > 0? 0: -EINVAL;
if(ret < 0)
{
printk(KERN_ERR "%s>>err\n",__func__);
printk("addr %d",client->addr);
}
return ret;
}
static int anx6345_i2c_write_p1_reg(struct i2c_client *client, char reg, char *val)
{
int ret;
client->addr = HDMI_TX_PORT0_ADDR >> 1;
ret = i2c_master_reg8_send(client, reg, val, 1, ANX6345_SCL_RATE) > 0? 0: -EINVAL;
if(ret < 0)
{
printk(KERN_ERR "%s>>err\n",__func__);
}
return ret;
}
static int edp_reg_show(struct seq_file *s, void *v)
{
int i = 0;
char val;
struct edp_anx6345 *anx6345 = s->private;
if(!anx6345)
{
printk(KERN_ERR "no edp device!\n");
return 0;
}
seq_printf(s,"0x70:\n");
for(i=0;i< MAX_REG;i++)
{
anx6345_i2c_read_p0_reg(anx6345->client, i , &val);
seq_printf(s,"0x%02x>>0x%02x\n",i,val);
}
seq_printf(s,"\n0x72:\n");
for(i=0;i< MAX_REG;i++)
{
anx6345_i2c_read_p1_reg(anx6345->client, i , &val);
seq_printf(s,"0x%02x>>0x%02x\n",i,val);
}
return 0;
}
static int edp_reg_open(struct inode *inode, struct file *file)
{
struct edp_anx6345 *anx6345 = inode->i_private;
return single_open(file, edp_reg_show, anx6345);
}
static const struct file_operations edp_reg_fops = {
.owner = THIS_MODULE,
.open = edp_reg_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
//get chip ID. Make sure I2C is OK
static int get_dp_chip_id(struct i2c_client *client)
{
char c1,c2;
int id;
anx6345_i2c_read_p1_reg(client,SP_TX_DEV_IDL_REG,&c1);
anx6345_i2c_read_p1_reg(client,SP_TX_DEV_IDH_REG,&c2);
id = c2;
return (id<<8)|c1;
}
#if 0
static int anx980x_aux_rst(struct i2c_client *client)
{
char val;
anx6345_i2c_read_p1_reg(client, DP_TX_RST_CTRL2_REG, &val);
val |= DP_TX_AUX_RST;
anx6345_i2c_write_p1_reg(client, DP_TX_RST_CTRL2_REG, &val);
val &= ~DP_TX_AUX_RST;
anx6345_i2c_write_p1_reg(client, DP_TX_RST_CTRL2_REG, &val);
return 0;
}
static int anx980x_wait_aux_finished(struct i2c_client *client)
{
char val,cnt;
cnt = 0;
anx6345_i2c_read_p0_reg(client,DP_TX_AUX_CTRL_REG2, &val);
while(val&0x01)
{
//delay_ms(20);
cnt ++;
if(cnt == 10)
{
printk("aux break");
anx980x_aux_rst(client);
//cnt = 0;
break;
}
anx6345_i2c_read_p0_reg(client, DP_TX_AUX_CTRL_REG2, &val);
}
return 0;
}
static int anx980x_aux_dpcdread_bytes(struct i2c_client *client,unsigned long addr, char cCount,char* pBuf)
{
char val,i;
val = 0x80;
anx6345_i2c_write_p0_reg(client, DP_TX_BUF_DATA_COUNT_REG, &val);
//set read cmd and count
val = (((char)(cCount-1) <<4)&(0xf0))|0x09;
anx6345_i2c_write_p0_reg(client, DP_TX_AUX_CTRL_REG, &val);
//set aux address15:0
val = (char)addr&0xff;
anx6345_i2c_write_p0_reg(client, DP_TX_AUX_ADDR_7_0_REG, &val);
val = (char)((addr>>8)&0xff);
anx6345_i2c_write_p0_reg(client, DP_TX_AUX_ADDR_15_8_REG, &val);
//set address19:16 and enable aux
anx6345_i2c_read_p0_reg(client, DP_TX_AUX_ADDR_19_16_REG, &val);
val &=(0xf0)|(char)((addr>>16)&0xff);
anx6345_i2c_write_p0_reg(client, DP_TX_AUX_ADDR_19_16_REG, &val);
//Enable Aux
anx6345_i2c_read_p0_reg(client, DP_TX_AUX_CTRL_REG2, &val);
val |= 0x01;
anx6345_i2c_write_p0_reg(client, DP_TX_AUX_CTRL_REG2, &val);
//delay_ms(2);
anx980x_wait_aux_finished(client);
for(i =0;i<cCount;i++)
{
anx6345_i2c_read_p0_reg(client, DP_TX_BUF_DATA_0_REG+i, &val);
//printk("c = %.2x\n",(WORD)c);
*(pBuf+i) = val;
if(i >= MAX_BUF_CNT)
return 1;
//break;
}
return 0;
}
static int anx_video_map_config(struct i2c_client *client)
{
char val = 0;
char i = 0;
anx6345_i2c_write_p1_reg(client, 0x40, &val);
anx6345_i2c_write_p1_reg(client, 0x41, &val);
anx6345_i2c_write_p1_reg(client, 0x48, &val);
anx6345_i2c_write_p1_reg(client, 0x49, &val);
anx6345_i2c_write_p1_reg(client, 0x50, &val);
anx6345_i2c_write_p1_reg(client, 0x51, &val);
for(i=0; i<6; i++)
{
val = i;
anx6345_i2c_write_p1_reg(client, 0x42+i, &val);
}
for(i=0; i<6; i++)
{
val = 6+i;
anx6345_i2c_write_p1_reg(client, 0x4a+i, &val);
}
for(i=0; i<6; i++)
{
val = 0x0c+i;
anx6345_i2c_write_p1_reg(client, 0x52+i, &val);
}
return 0;
}
static int anx980x_eanble_video_input(struct i2c_client *client)
{
char val;
anx6345_i2c_read_p1_reg(client, DP_TX_VID_CTRL1_REG, &val);
val |= DP_TX_VID_CTRL1_VID_EN;
anx6345_i2c_write_p1_reg(client, DP_TX_VID_CTRL1_REG, &val);
anx_video_map_config(client);
return 0;
}
#endif
#if defined(BIST_MODE)
static int anx6345_bist_mode(struct i2c_client *client)
{
char val = 0x00;
//these register are for bist mode
val = 0x28;
anx6345_i2c_write_p1_reg(client,SP_TX_TOTAL_LINEL_REG,&val);
val = 0x03;
anx6345_i2c_write_p1_reg(client,SP_TX_TOTAL_LINEH_REG,&val);
val = 0x00;
anx6345_i2c_write_p1_reg(client,SP_TX_ACT_LINEL_REG,&val);
val = 0x03;
anx6345_i2c_write_p1_reg(client,SP_TX_ACT_LINEH_REG,&val);
val = 0x14;
anx6345_i2c_write_p1_reg(client,SP_TX_VF_PORCH_REG,&val);
val = 0x04 ;
anx6345_i2c_write_p1_reg(client,SP_TX_VSYNC_CFG_REG,&val);
val = 0x10;
anx6345_i2c_write_p1_reg(client,SP_TX_VB_PORCH_REG,&val);
val = 0x38;
anx6345_i2c_write_p1_reg(client,SP_TX_TOTAL_PIXELL_REG,&val);
val = 0x06;
anx6345_i2c_write_p1_reg(client,SP_TX_TOTAL_PIXELH_REG,&val);
val = 0x56;
anx6345_i2c_write_p1_reg(client,SP_TX_ACT_PIXELL_REG,&val);
val = 0x05;
anx6345_i2c_write_p1_reg(client,SP_TX_ACT_PIXELH_REG,&val);
val = 0x31;
anx6345_i2c_write_p1_reg(client,SP_TX_HF_PORCHL_REG,&val);
val = 0x00;
anx6345_i2c_write_p1_reg(client,SP_TX_HF_PORCHH_REG,&val);
val = 0x10;
anx6345_i2c_write_p1_reg(client,SP_TX_HSYNC_CFGL_REG,&val);
val = 0x00;
anx6345_i2c_write_p1_reg(client,SP_TX_HSYNC_CFGH_REG,&val);
val = 0xA1;
anx6345_i2c_write_p1_reg(client,SP_TX_HB_PORCHL_REG,&val);
val=0x00;
anx6345_i2c_write_p1_reg(client,SP_TX_HB_PORCHH_REG,&val);
val = 0x13;
anx6345_i2c_write_p1_reg(client,SP_TX_VID_CTRL10_REG,&val);
//enable BIST. In normal mode, don't need to config this reg
val = 0x08;
anx6345_i2c_write_p1_reg(client, 0x0b, &val);
val=0x81;
anx6345_i2c_write_p1_reg(client,0x08,&val);
val=0x33;
anx6345_i2c_write_p0_reg(client,0x82,&val);
printk("anx6345 enter bist mode\n");
return 0;
}
#endif
static int anx6345_init(struct i2c_client *client)
{
char val = 0x00;
char i = 0;
char cnt = 50;
val = 0x30;
val = 0x00;
anx6345_i2c_write_p1_reg(client,0x05,&val);
anx6345_i2c_read_p1_reg(client,0x01,&val);
val = 00;
anx6345_i2c_write_p1_reg(client, SP_POWERD_CTRL_REG, &val);
val = SP_TX_RST_HW_RST;
anx6345_i2c_write_p1_reg(client, SP_TX_RST_CTRL_REG, &val);
msleep(1);
val = 0x00;
anx6345_i2c_write_p1_reg(client, SP_TX_RST_CTRL_REG, &val);
//reset i2c registers
val = SP_TX_I2C_REG_RST;
anx6345_i2c_write_p1_reg(client, SP_TX_RST_CTRL2_REG, &val);
val = 0x40;
msleep(50);
anx6345_i2c_write_p1_reg(client, SP_TX_RST_CTRL2_REG, &val);
val = 0x01;
anx6345_i2c_write_p1_reg(client, 0xd9, &val);
anx6345_i2c_read_p1_reg(client, SP_POWERD_CTRL_REG,&val);
val = 0x00;
anx6345_i2c_write_p1_reg(client, SP_POWERD_CTRL_REG, &val);
//get chip ID. Make sure I2C is OK
anx6345_i2c_read_p1_reg(client, SP_TX_DEV_IDH_REG,&val );
if (val==0x63)
printk("Chip found\n");
//for clocl detect
for(i=0;i<100;i++)
{
anx6345_i2c_read_p0_reg(client, SP_TX_SYS_CTRL1_REG,&val);
anx6345_i2c_write_p0_reg(client, SP_TX_SYS_CTRL1_REG, &val);
anx6345_i2c_read_p0_reg(client, SP_TX_SYS_CTRL1_REG,&val);
if((val&SP_TX_SYS_CTRL1_DET_STA)!=0)
{
printk("clock is detected.\n");
break;
}
msleep(100);
}
for(i=0;i<50;i++)
{
anx6345_i2c_read_p0_reg(client, SP_TX_SYS_CTRL2_REG,&val);
anx6345_i2c_write_p0_reg(client, SP_TX_SYS_CTRL2_REG, &val);
anx6345_i2c_read_p0_reg(client, SP_TX_SYS_CTRL2_REG,&val);
if((val&SP_TX_SYS_CTRL2_CHA_STA)==0)
{
printk("clock is stable. \n");
break;
}
msleep(100);
}
//VESA range, 8bits BPC, RGB
val = 0x00;//0x10
anx6345_i2c_write_p1_reg(client, SP_TX_VID_CTRL2_REG, &val);
//RK_EDP chip analog setting
val = 0x07;
anx6345_i2c_write_p0_reg(client, SP_TX_PLL_CTRL_REG, &val);
val = 0x19;
anx6345_i2c_write_p1_reg(client, PLL_FILTER_CTRL3, &val);
val = 0xd9;
anx6345_i2c_write_p1_reg(client, 0xE6, &val); // not sure ? DP_TX_PLL_CTRL3
//24bit SDR,negedge latch, and wait video stable
val = 0x01;
anx6345_i2c_write_p1_reg(client, SP_TX_VID_CTRL1_REG, &val);//72:08 for 9804 SDR, neg edge 05/04/09 extra pxl
val = 0x19;
anx6345_i2c_write_p1_reg(client, 0xE1, &val);
val = 0xd9;
anx6345_i2c_write_p1_reg(client, 0xE1, &val);
//Select AC mode
val = 0x40; //0x40;
anx6345_i2c_write_p1_reg(client, SP_TX_RST_CTRL2_REG, &val);
/*
//Set bist format 1366 768
val = 0x20;//0x58; //0x20;
anx6345_i2c_write_p1_reg(client, SP_TX_TOTAL_LINEL_REG, &val);
val = 0x03;//0x04;//0x03;
anx6345_i2c_write_p1_reg(client, SP_TX_TOTAL_LINEH_REG, &val);
val = 0x00;//0x38;//0x00;
anx6345_i2c_write_p1_reg(client, SP_TX_ACT_LINEL_REG, &val);
val = 0x03;//0x04;//0x03;
anx6345_i2c_write_p1_reg(client, SP_TX_ACT_LINEH_REG,&val);
val = 0x14;
anx6345_i2c_write_p1_reg(client, SP_TX_VF_PORCH_REG, &val);
val = 0x04;
anx6345_i2c_write_p1_reg(client, SP_TX_VSYNC_CFG_REG,&val);
val = 0x10;
anx6345_i2c_write_p1_reg(client, SP_TX_VB_PORCH_REG, &val);
val = 0x38;//0x62;//0x38;
anx6345_i2c_write_p1_reg(client, SP_TX_TOTAL_PIXELL_REG, &val);
val = 0x06;//0x08;//0x06;
anx6345_i2c_write_p1_reg(client, SP_TX_TOTAL_PIXELH_REG, &val);
val = 0x56;//0x80;//0x56;
anx6345_i2c_write_p1_reg(client, SP_TX_ACT_PIXELL_REG, &val);
val = 0x05;//0x07;//0x05;
anx6345_i2c_write_p1_reg(client, SP_TX_ACT_PIXELH_REG, &val);
val = 0x31;
anx6345_i2c_write_p1_reg(client, SP_TX_HF_PORCHL_REG, &val);
val = 0x00;
anx6345_i2c_write_p1_reg(client, SP_TX_HF_PORCHH_REG, &val);
val = 0x10;
anx6345_i2c_write_p1_reg(client, SP_TX_HSYNC_CFGL_REG,&val);
val = 0x00;
anx6345_i2c_write_p1_reg(client, SP_TX_HSYNC_CFGH_REG,&val);
val = 0xa1;
anx6345_i2c_write_p1_reg(client, SP_TX_HB_PORCHL_REG, &val);
val = 0x00;
anx6345_i2c_write_p1_reg(client, SP_TX_HB_PORCHH_REG, &val);
*/
val = 0x00; //0x03;
anx6345_i2c_write_p1_reg(client, SP_TX_VID_CTRL10_REG, &val);
//RK_EDP chip analog setting
val = 0xf0; //0xf0;
anx6345_i2c_write_p1_reg(client, ANALOG_DEBUG_REG1, &val);
val = 0x99; //0x99;
anx6345_i2c_write_p1_reg(client, ANALOG_DEBUG_REG3, &val);//maybe lane level
val = 0x7b; //0x7b;
anx6345_i2c_write_p1_reg(client, PLL_FILTER_CTRL1, &val);
val = 0x70;//0x30;
anx6345_i2c_write_p1_reg(client, SP_TX_LINK_DEBUG_REG,&val);
val = 0x06;//0x06;
anx6345_i2c_write_p1_reg(client,0xE2, &val);
val = 0x00;//0x06;
anx6345_i2c_write_p0_reg(client,0xdd, &val);
//force HPD
val = 0x30;
anx6345_i2c_write_p0_reg(client, SP_TX_SYS_CTRL3_REG, &val);
//power on 4 lanes
val = 0xce;
anx6345_i2c_write_p0_reg(client, 0xc8, &val);
//lanes setting
val = 0xfe;
anx6345_i2c_write_p0_reg(client, 0xa3, &val);
val = 0xff;
anx6345_i2c_write_p0_reg(client, 0xa4, &val);
anx6345_i2c_write_p0_reg(client, 0xa5, &val);
anx6345_i2c_write_p0_reg(client, 0xa6, &val);
//reset AUX CH
val = 0x44;
anx6345_i2c_write_p1_reg(client, SP_TX_RST_CTRL2_REG, &val);
val = 0x40;
msleep(50);
anx6345_i2c_write_p1_reg(client, SP_TX_RST_CTRL2_REG, &val);
//Select 1.62G
val = 0x0a;
anx6345_i2c_write_p0_reg(client, SP_TX_LINK_BW_SET_REG, &val);
//Select 4 lanes
val = 0x01;
anx6345_i2c_write_p0_reg(client, SP_TX_LANE_COUNT_SET_REG, &val);
#if 1
val = SP_TX_LINK_TRAINING_CTRL_EN;
anx6345_i2c_write_p0_reg(client, SP_TX_LINK_TRAINING_CTRL_REG, &val);
msleep(50);
anx6345_i2c_write_p0_reg(client,SP_TX_LINK_TRAINING_CTRL_REG, &val);
while((val&0x01)&&(cnt++ < 10))
{
printk("Waiting...\n");
msleep(50);
anx6345_i2c_read_p0_reg(client,SP_TX_LINK_TRAINING_CTRL_REG, &val );
}
if(cnt >= 10)
{
printk("HW LT fail\n");
}
else
{
printk( "HW LT success ...cnt:%d\n",cnt);
}
#endif
//enable video input
val = 0x81;
anx6345_i2c_write_p1_reg(client, SP_TX_VID_CTRL1_REG, &val);
//enable BIST
val = 0x00; //SP_TX_VID_CTRL4_BIST;
anx6345_i2c_write_p1_reg(client, SP_TX_VID_CTRL4_REG, &val);
// I2CWriteReg8(fd, 0x08, 0x83); //SDR:0x81;DDR:0x8f
val=0x83;
anx6345_i2c_write_p0_reg(client, 0x08, &val);
//force HPD and stream valid
val = 0x03;
anx6345_i2c_write_p0_reg(client, 0x82, &val);
return 0;
}
static int anx6345_i2c_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
int ret;
struct edp_anx6345 *anx6345 = NULL;
int chip_id;
printk("Probe Enter!!!\n");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
{
dev_err(&client->dev, "Must have I2C_FUNC_I2C.\n");
ret = -ENODEV;
}
anx6345 = kzalloc(sizeof(struct edp_anx6345), GFP_KERNEL);
if (anx6345 == NULL)
{
printk("alloc for struct anx6345 fail\n");
ret = -ENOMEM;
}
client->addr = 0x39;
anx6345->client = client;
anx6345->pdata = client->dev.platform_data;
i2c_set_clientdata(client,anx6345);
debugfs_create_file("edp-reg", S_IRUSR,NULL,anx6345,&edp_reg_fops);
printk("Probe Enter get id!!!\n");
anx6345->edp_anx_init = anx6345_init;
printk("Probe Enter edp_anx_init!!!\n");
anx6345->edp_anx_init(client);
printk("edp anx%x probe ok\n",get_dp_chip_id(client));
return ret;
}
static int anx6345_i2c_remove(struct i2c_client *client)
{
return 0;
}
static const struct i2c_device_id id_table[] = {
{"anx6345", 0 },
{ }
};
static struct i2c_driver anx6345_i2c_driver = {
.driver = {
.name = "anx6345",
},
.probe = anx6345_i2c_probe,
.remove = anx6345_i2c_remove,
.id_table = id_table,
.detect = anx6345_detect,
.address_list = normal_i2c,
};
MODULE_DEVICE_TABLE(i2c, id_table);
static int __init anx6345_module_init(void)
{
int ret = -1;
ret = i2c_add_driver(&anx6345_i2c_driver);
printk("anx6345 Init Ret: %d \n",ret);
return ret;
}
static void anx6345_module_exit(void)
{
i2c_del_driver(&anx6345_i2c_driver);
}
module_exit(anx6345_module_exit);
module_init(anx6345_module_init);
MODULE_AUTHOR("Mitko Gamishev <hehopmajieh@debian.bg>");
MODULE_DESCRIPTION("AN6345 RGB<->eDP Transmitter");
MODULE_LICENSE("GPL");