linux icon indicating copy to clipboard operation
linux copied to clipboard

drm/panel: add backlight support to ea8061v-ams497ee01 driver

Open Grimler91 opened this issue 3 years ago • 15 comments

After looking at the s6e3fa2 driver I have something that is not super ugly and mostly work. Compared to previous WIP it now becomes quite dark at low brightness, and it remembers the brightness when turning off and on the screen.

The panel becomes out of sync quite a lot when changing brightness. I can try playing with sleep times and other things to improve the situation. I also think (might be my imagination) that the screen becomes a bit green-ish at intermediate brightness levels. Perhaps because some things have been simplified compared to downstream, mainly:

  • Gamma table is dumped from downstream kernel rather than calculated dynamically
  • We do no temperature correction. This should mainly be important for panel function at low temperatures I think though.
  • ACL (automatic current limit(?)) is set to always off rather than on below some brightness threshold

Another issue (which was a problem before my updates here as well) is that the screen can become sluggish after the device comes out of sleep. If I have left the device with screen off for X minutes I sometimes have to do slow movements/touch the screen for several seconds for the touchscreen to react, so inputting the pin-code takes quite some time. After unlocking the screen usually becomes responsive again after a short time.


There is still room for improvement in the driver as well:

  • The magic numbers (0xb2, 0xb6 and so on) should be defined close to the top, as in s6e3fa2 driver
  • A few commands more can be put in more descriptive functions instead (like dsi_dcs_write_seq(dsi, 0xb5, 0x29); -> ea8061v_ams497ee01_acl_opr_on(ctx);)
  • Go through comments and error messages
  • Other things?

Ping @minlexx as well since we discussed these panel drivers before, and you have the a5ulte.

Grimler91 avatar Jun 14 '21 16:06 Grimler91

Oh yeah, and if someone wants to try it, remember that the device package has to be updated after building the kernel modules, or this commit reverted.

Grimler91 avatar Jun 14 '21 16:06 Grimler91

Oh yeah, and if someone wants to try it, remember that the device package has to be updated after building the kernel modules, or this commit reverted.

The kernel module does not become part of the device package, the device package just says which kernel modules should be included in the initramfs. AFAICT you did not change the module name so it should still work fine with those changes in the device package?

stephan-gh avatar Jun 14 '21 16:06 stephan-gh

the device package just says which kernel modules should be included in the initramfs. AFAICT you did not change the module name so it should still work fine with those changes in the device package?

Right, re-building initramfs should be enough. I spent some time when I started looking at this figuring out why my changes did not have an effect when I built linux-postmarketos-qcom-msm8916 or pushed the updated module to /lib/firmware, so just want to save other ~~noobs~~ people like me that trouble.. :)

Grimler91 avatar Jun 14 '21 16:06 Grimler91

the screen can become sluggish after the device comes out of sleep

This sounds like a general issue with sleep, not with panel driver.

minlexx avatar Jun 14 '21 17:06 minlexx

recently I've tried to debug brightness on a5lte, using patches from latest commit in this branch: https://gitlab.com/minlexx/pmaports/-/commits/a5lte-debug . "Reduce dmesg spam" one helps to get a less spammy dmesg output, and 2nd one - uncommented (+slightly fixed) cmd dumper in mdss_dsi_panel_cmds_send allows us to see all the commands sent to panel in dmesg in a nice way

image

Uncommented #define SMART_DIMMING_DEBUG allows us to see gamma tables dump, like this: a5lte-panel-gamma-tables.log (useful info is at the bottom)

But the most interesting are brightness change command dumps, take a look at https://paste.sr.ht/~minlexx/8dafcf2400372cb369a14cc767b027f770c045f4. They look a bit different from what I saw for klte panel, this driver wraps every sub-command with level2-key-enable/disable pairs (f0 5a 5a, f1 5a 5a ... f0 a5 a5, f1 a5 a5). Klte panel uses single "level2-key-enable" operation at the beginning of brightness change operation, then does all the thigs, and then "level2-key-disable". This driver is special, it wraps every command in those sequences.. Feel the difference, roughly: klte:

  • enter level 2 mode
    • send aid
    • send elvss
    • send gamma
  • leave level 2 mode

a5lte:

  • enter level 2 mode, send aid, leave level 2 mode
  • enter level 2 mode, send elvss, leave level 2 mode
  • enter level 2 mode, send elvss, leave level 2 mode (again, for some reason??)
  • enter level 2 mode, send gamma, leave level 2 mode

:see_no_evil: this results in much longer sequences, and if we want to mimic downstream driver behaviour fully (I think we want) - this driver will need to be adjusted, as I currently don't see f0 5a 5a, f1 5a 5a .. sequences here at all.

minlexx avatar Jul 26 '21 08:07 minlexx

The explanation of "level 2 test keys" from linusw:

alexeymin: essentially commands intended to be used during manufacturing.
Just that Samsung tend to use it in deployment. Display manufacturers normally
have the same "firmware ambition" to make the display self-configure from factory
programmed "MTP" (manufacturing time programmable) settings.
Samsung doesn't do this. The reason can be to save cost (no flash EEPROM) or
speed up deployment and making it field-alterable.

Looks like it's important to exit from that programming mode after entering it, to avoid some display data (or other things) to be accidentally interpreted as some "MTP" command? Maybe we can do like klte's panel does, wrap the whole brightnes set operation in enter-exit pair at least, wrapping every subcommand seems like too much

minlexx avatar Jul 26 '21 11:07 minlexx

allows us to see gamma tables dump, like this: a5lte-panel-gamma-tables.log

Your dump differs a bit from the one I got, will check yours and see if the colors seem better.

see_no_evil this results in much longer sequences, and if we want to mimic downstream driver behaviour fully (I think we want)

