blob: 288ea2f327720b17992d7332f333f717f34f1adc [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090032#include <linux/slab.h>
Dave Airlie785b93e2009-08-28 15:46:53 +100033#include <linux/fb.h>
34#include "drmP.h"
35#include "drm_crtc.h"
36#include "drm_fb_helper.h"
37#include "drm_crtc_helper.h"
38
Dave Airlie6fcefd52009-09-08 11:08:32 +100039MODULE_AUTHOR("David Airlie, Jesse Barnes");
40MODULE_DESCRIPTION("DRM KMS helper");
41MODULE_LICENSE("GPL and additional rights");
42
Dave Airlie785b93e2009-08-28 15:46:53 +100043static LIST_HEAD(kernel_fb_helper_list);
44
Dave Airlied50ba252009-09-23 14:44:08 +100045int drm_fb_helper_add_connector(struct drm_connector *connector)
46{
47 connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
48 if (!connector->fb_helper_private)
49 return -ENOMEM;
50
51 return 0;
Dave Airlied50ba252009-09-23 14:44:08 +100052}
53EXPORT_SYMBOL(drm_fb_helper_add_connector);
54
Dave Airlied50ba252009-09-23 14:44:08 +100055/**
56 * drm_fb_helper_connector_parse_command_line - parse command line for connector
57 * @connector - connector to parse line for
58 * @mode_option - per connector mode option
59 *
60 * This parses the connector specific then generic command lines for
61 * modes and options to configure the connector.
62 *
63 * This uses the same parameters as the fb modedb.c, except for extra
64 * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
65 *
66 * enable/enable Digital/disable bit at the end
67 */
68static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *connector,
69 const char *mode_option)
70{
71 const char *name;
72 unsigned int namelen;
73 int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
74 unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
75 int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
76 int i;
77 enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
78 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
Dave Airlie8ef86782009-09-26 06:39:00 +100079 struct drm_fb_helper_cmdline_mode *cmdline_mode;
Dave Airlied50ba252009-09-23 14:44:08 +100080
Dave Airlie8ef86782009-09-26 06:39:00 +100081 if (!fb_help_conn)
82 return false;
83
84 cmdline_mode = &fb_help_conn->cmdline_mode;
Dave Airlied50ba252009-09-23 14:44:08 +100085 if (!mode_option)
86 mode_option = fb_mode_option;
87
88 if (!mode_option) {
89 cmdline_mode->specified = false;
90 return false;
91 }
92
93 name = mode_option;
94 namelen = strlen(name);
95 for (i = namelen-1; i >= 0; i--) {
96 switch (name[i]) {
97 case '@':
98 namelen = i;
99 if (!refresh_specified && !bpp_specified &&
100 !yres_specified) {
Andy Shevchenko3b40a442010-02-02 14:40:32 -0800101 refresh = simple_strtol(&name[i+1], NULL, 10);
Dave Airlied50ba252009-09-23 14:44:08 +1000102 refresh_specified = 1;
103 if (cvt || rb)
104 cvt = 0;
105 } else
106 goto done;
107 break;
108 case '-':
109 namelen = i;
110 if (!bpp_specified && !yres_specified) {
Andy Shevchenko3b40a442010-02-02 14:40:32 -0800111 bpp = simple_strtol(&name[i+1], NULL, 10);
Dave Airlied50ba252009-09-23 14:44:08 +1000112 bpp_specified = 1;
113 if (cvt || rb)
114 cvt = 0;
115 } else
116 goto done;
117 break;
118 case 'x':
119 if (!yres_specified) {
Andy Shevchenko3b40a442010-02-02 14:40:32 -0800120 yres = simple_strtol(&name[i+1], NULL, 10);
Dave Airlied50ba252009-09-23 14:44:08 +1000121 yres_specified = 1;
122 } else
123 goto done;
124 case '0' ... '9':
125 break;
126 case 'M':
127 if (!yres_specified)
128 cvt = 1;
129 break;
130 case 'R':
131 if (!cvt)
132 rb = 1;
133 break;
134 case 'm':
135 if (!cvt)
136 margins = 1;
137 break;
138 case 'i':
139 if (!cvt)
140 interlace = 1;
141 break;
142 case 'e':
143 force = DRM_FORCE_ON;
144 break;
145 case 'D':
Roel Kluine89a8c92009-12-31 13:06:29 +0100146 if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
Dave Airlied50ba252009-09-23 14:44:08 +1000147 (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
148 force = DRM_FORCE_ON;
149 else
150 force = DRM_FORCE_ON_DIGITAL;
151 break;
152 case 'd':
153 force = DRM_FORCE_OFF;
154 break;
155 default:
156 goto done;
157 }
158 }
159 if (i < 0 && yres_specified) {
Andy Shevchenko3b40a442010-02-02 14:40:32 -0800160 xres = simple_strtol(name, NULL, 10);
Dave Airlied50ba252009-09-23 14:44:08 +1000161 res_specified = 1;
162 }
163done:
164
165 DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
166 drm_get_connector_name(connector), xres, yres,
167 (refresh) ? refresh : 60, (rb) ? " reduced blanking" :
168 "", (margins) ? " with margins" : "", (interlace) ?
169 " interlaced" : "");
170
171 if (force) {
172 const char *s;
173 switch (force) {
174 case DRM_FORCE_OFF: s = "OFF"; break;
175 case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
176 default:
177 case DRM_FORCE_ON: s = "ON"; break;
178 }
179
180 DRM_INFO("forcing %s connector %s\n",
181 drm_get_connector_name(connector), s);
182 connector->force = force;
183 }
184
185 if (res_specified) {
186 cmdline_mode->specified = true;
187 cmdline_mode->xres = xres;
188 cmdline_mode->yres = yres;
189 }
190
191 if (refresh_specified) {
192 cmdline_mode->refresh_specified = true;
193 cmdline_mode->refresh = refresh;
194 }
195
196 if (bpp_specified) {
197 cmdline_mode->bpp_specified = true;
198 cmdline_mode->bpp = bpp;
199 }
200 cmdline_mode->rb = rb ? true : false;
201 cmdline_mode->cvt = cvt ? true : false;
202 cmdline_mode->interlace = interlace ? true : false;
203
204 return true;
205}
206
207int drm_fb_helper_parse_command_line(struct drm_device *dev)
208{
209 struct drm_connector *connector;
210
211 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
212 char *option = NULL;
213
214 /* do something on return - turn off connector maybe */
215 if (fb_get_options(drm_get_connector_name(connector), &option))
216 continue;
217
218 drm_fb_helper_connector_parse_command_line(connector, option);
219 }
220 return 0;
221}
222
Dave Airlie785b93e2009-08-28 15:46:53 +1000223bool drm_fb_helper_force_kernel_mode(void)
224{
225 int i = 0;
226 bool ret, error = false;
227 struct drm_fb_helper *helper;
228
229 if (list_empty(&kernel_fb_helper_list))
230 return false;
231
232 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
233 for (i = 0; i < helper->crtc_count; i++) {
234 struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
235 ret = drm_crtc_helper_set_config(mode_set);
236 if (ret)
237 error = true;
238 }
239 }
240 return error;
241}
242
243int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
244 void *panic_str)
245{
246 DRM_ERROR("panic occurred, switching back to text console\n");
247 return drm_fb_helper_force_kernel_mode();
248 return 0;
249}
250EXPORT_SYMBOL(drm_fb_helper_panic);
251
252static struct notifier_block paniced = {
253 .notifier_call = drm_fb_helper_panic,
254};
255
256/**
257 * drm_fb_helper_restore - restore the framebuffer console (kernel) config
258 *
259 * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
260 */
261void drm_fb_helper_restore(void)
262{
263 bool ret;
264 ret = drm_fb_helper_force_kernel_mode();
265 if (ret == true)
266 DRM_ERROR("Failed to restore crtc configuration\n");
267}
268EXPORT_SYMBOL(drm_fb_helper_restore);
269
Mikael Petterssonbea1d352009-09-28 18:26:25 +0200270#ifdef CONFIG_MAGIC_SYSRQ
Dave Airlie785b93e2009-08-28 15:46:53 +1000271static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
272{
273 drm_fb_helper_restore();
274}
275static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
276
277static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3)
278{
279 schedule_work(&drm_fb_helper_restore_work);
280}
281
282static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
283 .handler = drm_fb_helper_sysrq,
284 .help_msg = "force-fb(V)",
285 .action_msg = "Restore framebuffer console",
286};
Randy Dunlapb8c40d62010-03-25 18:29:05 +0000287#else
288static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
Mikael Petterssonbea1d352009-09-28 18:26:25 +0200289#endif
Dave Airlie785b93e2009-08-28 15:46:53 +1000290
291static void drm_fb_helper_on(struct fb_info *info)
292{
293 struct drm_fb_helper *fb_helper = info->par;
294 struct drm_device *dev = fb_helper->dev;
295 struct drm_crtc *crtc;
296 struct drm_encoder *encoder;
297 int i;
298
299 /*
300 * For each CRTC in this fb, turn the crtc on then,
301 * find all associated encoders and turn them on.
302 */
Jesse Barnese87b2c42009-09-17 18:14:41 -0700303 for (i = 0; i < fb_helper->crtc_count; i++) {
304 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
305 struct drm_crtc_helper_funcs *crtc_funcs =
306 crtc->helper_private;
Dave Airlie785b93e2009-08-28 15:46:53 +1000307
Jesse Barnese87b2c42009-09-17 18:14:41 -0700308 /* Only mess with CRTCs in this fb */
309 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
310 !crtc->enabled)
311 continue;
Dave Airlie785b93e2009-08-28 15:46:53 +1000312
Jesse Barnese87b2c42009-09-17 18:14:41 -0700313 mutex_lock(&dev->mode_config.mutex);
314 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
315 mutex_unlock(&dev->mode_config.mutex);
Dave Airlie785b93e2009-08-28 15:46:53 +1000316
Jesse Barnese87b2c42009-09-17 18:14:41 -0700317 /* Found a CRTC on this fb, now find encoders */
318 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
319 if (encoder->crtc == crtc) {
320 struct drm_encoder_helper_funcs *encoder_funcs;
Dave Airlie785b93e2009-08-28 15:46:53 +1000321
Jesse Barnese87b2c42009-09-17 18:14:41 -0700322 encoder_funcs = encoder->helper_private;
323 mutex_lock(&dev->mode_config.mutex);
324 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
325 mutex_unlock(&dev->mode_config.mutex);
326 }
Dave Airlie785b93e2009-08-28 15:46:53 +1000327 }
328 }
329 }
330}
331
332static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
333{
334 struct drm_fb_helper *fb_helper = info->par;
335 struct drm_device *dev = fb_helper->dev;
336 struct drm_crtc *crtc;
337 struct drm_encoder *encoder;
338 int i;
339
340 /*
341 * For each CRTC in this fb, find all associated encoders
342 * and turn them off, then turn off the CRTC.
343 */
Jesse Barnese87b2c42009-09-17 18:14:41 -0700344 for (i = 0; i < fb_helper->crtc_count; i++) {
345 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
346 struct drm_crtc_helper_funcs *crtc_funcs =
347 crtc->helper_private;
Dave Airlie785b93e2009-08-28 15:46:53 +1000348
Jesse Barnese87b2c42009-09-17 18:14:41 -0700349 /* Only mess with CRTCs in this fb */
350 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
351 !crtc->enabled)
352 continue;
Dave Airlie785b93e2009-08-28 15:46:53 +1000353
Jesse Barnese87b2c42009-09-17 18:14:41 -0700354 /* Found a CRTC on this fb, now find encoders */
355 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
356 if (encoder->crtc == crtc) {
357 struct drm_encoder_helper_funcs *encoder_funcs;
Dave Airlie785b93e2009-08-28 15:46:53 +1000358
Jesse Barnese87b2c42009-09-17 18:14:41 -0700359 encoder_funcs = encoder->helper_private;
360 mutex_lock(&dev->mode_config.mutex);
361 encoder_funcs->dpms(encoder, dpms_mode);
362 mutex_unlock(&dev->mode_config.mutex);
363 }
364 }
James Simmons731b5a12009-10-29 20:39:07 +0000365 mutex_lock(&dev->mode_config.mutex);
366 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
367 mutex_unlock(&dev->mode_config.mutex);
Dave Airlie785b93e2009-08-28 15:46:53 +1000368 }
Dave Airlie785b93e2009-08-28 15:46:53 +1000369 }
370}
371
372int drm_fb_helper_blank(int blank, struct fb_info *info)
373{
374 switch (blank) {
James Simmons731b5a12009-10-29 20:39:07 +0000375 /* Display: On; HSync: On, VSync: On */
Dave Airlie785b93e2009-08-28 15:46:53 +1000376 case FB_BLANK_UNBLANK:
377 drm_fb_helper_on(info);
378 break;
James Simmons731b5a12009-10-29 20:39:07 +0000379 /* Display: Off; HSync: On, VSync: On */
Dave Airlie785b93e2009-08-28 15:46:53 +1000380 case FB_BLANK_NORMAL:
Zhenyu Wang5fd4df42010-01-18 16:47:04 +0800381 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
Dave Airlie785b93e2009-08-28 15:46:53 +1000382 break;
James Simmons731b5a12009-10-29 20:39:07 +0000383 /* Display: Off; HSync: Off, VSync: On */
Dave Airlie785b93e2009-08-28 15:46:53 +1000384 case FB_BLANK_HSYNC_SUSPEND:
385 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
386 break;
James Simmons731b5a12009-10-29 20:39:07 +0000387 /* Display: Off; HSync: On, VSync: Off */
Dave Airlie785b93e2009-08-28 15:46:53 +1000388 case FB_BLANK_VSYNC_SUSPEND:
389 drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
390 break;
James Simmons731b5a12009-10-29 20:39:07 +0000391 /* Display: Off; HSync: Off, VSync: Off */
Dave Airlie785b93e2009-08-28 15:46:53 +1000392 case FB_BLANK_POWERDOWN:
393 drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
394 break;
395 }
396 return 0;
397}
398EXPORT_SYMBOL(drm_fb_helper_blank);
399
400static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
401{
402 int i;
403
404 for (i = 0; i < helper->crtc_count; i++)
405 kfree(helper->crtc_info[i].mode_set.connectors);
406 kfree(helper->crtc_info);
407}
408
409int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
410{
411 struct drm_device *dev = helper->dev;
412 struct drm_crtc *crtc;
413 int ret = 0;
414 int i;
415
416 helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
417 if (!helper->crtc_info)
418 return -ENOMEM;
419
420 helper->crtc_count = crtc_count;
421
422 for (i = 0; i < crtc_count; i++) {
423 helper->crtc_info[i].mode_set.connectors =
424 kcalloc(max_conn_count,
425 sizeof(struct drm_connector *),
426 GFP_KERNEL);
427
428 if (!helper->crtc_info[i].mode_set.connectors) {
429 ret = -ENOMEM;
430 goto out_free;
431 }
432 helper->crtc_info[i].mode_set.num_connectors = 0;
433 }
434
435 i = 0;
436 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
437 helper->crtc_info[i].crtc_id = crtc->base.id;
438 helper->crtc_info[i].mode_set.crtc = crtc;
439 i++;
440 }
441 helper->conn_limit = max_conn_count;
442 return 0;
443out_free:
444 drm_fb_helper_crtc_free(helper);
445 return -ENOMEM;
446}
447EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
448
Dave Airliec850cb72009-10-23 18:49:03 +1000449static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000450 u16 blue, u16 regno, struct fb_info *info)
451{
452 struct drm_fb_helper *fb_helper = info->par;
453 struct drm_framebuffer *fb = fb_helper->fb;
454 int pindex;
455
Dave Airliec850cb72009-10-23 18:49:03 +1000456 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
457 u32 *palette;
458 u32 value;
459 /* place color in psuedopalette */
460 if (regno > 16)
461 return -EINVAL;
462 palette = (u32 *)info->pseudo_palette;
463 red >>= (16 - info->var.red.length);
464 green >>= (16 - info->var.green.length);
465 blue >>= (16 - info->var.blue.length);
466 value = (red << info->var.red.offset) |
467 (green << info->var.green.offset) |
468 (blue << info->var.blue.offset);
469 palette[regno] = value;
470 return 0;
471 }
472
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000473 pindex = regno;
474
475 if (fb->bits_per_pixel == 16) {
476 pindex = regno << 3;
477
478 if (fb->depth == 16 && regno > 63)
Dave Airliec850cb72009-10-23 18:49:03 +1000479 return -EINVAL;
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000480 if (fb->depth == 15 && regno > 31)
Dave Airliec850cb72009-10-23 18:49:03 +1000481 return -EINVAL;
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000482
483 if (fb->depth == 16) {
484 u16 r, g, b;
485 int i;
486 if (regno < 32) {
487 for (i = 0; i < 8; i++)
488 fb_helper->funcs->gamma_set(crtc, red,
489 green, blue, pindex + i);
490 }
491
492 fb_helper->funcs->gamma_get(crtc, &r,
493 &g, &b,
494 pindex >> 1);
495
496 for (i = 0; i < 4; i++)
497 fb_helper->funcs->gamma_set(crtc, r,
498 green, b,
499 (pindex >> 1) + i);
500 }
501 }
502
503 if (fb->depth != 16)
504 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
Dave Airliec850cb72009-10-23 18:49:03 +1000505 return 0;
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000506}
507
Dave Airlie068143d2009-10-05 09:58:02 +1000508int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
509{
510 struct drm_fb_helper *fb_helper = info->par;
511 struct drm_device *dev = fb_helper->dev;
512 u16 *red, *green, *blue, *transp;
513 struct drm_crtc *crtc;
514 int i, rc = 0;
515 int start;
516
517 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
518 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
519 for (i = 0; i < fb_helper->crtc_count; i++) {
520 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
521 break;
522 }
523 if (i == fb_helper->crtc_count)
524 continue;
525
526 red = cmap->red;
527 green = cmap->green;
528 blue = cmap->blue;
529 transp = cmap->transp;
530 start = cmap->start;
531
532 for (i = 0; i < cmap->len; i++) {
533 u16 hred, hgreen, hblue, htransp = 0xffff;
534
535 hred = *red++;
536 hgreen = *green++;
537 hblue = *blue++;
538
539 if (transp)
540 htransp = *transp++;
541
Dave Airliec850cb72009-10-23 18:49:03 +1000542 rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
543 if (rc)
544 return rc;
Dave Airlie068143d2009-10-05 09:58:02 +1000545 }
546 crtc_funcs->load_lut(crtc);
547 }
548 return rc;
549}
550EXPORT_SYMBOL(drm_fb_helper_setcmap);
551
Dave Airlie785b93e2009-08-28 15:46:53 +1000552int drm_fb_helper_setcolreg(unsigned regno,
553 unsigned red,
554 unsigned green,
555 unsigned blue,
556 unsigned transp,
557 struct fb_info *info)
558{
559 struct drm_fb_helper *fb_helper = info->par;
560 struct drm_device *dev = fb_helper->dev;
561 struct drm_crtc *crtc;
562 int i;
Dave Airliec850cb72009-10-23 18:49:03 +1000563 int ret;
Dave Airlie785b93e2009-08-28 15:46:53 +1000564
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000565 if (regno > 255)
566 return 1;
Dave Airlie785b93e2009-08-28 15:46:53 +1000567
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000568 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
569 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
Dave Airlie785b93e2009-08-28 15:46:53 +1000570 for (i = 0; i < fb_helper->crtc_count; i++) {
571 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
572 break;
573 }
574 if (i == fb_helper->crtc_count)
575 continue;
576
Dave Airliec850cb72009-10-23 18:49:03 +1000577 ret = setcolreg(crtc, red, green, blue, regno, info);
578 if (ret)
579 return ret;
Dave Airlie785b93e2009-08-28 15:46:53 +1000580
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000581 crtc_funcs->load_lut(crtc);
Dave Airlie785b93e2009-08-28 15:46:53 +1000582 }
583 return 0;
584}
585EXPORT_SYMBOL(drm_fb_helper_setcolreg);
586
587int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
588 struct fb_info *info)
589{
590 struct drm_fb_helper *fb_helper = info->par;
591 struct drm_framebuffer *fb = fb_helper->fb;
592 int depth;
593
Clemens Ladisch5349ef32009-11-04 09:42:52 +0100594 if (var->pixclock != 0)
Dave Airlie785b93e2009-08-28 15:46:53 +1000595 return -EINVAL;
596
597 /* Need to resize the fb object !!! */
Dave Airlie509c7d82010-01-08 09:27:08 +1000598 if (var->bits_per_pixel > fb->bits_per_pixel || var->xres > fb->width || var->yres > fb->height) {
599 DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
600 "object %dx%d-%d > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel,
601 fb->width, fb->height, fb->bits_per_pixel);
Dave Airlie785b93e2009-08-28 15:46:53 +1000602 return -EINVAL;
603 }
604
605 switch (var->bits_per_pixel) {
606 case 16:
607 depth = (var->green.length == 6) ? 16 : 15;
608 break;
609 case 32:
610 depth = (var->transp.length > 0) ? 32 : 24;
611 break;
612 default:
613 depth = var->bits_per_pixel;
614 break;
615 }
616
617 switch (depth) {
618 case 8:
619 var->red.offset = 0;
620 var->green.offset = 0;
621 var->blue.offset = 0;
622 var->red.length = 8;
623 var->green.length = 8;
624 var->blue.length = 8;
625 var->transp.length = 0;
626 var->transp.offset = 0;
627 break;
628 case 15:
629 var->red.offset = 10;
630 var->green.offset = 5;
631 var->blue.offset = 0;
632 var->red.length = 5;
633 var->green.length = 5;
634 var->blue.length = 5;
635 var->transp.length = 1;
636 var->transp.offset = 15;
637 break;
638 case 16:
639 var->red.offset = 11;
640 var->green.offset = 5;
641 var->blue.offset = 0;
642 var->red.length = 5;
643 var->green.length = 6;
644 var->blue.length = 5;
645 var->transp.length = 0;
646 var->transp.offset = 0;
647 break;
648 case 24:
649 var->red.offset = 16;
650 var->green.offset = 8;
651 var->blue.offset = 0;
652 var->red.length = 8;
653 var->green.length = 8;
654 var->blue.length = 8;
655 var->transp.length = 0;
656 var->transp.offset = 0;
657 break;
658 case 32:
659 var->red.offset = 16;
660 var->green.offset = 8;
661 var->blue.offset = 0;
662 var->red.length = 8;
663 var->green.length = 8;
664 var->blue.length = 8;
665 var->transp.length = 8;
666 var->transp.offset = 24;
667 break;
668 default:
669 return -EINVAL;
670 }
671 return 0;
672}
673EXPORT_SYMBOL(drm_fb_helper_check_var);
674
675/* this will let fbcon do the mode init */
676int drm_fb_helper_set_par(struct fb_info *info)
677{
678 struct drm_fb_helper *fb_helper = info->par;
679 struct drm_device *dev = fb_helper->dev;
680 struct fb_var_screeninfo *var = &info->var;
681 struct drm_crtc *crtc;
682 int ret;
683 int i;
684
Clemens Ladisch5349ef32009-11-04 09:42:52 +0100685 if (var->pixclock != 0) {
Pavel Roskin172e91f2010-02-11 14:31:32 +1000686 DRM_ERROR("PIXEL CLOCK SET\n");
Dave Airlie785b93e2009-08-28 15:46:53 +1000687 return -EINVAL;
688 }
689
690 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
691
692 for (i = 0; i < fb_helper->crtc_count; i++) {
693 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
694 break;
695 }
696 if (i == fb_helper->crtc_count)
697 continue;
698
699 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
700 mutex_lock(&dev->mode_config.mutex);
James Simmonsa2d49ae2009-10-27 21:09:24 +0000701 ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
Dave Airlie785b93e2009-08-28 15:46:53 +1000702 mutex_unlock(&dev->mode_config.mutex);
703 if (ret)
704 return ret;
705 }
706 }
707 return 0;
708}
709EXPORT_SYMBOL(drm_fb_helper_set_par);
710
711int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
712 struct fb_info *info)
713{
714 struct drm_fb_helper *fb_helper = info->par;
715 struct drm_device *dev = fb_helper->dev;
716 struct drm_mode_set *modeset;
717 struct drm_crtc *crtc;
718 int ret = 0;
719 int i;
720
721 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
722 for (i = 0; i < fb_helper->crtc_count; i++) {
723 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
724 break;
725 }
726
727 if (i == fb_helper->crtc_count)
728 continue;
729
730 modeset = &fb_helper->crtc_info[i].mode_set;
731
732 modeset->x = var->xoffset;
733 modeset->y = var->yoffset;
734
735 if (modeset->num_connectors) {
736 mutex_lock(&dev->mode_config.mutex);
737 ret = crtc->funcs->set_config(modeset);
738 mutex_unlock(&dev->mode_config.mutex);
739 if (!ret) {
740 info->var.xoffset = var->xoffset;
741 info->var.yoffset = var->yoffset;
742 }
743 }
744 }
745 return ret;
746}
747EXPORT_SYMBOL(drm_fb_helper_pan_display);
748
749int drm_fb_helper_single_fb_probe(struct drm_device *dev,
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000750 int preferred_bpp,
Dave Airlie785b93e2009-08-28 15:46:53 +1000751 int (*fb_create)(struct drm_device *dev,
752 uint32_t fb_width,
753 uint32_t fb_height,
754 uint32_t surface_width,
755 uint32_t surface_height,
Dave Airlied50ba252009-09-23 14:44:08 +1000756 uint32_t surface_depth,
757 uint32_t surface_bpp,
Dave Airlie785b93e2009-08-28 15:46:53 +1000758 struct drm_framebuffer **fb_ptr))
759{
760 struct drm_crtc *crtc;
761 struct drm_connector *connector;
762 unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
763 unsigned int surface_width = 0, surface_height = 0;
764 int new_fb = 0;
765 int crtc_count = 0;
766 int ret, i, conn_count = 0;
767 struct fb_info *info;
768 struct drm_framebuffer *fb;
769 struct drm_mode_set *modeset = NULL;
770 struct drm_fb_helper *fb_helper;
Dave Airlied50ba252009-09-23 14:44:08 +1000771 uint32_t surface_depth = 24, surface_bpp = 32;
Dave Airlie785b93e2009-08-28 15:46:53 +1000772
Dave Airlieb8c00ac2009-10-06 13:54:01 +1000773 /* if driver picks 8 or 16 by default use that
774 for both depth/bpp */
775 if (preferred_bpp != surface_bpp) {
776 surface_depth = surface_bpp = preferred_bpp;
777 }
Dave Airlie785b93e2009-08-28 15:46:53 +1000778 /* first up get a count of crtcs now in use and new min/maxes width/heights */
Dave Airlied50ba252009-09-23 14:44:08 +1000779 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
780 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
Dave Airlie8ef86782009-09-26 06:39:00 +1000781
782 struct drm_fb_helper_cmdline_mode *cmdline_mode;
783
784 if (!fb_help_conn)
785 continue;
786
787 cmdline_mode = &fb_help_conn->cmdline_mode;
Dave Airlied50ba252009-09-23 14:44:08 +1000788
789 if (cmdline_mode->bpp_specified) {
790 switch (cmdline_mode->bpp) {
791 case 8:
792 surface_depth = surface_bpp = 8;
793 break;
794 case 15:
795 surface_depth = 15;
796 surface_bpp = 16;
797 break;
798 case 16:
799 surface_depth = surface_bpp = 16;
800 break;
801 case 24:
802 surface_depth = surface_bpp = 24;
803 break;
804 case 32:
805 surface_depth = 24;
806 surface_bpp = 32;
807 break;
808 }
809 break;
810 }
811 }
812
Dave Airlie785b93e2009-08-28 15:46:53 +1000813 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
814 if (drm_helper_crtc_in_use(crtc)) {
815 if (crtc->desired_mode) {
816 if (crtc->desired_mode->hdisplay < fb_width)
817 fb_width = crtc->desired_mode->hdisplay;
818
819 if (crtc->desired_mode->vdisplay < fb_height)
820 fb_height = crtc->desired_mode->vdisplay;
821
822 if (crtc->desired_mode->hdisplay > surface_width)
823 surface_width = crtc->desired_mode->hdisplay;
824
825 if (crtc->desired_mode->vdisplay > surface_height)
826 surface_height = crtc->desired_mode->vdisplay;
827 }
828 crtc_count++;
829 }
830 }
831
832 if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
833 /* hmm everyone went away - assume VGA cable just fell out
834 and will come back later. */
835 return 0;
836 }
837
838 /* do we have an fb already? */
839 if (list_empty(&dev->mode_config.fb_kernel_list)) {
840 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
Dave Airlied50ba252009-09-23 14:44:08 +1000841 surface_height, surface_depth, surface_bpp,
842 &fb);
Dave Airlie785b93e2009-08-28 15:46:53 +1000843 if (ret)
844 return -EINVAL;
845 new_fb = 1;
846 } else {
847 fb = list_first_entry(&dev->mode_config.fb_kernel_list,
848 struct drm_framebuffer, filp_head);
849
850 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
851 As really we can't resize an fbdev that is in the wild currently due to fbdev
852 not really being designed for the lower layers moving stuff around under it.
853 - so in the grand style of things - punt. */
854 if ((fb->width < surface_width) ||
855 (fb->height < surface_height)) {
856 DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
857 return -EINVAL;
858 }
859 }
860
861 info = fb->fbdev;
862 fb_helper = info->par;
863
864 crtc_count = 0;
865 /* okay we need to setup new connector sets in the crtcs */
866 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
867 modeset = &fb_helper->crtc_info[crtc_count].mode_set;
868 modeset->fb = fb;
869 conn_count = 0;
870 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
871 if (connector->encoder)
872 if (connector->encoder->crtc == modeset->crtc) {
873 modeset->connectors[conn_count] = connector;
874 conn_count++;
875 if (conn_count > fb_helper->conn_limit)
876 BUG();
877 }
878 }
879
880 for (i = conn_count; i < fb_helper->conn_limit; i++)
881 modeset->connectors[i] = NULL;
882
883 modeset->crtc = crtc;
884 crtc_count++;
885
886 modeset->num_connectors = conn_count;
887 if (modeset->crtc->desired_mode) {
888 if (modeset->mode)
889 drm_mode_destroy(dev, modeset->mode);
890 modeset->mode = drm_mode_duplicate(dev,
891 modeset->crtc->desired_mode);
892 }
893 }
894 fb_helper->crtc_count = crtc_count;
895 fb_helper->fb = fb;
896
897 if (new_fb) {
Clemens Ladisch5349ef32009-11-04 09:42:52 +0100898 info->var.pixclock = 0;
Ben Skeggsb0a007d2009-12-08 11:15:10 +1000899 ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0);
Clemens Ladisch3bea21b2009-11-04 09:43:00 +0100900 if (ret)
901 return ret;
902 if (register_framebuffer(info) < 0) {
903 fb_dealloc_cmap(&info->cmap);
Dave Airlie785b93e2009-08-28 15:46:53 +1000904 return -EINVAL;
Clemens Ladisch3bea21b2009-11-04 09:43:00 +0100905 }
Dave Airlie785b93e2009-08-28 15:46:53 +1000906 } else {
907 drm_fb_helper_set_par(info);
908 }
909 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
910 info->fix.id);
911
912 /* Switch back to kernel console on panic */
913 /* multi card linked list maybe */
914 if (list_empty(&kernel_fb_helper_list)) {
915 printk(KERN_INFO "registered panic notifier\n");
916 atomic_notifier_chain_register(&panic_notifier_list,
917 &paniced);
918 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
919 }
920 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
921 return 0;
922}
923EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
924
925void drm_fb_helper_free(struct drm_fb_helper *helper)
926{
927 list_del(&helper->kernel_fb_list);
928 if (list_empty(&kernel_fb_helper_list)) {
929 printk(KERN_INFO "unregistered panic notifier\n");
930 atomic_notifier_chain_unregister(&panic_notifier_list,
931 &paniced);
932 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
933 }
934 drm_fb_helper_crtc_free(helper);
Clemens Ladisch3bea21b2009-11-04 09:43:00 +0100935 fb_dealloc_cmap(&helper->fb->fbdev->cmap);
Dave Airlie785b93e2009-08-28 15:46:53 +1000936}
937EXPORT_SYMBOL(drm_fb_helper_free);
938
Dave Airlie068143d2009-10-05 09:58:02 +1000939void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
940 uint32_t depth)
Dave Airlie785b93e2009-08-28 15:46:53 +1000941{
942 info->fix.type = FB_TYPE_PACKED_PIXELS;
Dave Airlie068143d2009-10-05 09:58:02 +1000943 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
Dave Airliec850cb72009-10-23 18:49:03 +1000944 FB_VISUAL_TRUECOLOR;
Dave Airlie785b93e2009-08-28 15:46:53 +1000945 info->fix.type_aux = 0;
946 info->fix.xpanstep = 1; /* doing it in hw */
947 info->fix.ypanstep = 1; /* doing it in hw */
948 info->fix.ywrapstep = 0;
Dave Airlie3420e742009-08-31 10:33:29 +1000949 info->fix.accel = FB_ACCEL_NONE;
Dave Airlie785b93e2009-08-28 15:46:53 +1000950 info->fix.type_aux = 0;
951
952 info->fix.line_length = pitch;
953 return;
954}
955EXPORT_SYMBOL(drm_fb_helper_fill_fix);
956
957void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
958 uint32_t fb_width, uint32_t fb_height)
959{
960 info->pseudo_palette = fb->pseudo_palette;
961 info->var.xres_virtual = fb->width;
962 info->var.yres_virtual = fb->height;
963 info->var.bits_per_pixel = fb->bits_per_pixel;
964 info->var.xoffset = 0;
965 info->var.yoffset = 0;
966 info->var.activate = FB_ACTIVATE_NOW;
967 info->var.height = -1;
968 info->var.width = -1;
969
970 switch (fb->depth) {
971 case 8:
972 info->var.red.offset = 0;
973 info->var.green.offset = 0;
974 info->var.blue.offset = 0;
975 info->var.red.length = 8; /* 8bit DAC */
976 info->var.green.length = 8;
977 info->var.blue.length = 8;
978 info->var.transp.offset = 0;
979 info->var.transp.length = 0;
980 break;
981 case 15:
982 info->var.red.offset = 10;
983 info->var.green.offset = 5;
984 info->var.blue.offset = 0;
985 info->var.red.length = 5;
986 info->var.green.length = 5;
987 info->var.blue.length = 5;
988 info->var.transp.offset = 15;
989 info->var.transp.length = 1;
990 break;
991 case 16:
992 info->var.red.offset = 11;
993 info->var.green.offset = 5;
994 info->var.blue.offset = 0;
995 info->var.red.length = 5;
996 info->var.green.length = 6;
997 info->var.blue.length = 5;
998 info->var.transp.offset = 0;
999 break;
1000 case 24:
1001 info->var.red.offset = 16;
1002 info->var.green.offset = 8;
1003 info->var.blue.offset = 0;
1004 info->var.red.length = 8;
1005 info->var.green.length = 8;
1006 info->var.blue.length = 8;
1007 info->var.transp.offset = 0;
1008 info->var.transp.length = 0;
1009 break;
1010 case 32:
1011 info->var.red.offset = 16;
1012 info->var.green.offset = 8;
1013 info->var.blue.offset = 0;
1014 info->var.red.length = 8;
1015 info->var.green.length = 8;
1016 info->var.blue.length = 8;
1017 info->var.transp.offset = 24;
1018 info->var.transp.length = 8;
1019 break;
1020 default:
1021 break;
1022 }
1023
1024 info->var.xres = fb_width;
1025 info->var.yres = fb_height;
1026}
1027EXPORT_SYMBOL(drm_fb_helper_fill_var);