1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2012 Red Hat
5 * based in parts on udlfb.c:
6 * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
7 * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
8 * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
12 #include <linux/dma-buf.h>
14 #include <drm/drm_atomic_helper.h>
15 #include <drm/drm_crtc_helper.h>
16 #include <drm/drm_damage_helper.h>
17 #include <drm/drm_fourcc.h>
18 #include <drm/drm_gem_framebuffer_helper.h>
19 #include <drm/drm_gem_shmem_helper.h>
20 #include <drm/drm_modeset_helper_vtables.h>
21 #include <drm/drm_vblank.h>
25 #define UDL_COLOR_DEPTH_16BPP 0
28 * All DisplayLink bulk operations start with 0xAF, followed by specific code
29 * All operations are written to buffers which then later get sent to device
31 static char *udl_set_register(char *buf
, u8 reg
, u8 val
)
40 static char *udl_vidreg_lock(char *buf
)
42 return udl_set_register(buf
, 0xFF, 0x00);
45 static char *udl_vidreg_unlock(char *buf
)
47 return udl_set_register(buf
, 0xFF, 0xFF);
50 static char *udl_set_blank_mode(char *buf
, u8 mode
)
52 return udl_set_register(buf
, UDL_REG_BLANK_MODE
, mode
);
55 static char *udl_set_color_depth(char *buf
, u8 selection
)
57 return udl_set_register(buf
, 0x00, selection
);
60 static char *udl_set_base16bpp(char *wrptr
, u32 base
)
62 /* the base pointer is 16 bits wide, 0x20 is hi byte. */
63 wrptr
= udl_set_register(wrptr
, 0x20, base
>> 16);
64 wrptr
= udl_set_register(wrptr
, 0x21, base
>> 8);
65 return udl_set_register(wrptr
, 0x22, base
);
69 * DisplayLink HW has separate 16bpp and 8bpp framebuffers.
70 * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer
72 static char *udl_set_base8bpp(char *wrptr
, u32 base
)
74 wrptr
= udl_set_register(wrptr
, 0x26, base
>> 16);
75 wrptr
= udl_set_register(wrptr
, 0x27, base
>> 8);
76 return udl_set_register(wrptr
, 0x28, base
);
79 static char *udl_set_register_16(char *wrptr
, u8 reg
, u16 value
)
81 wrptr
= udl_set_register(wrptr
, reg
, value
>> 8);
82 return udl_set_register(wrptr
, reg
+1, value
);
86 * This is kind of weird because the controller takes some
87 * register values in a different byte order than other registers.
89 static char *udl_set_register_16be(char *wrptr
, u8 reg
, u16 value
)
91 wrptr
= udl_set_register(wrptr
, reg
, value
);
92 return udl_set_register(wrptr
, reg
+1, value
>> 8);
96 * LFSR is linear feedback shift register. The reason we have this is
97 * because the display controller needs to minimize the clock depth of
98 * various counters used in the display path. So this code reverses the
99 * provided value into the lfsr16 value by counting backwards to get
100 * the value that needs to be set in the hardware comparator to get the
101 * same actual count. This makes sense once you read above a couple of
102 * times and think about it from a hardware perspective.
104 static u16
udl_lfsr16(u16 actual_count
)
106 u32 lv
= 0xFFFF; /* This is the lfsr value that the hw starts with */
108 while (actual_count
--) {
110 (((lv
>> 15) ^ (lv
>> 4) ^ (lv
>> 2) ^ (lv
>> 1)) & 1))
118 * This does LFSR conversion on the value that is to be written.
119 * See LFSR explanation above for more detail.
121 static char *udl_set_register_lfsr16(char *wrptr
, u8 reg
, u16 value
)
123 return udl_set_register_16(wrptr
, reg
, udl_lfsr16(value
));
127 * This takes a standard fbdev screeninfo struct and all of its monitor mode
128 * details and converts them into the DisplayLink equivalent register commands.
129 ERR(vreg(dev, 0x00, (color_depth == 16) ? 0 : 1));
130 ERR(vreg_lfsr16(dev, 0x01, xDisplayStart));
131 ERR(vreg_lfsr16(dev, 0x03, xDisplayEnd));
132 ERR(vreg_lfsr16(dev, 0x05, yDisplayStart));
133 ERR(vreg_lfsr16(dev, 0x07, yDisplayEnd));
134 ERR(vreg_lfsr16(dev, 0x09, xEndCount));
135 ERR(vreg_lfsr16(dev, 0x0B, hSyncStart));
136 ERR(vreg_lfsr16(dev, 0x0D, hSyncEnd));
137 ERR(vreg_big_endian(dev, 0x0F, hPixels));
138 ERR(vreg_lfsr16(dev, 0x11, yEndCount));
139 ERR(vreg_lfsr16(dev, 0x13, vSyncStart));
140 ERR(vreg_lfsr16(dev, 0x15, vSyncEnd));
141 ERR(vreg_big_endian(dev, 0x17, vPixels));
142 ERR(vreg_little_endian(dev, 0x1B, pixelClock5KHz));
144 ERR(vreg(dev, 0x1F, 0));
146 ERR(vbuf(dev, WRITE_VIDREG_UNLOCK, DSIZEOF(WRITE_VIDREG_UNLOCK)));
148 static char *udl_set_vid_cmds(char *wrptr
, struct drm_display_mode
*mode
)
154 /* x display start */
155 xds
= mode
->crtc_htotal
- mode
->crtc_hsync_start
;
156 wrptr
= udl_set_register_lfsr16(wrptr
, 0x01, xds
);
158 xde
= xds
+ mode
->crtc_hdisplay
;
159 wrptr
= udl_set_register_lfsr16(wrptr
, 0x03, xde
);
161 /* y display start */
162 yds
= mode
->crtc_vtotal
- mode
->crtc_vsync_start
;
163 wrptr
= udl_set_register_lfsr16(wrptr
, 0x05, yds
);
165 yde
= yds
+ mode
->crtc_vdisplay
;
166 wrptr
= udl_set_register_lfsr16(wrptr
, 0x07, yde
);
168 /* x end count is active + blanking - 1 */
169 wrptr
= udl_set_register_lfsr16(wrptr
, 0x09,
170 mode
->crtc_htotal
- 1);
172 /* libdlo hardcodes hsync start to 1 */
173 wrptr
= udl_set_register_lfsr16(wrptr
, 0x0B, 1);
175 /* hsync end is width of sync pulse + 1 */
176 wrptr
= udl_set_register_lfsr16(wrptr
, 0x0D,
177 mode
->crtc_hsync_end
- mode
->crtc_hsync_start
+ 1);
179 /* hpixels is active pixels */
180 wrptr
= udl_set_register_16(wrptr
, 0x0F, mode
->hdisplay
);
182 /* yendcount is vertical active + vertical blanking */
183 yec
= mode
->crtc_vtotal
;
184 wrptr
= udl_set_register_lfsr16(wrptr
, 0x11, yec
);
186 /* libdlo hardcodes vsync start to 0 */
187 wrptr
= udl_set_register_lfsr16(wrptr
, 0x13, 0);
189 /* vsync end is width of vsync pulse */
190 wrptr
= udl_set_register_lfsr16(wrptr
, 0x15, mode
->crtc_vsync_end
- mode
->crtc_vsync_start
);
192 /* vpixels is active pixels */
193 wrptr
= udl_set_register_16(wrptr
, 0x17, mode
->crtc_vdisplay
);
195 wrptr
= udl_set_register_16be(wrptr
, 0x1B,
201 static char *udl_dummy_render(char *wrptr
)
204 *wrptr
++ = 0x6A; /* copy */
205 *wrptr
++ = 0x00; /* from addr */
208 *wrptr
++ = 0x01; /* one pixel */
209 *wrptr
++ = 0x00; /* to address */
215 static int udl_crtc_write_mode_to_hw(struct drm_crtc
*crtc
)
217 struct drm_device
*dev
= crtc
->dev
;
218 struct udl_device
*udl
= to_udl(dev
);
223 if (udl
->mode_buf_len
== 0) {
224 DRM_ERROR("No mode set\n");
228 urb
= udl_get_urb(dev
);
232 buf
= (char *)urb
->transfer_buffer
;
234 memcpy(buf
, udl
->mode_buf
, udl
->mode_buf_len
);
235 retval
= udl_submit_urb(dev
, urb
, udl
->mode_buf_len
);
236 DRM_DEBUG("write mode info %d\n", udl
->mode_buf_len
);
240 static long udl_log_cpp(unsigned int cpp
)
242 if (WARN_ON(!is_power_of_2(cpp
)))
247 static int udl_aligned_damage_clip(struct drm_rect
*clip
, int x
, int y
,
248 int width
, int height
)
252 if (WARN_ON_ONCE(x
< 0) ||
253 WARN_ON_ONCE(y
< 0) ||
254 WARN_ON_ONCE(width
< 0) ||
255 WARN_ON_ONCE(height
< 0))
258 x1
= ALIGN_DOWN(x
, sizeof(unsigned long));
259 x2
= ALIGN(width
+ (x
- x1
), sizeof(unsigned long)) + x1
;
264 clip
->y2
= y
+ height
;
269 static int udl_handle_damage(struct drm_framebuffer
*fb
, int x
, int y
,
270 int width
, int height
)
272 struct drm_device
*dev
= fb
->dev
;
273 struct dma_buf_attachment
*import_attach
= fb
->obj
[0]->import_attach
;
277 struct drm_rect clip
;
279 struct dma_buf_map map
;
282 ret
= udl_log_cpp(fb
->format
->cpp
[0]);
287 ret
= udl_aligned_damage_clip(&clip
, x
, y
, width
, height
);
290 else if ((clip
.x2
> fb
->width
) || (clip
.y2
> fb
->height
))
294 ret
= dma_buf_begin_cpu_access(import_attach
->dmabuf
,
300 ret
= drm_gem_shmem_vmap(fb
->obj
[0], &map
);
302 DRM_ERROR("failed to vmap fb\n");
303 goto out_dma_buf_end_cpu_access
;
305 vaddr
= map
.vaddr
; /* TODO: Use mapping abstraction properly */
307 urb
= udl_get_urb(dev
);
310 goto out_drm_gem_shmem_vunmap
;
312 cmd
= urb
->transfer_buffer
;
314 for (i
= clip
.y1
; i
< clip
.y2
; i
++) {
315 const int line_offset
= fb
->pitches
[0] * i
;
316 const int byte_offset
= line_offset
+ (clip
.x1
<< log_bpp
);
317 const int dev_byte_offset
= (fb
->width
* i
+ clip
.x1
) << log_bpp
;
318 const int byte_width
= (clip
.x2
- clip
.x1
) << log_bpp
;
319 ret
= udl_render_hline(dev
, log_bpp
, &urb
, (char *)vaddr
,
320 &cmd
, byte_offset
, dev_byte_offset
,
323 goto out_drm_gem_shmem_vunmap
;
326 if (cmd
> (char *)urb
->transfer_buffer
) {
327 /* Send partial buffer remaining before exiting */
329 if (cmd
< (char *)urb
->transfer_buffer
+ urb
->transfer_buffer_length
)
331 len
= cmd
- (char *)urb
->transfer_buffer
;
332 ret
= udl_submit_urb(dev
, urb
, len
);
334 udl_urb_completion(urb
);
339 out_drm_gem_shmem_vunmap
:
340 drm_gem_shmem_vunmap(fb
->obj
[0], &map
);
341 out_dma_buf_end_cpu_access
:
343 tmp_ret
= dma_buf_end_cpu_access(import_attach
->dmabuf
,
346 ret
= tmp_ret
; /* only update ret if not set yet */
353 * Simple display pipeline
356 static const uint32_t udl_simple_display_pipe_formats
[] = {
361 static enum drm_mode_status
362 udl_simple_display_pipe_mode_valid(struct drm_simple_display_pipe
*pipe
,
363 const struct drm_display_mode
*mode
)
369 udl_simple_display_pipe_enable(struct drm_simple_display_pipe
*pipe
,
370 struct drm_crtc_state
*crtc_state
,
371 struct drm_plane_state
*plane_state
)
373 struct drm_crtc
*crtc
= &pipe
->crtc
;
374 struct drm_device
*dev
= crtc
->dev
;
375 struct drm_framebuffer
*fb
= plane_state
->fb
;
376 struct udl_device
*udl
= to_udl(dev
);
377 struct drm_display_mode
*mode
= &crtc_state
->mode
;
380 int color_depth
= UDL_COLOR_DEPTH_16BPP
;
382 buf
= (char *)udl
->mode_buf
;
384 /* This first section has to do with setting the base address on the
385 * controller associated with the display. There are 2 base
386 * pointers, currently, we only use the 16 bpp segment.
388 wrptr
= udl_vidreg_lock(buf
);
389 wrptr
= udl_set_color_depth(wrptr
, color_depth
);
390 /* set base for 16bpp segment to 0 */
391 wrptr
= udl_set_base16bpp(wrptr
, 0);
392 /* set base for 8bpp segment to end of fb */
393 wrptr
= udl_set_base8bpp(wrptr
, 2 * mode
->vdisplay
* mode
->hdisplay
);
395 wrptr
= udl_set_vid_cmds(wrptr
, mode
);
396 wrptr
= udl_set_blank_mode(wrptr
, UDL_BLANK_MODE_ON
);
397 wrptr
= udl_vidreg_unlock(wrptr
);
399 wrptr
= udl_dummy_render(wrptr
);
401 udl
->mode_buf_len
= wrptr
- buf
;
403 udl_handle_damage(fb
, 0, 0, fb
->width
, fb
->height
);
405 if (!crtc_state
->mode_changed
)
409 udl_crtc_write_mode_to_hw(crtc
);
413 udl_simple_display_pipe_disable(struct drm_simple_display_pipe
*pipe
)
415 struct drm_crtc
*crtc
= &pipe
->crtc
;
416 struct drm_device
*dev
= crtc
->dev
;
420 urb
= udl_get_urb(dev
);
424 buf
= (char *)urb
->transfer_buffer
;
425 buf
= udl_vidreg_lock(buf
);
426 buf
= udl_set_blank_mode(buf
, UDL_BLANK_MODE_POWERDOWN
);
427 buf
= udl_vidreg_unlock(buf
);
428 buf
= udl_dummy_render(buf
);
430 udl_submit_urb(dev
, urb
, buf
- (char *)urb
->transfer_buffer
);
434 udl_simple_display_pipe_update(struct drm_simple_display_pipe
*pipe
,
435 struct drm_plane_state
*old_plane_state
)
437 struct drm_plane_state
*state
= pipe
->plane
.state
;
438 struct drm_framebuffer
*fb
= state
->fb
;
439 struct drm_rect rect
;
444 if (drm_atomic_helper_damage_merged(old_plane_state
, state
, &rect
))
445 udl_handle_damage(fb
, rect
.x1
, rect
.y1
, rect
.x2
- rect
.x1
,
450 struct drm_simple_display_pipe_funcs udl_simple_display_pipe_funcs
= {
451 .mode_valid
= udl_simple_display_pipe_mode_valid
,
452 .enable
= udl_simple_display_pipe_enable
,
453 .disable
= udl_simple_display_pipe_disable
,
454 .update
= udl_simple_display_pipe_update
,
455 .prepare_fb
= drm_gem_fb_simple_display_pipe_prepare_fb
,
462 static const struct drm_mode_config_funcs udl_mode_funcs
= {
463 .fb_create
= drm_gem_fb_create_with_dirty
,
464 .atomic_check
= drm_atomic_helper_check
,
465 .atomic_commit
= drm_atomic_helper_commit
,
468 int udl_modeset_init(struct drm_device
*dev
)
470 size_t format_count
= ARRAY_SIZE(udl_simple_display_pipe_formats
);
471 struct udl_device
*udl
= to_udl(dev
);
472 struct drm_connector
*connector
;
475 ret
= drmm_mode_config_init(dev
);
479 dev
->mode_config
.min_width
= 640;
480 dev
->mode_config
.min_height
= 480;
482 dev
->mode_config
.max_width
= 2048;
483 dev
->mode_config
.max_height
= 2048;
485 dev
->mode_config
.prefer_shadow
= 0;
486 dev
->mode_config
.preferred_depth
= 16;
488 dev
->mode_config
.funcs
= &udl_mode_funcs
;
490 connector
= udl_connector_init(dev
);
491 if (IS_ERR(connector
))
492 return PTR_ERR(connector
);
494 format_count
= ARRAY_SIZE(udl_simple_display_pipe_formats
);
496 ret
= drm_simple_display_pipe_init(dev
, &udl
->display_pipe
,
497 &udl_simple_display_pipe_funcs
,
498 udl_simple_display_pipe_formats
,
499 format_count
, NULL
, connector
);
503 drm_mode_config_reset(dev
);