Mimic'ing downstream is probably the safest, but the driver for a5lte is clearly written to be as generic as possible and to handle all possible types of panels, to make samsung's life easier. Really feels like unlocking and locking again on every command is something they have added because some panel (not necessarily ea8061v ams497ee01) needed it. If we look at other ea8061v drivers (and assuming they should work similarly, which we can't be sure of I guess), they do it more like klte does it (this is the driver my first version was based on, it also worked reasonably well).

this driver will need to be adjusted, as I currently don't see f0 5a 5a, f1 5a 5a .. sequences here at all.

I unlock them, and leave them unlocked, in the init function. Given the level 2 testkey explanation I definitely agree that they should be locked again, preferably in the way klte does it.

You had "B8 14" and "B5 21" in your dump, B8 14 probably sets some reference temperature to 20 deg C, since B8 19 seem to set reference temperature 25 degC, and B5 21 seem to be the "opposite" of B5 29.

Grimler91 avatar Aug 07 '21 13:08 Grimler91

You had "B8 14" and "B5 21" in your dump, B8 14 probably sets some reference temperature to 20 deg C, since B8 19 seem to set reference temperature 25 degC, and B5 21 seem to be the "opposite" of B5 29.

Nice, then we can have functions named like ..._set_tset(), acl_off/acl_on or in some other meaningful way instead of "unknown bytes sequence"

minlexx avatar Aug 07 '21 23:08 minlexx

@minlexx I tested with the lux dump you captured but could not tell any difference from with what I have now. I guess the downstream driver can calculate a slightly different tables on different devices (since it is generated dynamically)

Grimler91 avatar Aug 11 '21 19:08 Grimler91

Do you have any plans to submit the full driver upstream?

If not, please keep the driver in msm8916-generated for now and just have incremental changes on top of the generated driver (even if they are quite large). That way we can still try to regenerate the base driver with refactorings made upstream and either have them merged in automatically or at least get a conflict that signifies the driver might need changes.

stephan-gh avatar Jul 29 '23 12:07 stephan-gh

Do you have any plans to submit the full driver upstream?

Not until the remaining flickering issue has been resolved at least.

If not, please keep the driver in msm8916-generated for now and just have incremental changes on top of the generated driver (even if they are quite large). That way we can still try to regenerate the base driver with refactorings made upstream and either have them merged in automatically or at least get a conflict that signifies the driver might need changes.

Sure, makes sense. I moved it back to msm8916-generated folder, and edited the Kconfig option in drivers/gpu/drm/panel/msm8916-generated instead.

Grimler91 avatar Jul 30 '23 08:07 Grimler91

Not until the remaining flickering issue has been resolved at least.

I think this is likely not an issue with your driver but rather in the Qualcomm/msm DSI implementation. Similar issues when changing the brightness levels via DSI commands also exist on other devices. It's not that bad there because they only need to send a single command though and not multiple like you here.

In other words, I think fixing the flickering does likely not involve any changes in the panel driver, but rather in the common msm DSI code.

Sure, makes sense. I moved it back to msm8916-generated folder, and edited the Kconfig option in drivers/gpu/drm/panel/msm8916-generated instead.

Thanks!

stephan-gh avatar Jul 30 '23 14:07 stephan-gh

I tried to minimize the diff compared to the generated driver a bit more ~~and pushed an updated version to your branch~~ (I don't have permission for some reason, GitHub bug?). I pushed it to wip/msm8916/a5lte-backlight (https://github.com/msm8916-mainline/linux/commit/2d54c1f70db3cfff0b99d489a69ba3abc95337d9). Can you check if this looks good to you and test if it still works?

Some notable differences:

  • The backlight handling is closer to the generated code now. It assigns the backlight to the drm_panel. With this, backlight_enable() and backlight_disable() happens automatically on the enable()/disable() calls and do not need to be handled in the driver (see drm_panel_enable()).
  • The mipi_dsi_dcs_exit_sleep_mode() in the enable() callback seems to be duplicated, since it's already in ea8061v_ams497ee01_init(). I just dropped the enable()/disable() callbacks entirely for consistency.

I noticed some more minor things, but I will add separate comments for these.

The main code for setting the actual brightness looks good to me, thanks for trying to understand the mess Samsung did there. :)

Backup of the previous version for reference
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (c) 2023 FIXME
// Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree:
//   Copyright (c) 2013, The Linux Foundation. All rights reserved. (FIXME)
//
// Mostly based on s6e3fa2 and s6e63m0. Table values dumped from
// Samsung's SM-A500F android kernel

#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>

#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>


#define EA8061V_AMS497EE01_NUM_GAMMA_LEVELS	62
#define EA8061V_AMS497EE01_GAMMA_CMD_CNT	34

#define EA8061V_AMS497EE01_MIN_BRIGHTNESS	0
#define EA8061V_AMS497EE01_MAX_BRIGHTNESS	(EA8061V_AMS497EE01_NUM_GAMMA_LEVELS - 1)
#define EA8061V_AMS497EE01_DEFAULT_BRIGHTNESS	40

/* Commands from manufacturer */
#define MCS_ACL_OFF		0x5c
#define MCS_ACL_ON		0x4c
#define MCS_ACL_CONTROL		0x55
#define MCS_AID_CONTROL		0xb2 /* Samsung AMOLED Impulsive Driving */
#define MCS_ACL_OPR_CONTROL	0xb5
#define MCS_ELVSS_CONTROL	0xb6 /* Amoled negative power supply */
#define MCS_TSET		0xb8 /* Set reference conditions */
#define MCS_GAMMA		0xca
#define MCS_GAMMA_UPDATE	0xf7

/* Which ELVSS sequence to use for which candela level.
 */
static const u8 map_candela_to_elvss[EA8061V_AMS497EE01_NUM_GAMMA_LEVELS] = {
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	1, 1, 2, 2, 3, 4, 4, 5, 6,
	7, 7, 7, 8, 9, 10,
	11, 11, 11, 12, 13, 14, 15, 16, 17
};

static const u8 seq_ea8061v_ams497ee01_lux[EA8061V_AMS497EE01_NUM_GAMMA_LEVELS][EA8061V_AMS497EE01_GAMMA_CMD_CNT] = {
	{ MCS_GAMMA,
	  0x00, 0x92, 0x00, 0xac, 0x00, 0x7e, 0x8a, 0x8f, 0x8b,
	  0x8d, 0x91, 0x8d, 0x96, 0xa1, 0x99, 0x9b, 0xa5, 0x00,
	  0x9c, 0x95, 0xa1, 0x8d, 0x9d, 0xa3, 0xa1, 0xba, 0xbb,
	  0xec, 0xbd, 0xd5, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x93, 0x00, 0xac, 0x00, 0x7e, 0x8b, 0x8e, 0x8b,
	  0x8d, 0x90, 0x8c, 0x93, 0x9e, 0x96, 0x94, 0xa1, 0x00,
	  0x96, 0x96, 0xa2, 0x8d, 0x9e, 0xa2, 0xa1, 0xb9, 0xba,
	  0xe1, 0xb4, 0xcd, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x94, 0x00, 0xac, 0x00, 0x7f, 0x8a, 0x8d, 0x8a,
	  0x89, 0x8e, 0x8a, 0x92, 0x9d, 0x95, 0x95, 0xa1, 0x00,
	  0x97, 0x97, 0xa1, 0x8e, 0x98, 0xa0, 0x9d, 0xb3, 0xb7,
	  0xe0, 0xb3, 0xcb, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x94, 0x00, 0xac, 0x00, 0x7f, 0x8b, 0x8d, 0x8b,
	  0x89, 0x8e, 0x8a, 0x92, 0x9c, 0x94, 0x93, 0x9e, 0x00,
	  0x96, 0x95, 0x9f, 0x8d, 0x98, 0xa0, 0x9c, 0xb2, 0xb4,
	  0xe9, 0xba, 0xd2, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x94, 0x00, 0xac, 0x00, 0x7f, 0x8a, 0x8c, 0x8b,
	  0x89, 0x8d, 0x89, 0x91, 0x9b, 0x94, 0x93, 0x9e, 0x00,
	  0x95, 0x94, 0x9e, 0x8c, 0x97, 0x9e, 0x9c, 0xb3, 0xb4,
	  0xde, 0xb1, 0xc5, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x95, 0x00, 0xac, 0x00, 0x80, 0x89, 0x8c, 0x8a,
	  0x8a, 0x8d, 0x8b, 0x90, 0x99, 0x92, 0x8f, 0x9a, 0x00,
	  0x93, 0x93, 0x9e, 0x8c, 0x99, 0x9e, 0x9c, 0xb1, 0xb3,
	  0xd9, 0xae, 0xbf, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x96, 0x00, 0xac, 0x00, 0x81, 0x89, 0x8c, 0x8a,
	  0x89, 0x8b, 0x89, 0x90, 0x98, 0x92, 0x92, 0x9c, 0x00,
	  0x95, 0x90, 0x9b, 0x88, 0x98, 0x9e, 0x9c, 0xb1, 0xb1,
	  0xdd, 0xb0, 0xc3, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x96, 0x00, 0xac, 0x00, 0x81, 0x89, 0x8c, 0x8a,
	  0x8a, 0x8b, 0x8a, 0x8e, 0x96, 0x91, 0x92, 0x9b, 0x00,
	  0x97, 0x92, 0x9b, 0x8a, 0x92, 0x9d, 0x96, 0xae, 0xae,
	  0xdc, 0xb0, 0xc2, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x96, 0x00, 0xac, 0x00, 0x81, 0x89, 0x8b, 0x88,
	  0x8b, 0x8c, 0x8b, 0x8f, 0x96, 0x92, 0x8f, 0x99, 0x00,
	  0x93, 0x91, 0x9b, 0x88, 0x93, 0x9a, 0x98, 0xaf, 0xad,
	  0xe0, 0xb3, 0xc6, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x96, 0x00, 0xac, 0x00, 0x81, 0x8a, 0x8b, 0x89,
	  0x89, 0x8b, 0x87, 0x91, 0x97, 0x94, 0x8b, 0x96, 0x00,
	  0x91, 0x90, 0x9b, 0x89, 0x93, 0x9b, 0x95, 0xad, 0xac,
	  0xdf, 0xb3, 0xc5, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x89, 0x8b, 0x88,
	  0x89, 0x8b, 0x88, 0x90, 0x96, 0x92, 0x8f, 0x98, 0x00,
	  0x94, 0x8d, 0x98, 0x87, 0x93, 0x9a, 0x95, 0xad, 0xac,
	  0xdf, 0xb3, 0xc5, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x89, 0x8b, 0x88,
	  0x8a, 0x8b, 0x88, 0x90, 0x96, 0x92, 0x8c, 0x95, 0x00,
	  0x92, 0x8d, 0x98, 0x87, 0x92, 0x9a, 0x93, 0xae, 0xac,
	  0xd9, 0xae, 0xbf, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x89, 0x8b, 0x88,
	  0x8a, 0x8b, 0x88, 0x8e, 0x93, 0x90, 0x8e, 0x97, 0x00,
	  0x93, 0x8d, 0x98, 0x88, 0x92, 0x9a, 0x93, 0xa7, 0xa6,
	  0xea, 0xbb, 0xd1, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8b, 0x8a,
	  0x88, 0x8a, 0x87, 0x8c, 0x93, 0x90, 0x8e, 0x96, 0x00,
	  0x93, 0x8a, 0x95, 0x84, 0x90, 0x98, 0x8f, 0xad, 0xab,
	  0xdb, 0xb0, 0xc2, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8b, 0x8a,
	  0x88, 0x8a, 0x87, 0x8d, 0x93, 0x91, 0x8b, 0x93, 0x00,
	  0x91, 0x8e, 0x96, 0x88, 0x90, 0x98, 0x8e, 0xa6, 0xa5,
	  0xe3, 0xb7, 0xca, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8b, 0x8a,
	  0x88, 0x8a, 0x87, 0x8d, 0x93, 0x91, 0x8c, 0x93, 0x00,
	  0x92, 0x89, 0x94, 0x83, 0x8c, 0x94, 0x8b, 0xab, 0xab,
	  0xd2, 0xab, 0xba, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8b, 0x8a,
	  0x88, 0x8a, 0x87, 0x8c, 0x91, 0x8e, 0x8d, 0x93, 0x00,
	  0x92, 0x8c, 0x96, 0x86, 0x91, 0x98, 0x8f, 0xa6, 0xa6,
	  0xe7, 0xba, 0xce, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x89, 0x8a, 0x89,
	  0x89, 0x8a, 0x88, 0x8d, 0x92, 0x8f, 0x8d, 0x93, 0x00,
	  0x92, 0x89, 0x93, 0x83, 0x8f, 0x97, 0x8f, 0xa4, 0xa4,
	  0xd7, 0xae, 0xbf, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8b, 0x8a,
	  0x88, 0x89, 0x87, 0x8d, 0x92, 0x8f, 0x8d, 0x92, 0x00,
	  0x92, 0x89, 0x93, 0x83, 0x8c, 0x94, 0x8b, 0xa2, 0xa2,
	  0xe7, 0xba, 0xce, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x89, 0x8a, 0x89,
	  0x89, 0x8a, 0x88, 0x8d, 0x91, 0x90, 0x89, 0x8f, 0x00,
	  0x8f, 0x8c, 0x95, 0x84, 0x8b, 0x94, 0x8a, 0xa2, 0xa0,
	  0xe1, 0xb6, 0xc8, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x89, 0x8a, 0x89,
	  0x89, 0x8a, 0x88, 0x8e, 0x91, 0x90, 0x89, 0x8f, 0x00,
	  0x90, 0x88, 0x91, 0x81, 0x8c, 0x94, 0x8a, 0xa6, 0xa6,
	  0xd6, 0xae, 0xbe, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x89, 0x8a, 0x89,
	  0x89, 0x8a, 0x88, 0x8d, 0x90, 0x90, 0x89, 0x8f, 0x00,
	  0x8f, 0x88, 0x91, 0x81, 0x8c, 0x93, 0x8a, 0xa7, 0xa6,
	  0xd1, 0xaa, 0xb8, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x89, 0x8a, 0x89,
	  0x89, 0x8a, 0x88, 0x8e, 0x90, 0x90, 0x86, 0x8c, 0x00,
	  0x8e, 0x87, 0x90, 0x7f, 0x8f, 0x96, 0x8e, 0xa2, 0xa0,
	  0xd6, 0xad, 0xbd, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8a, 0x89,
	  0x88, 0x89, 0x87, 0x8d, 0x8f, 0x8f, 0x88, 0x8d, 0x00,
	  0x90, 0x87, 0x90, 0x7f, 0x8f, 0x96, 0x8e, 0xa2, 0xa0,
	  0xd6, 0xad, 0xbd, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8a, 0x89,
	  0x89, 0x89, 0x87, 0x8c, 0x8f, 0x8f, 0x8a, 0x8d, 0x00,
	  0x91, 0x88, 0x90, 0x81, 0x8b, 0x93, 0x8a, 0xa1, 0x9e,
	  0xcf, 0xa9, 0xb7, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8a, 0x89,
	  0x89, 0x89, 0x87, 0x8a, 0x8e, 0x8e, 0x89, 0x8c, 0x00,
	  0x91, 0x87, 0x8f, 0x7f, 0x90, 0x96, 0x90, 0x99, 0x97,
	  0xe0, 0xb5, 0xc8, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8a, 0x89,
	  0x89, 0x89, 0x87, 0x8b, 0x8e, 0x8f, 0x89, 0x8c, 0x00,
	  0x91, 0x87, 0x8f, 0x7f, 0x89, 0x91, 0x88, 0x9f, 0x9e,
	  0xda, 0xb1, 0xc2, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8a, 0x89,
	  0x89, 0x89, 0x87, 0x8b, 0x8e, 0x8f, 0x87, 0x89, 0x00,
	  0x8f, 0x87, 0x8d, 0x7e, 0x8f, 0x95, 0x8d, 0x9a, 0x97,
	  0xd4, 0xad, 0xbc, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8a, 0x89,
	  0x8a, 0x89, 0x88, 0x8a, 0x8e, 0x8e, 0x88, 0x89, 0x00,
	  0x90, 0x81, 0x8a, 0x79, 0x93, 0x98, 0x92, 0x9b, 0x98,
	  0xd4, 0xad, 0xbc, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8a, 0x89,
	  0x89, 0x88, 0x87, 0x89, 0x8d, 0x8d, 0x8a, 0x8b, 0x00,
	  0x91, 0x81, 0x8a, 0x79, 0x93, 0x97, 0x92, 0x9b, 0x98,
	  0xba, 0x9d, 0xa8, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8a, 0x89,
	  0x89, 0x88, 0x87, 0x8a, 0x8d, 0x8e, 0x86, 0x88, 0x00,
	  0x8e, 0x87, 0x8d, 0x7d, 0x8e, 0x93, 0x8e, 0x94, 0x91,
	  0x9c, 0x8d, 0x92, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8a, 0x89,
	  0x89, 0x88, 0x87, 0x8a, 0x8d, 0x8e, 0x87, 0x88, 0x00,
	  0x8f, 0x82, 0x89, 0x79, 0x8d, 0x92, 0x8b, 0x97, 0x95,
	  0xdf, 0xb5, 0xc7, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8a, 0x89,
	  0x89, 0x88, 0x87, 0x8b, 0x8d, 0x8e, 0x86, 0x88, 0x00,
	  0x8f, 0x82, 0x88, 0x79, 0x8c, 0x92, 0x8b, 0x99, 0x96,
	  0xd2, 0xad, 0xbc, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x8a, 0x8a, 0x89,
	  0x89, 0x88, 0x87, 0x8b, 0x8d, 0x8e, 0x86, 0x87, 0x00,
	  0x8f, 0x82, 0x88, 0x79, 0x8d, 0x92, 0x8c, 0x9b, 0x97,
	  0x9c, 0x8d, 0x92, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x97, 0x00, 0xac, 0x00, 0x82, 0x89, 0x89, 0x88,
	  0x8a, 0x89, 0x88, 0x8b, 0x8d, 0x8e, 0x86, 0x87, 0x00,
	  0x8f, 0x83, 0x88, 0x7a, 0x8e, 0x92, 0x8d, 0x93, 0x8f,
	  0xbc, 0xa1, 0xac, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0x9b, 0x00, 0xaf, 0x00, 0x87, 0x88, 0x89, 0x88,
	  0x89, 0x88, 0x87, 0x8b, 0x8d, 0x8e, 0x87, 0x86, 0x00,
	  0x8f, 0x83, 0x88, 0x7a, 0x8e, 0x92, 0x8d, 0x94, 0x90,
	  0xa1, 0x91, 0x97, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xa0, 0x00, 0xb3, 0x00, 0x8d, 0x89, 0x89, 0x88,
	  0x89, 0x88, 0x86, 0x8b, 0x8c, 0x8d, 0x85, 0x85, 0x00,
	  0x8d, 0x84, 0x89, 0x7c, 0x8a, 0x8e, 0x89, 0x93, 0x8f,
	  0xca, 0xa9, 0xb7, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xa4, 0x00, 0xb6, 0x00, 0x92, 0x88, 0x89, 0x89,
	  0x88, 0x87, 0x86, 0x8a, 0x8b, 0x8b, 0x87, 0x86, 0x00,
	  0x8c, 0x81, 0x86, 0x79, 0x89, 0x8e, 0x88, 0x9b, 0x98,
	  0x81, 0x7d, 0x7d, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xa8, 0x00, 0xba, 0x00, 0x97, 0x88, 0x88, 0x88,
	  0x88, 0x87, 0x86, 0x87, 0x8b, 0x8a, 0x8a, 0x88, 0x00,
	  0x8f, 0x83, 0x88, 0x7d, 0x87, 0x8c, 0x87, 0x95, 0x91,
	  0xa9, 0x95, 0x9d, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xad, 0x00, 0xbe, 0x00, 0x9d, 0x87, 0x88, 0x88,
	  0x88, 0x87, 0x86, 0x8a, 0x8b, 0x8c, 0x86, 0x85, 0x00,
	  0x89, 0x82, 0x88, 0x7d, 0x87, 0x8b, 0x87, 0x96, 0x91,
	  0x9c, 0x8d, 0x92, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xb1, 0x00, 0xc1, 0x00, 0xa2, 0x87, 0x88, 0x88,
	  0x88, 0x87, 0x86, 0x87, 0x89, 0x89, 0x87, 0x86, 0x00,
	  0x8b, 0x80, 0x85, 0x7b, 0x8c, 0x8f, 0x8c, 0x8f, 0x8c,
	  0xc4, 0xa5, 0xb2, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xb7, 0x00, 0xc5, 0x00, 0xa8, 0x86, 0x87, 0x87,
	  0x88, 0x86, 0x85, 0x88, 0x89, 0x89, 0x87, 0x86, 0x00,
	  0x8a, 0x81, 0x86, 0x7d, 0x8a, 0x8d, 0x88, 0x8f, 0x8c,
	  0xb7, 0x9d, 0xa7, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xbc, 0x00, 0xc9, 0x00, 0xae, 0x86, 0x87, 0x87,
	  0x87, 0x86, 0x85, 0x87, 0x88, 0x87, 0x86, 0x87, 0x00,
	  0x8a, 0x80, 0x84, 0x7b, 0x8a, 0x8c, 0x88, 0x90, 0x8d,
	  0x9c, 0x8d, 0x92, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xc1, 0x00, 0xcd, 0x00, 0xb4, 0x86, 0x87, 0x87,
	  0x87, 0x86, 0x86, 0x86, 0x88, 0x87, 0x87, 0x86, 0x00,
	  0x88, 0x82, 0x84, 0x7c, 0x85, 0x89, 0x86, 0x91, 0x8e,
	  0x9c, 0x8d, 0x92, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xc6, 0x00, 0xd1, 0x00, 0xba, 0x85, 0x86, 0x86,
	  0x86, 0x85, 0x85, 0x85, 0x86, 0x86, 0x88, 0x87, 0x00,
	  0x89, 0x82, 0x86, 0x7d, 0x80, 0x86, 0x82, 0x91, 0x8e,
	  0xb7, 0x9d, 0xa7, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xca, 0x00, 0xd4, 0x00, 0xbf, 0x86, 0x87, 0x87,
	  0x86, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x84, 0x00,
	  0x86, 0x82, 0x85, 0x7f, 0x80, 0x86, 0x82, 0x91, 0x8e,
	  0x9c, 0x8d, 0x92, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xd0, 0x00, 0xd9, 0x00, 0xc6, 0x84, 0x85, 0x85,
	  0x85, 0x84, 0x84, 0x83, 0x85, 0x84, 0x86, 0x85, 0x00,
	  0x86, 0x7f, 0x82, 0x7c, 0x87, 0x8a, 0x87, 0x8e, 0x8a,
	  0x9c, 0x8d, 0x92, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xd5, 0x00, 0xdd, 0x00, 0xcc, 0x85, 0x86, 0x86,
	  0x85, 0x84, 0x84, 0x82, 0x84, 0x83, 0x86, 0x85, 0x00,
	  0x85, 0x80, 0x83, 0x7e, 0x83, 0x86, 0x84, 0x8d, 0x89,
	  0xa9, 0x95, 0x9d, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xda, 0x00, 0xe1, 0x00, 0xd3, 0x84, 0x84, 0x84,
	  0x84, 0x84, 0x84, 0x82, 0x83, 0x83, 0x86, 0x85, 0x00,
	  0x85, 0x83, 0x85, 0x7f, 0x7f, 0x84, 0x81, 0x8f, 0x8b,
	  0x8f, 0x85, 0x88, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xda, 0x00, 0xe1, 0x00, 0xd2, 0x84, 0x84, 0x84,
	  0x84, 0x84, 0x84, 0x82, 0x83, 0x83, 0x86, 0x85, 0x00,
	  0x85, 0x84, 0x85, 0x80, 0x7f, 0x83, 0x81, 0x8f, 0x8b,
	  0x74, 0x75, 0x73, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xda, 0x00, 0xe1, 0x00, 0xd2, 0x84, 0x84, 0x84,
	  0x83, 0x83, 0x83, 0x84, 0x84, 0x85, 0x83, 0x84, 0x00,
	  0x84, 0x82, 0x83, 0x7f, 0x7f, 0x81, 0x80, 0x8e, 0x8a,
	  0x85, 0x81, 0x81, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xda, 0x00, 0xe1, 0x00, 0xd2, 0x84, 0x84, 0x84,
	  0x83, 0x83, 0x83, 0x84, 0x84, 0x85, 0x83, 0x84, 0x00,
	  0x84, 0x7f, 0x80, 0x7b, 0x83, 0x85, 0x85, 0x87, 0x84,
	  0xa5, 0x94, 0x9c, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xda, 0x00, 0xe1, 0x00, 0xd2, 0x84, 0x84, 0x84,
	  0x83, 0x83, 0x83, 0x82, 0x83, 0x83, 0x82, 0x82, 0x00,
	  0x83, 0x82, 0x82, 0x7e, 0x82, 0x85, 0x85, 0x89, 0x85,
	  0x7b, 0x7c, 0x7b, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xda, 0x00, 0xe1, 0x00, 0xd2, 0x84, 0x84, 0x84,
	  0x83, 0x83, 0x83, 0x82, 0x83, 0x83, 0x83, 0x82, 0x00,
	  0x84, 0x81, 0x82, 0x7d, 0x83, 0x85, 0x85, 0x86, 0x86,
	  0x97, 0x8c, 0x91, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xda, 0x00, 0xe1, 0x00, 0xd2, 0x84, 0x84, 0x84,
	  0x82, 0x82, 0x82, 0x83, 0x84, 0x84, 0x84, 0x82, 0x00,
	  0x84, 0x82, 0x82, 0x7e, 0x7e, 0x80, 0x80, 0x8d, 0x8b,
	  0x7b, 0x7c, 0x7b, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xda, 0x00, 0xe1, 0x00, 0xd2, 0x83, 0x84, 0x84,
	  0x84, 0x84, 0x84, 0x82, 0x83, 0x83, 0x82, 0x82, 0x00,
	  0x85, 0x82, 0x82, 0x7e, 0x84, 0x85, 0x85, 0x8b, 0x8a,
	  0x7b, 0x7c, 0x7b, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xe0, 0x00, 0xe6, 0x00, 0xda, 0x83, 0x83, 0x83,
	  0x83, 0x82, 0x82, 0x83, 0x83, 0x83, 0x82, 0x81, 0x00,
	  0x84, 0x83, 0x83, 0x80, 0x81, 0x81, 0x81, 0x8b, 0x8a,
	  0x7b, 0x7c, 0x7b, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xe6, 0x00, 0xeb, 0x00, 0xe1, 0x82, 0x83, 0x83,
	  0x83, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81, 0x81, 0x00,
	  0x83, 0x84, 0x84, 0x82, 0x83, 0x83, 0x83, 0x86, 0x85,
	  0x89, 0x84, 0x86, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xec, 0x00, 0xf0, 0x00, 0xe8, 0x82, 0x83, 0x83,
	  0x81, 0x81, 0x81, 0x80, 0x81, 0x81, 0x82, 0x82, 0x00,
	  0x83, 0x81, 0x81, 0x80, 0x83, 0x83, 0x83, 0x86, 0x85,
	  0x89, 0x84, 0x86, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xf1, 0x00, 0xf4, 0x00, 0xee, 0x81, 0x81, 0x81,
	  0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x81, 0x80, 0x00,
	  0x82, 0x81, 0x81, 0x80, 0x83, 0x83, 0x83, 0x86, 0x85,
	  0x7b, 0x7c, 0x7b, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x00, 0xf6, 0x00, 0xf8, 0x00, 0xf4, 0x82, 0x82, 0x82,
	  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
	  0x81, 0x82, 0x82, 0x81, 0x7f, 0x7f, 0x7f, 0x86, 0x85,
	  0x7b, 0x7c, 0x7b, 0x00, 0x00, 0x00 },
	{ MCS_GAMMA,
	  0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x80, 0x80, 0x80,
	  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
	  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
	  0x80, 0x80, 0x80, 0x00, 0x00, 0x00 }
};

/* ELVSS (Amoled negative power supply) tables */
#define EA8061V_AMS497EE01_NUM_ELVSS_SEQUENCES	18
#define ELVSS_SEQUENCE_LEN			3
static const u8 seq_ea8061v_ams497ee01_elvss[EA8061V_AMS497EE01_NUM_ELVSS_SEQUENCES][ELVSS_SEQUENCE_LEN] = {
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x9b },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x9a },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x99 },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x98 },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x97 },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x96 },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x95 },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x94 },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x93 },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x92 },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x91 },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x90 },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x8f },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x8e },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x8d },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x8c },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x8b },
	{ MCS_ELVSS_CONTROL, MCS_ACL_OFF, 0x8a }
};

