F1C100s 主线内核适配 ST7701 RGB 屏幕

目录

硬件环境
驱动移植
设备树配置
内核配置
问题解决
最终屏参
结语

硬件环境

  • SoC: 全志 F1C100s (ARM926EJ-S)
  • 屏幕: ST7701S, 2.95 寸竖屏, SPI 9 bit 初始化 + RGB565 并行数据,不接复位
  • 接口: SPI (GPIO 软件模拟) + RGB565 HV 模式 (无 DE 信号)
  • 内核: Linux 7.2
  • 上游驱动: panel-sitronix-st7701.c

驱动移植

面板描述符 t295h5c4001_desc

根据厂商屏参文件配置, 包含显示时序、电源参数、Gamma 值、总线格式等。

初始显示时序 (直接照搬厂商值)

static const struct drm_display_mode t295h5c4001_mode = {
    .clock        = 28000,                  /* 像素时钟 28MHz */
    .hdisplay     = 360,
    .hsync_start  = 360 + 50,               /* HFP = 50 */
    .hsync_end    = 360 + 50 + 8,           /* HSW  = 8  */
    .htotal       = 360 + 50 + 8 + 20,      /* HBP = 20, htotal=438 */
    .vdisplay     = 640,
    .vsync_start  = 640 + 28,               /* VFP = 28 */
    .vsync_end    = 640 + 28 + 4,           /* VSW  = 4  */
    .vtotal       = 640 + 28 + 4 + 32,      /* VBP = 32, vtotal=704 */
    .width_mm     = 41,
    .height_mm    = 73,
    .flags        = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
    .type         = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
};

面板描述符

static const struct st7701_panel_desc t295h5c4001_desc = {
    .mode             = &t295h5c4001_mode,
    .panel_sleep_delay = 80,                /* 厂商总等待 200ms (120 base + 80 extra) */
    .bus_format       = MEDIA_BUS_FMT_RGB565_1X16,

    /* Gamma 校正 (厂商屏参直接提供) */
    .pv_gamma = {
        0x00, 0x0A, 0x11, 0x0C, 0x10, 0x05, 0x00, 0x08,
        0x08, 0x1F, 0x07, 0x13, 0x10, 0xA9, 0x30, 0x18
    },
    .nv_gamma = {
        0x00, 0x0B, 0x11, 0x0D, 0x0F, 0x05, 0x02, 0x07,
        0x06, 0x20, 0x05, 0x15, 0x13, 0xA9, 0x30, 0x18
    },

    /* 电源参数 (基于厂商参数反算) */
    .nlinv            = 1,
    .vop_uv           = 4575000,
    .vcom_uv          = 1300000,
    .vgh_mv           = 15000,
    .vgl_mv           = -10170,
    .avdd_mv          = 6600,
    .avcl_mv          = -4600,
    .gamma_op_bias    = OP_BIAS_MIDDLE,
    .input_op_bias    = OP_BIAS_MIN,
    .output_op_bias   = OP_BIAS_MIN,
    .t2d_ns           = 1600,
    .t3d_ns           = 10400,
    .eot_en           = true,

    .gip_sequence     = t295h5c4001_gip_sequence,
};

struct st7701_panel_desc 添加 bus_format 属性,并在 st7701_get_modes 中添加通过 bus_format 字段将 RGB565 格式传递给 TCON:

if (st7701->desc->bus_format)
    drm_display_info_set_bus_formats(&connector->display_info,
                                     &st7701->desc->bus_format, 1);

驱动注册与匹配 (SPI 接口)

of_device_id 匹配表和 SPI ID 表:

static const struct of_device_id st7701_spi_of_match[] = {
    { .compatible = "anbernic,rg28xx-panel", .data = &rg28xx_desc },
    { .compatible = "goldenmorning,t295h5c4001-panel", .data = &t295h5c4001_desc },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, st7701_spi_of_match);

