-
Notifications
You must be signed in to change notification settings - Fork 8.3k
drivers: ssd16xx: Make voltage overrides optional #47712
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
drivers: ssd16xx: Make voltage overrides optional #47712
Conversation
galak
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add description for new dts props
4d0ce9b to
dd9b794
Compare
dd9b794 to
5b12402
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes contrast worse than with default values on GDEH0213B72 displays. Can you counter test or remove it for GDEH0213B72 displays?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to use the values from the reference code or the values that produce better contrast here? I'm generally a bit reluctant to mess with these values since some of them (mainly voltages) presumably affect display reliability.
I can double check the values tonight and update to whatever we decide.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I think I know what's going on.
Broadly speaking, there are two generations of ssd16xx devices. These operate in subtly different ways, especially when it comes to partial refresh and some control registers (dummy line seems to be gen1 specific). I didn't really understand why some devices load two LUTs until recently. It seems like we are configuring most(?) gen 1 devices to use partial refresh using the second LUT. This is very strange though since we first configure the device for full refresh to clear the screen and then irreversibly enable partial refresh (but a lot of registers are incorrectly left in their full refresh state).
This causes two issues. The first problem is that there is no way to cause a full refresh to remove ghosting. As far as I understand, this will cause panel degradation. The second issue is that we potentially don't use the right voltages / timings for one of the LUTs. Looking at the example code from Waveshare, it seems like they got it wrong as well and don't correctly program the various voltages correctly when entering partial refresh.
The Waveshare example code use 0x30/0x0a for full refresh. Weirdly, the LUT table for partial refresh specifies 0x30/0x0a, but the code never writes that to the control registers but that's probably not a problem since they seem to correspond to the reset values. The old driver used 0x1a/0x08 which I have no idea where it came from.
I would suggest that we keep the new values since they are correct with respect to the LUT. You can find the corresponding example here: https://github.com/waveshare/e-Paper/blob/master/RaspberryPi_JetsonNano/c/lib/e-Paper/EPD_2in13_V2.c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Broadly speaking, there are two generations of ssd16xx devices. These operate in subtly different ways, especially when it comes to partial refresh and some control registers (dummy line seems to be gen1 specific). I didn't really understand why some devices load two LUTs until recently. It seems like we are configuring most(?) gen 1 devices to use partial refresh using the second LUT. This is very strange though since we first configure the device for full refresh to clear the screen and then irreversibly enable partial refresh (but a lot of registers are incorrectly left in their full refresh state).
The driver supports several similar controllers.
You probably mean the controllers on displays GDEH0213B1 (label HINK-E0213) and GDEH0213B72 (label HINK-E0213A22), both have been discontinued for a long time. Yes they use different controllers, also HINK-E0213 has no values in OTP at all. HINK-E0213A22 has values in OTP that produce very good results (but IIRC are too slow for reel board demo). Manufacturer for this EPDs (electrophoretic displays) is Good Display not Waveshare.
This causes two issues. The first problem is that there is no way to cause a full refresh to remove ghosting. As far as I understand, this will cause panel degradation. The second issue is that we potentially don't use the right voltages / timings for one of the LUTs. Looking at the example code from Waveshare, it seems like they got it wrong as well and don't correctly program the various voltages correctly when entering partial refresh.
The driver makes full refresh at initialization. IMHO ghosting is not avoidable when using partial refresh (faster/simpler wave-forms) and the only correct way is to use setting from OTP (if they are available). "right voltages" depends also on the circuit and components.
The Waveshare example code use 0x30/0x0a for full refresh. Weirdly, the LUT table for partial refresh specifies 0x30/0x0a, but the code never writes that to the control registers but that's probably not a problem since they seem to correspond to the reset values. The old driver used 0x1a/0x08 which I have no idea where it came from.
Timings 0x1a/0x08 are for HINK-E0213, and come from Good Display sample code. And your are right, 0x30/0x0a are for HINK-E0213A22.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Broadly speaking, there are two generations of ssd16xx devices. These operate in subtly different ways, especially when it comes to partial refresh and some control registers (dummy line seems to be gen1 specific). I didn't really understand why some devices load two LUTs until recently. It seems like we are configuring most(?) gen 1 devices to use partial refresh using the second LUT. This is very strange though since we first configure the device for full refresh to clear the screen and then irreversibly enable partial refresh (but a lot of registers are incorrectly left in their full refresh state).
The driver supports several similar controllers. You probably mean the controllers on displays GDEH0213B1 (label HINK-E0213) and GDEH0213B72 (label HINK-E0213A22), both have been discontinued for a long time. Yes they use different controllers, also HINK-E0213 has no values in OTP at all. HINK-E0213A22 has values in OTP that produce very good results (but IIRC are too slow for reel board demo). Manufacturer for this EPDs (electrophoretic displays) is Good Display not Waveshare.
As far as I can tell, the devices I referred to as gen2 devices are mostly SSD168x and SSD1675B (but not SSD1675A) based panels. I think the only gen2 device we have in-tree is the GDEH0154D67 (HINK-E0154A07). Some of the SSD167x (at least SSD1673) seem to be gen 1 while the SSD1675B seems to be a gen 2. I think there might be a class of gen0 devices as well judging by the differences in the data sheets I have seen.
The important difference between the generations isn't the OTP. It's how the device looks up LUTs in the OTP and how to enable partial refresh. It seems like gen2 devices have introduced a new flag in SSD16XX_CMD_UPDATE_CTRL2 that enables what is referred to as "mode 2". Gen 1 devices seem to use the same bit for something else, but it isn't clear what it does from the datasheet. Gen 1 devices, similar to UC81xx devices, seem to require software overriding the LUT to enable fast partial / differential updates.
I think one of the reasons they made this change is that it makes it possible for gen2 devices to use a single master activation to load either a full refresh or a partial refresh LUT from OTP by looking at the mode bit.
This causes two issues. The first problem is that there is no way to cause a full refresh to remove ghosting. As far as I understand, this will cause panel degradation. The second issue is that we potentially don't use the right voltages / timings for one of the LUTs. Looking at the example code from Waveshare, it seems like they got it wrong as well and don't correctly program the various voltages correctly when entering partial refresh.
The driver makes full refresh at initialization. IMHO ghosting is not avoidable when using partial refresh (faster/simpler wave-forms) and the only correct way is to use setting from OTP (if they are available). "right voltages" depends also on the circuit and components.
Yes, it does make a full refresh at initialisation. However, the manual for many of these panels specify that a full refresh is needed at a regular interval. Typically daily or after a fixed number of partial updates. This is currently not possible.
As for right voltages, the my understanding is that VCOM/VGH/VSH/VSL values are strongly dependant on the panel, LUT, and the frame rate (controlled by dummy line and friends). A higher voltage differential between the back and the front of the panel results in quicker movement, which means that a shorter update can be achieved. The LUT tables assume a specific frame rate and specifics voltages. If these don't match, you potentially end up driving the pixels too hard and/or too long which can result in permanent damage.
In this case, we seem to be using the VCOM voltage (0x26 / -0.95V) from the partial refresh instead of a full refresh (0x55 / -2.1V).
The Waveshare example code use 0x30/0x0a for full refresh. Weirdly, the LUT table for partial refresh specifies 0x30/0x0a, but the code never writes that to the control registers but that's probably not a problem since they seem to correspond to the reset values. The old driver used 0x1a/0x08 which I have no idea where it came from.
Timings 0x1a/0x08 are for HINK-E0213, and come from Good Display sample code. And your are right, 0x30/0x0a are for HINK-E0213A22.
So, should we go with the values matching the panel here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, should we go with the values matching the panel here?
Let me test it again please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have always seen variation in contrast among the same display types. It seems to be higher with these values, though I no longer have access to significant numbers of panels for an experiment.
5b12402 to
bd819fb
Compare
|
@mbolivar-nordic you have reviewed a few other changes related to this driver. Could you have a look at this PR as well? |
Device activation always follows the same sequence, we first write the SSD16XX_CMD_UPDATE_CTRL_2 register followed by SSD16XX_CMD_MASTER_ACTIVATION. Create a helper function that performs both of these actions. Signed-off-by: Andreas Sandberg <andreas@sandberg.uk>
Many register writes only use 8 bits of data. A common pattern in the driver is to assign a constant to a temporary variable and write that to the device. Simplify this pattern by adding a helper function that writes an uint8 to a device. Signed-off-by: Andreas Sandberg <andreas@sandberg.uk>
The OTP in most SSD16xx-based displays normally contain default VCOM/GDV/SDV values. Make all of these optional in the device tree. Signed-off-by: Andreas Sandberg <andreas@sandberg.uk>
The SSD16xx driver currently hard-codes a couple of register overrides that aren't relevant or even correct for many devices. Make them optional device tree properties instead. Note that this changes the behavior for panels that expect SSD16XX_CMD_DUMMY_LINE and SSD16XX_CMD_GATE_LINE_WIDTH to be set by the driver. This fixes a bug where the incorrect value was written to all SSD16xx panels except for GDEH0213B1 and GDEH029A1. The overlay files for devices that need dummy line and gate line width to be specified have been updated as a part of this commit. Signed-off-by: Andreas Sandberg <andreas@sandberg.uk>
bd819fb to
8d6a18f
Compare
|
@mbolivar-nordic Can you please take a look? |
Many modern panels don't require any overrides to work. All the relevant voltages are normally loaded from OTP when performing a refresh with the built-in LUT. Such panels are currently not possible to drive using the existing driver since it insists on specifying voltages and other parameters that aren't always provided by the vendor.
This pull requests includes a series of commits that:
Please not that the change that makes ssd167x-specific registers, specifically
SSD16XX_CMD_DUMMY_LINEandSSD16XX_CMD_GATE_LINE_WIDTH, optional and left with their default values unless the device tree provides an override. This is unlikely to cause issues in practice since the loaded values didn't match those used by some of the panels (e.g., GDEH0213B72) supported by Zephyr.Update 2022-07-26: Rebased onto latest main.
Update 2022-07-27: Rebased onto latest main to solve merge conflicts.