#define EA8061V_AMS497EE01_NUM_AID_LEVELS	42
#define EA8061V_AMS497EE01_AID_CMD_CNT		5
/* Which AID sequence to use for each candela level.
 */
static const u8 map_candela_to_aid[EA8061V_AMS497EE01_NUM_GAMMA_LEVELS] = {
	 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,
	12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
	24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 34,
	34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
	34, 35, 36, 37, 38, 39, 40, 41, 41, 41, 41, 41,
	41, 41
};

static const u8 seq_ea8061v_ams497ee01_aid[EA8061V_AMS497EE01_NUM_AID_LEVELS][EA8061V_AMS497EE01_AID_CMD_CNT] = {
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0xdf }, /* 95.63 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0xd3 }, /* 94.71 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0xc7 }, /* 93.79 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0xbb }, /* 92.87 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0xb0 }, /* 92.02 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0xa4 }, /* 91.10 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0x99 }, /* 90.26 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0x8d }, /* 89.34 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0x82 }, /* 88.50 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0x76 }, /* 87.58 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0x6a }, /* 86.66 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0x5f }, /* 85.81 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0x52 }, /* 84.82 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0x3b }, /* 83.05 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0x31 }, /* 82.29 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0x25 }, /* 81.37 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0x18 }, /* 80.37 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x04, 0x01 }, /* 78.60 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x03, 0xf5 }, /* 77.68 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x03, 0xdb }, /* 75.69 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x03, 0xc5 }, /* 74.00 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x03, 0xb7 }, /* 72.93 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x03, 0x9e }, /* 71.01 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x03, 0x88 }, /* 69.33 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x03, 0x64 }, /* 66.56 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x03, 0x4b }, /* 64.64 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x03, 0x32 }, /* 62.73 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x03, 0x0d }, /* 59.89 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x02, 0xe7 }, /* 56.98 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x02, 0xc2 }, /* 54.14 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x02, 0x9d }, /* 51.30 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x02, 0x79 }, /* 48.54 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x02, 0x45 }, /* 44.56 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x02, 0x13 }, /* 40.72 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x01, 0xe7 }, /* 37.35 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x01, 0xae }, /* 32.98 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x01, 0x76 }, /* 28.68 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x01, 0x39 }, /* 24.00 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x00, 0xf8 }, /* 19.02 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x00, 0xb4 }, /* 13.80 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x00, 0x63 }, /*  7.59 % */
	{ MCS_AID_CONTROL, 0x00, 0x00, 0x00, 0x0a }  /*  0.77 % */
};

