Skip to content

Commit 49837b6

Browse files
yedayaksuperna9999
authored andcommitted
drm: panel: Add Samsung S6E8FC0 DSI controller for M1906F9 panel
Add driver for Samsung S6E8FC0 DSI controller for M1906F9 video mode panel, found in Xiaomi Mi A3 mobile phone. Co-developed-by: Kamil Gołda <kamil.golda@protonmail.com> Signed-off-by: Kamil Gołda <kamil.golda@protonmail.com> Reviewed-by: David Heidelberg <david@ixit.cz> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> Signed-off-by: Yedaya Katsman <yedaya.ka@gmail.com> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org> Link: https://patch.msgid.link/20260320-panel-patches-v7-2-3eaefc4b3878@gmail.com
1 parent f4693b8 commit 49837b6

4 files changed

Lines changed: 320 additions & 0 deletions

File tree

MAINTAINERS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8302,6 +8302,11 @@ S: Maintained
83028302
F: Documentation/devicetree/bindings/display/panel/samsung,s6e3ha8.yaml
83038303
F: drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c
83048304

8305+
DRM DRIVER FOR SAMSUNG S6E8FC0 PANELS
8306+
M: Yedaya Katsman <yedaya.ka@gmail.com>
8307+
S: Maintained
8308+
F: drivers/gpu/drm/panel/panel-samsung-s6e8fc0-m1906f9.c
8309+
83058310
DRM DRIVER FOR SAMSUNG SOFEF00 DDIC
83068311
M: David Heidelberg <david@ixit.cz>
83078312
M: Casey Connolly <casey.connolly@linaro.org>

drivers/gpu/drm/panel/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,19 @@ config DRM_PANEL_SAMSUNG_S6E8AA5X01_AMS561RA01
966966
~5.6 inch AMOLED display, and the controller is driven by the MIPI
967967
DSI protocol with 4 lanes.
968968

969+
config DRM_PANEL_SAMSUNG_S6E8FC0
970+
tristate "Samsung S6E8FC0 DSI controller"
971+
depends on OF
972+
depends on BACKLIGHT_CLASS_DEVICE
973+
select DRM_MIPI_DSI
974+
help
975+
Say Y or M here if you want to enable support for the Samsung
976+
S6E8FC0 DSI controller and connected panel.
977+
Currently supported panels:
978+
979+
M1906F9 (M1906F9SH or M1906F9SI), 6.09 inch 720x1560, found
980+
in the Xiaomi Mi A3 smartphone (xiaomi-laurel).
981+
969982
config DRM_PANEL_SAMSUNG_SOFEF00
970983
tristate "Samsung SOFEF00 DSI panel controller"
971984
depends on OF

