Jump to content

Recommended Posts

Posted

Hello! I could find quite a bit of information about the analog video output for OrangePi Zero 2W on Armbian under Ubuntu. On Android TV-OUT works. Are there any plans to add this feature to Armbian?

It is very critical for me. If no one plans to do this now, then I would be extremely grateful for advice from experts on how to port / modify existing drivers, perhaps take something from the AndroidTV 12 code, where the video output works? I am a developer, but I have not previously dealt with driver development for Linux, but if I understand in which direction to act, then using ChatGPT sooner or later I should be able to cope.

I would be very grateful for any help!

Posted

The orange pi zero with the latest armbian can output analog TV

 

The other zeros (3, 2W) can't (with Linux)

 

It would be great if new developers can find the android code and port it to Linux.

Posted

Thanks.

Android source code is presented on OrangePi official website. I'm trying to port driver last weeks, but it's not easy for me, I'm never work with drivers early. At this reason I would be really greatful any help from experts

Posted

Maybe you can use devmem2 so you can manipulate registers from a userspace application, like some legendary developers did in the past ;)

 

I also worked updating the patches needed to bring TV out to the orange pi zero with newer kernels. Maybe it can be a reference.

 

https://github.com/robertojguerra/orangepi-zero-full-setup/blob/main/README2.md

 

https://forum.armbian.com/topic/6582-orange-pi-zero-h2h3-tv-out-on-mainline-working/

https://linux-sunxi.org/images/2/24/H616_User_Manual_V1.0_cleaned.pdf

 

 

 

Posted (edited)

 

I have been looking at the code and there's been a lot of changes between H3/H5 and H616/H618 SOC's.

 

H616/H618 now has a TVE_TOP register. 

 

H616/H618 uses the first DAC and moved the DAC MAP to TVE_TOP. 

 

https://linux-sunxi.org/images/2/24/H616_User_Manual_V1.0_cleaned.pdf

Module Name		Base Address
TVE_TOP			0x06520000
TVE  			0x06524000
  
Register Name		Offset		Description
TVE_DAC_MAP		0x0020		TV Encoder DAC MAP Register
TVE_DAC_STATUS		0x0024		TV Encoder DAC STAUTS Register
TVE_DAC_CFG0		0x0028		TV Encoder DAC CFG0 Register
TVE_DAC_CFG1		0x002C		TV Encoder DAC CFG1 Register
TVE_DAC_CFG2		0x0030		TV Encoder DAC CFG2 Register
TVE_DAC_CFG3		0x0034		TV Encoder DAC CFG2 Register
TVE_DAC_TEST		0x00F0		TV Encoder DAC TEST Register

 

 

H3/H5  TV Encoder Enable Register use to handle the DAC mapping. If you look at the two pdf's you can see the changes.

https://linux-sunxi.org/images/1/1e/Allwinner_A10_User_manual_V1.5.pdf

Module Name		Base Address
TVE			0x01C0A000

Register Name		Offset	Description
TVE_000_REG		0x0000	TV Encoder Enable Register

 

 

We need to modify the kernel TVE driver. 

https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/sun4i/sun4i_tv.c

#define SUN4I_TVE_TOP_DAC_MAP		0x020
#define SUN4I_TVE_TOP_EN_DAC_MAP_MASK		GENMASK(6, 4)
#define SUN4I_TVE_TOP_EN_DAC_MAP(dac, out)	(((out) & 0xf) << (dac + 1) * 4)
#define SUN4I_TVE_TOP_DAC_TEST		0x0F0


	if (tv->quirks->hastvtop) {
		/* Enable and map the DAC to the output */
		regmap_update_bits(tv->top_regs, SUN4I_TVE_TOP_DAC_MAP,
			   	   SUN4I_TVE_TOP_EN_DAC_MAP_MASK,
			   	   SUN4I_TVE_TOP_EN_DAC_MAP(0, 1) |
			   	   SUN4I_TVE_TOP_EN_DAC_MAP(1, 2) |
			   	   SUN4I_TVE_TOP_EN_DAC_MAP(2, 3) |
			   	   SUN4I_TVE_TOP_EN_DAC_MAP(3, 4));

	} else {
		/* Enable and map the DAC to the output */
		regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
			   	   SUN4I_TVE_EN_DAC_MAP_MASK,
			   	   SUN4I_TVE_EN_DAC_MAP(0, 1) |
			   	   SUN4I_TVE_EN_DAC_MAP(1, 2) |
			   	   SUN4I_TVE_EN_DAC_MAP(2, 3) |
			   	   SUN4I_TVE_EN_DAC_MAP(3, 4));
	}
