xref: /linux/drivers/gpu/drm/panel/panel-boe-td4320.c (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright (c) 2024 Barnabas Czeman <barnabas.czeman@mainlining.org>
3 // Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree:
4 //   Copyright (c) 2013, The Linux Foundation. All rights reserved.
5 
6 #include <linux/delay.h>
7 #include <linux/gpio/consumer.h>
8 #include <linux/module.h>
9 #include <linux/regulator/consumer.h>
10 
11 #include <video/mipi_display.h>
12 
13 #include <drm/drm_mipi_dsi.h>
14 #include <drm/drm_modes.h>
15 #include <drm/drm_panel.h>
16 #include <drm/drm_probe_helper.h>
17 
18 struct boe_td4320 {
19 	struct drm_panel panel;
20 	struct mipi_dsi_device *dsi;
21 	struct regulator_bulk_data *supplies;
22 	struct gpio_desc *reset_gpio;
23 };
24 
25 static const struct regulator_bulk_data boe_td4320_supplies[] = {
26 	{ .supply = "iovcc" },
27 	{ .supply = "vsn" },
28 	{ .supply = "vsp" },
29 };
30 
31 static inline struct boe_td4320 *to_boe_td4320(struct drm_panel *panel)
32 {
33 	return container_of(panel, struct boe_td4320, panel);
34 }
35 
36 static void boe_td4320_reset(struct boe_td4320 *ctx)
37 {
38 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
39 	usleep_range(1000, 2000);
40 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
41 	usleep_range(5000, 6000);
42 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
43 	msleep(30);
44 }
45 
46 static int boe_td4320_on(struct boe_td4320 *ctx)
47 {
48 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
49 
50 	ctx->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
51 
52 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb0, 0x04);
53 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0x00);
54 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb8,
55 					 0x19, 0x55, 0x00, 0xbe, 0x00, 0x00,
56 					 0x00);
57 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb9,
58 					 0x4d, 0x55, 0x05, 0xe6, 0x00, 0x02,
59 					 0x03);
60 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xba,
61 					 0x9b, 0x5b, 0x07, 0xe6, 0x00, 0x13,
62 					 0x00);
63 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9,
64 					 0x44, 0x3f, 0x00, 0x8d, 0xbf);
65 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce,
66 					 0x5d, 0x00, 0x0f, 0x1f, 0x2f, 0x3f,
67 					 0x4f, 0x5f, 0x6f, 0x7f, 0x8f, 0x9f,
68 					 0xaf, 0xbf, 0xcf, 0xdf, 0xef, 0xff,
69 					 0x04, 0x00, 0x02, 0x02, 0x42, 0x01,
70 					 0x69, 0x5a, 0x40, 0x40, 0x00, 0x00,
71 					 0x04, 0xfa, 0x00);
72 	mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x00b8);
73 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY,
74 				     0x2c);
75 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
76 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x03);
77 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x00);
78 	mipi_dsi_msleep(&dsi_ctx, 96);
79 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x00);
80 	mipi_dsi_msleep(&dsi_ctx, 20);
81 
82 	return dsi_ctx.accum_err;
83 }
84 
85 static int boe_td4320_off(struct boe_td4320 *ctx)
86 {
87 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
88 
89 	ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
90 
91 	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
92 	mipi_dsi_msleep(&dsi_ctx, 20);
93 	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
94 	mipi_dsi_msleep(&dsi_ctx, 120);
95 
96 	return dsi_ctx.accum_err;
97 }
98 
99 static int boe_td4320_prepare(struct drm_panel *panel)
100 {
101 	struct boe_td4320 *ctx = to_boe_td4320(panel);
102 	struct device *dev = &ctx->dsi->dev;
103 	int ret;
104 
105 	ret = regulator_bulk_enable(ARRAY_SIZE(boe_td4320_supplies), ctx->supplies);
106 	if (ret < 0) {
107 		dev_err(dev, "Failed to enable regulators: %d\n", ret);
108 		return ret;
109 	}
110 
111 	boe_td4320_reset(ctx);
112 
113 	ret = boe_td4320_on(ctx);
114 	if (ret < 0) {
115 		dev_err(dev, "Failed to initialize panel: %d\n", ret);
116 		gpiod_set_value_cansleep(ctx->reset_gpio, 1);
117 		regulator_bulk_disable(ARRAY_SIZE(boe_td4320_supplies), ctx->supplies);
118 		return ret;
119 	}
120 
121 	return 0;
122 }
123 
124 static int boe_td4320_unprepare(struct drm_panel *panel)
125 {
126 	struct boe_td4320 *ctx = to_boe_td4320(panel);
127 	struct device *dev = &ctx->dsi->dev;
128 	int ret;
129 
130 	ret = boe_td4320_off(ctx);
131 	if (ret < 0)
132 		dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
133 
134 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
135 	regulator_bulk_disable(ARRAY_SIZE(boe_td4320_supplies), ctx->supplies);
136 
137 	return 0;
138 }
139 
140 static const struct drm_display_mode boe_td4320_mode = {
141 	.clock = (1080 + 86 + 2 + 100) * (2340 + 4 + 4 + 60) * 60 / 1000,
142 	.hdisplay = 1080,
143 	.hsync_start = 1080 + 86,
144 	.hsync_end = 1080 + 86 + 2,
145 	.htotal = 1080 + 86 + 2 + 100,
146 	.vdisplay = 2340,
147 	.vsync_start = 2340 + 4,
148 	.vsync_end = 2340 + 4 + 4,
149 	.vtotal = 2340 + 4 + 4 + 60,
150 	.width_mm = 67,
151 	.height_mm = 145,
152 	.type = DRM_MODE_TYPE_DRIVER,
153 };
154 
155 static int boe_td4320_get_modes(struct drm_panel *panel,
156 				struct drm_connector *connector)
157 {
158 	return drm_connector_helper_get_modes_fixed(connector, &boe_td4320_mode);
159 }
160 
161 static const struct drm_panel_funcs boe_td4320_panel_funcs = {
162 	.prepare = boe_td4320_prepare,
163 	.unprepare = boe_td4320_unprepare,
164 	.get_modes = boe_td4320_get_modes,
165 };
166 
167 static int boe_td4320_probe(struct mipi_dsi_device *dsi)
168 {
169 	struct device *dev = &dsi->dev;
170 	struct boe_td4320 *ctx;
171 	int ret;
172 
173 	ctx = devm_drm_panel_alloc(dev, struct boe_td4320, panel,
174 				   &boe_td4320_panel_funcs,
175 				   DRM_MODE_CONNECTOR_DSI);
176 	if (IS_ERR(ctx))
177 		return PTR_ERR(ctx);
178 
179 	ret = devm_regulator_bulk_get_const(dev,
180 					    ARRAY_SIZE(boe_td4320_supplies),
181 					    boe_td4320_supplies,
182 					    &ctx->supplies);
183 	if (ret < 0)
184 		return ret;
185 
186 	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
187 	if (IS_ERR(ctx->reset_gpio))
188 		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
189 				     "Failed to get reset-gpios\n");
190 
191 	ctx->dsi = dsi;
192 	mipi_dsi_set_drvdata(dsi, ctx);
193 
194 	dsi->lanes = 4;
195 	dsi->format = MIPI_DSI_FMT_RGB888;
196 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
197 			  MIPI_DSI_CLOCK_NON_CONTINUOUS;
198 
199 	ctx->panel.prepare_prev_first = true;
200 
201 	ret = drm_panel_of_backlight(&ctx->panel);
202 	if (ret)
203 		return dev_err_probe(dev, ret, "Failed to get backlight\n");
204 
205 	drm_panel_add(&ctx->panel);
206 
207 	ret = mipi_dsi_attach(dsi);
208 	if (ret < 0) {
209 		drm_panel_remove(&ctx->panel);
210 		return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
211 	}
212 
213 	return 0;
214 }
215 
216 static void boe_td4320_remove(struct mipi_dsi_device *dsi)
217 {
218 	struct boe_td4320 *ctx = mipi_dsi_get_drvdata(dsi);
219 	int ret;
220 
221 	ret = mipi_dsi_detach(dsi);
222 	if (ret < 0)
223 		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
224 
225 	drm_panel_remove(&ctx->panel);
226 }
227 
228 static const struct of_device_id boe_td4320_of_match[] = {
229 	{ .compatible = "boe,td4320" },
230 	{ /* sentinel */ }
231 };
232 MODULE_DEVICE_TABLE(of, boe_td4320_of_match);
233 
234 static struct mipi_dsi_driver boe_td4320_driver = {
235 	.probe = boe_td4320_probe,
236 	.remove = boe_td4320_remove,
237 	.driver = {
238 		.name = "panel-boe-td4320",
239 		.of_match_table = boe_td4320_of_match,
240 	},
241 };
242 module_mipi_dsi_driver(boe_td4320_driver);
243 
244 MODULE_AUTHOR("Barnabas Czeman <barnabas.czeman@mainlining.org>");
245 MODULE_DESCRIPTION("DRM driver for boe td4320 fhdplus video mode dsi panel");
246 MODULE_LICENSE("GPL");
247