linux/drivers/gpu/drm/sis/sis_mm.c
<<
>>
Prefs
   1/**************************************************************************
   2 *
   3 * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., 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
  29/*
  30 * Authors:
  31 *    Thomas Hellström <thomas-at-tungstengraphics-dot-com>
  32 */
  33
  34#include "drmP.h"
  35#include "sis_drm.h"
  36#include "sis_drv.h"
  37
  38#include <video/sisfb.h>
  39
  40#define VIDEO_TYPE 0
  41#define AGP_TYPE 1
  42
  43
  44#if defined(CONFIG_FB_SIS) || defined(CONFIG_FB_SIS_MODULE)
  45/* fb management via fb device */
  46
  47#define SIS_MM_ALIGN_SHIFT 0
  48#define SIS_MM_ALIGN_MASK 0
  49
  50static void *sis_sman_mm_allocate(void *private, unsigned long size,
  51                                  unsigned alignment)
  52{
  53        struct sis_memreq req;
  54
  55        req.size = size;
  56        sis_malloc(&req);
  57        if (req.size == 0)
  58                return NULL;
  59        else
  60                return (void *)(unsigned long)~req.offset;
  61}
  62
  63static void sis_sman_mm_free(void *private, void *ref)
  64{
  65        sis_free(~((unsigned long)ref));
  66}
  67
  68static void sis_sman_mm_destroy(void *private)
  69{
  70        ;
  71}
  72
  73static unsigned long sis_sman_mm_offset(void *private, void *ref)
  74{
  75        return ~((unsigned long)ref);
  76}
  77
  78#else /* CONFIG_FB_SIS[_MODULE] */
  79
  80#define SIS_MM_ALIGN_SHIFT 4
  81#define SIS_MM_ALIGN_MASK ( (1 << SIS_MM_ALIGN_SHIFT) - 1)
  82
  83#endif /* CONFIG_FB_SIS[_MODULE] */
  84
  85static int sis_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv)
  86{
  87        drm_sis_private_t *dev_priv = dev->dev_private;
  88        drm_sis_fb_t *fb = data;
  89        int ret;
  90
  91        mutex_lock(&dev->struct_mutex);
  92#if defined(CONFIG_FB_SIS) || defined(CONFIG_FB_SIS_MODULE)
  93        {
  94                struct drm_sman_mm sman_mm;
  95                sman_mm.private = (void *)0xFFFFFFFF;
  96                sman_mm.allocate = sis_sman_mm_allocate;
  97                sman_mm.free = sis_sman_mm_free;
  98                sman_mm.destroy = sis_sman_mm_destroy;
  99                sman_mm.offset = sis_sman_mm_offset;
 100                ret =
 101                    drm_sman_set_manager(&dev_priv->sman, VIDEO_TYPE, &sman_mm);
 102        }
 103#else
 104        ret = drm_sman_set_range(&dev_priv->sman, VIDEO_TYPE, 0,
 105                                 fb->size >> SIS_MM_ALIGN_SHIFT);
 106#endif
 107
 108        if (ret) {
 109                DRM_ERROR("VRAM memory manager initialisation error\n");
 110                mutex_unlock(&dev->struct_mutex);
 111                return ret;
 112        }
 113
 114        dev_priv->vram_initialized = 1;
 115        dev_priv->vram_offset = fb->offset;
 116
 117        mutex_unlock(&dev->struct_mutex);
 118        DRM_DEBUG("offset = %u, size = %u\n", fb->offset, fb->size);
 119
 120        return 0;
 121}
 122
 123static int sis_drm_alloc(struct drm_device *dev, struct drm_file *file_priv,
 124                         void *data, int pool)
 125{
 126        drm_sis_private_t *dev_priv = dev->dev_private;
 127        drm_sis_mem_t *mem = data;
 128        int retval = 0;
 129        struct drm_memblock_item *item;
 130
 131        mutex_lock(&dev->struct_mutex);
 132
 133        if (0 == ((pool == 0) ? dev_priv->vram_initialized :
 134                      dev_priv->agp_initialized)) {
 135                DRM_ERROR
 136                    ("Attempt to allocate from uninitialized memory manager.\n");
 137                mutex_unlock(&dev->struct_mutex);
 138                return -EINVAL;
 139        }
 140
 141        mem->size = (mem->size + SIS_MM_ALIGN_MASK) >> SIS_MM_ALIGN_SHIFT;
 142        item = drm_sman_alloc(&dev_priv->sman, pool, mem->size, 0,
 143                              (unsigned long)file_priv);
 144
 145        mutex_unlock(&dev->struct_mutex);
 146        if (item) {
 147                mem->offset = ((pool == 0) ?
 148                              dev_priv->vram_offset : dev_priv->agp_offset) +
 149                    (item->mm->
 150                     offset(item->mm, item->mm_info) << SIS_MM_ALIGN_SHIFT);
 151                mem->free = item->user_hash.key;
 152                mem->size = mem->size << SIS_MM_ALIGN_SHIFT;
 153        } else {
 154                mem->offset = 0;
 155                mem->size = 0;
 156                mem->free = 0;
 157                retval = -ENOMEM;
 158        }
 159
 160        DRM_DEBUG("alloc %d, size = %d, offset = %d\n", pool, mem->size,
 161                  mem->offset);
 162
 163        return retval;
 164}
 165
 166static int sis_drm_free(struct drm_device *dev, void *data, struct drm_file *file_priv)
 167{
 168        drm_sis_private_t *dev_priv = dev->dev_private;
 169        drm_sis_mem_t *mem = data;
 170        int ret;
 171
 172        mutex_lock(&dev->struct_mutex);
 173        ret = drm_sman_free_key(&dev_priv->sman, mem->free);
 174        mutex_unlock(&dev->struct_mutex);
 175        DRM_DEBUG("free = 0x%lx\n", mem->free);
 176
 177        return ret;
 178}
 179
 180static int sis_fb_alloc(struct drm_device *dev, void *data,
 181                        struct drm_file *file_priv)
 182{
 183        return sis_drm_alloc(dev, file_priv, data, VIDEO_TYPE);
 184}
 185
 186static int sis_ioctl_agp_init(struct drm_device *dev, void *data,
 187                              struct drm_file *file_priv)
 188{
 189        drm_sis_private_t *dev_priv = dev->dev_private;
 190        drm_sis_agp_t *agp = data;
 191        int ret;
 192        dev_priv = dev->dev_private;
 193
 194        mutex_lock(&dev->struct_mutex);
 195        ret = drm_sman_set_range(&dev_priv->sman, AGP_TYPE, 0,
 196                                 agp->size >> SIS_MM_ALIGN_SHIFT);
 197
 198        if (ret) {
 199                DRM_ERROR("AGP memory manager initialisation error\n");
 200                mutex_unlock(&dev->struct_mutex);
 201                return ret;
 202        }
 203
 204        dev_priv->agp_initialized = 1;
 205        dev_priv->agp_offset = agp->offset;
 206        mutex_unlock(&dev->struct_mutex);
 207
 208        DRM_DEBUG("offset = %u, size = %u\n", agp->offset, agp->size);
 209        return 0;
 210}
 211
 212static int sis_ioctl_agp_alloc(struct drm_device *dev, void *data,
 213                               struct drm_file *file_priv)
 214{
 215
 216        return sis_drm_alloc(dev, file_priv, data, AGP_TYPE);
 217}
 218
 219static drm_local_map_t *sis_reg_init(struct drm_device *dev)
 220{
 221        struct drm_map_list *entry;
 222        drm_local_map_t *map;
 223
 224        list_for_each_entry(entry, &dev->maplist, head) {
 225                map = entry->map;
 226                if (!map)
 227                        continue;
 228                if (map->type == _DRM_REGISTERS) {
 229                        return map;
 230                }
 231        }
 232        return NULL;
 233}
 234
 235int sis_idle(struct drm_device *dev)
 236{
 237        drm_sis_private_t *dev_priv = dev->dev_private;
 238        uint32_t idle_reg;
 239        unsigned long end;
 240        int i;
 241
 242        if (dev_priv->idle_fault)
 243                return 0;
 244
 245        if (dev_priv->mmio == NULL) {
 246                dev_priv->mmio = sis_reg_init(dev);
 247                if (dev_priv->mmio == NULL) {
 248                        DRM_ERROR("Could not find register map.\n");
 249                        return 0;
 250                }
 251        }
 252
 253        /*
 254         * Implement a device switch here if needed
 255         */
 256
 257        if (dev_priv->chipset != SIS_CHIP_315)
 258                return 0;
 259
 260        /*
 261         * Timeout after 3 seconds. We cannot use DRM_WAIT_ON here
 262         * because its polling frequency is too low.
 263         */
 264
 265        end = jiffies + (DRM_HZ * 3);
 266
 267        for (i=0; i<4; ++i) {
 268                do {
 269                        idle_reg = SIS_READ(0x85cc);
 270                } while ( !time_after_eq(jiffies, end) &&
 271                          ((idle_reg & 0x80000000) != 0x80000000));
 272        }
 273
 274        if (time_after_eq(jiffies, end)) {
 275                DRM_ERROR("Graphics engine idle timeout. "
 276                          "Disabling idle check\n");
 277                dev_priv->idle_fault = 1;
 278        }
 279
 280        /*
 281         * The caller never sees an error code. It gets trapped
 282         * in libdrm.
 283         */
 284
 285        return 0;
 286}
 287
 288
 289void sis_lastclose(struct drm_device *dev)
 290{
 291        drm_sis_private_t *dev_priv = dev->dev_private;
 292
 293        if (!dev_priv)
 294                return;
 295
 296        mutex_lock(&dev->struct_mutex);
 297        drm_sman_cleanup(&dev_priv->sman);
 298        dev_priv->vram_initialized = 0;
 299        dev_priv->agp_initialized = 0;
 300        dev_priv->mmio = NULL;
 301        mutex_unlock(&dev->struct_mutex);
 302}
 303
 304void sis_reclaim_buffers_locked(struct drm_device * dev,
 305                                struct drm_file *file_priv)
 306{
 307        drm_sis_private_t *dev_priv = dev->dev_private;
 308
 309        mutex_lock(&dev->struct_mutex);
 310        if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)file_priv)) {
 311                mutex_unlock(&dev->struct_mutex);
 312                return;
 313        }
 314
 315        if (dev->driver->dma_quiescent) {
 316                dev->driver->dma_quiescent(dev);
 317        }
 318
 319        drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)file_priv);
 320        mutex_unlock(&dev->struct_mutex);
 321        return;
 322}
 323
 324struct drm_ioctl_desc sis_ioctls[] = {
 325        DRM_IOCTL_DEF(DRM_SIS_FB_ALLOC, sis_fb_alloc, DRM_AUTH),
 326        DRM_IOCTL_DEF(DRM_SIS_FB_FREE, sis_drm_free, DRM_AUTH),
 327        DRM_IOCTL_DEF(DRM_SIS_AGP_INIT, sis_ioctl_agp_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY),
 328        DRM_IOCTL_DEF(DRM_SIS_AGP_ALLOC, sis_ioctl_agp_alloc, DRM_AUTH),
 329        DRM_IOCTL_DEF(DRM_SIS_AGP_FREE, sis_drm_free, DRM_AUTH),
 330        DRM_IOCTL_DEF(DRM_SIS_FB_INIT, sis_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY),
 331};
 332
 333int sis_max_ioctl = DRM_ARRAY_SIZE(sis_ioctls);
 334