static const struct spi_device_id st7701_spi_ids[] = {
    { "rg28xx-panel" },
    { "t295h5c4001-panel" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(spi, st7701_spi_ids);

设备树配置

dtsi 补充

由于 F1C100s 在主线内核中没有 lcd-controller 等相关驱动,参考社区使用了 sun4i 的驱动,因此在 suniv-f1c100s.dtsi 中补充。

根节点下补充:

de: display-engine {
    compatible = "allwinner,sun4i-a10-display-engine";
    allwinner,pipelines = <&fe0>;
    status = "disabled";
};

soc 节点下补充:

tcon0: lcd-controller@1c0c000 {
    compatible = "allwinner,sun4i-a10-tcon";
    reg = <0x01c0c000 0x1000>;
    interrupts = <29>;
    resets = <&ccu RST_BUS_LCD>;
    reset-names = "lcd";
    clocks = <&ccu CLK_BUS_LCD>,
            <&ccu CLK_TCON>,
            <&ccu CLK_TVE1_CLK>;
    clock-names = "ahb",
                "tcon-ch0",
                "tcon-ch1";
    clock-output-names = "tcon-pixel-clock";
    #clock-cells = <0>;
    status = "disabled";

    ports {
        #address-cells = <1>;
        #size-cells = <0>;

        tcon0_in: port@0 {
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <0>;

            tcon0_in_be0: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&be0_out_tcon0>;
            };
        };

        tcon0_out: port@1 {
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <1>;
        };
    };
};

fe0: display-frontend@1e00000 {
    compatible = "allwinner,sun4i-a10-display-frontend";
    reg = <0x01e00000 0x20000>;
    interrupts = <30>;
    clocks = <&ccu CLK_BUS_DE_FE>, <&ccu CLK_DE_FE>,
            <&ccu CLK_DRAM_DE_FE>;
    clock-names = "ahb", "mod",
                "ram";
    resets = <&ccu RST_BUS_DE_FE>;
    status = "disabled";

    ports {
        #address-cells = <1>;
        #size-cells = <0>;

        fe0_out: port@1 {
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <1>;

            fe0_out_be0: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&be0_in_fe0>;
            };
        };
    };
};

be0: display-backend@1e60000 {
    compatible = "allwinner,sun4i-a10-display-backend";
    reg = <0x01e60000 0x10000>;
    reg-names = "be";
    interrupts = <31>;
    clocks = <&ccu CLK_BUS_DE_BE>, <&ccu CLK_DE_BE>,
            <&ccu CLK_DRAM_DE_BE>;
    clock-names = "ahb", "mod",
                "ram";
    resets = <&ccu RST_BUS_DE_BE>;
    reset-names = "be";
    assigned-clocks = <&ccu CLK_DE_BE>;
    assigned-clock-rates = <300000000>;
    status = "disabled";

    ports {
        #address-cells = <1>;
        #size-cells = <0>;

        be0_in: port@0 {
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <0>;

            be0_in_fe0: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&fe0_out_be0>;
            };
        };

        be0_out: port@1 {
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <1>;

            be0_out_tcon0: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&tcon0_in_be0>;
            };
        };
    };
};

引脚分配

引脚 功能 说明
PD1-PD11 LCD 数据 RGB565 数据线 (F1C100s LCD D3-D15 映射)
PD13-PD17 LCD 数据 RGB565 数据线 (F1C100s LCD D19-D23 映射)
PD18 LCD_PCLK 像素时钟
PD20 LCD_HS 行同步
PD21 LCD_VS 列同步
PD19 SPI SCK 软件 SPI 时钟
PE4 SPI MOSI 软件 SPI 数据输出
PE11 SPI CS 片选 (低有效)
PE12 PWM0 背光 PWM 输出

PD0 和 PD12 用作 I2C, PD19 因 SPI SCK 占用被排除。

关键设备树配置

根节点下添加 pwm_backlight 和 spi_gpio(panel) :

