Skip to content

Commit b330f3a

Browse files
JIaxygasuperna9999
authored andcommitted
drm/panel: Add Samsung AMS581VF01 panel driver
Add the driver for Samsung AMS581VF01 SOFEF01-based 5.81" FHD Plus CMD mode OLED panel support found in Google Pixel 4a (sm7150-google-sunfish) Signed-off-by: Danila Tikhonov <[email protected]> Reviewed-by: Dmitry Baryshkov <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Neil Armstrong <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent dca22e9 commit b330f3a

File tree

3 files changed

+293
-0
lines changed

3 files changed

+293
-0
lines changed

drivers/gpu/drm/panel/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,15 @@ config DRM_PANEL_RONBO_RB070D30
614614
Say Y here if you want to enable support for Ronbo Electronics
615615
RB070D30 1024x600 DSI panel.
616616

617+
config DRM_PANEL_SAMSUNG_AMS581VF01
618+
tristate "Samsung AMS581VF01 panel"
619+
depends on OF
620+
depends on DRM_MIPI_DSI
621+
depends on BACKLIGHT_CLASS_DEVICE
622+
help
623+
Say Y or M here if you want to enable support for the
624+
Samsung AMS581VF01 FHD Plus (2340x1080@60Hz) CMD mode panel.
625+
617626
config DRM_PANEL_SAMSUNG_AMS639RQ08
618627
tristate "Samsung AMS639RQ08 panel"
619628
depends on OF

