Skip to content

Commit d5658db

Browse files
Jakko3superna9999
authored andcommitted
drm/panel: samsung-s6e88a0-ams427ap24: Add initial driver
This initial part of the panel driver was mostly generated by the "linux-mdss-dsi-panel-driver-generator" tool [1], reading downstream Android kernel file "dsi_panel_S6E88A0_AMS427AP24_qhd_octa_video.dtsi" [2]. On top of the generic output of the tool, there were a couple of changes applied: - Added mipi_dsi_dcs_set_display_on() to function s6e88a0_ams427ap24_on(), otherwise the display does not show up. - In functions s6e88a0_ams427ap24_on() and s6e88a0_ams427ap24_off() changed DSI commands to multi context and used "accum_err" returns. - In functions s6e88a0_ams427ap24_on() and s6e88a0_ams427ap24_off() replaced msleep() by mipi_dsi_msleep(). - The function s6e88a0_ams427ap24_get_modes() was changed to make use of drm_connector_helper_get_modes_fixed(). This also required to include drm/drm_probe_helper.h. - In function s6e88a0_ams427ap24_probe() registring the regulators was changed to devm_regulator_bulk_get_const(). This required to change supplies in struct s6e88a0_ams427ap24 to a pointer. - Removed bool "prepared" from struct s6e88a0_ams427ap24 and according parts in functions s6e88a0_ams427ap24_prepare() and s6e88a0_ams427ap24_unprepare(). - Removed include <linux/of.h>, it's not needed. - Added comments to the mipi_dsi_dcs_write_seq_multi() lines in function s6e88a0_ams427ap24_on(). [1] https://github.com/msm8916-mainline/linux-mdss-dsi-panel-driver-generator [2] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/video/msm/mdss/samsung/S6E88A0_AMS427AP24/dsi_panel_S6E88A0_AMS427AP24_qhd_octa_video.dtsi Signed-off-by: Jakob Hauser <[email protected]> Reviewed-by: Linus Walleij <[email protected]> Link: https://lore.kernel.org/r/d92ef0036b66520bb6d1ec908165e776cf30c303.1730070570.git.jahau@rocketmail.com Signed-off-by: Neil Armstrong <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/d92ef0036b66520bb6d1ec908165e776cf30c303.1730070570.git.jahau@rocketmail.com
1 parent 4998d53 commit d5658db

File tree

3 files changed

+261
-0
lines changed

3 files changed

+261
-0
lines changed

drivers/gpu/drm/panel/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,15 @@ config DRM_PANEL_SAMSUNG_AMS639RQ08
632632
Say Y or M here if you want to enable support for the
633633
Samsung AMS639RQ08 FHD Plus (2340x1080@60Hz) CMD mode panel.
634634

635+
config DRM_PANEL_SAMSUNG_S6E88A0_AMS427AP24
636+
tristate "Samsung AMS427AP24 panel with S6E88A0 controller"
637+
depends on GPIOLIB && OF && REGULATOR
638+
depends on DRM_MIPI_DSI
639+
help
640+
Say Y here if you want to enable support for Samsung AMS427AP24 panel
641+
with S6E88A0 controller (found in Samsung Galaxy S4 Mini Value Edition
642+
GT-I9195I). To compile this driver as a module, choose M here.
643+
635644
config DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01
636645
tristate "Samsung AMS452EF01 panel with S6E88A0 DSI video mode controller"
637646
depends on OF