pwm_backlight: backlight {
    compatible = "pwm-backlight";
    pwms = <&pwm 0 10000 0>;
    brightness-levels = <0 4 8 16 32 64 128 196 220 255>;
    default-brightness-level = <6>;
};

spi_gpio: spi-gpio {
    compatible = "spi-gpio";
    #address-cells = <1>;
    #size-cells = <0>;
    sck-gpios = <&pio 3 19 GPIO_ACTIVE_HIGH>;
    mosi-gpios = <&pio 4 4 GPIO_ACTIVE_HIGH>;
    cs-gpios = <&pio 4 11 GPIO_ACTIVE_LOW>;
    num-chipselects = <1>;
    status = "okay";

    panel@0 {
        compatible = "goldenmorning,t295h5c4001-panel";
        #address-cells = <1>;
        #size-cells = <0>;
        reg = <0>;
        spi-max-frequency = <100000>;
        backlight = <&pwm_backlight>;

        port@0 {
            reg = <0>;
            #address-cells = <1>;
            #size-cells = <0>;

            panel_input: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&tcon0_out_lcd>;
            };
        };
    };
};

其他重要节点:

&de {
    status = "okay";
};

&tcon0 {
    pinctrl-names = "default";
    pinctrl-0 = <&lcd_rgb565_no_de_pins>;
    status = "okay";
};

&tcon0_out {
    tcon0_out_lcd: endpoint@0 {
        reg = <0>;
        remote-endpoint = <&panel_input>;
    };
};

&fe0 {
    status = "okay";
};

&be0 {
    status = "okay";
};

&pio {
    pwm0_pe_pins: pwm0-pe-pins {
        pins = "PE12";
        function = "pwm0";
    };

    lcd_rgb565_no_de_pins: lcd-rgb565-no-de-pins {
        pins = "PD1", "PD2", "PD3", "PD4", "PD5",
               "PD6", "PD7", "PD8", "PD9", "PD10", "PD11",
               "PD13", "PD14", "PD15", "PD16", "PD17",
               "PD18", "PD20", "PD21";
        function = "lcd";
    };
};

&pwm {
    pinctrl-names = "default";
    pinctrl-0 = <&pwm0_pe_pins>;
    status = "okay";
};

内核配置

DRM 显示框架和面板驱动

Device Drivers --->
    Graphics support --->
        <*> Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)
            Supported DRM clients  --->
                [*] Enable legacy fbdev support for your modesetting driver
            <*> DRM Support for Allwinner A10 Display Engine
                [*]   Sun4i/Sun5i/Sun7i/Sun8i/Sun9i Display Backend
                [*]   Allwinner A10 Display Engine support
            Display Panels  --->
                <*> Sitronix ST7701 panel driver

       Frame buffer Devices  ---> 
            <*> Support for frame buffer device drivers  ---- 
            [*] Provide legacy /dev/fb* device

SPI 软件 GPIO 驱动

Device Drivers --->
    SPI support --->
        <*> GPIO-based bitbanging SPI Master

背光与 PWM

Device Drivers --->
    Graphics support --->
        [*] Backlight & LCD device support --->
            <*> Lowlevel Backlight controls
            <*> Generic PWM based Backlight support
    PWM Support --->
        <*> Allwinner PWM support

控制台

Device Drivers --->
    Console display driver support  ---> 
        [*] Framebuffer Console support
        -*-   Map the console to the primary display device
    [*] Bootup logo  --->                            

问题解决

1. 竖条纹无显示

根因: 厂商 STM32 代码使用 DE 模式 (C3h=0x10), 移植时直接照搬。但实际硬件未连接 DE 引脚, TCON 也未配置 DE 输出。ST7701S 在 DE 模式下等待 DE 信号, 永远收不到。

修复: gip_sequence 中 C3h 改为 0x11, HV 模式:

ST7701_WRITE(st7701, 0xC3, 0x11, 0x08);

2. 色彩异常

根因: MADCTL (0x36) 寄存器值 0x80 的颜色通道顺序与实际 RGB565 接线不匹配。

