1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2019, The Linux Foundation. All rights reserved.
4  */
5 
6 #include <linux/delay.h>
7 #include <linux/module.h>
8 #include <linux/mod_devicetable.h>
9 #include <linux/gpio/consumer.h>
10 #include <linux/regulator/consumer.h>
11 
12 #include <video/mipi_display.h>
13 
14 #include <drm/drm_mipi_dsi.h>
15 #include <drm/drm_modes.h>
16 #include <drm/drm_panel.h>
17 
18 struct visionox_rm69299 {
19 	struct drm_panel panel;
20 	struct regulator_bulk_data supplies[2];
21 	struct gpio_desc *reset_gpio;
22 	struct mipi_dsi_device *dsi;
23 	bool prepared;
24 	bool enabled;
25 };
26 
27 static inline struct visionox_rm69299 *panel_to_ctx(struct drm_panel *panel)
28 {
29 	return container_of(panel, struct visionox_rm69299, panel);
30 }
31 
32 static int visionox_rm69299_power_on(struct visionox_rm69299 *ctx)
33 {
34 	int ret;
35 
36 	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
37 	if (ret < 0)
38 		return ret;
39 
40 	/*
41 	 * Reset sequence of visionox panel requires the panel to be
42 	 * out of reset for 10ms, followed by being held in reset
43 	 * for 10ms and then out again
44 	 */
45 	gpiod_set_value(ctx->reset_gpio, 1);
46 	usleep_range(10000, 20000);
47 	gpiod_set_value(ctx->reset_gpio, 0);
48 	usleep_range(10000, 20000);
49 	gpiod_set_value(ctx->reset_gpio, 1);
50 	usleep_range(10000, 20000);
51 
52 	return 0;
53 }
54 
55 static int visionox_rm69299_power_off(struct visionox_rm69299 *ctx)
56 {
57 	gpiod_set_value(ctx->reset_gpio, 0);
58 
59 	return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
60 }
61 
62 static int visionox_rm69299_unprepare(struct drm_panel *panel)
63 {
64 	struct visionox_rm69299 *ctx = panel_to_ctx(panel);
65 	int ret;
66 
67 	ctx->dsi->mode_flags = 0;
68 
69 	ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0);
70 	if (ret < 0)
71 		dev_err(ctx->panel.dev, "set_display_off cmd failed ret = %d\n", ret);
72 
73 	/* 120ms delay required here as per DCS spec */
74 	msleep(120);
75 
76 	ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0);
77 	if (ret < 0) {
78 		dev_err(ctx->panel.dev, "enter_sleep cmd failed ret = %d\n", ret);
79 	}
80 
81 	ret = visionox_rm69299_power_off(ctx);
82 
83 	ctx->prepared = false;
84 	return ret;
85 }
86 
87 static int visionox_rm69299_prepare(struct drm_panel *panel)
88 {
89 	struct visionox_rm69299 *ctx = panel_to_ctx(panel);
90 	int ret;
91 
92 	if (ctx->prepared)
93 		return 0;
94 
95 	ret = visionox_rm69299_power_on(ctx);
96 	if (ret < 0)
97 		return ret;
98 
99 	ctx->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
100 
101 	ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0xfe, 0x00 }, 2);
102 	if (ret < 0) {
103 		dev_err(ctx->panel.dev, "cmd set tx 0 failed, ret = %d\n", ret);
104 		goto power_off;
105 	}
106 
107 	ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0xc2, 0x08 }, 2);
108 	if (ret < 0) {
109 		dev_err(ctx->panel.dev, "cmd set tx 1 failed, ret = %d\n", ret);
110 		goto power_off;
111 	}
112 
113 	ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0x35, 0x00 }, 2);
114 	if (ret < 0) {
115 		dev_err(ctx->panel.dev, "cmd set tx 2 failed, ret = %d\n", ret);
116 		goto power_off;
117 	}
118 
119 	ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0x51, 0xff }, 2);
120 	if (ret < 0) {
121 		dev_err(ctx->panel.dev, "cmd set tx 3 failed, ret = %d\n", ret);
122 		goto power_off;
123 	}
124 
125 	ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
126 	if (ret < 0) {
127 		dev_err(ctx->panel.dev, "exit_sleep_mode cmd failed ret = %d\n", ret);
128 		goto power_off;
129 	}
130 
131 	/* Per DSI spec wait 120ms after sending exit sleep DCS command */
132 	msleep(120);
133 
134 	ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
135 	if (ret < 0) {
136 		dev_err(ctx->panel.dev, "set_display_on cmd failed ret = %d\n", ret);
137 		goto power_off;
138 	}
139 
140 	/* Per DSI spec wait 120ms after sending set_display_on DCS command */
141 	msleep(120);
142 
143 	ctx->prepared = true;
144 
145 	return 0;
146 
147 power_off:
148 	return ret;
149 }
150 
151 static const struct drm_display_mode visionox_rm69299_1080x2248_60hz = {
152 	.name = "1080x2248",
153 	.clock = 158695,
154 	.hdisplay = 1080,
155 	.hsync_start = 1080 + 26,
156 	.hsync_end = 1080 + 26 + 2,
157 	.htotal = 1080 + 26 + 2 + 36,
158 	.vdisplay = 2248,
159 	.vsync_start = 2248 + 56,
160 	.vsync_end = 2248 + 56 + 4,
161 	.vtotal = 2248 + 56 + 4 + 4,
162 	.flags = 0,
163 };
164 
165 static int visionox_rm69299_get_modes(struct drm_panel *panel,
166 				      struct drm_connector *connector)
167 {
168 	struct visionox_rm69299 *ctx = panel_to_ctx(panel);
169 	struct drm_display_mode *mode;
170 
171 	mode = drm_mode_duplicate(connector->dev,
172 				  &visionox_rm69299_1080x2248_60hz);
173 	if (!mode) {
174 		dev_err(ctx->panel.dev, "failed to create a new display mode\n");
175 		return 0;
176 	}
177 
178 	connector->display_info.width_mm = 74;
179 	connector->display_info.height_mm = 131;
180 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
181 	drm_mode_probed_add(connector, mode);
182 
183 	return 1;
184 }
185 
186 static const struct drm_panel_funcs visionox_rm69299_drm_funcs = {
187 	.unprepare = visionox_rm69299_unprepare,
188 	.prepare = visionox_rm69299_prepare,
189 	.get_modes = visionox_rm69299_get_modes,
190 };
191 
192 static int visionox_rm69299_probe(struct mipi_dsi_device *dsi)
193 {
194 	struct device *dev = &dsi->dev;
195 	struct visionox_rm69299 *ctx;
196 	int ret;
197 
198 	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
199 	if (!ctx)
200 		return -ENOMEM;
201 
202 	mipi_dsi_set_drvdata(dsi, ctx);
203 
204 	ctx->panel.dev = dev;
205 	ctx->dsi = dsi;
206 
207 	ctx->supplies[0].supply = "vdda";
208 	ctx->supplies[1].supply = "vdd3p3";
209 
210 	ret = devm_regulator_bulk_get(ctx->panel.dev, ARRAY_SIZE(ctx->supplies),
211 				      ctx->supplies);
212 	if (ret < 0)
213 		return ret;
214 
215 	ctx->reset_gpio = devm_gpiod_get(ctx->panel.dev,
216 					 "reset", GPIOD_OUT_LOW);
217 	if (IS_ERR(ctx->reset_gpio)) {
218 		dev_err(dev, "cannot get reset gpio %ld\n", PTR_ERR(ctx->reset_gpio));
219 		return PTR_ERR(ctx->reset_gpio);
220 	}
221 
222 	drm_panel_init(&ctx->panel, dev, &visionox_rm69299_drm_funcs,
223 		       DRM_MODE_CONNECTOR_DSI);
224 	ctx->panel.dev = dev;
225 	ctx->panel.funcs = &visionox_rm69299_drm_funcs;
226 	drm_panel_add(&ctx->panel);
227 
228 	dsi->lanes = 4;
229 	dsi->format = MIPI_DSI_FMT_RGB888;
230 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM |
231 			  MIPI_DSI_CLOCK_NON_CONTINUOUS;
232 	ret = mipi_dsi_attach(dsi);
233 	if (ret < 0) {
234 		dev_err(dev, "dsi attach failed ret = %d\n", ret);
235 		goto err_dsi_attach;
236 	}
237 
238 	ret = regulator_set_load(ctx->supplies[0].consumer, 32000);
239 	if (ret) {
240 		dev_err(dev, "regulator set load failed for vdda supply ret = %d\n", ret);
241 		goto err_set_load;
242 	}
243 
244 	ret = regulator_set_load(ctx->supplies[1].consumer, 13200);
245 	if (ret) {
246 		dev_err(dev, "regulator set load failed for vdd3p3 supply ret = %d\n", ret);
247 		goto err_set_load;
248 	}
249 
250 	return 0;
251 
252 err_set_load:
253 	mipi_dsi_detach(dsi);
254 err_dsi_attach:
255 	drm_panel_remove(&ctx->panel);
256 	return ret;
257 }
258 
259 static void visionox_rm69299_remove(struct mipi_dsi_device *dsi)
260 {
261 	struct visionox_rm69299 *ctx = mipi_dsi_get_drvdata(dsi);
262 
263 	mipi_dsi_detach(ctx->dsi);
264 	mipi_dsi_device_unregister(ctx->dsi);
265 
266 	drm_panel_remove(&ctx->panel);
267 }
268 
269 static const struct of_device_id visionox_rm69299_of_match[] = {
270 	{ .compatible = "visionox,rm69299-1080p-display", },
271 	{ /* sentinel */ }
272 };
273 MODULE_DEVICE_TABLE(of, visionox_rm69299_of_match);
274 
275 static struct mipi_dsi_driver visionox_rm69299_driver = {
276 	.driver = {
277 		.name = "panel-visionox-rm69299",
278 		.of_match_table = visionox_rm69299_of_match,
279 	},
280 	.probe = visionox_rm69299_probe,
281 	.remove = visionox_rm69299_remove,
282 };
283 module_mipi_dsi_driver(visionox_rm69299_driver);
284 
285 MODULE_DESCRIPTION("Visionox RM69299 DSI Panel Driver");
286 MODULE_LICENSE("GPL v2");
287