Jump to content

AV/HDMI switch and display rotation


PaddleStroke

Recommended Posts

Hi there,

 

Is there a way to make an automatic switch between AV and HDMI when HDMI connector is connected/disconnected?

ie By default AV composite signal is ON,  if HDMI is detected while booting, then video and sound are output to HDMI instead of AV/L/R output.

I read that if both TV out and HDMI are enable at the same time it can result in artifacts on HDMI and for that reason TV was disabled by default.

So is there a way to detect if HDMI is plugged in, and if so disable TV out. I would say it needs 2 .bin files with each config, but how to detect if HDMI is present before loading .bin and load the right .bin according to situation?

 

Also is it possible to rotate display by 90 or 180° ?

 

Would it be possible to have :

- by default AV with 180° rotation

- if HDMI detected then HDMI without rotation?

 

Thanks!

(for H2+/H3 board)

Link to comment
Share on other sites

2 hours ago, PaddleStroke said:

Also is it possible to rotate display by 90 or 180° ?

Probably, but it is not HW accelerated on H3.

 

About other things, something can be done, IF you rebuild kernel with CONFIG_SWITCH or CONFIG_ANDROID_SWITCH enabled. That way you will get few files, which will have different content, based on TV or HDMI hot plug detection status. Not sure where those files will be located, but it will be probably somewhere in /sys or /proc. Code for that is located here:

https://github.com/armbian/linux/blob/sun8i/drivers/video/sunxi/disp2/hdmi/drv_hdmi.c#L491-L510

https://github.com/armbian/linux/blob/sun8i/drivers/video/sunxi/disp2/tv/drv_tv.c#L31-L82

 

In script.bin you would need enabled both, hdmi and tv, or else you won't get status notification. Final step would be writing simple daemon, which will monitor aforementioned files and react on content change accordingly. There is simple way how to enable/disable TV/HDMI display from userspace on running system, but you would need root access. Since you already need to recompile the kernel, you should patch this part of code to have access rights 0666, so root rights are not needed The procedure how to turn on/off hdmi/tv can be found here Just instead "disp" use "disp0" and "disp1" respectively. There is another procedure if this doesn't work.

 

I'm not sure if work described above is worth the hassle, but your strategy may work.

Link to comment
Share on other sites

Thanks for your reply jernej!

 

I could fix my rotate problem by hardware modification of the LCD. 

 

However I have not yet got the HDMI/AV switch working. I think I have a total lack of understanding on how drv_hdmi and drv_tv work. Also I don't understand how this CONFIG_SWITCH works.

 

But is it not possible to just use a simple daemon that would do something like

 

define last_read_HPD = 0

While 1 

       HDMI_HPD = read status directly from registry

       if HDMI_HPD =! last_read_HPD
              if HDMI_HPD = 1 

                    turn OFF disp1 as you describe here

                    turn ON disp0

              if HDMI_HPD = 0 

                    turn OFF disp0

                    turn ON disp1

               last_read_HPD = HDMI_HPD

However I don't know how to read the HDMI_HPD status. I checked the H3 datasheet but can't find HPD registry address. I have found there (line 114) a function that seem to be doing just that but I can't understand how it works.

 114 static int sunxi_dw_hdmi_get_plug_in_status(void)
 115 {
 116         struct sunxi_hdmi_phy * const phy =
 117                 (struct sunxi_hdmi_phy *)(SUNXI_HDMI_BASE + HDMI_PHY_OFFS);
 118 
 119         return !!(readl(&phy->status) & (1 << 19));
 120 }

I can't find where is defined SUNXI_HDMI_BASE (and so it's value) to get the exact address. Also I don't understand the return.

 

Do you know how to read HPD status from the daemon?

Do you think this simple methode would work?

 

Thanks!

 

Link to comment
Share on other sites

That can work only if /dev/mem (or kmem?) access is permissive, i.e. it is allowed to access device memory regions from userspace. Additionally, daemon has to be run as root (maybe you can drop priviliges later?).

 

