Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
b57e8b76 DL |
2 | /* |
3 | * DRM driver for Ilitek ILI9225 panels | |
4 | * | |
5 | * Copyright 2017 David Lechner <david@lechnology.com> | |
6 | * | |
7 | * Some code copied from mipi-dbi.c | |
8 | * Copyright 2016 Noralf Trønnes | |
b57e8b76 DL |
9 | */ |
10 | ||
11 | #include <linux/delay.h> | |
12 | #include <linux/dma-buf.h> | |
13 | #include <linux/gpio/consumer.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/property.h> | |
16 | #include <linux/spi/spi.h> | |
17 | #include <video/mipi_display.h> | |
18 | ||
d0a51634 | 19 | #include <drm/drm_atomic_helper.h> |
af741381 | 20 | #include <drm/drm_damage_helper.h> |
84056e9b | 21 | #include <drm/drm_drv.h> |
3db8d37d | 22 | #include <drm/drm_fb_cma_helper.h> |
3eba3922 | 23 | #include <drm/drm_fb_helper.h> |
84056e9b | 24 | #include <drm/drm_fourcc.h> |
3db8d37d | 25 | #include <drm/drm_gem_cma_helper.h> |
b57e8b76 | 26 | #include <drm/drm_gem_framebuffer_helper.h> |
f5ad671b | 27 | #include <drm/drm_managed.h> |
174102f4 | 28 | #include <drm/drm_mipi_dbi.h> |
b051b345 | 29 | #include <drm/drm_rect.h> |
b57e8b76 DL |
30 | |
31 | #define ILI9225_DRIVER_READ_CODE 0x00 | |
32 | #define ILI9225_DRIVER_OUTPUT_CONTROL 0x01 | |
33 | #define ILI9225_LCD_AC_DRIVING_CONTROL 0x02 | |
34 | #define ILI9225_ENTRY_MODE 0x03 | |
35 | #define ILI9225_DISPLAY_CONTROL_1 0x07 | |
36 | #define ILI9225_BLANK_PERIOD_CONTROL_1 0x08 | |
37 | #define ILI9225_FRAME_CYCLE_CONTROL 0x0b | |
38 | #define ILI9225_INTERFACE_CONTROL 0x0c | |
39 | #define ILI9225_OSCILLATION_CONTROL 0x0f | |
40 | #define ILI9225_POWER_CONTROL_1 0x10 | |
41 | #define ILI9225_POWER_CONTROL_2 0x11 | |
42 | #define ILI9225_POWER_CONTROL_3 0x12 | |
43 | #define ILI9225_POWER_CONTROL_4 0x13 | |
44 | #define ILI9225_POWER_CONTROL_5 0x14 | |
45 | #define ILI9225_VCI_RECYCLING 0x15 | |
46 | #define ILI9225_RAM_ADDRESS_SET_1 0x20 | |
47 | #define ILI9225_RAM_ADDRESS_SET_2 0x21 | |
48 | #define ILI9225_WRITE_DATA_TO_GRAM 0x22 | |
49 | #define ILI9225_SOFTWARE_RESET 0x28 | |
50 | #define ILI9225_GATE_SCAN_CONTROL 0x30 | |
51 | #define ILI9225_VERTICAL_SCROLL_1 0x31 | |
52 | #define ILI9225_VERTICAL_SCROLL_2 0x32 | |
53 | #define ILI9225_VERTICAL_SCROLL_3 0x33 | |
54 | #define ILI9225_PARTIAL_DRIVING_POS_1 0x34 | |
55 | #define ILI9225_PARTIAL_DRIVING_POS_2 0x35 | |
56 | #define ILI9225_HORIZ_WINDOW_ADDR_1 0x36 | |
57 | #define ILI9225_HORIZ_WINDOW_ADDR_2 0x37 | |
58 | #define ILI9225_VERT_WINDOW_ADDR_1 0x38 | |
59 | #define ILI9225_VERT_WINDOW_ADDR_2 0x39 | |
60 | #define ILI9225_GAMMA_CONTROL_1 0x50 | |
61 | #define ILI9225_GAMMA_CONTROL_2 0x51 | |
62 | #define ILI9225_GAMMA_CONTROL_3 0x52 | |
63 | #define ILI9225_GAMMA_CONTROL_4 0x53 | |
64 | #define ILI9225_GAMMA_CONTROL_5 0x54 | |
65 | #define ILI9225_GAMMA_CONTROL_6 0x55 | |
66 | #define ILI9225_GAMMA_CONTROL_7 0x56 | |
67 | #define ILI9225_GAMMA_CONTROL_8 0x57 | |
68 | #define ILI9225_GAMMA_CONTROL_9 0x58 | |
69 | #define ILI9225_GAMMA_CONTROL_10 0x59 | |
70 | ||
36b50572 | 71 | static inline int ili9225_command(struct mipi_dbi *dbi, u8 cmd, u16 data) |
b57e8b76 DL |
72 | { |
73 | u8 par[2] = { data >> 8, data & 0xff }; | |
74 | ||
36b50572 | 75 | return mipi_dbi_command_buf(dbi, cmd, par, 2); |
b57e8b76 DL |
76 | } |
77 | ||
af741381 | 78 | static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) |
b57e8b76 DL |
79 | { |
80 | struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); | |
84137b86 | 81 | struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); |
af741381 NT |
82 | unsigned int height = rect->y2 - rect->y1; |
83 | unsigned int width = rect->x2 - rect->x1; | |
84137b86 | 84 | struct mipi_dbi *dbi = &dbidev->dbi; |
36b50572 | 85 | bool swap = dbi->swap_bytes; |
b57e8b76 DL |
86 | u16 x_start, y_start; |
87 | u16 x1, x2, y1, y2; | |
9d5645ad | 88 | int idx, ret = 0; |
b57e8b76 DL |
89 | bool full; |
90 | void *tr; | |
91 | ||
9d5645ad NT |
92 | if (!drm_dev_enter(fb->dev, &idx)) |
93 | return; | |
94 | ||
af741381 | 95 | full = width == fb->width && height == fb->height; |
b57e8b76 | 96 | |
b051b345 | 97 | DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); |
b57e8b76 | 98 | |
36b50572 | 99 | if (!dbi->dc || !full || swap || |
b57e8b76 | 100 | fb->format->format == DRM_FORMAT_XRGB8888) { |
440961d2 NT |
101 | tr = dbidev->tx_buf; |
102 | ret = mipi_dbi_buf_copy(dbidev->tx_buf, fb, rect, swap); | |
b57e8b76 | 103 | if (ret) |
af741381 | 104 | goto err_msg; |
b57e8b76 DL |
105 | } else { |
106 | tr = cma_obj->vaddr; | |
107 | } | |
108 | ||
440961d2 | 109 | switch (dbidev->rotation) { |
b57e8b76 | 110 | default: |
b051b345 NT |
111 | x1 = rect->x1; |
112 | x2 = rect->x2 - 1; | |
113 | y1 = rect->y1; | |
114 | y2 = rect->y2 - 1; | |
b57e8b76 DL |
115 | x_start = x1; |
116 | y_start = y1; | |
117 | break; | |
118 | case 90: | |
b051b345 NT |
119 | x1 = rect->y1; |
120 | x2 = rect->y2 - 1; | |
121 | y1 = fb->width - rect->x2; | |
122 | y2 = fb->width - rect->x1 - 1; | |
b57e8b76 DL |
123 | x_start = x1; |
124 | y_start = y2; | |
125 | break; | |
126 | case 180: | |
b051b345 NT |
127 | x1 = fb->width - rect->x2; |
128 | x2 = fb->width - rect->x1 - 1; | |
129 | y1 = fb->height - rect->y2; | |
130 | y2 = fb->height - rect->y1 - 1; | |
b57e8b76 DL |
131 | x_start = x2; |
132 | y_start = y2; | |
133 | break; | |
134 | case 270: | |
b051b345 NT |
135 | x1 = fb->height - rect->y2; |
136 | x2 = fb->height - rect->y1 - 1; | |
137 | y1 = rect->x1; | |
138 | y2 = rect->x2 - 1; | |
b57e8b76 DL |
139 | x_start = x2; |
140 | y_start = y1; | |
141 | break; | |
142 | } | |
143 | ||
36b50572 NT |
144 | ili9225_command(dbi, ILI9225_HORIZ_WINDOW_ADDR_1, x2); |
145 | ili9225_command(dbi, ILI9225_HORIZ_WINDOW_ADDR_2, x1); | |
146 | ili9225_command(dbi, ILI9225_VERT_WINDOW_ADDR_1, y2); | |
147 | ili9225_command(dbi, ILI9225_VERT_WINDOW_ADDR_2, y1); | |
b57e8b76 | 148 | |
36b50572 NT |
149 | ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_1, x_start); |
150 | ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_2, y_start); | |
b57e8b76 | 151 | |
36b50572 | 152 | ret = mipi_dbi_command_buf(dbi, ILI9225_WRITE_DATA_TO_GRAM, tr, |
af741381 NT |
153 | width * height * 2); |
154 | err_msg: | |
155 | if (ret) | |
156 | dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret); | |
9d5645ad NT |
157 | |
158 | drm_dev_exit(idx); | |
b57e8b76 DL |
159 | } |
160 | ||
af741381 NT |
161 | static void ili9225_pipe_update(struct drm_simple_display_pipe *pipe, |
162 | struct drm_plane_state *old_state) | |
163 | { | |
164 | struct drm_plane_state *state = pipe->plane.state; | |
af741381 NT |
165 | struct drm_rect rect; |
166 | ||
7e06886b DV |
167 | if (!pipe->crtc.state->active) |
168 | return; | |
169 | ||
af741381 NT |
170 | if (drm_atomic_helper_damage_merged(old_state, state, &rect)) |
171 | ili9225_fb_dirty(state->fb, &rect); | |
af741381 | 172 | } |
b57e8b76 DL |
173 | |
174 | static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe, | |
0c9c7fd0 VS |
175 | struct drm_crtc_state *crtc_state, |
176 | struct drm_plane_state *plane_state) | |
b57e8b76 | 177 | { |
84137b86 | 178 | struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); |
af741381 | 179 | struct drm_framebuffer *fb = plane_state->fb; |
4f834798 | 180 | struct device *dev = pipe->crtc.dev->dev; |
84137b86 | 181 | struct mipi_dbi *dbi = &dbidev->dbi; |
af741381 NT |
182 | struct drm_rect rect = { |
183 | .x1 = 0, | |
184 | .x2 = fb->width, | |
185 | .y1 = 0, | |
186 | .y2 = fb->height, | |
187 | }; | |
9d5645ad | 188 | int ret, idx; |
b57e8b76 DL |
189 | u8 am_id; |
190 | ||
9d5645ad NT |
191 | if (!drm_dev_enter(pipe->crtc.dev, &idx)) |
192 | return; | |
193 | ||
b57e8b76 DL |
194 | DRM_DEBUG_KMS("\n"); |
195 | ||
36b50572 | 196 | mipi_dbi_hw_reset(dbi); |
b57e8b76 DL |
197 | |
198 | /* | |
199 | * There don't seem to be two example init sequences that match, so | |
200 | * using the one from the popular Arduino library for this display. | |
201 | * https://github.com/Nkawu/TFT_22_ILI9225/blob/master/src/TFT_22_ILI9225.cpp | |
202 | */ | |
203 | ||
36b50572 | 204 | ret = ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0000); |
b57e8b76 DL |
205 | if (ret) { |
206 | DRM_DEV_ERROR(dev, "Error sending command %d\n", ret); | |
9d5645ad | 207 | goto out_exit; |
b57e8b76 | 208 | } |
36b50572 NT |
209 | ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0000); |
210 | ili9225_command(dbi, ILI9225_POWER_CONTROL_3, 0x0000); | |
211 | ili9225_command(dbi, ILI9225_POWER_CONTROL_4, 0x0000); | |
212 | ili9225_command(dbi, ILI9225_POWER_CONTROL_5, 0x0000); | |
b57e8b76 DL |
213 | |
214 | msleep(40); | |
215 | ||
36b50572 NT |
216 | ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0018); |
217 | ili9225_command(dbi, ILI9225_POWER_CONTROL_3, 0x6121); | |
218 | ili9225_command(dbi, ILI9225_POWER_CONTROL_4, 0x006f); | |
219 | ili9225_command(dbi, ILI9225_POWER_CONTROL_5, 0x495f); | |
220 | ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0800); | |
b57e8b76 DL |
221 | |
222 | msleep(10); | |
223 | ||
36b50572 | 224 | ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x103b); |
b57e8b76 DL |
225 | |
226 | msleep(50); | |
227 | ||
440961d2 | 228 | switch (dbidev->rotation) { |
b57e8b76 DL |
229 | default: |
230 | am_id = 0x30; | |
231 | break; | |
232 | case 90: | |
233 | am_id = 0x18; | |
234 | break; | |
235 | case 180: | |
236 | am_id = 0x00; | |
237 | break; | |
238 | case 270: | |
239 | am_id = 0x28; | |
240 | break; | |
241 | } | |
36b50572 NT |
242 | ili9225_command(dbi, ILI9225_DRIVER_OUTPUT_CONTROL, 0x011c); |
243 | ili9225_command(dbi, ILI9225_LCD_AC_DRIVING_CONTROL, 0x0100); | |
244 | ili9225_command(dbi, ILI9225_ENTRY_MODE, 0x1000 | am_id); | |
245 | ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0000); | |
246 | ili9225_command(dbi, ILI9225_BLANK_PERIOD_CONTROL_1, 0x0808); | |
247 | ili9225_command(dbi, ILI9225_FRAME_CYCLE_CONTROL, 0x1100); | |
248 | ili9225_command(dbi, ILI9225_INTERFACE_CONTROL, 0x0000); | |
249 | ili9225_command(dbi, ILI9225_OSCILLATION_CONTROL, 0x0d01); | |
250 | ili9225_command(dbi, ILI9225_VCI_RECYCLING, 0x0020); | |
251 | ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_1, 0x0000); | |
252 | ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_2, 0x0000); | |
253 | ||
254 | ili9225_command(dbi, ILI9225_GATE_SCAN_CONTROL, 0x0000); | |
255 | ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_1, 0x00db); | |
256 | ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_2, 0x0000); | |
257 | ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_3, 0x0000); | |
258 | ili9225_command(dbi, ILI9225_PARTIAL_DRIVING_POS_1, 0x00db); | |
259 | ili9225_command(dbi, ILI9225_PARTIAL_DRIVING_POS_2, 0x0000); | |
260 | ||
261 | ili9225_command(dbi, ILI9225_GAMMA_CONTROL_1, 0x0000); | |
262 | ili9225_command(dbi, ILI9225_GAMMA_CONTROL_2, 0x0808); | |
263 | ili9225_command(dbi, ILI9225_GAMMA_CONTROL_3, 0x080a); | |
264 | ili9225_command(dbi, ILI9225_GAMMA_CONTROL_4, 0x000a); | |
265 | ili9225_command(dbi, ILI9225_GAMMA_CONTROL_5, 0x0a08); | |
266 | ili9225_command(dbi, ILI9225_GAMMA_CONTROL_6, 0x0808); | |
267 | ili9225_command(dbi, ILI9225_GAMMA_CONTROL_7, 0x0000); | |
268 | ili9225_command(dbi, ILI9225_GAMMA_CONTROL_8, 0x0a00); | |
269 | ili9225_command(dbi, ILI9225_GAMMA_CONTROL_9, 0x0710); | |
270 | ili9225_command(dbi, ILI9225_GAMMA_CONTROL_10, 0x0710); | |
271 | ||
272 | ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0012); | |
b57e8b76 DL |
273 | |
274 | msleep(50); | |
275 | ||
36b50572 | 276 | ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x1017); |
b57e8b76 | 277 | |
af741381 | 278 | ili9225_fb_dirty(fb, &rect); |
9d5645ad NT |
279 | out_exit: |
280 | drm_dev_exit(idx); | |
b57e8b76 DL |
281 | } |
282 | ||
283 | static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe) | |
284 | { | |
84137b86 NT |
285 | struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); |
286 | struct mipi_dbi *dbi = &dbidev->dbi; | |
b57e8b76 DL |
287 | |
288 | DRM_DEBUG_KMS("\n"); | |
289 | ||
9d5645ad NT |
290 | /* |
291 | * This callback is not protected by drm_dev_enter/exit since we want to | |
292 | * turn off the display on regular driver unload. It's highly unlikely | |
293 | * that the underlying SPI controller is gone should this be called after | |
294 | * unplug. | |
295 | */ | |
296 | ||
36b50572 | 297 | ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0000); |
b57e8b76 | 298 | msleep(50); |
36b50572 | 299 | ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0007); |
b57e8b76 | 300 | msleep(50); |
36b50572 | 301 | ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0a02); |
b57e8b76 DL |
302 | } |
303 | ||
36b50572 | 304 | static int ili9225_dbi_command(struct mipi_dbi *dbi, u8 *cmd, u8 *par, |
b57e8b76 DL |
305 | size_t num) |
306 | { | |
36b50572 | 307 | struct spi_device *spi = dbi->spi; |
b57e8b76 DL |
308 | unsigned int bpw = 8; |
309 | u32 speed_hz; | |
310 | int ret; | |
311 | ||
36b50572 | 312 | gpiod_set_value_cansleep(dbi->dc, 0); |
b57e8b76 | 313 | speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1); |
d23d4d4d | 314 | ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1); |
b57e8b76 DL |
315 | if (ret || !num) |
316 | return ret; | |
317 | ||
36b50572 | 318 | if (*cmd == ILI9225_WRITE_DATA_TO_GRAM && !dbi->swap_bytes) |
b57e8b76 DL |
319 | bpw = 16; |
320 | ||
36b50572 | 321 | gpiod_set_value_cansleep(dbi->dc, 1); |
b57e8b76 DL |
322 | speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num); |
323 | ||
d23d4d4d | 324 | return mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num); |
b57e8b76 DL |
325 | } |
326 | ||
b57e8b76 DL |
327 | static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = { |
328 | .enable = ili9225_pipe_enable, | |
329 | .disable = ili9225_pipe_disable, | |
af741381 | 330 | .update = ili9225_pipe_update, |
ccc3b2b3 | 331 | .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, |
b57e8b76 DL |
332 | }; |
333 | ||
334 | static const struct drm_display_mode ili9225_mode = { | |
06db4b8b | 335 | DRM_SIMPLE_MODE(176, 220, 35, 44), |
b57e8b76 DL |
336 | }; |
337 | ||
338 | DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops); | |
339 | ||
70a59dd8 | 340 | static const struct drm_driver ili9225_driver = { |
0424fdaf | 341 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, |
b57e8b76 | 342 | .fops = &ili9225_fops, |
06d66201 | 343 | DRM_GEM_CMA_DRIVER_OPS_VMAP, |
b57e8b76 DL |
344 | .name = "ili9225", |
345 | .desc = "Ilitek ILI9225", | |
346 | .date = "20171106", | |
347 | .major = 1, | |
348 | .minor = 0, | |
349 | }; | |
350 | ||
351 | static const struct of_device_id ili9225_of_match[] = { | |
f3125a54 | 352 | { .compatible = "vot,v220hf01a-t" }, |
b57e8b76 DL |
353 | {}, |
354 | }; | |
355 | MODULE_DEVICE_TABLE(of, ili9225_of_match); | |
356 | ||
357 | static const struct spi_device_id ili9225_id[] = { | |
f3125a54 | 358 | { "v220hf01a-t", 0 }, |
b57e8b76 DL |
359 | { }, |
360 | }; | |
361 | MODULE_DEVICE_TABLE(spi, ili9225_id); | |
362 | ||
363 | static int ili9225_probe(struct spi_device *spi) | |
364 | { | |
365 | struct device *dev = &spi->dev; | |
84137b86 | 366 | struct mipi_dbi_dev *dbidev; |
3eba3922 | 367 | struct drm_device *drm; |
36b50572 | 368 | struct mipi_dbi *dbi; |
b57e8b76 DL |
369 | struct gpio_desc *rs; |
370 | u32 rotation = 0; | |
371 | int ret; | |
372 | ||
4c99859f DV |
373 | dbidev = devm_drm_dev_alloc(dev, &ili9225_driver, |
374 | struct mipi_dbi_dev, drm); | |
375 | if (IS_ERR(dbidev)) | |
376 | return PTR_ERR(dbidev); | |
b57e8b76 | 377 | |
84137b86 | 378 | dbi = &dbidev->dbi; |
440961d2 | 379 | drm = &dbidev->drm; |
3eba3922 | 380 | |
36b50572 NT |
381 | dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); |
382 | if (IS_ERR(dbi->reset)) { | |
b57e8b76 | 383 | DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); |
36b50572 | 384 | return PTR_ERR(dbi->reset); |
b57e8b76 DL |
385 | } |
386 | ||
387 | rs = devm_gpiod_get(dev, "rs", GPIOD_OUT_LOW); | |
388 | if (IS_ERR(rs)) { | |
389 | DRM_DEV_ERROR(dev, "Failed to get gpio 'rs'\n"); | |
390 | return PTR_ERR(rs); | |
391 | } | |
392 | ||
393 | device_property_read_u32(dev, "rotation", &rotation); | |
394 | ||
36b50572 | 395 | ret = mipi_dbi_spi_init(spi, dbi, rs); |
b57e8b76 DL |
396 | if (ret) |
397 | return ret; | |
398 | ||
399 | /* override the command function set in mipi_dbi_spi_init() */ | |
36b50572 | 400 | dbi->command = ili9225_dbi_command; |
b57e8b76 | 401 | |
84137b86 | 402 | ret = mipi_dbi_dev_init(dbidev, &ili9225_pipe_funcs, &ili9225_mode, rotation); |
b57e8b76 DL |
403 | if (ret) |
404 | return ret; | |
405 | ||
3eba3922 NT |
406 | drm_mode_config_reset(drm); |
407 | ||
408 | ret = drm_dev_register(drm, 0); | |
409 | if (ret) | |
410 | return ret; | |
411 | ||
412 | spi_set_drvdata(spi, drm); | |
413 | ||
f47056e8 | 414 | drm_fbdev_generic_setup(drm, 0); |
3eba3922 NT |
415 | |
416 | return 0; | |
417 | } | |
418 | ||
419 | static int ili9225_remove(struct spi_device *spi) | |
420 | { | |
421 | struct drm_device *drm = spi_get_drvdata(spi); | |
422 | ||
423 | drm_dev_unplug(drm); | |
424 | drm_atomic_helper_shutdown(drm); | |
b57e8b76 | 425 | |
3eba3922 | 426 | return 0; |
b57e8b76 DL |
427 | } |
428 | ||
429 | static void ili9225_shutdown(struct spi_device *spi) | |
430 | { | |
d0a51634 | 431 | drm_atomic_helper_shutdown(spi_get_drvdata(spi)); |
b57e8b76 DL |
432 | } |
433 | ||
434 | static struct spi_driver ili9225_spi_driver = { | |
435 | .driver = { | |
436 | .name = "ili9225", | |
437 | .owner = THIS_MODULE, | |
438 | .of_match_table = ili9225_of_match, | |
439 | }, | |
440 | .id_table = ili9225_id, | |
441 | .probe = ili9225_probe, | |
3eba3922 | 442 | .remove = ili9225_remove, |
b57e8b76 DL |
443 | .shutdown = ili9225_shutdown, |
444 | }; | |
445 | module_spi_driver(ili9225_spi_driver); | |
446 | ||
447 | MODULE_DESCRIPTION("Ilitek ILI9225 DRM driver"); | |
448 | MODULE_AUTHOR("David Lechner <david@lechnology.com>"); | |
449 | MODULE_LICENSE("GPL"); |