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