修复: MADCTL 改为 0x84 (bit2 置位, 交换 RGB 通道):

ST7701_WRITE(st7701, MIPI_DCS_SET_ADDRESS_MODE, 0x84);

3. 偏移

根因:

  1. gip_sequence 覆盖了 PORCTRL (0x07,0x02) 和 INVSEL (0x31,0x05) 为厂商 DE 模式值, 与 TCON 实际时序不匹配;
  2. htotal=438 低于 ST7701S 硬件最小值 512 PCLK/行 (RTNI 字段公式: PCLK = 512 + RTNI[4:0] × 16, RTNI 为 5 位无符号, 最小值 0 对应 512);
  3. 屏厂参数不可靠,无法直接使用。

修复:

  • 删除 gip_sequence 中 PORCTRL 和 INVSEL 的覆盖, 让 init_sequence 从 mode 自动计算;
  • HBP 从 20 改为 60, HFP 从 32 调整为 84, htotal 从 438 改为 512;
  • VBP 从 32 调整为 1, VFP 从 28 调整为 59, vtotal 保持 704 不变。

4. 冷启动帧截断

以下为 AI 推测。

根因: DRM 框架执行顺序为 prepare() → CRTC enable (TCON 启动) → enable()。prepare 阶段执行 sleep out (0x11) 时 TCON 尚未启动, ST7701S 没有外部 VSYNC 可锁定, 内部行计数器从随机位置开始运转。等到 TCON 启动输出 VSYNC 时, ST7701S 已经在错误位置运行, 不会自动重新同步, 导致画面从中间某行开始显示 (帧截断)。热启动正常是因为 ST7701S 未断电, 内部时序引擎保持运转, sleep out 后能快速恢复同步。

修复: 在 st7701_enable() 中 (此时 TCON 已运行) 做一次 sleep in → sleep out 循环, 让 ST7701S 重新锁定 VSYNC:

static int st7701_enable(struct drm_panel *panel)
{
    struct st7701 *st7701 = panel_to_st7701(panel);

    ST7701_WRITE(st7701, MIPI_DCS_ENTER_SLEEP_MODE, 0x00);
    msleep(50);
    ST7701_WRITE(st7701, MIPI_DCS_EXIT_SLEEP_MODE, 0x00);
    msleep(120);
    ST7701_WRITE(st7701, MIPI_DCS_SET_DISPLAY_ON, 0x00);

    return 0;
}

最终屏参

显示模式参数

static const struct drm_display_mode t295h5c4001_mode = {
    .clock        = 24000,

    .hdisplay     = 360,
    .hsync_start  = 360 + 84,           // HFP=84
    .hsync_end    = 360 + 84 + 8,       // HSW=8
    .htotal       = 360 + 84 + 8 + 60,  // HBP=60

    .vdisplay     = 640,
    .vsync_start  = 640 + 59,           // VFP=59
    .vsync_end    = 640 + 59 + 4,       // VSW=4
    .vtotal       = 640 + 59 + 4 + 1,   // VBP=1

    .width_mm     = 41,
    .height_mm    = 73,
    .flags        = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
    .type         = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
};

时序参数汇总:

参数 初始值 最终值 说明
clock 28000 24000 28MHz 文字不清晰, 改 24MHz
hdisplay 360 360 不变
HFP 50 84 增大 (补偿 htotal 提高到 512)
HSW 8 8 不变
HBP 20 60 等于 LCD_W_OFFSET
htotal 438 512 ST7701S 最小 PCLK/行
vdisplay 640 640 不变
VFP 28 59 增大 (补偿 VBP 减小)
VSW 4 4 不变
VBP 32 1 减小 (修正纵向偏移)
vtotal 704 704 不变
刷新率 ~90.7 Hz ~66.6 Hz 24000000 / (512 × 704)

gip_sequence 最终版本

