1// SPDX-License-Identifier: GPL-2.0-only
2/* Copyright (c) 2021 Linaro Ltd.
3 * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree:
4 *   Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
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/regulator/consumer.h>
12
13#include <video/mipi_display.h>
14
15#include <drm/drm_mipi_dsi.h>
16#include <drm/drm_modes.h>
17#include <drm/drm_panel.h>
18
19struct sharp_ls060 {
20	struct drm_panel panel;
21	struct mipi_dsi_device *dsi;
22	struct regulator *vddi_supply;
23	struct regulator *vddh_supply;
24	struct regulator *avdd_supply;
25	struct regulator *avee_supply;
26	struct gpio_desc *reset_gpio;
27	bool prepared;
28};
29
30static inline struct sharp_ls060 *to_sharp_ls060(struct drm_panel *panel)
31{
32	return container_of(panel, struct sharp_ls060, panel);
33}
34
35static void sharp_ls060_reset(struct sharp_ls060 *ctx)
36{
37	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
38	usleep_range(10000, 11000);
39	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
40	usleep_range(10000, 11000);
41	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
42	usleep_range(10000, 11000);
43}
44
45static int sharp_ls060_on(struct sharp_ls060 *ctx)
46{
47	struct mipi_dsi_device *dsi = ctx->dsi;
48	struct device *dev = &dsi->dev;
49	int ret;
50
51	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
52
53	mipi_dsi_dcs_write_seq(dsi, 0xbb, 0x13);
54	mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_MEMORY_START);
55
56	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
57	if (ret < 0) {
58		dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
59		return ret;
60	}
61	msleep(120);
62
63	ret = mipi_dsi_dcs_set_display_on(dsi);
64	if (ret < 0) {
65		dev_err(dev, "Failed to set display on: %d\n", ret);
66		return ret;
67	}
68	msleep(50);
69
70	return 0;
71}
72
73static int sharp_ls060_off(struct sharp_ls060 *ctx)
74{
75	struct mipi_dsi_device *dsi = ctx->dsi;
76	struct device *dev = &dsi->dev;
77	int ret;
78
79	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
80
81	ret = mipi_dsi_dcs_set_display_off(dsi);
82	if (ret < 0) {
83		dev_err(dev, "Failed to set display off: %d\n", ret);
84		return ret;
85	}
86	usleep_range(2000, 3000);
87
88	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
89	if (ret < 0) {
90		dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
91		return ret;
92	}
93	msleep(121);
94
95	return 0;
96}
97
98static int sharp_ls060_prepare(struct drm_panel *panel)
99{
100	struct sharp_ls060 *ctx = to_sharp_ls060(panel);
101	struct device *dev = &ctx->dsi->dev;
102	int ret;
103
104	if (ctx->prepared)
105		return 0;
106
107	ret = regulator_enable(ctx->vddi_supply);
108	if (ret < 0)
109		return ret;
110
111	ret = regulator_enable(ctx->avdd_supply);
112	if (ret < 0)
113		goto err_avdd;
114
115	usleep_range(1000, 2000);
116
117	ret = regulator_enable(ctx->avee_supply);
118	if (ret < 0)
119		goto err_avee;
120
121	usleep_range(10000, 11000);
122
123	ret = regulator_enable(ctx->vddh_supply);
124	if (ret < 0)
125		goto err_vddh;
126
127	usleep_range(10000, 11000);
128
129	sharp_ls060_reset(ctx);
130
131	ret = sharp_ls060_on(ctx);
132	if (ret < 0) {
133		dev_err(dev, "Failed to initialize panel: %d\n", ret);
134		goto err_on;
135	}
136
137	ctx->prepared = true;
138
139	return 0;
140
141err_on:
142	regulator_disable(ctx->vddh_supply);
143
144	usleep_range(10000, 11000);
145
146err_vddh:
147	regulator_disable(ctx->avee_supply);
148
149err_avee:
150	regulator_disable(ctx->avdd_supply);
151
152	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
153
154err_avdd:
155	regulator_disable(ctx->vddi_supply);
156
157	return ret;
158}
159
160static int sharp_ls060_unprepare(struct drm_panel *panel)
161{
162	struct sharp_ls060 *ctx = to_sharp_ls060(panel);
163	struct device *dev = &ctx->dsi->dev;
164	int ret;
165
166	if (!ctx->prepared)
167		return 0;
168
169	ret = sharp_ls060_off(ctx);
170	if (ret < 0)
171		dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
172
173	regulator_disable(ctx->vddh_supply);
174
175	usleep_range(10000, 11000);
176
177	regulator_disable(ctx->avee_supply);
178	regulator_disable(ctx->avdd_supply);
179
180	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
181
182	regulator_disable(ctx->vddi_supply);
183
184	ctx->prepared = false;
185	return 0;
186}
187
188static const struct drm_display_mode sharp_ls060_mode = {
189	.clock = (1080 + 96 + 16 + 64) * (1920 + 4 + 1 + 16) * 60 / 1000,
190	.hdisplay = 1080,
191	.hsync_start = 1080 + 96,
192	.hsync_end = 1080 + 96 + 16,
193	.htotal = 1080 + 96 + 16 + 64,
194	.vdisplay = 1920,
195	.vsync_start = 1920 + 4,
196	.vsync_end = 1920 + 4 + 1,
197	.vtotal = 1920 + 4 + 1 + 16,
198	.width_mm = 75,
199	.height_mm = 132,
200};
201
202static int sharp_ls060_get_modes(struct drm_panel *panel,
203				 struct drm_connector *connector)
204{
205	struct drm_display_mode *mode;
206
207	mode = drm_mode_duplicate(connector->dev, &sharp_ls060_mode);
208	if (!mode)
209		return -ENOMEM;
210
211	drm_mode_set_name(mode);
212
213	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
214	connector->display_info.width_mm = mode->width_mm;
215	connector->display_info.height_mm = mode->height_mm;
216	drm_mode_probed_add(connector, mode);
217
218	return 1;
219}
220
221static const struct drm_panel_funcs sharp_ls060_panel_funcs = {
222	.prepare = sharp_ls060_prepare,
223	.unprepare = sharp_ls060_unprepare,
224	.get_modes = sharp_ls060_get_modes,
225};
226
227static int sharp_ls060_probe(struct mipi_dsi_device *dsi)
228{
229	struct device *dev = &dsi->dev;
230	struct sharp_ls060 *ctx;
231	int ret;
232
233	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
234	if (!ctx)
235		return -ENOMEM;
236
237	ctx->vddi_supply = devm_regulator_get(dev, "vddi");
238	if (IS_ERR(ctx->vddi_supply))
239		return PTR_ERR(ctx->vddi_supply);
240
241	ctx->vddh_supply = devm_regulator_get(dev, "vddh");
242	if (IS_ERR(ctx->vddh_supply))
243		return PTR_ERR(ctx->vddh_supply);
244
245	ctx->avdd_supply = devm_regulator_get(dev, "avdd");
246	if (IS_ERR(ctx->avdd_supply))
247		return PTR_ERR(ctx->avdd_supply);
248
249	ctx->avee_supply = devm_regulator_get(dev, "avee");
250	if (IS_ERR(ctx->avee_supply))
251		return PTR_ERR(ctx->avee_supply);
252
253	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
254	if (IS_ERR(ctx->reset_gpio))
255		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
256				     "Failed to get reset-gpios\n");
257
258	ctx->dsi = dsi;
259	mipi_dsi_set_drvdata(dsi, ctx);
260
261	dsi->lanes = 4;
262	dsi->format = MIPI_DSI_FMT_RGB888;
263	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
264			  MIPI_DSI_MODE_NO_EOT_PACKET |
265			  MIPI_DSI_CLOCK_NON_CONTINUOUS;
266
267	drm_panel_init(&ctx->panel, dev, &sharp_ls060_panel_funcs,
268		       DRM_MODE_CONNECTOR_DSI);
269
270	ret = drm_panel_of_backlight(&ctx->panel);
271	if (ret)
272		return dev_err_probe(dev, ret, "Failed to get backlight\n");
273
274	drm_panel_add(&ctx->panel);
275
276	ret = mipi_dsi_attach(dsi);
277	if (ret < 0) {
278		dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
279		drm_panel_remove(&ctx->panel);
280		return ret;
281	}
282
283	return 0;
284}
285
286static void sharp_ls060_remove(struct mipi_dsi_device *dsi)
287{
288	struct sharp_ls060 *ctx = mipi_dsi_get_drvdata(dsi);
289	int ret;
290
291	ret = mipi_dsi_detach(dsi);
292	if (ret < 0)
293		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
294
295	drm_panel_remove(&ctx->panel);
296}
297
298static const struct of_device_id sharp_ls060t1sx01_of_match[] = {
299	{ .compatible = "sharp,ls060t1sx01" },
300	{ /* sentinel */ }
301};
302MODULE_DEVICE_TABLE(of, sharp_ls060t1sx01_of_match);
303
304static struct mipi_dsi_driver sharp_ls060_driver = {
305	.probe = sharp_ls060_probe,
306	.remove = sharp_ls060_remove,
307	.driver = {
308		.name = "panel-sharp-ls060t1sx01",
309		.of_match_table = sharp_ls060t1sx01_of_match,
310	},
311};
312module_mipi_dsi_driver(sharp_ls060_driver);
313
314MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>");
315MODULE_DESCRIPTION("DRM driver for Sharp LS060T1SX01 1080p video mode dsi panel");
316MODULE_LICENSE("GPL v2");
317