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