xref: /linux/drivers/gpu/drm/tiny/hx8357d.c (revision 5ea5880764cbb164afb17a62e76ca75dc371409d)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * DRM driver for the HX8357D LCD controller
4  *
5  * Copyright 2018 Broadcom
6  * Copyright 2018 David Lechner <david@lechnology.com>
7  * Copyright 2016 Noralf Trønnes
8  * Copyright (C) 2015 Adafruit Industries
9  * Copyright (C) 2013 Christian Vogelgsang
10  */
11 
12 #include <linux/backlight.h>
13 #include <linux/delay.h>
14 #include <linux/gpio/consumer.h>
15 #include <linux/module.h>
16 #include <linux/property.h>
17 #include <linux/spi/spi.h>
18 
19 #include <drm/clients/drm_client_setup.h>
20 #include <drm/drm_atomic_helper.h>
21 #include <drm/drm_drv.h>
22 #include <drm/drm_fbdev_dma.h>
23 #include <drm/drm_gem_atomic_helper.h>
24 #include <drm/drm_gem_dma_helper.h>
25 #include <drm/drm_managed.h>
26 #include <drm/drm_mipi_dbi.h>
27 #include <drm/drm_modeset_helper.h>
28 #include <drm/drm_print.h>
29 #include <video/mipi_display.h>
30 
31 #define HX8357D_SETOSC 0xb0
32 #define HX8357D_SETPOWER 0xb1
33 #define HX8357D_SETRGB 0xb3
34 #define HX8357D_SETCYC 0xb3
35 #define HX8357D_SETCOM 0xb6
36 #define HX8357D_SETEXTC 0xb9
37 #define HX8357D_SETSTBA 0xc0
38 #define HX8357D_SETPANEL 0xcc
39 #define HX8357D_SETGAMMA 0xe0
40 
41 #define HX8357D_MADCTL_MY  0x80
42 #define HX8357D_MADCTL_MX  0x40
43 #define HX8357D_MADCTL_MV  0x20
44 #define HX8357D_MADCTL_ML  0x10
45 #define HX8357D_MADCTL_RGB 0x00
46 #define HX8357D_MADCTL_BGR 0x08
47 #define HX8357D_MADCTL_MH  0x04
48 
49 struct hx8357d_device {
50 	struct mipi_dbi_dev dbidev;
51 
52 	struct drm_plane plane;
53 	struct drm_crtc crtc;
54 	struct drm_encoder encoder;
55 	struct drm_connector connector;
56 };
57 
58 static struct hx8357d_device *to_hx8357d_device(struct drm_device *dev)
59 {
60 	return container_of(drm_to_mipi_dbi_dev(dev), struct hx8357d_device, dbidev);
61 }
62 
63 static const u32 hx8357d_plane_formats[] = {
64 	DRM_MIPI_DBI_PLANE_FORMATS,
65 };
66 
67 static const u64 hx8357d_plane_format_modifiers[] = {
68 	DRM_MIPI_DBI_PLANE_FORMAT_MODIFIERS,
69 };
70 
71 static const struct drm_plane_helper_funcs hx8357d_plane_helper_funcs = {
72 	DRM_MIPI_DBI_PLANE_HELPER_FUNCS,
73 };
74 
75 static const struct drm_plane_funcs hx8357d_plane_funcs = {
76 	DRM_MIPI_DBI_PLANE_FUNCS,
77 	.destroy = drm_plane_cleanup,
78 };
79 
80 static void hx8357d_crtc_helper_atomic_enable(struct drm_crtc *crtc,
81 					      struct drm_atomic_state *state)
82 {
83 	struct drm_device *drm = crtc->dev;
84 	struct hx8357d_device *hx8357d = to_hx8357d_device(drm);
85 	struct mipi_dbi_dev *dbidev = &hx8357d->dbidev;
86 	struct mipi_dbi *dbi = &dbidev->dbi;
87 	u8 addr_mode;
88 	int ret, idx;
89 
90 	if (!drm_dev_enter(drm, &idx))
91 		return;
92 
93 	DRM_DEBUG_KMS("\n");
94 
95 	ret = mipi_dbi_poweron_conditional_reset(dbidev);
96 	if (ret < 0)
97 		goto out_exit;
98 	if (ret == 1)
99 		goto out_enable;
100 
101 	/* setextc */
102 	mipi_dbi_command(dbi, HX8357D_SETEXTC, 0xFF, 0x83, 0x57);
103 	msleep(150);
104 
105 	/* setRGB which also enables SDO */
106 	mipi_dbi_command(dbi, HX8357D_SETRGB, 0x00, 0x00, 0x06, 0x06);
107 
108 	/* -1.52V */
109 	mipi_dbi_command(dbi, HX8357D_SETCOM, 0x25);
110 
111 	/* Normal mode 70Hz, Idle mode 55 Hz */
112 	mipi_dbi_command(dbi, HX8357D_SETOSC, 0x68);
113 
114 	/* Set Panel - BGR, Gate direction swapped */
115 	mipi_dbi_command(dbi, HX8357D_SETPANEL, 0x05);
116 
117 	mipi_dbi_command(dbi, HX8357D_SETPOWER,
118 			 0x00,  /* Not deep standby */
119 			 0x15,  /* BT */
120 			 0x1C,  /* VSPR */
121 			 0x1C,  /* VSNR */
122 			 0x83,  /* AP */
123 			 0xAA);  /* FS */
124 
125 	mipi_dbi_command(dbi, HX8357D_SETSTBA,
126 			 0x50,  /* OPON normal */
127 			 0x50,  /* OPON idle */
128 			 0x01,  /* STBA */
129 			 0x3C,  /* STBA */
130 			 0x1E,  /* STBA */
131 			 0x08);  /* GEN */
132 
133 	mipi_dbi_command(dbi, HX8357D_SETCYC,
134 			 0x02,  /* NW 0x02 */
135 			 0x40,  /* RTN */
136 			 0x00,  /* DIV */
137 			 0x2A,  /* DUM */
138 			 0x2A,  /* DUM */
139 			 0x0D,  /* GDON */
140 			 0x78);  /* GDOFF */
141 
142 	mipi_dbi_command(dbi, HX8357D_SETGAMMA,
143 			 0x02,
144 			 0x0A,
145 			 0x11,
146 			 0x1d,
147 			 0x23,
148 			 0x35,
149 			 0x41,
150 			 0x4b,
151 			 0x4b,
152 			 0x42,
153 			 0x3A,
154 			 0x27,
155 			 0x1B,
156 			 0x08,
157 			 0x09,
158 			 0x03,
159 			 0x02,
160 			 0x0A,
161 			 0x11,
162 			 0x1d,
163 			 0x23,
164 			 0x35,
165 			 0x41,
166 			 0x4b,
167 			 0x4b,
168 			 0x42,
169 			 0x3A,
170 			 0x27,
171 			 0x1B,
172 			 0x08,
173 			 0x09,
174 			 0x03,
175 			 0x00,
176 			 0x01);
177 
178 	/* 16 bit */
179 	mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT,
180 			 MIPI_DCS_PIXEL_FMT_16BIT);
181 
182 	/* TE off */
183 	mipi_dbi_command(dbi, MIPI_DCS_SET_TEAR_ON, 0x00);
184 
185 	/* tear line */
186 	mipi_dbi_command(dbi, MIPI_DCS_SET_TEAR_SCANLINE, 0x00, 0x02);
187 
188 	/* Exit Sleep */
189 	mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
190 	msleep(150);
191 
192 	/* display on */
193 	mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
194 	usleep_range(5000, 7000);
195 
196 out_enable:
197 	switch (dbidev->rotation) {
198 	default:
199 		addr_mode = HX8357D_MADCTL_MX | HX8357D_MADCTL_MY;
200 		break;
201 	case 90:
202 		addr_mode = HX8357D_MADCTL_MV | HX8357D_MADCTL_MY;
203 		break;
204 	case 180:
205 		addr_mode = 0;
206 		break;
207 	case 270:
208 		addr_mode = HX8357D_MADCTL_MV | HX8357D_MADCTL_MX;
209 		break;
210 	}
211 	mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
212 
213 	backlight_enable(dbidev->backlight);
214 out_exit:
215 	drm_dev_exit(idx);
216 }
217 
218 static const struct drm_crtc_helper_funcs hx8357d_crtc_helper_funcs = {
219 	DRM_MIPI_DBI_CRTC_HELPER_FUNCS,
220 	.atomic_enable = hx8357d_crtc_helper_atomic_enable,
221 };
222 
223 static const struct drm_crtc_funcs hx8357d_crtc_funcs = {
224 	DRM_MIPI_DBI_CRTC_FUNCS,
225 	.destroy = drm_crtc_cleanup,
226 };
227 
228 static const struct drm_encoder_funcs hx8357d_encoder_funcs = {
229 	.destroy = drm_encoder_cleanup,
230 };
231 
232 static const struct drm_connector_helper_funcs hx8357d_connector_helper_funcs = {
233 	DRM_MIPI_DBI_CONNECTOR_HELPER_FUNCS,
234 };
235 
236 static const struct drm_connector_funcs hx8357d_connector_funcs = {
237 	DRM_MIPI_DBI_CONNECTOR_FUNCS,
238 	.destroy = drm_connector_cleanup,
239 };
240 
241 static const struct drm_mode_config_helper_funcs hx8357d_mode_config_helper_funcs = {
242 	DRM_MIPI_DBI_MODE_CONFIG_HELPER_FUNCS,
243 };
244 
245 static const struct drm_mode_config_funcs hx8357d_mode_config_funcs = {
246 	DRM_MIPI_DBI_MODE_CONFIG_FUNCS,
247 };
248 
249 static const struct drm_display_mode yx350hv15_mode = {
250 	DRM_SIMPLE_MODE(320, 480, 60, 75),
251 };
252 
253 DEFINE_DRM_GEM_DMA_FOPS(hx8357d_fops);
254 
255 static const struct drm_driver hx8357d_driver = {
256 	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
257 	.fops			= &hx8357d_fops,
258 	DRM_GEM_DMA_DRIVER_OPS_VMAP,
259 	DRM_FBDEV_DMA_DRIVER_OPS,
260 	.debugfs_init		= mipi_dbi_debugfs_init,
261 	.name			= "hx8357d",
262 	.desc			= "HX8357D",
263 	.major			= 1,
264 	.minor			= 0,
265 };
266 
267 static const struct of_device_id hx8357d_of_match[] = {
268 	{ .compatible = "adafruit,yx350hv15" },
269 	{ }
270 };
271 MODULE_DEVICE_TABLE(of, hx8357d_of_match);
272 
273 static const struct spi_device_id hx8357d_id[] = {
274 	{ "yx350hv15", 0 },
275 	{ }
276 };
277 MODULE_DEVICE_TABLE(spi, hx8357d_id);
278 
279 static int hx8357d_probe(struct spi_device *spi)
280 {
281 	struct device *dev = &spi->dev;
282 	struct hx8357d_device *hx8357d;
283 	struct mipi_dbi_dev *dbidev;
284 	struct drm_device *drm;
285 	struct gpio_desc *dc;
286 	struct drm_plane *plane;
287 	struct drm_crtc *crtc;
288 	struct drm_encoder *encoder;
289 	struct drm_connector *connector;
290 	u32 rotation = 0;
291 	int ret;
292 
293 	hx8357d = devm_drm_dev_alloc(dev, &hx8357d_driver, struct hx8357d_device, dbidev.drm);
294 	if (IS_ERR(hx8357d))
295 		return PTR_ERR(hx8357d);
296 	dbidev = &hx8357d->dbidev;
297 	drm = &dbidev->drm;
298 
299 	dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW);
300 	if (IS_ERR(dc))
301 		return dev_err_probe(dev, PTR_ERR(dc), "Failed to get GPIO 'dc'\n");
302 
303 	dbidev->backlight = devm_of_find_backlight(dev);
304 	if (IS_ERR(dbidev->backlight))
305 		return PTR_ERR(dbidev->backlight);
306 
307 	device_property_read_u32(dev, "rotation", &rotation);
308 
309 	ret = mipi_dbi_spi_init(spi, &dbidev->dbi, dc);
310 	if (ret)
311 		return ret;
312 
313 	ret = drm_mipi_dbi_dev_init(dbidev, &yx350hv15_mode, hx8357d_plane_formats[0],
314 				    rotation, 0);
315 	if (ret)
316 		return ret;
317 
318 	ret = drmm_mode_config_init(drm);
319 	if (ret)
320 		return ret;
321 
322 	drm->mode_config.min_width = dbidev->mode.hdisplay;
323 	drm->mode_config.max_width = dbidev->mode.hdisplay;
324 	drm->mode_config.min_height = dbidev->mode.vdisplay;
325 	drm->mode_config.max_height = dbidev->mode.vdisplay;
326 	drm->mode_config.funcs = &hx8357d_mode_config_funcs;
327 	drm->mode_config.preferred_depth = 16;
328 	drm->mode_config.helper_private = &hx8357d_mode_config_helper_funcs;
329 
330 	plane = &hx8357d->plane;
331 	ret = drm_universal_plane_init(drm, plane, 0, &hx8357d_plane_funcs,
332 				       hx8357d_plane_formats, ARRAY_SIZE(hx8357d_plane_formats),
333 				       hx8357d_plane_format_modifiers,
334 				       DRM_PLANE_TYPE_PRIMARY, NULL);
335 	if (ret)
336 		return ret;
337 	drm_plane_helper_add(plane, &hx8357d_plane_helper_funcs);
338 	drm_plane_enable_fb_damage_clips(plane);
339 
340 	crtc = &hx8357d->crtc;
341 	ret = drm_crtc_init_with_planes(drm, crtc, plane, NULL, &hx8357d_crtc_funcs, NULL);
342 	if (ret)
343 		return ret;
344 	drm_crtc_helper_add(crtc, &hx8357d_crtc_helper_funcs);
345 
346 	encoder = &hx8357d->encoder;
347 	ret = drm_encoder_init(drm, encoder, &hx8357d_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
348 	if (ret)
349 		return ret;
350 	encoder->possible_crtcs = drm_crtc_mask(crtc);
351 
352 	connector = &hx8357d->connector;
353 	ret = drm_connector_init(drm, connector, &hx8357d_connector_funcs,
354 				 DRM_MODE_CONNECTOR_SPI);
355 	if (ret)
356 		return ret;
357 	drm_connector_helper_add(connector, &hx8357d_connector_helper_funcs);
358 
359 	ret = drm_connector_attach_encoder(connector, encoder);
360 	if (ret)
361 		return ret;
362 
363 	drm_mode_config_reset(drm);
364 
365 	ret = drm_dev_register(drm, 0);
366 	if (ret)
367 		return ret;
368 
369 	spi_set_drvdata(spi, drm);
370 
371 	drm_client_setup(drm, NULL);
372 
373 	return 0;
374 }
375 
376 static void hx8357d_remove(struct spi_device *spi)
377 {
378 	struct drm_device *drm = spi_get_drvdata(spi);
379 
380 	drm_dev_unplug(drm);
381 	drm_atomic_helper_shutdown(drm);
382 }
383 
384 static void hx8357d_shutdown(struct spi_device *spi)
385 {
386 	drm_atomic_helper_shutdown(spi_get_drvdata(spi));
387 }
388 
389 static struct spi_driver hx8357d_spi_driver = {
390 	.driver = {
391 		.name = "hx8357d",
392 		.of_match_table = hx8357d_of_match,
393 	},
394 	.id_table = hx8357d_id,
395 	.probe = hx8357d_probe,
396 	.remove = hx8357d_remove,
397 	.shutdown = hx8357d_shutdown,
398 };
399 module_spi_driver(hx8357d_spi_driver);
400 
401 MODULE_DESCRIPTION("HX8357D DRM driver");
402 MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
403 MODULE_LICENSE("GPL");
404