1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2020 Caleb Connolly <caleb@connolly.tech>
3 * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree:
4 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
7 #include <linux/delay.h>
8 #include <linux/gpio/consumer.h>
9 #include <linux/module.h>
11 #include <linux/of_device.h>
12 #include <linux/regulator/consumer.h>
13 #include <linux/swab.h>
14 #include <linux/backlight.h>
16 #include <video/mipi_display.h>
18 #include <drm/drm_mipi_dsi.h>
19 #include <drm/drm_modes.h>
20 #include <drm/drm_panel.h>
22 struct sofef00_panel
{
23 struct drm_panel panel
;
24 struct mipi_dsi_device
*dsi
;
25 struct regulator
*supply
;
26 struct gpio_desc
*reset_gpio
;
27 const struct drm_display_mode
*mode
;
32 struct sofef00_panel
*to_sofef00_panel(struct drm_panel
*panel
)
34 return container_of(panel
, struct sofef00_panel
, panel
);
37 #define dsi_dcs_write_seq(dsi, seq...) do { \
38 static const u8 d[] = { seq }; \
40 ret = mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)); \
45 static void sofef00_panel_reset(struct sofef00_panel
*ctx
)
47 gpiod_set_value_cansleep(ctx
->reset_gpio
, 0);
48 usleep_range(5000, 6000);
49 gpiod_set_value_cansleep(ctx
->reset_gpio
, 1);
50 usleep_range(2000, 3000);
51 gpiod_set_value_cansleep(ctx
->reset_gpio
, 0);
52 usleep_range(12000, 13000);
55 static int sofef00_panel_on(struct sofef00_panel
*ctx
)
57 struct mipi_dsi_device
*dsi
= ctx
->dsi
;
58 struct device
*dev
= &dsi
->dev
;
61 dsi
->mode_flags
|= MIPI_DSI_MODE_LPM
;
63 ret
= mipi_dsi_dcs_exit_sleep_mode(dsi
);
65 dev_err(dev
, "Failed to exit sleep mode: %d\n", ret
);
68 usleep_range(10000, 11000);
70 dsi_dcs_write_seq(dsi
, 0xf0, 0x5a, 0x5a);
72 ret
= mipi_dsi_dcs_set_tear_on(dsi
, MIPI_DSI_DCS_TEAR_MODE_VBLANK
);
74 dev_err(dev
, "Failed to set tear on: %d\n", ret
);
78 dsi_dcs_write_seq(dsi
, 0xf0, 0xa5, 0xa5);
79 dsi_dcs_write_seq(dsi
, 0xf0, 0x5a, 0x5a);
80 dsi_dcs_write_seq(dsi
, 0xb0, 0x07);
81 dsi_dcs_write_seq(dsi
, 0xb6, 0x12);
82 dsi_dcs_write_seq(dsi
, 0xf0, 0xa5, 0xa5);
83 dsi_dcs_write_seq(dsi
, MIPI_DCS_WRITE_CONTROL_DISPLAY
, 0x20);
84 dsi_dcs_write_seq(dsi
, MIPI_DCS_WRITE_POWER_SAVE
, 0x00);
86 ret
= mipi_dsi_dcs_set_display_on(dsi
);
88 dev_err(dev
, "Failed to set display on: %d\n", ret
);
95 static int sofef00_panel_off(struct sofef00_panel
*ctx
)
97 struct mipi_dsi_device
*dsi
= ctx
->dsi
;
98 struct device
*dev
= &dsi
->dev
;
101 dsi
->mode_flags
&= ~MIPI_DSI_MODE_LPM
;
103 ret
= mipi_dsi_dcs_set_display_off(dsi
);
105 dev_err(dev
, "Failed to set display off: %d\n", ret
);
110 ret
= mipi_dsi_dcs_enter_sleep_mode(dsi
);
112 dev_err(dev
, "Failed to enter sleep mode: %d\n", ret
);
120 static int sofef00_panel_prepare(struct drm_panel
*panel
)
122 struct sofef00_panel
*ctx
= to_sofef00_panel(panel
);
123 struct device
*dev
= &ctx
->dsi
->dev
;
129 ret
= regulator_enable(ctx
->supply
);
131 dev_err(dev
, "Failed to enable regulator: %d\n", ret
);
135 sofef00_panel_reset(ctx
);
137 ret
= sofef00_panel_on(ctx
);
139 dev_err(dev
, "Failed to initialize panel: %d\n", ret
);
140 gpiod_set_value_cansleep(ctx
->reset_gpio
, 1);
144 ctx
->prepared
= true;
148 static int sofef00_panel_unprepare(struct drm_panel
*panel
)
150 struct sofef00_panel
*ctx
= to_sofef00_panel(panel
);
151 struct device
*dev
= &ctx
->dsi
->dev
;
157 ret
= sofef00_panel_off(ctx
);
159 dev_err(dev
, "Failed to un-initialize panel: %d\n", ret
);
161 regulator_disable(ctx
->supply
);
163 ctx
->prepared
= false;
167 static const struct drm_display_mode enchilada_panel_mode
= {
168 .clock
= (1080 + 112 + 16 + 36) * (2280 + 36 + 8 + 12) * 60 / 1000,
170 .hsync_start
= 1080 + 112,
171 .hsync_end
= 1080 + 112 + 16,
172 .htotal
= 1080 + 112 + 16 + 36,
174 .vsync_start
= 2280 + 36,
175 .vsync_end
= 2280 + 36 + 8,
176 .vtotal
= 2280 + 36 + 8 + 12,
181 static const struct drm_display_mode fajita_panel_mode
= {
182 .clock
= (1080 + 72 + 16 + 36) * (2340 + 32 + 4 + 18) * 60 / 1000,
184 .hsync_start
= 1080 + 72,
185 .hsync_end
= 1080 + 72 + 16,
186 .htotal
= 1080 + 72 + 16 + 36,
188 .vsync_start
= 2340 + 32,
189 .vsync_end
= 2340 + 32 + 4,
190 .vtotal
= 2340 + 32 + 4 + 18,
195 static int sofef00_panel_get_modes(struct drm_panel
*panel
, struct drm_connector
*connector
)
197 struct drm_display_mode
*mode
;
198 struct sofef00_panel
*ctx
= to_sofef00_panel(panel
);
200 mode
= drm_mode_duplicate(connector
->dev
, ctx
->mode
);
204 drm_mode_set_name(mode
);
206 mode
->type
= DRM_MODE_TYPE_DRIVER
| DRM_MODE_TYPE_PREFERRED
;
207 connector
->display_info
.width_mm
= mode
->width_mm
;
208 connector
->display_info
.height_mm
= mode
->height_mm
;
209 drm_mode_probed_add(connector
, mode
);
214 static const struct drm_panel_funcs sofef00_panel_panel_funcs
= {
215 .prepare
= sofef00_panel_prepare
,
216 .unprepare
= sofef00_panel_unprepare
,
217 .get_modes
= sofef00_panel_get_modes
,
220 static int sofef00_panel_bl_update_status(struct backlight_device
*bl
)
222 struct mipi_dsi_device
*dsi
= bl_get_data(bl
);
226 brightness
= (u16
)backlight_get_brightness(bl
);
227 // This panel needs the high and low bytes swapped for the brightness value
228 brightness
= __swab16(brightness
);
230 err
= mipi_dsi_dcs_set_display_brightness(dsi
, brightness
);
237 static const struct backlight_ops sofef00_panel_bl_ops
= {
238 .update_status
= sofef00_panel_bl_update_status
,
241 static struct backlight_device
*
242 sofef00_create_backlight(struct mipi_dsi_device
*dsi
)
244 struct device
*dev
= &dsi
->dev
;
245 const struct backlight_properties props
= {
246 .type
= BACKLIGHT_PLATFORM
,
248 .max_brightness
= 1023,
251 return devm_backlight_device_register(dev
, dev_name(dev
), dev
, dsi
,
252 &sofef00_panel_bl_ops
, &props
);
255 static int sofef00_panel_probe(struct mipi_dsi_device
*dsi
)
257 struct device
*dev
= &dsi
->dev
;
258 struct sofef00_panel
*ctx
;
261 ctx
= devm_kzalloc(dev
, sizeof(*ctx
), GFP_KERNEL
);
265 ctx
->mode
= of_device_get_match_data(dev
);
268 dev_err(dev
, "Missing device mode\n");
272 ctx
->supply
= devm_regulator_get(dev
, "vddio");
273 if (IS_ERR(ctx
->supply
)) {
274 ret
= PTR_ERR(ctx
->supply
);
275 dev_err(dev
, "Failed to get vddio regulator: %d\n", ret
);
279 ctx
->reset_gpio
= devm_gpiod_get(dev
, "reset", GPIOD_OUT_HIGH
);
280 if (IS_ERR(ctx
->reset_gpio
)) {
281 ret
= PTR_ERR(ctx
->reset_gpio
);
282 dev_warn(dev
, "Failed to get reset-gpios: %d\n", ret
);
287 mipi_dsi_set_drvdata(dsi
, ctx
);
290 dsi
->format
= MIPI_DSI_FMT_RGB888
;
292 drm_panel_init(&ctx
->panel
, dev
, &sofef00_panel_panel_funcs
,
293 DRM_MODE_CONNECTOR_DSI
);
295 ctx
->panel
.backlight
= sofef00_create_backlight(dsi
);
296 if (IS_ERR(ctx
->panel
.backlight
))
297 return dev_err_probe(dev
, PTR_ERR(ctx
->panel
.backlight
),
298 "Failed to create backlight\n");
300 drm_panel_add(&ctx
->panel
);
302 ret
= mipi_dsi_attach(dsi
);
304 dev_err(dev
, "Failed to attach to DSI host: %d\n", ret
);
311 static int sofef00_panel_remove(struct mipi_dsi_device
*dsi
)
313 struct sofef00_panel
*ctx
= mipi_dsi_get_drvdata(dsi
);
316 ret
= mipi_dsi_detach(dsi
);
318 dev_err(&dsi
->dev
, "Failed to detach from DSI host: %d\n", ret
);
320 drm_panel_remove(&ctx
->panel
);
325 static const struct of_device_id sofef00_panel_of_match
[] = {
326 { // OnePlus 6 / enchilada
327 .compatible
= "samsung,sofef00",
328 .data
= &enchilada_panel_mode
,
330 { // OnePlus 6T / fajita
331 .compatible
= "samsung,s6e3fc2x01",
332 .data
= &fajita_panel_mode
,
336 MODULE_DEVICE_TABLE(of
, sofef00_panel_of_match
);
338 static struct mipi_dsi_driver sofef00_panel_driver
= {
339 .probe
= sofef00_panel_probe
,
340 .remove
= sofef00_panel_remove
,
342 .name
= "panel-oneplus6",
343 .of_match_table
= sofef00_panel_of_match
,
347 module_mipi_dsi_driver(sofef00_panel_driver
);
349 MODULE_AUTHOR("Caleb Connolly <caleb@connolly.tech>");
350 MODULE_DESCRIPTION("DRM driver for Samsung AMOLED DSI panels found in OnePlus 6/6T phones");
351 MODULE_LICENSE("GPL v2");