162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Ilitek ILI9322 TFT LCD drm_panel driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This panel can be configured to support:
662306a36Sopenharmony_ci * - 8-bit serial RGB interface
762306a36Sopenharmony_ci * - 24-bit parallel RGB interface
862306a36Sopenharmony_ci * - 8-bit ITU-R BT.601 interface
962306a36Sopenharmony_ci * - 8-bit ITU-R BT.656 interface
1062306a36Sopenharmony_ci * - Up to 320RGBx240 dots resolution TFT LCD displays
1162306a36Sopenharmony_ci * - Scaling, brightness and contrast
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * The scaling means that the display accepts a 640x480 or 720x480
1462306a36Sopenharmony_ci * input and rescales it to fit to the 320x240 display. So what we
1562306a36Sopenharmony_ci * present to the system is something else than what comes out on the
1662306a36Sopenharmony_ci * actual display.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
1962306a36Sopenharmony_ci * Derived from drivers/drm/gpu/panel/panel-samsung-ld9040.c
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <linux/bitops.h>
2362306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2462306a36Sopenharmony_ci#include <linux/module.h>
2562306a36Sopenharmony_ci#include <linux/of.h>
2662306a36Sopenharmony_ci#include <linux/platform_device.h>
2762306a36Sopenharmony_ci#include <linux/regmap.h>
2862306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
2962306a36Sopenharmony_ci#include <linux/spi/spi.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <video/mipi_display.h>
3262306a36Sopenharmony_ci#include <video/of_videomode.h>
3362306a36Sopenharmony_ci#include <video/videomode.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <drm/drm_modes.h>
3662306a36Sopenharmony_ci#include <drm/drm_panel.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define ILI9322_CHIP_ID			0x00
3962306a36Sopenharmony_ci#define ILI9322_CHIP_ID_MAGIC		0x96
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/*
4262306a36Sopenharmony_ci * Voltage on the communication interface, from 0.7 (0x00)
4362306a36Sopenharmony_ci * to 1.32 (0x1f) times the VREG1OUT voltage in 2% increments.
4462306a36Sopenharmony_ci * 1.00 (0x0f) is the default.
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_ci#define ILI9322_VCOM_AMP		0x01
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * High voltage on the communication signals, from 0.37 (0x00) to
5062306a36Sopenharmony_ci * 1.0 (0x3f) times the VREGOUT1 voltage in 1% increments.
5162306a36Sopenharmony_ci * 0.83 (0x2e) is the default.
5262306a36Sopenharmony_ci */
5362306a36Sopenharmony_ci#define ILI9322_VCOM_HIGH		0x02
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/*
5662306a36Sopenharmony_ci * VREG1 voltage regulator from 3.6V (0x00) to 6.0V (0x18) in 0.1V
5762306a36Sopenharmony_ci * increments. 5.4V (0x12) is the default. This is the reference
5862306a36Sopenharmony_ci * voltage for the VCOM levels and the greyscale level.
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_ci#define ILI9322_VREG1_VOLTAGE		0x03
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* Describes the incoming signal */
6362306a36Sopenharmony_ci#define ILI9322_ENTRY			0x06
6462306a36Sopenharmony_ci/* 0 = right-to-left, 1 = left-to-right (default), horizontal flip */
6562306a36Sopenharmony_ci#define ILI9322_ENTRY_HDIR		BIT(0)
6662306a36Sopenharmony_ci/* 0 = down-to-up, 1 = up-to-down (default), vertical flip  */
6762306a36Sopenharmony_ci#define ILI9322_ENTRY_VDIR		BIT(1)
6862306a36Sopenharmony_ci/* NTSC, PAL or autodetect */
6962306a36Sopenharmony_ci#define ILI9322_ENTRY_NTSC		(0 << 2)
7062306a36Sopenharmony_ci#define ILI9322_ENTRY_PAL		(1 << 2)
7162306a36Sopenharmony_ci#define ILI9322_ENTRY_AUTODETECT	(3 << 2)
7262306a36Sopenharmony_ci/* Input format */
7362306a36Sopenharmony_ci#define ILI9322_ENTRY_SERIAL_RGB_THROUGH (0 << 4)
7462306a36Sopenharmony_ci#define ILI9322_ENTRY_SERIAL_RGB_ALIGNED (1 << 4)
7562306a36Sopenharmony_ci#define ILI9322_ENTRY_SERIAL_RGB_DUMMY_320X240 (2 << 4)
7662306a36Sopenharmony_ci#define ILI9322_ENTRY_SERIAL_RGB_DUMMY_360X240 (3 << 4)
7762306a36Sopenharmony_ci#define ILI9322_ENTRY_DISABLE_1		(4 << 4)
7862306a36Sopenharmony_ci#define ILI9322_ENTRY_PARALLEL_RGB_THROUGH (5 << 4)
7962306a36Sopenharmony_ci#define ILI9322_ENTRY_PARALLEL_RGB_ALIGNED (6 << 4)
8062306a36Sopenharmony_ci#define ILI9322_ENTRY_YUV_640Y_320CBCR_25_54_MHZ (7 << 4)
8162306a36Sopenharmony_ci#define ILI9322_ENTRY_YUV_720Y_360CBCR_27_MHZ (8 << 4)
8262306a36Sopenharmony_ci#define ILI9322_ENTRY_DISABLE_2		(9 << 4)
8362306a36Sopenharmony_ci#define ILI9322_ENTRY_ITU_R_BT_656_720X360 (10 << 4)
8462306a36Sopenharmony_ci#define ILI9322_ENTRY_ITU_R_BT_656_640X320 (11 << 4)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/* Power control */
8762306a36Sopenharmony_ci#define ILI9322_POW_CTRL		0x07
8862306a36Sopenharmony_ci#define ILI9322_POW_CTRL_STB		BIT(0) /* 0 = standby, 1 = normal */
8962306a36Sopenharmony_ci#define ILI9322_POW_CTRL_VGL		BIT(1) /* 0 = off, 1 = on  */
9062306a36Sopenharmony_ci#define ILI9322_POW_CTRL_VGH		BIT(2) /* 0 = off, 1 = on  */
9162306a36Sopenharmony_ci#define ILI9322_POW_CTRL_DDVDH		BIT(3) /* 0 = off, 1 = on  */
9262306a36Sopenharmony_ci#define ILI9322_POW_CTRL_VCOM		BIT(4) /* 0 = off, 1 = on  */
9362306a36Sopenharmony_ci#define ILI9322_POW_CTRL_VCL		BIT(5) /* 0 = off, 1 = on  */
9462306a36Sopenharmony_ci#define ILI9322_POW_CTRL_AUTO		BIT(6) /* 0 = interactive, 1 = auto */
9562306a36Sopenharmony_ci#define ILI9322_POW_CTRL_STANDBY	(ILI9322_POW_CTRL_VGL | \
9662306a36Sopenharmony_ci					 ILI9322_POW_CTRL_VGH | \
9762306a36Sopenharmony_ci					 ILI9322_POW_CTRL_DDVDH | \
9862306a36Sopenharmony_ci					 ILI9322_POW_CTRL_VCL | \
9962306a36Sopenharmony_ci					 ILI9322_POW_CTRL_AUTO | \
10062306a36Sopenharmony_ci					 BIT(7))
10162306a36Sopenharmony_ci#define ILI9322_POW_CTRL_DEFAULT	(ILI9322_POW_CTRL_STANDBY | \
10262306a36Sopenharmony_ci					 ILI9322_POW_CTRL_STB)
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/* Vertical back porch bits 0..5 */
10562306a36Sopenharmony_ci#define ILI9322_VBP			0x08
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/* Horizontal back porch, 8 bits */
10862306a36Sopenharmony_ci#define ILI9322_HBP			0x09
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci/*
11162306a36Sopenharmony_ci * Polarity settings:
11262306a36Sopenharmony_ci * 1 = positive polarity
11362306a36Sopenharmony_ci * 0 = negative polarity
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_ci#define ILI9322_POL			0x0a
11662306a36Sopenharmony_ci#define ILI9322_POL_DCLK		BIT(0) /* 1 default */
11762306a36Sopenharmony_ci#define ILI9322_POL_HSYNC		BIT(1) /* 0 default */
11862306a36Sopenharmony_ci#define ILI9322_POL_VSYNC		BIT(2) /* 0 default */
11962306a36Sopenharmony_ci#define ILI9322_POL_DE			BIT(3) /* 1 default */
12062306a36Sopenharmony_ci/*
12162306a36Sopenharmony_ci * 0 means YCBCR are ordered Cb0,Y0,Cr0,Y1,Cb2,Y2,Cr2,Y3 (default)
12262306a36Sopenharmony_ci *   in RGB mode this means RGB comes in RGBRGB
12362306a36Sopenharmony_ci * 1 means YCBCR are ordered Cr0,Y0,Cb0,Y1,Cr2,Y2,Cb2,Y3
12462306a36Sopenharmony_ci *   in RGB mode this means RGB comes in BGRBGR
12562306a36Sopenharmony_ci */
12662306a36Sopenharmony_ci#define ILI9322_POL_YCBCR_MODE		BIT(4)
12762306a36Sopenharmony_ci/* Formula A for YCbCR->RGB = 0, Formula B = 1 */
12862306a36Sopenharmony_ci#define ILI9322_POL_FORMULA		BIT(5)
12962306a36Sopenharmony_ci/* Reverse polarity: 0 = 0..255, 1 = 255..0 */
13062306a36Sopenharmony_ci#define ILI9322_POL_REV			BIT(6)
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci#define ILI9322_IF_CTRL			0x0b
13362306a36Sopenharmony_ci#define ILI9322_IF_CTRL_HSYNC_VSYNC	0x00
13462306a36Sopenharmony_ci#define ILI9322_IF_CTRL_HSYNC_VSYNC_DE	BIT(2)
13562306a36Sopenharmony_ci#define ILI9322_IF_CTRL_DE_ONLY		BIT(3)
13662306a36Sopenharmony_ci#define ILI9322_IF_CTRL_SYNC_DISABLED	(BIT(2) | BIT(3))
13762306a36Sopenharmony_ci#define ILI9322_IF_CTRL_LINE_INVERSION	BIT(0) /* Not set means frame inv */
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#define ILI9322_GLOBAL_RESET		0x04
14062306a36Sopenharmony_ci#define ILI9322_GLOBAL_RESET_ASSERT	0x00 /* bit 0 = 0 -> reset */
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/*
14362306a36Sopenharmony_ci * 4+4 bits of negative and positive gamma correction
14462306a36Sopenharmony_ci * Upper nybble, bits 4-7 are negative gamma
14562306a36Sopenharmony_ci * Lower nybble, bits 0-3 are positive gamma
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_ci#define ILI9322_GAMMA_1			0x10
14862306a36Sopenharmony_ci#define ILI9322_GAMMA_2			0x11
14962306a36Sopenharmony_ci#define ILI9322_GAMMA_3			0x12
15062306a36Sopenharmony_ci#define ILI9322_GAMMA_4			0x13
15162306a36Sopenharmony_ci#define ILI9322_GAMMA_5			0x14
15262306a36Sopenharmony_ci#define ILI9322_GAMMA_6			0x15
15362306a36Sopenharmony_ci#define ILI9322_GAMMA_7			0x16
15462306a36Sopenharmony_ci#define ILI9322_GAMMA_8			0x17
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/*
15762306a36Sopenharmony_ci * enum ili9322_input - the format of the incoming signal to the panel
15862306a36Sopenharmony_ci *
15962306a36Sopenharmony_ci * The panel can be connected to various input streams and four of them can
16062306a36Sopenharmony_ci * be selected by electronic straps on the display. However it is possible
16162306a36Sopenharmony_ci * to select another mode or override the electronic default with this
16262306a36Sopenharmony_ci * setting.
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_cienum ili9322_input {
16562306a36Sopenharmony_ci	ILI9322_INPUT_SRGB_THROUGH = 0x0,
16662306a36Sopenharmony_ci	ILI9322_INPUT_SRGB_ALIGNED = 0x1,
16762306a36Sopenharmony_ci	ILI9322_INPUT_SRGB_DUMMY_320X240 = 0x2,
16862306a36Sopenharmony_ci	ILI9322_INPUT_SRGB_DUMMY_360X240 = 0x3,
16962306a36Sopenharmony_ci	ILI9322_INPUT_DISABLED_1 = 0x4,
17062306a36Sopenharmony_ci	ILI9322_INPUT_PRGB_THROUGH = 0x5,
17162306a36Sopenharmony_ci	ILI9322_INPUT_PRGB_ALIGNED = 0x6,
17262306a36Sopenharmony_ci	ILI9322_INPUT_YUV_640X320_YCBCR = 0x7,
17362306a36Sopenharmony_ci	ILI9322_INPUT_YUV_720X360_YCBCR = 0x8,
17462306a36Sopenharmony_ci	ILI9322_INPUT_DISABLED_2 = 0x9,
17562306a36Sopenharmony_ci	ILI9322_INPUT_ITU_R_BT656_720X360_YCBCR = 0xa,
17662306a36Sopenharmony_ci	ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR = 0xb,
17762306a36Sopenharmony_ci	ILI9322_INPUT_UNKNOWN = 0xc,
17862306a36Sopenharmony_ci};
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic const char * const ili9322_inputs[] = {
18162306a36Sopenharmony_ci	"8 bit serial RGB through",
18262306a36Sopenharmony_ci	"8 bit serial RGB aligned",
18362306a36Sopenharmony_ci	"8 bit serial RGB dummy 320x240",
18462306a36Sopenharmony_ci	"8 bit serial RGB dummy 360x240",
18562306a36Sopenharmony_ci	"disabled 1",
18662306a36Sopenharmony_ci	"24 bit parallel RGB through",
18762306a36Sopenharmony_ci	"24 bit parallel RGB aligned",
18862306a36Sopenharmony_ci	"24 bit YUV 640Y 320CbCr",
18962306a36Sopenharmony_ci	"24 bit YUV 720Y 360CbCr",
19062306a36Sopenharmony_ci	"disabled 2",
19162306a36Sopenharmony_ci	"8 bit ITU-R BT.656 720Y 360CbCr",
19262306a36Sopenharmony_ci	"8 bit ITU-R BT.656 640Y 320CbCr",
19362306a36Sopenharmony_ci};
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/**
19662306a36Sopenharmony_ci * struct ili9322_config - the system specific ILI9322 configuration
19762306a36Sopenharmony_ci * @width_mm: physical panel width [mm]
19862306a36Sopenharmony_ci * @height_mm: physical panel height [mm]
19962306a36Sopenharmony_ci * @flip_horizontal: flip the image horizontally (right-to-left scan)
20062306a36Sopenharmony_ci * (only in RGB and YUV modes)
20162306a36Sopenharmony_ci * @flip_vertical: flip the image vertically (down-to-up scan)
20262306a36Sopenharmony_ci * (only in RGB and YUV modes)
20362306a36Sopenharmony_ci * @input: the input/entry type used in this system, if this is set to
20462306a36Sopenharmony_ci * ILI9322_INPUT_UNKNOWN the driver will try to figure it out by probing
20562306a36Sopenharmony_ci * the hardware
20662306a36Sopenharmony_ci * @vreg1out_mv: the output in microvolts for the VREGOUT1 regulator used
20762306a36Sopenharmony_ci * to drive the physical display. Valid ranges are 3600 thru 6000 in 100
20862306a36Sopenharmony_ci * microvolt increments. If not specified, hardware defaults will be
20962306a36Sopenharmony_ci * used (4.5V).
21062306a36Sopenharmony_ci * @vcom_high_percent: the percentage of VREGOUT1 used for the peak
21162306a36Sopenharmony_ci * voltage on the communications link. Valid ranges are 37 thru 100
21262306a36Sopenharmony_ci * percent. If not specified, hardware defaults will be used (91%).
21362306a36Sopenharmony_ci * @vcom_amplitude_percent: the percentage of VREGOUT1 used for the
21462306a36Sopenharmony_ci * peak-to-peak amplitude of the communcation signals to the physical
21562306a36Sopenharmony_ci * display. Valid ranges are 70 thru 132 percent in increments if two
21662306a36Sopenharmony_ci * percent. Odd percentages will be truncated. If not specified, hardware
21762306a36Sopenharmony_ci * defaults will be used (114%).
21862306a36Sopenharmony_ci * @dclk_active_high: data/pixel clock active high, data will be clocked
21962306a36Sopenharmony_ci * in on the rising edge of the DCLK (this is usually the case).
22062306a36Sopenharmony_ci * @syncmode: The synchronization mode, what sync signals are emitted.
22162306a36Sopenharmony_ci * See the enum for details.
22262306a36Sopenharmony_ci * @de_active_high: DE (data entry) is active high
22362306a36Sopenharmony_ci * @hsync_active_high: HSYNC is active high
22462306a36Sopenharmony_ci * @vsync_active_high: VSYNC is active high
22562306a36Sopenharmony_ci * @gamma_corr_pos: a set of 8 nybbles describing positive
22662306a36Sopenharmony_ci * gamma correction for voltages V1 thru V8. Valid range 0..15
22762306a36Sopenharmony_ci * @gamma_corr_neg: a set of 8 nybbles describing negative
22862306a36Sopenharmony_ci * gamma correction for voltages V1 thru V8. Valid range 0..15
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * These adjust what grayscale voltage will be output for input data V1 = 0,
23162306a36Sopenharmony_ci * V2 = 16, V3 = 48, V4 = 96, V5 = 160, V6 = 208, V7 = 240 and V8 = 255.
23262306a36Sopenharmony_ci * The curve is shaped like this:
23362306a36Sopenharmony_ci *
23462306a36Sopenharmony_ci *  ^
23562306a36Sopenharmony_ci *  |                                                        V8
23662306a36Sopenharmony_ci *  |                                                   V7
23762306a36Sopenharmony_ci *  |                                          V6
23862306a36Sopenharmony_ci *  |                               V5
23962306a36Sopenharmony_ci *  |                    V4
24062306a36Sopenharmony_ci *  |            V3
24162306a36Sopenharmony_ci *  |     V2
24262306a36Sopenharmony_ci *  | V1
24362306a36Sopenharmony_ci *  +----------------------------------------------------------->
24462306a36Sopenharmony_ci *    0   16     48      96         160        208      240  255
24562306a36Sopenharmony_ci *
24662306a36Sopenharmony_ci * The negative and postive gamma values adjust the V1 thru V8 up/down
24762306a36Sopenharmony_ci * according to the datasheet specifications. This is a property of the
24862306a36Sopenharmony_ci * physical display connected to the display controller and may vary.
24962306a36Sopenharmony_ci * If defined, both arrays must be supplied in full. If the properties
25062306a36Sopenharmony_ci * are not supplied, hardware defaults will be used.
25162306a36Sopenharmony_ci */
25262306a36Sopenharmony_cistruct ili9322_config {
25362306a36Sopenharmony_ci	u32 width_mm;
25462306a36Sopenharmony_ci	u32 height_mm;
25562306a36Sopenharmony_ci	bool flip_horizontal;
25662306a36Sopenharmony_ci	bool flip_vertical;
25762306a36Sopenharmony_ci	enum ili9322_input input;
25862306a36Sopenharmony_ci	u32 vreg1out_mv;
25962306a36Sopenharmony_ci	u32 vcom_high_percent;
26062306a36Sopenharmony_ci	u32 vcom_amplitude_percent;
26162306a36Sopenharmony_ci	bool dclk_active_high;
26262306a36Sopenharmony_ci	bool de_active_high;
26362306a36Sopenharmony_ci	bool hsync_active_high;
26462306a36Sopenharmony_ci	bool vsync_active_high;
26562306a36Sopenharmony_ci	u8 syncmode;
26662306a36Sopenharmony_ci	u8 gamma_corr_pos[8];
26762306a36Sopenharmony_ci	u8 gamma_corr_neg[8];
26862306a36Sopenharmony_ci};
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistruct ili9322 {
27162306a36Sopenharmony_ci	struct device *dev;
27262306a36Sopenharmony_ci	const struct ili9322_config *conf;
27362306a36Sopenharmony_ci	struct drm_panel panel;
27462306a36Sopenharmony_ci	struct regmap *regmap;
27562306a36Sopenharmony_ci	struct regulator_bulk_data supplies[3];
27662306a36Sopenharmony_ci	struct gpio_desc *reset_gpio;
27762306a36Sopenharmony_ci	enum ili9322_input input;
27862306a36Sopenharmony_ci	struct videomode vm;
27962306a36Sopenharmony_ci	u8 gamma[8];
28062306a36Sopenharmony_ci	u8 vreg1out;
28162306a36Sopenharmony_ci	u8 vcom_high;
28262306a36Sopenharmony_ci	u8 vcom_amplitude;
28362306a36Sopenharmony_ci};
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic inline struct ili9322 *panel_to_ili9322(struct drm_panel *panel)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	return container_of(panel, struct ili9322, panel);
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic int ili9322_regmap_spi_write(void *context, const void *data,
29162306a36Sopenharmony_ci				    size_t count)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct device *dev = context;
29462306a36Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
29562306a36Sopenharmony_ci	u8 buf[2];
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/* Clear bit 7 to write */
29862306a36Sopenharmony_ci	memcpy(buf, data, 2);
29962306a36Sopenharmony_ci	buf[0] &= ~0x80;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	dev_dbg(dev, "WRITE: %02x %02x\n", buf[0], buf[1]);
30262306a36Sopenharmony_ci	return spi_write_then_read(spi, buf, 2, NULL, 0);
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic int ili9322_regmap_spi_read(void *context, const void *reg,
30662306a36Sopenharmony_ci				   size_t reg_size, void *val, size_t val_size)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct device *dev = context;
30962306a36Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
31062306a36Sopenharmony_ci	u8 buf[1];
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/* Set bit 7 to 1 to read */
31362306a36Sopenharmony_ci	memcpy(buf, reg, 1);
31462306a36Sopenharmony_ci	dev_dbg(dev, "READ: %02x reg size = %zu, val size = %zu\n",
31562306a36Sopenharmony_ci		buf[0], reg_size, val_size);
31662306a36Sopenharmony_ci	buf[0] |= 0x80;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return spi_write_then_read(spi, buf, 1, val, 1);
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic struct regmap_bus ili9322_regmap_bus = {
32262306a36Sopenharmony_ci	.write = ili9322_regmap_spi_write,
32362306a36Sopenharmony_ci	.read = ili9322_regmap_spi_read,
32462306a36Sopenharmony_ci	.reg_format_endian_default = REGMAP_ENDIAN_BIG,
32562306a36Sopenharmony_ci	.val_format_endian_default = REGMAP_ENDIAN_BIG,
32662306a36Sopenharmony_ci};
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic bool ili9322_volatile_reg(struct device *dev, unsigned int reg)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	return false;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic bool ili9322_writeable_reg(struct device *dev, unsigned int reg)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	/* Just register 0 is read-only */
33662306a36Sopenharmony_ci	if (reg == 0x00)
33762306a36Sopenharmony_ci		return false;
33862306a36Sopenharmony_ci	return true;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic const struct regmap_config ili9322_regmap_config = {
34262306a36Sopenharmony_ci	.reg_bits = 8,
34362306a36Sopenharmony_ci	.val_bits = 8,
34462306a36Sopenharmony_ci	.max_register = 0x44,
34562306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
34662306a36Sopenharmony_ci	.volatile_reg = ili9322_volatile_reg,
34762306a36Sopenharmony_ci	.writeable_reg = ili9322_writeable_reg,
34862306a36Sopenharmony_ci};
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int ili9322_init(struct drm_panel *panel, struct ili9322 *ili)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	u8 reg;
35362306a36Sopenharmony_ci	int ret;
35462306a36Sopenharmony_ci	int i;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/* Reset display */
35762306a36Sopenharmony_ci	ret = regmap_write(ili->regmap, ILI9322_GLOBAL_RESET,
35862306a36Sopenharmony_ci			   ILI9322_GLOBAL_RESET_ASSERT);
35962306a36Sopenharmony_ci	if (ret) {
36062306a36Sopenharmony_ci		dev_err(ili->dev, "can't issue GRESET (%d)\n", ret);
36162306a36Sopenharmony_ci		return ret;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* Set up the main voltage regulator */
36562306a36Sopenharmony_ci	if (ili->vreg1out != U8_MAX) {
36662306a36Sopenharmony_ci		ret = regmap_write(ili->regmap, ILI9322_VREG1_VOLTAGE,
36762306a36Sopenharmony_ci				   ili->vreg1out);
36862306a36Sopenharmony_ci		if (ret) {
36962306a36Sopenharmony_ci			dev_err(ili->dev, "can't set up VREG1OUT (%d)\n", ret);
37062306a36Sopenharmony_ci			return ret;
37162306a36Sopenharmony_ci		}
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (ili->vcom_amplitude != U8_MAX) {
37562306a36Sopenharmony_ci		ret = regmap_write(ili->regmap, ILI9322_VCOM_AMP,
37662306a36Sopenharmony_ci				   ili->vcom_amplitude);
37762306a36Sopenharmony_ci		if (ret) {
37862306a36Sopenharmony_ci			dev_err(ili->dev,
37962306a36Sopenharmony_ci				"can't set up VCOM amplitude (%d)\n", ret);
38062306a36Sopenharmony_ci			return ret;
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (ili->vcom_high != U8_MAX) {
38562306a36Sopenharmony_ci		ret = regmap_write(ili->regmap, ILI9322_VCOM_HIGH,
38662306a36Sopenharmony_ci				   ili->vcom_high);
38762306a36Sopenharmony_ci		if (ret) {
38862306a36Sopenharmony_ci			dev_err(ili->dev, "can't set up VCOM high (%d)\n", ret);
38962306a36Sopenharmony_ci			return ret;
39062306a36Sopenharmony_ci		}
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	/* Set up gamma correction */
39462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ili->gamma); i++) {
39562306a36Sopenharmony_ci		ret = regmap_write(ili->regmap, ILI9322_GAMMA_1 + i,
39662306a36Sopenharmony_ci				   ili->gamma[i]);
39762306a36Sopenharmony_ci		if (ret) {
39862306a36Sopenharmony_ci			dev_err(ili->dev,
39962306a36Sopenharmony_ci				"can't write gamma V%d to 0x%02x (%d)\n",
40062306a36Sopenharmony_ci				i + 1, ILI9322_GAMMA_1 + i, ret);
40162306a36Sopenharmony_ci			return ret;
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/*
40662306a36Sopenharmony_ci	 * Polarity and inverted color order for RGB input.
40762306a36Sopenharmony_ci	 * None of this applies in the BT.656 mode.
40862306a36Sopenharmony_ci	 */
40962306a36Sopenharmony_ci	reg = 0;
41062306a36Sopenharmony_ci	if (ili->conf->dclk_active_high)
41162306a36Sopenharmony_ci		reg = ILI9322_POL_DCLK;
41262306a36Sopenharmony_ci	if (ili->conf->de_active_high)
41362306a36Sopenharmony_ci		reg |= ILI9322_POL_DE;
41462306a36Sopenharmony_ci	if (ili->conf->hsync_active_high)
41562306a36Sopenharmony_ci		reg |= ILI9322_POL_HSYNC;
41662306a36Sopenharmony_ci	if (ili->conf->vsync_active_high)
41762306a36Sopenharmony_ci		reg |= ILI9322_POL_VSYNC;
41862306a36Sopenharmony_ci	ret = regmap_write(ili->regmap, ILI9322_POL, reg);
41962306a36Sopenharmony_ci	if (ret) {
42062306a36Sopenharmony_ci		dev_err(ili->dev, "can't write POL register (%d)\n", ret);
42162306a36Sopenharmony_ci		return ret;
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	/*
42562306a36Sopenharmony_ci	 * Set up interface control.
42662306a36Sopenharmony_ci	 * This is not used in the BT.656 mode (no H/Vsync or DE signals).
42762306a36Sopenharmony_ci	 */
42862306a36Sopenharmony_ci	reg = ili->conf->syncmode;
42962306a36Sopenharmony_ci	reg |= ILI9322_IF_CTRL_LINE_INVERSION;
43062306a36Sopenharmony_ci	ret = regmap_write(ili->regmap, ILI9322_IF_CTRL, reg);
43162306a36Sopenharmony_ci	if (ret) {
43262306a36Sopenharmony_ci		dev_err(ili->dev, "can't write IF CTRL register (%d)\n", ret);
43362306a36Sopenharmony_ci		return ret;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	/* Set up the input mode */
43762306a36Sopenharmony_ci	reg = (ili->input << 4);
43862306a36Sopenharmony_ci	/* These are inverted, setting to 1 is the default, clearing flips */
43962306a36Sopenharmony_ci	if (!ili->conf->flip_horizontal)
44062306a36Sopenharmony_ci		reg |= ILI9322_ENTRY_HDIR;
44162306a36Sopenharmony_ci	if (!ili->conf->flip_vertical)
44262306a36Sopenharmony_ci		reg |= ILI9322_ENTRY_VDIR;
44362306a36Sopenharmony_ci	reg |= ILI9322_ENTRY_AUTODETECT;
44462306a36Sopenharmony_ci	ret = regmap_write(ili->regmap, ILI9322_ENTRY, reg);
44562306a36Sopenharmony_ci	if (ret) {
44662306a36Sopenharmony_ci		dev_err(ili->dev, "can't write ENTRY reg (%d)\n", ret);
44762306a36Sopenharmony_ci		return ret;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci	dev_info(ili->dev, "display is in %s mode, syncmode %02x\n",
45062306a36Sopenharmony_ci		 ili9322_inputs[ili->input],
45162306a36Sopenharmony_ci		 ili->conf->syncmode);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	dev_info(ili->dev, "initialized display\n");
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	return 0;
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci/*
45962306a36Sopenharmony_ci * This power-on sequence if from the datasheet, page 57.
46062306a36Sopenharmony_ci */
46162306a36Sopenharmony_cistatic int ili9322_power_on(struct ili9322 *ili)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	int ret;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	/* Assert RESET */
46662306a36Sopenharmony_ci	gpiod_set_value(ili->reset_gpio, 1);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	ret = regulator_bulk_enable(ARRAY_SIZE(ili->supplies), ili->supplies);
46962306a36Sopenharmony_ci	if (ret < 0) {
47062306a36Sopenharmony_ci		dev_err(ili->dev, "unable to enable regulators\n");
47162306a36Sopenharmony_ci		return ret;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci	msleep(20);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/* De-assert RESET */
47662306a36Sopenharmony_ci	gpiod_set_value(ili->reset_gpio, 0);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	msleep(10);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	return 0;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic int ili9322_power_off(struct ili9322 *ili)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	return regulator_bulk_disable(ARRAY_SIZE(ili->supplies), ili->supplies);
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic int ili9322_disable(struct drm_panel *panel)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct ili9322 *ili = panel_to_ili9322(panel);
49162306a36Sopenharmony_ci	int ret;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	ret = regmap_write(ili->regmap, ILI9322_POW_CTRL,
49462306a36Sopenharmony_ci			   ILI9322_POW_CTRL_STANDBY);
49562306a36Sopenharmony_ci	if (ret) {
49662306a36Sopenharmony_ci		dev_err(ili->dev, "unable to go to standby mode\n");
49762306a36Sopenharmony_ci		return ret;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	return 0;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic int ili9322_unprepare(struct drm_panel *panel)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	struct ili9322 *ili = panel_to_ili9322(panel);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	return ili9322_power_off(ili);
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic int ili9322_prepare(struct drm_panel *panel)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	struct ili9322 *ili = panel_to_ili9322(panel);
51362306a36Sopenharmony_ci	int ret;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	ret = ili9322_power_on(ili);
51662306a36Sopenharmony_ci	if (ret < 0)
51762306a36Sopenharmony_ci		return ret;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	ret = ili9322_init(panel, ili);
52062306a36Sopenharmony_ci	if (ret < 0)
52162306a36Sopenharmony_ci		ili9322_unprepare(panel);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	return ret;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic int ili9322_enable(struct drm_panel *panel)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct ili9322 *ili = panel_to_ili9322(panel);
52962306a36Sopenharmony_ci	int ret;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	ret = regmap_write(ili->regmap, ILI9322_POW_CTRL,
53262306a36Sopenharmony_ci			   ILI9322_POW_CTRL_DEFAULT);
53362306a36Sopenharmony_ci	if (ret) {
53462306a36Sopenharmony_ci		dev_err(ili->dev, "unable to enable panel\n");
53562306a36Sopenharmony_ci		return ret;
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	return 0;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci/* Serial RGB modes */
54262306a36Sopenharmony_cistatic const struct drm_display_mode srgb_320x240_mode = {
54362306a36Sopenharmony_ci	.clock = 24535,
54462306a36Sopenharmony_ci	.hdisplay = 320,
54562306a36Sopenharmony_ci	.hsync_start = 320 + 359,
54662306a36Sopenharmony_ci	.hsync_end = 320 + 359 + 1,
54762306a36Sopenharmony_ci	.htotal = 320 + 359 + 1 + 241,
54862306a36Sopenharmony_ci	.vdisplay = 240,
54962306a36Sopenharmony_ci	.vsync_start = 240 + 4,
55062306a36Sopenharmony_ci	.vsync_end = 240 + 4 + 1,
55162306a36Sopenharmony_ci	.vtotal = 262,
55262306a36Sopenharmony_ci	.flags = 0,
55362306a36Sopenharmony_ci};
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic const struct drm_display_mode srgb_360x240_mode = {
55662306a36Sopenharmony_ci	.clock = 27000,
55762306a36Sopenharmony_ci	.hdisplay = 360,
55862306a36Sopenharmony_ci	.hsync_start = 360 + 35,
55962306a36Sopenharmony_ci	.hsync_end = 360 + 35 + 1,
56062306a36Sopenharmony_ci	.htotal = 360 + 35 + 1 + 241,
56162306a36Sopenharmony_ci	.vdisplay = 240,
56262306a36Sopenharmony_ci	.vsync_start = 240 + 21,
56362306a36Sopenharmony_ci	.vsync_end = 240 + 21 + 1,
56462306a36Sopenharmony_ci	.vtotal = 262,
56562306a36Sopenharmony_ci	.flags = 0,
56662306a36Sopenharmony_ci};
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci/* This is the only mode listed for parallel RGB in the datasheet */
56962306a36Sopenharmony_cistatic const struct drm_display_mode prgb_320x240_mode = {
57062306a36Sopenharmony_ci	.clock = 64000,
57162306a36Sopenharmony_ci	.hdisplay = 320,
57262306a36Sopenharmony_ci	.hsync_start = 320 + 38,
57362306a36Sopenharmony_ci	.hsync_end = 320 + 38 + 1,
57462306a36Sopenharmony_ci	.htotal = 320 + 38 + 1 + 50,
57562306a36Sopenharmony_ci	.vdisplay = 240,
57662306a36Sopenharmony_ci	.vsync_start = 240 + 4,
57762306a36Sopenharmony_ci	.vsync_end = 240 + 4 + 1,
57862306a36Sopenharmony_ci	.vtotal = 262,
57962306a36Sopenharmony_ci	.flags = 0,
58062306a36Sopenharmony_ci};
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci/* YUV modes */
58362306a36Sopenharmony_cistatic const struct drm_display_mode yuv_640x320_mode = {
58462306a36Sopenharmony_ci	.clock = 24540,
58562306a36Sopenharmony_ci	.hdisplay = 640,
58662306a36Sopenharmony_ci	.hsync_start = 640 + 252,
58762306a36Sopenharmony_ci	.hsync_end = 640 + 252 + 1,
58862306a36Sopenharmony_ci	.htotal = 640 + 252 + 1 + 28,
58962306a36Sopenharmony_ci	.vdisplay = 320,
59062306a36Sopenharmony_ci	.vsync_start = 320 + 4,
59162306a36Sopenharmony_ci	.vsync_end = 320 + 4 + 1,
59262306a36Sopenharmony_ci	.vtotal = 320 + 4 + 1 + 18,
59362306a36Sopenharmony_ci	.flags = 0,
59462306a36Sopenharmony_ci};
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic const struct drm_display_mode yuv_720x360_mode = {
59762306a36Sopenharmony_ci	.clock = 27000,
59862306a36Sopenharmony_ci	.hdisplay = 720,
59962306a36Sopenharmony_ci	.hsync_start = 720 + 252,
60062306a36Sopenharmony_ci	.hsync_end = 720 + 252 + 1,
60162306a36Sopenharmony_ci	.htotal = 720 + 252 + 1 + 24,
60262306a36Sopenharmony_ci	.vdisplay = 360,
60362306a36Sopenharmony_ci	.vsync_start = 360 + 4,
60462306a36Sopenharmony_ci	.vsync_end = 360 + 4 + 1,
60562306a36Sopenharmony_ci	.vtotal = 360 + 4 + 1 + 18,
60662306a36Sopenharmony_ci	.flags = 0,
60762306a36Sopenharmony_ci};
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci/* BT.656 VGA mode, 640x480 */
61062306a36Sopenharmony_cistatic const struct drm_display_mode itu_r_bt_656_640_mode = {
61162306a36Sopenharmony_ci	.clock = 24540,
61262306a36Sopenharmony_ci	.hdisplay = 640,
61362306a36Sopenharmony_ci	.hsync_start = 640 + 3,
61462306a36Sopenharmony_ci	.hsync_end = 640 + 3 + 1,
61562306a36Sopenharmony_ci	.htotal = 640 + 3 + 1 + 272,
61662306a36Sopenharmony_ci	.vdisplay = 480,
61762306a36Sopenharmony_ci	.vsync_start = 480 + 4,
61862306a36Sopenharmony_ci	.vsync_end = 480 + 4 + 1,
61962306a36Sopenharmony_ci	.vtotal = 500,
62062306a36Sopenharmony_ci	.flags = 0,
62162306a36Sopenharmony_ci};
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci/* BT.656 D1 mode 720x480 */
62462306a36Sopenharmony_cistatic const struct drm_display_mode itu_r_bt_656_720_mode = {
62562306a36Sopenharmony_ci	.clock = 27000,
62662306a36Sopenharmony_ci	.hdisplay = 720,
62762306a36Sopenharmony_ci	.hsync_start = 720 + 3,
62862306a36Sopenharmony_ci	.hsync_end = 720 + 3 + 1,
62962306a36Sopenharmony_ci	.htotal = 720 + 3 + 1 + 272,
63062306a36Sopenharmony_ci	.vdisplay = 480,
63162306a36Sopenharmony_ci	.vsync_start = 480 + 4,
63262306a36Sopenharmony_ci	.vsync_end = 480 + 4 + 1,
63362306a36Sopenharmony_ci	.vtotal = 500,
63462306a36Sopenharmony_ci	.flags = 0,
63562306a36Sopenharmony_ci};
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic int ili9322_get_modes(struct drm_panel *panel,
63862306a36Sopenharmony_ci			     struct drm_connector *connector)
63962306a36Sopenharmony_ci{
64062306a36Sopenharmony_ci	struct ili9322 *ili = panel_to_ili9322(panel);
64162306a36Sopenharmony_ci	struct drm_device *drm = connector->dev;
64262306a36Sopenharmony_ci	struct drm_display_mode *mode;
64362306a36Sopenharmony_ci	struct drm_display_info *info;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	info = &connector->display_info;
64662306a36Sopenharmony_ci	info->width_mm = ili->conf->width_mm;
64762306a36Sopenharmony_ci	info->height_mm = ili->conf->height_mm;
64862306a36Sopenharmony_ci	if (ili->conf->dclk_active_high)
64962306a36Sopenharmony_ci		info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
65062306a36Sopenharmony_ci	else
65162306a36Sopenharmony_ci		info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (ili->conf->de_active_high)
65462306a36Sopenharmony_ci		info->bus_flags |= DRM_BUS_FLAG_DE_HIGH;
65562306a36Sopenharmony_ci	else
65662306a36Sopenharmony_ci		info->bus_flags |= DRM_BUS_FLAG_DE_LOW;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	switch (ili->input) {
65962306a36Sopenharmony_ci	case ILI9322_INPUT_SRGB_DUMMY_320X240:
66062306a36Sopenharmony_ci		mode = drm_mode_duplicate(drm, &srgb_320x240_mode);
66162306a36Sopenharmony_ci		break;
66262306a36Sopenharmony_ci	case ILI9322_INPUT_SRGB_DUMMY_360X240:
66362306a36Sopenharmony_ci		mode = drm_mode_duplicate(drm, &srgb_360x240_mode);
66462306a36Sopenharmony_ci		break;
66562306a36Sopenharmony_ci	case ILI9322_INPUT_PRGB_THROUGH:
66662306a36Sopenharmony_ci	case ILI9322_INPUT_PRGB_ALIGNED:
66762306a36Sopenharmony_ci		mode = drm_mode_duplicate(drm, &prgb_320x240_mode);
66862306a36Sopenharmony_ci		break;
66962306a36Sopenharmony_ci	case ILI9322_INPUT_YUV_640X320_YCBCR:
67062306a36Sopenharmony_ci		mode = drm_mode_duplicate(drm, &yuv_640x320_mode);
67162306a36Sopenharmony_ci		break;
67262306a36Sopenharmony_ci	case ILI9322_INPUT_YUV_720X360_YCBCR:
67362306a36Sopenharmony_ci		mode = drm_mode_duplicate(drm, &yuv_720x360_mode);
67462306a36Sopenharmony_ci		break;
67562306a36Sopenharmony_ci	case ILI9322_INPUT_ITU_R_BT656_720X360_YCBCR:
67662306a36Sopenharmony_ci		mode = drm_mode_duplicate(drm, &itu_r_bt_656_720_mode);
67762306a36Sopenharmony_ci		break;
67862306a36Sopenharmony_ci	case ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR:
67962306a36Sopenharmony_ci		mode = drm_mode_duplicate(drm, &itu_r_bt_656_640_mode);
68062306a36Sopenharmony_ci		break;
68162306a36Sopenharmony_ci	default:
68262306a36Sopenharmony_ci		mode = NULL;
68362306a36Sopenharmony_ci		break;
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci	if (!mode) {
68662306a36Sopenharmony_ci		dev_err(panel->dev, "bad mode or failed to add mode\n");
68762306a36Sopenharmony_ci		return -EINVAL;
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci	drm_mode_set_name(mode);
69062306a36Sopenharmony_ci	/*
69162306a36Sopenharmony_ci	 * This is the preferred mode because most people are going
69262306a36Sopenharmony_ci	 * to want to use the display with VGA type graphics.
69362306a36Sopenharmony_ci	 */
69462306a36Sopenharmony_ci	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	/* Set up the polarity */
69762306a36Sopenharmony_ci	if (ili->conf->hsync_active_high)
69862306a36Sopenharmony_ci		mode->flags |= DRM_MODE_FLAG_PHSYNC;
69962306a36Sopenharmony_ci	else
70062306a36Sopenharmony_ci		mode->flags |= DRM_MODE_FLAG_NHSYNC;
70162306a36Sopenharmony_ci	if (ili->conf->vsync_active_high)
70262306a36Sopenharmony_ci		mode->flags |= DRM_MODE_FLAG_PVSYNC;
70362306a36Sopenharmony_ci	else
70462306a36Sopenharmony_ci		mode->flags |= DRM_MODE_FLAG_NVSYNC;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	mode->width_mm = ili->conf->width_mm;
70762306a36Sopenharmony_ci	mode->height_mm = ili->conf->height_mm;
70862306a36Sopenharmony_ci	drm_mode_probed_add(connector, mode);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	return 1; /* Number of modes */
71162306a36Sopenharmony_ci}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cistatic const struct drm_panel_funcs ili9322_drm_funcs = {
71462306a36Sopenharmony_ci	.disable = ili9322_disable,
71562306a36Sopenharmony_ci	.unprepare = ili9322_unprepare,
71662306a36Sopenharmony_ci	.prepare = ili9322_prepare,
71762306a36Sopenharmony_ci	.enable = ili9322_enable,
71862306a36Sopenharmony_ci	.get_modes = ili9322_get_modes,
71962306a36Sopenharmony_ci};
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cistatic int ili9322_probe(struct spi_device *spi)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	struct device *dev = &spi->dev;
72462306a36Sopenharmony_ci	struct ili9322 *ili;
72562306a36Sopenharmony_ci	const struct regmap_config *regmap_config;
72662306a36Sopenharmony_ci	u8 gamma;
72762306a36Sopenharmony_ci	u32 val;
72862306a36Sopenharmony_ci	int ret;
72962306a36Sopenharmony_ci	int i;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	ili = devm_kzalloc(dev, sizeof(struct ili9322), GFP_KERNEL);
73262306a36Sopenharmony_ci	if (!ili)
73362306a36Sopenharmony_ci		return -ENOMEM;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	spi_set_drvdata(spi, ili);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	ili->dev = dev;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	/*
74062306a36Sopenharmony_ci	 * Every new incarnation of this display must have a unique
74162306a36Sopenharmony_ci	 * data entry for the system in this driver.
74262306a36Sopenharmony_ci	 */
74362306a36Sopenharmony_ci	ili->conf = of_device_get_match_data(dev);
74462306a36Sopenharmony_ci	if (!ili->conf) {
74562306a36Sopenharmony_ci		dev_err(dev, "missing device configuration\n");
74662306a36Sopenharmony_ci		return -ENODEV;
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	val = ili->conf->vreg1out_mv;
75062306a36Sopenharmony_ci	if (!val) {
75162306a36Sopenharmony_ci		/* Default HW value, do not touch (should be 4.5V) */
75262306a36Sopenharmony_ci		ili->vreg1out = U8_MAX;
75362306a36Sopenharmony_ci	} else {
75462306a36Sopenharmony_ci		if (val < 3600) {
75562306a36Sopenharmony_ci			dev_err(dev, "too low VREG1OUT\n");
75662306a36Sopenharmony_ci			return -EINVAL;
75762306a36Sopenharmony_ci		}
75862306a36Sopenharmony_ci		if (val > 6000) {
75962306a36Sopenharmony_ci			dev_err(dev, "too high VREG1OUT\n");
76062306a36Sopenharmony_ci			return -EINVAL;
76162306a36Sopenharmony_ci		}
76262306a36Sopenharmony_ci		if ((val % 100) != 0) {
76362306a36Sopenharmony_ci			dev_err(dev, "VREG1OUT is no even 100 microvolt\n");
76462306a36Sopenharmony_ci			return -EINVAL;
76562306a36Sopenharmony_ci		}
76662306a36Sopenharmony_ci		val -= 3600;
76762306a36Sopenharmony_ci		val /= 100;
76862306a36Sopenharmony_ci		dev_dbg(dev, "VREG1OUT = 0x%02x\n", val);
76962306a36Sopenharmony_ci		ili->vreg1out = val;
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	val = ili->conf->vcom_high_percent;
77362306a36Sopenharmony_ci	if (!val) {
77462306a36Sopenharmony_ci		/* Default HW value, do not touch (should be 91%) */
77562306a36Sopenharmony_ci		ili->vcom_high = U8_MAX;
77662306a36Sopenharmony_ci	} else {
77762306a36Sopenharmony_ci		if (val < 37) {
77862306a36Sopenharmony_ci			dev_err(dev, "too low VCOM high\n");
77962306a36Sopenharmony_ci			return -EINVAL;
78062306a36Sopenharmony_ci		}
78162306a36Sopenharmony_ci		if (val > 100) {
78262306a36Sopenharmony_ci			dev_err(dev, "too high VCOM high\n");
78362306a36Sopenharmony_ci			return -EINVAL;
78462306a36Sopenharmony_ci		}
78562306a36Sopenharmony_ci		val -= 37;
78662306a36Sopenharmony_ci		dev_dbg(dev, "VCOM high = 0x%02x\n", val);
78762306a36Sopenharmony_ci		ili->vcom_high = val;
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	val = ili->conf->vcom_amplitude_percent;
79162306a36Sopenharmony_ci	if (!val) {
79262306a36Sopenharmony_ci		/* Default HW value, do not touch (should be 114%) */
79362306a36Sopenharmony_ci		ili->vcom_high = U8_MAX;
79462306a36Sopenharmony_ci	} else {
79562306a36Sopenharmony_ci		if (val < 70) {
79662306a36Sopenharmony_ci			dev_err(dev, "too low VCOM amplitude\n");
79762306a36Sopenharmony_ci			return -EINVAL;
79862306a36Sopenharmony_ci		}
79962306a36Sopenharmony_ci		if (val > 132) {
80062306a36Sopenharmony_ci			dev_err(dev, "too high VCOM amplitude\n");
80162306a36Sopenharmony_ci			return -EINVAL;
80262306a36Sopenharmony_ci		}
80362306a36Sopenharmony_ci		val -= 70;
80462306a36Sopenharmony_ci		val >>= 1; /* Increments of 2% */
80562306a36Sopenharmony_ci		dev_dbg(dev, "VCOM amplitude = 0x%02x\n", val);
80662306a36Sopenharmony_ci		ili->vcom_amplitude = val;
80762306a36Sopenharmony_ci	}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ili->gamma); i++) {
81062306a36Sopenharmony_ci		val = ili->conf->gamma_corr_neg[i];
81162306a36Sopenharmony_ci		if (val > 15) {
81262306a36Sopenharmony_ci			dev_err(dev, "negative gamma %u > 15, capping\n", val);
81362306a36Sopenharmony_ci			val = 15;
81462306a36Sopenharmony_ci		}
81562306a36Sopenharmony_ci		gamma = val << 4;
81662306a36Sopenharmony_ci		val = ili->conf->gamma_corr_pos[i];
81762306a36Sopenharmony_ci		if (val > 15) {
81862306a36Sopenharmony_ci			dev_err(dev, "positive gamma %u > 15, capping\n", val);
81962306a36Sopenharmony_ci			val = 15;
82062306a36Sopenharmony_ci		}
82162306a36Sopenharmony_ci		gamma |= val;
82262306a36Sopenharmony_ci		ili->gamma[i] = gamma;
82362306a36Sopenharmony_ci		dev_dbg(dev, "gamma V%d: 0x%02x\n", i + 1, gamma);
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	ili->supplies[0].supply = "vcc"; /* 2.7-3.6 V */
82762306a36Sopenharmony_ci	ili->supplies[1].supply = "iovcc"; /* 1.65-3.6V */
82862306a36Sopenharmony_ci	ili->supplies[2].supply = "vci"; /* 2.7-3.6V */
82962306a36Sopenharmony_ci	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ili->supplies),
83062306a36Sopenharmony_ci				      ili->supplies);
83162306a36Sopenharmony_ci	if (ret < 0)
83262306a36Sopenharmony_ci		return ret;
83362306a36Sopenharmony_ci	ret = regulator_set_voltage(ili->supplies[0].consumer,
83462306a36Sopenharmony_ci				    2700000, 3600000);
83562306a36Sopenharmony_ci	if (ret)
83662306a36Sopenharmony_ci		return ret;
83762306a36Sopenharmony_ci	ret = regulator_set_voltage(ili->supplies[1].consumer,
83862306a36Sopenharmony_ci				    1650000, 3600000);
83962306a36Sopenharmony_ci	if (ret)
84062306a36Sopenharmony_ci		return ret;
84162306a36Sopenharmony_ci	ret = regulator_set_voltage(ili->supplies[2].consumer,
84262306a36Sopenharmony_ci				    2700000, 3600000);
84362306a36Sopenharmony_ci	if (ret)
84462306a36Sopenharmony_ci		return ret;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	ili->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
84762306a36Sopenharmony_ci	if (IS_ERR(ili->reset_gpio)) {
84862306a36Sopenharmony_ci		dev_err(dev, "failed to get RESET GPIO\n");
84962306a36Sopenharmony_ci		return PTR_ERR(ili->reset_gpio);
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	spi->bits_per_word = 8;
85362306a36Sopenharmony_ci	ret = spi_setup(spi);
85462306a36Sopenharmony_ci	if (ret < 0) {
85562306a36Sopenharmony_ci		dev_err(dev, "spi setup failed.\n");
85662306a36Sopenharmony_ci		return ret;
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci	regmap_config = &ili9322_regmap_config;
85962306a36Sopenharmony_ci	ili->regmap = devm_regmap_init(dev, &ili9322_regmap_bus, dev,
86062306a36Sopenharmony_ci				       regmap_config);
86162306a36Sopenharmony_ci	if (IS_ERR(ili->regmap)) {
86262306a36Sopenharmony_ci		dev_err(dev, "failed to allocate register map\n");
86362306a36Sopenharmony_ci		return PTR_ERR(ili->regmap);
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	ret = regmap_read(ili->regmap, ILI9322_CHIP_ID, &val);
86762306a36Sopenharmony_ci	if (ret) {
86862306a36Sopenharmony_ci		dev_err(dev, "can't get chip ID (%d)\n", ret);
86962306a36Sopenharmony_ci		return ret;
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci	if (val != ILI9322_CHIP_ID_MAGIC) {
87262306a36Sopenharmony_ci		dev_err(dev, "chip ID 0x%0x2, expected 0x%02x\n", val,
87362306a36Sopenharmony_ci			ILI9322_CHIP_ID_MAGIC);
87462306a36Sopenharmony_ci		return -ENODEV;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	/* Probe the system to find the display setting */
87862306a36Sopenharmony_ci	if (ili->conf->input == ILI9322_INPUT_UNKNOWN) {
87962306a36Sopenharmony_ci		ret = regmap_read(ili->regmap, ILI9322_ENTRY, &val);
88062306a36Sopenharmony_ci		if (ret) {
88162306a36Sopenharmony_ci			dev_err(dev, "can't get entry setting (%d)\n", ret);
88262306a36Sopenharmony_ci			return ret;
88362306a36Sopenharmony_ci		}
88462306a36Sopenharmony_ci		/* Input enum corresponds to HW setting */
88562306a36Sopenharmony_ci		ili->input = (val >> 4) & 0x0f;
88662306a36Sopenharmony_ci		if (ili->input >= ILI9322_INPUT_UNKNOWN)
88762306a36Sopenharmony_ci			ili->input = ILI9322_INPUT_UNKNOWN;
88862306a36Sopenharmony_ci	} else {
88962306a36Sopenharmony_ci		ili->input = ili->conf->input;
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	drm_panel_init(&ili->panel, dev, &ili9322_drm_funcs,
89362306a36Sopenharmony_ci		       DRM_MODE_CONNECTOR_DPI);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	drm_panel_add(&ili->panel);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	return 0;
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cistatic void ili9322_remove(struct spi_device *spi)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	struct ili9322 *ili = spi_get_drvdata(spi);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	ili9322_power_off(ili);
90562306a36Sopenharmony_ci	drm_panel_remove(&ili->panel);
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci/*
90962306a36Sopenharmony_ci * The D-Link DIR-685 panel is marked LM918A01-1A SY-B4-091116-E0199
91062306a36Sopenharmony_ci */
91162306a36Sopenharmony_cistatic const struct ili9322_config ili9322_dir_685 = {
91262306a36Sopenharmony_ci	.width_mm = 65,
91362306a36Sopenharmony_ci	.height_mm = 50,
91462306a36Sopenharmony_ci	.input = ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR,
91562306a36Sopenharmony_ci	.vreg1out_mv = 4600,
91662306a36Sopenharmony_ci	.vcom_high_percent = 91,
91762306a36Sopenharmony_ci	.vcom_amplitude_percent = 114,
91862306a36Sopenharmony_ci	.syncmode = ILI9322_IF_CTRL_SYNC_DISABLED,
91962306a36Sopenharmony_ci	.dclk_active_high = true,
92062306a36Sopenharmony_ci	.gamma_corr_neg = { 0xa, 0x5, 0x7, 0x7, 0x7, 0x5, 0x1, 0x6 },
92162306a36Sopenharmony_ci	.gamma_corr_pos = { 0x7, 0x7, 0x3, 0x2, 0x3, 0x5, 0x7, 0x2 },
92262306a36Sopenharmony_ci};
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_cistatic const struct of_device_id ili9322_of_match[] = {
92562306a36Sopenharmony_ci	{
92662306a36Sopenharmony_ci		.compatible = "dlink,dir-685-panel",
92762306a36Sopenharmony_ci		.data = &ili9322_dir_685,
92862306a36Sopenharmony_ci	},
92962306a36Sopenharmony_ci	{
93062306a36Sopenharmony_ci		.compatible = "ilitek,ili9322",
93162306a36Sopenharmony_ci		.data = NULL,
93262306a36Sopenharmony_ci	},
93362306a36Sopenharmony_ci	{ }
93462306a36Sopenharmony_ci};
93562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ili9322_of_match);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_cistatic struct spi_driver ili9322_driver = {
93862306a36Sopenharmony_ci	.probe = ili9322_probe,
93962306a36Sopenharmony_ci	.remove = ili9322_remove,
94062306a36Sopenharmony_ci	.driver = {
94162306a36Sopenharmony_ci		.name = "panel-ilitek-ili9322",
94262306a36Sopenharmony_ci		.of_match_table = ili9322_of_match,
94362306a36Sopenharmony_ci	},
94462306a36Sopenharmony_ci};
94562306a36Sopenharmony_cimodule_spi_driver(ili9322_driver);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ciMODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
94862306a36Sopenharmony_ciMODULE_DESCRIPTION("ILI9322 LCD panel driver");
94962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
950