162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NewVision NV3052C IPS LCD panel driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net>
662306a36Sopenharmony_ci * Copyright (C) 2022, Christophe Branchereau <cbranchereau@gmail.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1262306a36Sopenharmony_ci#include <linux/media-bus-format.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1762306a36Sopenharmony_ci#include <linux/spi/spi.h>
1862306a36Sopenharmony_ci#include <video/mipi_display.h>
1962306a36Sopenharmony_ci#include <drm/drm_mipi_dbi.h>
2062306a36Sopenharmony_ci#include <drm/drm_modes.h>
2162306a36Sopenharmony_ci#include <drm/drm_panel.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct nv3052c_panel_info {
2462306a36Sopenharmony_ci	const struct drm_display_mode *display_modes;
2562306a36Sopenharmony_ci	unsigned int num_modes;
2662306a36Sopenharmony_ci	u16 width_mm, height_mm;
2762306a36Sopenharmony_ci	u32 bus_format, bus_flags;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct nv3052c {
3162306a36Sopenharmony_ci	struct device *dev;
3262306a36Sopenharmony_ci	struct drm_panel panel;
3362306a36Sopenharmony_ci	struct mipi_dbi dbi;
3462306a36Sopenharmony_ci	const struct nv3052c_panel_info *panel_info;
3562306a36Sopenharmony_ci	struct regulator *supply;
3662306a36Sopenharmony_ci	struct gpio_desc *reset_gpio;
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct nv3052c_reg {
4062306a36Sopenharmony_ci	u8 cmd;
4162306a36Sopenharmony_ci	u8 val;
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic const struct nv3052c_reg nv3052c_panel_regs[] = {
4562306a36Sopenharmony_ci	{ 0xff, 0x30 },
4662306a36Sopenharmony_ci	{ 0xff, 0x52 },
4762306a36Sopenharmony_ci	{ 0xff, 0x01 },
4862306a36Sopenharmony_ci	{ 0xe3, 0x00 },
4962306a36Sopenharmony_ci	{ 0x40, 0x00 },
5062306a36Sopenharmony_ci	{ 0x03, 0x40 },
5162306a36Sopenharmony_ci	{ 0x04, 0x00 },
5262306a36Sopenharmony_ci	{ 0x05, 0x03 },
5362306a36Sopenharmony_ci	{ 0x08, 0x00 },
5462306a36Sopenharmony_ci	{ 0x09, 0x07 },
5562306a36Sopenharmony_ci	{ 0x0a, 0x01 },
5662306a36Sopenharmony_ci	{ 0x0b, 0x32 },
5762306a36Sopenharmony_ci	{ 0x0c, 0x32 },
5862306a36Sopenharmony_ci	{ 0x0d, 0x0b },
5962306a36Sopenharmony_ci	{ 0x0e, 0x00 },
6062306a36Sopenharmony_ci	{ 0x23, 0xa0 },
6162306a36Sopenharmony_ci	{ 0x24, 0x0c },
6262306a36Sopenharmony_ci	{ 0x25, 0x06 },
6362306a36Sopenharmony_ci	{ 0x26, 0x14 },
6462306a36Sopenharmony_ci	{ 0x27, 0x14 },
6562306a36Sopenharmony_ci	{ 0x38, 0xcc },
6662306a36Sopenharmony_ci	{ 0x39, 0xd7 },
6762306a36Sopenharmony_ci	{ 0x3a, 0x4a },
6862306a36Sopenharmony_ci	{ 0x28, 0x40 },
6962306a36Sopenharmony_ci	{ 0x29, 0x01 },
7062306a36Sopenharmony_ci	{ 0x2a, 0xdf },
7162306a36Sopenharmony_ci	{ 0x49, 0x3c },
7262306a36Sopenharmony_ci	{ 0x91, 0x77 },
7362306a36Sopenharmony_ci	{ 0x92, 0x77 },
7462306a36Sopenharmony_ci	{ 0xa0, 0x55 },
7562306a36Sopenharmony_ci	{ 0xa1, 0x50 },
7662306a36Sopenharmony_ci	{ 0xa4, 0x9c },
7762306a36Sopenharmony_ci	{ 0xa7, 0x02 },
7862306a36Sopenharmony_ci	{ 0xa8, 0x01 },
7962306a36Sopenharmony_ci	{ 0xa9, 0x01 },
8062306a36Sopenharmony_ci	{ 0xaa, 0xfc },
8162306a36Sopenharmony_ci	{ 0xab, 0x28 },
8262306a36Sopenharmony_ci	{ 0xac, 0x06 },
8362306a36Sopenharmony_ci	{ 0xad, 0x06 },
8462306a36Sopenharmony_ci	{ 0xae, 0x06 },
8562306a36Sopenharmony_ci	{ 0xaf, 0x03 },
8662306a36Sopenharmony_ci	{ 0xb0, 0x08 },
8762306a36Sopenharmony_ci	{ 0xb1, 0x26 },
8862306a36Sopenharmony_ci	{ 0xb2, 0x28 },
8962306a36Sopenharmony_ci	{ 0xb3, 0x28 },
9062306a36Sopenharmony_ci	{ 0xb4, 0x33 },
9162306a36Sopenharmony_ci	{ 0xb5, 0x08 },
9262306a36Sopenharmony_ci	{ 0xb6, 0x26 },
9362306a36Sopenharmony_ci	{ 0xb7, 0x08 },
9462306a36Sopenharmony_ci	{ 0xb8, 0x26 },
9562306a36Sopenharmony_ci	{ 0xf0, 0x00 },
9662306a36Sopenharmony_ci	{ 0xf6, 0xc0 },
9762306a36Sopenharmony_ci	{ 0xff, 0x30 },
9862306a36Sopenharmony_ci	{ 0xff, 0x52 },
9962306a36Sopenharmony_ci	{ 0xff, 0x02 },
10062306a36Sopenharmony_ci	{ 0xb0, 0x0b },
10162306a36Sopenharmony_ci	{ 0xb1, 0x16 },
10262306a36Sopenharmony_ci	{ 0xb2, 0x17 },
10362306a36Sopenharmony_ci	{ 0xb3, 0x2c },
10462306a36Sopenharmony_ci	{ 0xb4, 0x32 },
10562306a36Sopenharmony_ci	{ 0xb5, 0x3b },
10662306a36Sopenharmony_ci	{ 0xb6, 0x29 },
10762306a36Sopenharmony_ci	{ 0xb7, 0x40 },
10862306a36Sopenharmony_ci	{ 0xb8, 0x0d },
10962306a36Sopenharmony_ci	{ 0xb9, 0x05 },
11062306a36Sopenharmony_ci	{ 0xba, 0x12 },
11162306a36Sopenharmony_ci	{ 0xbb, 0x10 },
11262306a36Sopenharmony_ci	{ 0xbc, 0x12 },
11362306a36Sopenharmony_ci	{ 0xbd, 0x15 },
11462306a36Sopenharmony_ci	{ 0xbe, 0x19 },
11562306a36Sopenharmony_ci	{ 0xbf, 0x0e },
11662306a36Sopenharmony_ci	{ 0xc0, 0x16 },
11762306a36Sopenharmony_ci	{ 0xc1, 0x0a },
11862306a36Sopenharmony_ci	{ 0xd0, 0x0c },
11962306a36Sopenharmony_ci	{ 0xd1, 0x17 },
12062306a36Sopenharmony_ci	{ 0xd2, 0x14 },
12162306a36Sopenharmony_ci	{ 0xd3, 0x2e },
12262306a36Sopenharmony_ci	{ 0xd4, 0x32 },
12362306a36Sopenharmony_ci	{ 0xd5, 0x3c },
12462306a36Sopenharmony_ci	{ 0xd6, 0x22 },
12562306a36Sopenharmony_ci	{ 0xd7, 0x3d },
12662306a36Sopenharmony_ci	{ 0xd8, 0x0d },
12762306a36Sopenharmony_ci	{ 0xd9, 0x07 },
12862306a36Sopenharmony_ci	{ 0xda, 0x13 },
12962306a36Sopenharmony_ci	{ 0xdb, 0x13 },
13062306a36Sopenharmony_ci	{ 0xdc, 0x11 },
13162306a36Sopenharmony_ci	{ 0xdd, 0x15 },
13262306a36Sopenharmony_ci	{ 0xde, 0x19 },
13362306a36Sopenharmony_ci	{ 0xdf, 0x10 },
13462306a36Sopenharmony_ci	{ 0xe0, 0x17 },
13562306a36Sopenharmony_ci	{ 0xe1, 0x0a },
13662306a36Sopenharmony_ci	{ 0xff, 0x30 },
13762306a36Sopenharmony_ci	{ 0xff, 0x52 },
13862306a36Sopenharmony_ci	{ 0xff, 0x03 },
13962306a36Sopenharmony_ci	{ 0x00, 0x2a },
14062306a36Sopenharmony_ci	{ 0x01, 0x2a },
14162306a36Sopenharmony_ci	{ 0x02, 0x2a },
14262306a36Sopenharmony_ci	{ 0x03, 0x2a },
14362306a36Sopenharmony_ci	{ 0x04, 0x61 },
14462306a36Sopenharmony_ci	{ 0x05, 0x80 },
14562306a36Sopenharmony_ci	{ 0x06, 0xc7 },
14662306a36Sopenharmony_ci	{ 0x07, 0x01 },
14762306a36Sopenharmony_ci	{ 0x08, 0x03 },
14862306a36Sopenharmony_ci	{ 0x09, 0x04 },
14962306a36Sopenharmony_ci	{ 0x70, 0x22 },
15062306a36Sopenharmony_ci	{ 0x71, 0x80 },
15162306a36Sopenharmony_ci	{ 0x30, 0x2a },
15262306a36Sopenharmony_ci	{ 0x31, 0x2a },
15362306a36Sopenharmony_ci	{ 0x32, 0x2a },
15462306a36Sopenharmony_ci	{ 0x33, 0x2a },
15562306a36Sopenharmony_ci	{ 0x34, 0x61 },
15662306a36Sopenharmony_ci	{ 0x35, 0xc5 },
15762306a36Sopenharmony_ci	{ 0x36, 0x80 },
15862306a36Sopenharmony_ci	{ 0x37, 0x23 },
15962306a36Sopenharmony_ci	{ 0x40, 0x03 },
16062306a36Sopenharmony_ci	{ 0x41, 0x04 },
16162306a36Sopenharmony_ci	{ 0x42, 0x05 },
16262306a36Sopenharmony_ci	{ 0x43, 0x06 },
16362306a36Sopenharmony_ci	{ 0x44, 0x11 },
16462306a36Sopenharmony_ci	{ 0x45, 0xe8 },
16562306a36Sopenharmony_ci	{ 0x46, 0xe9 },
16662306a36Sopenharmony_ci	{ 0x47, 0x11 },
16762306a36Sopenharmony_ci	{ 0x48, 0xea },
16862306a36Sopenharmony_ci	{ 0x49, 0xeb },
16962306a36Sopenharmony_ci	{ 0x50, 0x07 },
17062306a36Sopenharmony_ci	{ 0x51, 0x08 },
17162306a36Sopenharmony_ci	{ 0x52, 0x09 },
17262306a36Sopenharmony_ci	{ 0x53, 0x0a },
17362306a36Sopenharmony_ci	{ 0x54, 0x11 },
17462306a36Sopenharmony_ci	{ 0x55, 0xec },
17562306a36Sopenharmony_ci	{ 0x56, 0xed },
17662306a36Sopenharmony_ci	{ 0x57, 0x11 },
17762306a36Sopenharmony_ci	{ 0x58, 0xef },
17862306a36Sopenharmony_ci	{ 0x59, 0xf0 },
17962306a36Sopenharmony_ci	{ 0xb1, 0x01 },
18062306a36Sopenharmony_ci	{ 0xb4, 0x15 },
18162306a36Sopenharmony_ci	{ 0xb5, 0x16 },
18262306a36Sopenharmony_ci	{ 0xb6, 0x09 },
18362306a36Sopenharmony_ci	{ 0xb7, 0x0f },
18462306a36Sopenharmony_ci	{ 0xb8, 0x0d },
18562306a36Sopenharmony_ci	{ 0xb9, 0x0b },
18662306a36Sopenharmony_ci	{ 0xba, 0x00 },
18762306a36Sopenharmony_ci	{ 0xc7, 0x02 },
18862306a36Sopenharmony_ci	{ 0xca, 0x17 },
18962306a36Sopenharmony_ci	{ 0xcb, 0x18 },
19062306a36Sopenharmony_ci	{ 0xcc, 0x0a },
19162306a36Sopenharmony_ci	{ 0xcd, 0x10 },
19262306a36Sopenharmony_ci	{ 0xce, 0x0e },
19362306a36Sopenharmony_ci	{ 0xcf, 0x0c },
19462306a36Sopenharmony_ci	{ 0xd0, 0x00 },
19562306a36Sopenharmony_ci	{ 0x81, 0x00 },
19662306a36Sopenharmony_ci	{ 0x84, 0x15 },
19762306a36Sopenharmony_ci	{ 0x85, 0x16 },
19862306a36Sopenharmony_ci	{ 0x86, 0x10 },
19962306a36Sopenharmony_ci	{ 0x87, 0x0a },
20062306a36Sopenharmony_ci	{ 0x88, 0x0c },
20162306a36Sopenharmony_ci	{ 0x89, 0x0e },
20262306a36Sopenharmony_ci	{ 0x8a, 0x02 },
20362306a36Sopenharmony_ci	{ 0x97, 0x00 },
20462306a36Sopenharmony_ci	{ 0x9a, 0x17 },
20562306a36Sopenharmony_ci	{ 0x9b, 0x18 },
20662306a36Sopenharmony_ci	{ 0x9c, 0x0f },
20762306a36Sopenharmony_ci	{ 0x9d, 0x09 },
20862306a36Sopenharmony_ci	{ 0x9e, 0x0b },
20962306a36Sopenharmony_ci	{ 0x9f, 0x0d },
21062306a36Sopenharmony_ci	{ 0xa0, 0x01 },
21162306a36Sopenharmony_ci	{ 0xff, 0x30 },
21262306a36Sopenharmony_ci	{ 0xff, 0x52 },
21362306a36Sopenharmony_ci	{ 0xff, 0x02 },
21462306a36Sopenharmony_ci	{ 0x01, 0x01 },
21562306a36Sopenharmony_ci	{ 0x02, 0xda },
21662306a36Sopenharmony_ci	{ 0x03, 0xba },
21762306a36Sopenharmony_ci	{ 0x04, 0xa8 },
21862306a36Sopenharmony_ci	{ 0x05, 0x9a },
21962306a36Sopenharmony_ci	{ 0x06, 0x70 },
22062306a36Sopenharmony_ci	{ 0x07, 0xff },
22162306a36Sopenharmony_ci	{ 0x08, 0x91 },
22262306a36Sopenharmony_ci	{ 0x09, 0x90 },
22362306a36Sopenharmony_ci	{ 0x0a, 0xff },
22462306a36Sopenharmony_ci	{ 0x0b, 0x8f },
22562306a36Sopenharmony_ci	{ 0x0c, 0x60 },
22662306a36Sopenharmony_ci	{ 0x0d, 0x58 },
22762306a36Sopenharmony_ci	{ 0x0e, 0x48 },
22862306a36Sopenharmony_ci	{ 0x0f, 0x38 },
22962306a36Sopenharmony_ci	{ 0x10, 0x2b },
23062306a36Sopenharmony_ci	{ 0xff, 0x30 },
23162306a36Sopenharmony_ci	{ 0xff, 0x52 },
23262306a36Sopenharmony_ci	{ 0xff, 0x00 },
23362306a36Sopenharmony_ci	{ 0x36, 0x0a },
23462306a36Sopenharmony_ci};
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic inline struct nv3052c *to_nv3052c(struct drm_panel *panel)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	return container_of(panel, struct nv3052c, panel);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int nv3052c_prepare(struct drm_panel *panel)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct nv3052c *priv = to_nv3052c(panel);
24462306a36Sopenharmony_ci	struct mipi_dbi *dbi = &priv->dbi;
24562306a36Sopenharmony_ci	unsigned int i;
24662306a36Sopenharmony_ci	int err;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	err = regulator_enable(priv->supply);
24962306a36Sopenharmony_ci	if (err) {
25062306a36Sopenharmony_ci		dev_err(priv->dev, "Failed to enable power supply: %d\n", err);
25162306a36Sopenharmony_ci		return err;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* Reset the chip */
25562306a36Sopenharmony_ci	gpiod_set_value_cansleep(priv->reset_gpio, 1);
25662306a36Sopenharmony_ci	usleep_range(10, 1000);
25762306a36Sopenharmony_ci	gpiod_set_value_cansleep(priv->reset_gpio, 0);
25862306a36Sopenharmony_ci	usleep_range(5000, 20000);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(nv3052c_panel_regs); i++) {
26162306a36Sopenharmony_ci		err = mipi_dbi_command(dbi, nv3052c_panel_regs[i].cmd,
26262306a36Sopenharmony_ci				       nv3052c_panel_regs[i].val);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		if (err) {
26562306a36Sopenharmony_ci			dev_err(priv->dev, "Unable to set register: %d\n", err);
26662306a36Sopenharmony_ci			goto err_disable_regulator;
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	err = mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
27162306a36Sopenharmony_ci	if (err) {
27262306a36Sopenharmony_ci		dev_err(priv->dev, "Unable to exit sleep mode: %d\n", err);
27362306a36Sopenharmony_ci		goto err_disable_regulator;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	return 0;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cierr_disable_regulator:
27962306a36Sopenharmony_ci	regulator_disable(priv->supply);
28062306a36Sopenharmony_ci	return err;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic int nv3052c_unprepare(struct drm_panel *panel)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct nv3052c *priv = to_nv3052c(panel);
28662306a36Sopenharmony_ci	struct mipi_dbi *dbi = &priv->dbi;
28762306a36Sopenharmony_ci	int err;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	err = mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE);
29062306a36Sopenharmony_ci	if (err)
29162306a36Sopenharmony_ci		dev_err(priv->dev, "Unable to enter sleep mode: %d\n", err);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	gpiod_set_value_cansleep(priv->reset_gpio, 1);
29462306a36Sopenharmony_ci	regulator_disable(priv->supply);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	return 0;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic int nv3052c_enable(struct drm_panel *panel)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct nv3052c *priv = to_nv3052c(panel);
30262306a36Sopenharmony_ci	struct mipi_dbi *dbi = &priv->dbi;
30362306a36Sopenharmony_ci	int err;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
30662306a36Sopenharmony_ci	if (err) {
30762306a36Sopenharmony_ci		dev_err(priv->dev, "Unable to enable display: %d\n", err);
30862306a36Sopenharmony_ci		return err;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (panel->backlight) {
31262306a36Sopenharmony_ci		/* Wait for the picture to be ready before enabling backlight */
31362306a36Sopenharmony_ci		msleep(120);
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return 0;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int nv3052c_disable(struct drm_panel *panel)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct nv3052c *priv = to_nv3052c(panel);
32262306a36Sopenharmony_ci	struct mipi_dbi *dbi = &priv->dbi;
32362306a36Sopenharmony_ci	int err;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
32662306a36Sopenharmony_ci	if (err) {
32762306a36Sopenharmony_ci		dev_err(priv->dev, "Unable to disable display: %d\n", err);
32862306a36Sopenharmony_ci		return err;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	return 0;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic int nv3052c_get_modes(struct drm_panel *panel,
33562306a36Sopenharmony_ci			     struct drm_connector *connector)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	struct nv3052c *priv = to_nv3052c(panel);
33862306a36Sopenharmony_ci	const struct nv3052c_panel_info *panel_info = priv->panel_info;
33962306a36Sopenharmony_ci	struct drm_display_mode *mode;
34062306a36Sopenharmony_ci	unsigned int i;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	for (i = 0; i < panel_info->num_modes; i++) {
34362306a36Sopenharmony_ci		mode = drm_mode_duplicate(connector->dev,
34462306a36Sopenharmony_ci					  &panel_info->display_modes[i]);
34562306a36Sopenharmony_ci		if (!mode)
34662306a36Sopenharmony_ci			return -ENOMEM;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		drm_mode_set_name(mode);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci		mode->type = DRM_MODE_TYPE_DRIVER;
35162306a36Sopenharmony_ci		if (panel_info->num_modes == 1)
35262306a36Sopenharmony_ci			mode->type |= DRM_MODE_TYPE_PREFERRED;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		drm_mode_probed_add(connector, mode);
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	connector->display_info.bpc = 8;
35862306a36Sopenharmony_ci	connector->display_info.width_mm = panel_info->width_mm;
35962306a36Sopenharmony_ci	connector->display_info.height_mm = panel_info->height_mm;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	drm_display_info_set_bus_formats(&connector->display_info,
36262306a36Sopenharmony_ci					 &panel_info->bus_format, 1);
36362306a36Sopenharmony_ci	connector->display_info.bus_flags = panel_info->bus_flags;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return panel_info->num_modes;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic const struct drm_panel_funcs nv3052c_funcs = {
36962306a36Sopenharmony_ci	.prepare	= nv3052c_prepare,
37062306a36Sopenharmony_ci	.unprepare	= nv3052c_unprepare,
37162306a36Sopenharmony_ci	.enable		= nv3052c_enable,
37262306a36Sopenharmony_ci	.disable	= nv3052c_disable,
37362306a36Sopenharmony_ci	.get_modes	= nv3052c_get_modes,
37462306a36Sopenharmony_ci};
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic int nv3052c_probe(struct spi_device *spi)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	struct device *dev = &spi->dev;
37962306a36Sopenharmony_ci	struct nv3052c *priv;
38062306a36Sopenharmony_ci	int err;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
38362306a36Sopenharmony_ci	if (!priv)
38462306a36Sopenharmony_ci		return -ENOMEM;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	priv->dev = dev;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	priv->panel_info = of_device_get_match_data(dev);
38962306a36Sopenharmony_ci	if (!priv->panel_info)
39062306a36Sopenharmony_ci		return -EINVAL;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	priv->supply = devm_regulator_get(dev, "power");
39362306a36Sopenharmony_ci	if (IS_ERR(priv->supply))
39462306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(priv->supply), "Failed to get power supply\n");
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
39762306a36Sopenharmony_ci	if (IS_ERR(priv->reset_gpio))
39862306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n");
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	err = mipi_dbi_spi_init(spi, &priv->dbi, NULL);
40162306a36Sopenharmony_ci	if (err)
40262306a36Sopenharmony_ci		return dev_err_probe(dev, err, "MIPI DBI init failed\n");
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	priv->dbi.read_commands = NULL;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	spi_set_drvdata(spi, priv);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	drm_panel_init(&priv->panel, dev, &nv3052c_funcs,
40962306a36Sopenharmony_ci		       DRM_MODE_CONNECTOR_DPI);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	err = drm_panel_of_backlight(&priv->panel);
41262306a36Sopenharmony_ci	if (err)
41362306a36Sopenharmony_ci		return dev_err_probe(dev, err, "Failed to attach backlight\n");
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	drm_panel_add(&priv->panel);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	return 0;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic void nv3052c_remove(struct spi_device *spi)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct nv3052c *priv = spi_get_drvdata(spi);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	drm_panel_remove(&priv->panel);
42562306a36Sopenharmony_ci	drm_panel_disable(&priv->panel);
42662306a36Sopenharmony_ci	drm_panel_unprepare(&priv->panel);
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic const struct drm_display_mode ltk035c5444t_modes[] = {
43062306a36Sopenharmony_ci	{ /* 60 Hz */
43162306a36Sopenharmony_ci		.clock = 24000,
43262306a36Sopenharmony_ci		.hdisplay = 640,
43362306a36Sopenharmony_ci		.hsync_start = 640 + 96,
43462306a36Sopenharmony_ci		.hsync_end = 640 + 96 + 16,
43562306a36Sopenharmony_ci		.htotal = 640 + 96 + 16 + 48,
43662306a36Sopenharmony_ci		.vdisplay = 480,
43762306a36Sopenharmony_ci		.vsync_start = 480 + 5,
43862306a36Sopenharmony_ci		.vsync_end = 480 + 5 + 2,
43962306a36Sopenharmony_ci		.vtotal = 480 + 5 + 2 + 13,
44062306a36Sopenharmony_ci		.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
44162306a36Sopenharmony_ci	},
44262306a36Sopenharmony_ci	{ /* 50 Hz */
44362306a36Sopenharmony_ci		.clock = 18000,
44462306a36Sopenharmony_ci		.hdisplay = 640,
44562306a36Sopenharmony_ci		.hsync_start = 640 + 39,
44662306a36Sopenharmony_ci		.hsync_end = 640 + 39 + 2,
44762306a36Sopenharmony_ci		.htotal = 640 + 39 + 2 + 39,
44862306a36Sopenharmony_ci		.vdisplay = 480,
44962306a36Sopenharmony_ci		.vsync_start = 480 + 5,
45062306a36Sopenharmony_ci		.vsync_end = 480 + 5 + 2,
45162306a36Sopenharmony_ci		.vtotal = 480 + 5 + 2 + 13,
45262306a36Sopenharmony_ci		.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
45362306a36Sopenharmony_ci	},
45462306a36Sopenharmony_ci};
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic const struct nv3052c_panel_info ltk035c5444t_panel_info = {
45762306a36Sopenharmony_ci	.display_modes = ltk035c5444t_modes,
45862306a36Sopenharmony_ci	.num_modes = ARRAY_SIZE(ltk035c5444t_modes),
45962306a36Sopenharmony_ci	.width_mm = 77,
46062306a36Sopenharmony_ci	.height_mm = 64,
46162306a36Sopenharmony_ci	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
46262306a36Sopenharmony_ci	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
46362306a36Sopenharmony_ci};
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic const struct of_device_id nv3052c_of_match[] = {
46662306a36Sopenharmony_ci	{ .compatible = "leadtek,ltk035c5444t", .data = &ltk035c5444t_panel_info },
46762306a36Sopenharmony_ci	{ /* sentinel */ }
46862306a36Sopenharmony_ci};
46962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, nv3052c_of_match);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cistatic struct spi_driver nv3052c_driver = {
47262306a36Sopenharmony_ci	.driver = {
47362306a36Sopenharmony_ci		.name = "nv3052c",
47462306a36Sopenharmony_ci		.of_match_table = nv3052c_of_match,
47562306a36Sopenharmony_ci	},
47662306a36Sopenharmony_ci	.probe = nv3052c_probe,
47762306a36Sopenharmony_ci	.remove = nv3052c_remove,
47862306a36Sopenharmony_ci};
47962306a36Sopenharmony_cimodule_spi_driver(nv3052c_driver);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ciMODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
48262306a36Sopenharmony_ciMODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>");
48362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
484