drivers/gpu/drm/panel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o
6262
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM692E5) += panel-raydium-rm692e5.o
6363
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM69380) += panel-raydium-rm69380.o
6464
obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o
65+
obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMS581VF01) += panel-samsung-ams581vf01.o
6566
obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMS639RQ08) += panel-samsung-ams639rq08.o
6667
obj-$(CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20) += panel-samsung-atna33xc20.o
6768
obj-$(CONFIG_DRM_PANEL_SAMSUNG_DB7430) += panel-samsung-db7430.o
Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (c) 2024, Danila Tikhonov <[email protected]>
4+
*/
5+
6+
#include <linux/backlight.h>
7+
#include <linux/delay.h>
8+
#include <linux/gpio/consumer.h>
9+
#include <linux/module.h>
10+
#include <linux/of.h>
11+
#include <linux/regulator/consumer.h>
12+
13+
#include <video/mipi_display.h>
14+
15+
#include <drm/drm_mipi_dsi.h>
16+
#include <drm/drm_modes.h>
17+
#include <drm/drm_panel.h>
18+
#include <drm/drm_probe_helper.h>
19+
20+
/* Manufacturer Command Set */
21+
#define MCS_ACCESS_PROT_OFF 0xb0
22+
#define MCS_PASSWD 0xf0
23+
24+
struct ams581vf01 {
25+
struct drm_panel panel;
26+
struct mipi_dsi_device *dsi;
27+
struct gpio_desc *reset_gpio;
28+
struct regulator_bulk_data *supplies;
29+
};
30+
31+
static const struct regulator_bulk_data ams581vf01_supplies[] = {
32+
{ .supply = "vdd3p3" },
33+
{ .supply = "vddio" },
34+
{ .supply = "vsn" },
35+
{ .supply = "vsp" },
36+
};
37+
38+
static inline struct ams581vf01 *to_ams581vf01(struct drm_panel *panel)
39+
{
40+
return container_of(panel, struct ams581vf01, panel);
41+
}
42+
43+
static void ams581vf01_reset(struct ams581vf01 *ctx)
44+
{
45+
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
46+
usleep_range(10000, 11000);
47+
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
48+
usleep_range(10000, 11000);
49+
}
50+
51+
static int ams581vf01_on(struct ams581vf01 *ctx)
52+
{
53+
struct mipi_dsi_device *dsi = ctx->dsi;
54+
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
55+
56+
/* Sleep Out, Wait 10ms */
57+
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
58+
usleep_range(10000, 11000);
59+
60+
/* TE On */
61+
mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
62+
63+
/* MIC Setting */
64+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD, 0x5a, 0x5a); /* Unlock */
65+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xeb, 0x17,
66+
0x41, 0x92,
67+
0x0e, 0x10,
68+
0x82, 0x5a);
69+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD, 0xa5, 0xa5); /* Lock */
70+
71+
/* Column & Page Address Setting */
72+
mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0x0000, 0x0437);
73+
mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0x0000, 0x0923);
74+
75+
/* Brightness Setting */
76+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
77+
78+
/* Horizontal & Vertical sync Setting */
79+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD, 0x5a, 0x5a); /* Unlock */
80+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_ACCESS_PROT_OFF, 0x09);
81+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe8, 0x11, 0x30);
82+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD, 0xa5, 0xa5); /* Lock */
83+
mipi_dsi_msleep(&dsi_ctx, 110);
84+
85+
/* Display On */
86+
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
87+
88+
return dsi_ctx.accum_err;
89+
}
90+
91+
static void ams581vf01_off(struct ams581vf01 *ctx)
92+
{
93+
struct mipi_dsi_device *dsi = ctx->dsi;
94+
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
95+
96+
/* Display Off & Sleep In */
97+
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
98+
mipi_dsi_msleep(&dsi_ctx, 20);
99+
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
100+
101+
/* VCI operating mode change */
102+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD, 0x5a, 0x5a); /* Unlock */
103+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_ACCESS_PROT_OFF, 0x05);
104+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf4, 0x01);
105+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD, 0xa5, 0xa5); /* Lock */
106+
107+
mipi_dsi_msleep(&dsi_ctx, 120);
108+
}
109+
110+
static int ams581vf01_prepare(struct drm_panel *panel)
111+
{
112+
struct ams581vf01 *ctx = to_ams581vf01(panel);
113+
int ret;
114+
115+
ret = regulator_bulk_enable(ARRAY_SIZE(ams581vf01_supplies),
116+
ctx->supplies);
117+
if (ret < 0)
118+
return ret;
119+
120+
ams581vf01_reset(ctx);
121+
122+
ret = ams581vf01_on(ctx);
123+
if (ret < 0) {
124+
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
125+
regulator_bulk_disable(ARRAY_SIZE(ams581vf01_supplies),
126+
ctx->supplies);
127+
return ret;
128+
}
129+
130+
return 0;
131+
}
132+
133+
static int ams581vf01_unprepare(struct drm_panel *panel)
134+
{
135+
struct ams581vf01 *ctx = to_ams581vf01(panel);
136+
137+
ams581vf01_off(ctx);
138+
139+
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
140+
regulator_bulk_disable(ARRAY_SIZE(ams581vf01_supplies),
141+
ctx->supplies);
142+
143+
return 0;
144+
}
145+
146+
static const struct drm_display_mode ams581vf01_mode = {
147+
.clock = (1080 + 32 + 73 + 98) * (2340 + 8 + 1 + 8) * 60 / 1000,
148+
.hdisplay = 1080,
149+
.hsync_start = 1080 + 32,
150+
.hsync_end = 1080 + 32 + 73,
151+
.htotal = 1080 + 32 + 73 + 98,
152+
.vdisplay = 2340,
153+
.vsync_start = 2340 + 8,
154+
.vsync_end = 2340 + 8 + 1,
155+
.vtotal = 2340 + 8 + 1 + 8,
156+
.width_mm = 62,
157+
.height_mm = 134,
158+
.type = DRM_MODE_TYPE_DRIVER,
159+
};
160+
161+
static int ams581vf01_get_modes(struct drm_panel *panel,
162+
struct drm_connector *connector)
163+
{
164+
return drm_connector_helper_get_modes_fixed(connector, &ams581vf01_mode);
165+
}
166+
167+
static const struct drm_panel_funcs ams581vf01_panel_funcs = {
168+
.prepare = ams581vf01_prepare,
169+
.unprepare = ams581vf01_unprepare,
170+
.get_modes = ams581vf01_get_modes,
171+
};
172+
173+
static int ams581vf01_bl_update_status(struct backlight_device *bl)
174+
{
175+
struct mipi_dsi_device *dsi = bl_get_data(bl);
176+
u16 brightness = backlight_get_brightness(bl);
177+
int ret;
178+
179+
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
180+
181+
ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
182+
if (ret < 0)
183+
return ret;
184+
185+
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
186+
187+
return 0;
188+
}
189+
190+
static const struct backlight_ops ams581vf01_bl_ops = {
191+
.update_status = ams581vf01_bl_update_status,
192+
};
193+
194+
static struct backlight_device *
195+
ams581vf01_create_backlight(struct mipi_dsi_device *dsi)
196+
{
197+
struct device *dev = &dsi->dev;
198+
const struct backlight_properties props = {
199+
.type = BACKLIGHT_RAW,
200+
.brightness = 511,
201+
.max_brightness = 1023,
202+
};
203+
204+
return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
205+
&ams581vf01_bl_ops, &props);
206+
}
207+
208+
static int ams581vf01_probe(struct mipi_dsi_device *dsi)
209+
{
210+
struct device *dev = &dsi->dev;
211+
struct ams581vf01 *ctx;
212+
int ret;
213+
214+
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
215+
if (!ctx)
216+
return -ENOMEM;
217+
218+
ret = devm_regulator_bulk_get_const(&dsi->dev,
219+
ARRAY_SIZE(ams581vf01_supplies),
220+
ams581vf01_supplies,
221+
&ctx->supplies);
222+
if (ret < 0)
223+
return dev_err_probe(dev, ret, "Failed to get regulators\n");
224+
225+
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
226+
if (IS_ERR(ctx->reset_gpio))
227+
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
228+
"Failed to get reset-gpios\n");
229+
230+
ctx->dsi = dsi;
231+
mipi_dsi_set_drvdata(dsi, ctx);
232+
233+
dsi->lanes = 4;
234+
dsi->format = MIPI_DSI_FMT_RGB888;
235+
dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
236+
MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
237+
238+
drm_panel_init(&ctx->panel, dev, &ams581vf01_panel_funcs,
239+
DRM_MODE_CONNECTOR_DSI);
240+
ctx->panel.prepare_prev_first = true;
241+
242+
ctx->panel.backlight = ams581vf01_create_backlight(dsi);
243+
if (IS_ERR(ctx->panel.backlight))
244+
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
245+
"Failed to create backlight\n");
246+
247+
drm_panel_add(&ctx->panel);
248+
249+
ret = devm_mipi_dsi_attach(dev, dsi);
250+
if (ret < 0) {
251+
drm_panel_remove(&ctx->panel);
252+
return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
253+
}
254+
255+
return 0;
256+
}
257+
258+
static void ams581vf01_remove(struct mipi_dsi_device *dsi)
259+
{
260+
struct ams581vf01 *ctx = mipi_dsi_get_drvdata(dsi);
261+
262+
drm_panel_remove(&ctx->panel);
263+
}
264+
265+
static const struct of_device_id ams581vf01_of_match[] = {
266+
{ .compatible = "samsung,ams581vf01" },
267+
{ /* sentinel */ }
268+
};
269+
MODULE_DEVICE_TABLE(of, ams581vf01_of_match);
270+
271+
static struct mipi_dsi_driver ams581vf01_driver = {
272+
.probe = ams581vf01_probe,
273+
.remove = ams581vf01_remove,
274+
.driver = {
275+
.name = "panel-samsung-ams581vf01",
276+
.of_match_table = ams581vf01_of_match,
277+
},
278+
};
279+
module_mipi_dsi_driver(ams581vf01_driver);
280+
281+
MODULE_AUTHOR("Danila Tikhonov <[email protected]>");
282+
MODULE_DESCRIPTION("DRM driver for SAMSUNG AMS581VF01 cmd mode dsi panel");
283+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)