车载项目中串行/解串器是十分常见的外设,目前常用的有两种标准:GMSL(美信家的)、FPD-Link(TI家的)。本次基于美信的一对Ser/Deser实现:串行端将HDMI源视频信号转为GMSL2串行信号,解串端解串后转为LVDS格式连接显示屏,并在屏幕上显示。
SOC:Amlogic A311D2
SDK:Amlogic Android 11
Kernel:5.4
Serializer:MAX96763
Deserializer:MAX96752F
GPIO17/I2CSEL:low(Main Uart mode)
GPIO16/CXTP:low(TP双绞线)
ADD0~ADD2:000(Dev Addr=0x80)
确保:
HPD:high(即SOC的HDMI TX与MAX96763连接上)
PWDNB:high(MAX96763 ready)
GPIO01/I2CSEL:low(Main Uart mode)
GPIO09/CXTP:low(TP双绞线)
ADD0~ADD2:010(Dev Addr=0x98)
确保:
PWDNB:high(MAX96752F ready)
START(1bit) : 固定填0
DATA(8bit) : 8位数据
EVEN PARITY(1bit) :1位偶校验,1表示DATA的8位中1的个数为奇数;0表示DATA的8位中1的个数为偶数。
STOP(至少1bit,最多4bit): 固定填1
START D0 D1 D2 D3 D4 D5 D6 D7 PARITY STOP
0 1 0 0 1 1 1 1 0 1 1 // 0x79
START D0 D1 D2 D3 D4 D5 D6 D7 PARITY STOP
0 1 1 0 0 0 0 1 1 0 1 // 0xC3
写操作(地址帧的bit0置0):
SYNC帧 | DEV ADDR帧(LSB=0) | REG ADDR(MSB) | REG ADDR(LSB) | BYTE COUNT | DATA1 | ········ | DATA N
读操作(地址帧的bit0置1):
SYNC帧 | DEV ADDR帧(LSB=1) | REG ADDR(MSB) | REG ADDR(LSB) | BYTE COUNT
示例:
读MAX96763的DEV ID,即读0x000D寄存器(应返回:C3 B2):
79 81 00 0D 01
读MAX96752F的DEV ID,即读0x000D寄存器(应返回:C3 82):
79 99 00 0D 01
// SPDX-License-Identifier: GPL-2.0
/*
* Author: rentong [email protected]
* Description:This module configs the Maxin serializer and deserializer during kernel startup.
* Version: 1.0 2022/3/26
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX96763_DEV_ADDR 0x80
#define MAX96752_DEV_ADDR 0x98
#define MAX96763_DEV_ID 0xB2
#define MAX96752_DEV_ID 0x82
#define TTY_NAME "/dev/ttyS3"
#define TTY_SPEED 921600
#define TRY_TIMES 10
#define SCHE_TIMEOUT msecs_to_jiffies(1000)
static struct task_struct *server_thread;
static int max96763_main_uart_set_termios(struct file *filp)
{
struct tty_file_private *priv = NULL;
struct tty_struct *tty = NULL;
priv = filp->private_data;
if(IS_ERR(priv)) {
pr_err("%s %d priv is NULL\n", __func__, __LINE__);
return -1;
}
else {
tty = priv->tty;
}
if(IS_ERR(tty)) {
pr_err("%s %d tty is NULL\n", __func__, __LINE__);
return -1;
}
else {
struct ktermios ktermios = tty->termios;
ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
ktermios.c_oflag &= ~OPOST;
ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
ktermios.c_cflag |= CLOCAL;
ktermios.c_cflag &= ~CBAUD; /* clear baud */
tty_termios_encode_baud_rate(&ktermios, TTY_SPEED, TTY_SPEED); /* set baud rate */
ktermios.c_cflag &= ~CSIZE; /* clear bit size */
ktermios.c_cflag |= CS8; /* set bit size */
ktermios.c_cflag &= ~(PARENB | PARODD | CMSPAR); /* clear parity enable and odd parity */
ktermios.c_cflag |= PARENB; /* enable even parity */
ktermios.c_cflag &= ~CSTOPB; /* set stop bit with one bit */
ktermios.c_cflag &= ~CRTSCTS; /* disable hardware flow control */
tty_set_termios(tty, &ktermios);
}
pr_info("%s %d ok\n", __func__, __LINE__);
return 0;
}
static void max96763_main_uart_config_reg(struct file *filp)
{
// for 96752
unsigned char cmd_1[6]={ 0x79, MAX96752_DEV_ADDR, 0x00, 0x01, 0x01, 0x02 };
unsigned char cmd_2[6]={ 0x79, MAX96752_DEV_ADDR, 0x01, 0xCE, 0x01, 0x5E };
unsigned char cmd_3[6]={ 0x79, MAX96752_DEV_ADDR, 0x00, 0x02, 0x01, 0x43 };
unsigned char cmd_4[6]={ 0x79, MAX96752_DEV_ADDR, 0x02, 0x06, 0x01, 0x83 };
unsigned char cmd_5[6]={ 0x79, MAX96752_DEV_ADDR, 0x02, 0x07, 0x01, 0x27 };
// for 96763
unsigned char cmd_6[6]={ 0x79, MAX96763_DEV_ADDR, 0x00, 0x01, 0x01, 0x88 };
unsigned char cmd_206[6]={ 0x79, MAX96763_DEV_ADDR, 0x02, 0x06, 0x01, 0x84 };
unsigned char cmd_207[6]={ 0x79, MAX96763_DEV_ADDR, 0x02, 0x07, 0x01, 0x20 };
unsigned char cmd_208[6]={ 0x79, MAX96763_DEV_ADDR, 0x02, 0x08, 0x01, 0x87 };
unsigned char cmd_7[6]={ 0x79, MAX96763_DEV_ADDR, 0x00, 0x10, 0x01, 0x31 };
// read reg 0x000d to get dev id
unsigned char cmd_a[6]={ 0x79, MAX96763_DEV_ADDR|0x01, 0x00, 0x0D, 0x01 };
unsigned char cmd_b[6]={ 0x79, MAX96752_DEV_ADDR|0x01, 0x00, 0x0D, 0x01 };
// RFDM contorl
unsigned char cmd_c[13]={ 0x79, 0x6c, 0x00, 0x02, 0x08, 0x09, 0x00, 0x01, 0x01, 0x53, 0xE1, 0x70, 0x43 }; // SOC ready
//unsigned char cmd_d[13]={ 0x79, 0x6c, 0x00, 0x00, 0x08, 0x09, 0x00, 0x03, 0x03, 0xC2, 0x11, 0xD2, 0xE6 }; // 105° angle
unsigned char cmd_e[13]={ 0x79, 0x6c, 0x00, 0x00, 0x08, 0x09, 0x00, 0x03, 0x01, 0x2C, 0x1F, 0xB3, 0xCA }; // 95° angle
unsigned char cmd_f[13]={ 0x79, 0x6c, 0x00, 0x00, 0x08, 0x01, 0x00, 0x01, 0x00, 0xAC, 0x9A, 0xC9, 0x31 }; // power on
loff_t pos = 0;
unsigned int size = 0;
mm_segment_t old_fs = get_fs();
//char buf[2] = {0, 0};
//unsigned int buf_len = 2;
set_fs(KERNEL_DS);
/**** check 96763's dev id ****/
size = vfs_write(filp, cmd_a, sizeof(cmd_a), &pos);
pr_info("[%s] write cmd_a %d bytes to file %s\n", __func__, size, TTY_NAME);
#if 0
size = vfs_read(filp, buf, buf_len, &pos);
pr_info("[%s] read %d bytes from file %s, buf = %X %x\n", __func__, size, TTY_NAME, buf[0], buf[1]);
if(buf[0]==0xC3 && buf[1]==MAX96763_DEV_ID)
{
pr_info("[%s] MAX96763 found!\n", __func__);
}else {
pr_err("[%s] MAX96763 not found!\n", __func__);
goto PROCESS_END;
}
memset(buf, 0, buf_len);
#endif
/**** check 96752's dev id ****/
size = vfs_write(filp, cmd_b, sizeof(cmd_b), &pos);
pr_info("[%s] write cmd_b %d bytes to file %s\n", __func__, size, TTY_NAME);
#if 0
size = vfs_read(filp, buf, buf_len, &pos);
pr_info("[%s] read %d bytes from file %s, buf = %X %x\n", __func__, size, TTY_NAME, buf[0], buf[1]);
if(buf[0]==0xC3 && buf[1]==MAX96752_DEV_ID)
{
pr_info("[%s] MAX96752 found!\n", __func__);
}else {
pr_err("[%s] MAX96752 not found!\n", __func__);
goto PROCESS_END;
}
#endif
/**** config 96752 reg ****/
size = vfs_write(filp, cmd_1, sizeof(cmd_1), &pos);
pr_info("[%s] write cmd_1 %d bytes to file %s\n", __func__, size, TTY_NAME);
size = vfs_write(filp, cmd_2, sizeof(cmd_2), &pos);
pr_info("[%s] write cmd_2 %d bytes to file %s\n", __func__, size, TTY_NAME);
size = vfs_write(filp, cmd_3, sizeof(cmd_3), &pos);
pr_info("[%s] write cmd_3 %d bytes to file %s\n", __func__, size, TTY_NAME);
size = vfs_write(filp, cmd_4, sizeof(cmd_4), &pos);
pr_info("[%s] write cmd_4 %d bytes to file %s\n", __func__, size, TTY_NAME);
size = vfs_write(filp, cmd_5, sizeof(cmd_5), &pos);
pr_info("[%s] write cmd_5 %d bytes to file %s\n", __func__, size, TTY_NAME);
/**** config 96763 reg ****/
size = vfs_write(filp, cmd_6, sizeof(cmd_6), &pos);
pr_info("[%s] write cmd_6 %d bytes to file %s\n", __func__, size, TTY_NAME);
size = vfs_write(filp, cmd_206, sizeof(cmd_206), &pos);
pr_info("[%s] write cmd_206 %d bytes to file %s\n", __func__, size, TTY_NAME);
size = vfs_write(filp, cmd_207, sizeof(cmd_207), &pos);
pr_info("[%s] write cmd_207 %d bytes to file %s\n", __func__, size, TTY_NAME);
size = vfs_write(filp, cmd_208, sizeof(cmd_208), &pos);
pr_info("[%s] write cmd_208 %d bytes to file %s\n", __func__, size, TTY_NAME);
size = vfs_write(filp, cmd_7, sizeof(cmd_7), &pos);
pr_info("[%s] write cmd_7 %d bytes to file %s\n", __func__, size, TTY_NAME);
/**** control RFDM screen through main uart ****/
msleep(10);
size = vfs_write(filp, cmd_c, sizeof(cmd_c), &pos);
pr_info("[%s] write cmd_c %d bytes to file %s\n", __func__, size, TTY_NAME);
msleep(10);
size = vfs_write(filp, cmd_e, sizeof(cmd_e), &pos);
pr_info("[%s] write cmd_e %d bytes to file %s\n", __func__, size, TTY_NAME);
msleep(10);
size = vfs_write(filp, cmd_f, sizeof(cmd_f), &pos);
pr_info("[%s] write cmd_f %d bytes to file %s\n", __func__, size, TTY_NAME);
pr_info("[%s] -------------------- OK!\n", __func__);
set_fs(old_fs);
return;
}
static int max96763_main_uart_config_thread(void *unused)
{
struct file *filp = NULL;
int try = TRY_TIMES;
while (!kthread_should_stop() && try--) {
filp = filp_open(TTY_NAME, O_RDWR|O_NOCTTY, 0);
if (IS_ERR(filp)) {
pr_err("%s: cannot open %s\n", __func__, TTY_NAME);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(SCHE_TIMEOUT);
}else {
pr_info("%s: open %s success!\n", __func__, TTY_NAME);
if(max96763_main_uart_set_termios(filp)==0) {
max96763_main_uart_config_reg(filp);
} else {
pr_err("%s: set %s termios failed!\n", __func__, TTY_NAME);
}
filp_close(filp, NULL);
break;
}
}
pr_info("%s %d exit, try %d\n", __func__, __LINE__, try);
return 0;
}
static int __init max96763_main_uart_init(void)
{
int rc;
server_thread = kthread_run(max96763_main_uart_config_thread, NULL, "max96763_config");
if (IS_ERR(server_thread)) {
rc = PTR_ERR(server_thread);
pr_err("%s %d kthread_run rc %d\n", __func__, __LINE__, rc);
return -1;
}
pr_info("%s %d ok\n", __func__, __LINE__);
return 0;
}
static void __exit max96763_main_uart_exit(void)
{
pr_info("%s %d\n", __func__, __LINE__);
}
module_init(max96763_main_uart_init);
module_exit(max96763_main_uart_exit);
MODULE_AUTHOR("rentong " );
MODULE_DESCRIPTION("This module is used to config MAXIN chips through serial port");
MODULE_LICENSE("GPL");