xref: /linux/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c (revision 18fbf5151d2c0bfe433c7428eef03cabf5fdb2fa)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * ChipWealth CH13726A MIPI-DSI panel driver
4  * Copyright (c) 2024, Teguh Sobirin <teguh@sobir.in>.
5  */
6 
7 #include <linux/backlight.h>
8 #include <linux/delay.h>
9 #include <linux/gpio/consumer.h>
10 #include <linux/module.h>
11 #include <linux/of.h>
12 #include <linux/regulator/consumer.h>
13 
14 #include <drm/drm_mipi_dsi.h>
15 #include <drm/drm_modes.h>
16 #include <drm/drm_of.h>
17 #include <drm/drm_panel.h>
18 
19 #include <video/mipi_display.h>
20 
21 static const struct regulator_bulk_data ch13726a_supplies[] = {
22 	{ .supply = "vdd1v2", },
23 	{ .supply = "vddio", },
24 	{ .supply = "vdd", },
25 	{ .supply = "avdd", },
26 };
27 
28 struct ch13726a_panel {
29 	struct drm_panel panel;
30 	struct mipi_dsi_device *dsi;
31 	struct regulator_bulk_data *supplies;
32 	struct gpio_desc *reset_gpio;
33 	struct ch13726a_desc *desc;
34 	enum drm_panel_orientation orientation;
35 };
36 
37 struct ch13726a_desc {
38 	unsigned int width_mm;
39 	unsigned int height_mm;
40 	unsigned int bpc;
41 
42 	const struct drm_display_mode *modes;
43 	unsigned int num_modes;
44 };
45 
46 static inline struct ch13726a_panel *to_ch13726a_panel(struct drm_panel *panel)
47 {
48 	return container_of(panel, struct ch13726a_panel, panel);
49 }
50 
51 static void ch13726a_reset(struct ch13726a_panel *ctx)
52 {
53 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
54 	usleep_range(10000, 11000);
55 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
56 	usleep_range(10000, 11000);
57 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
58 	usleep_range(10000, 11000);
59 }
60 
61 static int ch13726a_on(struct ch13726a_panel *ctx)
62 {
63 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
64 
65 	ctx->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
66 
67 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x50);
68 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb9, 0x00);
69 
70 	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
71 
72 	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
73 
74 	return dsi_ctx.accum_err;
75 }
76 
77 static int ch13726a_disable(struct drm_panel *panel)
78 {
79 	struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
80 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
81 
82 	ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
83 
84 	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
85 	mipi_dsi_msleep(&dsi_ctx, 50);
86 	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
87 
88 	return dsi_ctx.accum_err;
89 }
90 
91 static int ch13726a_prepare(struct drm_panel *panel)
92 {
93 	struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
94 	struct device *dev = &ctx->dsi->dev;
95 	int ret;
96 
97 	ret = regulator_bulk_enable(ARRAY_SIZE(ch13726a_supplies), ctx->supplies);
98 	if (ret < 0) {
99 		dev_err(dev, "Failed to enable regulators: %d\n", ret);
100 		return ret;
101 	}
102 
103 	ch13726a_reset(ctx);
104 
105 	ret = ch13726a_on(ctx);
106 	if (ret < 0) {
107 		dev_err(dev, "Failed to initialize panel: %d\n", ret);
108 		gpiod_set_value_cansleep(ctx->reset_gpio, 1);
109 		regulator_bulk_disable(ARRAY_SIZE(ch13726a_supplies), ctx->supplies);
110 		return ret;
111 	}
112 
113 	msleep(28);
114 
115 	return 0;
116 }
117 
118 static int ch13726a_unprepare(struct drm_panel *panel)
119 {
120 	struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
121 
122 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
123 	regulator_bulk_disable(ARRAY_SIZE(ch13726a_supplies), ctx->supplies);
124 
125 	return 0;
126 }
127 
128 static const struct drm_display_mode thor_bottom_modes[] = {
129 	{
130 		/* 120Hz */
131 		.clock = (1080 + 28 + 4 + 36) * (1240 + 16 + 4 + 8) * 120 / 1000,
132 		.hdisplay = 1080,
133 		.hsync_start = 1080 + 28,
134 		.hsync_end = 1080 + 28 + 4,
135 		.htotal = 1080 + 28 + 4 + 36,
136 		.vdisplay = 1240,
137 		.vsync_start = 1240 + 16,
138 		.vsync_end = 1240 + 16 + 4,
139 		.vtotal = 1240 + 16 + 4 + 8,
140 	},
141 	{
142 		/* 60Hz */
143 		.clock = (1080 + 28 + 4 + 36) * (1240 + 16 + 4 + 8) * 60 / 1000,
144 		.hdisplay = 1080,
145 		.hsync_start = 1080 + 28,
146 		.hsync_end = 1080 + 28 + 4,
147 		.htotal = 1080 + 28 + 4 + 36,
148 		.vdisplay = 1240,
149 		.vsync_start = 1240 + 16,
150 		.vsync_end = 1240 + 16 + 4,
151 		.vtotal = 1240 + 16 + 4 + 8,
152 	}
153 };
154 
155 static struct ch13726a_desc thor_bottom_desc = {
156 	.modes = thor_bottom_modes,
157 	.num_modes = ARRAY_SIZE(thor_bottom_modes),
158 	.width_mm = 65,
159 	.height_mm = 75,
160 	.bpc = 8,
161 };
162 
163 static int ch13726a_get_modes(struct drm_panel *panel,
164 					struct drm_connector *connector)
165 {
166 	struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
167 
168 	for (uint8_t i = 0; i < ctx->desc->num_modes; i++) {
169 		const struct drm_display_mode *m = &ctx->desc->modes[i];
170 		struct drm_display_mode *mode;
171 
172 		mode = drm_mode_duplicate(connector->dev, m);
173 		if (!mode) {
174 			dev_err(&ctx->dsi->dev, "failed to add mode %ux%u@%u\n",
175 				m->hdisplay, m->vdisplay, drm_mode_vrefresh(m));
176 			return -ENOMEM;
177 		}
178 
179 		mode->type = DRM_MODE_TYPE_DRIVER;
180 		if (i == 0)
181 			mode->type |= DRM_MODE_TYPE_PREFERRED;
182 
183 		drm_mode_set_name(mode);
184 		drm_mode_probed_add(connector, mode);
185 	}
186 
187 	connector->display_info.width_mm = ctx->desc->width_mm;
188 	connector->display_info.height_mm = ctx->desc->height_mm;
189 	connector->display_info.bpc = ctx->desc->bpc;
190 
191 	return ctx->desc->num_modes;
192 }
193 
194 static enum drm_panel_orientation ch13726a_get_orientation(struct drm_panel *panel)
195 {
196 	struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
197 
198 	return ctx->orientation;
199 }
200 
201 static const struct drm_panel_funcs ch13726a_panel_funcs = {
202 	.prepare = ch13726a_prepare,
203 	.unprepare = ch13726a_unprepare,
204 	.disable = ch13726a_disable,
205 	.get_modes = ch13726a_get_modes,
206 	.get_orientation = ch13726a_get_orientation,
207 };
208 
209 static int ch13726a_bl_update_status(struct backlight_device *bl)
210 {
211 	struct mipi_dsi_device *dsi = bl_get_data(bl);
212 	u16 brightness = backlight_get_brightness(bl);
213 	int ret;
214 
215 	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
216 
217 	ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
218 	if (ret < 0)
219 		return ret;
220 
221 	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
222 
223 	return 0;
224 }
225 
226 static const struct backlight_ops ch13726a_bl_ops = {
227 	.update_status = ch13726a_bl_update_status,
228 };
229 
230 static struct backlight_device *
231 ch13726a_create_backlight(struct mipi_dsi_device *dsi)
232 {
233 	struct device *dev = &dsi->dev;
234 	const struct backlight_properties props = {
235 		.type = BACKLIGHT_RAW,
236 		.brightness = 255,
237 		.max_brightness = 255,
238 	};
239 
240 	return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
241 					      &ch13726a_bl_ops, &props);
242 }
243 
244 static int ch13726a_probe(struct mipi_dsi_device *dsi)
245 {
246 	struct device *dev = &dsi->dev;
247 	struct ch13726a_panel *ctx;
248 	int ret;
249 
250 	ctx = devm_drm_panel_alloc(dev, __typeof(*ctx), panel,
251 				   &ch13726a_panel_funcs,
252 				   DRM_MODE_CONNECTOR_DSI);
253 	if (IS_ERR(ctx))
254 		return PTR_ERR(ctx);
255 
256 	ctx->desc = (struct ch13726a_desc *)of_device_get_match_data(dev);
257 	if (!ctx->desc)
258 		return -ENODEV;
259 
260 	ret = devm_regulator_bulk_get_const(dev,
261 					    ARRAY_SIZE(ch13726a_supplies),
262 					    ch13726a_supplies,
263 					    &ctx->supplies);
264 	if (ret < 0)
265 		return dev_err_probe(dev, ret, "Failed to get regulators\n");
266 
267 	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
268 	if (IS_ERR(ctx->reset_gpio))
269 		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
270 				     "Failed to get reset-gpios\n");
271 
272 	ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation);
273 	if (ret < 0) {
274 		dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
275 		return ret;
276 	}
277 
278 	ctx->dsi = dsi;
279 	mipi_dsi_set_drvdata(dsi, ctx);
280 
281 	dsi->lanes = 4;
282 	dsi->format = MIPI_DSI_FMT_RGB888;
283 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
284 			  MIPI_DSI_CLOCK_NON_CONTINUOUS;
285 
286 	ctx->panel.prepare_prev_first = true;
287 
288 	ctx->panel.backlight = ch13726a_create_backlight(dsi);
289 	if (IS_ERR(ctx->panel.backlight))
290 		return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
291 				     "Failed to create backlight\n");
292 
293 	drm_panel_add(&ctx->panel);
294 
295 	ret = mipi_dsi_attach(dsi);
296 	if (ret < 0) {
297 		dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
298 		drm_panel_remove(&ctx->panel);
299 		return ret;
300 	}
301 
302 	return 0;
303 }
304 
305 static void ch13726a_remove(struct mipi_dsi_device *dsi)
306 {
307 	struct ch13726a_panel *ctx = mipi_dsi_get_drvdata(dsi);
308 	int ret;
309 
310 	ret = mipi_dsi_detach(dsi);
311 	if (ret < 0)
312 		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
313 
314 	drm_panel_remove(&ctx->panel);
315 }
316 
317 static const struct of_device_id ch13726a_of_match[] = {
318 	{ .compatible = "ayntec,thor-panel-bottom", .data = &thor_bottom_desc },
319 	{ /* sentinel */ }
320 };
321 MODULE_DEVICE_TABLE(of, ch13726a_of_match);
322 
323 static struct mipi_dsi_driver ch13726a_driver = {
324 	.probe = ch13726a_probe,
325 	.remove = ch13726a_remove,
326 	.driver = {
327 		.name = "panel-ch13726a-amoled",
328 		.of_match_table = ch13726a_of_match,
329 	},
330 };
331 module_mipi_dsi_driver(ch13726a_driver);
332 
333 MODULE_DESCRIPTION("DRM driver for CH13726A DSI panels");
334 MODULE_LICENSE("GPL");
335