aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/udl/udl_modeset.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@gmail.com>2010-12-14 16:14:24 -0500
committerDave Airlie <airlied@redhat.com>2012-03-15 09:35:34 -0400
commit5320918b9a87865223fd6b228e530bf30bc64d9d (patch)
tree2bc55de1fc03c57851fd86d0cfaa7377d34cdc25 /drivers/gpu/drm/udl/udl_modeset.c
parent2c07a21d6fb0be47fda696a618b726ea258ed1dd (diff)
drm/udl: initial UDL driver (v4)
This is an initial drm/kms driver for the displaylink devices. Supports fb_defio, supports KMS dumb interface supports 24bpp via conversion to 16bpp, hw can do this better. supports hot unplug using new drm core features. On an unplug, it disables connector polling, unplugs connectors from sysfs, unplugs fbdev layer (using Kay's API), drops all the USB device URBs, and call the drm core to unplug the device. This driver is based in large parts on udlfb.c so I've licensed it under GPLv2. Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/udl/udl_modeset.c')
-rw-r--r--drivers/gpu/drm/udl/udl_modeset.c414
1 files changed, 414 insertions, 0 deletions
diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c
new file mode 100644
index 000000000000..b3ecb3d12a1d
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_modeset.c
@@ -0,0 +1,414 @@
1/*
2 * Copyright (C) 2012 Red Hat
3 *
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>
8
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
11 * more details.
12 */
13
14#include "drmP.h"
15#include "drm_crtc.h"
16#include "drm_crtc_helper.h"
17#include "udl_drv.h"
18
19/*
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
22 */
23static char *udl_set_register(char *buf, u8 reg, u8 val)
24{
25 *buf++ = 0xAF;
26 *buf++ = 0x20;
27 *buf++ = reg;
28 *buf++ = val;
29 return buf;
30}
31
32static char *udl_vidreg_lock(char *buf)
33{
34 return udl_set_register(buf, 0xFF, 0x00);
35}
36
37static char *udl_vidreg_unlock(char *buf)
38{
39 return udl_set_register(buf, 0xFF, 0xFF);
40}
41
42/*
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)
47 */
48static char *udl_enable_hvsync(char *buf, bool enable)
49{
50 if (enable)
51 return udl_set_register(buf, 0x1F, 0x00);
52 else
53 return udl_set_register(buf, 0x1F, 0x07);
54}
55
56static char *udl_set_color_depth(char *buf, u8 selection)
57{
58 return udl_set_register(buf, 0x00, selection);
59}
60
61static char *udl_set_base16bpp(char *wrptr, u32 base)
62{
63 /* the base pointer is 16 bits wide, 0x20 is hi byte. */
64 wrptr = udl_set_register(wrptr, 0x20, base >> 16);
65 wrptr = udl_set_register(wrptr, 0x21, base >> 8);
66 return udl_set_register(wrptr, 0x22, base);
67}
68
69/*
70 * DisplayLink HW has separate 16bpp and 8bpp framebuffers.
71 * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer
72 */
73static char *udl_set_base8bpp(char *wrptr, u32 base)
74{
75 wrptr = udl_set_register(wrptr, 0x26, base >> 16);
76 wrptr = udl_set_register(wrptr, 0x27, base >> 8);
77 return udl_set_register(wrptr, 0x28, base);
78}
79
80static char *udl_set_register_16(char *wrptr, u8 reg, u16 value)
81{
82 wrptr = udl_set_register(wrptr, reg, value >> 8);
83 return udl_set_register(wrptr, reg+1, value);
84}
85
86/*
87 * This is kind of weird because the controller takes some
88 * register values in a different byte order than other registers.
89 */
90static char *udl_set_register_16be(char *wrptr, u8 reg, u16 value)
91{
92 wrptr = udl_set_register(wrptr, reg, value);
93 return udl_set_register(wrptr, reg+1, value >> 8);
94}
95
96/*
97 * LFSR is linear feedback shift register. The reason we have this is
98 * because the display controller needs to minimize the clock depth of
99 * various counters used in the display path. So this code reverses the
100 * provided value into the lfsr16 value by counting backwards to get
101 * the value that needs to be set in the hardware comparator to get the
102 * same actual count. This makes sense once you read above a couple of
103 * times and think about it from a hardware perspective.
104 */
105static u16 udl_lfsr16(u16 actual_count)
106{
107 u32 lv = 0xFFFF; /* This is the lfsr value that the hw starts with */
108
109 while (actual_count--) {
110 lv = ((lv << 1) |
111 (((lv >> 15) ^ (lv >> 4) ^ (lv >> 2) ^ (lv >> 1)) & 1))
112 & 0xFFFF;
113 }
114
115 return (u16) lv;
116}
117
118/*
119 * This does LFSR conversion on the value that is to be written.
120 * See LFSR explanation above for more detail.
121 */
122static char *udl_set_register_lfsr16(char *wrptr, u8 reg, u16 value)
123{
124 return udl_set_register_16(wrptr, reg, udl_lfsr16(value));
125}
126
127/*
128 * This takes a standard fbdev screeninfo struct and all of its monitor mode
129 * details and converts them into the DisplayLink equivalent register commands.
130 ERR(vreg(dev, 0x00, (color_depth == 16) ? 0 : 1));
131 ERR(vreg_lfsr16(dev, 0x01, xDisplayStart));
132 ERR(vreg_lfsr16(dev, 0x03, xDisplayEnd));
133 ERR(vreg_lfsr16(dev, 0x05, yDisplayStart));
134 ERR(vreg_lfsr16(dev, 0x07, yDisplayEnd));
135 ERR(vreg_lfsr16(dev, 0x09, xEndCount));
136 ERR(vreg_lfsr16(dev, 0x0B, hSyncStart));
137 ERR(vreg_lfsr16(dev, 0x0D, hSyncEnd));
138 ERR(vreg_big_endian(dev, 0x0F, hPixels));
139 ERR(vreg_lfsr16(dev, 0x11, yEndCount));
140 ERR(vreg_lfsr16(dev, 0x13, vSyncStart));
141 ERR(vreg_lfsr16(dev, 0x15, vSyncEnd));
142 ERR(vreg_big_endian(dev, 0x17, vPixels));
143 ERR(vreg_little_endian(dev, 0x1B, pixelClock5KHz));
144
145 ERR(vreg(dev, 0x1F, 0));
146
147 ERR(vbuf(dev, WRITE_VIDREG_UNLOCK, DSIZEOF(WRITE_VIDREG_UNLOCK)));
148 */
149static char *udl_set_vid_cmds(char *wrptr, struct drm_display_mode *mode)
150{
151 u16 xds, yds;
152 u16 xde, yde;
153 u16 yec;
154
155 /* x display start */
156 xds = mode->crtc_htotal - mode->crtc_hsync_start;
157 wrptr = udl_set_register_lfsr16(wrptr, 0x01, xds);
158 /* x display end */
159 xde = xds + mode->crtc_hdisplay;
160 wrptr = udl_set_register_lfsr16(wrptr, 0x03, xde);
161
162 /* y display start */
163 yds = mode->crtc_vtotal - mode->crtc_vsync_start;
164 wrptr = udl_set_register_lfsr16(wrptr, 0x05, yds);
165 /* y display end */
166 yde = yds + mode->crtc_vdisplay;
167 wrptr = udl_set_register_lfsr16(wrptr, 0x07, yde);
168
169 /* x end count is active + blanking - 1 */
170 wrptr = udl_set_register_lfsr16(wrptr, 0x09,
171 mode->crtc_htotal - 1);
172
173 /* libdlo hardcodes hsync start to 1 */
174 wrptr = udl_set_register_lfsr16(wrptr, 0x0B, 1);
175
176 /* hsync end is width of sync pulse + 1 */
177 wrptr = udl_set_register_lfsr16(wrptr, 0x0D,
178 mode->crtc_hsync_end - mode->crtc_hsync_start + 1);
179
180 /* hpixels is active pixels */
181 wrptr = udl_set_register_16(wrptr, 0x0F, mode->hdisplay);
182
183 /* yendcount is vertical active + vertical blanking */
184 yec = mode->crtc_vtotal;
185 wrptr = udl_set_register_lfsr16(wrptr, 0x11, yec);
186
187 /* libdlo hardcodes vsync start to 0 */
188 wrptr = udl_set_register_lfsr16(wrptr, 0x13, 0);
189
190 /* vsync end is width of vsync pulse */
191 wrptr = udl_set_register_lfsr16(wrptr, 0x15, mode->crtc_vsync_end - mode->crtc_vsync_start);
192
193 /* vpixels is active pixels */
194 wrptr = udl_set_register_16(wrptr, 0x17, mode->crtc_vdisplay);
195
196 wrptr = udl_set_register_16be(wrptr, 0x1B,
197 mode->clock / 5);
198
199 return wrptr;
200}
201
202static int udl_crtc_write_mode_to_hw(struct drm_crtc *crtc)
203{
204 struct drm_device *dev = crtc->dev;
205 struct udl_device *udl = dev->dev_private;
206 struct urb *urb;
207 char *buf;
208 int retval;
209
210 urb = udl_get_urb(dev);
211 if (!urb)
212 return -ENOMEM;
213
214 buf = (char *)urb->transfer_buffer;
215
216 memcpy(buf, udl->mode_buf, udl->mode_buf_len);
217 retval = udl_submit_urb(dev, urb, udl->mode_buf_len);
218 DRM_INFO("write mode info %d\n", udl->mode_buf_len);
219 return retval;
220}
221
222
223static void udl_crtc_dpms(struct drm_crtc *crtc, int mode)
224{
225 struct drm_device *dev = crtc->dev;
226 struct udl_device *udl = dev->dev_private;
227 int retval;
228
229 if (mode == DRM_MODE_DPMS_OFF) {
230 char *buf;
231 struct urb *urb;
232 urb = udl_get_urb(dev);
233 if (!urb)
234 return;
235
236 buf = (char *)urb->transfer_buffer;
237 buf = udl_vidreg_lock(buf);
238 buf = udl_enable_hvsync(buf, false);
239 buf = udl_vidreg_unlock(buf);
240
241 retval = udl_submit_urb(dev, urb, buf - (char *)
242 urb->transfer_buffer);
243 } else {
244 if (udl->mode_buf_len == 0) {
245 DRM_ERROR("Trying to enable DPMS with no mode\n");
246 return;
247 }
248 udl_crtc_write_mode_to_hw(crtc);
249 }
250
251}
252
253static bool udl_crtc_mode_fixup(struct drm_crtc *crtc,
254 struct drm_display_mode *mode,
255 struct drm_display_mode *adjusted_mode)
256
257{
258 return true;
259}
260
261#if 0
262static int
263udl_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
264 int x, int y, enum mode_set_atomic state)
265{
266 return 0;
267}
268
269static int
270udl_pipe_set_base(struct drm_crtc *crtc, int x, int y,
271 struct drm_framebuffer *old_fb)
272{
273 return 0;
274}
275#endif
276
277static int udl_crtc_mode_set(struct drm_crtc *crtc,
278 struct drm_display_mode *mode,
279 struct drm_display_mode *adjusted_mode,
280 int x, int y,
281 struct drm_framebuffer *old_fb)
282
283{
284 struct drm_device *dev = crtc->dev;
285 struct udl_framebuffer *ufb = to_udl_fb(crtc->fb);
286 struct udl_device *udl = dev->dev_private;
287 char *buf;
288 char *wrptr;
289 int color_depth = 0;
290
291 buf = (char *)udl->mode_buf;
292
293 /* for now we just clip 24 -> 16 - if we fix that fix this */
294 /*if (crtc->fb->bits_per_pixel != 16)
295 color_depth = 1; */
296
297 /* This first section has to do with setting the base address on the
298 * controller * associated with the display. There are 2 base
299 * pointers, currently, we only * use the 16 bpp segment.
300 */
301 wrptr = udl_vidreg_lock(buf);
302 wrptr = udl_set_color_depth(wrptr, color_depth);
303 /* set base for 16bpp segment to 0 */
304 wrptr = udl_set_base16bpp(wrptr, 0);
305 /* set base for 8bpp segment to end of fb */
306 wrptr = udl_set_base8bpp(wrptr, 2 * mode->vdisplay * mode->hdisplay);
307
308 wrptr = udl_set_vid_cmds(wrptr, adjusted_mode);
309 wrptr = udl_enable_hvsync(wrptr, true);
310 wrptr = udl_vidreg_unlock(wrptr);
311
312 ufb->active_16 = true;
313 if (old_fb) {
314 struct udl_framebuffer *uold_fb = to_udl_fb(old_fb);
315 uold_fb->active_16 = false;
316 }
317 udl->mode_buf_len = wrptr - buf;
318
319 /* damage all of it */
320 udl_handle_damage(ufb, 0, 0, ufb->base.width, ufb->base.height);
321 return 0;
322}
323
324
325static void udl_crtc_disable(struct drm_crtc *crtc)
326{
327
328
329}
330
331static void udl_crtc_destroy(struct drm_crtc *crtc)
332{
333 drm_crtc_cleanup(crtc);
334 kfree(crtc);
335}
336
337static void udl_load_lut(struct drm_crtc *crtc)
338{
339}
340
341static void udl_crtc_prepare(struct drm_crtc *crtc)
342{
343}
344
345static void udl_crtc_commit(struct drm_crtc *crtc)
346{
347 udl_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
348}
349
350static struct drm_crtc_helper_funcs udl_helper_funcs = {
351 .dpms = udl_crtc_dpms,
352 .mode_fixup = udl_crtc_mode_fixup,
353 .mode_set = udl_crtc_mode_set,
354 .prepare = udl_crtc_prepare,
355 .commit = udl_crtc_commit,
356 .disable = udl_crtc_disable,
357 .load_lut = udl_load_lut,
358};
359
360static const struct drm_crtc_funcs udl_crtc_funcs = {
361 .set_config = drm_crtc_helper_set_config,
362 .destroy = udl_crtc_destroy,
363};
364
365int udl_crtc_init(struct drm_device *dev)
366{
367 struct drm_crtc *crtc;
368
369 crtc = kzalloc(sizeof(struct drm_crtc) + sizeof(struct drm_connector *), GFP_KERNEL);
370 if (crtc == NULL)
371 return -ENOMEM;
372
373 drm_crtc_init(dev, crtc, &udl_crtc_funcs);
374 drm_crtc_helper_add(crtc, &udl_helper_funcs);
375
376 return 0;
377}
378
379static const struct drm_mode_config_funcs udl_mode_funcs = {
380 .fb_create = udl_fb_user_fb_create,
381 .output_poll_changed = NULL,
382};
383
384int udl_modeset_init(struct drm_device *dev)
385{
386 struct drm_encoder *encoder;
387 drm_mode_config_init(dev);
388
389 dev->mode_config.min_width = 640;
390 dev->mode_config.min_height = 480;
391
392 dev->mode_config.max_width = 2048;
393 dev->mode_config.max_height = 2048;
394
395 dev->mode_config.prefer_shadow = 0;
396 dev->mode_config.preferred_depth = 24;
397
398 dev->mode_config.funcs = (void *)&udl_mode_funcs;
399
400 drm_mode_create_dirty_info_property(dev);
401
402 udl_crtc_init(dev);
403
404 encoder = udl_encoder_init(dev);
405
406 udl_connector_init(dev, encoder);
407
408 return 0;
409}
410
411void udl_modeset_cleanup(struct drm_device *dev)
412{
413 drm_mode_config_cleanup(dev);
414}