xref: /titanic_44/usr/src/uts/intel/io/drm/i915_drv.c (revision ee5416c9d7e449233197d5d20bc6b81e4ff091b2)
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, &regoff);
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