Skip to content

Commit 118ba47

Browse files
committed
drm: panel: Add driver for Visionox G1639FP106
Signed-off-by: Jens Reidel <adrian@mainlining.org>
1 parent 9b7a282 commit 118ba47

3 files changed

Lines changed: 299 additions & 0 deletions

File tree

drivers/gpu/drm/panel/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,15 @@ config DRM_PANEL_VISIONOX_G2647FB105
11261126
Say Y here if you want to enable support for the Visionox
11271127
G2647FB105 (2340x1080@60Hz) AMOLED DSI cmd mode panel.
11281128

1129+
config DRM_PANEL_VISIONOX_G1639FP106
1130+
tristate "Visionox G1639FP106"
1131+
depends on OF
1132+
depends on DRM_MIPI_DSI
1133+
depends on BACKLIGHT_CLASS_DEVICE
1134+
help
1135+
Say Y here if you want to enable support for the Visionox
1136+
G1639FP106 (2340x1080@60Hz) AMOLED DSI cmd mode panel.
1137+
11291138
config DRM_PANEL_VISIONOX_R66451
11301139
tristate "Visionox R66451"
11311140
depends on OF

drivers/gpu/drm/panel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
113113
obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o
114114
obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o
115115
obj-$(CONFIG_DRM_PANEL_VISIONOX_G2647FB105) += panel-visionox-g2647fb105.o
116+
obj-$(CONFIG_DRM_PANEL_VISIONOX_G1639FP106) += panel-visionox-g1639fp106.o
116117
obj-$(CONFIG_DRM_PANEL_VISIONOX_RM69299) += panel-visionox-rm69299.o
117118
obj-$(CONFIG_DRM_PANEL_VISIONOX_RM692E5) += panel-visionox-rm692e5.o
118119
obj-$(CONFIG_DRM_PANEL_VISIONOX_VTDR6130) += panel-visionox-vtdr6130.o
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
// Copyright (c) 2025 Jens Reidel <adrian@mainlining.org>
3+
// Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree:
4+
// Copyright (c) 2013, The Linux Foundation. All rights reserved.
5+
6+
#include <linux/backlight.h>
7+
#include <linux/delay.h>
8+
#include <linux/gpio/consumer.h>
9+
#include <linux/mod_devicetable.h>
10+
#include <linux/module.h>
11+
#include <linux/regulator/consumer.h>
12+
13+
#include <drm/drm_mipi_dsi.h>
14+
#include <drm/drm_modes.h>
15+
#include <drm/drm_panel.h>
16+
#include <drm/drm_probe_helper.h>
17+
18+
struct g1639fp106 {
19+
struct drm_panel panel;
20+
struct mipi_dsi_device *dsi;
21+
struct gpio_desc *reset_gpio;
22+
struct regulator_bulk_data *supplies;
23+
};
24+
25+
static const struct regulator_bulk_data g1639fp106_supplies[] = {
26+
{ .supply = "vdd3p3" },
27+
{ .supply = "vddio" },
28+
{ .supply = "vsn" },
29+
{ .supply = "vsp" },
30+
};
31+
32+
static inline struct g1639fp106 *to_g1639fp106(struct drm_panel *panel)
33+
{
34+
return container_of(panel, struct g1639fp106, panel);
35+
}
36+
37+
static void g1639fp106_reset(struct g1639fp106 *ctx)
38+
{
39+
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
40+
usleep_range(1000, 2000);
41+
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
42+
usleep_range(10000, 11000);
43+
}
44+
45+
static int g1639fp106_on(struct g1639fp106 *ctx)
46+
{
47+
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
48+
49+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x00);
50+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbb,
51+
0x59, 0xff, 0xff, 0xff, 0xef, 0xcf, 0xab,
52+
0x87, 0x63, 0x3f, 0x4a, 0x48, 0x46, 0x44,
53+
0x42, 0x40, 0x3e, 0x3c, 0x3a, 0x64, 0x00,
54+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
55+
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
56+
0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x42,
57+
0x00);
58+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd4, 0x00, 0x8d);
59+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfa, 0x0f);
60+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x80);
61+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe6, 0x00);
62+
mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
63+
mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0x0000, 0x0437);
64+
mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0x0000, 0x0923);
65+
mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x0001);
66+
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
67+
mipi_dsi_msleep(&dsi_ctx, 110);
68+
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
69+
mipi_dsi_usleep_range(&dsi_ctx, 16000, 17000);
70+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x00);
71+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xcf, 0xc3, 0x00);
72+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xcf, 0xc4, 0x00);
73+
74+
return dsi_ctx.accum_err;
75+
}
76+
77+
static int g1639fp106_off(struct g1639fp106 *ctx)
78+
{
79+
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
80+
81+
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
82+
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
83+
mipi_dsi_msleep(&dsi_ctx, 120);
84+
85+
return dsi_ctx.accum_err;
86+
}
87+
88+
static int g1639fp106_prepare(struct drm_panel *panel)
89+
{
90+
struct g1639fp106 *ctx = to_g1639fp106(panel);
91+
struct device *dev = &ctx->dsi->dev;
92+
int ret;
93+
94+
ret = regulator_bulk_enable(ARRAY_SIZE(g1639fp106_supplies), ctx->supplies);
95+
if (ret < 0) {
96+
dev_err(dev, "Failed to enable regulators: %d\n", ret);
97+
return ret;
98+
}
99+
100+
g1639fp106_reset(ctx);
101+
102+
ret = g1639fp106_on(ctx);
103+
if (ret < 0) {
104+
dev_err(dev, "Failed to initialize panel: %d\n", ret);
105+
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
106+
regulator_bulk_disable(ARRAY_SIZE(g1639fp106_supplies), ctx->supplies);
107+
return ret;
108+
}
109+
110+
return 0;
111+
}
112+
113+
static int g1639fp106_unprepare(struct drm_panel *panel)
114+
{
115+
struct g1639fp106 *ctx = to_g1639fp106(panel);
116+
struct device *dev = &ctx->dsi->dev;
117+
int ret;
118+
119+
ret = g1639fp106_off(ctx);
120+
if (ret < 0)
121+
dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
122+
123+
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
124+
regulator_bulk_disable(ARRAY_SIZE(g1639fp106_supplies), ctx->supplies);
125+
126+
return 0;
127+
}
128+
129+
static const struct drm_display_mode g1639fp106_mode = {
130+
.clock = (1080 + 64 + 20 + 64) * (2340 + 64 + 20 + 64) * 60 / 1000,
131+
.hdisplay = 1080,
132+
.hsync_start = 1080 + 64,
133+
.hsync_end = 1080 + 64 + 20,
134+
.htotal = 1080 + 64 + 20 + 64,
135+
.vdisplay = 2340,
136+
.vsync_start = 2340 + 64,
137+
.vsync_end = 2340 + 64 + 20,
138+
.vtotal = 2340 + 64 + 20 + 64,
139+
.width_mm = 68,
140+
.height_mm = 147,
141+
.type = DRM_MODE_TYPE_DRIVER,
142+
};
143+
144+
static int g1639fp106_get_modes(struct drm_panel *panel,
145+
struct drm_connector *connector)
146+
{
147+
return drm_connector_helper_get_modes_fixed(connector, &g1639fp106_mode);
148+
}
149+
150+
static const struct drm_panel_funcs g1639fp106_panel_funcs = {
151+
.prepare = g1639fp106_prepare,
152+
.unprepare = g1639fp106_unprepare,
153+
.get_modes = g1639fp106_get_modes,
154+
};
155+
156+
static int g1639fp106_bl_update_status(struct backlight_device *bl)
157+
{
158+
struct mipi_dsi_device *dsi = bl_get_data(bl);
159+
u16 brightness = backlight_get_brightness(bl);
160+
int ret;
161+
162+
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
163+
164+
ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
165+
if (ret < 0)
166+
return ret;
167+
168+
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
169+
170+
return 0;
171+
}
172+
173+
static int g1639fp106_bl_get_brightness(struct backlight_device *bl)
174+
{
175+
struct mipi_dsi_device *dsi = bl_get_data(bl);
176+
u16 brightness;
177+
int ret;
178+
179+
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
180+
181+
ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
182+
if (ret < 0)
183+
return ret;
184+
185+
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
186+
187+
return brightness;
188+
}
189+
190+
static const struct backlight_ops g1639fp106_bl_ops = {
191+
.update_status = g1639fp106_bl_update_status,
192+
.get_brightness = g1639fp106_bl_get_brightness,
193+
};
194+
195+
static struct backlight_device *
196+
g1639fp106_create_backlight(struct mipi_dsi_device *dsi)
197+
{
198+
struct device *dev = &dsi->dev;
199+
const struct backlight_properties props = {
200+
.type = BACKLIGHT_RAW,
201+
.brightness = 2047,
202+
.max_brightness = 4095,
203+
};
204+
205+
return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
206+
&g1639fp106_bl_ops, &props);
207+
}
208+
209+
static int g1639fp106_probe(struct mipi_dsi_device *dsi)
210+
{
211+
struct device *dev = &dsi->dev;
212+
struct g1639fp106 *ctx;
213+
int ret;
214+
215+
ctx = devm_drm_panel_alloc(dev, struct g1639fp106, panel,
216+
&g1639fp106_panel_funcs,
217+
DRM_MODE_CONNECTOR_DSI);
218+
if (IS_ERR(ctx))
219+
return PTR_ERR(ctx);
220+
221+
ret = devm_regulator_bulk_get_const(dev,
222+
ARRAY_SIZE(g1639fp106_supplies),
223+
g1639fp106_supplies,
224+
&ctx->supplies);
225+
if (ret < 0)
226+
return ret;
227+
228+
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
229+
if (IS_ERR(ctx->reset_gpio))
230+
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
231+
"Failed to get reset-gpios\n");
232+
233+
ctx->dsi = dsi;
234+
mipi_dsi_set_drvdata(dsi, ctx);
235+
236+
dsi->lanes = 4;
237+
dsi->format = MIPI_DSI_FMT_RGB888;
238+
dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
239+
MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
240+
241+
ctx->panel.prepare_prev_first = true;
242+
243+
ctx->panel.backlight = g1639fp106_create_backlight(dsi);
244+
if (IS_ERR(ctx->panel.backlight))
245+
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
246+
"Failed to create backlight\n");
247+
248+
drm_panel_add(&ctx->panel);
249+
250+
ret = mipi_dsi_attach(dsi);
251+
if (ret < 0) {
252+
drm_panel_remove(&ctx->panel);
253+
return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
254+
}
255+
256+
return 0;
257+
}
258+
259+
static void g1639fp106_remove(struct mipi_dsi_device *dsi)
260+
{
261+
struct g1639fp106 *ctx = mipi_dsi_get_drvdata(dsi);
262+
int ret;
263+
264+
ret = mipi_dsi_detach(dsi);
265+
if (ret < 0)
266+
dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
267+
268+
drm_panel_remove(&ctx->panel);
269+
}
270+
271+
static const struct of_device_id g1639fp106_of_match[] = {
272+
{ .compatible = "visionox,g1639fp106" },
273+
{ /* sentinel */ }
274+
};
275+
MODULE_DEVICE_TABLE(of, g1639fp106_of_match);
276+
277+
static struct mipi_dsi_driver g1639fp106_driver = {
278+
.probe = g1639fp106_probe,
279+
.remove = g1639fp106_remove,
280+
.driver = {
281+
.name = "panel-visionox-g1639fp106",
282+
.of_match_table = g1639fp106_of_match,
283+
},
284+
};
285+
module_mipi_dsi_driver(g1639fp106_driver);
286+
287+
MODULE_AUTHOR("Jens Reidel <adrian@mainlining.org>");
288+
MODULE_DESCRIPTION("DRM driver for Visionox G1639FP106 panel");
289+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)