PAT 1091 Acute Stroke
2025/12/22 7:48:20
/** * @struct lvds_panel_config * @brief LVDS面板配置数据结构 * * 存储特定尺寸面板的所有配置参数 */ struct lvds_panel_config { char name[32]; /**< 面板名称 */ u32 width; /**< 面板宽度(mm) */ u32 height; /**< 面板高度(mm) */ /* 显示时序参数 */ struct drm_display_mode mode; /**< DRM显示模式 */ struct videomode vm; /**< 视频模式 */ /* LVDS配置 */ u32 format; /**< LVDS格式 */ u32 bus_format; /**< 总线格式 */ u32 bpc; /**< 每颜色位数 */ bool dual_channel; /**< 是否双通道 */ bool data_swap; /**< 是否数据交换 */ /* 电源时序 */ u32 power_on_delay; /**< 上电延迟(ms) */ u32 reset_delay; /**< 复位延迟(ms) */ u32 init_delay; /**< 初始化延迟(ms) */ /* GPIO极性 */ bool enable_active_high; /**< 使能引脚高有效 */ bool reset_active_high; /**< 复位引脚高有效 */ bool bl_active_high; /**< 背光引脚高有效 */ /* 背光配置 */ u32 max_brightness; /**< 最大亮度 */ u32 default_brightness; /**< 默认亮度 */ u32 pwm_period; /**< PWM周期(ns) */ }; /** * @struct rk3568_lvds * @brief RK3568 LVDS驱动主数据结构 */ struct rk3568_lvds { struct device *dev; struct drm_device *drm_dev; struct drm_panel panel; struct drm_bridge bridge; struct drm_connector connector; struct drm_encoder *encoder; /* 硬件资源 */ struct regmap *grf; void __iomem *regs; struct phy *phy; struct clk *pclk; struct clk *hclk; struct reset_control *reset; /* GPIO控制 */ struct regulator *vcc_supply; struct regulator *vccio_supply; struct gpio_desc *enable_gpio; struct gpio_desc *reset_gpio; struct gpio_desc *bl_enable_gpio; struct backlight_device *backlight; struct pwm_device *pwm; /* 面板配置 */ struct lvds_panel_config *config; /**< 当前面板配置 */ struct lvds_panel_config *configs; /**< 支持的面板配置数组 */ int num_configs; /**< 配置数量 */ int current_config_idx; /**< 当前配置索引 */ /* 状态标志 */ bool prepared; bool enabled; bool phy_enabled; /* 工作队列 */ struct work_struct reset_work; struct delayed_work enable_work; /* 调试信息 */ struct dentry *debugfs_dir; }; /* 常用面板配置数据库 */ static struct lvds_panel_config common_panels[] = { /* 7寸 1280x800 通用LVDS屏 */ { .name = "7-inch-1280x800", .width = 154, .height = 85, .mode = { .clock = 72000, .hdisplay = 1280, .hsync_start = 1280 + 48, .hsync_end = 1280 + 48 + 32, .htotal = 1280 + 48 + 32 + 80, .vdisplay = 800, .vsync_start = 800 + 3, .vsync_end = 800 + 3 + 6, .vtotal = 800 + 3 + 6 + 14, .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, .type = DRM_MODE_TYPE_DRIVER, }, .format = LVDS_8BIT_MODE_FORMAT_1, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, .bpc = 8, .dual_channel = false, .data_swap = false, .power_on_delay = 20, .reset_delay = 10, .init_delay = 120, .enable_active_high = true, .reset_active_high = false, .bl_active_high = true, .max_brightness = 255, .default_brightness = 200, .pwm_period = 25000, }, /* 10.1寸 1280x800 LVDS屏 */ { .name = "10.1-inch-1280x800", .width = 216, .height = 135, .mode = { .clock = 71000, .hdisplay = 1280, .hsync_start = 1280 + 64, .hsync_end = 1280 + 64 + 128, .htotal = 1280 + 64 + 128 + 192, .vdisplay = 800, .vsync_start = 800 + 3, .vsync_end = 800 + 3 + 10, .vtotal = 800 + 3 + 10 + 20, .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, .type = DRM_MODE_TYPE_DRIVER, }, .format = LVDS_8BIT_MODE_FORMAT_1, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, .bpc = 8, .dual_channel = false, .data_swap = false, .power_on_delay = 25, .reset_delay = 15, .init_delay = 150, .enable_active_high = true, .reset_active_high = false, .bl_active_high = true, .max_brightness = 255, .default_brightness = 200, .pwm_period = 25000, }, /* 10.1寸 1024x600 LVDS屏 */ { .name = "10.1-inch-1024x600", .width = 216, .height = 135, .mode = { .clock = 50000, .hdisplay = 1024, .hsync_start = 1024 + 160, .hsync_end = 1024 + 160 + 70, .htotal = 1024 + 160 + 70 + 160, .vdisplay = 600, .vsync_start = 600 + 12, .vsync_end = 600 + 12 + 13, .vtotal = 600 + 12 + 13 + 12, .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, .type = DRM_MODE_TYPE_DRIVER, }, .format = LVDS_8BIT_MODE_FORMAT_1, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, .bpc = 8, .dual_channel = false, .data_swap = false, .power_on_delay = 20, .reset_delay = 10, .init_delay = 120, .enable_active_high = true, .reset_active_high = false, .bl_active_high = true, .max_brightness = 255, .default_brightness = 200, .pwm_period = 25000, }, /* 19寸 1280x1024 LVDS屏 */ { .name = "19-inch-1280x1024", .width = 376, .height = 301, .mode = { .clock = 108000, .hdisplay = 1280, .hsync_start = 1280 + 48, .hsync_end = 1280 + 48 + 112, .htotal = 1280 + 48 + 112 + 248, .vdisplay = 1024, .vsync_start = 1024 + 1, .vsync_end = 1024 + 1 + 3, .vtotal = 1024 + 1 + 3 + 38, .flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_PHSYNC, .type = DRM_MODE_TYPE_DRIVER, }, .format = LVDS_8BIT_MODE_FORMAT_1, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, .bpc = 8, .dual_channel = false, .data_swap = false, .power_on_delay = 30, .reset_delay = 20, .init_delay = 200, .enable_active_high = true, .reset_active_high = true, .bl_active_high = true, .max_brightness = 255, .default_brightness = 180, .pwm_period = 25000, }, /* 19寸 1440x900 LVDS屏 */ { .name = "19-inch-1440x900", .width = 408, .height = 255, .mode = { .clock = 106500, .hdisplay = 1440, .hsync_start = 1440 + 80, .hsync_end = 1440 + 80 + 152, .htotal = 1440 + 80 + 152 + 232, .vdisplay = 900, .vsync_start = 900 + 1, .vsync_end = 900 + 1 + 3, .vtotal = 900 + 1 + 3 + 28, .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, .type = DRM_MODE_TYPE_DRIVER, }, .format = LVDS_8BIT_MODE_FORMAT_1, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, .bpc = 8, .dual_channel = false, .data_swap = false, .power_on_delay = 30, .reset_delay = 20, .init_delay = 200, .enable_active_high = true, .reset_active_high = false, .bl_active_high = true, .max_brightness = 255, .default_brightness = 180, .pwm_period = 25000, }, /* 21.5寸 1920x1080 LVDS屏 */ { .name = "21.5-inch-1920x1080", .width = 476, .height = 268, .mode = { .clock = 148500, .hdisplay = 1920, .hsync_start = 1920 + 88, .hsync_end = 1920 + 88 + 44, .htotal = 1920 + 88 + 44 + 148, .vdisplay = 1080, .vsync_start = 1080 + 4, .vsync_end = 1080 + 4 + 5, .vtotal = 1080 + 4 + 5 + 36, .flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_PHSYNC, .type = DRM_MODE_TYPE_DRIVER, }, .format = LVDS_8BIT_MODE_FORMAT_1, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, .bpc = 8, .dual_channel = true, /* 高分辨率需要双通道 */ .data_swap = false, .power_on_delay = 50, .reset_delay = 30, .init_delay = 250, .enable_active_high = true, .reset_active_high = true, .bl_active_high = true, .max_brightness = 255, .default_brightness = 150, .pwm_period = 25000, }, };/** * @brief 根据EDID自动检测面板类型 * @param lvds LVDS驱动私有数据 * @param edid EDID数据 * @return 成功返回配置索引,失败返回-1 * * 通过读取EDID信息自动识别连接的显示器型号 */ static int rk3568_lvds_detect_panel_by_edid(struct rk3568_lvds *lvds, const struct edid *edid) { u32 width_mm, height_mm; u32 hdisplay, vdisplay; int i; if (!edid) { LVDS_ERROR("EDID数据为空\n"); return -1; } /* 从EDID获取显示器尺寸 */ width_mm = edid->width_cm * 10; height_mm = edid->height_cm * 10; /* 从首选时序获取分辨率 */ hdisplay = edid->detailed_timings[0].data.hactive_lo | ((edid->detailed_timings[0].data.hactive_hi & 0xf0) << 4); vdisplay = edid->detailed_timings[0].data.vactive_lo | ((edid->detailed_timings[0].data.vactive_hi & 0xf0) << 4); LVDS_INFO("EDID检测: %dx%d mm, %dx%d pixels\n", width_mm, height_mm, hdisplay, vdisplay); /* 在配置数据库中查找匹配的面板 */ for (i = 0; i < lvds->num_configs; i++) { struct lvds_panel_config *cfg = &lvds->configs[i]; /* 检查分辨率匹配 */ if (cfg->mode.hdisplay == hdisplay && cfg->mode.vdisplay == vdisplay) { /* 检查尺寸匹配(允许±10%误差) */ int width_diff = abs((int)cfg->width - (int)width_mm); int height_diff = abs((int)cfg->height - (int)height_mm); if (width_diff < cfg->width * 0.1 && height_diff < cfg->height * 0.1) { LVDS_INFO("自动检测到面板: %s\n", cfg->name); return i; } } } LVDS_WARN("未找到精确匹配的面板,使用最接近的配置\n"); /* 查找最接近的分辨率 */ int best_match = -1; int min_diff = INT_MAX; for (i = 0; i < lvds->num_configs; i++) { struct lvds_panel_config *cfg = &lvds->configs[i]; int diff = abs((int)cfg->mode.hdisplay - (int)hdisplay) + abs((int)cfg->mode.vdisplay - (int)vdisplay); if (diff < min_diff) { min_diff = diff; best_match = i; } } if (best_match >= 0) { LVDS_INFO("使用最接近的面板: %s\n", lvds->configs[best_match].name); } return best_match; } /** * @brief 根据GPIO检测面板类型 * @param lvds LVDS驱动私有数据 * @return 成功返回配置索引,失败返回-1 * * 通过检测GPIO电平或使用跳线识别面板类型 */ static int rk3568_lvds_detect_panel_by_gpio(struct rk3568_lvds *lvds) { struct device *dev = lvds->dev; struct device_node *np = dev->of_node; int panel_id_gpios[4]; int panel_id = 0; int num_gpios = 0; int i, ret; /* 从设备树读取面板ID GPIO */ for (i = 0; i < 4; i++) { char prop_name[32]; snprintf(prop_name, sizeof(prop_name), "panel-id-gpios%d", i); ret = of_get_named_gpio(np, prop_name, 0); if (ret >= 0) { panel_id_gpios[num_gpios] = ret; num_gpios++; } } if (num_gpios == 0) { LVDS_DEBUG(1, "未找到面板ID GPIO\n"); return -1; } /* 读取GPIO状态 */ for (i = 0; i < num_gpios; i++) { int value; ret = gpio_request(panel_id_gpios[i], "panel-id"); if (ret) { LVDS_ERROR("无法请求GPIO%d: %d\n", panel_id_gpios[i], ret); continue; } ret = gpio_direction_input(panel_id_gpios[i]); if (ret) { LVDS_ERROR("无法设置GPIO%d为输入: %d\n", panel_id_gpios[i], ret); gpio_free(panel_id_gpios[i]); continue; } value = gpio_get_value(panel_id_gpios[i]); panel_id |= (value << i); gpio_free(panel_id_gpios[i]); LVDS_DEBUG(2, "面板ID GPIO%d: %d\n", i, value); } LVDS_INFO("面板ID检测: 0x%x (使用%d个GPIO)\n", panel_id, num_gpios); /* 根据ID选择配置 */ if (panel_id < lvds->num_configs) { LVDS_INFO("根据GPIO ID选择面板: %s\n", lvds->configs[panel_id].name); return panel_id; } LVDS_WARN("面板ID超出范围,使用默认配置\n"); return 0; } /** * @brief 从设备树解析面板配置 * @param lvds LVDS驱动私有数据 * @return 成功返回0,失败返回错误码 */ static int rk3568_lvds_parse_panel_config(struct rk3568_lvds *lvds) { struct device *dev = lvds->dev; struct device_node *np = dev->of_node; struct device_node *panel_np; const char *panel_name; int ret, i; LVDS_DEBUG(1, "解析面板配置\n"); /* 查找面板子节点 */ panel_np = of_get_child_by_name(np, "panel"); if (!panel_np) { LVDS_DEBUG(1, "无面板子节点,使用通用配置\n"); /* 使用通用配置数据库 */ lvds->configs = common_panels; lvds->num_configs = ARRAY_SIZE(common_panels); /* 尝试自动检测面板 */ lvds->current_config_idx = rk3568_lvds_detect_panel_by_gpio(lvds); if (lvds->current_config_idx < 0) { /* 使用第一个配置作为默认 */ lvds->current_config_idx = 0; LVDS_INFO("使用默认面板: %s\n", lvds->configs[0].name); } lvds->config = &lvds->configs[lvds->current_config_idx]; return 0; } /* 从设备树读取面板名称 */ ret = of_property_read_string(panel_np, "panel-name", &panel_name); if (ret) { LVDS_WARN("未指定面板名称,使用默认\n"); panel_name = "default"; } /* 分配配置数组 */ lvds->num_configs = 1; lvds->configs = devm_kzalloc(dev, sizeof(struct lvds_panel_config), GFP_KERNEL); if (!lvds->configs) return -ENOMEM; lvds->config = &lvds->configs[0]; /* 设置面板名称 */ strncpy(lvds->config->name, panel_name, sizeof(lvds->config->name) - 1); /* 解析面板尺寸 */ ret = of_property_read_u32(panel_np, "width-mm", &lvds->config->width); if (ret) { LVDS_WARN("未指定面板宽度,使用默认值\n"); lvds->config->width = 154; /* 7寸默认 */ } ret = of_property_read_u32(panel_np, "height-mm", &lvds->config->height); if (ret) { LVDS_WARN("未指定面板高度,使用默认值\n"); lvds->config->height = 85; /* 7寸默认 */ } /* 解析显示时序 */ ret = of_get_drm_display_mode(panel_np, &lvds->config->mode, OF_USE_NATIVE_MODE, NULL); if (ret) { LVDS_ERROR("无法解析显示模式\n"); return ret; } /* 设置模式名称 */ snprintf(lvds->config->mode.name, DRM_DISPLAY_MODE_LEN, "%dx%d", lvds->config->mode.hdisplay, lvds->config->mode.vdisplay); /* 转换为videomode */ drm_display_mode_to_videomode(&lvds->config->mode, &lvds->config->vm); /* 解析LVDS配置 */ const char *format_str; ret = of_property_read_string(panel_np, "format", &format_str); if (!ret) { if (!strcmp(format_str, "jeida-24")) lvds->config->format = LVDS_8BIT_MODE_FORMAT_1; else if (!strcmp(format_str, "vesa-24")) lvds->config->format = LVDS_8BIT_MODE_FORMAT_2; else if (!strcmp(format_str, "jeida-18")) lvds->config->format = LVDS_6BIT_MODE; else lvds->config->format = LVDS_8BIT_MODE_FORMAT_1; } lvds->config->dual_channel = of_property_read_bool(panel_np, "dual-channel"); lvds->config->data_swap = of_property_read_bool(panel_np, "data-swap"); /* 解析电源时序 */ of_property_read_u32(panel_np, "power-on-delay-ms", &lvds->config->power_on_delay); of_property_read_u32(panel_np, "reset-delay-ms", &lvds->config->reset_delay); of_property_read_u32(panel_np, "init-delay-ms", &lvds->config->init_delay); /* 设置默认值 */ if (!lvds->config->power_on_delay) lvds->config->power_on_delay = 20; if (!lvds->config->reset_delay) lvds->config->reset_delay = 10; if (!lvds->config->init_delay) lvds->config->init_delay = 120; /* 解析GPIO极性 */ lvds->config->enable_active_high = !of_property_read_bool(panel_np, "enable-active-low"); lvds->config->reset_active_high = !of_property_read_bool(panel_np, "reset-active-low"); lvds->config->bl_active_high = !of_property_read_bool(panel_np, "bl-active-low"); /* 解析背光配置 */ of_property_read_u32(panel_np, "max-brightness", &lvds->config->max_brightness); of_property_read_u32(panel_np, "default-brightness", &lvds->config->default_brightness); of_property_read_u32(panel_np, "pwm-period-ns", &lvds->config->pwm_period); /* 设置背光默认值 */ if (!lvds->config->max_brightness) lvds->config->max_brightness = 255; if (!lvds->config->default_brightness) lvds->config->default_brightness = 200; if (!lvds->config->pwm_period) lvds->config->pwm_period = 25000; LVDS_INFO("面板配置: %s, %dx%d, %dx%d mm\n", lvds->config->name, lvds->config->mode.hdisplay, lvds->config->mode.vdisplay, lvds->config->width, lvds->config->height); of_node_put(panel_np); return 0; }/** * @brief 应用面板配置 * @param lvds LVDS驱动私有数据 * @param config_idx 配置索引 * @return 成功返回0,失败返回错误码 * * 应用指定面板配置到硬件 */ static int rk3568_lvds_apply_panel_config(struct rk3568_lvds *lvds, int config_idx) { struct lvds_panel_config *cfg; if (config_idx < 0 || config_idx >= lvds->num_configs) { LVDS_ERROR("无效的配置索引: %d\n", config_idx); return -EINVAL; } cfg = &lvds->configs[config_idx]; lvds->config = cfg; lvds->current_config_idx = config_idx; LVDS_INFO("应用面板配置: %s\n", cfg->name); /* 更新DRM显示模式 */ memcpy(&lvds->mode, &cfg->mode, sizeof(struct drm_display_mode)); memcpy(&lvds->vm, &cfg->vm, sizeof(struct videomode)); /* 更新LVDS配置 */ lvds->format = cfg->format; lvds->bus_format = cfg->bus_format; lvds->bpc = cfg->bpc; lvds->dual_channel = cfg->dual_channel; lvds->data_swap = cfg->data_swap; /* 重新配置硬件 */ if (lvds->prepared) { int ret; /* 先禁用显示 */ rk3568_lvds_panel_disable(&lvds->panel); /* 重新配置寄存器 */ ret = rk3568_lvds_registers_config(lvds); if (ret) { LVDS_ERROR("重新配置寄存器失败: %d\n", ret); return ret; } /* 重新使能显示 */ rk3568_lvds_panel_enable(&lvds->panel); } return 0; } /** * @brief 获取支持的面板列表 * @param lvds LVDS驱动私有数据 * @param buf 输出缓冲区 * @param size 缓冲区大小 * @return 写入的字节数 */ static ssize_t rk3568_lvds_list_panels(struct rk3568_lvds *lvds, char *buf, size_t size) { ssize_t len = 0; int i; len += snprintf(buf + len, size - len, "支持的LVDS面板列表:\n" "====================\n"); for (i = 0; i < lvds->num_configs; i++) { struct lvds_panel_config *cfg = &lvds->configs[i]; const char *current = (i == lvds->current_config_idx) ? "*" : " "; len += snprintf(buf + len, size - len, "%s [%d] %s\n" " 分辨率: %dx%d @ %d Hz\n" " 尺寸: %dx%d mm\n" " 格式: %s, %s通道\n" " 当前: %s\n\n", current, i, cfg->name, cfg->mode.hdisplay, cfg->mode.vdisplay, drm_mode_vrefresh(&cfg->mode), cfg->width, cfg->height, (cfg->format == LVDS_8BIT_MODE_FORMAT_1) ? "JEIDA-24" : (cfg->format == LVDS_8BIT_MODE_FORMAT_2) ? "VESA-24" : "JEIDA-18", cfg->dual_channel ? "双" : "单", (i == lvds->current_config_idx) ? "是" : "否"); } return len; } /** * @brief 切换面板配置 * @param lvds LVDS驱动私有数据 * @param panel_name 面板名称 * @return 成功返回0,失败返回错误码 */ static int rk3568_lvds_switch_panel(struct rk3568_lvds *lvds, const char *panel_name) { int i; if (!panel_name || !panel_name[0]) { LVDS_ERROR("面板名称不能为空\n"); return -EINVAL; } /* 查找匹配的面板 */ for (i = 0; i < lvds->num_configs; i++) { if (strcmp(lvds->configs[i].name, panel_name) == 0) { LVDS_INFO("切换到面板: %s\n", panel_name); return rk3568_lvds_apply_panel_config(lvds, i); } } /* 尝试按索引切换 */ if (isdigit(panel_name[0])) { int idx = simple_strtol(panel_name, NULL, 10); if (idx >= 0 && idx < lvds->num_configs) { LVDS_INFO("按索引切换到面板[%d]\n", idx); return rk3568_lvds_apply_panel_config(lvds, idx); } } LVDS_ERROR("未找到面板: %s\n", panel_name); return -ENODEV; }// rk3568-lvds-multi-panel.dts // 支持多种面板的设备树配置 / { compatible = "rockchip,rk3568"; lvds_panels: lvds-panels { compatible = "rockchip,lvds-panels"; // 面板选择GPIO(可选) panel-id-gpios0 = <&gpio0 RK_PC0 GPIO_ACTIVE_HIGH>; panel-id-gpios1 = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>; // 默认面板 default-panel = <&panel_7inch>; // 7寸面板配置 panel_7inch: panel-7inch { panel-name = "7-inch-1280x800"; width-mm = <154>; height-mm = <85>; panel-timing { clock-frequency = <72000000>; hactive = <1280>; vactive = <800>; hfront-porch = <48>; hback-porch = <80>; hsync-len = <32>; vfront-porch = <3>; vback-porch = <14>; vsync-len = <6>; hsync-active = <0>; vsync-active = <0>; }; format = "jeida-24"; bus-format = "rgb888"; bpc = <8>; power-on-delay-ms = <20>; reset-delay-ms = <10>; init-delay-ms = <120>; enable-active-high; reset-active-low; bl-active-high; max-brightness = <255>; default-brightness = <200>; pwm-period-ns = <25000>; }; // 10.1寸面板配置 panel_10inch: panel-10inch { panel-name = "10.1-inch-1280x800"; width-mm = <216>; height-mm = <135>; panel-timing { clock-frequency = <71000>; hactive = <1280>; vactive = <800>; hfront-porch = <64>; hback-porch = <192>; hsync-len = <128>; vfront-porch = <3>; vback-porch = <20>; vsync-len = <10>; hsync-active = <0>; vsync-active = <0>; }; format = "jeida-24"; bus-format = "rgb888"; bpc = <8>; power-on-delay-ms = <25>; reset-delay-ms = <15>; init-delay-ms = <150>; enable-active-high; reset-active-low; bl-active-high; max-brightness = <255>; default-brightness = <200>; pwm-period-ns = <25000>; }; // 19寸面板配置 panel_19inch: panel-19inch { panel-name = "19-inch-1280x1024"; width-mm = <376>; height-mm = <301>; panel-timing { clock-frequency = <108000>; hactive = <1280>; vactive = <1024>; hfront-porch = <48>; hback-porch = <248>; hsync-len = <112>; vfront-porch = <1>; vback-porch = <38>; vsync-len = <3>; hsync-active = <1>; vsync-active = <1>; }; format = "jeida-24"; bus-format = "rgb888"; bpc = <8>; dual-channel; power-on-delay-ms = <30>; reset-delay-ms = <20>; init-delay-ms = <200>; enable-active-high; reset-active-high; bl-active-high; max-brightness = <255>; default-brightness = <180>; pwm-period-ns = <25000>; }; }; lvds: lvds@fdfc0000 { compatible = "rockchip,rk3568-lvds"; reg = <0x0 0xfdfc0000 0x0 0x10000>; clocks = <&cru PCLK_LVDS>, <&cru HCLK_LVDS>; clock-names = "pclk", "hclk"; resets = <&cru SRST_P_LVDS>, <&cru SRST_H_LVDS>; reset-names = "reset", "reset-h"; phys = <&video_phy>; phy-names = "dphy"; rockchip,grf = <&grf>; status = "okay"; ports { port@1 { reg = <1>; lvds_out_panel: endpoint { remote-endpoint = <&panel_in>; }; }; }; }; vop: vop@fe040000 { compatible = "rockchip,rk3568-vop"; reg = <0x0 0xfe040000 0x0 0x3000>; interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>, <&cru DCLK_VOP0>; clock-names = "aclk", "hclk", "dclk_vop0"; resets = <&cru SRST_A_VOP>, <&cru SRST_H_VOP>, <&cru SRST_D_VOP0>; reset-names = "axi", "ahb", "dclk"; iommus = <&vop_mmu>; status = "okay"; vop_out: ports { #address-cells = <1>; #size-cells = <0>; port@0 { reg = <0>; #address-cells = <1>; #size-cells = <0>; vop_out_lvds: endpoint@0 { reg = <0>; remote-endpoint = <&lvds_in_vop>; }; }; }; }; video_phy: video-phy@fdfb0000 { compatible = "rockchip,rk3568-video-phy"; reg = <0x0 0xfdfb0000 0x0 0x10000>; clocks = <&cru PCLK_VIDEO_PHY>; clock-names = "pclk"; #phy-cells = <0>; status = "okay"; }; };/** * @brief 调试面板切换操作 * @param file 文件 * @param user_buf 用户缓冲区 * @param count 字节数 * @param ppos 文件位置 * @return 读取的字节数 */ static ssize_t rk3568_lvds_panel_switch_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct rk3568_lvds *lvds = file->private_data; char buf[64]; char *panel_name; int ret; if (count >= sizeof(buf)) return -EINVAL; if (copy_from_user(buf, user_buf, count)) return -EFAULT; buf[count] = '\0'; /* 去除换行符 */ panel_name = strim(buf); if (strlen(panel_name) == 0) { /* 显示当前面板 */ char info[256]; ssize_t len; len = snprintf(info, sizeof(info), "当前面板: %s [%d]\n" "分辨率: %dx%d @ %d Hz\n" "像素时钟: %d kHz\n" "双通道: %s\n", lvds->config->name, lvds->current_config_idx, lvds->config->mode.hdisplay, lvds->config->mode.vdisplay, drm_mode_vrefresh(&lvds->config->mode), lvds->config->mode.clock, lvds->config->dual_channel ? "是" : "否"); return simple_read_from_buffer(user_buf, count, ppos, info, len); } /* 切换面板 */ ret = rk3568_lvds_switch_panel(lvds, panel_name); if (ret) { char err_msg[128]; ssize_t len; len = snprintf(err_msg, sizeof(err_msg), "切换面板失败: %s\n", panel_name); return simple_read_from_buffer(user_buf, count, ppos, err_msg, len); } return count; } /** * @brief 调试面板列表读取 * @param file 文件 * @param user_buf 用户缓冲区 * @param count 字节数 * @param ppos 文件位置 * @return 读取的字节数 */ static ssize_t rk3568_lvds_panels_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct rk3568_lvds *lvds = file->private_data; char *buf; ssize_t len; buf = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM; len = rk3568_lvds_list_panels(lvds, buf, PAGE_SIZE); if (len < 0) { kfree(buf); return len; } len = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return len; } /* 创建更多调试文件 */ static void rk3568_lvds_debugfs_init(struct rk3568_lvds *lvds) { struct dentry *root; root = debugfs_create_dir("rk3568_lvds", NULL); if (IS_ERR(root)) return; lvds->debugfs_dir = root; /* 面板列表 */ debugfs_create_file("panels", 0444, root, lvds, &rk3568_lvds_panels_fops); /* 面板切换 */ debugfs_create_file("panel_switch", 0644, root, lvds, &rk3568_lvds_panel_switch_fops); /* 当前配置 */ debugfs_create_u32("current_panel", 0444, root, &lvds->current_config_idx); /* 时序参数 */ debugfs_create_x32("hdisplay", 0444, root, (u32 *)&lvds->config->mode.hdisplay); debugfs_create_x32("vdisplay", 0444, root, (u32 *)&lvds->config->mode.vdisplay); debugfs_create_x32("clock", 0444, root, (u32 *)&lvds->config->mode.clock); /* LVDS配置 */ debugfs_create_x32("format", 0444, root, &lvds->format); debugfs_create_bool("dual_channel", 0444, root, &lvds->dual_channel); debugfs_create_bool("data_swap", 0444, root, &lvds->data_swap); } /* 文件操作结构 */ static const struct file_operations rk3568_lvds_panels_fops = { .owner = THIS_MODULE, .read = rk3568_lvds_panels_read, .llseek = default_llseek, }; static const struct file_operations rk3568_lvds_panel_switch_fops = { .owner = THIS_MODULE, .write = rk3568_lvds_panel_switch_write, .llseek = default_llseek, };#!/bin/bash # 多面板使用示例 echo "=== RK3568 LVDS多面板驱动使用 ===" # 1. 检查当前面板 echo "1. 检查当前面板:" cat /sys/kernel/debug/rk3568_lvds/panels # 2. 查看当前配置 echo "2. 当前配置:" cat /sys/kernel/debug/rk3568_lvds/panel_switch # 3. 切换面板(通过名称) echo "3. 切换到10.1寸面板:" echo "10.1-inch-1280x800" > /sys/kernel/debug/rk3568_lvds/panel_switch # 4. 切换面板(通过索引) echo "4. 切换到19寸面板(索引2):" echo "2" > /sys/kernel/debug/rk3568_lvds/panel_switch # 5. 重启显示服务应用新配置 echo "5. 重启显示服务:" systemctl restart lvds.service # 6. 验证新配置 echo "6. 验证配置:" cat /sys/class/drm/card0-LVDS-1/modes cat /sys/class/backlight/*/actual_brightness
第六部分总结:
重构了数据结构,支持多种尺寸和分辨率的面板
实现了7寸、10.1寸、19寸、21.5寸等多种常用面板的配置
添加了面板自动检测功能(EDID/GPIO)
实现了动态面板切换功能
提供了完整的设备树多面板配置示例
增强了调试接口,支持面板列表查看和切换
现在驱动可以支持:
7寸 1280x800
10.1寸 1280x800
10.1寸 1024x600
19寸 1280x1024
19寸 1440x900
21.5寸 1920x1080
并且可以轻松扩展支持更多面板型号。