I still need to modify this part...
  
	/* Configure the DAC for a composite output */
	regmap_write(tv->regs, SUN4I_TVE_DAC0_REG,
		     SUN4I_TVE_DAC0_DAC_EN(0) |
		     (tv_mode->dac3_en ? SUN4I_TVE_DAC0_DAC_EN(3) : 0) |
		     SUN4I_TVE_DAC0_INTERNAL_DAC_37_5_OHMS |
		     SUN4I_TVE_DAC0_CHROMA_0_75 |
		     SUN4I_TVE_DAC0_LUMA_0_4 |
		     SUN4I_TVE_DAC0_CLOCK_INVERT |
		     (tv_mode->dac_bit25_en ? BIT(25) : 0) |
		     BIT(30));

 

 

To access the TVE_TOP DAC MAP Register we need to first enable the TVE_TOP clocks. (in function sun4i_tv_bind())

static const struct regmap_config sun4i_tv_top_regmap_config = {
	.reg_bits	= 32,
	.val_bits	= 32,
	.reg_stride	= 4,
	.max_register	= SUN4I_TVE_TOP_DAC_TEST,
	.name		= "tv-top",
};


	/* tve top */
	if (tv->quirks->hastvtop) {
		top_regs = devm_platform_ioremap_resource(pdev, 0);
		if (IS_ERR(top_regs)) {
			dev_err(dev, "Couldn't map the TV TOP registers\n");
			return PTR_ERR(top_regs);
		}

		tv->top_regs = devm_regmap_init_mmio(dev, top_regs,
					 	     &sun4i_tv_top_regmap_config);
		if (IS_ERR(tv->top_regs)) {
			dev_err(dev, "Couldn't create the TV TOP regmap\n");
			return PTR_ERR(tv->top_regs);
		}

		tv->top_reset = devm_reset_control_get(dev, "rst_bus_tve_top");
		if (IS_ERR(tv->top_reset)) {
			dev_err(dev, "Couldn't get our reset line\n");
			return PTR_ERR(tv->top_reset);
		}

		ret = reset_control_deassert(tv->top_reset);
		if (ret) {
			dev_err(dev, "Couldn't deassert our reset line\n");
			return ret;
		}

		tv->top_clk = devm_clk_get(dev, "clk_bus_tve_top");
		if (IS_ERR(tv->top_clk)) {
			dev_err(dev, "Couldn't get the TV TOP clock\n");
			ret = PTR_ERR(tv->top_clk);
			goto err_assert_reset;
		}
		clk_prepare_enable(tv->top_clk);
	} 

 

 

This is from the H616 user manual.

 

Figure 7- 10. DAC Calibration
10-bit calibration value is burned into efuse. Every time software can read the 10-bit calibration value from efuse, to control BIAS current and BIAS current switch, then a specific BIAS current is generated to calibrate maximum output voltage of DAC.

 

We need to extract the DAC calibration value (tvout 32) from SID.

https://linux-sunxi.org/SID_Register_Guide

 

hexdump -C /sys/bus/nvmem/devices/sunxi-sid0/nvmem

 

H6
Name			Offset	Size	Description
CHIPID			0x00	128 bit	Chip-ID, also known as SID
BROM_CONFIG		0x10	32 bit	unknown, "16 bits config, 16 bits try"
THERMAL_SENSOR		0x14	64 bit	Thermal sensor calibration data
TF_ZONE			0x1c	128 bit	unknown, probably reserved for Trusted Firmware
OEM_PROGRAM		0x2c	160 bit	unknown, "emac 16 + tvout 32 + reserv 112"

 

 

Add quirks for h616. Not sure if .unknown is needed?? 

static const struct sun4i_tv_quirks h616_quirks = {
	.calibration = 0x?????????????,
	.unknown = 1,
	.hastvtop = true,
};

