blob: 50549703584f5b7d91c54d24f037dfa5d26f5ebc [file] [log] [blame]
Dave Airlie785b93e2009-08-28 15:46:53 +10001/*
2 * Copyright (c) 2006-2009 Red Hat Inc.
3 * Copyright (c) 2006-2008 Intel Corporation
4 * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
5 *
6 * DRM framebuffer helper functions
7 *
8 * Permission to use, copy, modify, distribute, and sell this software and its
9 * documentation for any purpose is hereby granted without fee, provided that
10 * the above copyright notice appear in all copies and that both that copyright
11 * notice and this permission notice appear in supporting documentation, and
12 * that the name of the copyright holders not be used in advertising or
13 * publicity pertaining to distribution of the software without specific,
14 * written prior permission. The copyright holders make no representations
15 * about the suitability of this software for any purpose. It is provided "as
16 * is" without express or implied warranty.
17 *
18 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
20 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
21 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
22 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
23 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24 * OF THIS SOFTWARE.
25 *
26 * Authors:
27 * Dave Airlie <airlied@linux.ie>
28 * Jesse Barnes <jesse.barnes@intel.com>
29 */
Andy Shevchenko3b40a442010-02-02 14:40:32 -080030#include <linux/kernel.h>
Dave Airlie785b93e2009-08-28 15:46:53 +100031#include <linux/sysrq.h>
32#include <linux/fb.h>
33#include "drmP.h"
34#include "drm_crtc.h"
35#include "drm_fb_helper.h"
36#include "drm_crtc_helper.h"
37
Dave Airlie6fcefd52009-09-08 11:08:32 +100038MODULE_AUTHOR("David Airlie, Jesse Barnes");
39MODULE_DESCRIPTION("DRM KMS helper");
40MODULE_LICENSE("GPL and additional rights");
41
Dave Airlie785b93e2009-08-28 15:46:53 +100042static LIST_HEAD(kernel_fb_helper_list);
43
Dave Airlied50ba252009-09-23 14:44:08 +100044int drm_fb_helper_add_connector(struct drm_connector *connector)
45{
46 connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
47 if (!connector->fb_helper_private)
48 return -ENOMEM;
49
50 return 0;
Dave Airlied50ba252009-09-23 14:44:08 +100051}
52EXPORT_SYMBOL(drm_fb_helper_add_connector);
53
Dave Airlied50ba252009-09-23 14:44:08 +100054/**
55 * drm_fb_helper_connector_parse_command_line - parse command line for connector
56 * @connector - connector to parse line for
57 * @mode_option - per connector mode option
58 *
59 * This parses the connector specific then generic command lines for
60 * modes and options to configure the connector.
61 *
62 * This uses the same parameters as the fb modedb.c, except for extra
63 * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
64 *
65 * enable/enable Digital/disable bit at the end
66 */
67static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *connector,
68 const char *mode_option)
69{
70 const char *name;
71 unsigned int namelen;
72 int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
73 unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
74 int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
75 int i;
76 enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
77 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
Dave Airlie8ef86782009-09-26 06:39:00 +100078 struct drm_fb_helper_cmdline_mode *cmdline_mode;
Dave Airlied50ba252009-09-23 14:44:08 +100079
Dave Airlie8ef86782009-09-26 06:39:00 +100080 if (!fb_help_conn)
81 return false;
82
83 cmdline_mode = &fb_help_conn->cmdline_mode;
Dave Airlied50ba252009-09-23 14:44:08 +100084 if (!mode_option)
85 mode_option = fb_mode_option;
86
87 if (!mode_option) {
88 cmdline_mode->specified = false;
89 return false;
90 }
91
92 name = mode_option;
93 namelen = strlen(name);
94 for (i = namelen-1; i >= 0; i--) {
95 switch (name[i]) {
96 case '@':
97 namelen = i;
98 if (!refresh_specified && !bpp_specified &&
99 !yres_specified) {
Andy Shevchenko3b40a442010-02-02 14:40:32 -0800100 refresh = simple_strtol(&name[i+1], NULL, 10);
Dave Airlied50ba252009-09-23 14:44:08 +1000101 refresh_specified = 1;
102 if (cvt || rb)
103 cvt = 0;
104 } else
105 goto done;
106 break;
107 case '-':
108 namelen = i;
109 if (!bpp_specified && !yres_specified) {
Andy Shevchenko3b40a442010-02-02 14:40:32 -0800110 bpp = simple_strtol(&name[i+1], NULL, 10);
Dave Airlied50ba252009-09-23 14:44:08 +1000111 bpp_specified = 1;
112 if (cvt || rb)
113 cvt = 0;
114 } else
115 goto done;
116 break;
117 case 'x':
118 if (!yres_specified) {
Andy Shevchenko3b40a442010-02-02 14:40:32 -0800119 yres = simple_strtol(&name[i+1], NULL, 10);
Dave Airlied50ba252009-09-23 14:44:08 +1000120 yres_specified = 1;
121 } else
122 goto done;
123 case '0' ... '9':
124 break;
125 case 'M':
126 if (!yres_specified)
127 cvt = 1;
128 break;
129 case 'R':
130 if (!cvt)
131 rb = 1;
132 break;
133 case 'm':
134 if (!cvt)
135 margins = 1;
136 break;
137 case 'i':
138 if (!cvt)
139 interlace = 1;
140 break;
141 case 'e':
142 force = DRM_FORCE_ON;
143 break;
144 case 'D':
Roel Kluine89a8c92009-12-31 13:06:29 +0100145 if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
Dave Airlied50ba252009-09-23 14:44:08 +1000146 (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
147 force = DRM_FORCE_ON;
148 else
149 force = DRM_FORCE_ON_DIGITAL;
150 break;
151 case 'd':
152 force = DRM_FORCE_OFF;
153 break;
154 default:
155 goto done;
156 }
157 }
158 if (i < 0 && yres_specified) {
Andy Shevchenko3b40a442010-02-02 14:40:32 -0800159 xres = simple_strtol(name, NULL, 10);
Dave Airlied50ba252009-09-23 14:44:08 +1000160 res_specified = 1;
161 }
162done:
163
164 DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
165 drm_get_connector_name(connector), xres, yres,
166 (refresh) ? refresh : 60, (rb) ? " reduced blanking" :
167 "", (margins) ? " with margins" : "", (interlace) ?
168 " interlaced" : "");
169
170 if (force) {
171 const char *s;
172 switch (force) {
173 case DRM_FORCE_OFF: s = "OFF"; break;
174 case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
175 default:
176 case DRM_FORCE_ON: s = "ON"; break;
177 }
178
179 DRM_INFO("forcing %s connector %s\n",
180 drm_get_connector_name(connector), s);
181 connector->force = force;
182 }
183
184 if (res_specified) {
185 cmdline_mode->specified = true;
186 cmdline_mode->xres = xres;
187 cmdline_mode->yres = yres;
188 }
189
190 if (refresh_specified) {
191 cmdline_mode->refresh_specified = true;
192 cmdline_mode->refresh = refresh;
193 }
194
195 if (bpp_specified) {
196 cmdline_mode->bpp_specified = true;
197 cmdline_mode->bpp = bpp;
198 }
199 cmdline_mode->rb = rb ? true : false;
200 cmdline_mode->cvt = cvt ? true : false;
201 cmdline_mode->interlace = interlace ? true : false;
202
203 return true;
204}
205
206int drm_fb_helper_parse_command_line(struct drm_device *dev)
207{
208 struct drm_connector *connector;
209
210 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
211 char *option = NULL;
212
213 /* do something on return - turn off connector maybe */
214 if (fb_get_options(drm_get_connector_name(connector), &option))
215 continue;
216
217 drm_fb_helper_connector_parse_command_line(connector, option);
218 }
219 return 0;
220}
221
Dave Airlie785b93e2009-08-28 15:46:53 +1000222bool drm_fb_helper_force_kernel_mode(void)
223{
224 int i = 0;
225 bool ret, error = false;
226 struct drm_fb_helper *helper;
227
228 if (list_empty(&kernel_fb_helper_list))
229 return false;
230
231 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
232 for (i = 0; i < helper->crtc_count; i++) {
233 struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
234 ret = drm_crtc_helper_set_config(mode_set);
235 if (ret)
236 error = true;
237 }
238 }
239 return error;
240}
241
242int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
243 void *panic_str)
244{
245 DRM_ERROR("panic occurred, switching back to text console\n");
246 return drm_fb_helper_force_kernel_mode();
247 return 0;
248}
249EXPORT_SYMBOL(drm_fb_helper_panic);
250
251static struct notifier_block paniced = {
252 .notifier_call = drm_fb_helper_panic,
253};
254
255/**
256 * drm_fb_helper_restore - restore the framebuffer console (kernel) config
257 *
258 * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
259 */
260void drm_fb_helper_restore(void)
261{
262 bool ret;
263 ret = drm_fb_helper_force_kernel_mode();
264 if (ret == true)
265 DRM_ERROR("Failed to restore crtc configuration\n");
266}
267EXPORT_SYMBOL(drm_fb_helper_restore);
268
Mikael Petterssonbea1d352009-09-28 18:26:25 +0200269#ifdef CONFIG_MAGIC_SYSRQ
Dave Airlie785b93e2009-08-28 15:46:53 +1000270static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
271{
272 drm_fb_helper_restore();
273}
274static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
275
276static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3)
277{
278 schedule_work(&drm_fb_helper_restore_work);
279}
280
281static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
282 .handler = drm_fb_helper_sysrq,
283 .help_msg = "force-fb(V)",
284 .action_msg = "Restore framebuffer console",
285};
Mikael Petterssonbea1d352009-09-28 18:26:25 +0200286#endif
Dave Airlie785b93e2009-08-28 15:46:53 +1000287
288static void drm_fb_helper_on(struct fb_info *info)
289{
290 struct drm_fb_helper *fb_helper = info->par;
291 struct drm_device *dev = fb_helper->dev;
292 struct drm_crtc *crtc;
293 struct drm_encoder *encoder;
294 int i;
295
296 /*
297 * For each CRTC in this fb, turn the crtc on then,
298 * find all associated encoders and turn them on.
299 */
Jesse Barnese87b2c42009-09-17 18:14:41 -0700300 for (i = 0; i < fb_helper->crtc_count; i++) {
301 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
302 struct drm_crtc_helper_funcs *crtc_funcs =
303 crtc->helper_private;
Dave Airlie785b93e2009-08-28 15:46:53 +1000304
Jesse Barnese87b2c42009-09-17 18:14:41 -0700305 /* Only mess with CRTCs in this fb */
306 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
307 !crtc->enabled)
308 continue;
Dave Airlie785b93e2009-08-28 15:46:53 +1000309
Jesse Barnese87b2c42009-09-17 18:14:41 -0700310 mutex_lock(&dev->mode_config.mutex);
311 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
312 mutex_unlock(&dev->mode_config.mutex);
Dave Airlie785b93e2009-08-28 15:46:53 +1000313
Jesse Barnese87b2c42009-09-17 18:14:41 -0700314 /* Found a CRTC on this fb, now find encoders */
315 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
316 if (encoder->crtc == crtc) {
317 struct drm_encoder_helper_funcs *encoder_funcs;
Dave Airlie785b93e2009-08-28 15:46:53 +1000318
Jesse Barnese87b2c42009-09-17 18:14:41 -0700319 encoder_funcs = encoder->helper_private;
320 mutex_lock(&dev->mode_config.mutex);
321 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
322 mutex_unlock(&dev->mode_config.mutex);
323 }
Dave Airlie785b93e2009-08-28 15:46:53 +1000324 }
325 }
326 }
327}
328
329static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
330{
331 struct drm_fb_helper *fb_helper = info->par;
332 struct drm_device *dev = fb_helper->dev;
333 struct drm_crtc *crtc;
334 struct drm_encoder *encoder;
335 int i;
336
337 /*
338 * For each CRTC in this fb, find all associated encoders
339 * and turn them off, then turn off the CRTC.
340 */
Jesse Barnese87b2c42009-09-17 18:14:41 -0700341 for (i = 0; i < fb_helper->crtc_count; i++) {
342 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
343 struct drm_crtc_helper_funcs *crtc_funcs =
344 crtc->helper_private;
Dave Airlie785b93e2009-08-28 15:46:53 +1000345
Jesse Barnese87b2c42009-09-17 18:14:41 -0700346 /* Only mess with CRTCs in this fb */
347 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
348 !crtc->enabled)
349 continue;
Dave Airlie785b93e2009-08-28 15:46:53 +1000350
Jesse Barnese87b2c42009-09-17 18:14:41 -0700351 /* Found a CRTC on this fb, now find encoders */
352 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
353 if (encoder->crtc == crtc) {
354 struct drm_encoder_helper_funcs *encoder_funcs;
Dave Airlie785b93e2009-08-28 15:46:53 +1000355
Jesse Barnese87b2c42009-09-17 18:14:41 -0700356 encoder_funcs = encoder->helper_private;
357 mutex_lock(&dev->mode_config.mutex);
358 encoder_funcs->dpms(encoder, dpms_mode);
359 mutex_unlock(&dev->mode_config.mutex);
360 }
361 }
James Simmons731b5a12009-10-29 20:39:07 +0000362 mutex_lock(&dev->mode_config.mutex);
363 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
364 mutex_unlock(&dev->mode_config.mutex);
Dave Airlie785b93e2009-08-28 15:46:53 +1000365 }
Dave Airlie785b93e2009-08-28 15:46:53 +1000366 }
367}
368
369int drm_fb_helper_blank(int blank, struct fb_info *info)
370{
371 switch (blank) {
James Simmons731b5a12009-10-29 20:39:07 +0000372 /* Display: On; HSync: On, VSync: On */
Dave Airlie785b93e2009-08-28 15:46:53 +1000373 case FB_BLANK_UNBLANK:
374 drm_fb_helper_on(info);
375 break;
James Simmons731b5a12009-10-29 20:39:07 +0000376 /* Display: Off; HSync: On, VSync: On */
Dave Airlie785b93e2009-08-28 15:46:53 +1000377 case FB_BLANK_NORMAL:
Zhenyu Wang5fd4df42010-01-18 16:47:04 +0800378 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
Dave Airlie785b93e2009-08-28 15:46:53 +1000379 break;
James Simmons731b5a12009-10-29 20:39:07 +0000380 /* Display: Off; HSync: Off, VSync: On */
Dave Airlie785b93e2009-08-28 15:46:53 +1000381 case FB_BLANK_HSYNC_SUSPEND:
382 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
383 break;
James Simmons731b5a12009-10-29 20:39:07 +0000384 /* Display: Off; HSync: On, VSync: Off */
Dave Airlie785b93e2009-08-28 15:46:53 +1000385 case FB_BLANK_VSYNC_SUSPEND:
386 drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
387 break;
James Simmons731b5a12009-10-29 20:39:07 +0000388 /* Display: Off; HSync: Off, VSync: Off */
Dave Airlie785b93e2009-08-28 15:46:53 +1000389 case FB_BLANK_POWERDOWN:
390 drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
391 break;
392 }
393 return 0;
394}
395EXPORT_SYMBOL(drm_fb_helper_blank);
396
397static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
398{
399 int i;
400
401 for (i = 0; i < helper->crtc_count; i++)
402 kfree(helper->crtc_info[i].mode_set.connectors);
403 kfree(helper->crtc_info);
404}
405
406int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
407{
408 struct drm_device *dev = helper->dev;
409 struct drm_crtc *crtc;
410 int ret = 0;
411 int i;
412
413 helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
414 if (!helper->crtc_info)
415 return -ENOMEM;
416
417 helper->crtc_count = crtc_count;
418
419 for (i = 0; i < crtc_count; i++) {
420 helper->crtc_info[i].mode_set.connectors =
421 kcalloc(max_conn_count,
422 sizeof(struct drm_connector *),
423 GFP_KERNEL);
424
425 if (!helper->crtc_info[i].mode_set.connectors) {
426 ret = -ENOMEM;
427 goto out_free;
428 }
429 helper->crtc_info[i].mode_set.num_connectors = 0;
430 }
431
432 i = 0;
433 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
434 helper->crtc_info[i].crtc_id = crtc->base.id;
435 helper->crtc_info[i].mode_set.crtc = crtc;
436 i++;
437 }
438 helper->conn_limit = max_conn_count;
439 return 0;
440out_free:
441 drm_fb_helper_crtc_free(helper);
442 return -ENOMEM;
443}
444EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
445
Dave Airliec850cb72009-10-23 18:49:03 +1000446static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000447 u16 blue, u16 regno, struct fb_info *info)
448{
449 struct drm_fb_helper *fb_helper = info->par;
450 struct drm_framebuffer *fb = fb_helper->fb;
451 int pindex;
452
Dave Airliec850cb72009-10-23 18:49:03 +1000453 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
454 u32 *palette;
455 u32 value;
456 /* place color in psuedopalette */
457 if (regno > 16)
458 return -EINVAL;
459 palette = (u32 *)info->pseudo_palette;
460 red >>= (16 - info->var.red.length);
461 green >>= (16 - info->var.green.length);
462 blue >>= (16 - info->var.blue.length);
463 value = (red << info->var.red.offset) |
464 (green << info->var.green.offset) |
465 (blue << info->var.blue.offset);
466 palette[regno] = value;
467 return 0;
468 }
469
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000470 pindex = regno;
471
472 if (fb->bits_per_pixel == 16) {
473 pindex = regno << 3;
474
475 if (fb->depth == 16 && regno > 63)
Dave Airliec850cb72009-10-23 18:49:03 +1000476 return -EINVAL;
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000477 if (fb->depth == 15 && regno > 31)
Dave Airliec850cb72009-10-23 18:49:03 +1000478 return -EINVAL;
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000479
480 if (fb->depth == 16) {
481 u16 r, g, b;
482 int i;
483 if (regno < 32) {
484 for (i = 0; i < 8; i++)
485 fb_helper->funcs->gamma_set(crtc, red,
486 green, blue, pindex + i);
487 }
488
489 fb_helper->funcs->gamma_get(crtc, &r,
490 &g, &b,
491 pindex >> 1);
492
493 for (i = 0; i < 4; i++)
494 fb_helper->funcs->gamma_set(crtc, r,
495 green, b,
496 (pindex >> 1) + i);
497 }
498 }
499
500 if (fb->depth != 16)
501 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
Dave Airliec850cb72009-10-23 18:49:03 +1000502 return 0;
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000503}
504
Dave Airlie068143d2009-10-05 09:58:02 +1000505int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
506{
507 struct drm_fb_helper *fb_helper = info->par;
508 struct drm_device *dev = fb_helper->dev;
509 u16 *red, *green, *blue, *transp;
510 struct drm_crtc *crtc;
511 int i, rc = 0;
512 int start;
513
514 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
515 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
516 for (i = 0; i < fb_helper->crtc_count; i++) {
517 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
518 break;
519 }
520 if (i == fb_helper->crtc_count)
521 continue;
522
523 red = cmap->red;
524 green = cmap->green;
525 blue = cmap->blue;
526 transp = cmap->transp;
527 start = cmap->start;
528
529 for (i = 0; i < cmap->len; i++) {
530 u16 hred, hgreen, hblue, htransp = 0xffff;
531
532 hred = *red++;
533 hgreen = *green++;
534 hblue = *blue++;
535
536 if (transp)
537 htransp = *transp++;
538
Dave Airliec850cb72009-10-23 18:49:03 +1000539 rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
540 if (rc)
541 return rc;
Dave Airlie068143d2009-10-05 09:58:02 +1000542 }
543 crtc_funcs->load_lut(crtc);
544 }
545 return rc;
546}
547EXPORT_SYMBOL(drm_fb_helper_setcmap);
548
Dave Airlie785b93e2009-08-28 15:46:53 +1000549int drm_fb_helper_setcolreg(unsigned regno,
550 unsigned red,
551 unsigned green,
552 unsigned blue,
553 unsigned transp,
554 struct fb_info *info)
555{
556 struct drm_fb_helper *fb_helper = info->par;
557 struct drm_device *dev = fb_helper->dev;
558 struct drm_crtc *crtc;
559 int i;
Dave Airliec850cb72009-10-23 18:49:03 +1000560 int ret;
Dave Airlie785b93e2009-08-28 15:46:53 +1000561
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000562 if (regno > 255)
563 return 1;
Dave Airlie785b93e2009-08-28 15:46:53 +1000564
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000565 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
566 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
Dave Airlie785b93e2009-08-28 15:46:53 +1000567 for (i = 0; i < fb_helper->crtc_count; i++) {
568 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
569 break;
570 }
571 if (i == fb_helper->crtc_count)
572 continue;
573
Dave Airliec850cb72009-10-23 18:49:03 +1000574 ret = setcolreg(crtc, red, green, blue, regno, info);
575 if (ret)
576 return ret;
Dave Airlie785b93e2009-08-28 15:46:53 +1000577
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000578 crtc_funcs->load_lut(crtc);
Dave Airlie785b93e2009-08-28 15:46:53 +1000579 }
580 return 0;
581}
582EXPORT_SYMBOL(drm_fb_helper_setcolreg);
583
584int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
585 struct fb_info *info)
586{
587 struct drm_fb_helper *fb_helper = info->par;
588 struct drm_framebuffer *fb = fb_helper->fb;
589 int depth;
590
Clemens Ladisch5349ef32009-11-04 09:42:52 +0100591 if (var->pixclock != 0)
Dave Airlie785b93e2009-08-28 15:46:53 +1000592 return -EINVAL;
593
594 /* Need to resize the fb object !!! */
Dave Airlie509c7d82010-01-08 09:27:08 +1000595 if (var->bits_per_pixel > fb->bits_per_pixel || var->xres > fb->width || var->yres > fb->height) {
596 DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
597 "object %dx%d-%d > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel,
598 fb->width, fb->height, fb->bits_per_pixel);
Dave Airlie785b93e2009-08-28 15:46:53 +1000599 return -EINVAL;
600 }
601
602 switch (var->bits_per_pixel) {
603 case 16:
604 depth = (var->green.length == 6) ? 16 : 15;
605 break;
606 case 32:
607 depth = (var->transp.length > 0) ? 32 : 24;
608 break;
609 default:
610 depth = var->bits_per_pixel;
611 break;
612 }
613
614 switch (depth) {
615 case 8:
616 var->red.offset = 0;
617 var->green.offset = 0;
618 var->blue.offset = 0;
619 var->red.length = 8;
620 var->green.length = 8;
621 var->blue.length = 8;
622 var->transp.length = 0;
623 var->transp.offset = 0;
624 break;
625 case 15:
626 var->red.offset = 10;
627 var->green.offset = 5;
628 var->blue.offset = 0;
629 var->red.length = 5;
630 var->green.length = 5;
631 var->blue.length = 5;
632 var->transp.length = 1;
633 var->transp.offset = 15;
634 break;
635 case 16:
636 var->red.offset = 11;
637 var->green.offset = 5;
638 var->blue.offset = 0;
639 var->red.length = 5;
640 var->green.length = 6;
641 var->blue.length = 5;
642 var->transp.length = 0;
643 var->transp.offset = 0;
644 break;
645 case 24:
646 var->red.offset = 16;
647 var->green.offset = 8;
648 var->blue.offset = 0;
649 var->red.length = 8;
650 var->green.length = 8;
651 var->blue.length = 8;
652 var->transp.length = 0;
653 var->transp.offset = 0;
654 break;
655 case 32:
656 var->red.offset = 16;
657 var->green.offset = 8;
658 var->blue.offset = 0;
659 var->red.length = 8;
660 var->green.length = 8;
661 var->blue.length = 8;
662 var->transp.length = 8;
663 var->transp.offset = 24;
664 break;
665 default:
666 return -EINVAL;
667 }
668 return 0;
669}
670EXPORT_SYMBOL(drm_fb_helper_check_var);
671
672/* this will let fbcon do the mode init */
673int drm_fb_helper_set_par(struct fb_info *info)
674{
675 struct drm_fb_helper *fb_helper = info->par;
676 struct drm_device *dev = fb_helper->dev;
677 struct fb_var_screeninfo *var = &info->var;
678 struct drm_crtc *crtc;
679 int ret;
680 int i;
681
Clemens Ladisch5349ef32009-11-04 09:42:52 +0100682 if (var->pixclock != 0) {
Pavel Roskin172e91f2010-02-11 14:31:32 +1000683 DRM_ERROR("PIXEL CLOCK SET\n");
Dave Airlie785b93e2009-08-28 15:46:53 +1000684 return -EINVAL;
685 }
686
687 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
688
689 for (i = 0; i < fb_helper->crtc_count; i++) {
690 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
691 break;
692 }
693 if (i == fb_helper->crtc_count)
694 continue;
695
696 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
697 mutex_lock(&dev->mode_config.mutex);
James Simmonsa2d49ae2009-10-27 21:09:24 +0000698 ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
Dave Airlie785b93e2009-08-28 15:46:53 +1000699 mutex_unlock(&dev->mode_config.mutex);
700 if (ret)
701 return ret;
702 }
703 }
704 return 0;
705}
706EXPORT_SYMBOL(drm_fb_helper_set_par);
707
708int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
709 struct fb_info *info)
710{
711 struct drm_fb_helper *fb_helper = info->par;
712 struct drm_device *dev = fb_helper->dev;
713 struct drm_mode_set *modeset;
714 struct drm_crtc *crtc;
715 int ret = 0;
716 int i;
717
718 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
719 for (i = 0; i < fb_helper->crtc_count; i++) {
720 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
721 break;
722 }
723
724 if (i == fb_helper->crtc_count)
725 continue;
726
727 modeset = &fb_helper->crtc_info[i].mode_set;
728
729 modeset->x = var->xoffset;
730 modeset->y = var->yoffset;
731
732 if (modeset->num_connectors) {
733 mutex_lock(&dev->mode_config.mutex);
734 ret = crtc->funcs->set_config(modeset);
735 mutex_unlock(&dev->mode_config.mutex);
736 if (!ret) {
737 info->var.xoffset = var->xoffset;
738 info->var.yoffset = var->yoffset;
739 }
740 }
741 }
742 return ret;
743}
744EXPORT_SYMBOL(drm_fb_helper_pan_display);
745
746int drm_fb_helper_single_fb_probe(struct drm_device *dev,
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000747 int preferred_bpp,
Dave Airlie785b93e2009-08-28 15:46:53 +1000748 int (*fb_create)(struct drm_device *dev,
749 uint32_t fb_width,
750 uint32_t fb_height,
751 uint32_t surface_width,
752 uint32_t surface_height,
Dave Airlied50ba252009-09-23 14:44:08 +1000753 uint32_t surface_depth,
754 uint32_t surface_bpp,
Dave Airlie785b93e2009-08-28 15:46:53 +1000755 struct drm_framebuffer **fb_ptr))
756{
757 struct drm_crtc *crtc;
758 struct drm_connector *connector;
759 unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
760 unsigned int surface_width = 0, surface_height = 0;
761 int new_fb = 0;
762 int crtc_count = 0;
763 int ret, i, conn_count = 0;
764 struct fb_info *info;
765 struct drm_framebuffer *fb;
766 struct drm_mode_set *modeset = NULL;
767 struct drm_fb_helper *fb_helper;
Dave Airlied50ba252009-09-23 14:44:08 +1000768 uint32_t surface_depth = 24, surface_bpp = 32;
Dave Airlie785b93e2009-08-28 15:46:53 +1000769
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000770 /* if driver picks 8 or 16 by default use that
771 for both depth/bpp */
772 if (preferred_bpp != surface_bpp) {
773 surface_depth = surface_bpp = preferred_bpp;
774 }
Dave Airlie785b93e2009-08-28 15:46:53 +1000775 /* first up get a count of crtcs now in use and new min/maxes width/heights */
Dave Airlied50ba252009-09-23 14:44:08 +1000776 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
777 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
Dave Airlie8ef86782009-09-26 06:39:00 +1000778
779 struct drm_fb_helper_cmdline_mode *cmdline_mode;
780
781 if (!fb_help_conn)
782 continue;
783
784 cmdline_mode = &fb_help_conn->cmdline_mode;
Dave Airlied50ba252009-09-23 14:44:08 +1000785
786 if (cmdline_mode->bpp_specified) {
787 switch (cmdline_mode->bpp) {
788 case 8:
789 surface_depth = surface_bpp = 8;
790 break;
791 case 15:
792 surface_depth = 15;
793 surface_bpp = 16;
794 break;
795 case 16:
796 surface_depth = surface_bpp = 16;
797 break;
798 case 24:
799 surface_depth = surface_bpp = 24;
800 break;
801 case 32:
802 surface_depth = 24;
803 surface_bpp = 32;
804 break;
805 }
806 break;
807 }
808 }
809
Dave Airlie785b93e2009-08-28 15:46:53 +1000810 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
811 if (drm_helper_crtc_in_use(crtc)) {
812 if (crtc->desired_mode) {
813 if (crtc->desired_mode->hdisplay < fb_width)
814 fb_width = crtc->desired_mode->hdisplay;
815
816 if (crtc->desired_mode->vdisplay < fb_height)
817 fb_height = crtc->desired_mode->vdisplay;
818
819 if (crtc->desired_mode->hdisplay > surface_width)
820 surface_width = crtc->desired_mode->hdisplay;
821
822 if (crtc->desired_mode->vdisplay > surface_height)
823 surface_height = crtc->desired_mode->vdisplay;
824 }
825 crtc_count++;
826 }
827 }
828
829 if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
830 /* hmm everyone went away - assume VGA cable just fell out
831 and will come back later. */
832 return 0;
833 }
834
835 /* do we have an fb already? */
836 if (list_empty(&dev->mode_config.fb_kernel_list)) {
837 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
Dave Airlied50ba252009-09-23 14:44:08 +1000838 surface_height, surface_depth, surface_bpp,
839 &fb);
Dave Airlie785b93e2009-08-28 15:46:53 +1000840 if (ret)
841 return -EINVAL;
842 new_fb = 1;
843 } else {
844 fb = list_first_entry(&dev->mode_config.fb_kernel_list,
845 struct drm_framebuffer, filp_head);
846
847 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
848 As really we can't resize an fbdev that is in the wild currently due to fbdev
849 not really being designed for the lower layers moving stuff around under it.
850 - so in the grand style of things - punt. */
851 if ((fb->width < surface_width) ||
852 (fb->height < surface_height)) {
853 DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
854 return -EINVAL;
855 }
856 }
857
858 info = fb->fbdev;
859 fb_helper = info->par;
860
861 crtc_count = 0;
862 /* okay we need to setup new connector sets in the crtcs */
863 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
864 modeset = &fb_helper->crtc_info[crtc_count].mode_set;
865 modeset->fb = fb;
866 conn_count = 0;
867 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
868 if (connector->encoder)
869 if (connector->encoder->crtc == modeset->crtc) {
870 modeset->connectors[conn_count] = connector;
871 conn_count++;
872 if (conn_count > fb_helper->conn_limit)
873 BUG();
874 }
875 }
876
877 for (i = conn_count; i < fb_helper->conn_limit; i++)
878 modeset->connectors[i] = NULL;
879
880 modeset->crtc = crtc;
881 crtc_count++;
882
883 modeset->num_connectors = conn_count;
884 if (modeset->crtc->desired_mode) {
885 if (modeset->mode)
886 drm_mode_destroy(dev, modeset->mode);
887 modeset->mode = drm_mode_duplicate(dev,
888 modeset->crtc->desired_mode);
889 }
890 }
891 fb_helper->crtc_count = crtc_count;
892 fb_helper->fb = fb;
893
894 if (new_fb) {
Clemens Ladisch5349ef32009-11-04 09:42:52 +0100895 info->var.pixclock = 0;
Ben Skeggsb0a007d2009-12-08 11:15:10 +1000896 ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0);
Clemens Ladisch3bea21b2009-11-04 09:43:00 +0100897 if (ret)
898 return ret;
899 if (register_framebuffer(info) < 0) {
900 fb_dealloc_cmap(&info->cmap);
Dave Airlie785b93e2009-08-28 15:46:53 +1000901 return -EINVAL;
Clemens Ladisch3bea21b2009-11-04 09:43:00 +0100902 }
Dave Airlie785b93e2009-08-28 15:46:53 +1000903 } else {
904 drm_fb_helper_set_par(info);
905 }
906 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
907 info->fix.id);
908
909 /* Switch back to kernel console on panic */
910 /* multi card linked list maybe */
911 if (list_empty(&kernel_fb_helper_list)) {
912 printk(KERN_INFO "registered panic notifier\n");
913 atomic_notifier_chain_register(&panic_notifier_list,
914 &paniced);
915 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
916 }
917 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
918 return 0;
919}
920EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
921
922void drm_fb_helper_free(struct drm_fb_helper *helper)
923{
924 list_del(&helper->kernel_fb_list);
925 if (list_empty(&kernel_fb_helper_list)) {
926 printk(KERN_INFO "unregistered panic notifier\n");
927 atomic_notifier_chain_unregister(&panic_notifier_list,
928 &paniced);
929 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
930 }
931 drm_fb_helper_crtc_free(helper);
Clemens Ladisch3bea21b2009-11-04 09:43:00 +0100932 fb_dealloc_cmap(&helper->fb->fbdev->cmap);
Dave Airlie785b93e2009-08-28 15:46:53 +1000933}
934EXPORT_SYMBOL(drm_fb_helper_free);
935
Dave Airlie068143d2009-10-05 09:58:02 +1000936void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
937 uint32_t depth)
Dave Airlie785b93e2009-08-28 15:46:53 +1000938{
939 info->fix.type = FB_TYPE_PACKED_PIXELS;
Dave Airlie068143d2009-10-05 09:58:02 +1000940 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
Dave Airliec850cb72009-10-23 18:49:03 +1000941 FB_VISUAL_TRUECOLOR;
Dave Airlie785b93e2009-08-28 15:46:53 +1000942 info->fix.type_aux = 0;
943 info->fix.xpanstep = 1; /* doing it in hw */
944 info->fix.ypanstep = 1; /* doing it in hw */
945 info->fix.ywrapstep = 0;
Dave Airlie3420e742009-08-31 10:33:29 +1000946 info->fix.accel = FB_ACCEL_NONE;
Dave Airlie785b93e2009-08-28 15:46:53 +1000947 info->fix.type_aux = 0;
948
949 info->fix.line_length = pitch;
950 return;
951}
952EXPORT_SYMBOL(drm_fb_helper_fill_fix);
953
954void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
955 uint32_t fb_width, uint32_t fb_height)
956{
957 info->pseudo_palette = fb->pseudo_palette;
958 info->var.xres_virtual = fb->width;
959 info->var.yres_virtual = fb->height;
960 info->var.bits_per_pixel = fb->bits_per_pixel;
961 info->var.xoffset = 0;
962 info->var.yoffset = 0;
963 info->var.activate = FB_ACTIVATE_NOW;
964 info->var.height = -1;
965 info->var.width = -1;
966
967 switch (fb->depth) {
968 case 8:
969 info->var.red.offset = 0;
970 info->var.green.offset = 0;
971 info->var.blue.offset = 0;
972 info->var.red.length = 8; /* 8bit DAC */
973 info->var.green.length = 8;
974 info->var.blue.length = 8;
975 info->var.transp.offset = 0;
976 info->var.transp.length = 0;
977 break;
978 case 15:
979 info->var.red.offset = 10;
980 info->var.green.offset = 5;
981 info->var.blue.offset = 0;
982 info->var.red.length = 5;
983 info->var.green.length = 5;
984 info->var.blue.length = 5;
985 info->var.transp.offset = 15;
986 info->var.transp.length = 1;
987 break;
988 case 16:
989 info->var.red.offset = 11;
990 info->var.green.offset = 5;
991 info->var.blue.offset = 0;
992 info->var.red.length = 5;
993 info->var.green.length = 6;
994 info->var.blue.length = 5;
995 info->var.transp.offset = 0;
996 break;
997 case 24:
998 info->var.red.offset = 16;
999 info->var.green.offset = 8;
1000 info->var.blue.offset = 0;
1001 info->var.red.length = 8;
1002 info->var.green.length = 8;
1003 info->var.blue.length = 8;
1004 info->var.transp.offset = 0;
1005 info->var.transp.length = 0;
1006 break;
1007 case 32:
1008 info->var.red.offset = 16;
1009 info->var.green.offset = 8;
1010 info->var.blue.offset = 0;
1011 info->var.red.length = 8;
1012 info->var.green.length = 8;
1013 info->var.blue.length = 8;
1014 info->var.transp.offset = 24;
1015 info->var.transp.length = 8;
1016 break;
1017 default:
1018 break;
1019 }
1020
1021 info->var.xres = fb_width;
1022 info->var.yres = fb_height;
1023}
1024EXPORT_SYMBOL(drm_fb_helper_fill_var);