File Index
Files and directories
Sign in
FILE

panel-sharp-lq079l1sx01.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/panel/panel-sharp-lq079l1sx01.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2016 XiaoMi, Inc.
  4 * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
  5 */
  6
  7#include <linux/delay.h>
  8#include <linux/gpio/consumer.h>
  9#include <linux/module.h>
 10#include <linux/of.h>
 11#include <linux/of_graph.h>
 12#include <linux/regulator/consumer.h>
 13
 14#include <video/mipi_display.h>
 15
 16#include <drm/drm_connector.h>
 17#include <drm/drm_crtc.h>
 18#include <drm/drm_device.h>
 19#include <drm/drm_mipi_dsi.h>
 20#include <drm/drm_modes.h>
 21#include <drm/drm_panel.h>
 22#include <drm/drm_probe_helper.h>
 23
 24static const struct regulator_bulk_data sharp_supplies[] = {
 25	{ .supply = "avdd" }, { .supply = "vddio" },
 26	{ .supply = "vsp" }, { .supply = "vsn" },
 27};
 28
 29struct sharp_panel {
 30	struct drm_panel panel;
 31	struct mipi_dsi_device *dsi[2];
 32
 33	struct gpio_desc *reset_gpio;
 34	struct regulator_bulk_data *supplies;
 35
 36	const struct drm_display_mode *mode;
 37};
 38
 39static inline struct sharp_panel *to_sharp_panel(struct drm_panel *panel)
 40{
 41	return container_of(panel, struct sharp_panel, panel);
 42}
 43
 44static void sharp_panel_reset(struct sharp_panel *sharp)
 45{
 46	gpiod_set_value_cansleep(sharp->reset_gpio, 1);
 47	usleep_range(2000, 3000);
 48	gpiod_set_value_cansleep(sharp->reset_gpio, 0);
 49	usleep_range(2000, 3000);
 50}
 51
 52static int sharp_panel_prepare(struct drm_panel *panel)
 53{
 54	struct sharp_panel *sharp = to_sharp_panel(panel);
 55	struct device *dev = panel->dev;
 56	struct mipi_dsi_device *dsi0 = sharp->dsi[0];
 57	struct mipi_dsi_device *dsi1 = sharp->dsi[1];
 58	struct mipi_dsi_multi_context dsi_ctx = { .dsi = NULL };
 59	int ret;
 60
 61	ret = regulator_bulk_enable(ARRAY_SIZE(sharp_supplies), sharp->supplies);
 62	if (ret) {
 63		dev_err(dev, "error enabling regulators (%d)\n", ret);
 64		return ret;
 65	}
 66
 67	msleep(24);
 68
 69	if (sharp->reset_gpio)
 70		sharp_panel_reset(sharp);
 71
 72	msleep(32);
 73
 74	mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_EXIT_SLEEP_MODE);
 75	mipi_dsi_msleep(&dsi_ctx, 120);
 76
 77	mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1,
 78					  MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0xff);
 79	mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1,
 80					  MIPI_DCS_WRITE_POWER_SAVE, 0x01);
 81	mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1,
 82					  MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x2c);
 83
 84	mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_SET_DISPLAY_ON);
 85
 86	return 0;
 87}
 88
 89static int sharp_panel_unprepare(struct drm_panel *panel)
 90{
 91	struct sharp_panel *sharp = to_sharp_panel(panel);
 92	struct mipi_dsi_device *dsi0 = sharp->dsi[0];
 93	struct mipi_dsi_device *dsi1 = sharp->dsi[1];
 94	struct mipi_dsi_multi_context dsi_ctx = { .dsi = NULL };
 95
 96	mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_SET_DISPLAY_OFF);
 97	mipi_dsi_msleep(&dsi_ctx, 100);
 98	mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_ENTER_SLEEP_MODE);
 99	mipi_dsi_msleep(&dsi_ctx, 150);
