Loading...
Note: File does not exist in v3.1.
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (c) 2023 Alexander Warnecke <awarnecke002@hotmail.com>
4 * Copyright (c) 2023 Manuel Traut <manut@mecka.net>
5 * Copyright (c) 2023 Dang Huynh <danct12@riseup.net>
6 */
7
8#include <linux/delay.h>
9#include <linux/gpio/consumer.h>
10#include <linux/module.h>
11#include <linux/of.h>
12#include <linux/of_device.h>
13#include <linux/regulator/consumer.h>
14
15#include <drm/drm_connector.h>
16#include <drm/drm_mipi_dsi.h>
17#include <drm/drm_modes.h>
18#include <drm/drm_panel.h>
19
20struct boe_th101mb31ig002 {
21 struct drm_panel panel;
22
23 struct mipi_dsi_device *dsi;
24
25 struct regulator *power;
26 struct gpio_desc *enable;
27 struct gpio_desc *reset;
28
29 enum drm_panel_orientation orientation;
30};
31
32static void boe_th101mb31ig002_reset(struct boe_th101mb31ig002 *ctx)
33{
34 gpiod_direction_output(ctx->reset, 0);
35 usleep_range(10, 100);
36 gpiod_direction_output(ctx->reset, 1);
37 usleep_range(10, 100);
38 gpiod_direction_output(ctx->reset, 0);
39 usleep_range(5000, 6000);
40}
41
42static int boe_th101mb31ig002_enable(struct drm_panel *panel)
43{
44 struct boe_th101mb31ig002 *ctx = container_of(panel,
45 struct boe_th101mb31ig002,
46 panel);
47 struct mipi_dsi_device *dsi = ctx->dsi;
48 struct device *dev = &dsi->dev;
49 int ret;
50
51 mipi_dsi_dcs_write_seq(dsi, 0xE0, 0xAB, 0xBA);
52 mipi_dsi_dcs_write_seq(dsi, 0xE1, 0xBA, 0xAB);
53 mipi_dsi_dcs_write_seq(dsi, 0xB1, 0x10, 0x01, 0x47, 0xFF);
54 mipi_dsi_dcs_write_seq(dsi, 0xB2, 0x0C, 0x14, 0x04, 0x50, 0x50, 0x14);
55 mipi_dsi_dcs_write_seq(dsi, 0xB3, 0x56, 0x53, 0x00);
56 mipi_dsi_dcs_write_seq(dsi, 0xB4, 0x33, 0x30, 0x04);
57 mipi_dsi_dcs_write_seq(dsi, 0xB6, 0xB0, 0x00, 0x00, 0x10, 0x00, 0x10,
58 0x00);
59 mipi_dsi_dcs_write_seq(dsi, 0xB8, 0x05, 0x12, 0x29, 0x49, 0x48, 0x00,
60 0x00);
61 mipi_dsi_dcs_write_seq(dsi, 0xB9, 0x7C, 0x65, 0x55, 0x49, 0x46, 0x36,
62 0x3B, 0x24, 0x3D, 0x3C, 0x3D, 0x5C, 0x4C,
63 0x55, 0x47, 0x46, 0x39, 0x26, 0x06, 0x7C,
64 0x65, 0x55, 0x49, 0x46, 0x36, 0x3B, 0x24,
65 0x3D, 0x3C, 0x3D, 0x5C, 0x4C, 0x55, 0x47,
66 0x46, 0x39, 0x26, 0x06);
67 mipi_dsi_dcs_write_seq(dsi, 0x00, 0xFF, 0x87, 0x12, 0x34, 0x44, 0x44,
68 0x44, 0x44, 0x98, 0x04, 0x98, 0x04, 0x0F,
69 0x00, 0x00, 0xC1);
70 mipi_dsi_dcs_write_seq(dsi, 0xC1, 0x54, 0x94, 0x02, 0x85, 0x9F, 0x00,
71 0x7F, 0x00, 0x54, 0x00);
72 mipi_dsi_dcs_write_seq(dsi, 0xC2, 0x17, 0x09, 0x08, 0x89, 0x08, 0x11,
73 0x22, 0x20, 0x44, 0xFF, 0x18, 0x00);
74 mipi_dsi_dcs_write_seq(dsi, 0xC3, 0x86, 0x46, 0x05, 0x05, 0x1C, 0x1C,
75 0x1D, 0x1D, 0x02, 0x1F, 0x1F, 0x1E, 0x1E,
76 0x0F, 0x0F, 0x0D, 0x0D, 0x13, 0x13, 0x11,
77 0x11, 0x00);
78 mipi_dsi_dcs_write_seq(dsi, 0xC4, 0x07, 0x07, 0x04, 0x04, 0x1C, 0x1C,
79 0x1D, 0x1D, 0x02, 0x1F, 0x1F, 0x1E, 0x1E,
80 0x0E, 0x0E, 0x0C, 0x0C, 0x12, 0x12, 0x10,
81 0x10, 0x00);
82 mipi_dsi_dcs_write_seq(dsi, 0xC6, 0x2A, 0x2A);
83 mipi_dsi_dcs_write_seq(dsi, 0xC8, 0x21, 0x00, 0x31, 0x42, 0x34, 0x16);
84 mipi_dsi_dcs_write_seq(dsi, 0xCA, 0xCB, 0x43);
85 mipi_dsi_dcs_write_seq(dsi, 0xCD, 0x0E, 0x4B, 0x4B, 0x20, 0x19, 0x6B,
86 0x06, 0xB3);
87 mipi_dsi_dcs_write_seq(dsi, 0xD2, 0xE3, 0x2B, 0x38, 0x00);
88 mipi_dsi_dcs_write_seq(dsi, 0xD4, 0x00, 0x01, 0x00, 0x0E, 0x04, 0x44,
89 0x08, 0x10, 0x00, 0x00, 0x00);
90 mipi_dsi_dcs_write_seq(dsi, 0xE6, 0x80, 0x01, 0xFF, 0xFF, 0xFF, 0xFF,
91 0xFF, 0xFF);
92 mipi_dsi_dcs_write_seq(dsi, 0xF0, 0x12, 0x03, 0x20, 0x00, 0xFF);
93 mipi_dsi_dcs_write_seq(dsi, 0xF3, 0x00);
94
95 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
96 if (ret < 0) {
97 dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
98 return ret;
99 }
100
101 msleep(120);
102
103 ret = mipi_dsi_dcs_set_display_on(dsi);
104 if (ret < 0) {
105 dev_err(dev, "Failed to set panel on: %d\n", ret);
106 return ret;
107 }
108
109 return 0;
110}
111
112static int boe_th101mb31ig002_disable(struct drm_panel *panel)
113{
114 struct boe_th101mb31ig002 *ctx = container_of(panel,
115 struct boe_th101mb31ig002,
116 panel);
117 struct mipi_dsi_device *dsi = ctx->dsi;
118 struct device *dev = &dsi->dev;
119 int ret;
120
121 ret = mipi_dsi_dcs_set_display_off(dsi);
122 if (ret < 0)
123 dev_err(dev, "Failed to set panel off: %d\n", ret);
124
125 msleep(120);
126
127 ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
128 if (ret < 0)
129 dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
130
131 return 0;
132}
133
134static int boe_th101mb31ig002_unprepare(struct drm_panel *panel)
135{
136 struct boe_th101mb31ig002 *ctx = container_of(panel,
137 struct boe_th101mb31ig002,
138 panel);
139
140 gpiod_set_value_cansleep(ctx->reset, 1);
141 gpiod_set_value_cansleep(ctx->enable, 0);
142 regulator_disable(ctx->power);
143
144 return 0;
145}
146
147static int boe_th101mb31ig002_prepare(struct drm_panel *panel)
148{
149 struct boe_th101mb31ig002 *ctx = container_of(panel,
150 struct boe_th101mb31ig002,
151 panel);
152 struct device *dev = &ctx->dsi->dev;
153 int ret;
154
155 ret = regulator_enable(ctx->power);
156 if (ret) {
157 dev_err(dev, "Failed to enable power supply: %d\n", ret);
158 return ret;
159 }
160
161 gpiod_set_value_cansleep(ctx->enable, 1);
162 msleep(50);
163 boe_th101mb31ig002_reset(ctx);
164 boe_th101mb31ig002_enable(panel);
165
166 return 0;
167}
168
169static const struct drm_display_mode boe_th101mb31ig002_default_mode = {
170 .clock = 73500,
171 .hdisplay = 800,
172 .hsync_start = 800 + 64,
173 .hsync_end = 800 + 64 + 16,
174 .htotal = 800 + 64 + 16 + 64,
175 .vdisplay = 1280,
176 .vsync_start = 1280 + 2,
177 .vsync_end = 1280 + 2 + 4,
178 .vtotal = 1280 + 2 + 4 + 12,
179 .width_mm = 135,
180 .height_mm = 216,
181 .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
182};
183
184static int boe_th101mb31ig002_get_modes(struct drm_panel *panel,
185 struct drm_connector *connector)
186{
187 struct boe_th101mb31ig002 *ctx = container_of(panel,
188 struct boe_th101mb31ig002,
189 panel);
190 struct drm_display_mode *mode;
191
192 mode = drm_mode_duplicate(connector->dev,
193 &boe_th101mb31ig002_default_mode);
194 if (!mode) {
195 dev_err(panel->dev, "Failed to add mode %ux%u@%u\n",
196 boe_th101mb31ig002_default_mode.hdisplay,
197 boe_th101mb31ig002_default_mode.vdisplay,
198 drm_mode_vrefresh(&boe_th101mb31ig002_default_mode));
199 return -ENOMEM;
200 }
201
202 drm_mode_set_name(mode);
203
204 connector->display_info.bpc = 8;
205 connector->display_info.width_mm = mode->width_mm;
206 connector->display_info.height_mm = mode->height_mm;
207
208 /*
209 * TODO: Remove once all drm drivers call
210 * drm_connector_set_orientation_from_panel()
211 */
212 drm_connector_set_panel_orientation(connector, ctx->orientation);
213
214 drm_mode_probed_add(connector, mode);
215
216 return 1;
217}
218
219static enum drm_panel_orientation
220boe_th101mb31ig002_get_orientation(struct drm_panel *panel)
221{
222 struct boe_th101mb31ig002 *ctx = container_of(panel,
223 struct boe_th101mb31ig002,
224 panel);
225
226 return ctx->orientation;
227}
228
229static const struct drm_panel_funcs boe_th101mb31ig002_funcs = {
230 .prepare = boe_th101mb31ig002_prepare,
231 .unprepare = boe_th101mb31ig002_unprepare,
232 .disable = boe_th101mb31ig002_disable,
233 .get_modes = boe_th101mb31ig002_get_modes,
234 .get_orientation = boe_th101mb31ig002_get_orientation,
235};
236
237static int boe_th101mb31ig002_dsi_probe(struct mipi_dsi_device *dsi)
238{
239 struct boe_th101mb31ig002 *ctx;
240 int ret;
241
242 ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
243 if (!ctx)
244 return -ENOMEM;
245
246 mipi_dsi_set_drvdata(dsi, ctx);
247 ctx->dsi = dsi;
248
249 dsi->lanes = 4;
250 dsi->format = MIPI_DSI_FMT_RGB888;
251 dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
252 MIPI_DSI_MODE_NO_EOT_PACKET |
253 MIPI_DSI_MODE_LPM;
254
255 ctx->power = devm_regulator_get(&dsi->dev, "power");
256 if (IS_ERR(ctx->power))
257 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->power),
258 "Failed to get power regulator\n");
259
260 ctx->enable = devm_gpiod_get(&dsi->dev, "enable", GPIOD_OUT_LOW);
261 if (IS_ERR(ctx->enable))
262 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->enable),
263 "Failed to get enable GPIO\n");
264
265 ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_HIGH);
266 if (IS_ERR(ctx->reset))
267 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset),
268 "Failed to get reset GPIO\n");
269
270 ret = of_drm_get_panel_orientation(dsi->dev.of_node,
271 &ctx->orientation);
272 if (ret)
273 return dev_err_probe(&dsi->dev, ret,
274 "Failed to get orientation\n");
275
276 drm_panel_init(&ctx->panel, &dsi->dev, &boe_th101mb31ig002_funcs,
277 DRM_MODE_CONNECTOR_DSI);
278
279 ret = drm_panel_of_backlight(&ctx->panel);
280 if (ret)
281 return ret;
282
283 drm_panel_add(&ctx->panel);
284
285 ret = mipi_dsi_attach(dsi);
286 if (ret < 0) {
287 dev_err_probe(&dsi->dev, ret,
288 "Failed to attach panel to DSI host\n");
289 drm_panel_remove(&ctx->panel);
290 return ret;
291 }
292
293 return 0;
294}
295
296static void boe_th101mb31ig002_dsi_remove(struct mipi_dsi_device *dsi)
297{
298 struct boe_th101mb31ig002 *ctx = mipi_dsi_get_drvdata(dsi);
299
300 mipi_dsi_detach(dsi);
301 drm_panel_remove(&ctx->panel);
302}
303
304static const struct of_device_id boe_th101mb31ig002_of_match[] = {
305 { .compatible = "boe,th101mb31ig002-28a", },
306 { /* sentinel */ }
307};
308MODULE_DEVICE_TABLE(of, boe_th101mb31ig002_of_match);
309
310static struct mipi_dsi_driver boe_th101mb31ig002_driver = {
311 .driver = {
312 .name = "boe-th101mb31ig002-28a",
313 .of_match_table = boe_th101mb31ig002_of_match,
314 },
315 .probe = boe_th101mb31ig002_dsi_probe,
316 .remove = boe_th101mb31ig002_dsi_remove,
317};
318module_mipi_dsi_driver(boe_th101mb31ig002_driver);
319
320MODULE_AUTHOR("Alexander Warnecke <awarnecke002@hotmail.com>");
321MODULE_DESCRIPTION("BOE TH101MB31IG002-28A MIPI-DSI LCD panel");
322MODULE_LICENSE("GPL");