That being said, it can work. HDMI PHY status register is 32 bit wide, located at 0x01ef0038 and as you can see from above snippet, you have to check bit 19.

Link to comment
Share on other sites

Thanks!

 

Quote

/dev/mem (or kmem?) access is permissive,  i.e. it is allowed to access device memory regions from userspace.

Is this the same with access rights 0666 ? If not how to enable this access?

 

Also do you know how to read from registers? From my reading we can use dev/mem file right? In pyA20 there's an example but I am not sure to understand how it works. and how this mem file is structured. Any hints how to read a specific register address from it?

 

 

 

Link to comment
Share on other sites

I found how to modify pyA20 to check the register value. 

I added this to gpio_lib.c and gpio_lib.h, then some other things in gpio.c to declare the new module.

 - added in gpio_lib.h

#define HDMI_PHY_BASE 0x01ef0038

- added in gpio_lib.c

int get_HDMI_HPD()
{
	if (SUNXI_PHY_BASE == 0)
		return -1;

    uint32_t reg = (uint32_t *)SUNXI_PHY_BASE;
    return (reg & ( 1 << 19))>> 19;
}

However I still can't read the hpd status. At the beginning it just returned me 1, if HDMI connected or not.

Then to further investigate I modified the function to : 

int get_HDMI_HPD()
{
	if (SUNXI_PHY_BASE == 0)
		return -1;

    uint32_t reg = (uint32_t *)SUNXI_PHY_BASE;
    return reg & 1048575; #1048575 because it's 11111111111111111111 in binary.
}

So I could read the 20 first bits of the register, and see what happens.

 

Then I have some very strange results. The value returned does not depend if HDMI is connected or not. So when I run my python script, it initialize pyA20 (so the mmap to dev/mem)

Then whenever I check get_HDMI_HPD() I have the same number returned again and again. Even if I unconnect HDMI. For example 

- return = 11010010000000111000 (= 860216 in dec)

- disconnect HDMI

- return = 11010010000000111000 (= 860216 in dec)

- wait 10 minutes

- return = 11010010000000111000 (= 860216 in dec)

 

Then if I close the python script and open again (so mmap is reinitialized) the number change

-  return = 01010111000000111000 (356408)

- disconnect HDMI

- return = 01010111000000111000 (356408)

...

 

I tested several times and each time mmap is reinitialized the number changes. HDMI has no effect what so ever.

11010010000000111000 860216
10111101000000111000 774200
11011111000000111000 913464
11001010000000111000 827448
01010111000000111000 356408
00000001000000111000 4152

 

the mmap seem correct

int sunxi_gpio_init(void)
{
	int fd;
	unsigned int addr_start, addr_offset;
	unsigned int PageSize, PageMask;
	void *pc;
	void *pc2;

	fd = open("/dev/mem", O_RDWR);
	if (fd < 0)
		return -1;

	//mmap for GPIO
	PageSize = sysconf(_SC_PAGESIZE);
	PageMask = (~(PageSize - 1));

	addr_start = SW_PORTC_IO_BASE & PageMask;
	addr_offset = SW_PORTC_IO_BASE & ~PageMask;

	pc = (void *)mmap(0, PageSize * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr_start);
	if (pc == MAP_FAILED)
		return -1;

	SUNXI_PIO_BASE = (unsigned int)pc;
	SUNXI_PIO_BASE += addr_offset;
	
	//mmap for HPD
	addr_start = HDMI_PHY_BASE & PageMask;
	addr_offset = HDMI_PHY_BASE & ~PageMask;

	pc2 = (void *)mmap(0, PageSize * 2, PROT_READ, MAP_SHARED, fd, addr_start);
	if (pc2 == MAP_FAILED)
		return -1;

	SUNXI_PHY_BASE = (unsigned int)pc2;
	SUNXI_PHY_BASE += addr_offset;

	close(fd);
	return 0;
}

 

Do you see something wrong in my mmap? I used the same syntax with the GPIO mmap, and I tested the GPIO are still working correctly, even if the getHPD always give the same result.

 