drivers/gpu/drm/panel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o
7777
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0) += panel-samsung-s6e63m0.o
7878
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_SPI) += panel-samsung-s6e63m0-spi.o
7979
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_DSI) += panel-samsung-s6e63m0-dsi.o
80+
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS427AP24) += panel-samsung-s6e88a0-ams427ap24.o
8081
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams452ef01.o
8182
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
8283
obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Samsung AMS427AP24 panel with S6E88A0 controller
4+
* Copyright (c) 2024 Jakob Hauser <[email protected]>
5+
*/
6+
7+
#include <linux/delay.h>
8+
#include <linux/gpio/consumer.h>
9+
#include <linux/module.h>
10+
#include <linux/regulator/consumer.h>
11+
12+
#include <video/mipi_display.h>
13+
14+
#include <drm/drm_mipi_dsi.h>
15+
#include <drm/drm_modes.h>
16+
#include <drm/drm_panel.h>
17+
#include <drm/drm_probe_helper.h>
18+
19+
struct s6e88a0_ams427ap24 {
20+
struct drm_panel panel;
21+
struct mipi_dsi_device *dsi;
22+
struct regulator_bulk_data *supplies;
23+
struct gpio_desc *reset_gpio;
24+
};
25+
26+
static const struct regulator_bulk_data s6e88a0_ams427ap24_supplies[] = {
27+
{ .supply = "vdd3" },
28+
{ .supply = "vci" },
29+
};
30+
31+
static inline
32+
struct s6e88a0_ams427ap24 *to_s6e88a0_ams427ap24(struct drm_panel *panel)
33+
{
34+
return container_of(panel, struct s6e88a0_ams427ap24, panel);
35+
}
36+
37+
static void s6e88a0_ams427ap24_reset(struct s6e88a0_ams427ap24 *ctx)
38+
{
39+
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
40+
usleep_range(5000, 6000);
41+
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
42+
usleep_range(1000, 2000);
43+
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
44+
usleep_range(18000, 19000);
45+
}
46+
47+
static int s6e88a0_ams427ap24_on(struct s6e88a0_ams427ap24 *ctx)
48+
{
49+
struct mipi_dsi_device *dsi = ctx->dsi;
50+
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
51+
52+
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
53+
54+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a); // level 1 key on
55+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfc, 0x5a, 0x5a); // level 2 key on
56+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x11); // src latch set global 1
57+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfd, 0x11); // src latch set 1
58+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x13); // src latch set global 2
59+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfd, 0x18); // src latch set 2
60+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x02); // avdd set 1
61+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb8, 0x30); // avdd set 2
62+
63+
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
64+
mipi_dsi_msleep(&dsi_ctx, 20);
65+
66+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf1, 0x5a, 0x5a); // level 3 key on
67+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xcc, 0x4c); // pixel clock divider pol.
68+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf2, 0x03, 0x0d); // unknown
69+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf1, 0xa5, 0xa5); // level 3 key off
70+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xca,
71+
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x80,
72+
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
73+
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
74+
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
75+
0x80, 0x80, 0x00, 0x00, 0x00); // set gamma
76+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2,
77+
0x40, 0x08, 0x20, 0x00, 0x08); // set aid
78+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb6, 0x28, 0x0b); // set elvss
79+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf7, 0x03); // gamma update
80+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x00); // acl off
81+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5); // level 1 key off
82+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfc, 0xa5, 0xa5); // level 2 key off
83+
84+
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
85+
86+
return dsi_ctx.accum_err;
87+
}
88+
89+
static int s6e88a0_ams427ap24_off(struct s6e88a0_ams427ap24 *ctx)
90+
{
91+
struct mipi_dsi_device *dsi = ctx->dsi;
92+
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
93+
94+
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
95+
96+
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
97+
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
98+
mipi_dsi_msleep(&dsi_ctx, 120);
99+
100+
return dsi_ctx.accum_err;
101+
}
102+
103+
static int s6e88a0_ams427ap24_prepare(struct drm_panel *panel)
104+
{
105+
struct s6e88a0_ams427ap24 *ctx = to_s6e88a0_ams427ap24(panel);
106+
struct device *dev = &ctx->dsi->dev;
107+
int ret;
108+
109+
ret = regulator_bulk_enable(ARRAY_SIZE(s6e88a0_ams427ap24_supplies),
110+
ctx->supplies);
111+
if (ret < 0) {
112+
dev_err(dev, "Failed to enable regulators: %d\n", ret);
113+
return ret;
114+
}
115+
116+
s6e88a0_ams427ap24_reset(ctx);
117+
118+
ret = s6e88a0_ams427ap24_on(ctx);
119+
if (ret < 0) {
120+
dev_err(dev, "Failed to initialize panel: %d\n", ret);
121+
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
122+
regulator_bulk_disable(ARRAY_SIZE(s6e88a0_ams427ap24_supplies),
123+
ctx->supplies);
124+
return ret;
125+
}
126+
127+
return 0;
128+
}
129+
130+
static int s6e88a0_ams427ap24_unprepare(struct drm_panel *panel)
131+
{
132+
struct s6e88a0_ams427ap24 *ctx = to_s6e88a0_ams427ap24(panel);
133+
struct device *dev = &ctx->dsi->dev;
134+
int ret;
135+
136+
ret = s6e88a0_ams427ap24_off(ctx);
137+
if (ret < 0)
138+
dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
139+
140+
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
141+
regulator_bulk_disable(ARRAY_SIZE(s6e88a0_ams427ap24_supplies),
142+
ctx->supplies);
143+
144+
return 0;
145+
}
146+
147+
static const struct drm_display_mode s6e88a0_ams427ap24_mode = {
148+
.clock = (540 + 94 + 4 + 18) * (960 + 12 + 1 + 3) * 60 / 1000,
149+
.hdisplay = 540,
150+
.hsync_start = 540 + 94,
151+
.hsync_end = 540 + 94 + 4,
152+
.htotal = 540 + 94 + 4 + 18,
153+
.vdisplay = 960,
154+
.vsync_start = 960 + 12,
155+
.vsync_end = 960 + 12 + 1,
156+
.vtotal = 960 + 12 + 1 + 3,
157+
.width_mm = 55,
158+
.height_mm = 95,
159+
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
160+
};
161+
162+
static int s6e88a0_ams427ap24_get_modes(struct drm_panel *panel,
163+
struct drm_connector *connector)
164+
{
165+
return drm_connector_helper_get_modes_fixed(connector,
166+
&s6e88a0_ams427ap24_mode);
167+
}
168+
169+
static const struct drm_panel_funcs s6e88a0_ams427ap24_panel_funcs = {
170+
.prepare = s6e88a0_ams427ap24_prepare,
171+
.unprepare = s6e88a0_ams427ap24_unprepare,
172+
.get_modes = s6e88a0_ams427ap24_get_modes,
173+
};
174+
175+
static int s6e88a0_ams427ap24_probe(struct mipi_dsi_device *dsi)
176+
{
177+
struct device *dev = &dsi->dev;
178+
struct s6e88a0_ams427ap24 *ctx;
179+
int ret;
180+
181+
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
182+
if (!ctx)
183+
return -ENOMEM;
184+
185+
ret = devm_regulator_bulk_get_const(dev,
186+
ARRAY_SIZE(s6e88a0_ams427ap24_supplies),
187+
s6e88a0_ams427ap24_supplies,
188+
&ctx->supplies);
189+
if (ret < 0)
190+
return ret;
191+
192+
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
193+
if (IS_ERR(ctx->reset_gpio))
194+
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
195+
"Failed to get reset-gpios\n");
196+
197+
ctx->dsi = dsi;
198+
mipi_dsi_set_drvdata(dsi, ctx);
199+
200+
dsi->lanes = 2;
201+
dsi->format = MIPI_DSI_FMT_RGB888;
202+
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
203+
MIPI_DSI_MODE_NO_EOT_PACKET;
204+
205+
drm_panel_init(&ctx->panel, dev, &s6e88a0_ams427ap24_panel_funcs,
206+
DRM_MODE_CONNECTOR_DSI);
207+
ctx->panel.prepare_prev_first = true;
208+
209+
drm_panel_add(&ctx->panel);
210+
211+
ret = mipi_dsi_attach(dsi);
212+
if (ret < 0) {
213+
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
214+
drm_panel_remove(&ctx->panel);
215+
return ret;
216+
}
217+
218+
return 0;
219+
}
220+
221+
static void s6e88a0_ams427ap24_remove(struct mipi_dsi_device *dsi)
222+
{
223+
struct s6e88a0_ams427ap24 *ctx = mipi_dsi_get_drvdata(dsi);
224+
int ret;
225+
226+
ret = mipi_dsi_detach(dsi);
227+
if (ret < 0)
228+
dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
229+
230+
drm_panel_remove(&ctx->panel);
231+
}
232+
233+
static const struct of_device_id s6e88a0_ams427ap24_of_match[] = {
234+
{ .compatible = "samsung,s6e88a0-ams427ap24" },
235+
{ /* sentinel */ },
236+
};
237+
MODULE_DEVICE_TABLE(of, s6e88a0_ams427ap24_of_match);
238+
239+
static struct mipi_dsi_driver s6e88a0_ams427ap24_driver = {
240+
.probe = s6e88a0_ams427ap24_probe,
241+
.remove = s6e88a0_ams427ap24_remove,
242+
.driver = {
243+
.name = "panel-s6e88a0-ams427ap24",
244+
.of_match_table = s6e88a0_ams427ap24_of_match,
245+
},
246+
};
247+
module_mipi_dsi_driver(s6e88a0_ams427ap24_driver);
248+
249+
MODULE_AUTHOR("Jakob Hauser <[email protected]>");
250+
MODULE_DESCRIPTION("Samsung AMS427AP24 panel with S6E88A0 controller");
251+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)