1
2
3
4
5
6
7
8
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
19#include <drm/drm_atomic_helper.h>
20#include <drm/drm_damage_helper.h>
21#include <drm/drm_drv.h>
22#include <drm/drm_fb_cma_helper.h>
23#include <drm/drm_fb_helper.h>
24#include <drm/drm_fourcc.h>
25#include <drm/drm_gem_cma_helper.h>
26#include <drm/drm_gem_framebuffer_helper.h>
27#include <drm/drm_rect.h>
28#include <drm/drm_vblank.h>
29#include <drm/tinydrm/mipi-dbi.h>
30#include <drm/tinydrm/tinydrm-helpers.h>
31
32#define ILI9225_DRIVER_READ_CODE 0x00
33#define ILI9225_DRIVER_OUTPUT_CONTROL 0x01
34#define ILI9225_LCD_AC_DRIVING_CONTROL 0x02
35#define ILI9225_ENTRY_MODE 0x03
36#define ILI9225_DISPLAY_CONTROL_1 0x07
37#define ILI9225_BLANK_PERIOD_CONTROL_1 0x08
38#define ILI9225_FRAME_CYCLE_CONTROL 0x0b
39#define ILI9225_INTERFACE_CONTROL 0x0c
40#define ILI9225_OSCILLATION_CONTROL 0x0f
41#define ILI9225_POWER_CONTROL_1 0x10
42#define ILI9225_POWER_CONTROL_2 0x11
43#define ILI9225_POWER_CONTROL_3 0x12
44#define ILI9225_POWER_CONTROL_4 0x13
45#define ILI9225_POWER_CONTROL_5 0x14
46#define ILI9225_VCI_RECYCLING 0x15
47#define ILI9225_RAM_ADDRESS_SET_1 0x20
48#define ILI9225_RAM_ADDRESS_SET_2 0x21
49#define ILI9225_WRITE_DATA_TO_GRAM 0x22
50#define ILI9225_SOFTWARE_RESET 0x28
51#define ILI9225_GATE_SCAN_CONTROL 0x30
52#define ILI9225_VERTICAL_SCROLL_1 0x31
53#define ILI9225_VERTICAL_SCROLL_2 0x32
54#define ILI9225_VERTICAL_SCROLL_3 0x33
55#define ILI9225_PARTIAL_DRIVING_POS_1 0x34
56#define ILI9225_PARTIAL_DRIVING_POS_2 0x35
57#define ILI9225_HORIZ_WINDOW_ADDR_1 0x36
58#define ILI9225_HORIZ_WINDOW_ADDR_2 0x37
59#define ILI9225_VERT_WINDOW_ADDR_1 0x38
60#define ILI9225_VERT_WINDOW_ADDR_2 0x39
61#define ILI9225_GAMMA_CONTROL_1 0x50
62#define ILI9225_GAMMA_CONTROL_2 0x51
63#define ILI9225_GAMMA_CONTROL_3 0x52
64#define ILI9225_GAMMA_CONTROL_4 0x53
65#define ILI9225_GAMMA_CONTROL_5 0x54
66#define ILI9225_GAMMA_CONTROL_6 0x55
67#define ILI9225_GAMMA_CONTROL_7 0x56
68#define ILI9225_GAMMA_CONTROL_8 0x57
69#define ILI9225_GAMMA_CONTROL_9 0x58
70#define ILI9225_GAMMA_CONTROL_10 0x59
71
72static inline int ili9225_command(struct mipi_dbi *mipi, u8 cmd, u16 data)
73{
74 u8 par[2] = { data >> 8, data & 0xff };
75
76 return mipi_dbi_command_buf(mipi, cmd, par, 2);
77}
78
79static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
80{
81 struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
82 struct mipi_dbi *mipi = drm_to_mipi_dbi(fb->dev);
83 unsigned int height = rect->y2 - rect->y1;
84 unsigned int width = rect->x2 - rect->x1;
85 bool swap = mipi->swap_bytes;
86 u16 x_start, y_start;
87 u16 x1, x2, y1, y2;
88 int idx, ret = 0;
89 bool full;
90 void *tr;
91
92 if (!mipi->enabled)
93 return;
94
95 if (!drm_dev_enter(fb->dev, &idx))
96 return;
97
98 full = width == fb->width && height == fb->height;
99
100 DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
101
102 if (!mipi->dc || !full || swap ||
103 fb->format->format == DRM_FORMAT_XRGB8888) {
104 tr = mipi->tx_buf;
105 ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, rect, swap);
106 if (ret)
107 goto err_msg;
108 } else {
109 tr = cma_obj->vaddr;
110 }
111
112 switch (mipi->rotation) {
113 default:
114 x1 = rect->x1;
115 x2 = rect->x2 - 1;
116 y1 = rect->y1;
117 y2 = rect->y2 - 1;
118 x_start = x1;
119 y_start = y1;
120 break;
121 case 90:
122 x1 = rect->y1;
123 x2 = rect->y2 - 1;
124 y1 = fb->width - rect->x2;
125 y2 = fb->width - rect->x1 - 1;
126 x_start = x1;
127 y_start = y2;
128 break;
129 case 180:
130 x1 = fb->width - rect->x2;
131 x2 = fb->width - rect->x1 - 1;
132 y1 = fb->height - rect->y2;
133 y2 = fb->height - rect->y1 - 1;
134 x_start = x2;
135 y_start = y2;
136 break;
137 case 270:
138 x1 = fb->height - rect->y2;
139 x2 = fb->height - rect->y1 - 1;
140 y1 = rect->x1;
141 y2 = rect->x2 - 1;
142 x_start = x2;
143 y_start = y1;
144 break;
145 }
146
147 ili9225_command(mipi, ILI9225_HORIZ_WINDOW_ADDR_1, x2);
148 ili9225_command(mipi, ILI9225_HORIZ_WINDOW_ADDR_2, x1);
149 ili9225_command(mipi, ILI9225_VERT_WINDOW_ADDR_1, y2);
150 ili9225_command(mipi, ILI9225_VERT_WINDOW_ADDR_2, y1);
151
152 ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_1, x_start);
153 ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_2, y_start);
154
155 ret = mipi_dbi_command_buf(mipi, ILI9225_WRITE_DATA_TO_GRAM, tr,
156 width * height * 2);
157err_msg:
158 if (ret)
159 dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
160
161 drm_dev_exit(idx);
162}
163
164static void ili9225_pipe_update(struct drm_simple_display_pipe *pipe,
165 struct drm_plane_state *old_state)
166{
167 struct drm_plane_state *state = pipe->plane.state;
168 struct drm_crtc *crtc = &pipe->crtc;
169 struct drm_rect rect;
170
171 if (drm_atomic_helper_damage_merged(old_state, state, &rect))
172 ili9225_fb_dirty(state->fb, &rect);
173
174 if (crtc->state->event) {
175 spin_lock_irq(&crtc->dev->event_lock);
176 drm_crtc_send_vblank_event(crtc, crtc->state->event);
177 spin_unlock_irq(&crtc->dev->event_lock);
178 crtc->state->event = NULL;
179 }
180}
181
182static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
183 struct drm_crtc_state *crtc_state,
184 struct drm_plane_state *plane_state)
185{
186 struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
187 struct drm_framebuffer *fb = plane_state->fb;
188 struct device *dev = pipe->crtc.dev->dev;
189 struct drm_rect rect = {
190 .x1 = 0,
191 .x2 = fb->width,
192 .y1 = 0,
193 .y2 = fb->height,
194 };
195 int ret, idx;
196 u8 am_id;
197
198 if (!drm_dev_enter(pipe->crtc.dev, &idx))
199 return;
200
201 DRM_DEBUG_KMS("\n");
202
203 mipi_dbi_hw_reset(mipi);
204
205
206
207
208
209
210
211 ret = ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0000);
212 if (ret) {
213 DRM_DEV_ERROR(dev, "Error sending command %d\n", ret);
214 goto out_exit;
215 }
216 ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0000);
217 ili9225_command(mipi, ILI9225_POWER_CONTROL_3, 0x0000);
218 ili9225_command(mipi, ILI9225_POWER_CONTROL_4, 0x0000);
219 ili9225_command(mipi, ILI9225_POWER_CONTROL_5, 0x0000);
220
221 msleep(40);
222
223 ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0018);
224 ili9225_command(mipi, ILI9225_POWER_CONTROL_3, 0x6121);
225 ili9225_command(mipi, ILI9225_POWER_CONTROL_4, 0x006f);
226 ili9225_command(mipi, ILI9225_POWER_CONTROL_5, 0x495f);
227 ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0800);
228
229 msleep(10);
230
231 ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x103b);
232
233 msleep(50);
234
235 switch (mipi->rotation) {
236 default:
237 am_id = 0x30;
238 break;
239 case 90:
240 am_id = 0x18;
241 break;
242 case 180:
243 am_id = 0x00;
244 break;
245 case 270:
246 am_id = 0x28;
247 break;
248 }
249 ili9225_command(mipi, ILI9225_DRIVER_OUTPUT_CONTROL, 0x011c);
250 ili9225_command(mipi, ILI9225_LCD_AC_DRIVING_CONTROL, 0x0100);
251 ili9225_command(mipi, ILI9225_ENTRY_MODE, 0x1000 | am_id);
252 ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0000);
253 ili9225_command(mipi, ILI9225_BLANK_PERIOD_CONTROL_1, 0x0808);
254 ili9225_command(mipi, ILI9225_FRAME_CYCLE_CONTROL, 0x1100);
255 ili9225_command(mipi, ILI9225_INTERFACE_CONTROL, 0x0000);
256 ili9225_command(mipi, ILI9225_OSCILLATION_CONTROL, 0x0d01);
257 ili9225_command(mipi, ILI9225_VCI_RECYCLING, 0x0020);
258 ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_1, 0x0000);
259 ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_2, 0x0000);
260
261 ili9225_command(mipi, ILI9225_GATE_SCAN_CONTROL, 0x0000);
262 ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_1, 0x00db);
263 ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_2, 0x0000);
264 ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_3, 0x0000);
265 ili9225_command(mipi, ILI9225_PARTIAL_DRIVING_POS_1, 0x00db);
266 ili9225_command(mipi, ILI9225_PARTIAL_DRIVING_POS_2, 0x0000);
267
268 ili9225_command(mipi, ILI9225_GAMMA_CONTROL_1, 0x0000);
269 ili9225_command(mipi, ILI9225_GAMMA_CONTROL_2, 0x0808);
270 ili9225_command(mipi, ILI9225_GAMMA_CONTROL_3, 0x080a);
271 ili9225_command(mipi, ILI9225_GAMMA_CONTROL_4, 0x000a);
272 ili9225_command(mipi, ILI9225_GAMMA_CONTROL_5, 0x0a08);
273 ili9225_command(mipi, ILI9225_GAMMA_CONTROL_6, 0x0808);
274 ili9225_command(mipi, ILI9225_GAMMA_CONTROL_7, 0x0000);
275 ili9225_command(mipi, ILI9225_GAMMA_CONTROL_8, 0x0a00);
276 ili9225_command(mipi, ILI9225_GAMMA_CONTROL_9, 0x0710);
277 ili9225_command(mipi, ILI9225_GAMMA_CONTROL_10, 0x0710);
278
279 ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0012);
280
281 msleep(50);
282
283 ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x1017);
284
285 mipi->enabled = true;
286 ili9225_fb_dirty(fb, &rect);
287out_exit:
288 drm_dev_exit(idx);
289}
290
291static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
292{
293 struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
294
295 DRM_DEBUG_KMS("\n");
296
297
298
299
300
301
302
303
304 if (!mipi->enabled)
305 return;
306
307 ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0000);
308 msleep(50);
309 ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0007);
310 msleep(50);
311 ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0a02);
312
313 mipi->enabled = false;
314}
315
316static int ili9225_dbi_command(struct mipi_dbi *mipi, u8 *cmd, u8 *par,
317 size_t num)
318{
319 struct spi_device *spi = mipi->spi;
320 unsigned int bpw = 8;
321 u32 speed_hz;
322 int ret;
323
324 gpiod_set_value_cansleep(mipi->dc, 0);
325 speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
326 ret = tinydrm_spi_transfer(spi, speed_hz, NULL, 8, cmd, 1);
327 if (ret || !num)
328 return ret;
329
330 if (*cmd == ILI9225_WRITE_DATA_TO_GRAM && !mipi->swap_bytes)
331 bpw = 16;
332
333 gpiod_set_value_cansleep(mipi->dc, 1);
334 speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
335
336 return tinydrm_spi_transfer(spi, speed_hz, NULL, bpw, par, num);
337}
338
339static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = {
340 .enable = ili9225_pipe_enable,
341 .disable = ili9225_pipe_disable,
342 .update = ili9225_pipe_update,
343 .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
344};
345
346static const struct drm_display_mode ili9225_mode = {
347 DRM_SIMPLE_MODE(176, 220, 35, 44),
348};
349
350DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops);
351
352static struct drm_driver ili9225_driver = {
353 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
354 DRIVER_ATOMIC,
355 .fops = &ili9225_fops,
356 .release = mipi_dbi_release,
357 DRM_GEM_CMA_VMAP_DRIVER_OPS,
358 .name = "ili9225",
359 .desc = "Ilitek ILI9225",
360 .date = "20171106",
361 .major = 1,
362 .minor = 0,
363};
364
365static const struct of_device_id ili9225_of_match[] = {
366 { .compatible = "vot,v220hf01a-t" },
367 {},
368};
369MODULE_DEVICE_TABLE(of, ili9225_of_match);
370
371static const struct spi_device_id ili9225_id[] = {
372 { "v220hf01a-t", 0 },
373 { },
374};
375MODULE_DEVICE_TABLE(spi, ili9225_id);
376
377static int ili9225_probe(struct spi_device *spi)
378{
379 struct device *dev = &spi->dev;
380 struct drm_device *drm;
381 struct mipi_dbi *mipi;
382 struct gpio_desc *rs;
383 u32 rotation = 0;
384 int ret;
385
386 mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
387 if (!mipi)
388 return -ENOMEM;
389
390 drm = &mipi->drm;
391 ret = devm_drm_dev_init(dev, drm, &ili9225_driver);
392 if (ret) {
393 kfree(mipi);
394 return ret;
395 }
396
397 drm_mode_config_init(drm);
398
399 mipi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
400 if (IS_ERR(mipi->reset)) {
401 DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
402 return PTR_ERR(mipi->reset);
403 }
404
405 rs = devm_gpiod_get(dev, "rs", GPIOD_OUT_LOW);
406 if (IS_ERR(rs)) {
407 DRM_DEV_ERROR(dev, "Failed to get gpio 'rs'\n");
408 return PTR_ERR(rs);
409 }
410
411 device_property_read_u32(dev, "rotation", &rotation);
412
413 ret = mipi_dbi_spi_init(spi, mipi, rs);
414 if (ret)
415 return ret;
416
417
418 mipi->command = ili9225_dbi_command;
419
420 ret = mipi_dbi_init(mipi, &ili9225_pipe_funcs, &ili9225_mode, rotation);
421 if (ret)
422 return ret;
423
424 drm_mode_config_reset(drm);
425
426 ret = drm_dev_register(drm, 0);
427 if (ret)
428 return ret;
429
430 spi_set_drvdata(spi, drm);
431
432 drm_fbdev_generic_setup(drm, 0);
433
434 return 0;
435}
436
437static int ili9225_remove(struct spi_device *spi)
438{
439 struct drm_device *drm = spi_get_drvdata(spi);
440
441 drm_dev_unplug(drm);
442 drm_atomic_helper_shutdown(drm);
443
444 return 0;
445}
446
447static void ili9225_shutdown(struct spi_device *spi)
448{
449 drm_atomic_helper_shutdown(spi_get_drvdata(spi));
450}
451
452static struct spi_driver ili9225_spi_driver = {
453 .driver = {
454 .name = "ili9225",
455 .owner = THIS_MODULE,
456 .of_match_table = ili9225_of_match,
457 },
458 .id_table = ili9225_id,
459 .probe = ili9225_probe,
460 .remove = ili9225_remove,
461 .shutdown = ili9225_shutdown,
462};
463module_spi_driver(ili9225_spi_driver);
464
465MODULE_DESCRIPTION("Ilitek ILI9225 DRM driver");
466MODULE_AUTHOR("David Lechner <david@lechnology.com>");
467MODULE_LICENSE("GPL");
468