diff mbox

[v8,2/3] drm/panel: Add support for S6E3HA2 panel driver on TM2 board

Message ID 1484116439-7275-3-git-send-email-hoegeun.kwon@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

Hoegeun Kwon Jan. 11, 2017, 6:33 a.m. UTC
This patch add support for MIPI-DSI based S6E3HA2 AMOLED panel
driver. This panel has 1440x2560 resolution in 5.7-inch physical
panel in the TM2 device.

Signed-off-by: Donghwa Lee <dh09.lee@samsung.com>
Signed-off-by: Hyungwon Hwang <human.hwang@samsung.com>
Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
Tested-by: Chanwoo Choi <cw00.choi@samsung.com>
Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
---
 drivers/gpu/drm/panel/Kconfig                 |   6 +
 drivers/gpu/drm/panel/Makefile                |   1 +
 drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c | 754 ++++++++++++++++++++++++++
 3 files changed, 761 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c

Comments

Hoegeun Kwon Jan. 24, 2017, 1:50 a.m. UTC | #1
Dear Thierry,

Could you please review this patch?

Best Regards,
Hoegeun Kwon

On 01/11/2017 03:33 PM, Hoegeun Kwon wrote:
> This patch add support for MIPI-DSI based S6E3HA2 AMOLED panel
> driver. This panel has 1440x2560 resolution in 5.7-inch physical
> panel in the TM2 device.
>
> Signed-off-by: Donghwa Lee <dh09.lee@samsung.com>
> Signed-off-by: Hyungwon Hwang <human.hwang@samsung.com>
> Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
> Tested-by: Chanwoo Choi <cw00.choi@samsung.com>
> Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
> ---
>   drivers/gpu/drm/panel/Kconfig                 |   6 +
>   drivers/gpu/drm/panel/Makefile                |   1 +
>   drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c | 754 ++++++++++++++++++++++++++
>   3 files changed, 761 insertions(+)
>   create mode 100644 drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
>
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 62aba97..d913c83 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -52,6 +52,12 @@ config DRM_PANEL_PANASONIC_VVX10F034N00
>   	  WUXGA (1920x1200) Novatek NT1397-based DSI panel as found in some
>   	  Xperia Z2 tablets
>   
> +config DRM_PANEL_SAMSUNG_S6E3HA2
> +	tristate "Samsung S6E3HA2 DSI video mode panel"
> +	depends on OF
> +	depends on DRM_MIPI_DSI
> +	select VIDEOMODE_HELPERS
> +
>   config DRM_PANEL_SAMSUNG_S6E8AA0
>   	tristate "Samsung S6E8AA0 DSI video mode panel"
>   	depends on OF
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index a5c7ec0..1d483b0 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -3,6 +3,7 @@ obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
>   obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
>   obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
>   obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
> +obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
>   obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>   obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>   obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
> diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
> new file mode 100644
> index 0000000..0b9c6f4
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
> @@ -0,0 +1,754 @@
> +/*
> + * MIPI-DSI based s6e3ha2 AMOLED 5.7 inch panel driver.
> + *
> + * Copyright (c) 2016 Samsung Electronics Co., Ltd.
> + * Donghwa Lee <dh09.lee@samsung.com>
> + * Hyungwon Hwang <human.hwang@samsung.com>
> + * Hoegeun Kwon <hoegeun.kwon@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.h>
> +#include <linux/backlight.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/regulator/consumer.h>
> +
> +#define S6E3HA2_MIN_BRIGHTNESS		0
> +#define S6E3HA2_MAX_BRIGHTNESS		100
> +#define S6E3HA2_DEFAULT_BRIGHTNESS	80
> +
> +#define S6E3HA2_NUM_GAMMA_STEPS		46
> +#define S6E3HA2_GAMMA_CMD_CNT		35
> +#define S6E3HA2_VINT_STATUS_MAX		10
> +
> +static const u8 gamma_tbl[S6E3HA2_NUM_GAMMA_STEPS][S6E3HA2_GAMMA_CMD_CNT] = {
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x82, 0x83,
> +	  0x85, 0x88, 0x8b, 0x8b, 0x84, 0x88, 0x82, 0x82, 0x89, 0x86, 0x8c,
> +	  0x94, 0x84, 0xb1, 0xaf, 0x8e, 0xcf, 0xad, 0xc9, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x84, 0x84,
> +	  0x85, 0x87, 0x8b, 0x8a, 0x84, 0x88, 0x82, 0x82, 0x89, 0x86, 0x8a,
> +	  0x93, 0x84, 0xb0, 0xae, 0x8e, 0xc9, 0xa8, 0xc5, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x8a, 0x8a, 0x84, 0x88, 0x81, 0x84, 0x8a, 0x88, 0x8a,
> +	  0x91, 0x84, 0xb1, 0xae, 0x8b, 0xd5, 0xb2, 0xcc, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x8a, 0x8a, 0x84, 0x87, 0x81, 0x84, 0x8a, 0x87, 0x8a,
> +	  0x91, 0x85, 0xae, 0xac, 0x8a, 0xc3, 0xa3, 0xc0, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x85, 0x85,
> +	  0x86, 0x85, 0x88, 0x89, 0x84, 0x89, 0x82, 0x84, 0x87, 0x85, 0x8b,
> +	  0x91, 0x88, 0xad, 0xab, 0x8a, 0xb7, 0x9b, 0xb6, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x89, 0x8a, 0x84, 0x89, 0x83, 0x83, 0x86, 0x84, 0x8b,
> +	  0x90, 0x84, 0xb0, 0xae, 0x8b, 0xce, 0xad, 0xc8, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> +	  0x85, 0x87, 0x89, 0x8a, 0x83, 0x87, 0x82, 0x85, 0x88, 0x87, 0x89,
> +	  0x8f, 0x84, 0xac, 0xaa, 0x89, 0xb1, 0x98, 0xaf, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x88, 0x89, 0x84, 0x88, 0x83, 0x82, 0x85, 0x84, 0x8c,
> +	  0x91, 0x86, 0xac, 0xaa, 0x89, 0xc2, 0xa5, 0xbd, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x85, 0x87, 0x89, 0x8a, 0x83, 0x87, 0x82, 0x85, 0x88, 0x87, 0x88,
> +	  0x8b, 0x82, 0xad, 0xaa, 0x8a, 0xc2, 0xa5, 0xbd, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x87, 0x89, 0x84, 0x88, 0x83, 0x82, 0x85, 0x84, 0x8a,
> +	  0x8e, 0x84, 0xae, 0xac, 0x89, 0xda, 0xb7, 0xd0, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x85, 0x86, 0x87, 0x89, 0x84, 0x88, 0x83, 0x80, 0x83, 0x82, 0x8b,
> +	  0x8e, 0x85, 0xac, 0xaa, 0x89, 0xc8, 0xaa, 0xc1, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x85, 0x86, 0x87, 0x89, 0x81, 0x85, 0x81, 0x84, 0x86, 0x84, 0x8c,
> +	  0x8c, 0x84, 0xa9, 0xa8, 0x87, 0xa3, 0x92, 0xa1, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x85, 0x86, 0x87, 0x89, 0x84, 0x86, 0x83, 0x80, 0x83, 0x81, 0x8c,
> +	  0x8d, 0x84, 0xaa, 0xaa, 0x89, 0xce, 0xaf, 0xc5, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x85, 0x86, 0x87, 0x89, 0x81, 0x83, 0x80, 0x83, 0x85, 0x85, 0x8c,
> +	  0x8c, 0x84, 0xa8, 0xa8, 0x88, 0xb5, 0x9f, 0xb0, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x86, 0x86, 0x87, 0x88, 0x81, 0x83, 0x80, 0x83, 0x85, 0x85, 0x8c,
> +	  0x8b, 0x84, 0xab, 0xa8, 0x86, 0xd4, 0xb4, 0xc9, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x86, 0x86, 0x87, 0x88, 0x81, 0x83, 0x80, 0x84, 0x84, 0x85, 0x8b,
> +	  0x8a, 0x83, 0xa6, 0xa5, 0x84, 0xbb, 0xa4, 0xb3, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x86, 0x85, 0x86, 0x86, 0x82, 0x85, 0x81, 0x82, 0x83, 0x84, 0x8e,
> +	  0x8b, 0x83, 0xa4, 0xa3, 0x8a, 0xa1, 0x93, 0x9d, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x82, 0x82, 0x84, 0x8e,
> +	  0x8b, 0x83, 0xa4, 0xa2, 0x86, 0xc1, 0xa9, 0xb7, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x82, 0x82, 0x84, 0x8d,
> +	  0x89, 0x82, 0xa2, 0xa1, 0x84, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x83, 0x83, 0x85, 0x8c,
> +	  0x87, 0x7f, 0xa2, 0x9d, 0x88, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xbb, 0x00, 0xc5, 0x00, 0xb4, 0x87, 0x86, 0x86, 0x84, 0x83,
> +	  0x86, 0x87, 0x87, 0x87, 0x80, 0x82, 0x7f, 0x86, 0x86, 0x88, 0x8a,
> +	  0x84, 0x7e, 0x9d, 0x9c, 0x82, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xbd, 0x00, 0xc7, 0x00, 0xb7, 0x87, 0x85, 0x85, 0x84, 0x83,
> +	  0x86, 0x86, 0x86, 0x88, 0x81, 0x83, 0x80, 0x83, 0x84, 0x85, 0x8a,
> +	  0x85, 0x7e, 0x9c, 0x9b, 0x85, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xc0, 0x00, 0xca, 0x00, 0xbb, 0x87, 0x86, 0x85, 0x83, 0x83,
> +	  0x85, 0x86, 0x86, 0x88, 0x81, 0x83, 0x80, 0x84, 0x85, 0x86, 0x89,
> +	  0x83, 0x7d, 0x9c, 0x99, 0x87, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xc4, 0x00, 0xcd, 0x00, 0xbe, 0x87, 0x86, 0x85, 0x83, 0x83,
> +	  0x86, 0x85, 0x85, 0x87, 0x81, 0x82, 0x80, 0x82, 0x82, 0x83, 0x8a,
> +	  0x85, 0x7f, 0x9f, 0x9b, 0x86, 0xb4, 0xa1, 0xac, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xc7, 0x00, 0xd0, 0x00, 0xc2, 0x87, 0x85, 0x85, 0x83, 0x82,
> +	  0x85, 0x85, 0x85, 0x86, 0x82, 0x83, 0x80, 0x82, 0x82, 0x84, 0x87,
> +	  0x86, 0x80, 0x9e, 0x9a, 0x87, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xca, 0x00, 0xd2, 0x00, 0xc5, 0x87, 0x85, 0x84, 0x82, 0x82,
> +	  0x84, 0x85, 0x85, 0x86, 0x81, 0x82, 0x7f, 0x82, 0x82, 0x84, 0x88,
> +	  0x86, 0x81, 0x9d, 0x98, 0x86, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xce, 0x00, 0xd6, 0x00, 0xca, 0x86, 0x85, 0x84, 0x83, 0x83,
> +	  0x85, 0x84, 0x84, 0x85, 0x81, 0x82, 0x80, 0x81, 0x81, 0x82, 0x89,
> +	  0x86, 0x81, 0x9c, 0x97, 0x86, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xd1, 0x00, 0xd9, 0x00, 0xce, 0x86, 0x84, 0x83, 0x83, 0x82,
> +	  0x85, 0x85, 0x85, 0x86, 0x81, 0x83, 0x81, 0x82, 0x82, 0x83, 0x86,
> +	  0x83, 0x7f, 0x99, 0x95, 0x86, 0xbb, 0xa4, 0xb3, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xd4, 0x00, 0xdb, 0x00, 0xd1, 0x86, 0x85, 0x83, 0x83, 0x82,
> +	  0x85, 0x84, 0x84, 0x85, 0x80, 0x83, 0x82, 0x80, 0x80, 0x81, 0x87,
> +	  0x84, 0x81, 0x98, 0x93, 0x85, 0xae, 0x9c, 0xa8, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xd8, 0x00, 0xde, 0x00, 0xd6, 0x86, 0x84, 0x83, 0x81, 0x81,
> +	  0x83, 0x85, 0x85, 0x85, 0x82, 0x83, 0x81, 0x81, 0x81, 0x83, 0x86,
> +	  0x84, 0x80, 0x98, 0x91, 0x85, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xdc, 0x00, 0xe2, 0x00, 0xda, 0x85, 0x84, 0x83, 0x82, 0x82,
> +	  0x84, 0x84, 0x84, 0x85, 0x81, 0x82, 0x82, 0x80, 0x80, 0x81, 0x83,
> +	  0x82, 0x7f, 0x99, 0x93, 0x86, 0x94, 0x8b, 0x92, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xdf, 0x00, 0xe5, 0x00, 0xde, 0x85, 0x84, 0x82, 0x82, 0x82,
> +	  0x84, 0x83, 0x83, 0x84, 0x81, 0x81, 0x80, 0x83, 0x82, 0x84, 0x82,
> +	  0x81, 0x7f, 0x99, 0x92, 0x86, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> +	  0x82, 0x83, 0x83, 0x84, 0x80, 0x81, 0x80, 0x83, 0x83, 0x84, 0x80,
> +	  0x81, 0x7c, 0x99, 0x92, 0x87, 0xa1, 0x93, 0x9d, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x85, 0x84, 0x83, 0x81, 0x81,
> +	  0x82, 0x82, 0x82, 0x83, 0x80, 0x81, 0x80, 0x81, 0x80, 0x82, 0x83,
> +	  0x82, 0x80, 0x91, 0x8d, 0x83, 0x9a, 0x90, 0x96, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> +	  0x82, 0x83, 0x83, 0x84, 0x80, 0x81, 0x80, 0x81, 0x80, 0x82, 0x83,
> +	  0x81, 0x7f, 0x91, 0x8c, 0x82, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> +	  0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x82, 0x82,
> +	  0x82, 0x7f, 0x94, 0x89, 0x84, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> +	  0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x82, 0x83,
> +	  0x82, 0x7f, 0x91, 0x85, 0x81, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> +	  0x82, 0x83, 0x83, 0x83, 0x80, 0x80, 0x7f, 0x83, 0x82, 0x84, 0x83,
> +	  0x82, 0x7f, 0x90, 0x84, 0x81, 0x9a, 0x90, 0x96, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x80, 0x80,
> +	  0x82, 0x83, 0x83, 0x83, 0x80, 0x80, 0x7f, 0x80, 0x80, 0x81, 0x81,
> +	  0x82, 0x83, 0x7e, 0x80, 0x7c, 0xa4, 0x97, 0x9f, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe9, 0x00, 0xec, 0x00, 0xe8, 0x84, 0x83, 0x82, 0x81, 0x81,
> +	  0x82, 0x82, 0x82, 0x83, 0x7f, 0x7f, 0x7f, 0x81, 0x80, 0x82, 0x83,
> +	  0x83, 0x84, 0x79, 0x7c, 0x79, 0xb1, 0xa0, 0xaa, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xed, 0x00, 0xf0, 0x00, 0xec, 0x83, 0x83, 0x82, 0x80, 0x80,
> +	  0x81, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7e, 0x81, 0x81, 0x82, 0x80,
> +	  0x81, 0x81, 0x84, 0x84, 0x83, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xf1, 0x00, 0xf4, 0x00, 0xf1, 0x83, 0x82, 0x82, 0x80, 0x80,
> +	  0x81, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x7d,
> +	  0x7e, 0x7f, 0x84, 0x84, 0x83, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xf6, 0x00, 0xf7, 0x00, 0xf5, 0x82, 0x82, 0x81, 0x80, 0x80,
> +	  0x80, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x82,
> +	  0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xfa, 0x00, 0xfb, 0x00, 0xfa, 0x81, 0x81, 0x81, 0x80, 0x80,
> +	  0x80, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
> +	  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 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,
> +	  0x00, 0x00 },
> +	{ 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,
> +	  0x00, 0x00 }
> +};
> +
> +unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = {
> +	0x18, 0x19, 0x1a, 0x1b, 0x1c,
> +	0x1d, 0x1e, 0x1f, 0x20, 0x21
> +};
> +
> +struct s6e3ha2 {
> +	struct device *dev;
> +	struct drm_panel panel;
> +	struct backlight_device *bl_dev;
> +
> +	struct regulator_bulk_data supplies[2];
> +	struct gpio_desc *reset_gpio;
> +	struct gpio_desc *enable_gpio;
> +
> +	/* This field is tested by functions directly accessing DSI bus before
> +	 * transfer, transfer is skipped if it is set. In case of transfer
> +	 * failure or unexpected response the field is set to error value.
> +	 * Such construct allows to eliminate many checks in higher level
> +	 * functions.
> +	 */
> +	int error;
> +};
> +
> +static int  s6e3ha2_clear_error(struct s6e3ha2 *ctx)
> +{
> +	int ret = ctx->error;
> +
> +	ctx->error = 0;
> +	return ret;
> +}
> +
> +static void s6e3ha2_dcs_write(struct s6e3ha2 *ctx, const void *data, size_t len)
> +{
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> +	ssize_t ret;
> +
> +	if (ctx->error < 0)
> +		return;
> +
> +	ret = mipi_dsi_dcs_write_buffer(dsi, data, len);
> +	if (ret < 0) {
> +		dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n",
> +						ret, (int)len, data);
> +		ctx->error = ret;
> +	}
> +}
> +
> +#define s6e3ha2_dcs_write_seq_static(ctx, seq...) do {	\
> +	static const u8 d[] = { seq };			\
> +	s6e3ha2_dcs_write(ctx, d, ARRAY_SIZE(d));	\
> +} while (0)
> +
> +static void s6e3ha2_test_key_on_f0(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xf0, 0x5a, 0x5a);
> +}
> +
> +static void s6e3ha2_test_key_off_f0(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xf0, 0xa5, 0xa5);
> +}
> +
> +static void s6e3ha2_test_key_on_fc(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xfc, 0x5a, 0x5a);
> +}
> +
> +static void s6e3ha2_test_key_off_fc(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xfc, 0xa5, 0xa5);
> +}
> +
> +static void s6e3ha2_single_dsi_set(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xf2, 0x67);
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xf9, 0x09);
> +}
> +
> +static void s6e3ha2_freq_calibration(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xfd, 0x1c);
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20, 0x39);
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0xa0);
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20);
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x12, 0x62,
> +		0x40, 0x80, 0xc0, 0x28, 0x28, 0x28, 0x28, 0x39, 0xc5);
> +}
> +
> +static void s6e3ha2_aor_control(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xb2, 0x03, 0x10);
> +}
> +
> +static void s6e3ha2_caps_elvss_set(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xb6, 0x9c, 0x0a);
> +}
> +
> +static void s6e3ha2_acl_off(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0x55, 0x00);
> +}
> +
> +static void s6e3ha2_acl_off_opr(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xb5, 0x40);
> +}
> +
> +static void s6e3ha2_test_global(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xb0, 0x07);
> +}
> +
> +static void s6e3ha2_test(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xb8, 0x19);
> +}
> +
> +static void s6e3ha2_touch_hsync_on1(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx,
> +			0xbd, 0x33, 0x11, 0x02, 0x16, 0x02, 0x16);
> +}
> +
> +static void s6e3ha2_pentile_control(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xc0, 0x00, 0x00, 0xd8, 0xd8);
> +}
> +
> +static void s6e3ha2_poc_global(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xb0, 0x20);
> +}
> +
> +static void s6e3ha2_poc_setting(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x08);
> +}
> +
> +static void s6e3ha2_pcd_set_off(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xcc, 0x40, 0x51);
> +}
> +
> +static void s6e3ha2_err_fg_set(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xed, 0x44);
> +}
> +
> +static void s6e3ha2_hbm_off(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0x53, 0x00);
> +}
> +
> +static void s6e3ha2_te_start_setting(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xb9, 0x10, 0x09, 0xff, 0x00, 0x09);
> +}
> +
> +static void s6e3ha2_gamma_update(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xf7, 0x03);
> +	ndelay(100); /* need for 100ns delay */
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xf7, 0x00);
> +}
> +
> +static int s6e3ha2_get_brightness(struct backlight_device *bl_dev)
> +{
> +	return bl_dev->props.brightness;
> +}
> +
> +static void s6e3ha2_set_vint(struct s6e3ha2 *ctx)
> +{
> +	struct backlight_device *bl_dev = ctx->bl_dev;
> +	unsigned int brightness = bl_dev->props.brightness;
> +	unsigned char data[] = { 0xf4, 0x8b,
> +			vint_table[brightness * (S6E3HA2_VINT_STATUS_MAX - 1) /
> +			S6E3HA2_MAX_BRIGHTNESS] };
> +
> +	s6e3ha2_dcs_write(ctx, data, ARRAY_SIZE(data));
> +}
> +
> +static unsigned int s6e3ha2_get_brightness_index(unsigned int brightness)
> +{
> +	return (brightness * (S6E3HA2_NUM_GAMMA_STEPS - 1)) /
> +		S6E3HA2_MAX_BRIGHTNESS;
> +}
> +
> +static int s6e3ha2_update_gamma(struct s6e3ha2 *ctx, unsigned int brightness)
> +{
> +	struct backlight_device *bl_dev = ctx->bl_dev;
> +	unsigned int index = s6e3ha2_get_brightness_index(brightness);
> +	u8 data[S6E3HA2_GAMMA_CMD_CNT + 1] = { 0xca, };
> +
> +	memcpy(data + 1, gamma_tbl + index, S6E3HA2_GAMMA_CMD_CNT);
> +	s6e3ha2_dcs_write(ctx, data, ARRAY_SIZE(data));
> +
> +	s6e3ha2_gamma_update(ctx);
> +	bl_dev->props.brightness = brightness;
> +
> +	return 0;
> +}
> +
> +static int s6e3ha2_set_brightness(struct backlight_device *bl_dev)
> +{
> +	struct s6e3ha2 *ctx = bl_get_data(bl_dev);
> +	unsigned int brightness = bl_dev->props.brightness;
> +
> +	if (brightness < S6E3HA2_MIN_BRIGHTNESS ||
> +		brightness > bl_dev->props.max_brightness) {
> +		dev_err(ctx->dev, "Invalid brightness: %u\n", brightness);
> +		return -EINVAL;
> +	}
> +
> +	if (bl_dev->props.power > FB_BLANK_NORMAL)
> +		return -EPERM;
> +
> +	s6e3ha2_test_key_on_f0(ctx);
> +	s6e3ha2_update_gamma(ctx, brightness);
> +	s6e3ha2_aor_control(ctx);
> +	s6e3ha2_set_vint(ctx);
> +	s6e3ha2_test_key_off_f0(ctx);
> +
> +	return ctx->error;
> +}
> +
> +static const struct backlight_ops s6e3ha2_bl_ops = {
> +	.get_brightness = s6e3ha2_get_brightness,
> +	.update_status = s6e3ha2_set_brightness,
> +};
> +
> +static int s6e3ha2_panel_init(struct s6e3ha2 *ctx)
> +{
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> +	int ret;
> +
> +	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +	if (ret < 0)
> +		return ret;
> +	usleep_range(5000, 6000);
> +
> +	s6e3ha2_test_key_on_f0(ctx);
> +	s6e3ha2_single_dsi_set(ctx);
> +	s6e3ha2_test_key_on_fc(ctx);
> +	s6e3ha2_freq_calibration(ctx);
> +	s6e3ha2_test_key_off_fc(ctx);
> +	s6e3ha2_test_key_off_f0(ctx);
> +
> +	return 0;
> +}
> +
> +static int s6e3ha2_power_off(struct s6e3ha2 *ctx)
> +{
> +	return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> +}
> +
> +static int s6e3ha2_disable(struct drm_panel *panel)
> +{
> +	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> +	int ret;
> +
> +	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_set_display_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(40);
> +	ctx->bl_dev->props.power = FB_BLANK_NORMAL;
> +
> +	return 0;
> +}
> +
> +static int s6e3ha2_unprepare(struct drm_panel *panel)
> +{
> +	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
> +	int ret;
> +
> +	ret = s6e3ha2_clear_error(ctx);
> +	if (!ret)
> +		ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
> +
> +	return s6e3ha2_power_off(ctx);
> +}
> +
> +static int s6e3ha2_power_on(struct s6e3ha2 *ctx)
> +{
> +	int ret;
> +
> +	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(120);
> +
> +	gpiod_set_value(ctx->enable_gpio, 0);
> +	usleep_range(5000, 6000);
> +	gpiod_set_value(ctx->enable_gpio, 1);
> +
> +	gpiod_set_value(ctx->reset_gpio, 1);
> +	usleep_range(5000, 6000);
> +	gpiod_set_value(ctx->reset_gpio, 0);
> +	usleep_range(5000, 6000);
> +
> +	return 0;
> +}
> +static int s6e3ha2_prepare(struct drm_panel *panel)
> +{
> +	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
> +	int ret;
> +
> +	ret = s6e3ha2_power_on(ctx);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = s6e3ha2_panel_init(ctx);
> +	if (ret < 0)
> +		goto err;
> +
> +	ret = s6e3ha2_clear_error(ctx);
> +	if (ret < 0)
> +		goto err;
> +
> +	ctx->bl_dev->props.power = FB_BLANK_NORMAL;
> +
> +	return 0;
> +
> +err:
> +	s6e3ha2_power_off(ctx);
> +	return ret;
> +}
> +
> +static int s6e3ha2_enable(struct drm_panel *panel)
> +{
> +	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> +	int ret;
> +
> +	/* common setting */
> +	ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> +	if (ret < 0)
> +		return ret;
> +
> +	s6e3ha2_test_key_on_f0(ctx);
> +	s6e3ha2_test_key_on_fc(ctx);
> +	s6e3ha2_touch_hsync_on1(ctx);
> +	s6e3ha2_pentile_control(ctx);
> +	s6e3ha2_poc_global(ctx);
> +	s6e3ha2_poc_setting(ctx);
> +	s6e3ha2_test_key_off_fc(ctx);
> +
> +	/* pcd setting off for TB */
> +	s6e3ha2_pcd_set_off(ctx);
> +	s6e3ha2_err_fg_set(ctx);
> +	s6e3ha2_te_start_setting(ctx);
> +
> +	/* brightness setting */
> +	s6e3ha2_set_brightness(ctx->bl_dev);
> +	s6e3ha2_aor_control(ctx);
> +	s6e3ha2_caps_elvss_set(ctx);
> +	s6e3ha2_gamma_update(ctx);
> +	s6e3ha2_acl_off(ctx);
> +	s6e3ha2_acl_off_opr(ctx);
> +	s6e3ha2_hbm_off(ctx);
> +
> +	/* elvss temp compensation */
> +	s6e3ha2_test_global(ctx);
> +	s6e3ha2_test(ctx);
> +	s6e3ha2_test_key_off_f0(ctx);
> +
> +	if (ctx->error != 0)
> +		return ctx->error;
> +
> +	ret = mipi_dsi_dcs_set_display_on(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
> +
> +	return 0;
> +}
> +
> +static const struct drm_display_mode default_mode = {
> +	.clock = 222372,
> +	.hdisplay = 1440,
> +	.hsync_start = 1440 + 1,
> +	.hsync_end = 1440 + 1 + 1,
> +	.htotal = 1440 + 1 + 1 + 1,
> +	.vdisplay = 2560,
> +	.vsync_start = 2560 + 1,
> +	.vsync_end = 2560 + 1 + 1,
> +	.vtotal = 2560 + 1 + 1 + 15,
> +	.vrefresh = 60,
> +	.flags = 0,
> +};
> +
> +static int s6e3ha2_get_modes(struct drm_panel *panel)
> +{
> +	struct drm_connector *connector = panel->connector;
> +	struct drm_display_mode *mode;
> +
> +	mode = drm_mode_duplicate(panel->drm, &default_mode);
> +	if (!mode) {
> +		DRM_ERROR("failed to add mode %ux%ux@%u\n",
> +				default_mode.hdisplay, default_mode.vdisplay,
> +				default_mode.vrefresh);
> +		return -ENOMEM;
> +	}
> +
> +	drm_mode_set_name(mode);
> +
> +	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
> +	drm_mode_probed_add(connector, mode);
> +
> +	connector->display_info.width_mm = 71;
> +	connector->display_info.height_mm = 125;
> +
> +	return 1;
> +}
> +
> +static const struct drm_panel_funcs s6e3ha2_drm_funcs = {
> +	.disable = s6e3ha2_disable,
> +	.unprepare = s6e3ha2_unprepare,
> +	.prepare = s6e3ha2_prepare,
> +	.enable = s6e3ha2_enable,
> +	.get_modes = s6e3ha2_get_modes,
> +};
> +
> +static int s6e3ha2_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct device *dev = &dsi->dev;
> +	struct s6e3ha2 *ctx;
> +	int ret;
> +
> +	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx)
> +		return -ENOMEM;
> +
> +	mipi_dsi_set_drvdata(dsi, ctx);
> +
> +	ctx->dev = dev;
> +
> +	dsi->lanes = 4;
> +	dsi->format = MIPI_DSI_FMT_RGB888;
> +	dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
> +
> +	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) {
> +		dev_err(dev, "failed to get regulators: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(ctx->reset_gpio)) {
> +		dev_err(dev, "cannot get reset-gpios %ld\n",
> +			PTR_ERR(ctx->reset_gpio));
> +		return PTR_ERR(ctx->reset_gpio);
> +	}
> +
> +	ctx->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
> +	if (IS_ERR(ctx->enable_gpio)) {
> +		dev_err(dev, "cannot get enable-gpios %ld\n",
> +			PTR_ERR(ctx->enable_gpio));
> +		return PTR_ERR(ctx->enable_gpio);
> +	}
> +
> +	ctx->bl_dev = backlight_device_register("s6e3ha2", dev, ctx,
> +						&s6e3ha2_bl_ops, NULL);
> +	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 = S6E3HA2_MAX_BRIGHTNESS;
> +	ctx->bl_dev->props.brightness = S6E3HA2_DEFAULT_BRIGHTNESS;
> +	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
> +
> +	drm_panel_init(&ctx->panel);
> +	ctx->panel.dev = dev;
> +	ctx->panel.funcs = &s6e3ha2_drm_funcs;
> +
> +	ret = drm_panel_add(&ctx->panel);
> +	if (ret < 0)
> +		goto unregister_backlight;
> +
> +	ret = mipi_dsi_attach(dsi);
> +	if (ret < 0)
> +		goto remove_panel;
> +
> +	return ret;
> +
> +remove_panel:
> +	drm_panel_remove(&ctx->panel);
> +
> +unregister_backlight:
> +	backlight_device_unregister(ctx->bl_dev);
> +
> +	return ret;
> +}
> +
> +static int s6e3ha2_remove(struct mipi_dsi_device *dsi)
> +{
> +	struct s6e3ha2 *ctx = mipi_dsi_get_drvdata(dsi);
> +
> +	mipi_dsi_detach(dsi);
> +	drm_panel_remove(&ctx->panel);
> +	backlight_device_unregister(ctx->bl_dev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id s6e3ha2_of_match[] = {
> +	{ .compatible = "samsung,s6e3ha2" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, s6e3ha2_of_match);
> +
> +static struct mipi_dsi_driver s6e3ha2_driver = {
> +	.probe = s6e3ha2_probe,
> +	.remove = s6e3ha2_remove,
> +	.driver = {
> +		.name = "panel-samsung-s6e3ha2",
> +		.of_match_table = s6e3ha2_of_match,
> +	},
> +};
> +module_mipi_dsi_driver(s6e3ha2_driver);
> +
> +MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
> +MODULE_AUTHOR("Hyungwon Hwang <human.hwang@samsung.com>");
> +MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
> +MODULE_DESCRIPTION("MIPI-DSI based s6e3ha2 AMOLED Panel Driver");
> +MODULE_LICENSE("GPL v2");
Inki Dae Jan. 31, 2017, 12:01 a.m. UTC | #2
2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
> Dear Thierry,
> 
> Could you please review this patch?

Thierry, I think this patch has been reviewed enough but no comment from you. Seems you are busy. I will pick up this.

Thanks.

> 
> Best Regards,
> Hoegeun Kwon
> 
> On 01/11/2017 03:33 PM, Hoegeun Kwon wrote:
>> This patch add support for MIPI-DSI based S6E3HA2 AMOLED panel
>> driver. This panel has 1440x2560 resolution in 5.7-inch physical
>> panel in the TM2 device.
>>
>> Signed-off-by: Donghwa Lee <dh09.lee@samsung.com>
>> Signed-off-by: Hyungwon Hwang <human.hwang@samsung.com>
>> Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
>> Tested-by: Chanwoo Choi <cw00.choi@samsung.com>
>> Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
>> ---
>>   drivers/gpu/drm/panel/Kconfig                 |   6 +
>>   drivers/gpu/drm/panel/Makefile                |   1 +
>>   drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c | 754 ++++++++++++++++++++++++++
>>   3 files changed, 761 insertions(+)
>>   create mode 100644 drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
>>
>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>> index 62aba97..d913c83 100644
>> --- a/drivers/gpu/drm/panel/Kconfig
>> +++ b/drivers/gpu/drm/panel/Kconfig
>> @@ -52,6 +52,12 @@ config DRM_PANEL_PANASONIC_VVX10F034N00
>>         WUXGA (1920x1200) Novatek NT1397-based DSI panel as found in some
>>         Xperia Z2 tablets
>>   +config DRM_PANEL_SAMSUNG_S6E3HA2
>> +    tristate "Samsung S6E3HA2 DSI video mode panel"
>> +    depends on OF
>> +    depends on DRM_MIPI_DSI
>> +    select VIDEOMODE_HELPERS
>> +
>>   config DRM_PANEL_SAMSUNG_S6E8AA0
>>       tristate "Samsung S6E8AA0 DSI video mode panel"
>>       depends on OF
>> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
>> index a5c7ec0..1d483b0 100644
>> --- a/drivers/gpu/drm/panel/Makefile
>> +++ b/drivers/gpu/drm/panel/Makefile
>> @@ -3,6 +3,7 @@ obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
>>   obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
>>   obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
>>   obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
>> +obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
>>   obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>>   obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>>   obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
>> diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
>> new file mode 100644
>> index 0000000..0b9c6f4
>> --- /dev/null
>> +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
>> @@ -0,0 +1,754 @@
>> +/*
>> + * MIPI-DSI based s6e3ha2 AMOLED 5.7 inch panel driver.
>> + *
>> + * Copyright (c) 2016 Samsung Electronics Co., Ltd.
>> + * Donghwa Lee <dh09.lee@samsung.com>
>> + * Hyungwon Hwang <human.hwang@samsung.com>
>> + * Hoegeun Kwon <hoegeun.kwon@samsung.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_mipi_dsi.h>
>> +#include <drm/drm_panel.h>
>> +#include <linux/backlight.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/regulator/consumer.h>
>> +
>> +#define S6E3HA2_MIN_BRIGHTNESS        0
>> +#define S6E3HA2_MAX_BRIGHTNESS        100
>> +#define S6E3HA2_DEFAULT_BRIGHTNESS    80
>> +
>> +#define S6E3HA2_NUM_GAMMA_STEPS        46
>> +#define S6E3HA2_GAMMA_CMD_CNT        35
>> +#define S6E3HA2_VINT_STATUS_MAX        10
>> +
>> +static const u8 gamma_tbl[S6E3HA2_NUM_GAMMA_STEPS][S6E3HA2_GAMMA_CMD_CNT] = {
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x82, 0x83,
>> +      0x85, 0x88, 0x8b, 0x8b, 0x84, 0x88, 0x82, 0x82, 0x89, 0x86, 0x8c,
>> +      0x94, 0x84, 0xb1, 0xaf, 0x8e, 0xcf, 0xad, 0xc9, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x84, 0x84,
>> +      0x85, 0x87, 0x8b, 0x8a, 0x84, 0x88, 0x82, 0x82, 0x89, 0x86, 0x8a,
>> +      0x93, 0x84, 0xb0, 0xae, 0x8e, 0xc9, 0xa8, 0xc5, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
>> +      0x85, 0x86, 0x8a, 0x8a, 0x84, 0x88, 0x81, 0x84, 0x8a, 0x88, 0x8a,
>> +      0x91, 0x84, 0xb1, 0xae, 0x8b, 0xd5, 0xb2, 0xcc, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
>> +      0x85, 0x86, 0x8a, 0x8a, 0x84, 0x87, 0x81, 0x84, 0x8a, 0x87, 0x8a,
>> +      0x91, 0x85, 0xae, 0xac, 0x8a, 0xc3, 0xa3, 0xc0, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x85, 0x85,
>> +      0x86, 0x85, 0x88, 0x89, 0x84, 0x89, 0x82, 0x84, 0x87, 0x85, 0x8b,
>> +      0x91, 0x88, 0xad, 0xab, 0x8a, 0xb7, 0x9b, 0xb6, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
>> +      0x85, 0x86, 0x89, 0x8a, 0x84, 0x89, 0x83, 0x83, 0x86, 0x84, 0x8b,
>> +      0x90, 0x84, 0xb0, 0xae, 0x8b, 0xce, 0xad, 0xc8, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
>> +      0x85, 0x87, 0x89, 0x8a, 0x83, 0x87, 0x82, 0x85, 0x88, 0x87, 0x89,
>> +      0x8f, 0x84, 0xac, 0xaa, 0x89, 0xb1, 0x98, 0xaf, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
>> +      0x85, 0x86, 0x88, 0x89, 0x84, 0x88, 0x83, 0x82, 0x85, 0x84, 0x8c,
>> +      0x91, 0x86, 0xac, 0xaa, 0x89, 0xc2, 0xa5, 0xbd, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>> +      0x85, 0x87, 0x89, 0x8a, 0x83, 0x87, 0x82, 0x85, 0x88, 0x87, 0x88,
>> +      0x8b, 0x82, 0xad, 0xaa, 0x8a, 0xc2, 0xa5, 0xbd, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
>> +      0x85, 0x86, 0x87, 0x89, 0x84, 0x88, 0x83, 0x82, 0x85, 0x84, 0x8a,
>> +      0x8e, 0x84, 0xae, 0xac, 0x89, 0xda, 0xb7, 0xd0, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>> +      0x85, 0x86, 0x87, 0x89, 0x84, 0x88, 0x83, 0x80, 0x83, 0x82, 0x8b,
>> +      0x8e, 0x85, 0xac, 0xaa, 0x89, 0xc8, 0xaa, 0xc1, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>> +      0x85, 0x86, 0x87, 0x89, 0x81, 0x85, 0x81, 0x84, 0x86, 0x84, 0x8c,
>> +      0x8c, 0x84, 0xa9, 0xa8, 0x87, 0xa3, 0x92, 0xa1, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>> +      0x85, 0x86, 0x87, 0x89, 0x84, 0x86, 0x83, 0x80, 0x83, 0x81, 0x8c,
>> +      0x8d, 0x84, 0xaa, 0xaa, 0x89, 0xce, 0xaf, 0xc5, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>> +      0x85, 0x86, 0x87, 0x89, 0x81, 0x83, 0x80, 0x83, 0x85, 0x85, 0x8c,
>> +      0x8c, 0x84, 0xa8, 0xa8, 0x88, 0xb5, 0x9f, 0xb0, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>> +      0x86, 0x86, 0x87, 0x88, 0x81, 0x83, 0x80, 0x83, 0x85, 0x85, 0x8c,
>> +      0x8b, 0x84, 0xab, 0xa8, 0x86, 0xd4, 0xb4, 0xc9, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>> +      0x86, 0x86, 0x87, 0x88, 0x81, 0x83, 0x80, 0x84, 0x84, 0x85, 0x8b,
>> +      0x8a, 0x83, 0xa6, 0xa5, 0x84, 0xbb, 0xa4, 0xb3, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>> +      0x86, 0x85, 0x86, 0x86, 0x82, 0x85, 0x81, 0x82, 0x83, 0x84, 0x8e,
>> +      0x8b, 0x83, 0xa4, 0xa3, 0x8a, 0xa1, 0x93, 0x9d, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
>> +      0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x82, 0x82, 0x84, 0x8e,
>> +      0x8b, 0x83, 0xa4, 0xa2, 0x86, 0xc1, 0xa9, 0xb7, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
>> +      0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x82, 0x82, 0x84, 0x8d,
>> +      0x89, 0x82, 0xa2, 0xa1, 0x84, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
>> +      0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x83, 0x83, 0x85, 0x8c,
>> +      0x87, 0x7f, 0xa2, 0x9d, 0x88, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xbb, 0x00, 0xc5, 0x00, 0xb4, 0x87, 0x86, 0x86, 0x84, 0x83,
>> +      0x86, 0x87, 0x87, 0x87, 0x80, 0x82, 0x7f, 0x86, 0x86, 0x88, 0x8a,
>> +      0x84, 0x7e, 0x9d, 0x9c, 0x82, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xbd, 0x00, 0xc7, 0x00, 0xb7, 0x87, 0x85, 0x85, 0x84, 0x83,
>> +      0x86, 0x86, 0x86, 0x88, 0x81, 0x83, 0x80, 0x83, 0x84, 0x85, 0x8a,
>> +      0x85, 0x7e, 0x9c, 0x9b, 0x85, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xc0, 0x00, 0xca, 0x00, 0xbb, 0x87, 0x86, 0x85, 0x83, 0x83,
>> +      0x85, 0x86, 0x86, 0x88, 0x81, 0x83, 0x80, 0x84, 0x85, 0x86, 0x89,
>> +      0x83, 0x7d, 0x9c, 0x99, 0x87, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xc4, 0x00, 0xcd, 0x00, 0xbe, 0x87, 0x86, 0x85, 0x83, 0x83,
>> +      0x86, 0x85, 0x85, 0x87, 0x81, 0x82, 0x80, 0x82, 0x82, 0x83, 0x8a,
>> +      0x85, 0x7f, 0x9f, 0x9b, 0x86, 0xb4, 0xa1, 0xac, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xc7, 0x00, 0xd0, 0x00, 0xc2, 0x87, 0x85, 0x85, 0x83, 0x82,
>> +      0x85, 0x85, 0x85, 0x86, 0x82, 0x83, 0x80, 0x82, 0x82, 0x84, 0x87,
>> +      0x86, 0x80, 0x9e, 0x9a, 0x87, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xca, 0x00, 0xd2, 0x00, 0xc5, 0x87, 0x85, 0x84, 0x82, 0x82,
>> +      0x84, 0x85, 0x85, 0x86, 0x81, 0x82, 0x7f, 0x82, 0x82, 0x84, 0x88,
>> +      0x86, 0x81, 0x9d, 0x98, 0x86, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xce, 0x00, 0xd6, 0x00, 0xca, 0x86, 0x85, 0x84, 0x83, 0x83,
>> +      0x85, 0x84, 0x84, 0x85, 0x81, 0x82, 0x80, 0x81, 0x81, 0x82, 0x89,
>> +      0x86, 0x81, 0x9c, 0x97, 0x86, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xd1, 0x00, 0xd9, 0x00, 0xce, 0x86, 0x84, 0x83, 0x83, 0x82,
>> +      0x85, 0x85, 0x85, 0x86, 0x81, 0x83, 0x81, 0x82, 0x82, 0x83, 0x86,
>> +      0x83, 0x7f, 0x99, 0x95, 0x86, 0xbb, 0xa4, 0xb3, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xd4, 0x00, 0xdb, 0x00, 0xd1, 0x86, 0x85, 0x83, 0x83, 0x82,
>> +      0x85, 0x84, 0x84, 0x85, 0x80, 0x83, 0x82, 0x80, 0x80, 0x81, 0x87,
>> +      0x84, 0x81, 0x98, 0x93, 0x85, 0xae, 0x9c, 0xa8, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xd8, 0x00, 0xde, 0x00, 0xd6, 0x86, 0x84, 0x83, 0x81, 0x81,
>> +      0x83, 0x85, 0x85, 0x85, 0x82, 0x83, 0x81, 0x81, 0x81, 0x83, 0x86,
>> +      0x84, 0x80, 0x98, 0x91, 0x85, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xdc, 0x00, 0xe2, 0x00, 0xda, 0x85, 0x84, 0x83, 0x82, 0x82,
>> +      0x84, 0x84, 0x84, 0x85, 0x81, 0x82, 0x82, 0x80, 0x80, 0x81, 0x83,
>> +      0x82, 0x7f, 0x99, 0x93, 0x86, 0x94, 0x8b, 0x92, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xdf, 0x00, 0xe5, 0x00, 0xde, 0x85, 0x84, 0x82, 0x82, 0x82,
>> +      0x84, 0x83, 0x83, 0x84, 0x81, 0x81, 0x80, 0x83, 0x82, 0x84, 0x82,
>> +      0x81, 0x7f, 0x99, 0x92, 0x86, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
>> +      0x82, 0x83, 0x83, 0x84, 0x80, 0x81, 0x80, 0x83, 0x83, 0x84, 0x80,
>> +      0x81, 0x7c, 0x99, 0x92, 0x87, 0xa1, 0x93, 0x9d, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x85, 0x84, 0x83, 0x81, 0x81,
>> +      0x82, 0x82, 0x82, 0x83, 0x80, 0x81, 0x80, 0x81, 0x80, 0x82, 0x83,
>> +      0x82, 0x80, 0x91, 0x8d, 0x83, 0x9a, 0x90, 0x96, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
>> +      0x82, 0x83, 0x83, 0x84, 0x80, 0x81, 0x80, 0x81, 0x80, 0x82, 0x83,
>> +      0x81, 0x7f, 0x91, 0x8c, 0x82, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
>> +      0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x82, 0x82,
>> +      0x82, 0x7f, 0x94, 0x89, 0x84, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
>> +      0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x82, 0x83,
>> +      0x82, 0x7f, 0x91, 0x85, 0x81, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
>> +      0x82, 0x83, 0x83, 0x83, 0x80, 0x80, 0x7f, 0x83, 0x82, 0x84, 0x83,
>> +      0x82, 0x7f, 0x90, 0x84, 0x81, 0x9a, 0x90, 0x96, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x80, 0x80,
>> +      0x82, 0x83, 0x83, 0x83, 0x80, 0x80, 0x7f, 0x80, 0x80, 0x81, 0x81,
>> +      0x82, 0x83, 0x7e, 0x80, 0x7c, 0xa4, 0x97, 0x9f, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xe9, 0x00, 0xec, 0x00, 0xe8, 0x84, 0x83, 0x82, 0x81, 0x81,
>> +      0x82, 0x82, 0x82, 0x83, 0x7f, 0x7f, 0x7f, 0x81, 0x80, 0x82, 0x83,
>> +      0x83, 0x84, 0x79, 0x7c, 0x79, 0xb1, 0xa0, 0xaa, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xed, 0x00, 0xf0, 0x00, 0xec, 0x83, 0x83, 0x82, 0x80, 0x80,
>> +      0x81, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7e, 0x81, 0x81, 0x82, 0x80,
>> +      0x81, 0x81, 0x84, 0x84, 0x83, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xf1, 0x00, 0xf4, 0x00, 0xf1, 0x83, 0x82, 0x82, 0x80, 0x80,
>> +      0x81, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x7d,
>> +      0x7e, 0x7f, 0x84, 0x84, 0x83, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xf6, 0x00, 0xf7, 0x00, 0xf5, 0x82, 0x82, 0x81, 0x80, 0x80,
>> +      0x80, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x82,
>> +      0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 0x00, 0xfa, 0x00, 0xfb, 0x00, 0xfa, 0x81, 0x81, 0x81, 0x80, 0x80,
>> +      0x80, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
>> +      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
>> +      0x00, 0x00 },
>> +    { 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,
>> +      0x00, 0x00 },
>> +    { 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,
>> +      0x00, 0x00 }
>> +};
>> +
>> +unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = {
>> +    0x18, 0x19, 0x1a, 0x1b, 0x1c,
>> +    0x1d, 0x1e, 0x1f, 0x20, 0x21
>> +};
>> +
>> +struct s6e3ha2 {
>> +    struct device *dev;
>> +    struct drm_panel panel;
>> +    struct backlight_device *bl_dev;
>> +
>> +    struct regulator_bulk_data supplies[2];
>> +    struct gpio_desc *reset_gpio;
>> +    struct gpio_desc *enable_gpio;
>> +
>> +    /* This field is tested by functions directly accessing DSI bus before
>> +     * transfer, transfer is skipped if it is set. In case of transfer
>> +     * failure or unexpected response the field is set to error value.
>> +     * Such construct allows to eliminate many checks in higher level
>> +     * functions.
>> +     */
>> +    int error;
>> +};
>> +
>> +static int  s6e3ha2_clear_error(struct s6e3ha2 *ctx)
>> +{
>> +    int ret = ctx->error;
>> +
>> +    ctx->error = 0;
>> +    return ret;
>> +}
>> +
>> +static void s6e3ha2_dcs_write(struct s6e3ha2 *ctx, const void *data, size_t len)
>> +{
>> +    struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
>> +    ssize_t ret;
>> +
>> +    if (ctx->error < 0)
>> +        return;
>> +
>> +    ret = mipi_dsi_dcs_write_buffer(dsi, data, len);
>> +    if (ret < 0) {
>> +        dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n",
>> +                        ret, (int)len, data);
>> +        ctx->error = ret;
>> +    }
>> +}
>> +
>> +#define s6e3ha2_dcs_write_seq_static(ctx, seq...) do {    \
>> +    static const u8 d[] = { seq };            \
>> +    s6e3ha2_dcs_write(ctx, d, ARRAY_SIZE(d));    \
>> +} while (0)
>> +
>> +static void s6e3ha2_test_key_on_f0(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf0, 0x5a, 0x5a);
>> +}
>> +
>> +static void s6e3ha2_test_key_off_f0(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf0, 0xa5, 0xa5);
>> +}
>> +
>> +static void s6e3ha2_test_key_on_fc(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfc, 0x5a, 0x5a);
>> +}
>> +
>> +static void s6e3ha2_test_key_off_fc(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfc, 0xa5, 0xa5);
>> +}
>> +
>> +static void s6e3ha2_single_dsi_set(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf2, 0x67);
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf9, 0x09);
>> +}
>> +
>> +static void s6e3ha2_freq_calibration(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfd, 0x1c);
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20, 0x39);
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0xa0);
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20);
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x12, 0x62,
>> +        0x40, 0x80, 0xc0, 0x28, 0x28, 0x28, 0x28, 0x39, 0xc5);
>> +}
>> +
>> +static void s6e3ha2_aor_control(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb2, 0x03, 0x10);
>> +}
>> +
>> +static void s6e3ha2_caps_elvss_set(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb6, 0x9c, 0x0a);
>> +}
>> +
>> +static void s6e3ha2_acl_off(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0x55, 0x00);
>> +}
>> +
>> +static void s6e3ha2_acl_off_opr(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb5, 0x40);
>> +}
>> +
>> +static void s6e3ha2_test_global(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb0, 0x07);
>> +}
>> +
>> +static void s6e3ha2_test(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb8, 0x19);
>> +}
>> +
>> +static void s6e3ha2_touch_hsync_on1(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx,
>> +            0xbd, 0x33, 0x11, 0x02, 0x16, 0x02, 0x16);
>> +}
>> +
>> +static void s6e3ha2_pentile_control(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xc0, 0x00, 0x00, 0xd8, 0xd8);
>> +}
>> +
>> +static void s6e3ha2_poc_global(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb0, 0x20);
>> +}
>> +
>> +static void s6e3ha2_poc_setting(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x08);
>> +}
>> +
>> +static void s6e3ha2_pcd_set_off(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xcc, 0x40, 0x51);
>> +}
>> +
>> +static void s6e3ha2_err_fg_set(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xed, 0x44);
>> +}
>> +
>> +static void s6e3ha2_hbm_off(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0x53, 0x00);
>> +}
>> +
>> +static void s6e3ha2_te_start_setting(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb9, 0x10, 0x09, 0xff, 0x00, 0x09);
>> +}
>> +
>> +static void s6e3ha2_gamma_update(struct s6e3ha2 *ctx)
>> +{
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf7, 0x03);
>> +    ndelay(100); /* need for 100ns delay */
>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf7, 0x00);
>> +}
>> +
>> +static int s6e3ha2_get_brightness(struct backlight_device *bl_dev)
>> +{
>> +    return bl_dev->props.brightness;
>> +}
>> +
>> +static void s6e3ha2_set_vint(struct s6e3ha2 *ctx)
>> +{
>> +    struct backlight_device *bl_dev = ctx->bl_dev;
>> +    unsigned int brightness = bl_dev->props.brightness;
>> +    unsigned char data[] = { 0xf4, 0x8b,
>> +            vint_table[brightness * (S6E3HA2_VINT_STATUS_MAX - 1) /
>> +            S6E3HA2_MAX_BRIGHTNESS] };
>> +
>> +    s6e3ha2_dcs_write(ctx, data, ARRAY_SIZE(data));
>> +}
>> +
>> +static unsigned int s6e3ha2_get_brightness_index(unsigned int brightness)
>> +{
>> +    return (brightness * (S6E3HA2_NUM_GAMMA_STEPS - 1)) /
>> +        S6E3HA2_MAX_BRIGHTNESS;
>> +}
>> +
>> +static int s6e3ha2_update_gamma(struct s6e3ha2 *ctx, unsigned int brightness)
>> +{
>> +    struct backlight_device *bl_dev = ctx->bl_dev;
>> +    unsigned int index = s6e3ha2_get_brightness_index(brightness);
>> +    u8 data[S6E3HA2_GAMMA_CMD_CNT + 1] = { 0xca, };
>> +
>> +    memcpy(data + 1, gamma_tbl + index, S6E3HA2_GAMMA_CMD_CNT);
>> +    s6e3ha2_dcs_write(ctx, data, ARRAY_SIZE(data));
>> +
>> +    s6e3ha2_gamma_update(ctx);
>> +    bl_dev->props.brightness = brightness;
>> +
>> +    return 0;
>> +}
>> +
>> +static int s6e3ha2_set_brightness(struct backlight_device *bl_dev)
>> +{
>> +    struct s6e3ha2 *ctx = bl_get_data(bl_dev);
>> +    unsigned int brightness = bl_dev->props.brightness;
>> +
>> +    if (brightness < S6E3HA2_MIN_BRIGHTNESS ||
>> +        brightness > bl_dev->props.max_brightness) {
>> +        dev_err(ctx->dev, "Invalid brightness: %u\n", brightness);
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (bl_dev->props.power > FB_BLANK_NORMAL)
>> +        return -EPERM;
>> +
>> +    s6e3ha2_test_key_on_f0(ctx);
>> +    s6e3ha2_update_gamma(ctx, brightness);
>> +    s6e3ha2_aor_control(ctx);
>> +    s6e3ha2_set_vint(ctx);
>> +    s6e3ha2_test_key_off_f0(ctx);
>> +
>> +    return ctx->error;
>> +}
>> +
>> +static const struct backlight_ops s6e3ha2_bl_ops = {
>> +    .get_brightness = s6e3ha2_get_brightness,
>> +    .update_status = s6e3ha2_set_brightness,
>> +};
>> +
>> +static int s6e3ha2_panel_init(struct s6e3ha2 *ctx)
>> +{
>> +    struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
>> +    int ret;
>> +
>> +    ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> +    if (ret < 0)
>> +        return ret;
>> +    usleep_range(5000, 6000);
>> +
>> +    s6e3ha2_test_key_on_f0(ctx);
>> +    s6e3ha2_single_dsi_set(ctx);
>> +    s6e3ha2_test_key_on_fc(ctx);
>> +    s6e3ha2_freq_calibration(ctx);
>> +    s6e3ha2_test_key_off_fc(ctx);
>> +    s6e3ha2_test_key_off_f0(ctx);
>> +
>> +    return 0;
>> +}
>> +
>> +static int s6e3ha2_power_off(struct s6e3ha2 *ctx)
>> +{
>> +    return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
>> +}
>> +
>> +static int s6e3ha2_disable(struct drm_panel *panel)
>> +{
>> +    struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
>> +    struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
>> +    int ret;
>> +
>> +    ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    ret = mipi_dsi_dcs_set_display_off(dsi);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    msleep(40);
>> +    ctx->bl_dev->props.power = FB_BLANK_NORMAL;
>> +
>> +    return 0;
>> +}
>> +
>> +static int s6e3ha2_unprepare(struct drm_panel *panel)
>> +{
>> +    struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
>> +    int ret;
>> +
>> +    ret = s6e3ha2_clear_error(ctx);
>> +    if (!ret)
>> +        ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
>> +
>> +    return s6e3ha2_power_off(ctx);
>> +}
>> +
>> +static int s6e3ha2_power_on(struct s6e3ha2 *ctx)
>> +{
>> +    int ret;
>> +
>> +    ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    msleep(120);
>> +
>> +    gpiod_set_value(ctx->enable_gpio, 0);
>> +    usleep_range(5000, 6000);
>> +    gpiod_set_value(ctx->enable_gpio, 1);
>> +
>> +    gpiod_set_value(ctx->reset_gpio, 1);
>> +    usleep_range(5000, 6000);
>> +    gpiod_set_value(ctx->reset_gpio, 0);
>> +    usleep_range(5000, 6000);
>> +
>> +    return 0;
>> +}
>> +static int s6e3ha2_prepare(struct drm_panel *panel)
>> +{
>> +    struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
>> +    int ret;
>> +
>> +    ret = s6e3ha2_power_on(ctx);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    ret = s6e3ha2_panel_init(ctx);
>> +    if (ret < 0)
>> +        goto err;
>> +
>> +    ret = s6e3ha2_clear_error(ctx);
>> +    if (ret < 0)
>> +        goto err;
>> +
>> +    ctx->bl_dev->props.power = FB_BLANK_NORMAL;
>> +
>> +    return 0;
>> +
>> +err:
>> +    s6e3ha2_power_off(ctx);
>> +    return ret;
>> +}
>> +
>> +static int s6e3ha2_enable(struct drm_panel *panel)
>> +{
>> +    struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
>> +    struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
>> +    int ret;
>> +
>> +    /* common setting */
>> +    ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    s6e3ha2_test_key_on_f0(ctx);
>> +    s6e3ha2_test_key_on_fc(ctx);
>> +    s6e3ha2_touch_hsync_on1(ctx);
>> +    s6e3ha2_pentile_control(ctx);
>> +    s6e3ha2_poc_global(ctx);
>> +    s6e3ha2_poc_setting(ctx);
>> +    s6e3ha2_test_key_off_fc(ctx);
>> +
>> +    /* pcd setting off for TB */
>> +    s6e3ha2_pcd_set_off(ctx);
>> +    s6e3ha2_err_fg_set(ctx);
>> +    s6e3ha2_te_start_setting(ctx);
>> +
>> +    /* brightness setting */
>> +    s6e3ha2_set_brightness(ctx->bl_dev);
>> +    s6e3ha2_aor_control(ctx);
>> +    s6e3ha2_caps_elvss_set(ctx);
>> +    s6e3ha2_gamma_update(ctx);
>> +    s6e3ha2_acl_off(ctx);
>> +    s6e3ha2_acl_off_opr(ctx);
>> +    s6e3ha2_hbm_off(ctx);
>> +
>> +    /* elvss temp compensation */
>> +    s6e3ha2_test_global(ctx);
>> +    s6e3ha2_test(ctx);
>> +    s6e3ha2_test_key_off_f0(ctx);
>> +
>> +    if (ctx->error != 0)
>> +        return ctx->error;
>> +
>> +    ret = mipi_dsi_dcs_set_display_on(dsi);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
>> +
>> +    return 0;
>> +}
>> +
>> +static const struct drm_display_mode default_mode = {
>> +    .clock = 222372,
>> +    .hdisplay = 1440,
>> +    .hsync_start = 1440 + 1,
>> +    .hsync_end = 1440 + 1 + 1,
>> +    .htotal = 1440 + 1 + 1 + 1,
>> +    .vdisplay = 2560,
>> +    .vsync_start = 2560 + 1,
>> +    .vsync_end = 2560 + 1 + 1,
>> +    .vtotal = 2560 + 1 + 1 + 15,
>> +    .vrefresh = 60,
>> +    .flags = 0,
>> +};
>> +
>> +static int s6e3ha2_get_modes(struct drm_panel *panel)
>> +{
>> +    struct drm_connector *connector = panel->connector;
>> +    struct drm_display_mode *mode;
>> +
>> +    mode = drm_mode_duplicate(panel->drm, &default_mode);
>> +    if (!mode) {
>> +        DRM_ERROR("failed to add mode %ux%ux@%u\n",
>> +                default_mode.hdisplay, default_mode.vdisplay,
>> +                default_mode.vrefresh);
>> +        return -ENOMEM;
>> +    }
>> +
>> +    drm_mode_set_name(mode);
>> +
>> +    mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
>> +    drm_mode_probed_add(connector, mode);
>> +
>> +    connector->display_info.width_mm = 71;
>> +    connector->display_info.height_mm = 125;
>> +
>> +    return 1;
>> +}
>> +
>> +static const struct drm_panel_funcs s6e3ha2_drm_funcs = {
>> +    .disable = s6e3ha2_disable,
>> +    .unprepare = s6e3ha2_unprepare,
>> +    .prepare = s6e3ha2_prepare,
>> +    .enable = s6e3ha2_enable,
>> +    .get_modes = s6e3ha2_get_modes,
>> +};
>> +
>> +static int s6e3ha2_probe(struct mipi_dsi_device *dsi)
>> +{
>> +    struct device *dev = &dsi->dev;
>> +    struct s6e3ha2 *ctx;
>> +    int ret;
>> +
>> +    ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
>> +    if (!ctx)
>> +        return -ENOMEM;
>> +
>> +    mipi_dsi_set_drvdata(dsi, ctx);
>> +
>> +    ctx->dev = dev;
>> +
>> +    dsi->lanes = 4;
>> +    dsi->format = MIPI_DSI_FMT_RGB888;
>> +    dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
>> +
>> +    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) {
>> +        dev_err(dev, "failed to get regulators: %d\n", ret);
>> +        return ret;
>> +    }
>> +
>> +    ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
>> +    if (IS_ERR(ctx->reset_gpio)) {
>> +        dev_err(dev, "cannot get reset-gpios %ld\n",
>> +            PTR_ERR(ctx->reset_gpio));
>> +        return PTR_ERR(ctx->reset_gpio);
>> +    }
>> +
>> +    ctx->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
>> +    if (IS_ERR(ctx->enable_gpio)) {
>> +        dev_err(dev, "cannot get enable-gpios %ld\n",
>> +            PTR_ERR(ctx->enable_gpio));
>> +        return PTR_ERR(ctx->enable_gpio);
>> +    }
>> +
>> +    ctx->bl_dev = backlight_device_register("s6e3ha2", dev, ctx,
>> +                        &s6e3ha2_bl_ops, NULL);
>> +    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 = S6E3HA2_MAX_BRIGHTNESS;
>> +    ctx->bl_dev->props.brightness = S6E3HA2_DEFAULT_BRIGHTNESS;
>> +    ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
>> +
>> +    drm_panel_init(&ctx->panel);
>> +    ctx->panel.dev = dev;
>> +    ctx->panel.funcs = &s6e3ha2_drm_funcs;
>> +
>> +    ret = drm_panel_add(&ctx->panel);
>> +    if (ret < 0)
>> +        goto unregister_backlight;
>> +
>> +    ret = mipi_dsi_attach(dsi);
>> +    if (ret < 0)
>> +        goto remove_panel;
>> +
>> +    return ret;
>> +
>> +remove_panel:
>> +    drm_panel_remove(&ctx->panel);
>> +
>> +unregister_backlight:
>> +    backlight_device_unregister(ctx->bl_dev);
>> +
>> +    return ret;
>> +}
>> +
>> +static int s6e3ha2_remove(struct mipi_dsi_device *dsi)
>> +{
>> +    struct s6e3ha2 *ctx = mipi_dsi_get_drvdata(dsi);
>> +
>> +    mipi_dsi_detach(dsi);
>> +    drm_panel_remove(&ctx->panel);
>> +    backlight_device_unregister(ctx->bl_dev);
>> +
>> +    return 0;
>> +}
>> +
>> +static const struct of_device_id s6e3ha2_of_match[] = {
>> +    { .compatible = "samsung,s6e3ha2" },
>> +    { }
>> +};
>> +MODULE_DEVICE_TABLE(of, s6e3ha2_of_match);
>> +
>> +static struct mipi_dsi_driver s6e3ha2_driver = {
>> +    .probe = s6e3ha2_probe,
>> +    .remove = s6e3ha2_remove,
>> +    .driver = {
>> +        .name = "panel-samsung-s6e3ha2",
>> +        .of_match_table = s6e3ha2_of_match,
>> +    },
>> +};
>> +module_mipi_dsi_driver(s6e3ha2_driver);
>> +
>> +MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
>> +MODULE_AUTHOR("Hyungwon Hwang <human.hwang@samsung.com>");
>> +MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
>> +MODULE_DESCRIPTION("MIPI-DSI based s6e3ha2 AMOLED Panel Driver");
>> +MODULE_LICENSE("GPL v2");
> 
> -- 
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
>
Thierry Reding Jan. 31, 2017, 8:54 a.m. UTC | #3
On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
> 
> 
> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
> > Dear Thierry,
> > 
> > Could you please review this patch?
> 
> Thierry, I think this patch has been reviewed enough but no comment
> from you. Seems you are busy. I will pick up this.

Sorry, but that's not how it works. This patch has gone through 8
revisions within 4 weeks, and I tend to ignore patches like that until
the dust settles.

Other than that, this continues the same madness that I've repeatedly
complained about in the past. The whole mechanism of running through a
series of writes and not caring about errors until the very end is
something we've discussed at length in the past. It also in large parts
duplicates a bunch of functions from other Samsung panel drivers that I
already said should eventually be moved to something saner.

This is now the third driver and nobody's giving a damn about improving
things.

Until somebody does, I'm going to have to NAK this.

Thierry

> > On 01/11/2017 03:33 PM, Hoegeun Kwon wrote:
> >> This patch add support for MIPI-DSI based S6E3HA2 AMOLED panel
> >> driver. This panel has 1440x2560 resolution in 5.7-inch physical
> >> panel in the TM2 device.
> >>
> >> Signed-off-by: Donghwa Lee <dh09.lee@samsung.com>
> >> Signed-off-by: Hyungwon Hwang <human.hwang@samsung.com>
> >> Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
> >> Tested-by: Chanwoo Choi <cw00.choi@samsung.com>
> >> Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
> >> ---
> >>   drivers/gpu/drm/panel/Kconfig                 |   6 +
> >>   drivers/gpu/drm/panel/Makefile                |   1 +
> >>   drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c | 754 ++++++++++++++++++++++++++
> >>   3 files changed, 761 insertions(+)
> >>   create mode 100644 drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
> >>
> >> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> >> index 62aba97..d913c83 100644
> >> --- a/drivers/gpu/drm/panel/Kconfig
> >> +++ b/drivers/gpu/drm/panel/Kconfig
> >> @@ -52,6 +52,12 @@ config DRM_PANEL_PANASONIC_VVX10F034N00
> >>         WUXGA (1920x1200) Novatek NT1397-based DSI panel as found in some
> >>         Xperia Z2 tablets
> >>   +config DRM_PANEL_SAMSUNG_S6E3HA2
> >> +    tristate "Samsung S6E3HA2 DSI video mode panel"
> >> +    depends on OF
> >> +    depends on DRM_MIPI_DSI
> >> +    select VIDEOMODE_HELPERS
> >> +
> >>   config DRM_PANEL_SAMSUNG_S6E8AA0
> >>       tristate "Samsung S6E8AA0 DSI video mode panel"
> >>       depends on OF
> >> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> >> index a5c7ec0..1d483b0 100644
> >> --- a/drivers/gpu/drm/panel/Makefile
> >> +++ b/drivers/gpu/drm/panel/Makefile
> >> @@ -3,6 +3,7 @@ obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
> >>   obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
> >>   obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
> >>   obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
> >> +obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
> >>   obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
> >>   obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
> >>   obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
> >> diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
> >> new file mode 100644
> >> index 0000000..0b9c6f4
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
> >> @@ -0,0 +1,754 @@
> >> +/*
> >> + * MIPI-DSI based s6e3ha2 AMOLED 5.7 inch panel driver.
> >> + *
> >> + * Copyright (c) 2016 Samsung Electronics Co., Ltd.
> >> + * Donghwa Lee <dh09.lee@samsung.com>
> >> + * Hyungwon Hwang <human.hwang@samsung.com>
> >> + * Hoegeun Kwon <hoegeun.kwon@samsung.com>
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify
> >> + * it under the terms of the GNU General Public License version 2 as
> >> + * published by the Free Software Foundation.
> >> + */
> >> +
> >> +#include <drm/drmP.h>
> >> +#include <drm/drm_mipi_dsi.h>
> >> +#include <drm/drm_panel.h>
> >> +#include <linux/backlight.h>
> >> +#include <linux/gpio/consumer.h>
> >> +#include <linux/regulator/consumer.h>
> >> +
> >> +#define S6E3HA2_MIN_BRIGHTNESS        0
> >> +#define S6E3HA2_MAX_BRIGHTNESS        100
> >> +#define S6E3HA2_DEFAULT_BRIGHTNESS    80
> >> +
> >> +#define S6E3HA2_NUM_GAMMA_STEPS        46
> >> +#define S6E3HA2_GAMMA_CMD_CNT        35
> >> +#define S6E3HA2_VINT_STATUS_MAX        10
> >> +
> >> +static const u8 gamma_tbl[S6E3HA2_NUM_GAMMA_STEPS][S6E3HA2_GAMMA_CMD_CNT] = {
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x82, 0x83,
> >> +      0x85, 0x88, 0x8b, 0x8b, 0x84, 0x88, 0x82, 0x82, 0x89, 0x86, 0x8c,
> >> +      0x94, 0x84, 0xb1, 0xaf, 0x8e, 0xcf, 0xad, 0xc9, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x84, 0x84,
> >> +      0x85, 0x87, 0x8b, 0x8a, 0x84, 0x88, 0x82, 0x82, 0x89, 0x86, 0x8a,
> >> +      0x93, 0x84, 0xb0, 0xae, 0x8e, 0xc9, 0xa8, 0xc5, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> >> +      0x85, 0x86, 0x8a, 0x8a, 0x84, 0x88, 0x81, 0x84, 0x8a, 0x88, 0x8a,
> >> +      0x91, 0x84, 0xb1, 0xae, 0x8b, 0xd5, 0xb2, 0xcc, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> >> +      0x85, 0x86, 0x8a, 0x8a, 0x84, 0x87, 0x81, 0x84, 0x8a, 0x87, 0x8a,
> >> +      0x91, 0x85, 0xae, 0xac, 0x8a, 0xc3, 0xa3, 0xc0, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x85, 0x85,
> >> +      0x86, 0x85, 0x88, 0x89, 0x84, 0x89, 0x82, 0x84, 0x87, 0x85, 0x8b,
> >> +      0x91, 0x88, 0xad, 0xab, 0x8a, 0xb7, 0x9b, 0xb6, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> >> +      0x85, 0x86, 0x89, 0x8a, 0x84, 0x89, 0x83, 0x83, 0x86, 0x84, 0x8b,
> >> +      0x90, 0x84, 0xb0, 0xae, 0x8b, 0xce, 0xad, 0xc8, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> >> +      0x85, 0x87, 0x89, 0x8a, 0x83, 0x87, 0x82, 0x85, 0x88, 0x87, 0x89,
> >> +      0x8f, 0x84, 0xac, 0xaa, 0x89, 0xb1, 0x98, 0xaf, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> >> +      0x85, 0x86, 0x88, 0x89, 0x84, 0x88, 0x83, 0x82, 0x85, 0x84, 0x8c,
> >> +      0x91, 0x86, 0xac, 0xaa, 0x89, 0xc2, 0xa5, 0xbd, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> >> +      0x85, 0x87, 0x89, 0x8a, 0x83, 0x87, 0x82, 0x85, 0x88, 0x87, 0x88,
> >> +      0x8b, 0x82, 0xad, 0xaa, 0x8a, 0xc2, 0xa5, 0xbd, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> >> +      0x85, 0x86, 0x87, 0x89, 0x84, 0x88, 0x83, 0x82, 0x85, 0x84, 0x8a,
> >> +      0x8e, 0x84, 0xae, 0xac, 0x89, 0xda, 0xb7, 0xd0, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> >> +      0x85, 0x86, 0x87, 0x89, 0x84, 0x88, 0x83, 0x80, 0x83, 0x82, 0x8b,
> >> +      0x8e, 0x85, 0xac, 0xaa, 0x89, 0xc8, 0xaa, 0xc1, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> >> +      0x85, 0x86, 0x87, 0x89, 0x81, 0x85, 0x81, 0x84, 0x86, 0x84, 0x8c,
> >> +      0x8c, 0x84, 0xa9, 0xa8, 0x87, 0xa3, 0x92, 0xa1, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> >> +      0x85, 0x86, 0x87, 0x89, 0x84, 0x86, 0x83, 0x80, 0x83, 0x81, 0x8c,
> >> +      0x8d, 0x84, 0xaa, 0xaa, 0x89, 0xce, 0xaf, 0xc5, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> >> +      0x85, 0x86, 0x87, 0x89, 0x81, 0x83, 0x80, 0x83, 0x85, 0x85, 0x8c,
> >> +      0x8c, 0x84, 0xa8, 0xa8, 0x88, 0xb5, 0x9f, 0xb0, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> >> +      0x86, 0x86, 0x87, 0x88, 0x81, 0x83, 0x80, 0x83, 0x85, 0x85, 0x8c,
> >> +      0x8b, 0x84, 0xab, 0xa8, 0x86, 0xd4, 0xb4, 0xc9, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> >> +      0x86, 0x86, 0x87, 0x88, 0x81, 0x83, 0x80, 0x84, 0x84, 0x85, 0x8b,
> >> +      0x8a, 0x83, 0xa6, 0xa5, 0x84, 0xbb, 0xa4, 0xb3, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> >> +      0x86, 0x85, 0x86, 0x86, 0x82, 0x85, 0x81, 0x82, 0x83, 0x84, 0x8e,
> >> +      0x8b, 0x83, 0xa4, 0xa3, 0x8a, 0xa1, 0x93, 0x9d, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
> >> +      0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x82, 0x82, 0x84, 0x8e,
> >> +      0x8b, 0x83, 0xa4, 0xa2, 0x86, 0xc1, 0xa9, 0xb7, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
> >> +      0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x82, 0x82, 0x84, 0x8d,
> >> +      0x89, 0x82, 0xa2, 0xa1, 0x84, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
> >> +      0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x83, 0x83, 0x85, 0x8c,
> >> +      0x87, 0x7f, 0xa2, 0x9d, 0x88, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xbb, 0x00, 0xc5, 0x00, 0xb4, 0x87, 0x86, 0x86, 0x84, 0x83,
> >> +      0x86, 0x87, 0x87, 0x87, 0x80, 0x82, 0x7f, 0x86, 0x86, 0x88, 0x8a,
> >> +      0x84, 0x7e, 0x9d, 0x9c, 0x82, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xbd, 0x00, 0xc7, 0x00, 0xb7, 0x87, 0x85, 0x85, 0x84, 0x83,
> >> +      0x86, 0x86, 0x86, 0x88, 0x81, 0x83, 0x80, 0x83, 0x84, 0x85, 0x8a,
> >> +      0x85, 0x7e, 0x9c, 0x9b, 0x85, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xc0, 0x00, 0xca, 0x00, 0xbb, 0x87, 0x86, 0x85, 0x83, 0x83,
> >> +      0x85, 0x86, 0x86, 0x88, 0x81, 0x83, 0x80, 0x84, 0x85, 0x86, 0x89,
> >> +      0x83, 0x7d, 0x9c, 0x99, 0x87, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xc4, 0x00, 0xcd, 0x00, 0xbe, 0x87, 0x86, 0x85, 0x83, 0x83,
> >> +      0x86, 0x85, 0x85, 0x87, 0x81, 0x82, 0x80, 0x82, 0x82, 0x83, 0x8a,
> >> +      0x85, 0x7f, 0x9f, 0x9b, 0x86, 0xb4, 0xa1, 0xac, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xc7, 0x00, 0xd0, 0x00, 0xc2, 0x87, 0x85, 0x85, 0x83, 0x82,
> >> +      0x85, 0x85, 0x85, 0x86, 0x82, 0x83, 0x80, 0x82, 0x82, 0x84, 0x87,
> >> +      0x86, 0x80, 0x9e, 0x9a, 0x87, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xca, 0x00, 0xd2, 0x00, 0xc5, 0x87, 0x85, 0x84, 0x82, 0x82,
> >> +      0x84, 0x85, 0x85, 0x86, 0x81, 0x82, 0x7f, 0x82, 0x82, 0x84, 0x88,
> >> +      0x86, 0x81, 0x9d, 0x98, 0x86, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xce, 0x00, 0xd6, 0x00, 0xca, 0x86, 0x85, 0x84, 0x83, 0x83,
> >> +      0x85, 0x84, 0x84, 0x85, 0x81, 0x82, 0x80, 0x81, 0x81, 0x82, 0x89,
> >> +      0x86, 0x81, 0x9c, 0x97, 0x86, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xd1, 0x00, 0xd9, 0x00, 0xce, 0x86, 0x84, 0x83, 0x83, 0x82,
> >> +      0x85, 0x85, 0x85, 0x86, 0x81, 0x83, 0x81, 0x82, 0x82, 0x83, 0x86,
> >> +      0x83, 0x7f, 0x99, 0x95, 0x86, 0xbb, 0xa4, 0xb3, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xd4, 0x00, 0xdb, 0x00, 0xd1, 0x86, 0x85, 0x83, 0x83, 0x82,
> >> +      0x85, 0x84, 0x84, 0x85, 0x80, 0x83, 0x82, 0x80, 0x80, 0x81, 0x87,
> >> +      0x84, 0x81, 0x98, 0x93, 0x85, 0xae, 0x9c, 0xa8, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xd8, 0x00, 0xde, 0x00, 0xd6, 0x86, 0x84, 0x83, 0x81, 0x81,
> >> +      0x83, 0x85, 0x85, 0x85, 0x82, 0x83, 0x81, 0x81, 0x81, 0x83, 0x86,
> >> +      0x84, 0x80, 0x98, 0x91, 0x85, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xdc, 0x00, 0xe2, 0x00, 0xda, 0x85, 0x84, 0x83, 0x82, 0x82,
> >> +      0x84, 0x84, 0x84, 0x85, 0x81, 0x82, 0x82, 0x80, 0x80, 0x81, 0x83,
> >> +      0x82, 0x7f, 0x99, 0x93, 0x86, 0x94, 0x8b, 0x92, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xdf, 0x00, 0xe5, 0x00, 0xde, 0x85, 0x84, 0x82, 0x82, 0x82,
> >> +      0x84, 0x83, 0x83, 0x84, 0x81, 0x81, 0x80, 0x83, 0x82, 0x84, 0x82,
> >> +      0x81, 0x7f, 0x99, 0x92, 0x86, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> >> +      0x82, 0x83, 0x83, 0x84, 0x80, 0x81, 0x80, 0x83, 0x83, 0x84, 0x80,
> >> +      0x81, 0x7c, 0x99, 0x92, 0x87, 0xa1, 0x93, 0x9d, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x85, 0x84, 0x83, 0x81, 0x81,
> >> +      0x82, 0x82, 0x82, 0x83, 0x80, 0x81, 0x80, 0x81, 0x80, 0x82, 0x83,
> >> +      0x82, 0x80, 0x91, 0x8d, 0x83, 0x9a, 0x90, 0x96, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> >> +      0x82, 0x83, 0x83, 0x84, 0x80, 0x81, 0x80, 0x81, 0x80, 0x82, 0x83,
> >> +      0x81, 0x7f, 0x91, 0x8c, 0x82, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> >> +      0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x82, 0x82,
> >> +      0x82, 0x7f, 0x94, 0x89, 0x84, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> >> +      0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x82, 0x83,
> >> +      0x82, 0x7f, 0x91, 0x85, 0x81, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> >> +      0x82, 0x83, 0x83, 0x83, 0x80, 0x80, 0x7f, 0x83, 0x82, 0x84, 0x83,
> >> +      0x82, 0x7f, 0x90, 0x84, 0x81, 0x9a, 0x90, 0x96, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x80, 0x80,
> >> +      0x82, 0x83, 0x83, 0x83, 0x80, 0x80, 0x7f, 0x80, 0x80, 0x81, 0x81,
> >> +      0x82, 0x83, 0x7e, 0x80, 0x7c, 0xa4, 0x97, 0x9f, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xe9, 0x00, 0xec, 0x00, 0xe8, 0x84, 0x83, 0x82, 0x81, 0x81,
> >> +      0x82, 0x82, 0x82, 0x83, 0x7f, 0x7f, 0x7f, 0x81, 0x80, 0x82, 0x83,
> >> +      0x83, 0x84, 0x79, 0x7c, 0x79, 0xb1, 0xa0, 0xaa, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xed, 0x00, 0xf0, 0x00, 0xec, 0x83, 0x83, 0x82, 0x80, 0x80,
> >> +      0x81, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7e, 0x81, 0x81, 0x82, 0x80,
> >> +      0x81, 0x81, 0x84, 0x84, 0x83, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xf1, 0x00, 0xf4, 0x00, 0xf1, 0x83, 0x82, 0x82, 0x80, 0x80,
> >> +      0x81, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x7d,
> >> +      0x7e, 0x7f, 0x84, 0x84, 0x83, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xf6, 0x00, 0xf7, 0x00, 0xf5, 0x82, 0x82, 0x81, 0x80, 0x80,
> >> +      0x80, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x82,
> >> +      0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 0x00, 0xfa, 0x00, 0xfb, 0x00, 0xfa, 0x81, 0x81, 0x81, 0x80, 0x80,
> >> +      0x80, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
> >> +      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> >> +      0x00, 0x00 },
> >> +    { 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,
> >> +      0x00, 0x00 },
> >> +    { 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,
> >> +      0x00, 0x00 }
> >> +};
> >> +
> >> +unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = {
> >> +    0x18, 0x19, 0x1a, 0x1b, 0x1c,
> >> +    0x1d, 0x1e, 0x1f, 0x20, 0x21
> >> +};
> >> +
> >> +struct s6e3ha2 {
> >> +    struct device *dev;
> >> +    struct drm_panel panel;
> >> +    struct backlight_device *bl_dev;
> >> +
> >> +    struct regulator_bulk_data supplies[2];
> >> +    struct gpio_desc *reset_gpio;
> >> +    struct gpio_desc *enable_gpio;
> >> +
> >> +    /* This field is tested by functions directly accessing DSI bus before
> >> +     * transfer, transfer is skipped if it is set. In case of transfer
> >> +     * failure or unexpected response the field is set to error value.
> >> +     * Such construct allows to eliminate many checks in higher level
> >> +     * functions.
> >> +     */
> >> +    int error;
> >> +};
> >> +
> >> +static int  s6e3ha2_clear_error(struct s6e3ha2 *ctx)
> >> +{
> >> +    int ret = ctx->error;
> >> +
> >> +    ctx->error = 0;
> >> +    return ret;
> >> +}
> >> +
> >> +static void s6e3ha2_dcs_write(struct s6e3ha2 *ctx, const void *data, size_t len)
> >> +{
> >> +    struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> >> +    ssize_t ret;
> >> +
> >> +    if (ctx->error < 0)
> >> +        return;
> >> +
> >> +    ret = mipi_dsi_dcs_write_buffer(dsi, data, len);
> >> +    if (ret < 0) {
> >> +        dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n",
> >> +                        ret, (int)len, data);
> >> +        ctx->error = ret;
> >> +    }
> >> +}
> >> +
> >> +#define s6e3ha2_dcs_write_seq_static(ctx, seq...) do {    \
> >> +    static const u8 d[] = { seq };            \
> >> +    s6e3ha2_dcs_write(ctx, d, ARRAY_SIZE(d));    \
> >> +} while (0)
> >> +
> >> +static void s6e3ha2_test_key_on_f0(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf0, 0x5a, 0x5a);
> >> +}
> >> +
> >> +static void s6e3ha2_test_key_off_f0(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf0, 0xa5, 0xa5);
> >> +}
> >> +
> >> +static void s6e3ha2_test_key_on_fc(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfc, 0x5a, 0x5a);
> >> +}
> >> +
> >> +static void s6e3ha2_test_key_off_fc(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfc, 0xa5, 0xa5);
> >> +}
> >> +
> >> +static void s6e3ha2_single_dsi_set(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf2, 0x67);
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf9, 0x09);
> >> +}
> >> +
> >> +static void s6e3ha2_freq_calibration(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfd, 0x1c);
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20, 0x39);
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0xa0);
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20);
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x12, 0x62,
> >> +        0x40, 0x80, 0xc0, 0x28, 0x28, 0x28, 0x28, 0x39, 0xc5);
> >> +}
> >> +
> >> +static void s6e3ha2_aor_control(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb2, 0x03, 0x10);
> >> +}
> >> +
> >> +static void s6e3ha2_caps_elvss_set(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb6, 0x9c, 0x0a);
> >> +}
> >> +
> >> +static void s6e3ha2_acl_off(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0x55, 0x00);
> >> +}
> >> +
> >> +static void s6e3ha2_acl_off_opr(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb5, 0x40);
> >> +}
> >> +
> >> +static void s6e3ha2_test_global(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb0, 0x07);
> >> +}
> >> +
> >> +static void s6e3ha2_test(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb8, 0x19);
> >> +}
> >> +
> >> +static void s6e3ha2_touch_hsync_on1(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx,
> >> +            0xbd, 0x33, 0x11, 0x02, 0x16, 0x02, 0x16);
> >> +}
> >> +
> >> +static void s6e3ha2_pentile_control(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xc0, 0x00, 0x00, 0xd8, 0xd8);
> >> +}
> >> +
> >> +static void s6e3ha2_poc_global(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb0, 0x20);
> >> +}
> >> +
> >> +static void s6e3ha2_poc_setting(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x08);
> >> +}
> >> +
> >> +static void s6e3ha2_pcd_set_off(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xcc, 0x40, 0x51);
> >> +}
> >> +
> >> +static void s6e3ha2_err_fg_set(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xed, 0x44);
> >> +}
> >> +
> >> +static void s6e3ha2_hbm_off(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0x53, 0x00);
> >> +}
> >> +
> >> +static void s6e3ha2_te_start_setting(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb9, 0x10, 0x09, 0xff, 0x00, 0x09);
> >> +}
> >> +
> >> +static void s6e3ha2_gamma_update(struct s6e3ha2 *ctx)
> >> +{
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf7, 0x03);
> >> +    ndelay(100); /* need for 100ns delay */
> >> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf7, 0x00);
> >> +}
> >> +
> >> +static int s6e3ha2_get_brightness(struct backlight_device *bl_dev)
> >> +{
> >> +    return bl_dev->props.brightness;
> >> +}
> >> +
> >> +static void s6e3ha2_set_vint(struct s6e3ha2 *ctx)
> >> +{
> >> +    struct backlight_device *bl_dev = ctx->bl_dev;
> >> +    unsigned int brightness = bl_dev->props.brightness;
> >> +    unsigned char data[] = { 0xf4, 0x8b,
> >> +            vint_table[brightness * (S6E3HA2_VINT_STATUS_MAX - 1) /
> >> +            S6E3HA2_MAX_BRIGHTNESS] };
> >> +
> >> +    s6e3ha2_dcs_write(ctx, data, ARRAY_SIZE(data));
> >> +}
> >> +
> >> +static unsigned int s6e3ha2_get_brightness_index(unsigned int brightness)
> >> +{
> >> +    return (brightness * (S6E3HA2_NUM_GAMMA_STEPS - 1)) /
> >> +        S6E3HA2_MAX_BRIGHTNESS;
> >> +}
> >> +
> >> +static int s6e3ha2_update_gamma(struct s6e3ha2 *ctx, unsigned int brightness)
> >> +{
> >> +    struct backlight_device *bl_dev = ctx->bl_dev;
> >> +    unsigned int index = s6e3ha2_get_brightness_index(brightness);
> >> +    u8 data[S6E3HA2_GAMMA_CMD_CNT + 1] = { 0xca, };
> >> +
> >> +    memcpy(data + 1, gamma_tbl + index, S6E3HA2_GAMMA_CMD_CNT);
> >> +    s6e3ha2_dcs_write(ctx, data, ARRAY_SIZE(data));
> >> +
> >> +    s6e3ha2_gamma_update(ctx);
> >> +    bl_dev->props.brightness = brightness;
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int s6e3ha2_set_brightness(struct backlight_device *bl_dev)
> >> +{
> >> +    struct s6e3ha2 *ctx = bl_get_data(bl_dev);
> >> +    unsigned int brightness = bl_dev->props.brightness;
> >> +
> >> +    if (brightness < S6E3HA2_MIN_BRIGHTNESS ||
> >> +        brightness > bl_dev->props.max_brightness) {
> >> +        dev_err(ctx->dev, "Invalid brightness: %u\n", brightness);
> >> +        return -EINVAL;
> >> +    }
> >> +
> >> +    if (bl_dev->props.power > FB_BLANK_NORMAL)
> >> +        return -EPERM;
> >> +
> >> +    s6e3ha2_test_key_on_f0(ctx);
> >> +    s6e3ha2_update_gamma(ctx, brightness);
> >> +    s6e3ha2_aor_control(ctx);
> >> +    s6e3ha2_set_vint(ctx);
> >> +    s6e3ha2_test_key_off_f0(ctx);
> >> +
> >> +    return ctx->error;
> >> +}
> >> +
> >> +static const struct backlight_ops s6e3ha2_bl_ops = {
> >> +    .get_brightness = s6e3ha2_get_brightness,
> >> +    .update_status = s6e3ha2_set_brightness,
> >> +};
> >> +
> >> +static int s6e3ha2_panel_init(struct s6e3ha2 *ctx)
> >> +{
> >> +    struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> >> +    int ret;
> >> +
> >> +    ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +    usleep_range(5000, 6000);
> >> +
> >> +    s6e3ha2_test_key_on_f0(ctx);
> >> +    s6e3ha2_single_dsi_set(ctx);
> >> +    s6e3ha2_test_key_on_fc(ctx);
> >> +    s6e3ha2_freq_calibration(ctx);
> >> +    s6e3ha2_test_key_off_fc(ctx);
> >> +    s6e3ha2_test_key_off_f0(ctx);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int s6e3ha2_power_off(struct s6e3ha2 *ctx)
> >> +{
> >> +    return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> >> +}
> >> +
> >> +static int s6e3ha2_disable(struct drm_panel *panel)
> >> +{
> >> +    struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
> >> +    struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> >> +    int ret;
> >> +
> >> +    ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    ret = mipi_dsi_dcs_set_display_off(dsi);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    msleep(40);
> >> +    ctx->bl_dev->props.power = FB_BLANK_NORMAL;
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int s6e3ha2_unprepare(struct drm_panel *panel)
> >> +{
> >> +    struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
> >> +    int ret;
> >> +
> >> +    ret = s6e3ha2_clear_error(ctx);
> >> +    if (!ret)
> >> +        ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
> >> +
> >> +    return s6e3ha2_power_off(ctx);
> >> +}
> >> +
> >> +static int s6e3ha2_power_on(struct s6e3ha2 *ctx)
> >> +{
> >> +    int ret;
> >> +
> >> +    ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    msleep(120);
> >> +
> >> +    gpiod_set_value(ctx->enable_gpio, 0);
> >> +    usleep_range(5000, 6000);
> >> +    gpiod_set_value(ctx->enable_gpio, 1);
> >> +
> >> +    gpiod_set_value(ctx->reset_gpio, 1);
> >> +    usleep_range(5000, 6000);
> >> +    gpiod_set_value(ctx->reset_gpio, 0);
> >> +    usleep_range(5000, 6000);
> >> +
> >> +    return 0;
> >> +}
> >> +static int s6e3ha2_prepare(struct drm_panel *panel)
> >> +{
> >> +    struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
> >> +    int ret;
> >> +
> >> +    ret = s6e3ha2_power_on(ctx);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    ret = s6e3ha2_panel_init(ctx);
> >> +    if (ret < 0)
> >> +        goto err;
> >> +
> >> +    ret = s6e3ha2_clear_error(ctx);
> >> +    if (ret < 0)
> >> +        goto err;
> >> +
> >> +    ctx->bl_dev->props.power = FB_BLANK_NORMAL;
> >> +
> >> +    return 0;
> >> +
> >> +err:
> >> +    s6e3ha2_power_off(ctx);
> >> +    return ret;
> >> +}
> >> +
> >> +static int s6e3ha2_enable(struct drm_panel *panel)
> >> +{
> >> +    struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
> >> +    struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> >> +    int ret;
> >> +
> >> +    /* common setting */
> >> +    ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    s6e3ha2_test_key_on_f0(ctx);
> >> +    s6e3ha2_test_key_on_fc(ctx);
> >> +    s6e3ha2_touch_hsync_on1(ctx);
> >> +    s6e3ha2_pentile_control(ctx);
> >> +    s6e3ha2_poc_global(ctx);
> >> +    s6e3ha2_poc_setting(ctx);
> >> +    s6e3ha2_test_key_off_fc(ctx);
> >> +
> >> +    /* pcd setting off for TB */
> >> +    s6e3ha2_pcd_set_off(ctx);
> >> +    s6e3ha2_err_fg_set(ctx);
> >> +    s6e3ha2_te_start_setting(ctx);
> >> +
> >> +    /* brightness setting */
> >> +    s6e3ha2_set_brightness(ctx->bl_dev);
> >> +    s6e3ha2_aor_control(ctx);
> >> +    s6e3ha2_caps_elvss_set(ctx);
> >> +    s6e3ha2_gamma_update(ctx);
> >> +    s6e3ha2_acl_off(ctx);
> >> +    s6e3ha2_acl_off_opr(ctx);
> >> +    s6e3ha2_hbm_off(ctx);
> >> +
> >> +    /* elvss temp compensation */
> >> +    s6e3ha2_test_global(ctx);
> >> +    s6e3ha2_test(ctx);
> >> +    s6e3ha2_test_key_off_f0(ctx);
> >> +
> >> +    if (ctx->error != 0)
> >> +        return ctx->error;
> >> +
> >> +    ret = mipi_dsi_dcs_set_display_on(dsi);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static const struct drm_display_mode default_mode = {
> >> +    .clock = 222372,
> >> +    .hdisplay = 1440,
> >> +    .hsync_start = 1440 + 1,
> >> +    .hsync_end = 1440 + 1 + 1,
> >> +    .htotal = 1440 + 1 + 1 + 1,
> >> +    .vdisplay = 2560,
> >> +    .vsync_start = 2560 + 1,
> >> +    .vsync_end = 2560 + 1 + 1,
> >> +    .vtotal = 2560 + 1 + 1 + 15,
> >> +    .vrefresh = 60,
> >> +    .flags = 0,
> >> +};
> >> +
> >> +static int s6e3ha2_get_modes(struct drm_panel *panel)
> >> +{
> >> +    struct drm_connector *connector = panel->connector;
> >> +    struct drm_display_mode *mode;
> >> +
> >> +    mode = drm_mode_duplicate(panel->drm, &default_mode);
> >> +    if (!mode) {
> >> +        DRM_ERROR("failed to add mode %ux%ux@%u\n",
> >> +                default_mode.hdisplay, default_mode.vdisplay,
> >> +                default_mode.vrefresh);
> >> +        return -ENOMEM;
> >> +    }
> >> +
> >> +    drm_mode_set_name(mode);
> >> +
> >> +    mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
> >> +    drm_mode_probed_add(connector, mode);
> >> +
> >> +    connector->display_info.width_mm = 71;
> >> +    connector->display_info.height_mm = 125;
> >> +
> >> +    return 1;
> >> +}
> >> +
> >> +static const struct drm_panel_funcs s6e3ha2_drm_funcs = {
> >> +    .disable = s6e3ha2_disable,
> >> +    .unprepare = s6e3ha2_unprepare,
> >> +    .prepare = s6e3ha2_prepare,
> >> +    .enable = s6e3ha2_enable,
> >> +    .get_modes = s6e3ha2_get_modes,
> >> +};
> >> +
> >> +static int s6e3ha2_probe(struct mipi_dsi_device *dsi)
> >> +{
> >> +    struct device *dev = &dsi->dev;
> >> +    struct s6e3ha2 *ctx;
> >> +    int ret;
> >> +
> >> +    ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> >> +    if (!ctx)
> >> +        return -ENOMEM;
> >> +
> >> +    mipi_dsi_set_drvdata(dsi, ctx);
> >> +
> >> +    ctx->dev = dev;
> >> +
> >> +    dsi->lanes = 4;
> >> +    dsi->format = MIPI_DSI_FMT_RGB888;
> >> +    dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
> >> +
> >> +    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) {
> >> +        dev_err(dev, "failed to get regulators: %d\n", ret);
> >> +        return ret;
> >> +    }
> >> +
> >> +    ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> >> +    if (IS_ERR(ctx->reset_gpio)) {
> >> +        dev_err(dev, "cannot get reset-gpios %ld\n",
> >> +            PTR_ERR(ctx->reset_gpio));
> >> +        return PTR_ERR(ctx->reset_gpio);
> >> +    }
> >> +
> >> +    ctx->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
> >> +    if (IS_ERR(ctx->enable_gpio)) {
> >> +        dev_err(dev, "cannot get enable-gpios %ld\n",
> >> +            PTR_ERR(ctx->enable_gpio));
> >> +        return PTR_ERR(ctx->enable_gpio);
> >> +    }
> >> +
> >> +    ctx->bl_dev = backlight_device_register("s6e3ha2", dev, ctx,
> >> +                        &s6e3ha2_bl_ops, NULL);
> >> +    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 = S6E3HA2_MAX_BRIGHTNESS;
> >> +    ctx->bl_dev->props.brightness = S6E3HA2_DEFAULT_BRIGHTNESS;
> >> +    ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
> >> +
> >> +    drm_panel_init(&ctx->panel);
> >> +    ctx->panel.dev = dev;
> >> +    ctx->panel.funcs = &s6e3ha2_drm_funcs;
> >> +
> >> +    ret = drm_panel_add(&ctx->panel);
> >> +    if (ret < 0)
> >> +        goto unregister_backlight;
> >> +
> >> +    ret = mipi_dsi_attach(dsi);
> >> +    if (ret < 0)
> >> +        goto remove_panel;
> >> +
> >> +    return ret;
> >> +
> >> +remove_panel:
> >> +    drm_panel_remove(&ctx->panel);
> >> +
> >> +unregister_backlight:
> >> +    backlight_device_unregister(ctx->bl_dev);
> >> +
> >> +    return ret;
> >> +}
> >> +
> >> +static int s6e3ha2_remove(struct mipi_dsi_device *dsi)
> >> +{
> >> +    struct s6e3ha2 *ctx = mipi_dsi_get_drvdata(dsi);
> >> +
> >> +    mipi_dsi_detach(dsi);
> >> +    drm_panel_remove(&ctx->panel);
> >> +    backlight_device_unregister(ctx->bl_dev);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static const struct of_device_id s6e3ha2_of_match[] = {
> >> +    { .compatible = "samsung,s6e3ha2" },
> >> +    { }
> >> +};
> >> +MODULE_DEVICE_TABLE(of, s6e3ha2_of_match);
> >> +
> >> +static struct mipi_dsi_driver s6e3ha2_driver = {
> >> +    .probe = s6e3ha2_probe,
> >> +    .remove = s6e3ha2_remove,
> >> +    .driver = {
> >> +        .name = "panel-samsung-s6e3ha2",
> >> +        .of_match_table = s6e3ha2_of_match,
> >> +    },
> >> +};
> >> +module_mipi_dsi_driver(s6e3ha2_driver);
> >> +
> >> +MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
> >> +MODULE_AUTHOR("Hyungwon Hwang <human.hwang@samsung.com>");
> >> +MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
> >> +MODULE_DESCRIPTION("MIPI-DSI based s6e3ha2 AMOLED Panel Driver");
> >> +MODULE_LICENSE("GPL v2");
> > 
> > -- 
> > To unsubscribe from this list: send the line "unsubscribe devicetree" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > 
> >
Krzysztof Kozlowski Jan. 31, 2017, 9:22 a.m. UTC | #4
On Tue, Jan 31, 2017 at 2:01 AM, Inki Dae <inki.dae@samsung.com> wrote:
>
>
> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>> Dear Thierry,
>>
>> Could you please review this patch?
>
> Thierry, I think this patch has been reviewed enough but no comment from you. Seems you are busy. I will pick up this.
>

Comments from v8 were not resolved and I think we are waiting for v9:
https://lkml.org/lkml/2017/1/11/178

If that is not correct then please clarify.

Best regards,
Krzysztof
Inki Dae Jan. 31, 2017, 9:26 a.m. UTC | #5
2017년 01월 31일 17:54에 Thierry Reding 이(가) 쓴 글:
> On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
>>
>>
>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>> Dear Thierry,
>>>
>>> Could you please review this patch?
>>
>> Thierry, I think this patch has been reviewed enough but no comment
>> from you. Seems you are busy. I will pick up this.
> 
> Sorry, but that's not how it works. This patch has gone through 8
> revisions within 4 weeks, and I tend to ignore patches like that until
> the dust settles.
> 
> Other than that, this continues the same madness that I've repeatedly
> complained about in the past. The whole mechanism of running through a
> series of writes and not caring about errors until the very end is
> something we've discussed at length in the past. It also in large parts
> duplicates a bunch of functions from other Samsung panel drivers that I
> already said should eventually be moved to something saner.
> 
> This is now the third driver and nobody's giving a damn about improving
> things.
> 
> Until somebody does, I'm going to have to NAK this.

Hoegeun had got review comments and updated his patch seriously. no feedback from maintainer would make him and new contributors to frustrate.
So please leave *the things* so that the contributors can understand *what they have to modify more* or *why maintainer ignores the patch*.

And also if you specify *the things* here than I would do what I can so *the things* could be cleared. 

Thanks,
Inki Dae

> 
> Thierry
> 
>>> On 01/11/2017 03:33 PM, Hoegeun Kwon wrote:
>>>> This patch add support for MIPI-DSI based S6E3HA2 AMOLED panel
>>>> driver. This panel has 1440x2560 resolution in 5.7-inch physical
>>>> panel in the TM2 device.
>>>>
>>>> Signed-off-by: Donghwa Lee <dh09.lee@samsung.com>
>>>> Signed-off-by: Hyungwon Hwang <human.hwang@samsung.com>
>>>> Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
>>>> Tested-by: Chanwoo Choi <cw00.choi@samsung.com>
>>>> Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
>>>> ---
>>>>   drivers/gpu/drm/panel/Kconfig                 |   6 +
>>>>   drivers/gpu/drm/panel/Makefile                |   1 +
>>>>   drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c | 754 ++++++++++++++++++++++++++
>>>>   3 files changed, 761 insertions(+)
>>>>   create mode 100644 drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
>>>>
>>>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>>>> index 62aba97..d913c83 100644
>>>> --- a/drivers/gpu/drm/panel/Kconfig
>>>> +++ b/drivers/gpu/drm/panel/Kconfig
>>>> @@ -52,6 +52,12 @@ config DRM_PANEL_PANASONIC_VVX10F034N00
>>>>         WUXGA (1920x1200) Novatek NT1397-based DSI panel as found in some
>>>>         Xperia Z2 tablets
>>>>   +config DRM_PANEL_SAMSUNG_S6E3HA2
>>>> +    tristate "Samsung S6E3HA2 DSI video mode panel"
>>>> +    depends on OF
>>>> +    depends on DRM_MIPI_DSI
>>>> +    select VIDEOMODE_HELPERS
>>>> +
>>>>   config DRM_PANEL_SAMSUNG_S6E8AA0
>>>>       tristate "Samsung S6E8AA0 DSI video mode panel"
>>>>       depends on OF
>>>> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
>>>> index a5c7ec0..1d483b0 100644
>>>> --- a/drivers/gpu/drm/panel/Makefile
>>>> +++ b/drivers/gpu/drm/panel/Makefile
>>>> @@ -3,6 +3,7 @@ obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
>>>>   obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
>>>>   obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
>>>>   obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
>>>> +obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
>>>>   obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>>>>   obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>>>>   obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
>>>> diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
>>>> new file mode 100644
>>>> index 0000000..0b9c6f4
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
>>>> @@ -0,0 +1,754 @@
>>>> +/*
>>>> + * MIPI-DSI based s6e3ha2 AMOLED 5.7 inch panel driver.
>>>> + *
>>>> + * Copyright (c) 2016 Samsung Electronics Co., Ltd.
>>>> + * Donghwa Lee <dh09.lee@samsung.com>
>>>> + * Hyungwon Hwang <human.hwang@samsung.com>
>>>> + * Hoegeun Kwon <hoegeun.kwon@samsung.com>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify
>>>> + * it under the terms of the GNU General Public License version 2 as
>>>> + * published by the Free Software Foundation.
>>>> + */
>>>> +
>>>> +#include <drm/drmP.h>
>>>> +#include <drm/drm_mipi_dsi.h>
>>>> +#include <drm/drm_panel.h>
>>>> +#include <linux/backlight.h>
>>>> +#include <linux/gpio/consumer.h>
>>>> +#include <linux/regulator/consumer.h>
>>>> +
>>>> +#define S6E3HA2_MIN_BRIGHTNESS        0
>>>> +#define S6E3HA2_MAX_BRIGHTNESS        100
>>>> +#define S6E3HA2_DEFAULT_BRIGHTNESS    80
>>>> +
>>>> +#define S6E3HA2_NUM_GAMMA_STEPS        46
>>>> +#define S6E3HA2_GAMMA_CMD_CNT        35
>>>> +#define S6E3HA2_VINT_STATUS_MAX        10
>>>> +
>>>> +static const u8 gamma_tbl[S6E3HA2_NUM_GAMMA_STEPS][S6E3HA2_GAMMA_CMD_CNT] = {
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x82, 0x83,
>>>> +      0x85, 0x88, 0x8b, 0x8b, 0x84, 0x88, 0x82, 0x82, 0x89, 0x86, 0x8c,
>>>> +      0x94, 0x84, 0xb1, 0xaf, 0x8e, 0xcf, 0xad, 0xc9, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x84, 0x84,
>>>> +      0x85, 0x87, 0x8b, 0x8a, 0x84, 0x88, 0x82, 0x82, 0x89, 0x86, 0x8a,
>>>> +      0x93, 0x84, 0xb0, 0xae, 0x8e, 0xc9, 0xa8, 0xc5, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
>>>> +      0x85, 0x86, 0x8a, 0x8a, 0x84, 0x88, 0x81, 0x84, 0x8a, 0x88, 0x8a,
>>>> +      0x91, 0x84, 0xb1, 0xae, 0x8b, 0xd5, 0xb2, 0xcc, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
>>>> +      0x85, 0x86, 0x8a, 0x8a, 0x84, 0x87, 0x81, 0x84, 0x8a, 0x87, 0x8a,
>>>> +      0x91, 0x85, 0xae, 0xac, 0x8a, 0xc3, 0xa3, 0xc0, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x85, 0x85,
>>>> +      0x86, 0x85, 0x88, 0x89, 0x84, 0x89, 0x82, 0x84, 0x87, 0x85, 0x8b,
>>>> +      0x91, 0x88, 0xad, 0xab, 0x8a, 0xb7, 0x9b, 0xb6, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
>>>> +      0x85, 0x86, 0x89, 0x8a, 0x84, 0x89, 0x83, 0x83, 0x86, 0x84, 0x8b,
>>>> +      0x90, 0x84, 0xb0, 0xae, 0x8b, 0xce, 0xad, 0xc8, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
>>>> +      0x85, 0x87, 0x89, 0x8a, 0x83, 0x87, 0x82, 0x85, 0x88, 0x87, 0x89,
>>>> +      0x8f, 0x84, 0xac, 0xaa, 0x89, 0xb1, 0x98, 0xaf, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
>>>> +      0x85, 0x86, 0x88, 0x89, 0x84, 0x88, 0x83, 0x82, 0x85, 0x84, 0x8c,
>>>> +      0x91, 0x86, 0xac, 0xaa, 0x89, 0xc2, 0xa5, 0xbd, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>>>> +      0x85, 0x87, 0x89, 0x8a, 0x83, 0x87, 0x82, 0x85, 0x88, 0x87, 0x88,
>>>> +      0x8b, 0x82, 0xad, 0xaa, 0x8a, 0xc2, 0xa5, 0xbd, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
>>>> +      0x85, 0x86, 0x87, 0x89, 0x84, 0x88, 0x83, 0x82, 0x85, 0x84, 0x8a,
>>>> +      0x8e, 0x84, 0xae, 0xac, 0x89, 0xda, 0xb7, 0xd0, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>>>> +      0x85, 0x86, 0x87, 0x89, 0x84, 0x88, 0x83, 0x80, 0x83, 0x82, 0x8b,
>>>> +      0x8e, 0x85, 0xac, 0xaa, 0x89, 0xc8, 0xaa, 0xc1, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>>>> +      0x85, 0x86, 0x87, 0x89, 0x81, 0x85, 0x81, 0x84, 0x86, 0x84, 0x8c,
>>>> +      0x8c, 0x84, 0xa9, 0xa8, 0x87, 0xa3, 0x92, 0xa1, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>>>> +      0x85, 0x86, 0x87, 0x89, 0x84, 0x86, 0x83, 0x80, 0x83, 0x81, 0x8c,
>>>> +      0x8d, 0x84, 0xaa, 0xaa, 0x89, 0xce, 0xaf, 0xc5, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>>>> +      0x85, 0x86, 0x87, 0x89, 0x81, 0x83, 0x80, 0x83, 0x85, 0x85, 0x8c,
>>>> +      0x8c, 0x84, 0xa8, 0xa8, 0x88, 0xb5, 0x9f, 0xb0, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>>>> +      0x86, 0x86, 0x87, 0x88, 0x81, 0x83, 0x80, 0x83, 0x85, 0x85, 0x8c,
>>>> +      0x8b, 0x84, 0xab, 0xa8, 0x86, 0xd4, 0xb4, 0xc9, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>>>> +      0x86, 0x86, 0x87, 0x88, 0x81, 0x83, 0x80, 0x84, 0x84, 0x85, 0x8b,
>>>> +      0x8a, 0x83, 0xa6, 0xa5, 0x84, 0xbb, 0xa4, 0xb3, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
>>>> +      0x86, 0x85, 0x86, 0x86, 0x82, 0x85, 0x81, 0x82, 0x83, 0x84, 0x8e,
>>>> +      0x8b, 0x83, 0xa4, 0xa3, 0x8a, 0xa1, 0x93, 0x9d, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
>>>> +      0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x82, 0x82, 0x84, 0x8e,
>>>> +      0x8b, 0x83, 0xa4, 0xa2, 0x86, 0xc1, 0xa9, 0xb7, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
>>>> +      0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x82, 0x82, 0x84, 0x8d,
>>>> +      0x89, 0x82, 0xa2, 0xa1, 0x84, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
>>>> +      0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x83, 0x83, 0x85, 0x8c,
>>>> +      0x87, 0x7f, 0xa2, 0x9d, 0x88, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xbb, 0x00, 0xc5, 0x00, 0xb4, 0x87, 0x86, 0x86, 0x84, 0x83,
>>>> +      0x86, 0x87, 0x87, 0x87, 0x80, 0x82, 0x7f, 0x86, 0x86, 0x88, 0x8a,
>>>> +      0x84, 0x7e, 0x9d, 0x9c, 0x82, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xbd, 0x00, 0xc7, 0x00, 0xb7, 0x87, 0x85, 0x85, 0x84, 0x83,
>>>> +      0x86, 0x86, 0x86, 0x88, 0x81, 0x83, 0x80, 0x83, 0x84, 0x85, 0x8a,
>>>> +      0x85, 0x7e, 0x9c, 0x9b, 0x85, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xc0, 0x00, 0xca, 0x00, 0xbb, 0x87, 0x86, 0x85, 0x83, 0x83,
>>>> +      0x85, 0x86, 0x86, 0x88, 0x81, 0x83, 0x80, 0x84, 0x85, 0x86, 0x89,
>>>> +      0x83, 0x7d, 0x9c, 0x99, 0x87, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xc4, 0x00, 0xcd, 0x00, 0xbe, 0x87, 0x86, 0x85, 0x83, 0x83,
>>>> +      0x86, 0x85, 0x85, 0x87, 0x81, 0x82, 0x80, 0x82, 0x82, 0x83, 0x8a,
>>>> +      0x85, 0x7f, 0x9f, 0x9b, 0x86, 0xb4, 0xa1, 0xac, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xc7, 0x00, 0xd0, 0x00, 0xc2, 0x87, 0x85, 0x85, 0x83, 0x82,
>>>> +      0x85, 0x85, 0x85, 0x86, 0x82, 0x83, 0x80, 0x82, 0x82, 0x84, 0x87,
>>>> +      0x86, 0x80, 0x9e, 0x9a, 0x87, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xca, 0x00, 0xd2, 0x00, 0xc5, 0x87, 0x85, 0x84, 0x82, 0x82,
>>>> +      0x84, 0x85, 0x85, 0x86, 0x81, 0x82, 0x7f, 0x82, 0x82, 0x84, 0x88,
>>>> +      0x86, 0x81, 0x9d, 0x98, 0x86, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xce, 0x00, 0xd6, 0x00, 0xca, 0x86, 0x85, 0x84, 0x83, 0x83,
>>>> +      0x85, 0x84, 0x84, 0x85, 0x81, 0x82, 0x80, 0x81, 0x81, 0x82, 0x89,
>>>> +      0x86, 0x81, 0x9c, 0x97, 0x86, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xd1, 0x00, 0xd9, 0x00, 0xce, 0x86, 0x84, 0x83, 0x83, 0x82,
>>>> +      0x85, 0x85, 0x85, 0x86, 0x81, 0x83, 0x81, 0x82, 0x82, 0x83, 0x86,
>>>> +      0x83, 0x7f, 0x99, 0x95, 0x86, 0xbb, 0xa4, 0xb3, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xd4, 0x00, 0xdb, 0x00, 0xd1, 0x86, 0x85, 0x83, 0x83, 0x82,
>>>> +      0x85, 0x84, 0x84, 0x85, 0x80, 0x83, 0x82, 0x80, 0x80, 0x81, 0x87,
>>>> +      0x84, 0x81, 0x98, 0x93, 0x85, 0xae, 0x9c, 0xa8, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xd8, 0x00, 0xde, 0x00, 0xd6, 0x86, 0x84, 0x83, 0x81, 0x81,
>>>> +      0x83, 0x85, 0x85, 0x85, 0x82, 0x83, 0x81, 0x81, 0x81, 0x83, 0x86,
>>>> +      0x84, 0x80, 0x98, 0x91, 0x85, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xdc, 0x00, 0xe2, 0x00, 0xda, 0x85, 0x84, 0x83, 0x82, 0x82,
>>>> +      0x84, 0x84, 0x84, 0x85, 0x81, 0x82, 0x82, 0x80, 0x80, 0x81, 0x83,
>>>> +      0x82, 0x7f, 0x99, 0x93, 0x86, 0x94, 0x8b, 0x92, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xdf, 0x00, 0xe5, 0x00, 0xde, 0x85, 0x84, 0x82, 0x82, 0x82,
>>>> +      0x84, 0x83, 0x83, 0x84, 0x81, 0x81, 0x80, 0x83, 0x82, 0x84, 0x82,
>>>> +      0x81, 0x7f, 0x99, 0x92, 0x86, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
>>>> +      0x82, 0x83, 0x83, 0x84, 0x80, 0x81, 0x80, 0x83, 0x83, 0x84, 0x80,
>>>> +      0x81, 0x7c, 0x99, 0x92, 0x87, 0xa1, 0x93, 0x9d, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x85, 0x84, 0x83, 0x81, 0x81,
>>>> +      0x82, 0x82, 0x82, 0x83, 0x80, 0x81, 0x80, 0x81, 0x80, 0x82, 0x83,
>>>> +      0x82, 0x80, 0x91, 0x8d, 0x83, 0x9a, 0x90, 0x96, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
>>>> +      0x82, 0x83, 0x83, 0x84, 0x80, 0x81, 0x80, 0x81, 0x80, 0x82, 0x83,
>>>> +      0x81, 0x7f, 0x91, 0x8c, 0x82, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
>>>> +      0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x82, 0x82,
>>>> +      0x82, 0x7f, 0x94, 0x89, 0x84, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
>>>> +      0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x82, 0x83,
>>>> +      0x82, 0x7f, 0x91, 0x85, 0x81, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
>>>> +      0x82, 0x83, 0x83, 0x83, 0x80, 0x80, 0x7f, 0x83, 0x82, 0x84, 0x83,
>>>> +      0x82, 0x7f, 0x90, 0x84, 0x81, 0x9a, 0x90, 0x96, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x80, 0x80,
>>>> +      0x82, 0x83, 0x83, 0x83, 0x80, 0x80, 0x7f, 0x80, 0x80, 0x81, 0x81,
>>>> +      0x82, 0x83, 0x7e, 0x80, 0x7c, 0xa4, 0x97, 0x9f, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xe9, 0x00, 0xec, 0x00, 0xe8, 0x84, 0x83, 0x82, 0x81, 0x81,
>>>> +      0x82, 0x82, 0x82, 0x83, 0x7f, 0x7f, 0x7f, 0x81, 0x80, 0x82, 0x83,
>>>> +      0x83, 0x84, 0x79, 0x7c, 0x79, 0xb1, 0xa0, 0xaa, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xed, 0x00, 0xf0, 0x00, 0xec, 0x83, 0x83, 0x82, 0x80, 0x80,
>>>> +      0x81, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7e, 0x81, 0x81, 0x82, 0x80,
>>>> +      0x81, 0x81, 0x84, 0x84, 0x83, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xf1, 0x00, 0xf4, 0x00, 0xf1, 0x83, 0x82, 0x82, 0x80, 0x80,
>>>> +      0x81, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x7d,
>>>> +      0x7e, 0x7f, 0x84, 0x84, 0x83, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xf6, 0x00, 0xf7, 0x00, 0xf5, 0x82, 0x82, 0x81, 0x80, 0x80,
>>>> +      0x80, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x82,
>>>> +      0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 0x00, 0xfa, 0x00, 0xfb, 0x00, 0xfa, 0x81, 0x81, 0x81, 0x80, 0x80,
>>>> +      0x80, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
>>>> +      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
>>>> +      0x00, 0x00 },
>>>> +    { 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,
>>>> +      0x00, 0x00 },
>>>> +    { 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,
>>>> +      0x00, 0x00 }
>>>> +};
>>>> +
>>>> +unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = {
>>>> +    0x18, 0x19, 0x1a, 0x1b, 0x1c,
>>>> +    0x1d, 0x1e, 0x1f, 0x20, 0x21
>>>> +};
>>>> +
>>>> +struct s6e3ha2 {
>>>> +    struct device *dev;
>>>> +    struct drm_panel panel;
>>>> +    struct backlight_device *bl_dev;
>>>> +
>>>> +    struct regulator_bulk_data supplies[2];
>>>> +    struct gpio_desc *reset_gpio;
>>>> +    struct gpio_desc *enable_gpio;
>>>> +
>>>> +    /* This field is tested by functions directly accessing DSI bus before
>>>> +     * transfer, transfer is skipped if it is set. In case of transfer
>>>> +     * failure or unexpected response the field is set to error value.
>>>> +     * Such construct allows to eliminate many checks in higher level
>>>> +     * functions.
>>>> +     */
>>>> +    int error;
>>>> +};
>>>> +
>>>> +static int  s6e3ha2_clear_error(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    int ret = ctx->error;
>>>> +
>>>> +    ctx->error = 0;
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static void s6e3ha2_dcs_write(struct s6e3ha2 *ctx, const void *data, size_t len)
>>>> +{
>>>> +    struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
>>>> +    ssize_t ret;
>>>> +
>>>> +    if (ctx->error < 0)
>>>> +        return;
>>>> +
>>>> +    ret = mipi_dsi_dcs_write_buffer(dsi, data, len);
>>>> +    if (ret < 0) {
>>>> +        dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n",
>>>> +                        ret, (int)len, data);
>>>> +        ctx->error = ret;
>>>> +    }
>>>> +}
>>>> +
>>>> +#define s6e3ha2_dcs_write_seq_static(ctx, seq...) do {    \
>>>> +    static const u8 d[] = { seq };            \
>>>> +    s6e3ha2_dcs_write(ctx, d, ARRAY_SIZE(d));    \
>>>> +} while (0)
>>>> +
>>>> +static void s6e3ha2_test_key_on_f0(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf0, 0x5a, 0x5a);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_test_key_off_f0(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf0, 0xa5, 0xa5);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_test_key_on_fc(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfc, 0x5a, 0x5a);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_test_key_off_fc(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfc, 0xa5, 0xa5);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_single_dsi_set(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf2, 0x67);
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf9, 0x09);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_freq_calibration(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfd, 0x1c);
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20, 0x39);
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0xa0);
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20);
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x12, 0x62,
>>>> +        0x40, 0x80, 0xc0, 0x28, 0x28, 0x28, 0x28, 0x39, 0xc5);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_aor_control(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb2, 0x03, 0x10);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_caps_elvss_set(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb6, 0x9c, 0x0a);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_acl_off(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0x55, 0x00);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_acl_off_opr(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb5, 0x40);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_test_global(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb0, 0x07);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_test(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb8, 0x19);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_touch_hsync_on1(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx,
>>>> +            0xbd, 0x33, 0x11, 0x02, 0x16, 0x02, 0x16);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_pentile_control(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xc0, 0x00, 0x00, 0xd8, 0xd8);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_poc_global(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb0, 0x20);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_poc_setting(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x08);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_pcd_set_off(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xcc, 0x40, 0x51);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_err_fg_set(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xed, 0x44);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_hbm_off(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0x53, 0x00);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_te_start_setting(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xb9, 0x10, 0x09, 0xff, 0x00, 0x09);
>>>> +}
>>>> +
>>>> +static void s6e3ha2_gamma_update(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf7, 0x03);
>>>> +    ndelay(100); /* need for 100ns delay */
>>>> +    s6e3ha2_dcs_write_seq_static(ctx, 0xf7, 0x00);
>>>> +}
>>>> +
>>>> +static int s6e3ha2_get_brightness(struct backlight_device *bl_dev)
>>>> +{
>>>> +    return bl_dev->props.brightness;
>>>> +}
>>>> +
>>>> +static void s6e3ha2_set_vint(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    struct backlight_device *bl_dev = ctx->bl_dev;
>>>> +    unsigned int brightness = bl_dev->props.brightness;
>>>> +    unsigned char data[] = { 0xf4, 0x8b,
>>>> +            vint_table[brightness * (S6E3HA2_VINT_STATUS_MAX - 1) /
>>>> +            S6E3HA2_MAX_BRIGHTNESS] };
>>>> +
>>>> +    s6e3ha2_dcs_write(ctx, data, ARRAY_SIZE(data));
>>>> +}
>>>> +
>>>> +static unsigned int s6e3ha2_get_brightness_index(unsigned int brightness)
>>>> +{
>>>> +    return (brightness * (S6E3HA2_NUM_GAMMA_STEPS - 1)) /
>>>> +        S6E3HA2_MAX_BRIGHTNESS;
>>>> +}
>>>> +
>>>> +static int s6e3ha2_update_gamma(struct s6e3ha2 *ctx, unsigned int brightness)
>>>> +{
>>>> +    struct backlight_device *bl_dev = ctx->bl_dev;
>>>> +    unsigned int index = s6e3ha2_get_brightness_index(brightness);
>>>> +    u8 data[S6E3HA2_GAMMA_CMD_CNT + 1] = { 0xca, };
>>>> +
>>>> +    memcpy(data + 1, gamma_tbl + index, S6E3HA2_GAMMA_CMD_CNT);
>>>> +    s6e3ha2_dcs_write(ctx, data, ARRAY_SIZE(data));
>>>> +
>>>> +    s6e3ha2_gamma_update(ctx);
>>>> +    bl_dev->props.brightness = brightness;
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int s6e3ha2_set_brightness(struct backlight_device *bl_dev)
>>>> +{
>>>> +    struct s6e3ha2 *ctx = bl_get_data(bl_dev);
>>>> +    unsigned int brightness = bl_dev->props.brightness;
>>>> +
>>>> +    if (brightness < S6E3HA2_MIN_BRIGHTNESS ||
>>>> +        brightness > bl_dev->props.max_brightness) {
>>>> +        dev_err(ctx->dev, "Invalid brightness: %u\n", brightness);
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    if (bl_dev->props.power > FB_BLANK_NORMAL)
>>>> +        return -EPERM;
>>>> +
>>>> +    s6e3ha2_test_key_on_f0(ctx);
>>>> +    s6e3ha2_update_gamma(ctx, brightness);
>>>> +    s6e3ha2_aor_control(ctx);
>>>> +    s6e3ha2_set_vint(ctx);
>>>> +    s6e3ha2_test_key_off_f0(ctx);
>>>> +
>>>> +    return ctx->error;
>>>> +}
>>>> +
>>>> +static const struct backlight_ops s6e3ha2_bl_ops = {
>>>> +    .get_brightness = s6e3ha2_get_brightness,
>>>> +    .update_status = s6e3ha2_set_brightness,
>>>> +};
>>>> +
>>>> +static int s6e3ha2_panel_init(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
>>>> +    int ret;
>>>> +
>>>> +    ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +    usleep_range(5000, 6000);
>>>> +
>>>> +    s6e3ha2_test_key_on_f0(ctx);
>>>> +    s6e3ha2_single_dsi_set(ctx);
>>>> +    s6e3ha2_test_key_on_fc(ctx);
>>>> +    s6e3ha2_freq_calibration(ctx);
>>>> +    s6e3ha2_test_key_off_fc(ctx);
>>>> +    s6e3ha2_test_key_off_f0(ctx);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int s6e3ha2_power_off(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
>>>> +}
>>>> +
>>>> +static int s6e3ha2_disable(struct drm_panel *panel)
>>>> +{
>>>> +    struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
>>>> +    struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
>>>> +    int ret;
>>>> +
>>>> +    ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    ret = mipi_dsi_dcs_set_display_off(dsi);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    msleep(40);
>>>> +    ctx->bl_dev->props.power = FB_BLANK_NORMAL;
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int s6e3ha2_unprepare(struct drm_panel *panel)
>>>> +{
>>>> +    struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
>>>> +    int ret;
>>>> +
>>>> +    ret = s6e3ha2_clear_error(ctx);
>>>> +    if (!ret)
>>>> +        ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
>>>> +
>>>> +    return s6e3ha2_power_off(ctx);
>>>> +}
>>>> +
>>>> +static int s6e3ha2_power_on(struct s6e3ha2 *ctx)
>>>> +{
>>>> +    int ret;
>>>> +
>>>> +    ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    msleep(120);
>>>> +
>>>> +    gpiod_set_value(ctx->enable_gpio, 0);
>>>> +    usleep_range(5000, 6000);
>>>> +    gpiod_set_value(ctx->enable_gpio, 1);
>>>> +
>>>> +    gpiod_set_value(ctx->reset_gpio, 1);
>>>> +    usleep_range(5000, 6000);
>>>> +    gpiod_set_value(ctx->reset_gpio, 0);
>>>> +    usleep_range(5000, 6000);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +static int s6e3ha2_prepare(struct drm_panel *panel)
>>>> +{
>>>> +    struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
>>>> +    int ret;
>>>> +
>>>> +    ret = s6e3ha2_power_on(ctx);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    ret = s6e3ha2_panel_init(ctx);
>>>> +    if (ret < 0)
>>>> +        goto err;
>>>> +
>>>> +    ret = s6e3ha2_clear_error(ctx);
>>>> +    if (ret < 0)
>>>> +        goto err;
>>>> +
>>>> +    ctx->bl_dev->props.power = FB_BLANK_NORMAL;
>>>> +
>>>> +    return 0;
>>>> +
>>>> +err:
>>>> +    s6e3ha2_power_off(ctx);
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static int s6e3ha2_enable(struct drm_panel *panel)
>>>> +{
>>>> +    struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
>>>> +    struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
>>>> +    int ret;
>>>> +
>>>> +    /* common setting */
>>>> +    ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    s6e3ha2_test_key_on_f0(ctx);
>>>> +    s6e3ha2_test_key_on_fc(ctx);
>>>> +    s6e3ha2_touch_hsync_on1(ctx);
>>>> +    s6e3ha2_pentile_control(ctx);
>>>> +    s6e3ha2_poc_global(ctx);
>>>> +    s6e3ha2_poc_setting(ctx);
>>>> +    s6e3ha2_test_key_off_fc(ctx);
>>>> +
>>>> +    /* pcd setting off for TB */
>>>> +    s6e3ha2_pcd_set_off(ctx);
>>>> +    s6e3ha2_err_fg_set(ctx);
>>>> +    s6e3ha2_te_start_setting(ctx);
>>>> +
>>>> +    /* brightness setting */
>>>> +    s6e3ha2_set_brightness(ctx->bl_dev);
>>>> +    s6e3ha2_aor_control(ctx);
>>>> +    s6e3ha2_caps_elvss_set(ctx);
>>>> +    s6e3ha2_gamma_update(ctx);
>>>> +    s6e3ha2_acl_off(ctx);
>>>> +    s6e3ha2_acl_off_opr(ctx);
>>>> +    s6e3ha2_hbm_off(ctx);
>>>> +
>>>> +    /* elvss temp compensation */
>>>> +    s6e3ha2_test_global(ctx);
>>>> +    s6e3ha2_test(ctx);
>>>> +    s6e3ha2_test_key_off_f0(ctx);
>>>> +
>>>> +    if (ctx->error != 0)
>>>> +        return ctx->error;
>>>> +
>>>> +    ret = mipi_dsi_dcs_set_display_on(dsi);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static const struct drm_display_mode default_mode = {
>>>> +    .clock = 222372,
>>>> +    .hdisplay = 1440,
>>>> +    .hsync_start = 1440 + 1,
>>>> +    .hsync_end = 1440 + 1 + 1,
>>>> +    .htotal = 1440 + 1 + 1 + 1,
>>>> +    .vdisplay = 2560,
>>>> +    .vsync_start = 2560 + 1,
>>>> +    .vsync_end = 2560 + 1 + 1,
>>>> +    .vtotal = 2560 + 1 + 1 + 15,
>>>> +    .vrefresh = 60,
>>>> +    .flags = 0,
>>>> +};
>>>> +
>>>> +static int s6e3ha2_get_modes(struct drm_panel *panel)
>>>> +{
>>>> +    struct drm_connector *connector = panel->connector;
>>>> +    struct drm_display_mode *mode;
>>>> +
>>>> +    mode = drm_mode_duplicate(panel->drm, &default_mode);
>>>> +    if (!mode) {
>>>> +        DRM_ERROR("failed to add mode %ux%ux@%u\n",
>>>> +                default_mode.hdisplay, default_mode.vdisplay,
>>>> +                default_mode.vrefresh);
>>>> +        return -ENOMEM;
>>>> +    }
>>>> +
>>>> +    drm_mode_set_name(mode);
>>>> +
>>>> +    mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
>>>> +    drm_mode_probed_add(connector, mode);
>>>> +
>>>> +    connector->display_info.width_mm = 71;
>>>> +    connector->display_info.height_mm = 125;
>>>> +
>>>> +    return 1;
>>>> +}
>>>> +
>>>> +static const struct drm_panel_funcs s6e3ha2_drm_funcs = {
>>>> +    .disable = s6e3ha2_disable,
>>>> +    .unprepare = s6e3ha2_unprepare,
>>>> +    .prepare = s6e3ha2_prepare,
>>>> +    .enable = s6e3ha2_enable,
>>>> +    .get_modes = s6e3ha2_get_modes,
>>>> +};
>>>> +
>>>> +static int s6e3ha2_probe(struct mipi_dsi_device *dsi)
>>>> +{
>>>> +    struct device *dev = &dsi->dev;
>>>> +    struct s6e3ha2 *ctx;
>>>> +    int ret;
>>>> +
>>>> +    ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
>>>> +    if (!ctx)
>>>> +        return -ENOMEM;
>>>> +
>>>> +    mipi_dsi_set_drvdata(dsi, ctx);
>>>> +
>>>> +    ctx->dev = dev;
>>>> +
>>>> +    dsi->lanes = 4;
>>>> +    dsi->format = MIPI_DSI_FMT_RGB888;
>>>> +    dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
>>>> +
>>>> +    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) {
>>>> +        dev_err(dev, "failed to get regulators: %d\n", ret);
>>>> +        return ret;
>>>> +    }
>>>> +
>>>> +    ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
>>>> +    if (IS_ERR(ctx->reset_gpio)) {
>>>> +        dev_err(dev, "cannot get reset-gpios %ld\n",
>>>> +            PTR_ERR(ctx->reset_gpio));
>>>> +        return PTR_ERR(ctx->reset_gpio);
>>>> +    }
>>>> +
>>>> +    ctx->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
>>>> +    if (IS_ERR(ctx->enable_gpio)) {
>>>> +        dev_err(dev, "cannot get enable-gpios %ld\n",
>>>> +            PTR_ERR(ctx->enable_gpio));
>>>> +        return PTR_ERR(ctx->enable_gpio);
>>>> +    }
>>>> +
>>>> +    ctx->bl_dev = backlight_device_register("s6e3ha2", dev, ctx,
>>>> +                        &s6e3ha2_bl_ops, NULL);
>>>> +    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 = S6E3HA2_MAX_BRIGHTNESS;
>>>> +    ctx->bl_dev->props.brightness = S6E3HA2_DEFAULT_BRIGHTNESS;
>>>> +    ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
>>>> +
>>>> +    drm_panel_init(&ctx->panel);
>>>> +    ctx->panel.dev = dev;
>>>> +    ctx->panel.funcs = &s6e3ha2_drm_funcs;
>>>> +
>>>> +    ret = drm_panel_add(&ctx->panel);
>>>> +    if (ret < 0)
>>>> +        goto unregister_backlight;
>>>> +
>>>> +    ret = mipi_dsi_attach(dsi);
>>>> +    if (ret < 0)
>>>> +        goto remove_panel;
>>>> +
>>>> +    return ret;
>>>> +
>>>> +remove_panel:
>>>> +    drm_panel_remove(&ctx->panel);
>>>> +
>>>> +unregister_backlight:
>>>> +    backlight_device_unregister(ctx->bl_dev);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static int s6e3ha2_remove(struct mipi_dsi_device *dsi)
>>>> +{
>>>> +    struct s6e3ha2 *ctx = mipi_dsi_get_drvdata(dsi);
>>>> +
>>>> +    mipi_dsi_detach(dsi);
>>>> +    drm_panel_remove(&ctx->panel);
>>>> +    backlight_device_unregister(ctx->bl_dev);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static const struct of_device_id s6e3ha2_of_match[] = {
>>>> +    { .compatible = "samsung,s6e3ha2" },
>>>> +    { }
>>>> +};
>>>> +MODULE_DEVICE_TABLE(of, s6e3ha2_of_match);
>>>> +
>>>> +static struct mipi_dsi_driver s6e3ha2_driver = {
>>>> +    .probe = s6e3ha2_probe,
>>>> +    .remove = s6e3ha2_remove,
>>>> +    .driver = {
>>>> +        .name = "panel-samsung-s6e3ha2",
>>>> +        .of_match_table = s6e3ha2_of_match,
>>>> +    },
>>>> +};
>>>> +module_mipi_dsi_driver(s6e3ha2_driver);
>>>> +
>>>> +MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
>>>> +MODULE_AUTHOR("Hyungwon Hwang <human.hwang@samsung.com>");
>>>> +MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
>>>> +MODULE_DESCRIPTION("MIPI-DSI based s6e3ha2 AMOLED Panel Driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>
>>> -- 
>>> To unsubscribe from this list: send the line "unsubscribe devicetree" in
>>> the body of a message to majordomo@vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>
>>>
Inki Dae Jan. 31, 2017, 9:34 a.m. UTC | #6
2017년 01월 31일 18:22에 Krzysztof Kozlowski 이(가) 쓴 글:
> On Tue, Jan 31, 2017 at 2:01 AM, Inki Dae <inki.dae@samsung.com> wrote:
>>
>>
>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>> Dear Thierry,
>>>
>>> Could you please review this patch?
>>
>> Thierry, I think this patch has been reviewed enough but no comment from you. Seems you are busy. I will pick up this.
>>
> 
> Comments from v8 were not resolved and I think we are waiting for v9:
> https://lkml.org/lkml/2017/1/11/178
> 
> If that is not correct then please clarify.

Seems you pointed to change te-gpios bindings to optional. right?

I thought Rob left ack so it's no problem.
https://lkml.org/lkml/2017/1/13/626

Thanks,
Inki Dae

> 
> Best regards,
> Krzysztof
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
>
Krzysztof Kozlowski Jan. 31, 2017, 10:01 a.m. UTC | #7
On Tue, Jan 31, 2017 at 11:34 AM, Inki Dae <inki.dae@samsung.com> wrote:
>
>
> 2017년 01월 31일 18:22에 Krzysztof Kozlowski 이(가) 쓴 글:
>> On Tue, Jan 31, 2017 at 2:01 AM, Inki Dae <inki.dae@samsung.com> wrote:
>>>
>>>
>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>>> Dear Thierry,
>>>>
>>>> Could you please review this patch?
>>>
>>> Thierry, I think this patch has been reviewed enough but no comment from you. Seems you are busy. I will pick up this.
>>>
>>
>> Comments from v8 were not resolved and I think we are waiting for v9:
>> https://lkml.org/lkml/2017/1/11/178
>>
>> If that is not correct then please clarify.
>
> Seems you pointed to change te-gpios bindings to optional. right?
>
> I thought Rob left ack so it's no problem.
> https://lkml.org/lkml/2017/1/13/626

Yes, change them to optional. I think it is a problem (regardless of
Rob's ack) because you are merging a driver requiring a property which
soon you want to remove. If you merge it (like this) removal of
te-gpios property will be breakage of ABI. This is not a serious
problem but knowing such plan of te-gpios removal upfront, it would be
wrong to commit such driver.

Best regards,
Krzysztof
Inki Dae Jan. 31, 2017, 10:37 a.m. UTC | #8
2017-01-31 19:01 GMT+09:00 Krzysztof Kozlowski <krzk@kernel.org>:
> On Tue, Jan 31, 2017 at 11:34 AM, Inki Dae <inki.dae@samsung.com> wrote:
>>
>>
>> 2017년 01월 31일 18:22에 Krzysztof Kozlowski 이(가) 쓴 글:
>>> On Tue, Jan 31, 2017 at 2:01 AM, Inki Dae <inki.dae@samsung.com> wrote:
>>>>
>>>>
>>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>>>> Dear Thierry,
>>>>>
>>>>> Could you please review this patch?
>>>>
>>>> Thierry, I think this patch has been reviewed enough but no comment from you. Seems you are busy. I will pick up this.
>>>>
>>>
>>> Comments from v8 were not resolved and I think we are waiting for v9:
>>> https://lkml.org/lkml/2017/1/11/178
>>>
>>> If that is not correct then please clarify.
>>
>> Seems you pointed to change te-gpios bindings to optional. right?
>>
>> I thought Rob left ack so it's no problem.
>> https://lkml.org/lkml/2017/1/13/626
>
> Yes, change them to optional. I think it is a problem (regardless of
> Rob's ack) because you are merging a driver requiring a property which
> soon you want to remove. If you merge it (like this) removal of
> te-gpios property will be breakage of ABI. This is not a serious
> problem but knowing such plan of te-gpios removal upfront, it would be
> wrong to commit such driver.

This is a trivial thing so it doesn't make breakage of ABI because the
property, te-gpios, is *optional*.

Anyway, this wouldn't be *the things* Thierry mentioned.

Thanks,
Inki Dae

>
> Best regards,
> Krzysztof
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
Krzysztof Kozlowski Jan. 31, 2017, 11:30 a.m. UTC | #9
On Tue, Jan 31, 2017 at 12:37 PM, Inki Dae <daeinki@gmail.com> wrote:
> 2017-01-31 19:01 GMT+09:00 Krzysztof Kozlowski <krzk@kernel.org>:
>> On Tue, Jan 31, 2017 at 11:34 AM, Inki Dae <inki.dae@samsung.com> wrote:
>>>
>>>
>>> 2017년 01월 31일 18:22에 Krzysztof Kozlowski 이(가) 쓴 글:
>>>> On Tue, Jan 31, 2017 at 2:01 AM, Inki Dae <inki.dae@samsung.com> wrote:
>>>>>
>>>>>
>>>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>>>>> Dear Thierry,
>>>>>>
>>>>>> Could you please review this patch?
>>>>>
>>>>> Thierry, I think this patch has been reviewed enough but no comment from you. Seems you are busy. I will pick up this.
>>>>>
>>>>
>>>> Comments from v8 were not resolved and I think we are waiting for v9:
>>>> https://lkml.org/lkml/2017/1/11/178
>>>>
>>>> If that is not correct then please clarify.
>>>
>>> Seems you pointed to change te-gpios bindings to optional. right?
>>>
>>> I thought Rob left ack so it's no problem.
>>> https://lkml.org/lkml/2017/1/13/626
>>
>> Yes, change them to optional. I think it is a problem (regardless of
>> Rob's ack) because you are merging a driver requiring a property which
>> soon you want to remove. If you merge it (like this) removal of
>> te-gpios property will be breakage of ABI. This is not a serious
>> problem but knowing such plan of te-gpios removal upfront, it would be
>> wrong to commit such driver.
>
> This is a trivial thing so it doesn't make breakage of ABI because the
> property, te-gpios, is *optional*.

So why bindings document is not changed?

+Required properties:
(...)
+  - te-gpios: a GPIO spec for the tearing effect synchronization signal
+    gpio pin (active high)

Andrzej pointed this out and it is not fixed since then. What is the
problem with fixing the bindings documentation?

> Anyway, this wouldn't be *the things* Thierry mentioned.

Probably not, I did not respond to Thierry's feedback.

Best regards,
Krzysztof
Andrzej Hajda Jan. 31, 2017, 12:05 p.m. UTC | #10
On 31.01.2017 09:54, Thierry Reding wrote:
> On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
>>
>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>> Dear Thierry,
>>>
>>> Could you please review this patch?
>> Thierry, I think this patch has been reviewed enough but no comment
>> from you. Seems you are busy. I will pick up this.
> Sorry, but that's not how it works. This patch has gone through 8
> revisions within 4 weeks, and I tend to ignore patches like that until
> the dust settles.
>
> Other than that, this continues the same madness that I've repeatedly
> complained about in the past. The whole mechanism of running through a
> series of writes and not caring about errors until the very end is
> something we've discussed at length in the past. 

Yes, we have discussed the pattern, but without any conclusion. The
pattern is correct, used in different places in kernel (see below for
examples) and significantly decreases source code size. Disallowing it
in panels subsystem just because of personal preferences of the
maintainer does not seem to be proper.

> It also in large parts
> duplicates a bunch of functions from other Samsung panel drivers that I
> already said should eventually be moved to something saner.

Currently there are two Samsung panel drivers, one is on SPI bus,
another one is on DSI.
I am (co-)author of both drivers, they have some similarities but I did
not see any gain in making additional abstractions over transport layer
just to make one or two small functions common.
Could you be more precise what are you talking about, or could you give
a link to the mail where you said it. Anything I remember was a
discussion about ugly magic initialization sequences due to poor
documentation.

And below promised examples of the error pattern, it was time consuming
to find them, so please at least read them :)

Almost exactly the same patterns for the same purpose:

1. http://lxr.free-electrons.com/source/drivers/net/ieee802154/atusb.c#L63
    Citation from the code:
    To reduce the number of error checks in the code, we record the
first error
    in atusb->err and reject all subsequent requests until the error is
cleared.

2. http://lxr.free-electrons.com/source/drivers/md/dm-thin.c?v=4.4#L917
3. http://lxr.free-electrons.com/source/net/9p/trans_fd.c?v=3.18#L297
4.
http://lxr.free-electrons.com/source/drivers/media/v4l2-core/v4l2-ctrls.c#L2234
5. http://lxr.free-electrons.com/source/drivers/media/i2c/s5k5baf.c#L451
    This is my driver, I mention it here just to show it was not a
problem to merge it to media subsystem.

Similar patterns:

6. http://lxr.free-electrons.com/source/fs/seq_file.c#L398
    Do not process object if buffer is full, it allows to do not check
buffer size after every write, for example:
>     seq_printf(m, " hardirq-safe locks:            %11lu\n",
>             nr_hardirq_safe);
>     seq_printf(m, " hardirq-unsafe locks:          %11lu\n",
>             nr_hardirq_unsafe);
>     seq_printf(m, " softirq-safe locks:            %11lu\n",
>             nr_softirq_safe);
>     seq_printf(m, " softirq-unsafe locks:          %11lu\n",
>             nr_softirq_unsafe);
>     seq_printf(m, " irq-safe locks:                %11lu\n",
>             nr_irq_safe);
>     seq_printf(m, " irq-unsafe locks:              %11lu\n",
>             nr_irq_unsafe);
>
>     seq_printf(m, " hardirq-read-safe locks:       %11lu\n",
>             nr_hardirq_read_safe);
>     seq_printf(m, " hardirq-read-unsafe locks:     %11lu\n",
>             nr_hardirq_read_unsafe);
>     seq_printf(m, " softirq-read-safe locks:       %11lu\n",
>             nr_softirq_read_safe);
>     seq_printf(m, " softirq-read-unsafe locks:     %11lu\n",
>             nr_softirq_read_unsafe);
>     seq_printf(m, " irq-read-safe locks:           %11lu\n",
>             nr_irq_read_safe);
>     seq_printf(m, " irq-read-unsafe locks:         %11lu\n",
>             nr_irq_read_unsafe);
>
>     seq_printf(m, " uncategorized locks:           %11lu\n",
>             nr_uncategorized);
>     seq_printf(m, " unused locks:                  %11lu\n",
>             nr_unused);
>     seq_printf(m, " max locking depth:             %11u\n",
>             max_lockdep_depth);

Now try to imagine how it would look like if you add error checking
after each call.

7. http://lxr.free-electrons.com/source/lib/devres.c#L129
    Postponed error check:
> */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*
> /*b*/*/ase = devm_ioremap_resource(&pdev->dev, res);/*
> */if (IS_ERR(base))/*
> /**/*/return PTR_ERR(base);/*


Regards
Andrzej
Inki Dae Jan. 31, 2017, 12:58 p.m. UTC | #11
2017-01-31 20:30 GMT+09:00 Krzysztof Kozlowski <krzk@kernel.org>:
> On Tue, Jan 31, 2017 at 12:37 PM, Inki Dae <daeinki@gmail.com> wrote:
>> 2017-01-31 19:01 GMT+09:00 Krzysztof Kozlowski <krzk@kernel.org>:
>>> On Tue, Jan 31, 2017 at 11:34 AM, Inki Dae <inki.dae@samsung.com> wrote:
>>>>
>>>>
>>>> 2017년 01월 31일 18:22에 Krzysztof Kozlowski 이(가) 쓴 글:
>>>>> On Tue, Jan 31, 2017 at 2:01 AM, Inki Dae <inki.dae@samsung.com> wrote:
>>>>>>
>>>>>>
>>>>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>>>>>> Dear Thierry,
>>>>>>>
>>>>>>> Could you please review this patch?
>>>>>>
>>>>>> Thierry, I think this patch has been reviewed enough but no comment from you. Seems you are busy. I will pick up this.
>>>>>>
>>>>>
>>>>> Comments from v8 were not resolved and I think we are waiting for v9:
>>>>> https://lkml.org/lkml/2017/1/11/178
>>>>>
>>>>> If that is not correct then please clarify.
>>>>
>>>> Seems you pointed to change te-gpios bindings to optional. right?
>>>>
>>>> I thought Rob left ack so it's no problem.
>>>> https://lkml.org/lkml/2017/1/13/626
>>>
>>> Yes, change them to optional. I think it is a problem (regardless of
>>> Rob's ack) because you are merging a driver requiring a property which
>>> soon you want to remove. If you merge it (like this) removal of
>>> te-gpios property will be breakage of ABI. This is not a serious
>>> problem but knowing such plan of te-gpios removal upfront, it would be
>>> wrong to commit such driver.
>>
>> This is a trivial thing so it doesn't make breakage of ABI because the
>> property, te-gpios, is *optional*.
>
> So why bindings document is not changed?

Seems Hoegeun missed this.
I also think it should specify *te-gpios is optional property* like
mipi-dsi binding document did but this doesn't breakage of ABI you
mentioned. And I'd like to say we should feel ABI breakage is a
serious problem.

Sad to say, Exynos is already broken like Marek declared. This is
really a serious problem Exynos maintainers have to fix including you
and me.

Thanks,
Inki Dae

>
> +Required properties:
> (...)
> +  - te-gpios: a GPIO spec for the tearing effect synchronization signal
> +    gpio pin (active high)
>
> Andrzej pointed this out and it is not fixed since then. What is the
> problem with fixing the bindings documentation?
>
>> Anyway, this wouldn't be *the things* Thierry mentioned.
>
> Probably not, I did not respond to Thierry's feedback.
>
> Best regards,
> Krzysztof
Sean Paul Jan. 31, 2017, 2:38 p.m. UTC | #12
On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
> On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
> > 
> > 
> > 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
> > > Dear Thierry,
> > > 
> > > Could you please review this patch?
> > 
> > Thierry, I think this patch has been reviewed enough but no comment
> > from you. Seems you are busy. I will pick up this.
> 
> Sorry, but that's not how it works. This patch has gone through 8
> revisions within 4 weeks, and I tend to ignore patches like that until
> the dust settles.
> 

Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
and picked up on 1/31. I don't think it's unreasonable to take it through
another tree after that.

I wonder if drm_panel would benefit from the -misc group maintainership model
as drm_bridge does. By spreading out the workload, the high-maintenance
patches would hopefully find someone to shepherd them through.

> Other than that, this continues the same madness that I've repeatedly
> complained about in the past. The whole mechanism of running through a
> series of writes and not caring about errors until the very end is
> something we've discussed at length in the past. It also in large parts
> duplicates a bunch of functions from other Samsung panel drivers that I
> already said should eventually be moved to something saner.
> 

FWIW, this type of error handling isn't my preference either. If we must defer,
I'd rather not keep it in ctx, but rather pass around an argument so it's more
obvious we need to deal with it in the return. That said, this seems like
a case of letting the perfect be the enemy of the good, surely something is
better than nothing?

Sean



> This is now the third driver and nobody's giving a damn about improving
> things.
> 
> Until somebody does, I'm going to have to NAK this.
> 
> Thierry
> 

<snip>
Thierry Reding Jan. 31, 2017, 3:02 p.m. UTC | #13
On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
> On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
> > On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
> > > 
> > > 
> > > 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
> > > > Dear Thierry,
> > > > 
> > > > Could you please review this patch?
> > > 
> > > Thierry, I think this patch has been reviewed enough but no comment
> > > from you. Seems you are busy. I will pick up this.
> > 
> > Sorry, but that's not how it works. This patch has gone through 8
> > revisions within 4 weeks, and I tend to ignore patches like that until
> > the dust settles.
> > 
> 
> Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
> and picked up on 1/31. I don't think it's unreasonable to take it through
> another tree after that.
> 
> I wonder if drm_panel would benefit from the -misc group maintainership model
> as drm_bridge does. By spreading out the workload, the high-maintenance
> patches would hopefully find someone to shepherd them through.

Except that nobody except me really cares. If we let people take patches
through separate trees or group-maintained trees they'll likely go in
without too much thought. DRM panel is somewhat different from core DRM
in this regard because its infrastructure is minimal and there's little
outside the panel-simple driver. So we're still at a stage where we need
to fine-tune what drivers should look like and how we can improve.

> > Other than that, this continues the same madness that I've repeatedly
> > complained about in the past. The whole mechanism of running through a
> > series of writes and not caring about errors until the very end is
> > something we've discussed at length in the past. It also in large parts
> > duplicates a bunch of functions from other Samsung panel drivers that I
> > already said should eventually be moved to something saner.
> > 
> 
> FWIW, this type of error handling isn't my preference either. If we must defer,
> I'd rather not keep it in ctx, but rather pass around an argument so it's more
> obvious we need to deal with it in the return. That said, this seems like
> a case of letting the perfect be the enemy of the good, surely something is
> better than nothing?

That's what I ended up saying the last two times. But this has got to
stop at some point. If you look at most of the panel drivers, they look
more like material for the staging tree rather than DRM proper.

Yes, something is better than nothing, but we can't have this multiply
further.

Last time we discussed this there was some rough concensus that the
initialization sequence would be split into separate functions, which is
already mostly true for these drivers, and then settle on a common
signature for these functions, which is also mostly the case already,
and then have a table of functions that can be called one after another
with error handling on each call. That way at least you can provide
diagnostics about what function failed, and you can abort early because
we have to assume that failures are fatal.

Thierry
Sean Paul Jan. 31, 2017, 3:49 p.m. UTC | #14
On Tue, Jan 31, 2017 at 04:02:26PM +0100, Thierry Reding wrote:
> On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
> > On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
> > > On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
> > > > 
> > > > 
> > > > 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
> > > > > Dear Thierry,
> > > > > 
> > > > > Could you please review this patch?
> > > > 
> > > > Thierry, I think this patch has been reviewed enough but no comment
> > > > from you. Seems you are busy. I will pick up this.
> > > 
> > > Sorry, but that's not how it works. This patch has gone through 8
> > > revisions within 4 weeks, and I tend to ignore patches like that until
> > > the dust settles.
> > > 
> > 
> > Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
> > and picked up on 1/31. I don't think it's unreasonable to take it through
> > another tree after that.
> > 
> > I wonder if drm_panel would benefit from the -misc group maintainership model
> > as drm_bridge does. By spreading out the workload, the high-maintenance
> > patches would hopefully find someone to shepherd them through.
> 
> Except that nobody except me really cares. 

I certainly haven't been paying attention. My excuse, at least, is that you're a
great maintainer and I haven't thought the patches need a second look. Perhaps
if we moved towards a group, more people would be vested/care?

> If we let people take patches
> through separate trees or group-maintained trees they'll likely go in
> without too much thought. DRM panel is somewhat different from core DRM
> in this regard because its infrastructure is minimal and there's little
> outside the panel-simple driver. So we're still at a stage where we need
> to fine-tune what drivers should look like and how we can improve.
> 

Fair point. With drm_bridge, I've been lending review help, but deferring to
Archit for merge on patches which I think he should look at. Gustavo is in a
similar place with fences. drm_panel seems like something that should follow
the same model. Maybe once more people (or one person) get up to speed on
things, we could share the load.

> > > Other than that, this continues the same madness that I've repeatedly
> > > complained about in the past. The whole mechanism of running through a
> > > series of writes and not caring about errors until the very end is
> > > something we've discussed at length in the past. It also in large parts
> > > duplicates a bunch of functions from other Samsung panel drivers that I
> > > already said should eventually be moved to something saner.
> > > 
> > 
> > FWIW, this type of error handling isn't my preference either. If we must defer,
> > I'd rather not keep it in ctx, but rather pass around an argument so it's more
> > obvious we need to deal with it in the return. That said, this seems like
> > a case of letting the perfect be the enemy of the good, surely something is
> > better than nothing?
> 
> That's what I ended up saying the last two times. But this has got to
> stop at some point. If you look at most of the panel drivers, they look
> more like material for the staging tree rather than DRM proper.
> 
> Yes, something is better than nothing, but we can't have this multiply
> further.
>

So perhaps if we had more reviewers, we could tighten up the review feedback
loop and avoid this while still getting things merged?

I'm mostly thinking out load here, so take it for what it's worth.

Sean



> Last time we discussed this there was some rough concensus that the
> initialization sequence would be split into separate functions, which is
> already mostly true for these drivers, and then settle on a common
> signature for these functions, which is also mostly the case already,
> and then have a table of functions that can be called one after another
> with error handling on each call. That way at least you can provide
> diagnostics about what function failed, and you can abort early because
> we have to assume that failures are fatal.
> 
> Thierry



> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
Eric Anholt Jan. 31, 2017, 6:15 p.m. UTC | #15
Thierry Reding <thierry.reding@gmail.com> writes:

> [ Unknown signature status ]
> On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
>> On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
>> > On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
>> > > 
>> > > 
>> > > 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>> > > > Dear Thierry,
>> > > > 
>> > > > Could you please review this patch?
>> > > 
>> > > Thierry, I think this patch has been reviewed enough but no comment
>> > > from you. Seems you are busy. I will pick up this.
>> > 
>> > Sorry, but that's not how it works. This patch has gone through 8
>> > revisions within 4 weeks, and I tend to ignore patches like that until
>> > the dust settles.
>> > 
>> 
>> Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
>> and picked up on 1/31. I don't think it's unreasonable to take it through
>> another tree after that.
>> 
>> I wonder if drm_panel would benefit from the -misc group maintainership model
>> as drm_bridge does. By spreading out the workload, the high-maintenance
>> patches would hopefully find someone to shepherd them through.
>
> Except that nobody except me really cares. If we let people take patches
> through separate trees or group-maintained trees they'll likely go in
> without too much thought. DRM panel is somewhat different from core DRM
> in this regard because its infrastructure is minimal and there's little
> outside the panel-simple driver. So we're still at a stage where we need
> to fine-tune what drivers should look like and how we can improve.

I would love to care and participate in review, but with the structure
of your tree you're the only one whose review counts, so I don't
participate.

As is, I'm stuck out here with my panel driver I submitted on December
14th completely ignored, and no other developer will look at it because
their review doesn't count and only yours does.

I would love for drm-panel to be moved under -misc.
Thierry Reding Jan. 31, 2017, 9:17 p.m. UTC | #16
On Tue, Jan 31, 2017 at 10:49:51AM -0500, Sean Paul wrote:
> On Tue, Jan 31, 2017 at 04:02:26PM +0100, Thierry Reding wrote:
> > On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
> > > On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
> > > > On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
> > > > > 
> > > > > 
> > > > > 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
> > > > > > Dear Thierry,
> > > > > > 
> > > > > > Could you please review this patch?
> > > > > 
> > > > > Thierry, I think this patch has been reviewed enough but no comment
> > > > > from you. Seems you are busy. I will pick up this.
> > > > 
> > > > Sorry, but that's not how it works. This patch has gone through 8
> > > > revisions within 4 weeks, and I tend to ignore patches like that until
> > > > the dust settles.
> > > > 
> > > 
> > > Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
> > > and picked up on 1/31. I don't think it's unreasonable to take it through
> > > another tree after that.
> > > 
> > > I wonder if drm_panel would benefit from the -misc group maintainership model
> > > as drm_bridge does. By spreading out the workload, the high-maintenance
> > > patches would hopefully find someone to shepherd them through.
> > 
> > Except that nobody except me really cares. 
> 
> I certainly haven't been paying attention. My excuse, at least, is that you're a
> great maintainer and I haven't thought the patches need a second look. Perhaps
> if we moved towards a group, more people would be vested/care?

I doubt that group maintainership would change much about the lack of
peer review. Peer review makes maintenance a lot easier. Usually when I
see that a patch has been reviewed by someone that I think I can trust,
I only give it a very brief look before applying. However, if there's
been no other review I need to take a lot more time to review.

> > If we let people take patches
> > through separate trees or group-maintained trees they'll likely go in
> > without too much thought. DRM panel is somewhat different from core DRM
> > in this regard because its infrastructure is minimal and there's little
> > outside the panel-simple driver. So we're still at a stage where we need
> > to fine-tune what drivers should look like and how we can improve.
> > 
> 
> Fair point. With drm_bridge, I've been lending review help, but deferring to
> Archit for merge on patches which I think he should look at. Gustavo is in a
> similar place with fences. drm_panel seems like something that should follow
> the same model. Maybe once more people (or one person) get up to speed on
> things, we could share the load.

I certainly wouldn't mind more people reviewing panel patches. Applying
them is the easy part.

> > > > Other than that, this continues the same madness that I've repeatedly
> > > > complained about in the past. The whole mechanism of running through a
> > > > series of writes and not caring about errors until the very end is
> > > > something we've discussed at length in the past. It also in large parts
> > > > duplicates a bunch of functions from other Samsung panel drivers that I
> > > > already said should eventually be moved to something saner.
> > > > 
> > > 
> > > FWIW, this type of error handling isn't my preference either. If we must defer,
> > > I'd rather not keep it in ctx, but rather pass around an argument so it's more
> > > obvious we need to deal with it in the return. That said, this seems like
> > > a case of letting the perfect be the enemy of the good, surely something is
> > > better than nothing?
> > 
> > That's what I ended up saying the last two times. But this has got to
> > stop at some point. If you look at most of the panel drivers, they look
> > more like material for the staging tree rather than DRM proper.
> > 
> > Yes, something is better than nothing, but we can't have this multiply
> > further.
> >
> 
> So perhaps if we had more reviewers, we could tighten up the review feedback
> loop and avoid this while still getting things merged?

Maybe. Like I said, I would very much welcome more review on panel
patches.

Thierry
Thierry Reding Jan. 31, 2017, 9:31 p.m. UTC | #17
On Tue, Jan 31, 2017 at 10:15:10AM -0800, Eric Anholt wrote:
> Thierry Reding <thierry.reding@gmail.com> writes:
> 
> > [ Unknown signature status ]
> > On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
> >> On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
> >> > On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
> >> > > 
> >> > > 
> >> > > 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
> >> > > > Dear Thierry,
> >> > > > 
> >> > > > Could you please review this patch?
> >> > > 
> >> > > Thierry, I think this patch has been reviewed enough but no comment
> >> > > from you. Seems you are busy. I will pick up this.
> >> > 
> >> > Sorry, but that's not how it works. This patch has gone through 8
> >> > revisions within 4 weeks, and I tend to ignore patches like that until
> >> > the dust settles.
> >> > 
> >> 
> >> Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
> >> and picked up on 1/31. I don't think it's unreasonable to take it through
> >> another tree after that.
> >> 
> >> I wonder if drm_panel would benefit from the -misc group maintainership model
> >> as drm_bridge does. By spreading out the workload, the high-maintenance
> >> patches would hopefully find someone to shepherd them through.
> >
> > Except that nobody except me really cares. If we let people take patches
> > through separate trees or group-maintained trees they'll likely go in
> > without too much thought. DRM panel is somewhat different from core DRM
> > in this regard because its infrastructure is minimal and there's little
> > outside the panel-simple driver. So we're still at a stage where we need
> > to fine-tune what drivers should look like and how we can improve.
> 
> I would love to care and participate in review, but with the structure
> of your tree you're the only one whose review counts, so I don't
> participate.

Really? What exactly do you think is special about the structure of my
tree? I require patches to be on dri-devel (I pick them up from the
patchwork instance at freedesktop.org), the tree is publicly available
and reviewed-by tags get picked up automatically by patchwork.

The panel tree works exactly like any other maintainer tree. And my
review is *not* the only one that counts. I appreciate every Reviewed-by
tag I see on panel patches because it means that I don't have to look as
closely as I have to otherwise.

It is true that I am responsible for those patches, that's why I get to
have the final word on whether or not a patch gets applied. And that's
no different from any other maintainer tree either.

> As is, I'm stuck out here with my panel driver I submitted on December
> 14th completely ignored, and no other developer will look at it because
> their review doesn't count and only yours does.

That's the same lame excuse as above. Nobody's keeping you or anyone
else from reviewing panel patches.

The truth is that reviewing code is hard and time-consuming, and that's
why nobody can be bothered to do it. That has nothing whatsoever to do
with how any specific maintainer operates.

> I would love for drm-panel to be moved under -misc.

Like that's going to magically motivate people to spend their time
reviewing other patches. The only thing that group maintainership adds
is redundancy.

Thierry
Emil Velikov Jan. 31, 2017, 9:32 p.m. UTC | #18
On 31 January 2017 at 21:17, Thierry Reding <thierry.reding@gmail.com> wrote:
> On Tue, Jan 31, 2017 at 10:49:51AM -0500, Sean Paul wrote:
>> On Tue, Jan 31, 2017 at 04:02:26PM +0100, Thierry Reding wrote:
>> > On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
>> > > On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
>> > > > On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
>> > > > >
>> > > > >
>> > > > > 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>> > > > > > Dear Thierry,
>> > > > > >
>> > > > > > Could you please review this patch?
>> > > > >
>> > > > > Thierry, I think this patch has been reviewed enough but no comment
>> > > > > from you. Seems you are busy. I will pick up this.
>> > > >
>> > > > Sorry, but that's not how it works. This patch has gone through 8
>> > > > revisions within 4 weeks, and I tend to ignore patches like that until
>> > > > the dust settles.
>> > > >
>> > >
>> > > Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
>> > > and picked up on 1/31. I don't think it's unreasonable to take it through
>> > > another tree after that.
>> > >
>> > > I wonder if drm_panel would benefit from the -misc group maintainership model
>> > > as drm_bridge does. By spreading out the workload, the high-maintenance
>> > > patches would hopefully find someone to shepherd them through.
>> >
>> > Except that nobody except me really cares.
>>
>> I certainly haven't been paying attention. My excuse, at least, is that you're a
>> great maintainer and I haven't thought the patches need a second look. Perhaps
>> if we moved towards a group, more people would be vested/care?
>
> I doubt that group maintainership would change much about the lack of
> peer review. Peer review makes maintenance a lot easier. Usually when I
> see that a patch has been reviewed by someone that I think I can trust,
> I only give it a very brief look before applying. However, if there's
> been no other review I need to take a lot more time to review.
>
>> > If we let people take patches
>> > through separate trees or group-maintained trees they'll likely go in
>> > without too much thought. DRM panel is somewhat different from core DRM
>> > in this regard because its infrastructure is minimal and there's little
>> > outside the panel-simple driver. So we're still at a stage where we need
>> > to fine-tune what drivers should look like and how we can improve.
>> >
>>
>> Fair point. With drm_bridge, I've been lending review help, but deferring to
>> Archit for merge on patches which I think he should look at. Gustavo is in a
>> similar place with fences. drm_panel seems like something that should follow
>> the same model. Maybe once more people (or one person) get up to speed on
>> things, we could share the load.
>
> I certainly wouldn't mind more people reviewing panel patches. Applying
> them is the easy part.
>
>> > > > Other than that, this continues the same madness that I've repeatedly
>> > > > complained about in the past. The whole mechanism of running through a
>> > > > series of writes and not caring about errors until the very end is
>> > > > something we've discussed at length in the past. It also in large parts
>> > > > duplicates a bunch of functions from other Samsung panel drivers that I
>> > > > already said should eventually be moved to something saner.
>> > > >
>> > >
>> > > FWIW, this type of error handling isn't my preference either. If we must defer,
>> > > I'd rather not keep it in ctx, but rather pass around an argument so it's more
>> > > obvious we need to deal with it in the return. That said, this seems like
>> > > a case of letting the perfect be the enemy of the good, surely something is
>> > > better than nothing?
>> >
>> > That's what I ended up saying the last two times. But this has got to
>> > stop at some point. If you look at most of the panel drivers, they look
>> > more like material for the staging tree rather than DRM proper.
>> >
>> > Yes, something is better than nothing, but we can't have this multiply
>> > further.
>> >
>>
>> So perhaps if we had more reviewers, we could tighten up the review feedback
>> loop and avoid this while still getting things merged?
>
> Maybe. Like I said, I would very much welcome more review on panel
> patches.
>
I think that there's a part that Inki and/or others might have
forgotten. Being the sole/core person reviewing this leads to a bit
too often repeat of the same principles ... which after a while gets a
bit [pick your favourite non-cool word].

In one of my experiences [whist attempting to help out], one had to
repeat/reword the exact same thing three times in order to be
addressed :-\

-Emil
Thierry Reding Jan. 31, 2017, 9:51 p.m. UTC | #19
On Tue, Jan 31, 2017 at 10:15:10AM -0800, Eric Anholt wrote:
[...]
> As is, I'm stuck out here with my panel driver I submitted on December
> 14th completely ignored, and no other developer will look at it because
> their review doesn't count and only yours does.

For the record, until earlier today when Daniel started to review your
patches, no doubt in response to this discussion, there was exactly one
response to one of your clock driver patches in the series.

So can we please stop singling out panel drivers? This is a general
problem that we've had across the kernel, or at least the ARM and
related parts of it, for as long as I can remember.

Thierry
Eric Anholt Jan. 31, 2017, 10:54 p.m. UTC | #20
Thierry Reding <thierry.reding@gmail.com> writes:

> [ Unknown signature status ]
> On Tue, Jan 31, 2017 at 10:15:10AM -0800, Eric Anholt wrote:
>> Thierry Reding <thierry.reding@gmail.com> writes:
>> 
>> > [ Unknown signature status ]
>> > On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
>> >> On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
>> >> > On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
>> >> > > 
>> >> > > 
>> >> > > 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>> >> > > > Dear Thierry,
>> >> > > > 
>> >> > > > Could you please review this patch?
>> >> > > 
>> >> > > Thierry, I think this patch has been reviewed enough but no comment
>> >> > > from you. Seems you are busy. I will pick up this.
>> >> > 
>> >> > Sorry, but that's not how it works. This patch has gone through 8
>> >> > revisions within 4 weeks, and I tend to ignore patches like that until
>> >> > the dust settles.
>> >> > 
>> >> 
>> >> Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
>> >> and picked up on 1/31. I don't think it's unreasonable to take it through
>> >> another tree after that.
>> >> 
>> >> I wonder if drm_panel would benefit from the -misc group maintainership model
>> >> as drm_bridge does. By spreading out the workload, the high-maintenance
>> >> patches would hopefully find someone to shepherd them through.
>> >
>> > Except that nobody except me really cares. If we let people take patches
>> > through separate trees or group-maintained trees they'll likely go in
>> > without too much thought. DRM panel is somewhat different from core DRM
>> > in this regard because its infrastructure is minimal and there's little
>> > outside the panel-simple driver. So we're still at a stage where we need
>> > to fine-tune what drivers should look like and how we can improve.
>> 
>> I would love to care and participate in review, but with the structure
>> of your tree you're the only one whose review counts, so I don't
>> participate.
>
> Really? What exactly do you think is special about the structure of my
> tree? I require patches to be on dri-devel (I pick them up from the
> patchwork instance at freedesktop.org), the tree is publicly available
> and reviewed-by tags get picked up automatically by patchwork.
>
> The panel tree works exactly like any other maintainer tree. And my
> review is *not* the only one that counts. I appreciate every Reviewed-by
> tag I see on panel patches because it means that I don't have to look as
> closely as I have to otherwise.
>
> It is true that I am responsible for those patches, that's why I get to
> have the final word on whether or not a patch gets applied. And that's
> no different from any other maintainer tree either.

If me reviewing a patch isn't part of unblocking that patch getting in,
then I won't bother because all I could end up doing is punishing the
developer of the patch.  Contributors have a hard enough time already.
Inki Dae Jan. 31, 2017, 11:48 p.m. UTC | #21
2017년 02월 01일 06:31에 Thierry Reding 이(가) 쓴 글:
> On Tue, Jan 31, 2017 at 10:15:10AM -0800, Eric Anholt wrote:
>> Thierry Reding <thierry.reding@gmail.com> writes:
>>
>>> [ Unknown signature status ]
>>> On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
>>>> On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
>>>>> On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
>>>>>>
>>>>>>
>>>>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>>>>>> Dear Thierry,
>>>>>>>
>>>>>>> Could you please review this patch?
>>>>>>
>>>>>> Thierry, I think this patch has been reviewed enough but no comment
>>>>>> from you. Seems you are busy. I will pick up this.
>>>>>
>>>>> Sorry, but that's not how it works. This patch has gone through 8
>>>>> revisions within 4 weeks, and I tend to ignore patches like that until
>>>>> the dust settles.
>>>>>
>>>>
>>>> Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
>>>> and picked up on 1/31. I don't think it's unreasonable to take it through
>>>> another tree after that.
>>>>
>>>> I wonder if drm_panel would benefit from the -misc group maintainership model
>>>> as drm_bridge does. By spreading out the workload, the high-maintenance
>>>> patches would hopefully find someone to shepherd them through.
>>>
>>> Except that nobody except me really cares. If we let people take patches
>>> through separate trees or group-maintained trees they'll likely go in
>>> without too much thought. DRM panel is somewhat different from core DRM
>>> in this regard because its infrastructure is minimal and there's little
>>> outside the panel-simple driver. So we're still at a stage where we need
>>> to fine-tune what drivers should look like and how we can improve.
>>
>> I would love to care and participate in review, but with the structure
>> of your tree you're the only one whose review counts, so I don't
>> participate.
> 
> Really? What exactly do you think is special about the structure of my
> tree? I require patches to be on dri-devel (I pick them up from the
> patchwork instance at freedesktop.org), the tree is publicly available
> and reviewed-by tags get picked up automatically by patchwork.
> 
> The panel tree works exactly like any other maintainer tree. And my
> review is *not* the only one that counts. I appreciate every Reviewed-by
> tag I see on panel patches because it means that I don't have to look as
> closely as I have to otherwise.

I don't think the panel tree works exactly like other maintainer tree.

I'd like to recommend you to read below Greg's blog. This blog says about *Role of a Linux Kernel Maintainer*.
http://www.kroah.com/log/linux/maintainer_pledge.html

Especially, I'd like to emphasize below things,
- I will review your patch within 1-2 weeks.
- I will offer semi-constructive criticism of your patches.
- I will let you know the status of your patch if it is rejected, or if it is accepted, what tree it has gone into, where you can find it, and when you can expect to see it merged into Linus's tree.

Why do you ignore contributor's patch? Even though the patch is ugly, I think you need to point it out and give your feedback to contributers as a maintainer.
There was some cases I often missed to review with busy work but I don't ignore contributor's patch.
That was why I tried to pick this patch up to my tree to induce your feedback.

You mentioned like below,
"This patch has gone through 8 revisions within 4 weeks, and I tend to ignore patches like that until the dust settles."

Yes, it's been over a month since contributor sent this patch, and even he requested ping~~~ but there was no comment from you.
You say "I tend to ignore patches like that until the dust settles."

I'd like to say *maintainer is really not a place for power*, and maintainer would implicitly have a role to encourage in contribution activity of contributer.

And you are continuing reply to other maintainer's comments but no comment to the contributor. This guy would still be ping~. :)
You said you've repeatedly complained but how new contributors know this?

And you also said,
"DRM panel is somewhat different from core DRM in this regard because its infrastructure is minimal and there's little outside the panel-simple driver. So we're still at a stage where we need to fine-tune what drivers should look like and how we can improve"

Please, move panel directory to drivers/staging so that other contributors aren't confused. I think drm-panel should be stayed in staging yet until the things you mentioned will be improved because while being discussed and improved, other contributors will continue their contributions.

Thanks,
Inki Dae

> 
> It is true that I am responsible for those patches, that's why I get to
> have the final word on whether or not a patch gets applied. And that's
> no different from any other maintainer tree either.
> 
>> As is, I'm stuck out here with my panel driver I submitted on December
>> 14th completely ignored, and no other developer will look at it because
>> their review doesn't count and only yours does.
> 
> That's the same lame excuse as above. Nobody's keeping you or anyone
> else from reviewing panel patches.
> 
> The truth is that reviewing code is hard and time-consuming, and that's
> why nobody can be bothered to do it. That has nothing whatsoever to do
> with how any specific maintainer operates.
> 
>> I would love for drm-panel to be moved under -misc.
> 
> Like that's going to magically motivate people to spend their time
> reviewing other patches. The only thing that group maintainership adds
> is redundancy.
> 
> Thierry
>
Hoegeun Kwon Feb. 1, 2017, 5:47 a.m. UTC | #22
On 01/31/2017 06:22 PM, Krzysztof Kozlowski wrote:
> On Tue, Jan 31, 2017 at 2:01 AM, Inki Dae <inki.dae@samsung.com> wrote:
>>
>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>> Dear Thierry,
>>>
>>> Could you please review this patch?
>> Thierry, I think this patch has been reviewed enough but no comment from you. Seems you are busy. I will pick up this.
>>
> Comments from v8 were not resolved and I think we are waiting for v9:
> https://lkml.org/lkml/2017/1/11/178
>
> If that is not correct then please clarify.

Dear Krzysztof,

I'm sorry, I was mistaken.
I will change them to optional and will send it to the V9 patch.

Best Regards,
Hoegeun

>
> Best regards,
> Krzysztof
>
>
Thierry Reding Feb. 1, 2017, 2:44 p.m. UTC | #23
On Wed, Feb 01, 2017 at 08:48:30AM +0900, Inki Dae wrote:
> 
> 
> 2017년 02월 01일 06:31에 Thierry Reding 이(가) 쓴 글:
> > On Tue, Jan 31, 2017 at 10:15:10AM -0800, Eric Anholt wrote:
> >> Thierry Reding <thierry.reding@gmail.com> writes:
> >>
> >>> [ Unknown signature status ]
> >>> On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
> >>>> On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
> >>>>> On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
> >>>>>>
> >>>>>>
> >>>>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
> >>>>>>> Dear Thierry,
> >>>>>>>
> >>>>>>> Could you please review this patch?
> >>>>>>
> >>>>>> Thierry, I think this patch has been reviewed enough but no comment
> >>>>>> from you. Seems you are busy. I will pick up this.
> >>>>>
> >>>>> Sorry, but that's not how it works. This patch has gone through 8
> >>>>> revisions within 4 weeks, and I tend to ignore patches like that until
> >>>>> the dust settles.
> >>>>>
> >>>>
> >>>> Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
> >>>> and picked up on 1/31. I don't think it's unreasonable to take it through
> >>>> another tree after that.
> >>>>
> >>>> I wonder if drm_panel would benefit from the -misc group maintainership model
> >>>> as drm_bridge does. By spreading out the workload, the high-maintenance
> >>>> patches would hopefully find someone to shepherd them through.
> >>>
> >>> Except that nobody except me really cares. If we let people take patches
> >>> through separate trees or group-maintained trees they'll likely go in
> >>> without too much thought. DRM panel is somewhat different from core DRM
> >>> in this regard because its infrastructure is minimal and there's little
> >>> outside the panel-simple driver. So we're still at a stage where we need
> >>> to fine-tune what drivers should look like and how we can improve.
> >>
> >> I would love to care and participate in review, but with the structure
> >> of your tree you're the only one whose review counts, so I don't
> >> participate.
> > 
> > Really? What exactly do you think is special about the structure of my
> > tree? I require patches to be on dri-devel (I pick them up from the
> > patchwork instance at freedesktop.org), the tree is publicly available
> > and reviewed-by tags get picked up automatically by patchwork.
> > 
> > The panel tree works exactly like any other maintainer tree. And my
> > review is *not* the only one that counts. I appreciate every Reviewed-by
> > tag I see on panel patches because it means that I don't have to look as
> > closely as I have to otherwise.
> 
> I don't think the panel tree works exactly like other maintainer tree.
> 
> I'd like to recommend you to read below Greg's blog. This blog says
> about *Role of a Linux Kernel Maintainer*.
> http://www.kroah.com/log/linux/maintainer_pledge.html

Okay, now you're being unfair. You can't compare people to Greg. He
is beyond human.

> Especially, I'd like to emphasize below things,
> - I will review your patch within 1-2 weeks.
> - I will offer semi-constructive criticism of your patches.
> - I will let you know the status of your patch if it is rejected, or
> if it is accepted, what tree it has gone into, where you can find it,
> and when you can expect to see it merged into Linus's tree.

First, this is a pledge by Greg, and you can hardly hold me to this if
it isn't coming from me. I agree that the above is ideal, but I'm also
much less efficient as a maintainer as Greg. So are many others. There
simply isn't enough bandwidth to be able to do the above in every case
in addition to the day job and real life.

That said, when I do get around to review patches I think I'm pretty
good at the second and third points, though.

Secondly, it's very convenient how you focus on the maintainers' duties
and completely leave out what maintainers expect from contributors. If
you go and read some of the references linked to by Greg's post, maybe
you'll understand my position a little better as well.

> Why do you ignore contributor's patch? Even though the patch is ugly,
> I think you need to point it out and give your feedback to
> contributers as a maintainer.
> There was some cases I often missed to review with busy work but I
> don't ignore contributor's patch.

I will admit that ignoring the patch in this instance may not have been
the best course of action. But this particular instance was making it
exceptionally difficult for me, which is why I was going to shunt this
until the next cycle so that I could focus on getting the less tiresome
patches in.

And interestingly nobody seems to care about this current discussion
either. So yesterday somebody requested another change to the DT binding
after this discussion had started, and after I had NAK'ed the patch, but
then I see that today there's yet another revision with no attempt to do
anything about the concerns that I had raised.

> That was why I tried to pick this patch up to my tree to induce your
> feedback.
> 
> You mentioned like below,
> "This patch has gone through 8 revisions within 4 weeks, and I tend to
> ignore patches like that until the dust settles."

Yes, two or three of those weeks were during a Christmas break and while
the merge window was open. Sometimes maintainers do need time to
recharge.

> Yes, it's been over a month since contributor sent this patch, and
> even he requested ping~~~ but there was no comment from you.
> You say "I tend to ignore patches like that until the dust settles."

And I did look at the patch after seeing the ping, but I immediately got
frustrated because it repeats the same mistakes that I had been
complaining about, and evidently had been too lax about, when the other
two Samsung drivers got merged. And I do remember some of the names that
were involved previously, so I think it's fair to assume that you knew I
wasn't happy about it, and yet you keep sending the same crappy code.

> I'd like to say *maintainer is really not a place for power*, and
> maintainer would implicitly have a role to encourage in contribution
> activity of contributer.

Those are two orthogonal issues. Of course maintainership is about
power, because otherwise how is a maintainer different from a regular
contributor?

And I do encourage contributions, as long as they are worthy. I did go
through the trouble of explaining the last few times why I think these
drivers aren't good enough, but I'm not very motivated to do it once
more because I did try and be encouraging in the past, and did accept
patches because I thought somebody would eventually come around and do
things better with the next submission. But I was proven wrong. If you
don't care about addressing the issues I bring up during review, I can
not be expected to care about your patch submissions any longer.

> And you are continuing reply to other maintainer's comments but no
> comment to the contributor. This guy would still be ping~. :)

I fully expect contributors to read all of the comments that are made in
response to their submission. If they can't be bothered to follow the
discussion that ensues from their contributions, why should I bother
looking at their patches?

> You said you've repeatedly complained but how new contributors know this?
> 
> And you also said,
> "DRM panel is somewhat different from core DRM in this regard because
> its infrastructure is minimal and there's little outside the
> panel-simple driver. So we're still at a stage where we need to
> fine-tune what drivers should look like and how we can improve"
> 
> Please, move panel directory to drivers/staging so that other
> contributors aren't confused. I think drm-panel should be stayed in
> staging yet until the things you mentioned will be improved because
> while being discussed and improved, other contributors will continue
> their contributions.

You're twisting my words. I'm not saying that the DRM panel framework
should be in staging. What I am saying is that we have only a handful of
drivers, and most people find that panel-simple is a suitable one. And I
think it works great for those cases.

But the other more complicated drivers are a new thing, so we don't have
anything like best practices yet. But when I look at the current patches
as well as the existing Samsung panel drivers that it no doubt was
inspired by, I realize this is not at all something I consider a good
example of how such drivers should be written. So we're going to have to
find ways to improve this before I'm going to accept this patch.

Thierry
Thierry Reding Feb. 1, 2017, 2:52 p.m. UTC | #24
On Tue, Jan 31, 2017 at 02:54:53PM -0800, Eric Anholt wrote:
> Thierry Reding <thierry.reding@gmail.com> writes:
> 
> > [ Unknown signature status ]
> > On Tue, Jan 31, 2017 at 10:15:10AM -0800, Eric Anholt wrote:
> >> Thierry Reding <thierry.reding@gmail.com> writes:
> >> 
> >> > [ Unknown signature status ]
> >> > On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
> >> >> On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
> >> >> > On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
> >> >> > > 
> >> >> > > 
> >> >> > > 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
> >> >> > > > Dear Thierry,
> >> >> > > > 
> >> >> > > > Could you please review this patch?
> >> >> > > 
> >> >> > > Thierry, I think this patch has been reviewed enough but no comment
> >> >> > > from you. Seems you are busy. I will pick up this.
> >> >> > 
> >> >> > Sorry, but that's not how it works. This patch has gone through 8
> >> >> > revisions within 4 weeks, and I tend to ignore patches like that until
> >> >> > the dust settles.
> >> >> > 
> >> >> 
> >> >> Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
> >> >> and picked up on 1/31. I don't think it's unreasonable to take it through
> >> >> another tree after that.
> >> >> 
> >> >> I wonder if drm_panel would benefit from the -misc group maintainership model
> >> >> as drm_bridge does. By spreading out the workload, the high-maintenance
> >> >> patches would hopefully find someone to shepherd them through.
> >> >
> >> > Except that nobody except me really cares. If we let people take patches
> >> > through separate trees or group-maintained trees they'll likely go in
> >> > without too much thought. DRM panel is somewhat different from core DRM
> >> > in this regard because its infrastructure is minimal and there's little
> >> > outside the panel-simple driver. So we're still at a stage where we need
> >> > to fine-tune what drivers should look like and how we can improve.
> >> 
> >> I would love to care and participate in review, but with the structure
> >> of your tree you're the only one whose review counts, so I don't
> >> participate.
> >
> > Really? What exactly do you think is special about the structure of my
> > tree? I require patches to be on dri-devel (I pick them up from the
> > patchwork instance at freedesktop.org), the tree is publicly available
> > and reviewed-by tags get picked up automatically by patchwork.
> >
> > The panel tree works exactly like any other maintainer tree. And my
> > review is *not* the only one that counts. I appreciate every Reviewed-by
> > tag I see on panel patches because it means that I don't have to look as
> > closely as I have to otherwise.
> >
> > It is true that I am responsible for those patches, that's why I get to
> > have the final word on whether or not a patch gets applied. And that's
> > no different from any other maintainer tree either.
> 
> If me reviewing a patch isn't part of unblocking that patch getting in,
> then I won't bother because all I could end up doing is punishing the
> developer of the patch.  Contributors have a hard enough time already.

Maybe you should go and read my previous reply again more carefully.
Perhaps then you'll realize that reviews are in fact helping in getting
patches merged.

Interestingly my inbox doesn't show you ever bothering to review panel
patches, so maybe you should be more careful about your assumptions.

Thierry
Emil Velikov Feb. 1, 2017, 3:29 p.m. UTC | #25
On 1 February 2017 at 14:52, Thierry Reding <thierry.reding@gmail.com> wrote:
> On Tue, Jan 31, 2017 at 02:54:53PM -0800, Eric Anholt wrote:
>> Thierry Reding <thierry.reding@gmail.com> writes:
>>
>> > [ Unknown signature status ]
>> > On Tue, Jan 31, 2017 at 10:15:10AM -0800, Eric Anholt wrote:
>> >> Thierry Reding <thierry.reding@gmail.com> writes:
>> >>
>> >> > [ Unknown signature status ]
>> >> > On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
>> >> >> On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
>> >> >> > On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
>> >> >> > >
>> >> >> > >
>> >> >> > > 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>> >> >> > > > Dear Thierry,
>> >> >> > > >
>> >> >> > > > Could you please review this patch?
>> >> >> > >
>> >> >> > > Thierry, I think this patch has been reviewed enough but no comment
>> >> >> > > from you. Seems you are busy. I will pick up this.
>> >> >> >
>> >> >> > Sorry, but that's not how it works. This patch has gone through 8
>> >> >> > revisions within 4 weeks, and I tend to ignore patches like that until
>> >> >> > the dust settles.
>> >> >> >
>> >> >>
>> >> >> Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
>> >> >> and picked up on 1/31. I don't think it's unreasonable to take it through
>> >> >> another tree after that.
>> >> >>
>> >> >> I wonder if drm_panel would benefit from the -misc group maintainership model
>> >> >> as drm_bridge does. By spreading out the workload, the high-maintenance
>> >> >> patches would hopefully find someone to shepherd them through.
>> >> >
>> >> > Except that nobody except me really cares. If we let people take patches
>> >> > through separate trees or group-maintained trees they'll likely go in
>> >> > without too much thought. DRM panel is somewhat different from core DRM
>> >> > in this regard because its infrastructure is minimal and there's little
>> >> > outside the panel-simple driver. So we're still at a stage where we need
>> >> > to fine-tune what drivers should look like and how we can improve.
>> >>
>> >> I would love to care and participate in review, but with the structure
>> >> of your tree you're the only one whose review counts, so I don't
>> >> participate.
>> >
>> > Really? What exactly do you think is special about the structure of my
>> > tree? I require patches to be on dri-devel (I pick them up from the
>> > patchwork instance at freedesktop.org), the tree is publicly available
>> > and reviewed-by tags get picked up automatically by patchwork.
>> >
>> > The panel tree works exactly like any other maintainer tree. And my
>> > review is *not* the only one that counts. I appreciate every Reviewed-by
>> > tag I see on panel patches because it means that I don't have to look as
>> > closely as I have to otherwise.
>> >
>> > It is true that I am responsible for those patches, that's why I get to
>> > have the final word on whether or not a patch gets applied. And that's
>> > no different from any other maintainer tree either.
>>
>> If me reviewing a patch isn't part of unblocking that patch getting in,
>> then I won't bother because all I could end up doing is punishing the
>> developer of the patch.  Contributors have a hard enough time already.
>
> Maybe you should go and read my previous reply again more carefully.
> Perhaps then you'll realize that reviews are in fact helping in getting
> patches merged.
>
> Interestingly my inbox doesn't show you ever bothering to review panel
> patches, so maybe you should be more careful about your assumptions.
>
Gents, it's understandable that emotions might be running high.

What's the point in pointing fingers at each other - there is enough
to go in each direction.
Let us all step back for a second and consider how we can make things better.

I think it'll be nice to have some/most of the common concerns that
Thierry/others comes across documented - in-kernel, blog post, other.
Such that one can reference to specific points as patch falls sub-par.
We all want to have a balance of nicely written driver and quick
merge.

Inki, I believe myself and others have invited you before on
#dri-devel. This is another medium where you can poke devs and from my
experience - it tends to be more efficient, most of the time.

Thanks
Emil
Sean Paul Feb. 1, 2017, 7:03 p.m. UTC | #26
On Wed, Feb 01, 2017 at 03:29:40PM +0000, Emil Velikov wrote:
> On 1 February 2017 at 14:52, Thierry Reding <thierry.reding@gmail.com> wrote:
> > On Tue, Jan 31, 2017 at 02:54:53PM -0800, Eric Anholt wrote:
> >> Thierry Reding <thierry.reding@gmail.com> writes:
> >>
> >> > [ Unknown signature status ]
> >> > On Tue, Jan 31, 2017 at 10:15:10AM -0800, Eric Anholt wrote:
> >> >> Thierry Reding <thierry.reding@gmail.com> writes:
> >> >>
> >> >> > [ Unknown signature status ]
> >> >> > On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
> >> >> >> On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
> >> >> >> > On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
> >> >> >> > >
> >> >> >> > >
> >> >> >> > > 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
> >> >> >> > > > Dear Thierry,
> >> >> >> > > >
> >> >> >> > > > Could you please review this patch?
> >> >> >> > >
> >> >> >> > > Thierry, I think this patch has been reviewed enough but no comment
> >> >> >> > > from you. Seems you are busy. I will pick up this.
> >> >> >> >
> >> >> >> > Sorry, but that's not how it works. This patch has gone through 8
> >> >> >> > revisions within 4 weeks, and I tend to ignore patches like that until
> >> >> >> > the dust settles.
> >> >> >> >
> >> >> >>
> >> >> >> Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
> >> >> >> and picked up on 1/31. I don't think it's unreasonable to take it through
> >> >> >> another tree after that.
> >> >> >>
> >> >> >> I wonder if drm_panel would benefit from the -misc group maintainership model
> >> >> >> as drm_bridge does. By spreading out the workload, the high-maintenance
> >> >> >> patches would hopefully find someone to shepherd them through.
> >> >> >
> >> >> > Except that nobody except me really cares. If we let people take patches
> >> >> > through separate trees or group-maintained trees they'll likely go in
> >> >> > without too much thought. DRM panel is somewhat different from core DRM
> >> >> > in this regard because its infrastructure is minimal and there's little
> >> >> > outside the panel-simple driver. So we're still at a stage where we need
> >> >> > to fine-tune what drivers should look like and how we can improve.
> >> >>
> >> >> I would love to care and participate in review, but with the structure
> >> >> of your tree you're the only one whose review counts, so I don't
> >> >> participate.
> >> >
> >> > Really? What exactly do you think is special about the structure of my
> >> > tree? I require patches to be on dri-devel (I pick them up from the
> >> > patchwork instance at freedesktop.org), the tree is publicly available
> >> > and reviewed-by tags get picked up automatically by patchwork.
> >> >
> >> > The panel tree works exactly like any other maintainer tree. And my
> >> > review is *not* the only one that counts. I appreciate every Reviewed-by
> >> > tag I see on panel patches because it means that I don't have to look as
> >> > closely as I have to otherwise.
> >> >
> >> > It is true that I am responsible for those patches, that's why I get to
> >> > have the final word on whether or not a patch gets applied. And that's
> >> > no different from any other maintainer tree either.
> >>
> >> If me reviewing a patch isn't part of unblocking that patch getting in,
> >> then I won't bother because all I could end up doing is punishing the
> >> developer of the patch.  Contributors have a hard enough time already.
> >
> > Maybe you should go and read my previous reply again more carefully.
> > Perhaps then you'll realize that reviews are in fact helping in getting
> > patches merged.
> >
> > Interestingly my inbox doesn't show you ever bothering to review panel
> > patches, so maybe you should be more careful about your assumptions.
> >
> Gents, it's understandable that emotions might be running high.
> 
> What's the point in pointing fingers at each other - there is enough
> to go in each direction.
> Let us all step back for a second and consider how we can make things better.
> 

Seems like I kicked up some dust here, for that I apologize. I certainly did not
intend to diminish Thierry's (or anyone else's) role as maintainer.

To put this as concisely as I can, I thought drm_panel would be a good candidate
for -misc given:
        - drm_bridge is already maintained there
        - the drivers are small, and we just resolved to maintain small drivers
          in -misc
        - new patches are blocked on a single reviewer/committer as opposed to a
          qualified committee (which I have come to understand is a feature in
          this instance)

So if we can't migrate it to -misc now, for fear of quality issues, what are the
steps necessary to "de-stage" it?

Sean


> I think it'll be nice to have some/most of the common concerns that
> Thierry/others comes across documented - in-kernel, blog post, other.
> Such that one can reference to specific points as patch falls sub-par.
> We all want to have a balance of nicely written driver and quick
> merge.
> 
> Inki, I believe myself and others have invited you before on
> #dri-devel. This is another medium where you can poke devs and from my
> experience - it tends to be more efficient, most of the time.
> 
> Thanks
> Emil
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
Inki Dae Feb. 2, 2017, 12:44 p.m. UTC | #27
Dear Thierry,


2017년 02월 01일 23:44에 Thierry Reding 이(가) 쓴 글:
> On Wed, Feb 01, 2017 at 08:48:30AM +0900, Inki Dae wrote:
>>
>>
>> 2017년 02월 01일 06:31에 Thierry Reding 이(가) 쓴 글:
>>> On Tue, Jan 31, 2017 at 10:15:10AM -0800, Eric Anholt wrote:
>>>> Thierry Reding <thierry.reding@gmail.com> writes:
>>>>
>>>>> [ Unknown signature status ]
>>>>> On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
>>>>>> On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
>>>>>>> On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>>>>>>>> Dear Thierry,
>>>>>>>>>
>>>>>>>>> Could you please review this patch?
>>>>>>>>
>>>>>>>> Thierry, I think this patch has been reviewed enough but no comment
>>>>>>>> from you. Seems you are busy. I will pick up this.
>>>>>>>
>>>>>>> Sorry, but that's not how it works. This patch has gone through 8
>>>>>>> revisions within 4 weeks, and I tend to ignore patches like that until
>>>>>>> the dust settles.
>>>>>>>
>>>>>>
>>>>>> Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
>>>>>> and picked up on 1/31. I don't think it's unreasonable to take it through
>>>>>> another tree after that.
>>>>>>
>>>>>> I wonder if drm_panel would benefit from the -misc group maintainership model
>>>>>> as drm_bridge does. By spreading out the workload, the high-maintenance
>>>>>> patches would hopefully find someone to shepherd them through.
>>>>>
>>>>> Except that nobody except me really cares. If we let people take patches
>>>>> through separate trees or group-maintained trees they'll likely go in
>>>>> without too much thought. DRM panel is somewhat different from core DRM
>>>>> in this regard because its infrastructure is minimal and there's little
>>>>> outside the panel-simple driver. So we're still at a stage where we need
>>>>> to fine-tune what drivers should look like and how we can improve.
>>>>
>>>> I would love to care and participate in review, but with the structure
>>>> of your tree you're the only one whose review counts, so I don't
>>>> participate.
>>>
>>> Really? What exactly do you think is special about the structure of my
>>> tree? I require patches to be on dri-devel (I pick them up from the
>>> patchwork instance at freedesktop.org), the tree is publicly available
>>> and reviewed-by tags get picked up automatically by patchwork.
>>>
>>> The panel tree works exactly like any other maintainer tree. And my
>>> review is *not* the only one that counts. I appreciate every Reviewed-by
>>> tag I see on panel patches because it means that I don't have to look as
>>> closely as I have to otherwise.
>>
>> I don't think the panel tree works exactly like other maintainer tree.
>>
>> I'd like to recommend you to read below Greg's blog. This blog says
>> about *Role of a Linux Kernel Maintainer*.
>> http://www.kroah.com/log/linux/maintainer_pledge.html
> 
> Okay, now you're being unfair. You can't compare people to Greg. He
> is beyond human.
> 
>> Especially, I'd like to emphasize below things,
>> - I will review your patch within 1-2 weeks.
>> - I will offer semi-constructive criticism of your patches.
>> - I will let you know the status of your patch if it is rejected, or
>> if it is accepted, what tree it has gone into, where you can find it,
>> and when you can expect to see it merged into Linus's tree.
> 
> First, this is a pledge by Greg, and you can hardly hold me to this if
> it isn't coming from me. I agree that the above is ideal, but I'm also
> much less efficient as a maintainer as Greg. So are many others. There
> simply isn't enough bandwidth to be able to do the above in every case
> in addition to the day job and real life.
> 
> That said, when I do get around to review patches I think I'm pretty
> good at the second and third points, though.

Agree. You did well really.

> 
> Secondly, it's very convenient how you focus on the maintainers' duties
> and completely leave out what maintainers expect from contributors. If
> you go and read some of the references linked to by Greg's post, maybe
> you'll understand my position a little better as well.
> 
>> Why do you ignore contributor's patch? Even though the patch is ugly,
>> I think you need to point it out and give your feedback to
>> contributers as a maintainer.
>> There was some cases I often missed to review with busy work but I
>> don't ignore contributor's patch.
> 
> I will admit that ignoring the patch in this instance may not have been
> the best course of action. But this particular instance was making it
> exceptionally difficult for me, which is why I was going to shunt this
> until the next cycle so that I could focus on getting the less tiresome
> patches in.
> 
> And interestingly nobody seems to care about this current discussion
> either. So yesterday somebody requested another change to the DT binding
> after this discussion had started, and after I had NAK'ed the patch, but
> then I see that today there's yet another revision with no attempt to do
> anything about the concerns that I had raised.
> 
>> That was why I tried to pick this patch up to my tree to induce your
>> feedback.
>>
>> You mentioned like below,
>> "This patch has gone through 8 revisions within 4 weeks, and I tend to
>> ignore patches like that until the dust settles."
> 
> Yes, two or three of those weeks were during a Christmas break and while
> the merge window was open. Sometimes maintainers do need time to
> recharge.
> 
>> Yes, it's been over a month since contributor sent this patch, and
>> even he requested ping~~~ but there was no comment from you.
>> You say "I tend to ignore patches like that until the dust settles."
> 
> And I did look at the patch after seeing the ping, but I immediately got
> frustrated because it repeats the same mistakes that I had been
> complaining about, and evidently had been too lax about, when the other
> two Samsung drivers got merged. And I do remember some of the names that
> were involved previously, so I think it's fair to assume that you knew I
> wasn't happy about it, and yet you keep sending the same crappy code.

I think we hadn't reached any conclusion for futher specified TODO.

And below thing Andrzej H. mentioned is all which making you to complain? 
https://lkml.org/lkml/2017/1/31/353

> 
>> I'd like to say *maintainer is really not a place for power*, and
>> maintainer would implicitly have a role to encourage in contribution
>> activity of contributer.
> 
> Those are two orthogonal issues. Of course maintainership is about
> power, because otherwise how is a maintainer different from a regular
> contributor?
> 
> And I do encourage contributions, as long as they are worthy. I did go
> through the trouble of explaining the last few times why I think these
> drivers aren't good enough, but I'm not very motivated to do it once
> more because I did try and be encouraging in the past, and did accept
> patches because I thought somebody would eventually come around and do
> things better with the next submission. But I was proven wrong. If you

Please, do not expect all other contributors know and understand this. Especially, what you talked about in the past would be an unknown fact to new people.
And please, do encourage contributions from people who may post really ugly things you think. If you ignore or don't encourage them, then the community will stop growing.

Seems you want to have only the maintainer's role as a technical leader.

I think you already know below Daniel's blog,
http://blog.ffwll.ch/2017/01/maintainers-dont-scale.html

I realized other maintainers had been contemplating this - maintainer's role - for a long time.
I'd like to recommend you to read *It's About the People* chapter.

Thank someone for sharing this. :)

> don't care about addressing the issues I bring up during review, I can
> not be expected to care about your patch submissions any longer.

Again. That is what Andrzej H. mentioned?
https://lkml.org/lkml/2017/1/31/353

Or is there other thing I don't know?

> 
>> And you are continuing reply to other maintainer's comments but no
>> comment to the contributor. This guy would still be ping~. :)
> 
> I fully expect contributors to read all of the comments that are made in
> response to their submission. If they can't be bothered to follow the
> discussion that ensues from their contributions, why should I bother
> looking at their patches?
> 
>> You said you've repeatedly complained but how new contributors know this?
>>
>> And you also said,
>> "DRM panel is somewhat different from core DRM in this regard because
>> its infrastructure is minimal and there's little outside the
>> panel-simple driver. So we're still at a stage where we need to
>> fine-tune what drivers should look like and how we can improve"
>>
>> Please, move panel directory to drivers/staging so that other
>> contributors aren't confused. I think drm-panel should be stayed in
>> staging yet until the things you mentioned will be improved because
>> while being discussed and improved, other contributors will continue
>> their contributions.
> 
> You're twisting my words. I'm not saying that the DRM panel framework
> should be in staging. What I am saying is that we have only a handful of
> drivers, and most people find that panel-simple is a suitable one. And I
> think it works great for those cases.

I know this, and yes, maybe it can definitely reduce much code and it works well but I think this couldn't cover everything.
Display panel device have many functions by vendor. I.e., Color tone change, Outdoor mode, and so on.

And seems that you didn't reached the agreement enough with other people at least.

Thierry, if you continue to insist on this and block other contributions, you may become a diminisher than becoming a multiplier.

Thanks,
Inki Dae

> 
> But the other more complicated drivers are a new thing, so we don't have
> anything like best practices yet. But when I look at the current patches
> as well as the existing Samsung panel drivers that it no doubt was
> inspired by, I realize this is not at all something I consider a good
> example of how such drivers should be written. So we're going to have to
> find ways to improve this before I'm going to accept this patch.
> 
> Thierry
>
Daniel Vetter Feb. 2, 2017, 2:24 p.m. UTC | #28
On Tue, Jan 31, 2017 at 10:51:06PM +0100, Thierry Reding wrote:
> On Tue, Jan 31, 2017 at 10:15:10AM -0800, Eric Anholt wrote:
> [...]
> > As is, I'm stuck out here with my panel driver I submitted on December
> > 14th completely ignored, and no other developer will look at it because
> > their review doesn't count and only yours does.
> 
> For the record, until earlier today when Daniel started to review your
> patches, no doubt in response to this discussion, there was exactly one
> response to one of your clock driver patches in the series.
> 
> So can we please stop singling out panel drivers? This is a general
> problem that we've had across the kernel, or at least the ARM and
> related parts of it, for as long as I can remember.

I only looked at that as part of the drm-misc small drivers experiment, to
help kickstart a review market. I didn't look at the panel stuff. And the
reason I looked at that right now is because right now we started this
experiment. That has nothing at all to do with what's going on with
drm-panel. If you look at drm-misc, the dsi patches for vc4 have now
landed, but the panel driver for the rpi 7" touchscreen hasn't.

And Eric had the dsi patches queued in his vc4 tree since a while, just
waiting to be sent out in a pull request. We've only gone through the fast
review+merging to drm-misc to try out the new process. So not related at
all I think.
-Daniel
Jani Nikula Feb. 2, 2017, 3:30 p.m. UTC | #29
On Tue, 31 Jan 2017, Thierry Reding <thierry.reding@gmail.com> wrote:
> On Tue, Jan 31, 2017 at 10:15:10AM -0800, Eric Anholt wrote:
>> I would love for drm-panel to be moved under -misc.
>
> Like that's going to magically motivate people to spend their time
> reviewing other patches. The only thing that group maintainership adds
> is redundancy.

Adding redundancy is not an insignificant thing. I think it can be quite
liberating to not have everything and everybody depend on you. You can
defer to others when you're busy, tired, sick, whatever. Things still
move on.

And I don't think redundancy is the only thing that group maintainership
adds. You'll have maintainers that complement each other, with different
skill sets and abilities and experience. They don't all look at the same
things. As maintainers tend to be more senior folks, I find sharing the
load of the more mundane tasks of maintainership free up their time to
contribute more of their technical skills to the project, for example
review.

It's just my personal view on i915, but I think people take more
responsibility of their own work, instead of just sending patches and
waiting for stuff to happen, when they have commit access. But you have
to trust the people.

I didn't intend for this to become a kind of sales pitch, but I do think
drm-panel would be a good fit for drm-misc. Personally I think it's your
call, but I think you should think about it. (And that decision should,
obviously, be made calmly, independent of any particular patch series.)


BR,
Jani.
Thierry Reding Feb. 2, 2017, 4:48 p.m. UTC | #30
On Thu, Feb 02, 2017 at 05:30:34PM +0200, Jani Nikula wrote:
> On Tue, 31 Jan 2017, Thierry Reding <thierry.reding@gmail.com> wrote:
> > On Tue, Jan 31, 2017 at 10:15:10AM -0800, Eric Anholt wrote:
> >> I would love for drm-panel to be moved under -misc.
> >
> > Like that's going to magically motivate people to spend their time
> > reviewing other patches. The only thing that group maintainership adds
> > is redundancy.
> 
> Adding redundancy is not an insignificant thing. I think it can be quite
> liberating to not have everything and everybody depend on you. You can
> defer to others when you're busy, tired, sick, whatever. Things still
> move on.

Oh, I certainly see the advantages in sharing maintainership. However, I
think it really only works well if you've got active developers working
together on the code already. Then it's very natural to let all of them
manage a tree.

But for drm-panel, I've rarely seen anyone review patches. Sometimes the
easy patches (i.e. panel-simple) get reviewed by parties that have some
interest in seeing the patches merged. However, there's usually no
review at all for the more complicated patches.

Given that, I doubt that group maintainership is going to have the
desired effect. Just because the tree is group maintained doesn't mean
that all of a sudden people are going to care about the patches. So I
suspect that even if panel drivers were managed in drm-misc, I'd still
be the only person reviewing the patches. And really, managing a tree
is peanuts compared to the amount of work it takes to properly review
code.

With group maintainership, for this type of tree in particular, I think
it's likely for the quality bar to be lowered. There aren't any best
practices yet for anything beyond panel-simple, and therefore little to
no review is likely going to make people still pick up patches. This is
quite different to the DRM/KMS core bits and drivers because there are
established best practices and people can usually spot when new code is
not living up to expectations.

> And I don't think redundancy is the only thing that group maintainership
> adds. You'll have maintainers that complement each other, with different
> skill sets and abilities and experience. They don't all look at the same
> things. As maintainers tend to be more senior folks, I find sharing the
> load of the more mundane tasks of maintainership free up their time to
> contribute more of their technical skills to the project, for example
> review.

I don't understand why group maintainership would be necessary for this.
Surely people will review code irrespective of who finally applies their
patch. If they don't in a single maintainer project, why would they
suddenly start reviewing code in group maintained projects?

> It's just my personal view on i915, but I think people take more
> responsibility of their own work, instead of just sending patches and
> waiting for stuff to happen, when they have commit access. But you have
> to trust the people.

That's not something that we're discussing here. Surely giving the world
access to the maintainer trees is not a goal that we're pursuing here.
It will still only be a handful of selected people that will have commit
access, so how's that going to change things for contributors that don't
have commit access?

The bottom line still is that we have a requirement to have patches
reviewed before they get applied. So even if everybody had commit access
we'd still need someone to review a patch before it gets applied. Like I
said, reviewing is really the difficult part of a maintainer's job.
Applying a patch is trivial, build-testing is equally trivial and so is
sending out a pull request. All of the above can be easily automated to
a point where it's completely painless.

> I didn't intend for this to become a kind of sales pitch, but I do think
> drm-panel would be a good fit for drm-misc. Personally I think it's your
> call, but I think you should think about it. (And that decision should,
> obviously, be made calmly, independent of any particular patch series.)

You know what? I completely agree with you that drm-panel would be a
good fit for drm-misc. It's effectively part of the DRM/KMS core and
used by most drivers. But it's never been treated that way. Maybe it
is because it's been maintained in a separate tree from the very
beginning, perhaps that was a mistake in the first place. Then again,
back when drm-panel was started we didn't have group maintainership,
so none of the existing trees would've been a good fit.

In the end, I keep getting back to the question that everybody except
me seems to know the answer to: why would there be a difference in how
contributors behave depending on the number of maintainers?

Thierry
Thierry Reding Feb. 2, 2017, 5:58 p.m. UTC | #31
On Tue, Jan 31, 2017 at 01:05:20PM +0100, Andrzej Hajda wrote:
> On 31.01.2017 09:54, Thierry Reding wrote:
> > On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
> >>
> >> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
> >>> Dear Thierry,
> >>>
> >>> Could you please review this patch?
> >> Thierry, I think this patch has been reviewed enough but no comment
> >> from you. Seems you are busy. I will pick up this.
> > Sorry, but that's not how it works. This patch has gone through 8
> > revisions within 4 weeks, and I tend to ignore patches like that until
> > the dust settles.
> >
> > Other than that, this continues the same madness that I've repeatedly
> > complained about in the past. The whole mechanism of running through a
> > series of writes and not caring about errors until the very end is
> > something we've discussed at length in the past. 
> 
> Yes, we have discussed the pattern, but without any conclusion. The
> pattern is correct, used in different places in kernel (see below for
> examples) and significantly decreases source code size. Disallowing it
> in panels subsystem just because of personal preferences of the
> maintainer does not seem to be proper.
> 
> > It also in large parts
> > duplicates a bunch of functions from other Samsung panel drivers that I
> > already said should eventually be moved to something saner.
> 
> Currently there are two Samsung panel drivers, one is on SPI bus,
> another one is on DSI.
> I am (co-)author of both drivers, they have some similarities but I did
> not see any gain in making additional abstractions over transport layer
> just to make one or two small functions common.
> Could you be more precise what are you talking about, or could you give
> a link to the mail where you said it. Anything I remember was a
> discussion about ugly magic initialization sequences due to poor
> documentation.
> 
> And below promised examples of the error pattern, it was time consuming
> to find them, so please at least read them :)

I've done better, below is what I hope a list of good arguments why the
pattern is a bad idea, and why in some cases it can be justified.

> Almost exactly the same patterns for the same purpose:
> 
> 1. http://lxr.free-electrons.com/source/drivers/net/ieee802154/atusb.c#L63
>     Citation from the code:
>     To reduce the number of error checks in the code, we record the
> first error
>     in atusb->err and reject all subsequent requests until the error is
> cleared.

That's about the worst example you could've used. Have you even looked
at the code that uses this? It's completely crazy. So here we have the
atusb_control_msg() function that stores this error, but then also
propagates it to its caller. One of these callers is atusb_read_reg(),
which also propagates the error or returns the register value if the
read was successful.

Now the really insane part is how this is then used in something like
atusb_write_subreg():

	orig = atusb_read_reg(atusb, reg);
	tmp = orig & ~mask;
	tmp |= (value << shift) & mask;
	
	if (tmp != orig)
		ret = atusb_write_reg(atusb, reg, tmp);

So let's assume that atusb_control_msg() fails in the call to
atusb_read_reg(). You'll be returning an error code, mask out some bits,
OR in new ones and write the result to the register. So not only does
the code not bother to check for errors, but it will also happily go and
corrupt registers when an error has occurred while reading.

> 2. http://lxr.free-electrons.com/source/drivers/md/dm-thin.c?v=4.4#L917

This is completely different from the panel driver because it's used to
store a value from within callbacks that can't return one.

> 3. http://lxr.free-electrons.com/source/net/9p/trans_fd.c?v=3.18#L297

Essentially the same thing.

> 4.
> http://lxr.free-electrons.com/source/drivers/media/v4l2-core/v4l2-ctrls.c#L2234

Looks like this could be replaced by the more idiomatic use of ERR_PTR()
encoding error codes. But the most significant difference here is that
each use of the handler_set_err() function is followed by a return. So
instead of ignoring errors like you do in the panel drivers, this does
recognize them and aborts early, rather than trying to go through the
remainder of the code, irrespective of how small the chances are of it
succeeding. Or ignoring that /even if/ the remainder didn't fail, the
one operation that fail might have been essential to the operation of
the device.

> 5. http://lxr.free-electrons.com/source/drivers/media/i2c/s5k5baf.c#L451

That's a particularly good example of why you shouldn't be doing this
kind of thing. Consider what would happen in these cases if for example
there was a problem with the I2C interface. There's a bunch of read and
write sequences in that driver that go completely without checking for
errors. Imagine how that will look to a user when the communication to a
chip doesn't work. They'll get a load of error messages all saying that
communication timed out, or that the slave isn't responding or what not.
And worse still, for timeouts you're also going to introduce a bunch of
error messages interleaved with potentially other useful messages from
other drivers or the core. Depending on the number of access you have it
might take seconds for all of the error messages to drain before you
notice somewhere at the end of the code that something went wrong and
decide to abort whatever you were trying to do.

>     This is my driver, I mention it here just to show it was not a
> problem to merge it to media subsystem.

That just shows what everybody knows: that maintainers care about
different things at different levels.

> Similar patterns:
> 
> 6. http://lxr.free-electrons.com/source/fs/seq_file.c#L398
>     Do not process object if buffer is full, it allows to do not check
> buffer size after every write, for example:
> >     seq_printf(m, " hardirq-safe locks:            %11lu\n",
> >             nr_hardirq_safe);
> >     seq_printf(m, " hardirq-unsafe locks:          %11lu\n",
> >             nr_hardirq_unsafe);
> >     seq_printf(m, " softirq-safe locks:            %11lu\n",
> >             nr_softirq_safe);
> >     seq_printf(m, " softirq-unsafe locks:          %11lu\n",
> >             nr_softirq_unsafe);
> >     seq_printf(m, " irq-safe locks:                %11lu\n",
> >             nr_irq_safe);
> >     seq_printf(m, " irq-unsafe locks:              %11lu\n",
> >             nr_irq_unsafe);
> >
> >     seq_printf(m, " hardirq-read-safe locks:       %11lu\n",
> >             nr_hardirq_read_safe);
> >     seq_printf(m, " hardirq-read-unsafe locks:     %11lu\n",
> >             nr_hardirq_read_unsafe);
> >     seq_printf(m, " softirq-read-safe locks:       %11lu\n",
> >             nr_softirq_read_safe);
> >     seq_printf(m, " softirq-read-unsafe locks:     %11lu\n",
> >             nr_softirq_read_unsafe);
> >     seq_printf(m, " irq-read-safe locks:           %11lu\n",
> >             nr_irq_read_safe);
> >     seq_printf(m, " irq-read-unsafe locks:         %11lu\n",
> >             nr_irq_read_unsafe);
> >
> >     seq_printf(m, " uncategorized locks:           %11lu\n",
> >             nr_uncategorized);
> >     seq_printf(m, " unused locks:                  %11lu\n",
> >             nr_unused);
> >     seq_printf(m, " max locking depth:             %11u\n",
> >             max_lockdep_depth);
> 
> Now try to imagine how it would look like if you add error checking
> after each call.

I think that's a fairly sane use-case for this kind of pattern. The big
difference is that this condition is checked in subsequent accesses to
shortcut failure paths. It's also very different in that it performs
writes to memory and those aren't going to fail. The root cause of this
overflow would be static in nature and likely found doing basic testing
and fixed by making the buffer larger.

That's contrary to a driver doing I/O (I2C, SPI, DSI, ...) that can fail
unexpectedly for any number of reasons.

> 
> 7. http://lxr.free-electrons.com/source/lib/devres.c#L129
>     Postponed error check:
> > */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*
> > /*b*/*/ase = devm_ioremap_resource(&pdev->dev, res);/*
> > */if (IS_ERR(base))/*
> > /**/*/return PTR_ERR(base);/*

Heh, I wrote that =)

This is also quite different from your usage in the panel driver. We do
not simply ignore failure from platform_get_resource(), instead we leave
it up to devm_ioremap_resource() to turn it into a meaningful error code
and message. The function does this very early on, so the error
condition is not ignored, as it is in the panel driver.

Also doing this helps with providing unified error codes and messages
across all callers, and removing a lot of boiler plate from drivers that
previously used to come up with all sorts of meaningless error codes and
completely inconsistent messages.

I hope this somewhat clarifies my opposition to your usage of the
pattern.

Thierry
Thierry Reding Feb. 2, 2017, 7:52 p.m. UTC | #32
I'm going to need to cool down for a bit. Let's resume this on Monday,
maybe we can get back to being constructive after the weekend.

Thierry
Inki Dae Feb. 3, 2017, 4:46 a.m. UTC | #33
2017년 02월 02일 00:29에 Emil Velikov 이(가) 쓴 글:
> On 1 February 2017 at 14:52, Thierry Reding <thierry.reding@gmail.com> wrote:
>> On Tue, Jan 31, 2017 at 02:54:53PM -0800, Eric Anholt wrote:
>>> Thierry Reding <thierry.reding@gmail.com> writes:
>>>
>>>> [ Unknown signature status ]
>>>> On Tue, Jan 31, 2017 at 10:15:10AM -0800, Eric Anholt wrote:
>>>>> Thierry Reding <thierry.reding@gmail.com> writes:
>>>>>
>>>>>> [ Unknown signature status ]
>>>>>> On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
>>>>>>> On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
>>>>>>>> On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>>>>>>>>> Dear Thierry,
>>>>>>>>>>
>>>>>>>>>> Could you please review this patch?
>>>>>>>>>
>>>>>>>>> Thierry, I think this patch has been reviewed enough but no comment
>>>>>>>>> from you. Seems you are busy. I will pick up this.
>>>>>>>>
>>>>>>>> Sorry, but that's not how it works. This patch has gone through 8
>>>>>>>> revisions within 4 weeks, and I tend to ignore patches like that until
>>>>>>>> the dust settles.
>>>>>>>>
>>>>>>>
>>>>>>> Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
>>>>>>> and picked up on 1/31. I don't think it's unreasonable to take it through
>>>>>>> another tree after that.
>>>>>>>
>>>>>>> I wonder if drm_panel would benefit from the -misc group maintainership model
>>>>>>> as drm_bridge does. By spreading out the workload, the high-maintenance
>>>>>>> patches would hopefully find someone to shepherd them through.
>>>>>>
>>>>>> Except that nobody except me really cares. If we let people take patches
>>>>>> through separate trees or group-maintained trees they'll likely go in
>>>>>> without too much thought. DRM panel is somewhat different from core DRM
>>>>>> in this regard because its infrastructure is minimal and there's little
>>>>>> outside the panel-simple driver. So we're still at a stage where we need
>>>>>> to fine-tune what drivers should look like and how we can improve.
>>>>>
>>>>> I would love to care and participate in review, but with the structure
>>>>> of your tree you're the only one whose review counts, so I don't
>>>>> participate.
>>>>
>>>> Really? What exactly do you think is special about the structure of my
>>>> tree? I require patches to be on dri-devel (I pick them up from the
>>>> patchwork instance at freedesktop.org), the tree is publicly available
>>>> and reviewed-by tags get picked up automatically by patchwork.
>>>>
>>>> The panel tree works exactly like any other maintainer tree. And my
>>>> review is *not* the only one that counts. I appreciate every Reviewed-by
>>>> tag I see on panel patches because it means that I don't have to look as
>>>> closely as I have to otherwise.
>>>>
>>>> It is true that I am responsible for those patches, that's why I get to
>>>> have the final word on whether or not a patch gets applied. And that's
>>>> no different from any other maintainer tree either.
>>>
>>> If me reviewing a patch isn't part of unblocking that patch getting in,
>>> then I won't bother because all I could end up doing is punishing the
>>> developer of the patch.  Contributors have a hard enough time already.
>>
>> Maybe you should go and read my previous reply again more carefully.
>> Perhaps then you'll realize that reviews are in fact helping in getting
>> patches merged.
>>
>> Interestingly my inbox doesn't show you ever bothering to review panel
>> patches, so maybe you should be more careful about your assumptions.
>>
> Gents, it's understandable that emotions might be running high.
> 
> What's the point in pointing fingers at each other - there is enough
> to go in each direction.
> Let us all step back for a second and consider how we can make things better.
> 
> I think it'll be nice to have some/most of the common concerns that
> Thierry/others comes across documented - in-kernel, blog post, other.
> Such that one can reference to specific points as patch falls sub-par.
> We all want to have a balance of nicely written driver and quick
> merge.
> 
> Inki, I believe myself and others have invited you before on
> #dri-devel. This is another medium where you can poke devs and from my
> experience - it tends to be more efficient, most of the time.

It's true and totally agree. I can really understand Thierry but I think we need to think about maintainer's role for our community.
And also I think the big and small collisions between maintainers and contributors are just the process of getting better.

Thanks,
Inki Dae

> 
> Thanks
> Emil
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
>
Inki Dae Feb. 3, 2017, 5:47 a.m. UTC | #34
2017년 02월 02일 04:03에 Sean Paul 이(가) 쓴 글:
> On Wed, Feb 01, 2017 at 03:29:40PM +0000, Emil Velikov wrote:
>> On 1 February 2017 at 14:52, Thierry Reding <thierry.reding@gmail.com> wrote:
>>> On Tue, Jan 31, 2017 at 02:54:53PM -0800, Eric Anholt wrote:
>>>> Thierry Reding <thierry.reding@gmail.com> writes:
>>>>
>>>>> [ Unknown signature status ]
>>>>> On Tue, Jan 31, 2017 at 10:15:10AM -0800, Eric Anholt wrote:
>>>>>> Thierry Reding <thierry.reding@gmail.com> writes:
>>>>>>
>>>>>>> [ Unknown signature status ]
>>>>>>> On Tue, Jan 31, 2017 at 09:38:53AM -0500, Sean Paul wrote:
>>>>>>>> On Tue, Jan 31, 2017 at 09:54:49AM +0100, Thierry Reding wrote:
>>>>>>>>> On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>>>>>>>>>> Dear Thierry,
>>>>>>>>>>>
>>>>>>>>>>> Could you please review this patch?
>>>>>>>>>>
>>>>>>>>>> Thierry, I think this patch has been reviewed enough but no comment
>>>>>>>>>> from you. Seems you are busy. I will pick up this.
>>>>>>>>>
>>>>>>>>> Sorry, but that's not how it works. This patch has gone through 8
>>>>>>>>> revisions within 4 weeks, and I tend to ignore patches like that until
>>>>>>>>> the dust settles.
>>>>>>>>>
>>>>>>>>
>>>>>>>> Seems like the dust was pretty settled. It was posted on 1/11, pinged on 1/24,
>>>>>>>> and picked up on 1/31. I don't think it's unreasonable to take it through
>>>>>>>> another tree after that.
>>>>>>>>
>>>>>>>> I wonder if drm_panel would benefit from the -misc group maintainership model
>>>>>>>> as drm_bridge does. By spreading out the workload, the high-maintenance
>>>>>>>> patches would hopefully find someone to shepherd them through.
>>>>>>>
>>>>>>> Except that nobody except me really cares. If we let people take patches
>>>>>>> through separate trees or group-maintained trees they'll likely go in
>>>>>>> without too much thought. DRM panel is somewhat different from core DRM
>>>>>>> in this regard because its infrastructure is minimal and there's little
>>>>>>> outside the panel-simple driver. So we're still at a stage where we need
>>>>>>> to fine-tune what drivers should look like and how we can improve.
>>>>>>
>>>>>> I would love to care and participate in review, but with the structure
>>>>>> of your tree you're the only one whose review counts, so I don't
>>>>>> participate.
>>>>>
>>>>> Really? What exactly do you think is special about the structure of my
>>>>> tree? I require patches to be on dri-devel (I pick them up from the
>>>>> patchwork instance at freedesktop.org), the tree is publicly available
>>>>> and reviewed-by tags get picked up automatically by patchwork.
>>>>>
>>>>> The panel tree works exactly like any other maintainer tree. And my
>>>>> review is *not* the only one that counts. I appreciate every Reviewed-by
>>>>> tag I see on panel patches because it means that I don't have to look as
>>>>> closely as I have to otherwise.
>>>>>
>>>>> It is true that I am responsible for those patches, that's why I get to
>>>>> have the final word on whether or not a patch gets applied. And that's
>>>>> no different from any other maintainer tree either.
>>>>
>>>> If me reviewing a patch isn't part of unblocking that patch getting in,
>>>> then I won't bother because all I could end up doing is punishing the
>>>> developer of the patch.  Contributors have a hard enough time already.
>>>
>>> Maybe you should go and read my previous reply again more carefully.
>>> Perhaps then you'll realize that reviews are in fact helping in getting
>>> patches merged.
>>>
>>> Interestingly my inbox doesn't show you ever bothering to review panel
>>> patches, so maybe you should be more careful about your assumptions.
>>>
>> Gents, it's understandable that emotions might be running high.
>>
>> What's the point in pointing fingers at each other - there is enough
>> to go in each direction.
>> Let us all step back for a second and consider how we can make things better.
>>
> 
> Seems like I kicked up some dust here, for that I apologize. I certainly did not
> intend to diminish Thierry's (or anyone else's) role as maintainer.
> 
> To put this as concisely as I can, I thought drm_panel would be a good candidate
> for -misc given:
>         - drm_bridge is already maintained there
>         - the drivers are small, and we just resolved to maintain small drivers
>           in -misc
>         - new patches are blocked on a single reviewer/committer as opposed to a
>           qualified committee (which I have come to understand is a feature in
>           this instance)

Agree. drm_panel is not large enough to require another maintainer.
However, we had already agreed that Thierry manages drm_panel, either implicitly or explicitly.
Seems he's tired now and he wants to talk about this issue again on next Monday.
At the meeting, I think we could decide whether going to group maintainership model, stay as-is or other better way, including reaching consensus.

Thanks,
Inki Dae

> 
> So if we can't migrate it to -misc now, for fear of quality issues, what are the
> steps necessary to "de-stage" it?
> 
> Sean
> 
> 
>> I think it'll be nice to have some/most of the common concerns that
>> Thierry/others comes across documented - in-kernel, blog post, other.
>> Such that one can reference to specific points as patch falls sub-par.
>> We all want to have a balance of nicely written driver and quick
>> merge.
>>
>> Inki, I believe myself and others have invited you before on
>> #dri-devel. This is another medium where you can poke devs and from my
>> experience - it tends to be more efficient, most of the time.
>>
>> Thanks
>> Emil
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
Andrzej Hajda Feb. 3, 2017, 8:54 a.m. UTC | #35
Hi Thierry,

Finally something technical :)

On 02.02.2017 18:58, Thierry Reding wrote:
> On Tue, Jan 31, 2017 at 01:05:20PM +0100, Andrzej Hajda wrote:
>> On 31.01.2017 09:54, Thierry Reding wrote:
>>> On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
>>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>>>> Dear Thierry,
>>>>>
>>>>> Could you please review this patch?
>>>> Thierry, I think this patch has been reviewed enough but no comment
>>>> from you. Seems you are busy. I will pick up this.
>>> Sorry, but that's not how it works. This patch has gone through 8
>>> revisions within 4 weeks, and I tend to ignore patches like that until
>>> the dust settles.
>>>
>>> Other than that, this continues the same madness that I've repeatedly
>>> complained about in the past. The whole mechanism of running through a
>>> series of writes and not caring about errors until the very end is
>>> something we've discussed at length in the past. 
>> Yes, we have discussed the pattern, but without any conclusion. The
>> pattern is correct, used in different places in kernel (see below for
>> examples) and significantly decreases source code size. Disallowing it
>> in panels subsystem just because of personal preferences of the
>> maintainer does not seem to be proper.
>>
>>> It also in large parts
>>> duplicates a bunch of functions from other Samsung panel drivers that I
>>> already said should eventually be moved to something saner.
>> Currently there are two Samsung panel drivers, one is on SPI bus,
>> another one is on DSI.
>> I am (co-)author of both drivers, they have some similarities but I did
>> not see any gain in making additional abstractions over transport layer
>> just to make one or two small functions common.
>> Could you be more precise what are you talking about, or could you give
>> a link to the mail where you said it. Anything I remember was a
>> discussion about ugly magic initialization sequences due to poor
>> documentation.
>>
>> And below promised examples of the error pattern, it was time consuming
>> to find them, so please at least read them :)
> I've done better, below is what I hope a list of good arguments why the
> pattern is a bad idea, and why in some cases it can be justified.
>
>> Almost exactly the same patterns for the same purpose:
>>
>> 1. http://lxr.free-electrons.com/source/drivers/net/ieee802154/atusb.c#L63
>>     Citation from the code:
>>     To reduce the number of error checks in the code, we record the
>> first error
>>     in atusb->err and reject all subsequent requests until the error is
>> cleared.
> That's about the worst example you could've used. Have you even looked
> at the code that uses this? It's completely crazy. So here we have the
> atusb_control_msg() function that stores this error, but then also
> propagates it to its caller. One of these callers is atusb_read_reg(),
> which also propagates the error or returns the register value if the
> read was successful.
>
> Now the really insane part is how this is then used in something like
> atusb_write_subreg():
>
> 	orig = atusb_read_reg(atusb, reg);
> 	tmp = orig & ~mask;
> 	tmp |= (value << shift) & mask;
> 	
> 	if (tmp != orig)
> 		ret = atusb_write_reg(atusb, reg, tmp);
>
> So let's assume that atusb_control_msg() fails in the call to
> atusb_read_reg(). You'll be returning an error code, mask out some bits,
> OR in new ones and write the result to the register. So not only does
> the code not bother to check for errors, but it will also happily go and
> corrupt registers when an error has occurred while reading.

Not true, in case of error in atusb_read_reg atusb_write_reg will do
nothing because atusb->err is set !
And this is strong point of the idiom - you will not be able to perform
transfer until the error is explicitly cleared.
With this idiom it is enough to put guards only in one/two/few places,
in traditional error handling you just need to put checks after every
function call and it is quite common situation that developers forgot to
do that, for example look at mipi calls here [1] :)
[1]:
http://lxr.free-electrons.com/source/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c#L160

So the function is correct, but to avoid fooling unaware readers I would
add error checking after read:

    orig = atusb_read_reg(atusb, reg);
    if (atusb->err)
        return err;


>> 2. http://lxr.free-electrons.com/source/drivers/md/dm-thin.c?v=4.4#L917
> This is completely different from the panel driver because it's used to
> store a value from within callbacks that can't return one.

This is internal framework of the driver, so nothing prevents developer
from implementing callbacks that return value.
Apparently storing error in the object(struct dm_thin_new_mapping) is
good and convenient  :)

>
>> 3. http://lxr.free-electrons.com/source/net/9p/trans_fd.c?v=3.18#L297
> Essentially the same thing.

The same here.

>
>> 4.
>> http://lxr.free-electrons.com/source/drivers/media/v4l2-core/v4l2-ctrls.c#L2234
> Looks like this could be replaced by the more idiomatic use of ERR_PTR()
> encoding error codes. But the most significant difference here is that
> each use of the handler_set_err() function is followed by a return. So
> instead of ignoring errors like you do in the panel drivers, this does
> recognize them and aborts early, rather than trying to go through the
> remainder of the code, irrespective of how small the chances are of it
> succeeding. Or ignoring that /even if/ the remainder didn't fail, the
> one operation that fail might have been essential to the operation of
> the device.

I have an impression that you do not understand the idiom. It does not
allow to ignore error - as soon as the error is detected, guard is set
and no further harm can be done.
Going back to V4L2, look at the usage of the idiom [2]:
                 v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_gen, NULL);
                 v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_vid, NULL);
                 v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL);
                 v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL);
                 v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL);
                 v4l2_ctrl_add_handler(hdl_vid_cap, hdl_loop_cap, NULL);
                 if (hdl_vid_cap->error)
                         return hdl_vid_cap->error;

Again, the point is you do not need to use old construct, which enlarges
the code about three times:
    err = func(obj,...);
    if (err < 0)
        return err;

Instead you just call func(obj,...), and the burden of error checking is
put into the function, but for this err must be stored in object.

[2]:
http://lxr.free-electrons.com/source/drivers/media/platform/vivid/vivid-ctrls.c#L1630

>
>> 5. http://lxr.free-electrons.com/source/drivers/media/i2c/s5k5baf.c#L451
> That's a particularly good example of why you shouldn't be doing this
> kind of thing. Consider what would happen in these cases if for example
> there was a problem with the I2C interface. There's a bunch of read and
> write sequences in that driver that go completely without checking for
> errors. 

Not true, no further transfer will be performed.

> Imagine how that will look to a user when the communication to a
> chip doesn't work. They'll get a load of error messages all saying that
> communication timed out, or that the slave isn't responding or what not.
> And worse still, for timeouts you're also going to introduce a bunch of
> error messages interleaved with potentially other useful messages from
> other drivers or the core. Depending on the number of access you have it
> might take seconds for all of the error messages to drain before you
> notice somewhere at the end of the code that something went wrong and
> decide to abort whatever you were trying to do.

Again, not true at all.

>
>>     This is my driver, I mention it here just to show it was not a
>> problem to merge it to media subsystem.
> That just shows what everybody knows: that maintainers care about
> different things at different levels.
>
>> Similar patterns:
>>
>> 6. http://lxr.free-electrons.com/source/fs/seq_file.c#L398
>>     Do not process object if buffer is full, it allows to do not check
>> buffer size after every write, for example:
>>>     seq_printf(m, " hardirq-safe locks:            %11lu\n",
>>>             nr_hardirq_safe);
>>>     seq_printf(m, " hardirq-unsafe locks:          %11lu\n",
>>>             nr_hardirq_unsafe);
>>>     seq_printf(m, " softirq-safe locks:            %11lu\n",
>>>             nr_softirq_safe);
>>>     seq_printf(m, " softirq-unsafe locks:          %11lu\n",
>>>             nr_softirq_unsafe);
>>>     seq_printf(m, " irq-safe locks:                %11lu\n",
>>>             nr_irq_safe);
>>>     seq_printf(m, " irq-unsafe locks:              %11lu\n",
>>>             nr_irq_unsafe);
>>>
>>>     seq_printf(m, " hardirq-read-safe locks:       %11lu\n",
>>>             nr_hardirq_read_safe);
>>>     seq_printf(m, " hardirq-read-unsafe locks:     %11lu\n",
>>>             nr_hardirq_read_unsafe);
>>>     seq_printf(m, " softirq-read-safe locks:       %11lu\n",
>>>             nr_softirq_read_safe);
>>>     seq_printf(m, " softirq-read-unsafe locks:     %11lu\n",
>>>             nr_softirq_read_unsafe);
>>>     seq_printf(m, " irq-read-safe locks:           %11lu\n",
>>>             nr_irq_read_safe);
>>>     seq_printf(m, " irq-read-unsafe locks:         %11lu\n",
>>>             nr_irq_read_unsafe);
>>>
>>>     seq_printf(m, " uncategorized locks:           %11lu\n",
>>>             nr_uncategorized);
>>>     seq_printf(m, " unused locks:                  %11lu\n",
>>>             nr_unused);
>>>     seq_printf(m, " max locking depth:             %11u\n",
>>>             max_lockdep_depth);
>> Now try to imagine how it would look like if you add error checking
>> after each call.
> I think that's a fairly sane use-case for this kind of pattern. The big
> difference is that this condition is checked in subsequent accesses to
> shortcut failure paths. It's also very different in that it performs
> writes to memory and those aren't going to fail. The root cause of this
> overflow would be static in nature and likely found doing basic testing
> and fixed by making the buffer larger.
>
> That's contrary to a driver doing I/O (I2C, SPI, DSI, ...) that can fail
> unexpectedly for any number of reasons.

The idiom is still the same - storing error state in the object allows
to move error checking inside called functions, removing much of
redundant code from caller.

>
>> 7. http://lxr.free-electrons.com/source/lib/devres.c#L129
>>     Postponed error check:
>>> */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*
>>> /*b*/*/ase = devm_ioremap_resource(&pdev->dev, res);/*
>>> */if (IS_ERR(base))/*
>>> /**/*/return PTR_ERR(base);/*
> Heh, I wrote that =)
>
> This is also quite different from your usage in the panel driver. We do
> not simply ignore failure from platform_get_resource(), instead we leave
> it up to devm_ioremap_resource() to turn it into a meaningful error code
> and message. The function does this very early on, so the error
> condition is not ignored, as it is in the panel driver.

Claims about the panel again not true, reasons described above.

The similarity here is that you do not perform error checking in the
caller, you just blindly pass the 'object' to the next function, and
still everything is handled correctly.

>
> Also doing this helps with providing unified error codes and messages
> across all callers, and removing a lot of boiler plate from drivers that
> previously used to come up with all sorts of meaningless error codes and
> completely inconsistent messages.

I like this pattern and I agree with this reasoning I just want to allow
usage of it more widely, or at least not to block patches using it :)


Regards
Andrzej
Daniel Vetter Feb. 3, 2017, 8:58 a.m. UTC | #36
On Fri, Feb 03, 2017 at 09:54:42AM +0100, Andrzej Hajda wrote:
> Hi Thierry,
> 
> Finally something technical :)

Please read Thierry's other response, I think a time-out on this thread
would be good for everyone.

Also I'm not sure you're introductory comment was good here, it can be
read as extremely sarcastic and dismissive of Thierry. That doesn't help,
and we expect everyone to be respective of each another here on dri-devel.

Thierry, please keep your long w/e and only read this when you're back
next week :-)

Cheers, Daniel

> 
> On 02.02.2017 18:58, Thierry Reding wrote:
> > On Tue, Jan 31, 2017 at 01:05:20PM +0100, Andrzej Hajda wrote:
> >> On 31.01.2017 09:54, Thierry Reding wrote:
> >>> On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
> >>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
> >>>>> Dear Thierry,
> >>>>>
> >>>>> Could you please review this patch?
> >>>> Thierry, I think this patch has been reviewed enough but no comment
> >>>> from you. Seems you are busy. I will pick up this.
> >>> Sorry, but that's not how it works. This patch has gone through 8
> >>> revisions within 4 weeks, and I tend to ignore patches like that until
> >>> the dust settles.
> >>>
> >>> Other than that, this continues the same madness that I've repeatedly
> >>> complained about in the past. The whole mechanism of running through a
> >>> series of writes and not caring about errors until the very end is
> >>> something we've discussed at length in the past. 
> >> Yes, we have discussed the pattern, but without any conclusion. The
> >> pattern is correct, used in different places in kernel (see below for
> >> examples) and significantly decreases source code size. Disallowing it
> >> in panels subsystem just because of personal preferences of the
> >> maintainer does not seem to be proper.
> >>
> >>> It also in large parts
> >>> duplicates a bunch of functions from other Samsung panel drivers that I
> >>> already said should eventually be moved to something saner.
> >> Currently there are two Samsung panel drivers, one is on SPI bus,
> >> another one is on DSI.
> >> I am (co-)author of both drivers, they have some similarities but I did
> >> not see any gain in making additional abstractions over transport layer
> >> just to make one or two small functions common.
> >> Could you be more precise what are you talking about, or could you give
> >> a link to the mail where you said it. Anything I remember was a
> >> discussion about ugly magic initialization sequences due to poor
> >> documentation.
> >>
> >> And below promised examples of the error pattern, it was time consuming
> >> to find them, so please at least read them :)
> > I've done better, below is what I hope a list of good arguments why the
> > pattern is a bad idea, and why in some cases it can be justified.
> >
> >> Almost exactly the same patterns for the same purpose:
> >>
> >> 1. http://lxr.free-electrons.com/source/drivers/net/ieee802154/atusb.c#L63
> >>     Citation from the code:
> >>     To reduce the number of error checks in the code, we record the
> >> first error
> >>     in atusb->err and reject all subsequent requests until the error is
> >> cleared.
> > That's about the worst example you could've used. Have you even looked
> > at the code that uses this? It's completely crazy. So here we have the
> > atusb_control_msg() function that stores this error, but then also
> > propagates it to its caller. One of these callers is atusb_read_reg(),
> > which also propagates the error or returns the register value if the
> > read was successful.
> >
> > Now the really insane part is how this is then used in something like
> > atusb_write_subreg():
> >
> > 	orig = atusb_read_reg(atusb, reg);
> > 	tmp = orig & ~mask;
> > 	tmp |= (value << shift) & mask;
> > 	
> > 	if (tmp != orig)
> > 		ret = atusb_write_reg(atusb, reg, tmp);
> >
> > So let's assume that atusb_control_msg() fails in the call to
> > atusb_read_reg(). You'll be returning an error code, mask out some bits,
> > OR in new ones and write the result to the register. So not only does
> > the code not bother to check for errors, but it will also happily go and
> > corrupt registers when an error has occurred while reading.
> 
> Not true, in case of error in atusb_read_reg atusb_write_reg will do
> nothing because atusb->err is set !
> And this is strong point of the idiom - you will not be able to perform
> transfer until the error is explicitly cleared.
> With this idiom it is enough to put guards only in one/two/few places,
> in traditional error handling you just need to put checks after every
> function call and it is quite common situation that developers forgot to
> do that, for example look at mipi calls here [1] :)
> [1]:
> http://lxr.free-electrons.com/source/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c#L160
> 
> So the function is correct, but to avoid fooling unaware readers I would
> add error checking after read:
> 
>     orig = atusb_read_reg(atusb, reg);
>     if (atusb->err)
>         return err;
> 
> 
> >> 2. http://lxr.free-electrons.com/source/drivers/md/dm-thin.c?v=4.4#L917
> > This is completely different from the panel driver because it's used to
> > store a value from within callbacks that can't return one.
> 
> This is internal framework of the driver, so nothing prevents developer
> from implementing callbacks that return value.
> Apparently storing error in the object(struct dm_thin_new_mapping) is
> good and convenient  :)
> 
> >
> >> 3. http://lxr.free-electrons.com/source/net/9p/trans_fd.c?v=3.18#L297
> > Essentially the same thing.
> 
> The same here.
> 
> >
> >> 4.
> >> http://lxr.free-electrons.com/source/drivers/media/v4l2-core/v4l2-ctrls.c#L2234
> > Looks like this could be replaced by the more idiomatic use of ERR_PTR()
> > encoding error codes. But the most significant difference here is that
> > each use of the handler_set_err() function is followed by a return. So
> > instead of ignoring errors like you do in the panel drivers, this does
> > recognize them and aborts early, rather than trying to go through the
> > remainder of the code, irrespective of how small the chances are of it
> > succeeding. Or ignoring that /even if/ the remainder didn't fail, the
> > one operation that fail might have been essential to the operation of
> > the device.
> 
> I have an impression that you do not understand the idiom. It does not
> allow to ignore error - as soon as the error is detected, guard is set
> and no further harm can be done.
> Going back to V4L2, look at the usage of the idiom [2]:
>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_gen, NULL);
>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_vid, NULL);
>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL);
>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL);
>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL);
>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_loop_cap, NULL);
>                  if (hdl_vid_cap->error)
>                          return hdl_vid_cap->error;
> 
> Again, the point is you do not need to use old construct, which enlarges
> the code about three times:
>     err = func(obj,...);
>     if (err < 0)
>         return err;
> 
> Instead you just call func(obj,...), and the burden of error checking is
> put into the function, but for this err must be stored in object.
> 
> [2]:
> http://lxr.free-electrons.com/source/drivers/media/platform/vivid/vivid-ctrls.c#L1630
> 
> >
> >> 5. http://lxr.free-electrons.com/source/drivers/media/i2c/s5k5baf.c#L451
> > That's a particularly good example of why you shouldn't be doing this
> > kind of thing. Consider what would happen in these cases if for example
> > there was a problem with the I2C interface. There's a bunch of read and
> > write sequences in that driver that go completely without checking for
> > errors. 
> 
> Not true, no further transfer will be performed.
> 
> > Imagine how that will look to a user when the communication to a
> > chip doesn't work. They'll get a load of error messages all saying that
> > communication timed out, or that the slave isn't responding or what not.
> > And worse still, for timeouts you're also going to introduce a bunch of
> > error messages interleaved with potentially other useful messages from
> > other drivers or the core. Depending on the number of access you have it
> > might take seconds for all of the error messages to drain before you
> > notice somewhere at the end of the code that something went wrong and
> > decide to abort whatever you were trying to do.
> 
> Again, not true at all.
> 
> >
> >>     This is my driver, I mention it here just to show it was not a
> >> problem to merge it to media subsystem.
> > That just shows what everybody knows: that maintainers care about
> > different things at different levels.
> >
> >> Similar patterns:
> >>
> >> 6. http://lxr.free-electrons.com/source/fs/seq_file.c#L398
> >>     Do not process object if buffer is full, it allows to do not check
> >> buffer size after every write, for example:
> >>>     seq_printf(m, " hardirq-safe locks:            %11lu\n",
> >>>             nr_hardirq_safe);
> >>>     seq_printf(m, " hardirq-unsafe locks:          %11lu\n",
> >>>             nr_hardirq_unsafe);
> >>>     seq_printf(m, " softirq-safe locks:            %11lu\n",
> >>>             nr_softirq_safe);
> >>>     seq_printf(m, " softirq-unsafe locks:          %11lu\n",
> >>>             nr_softirq_unsafe);
> >>>     seq_printf(m, " irq-safe locks:                %11lu\n",
> >>>             nr_irq_safe);
> >>>     seq_printf(m, " irq-unsafe locks:              %11lu\n",
> >>>             nr_irq_unsafe);
> >>>
> >>>     seq_printf(m, " hardirq-read-safe locks:       %11lu\n",
> >>>             nr_hardirq_read_safe);
> >>>     seq_printf(m, " hardirq-read-unsafe locks:     %11lu\n",
> >>>             nr_hardirq_read_unsafe);
> >>>     seq_printf(m, " softirq-read-safe locks:       %11lu\n",
> >>>             nr_softirq_read_safe);
> >>>     seq_printf(m, " softirq-read-unsafe locks:     %11lu\n",
> >>>             nr_softirq_read_unsafe);
> >>>     seq_printf(m, " irq-read-safe locks:           %11lu\n",
> >>>             nr_irq_read_safe);
> >>>     seq_printf(m, " irq-read-unsafe locks:         %11lu\n",
> >>>             nr_irq_read_unsafe);
> >>>
> >>>     seq_printf(m, " uncategorized locks:           %11lu\n",
> >>>             nr_uncategorized);
> >>>     seq_printf(m, " unused locks:                  %11lu\n",
> >>>             nr_unused);
> >>>     seq_printf(m, " max locking depth:             %11u\n",
> >>>             max_lockdep_depth);
> >> Now try to imagine how it would look like if you add error checking
> >> after each call.
> > I think that's a fairly sane use-case for this kind of pattern. The big
> > difference is that this condition is checked in subsequent accesses to
> > shortcut failure paths. It's also very different in that it performs
> > writes to memory and those aren't going to fail. The root cause of this
> > overflow would be static in nature and likely found doing basic testing
> > and fixed by making the buffer larger.
> >
> > That's contrary to a driver doing I/O (I2C, SPI, DSI, ...) that can fail
> > unexpectedly for any number of reasons.
> 
> The idiom is still the same - storing error state in the object allows
> to move error checking inside called functions, removing much of
> redundant code from caller.
> 
> >
> >> 7. http://lxr.free-electrons.com/source/lib/devres.c#L129
> >>     Postponed error check:
> >>> */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*
> >>> /*b*/*/ase = devm_ioremap_resource(&pdev->dev, res);/*
> >>> */if (IS_ERR(base))/*
> >>> /**/*/return PTR_ERR(base);/*
> > Heh, I wrote that =)
> >
> > This is also quite different from your usage in the panel driver. We do
> > not simply ignore failure from platform_get_resource(), instead we leave
> > it up to devm_ioremap_resource() to turn it into a meaningful error code
> > and message. The function does this very early on, so the error
> > condition is not ignored, as it is in the panel driver.
> 
> Claims about the panel again not true, reasons described above.
> 
> The similarity here is that you do not perform error checking in the
> caller, you just blindly pass the 'object' to the next function, and
> still everything is handled correctly.
> 
> >
> > Also doing this helps with providing unified error codes and messages
> > across all callers, and removing a lot of boiler plate from drivers that
> > previously used to come up with all sorts of meaningless error codes and
> > completely inconsistent messages.
> 
> I like this pattern and I agree with this reasoning I just want to allow
> usage of it more widely, or at least not to block patches using it :)
> 
> 
> Regards
> Andrzej
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
Andrzej Hajda Feb. 6, 2017, 9:12 a.m. UTC | #37
On 03.02.2017 09:58, Daniel Vetter wrote:
> On Fri, Feb 03, 2017 at 09:54:42AM +0100, Andrzej Hajda wrote:
>> Hi Thierry,
>>
>> Finally something technical :)
> Please read Thierry's other response, I think a time-out on this thread
> would be good for everyone.
>
> Also I'm not sure you're introductory comment was good here, it can be
> read as extremely sarcastic and dismissive of Thierry. That doesn't help,
> and we expect everyone to be respective of each another here on dri-devel.

I do not know why do you suggest that my response can be sarcastic.
You can see our whole conversation from this thread even in this email,
there is serious disagreement on the matter and we try to figure it out
and we are focused on technical side, no whining, no personal attacks.
I hope Thierry sees it also this way, if not, I clearly state that this
phrase has no hidden meaning. I am just glad we can focus on technical
side of the problem.
I hope this will end any possible misreadings.

Regards
Andrzej


>
> Thierry, please keep your long w/e and only read this when you're back
> next week :-)
>
> Cheers, Daniel
>
>> On 02.02.2017 18:58, Thierry Reding wrote:
>>> On Tue, Jan 31, 2017 at 01:05:20PM +0100, Andrzej Hajda wrote:
>>>> On 31.01.2017 09:54, Thierry Reding wrote:
>>>>> On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
>>>>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>>>>>> Dear Thierry,
>>>>>>>
>>>>>>> Could you please review this patch?
>>>>>> Thierry, I think this patch has been reviewed enough but no comment
>>>>>> from you. Seems you are busy. I will pick up this.
>>>>> Sorry, but that's not how it works. This patch has gone through 8
>>>>> revisions within 4 weeks, and I tend to ignore patches like that until
>>>>> the dust settles.
>>>>>
>>>>> Other than that, this continues the same madness that I've repeatedly
>>>>> complained about in the past. The whole mechanism of running through a
>>>>> series of writes and not caring about errors until the very end is
>>>>> something we've discussed at length in the past. 
>>>> Yes, we have discussed the pattern, but without any conclusion. The
>>>> pattern is correct, used in different places in kernel (see below for
>>>> examples) and significantly decreases source code size. Disallowing it
>>>> in panels subsystem just because of personal preferences of the
>>>> maintainer does not seem to be proper.
>>>>
>>>>> It also in large parts
>>>>> duplicates a bunch of functions from other Samsung panel drivers that I
>>>>> already said should eventually be moved to something saner.
>>>> Currently there are two Samsung panel drivers, one is on SPI bus,
>>>> another one is on DSI.
>>>> I am (co-)author of both drivers, they have some similarities but I did
>>>> not see any gain in making additional abstractions over transport layer
>>>> just to make one or two small functions common.
>>>> Could you be more precise what are you talking about, or could you give
>>>> a link to the mail where you said it. Anything I remember was a
>>>> discussion about ugly magic initialization sequences due to poor
>>>> documentation.
>>>>
>>>> And below promised examples of the error pattern, it was time consuming
>>>> to find them, so please at least read them :)
>>> I've done better, below is what I hope a list of good arguments why the
>>> pattern is a bad idea, and why in some cases it can be justified.
>>>
>>>> Almost exactly the same patterns for the same purpose:
>>>>
>>>> 1. http://lxr.free-electrons.com/source/drivers/net/ieee802154/atusb.c#L63
>>>>     Citation from the code:
>>>>     To reduce the number of error checks in the code, we record the
>>>> first error
>>>>     in atusb->err and reject all subsequent requests until the error is
>>>> cleared.
>>> That's about the worst example you could've used. Have you even looked
>>> at the code that uses this? It's completely crazy. So here we have the
>>> atusb_control_msg() function that stores this error, but then also
>>> propagates it to its caller. One of these callers is atusb_read_reg(),
>>> which also propagates the error or returns the register value if the
>>> read was successful.
>>>
>>> Now the really insane part is how this is then used in something like
>>> atusb_write_subreg():
>>>
>>> 	orig = atusb_read_reg(atusb, reg);
>>> 	tmp = orig & ~mask;
>>> 	tmp |= (value << shift) & mask;
>>> 	
>>> 	if (tmp != orig)
>>> 		ret = atusb_write_reg(atusb, reg, tmp);
>>>
>>> So let's assume that atusb_control_msg() fails in the call to
>>> atusb_read_reg(). You'll be returning an error code, mask out some bits,
>>> OR in new ones and write the result to the register. So not only does
>>> the code not bother to check for errors, but it will also happily go and
>>> corrupt registers when an error has occurred while reading.
>> Not true, in case of error in atusb_read_reg atusb_write_reg will do
>> nothing because atusb->err is set !
>> And this is strong point of the idiom - you will not be able to perform
>> transfer until the error is explicitly cleared.
>> With this idiom it is enough to put guards only in one/two/few places,
>> in traditional error handling you just need to put checks after every
>> function call and it is quite common situation that developers forgot to
>> do that, for example look at mipi calls here [1] :)
>> [1]:
>> http://lxr.free-electrons.com/source/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c#L160
>>
>> So the function is correct, but to avoid fooling unaware readers I would
>> add error checking after read:
>>
>>     orig = atusb_read_reg(atusb, reg);
>>     if (atusb->err)
>>         return err;
>>
>>
>>>> 2. http://lxr.free-electrons.com/source/drivers/md/dm-thin.c?v=4.4#L917
>>> This is completely different from the panel driver because it's used to
>>> store a value from within callbacks that can't return one.
>> This is internal framework of the driver, so nothing prevents developer
>> from implementing callbacks that return value.
>> Apparently storing error in the object(struct dm_thin_new_mapping) is
>> good and convenient  :)
>>
>>>> 3. http://lxr.free-electrons.com/source/net/9p/trans_fd.c?v=3.18#L297
>>> Essentially the same thing.
>> The same here.
>>
>>>> 4.
>>>> http://lxr.free-electrons.com/source/drivers/media/v4l2-core/v4l2-ctrls.c#L2234
>>> Looks like this could be replaced by the more idiomatic use of ERR_PTR()
>>> encoding error codes. But the most significant difference here is that
>>> each use of the handler_set_err() function is followed by a return. So
>>> instead of ignoring errors like you do in the panel drivers, this does
>>> recognize them and aborts early, rather than trying to go through the
>>> remainder of the code, irrespective of how small the chances are of it
>>> succeeding. Or ignoring that /even if/ the remainder didn't fail, the
>>> one operation that fail might have been essential to the operation of
>>> the device.
>> I have an impression that you do not understand the idiom. It does not
>> allow to ignore error - as soon as the error is detected, guard is set
>> and no further harm can be done.
>> Going back to V4L2, look at the usage of the idiom [2]:
>>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_gen, NULL);
>>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_vid, NULL);
>>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL);
>>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL);
>>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL);
>>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_loop_cap, NULL);
>>                  if (hdl_vid_cap->error)
>>                          return hdl_vid_cap->error;
>>
>> Again, the point is you do not need to use old construct, which enlarges
>> the code about three times:
>>     err = func(obj,...);
>>     if (err < 0)
>>         return err;
>>
>> Instead you just call func(obj,...), and the burden of error checking is
>> put into the function, but for this err must be stored in object.
>>
>> [2]:
>> http://lxr.free-electrons.com/source/drivers/media/platform/vivid/vivid-ctrls.c#L1630
>>
>>>> 5. http://lxr.free-electrons.com/source/drivers/media/i2c/s5k5baf.c#L451
>>> That's a particularly good example of why you shouldn't be doing this
>>> kind of thing. Consider what would happen in these cases if for example
>>> there was a problem with the I2C interface. There's a bunch of read and
>>> write sequences in that driver that go completely without checking for
>>> errors. 
>> Not true, no further transfer will be performed.
>>
>>> Imagine how that will look to a user when the communication to a
>>> chip doesn't work. They'll get a load of error messages all saying that
>>> communication timed out, or that the slave isn't responding or what not.
>>> And worse still, for timeouts you're also going to introduce a bunch of
>>> error messages interleaved with potentially other useful messages from
>>> other drivers or the core. Depending on the number of access you have it
>>> might take seconds for all of the error messages to drain before you
>>> notice somewhere at the end of the code that something went wrong and
>>> decide to abort whatever you were trying to do.
>> Again, not true at all.
>>
>>>>     This is my driver, I mention it here just to show it was not a
>>>> problem to merge it to media subsystem.
>>> That just shows what everybody knows: that maintainers care about
>>> different things at different levels.
>>>
>>>> Similar patterns:
>>>>
>>>> 6. http://lxr.free-electrons.com/source/fs/seq_file.c#L398
>>>>     Do not process object if buffer is full, it allows to do not check
>>>> buffer size after every write, for example:
>>>>>     seq_printf(m, " hardirq-safe locks:            %11lu\n",
>>>>>             nr_hardirq_safe);
>>>>>     seq_printf(m, " hardirq-unsafe locks:          %11lu\n",
>>>>>             nr_hardirq_unsafe);
>>>>>     seq_printf(m, " softirq-safe locks:            %11lu\n",
>>>>>             nr_softirq_safe);
>>>>>     seq_printf(m, " softirq-unsafe locks:          %11lu\n",
>>>>>             nr_softirq_unsafe);
>>>>>     seq_printf(m, " irq-safe locks:                %11lu\n",
>>>>>             nr_irq_safe);
>>>>>     seq_printf(m, " irq-unsafe locks:              %11lu\n",
>>>>>             nr_irq_unsafe);
>>>>>
>>>>>     seq_printf(m, " hardirq-read-safe locks:       %11lu\n",
>>>>>             nr_hardirq_read_safe);
>>>>>     seq_printf(m, " hardirq-read-unsafe locks:     %11lu\n",
>>>>>             nr_hardirq_read_unsafe);
>>>>>     seq_printf(m, " softirq-read-safe locks:       %11lu\n",
>>>>>             nr_softirq_read_safe);
>>>>>     seq_printf(m, " softirq-read-unsafe locks:     %11lu\n",
>>>>>             nr_softirq_read_unsafe);
>>>>>     seq_printf(m, " irq-read-safe locks:           %11lu\n",
>>>>>             nr_irq_read_safe);
>>>>>     seq_printf(m, " irq-read-unsafe locks:         %11lu\n",
>>>>>             nr_irq_read_unsafe);
>>>>>
>>>>>     seq_printf(m, " uncategorized locks:           %11lu\n",
>>>>>             nr_uncategorized);
>>>>>     seq_printf(m, " unused locks:                  %11lu\n",
>>>>>             nr_unused);
>>>>>     seq_printf(m, " max locking depth:             %11u\n",
>>>>>             max_lockdep_depth);
>>>> Now try to imagine how it would look like if you add error checking
>>>> after each call.
>>> I think that's a fairly sane use-case for this kind of pattern. The big
>>> difference is that this condition is checked in subsequent accesses to
>>> shortcut failure paths. It's also very different in that it performs
>>> writes to memory and those aren't going to fail. The root cause of this
>>> overflow would be static in nature and likely found doing basic testing
>>> and fixed by making the buffer larger.
>>>
>>> That's contrary to a driver doing I/O (I2C, SPI, DSI, ...) that can fail
>>> unexpectedly for any number of reasons.
>> The idiom is still the same - storing error state in the object allows
>> to move error checking inside called functions, removing much of
>> redundant code from caller.
>>
>>>> 7. http://lxr.free-electrons.com/source/lib/devres.c#L129
>>>>     Postponed error check:
>>>>> */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*
>>>>> /*b*/*/ase = devm_ioremap_resource(&pdev->dev, res);/*
>>>>> */if (IS_ERR(base))/*
>>>>> /**/*/return PTR_ERR(base);/*
>>> Heh, I wrote that =)
>>>
>>> This is also quite different from your usage in the panel driver. We do
>>> not simply ignore failure from platform_get_resource(), instead we leave
>>> it up to devm_ioremap_resource() to turn it into a meaningful error code
>>> and message. The function does this very early on, so the error
>>> condition is not ignored, as it is in the panel driver.
>> Claims about the panel again not true, reasons described above.
>>
>> The similarity here is that you do not perform error checking in the
>> caller, you just blindly pass the 'object' to the next function, and
>> still everything is handled correctly.
>>
>>> Also doing this helps with providing unified error codes and messages
>>> across all callers, and removing a lot of boiler plate from drivers that
>>> previously used to come up with all sorts of meaningless error codes and
>>> completely inconsistent messages.
>> I like this pattern and I agree with this reasoning I just want to allow
>> usage of it more widely, or at least not to block patches using it :)
>>
>>
>> Regards
>> Andrzej
>>
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
Daniel Vetter Feb. 6, 2017, 9:39 a.m. UTC | #38
Hi Andrzej,

On Mon, Feb 06, 2017 at 10:12:41AM +0100, Andrzej Hajda wrote:
> On 03.02.2017 09:58, Daniel Vetter wrote:
> > On Fri, Feb 03, 2017 at 09:54:42AM +0100, Andrzej Hajda wrote:
> >> Hi Thierry,
> >>
> >> Finally something technical :)
> > Please read Thierry's other response, I think a time-out on this thread
> > would be good for everyone.
> >
> > Also I'm not sure you're introductory comment was good here, it can be
> > read as extremely sarcastic and dismissive of Thierry. That doesn't help,
> > and we expect everyone to be respective of each another here on dri-devel.
> 
> I do not know why do you suggest that my response can be sarcastic.
> You can see our whole conversation from this thread even in this email,
> there is serious disagreement on the matter and we try to figure it out
> and we are focused on technical side, no whining, no personal attacks.
> I hope Thierry sees it also this way, if not, I clearly state that this
> phrase has no hidden meaning. I am just glad we can focus on technical
> side of the problem.
> I hope this will end any possible misreadings.

Sorry, I didn't want to attack you at all, but assumed that you mean well.
Like everyone else in this thread here. I just wanted to highlight that in
a tricky situation like this, jokes can be misread easily. And personally
I found it rather sarcastic, that's why I said "can". But since everyone
here on dri-devel tries to be respectful of each another, no harm done.

Cheers, Daniel

> 
> Regards
> Andrzej
> 
> 
> >
> > Thierry, please keep your long w/e and only read this when you're back
> > next week :-)
> >
> > Cheers, Daniel
> >
> >> On 02.02.2017 18:58, Thierry Reding wrote:
> >>> On Tue, Jan 31, 2017 at 01:05:20PM +0100, Andrzej Hajda wrote:
> >>>> On 31.01.2017 09:54, Thierry Reding wrote:
> >>>>> On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
> >>>>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
> >>>>>>> Dear Thierry,
> >>>>>>>
> >>>>>>> Could you please review this patch?
> >>>>>> Thierry, I think this patch has been reviewed enough but no comment
> >>>>>> from you. Seems you are busy. I will pick up this.
> >>>>> Sorry, but that's not how it works. This patch has gone through 8
> >>>>> revisions within 4 weeks, and I tend to ignore patches like that until
> >>>>> the dust settles.
> >>>>>
> >>>>> Other than that, this continues the same madness that I've repeatedly
> >>>>> complained about in the past. The whole mechanism of running through a
> >>>>> series of writes and not caring about errors until the very end is
> >>>>> something we've discussed at length in the past. 
> >>>> Yes, we have discussed the pattern, but without any conclusion. The
> >>>> pattern is correct, used in different places in kernel (see below for
> >>>> examples) and significantly decreases source code size. Disallowing it
> >>>> in panels subsystem just because of personal preferences of the
> >>>> maintainer does not seem to be proper.
> >>>>
> >>>>> It also in large parts
> >>>>> duplicates a bunch of functions from other Samsung panel drivers that I
> >>>>> already said should eventually be moved to something saner.
> >>>> Currently there are two Samsung panel drivers, one is on SPI bus,
> >>>> another one is on DSI.
> >>>> I am (co-)author of both drivers, they have some similarities but I did
> >>>> not see any gain in making additional abstractions over transport layer
> >>>> just to make one or two small functions common.
> >>>> Could you be more precise what are you talking about, or could you give
> >>>> a link to the mail where you said it. Anything I remember was a
> >>>> discussion about ugly magic initialization sequences due to poor
> >>>> documentation.
> >>>>
> >>>> And below promised examples of the error pattern, it was time consuming
> >>>> to find them, so please at least read them :)
> >>> I've done better, below is what I hope a list of good arguments why the
> >>> pattern is a bad idea, and why in some cases it can be justified.
> >>>
> >>>> Almost exactly the same patterns for the same purpose:
> >>>>
> >>>> 1. http://lxr.free-electrons.com/source/drivers/net/ieee802154/atusb.c#L63
> >>>>     Citation from the code:
> >>>>     To reduce the number of error checks in the code, we record the
> >>>> first error
> >>>>     in atusb->err and reject all subsequent requests until the error is
> >>>> cleared.
> >>> That's about the worst example you could've used. Have you even looked
> >>> at the code that uses this? It's completely crazy. So here we have the
> >>> atusb_control_msg() function that stores this error, but then also
> >>> propagates it to its caller. One of these callers is atusb_read_reg(),
> >>> which also propagates the error or returns the register value if the
> >>> read was successful.
> >>>
> >>> Now the really insane part is how this is then used in something like
> >>> atusb_write_subreg():
> >>>
> >>> 	orig = atusb_read_reg(atusb, reg);
> >>> 	tmp = orig & ~mask;
> >>> 	tmp |= (value << shift) & mask;
> >>> 	
> >>> 	if (tmp != orig)
> >>> 		ret = atusb_write_reg(atusb, reg, tmp);
> >>>
> >>> So let's assume that atusb_control_msg() fails in the call to
> >>> atusb_read_reg(). You'll be returning an error code, mask out some bits,
> >>> OR in new ones and write the result to the register. So not only does
> >>> the code not bother to check for errors, but it will also happily go and
> >>> corrupt registers when an error has occurred while reading.
> >> Not true, in case of error in atusb_read_reg atusb_write_reg will do
> >> nothing because atusb->err is set !
> >> And this is strong point of the idiom - you will not be able to perform
> >> transfer until the error is explicitly cleared.
> >> With this idiom it is enough to put guards only in one/two/few places,
> >> in traditional error handling you just need to put checks after every
> >> function call and it is quite common situation that developers forgot to
> >> do that, for example look at mipi calls here [1] :)
> >> [1]:
> >> http://lxr.free-electrons.com/source/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c#L160
> >>
> >> So the function is correct, but to avoid fooling unaware readers I would
> >> add error checking after read:
> >>
> >>     orig = atusb_read_reg(atusb, reg);
> >>     if (atusb->err)
> >>         return err;
> >>
> >>
> >>>> 2. http://lxr.free-electrons.com/source/drivers/md/dm-thin.c?v=4.4#L917
> >>> This is completely different from the panel driver because it's used to
> >>> store a value from within callbacks that can't return one.
> >> This is internal framework of the driver, so nothing prevents developer
> >> from implementing callbacks that return value.
> >> Apparently storing error in the object(struct dm_thin_new_mapping) is
> >> good and convenient  :)
> >>
> >>>> 3. http://lxr.free-electrons.com/source/net/9p/trans_fd.c?v=3.18#L297
> >>> Essentially the same thing.
> >> The same here.
> >>
> >>>> 4.
> >>>> http://lxr.free-electrons.com/source/drivers/media/v4l2-core/v4l2-ctrls.c#L2234
> >>> Looks like this could be replaced by the more idiomatic use of ERR_PTR()
> >>> encoding error codes. But the most significant difference here is that
> >>> each use of the handler_set_err() function is followed by a return. So
> >>> instead of ignoring errors like you do in the panel drivers, this does
> >>> recognize them and aborts early, rather than trying to go through the
> >>> remainder of the code, irrespective of how small the chances are of it
> >>> succeeding. Or ignoring that /even if/ the remainder didn't fail, the
> >>> one operation that fail might have been essential to the operation of
> >>> the device.
> >> I have an impression that you do not understand the idiom. It does not
> >> allow to ignore error - as soon as the error is detected, guard is set
> >> and no further harm can be done.
> >> Going back to V4L2, look at the usage of the idiom [2]:
> >>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_gen, NULL);
> >>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_vid, NULL);
> >>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL);
> >>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL);
> >>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL);
> >>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_loop_cap, NULL);
> >>                  if (hdl_vid_cap->error)
> >>                          return hdl_vid_cap->error;
> >>
> >> Again, the point is you do not need to use old construct, which enlarges
> >> the code about three times:
> >>     err = func(obj,...);
> >>     if (err < 0)
> >>         return err;
> >>
> >> Instead you just call func(obj,...), and the burden of error checking is
> >> put into the function, but for this err must be stored in object.
> >>
> >> [2]:
> >> http://lxr.free-electrons.com/source/drivers/media/platform/vivid/vivid-ctrls.c#L1630
> >>
> >>>> 5. http://lxr.free-electrons.com/source/drivers/media/i2c/s5k5baf.c#L451
> >>> That's a particularly good example of why you shouldn't be doing this
> >>> kind of thing. Consider what would happen in these cases if for example
> >>> there was a problem with the I2C interface. There's a bunch of read and
> >>> write sequences in that driver that go completely without checking for
> >>> errors. 
> >> Not true, no further transfer will be performed.
> >>
> >>> Imagine how that will look to a user when the communication to a
> >>> chip doesn't work. They'll get a load of error messages all saying that
> >>> communication timed out, or that the slave isn't responding or what not.
> >>> And worse still, for timeouts you're also going to introduce a bunch of
> >>> error messages interleaved with potentially other useful messages from
> >>> other drivers or the core. Depending on the number of access you have it
> >>> might take seconds for all of the error messages to drain before you
> >>> notice somewhere at the end of the code that something went wrong and
> >>> decide to abort whatever you were trying to do.
> >> Again, not true at all.
> >>
> >>>>     This is my driver, I mention it here just to show it was not a
> >>>> problem to merge it to media subsystem.
> >>> That just shows what everybody knows: that maintainers care about
> >>> different things at different levels.
> >>>
> >>>> Similar patterns:
> >>>>
> >>>> 6. http://lxr.free-electrons.com/source/fs/seq_file.c#L398
> >>>>     Do not process object if buffer is full, it allows to do not check
> >>>> buffer size after every write, for example:
> >>>>>     seq_printf(m, " hardirq-safe locks:            %11lu\n",
> >>>>>             nr_hardirq_safe);
> >>>>>     seq_printf(m, " hardirq-unsafe locks:          %11lu\n",
> >>>>>             nr_hardirq_unsafe);
> >>>>>     seq_printf(m, " softirq-safe locks:            %11lu\n",
> >>>>>             nr_softirq_safe);
> >>>>>     seq_printf(m, " softirq-unsafe locks:          %11lu\n",
> >>>>>             nr_softirq_unsafe);
> >>>>>     seq_printf(m, " irq-safe locks:                %11lu\n",
> >>>>>             nr_irq_safe);
> >>>>>     seq_printf(m, " irq-unsafe locks:              %11lu\n",
> >>>>>             nr_irq_unsafe);
> >>>>>
> >>>>>     seq_printf(m, " hardirq-read-safe locks:       %11lu\n",
> >>>>>             nr_hardirq_read_safe);
> >>>>>     seq_printf(m, " hardirq-read-unsafe locks:     %11lu\n",
> >>>>>             nr_hardirq_read_unsafe);
> >>>>>     seq_printf(m, " softirq-read-safe locks:       %11lu\n",
> >>>>>             nr_softirq_read_safe);
> >>>>>     seq_printf(m, " softirq-read-unsafe locks:     %11lu\n",
> >>>>>             nr_softirq_read_unsafe);
> >>>>>     seq_printf(m, " irq-read-safe locks:           %11lu\n",
> >>>>>             nr_irq_read_safe);
> >>>>>     seq_printf(m, " irq-read-unsafe locks:         %11lu\n",
> >>>>>             nr_irq_read_unsafe);
> >>>>>
> >>>>>     seq_printf(m, " uncategorized locks:           %11lu\n",
> >>>>>             nr_uncategorized);
> >>>>>     seq_printf(m, " unused locks:                  %11lu\n",
> >>>>>             nr_unused);
> >>>>>     seq_printf(m, " max locking depth:             %11u\n",
> >>>>>             max_lockdep_depth);
> >>>> Now try to imagine how it would look like if you add error checking
> >>>> after each call.
> >>> I think that's a fairly sane use-case for this kind of pattern. The big
> >>> difference is that this condition is checked in subsequent accesses to
> >>> shortcut failure paths. It's also very different in that it performs
> >>> writes to memory and those aren't going to fail. The root cause of this
> >>> overflow would be static in nature and likely found doing basic testing
> >>> and fixed by making the buffer larger.
> >>>
> >>> That's contrary to a driver doing I/O (I2C, SPI, DSI, ...) that can fail
> >>> unexpectedly for any number of reasons.
> >> The idiom is still the same - storing error state in the object allows
> >> to move error checking inside called functions, removing much of
> >> redundant code from caller.
> >>
> >>>> 7. http://lxr.free-electrons.com/source/lib/devres.c#L129
> >>>>     Postponed error check:
> >>>>> */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*
> >>>>> /*b*/*/ase = devm_ioremap_resource(&pdev->dev, res);/*
> >>>>> */if (IS_ERR(base))/*
> >>>>> /**/*/return PTR_ERR(base);/*
> >>> Heh, I wrote that =)
> >>>
> >>> This is also quite different from your usage in the panel driver. We do
> >>> not simply ignore failure from platform_get_resource(), instead we leave
> >>> it up to devm_ioremap_resource() to turn it into a meaningful error code
> >>> and message. The function does this very early on, so the error
> >>> condition is not ignored, as it is in the panel driver.
> >> Claims about the panel again not true, reasons described above.
> >>
> >> The similarity here is that you do not perform error checking in the
> >> caller, you just blindly pass the 'object' to the next function, and
> >> still everything is handled correctly.
> >>
> >>> Also doing this helps with providing unified error codes and messages
> >>> across all callers, and removing a lot of boiler plate from drivers that
> >>> previously used to come up with all sorts of meaningless error codes and
> >>> completely inconsistent messages.
> >> I like this pattern and I agree with this reasoning I just want to allow
> >> usage of it more widely, or at least not to block patches using it :)
> >>
> >>
> >> Regards
> >> Andrzej
> >>
> >>
> >> _______________________________________________
> >> dri-devel mailing list
> >> dri-devel@lists.freedesktop.org
> >> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
Thierry Reding Feb. 6, 2017, 10:39 a.m. UTC | #39
On Fri, Feb 03, 2017 at 09:54:42AM +0100, Andrzej Hajda wrote:
> Hi Thierry,
> 
> Finally something technical :)
> 
> On 02.02.2017 18:58, Thierry Reding wrote:
> > On Tue, Jan 31, 2017 at 01:05:20PM +0100, Andrzej Hajda wrote:
> >> On 31.01.2017 09:54, Thierry Reding wrote:
> >>> On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
> >>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
> >>>>> Dear Thierry,
> >>>>>
> >>>>> Could you please review this patch?
> >>>> Thierry, I think this patch has been reviewed enough but no comment
> >>>> from you. Seems you are busy. I will pick up this.
> >>> Sorry, but that's not how it works. This patch has gone through 8
> >>> revisions within 4 weeks, and I tend to ignore patches like that until
> >>> the dust settles.
> >>>
> >>> Other than that, this continues the same madness that I've repeatedly
> >>> complained about in the past. The whole mechanism of running through a
> >>> series of writes and not caring about errors until the very end is
> >>> something we've discussed at length in the past. 
> >> Yes, we have discussed the pattern, but without any conclusion. The
> >> pattern is correct, used in different places in kernel (see below for
> >> examples) and significantly decreases source code size. Disallowing it
> >> in panels subsystem just because of personal preferences of the
> >> maintainer does not seem to be proper.
> >>
> >>> It also in large parts
> >>> duplicates a bunch of functions from other Samsung panel drivers that I
> >>> already said should eventually be moved to something saner.
> >> Currently there are two Samsung panel drivers, one is on SPI bus,
> >> another one is on DSI.
> >> I am (co-)author of both drivers, they have some similarities but I did
> >> not see any gain in making additional abstractions over transport layer
> >> just to make one or two small functions common.
> >> Could you be more precise what are you talking about, or could you give
> >> a link to the mail where you said it. Anything I remember was a
> >> discussion about ugly magic initialization sequences due to poor
> >> documentation.
> >>
> >> And below promised examples of the error pattern, it was time consuming
> >> to find them, so please at least read them :)
> > I've done better, below is what I hope a list of good arguments why the
> > pattern is a bad idea, and why in some cases it can be justified.
> >
> >> Almost exactly the same patterns for the same purpose:
> >>
> >> 1. http://lxr.free-electrons.com/source/drivers/net/ieee802154/atusb.c#L63
> >>     Citation from the code:
> >>     To reduce the number of error checks in the code, we record the
> >> first error
> >>     in atusb->err and reject all subsequent requests until the error is
> >> cleared.
> > That's about the worst example you could've used. Have you even looked
> > at the code that uses this? It's completely crazy. So here we have the
> > atusb_control_msg() function that stores this error, but then also
> > propagates it to its caller. One of these callers is atusb_read_reg(),
> > which also propagates the error or returns the register value if the
> > read was successful.
> >
> > Now the really insane part is how this is then used in something like
> > atusb_write_subreg():
> >
> > 	orig = atusb_read_reg(atusb, reg);
> > 	tmp = orig & ~mask;
> > 	tmp |= (value << shift) & mask;
> > 	
> > 	if (tmp != orig)
> > 		ret = atusb_write_reg(atusb, reg, tmp);
> >
> > So let's assume that atusb_control_msg() fails in the call to
> > atusb_read_reg(). You'll be returning an error code, mask out some bits,
> > OR in new ones and write the result to the register. So not only does
> > the code not bother to check for errors, but it will also happily go and
> > corrupt registers when an error has occurred while reading.
> 
> Not true, in case of error in atusb_read_reg atusb_write_reg will do
> nothing because atusb->err is set !
> And this is strong point of the idiom - you will not be able to perform
> transfer until the error is explicitly cleared.

Yes, I see now that there's an additional check before further accesses
can be made. So I have to apologize to the author and it turns out that
the code isn't crazy. I suppose it's me that's too stupid to understand
how this works.

I wouldn't have been confused if this was using a more idiomatic way of
doing things. The mere fact that code will read/modify/write registers
unconditionally suggests that there's no error checking going on. Even
if the code doesn't actually corrupt registers, it does have the effect
of executing a bunch of instructions completely unnecessarily.

To add to the confusion there are other parts in the driver that don't
seem comfortable using the idiom and you'll actually see things like:

	ret = atusb_write_subreg(...);
	if (ret)
		return ret;

If you ask me, that's bound to lead to mistakes at some point.

> With this idiom it is enough to put guards only in one/two/few places,
> in traditional error handling you just need to put checks after every
> function call and it is quite common situation that developers forgot to
> do that, for example look at mipi calls here [1] :)
> [1]:
> http://lxr.free-electrons.com/source/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c#L160
> 
> So the function is correct, but to avoid fooling unaware readers I would
> add error checking after read:
> 
>     orig = atusb_read_reg(atusb, reg);
>     if (atusb->err)
>         return err;

But then what's the point of storing the error somewhere in the first
place?

> >> 2. http://lxr.free-electrons.com/source/drivers/md/dm-thin.c?v=4.4#L917
> > This is completely different from the panel driver because it's used to
> > store a value from within callbacks that can't return one.
> 
> This is internal framework of the driver, so nothing prevents developer
> from implementing callbacks that return value.
> Apparently storing error in the object(struct dm_thin_new_mapping) is
> good and convenient  :)

No, I think this comes from the fact that the device mapper and block
I/O frameworks have infrastructure that doesn't allow the error code to
be directly propagated (see the copy_complete() and overwrite_endio()
callbacks).

> >> 3. http://lxr.free-electrons.com/source/net/9p/trans_fd.c?v=3.18#L297
> > Essentially the same thing.
> 
> The same here.

Yes, this seems like it could easily be done using propagated error
codes.

> >> 4.
> >> http://lxr.free-electrons.com/source/drivers/media/v4l2-core/v4l2-ctrls.c#L2234
> > Looks like this could be replaced by the more idiomatic use of ERR_PTR()
> > encoding error codes. But the most significant difference here is that
> > each use of the handler_set_err() function is followed by a return. So
> > instead of ignoring errors like you do in the panel drivers, this does
> > recognize them and aborts early, rather than trying to go through the
> > remainder of the code, irrespective of how small the chances are of it
> > succeeding. Or ignoring that /even if/ the remainder didn't fail, the
> > one operation that fail might have been essential to the operation of
> > the device.
> 
> I have an impression that you do not understand the idiom. It does not
> allow to ignore error - as soon as the error is detected, guard is set
> and no further harm can be done.
> Going back to V4L2, look at the usage of the idiom [2]:
>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_gen, NULL);
>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_vid, NULL);
>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL);
>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL);
>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL);
>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_loop_cap, NULL);
>                  if (hdl_vid_cap->error)
>                          return hdl_vid_cap->error;
> 
> Again, the point is you do not need to use old construct, which enlarges
> the code about three times:
>     err = func(obj,...);
>     if (err < 0)
>         return err;
> 
> Instead you just call func(obj,...), and the burden of error checking is
> put into the function, but for this err must be stored in object.
> 
> [2]:
> http://lxr.free-electrons.com/source/drivers/media/platform/vivid/vivid-ctrls.c#L1630

There are a couple of pitfalls here: for example what do you do if the
first two calls fail, but the third succeeds. The end result will be
that the whole operation fails. What do you do if one of the operations
has side-effects that you need to undo? If you don't know which one it
was, how will you know?

And yes, this does allow you to ignore errors. The function calls before
the actual error handling will be executed regardless of errors that are
stored. While in many cases the code seems to correctly shortcut if the
guard is set, it also encodes many assumptions, not all of which may be
understood by morons like me.

The end result is that the code becomes more vulnerable to accidental
mistakes. These will be hard to spot during review. Keeping code linear
and simple makes it much more difficult to introduce bugs.

> >> 5. http://lxr.free-electrons.com/source/drivers/media/i2c/s5k5baf.c#L451
> > That's a particularly good example of why you shouldn't be doing this
> > kind of thing. Consider what would happen in these cases if for example
> > there was a problem with the I2C interface. There's a bunch of read and
> > write sequences in that driver that go completely without checking for
> > errors. 
> 
> Not true, no further transfer will be performed.

Indeed. Clearly I wasn't looking closely enough. How about knowing
whether or not reads or writes succeed? To do so you would have to
explicitly check the error code again.

> > Imagine how that will look to a user when the communication to a
> > chip doesn't work. They'll get a load of error messages all saying that
> > communication timed out, or that the slave isn't responding or what not.
> > And worse still, for timeouts you're also going to introduce a bunch of
> > error messages interleaved with potentially other useful messages from
> > other drivers or the core. Depending on the number of access you have it
> > might take seconds for all of the error messages to drain before you
> > notice somewhere at the end of the code that something went wrong and
> > decide to abort whatever you were trying to do.
> 
> Again, not true at all.

So after looking at this is more detail, I'll grant you that it gives
you a way to avoid checking for errors at every step of the way. There
is a disadvantage because you keep executing a lot of code
unnecessarily, and you make the code susceptible to mistakes because
it contains a non-obvious assumption that contributors to your code may
not be aware of.

> >>     This is my driver, I mention it here just to show it was not a
> >> problem to merge it to media subsystem.
> > That just shows what everybody knows: that maintainers care about
> > different things at different levels.
> >
> >> Similar patterns:
> >>
> >> 6. http://lxr.free-electrons.com/source/fs/seq_file.c#L398
> >>     Do not process object if buffer is full, it allows to do not check
> >> buffer size after every write, for example:
> >>>     seq_printf(m, " hardirq-safe locks:            %11lu\n",
> >>>             nr_hardirq_safe);
> >>>     seq_printf(m, " hardirq-unsafe locks:          %11lu\n",
> >>>             nr_hardirq_unsafe);
> >>>     seq_printf(m, " softirq-safe locks:            %11lu\n",
> >>>             nr_softirq_safe);
> >>>     seq_printf(m, " softirq-unsafe locks:          %11lu\n",
> >>>             nr_softirq_unsafe);
> >>>     seq_printf(m, " irq-safe locks:                %11lu\n",
> >>>             nr_irq_safe);
> >>>     seq_printf(m, " irq-unsafe locks:              %11lu\n",
> >>>             nr_irq_unsafe);
> >>>
> >>>     seq_printf(m, " hardirq-read-safe locks:       %11lu\n",
> >>>             nr_hardirq_read_safe);
> >>>     seq_printf(m, " hardirq-read-unsafe locks:     %11lu\n",
> >>>             nr_hardirq_read_unsafe);
> >>>     seq_printf(m, " softirq-read-safe locks:       %11lu\n",
> >>>             nr_softirq_read_safe);
> >>>     seq_printf(m, " softirq-read-unsafe locks:     %11lu\n",
> >>>             nr_softirq_read_unsafe);
> >>>     seq_printf(m, " irq-read-safe locks:           %11lu\n",
> >>>             nr_irq_read_safe);
> >>>     seq_printf(m, " irq-read-unsafe locks:         %11lu\n",
> >>>             nr_irq_read_unsafe);
> >>>
> >>>     seq_printf(m, " uncategorized locks:           %11lu\n",
> >>>             nr_uncategorized);
> >>>     seq_printf(m, " unused locks:                  %11lu\n",
> >>>             nr_unused);
> >>>     seq_printf(m, " max locking depth:             %11u\n",
> >>>             max_lockdep_depth);
> >> Now try to imagine how it would look like if you add error checking
> >> after each call.
> > I think that's a fairly sane use-case for this kind of pattern. The big
> > difference is that this condition is checked in subsequent accesses to
> > shortcut failure paths. It's also very different in that it performs
> > writes to memory and those aren't going to fail. The root cause of this
> > overflow would be static in nature and likely found doing basic testing
> > and fixed by making the buffer larger.
> >
> > That's contrary to a driver doing I/O (I2C, SPI, DSI, ...) that can fail
> > unexpectedly for any number of reasons.
> 
> The idiom is still the same - storing error state in the object allows
> to move error checking inside called functions, removing much of
> redundant code from caller.

Yes, the idiom is the same, but the use-case is also completely
different, so I don't think you can apply the same rules. If you've got
communication with an external peripheral, like in the case of SPI, I2C
or DSI, I think the best thing to do is abort as early as possible on
failure. The most likely reason for failure would be a persistent cause
on the bus, in which case none of your accesses will succeed. If so I
think it makes sense to let the user know what exactly went wrong and
leave it at that. Continuing to execute code, even if no physical access
to the hardware happens anymore, is a waste of time.

Failures on such busses can happen for any number of reasons, and they
can happen sporadically, so it's important to catch errors. For seq_buf
in particular you have a static failure case, which is if somebody were
trying to add more characters to the buffer than would fit. But callers
will, most of the time, put exactly the same number of characters into
that buffer. So if there was a failure once they'd notice quickly and
the issue would get fixed, at which point it becomes very unlikely to
ever happen again.

So seq_buf is not expected to fail, whereas with peripheral busses you
can't make that assumption.

> >> 7. http://lxr.free-electrons.com/source/lib/devres.c#L129
> >>     Postponed error check:
> >>> */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*
> >>> /*b*/*/ase = devm_ioremap_resource(&pdev->dev, res);/*
> >>> */if (IS_ERR(base))/*
> >>> /**/*/return PTR_ERR(base);/*
> > Heh, I wrote that =)
> >
> > This is also quite different from your usage in the panel driver. We do
> > not simply ignore failure from platform_get_resource(), instead we leave
> > it up to devm_ioremap_resource() to turn it into a meaningful error code
> > and message. The function does this very early on, so the error
> > condition is not ignored, as it is in the panel driver.
> 
> Claims about the panel again not true, reasons described above.
> 
> The similarity here is that you do not perform error checking in the
> caller, you just blindly pass the 'object' to the next function, and
> still everything is handled correctly.

But we're not passing any object to the next function. We're passing the
return value from an earlier call, one line above. We're not hiding an
error code in some driver-specific structure. So the code is completely
explicit.

Maybe I should say a few words about why this is important to me because
you may think this to be somewhat pedantic. This is fundamentally caused
by the fact that once I merge code into a tree that I maintain, then the
responsibility for that code becomes mine. That means I will not only be
held accountable if it doesn't work, breaks or upsets build farms all
over the world. It also means that I am the one that will need to make
changes to it along the way. If the code is difficult for me to
understand because it doesn't behave the way I'm used to, then that
makes my life harder.

Sometimes authors do stick around and care about their drivers, but
often they'll just disappear, or ignore patches and bug reports about
their drivers. So if worst comes to worst people will look to me to fix
their problems. That's why I want it to be easy to read and modify the
code that I maintain, because that way when users show up and complain
about drivers I don't have to spend hours trying to understand what the
original author was trying to achieve.

The above is a variant of what others have termed "obviously correct".
One good way to make code obviously correct is to make it simple.

Thierry
Andrzej Hajda Feb. 7, 2017, 9:53 a.m. UTC | #40
On 06.02.2017 11:39, Thierry Reding wrote:
> On Fri, Feb 03, 2017 at 09:54:42AM +0100, Andrzej Hajda wrote:
>> Hi Thierry,
>>
>> Finally something technical :)
>>
>> On 02.02.2017 18:58, Thierry Reding wrote:
>>> On Tue, Jan 31, 2017 at 01:05:20PM +0100, Andrzej Hajda wrote:
>>>> On 31.01.2017 09:54, Thierry Reding wrote:
>>>>> On Tue, Jan 31, 2017 at 09:01:07AM +0900, Inki Dae wrote:
>>>>>> 2017년 01월 24일 10:50에 Hoegeun Kwon 이(가) 쓴 글:
>>>>>>> Dear Thierry,
>>>>>>>
>>>>>>> Could you please review this patch?
>>>>>> Thierry, I think this patch has been reviewed enough but no comment
>>>>>> from you. Seems you are busy. I will pick up this.
>>>>> Sorry, but that's not how it works. This patch has gone through 8
>>>>> revisions within 4 weeks, and I tend to ignore patches like that until
>>>>> the dust settles.
>>>>>
>>>>> Other than that, this continues the same madness that I've repeatedly
>>>>> complained about in the past. The whole mechanism of running through a
>>>>> series of writes and not caring about errors until the very end is
>>>>> something we've discussed at length in the past. 
>>>> Yes, we have discussed the pattern, but without any conclusion. The
>>>> pattern is correct, used in different places in kernel (see below for
>>>> examples) and significantly decreases source code size. Disallowing it
>>>> in panels subsystem just because of personal preferences of the
>>>> maintainer does not seem to be proper.
>>>>
>>>>> It also in large parts
>>>>> duplicates a bunch of functions from other Samsung panel drivers that I
>>>>> already said should eventually be moved to something saner.
>>>> Currently there are two Samsung panel drivers, one is on SPI bus,
>>>> another one is on DSI.
>>>> I am (co-)author of both drivers, they have some similarities but I did
>>>> not see any gain in making additional abstractions over transport layer
>>>> just to make one or two small functions common.
>>>> Could you be more precise what are you talking about, or could you give
>>>> a link to the mail where you said it. Anything I remember was a
>>>> discussion about ugly magic initialization sequences due to poor
>>>> documentation.
>>>>
>>>> And below promised examples of the error pattern, it was time consuming
>>>> to find them, so please at least read them :)
>>> I've done better, below is what I hope a list of good arguments why the
>>> pattern is a bad idea, and why in some cases it can be justified.
>>>
>>>> Almost exactly the same patterns for the same purpose:
>>>>
>>>> 1. http://lxr.free-electrons.com/source/drivers/net/ieee802154/atusb.c#L63
>>>>     Citation from the code:
>>>>     To reduce the number of error checks in the code, we record the
>>>> first error
>>>>     in atusb->err and reject all subsequent requests until the error is
>>>> cleared.
>>> That's about the worst example you could've used. Have you even looked
>>> at the code that uses this? It's completely crazy. So here we have the
>>> atusb_control_msg() function that stores this error, but then also
>>> propagates it to its caller. One of these callers is atusb_read_reg(),
>>> which also propagates the error or returns the register value if the
>>> read was successful.
>>>
>>> Now the really insane part is how this is then used in something like
>>> atusb_write_subreg():
>>>
>>> 	orig = atusb_read_reg(atusb, reg);
>>> 	tmp = orig & ~mask;
>>> 	tmp |= (value << shift) & mask;
>>> 	
>>> 	if (tmp != orig)
>>> 		ret = atusb_write_reg(atusb, reg, tmp);
>>>
>>> So let's assume that atusb_control_msg() fails in the call to
>>> atusb_read_reg(). You'll be returning an error code, mask out some bits,
>>> OR in new ones and write the result to the register. So not only does
>>> the code not bother to check for errors, but it will also happily go and
>>> corrupt registers when an error has occurred while reading.
>> Not true, in case of error in atusb_read_reg atusb_write_reg will do
>> nothing because atusb->err is set !
>> And this is strong point of the idiom - you will not be able to perform
>> transfer until the error is explicitly cleared.
> Yes, I see now that there's an additional check before further accesses
> can be made. So I have to apologize to the author and it turns out that
> the code isn't crazy. I suppose it's me that's too stupid to understand
> how this works.
>
> I wouldn't have been confused if this was using a more idiomatic way of
> doing things. The mere fact that code will read/modify/write registers
> unconditionally suggests that there's no error checking going on. Even
> if the code doesn't actually corrupt registers, it does have the effect
> of executing a bunch of instructions completely unnecessarily.

If there are really time consuming tasks you can add always error check
before, as I showed below. Anyway this is error path so the delay
because of the idiom will be usually insignificant comparing to time of
hw error handling/recovery/timeouts etc.

>
> To add to the confusion there are other parts in the driver that don't
> seem comfortable using the idiom and you'll actually see things like:
>
> 	ret = atusb_write_subreg(...);
> 	if (ret)
> 		return ret;
>
> If you ask me, that's bound to lead to mistakes at some point.

I agree propagating error in two ways can be misleading, however I guess
in some cases it can be convenient, anyway this is small detail.
I guess it is still easier to forgot about error handling with classical
approach, here you add checks only before actions with side effects, in
classical approach you are obliged to put checks everywhere, to achieve
the same goal.

>
>> With this idiom it is enough to put guards only in one/two/few places,
>> in traditional error handling you just need to put checks after every
>> function call and it is quite common situation that developers forgot to
>> do that, for example look at mipi calls here [1] :)
>> [1]:
>> http://lxr.free-electrons.com/source/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c#L160
>>
>> So the function is correct, but to avoid fooling unaware readers I would
>> add error checking after read:
>>
>>     orig = atusb_read_reg(atusb, reg);
>>     if (atusb->err)
>>         return err;
> But then what's the point of storing the error somewhere in the first
> place?

Because you have still multiple places where you do not need to add checks.
And placing check here will save few cpu clocks, but of course without
it the code is still correct, it is just matter of taste and balance
between short code vs quick error paths.


>
>>>> 2. http://lxr.free-electrons.com/source/drivers/md/dm-thin.c?v=4.4#L917
>>> This is completely different from the panel driver because it's used to
>>> store a value from within callbacks that can't return one.
>> This is internal framework of the driver, so nothing prevents developer
>> from implementing callbacks that return value.
>> Apparently storing error in the object(struct dm_thin_new_mapping) is
>> good and convenient  :)
> No, I think this comes from the fact that the device mapper and block
> I/O frameworks have infrastructure that doesn't allow the error code to
> be directly propagated (see the copy_complete() and overwrite_endio()
> callbacks).
>
>>>> 3. http://lxr.free-electrons.com/source/net/9p/trans_fd.c?v=3.18#L297
>>> Essentially the same thing.
>> The same here.
> Yes, this seems like it could easily be done using propagated error
> codes.
>
>>>> 4.
>>>> http://lxr.free-electrons.com/source/drivers/media/v4l2-core/v4l2-ctrls.c#L2234
>>> Looks like this could be replaced by the more idiomatic use of ERR_PTR()
>>> encoding error codes. But the most significant difference here is that
>>> each use of the handler_set_err() function is followed by a return. So
>>> instead of ignoring errors like you do in the panel drivers, this does
>>> recognize them and aborts early, rather than trying to go through the
>>> remainder of the code, irrespective of how small the chances are of it
>>> succeeding. Or ignoring that /even if/ the remainder didn't fail, the
>>> one operation that fail might have been essential to the operation of
>>> the device.
>> I have an impression that you do not understand the idiom. It does not
>> allow to ignore error - as soon as the error is detected, guard is set
>> and no further harm can be done.
>> Going back to V4L2, look at the usage of the idiom [2]:
>>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_gen, NULL);
>>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_vid, NULL);
>>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL);
>>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL);
>>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL);
>>                  v4l2_ctrl_add_handler(hdl_vid_cap, hdl_loop_cap, NULL);
>>                  if (hdl_vid_cap->error)
>>                          return hdl_vid_cap->error;
>>
>> Again, the point is you do not need to use old construct, which enlarges
>> the code about three times:
>>     err = func(obj,...);
>>     if (err < 0)
>>         return err;
>>
>> Instead you just call func(obj,...), and the burden of error checking is
>> put into the function, but for this err must be stored in object.
>>
>> [2]:
>> http://lxr.free-electrons.com/source/drivers/media/platform/vivid/vivid-ctrls.c#L1630
> There are a couple of pitfalls here: for example what do you do if the
> first two calls fail, but the third succeeds. 

No way, at the beginning of v4l2_ctrl_add_handler there is check:

         if (hdl->error <http://lxr.free-electrons.com/ident?i=error>)
                 return hdl->error <http://lxr.free-electrons.com/ident?i=error>;

In fact this check is also in other functions: handler_new_ref,
v4l2_ctrl_new, handler_new_ref.
With this check after first error no further code is executed.

> The end result will be
> that the whole operation fails. What do you do if one of the operations
> has side-effects that you need to undo? If you don't know which one it
> was, how will you know?

You can always add explicit check after the operation which requires
special handling, like in traditional approach. The point is you do not
need to add checks which just propagate the error: "if (err < 0) return
err;".

The last sentence clearly suggests where the idiom is most suitable -
the object on which you should perform bunch of operations and you are
mainly interested if all operations succeeded. This is for example
creation of V4L2 control handler - we are not interested in handler with
some controls broken, we want fully working handler with all controls or
nothing. Another example are devices attached to some bus which can
fail, if we initialize it we want to perform bunch of writes, if any of
write fails whole initialization fails also.

>
> And yes, this does allow you to ignore errors. The function calls before
> the actual error handling will be executed regardless of errors that are
> stored. While in many cases the code seems to correctly shortcut if the
> guard is set, it also encodes many assumptions, not all of which may be
> understood by morons like me.
>
> The end result is that the code becomes more vulnerable to accidental
> mistakes. These will be hard to spot during review. Keeping code linear
> and simple makes it much more difficult to introduce bugs.

This is very disputable, error paths are the most error prone pieces of
code :) There are many places where these errors are not propagated, or
are propagated incorrectly, I suppose it is mainly due to fact it should
be put everywhere.

>
>>>> 5. http://lxr.free-electrons.com/source/drivers/media/i2c/s5k5baf.c#L451
>>> That's a particularly good example of why you shouldn't be doing this
>>> kind of thing. Consider what would happen in these cases if for example
>>> there was a problem with the I2C interface. There's a bunch of read and
>>> write sequences in that driver that go completely without checking for
>>> errors. 
>> Not true, no further transfer will be performed.
> Indeed. Clearly I wasn't looking closely enough. How about knowing
> whether or not reads or writes succeed? To do so you would have to
> explicitly check the error code again.

If you need this information you can check, if don't you don't :)

>
>>> Imagine how that will look to a user when the communication to a
>>> chip doesn't work. They'll get a load of error messages all saying that
>>> communication timed out, or that the slave isn't responding or what not.
>>> And worse still, for timeouts you're also going to introduce a bunch of
>>> error messages interleaved with potentially other useful messages from
>>> other drivers or the core. Depending on the number of access you have it
>>> might take seconds for all of the error messages to drain before you
>>> notice somewhere at the end of the code that something went wrong and
>>> decide to abort whatever you were trying to do.
>> Again, not true at all.
> So after looking at this is more detail, I'll grant you that it gives
> you a way to avoid checking for errors at every step of the way. There
> is a disadvantage because you keep executing a lot of code
> unnecessarily, and you make the code susceptible to mistakes because
> it contains a non-obvious assumption that contributors to your code may
> not be aware of.
>
>>>>     This is my driver, I mention it here just to show it was not a
>>>> problem to merge it to media subsystem.
>>> That just shows what everybody knows: that maintainers care about
>>> different things at different levels.
>>>
>>>> Similar patterns:
>>>>
>>>> 6. http://lxr.free-electrons.com/source/fs/seq_file.c#L398
>>>>     Do not process object if buffer is full, it allows to do not check
>>>> buffer size after every write, for example:
>>>>>     seq_printf(m, " hardirq-safe locks:            %11lu\n",
>>>>>             nr_hardirq_safe);
>>>>>     seq_printf(m, " hardirq-unsafe locks:          %11lu\n",
>>>>>             nr_hardirq_unsafe);
>>>>>     seq_printf(m, " softirq-safe locks:            %11lu\n",
>>>>>             nr_softirq_safe);
>>>>>     seq_printf(m, " softirq-unsafe locks:          %11lu\n",
>>>>>             nr_softirq_unsafe);
>>>>>     seq_printf(m, " irq-safe locks:                %11lu\n",
>>>>>             nr_irq_safe);
>>>>>     seq_printf(m, " irq-unsafe locks:              %11lu\n",
>>>>>             nr_irq_unsafe);
>>>>>
>>>>>     seq_printf(m, " hardirq-read-safe locks:       %11lu\n",
>>>>>             nr_hardirq_read_safe);
>>>>>     seq_printf(m, " hardirq-read-unsafe locks:     %11lu\n",
>>>>>             nr_hardirq_read_unsafe);
>>>>>     seq_printf(m, " softirq-read-safe locks:       %11lu\n",
>>>>>             nr_softirq_read_safe);
>>>>>     seq_printf(m, " softirq-read-unsafe locks:     %11lu\n",
>>>>>             nr_softirq_read_unsafe);
>>>>>     seq_printf(m, " irq-read-safe locks:           %11lu\n",
>>>>>             nr_irq_read_safe);
>>>>>     seq_printf(m, " irq-read-unsafe locks:         %11lu\n",
>>>>>             nr_irq_read_unsafe);
>>>>>
>>>>>     seq_printf(m, " uncategorized locks:           %11lu\n",
>>>>>             nr_uncategorized);
>>>>>     seq_printf(m, " unused locks:                  %11lu\n",
>>>>>             nr_unused);
>>>>>     seq_printf(m, " max locking depth:             %11u\n",
>>>>>             max_lockdep_depth);
>>>> Now try to imagine how it would look like if you add error checking
>>>> after each call.
>>> I think that's a fairly sane use-case for this kind of pattern. The big
>>> difference is that this condition is checked in subsequent accesses to
>>> shortcut failure paths. It's also very different in that it performs
>>> writes to memory and those aren't going to fail. The root cause of this
>>> overflow would be static in nature and likely found doing basic testing
>>> and fixed by making the buffer larger.
>>>
>>> That's contrary to a driver doing I/O (I2C, SPI, DSI, ...) that can fail
>>> unexpectedly for any number of reasons.
>> The idiom is still the same - storing error state in the object allows
>> to move error checking inside called functions, removing much of
>> redundant code from caller.
> Yes, the idiom is the same, but the use-case is also completely
> different, so I don't think you can apply the same rules. If you've got
> communication with an external peripheral, like in the case of SPI, I2C
> or DSI, I think the best thing to do is abort as early as possible on
> failure. The most likely reason for failure would be a persistent cause
> on the bus, in which case none of your accesses will succeed. If so I
> think it makes sense to let the user know what exactly went wrong and
> leave it at that. Continuing to execute code, even if no physical access
> to the hardware happens anymore, is a waste of time.
>
> Failures on such busses can happen for any number of reasons, and they
> can happen sporadically, so it's important to catch errors. For seq_buf
> in particular you have a static failure case, which is if somebody were
> trying to add more characters to the buffer than would fit. But callers
> will, most of the time, put exactly the same number of characters into
> that buffer. So if there was a failure once they'd notice quickly and
> the issue would get fixed, at which point it becomes very unlikely to
> ever happen again.
>
> So seq_buf is not expected to fail, whereas with peripheral busses you
> can't make that assumption.

The error is logged as soon as it happens, you can easily check that
every time error is set, it is also logged.
This is another advantage of the idiom - you do not need to analyze, if
the error some function has returned was logged already, or you should
do it in the caller.
With the idiom you can log error in every place the error is set, to be
sure that it will not be silently omitted, of course sometimes you can
want to log in wider context, the idiom of course allows it.

>
>>>> 7. http://lxr.free-electrons.com/source/lib/devres.c#L129
>>>>     Postponed error check:
>>>>> */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*
>>>>> /*b*/*/ase = devm_ioremap_resource(&pdev->dev, res);/*
>>>>> */if (IS_ERR(base))/*
>>>>> /**/*/return PTR_ERR(base);/*
>>> Heh, I wrote that =)
>>>
>>> This is also quite different from your usage in the panel driver. We do
>>> not simply ignore failure from platform_get_resource(), instead we leave
>>> it up to devm_ioremap_resource() to turn it into a meaningful error code
>>> and message. The function does this very early on, so the error
>>> condition is not ignored, as it is in the panel driver.
>> Claims about the panel again not true, reasons described above.
>>
>> The similarity here is that you do not perform error checking in the
>> caller, you just blindly pass the 'object' to the next function, and
>> still everything is handled correctly.
> But we're not passing any object to the next function. We're passing the
> return value from an earlier call, one line above. We're not hiding an
> error code in some driver-specific structure. So the code is completely
> explicit.
>
> Maybe I should say a few words about why this is important to me because
> you may think this to be somewhat pedantic. This is fundamentally caused
> by the fact that once I merge code into a tree that I maintain, then the
> responsibility for that code becomes mine. That means I will not only be
> held accountable if it doesn't work, breaks or upsets build farms all
> over the world. It also means that I am the one that will need to make
> changes to it along the way. If the code is difficult for me to
> understand because it doesn't behave the way I'm used to, then that
> makes my life harder.
>
> Sometimes authors do stick around and care about their drivers, but
> often they'll just disappear, or ignore patches and bug reports about
> their drivers. So if worst comes to worst people will look to me to fix
> their problems. That's why I want it to be easy to read and modify the
> code that I maintain, because that way when users show up and complain
> about drivers I don't have to spend hours trying to understand what the
> original author was trying to achieve.

But this is common problem of all subsystems, there is nothing special
about panels.

>
> The above is a variant of what others have termed "obviously correct".
> One good way to make code obviously correct is to make it simple.
>

I understand what you want to say, but I cannot fully agree with it.
With such conservative approach Linux would not evolve at all :)
We need some balance between "code exactly as maintainer says, otherwise
you will be nacked" and "do whatever you want",
but I think in case of panel subsystem requirements are unnecessarily
very strict.

Ok, this part of the discussion is less technical, I guess it would be
good to have opinions of other maintainers about the subject.

Regards
Andrzej
Andi Shyti Feb. 9, 2017, 8:20 a.m. UTC | #41
Hi,

just for my knowledge and all those working on the tm2(e)
devices, is this patch going in or not?

If not, Thierry, could you please say what Hoegeun needs to do in
order to get this in?

Thanks,
Andi

On Wed, Jan 11, 2017 at 03:33:58PM +0900, Hoegeun Kwon wrote:
> This patch add support for MIPI-DSI based S6E3HA2 AMOLED panel
> driver. This panel has 1440x2560 resolution in 5.7-inch physical
> panel in the TM2 device.
> 
> Signed-off-by: Donghwa Lee <dh09.lee@samsung.com>
> Signed-off-by: Hyungwon Hwang <human.hwang@samsung.com>
> Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
> Tested-by: Chanwoo Choi <cw00.choi@samsung.com>
> Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
> ---
>  drivers/gpu/drm/panel/Kconfig                 |   6 +
>  drivers/gpu/drm/panel/Makefile                |   1 +
>  drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c | 754 ++++++++++++++++++++++++++
>  3 files changed, 761 insertions(+)
>  create mode 100644 drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
> 
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 62aba97..d913c83 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -52,6 +52,12 @@ config DRM_PANEL_PANASONIC_VVX10F034N00
>  	  WUXGA (1920x1200) Novatek NT1397-based DSI panel as found in some
>  	  Xperia Z2 tablets
>  
> +config DRM_PANEL_SAMSUNG_S6E3HA2
> +	tristate "Samsung S6E3HA2 DSI video mode panel"
> +	depends on OF
> +	depends on DRM_MIPI_DSI
> +	select VIDEOMODE_HELPERS
> +
>  config DRM_PANEL_SAMSUNG_S6E8AA0
>  	tristate "Samsung S6E8AA0 DSI video mode panel"
>  	depends on OF
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index a5c7ec0..1d483b0 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -3,6 +3,7 @@ obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
>  obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
>  obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
>  obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
> +obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
>  obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>  obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>  obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
> diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
> new file mode 100644
> index 0000000..0b9c6f4
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
> @@ -0,0 +1,754 @@
> +/*
> + * MIPI-DSI based s6e3ha2 AMOLED 5.7 inch panel driver.
> + *
> + * Copyright (c) 2016 Samsung Electronics Co., Ltd.
> + * Donghwa Lee <dh09.lee@samsung.com>
> + * Hyungwon Hwang <human.hwang@samsung.com>
> + * Hoegeun Kwon <hoegeun.kwon@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.h>
> +#include <linux/backlight.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/regulator/consumer.h>
> +
> +#define S6E3HA2_MIN_BRIGHTNESS		0
> +#define S6E3HA2_MAX_BRIGHTNESS		100
> +#define S6E3HA2_DEFAULT_BRIGHTNESS	80
> +
> +#define S6E3HA2_NUM_GAMMA_STEPS		46
> +#define S6E3HA2_GAMMA_CMD_CNT		35
> +#define S6E3HA2_VINT_STATUS_MAX		10
> +
> +static const u8 gamma_tbl[S6E3HA2_NUM_GAMMA_STEPS][S6E3HA2_GAMMA_CMD_CNT] = {
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x82, 0x83,
> +	  0x85, 0x88, 0x8b, 0x8b, 0x84, 0x88, 0x82, 0x82, 0x89, 0x86, 0x8c,
> +	  0x94, 0x84, 0xb1, 0xaf, 0x8e, 0xcf, 0xad, 0xc9, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x84, 0x84,
> +	  0x85, 0x87, 0x8b, 0x8a, 0x84, 0x88, 0x82, 0x82, 0x89, 0x86, 0x8a,
> +	  0x93, 0x84, 0xb0, 0xae, 0x8e, 0xc9, 0xa8, 0xc5, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x8a, 0x8a, 0x84, 0x88, 0x81, 0x84, 0x8a, 0x88, 0x8a,
> +	  0x91, 0x84, 0xb1, 0xae, 0x8b, 0xd5, 0xb2, 0xcc, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x8a, 0x8a, 0x84, 0x87, 0x81, 0x84, 0x8a, 0x87, 0x8a,
> +	  0x91, 0x85, 0xae, 0xac, 0x8a, 0xc3, 0xa3, 0xc0, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x85, 0x85,
> +	  0x86, 0x85, 0x88, 0x89, 0x84, 0x89, 0x82, 0x84, 0x87, 0x85, 0x8b,
> +	  0x91, 0x88, 0xad, 0xab, 0x8a, 0xb7, 0x9b, 0xb6, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x89, 0x8a, 0x84, 0x89, 0x83, 0x83, 0x86, 0x84, 0x8b,
> +	  0x90, 0x84, 0xb0, 0xae, 0x8b, 0xce, 0xad, 0xc8, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> +	  0x85, 0x87, 0x89, 0x8a, 0x83, 0x87, 0x82, 0x85, 0x88, 0x87, 0x89,
> +	  0x8f, 0x84, 0xac, 0xaa, 0x89, 0xb1, 0x98, 0xaf, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x88, 0x89, 0x84, 0x88, 0x83, 0x82, 0x85, 0x84, 0x8c,
> +	  0x91, 0x86, 0xac, 0xaa, 0x89, 0xc2, 0xa5, 0xbd, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x85, 0x87, 0x89, 0x8a, 0x83, 0x87, 0x82, 0x85, 0x88, 0x87, 0x88,
> +	  0x8b, 0x82, 0xad, 0xaa, 0x8a, 0xc2, 0xa5, 0xbd, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x87, 0x89, 0x84, 0x88, 0x83, 0x82, 0x85, 0x84, 0x8a,
> +	  0x8e, 0x84, 0xae, 0xac, 0x89, 0xda, 0xb7, 0xd0, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x85, 0x86, 0x87, 0x89, 0x84, 0x88, 0x83, 0x80, 0x83, 0x82, 0x8b,
> +	  0x8e, 0x85, 0xac, 0xaa, 0x89, 0xc8, 0xaa, 0xc1, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x85, 0x86, 0x87, 0x89, 0x81, 0x85, 0x81, 0x84, 0x86, 0x84, 0x8c,
> +	  0x8c, 0x84, 0xa9, 0xa8, 0x87, 0xa3, 0x92, 0xa1, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x85, 0x86, 0x87, 0x89, 0x84, 0x86, 0x83, 0x80, 0x83, 0x81, 0x8c,
> +	  0x8d, 0x84, 0xaa, 0xaa, 0x89, 0xce, 0xaf, 0xc5, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x85, 0x86, 0x87, 0x89, 0x81, 0x83, 0x80, 0x83, 0x85, 0x85, 0x8c,
> +	  0x8c, 0x84, 0xa8, 0xa8, 0x88, 0xb5, 0x9f, 0xb0, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x86, 0x86, 0x87, 0x88, 0x81, 0x83, 0x80, 0x83, 0x85, 0x85, 0x8c,
> +	  0x8b, 0x84, 0xab, 0xa8, 0x86, 0xd4, 0xb4, 0xc9, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x86, 0x86, 0x87, 0x88, 0x81, 0x83, 0x80, 0x84, 0x84, 0x85, 0x8b,
> +	  0x8a, 0x83, 0xa6, 0xa5, 0x84, 0xbb, 0xa4, 0xb3, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
> +	  0x86, 0x85, 0x86, 0x86, 0x82, 0x85, 0x81, 0x82, 0x83, 0x84, 0x8e,
> +	  0x8b, 0x83, 0xa4, 0xa3, 0x8a, 0xa1, 0x93, 0x9d, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x82, 0x82, 0x84, 0x8e,
> +	  0x8b, 0x83, 0xa4, 0xa2, 0x86, 0xc1, 0xa9, 0xb7, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x82, 0x82, 0x84, 0x8d,
> +	  0x89, 0x82, 0xa2, 0xa1, 0x84, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
> +	  0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x83, 0x83, 0x85, 0x8c,
> +	  0x87, 0x7f, 0xa2, 0x9d, 0x88, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xbb, 0x00, 0xc5, 0x00, 0xb4, 0x87, 0x86, 0x86, 0x84, 0x83,
> +	  0x86, 0x87, 0x87, 0x87, 0x80, 0x82, 0x7f, 0x86, 0x86, 0x88, 0x8a,
> +	  0x84, 0x7e, 0x9d, 0x9c, 0x82, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xbd, 0x00, 0xc7, 0x00, 0xb7, 0x87, 0x85, 0x85, 0x84, 0x83,
> +	  0x86, 0x86, 0x86, 0x88, 0x81, 0x83, 0x80, 0x83, 0x84, 0x85, 0x8a,
> +	  0x85, 0x7e, 0x9c, 0x9b, 0x85, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xc0, 0x00, 0xca, 0x00, 0xbb, 0x87, 0x86, 0x85, 0x83, 0x83,
> +	  0x85, 0x86, 0x86, 0x88, 0x81, 0x83, 0x80, 0x84, 0x85, 0x86, 0x89,
> +	  0x83, 0x7d, 0x9c, 0x99, 0x87, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xc4, 0x00, 0xcd, 0x00, 0xbe, 0x87, 0x86, 0x85, 0x83, 0x83,
> +	  0x86, 0x85, 0x85, 0x87, 0x81, 0x82, 0x80, 0x82, 0x82, 0x83, 0x8a,
> +	  0x85, 0x7f, 0x9f, 0x9b, 0x86, 0xb4, 0xa1, 0xac, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xc7, 0x00, 0xd0, 0x00, 0xc2, 0x87, 0x85, 0x85, 0x83, 0x82,
> +	  0x85, 0x85, 0x85, 0x86, 0x82, 0x83, 0x80, 0x82, 0x82, 0x84, 0x87,
> +	  0x86, 0x80, 0x9e, 0x9a, 0x87, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xca, 0x00, 0xd2, 0x00, 0xc5, 0x87, 0x85, 0x84, 0x82, 0x82,
> +	  0x84, 0x85, 0x85, 0x86, 0x81, 0x82, 0x7f, 0x82, 0x82, 0x84, 0x88,
> +	  0x86, 0x81, 0x9d, 0x98, 0x86, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xce, 0x00, 0xd6, 0x00, 0xca, 0x86, 0x85, 0x84, 0x83, 0x83,
> +	  0x85, 0x84, 0x84, 0x85, 0x81, 0x82, 0x80, 0x81, 0x81, 0x82, 0x89,
> +	  0x86, 0x81, 0x9c, 0x97, 0x86, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xd1, 0x00, 0xd9, 0x00, 0xce, 0x86, 0x84, 0x83, 0x83, 0x82,
> +	  0x85, 0x85, 0x85, 0x86, 0x81, 0x83, 0x81, 0x82, 0x82, 0x83, 0x86,
> +	  0x83, 0x7f, 0x99, 0x95, 0x86, 0xbb, 0xa4, 0xb3, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xd4, 0x00, 0xdb, 0x00, 0xd1, 0x86, 0x85, 0x83, 0x83, 0x82,
> +	  0x85, 0x84, 0x84, 0x85, 0x80, 0x83, 0x82, 0x80, 0x80, 0x81, 0x87,
> +	  0x84, 0x81, 0x98, 0x93, 0x85, 0xae, 0x9c, 0xa8, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xd8, 0x00, 0xde, 0x00, 0xd6, 0x86, 0x84, 0x83, 0x81, 0x81,
> +	  0x83, 0x85, 0x85, 0x85, 0x82, 0x83, 0x81, 0x81, 0x81, 0x83, 0x86,
> +	  0x84, 0x80, 0x98, 0x91, 0x85, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xdc, 0x00, 0xe2, 0x00, 0xda, 0x85, 0x84, 0x83, 0x82, 0x82,
> +	  0x84, 0x84, 0x84, 0x85, 0x81, 0x82, 0x82, 0x80, 0x80, 0x81, 0x83,
> +	  0x82, 0x7f, 0x99, 0x93, 0x86, 0x94, 0x8b, 0x92, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xdf, 0x00, 0xe5, 0x00, 0xde, 0x85, 0x84, 0x82, 0x82, 0x82,
> +	  0x84, 0x83, 0x83, 0x84, 0x81, 0x81, 0x80, 0x83, 0x82, 0x84, 0x82,
> +	  0x81, 0x7f, 0x99, 0x92, 0x86, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> +	  0x82, 0x83, 0x83, 0x84, 0x80, 0x81, 0x80, 0x83, 0x83, 0x84, 0x80,
> +	  0x81, 0x7c, 0x99, 0x92, 0x87, 0xa1, 0x93, 0x9d, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x85, 0x84, 0x83, 0x81, 0x81,
> +	  0x82, 0x82, 0x82, 0x83, 0x80, 0x81, 0x80, 0x81, 0x80, 0x82, 0x83,
> +	  0x82, 0x80, 0x91, 0x8d, 0x83, 0x9a, 0x90, 0x96, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> +	  0x82, 0x83, 0x83, 0x84, 0x80, 0x81, 0x80, 0x81, 0x80, 0x82, 0x83,
> +	  0x81, 0x7f, 0x91, 0x8c, 0x82, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> +	  0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x82, 0x82,
> +	  0x82, 0x7f, 0x94, 0x89, 0x84, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> +	  0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x82, 0x83,
> +	  0x82, 0x7f, 0x91, 0x85, 0x81, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
> +	  0x82, 0x83, 0x83, 0x83, 0x80, 0x80, 0x7f, 0x83, 0x82, 0x84, 0x83,
> +	  0x82, 0x7f, 0x90, 0x84, 0x81, 0x9a, 0x90, 0x96, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x80, 0x80,
> +	  0x82, 0x83, 0x83, 0x83, 0x80, 0x80, 0x7f, 0x80, 0x80, 0x81, 0x81,
> +	  0x82, 0x83, 0x7e, 0x80, 0x7c, 0xa4, 0x97, 0x9f, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xe9, 0x00, 0xec, 0x00, 0xe8, 0x84, 0x83, 0x82, 0x81, 0x81,
> +	  0x82, 0x82, 0x82, 0x83, 0x7f, 0x7f, 0x7f, 0x81, 0x80, 0x82, 0x83,
> +	  0x83, 0x84, 0x79, 0x7c, 0x79, 0xb1, 0xa0, 0xaa, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xed, 0x00, 0xf0, 0x00, 0xec, 0x83, 0x83, 0x82, 0x80, 0x80,
> +	  0x81, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7e, 0x81, 0x81, 0x82, 0x80,
> +	  0x81, 0x81, 0x84, 0x84, 0x83, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xf1, 0x00, 0xf4, 0x00, 0xf1, 0x83, 0x82, 0x82, 0x80, 0x80,
> +	  0x81, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x7d,
> +	  0x7e, 0x7f, 0x84, 0x84, 0x83, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xf6, 0x00, 0xf7, 0x00, 0xf5, 0x82, 0x82, 0x81, 0x80, 0x80,
> +	  0x80, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x82,
> +	  0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 0x00, 0xfa, 0x00, 0xfb, 0x00, 0xfa, 0x81, 0x81, 0x81, 0x80, 0x80,
> +	  0x80, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
> +	  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
> +	  0x00, 0x00 },
> +	{ 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,
> +	  0x00, 0x00 },
> +	{ 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,
> +	  0x00, 0x00 }
> +};
> +
> +unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = {
> +	0x18, 0x19, 0x1a, 0x1b, 0x1c,
> +	0x1d, 0x1e, 0x1f, 0x20, 0x21
> +};
> +
> +struct s6e3ha2 {
> +	struct device *dev;
> +	struct drm_panel panel;
> +	struct backlight_device *bl_dev;
> +
> +	struct regulator_bulk_data supplies[2];
> +	struct gpio_desc *reset_gpio;
> +	struct gpio_desc *enable_gpio;
> +
> +	/* This field is tested by functions directly accessing DSI bus before
> +	 * transfer, transfer is skipped if it is set. In case of transfer
> +	 * failure or unexpected response the field is set to error value.
> +	 * Such construct allows to eliminate many checks in higher level
> +	 * functions.
> +	 */
> +	int error;
> +};
> +
> +static int  s6e3ha2_clear_error(struct s6e3ha2 *ctx)
> +{
> +	int ret = ctx->error;
> +
> +	ctx->error = 0;
> +	return ret;
> +}
> +
> +static void s6e3ha2_dcs_write(struct s6e3ha2 *ctx, const void *data, size_t len)
> +{
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> +	ssize_t ret;
> +
> +	if (ctx->error < 0)
> +		return;
> +
> +	ret = mipi_dsi_dcs_write_buffer(dsi, data, len);
> +	if (ret < 0) {
> +		dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n",
> +						ret, (int)len, data);
> +		ctx->error = ret;
> +	}
> +}
> +
> +#define s6e3ha2_dcs_write_seq_static(ctx, seq...) do {	\
> +	static const u8 d[] = { seq };			\
> +	s6e3ha2_dcs_write(ctx, d, ARRAY_SIZE(d));	\
> +} while (0)
> +
> +static void s6e3ha2_test_key_on_f0(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xf0, 0x5a, 0x5a);
> +}
> +
> +static void s6e3ha2_test_key_off_f0(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xf0, 0xa5, 0xa5);
> +}
> +
> +static void s6e3ha2_test_key_on_fc(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xfc, 0x5a, 0x5a);
> +}
> +
> +static void s6e3ha2_test_key_off_fc(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xfc, 0xa5, 0xa5);
> +}
> +
> +static void s6e3ha2_single_dsi_set(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xf2, 0x67);
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xf9, 0x09);
> +}
> +
> +static void s6e3ha2_freq_calibration(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xfd, 0x1c);
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20, 0x39);
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0xa0);
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20);
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x12, 0x62,
> +		0x40, 0x80, 0xc0, 0x28, 0x28, 0x28, 0x28, 0x39, 0xc5);
> +}
> +
> +static void s6e3ha2_aor_control(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xb2, 0x03, 0x10);
> +}
> +
> +static void s6e3ha2_caps_elvss_set(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xb6, 0x9c, 0x0a);
> +}
> +
> +static void s6e3ha2_acl_off(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0x55, 0x00);
> +}
> +
> +static void s6e3ha2_acl_off_opr(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xb5, 0x40);
> +}
> +
> +static void s6e3ha2_test_global(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xb0, 0x07);
> +}
> +
> +static void s6e3ha2_test(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xb8, 0x19);
> +}
> +
> +static void s6e3ha2_touch_hsync_on1(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx,
> +			0xbd, 0x33, 0x11, 0x02, 0x16, 0x02, 0x16);
> +}
> +
> +static void s6e3ha2_pentile_control(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xc0, 0x00, 0x00, 0xd8, 0xd8);
> +}
> +
> +static void s6e3ha2_poc_global(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xb0, 0x20);
> +}
> +
> +static void s6e3ha2_poc_setting(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x08);
> +}
> +
> +static void s6e3ha2_pcd_set_off(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xcc, 0x40, 0x51);
> +}
> +
> +static void s6e3ha2_err_fg_set(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xed, 0x44);
> +}
> +
> +static void s6e3ha2_hbm_off(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0x53, 0x00);
> +}
> +
> +static void s6e3ha2_te_start_setting(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xb9, 0x10, 0x09, 0xff, 0x00, 0x09);
> +}
> +
> +static void s6e3ha2_gamma_update(struct s6e3ha2 *ctx)
> +{
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xf7, 0x03);
> +	ndelay(100); /* need for 100ns delay */
> +	s6e3ha2_dcs_write_seq_static(ctx, 0xf7, 0x00);
> +}
> +
> +static int s6e3ha2_get_brightness(struct backlight_device *bl_dev)
> +{
> +	return bl_dev->props.brightness;
> +}
> +
> +static void s6e3ha2_set_vint(struct s6e3ha2 *ctx)
> +{
> +	struct backlight_device *bl_dev = ctx->bl_dev;
> +	unsigned int brightness = bl_dev->props.brightness;
> +	unsigned char data[] = { 0xf4, 0x8b,
> +			vint_table[brightness * (S6E3HA2_VINT_STATUS_MAX - 1) /
> +			S6E3HA2_MAX_BRIGHTNESS] };
> +
> +	s6e3ha2_dcs_write(ctx, data, ARRAY_SIZE(data));
> +}
> +
> +static unsigned int s6e3ha2_get_brightness_index(unsigned int brightness)
> +{
> +	return (brightness * (S6E3HA2_NUM_GAMMA_STEPS - 1)) /
> +		S6E3HA2_MAX_BRIGHTNESS;
> +}
> +
> +static int s6e3ha2_update_gamma(struct s6e3ha2 *ctx, unsigned int brightness)
> +{
> +	struct backlight_device *bl_dev = ctx->bl_dev;
> +	unsigned int index = s6e3ha2_get_brightness_index(brightness);
> +	u8 data[S6E3HA2_GAMMA_CMD_CNT + 1] = { 0xca, };
> +
> +	memcpy(data + 1, gamma_tbl + index, S6E3HA2_GAMMA_CMD_CNT);
> +	s6e3ha2_dcs_write(ctx, data, ARRAY_SIZE(data));
> +
> +	s6e3ha2_gamma_update(ctx);
> +	bl_dev->props.brightness = brightness;
> +
> +	return 0;
> +}
> +
> +static int s6e3ha2_set_brightness(struct backlight_device *bl_dev)
> +{
> +	struct s6e3ha2 *ctx = bl_get_data(bl_dev);
> +	unsigned int brightness = bl_dev->props.brightness;
> +
> +	if (brightness < S6E3HA2_MIN_BRIGHTNESS ||
> +		brightness > bl_dev->props.max_brightness) {
> +		dev_err(ctx->dev, "Invalid brightness: %u\n", brightness);
> +		return -EINVAL;
> +	}
> +
> +	if (bl_dev->props.power > FB_BLANK_NORMAL)
> +		return -EPERM;
> +
> +	s6e3ha2_test_key_on_f0(ctx);
> +	s6e3ha2_update_gamma(ctx, brightness);
> +	s6e3ha2_aor_control(ctx);
> +	s6e3ha2_set_vint(ctx);
> +	s6e3ha2_test_key_off_f0(ctx);
> +
> +	return ctx->error;
> +}
> +
> +static const struct backlight_ops s6e3ha2_bl_ops = {
> +	.get_brightness = s6e3ha2_get_brightness,
> +	.update_status = s6e3ha2_set_brightness,
> +};
> +
> +static int s6e3ha2_panel_init(struct s6e3ha2 *ctx)
> +{
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> +	int ret;
> +
> +	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +	if (ret < 0)
> +		return ret;
> +	usleep_range(5000, 6000);
> +
> +	s6e3ha2_test_key_on_f0(ctx);
> +	s6e3ha2_single_dsi_set(ctx);
> +	s6e3ha2_test_key_on_fc(ctx);
> +	s6e3ha2_freq_calibration(ctx);
> +	s6e3ha2_test_key_off_fc(ctx);
> +	s6e3ha2_test_key_off_f0(ctx);
> +
> +	return 0;
> +}
> +
> +static int s6e3ha2_power_off(struct s6e3ha2 *ctx)
> +{
> +	return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> +}
> +
> +static int s6e3ha2_disable(struct drm_panel *panel)
> +{
> +	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> +	int ret;
> +
> +	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_set_display_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(40);
> +	ctx->bl_dev->props.power = FB_BLANK_NORMAL;
> +
> +	return 0;
> +}
> +
> +static int s6e3ha2_unprepare(struct drm_panel *panel)
> +{
> +	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
> +	int ret;
> +
> +	ret = s6e3ha2_clear_error(ctx);
> +	if (!ret)
> +		ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
> +
> +	return s6e3ha2_power_off(ctx);
> +}
> +
> +static int s6e3ha2_power_on(struct s6e3ha2 *ctx)
> +{
> +	int ret;
> +
> +	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(120);
> +
> +	gpiod_set_value(ctx->enable_gpio, 0);
> +	usleep_range(5000, 6000);
> +	gpiod_set_value(ctx->enable_gpio, 1);
> +
> +	gpiod_set_value(ctx->reset_gpio, 1);
> +	usleep_range(5000, 6000);
> +	gpiod_set_value(ctx->reset_gpio, 0);
> +	usleep_range(5000, 6000);
> +
> +	return 0;
> +}
> +static int s6e3ha2_prepare(struct drm_panel *panel)
> +{
> +	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
> +	int ret;
> +
> +	ret = s6e3ha2_power_on(ctx);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = s6e3ha2_panel_init(ctx);
> +	if (ret < 0)
> +		goto err;
> +
> +	ret = s6e3ha2_clear_error(ctx);
> +	if (ret < 0)
> +		goto err;
> +
> +	ctx->bl_dev->props.power = FB_BLANK_NORMAL;
> +
> +	return 0;
> +
> +err:
> +	s6e3ha2_power_off(ctx);
> +	return ret;
> +}
> +
> +static int s6e3ha2_enable(struct drm_panel *panel)
> +{
> +	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> +	int ret;
> +
> +	/* common setting */
> +	ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> +	if (ret < 0)
> +		return ret;
> +
> +	s6e3ha2_test_key_on_f0(ctx);
> +	s6e3ha2_test_key_on_fc(ctx);
> +	s6e3ha2_touch_hsync_on1(ctx);
> +	s6e3ha2_pentile_control(ctx);
> +	s6e3ha2_poc_global(ctx);
> +	s6e3ha2_poc_setting(ctx);
> +	s6e3ha2_test_key_off_fc(ctx);
> +
> +	/* pcd setting off for TB */
> +	s6e3ha2_pcd_set_off(ctx);
> +	s6e3ha2_err_fg_set(ctx);
> +	s6e3ha2_te_start_setting(ctx);
> +
> +	/* brightness setting */
> +	s6e3ha2_set_brightness(ctx->bl_dev);
> +	s6e3ha2_aor_control(ctx);
> +	s6e3ha2_caps_elvss_set(ctx);
> +	s6e3ha2_gamma_update(ctx);
> +	s6e3ha2_acl_off(ctx);
> +	s6e3ha2_acl_off_opr(ctx);
> +	s6e3ha2_hbm_off(ctx);
> +
> +	/* elvss temp compensation */
> +	s6e3ha2_test_global(ctx);
> +	s6e3ha2_test(ctx);
> +	s6e3ha2_test_key_off_f0(ctx);
> +
> +	if (ctx->error != 0)
> +		return ctx->error;
> +
> +	ret = mipi_dsi_dcs_set_display_on(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
> +
> +	return 0;
> +}
> +
> +static const struct drm_display_mode default_mode = {
> +	.clock = 222372,
> +	.hdisplay = 1440,
> +	.hsync_start = 1440 + 1,
> +	.hsync_end = 1440 + 1 + 1,
> +	.htotal = 1440 + 1 + 1 + 1,
> +	.vdisplay = 2560,
> +	.vsync_start = 2560 + 1,
> +	.vsync_end = 2560 + 1 + 1,
> +	.vtotal = 2560 + 1 + 1 + 15,
> +	.vrefresh = 60,
> +	.flags = 0,
> +};
> +
> +static int s6e3ha2_get_modes(struct drm_panel *panel)
> +{
> +	struct drm_connector *connector = panel->connector;
> +	struct drm_display_mode *mode;
> +
> +	mode = drm_mode_duplicate(panel->drm, &default_mode);
> +	if (!mode) {
> +		DRM_ERROR("failed to add mode %ux%ux@%u\n",
> +				default_mode.hdisplay, default_mode.vdisplay,
> +				default_mode.vrefresh);
> +		return -ENOMEM;
> +	}
> +
> +	drm_mode_set_name(mode);
> +
> +	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
> +	drm_mode_probed_add(connector, mode);
> +
> +	connector->display_info.width_mm = 71;
> +	connector->display_info.height_mm = 125;
> +
> +	return 1;
> +}
> +
> +static const struct drm_panel_funcs s6e3ha2_drm_funcs = {
> +	.disable = s6e3ha2_disable,
> +	.unprepare = s6e3ha2_unprepare,
> +	.prepare = s6e3ha2_prepare,
> +	.enable = s6e3ha2_enable,
> +	.get_modes = s6e3ha2_get_modes,
> +};
> +
> +static int s6e3ha2_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct device *dev = &dsi->dev;
> +	struct s6e3ha2 *ctx;
> +	int ret;
> +
> +	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx)
> +		return -ENOMEM;
> +
> +	mipi_dsi_set_drvdata(dsi, ctx);
> +
> +	ctx->dev = dev;
> +
> +	dsi->lanes = 4;
> +	dsi->format = MIPI_DSI_FMT_RGB888;
> +	dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
> +
> +	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) {
> +		dev_err(dev, "failed to get regulators: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(ctx->reset_gpio)) {
> +		dev_err(dev, "cannot get reset-gpios %ld\n",
> +			PTR_ERR(ctx->reset_gpio));
> +		return PTR_ERR(ctx->reset_gpio);
> +	}
> +
> +	ctx->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
> +	if (IS_ERR(ctx->enable_gpio)) {
> +		dev_err(dev, "cannot get enable-gpios %ld\n",
> +			PTR_ERR(ctx->enable_gpio));
> +		return PTR_ERR(ctx->enable_gpio);
> +	}
> +
> +	ctx->bl_dev = backlight_device_register("s6e3ha2", dev, ctx,
> +						&s6e3ha2_bl_ops, NULL);
> +	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 = S6E3HA2_MAX_BRIGHTNESS;
> +	ctx->bl_dev->props.brightness = S6E3HA2_DEFAULT_BRIGHTNESS;
> +	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
> +
> +	drm_panel_init(&ctx->panel);
> +	ctx->panel.dev = dev;
> +	ctx->panel.funcs = &s6e3ha2_drm_funcs;
> +
> +	ret = drm_panel_add(&ctx->panel);
> +	if (ret < 0)
> +		goto unregister_backlight;
> +
> +	ret = mipi_dsi_attach(dsi);
> +	if (ret < 0)
> +		goto remove_panel;
> +
> +	return ret;
> +
> +remove_panel:
> +	drm_panel_remove(&ctx->panel);
> +
> +unregister_backlight:
> +	backlight_device_unregister(ctx->bl_dev);
> +
> +	return ret;
> +}
> +
> +static int s6e3ha2_remove(struct mipi_dsi_device *dsi)
> +{
> +	struct s6e3ha2 *ctx = mipi_dsi_get_drvdata(dsi);
> +
> +	mipi_dsi_detach(dsi);
> +	drm_panel_remove(&ctx->panel);
> +	backlight_device_unregister(ctx->bl_dev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id s6e3ha2_of_match[] = {
> +	{ .compatible = "samsung,s6e3ha2" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, s6e3ha2_of_match);
> +
> +static struct mipi_dsi_driver s6e3ha2_driver = {
> +	.probe = s6e3ha2_probe,
> +	.remove = s6e3ha2_remove,
> +	.driver = {
> +		.name = "panel-samsung-s6e3ha2",
> +		.of_match_table = s6e3ha2_of_match,
> +	},
> +};
> +module_mipi_dsi_driver(s6e3ha2_driver);
> +
> +MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
> +MODULE_AUTHOR("Hyungwon Hwang <human.hwang@samsung.com>");
> +MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
> +MODULE_DESCRIPTION("MIPI-DSI based s6e3ha2 AMOLED Panel Driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 1.9.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 62aba97..d913c83 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -52,6 +52,12 @@  config DRM_PANEL_PANASONIC_VVX10F034N00
 	  WUXGA (1920x1200) Novatek NT1397-based DSI panel as found in some
 	  Xperia Z2 tablets
 
+config DRM_PANEL_SAMSUNG_S6E3HA2
+	tristate "Samsung S6E3HA2 DSI video mode panel"
+	depends on OF
+	depends on DRM_MIPI_DSI
+	select VIDEOMODE_HELPERS
+
 config DRM_PANEL_SAMSUNG_S6E8AA0
 	tristate "Samsung S6E8AA0 DSI video mode panel"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index a5c7ec0..1d483b0 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -3,6 +3,7 @@  obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
 obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
+obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
new file mode 100644
index 0000000..0b9c6f4
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
@@ -0,0 +1,754 @@ 
+/*
+ * MIPI-DSI based s6e3ha2 AMOLED 5.7 inch panel driver.
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Donghwa Lee <dh09.lee@samsung.com>
+ * Hyungwon Hwang <human.hwang@samsung.com>
+ * Hoegeun Kwon <hoegeun.kwon@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#define S6E3HA2_MIN_BRIGHTNESS		0
+#define S6E3HA2_MAX_BRIGHTNESS		100
+#define S6E3HA2_DEFAULT_BRIGHTNESS	80
+
+#define S6E3HA2_NUM_GAMMA_STEPS		46
+#define S6E3HA2_GAMMA_CMD_CNT		35
+#define S6E3HA2_VINT_STATUS_MAX		10
+
+static const u8 gamma_tbl[S6E3HA2_NUM_GAMMA_STEPS][S6E3HA2_GAMMA_CMD_CNT] = {
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x82, 0x83,
+	  0x85, 0x88, 0x8b, 0x8b, 0x84, 0x88, 0x82, 0x82, 0x89, 0x86, 0x8c,
+	  0x94, 0x84, 0xb1, 0xaf, 0x8e, 0xcf, 0xad, 0xc9, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x84, 0x84,
+	  0x85, 0x87, 0x8b, 0x8a, 0x84, 0x88, 0x82, 0x82, 0x89, 0x86, 0x8a,
+	  0x93, 0x84, 0xb0, 0xae, 0x8e, 0xc9, 0xa8, 0xc5, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x8a, 0x8a, 0x84, 0x88, 0x81, 0x84, 0x8a, 0x88, 0x8a,
+	  0x91, 0x84, 0xb1, 0xae, 0x8b, 0xd5, 0xb2, 0xcc, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x8a, 0x8a, 0x84, 0x87, 0x81, 0x84, 0x8a, 0x87, 0x8a,
+	  0x91, 0x85, 0xae, 0xac, 0x8a, 0xc3, 0xa3, 0xc0, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x85, 0x85,
+	  0x86, 0x85, 0x88, 0x89, 0x84, 0x89, 0x82, 0x84, 0x87, 0x85, 0x8b,
+	  0x91, 0x88, 0xad, 0xab, 0x8a, 0xb7, 0x9b, 0xb6, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x89, 0x8a, 0x84, 0x89, 0x83, 0x83, 0x86, 0x84, 0x8b,
+	  0x90, 0x84, 0xb0, 0xae, 0x8b, 0xce, 0xad, 0xc8, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
+	  0x85, 0x87, 0x89, 0x8a, 0x83, 0x87, 0x82, 0x85, 0x88, 0x87, 0x89,
+	  0x8f, 0x84, 0xac, 0xaa, 0x89, 0xb1, 0x98, 0xaf, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x88, 0x89, 0x84, 0x88, 0x83, 0x82, 0x85, 0x84, 0x8c,
+	  0x91, 0x86, 0xac, 0xaa, 0x89, 0xc2, 0xa5, 0xbd, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x85, 0x87, 0x89, 0x8a, 0x83, 0x87, 0x82, 0x85, 0x88, 0x87, 0x88,
+	  0x8b, 0x82, 0xad, 0xaa, 0x8a, 0xc2, 0xa5, 0xbd, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x87, 0x89, 0x84, 0x88, 0x83, 0x82, 0x85, 0x84, 0x8a,
+	  0x8e, 0x84, 0xae, 0xac, 0x89, 0xda, 0xb7, 0xd0, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x85, 0x86, 0x87, 0x89, 0x84, 0x88, 0x83, 0x80, 0x83, 0x82, 0x8b,
+	  0x8e, 0x85, 0xac, 0xaa, 0x89, 0xc8, 0xaa, 0xc1, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x85, 0x86, 0x87, 0x89, 0x81, 0x85, 0x81, 0x84, 0x86, 0x84, 0x8c,
+	  0x8c, 0x84, 0xa9, 0xa8, 0x87, 0xa3, 0x92, 0xa1, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x85, 0x86, 0x87, 0x89, 0x84, 0x86, 0x83, 0x80, 0x83, 0x81, 0x8c,
+	  0x8d, 0x84, 0xaa, 0xaa, 0x89, 0xce, 0xaf, 0xc5, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x85, 0x86, 0x87, 0x89, 0x81, 0x83, 0x80, 0x83, 0x85, 0x85, 0x8c,
+	  0x8c, 0x84, 0xa8, 0xa8, 0x88, 0xb5, 0x9f, 0xb0, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x86, 0x86, 0x87, 0x88, 0x81, 0x83, 0x80, 0x83, 0x85, 0x85, 0x8c,
+	  0x8b, 0x84, 0xab, 0xa8, 0x86, 0xd4, 0xb4, 0xc9, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x86, 0x86, 0x87, 0x88, 0x81, 0x83, 0x80, 0x84, 0x84, 0x85, 0x8b,
+	  0x8a, 0x83, 0xa6, 0xa5, 0x84, 0xbb, 0xa4, 0xb3, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x86, 0x85, 0x86, 0x86, 0x82, 0x85, 0x81, 0x82, 0x83, 0x84, 0x8e,
+	  0x8b, 0x83, 0xa4, 0xa3, 0x8a, 0xa1, 0x93, 0x9d, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x82, 0x82, 0x84, 0x8e,
+	  0x8b, 0x83, 0xa4, 0xa2, 0x86, 0xc1, 0xa9, 0xb7, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x82, 0x82, 0x84, 0x8d,
+	  0x89, 0x82, 0xa2, 0xa1, 0x84, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x83, 0x83, 0x85, 0x8c,
+	  0x87, 0x7f, 0xa2, 0x9d, 0x88, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xbb, 0x00, 0xc5, 0x00, 0xb4, 0x87, 0x86, 0x86, 0x84, 0x83,
+	  0x86, 0x87, 0x87, 0x87, 0x80, 0x82, 0x7f, 0x86, 0x86, 0x88, 0x8a,
+	  0x84, 0x7e, 0x9d, 0x9c, 0x82, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xbd, 0x00, 0xc7, 0x00, 0xb7, 0x87, 0x85, 0x85, 0x84, 0x83,
+	  0x86, 0x86, 0x86, 0x88, 0x81, 0x83, 0x80, 0x83, 0x84, 0x85, 0x8a,
+	  0x85, 0x7e, 0x9c, 0x9b, 0x85, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xc0, 0x00, 0xca, 0x00, 0xbb, 0x87, 0x86, 0x85, 0x83, 0x83,
+	  0x85, 0x86, 0x86, 0x88, 0x81, 0x83, 0x80, 0x84, 0x85, 0x86, 0x89,
+	  0x83, 0x7d, 0x9c, 0x99, 0x87, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xc4, 0x00, 0xcd, 0x00, 0xbe, 0x87, 0x86, 0x85, 0x83, 0x83,
+	  0x86, 0x85, 0x85, 0x87, 0x81, 0x82, 0x80, 0x82, 0x82, 0x83, 0x8a,
+	  0x85, 0x7f, 0x9f, 0x9b, 0x86, 0xb4, 0xa1, 0xac, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xc7, 0x00, 0xd0, 0x00, 0xc2, 0x87, 0x85, 0x85, 0x83, 0x82,
+	  0x85, 0x85, 0x85, 0x86, 0x82, 0x83, 0x80, 0x82, 0x82, 0x84, 0x87,
+	  0x86, 0x80, 0x9e, 0x9a, 0x87, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xca, 0x00, 0xd2, 0x00, 0xc5, 0x87, 0x85, 0x84, 0x82, 0x82,
+	  0x84, 0x85, 0x85, 0x86, 0x81, 0x82, 0x7f, 0x82, 0x82, 0x84, 0x88,
+	  0x86, 0x81, 0x9d, 0x98, 0x86, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xce, 0x00, 0xd6, 0x00, 0xca, 0x86, 0x85, 0x84, 0x83, 0x83,
+	  0x85, 0x84, 0x84, 0x85, 0x81, 0x82, 0x80, 0x81, 0x81, 0x82, 0x89,
+	  0x86, 0x81, 0x9c, 0x97, 0x86, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xd1, 0x00, 0xd9, 0x00, 0xce, 0x86, 0x84, 0x83, 0x83, 0x82,
+	  0x85, 0x85, 0x85, 0x86, 0x81, 0x83, 0x81, 0x82, 0x82, 0x83, 0x86,
+	  0x83, 0x7f, 0x99, 0x95, 0x86, 0xbb, 0xa4, 0xb3, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xd4, 0x00, 0xdb, 0x00, 0xd1, 0x86, 0x85, 0x83, 0x83, 0x82,
+	  0x85, 0x84, 0x84, 0x85, 0x80, 0x83, 0x82, 0x80, 0x80, 0x81, 0x87,
+	  0x84, 0x81, 0x98, 0x93, 0x85, 0xae, 0x9c, 0xa8, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xd8, 0x00, 0xde, 0x00, 0xd6, 0x86, 0x84, 0x83, 0x81, 0x81,
+	  0x83, 0x85, 0x85, 0x85, 0x82, 0x83, 0x81, 0x81, 0x81, 0x83, 0x86,
+	  0x84, 0x80, 0x98, 0x91, 0x85, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xdc, 0x00, 0xe2, 0x00, 0xda, 0x85, 0x84, 0x83, 0x82, 0x82,
+	  0x84, 0x84, 0x84, 0x85, 0x81, 0x82, 0x82, 0x80, 0x80, 0x81, 0x83,
+	  0x82, 0x7f, 0x99, 0x93, 0x86, 0x94, 0x8b, 0x92, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xdf, 0x00, 0xe5, 0x00, 0xde, 0x85, 0x84, 0x82, 0x82, 0x82,
+	  0x84, 0x83, 0x83, 0x84, 0x81, 0x81, 0x80, 0x83, 0x82, 0x84, 0x82,
+	  0x81, 0x7f, 0x99, 0x92, 0x86, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
+	  0x82, 0x83, 0x83, 0x84, 0x80, 0x81, 0x80, 0x83, 0x83, 0x84, 0x80,
+	  0x81, 0x7c, 0x99, 0x92, 0x87, 0xa1, 0x93, 0x9d, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x85, 0x84, 0x83, 0x81, 0x81,
+	  0x82, 0x82, 0x82, 0x83, 0x80, 0x81, 0x80, 0x81, 0x80, 0x82, 0x83,
+	  0x82, 0x80, 0x91, 0x8d, 0x83, 0x9a, 0x90, 0x96, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
+	  0x82, 0x83, 0x83, 0x84, 0x80, 0x81, 0x80, 0x81, 0x80, 0x82, 0x83,
+	  0x81, 0x7f, 0x91, 0x8c, 0x82, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
+	  0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x82, 0x82,
+	  0x82, 0x7f, 0x94, 0x89, 0x84, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
+	  0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x82, 0x83,
+	  0x82, 0x7f, 0x91, 0x85, 0x81, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
+	  0x82, 0x83, 0x83, 0x83, 0x80, 0x80, 0x7f, 0x83, 0x82, 0x84, 0x83,
+	  0x82, 0x7f, 0x90, 0x84, 0x81, 0x9a, 0x90, 0x96, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x80, 0x80,
+	  0x82, 0x83, 0x83, 0x83, 0x80, 0x80, 0x7f, 0x80, 0x80, 0x81, 0x81,
+	  0x82, 0x83, 0x7e, 0x80, 0x7c, 0xa4, 0x97, 0x9f, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe9, 0x00, 0xec, 0x00, 0xe8, 0x84, 0x83, 0x82, 0x81, 0x81,
+	  0x82, 0x82, 0x82, 0x83, 0x7f, 0x7f, 0x7f, 0x81, 0x80, 0x82, 0x83,
+	  0x83, 0x84, 0x79, 0x7c, 0x79, 0xb1, 0xa0, 0xaa, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xed, 0x00, 0xf0, 0x00, 0xec, 0x83, 0x83, 0x82, 0x80, 0x80,
+	  0x81, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7e, 0x81, 0x81, 0x82, 0x80,
+	  0x81, 0x81, 0x84, 0x84, 0x83, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xf1, 0x00, 0xf4, 0x00, 0xf1, 0x83, 0x82, 0x82, 0x80, 0x80,
+	  0x81, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x7d,
+	  0x7e, 0x7f, 0x84, 0x84, 0x83, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xf6, 0x00, 0xf7, 0x00, 0xf5, 0x82, 0x82, 0x81, 0x80, 0x80,
+	  0x80, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x82,
+	  0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xfa, 0x00, 0xfb, 0x00, 0xfa, 0x81, 0x81, 0x81, 0x80, 0x80,
+	  0x80, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 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,
+	  0x00, 0x00 },
+	{ 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,
+	  0x00, 0x00 }
+};
+
+unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = {
+	0x18, 0x19, 0x1a, 0x1b, 0x1c,
+	0x1d, 0x1e, 0x1f, 0x20, 0x21
+};
+
+struct s6e3ha2 {
+	struct device *dev;
+	struct drm_panel panel;
+	struct backlight_device *bl_dev;
+
+	struct regulator_bulk_data supplies[2];
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
+
+	/* This field is tested by functions directly accessing DSI bus before
+	 * transfer, transfer is skipped if it is set. In case of transfer
+	 * failure or unexpected response the field is set to error value.
+	 * Such construct allows to eliminate many checks in higher level
+	 * functions.
+	 */
+	int error;
+};
+
+static int  s6e3ha2_clear_error(struct s6e3ha2 *ctx)
+{
+	int ret = ctx->error;
+
+	ctx->error = 0;
+	return ret;
+}
+
+static void s6e3ha2_dcs_write(struct s6e3ha2 *ctx, const void *data, size_t len)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+	ssize_t ret;
+
+	if (ctx->error < 0)
+		return;
+
+	ret = mipi_dsi_dcs_write_buffer(dsi, data, len);
+	if (ret < 0) {
+		dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n",
+						ret, (int)len, data);
+		ctx->error = ret;
+	}
+}
+
+#define s6e3ha2_dcs_write_seq_static(ctx, seq...) do {	\
+	static const u8 d[] = { seq };			\
+	s6e3ha2_dcs_write(ctx, d, ARRAY_SIZE(d));	\
+} while (0)
+
+static void s6e3ha2_test_key_on_f0(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xf0, 0x5a, 0x5a);
+}
+
+static void s6e3ha2_test_key_off_f0(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xf0, 0xa5, 0xa5);
+}
+
+static void s6e3ha2_test_key_on_fc(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xfc, 0x5a, 0x5a);
+}
+
+static void s6e3ha2_test_key_off_fc(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xfc, 0xa5, 0xa5);
+}
+
+static void s6e3ha2_single_dsi_set(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xf2, 0x67);
+	s6e3ha2_dcs_write_seq_static(ctx, 0xf9, 0x09);
+}
+
+static void s6e3ha2_freq_calibration(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xfd, 0x1c);
+	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20, 0x39);
+	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0xa0);
+	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20);
+	s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x12, 0x62,
+		0x40, 0x80, 0xc0, 0x28, 0x28, 0x28, 0x28, 0x39, 0xc5);
+}
+
+static void s6e3ha2_aor_control(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xb2, 0x03, 0x10);
+}
+
+static void s6e3ha2_caps_elvss_set(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xb6, 0x9c, 0x0a);
+}
+
+static void s6e3ha2_acl_off(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0x55, 0x00);
+}
+
+static void s6e3ha2_acl_off_opr(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xb5, 0x40);
+}
+
+static void s6e3ha2_test_global(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xb0, 0x07);
+}
+
+static void s6e3ha2_test(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xb8, 0x19);
+}
+
+static void s6e3ha2_touch_hsync_on1(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx,
+			0xbd, 0x33, 0x11, 0x02, 0x16, 0x02, 0x16);
+}
+
+static void s6e3ha2_pentile_control(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xc0, 0x00, 0x00, 0xd8, 0xd8);
+}
+
+static void s6e3ha2_poc_global(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xb0, 0x20);
+}
+
+static void s6e3ha2_poc_setting(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x08);
+}
+
+static void s6e3ha2_pcd_set_off(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xcc, 0x40, 0x51);
+}
+
+static void s6e3ha2_err_fg_set(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xed, 0x44);
+}
+
+static void s6e3ha2_hbm_off(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0x53, 0x00);
+}
+
+static void s6e3ha2_te_start_setting(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xb9, 0x10, 0x09, 0xff, 0x00, 0x09);
+}
+
+static void s6e3ha2_gamma_update(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xf7, 0x03);
+	ndelay(100); /* need for 100ns delay */
+	s6e3ha2_dcs_write_seq_static(ctx, 0xf7, 0x00);
+}
+
+static int s6e3ha2_get_brightness(struct backlight_device *bl_dev)
+{
+	return bl_dev->props.brightness;
+}
+
+static void s6e3ha2_set_vint(struct s6e3ha2 *ctx)
+{
+	struct backlight_device *bl_dev = ctx->bl_dev;
+	unsigned int brightness = bl_dev->props.brightness;
+	unsigned char data[] = { 0xf4, 0x8b,
+			vint_table[brightness * (S6E3HA2_VINT_STATUS_MAX - 1) /
+			S6E3HA2_MAX_BRIGHTNESS] };
+
+	s6e3ha2_dcs_write(ctx, data, ARRAY_SIZE(data));
+}
+
+static unsigned int s6e3ha2_get_brightness_index(unsigned int brightness)
+{
+	return (brightness * (S6E3HA2_NUM_GAMMA_STEPS - 1)) /
+		S6E3HA2_MAX_BRIGHTNESS;
+}
+
+static int s6e3ha2_update_gamma(struct s6e3ha2 *ctx, unsigned int brightness)
+{
+	struct backlight_device *bl_dev = ctx->bl_dev;
+	unsigned int index = s6e3ha2_get_brightness_index(brightness);
+	u8 data[S6E3HA2_GAMMA_CMD_CNT + 1] = { 0xca, };
+
+	memcpy(data + 1, gamma_tbl + index, S6E3HA2_GAMMA_CMD_CNT);
+	s6e3ha2_dcs_write(ctx, data, ARRAY_SIZE(data));
+
+	s6e3ha2_gamma_update(ctx);
+	bl_dev->props.brightness = brightness;
+
+	return 0;
+}
+
+static int s6e3ha2_set_brightness(struct backlight_device *bl_dev)
+{
+	struct s6e3ha2 *ctx = bl_get_data(bl_dev);
+	unsigned int brightness = bl_dev->props.brightness;
+
+	if (brightness < S6E3HA2_MIN_BRIGHTNESS ||
+		brightness > bl_dev->props.max_brightness) {
+		dev_err(ctx->dev, "Invalid brightness: %u\n", brightness);
+		return -EINVAL;
+	}
+
+	if (bl_dev->props.power > FB_BLANK_NORMAL)
+		return -EPERM;
+
+	s6e3ha2_test_key_on_f0(ctx);
+	s6e3ha2_update_gamma(ctx, brightness);
+	s6e3ha2_aor_control(ctx);
+	s6e3ha2_set_vint(ctx);
+	s6e3ha2_test_key_off_f0(ctx);
+
+	return ctx->error;
+}
+
+static const struct backlight_ops s6e3ha2_bl_ops = {
+	.get_brightness = s6e3ha2_get_brightness,
+	.update_status = s6e3ha2_set_brightness,
+};
+
+static int s6e3ha2_panel_init(struct s6e3ha2 *ctx)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+	int ret;
+
+	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+	if (ret < 0)
+		return ret;
+	usleep_range(5000, 6000);
+
+	s6e3ha2_test_key_on_f0(ctx);
+	s6e3ha2_single_dsi_set(ctx);
+	s6e3ha2_test_key_on_fc(ctx);
+	s6e3ha2_freq_calibration(ctx);
+	s6e3ha2_test_key_off_fc(ctx);
+	s6e3ha2_test_key_off_f0(ctx);
+
+	return 0;
+}
+
+static int s6e3ha2_power_off(struct s6e3ha2 *ctx)
+{
+	return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+}
+
+static int s6e3ha2_disable(struct drm_panel *panel)
+{
+	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+	int ret;
+
+	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_display_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	msleep(40);
+	ctx->bl_dev->props.power = FB_BLANK_NORMAL;
+
+	return 0;
+}
+
+static int s6e3ha2_unprepare(struct drm_panel *panel)
+{
+	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
+	int ret;
+
+	ret = s6e3ha2_clear_error(ctx);
+	if (!ret)
+		ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
+
+	return s6e3ha2_power_off(ctx);
+}
+
+static int s6e3ha2_power_on(struct s6e3ha2 *ctx)
+{
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+	if (ret < 0)
+		return ret;
+
+	msleep(120);
+
+	gpiod_set_value(ctx->enable_gpio, 0);
+	usleep_range(5000, 6000);
+	gpiod_set_value(ctx->enable_gpio, 1);
+
+	gpiod_set_value(ctx->reset_gpio, 1);
+	usleep_range(5000, 6000);
+	gpiod_set_value(ctx->reset_gpio, 0);
+	usleep_range(5000, 6000);
+
+	return 0;
+}
+static int s6e3ha2_prepare(struct drm_panel *panel)
+{
+	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
+	int ret;
+
+	ret = s6e3ha2_power_on(ctx);
+	if (ret < 0)
+		return ret;
+
+	ret = s6e3ha2_panel_init(ctx);
+	if (ret < 0)
+		goto err;
+
+	ret = s6e3ha2_clear_error(ctx);
+	if (ret < 0)
+		goto err;
+
+	ctx->bl_dev->props.power = FB_BLANK_NORMAL;
+
+	return 0;
+
+err:
+	s6e3ha2_power_off(ctx);
+	return ret;
+}
+
+static int s6e3ha2_enable(struct drm_panel *panel)
+{
+	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+	int ret;
+
+	/* common setting */
+	ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+	if (ret < 0)
+		return ret;
+
+	s6e3ha2_test_key_on_f0(ctx);
+	s6e3ha2_test_key_on_fc(ctx);
+	s6e3ha2_touch_hsync_on1(ctx);
+	s6e3ha2_pentile_control(ctx);
+	s6e3ha2_poc_global(ctx);
+	s6e3ha2_poc_setting(ctx);
+	s6e3ha2_test_key_off_fc(ctx);
+
+	/* pcd setting off for TB */
+	s6e3ha2_pcd_set_off(ctx);
+	s6e3ha2_err_fg_set(ctx);
+	s6e3ha2_te_start_setting(ctx);
+
+	/* brightness setting */
+	s6e3ha2_set_brightness(ctx->bl_dev);
+	s6e3ha2_aor_control(ctx);
+	s6e3ha2_caps_elvss_set(ctx);
+	s6e3ha2_gamma_update(ctx);
+	s6e3ha2_acl_off(ctx);
+	s6e3ha2_acl_off_opr(ctx);
+	s6e3ha2_hbm_off(ctx);
+
+	/* elvss temp compensation */
+	s6e3ha2_test_global(ctx);
+	s6e3ha2_test(ctx);
+	s6e3ha2_test_key_off_f0(ctx);
+
+	if (ctx->error != 0)
+		return ctx->error;
+
+	ret = mipi_dsi_dcs_set_display_on(dsi);
+	if (ret < 0)
+		return ret;
+
+	ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
+
+	return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+	.clock = 222372,
+	.hdisplay = 1440,
+	.hsync_start = 1440 + 1,
+	.hsync_end = 1440 + 1 + 1,
+	.htotal = 1440 + 1 + 1 + 1,
+	.vdisplay = 2560,
+	.vsync_start = 2560 + 1,
+	.vsync_end = 2560 + 1 + 1,
+	.vtotal = 2560 + 1 + 1 + 15,
+	.vrefresh = 60,
+	.flags = 0,
+};
+
+static int s6e3ha2_get_modes(struct drm_panel *panel)
+{
+	struct drm_connector *connector = panel->connector;
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	if (!mode) {
+		DRM_ERROR("failed to add mode %ux%ux@%u\n",
+				default_mode.hdisplay, default_mode.vdisplay,
+				default_mode.vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+	drm_mode_probed_add(connector, mode);
+
+	connector->display_info.width_mm = 71;
+	connector->display_info.height_mm = 125;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs s6e3ha2_drm_funcs = {
+	.disable = s6e3ha2_disable,
+	.unprepare = s6e3ha2_unprepare,
+	.prepare = s6e3ha2_prepare,
+	.enable = s6e3ha2_enable,
+	.get_modes = s6e3ha2_get_modes,
+};
+
+static int s6e3ha2_probe(struct mipi_dsi_device *dsi)
+{
+	struct device *dev = &dsi->dev;
+	struct s6e3ha2 *ctx;
+	int ret;
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, ctx);
+
+	ctx->dev = dev;
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	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) {
+		dev_err(dev, "failed to get regulators: %d\n", ret);
+		return ret;
+	}
+
+	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(ctx->reset_gpio)) {
+		dev_err(dev, "cannot get reset-gpios %ld\n",
+			PTR_ERR(ctx->reset_gpio));
+		return PTR_ERR(ctx->reset_gpio);
+	}
+
+	ctx->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
+	if (IS_ERR(ctx->enable_gpio)) {
+		dev_err(dev, "cannot get enable-gpios %ld\n",
+			PTR_ERR(ctx->enable_gpio));
+		return PTR_ERR(ctx->enable_gpio);
+	}
+
+	ctx->bl_dev = backlight_device_register("s6e3ha2", dev, ctx,
+						&s6e3ha2_bl_ops, NULL);
+	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 = S6E3HA2_MAX_BRIGHTNESS;
+	ctx->bl_dev->props.brightness = S6E3HA2_DEFAULT_BRIGHTNESS;
+	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
+
+	drm_panel_init(&ctx->panel);
+	ctx->panel.dev = dev;
+	ctx->panel.funcs = &s6e3ha2_drm_funcs;
+
+	ret = drm_panel_add(&ctx->panel);
+	if (ret < 0)
+		goto unregister_backlight;
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret < 0)
+		goto remove_panel;
+
+	return ret;
+
+remove_panel:
+	drm_panel_remove(&ctx->panel);
+
+unregister_backlight:
+	backlight_device_unregister(ctx->bl_dev);
+
+	return ret;
+}
+
+static int s6e3ha2_remove(struct mipi_dsi_device *dsi)
+{
+	struct s6e3ha2 *ctx = mipi_dsi_get_drvdata(dsi);
+
+	mipi_dsi_detach(dsi);
+	drm_panel_remove(&ctx->panel);
+	backlight_device_unregister(ctx->bl_dev);
+
+	return 0;
+}
+
+static const struct of_device_id s6e3ha2_of_match[] = {
+	{ .compatible = "samsung,s6e3ha2" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, s6e3ha2_of_match);
+
+static struct mipi_dsi_driver s6e3ha2_driver = {
+	.probe = s6e3ha2_probe,
+	.remove = s6e3ha2_remove,
+	.driver = {
+		.name = "panel-samsung-s6e3ha2",
+		.of_match_table = s6e3ha2_of_match,
+	},
+};
+module_mipi_dsi_driver(s6e3ha2_driver);
+
+MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
+MODULE_AUTHOR("Hyungwon Hwang <human.hwang@samsung.com>");
+MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
+MODULE_DESCRIPTION("MIPI-DSI based s6e3ha2 AMOLED Panel Driver");
+MODULE_LICENSE("GPL v2");