1280183Sdumbbell
2280183Sdumbbell#include <sys/cdefs.h>
3280183Sdumbbell__FBSDID("$FreeBSD$");
4280183Sdumbbell
5280183Sdumbbell#include <dev/drm2/drmP.h>
6280183Sdumbbell
7280183Sdumbbell#include <dev/agp/agpreg.h>
8280183Sdumbbell#include <dev/pci/pcireg.h>
9280183Sdumbbell
10280183Sdumbbelldevclass_t drm_devclass;
11280183Sdumbbell
12280183SdumbbellMALLOC_DEFINE(DRM_MEM_DMA, "drm_dma", "DRM DMA Data Structures");
13280183SdumbbellMALLOC_DEFINE(DRM_MEM_SAREA, "drm_sarea", "DRM SAREA Data Structures");
14280183SdumbbellMALLOC_DEFINE(DRM_MEM_DRIVER, "drm_driver", "DRM DRIVER Data Structures");
15280183SdumbbellMALLOC_DEFINE(DRM_MEM_MAGIC, "drm_magic", "DRM MAGIC Data Structures");
16280183SdumbbellMALLOC_DEFINE(DRM_MEM_MINOR, "drm_minor", "DRM MINOR Data Structures");
17280183SdumbbellMALLOC_DEFINE(DRM_MEM_IOCTLS, "drm_ioctls", "DRM IOCTL Data Structures");
18280183SdumbbellMALLOC_DEFINE(DRM_MEM_MAPS, "drm_maps", "DRM MAP Data Structures");
19280183SdumbbellMALLOC_DEFINE(DRM_MEM_BUFS, "drm_bufs", "DRM BUFFER Data Structures");
20280183SdumbbellMALLOC_DEFINE(DRM_MEM_SEGS, "drm_segs", "DRM SEGMENTS Data Structures");
21280183SdumbbellMALLOC_DEFINE(DRM_MEM_PAGES, "drm_pages", "DRM PAGES Data Structures");
22280183SdumbbellMALLOC_DEFINE(DRM_MEM_FILES, "drm_files", "DRM FILE Data Structures");
23280183SdumbbellMALLOC_DEFINE(DRM_MEM_QUEUES, "drm_queues", "DRM QUEUE Data Structures");
24280183SdumbbellMALLOC_DEFINE(DRM_MEM_CMDS, "drm_cmds", "DRM COMMAND Data Structures");
25280183SdumbbellMALLOC_DEFINE(DRM_MEM_MAPPINGS, "drm_mapping", "DRM MAPPING Data Structures");
26280183SdumbbellMALLOC_DEFINE(DRM_MEM_BUFLISTS, "drm_buflists", "DRM BUFLISTS Data Structures");
27280183SdumbbellMALLOC_DEFINE(DRM_MEM_AGPLISTS, "drm_agplists", "DRM AGPLISTS Data Structures");
28280183SdumbbellMALLOC_DEFINE(DRM_MEM_CTXBITMAP, "drm_ctxbitmap",
29280183Sdumbbell    "DRM CTXBITMAP Data Structures");
30280183SdumbbellMALLOC_DEFINE(DRM_MEM_SGLISTS, "drm_sglists", "DRM SGLISTS Data Structures");
31280183SdumbbellMALLOC_DEFINE(DRM_MEM_MM, "drm_sman", "DRM MEMORY MANAGER Data Structures");
32280183SdumbbellMALLOC_DEFINE(DRM_MEM_HASHTAB, "drm_hashtab", "DRM HASHTABLE Data Structures");
33280183SdumbbellMALLOC_DEFINE(DRM_MEM_KMS, "drm_kms", "DRM KMS Data Structures");
34280183SdumbbellMALLOC_DEFINE(DRM_MEM_VBLANK, "drm_vblank", "DRM VBLANK Handling Data");
35280183Sdumbbell
36280183Sdumbbellconst char *fb_mode_option = NULL;
37280183Sdumbbell
38280183Sdumbbell#define NSEC_PER_USEC	1000L
39280183Sdumbbell#define NSEC_PER_SEC	1000000000L
40280183Sdumbbell
41280183Sdumbbellint64_t
42280183Sdumbbelltimeval_to_ns(const struct timeval *tv)
43280183Sdumbbell{
44280183Sdumbbell	return ((int64_t)tv->tv_sec * NSEC_PER_SEC) +
45280183Sdumbbell		tv->tv_usec * NSEC_PER_USEC;
46280183Sdumbbell}
47280183Sdumbbell
48280183Sdumbbellstruct timeval
49280183Sdumbbellns_to_timeval(const int64_t nsec)
50280183Sdumbbell{
51280183Sdumbbell        struct timeval tv;
52280183Sdumbbell	long rem;
53280183Sdumbbell
54280183Sdumbbell	if (nsec == 0) {
55280183Sdumbbell		tv.tv_sec = 0;
56280183Sdumbbell		tv.tv_usec = 0;
57280183Sdumbbell		return (tv);
58280183Sdumbbell	}
59280183Sdumbbell
60280183Sdumbbell        tv.tv_sec = nsec / NSEC_PER_SEC;
61280183Sdumbbell	rem = nsec % NSEC_PER_SEC;
62280183Sdumbbell        if (rem < 0) {
63280183Sdumbbell                tv.tv_sec--;
64280183Sdumbbell                rem += NSEC_PER_SEC;
65280183Sdumbbell        }
66280183Sdumbbell	tv.tv_usec = rem / 1000;
67280183Sdumbbell        return (tv);
68280183Sdumbbell}
69280183Sdumbbell
70280183Sdumbbellstatic drm_pci_id_list_t *
71280183Sdumbbelldrm_find_description(int vendor, int device, drm_pci_id_list_t *idlist)
72280183Sdumbbell{
73280183Sdumbbell	int i = 0;
74280183Sdumbbell
75280183Sdumbbell	for (i = 0; idlist[i].vendor != 0; i++) {
76280183Sdumbbell		if ((idlist[i].vendor == vendor) &&
77280183Sdumbbell		    ((idlist[i].device == device) ||
78280183Sdumbbell		    (idlist[i].device == 0))) {
79280183Sdumbbell			return (&idlist[i]);
80280183Sdumbbell		}
81280183Sdumbbell	}
82280183Sdumbbell	return (NULL);
83280183Sdumbbell}
84280183Sdumbbell
85280183Sdumbbell/*
86280183Sdumbbell * drm_probe_helper: called by a driver at the end of its probe
87280183Sdumbbell * method.
88280183Sdumbbell */
89280183Sdumbbellint
90280183Sdumbbelldrm_probe_helper(device_t kdev, drm_pci_id_list_t *idlist)
91280183Sdumbbell{
92280183Sdumbbell	drm_pci_id_list_t *id_entry;
93280183Sdumbbell	int vendor, device;
94280183Sdumbbell
95280183Sdumbbell	vendor = pci_get_vendor(kdev);
96280183Sdumbbell	device = pci_get_device(kdev);
97280183Sdumbbell
98280183Sdumbbell	if (pci_get_class(kdev) != PCIC_DISPLAY ||
99280183Sdumbbell	    (pci_get_subclass(kdev) != PCIS_DISPLAY_VGA &&
100280183Sdumbbell	     pci_get_subclass(kdev) != PCIS_DISPLAY_OTHER))
101280183Sdumbbell		return (-ENXIO);
102280183Sdumbbell
103280183Sdumbbell	id_entry = drm_find_description(vendor, device, idlist);
104280183Sdumbbell	if (id_entry != NULL) {
105280183Sdumbbell		if (device_get_desc(kdev) == NULL) {
106280183Sdumbbell			DRM_DEBUG("%s desc: %s\n",
107280183Sdumbbell			    device_get_nameunit(kdev), id_entry->name);
108280183Sdumbbell			device_set_desc(kdev, id_entry->name);
109280183Sdumbbell		}
110280183Sdumbbell		return (0);
111280183Sdumbbell	}
112280183Sdumbbell
113280183Sdumbbell	return (-ENXIO);
114280183Sdumbbell}
115280183Sdumbbell
116280183Sdumbbell/*
117280183Sdumbbell * drm_attach_helper: called by a driver at the end of its attach
118280183Sdumbbell * method.
119280183Sdumbbell */
120280183Sdumbbellint
121280183Sdumbbelldrm_attach_helper(device_t kdev, drm_pci_id_list_t *idlist,
122280183Sdumbbell    struct drm_driver *driver)
123280183Sdumbbell{
124280183Sdumbbell	struct drm_device *dev;
125280183Sdumbbell	int vendor, device;
126280183Sdumbbell	int ret;
127280183Sdumbbell
128280183Sdumbbell	dev = device_get_softc(kdev);
129280183Sdumbbell
130280183Sdumbbell	vendor = pci_get_vendor(kdev);
131280183Sdumbbell	device = pci_get_device(kdev);
132280183Sdumbbell	dev->id_entry = drm_find_description(vendor, device, idlist);
133280183Sdumbbell
134280183Sdumbbell	ret = drm_get_pci_dev(kdev, dev, driver);
135280183Sdumbbell
136280183Sdumbbell	return (ret);
137280183Sdumbbell}
138280183Sdumbbell
139280183Sdumbbellint
140280183Sdumbbelldrm_generic_detach(device_t kdev)
141280183Sdumbbell{
142280183Sdumbbell	struct drm_device *dev;
143280183Sdumbbell	int i;
144280183Sdumbbell
145280183Sdumbbell	dev = device_get_softc(kdev);
146280183Sdumbbell
147280183Sdumbbell	drm_put_dev(dev);
148280183Sdumbbell
149280183Sdumbbell	/* Clean up PCI resources allocated by drm_bufs.c.  We're not really
150280183Sdumbbell	 * worried about resource consumption while the DRM is inactive (between
151280183Sdumbbell	 * lastclose and firstopen or unload) because these aren't actually
152280183Sdumbbell	 * taking up KVA, just keeping the PCI resource allocated.
153280183Sdumbbell	 */
154280183Sdumbbell	for (i = 0; i < DRM_MAX_PCI_RESOURCE; i++) {
155280183Sdumbbell		if (dev->pcir[i] == NULL)
156280183Sdumbbell			continue;
157280183Sdumbbell		bus_release_resource(dev->dev, SYS_RES_MEMORY,
158280183Sdumbbell		    dev->pcirid[i], dev->pcir[i]);
159280183Sdumbbell		dev->pcir[i] = NULL;
160280183Sdumbbell	}
161280183Sdumbbell
162280183Sdumbbell	if (pci_disable_busmaster(dev->dev))
163280183Sdumbbell		DRM_ERROR("Request to disable bus-master failed.\n");
164280183Sdumbbell
165280183Sdumbbell	return (0);
166280183Sdumbbell}
167280183Sdumbbell
168280183Sdumbbellint
169280183Sdumbbelldrm_add_busid_modesetting(struct drm_device *dev, struct sysctl_ctx_list *ctx,
170280183Sdumbbell    struct sysctl_oid *top)
171280183Sdumbbell{
172280183Sdumbbell	struct sysctl_oid *oid;
173280183Sdumbbell
174280183Sdumbbell	snprintf(dev->busid_str, sizeof(dev->busid_str),
175280183Sdumbbell	     "pci:%04x:%02x:%02x.%d", dev->pci_domain, dev->pci_bus,
176280183Sdumbbell	     dev->pci_slot, dev->pci_func);
177280183Sdumbbell	oid = SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(top), OID_AUTO, "busid",
178280183Sdumbbell	    CTLFLAG_RD, dev->busid_str, 0, NULL);
179280183Sdumbbell	if (oid == NULL)
180280183Sdumbbell		return (-ENOMEM);
181280183Sdumbbell	dev->modesetting = (dev->driver->driver_features & DRIVER_MODESET) != 0;
182280183Sdumbbell	oid = SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(top), OID_AUTO,
183280183Sdumbbell	    "modesetting", CTLFLAG_RD, &dev->modesetting, 0, NULL);
184280183Sdumbbell	if (oid == NULL)
185280183Sdumbbell		return (-ENOMEM);
186280183Sdumbbell
187280183Sdumbbell	return (0);
188280183Sdumbbell}
189280183Sdumbbell
190280183Sdumbbellstatic int
191280183Sdumbbelldrm_device_find_capability(struct drm_device *dev, int cap)
192280183Sdumbbell{
193280183Sdumbbell
194280183Sdumbbell	return (pci_find_cap(dev->dev, cap, NULL) == 0);
195280183Sdumbbell}
196280183Sdumbbell
197280183Sdumbbellint
198280183Sdumbbelldrm_pci_device_is_agp(struct drm_device *dev)
199280183Sdumbbell{
200280183Sdumbbell	if (dev->driver->device_is_agp != NULL) {
201280183Sdumbbell		int ret;
202280183Sdumbbell
203280183Sdumbbell		/* device_is_agp returns a tristate, 0 = not AGP, 1 = definitely
204280183Sdumbbell		 * AGP, 2 = fall back to PCI capability
205280183Sdumbbell		 */
206280183Sdumbbell		ret = (*dev->driver->device_is_agp)(dev);
207280183Sdumbbell		if (ret != DRM_MIGHT_BE_AGP)
208280183Sdumbbell			return ret;
209280183Sdumbbell	}
210280183Sdumbbell
211280183Sdumbbell	return (drm_device_find_capability(dev, PCIY_AGP));
212280183Sdumbbell}
213280183Sdumbbell
214280183Sdumbbellint
215280183Sdumbbelldrm_pci_device_is_pcie(struct drm_device *dev)
216280183Sdumbbell{
217280183Sdumbbell
218280183Sdumbbell	return (drm_device_find_capability(dev, PCIY_EXPRESS));
219280183Sdumbbell}
220280183Sdumbbell
221280183Sdumbbellstatic bool
222280183Sdumbbelldmi_found(const struct dmi_system_id *dsi)
223280183Sdumbbell{
224280183Sdumbbell	char *hw_vendor, *hw_prod;
225280183Sdumbbell	int i, slot;
226280183Sdumbbell	bool res;
227280183Sdumbbell
228282199Sdumbbell	hw_vendor = getenv("smbios.planar.maker");
229282199Sdumbbell	hw_prod = getenv("smbios.planar.product");
230280183Sdumbbell	res = true;
231280183Sdumbbell	for (i = 0; i < nitems(dsi->matches); i++) {
232280183Sdumbbell		slot = dsi->matches[i].slot;
233280183Sdumbbell		switch (slot) {
234280183Sdumbbell		case DMI_NONE:
235280183Sdumbbell			break;
236280183Sdumbbell		case DMI_SYS_VENDOR:
237280183Sdumbbell		case DMI_BOARD_VENDOR:
238280183Sdumbbell			if (hw_vendor != NULL &&
239280183Sdumbbell			    !strcmp(hw_vendor, dsi->matches[i].substr)) {
240280183Sdumbbell				break;
241280183Sdumbbell			} else {
242280183Sdumbbell				res = false;
243280183Sdumbbell				goto out;
244280183Sdumbbell			}
245280183Sdumbbell		case DMI_PRODUCT_NAME:
246280183Sdumbbell		case DMI_BOARD_NAME:
247280183Sdumbbell			if (hw_prod != NULL &&
248280183Sdumbbell			    !strcmp(hw_prod, dsi->matches[i].substr)) {
249280183Sdumbbell				break;
250280183Sdumbbell			} else {
251280183Sdumbbell				res = false;
252280183Sdumbbell				goto out;
253280183Sdumbbell			}
254280183Sdumbbell		default:
255280183Sdumbbell			res = false;
256280183Sdumbbell			goto out;
257280183Sdumbbell		}
258280183Sdumbbell	}
259280183Sdumbbellout:
260280183Sdumbbell	freeenv(hw_vendor);
261280183Sdumbbell	freeenv(hw_prod);
262280183Sdumbbell
263280183Sdumbbell	return (res);
264280183Sdumbbell}
265280183Sdumbbell
266280183Sdumbbellbool
267280183Sdumbbelldmi_check_system(const struct dmi_system_id *sysid)
268280183Sdumbbell{
269280183Sdumbbell	const struct dmi_system_id *dsi;
270280183Sdumbbell	bool res;
271280183Sdumbbell
272280183Sdumbbell	for (res = false, dsi = sysid; dsi->matches[0].slot != 0 ; dsi++) {
273280183Sdumbbell		if (dmi_found(dsi)) {
274280183Sdumbbell			res = true;
275280183Sdumbbell			if (dsi->callback != NULL && dsi->callback(dsi))
276280183Sdumbbell				break;
277280183Sdumbbell		}
278280183Sdumbbell	}
279280183Sdumbbell	return (res);
280280183Sdumbbell}
281280183Sdumbbell
282280183Sdumbbellint
283280183Sdumbbelldrm_mtrr_add(unsigned long offset, unsigned long size, unsigned int flags)
284280183Sdumbbell{
285280183Sdumbbell	int act;
286280183Sdumbbell	struct mem_range_desc mrdesc;
287280183Sdumbbell
288280183Sdumbbell	mrdesc.mr_base = offset;
289280183Sdumbbell	mrdesc.mr_len = size;
290280183Sdumbbell	mrdesc.mr_flags = flags;
291280183Sdumbbell	act = MEMRANGE_SET_UPDATE;
292280183Sdumbbell	strlcpy(mrdesc.mr_owner, "drm", sizeof(mrdesc.mr_owner));
293280183Sdumbbell	return (-mem_range_attr_set(&mrdesc, &act));
294280183Sdumbbell}
295280183Sdumbbell
296280183Sdumbbellint
297280183Sdumbbelldrm_mtrr_del(int handle __unused, unsigned long offset, unsigned long size,
298280183Sdumbbell    unsigned int flags)
299280183Sdumbbell{
300280183Sdumbbell	int act;
301280183Sdumbbell	struct mem_range_desc mrdesc;
302280183Sdumbbell
303280183Sdumbbell	mrdesc.mr_base = offset;
304280183Sdumbbell	mrdesc.mr_len = size;
305280183Sdumbbell	mrdesc.mr_flags = flags;
306280183Sdumbbell	act = MEMRANGE_SET_REMOVE;
307280183Sdumbbell	strlcpy(mrdesc.mr_owner, "drm", sizeof(mrdesc.mr_owner));
308280183Sdumbbell	return (-mem_range_attr_set(&mrdesc, &act));
309280183Sdumbbell}
310280183Sdumbbell
311280183Sdumbbellvoid
312280183Sdumbbelldrm_clflush_pages(vm_page_t *pages, unsigned long num_pages)
313280183Sdumbbell{
314280183Sdumbbell
315280183Sdumbbell#if defined(__i386__) || defined(__amd64__)
316280183Sdumbbell	pmap_invalidate_cache_pages(pages, num_pages);
317280183Sdumbbell#else
318280183Sdumbbell	DRM_ERROR("drm_clflush_pages not implemented on this architecture");
319280183Sdumbbell#endif
320280183Sdumbbell}
321280183Sdumbbell
322280183Sdumbbellvoid
323280183Sdumbbelldrm_clflush_virt_range(char *addr, unsigned long length)
324280183Sdumbbell{
325280183Sdumbbell
326280183Sdumbbell#if defined(__i386__) || defined(__amd64__)
327280183Sdumbbell	pmap_invalidate_cache_range((vm_offset_t)addr,
328280183Sdumbbell	    (vm_offset_t)addr + length, TRUE);
329280183Sdumbbell#else
330280183Sdumbbell	DRM_ERROR("drm_clflush_virt_range not implemented on this architecture");
331280183Sdumbbell#endif
332280183Sdumbbell}
333280183Sdumbbell
334280183Sdumbbell#if DRM_LINUX
335280183Sdumbbell
336280183Sdumbbell#include <sys/sysproto.h>
337280183Sdumbbell
338280183SdumbbellMODULE_DEPEND(DRIVER_NAME, linux, 1, 1, 1);
339280183Sdumbbell
340280183Sdumbbell#define LINUX_IOCTL_DRM_MIN		0x6400
341280183Sdumbbell#define LINUX_IOCTL_DRM_MAX		0x64ff
342280183Sdumbbell
343280183Sdumbbellstatic linux_ioctl_function_t drm_linux_ioctl;
344280183Sdumbbellstatic struct linux_ioctl_handler drm_handler = {drm_linux_ioctl,
345280183Sdumbbell    LINUX_IOCTL_DRM_MIN, LINUX_IOCTL_DRM_MAX};
346280183Sdumbbell
347280183Sdumbbell/* The bits for in/out are switched on Linux */
348280183Sdumbbell#define LINUX_IOC_IN	IOC_OUT
349280183Sdumbbell#define LINUX_IOC_OUT	IOC_IN
350280183Sdumbbell
351280183Sdumbbellstatic int
352280183Sdumbbelldrm_linux_ioctl(DRM_STRUCTPROC *p, struct linux_ioctl_args* args)
353280183Sdumbbell{
354280183Sdumbbell	int error;
355280183Sdumbbell	int cmd = args->cmd;
356280183Sdumbbell
357280183Sdumbbell	args->cmd &= ~(LINUX_IOC_IN | LINUX_IOC_OUT);
358280183Sdumbbell	if (cmd & LINUX_IOC_IN)
359280183Sdumbbell		args->cmd |= IOC_IN;
360280183Sdumbbell	if (cmd & LINUX_IOC_OUT)
361280183Sdumbbell		args->cmd |= IOC_OUT;
362280183Sdumbbell
363280183Sdumbbell	error = ioctl(p, (struct ioctl_args *)args);
364280183Sdumbbell
365280183Sdumbbell	return error;
366280183Sdumbbell}
367280183Sdumbbell#endif /* DRM_LINUX */
368280183Sdumbbell
369280183Sdumbbellstatic int
370280183Sdumbbelldrm_modevent(module_t mod, int type, void *data)
371280183Sdumbbell{
372280183Sdumbbell
373280183Sdumbbell	switch (type) {
374280183Sdumbbell	case MOD_LOAD:
375280183Sdumbbell		TUNABLE_INT_FETCH("drm.debug", &drm_debug);
376280183Sdumbbell		TUNABLE_INT_FETCH("drm.notyet", &drm_notyet);
377280183Sdumbbell		break;
378280183Sdumbbell	}
379280183Sdumbbell	return (0);
380280183Sdumbbell}
381280183Sdumbbell
382280183Sdumbbellstatic moduledata_t drm_mod = {
383280183Sdumbbell	"drmn",
384280183Sdumbbell	drm_modevent,
385280183Sdumbbell	0
386280183Sdumbbell};
387280183Sdumbbell
388280183SdumbbellDECLARE_MODULE(drmn, drm_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
389280183SdumbbellMODULE_VERSION(drmn, 1);
390280183SdumbbellMODULE_DEPEND(drmn, agp, 1, 1, 1);
391280183SdumbbellMODULE_DEPEND(drmn, pci, 1, 1, 1);
392280183SdumbbellMODULE_DEPEND(drmn, mem, 1, 1, 1);
393280183SdumbbellMODULE_DEPEND(drmn, iicbus, 1, 1, 1);
394