struct ea8061v_ams497ee01 {
	struct drm_panel panel;
	struct mipi_dsi_device *dsi;

	struct backlight_device *bl_dev;

	struct regulator_bulk_data supplies[2];
	struct gpio_desc *reset_gpio;

	bool prepared;
	bool enabled;
};

static inline
struct ea8061v_ams497ee01 *to_ea8061v_ams497ee01(struct drm_panel *panel)
{
	return container_of(panel, struct ea8061v_ams497ee01, panel);
}

static int ea8061v_ams497ee01_unlock(struct ea8061v_ams497ee01 *ctx, u8 lock)
{
	switch (lock) {
	case 0xf0:
		mipi_dsi_dcs_write_seq(ctx->dsi, 0xf0, 0x5a, 0x5a);
		break;
	case 0xf1:
		mipi_dsi_dcs_write_seq(ctx->dsi, 0xf1, 0x5a, 0x5a);
		break;
	case 0xfc:
		mipi_dsi_dcs_write_seq(ctx->dsi, 0xfc, 0x5a, 0x5a);
		break;
	default:
		return -EINVAL;
	}
	return 0;
}

static int ea8061v_ams497ee01_lock(struct ea8061v_ams497ee01 *ctx, u8 lock)
{
	switch (lock) {
	case 0xf0:
		mipi_dsi_dcs_write_seq(ctx->dsi, 0xf0, 0xa5, 0xa5);
		break;
	case 0xf1:
		mipi_dsi_dcs_write_seq(ctx->dsi, 0xf1, 0xa5, 0xa5);
		break;
	case 0xfc:
		mipi_dsi_dcs_write_seq(ctx->dsi, 0xfc, 0xa5, 0xa5);
		break;
	default:
		return -EINVAL;
	}
	return 0;
}

