We have a custom board using the IMX8MP SOC which has an LVDS LCD display and a HDMI display connected.
We wish to be able to control the contrast and colour saturation on the LCD display. Is this possible somehow, ideally through Wayland/Weston or Linux drivers but if not using direct hardware registers ?
We note the LCDIF driver has colour space conversion matrix registers but it looks like these cannot be used for RGB -> RGB changes which I presume would be needed in a default IMX8MP Linux with Wayland/Weston setup.
May somewhat interesting for others. The Color Space Conversion can be somewhat missused to do RGB -> RGB. I barely spent any time on it, but the following coefficients
A1=0x100, A2=0, A3=0, B1=0, B2=0x100, B3=0, C1=0, C2=0, C3=0x100, D1=0, D2=0x100, D3=0x100
seem to lead to the same output as
CSC0_CTRL = 1 (the bypass mode)
Unfortunately NXP does not fully document what the conversion are doing. But with adjusting this coefficients, I at least I was able to do a slight color correction for a display we use. Is this an ugly hack, yes absolutely, did it solve my problem, yes.
#!/usr/bin/env python3
import mmap
import struct
def write_word(address, value):
with open('/dev/mem', 'r+b') as f:
mem = mmap.mmap(f.fileno(), mmap.PAGESIZE, mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, offset=address & ~(mmap.PAGESIZE - 1))
struct.pack_into('I', mem, address & (mmap.PAGESIZE - 1), value)
mem.close()
def write_coeffs(a1, a2, a3, b1, b2, b3, c1, c2, c3, d1, d2, d3):
# LCDIF2 = 0x32E90000
# A1-C3 11-bits, D1-D3 10 bits
def w(x, y):
return ((x & 0x3FF) << 16) | ((y & 0x3FF) << 0)
write_word(0x32E9021C, 0) # CSC0_CTRL
write_word(0x32E90220, w(a2, a1)) # CSC0_COEF0 - A2 A1
write_word(0x32E90224, w(b1, a3)) # CSC0_COEF1 - B1 A3
write_word(0x32E90228, w(b3, b2)) # CSC0_COEF2 - B3 B2
write_word(0x32E9022C, w(c2, c1)) # CSC0_COEF3 - C2 C1
write_word(0x32E90230, w(d1, c3)) # CSC0_COEF4 - D1 C3
write_word(0x32E90234, w(d3, d2)) # CSC0_COEF5 - D3 D2
write_coeffs(0x100, 0, 0, 0, 0x100, 0, 0, 0, 0x100, 0x000, 0x100, 0x100) # "somewhat" close to identity
write_coeffs(0x0B0, 0, 0, 0, 0x100, 0, 0, 0, 0x100, 0x000, 0x100, 0x100) # less red
This might be a stupid question.
Your python script is referring to CSC0_CTRL register 0x32E9021C whereas in the imx8mp reference manual, the address is 0x32FC621C as can be seen in picture below . An idea where the difference comes from
write_word(0x32E9021C, 0) # CSC0_CTRL
The baseaddress for LCDIF2 is 0x32E90000, the base address for LCDIF3 is 0x32FC600. Could have done this better in the script to allow it for all 3 LCDIF. The display I have tough, is connected via LCDIF2.
you are the man @tsmt
This was rather stupid question from me.
I can now reproduce your result on my display too which sits on LCDIF1.
The colors are not yet quite the same as original, but I will play with the coefficient and try to achieve 1-to-1 RGB2RGB conversion.
Ah, that looks interesting and looking at the datasheet might do what I need with:
A1, B2, C3 = RGB gains (0x100 being 1x) so change these equally for contrast.
D1, D2, D3 = RGB offsets so change these equally for brightness.
This assumes the calculated YUV values clip to 255 and 0.
Any reason why you set D2=0x100, D3=0x100 that looks like -256 whatever that would do ?
If I get time I will try that out.
A1 through C3 seemed somewhat obvious, just creating an identity matrix. Why D2 and D3 are these numbers I don't understand (yet), but I just found out testing it with a white image and adjusting the coefficients until I saw a nice blue, red, green and finally white again.
I assume that this conversion modes quite likely do some additional underlining calculation. Else the control registers would need IMHO 1 bit less. But this is all a wild guess. I also read a little bit through the Linux kernel code, and the comments also lead to the conclusion that not everything is fully understood.
And yes the values seem to be clipped (I think I read this somewhere, but it may also have been on an iMX6 post).
By the way, for a quick hack, it's easiest to just write this values through /dev/mem
@tsmt
>>By the way, for a quick hack, it's easiest to just write this values through /dev/mem
would you share your config script?
I would like to quickly reproduce the RGB2RGB just to check.
otherwise I will read the register addresses from the manual but I am too lazy
Thx
Have you tried setting CSC_MODE to 0x04 so CSC_MODE 2,RGB2YUV or even CSC_MODE to 0x06 so CSC_MODE 3, RGB2YCbCr ?
The RGB2YUV "seems" most useful.
Hi Terry,
Could you solve this problem?
I have exact same issue.
GPU cannot solve this because AFAIK Color gain cannot be set using e.g G2D API.
Thank you,
Unfortunately no, its a shame NXP did not provide this ability with the colour conversion matrix as used for YUV -> RGB could not be used for RGB -> RGB.
In my case we had a TP2856 analogue TV to CSI chip that had this ability. Although this only affect the video, this was good enough for our usage.
Thanks for the info.
When you say "lcdif only has normal colour space conversion matrix" is this matrix programmable or are you saying there really is no matrix (ie: 1:1 RGB to RGB with no changes ?)
If so its a shame the colour conversion matrix as used for YUV -> RGB could not be used for RGB -> RGB.
I mean only support YUV to RGB or RGB to YUV color space convertion
yes, lcdif only has normal colour space conversion matrix, maybe customer needs to use GPU to do this job, but we don't have such demo on imx8mp, imx8x display controller has this feature