1.1 rk3576 bt1120硬件确认

rk3576 bt1120硬件确认
查阅硬件设计指南可以确认 rk3576是支持 16bit bt1120模式 ,最高支持1080p60Hz。

image

对应引脚可以查阅引脚设计指南或者设备树pinctrl ,bt1120只有这些引脚,如下图所示:

image

1.2、nvp6021硬件确认
电源,有供电引脚控制需要开启,并测了一下是否正常。

image

复位:这里是active low ,因为使用硬件原理图上加了三极管,需要取反处理

image

I2C地址及电平,使用i2c的电平必须为3v3或者转为3v3的。

image

image

2、驱动及设备树配置
2.1、nvp6021驱动实现
nvp6021厂家是没有提供Linux版本驱动的,只提供一个1080P_720P5060_74.25MHz_16bit_20191213.txt这种寄存器配置,根据这个配置结合设备树简单实现对应分辨率及帧数选择。

通过设备树nextchip,vmode 属性来选择对应寄存器写入。

复制代码
nvp6021: nvp6021@30 {
pinctrl-names = “default”;
pinctrl-0 = <&nvp6021_rst_pins>;
compatible = “nextchip,nvp6021”;
reg = <0x30>;
reset-gpios = <&gpio3 RK_PD4 GPIO_ACTIVE_HIGH>;
nextchip,vmode = <0>; // 0:1080p30, 1:1080p25, 2:720p60, 3:720p50
};
复制代码
实现的驱动源码,复位引脚电平会导致寄存器是否正常写入,以实际硬件为准。

复制代码
// SPDX-License-Identifier: GPL-2.0
/*

  • NVP6021 AHD TX initialization driver (I2C only)
  • Supports video modes:
  • 0 = 1080p30
  • 1 = 1080p25
  • 2 = 720p60
  • 3 = 720p50
    */

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/property.h>

/* Video mode defines (matching original DecoderInitial) */
#define NVP6021_VMODE_1080P30 0
#define NVP6021_VMODE_1080P25 1
#define NVP6021_VMODE_720P60 2
#define NVP6021_VMODE_720P50 3

struct nvp6021_priv {
struct i2c_client *client;
struct gpio_desc *reset_gpio;
u8 vmode;
};

/* Write one byte to register address */
static int nvp6021_write(struct i2c_client *client, u8 reg, u8 val)
{
return i2c_smbus_write_byte_data(client, reg, val);
}

/* Hardware reset sequence (exactly as original) */

static void nvp6021_hw_reset(struct nvp6021_priv *priv)
{
if (!priv->reset_gpio) {
dev_warn(&priv->client->dev, “No reset GPIO, skip reset\n”);
return;
}

gpiod_set_value_cansleep(priv->reset_gpio, 0); usleep_range(2000, 3000); gpiod_set_value_cansleep(priv->reset_gpio, 1); usleep_range(20000, 21000); gpiod_set_value_cansleep(priv->reset_gpio, 0); usleep_range(5000, 6000);

}

