162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2014 NVIDIA Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/of.h>
1062306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <video/mipi_display.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <drm/drm_crtc.h>
1562306a36Sopenharmony_ci#include <drm/drm_device.h>
1662306a36Sopenharmony_ci#include <drm/drm_mipi_dsi.h>
1762306a36Sopenharmony_ci#include <drm/drm_panel.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct sharp_panel {
2062306a36Sopenharmony_ci	struct drm_panel base;
2162306a36Sopenharmony_ci	/* the datasheet refers to them as DSI-LINK1 and DSI-LINK2 */
2262306a36Sopenharmony_ci	struct mipi_dsi_device *link1;
2362306a36Sopenharmony_ci	struct mipi_dsi_device *link2;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	struct regulator *supply;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	bool prepared;
2862306a36Sopenharmony_ci	bool enabled;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	const struct drm_display_mode *mode;
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic inline struct sharp_panel *to_sharp_panel(struct drm_panel *panel)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	return container_of(panel, struct sharp_panel, base);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void sharp_wait_frames(struct sharp_panel *sharp, unsigned int frames)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	unsigned int refresh = drm_mode_vrefresh(sharp->mode);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (WARN_ON(frames > refresh))
4362306a36Sopenharmony_ci		return;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	msleep(1000 / (refresh / frames));
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int sharp_panel_write(struct sharp_panel *sharp, u16 offset, u8 value)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	u8 payload[3] = { offset >> 8, offset & 0xff, value };
5162306a36Sopenharmony_ci	struct mipi_dsi_device *dsi = sharp->link1;
5262306a36Sopenharmony_ci	ssize_t err;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	err = mipi_dsi_generic_write(dsi, payload, sizeof(payload));
5562306a36Sopenharmony_ci	if (err < 0) {
5662306a36Sopenharmony_ci		dev_err(&dsi->dev, "failed to write %02x to %04x: %zd\n",
5762306a36Sopenharmony_ci			value, offset, err);
5862306a36Sopenharmony_ci		return err;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	err = mipi_dsi_dcs_nop(dsi);
6262306a36Sopenharmony_ci	if (err < 0) {
6362306a36Sopenharmony_ci		dev_err(&dsi->dev, "failed to send DCS nop: %zd\n", err);
6462306a36Sopenharmony_ci		return err;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	usleep_range(10, 20);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return 0;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic __maybe_unused int sharp_panel_read(struct sharp_panel *sharp,
7362306a36Sopenharmony_ci					   u16 offset, u8 *value)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	ssize_t err;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	cpu_to_be16s(&offset);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	err = mipi_dsi_generic_read(sharp->link1, &offset, sizeof(offset),
8062306a36Sopenharmony_ci				    value, sizeof(*value));
8162306a36Sopenharmony_ci	if (err < 0)
8262306a36Sopenharmony_ci		dev_err(&sharp->link1->dev, "failed to read from %04x: %zd\n",
8362306a36Sopenharmony_ci			offset, err);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return err;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int sharp_panel_disable(struct drm_panel *panel)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct sharp_panel *sharp = to_sharp_panel(panel);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (!sharp->enabled)
9362306a36Sopenharmony_ci		return 0;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	sharp->enabled = false;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int sharp_panel_unprepare(struct drm_panel *panel)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct sharp_panel *sharp = to_sharp_panel(panel);
10362306a36Sopenharmony_ci	int err;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (!sharp->prepared)
10662306a36Sopenharmony_ci		return 0;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	sharp_wait_frames(sharp, 4);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	err = mipi_dsi_dcs_set_display_off(sharp->link1);
11162306a36Sopenharmony_ci	if (err < 0)
11262306a36Sopenharmony_ci		dev_err(panel->dev, "failed to set display off: %d\n", err);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	err = mipi_dsi_dcs_enter_sleep_mode(sharp->link1);
11562306a36Sopenharmony_ci	if (err < 0)
11662306a36Sopenharmony_ci		dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	msleep(120);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	regulator_disable(sharp->supply);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	sharp->prepared = false;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return 0;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic int sharp_setup_symmetrical_split(struct mipi_dsi_device *left,
12862306a36Sopenharmony_ci					 struct mipi_dsi_device *right,
12962306a36Sopenharmony_ci					 const struct drm_display_mode *mode)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	int err;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	err = mipi_dsi_dcs_set_column_address(left, 0, mode->hdisplay / 2 - 1);
13462306a36Sopenharmony_ci	if (err < 0) {
13562306a36Sopenharmony_ci		dev_err(&left->dev, "failed to set column address: %d\n", err);
13662306a36Sopenharmony_ci		return err;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	err = mipi_dsi_dcs_set_page_address(left, 0, mode->vdisplay - 1);
14062306a36Sopenharmony_ci	if (err < 0) {
14162306a36Sopenharmony_ci		dev_err(&left->dev, "failed to set page address: %d\n", err);
14262306a36Sopenharmony_ci		return err;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	err = mipi_dsi_dcs_set_column_address(right, mode->hdisplay / 2,
14662306a36Sopenharmony_ci					      mode->hdisplay - 1);
14762306a36Sopenharmony_ci	if (err < 0) {
14862306a36Sopenharmony_ci		dev_err(&right->dev, "failed to set column address: %d\n", err);
14962306a36Sopenharmony_ci		return err;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	err = mipi_dsi_dcs_set_page_address(right, 0, mode->vdisplay - 1);
15362306a36Sopenharmony_ci	if (err < 0) {
15462306a36Sopenharmony_ci		dev_err(&right->dev, "failed to set page address: %d\n", err);
15562306a36Sopenharmony_ci		return err;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return 0;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int sharp_panel_prepare(struct drm_panel *panel)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct sharp_panel *sharp = to_sharp_panel(panel);
16462306a36Sopenharmony_ci	u8 format = MIPI_DCS_PIXEL_FMT_24BIT;
16562306a36Sopenharmony_ci	int err;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (sharp->prepared)
16862306a36Sopenharmony_ci		return 0;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	err = regulator_enable(sharp->supply);
17162306a36Sopenharmony_ci	if (err < 0)
17262306a36Sopenharmony_ci		return err;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/*
17562306a36Sopenharmony_ci	 * According to the datasheet, the panel needs around 10 ms to fully
17662306a36Sopenharmony_ci	 * power up. At least another 120 ms is required before exiting sleep
17762306a36Sopenharmony_ci	 * mode to make sure the panel is ready. Throw in another 20 ms for
17862306a36Sopenharmony_ci	 * good measure.
17962306a36Sopenharmony_ci	 */
18062306a36Sopenharmony_ci	msleep(150);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	err = mipi_dsi_dcs_exit_sleep_mode(sharp->link1);
18362306a36Sopenharmony_ci	if (err < 0) {
18462306a36Sopenharmony_ci		dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
18562306a36Sopenharmony_ci		goto poweroff;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/*
18962306a36Sopenharmony_ci	 * The MIPI DCS specification mandates this delay only between the
19062306a36Sopenharmony_ci	 * exit_sleep_mode and enter_sleep_mode commands, so it isn't strictly
19162306a36Sopenharmony_ci	 * necessary here.
19262306a36Sopenharmony_ci	 */
19362306a36Sopenharmony_ci	/*
19462306a36Sopenharmony_ci	msleep(120);
19562306a36Sopenharmony_ci	*/
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/* set left-right mode */
19862306a36Sopenharmony_ci	err = sharp_panel_write(sharp, 0x1000, 0x2a);
19962306a36Sopenharmony_ci	if (err < 0) {
20062306a36Sopenharmony_ci		dev_err(panel->dev, "failed to set left-right mode: %d\n", err);
20162306a36Sopenharmony_ci		goto poweroff;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* enable command mode */
20562306a36Sopenharmony_ci	err = sharp_panel_write(sharp, 0x1001, 0x01);
20662306a36Sopenharmony_ci	if (err < 0) {
20762306a36Sopenharmony_ci		dev_err(panel->dev, "failed to enable command mode: %d\n", err);
20862306a36Sopenharmony_ci		goto poweroff;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	err = mipi_dsi_dcs_set_pixel_format(sharp->link1, format);
21262306a36Sopenharmony_ci	if (err < 0) {
21362306a36Sopenharmony_ci		dev_err(panel->dev, "failed to set pixel format: %d\n", err);
21462306a36Sopenharmony_ci		goto poweroff;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/*
21862306a36Sopenharmony_ci	 * TODO: The device supports both left-right and even-odd split
21962306a36Sopenharmony_ci	 * configurations, but this driver currently supports only the left-
22062306a36Sopenharmony_ci	 * right split. To support a different mode a mechanism needs to be
22162306a36Sopenharmony_ci	 * put in place to communicate the configuration back to the DSI host
22262306a36Sopenharmony_ci	 * controller.
22362306a36Sopenharmony_ci	 */
22462306a36Sopenharmony_ci	err = sharp_setup_symmetrical_split(sharp->link1, sharp->link2,
22562306a36Sopenharmony_ci					    sharp->mode);
22662306a36Sopenharmony_ci	if (err < 0) {
22762306a36Sopenharmony_ci		dev_err(panel->dev, "failed to set up symmetrical split: %d\n",
22862306a36Sopenharmony_ci			err);
22962306a36Sopenharmony_ci		goto poweroff;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	err = mipi_dsi_dcs_set_display_on(sharp->link1);
23362306a36Sopenharmony_ci	if (err < 0) {
23462306a36Sopenharmony_ci		dev_err(panel->dev, "failed to set display on: %d\n", err);
23562306a36Sopenharmony_ci		goto poweroff;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	sharp->prepared = true;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/* wait for 6 frames before continuing */
24162306a36Sopenharmony_ci	sharp_wait_frames(sharp, 6);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return 0;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cipoweroff:
24662306a36Sopenharmony_ci	regulator_disable(sharp->supply);
24762306a36Sopenharmony_ci	return err;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic int sharp_panel_enable(struct drm_panel *panel)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct sharp_panel *sharp = to_sharp_panel(panel);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (sharp->enabled)
25562306a36Sopenharmony_ci		return 0;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	sharp->enabled = true;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic const struct drm_display_mode default_mode = {
26362306a36Sopenharmony_ci	.clock = 278000,
26462306a36Sopenharmony_ci	.hdisplay = 2560,
26562306a36Sopenharmony_ci	.hsync_start = 2560 + 128,
26662306a36Sopenharmony_ci	.hsync_end = 2560 + 128 + 64,
26762306a36Sopenharmony_ci	.htotal = 2560 + 128 + 64 + 64,
26862306a36Sopenharmony_ci	.vdisplay = 1600,
26962306a36Sopenharmony_ci	.vsync_start = 1600 + 4,
27062306a36Sopenharmony_ci	.vsync_end = 1600 + 4 + 8,
27162306a36Sopenharmony_ci	.vtotal = 1600 + 4 + 8 + 32,
27262306a36Sopenharmony_ci};
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic int sharp_panel_get_modes(struct drm_panel *panel,
27562306a36Sopenharmony_ci				 struct drm_connector *connector)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct drm_display_mode *mode;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	mode = drm_mode_duplicate(connector->dev, &default_mode);
28062306a36Sopenharmony_ci	if (!mode) {
28162306a36Sopenharmony_ci		dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
28262306a36Sopenharmony_ci			default_mode.hdisplay, default_mode.vdisplay,
28362306a36Sopenharmony_ci			drm_mode_vrefresh(&default_mode));
28462306a36Sopenharmony_ci		return -ENOMEM;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	drm_mode_set_name(mode);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	drm_mode_probed_add(connector, mode);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	connector->display_info.width_mm = 217;
29262306a36Sopenharmony_ci	connector->display_info.height_mm = 136;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return 1;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic const struct drm_panel_funcs sharp_panel_funcs = {
29862306a36Sopenharmony_ci	.disable = sharp_panel_disable,
29962306a36Sopenharmony_ci	.unprepare = sharp_panel_unprepare,
30062306a36Sopenharmony_ci	.prepare = sharp_panel_prepare,
30162306a36Sopenharmony_ci	.enable = sharp_panel_enable,
30262306a36Sopenharmony_ci	.get_modes = sharp_panel_get_modes,
30362306a36Sopenharmony_ci};
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic const struct of_device_id sharp_of_match[] = {
30662306a36Sopenharmony_ci	{ .compatible = "sharp,lq101r1sx01", },
30762306a36Sopenharmony_ci	{ }
30862306a36Sopenharmony_ci};
30962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sharp_of_match);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic int sharp_panel_add(struct sharp_panel *sharp)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	int ret;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	sharp->mode = &default_mode;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	sharp->supply = devm_regulator_get(&sharp->link1->dev, "power");
31862306a36Sopenharmony_ci	if (IS_ERR(sharp->supply))
31962306a36Sopenharmony_ci		return PTR_ERR(sharp->supply);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	drm_panel_init(&sharp->base, &sharp->link1->dev, &sharp_panel_funcs,
32262306a36Sopenharmony_ci		       DRM_MODE_CONNECTOR_DSI);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	ret = drm_panel_of_backlight(&sharp->base);
32562306a36Sopenharmony_ci	if (ret)
32662306a36Sopenharmony_ci		return ret;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	drm_panel_add(&sharp->base);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return 0;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic void sharp_panel_del(struct sharp_panel *sharp)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	if (sharp->base.dev)
33662306a36Sopenharmony_ci		drm_panel_remove(&sharp->base);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (sharp->link2)
33962306a36Sopenharmony_ci		put_device(&sharp->link2->dev);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic int sharp_panel_probe(struct mipi_dsi_device *dsi)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	struct mipi_dsi_device *secondary = NULL;
34562306a36Sopenharmony_ci	struct sharp_panel *sharp;
34662306a36Sopenharmony_ci	struct device_node *np;
34762306a36Sopenharmony_ci	int err;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	dsi->lanes = 4;
35062306a36Sopenharmony_ci	dsi->format = MIPI_DSI_FMT_RGB888;
35162306a36Sopenharmony_ci	dsi->mode_flags = MIPI_DSI_MODE_LPM;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/* Find DSI-LINK1 */
35462306a36Sopenharmony_ci	np = of_parse_phandle(dsi->dev.of_node, "link2", 0);
35562306a36Sopenharmony_ci	if (np) {
35662306a36Sopenharmony_ci		secondary = of_find_mipi_dsi_device_by_node(np);
35762306a36Sopenharmony_ci		of_node_put(np);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		if (!secondary)
36062306a36Sopenharmony_ci			return -EPROBE_DEFER;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	/* register a panel for only the DSI-LINK1 interface */
36462306a36Sopenharmony_ci	if (secondary) {
36562306a36Sopenharmony_ci		sharp = devm_kzalloc(&dsi->dev, sizeof(*sharp), GFP_KERNEL);
36662306a36Sopenharmony_ci		if (!sharp) {
36762306a36Sopenharmony_ci			put_device(&secondary->dev);
36862306a36Sopenharmony_ci			return -ENOMEM;
36962306a36Sopenharmony_ci		}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		mipi_dsi_set_drvdata(dsi, sharp);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		sharp->link2 = secondary;
37462306a36Sopenharmony_ci		sharp->link1 = dsi;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci		err = sharp_panel_add(sharp);
37762306a36Sopenharmony_ci		if (err < 0) {
37862306a36Sopenharmony_ci			put_device(&secondary->dev);
37962306a36Sopenharmony_ci			return err;
38062306a36Sopenharmony_ci		}
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	err = mipi_dsi_attach(dsi);
38462306a36Sopenharmony_ci	if (err < 0) {
38562306a36Sopenharmony_ci		if (secondary)
38662306a36Sopenharmony_ci			sharp_panel_del(sharp);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		return err;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	return 0;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic void sharp_panel_remove(struct mipi_dsi_device *dsi)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi);
39762306a36Sopenharmony_ci	int err;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/* only detach from host for the DSI-LINK2 interface */
40062306a36Sopenharmony_ci	if (!sharp) {
40162306a36Sopenharmony_ci		mipi_dsi_detach(dsi);
40262306a36Sopenharmony_ci		return;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	err = drm_panel_disable(&sharp->base);
40662306a36Sopenharmony_ci	if (err < 0)
40762306a36Sopenharmony_ci		dev_err(&dsi->dev, "failed to disable panel: %d\n", err);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	err = mipi_dsi_detach(dsi);
41062306a36Sopenharmony_ci	if (err < 0)
41162306a36Sopenharmony_ci		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	sharp_panel_del(sharp);
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic void sharp_panel_shutdown(struct mipi_dsi_device *dsi)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/* nothing to do for DSI-LINK2 */
42162306a36Sopenharmony_ci	if (!sharp)
42262306a36Sopenharmony_ci		return;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	drm_panel_disable(&sharp->base);
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic struct mipi_dsi_driver sharp_panel_driver = {
42862306a36Sopenharmony_ci	.driver = {
42962306a36Sopenharmony_ci		.name = "panel-sharp-lq101r1sx01",
43062306a36Sopenharmony_ci		.of_match_table = sharp_of_match,
43162306a36Sopenharmony_ci	},
43262306a36Sopenharmony_ci	.probe = sharp_panel_probe,
43362306a36Sopenharmony_ci	.remove = sharp_panel_remove,
43462306a36Sopenharmony_ci	.shutdown = sharp_panel_shutdown,
43562306a36Sopenharmony_ci};
43662306a36Sopenharmony_cimodule_mipi_dsi_driver(sharp_panel_driver);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ciMODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
43962306a36Sopenharmony_ciMODULE_DESCRIPTION("Sharp LQ101R1SX01 panel driver");
44062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
441