屏幕适配
ypt153evb 支持两种屏幕显示规格,rgb 和 dsi 转 hdmi。
1. RGB 屏幕适配
这里以我们适配的 1024*600 的 RGB 屏幕为例进行介绍。
硬件原理图
1.1 uboot 设备树修改
相关设备树路径
brandy/brandy-2.0/u-boot-bsp/arch/arm/dts/sun8iw22p1.dtsi // 官方基础型号配置
device/config/chips/t153/configs/t153_nand_base/uboot-board-base.dtsi // 基础板载配置
BoardConfig/uboot_dts/ypt153evb-board.dts // ypt153evb 对应板载配置
// 对照原理图复用 rgb0_引脚
rgb0_18_pins_a: rgb0@0 {
pins = "PD0", "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD9", \
"PD10", "PD11", "PD12", "PD13", "PD14", "PD15", "PD16", "PD17", "PD18", "PD19", \
"PD20", "PD21";
function = "lcd";
drive-strength = <20>;
};
rgb0_18_pins_b: rgb0@1 {
pins = "PD0", "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD9", \
"PD10", "PD11", "PD12", "PD13", "PD14", "PD15", "PD16", "PD17", "PD18", "PD19", \
"PD20", "PD21";
function = "gpio_in";
};
// 配置 rgb0
&rgb0 {
status = "okay";
pinctrl-0 = <&rgb0_18_pins_a>;
pinctrl-1 = <&rgb0_18_pins_b>;
pinctrl-names = "active","sleep";
ports {
port@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
rgb0_panel_out: endpoint@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
remote-endpoint = <&rgb0_panel_in>;
};
rgb1_panel_out: endpoint@1 {
};
};
};
};
// 开启 route_rgb0
&route_rgb0 {
status = "okay";
logo,uboot = "bootlogo.bmp";
};
// 其他 route 需要关闭
&route_lvds0 {
status = "disabled";
};
&route_dsi0 {
status = "disabled";
};
// rgb_panel0 面板配置
rgb_panel0: rgb_panel@1 {
compatible = "sunxi-rgb";
status = "okay";
backlight = <&backlight0>;
display-timings {
native-mode = <&rgb0_timing0>;
rgb0_timing0: timing0 {
clock-frequency = <61470000>; /* pixel clock */
hactive = <1024>;
hback-porch = <90>;
hfront-porch = <182>;
hsync-len = <70>;
vactive = <600>;
vback-porch = <3>;
vfront-porch = <127>;
vsync-len = <20>;
};
};
port {
rgb0_panel_in: endpoint {
remote-endpoint = <&rgb0_panel_out>;
};
};
};参考屏幕手册,在设备树的 rgb_timing0 节点中写入如下参数:
| 设备树参数 | 含义(DRM标准定义) | 当前值 |
|---|---|---|
| clock-frequency | 像素时钟(DCLK)频率 | 61470000 |
| hactive | 水平有效显示区域(分辨率宽) | 1024 |
| hfront-porch | 有效数据结束到 HSYNC 开始之间的空闲周期 | 182 |
| hback-porch | HSYNC 结束到有效数据开始之间的空闲周期 | 90 |
| hsync-len | HSYNC 脉冲宽度(低电平或高电平持续周期) | 70 |
| vactive | 垂直有效显示区域(分辨率高) | 600 |
| vback-porch | VSYNC 结束到有效数据开始之间的空闲行数 | 3 |
| vfront-porch | 有效数据结束到 VSYNC 开始之间的空闲行数 | 127 |
| vsync-len | VSYNC 脉冲宽度(行数) | 20 |
1.2 内核设备树修改
除了没有 route_rgb0 基本上和 uboot 相同
/* kenrel rgb config */
/{
rgb_panel0: rgb_panel@1 {
compatible = "sunxi-rgb";
status = "okay";
reg = <0>;
backlight = <&backlight0>;
enable0-gpios = <&pio PD 22 GPIO_ACTIVE_HIGH>;
display-timings {
native-mode = <&rgb0_timing0>;
rgb0_timing0: timing0 {
clock-frequency = <61470000>; /* pixel clock */
hactive = <1024>;
hback-porch = <90>;
hfront-porch = <182>;
hsync-len = <70>;
vactive = <600>;
vback-porch = <3>;
vfront-porch = <127>;
vsync-len = <20>;
};
};
port {
rgb0_panel_in: endpoint {
remote-endpoint = <&rgb0_panel_out>;
};
};
};
};
&rgb0 {
status = "okay";
pinctrl-0 = <&rgb0_18_pins_a>;
pinctrl-1 = <&rgb0_18_pins_b>;
pinctrl-names = "active","sleep";
ports {
port@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
rgb0_panel_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&rgb0_panel_in>;
};
rgb1_panel_out: endpoint@1 {
};
};
};
};
2. HDMI 屏幕适配
ypt153evb 的 HDMI 由 MIPI DSI 经过 LT8912B 芯片转换为 HDMI 输出,并且由 uboot 完成 LT8912C 芯片的初始化 ,kernel 不对该芯片做任何操作,只负责 MIPI DSI 输出。
2.1 uboot 增加 LT8912C 芯片驱动
放置路径 brandy/brandy-2.0/u-boot-bsp/drivers/video/drm/panels/lt8912b.c
/**
* Copyright (C) 2013 Guangzhou Tronlong Electronic Technology Co., Ltd. - www.tronlong.com
*
* @file lt8912b.c
*
* @brief lt8912 init.
*
* @author Tronlong <support@tronlong.com>
*
* @version V1.0
*
* @date 2025-4-2
*
**/
#include <common.h>
#include <dm.h>
#include <dm/devres.h>
#include <dm/device_compat.h>
#include <backlight.h>
#include <power/regulator.h>
#include <asm/gpio.h>
#include <linux/delay.h>
#include <drm/drm_modes.h>
#include "../sunxi_drm_panel.h"
#include "../sunxi_drm_helper_funcs.h"
#include <drm/drm_mipi_dsi.h>
#include "../drm_mipi_dsi.h"
#include <i2c.h>
#include <asm/arch/i2c.h>
/*
* LT8912B
*/
#define LT8912B_I2C_ID 1
#define I2C_BASS_ADDRESS1 0x48
#define I2C_BASS_ADDRESS2 0x49
#define I2C_BASS_ADDRESS3 0x4a
//#define _pattern_test_
#define _1080P_60Hz
/* output mode */
#define _hdmi_output
/* lanes */
/* 0: 4lane; 1: 1lane; 2: 2lane; 3: 3lane */
#define lane_cnt 0
static struct udevice *lt8912b_dev;
struct sunxi_twi {
struct sunxi_twi_reg *reg;
int bus_num;
unsigned int speed;
struct reset_ctl reset;
struct clk clk;
};
static struct sunxi_twi *lt8912b_twi;
struct video_timing{
unsigned short hfp;
unsigned short hs;
unsigned short hbp;
unsigned short hact;
unsigned short htotal;
unsigned short vfp;
unsigned short vs;
unsigned short vbp;
unsigned short vact;
unsigned short vtotal;
unsigned int pclk_khz;
};
typedef enum
{
I2S_2CH,
I2S_8CH,
SPDIF
}
_Audio_Input_Mode;
#define Audio_Input_Mode I2S_2CH
struct panel_parameter
{
unsigned short hfp;
unsigned short hs;
unsigned short hbp;
unsigned short hact;
unsigned short htotal;
unsigned short vfp;
unsigned short vs;
unsigned short vbp;
unsigned short vact;
unsigned short vtotal;
unsigned int pclk_khz;
};
unsigned char I2CADR = I2C_BASS_ADDRESS1;
unsigned char Hsync_H_last = 0x00;
unsigned char Hsync_L_last = 0x00;
unsigned char Vsync_H_last = 0x00;
unsigned char Vsync_L_last = 0x00;
unsigned char Hsync_L, Hsync_H, Vsync_L, Vsync_H;
int suspend_on = 0;
unsigned char Tx_HPD=0;
/*
* this timing is mipi timing, please set these timing paremeter
* same with actual mipi timing(processor's timing)
* hfp, hs, hbp, hact, htotal, vfp, vs, vbp, vact, vtotal, pclk_khz
*/
struct video_timing video_640x480_60Hz = {8, 96, 40, 640, 784, 33, 2, 10, 480, 525};
struct video_timing video_720x480_60Hz = {16, 62, 60, 720, 858, 9, 6, 30, 480, 525};
struct video_timing video_1280x720_60Hz = {110, 40, 220, 1280, 1650, 5, 5, 20, 720, 750};
struct video_timing video_1366x768_60Hz = {14, 56, 64, 1366, 1500, 1, 3, 28, 768, 800};
struct video_timing video_1920x1080_60Hz = {88, 44, 148, 1920, 2200, 4, 5, 36, 1080, 1125};
struct video_timing video_3840x1080_60Hz = {176, 88, 296, 3840, 4400, 4, 5, 36, 1080, 1125};
struct video_timing video_3840x2160_30Hz = {176, 88, 296, 3840, 4400, 8, 10, 72, 2160, 2250};
struct video_timing video_1024x768_60Hz = {24, 136, 160, 1024, 1344, 3, 6, 29, 768, 806, 65000};
struct video_timing video_1280x800_60Hz = {64, 136, 200, 1280, 1680, 1, 3, 24, 800, 828, 74250};
struct video_timing video_800x1280_60Hz = {80, 20, 20, 800, 920, 15, 6, 8, 1280, 1309, 67200};
/*
* panel timing
* this timing is used for scaler output for LVDS,
* HDMI output and lvds bypass mode will not use this timing.
* hfp, hs, hbp, hact, htotal, vfp, vs, vbp, vact, vtotal, pclk_khz
*/
struct video_timing video_1024x600_60Hz = {50, 20, 50, 1024, 1144, 9, 3, 13, 600, 625, 42500};
struct video_timing video_lvds_60Hz = {24, 136, 160, 1024, 1344, 3, 6, 29, 768, 806, 65000};
static int i2c_dev_select(unsigned char chip_addr)
{
struct udevice *bus;
int ret;
ret = uclass_get_device_by_seq(UCLASS_I2C, LT8912B_I2C_ID , &bus);
if (ret) {
printf("Failed to get I2C bus\n");
return ret;
}
ret = i2c_get_chip(bus, chip_addr, 1, <8912b_dev);
if (ret) {
printf("Failed to get I2C chip at 0x%02x\n", chip_addr);
return ret;
}
return 0;
}
static int i2c_dev_write(unsigned char addr, unsigned char data)
{
int ret;
ret = i2c_dev_select(I2CADR);
if (ret){
return ret;
}
ret = dm_i2c_write(lt8912b_dev, addr, &data, 1);
return ret;
}
static int i2c_dev_read(unsigned char addr, unsigned char* data)
{
int ret = 0;
ret = i2c_dev_select(I2CADR);
if (ret){
return ret;
}
ret = dm_i2c_read(lt8912b_dev, addr, data, 1);
return ret;
}
static int HDMI_WriteI2C_Byte(unsigned char addr, unsigned char data)
{
int flag;
flag = i2c_dev_write(addr, data);
mdelay(1);
return flag;
}
static unsigned char HDMI_ReadI2C_Byte(unsigned char addr)
{
unsigned char p_data = 0;
if(i2c_dev_read(addr, &p_data) == 0)
return p_data;
return 0;
}
void Timer0_Delay1ms(int t)
{
int tem = t;
mdelay(tem);
}
void DigitalClockEn(void)
{
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte(0x02,0xf7);
HDMI_WriteI2C_Byte(0x08,0xff);
HDMI_WriteI2C_Byte(0x09,0xff);
HDMI_WriteI2C_Byte(0x0a,0xff);
HDMI_WriteI2C_Byte(0x0b,0x7c);
HDMI_WriteI2C_Byte(0x0c,0xff);
}
void TxAnalog(void)
{
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte(0x31,0xE1);
HDMI_WriteI2C_Byte(0x32,0xE1);
/* en/disable hdmid output */
HDMI_WriteI2C_Byte(0x33,0x0c);
HDMI_WriteI2C_Byte(0x37,0x00);
HDMI_WriteI2C_Byte(0x38,0x22);
HDMI_WriteI2C_Byte(0x60,0x82);
}
void CbusAnalog(void)
{
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte(0x39,0x45);
/* 20180719 */
HDMI_WriteI2C_Byte(0x3a,0x00);
HDMI_WriteI2C_Byte(0x3b,0x00);
}
void HDMIPllAnalog(void)
{
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte(0x44,0x31);
HDMI_WriteI2C_Byte(0x55,0x44);
HDMI_WriteI2C_Byte(0x57,0x01);
HDMI_WriteI2C_Byte(0x5a,0x02);
}
void AviInfoframe(void)
{
I2CADR = I2C_BASS_ADDRESS3;
/* enable null package */
HDMI_WriteI2C_Byte(0x3c,0x41);
/* defualt AVI */
I2CADR = I2C_BASS_ADDRESS1;
/* sync polarity + */
HDMI_WriteI2C_Byte(0xab,0x03);
I2CADR = I2C_BASS_ADDRESS3;
/* PB0:check sum */
HDMI_WriteI2C_Byte(0x43,0x27);
/* PB1 */
HDMI_WriteI2C_Byte(0x44,0x10);
/* PB2 */
HDMI_WriteI2C_Byte(0x45,0x28);
/* PB3 */
HDMI_WriteI2C_Byte(0x46,0x00);
/* PB4;vic */
HDMI_WriteI2C_Byte(0x47,0x10);
#ifdef _1080P_60Hz
/* 1080P60Hz 16:9 */
I2CADR = I2C_BASS_ADDRESS1;
/* sync polarity + */
HDMI_WriteI2C_Byte(0xab,0x03);
I2CADR = I2C_BASS_ADDRESS3;
/* PB0:check sum */
HDMI_WriteI2C_Byte(0x43,0x27);
/* PB1 */
HDMI_WriteI2C_Byte(0x44,0x10);
/* PB2 */
HDMI_WriteI2C_Byte(0x45,0x28);
/* PB3 */
HDMI_WriteI2C_Byte(0x46,0x00);
/* PB4:vic */
HDMI_WriteI2C_Byte(0x47,0x10);
#endif
#ifdef _720P_60Hz
/* 720P60Hz 16:9 */
I2CADR = I2C_BASS_ADDRESS1;
/* sync polarity + */
HDMI_WriteI2C_Byte(0xab,0x03);
I2CADR = I2C_BASS_ADDRESS3;
/* PB0:check sum */
HDMI_WriteI2C_Byte(0x43,0x33);
/* PB1 */
HDMI_WriteI2C_Byte(0x44,0x10);
/* PB2 */
HDMI_WriteI2C_Byte(0x45,0x28);
/* PB3 */
HDMI_WriteI2C_Byte(0x46,0x00);
/* PB4:vic */
HDMI_WriteI2C_Byte(0x47,0x04);
#endif
#ifdef _480P_60Hz
/* 720x480 60Hz 4:3 */
I2CADR = I2C_BASS_ADDRESS1;
/* sync polarity + */
HDMI_WriteI2C_Byte(0xab,0x0c);
I2CADR = I2C_BASS_ADDRESS3;
/* PB0:check sum */
HDMI_WriteI2C_Byte(0x43,0x45);
/* PB1 */
HDMI_WriteI2C_Byte(0x44,0x10);
/* PB2 */
HDMI_WriteI2C_Byte(0x45,0x18);
/* PB3 */
HDMI_WriteI2C_Byte(0x46,0x00);
/* PB4:vic */
HDMI_WriteI2C_Byte(0x47,0x02);
#endif
}
void MipiAnalog(void)
{
I2CADR = I2C_BASS_ADDRESS1;
#ifdef _pn_swap_
/* P/N swap */
HDMI_WriteI2C_Byte(0x3e,0xf6);
#else
/*
* if mipi pin map follow reference design,
* no need swap P/N.
*/
HDMI_WriteI2C_Byte(0x3e,0xd6);
#endif
/* EQ */
HDMI_WriteI2C_Byte(0x3f,0xd4);
/* EQ */
HDMI_WriteI2C_Byte(0x41,0x3c);
}
void MipiBasicSet(void)
{
I2CADR = I2C_BASS_ADDRESS2;
/* term en */
HDMI_WriteI2C_Byte(0x10,0x01);
/* settle */
HDMI_WriteI2C_Byte(0x11,0x5);
/*
* 00 4 lane
* 01 1 lane
* 02 2 lane
* 03 3 lane
*/
HDMI_WriteI2C_Byte(0x13,lane_cnt);
/* debug mux */
HDMI_WriteI2C_Byte(0x14,0x00);
#ifdef _lane_swap_
/*
* for EVB only,
* if mipi pin map follow reference design,
* no need swap lane.
*/
/* lane swap:3210 */
HDMI_WriteI2C_Byte(0x15,0xa8);
#else
/* lane swap:0123 */
HDMI_WriteI2C_Byte(0x15,0x00);
#endif
/* hshift 3 */
HDMI_WriteI2C_Byte(0x1a,0x03);
/* vshift 3 */
HDMI_WriteI2C_Byte(0x1b,0x03);
}
void MIPI_Video_Setup(struct video_timing *video_format)
{
I2CADR = I2C_BASS_ADDRESS2;
/* hwidth */
HDMI_WriteI2C_Byte(0x18,(unsigned char)(video_format->hs%256));
/* vwidth 6 */
HDMI_WriteI2C_Byte(0x19,(unsigned char)(video_format->vs%256));
/* H_active[7:0] */
HDMI_WriteI2C_Byte(0x1c,(unsigned char)(video_format->hact%256));
/* H_active[15:8] */
HDMI_WriteI2C_Byte(0x1d,(unsigned char)(video_format->hact/256));
/* fifo_buff_length 12 */
HDMI_WriteI2C_Byte(0x2f,0x0c);
/* H_total[7:0] */
HDMI_WriteI2C_Byte(0x34,(unsigned char)(video_format->htotal%256));
/* H_total[15:8] */
HDMI_WriteI2C_Byte(0x35,(unsigned char)(video_format->htotal/256));
/* V_total[7:0] */
HDMI_WriteI2C_Byte(0x36,(unsigned char)(video_format->vtotal%256));
/* V_total[15:8] */
HDMI_WriteI2C_Byte(0x37,(unsigned char)(video_format->vtotal/256));
/* VBP[7:0] */
HDMI_WriteI2C_Byte(0x38,(unsigned char)(video_format->vbp%256));
/* VBP[15:8] */
HDMI_WriteI2C_Byte(0x39,(unsigned char)(video_format->vbp/256));
/* VFP[7:0] */
HDMI_WriteI2C_Byte(0x3a,(unsigned char)(video_format->vfp%256));
/* VFP[15:8] */
HDMI_WriteI2C_Byte(0x3b,(unsigned char)(video_format->vfp/256));
/* HBP[7:0] */
HDMI_WriteI2C_Byte(0x3c,(unsigned char)(video_format->hbp%256));
/* HBP[15:8] */
HDMI_WriteI2C_Byte(0x3d,(unsigned char)(video_format->hbp/256));
/* HFP[7:0] */
HDMI_WriteI2C_Byte(0x3e,(unsigned char)(video_format->hfp%256));
/* HFP[15:8] */
HDMI_WriteI2C_Byte(0x3f,(unsigned char)(video_format->hfp/256));
}
void MIPIRxLogicRes(void)
{
I2CADR = I2C_BASS_ADDRESS1;
/* mipi rx reset */
HDMI_WriteI2C_Byte(0x03,0x7f);
Timer0_Delay1ms(10);
HDMI_WriteI2C_Byte(0x03,0xff);
/* dds reset */
HDMI_WriteI2C_Byte(0x05,0xfb);
Timer0_Delay1ms(10);
HDMI_WriteI2C_Byte(0x05,0xff);
}
void DDSConfig(void)
{
I2CADR = I2C_BASS_ADDRESS2;
/* strm_sw_freq_word[ 7: 0] */
HDMI_WriteI2C_Byte(0x4e,0xaa);
/* strm_sw_freq_word[15: 8] */
HDMI_WriteI2C_Byte(0x4f,0xaa);
/* strm_sw_freq_word[23:16] */
HDMI_WriteI2C_Byte(0x50,0x6a);
/* [0]=strm_sw_freq_word[24] */
HDMI_WriteI2C_Byte(0x51,0x80);
HDMI_WriteI2C_Byte(0x1e,0x4f);
/* full_value 464 */
HDMI_WriteI2C_Byte(0x1f,0x5e);
HDMI_WriteI2C_Byte(0x20,0x01);
/* full_value1 416 */
HDMI_WriteI2C_Byte(0x21,0x2c);
HDMI_WriteI2C_Byte(0x22,0x01);
/* full_value2 400 */
HDMI_WriteI2C_Byte(0x23,0xfa);
HDMI_WriteI2C_Byte(0x24,0x00);
/* full_value3 384 */
HDMI_WriteI2C_Byte(0x25,0xc8);
HDMI_WriteI2C_Byte(0x26,0x00);
/* empty_value 464 */
HDMI_WriteI2C_Byte(0x27,0x5e);
HDMI_WriteI2C_Byte(0x28,0x01);
/* empty_value1 416 */
HDMI_WriteI2C_Byte(0x29,0x2c);
HDMI_WriteI2C_Byte(0x2a,0x01);
/* empty_value2 400 */
HDMI_WriteI2C_Byte(0x2b,0xfa);
HDMI_WriteI2C_Byte(0x2c,0x00);
/* empty_value3 384 */
HDMI_WriteI2C_Byte(0x2d,0xc8);
HDMI_WriteI2C_Byte(0x2e,0x00);
/* tmr_set[ 7:0]:100us */
HDMI_WriteI2C_Byte(0x42,0x64);
/* tmr_set[15:8]:100us */
HDMI_WriteI2C_Byte(0x43,0x00);
/* timer step */
HDMI_WriteI2C_Byte(0x44,0x04);
HDMI_WriteI2C_Byte(0x45,0x00);
HDMI_WriteI2C_Byte(0x46,0x59);
HDMI_WriteI2C_Byte(0x47,0x00);
HDMI_WriteI2C_Byte(0x48,0xf2);
HDMI_WriteI2C_Byte(0x49,0x06);
HDMI_WriteI2C_Byte(0x4a,0x00);
HDMI_WriteI2C_Byte(0x4b,0x72);
HDMI_WriteI2C_Byte(0x4c,0x45);
HDMI_WriteI2C_Byte(0x4d,0x00);
/* trend step */
HDMI_WriteI2C_Byte(0x52,0x08);
HDMI_WriteI2C_Byte(0x53,0x00);
HDMI_WriteI2C_Byte(0x54,0xb2);
HDMI_WriteI2C_Byte(0x55,0x00);
HDMI_WriteI2C_Byte(0x56,0xe4);
HDMI_WriteI2C_Byte(0x57,0x0d);
HDMI_WriteI2C_Byte(0x58,0x00);
HDMI_WriteI2C_Byte(0x59,0xe4);
HDMI_WriteI2C_Byte(0x5a,0x8a);
HDMI_WriteI2C_Byte(0x5b,0x00);
HDMI_WriteI2C_Byte(0x5c,0x34);
HDMI_WriteI2C_Byte(0x51,0x00);
}
void AudioIIsEn(void)
{
/* sampling 48K, sclk = 64*fs. */
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte(0xB2,0x01);
I2CADR = I2C_BASS_ADDRESS3;
HDMI_WriteI2C_Byte(0x06,0x08);
HDMI_WriteI2C_Byte(0x07,0xF0);
/* 0xE2:32FS; 0xD2:64FS */
HDMI_WriteI2C_Byte(0x34,0xD2);
}
void AudioSpdifEn(void)
{
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte(0xB2,0x01);
I2CADR = I2C_BASS_ADDRESS3;
HDMI_WriteI2C_Byte(0x06,0x0e);
HDMI_WriteI2C_Byte(0x07,0x00);
/* 0xE2:32FS; 0xD2:64FS */
HDMI_WriteI2C_Byte(0x34,0xD2);
}
void Core_Pll_setup(struct video_timing *panel)
{
unsigned char cpll_m, cpll_k1,cpll_k2;
unsigned int temp;
temp=(panel->pclk_khz*7)/25;
cpll_m=temp/1000;
temp=(panel->pclk_khz*7)/25;
temp=temp%1000;
temp=temp*16.384;
cpll_k1=temp%256;
cpll_k2=temp/256;
I2CADR = I2C_BASS_ADDRESS1;
/* cp=50uA */
HDMI_WriteI2C_Byte(0x50,0x24);
/* xtal_clk as reference,second order passive LPF PLL */
HDMI_WriteI2C_Byte(0x51,0x05);
/* use second-order PLL */
HDMI_WriteI2C_Byte(0x52,0x14);
/* CP_PRESET_DIV_RATIO */
HDMI_WriteI2C_Byte(0x69,cpll_m);
HDMI_WriteI2C_Byte(0x69,(cpll_m|0x80));
/* RGD_CP_SOFT_K_EN,RGD_CP_SOFT_K[13:8] */
HDMI_WriteI2C_Byte(0x6c,(cpll_k2|0x80));
HDMI_WriteI2C_Byte(0x6b,cpll_k1);
/* core pll reset */
HDMI_WriteI2C_Byte(0x04,0xfb);
HDMI_WriteI2C_Byte(0x04,0xff);
}
void Core_Pll_bypass(void)
{
I2CADR = I2C_BASS_ADDRESS1;
/* cp=50uA */
HDMI_WriteI2C_Byte(0x50,0x24);
/* Pix_clk as reference,second order passive LPF PLL */
HDMI_WriteI2C_Byte(0x51,0x2d);
/* loopdiv=0;use second-order PLL */
HDMI_WriteI2C_Byte(0x52,0x04);
/* CP_PRESET_DIV_RATIO */
HDMI_WriteI2C_Byte(0x69,0x0e);
HDMI_WriteI2C_Byte(0x69,0x8e);
HDMI_WriteI2C_Byte(0x6a,0x00);
/* RGD_CP_SOFT_K_EN,RGD_CP_SOFT_K[13:8] */
HDMI_WriteI2C_Byte(0x6c,0xb8);
HDMI_WriteI2C_Byte(0x6b,0x51);
/* core pll reset */
HDMI_WriteI2C_Byte(0x04,0xfb);
HDMI_WriteI2C_Byte(0x04,0xff);
}
void Lvds_Pll_Reset(void)
{
I2CADR = I2C_BASS_ADDRESS1;
/* lvds pll reset */
HDMI_WriteI2C_Byte(0x02,0xf7);
HDMI_WriteI2C_Byte(0x02,0xff);
}
void Scaler_bypass(void)
{
I2CADR = I2C_BASS_ADDRESS1;
/* disable scaler */
HDMI_WriteI2C_Byte(0x7f,0x00);
HDMI_WriteI2C_Byte(0xa8,0x13);
}
void Scaler_setup(struct video_timing *input_video,struct video_timing *panel)
{
/*
* for example: 720P to 1280x800
* These register base on MIPI resolution and LVDS panel resolution.
*/
unsigned int h_ratio,v_ratio;
unsigned char i;
unsigned int htotal;
h_ratio = input_video->hact*4096.00/panel->hact;
v_ratio = input_video->vact*4096.00/panel->vact;
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte(0x80,0x00);
HDMI_WriteI2C_Byte(0x81,0xff);
HDMI_WriteI2C_Byte(0x82,0x03);
HDMI_WriteI2C_Byte(0x83,(unsigned char)(input_video->hact%256));
HDMI_WriteI2C_Byte(0x84,(unsigned char)(input_video->hact/256));
HDMI_WriteI2C_Byte(0x85,0x80);
HDMI_WriteI2C_Byte(0x86,0x10);
HDMI_WriteI2C_Byte(0x87,(unsigned char)(panel->htotal%256));
HDMI_WriteI2C_Byte(0x88,(unsigned char)(panel->htotal/256));
HDMI_WriteI2C_Byte(0x89,(unsigned char)(panel->hs%256));
HDMI_WriteI2C_Byte(0x8a,(unsigned char)(panel->hbp%256));
HDMI_WriteI2C_Byte(0x8b,(unsigned char)(panel->vs%256));
HDMI_WriteI2C_Byte(0x8c,(unsigned char)(panel->hact%256));
HDMI_WriteI2C_Byte(0x8d,(unsigned char)(panel->vact%256));
HDMI_WriteI2C_Byte(0x8e,(unsigned char)(panel->vact/256)*16+(panel->hact/256));
HDMI_WriteI2C_Byte(0x8f,(unsigned char)(h_ratio%256));
HDMI_WriteI2C_Byte(0x90,(unsigned char)(h_ratio/256));
HDMI_WriteI2C_Byte(0x91,(unsigned char)(v_ratio%256));
HDMI_WriteI2C_Byte(0x92,(unsigned char)(v_ratio/256));
HDMI_WriteI2C_Byte(0x7f,0x96);
HDMI_WriteI2C_Byte(0xa8,0x13);
/* lvds pll reset */
HDMI_WriteI2C_Byte(0x02,0xf7);
HDMI_WriteI2C_Byte(0x02,0xff);
/* scaler reset */
HDMI_WriteI2C_Byte(0x03,0xcf);
HDMI_WriteI2C_Byte(0x03,0xff);
HDMI_WriteI2C_Byte(0x7f,0xb0);
for(i = 0; i < 5; i++)
{
if(HDMI_ReadI2C_Byte(0xa7)&0x20)
{
htotal = (HDMI_ReadI2C_Byte(0xa7)&0x0f)*0x100 + HDMI_ReadI2C_Byte(0xa6);
printf("\r\n scaler setup htotal = %d", htotal);
break;
}
Timer0_Delay1ms(100);
}
}
void LvdsPowerUp(void)
{
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte(0x44,0x30);
HDMI_WriteI2C_Byte(0x51,0x05);
}
void LvdsPowerDown(void)
{
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte(0x51,0x15);
}
void LvdsBypass(void)
{
I2CADR = I2C_BASS_ADDRESS1;
/* cp=50uA */
HDMI_WriteI2C_Byte(0x50,0x24);
/* Pix_clk as reference,second order passive LPF PLL */
HDMI_WriteI2C_Byte(0x51,0x2d);
/* loopdiv=0;use second-order PLL */
HDMI_WriteI2C_Byte(0x52,0x04);
/* CP_PRESET_DIV_RATIO */
HDMI_WriteI2C_Byte(0x69,0x0e);
HDMI_WriteI2C_Byte(0x69,0x8e);
HDMI_WriteI2C_Byte(0x6a,0x00);
/* RGD_CP_SOFT_K_EN,RGD_CP_SOFT_K[13:8] */
HDMI_WriteI2C_Byte(0x6c,0xb8);
HDMI_WriteI2C_Byte(0x6b,0x51);
/* core pll reset */
HDMI_WriteI2C_Byte(0x04,0xfb);
HDMI_WriteI2C_Byte(0x04,0xff);
/* disable scaler */
HDMI_WriteI2C_Byte(0x7f,0x00);
/* 0x13:VSEA ; 0x33:JEIDA */
HDMI_WriteI2C_Byte(0xa8,0x13);
}
void LvdsOutput(int on)
{
if(on)
{
I2CADR = I2C_BASS_ADDRESS1;
/* lvds pll reset */
HDMI_WriteI2C_Byte(0x02,0xf7);
/* scaler module reset */
HDMI_WriteI2C_Byte(0x02,0xff);
/* lvds tx module reset */
HDMI_WriteI2C_Byte(0x03,0xcb);
HDMI_WriteI2C_Byte(0x03,0xfb);
HDMI_WriteI2C_Byte(0x03,0xff);
/* enbale lvds output */
HDMI_WriteI2C_Byte(0x44,0x30);
}
else
{
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte(0x44,0x31);
}
}
void HdmiOutput(int on)
{
if(on)
{
I2CADR = I2C_BASS_ADDRESS1;
/* enable hdmi output */
HDMI_WriteI2C_Byte(0x33,0x0e);
}
else
{
I2CADR = I2C_BASS_ADDRESS1;
/* disable hdmi output */
HDMI_WriteI2C_Byte(0x33,0x0c);
}
}
void LvdsScalerResult(void)
{
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte(0x7f,0xb0);
}
void ScalerReset(void)
{
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte(0x03,0xcf);
HDMI_WriteI2C_Byte(0x03,0xff);
}
void lt8912_check_dds(void)
{
unsigned char reg_920d, reg_920e;
unsigned char i;
for(i = 0; i < 10; i++)
{
I2CADR = I2C_BASS_ADDRESS2;
reg_920d = HDMI_ReadI2C_Byte(0x0d);
reg_920e = HDMI_ReadI2C_Byte(0x0e);
/* shall update threshold here base on actual dds result. */
if((reg_920e == 0xd2)&&(reg_920d < 0xff)&&(reg_920d > 0xd0))
{
printf("\r\nlvds_check_dds: stable!");
break;
}
Timer0_Delay1ms(1000);
}
}
void lvds_output_cfg(void)
{
LvdsPowerUp();
#ifdef _lvds_bypass
LvdsBypass();
#else
Core_Pll_setup(&video_lvds_60Hz);
Scaler_setup(&video_lvds_60Hz,&video_lvds_60Hz);
#endif
}
void MIPI_Input_det(void)
{
I2CADR = I2C_BASS_ADDRESS1;
Hsync_L = HDMI_ReadI2C_Byte(0x9c);
Hsync_H = HDMI_ReadI2C_Byte(0x9d);
Vsync_L = HDMI_ReadI2C_Byte(0x9e);
Vsync_H = HDMI_ReadI2C_Byte(0x9f);
/* hiht byte changed */
if((Hsync_H!=Hsync_H_last)||(Vsync_H!=Vsync_H_last))
{
#if 0
if(Vsync_H == 0x02 && Vsync_L == 0x71)
{
MIPI_Video_Setup(&video_1024x600_60Hz);
}
else if(Vsync_H==0x02 && Vsync_L <= 0xef&&Vsync_L >= 0xec)
{
MIPI_Video_Setup(&video_1280x720_60Hz);
}
else if(Vsync_H == 0x03 && Vsync_L <= 0x3a &&Vsync_L >= 0x34)
{
MIPI_Video_Setup(&video_1280x800_60Hz);
}
else if(Vsync_H == 0x04 && Vsync_L <= 0x67&&Vsync_L >= 0x63)
{
MIPI_Video_Setup(&video_1920x1080_60Hz);
}
else if(Vsync_H == 0x03 && Vsync_L <= 0x23&&Vsync_L >= 0x1d)
{
MIPI_Video_Setup(&video_lvds_60Hz);
}
else if(Vsync_H == 0x1d && Vsync_L <= 0x05 &&Vsync_L >= 0x1d)
{
MIPI_Video_Setup(&video_800x1280_60Hz);
}
else
{
MIPI_Video_Setup(&video_1920x1080_60Hz);
}
#endif
MIPI_Video_Setup(&video_1920x1080_60Hz);
Hsync_L_last = Hsync_L;
Hsync_H_last = Hsync_H;
Vsync_L_last = Vsync_L;
Vsync_H_last = Vsync_H;
MIPIRxLogicRes();
}
}
void dds_clock_debug(void)
{
#ifdef dds_debug
unsigned char reg_920c, reg_920d, reg_920e, reg_920f;
while(1)
{
I2CADR = I2C_BASS_ADDRESS2;
reg_920c = HDMI_ReadI2C_Byte(0x0c);
reg_920d = HDMI_ReadI2C_Byte(0x0d);
reg_920e = HDMI_ReadI2C_Byte(0x0e);
reg_920f = HDMI_ReadI2C_Byte(0x0f);
printf("\r\n0x0c~0e = %02x, %02x, %02x",reg_920c, reg_920d, reg_920e);
printf("\r\nEnter the dds_clock_debug cycle");
Timer0_Delay1ms(1000);
}
#endif
}
void pattern_test(void)
{
/* 1080P Pattern output */
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte(0x08,0xff);
HDMI_WriteI2C_Byte(0x09,0xff);
HDMI_WriteI2C_Byte(0x0a,0xff);
HDMI_WriteI2C_Byte(0x0b,0xff);
HDMI_WriteI2C_Byte(0x0c,0xff);
HDMI_WriteI2C_Byte(0x31,0xa1);
HDMI_WriteI2C_Byte(0x32,0xa1);
HDMI_WriteI2C_Byte(0x33,0x03);
HDMI_WriteI2C_Byte(0x37,0x00);
HDMI_WriteI2C_Byte(0x38,0x22);
HDMI_WriteI2C_Byte(0x60,0x82);
HDMI_WriteI2C_Byte(0x39,0x45);
HDMI_WriteI2C_Byte(0x3b,0x00);
HDMI_WriteI2C_Byte(0x44,0x31);
HDMI_WriteI2C_Byte(0x55,0x44);
HDMI_WriteI2C_Byte(0x57,0x01);
HDMI_WriteI2C_Byte(0x5a,0x02);
DigitalClockEn();
TxAnalog();
CbusAnalog();
HDMIPllAnalog();
AudioIIsEn();
AviInfoframe();
I2CADR = I2C_BASS_ADDRESS2;
/* term en To analog phy for trans lp mode to hs mode */
HDMI_WriteI2C_Byte(0x10,0x00);
/* settle Set timing for dphy trans state from PRPR to SOT state */
HDMI_WriteI2C_Byte(0x11,0x04);
/* trail */
HDMI_WriteI2C_Byte(0x12,0x04);
/* 4 lane, 01 lane, 02 2lane, 03 3lane */
HDMI_WriteI2C_Byte(0x13,0x00);
/* debug mux */
HDMI_WriteI2C_Byte(0x14,0x00);
HDMI_WriteI2C_Byte(0x15,0x00);
/* hshift 3 */
HDMI_WriteI2C_Byte(0x1a,0x03);
/* vshift 3 */
HDMI_WriteI2C_Byte(0x1b,0x03);
/* hwidth 62 */
HDMI_WriteI2C_Byte(0x18,0x28);
/* vwidth 6 */
HDMI_WriteI2C_Byte(0x19,0x05);
/* pix num hactive */
HDMI_WriteI2C_Byte(0x1c,0x00);
HDMI_WriteI2C_Byte(0x1d,0x05);
/* h v d pol hdmi sel pll sel */
HDMI_WriteI2C_Byte(0x1e,0x67);
/* fifo_buff_length 12 */
HDMI_WriteI2C_Byte(0x2f,0x0c);
/* htotal */
HDMI_WriteI2C_Byte(0x34,0x72);
/* htotal */
HDMI_WriteI2C_Byte(0x35,0x06);
/* vtotal */
HDMI_WriteI2C_Byte(0x36,0xee);
/* vtotal */
HDMI_WriteI2C_Byte(0x37,0x02);
/* vbp */
HDMI_WriteI2C_Byte(0x38,0x14);
/* vbp */
HDMI_WriteI2C_Byte(0x39,0x00);
/* vfp */
HDMI_WriteI2C_Byte(0x3a,0x05);
/* vfp */
HDMI_WriteI2C_Byte(0x3b,0x00);
/* hbp */
HDMI_WriteI2C_Byte(0x3c,0xdc);
/* hbp */
HDMI_WriteI2C_Byte(0x3d,0x00);
/* hfp */
HDMI_WriteI2C_Byte(0x3e,0x6e);
/* hfp */
HDMI_WriteI2C_Byte(0x3f,0x00);
HDMI_WriteI2C_Byte(0x72,0x12);
/* RGD_PTN_DE_DLY[7:0] */
HDMI_WriteI2C_Byte(0x73,0x04);
/* RGD_PTN_DE_DLY[11:8] 260 */
HDMI_WriteI2C_Byte(0x74,0x01);
/* RGD_PTN_DE_TOP[6:0] 150 */
HDMI_WriteI2C_Byte(0x75,0x19);
/* RGD_PTN_DE_CNT[7:0] */
HDMI_WriteI2C_Byte(0x76,0x00);
/* RGD_PTN_DE_LIN[7:0] */
HDMI_WriteI2C_Byte(0x77,0xd0);
/* RGD_PTN_DE_LIN[10:8],RGD_PTN_DE_CNT[11:8] */
HDMI_WriteI2C_Byte(0x78,0x25);
/* RGD_PTN_H_TOTAL[7:0] */
HDMI_WriteI2C_Byte(0x79,0x72);
/* RGD_PTN_V_TOTAL[7:0] */
HDMI_WriteI2C_Byte(0x7a,0xee);
/* RGD_PTN_V_TOTAL[10:8],RGD_PTN_H_TOTAL[11:8] */
HDMI_WriteI2C_Byte(0x7b,0x26);
/* RGD_PTN_HWIDTH[7:0] */
HDMI_WriteI2C_Byte(0x7c,0x28);
/* RGD_PTN_HWIDTH[9:8],RGD_PTN_VWIDTH[5:0] */
HDMI_WriteI2C_Byte(0x7d,0x05);
/* pattern enable */
HDMI_WriteI2C_Byte(0x70,0x80);
HDMI_WriteI2C_Byte(0x71,0x51);
HDMI_WriteI2C_Byte(0x42,0x12);
/* strm_sw_freq_word[ 7: 0] */
HDMI_WriteI2C_Byte(0x4e,0xAA);
/* strm_sw_freq_word[15: 8] */
HDMI_WriteI2C_Byte(0x4f,0xAA);
/* strm_sw_freq_word[23:16] */
HDMI_WriteI2C_Byte(0x50,0x6A);
/* pattern en */
HDMI_WriteI2C_Byte(0x51,0x80);
HdmiOutput(1);
}
unsigned char LT8912_Get_HPD(void)
{
I2CADR = I2C_BASS_ADDRESS1;
if((HDMI_ReadI2C_Byte(0xc1)&0x80)==0x80)
{
printf("\r\nLT8912_Get_HPD: high");
return 1;
}
else
{
printf("\r\nLT8912_Get_HPD: low");
return 0;
}
}
void read_LT8912_chip_ID(void)
{
I2CADR = I2C_BASS_ADDRESS1;
printf("LT8912b chip ID: 0x%x, 0x%x\r\n",HDMI_ReadI2C_Byte(0x00), HDMI_ReadI2C_Byte(0x01));
}
void LT8912B_Suspend(int on)
{
/* 9mA,HPD detect is normal. */
if(on)
{
if(!suspend_on)
{
/* enter suspend mode */
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte( 0x54, 0x1d );
HDMI_WriteI2C_Byte( 0x51, 0x15 );
HDMI_WriteI2C_Byte( 0x44, 0x31 );
HDMI_WriteI2C_Byte( 0x41, 0xbd );
HDMI_WriteI2C_Byte( 0x5c, 0x11 );
suspend_on = 1;
printf("\r\nsuspend on");
}
}
else
{
if(suspend_on)
{
/* exist suspend mode */
I2CADR = I2C_BASS_ADDRESS1;
HDMI_WriteI2C_Byte( 0x5c, 0x10 );
HDMI_WriteI2C_Byte( 0x54, 0x1c );
HDMI_WriteI2C_Byte( 0x51, 0x2d );
HDMI_WriteI2C_Byte( 0x44, 0x30 );
HDMI_WriteI2C_Byte( 0x41, 0xbc );
Timer0_Delay1ms(10);
HDMI_WriteI2C_Byte(0x03,0x7f);
Timer0_Delay1ms(10);
HDMI_WriteI2C_Byte(0x03,0xff);
HDMI_WriteI2C_Byte(0x05,0xfb);
Timer0_Delay1ms(10);
HDMI_WriteI2C_Byte(0x05,0xff);
suspend_on = 0;
printf("\r\nsuspend off");
}
}
}
int lt8912b_probe(struct udevice *dev)
{
lt8912b_twi = dev_get_priv(dev->parent);
lt8912b_dev = dev;
read_LT8912_chip_ID();
#ifdef _pattern_test_
// printf("[LT8912B] Step 3: Starting PATTERN TEST mode...\r\n");
pattern_test();
return 0;
#else
DigitalClockEn();
TxAnalog();
CbusAnalog();
HDMIPllAnalog();
MipiAnalog();
MipiBasicSet();
DDSConfig();
MIPI_Video_Setup(&video_1920x1080_60Hz);
// MIPI_Input_det();
AudioIIsEn();
AviInfoframe();
MIPIRxLogicRes();
#ifdef dds_debug
lt8912_check_dds();
#endif
#endif
// MIPI_Input_det();
dds_clock_debug();
Timer0_Delay1ms(1000);
LT8912B_Suspend(0);
HdmiOutput(1);
return 0;
}
static const struct udevice_id lt8912b_match[] = {
{ .compatible = "panel-lt8912b-hdmi", },
};
U_BOOT_DRIVER(lt8912b) = {
.name = "panel_lt8912b_hdmi",
.id = UCLASS_I2C_GENERIC,
.of_match = of_match_ptr(lt8912b_match),
.probe = lt8912b_probe,
.bind = dm_scan_fdt_dev,
};
2.2 修改 Makefile 和 Kconfig
修改该目录下的 Makefile 和 Kconfig,在最底下分别添加
obj-$(CONFIG_LONTIUM_LT8912B) += lt8912b.oconfig LONTIUM_LT8912B
bool "lontium chip lt8912b"
default n
help
Single-Channel MIPI DSI Bridge to LVDS/HDMI. If you want to support lt8912b, select it.2.3 修改 uboot defconfig
brandy/brandy-2.0/u-boot-bsp/configs/sun8iw22p1_t153_defconfig 最后添加
CONFIG_LONTIUM_LT8912B=y2.4 改设备树
&twi2 {
clock-frequency = <100000>;
pinctrl-names = "default, sleep";
pinctrl-0 = <&twi2_pins_default>;
pinctrl-1 = <&twi2_pins_sleep>;
status = "okay";
lt8912b: lt8912b@48 {
compatible = "panel-lt8912b-hdmi";
status = "okay";
reg = <0x48>;
reset-gpio = <&pio PA 3 GPIO_ACTIVE_HIGH>; //reset
};
};2.5 修改 board.c
因为 uboot 的 pinctrl 驱动 pinctrl-sunxi-v3.c (路径 brandy/brandy-2.0/u-boot-bsp/drivers/pinctrl/) 的 pinmux_set/pinconf_set 只被 DM pinctrl uclass 调用,而 pinctrl_select_state() 触发 uclass 的 set_state 这套 fork 没有 pinctrl-uclass.c,所以:
pinctrl_select_state(dev,"default") 是空操作 → V3 驱动 pinmux_set 不被自动调用;
因此需要修改 brandy/brandy-2.0/u-boot-bsp/board/sunxi/board.c 手动写寄存器、配置引脚来初始化twi。
2.5.1 增加头文件声明
#define SUNXI_MODNAME LOGC_BOOT
// 新增头文件声明
#include <dm/uclass.h>
#include <dm/uclass-internal.h>
#include <dm/device-internal.h>
#include <dm/root.h>2.5.2 修改 twi_init_board 函数
在 twi_init_board 函数最后添加写寄存器、配置引脚功能:PB2,PB3 复用为 twi2,PA3 为 LT8912b 的 reset 引脚。
void twi_init_board(void)
{
...
clock_twi_onoff(5, 1);
sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_H3_GPL_R_TWI);
sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN8I_H3_GPL_R_TWI);
#endif
#endif
// 新增 ↓
#if defined(CONFIG_MACH_SUN8IW22)
/*
* T153 (ypt153evb): TWI2 is on PB2(SCK)/PB3(SDA), function mux = 5.
* This SoC uses the AW_GPIO_V3 framework, where the legacy
* sunxi_gpio_set_cfgpin()/sunxi_gpio_set_pull() helpers are NOT
* declared (see arch/arm/include/asm/arch/gpio.h under
* CONFIG_AW_GPIO_V3), and there is no pinctrl uclass in this tree.
* So configure the pins by writing the PIO registers directly.
* PB mux base 0x03604100, pull +0x30 (same addresses confirmed
* working via "mw" on the board). mux = 4 bits/pin, pull = 2 bits/pin.
*/
/* PB2/PB3 -> mux 5 (TWI2). mux = 4 bits/pin. */
writel((readl((void __iomem *)0x03604100) & ~0x0000ff00u) | 0x00005500u,
(void __iomem *)0x03604100);
/* PB2/PB3 internal pull-up. pull = 2 bits/pin. */
writel((readl((void __iomem *)0x03604130) & ~0x000000f0u) | 0x00000050u,
(void __iomem *)0x03604130);
// clock_twi_onoff(2, 1);
/*
* Release LT8912B from reset: its RESETB is active-low, so drive
* PA3 high. PA mux base 0x03604080, data +0x10. The DT
* "reset-gpio = <&pio PA 3 GPIO_ACTIVE_HIGH>" polarity is inverted
* (should be GPIO_ACTIVE_LOW) and the panel driver uses the reset
* subsystem rather than this gpio, so uboot never released it
* automatically. Do it here for ypt153evb wiring.
*/
/* PA3 -> output (mux = 1) */
writel((readl((void __iomem *)0x03604080) & ~0x0000f000u) | 0x00001000u,
(void __iomem *)0x03604080);
/* PA3 = high */
writel(readl((void __iomem *)0x03604090) | (1u << 3),
(void __iomem *)0x03604090);
#endif
}2.5.3 修改 board_init 函数
int board_init(void)
{
struct udevice *dev; // 开头增加变量
struct uclass *uc; // 开头增加变量
__maybe_unused int id_pfr1, ret, satapwr_pin, macpwr_pin;
.......
/* update ddr tuning para to sram address */
sunxi_update_ddrpara_to_sram();
// 使能全部 i2c 子设备
ret = uclass_get(UCLASS_I2C_GENERIC, &uc);
if (ret) {
printf("uclass get UCLASS_I2C_GENERIC fail. \n");
return -1;
}
uclass_foreach_dev(dev, uc) {
ret = device_probe(dev);
if (ret) {
printf("uclass probe fail. \n");
}
}
return 0;
}2.5.4 可选修改
twi 可能会 busy 报错
修改 brandy/brandy-2.0/u-boot-bsp/drivers/twi/sunxi_twi.c 修改以下函数
static int sunxi_twi_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
{
struct sunxi_twi *twi = dev_get_plat(dev);
struct i2c_msg *dmsg;
if (nmsgs > 2 || nmsgs == 0) {
pr_err("twi-%d: Only support transfer one or two messages\n", twi->bus_num);
return -1;
}
if (sunxi_twi_check_device_busy(twi)) {
/*
* status != TWI_STAT_IDLE(i.e. 0x20 after slave-NACK) is
* mis-treated as "bus busy" here. The previous transfer left
* the state machine stuck (e.g. slave NACK without a clean
* STOP), so soft-reset the controller to recover it instead
* of failing every following transfer.
*/
pr_err("twi-%d: Bus is busy, try to recover\n", twi->bus_num);
sunxi_twi_soft_reset(twi->reg);
udelay(50);
twi->reg->ctl |= TWI_CTL_BUSEN;
if (sunxi_twi_check_device_busy(twi))
return -EBUSY;
}
dmsg = nmsgs == 1 ? msg : msg + 1;
if (dmsg->flags & I2C_M_RD)
return sunxi_twi_read(twi, msg);
else
return sunxi_twi_write(twi, msg);
return 0;
}最后编辑:Mai 更新时间:2026-11-05 14:56