1 1.16 chs /* $NetBSD: nouveau_fbcon.c,v 1.16 2021/12/20 20:34:58 chs Exp $ */ 2 1.2 riastrad 3 1.1 riastrad /* 4 1.1 riastrad * Copyright 2007 David Airlie 5 1.1 riastrad * 6 1.1 riastrad * Permission is hereby granted, free of charge, to any person obtaining a 7 1.1 riastrad * copy of this software and associated documentation files (the "Software"), 8 1.1 riastrad * to deal in the Software without restriction, including without limitation 9 1.1 riastrad * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 1.1 riastrad * and/or sell copies of the Software, and to permit persons to whom the 11 1.1 riastrad * Software is furnished to do so, subject to the following conditions: 12 1.1 riastrad * 13 1.1 riastrad * The above copyright notice and this permission notice (including the next 14 1.1 riastrad * paragraph) shall be included in all copies or substantial portions of the 15 1.1 riastrad * Software. 16 1.1 riastrad * 17 1.1 riastrad * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 1.1 riastrad * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 1.1 riastrad * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 1.1 riastrad * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 1.1 riastrad * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 1.1 riastrad * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 1.1 riastrad * DEALINGS IN THE SOFTWARE. 24 1.1 riastrad * 25 1.1 riastrad * Authors: 26 1.1 riastrad * David Airlie 27 1.1 riastrad */ 28 1.1 riastrad 29 1.2 riastrad #include <sys/cdefs.h> 30 1.16 chs __KERNEL_RCSID(0, "$NetBSD: nouveau_fbcon.c,v 1.16 2021/12/20 20:34:58 chs Exp $"); 31 1.2 riastrad 32 1.1 riastrad #include <linux/module.h> 33 1.1 riastrad #include <linux/kernel.h> 34 1.1 riastrad #include <linux/errno.h> 35 1.1 riastrad #include <linux/string.h> 36 1.1 riastrad #include <linux/mm.h> 37 1.1 riastrad #include <linux/tty.h> 38 1.1 riastrad #include <linux/sysrq.h> 39 1.1 riastrad #include <linux/delay.h> 40 1.1 riastrad #include <linux/init.h> 41 1.1 riastrad #include <linux/screen_info.h> 42 1.1 riastrad #include <linux/vga_switcheroo.h> 43 1.1 riastrad #include <linux/console.h> 44 1.1 riastrad 45 1.1 riastrad #include <drm/drm_crtc.h> 46 1.1 riastrad #include <drm/drm_crtc_helper.h> 47 1.1 riastrad #include <drm/drm_fb_helper.h> 48 1.12 riastrad #include <drm/drm_fourcc.h> 49 1.12 riastrad #include <drm/drm_atomic.h> 50 1.1 riastrad 51 1.12 riastrad #include "nouveau_drv.h" 52 1.1 riastrad #include "nouveau_gem.h" 53 1.1 riastrad #include "nouveau_bo.h" 54 1.1 riastrad #include "nouveau_fbcon.h" 55 1.1 riastrad #include "nouveau_chan.h" 56 1.12 riastrad #include "nouveau_vmm.h" 57 1.1 riastrad 58 1.1 riastrad #include "nouveau_crtc.h" 59 1.1 riastrad 60 1.2 riastrad #ifdef __NetBSD__ 61 1.2 riastrad #include "nouveaufb.h" 62 1.14 riastrad #include <linux/nbsd-namespace.h> 63 1.2 riastrad #endif 64 1.2 riastrad 65 1.2 riastrad #ifdef __NetBSD__ /* XXX nouveau fbaccel */ 66 1.4 riastrad int nouveau_nofbaccel = 1; 67 1.2 riastrad #else 68 1.1 riastrad MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration"); 69 1.3 riastrad int nouveau_nofbaccel = 0; 70 1.1 riastrad module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400); 71 1.14 riastrad #endif 72 1.1 riastrad 73 1.12 riastrad MODULE_PARM_DESC(fbcon_bpp, "fbcon bits-per-pixel (default: auto)"); 74 1.12 riastrad static int nouveau_fbcon_bpp; 75 1.12 riastrad module_param_named(fbcon_bpp, nouveau_fbcon_bpp, int, 0400); 76 1.12 riastrad 77 1.14 riastrad #ifndef __NetBSD__ /* XXX nouveau fbaccel */ 78 1.1 riastrad static void 79 1.1 riastrad nouveau_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 80 1.1 riastrad { 81 1.1 riastrad struct nouveau_fbdev *fbcon = info->par; 82 1.12 riastrad struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev); 83 1.12 riastrad struct nvif_device *device = &drm->client.device; 84 1.1 riastrad int ret; 85 1.1 riastrad 86 1.1 riastrad if (info->state != FBINFO_STATE_RUNNING) 87 1.1 riastrad return; 88 1.1 riastrad 89 1.1 riastrad ret = -ENODEV; 90 1.1 riastrad if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) && 91 1.1 riastrad mutex_trylock(&drm->client.mutex)) { 92 1.3 riastrad if (device->info.family < NV_DEVICE_INFO_V0_TESLA) 93 1.1 riastrad ret = nv04_fbcon_fillrect(info, rect); 94 1.1 riastrad else 95 1.3 riastrad if (device->info.family < NV_DEVICE_INFO_V0_FERMI) 96 1.1 riastrad ret = nv50_fbcon_fillrect(info, rect); 97 1.1 riastrad else 98 1.1 riastrad ret = nvc0_fbcon_fillrect(info, rect); 99 1.1 riastrad mutex_unlock(&drm->client.mutex); 100 1.1 riastrad } 101 1.1 riastrad 102 1.1 riastrad if (ret == 0) 103 1.1 riastrad return; 104 1.1 riastrad 105 1.1 riastrad if (ret != -ENODEV) 106 1.1 riastrad nouveau_fbcon_gpu_lockup(info); 107 1.3 riastrad drm_fb_helper_cfb_fillrect(info, rect); 108 1.1 riastrad } 109 1.1 riastrad 110 1.1 riastrad static void 111 1.1 riastrad nouveau_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *image) 112 1.1 riastrad { 113 1.1 riastrad struct nouveau_fbdev *fbcon = info->par; 114 1.12 riastrad struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev); 115 1.12 riastrad struct nvif_device *device = &drm->client.device; 116 1.1 riastrad int ret; 117 1.1 riastrad 118 1.1 riastrad if (info->state != FBINFO_STATE_RUNNING) 119 1.1 riastrad return; 120 1.1 riastrad 121 1.1 riastrad ret = -ENODEV; 122 1.1 riastrad if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) && 123 1.1 riastrad mutex_trylock(&drm->client.mutex)) { 124 1.3 riastrad if (device->info.family < NV_DEVICE_INFO_V0_TESLA) 125 1.1 riastrad ret = nv04_fbcon_copyarea(info, image); 126 1.1 riastrad else 127 1.3 riastrad if (device->info.family < NV_DEVICE_INFO_V0_FERMI) 128 1.1 riastrad ret = nv50_fbcon_copyarea(info, image); 129 1.1 riastrad else 130 1.1 riastrad ret = nvc0_fbcon_copyarea(info, image); 131 1.1 riastrad mutex_unlock(&drm->client.mutex); 132 1.1 riastrad } 133 1.1 riastrad 134 1.1 riastrad if (ret == 0) 135 1.1 riastrad return; 136 1.1 riastrad 137 1.1 riastrad if (ret != -ENODEV) 138 1.1 riastrad nouveau_fbcon_gpu_lockup(info); 139 1.3 riastrad drm_fb_helper_cfb_copyarea(info, image); 140 1.1 riastrad } 141 1.1 riastrad 142 1.1 riastrad static void 143 1.1 riastrad nouveau_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) 144 1.1 riastrad { 145 1.1 riastrad struct nouveau_fbdev *fbcon = info->par; 146 1.12 riastrad struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev); 147 1.12 riastrad struct nvif_device *device = &drm->client.device; 148 1.1 riastrad int ret; 149 1.1 riastrad 150 1.1 riastrad if (info->state != FBINFO_STATE_RUNNING) 151 1.1 riastrad return; 152 1.1 riastrad 153 1.1 riastrad ret = -ENODEV; 154 1.1 riastrad if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) && 155 1.1 riastrad mutex_trylock(&drm->client.mutex)) { 156 1.3 riastrad if (device->info.family < NV_DEVICE_INFO_V0_TESLA) 157 1.1 riastrad ret = nv04_fbcon_imageblit(info, image); 158 1.1 riastrad else 159 1.3 riastrad if (device->info.family < NV_DEVICE_INFO_V0_FERMI) 160 1.1 riastrad ret = nv50_fbcon_imageblit(info, image); 161 1.1 riastrad else 162 1.1 riastrad ret = nvc0_fbcon_imageblit(info, image); 163 1.1 riastrad mutex_unlock(&drm->client.mutex); 164 1.1 riastrad } 165 1.1 riastrad 166 1.1 riastrad if (ret == 0) 167 1.1 riastrad return; 168 1.1 riastrad 169 1.1 riastrad if (ret != -ENODEV) 170 1.1 riastrad nouveau_fbcon_gpu_lockup(info); 171 1.3 riastrad drm_fb_helper_cfb_imageblit(info, image); 172 1.1 riastrad } 173 1.1 riastrad 174 1.1 riastrad static int 175 1.1 riastrad nouveau_fbcon_sync(struct fb_info *info) 176 1.1 riastrad { 177 1.1 riastrad struct nouveau_fbdev *fbcon = info->par; 178 1.12 riastrad struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev); 179 1.1 riastrad struct nouveau_channel *chan = drm->channel; 180 1.1 riastrad int ret; 181 1.1 riastrad 182 1.1 riastrad if (!chan || !chan->accel_done || in_interrupt() || 183 1.1 riastrad info->state != FBINFO_STATE_RUNNING || 184 1.1 riastrad info->flags & FBINFO_HWACCEL_DISABLED) 185 1.1 riastrad return 0; 186 1.1 riastrad 187 1.1 riastrad if (!mutex_trylock(&drm->client.mutex)) 188 1.1 riastrad return 0; 189 1.1 riastrad 190 1.1 riastrad ret = nouveau_channel_idle(chan); 191 1.1 riastrad mutex_unlock(&drm->client.mutex); 192 1.1 riastrad if (ret) { 193 1.1 riastrad nouveau_fbcon_gpu_lockup(info); 194 1.1 riastrad return 0; 195 1.1 riastrad } 196 1.1 riastrad 197 1.1 riastrad chan->accel_done = false; 198 1.1 riastrad return 0; 199 1.1 riastrad } 200 1.1 riastrad 201 1.3 riastrad static int 202 1.3 riastrad nouveau_fbcon_open(struct fb_info *info, int user) 203 1.3 riastrad { 204 1.3 riastrad struct nouveau_fbdev *fbcon = info->par; 205 1.12 riastrad struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev); 206 1.3 riastrad int ret = pm_runtime_get_sync(drm->dev->dev); 207 1.3 riastrad if (ret < 0 && ret != -EACCES) 208 1.3 riastrad return ret; 209 1.3 riastrad return 0; 210 1.3 riastrad } 211 1.3 riastrad 212 1.3 riastrad static int 213 1.3 riastrad nouveau_fbcon_release(struct fb_info *info, int user) 214 1.3 riastrad { 215 1.3 riastrad struct nouveau_fbdev *fbcon = info->par; 216 1.12 riastrad struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev); 217 1.3 riastrad pm_runtime_put(drm->dev->dev); 218 1.3 riastrad return 0; 219 1.3 riastrad } 220 1.3 riastrad 221 1.12 riastrad static const struct fb_ops nouveau_fbcon_ops = { 222 1.1 riastrad .owner = THIS_MODULE, 223 1.12 riastrad DRM_FB_HELPER_DEFAULT_OPS, 224 1.3 riastrad .fb_open = nouveau_fbcon_open, 225 1.3 riastrad .fb_release = nouveau_fbcon_release, 226 1.1 riastrad .fb_fillrect = nouveau_fbcon_fillrect, 227 1.1 riastrad .fb_copyarea = nouveau_fbcon_copyarea, 228 1.1 riastrad .fb_imageblit = nouveau_fbcon_imageblit, 229 1.1 riastrad .fb_sync = nouveau_fbcon_sync, 230 1.1 riastrad }; 231 1.1 riastrad 232 1.12 riastrad static const struct fb_ops nouveau_fbcon_sw_ops = { 233 1.1 riastrad .owner = THIS_MODULE, 234 1.12 riastrad DRM_FB_HELPER_DEFAULT_OPS, 235 1.3 riastrad .fb_open = nouveau_fbcon_open, 236 1.3 riastrad .fb_release = nouveau_fbcon_release, 237 1.3 riastrad .fb_fillrect = drm_fb_helper_cfb_fillrect, 238 1.3 riastrad .fb_copyarea = drm_fb_helper_cfb_copyarea, 239 1.3 riastrad .fb_imageblit = drm_fb_helper_cfb_imageblit, 240 1.1 riastrad }; 241 1.2 riastrad #endif /* XXX nouveau fbaccel */ 242 1.1 riastrad 243 1.3 riastrad void 244 1.3 riastrad nouveau_fbcon_accel_save_disable(struct drm_device *dev) 245 1.3 riastrad { 246 1.4 riastrad #ifndef __NetBSD__ /* XXX nouveau fbaccel */ 247 1.3 riastrad struct nouveau_drm *drm = nouveau_drm(dev); 248 1.12 riastrad if (drm->fbcon && drm->fbcon->helper.fbdev) { 249 1.3 riastrad drm->fbcon->saved_flags = drm->fbcon->helper.fbdev->flags; 250 1.3 riastrad drm->fbcon->helper.fbdev->flags |= FBINFO_HWACCEL_DISABLED; 251 1.3 riastrad } 252 1.4 riastrad #endif 253 1.3 riastrad } 254 1.3 riastrad 255 1.3 riastrad void 256 1.3 riastrad nouveau_fbcon_accel_restore(struct drm_device *dev) 257 1.3 riastrad { 258 1.4 riastrad #ifndef __NetBSD__ /* XXX nouveau fbaccel */ 259 1.3 riastrad struct nouveau_drm *drm = nouveau_drm(dev); 260 1.12 riastrad if (drm->fbcon && drm->fbcon->helper.fbdev) { 261 1.3 riastrad drm->fbcon->helper.fbdev->flags = drm->fbcon->saved_flags; 262 1.3 riastrad } 263 1.4 riastrad #endif 264 1.3 riastrad } 265 1.3 riastrad 266 1.3 riastrad static void 267 1.3 riastrad nouveau_fbcon_accel_fini(struct drm_device *dev) 268 1.3 riastrad { 269 1.3 riastrad struct nouveau_drm *drm = nouveau_drm(dev); 270 1.3 riastrad struct nouveau_fbdev *fbcon = drm->fbcon; 271 1.3 riastrad if (fbcon && drm->channel) { 272 1.9 riastrad console_lock(); 273 1.4 riastrad #ifndef __NetBSD__ /* XXX nouveau fbaccel */ 274 1.12 riastrad if (fbcon->helper.fbdev) 275 1.12 riastrad fbcon->helper.fbdev->flags |= FBINFO_HWACCEL_DISABLED; 276 1.9 riastrad #endif 277 1.3 riastrad console_unlock(); 278 1.3 riastrad nouveau_channel_idle(drm->channel); 279 1.3 riastrad nvif_object_fini(&fbcon->twod); 280 1.3 riastrad nvif_object_fini(&fbcon->blit); 281 1.3 riastrad nvif_object_fini(&fbcon->gdi); 282 1.3 riastrad nvif_object_fini(&fbcon->patt); 283 1.3 riastrad nvif_object_fini(&fbcon->rop); 284 1.3 riastrad nvif_object_fini(&fbcon->clip); 285 1.3 riastrad nvif_object_fini(&fbcon->surf2d); 286 1.3 riastrad } 287 1.3 riastrad } 288 1.3 riastrad 289 1.4 riastrad #ifndef __NetBSD__ /* XXX nouveau fbaccel */ 290 1.3 riastrad static void 291 1.3 riastrad nouveau_fbcon_accel_init(struct drm_device *dev) 292 1.3 riastrad { 293 1.3 riastrad struct nouveau_drm *drm = nouveau_drm(dev); 294 1.3 riastrad struct nouveau_fbdev *fbcon = drm->fbcon; 295 1.3 riastrad struct fb_info *info = fbcon->helper.fbdev; 296 1.3 riastrad int ret; 297 1.3 riastrad 298 1.12 riastrad if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) 299 1.3 riastrad ret = nv04_fbcon_accel_init(info); 300 1.3 riastrad else 301 1.12 riastrad if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI) 302 1.3 riastrad ret = nv50_fbcon_accel_init(info); 303 1.3 riastrad else 304 1.3 riastrad ret = nvc0_fbcon_accel_init(info); 305 1.3 riastrad 306 1.3 riastrad if (ret == 0) 307 1.3 riastrad info->fbops = &nouveau_fbcon_ops; 308 1.3 riastrad } 309 1.4 riastrad #endif 310 1.3 riastrad 311 1.1 riastrad static void 312 1.1 riastrad nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *fbcon) 313 1.1 riastrad { 314 1.2 riastrad #ifdef __NetBSD__ /* XXX nouveau fbaccel */ 315 1.14 riastrad struct drm_framebuffer *fb = fbcon->helper.fb; 316 1.14 riastrad struct nouveau_framebuffer *nvfb = container_of(fb, 317 1.14 riastrad struct nouveau_framebuffer, base); 318 1.14 riastrad struct nouveau_bo *nvbo = nvfb->nvbo; 319 1.2 riastrad 320 1.2 riastrad (void)memset(__UNVOLATILE(nvbo_kmap_obj_iovirtual(nvbo)), 0, 321 1.2 riastrad nvbo->bo.num_pages << PAGE_SHIFT); 322 1.2 riastrad #else 323 1.1 riastrad struct fb_info *info = fbcon->helper.fbdev; 324 1.1 riastrad struct fb_fillrect rect; 325 1.1 riastrad 326 1.1 riastrad /* Clear the entire fbcon. The drm will program every connector 327 1.1 riastrad * with it's preferred mode. If the sizes differ, one display will 328 1.1 riastrad * quite likely have garbage around the console. 329 1.1 riastrad */ 330 1.1 riastrad rect.dx = rect.dy = 0; 331 1.1 riastrad rect.width = info->var.xres_virtual; 332 1.1 riastrad rect.height = info->var.yres_virtual; 333 1.1 riastrad rect.color = 0; 334 1.1 riastrad rect.rop = ROP_COPY; 335 1.1 riastrad info->fbops->fb_fillrect(info, &rect); 336 1.2 riastrad #endif 337 1.1 riastrad } 338 1.1 riastrad 339 1.7 jdolecek #ifdef __NetBSD__ 340 1.7 jdolecek static int 341 1.7 jdolecek nouveau_fbcon_print(void *aux, const char *pnp) 342 1.7 jdolecek { 343 1.7 jdolecek if (pnp) 344 1.7 jdolecek aprint_normal("nouveaufbbus at %s", pnp); 345 1.7 jdolecek 346 1.7 jdolecek return (UNCONF); 347 1.7 jdolecek } 348 1.7 jdolecek #endif 349 1.7 jdolecek 350 1.1 riastrad static int 351 1.1 riastrad nouveau_fbcon_create(struct drm_fb_helper *helper, 352 1.1 riastrad struct drm_fb_helper_surface_size *sizes) 353 1.1 riastrad { 354 1.3 riastrad struct nouveau_fbdev *fbcon = 355 1.3 riastrad container_of(helper, struct nouveau_fbdev, helper); 356 1.12 riastrad struct drm_device *dev = fbcon->helper.dev; 357 1.1 riastrad struct nouveau_drm *drm = nouveau_drm(dev); 358 1.12 riastrad struct nvif_device *device = &drm->client.device; 359 1.2 riastrad #ifndef __NetBSD__ 360 1.1 riastrad struct fb_info *info; 361 1.2 riastrad #endif 362 1.12 riastrad struct nouveau_framebuffer *fb; 363 1.1 riastrad struct nouveau_channel *chan; 364 1.1 riastrad struct nouveau_bo *nvbo; 365 1.1 riastrad struct drm_mode_fb_cmd2 mode_cmd; 366 1.12 riastrad int ret; 367 1.1 riastrad 368 1.1 riastrad mode_cmd.width = sizes->surface_width; 369 1.1 riastrad mode_cmd.height = sizes->surface_height; 370 1.1 riastrad 371 1.1 riastrad mode_cmd.pitches[0] = mode_cmd.width * (sizes->surface_bpp >> 3); 372 1.1 riastrad mode_cmd.pitches[0] = roundup(mode_cmd.pitches[0], 256); 373 1.1 riastrad 374 1.1 riastrad mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 375 1.1 riastrad sizes->surface_depth); 376 1.1 riastrad 377 1.12 riastrad ret = nouveau_gem_new(&drm->client, mode_cmd.pitches[0] * 378 1.12 riastrad mode_cmd.height, 0, NOUVEAU_GEM_DOMAIN_VRAM, 379 1.1 riastrad 0, 0x0000, &nvbo); 380 1.1 riastrad if (ret) { 381 1.1 riastrad NV_ERROR(drm, "failed to allocate framebuffer\n"); 382 1.1 riastrad goto out; 383 1.1 riastrad } 384 1.1 riastrad 385 1.12 riastrad ret = nouveau_framebuffer_new(dev, &mode_cmd, nvbo, &fb); 386 1.12 riastrad if (ret) 387 1.12 riastrad goto out_unref; 388 1.12 riastrad 389 1.3 riastrad ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM, false); 390 1.1 riastrad if (ret) { 391 1.1 riastrad NV_ERROR(drm, "failed to pin fb: %d\n", ret); 392 1.1 riastrad goto out_unref; 393 1.1 riastrad } 394 1.1 riastrad 395 1.1 riastrad ret = nouveau_bo_map(nvbo); 396 1.1 riastrad if (ret) { 397 1.1 riastrad NV_ERROR(drm, "failed to map fb: %d\n", ret); 398 1.1 riastrad goto out_unpin; 399 1.1 riastrad } 400 1.1 riastrad 401 1.1 riastrad chan = nouveau_nofbaccel ? NULL : drm->channel; 402 1.3 riastrad if (chan && device->info.family >= NV_DEVICE_INFO_V0_TESLA) { 403 1.12 riastrad ret = nouveau_vma_new(nvbo, chan->vmm, &fb->vma); 404 1.1 riastrad if (ret) { 405 1.1 riastrad NV_ERROR(drm, "failed to map fb into chan: %d\n", ret); 406 1.1 riastrad chan = NULL; 407 1.1 riastrad } 408 1.1 riastrad } 409 1.1 riastrad 410 1.2 riastrad #ifdef __NetBSD__ 411 1.14 riastrad fbcon->helper.fb = &fb->base; 412 1.2 riastrad 413 1.2 riastrad nouveau_fbcon_zfill(dev, fbcon); 414 1.2 riastrad 415 1.8 jdolecek struct nouveaufb_attach_args nfa; 416 1.2 riastrad 417 1.8 jdolecek memset(&nfa, 0, sizeof(nfa)); 418 1.14 riastrad nfa.nfa_fb_helper = &fbcon->helper; 419 1.2 riastrad nfa.nfa_fb_sizes = *sizes; 420 1.2 riastrad nfa.nfa_fb_ptr = nvbo_kmap_obj_iovirtual(nvbo); 421 1.2 riastrad nfa.nfa_fb_linebytes = mode_cmd.pitches[0]; 422 1.2 riastrad 423 1.16 chs KERNEL_LOCK(1, NULL); 424 1.10 thorpej helper->fbdev = config_found(dev->dev, &nfa, nouveau_fbcon_print, 425 1.11 thorpej CFARGS(.iattr = "nouveaufbbus")); 426 1.16 chs KERNEL_UNLOCK_ONE(NULL); 427 1.2 riastrad if (helper->fbdev == NULL) { 428 1.2 riastrad goto out_unlock; 429 1.2 riastrad } 430 1.8 jdolecek 431 1.2 riastrad return 0; 432 1.2 riastrad #else 433 1.3 riastrad info = drm_fb_helper_alloc_fbi(helper); 434 1.3 riastrad if (IS_ERR(info)) { 435 1.3 riastrad ret = PTR_ERR(info); 436 1.1 riastrad goto out_unlock; 437 1.1 riastrad } 438 1.1 riastrad 439 1.1 riastrad /* setup helper */ 440 1.12 riastrad fbcon->helper.fb = &fb->base; 441 1.1 riastrad 442 1.1 riastrad if (!chan) 443 1.12 riastrad info->flags = FBINFO_HWACCEL_DISABLED; 444 1.1 riastrad else 445 1.12 riastrad info->flags = FBINFO_HWACCEL_COPYAREA | 446 1.1 riastrad FBINFO_HWACCEL_FILLRECT | 447 1.1 riastrad FBINFO_HWACCEL_IMAGEBLIT; 448 1.1 riastrad info->fbops = &nouveau_fbcon_sw_ops; 449 1.12 riastrad info->fix.smem_start = fb->nvbo->bo.mem.bus.base + 450 1.12 riastrad fb->nvbo->bo.mem.bus.offset; 451 1.12 riastrad info->fix.smem_len = fb->nvbo->bo.mem.num_pages << PAGE_SHIFT; 452 1.1 riastrad 453 1.12 riastrad info->screen_base = nvbo_kmap_obj_iovirtual(fb->nvbo); 454 1.12 riastrad info->screen_size = fb->nvbo->bo.mem.num_pages << PAGE_SHIFT; 455 1.1 riastrad 456 1.12 riastrad drm_fb_helper_fill_info(info, &fbcon->helper, sizes); 457 1.1 riastrad 458 1.1 riastrad /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ 459 1.1 riastrad 460 1.3 riastrad if (chan) 461 1.3 riastrad nouveau_fbcon_accel_init(dev); 462 1.1 riastrad nouveau_fbcon_zfill(dev, fbcon); 463 1.1 riastrad 464 1.1 riastrad /* To allow resizeing without swapping buffers */ 465 1.3 riastrad NV_INFO(drm, "allocated %dx%d fb: 0x%jx, bo %p\n", 466 1.1 riastrad nouveau_fb->base.width, nouveau_fb->base.height, 467 1.3 riastrad (uintmax_t)nvbo->bo.offset, nvbo); 468 1.1 riastrad 469 1.1 riastrad vga_switcheroo_client_fb_set(dev->pdev, info); 470 1.1 riastrad return 0; 471 1.2 riastrad #endif /* defined(__NetBSD__) */ 472 1.1 riastrad 473 1.1 riastrad out_unlock: 474 1.1 riastrad if (chan) 475 1.12 riastrad nouveau_vma_del(&fb->vma); 476 1.12 riastrad nouveau_bo_unmap(fb->nvbo); 477 1.1 riastrad out_unpin: 478 1.12 riastrad nouveau_bo_unpin(fb->nvbo); 479 1.1 riastrad out_unref: 480 1.12 riastrad nouveau_bo_ref(NULL, &fb->nvbo); 481 1.1 riastrad out: 482 1.1 riastrad return ret; 483 1.1 riastrad } 484 1.1 riastrad 485 1.1 riastrad static int 486 1.1 riastrad nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon) 487 1.1 riastrad { 488 1.12 riastrad struct nouveau_framebuffer *nouveau_fb = nouveau_framebuffer(fbcon->helper.fb); 489 1.1 riastrad 490 1.3 riastrad drm_fb_helper_unregister_fbi(&fbcon->helper); 491 1.12 riastrad drm_fb_helper_fini(&fbcon->helper); 492 1.1 riastrad 493 1.12 riastrad if (nouveau_fb && nouveau_fb->nvbo) { 494 1.12 riastrad nouveau_vma_del(&nouveau_fb->vma); 495 1.1 riastrad nouveau_bo_unmap(nouveau_fb->nvbo); 496 1.1 riastrad nouveau_bo_unpin(nouveau_fb->nvbo); 497 1.12 riastrad drm_framebuffer_put(&nouveau_fb->base); 498 1.1 riastrad } 499 1.12 riastrad 500 1.1 riastrad return 0; 501 1.1 riastrad } 502 1.1 riastrad 503 1.2 riastrad #ifndef __NetBSD__ /* XXX nouveau fbaccel */ 504 1.1 riastrad void nouveau_fbcon_gpu_lockup(struct fb_info *info) 505 1.1 riastrad { 506 1.1 riastrad struct nouveau_fbdev *fbcon = info->par; 507 1.12 riastrad struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev); 508 1.1 riastrad 509 1.1 riastrad NV_ERROR(drm, "GPU lockup - switching to software fbcon\n"); 510 1.1 riastrad info->flags |= FBINFO_HWACCEL_DISABLED; 511 1.1 riastrad } 512 1.2 riastrad #endif 513 1.1 riastrad 514 1.3 riastrad static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { 515 1.1 riastrad .fb_probe = nouveau_fbcon_create, 516 1.1 riastrad }; 517 1.1 riastrad 518 1.12 riastrad static void 519 1.12 riastrad nouveau_fbcon_set_suspend_work(struct work_struct *work) 520 1.12 riastrad { 521 1.12 riastrad struct nouveau_drm *drm = container_of(work, typeof(*drm), fbcon_work); 522 1.12 riastrad int state = READ_ONCE(drm->fbcon_new_state); 523 1.12 riastrad 524 1.12 riastrad if (state == FBINFO_STATE_RUNNING) 525 1.12 riastrad pm_runtime_get_sync(drm->dev->dev); 526 1.12 riastrad 527 1.12 riastrad console_lock(); 528 1.12 riastrad if (state == FBINFO_STATE_RUNNING) 529 1.12 riastrad nouveau_fbcon_accel_restore(drm->dev); 530 1.12 riastrad drm_fb_helper_set_suspend(&drm->fbcon->helper, state); 531 1.12 riastrad if (state != FBINFO_STATE_RUNNING) 532 1.12 riastrad nouveau_fbcon_accel_save_disable(drm->dev); 533 1.12 riastrad console_unlock(); 534 1.12 riastrad 535 1.12 riastrad if (state == FBINFO_STATE_RUNNING) { 536 1.12 riastrad nouveau_fbcon_hotplug_resume(drm->fbcon); 537 1.12 riastrad pm_runtime_mark_last_busy(drm->dev->dev); 538 1.12 riastrad pm_runtime_put_sync(drm->dev->dev); 539 1.12 riastrad } 540 1.12 riastrad } 541 1.12 riastrad 542 1.3 riastrad void 543 1.3 riastrad nouveau_fbcon_set_suspend(struct drm_device *dev, int state) 544 1.3 riastrad { 545 1.3 riastrad struct nouveau_drm *drm = nouveau_drm(dev); 546 1.12 riastrad 547 1.12 riastrad if (!drm->fbcon) 548 1.12 riastrad return; 549 1.12 riastrad 550 1.12 riastrad drm->fbcon_new_state = state; 551 1.12 riastrad /* Since runtime resume can happen as a result of a sysfs operation, 552 1.12 riastrad * it's possible we already have the console locked. So handle fbcon 553 1.12 riastrad * init/deinit from a seperate work thread 554 1.12 riastrad */ 555 1.12 riastrad schedule_work(&drm->fbcon_work); 556 1.12 riastrad } 557 1.12 riastrad 558 1.12 riastrad void 559 1.12 riastrad nouveau_fbcon_output_poll_changed(struct drm_device *dev) 560 1.12 riastrad { 561 1.12 riastrad struct nouveau_drm *drm = nouveau_drm(dev); 562 1.12 riastrad struct nouveau_fbdev *fbcon = drm->fbcon; 563 1.12 riastrad int ret; 564 1.12 riastrad 565 1.12 riastrad if (!fbcon) 566 1.12 riastrad return; 567 1.12 riastrad 568 1.12 riastrad mutex_lock(&fbcon->hotplug_lock); 569 1.12 riastrad 570 1.12 riastrad ret = pm_runtime_get(dev->dev); 571 1.12 riastrad if (ret == 1 || ret == -EACCES) { 572 1.12 riastrad drm_fb_helper_hotplug_event(&fbcon->helper); 573 1.12 riastrad 574 1.12 riastrad pm_runtime_mark_last_busy(dev->dev); 575 1.12 riastrad pm_runtime_put_autosuspend(dev->dev); 576 1.12 riastrad } else if (ret == 0) { 577 1.12 riastrad /* If the GPU was already in the process of suspending before 578 1.12 riastrad * this event happened, then we can't block here as we'll 579 1.12 riastrad * deadlock the runtime pmops since they wait for us to 580 1.12 riastrad * finish. So, just defer this event for when we runtime 581 1.12 riastrad * resume again. It will be handled by fbcon_work. 582 1.12 riastrad */ 583 1.12 riastrad NV_DEBUG(drm, "fbcon HPD event deferred until runtime resume\n"); 584 1.12 riastrad fbcon->hotplug_waiting = true; 585 1.12 riastrad pm_runtime_put_noidle(drm->dev->dev); 586 1.12 riastrad } else { 587 1.12 riastrad DRM_WARN("fbcon HPD event lost due to RPM failure: %d\n", 588 1.12 riastrad ret); 589 1.12 riastrad } 590 1.12 riastrad 591 1.12 riastrad mutex_unlock(&fbcon->hotplug_lock); 592 1.12 riastrad } 593 1.12 riastrad 594 1.12 riastrad void 595 1.12 riastrad nouveau_fbcon_hotplug_resume(struct nouveau_fbdev *fbcon) 596 1.12 riastrad { 597 1.12 riastrad struct nouveau_drm *drm; 598 1.12 riastrad 599 1.12 riastrad if (!fbcon) 600 1.12 riastrad return; 601 1.12 riastrad drm = nouveau_drm(fbcon->helper.dev); 602 1.12 riastrad 603 1.12 riastrad mutex_lock(&fbcon->hotplug_lock); 604 1.12 riastrad if (fbcon->hotplug_waiting) { 605 1.12 riastrad fbcon->hotplug_waiting = false; 606 1.12 riastrad 607 1.12 riastrad NV_DEBUG(drm, "Handling deferred fbcon HPD events\n"); 608 1.12 riastrad drm_fb_helper_hotplug_event(&fbcon->helper); 609 1.3 riastrad } 610 1.12 riastrad mutex_unlock(&fbcon->hotplug_lock); 611 1.3 riastrad } 612 1.1 riastrad 613 1.1 riastrad int 614 1.1 riastrad nouveau_fbcon_init(struct drm_device *dev) 615 1.1 riastrad { 616 1.1 riastrad struct nouveau_drm *drm = nouveau_drm(dev); 617 1.1 riastrad struct nouveau_fbdev *fbcon; 618 1.12 riastrad int preferred_bpp = nouveau_fbcon_bpp; 619 1.1 riastrad int ret; 620 1.1 riastrad 621 1.1 riastrad if (!dev->mode_config.num_crtc || 622 1.1 riastrad (dev->pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA) 623 1.1 riastrad return 0; 624 1.1 riastrad 625 1.1 riastrad fbcon = kzalloc(sizeof(struct nouveau_fbdev), GFP_KERNEL); 626 1.1 riastrad if (!fbcon) 627 1.1 riastrad return -ENOMEM; 628 1.1 riastrad 629 1.1 riastrad drm->fbcon = fbcon; 630 1.12 riastrad INIT_WORK(&drm->fbcon_work, nouveau_fbcon_set_suspend_work); 631 1.12 riastrad mutex_init(&fbcon->hotplug_lock); 632 1.3 riastrad 633 1.3 riastrad drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs); 634 1.1 riastrad 635 1.12 riastrad ret = drm_fb_helper_init(dev, &fbcon->helper, 4); 636 1.3 riastrad if (ret) 637 1.3 riastrad goto free; 638 1.1 riastrad 639 1.3 riastrad ret = drm_fb_helper_single_add_all_connectors(&fbcon->helper); 640 1.3 riastrad if (ret) 641 1.3 riastrad goto fini; 642 1.1 riastrad 643 1.12 riastrad if (preferred_bpp != 8 && preferred_bpp != 16 && preferred_bpp != 32) { 644 1.12 riastrad if (drm->client.device.info.ram_size <= 32 * 1024 * 1024) 645 1.12 riastrad preferred_bpp = 8; 646 1.12 riastrad else 647 1.12 riastrad if (drm->client.device.info.ram_size <= 64 * 1024 * 1024) 648 1.12 riastrad preferred_bpp = 16; 649 1.12 riastrad else 650 1.12 riastrad preferred_bpp = 32; 651 1.12 riastrad } 652 1.1 riastrad 653 1.1 riastrad /* disable all the possible outputs/crtcs before entering KMS mode */ 654 1.12 riastrad if (!drm_drv_uses_atomic_modeset(dev)) 655 1.12 riastrad drm_helper_disable_unused_functions(dev); 656 1.1 riastrad 657 1.3 riastrad ret = drm_fb_helper_initial_config(&fbcon->helper, preferred_bpp); 658 1.3 riastrad if (ret) 659 1.3 riastrad goto fini; 660 1.3 riastrad 661 1.4 riastrad #ifndef __NetBSD__ 662 1.3 riastrad if (fbcon->helper.fbdev) 663 1.3 riastrad fbcon->helper.fbdev->pixmap.buf_align = 4; 664 1.4 riastrad #endif 665 1.1 riastrad return 0; 666 1.3 riastrad 667 1.3 riastrad fini: 668 1.3 riastrad drm_fb_helper_fini(&fbcon->helper); 669 1.3 riastrad free: 670 1.3 riastrad kfree(fbcon); 671 1.3 riastrad return ret; 672 1.1 riastrad } 673 1.1 riastrad 674 1.1 riastrad void 675 1.1 riastrad nouveau_fbcon_fini(struct drm_device *dev) 676 1.1 riastrad { 677 1.1 riastrad struct nouveau_drm *drm = nouveau_drm(dev); 678 1.1 riastrad 679 1.1 riastrad if (!drm->fbcon) 680 1.1 riastrad return; 681 1.1 riastrad 682 1.3 riastrad nouveau_fbcon_accel_fini(dev); 683 1.1 riastrad nouveau_fbcon_destroy(dev, drm->fbcon); 684 1.15 riastrad mutex_destroy(&drm->fbcon->hotplug_lock); 685 1.1 riastrad kfree(drm->fbcon); 686 1.1 riastrad drm->fbcon = NULL; 687 1.1 riastrad } 688