xref: /openbmc/linux/drivers/gpu/drm/tiny/ili9225.c (revision 8cc8ccba)
1043386a0SNoralf Trønnes // SPDX-License-Identifier: GPL-2.0-or-later
2043386a0SNoralf Trønnes /*
3043386a0SNoralf Trønnes  * DRM driver for Ilitek ILI9225 panels
4043386a0SNoralf Trønnes  *
5043386a0SNoralf Trønnes  * Copyright 2017 David Lechner <david@lechnology.com>
6043386a0SNoralf Trønnes  *
7043386a0SNoralf Trønnes  * Some code copied from mipi-dbi.c
8043386a0SNoralf Trønnes  * Copyright 2016 Noralf Trønnes
9043386a0SNoralf Trønnes  */
10043386a0SNoralf Trønnes 
11043386a0SNoralf Trønnes #include <linux/delay.h>
12043386a0SNoralf Trønnes #include <linux/dma-buf.h>
13043386a0SNoralf Trønnes #include <linux/gpio/consumer.h>
14043386a0SNoralf Trønnes #include <linux/module.h>
15043386a0SNoralf Trønnes #include <linux/property.h>
16043386a0SNoralf Trønnes #include <linux/spi/spi.h>
17043386a0SNoralf Trønnes #include <video/mipi_display.h>
18043386a0SNoralf Trønnes 
19043386a0SNoralf Trønnes #include <drm/drm_atomic_helper.h>
20043386a0SNoralf Trønnes #include <drm/drm_damage_helper.h>
21043386a0SNoralf Trønnes #include <drm/drm_drv.h>
226bcfe8eaSDanilo Krummrich #include <drm/drm_fb_dma_helper.h>
238ab59da2SThomas Zimmermann #include <drm/drm_fbdev_generic.h>
24043386a0SNoralf Trønnes #include <drm/drm_fourcc.h>
25720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
26820c1707SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
274a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h>
28b5f636e6SThomas Zimmermann #include <drm/drm_gem_framebuffer_helper.h>
29f5ad671bSDaniel Vetter #include <drm/drm_managed.h>
30043386a0SNoralf Trønnes #include <drm/drm_mipi_dbi.h>
31043386a0SNoralf Trønnes #include <drm/drm_rect.h>
32043386a0SNoralf Trønnes 
33043386a0SNoralf Trønnes #define ILI9225_DRIVER_READ_CODE	0x00
34043386a0SNoralf Trønnes #define ILI9225_DRIVER_OUTPUT_CONTROL	0x01
35043386a0SNoralf Trønnes #define ILI9225_LCD_AC_DRIVING_CONTROL	0x02
36043386a0SNoralf Trønnes #define ILI9225_ENTRY_MODE		0x03
37043386a0SNoralf Trønnes #define ILI9225_DISPLAY_CONTROL_1	0x07
38043386a0SNoralf Trønnes #define ILI9225_BLANK_PERIOD_CONTROL_1	0x08
39043386a0SNoralf Trønnes #define ILI9225_FRAME_CYCLE_CONTROL	0x0b
40043386a0SNoralf Trønnes #define ILI9225_INTERFACE_CONTROL	0x0c
41043386a0SNoralf Trønnes #define ILI9225_OSCILLATION_CONTROL	0x0f
42043386a0SNoralf Trønnes #define ILI9225_POWER_CONTROL_1		0x10
43043386a0SNoralf Trønnes #define ILI9225_POWER_CONTROL_2		0x11
44043386a0SNoralf Trønnes #define ILI9225_POWER_CONTROL_3		0x12
45043386a0SNoralf Trønnes #define ILI9225_POWER_CONTROL_4		0x13
46043386a0SNoralf Trønnes #define ILI9225_POWER_CONTROL_5		0x14
47043386a0SNoralf Trønnes #define ILI9225_VCI_RECYCLING		0x15
48043386a0SNoralf Trønnes #define ILI9225_RAM_ADDRESS_SET_1	0x20
49043386a0SNoralf Trønnes #define ILI9225_RAM_ADDRESS_SET_2	0x21
50043386a0SNoralf Trønnes #define ILI9225_WRITE_DATA_TO_GRAM	0x22
51043386a0SNoralf Trønnes #define ILI9225_SOFTWARE_RESET		0x28
52043386a0SNoralf Trønnes #define ILI9225_GATE_SCAN_CONTROL	0x30
53043386a0SNoralf Trønnes #define ILI9225_VERTICAL_SCROLL_1	0x31
54043386a0SNoralf Trønnes #define ILI9225_VERTICAL_SCROLL_2	0x32
55043386a0SNoralf Trønnes #define ILI9225_VERTICAL_SCROLL_3	0x33
56043386a0SNoralf Trønnes #define ILI9225_PARTIAL_DRIVING_POS_1	0x34
57043386a0SNoralf Trønnes #define ILI9225_PARTIAL_DRIVING_POS_2	0x35
58043386a0SNoralf Trønnes #define ILI9225_HORIZ_WINDOW_ADDR_1	0x36
59043386a0SNoralf Trønnes #define ILI9225_HORIZ_WINDOW_ADDR_2	0x37
60043386a0SNoralf Trønnes #define ILI9225_VERT_WINDOW_ADDR_1	0x38
61043386a0SNoralf Trønnes #define ILI9225_VERT_WINDOW_ADDR_2	0x39
62043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_1		0x50
63043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_2		0x51
64043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_3		0x52
65043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_4		0x53
66043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_5		0x54
67043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_6		0x55
68043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_7		0x56
69043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_8		0x57
70043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_9		0x58
71043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_10	0x59
72043386a0SNoralf Trønnes 
ili9225_command(struct mipi_dbi * dbi,u8 cmd,u16 data)73043386a0SNoralf Trønnes static inline int ili9225_command(struct mipi_dbi *dbi, u8 cmd, u16 data)
74043386a0SNoralf Trønnes {
75043386a0SNoralf Trønnes 	u8 par[2] = { data >> 8, data & 0xff };
76043386a0SNoralf Trønnes 
77043386a0SNoralf Trønnes 	return mipi_dbi_command_buf(dbi, cmd, par, 2);
78043386a0SNoralf Trønnes }
79043386a0SNoralf Trønnes 
ili9225_fb_dirty(struct iosys_map * src,struct drm_framebuffer * fb,struct drm_rect * rect)80b5f636e6SThomas Zimmermann static void ili9225_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb,
81b5f636e6SThomas Zimmermann 			     struct drm_rect *rect)
82043386a0SNoralf Trønnes {
83043386a0SNoralf Trønnes 	struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev);
84043386a0SNoralf Trønnes 	unsigned int height = rect->y2 - rect->y1;
85043386a0SNoralf Trønnes 	unsigned int width = rect->x2 - rect->x1;
86043386a0SNoralf Trønnes 	struct mipi_dbi *dbi = &dbidev->dbi;
87043386a0SNoralf Trønnes 	bool swap = dbi->swap_bytes;
88043386a0SNoralf Trønnes 	u16 x_start, y_start;
89043386a0SNoralf Trønnes 	u16 x1, x2, y1, y2;
903ea44105SThomas Zimmermann 	int ret = 0;
91043386a0SNoralf Trønnes 	bool full;
92043386a0SNoralf Trønnes 	void *tr;
93043386a0SNoralf Trønnes 
94043386a0SNoralf Trønnes 	full = width == fb->width && height == fb->height;
95043386a0SNoralf Trønnes 
96043386a0SNoralf Trønnes 	DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
97043386a0SNoralf Trønnes 
98043386a0SNoralf Trønnes 	if (!dbi->dc || !full || swap ||
99043386a0SNoralf Trønnes 	    fb->format->format == DRM_FORMAT_XRGB8888) {
100043386a0SNoralf Trønnes 		tr = dbidev->tx_buf;
101b5f636e6SThomas Zimmermann 		ret = mipi_dbi_buf_copy(tr, src, fb, rect, swap);
102043386a0SNoralf Trønnes 		if (ret)
103043386a0SNoralf Trønnes 			goto err_msg;
104043386a0SNoralf Trønnes 	} else {
105b5f636e6SThomas Zimmermann 		tr = src->vaddr; /* TODO: Use mapping abstraction properly */
106043386a0SNoralf Trønnes 	}
107043386a0SNoralf Trønnes 
108043386a0SNoralf Trønnes 	switch (dbidev->rotation) {
109043386a0SNoralf Trønnes 	default:
110043386a0SNoralf Trønnes 		x1 = rect->x1;
111043386a0SNoralf Trønnes 		x2 = rect->x2 - 1;
112043386a0SNoralf Trønnes 		y1 = rect->y1;
113043386a0SNoralf Trønnes 		y2 = rect->y2 - 1;
114043386a0SNoralf Trønnes 		x_start = x1;
115043386a0SNoralf Trønnes 		y_start = y1;
116043386a0SNoralf Trønnes 		break;
117043386a0SNoralf Trønnes 	case 90:
118043386a0SNoralf Trønnes 		x1 = rect->y1;
119043386a0SNoralf Trønnes 		x2 = rect->y2 - 1;
120043386a0SNoralf Trønnes 		y1 = fb->width - rect->x2;
121043386a0SNoralf Trønnes 		y2 = fb->width - rect->x1 - 1;
122043386a0SNoralf Trønnes 		x_start = x1;
123043386a0SNoralf Trønnes 		y_start = y2;
124043386a0SNoralf Trønnes 		break;
125043386a0SNoralf Trønnes 	case 180:
126043386a0SNoralf Trønnes 		x1 = fb->width - rect->x2;
127043386a0SNoralf Trønnes 		x2 = fb->width - rect->x1 - 1;
128043386a0SNoralf Trønnes 		y1 = fb->height - rect->y2;
129043386a0SNoralf Trønnes 		y2 = fb->height - rect->y1 - 1;
130043386a0SNoralf Trønnes 		x_start = x2;
131043386a0SNoralf Trønnes 		y_start = y2;
132043386a0SNoralf Trønnes 		break;
133043386a0SNoralf Trønnes 	case 270:
134043386a0SNoralf Trønnes 		x1 = fb->height - rect->y2;
135043386a0SNoralf Trønnes 		x2 = fb->height - rect->y1 - 1;
136043386a0SNoralf Trønnes 		y1 = rect->x1;
137043386a0SNoralf Trønnes 		y2 = rect->x2 - 1;
138043386a0SNoralf Trønnes 		x_start = x2;
139043386a0SNoralf Trønnes 		y_start = y1;
140043386a0SNoralf Trønnes 		break;
141043386a0SNoralf Trønnes 	}
142043386a0SNoralf Trønnes 
143043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_HORIZ_WINDOW_ADDR_1, x2);
144043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_HORIZ_WINDOW_ADDR_2, x1);
145043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_VERT_WINDOW_ADDR_1, y2);
146043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_VERT_WINDOW_ADDR_2, y1);
147043386a0SNoralf Trønnes 
148043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_1, x_start);
149043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_2, y_start);
150043386a0SNoralf Trønnes 
151043386a0SNoralf Trønnes 	ret = mipi_dbi_command_buf(dbi, ILI9225_WRITE_DATA_TO_GRAM, tr,
152043386a0SNoralf Trønnes 				   width * height * 2);
153043386a0SNoralf Trønnes err_msg:
154043386a0SNoralf Trønnes 	if (ret)
155043386a0SNoralf Trønnes 		dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
156043386a0SNoralf Trønnes }
157043386a0SNoralf Trønnes 
ili9225_pipe_update(struct drm_simple_display_pipe * pipe,struct drm_plane_state * old_state)158043386a0SNoralf Trønnes static void ili9225_pipe_update(struct drm_simple_display_pipe *pipe,
159043386a0SNoralf Trønnes 				struct drm_plane_state *old_state)
160043386a0SNoralf Trønnes {
161043386a0SNoralf Trønnes 	struct drm_plane_state *state = pipe->plane.state;
16269c63e88SThomas Zimmermann 	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
163b5f636e6SThomas Zimmermann 	struct drm_framebuffer *fb = state->fb;
164043386a0SNoralf Trønnes 	struct drm_rect rect;
1653ea44105SThomas Zimmermann 	int idx;
166043386a0SNoralf Trønnes 
1677e06886bSDaniel Vetter 	if (!pipe->crtc.state->active)
1687e06886bSDaniel Vetter 		return;
1697e06886bSDaniel Vetter 
1703ea44105SThomas Zimmermann 	if (!drm_dev_enter(fb->dev, &idx))
1713ea44105SThomas Zimmermann 		return;
1723ea44105SThomas Zimmermann 
173043386a0SNoralf Trønnes 	if (drm_atomic_helper_damage_merged(old_state, state, &rect))
17469c63e88SThomas Zimmermann 		ili9225_fb_dirty(&shadow_plane_state->data[0], fb, &rect);
1753ea44105SThomas Zimmermann 
1763ea44105SThomas Zimmermann 	drm_dev_exit(idx);
177043386a0SNoralf Trønnes }
178043386a0SNoralf Trønnes 
ili9225_pipe_enable(struct drm_simple_display_pipe * pipe,struct drm_crtc_state * crtc_state,struct drm_plane_state * plane_state)179043386a0SNoralf Trønnes static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
180043386a0SNoralf Trønnes 				struct drm_crtc_state *crtc_state,
181043386a0SNoralf Trønnes 				struct drm_plane_state *plane_state)
182043386a0SNoralf Trønnes {
183043386a0SNoralf Trønnes 	struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
18469c63e88SThomas Zimmermann 	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
185043386a0SNoralf Trønnes 	struct drm_framebuffer *fb = plane_state->fb;
186043386a0SNoralf Trønnes 	struct device *dev = pipe->crtc.dev->dev;
187043386a0SNoralf Trønnes 	struct mipi_dbi *dbi = &dbidev->dbi;
188043386a0SNoralf Trønnes 	struct drm_rect rect = {
189043386a0SNoralf Trønnes 		.x1 = 0,
190043386a0SNoralf Trønnes 		.x2 = fb->width,
191043386a0SNoralf Trønnes 		.y1 = 0,
192043386a0SNoralf Trønnes 		.y2 = fb->height,
193043386a0SNoralf Trønnes 	};
194043386a0SNoralf Trønnes 	int ret, idx;
195043386a0SNoralf Trønnes 	u8 am_id;
196043386a0SNoralf Trønnes 
197043386a0SNoralf Trønnes 	if (!drm_dev_enter(pipe->crtc.dev, &idx))
198043386a0SNoralf Trønnes 		return;
199043386a0SNoralf Trønnes 
200043386a0SNoralf Trønnes 	DRM_DEBUG_KMS("\n");
201043386a0SNoralf Trønnes 
202043386a0SNoralf Trønnes 	mipi_dbi_hw_reset(dbi);
203043386a0SNoralf Trønnes 
204043386a0SNoralf Trønnes 	/*
205043386a0SNoralf Trønnes 	 * There don't seem to be two example init sequences that match, so
206043386a0SNoralf Trønnes 	 * using the one from the popular Arduino library for this display.
207043386a0SNoralf Trønnes 	 * https://github.com/Nkawu/TFT_22_ILI9225/blob/master/src/TFT_22_ILI9225.cpp
208043386a0SNoralf Trønnes 	 */
209043386a0SNoralf Trønnes 
210043386a0SNoralf Trønnes 	ret = ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0000);
211043386a0SNoralf Trønnes 	if (ret) {
212043386a0SNoralf Trønnes 		DRM_DEV_ERROR(dev, "Error sending command %d\n", ret);
213043386a0SNoralf Trønnes 		goto out_exit;
214043386a0SNoralf Trønnes 	}
215043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0000);
216043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_POWER_CONTROL_3, 0x0000);
217043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_POWER_CONTROL_4, 0x0000);
218043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_POWER_CONTROL_5, 0x0000);
219043386a0SNoralf Trønnes 
220043386a0SNoralf Trønnes 	msleep(40);
221043386a0SNoralf Trønnes 
222043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0018);
223043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_POWER_CONTROL_3, 0x6121);
224043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_POWER_CONTROL_4, 0x006f);
225043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_POWER_CONTROL_5, 0x495f);
226043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0800);
227043386a0SNoralf Trønnes 
228043386a0SNoralf Trønnes 	msleep(10);
229043386a0SNoralf Trønnes 
230043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x103b);
231043386a0SNoralf Trønnes 
232043386a0SNoralf Trønnes 	msleep(50);
233043386a0SNoralf Trønnes 
234043386a0SNoralf Trønnes 	switch (dbidev->rotation) {
235043386a0SNoralf Trønnes 	default:
236043386a0SNoralf Trønnes 		am_id = 0x30;
237043386a0SNoralf Trønnes 		break;
238043386a0SNoralf Trønnes 	case 90:
239043386a0SNoralf Trønnes 		am_id = 0x18;
240043386a0SNoralf Trønnes 		break;
241043386a0SNoralf Trønnes 	case 180:
242043386a0SNoralf Trønnes 		am_id = 0x00;
243043386a0SNoralf Trønnes 		break;
244043386a0SNoralf Trønnes 	case 270:
245043386a0SNoralf Trønnes 		am_id = 0x28;
246043386a0SNoralf Trønnes 		break;
247043386a0SNoralf Trønnes 	}
248043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_DRIVER_OUTPUT_CONTROL, 0x011c);
249043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_LCD_AC_DRIVING_CONTROL, 0x0100);
250043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_ENTRY_MODE, 0x1000 | am_id);
251043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0000);
252043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_BLANK_PERIOD_CONTROL_1, 0x0808);
253043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_FRAME_CYCLE_CONTROL, 0x1100);
254043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_INTERFACE_CONTROL, 0x0000);
255043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_OSCILLATION_CONTROL, 0x0d01);
256043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_VCI_RECYCLING, 0x0020);
257043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_1, 0x0000);
258043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_2, 0x0000);
259043386a0SNoralf Trønnes 
260043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_GATE_SCAN_CONTROL, 0x0000);
261043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_1, 0x00db);
262043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_2, 0x0000);
263043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_3, 0x0000);
264043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_PARTIAL_DRIVING_POS_1, 0x00db);
265043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_PARTIAL_DRIVING_POS_2, 0x0000);
266043386a0SNoralf Trønnes 
267043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_GAMMA_CONTROL_1, 0x0000);
268043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_GAMMA_CONTROL_2, 0x0808);
269043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_GAMMA_CONTROL_3, 0x080a);
270043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_GAMMA_CONTROL_4, 0x000a);
271043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_GAMMA_CONTROL_5, 0x0a08);
272043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_GAMMA_CONTROL_6, 0x0808);
273043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_GAMMA_CONTROL_7, 0x0000);
274043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_GAMMA_CONTROL_8, 0x0a00);
275043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_GAMMA_CONTROL_9, 0x0710);
276043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_GAMMA_CONTROL_10, 0x0710);
277043386a0SNoralf Trønnes 
278043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0012);
279043386a0SNoralf Trønnes 
280043386a0SNoralf Trønnes 	msleep(50);
281043386a0SNoralf Trønnes 
282043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x1017);
283043386a0SNoralf Trønnes 
28469c63e88SThomas Zimmermann 	ili9225_fb_dirty(&shadow_plane_state->data[0], fb, &rect);
285b5f636e6SThomas Zimmermann 
286043386a0SNoralf Trønnes out_exit:
287043386a0SNoralf Trønnes 	drm_dev_exit(idx);
288043386a0SNoralf Trønnes }
289043386a0SNoralf Trønnes 
ili9225_pipe_disable(struct drm_simple_display_pipe * pipe)290043386a0SNoralf Trønnes static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
291043386a0SNoralf Trønnes {
292043386a0SNoralf Trønnes 	struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
293043386a0SNoralf Trønnes 	struct mipi_dbi *dbi = &dbidev->dbi;
294043386a0SNoralf Trønnes 
295043386a0SNoralf Trønnes 	DRM_DEBUG_KMS("\n");
296043386a0SNoralf Trønnes 
297043386a0SNoralf Trønnes 	/*
298043386a0SNoralf Trønnes 	 * This callback is not protected by drm_dev_enter/exit since we want to
299043386a0SNoralf Trønnes 	 * turn off the display on regular driver unload. It's highly unlikely
300043386a0SNoralf Trønnes 	 * that the underlying SPI controller is gone should this be called after
301043386a0SNoralf Trønnes 	 * unplug.
302043386a0SNoralf Trønnes 	 */
303043386a0SNoralf Trønnes 
304043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0000);
305043386a0SNoralf Trønnes 	msleep(50);
306043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0007);
307043386a0SNoralf Trønnes 	msleep(50);
308043386a0SNoralf Trønnes 	ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0a02);
309043386a0SNoralf Trønnes }
310043386a0SNoralf Trønnes 
ili9225_dbi_command(struct mipi_dbi * dbi,u8 * cmd,u8 * par,size_t num)311043386a0SNoralf Trønnes static int ili9225_dbi_command(struct mipi_dbi *dbi, u8 *cmd, u8 *par,
312043386a0SNoralf Trønnes 			       size_t num)
313043386a0SNoralf Trønnes {
314043386a0SNoralf Trønnes 	struct spi_device *spi = dbi->spi;
315043386a0SNoralf Trønnes 	unsigned int bpw = 8;
316043386a0SNoralf Trønnes 	u32 speed_hz;
317043386a0SNoralf Trønnes 	int ret;
318043386a0SNoralf Trønnes 
319*8cc8ccbaSOtto Pflüger 	spi_bus_lock(spi->controller);
320043386a0SNoralf Trønnes 	gpiod_set_value_cansleep(dbi->dc, 0);
321043386a0SNoralf Trønnes 	speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
322043386a0SNoralf Trønnes 	ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1);
323*8cc8ccbaSOtto Pflüger 	spi_bus_unlock(spi->controller);
324043386a0SNoralf Trønnes 	if (ret || !num)
325043386a0SNoralf Trønnes 		return ret;
326043386a0SNoralf Trønnes 
327043386a0SNoralf Trønnes 	if (*cmd == ILI9225_WRITE_DATA_TO_GRAM && !dbi->swap_bytes)
328043386a0SNoralf Trønnes 		bpw = 16;
329043386a0SNoralf Trønnes 
330*8cc8ccbaSOtto Pflüger 	spi_bus_lock(spi->controller);
331043386a0SNoralf Trønnes 	gpiod_set_value_cansleep(dbi->dc, 1);
332043386a0SNoralf Trønnes 	speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
333*8cc8ccbaSOtto Pflüger 	ret = mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num);
334*8cc8ccbaSOtto Pflüger 	spi_bus_unlock(spi->controller);
335043386a0SNoralf Trønnes 
336*8cc8ccbaSOtto Pflüger 	return ret;
337043386a0SNoralf Trønnes }
338043386a0SNoralf Trønnes 
339043386a0SNoralf Trønnes static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = {
340fe2c021bSThomas Zimmermann 	.mode_valid	= mipi_dbi_pipe_mode_valid,
341043386a0SNoralf Trønnes 	.enable		= ili9225_pipe_enable,
342043386a0SNoralf Trønnes 	.disable	= ili9225_pipe_disable,
343043386a0SNoralf Trønnes 	.update		= ili9225_pipe_update,
344e7caf04dSThomas Zimmermann 	.begin_fb_access = mipi_dbi_pipe_begin_fb_access,
345e7caf04dSThomas Zimmermann 	.end_fb_access	= mipi_dbi_pipe_end_fb_access,
346e7caf04dSThomas Zimmermann 	.reset_plane	= mipi_dbi_pipe_reset_plane,
347e7caf04dSThomas Zimmermann 	.duplicate_plane_state = mipi_dbi_pipe_duplicate_plane_state,
348e7caf04dSThomas Zimmermann 	.destroy_plane_state = mipi_dbi_pipe_destroy_plane_state,
349043386a0SNoralf Trønnes };
350043386a0SNoralf Trønnes 
351043386a0SNoralf Trønnes static const struct drm_display_mode ili9225_mode = {
352043386a0SNoralf Trønnes 	DRM_SIMPLE_MODE(176, 220, 35, 44),
353043386a0SNoralf Trønnes };
354043386a0SNoralf Trønnes 
3554a83c26aSDanilo Krummrich DEFINE_DRM_GEM_DMA_FOPS(ili9225_fops);
356043386a0SNoralf Trønnes 
35770a59dd8SDaniel Vetter static const struct drm_driver ili9225_driver = {
358043386a0SNoralf Trønnes 	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
359043386a0SNoralf Trønnes 	.fops			= &ili9225_fops,
3604a83c26aSDanilo Krummrich 	DRM_GEM_DMA_DRIVER_OPS_VMAP,
361043386a0SNoralf Trønnes 	.name			= "ili9225",
362043386a0SNoralf Trønnes 	.desc			= "Ilitek ILI9225",
363043386a0SNoralf Trønnes 	.date			= "20171106",
364043386a0SNoralf Trønnes 	.major			= 1,
365043386a0SNoralf Trønnes 	.minor			= 0,
366043386a0SNoralf Trønnes };
367043386a0SNoralf Trønnes 
368043386a0SNoralf Trønnes static const struct of_device_id ili9225_of_match[] = {
369043386a0SNoralf Trønnes 	{ .compatible = "vot,v220hf01a-t" },
370043386a0SNoralf Trønnes 	{},
371043386a0SNoralf Trønnes };
372043386a0SNoralf Trønnes MODULE_DEVICE_TABLE(of, ili9225_of_match);
373043386a0SNoralf Trønnes 
374043386a0SNoralf Trønnes static const struct spi_device_id ili9225_id[] = {
375043386a0SNoralf Trønnes 	{ "v220hf01a-t", 0 },
376043386a0SNoralf Trønnes 	{ },
377043386a0SNoralf Trønnes };
378043386a0SNoralf Trønnes MODULE_DEVICE_TABLE(spi, ili9225_id);
379043386a0SNoralf Trønnes 
ili9225_probe(struct spi_device * spi)380043386a0SNoralf Trønnes static int ili9225_probe(struct spi_device *spi)
381043386a0SNoralf Trønnes {
382043386a0SNoralf Trønnes 	struct device *dev = &spi->dev;
383043386a0SNoralf Trønnes 	struct mipi_dbi_dev *dbidev;
384043386a0SNoralf Trønnes 	struct drm_device *drm;
385043386a0SNoralf Trønnes 	struct mipi_dbi *dbi;
386043386a0SNoralf Trønnes 	struct gpio_desc *rs;
387043386a0SNoralf Trønnes 	u32 rotation = 0;
388043386a0SNoralf Trønnes 	int ret;
389043386a0SNoralf Trønnes 
3904c99859fSDaniel Vetter 	dbidev = devm_drm_dev_alloc(dev, &ili9225_driver,
3914c99859fSDaniel Vetter 				    struct mipi_dbi_dev, drm);
3924c99859fSDaniel Vetter 	if (IS_ERR(dbidev))
3934c99859fSDaniel Vetter 		return PTR_ERR(dbidev);
394043386a0SNoralf Trønnes 
395043386a0SNoralf Trønnes 	dbi = &dbidev->dbi;
396043386a0SNoralf Trønnes 	drm = &dbidev->drm;
397043386a0SNoralf Trønnes 
398043386a0SNoralf Trønnes 	dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
399d39353c7SAndy Shevchenko 	if (IS_ERR(dbi->reset))
400d39353c7SAndy Shevchenko 		return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n");
401043386a0SNoralf Trønnes 
402043386a0SNoralf Trønnes 	rs = devm_gpiod_get(dev, "rs", GPIOD_OUT_LOW);
403d39353c7SAndy Shevchenko 	if (IS_ERR(rs))
404d39353c7SAndy Shevchenko 		return dev_err_probe(dev, PTR_ERR(rs), "Failed to get GPIO 'rs'\n");
405043386a0SNoralf Trønnes 
406043386a0SNoralf Trønnes 	device_property_read_u32(dev, "rotation", &rotation);
407043386a0SNoralf Trønnes 
408043386a0SNoralf Trønnes 	ret = mipi_dbi_spi_init(spi, dbi, rs);
409043386a0SNoralf Trønnes 	if (ret)
410043386a0SNoralf Trønnes 		return ret;
411043386a0SNoralf Trønnes 
412043386a0SNoralf Trønnes 	/* override the command function set in  mipi_dbi_spi_init() */
413043386a0SNoralf Trønnes 	dbi->command = ili9225_dbi_command;
414043386a0SNoralf Trønnes 
415043386a0SNoralf Trønnes 	ret = mipi_dbi_dev_init(dbidev, &ili9225_pipe_funcs, &ili9225_mode, rotation);
416043386a0SNoralf Trønnes 	if (ret)
417043386a0SNoralf Trønnes 		return ret;
418043386a0SNoralf Trønnes 
419043386a0SNoralf Trønnes 	drm_mode_config_reset(drm);
420043386a0SNoralf Trønnes 
421043386a0SNoralf Trønnes 	ret = drm_dev_register(drm, 0);
422043386a0SNoralf Trønnes 	if (ret)
423043386a0SNoralf Trønnes 		return ret;
424043386a0SNoralf Trønnes 
425043386a0SNoralf Trønnes 	spi_set_drvdata(spi, drm);
426043386a0SNoralf Trønnes 
427043386a0SNoralf Trønnes 	drm_fbdev_generic_setup(drm, 0);
428043386a0SNoralf Trønnes 
429043386a0SNoralf Trønnes 	return 0;
430043386a0SNoralf Trønnes }
431043386a0SNoralf Trønnes 
ili9225_remove(struct spi_device * spi)432a0386bbaSUwe Kleine-König static void ili9225_remove(struct spi_device *spi)
433043386a0SNoralf Trønnes {
434043386a0SNoralf Trønnes 	struct drm_device *drm = spi_get_drvdata(spi);
435043386a0SNoralf Trønnes 
436043386a0SNoralf Trønnes 	drm_dev_unplug(drm);
437043386a0SNoralf Trønnes 	drm_atomic_helper_shutdown(drm);
438043386a0SNoralf Trønnes }
439043386a0SNoralf Trønnes 
ili9225_shutdown(struct spi_device * spi)440043386a0SNoralf Trønnes static void ili9225_shutdown(struct spi_device *spi)
441043386a0SNoralf Trønnes {
442043386a0SNoralf Trønnes 	drm_atomic_helper_shutdown(spi_get_drvdata(spi));
443043386a0SNoralf Trønnes }
444043386a0SNoralf Trønnes 
445043386a0SNoralf Trønnes static struct spi_driver ili9225_spi_driver = {
446043386a0SNoralf Trønnes 	.driver = {
447043386a0SNoralf Trønnes 		.name = "ili9225",
448043386a0SNoralf Trønnes 		.owner = THIS_MODULE,
449043386a0SNoralf Trønnes 		.of_match_table = ili9225_of_match,
450043386a0SNoralf Trønnes 	},
451043386a0SNoralf Trønnes 	.id_table = ili9225_id,
452043386a0SNoralf Trønnes 	.probe = ili9225_probe,
453043386a0SNoralf Trønnes 	.remove = ili9225_remove,
454043386a0SNoralf Trønnes 	.shutdown = ili9225_shutdown,
455043386a0SNoralf Trønnes };
456043386a0SNoralf Trønnes module_spi_driver(ili9225_spi_driver);
457043386a0SNoralf Trønnes 
458043386a0SNoralf Trønnes MODULE_DESCRIPTION("Ilitek ILI9225 DRM driver");
459043386a0SNoralf Trønnes MODULE_AUTHOR("David Lechner <david@lechnology.com>");
460043386a0SNoralf Trønnes MODULE_LICENSE("GPL");
461