Are you sure about the register address for HPD? Or maybe it's not in the dev/mem file, I don't know how this file works. Is it the full register values?

 

Enclosed are the modified pyA20 files.

 

gpio.c

gpio_lib.c

gpio_lib.h

Link to comment
Share on other sites

Address is correct.

 

I would suggest you first find devmem2 program (source is all over the net). It does almost exactly what you want, but universally (you give it physical address on command line which you want to read).

Link to comment
Share on other sites

Thanks!! It's indeed working with devmem2. There should have been something wrong in my mmap before.

 

When I run devmem2 the value change when HDMI is connected or not.

 

No HDMI : 266f0
0000 0000 0000 0010 0110 0110 1111 0000

HDMI : a66F0
0000 0000 0000 1010 0110 0110 1111 0000

 

The method you gave here, is working for legacy 3.4.113 correct? So I should do : 

Switch from TV (TNSC) to HDMI

sudo su
cd /sys/kernel/debug/dispdbg/
echo "switch" > command
echo "disp" > name
echo "4 5" > param
echo "1" > start

Why is it "4 5" ? Is it not as follow

"screen0_output_type  screen0_output_mode"

In script.bin HDMI output type is "3", "4" being VGA. (source here)

 

Switch from HDMI to TV (TNSC)

sudo su
cd /sys/kernel/debug/dispdbg/
echo "switch" > command
echo "disp" > name
echo "2 14" > param
echo "1" > start

 

Or should I first disable the previous video output with

sudo su
cd /sys/kernel/debug/dispdbg/
echo "switch" > command
echo "disp" > name
echo "0" > param
echo "1" > start

 

Or should I used disp0 and disp1 as you suggest? Then it would be as follow : 

Switch from TV (TNSC) to HDMI


 

sudo su
cd /sys/kernel/debug/dispdbg/
echo "switch" > command
echo "disp1" > name
echo "0 14" > param
echo "1" > start

echo "switch" > command
echo "disp0" > name
echo "4 5" > param
echo "1" > start

 

Switch from HDMI to TV (TNSC)

 

sudo su
cd /sys/kernel/debug/dispdbg/
echo "switch" > command
echo "disp0" > name
echo "0 5" > param
echo "1" > start

echo "switch" > command
echo "disp1" > name
echo "2 14" > param
echo "1" > start

 

 

PS : for those that might be interested, here is how to install devmem2

If you have internet access just do the following

wget http://free-electrons.com/pub/mirror/devmem2.c
gcc ./devmem2.c
sudo mv ./a.out /usr/local/bin/devmem2

 

Then to run devmem2 to read a value type : 

sudo devmem2 0x01EF0038

You can replace 0x01EF0038 by the address you want to read.

Link to comment
Share on other sites

Ok after few tests :  Currently my script.bin is as follow

[disp_init]
disp_mode = 1 #(tv mode)
screen0_output_type = 3
screen0_output_mode = 5
screen1_output_type = 2
screen1_output_mode = 14

hdmi_used = 0

tv_used = 1

Using "disp"  --> does not work

Using "disp1"  --> works

echo "switch" > command
echo "disp1" > name 
echo "0" > param
echo "1" > start

--> turn off my TV signal.  However enabling HDMI instead does not work.  I tried : 

echo "switch" > command
echo "disp1" > name 
echo "4 5" > param
echo "1" > start

and with "3 5" both don't work. It just turns OFF the TV signal but HDMI is not turning ON.

 

I guess it's because HDMI should be set on disp0 and not disp1?

Is there any documentation about this dispdbg commands?  Can't find any on google.

 

Then as I have no longer video signal I don't know how to know what happens afterwards and have to reboot the unit.

 

Link to comment
Share on other sites

on another topic, I found this about LCD rotation : 

http://linux-sunxi.org/Display

Quote

Rotation

You can rotate the framebuffer by supplying fbcon=rotate:<n> kernel parameter, where n can be one of the following.

0 - normal orientation (0 degree)

