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>
20 * All DisplayLink bulk operations start with 0xAF, followed by specific code
21 * All operations are written to buffers which then later get sent to device
23 static char *udl_set_register(char *buf
, u8 reg
, u8 val
)
32 static char *udl_vidreg_lock(char *buf
)
34 return udl_set_register(buf
, 0xFF, 0x00);
37 static char *udl_vidreg_unlock(char *buf
)
39 return udl_set_register(buf
, 0xFF, 0xFF);
43 * On/Off for driving the DisplayLink framebuffer to the display
44 * 0x00 H and V sync on
45 * 0x01 H and V sync off (screen blank but powered)
46 * 0x07 DPMS powerdown (requires modeset to come back)
48 static char *udl_set_blank(char *buf
, int dpms_mode
)
52 case DRM_MODE_DPMS_OFF
:
55 case DRM_MODE_DPMS_STANDBY
:
58 case DRM_MODE_DPMS_SUSPEND
:
61 case DRM_MODE_DPMS_ON
:
66 return udl_set_register(buf
, 0x1f, reg
);
69 static char *udl_set_color_depth(char *buf
, u8 selection
)
71 return udl_set_register(buf
, 0x00, selection
);
74 static char *udl_set_base16bpp(char *wrptr
, u32 base
)
76 /* the base pointer is 16 bits wide, 0x20 is hi byte. */
77 wrptr
= udl_set_register(wrptr
, 0x20, base
>> 16);
78 wrptr
= udl_set_register(wrptr
, 0x21, base
>> 8);
79 return udl_set_register(wrptr
, 0x22, base
);
83 * DisplayLink HW has separate 16bpp and 8bpp framebuffers.
84 * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer
86 static char *udl_set_base8bpp(char *wrptr
, u32 base
)
88 wrptr
= udl_set_register(wrptr
, 0x26, base
>> 16);
89 wrptr
= udl_set_register(wrptr
, 0x27, base
>> 8);
90 return udl_set_register(wrptr
, 0x28, base
);
93 static char *udl_set_register_16(char *wrptr
, u8 reg
, u16 value
)
95 wrptr
= udl_set_register(wrptr
, reg
, value
>> 8);
96 return udl_set_register(wrptr
, reg
+1, value
);
100 * This is kind of weird because the controller takes some
101 * register values in a different byte order than other registers.
103 static char *udl_set_register_16be(char *wrptr
, u8 reg
, u16 value
)
105 wrptr
= udl_set_register(wrptr
, reg
, value
);
106 return udl_set_register(wrptr
, reg
+1, value
>> 8);
110 * LFSR is linear feedback shift register. The reason we have this is
111 * because the display controller needs to minimize the clock depth of
112 * various counters used in the display path. So this code reverses the
113 * provided value into the lfsr16 value by counting backwards to get
114 * the value that needs to be set in the hardware comparator to get the
115 * same actual count. This makes sense once you read above a couple of
116 * times and think about it from a hardware perspective.
118 static u16
udl_lfsr16(u16 actual_count
)
120 u32 lv
= 0xFFFF; /* This is the lfsr value that the hw starts with */
122 while (actual_count
--) {
124 (((lv
>> 15) ^ (lv
>> 4) ^ (lv
>> 2) ^ (lv
>> 1)) & 1))
132 * This does LFSR conversion on the value that is to be written.
133 * See LFSR explanation above for more detail.
135 static char *udl_set_register_lfsr16(char *wrptr
, u8 reg
, u16 value
)
137 return udl_set_register_16(wrptr
, reg
, udl_lfsr16(value
));
141 * This takes a standard fbdev screeninfo struct and all of its monitor mode
142 * details and converts them into the DisplayLink equivalent register commands.
143 ERR(vreg(dev, 0x00, (color_depth == 16) ? 0 : 1));
144 ERR(vreg_lfsr16(dev, 0x01, xDisplayStart));
145 ERR(vreg_lfsr16(dev, 0x03, xDisplayEnd));
146 ERR(vreg_lfsr16(dev, 0x05, yDisplayStart));
147 ERR(vreg_lfsr16(dev, 0x07, yDisplayEnd));
148 ERR(vreg_lfsr16(dev, 0x09, xEndCount));
149 ERR(vreg_lfsr16(dev, 0x0B, hSyncStart));
150 ERR(vreg_lfsr16(dev, 0x0D, hSyncEnd));
151 ERR(vreg_big_endian(dev, 0x0F, hPixels));
152 ERR(vreg_lfsr16(dev, 0x11, yEndCount));
153 ERR(vreg_lfsr16(dev, 0x13, vSyncStart));
154 ERR(vreg_lfsr16(dev, 0x15, vSyncEnd));
155 ERR(vreg_big_endian(dev, 0x17, vPixels));
156 ERR(vreg_little_endian(dev, 0x1B, pixelClock5KHz));
158 ERR(vreg(dev, 0x1F, 0));
160 ERR(vbuf(dev, WRITE_VIDREG_UNLOCK, DSIZEOF(WRITE_VIDREG_UNLOCK)));
162 static char *udl_set_vid_cmds(char *wrptr
, struct drm_display_mode
*mode
)
168 /* x display start */
169 xds
= mode
->crtc_htotal
- mode
->crtc_hsync_start
;
170 wrptr
= udl_set_register_lfsr16(wrptr
, 0x01, xds
);
172 xde
= xds
+ mode
->crtc_hdisplay
;
173 wrptr
= udl_set_register_lfsr16(wrptr
, 0x03, xde
);
175 /* y display start */
176 yds
= mode
->crtc_vtotal
- mode
->crtc_vsync_start
;
177 wrptr
= udl_set_register_lfsr16(wrptr
, 0x05, yds
);
179 yde
= yds
+ mode
->crtc_vdisplay
;
180 wrptr
= udl_set_register_lfsr16(wrptr
, 0x07, yde
);
182 /* x end count is active + blanking - 1 */
183 wrptr
= udl_set_register_lfsr16(wrptr
, 0x09,
184 mode
->crtc_htotal
- 1);
186 /* libdlo hardcodes hsync start to 1 */
187 wrptr
= udl_set_register_lfsr16(wrptr
, 0x0B, 1);
189 /* hsync end is width of sync pulse + 1 */
190 wrptr
= udl_set_register_lfsr16(wrptr
, 0x0D,
191 mode
->crtc_hsync_end
- mode
->crtc_hsync_start
+ 1);
193 /* hpixels is active pixels */
194 wrptr
= udl_set_register_16(wrptr
, 0x0F, mode
->hdisplay
);
196 /* yendcount is vertical active + vertical blanking */
197 yec
= mode
->crtc_vtotal
;
198 wrptr
= udl_set_register_lfsr16(wrptr
, 0x11, yec
);
200 /* libdlo hardcodes vsync start to 0 */
201 wrptr
= udl_set_register_lfsr16(wrptr
, 0x13, 0);
203 /* vsync end is width of vsync pulse */
204 wrptr
= udl_set_register_lfsr16(wrptr
, 0x15, mode
->crtc_vsync_end
- mode
->crtc_vsync_start
);
206 /* vpixels is active pixels */
207 wrptr
= udl_set_register_16(wrptr
, 0x17, mode
->crtc_vdisplay
);
209 wrptr
= udl_set_register_16be(wrptr
, 0x1B,
215 static char *udl_dummy_render(char *wrptr
)
218 *wrptr
++ = 0x6A; /* copy */
219 *wrptr
++ = 0x00; /* from addr */
222 *wrptr
++ = 0x01; /* one pixel */
223 *wrptr
++ = 0x00; /* to address */
229 static int udl_crtc_write_mode_to_hw(struct drm_crtc
*crtc
)
231 struct drm_device
*dev
= crtc
->dev
;
232 struct udl_device
*udl
= dev
->dev_private
;
237 urb
= udl_get_urb(dev
);
241 buf
= (char *)urb
->transfer_buffer
;
243 memcpy(buf
, udl
->mode_buf
, udl
->mode_buf_len
);
244 retval
= udl_submit_urb(dev
, urb
, udl
->mode_buf_len
);
245 DRM_INFO("write mode info %d\n", udl
->mode_buf_len
);
250 static void udl_crtc_dpms(struct drm_crtc
*crtc
, int mode
)
252 struct drm_device
*dev
= crtc
->dev
;
253 struct udl_device
*udl
= dev
->dev_private
;
256 if (mode
== DRM_MODE_DPMS_OFF
) {
259 urb
= udl_get_urb(dev
);
263 buf
= (char *)urb
->transfer_buffer
;
264 buf
= udl_vidreg_lock(buf
);
265 buf
= udl_set_blank(buf
, mode
);
266 buf
= udl_vidreg_unlock(buf
);
268 buf
= udl_dummy_render(buf
);
269 retval
= udl_submit_urb(dev
, urb
, buf
- (char *)
270 urb
->transfer_buffer
);
272 if (udl
->mode_buf_len
== 0) {
273 DRM_ERROR("Trying to enable DPMS with no mode\n");
276 udl_crtc_write_mode_to_hw(crtc
);
281 static bool udl_crtc_mode_fixup(struct drm_crtc
*crtc
,
282 const struct drm_display_mode
*mode
,
283 struct drm_display_mode
*adjusted_mode
)
291 udl_pipe_set_base_atomic(struct drm_crtc
*crtc
, struct drm_framebuffer
*fb
,
292 int x
, int y
, enum mode_set_atomic state
)
298 udl_pipe_set_base(struct drm_crtc
*crtc
, int x
, int y
,
299 struct drm_framebuffer
*old_fb
)
305 static int udl_crtc_mode_set(struct drm_crtc
*crtc
,
306 struct drm_display_mode
*mode
,
307 struct drm_display_mode
*adjusted_mode
,
309 struct drm_framebuffer
*old_fb
)
312 struct drm_device
*dev
= crtc
->dev
;
313 struct udl_framebuffer
*ufb
= to_udl_fb(crtc
->fb
);
314 struct udl_device
*udl
= dev
->dev_private
;
319 buf
= (char *)udl
->mode_buf
;
321 /* for now we just clip 24 -> 16 - if we fix that fix this */
322 /*if (crtc->fb->bits_per_pixel != 16)
325 /* This first section has to do with setting the base address on the
326 * controller * associated with the display. There are 2 base
327 * pointers, currently, we only * use the 16 bpp segment.
329 wrptr
= udl_vidreg_lock(buf
);
330 wrptr
= udl_set_color_depth(wrptr
, color_depth
);
331 /* set base for 16bpp segment to 0 */
332 wrptr
= udl_set_base16bpp(wrptr
, 0);
333 /* set base for 8bpp segment to end of fb */
334 wrptr
= udl_set_base8bpp(wrptr
, 2 * mode
->vdisplay
* mode
->hdisplay
);
336 wrptr
= udl_set_vid_cmds(wrptr
, adjusted_mode
);
337 wrptr
= udl_set_blank(wrptr
, DRM_MODE_DPMS_ON
);
338 wrptr
= udl_vidreg_unlock(wrptr
);
340 wrptr
= udl_dummy_render(wrptr
);
342 ufb
->active_16
= true;
344 struct udl_framebuffer
*uold_fb
= to_udl_fb(old_fb
);
345 uold_fb
->active_16
= false;
347 udl
->mode_buf_len
= wrptr
- buf
;
349 /* damage all of it */
350 udl_handle_damage(ufb
, 0, 0, ufb
->base
.width
, ufb
->base
.height
);
355 static void udl_crtc_disable(struct drm_crtc
*crtc
)
357 udl_crtc_dpms(crtc
, DRM_MODE_DPMS_OFF
);
360 static void udl_crtc_destroy(struct drm_crtc
*crtc
)
362 drm_crtc_cleanup(crtc
);
366 static void udl_crtc_prepare(struct drm_crtc
*crtc
)
370 static void udl_crtc_commit(struct drm_crtc
*crtc
)
372 udl_crtc_dpms(crtc
, DRM_MODE_DPMS_ON
);
375 static struct drm_crtc_helper_funcs udl_helper_funcs
= {
376 .dpms
= udl_crtc_dpms
,
377 .mode_fixup
= udl_crtc_mode_fixup
,
378 .mode_set
= udl_crtc_mode_set
,
379 .prepare
= udl_crtc_prepare
,
380 .commit
= udl_crtc_commit
,
381 .disable
= udl_crtc_disable
,
384 static const struct drm_crtc_funcs udl_crtc_funcs
= {
385 .set_config
= drm_crtc_helper_set_config
,
386 .destroy
= udl_crtc_destroy
,
389 static int udl_crtc_init(struct drm_device
*dev
)
391 struct drm_crtc
*crtc
;
393 crtc
= kzalloc(sizeof(struct drm_crtc
) + sizeof(struct drm_connector
*), GFP_KERNEL
);
397 drm_crtc_init(dev
, crtc
, &udl_crtc_funcs
);
398 drm_crtc_helper_add(crtc
, &udl_helper_funcs
);
403 static const struct drm_mode_config_funcs udl_mode_funcs
= {
404 .fb_create
= udl_fb_user_fb_create
,
405 .output_poll_changed
= NULL
,
408 int udl_modeset_init(struct drm_device
*dev
)
410 struct drm_encoder
*encoder
;
411 drm_mode_config_init(dev
);
413 dev
->mode_config
.min_width
= 640;
414 dev
->mode_config
.min_height
= 480;
416 dev
->mode_config
.max_width
= 2048;
417 dev
->mode_config
.max_height
= 2048;
419 dev
->mode_config
.prefer_shadow
= 0;
420 dev
->mode_config
.preferred_depth
= 24;
422 dev
->mode_config
.funcs
= &udl_mode_funcs
;
424 drm_mode_create_dirty_info_property(dev
);
428 encoder
= udl_encoder_init(dev
);
430 udl_connector_init(dev
, encoder
);
435 void udl_modeset_cleanup(struct drm_device
*dev
)
437 drm_mode_config_cleanup(dev
);