/* Full initialization sequence based on the given code */
static int nvp6021_initialize(struct nvp6021_priv *priv)
{
struct i2c_client *client = priv->client;
u8 vmode = priv->vmode;
int ret;

dev_info(&client->dev, "Initializing NVP6021 (vmode=%d)\n", vmode); /* Hardware reset */ nvp6021_hw_reset(priv); /* ---- BANK0 ---- */ ret = nvp6021_write(client, 0xFF, 0x00); // select BANK0 if (ret) goto err; ret = nvp6021_write(client, 0x00, 0x01); if (ret) goto err; switch (vmode) { case NVP6021_VMODE_1080P30: ret = nvp6021_write(client, 0x01, 0xF0); if (ret) goto err; ret = nvp6021_write(client, 0x04, 0x00); //ret = nvp6021_write(client, 0x04, 0x85); break; case NVP6021_VMODE_1080P25: ret = nvp6021_write(client, 0x01, 0xF1); if (ret) goto err; ret = nvp6021_write(client, 0x04, 0x00); break; case NVP6021_VMODE_720P60: ret = nvp6021_write(client, 0x01, 0xE2); if (ret) goto err; ret = nvp6021_write(client, 0x04, 0x00); break; case NVP6021_VMODE_720P50: ret = nvp6021_write(client, 0x01, 0xE3); if (ret) goto err; ret = nvp6021_write(client, 0x04, 0x00); break; default: dev_err(&client->dev, "Invalid vmode %d\n", vmode); return -EINVAL; } if (ret) goto err; /* ---- BANK1 ---- */ ret = nvp6021_write(client, 0xFF, 0x01); // select BANK1 if (ret) goto err; ret = nvp6021_write(client, 0x0E, 0x00); if (ret) goto err; /* ---- BANK2 (main configuration) ---- */ ret = nvp6021_write(client, 0xFF, 0x02); // select BANK2 if (ret) goto err; /* Write the sequence exactly as provided */ u8 init_vals[][2] = { {0x00, 0xFE}, {0x02, 0x00}, {0x04, 0x80}, {0x05, 0x00}, {0x06, 0x10}, {0x0C, 0x04}, {0x0D, 0x3F}, {0x0E, 0x00}, {0x10, 0xEB}, {0x11, 0x10}, {0x12, 0xF0}, {0x13, 0x10}, {0x14, 0x01}, {0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00}, {0x18, 0x00}, {0x19, 0x00}, {0x1C, 0x80}, {0x1D, 0x80}, {0x1E, 0x80}, {0x36, 0x01}, {0x37, 0x80}, {0x39, 0x00}, {0x3C, 0x00}, {0x3D, 0x00}, {0x3E, 0x00}, {0x40, 0x01}, {0x41, 0xFF}, {0x42, 0x80}, {0x60, 0x80}, {0x61, 0x80}, {0x63, 0xE0}, {0x64, 0x19}, {0x65, 0x04}, {0x66, 0xEB}, {0x67, 0x60}, {0x68, 0x00}, {0x69, 0x00}, {0x6A, 0x00}, {0x6B, 0x00}, }; for (int i = 0; i < ARRAY_SIZE(init_vals); i++) { ret = nvp6021_write(client, init_vals[i][0], init_vals[i][1]); if (ret) goto err; } /* Clear 0x20..0x34 */ for (u8 r = 0x20; r <= 0x34; r++) { ret = nvp6021_write(client, r, 0x00); if (ret) goto err; } /* Clear 0x48..0x57 */ for (u8 r = 0x48; r <= 0x57; r++) { ret = nvp6021_write(client, r, 0x00); if (ret) goto err; } /* Extra clears */ ret = nvp6021_write(client, 0x59, 0x00); if (ret) goto err; ret = nvp6021_write(client, 0x5A, 0x00); if (ret) goto err; for (u8 r = 0x5C; r <= 0x5F; r++) { ret = nvp6021_write(client, r, 0x00); if (ret) goto err; } /* Mode-dependent BANK2 writes (0x3A, 0x01) */ switch (vmode) { case NVP6021_VMODE_1080P30: ret = nvp6021_write(client, 0x3A, 0x11); if (ret) goto err; ret = nvp6021_write(client, 0x01, 0xF0); break; case NVP6021_VMODE_1080P25: ret = nvp6021_write(client, 0x3A, 0x11); if (ret) goto err; ret = nvp6021_write(client, 0x01, 0xF1); break; case NVP6021_VMODE_720P60: ret = nvp6021_write(client, 0x3A, 0x11); if (ret) goto err; ret = nvp6021_write(client, 0x01, 0xE2); break; case NVP6021_VMODE_720P50: ret = nvp6021_write(client, 0x3A, 0x11); if (ret) goto err; ret = nvp6021_write(client, 0x01, 0xE3); break; } if (ret) goto err; /* Second BANK2 access – mode‑specific fine‑tuning */ ret = nvp6021_write(client, 0xFF, 0x02); // ensure BANK2 if (ret) goto err; switch (vmode) { case NVP6021_VMODE_1080P30: ret = nvp6021_write(client, 0x3D, 0x80); if (ret) goto err; ret = nvp6021_write(client, 0x5C, 0x52); if (ret) goto err; ret = nvp6021_write(client, 0x5D, 0xCA); if (ret) goto err; ret = nvp6021_write(client, 0x5E, 0xF0); if (ret) goto err; ret = nvp6021_write(client, 0x5F, 0x2C); if (ret) goto err; ret = nvp6021_write(client, 0x1D, 0xB0); if (ret) goto err; ret = nvp6021_write(client, 0x1E, 0xB0); if (ret) goto err; ret = nvp6021_write(client, 0x37, 0xB0); break; case NVP6021_VMODE_1080P25: ret = nvp6021_write(client, 0x3D, 0x80); if (ret) goto err; ret = nvp6021_write(client, 0x5C, 0x52); if (ret) goto err; ret = nvp6021_write(client, 0x5D, 0xC3); if (ret) goto err; ret = nvp6021_write(client, 0x5E, 0x7D); if (ret) goto err; ret = nvp6021_write(client, 0x5F, 0xC8); if (ret) goto err; ret = nvp6021_write(client, 0x1D, 0xB0); if (ret) goto err; ret = nvp6021_write(client, 0x1E, 0xB0); if (ret) goto err; ret = nvp6021_write(client, 0x37, 0xB0); break; case NVP6021_VMODE_720P60: ret = nvp6021_write(client, 0x3D, 0x80); if (ret) goto err; ret = nvp6021_write(client, 0x5C, 0x52); if (ret) goto err; ret = nvp6021_write(client, 0x5D, 0xC5); if (ret) goto err; ret = nvp6021_write(client, 0x5E, 0xF9); if (ret) goto err; ret = nvp6021_write(client, 0x5F, 0x2C); if (ret) goto err; ret = nvp6021_write(client, 0x1D, 0xB0); if (ret) goto err; ret = nvp6021_write(client, 0x1E, 0xB0); if (ret) goto err; ret = nvp6021_write(client, 0x37, 0xB0); break; case NVP6021_VMODE_720P50: ret = nvp6021_write(client, 0x3D, 0x80); if (ret) goto err; ret = nvp6021_write(client, 0x5C, 0x52); if (ret) goto err; ret = nvp6021_write(client, 0x5D, 0xCF); if (ret) goto err; ret = nvp6021_write(client, 0x5E, 0xE7); if (ret) goto err; ret = nvp6021_write(client, 0x5F, 0x2C); if (ret) goto err; ret = nvp6021_write(client, 0x1D, 0xB0); if (ret) goto err; ret = nvp6021_write(client, 0x1E, 0xB0); if (ret) goto err; ret = nvp6021_write(client, 0x37, 0xB0); break; } if (ret) goto err; dev_info(&client->dev, "NVP6021 initialization completed successfully\n"); //return 0; //ret = nvp6021_write(client, 0xFF, 0x02); // 确保选择 BANK2 //if (ret) goto err; //ret = nvp6021_write(client, 0x04, 0x81); // 0x04 的 bit7 置 1 使能彩条输出 //if (ret) goto err; dev_info(&client->dev, "NVP6021 initialization done (color bar test enabled)\n"); return 0;

err:
dev_err(&client->dev, “I2C write failed\n”);
return ret;
}