100
101	if (sharp->reset_gpio)
102		gpiod_set_value_cansleep(sharp->reset_gpio, 1);
103
104	return regulator_bulk_disable(ARRAY_SIZE(sharp_supplies), sharp->supplies);
105}
106
107static const struct drm_display_mode default_mode = {
108	.clock = (1536 + 136 + 28 + 28) * (2048 + 14 + 8 + 2) * 60 / 1000,
109	.hdisplay = 1536,
110	.hsync_start = 1536 + 136,
111	.hsync_end = 1536 + 136 + 28,
112	.htotal = 1536 + 136 + 28 + 28,
113	.vdisplay = 2048,
114	.vsync_start = 2048 + 14,
115	.vsync_end = 2048 + 14 + 8,
116	.vtotal = 2048 + 14 + 8 + 2,
117	.width_mm = 120,
118	.height_mm = 160,
119	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
120};
121
122static int sharp_panel_get_modes(struct drm_panel *panel,
123				 struct drm_connector *connector)
124{
125	return drm_connector_helper_get_modes_fixed(connector, &default_mode);
126}
127
128static const struct drm_panel_funcs sharp_panel_funcs = {
129	.unprepare = sharp_panel_unprepare,
130	.prepare = sharp_panel_prepare,
131	.get_modes = sharp_panel_get_modes,
132};
133
134static int sharp_panel_probe(struct mipi_dsi_device *dsi)
135{
136	const struct mipi_dsi_device_info info = { "sharp-link1", 0, NULL };
137	struct device *dev = &dsi->dev;
138	struct device_node *dsi_r;
139	struct mipi_dsi_host *dsi_r_host;
140	struct sharp_panel *sharp;
141	int i, ret;
142
143	sharp = devm_drm_panel_alloc(dev, struct sharp_panel, panel,
144				     &sharp_panel_funcs, DRM_MODE_CONNECTOR_DSI);
145	if (IS_ERR(sharp))
146		return PTR_ERR(sharp);
147
148	ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(sharp_supplies),
149					    sharp_supplies, &sharp->supplies);
150	if (ret)
151		return dev_err_probe(dev, ret, "failed to get supplies\n");
152
153	sharp->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
154	if (IS_ERR(sharp->reset_gpio))
155		return dev_err_probe(dev, PTR_ERR(sharp->reset_gpio),
156				     "failed to get reset GPIO\n");
157
158	/* Panel is always connected to two DSI hosts, DSI0 is left, DSI1 is right */
159	dsi_r = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
160	if (!dsi_r)
161		return dev_err_probe(dev, -ENODEV, "failed to find second DSI host node\n");
162
163	dsi_r_host = of_find_mipi_dsi_host_by_node(dsi_r);
164	of_node_put(dsi_r);
165	if (!dsi_r_host)
166		return dev_err_probe(dev, -EPROBE_DEFER, "cannot get secondary DSI host\n");
167
168	sharp->dsi[1] = devm_mipi_dsi_device_register_full(dev, dsi_r_host, &info);
169	if (IS_ERR(sharp->dsi[1]))
170		return dev_err_probe(dev, PTR_ERR(sharp->dsi[1]),
171				     "second link registration failed\n");
172
173	sharp->dsi[0] = dsi;
174	mipi_dsi_set_drvdata(dsi, sharp);
175
176	ret = drm_panel_of_backlight(&sharp->panel);
177	if (ret)
178		return dev_err_probe(dev, ret, "Failed to get backlight\n");
179
180	drm_panel_add(&sharp->panel);
181
182	for (i = 0; i < ARRAY_SIZE(sharp->dsi); i++) {
183		if (!sharp->dsi[i])
184			continue;
185
186		sharp->dsi[i]->lanes = 4;
187		sharp->dsi[i]->format = MIPI_DSI_FMT_RGB888;
188		sharp->dsi[i]->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM;
189
190		ret = devm_mipi_dsi_attach(dev, sharp->dsi[i]);
191		if (ret < 0) {
192			drm_panel_remove(&sharp->panel);
193			return dev_err_probe(dev, ret, "failed to attach to DSI%d\n", i);
194		}
195	}
196
197	return 0;
198}
199
200static void sharp_panel_remove(struct mipi_dsi_device *dsi)
201{
202	struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi);
203
204	drm_panel_remove(&sharp->panel);
205}
206
207static const struct of_device_id sharp_of_match[] = {
208	{ .compatible = "sharp,lq079l1sx01" },
209	{ }
210};
211MODULE_DEVICE_TABLE(of, sharp_of_match);
212
213static struct mipi_dsi_driver sharp_panel_driver = {
214	.driver = {
215		.name = "panel-sharp-lq079l1sx01",
216		.of_match_table = sharp_of_match,
217	},
218	.probe = sharp_panel_probe,
219	.remove = sharp_panel_remove,
220};
221module_mipi_dsi_driver(sharp_panel_driver);
222
223MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
224MODULE_DESCRIPTION("Sharp LQ079L1SX01 panel driver");
225MODULE_LICENSE("GPL");
~/ Read-only file preview.