]> Git Repo - J-linux.git/blob - drivers/gpu/drm/panel/panel-raydium-rm69380.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / gpu / drm / panel / panel-raydium-rm69380.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree.
4  * Copyright (c) 2024 David Wronek <[email protected]>
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/of_device.h>
13 #include <linux/of_graph.h>
14 #include <linux/regulator/consumer.h>
15
16 #include <video/mipi_display.h>
17
18 #include <drm/drm_mipi_dsi.h>
19 #include <drm/drm_modes.h>
20 #include <drm/drm_panel.h>
21 #include <drm/drm_probe_helper.h>
22
23 struct rm69380_panel {
24         struct drm_panel panel;
25         struct mipi_dsi_device *dsi[2];
26         struct regulator_bulk_data supplies[2];
27         struct gpio_desc *reset_gpio;
28 };
29
30 static inline
31 struct rm69380_panel *to_rm69380_panel(struct drm_panel *panel)
32 {
33         return container_of(panel, struct rm69380_panel, panel);
34 }
35
36 static void rm69380_reset(struct rm69380_panel *ctx)
37 {
38         gpiod_set_value_cansleep(ctx->reset_gpio, 0);
39         usleep_range(15000, 16000);
40         gpiod_set_value_cansleep(ctx->reset_gpio, 1);
41         usleep_range(10000, 11000);
42         gpiod_set_value_cansleep(ctx->reset_gpio, 0);
43         msleep(30);
44 }
45
46 static int rm69380_on(struct rm69380_panel *ctx)
47 {
48         struct mipi_dsi_device *dsi = ctx->dsi[0];
49         struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
50
51         dsi->mode_flags |= MIPI_DSI_MODE_LPM;
52         if (ctx->dsi[1])
53                 ctx->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;
54
55         mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xd4);
56         mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x80);
57         mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xd0);
58         mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x00);
59         mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x26);
60         mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x3f);
61         mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1d, 0x1a);
62         mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x00);
63         mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x28);
64         mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc2, 0x08);
65
66         mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
67         mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
68         mipi_dsi_msleep(&dsi_ctx, 20);
69
70         mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
71         mipi_dsi_msleep(&dsi_ctx, 36);
72
73         return dsi_ctx.accum_err;
74 }
75
76 static void rm69380_off(struct rm69380_panel *ctx)
77 {
78         struct mipi_dsi_device *dsi = ctx->dsi[0];
79         struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
80
81         dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
82         if (ctx->dsi[1])
83                 ctx->dsi[1]->mode_flags &= ~MIPI_DSI_MODE_LPM;
84
85         mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
86         mipi_dsi_msleep(&dsi_ctx, 35);
87         mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
88         mipi_dsi_msleep(&dsi_ctx, 20);
89 }
90
91 static int rm69380_prepare(struct drm_panel *panel)
92 {
93         struct rm69380_panel *ctx = to_rm69380_panel(panel);
94         int ret;
95
96         ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
97         if (ret < 0)
98                 return ret;
99
100         rm69380_reset(ctx);
101
102         ret = rm69380_on(ctx);
103         if (ret < 0) {
104                 gpiod_set_value_cansleep(ctx->reset_gpio, 1);
105                 regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
106         }
107
108         return ret;
109 }
110
111 static int rm69380_unprepare(struct drm_panel *panel)
112 {
113         struct rm69380_panel *ctx = to_rm69380_panel(panel);
114
115         rm69380_off(ctx);
116
117         gpiod_set_value_cansleep(ctx->reset_gpio, 1);
118         regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
119
120         return 0;
121 }
122
123 static const struct drm_display_mode rm69380_mode = {
124         .clock = (2560 + 32 + 12 + 38) * (1600 + 20 + 4 + 8) * 90 / 1000,
125         .hdisplay = 2560,
126         .hsync_start = 2560 + 32,
127         .hsync_end = 2560 + 32 + 12,
128         .htotal = 2560 + 32 + 12 + 38,
129         .vdisplay = 1600,
130         .vsync_start = 1600 + 20,
131         .vsync_end = 1600 + 20 + 4,
132         .vtotal = 1600 + 20 + 4 + 8,
133         .width_mm = 248,
134         .height_mm = 155,
135         .type = DRM_MODE_TYPE_DRIVER,
136 };
137
138 static int rm69380_get_modes(struct drm_panel *panel,
139                                         struct drm_connector *connector)
140 {
141         return drm_connector_helper_get_modes_fixed(connector, &rm69380_mode);
142 }
143
144 static const struct drm_panel_funcs rm69380_panel_funcs = {
145         .prepare = rm69380_prepare,
146         .unprepare = rm69380_unprepare,
147         .get_modes = rm69380_get_modes,
148 };
149
150 static int rm69380_bl_update_status(struct backlight_device *bl)
151 {
152         struct mipi_dsi_device *dsi = bl_get_data(bl);
153         u16 brightness = backlight_get_brightness(bl);
154         int ret;
155
156         dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
157
158         ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
159         if (ret < 0)
160                 return ret;
161
162         dsi->mode_flags |= MIPI_DSI_MODE_LPM;
163
164         return 0;
165 }
166
167 static int rm69380_bl_get_brightness(struct backlight_device *bl)
168 {
169         struct mipi_dsi_device *dsi = bl_get_data(bl);
170         u16 brightness;
171         int ret;
172
173         dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
174
175         ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
176         if (ret < 0)
177                 return ret;
178
179         dsi->mode_flags |= MIPI_DSI_MODE_LPM;
180
181         return brightness;
182 }
183
184 static const struct backlight_ops rm69380_bl_ops = {
185         .update_status = rm69380_bl_update_status,
186         .get_brightness = rm69380_bl_get_brightness,
187 };
188
189 static struct backlight_device *
190 rm69380_create_backlight(struct mipi_dsi_device *dsi)
191 {
192         struct device *dev = &dsi->dev;
193         const struct backlight_properties props = {
194                 .type = BACKLIGHT_RAW,
195                 .brightness = 511,
196                 .max_brightness = 2047,
197         };
198
199         return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
200                                               &rm69380_bl_ops, &props);
201 }
202
203 static int rm69380_probe(struct mipi_dsi_device *dsi)
204 {
205         struct mipi_dsi_host *dsi_sec_host;
206         struct rm69380_panel *ctx;
207         struct device *dev = &dsi->dev;
208         struct device_node *dsi_sec;
209         int ret, i;
210
211         ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
212         if (!ctx)
213                 return -ENOMEM;
214
215         ctx->supplies[0].supply = "vddio";
216         ctx->supplies[1].supply = "avdd";
217         ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
218                                       ctx->supplies);
219         if (ret < 0)
220                 return dev_err_probe(dev, ret, "Failed to get regulators\n");
221
222         ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
223         if (IS_ERR(ctx->reset_gpio))
224                 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
225                                      "Failed to get reset-gpios\n");
226
227         dsi_sec = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
228
229         if (dsi_sec) {
230                 const struct mipi_dsi_device_info info = { "RM69380 DSI1", 0,
231                                                            dsi_sec };
232
233                 dsi_sec_host = of_find_mipi_dsi_host_by_node(dsi_sec);
234                 of_node_put(dsi_sec);
235                 if (!dsi_sec_host)
236                         return dev_err_probe(dev, -EPROBE_DEFER,
237                                              "Cannot get secondary DSI host\n");
238
239                 ctx->dsi[1] =
240                         devm_mipi_dsi_device_register_full(dev, dsi_sec_host, &info);
241                 if (IS_ERR(ctx->dsi[1]))
242                         return dev_err_probe(dev, PTR_ERR(ctx->dsi[1]),
243                                              "Cannot get secondary DSI node\n");
244
245                 mipi_dsi_set_drvdata(ctx->dsi[1], ctx);
246         }
247
248         ctx->dsi[0] = dsi;
249         mipi_dsi_set_drvdata(dsi, ctx);
250
251         drm_panel_init(&ctx->panel, dev, &rm69380_panel_funcs,
252                        DRM_MODE_CONNECTOR_DSI);
253         ctx->panel.prepare_prev_first = true;
254
255         ctx->panel.backlight = rm69380_create_backlight(dsi);
256         if (IS_ERR(ctx->panel.backlight))
257                 return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
258                                      "Failed to create backlight\n");
259
260         drm_panel_add(&ctx->panel);
261
262         for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {
263                 if (!ctx->dsi[i])
264                         continue;
265
266                 dev_dbg(&ctx->dsi[i]->dev, "Binding DSI %d\n", i);
267
268                 ctx->dsi[i]->lanes = 4;
269                 ctx->dsi[i]->format = MIPI_DSI_FMT_RGB888;
270                 ctx->dsi[i]->mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
271                                           MIPI_DSI_CLOCK_NON_CONTINUOUS;
272
273                 ret = devm_mipi_dsi_attach(dev, ctx->dsi[i]);
274                 if (ret < 0) {
275                         drm_panel_remove(&ctx->panel);
276                         return dev_err_probe(dev, ret,
277                                              "Failed to attach to DSI%d\n", i);
278                 }
279         }
280
281         return 0;
282 }
283
284 static void rm69380_remove(struct mipi_dsi_device *dsi)
285 {
286         struct rm69380_panel *ctx = mipi_dsi_get_drvdata(dsi);
287
288         drm_panel_remove(&ctx->panel);
289 }
290
291 static const struct of_device_id rm69380_of_match[] = {
292         { .compatible = "lenovo,j716f-edo-rm69380" },
293         { /* sentinel */ }
294 };
295 MODULE_DEVICE_TABLE(of, rm69380_of_match);
296
297 static struct mipi_dsi_driver rm69380_panel_driver = {
298         .probe = rm69380_probe,
299         .remove = rm69380_remove,
300         .driver = {
301                 .name = "panel-raydium-rm69380",
302                 .of_match_table = rm69380_of_match,
303         },
304 };
305 module_mipi_dsi_driver(rm69380_panel_driver);
306
307 MODULE_AUTHOR("David Wronek <[email protected]");
308 MODULE_DESCRIPTION("DRM driver for Raydium RM69380-equipped DSI panels");
309 MODULE_LICENSE("GPL");
This page took 0.045198 seconds and 4 git commands to generate.