linux/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
<<
>>
Prefs
   1/**************************************************************************
   2 *
   3 * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
   4 * All Rights Reserved.
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a
   7 * copy of this software and associated documentation files (the
   8 * "Software"), to deal in the Software without restriction, including
   9 * without limitation the rights to use, copy, modify, merge, publish,
  10 * distribute, sub license, and/or sell copies of the Software, and to
  11 * permit persons to whom the Software is furnished to do so, subject to
  12 * the following conditions:
  13 *
  14 * The above copyright notice and this permission notice (including the
  15 * next paragraph) shall be included in all copies or substantial portions
  16 * of the Software.
  17 *
  18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
  21 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
  22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  24 * USE OR OTHER DEALINGS IN THE SOFTWARE.
  25 *
  26 **************************************************************************/
  27
  28#include "vmwgfx_kms.h"
  29
  30
  31#define vmw_crtc_to_ldu(x) \
  32        container_of(x, struct vmw_legacy_display_unit, base.crtc)
  33#define vmw_encoder_to_ldu(x) \
  34        container_of(x, struct vmw_legacy_display_unit, base.encoder)
  35#define vmw_connector_to_ldu(x) \
  36        container_of(x, struct vmw_legacy_display_unit, base.connector)
  37
  38struct vmw_legacy_display {
  39        struct list_head active;
  40
  41        unsigned num_active;
  42        unsigned last_num_active;
  43
  44        struct vmw_framebuffer *fb;
  45};
  46
  47/**
  48 * Display unit using the legacy register interface.
  49 */
  50struct vmw_legacy_display_unit {
  51        struct vmw_display_unit base;
  52
  53        struct list_head active;
  54};
  55
  56static void vmw_ldu_destroy(struct vmw_legacy_display_unit *ldu)
  57{
  58        list_del_init(&ldu->active);
  59        vmw_display_unit_cleanup(&ldu->base);
  60        kfree(ldu);
  61}
  62
  63
  64/*
  65 * Legacy Display Unit CRTC functions
  66 */
  67
  68static void vmw_ldu_crtc_destroy(struct drm_crtc *crtc)
  69{
  70        vmw_ldu_destroy(vmw_crtc_to_ldu(crtc));
  71}
  72
  73static int vmw_ldu_commit_list(struct vmw_private *dev_priv)
  74{
  75        struct vmw_legacy_display *lds = dev_priv->ldu_priv;
  76        struct vmw_legacy_display_unit *entry;
  77        struct vmw_display_unit *du = NULL;
  78        struct drm_framebuffer *fb = NULL;
  79        struct drm_crtc *crtc = NULL;
  80        int i = 0, ret;
  81
  82        /* If there is no display topology the host just assumes
  83         * that the guest will set the same layout as the host.
  84         */
  85        if (!(dev_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY)) {
  86                int w = 0, h = 0;
  87                list_for_each_entry(entry, &lds->active, active) {
  88                        crtc = &entry->base.crtc;
  89                        w = max(w, crtc->x + crtc->mode.hdisplay);
  90                        h = max(h, crtc->y + crtc->mode.vdisplay);
  91                        i++;
  92                }
  93
  94                if (crtc == NULL)
  95                        return 0;
  96                fb = entry->base.crtc.fb;
  97
  98                return vmw_kms_write_svga(dev_priv, w, h, fb->pitches[0],
  99                                          fb->bits_per_pixel, fb->depth);
 100        }
 101
 102        if (!list_empty(&lds->active)) {
 103                entry = list_entry(lds->active.next, typeof(*entry), active);
 104                fb = entry->base.crtc.fb;
 105
 106                vmw_kms_write_svga(dev_priv, fb->width, fb->height, fb->pitches[0],
 107                                   fb->bits_per_pixel, fb->depth);
 108        }
 109
 110        /* Make sure we always show something. */
 111        vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS,
 112                  lds->num_active ? lds->num_active : 1);
 113
 114        i = 0;
 115        list_for_each_entry(entry, &lds->active, active) {
 116                crtc = &entry->base.crtc;
 117
 118                vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, i);
 119                vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, !i);
 120                vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, crtc->x);
 121                vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, crtc->y);
 122                vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, crtc->mode.hdisplay);
 123                vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, crtc->mode.vdisplay);
 124                vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
 125
 126                i++;
 127        }
 128
 129        BUG_ON(i != lds->num_active);
 130
 131        lds->last_num_active = lds->num_active;
 132
 133
 134        /* Find the first du with a cursor. */
 135        list_for_each_entry(entry, &lds->active, active) {
 136                du = &entry->base;
 137
 138                if (!du->cursor_dmabuf)
 139                        continue;
 140
 141                ret = vmw_cursor_update_dmabuf(dev_priv,
 142                                               du->cursor_dmabuf,
 143                                               64, 64,
 144                                               du->hotspot_x,
 145                                               du->hotspot_y);
 146                if (ret == 0)
 147                        break;
 148
 149                DRM_ERROR("Could not update cursor image\n");
 150        }
 151
 152        return 0;
 153}
 154
 155static int vmw_ldu_del_active(struct vmw_private *vmw_priv,
 156                              struct vmw_legacy_display_unit *ldu)
 157{
 158        struct vmw_legacy_display *ld = vmw_priv->ldu_priv;
 159        if (list_empty(&ldu->active))
 160                return 0;
 161
 162        /* Must init otherwise list_empty(&ldu->active) will not work. */
 163        list_del_init(&ldu->active);
 164        if (--(ld->num_active) == 0) {
 165                BUG_ON(!ld->fb);
 166                if (ld->fb->unpin)
 167                        ld->fb->unpin(ld->fb);
 168                ld->fb = NULL;
 169        }
 170
 171        return 0;
 172}
 173
 174static int vmw_ldu_add_active(struct vmw_private *vmw_priv,
 175                              struct vmw_legacy_display_unit *ldu,
 176                              struct vmw_framebuffer *vfb)
 177{
 178        struct vmw_legacy_display *ld = vmw_priv->ldu_priv;
 179        struct vmw_legacy_display_unit *entry;
 180        struct list_head *at;
 181
 182        BUG_ON(!ld->num_active && ld->fb);
 183        if (vfb != ld->fb) {
 184                if (ld->fb && ld->fb->unpin)
 185                        ld->fb->unpin(ld->fb);
 186                if (vfb->pin)
 187                        vfb->pin(vfb);
 188                ld->fb = vfb;
 189        }
 190
 191        if (!list_empty(&ldu->active))
 192                return 0;
 193
 194        at = &ld->active;
 195        list_for_each_entry(entry, &ld->active, active) {
 196                if (entry->base.unit > ldu->base.unit)
 197                        break;
 198
 199                at = &entry->active;
 200        }
 201
 202        list_add(&ldu->active, at);
 203
 204        ld->num_active++;
 205
 206        return 0;
 207}
 208
 209static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
 210{
 211        struct vmw_private *dev_priv;
 212        struct vmw_legacy_display_unit *ldu;
 213        struct drm_connector *connector;
 214        struct drm_display_mode *mode;
 215        struct drm_encoder *encoder;
 216        struct vmw_framebuffer *vfb;
 217        struct drm_framebuffer *fb;
 218        struct drm_crtc *crtc;
 219
 220        if (!set)
 221                return -EINVAL;
 222
 223        if (!set->crtc)
 224                return -EINVAL;
 225
 226        /* get the ldu */
 227        crtc = set->crtc;
 228        ldu = vmw_crtc_to_ldu(crtc);
 229        vfb = set->fb ? vmw_framebuffer_to_vfb(set->fb) : NULL;
 230        dev_priv = vmw_priv(crtc->dev);
 231
 232        if (set->num_connectors > 1) {
 233                DRM_ERROR("to many connectors\n");
 234                return -EINVAL;
 235        }
 236
 237        if (set->num_connectors == 1 &&
 238            set->connectors[0] != &ldu->base.connector) {
 239                DRM_ERROR("connector doesn't match %p %p\n",
 240                        set->connectors[0], &ldu->base.connector);
 241                return -EINVAL;
 242        }
 243
 244        /* ldu only supports one fb active at the time */
 245        if (dev_priv->ldu_priv->fb && vfb &&
 246            !(dev_priv->ldu_priv->num_active == 1 &&
 247              !list_empty(&ldu->active)) &&
 248            dev_priv->ldu_priv->fb != vfb) {
 249                DRM_ERROR("Multiple framebuffers not supported\n");
 250                return -EINVAL;
 251        }
 252
 253        /* since they always map one to one these are safe */
 254        connector = &ldu->base.connector;
 255        encoder = &ldu->base.encoder;
 256
 257        /* should we turn the crtc off? */
 258        if (set->num_connectors == 0 || !set->mode || !set->fb) {
 259
 260                connector->encoder = NULL;
 261                encoder->crtc = NULL;
 262                crtc->fb = NULL;
 263
 264                vmw_ldu_del_active(dev_priv, ldu);
 265
 266                return vmw_ldu_commit_list(dev_priv);
 267        }
 268
 269
 270        /* we now know we want to set a mode */
 271        mode = set->mode;
 272        fb = set->fb;
 273
 274        if (set->x + mode->hdisplay > fb->width ||
 275            set->y + mode->vdisplay > fb->height) {
 276                DRM_ERROR("set outside of framebuffer\n");
 277                return -EINVAL;
 278        }
 279
 280        vmw_fb_off(dev_priv);
 281
 282        crtc->fb = fb;
 283        encoder->crtc = crtc;
 284        connector->encoder = encoder;
 285        crtc->x = set->x;
 286        crtc->y = set->y;
 287        crtc->mode = *mode;
 288
 289        vmw_ldu_add_active(dev_priv, ldu, vfb);
 290
 291        return vmw_ldu_commit_list(dev_priv);
 292}
 293
 294static struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
 295        .save = vmw_du_crtc_save,
 296        .restore = vmw_du_crtc_restore,
 297        .cursor_set = vmw_du_crtc_cursor_set,
 298        .cursor_move = vmw_du_crtc_cursor_move,
 299        .gamma_set = vmw_du_crtc_gamma_set,
 300        .destroy = vmw_ldu_crtc_destroy,
 301        .set_config = vmw_ldu_crtc_set_config,
 302};
 303
 304
 305/*
 306 * Legacy Display Unit encoder functions
 307 */
 308
 309static void vmw_ldu_encoder_destroy(struct drm_encoder *encoder)
 310{
 311        vmw_ldu_destroy(vmw_encoder_to_ldu(encoder));
 312}
 313
 314static struct drm_encoder_funcs vmw_legacy_encoder_funcs = {
 315        .destroy = vmw_ldu_encoder_destroy,
 316};
 317
 318/*
 319 * Legacy Display Unit connector functions
 320 */
 321
 322static void vmw_ldu_connector_destroy(struct drm_connector *connector)
 323{
 324        vmw_ldu_destroy(vmw_connector_to_ldu(connector));
 325}
 326
 327static struct drm_connector_funcs vmw_legacy_connector_funcs = {
 328        .dpms = vmw_du_connector_dpms,
 329        .save = vmw_du_connector_save,
 330        .restore = vmw_du_connector_restore,
 331        .detect = vmw_du_connector_detect,
 332        .fill_modes = vmw_du_connector_fill_modes,
 333        .set_property = vmw_du_connector_set_property,
 334        .destroy = vmw_ldu_connector_destroy,
 335};
 336
 337static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
 338{
 339        struct vmw_legacy_display_unit *ldu;
 340        struct drm_device *dev = dev_priv->dev;
 341        struct drm_connector *connector;
 342        struct drm_encoder *encoder;
 343        struct drm_crtc *crtc;
 344
 345        ldu = kzalloc(sizeof(*ldu), GFP_KERNEL);
 346        if (!ldu)
 347                return -ENOMEM;
 348
 349        ldu->base.unit = unit;
 350        crtc = &ldu->base.crtc;
 351        encoder = &ldu->base.encoder;
 352        connector = &ldu->base.connector;
 353
 354        INIT_LIST_HEAD(&ldu->active);
 355
 356        ldu->base.pref_active = (unit == 0);
 357        ldu->base.pref_width = dev_priv->initial_width;
 358        ldu->base.pref_height = dev_priv->initial_height;
 359        ldu->base.pref_mode = NULL;
 360        ldu->base.is_implicit = true;
 361
 362        drm_connector_init(dev, connector, &vmw_legacy_connector_funcs,
 363                           DRM_MODE_CONNECTOR_VIRTUAL);
 364        connector->status = vmw_du_connector_detect(connector, true);
 365
 366        drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs,
 367                         DRM_MODE_ENCODER_VIRTUAL);
 368        drm_mode_connector_attach_encoder(connector, encoder);
 369        encoder->possible_crtcs = (1 << unit);
 370        encoder->possible_clones = 0;
 371
 372        drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs);
 373
 374        drm_mode_crtc_set_gamma_size(crtc, 256);
 375
 376        drm_connector_attach_property(connector,
 377                                      dev->mode_config.dirty_info_property,
 378                                      1);
 379
 380        return 0;
 381}
 382
 383int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv)
 384{
 385        struct drm_device *dev = dev_priv->dev;
 386        int i, ret;
 387
 388        if (dev_priv->ldu_priv) {
 389                DRM_INFO("ldu system already on\n");
 390                return -EINVAL;
 391        }
 392
 393        dev_priv->ldu_priv = kmalloc(sizeof(*dev_priv->ldu_priv), GFP_KERNEL);
 394        if (!dev_priv->ldu_priv)
 395                return -ENOMEM;
 396
 397        INIT_LIST_HEAD(&dev_priv->ldu_priv->active);
 398        dev_priv->ldu_priv->num_active = 0;
 399        dev_priv->ldu_priv->last_num_active = 0;
 400        dev_priv->ldu_priv->fb = NULL;
 401
 402        /* for old hardware without multimon only enable one display */
 403        if (dev_priv->capabilities & SVGA_CAP_MULTIMON)
 404                ret = drm_vblank_init(dev, VMWGFX_NUM_DISPLAY_UNITS);
 405        else
 406                ret = drm_vblank_init(dev, 1);
 407        if (ret != 0)
 408                goto err_free;
 409
 410        ret = drm_mode_create_dirty_info_property(dev);
 411        if (ret != 0)
 412                goto err_vblank_cleanup;
 413
 414        if (dev_priv->capabilities & SVGA_CAP_MULTIMON)
 415                for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i)
 416                        vmw_ldu_init(dev_priv, i);
 417        else
 418                vmw_ldu_init(dev_priv, 0);
 419
 420        return 0;
 421
 422err_vblank_cleanup:
 423        drm_vblank_cleanup(dev);
 424err_free:
 425        kfree(dev_priv->ldu_priv);
 426        dev_priv->ldu_priv = NULL;
 427        return ret;
 428}
 429
 430int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv)
 431{
 432        struct drm_device *dev = dev_priv->dev;
 433
 434        if (!dev_priv->ldu_priv)
 435                return -ENOSYS;
 436
 437        drm_vblank_cleanup(dev);
 438
 439        BUG_ON(!list_empty(&dev_priv->ldu_priv->active));
 440
 441        kfree(dev_priv->ldu_priv);
 442
 443        return 0;
 444}
 445