static int ea8061v_ams497ee01_apply_gamma(struct ea8061v_ams497ee01 *ctx)
{
	mipi_dsi_dcs_write_seq(ctx->dsi, MCS_GAMMA_UPDATE, 0x01);
	return 0;
}

static int ea8061v_ams497ee01_update_aid(struct ea8061v_ams497ee01 *ctx,
					 unsigned int brightness)
{
	int ret;
	int index = map_candela_to_aid[brightness];

	ret = mipi_dsi_dcs_write_buffer(ctx->dsi,
					seq_ea8061v_ams497ee01_aid[index],
					EA8061V_AMS497EE01_AID_CMD_CNT);
	if (ret < 0)
		return ret;
	return 0;
}

static int ea8061v_ams497ee01_update_elvss(struct ea8061v_ams497ee01 *ctx,
					   unsigned int brightness)
{
	int ret;
	int index = map_candela_to_elvss[brightness];

	ret = mipi_dsi_dcs_write_buffer(ctx->dsi,
					seq_ea8061v_ams497ee01_elvss[index],
					ELVSS_SEQUENCE_LEN);
	if (ret < 0)
		return ret;
	return 0;
}

static int ea8061v_ams497ee01_update_gamma(struct ea8061v_ams497ee01 *ctx,
					   unsigned int brightness)
{
	int ret;
	struct backlight_device *bl_dev = ctx->bl_dev;

	ret = mipi_dsi_dcs_write_buffer(ctx->dsi,
					seq_ea8061v_ams497ee01_lux[brightness],
					EA8061V_AMS497EE01_GAMMA_CMD_CNT);
	if (ret < 0)
		return ret;

	bl_dev->props.brightness = brightness;

	return 0;
}

