9f6d4b5f3fe9e99939e4225c0135f5ae06df8a8e
[dragonfly.git] / sys / dev / drm / drm_fb_helper.c
1 /*
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  */
30
31 #include <linux/kernel.h>
32 #include <linux/fb.h>
33 #include <linux/module.h>
34 #include <drm/drmP.h>
35 #include <drm/drm_crtc.h>
36 #include <drm/drm_fb_helper.h>
37 #include <drm/drm_crtc_helper.h>
38
39 static LINUX_LIST_HEAD(kernel_fb_helper_list);
40
41 /**
42  * DOC: fbdev helpers
43  *
44  * The fb helper functions are useful to provide an fbdev on top of a drm kernel
45  * mode setting driver. They can be used mostly independently from the crtc
46  * helper functions used by many drivers to implement the kernel mode setting
47  * interfaces.
48  *
49  * Initialization is done as a three-step process with drm_fb_helper_init(),
50  * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
51  * Drivers with fancier requirements than the default behaviour can override the
52  * second step with their own code.  Teardown is done with drm_fb_helper_fini().
53  *
54  * At runtime drivers should restore the fbdev console by calling
55  * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
56  * should also notify the fb helper code from updates to the output
57  * configuration by calling drm_fb_helper_hotplug_event(). For easier
58  * integration with the output polling code in drm_crtc_helper.c the modeset
59  * code provides a ->output_poll_changed callback.
60  *
61  * All other functions exported by the fb helper library can be used to
62  * implement the fbdev driver interface by the driver.
63  */
64
65 /**
66  * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
67  *                                             emulation helper
68  * @fb_helper: fbdev initialized with drm_fb_helper_init
69  *
70  * This functions adds all the available connectors for use with the given
71  * fb_helper. This is a separate step to allow drivers to freely assign
72  * connectors to the fbdev, e.g. if some are reserved for special purposes or
73  * not adequate to be used for the fbcon.
74  *
75  * Since this is part of the initial setup before the fbdev is published, no
76  * locking is required.
77  */
78 int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
79 {
80         struct drm_device *dev = fb_helper->dev;
81         struct drm_connector *connector;
82         int i;
83
84         list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
85                 struct drm_fb_helper_connector *fb_helper_connector;
86
87                 fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
88                 if (!fb_helper_connector)
89                         goto fail;
90
91                 fb_helper_connector->connector = connector;
92                 fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
93         }
94         return 0;
95 fail:
96         for (i = 0; i < fb_helper->connector_count; i++) {
97                 kfree(fb_helper->connector_info[i]);
98                 fb_helper->connector_info[i] = NULL;
99         }
100         fb_helper->connector_count = 0;
101         return -ENOMEM;
102 }
103 EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
104
105 static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
106 {
107         struct drm_fb_helper_connector *fb_helper_conn;
108         int i;
109
110         for (i = 0; i < fb_helper->connector_count; i++) {
111                 struct drm_cmdline_mode *mode;
112                 struct drm_connector *connector;
113                 char *option = NULL;
114
115                 fb_helper_conn = fb_helper->connector_info[i];
116                 connector = fb_helper_conn->connector;
117                 mode = &fb_helper_conn->cmdline_mode;
118
119                 /* do something on return - turn off connector maybe */
120                 if (fb_get_options(connector->name, &option))
121                         continue;
122
123                 if (drm_mode_parse_command_line_for_connector(option,
124                                                               connector,
125                                                               mode)) {
126                         if (mode->force) {
127                                 const char *s;
128                                 switch (mode->force) {
129                                 case DRM_FORCE_OFF:
130                                         s = "OFF";
131                                         break;
132                                 case DRM_FORCE_ON_DIGITAL:
133                                         s = "ON - dig";
134                                         break;
135                                 default:
136                                 case DRM_FORCE_ON:
137                                         s = "ON";
138                                         break;
139                                 }
140
141                                 DRM_INFO("forcing %s connector %s\n",
142                                          connector->name, s);
143                                 connector->force = mode->force;
144                         }
145
146                         DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
147                                       connector->name,
148                                       mode->xres, mode->yres,
149                                       mode->refresh_specified ? mode->refresh : 60,
150                                       mode->rb ? " reduced blanking" : "",
151                                       mode->margins ? " with margins" : "",
152                                       mode->interlace ?  " interlaced" : "");
153                 }
154
155         }
156         return 0;
157 }
158
159 #if 0
160 static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper)
161 {
162         uint16_t *r_base, *g_base, *b_base;
163         int i;
164
165         if (helper->funcs->gamma_get == NULL)
166                 return;
167
168         r_base = crtc->gamma_store;
169         g_base = r_base + crtc->gamma_size;
170         b_base = g_base + crtc->gamma_size;
171
172         for (i = 0; i < crtc->gamma_size; i++)
173                 helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i);
174 }
175
176 static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
177 {
178         uint16_t *r_base, *g_base, *b_base;
179
180         if (crtc->funcs->gamma_set == NULL)
181                 return;
182
183         r_base = crtc->gamma_store;
184         g_base = r_base + crtc->gamma_size;
185         b_base = g_base + crtc->gamma_size;
186
187         crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
188 }
189
190 /**
191  * drm_fb_helper_debug_enter - implementation for ->fb_debug_enter
192  * @info: fbdev registered by the helper
193  */
194 int drm_fb_helper_debug_enter(struct fb_info *info)
195 {
196         struct drm_fb_helper *helper = info->par;
197         struct drm_crtc_helper_funcs *funcs;
198         int i;
199
200         if (list_empty(&kernel_fb_helper_list))
201                 return false;
202
203         list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
204                 for (i = 0; i < helper->crtc_count; i++) {
205                         struct drm_mode_set *mode_set =
206                                 &helper->crtc_info[i].mode_set;
207
208                         if (!mode_set->crtc->enabled)
209                                 continue;
210
211                         funcs = mode_set->crtc->helper_private;
212                         drm_fb_helper_save_lut_atomic(mode_set->crtc, helper);
213                         funcs->mode_set_base_atomic(mode_set->crtc,
214                                                     mode_set->fb,
215                                                     mode_set->x,
216                                                     mode_set->y,
217                                                     ENTER_ATOMIC_MODE_SET);
218                 }
219         }
220
221         return 0;
222 }
223 EXPORT_SYMBOL(drm_fb_helper_debug_enter);
224
225 /* Find the real fb for a given fb helper CRTC */
226 static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
227 {
228         struct drm_device *dev = crtc->dev;
229         struct drm_crtc *c;
230
231         list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
232                 if (crtc->base.id == c->base.id)
233                         return c->primary->fb;
234         }
235
236         return NULL;
237 }
238
239 /**
240  * drm_fb_helper_debug_leave - implementation for ->fb_debug_leave
241  * @info: fbdev registered by the helper
242  */
243 int drm_fb_helper_debug_leave(struct fb_info *info)
244 {
245         struct drm_fb_helper *helper = info->par;
246         struct drm_crtc *crtc;
247         struct drm_crtc_helper_funcs *funcs;
248         struct drm_framebuffer *fb;
249         int i;
250
251         for (i = 0; i < helper->crtc_count; i++) {
252                 struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
253                 crtc = mode_set->crtc;
254                 funcs = crtc->helper_private;
255                 fb = drm_mode_config_fb(crtc);
256
257                 if (!crtc->enabled)
258                         continue;
259
260                 if (!fb) {
261                         DRM_ERROR("no fb to restore??\n");
262                         continue;
263                 }
264
265                 drm_fb_helper_restore_lut_atomic(mode_set->crtc);
266                 funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,
267                                             crtc->y, LEAVE_ATOMIC_MODE_SET);
268         }
269
270         return 0;
271 }
272 EXPORT_SYMBOL(drm_fb_helper_debug_leave);
273 #endif
274
275 static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
276 {
277         struct drm_device *dev = fb_helper->dev;
278         struct drm_plane *plane;
279         bool error = false;
280         int i;
281
282         drm_warn_on_modeset_not_all_locked(dev);
283
284         list_for_each_entry(plane, &dev->mode_config.plane_list, head)
285                 if (plane->type != DRM_PLANE_TYPE_PRIMARY)
286                         drm_plane_force_disable(plane);
287
288         for (i = 0; i < fb_helper->crtc_count; i++) {
289                 struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
290                 struct drm_crtc *crtc = mode_set->crtc;
291                 int ret;
292
293                 if (crtc->funcs->cursor_set) {
294                         ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
295                         if (ret)
296                                 error = true;
297                 }
298
299                 ret = drm_mode_set_config_internal(mode_set);
300                 if (ret)
301                         error = true;
302         }
303         return error;
304 }
305 /**
306  * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration
307  * @fb_helper: fbcon to restore
308  *
309  * This should be called from driver's drm ->lastclose callback
310  * when implementing an fbcon on top of kms using this helper. This ensures that
311  * the user isn't greeted with a black screen when e.g. X dies.
312  *
313  * Use this variant if you need to bypass locking (panic), or already
314  * hold all modeset locks.  Otherwise use drm_fb_helper_restore_fbdev_mode_unlocked()
315  */
316 static bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
317 {
318         return restore_fbdev_mode(fb_helper);
319 }
320
321 /**
322  * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
323  * @fb_helper: fbcon to restore
324  *
325  * This should be called from driver's drm ->lastclose callback
326  * when implementing an fbcon on top of kms using this helper. This ensures that
327  * the user isn't greeted with a black screen when e.g. X dies.
328  */
329 bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
330 {
331         struct drm_device *dev = fb_helper->dev;
332         bool ret;
333         drm_modeset_lock_all(dev);
334         ret = restore_fbdev_mode(fb_helper);
335         drm_modeset_unlock_all(dev);
336         return ret;
337 }
338 EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
339
340 #if 0
341 /*
342  * restore fbcon display for all kms driver's using this helper, used for sysrq
343  * and panic handling.
344  */
345 static bool drm_fb_helper_force_kernel_mode(void)
346 {
347         bool ret, error = false;
348         struct drm_fb_helper *helper;
349
350         if (list_empty(&kernel_fb_helper_list))
351                 return false;
352
353         list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
354                 struct drm_device *dev = helper->dev;
355
356                 if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
357                         continue;
358
359                 /* NOTE: we use lockless flag below to avoid grabbing other
360                  * modeset locks.  So just trylock the underlying mutex
361                  * directly:
362                  */
363                 if (!mutex_trylock(&dev->mode_config.mutex)) {
364                         error = true;
365                         continue;
366                 }
367
368                 ret = drm_fb_helper_restore_fbdev_mode(helper);
369                 if (ret)
370                         error = true;
371
372                 mutex_unlock(&dev->mode_config.mutex);
373         }
374         return error;
375 }
376
377 static int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
378                         void *panic_str)
379 {
380         /*
381          * It's a waste of time and effort to switch back to text console
382          * if the kernel should reboot before panic messages can be seen.
383          */
384         if (panic_timeout < 0)
385                 return 0;
386
387         pr_err("panic occurred, switching back to text console\n");
388         return drm_fb_helper_force_kernel_mode();
389 }
390
391 static struct notifier_block paniced = {
392         .notifier_call = drm_fb_helper_panic,
393 };
394 #endif
395
396 static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
397 {
398         struct drm_device *dev = fb_helper->dev;
399         struct drm_crtc *crtc;
400         int bound = 0, crtcs_bound = 0;
401
402 #if 0
403         /* Sometimes user space wants everything disabled, so don't steal the
404          * display if there's a master. */
405         if (dev->primary->master)
406                 return false;
407 #endif
408
409         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
410                 if (crtc->primary->fb)
411                         crtcs_bound++;
412                 if (crtc->primary->fb == fb_helper->fb)
413                         bound++;
414         }
415
416         if (bound < crtcs_bound)
417                 return false;
418
419         return true;
420 }
421
422 #if 0
423 #ifdef CONFIG_MAGIC_SYSRQ
424 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
425 {
426         bool ret;
427         ret = drm_fb_helper_force_kernel_mode();
428         if (ret == true)
429                 DRM_ERROR("Failed to restore crtc configuration\n");
430 }
431 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
432
433 static void drm_fb_helper_sysrq(int dummy1)
434 {
435         schedule_work(&drm_fb_helper_restore_work);
436 }
437
438 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
439         .handler = drm_fb_helper_sysrq,
440         .help_msg = "force-fb(V)",
441         .action_msg = "Restore framebuffer console",
442 };
443 #else
444 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
445 #endif
446
447 static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
448 {
449         struct drm_fb_helper *fb_helper = info->par;
450         struct drm_device *dev = fb_helper->dev;
451         struct drm_crtc *crtc;
452         struct drm_connector *connector;
453         int i, j;
454
455         /*
456          * fbdev->blank can be called from irq context in case of a panic.
457          * Since we already have our own special panic handler which will
458          * restore the fbdev console mode completely, just bail out early.
459          */
460         if (oops_in_progress)
461                 return;
462
463         /*
464          * For each CRTC in this fb, turn the connectors on/off.
465          */
466         drm_modeset_lock_all(dev);
467         if (!drm_fb_helper_is_bound(fb_helper)) {
468                 drm_modeset_unlock_all(dev);
469                 return;
470         }
471
472         for (i = 0; i < fb_helper->crtc_count; i++) {
473                 crtc = fb_helper->crtc_info[i].mode_set.crtc;
474
475                 if (!crtc->enabled)
476                         continue;
477
478                 /* Walk the connectors & encoders on this fb turning them on/off */
479                 for (j = 0; j < fb_helper->connector_count; j++) {
480                         connector = fb_helper->connector_info[j]->connector;
481                         connector->funcs->dpms(connector, dpms_mode);
482                         drm_object_property_set_value(&connector->base,
483                                 dev->mode_config.dpms_property, dpms_mode);
484                 }
485         }
486         drm_modeset_unlock_all(dev);
487 }
488
489 /**
490  * drm_fb_helper_blank - implementation for ->fb_blank
491  * @blank: desired blanking state
492  * @info: fbdev registered by the helper
493  */
494 int drm_fb_helper_blank(int blank, struct fb_info *info)
495 {
496         switch (blank) {
497         /* Display: On; HSync: On, VSync: On */
498         case FB_BLANK_UNBLANK:
499                 drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON);
500                 break;
501         /* Display: Off; HSync: On, VSync: On */
502         case FB_BLANK_NORMAL:
503                 drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
504                 break;
505         /* Display: Off; HSync: Off, VSync: On */
506         case FB_BLANK_HSYNC_SUSPEND:
507                 drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
508                 break;
509         /* Display: Off; HSync: On, VSync: Off */
510         case FB_BLANK_VSYNC_SUSPEND:
511                 drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND);
512                 break;
513         /* Display: Off; HSync: Off, VSync: Off */
514         case FB_BLANK_POWERDOWN:
515                 drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF);
516                 break;
517         }
518         return 0;
519 }
520 EXPORT_SYMBOL(drm_fb_helper_blank);
521 #endif
522
523 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
524 {
525         int i;
526
527         for (i = 0; i < helper->connector_count; i++)
528                 kfree(helper->connector_info[i]);
529         kfree(helper->connector_info);
530         for (i = 0; i < helper->crtc_count; i++) {
531                 kfree(helper->crtc_info[i].mode_set.connectors);
532                 if (helper->crtc_info[i].mode_set.mode)
533                         drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
534         }
535         kfree(helper->crtc_info);
536 }
537
538 /**
539  * drm_fb_helper_init - initialize a drm_fb_helper structure
540  * @dev: drm device
541  * @fb_helper: driver-allocated fbdev helper structure to initialize
542  * @crtc_count: maximum number of crtcs to support in this fbdev emulation
543  * @max_conn_count: max connector count
544  *
545  * This allocates the structures for the fbdev helper with the given limits.
546  * Note that this won't yet touch the hardware (through the driver interfaces)
547  * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
548  * to allow driver writes more control over the exact init sequence.
549  *
550  * Drivers must set fb_helper->funcs before calling
551  * drm_fb_helper_initial_config().
552  *
553  * RETURNS:
554  * Zero if everything went ok, nonzero otherwise.
555  */
556 int drm_fb_helper_init(struct drm_device *dev,
557                        struct drm_fb_helper *fb_helper,
558                        int crtc_count, int max_conn_count)
559 {
560         struct drm_crtc *crtc;
561         int i;
562
563         if (!max_conn_count)
564                 return -EINVAL;
565
566         fb_helper->dev = dev;
567
568         INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
569
570         fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
571         if (!fb_helper->crtc_info)
572                 return -ENOMEM;
573
574         fb_helper->crtc_count = crtc_count;
575         fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
576         if (!fb_helper->connector_info) {
577                 kfree(fb_helper->crtc_info);
578                 return -ENOMEM;
579         }
580         fb_helper->connector_count = 0;
581
582         for (i = 0; i < crtc_count; i++) {
583                 fb_helper->crtc_info[i].mode_set.connectors =
584                         kcalloc(max_conn_count,
585                                 sizeof(struct drm_connector *),
586                                 GFP_KERNEL);
587
588                 if (!fb_helper->crtc_info[i].mode_set.connectors)
589                         goto out_free;
590                 fb_helper->crtc_info[i].mode_set.num_connectors = 0;
591         }
592
593         i = 0;
594         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
595                 fb_helper->crtc_info[i].mode_set.crtc = crtc;
596                 i++;
597         }
598
599         return 0;
600 out_free:
601         drm_fb_helper_crtc_free(fb_helper);
602         return -ENOMEM;
603 }
604 EXPORT_SYMBOL(drm_fb_helper_init);
605
606 void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
607 {
608         if (!list_empty(&fb_helper->kernel_fb_list)) {
609                 list_del(&fb_helper->kernel_fb_list);
610                 if (list_empty(&kernel_fb_helper_list)) {
611 #if 0
612                         pr_info("drm: unregistered panic notifier\n");
613                         atomic_notifier_chain_unregister(&panic_notifier_list,
614                                                          &paniced);
615                         unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
616 #endif
617                 }
618         }
619
620         drm_fb_helper_crtc_free(fb_helper);
621
622 }
623 EXPORT_SYMBOL(drm_fb_helper_fini);
624
625 #if 0
626 static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
627                      u16 blue, u16 regno, struct fb_info *info)
628 {
629         struct drm_fb_helper *fb_helper = info->par;
630         struct drm_framebuffer *fb = fb_helper->fb;
631         int pindex;
632
633         if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
634                 u32 *palette;
635                 u32 value;
636                 /* place color in psuedopalette */
637                 if (regno > 16)
638                         return -EINVAL;
639                 palette = (u32 *)info->pseudo_palette;
640                 red >>= (16 - info->var.red.length);
641                 green >>= (16 - info->var.green.length);
642                 blue >>= (16 - info->var.blue.length);
643                 value = (red << info->var.red.offset) |
644                         (green << info->var.green.offset) |
645                         (blue << info->var.blue.offset);
646                 if (info->var.transp.length > 0) {
647                         u32 mask = (1 << info->var.transp.length) - 1;
648                         mask <<= info->var.transp.offset;
649                         value |= mask;
650                 }
651                 palette[regno] = value;
652                 return 0;
653         }
654
655         /*
656          * The driver really shouldn't advertise pseudo/directcolor
657          * visuals if it can't deal with the palette.
658          */
659         if (WARN_ON(!fb_helper->funcs->gamma_set ||
660                     !fb_helper->funcs->gamma_get))
661                 return -EINVAL;
662
663         pindex = regno;
664
665         if (fb->bits_per_pixel == 16) {
666                 pindex = regno << 3;
667
668                 if (fb->depth == 16 && regno > 63)
669                         return -EINVAL;
670                 if (fb->depth == 15 && regno > 31)
671                         return -EINVAL;
672
673                 if (fb->depth == 16) {
674                         u16 r, g, b;
675                         int i;
676                         if (regno < 32) {
677                                 for (i = 0; i < 8; i++)
678                                         fb_helper->funcs->gamma_set(crtc, red,
679                                                 green, blue, pindex + i);
680                         }
681
682                         fb_helper->funcs->gamma_get(crtc, &r,
683                                                     &g, &b,
684                                                     pindex >> 1);
685
686                         for (i = 0; i < 4; i++)
687                                 fb_helper->funcs->gamma_set(crtc, r,
688                                                             green, b,
689                                                             (pindex >> 1) + i);
690                 }
691         }
692
693         if (fb->depth != 16)
694                 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
695         return 0;
696 }
697
698 /**
699  * drm_fb_helper_setcmap - implementation for ->fb_setcmap
700  * @cmap: cmap to set
701  * @info: fbdev registered by the helper
702  */
703 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
704 {
705         struct drm_fb_helper *fb_helper = info->par;
706         struct drm_device *dev = fb_helper->dev;
707         struct drm_crtc_helper_funcs *crtc_funcs;
708         u16 *red, *green, *blue, *transp;
709         struct drm_crtc *crtc;
710         int i, j, rc = 0;
711         int start;
712
713         drm_modeset_lock_all(dev);
714         if (!drm_fb_helper_is_bound(fb_helper)) {
715                 drm_modeset_unlock_all(dev);
716                 return -EBUSY;
717         }
718
719         for (i = 0; i < fb_helper->crtc_count; i++) {
720                 crtc = fb_helper->crtc_info[i].mode_set.crtc;
721                 crtc_funcs = crtc->helper_private;
722
723                 red = cmap->red;
724                 green = cmap->green;
725                 blue = cmap->blue;
726                 transp = cmap->transp;
727                 start = cmap->start;
728
729                 for (j = 0; j < cmap->len; j++) {
730                         u16 hred, hgreen, hblue, htransp = 0xffff;
731
732                         hred = *red++;
733                         hgreen = *green++;
734                         hblue = *blue++;
735
736                         if (transp)
737                                 htransp = *transp++;
738
739                         rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
740                         if (rc)
741                                 goto out;
742                 }
743                 if (crtc_funcs->load_lut)
744                         crtc_funcs->load_lut(crtc);
745         }
746  out:
747         drm_modeset_unlock_all(dev);
748         return rc;
749 }
750 EXPORT_SYMBOL(drm_fb_helper_setcmap);
751
752 /**
753  * drm_fb_helper_check_var - implementation for ->fb_check_var
754  * @var: screeninfo to check
755  * @info: fbdev registered by the helper
756  */
757 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
758                             struct fb_info *info)
759 {
760         struct drm_fb_helper *fb_helper = info->par;
761         struct drm_framebuffer *fb = fb_helper->fb;
762         int depth;
763
764         if (var->pixclock != 0 || in_dbg_master())
765                 return -EINVAL;
766
767         /* Need to resize the fb object !!! */
768         if (var->bits_per_pixel > fb->bits_per_pixel ||
769             var->xres > fb->width || var->yres > fb->height ||
770             var->xres_virtual > fb->width || var->yres_virtual > fb->height) {
771                 DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
772                           "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n",
773                           var->xres, var->yres, var->bits_per_pixel,
774                           var->xres_virtual, var->yres_virtual,
775                           fb->width, fb->height, fb->bits_per_pixel);
776                 return -EINVAL;
777         }
778
779         switch (var->bits_per_pixel) {
780         case 16:
781                 depth = (var->green.length == 6) ? 16 : 15;
782                 break;
783         case 32:
784                 depth = (var->transp.length > 0) ? 32 : 24;
785                 break;
786         default:
787                 depth = var->bits_per_pixel;
788                 break;
789         }
790
791         switch (depth) {
792         case 8:
793                 var->red.offset = 0;
794                 var->green.offset = 0;
795                 var->blue.offset = 0;
796                 var->red.length = 8;
797                 var->green.length = 8;
798                 var->blue.length = 8;
799                 var->transp.length = 0;
800                 var->transp.offset = 0;
801                 break;
802         case 15:
803                 var->red.offset = 10;
804                 var->green.offset = 5;
805                 var->blue.offset = 0;
806                 var->red.length = 5;
807                 var->green.length = 5;
808                 var->blue.length = 5;
809                 var->transp.length = 1;
810                 var->transp.offset = 15;
811                 break;
812         case 16:
813                 var->red.offset = 11;
814                 var->green.offset = 5;
815                 var->blue.offset = 0;
816                 var->red.length = 5;
817                 var->green.length = 6;
818                 var->blue.length = 5;
819                 var->transp.length = 0;
820                 var->transp.offset = 0;
821                 break;
822         case 24:
823                 var->red.offset = 16;
824                 var->green.offset = 8;
825                 var->blue.offset = 0;
826                 var->red.length = 8;
827                 var->green.length = 8;
828                 var->blue.length = 8;
829                 var->transp.length = 0;
830                 var->transp.offset = 0;
831                 break;
832         case 32:
833                 var->red.offset = 16;
834                 var->green.offset = 8;
835                 var->blue.offset = 0;
836                 var->red.length = 8;
837                 var->green.length = 8;
838                 var->blue.length = 8;
839                 var->transp.length = 8;
840                 var->transp.offset = 24;
841                 break;
842         default:
843                 return -EINVAL;
844         }
845         return 0;
846 }
847 EXPORT_SYMBOL(drm_fb_helper_check_var);
848
849 /**
850  * drm_fb_helper_set_par - implementation for ->fb_set_par
851  * @info: fbdev registered by the helper
852  *
853  * This will let fbcon do the mode init and is called at initialization time by
854  * the fbdev core when registering the driver, and later on through the hotplug
855  * callback.
856  */
857 int drm_fb_helper_set_par(struct fb_info *info)
858 {
859         struct drm_fb_helper *fb_helper = info->par;
860         struct fb_var_screeninfo *var = &info->var;
861
862         if (var->pixclock != 0) {
863                 DRM_ERROR("PIXEL CLOCK SET\n");
864                 return -EINVAL;
865         }
866
867         drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
868
869         if (fb_helper->delayed_hotplug) {
870                 fb_helper->delayed_hotplug = false;
871                 drm_fb_helper_hotplug_event(fb_helper);
872         }
873         return 0;
874 }
875 EXPORT_SYMBOL(drm_fb_helper_set_par);
876
877 /**
878  * drm_fb_helper_pan_display - implementation for ->fb_pan_display
879  * @var: updated screen information
880  * @info: fbdev registered by the helper
881  */
882 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
883                               struct fb_info *info)
884 {
885         struct drm_fb_helper *fb_helper = info->par;
886         struct drm_device *dev = fb_helper->dev;
887         struct drm_mode_set *modeset;
888         int ret = 0;
889         int i;
890
891         drm_modeset_lock_all(dev);
892         if (!drm_fb_helper_is_bound(fb_helper)) {
893                 drm_modeset_unlock_all(dev);
894                 return -EBUSY;
895         }
896
897         for (i = 0; i < fb_helper->crtc_count; i++) {
898                 modeset = &fb_helper->crtc_info[i].mode_set;
899
900                 modeset->x = var->xoffset;
901                 modeset->y = var->yoffset;
902
903                 if (modeset->num_connectors) {
904                         ret = drm_mode_set_config_internal(modeset);
905                         if (!ret) {
906                                 info->var.xoffset = var->xoffset;
907                                 info->var.yoffset = var->yoffset;
908                         }
909                 }
910         }
911         drm_modeset_unlock_all(dev);
912         return ret;
913 }
914 EXPORT_SYMBOL(drm_fb_helper_pan_display);
915 #endif
916
917 /* XXX: DragonFly-specific */
918 static void
919 do_restore_fbdev_mode(void *context, int pending)
920 {
921         struct drm_fb_helper *fb_helper = context;
922         struct drm_device *dev = fb_helper->dev;
923
924         if (!fb_helper->fb)
925                 return;
926
927         drm_modeset_lock_all(dev);
928         drm_fb_helper_restore_fbdev_mode(fb_helper);
929         drm_modeset_unlock_all(dev);
930 }
931
932 /* XXX: DragonFly-specific */
933 static void
934 sc_restore_fbdev_mode(void *cookie)
935 {
936         struct drm_fb_helper *fb_helper = cookie;
937
938         if (!fb_helper->fb)
939                 return;
940
941         taskqueue_enqueue(taskqueue_thread[0], &fb_helper->fb_mode_task);
942 }
943
944 /*
945  * Allocates the backing storage and sets up the fbdev info structure through
946  * the ->fb_probe callback and then registers the fbdev and sets up the panic
947  * notifier.
948  */
949 static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
950                                          int preferred_bpp)
951 {
952         int ret = 0;
953         int crtc_count = 0;
954         int i;
955         struct fb_info *info;
956         struct drm_fb_helper_surface_size sizes;
957         int gamma_size = 0;
958         int kms_console = 1;    /* XXX: DragonFly-specific */
959
960         memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
961         sizes.surface_depth = 24;
962         sizes.surface_bpp = 32;
963         sizes.fb_width = (unsigned)-1;
964         sizes.fb_height = (unsigned)-1;
965
966         /* if driver picks 8 or 16 by default use that
967            for both depth/bpp */
968         if (preferred_bpp != sizes.surface_bpp)
969                 sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
970
971         /* first up get a count of crtcs now in use and new min/maxes width/heights */
972         for (i = 0; i < fb_helper->connector_count; i++) {
973                 struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
974                 struct drm_cmdline_mode *cmdline_mode;
975
976                 cmdline_mode = &fb_helper_conn->cmdline_mode;
977
978                 if (cmdline_mode->bpp_specified) {
979                         switch (cmdline_mode->bpp) {
980                         case 8:
981                                 sizes.surface_depth = sizes.surface_bpp = 8;
982                                 break;
983                         case 15:
984                                 sizes.surface_depth = 15;
985                                 sizes.surface_bpp = 16;
986                                 break;
987                         case 16:
988                                 sizes.surface_depth = sizes.surface_bpp = 16;
989                                 break;
990                         case 24:
991                                 sizes.surface_depth = sizes.surface_bpp = 24;
992                                 break;
993                         case 32:
994                                 sizes.surface_depth = 24;
995                                 sizes.surface_bpp = 32;
996                                 break;
997                         }
998                         break;
999                 }
1000         }
1001
1002         crtc_count = 0;
1003         for (i = 0; i < fb_helper->crtc_count; i++) {
1004                 struct drm_display_mode *desired_mode;
1005                 desired_mode = fb_helper->crtc_info[i].desired_mode;
1006
1007                 if (desired_mode) {
1008                         if (gamma_size == 0)
1009                                 gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
1010                         if (desired_mode->hdisplay < sizes.fb_width)
1011                                 sizes.fb_width = desired_mode->hdisplay;
1012                         if (desired_mode->vdisplay < sizes.fb_height)
1013                                 sizes.fb_height = desired_mode->vdisplay;
1014                         if (desired_mode->hdisplay > sizes.surface_width)
1015                                 sizes.surface_width = desired_mode->hdisplay;
1016                         if (desired_mode->vdisplay > sizes.surface_height)
1017                                 sizes.surface_height = desired_mode->vdisplay;
1018                         crtc_count++;
1019                 }
1020         }
1021
1022         if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
1023                 /* hmm everyone went away - assume VGA cable just fell out
1024                    and will come back later. */
1025                 DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n");
1026                 sizes.fb_width = sizes.surface_width = 1024;
1027                 sizes.fb_height = sizes.surface_height = 768;
1028         }
1029
1030         /* push down into drivers */
1031         ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
1032         if (ret < 0)
1033                 return ret;
1034
1035         info = fb_helper->fbdev;
1036
1037         /*
1038          * Set the fb pointer - usually drm_setup_crtcs does this for hotplug
1039          * events, but at init time drm_setup_crtcs needs to be called before
1040          * the fb is allocated (since we need to figure out the desired size of
1041          * the fb before we can allocate it ...). Hence we need to fix things up
1042          * here again.
1043          */
1044         for (i = 0; i < fb_helper->crtc_count; i++)
1045                 if (fb_helper->crtc_info[i].mode_set.num_connectors)
1046                         fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
1047
1048
1049         /* XXX: DragonFly-specific */
1050         TUNABLE_INT_FETCH("kern.kms_console", &kms_console);
1051         if (kms_console) {
1052                 TASK_INIT(&fb_helper->fb_mode_task, 0, do_restore_fbdev_mode,
1053                     fb_helper);
1054                 info->cookie = fb_helper;
1055                 info->restore = (void *)&sc_restore_fbdev_mode;
1056                 if (register_framebuffer(info) < 0)
1057                         return -EINVAL;
1058         }
1059
1060 #if 0
1061         info->var.pixclock = 0;
1062         if (register_framebuffer(info) < 0)
1063                 return -EINVAL;
1064
1065         dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n",
1066                         info->node, info->fix.id);
1067
1068         /* Switch back to kernel console on panic */
1069         /* multi card linked list maybe */
1070         if (list_empty(&kernel_fb_helper_list)) {
1071                 dev_info(fb_helper->dev->dev, "registered panic notifier\n");
1072                 atomic_notifier_chain_register(&panic_notifier_list,
1073                                                &paniced);
1074                 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
1075         }
1076
1077         list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
1078 #endif
1079
1080         return 0;
1081 }
1082
1083 #if 0
1084 /**
1085  * drm_fb_helper_fill_fix - initializes fixed fbdev information
1086  * @info: fbdev registered by the helper
1087  * @pitch: desired pitch
1088  * @depth: desired depth
1089  *
1090  * Helper to fill in the fixed fbdev information useful for a non-accelerated
1091  * fbdev emulations. Drivers which support acceleration methods which impose
1092  * additional constraints need to set up their own limits.
1093  *
1094  * Drivers should call this (or their equivalent setup code) from their
1095  * ->fb_probe callback.
1096  */
1097 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
1098                             uint32_t depth)
1099 {
1100         info->fix.type = FB_TYPE_PACKED_PIXELS;
1101         info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
1102                 FB_VISUAL_TRUECOLOR;
1103         info->fix.mmio_start = 0;
1104         info->fix.mmio_len = 0;
1105         info->fix.type_aux = 0;
1106         info->fix.xpanstep = 1; /* doing it in hw */
1107         info->fix.ypanstep = 1; /* doing it in hw */
1108         info->fix.ywrapstep = 0;
1109         info->fix.accel = FB_ACCEL_NONE;
1110         info->fix.type_aux = 0;
1111
1112         info->fix.line_length = pitch;
1113         return;
1114 }
1115 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
1116
1117 /**
1118  * drm_fb_helper_fill_var - initalizes variable fbdev information
1119  * @info: fbdev instance to set up
1120  * @fb_helper: fb helper instance to use as template
1121  * @fb_width: desired fb width
1122  * @fb_height: desired fb height
1123  *
1124  * Sets up the variable fbdev metainformation from the given fb helper instance
1125  * and the drm framebuffer allocated in fb_helper->fb.
1126  *
1127  * Drivers should call this (or their equivalent setup code) from their
1128  * ->fb_probe callback after having allocated the fbdev backing
1129  * storage framebuffer.
1130  */
1131 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
1132                             uint32_t fb_width, uint32_t fb_height)
1133 {
1134         struct drm_framebuffer *fb = fb_helper->fb;
1135         info->pseudo_palette = fb_helper->pseudo_palette;
1136         info->var.xres_virtual = fb->width;
1137         info->var.yres_virtual = fb->height;
1138         info->var.bits_per_pixel = fb->bits_per_pixel;
1139         info->var.accel_flags = FB_ACCELF_TEXT;
1140         info->var.xoffset = 0;
1141         info->var.yoffset = 0;
1142         info->var.activate = FB_ACTIVATE_NOW;
1143         info->var.height = -1;
1144         info->var.width = -1;
1145
1146         switch (fb->depth) {
1147         case 8:
1148                 info->var.red.offset = 0;
1149                 info->var.green.offset = 0;
1150                 info->var.blue.offset = 0;
1151                 info->var.red.length = 8; /* 8bit DAC */
1152                 info->var.green.length = 8;
1153                 info->var.blue.length = 8;
1154                 info->var.transp.offset = 0;
1155                 info->var.transp.length = 0;
1156                 break;
1157         case 15:
1158                 info->var.red.offset = 10;
1159                 info->var.green.offset = 5;
1160                 info->var.blue.offset = 0;
1161                 info->var.red.length = 5;
1162                 info->var.green.length = 5;
1163                 info->var.blue.length = 5;
1164                 info->var.transp.offset = 15;
1165                 info->var.transp.length = 1;
1166                 break;
1167         case 16:
1168                 info->var.red.offset = 11;
1169                 info->var.green.offset = 5;
1170                 info->var.blue.offset = 0;
1171                 info->var.red.length = 5;
1172                 info->var.green.length = 6;
1173                 info->var.blue.length = 5;
1174                 info->var.transp.offset = 0;
1175                 break;
1176         case 24:
1177                 info->var.red.offset = 16;
1178                 info->var.green.offset = 8;
1179                 info->var.blue.offset = 0;
1180                 info->var.red.length = 8;
1181                 info->var.green.length = 8;
1182                 info->var.blue.length = 8;
1183                 info->var.transp.offset = 0;
1184                 info->var.transp.length = 0;
1185                 break;
1186         case 32:
1187                 info->var.red.offset = 16;
1188                 info->var.green.offset = 8;
1189                 info->var.blue.offset = 0;
1190                 info->var.red.length = 8;
1191                 info->var.green.length = 8;
1192                 info->var.blue.length = 8;
1193                 info->var.transp.offset = 24;
1194                 info->var.transp.length = 8;
1195                 break;
1196         default:
1197                 break;
1198         }
1199
1200         info->var.xres = fb_width;
1201         info->var.yres = fb_height;
1202 }
1203 EXPORT_SYMBOL(drm_fb_helper_fill_var);
1204 #endif
1205
1206 static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
1207                                                uint32_t maxX,
1208                                                uint32_t maxY)
1209 {
1210         struct drm_connector *connector;
1211         int count = 0;
1212         int i;
1213
1214         for (i = 0; i < fb_helper->connector_count; i++) {
1215                 connector = fb_helper->connector_info[i]->connector;
1216                 count += connector->funcs->fill_modes(connector, maxX, maxY);
1217         }
1218
1219         return count;
1220 }
1221
1222 struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
1223 {
1224         struct drm_display_mode *mode;
1225
1226         list_for_each_entry(mode, &fb_connector->connector->modes, head) {
1227                 if (mode->hdisplay > width ||
1228                     mode->vdisplay > height)
1229                         continue;
1230                 if (mode->type & DRM_MODE_TYPE_PREFERRED)
1231                         return mode;
1232         }
1233         return NULL;
1234 }
1235 EXPORT_SYMBOL(drm_has_preferred_mode);
1236
1237 static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
1238 {
1239         struct drm_cmdline_mode *cmdline_mode;
1240         cmdline_mode = &fb_connector->cmdline_mode;
1241         return cmdline_mode->specified;
1242 }
1243
1244 struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
1245                                                       int width, int height)
1246 {
1247         struct drm_cmdline_mode *cmdline_mode;
1248         struct drm_display_mode *mode = NULL;
1249         bool prefer_non_interlace;
1250
1251         cmdline_mode = &fb_helper_conn->cmdline_mode;
1252         if (cmdline_mode->specified == false)
1253                 return mode;
1254
1255         /* attempt to find a matching mode in the list of modes
1256          *  we have gotten so far, if not add a CVT mode that conforms
1257          */
1258         if (cmdline_mode->rb || cmdline_mode->margins)
1259                 goto create_mode;
1260
1261         prefer_non_interlace = !cmdline_mode->interlace;
1262  again:
1263         list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1264                 /* check width/height */
1265                 if (mode->hdisplay != cmdline_mode->xres ||
1266                     mode->vdisplay != cmdline_mode->yres)
1267                         continue;
1268
1269                 if (cmdline_mode->refresh_specified) {
1270                         if (mode->vrefresh != cmdline_mode->refresh)
1271                                 continue;
1272                 }
1273
1274                 if (cmdline_mode->interlace) {
1275                         if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
1276                                 continue;
1277                 } else if (prefer_non_interlace) {
1278                         if (mode->flags & DRM_MODE_FLAG_INTERLACE)
1279                                 continue;
1280                 }
1281                 return mode;
1282         }
1283
1284         if (prefer_non_interlace) {
1285                 prefer_non_interlace = false;
1286                 goto again;
1287         }
1288
1289 create_mode:
1290         mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
1291                                                  cmdline_mode);
1292         list_add(&mode->head, &fb_helper_conn->connector->modes);
1293         return mode;
1294 }
1295 EXPORT_SYMBOL(drm_pick_cmdline_mode);
1296
1297 static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
1298 {
1299         bool enable;
1300
1301         if (strict)
1302                 enable = connector->status == connector_status_connected;
1303         else
1304                 enable = connector->status != connector_status_disconnected;
1305
1306         return enable;
1307 }
1308
1309 static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
1310                                   bool *enabled)
1311 {
1312         bool any_enabled = false;
1313         struct drm_connector *connector;
1314         int i = 0;
1315
1316         for (i = 0; i < fb_helper->connector_count; i++) {
1317                 connector = fb_helper->connector_info[i]->connector;
1318                 enabled[i] = drm_connector_enabled(connector, true);
1319                 DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
1320                           enabled[i] ? "yes" : "no");
1321                 any_enabled |= enabled[i];
1322         }
1323
1324         if (any_enabled)
1325                 return;
1326
1327         for (i = 0; i < fb_helper->connector_count; i++) {
1328                 connector = fb_helper->connector_info[i]->connector;
1329                 enabled[i] = drm_connector_enabled(connector, false);
1330         }
1331 }
1332
1333 static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
1334                               struct drm_display_mode **modes,
1335                               bool *enabled, int width, int height)
1336 {
1337         int count, i, j;
1338         bool can_clone = false;
1339         struct drm_fb_helper_connector *fb_helper_conn;
1340         struct drm_display_mode *dmt_mode, *mode;
1341
1342         /* only contemplate cloning in the single crtc case */
1343         if (fb_helper->crtc_count > 1)
1344                 return false;
1345
1346         count = 0;
1347         for (i = 0; i < fb_helper->connector_count; i++) {
1348                 if (enabled[i])
1349                         count++;
1350         }
1351
1352         /* only contemplate cloning if more than one connector is enabled */
1353         if (count <= 1)
1354                 return false;
1355
1356         /* check the command line or if nothing common pick 1024x768 */
1357         can_clone = true;
1358         for (i = 0; i < fb_helper->connector_count; i++) {
1359                 if (!enabled[i])
1360                         continue;
1361                 fb_helper_conn = fb_helper->connector_info[i];
1362                 modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1363                 if (!modes[i]) {
1364                         can_clone = false;
1365                         break;
1366                 }
1367                 for (j = 0; j < i; j++) {
1368                         if (!enabled[j])
1369                                 continue;
1370                         if (!drm_mode_equal(modes[j], modes[i]))
1371                                 can_clone = false;
1372                 }
1373         }
1374
1375         if (can_clone) {
1376                 DRM_DEBUG_KMS("can clone using command line\n");
1377                 return true;
1378         }
1379
1380         /* try and find a 1024x768 mode on each connector */
1381         can_clone = true;
1382         dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
1383
1384         for (i = 0; i < fb_helper->connector_count; i++) {
1385
1386                 if (!enabled[i])
1387                         continue;
1388
1389                 fb_helper_conn = fb_helper->connector_info[i];
1390                 list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1391                         if (drm_mode_equal(mode, dmt_mode))
1392                                 modes[i] = mode;
1393                 }
1394                 if (!modes[i])
1395                         can_clone = false;
1396         }
1397
1398         if (can_clone) {
1399                 DRM_DEBUG_KMS("can clone using 1024x768\n");
1400                 return true;
1401         }
1402         DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
1403         return false;
1404 }
1405
1406 static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
1407                                  struct drm_display_mode **modes,
1408                                  bool *enabled, int width, int height)
1409 {
1410         struct drm_fb_helper_connector *fb_helper_conn;
1411         int i;
1412
1413         for (i = 0; i < fb_helper->connector_count; i++) {
1414                 fb_helper_conn = fb_helper->connector_info[i];
1415
1416                 if (enabled[i] == false)
1417                         continue;
1418
1419                 DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
1420                               fb_helper_conn->connector->base.id);
1421
1422                 /* got for command line mode first */
1423                 modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1424                 if (!modes[i]) {
1425                         DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
1426                                       fb_helper_conn->connector->base.id);
1427                         modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
1428                 }
1429                 /* No preferred modes, pick one off the list */
1430                 if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
1431                         list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
1432                                 break;
1433                 }
1434                 DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
1435                           "none");
1436         }
1437         return true;
1438 }
1439
1440 static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
1441                           struct drm_fb_helper_crtc **best_crtcs,
1442                           struct drm_display_mode **modes,
1443                           int n, int width, int height)
1444 {
1445         int c, o;
1446         struct drm_device *dev = fb_helper->dev;
1447         struct drm_connector *connector;
1448         struct drm_connector_helper_funcs *connector_funcs;
1449         struct drm_encoder *encoder;
1450         int my_score, best_score, score;
1451         struct drm_fb_helper_crtc **crtcs, *crtc;
1452         struct drm_fb_helper_connector *fb_helper_conn;
1453
1454         if (n == fb_helper->connector_count)
1455                 return 0;
1456
1457         fb_helper_conn = fb_helper->connector_info[n];
1458         connector = fb_helper_conn->connector;
1459
1460         best_crtcs[n] = NULL;
1461         best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
1462         if (modes[n] == NULL)
1463                 return best_score;
1464
1465         crtcs = kzalloc(dev->mode_config.num_connector *
1466                         sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
1467         if (!crtcs)
1468                 return best_score;
1469
1470         my_score = 1;
1471         if (connector->status == connector_status_connected)
1472                 my_score++;
1473         if (drm_has_cmdline_mode(fb_helper_conn))
1474                 my_score++;
1475         if (drm_has_preferred_mode(fb_helper_conn, width, height))
1476                 my_score++;
1477
1478         connector_funcs = connector->helper_private;
1479         encoder = connector_funcs->best_encoder(connector);
1480         if (!encoder)
1481                 goto out;
1482
1483         /* select a crtc for this connector and then attempt to configure
1484            remaining connectors */
1485         for (c = 0; c < fb_helper->crtc_count; c++) {
1486                 crtc = &fb_helper->crtc_info[c];
1487
1488                 if ((encoder->possible_crtcs & (1 << c)) == 0)
1489                         continue;
1490
1491                 for (o = 0; o < n; o++)
1492                         if (best_crtcs[o] == crtc)
1493                                 break;
1494
1495                 if (o < n) {
1496                         /* ignore cloning unless only a single crtc */
1497                         if (fb_helper->crtc_count > 1)
1498                                 continue;
1499
1500                         if (!drm_mode_equal(modes[o], modes[n]))
1501                                 continue;
1502                 }
1503
1504                 crtcs[n] = crtc;
1505                 memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
1506                 score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
1507                                                   width, height);
1508                 if (score > best_score) {
1509                         best_score = score;
1510                         memcpy(best_crtcs, crtcs,
1511                                dev->mode_config.num_connector *
1512                                sizeof(struct drm_fb_helper_crtc *));
1513                 }
1514         }
1515 out:
1516         kfree(crtcs);
1517         return best_score;
1518 }
1519
1520 static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
1521 {
1522         struct drm_device *dev = fb_helper->dev;
1523         struct drm_fb_helper_crtc **crtcs;
1524         struct drm_display_mode **modes;
1525         struct drm_mode_set *modeset;
1526         bool *enabled;
1527         int width, height;
1528         int i;
1529
1530         DRM_DEBUG_KMS("\n");
1531
1532         width = dev->mode_config.max_width;
1533         height = dev->mode_config.max_height;
1534
1535         crtcs = kcalloc(dev->mode_config.num_connector,
1536                         sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
1537         modes = kcalloc(dev->mode_config.num_connector,
1538                         sizeof(struct drm_display_mode *), GFP_KERNEL);
1539         enabled = kcalloc(dev->mode_config.num_connector,
1540                           sizeof(bool), GFP_KERNEL);
1541         if (!crtcs || !modes || !enabled) {
1542                 DRM_ERROR("Memory allocation failed\n");
1543                 goto out;
1544         }
1545
1546
1547         drm_enable_connectors(fb_helper, enabled);
1548
1549         if (!(fb_helper->funcs->initial_config &&
1550               fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
1551                                                enabled, width, height))) {
1552                 memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0]));
1553                 memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0]));
1554
1555                 if (!drm_target_cloned(fb_helper,
1556                                        modes, enabled, width, height) &&
1557                     !drm_target_preferred(fb_helper,
1558                                           modes, enabled, width, height))
1559                         DRM_ERROR("Unable to find initial modes\n");
1560
1561                 DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
1562                               width, height);
1563
1564                 drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
1565         }
1566
1567         /* need to set the modesets up here for use later */
1568         /* fill out the connector<->crtc mappings into the modesets */
1569         for (i = 0; i < fb_helper->crtc_count; i++) {
1570                 modeset = &fb_helper->crtc_info[i].mode_set;
1571                 modeset->num_connectors = 0;
1572                 modeset->fb = NULL;
1573         }
1574
1575         for (i = 0; i < fb_helper->connector_count; i++) {
1576                 struct drm_display_mode *mode = modes[i];
1577                 struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
1578                 modeset = &fb_crtc->mode_set;
1579
1580                 if (mode && fb_crtc) {
1581                         DRM_DEBUG_KMS("desired mode %s set on crtc %d\n",
1582                                       mode->name, fb_crtc->mode_set.crtc->base.id);
1583                         fb_crtc->desired_mode = mode;
1584                         if (modeset->mode)
1585                                 drm_mode_destroy(dev, modeset->mode);
1586                         modeset->mode = drm_mode_duplicate(dev,
1587                                                            fb_crtc->desired_mode);
1588                         modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
1589                         modeset->fb = fb_helper->fb;
1590                 }
1591         }
1592
1593         /* Clear out any old modes if there are no more connected outputs. */
1594         for (i = 0; i < fb_helper->crtc_count; i++) {
1595                 modeset = &fb_helper->crtc_info[i].mode_set;
1596                 if (modeset->num_connectors == 0) {
1597                         BUG_ON(modeset->fb);
1598                         BUG_ON(modeset->num_connectors);
1599                         if (modeset->mode)
1600                                 drm_mode_destroy(dev, modeset->mode);
1601                         modeset->mode = NULL;
1602                 }
1603         }
1604 out:
1605         kfree(crtcs);
1606         kfree(modes);
1607         kfree(enabled);
1608 }
1609
1610 /**
1611  * drm_fb_helper_initial_config - setup a sane initial connector configuration
1612  * @fb_helper: fb_helper device struct
1613  * @bpp_sel: bpp value to use for the framebuffer configuration
1614  *
1615  * Scans the CRTCs and connectors and tries to put together an initial setup.
1616  * At the moment, this is a cloned configuration across all heads with
1617  * a new framebuffer object as the backing store.
1618  *
1619  * Note that this also registers the fbdev and so allows userspace to call into
1620  * the driver through the fbdev interfaces.
1621  *
1622  * This function will call down into the ->fb_probe callback to let
1623  * the driver allocate and initialize the fbdev info structure and the drm
1624  * framebuffer used to back the fbdev. drm_fb_helper_fill_var() and
1625  * drm_fb_helper_fill_fix() are provided as helpers to setup simple default
1626  * values for the fbdev info structure.
1627  *
1628  * RETURNS:
1629  * Zero if everything went ok, nonzero otherwise.
1630  */
1631 bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
1632 {
1633         struct drm_device *dev = fb_helper->dev;
1634         int count = 0;
1635
1636         drm_fb_helper_parse_command_line(fb_helper);
1637
1638         mutex_lock(&dev->mode_config.mutex);
1639         count = drm_fb_helper_probe_connector_modes(fb_helper,
1640                                                     dev->mode_config.max_width,
1641                                                     dev->mode_config.max_height);
1642         mutex_unlock(&dev->mode_config.mutex);
1643         /*
1644          * we shouldn't end up with no modes here.
1645          */
1646         if (count == 0)
1647                 dev_info(fb_helper->dev->dev, "No connectors reported connected with modes\n");
1648
1649         drm_setup_crtcs(fb_helper);
1650
1651         return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
1652 }
1653 EXPORT_SYMBOL(drm_fb_helper_initial_config);
1654
1655 /**
1656  * drm_fb_helper_hotplug_event - respond to a hotplug notification by
1657  *                               probing all the outputs attached to the fb
1658  * @fb_helper: the drm_fb_helper
1659  *
1660  * Scan the connectors attached to the fb_helper and try to put together a
1661  * setup after *notification of a change in output configuration.
1662  *
1663  * Called at runtime, takes the mode config locks to be able to check/change the
1664  * modeset configuration. Must be run from process context (which usually means
1665  * either the output polling work or a work item launched from the driver's
1666  * hotplug interrupt).
1667  *
1668  * Note that the driver must ensure that this is only called _after_ the fb has
1669  * been fully set up, i.e. after the call to drm_fb_helper_initial_config.
1670  *
1671  * RETURNS:
1672  * 0 on success and a non-zero error code otherwise.
1673  */
1674 int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
1675 {
1676         struct drm_device *dev = fb_helper->dev;
1677         u32 max_width, max_height;
1678
1679         if (!fb_helper->fb)
1680                 return 0;
1681
1682         mutex_lock(&fb_helper->dev->mode_config.mutex);
1683         if (!drm_fb_helper_is_bound(fb_helper)) {
1684                 fb_helper->delayed_hotplug = true;
1685                 mutex_unlock(&fb_helper->dev->mode_config.mutex);
1686                 return 0;
1687         }
1688         DRM_DEBUG_KMS("\n");
1689
1690         max_width = fb_helper->fb->width;
1691         max_height = fb_helper->fb->height;
1692
1693         drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height);
1694         mutex_unlock(&fb_helper->dev->mode_config.mutex);
1695
1696         drm_modeset_lock_all(dev);
1697         drm_setup_crtcs(fb_helper);
1698         drm_modeset_unlock_all(dev);
1699 #if 0
1700         drm_fb_helper_set_par(fb_helper->fbdev);
1701 #endif
1702
1703         return 0;
1704 }
1705 EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
1706
1707 /* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
1708  * but the module doesn't depend on any fb console symbols.  At least
1709  * attempt to load fbcon to avoid leaving the system without a usable console.
1710  */
1711 #if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT)
1712 static int __init drm_fb_helper_modinit(void)
1713 {
1714         const char *name = "fbcon";
1715         struct module *fbcon;
1716
1717         mutex_lock(&module_mutex);
1718         fbcon = find_module(name);
1719         mutex_unlock(&module_mutex);
1720
1721         if (!fbcon)
1722                 request_module_nowait(name);
1723         return 0;
1724 }
1725
1726 module_init(drm_fb_helper_modinit);
1727 #endif