1 /* BEGIN CSTYLED */ 2 3 /* 4 * i915_drv.c -- Intel i915 driver -*- linux-c -*- 5 * Created: Wed Feb 14 17:10:04 2001 by gareth@valinux.com 6 */ 7 8 /* 9 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 10 * All Rights Reserved. 11 * 12 * Permission is hereby granted, free of charge, to any person obtaining a 13 * copy of this software and associated documentation files (the "Software"), 14 * to deal in the Software without restriction, including without limitation 15 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 16 * and/or sell copies of the Software, and to permit persons to whom the 17 * Software is furnished to do so, subject to the following conditions: 18 * 19 * The above copyright notice and this permission notice (including the next 20 * paragraph) shall be included in all copies or substantial portions of the 21 * Software. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 26 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 27 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 28 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 29 * OTHER DEALINGS IN THE SOFTWARE. 30 * 31 * Authors: 32 * Gareth Hughes <gareth@valinux.com> 33 * 34 */ 35 36 /* 37 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 38 * Use is subject to license terms. 39 */ 40 41 #pragma ident "%Z%%M% %I% %E% SMI" 42 43 #include "drmP.h" 44 #include "drm.h" 45 #include "i915_drm.h" 46 #include "i915_drv.h" 47 #include "drm_pciids.h" 48 49 #define i915_max_ioctl 15 50 51 /* drv_PCI_IDs comes from drm_pciids.h, generated from drm_pciids.txt. */ 52 static drm_pci_id_list_t i915_pciidlist[] = { 53 i915_PCI_IDS 54 }; 55 56 drm_ioctl_desc_t i915_ioctls[i915_max_ioctl]; 57 58 extern drm_ioctl_desc_t drm_ioctls[]; 59 extern void i915_init_ioctl_arrays(void); 60 extern uint_t i915_driver_irq_handler(caddr_t); 61 extern int drm_get_pci_index_reg(dev_info_t *devi, uint_t physical, 62 uint_t size, off_t *off); 63 64 static void i915_configure(drm_device_t *dev) 65 { 66 i915_init_ioctl_arrays(); 67 68 dev->dev_priv_size = 1; /* No dev_priv */ 69 70 dev->irq_preinstall = i915_driver_irq_preinstall; 71 dev->irq_postinstall = i915_driver_irq_postinstall; 72 dev->irq_uninstall = i915_driver_irq_uninstall; 73 dev->irq_handler = i915_driver_irq_handler; 74 75 dev->driver_ioctls = i915_ioctls; 76 dev->max_driver_ioctl = i915_max_ioctl; 77 78 dev->driver_name = DRIVER_NAME; 79 dev->driver_desc = DRIVER_DESC; 80 dev->driver_date = DRIVER_DATE; 81 dev->driver_major = DRIVER_MAJOR; 82 dev->driver_minor = DRIVER_MINOR; 83 dev->driver_patchlevel = DRIVER_PATCHLEVEL; 84 85 dev->use_agp = 0; 86 dev->use_irq = 1; 87 } 88 89 extern int 90 i915_open(dev_t *dev, int openflags, int otyp, cred_t *credp, 91 struct drm_softstate *softc) 92 { 93 int minor; 94 struct minordev *mp, *newp; 95 int cloneminor, cleanpass; 96 97 if (softc == NULL) { 98 DRM_ERROR("i915_open: NULL soft state"); 99 return (ENXIO); 100 } 101 102 if (softc->drm_supported == DRM_UNSUPPORT) { 103 if (drm_probe(softc, i915_pciidlist) != 104 DDI_SUCCESS) { 105 DRM_ERROR("i915_open: " 106 "DRM current don't support this graphics card"); 107 return (ENXIO); 108 } 109 softc->drm_supported = DRM_SUPPORT; 110 111 } 112 113 minor = (getminor(*dev)); 114 115 newp = kmem_zalloc(sizeof (struct minordev), KM_SLEEP); 116 117 mutex_enter(&softc->dev_lock); 118 119 for (cloneminor = minor; ; cloneminor += 1) { 120 cleanpass = 1; 121 for (mp = softc->minordevs; mp != NULL; mp = mp->next) { 122 if (mp->cloneminor == cloneminor) { 123 cleanpass = 0; 124 break; 125 } 126 } 127 if (cleanpass) { 128 goto gotminor; 129 } 130 } 131 132 gotminor: 133 newp->next = softc->minordevs; 134 newp->cloneminor = cloneminor; 135 softc->minordevs = newp; 136 softc->cloneopens++; 137 mutex_exit(&softc->dev_lock); 138 139 *dev = makedevice(getmajor(*dev), cloneminor); 140 141 return (drm_open(softc, dev, openflags, otyp, credp)); 142 143 } 144 145 extern int 146 i915_close(dev_t dev, int flag, int otyp, cred_t *credp, 147 struct drm_softstate *softc) 148 { 149 struct minordev *lastp, *mp; 150 int minor; 151 DRMFILE filp = (void *)(uintptr_t)(DRM_CURRENTPID); 152 drm_i915_private_t *dev_priv; 153 struct mem_block *block, **heap; 154 155 block = NULL; 156 heap = NULL; 157 dev_priv = NULL; 158 159 if (softc == NULL) { 160 DRM_ERROR("i915_close: NULL soft state"); 161 return (ENXIO); 162 } 163 164 dev_priv = softc->dev_private; 165 166 if (dev_priv) { 167 heap = get_heap(dev_priv, I915_MEM_REGION_AGP); 168 if (heap == NULL || *heap == NULL) 169 return DRM_ERR(EFAULT); 170 171 block = find_block_by_proc(*heap, filp); 172 if (block != NULL) 173 { 174 mark_block(softc, block, 0); 175 free_block(block); 176 } 177 } 178 179 if ((minor = getminor(dev)) < 0) { 180 return (ENXIO); 181 } 182 183 mutex_enter(&softc->dev_lock); 184 185 lastp = NULL; 186 for (mp = softc->minordevs; mp != NULL; mp = mp->next) { 187 if (mp->cloneminor == minor) { 188 if (lastp == NULL) { 189 softc->minordevs = mp->next; 190 } else { 191 lastp->next = mp->next; 192 } 193 194 softc->cloneopens--; 195 (void) kmem_free(mp, sizeof (struct minordev)); 196 break; 197 } else { 198 lastp = mp; 199 } 200 } 201 202 mutex_exit(&softc->dev_lock); 203 204 return (drm_close(softc, dev, flag, otyp, credp)); 205 } 206 207 int 208 i915_ioctl(dev_t kdev, int cmd, intptr_t intarg, int flags, cred_t *credp, 209 int *rvalp, struct drm_softstate *dev) 210 { 211 int retcode = ENXIO; 212 drm_ioctl_desc_t *ioctl; 213 drm_ioctl_t *func; 214 int nr = DRM_IOCTL_NR(cmd); 215 drm_file_t *priv; 216 DRMFILE filp; 217 218 DRM_LOCK(); 219 priv = drm_find_file_by_proc(dev, credp); 220 DRM_UNLOCK(); 221 if (priv == NULL) { 222 DRM_ERROR("i915_ioctl : can't find authenticator"); 223 return (EINVAL); 224 } 225 226 atomic_inc_32(&dev->counts[_DRM_STAT_IOCTLS]); 227 ++priv->ioctl_count; 228 229 ioctl = &drm_ioctls[nr]; 230 /* It's not a core DRM ioctl, try driver-specific. */ 231 if (ioctl->func == NULL && nr >= DRM_COMMAND_BASE) { 232 /* The array entries begin at DRM_COMMAND_BASE ioctl nr */ 233 nr -= DRM_COMMAND_BASE; 234 if (nr > dev->max_driver_ioctl) { 235 DRM_ERROR("Bad driver ioctl number, 0x%x (of 0x%x)", 236 nr, dev->max_driver_ioctl); 237 return (EINVAL); 238 } 239 ioctl = &dev->driver_ioctls[nr]; 240 } 241 242 func = ioctl->func; 243 if ((ioctl->root_only && !DRM_SUSER(credp)) || (ioctl->auth_needed && 244 !priv->authenticated)) 245 return (EACCES); 246 247 if (func == NULL) { 248 DRM_ERROR("i915_ioctl: no function "); 249 return (EINVAL); 250 } 251 filp = (void *)(uintptr_t)(DRM_CURRENTPID); 252 retcode = func(kdev, dev, intarg, flags, credp, rvalp, filp); 253 254 return (retcode); 255 } 256 257 /*ARGSUSED*/ 258 int 259 i915_devmap(dev_t kdev, devmap_cookie_t cookie, offset_t offset, size_t len, 260 size_t *maplen, uint_t model, struct drm_softstate *dev, 261 ddi_device_acc_attr_t *accattrp) 262 { 263 drm_local_map_t *map; 264 offset_t koff; 265 size_t length; 266 int ret; 267 268 if (dev == NULL) { 269 DRM_ERROR("i915_devmap: NULL soft state"); 270 return (EINVAL); 271 } 272 273 DRM_LOCK(); 274 TAILQ_FOREACH(map, &dev->maplist, link) { 275 DRM_DEBUG("i915_devmap: offset is 0x%llx map->offset is 0x%llx", 276 offset, map->offset); 277 /* 278 * use low 32-bit to search only, since 32-bit user app is 279 * incapable of passing in 64-bit offset when doing mmap. 280 */ 281 if ((u_offset_t)(unsigned int)offset >= map->offset.off && 282 (u_offset_t)(unsigned int)offset 283 < (u_offset_t)map->offset.off + map->size) 284 break; 285 } 286 287 if (map == NULL) { 288 DRM_UNLOCK(); 289 DRM_ERROR("can't find map\n"); 290 return (-1); 291 } 292 if (map->flags&_DRM_RESTRICTED) { 293 DRM_UNLOCK(); 294 DRM_ERROR("restricted map\n"); 295 return (-1); 296 } 297 298 DRM_UNLOCK(); 299 300 switch (map->type) { 301 case _DRM_FRAME_BUFFER: 302 case _DRM_REGISTERS: 303 case _DRM_AGP: 304 { 305 int err; 306 int regno; 307 off_t regoff; 308 309 regno = drm_get_pci_index_reg(dev->dip, 310 offset, (uint_t)len, ®off); 311 312 err = devmap_devmem_setup(cookie, dev->dip, NULL, 313 regno, (offset_t)regoff, len, PROT_ALL, 314 0, accattrp); 315 if (err != 0) { 316 *maplen = 0; 317 DRM_ERROR("i915_devmap: devmap failed"); 318 return (err); 319 } 320 *maplen = len; 321 return (err); 322 } 323 324 case _DRM_SHM: 325 { 326 DRM_DEBUG("i915_devmap: map type is _DRM_SHM"); 327 if (map->drm_umem_cookie == NULL) { 328 DRM_ERROR("i915_devmap: " 329 "Fatal error! sarea_cookie is NULL"); 330 return (EINVAL); 331 } 332 koff = 0; 333 length = ptob(btopr(map->size)); 334 ret = devmap_umem_setup(cookie, dev->dip, NULL, 335 map->drm_umem_cookie, koff, length, 336 PROT_ALL, DEVMAP_DEFAULTS, NULL); 337 if (ret != 0) { 338 *maplen = 0; 339 return (ret); 340 } 341 *maplen = length; 342 343 return (DDI_SUCCESS); 344 } 345 default: 346 return (DDI_FAILURE); 347 } 348 } 349 350 int 351 i915_attach(dev_info_t *dip, 352 ddi_attach_cmd_t cmd, 353 struct drm_softstate **drm_softcp, 354 ddi_acc_handle_t pci_cfg_hdl, 355 minor_t minor) 356 { 357 int instance; 358 drm_softstate_t *softc; 359 int ret; 360 char buf[80]; 361 362 if (cmd != DDI_ATTACH) { 363 DRM_ERROR( 364 "i915_attach: only attach op supported"); 365 return (DDI_FAILURE); 366 } 367 368 softc = (drm_softstate_t *) 369 kmem_zalloc(sizeof (drm_softstate_t), KM_SLEEP); 370 371 softc->dip = dip; 372 softc->pci_cfg_hdl = pci_cfg_hdl; 373 softc->drm_supported = DRM_UNSUPPORT; 374 i915_configure(softc); 375 376 /* call common attach code */ 377 ret = drm_attach(softc); 378 if (ret != DDI_SUCCESS) { 379 DRM_ERROR( 380 "i915_attach: drm attach ops failed"); 381 goto err1; 382 } 383 384 /* create minor node for DRM access */ 385 instance = ddi_get_instance(dip); 386 387 (void) sprintf(buf, "%s%d", DRM_DEVNODE, instance); 388 if (ddi_create_minor_node(dip, buf, S_IFCHR, 389 minor, DDI_NT_DISPLAY_DRM, 0)) { 390 DRM_ERROR("i915_attach: create minor node failed"); 391 goto err2; 392 } 393 394 *drm_softcp = softc; 395 396 return (DDI_SUCCESS); 397 err2: 398 ddi_remove_minor_node(dip, DRM_DEVNODE); 399 err1: 400 kmem_free(softc, sizeof (drm_softstate_t)); 401 *drm_softcp = NULL; 402 return (DDI_FAILURE); 403 404 } 405 406 int 407 i915_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, 408 drm_softstate_t **drm_softcp) 409 { 410 drm_softstate_t *softc = *drm_softcp; 411 412 if (cmd != DDI_DETACH) 413 return (DDI_FAILURE); 414 415 (void) drm_detach(softc); 416 417 ddi_remove_minor_node(dip, DRM_DEVNODE); 418 kmem_free(softc, sizeof (drm_softstate_t)); 419 *drm_softcp = NULL; 420 421 return (DDI_SUCCESS); 422 } 423