1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2018-2019, Bridge Systems BV
4  * Copyright (C) 2018-2019, Bootlin
5  * Copyright (C) 2017, Free Electrons
6  *
7  * This file based on panel-ilitek-ili9881c.c
8  */
9 
10 #include <linux/delay.h>
11 #include <linux/device.h>
12 #include <linux/err.h>
13 #include <linux/errno.h>
14 #include <linux/fb.h>
15 #include <linux/kernel.h>
16 #include <linux/media-bus-format.h>
17 #include <linux/module.h>
18 
19 #include <linux/gpio/consumer.h>
20 #include <linux/regulator/consumer.h>
21 
22 #include <drm/drm_connector.h>
23 #include <drm/drm_mipi_dsi.h>
24 #include <drm/drm_modes.h>
25 #include <drm/drm_panel.h>
26 
27 struct rb070d30_panel {
28 	struct drm_panel panel;
29 	struct mipi_dsi_device *dsi;
30 	struct regulator *supply;
31 
32 	struct {
33 		struct gpio_desc *power;
34 		struct gpio_desc *reset;
35 		struct gpio_desc *updn;
36 		struct gpio_desc *shlr;
37 	} gpios;
38 };
39 
40 static inline struct rb070d30_panel *panel_to_rb070d30_panel(struct drm_panel *panel)
41 {
42 	return container_of(panel, struct rb070d30_panel, panel);
43 }
44 
45 static int rb070d30_panel_prepare(struct drm_panel *panel)
46 {
47 	struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
48 	int ret;
49 
50 	ret = regulator_enable(ctx->supply);
51 	if (ret < 0) {
52 		dev_err(&ctx->dsi->dev, "Failed to enable supply: %d\n", ret);
53 		return ret;
54 	}
55 
56 	msleep(20);
57 	gpiod_set_value(ctx->gpios.power, 1);
58 	msleep(20);
59 	gpiod_set_value(ctx->gpios.reset, 1);
60 	msleep(20);
61 	return 0;
62 }
63 
64 static int rb070d30_panel_unprepare(struct drm_panel *panel)
65 {
66 	struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
67 
68 	gpiod_set_value(ctx->gpios.reset, 0);
69 	gpiod_set_value(ctx->gpios.power, 0);
70 	regulator_disable(ctx->supply);
71 
72 	return 0;
73 }
74 
75 static int rb070d30_panel_enable(struct drm_panel *panel)
76 {
77 	struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
78 
79 	return mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
80 }
81 
82 static int rb070d30_panel_disable(struct drm_panel *panel)
83 {
84 	struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
85 
86 	return mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
87 }
88 
89 /* Default timings */
90 static const struct drm_display_mode default_mode = {
91 	.clock		= 51206,
92 	.hdisplay	= 1024,
93 	.hsync_start	= 1024 + 160,
94 	.hsync_end	= 1024 + 160 + 80,
95 	.htotal		= 1024 + 160 + 80 + 80,
96 	.vdisplay	= 600,
97 	.vsync_start	= 600 + 12,
98 	.vsync_end	= 600 + 12 + 10,
99 	.vtotal		= 600 + 12 + 10 + 13,
100 
101 	.width_mm	= 154,
102 	.height_mm	= 85,
103 };
104 
105 static int rb070d30_panel_get_modes(struct drm_panel *panel,
106 				    struct drm_connector *connector)
107 {
108 	struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
109 	struct drm_display_mode *mode;
110 	static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
111 
112 	mode = drm_mode_duplicate(connector->dev, &default_mode);
113 	if (!mode) {
114 		dev_err(&ctx->dsi->dev, "Failed to add mode " DRM_MODE_FMT "\n",
115 			DRM_MODE_ARG(&default_mode));
116 		return -EINVAL;
117 	}
118 
119 	drm_mode_set_name(mode);
120 
121 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
122 	drm_mode_probed_add(connector, mode);
123 
124 	connector->display_info.bpc = 8;
125 	connector->display_info.width_mm = mode->width_mm;
126 	connector->display_info.height_mm = mode->height_mm;
127 	drm_display_info_set_bus_formats(&connector->display_info,
128 					 &bus_format, 1);
129 
130 	return 1;
131 }
132 
133 static const struct drm_panel_funcs rb070d30_panel_funcs = {
134 	.get_modes	= rb070d30_panel_get_modes,
135 	.prepare	= rb070d30_panel_prepare,
136 	.enable		= rb070d30_panel_enable,
137 	.disable	= rb070d30_panel_disable,
138 	.unprepare	= rb070d30_panel_unprepare,
139 };
140 
141 static int rb070d30_panel_dsi_probe(struct mipi_dsi_device *dsi)
142 {
143 	struct rb070d30_panel *ctx;
144 	int ret;
145 
146 	ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
147 	if (!ctx)
148 		return -ENOMEM;
149 
150 	ctx->supply = devm_regulator_get(&dsi->dev, "vcc-lcd");
151 	if (IS_ERR(ctx->supply))
152 		return PTR_ERR(ctx->supply);
153 
154 	mipi_dsi_set_drvdata(dsi, ctx);
155 	ctx->dsi = dsi;
156 
157 	drm_panel_init(&ctx->panel, &dsi->dev, &rb070d30_panel_funcs,
158 		       DRM_MODE_CONNECTOR_DSI);
159 
160 	ctx->gpios.reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
161 	if (IS_ERR(ctx->gpios.reset)) {
162 		dev_err(&dsi->dev, "Couldn't get our reset GPIO\n");
163 		return PTR_ERR(ctx->gpios.reset);
164 	}
165 
166 	ctx->gpios.power = devm_gpiod_get(&dsi->dev, "power", GPIOD_OUT_LOW);
167 	if (IS_ERR(ctx->gpios.power)) {
168 		dev_err(&dsi->dev, "Couldn't get our power GPIO\n");
169 		return PTR_ERR(ctx->gpios.power);
170 	}
171 
172 	/*
173 	 * We don't change the state of that GPIO later on but we need
174 	 * to force it into a low state.
175 	 */
176 	ctx->gpios.updn = devm_gpiod_get(&dsi->dev, "updn", GPIOD_OUT_LOW);
177 	if (IS_ERR(ctx->gpios.updn)) {
178 		dev_err(&dsi->dev, "Couldn't get our updn GPIO\n");
179 		return PTR_ERR(ctx->gpios.updn);
180 	}
181 
182 	/*
183 	 * We don't change the state of that GPIO later on but we need
184 	 * to force it into a low state.
185 	 */
186 	ctx->gpios.shlr = devm_gpiod_get(&dsi->dev, "shlr", GPIOD_OUT_LOW);
187 	if (IS_ERR(ctx->gpios.shlr)) {
188 		dev_err(&dsi->dev, "Couldn't get our shlr GPIO\n");
189 		return PTR_ERR(ctx->gpios.shlr);
190 	}
191 
192 	ret = drm_panel_of_backlight(&ctx->panel);
193 	if (ret)
194 		return ret;
195 
196 	drm_panel_add(&ctx->panel);
197 
198 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM;
199 	dsi->format = MIPI_DSI_FMT_RGB888;
200 	dsi->lanes = 4;
201 
202 	ret = mipi_dsi_attach(dsi);
203 	if (ret < 0) {
204 		drm_panel_remove(&ctx->panel);
205 		return ret;
206 	}
207 
208 	return 0;
209 }
210 
211 static void rb070d30_panel_dsi_remove(struct mipi_dsi_device *dsi)
212 {
213 	struct rb070d30_panel *ctx = mipi_dsi_get_drvdata(dsi);
214 
215 	mipi_dsi_detach(dsi);
216 	drm_panel_remove(&ctx->panel);
217 }
218 
219 static const struct of_device_id rb070d30_panel_of_match[] = {
220 	{ .compatible = "ronbo,rb070d30" },
221 	{ /* sentinel */ },
222 };
223 MODULE_DEVICE_TABLE(of, rb070d30_panel_of_match);
224 
225 static struct mipi_dsi_driver rb070d30_panel_driver = {
226 	.probe = rb070d30_panel_dsi_probe,
227 	.remove = rb070d30_panel_dsi_remove,
228 	.driver = {
229 		.name = "panel-ronbo-rb070d30",
230 		.of_match_table	= rb070d30_panel_of_match,
231 	},
232 };
233 module_mipi_dsi_driver(rb070d30_panel_driver);
234 
235 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@bootlin.com>");
236 MODULE_AUTHOR("Konstantin Sudakov <k.sudakov@integrasources.com>");
237 MODULE_DESCRIPTION("Ronbo RB070D30 Panel Driver");
238 MODULE_LICENSE("GPL");
239