1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (c) 2021 Raffaele Tranquillini <raffaele.tranquillini@gmail.com>
4 *
5 * Generated using linux-mdss-dsi-panel-driver-generator from Lineage OS device tree:
6 * https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/arch/arm/boot/dts/qcom/a1-msm8996-mtp.dtsi
7 */
8
9#include <linux/delay.h>
10#include <linux/gpio/consumer.h>
11#include <linux/module.h>
12#include <linux/of.h>
13
14#include <video/mipi_display.h>
15
16#include <drm/drm_mipi_dsi.h>
17#include <drm/drm_modes.h>
18#include <drm/drm_panel.h>
19
20struct jdi_fhd_r63452 {
21 struct drm_panel panel;
22 struct mipi_dsi_device *dsi;
23 struct gpio_desc *reset_gpio;
24};
25
26static inline struct jdi_fhd_r63452 *to_jdi_fhd_r63452(struct drm_panel *panel)
27{
28 return container_of(panel, struct jdi_fhd_r63452, panel);
29}
30
31static void jdi_fhd_r63452_reset(struct jdi_fhd_r63452 *ctx)
32{
33 gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 0);
34 usleep_range(min: 10000, max: 11000);
35 gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 1);
36 usleep_range(min: 1000, max: 2000);
37 gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 0);
38 usleep_range(min: 10000, max: 11000);
39}
40
41static int jdi_fhd_r63452_on(struct jdi_fhd_r63452 *ctx)
42{
43 struct mipi_dsi_device *dsi = ctx->dsi;
44 struct device *dev = &dsi->dev;
45 int ret;
46
47 dsi->mode_flags |= MIPI_DSI_MODE_LPM;
48
49 mipi_dsi_generic_write_seq(dsi, 0xb0, 0x00);
50 mipi_dsi_generic_write_seq(dsi, 0xd6, 0x01);
51 mipi_dsi_generic_write_seq(dsi, 0xec,
52 0x64, 0xdc, 0xec, 0x3b, 0x52, 0x00, 0x0b, 0x0b,
53 0x13, 0x15, 0x68, 0x0b, 0xb5);
54 mipi_dsi_generic_write_seq(dsi, 0xb0, 0x03);
55
56 ret = mipi_dsi_dcs_set_tear_on(dsi, mode: MIPI_DSI_DCS_TEAR_MODE_VBLANK);
57 if (ret < 0) {
58 dev_err(dev, "Failed to set tear on: %d\n", ret);
59 return ret;
60 }
61
62 mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
63
64 ret = mipi_dsi_dcs_set_pixel_format(dsi, format: 0x77);
65 if (ret < 0) {
66 dev_err(dev, "Failed to set pixel format: %d\n", ret);
67 return ret;
68 }
69
70 ret = mipi_dsi_dcs_set_column_address(dsi, start: 0x0000, end: 0x0437);
71 if (ret < 0) {
72 dev_err(dev, "Failed to set column address: %d\n", ret);
73 return ret;
74 }
75
76 ret = mipi_dsi_dcs_set_page_address(dsi, start: 0x0000, end: 0x077f);
77 if (ret < 0) {
78 dev_err(dev, "Failed to set page address: %d\n", ret);
79 return ret;
80 }
81
82 ret = mipi_dsi_dcs_set_tear_scanline(dsi, scanline: 0x0000);
83 if (ret < 0) {
84 dev_err(dev, "Failed to set tear scanline: %d\n", ret);
85 return ret;
86 }
87
88 ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness: 0x00ff);
89 if (ret < 0) {
90 dev_err(dev, "Failed to set display brightness: %d\n", ret);
91 return ret;
92 }
93
94 mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24);
95 mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
96 mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS, 0x00);
97 mipi_dsi_dcs_write_seq(dsi, 0x84, 0x00);
98
99 ret = mipi_dsi_dcs_set_display_on(dsi);
100 if (ret < 0) {
101 dev_err(dev, "Failed to set display on: %d\n", ret);
102 return ret;
103 }
104 msleep(msecs: 20);
105
106 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
107 if (ret < 0) {
108 dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
109 return ret;
110 }
111 msleep(msecs: 80);
112
113 mipi_dsi_generic_write_seq(dsi, 0xb0, 0x04);
114 mipi_dsi_dcs_write_seq(dsi, 0x84, 0x00);
115 mipi_dsi_generic_write_seq(dsi, 0xc8, 0x11);
116 mipi_dsi_generic_write_seq(dsi, 0xb0, 0x03);
117
118 return 0;
119}
120
121static int jdi_fhd_r63452_off(struct jdi_fhd_r63452 *ctx)
122{
123 struct mipi_dsi_device *dsi = ctx->dsi;
124 struct device *dev = &dsi->dev;
125 int ret;
126
127 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
128
129 mipi_dsi_generic_write_seq(dsi, 0xb0, 0x00);
130 mipi_dsi_generic_write_seq(dsi, 0xd6, 0x01);
131 mipi_dsi_generic_write_seq(dsi, 0xec,
132 0x64, 0xdc, 0xec, 0x3b, 0x52, 0x00, 0x0b, 0x0b,
133 0x13, 0x15, 0x68, 0x0b, 0x95);
134 mipi_dsi_generic_write_seq(dsi, 0xb0, 0x03);
135
136 ret = mipi_dsi_dcs_set_display_off(dsi);
137 if (ret < 0) {
138 dev_err(dev, "Failed to set display off: %d\n", ret);
139 return ret;
140 }
141 usleep_range(min: 2000, max: 3000);
142
143 ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
144 if (ret < 0) {
145 dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
146 return ret;
147 }
148 msleep(msecs: 120);
149
150 return 0;
151}
152
153static int jdi_fhd_r63452_prepare(struct drm_panel *panel)
154{
155 struct jdi_fhd_r63452 *ctx = to_jdi_fhd_r63452(panel);
156 struct device *dev = &ctx->dsi->dev;
157 int ret;
158
159 jdi_fhd_r63452_reset(ctx);
160
161 ret = jdi_fhd_r63452_on(ctx);
162 if (ret < 0) {
163 dev_err(dev, "Failed to initialize panel: %d\n", ret);
164 gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 1);
165 return ret;
166 }
167
168 return 0;
169}
170
171static int jdi_fhd_r63452_unprepare(struct drm_panel *panel)
172{
173 struct jdi_fhd_r63452 *ctx = to_jdi_fhd_r63452(panel);
174 struct device *dev = &ctx->dsi->dev;
175 int ret;
176
177 ret = jdi_fhd_r63452_off(ctx);
178 if (ret < 0)
179 dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
180
181 gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 1);
182
183 return 0;
184}
185
186static const struct drm_display_mode jdi_fhd_r63452_mode = {
187 .clock = (1080 + 120 + 16 + 40) * (1920 + 4 + 2 + 4) * 60 / 1000,
188 .hdisplay = 1080,
189 .hsync_start = 1080 + 120,
190 .hsync_end = 1080 + 120 + 16,
191 .htotal = 1080 + 120 + 16 + 40,
192 .vdisplay = 1920,
193 .vsync_start = 1920 + 4,
194 .vsync_end = 1920 + 4 + 2,
195 .vtotal = 1920 + 4 + 2 + 4,
196 .width_mm = 64,
197 .height_mm = 114,
198};
199
200static int jdi_fhd_r63452_get_modes(struct drm_panel *panel,
201 struct drm_connector *connector)
202{
203 struct drm_display_mode *mode;
204
205 mode = drm_mode_duplicate(dev: connector->dev, mode: &jdi_fhd_r63452_mode);
206 if (!mode)
207 return -ENOMEM;
208
209 drm_mode_set_name(mode);
210
211 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
212 connector->display_info.width_mm = mode->width_mm;
213 connector->display_info.height_mm = mode->height_mm;
214 drm_mode_probed_add(connector, mode);
215
216 return 1;
217}
218
219static const struct drm_panel_funcs jdi_fhd_r63452_panel_funcs = {
220 .prepare = jdi_fhd_r63452_prepare,
221 .unprepare = jdi_fhd_r63452_unprepare,
222 .get_modes = jdi_fhd_r63452_get_modes,
223};
224
225static int jdi_fhd_r63452_probe(struct mipi_dsi_device *dsi)
226{
227 struct device *dev = &dsi->dev;
228 struct jdi_fhd_r63452 *ctx;
229 int ret;
230
231 ctx = devm_kzalloc(dev, size: sizeof(*ctx), GFP_KERNEL);
232 if (!ctx)
233 return -ENOMEM;
234
235 ctx->reset_gpio = devm_gpiod_get(dev, con_id: "reset", flags: GPIOD_OUT_HIGH);
236 if (IS_ERR(ptr: ctx->reset_gpio))
237 return dev_err_probe(dev, err: PTR_ERR(ptr: ctx->reset_gpio),
238 fmt: "Failed to get reset-gpios\n");
239
240 ctx->dsi = dsi;
241 mipi_dsi_set_drvdata(dsi, data: ctx);
242
243 dsi->lanes = 4;
244 dsi->format = MIPI_DSI_FMT_RGB888;
245 dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
246 MIPI_DSI_CLOCK_NON_CONTINUOUS;
247
248 drm_panel_init(panel: &ctx->panel, dev, funcs: &jdi_fhd_r63452_panel_funcs,
249 DRM_MODE_CONNECTOR_DSI);
250
251 ret = drm_panel_of_backlight(panel: &ctx->panel);
252 if (ret)
253 return dev_err_probe(dev, err: ret, fmt: "Failed to get backlight\n");
254
255 drm_panel_add(panel: &ctx->panel);
256
257 ret = mipi_dsi_attach(dsi);
258 if (ret < 0) {
259 dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
260 return ret;
261 }
262
263 return 0;
264}
265
266static void jdi_fhd_r63452_remove(struct mipi_dsi_device *dsi)
267{
268 struct jdi_fhd_r63452 *ctx = mipi_dsi_get_drvdata(dsi);
269 int ret;
270
271 ret = mipi_dsi_detach(dsi);
272 if (ret < 0)
273 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
274
275 drm_panel_remove(panel: &ctx->panel);
276}
277
278static const struct of_device_id jdi_fhd_r63452_of_match[] = {
279 { .compatible = "jdi,fhd-r63452" },
280 { /* sentinel */ }
281};
282MODULE_DEVICE_TABLE(of, jdi_fhd_r63452_of_match);
283
284static struct mipi_dsi_driver jdi_fhd_r63452_driver = {
285 .probe = jdi_fhd_r63452_probe,
286 .remove = jdi_fhd_r63452_remove,
287 .driver = {
288 .name = "panel-jdi-fhd-r63452",
289 .of_match_table = jdi_fhd_r63452_of_match,
290 },
291};
292module_mipi_dsi_driver(jdi_fhd_r63452_driver);
293
294MODULE_AUTHOR("Raffaele Tranquillini <raffaele.tranquillini@gmail.com>");
295MODULE_DESCRIPTION("DRM driver for JDI FHD R63452 DSI panel, command mode");
296MODULE_LICENSE("GPL v2");
297

source code of linux/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c