页次: 1
CONFIG_SYS_TEXT_BASE 就在 .config 里配置的,全志的会有默认值。其他没有默认值的芯片一般对应的 defconfig 里也会指定上,如果没有默认值或要自定义的,那就自己在 make menuconfig 里配置上就是了。
这几个文件,一般情况下:
1. u-boot 文件是首先编译链接出来的 elf 格式的文件
2. 在1的基础上进行 objcopy 变成纯二进制代码文件,产生 u-boot-nodtb.bin
3. 在2的基础上在屁股那加上 dtb,就变成了 u-boot-dtb.bin,这个文件就是可以跑起来的 u-boot 程序了。u-boot.bin 跟 u-boot-dtb.bin 是一样的
4. 在3的基础上加上一个u-boot 的头,就变成了 u-boot-dtb.img,头里包含了校验码,入口地址(就是CONFIG_SYS_TEXT_BASE)等,这个文件是给 SPL 加载用的,正如我上面所说的,u-boot 要放到指定的内存地址上才能跑,SPL 加载了 U-BOOT 的镜像后,就是从这个头里知道要放哪里的
5. 在4的基础上,在头部那加上带了全志启动头的spl(在 spl 目录下,又是一堆生成文件),就变成了u-boot-sunxi-with-spl.bin,这是直接给全志的芯片加载用的。
启动的时候,BROM 识别到启动头,加载 SPL 执行,SPL 再去加载带了 u-boot 头的 u-boot,放到头里指示的位置上,再跳转到 u-boot 执行。就这么跑起来了。
以上是一般情况下的流程,不一般的比如带不带 dtb,带不带 spl,是用 legacy image 还是 fitImage,会有些差异。
T113 我没试过不清除具体的情况,但可以分享一下其他的芯片的经验。全志在 FEL 模式下的时候 SRAM 里面是有布局的,一般中间会夹杂着两个 Stack,分别是 IRQ Stack 和普通 Stack,每个芯片具体的位置会不一样。所以如果直接写程序到 SRAM 里,一般情况下是会破坏了栈,从而造成死机。
sunxi-tools 里用了奇技淫巧,分段写入程序,绕开这 stack 位置,然后写入一段特殊的 thunk 程序,再执行这个 thunk 程序,他会备份 stack,然后把之前分段的程序复制回去,然后再跳转执行,以此来来利用整个 SRAM 空间,完事了还能把 Stack 恢复回去,让 FEL 继续正常工作,使接下来能写 u-boot 到 DDR。sunxi-fel spl 干的就是这个事。
参考1: 实现原理
参考2: 代码1 代码2
买全志的开发板前可以先看看这张表https://linux-sunxi.org/Linux_mainlining_effort#Status_Matrix,主线 Linux 支持程度高的玩起来轻松一点。当然如果要挑战自己,研究驱动移植的话,就挑支持少的。
1. 图像输入宽度超过1024后,显示不正常的问题,这是由于驱动中对于 V3s 的 scanline 值配置不正确引起到,目前最新的内核(5.18-rc)依然存在这个 bug。该问题我在另外一个贴子描述过。
解决方法: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/sun4i/sun8i_vi_layer.c?h=v5.18-rc5#n192 这行直接赋值为 1024 就行。
2. 第二个 Plane 输入 YUV 格式时颜色不正常。该问题主线在 5.9 的时候修复过了(5.4.70 也打了补丁),但估计很多人还在使用 5.2 版本,所以再此多提一嘴。问题一共是两处:一处是第二个 Plane 的 CSC 寄存器地址不正确,另外一处是 Mixer 的寄存器映射范围不正确。
解决方法: 参考主线的两个提交:提交1, 提交2
@benlypan
牛,请问最后怎么解决的呢?
把 2048 改成 1024 就行了。V3s 里 YUV 和 RGB 的 scanline 都是 1024,所以可以直接把 https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/sun4i/sun8i_vi_layer.c#L192 这行改成 scanline = 2048 就行了。
然而我的情况比较特殊,我要在别的产品里二次开发,不能换内核,所以就写了个内核模块,启动时 insmod 来动态更改这个配置,这样只能对 YUV 生效,不过反正我也不用 RGB,分享下这个内核模块的代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include "../linux/drivers/gpu/drm/sun4i/sun8i_mixer.h"
static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
.vi_num = 2,
.ui_num = 1,
.scaler_mask = 0x3,
.scanline_yuv = 1024,
.ccsc = 0,
.mod_rate = 150000000,
};
static struct sun8i_mixer* mixer;
static const struct sun8i_mixer_cfg* old_mixer_cfg;
static int find_device_match(struct device* dev, void* data) {
return 1;
}
static int __init fixdrm_init(void) {
struct device_driver* drv;
struct device* dev;
drv = driver_find("sun8i-mixer", &platform_bus_type);
if (!drv) {
pr_err("driver not found");
return -ENODEV;
}
dev = driver_find_device(drv, NULL, NULL, find_device_match);
if (!dev) {
pr_err("device not found");
return -ENODEV;
}
mixer = dev_get_drvdata(dev);
if (!mixer) {
pr_err("mixer not found");
return -ENODEV;
}
old_mixer_cfg = mixer->cfg;
pr_info("scanline_yuv: %d\n", old_mixer_cfg->scanline_yuv);
mixer->cfg = &sun8i_v3s_mixer_cfg;
return 0;
}
static __exit void fixdrm_exit(void) {
if (mixer && old_mixer_cfg) {
mixer->cfg = old_mixer_cfg;
}
pr_info("exit\n");
}
module_init(fixdrm_init);
module_exit(fixdrm_exit);
MODULE_LICENSE("GPL");
我今天在研究 V3s 的 DRM,内核版本就是 5.2,发现一个问题,输入图像格式是 NV21,然后调用 drmModeSetPlane 时如果 SRC_W 超过 1024 图像就显示就不正常,1024以下完全没问题。请问我碰到的这个问题跟您解决的BUG是否相关?
研究了将近两天时间,终于把这个问题给搞定。DE 里有个叫 scanline 的东西,V3s 里的值是 1024,输入宽度要是超过 scanline,需要进行"coarse scaling",然后主线里将 V3s 的这个值配置成 2048 了,所以图像显示不正常。
全志 Linux 的相关代码 https://github.com/Tina-Linux/tina-v3s-linux-4.9/blob/master/drivers/video/fbdev/sunxi/disp2/disp/de/lowlevel_sun8iw8/de_rtmx.c#L1212 https://github.com/Tina-Linux/tina-v3s-linux-4.9/blob/master/drivers/video/fbdev/sunxi/disp2/disp/de/lowlevel_sun8iw8/de_feat.c#L57
主线 Linux 的相关代码 https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/sun4i/sun8i_vi_layer.c#L192 https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/sun4i/sun8i_mixer.c#L583
一开就猜测是驱动的 bug,然后有了以下排查流程:
1. 换最新版本的主线内核,问题依旧
2. 因为我用的是 AIC502,虽然跟 V3s 一样,但就担心硬件有差异,换 V3s,一样有问题
3. 换 A64 用主线内核,不存在问题,他们都属于 DE2,用的是同一份驱动,难道是某些条件下对 V3s 设置错寄存器值了?于是用同样的内核版本,同样的测试数据,把 DE 的相关寄存器值都 dump 下来,比对发现几乎一模一样,泪崩。
4. 换全志的 Linux Kernel,不存在问题,此时确定了肯定是驱动问题,不是硬件问题。再把这个系统下的寄存器值 dump 下来,与主线的一个个寄存器比对过去,发现了一个输入宽度相关的寄存器数值不一样,全志的内核是 1024,主线是我图像的宽度 1280,进一步确定了 1024 是一个 magic number
5. 打开调试日志+printk大法,很快定位到是这个 scanline 引起的。虽然具体的计算原理没看懂,但问题解决了。
https://github.com/lindenis-org/lindenis-v536-package
这就是 tina,全志的硬解软件分3个部分:
1. 内核驱动模块,就是 /dev/cedar_dev,你现在已经有了
2. cedarc, 操作内核模块,各种格式的解码模块(闭源),导出解码接口,这是接口文档http://files.lindeni.org/lindenis-v536/ … %d1%9e.pdf
3. cedarx, 集成了数据流解析,解码(调用 cedarc),渲染等功能,也就是完整的播放器了,xplayerdemo 就在里面(allwinner/tina_multimedia/libcedarx/demo/xplayerdemo)
/dev/disp 是全志的显示模块驱动的设备文件,主线中功能与之对应的是 drm,故主线中不会有 /dev/disp,除非将全志的显示驱动移植过来。尚不清除用全志的解码驱动与主线的 drm 能不能合作。
主线也有对应的解码驱动,叫 cedrus( https://linux-sunxi.org/Sunxi-Cedrus ),他能很好的与 drm 合作。只是 v3s 的还没合并到主线,这是开发中的代码 https://github.com/mcerveny/linux/commits/v3s_videocodec_v4 ,我没试过,但有计划去尝试。主线的解码接口叫 v4l2-request,可以参考这个工程如何调用解码以及如何调用 drm 进行渲染
https://github.com/bootlin/v4l2-request-test ,关于主线的渲染的流程可参考这个文档FOSDEM_Presentation_2018___Lukas_Rusak.pdf
全志的显示模块 DE 分多个通道,其中通道0和通道1为视频通道,可接受 YUV 和 RGB 格式,通道之间可以透明叠加。一个通道分多个层(一般4层),层之间可叠加。同一个通道只接受一种像素格式,默认情况下 /dev/fb0 配置在通道1的第0层上,那么放视频就要放在通道0上了。在放视频的时候可看一下当前的 DE 状态 /sys/class/disp/disp/attr/sys
比如我这个
# cat /sys/class/disp/disp/attr/sys
screen 0:
de_rate 297000000 hz, ref_fps:58
mgr0: 1024x600 fmt[rgb] cs[0x0] range[limit] unblank direct_show[false]
lcd output backlight( 50) fps:58.5 1024x 600
err:0 skip:1 irq:154729 vsync:0 vsync_skip:0
BUF enable ch[0] lyr[0] z[1] prem[N] a[globl 255] fmt[ 77] fb[1280, 736;1280, 736;1280, 736] crop[ 0, 0,1280, 720] frame[ 85, 0, 853, 480] addr[5b400000,5b4e6000, 0] flags[0x 0] trd[0,0]
BUF enable ch[1] lyr[0] z[0] prem[N] a[pixel 255] fmt[ 0] fb[1024, 600;1024, 600;1024, 600] crop[ 0, 600,1024, 600] frame[ 0, 0,1024, 600] addr[58100000, 0, 0] flags[0x 0] trd[0,0]
ch0 lry0 是 fmt77 格式,也就是NV21格式,就是我显示视频的层了。ch1 lyr0 是 fmt0 格式,是 ARGB 格式,就是/dev/fb0,是我显示UI的层。如果当前只有一个 fmt0 的层,那么铁定不是"硬渲染"的。
我不知道有没有现成的软件,我这个播放器是自己写的,但是是公司的项目,不好开源出来。
benlypan 说:@baidxi
只是做了微小的工作,让 sunxi-fel 可以识别 A133,能下载程序,执行 thunk 代码。计划是下一步开始研究 SPL我也在准备研究一下SPL,能否分享一下sunxi-fel?
最近打算用jtag下载SPL执行。
我上传到 github 了,通过 FEL 执行 sunxi-tools 里的 uart0-helloworld-sdboot 程序是没问题的。
https://github.com/benlypan/sunxi-tools
全志64位的芯片的 FEL 很恶心,看 U-BOOT 2021.04 引入了一种使用 cpu hotplug 的黑科技从 SPL 跳回到 FEL,不知道 A133 是否兼容这个黑科技,我之前刚研究到这步。
可以将多个SPI设备与文件系统FLASH挂载在一起的,因为系统运行过程中执行程序或者读取文件也是按扇区读 flash 内容到内存中再执行或操作的,这完全是操作系统层面的逻辑,不会在硬件上独占SPI总线,因此可以与其他SPI设备挂载在一起。
有两点要注意:
1. 其他设备的 CS 脚需硬件上拉,原因是 CPU 上电后 BROM 读取 U-BOOT SPL 的时候,他不知道有其他 SPI 设备的存在,所以不会去控制这些 SPI 设备的片选信号,如果不硬件上拉,这些 SPI 设备的 CS 脚的状态是不确定的,会干扰总线,造成启动失败。
2. 使用多个 SPI 设备,需要改用 GPIO 来控制 CS 信号
我在 dts 中这么配置了就能用了
/ {
soc {
ehci0: usb@01c1a000 {
compatible = "allwinner,sun8i-v3s-ehci", "generic-ehci";
reg = <0x01c1a000 0x100>;
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_BUS_EHCI0>, <&ccu CLK_BUS_OHCI0>;
resets = <&ccu RST_BUS_EHCI0>, <&ccu RST_BUS_OHCI0>;
status = "okay";
};
ohci0: usb@01c1a400 {
compatible = "allwinner,sun8i-v3s-ohci", "generic-ohci";
reg = <0x01c1a400 0x100>;
interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_BUS_EHCI0>, <&ccu CLK_BUS_OHCI0>,
<&ccu CLK_USB_OHCI0>;
resets = <&ccu RST_BUS_EHCI0>, <&ccu RST_BUS_OHCI0>;
status = "okay";
};
};
}
&usb_otg {
dr_mode = "host";
status = "okay";
};
&usbphy {
status = "okay";
};
u-boot 会根据芯片 id 生成 mac 地址(实现代码在 board/sunxi/board.c 中的 setup_environment 函数里),并传递给 Linux 内核(实现代码在 common/fdt_support.c 中的 fdt_fixup_ethernet 函数里)。前提是 dts 要配置。
在 u-boot 的 dts 中的 aliases 里加上 ethernet0 = &emac; 这样 u-boot 就会自动生成 ethaddr 环境变量。在 Linux 的 dts 中除了加上 aliases 的那句,还要在 emac 里加上 mac-address = [ 00 00 00 00 00 00 ]; 这么一个占位符,那么 u-boot 在启动 Linux 内核的时候,会将环境变量里的 ethaddr 环境变量里的内容覆盖写入 Linux 的 dts 里,这样 Linux 就能得到由芯片 id 生成的 mac 地址了。
所以检查一下 dts 的相关配置
页次: 1