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