显示异常之黑白屏黑屏不黑

      接到了一个在RK3588s平台上点亮一款HDMI2eDP的黑白屏,本以为是小菜一碟,看规格发现大意了。分辨率是2560*4320,看着这种不常见的分辨率就头大,不由得想起来之前在RK3568平台上尝试点的一块屏。

        在RK3568平台上,要点的一款分辨率为2560*3600*23fps的eDP屏幕。点之前查看了RK3568的eDP规格:

 虽然分辨率超过了规格,但数据量小于规格,当时天真的以为只要数据量小于规格最大值,就能支持,没想到RK3568的VP在冷冷的看着我:小伙子,你太天真了!按照常规方式,该检查的都检查了,屏幕就是不亮!

static const struct vop2_data rk3568_vop = {
	.version = VOP_VERSION(0x40, 0x15),
	.nr_vps = 3,
	.nr_mixers = 5,
	.nr_gammas = 1,
	.max_input = { 4096, 2304 },
	.max_output = { 4096, 2304 },
	.ctrl = &rk3568_vop_ctrl,
	.grf_ctrl = &rk3568_grf_ctrl,
	.axi_intr = rk3568_vop_axi_intr,
	.nr_axi_intr = ARRAY_SIZE(rk3568_vop_axi_intr),
	.vp = rk3568_vop_video_ports,
	.wb = &rk3568_vop_wb_data,
	.layer = rk3568_vop_layers,
	.nr_layers = ARRAY_SIZE(rk3568_vop_layers),
	.win = rk3568_vop_win_data,
	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
	.dump_regs = rk3568_dump_regs,
	.dump_regs_size = ARRAY_SIZE(rk3568_dump_regs),
};

        瞧瞧,最大只有2304,能亮才怪呢! 有了上次的教训,这次老老实实的查了平台资料和源码,一切都符合,看起来事情要顺利起来了!

        由于平台端看到的是HDMI接口,基本上不需要做修改,如果在平台的支持范围内,插入HDMI cable就应该亮起来。显然我没有那么幸运,屏并没有如愿亮起来,检查debug fs,发现并没有对应屏规格的mode被创建。没有办法,只能通过log检查整个HDMI插入检测流程了:

HPD检测正常,插入事件顺利获取!

EDID读取正常,Header和Check sum检查顺利Pass!

EDID解析正……,等等,为什么Hactive、Vactive只有12bit,那最大只能到4096啊,我的4320可怎么整!查看EDID标准,果不其然,EDID DTB(Detailed Timing Descriptions)对此有描述:

图中红色方框选中的为Hactive的定义,只有12个bit,可见现在主流的EDID对detail timing的支持最多也只能做到4096*4096。相信新版的EDID能解决这个限制,毕竟,4K虽然是主流,但8K的电视和显示器也慢慢多了起来。

现在面临两个选择:

 一是升级EDID,升级DRM EDID相关驱动,升级HDMI2EDP屏使用的EDID内容和版本。要想做到这两点,首先要拿到高版本的EDID规格。很遗憾,网上找了半天也没有找到!然后动手去升级DRM EDID驱动,先假设花了很大的功夫,能过做到。最后还要升级屏中的EDID,显然,这条有点困难,属于不可控行为。看起来这个选择困难大,不确定性多,只能作罢!

二是只把EDID做ID,看到这款屏的EDID,就创建与之对应的mode。说干就干,查看drm_edid.c,很容易就找到了切入点,函数drm_mode_detailed, 看它的注释就觉得很亲切:“create a new mode from an EDID detailed timing section”,简直是量身定做:

/**
 * drm_mode_detailed - create a new mode from an EDID detailed timing section
 * @dev: DRM device (needed to create new mode)
 * @edid: EDID block
 * @timing: EDID detailed timing info
 * @quirks: quirks to apply
 *
 * An EDID detailed timing block contains enough info for us to create and
 * return a new struct drm_display_mode.
 */
static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
						  struct edid *edid,
						  struct detailed_timing *timing,
						  u32 quirks)

判断是否为这款屏也找到了切入点,EDID第8~11个字节做了定义(VID/PID):

废话不多说,直接上简单粗暴的修改:

	//20230422, create mode for mfg id: 0x32/0x8d
	if( edid->mfg_id[0] == 0x32 && edid->mfg_id[1] == 0x8d)
	{
		//hardcode timing for 10.1 AUO panel
		hactive = 2560;
		vactive = 4320;
		timing->pixel_clock = 27320;
		hsync_pulse_width = 32;
		hsync_offset = 48;
		hblank = 160;
		vsync_pulse_width = 10;
		vsync_offset = 3;
		vblank = 47;
	}

改好代码后,就是肌肉记忆了:编译,刷机,开机,插cable。虽然改的简单粗暴,但效果杠杠的,它亮起来了!