static void t295h5c4001_gip_sequence(struct st7701 *st7701)
{
    st7701_switch_cmd_bkx(st7701, true, 0);

    // ST7701_WRITE(st7701, ST7701_CMD2_BK0_PORCTRL, 0x07, 0x02);
    // ST7701_WRITE(st7701, ST7701_CMD2_BK0_INVSEL, 0x31, 0x05);

    ST7701_WRITE(st7701, 0xC3, 0x11, 0x08);
    ST7701_WRITE(st7701, 0xCC, 0x10);

    st7701_switch_cmd_bkx(st7701, true, 1);
    ST7701_WRITE(st7701, 0xBB, 0x03);
    ST7701_WRITE(st7701, 0xBC, 0x75);
    ST7701_WRITE(st7701, 0xCD, 0x08);

    msleep(100);

    ST7701_WRITE(st7701, 0xE0, 0x00, 0x00, 0x02);
    ST7701_WRITE(st7701, 0xE1, 0x03, 0xA0, 0x00, 0x00, 0x02, 0xA0, 0x00,
                 0x00, 0x00, 0x33, 0x33);
    ST7701_WRITE(st7701, 0xE2, 0x22, 0x22, 0x33, 0x33, 0x88, 0xA0, 0x00,
                 0x00, 0x87, 0xA0, 0x00, 0x00);
    ST7701_WRITE(st7701, 0xE3, 0x00, 0x00, 0x22, 0x22);
    ST7701_WRITE(st7701, 0xE4, 0x44, 0x44);
    ST7701_WRITE(st7701, 0xE5, 0x04, 0x84, 0xA0, 0xA0, 0x06, 0x86, 0xA0,
                 0xA0, 0x08, 0x88, 0xA0, 0xA0, 0x0A, 0x8A, 0xA0, 0xA0);
    ST7701_WRITE(st7701, 0xE6, 0x00, 0x00, 0x22, 0x22);
    ST7701_WRITE(st7701, 0xE7, 0x44, 0x44);
    ST7701_WRITE(st7701, 0xE8, 0x03, 0x83, 0xA0, 0xA0, 0x05, 0x85, 0xA0,
                 0xA0, 0x07, 0x87, 0xA0, 0xA0, 0x09, 0x89, 0xA0, 0xA0);
    ST7701_WRITE(st7701, 0xEB, 0x00, 0x01, 0xE4, 0xE4, 0x88, 0x00, 0x40);
    ST7701_WRITE(st7701, 0xEC, 0x3C, 0x01);
    ST7701_WRITE(st7701, 0xED, 0xAB, 0x89, 0x76, 0x54, 0x02, 0xFF, 0xFF,
                 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0x45, 0x67, 0x98, 0xBA);
    ST7701_WRITE(st7701, 0xEF, 0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F);

    st7701_switch_cmd_bkx(st7701, true, 3);
    ST7701_WRITE(st7701, 0xE6, 0x16, 0x7C);
    ST7701_WRITE(st7701, 0xE8, 0x00, 0x0E);

    st7701_switch_cmd_bkx(st7701, true, 3);
    ST7701_WRITE(st7701, 0xE8, 0x00, 0x0C);
    ST7701_WRITE(st7701, 0xE8, 0x00, 0x00);

    st7701_switch_cmd_bkx(st7701, false, 0);

    ST7701_WRITE(st7701, MIPI_DCS_SET_ADDRESS_MODE, 0x84);
    ST7701_WRITE(st7701, MIPI_DCS_SET_PIXEL_FORMAT, 0x55);
    ST7701_WRITE(st7701, MIPI_DCS_EXIT_SLEEP_MODE);
    msleep(120);
}

结语

本次移植完成了 F1C100s SoC 平台上一款 ST7701S SPI+RGB565 屏幕在 Linux 7.2 的适配,历时约两天。得益于社区版 dtsi ,在内核和设备树配置上还算比较顺利,驱动是在上游 st7701 的驱动上修改而来。

posted @ 2026-09-02 21:02  DouglasX  阅读(3)  评论(0)    收藏  举报