static const struct of_device_id sun4i_tv_of_table[] = {
	{ 
		.compatible = "allwinner,sun4i-a10-tv-encoder", 
		.data = &a10_quirks
	},
	{ 
		.compatible = "allwinner,sun8i-h3-tv-encoder", 
		.data = &h3_quirks
	},
	{ 
		.compatible = "allwinner,sun50i-h5-tv-encoder",
		.data = &h5_quirks
	},
	{
		.compatible = "allwinner,sun50i-h616-tv-encoder",
		.data = &h616_quirks },
	{ /* sentinel */ },
};

 

 

The dtsi might look similar to this. Not sure if it's correct. I think we need to add <&ccu CLK_TVE> to tcon_tv0 clocks.

		tcon_tv0: lcd-controller@6515000 {
			compatible = "allwinner,sun50i-h6-tcon-tv",
				     "allwinner,sun8i-r40-tcon-tv";
			reg = <0x06515000 0x1000>;
			interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
			clocks = <&ccu CLK_BUS_TCON_TV0>,
				 <&tcon_top CLK_TCON_TOP_TV0>;
			clock-names = "ahb",
				      "tcon-ch1";
			resets = <&ccu RST_BUS_TCON_TV0>;
			reset-names = "lcd";
			

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

				tcon_tv0_in: port@0 {
					reg = <0>;

					tcon_tv0_in_tcon_top_mixer0: endpoint@0 {
						reg = <0>;
						remote-endpoint = <&tcon_top_mixer0_out_tcon_tv0>;
					};
				};

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

					tcon_tv0_out_tve: endpoint@0 {
						reg = <0>;
						remote-endpoint = <&tve_in_tcon_tv0>;
					};
                  
                  			tcon_tv0_out_tcon_top: endpoint@1 {
						reg = <1>;
						remote-endpoint = <&tcon_top_hdmi_in_tcon_tv0>;
					};
				};
			};
		};

		tve: tv-encoder@6520000 {
			compatible = "allwinner,sun50i-h616-tv-encoder";
			reg = <0x06520000 0x100>,
			      <0x06524000 0x3fc>;
			clocks =<&ccu CLK_BUS_TVE_TOP>,
				<&ccu CLK_BUS_TVE0>;
			clock-names = "clk_bus_tve_top", 
				      "clk_bus_tve";
			resets = <&ccu RST_BUS_TVE_TOP>,
				 <&ccu RST_BUS_TVE0>;
			reset-names = "rst_bus_tve_top",
				      "rst_bus_tve";
			status = "disabled";

			port {
				tve_in_tcon_tv0: endpoint {
					remote-endpoint = <&tcon_tv0_out_tve>;
 				};
 			};
 		};

 

I haven't looked into the mixer1 part of the patch. Not sure if H616/H618 has one. I can't find the base address in the H616 user manual.

 

BSP kernel code that might help us.

https://github.com/AvaotaSBC/linux/tree/main/bsp/drivers/video/sunxi/disp2/tv

https://github.com/AvaotaSBC/linux/tree/main/bsp/drivers/video/sunxi/disp2/disp/de

Edited by Nick A
Posted
On 11/9/2024 at 7:41 PM, Nick A said:

I haven't looked into the mixer1 part of the patch. Not sure if H616/H618 has one. I can't find the base address in the H616 user manual.

It has, all H family has 2 mixers. Addresses are hidden in DE BSP source code, but not as absolute address, only as relative. In any case, you don't need second mixer if you won't use any other display output.

 

Note that there is no documentation for DE3.3. There is documentation for DE3 on linux-sunxi.org, which is older, but nevertheless similar core. Still, BSP source code will give you best info, even if it's sometimes hard to understand.

 

In any case, you seem to be on right track to get it working. I strongly suggest that you dump register values of all components involved in pipeline from working BSP solution (most likely Android image) and compare to them to register values dump from your mainline based system. This includes TVE, TCON top and also very importantly, clocks and resets.

 

One extremely useful trick for diagnostics is to use TCON test patterns. Those patterns are directly generated in TCON and then piped to encoder, TVE in this case. It allows you to test TVE without worrying about mixer and if it's properly routed through TCON top. Those test patterns can be enabled by setting TCON register 0x40 to, for example, 1, which will get you well know color check pattern.

 

Note that your solution with TV TOP included in same driver as TVE will work here, but it's not appropriate solution for upstream. TV TOP should be standalone driver like TCON TOP...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...

Important Information

Terms of Use - Privacy Policy - Guidelines