static int ea8061v_ams497ee01_set_brightness(struct backlight_device *bl_dev)
{
	struct ea8061v_ams497ee01 *ctx = bl_get_data(bl_dev);
	unsigned int brightness = bl_dev->props.brightness;
	struct mipi_dsi_device *dsi = ctx->dsi;
	struct device *dev = &dsi->dev;
	int ret;

	ret = ea8061v_ams497ee01_unlock(ctx, 0xf0);
	if (ret < 0) {
		dev_err(dev, "Failed to unlock 0xf0: %d\n", ret);
		return ret;
	}
	ret = ea8061v_ams497ee01_unlock(ctx, 0xf1);
	if (ret < 0) {
		dev_err(dev, "Failed to unlock 0xf1: %d\n", ret);
		return ret;
	}

	/* aid/aor */
	ret = ea8061v_ams497ee01_update_aid(ctx, brightness);
	if (ret < 0) {
		dev_err(dev, "Failed to send aid sequence: %d\n", ret);
		return ret;
	}
	mipi_dsi_dcs_write_seq(dsi, MCS_ACL_OPR_CONTROL, 0x29);
	mipi_dsi_dcs_write_seq(dsi, MCS_ACL_CONTROL, 0x00);

	ret = ea8061v_ams497ee01_update_elvss(ctx, brightness);
	if (ret < 0) {
		dev_err(dev, "Failed to update elvss: %d\n", ret);
		return ret;
	}

	ret = ea8061v_ams497ee01_update_gamma(ctx, brightness);
	if (ret < 0) {
		dev_err(dev, "Failed to update gamma: %d\n", ret);
		return ret;
	}

	ret = ea8061v_ams497ee01_apply_gamma(ctx);
	if (ret < 0) {
		dev_err(dev, "Failed to apply gamma update: %d\n", ret);
		return ret;
	}

	ea8061v_ams497ee01_lock(ctx, 0xf1);
	if (ret < 0) {
		dev_err(dev, "Failed to lock 0xf1: %d\n", ret);
		return ret;
	}
	ea8061v_ams497ee01_lock(ctx, 0xf0);
	if (ret < 0) {
		dev_err(dev, "Failed to lock 0xf0: %d\n", ret);
		return ret;
	}

	return 0;
}