/* Probe - called when device is found on I2C bus */
static int nvp6021_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct nvp6021_priv *priv;
u32 vmode = NVP6021_VMODE_1080P30;
int ret;

priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->client = client; i2c_set_clientdata(client, priv); /* Obtain reset GPIO (active high as in original) */ priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(priv->reset_gpio)) return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n"); /* Read video mode from device tree property */ device_property_read_u32(dev, "nextchip,vmode", &vmode); if (vmode > NVP6021_VMODE_720P50) { dev_warn(dev, "Invalid vmode %u, fallback to 1080p30\n", vmode); vmode = NVP6021_VMODE_1080P30; } priv->vmode = (u8)vmode; /* Run initialization sequence */ ret = nvp6021_initialize(priv); if (ret) return ret; dev_info(dev, "NVP6021 driver probe successful\n"); return 0;

}

static const struct of_device_id nvp6021_of_match[] = {
{ .compatible = “nextchip,nvp6021” },
{ }
};
MODULE_DEVICE_TABLE(of, nvp6021_of_match);

static const struct i2c_device_id nvp6021_id[] = {
{ “nvp6021”, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, nvp6021_id);

static struct i2c_driver nvp6021_driver = {
.driver = {
.name = “nvp6021”,
.of_match_table = nvp6021_of_match,
},
.probe = nvp6021_probe,
.id_table = nvp6021_id,
};
module_i2c_driver(nvp6021_driver);

MODULE_DESCRIPTION(“Nextchip NVP6021 AHD TX initialization driver”);
MODULE_AUTHOR(“lhx”);
MODULE_LICENSE(“GPL”);
复制代码

使用i2c-tools功能查看寄存器是正常写入,驱动里面有三个bank的寄存器配置,这里以切换到bank0为例子

image

image

2.2 设备树配置
复制代码
#include <dt-bindings/display/media-bus-format.h>
#include <dt-bindings/display/rockchip_vop.h>

/{
panel: panel {
compatible = “simple-panel”;
clock-frequency = <74250000>;
bus-format = <MEDIA_BUS_FMT_YUYV8_1X16>;
status = “okay”;

display-timings { native-mode = <&timing_1080p30>; timing_1080p30: timing { clock-frequency = <74250000>; // 74.25MHz时钟 hactive = <1920>; // 有效水平像素 vactive = <1080>; // 有效垂直行数 hfront-porch = <88>; // 水平前沿 hback-porch = <148>; // 水平后沿 hsync-len = <44>; // 水平同步脉冲 vfront-porch = <4>; // 垂直前沿 vback-porch = <36>; // 垂直后沿 vsync-len = <5>; // 垂直同步脉冲 hsync-active = <1>; // 高电平有效 vsync-active = <1>; // 高电平有效 de-active = <0>; // DE信号极性 pixelclk-active = <0>; // 像素时钟下降沿采样 }; timing_720p60: timing1 { clock-frequency = <74250000>; hactive = <1280>; vactive = <720>; hfront-porch = <110>; hback-porch = <220>; hsync-len = <40>; vfront-porch = <5>; vback-porch = <20>; vsync-len = <5>; hsync-active = <1>; vsync-active = <1>; de-active = <0>; pixelclk-active = <0>; }; }; port { panel_in_rgb: endpoint { remote-endpoint = <&rgb_out_panel>; }; }; };

};

&display_subsystem {
status = “okay”;
};
&i2c8 {
clock-frequency = <400000>;
pinctrl-0 = <&i2c8m1_xfer>;
status = “okay”;

nvp6021: nvp6021@30 { pinctrl-names = "default"; pinctrl-0 = <&nvp6021_rst_pins>; compatible = "nextchip,nvp6021"; reg = <0x30>; reset-gpios = <&gpio3 RK_PD4 GPIO_ACTIVE_HIGH>; nextchip,vmode = <2>; // 0:1080p30, 1:1080p25, 2:720p60, 3:720p50 };

};

&rgb {
status = “okay”;
pinctrl-names = “default”;
pinctrl-0 = <&bt1120_pins>;

ports { port@1 { reg = <1>; rgb_out_panel: endpoint { remote-endpoint = <&panel_in_rgb>; }; }; };

};

&rgb_in_vp2 {
status = “okay”;