1 - clockwise orientation (90 degrees)

2 - upside down orientation (180 degrees)

3 - counterclockwise orientation (270 degrees)

Alternatively you could echo <n> to

/sys/class/graphics/fbcon/rotate

after kernel has booted. For more see Documentation/fb/fbcon.txt in kernel tree.

 

Can someone explain me what it means "supply" a kernel parameter and how to do it?

Also I tried to echo on a booted device.

 

sudo su
cd /sys/class/graphics/fbcon
echo "2" > rotate

But nothing happenned, no error message. I am missing something? I am not sure to understand differences between sunxi-linux and armbian and how they interact...

Link to comment
Share on other sites

44 minutes ago, PaddleStroke said:

But nothing happenned, no error message. I am missing something?

I guess this functionality doesn't work on H3. I have seen something like that for A64 with Allwinner provided changes to X11 driver which used "transform" HW, which exists primarly on A series SoCs (and maybe few others).

 

But I might be wrong...

Link to comment
Share on other sites

16 hours ago, jernej said:

I guess this functionality doesn't work on H3. I have seen something like that for A64 with Allwinner provided changes to X11 driver which used "transform" HW, which exists primarly on A series SoCs (and maybe few others).

 

But I might be wrong...

 

Thanks for feedback, anyway I found a way to hardware rotate LCD now. It was just for curiosity.

 

What about the dispdbg commands? Do you know where I can find documentation about that? Or the source code of these commands?

 

Any idea how to disable disp1 and enable disp0 ? I think perhaps it's not working because the switch command only changes the screen_output_type and mode but is not actually enabling/disabling screen0/screen1 I tried to enable both disp in my script.bin with

 

[disp_init]
disp_init_enable = 1
disp_mode = 2   # dual head
screen0_output_type = 0   # 0 should be NONE
screen0_output_mode = 5
screen1_output_type = 2
screen1_output_mode = 14

But then I have not any signal on either screen. Maybe disp_mode= 4 (clone(screen0, screen1, fb0) (2 screens, one standard framebuffer) ) would work?

 

Also I am not sure how to enable HDMI after disable TV (as I don't have screen anymore...). So I tried to make a bash file but not sure the syntax is correct. Can you tell me if something is wrong in the syntax?

 

#!/bin/bash 

cd /sys/kernel/debug/dispdbg/

echo "switch" > command
echo "disp1" > name
echo "0 14" > param
echo "1" > start

sleep 2

cd /sys/kernel/debug/dispdbg/
echo "switch" > command
echo "disp0" > name
echo "4 5" > param
echo "1" > start

 

Link to comment
Share on other sites

8 hours ago, PaddleStroke said:

What about the dispdbg commands? Do you know where I can find documentation about that? Or the source code of these commands?

No documentation whatsoever, except maybe in Chinese. I found commands in source code here.

 

I already forgot everything related to those commands, so I suggest you take a look at code linked above.

Link to comment
Share on other sites

14 hours ago, jernej said:

No documentation whatsoever, except maybe in Chinese. I found commands in source code here.

 

I already forgot everything related to those commands, so I suggest you take a look at code linked above.

Thanks for feedback!

 

In chinese, do you mean that those commands are hardware coded in the allwinner chip? It's not something that is coded in armbian?

 

I can handle chinese documentation if you have any. Or if you confirm allwinner could help I'll ask them, but I am not sure what to ask them right now as I thought it was a armbian feature.

Link to comment
Share on other sites

9 hours ago, PaddleStroke said:

In chinese, do you mean that those commands are hardware coded in the allwinner chip? It's not something that is coded in armbian?

I meant chinese pdfs. SDK releases sometimes have some documentation, but it is almost always in chinese. And before you ask, I don't now where to find it. It is already years when I last saw any kind of H3 SDK documentation.

 

BTW, these last post have everything I remember from head. I don't want to spend any time with 3.4 BSP kernel.

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
×
×
  • Create New...

Important Information

Terms of Use - Privacy Policy - Guidelines