static const struct backlight_ops ea8061v_ams497ee01_bl_ops = {
	.update_status = ea8061v_ams497ee01_set_brightness,
};

static int ea8061v_ams497ee01_init(struct ea8061v_ams497ee01 *ctx)
{
	struct mipi_dsi_device *dsi = ctx->dsi;
	struct device *dev = &dsi->dev;
	int ret;

	unsigned int brightness = ctx->bl_dev->props.brightness;

	dsi->mode_flags |= MIPI_DSI_MODE_LPM;

	ret = ea8061v_ams497ee01_unlock(ctx, 0xf0);
	if (ret < 0) {
		dev_err(dev, "Failed to unlock 0xf0: %d\n", ret);
		return ret;
	}
	ret = ea8061v_ams497ee01_unlock(ctx, 0xf1);
	if (ret < 0) {
		dev_err(dev, "Failed to unlock 0xf1: %d\n", ret);
		return ret;
	}

	/* Common conditions */
	/* set ref temperature/voltage (0x19 = 25 degC, 0x10 = 7.6 V) */
	mipi_dsi_dcs_write_seq(dsi, MCS_TSET, 0x19, 0x10);
	/* ? */
	mipi_dsi_dcs_write_seq(dsi, 0xba, 0x57);

	ret = ea8061v_ams497ee01_unlock(ctx, 0xfc);
	if (ret < 0) {
		dev_err(dev, "Failed to unlock 0xfc: %d\n", ret);
		return ret;
	}

	/* skip 11 parameters in HBM sequence? */
	mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x0b);
	/* ? */
	mipi_dsi_dcs_write_seq(dsi, 0xd2, 0x00, 0x85);
	/* ? */
	mipi_dsi_dcs_write_seq(dsi, 0xcb, 0x70);

	ret = ea8061v_ams497ee01_lock(ctx, 0xfc);
	if (ret < 0) {
		dev_err(dev, "Failed to lock 0xfc: %d\n", ret);
		return ret;
	}
	/* Brightness control */
	ret = ea8061v_ams497ee01_update_gamma(ctx, brightness);
	if (ret < 0) {
		dev_err(dev, "Failed to update gamma: %d\n", ret);
		return ret;
	}
	ret = ea8061v_ams497ee01_update_aid(ctx, brightness);
	if (ret < 0) {
		dev_err(dev, "Failed to send aid sequence: %d\n", ret);
		return ret;
	}
	ret = ea8061v_ams497ee01_update_elvss(ctx, brightness);
	if (ret < 0) {
		dev_err(dev, "Failed to update elvss: %d\n", ret);
		return ret;
	}
	ret = ea8061v_ams497ee01_apply_gamma(ctx);
	if (ret < 0) {
		dev_err(dev, "Failed to apply gamma update: %d\n", ret);
		return ret;
	}

	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
	if (ret < 0) {
		dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
		return ret;
	}
	msleep(120);

	ret = ea8061v_ams497ee01_lock(ctx, 0xf1);
	if (ret < 0) {
		dev_err(dev, "Failed to lock 0xf1: %d\n", ret);
		return ret;
	}

	ret = mipi_dsi_dcs_set_display_on(dsi);
	if (ret < 0) {
		dev_err(dev, "Failed to set display on: %d\n", ret);
		return ret;
	}

	return 0;
}

static int ea8061v_ams497ee01_power_on(struct ea8061v_ams497ee01 *ctx)
{
	int ret;

	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
	if (ret < 0) {
		dev_err(&ctx->dsi->dev,
			"Failed to enable regulators: %d\n", ret);
		return ret;
	}

	/* send reset pulse */
	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
	msleep(20);
	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
	usleep_range(1000, 2000);
	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
	msleep(20);

	return 0;
}

static int ea8061v_ams497ee01_power_off(struct ea8061v_ams497ee01 *ctx)
{
	int ret;

	gpiod_set_value(ctx->reset_gpio, 1);

	ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
	if (ret < 0)
		return ret;

	return 0;
}

static int ea8061v_ams497ee01_enable(struct drm_panel *panel)
{
	struct ea8061v_ams497ee01 *ctx = to_ea8061v_ams497ee01(panel);
	struct mipi_dsi_device *dsi = ctx->dsi;
	int ret;

	if (ctx->prepared)
		return 0;

	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
	if (ret < 0) {
		dev_err(&dsi->dev, "Failed to exit sleep mode: %d\n", ret);
		return ret;
	};
	msleep(120);

	backlight_enable(ctx->bl_dev);

	ctx->enabled = true;
	return 0;
}

static int ea8061v_ams497ee01_disable(struct drm_panel *panel)
{
	struct ea8061v_ams497ee01 *ctx = to_ea8061v_ams497ee01(panel);
	struct mipi_dsi_device *dsi = ctx->dsi;
	int ret;

	if (!ctx->enabled)
		return 0;

	backlight_disable(ctx->bl_dev);

	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;

	ret = mipi_dsi_dcs_set_display_off(dsi);
	if (ret < 0) {
		dev_err(&dsi->dev, "Failed to set display off: %d\n", ret);
		return ret;
	}
	msleep(35);

	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
	if (ret < 0) {
		dev_err(&dsi->dev, "Failed to enter sleep mode: %d\n", ret);
		return ret;
	}
	msleep(100);

	ctx->enabled = false;
	return 0;
}

