在车机显示 系统中,AIM915X和AIM916X作为车机和显示屏之间的传输芯片,车机的LVDS视频信号传到显示屏;控制信号如I2C、GPIO可实现双向透传;
一、设备树
开发平台:IMX6D
1、设备节点
2、timing参数
二、分辨率
1、修改为屏幕适配的分辨率
路径:drivers/video/fbdev/core/modedb.c
2、修改相关启动参数
烧录后会显示相关信息
cat /proc/cmdline
3、查看修改后的设备信息
cat /sys/class/graphics/fb0/mode
三、AIM915X AIM916X芯片调试
1、原理图
AIM915X:
AIM916X:
2、IIC地址
915:
Linux 7位地址为0x0C
916:
Linux 7位地址为0x2C
3、PDB
4、上电时序
5、配置步骤
#第 1 步:初始化 915X Rx/Tx 相关寄存器
i2cset -f -y 2 0x0c 0x6A 0x01
sleep 0.01
i2cset -f -y 2 0x0c 0x66 0x02
sleep 0.01
i2cset -f -y 2 0x0c 0xAD 0x02
sleep 0.01
i2cset -f -y 2 0x0c 0x06 0x59
sleep 0.01
i2cset -f -y 2 0x0c 0x2F 0x05
sleep 0.01
i2cset -f -y 2 0x0c 0x2A 0x30
sleep 0.01
i2cset -f -y 2 0x0c 0x2C 0x01
sleep 0.01
i2cset -f -y 2 0x0c 0x33 0x35
sleep 0.01
i2cset -f -y 2 0x0c 0x32 0xA2
sleep 0.01
i2cset -f -y 2 0x0c 0x34 0x00
sleep 0.01
i2cset -f -y 2 0x0c 0xA3 0x50
sleep 0.01
i2cset -f -y 2 0x0c 0x03 0x3A
#第 2 步:选择 915X/916X 工作模式,根据需要选择一种模式,进行配置。第 2 步配置完成后,延时 30ms,再配置第 3 步。
40bit 模式
i2cset -f -y 2 0x2c 0x42 0x12
i2cset -f -y 2 0x0c 0x6c 0x12
sleep 0.3
# 第 3 步:915X 外设 I2C 地址映射
# 此处是映射触摸的地址,GT9XX(0x14),此处可以不要,后续文章会更新调试触摸的过程
i2cset -f -y 2 0x0c 0x07 0x28
i2cset -f -y 2 0x0c 0x08 0x28
#第 4 步:916X Rx/Tx 相关寄存器
i2cset -f -y 2 0x0c 0x20 0x03
i2cset -f -y 2 0x0c 0x48 0xFF
i2cset -f -y 2 0x2c 0x67 0x37
i2cset -f -y 2 0x2c 0x49 0x3F
i2cset -f -y 2 0x0c 0x5A 0xF0
i2cset -f -y 2 0x0c 0x28 0x04
i2cset -f -y 2 0x2c 0x49 0x3A
i2cset -f -y 2 0x2c 0x49 0x3E
i2cset -f -y 2 0x2c 0x49 0x3F
i2cset -f -y 2 0x2c 0x49 0x3E
i2cset -f -y 2 0x2c 0x49 0x3F
i2cset -f -y 2 0x2c 0x49 0x3E
i2cset -f -y 2 0x2c 0x49 0x3F
i2cset -f -y 2 0x2c 0x49 0x3E
i2cset -f -y 2 0x2c 0x49 0x3F
i2cset -f -y 2 0x2c 0x49 0x3E
i2cset -f -y 2 0x2c 0x49 0x3F
i2cset -f -y 2 0x2c 0x49 0x3E
i2cset -f -y 2 0x2c 0x49 0x3F
i2cset -f -y 2 0x2c 0x49 0x3E
i2cset -f -y 2 0x2c 0x49 0x3F
i2cset -f -y 2 0x2c 0x49 0x3E
i2cset -f -y 2 0x2c 0x49 0x3F
i2cset -f -y 2 0x2c 0x49 0x3E
i2cset -f -y 2 0x2c 0x49 0x3F
i2cset -f -y 2 0x2c 0x49 0x3E
i2cset -f -y 2 0x2c 0x49 0x3F
i2cset -f -y 2 0x2c 0x5A 0xF4
i2cset -f -y 2 0x2c 0x40 0xF8
i2cset -f -y 2 0x2c 0x28 0x00
i2cset -f -y 2 0x2c 0x40 0xF0
i2cset -f -y 2 0x2c 0x67 0x33
#第 5 步:配置 915X 的 GPIO[3:0]GPIO[5:8]至 916X 相应 GPIO 的透传(透传指的是915端控制远端916的GPIO状态)
GPIO2 用作 PWM 915 in 916 out
i2cget -y 2 0x0c 0x0B
# 916端本地使能背光
i2cset -y 2 0x2C 0x1D 0x99
#第 6 步:配置 915X 端接收 916X 端输入的中断
i2cset -f -y 2 0x0C 0x7F 0x21
#第 7 步:915X 完成初始化
i2cset -f -y 2 0x0c 0x03 0x1A
i2cset -f -y 2 0x0c 0x44 0x16
i2cset -f -y 2 0x0c 0x6F 0x80
i2cset -f -y 2 0x0c 0x72 0x02
i2cset -f -y 2 0x0c 0x72 0x03
i2cset -f -y 2 0x0c 0x6F 0x00
i2cset -f -y 2 0x0c 0x25 0x07
i2cset -f -y 2 0x0c 0x26 0x00
i2cset -f -y 2 0x0c 0x6A 0xC9
i2cset -f -y 2 0x0c 0x72 0x02
i2cset -f -y 2 0x0c 0x72 0x03
i2cset -f -y 2 0x0c 0x73 0x02
i2cset -f -y 2 0x0c 0x73 0x03
i2cset -f -y 2 0x0c 0x26 0x04
i2cset -f -y 2 0x0c 0x6a 0x01
i2cset -f -y 2 0x0c 0x72 0xFF
i2cset -f -y 2 0x0c 0x23 0x37
i2cset -f -y 2 0x0c 0x25 0x0F
i2cset -f -y 2 0x0c 0x26 0x00
i2cset -f -y 2 0x0c 0x6F 0x80
i2cset -f -y 2 0x0c 0x71 0xBF
i2cset -f -y 2 0x0c 0x72 0xBE
i2cset -f -y 2 0x0c 0x71 0xFF
i2cset -f -y 2 0x0c 0x72 0xFF
i2cset -f -y 2 0x0c 0x72 0xFE
i2cset -f -y 2 0x0c 0x72 0xFF
i2cset -f -y 2 0x0c 0x72 0xFE
i2cset -f -y 2 0x0c 0x72 0xFF
i2cset -f -y 2 0x0c 0x72 0xFE
i2cset -f -y 2 0x0c 0x72 0xFF
i2cset -f -y 2 0x0c 0x72 0xFE
i2cset -f -y 2 0x0c 0x72 0xFF
i2cset -f -y 2 0x0c 0x72 0xFE
i2cset -f -y 2 0x0c 0x72 0xFF
i2cset -f -y 2 0x0c 0x72 0xFE
i2cset -f -y 2 0x0c 0x72 0xFF
i2cset -f -y 2 0x0c 0x72 0xFE
i2cset -f -y 2 0x0c 0x72 0xFF
i2cset -f -y 2 0x0c 0x72 0xFE
i2cset -f -y 2 0x0c 0x72 0xFF
i2cset -f -y 2 0x0c 0x72 0xFE
i2cset -f -y 2 0x0c 0x72 0xFF
i2cset -f -y 2 0x0c 0x26 0x04
i2cset -f -y 2 0x0c 0x6a 0x09
i2cset -f -y 2 0x0c 0x6f 0x00
i2cset -f -y 2 0x0c 0x6a 0x01
i2cset -f -y 2 0x0c 0x23 0x33
#第八步:驱动编写
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/unistd.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/semaphore.h>
#include <linux/kthread.h>
#include <linux/notifier.h>
#include <linux/fb.h>
#include <linux/reboot.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include "aim915916.h"
#define DYNAMIC_DETECT_AIM955956 1
static struct i2c_client *aim955_client = NULL;
static struct i2c_client *aim956_client = NULL;
#ifdef DYNAMIC_DETECT_AIM955956
struct task_struct *glink_detect_kt = NULL;
static int link_detect_kernel_thread(void *data);
static void link_detect_kernel_thread_cleanup_module(void);
#endif
static int aim955_init_status = -1;
static int aim956_init_status = -1;
static int reconfig_aim955956_status = -1;
int reset_915(void)
{
int ret = 0;
unsigned char value = 0;
unsigned char addr = 0;
addr = 0x01;
value = 0x01;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_RESET\n");
return ret;
}
msleep(100);
}
int remote_mode(void)
{
int ret = 0;
unsigned char value = 0;
unsigned char addr = 0;
addr = 0x0a;
value = 0x05;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM956 error: 0x%x 0x%x\n", addr, value);
return ret;
}
addr = 0x1c;
value = 0x03;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM956 error: 0x%x 0x%x\n", addr, value);
return ret;
}
}
int reset_mode(int mode)
{
int ret = 0;
unsigned char value = 0;
unsigned char addr = 0;
if (aim956_client == NULL) {
printk("aim956_client is NULL\n");
return -EINVAL;
}
//gpio1 RESET
if (mode == resetin) {
addr = 0x1D;
value = 0x53;
printk("reset set input\n");
} else if (mode == resetoutlow) {
addr = 0x1D;
value = 0x01;
printk("reset set ouput\n");
}else if (mode == resetouthigh) {
addr = 0x1D;
value = 0x09;
printk("reset set ouput\n");
} else if(mode == intin){//INT input gpio0
addr = 0x1c;
value = 0x03;
printk("INT set input\n");
}else if(mode == intouthigh){// INT output
addr = 0x1c;
value = 0x09;
printk("INT set input1\n");
}else if(mode == intoutlow){// INT output
addr = 0x1c;
value = 0x01;
printk("INT set input1\n");
}
ret = i2c_smbus_write_byte_data(aim956_client, addr, value);
if (ret < 0) {
printk("I2C_AIM956 error: addr=0x%x value=0x%x ret=%d\n", addr, value, ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL(reset_mode);
EXPORT_SYMBOL(remote_mode);
//#define TIME_DELAY 100
int configure_aim956955(void)
{
int ret = 0;
unsigned char value = 0;
unsigned char addr = 0;
//第二步 40bit
addr = 0x42;
value = 0x12;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM956 error: 0x%x 0x%x\n", addr, value);
return ret;
}
addr = 0x6c;
value = 0x12;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM956 error: 0x%x 0x%x\n", addr, value);
return ret;
}
msleep(30);
//#第 3 步:955 外设 I2C 地址映射
//TP i2caddr,0x14 * 2 = 0x28
addr = 0x07;
value = 0x28;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
addr = 0x08;
value = 0x28;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
//light
addr = 0x51;
value = 0x52;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
addr = 0x58;
value = 0x52;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
//
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
//printk("step3\n");
//第四步 916X Rx/Tx 相关寄存器
addr = 0x20;
value = 0x03;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x48;
value = 0xFF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x67;
value = 0x37;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x49;
value = 0x3F;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x5A;
value = 0xF0;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x28;
value = 0x04;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x49;
value = 0x3A;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x49;
value = 0x3E;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x49;
value = 0x3F;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x5A;
value = 0xF4;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x40;
value = 0xF8;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x28;
value = 0x00;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x40;
value = 0xF0;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x67;
value = 0x33;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
//printk ("step 4\n");
//第 5 步:配置 915 的 GPIO[3:0]、D_GPIO[3:0]、GPIO[8:5]_reg 至 956 相应 GPIO 的透传
//GPIO2 PWM
addr = 0x0B;
value = 0x30;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
addr = 0x1D;
value = 0x50;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
//printk ("step 5\n");
//#第 6 步:配置 915 端接收 916 端输入的中断
addr = 0x7F;
value = 0x21;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
//#第 7 步:955 完成初始化
addr = 0x03;
value = 0x1A;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x44;
value = 0x16;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM956 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x6F;
value = 0x80;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0x02;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0x03;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x6F;
value = 0x00;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x25;
value = 0x07;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x26;
value = 0x00;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x6A;
value = 0xC9;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0x02;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0x03;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0x02;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0x03;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x26;
value = 0x04;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x6A;
value = 0x01;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x23;
value = 0x37;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x25;
value = 0x0F;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x26;
value = 0x00;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x6F;
value = 0x80;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x71;
value = 0xBF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xBF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x71;
value = 0xFF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
//
addr = 0x72;
value = 0xFF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFE;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFE;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFE;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFE;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFE;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFE;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFE;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFE;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFE;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x72;
value = 0xFF;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
//
addr = 0x26;
value = 0x04;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x6A;
value = 0x09;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x6F;
value = 0x00;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x6A;
value = 0x01;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
addr = 0x23;
value = 0x33;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
#ifdef TIME_DELAY
msleep(TIME_DELAY);
#endif
//iic直通模式
/*
addr = 0x03;
value = 0x08;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
//addr = 0x0d;
//value = 0x9e;
//if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
// printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
// return ret;
// }
//add 916X
addr = 0x03;
value = 0x09;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
addr = 0x04;
value = 0x80;
if((ret = i2c_smbus_write_byte_data(aim956_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
*/
printk("AIM955 AIM956 single lvds 20221018 Setup success.\n");
return ret;
}
int configure_aim955(void)
{
int ret = 0;
unsigned char value = 0;
unsigned char addr = 0;
reset_915();//初始化失败,reset
msleep(100);
#if 0
//第 0 步骤: 初始化 955 之前,先重启设备
addr = 0x01;
value = 0x02;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 reset before kernel driver init: 0x%x 0x%x, ret: %d\n", addr, value, ret);
//return ret;
}
//955 重启过后, 延迟100ms.
msleep(100);
#endif
//第 1 步:初始化 915 Rx/Tx 相关寄存器
addr = 0x6A;
value = 0x01;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
msleep(1);
addr = 0x66;
value = 0x02;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
msleep(1);
/*DES ID*/
addr = 0xAD;
value = 0x02;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
msleep(1);
addr = 0x06;
value = 0x59;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
msleep(1);
addr = 0x2F;
value = 0x05;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
msleep(1);
addr = 0x2A;
value = 0x30;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
msleep(1);
addr = 0x2C;
value = 0x01;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
msleep(1);
addr = 0x33;
value = 0x35;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
msleep(1);
addr = 0x32;
value = 0xA2;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
msleep(1);
addr = 0x34;
value = 0x00;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
msleep(1);
addr = 0xA3;
value = 0x50;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
msleep(1);
addr = 0x03;
value = 0x3A;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 error: 0x%x 0x%x\n", addr, value);
return ret;
}
msleep(1);
return ret;
}
static unsigned long is_cabled = 0;
static ssize_t aim955956_is_link_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
printk("aim955956_is_link_show ...\n");
u8 detect_reg = 0x07;
//u8 detect_reg_data = 0;
int data = i2c_smbus_read_byte_data(aim956_client, detect_reg);
printk("aim - 955 - 956 is_cabled: 0x%x, invert: 0x%x\n", data, (data & 0xff));
if((data & 0xff) == 0x19){
is_cabled = 1;
}else{
is_cabled = 0;
}
return sprintf(buf, "%d\n", is_cabled);
}
static ssize_t aim955_probe_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", aim955_init_status);
}
static ssize_t aim956_probe_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", aim956_init_status);
}
#ifdef DYNAMIC_DETECT_AIM955956
static ssize_t aim956_reconfig_num_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", reconfig_aim955956_status);
}
#endif
static DEVICE_ATTR(is_linked, 0664, aim955956_is_link_show,NULL);
static DEVICE_ATTR(955_probe, 0664, aim955_probe_status_show,NULL);
static DEVICE_ATTR(956_probe, 0664, aim956_probe_status_show,NULL);
#ifdef DYNAMIC_DETECT_AIM955956
static DEVICE_ATTR(aim955956_reconfig_num, 0664, aim956_reconfig_num_show,NULL);
#endif
static struct attribute *aim955956_attributes[] = {
&dev_attr_is_linked.attr,
&dev_attr_955_probe.attr,
&dev_attr_956_probe.attr,
#ifdef DYNAMIC_DETECT_AIM955956
&dev_attr_aim955956_reconfig_num.attr,
#endif
NULL
};
static const struct attribute_group aim955956_attr_group = {
.attrs = aim955956_attributes,
};
static int __init aim956_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
aim956_client = client;
printk("aim956_probe ... \n");
ret = configure_aim956955();
aim956_init_status = ret;
printk("aim956_probe ... ..., ret = %d\n", ret);
#ifdef DYNAMIC_DETECT_AIM955956
glink_detect_kt = kthread_create(link_detect_kernel_thread, (void*)aim956_client , "glink_detect_kt");
if(!IS_ERR(glink_detect_kt))
wake_up_process(glink_detect_kt);
else {
printk("create glink_detect_kt watchdog thread failed!!\n");
glink_detect_kt = 0;
}
#endif
return ret;
}
static int aim956_remove(struct i2c_client *client)
{
#ifdef DYNAMIC_DETECT_AIM955956
link_detect_kernel_thread_cleanup_module();
#endif
return 0;
}
static const struct i2c_device_id aim956_id[] = {
{ "aim956", 0 },
{ }
};
static const struct of_device_id aim956_match_table[] = {
{.compatible = "aimx916",},
{ },
};
static struct i2c_driver aim956_driver = {
.driver = {
.name = "aim956",
.owner = THIS_MODULE,
.of_match_table = aim956_match_table,
},
.probe = aim956_probe,
.remove = aim956_remove,
.id_table = aim956_id,
};
#ifdef DYNAMIC_DETECT_AIM955956
int reconfigure_aim955956(void)
{
int ret = 0;
unsigned char value = 0;
unsigned char addr = 0;
#if 1
//第 0 步骤: 初始化 955 之前,先重启设备
addr = 0x01;
value = 0x01;
if((ret = i2c_smbus_write_byte_data(aim955_client, addr, value)) < 0){
printk("I2C_AIM955 reset before kernel driver init: 0x%x 0x%x, ret: %d\n", addr, value, ret);
//return ret;
}
//955 重启过后, 延迟100ms.
msleep(100);
#endif
reconfig_aim955956_status++;
ret = configure_aim955();
msleep(10);
if (ret < 0)
return ret;
else
ret = configure_aim956955();
return ret;
}
bool isLinkDetected(struct i2c_client *client)
{
u8 detect_reg = 0x67;
int data = i2c_smbus_read_byte_data(aim956_client, detect_reg);
//printk("isLinkDetected aim - 955 - 956 is_cabled: 0x%x, invert: 0x%x\n", data, (data & 0xff));
if((data & 0xff) == 0x33){
return true;
}else{
return false;
}
}
static void link_detect_kernel_thread_cleanup_module(void)
{
if(glink_detect_kt){
kthread_stop(glink_detect_kt);
glink_detect_kt = NULL;
}
}
static int link_detect_kernel_thread(void *data)
{
struct i2c_client *client = (struct i2c_client *)data;
while(!kthread_should_stop())
{
if(isLinkDetected(client) == true) {
// do nothing.
} else {
printk("----------------link_detect_kernel_thread call reconfigure_aim955956()-------------- \n");
reconfig:
if(reconfigure_aim955956() < 0){
schedule_timeout_interruptible(msecs_to_jiffies(500));
goto reconfig;
}
}
schedule_timeout_interruptible(msecs_to_jiffies(1000));
}
}
#endif
static int __init aim955_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
aim955_client = client;
printk("aim955_probe ... \n");
ret = configure_aim955();
aim955_init_status = ret;
printk("aim955_probe ... ..., ret = %d\n", ret);
sysfs_create_group(&aim955_client->dev.kobj,&aim955956_attr_group);
return ret;
}
static int aim955_remove(struct i2c_client *client)
{
sysfs_remove_group(&aim955_client->dev.kobj,&aim955956_attr_group);
return 0;
}
static const struct i2c_device_id aim955_id[] = {
{ "aim955", 0 },
{ }
};
static const struct of_device_id aim955_match_table[] = {
{.compatible = "aimx915",},
{ },
};
static struct i2c_driver aim955_driver = {
.driver = {
.name = "aim955",
.owner = THIS_MODULE,
.of_match_table = aim955_match_table,
},
.probe = aim955_probe,
.remove = aim955_remove,
.id_table = aim955_id,
};
static int __init aim955956_init(void)
{
int ret = 0;
printk("aim955956_init\n");
ret = i2c_add_driver(&aim955_driver);
ret = i2c_add_driver(&aim956_driver);
return ret;
}
static void __exit aim955956_exit(void)
{
printk("aim955956_exit\n");
i2c_del_driver(&aim955_driver);
i2c_del_driver(&aim956_driver);
}
MODULE_AUTHOR("xth");
MODULE_DESCRIPTION("aim955-956-xxx Driver");
MODULE_LICENSE("GPL");
module_init(aim955956_init);
//subsys_initcall(aim955956_init);
module_exit(aim955956_exit);
驱动写的比较简单,大家可自行完善
四、测试
问题1:
写入寄存器测试后,出现图像偏移的情况
原因: 屏配置了 SYNC 模式, RGB芯片输出 DE 模式
解决方案:拆掉R103电阻,使DE悬空后显示正常
问题2:驱动寄存器初始化会失败的情况
915有对应的复位寄存器,在每次初始化915X寄存器之前进行复位,重新写入
添加915复位代码:
I2C重试机制:
修改后反复测试20次未出现I2C初始化失败的情况
热插拔检测:
-
isLinkDetected
函数:通过I2C读取设备的特定寄存器(0x67
),检查返回的数据是否为0x33
。如果是,返回true
,表示检测到连接;否则返回false
。 -
link_detect_kernel_thread_cleanup_module
函数:如果全局线程glink_detect_kt
存在,则停止该线程并将其指针置为NULL
,用于清理工作。 -
link_detect_kernel_thread
函数:这是一个内核线程,循环检查链接状态。如果检测到链接,则什么都不做;如果未检测到链接,则调用reconfigure_aim955956
函数进行重新配置。