Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
02dd95fe NT |
2 | /* |
3 | * MIPI Display Bus Interface (DBI) LCD controller support | |
4 | * | |
5 | * Copyright 2016 Noralf Trønnes | |
02dd95fe NT |
6 | */ |
7 | ||
73289afe | 8 | #include <linux/backlight.h> |
02dd95fe | 9 | #include <linux/debugfs.h> |
84056e9b | 10 | #include <linux/delay.h> |
02dd95fe NT |
11 | #include <linux/gpio/consumer.h> |
12 | #include <linux/module.h> | |
13 | #include <linux/regulator/consumer.h> | |
14 | #include <linux/spi/spi.h> | |
3db8d37d | 15 | |
710ae47d | 16 | #include <drm/drm_connector.h> |
af741381 | 17 | #include <drm/drm_damage_helper.h> |
84056e9b | 18 | #include <drm/drm_drv.h> |
c47160d8 | 19 | #include <drm/drm_file.h> |
7415287e | 20 | #include <drm/drm_format_helper.h> |
84056e9b | 21 | #include <drm/drm_fourcc.h> |
720cf96d | 22 | #include <drm/drm_framebuffer.h> |
c47160d8 | 23 | #include <drm/drm_gem.h> |
e7caf04d | 24 | #include <drm/drm_gem_atomic_helper.h> |
3db8d37d | 25 | #include <drm/drm_gem_framebuffer_helper.h> |
174102f4 | 26 | #include <drm/drm_mipi_dbi.h> |
710ae47d NT |
27 | #include <drm/drm_modes.h> |
28 | #include <drm/drm_probe_helper.h> | |
b051b345 | 29 | #include <drm/drm_rect.h> |
02dd95fe NT |
30 | #include <video/mipi_display.h> |
31 | ||
32 | #define MIPI_DBI_MAX_SPI_READ_SPEED 2000000 /* 2MHz */ | |
33 | ||
34 | #define DCS_POWER_MODE_DISPLAY BIT(2) | |
35 | #define DCS_POWER_MODE_DISPLAY_NORMAL_MODE BIT(3) | |
36 | #define DCS_POWER_MODE_SLEEP_MODE BIT(4) | |
37 | #define DCS_POWER_MODE_PARTIAL_MODE BIT(5) | |
38 | #define DCS_POWER_MODE_IDLE_MODE BIT(6) | |
39 | #define DCS_POWER_MODE_RESERVED_MASK (BIT(0) | BIT(1) | BIT(7)) | |
40 | ||
41 | /** | |
42 | * DOC: overview | |
43 | * | |
44 | * This library provides helpers for MIPI Display Bus Interface (DBI) | |
45 | * compatible display controllers. | |
46 | * | |
47 | * Many controllers for tiny lcd displays are MIPI compliant and can use this | |
48 | * library. If a controller uses registers 0x2A and 0x2B to set the area to | |
49 | * update and uses register 0x2C to write to frame memory, it is most likely | |
50 | * MIPI compliant. | |
51 | * | |
52 | * Only MIPI Type 1 displays are supported since a full frame memory is needed. | |
53 | * | |
54 | * There are 3 MIPI DBI implementation types: | |
55 | * | |
56 | * A. Motorola 6800 type parallel bus | |
57 | * | |
58 | * B. Intel 8080 type parallel bus | |
59 | * | |
60 | * C. SPI type with 3 options: | |
61 | * | |
62 | * 1. 9-bit with the Data/Command signal as the ninth bit | |
63 | * 2. Same as above except it's sent as 16 bits | |
64 | * 3. 8-bit with the Data/Command signal as a separate D/CX pin | |
65 | * | |
66 | * Currently mipi_dbi only supports Type C options 1 and 3 with | |
67 | * mipi_dbi_spi_init(). | |
68 | */ | |
69 | ||
70 | #define MIPI_DBI_DEBUG_COMMAND(cmd, data, len) \ | |
71 | ({ \ | |
72 | if (!len) \ | |
73 | DRM_DEBUG_DRIVER("cmd=%02x\n", cmd); \ | |
74 | else if (len <= 32) \ | |
ce8c0137 | 75 | DRM_DEBUG_DRIVER("cmd=%02x, par=%*ph\n", cmd, (int)len, data);\ |
02dd95fe | 76 | else \ |
ef96152e | 77 | DRM_DEBUG_DRIVER("cmd=%02x, len=%zu\n", cmd, len); \ |
02dd95fe NT |
78 | }) |
79 | ||
80 | static const u8 mipi_dbi_dcs_read_commands[] = { | |
81 | MIPI_DCS_GET_DISPLAY_ID, | |
82 | MIPI_DCS_GET_RED_CHANNEL, | |
83 | MIPI_DCS_GET_GREEN_CHANNEL, | |
84 | MIPI_DCS_GET_BLUE_CHANNEL, | |
85 | MIPI_DCS_GET_DISPLAY_STATUS, | |
86 | MIPI_DCS_GET_POWER_MODE, | |
87 | MIPI_DCS_GET_ADDRESS_MODE, | |
88 | MIPI_DCS_GET_PIXEL_FORMAT, | |
89 | MIPI_DCS_GET_DISPLAY_MODE, | |
90 | MIPI_DCS_GET_SIGNAL_MODE, | |
91 | MIPI_DCS_GET_DIAGNOSTIC_RESULT, | |
92 | MIPI_DCS_READ_MEMORY_START, | |
93 | MIPI_DCS_READ_MEMORY_CONTINUE, | |
94 | MIPI_DCS_GET_SCANLINE, | |
95 | MIPI_DCS_GET_DISPLAY_BRIGHTNESS, | |
96 | MIPI_DCS_GET_CONTROL_DISPLAY, | |
97 | MIPI_DCS_GET_POWER_SAVE, | |
98 | MIPI_DCS_GET_CABC_MIN_BRIGHTNESS, | |
99 | MIPI_DCS_READ_DDB_START, | |
100 | MIPI_DCS_READ_DDB_CONTINUE, | |
101 | 0, /* sentinel */ | |
102 | }; | |
103 | ||
36b50572 | 104 | static bool mipi_dbi_command_is_read(struct mipi_dbi *dbi, u8 cmd) |
02dd95fe NT |
105 | { |
106 | unsigned int i; | |
107 | ||
36b50572 | 108 | if (!dbi->read_commands) |
02dd95fe NT |
109 | return false; |
110 | ||
111 | for (i = 0; i < 0xff; i++) { | |
36b50572 | 112 | if (!dbi->read_commands[i]) |
02dd95fe | 113 | return false; |
36b50572 | 114 | if (cmd == dbi->read_commands[i]) |
02dd95fe NT |
115 | return true; |
116 | } | |
117 | ||
118 | return false; | |
119 | } | |
120 | ||
121 | /** | |
122 | * mipi_dbi_command_read - MIPI DCS read command | |
36b50572 | 123 | * @dbi: MIPI DBI structure |
02dd95fe NT |
124 | * @cmd: Command |
125 | * @val: Value read | |
126 | * | |
127 | * Send MIPI DCS read command to the controller. | |
128 | * | |
129 | * Returns: | |
130 | * Zero on success, negative error code on failure. | |
131 | */ | |
36b50572 | 132 | int mipi_dbi_command_read(struct mipi_dbi *dbi, u8 cmd, u8 *val) |
02dd95fe | 133 | { |
36b50572 | 134 | if (!dbi->read_commands) |
02dd95fe NT |
135 | return -EACCES; |
136 | ||
36b50572 | 137 | if (!mipi_dbi_command_is_read(dbi, cmd)) |
02dd95fe NT |
138 | return -EINVAL; |
139 | ||
36b50572 | 140 | return mipi_dbi_command_buf(dbi, cmd, val, 1); |
02dd95fe NT |
141 | } |
142 | EXPORT_SYMBOL(mipi_dbi_command_read); | |
143 | ||
144 | /** | |
145 | * mipi_dbi_command_buf - MIPI DCS command with parameter(s) in an array | |
36b50572 | 146 | * @dbi: MIPI DBI structure |
02dd95fe NT |
147 | * @cmd: Command |
148 | * @data: Parameter buffer | |
149 | * @len: Buffer length | |
150 | * | |
151 | * Returns: | |
152 | * Zero on success, negative error code on failure. | |
153 | */ | |
36b50572 | 154 | int mipi_dbi_command_buf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len) |
02dd95fe | 155 | { |
a89bfc5d | 156 | u8 *cmdbuf; |
02dd95fe NT |
157 | int ret; |
158 | ||
a89bfc5d NT |
159 | /* SPI requires dma-safe buffers */ |
160 | cmdbuf = kmemdup(&cmd, 1, GFP_KERNEL); | |
161 | if (!cmdbuf) | |
162 | return -ENOMEM; | |
163 | ||
36b50572 NT |
164 | mutex_lock(&dbi->cmdlock); |
165 | ret = dbi->command(dbi, cmdbuf, data, len); | |
166 | mutex_unlock(&dbi->cmdlock); | |
02dd95fe | 167 | |
a89bfc5d NT |
168 | kfree(cmdbuf); |
169 | ||
02dd95fe NT |
170 | return ret; |
171 | } | |
172 | EXPORT_SYMBOL(mipi_dbi_command_buf); | |
173 | ||
a89bfc5d | 174 | /* This should only be used by mipi_dbi_command() */ |
f019190b GU |
175 | int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data, |
176 | size_t len) | |
a89bfc5d NT |
177 | { |
178 | u8 *buf; | |
179 | int ret; | |
180 | ||
181 | buf = kmemdup(data, len, GFP_KERNEL); | |
182 | if (!buf) | |
183 | return -ENOMEM; | |
184 | ||
36b50572 | 185 | ret = mipi_dbi_command_buf(dbi, cmd, buf, len); |
a89bfc5d NT |
186 | |
187 | kfree(buf); | |
188 | ||
189 | return ret; | |
190 | } | |
191 | EXPORT_SYMBOL(mipi_dbi_command_stackbuf); | |
192 | ||
13deee81 DL |
193 | /** |
194 | * mipi_dbi_buf_copy - Copy a framebuffer, transforming it if necessary | |
195 | * @dst: The destination buffer | |
b5f636e6 | 196 | * @src: The source buffer |
13deee81 DL |
197 | * @fb: The source framebuffer |
198 | * @clip: Clipping rectangle of the area to be copied | |
199 | * @swap: When true, swap MSB/LSB of 16-bit values | |
4cd24d4b | 200 | * @fmtcnv_state: Format-conversion state |
13deee81 DL |
201 | * |
202 | * Returns: | |
203 | * Zero on success, negative error code on failure. | |
204 | */ | |
b5f636e6 | 205 | int mipi_dbi_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *fb, |
4cd24d4b TZ |
206 | struct drm_rect *clip, bool swap, |
207 | struct drm_format_conv_state *fmtcnv_state) | |
02dd95fe | 208 | { |
4aebb790 | 209 | struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); |
7c9f1312 | 210 | struct drm_gem_object *gem = drm_gem_fb_get_obj(fb, 0); |
edbe262a | 211 | struct iosys_map dst_map = IOSYS_MAP_INIT_VADDR(dst); |
08971eea | 212 | int ret; |
02dd95fe | 213 | |
08971eea TZ |
214 | ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); |
215 | if (ret) | |
216 | return ret; | |
c47160d8 | 217 | |
02dd95fe NT |
218 | switch (fb->format->format) { |
219 | case DRM_FORMAT_RGB565: | |
220 | if (swap) | |
4cd24d4b TZ |
221 | drm_fb_swab(&dst_map, NULL, src, fb, clip, !gem->import_attach, |
222 | fmtcnv_state); | |
02dd95fe | 223 | else |
b5f636e6 | 224 | drm_fb_memcpy(&dst_map, NULL, src, fb, clip); |
02dd95fe | 225 | break; |
4aebb790 NT |
226 | case DRM_FORMAT_RGB888: |
227 | drm_fb_memcpy(&dst_map, NULL, src, fb, clip); | |
228 | break; | |
02dd95fe | 229 | case DRM_FORMAT_XRGB8888: |
4aebb790 NT |
230 | switch (dbidev->pixel_format) { |
231 | case DRM_FORMAT_RGB565: | |
232 | drm_fb_xrgb8888_to_rgb565(&dst_map, NULL, src, fb, clip, fmtcnv_state, swap); | |
233 | break; | |
234 | case DRM_FORMAT_RGB888: | |
235 | drm_fb_xrgb8888_to_rgb888(&dst_map, NULL, src, fb, clip, fmtcnv_state); | |
236 | break; | |
237 | } | |
02dd95fe NT |
238 | break; |
239 | default: | |
92f1d09c SA |
240 | drm_err_once(fb->dev, "Format is not supported: %p4cc\n", |
241 | &fb->format->format); | |
c47160d8 | 242 | ret = -EINVAL; |
02dd95fe NT |
243 | } |
244 | ||
08971eea TZ |
245 | drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); |
246 | ||
02dd95fe NT |
247 | return ret; |
248 | } | |
13deee81 | 249 | EXPORT_SYMBOL(mipi_dbi_buf_copy); |
02dd95fe | 250 | |
f41a8a69 GU |
251 | static void mipi_dbi_set_window_address(struct mipi_dbi_dev *dbidev, |
252 | unsigned int xs, unsigned int xe, | |
253 | unsigned int ys, unsigned int ye) | |
254 | { | |
255 | struct mipi_dbi *dbi = &dbidev->dbi; | |
256 | ||
257 | xs += dbidev->left_offset; | |
258 | xe += dbidev->left_offset; | |
259 | ys += dbidev->top_offset; | |
260 | ye += dbidev->top_offset; | |
261 | ||
262 | mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS, (xs >> 8) & 0xff, | |
263 | xs & 0xff, (xe >> 8) & 0xff, xe & 0xff); | |
264 | mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS, (ys >> 8) & 0xff, | |
265 | ys & 0xff, (ye >> 8) & 0xff, ye & 0xff); | |
266 | } | |
267 | ||
b5f636e6 | 268 | static void mipi_dbi_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, |
4cd24d4b | 269 | struct drm_rect *rect, struct drm_format_conv_state *fmtcnv_state) |
02dd95fe | 270 | { |
84137b86 | 271 | struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); |
af741381 NT |
272 | unsigned int height = rect->y2 - rect->y1; |
273 | unsigned int width = rect->x2 - rect->x1; | |
4aebb790 | 274 | const struct drm_format_info *dst_format; |
84137b86 | 275 | struct mipi_dbi *dbi = &dbidev->dbi; |
36b50572 | 276 | bool swap = dbi->swap_bytes; |
3ea44105 | 277 | int ret = 0; |
4aebb790 | 278 | size_t len; |
02dd95fe NT |
279 | bool full; |
280 | void *tr; | |
281 | ||
af741381 | 282 | full = width == fb->width && height == fb->height; |
02dd95fe | 283 | |
b051b345 | 284 | DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); |
02dd95fe | 285 | |
36b50572 | 286 | if (!dbi->dc || !full || swap || |
02dd95fe | 287 | fb->format->format == DRM_FORMAT_XRGB8888) { |
440961d2 | 288 | tr = dbidev->tx_buf; |
4cd24d4b | 289 | ret = mipi_dbi_buf_copy(tr, src, fb, rect, swap, fmtcnv_state); |
02dd95fe | 290 | if (ret) |
af741381 | 291 | goto err_msg; |
02dd95fe | 292 | } else { |
b5f636e6 | 293 | tr = src->vaddr; /* TODO: Use mapping abstraction properly */ |
02dd95fe NT |
294 | } |
295 | ||
f41a8a69 GU |
296 | mipi_dbi_set_window_address(dbidev, rect->x1, rect->x2 - 1, rect->y1, |
297 | rect->y2 - 1); | |
02dd95fe | 298 | |
4aebb790 NT |
299 | if (fb->format->format == DRM_FORMAT_XRGB8888) |
300 | dst_format = drm_format_info(dbidev->pixel_format); | |
301 | else | |
302 | dst_format = fb->format; | |
303 | len = drm_format_info_min_pitch(dst_format, 0, width) * height; | |
304 | ||
305 | ret = mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, tr, len); | |
af741381 NT |
306 | err_msg: |
307 | if (ret) | |
6d45fff5 | 308 | drm_err_once(fb->dev, "Failed to update display %d\n", ret); |
02dd95fe NT |
309 | } |
310 | ||
216b9bba TZ |
311 | /** |
312 | * mipi_dbi_pipe_mode_valid - MIPI DBI mode-valid helper | |
313 | * @pipe: Simple display pipe | |
314 | * @mode: The mode to test | |
315 | * | |
316 | * This function validates a given display mode against the MIPI DBI's hardware | |
317 | * display. Drivers can use this as their &drm_simple_display_pipe_funcs->mode_valid | |
318 | * callback. | |
319 | */ | |
320 | enum drm_mode_status mipi_dbi_pipe_mode_valid(struct drm_simple_display_pipe *pipe, | |
321 | const struct drm_display_mode *mode) | |
322 | { | |
323 | struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); | |
324 | ||
325 | return drm_crtc_helper_mode_valid_fixed(&pipe->crtc, mode, &dbidev->mode); | |
326 | } | |
327 | EXPORT_SYMBOL(mipi_dbi_pipe_mode_valid); | |
328 | ||
af741381 NT |
329 | /** |
330 | * mipi_dbi_pipe_update - Display pipe update helper | |
331 | * @pipe: Simple display pipe | |
332 | * @old_state: Old plane state | |
333 | * | |
334 | * This function handles framebuffer flushing and vblank events. Drivers can use | |
335 | * this as their &drm_simple_display_pipe_funcs->update callback. | |
336 | */ | |
337 | void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe, | |
338 | struct drm_plane_state *old_state) | |
339 | { | |
340 | struct drm_plane_state *state = pipe->plane.state; | |
69c63e88 | 341 | struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); |
b5f636e6 | 342 | struct drm_framebuffer *fb = state->fb; |
af741381 | 343 | struct drm_rect rect; |
3ea44105 | 344 | int idx; |
af741381 | 345 | |
7e06886b DV |
346 | if (!pipe->crtc.state->active) |
347 | return; | |
348 | ||
b5f636e6 TZ |
349 | if (WARN_ON(!fb)) |
350 | return; | |
351 | ||
3ea44105 TZ |
352 | if (!drm_dev_enter(fb->dev, &idx)) |
353 | return; | |
354 | ||
af741381 | 355 | if (drm_atomic_helper_damage_merged(old_state, state, &rect)) |
4cd24d4b TZ |
356 | mipi_dbi_fb_dirty(&shadow_plane_state->data[0], fb, &rect, |
357 | &shadow_plane_state->fmtcnv_state); | |
3ea44105 TZ |
358 | |
359 | drm_dev_exit(idx); | |
af741381 NT |
360 | } |
361 | EXPORT_SYMBOL(mipi_dbi_pipe_update); | |
02dd95fe | 362 | |
22edc8d3 NT |
363 | /** |
364 | * mipi_dbi_enable_flush - MIPI DBI enable helper | |
440961d2 | 365 | * @dbidev: MIPI DBI device structure |
5685ca0c NT |
366 | * @crtc_state: CRTC state |
367 | * @plane_state: Plane state | |
22edc8d3 | 368 | * |
7e06886b DV |
369 | * Flushes the whole framebuffer and enables the backlight. Drivers can use this |
370 | * in their &drm_simple_display_pipe_funcs->enable callback. | |
af741381 NT |
371 | * |
372 | * Note: Drivers which don't use mipi_dbi_pipe_update() because they have custom | |
373 | * framebuffer flushing, can't use this function since they both use the same | |
374 | * flushing code. | |
22edc8d3 | 375 | */ |
84137b86 | 376 | void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev, |
e85d3006 VS |
377 | struct drm_crtc_state *crtc_state, |
378 | struct drm_plane_state *plane_state) | |
22edc8d3 | 379 | { |
69c63e88 | 380 | struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); |
e85d3006 | 381 | struct drm_framebuffer *fb = plane_state->fb; |
af741381 NT |
382 | struct drm_rect rect = { |
383 | .x1 = 0, | |
384 | .x2 = fb->width, | |
385 | .y1 = 0, | |
386 | .y2 = fb->height, | |
387 | }; | |
69c63e88 | 388 | int idx; |
9d5645ad | 389 | |
440961d2 | 390 | if (!drm_dev_enter(&dbidev->drm, &idx)) |
9d5645ad | 391 | return; |
22edc8d3 | 392 | |
4cd24d4b TZ |
393 | mipi_dbi_fb_dirty(&shadow_plane_state->data[0], fb, &rect, |
394 | &shadow_plane_state->fmtcnv_state); | |
440961d2 | 395 | backlight_enable(dbidev->backlight); |
9d5645ad NT |
396 | |
397 | drm_dev_exit(idx); | |
22edc8d3 NT |
398 | } |
399 | EXPORT_SYMBOL(mipi_dbi_enable_flush); | |
400 | ||
84137b86 | 401 | static void mipi_dbi_blank(struct mipi_dbi_dev *dbidev) |
02dd95fe | 402 | { |
440961d2 | 403 | struct drm_device *drm = &dbidev->drm; |
02dd95fe NT |
404 | u16 height = drm->mode_config.min_height; |
405 | u16 width = drm->mode_config.min_width; | |
84137b86 | 406 | struct mipi_dbi *dbi = &dbidev->dbi; |
02dd95fe | 407 | size_t len = width * height * 2; |
9d5645ad NT |
408 | int idx; |
409 | ||
410 | if (!drm_dev_enter(drm, &idx)) | |
411 | return; | |
02dd95fe | 412 | |
440961d2 | 413 | memset(dbidev->tx_buf, 0, len); |
02dd95fe | 414 | |
f41a8a69 | 415 | mipi_dbi_set_window_address(dbidev, 0, width - 1, 0, height - 1); |
36b50572 | 416 | mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, |
440961d2 | 417 | (u8 *)dbidev->tx_buf, len); |
9d5645ad NT |
418 | |
419 | drm_dev_exit(idx); | |
02dd95fe NT |
420 | } |
421 | ||
422 | /** | |
423 | * mipi_dbi_pipe_disable - MIPI DBI pipe disable helper | |
424 | * @pipe: Display pipe | |
425 | * | |
f730eceb NT |
426 | * This function disables backlight if present, if not the display memory is |
427 | * blanked. The regulator is disabled if in use. Drivers can use this as their | |
02dd95fe NT |
428 | * &drm_simple_display_pipe_funcs->disable callback. |
429 | */ | |
430 | void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe) | |
431 | { | |
84137b86 | 432 | struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); |
02dd95fe NT |
433 | |
434 | DRM_DEBUG_KMS("\n"); | |
435 | ||
440961d2 NT |
436 | if (dbidev->backlight) |
437 | backlight_disable(dbidev->backlight); | |
02dd95fe | 438 | else |
440961d2 | 439 | mipi_dbi_blank(dbidev); |
f730eceb | 440 | |
440961d2 NT |
441 | if (dbidev->regulator) |
442 | regulator_disable(dbidev->regulator); | |
3b1fb8b3 OP |
443 | if (dbidev->io_regulator) |
444 | regulator_disable(dbidev->io_regulator); | |
02dd95fe NT |
445 | } |
446 | EXPORT_SYMBOL(mipi_dbi_pipe_disable); | |
447 | ||
e7caf04d TZ |
448 | /** |
449 | * mipi_dbi_pipe_begin_fb_access - MIPI DBI pipe begin-access helper | |
450 | * @pipe: Display pipe | |
451 | * @plane_state: Plane state | |
452 | * | |
453 | * This function implements struct &drm_simple_display_funcs.begin_fb_access. | |
454 | * | |
455 | * See drm_gem_begin_shadow_fb_access() for details and mipi_dbi_pipe_cleanup_fb() | |
456 | * for cleanup. | |
457 | * | |
458 | * Returns: | |
459 | * 0 on success, or a negative errno code otherwise. | |
460 | */ | |
461 | int mipi_dbi_pipe_begin_fb_access(struct drm_simple_display_pipe *pipe, | |
462 | struct drm_plane_state *plane_state) | |
463 | { | |
464 | return drm_gem_begin_shadow_fb_access(&pipe->plane, plane_state); | |
465 | } | |
466 | EXPORT_SYMBOL(mipi_dbi_pipe_begin_fb_access); | |
467 | ||
468 | /** | |
469 | * mipi_dbi_pipe_end_fb_access - MIPI DBI pipe end-access helper | |
470 | * @pipe: Display pipe | |
471 | * @plane_state: Plane state | |
472 | * | |
473 | * This function implements struct &drm_simple_display_funcs.end_fb_access. | |
474 | * | |
475 | * See mipi_dbi_pipe_begin_fb_access(). | |
476 | */ | |
477 | void mipi_dbi_pipe_end_fb_access(struct drm_simple_display_pipe *pipe, | |
478 | struct drm_plane_state *plane_state) | |
479 | { | |
480 | drm_gem_end_shadow_fb_access(&pipe->plane, plane_state); | |
481 | } | |
482 | EXPORT_SYMBOL(mipi_dbi_pipe_end_fb_access); | |
483 | ||
484 | /** | |
485 | * mipi_dbi_pipe_reset_plane - MIPI DBI plane-reset helper | |
486 | * @pipe: Display pipe | |
487 | * | |
488 | * This function implements struct &drm_simple_display_funcs.reset_plane | |
489 | * for MIPI DBI planes. | |
490 | */ | |
491 | void mipi_dbi_pipe_reset_plane(struct drm_simple_display_pipe *pipe) | |
492 | { | |
493 | drm_gem_reset_shadow_plane(&pipe->plane); | |
494 | } | |
495 | EXPORT_SYMBOL(mipi_dbi_pipe_reset_plane); | |
496 | ||
497 | /** | |
498 | * mipi_dbi_pipe_duplicate_plane_state - duplicates MIPI DBI plane state | |
499 | * @pipe: Display pipe | |
500 | * | |
501 | * This function implements struct &drm_simple_display_funcs.duplicate_plane_state | |
502 | * for MIPI DBI planes. | |
503 | * | |
504 | * See drm_gem_duplicate_shadow_plane_state() for additional details. | |
505 | * | |
506 | * Returns: | |
507 | * A pointer to a new plane state on success, or NULL otherwise. | |
508 | */ | |
509 | struct drm_plane_state *mipi_dbi_pipe_duplicate_plane_state(struct drm_simple_display_pipe *pipe) | |
510 | { | |
511 | return drm_gem_duplicate_shadow_plane_state(&pipe->plane); | |
512 | } | |
513 | EXPORT_SYMBOL(mipi_dbi_pipe_duplicate_plane_state); | |
514 | ||
515 | /** | |
516 | * mipi_dbi_pipe_destroy_plane_state - cleans up MIPI DBI plane state | |
517 | * @pipe: Display pipe | |
518 | * @plane_state: Plane state | |
519 | * | |
520 | * This function implements struct drm_simple_display_funcs.destroy_plane_state | |
521 | * for MIPI DBI planes. | |
522 | * | |
523 | * See drm_gem_destroy_shadow_plane_state() for additional details. | |
524 | */ | |
525 | void mipi_dbi_pipe_destroy_plane_state(struct drm_simple_display_pipe *pipe, | |
526 | struct drm_plane_state *plane_state) | |
527 | { | |
528 | drm_gem_destroy_shadow_plane_state(&pipe->plane, plane_state); | |
529 | } | |
530 | EXPORT_SYMBOL(mipi_dbi_pipe_destroy_plane_state); | |
531 | ||
710ae47d NT |
532 | static int mipi_dbi_connector_get_modes(struct drm_connector *connector) |
533 | { | |
84137b86 | 534 | struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(connector->dev); |
710ae47d | 535 | |
d25654b3 | 536 | return drm_connector_helper_get_modes_fixed(connector, &dbidev->mode); |
710ae47d NT |
537 | } |
538 | ||
539 | static const struct drm_connector_helper_funcs mipi_dbi_connector_hfuncs = { | |
540 | .get_modes = mipi_dbi_connector_get_modes, | |
541 | }; | |
542 | ||
543 | static const struct drm_connector_funcs mipi_dbi_connector_funcs = { | |
544 | .reset = drm_atomic_helper_connector_reset, | |
545 | .fill_modes = drm_helper_probe_single_connector_modes, | |
546 | .destroy = drm_connector_cleanup, | |
547 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
548 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
549 | }; | |
550 | ||
551 | static int mipi_dbi_rotate_mode(struct drm_display_mode *mode, | |
552 | unsigned int rotation) | |
553 | { | |
554 | if (rotation == 0 || rotation == 180) { | |
555 | return 0; | |
556 | } else if (rotation == 90 || rotation == 270) { | |
557 | swap(mode->hdisplay, mode->vdisplay); | |
558 | swap(mode->hsync_start, mode->vsync_start); | |
559 | swap(mode->hsync_end, mode->vsync_end); | |
560 | swap(mode->htotal, mode->vtotal); | |
561 | swap(mode->width_mm, mode->height_mm); | |
562 | return 0; | |
563 | } else { | |
564 | return -EINVAL; | |
565 | } | |
566 | } | |
567 | ||
3eba3922 NT |
568 | static const struct drm_mode_config_funcs mipi_dbi_mode_config_funcs = { |
569 | .fb_create = drm_gem_fb_create_with_dirty, | |
570 | .atomic_check = drm_atomic_helper_check, | |
571 | .atomic_commit = drm_atomic_helper_commit, | |
572 | }; | |
573 | ||
02dd95fe NT |
574 | static const uint32_t mipi_dbi_formats[] = { |
575 | DRM_FORMAT_RGB565, | |
576 | DRM_FORMAT_XRGB8888, | |
577 | }; | |
578 | ||
579 | /** | |
84137b86 | 580 | * mipi_dbi_dev_init_with_formats - MIPI DBI device initialization with custom formats |
440961d2 | 581 | * @dbidev: MIPI DBI device structure to initialize |
3eba3922 | 582 | * @funcs: Display pipe functions |
cc431212 NT |
583 | * @formats: Array of supported formats (DRM_FORMAT\_\*). |
584 | * @format_count: Number of elements in @formats | |
02dd95fe NT |
585 | * @mode: Display mode |
586 | * @rotation: Initial rotation in degrees Counter Clock Wise | |
cc431212 | 587 | * @tx_buf_size: Allocate a transmit buffer of this size. |
02dd95fe | 588 | * |
3eba3922 NT |
589 | * This function sets up a &drm_simple_display_pipe with a &drm_connector that |
590 | * has one fixed &drm_display_mode which is rotated according to @rotation. | |
591 | * This mode is used to set the mode config min/max width/height properties. | |
02dd95fe | 592 | * |
4aebb790 | 593 | * Use mipi_dbi_dev_init() if you want native RGB565 and emulated XRGB8888 format. |
cc431212 NT |
594 | * |
595 | * Note: | |
596 | * Some of the helper functions expects RGB565 to be the default format and the | |
597 | * transmit buffer sized to fit that. | |
02dd95fe | 598 | * |
02dd95fe NT |
599 | * Returns: |
600 | * Zero on success, negative error code on failure. | |
601 | */ | |
84137b86 NT |
602 | int mipi_dbi_dev_init_with_formats(struct mipi_dbi_dev *dbidev, |
603 | const struct drm_simple_display_pipe_funcs *funcs, | |
604 | const uint32_t *formats, unsigned int format_count, | |
605 | const struct drm_display_mode *mode, | |
606 | unsigned int rotation, size_t tx_buf_size) | |
02dd95fe | 607 | { |
710ae47d NT |
608 | static const uint64_t modifiers[] = { |
609 | DRM_FORMAT_MOD_LINEAR, | |
610 | DRM_FORMAT_MOD_INVALID | |
611 | }; | |
440961d2 | 612 | struct drm_device *drm = &dbidev->drm; |
02dd95fe NT |
613 | int ret; |
614 | ||
84137b86 | 615 | if (!dbidev->dbi.command) |
02dd95fe NT |
616 | return -EINVAL; |
617 | ||
3421a6c4 | 618 | ret = drmm_mode_config_init(drm); |
53bdebf7 DV |
619 | if (ret) |
620 | return ret; | |
621 | ||
440961d2 NT |
622 | dbidev->tx_buf = devm_kmalloc(drm->dev, tx_buf_size, GFP_KERNEL); |
623 | if (!dbidev->tx_buf) | |
02dd95fe NT |
624 | return -ENOMEM; |
625 | ||
440961d2 NT |
626 | drm_mode_copy(&dbidev->mode, mode); |
627 | ret = mipi_dbi_rotate_mode(&dbidev->mode, rotation); | |
710ae47d NT |
628 | if (ret) { |
629 | DRM_ERROR("Illegal rotation value %u\n", rotation); | |
630 | return -EINVAL; | |
631 | } | |
632 | ||
440961d2 NT |
633 | drm_connector_helper_add(&dbidev->connector, &mipi_dbi_connector_hfuncs); |
634 | ret = drm_connector_init(drm, &dbidev->connector, &mipi_dbi_connector_funcs, | |
710ae47d NT |
635 | DRM_MODE_CONNECTOR_SPI); |
636 | if (ret) | |
637 | return ret; | |
638 | ||
440961d2 NT |
639 | ret = drm_simple_display_pipe_init(drm, &dbidev->pipe, funcs, formats, format_count, |
640 | modifiers, &dbidev->connector); | |
02dd95fe NT |
641 | if (ret) |
642 | return ret; | |
643 | ||
440961d2 | 644 | drm_plane_enable_fb_damage_clips(&dbidev->pipe.plane); |
af741381 | 645 | |
3eba3922 | 646 | drm->mode_config.funcs = &mipi_dbi_mode_config_funcs; |
440961d2 NT |
647 | drm->mode_config.min_width = dbidev->mode.hdisplay; |
648 | drm->mode_config.max_width = dbidev->mode.hdisplay; | |
649 | drm->mode_config.min_height = dbidev->mode.vdisplay; | |
650 | drm->mode_config.max_height = dbidev->mode.vdisplay; | |
651 | dbidev->rotation = rotation; | |
4aebb790 NT |
652 | dbidev->pixel_format = formats[0]; |
653 | if (formats[0] == DRM_FORMAT_RGB888) | |
654 | dbidev->dbi.write_memory_bpw = 8; | |
02dd95fe | 655 | |
cc431212 | 656 | DRM_DEBUG_KMS("rotation = %u\n", rotation); |
02dd95fe NT |
657 | |
658 | return 0; | |
659 | } | |
84137b86 | 660 | EXPORT_SYMBOL(mipi_dbi_dev_init_with_formats); |
cc431212 NT |
661 | |
662 | /** | |
84137b86 | 663 | * mipi_dbi_dev_init - MIPI DBI device initialization |
440961d2 | 664 | * @dbidev: MIPI DBI device structure to initialize |
cc431212 NT |
665 | * @funcs: Display pipe functions |
666 | * @mode: Display mode | |
667 | * @rotation: Initial rotation in degrees Counter Clock Wise | |
668 | * | |
669 | * This function sets up a &drm_simple_display_pipe with a &drm_connector that | |
670 | * has one fixed &drm_display_mode which is rotated according to @rotation. | |
671 | * This mode is used to set the mode config min/max width/height properties. | |
672 | * Additionally &mipi_dbi.tx_buf is allocated. | |
673 | * | |
674 | * Supported formats: Native RGB565 and emulated XRGB8888. | |
675 | * | |
676 | * Returns: | |
677 | * Zero on success, negative error code on failure. | |
678 | */ | |
84137b86 NT |
679 | int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev, |
680 | const struct drm_simple_display_pipe_funcs *funcs, | |
681 | const struct drm_display_mode *mode, unsigned int rotation) | |
cc431212 NT |
682 | { |
683 | size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16); | |
684 | ||
440961d2 | 685 | dbidev->drm.mode_config.preferred_depth = 16; |
cc431212 | 686 | |
84137b86 NT |
687 | return mipi_dbi_dev_init_with_formats(dbidev, funcs, mipi_dbi_formats, |
688 | ARRAY_SIZE(mipi_dbi_formats), mode, | |
689 | rotation, bufsize); | |
cc431212 | 690 | } |
84137b86 | 691 | EXPORT_SYMBOL(mipi_dbi_dev_init); |
02dd95fe NT |
692 | |
693 | /** | |
694 | * mipi_dbi_hw_reset - Hardware reset of controller | |
36b50572 | 695 | * @dbi: MIPI DBI structure |
02dd95fe NT |
696 | * |
697 | * Reset controller if the &mipi_dbi->reset gpio is set. | |
698 | */ | |
36b50572 | 699 | void mipi_dbi_hw_reset(struct mipi_dbi *dbi) |
02dd95fe | 700 | { |
36b50572 | 701 | if (!dbi->reset) |
02dd95fe NT |
702 | return; |
703 | ||
36b50572 | 704 | gpiod_set_value_cansleep(dbi->reset, 0); |
10399b22 | 705 | usleep_range(20, 1000); |
36b50572 | 706 | gpiod_set_value_cansleep(dbi->reset, 1); |
02dd95fe NT |
707 | msleep(120); |
708 | } | |
709 | EXPORT_SYMBOL(mipi_dbi_hw_reset); | |
710 | ||
711 | /** | |
712 | * mipi_dbi_display_is_on - Check if display is on | |
36b50572 | 713 | * @dbi: MIPI DBI structure |
02dd95fe NT |
714 | * |
715 | * This function checks the Power Mode register (if readable) to see if | |
716 | * display output is turned on. This can be used to see if the bootloader | |
717 | * has already turned on the display avoiding flicker when the pipeline is | |
718 | * enabled. | |
719 | * | |
720 | * Returns: | |
721 | * true if the display can be verified to be on, false otherwise. | |
722 | */ | |
36b50572 | 723 | bool mipi_dbi_display_is_on(struct mipi_dbi *dbi) |
02dd95fe NT |
724 | { |
725 | u8 val; | |
726 | ||
36b50572 | 727 | if (mipi_dbi_command_read(dbi, MIPI_DCS_GET_POWER_MODE, &val)) |
02dd95fe NT |
728 | return false; |
729 | ||
730 | val &= ~DCS_POWER_MODE_RESERVED_MASK; | |
731 | ||
070ab128 | 732 | /* The poweron/reset value is 08h DCS_POWER_MODE_DISPLAY_NORMAL_MODE */ |
02dd95fe NT |
733 | if (val != (DCS_POWER_MODE_DISPLAY | |
734 | DCS_POWER_MODE_DISPLAY_NORMAL_MODE | DCS_POWER_MODE_SLEEP_MODE)) | |
735 | return false; | |
736 | ||
737 | DRM_DEBUG_DRIVER("Display is ON\n"); | |
738 | ||
739 | return true; | |
740 | } | |
741 | EXPORT_SYMBOL(mipi_dbi_display_is_on); | |
742 | ||
84137b86 | 743 | static int mipi_dbi_poweron_reset_conditional(struct mipi_dbi_dev *dbidev, bool cond) |
070ab128 | 744 | { |
440961d2 | 745 | struct device *dev = dbidev->drm.dev; |
84137b86 | 746 | struct mipi_dbi *dbi = &dbidev->dbi; |
070ab128 NT |
747 | int ret; |
748 | ||
440961d2 NT |
749 | if (dbidev->regulator) { |
750 | ret = regulator_enable(dbidev->regulator); | |
070ab128 NT |
751 | if (ret) { |
752 | DRM_DEV_ERROR(dev, "Failed to enable regulator (%d)\n", ret); | |
753 | return ret; | |
754 | } | |
755 | } | |
756 | ||
3b1fb8b3 OP |
757 | if (dbidev->io_regulator) { |
758 | ret = regulator_enable(dbidev->io_regulator); | |
759 | if (ret) { | |
760 | DRM_DEV_ERROR(dev, "Failed to enable I/O regulator (%d)\n", ret); | |
761 | if (dbidev->regulator) | |
762 | regulator_disable(dbidev->regulator); | |
763 | return ret; | |
764 | } | |
765 | } | |
766 | ||
36b50572 | 767 | if (cond && mipi_dbi_display_is_on(dbi)) |
070ab128 NT |
768 | return 1; |
769 | ||
36b50572 NT |
770 | mipi_dbi_hw_reset(dbi); |
771 | ret = mipi_dbi_command(dbi, MIPI_DCS_SOFT_RESET); | |
070ab128 NT |
772 | if (ret) { |
773 | DRM_DEV_ERROR(dev, "Failed to send reset command (%d)\n", ret); | |
440961d2 NT |
774 | if (dbidev->regulator) |
775 | regulator_disable(dbidev->regulator); | |
3b1fb8b3 OP |
776 | if (dbidev->io_regulator) |
777 | regulator_disable(dbidev->io_regulator); | |
070ab128 NT |
778 | return ret; |
779 | } | |
780 | ||
781 | /* | |
782 | * If we did a hw reset, we know the controller is in Sleep mode and | |
783 | * per MIPI DSC spec should wait 5ms after soft reset. If we didn't, | |
784 | * we assume worst case and wait 120ms. | |
785 | */ | |
36b50572 | 786 | if (dbi->reset) |
070ab128 NT |
787 | usleep_range(5000, 20000); |
788 | else | |
789 | msleep(120); | |
790 | ||
791 | return 0; | |
792 | } | |
793 | ||
794 | /** | |
795 | * mipi_dbi_poweron_reset - MIPI DBI poweron and reset | |
440961d2 | 796 | * @dbidev: MIPI DBI device structure |
070ab128 NT |
797 | * |
798 | * This function enables the regulator if used and does a hardware and software | |
799 | * reset. | |
800 | * | |
801 | * Returns: | |
802 | * Zero on success, or a negative error code. | |
803 | */ | |
84137b86 | 804 | int mipi_dbi_poweron_reset(struct mipi_dbi_dev *dbidev) |
070ab128 | 805 | { |
440961d2 | 806 | return mipi_dbi_poweron_reset_conditional(dbidev, false); |
070ab128 NT |
807 | } |
808 | EXPORT_SYMBOL(mipi_dbi_poweron_reset); | |
809 | ||
810 | /** | |
811 | * mipi_dbi_poweron_conditional_reset - MIPI DBI poweron and conditional reset | |
440961d2 | 812 | * @dbidev: MIPI DBI device structure |
070ab128 NT |
813 | * |
814 | * This function enables the regulator if used and if the display is off, it | |
815 | * does a hardware and software reset. If mipi_dbi_display_is_on() determines | |
816 | * that the display is on, no reset is performed. | |
817 | * | |
818 | * Returns: | |
819 | * Zero if the controller was reset, 1 if the display was already on, or a | |
820 | * negative error code. | |
821 | */ | |
84137b86 | 822 | int mipi_dbi_poweron_conditional_reset(struct mipi_dbi_dev *dbidev) |
070ab128 | 823 | { |
440961d2 | 824 | return mipi_dbi_poweron_reset_conditional(dbidev, true); |
070ab128 NT |
825 | } |
826 | EXPORT_SYMBOL(mipi_dbi_poweron_conditional_reset); | |
827 | ||
02dd95fe NT |
828 | #if IS_ENABLED(CONFIG_SPI) |
829 | ||
13deee81 DL |
830 | /** |
831 | * mipi_dbi_spi_cmd_max_speed - get the maximum SPI bus speed | |
832 | * @spi: SPI device | |
833 | * @len: The transfer buffer length. | |
834 | * | |
02dd95fe NT |
835 | * Many controllers have a max speed of 10MHz, but can be pushed way beyond |
836 | * that. Increase reliability by running pixel data at max speed and the rest | |
837 | * at 10MHz, preventing transfer glitches from messing up the init settings. | |
838 | */ | |
13deee81 | 839 | u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len) |
02dd95fe NT |
840 | { |
841 | if (len > 64) | |
842 | return 0; /* use default */ | |
843 | ||
844 | return min_t(u32, 10000000, spi->max_speed_hz); | |
845 | } | |
13deee81 | 846 | EXPORT_SYMBOL(mipi_dbi_spi_cmd_max_speed); |
02dd95fe NT |
847 | |
848 | /* | |
849 | * MIPI DBI Type C Option 1 | |
850 | * | |
851 | * If the SPI controller doesn't have 9 bits per word support, | |
852 | * use blocks of 9 bytes to send 8x 9-bit words using a 8-bit SPI transfer. | |
853 | * Pad partial blocks with MIPI_DCS_NOP (zero). | |
854 | * This is how the D/C bit (x) is added: | |
855 | * x7654321 | |
856 | * 0x765432 | |
857 | * 10x76543 | |
858 | * 210x7654 | |
859 | * 3210x765 | |
860 | * 43210x76 | |
861 | * 543210x7 | |
862 | * 6543210x | |
863 | * 76543210 | |
864 | */ | |
865 | ||
36b50572 | 866 | static int mipi_dbi_spi1e_transfer(struct mipi_dbi *dbi, int dc, |
02dd95fe NT |
867 | const void *buf, size_t len, |
868 | unsigned int bpw) | |
869 | { | |
f34f014c | 870 | bool swap_bytes = (bpw == 16); |
36b50572 NT |
871 | size_t chunk, max_chunk = dbi->tx_buf9_len; |
872 | struct spi_device *spi = dbi->spi; | |
02dd95fe | 873 | struct spi_transfer tr = { |
36b50572 | 874 | .tx_buf = dbi->tx_buf9, |
02dd95fe NT |
875 | .bits_per_word = 8, |
876 | }; | |
877 | struct spi_message m; | |
878 | const u8 *src = buf; | |
879 | int i, ret; | |
880 | u8 *dst; | |
881 | ||
f0a8f533 | 882 | if (drm_debug_enabled(DRM_UT_DRIVER)) |
02dd95fe NT |
883 | pr_debug("[drm:%s] dc=%d, max_chunk=%zu, transfers:\n", |
884 | __func__, dc, max_chunk); | |
885 | ||
886 | tr.speed_hz = mipi_dbi_spi_cmd_max_speed(spi, len); | |
887 | spi_message_init_with_transfers(&m, &tr, 1); | |
888 | ||
889 | if (!dc) { | |
890 | if (WARN_ON_ONCE(len != 1)) | |
891 | return -EINVAL; | |
892 | ||
893 | /* Command: pad no-op's (zeroes) at beginning of block */ | |
36b50572 | 894 | dst = dbi->tx_buf9; |
02dd95fe NT |
895 | memset(dst, 0, 9); |
896 | dst[8] = *src; | |
897 | tr.len = 9; | |
898 | ||
02dd95fe NT |
899 | return spi_sync(spi, &m); |
900 | } | |
901 | ||
902 | /* max with room for adding one bit per byte */ | |
903 | max_chunk = max_chunk / 9 * 8; | |
904 | /* but no bigger than len */ | |
905 | max_chunk = min(max_chunk, len); | |
906 | /* 8 byte blocks */ | |
907 | max_chunk = max_t(size_t, 8, max_chunk & ~0x7); | |
908 | ||
909 | while (len) { | |
910 | size_t added = 0; | |
911 | ||
912 | chunk = min(len, max_chunk); | |
913 | len -= chunk; | |
36b50572 | 914 | dst = dbi->tx_buf9; |
02dd95fe NT |
915 | |
916 | if (chunk < 8) { | |
917 | u8 val, carry = 0; | |
918 | ||
919 | /* Data: pad no-op's (zeroes) at end of block */ | |
920 | memset(dst, 0, 9); | |
921 | ||
922 | if (swap_bytes) { | |
923 | for (i = 1; i < (chunk + 1); i++) { | |
924 | val = src[1]; | |
925 | *dst++ = carry | BIT(8 - i) | (val >> i); | |
926 | carry = val << (8 - i); | |
927 | i++; | |
928 | val = src[0]; | |
929 | *dst++ = carry | BIT(8 - i) | (val >> i); | |
930 | carry = val << (8 - i); | |
931 | src += 2; | |
932 | } | |
933 | *dst++ = carry; | |
934 | } else { | |
935 | for (i = 1; i < (chunk + 1); i++) { | |
936 | val = *src++; | |
937 | *dst++ = carry | BIT(8 - i) | (val >> i); | |
938 | carry = val << (8 - i); | |
939 | } | |
940 | *dst++ = carry; | |
941 | } | |
942 | ||
943 | chunk = 8; | |
944 | added = 1; | |
945 | } else { | |
946 | for (i = 0; i < chunk; i += 8) { | |
947 | if (swap_bytes) { | |
948 | *dst++ = BIT(7) | (src[1] >> 1); | |
949 | *dst++ = (src[1] << 7) | BIT(6) | (src[0] >> 2); | |
950 | *dst++ = (src[0] << 6) | BIT(5) | (src[3] >> 3); | |
951 | *dst++ = (src[3] << 5) | BIT(4) | (src[2] >> 4); | |
952 | *dst++ = (src[2] << 4) | BIT(3) | (src[5] >> 5); | |
953 | *dst++ = (src[5] << 3) | BIT(2) | (src[4] >> 6); | |
954 | *dst++ = (src[4] << 2) | BIT(1) | (src[7] >> 7); | |
955 | *dst++ = (src[7] << 1) | BIT(0); | |
956 | *dst++ = src[6]; | |
957 | } else { | |
958 | *dst++ = BIT(7) | (src[0] >> 1); | |
959 | *dst++ = (src[0] << 7) | BIT(6) | (src[1] >> 2); | |
960 | *dst++ = (src[1] << 6) | BIT(5) | (src[2] >> 3); | |
961 | *dst++ = (src[2] << 5) | BIT(4) | (src[3] >> 4); | |
962 | *dst++ = (src[3] << 4) | BIT(3) | (src[4] >> 5); | |
963 | *dst++ = (src[4] << 3) | BIT(2) | (src[5] >> 6); | |
964 | *dst++ = (src[5] << 2) | BIT(1) | (src[6] >> 7); | |
965 | *dst++ = (src[6] << 1) | BIT(0); | |
966 | *dst++ = src[7]; | |
967 | } | |
968 | ||
969 | src += 8; | |
970 | added++; | |
971 | } | |
972 | } | |
973 | ||
974 | tr.len = chunk + added; | |
975 | ||
02dd95fe NT |
976 | ret = spi_sync(spi, &m); |
977 | if (ret) | |
978 | return ret; | |
265ffed7 | 979 | } |
02dd95fe NT |
980 | |
981 | return 0; | |
982 | } | |
983 | ||
36b50572 | 984 | static int mipi_dbi_spi1_transfer(struct mipi_dbi *dbi, int dc, |
02dd95fe NT |
985 | const void *buf, size_t len, |
986 | unsigned int bpw) | |
987 | { | |
36b50572 | 988 | struct spi_device *spi = dbi->spi; |
02dd95fe NT |
989 | struct spi_transfer tr = { |
990 | .bits_per_word = 9, | |
991 | }; | |
992 | const u16 *src16 = buf; | |
993 | const u8 *src8 = buf; | |
994 | struct spi_message m; | |
995 | size_t max_chunk; | |
996 | u16 *dst16; | |
997 | int ret; | |
998 | ||
cfcc8fcb | 999 | if (!spi_is_bpw_supported(spi, 9)) |
36b50572 | 1000 | return mipi_dbi_spi1e_transfer(dbi, dc, buf, len, bpw); |
02dd95fe NT |
1001 | |
1002 | tr.speed_hz = mipi_dbi_spi_cmd_max_speed(spi, len); | |
36b50572 NT |
1003 | max_chunk = dbi->tx_buf9_len; |
1004 | dst16 = dbi->tx_buf9; | |
02dd95fe | 1005 | |
f0a8f533 | 1006 | if (drm_debug_enabled(DRM_UT_DRIVER)) |
02dd95fe NT |
1007 | pr_debug("[drm:%s] dc=%d, max_chunk=%zu, transfers:\n", |
1008 | __func__, dc, max_chunk); | |
1009 | ||
1010 | max_chunk = min(max_chunk / 2, len); | |
1011 | ||
1012 | spi_message_init_with_transfers(&m, &tr, 1); | |
1013 | tr.tx_buf = dst16; | |
1014 | ||
1015 | while (len) { | |
1016 | size_t chunk = min(len, max_chunk); | |
1017 | unsigned int i; | |
1018 | ||
f34f014c | 1019 | if (bpw == 16) { |
02dd95fe NT |
1020 | for (i = 0; i < (chunk * 2); i += 2) { |
1021 | dst16[i] = *src16 >> 8; | |
1022 | dst16[i + 1] = *src16++ & 0xFF; | |
1023 | if (dc) { | |
1024 | dst16[i] |= 0x0100; | |
1025 | dst16[i + 1] |= 0x0100; | |
1026 | } | |
1027 | } | |
1028 | } else { | |
1029 | for (i = 0; i < chunk; i++) { | |
1030 | dst16[i] = *src8++; | |
1031 | if (dc) | |
1032 | dst16[i] |= 0x0100; | |
1033 | } | |
1034 | } | |
1035 | ||
900ab59e | 1036 | tr.len = chunk * 2; |
02dd95fe NT |
1037 | len -= chunk; |
1038 | ||
02dd95fe NT |
1039 | ret = spi_sync(spi, &m); |
1040 | if (ret) | |
1041 | return ret; | |
265ffed7 | 1042 | } |
02dd95fe NT |
1043 | |
1044 | return 0; | |
1045 | } | |
1046 | ||
413f52f1 LW |
1047 | static int mipi_dbi_typec1_command_read(struct mipi_dbi *dbi, u8 *cmd, |
1048 | u8 *data, size_t len) | |
1049 | { | |
1050 | struct spi_device *spi = dbi->spi; | |
1051 | u32 speed_hz = min_t(u32, MIPI_DBI_MAX_SPI_READ_SPEED, | |
1052 | spi->max_speed_hz / 2); | |
1053 | struct spi_transfer tr[2] = { | |
1054 | { | |
1055 | .speed_hz = speed_hz, | |
1056 | .bits_per_word = 9, | |
1057 | .tx_buf = dbi->tx_buf9, | |
1058 | .len = 2, | |
1059 | }, { | |
1060 | .speed_hz = speed_hz, | |
1061 | .bits_per_word = 8, | |
1062 | .len = len, | |
1063 | .rx_buf = data, | |
1064 | }, | |
1065 | }; | |
1066 | struct spi_message m; | |
1067 | u16 *dst16; | |
1068 | int ret; | |
1069 | ||
1070 | if (!len) | |
1071 | return -EINVAL; | |
1072 | ||
1073 | if (!spi_is_bpw_supported(spi, 9)) { | |
1074 | /* | |
1075 | * FIXME: implement something like mipi_dbi_spi1e_transfer() but | |
1076 | * for reads using emulation. | |
1077 | */ | |
1078 | dev_err(&spi->dev, | |
1079 | "reading on host not supporting 9 bpw not yet implemented\n"); | |
1080 | return -EOPNOTSUPP; | |
1081 | } | |
1082 | ||
1083 | /* | |
1084 | * Turn the 8bit command into a 16bit version of the command in the | |
1085 | * buffer. Only 9 bits of this will be used when executing the actual | |
1086 | * transfer. | |
1087 | */ | |
1088 | dst16 = dbi->tx_buf9; | |
1089 | dst16[0] = *cmd; | |
1090 | ||
1091 | spi_message_init_with_transfers(&m, tr, ARRAY_SIZE(tr)); | |
1092 | ret = spi_sync(spi, &m); | |
1093 | ||
1094 | if (!ret) | |
1095 | MIPI_DBI_DEBUG_COMMAND(*cmd, data, len); | |
1096 | ||
1097 | return ret; | |
1098 | } | |
1099 | ||
36b50572 | 1100 | static int mipi_dbi_typec1_command(struct mipi_dbi *dbi, u8 *cmd, |
02dd95fe NT |
1101 | u8 *parameters, size_t num) |
1102 | { | |
df3fb27a | 1103 | unsigned int bpw = 8; |
02dd95fe NT |
1104 | int ret; |
1105 | ||
36b50572 | 1106 | if (mipi_dbi_command_is_read(dbi, *cmd)) |
413f52f1 | 1107 | return mipi_dbi_typec1_command_read(dbi, cmd, parameters, num); |
02dd95fe | 1108 | |
a89bfc5d | 1109 | MIPI_DBI_DEBUG_COMMAND(*cmd, parameters, num); |
02dd95fe | 1110 | |
36b50572 | 1111 | ret = mipi_dbi_spi1_transfer(dbi, 0, cmd, 1, 8); |
02dd95fe NT |
1112 | if (ret || !num) |
1113 | return ret; | |
1114 | ||
df3fb27a NT |
1115 | if (*cmd == MIPI_DCS_WRITE_MEMORY_START) |
1116 | bpw = dbi->write_memory_bpw; | |
1117 | ||
36b50572 | 1118 | return mipi_dbi_spi1_transfer(dbi, 1, parameters, num, bpw); |
02dd95fe NT |
1119 | } |
1120 | ||
1121 | /* MIPI DBI Type C Option 3 */ | |
1122 | ||
36b50572 | 1123 | static int mipi_dbi_typec3_command_read(struct mipi_dbi *dbi, u8 *cmd, |
02dd95fe NT |
1124 | u8 *data, size_t len) |
1125 | { | |
36b50572 | 1126 | struct spi_device *spi = dbi->spi; |
02dd95fe NT |
1127 | u32 speed_hz = min_t(u32, MIPI_DBI_MAX_SPI_READ_SPEED, |
1128 | spi->max_speed_hz / 2); | |
1129 | struct spi_transfer tr[2] = { | |
1130 | { | |
1131 | .speed_hz = speed_hz, | |
a89bfc5d | 1132 | .tx_buf = cmd, |
02dd95fe NT |
1133 | .len = 1, |
1134 | }, { | |
1135 | .speed_hz = speed_hz, | |
1136 | .len = len, | |
1137 | }, | |
1138 | }; | |
1139 | struct spi_message m; | |
1140 | u8 *buf; | |
1141 | int ret; | |
1142 | ||
1143 | if (!len) | |
1144 | return -EINVAL; | |
1145 | ||
1146 | /* | |
1147 | * Support non-standard 24-bit and 32-bit Nokia read commands which | |
1148 | * start with a dummy clock, so we need to read an extra byte. | |
1149 | */ | |
a89bfc5d NT |
1150 | if (*cmd == MIPI_DCS_GET_DISPLAY_ID || |
1151 | *cmd == MIPI_DCS_GET_DISPLAY_STATUS) { | |
02dd95fe NT |
1152 | if (!(len == 3 || len == 4)) |
1153 | return -EINVAL; | |
1154 | ||
1155 | tr[1].len = len + 1; | |
1156 | } | |
1157 | ||
1158 | buf = kmalloc(tr[1].len, GFP_KERNEL); | |
1159 | if (!buf) | |
1160 | return -ENOMEM; | |
1161 | ||
1162 | tr[1].rx_buf = buf; | |
8cc8ccba OP |
1163 | |
1164 | spi_bus_lock(spi->controller); | |
36b50572 | 1165 | gpiod_set_value_cansleep(dbi->dc, 0); |
02dd95fe NT |
1166 | |
1167 | spi_message_init_with_transfers(&m, tr, ARRAY_SIZE(tr)); | |
8cc8ccba OP |
1168 | ret = spi_sync_locked(spi, &m); |
1169 | spi_bus_unlock(spi->controller); | |
02dd95fe NT |
1170 | if (ret) |
1171 | goto err_free; | |
1172 | ||
02dd95fe NT |
1173 | if (tr[1].len == len) { |
1174 | memcpy(data, buf, len); | |
1175 | } else { | |
1176 | unsigned int i; | |
1177 | ||
1178 | for (i = 0; i < len; i++) | |
dc6015cb | 1179 | data[i] = (buf[i] << 1) | (buf[i + 1] >> 7); |
02dd95fe NT |
1180 | } |
1181 | ||
a89bfc5d | 1182 | MIPI_DBI_DEBUG_COMMAND(*cmd, data, len); |
02dd95fe NT |
1183 | |
1184 | err_free: | |
1185 | kfree(buf); | |
1186 | ||
1187 | return ret; | |
1188 | } | |
1189 | ||
36b50572 | 1190 | static int mipi_dbi_typec3_command(struct mipi_dbi *dbi, u8 *cmd, |
02dd95fe NT |
1191 | u8 *par, size_t num) |
1192 | { | |
36b50572 | 1193 | struct spi_device *spi = dbi->spi; |
02dd95fe NT |
1194 | unsigned int bpw = 8; |
1195 | u32 speed_hz; | |
1196 | int ret; | |
1197 | ||
36b50572 NT |
1198 | if (mipi_dbi_command_is_read(dbi, *cmd)) |
1199 | return mipi_dbi_typec3_command_read(dbi, cmd, par, num); | |
02dd95fe | 1200 | |
a89bfc5d | 1201 | MIPI_DBI_DEBUG_COMMAND(*cmd, par, num); |
02dd95fe | 1202 | |
8cc8ccba | 1203 | spi_bus_lock(spi->controller); |
36b50572 | 1204 | gpiod_set_value_cansleep(dbi->dc, 0); |
02dd95fe | 1205 | speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1); |
d23d4d4d | 1206 | ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1); |
8cc8ccba | 1207 | spi_bus_unlock(spi->controller); |
02dd95fe NT |
1208 | if (ret || !num) |
1209 | return ret; | |
1210 | ||
df3fb27a NT |
1211 | if (*cmd == MIPI_DCS_WRITE_MEMORY_START) |
1212 | bpw = dbi->write_memory_bpw; | |
02dd95fe | 1213 | |
8cc8ccba | 1214 | spi_bus_lock(spi->controller); |
36b50572 | 1215 | gpiod_set_value_cansleep(dbi->dc, 1); |
02dd95fe | 1216 | speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num); |
8cc8ccba OP |
1217 | ret = mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num); |
1218 | spi_bus_unlock(spi->controller); | |
02dd95fe | 1219 | |
8cc8ccba | 1220 | return ret; |
02dd95fe NT |
1221 | } |
1222 | ||
1223 | /** | |
36b50572 | 1224 | * mipi_dbi_spi_init - Initialize MIPI DBI SPI interface |
02dd95fe | 1225 | * @spi: SPI device |
36b50572 | 1226 | * @dbi: MIPI DBI structure to initialize |
ace98812 | 1227 | * @dc: D/C gpio (optional) |
02dd95fe | 1228 | * |
36b50572 | 1229 | * This function sets &mipi_dbi->command, enables &mipi_dbi->read_commands for the |
84137b86 | 1230 | * usual read commands. It should be followed by a call to mipi_dbi_dev_init() or |
ace98812 | 1231 | * a driver-specific init. |
02dd95fe NT |
1232 | * |
1233 | * If @dc is set, a Type C Option 3 interface is assumed, if not | |
1234 | * Type C Option 1. | |
1235 | * | |
f34f014c NT |
1236 | * If the command is %MIPI_DCS_WRITE_MEMORY_START and the pixel format is RGB565, endianness has |
1237 | * to be taken into account. The MIPI DBI serial interface is big endian and framebuffers are | |
1238 | * assumed stored in memory as little endian (%DRM_FORMAT_BIG_ENDIAN is not supported). | |
1239 | * | |
1240 | * This is how endianness is handled: | |
1241 | * | |
1242 | * Option 1 (D/C as a bit): The buffer is sent on the wire byte by byte so the 16-bit buffer is | |
1243 | * byteswapped before transfer. | |
1244 | * | |
1245 | * Option 3 (D/C as a gpio): If the SPI controller supports 16 bits per word the buffer can be | |
1246 | * sent as-is. If not the caller is responsible for swapping the bytes | |
1247 | * before calling mipi_dbi_command_buf() and the buffer is sent 8 bpw. | |
1248 | * | |
1249 | * This handling is optimised for %DRM_FORMAT_RGB565 framebuffers. | |
02dd95fe | 1250 | * |
f34f014c NT |
1251 | * If the interface is Option 1 and the SPI controller doesn't support 9 bits per word, |
1252 | * the buffer is sent as 9x 8-bit words, padded with MIPI DCS no-op commands if necessary. | |
02dd95fe NT |
1253 | * |
1254 | * Returns: | |
1255 | * Zero on success, negative error code on failure. | |
1256 | */ | |
36b50572 | 1257 | int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi, |
ace98812 | 1258 | struct gpio_desc *dc) |
02dd95fe | 1259 | { |
02dd95fe NT |
1260 | struct device *dev = &spi->dev; |
1261 | int ret; | |
1262 | ||
02dd95fe NT |
1263 | /* |
1264 | * Even though it's not the SPI device that does DMA (the master does), | |
c47160d8 | 1265 | * the dma mask is necessary for the dma_alloc_wc() in the GEM code |
4a83c26a | 1266 | * (e.g., drm_gem_dma_create()). The dma_addr returned will be a physical |
eb73e1d5 | 1267 | * address which might be different from the bus address, but this is |
02dd95fe NT |
1268 | * not a problem since the address will not be used. |
1269 | * The virtual address is used in the transfer and the SPI core | |
1270 | * re-maps it on the SPI master device using the DMA streaming API | |
1271 | * (spi_map_buf()). | |
1272 | */ | |
1273 | if (!dev->coherent_dma_mask) { | |
1274 | ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); | |
1275 | if (ret) { | |
1276 | dev_warn(dev, "Failed to set dma mask %d\n", ret); | |
1277 | return ret; | |
1278 | } | |
1279 | } | |
1280 | ||
36b50572 NT |
1281 | dbi->spi = spi; |
1282 | dbi->read_commands = mipi_dbi_dcs_read_commands; | |
df3fb27a | 1283 | dbi->write_memory_bpw = 16; |
02dd95fe NT |
1284 | |
1285 | if (dc) { | |
36b50572 NT |
1286 | dbi->command = mipi_dbi_typec3_command; |
1287 | dbi->dc = dc; | |
df3fb27a NT |
1288 | if (!spi_is_bpw_supported(spi, 16)) { |
1289 | dbi->write_memory_bpw = 8; | |
36b50572 | 1290 | dbi->swap_bytes = true; |
df3fb27a | 1291 | } |
02dd95fe | 1292 | } else { |
36b50572 NT |
1293 | dbi->command = mipi_dbi_typec1_command; |
1294 | dbi->tx_buf9_len = SZ_16K; | |
1295 | dbi->tx_buf9 = devm_kmalloc(dev, dbi->tx_buf9_len, GFP_KERNEL); | |
1296 | if (!dbi->tx_buf9) | |
02dd95fe NT |
1297 | return -ENOMEM; |
1298 | } | |
1299 | ||
36b50572 | 1300 | mutex_init(&dbi->cmdlock); |
771ea160 | 1301 | |
95a0cfe9 NT |
1302 | DRM_DEBUG_DRIVER("SPI speed: %uMHz\n", spi->max_speed_hz / 1000000); |
1303 | ||
ace98812 | 1304 | return 0; |
02dd95fe NT |
1305 | } |
1306 | EXPORT_SYMBOL(mipi_dbi_spi_init); | |
1307 | ||
d23d4d4d NT |
1308 | /** |
1309 | * mipi_dbi_spi_transfer - SPI transfer helper | |
1310 | * @spi: SPI device | |
1311 | * @speed_hz: Override speed (optional) | |
1312 | * @bpw: Bits per word | |
1313 | * @buf: Buffer to transfer | |
1314 | * @len: Buffer length | |
1315 | * | |
1316 | * This SPI transfer helper breaks up the transfer of @buf into chunks which | |
8cc8ccba OP |
1317 | * the SPI controller driver can handle. The SPI bus must be locked when |
1318 | * calling this. | |
d23d4d4d NT |
1319 | * |
1320 | * Returns: | |
1321 | * Zero on success, negative error code on failure. | |
1322 | */ | |
1323 | int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz, | |
1324 | u8 bpw, const void *buf, size_t len) | |
1325 | { | |
1326 | size_t max_chunk = spi_max_transfer_size(spi); | |
1327 | struct spi_transfer tr = { | |
1328 | .bits_per_word = bpw, | |
1329 | .speed_hz = speed_hz, | |
1330 | }; | |
1331 | struct spi_message m; | |
1332 | size_t chunk; | |
1333 | int ret; | |
1334 | ||
435c2490 YT |
1335 | /* In __spi_validate, there's a validation that no partial transfers |
1336 | * are accepted (xfer->len % w_size must be zero). | |
1337 | * Here we align max_chunk to multiple of 2 (16bits), | |
1338 | * to prevent transfers from being rejected. | |
1339 | */ | |
1340 | max_chunk = ALIGN_DOWN(max_chunk, 2); | |
1341 | ||
d23d4d4d NT |
1342 | spi_message_init_with_transfers(&m, &tr, 1); |
1343 | ||
1344 | while (len) { | |
1345 | chunk = min(len, max_chunk); | |
1346 | ||
1347 | tr.tx_buf = buf; | |
1348 | tr.len = chunk; | |
1349 | buf += chunk; | |
1350 | len -= chunk; | |
1351 | ||
8cc8ccba | 1352 | ret = spi_sync_locked(spi, &m); |
d23d4d4d NT |
1353 | if (ret) |
1354 | return ret; | |
1355 | } | |
1356 | ||
1357 | return 0; | |
1358 | } | |
1359 | EXPORT_SYMBOL(mipi_dbi_spi_transfer); | |
1360 | ||
02dd95fe NT |
1361 | #endif /* CONFIG_SPI */ |
1362 | ||
1363 | #ifdef CONFIG_DEBUG_FS | |
1364 | ||
1365 | static ssize_t mipi_dbi_debugfs_command_write(struct file *file, | |
1366 | const char __user *ubuf, | |
1367 | size_t count, loff_t *ppos) | |
1368 | { | |
1369 | struct seq_file *m = file->private_data; | |
84137b86 | 1370 | struct mipi_dbi_dev *dbidev = m->private; |
b401f343 | 1371 | u8 val, cmd = 0, parameters[64]; |
02dd95fe | 1372 | char *buf, *pos, *token; |
d72cf01f | 1373 | int i, ret, idx; |
9d5645ad | 1374 | |
84137b86 | 1375 | if (!drm_dev_enter(&dbidev->drm, &idx)) |
9d5645ad | 1376 | return -ENODEV; |
02dd95fe NT |
1377 | |
1378 | buf = memdup_user_nul(ubuf, count); | |
9d5645ad NT |
1379 | if (IS_ERR(buf)) { |
1380 | ret = PTR_ERR(buf); | |
1381 | goto err_exit; | |
1382 | } | |
02dd95fe NT |
1383 | |
1384 | /* strip trailing whitespace */ | |
1385 | for (i = count - 1; i > 0; i--) | |
1386 | if (isspace(buf[i])) | |
1387 | buf[i] = '\0'; | |
1388 | else | |
1389 | break; | |
1390 | i = 0; | |
1391 | pos = buf; | |
1392 | while (pos) { | |
1393 | token = strsep(&pos, " "); | |
1394 | if (!token) { | |
1395 | ret = -EINVAL; | |
1396 | goto err_free; | |
1397 | } | |
1398 | ||
1399 | ret = kstrtou8(token, 16, &val); | |
1400 | if (ret < 0) | |
1401 | goto err_free; | |
1402 | ||
1403 | if (token == buf) | |
1404 | cmd = val; | |
1405 | else | |
1406 | parameters[i++] = val; | |
1407 | ||
1408 | if (i == 64) { | |
1409 | ret = -E2BIG; | |
1410 | goto err_free; | |
1411 | } | |
1412 | } | |
1413 | ||
84137b86 | 1414 | ret = mipi_dbi_command_buf(&dbidev->dbi, cmd, parameters, i); |
02dd95fe NT |
1415 | |
1416 | err_free: | |
1417 | kfree(buf); | |
9d5645ad NT |
1418 | err_exit: |
1419 | drm_dev_exit(idx); | |
02dd95fe NT |
1420 | |
1421 | return ret < 0 ? ret : count; | |
1422 | } | |
1423 | ||
1424 | static int mipi_dbi_debugfs_command_show(struct seq_file *m, void *unused) | |
1425 | { | |
84137b86 NT |
1426 | struct mipi_dbi_dev *dbidev = m->private; |
1427 | struct mipi_dbi *dbi = &dbidev->dbi; | |
02dd95fe | 1428 | u8 cmd, val[4]; |
9d5645ad | 1429 | int ret, idx; |
46466b0d | 1430 | size_t len; |
9d5645ad | 1431 | |
84137b86 | 1432 | if (!drm_dev_enter(&dbidev->drm, &idx)) |
9d5645ad | 1433 | return -ENODEV; |
02dd95fe NT |
1434 | |
1435 | for (cmd = 0; cmd < 255; cmd++) { | |
84137b86 | 1436 | if (!mipi_dbi_command_is_read(dbi, cmd)) |
02dd95fe NT |
1437 | continue; |
1438 | ||
1439 | switch (cmd) { | |
1440 | case MIPI_DCS_READ_MEMORY_START: | |
1441 | case MIPI_DCS_READ_MEMORY_CONTINUE: | |
1442 | len = 2; | |
1443 | break; | |
1444 | case MIPI_DCS_GET_DISPLAY_ID: | |
1445 | len = 3; | |
1446 | break; | |
1447 | case MIPI_DCS_GET_DISPLAY_STATUS: | |
1448 | len = 4; | |
1449 | break; | |
1450 | default: | |
1451 | len = 1; | |
1452 | break; | |
1453 | } | |
1454 | ||
1455 | seq_printf(m, "%02x: ", cmd); | |
84137b86 | 1456 | ret = mipi_dbi_command_buf(dbi, cmd, val, len); |
02dd95fe NT |
1457 | if (ret) { |
1458 | seq_puts(m, "XX\n"); | |
1459 | continue; | |
1460 | } | |
46466b0d | 1461 | seq_printf(m, "%*phN\n", (int)len, val); |
02dd95fe NT |
1462 | } |
1463 | ||
9d5645ad NT |
1464 | drm_dev_exit(idx); |
1465 | ||
02dd95fe NT |
1466 | return 0; |
1467 | } | |
1468 | ||
1469 | static int mipi_dbi_debugfs_command_open(struct inode *inode, | |
1470 | struct file *file) | |
1471 | { | |
1472 | return single_open(file, mipi_dbi_debugfs_command_show, | |
1473 | inode->i_private); | |
1474 | } | |
1475 | ||
1476 | static const struct file_operations mipi_dbi_debugfs_command_fops = { | |
1477 | .owner = THIS_MODULE, | |
1478 | .open = mipi_dbi_debugfs_command_open, | |
1479 | .read = seq_read, | |
1480 | .llseek = seq_lseek, | |
1481 | .release = single_release, | |
1482 | .write = mipi_dbi_debugfs_command_write, | |
1483 | }; | |
1484 | ||
02dd95fe NT |
1485 | /** |
1486 | * mipi_dbi_debugfs_init - Create debugfs entries | |
1487 | * @minor: DRM minor | |
1488 | * | |
1489 | * This function creates a 'command' debugfs file for sending commands to the | |
1490 | * controller or getting the read command values. | |
1491 | * Drivers can use this as their &drm_driver->debugfs_init callback. | |
1492 | * | |
02dd95fe | 1493 | */ |
7ce84471 | 1494 | void mipi_dbi_debugfs_init(struct drm_minor *minor) |
02dd95fe | 1495 | { |
84137b86 | 1496 | struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(minor->dev); |
02dd95fe NT |
1497 | umode_t mode = S_IFREG | S_IWUSR; |
1498 | ||
84137b86 | 1499 | if (dbidev->dbi.read_commands) |
02dd95fe | 1500 | mode |= S_IRUGO; |
84137b86 | 1501 | debugfs_create_file("command", mode, minor->debugfs_root, dbidev, |
02dd95fe | 1502 | &mipi_dbi_debugfs_command_fops); |
02dd95fe NT |
1503 | } |
1504 | EXPORT_SYMBOL(mipi_dbi_debugfs_init); | |
1505 | ||
1506 | #endif | |
1507 | ||
66541509 | 1508 | MODULE_DESCRIPTION("MIPI Display Bus Interface (DBI) LCD controller support"); |
02dd95fe | 1509 | MODULE_LICENSE("GPL"); |