static int ea8061v_ams497ee01_prepare(struct drm_panel *panel)
{
	struct ea8061v_ams497ee01 *ctx = to_ea8061v_ams497ee01(panel);
	struct device *dev = &ctx->dsi->dev;
	int ret;

	if (ctx->prepared)
		return 0;

	ret = ea8061v_ams497ee01_power_on(ctx);
	if (ret < 0) {
		dev_err(dev, "Failed to power on panel: %d\n", ret);
		return ret;
	}

	ret = ea8061v_ams497ee01_init(ctx);
	if (ret < 0) {
		dev_err(dev, "Failed to initialize panel: %d\n", ret);
		ea8061v_ams497ee01_power_off(ctx);
		return ret;
	}

	ctx->prepared = true;
	return 0;
}

static int ea8061v_ams497ee01_unprepare(struct drm_panel *panel)
{
	struct ea8061v_ams497ee01 *ctx = to_ea8061v_ams497ee01(panel);
	struct device *dev = &ctx->dsi->dev;
	int ret;

	if (!ctx->prepared)
		return 0;

	ret = ea8061v_ams497ee01_power_off(ctx);
	if (ret < 0)
		dev_err(dev, "Failed to un-initialize panel: %d\n", ret);

	ctx->prepared = false;
	return 0;
}

static const struct drm_display_mode ea8061v_ams497ee01_mode = {
	.clock = (720 + 80 + 96 + 128) * (1280 + 14 + 2 + 8) * 60 / 1000,
	.hdisplay = 720,
	.hsync_start = 720 + 80,
	.hsync_end = 720 + 80 + 96,
	.htotal = 720 + 80 + 96 + 128,
	.vdisplay = 1280,
	.vsync_start = 1280 + 14,
	.vsync_end = 1280 + 14 + 2,
	.vtotal = 1280 + 14 + 2 + 8,
	.width_mm = 62,
	.height_mm = 110,
};

static int ea8061v_ams497ee01_get_modes(struct drm_panel *panel,
					struct drm_connector *connector)
{
	struct drm_display_mode *mode;

	mode = drm_mode_duplicate(connector->dev, &ea8061v_ams497ee01_mode);
	if (!mode)
		return -ENOMEM;

	drm_mode_set_name(mode);

	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
	connector->display_info.width_mm = mode->width_mm;
	connector->display_info.height_mm = mode->height_mm;
	drm_mode_probed_add(connector, mode);

	return 1;
}

static const struct drm_panel_funcs ea8061v_ams497ee01_panel_funcs = {
	.prepare = ea8061v_ams497ee01_prepare,
	.enable = ea8061v_ams497ee01_enable,
	.disable = ea8061v_ams497ee01_disable,
	.unprepare = ea8061v_ams497ee01_unprepare,
	.get_modes = ea8061v_ams497ee01_get_modes,
};

static int ea8061v_ams497ee01_probe(struct mipi_dsi_device *dsi)
{
	struct device *dev = &dsi->dev;
	struct ea8061v_ams497ee01 *ctx;
	int ret;

	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
	if (!ctx)
		return -ENOMEM;

	ctx->dsi = dsi;
	mipi_dsi_set_drvdata(dsi, ctx);

	ctx->supplies[0].supply = "vdd3";
	ctx->supplies[1].supply = "vci";
	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
				      ctx->supplies);
	if (ret < 0)
		return dev_err_probe(dev, ret, "Failed to get regulators\n");

	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
	if (IS_ERR(ctx->reset_gpio))
		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
				     "Failed to get reset-gpios\n");

	ctx->bl_dev = backlight_device_register("panel", dev, ctx,
						&ea8061v_ams497ee01_bl_ops,
						NULL);

	dsi->lanes = 4;
	dsi->format = MIPI_DSI_FMT_RGB888;
	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
			  MIPI_DSI_MODE_NO_EOT_PACKET;

	if (IS_ERR(ctx->bl_dev)) {
		dev_err(dev, "Failed to register backlight device\n");
		return PTR_ERR(ctx->bl_dev);
	}

	ctx->bl_dev->props.max_brightness = EA8061V_AMS497EE01_MAX_BRIGHTNESS;
	ctx->bl_dev->props.brightness = EA8061V_AMS497EE01_DEFAULT_BRIGHTNESS;
	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;

	drm_panel_init(&ctx->panel, dev, &ea8061v_ams497ee01_panel_funcs,
		       DRM_MODE_CONNECTOR_DSI);
	ctx->panel.prepare_prev_first = true;

	drm_panel_add(&ctx->panel);

	ret = mipi_dsi_attach(dsi);
	if (ret < 0) {
		dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
		drm_panel_remove(&ctx->panel);
		return ret;
	}

	return 0;
}

static void ea8061v_ams497ee01_remove(struct mipi_dsi_device *dsi)
{
	struct ea8061v_ams497ee01 *ctx = mipi_dsi_get_drvdata(dsi);
	int ret;

	ret = mipi_dsi_detach(dsi);
	if (ret < 0)
		dev_err(&dsi->dev,
			"Failed to detach from DSI host: %d\n", ret);

	drm_panel_remove(&ctx->panel);
}

static const struct of_device_id ea8061v_ams497ee01_of_match[] = {
	{ .compatible = "samsung,ea8061v-ams497ee01" },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ea8061v_ams497ee01_of_match);

static struct mipi_dsi_driver ea8061v_ams497ee01_driver = {
	.probe = ea8061v_ams497ee01_probe,
	.remove = ea8061v_ams497ee01_remove,
	.driver = {
		.name = "panel-ea8061v-ams497ee01",
		.of_match_table = ea8061v_ams497ee01_of_match,
	},
};
module_mipi_dsi_driver(ea8061v_ams497ee01_driver);

MODULE_AUTHOR("linux-mdss-dsi-panel-driver-generator <fix@me>"); // FIXME
MODULE_DESCRIPTION("DRM driver for ss_dsi_panel_EA8061V_AMS497EE01_HD");
MODULE_LICENSE("GPL");

stephan-gh avatar Aug 14 '23 10:08 stephan-gh

@stephan-gh thanks for review/cleanup! Still works fine from some quick testing at least.

Will test it for a day or two and go through modifications.

Edit: some new dmesg errors during power on:

[  707.039921] msm_dsi 1a98000.dsi: [drm:dsi_cmds2buf_tx [msm]] *ERROR* wait for video done timed out
[  707.116441] msm_dsi 1a98000.dsi: [drm:dsi_cmds2buf_tx [msm]] *ERROR* wait for video done timed out
[  707.192995] msm_dsi 1a98000.dsi: [drm:dsi_cmds2buf_tx [msm]] *ERROR* wait for video done timed out
[ above happens 10 times ]
[  707.196728] [drm:mdp5_irq_error_handler [msm]] *ERROR* errors: 04000000

and when turning off the screen it goes to 100 % brightness for a short while.

Grimler91 avatar Aug 14 '23 18:08 Grimler91

[ above happens 10 times ]

That would be the brightness setting I guess (that's exactly 10 commands). Is this only with the MIPI_DSI_MODE_LPM change or also with my other changes?

stephan-gh avatar Aug 19 '23 17:08 stephan-gh