xref: /linux/drivers/gpu/drm/panel/panel-ilitek-ili7807s.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1*dd7ec83fSArpit Saini // SPDX-License-Identifier: GPL-2.0-only
2*dd7ec83fSArpit Saini /*
3*dd7ec83fSArpit Saini  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
4*dd7ec83fSArpit Saini  */
5*dd7ec83fSArpit Saini 
6*dd7ec83fSArpit Saini #include <linux/backlight.h>
7*dd7ec83fSArpit Saini #include <linux/delay.h>
8*dd7ec83fSArpit Saini #include <linux/gpio/consumer.h>
9*dd7ec83fSArpit Saini #include <linux/module.h>
10*dd7ec83fSArpit Saini #include <linux/of.h>
11*dd7ec83fSArpit Saini #include <linux/regulator/consumer.h>
12*dd7ec83fSArpit Saini 
13*dd7ec83fSArpit Saini #include <video/mipi_display.h>
14*dd7ec83fSArpit Saini 
15*dd7ec83fSArpit Saini #include <drm/drm_mipi_dsi.h>
16*dd7ec83fSArpit Saini #include <drm/drm_modes.h>
17*dd7ec83fSArpit Saini #include <drm/drm_panel.h>
18*dd7ec83fSArpit Saini #include <drm/drm_probe_helper.h>
19*dd7ec83fSArpit Saini 
20*dd7ec83fSArpit Saini struct panel_desc {
21*dd7ec83fSArpit Saini 	const struct drm_display_mode *mode;
22*dd7ec83fSArpit Saini 	unsigned int lanes;
23*dd7ec83fSArpit Saini 	enum mipi_dsi_pixel_format format;
24*dd7ec83fSArpit Saini 	unsigned long mode_flags;
25*dd7ec83fSArpit Saini 	void (*init)(struct mipi_dsi_multi_context *dsi_ctx);
26*dd7ec83fSArpit Saini };
27*dd7ec83fSArpit Saini 
28*dd7ec83fSArpit Saini struct ili7807s {
29*dd7ec83fSArpit Saini 	struct drm_panel panel;
30*dd7ec83fSArpit Saini 	struct mipi_dsi_device *dsi;
31*dd7ec83fSArpit Saini 	const struct panel_desc *desc;
32*dd7ec83fSArpit Saini 
33*dd7ec83fSArpit Saini 	struct regulator_bulk_data *supplies;
34*dd7ec83fSArpit Saini 	struct gpio_desc *reset_gpio;
35*dd7ec83fSArpit Saini };
36*dd7ec83fSArpit Saini 
37*dd7ec83fSArpit Saini static const struct regulator_bulk_data ili7807s_supplies[] = {
38*dd7ec83fSArpit Saini 	{ .supply = "vddi" },
39*dd7ec83fSArpit Saini 	{ .supply = "avdd" },
40*dd7ec83fSArpit Saini 	{ .supply = "avee" },
41*dd7ec83fSArpit Saini };
42*dd7ec83fSArpit Saini 
43*dd7ec83fSArpit Saini static inline struct ili7807s *to_ili7807s(struct drm_panel *panel)
44*dd7ec83fSArpit Saini {
45*dd7ec83fSArpit Saini 	return container_of(panel, struct ili7807s, panel);
46*dd7ec83fSArpit Saini }
47*dd7ec83fSArpit Saini 
48*dd7ec83fSArpit Saini static void ili7807s_reset(struct ili7807s *ctx)
49*dd7ec83fSArpit Saini {
50*dd7ec83fSArpit Saini 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
51*dd7ec83fSArpit Saini 	usleep_range(10000, 11000);
52*dd7ec83fSArpit Saini 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
53*dd7ec83fSArpit Saini 	usleep_range(10000, 11000);
54*dd7ec83fSArpit Saini }
55*dd7ec83fSArpit Saini 
56*dd7ec83fSArpit Saini static void dlc0697_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
57*dd7ec83fSArpit Saini {
58*dd7ec83fSArpit Saini 	mipi_dsi_dcs_soft_reset_multi(dsi_ctx);
59*dd7ec83fSArpit Saini 	mipi_dsi_msleep(dsi_ctx, 120);
60*dd7ec83fSArpit Saini 
61*dd7ec83fSArpit Saini 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x78, 0x07, 0x00);
62*dd7ec83fSArpit Saini 	mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
63*dd7ec83fSArpit Saini 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS, 0x09, 0x99);
64*dd7ec83fSArpit Saini 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24);
65*dd7ec83fSArpit Saini 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x01);
66*dd7ec83fSArpit Saini 	mipi_dsi_dcs_set_display_brightness_multi(dsi_ctx, 0x1fff);
67*dd7ec83fSArpit Saini 
68*dd7ec83fSArpit Saini 	mipi_dsi_dcs_exit_sleep_mode_multi(dsi_ctx);
69*dd7ec83fSArpit Saini 	mipi_dsi_msleep(dsi_ctx, 120);
70*dd7ec83fSArpit Saini 
71*dd7ec83fSArpit Saini 	mipi_dsi_dcs_set_display_on_multi(dsi_ctx);
72*dd7ec83fSArpit Saini 	mipi_dsi_msleep(dsi_ctx, 20);
73*dd7ec83fSArpit Saini }
74*dd7ec83fSArpit Saini 
75*dd7ec83fSArpit Saini static int ili7807s_on(struct ili7807s *ctx)
76*dd7ec83fSArpit Saini {
77*dd7ec83fSArpit Saini 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
78*dd7ec83fSArpit Saini 
79*dd7ec83fSArpit Saini 	ctx->desc->init(&dsi_ctx);
80*dd7ec83fSArpit Saini 
81*dd7ec83fSArpit Saini 	return dsi_ctx.accum_err;
82*dd7ec83fSArpit Saini }
83*dd7ec83fSArpit Saini 
84*dd7ec83fSArpit Saini static int ili7807s_off(struct ili7807s *ctx)
85*dd7ec83fSArpit Saini {
86*dd7ec83fSArpit Saini 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
87*dd7ec83fSArpit Saini 
88*dd7ec83fSArpit Saini 	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
89*dd7ec83fSArpit Saini 	mipi_dsi_msleep(&dsi_ctx, 20);
90*dd7ec83fSArpit Saini 
91*dd7ec83fSArpit Saini 	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
92*dd7ec83fSArpit Saini 	mipi_dsi_msleep(&dsi_ctx, 120);
93*dd7ec83fSArpit Saini 
94*dd7ec83fSArpit Saini 	return dsi_ctx.accum_err;
95*dd7ec83fSArpit Saini }
96*dd7ec83fSArpit Saini 
97*dd7ec83fSArpit Saini static int ili7807s_prepare(struct drm_panel *panel)
98*dd7ec83fSArpit Saini {
99*dd7ec83fSArpit Saini 	struct ili7807s *ctx = to_ili7807s(panel);
100*dd7ec83fSArpit Saini 	int ret;
101*dd7ec83fSArpit Saini 
102*dd7ec83fSArpit Saini 	ret = regulator_bulk_enable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies);
103*dd7ec83fSArpit Saini 	if (ret < 0) {
104*dd7ec83fSArpit Saini 		dev_err(ctx->panel.dev, "failed to enable regulators: %d\n", ret);
105*dd7ec83fSArpit Saini 		return ret;
106*dd7ec83fSArpit Saini 	}
107*dd7ec83fSArpit Saini 
108*dd7ec83fSArpit Saini 	msleep(20);
109*dd7ec83fSArpit Saini 
110*dd7ec83fSArpit Saini 	ili7807s_reset(ctx);
111*dd7ec83fSArpit Saini 
112*dd7ec83fSArpit Saini 	ret = ili7807s_on(ctx);
113*dd7ec83fSArpit Saini 	if (ret < 0) {
114*dd7ec83fSArpit Saini 		dev_err(ctx->panel.dev, "failed to initialise panel: %d\n", ret);
115*dd7ec83fSArpit Saini 		goto err;
116*dd7ec83fSArpit Saini 	}
117*dd7ec83fSArpit Saini 
118*dd7ec83fSArpit Saini 	return 0;
119*dd7ec83fSArpit Saini 
120*dd7ec83fSArpit Saini err:
121*dd7ec83fSArpit Saini 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
122*dd7ec83fSArpit Saini 
123*dd7ec83fSArpit Saini 	regulator_bulk_disable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies);
124*dd7ec83fSArpit Saini 	return ret;
125*dd7ec83fSArpit Saini }
126*dd7ec83fSArpit Saini 
127*dd7ec83fSArpit Saini static int ili7807s_unprepare(struct drm_panel *panel)
128*dd7ec83fSArpit Saini {
129*dd7ec83fSArpit Saini 	struct ili7807s *ctx = to_ili7807s(panel);
130*dd7ec83fSArpit Saini 	int ret;
131*dd7ec83fSArpit Saini 
132*dd7ec83fSArpit Saini 	ret = ili7807s_off(ctx);
133*dd7ec83fSArpit Saini 	if (ret < 0)
134*dd7ec83fSArpit Saini 		dev_err(ctx->panel.dev, "failed to disable panel: %d\n", ret);
135*dd7ec83fSArpit Saini 
136*dd7ec83fSArpit Saini 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
137*dd7ec83fSArpit Saini 
138*dd7ec83fSArpit Saini 	regulator_bulk_disable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies);
139*dd7ec83fSArpit Saini 
140*dd7ec83fSArpit Saini 	return 0;
141*dd7ec83fSArpit Saini }
142*dd7ec83fSArpit Saini 
143*dd7ec83fSArpit Saini static int ili7807s_get_modes(struct drm_panel *panel,
144*dd7ec83fSArpit Saini 			      struct drm_connector *connector)
145*dd7ec83fSArpit Saini {
146*dd7ec83fSArpit Saini 	struct ili7807s *ctx = to_ili7807s(panel);
147*dd7ec83fSArpit Saini 
148*dd7ec83fSArpit Saini 	return drm_connector_helper_get_modes_fixed(connector, ctx->desc->mode);
149*dd7ec83fSArpit Saini }
150*dd7ec83fSArpit Saini 
151*dd7ec83fSArpit Saini static const struct drm_panel_funcs ili7807s_panel_funcs = {
152*dd7ec83fSArpit Saini 	.prepare = ili7807s_prepare,
153*dd7ec83fSArpit Saini 	.unprepare = ili7807s_unprepare,
154*dd7ec83fSArpit Saini 	.get_modes = ili7807s_get_modes,
155*dd7ec83fSArpit Saini };
156*dd7ec83fSArpit Saini 
157*dd7ec83fSArpit Saini static int ili7807s_bl_update_status(struct backlight_device *bl)
158*dd7ec83fSArpit Saini {
159*dd7ec83fSArpit Saini 	struct mipi_dsi_device *dsi = bl_get_data(bl);
160*dd7ec83fSArpit Saini 	u16 brightness = backlight_get_brightness(bl);
161*dd7ec83fSArpit Saini 	int ret;
162*dd7ec83fSArpit Saini 
163*dd7ec83fSArpit Saini 	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
164*dd7ec83fSArpit Saini 
165*dd7ec83fSArpit Saini 	ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
166*dd7ec83fSArpit Saini 
167*dd7ec83fSArpit Saini 	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
168*dd7ec83fSArpit Saini 
169*dd7ec83fSArpit Saini 	return ret;
170*dd7ec83fSArpit Saini }
171*dd7ec83fSArpit Saini 
172*dd7ec83fSArpit Saini static const struct backlight_ops ili7807s_bl_ops = {
173*dd7ec83fSArpit Saini 	.update_status = ili7807s_bl_update_status,
174*dd7ec83fSArpit Saini };
175*dd7ec83fSArpit Saini 
176*dd7ec83fSArpit Saini static struct backlight_device *ili7807s_create_backlight(struct mipi_dsi_device *dsi)
177*dd7ec83fSArpit Saini {
178*dd7ec83fSArpit Saini 	struct device *dev = &dsi->dev;
179*dd7ec83fSArpit Saini 	const struct backlight_properties props = {
180*dd7ec83fSArpit Saini 		.type           = BACKLIGHT_RAW,
181*dd7ec83fSArpit Saini 		.brightness     = 0x3fff,
182*dd7ec83fSArpit Saini 		.max_brightness = 0x3fff,
183*dd7ec83fSArpit Saini 	};
184*dd7ec83fSArpit Saini 
185*dd7ec83fSArpit Saini 	return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
186*dd7ec83fSArpit Saini 					      &ili7807s_bl_ops, &props);
187*dd7ec83fSArpit Saini }
188*dd7ec83fSArpit Saini 
189*dd7ec83fSArpit Saini static const struct drm_display_mode dlc0697_mode = {
190*dd7ec83fSArpit Saini 	.clock = (1080 + 18 + 2 + 16) * (1920 + 26 + 4 + 20) * 60 / 1000,
191*dd7ec83fSArpit Saini 
192*dd7ec83fSArpit Saini 	.hdisplay    = 1080,
193*dd7ec83fSArpit Saini 	.hsync_start = 1080 + 18,
194*dd7ec83fSArpit Saini 	.hsync_end   = 1080 + 18 + 2,
195*dd7ec83fSArpit Saini 	.htotal      = 1080 + 18 + 2 + 16,
196*dd7ec83fSArpit Saini 
197*dd7ec83fSArpit Saini 	.vdisplay    = 1920,
198*dd7ec83fSArpit Saini 	.vsync_start = 1920 + 26,
199*dd7ec83fSArpit Saini 	.vsync_end   = 1920 + 26 + 4,
200*dd7ec83fSArpit Saini 	.vtotal      = 1920 + 26 + 4 + 20,
201*dd7ec83fSArpit Saini 
202*dd7ec83fSArpit Saini 	.width_mm  = 87,
203*dd7ec83fSArpit Saini 	.height_mm = 154,
204*dd7ec83fSArpit Saini 	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
205*dd7ec83fSArpit Saini };
206*dd7ec83fSArpit Saini 
207*dd7ec83fSArpit Saini static const struct panel_desc dlc0697_desc = {
208*dd7ec83fSArpit Saini 	.mode       = &dlc0697_mode,
209*dd7ec83fSArpit Saini 	.lanes      = 4,
210*dd7ec83fSArpit Saini 	.format     = MIPI_DSI_FMT_RGB888,
211*dd7ec83fSArpit Saini 	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST,
212*dd7ec83fSArpit Saini 	.init       = dlc0697_init_sequence,
213*dd7ec83fSArpit Saini };
214*dd7ec83fSArpit Saini 
215*dd7ec83fSArpit Saini static int ili7807s_probe(struct mipi_dsi_device *dsi)
216*dd7ec83fSArpit Saini {
217*dd7ec83fSArpit Saini 	struct device *dev = &dsi->dev;
218*dd7ec83fSArpit Saini 	const struct panel_desc *desc;
219*dd7ec83fSArpit Saini 	struct ili7807s *ctx;
220*dd7ec83fSArpit Saini 	int ret;
221*dd7ec83fSArpit Saini 
222*dd7ec83fSArpit Saini 	ctx = devm_drm_panel_alloc(dev, struct ili7807s, panel,
223*dd7ec83fSArpit Saini 				   &ili7807s_panel_funcs,
224*dd7ec83fSArpit Saini 				   DRM_MODE_CONNECTOR_DSI);
225*dd7ec83fSArpit Saini 	if (IS_ERR(ctx))
226*dd7ec83fSArpit Saini 		return PTR_ERR(ctx);
227*dd7ec83fSArpit Saini 
228*dd7ec83fSArpit Saini 	desc = of_device_get_match_data(dev);
229*dd7ec83fSArpit Saini 	ctx->desc = desc;
230*dd7ec83fSArpit Saini 
231*dd7ec83fSArpit Saini 	ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(ili7807s_supplies),
232*dd7ec83fSArpit Saini 					    ili7807s_supplies, &ctx->supplies);
233*dd7ec83fSArpit Saini 	if (ret < 0)
234*dd7ec83fSArpit Saini 		return dev_err_probe(dev, ret, "failed to get regulators\n");
235*dd7ec83fSArpit Saini 
236*dd7ec83fSArpit Saini 	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
237*dd7ec83fSArpit Saini 	if (IS_ERR(ctx->reset_gpio))
238*dd7ec83fSArpit Saini 		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
239*dd7ec83fSArpit Saini 				     "failed to get reset gpio\n");
240*dd7ec83fSArpit Saini 
241*dd7ec83fSArpit Saini 	ctx->dsi = dsi;
242*dd7ec83fSArpit Saini 	mipi_dsi_set_drvdata(dsi, ctx);
243*dd7ec83fSArpit Saini 
244*dd7ec83fSArpit Saini 	dsi->lanes      = desc->lanes;
245*dd7ec83fSArpit Saini 	dsi->format     = desc->format;
246*dd7ec83fSArpit Saini 	dsi->mode_flags = desc->mode_flags | MIPI_DSI_MODE_LPM;
247*dd7ec83fSArpit Saini 
248*dd7ec83fSArpit Saini 	ctx->panel.prepare_prev_first = true;
249*dd7ec83fSArpit Saini 
250*dd7ec83fSArpit Saini 	ret = drm_panel_of_backlight(&ctx->panel);
251*dd7ec83fSArpit Saini 	if (ret)
252*dd7ec83fSArpit Saini 		return ret;
253*dd7ec83fSArpit Saini 
254*dd7ec83fSArpit Saini 	if (!ctx->panel.backlight) {
255*dd7ec83fSArpit Saini 		ctx->panel.backlight = ili7807s_create_backlight(dsi);
256*dd7ec83fSArpit Saini 		if (IS_ERR(ctx->panel.backlight))
257*dd7ec83fSArpit Saini 			return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
258*dd7ec83fSArpit Saini 					     "failed to create backlight\n");
259*dd7ec83fSArpit Saini 	}
260*dd7ec83fSArpit Saini 
261*dd7ec83fSArpit Saini 	ret = devm_drm_panel_add(dev, &ctx->panel);
262*dd7ec83fSArpit Saini 	if (ret < 0)
263*dd7ec83fSArpit Saini 		return dev_err_probe(dev, ret, "failed to add panel\n");
264*dd7ec83fSArpit Saini 
265*dd7ec83fSArpit Saini 	return devm_mipi_dsi_attach(dev, dsi);
266*dd7ec83fSArpit Saini }
267*dd7ec83fSArpit Saini 
268*dd7ec83fSArpit Saini static const struct of_device_id ili7807s_of_match[] = {
269*dd7ec83fSArpit Saini 	{ .compatible = "dlc,dlc0697", .data = &dlc0697_desc },
270*dd7ec83fSArpit Saini 	{ }
271*dd7ec83fSArpit Saini };
272*dd7ec83fSArpit Saini MODULE_DEVICE_TABLE(of, ili7807s_of_match);
273*dd7ec83fSArpit Saini 
274*dd7ec83fSArpit Saini static struct mipi_dsi_driver ili7807s_dsi_driver = {
275*dd7ec83fSArpit Saini 	.probe  = ili7807s_probe,
276*dd7ec83fSArpit Saini 	.driver = {
277*dd7ec83fSArpit Saini 		.name           = "panel-ilitek-ili7807s",
278*dd7ec83fSArpit Saini 		.of_match_table = ili7807s_of_match,
279*dd7ec83fSArpit Saini 	},
280*dd7ec83fSArpit Saini };
281*dd7ec83fSArpit Saini module_mipi_dsi_driver(ili7807s_dsi_driver);
282*dd7ec83fSArpit Saini 
283*dd7ec83fSArpit Saini MODULE_AUTHOR("Arpit Saini <arpit.saini@oss.qualcomm.com>");
284*dd7ec83fSArpit Saini MODULE_DESCRIPTION("Panel driver for Ilitek ILI7807S LCD DSI panel");
285*dd7ec83fSArpit Saini MODULE_LICENSE("GPL");
286