drivers/gpu/drm/panel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS427AP24) += panel-samsung-s6e88a0-ams4
9898
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams452ef01.o
9999
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
100100
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA5X01_AMS561RA01) += panel-samsung-s6e8aa5x01-ams561ra01.o
101+
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8FC0) += panel-samsung-s6e8fc0-m1906f9.o
101102
obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
102103
obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
103104
obj-$(CONFIG_DRM_PANEL_SHARP_LQ079L1SX01) += panel-sharp-lq079l1sx01.o
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
// Copyright (c) Kamil Gołda <kamil.golda@protonmail.com>
3+
// Copyright (c) Yedaya Katsman <yedaya.ka@gmail.com>
4+
// Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree:
5+
// Copyright (c) The Linux Foundation. All rights reserved.
6+
7+
#include <linux/backlight.h>
8+
#include <linux/delay.h>
9+
#include <linux/gpio/consumer.h>
10+
#include <linux/mod_devicetable.h>
11+
#include <linux/module.h>
12+
#include <linux/regulator/consumer.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+
#include <drm/drm_probe_helper.h>
20+
21+
struct s6e8fc0_ctx {
22+
struct drm_panel panel;
23+
struct mipi_dsi_device *dsi;
24+
struct regulator_bulk_data *supplies;
25+
struct gpio_desc *reset_gpio;
26+
};
27+
28+
static const struct regulator_bulk_data s6e8fc0_supplies[] = {
29+
{ .supply = "vdd" },
30+
{ .supply = "vci" },
31+
};
32+
33+
static inline
34+
struct s6e8fc0_ctx *to_s6e8fc0_ctx(struct drm_panel *panel)
35+
{
36+
return container_of_const(panel, struct s6e8fc0_ctx, panel);
37+
}
38+
39+
static void s6e8fc0_m1906f9_reset(struct s6e8fc0_ctx *ctx)
40+
{
41+
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
42+
usleep_range(12000, 13000);
43+
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
44+
usleep_range(2000, 3000);
45+
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
46+
usleep_range(10000, 11000);
47+
}
48+
49+
#define s6e8fc0_test_key_on_lvl2(ctx) \
50+
mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0x5a, 0x5a)
51+
#define s6e8fc0_test_key_off_lvl2(ctx) \
52+
mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0xa5, 0xa5)
53+
#define s6e8fc0_test_key_on_lvl3(ctx) \
54+
mipi_dsi_dcs_write_seq_multi(ctx, 0xfc, 0x5a, 0x5a)
55+
#define s6e8fc0_test_key_off_lvl3(ctx) \
56+
mipi_dsi_dcs_write_seq_multi(ctx, 0xfc, 0xa5, 0xa5)
57+
58+
static int s6e8fc0_m1906f9_on(struct s6e8fc0_ctx *ctx)
59+
{
60+
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
61+
62+
s6e8fc0_test_key_on_lvl3(&dsi_ctx);
63+
64+
mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x0000);
65+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY,
66+
0x20);
67+
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
68+
mipi_dsi_msleep(&dsi_ctx, 50);
69+
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
70+
71+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x04, 0xed);
72+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xed,
73+
0xe4, 0x08, 0x96, 0xa4, 0x2a, 0x72, 0xe2,
74+
0xca, 0x00);
75+
s6e8fc0_test_key_off_lvl3(&dsi_ctx);
76+
s6e8fc0_test_key_on_lvl2(&dsi_ctx);
77+
s6e8fc0_test_key_on_lvl3(&dsi_ctx);
78+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe1, 0x93);
79+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x05, 0xf4);
80+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf4, 0x03);
81+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xed, 0x01, 0x81, 0x04);
82+
s6e8fc0_test_key_off_lvl2(&dsi_ctx);
83+
s6e8fc0_test_key_off_lvl3(&dsi_ctx);
84+
85+
return dsi_ctx.accum_err;
86+
}
87+
88+
static int s6e8fc0_m1906f9_off(struct s6e8fc0_ctx *ctx)
89+
{
90+
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
91+
92+
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
93+
mipi_dsi_msleep(&dsi_ctx, 20);
94+
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
95+
mipi_dsi_msleep(&dsi_ctx, 120);
96+
97+
return dsi_ctx.accum_err;
98+
}
99+
100+
static int s6e8fc0_m1906f9_prepare(struct drm_panel *panel)
101+
{
102+
struct s6e8fc0_ctx *ctx = to_s6e8fc0_ctx(panel);
103+
struct device *dev = &ctx->dsi->dev;
104+
int ret;
105+
106+
ret = regulator_bulk_enable(ARRAY_SIZE(s6e8fc0_supplies), ctx->supplies);
107+
if (ret < 0) {
108+
dev_err(dev, "Failed to enable regulators: %d\n", ret);
109+
return ret;
110+
}
111+
112+
s6e8fc0_m1906f9_reset(ctx);
113+
114+
ret = s6e8fc0_m1906f9_on(ctx);
115+
if (ret < 0) {
116+
dev_err(dev, "Failed to initialize panel: %d\n", ret);
117+
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
118+
regulator_bulk_disable(ARRAY_SIZE(s6e8fc0_supplies), ctx->supplies);
119+
return ret;
120+
}
121+
122+
return 0;
123+
}
124+
125+
static int s6e8fc0_m1906f9_unprepare(struct drm_panel *panel)
126+
{
127+
struct s6e8fc0_ctx *ctx = to_s6e8fc0_ctx(panel);
128+
struct device *dev = &ctx->dsi->dev;
129+
int ret;
130+
131+
ret = s6e8fc0_m1906f9_off(ctx);
132+
if (ret < 0)
133+
dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
134+
135+
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
136+
regulator_bulk_disable(ARRAY_SIZE(s6e8fc0_supplies), ctx->supplies);
137+
138+
return 0;
139+
}
140+
141+
static const struct drm_display_mode s6e8fc0_m1906f9_samsungp_mode = {
142+
.clock = (720 + 350 + 40 + 294) * (1560 + 17 + 2 + 5) * 60 / 1000,
143+
.hdisplay = 720,
144+
.hsync_start = 720 + 350,
145+
.hsync_end = 720 + 350 + 40,
146+
.htotal = 720 + 350 + 40 + 294,
147+
.vdisplay = 1560,
148+
.vsync_start = 1560 + 17,
149+
.vsync_end = 1560 + 17 + 2,
150+
.vtotal = 1560 + 17 + 2 + 5,
151+
.width_mm = 65,
152+
.height_mm = 140,
153+
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
154+
};
155+
156+
static int s6e8fc0_m1906f9_get_modes(struct drm_panel *panel,
157+
struct drm_connector *connector)
158+
{
159+
return drm_connector_helper_get_modes_fixed(connector, &s6e8fc0_m1906f9_samsungp_mode);
160+
}
161+
162+
static const struct drm_panel_funcs s6e8fc0_m1906f9_panel_funcs = {
163+
.prepare = s6e8fc0_m1906f9_prepare,
164+
.unprepare = s6e8fc0_m1906f9_unprepare,
165+
.get_modes = s6e8fc0_m1906f9_get_modes,
166+
};
167+
168+
static int s6e8fc0_bl_update_status(struct backlight_device *bl)
169+
{
170+
struct mipi_dsi_device *dsi = bl_get_data(bl);
171+
u16 brightness = backlight_get_brightness(bl);
172+
int ret;
173+
174+
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
175+
176+
ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
177+
if (ret < 0)
178+
return ret;
179+
180+
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
181+
182+
return 0;
183+
}
184+
185+
static int s6e8fc0_bl_get_brightness(struct backlight_device *bl)
186+
{
187+
struct mipi_dsi_device *dsi = bl_get_data(bl);
188+
u16 brightness;
189+
int ret;
190+
191+
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
192+
193+
ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
194+
if (ret < 0)
195+
return ret;
196+
197+
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
198+
199+
return brightness;
200+
}
201+
202+
static const struct backlight_ops s6e8fc0_bl_ops = {
203+
.update_status = s6e8fc0_bl_update_status,
204+
.get_brightness = s6e8fc0_bl_get_brightness,
205+
};
206+
207+
static struct backlight_device *
208+
s6e8fc0_m1906f9_create_backlight(struct mipi_dsi_device *dsi)
209+
{
210+
struct device *dev = &dsi->dev;
211+
const struct backlight_properties props = {
212+
.type = BACKLIGHT_RAW,
213+
.brightness = 512,
214+
.max_brightness = 1023,
215+
};
216+
217+
return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
218+
&s6e8fc0_bl_ops, &props);
219+
}
220+
221+
static int s6e8fc0_m1906f9_probe(struct mipi_dsi_device *dsi)
222+
{
223+
struct device *dev = &dsi->dev;
224+
struct s6e8fc0_ctx *ctx;
225+
int ret;
226+
227+
ctx = devm_drm_panel_alloc(dev, struct s6e8fc0_ctx, panel,
228+
&s6e8fc0_m1906f9_panel_funcs,
229+
DRM_MODE_CONNECTOR_DSI);
230+
if (IS_ERR(ctx))
231+
return PTR_ERR(ctx);
232+
233+
ret = devm_regulator_bulk_get_const(dev,
234+
ARRAY_SIZE(s6e8fc0_supplies),
235+
s6e8fc0_supplies,
236+
&ctx->supplies);
237+
if (ret < 0)
238+
return ret;
239+
240+
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
241+
if (IS_ERR(ctx->reset_gpio))
242+
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
243+
"Failed to get reset-gpios\n");
244+
245+
ctx->dsi = dsi;
246+
mipi_dsi_set_drvdata(dsi, ctx);
247+
248+
dsi->lanes = 4;
249+
dsi->format = MIPI_DSI_FMT_RGB888;
250+
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
251+
MIPI_DSI_CLOCK_NON_CONTINUOUS;
252+
253+
ctx->panel.prepare_prev_first = true;
254+
255+
ctx->panel.backlight = s6e8fc0_m1906f9_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+
ret = mipi_dsi_attach(dsi);
263+
if (ret < 0) {
264+
drm_panel_remove(&ctx->panel);
265+
return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
266+
}
267+
268+
return 0;
269+
}
270+
271+
static void s6e8fc0_remove(struct mipi_dsi_device *dsi)
272+
{
273+
struct s6e8fc0_ctx *ctx = mipi_dsi_get_drvdata(dsi);
274+
int ret;
275+
276+
ret = mipi_dsi_detach(dsi);
277+
if (ret < 0)
278+
dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
279+
280+
drm_panel_remove(&ctx->panel);
281+
}
282+
283+
static const struct of_device_id samsung_s6e8fc0_of_match[] = {
284+
{ .compatible = "samsung,s6e8fc0-m1906f9" },
285+
{ /* sentinel */ }
286+
};
287+
MODULE_DEVICE_TABLE(of, samsung_s6e8fc0_of_match);
288+
289+
static struct mipi_dsi_driver s6e8fc0_driver = {
290+
.probe = s6e8fc0_m1906f9_probe,
291+
.remove = s6e8fc0_remove,
292+
.driver = {
293+
.name = "panel-samsung-s6e8fc0-m1906f9",
294+
.of_match_table = samsung_s6e8fc0_of_match,
295+
},
296+
};
297+
module_mipi_dsi_driver(s6e8fc0_driver);
298+
299+
MODULE_AUTHOR("Kamil Gołda <kamil.golda@protonmail.com>");
300+
MODULE_DESCRIPTION("DRM driver for Samsung s6e8fc0 DSI controller");
301+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)