162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (C) 2008 Maarten Maathuis. 362306a36Sopenharmony_ci * All Rights Reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining 662306a36Sopenharmony_ci * a copy of this software and associated documentation files (the 762306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 862306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 962306a36Sopenharmony_ci * distribute, sublicense, and/or sell copies of the Software, and to 1062306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 1162306a36Sopenharmony_ci * the following conditions: 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the 1462306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial 1562306a36Sopenharmony_ci * portions of the Software. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 1862306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1962306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 2062306a36Sopenharmony_ci * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 2162306a36Sopenharmony_ci * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 2262306a36Sopenharmony_ci * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 2362306a36Sopenharmony_ci * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <acpi/video.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <drm/drm_atomic.h> 3062306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 3162306a36Sopenharmony_ci#include <drm/drm_crtc_helper.h> 3262306a36Sopenharmony_ci#include <drm/drm_fb_helper.h> 3362306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 3462306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 3562306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 3662306a36Sopenharmony_ci#include <drm/drm_vblank.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "nouveau_crtc.h" 3962306a36Sopenharmony_ci#include "nouveau_gem.h" 4062306a36Sopenharmony_ci#include "nouveau_connector.h" 4162306a36Sopenharmony_ci#include "nv50_display.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#include <nvif/class.h> 4462306a36Sopenharmony_ci#include <nvif/if0011.h> 4562306a36Sopenharmony_ci#include <nvif/if0013.h> 4662306a36Sopenharmony_ci#include <dispnv50/crc.h> 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ciint 4962306a36Sopenharmony_cinouveau_display_vblank_enable(struct drm_crtc *crtc) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct nouveau_crtc *nv_crtc; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci nv_crtc = nouveau_crtc(crtc); 5462306a36Sopenharmony_ci nvif_event_allow(&nv_crtc->vblank); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_civoid 6062306a36Sopenharmony_cinouveau_display_vblank_disable(struct drm_crtc *crtc) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct nouveau_crtc *nv_crtc; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci nv_crtc = nouveau_crtc(crtc); 6562306a36Sopenharmony_ci nvif_event_block(&nv_crtc->vblank); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic inline int 6962306a36Sopenharmony_cicalc(int blanks, int blanke, int total, int line) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci if (blanke >= blanks) { 7262306a36Sopenharmony_ci if (line >= blanks) 7362306a36Sopenharmony_ci line -= total; 7462306a36Sopenharmony_ci } else { 7562306a36Sopenharmony_ci if (line >= blanks) 7662306a36Sopenharmony_ci line -= total; 7762306a36Sopenharmony_ci line -= blanke + 1; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci return line; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic bool 8362306a36Sopenharmony_cinouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos, 8462306a36Sopenharmony_ci ktime_t *stime, ktime_t *etime) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct drm_vblank_crtc *vblank = &crtc->dev->vblank[drm_crtc_index(crtc)]; 8762306a36Sopenharmony_ci struct nvif_head *head = &nouveau_crtc(crtc)->head; 8862306a36Sopenharmony_ci struct nvif_head_scanoutpos_v0 args; 8962306a36Sopenharmony_ci int retry = 20; 9062306a36Sopenharmony_ci bool ret = false; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci args.version = 0; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci do { 9562306a36Sopenharmony_ci ret = nvif_mthd(&head->object, NVIF_HEAD_V0_SCANOUTPOS, &args, sizeof(args)); 9662306a36Sopenharmony_ci if (ret != 0) 9762306a36Sopenharmony_ci return false; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (args.vline) { 10062306a36Sopenharmony_ci ret = true; 10162306a36Sopenharmony_ci break; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (retry) ndelay(vblank->linedur_ns); 10562306a36Sopenharmony_ci } while (retry--); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci *hpos = args.hline; 10862306a36Sopenharmony_ci *vpos = calc(args.vblanks, args.vblanke, args.vtotal, args.vline); 10962306a36Sopenharmony_ci if (stime) *stime = ns_to_ktime(args.time[0]); 11062306a36Sopenharmony_ci if (etime) *etime = ns_to_ktime(args.time[1]); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return ret; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cibool 11662306a36Sopenharmony_cinouveau_display_scanoutpos(struct drm_crtc *crtc, 11762306a36Sopenharmony_ci bool in_vblank_irq, int *vpos, int *hpos, 11862306a36Sopenharmony_ci ktime_t *stime, ktime_t *etime, 11962306a36Sopenharmony_ci const struct drm_display_mode *mode) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci return nouveau_display_scanoutpos_head(crtc, vpos, hpos, 12262306a36Sopenharmony_ci stime, etime); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = { 12662306a36Sopenharmony_ci .destroy = drm_gem_fb_destroy, 12762306a36Sopenharmony_ci .create_handle = drm_gem_fb_create_handle, 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void 13162306a36Sopenharmony_cinouveau_decode_mod(struct nouveau_drm *drm, 13262306a36Sopenharmony_ci uint64_t modifier, 13362306a36Sopenharmony_ci uint32_t *tile_mode, 13462306a36Sopenharmony_ci uint8_t *kind) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct nouveau_display *disp = nouveau_display(drm->dev); 13762306a36Sopenharmony_ci BUG_ON(!tile_mode || !kind); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (modifier == DRM_FORMAT_MOD_LINEAR) { 14062306a36Sopenharmony_ci /* tile_mode will not be used in this case */ 14162306a36Sopenharmony_ci *tile_mode = 0; 14262306a36Sopenharmony_ci *kind = 0; 14362306a36Sopenharmony_ci } else { 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * Extract the block height and kind from the corresponding 14662306a36Sopenharmony_ci * modifier fields. See drm_fourcc.h for details. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if ((modifier & (0xffull << 12)) == 0ull) { 15062306a36Sopenharmony_ci /* Legacy modifier. Translate to this dev's 'kind.' */ 15162306a36Sopenharmony_ci modifier |= disp->format_modifiers[0] & (0xffull << 12); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci *tile_mode = (uint32_t)(modifier & 0xF); 15562306a36Sopenharmony_ci *kind = (uint8_t)((modifier >> 12) & 0xFF); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (drm->client.device.info.chipset >= 0xc0) 15862306a36Sopenharmony_ci *tile_mode <<= 4; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_civoid 16362306a36Sopenharmony_cinouveau_framebuffer_get_layout(struct drm_framebuffer *fb, 16462306a36Sopenharmony_ci uint32_t *tile_mode, 16562306a36Sopenharmony_ci uint8_t *kind) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci if (fb->flags & DRM_MODE_FB_MODIFIERS) { 16862306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(fb->dev); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci nouveau_decode_mod(drm, fb->modifier, tile_mode, kind); 17162306a36Sopenharmony_ci } else { 17262306a36Sopenharmony_ci const struct nouveau_bo *nvbo = nouveau_gem_object(fb->obj[0]); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci *tile_mode = nvbo->mode; 17562306a36Sopenharmony_ci *kind = nvbo->kind; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic const u64 legacy_modifiers[] = { 18062306a36Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0), 18162306a36Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1), 18262306a36Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2), 18362306a36Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3), 18462306a36Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4), 18562306a36Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5), 18662306a36Sopenharmony_ci DRM_FORMAT_MOD_INVALID 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int 19062306a36Sopenharmony_cinouveau_validate_decode_mod(struct nouveau_drm *drm, 19162306a36Sopenharmony_ci uint64_t modifier, 19262306a36Sopenharmony_ci uint32_t *tile_mode, 19362306a36Sopenharmony_ci uint8_t *kind) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct nouveau_display *disp = nouveau_display(drm->dev); 19662306a36Sopenharmony_ci int mod; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) { 19962306a36Sopenharmony_ci return -EINVAL; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci BUG_ON(!disp->format_modifiers); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci for (mod = 0; 20562306a36Sopenharmony_ci (disp->format_modifiers[mod] != DRM_FORMAT_MOD_INVALID) && 20662306a36Sopenharmony_ci (disp->format_modifiers[mod] != modifier); 20762306a36Sopenharmony_ci mod++); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (disp->format_modifiers[mod] == DRM_FORMAT_MOD_INVALID) { 21062306a36Sopenharmony_ci for (mod = 0; 21162306a36Sopenharmony_ci (legacy_modifiers[mod] != DRM_FORMAT_MOD_INVALID) && 21262306a36Sopenharmony_ci (legacy_modifiers[mod] != modifier); 21362306a36Sopenharmony_ci mod++); 21462306a36Sopenharmony_ci if (legacy_modifiers[mod] == DRM_FORMAT_MOD_INVALID) 21562306a36Sopenharmony_ci return -EINVAL; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci nouveau_decode_mod(drm, modifier, tile_mode, kind); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic inline uint32_t 22462306a36Sopenharmony_cinouveau_get_width_in_blocks(uint32_t stride) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci /* GOBs per block in the x direction is always one, and GOBs are 22762306a36Sopenharmony_ci * 64 bytes wide 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci static const uint32_t log_block_width = 6; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return (stride + (1 << log_block_width) - 1) >> log_block_width; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic inline uint32_t 23562306a36Sopenharmony_cinouveau_get_height_in_blocks(struct nouveau_drm *drm, 23662306a36Sopenharmony_ci uint32_t height, 23762306a36Sopenharmony_ci uint32_t log_block_height_in_gobs) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci uint32_t log_gob_height; 24062306a36Sopenharmony_ci uint32_t log_block_height; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci BUG_ON(drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI) 24562306a36Sopenharmony_ci log_gob_height = 2; 24662306a36Sopenharmony_ci else 24762306a36Sopenharmony_ci log_gob_height = 3; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci log_block_height = log_block_height_in_gobs + log_gob_height; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return (height + (1 << log_block_height) - 1) >> log_block_height; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int 25562306a36Sopenharmony_cinouveau_check_bl_size(struct nouveau_drm *drm, struct nouveau_bo *nvbo, 25662306a36Sopenharmony_ci uint32_t offset, uint32_t stride, uint32_t h, 25762306a36Sopenharmony_ci uint32_t tile_mode) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci uint32_t gob_size, bw, bh; 26062306a36Sopenharmony_ci uint64_t bl_size; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci BUG_ON(drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (drm->client.device.info.chipset >= 0xc0) { 26562306a36Sopenharmony_ci if (tile_mode & 0xF) 26662306a36Sopenharmony_ci return -EINVAL; 26762306a36Sopenharmony_ci tile_mode >>= 4; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (tile_mode & 0xFFFFFFF0) 27162306a36Sopenharmony_ci return -EINVAL; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI) 27462306a36Sopenharmony_ci gob_size = 256; 27562306a36Sopenharmony_ci else 27662306a36Sopenharmony_ci gob_size = 512; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci bw = nouveau_get_width_in_blocks(stride); 27962306a36Sopenharmony_ci bh = nouveau_get_height_in_blocks(drm, h, tile_mode); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci bl_size = bw * bh * (1 << tile_mode) * gob_size; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci DRM_DEBUG_KMS("offset=%u stride=%u h=%u tile_mode=0x%02x bw=%u bh=%u gob_size=%u bl_size=%llu size=%zu\n", 28462306a36Sopenharmony_ci offset, stride, h, tile_mode, bw, bh, gob_size, bl_size, 28562306a36Sopenharmony_ci nvbo->bo.base.size); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (bl_size + offset > nvbo->bo.base.size) 28862306a36Sopenharmony_ci return -ERANGE; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ciint 29462306a36Sopenharmony_cinouveau_framebuffer_new(struct drm_device *dev, 29562306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd, 29662306a36Sopenharmony_ci struct drm_gem_object *gem, 29762306a36Sopenharmony_ci struct drm_framebuffer **pfb) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 30062306a36Sopenharmony_ci struct nouveau_bo *nvbo = nouveau_gem_object(gem); 30162306a36Sopenharmony_ci struct drm_framebuffer *fb; 30262306a36Sopenharmony_ci const struct drm_format_info *info; 30362306a36Sopenharmony_ci unsigned int height, i; 30462306a36Sopenharmony_ci uint32_t tile_mode; 30562306a36Sopenharmony_ci uint8_t kind; 30662306a36Sopenharmony_ci int ret; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* YUV overlays have special requirements pre-NV50 */ 30962306a36Sopenharmony_ci if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA && 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci (mode_cmd->pixel_format == DRM_FORMAT_YUYV || 31262306a36Sopenharmony_ci mode_cmd->pixel_format == DRM_FORMAT_UYVY || 31362306a36Sopenharmony_ci mode_cmd->pixel_format == DRM_FORMAT_NV12 || 31462306a36Sopenharmony_ci mode_cmd->pixel_format == DRM_FORMAT_NV21) && 31562306a36Sopenharmony_ci (mode_cmd->pitches[0] & 0x3f || /* align 64 */ 31662306a36Sopenharmony_ci mode_cmd->pitches[0] >= 0x10000 || /* at most 64k pitch */ 31762306a36Sopenharmony_ci (mode_cmd->pitches[1] && /* pitches for planes must match */ 31862306a36Sopenharmony_ci mode_cmd->pitches[0] != mode_cmd->pitches[1]))) { 31962306a36Sopenharmony_ci DRM_DEBUG_KMS("Unsuitable framebuffer: format: %p4cc; pitches: 0x%x\n 0x%x\n", 32062306a36Sopenharmony_ci &mode_cmd->pixel_format, 32162306a36Sopenharmony_ci mode_cmd->pitches[0], mode_cmd->pitches[1]); 32262306a36Sopenharmony_ci return -EINVAL; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (mode_cmd->flags & DRM_MODE_FB_MODIFIERS) { 32662306a36Sopenharmony_ci if (nouveau_validate_decode_mod(drm, mode_cmd->modifier[0], 32762306a36Sopenharmony_ci &tile_mode, &kind)) { 32862306a36Sopenharmony_ci DRM_DEBUG_KMS("Unsupported modifier: 0x%llx\n", 32962306a36Sopenharmony_ci mode_cmd->modifier[0]); 33062306a36Sopenharmony_ci return -EINVAL; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci } else { 33362306a36Sopenharmony_ci tile_mode = nvbo->mode; 33462306a36Sopenharmony_ci kind = nvbo->kind; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci info = drm_get_format_info(dev, mode_cmd); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci for (i = 0; i < info->num_planes; i++) { 34062306a36Sopenharmony_ci height = drm_format_info_plane_height(info, 34162306a36Sopenharmony_ci mode_cmd->height, 34262306a36Sopenharmony_ci i); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (kind) { 34562306a36Sopenharmony_ci ret = nouveau_check_bl_size(drm, nvbo, 34662306a36Sopenharmony_ci mode_cmd->offsets[i], 34762306a36Sopenharmony_ci mode_cmd->pitches[i], 34862306a36Sopenharmony_ci height, tile_mode); 34962306a36Sopenharmony_ci if (ret) 35062306a36Sopenharmony_ci return ret; 35162306a36Sopenharmony_ci } else { 35262306a36Sopenharmony_ci uint32_t size = mode_cmd->pitches[i] * height; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (size + mode_cmd->offsets[i] > nvbo->bo.base.size) 35562306a36Sopenharmony_ci return -ERANGE; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (!(fb = *pfb = kzalloc(sizeof(*fb), GFP_KERNEL))) 36062306a36Sopenharmony_ci return -ENOMEM; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd); 36362306a36Sopenharmony_ci fb->obj[0] = gem; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs); 36662306a36Sopenharmony_ci if (ret) 36762306a36Sopenharmony_ci kfree(fb); 36862306a36Sopenharmony_ci return ret; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistruct drm_framebuffer * 37262306a36Sopenharmony_cinouveau_user_framebuffer_create(struct drm_device *dev, 37362306a36Sopenharmony_ci struct drm_file *file_priv, 37462306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct drm_framebuffer *fb; 37762306a36Sopenharmony_ci struct drm_gem_object *gem; 37862306a36Sopenharmony_ci int ret; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci gem = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]); 38162306a36Sopenharmony_ci if (!gem) 38262306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci ret = nouveau_framebuffer_new(dev, mode_cmd, gem, &fb); 38562306a36Sopenharmony_ci if (ret == 0) 38662306a36Sopenharmony_ci return fb; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci drm_gem_object_put(gem); 38962306a36Sopenharmony_ci return ERR_PTR(ret); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic const struct drm_mode_config_funcs nouveau_mode_config_funcs = { 39362306a36Sopenharmony_ci .fb_create = nouveau_user_framebuffer_create, 39462306a36Sopenharmony_ci .output_poll_changed = drm_fb_helper_output_poll_changed, 39562306a36Sopenharmony_ci}; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistruct nouveau_drm_prop_enum_list { 39962306a36Sopenharmony_ci u8 gen_mask; 40062306a36Sopenharmony_ci int type; 40162306a36Sopenharmony_ci char *name; 40262306a36Sopenharmony_ci}; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic struct nouveau_drm_prop_enum_list underscan[] = { 40562306a36Sopenharmony_ci { 6, UNDERSCAN_AUTO, "auto" }, 40662306a36Sopenharmony_ci { 6, UNDERSCAN_OFF, "off" }, 40762306a36Sopenharmony_ci { 6, UNDERSCAN_ON, "on" }, 40862306a36Sopenharmony_ci {} 40962306a36Sopenharmony_ci}; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic struct nouveau_drm_prop_enum_list dither_mode[] = { 41262306a36Sopenharmony_ci { 7, DITHERING_MODE_AUTO, "auto" }, 41362306a36Sopenharmony_ci { 7, DITHERING_MODE_OFF, "off" }, 41462306a36Sopenharmony_ci { 1, DITHERING_MODE_ON, "on" }, 41562306a36Sopenharmony_ci { 6, DITHERING_MODE_STATIC2X2, "static 2x2" }, 41662306a36Sopenharmony_ci { 6, DITHERING_MODE_DYNAMIC2X2, "dynamic 2x2" }, 41762306a36Sopenharmony_ci { 4, DITHERING_MODE_TEMPORAL, "temporal" }, 41862306a36Sopenharmony_ci {} 41962306a36Sopenharmony_ci}; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic struct nouveau_drm_prop_enum_list dither_depth[] = { 42262306a36Sopenharmony_ci { 6, DITHERING_DEPTH_AUTO, "auto" }, 42362306a36Sopenharmony_ci { 6, DITHERING_DEPTH_6BPC, "6 bpc" }, 42462306a36Sopenharmony_ci { 6, DITHERING_DEPTH_8BPC, "8 bpc" }, 42562306a36Sopenharmony_ci {} 42662306a36Sopenharmony_ci}; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci#define PROP_ENUM(p,gen,n,list) do { \ 42962306a36Sopenharmony_ci struct nouveau_drm_prop_enum_list *l = (list); \ 43062306a36Sopenharmony_ci int c = 0; \ 43162306a36Sopenharmony_ci while (l->gen_mask) { \ 43262306a36Sopenharmony_ci if (l->gen_mask & (1 << (gen))) \ 43362306a36Sopenharmony_ci c++; \ 43462306a36Sopenharmony_ci l++; \ 43562306a36Sopenharmony_ci } \ 43662306a36Sopenharmony_ci if (c) { \ 43762306a36Sopenharmony_ci p = drm_property_create(dev, DRM_MODE_PROP_ENUM, n, c); \ 43862306a36Sopenharmony_ci l = (list); \ 43962306a36Sopenharmony_ci while (p && l->gen_mask) { \ 44062306a36Sopenharmony_ci if (l->gen_mask & (1 << (gen))) { \ 44162306a36Sopenharmony_ci drm_property_add_enum(p, l->type, l->name); \ 44262306a36Sopenharmony_ci } \ 44362306a36Sopenharmony_ci l++; \ 44462306a36Sopenharmony_ci } \ 44562306a36Sopenharmony_ci } \ 44662306a36Sopenharmony_ci} while(0) 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_civoid 44962306a36Sopenharmony_cinouveau_display_hpd_resume(struct drm_device *dev) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci spin_lock_irq(&drm->hpd_lock); 45462306a36Sopenharmony_ci drm->hpd_pending = ~0; 45562306a36Sopenharmony_ci spin_unlock_irq(&drm->hpd_lock); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci schedule_work(&drm->hpd_work); 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic void 46162306a36Sopenharmony_cinouveau_display_hpd_work(struct work_struct *work) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct nouveau_drm *drm = container_of(work, typeof(*drm), hpd_work); 46462306a36Sopenharmony_ci struct drm_device *dev = drm->dev; 46562306a36Sopenharmony_ci struct drm_connector *connector; 46662306a36Sopenharmony_ci struct drm_connector_list_iter conn_iter; 46762306a36Sopenharmony_ci u32 pending; 46862306a36Sopenharmony_ci int changed = 0; 46962306a36Sopenharmony_ci struct drm_connector *first_changed_connector = NULL; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci pm_runtime_get_sync(dev->dev); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci spin_lock_irq(&drm->hpd_lock); 47462306a36Sopenharmony_ci pending = drm->hpd_pending; 47562306a36Sopenharmony_ci drm->hpd_pending = 0; 47662306a36Sopenharmony_ci spin_unlock_irq(&drm->hpd_lock); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* Nothing to do, exit early without updating the last busy counter */ 47962306a36Sopenharmony_ci if (!pending) 48062306a36Sopenharmony_ci goto noop; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci mutex_lock(&dev->mode_config.mutex); 48362306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) { 48662306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 48762306a36Sopenharmony_ci enum drm_connector_status old_status = connector->status; 48862306a36Sopenharmony_ci u64 bits, old_epoch_counter = connector->epoch_counter; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (!(pending & drm_connector_mask(connector))) 49162306a36Sopenharmony_ci continue; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci spin_lock_irq(&drm->hpd_lock); 49462306a36Sopenharmony_ci bits = nv_connector->hpd_pending; 49562306a36Sopenharmony_ci nv_connector->hpd_pending = 0; 49662306a36Sopenharmony_ci spin_unlock_irq(&drm->hpd_lock); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci drm_dbg_kms(dev, "[CONNECTOR:%d:%s] plug:%d unplug:%d irq:%d\n", 49962306a36Sopenharmony_ci connector->base.id, connector->name, 50062306a36Sopenharmony_ci !!(bits & NVIF_CONN_EVENT_V0_PLUG), 50162306a36Sopenharmony_ci !!(bits & NVIF_CONN_EVENT_V0_UNPLUG), 50262306a36Sopenharmony_ci !!(bits & NVIF_CONN_EVENT_V0_IRQ)); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (bits & NVIF_CONN_EVENT_V0_IRQ) { 50562306a36Sopenharmony_ci if (nouveau_dp_link_check(nv_connector)) 50662306a36Sopenharmony_ci continue; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci connector->status = drm_helper_probe_detect(connector, NULL, false); 51062306a36Sopenharmony_ci if (old_epoch_counter == connector->epoch_counter) 51162306a36Sopenharmony_ci continue; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci changed++; 51462306a36Sopenharmony_ci if (!first_changed_connector) { 51562306a36Sopenharmony_ci drm_connector_get(connector); 51662306a36Sopenharmony_ci first_changed_connector = connector; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci drm_dbg_kms(dev, "[CONNECTOR:%d:%s] status updated from %s to %s (epoch counter %llu->%llu)\n", 52062306a36Sopenharmony_ci connector->base.id, connector->name, 52162306a36Sopenharmony_ci drm_get_connector_status_name(old_status), 52262306a36Sopenharmony_ci drm_get_connector_status_name(connector->status), 52362306a36Sopenharmony_ci old_epoch_counter, connector->epoch_counter); 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 52762306a36Sopenharmony_ci mutex_unlock(&dev->mode_config.mutex); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (changed == 1) 53062306a36Sopenharmony_ci drm_kms_helper_connector_hotplug_event(first_changed_connector); 53162306a36Sopenharmony_ci else if (changed > 0) 53262306a36Sopenharmony_ci drm_kms_helper_hotplug_event(dev); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (first_changed_connector) 53562306a36Sopenharmony_ci drm_connector_put(first_changed_connector); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci pm_runtime_mark_last_busy(drm->dev->dev); 53862306a36Sopenharmony_cinoop: 53962306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci#ifdef CONFIG_ACPI 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic int 54562306a36Sopenharmony_cinouveau_display_acpi_ntfy(struct notifier_block *nb, unsigned long val, 54662306a36Sopenharmony_ci void *data) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct nouveau_drm *drm = container_of(nb, typeof(*drm), acpi_nb); 54962306a36Sopenharmony_ci struct acpi_bus_event *info = data; 55062306a36Sopenharmony_ci int ret; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (!strcmp(info->device_class, ACPI_VIDEO_CLASS)) { 55362306a36Sopenharmony_ci if (info->type == ACPI_VIDEO_NOTIFY_PROBE) { 55462306a36Sopenharmony_ci ret = pm_runtime_get(drm->dev->dev); 55562306a36Sopenharmony_ci if (ret == 1 || ret == -EACCES) { 55662306a36Sopenharmony_ci /* If the GPU is already awake, or in a state 55762306a36Sopenharmony_ci * where we can't wake it up, it can handle 55862306a36Sopenharmony_ci * it's own hotplug events. 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_ci pm_runtime_put_autosuspend(drm->dev->dev); 56162306a36Sopenharmony_ci } else if (ret == 0 || ret == -EINPROGRESS) { 56262306a36Sopenharmony_ci /* We've started resuming the GPU already, so 56362306a36Sopenharmony_ci * it will handle scheduling a full reprobe 56462306a36Sopenharmony_ci * itself 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci NV_DEBUG(drm, "ACPI requested connector reprobe\n"); 56762306a36Sopenharmony_ci pm_runtime_put_noidle(drm->dev->dev); 56862306a36Sopenharmony_ci } else { 56962306a36Sopenharmony_ci NV_WARN(drm, "Dropped ACPI reprobe event due to RPM error: %d\n", 57062306a36Sopenharmony_ci ret); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* acpi-video should not generate keypresses for this */ 57462306a36Sopenharmony_ci return NOTIFY_BAD; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci return NOTIFY_DONE; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci#endif 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ciint 58362306a36Sopenharmony_cinouveau_display_init(struct drm_device *dev, bool resume, bool runtime) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct nouveau_display *disp = nouveau_display(dev); 58662306a36Sopenharmony_ci struct drm_connector *connector; 58762306a36Sopenharmony_ci struct drm_connector_list_iter conn_iter; 58862306a36Sopenharmony_ci int ret; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* 59162306a36Sopenharmony_ci * Enable hotplug interrupts (done as early as possible, since we need 59262306a36Sopenharmony_ci * them for MST) 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 59562306a36Sopenharmony_ci nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) { 59662306a36Sopenharmony_ci struct nouveau_connector *conn = nouveau_connector(connector); 59762306a36Sopenharmony_ci nvif_event_allow(&conn->hpd); 59862306a36Sopenharmony_ci nvif_event_allow(&conn->irq); 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci ret = disp->init(dev, resume, runtime); 60362306a36Sopenharmony_ci if (ret) 60462306a36Sopenharmony_ci return ret; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* enable connector detection and polling for connectors without HPD 60762306a36Sopenharmony_ci * support 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_ci drm_kms_helper_poll_enable(dev); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci return ret; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_civoid 61562306a36Sopenharmony_cinouveau_display_fini(struct drm_device *dev, bool suspend, bool runtime) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci struct nouveau_display *disp = nouveau_display(dev); 61862306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 61962306a36Sopenharmony_ci struct drm_connector *connector; 62062306a36Sopenharmony_ci struct drm_connector_list_iter conn_iter; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (!suspend) { 62362306a36Sopenharmony_ci if (drm_drv_uses_atomic_modeset(dev)) 62462306a36Sopenharmony_ci drm_atomic_helper_shutdown(dev); 62562306a36Sopenharmony_ci else 62662306a36Sopenharmony_ci drm_helper_force_disable_all(dev); 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* disable hotplug interrupts */ 63062306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 63162306a36Sopenharmony_ci nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) { 63262306a36Sopenharmony_ci struct nouveau_connector *conn = nouveau_connector(connector); 63362306a36Sopenharmony_ci nvif_event_block(&conn->irq); 63462306a36Sopenharmony_ci nvif_event_block(&conn->hpd); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (!runtime) 63962306a36Sopenharmony_ci cancel_work_sync(&drm->hpd_work); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci drm_kms_helper_poll_disable(dev); 64262306a36Sopenharmony_ci disp->fini(dev, runtime, suspend); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic void 64662306a36Sopenharmony_cinouveau_display_create_properties(struct drm_device *dev) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct nouveau_display *disp = nouveau_display(dev); 64962306a36Sopenharmony_ci int gen; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (disp->disp.object.oclass < NV50_DISP) 65262306a36Sopenharmony_ci gen = 0; 65362306a36Sopenharmony_ci else 65462306a36Sopenharmony_ci if (disp->disp.object.oclass < GF110_DISP) 65562306a36Sopenharmony_ci gen = 1; 65662306a36Sopenharmony_ci else 65762306a36Sopenharmony_ci gen = 2; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci PROP_ENUM(disp->dithering_mode, gen, "dithering mode", dither_mode); 66062306a36Sopenharmony_ci PROP_ENUM(disp->dithering_depth, gen, "dithering depth", dither_depth); 66162306a36Sopenharmony_ci PROP_ENUM(disp->underscan_property, gen, "underscan", underscan); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci disp->underscan_hborder_property = 66462306a36Sopenharmony_ci drm_property_create_range(dev, 0, "underscan hborder", 0, 128); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci disp->underscan_vborder_property = 66762306a36Sopenharmony_ci drm_property_create_range(dev, 0, "underscan vborder", 0, 128); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (gen < 1) 67062306a36Sopenharmony_ci return; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* -90..+90 */ 67362306a36Sopenharmony_ci disp->vibrant_hue_property = 67462306a36Sopenharmony_ci drm_property_create_range(dev, 0, "vibrant hue", 0, 180); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* -100..+100 */ 67762306a36Sopenharmony_ci disp->color_vibrance_property = 67862306a36Sopenharmony_ci drm_property_create_range(dev, 0, "color vibrance", 0, 200); 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ciint 68262306a36Sopenharmony_cinouveau_display_create(struct drm_device *dev) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 68562306a36Sopenharmony_ci struct nouveau_display *disp; 68662306a36Sopenharmony_ci int ret; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci disp = drm->display = kzalloc(sizeof(*disp), GFP_KERNEL); 68962306a36Sopenharmony_ci if (!disp) 69062306a36Sopenharmony_ci return -ENOMEM; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci drm_mode_config_init(dev); 69362306a36Sopenharmony_ci drm_mode_create_scaling_mode_property(dev); 69462306a36Sopenharmony_ci drm_mode_create_dvi_i_properties(dev); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci dev->mode_config.funcs = &nouveau_mode_config_funcs; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci dev->mode_config.min_width = 0; 69962306a36Sopenharmony_ci dev->mode_config.min_height = 0; 70062306a36Sopenharmony_ci if (drm->client.device.info.family < NV_DEVICE_INFO_V0_CELSIUS) { 70162306a36Sopenharmony_ci dev->mode_config.max_width = 2048; 70262306a36Sopenharmony_ci dev->mode_config.max_height = 2048; 70362306a36Sopenharmony_ci } else 70462306a36Sopenharmony_ci if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) { 70562306a36Sopenharmony_ci dev->mode_config.max_width = 4096; 70662306a36Sopenharmony_ci dev->mode_config.max_height = 4096; 70762306a36Sopenharmony_ci } else 70862306a36Sopenharmony_ci if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI) { 70962306a36Sopenharmony_ci dev->mode_config.max_width = 8192; 71062306a36Sopenharmony_ci dev->mode_config.max_height = 8192; 71162306a36Sopenharmony_ci } else { 71262306a36Sopenharmony_ci dev->mode_config.max_width = 16384; 71362306a36Sopenharmony_ci dev->mode_config.max_height = 16384; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci dev->mode_config.preferred_depth = 24; 71762306a36Sopenharmony_ci dev->mode_config.prefer_shadow = 1; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (drm->client.device.info.chipset < 0x11) 72062306a36Sopenharmony_ci dev->mode_config.async_page_flip = false; 72162306a36Sopenharmony_ci else 72262306a36Sopenharmony_ci dev->mode_config.async_page_flip = true; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci drm_kms_helper_poll_init(dev); 72562306a36Sopenharmony_ci drm_kms_helper_poll_disable(dev); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (nouveau_modeset != 2 && drm->vbios.dcb.entries) { 72862306a36Sopenharmony_ci ret = nvif_disp_ctor(&drm->client.device, "kmsDisp", 0, 72962306a36Sopenharmony_ci &disp->disp); 73062306a36Sopenharmony_ci if (ret == 0) { 73162306a36Sopenharmony_ci nouveau_display_create_properties(dev); 73262306a36Sopenharmony_ci if (disp->disp.object.oclass < NV50_DISP) { 73362306a36Sopenharmony_ci dev->mode_config.fb_modifiers_not_supported = true; 73462306a36Sopenharmony_ci ret = nv04_display_create(dev); 73562306a36Sopenharmony_ci } else { 73662306a36Sopenharmony_ci ret = nv50_display_create(dev); 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci } else { 74062306a36Sopenharmony_ci ret = 0; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (ret) 74462306a36Sopenharmony_ci goto disp_create_err; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci drm_mode_config_reset(dev); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci if (dev->mode_config.num_crtc) { 74962306a36Sopenharmony_ci ret = drm_vblank_init(dev, dev->mode_config.num_crtc); 75062306a36Sopenharmony_ci if (ret) 75162306a36Sopenharmony_ci goto vblank_err; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (disp->disp.object.oclass >= NV50_DISP) 75462306a36Sopenharmony_ci nv50_crc_init(dev); 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci INIT_WORK(&drm->hpd_work, nouveau_display_hpd_work); 75862306a36Sopenharmony_ci spin_lock_init(&drm->hpd_lock); 75962306a36Sopenharmony_ci#ifdef CONFIG_ACPI 76062306a36Sopenharmony_ci drm->acpi_nb.notifier_call = nouveau_display_acpi_ntfy; 76162306a36Sopenharmony_ci register_acpi_notifier(&drm->acpi_nb); 76262306a36Sopenharmony_ci#endif 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci return 0; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_civblank_err: 76762306a36Sopenharmony_ci disp->dtor(dev); 76862306a36Sopenharmony_cidisp_create_err: 76962306a36Sopenharmony_ci drm_kms_helper_poll_fini(dev); 77062306a36Sopenharmony_ci drm_mode_config_cleanup(dev); 77162306a36Sopenharmony_ci return ret; 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_civoid 77562306a36Sopenharmony_cinouveau_display_destroy(struct drm_device *dev) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci struct nouveau_display *disp = nouveau_display(dev); 77862306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci#ifdef CONFIG_ACPI 78162306a36Sopenharmony_ci unregister_acpi_notifier(&drm->acpi_nb); 78262306a36Sopenharmony_ci#endif 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci drm_kms_helper_poll_fini(dev); 78562306a36Sopenharmony_ci drm_mode_config_cleanup(dev); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (disp->dtor) 78862306a36Sopenharmony_ci disp->dtor(dev); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci nvif_disp_dtor(&disp->disp); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci drm->display = NULL; 79362306a36Sopenharmony_ci kfree(disp); 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ciint 79762306a36Sopenharmony_cinouveau_display_suspend(struct drm_device *dev, bool runtime) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci struct nouveau_display *disp = nouveau_display(dev); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* Disable console. */ 80262306a36Sopenharmony_ci drm_fb_helper_set_suspend_unlocked(dev->fb_helper, true); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if (drm_drv_uses_atomic_modeset(dev)) { 80562306a36Sopenharmony_ci if (!runtime) { 80662306a36Sopenharmony_ci disp->suspend = drm_atomic_helper_suspend(dev); 80762306a36Sopenharmony_ci if (IS_ERR(disp->suspend)) { 80862306a36Sopenharmony_ci int ret = PTR_ERR(disp->suspend); 80962306a36Sopenharmony_ci disp->suspend = NULL; 81062306a36Sopenharmony_ci return ret; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci nouveau_display_fini(dev, true, runtime); 81662306a36Sopenharmony_ci return 0; 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_civoid 82062306a36Sopenharmony_cinouveau_display_resume(struct drm_device *dev, bool runtime) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci struct nouveau_display *disp = nouveau_display(dev); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci nouveau_display_init(dev, true, runtime); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (drm_drv_uses_atomic_modeset(dev)) { 82762306a36Sopenharmony_ci if (disp->suspend) { 82862306a36Sopenharmony_ci drm_atomic_helper_resume(dev, disp->suspend); 82962306a36Sopenharmony_ci disp->suspend = NULL; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* Enable console. */ 83462306a36Sopenharmony_ci drm_fb_helper_set_suspend_unlocked(dev->fb_helper, false); 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ciint 83862306a36Sopenharmony_cinouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev, 83962306a36Sopenharmony_ci struct drm_mode_create_dumb *args) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci struct nouveau_cli *cli = nouveau_cli(file_priv); 84262306a36Sopenharmony_ci struct nouveau_bo *bo; 84362306a36Sopenharmony_ci uint32_t domain; 84462306a36Sopenharmony_ci int ret; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci args->pitch = roundup(args->width * (args->bpp / 8), 256); 84762306a36Sopenharmony_ci args->size = args->pitch * args->height; 84862306a36Sopenharmony_ci args->size = roundup(args->size, PAGE_SIZE); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* Use VRAM if there is any ; otherwise fallback to system memory */ 85162306a36Sopenharmony_ci if (nouveau_drm(dev)->client.device.info.ram_size != 0) 85262306a36Sopenharmony_ci domain = NOUVEAU_GEM_DOMAIN_VRAM; 85362306a36Sopenharmony_ci else 85462306a36Sopenharmony_ci domain = NOUVEAU_GEM_DOMAIN_GART; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci ret = nouveau_gem_new(cli, args->size, 0, domain, 0, 0, &bo); 85762306a36Sopenharmony_ci if (ret) 85862306a36Sopenharmony_ci return ret; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci ret = drm_gem_handle_create(file_priv, &bo->bo.base, &args->handle); 86162306a36Sopenharmony_ci drm_gem_object_put(&bo->bo.base); 86262306a36Sopenharmony_ci return ret; 86362306a36Sopenharmony_ci} 864