2 * Copyright (C) 2012 Red Hat
4 * based in parts on udlfb.c:
5 * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
6 * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
7 * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
9 * This file is subject to the terms and conditions of the GNU General Public
10 * License v2. See the file COPYING in the main directory of this archive for
15 #include <drm/drm_crtc.h>
16 #include <drm/drm_crtc_helper.h>
17 #include <drm/drm_plane_helper.h>
21 * All DisplayLink bulk operations start with 0xAF, followed by specific code
22 * All operations are written to buffers which then later get sent to device
24 static char *udl_set_register(char *buf
, u8 reg
, u8 val
)
33 static char *udl_vidreg_lock(char *buf
)
35 return udl_set_register(buf
, 0xFF, 0x00);
38 static char *udl_vidreg_unlock(char *buf
)
40 return udl_set_register(buf
, 0xFF, 0xFF);
44 * On/Off for driving the DisplayLink framebuffer to the display
45 * 0x00 H and V sync on
46 * 0x01 H and V sync off (screen blank but powered)
47 * 0x07 DPMS powerdown (requires modeset to come back)
49 static char *udl_set_blank(char *buf
, int dpms_mode
)
53 case DRM_MODE_DPMS_OFF
:
56 case DRM_MODE_DPMS_STANDBY
:
59 case DRM_MODE_DPMS_SUSPEND
:
62 case DRM_MODE_DPMS_ON
:
67 return udl_set_register(buf
, 0x1f, reg
);
70 static char *udl_set_color_depth(char *buf
, u8 selection
)
72 return udl_set_register(buf
, 0x00, selection
);
75 static char *udl_set_base16bpp(char *wrptr
, u32 base
)
77 /* the base pointer is 16 bits wide, 0x20 is hi byte. */
78 wrptr
= udl_set_register(wrptr
, 0x20, base
>> 16);
79 wrptr
= udl_set_register(wrptr
, 0x21, base
>> 8);
80 return udl_set_register(wrptr
, 0x22, base
);
84 * DisplayLink HW has separate 16bpp and 8bpp framebuffers.
85 * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer
87 static char *udl_set_base8bpp(char *wrptr
, u32 base
)
89 wrptr
= udl_set_register(wrptr
, 0x26, base
>> 16);
90 wrptr
= udl_set_register(wrptr
, 0x27, base
>> 8);
91 return udl_set_register(wrptr
, 0x28, base
);
94 static char *udl_set_register_16(char *wrptr
, u8 reg
, u16 value
)
96 wrptr
= udl_set_register(wrptr
, reg
, value
>> 8);
97 return udl_set_register(wrptr
, reg
+1, value
);
101 * This is kind of weird because the controller takes some
102 * register values in a different byte order than other registers.
104 static char *udl_set_register_16be(char *wrptr
, u8 reg
, u16 value
)
106 wrptr
= udl_set_register(wrptr
, reg
, value
);
107 return udl_set_register(wrptr
, reg
+1, value
>> 8);
111 * LFSR is linear feedback shift register. The reason we have this is
112 * because the display controller needs to minimize the clock depth of
113 * various counters used in the display path. So this code reverses the
114 * provided value into the lfsr16 value by counting backwards to get
115 * the value that needs to be set in the hardware comparator to get the
116 * same actual count. This makes sense once you read above a couple of
117 * times and think about it from a hardware perspective.
119 static u16
udl_lfsr16(u16 actual_count
)
121 u32 lv
= 0xFFFF; /* This is the lfsr value that the hw starts with */
123 while (actual_count
--) {
125 (((lv
>> 15) ^ (lv
>> 4) ^ (lv
>> 2) ^ (lv
>> 1)) & 1))
133 * This does LFSR conversion on the value that is to be written.
134 * See LFSR explanation above for more detail.
136 static char *udl_set_register_lfsr16(char *wrptr
, u8 reg
, u16 value
)
138 return udl_set_register_16(wrptr
, reg
, udl_lfsr16(value
));
142 * This takes a standard fbdev screeninfo struct and all of its monitor mode
143 * details and converts them into the DisplayLink equivalent register commands.
144 ERR(vreg(dev, 0x00, (color_depth == 16) ? 0 : 1));
145 ERR(vreg_lfsr16(dev, 0x01, xDisplayStart));
146 ERR(vreg_lfsr16(dev, 0x03, xDisplayEnd));
147 ERR(vreg_lfsr16(dev, 0x05, yDisplayStart));
148 ERR(vreg_lfsr16(dev, 0x07, yDisplayEnd));
149 ERR(vreg_lfsr16(dev, 0x09, xEndCount));
150 ERR(vreg_lfsr16(dev, 0x0B, hSyncStart));
151 ERR(vreg_lfsr16(dev, 0x0D, hSyncEnd));
152 ERR(vreg_big_endian(dev, 0x0F, hPixels));
153 ERR(vreg_lfsr16(dev, 0x11, yEndCount));
154 ERR(vreg_lfsr16(dev, 0x13, vSyncStart));
155 ERR(vreg_lfsr16(dev, 0x15, vSyncEnd));
156 ERR(vreg_big_endian(dev, 0x17, vPixels));
157 ERR(vreg_little_endian(dev, 0x1B, pixelClock5KHz));
159 ERR(vreg(dev, 0x1F, 0));
161 ERR(vbuf(dev, WRITE_VIDREG_UNLOCK, DSIZEOF(WRITE_VIDREG_UNLOCK)));
163 static char *udl_set_vid_cmds(char *wrptr
, struct drm_display_mode
*mode
)
169 /* x display start */
170 xds
= mode
->crtc_htotal
- mode
->crtc_hsync_start
;
171 wrptr
= udl_set_register_lfsr16(wrptr
, 0x01, xds
);
173 xde
= xds
+ mode
->crtc_hdisplay
;
174 wrptr
= udl_set_register_lfsr16(wrptr
, 0x03, xde
);
176 /* y display start */
177 yds
= mode
->crtc_vtotal
- mode
->crtc_vsync_start
;
178 wrptr
= udl_set_register_lfsr16(wrptr
, 0x05, yds
);
180 yde
= yds
+ mode
->crtc_vdisplay
;
181 wrptr
= udl_set_register_lfsr16(wrptr
, 0x07, yde
);
183 /* x end count is active + blanking - 1 */
184 wrptr
= udl_set_register_lfsr16(wrptr
, 0x09,
185 mode
->crtc_htotal
- 1);
187 /* libdlo hardcodes hsync start to 1 */
188 wrptr
= udl_set_register_lfsr16(wrptr
, 0x0B, 1);
190 /* hsync end is width of sync pulse + 1 */
191 wrptr
= udl_set_register_lfsr16(wrptr
, 0x0D,
192 mode
->crtc_hsync_end
- mode
->crtc_hsync_start
+ 1);
194 /* hpixels is active pixels */
195 wrptr
= udl_set_register_16(wrptr
, 0x0F, mode
->hdisplay
);
197 /* yendcount is vertical active + vertical blanking */
198 yec
= mode
->crtc_vtotal
;
199 wrptr
= udl_set_register_lfsr16(wrptr
, 0x11, yec
);
201 /* libdlo hardcodes vsync start to 0 */
202 wrptr
= udl_set_register_lfsr16(wrptr
, 0x13, 0);
204 /* vsync end is width of vsync pulse */
205 wrptr
= udl_set_register_lfsr16(wrptr
, 0x15, mode
->crtc_vsync_end
- mode
->crtc_vsync_start
);
207 /* vpixels is active pixels */
208 wrptr
= udl_set_register_16(wrptr
, 0x17, mode
->crtc_vdisplay
);
210 wrptr
= udl_set_register_16be(wrptr
, 0x1B,
216 static char *udl_dummy_render(char *wrptr
)
219 *wrptr
++ = 0x6A; /* copy */
220 *wrptr
++ = 0x00; /* from addr */
223 *wrptr
++ = 0x01; /* one pixel */
224 *wrptr
++ = 0x00; /* to address */
230 static int udl_crtc_write_mode_to_hw(struct drm_crtc
*crtc
)
232 struct drm_device
*dev
= crtc
->dev
;
233 struct udl_device
*udl
= dev
->dev_private
;
238 urb
= udl_get_urb(dev
);
242 buf
= (char *)urb
->transfer_buffer
;
244 memcpy(buf
, udl
->mode_buf
, udl
->mode_buf_len
);
245 retval
= udl_submit_urb(dev
, urb
, udl
->mode_buf_len
);
246 DRM_INFO("write mode info %d\n", udl
->mode_buf_len
);
251 static void udl_crtc_dpms(struct drm_crtc
*crtc
, int mode
)
253 struct drm_device
*dev
= crtc
->dev
;
254 struct udl_device
*udl
= dev
->dev_private
;
257 if (mode
== DRM_MODE_DPMS_OFF
) {
260 urb
= udl_get_urb(dev
);
264 buf
= (char *)urb
->transfer_buffer
;
265 buf
= udl_vidreg_lock(buf
);
266 buf
= udl_set_blank(buf
, mode
);
267 buf
= udl_vidreg_unlock(buf
);
269 buf
= udl_dummy_render(buf
);
270 retval
= udl_submit_urb(dev
, urb
, buf
- (char *)
271 urb
->transfer_buffer
);
273 if (udl
->mode_buf_len
== 0) {
274 DRM_ERROR("Trying to enable DPMS with no mode\n");
277 udl_crtc_write_mode_to_hw(crtc
);
282 static bool udl_crtc_mode_fixup(struct drm_crtc
*crtc
,
283 const struct drm_display_mode
*mode
,
284 struct drm_display_mode
*adjusted_mode
)
292 udl_pipe_set_base_atomic(struct drm_crtc
*crtc
, struct drm_framebuffer
*fb
,
293 int x
, int y
, enum mode_set_atomic state
)
299 udl_pipe_set_base(struct drm_crtc
*crtc
, int x
, int y
,
300 struct drm_framebuffer
*old_fb
)
306 static int udl_crtc_mode_set(struct drm_crtc
*crtc
,
307 struct drm_display_mode
*mode
,
308 struct drm_display_mode
*adjusted_mode
,
310 struct drm_framebuffer
*old_fb
)
313 struct drm_device
*dev
= crtc
->dev
;
314 struct udl_framebuffer
*ufb
= to_udl_fb(crtc
->primary
->fb
);
315 struct udl_device
*udl
= dev
->dev_private
;
320 buf
= (char *)udl
->mode_buf
;
322 /* for now we just clip 24 -> 16 - if we fix that fix this */
323 /*if (crtc->fb->bits_per_pixel != 16)
326 /* This first section has to do with setting the base address on the
327 * controller * associated with the display. There are 2 base
328 * pointers, currently, we only * use the 16 bpp segment.
330 wrptr
= udl_vidreg_lock(buf
);
331 wrptr
= udl_set_color_depth(wrptr
, color_depth
);
332 /* set base for 16bpp segment to 0 */
333 wrptr
= udl_set_base16bpp(wrptr
, 0);
334 /* set base for 8bpp segment to end of fb */
335 wrptr
= udl_set_base8bpp(wrptr
, 2 * mode
->vdisplay
* mode
->hdisplay
);
337 wrptr
= udl_set_vid_cmds(wrptr
, adjusted_mode
);
338 wrptr
= udl_set_blank(wrptr
, DRM_MODE_DPMS_ON
);
339 wrptr
= udl_vidreg_unlock(wrptr
);
341 wrptr
= udl_dummy_render(wrptr
);
344 struct udl_framebuffer
*uold_fb
= to_udl_fb(old_fb
);
345 uold_fb
->active_16
= false;
347 ufb
->active_16
= true;
348 udl
->mode_buf_len
= wrptr
- buf
;
350 /* damage all of it */
351 udl_handle_damage(ufb
, 0, 0, ufb
->base
.width
, ufb
->base
.height
);
356 static void udl_crtc_disable(struct drm_crtc
*crtc
)
358 udl_crtc_dpms(crtc
, DRM_MODE_DPMS_OFF
);
361 static void udl_crtc_destroy(struct drm_crtc
*crtc
)
363 drm_crtc_cleanup(crtc
);
367 static int udl_crtc_page_flip(struct drm_crtc
*crtc
,
368 struct drm_framebuffer
*fb
,
369 struct drm_pending_vblank_event
*event
,
370 uint32_t page_flip_flags
)
372 struct udl_framebuffer
*ufb
= to_udl_fb(fb
);
373 struct drm_device
*dev
= crtc
->dev
;
376 struct drm_framebuffer
*old_fb
= crtc
->primary
->fb
;
378 struct udl_framebuffer
*uold_fb
= to_udl_fb(old_fb
);
379 uold_fb
->active_16
= false;
381 ufb
->active_16
= true;
383 udl_handle_damage(ufb
, 0, 0, fb
->width
, fb
->height
);
385 spin_lock_irqsave(&dev
->event_lock
, flags
);
387 drm_send_vblank_event(dev
, 0, event
);
388 spin_unlock_irqrestore(&dev
->event_lock
, flags
);
389 crtc
->primary
->fb
= fb
;
394 static void udl_crtc_prepare(struct drm_crtc
*crtc
)
398 static void udl_crtc_commit(struct drm_crtc
*crtc
)
400 udl_crtc_dpms(crtc
, DRM_MODE_DPMS_ON
);
403 static struct drm_crtc_helper_funcs udl_helper_funcs
= {
404 .dpms
= udl_crtc_dpms
,
405 .mode_fixup
= udl_crtc_mode_fixup
,
406 .mode_set
= udl_crtc_mode_set
,
407 .prepare
= udl_crtc_prepare
,
408 .commit
= udl_crtc_commit
,
409 .disable
= udl_crtc_disable
,
412 static const struct drm_crtc_funcs udl_crtc_funcs
= {
413 .set_config
= drm_crtc_helper_set_config
,
414 .destroy
= udl_crtc_destroy
,
415 .page_flip
= udl_crtc_page_flip
,
418 static int udl_crtc_init(struct drm_device
*dev
)
420 struct drm_crtc
*crtc
;
422 crtc
= kzalloc(sizeof(struct drm_crtc
) + sizeof(struct drm_connector
*), GFP_KERNEL
);
426 drm_crtc_init(dev
, crtc
, &udl_crtc_funcs
);
427 drm_crtc_helper_add(crtc
, &udl_helper_funcs
);
432 static const struct drm_mode_config_funcs udl_mode_funcs
= {
433 .fb_create
= udl_fb_user_fb_create
,
434 .output_poll_changed
= NULL
,
437 int udl_modeset_init(struct drm_device
*dev
)
439 struct drm_encoder
*encoder
;
440 drm_mode_config_init(dev
);
442 dev
->mode_config
.min_width
= 640;
443 dev
->mode_config
.min_height
= 480;
445 dev
->mode_config
.max_width
= 2048;
446 dev
->mode_config
.max_height
= 2048;
448 dev
->mode_config
.prefer_shadow
= 0;
449 dev
->mode_config
.preferred_depth
= 24;
451 dev
->mode_config
.funcs
= &udl_mode_funcs
;
453 drm_mode_create_dirty_info_property(dev
);
457 encoder
= udl_encoder_init(dev
);
459 udl_connector_init(dev
, encoder
);
464 void udl_modeset_cleanup(struct drm_device
*dev
)
466 drm_mode_config_cleanup(dev
);