Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright (C) 2008 Maarten Maathuis.
0003  * All Rights Reserved.
0004  *
0005  * Permission is hereby granted, free of charge, to any person obtaining
0006  * a copy of this software and associated documentation files (the
0007  * "Software"), to deal in the Software without restriction, including
0008  * without limitation the rights to use, copy, modify, merge, publish,
0009  * distribute, sublicense, and/or sell copies of the Software, and to
0010  * permit persons to whom the Software is furnished to do so, subject to
0011  * the following conditions:
0012  *
0013  * The above copyright notice and this permission notice (including the
0014  * next paragraph) shall be included in all copies or substantial
0015  * portions of the Software.
0016  *
0017  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
0018  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0019  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
0020  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
0021  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
0022  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
0023  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0024  *
0025  */
0026 
0027 #include <acpi/video.h>
0028 
0029 #include <drm/drm_atomic.h>
0030 #include <drm/drm_atomic_helper.h>
0031 #include <drm/drm_crtc_helper.h>
0032 #include <drm/drm_fb_helper.h>
0033 #include <drm/drm_fourcc.h>
0034 #include <drm/drm_gem_framebuffer_helper.h>
0035 #include <drm/drm_probe_helper.h>
0036 #include <drm/drm_vblank.h>
0037 
0038 #include "nouveau_fbcon.h"
0039 #include "nouveau_crtc.h"
0040 #include "nouveau_gem.h"
0041 #include "nouveau_connector.h"
0042 #include "nv50_display.h"
0043 
0044 #include <nvif/class.h>
0045 #include <nvif/cl0046.h>
0046 #include <nvif/event.h>
0047 #include <dispnv50/crc.h>
0048 
0049 int
0050 nouveau_display_vblank_enable(struct drm_crtc *crtc)
0051 {
0052     struct nouveau_crtc *nv_crtc;
0053 
0054     nv_crtc = nouveau_crtc(crtc);
0055     nvif_notify_get(&nv_crtc->vblank);
0056 
0057     return 0;
0058 }
0059 
0060 void
0061 nouveau_display_vblank_disable(struct drm_crtc *crtc)
0062 {
0063     struct nouveau_crtc *nv_crtc;
0064 
0065     nv_crtc = nouveau_crtc(crtc);
0066     nvif_notify_put(&nv_crtc->vblank);
0067 }
0068 
0069 static inline int
0070 calc(int blanks, int blanke, int total, int line)
0071 {
0072     if (blanke >= blanks) {
0073         if (line >= blanks)
0074             line -= total;
0075     } else {
0076         if (line >= blanks)
0077             line -= total;
0078         line -= blanke + 1;
0079     }
0080     return line;
0081 }
0082 
0083 static bool
0084 nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
0085                 ktime_t *stime, ktime_t *etime)
0086 {
0087     struct {
0088         struct nv04_disp_mthd_v0 base;
0089         struct nv04_disp_scanoutpos_v0 scan;
0090     } args = {
0091         .base.method = NV04_DISP_SCANOUTPOS,
0092         .base.head = nouveau_crtc(crtc)->index,
0093     };
0094     struct nouveau_display *disp = nouveau_display(crtc->dev);
0095     struct drm_vblank_crtc *vblank = &crtc->dev->vblank[drm_crtc_index(crtc)];
0096     int retry = 20;
0097     bool ret = false;
0098 
0099     do {
0100         ret = nvif_mthd(&disp->disp.object, 0, &args, sizeof(args));
0101         if (ret != 0)
0102             return false;
0103 
0104         if (args.scan.vline) {
0105             ret = true;
0106             break;
0107         }
0108 
0109         if (retry) ndelay(vblank->linedur_ns);
0110     } while (retry--);
0111 
0112     *hpos = args.scan.hline;
0113     *vpos = calc(args.scan.vblanks, args.scan.vblanke,
0114              args.scan.vtotal, args.scan.vline);
0115     if (stime) *stime = ns_to_ktime(args.scan.time[0]);
0116     if (etime) *etime = ns_to_ktime(args.scan.time[1]);
0117 
0118     return ret;
0119 }
0120 
0121 bool
0122 nouveau_display_scanoutpos(struct drm_crtc *crtc,
0123                bool in_vblank_irq, int *vpos, int *hpos,
0124                ktime_t *stime, ktime_t *etime,
0125                const struct drm_display_mode *mode)
0126 {
0127     return nouveau_display_scanoutpos_head(crtc, vpos, hpos,
0128                            stime, etime);
0129 }
0130 
0131 static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = {
0132     .destroy = drm_gem_fb_destroy,
0133     .create_handle = drm_gem_fb_create_handle,
0134 };
0135 
0136 static void
0137 nouveau_decode_mod(struct nouveau_drm *drm,
0138            uint64_t modifier,
0139            uint32_t *tile_mode,
0140            uint8_t *kind)
0141 {
0142     struct nouveau_display *disp = nouveau_display(drm->dev);
0143     BUG_ON(!tile_mode || !kind);
0144 
0145     if (modifier == DRM_FORMAT_MOD_LINEAR) {
0146         /* tile_mode will not be used in this case */
0147         *tile_mode = 0;
0148         *kind = 0;
0149     } else {
0150         /*
0151          * Extract the block height and kind from the corresponding
0152          * modifier fields.  See drm_fourcc.h for details.
0153          */
0154 
0155         if ((modifier & (0xffull << 12)) == 0ull) {
0156             /* Legacy modifier.  Translate to this dev's 'kind.' */
0157             modifier |= disp->format_modifiers[0] & (0xffull << 12);
0158         }
0159 
0160         *tile_mode = (uint32_t)(modifier & 0xF);
0161         *kind = (uint8_t)((modifier >> 12) & 0xFF);
0162 
0163         if (drm->client.device.info.chipset >= 0xc0)
0164             *tile_mode <<= 4;
0165     }
0166 }
0167 
0168 void
0169 nouveau_framebuffer_get_layout(struct drm_framebuffer *fb,
0170                    uint32_t *tile_mode,
0171                    uint8_t *kind)
0172 {
0173     if (fb->flags & DRM_MODE_FB_MODIFIERS) {
0174         struct nouveau_drm *drm = nouveau_drm(fb->dev);
0175 
0176         nouveau_decode_mod(drm, fb->modifier, tile_mode, kind);
0177     } else {
0178         const struct nouveau_bo *nvbo = nouveau_gem_object(fb->obj[0]);
0179 
0180         *tile_mode = nvbo->mode;
0181         *kind = nvbo->kind;
0182     }
0183 }
0184 
0185 static const u64 legacy_modifiers[] = {
0186     DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0),
0187     DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1),
0188     DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2),
0189     DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3),
0190     DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4),
0191     DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5),
0192     DRM_FORMAT_MOD_INVALID
0193 };
0194 
0195 static int
0196 nouveau_validate_decode_mod(struct nouveau_drm *drm,
0197                 uint64_t modifier,
0198                 uint32_t *tile_mode,
0199                 uint8_t *kind)
0200 {
0201     struct nouveau_display *disp = nouveau_display(drm->dev);
0202     int mod;
0203 
0204     if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) {
0205         return -EINVAL;
0206     }
0207 
0208     BUG_ON(!disp->format_modifiers);
0209 
0210     for (mod = 0;
0211          (disp->format_modifiers[mod] != DRM_FORMAT_MOD_INVALID) &&
0212          (disp->format_modifiers[mod] != modifier);
0213          mod++);
0214 
0215     if (disp->format_modifiers[mod] == DRM_FORMAT_MOD_INVALID) {
0216         for (mod = 0;
0217              (legacy_modifiers[mod] != DRM_FORMAT_MOD_INVALID) &&
0218              (legacy_modifiers[mod] != modifier);
0219              mod++);
0220         if (legacy_modifiers[mod] == DRM_FORMAT_MOD_INVALID)
0221             return -EINVAL;
0222     }
0223 
0224     nouveau_decode_mod(drm, modifier, tile_mode, kind);
0225 
0226     return 0;
0227 }
0228 
0229 static inline uint32_t
0230 nouveau_get_width_in_blocks(uint32_t stride)
0231 {
0232     /* GOBs per block in the x direction is always one, and GOBs are
0233      * 64 bytes wide
0234      */
0235     static const uint32_t log_block_width = 6;
0236 
0237     return (stride + (1 << log_block_width) - 1) >> log_block_width;
0238 }
0239 
0240 static inline uint32_t
0241 nouveau_get_height_in_blocks(struct nouveau_drm *drm,
0242                  uint32_t height,
0243                  uint32_t log_block_height_in_gobs)
0244 {
0245     uint32_t log_gob_height;
0246     uint32_t log_block_height;
0247 
0248     BUG_ON(drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA);
0249 
0250     if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI)
0251         log_gob_height = 2;
0252     else
0253         log_gob_height = 3;
0254 
0255     log_block_height = log_block_height_in_gobs + log_gob_height;
0256 
0257     return (height + (1 << log_block_height) - 1) >> log_block_height;
0258 }
0259 
0260 static int
0261 nouveau_check_bl_size(struct nouveau_drm *drm, struct nouveau_bo *nvbo,
0262               uint32_t offset, uint32_t stride, uint32_t h,
0263               uint32_t tile_mode)
0264 {
0265     uint32_t gob_size, bw, bh;
0266     uint64_t bl_size;
0267 
0268     BUG_ON(drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA);
0269 
0270     if (drm->client.device.info.chipset >= 0xc0) {
0271         if (tile_mode & 0xF)
0272             return -EINVAL;
0273         tile_mode >>= 4;
0274     }
0275 
0276     if (tile_mode & 0xFFFFFFF0)
0277         return -EINVAL;
0278 
0279     if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI)
0280         gob_size = 256;
0281     else
0282         gob_size = 512;
0283 
0284     bw = nouveau_get_width_in_blocks(stride);
0285     bh = nouveau_get_height_in_blocks(drm, h, tile_mode);
0286 
0287     bl_size = bw * bh * (1 << tile_mode) * gob_size;
0288 
0289     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",
0290               offset, stride, h, tile_mode, bw, bh, gob_size, bl_size,
0291               nvbo->bo.base.size);
0292 
0293     if (bl_size + offset > nvbo->bo.base.size)
0294         return -ERANGE;
0295 
0296     return 0;
0297 }
0298 
0299 int
0300 nouveau_framebuffer_new(struct drm_device *dev,
0301             const struct drm_mode_fb_cmd2 *mode_cmd,
0302             struct drm_gem_object *gem,
0303             struct drm_framebuffer **pfb)
0304 {
0305     struct nouveau_drm *drm = nouveau_drm(dev);
0306     struct nouveau_bo *nvbo = nouveau_gem_object(gem);
0307     struct drm_framebuffer *fb;
0308     const struct drm_format_info *info;
0309     unsigned int height, i;
0310     uint32_t tile_mode;
0311     uint8_t kind;
0312     int ret;
0313 
0314         /* YUV overlays have special requirements pre-NV50 */
0315     if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA &&
0316 
0317         (mode_cmd->pixel_format == DRM_FORMAT_YUYV ||
0318          mode_cmd->pixel_format == DRM_FORMAT_UYVY ||
0319          mode_cmd->pixel_format == DRM_FORMAT_NV12 ||
0320          mode_cmd->pixel_format == DRM_FORMAT_NV21) &&
0321         (mode_cmd->pitches[0] & 0x3f || /* align 64 */
0322          mode_cmd->pitches[0] >= 0x10000 || /* at most 64k pitch */
0323          (mode_cmd->pitches[1] && /* pitches for planes must match */
0324           mode_cmd->pitches[0] != mode_cmd->pitches[1]))) {
0325         DRM_DEBUG_KMS("Unsuitable framebuffer: format: %p4cc; pitches: 0x%x\n 0x%x\n",
0326                   &mode_cmd->pixel_format,
0327                   mode_cmd->pitches[0], mode_cmd->pitches[1]);
0328         return -EINVAL;
0329     }
0330 
0331     if (mode_cmd->flags & DRM_MODE_FB_MODIFIERS) {
0332         if (nouveau_validate_decode_mod(drm, mode_cmd->modifier[0],
0333                         &tile_mode, &kind)) {
0334             DRM_DEBUG_KMS("Unsupported modifier: 0x%llx\n",
0335                       mode_cmd->modifier[0]);
0336             return -EINVAL;
0337         }
0338     } else {
0339         tile_mode = nvbo->mode;
0340         kind = nvbo->kind;
0341     }
0342 
0343     info = drm_get_format_info(dev, mode_cmd);
0344 
0345     for (i = 0; i < info->num_planes; i++) {
0346         height = drm_format_info_plane_height(info,
0347                               mode_cmd->height,
0348                               i);
0349 
0350         if (kind) {
0351             ret = nouveau_check_bl_size(drm, nvbo,
0352                             mode_cmd->offsets[i],
0353                             mode_cmd->pitches[i],
0354                             height, tile_mode);
0355             if (ret)
0356                 return ret;
0357         } else {
0358             uint32_t size = mode_cmd->pitches[i] * height;
0359 
0360             if (size + mode_cmd->offsets[i] > nvbo->bo.base.size)
0361                 return -ERANGE;
0362         }
0363     }
0364 
0365     if (!(fb = *pfb = kzalloc(sizeof(*fb), GFP_KERNEL)))
0366         return -ENOMEM;
0367 
0368     drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
0369     fb->obj[0] = gem;
0370 
0371     ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs);
0372     if (ret)
0373         kfree(fb);
0374     return ret;
0375 }
0376 
0377 struct drm_framebuffer *
0378 nouveau_user_framebuffer_create(struct drm_device *dev,
0379                 struct drm_file *file_priv,
0380                 const struct drm_mode_fb_cmd2 *mode_cmd)
0381 {
0382     struct drm_framebuffer *fb;
0383     struct drm_gem_object *gem;
0384     int ret;
0385 
0386     gem = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]);
0387     if (!gem)
0388         return ERR_PTR(-ENOENT);
0389 
0390     ret = nouveau_framebuffer_new(dev, mode_cmd, gem, &fb);
0391     if (ret == 0)
0392         return fb;
0393 
0394     drm_gem_object_put(gem);
0395     return ERR_PTR(ret);
0396 }
0397 
0398 static const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
0399     .fb_create = nouveau_user_framebuffer_create,
0400     .output_poll_changed = nouveau_fbcon_output_poll_changed,
0401 };
0402 
0403 
0404 struct nouveau_drm_prop_enum_list {
0405     u8 gen_mask;
0406     int type;
0407     char *name;
0408 };
0409 
0410 static struct nouveau_drm_prop_enum_list underscan[] = {
0411     { 6, UNDERSCAN_AUTO, "auto" },
0412     { 6, UNDERSCAN_OFF, "off" },
0413     { 6, UNDERSCAN_ON, "on" },
0414     {}
0415 };
0416 
0417 static struct nouveau_drm_prop_enum_list dither_mode[] = {
0418     { 7, DITHERING_MODE_AUTO, "auto" },
0419     { 7, DITHERING_MODE_OFF, "off" },
0420     { 1, DITHERING_MODE_ON, "on" },
0421     { 6, DITHERING_MODE_STATIC2X2, "static 2x2" },
0422     { 6, DITHERING_MODE_DYNAMIC2X2, "dynamic 2x2" },
0423     { 4, DITHERING_MODE_TEMPORAL, "temporal" },
0424     {}
0425 };
0426 
0427 static struct nouveau_drm_prop_enum_list dither_depth[] = {
0428     { 6, DITHERING_DEPTH_AUTO, "auto" },
0429     { 6, DITHERING_DEPTH_6BPC, "6 bpc" },
0430     { 6, DITHERING_DEPTH_8BPC, "8 bpc" },
0431     {}
0432 };
0433 
0434 #define PROP_ENUM(p,gen,n,list) do {                                           \
0435     struct nouveau_drm_prop_enum_list *l = (list);                         \
0436     int c = 0;                                                             \
0437     while (l->gen_mask) {                                                  \
0438         if (l->gen_mask & (1 << (gen)))                                \
0439             c++;                                                   \
0440         l++;                                                           \
0441     }                                                                      \
0442     if (c) {                                                               \
0443         p = drm_property_create(dev, DRM_MODE_PROP_ENUM, n, c);        \
0444         l = (list);                                                    \
0445         while (p && l->gen_mask) {                                     \
0446             if (l->gen_mask & (1 << (gen))) {                      \
0447                 drm_property_add_enum(p, l->type, l->name);    \
0448             }                                                      \
0449             l++;                                                   \
0450         }                                                              \
0451     }                                                                      \
0452 } while(0)
0453 
0454 void
0455 nouveau_display_hpd_resume(struct drm_device *dev)
0456 {
0457     struct nouveau_drm *drm = nouveau_drm(dev);
0458 
0459     mutex_lock(&drm->hpd_lock);
0460     drm->hpd_pending = ~0;
0461     mutex_unlock(&drm->hpd_lock);
0462 
0463     schedule_work(&drm->hpd_work);
0464 }
0465 
0466 static void
0467 nouveau_display_hpd_work(struct work_struct *work)
0468 {
0469     struct nouveau_drm *drm = container_of(work, typeof(*drm), hpd_work);
0470     struct drm_device *dev = drm->dev;
0471     struct drm_connector *connector;
0472     struct drm_connector_list_iter conn_iter;
0473     u32 pending;
0474     bool changed = false;
0475 
0476     pm_runtime_get_sync(dev->dev);
0477 
0478     mutex_lock(&drm->hpd_lock);
0479     pending = drm->hpd_pending;
0480     drm->hpd_pending = 0;
0481     mutex_unlock(&drm->hpd_lock);
0482 
0483     /* Nothing to do, exit early without updating the last busy counter */
0484     if (!pending)
0485         goto noop;
0486 
0487     mutex_lock(&dev->mode_config.mutex);
0488     drm_connector_list_iter_begin(dev, &conn_iter);
0489 
0490     nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) {
0491         enum drm_connector_status old_status = connector->status;
0492         u64 old_epoch_counter = connector->epoch_counter;
0493 
0494         if (!(pending & drm_connector_mask(connector)))
0495             continue;
0496 
0497         connector->status = drm_helper_probe_detect(connector, NULL,
0498                                 false);
0499         if (old_epoch_counter == connector->epoch_counter)
0500             continue;
0501 
0502         changed = true;
0503         drm_dbg_kms(dev, "[CONNECTOR:%d:%s] status updated from %s to %s (epoch counter %llu->%llu)\n",
0504                 connector->base.id, connector->name,
0505                 drm_get_connector_status_name(old_status),
0506                 drm_get_connector_status_name(connector->status),
0507                 old_epoch_counter, connector->epoch_counter);
0508     }
0509 
0510     drm_connector_list_iter_end(&conn_iter);
0511     mutex_unlock(&dev->mode_config.mutex);
0512 
0513     if (changed)
0514         drm_kms_helper_hotplug_event(dev);
0515 
0516     pm_runtime_mark_last_busy(drm->dev->dev);
0517 noop:
0518     pm_runtime_put_autosuspend(dev->dev);
0519 }
0520 
0521 #ifdef CONFIG_ACPI
0522 
0523 static int
0524 nouveau_display_acpi_ntfy(struct notifier_block *nb, unsigned long val,
0525               void *data)
0526 {
0527     struct nouveau_drm *drm = container_of(nb, typeof(*drm), acpi_nb);
0528     struct acpi_bus_event *info = data;
0529     int ret;
0530 
0531     if (!strcmp(info->device_class, ACPI_VIDEO_CLASS)) {
0532         if (info->type == ACPI_VIDEO_NOTIFY_PROBE) {
0533             ret = pm_runtime_get(drm->dev->dev);
0534             if (ret == 1 || ret == -EACCES) {
0535                 /* If the GPU is already awake, or in a state
0536                  * where we can't wake it up, it can handle
0537                  * it's own hotplug events.
0538                  */
0539                 pm_runtime_put_autosuspend(drm->dev->dev);
0540             } else if (ret == 0 || ret == -EINPROGRESS) {
0541                 /* We've started resuming the GPU already, so
0542                  * it will handle scheduling a full reprobe
0543                  * itself
0544                  */
0545                 NV_DEBUG(drm, "ACPI requested connector reprobe\n");
0546                 pm_runtime_put_noidle(drm->dev->dev);
0547             } else {
0548                 NV_WARN(drm, "Dropped ACPI reprobe event due to RPM error: %d\n",
0549                     ret);
0550             }
0551 
0552             /* acpi-video should not generate keypresses for this */
0553             return NOTIFY_BAD;
0554         }
0555     }
0556 
0557     return NOTIFY_DONE;
0558 }
0559 #endif
0560 
0561 int
0562 nouveau_display_init(struct drm_device *dev, bool resume, bool runtime)
0563 {
0564     struct nouveau_display *disp = nouveau_display(dev);
0565     struct drm_connector *connector;
0566     struct drm_connector_list_iter conn_iter;
0567     int ret;
0568 
0569     /*
0570      * Enable hotplug interrupts (done as early as possible, since we need
0571      * them for MST)
0572      */
0573     drm_connector_list_iter_begin(dev, &conn_iter);
0574     nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) {
0575         struct nouveau_connector *conn = nouveau_connector(connector);
0576         nvif_notify_get(&conn->hpd);
0577     }
0578     drm_connector_list_iter_end(&conn_iter);
0579 
0580     ret = disp->init(dev, resume, runtime);
0581     if (ret)
0582         return ret;
0583 
0584     /* enable connector detection and polling for connectors without HPD
0585      * support
0586      */
0587     drm_kms_helper_poll_enable(dev);
0588 
0589     return ret;
0590 }
0591 
0592 void
0593 nouveau_display_fini(struct drm_device *dev, bool suspend, bool runtime)
0594 {
0595     struct nouveau_display *disp = nouveau_display(dev);
0596     struct nouveau_drm *drm = nouveau_drm(dev);
0597     struct drm_connector *connector;
0598     struct drm_connector_list_iter conn_iter;
0599 
0600     if (!suspend) {
0601         if (drm_drv_uses_atomic_modeset(dev))
0602             drm_atomic_helper_shutdown(dev);
0603         else
0604             drm_helper_force_disable_all(dev);
0605     }
0606 
0607     /* disable hotplug interrupts */
0608     drm_connector_list_iter_begin(dev, &conn_iter);
0609     nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) {
0610         struct nouveau_connector *conn = nouveau_connector(connector);
0611         nvif_notify_put(&conn->hpd);
0612     }
0613     drm_connector_list_iter_end(&conn_iter);
0614 
0615     if (!runtime)
0616         cancel_work_sync(&drm->hpd_work);
0617 
0618     drm_kms_helper_poll_disable(dev);
0619     disp->fini(dev, runtime, suspend);
0620 }
0621 
0622 static void
0623 nouveau_display_create_properties(struct drm_device *dev)
0624 {
0625     struct nouveau_display *disp = nouveau_display(dev);
0626     int gen;
0627 
0628     if (disp->disp.object.oclass < NV50_DISP)
0629         gen = 0;
0630     else
0631     if (disp->disp.object.oclass < GF110_DISP)
0632         gen = 1;
0633     else
0634         gen = 2;
0635 
0636     PROP_ENUM(disp->dithering_mode, gen, "dithering mode", dither_mode);
0637     PROP_ENUM(disp->dithering_depth, gen, "dithering depth", dither_depth);
0638     PROP_ENUM(disp->underscan_property, gen, "underscan", underscan);
0639 
0640     disp->underscan_hborder_property =
0641         drm_property_create_range(dev, 0, "underscan hborder", 0, 128);
0642 
0643     disp->underscan_vborder_property =
0644         drm_property_create_range(dev, 0, "underscan vborder", 0, 128);
0645 
0646     if (gen < 1)
0647         return;
0648 
0649     /* -90..+90 */
0650     disp->vibrant_hue_property =
0651         drm_property_create_range(dev, 0, "vibrant hue", 0, 180);
0652 
0653     /* -100..+100 */
0654     disp->color_vibrance_property =
0655         drm_property_create_range(dev, 0, "color vibrance", 0, 200);
0656 }
0657 
0658 int
0659 nouveau_display_create(struct drm_device *dev)
0660 {
0661     struct nouveau_drm *drm = nouveau_drm(dev);
0662     struct nvkm_device *device = nvxx_device(&drm->client.device);
0663     struct nouveau_display *disp;
0664     int ret;
0665 
0666     disp = drm->display = kzalloc(sizeof(*disp), GFP_KERNEL);
0667     if (!disp)
0668         return -ENOMEM;
0669 
0670     drm_mode_config_init(dev);
0671     drm_mode_create_scaling_mode_property(dev);
0672     drm_mode_create_dvi_i_properties(dev);
0673 
0674     dev->mode_config.funcs = &nouveau_mode_config_funcs;
0675     dev->mode_config.fb_base = device->func->resource_addr(device, 1);
0676 
0677     dev->mode_config.min_width = 0;
0678     dev->mode_config.min_height = 0;
0679     if (drm->client.device.info.family < NV_DEVICE_INFO_V0_CELSIUS) {
0680         dev->mode_config.max_width = 2048;
0681         dev->mode_config.max_height = 2048;
0682     } else
0683     if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) {
0684         dev->mode_config.max_width = 4096;
0685         dev->mode_config.max_height = 4096;
0686     } else
0687     if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI) {
0688         dev->mode_config.max_width = 8192;
0689         dev->mode_config.max_height = 8192;
0690     } else {
0691         dev->mode_config.max_width = 16384;
0692         dev->mode_config.max_height = 16384;
0693     }
0694 
0695     dev->mode_config.preferred_depth = 24;
0696     dev->mode_config.prefer_shadow = 1;
0697 
0698     if (drm->client.device.info.chipset < 0x11)
0699         dev->mode_config.async_page_flip = false;
0700     else
0701         dev->mode_config.async_page_flip = true;
0702 
0703     drm_kms_helper_poll_init(dev);
0704     drm_kms_helper_poll_disable(dev);
0705 
0706     if (nouveau_modeset != 2 && drm->vbios.dcb.entries) {
0707         ret = nvif_disp_ctor(&drm->client.device, "kmsDisp", 0,
0708                      &disp->disp);
0709         if (ret == 0) {
0710             nouveau_display_create_properties(dev);
0711             if (disp->disp.object.oclass < NV50_DISP) {
0712                 dev->mode_config.fb_modifiers_not_supported = true;
0713                 ret = nv04_display_create(dev);
0714             } else {
0715                 ret = nv50_display_create(dev);
0716             }
0717         }
0718     } else {
0719         ret = 0;
0720     }
0721 
0722     if (ret)
0723         goto disp_create_err;
0724 
0725     drm_mode_config_reset(dev);
0726 
0727     if (dev->mode_config.num_crtc) {
0728         ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
0729         if (ret)
0730             goto vblank_err;
0731 
0732         if (disp->disp.object.oclass >= NV50_DISP)
0733             nv50_crc_init(dev);
0734     }
0735 
0736     INIT_WORK(&drm->hpd_work, nouveau_display_hpd_work);
0737     mutex_init(&drm->hpd_lock);
0738 #ifdef CONFIG_ACPI
0739     drm->acpi_nb.notifier_call = nouveau_display_acpi_ntfy;
0740     register_acpi_notifier(&drm->acpi_nb);
0741 #endif
0742 
0743     return 0;
0744 
0745 vblank_err:
0746     disp->dtor(dev);
0747 disp_create_err:
0748     drm_kms_helper_poll_fini(dev);
0749     drm_mode_config_cleanup(dev);
0750     return ret;
0751 }
0752 
0753 void
0754 nouveau_display_destroy(struct drm_device *dev)
0755 {
0756     struct nouveau_display *disp = nouveau_display(dev);
0757     struct nouveau_drm *drm = nouveau_drm(dev);
0758 
0759 #ifdef CONFIG_ACPI
0760     unregister_acpi_notifier(&drm->acpi_nb);
0761 #endif
0762 
0763     drm_kms_helper_poll_fini(dev);
0764     drm_mode_config_cleanup(dev);
0765 
0766     if (disp->dtor)
0767         disp->dtor(dev);
0768 
0769     nvif_disp_dtor(&disp->disp);
0770 
0771     nouveau_drm(dev)->display = NULL;
0772     mutex_destroy(&drm->hpd_lock);
0773     kfree(disp);
0774 }
0775 
0776 int
0777 nouveau_display_suspend(struct drm_device *dev, bool runtime)
0778 {
0779     struct nouveau_display *disp = nouveau_display(dev);
0780 
0781     if (drm_drv_uses_atomic_modeset(dev)) {
0782         if (!runtime) {
0783             disp->suspend = drm_atomic_helper_suspend(dev);
0784             if (IS_ERR(disp->suspend)) {
0785                 int ret = PTR_ERR(disp->suspend);
0786                 disp->suspend = NULL;
0787                 return ret;
0788             }
0789         }
0790     }
0791 
0792     nouveau_display_fini(dev, true, runtime);
0793     return 0;
0794 }
0795 
0796 void
0797 nouveau_display_resume(struct drm_device *dev, bool runtime)
0798 {
0799     struct nouveau_display *disp = nouveau_display(dev);
0800 
0801     nouveau_display_init(dev, true, runtime);
0802 
0803     if (drm_drv_uses_atomic_modeset(dev)) {
0804         if (disp->suspend) {
0805             drm_atomic_helper_resume(dev, disp->suspend);
0806             disp->suspend = NULL;
0807         }
0808         return;
0809     }
0810 }
0811 
0812 int
0813 nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
0814                 struct drm_mode_create_dumb *args)
0815 {
0816     struct nouveau_cli *cli = nouveau_cli(file_priv);
0817     struct nouveau_bo *bo;
0818     uint32_t domain;
0819     int ret;
0820 
0821     args->pitch = roundup(args->width * (args->bpp / 8), 256);
0822     args->size = args->pitch * args->height;
0823     args->size = roundup(args->size, PAGE_SIZE);
0824 
0825     /* Use VRAM if there is any ; otherwise fallback to system memory */
0826     if (nouveau_drm(dev)->client.device.info.ram_size != 0)
0827         domain = NOUVEAU_GEM_DOMAIN_VRAM;
0828     else
0829         domain = NOUVEAU_GEM_DOMAIN_GART;
0830 
0831     ret = nouveau_gem_new(cli, args->size, 0, domain, 0, 0, &bo);
0832     if (ret)
0833         return ret;
0834 
0835     ret = drm_gem_handle_create(file_priv, &bo->bo.base, &args->handle);
0836     drm_gem_object_put(&bo->bo.base);
0837     return ret;
0838 }