1255376Sdes// SPDX-License-Identifier: GPL-2.0-only
2255376Sdes/*
3255376Sdes * Copyright (c) 2022 Joel Selvaraj <jo@jsfamily.in>
4255376Sdes * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree:
5255376Sdes * Copyright (c) 2013, The Linux Foundation. All rights reserved.
6255376Sdes */
7255376Sdes
8255376Sdes#include <linux/delay.h>
9255376Sdes#include <linux/gpio/consumer.h>
10255376Sdes#include <linux/regulator/consumer.h>
11255376Sdes#include <linux/module.h>
12255376Sdes#include <linux/of.h>
13255376Sdes
14255376Sdes#include <video/mipi_display.h>
15255376Sdes
16255376Sdes#include <drm/drm_mipi_dsi.h>
17255376Sdes#include <drm/drm_modes.h>
18255376Sdes#include <drm/drm_panel.h>
19255376Sdes
20255376Sdesstatic const char * const regulator_names[] = {
21255376Sdes	"vddio",
22255376Sdes	"vddpos",
23255376Sdes	"vddneg",
24255376Sdes};
25255376Sdes
26255376Sdesstatic const unsigned long regulator_enable_loads[] = {
27255376Sdes	62000,
28236109Sdes	100000,
29236109Sdes	100000
30236109Sdes};
31236109Sdes
32236109Sdesstruct ebbg_ft8719 {
33236109Sdes	struct drm_panel panel;
34236109Sdes	struct mipi_dsi_device *dsi;
35236109Sdes
36236109Sdes	struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];
37236109Sdes
38236109Sdes	struct gpio_desc *reset_gpio;
39236109Sdes};
40236109Sdes
41236109Sdesstatic inline
42236109Sdesstruct ebbg_ft8719 *to_ebbg_ft8719(struct drm_panel *panel)
43236109Sdes{
44236109Sdes	return container_of(panel, struct ebbg_ft8719, panel);
45236109Sdes}
46236109Sdes
47236109Sdesstatic void ebbg_ft8719_reset(struct ebbg_ft8719 *ctx)
48236109Sdes{
49236109Sdes	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
50236109Sdes	usleep_range(4000, 5000);
51236109Sdes	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
52236109Sdes	usleep_range(1000, 2000);
53236109Sdes	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
54236109Sdes	usleep_range(15000, 16000);
55236109Sdes}
56236109Sdes
57236109Sdesstatic int ebbg_ft8719_on(struct ebbg_ft8719 *ctx)
58236109Sdes{
59236109Sdes	struct mipi_dsi_device *dsi = ctx->dsi;
60236109Sdes	struct device *dev = &dsi->dev;
61236109Sdes	int ret;
62236109Sdes
63236109Sdes	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
64236109Sdes
65236109Sdes	ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff);
66236109Sdes	if (ret < 0) {
67236109Sdes		dev_err(dev, "Failed to set display brightness: %d\n", ret);
68236109Sdes		return ret;
69236109Sdes	}
70236109Sdes
71236109Sdes	mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24);
72236109Sdes	mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
73236109Sdes
74236109Sdes	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
75236109Sdes	if (ret < 0) {
76228692Sdes		dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
77228692Sdes		return ret;
78228692Sdes	}
79228692Sdes	msleep(90);
80228692Sdes
81228692Sdes	ret = mipi_dsi_dcs_set_display_on(dsi);
82228692Sdes	if (ret < 0) {
83228692Sdes		dev_err(dev, "Failed to set display on: %d\n", ret);
84228692Sdes		return ret;
85228692Sdes	}
86228692Sdes
87228692Sdes	return 0;
88228692Sdes}
89228692Sdes
90228692Sdesstatic int ebbg_ft8719_off(struct ebbg_ft8719 *ctx)
91228692Sdes{
92228692Sdes	struct mipi_dsi_device *dsi = ctx->dsi;
93228692Sdes	struct device *dev = &dsi->dev;
94228692Sdes	int ret;
95228692Sdes
96228692Sdes	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
97228692Sdes
98228692Sdes	ret = mipi_dsi_dcs_set_display_off(dsi);
99228692Sdes	if (ret < 0) {
100228692Sdes		dev_err(dev, "Failed to set display off: %d\n", ret);
101228692Sdes		return ret;
102174832Sdes	}
103147455Sdes	usleep_range(10000, 11000);
104174832Sdes
105174832Sdes	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
106174832Sdes	if (ret < 0) {
107174832Sdes		dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
108174832Sdes		return ret;
109174832Sdes	}
110174832Sdes	msleep(90);
111174832Sdes
112174832Sdes	return 0;
113174832Sdes}
114174832Sdes
115174832Sdesstatic int ebbg_ft8719_prepare(struct drm_panel *panel)
116174832Sdes{
117174832Sdes	struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel);
118174832Sdes	struct device *dev = &ctx->dsi->dev;
119174832Sdes	int ret;
120174832Sdes
121174832Sdes	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
122174832Sdes	if (ret < 0)
123174832Sdes		return ret;
124174832Sdes
125228692Sdes	ebbg_ft8719_reset(ctx);
126174832Sdes
127147455Sdes	ret = ebbg_ft8719_on(ctx);
128147455Sdes	if (ret < 0) {
129147455Sdes		dev_err(dev, "Failed to initialize panel: %d\n", ret);
130147455Sdes		gpiod_set_value_cansleep(ctx->reset_gpio, 1);
131147455Sdes		return ret;
132147455Sdes	}
133147455Sdes
134147455Sdes	return 0;
135147455Sdes}
136147455Sdes
137147455Sdesstatic int ebbg_ft8719_unprepare(struct drm_panel *panel)
138141098Sdes{
139141098Sdes	struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel);
140141098Sdes	struct device *dev = &ctx->dsi->dev;
141141098Sdes	int ret;
142141098Sdes
143141098Sdes	ret = ebbg_ft8719_off(ctx);
144141098Sdes	if (ret < 0)
145141098Sdes		dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
146141098Sdes
147141098Sdes	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
148141098Sdes
149141098Sdes	ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
150141098Sdes	if (ret)
151141098Sdes		dev_err(panel->dev, "Failed to disable regulators: %d\n", ret);
152141098Sdes
153141098Sdes	return 0;
154141098Sdes}
155141098Sdes
156141098Sdesstatic const struct drm_display_mode ebbg_ft8719_mode = {
157125647Sdes	.clock = (1080 + 28 + 4 + 16) * (2246 + 120 + 4 + 12) * 60 / 1000,
158125647Sdes	.hdisplay = 1080,
159125647Sdes	.hsync_start = 1080 + 28,
160125647Sdes	.hsync_end = 1080 + 28 + 4,
161125647Sdes	.htotal = 1080 + 28 + 4 + 16,
162125647Sdes	.vdisplay = 2246,
163125647Sdes	.vsync_start = 2246 + 120,
164125647Sdes	.vsync_end = 2246 + 120 + 4,
165125647Sdes	.vtotal = 2246 + 120 + 4 + 12,
166117610Sdes	.width_mm = 68,
167117610Sdes	.height_mm = 141,
168117610Sdes};
169117610Sdes
170117610Sdesstatic int ebbg_ft8719_get_modes(struct drm_panel *panel,
171117610Sdes				     struct drm_connector *connector)
172117610Sdes{
173117610Sdes	struct drm_display_mode *mode;
174117610Sdes
175117610Sdes	mode = drm_mode_duplicate(connector->dev, &ebbg_ft8719_mode);
176117610Sdes	if (!mode)
177117610Sdes		return -ENOMEM;
178117610Sdes
179117610Sdes	drm_mode_set_name(mode);
180117610Sdes
181117610Sdes	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
182117610Sdes	connector->display_info.width_mm = mode->width_mm;
183117610Sdes	connector->display_info.height_mm = mode->height_mm;
184117610Sdes	drm_mode_probed_add(connector, mode);
185117610Sdes
186117610Sdes	return 1;
187115619Sdes}
188115619Sdes
189115619Sdesstatic const struct drm_panel_funcs ebbg_ft8719_panel_funcs = {
190115619Sdes	.prepare = ebbg_ft8719_prepare,
191115619Sdes	.unprepare = ebbg_ft8719_unprepare,
192115619Sdes	.get_modes = ebbg_ft8719_get_modes,
193115619Sdes};
194115619Sdes
195115619Sdesstatic int ebbg_ft8719_probe(struct mipi_dsi_device *dsi)
196115619Sdes{
197115619Sdes	struct device *dev = &dsi->dev;
198115619Sdes	struct ebbg_ft8719 *ctx;
199115619Sdes	int i, ret;
200115619Sdes
201115619Sdes	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
202115619Sdes	if (!ctx)
203115619Sdes		return -ENOMEM;
204115619Sdes
205115619Sdes	for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)
206114536Sdes		ctx->supplies[i].supply = regulator_names[i];
207114536Sdes
208114536Sdes	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
209114536Sdes				      ctx->supplies);
210114536Sdes	if (ret < 0)
211114536Sdes		return dev_err_probe(dev, ret, "Failed to get regulators\n");
212114536Sdes
213114536Sdes	for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {
214114536Sdes		ret = regulator_set_load(ctx->supplies[i].consumer,
215114536Sdes						regulator_enable_loads[i]);
216114536Sdes		if (ret)
217114536Sdes			return dev_err_probe(dev, ret,
218114536Sdes						 "Failed to set regulator load\n");
219114536Sdes	}
220114536Sdes
221114536Sdes	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
222114536Sdes	if (IS_ERR(ctx->reset_gpio))
223108794Sdes		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
224108794Sdes				     "Failed to get reset-gpios\n");
225108794Sdes
226108794Sdes	ctx->dsi = dsi;
227108794Sdes	mipi_dsi_set_drvdata(dsi, ctx);
228108794Sdes
229108794Sdes	dsi->lanes = 4;
230108794Sdes	dsi->format = MIPI_DSI_FMT_RGB888;
231107937Sdes	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
232107937Sdes			  MIPI_DSI_CLOCK_NON_CONTINUOUS;
233107937Sdes
234107937Sdes	drm_panel_init(&ctx->panel, dev, &ebbg_ft8719_panel_funcs,
235107937Sdes		       DRM_MODE_CONNECTOR_DSI);
236107937Sdes
237107937Sdes	ret = drm_panel_of_backlight(&ctx->panel);
238107937Sdes	if (ret)
239107937Sdes		return dev_err_probe(dev, ret, "Failed to get backlight\n");
240107937Sdes
241107937Sdes	drm_panel_add(&ctx->panel);
242107937Sdes
243107937Sdes	ret = mipi_dsi_attach(dsi);
244107937Sdes	if (ret < 0) {
245107937Sdes		dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
24691094Sdes		drm_panel_remove(&ctx->panel);
24799158Sdes		return ret;
24899158Sdes	}
24999158Sdes
25099158Sdes	return 0;
25199158Sdes}
25299158Sdes
25399158Sdesstatic void ebbg_ft8719_remove(struct mipi_dsi_device *dsi)
254107937Sdes{
25599158Sdes	struct ebbg_ft8719 *ctx = mipi_dsi_get_drvdata(dsi);
25699158Sdes	int ret;
25799158Sdes
25899158Sdes	ret = mipi_dsi_detach(dsi);
25999158Sdes	if (ret < 0)
26099158Sdes		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
26199158Sdes
26299158Sdes	drm_panel_remove(&ctx->panel);
26399158Sdes}
26499158Sdes
26599158Sdesstatic const struct of_device_id ebbg_ft8719_of_match[] = {
26699158Sdes	{ .compatible = "ebbg,ft8719" },
26799158Sdes	{ /* sentinel */ }
26897241Sdes};
26997241SdesMODULE_DEVICE_TABLE(of, ebbg_ft8719_of_match);
27097241Sdes
27197241Sdesstatic struct mipi_dsi_driver ebbg_ft8719_driver = {
27297241Sdes	.probe = ebbg_ft8719_probe,
27397241Sdes	.remove = ebbg_ft8719_remove,
27497241Sdes	.driver = {
27597241Sdes		.name = "panel-ebbg-ft8719",
27697241Sdes		.of_match_table = ebbg_ft8719_of_match,
27797241Sdes	},
27895908Sdes};
27995908Sdesmodule_mipi_dsi_driver(ebbg_ft8719_driver);
28095908Sdes
28195908SdesMODULE_AUTHOR("Joel Selvaraj <jo@jsfamily.in>");
28295908SdesMODULE_DESCRIPTION("DRM driver for EBBG FT8719 video dsi panel");
28395908SdesMODULE_LICENSE("GPL v2");
28495908Sdes