xref: /linux/drivers/gpu/drm/panel/panel-renesas-r61307.c (revision 8d2b0853add1d7534dc0794e3c8e0b9e8c4ec640)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <linux/array_size.h>
4 #include <linux/delay.h>
5 #include <linux/err.h>
6 #include <linux/gpio/consumer.h>
7 #include <linux/mod_devicetable.h>
8 #include <linux/module.h>
9 #include <linux/property.h>
10 #include <linux/regulator/consumer.h>
11 
12 #include <video/mipi_display.h>
13 
14 #include <drm/drm_mipi_dsi.h>
15 #include <drm/drm_modes.h>
16 #include <drm/drm_panel.h>
17 
18 #define R61307_MACP		0xb0 /* Manufacturer CMD Protect */
19 #define   R61307_MACP_ON	0x03
20 #define   R61307_MACP_OFF	0x04
21 
22 #define R61307_INVERSION	0xc1
23 #define R61307_GAMMA_SET_A	0xc8 /* Gamma Setting A */
24 #define R61307_GAMMA_SET_B	0xc9 /* Gamma Setting B */
25 #define R61307_GAMMA_SET_C	0xca /* Gamma Setting C */
26 #define R61307_CONTRAST_SET	0xcc
27 
28 struct renesas_r61307 {
29 	struct drm_panel panel;
30 	struct mipi_dsi_device *dsi;
31 
32 	struct regulator *vcc_supply;
33 	struct regulator *iovcc_supply;
34 
35 	struct gpio_desc *reset_gpio;
36 
37 	bool prepared;
38 
39 	bool dig_cont_adj;
40 	bool inversion;
41 	u32 gamma;
42 };
43 
44 static const u8 gamma_setting[][25] = {
45 	{ /* sentinel */ },
46 	{
47 		R61307_GAMMA_SET_A,
48 		0x00, 0x06, 0x0a, 0x0f,
49 		0x14, 0x1f, 0x1f, 0x17,
50 		0x12, 0x0c, 0x09, 0x06,
51 		0x00, 0x06, 0x0a, 0x0f,
52 		0x14, 0x1f, 0x1f, 0x17,
53 		0x12, 0x0c, 0x09, 0x06
54 	},
55 	{
56 		R61307_GAMMA_SET_A,
57 		0x00, 0x05, 0x0b, 0x0f,
58 		0x11, 0x1d, 0x20, 0x18,
59 		0x18, 0x09, 0x07, 0x06,
60 		0x00, 0x05, 0x0b, 0x0f,
61 		0x11, 0x1d, 0x20, 0x18,
62 		0x18, 0x09, 0x07, 0x06
63 	},
64 	{
65 		R61307_GAMMA_SET_A,
66 		0x0b, 0x0d, 0x10, 0x14,
67 		0x13, 0x1d, 0x20, 0x18,
68 		0x12, 0x09, 0x07, 0x06,
69 		0x0a, 0x0c, 0x10, 0x14,
70 		0x13, 0x1d, 0x20, 0x18,
71 		0x12, 0x09, 0x07, 0x06
72 	},
73 };
74 
75 static inline struct renesas_r61307 *to_renesas_r61307(struct drm_panel *panel)
76 {
77 	return container_of(panel, struct renesas_r61307, panel);
78 }
79 
80 static void renesas_r61307_reset(struct renesas_r61307 *priv)
81 {
82 	gpiod_set_value_cansleep(priv->reset_gpio, 1);
83 	usleep_range(10000, 11000);
84 	gpiod_set_value_cansleep(priv->reset_gpio, 0);
85 	usleep_range(2000, 3000);
86 }
87 
88 static int renesas_r61307_prepare(struct drm_panel *panel)
89 {
90 	struct renesas_r61307 *priv = to_renesas_r61307(panel);
91 	struct device *dev = &priv->dsi->dev;
92 	int ret;
93 
94 	if (priv->prepared)
95 		return 0;
96 
97 	ret = regulator_enable(priv->vcc_supply);
98 	if (ret) {
99 		dev_err(dev, "failed to enable vcc power supply\n");
100 		return ret;
101 	}
102 
103 	usleep_range(2000, 3000);
104 
105 	ret = regulator_enable(priv->iovcc_supply);
106 	if (ret) {
107 		dev_err(dev, "failed to enable iovcc power supply\n");
108 		return ret;
109 	}
110 
111 	usleep_range(2000, 3000);
112 
113 	renesas_r61307_reset(priv);
114 
115 	priv->prepared = true;
116 	return 0;
117 }
118 
119 static int renesas_r61307_enable(struct drm_panel *panel)
120 {
121 	struct renesas_r61307 *priv = to_renesas_r61307(panel);
122 	struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi };
123 
124 	mipi_dsi_dcs_exit_sleep_mode_multi(&ctx);
125 	mipi_dsi_msleep(&ctx, 80);
126 
127 	mipi_dsi_dcs_write_seq_multi(&ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
128 	mipi_dsi_msleep(&ctx, 20);
129 
130 	mipi_dsi_dcs_set_pixel_format_multi(&ctx, MIPI_DCS_PIXEL_FMT_24BIT << 4);
131 
132 	/* MACP Off */
133 	mipi_dsi_generic_write_seq_multi(&ctx, R61307_MACP, R61307_MACP_OFF);
134 
135 	if (priv->dig_cont_adj)
136 		mipi_dsi_generic_write_seq_multi(&ctx, R61307_CONTRAST_SET,
137 						 0xdc, 0xb4, 0xff);
138 
139 	if (priv->gamma)
140 		mipi_dsi_generic_write_multi(&ctx, gamma_setting[priv->gamma],
141 					     sizeof(gamma_setting[priv->gamma]));
142 
143 	if (priv->inversion)
144 		mipi_dsi_generic_write_seq_multi(&ctx, R61307_INVERSION,
145 						 0x00, 0x50, 0x03, 0x22,
146 						 0x16, 0x06, 0x60, 0x11);
147 	else
148 		mipi_dsi_generic_write_seq_multi(&ctx, R61307_INVERSION,
149 						 0x00, 0x10, 0x03, 0x22,
150 						 0x16, 0x06, 0x60, 0x01);
151 
152 	/* MACP On */
153 	mipi_dsi_generic_write_seq_multi(&ctx, R61307_MACP, R61307_MACP_ON);
154 
155 	mipi_dsi_dcs_set_display_on_multi(&ctx);
156 	mipi_dsi_msleep(&ctx, 50);
157 
158 	return 0;
159 }
160 
161 static int renesas_r61307_disable(struct drm_panel *panel)
162 {
163 	struct renesas_r61307 *priv = to_renesas_r61307(panel);
164 	struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi };
165 
166 	mipi_dsi_dcs_set_display_off_multi(&ctx);
167 	mipi_dsi_msleep(&ctx, 100);
168 	mipi_dsi_dcs_enter_sleep_mode_multi(&ctx);
169 
170 	return 0;
171 }
172 
173 static int renesas_r61307_unprepare(struct drm_panel *panel)
174 {
175 	struct renesas_r61307 *priv = to_renesas_r61307(panel);
176 
177 	if (!priv->prepared)
178 		return 0;
179 
180 	usleep_range(10000, 11000);
181 
182 	gpiod_set_value_cansleep(priv->reset_gpio, 1);
183 	usleep_range(5000, 6000);
184 
185 	regulator_disable(priv->iovcc_supply);
186 	usleep_range(2000, 3000);
187 	regulator_disable(priv->vcc_supply);
188 
189 	priv->prepared = false;
190 	return 0;
191 }
192 
193 static const struct drm_display_mode renesas_r61307_mode = {
194 	.clock = (768 + 116 + 81 + 5) * (1024 + 24 + 8 + 2) * 60 / 1000,
195 	.hdisplay = 768,
196 	.hsync_start = 768 + 116,
197 	.hsync_end = 768 + 116 + 81,
198 	.htotal = 768 + 116 + 81 + 5,
199 	.vdisplay = 1024,
200 	.vsync_start = 1024 + 24,
201 	.vsync_end = 1024 + 24 + 8,
202 	.vtotal = 1024 + 24 + 8 + 2,
203 	.width_mm = 76,
204 	.height_mm = 101,
205 };
206 
207 static int renesas_r61307_get_modes(struct drm_panel *panel,
208 				    struct drm_connector *connector)
209 {
210 	struct drm_display_mode *mode;
211 
212 	mode = drm_mode_duplicate(connector->dev, &renesas_r61307_mode);
213 	if (!mode)
214 		return -ENOMEM;
215 
216 	drm_mode_set_name(mode);
217 
218 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
219 	connector->display_info.width_mm = mode->width_mm;
220 	connector->display_info.height_mm = mode->height_mm;
221 	drm_mode_probed_add(connector, mode);
222 
223 	return 1;
224 }
225 
226 static const struct drm_panel_funcs renesas_r61307_panel_funcs = {
227 	.prepare = renesas_r61307_prepare,
228 	.enable = renesas_r61307_enable,
229 	.disable = renesas_r61307_disable,
230 	.unprepare = renesas_r61307_unprepare,
231 	.get_modes = renesas_r61307_get_modes,
232 };
233 
234 static int renesas_r61307_probe(struct mipi_dsi_device *dsi)
235 {
236 	struct device *dev = &dsi->dev;
237 	struct renesas_r61307 *priv;
238 	int ret;
239 
240 	priv = devm_drm_panel_alloc(dev, struct renesas_r61307, panel,
241 				    &renesas_r61307_panel_funcs,
242 				    DRM_MODE_CONNECTOR_DSI);
243 	if (IS_ERR(priv))
244 		return PTR_ERR(priv);
245 
246 	priv->vcc_supply = devm_regulator_get(dev, "vcc");
247 	if (IS_ERR(priv->vcc_supply))
248 		return dev_err_probe(dev, PTR_ERR(priv->vcc_supply),
249 				     "Failed to get vcc-supply\n");
250 
251 	priv->iovcc_supply = devm_regulator_get(dev, "iovcc");
252 	if (IS_ERR(priv->iovcc_supply))
253 		return dev_err_probe(dev, PTR_ERR(priv->iovcc_supply),
254 				     "Failed to get iovcc-supply\n");
255 
256 	priv->reset_gpio = devm_gpiod_get_optional(dev, "reset",
257 						   GPIOD_OUT_HIGH);
258 	if (IS_ERR(priv->reset_gpio))
259 		return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),
260 				     "Failed to get reset gpios\n");
261 
262 	if (device_property_read_bool(dev, "renesas,inversion"))
263 		priv->inversion = true;
264 
265 	if (device_property_read_bool(dev, "renesas,contrast"))
266 		priv->dig_cont_adj = true;
267 
268 	priv->gamma = 0;
269 	device_property_read_u32(dev, "renesas,gamma", &priv->gamma);
270 
271 	priv->dsi = dsi;
272 	mipi_dsi_set_drvdata(dsi, priv);
273 
274 	dsi->lanes = 4;
275 	dsi->format = MIPI_DSI_FMT_RGB888;
276 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
277 			  MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
278 
279 	ret = drm_panel_of_backlight(&priv->panel);
280 	if (ret)
281 		return dev_err_probe(dev, ret, "Failed to get backlight\n");
282 
283 	drm_panel_add(&priv->panel);
284 
285 	ret = mipi_dsi_attach(dsi);
286 	if (ret) {
287 		drm_panel_remove(&priv->panel);
288 		return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
289 	}
290 
291 	return 0;
292 }
293 
294 static void renesas_r61307_remove(struct mipi_dsi_device *dsi)
295 {
296 	struct renesas_r61307 *priv = mipi_dsi_get_drvdata(dsi);
297 	int ret;
298 
299 	ret = mipi_dsi_detach(dsi);
300 	if (ret)
301 		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
302 
303 	drm_panel_remove(&priv->panel);
304 }
305 
306 static const struct of_device_id renesas_r61307_of_match[] = {
307 	{ .compatible = "hit,tx13d100vm0eaa" },
308 	{ .compatible = "koe,tx13d100vm0eaa" },
309 	{ /* sentinel */ }
310 };
311 MODULE_DEVICE_TABLE(of, renesas_r61307_of_match);
312 
313 static struct mipi_dsi_driver renesas_r61307_driver = {
314 	.probe = renesas_r61307_probe,
315 	.remove = renesas_r61307_remove,
316 	.driver = {
317 		.name = "panel-renesas-r61307",
318 		.of_match_table = renesas_r61307_of_match,
319 	},
320 };
321 module_mipi_dsi_driver(renesas_r61307_driver);
322 
323 MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
324 MODULE_DESCRIPTION("Renesas R61307-based panel driver");
325 MODULE_LICENSE("GPL");
326