Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
fork
Configure Feed
Select the types of activity you want to include in your feed.
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
49struct 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
58static 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
63static const u32 hx8357d_plane_formats[] = {
64 DRM_MIPI_DBI_PLANE_FORMATS,
65};
66
67static const u64 hx8357d_plane_format_modifiers[] = {
68 DRM_MIPI_DBI_PLANE_FORMAT_MODIFIERS,
69};
70
71static const struct drm_plane_helper_funcs hx8357d_plane_helper_funcs = {
72 DRM_MIPI_DBI_PLANE_HELPER_FUNCS,
73};
74
75static const struct drm_plane_funcs hx8357d_plane_funcs = {
76 DRM_MIPI_DBI_PLANE_FUNCS,
77 .destroy = drm_plane_cleanup,
78};
79
80static 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
196out_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);
214out_exit:
215 drm_dev_exit(idx);
216}
217
218static 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
223static const struct drm_crtc_funcs hx8357d_crtc_funcs = {
224 DRM_MIPI_DBI_CRTC_FUNCS,
225 .destroy = drm_crtc_cleanup,
226};
227
228static const struct drm_encoder_funcs hx8357d_encoder_funcs = {
229 .destroy = drm_encoder_cleanup,
230};
231
232static const struct drm_connector_helper_funcs hx8357d_connector_helper_funcs = {
233 DRM_MIPI_DBI_CONNECTOR_HELPER_FUNCS,
234};
235
236static const struct drm_connector_funcs hx8357d_connector_funcs = {
237 DRM_MIPI_DBI_CONNECTOR_FUNCS,
238 .destroy = drm_connector_cleanup,
239};
240
241static const struct drm_mode_config_helper_funcs hx8357d_mode_config_helper_funcs = {
242 DRM_MIPI_DBI_MODE_CONFIG_HELPER_FUNCS,
243};
244
245static const struct drm_mode_config_funcs hx8357d_mode_config_funcs = {
246 DRM_MIPI_DBI_MODE_CONFIG_FUNCS,
247};
248
249static const struct drm_display_mode yx350hv15_mode = {
250 DRM_SIMPLE_MODE(320, 480, 60, 75),
251};
252
253DEFINE_DRM_GEM_DMA_FOPS(hx8357d_fops);
254
255static 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
267static const struct of_device_id hx8357d_of_match[] = {
268 { .compatible = "adafruit,yx350hv15" },
269 { }
270};
271MODULE_DEVICE_TABLE(of, hx8357d_of_match);
272
273static const struct spi_device_id hx8357d_id[] = {
274 { "yx350hv15", 0 },
275 { }
276};
277MODULE_DEVICE_TABLE(spi, hx8357d_id);
278
279static 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
376static 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
384static void hx8357d_shutdown(struct spi_device *spi)
385{
386 drm_atomic_helper_shutdown(spi_get_drvdata(spi));
387}
388
389static 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};
399module_spi_driver(hx8357d_spi_driver);
400
401MODULE_DESCRIPTION("HX8357D DRM driver");
402MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
403MODULE_LICENSE("GPL");