1 /* $NetBSD: drm_fb_helper.h,v 1.14 2021/12/19 10:46:44 riastradh Exp $ */
2
3 /*
4 * Copyright (c) 2006-2009 Red Hat Inc.
5 * Copyright (c) 2006-2008 Intel Corporation
6 * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
7 *
8 * DRM framebuffer helper functions
9 *
10 * Permission to use, copy, modify, distribute, and sell this software and its
11 * documentation for any purpose is hereby granted without fee, provided that
12 * the above copyright notice appear in all copies and that both that copyright
13 * notice and this permission notice appear in supporting documentation, and
14 * that the name of the copyright holders not be used in advertising or
15 * publicity pertaining to distribution of the software without specific,
16 * written prior permission. The copyright holders make no representations
17 * about the suitability of this software for any purpose. It is provided "as
18 * is" without express or implied warranty.
19 *
20 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
21 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
22 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
23 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
24 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
25 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
26 * OF THIS SOFTWARE.
27 *
28 * Authors:
29 * Dave Airlie <airlied@linux.ie>
30 * Jesse Barnes <jesse.barnes@intel.com>
31 */
32 #ifndef DRM_FB_HELPER_H
33 #define DRM_FB_HELPER_H
34
35 struct apertures_struct;
36 struct drm_fb_helper;
37
38 #include <drm/drm_client.h>
39 #include <drm/drm_crtc.h>
40 #include <drm/drm_device.h>
41 #include <linux/kgdb.h>
42 #include <linux/vgaarb.h>
43
44 #ifdef __NetBSD__
45 #include <sys/device_if.h>
46 #endif
47
48 enum mode_set_atomic {
49 LEAVE_ATOMIC_MODE_SET,
50 ENTER_ATOMIC_MODE_SET,
51 };
52
53 /**
54 * struct drm_fb_helper_surface_size - describes fbdev size and scanout surface size
55 * @fb_width: fbdev width
56 * @fb_height: fbdev height
57 * @surface_width: scanout buffer width
58 * @surface_height: scanout buffer height
59 * @surface_bpp: scanout buffer bpp
60 * @surface_depth: scanout buffer depth
61 *
62 * Note that the scanout surface width/height may be larger than the fbdev
63 * width/height. In case of multiple displays, the scanout surface is sized
64 * according to the largest width/height (so it is large enough for all CRTCs
65 * to scanout). But the fbdev width/height is sized to the minimum width/
66 * height of all the displays. This ensures that fbcon fits on the smallest
67 * of the attached displays. fb_width/fb_height is used by
68 * drm_fb_helper_fill_info() to fill out the &fb_info.var structure.
69 */
70 struct drm_fb_helper_surface_size {
71 u32 fb_width;
72 u32 fb_height;
73 u32 surface_width;
74 u32 surface_height;
75 u32 surface_bpp;
76 u32 surface_depth;
77 };
78
79 /**
80 * struct drm_fb_helper_funcs - driver callbacks for the fbdev emulation library
81 *
82 * Driver callbacks used by the fbdev emulation helper library.
83 */
84 struct drm_fb_helper_funcs {
85 /**
86 * @fb_probe:
87 *
88 * Driver callback to allocate and initialize the fbdev info structure.
89 * Furthermore it also needs to allocate the DRM framebuffer used to
90 * back the fbdev.
91 *
92 * This callback is mandatory.
93 *
94 * RETURNS:
95 *
96 * The driver should return 0 on success and a negative error code on
97 * failure.
98 */
99 int (*fb_probe)(struct drm_fb_helper *helper,
100 struct drm_fb_helper_surface_size *sizes);
101 };
102
103 /**
104 * struct drm_fb_helper - main structure to emulate fbdev on top of KMS
105 * @fb: Scanout framebuffer object
106 * @dev: DRM device
107 * @funcs: driver callbacks for fb helper
108 * @fbdev: emulated fbdev device info struct
109 * @pseudo_palette: fake palette of 16 colors
110 * @dirty_clip: clip rectangle used with deferred_io to accumulate damage to
111 * the screen buffer
112 * @dirty_lock: spinlock protecting @dirty_clip
113 * @dirty_work: worker used to flush the framebuffer
114 * @resume_work: worker used during resume if the console lock is already taken
115 *
116 * This is the main structure used by the fbdev helpers. Drivers supporting
117 * fbdev emulation should embedded this into their overall driver structure.
118 * Drivers must also fill out a &struct drm_fb_helper_funcs with a few
119 * operations.
120 */
121 struct drm_fb_helper {
122 /**
123 * @client:
124 *
125 * DRM client used by the generic fbdev emulation.
126 */
127 struct drm_client_dev client;
128
129 /**
130 * @buffer:
131 *
132 * Framebuffer used by the generic fbdev emulation.
133 */
134 struct drm_client_buffer *buffer;
135
136 struct drm_framebuffer *fb;
137 struct drm_device *dev;
138 const struct drm_fb_helper_funcs *funcs;
139 #ifdef __NetBSD__ /* XXX fb info */
140 device_t fbdev;
141 #else
142 struct fb_info *fbdev;
143 #endif
144 u32 pseudo_palette[17];
145 struct drm_clip_rect dirty_clip;
146 spinlock_t dirty_lock;
147 struct work_struct dirty_work;
148 struct work_struct resume_work;
149
150 /**
151 * @lock:
152 *
153 * Top-level FBDEV helper lock. This protects all internal data
154 * structures and lists, such as @connector_info and @crtc_info.
155 *
156 * FIXME: fbdev emulation locking is a mess and long term we want to
157 * protect all helper internal state with this lock as well as reduce
158 * core KMS locking as much as possible.
159 */
160 struct mutex lock;
161
162 /**
163 * @kernel_fb_list:
164 *
165 * Entry on the global kernel_fb_helper_list, used for kgdb entry/exit.
166 */
167 struct list_head kernel_fb_list;
168
169 /**
170 * @delayed_hotplug:
171 *
172 * A hotplug was received while fbdev wasn't in control of the DRM
173 * device, i.e. another KMS master was active. The output configuration
174 * needs to be reprobe when fbdev is in control again.
175 */
176 bool delayed_hotplug;
177
178 /**
179 * @deferred_setup:
180 *
181 * If no outputs are connected (disconnected or unknown) the FB helper
182 * code will defer setup until at least one of the outputs shows up.
183 * This field keeps track of the status so that setup can be retried
184 * at every hotplug event until it succeeds eventually.
185 *
186 * Protected by @lock.
187 */
188 bool deferred_setup;
189
190 /**
191 * @preferred_bpp:
192 *
193 * Temporary storage for the driver's preferred BPP setting passed to
194 * FB helper initialization. This needs to be tracked so that deferred
195 * FB helper setup can pass this on.
196 *
197 * See also: @deferred_setup
198 */
199 int preferred_bpp;
200 };
201
202 static inline struct drm_fb_helper *
drm_fb_helper_from_client(struct drm_client_dev * client)203 drm_fb_helper_from_client(struct drm_client_dev *client)
204 {
205 return container_of(client, struct drm_fb_helper, client);
206 }
207
208 /**
209 * define DRM_FB_HELPER_DEFAULT_OPS - helper define for drm drivers
210 *
211 * Helper define to register default implementations of drm_fb_helper
212 * functions. To be used in struct fb_ops of drm drivers.
213 */
214 #define DRM_FB_HELPER_DEFAULT_OPS \
215 .fb_check_var = drm_fb_helper_check_var, \
216 .fb_set_par = drm_fb_helper_set_par, \
217 .fb_setcmap = drm_fb_helper_setcmap, \
218 .fb_blank = drm_fb_helper_blank, \
219 .fb_pan_display = drm_fb_helper_pan_display, \
220 .fb_debug_enter = drm_fb_helper_debug_enter, \
221 .fb_debug_leave = drm_fb_helper_debug_leave, \
222 .fb_ioctl = drm_fb_helper_ioctl
223
224 #ifdef CONFIG_DRM_FBDEV_EMULATION
225 void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
226 const struct drm_fb_helper_funcs *funcs);
227 int drm_fb_helper_init(struct drm_device *dev,
228 struct drm_fb_helper *helper, int max_conn);
229 void drm_fb_helper_fini(struct drm_fb_helper *helper);
230 #ifndef __NetBSD__ /* XXX fb info */
231 int drm_fb_helper_blank(int blank, struct fb_info *info);
232 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
233 struct fb_info *info);
234 int drm_fb_helper_set_par(struct fb_info *info);
235 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
236 struct fb_info *info);
237 #endif
238
239 int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper);
240
241 #ifndef __NetBSD__ /* XXX fb info */
242 struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper);
243 #endif
244 void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper);
245 #ifndef __NetBSD__ /* XXX fb info */
246 void drm_fb_helper_fill_info(struct fb_info *info,
247 struct drm_fb_helper *fb_helper,
248 struct drm_fb_helper_surface_size *sizes);
249
250 void drm_fb_helper_deferred_io(struct fb_info *info,
251 struct list_head *pagelist);
252
253 ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
254 size_t count, loff_t *ppos);
255 ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf,
256 size_t count, loff_t *ppos);
257
258 void drm_fb_helper_sys_fillrect(struct fb_info *info,
259 const struct fb_fillrect *rect);
260 void drm_fb_helper_sys_copyarea(struct fb_info *info,
261 const struct fb_copyarea *area);
262 void drm_fb_helper_sys_imageblit(struct fb_info *info,
263 const struct fb_image *image);
264
265 void drm_fb_helper_cfb_fillrect(struct fb_info *info,
266 const struct fb_fillrect *rect);
267 void drm_fb_helper_cfb_copyarea(struct fb_info *info,
268 const struct fb_copyarea *area);
269 void drm_fb_helper_cfb_imageblit(struct fb_info *info,
270 const struct fb_image *image);
271 #endif
272
273 void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend);
274 void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
275 bool suspend);
276
277 #ifndef __NetBSD__ /* XXX fb cmap */
278 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info);
279
280 int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
281 unsigned long arg);
282 #endif
283
284 int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper);
285 int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel);
286 int drm_fb_helper_debug_enter_fb(struct drm_fb_helper *);
287 int drm_fb_helper_debug_leave_fb(struct drm_fb_helper *);
288
289 void drm_fb_helper_lastclose(struct drm_device *dev);
290 void drm_fb_helper_output_poll_changed(struct drm_device *dev);
291
292 int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp);
293 #else
drm_fb_helper_prepare(struct drm_device * dev,struct drm_fb_helper * helper,const struct drm_fb_helper_funcs * funcs)294 static inline void drm_fb_helper_prepare(struct drm_device *dev,
295 struct drm_fb_helper *helper,
296 const struct drm_fb_helper_funcs *funcs)
297 {
298 }
299
drm_fb_helper_init(struct drm_device * dev,struct drm_fb_helper * helper,int max_conn)300 static inline int drm_fb_helper_init(struct drm_device *dev,
301 struct drm_fb_helper *helper,
302 int max_conn)
303 {
304 /* So drivers can use it to free the struct */
305 helper->dev = dev;
306 dev->fb_helper = helper;
307
308 return 0;
309 }
310
drm_fb_helper_fini(struct drm_fb_helper * helper)311 static inline void drm_fb_helper_fini(struct drm_fb_helper *helper)
312 {
313 if (helper && helper->dev)
314 helper->dev->fb_helper = NULL;
315 }
316
drm_fb_helper_blank(int blank,struct fb_info * info)317 static inline int drm_fb_helper_blank(int blank, struct fb_info *info)
318 {
319 return 0;
320 }
321
drm_fb_helper_pan_display(struct fb_var_screeninfo * var,struct fb_info * info)322 static inline int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
323 struct fb_info *info)
324 {
325 return 0;
326 }
327
drm_fb_helper_set_par(struct fb_info * info)328 static inline int drm_fb_helper_set_par(struct fb_info *info)
329 {
330 return 0;
331 }
332
drm_fb_helper_check_var(struct fb_var_screeninfo * var,struct fb_info * info)333 static inline int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
334 struct fb_info *info)
335 {
336 return 0;
337 }
338
339 static inline int
drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper * fb_helper)340 drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
341 {
342 return 0;
343 }
344
345 static inline struct fb_info *
drm_fb_helper_alloc_fbi(struct drm_fb_helper * fb_helper)346 drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper)
347 {
348 return NULL;
349 }
350
drm_fb_helper_unregister_fbi(struct drm_fb_helper * fb_helper)351 static inline void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper)
352 {
353 }
354
355 static inline void
drm_fb_helper_fill_info(struct fb_info * info,struct drm_fb_helper * fb_helper,struct drm_fb_helper_surface_size * sizes)356 drm_fb_helper_fill_info(struct fb_info *info,
357 struct drm_fb_helper *fb_helper,
358 struct drm_fb_helper_surface_size *sizes)
359 {
360 }
361
drm_fb_helper_setcmap(struct fb_cmap * cmap,struct fb_info * info)362 static inline int drm_fb_helper_setcmap(struct fb_cmap *cmap,
363 struct fb_info *info)
364 {
365 return 0;
366 }
367
drm_fb_helper_ioctl(struct fb_info * info,unsigned int cmd,unsigned long arg)368 static inline int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
369 unsigned long arg)
370 {
371 return 0;
372 }
373
drm_fb_helper_deferred_io(struct fb_info * info,struct list_head * pagelist)374 static inline void drm_fb_helper_deferred_io(struct fb_info *info,
375 struct list_head *pagelist)
376 {
377 }
378
drm_fb_helper_defio_init(struct drm_fb_helper * fb_helper)379 static inline int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper)
380 {
381 return -ENODEV;
382 }
383
drm_fb_helper_sys_read(struct fb_info * info,char __user * buf,size_t count,loff_t * ppos)384 static inline ssize_t drm_fb_helper_sys_read(struct fb_info *info,
385 char __user *buf, size_t count,
386 loff_t *ppos)
387 {
388 return -ENODEV;
389 }
390
drm_fb_helper_sys_write(struct fb_info * info,const char __user * buf,size_t count,loff_t * ppos)391 static inline ssize_t drm_fb_helper_sys_write(struct fb_info *info,
392 const char __user *buf,
393 size_t count, loff_t *ppos)
394 {
395 return -ENODEV;
396 }
397
drm_fb_helper_sys_fillrect(struct fb_info * info,const struct fb_fillrect * rect)398 static inline void drm_fb_helper_sys_fillrect(struct fb_info *info,
399 const struct fb_fillrect *rect)
400 {
401 }
402
drm_fb_helper_sys_copyarea(struct fb_info * info,const struct fb_copyarea * area)403 static inline void drm_fb_helper_sys_copyarea(struct fb_info *info,
404 const struct fb_copyarea *area)
405 {
406 }
407
drm_fb_helper_sys_imageblit(struct fb_info * info,const struct fb_image * image)408 static inline void drm_fb_helper_sys_imageblit(struct fb_info *info,
409 const struct fb_image *image)
410 {
411 }
412
drm_fb_helper_cfb_fillrect(struct fb_info * info,const struct fb_fillrect * rect)413 static inline void drm_fb_helper_cfb_fillrect(struct fb_info *info,
414 const struct fb_fillrect *rect)
415 {
416 }
417
drm_fb_helper_cfb_copyarea(struct fb_info * info,const struct fb_copyarea * area)418 static inline void drm_fb_helper_cfb_copyarea(struct fb_info *info,
419 const struct fb_copyarea *area)
420 {
421 }
422
drm_fb_helper_cfb_imageblit(struct fb_info * info,const struct fb_image * image)423 static inline void drm_fb_helper_cfb_imageblit(struct fb_info *info,
424 const struct fb_image *image)
425 {
426 }
427
drm_fb_helper_set_suspend(struct drm_fb_helper * fb_helper,bool suspend)428 static inline void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper,
429 bool suspend)
430 {
431 }
432
433 static inline void
drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper * fb_helper,bool suspend)434 drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper, bool suspend)
435 {
436 }
437
drm_fb_helper_hotplug_event(struct drm_fb_helper * fb_helper)438 static inline int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
439 {
440 return 0;
441 }
442
drm_fb_helper_initial_config(struct drm_fb_helper * fb_helper,int bpp_sel)443 static inline int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper,
444 int bpp_sel)
445 {
446 return 0;
447 }
448
drm_fb_helper_debug_enter(struct fb_info * info)449 static inline int drm_fb_helper_debug_enter(struct fb_info *info)
450 {
451 return 0;
452 }
453
drm_fb_helper_debug_leave(struct fb_info * info)454 static inline int drm_fb_helper_debug_leave(struct fb_info *info)
455 {
456 return 0;
457 }
458
drm_fb_helper_lastclose(struct drm_device * dev)459 static inline void drm_fb_helper_lastclose(struct drm_device *dev)
460 {
461 }
462
drm_fb_helper_output_poll_changed(struct drm_device * dev)463 static inline void drm_fb_helper_output_poll_changed(struct drm_device *dev)
464 {
465 }
466
467 static inline int
drm_fbdev_generic_setup(struct drm_device * dev,unsigned int preferred_bpp)468 drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
469 {
470 return 0;
471 }
472
473 #endif
474
475 /* TODO: There's a todo entry to remove these three */
476 static inline int
drm_fb_helper_single_add_all_connectors(struct drm_fb_helper * fb_helper)477 drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
478 {
479 return 0;
480 }
481
482 static inline int
drm_fb_helper_add_one_connector(struct drm_fb_helper * fb_helper,struct drm_connector * connector)483 drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper,
484 struct drm_connector *connector)
485 {
486 return 0;
487 }
488
489 static inline int
drm_fb_helper_remove_one_connector(struct drm_fb_helper * fb_helper,struct drm_connector * connector)490 drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
491 struct drm_connector *connector)
492 {
493 return 0;
494 }
495
496 /**
497 * drm_fb_helper_remove_conflicting_framebuffers - remove firmware-configured framebuffers
498 * @a: memory range, users of which are to be removed
499 * @name: requesting driver name
500 * @primary: also kick vga16fb if present
501 *
502 * This function removes framebuffer devices (initialized by firmware/bootloader)
503 * which use memory range described by @a. If @a is NULL all such devices are
504 * removed.
505 */
506 static inline int
drm_fb_helper_remove_conflicting_framebuffers(struct apertures_struct * a,const char * name,bool primary)507 drm_fb_helper_remove_conflicting_framebuffers(struct apertures_struct *a,
508 const char *name, bool primary)
509 {
510 #if IS_REACHABLE(CONFIG_FB)
511 return remove_conflicting_framebuffers(a, name, primary);
512 #else
513 return 0;
514 #endif
515 }
516
517 /**
518 * drm_fb_helper_remove_conflicting_pci_framebuffers - remove firmware-configured framebuffers for PCI devices
519 * @pdev: PCI device
520 * @name: requesting driver name
521 *
522 * This function removes framebuffer devices (eg. initialized by firmware)
523 * using memory range configured for any of @pdev's memory bars.
524 *
525 * The function assumes that PCI device with shadowed ROM drives a primary
526 * display and so kicks out vga16fb.
527 */
528 static inline int
drm_fb_helper_remove_conflicting_pci_framebuffers(struct pci_dev * pdev,const char * name)529 drm_fb_helper_remove_conflicting_pci_framebuffers(struct pci_dev *pdev,
530 const char *name)
531 {
532 int ret = 0;
533
534 /*
535 * WARNING: Apparently we must kick fbdev drivers before vgacon,
536 * otherwise the vga fbdev driver falls over.
537 */
538 #if IS_REACHABLE(CONFIG_FB)
539 ret = remove_conflicting_pci_framebuffers(pdev, name);
540 #endif
541 if (ret == 0)
542 ret = vga_remove_vgacon(pdev);
543 return ret;
544 }
545
546 #endif
547