diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 7037ba180a9..fac9761bc6c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -3692,6 +3692,26 @@ int amdgpu_device_init(struct amdgpu_device *adev, goto release_ras_con; } +#ifdef __FreeBSD__ + /* + * After amdgpu_device_ip_init(), gmc.aper_base and gmc.aper_size are + * set, and mode_config.fb_base was initialized from gmc.aper_base. + * Therefore, we can register the fictitious memory range. + * + * This was handled in register_framebuffer() in the past, based on the + * values of info->apertures->ranges[0]. However, the `amdgpu` driver + * stopped setting them when it got rid of its specific framebuffer + * initialization to use the generic drm_fb_helper code. + * + * We can't do this in register_framebuffer() anymore because the + * values passed to register_fictitious_range() below are unavailable + * from a generic structure set by both drivers. + */ + register_fictitious_range( + adev_to_drm(adev)->mode_config.fb_base, + adev->gmc.aper_size); +#endif + amdgpu_fence_driver_hw_init(adev); dev_info(adev->dev, @@ -3876,6 +3896,12 @@ void amdgpu_device_fini_hw(struct amdgpu_device *adev) amdgpu_fbdev_fini(adev); +#ifdef __FreeBSD__ + unregister_fictitious_range( + adev_to_drm(adev)->mode_config.fb_base, + adev->gmc.aper_size); +#endif + amdgpu_device_ip_fini_early(adev); amdgpu_irq_fini_hw(adev); @@ -3919,10 +3945,6 @@ void amdgpu_device_fini_sw(struct amdgpu_device *adev) amdgpu_discovery_fini(adev); kfree(adev->pci_state); - -#ifdef __FreeBSD__ - vt_unfreeze_main_vd(); -#endif } /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c index a4add004828..cd0acbea75d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c @@ -46,7 +46,6 @@ the helper contains a pointer to amdgpu framebuffer baseclass. */ -#ifdef __linux__ static int amdgpufb_open(struct fb_info *info, int user) { @@ -69,18 +68,15 @@ amdgpufb_release(struct fb_info *info, int user) pm_runtime_put_autosuspend(fb_helper->dev->dev); return 0; } -#endif /* __linux__ */ static const struct fb_ops amdgpufb_ops = { .owner = THIS_MODULE, DRM_FB_HELPER_DEFAULT_OPS, -#ifdef __linux__ .fb_open = amdgpufb_open, .fb_release = amdgpufb_release, .fb_fillrect = drm_fb_helper_cfb_fillrect, .fb_copyarea = drm_fb_helper_cfb_copyarea, .fb_imageblit = drm_fb_helper_cfb_imageblit, -#endif }; diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 3a9ed6e97ef..b4f5edbca65 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -169,7 +169,11 @@ int drm_fb_helper_debug_enter(struct fb_info *info) continue; funcs = mode_set->crtc->helper_private; +#ifdef __linux__ if (funcs->mode_set_base_atomic == NULL) +#elif defined(__FreeBSD__) + if (funcs == NULL || funcs->mode_set_base_atomic == NULL) +#endif continue; if (drm_drv_uses_atomic_modeset(mode_set->crtc->dev)) @@ -332,7 +336,6 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) mutex_unlock(&fb_helper->lock); } -#ifdef __linux__ /** * drm_fb_helper_blank - implementation for &fb_ops.fb_blank * @blank: desired blanking state @@ -368,7 +371,6 @@ int drm_fb_helper_blank(int blank, struct fb_info *info) return 0; } EXPORT_SYMBOL(drm_fb_helper_blank); -#endif static void drm_fb_helper_resume_worker(struct work_struct *work) { @@ -590,9 +592,7 @@ struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper) } fb_helper->fbdev = info; -#ifdef __linux__ info->skip_vt_switch = true; -#endif return info; @@ -696,6 +696,13 @@ static void drm_fb_helper_damage(struct fb_info *info, u32 x, u32 y, clip->y2 = max_t(u32, clip->y2, y + height); spin_unlock_irqrestore(&helper->damage_lock, flags); +#ifdef __FreeBSD__ + if (kdb_active || KERNEL_PANICKED()) { + drm_fb_helper_damage_work(&helper->damage_work); + return; + } +#endif + schedule_work(&helper->damage_work); } @@ -735,6 +742,7 @@ void drm_fb_helper_deferred_io(struct fb_info *info, } } EXPORT_SYMBOL(drm_fb_helper_deferred_io); +#endif /* defined(__linux__) */ /** * drm_fb_helper_sys_read - wrapper around fb_sys_read @@ -863,7 +871,6 @@ void drm_fb_helper_cfb_imageblit(struct fb_info *info, drm_fb_helper_damage(info, image->dx, image->dy, image->width, image->height); } EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit); -#endif /* __linux__ */ /** * drm_fb_helper_set_suspend - wrapper around fb_set_suspend @@ -927,7 +934,6 @@ void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper, } EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked); -#ifdef __linux__ static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info) { u32 *palette = (u32 *)info->pseudo_palette; @@ -1168,7 +1174,6 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) return ret; } EXPORT_SYMBOL(drm_fb_helper_setcmap); -#endif /* __linux__ */ /** * drm_fb_helper_ioctl - legacy ioctl implementation @@ -1237,7 +1242,6 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, } EXPORT_SYMBOL(drm_fb_helper_ioctl); -#ifdef __linux__ static bool drm_fb_pixel_format_equal(const struct fb_var_screeninfo *var_1, const struct fb_var_screeninfo *var_2) { @@ -1330,12 +1334,10 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, if (in_dbg_master()) return -EINVAL; -#ifdef __linux__ if (var->pixclock != 0) { drm_dbg_kms(dev, "fbdev emulation doesn't support changing the pixel clock, value of pixclock is ignored\n"); var->pixclock = 0; } -#endif if ((drm_format_info_block_width(fb->format, 0) > 1) || (drm_format_info_block_height(fb->format, 0) > 1)) @@ -1387,7 +1389,6 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, return 0; } EXPORT_SYMBOL(drm_fb_helper_check_var); -#endif /* __linux__ */ /** * drm_fb_helper_set_par - implementation for &fb_ops.fb_set_par @@ -1406,7 +1407,6 @@ int drm_fb_helper_set_par(struct fb_info *info) if (oops_in_progress) return -EBUSY; -#ifdef __linux__ if (var->pixclock != 0) { drm_err(fb_helper->dev, "PIXEL CLOCK SET\n"); return -EINVAL; @@ -1428,18 +1428,18 @@ int drm_fb_helper_set_par(struct fb_info *info) * commit function, which ensures that we never steal the display from * an active drm master. */ +#ifdef __linux__ force = var->activate & FB_ACTIVATE_KD_TEXT; - - __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force); #elif defined(__FreeBSD__) - drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper); + force = false; #endif + __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force); + return 0; } EXPORT_SYMBOL(drm_fb_helper_set_par); -#ifdef __linux__ static void pan_set(struct drm_fb_helper *fb_helper, int x, int y) { struct drm_mode_set *mode_set; @@ -1451,12 +1451,10 @@ static void pan_set(struct drm_fb_helper *fb_helper, int x, int y) } mutex_unlock(&fb_helper->client.modeset_mutex); } -#endif static int pan_display_atomic(struct fb_var_screeninfo *var, struct fb_info *info) { -#ifdef __linux__ struct drm_fb_helper *fb_helper = info->par; int ret; @@ -1470,15 +1468,11 @@ static int pan_display_atomic(struct fb_var_screeninfo *var, pan_set(fb_helper, info->var.xoffset, info->var.yoffset); return ret; -#elif defined(__FreeBSD__) - return 0; -#endif } static int pan_display_legacy(struct fb_var_screeninfo *var, struct fb_info *info) { -#ifdef __linux__ struct drm_fb_helper *fb_helper = info->par; struct drm_client_dev *client = &fb_helper->client; struct drm_mode_set *modeset; @@ -1502,9 +1496,6 @@ static int pan_display_legacy(struct fb_var_screeninfo *var, mutex_unlock(&client->modeset_mutex); return ret; -#elif defined(__FreeBSD__) - return 0; -#endif } /** @@ -1726,7 +1717,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, static void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, uint32_t depth) { -#ifdef __linux__ info->fix.type = FB_TYPE_PACKED_PIXELS; info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; @@ -1737,13 +1727,8 @@ static void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, info->fix.ypanstep = 1; /* doing it in hw */ info->fix.ywrapstep = 0; info->fix.accel = FB_ACCEL_NONE; -#endif info->fix.line_length = pitch; -#ifdef __FreeBSD__ - info->fbio.fb_stride = pitch; -#endif - return; } static void drm_fb_helper_fill_var(struct fb_info *info, @@ -1755,29 +1740,18 @@ static void drm_fb_helper_fill_var(struct fb_info *info, WARN_ON((drm_format_info_block_width(fb->format, 0) > 1) || (drm_format_info_block_height(fb->format, 0) > 1)); info->pseudo_palette = fb_helper->pseudo_palette; -#ifdef __linux__ info->var.xres_virtual = fb->width; info->var.yres_virtual = fb->height; -#endif info->var.bits_per_pixel = fb->format->cpp[0] * 8; -#ifdef __linux__ info->var.accel_flags = FB_ACCELF_TEXT; info->var.xoffset = 0; info->var.yoffset = 0; info->var.activate = FB_ACTIVATE_NOW; drm_fb_helper_fill_pixel_fmt(&info->var, fb->format->depth); -#endif info->var.xres = fb_width; info->var.yres = fb_height; - -#ifdef __FreeBSD__ // fbio is BSD stuff - info->fbio.fb_name = device_get_nameunit(fb_helper->dev->dev->bsddev); - info->fbio.fb_width = fb->width; - info->fbio.fb_height = fb->height; - info->fbio.fb_depth = info->var.bits_per_pixel; -#endif } /** @@ -1804,7 +1778,6 @@ void drm_fb_helper_fill_info(struct fb_info *info, sizes->fb_width, sizes->fb_height); info->par = fb_helper; -#ifdef __linux__ /* * The DRM drivers fbdev emulation device name can be confusing if the * driver name also has a "drm" suffix on it. Leading to names such as @@ -1813,7 +1786,6 @@ void drm_fb_helper_fill_info(struct fb_info *info, */ snprintf(info->fix.id, sizeof(info->fix.id), "%sdrmfb", fb_helper->dev->driver->name); -#endif } EXPORT_SYMBOL(drm_fb_helper_fill_info); @@ -1854,16 +1826,13 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) /* use first connected connector for the physical dimensions */ if (connector->status == connector_status_connected) { -#ifdef __linux__ info->var.width = connector->display_info.width_mm; info->var.height = connector->display_info.height_mm; -#endif break; } } drm_connector_list_iter_end(&conn_iter); -#ifdef __linux__ switch (sw_rotations) { case DRM_MODE_ROTATE_0: info->fbcon_rotate_hint = FB_ROTATE_UR; @@ -1885,7 +1854,6 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) */ info->fbcon_rotate_hint = FB_ROTATE_UR; } -#endif } /* Note: Drops fb_helper->lock before returning. */ @@ -1918,7 +1886,6 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper, fb_helper->deferred_setup = false; info = fb_helper->fbdev; -#ifdef __linux__ info->var.pixclock = 0; /* Shamelessly allow physical address leaking to userspace */ #if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) @@ -1926,15 +1893,9 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper, #endif /* don't leak any physical addresses to userspace */ info->flags |= FBINFO_HIDE_SMEM_START; -#endif #ifdef __FreeBSD__ - info->fbio.fb_video_dev = device_get_parent(fb_helper->dev->dev->bsddev); - info->fbio.fb_bpp = bpp_sel; - info->fb_bsddev = fb_helper->dev->dev->bsddev; - struct vt_kms_softc *sc = (struct vt_kms_softc *)info->fbio.fb_priv; - if (sc) - sc->fb_helper = fb_helper; + info->fbio.fb_priv = fb_helper; #endif /* Need to drop locks to avoid recursive deadlock in @@ -2101,15 +2062,16 @@ void drm_fb_helper_output_poll_changed(struct drm_device *dev) } EXPORT_SYMBOL(drm_fb_helper_output_poll_changed); -#ifdef __linux__ /* @user: 1=userspace, 0=fbcon */ static int drm_fbdev_fb_open(struct fb_info *info, int user) { struct drm_fb_helper *fb_helper = info->par; +#ifdef __linux__ /* No need to take a ref for fbcon because it unbinds on unregister */ if (user && !try_module_get(fb_helper->dev->driver->fops->owner)) return -ENODEV; +#endif return 0; } @@ -2118,8 +2080,10 @@ static int drm_fbdev_fb_release(struct fb_info *info, int user) { struct drm_fb_helper *fb_helper = info->par; +#ifdef __linux__ if (user) module_put(fb_helper->dev->driver->fops->owner); +#endif return 0; } @@ -2133,8 +2097,10 @@ static void drm_fbdev_cleanup(struct drm_fb_helper *fb_helper) return; if (fbi) { +#ifdef __linux__ if (fbi->fbdefio) fb_deferred_io_cleanup(fbi); +#endif if (drm_fbdev_use_shadow_fb(fb_helper)) shadow = fbi->screen_buffer; } @@ -2385,10 +2351,12 @@ static const struct fb_ops drm_fbdev_fb_ops = { .fb_imageblit = drm_fbdev_fb_imageblit, }; +#ifdef __linux__ static struct fb_deferred_io drm_fbdev_defio = { .delay = HZ / 20, .deferred_io = drm_fb_helper_deferred_io, }; +#endif /* * This function uses the client API to create a framebuffer backed by a dumb buffer. @@ -2428,9 +2396,7 @@ static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, fbi->fbops = &drm_fbdev_fb_ops; fbi->screen_size = fb->height * fb->pitches[0]; -#ifdef __linux__ fbi->fix.smem_len = fbi->screen_size; -#endif drm_fb_helper_fill_info(fbi, fb_helper, sizes); @@ -2439,9 +2405,11 @@ static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, if (!fbi->screen_buffer) return -ENOMEM; +#ifdef __linux__ fbi->fbdefio = &drm_fbdev_defio; fb_deferred_io_init(fbi); +#endif } else { /* buffer is mapped for HW framebuffer */ ret = drm_client_buffer_vmap(fb_helper->buffer, &map); @@ -2611,4 +2579,3 @@ void drm_fbdev_generic_setup(struct drm_device *dev, drm_client_register(&fb_helper->client); } EXPORT_SYMBOL(drm_fbdev_generic_setup); -#endif /* __linux__*/ diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index 132dd21c3a7..c9732336567 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -138,11 +138,6 @@ void drm_modeset_lock_all(struct drm_device *dev) struct drm_modeset_acquire_ctx *ctx; int ret; -#ifdef __FreeBSD__ - if (oops_in_progress) - return; -#endif - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL | __GFP_NOFAIL); if (WARN_ON(!ctx)) return; @@ -196,11 +191,6 @@ void drm_modeset_unlock_all(struct drm_device *dev) struct drm_mode_config *config = &dev->mode_config; struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx; -#ifdef __FreeBSD__ - if (oops_in_progress) - return; -#endif - if (WARN_ON(!ctx)) return; @@ -225,10 +215,8 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) struct drm_crtc *crtc; /* Locking is currently fubar in the panic handler. */ -#ifdef __FreeBSD__ if (oops_in_progress) return; -#endif drm_for_each_crtc(crtc, dev) WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); @@ -250,11 +238,6 @@ EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, uint32_t flags) { -#ifdef __FreeBSD__ - if (oops_in_progress) - return; -#endif - memset(ctx, 0, sizeof(*ctx)); ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class); INIT_LIST_HEAD(&ctx->locked); @@ -270,11 +253,6 @@ EXPORT_SYMBOL(drm_modeset_acquire_init); */ void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx) { -#ifdef __FreeBSD__ - if (oops_in_progress) - return; -#endif - ww_acquire_fini(&ctx->ww_ctx); } EXPORT_SYMBOL(drm_modeset_acquire_fini); @@ -287,11 +265,6 @@ EXPORT_SYMBOL(drm_modeset_acquire_fini); */ void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx) { -#ifdef __FreeBSD__ - if (oops_in_progress) - return; -#endif - if (WARN_ON(ctx->contended)) __drm_stack_depot_print(ctx->stack_depot); @@ -312,11 +285,6 @@ static inline int modeset_lock(struct drm_modeset_lock *lock, { int ret; -#ifdef __FreeBSD__ - if (oops_in_progress) - return 0; -#endif - if (WARN_ON(ctx->contended)) __drm_stack_depot_print(ctx->stack_depot); @@ -371,11 +339,6 @@ int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx) { struct drm_modeset_lock *contended = ctx->contended; -#ifdef __FreeBSD__ - if (oops_in_progress) - return 0; -#endif - ctx->contended = NULL; ctx->stack_depot = 0; @@ -420,11 +383,6 @@ EXPORT_SYMBOL(drm_modeset_lock_init); int drm_modeset_lock(struct drm_modeset_lock *lock, struct drm_modeset_acquire_ctx *ctx) { -#ifdef __FreeBSD__ - if (oops_in_progress) - return 0; -#endif - if (ctx) return modeset_lock(lock, ctx, ctx->interruptible, false); @@ -444,10 +402,6 @@ EXPORT_SYMBOL(drm_modeset_lock); */ int drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock) { -#ifdef __FreeBSD__ - if (oops_in_progress) - return 0; -#endif return ww_mutex_lock_interruptible(&lock->mutex, NULL); } EXPORT_SYMBOL(drm_modeset_lock_single_interruptible); @@ -458,11 +412,6 @@ EXPORT_SYMBOL(drm_modeset_lock_single_interruptible); */ void drm_modeset_unlock(struct drm_modeset_lock *lock) { -#ifdef __FreeBSD__ - if (oops_in_progress) - return; -#endif - list_del_init(&lock->head); ww_mutex_unlock(&lock->mutex); } @@ -496,11 +445,6 @@ int drm_modeset_lock_all_ctx(struct drm_device *dev, struct drm_plane *plane; int ret; -#ifdef __FreeBSD__ - if (oops_in_progress) - return 0; -#endif - ret = drm_modeset_lock(&dev->mode_config.connection_mutex, ctx); if (ret) return ret; diff --git a/drivers/gpu/drm/drm_os_freebsd.c b/drivers/gpu/drm/drm_os_freebsd.c index e05555a2baa..2048ea5c6fe 100644 --- a/drivers/gpu/drm/drm_os_freebsd.c +++ b/drivers/gpu/drm/drm_os_freebsd.c @@ -18,6 +18,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include + #include #include #undef fb_info @@ -31,7 +33,7 @@ SYSCTL_NODE(_dev, OID_AUTO, drm, CTLFLAG_RW, 0, "DRM args (compat)"); SYSCTL_INT(_dev_drm, OID_AUTO, __drm_debug, CTLFLAG_RWTUN, &__drm_debug, 0, "drm debug flags (compat)"); SYSCTL_NODE(_hw, OID_AUTO, dri, CTLFLAG_RW, 0, "DRI args"); SYSCTL_INT(_hw_dri, OID_AUTO, __drm_debug, CTLFLAG_RWTUN, &__drm_debug, 0, "drm debug flags"); -static int skip_ddb; +int skip_ddb; SYSCTL_INT(_dev_drm, OID_AUTO, skip_ddb, CTLFLAG_RWTUN, &skip_ddb, 0, "go straight to dumping core (compat)"); SYSCTL_INT(_hw_dri, OID_AUTO, skip_ddb, CTLFLAG_RWTUN, &skip_ddb, 0, "go straight to dumping core"); #if defined(DRM_DEBUG_LOG_ALL) @@ -42,8 +44,6 @@ int drm_debug_persist = 0; SYSCTL_INT(_dev_drm, OID_AUTO, drm_debug_persist, CTLFLAG_RWTUN, &drm_debug_persist, 0, "keep drm debug flags post-load (compat)"); SYSCTL_INT(_hw_dri, OID_AUTO, drm_debug_persist, CTLFLAG_RWTUN, &drm_debug_persist, 0, "keep drm debug flags post-load"); -static bool already_switching_inside_panic = false; - static struct callout reset_debug_log_handle; static void @@ -79,62 +79,42 @@ sysctl_pci_id(SYSCTL_HANDLER_ARGS) return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); } -/* Framebuffer related code */ - -/* Call restore out of vt(9) locks. */ -void -vt_restore_fbdev_mode(void *arg, int pending) -{ - struct drm_fb_helper *fb_helper; - struct vt_kms_softc *sc; - struct mm_struct mm; - - sc = (struct vt_kms_softc *)arg; - fb_helper = sc->fb_helper; - linux_set_current(curthread); - if(!fb_helper) { - DRM_DEBUG("fb helper is null!\n"); - return; - } - drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper); -} - int -vt_kms_postswitch(void *arg) +register_fictitious_range(vm_paddr_t base, size_t size) { - struct vt_kms_softc *sc; + int ret; + struct apertures_struct *ap; - sc = (struct vt_kms_softc *)arg; + MPASS(base != 0); + MPASS(size != 0); - if (!kdb_active && !KERNEL_PANICKED()) { - taskqueue_enqueue(taskqueue_thread, &sc->fb_mode_task); + ap = alloc_apertures(1); + ap->ranges[0].base = base; + ap->ranges[0].size = size; + vt_freeze_main_vd(ap); + kfree(ap); - /* XXX the VT_ACTIVATE IOCTL must be synchronous */ - if (curthread->td_proc->p_pid != 0 && - taskqueue_member(taskqueue_thread, curthread) == 0) - taskqueue_drain(taskqueue_thread, &sc->fb_mode_task); - } else { -#ifdef DDB - db_trace_self_depth(10); - mdelay(1000); + ret = vm_phys_fictitious_reg_range(base, base + size, +#ifdef VM_MEMATTR_WRITE_COMBINING + VM_MEMATTR_WRITE_COMBINING +#else + VM_MEMATTR_UNCACHEABLE #endif - if (already_switching_inside_panic || skip_ddb) { - spinlock_enter(); - doadump(false); - EVENTHANDLER_INVOKE(shutdown_final, RB_NOSYNC); - } - linux_set_current(curthread); - if(!sc->fb_helper) { - DRM_DEBUG("fb helper is null!\n"); - return -1; - } - already_switching_inside_panic = true; - drm_fb_helper_restore_fbdev_mode_unlocked(sc->fb_helper); - already_switching_inside_panic = false; - } - return (0); + ); + MPASS(ret == 0); + + return (ret); +} + +void +unregister_fictitious_range(vm_paddr_t base, size_t size) +{ + vm_phys_fictitious_unreg_range(base, base + size); + vt_unfreeze_main_vd(); } +/* Framebuffer related code */ + int drm_dev_alias(struct device *ldev, struct drm_minor *minor, const char *minor_str) { diff --git a/drivers/gpu/drm/drm_os_freebsd.h b/drivers/gpu/drm/drm_os_freebsd.h index 29f499d9f10..d2bab7ed20e 100644 --- a/drivers/gpu/drm/drm_os_freebsd.h +++ b/drivers/gpu/drm/drm_os_freebsd.h @@ -20,11 +20,6 @@ __FBSDID("$FreeBSD$"); #define DRM_DEV_UID UID_ROOT #define DRM_DEV_GID GID_VIDEO -struct vt_kms_softc { - struct drm_fb_helper *fb_helper; - struct task fb_mode_task; -}; - /* XXXKIB what is the right code for the FreeBSD ? */ /* kib@ used ENXIO here -- dumbbell@ */ #define EREMOTEIO EIO @@ -35,13 +30,17 @@ struct vt_kms_softc { MALLOC_DECLARE(DRM_MEM_DRIVER); extern devclass_t drm_devclass; +extern int skip_ddb; struct drm_minor; int drm_dev_alias(struct device *dev, struct drm_minor *minor, const char *minor_str); void cancel_reset_debug_log(void); -void vt_restore_fbdev_mode(void *arg, int pending); -int vt_kms_postswitch(void *arg); +void vt_freeze_main_vd(struct apertures_struct *a); +void vt_unfreeze_main_vd(void); + +int register_fictitious_range(vm_paddr_t start, vm_paddr_t end); +void unregister_fictitious_range(vm_paddr_t start, vm_paddr_t end); #if 0 struct linux_fb_info; diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c index acc7de27d8a..608d0b1878a 100644 --- a/drivers/gpu/drm/i915/display/intel_fbdev.c +++ b/drivers/gpu/drm/i915/display/intel_fbdev.c @@ -74,7 +74,6 @@ static int intel_fbdev_set_par(struct fb_info *info) return ret; } -#ifdef __linux__ static int intel_fbdev_blank(int blank, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; @@ -103,19 +102,16 @@ static int intel_fbdev_pan_display(struct fb_var_screeninfo *var, return ret; } -#endif static const struct fb_ops intelfb_ops = { .owner = THIS_MODULE, DRM_FB_HELPER_DEFAULT_OPS, .fb_set_par = intel_fbdev_set_par, -#ifdef __linux__ .fb_fillrect = drm_fb_helper_cfb_fillrect, .fb_copyarea = drm_fb_helper_cfb_copyarea, .fb_imageblit = drm_fb_helper_cfb_imageblit, .fb_pan_display = intel_fbdev_pan_display, .fb_blank = intel_fbdev_blank, -#endif }; static int intelfb_alloc(struct drm_fb_helper *helper, @@ -269,6 +265,25 @@ static int intelfb_create(struct drm_fb_helper *helper, info->fix.smem_len = vma->node.size; } +#ifdef __FreeBSD__ + /* + * After the if() above, we can register the fictitious memory range + * based on the info->apertures->ranges[0] values. + * + * This was handled in register_framebuffer() in the past, also based + * on the values of info->apertures->ranges[0]. However, the `amdgpu` + * driver stopped setting them when it got rid of its specific + * framebuffer initialization to use the generic drm_fb_helper code. + * + * We can't do this in register_framebuffer() anymore because the + * values passed to register_fictitious_range() below are unavailable + * from a generic structure set by both drivers. + */ + register_fictitious_range( + info->apertures->ranges[0].base, + info->apertures->ranges[0].size); +#endif + vaddr = i915_vma_pin_iomap(vma); if (IS_ERR(vaddr)) { drm_err(&dev_priv->drm, @@ -318,6 +333,12 @@ static void intel_fbdev_destroy(struct intel_fbdev *ifbdev) * trying to rectify all the possible error paths leading here. */ +#ifdef __FreeBSD__ + unregister_fictitious_range( + ifbdev->helper.fbdev->apertures->ranges[0].base, + ifbdev->helper.fbdev->apertures->ranges[0].size); +#endif + drm_fb_helper_fini(&ifbdev->helper); if (ifbdev->vma) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c index e682f867699..ab46acbc088 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c @@ -410,12 +410,6 @@ static int i915_gem_init_stolen(struct intel_memory_region *mem) return 0; } -#ifdef __FreeBSD__ - DRM_INFO("Got stolen memory base 0x%x, size 0x%x\n", - intel_graphics_stolen_res.start, - resource_size(&intel_graphics_stolen_res)); -#endif - if (resource_size(&mem->region) == 0) return 0; diff --git a/drivers/gpu/drm/i915/i915_module.c b/drivers/gpu/drm/i915/i915_module.c index eb4313d9255..eaa9ce4cb3f 100644 --- a/drivers/gpu/drm/i915/i915_module.c +++ b/drivers/gpu/drm/i915/i915_module.c @@ -80,6 +80,17 @@ static int __init i915_init(void) { int err, i; +#ifdef __FreeBSD__ +#if defined(__amd64__) + intel_graphics_stolen_res = (struct linux_resource) + DEFINE_RES_MEM(intel_graphics_stolen_base, + intel_graphics_stolen_size); + DRM_INFO("Got Intel graphics stolen memory base 0x%x, size 0x%x\n", + intel_graphics_stolen_res.start, + resource_size(&intel_graphics_stolen_res)); +#endif +#endif + for (i = 0; i < ARRAY_SIZE(init_funcs); i++) { err = init_funcs[i].init(); if (err < 0) { diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c index 7d888460d67..8394eac8fb6 100644 --- a/drivers/gpu/drm/i915/i915_pci.c +++ b/drivers/gpu/drm/i915/i915_pci.c @@ -1256,6 +1256,5 @@ void i915_pci_unregister_driver(void) pci_unregister_driver(&i915_pci_driver); #elif defined(__FreeBSD__) linux_pci_unregister_drm_driver(&i915_pci_driver); - vt_unfreeze_main_vd(); #endif } diff --git a/drivers/gpu/drm/i915/intel_freebsd.c b/drivers/gpu/drm/i915/intel_freebsd.c index 94836150c13..1e2056ca580 100644 --- a/drivers/gpu/drm/i915/intel_freebsd.c +++ b/drivers/gpu/drm/i915/intel_freebsd.c @@ -158,15 +158,3 @@ linux_intel_gtt_insert_sg_entries(struct sg_table *st, unsigned int pg_start, intel_gtt_read_pte(pg_start + i - 1); } - -#if defined(__amd64__) -static void -intel_freebsd_init(void *arg __unused) -{ - /* Defined in $SYSDIR/x86/pci/pci_early_quirks.c */ - intel_graphics_stolen_res = (struct linux_resource) - DEFINE_RES_MEM(intel_graphics_stolen_base, - intel_graphics_stolen_size); -} -SYSINIT(intel_freebsd, SI_SUB_DRIVERS, SI_ORDER_ANY, intel_freebsd_init, NULL); -#endif diff --git a/drivers/gpu/drm/linux_fb.c b/drivers/gpu/drm/linux_fb.c index 55756766296..364fd3b092f 100644 --- a/drivers/gpu/drm/linux_fb.c +++ b/drivers/gpu/drm/linux_fb.c @@ -38,13 +38,10 @@ __FBSDID("$FreeBSD$"); #include #include -#include -#include -#include - #include -#include +#include "vt_drmfb.h" +#include #include #undef fb_info #include @@ -58,7 +55,7 @@ extern struct vt_device *main_vd; static int __unregister_framebuffer(struct linux_fb_info *fb_info); -static void +void vt_freeze_main_vd(struct apertures_struct *a) { struct fb_info *fb; @@ -70,7 +67,8 @@ vt_freeze_main_vd(struct apertures_struct *a) (strcmp(main_vd->vd_driver->vd_name, "efifb") == 0 || strcmp(main_vd->vd_driver->vd_name, "vbefb") == 0 || strcmp(main_vd->vd_driver->vd_name, "ofwfb") == 0 - || strcmp(main_vd->vd_driver->vd_name, "fb") == 0)) { + || strcmp(main_vd->vd_driver->vd_name, "fb") == 0 + || strcmp(main_vd->vd_driver->vd_name, "drmfb") == 0)) { fb = main_vd->vd_softc; for (i = 0; i < a->count; i++) { @@ -98,16 +96,28 @@ vt_unfreeze_main_vd(void) fb->fb_flags &= ~FB_FLAG_NOWRITE; } +/* Call restore out of vt(9) locks. */ +static void +vt_restore_fbdev_mode(void *arg, int pending) +{ + struct linux_fb_info *info; + + info = (struct linux_fb_info *)arg; + linux_set_current(curthread); + info->fbops->fb_set_par(info); +} + void -fb_info_print(struct fb_info *t) +fb_info_print(struct linux_fb_info *info) { printf("start FB_INFO:\n"); - printf("type=%d height=%d width=%d depth=%d\n", - t->fb_type, t->fb_height, t->fb_width, t->fb_depth); + printf("height=%d width=%d depth=%d\n", + info->var.yres, info->var.xres, info->var.bits_per_pixel); printf("pbase=0x%lx vbase=0x%lx\n", - t->fb_pbase, t->fb_vbase); - printf("name=%s flags=0x%x stride=%d bpp=%d\n", - t->fb_name, t->fb_flags, t->fb_stride, t->fb_bpp); + info->fix.smem_start, info->screen_base); + printf("name=%s id=%s flags=0x%x stride=%d\n", + info->fbio.fb_name, info->fix.id, info->fbio.fb_flags, + info->fix.line_length); printf("end FB_INFO\n"); } @@ -117,14 +127,9 @@ struct linux_fb_info * framebuffer_alloc(size_t size, struct device *dev) { struct linux_fb_info *info; - struct vt_kms_softc *sc; info = malloc(sizeof(*info) + size, LKPI_FB_MEM, M_WAITOK | M_ZERO); - sc = malloc(sizeof(*sc), LKPI_FB_MEM, M_WAITOK | M_ZERO); - TASK_INIT(&sc->fb_mode_task, 0, vt_restore_fbdev_mode, sc); - - info->fbio.fb_priv = sc; - info->fbio.enter = &vt_kms_postswitch; + TASK_INIT(&info->fb_mode_task, 0, vt_restore_fbdev_mode, info); if (size) info->par = info + 1; @@ -137,14 +142,9 @@ framebuffer_alloc(size_t size, struct device *dev) void framebuffer_release(struct linux_fb_info *info) { - struct vt_kms_softc *sc; - if (info == NULL) return; - if (info->fbio.fb_priv) - sc = info->fbio.fb_priv; kfree(info->apertures); - free(info->fbio.fb_priv, LKPI_FB_MEM); free(info, LKPI_FB_MEM); } @@ -195,23 +195,17 @@ static int __register_framebuffer(struct linux_fb_info *fb_info) { int i, err; + struct drm_fb_helper *fb_helper; - vt_freeze_main_vd(fb_info->apertures); - - MPASS(fb_info->apertures->ranges[0].base); - MPASS(fb_info->apertures->ranges[0].size); - vm_phys_fictitious_reg_range(fb_info->apertures->ranges[0].base, - fb_info->apertures->ranges[0].base + - fb_info->apertures->ranges[0].size, -#ifdef VM_MEMATTR_WRITE_COMBINING - VM_MEMATTR_WRITE_COMBINING); -#else - VM_MEMATTR_UNCACHEABLE); -#endif + fb_helper = (struct drm_fb_helper *)fb_info->fbio.fb_priv; + fb_info->fb_bsddev = fb_helper->dev->dev->bsddev; + fb_info->fbio.fb_video_dev = device_get_parent(fb_info->fb_bsddev); + fb_info->fbio.fb_name = device_get_nameunit(fb_info->fb_bsddev); fb_info->fbio.fb_type = FBTYPE_PCIMISC; fb_info->fbio.fb_height = fb_info->var.yres; fb_info->fbio.fb_width = fb_info->var.xres; + fb_info->fbio.fb_bpp = fb_info->var.bits_per_pixel; fb_info->fbio.fb_depth = fb_info->var.bits_per_pixel; fb_info->fbio.fb_cmsize = 0; fb_info->fbio.fb_stride = fb_info->fix.line_length; @@ -222,20 +216,21 @@ __register_framebuffer(struct linux_fb_info *fb_info) fb_info->fbio.fb_fbd_dev = device_add_child(fb_info->fb_bsddev, "fbd", device_get_unit(fb_info->fb_bsddev)); - /* tell vt_fb to initialize color map */ + /* tell vt_drmfb to initialize color map */ fb_info->fbio.fb_cmsize = 0; if (fb_info->fbio.fb_bpp == 0) { device_printf(fb_info->fbio.fb_fbd_dev, "fb_bpp not set, setting to 8\n"); fb_info->fbio.fb_bpp = 32; } - if ((err = vt_fb_attach(&fb_info->fbio)) != 0) { + if ((err = vt_drmfb_attach(&fb_info->fbio)) != 0) { switch (err) { case EEXIST: device_printf(fb_info->fbio.fb_fbd_dev, "not attached to vt(4) console; " "another device has precedence (err=%d)\n", err); + err = 0; break; default: device_printf(fb_info->fbio.fb_fbd_dev, @@ -244,7 +239,7 @@ __register_framebuffer(struct linux_fb_info *fb_info) } return (-err); } - fb_info_print(&fb_info->fbio); + fb_info_print(fb_info); return 0; } @@ -264,19 +259,18 @@ __unregister_framebuffer(struct linux_fb_info *fb_info) { int ret = 0; - vm_phys_fictitious_unreg_range(fb_info->apertures->ranges[0].base, - fb_info->apertures->ranges[0].base + - fb_info->apertures->ranges[0].size); + vt_drmfb_detach(&fb_info->fbio); + if (fb_info->fbio.fb_fbd_dev) { mtx_lock(&Giant); device_delete_child(fb_info->fb_bsddev, fb_info->fbio.fb_fbd_dev); mtx_unlock(&Giant); fb_info->fbio.fb_fbd_dev = NULL; } - vt_fb_detach(&fb_info->fbio); if (fb_info->fbops->fb_destroy) fb_info->fbops->fb_destroy(fb_info); + return 0; } @@ -322,3 +316,187 @@ linux_fb_get_options(const char *connector_name, char **option) return (*option != NULL ? 0 : -ENOENT); } + +/* + * Routines to write to the framebuffer. They are used to implement Linux' + * fbdev equivalent functions below. + * + * Copied from `sys/dev/vt/hw/fb/vt_fb.c`. + */ + +static void +fb_mem_wr1(struct linux_fb_info *info, uint32_t offset, uint8_t value) +{ + KASSERT( + (offset < info->screen_size), + ("Offset %#08x out of framebuffer size", offset)); + *(uint8_t *)(info->screen_base + offset) = value; +} + +static void +fb_mem_wr2(struct linux_fb_info *info, uint32_t offset, uint16_t value) +{ + KASSERT( + (offset < info->screen_size), + ("Offset %#08x out of framebuffer size", offset)); + *(uint16_t *)(info->screen_base + offset) = value; +} + +static void +fb_mem_wr4(struct linux_fb_info *info, uint32_t offset, uint32_t value) +{ + KASSERT( + (offset < info->screen_size), + ("Offset %#08x out of framebuffer size", offset)); + *(uint32_t *)(info->screen_base + offset) = value; +} + +static void +fb_setpixel(struct linux_fb_info *info, uint32_t x, uint32_t y, + uint32_t color) +{ + uint32_t bytes_per_pixel; + unsigned int offset; + + bytes_per_pixel = info->var.bits_per_pixel / 8; + offset = info->fix.line_length * y + x * bytes_per_pixel; + + KASSERT((info->screen_base != 0), ("Unmapped framebuffer")); + + switch (bytes_per_pixel) { + case 1: + fb_mem_wr1(info, offset, color); + break; + case 2: + fb_mem_wr2(info, offset, color); + break; + case 3: + fb_mem_wr1(info, offset, (color >> 16) & 0xff); + fb_mem_wr1(info, offset + 1, (color >> 8) & 0xff); + fb_mem_wr1(info, offset + 2, color & 0xff); + break; + case 4: + fb_mem_wr4(info, offset, color); + break; + default: + /* panic? */ + return; + } +} + +void +cfb_fillrect(struct linux_fb_info *info, const struct fb_fillrect *rect) +{ + uint32_t x, y; + + if (info->fbio.fb_flags & FB_FLAG_NOWRITE) + return; + + KASSERT( + (rect->rop == ROP_COPY), + ("`rect->rop=%u` is unsupported in cfb_fillrect()", rect->rop)); + + for (y = rect->dy; y < rect->dy + rect->height; ++y) { + for (x = rect->dx; x < rect->dx + rect->width; ++x) { + fb_setpixel(info, x, y, rect->color); + } + } +} + +void +cfb_copyarea(struct linux_fb_info *info, const struct fb_copyarea *area) +{ + panic("cfb_copyarea() not implemented"); +} + +void +cfb_imageblit(struct linux_fb_info *info, const struct fb_image *image) +{ + uint32_t x, y, width, height, xi, yi; + uint32_t bytes_per_img_line, bit, byte, color; + + if (info->fbio.fb_flags & FB_FLAG_NOWRITE) + return; + + KASSERT( + (image->depth == 1), + ("`image->depth=%u` is unsupported in cfb_imageblit()", + image->depth)); + + bytes_per_img_line = (image->width + 7) / 8; + + x = image->dx; + y = image->dy; + width = image->width; + height = image->height; + + if (x + width > info->var.xres) { + if (x >= info->var.xres) + return; + width = info->var.xres - x; + } + if (y + height > info->var.yres) { + if (y >= info->var.yres) + return; + height = info->var.yres - y; + } + + if (image->mask == NULL) { + for (yi = 0; yi < height; ++yi) { + for (xi = 0; xi < width; ++xi) { + byte = yi * bytes_per_img_line + xi / 8; + bit = 0x80 >> (xi % 8); + color = image->data[byte] & bit ? + image->fg_color : image->bg_color; + + fb_setpixel(info, x + xi, y + yi, color); + } + } + } else { + for (yi = 0; yi < height; ++yi) { + for (xi = 0; xi < width; ++xi) { + byte = yi * bytes_per_img_line + xi / 8; + bit = 0x80 >> (xi % 8); + if (image->mask[byte] & bit) { + color = image->fg_color; + + fb_setpixel(info, x + xi, y + yi, color); + } + } + } + } +} + +void +sys_fillrect(struct linux_fb_info *info, const struct fb_fillrect *rect) +{ + cfb_fillrect(info, rect); +} + +void +sys_copyarea(struct linux_fb_info *info, const struct fb_copyarea *area) +{ + cfb_copyarea(info, area); +} + +void +sys_imageblit(struct linux_fb_info *info, const struct fb_image *image) +{ + cfb_imageblit(info, image); +} + +ssize_t +fb_sys_read(struct linux_fb_info *info, char __user *buf, + size_t count, loff_t *ppos) +{ + panic("fb_sys_read() not implemented"); + return (0); +} + +ssize_t +fb_sys_write(struct linux_fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + panic("fb_sys_write() not implemented"); + return (0); +} diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index a650c926adb..c5587e7b568 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -48,7 +48,6 @@ struct radeon_fbdev { struct radeon_device *rdev; }; -#ifdef __linux__ static int radeonfb_open(struct fb_info *info, int user) { @@ -74,18 +73,15 @@ radeonfb_release(struct fb_info *info, int user) pm_runtime_put_autosuspend(rdev->ddev->dev); return 0; } -#endif /* __linux__ */ static const struct fb_ops radeonfb_ops = { .owner = THIS_MODULE, DRM_FB_HELPER_DEFAULT_OPS, -#ifdef __linux__ .fb_open = radeonfb_open, .fb_release = radeonfb_release, .fb_fillrect = drm_fb_helper_cfb_fillrect, .fb_copyarea = drm_fb_helper_cfb_copyarea, .fb_imageblit = drm_fb_helper_cfb_imageblit, -#endif }; @@ -284,6 +280,25 @@ static int radeonfb_create(struct drm_fb_helper *helper, info->apertures->ranges[0].base = rdev->ddev->mode_config.fb_base; info->apertures->ranges[0].size = rdev->mc.aper_size; +#ifdef __FreeBSD__ + /* + * We can register the fictitious memory range based on the + * info->apertures->ranges[0] values. + * + * This was handled in register_framebuffer() in the past, also based + * on the values of info->apertures->ranges[0]. However, the `amdgpu` + * driver stopped setting them when it got rid of its specific + * framebuffer initialization to use the generic drm_fb_helper code. + * + * We can't do this in register_framebuffer() anymore because the + * values passed to register_fictitious_range() below are unavailable + * from a generic structure set by both drivers. + */ + register_fictitious_range( + info->apertures->ranges[0].base, + info->apertures->ranges[0].size); +#endif + /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ if (info->screen_base == NULL) { @@ -314,6 +329,12 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb { struct drm_framebuffer *fb = &rfbdev->fb; +#ifdef __FreeBSD__ + unregister_fictitious_range( + rfbdev->helper.fbdev->apertures->ranges[0].base, + rfbdev->helper.fbdev->apertures->ranges[0].size); +#endif + drm_fb_helper_unregister_fbi(&rfbdev->helper); if (fb->obj[0]) { diff --git a/drivers/gpu/drm/vt_drmfb.c b/drivers/gpu/drm/vt_drmfb.c new file mode 100644 index 00000000000..33c9faa00b1 --- /dev/null +++ b/drivers/gpu/drm/vt_drmfb.c @@ -0,0 +1,373 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2013 The FreeBSD Foundation + * Copyright (c) 2023 Jean-Sébastien Pédron + * + * This initial software `sys/dev/vt/hw/vt_fb.c` was developed by Aleksandr + * Rybalko under sponsorship from the FreeBSD Foundation. + * This file is a copy of the initial file and is modified by Jean-Sébastien + * Pédron. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include + +#include + +/* + * `drm_fb_helper.h` redefines `fb_info` to be `linux_fb_info` to manage the + * name conflict between the Linux and FreeBSD structures, while avoiding a + * extensive rewrite and use of macros in the original drm_fb_helper.[ch] + * files. + * + * We need to undo this here because we use both structures. + */ +#undef fb_info + +#include + +#include "vt_drmfb.h" + +#define to_drm_fb_helper(fbio) ((struct drm_fb_helper *)fbio->fb_priv) +#define to_linux_fb_info(fbio) (to_drm_fb_helper(fbio)->fbdev) + +vd_init_t vt_drmfb_init; +vd_fini_t vt_drmfb_fini; +vd_blank_t vt_drmfb_blank; +vd_bitblt_bmp_t vt_drmfb_bitblt_bitmap; +vd_drawrect_t vt_drmfb_drawrect; +vd_setpixel_t vt_drmfb_setpixel; +vd_invalidate_text_t vt_drmfb_invalidate_text; +vd_postswitch_t vt_drmfb_postswitch; + +static struct vt_driver vt_drmfb_driver = { + .vd_name = "drmfb", + .vd_init = vt_drmfb_init, + .vd_fini = vt_drmfb_fini, + .vd_blank = vt_drmfb_blank, + /* + * .vd_bitblt_text is unset. + * + * We use the default implementation in vt(4) which copies the + * characters first and bitblt_bmp them in a second step outside of the + * vtbuf lock. Thus the `vd_bitblt_after_vtbuf_unlock` flag set below. + * We need this because vtbuf is a spin mutex and + * `vt_drmfb_bitblt_bitmap()` may sleep. + */ + .vd_bitblt_bmp = vt_drmfb_bitblt_bitmap, + .vd_drawrect = vt_drmfb_drawrect, + .vd_setpixel = vt_drmfb_setpixel, + .vd_invalidate_text = vt_drmfb_invalidate_text, + .vd_postswitch = vt_drmfb_postswitch, + .vd_priority = VD_PRIORITY_GENERIC+20, + .vd_suspend = vt_drmfb_suspend, + .vd_resume = vt_drmfb_resume, + + /* Use vt_fb implementation */ + .vd_fb_ioctl = vt_fb_ioctl, + .vd_fb_mmap = vt_fb_mmap, + + .vd_bitblt_after_vtbuf_unlock = true, +}; + +VT_DRIVER_DECLARE(vt_drmfb, vt_drmfb_driver); + +void +vt_drmfb_setpixel(struct vt_device *vd, int x, int y, term_color_t color) +{ + vt_drmfb_drawrect(vd, x, y, x, y, 1, color); +} + +void +vt_drmfb_drawrect( + struct vt_device *vd, + int x1, int y1, int x2, int y2, int fill, + term_color_t color) +{ + struct fb_info *fbio; + struct linux_fb_info *info; + struct fb_fillrect rect; + + fbio = vd->vd_softc; + info = to_linux_fb_info(fbio); + if (info->fbops->fb_fillrect == NULL) + return; + + KASSERT( + (x2 >= x1), + ("Invalid rectangle X coordinates passed to vd_drawrect: " + "x1=%d > x2=%d", x1, x2)); + KASSERT( + (y2 >= y1), + ("Invalid rectangle Y coordinates passed to vd_drawrect: " + "y1=%d > y2=%d", y1, y2)); + KASSERT( + (fill != 0), + ("`fill=0` argument to vd_drawrect unsupported in vt_drmfb")); + + rect.dx = x1; + rect.dy = y1; + rect.width = x2 - x1 + 1; + rect.height = y2 - y1 + 1; + rect.color = fbio->fb_cmap[color]; + rect.rop = ROP_COPY; + + info->fbops->fb_fillrect(info, &rect); +} + +void +vt_drmfb_blank(struct vt_device *vd, term_color_t color) +{ + struct fb_info *fbio; + struct linux_fb_info *info; + int x1, y1, x2, y2; + + fbio = vd->vd_softc; + info = to_linux_fb_info(fbio); + + x1 = info->var.xoffset; + y1 = info->var.yoffset; + x2 = info->var.xres - 1; + y2 = info->var.yres - 1; + + vt_drmfb_drawrect(vd, x1, y1, x2, y2, 1, color); +} + +void +vt_drmfb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw, + const uint8_t *pattern, const uint8_t *mask, + unsigned int width, unsigned int height, + unsigned int x, unsigned int y, term_color_t fg, term_color_t bg) +{ + struct fb_info *fbio; + struct linux_fb_info *info; + struct fb_image image; + + fbio = vd->vd_softc; + info = to_linux_fb_info(fbio); + if (info->fbops->fb_imageblit == NULL) + return; + + /* Bound by right and bottom edges. */ + if (y + height > vw->vw_draw_area.tr_end.tp_row) { + if (y >= vw->vw_draw_area.tr_end.tp_row) + return; + height = vw->vw_draw_area.tr_end.tp_row - y; + } + if (x + width > vw->vw_draw_area.tr_end.tp_col) { + if (x >= vw->vw_draw_area.tr_end.tp_col) + return; + width = vw->vw_draw_area.tr_end.tp_col - x; + } + + image.dx = x; + image.dy = y; + image.width = width; + image.height = height; + image.fg_color = fbio->fb_cmap[fg]; + image.bg_color = fbio->fb_cmap[bg]; + image.depth = 1; + image.data = pattern; + image.mask = mask; // Specific to FreeBSD to display the mouse pointer. + + if (!kdb_active && !KERNEL_PANICKED()) + linux_set_current(curthread); + + info->fbops->fb_imageblit(info, &image); +} + +void +vt_drmfb_postswitch(struct vt_device *vd) +{ + struct fb_info *fbio; + struct linux_fb_info *info; + + fbio = vd->vd_softc; + info = to_linux_fb_info(fbio); + + if (!kdb_active && !KERNEL_PANICKED()) { + taskqueue_enqueue(taskqueue_thread, &info->fb_mode_task); + + /* XXX the VT_ACTIVATE IOCTL must be synchronous */ + if (curthread->td_proc->p_pid != 0 && + taskqueue_member(taskqueue_thread, curthread) == 0) + taskqueue_drain(taskqueue_thread, &info->fb_mode_task); + } else { +#ifdef DDB + db_trace_self_depth(10); + mdelay(1000); +#endif + if (skip_ddb) { + spinlock_enter(); + doadump(false); + EVENTHANDLER_INVOKE(shutdown_final, RB_NOSYNC); + } + + if (vd->vd_grabwindow != NULL) { + if (info->fbops->fb_debug_enter) + info->fbops->fb_debug_enter(info); + } else { + if (info->fbops->fb_debug_leave) + info->fbops->fb_debug_leave(info); + } + } +} + +void +vt_drmfb_invalidate_text(struct vt_device *vd, const term_rect_t *area) +{ + unsigned int col, row; + size_t z; + + for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { + for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; + ++col) { + z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col; + if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * + PIXEL_WIDTH(VT_FB_MAX_WIDTH)) + continue; + if (vd->vd_drawn) + vd->vd_drawn[z] = 0; + if (vd->vd_drawnfg) + vd->vd_drawnfg[z] = 0; + if (vd->vd_drawnbg) + vd->vd_drawnbg[z] = 0; + if (vd->vd_pos_to_flush) + vd->vd_pos_to_flush[z] = true; + } + } +} + +static int +vt_drmfb_init_colors(struct fb_info *info) +{ + + switch (FBTYPE_GET_BPP(info)) { + case 8: + return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, + 0x7, 5, 0x7, 2, 0x3, 0)); + case 15: + return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, + 0x1f, 10, 0x1f, 5, 0x1f, 0)); + case 16: + return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, + 0x1f, 11, 0x3f, 5, 0x1f, 0)); + case 24: + case 32: /* Ignore alpha. */ + return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, + 0xff, 16, 0xff, 8, 0xff, 0)); + default: + return (1); + } +} + +int +vt_drmfb_init(struct vt_device *vd) +{ + struct fb_info *fbio; + u_int margin; + int bg, err; + term_color_t c; + + fbio = vd->vd_softc; + vd->vd_height = MIN(VT_FB_MAX_HEIGHT, fbio->fb_height); + margin = (fbio->fb_height - vd->vd_height) >> 1; + vd->vd_transpose = margin * fbio->fb_stride; + vd->vd_width = MIN(VT_FB_MAX_WIDTH, fbio->fb_width); + margin = (fbio->fb_width - vd->vd_width) >> 1; + vd->vd_transpose += margin * (fbio->fb_bpp / NBBY); + vd->vd_video_dev = fbio->fb_video_dev; + + if (fbio->fb_size == 0) + return (CN_DEAD); + + if (fbio->fb_pbase == 0 && fbio->fb_vbase == 0) + fbio->fb_flags |= FB_FLAG_NOMMAP; + + if (fbio->fb_cmsize <= 0) { + err = vt_drmfb_init_colors(fbio); + if (err) + return (CN_DEAD); + fbio->fb_cmsize = 16; + } + + c = TC_BLACK; + if (TUNABLE_INT_FETCH("teken.bg_color", &bg) != 0) { + if (bg == TC_WHITE) + bg |= TC_LIGHT; + c = bg; + } + + /* Clear the screen. */ + vd->vd_driver->vd_blank(vd, c); + + return (CN_INTERNAL); +} + +void +vt_drmfb_fini(struct vt_device *vd, void *softc) +{ + vd->vd_video_dev = NULL; +} + +int +vt_drmfb_attach(struct fb_info *fbio) +{ + int ret; + + ret = vt_allocate(&vt_drmfb_driver, fbio); + + return (ret); +} + +int +vt_drmfb_detach(struct fb_info *fbio) +{ + int ret; + + ret = vt_deallocate(&vt_drmfb_driver, fbio); + + return (ret); +} + +void +vt_drmfb_suspend(struct vt_device *vd) +{ + vt_suspend(vd); +} + +void +vt_drmfb_resume(struct vt_device *vd) +{ + vt_resume(vd); +} diff --git a/drivers/gpu/drm/vt_drmfb.h b/drivers/gpu/drm/vt_drmfb.h new file mode 100644 index 00000000000..f054883f71f --- /dev/null +++ b/drivers/gpu/drm/vt_drmfb.h @@ -0,0 +1,44 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2013 The FreeBSD Foundation + * Copyright (c) 2023 Jean-Sébastien Pédron + * + * This initial software `sys/dev/vt/hw/vt_fb.h` was developed by Aleksandr + * Rybalko under sponsorship from the FreeBSD Foundation. + * This file is a copy of the initial file and is modified by Jean-Sébastien + * Pédron. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_VT_HW_FB_VT_DRMFB_H_ +#define _DEV_VT_HW_FB_VT_DRMFB_H_ +/* Generic framebuffer interface call vt_drmfb_attach to init VT(9) */ +int vt_drmfb_attach(struct fb_info *info); +void vt_drmfb_resume(struct vt_device *vd); +void vt_drmfb_suspend(struct vt_device *vd); +int vt_drmfb_detach(struct fb_info *info); + +#endif /* _DEV_VT_HW_FB_VT_DRMFB_H_ */ diff --git a/drm/Makefile b/drm/Makefile index c6354e4ca3f..126dfa66e6e 100644 --- a/drm/Makefile +++ b/drm/Makefile @@ -68,7 +68,8 @@ SRCS= drm_atomic.c \ drm_vblank.c \ drm_vblank_work.c \ drm_vma_manager.c \ - linux_fb.c + linux_fb.c \ + vt_drmfb.c .if !empty(KCONFIG:MDRM_FBDEV_EMULATION) SRCS+= drm_fb_helper.c diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 574de10bb5b..ed9d381bcb7 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -203,7 +203,6 @@ drm_fb_helper_from_client(struct drm_client_dev *client) * Helper define to register default implementations of drm_fb_helper * functions. To be used in struct fb_ops of drm drivers. */ -#ifdef __linux__ #define DRM_FB_HELPER_DEFAULT_OPS \ .fb_check_var = drm_fb_helper_check_var, \ .fb_set_par = drm_fb_helper_set_par, \ @@ -213,10 +212,6 @@ drm_fb_helper_from_client(struct drm_client_dev *client) .fb_debug_enter = drm_fb_helper_debug_enter, \ .fb_debug_leave = drm_fb_helper_debug_leave, \ .fb_ioctl = drm_fb_helper_ioctl -#elif defined(__FreeBSD__) -#define DRM_FB_HELPER_DEFAULT_OPS \ - .fb_set_par = drm_fb_helper_set_par -#endif #ifdef CONFIG_DRM_FBDEV_EMULATION void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, @@ -246,7 +241,6 @@ ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf, ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos); -#ifdef __linux__ void drm_fb_helper_sys_fillrect(struct fb_info *info, const struct fb_fillrect *rect); void drm_fb_helper_sys_copyarea(struct fb_info *info, @@ -260,15 +254,12 @@ void drm_fb_helper_cfb_copyarea(struct fb_info *info, const struct fb_copyarea *area); void drm_fb_helper_cfb_imageblit(struct fb_info *info, const struct fb_image *image); -#endif /* __linux__ */ void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend); void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper, bool suspend); -#ifdef __linux__ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info); -#endif int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg); diff --git a/linuxkpi/bsd/include/uapi/linux/fb.h b/linuxkpi/bsd/include/uapi/linux/fb.h index 7e6e3514188..4498c3fa4f5 100644 --- a/linuxkpi/bsd/include/uapi/linux/fb.h +++ b/linuxkpi/bsd/include/uapi/linux/fb.h @@ -32,6 +32,180 @@ #define FB_ROTATE_UD 2 #define FB_ROTATE_CCW 3 -#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) +#define FBIO_WAITFORVSYNC _IOW('F', 0x20, uint32_t) -#endif /* _BSD_LKPI_UAPI_FB_H_ */ +#define PICOS2KHZ(a) (1000000000UL/(a)) +#define KHZ2PICOS(a) (1000000000UL/(a)) + +#define FB_TYPE_PACKED_PIXELS 0 +#define FB_TYPE_PLANES 1 +#define FB_TYPE_INTERLEAVED_PLANES 2 +#define FB_TYPE_TEXT 3 +#define FB_TYPE_VGA_PLANES 4 +#define FB_TYPE_FOURCC 5 + +#define FB_VISUAL_MONO01 0 +#define FB_VISUAL_MONO10 1 +#define FB_VISUAL_TRUECOLOR 2 +#define FB_VISUAL_PSEUDOCOLOR 3 +#define FB_VISUAL_DIRECTCOLOR 4 +#define FB_VISUAL_STATIC_PSEUDOCOLOR 5 +#define FB_VISUAL_FOURCC 6 + +#define FB_ACCEL_NONE 0 + +struct fb_fix_screeninfo { + char id[16]; +#ifdef __linux__ + unsigned long smem_start; +#elif defined(__FreeBSD__) + vm_paddr_t smem_start; +#endif + + uint32_t smem_len; + uint32_t type; + uint32_t type_aux; + uint32_t visual; + uint16_t xpanstep; + uint16_t ypanstep; + uint16_t ywrapstep; + uint32_t line_length; + unsigned long mmio_start; + + uint32_t mmio_len; + uint32_t accel; + + uint16_t capabilities; + uint16_t reserved[2]; +}; + +struct fb_bitfield { + uint32_t offset; + uint32_t length; + uint32_t msb_right; +}; + +#define FB_ACTIVATE_NOW 0 + +#define FB_ACCELF_TEXT 1 + +struct fb_var_screeninfo { + uint32_t xres; + uint32_t yres; + uint32_t xres_virtual; + uint32_t yres_virtual; + uint32_t xoffset; + uint32_t yoffset; + + uint32_t bits_per_pixel; + uint32_t grayscale; + + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield transp; + + uint32_t nonstd; + + uint32_t activate; + + uint32_t height; + uint32_t width; + + uint32_t accel_flags; + + uint32_t pixclock; + uint32_t left_margin; + uint32_t right_margin; + uint32_t upper_margin; + uint32_t lower_margin; + uint32_t hsync_len; + uint32_t vsync_len; + uint32_t sync; + uint32_t vmode; + uint32_t rotate; + uint32_t colorspace; + uint32_t reserved[4]; +}; + +struct fb_cmap { + uint32_t start; + uint32_t len; + uint16_t *red; + uint16_t *green; + uint16_t *blue; + uint16_t *transp; +}; + +/* VESA Blanking Levels */ +#define VESA_NO_BLANKING 0 +#define VESA_VSYNC_SUSPEND 1 +#define VESA_HSYNC_SUSPEND 2 +#define VESA_POWERDOWN 3 + +enum { + FB_BLANK_UNBLANK = VESA_NO_BLANKING, + FB_BLANK_NORMAL = VESA_NO_BLANKING + 1, + FB_BLANK_VSYNC_SUSPEND = VESA_VSYNC_SUSPEND + 1, + FB_BLANK_HSYNC_SUSPEND = VESA_HSYNC_SUSPEND + 1, + FB_BLANK_POWERDOWN = VESA_POWERDOWN + 1 +}; + +struct fb_vblank { + uint32_t flags; + uint32_t count; + uint32_t vcount; + uint32_t hcount; + uint32_t reserved[4]; +}; + +#define ROP_COPY 0 +#define ROP_XOR 1 + +struct fb_copyarea { + uint32_t dx; + uint32_t dy; + uint32_t width; + uint32_t height; + uint32_t sx; + uint32_t sy; +}; + +struct fb_fillrect { + uint32_t dx; + uint32_t dy; + uint32_t width; + uint32_t height; + uint32_t color; + uint32_t rop; +}; + +struct fb_image { + uint32_t dx; + uint32_t dy; + uint32_t width; + uint32_t height; + uint32_t fg_color; + uint32_t bg_color; + uint8_t depth; + const char *data; + struct fb_cmap cmap; +#ifdef __FreeBSD__ + const char *mask; +#endif +}; + +struct fbcurpos { + uint16_t x, y; +}; + +struct fb_cursor { + uint16_t set; + uint16_t enable; + uint16_t rop; + const char *mask; + struct fbcurpos hot; + struct fb_image image; +}; + +#endif diff --git a/linuxkpi/gplv2/include/linux/fb.h b/linuxkpi/gplv2/include/linux/fb.h index 87acdf4431f..44ff189dea9 100644 --- a/linuxkpi/gplv2/include/linux/fb.h +++ b/linuxkpi/gplv2/include/linux/fb.h @@ -18,32 +18,99 @@ struct linux_fb_info; struct videomode; struct vm_area_struct; -#define KHZ2PICOS(a) (1000000000UL/(a)) - -struct fb_fix_screeninfo { - vm_paddr_t smem_start; - uint32_t smem_len; - uint32_t line_length; -}; - -struct fb_var_screeninfo { - int xres; - int yres; - int bits_per_pixel; +struct fb_blit_caps { + u32 x; + u32 y; + u32 len; + u32 flags; }; struct fb_ops { /* open/release and usage marking */ struct module *owner; + int (*fb_open)(struct linux_fb_info *info, int user); + int (*fb_release)(struct linux_fb_info *info, int user); + + /* For framebuffers with strange non linear layouts or that do not + * work with normal memory mapped access + */ + ssize_t (*fb_read)(struct linux_fb_info *info, char __user *buf, + size_t count, loff_t *ppos); + ssize_t (*fb_write)(struct linux_fb_info *info, const char __user *buf, + size_t count, loff_t *ppos); + + /* checks var and eventually tweaks it to something supported, + * DO NOT MODIFY PAR */ + int (*fb_check_var)(struct fb_var_screeninfo *var, struct linux_fb_info *info); /* set the video mode according to info->var */ int (*fb_set_par)(struct linux_fb_info *info); + /* set color register */ + int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct linux_fb_info *info); + + /* set color registers in batch */ + int (*fb_setcmap)(struct fb_cmap *cmap, struct linux_fb_info *info); + + /* blank display */ + int (*fb_blank)(int blank, struct linux_fb_info *info); + + /* pan display */ + int (*fb_pan_display)(struct fb_var_screeninfo *var, struct linux_fb_info *info); + + /* Draws a rectangle */ + void (*fb_fillrect) (struct linux_fb_info *info, const struct fb_fillrect *rect); + /* Copy data from area to another */ + void (*fb_copyarea) (struct linux_fb_info *info, const struct fb_copyarea *region); + /* Draws a image to the display */ + void (*fb_imageblit) (struct linux_fb_info *info, const struct fb_image *image); + + /* Draws cursor */ + int (*fb_cursor) (struct linux_fb_info *info, struct fb_cursor *cursor); + + /* wait for blit idle, optional */ + int (*fb_sync)(struct linux_fb_info *info); + + /* perform fb specific ioctl (optional) */ + int (*fb_ioctl)(struct linux_fb_info *info, unsigned int cmd, + unsigned long arg); + + /* Handle 32bit compat ioctl (optional) */ + int (*fb_compat_ioctl)(struct linux_fb_info *info, unsigned cmd, + unsigned long arg); + + /* perform fb specific mmap */ + int (*fb_mmap)(struct linux_fb_info *info, struct vm_area_struct *vma); + + /* get capability given var */ + void (*fb_get_caps)(struct linux_fb_info *info, struct fb_blit_caps *caps, + struct fb_var_screeninfo *var); + /* teardown any resources to do with this framebuffer */ void (*fb_destroy)(struct linux_fb_info *info); + + /* called at KDB enter and leave time to prepare the console */ + int (*fb_debug_enter)(struct linux_fb_info *info); + int (*fb_debug_leave)(struct linux_fb_info *info); }; +/* + * Hide smem_start in the FBIOGET_FSCREENINFO IOCTL. This is used by modern DRM + * drivers to stop userspace from trying to share buffers behind the kernel's + * back. Instead dma-buf based buffer sharing should be used. + */ +#define FBINFO_HIDE_SMEM_START 0x200000 + + struct linux_fb_info { + int flags; + /* + * -1 by default, set to a FB_ROTATE_* value by the driver, if it knows + * a lcd is not mounted upright and fbcon should rotate to compensate. + */ + int fbcon_rotate_hint; + struct fb_var_screeninfo var; /* Current var */ struct fb_fix_screeninfo fix; /* Current fix */ @@ -72,8 +139,13 @@ struct linux_fb_info { } ranges[0]; } *apertures; + bool skip_vt_switch; /* no VT switch on suspend/resume required */ + +#ifdef __FreeBSD__ struct fb_info fbio; device_t fb_bsddev; + struct task fb_mode_task; +#endif } __aligned(sizeof(long)); static inline struct apertures_struct *alloc_apertures(unsigned int max_num) { @@ -85,6 +157,24 @@ static inline struct apertures_struct *alloc_apertures(unsigned int max_num) { return a; } + /* + * `Generic' versions of the frame buffer device operations + */ + +extern void cfb_fillrect(struct linux_fb_info *info, const struct fb_fillrect *rect); +extern void cfb_copyarea(struct linux_fb_info *info, const struct fb_copyarea *area); +extern void cfb_imageblit(struct linux_fb_info *info, const struct fb_image *image); +/* + * Drawing operations where framebuffer is in system RAM + */ +extern void sys_fillrect(struct linux_fb_info *info, const struct fb_fillrect *rect); +extern void sys_copyarea(struct linux_fb_info *info, const struct fb_copyarea *area); +extern void sys_imageblit(struct linux_fb_info *info, const struct fb_image *image); +extern ssize_t fb_sys_read(struct linux_fb_info *info, char __user *buf, + size_t count, loff_t *ppos); +extern ssize_t fb_sys_write(struct linux_fb_info *info, const char __user *buf, + size_t count, loff_t *ppos); + int linux_register_framebuffer(struct linux_fb_info *fb_info); int linux_unregister_framebuffer(struct linux_fb_info *fb_info); int remove_conflicting_framebuffers(struct apertures_struct *a, @@ -104,6 +194,4 @@ is_firmware_framebuffer(struct apertures_struct *a __unused) int linux_fb_get_options(const char *name, char **option); #define fb_get_options linux_fb_get_options -void vt_unfreeze_main_vd(void); - #endif /* __LINUX_FB_H_ */