它亮起来了!很高兴,虽然看上去有点怪怪的,但先不管了,交差喽! 发送改后的Image,然后该干嘛干嘛去了。

然后事实有一次教育了我:不能过早开香槟,不能在终场哨吹响前跳舞。还有,人的感觉往往很犀利,感觉不对劲它就是有问题。有显示的屏,字怎么偏黄,隔着屏幕怎么能看到草台桌子的纹理呢!果然,问题马上就报过来了:漏光!说字不够白,背景不够黑!

棘手的问题,往往是看起来对但有那么一丢丢不对,这个显示是有了,画面也没有花,屏幕也没有跑偏,问题出在哪里呢?

Timing估计不会有这方面的影响,但理论归理论,还是要实际验证一下。在屏幕规格范围呢,调了几组,显示画面没有变化!不要问我你一个HDMI的屏,如何调整timing?改EDID,不存在的,转板厂不支持!只能使用hardcode大法!Hblank和Vblank组合试了一遍,没有丝毫影响,只能放弃timing了!

一通猛试无效后,开始思考问题了!漏光,背景不够黑?黑色在RGB中,应该是全0,如果黑色不够黑,那么RGB中,一定是有非零的存在,让液晶排列不如预期,从而有光漏出来?看起来分析的有道理,为什么RGB中会有非零的存在呢?R会不会不是R呢?想到这里,不由得想起来了彩色图像的记录格式。

彩色图像的记录格式,常见的有RGB、YUV等。RGB诉求于人眼对色彩的感应,YUV则着重于视觉对于亮度的敏感程度,Y代表的是亮度,UV代表的是色度(因此黑白电影可省略UV,相近于RGB)……。什么,YUV在黑边电影近似于RGB?当前遇到的问题,显示的内容正常,仅仅是黑色不够黑,也是黑白屏,会不会是把YUV格式用于RGB屏了呢?求教于屏厂,这款屏确实是RGB黑白屏(吐槽一下,屏的规格书里没有这方面的描述)。 

知道了屏的规格后,再看驱动侧是如何选择格式的。因为是HDMI接口,应该从EDID解析开始,EDID里,应该有指示这款屏支持的颜色格式的设定。文件drm_edid.c中可以找到答案。struct drm_display_info 中有对颜色格式进行定义:

	/**
	 * @color_formats: HDMI Color formats, selects between RGB and YCrCb
	 * modes. Used DRM_COLOR_FORMAT\_ defines, which are _not_ the same ones
	 * as used to describe the pixel format in framebuffers, and also don't
	 * match the formats in @bus_formats which are shared with v4l.
	 */
	u32 color_formats;

这个color_formats是如何获取的呢?在函数drm_add_display_info进行设定

u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid)
{
	struct drm_display_info *info = &connector->display_info;

	u32 quirks = edid_get_quirks(edid);

	drm_reset_display_info(connector);

	info->width_mm = edid->width_cm * 10;
	info->height_mm = edid->height_cm * 10;

	info->non_desktop = !!(quirks & EDID_QUIRK_NON_DESKTOP);

	DRM_DEBUG_KMS("non_desktop set to %d\n", info->non_desktop);

	if (edid->revision < 3)
		return quirks;

	if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
		return quirks;

	info->color_formats |= DRM_COLOR_FORMAT_RGB444;
	drm_parse_cea_ext(connector, edid);

看到眼熟的了吗?先给一个初始值DRM_COLOR_FORMAT_RGB444,然后再去解析EDID扩展block。很不幸,这款屏搭配的HDMI转板提供的EDID是有扩展block的。所以要看一下函数drm_parse_cea_ext:

static void drm_parse_cea_ext(struct drm_connector *connector,
			      const struct edid *edid)
{
	struct drm_display_info *info = &connector->display_info;
	const u8 *edid_ext;
	int i, start, end;

	edid_ext = drm_find_cea_extension(edid);
	if (!edid_ext)
		return;

	info->cea_rev = edid_ext[1];

	/* The existence of a CEA block should imply RGB support */
	info->color_formats = DRM_COLOR_FORMAT_RGB444;
	if (edid_ext[3] & EDID_CEA_YCRCB444)
		info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
	if (edid_ext[3] & EDID_CEA_YCRCB422)
		info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;

从函数的实现来看,关键信息在extension block的第4个Byte,这个Byte,对应的数据是0xF2。

#define EDID_CEA_YCRCB444	(1 << 5)
#define EDID_CEA_YCRCB422	(1 << 4)

对应到0xF2, YUV444和YUV422都有打开,真想说句F**K!不支持还用EDID申明支持。没有办法,EDID无法修改,只能祭出最后的法宝,直接上hardcode!YUV再好,咱不用了,直接注释掉!编译->刷机->验证,丝滑的验证过程预示着美好的结果:

终于,黑的是黑的了! 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值