1/*
2 * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and
3 *                VA Linux Systems Inc., Fremont, California.
4 *
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation on the rights to use, copy, modify, merge,
11 * publish, distribute, sublicense, and/or sell copies of the Software,
12 * and to permit persons to whom the Software is furnished to do so,
13 * subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial
17 * portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 * NON-INFRINGEMENT.  IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
23 * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 */
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include <errno.h>
34#include <string.h>
35#include <stdlib.h>
36#include <sys/stat.h>
37#include <fcntl.h>
38
39/*
40 * Authors:
41 *   Kevin E. Martin <martin@xfree86.org>
42 *   Rickard E. Faith <faith@valinux.com>
43 * KMS support - Dave Airlie <airlied@redhat.com>
44 */
45
46#include "amdgpu_probe.h"
47#include "amdgpu_version.h"
48#include "amdgpu_drv.h"
49
50#include "xf86.h"
51
52#include "xf86drmMode.h"
53
54#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
55#include <xf86_OSproc.h>
56#endif
57
58#include <xf86platformBus.h>
59
60_X_EXPORT int gAMDGPUEntityIndex = -1;
61
62/* Return the options for supported chipset 'n'; NULL otherwise */
63static const OptionInfoRec *AMDGPUAvailableOptions(int chipid, int busid)
64{
65	return AMDGPUOptionsWeak();
66}
67
68static SymTabRec AMDGPUAny[] = {
69	{ 0, "All GPUs supported by the amdgpu kernel driver" },
70	{ -1, NULL }
71};
72
73/* Return the string name for supported chipset 'n'; NULL otherwise. */
74static void AMDGPUIdentify(int flags)
75{
76	xf86PrintChipsets(AMDGPU_NAME, "Driver for AMD Radeon", AMDGPUAny);
77}
78
79static Bool amdgpu_device_matches(const drmDevicePtr device,
80				  const struct pci_device *dev)
81{
82	return (device->bustype == DRM_BUS_PCI &&
83		device->businfo.pci->domain == dev->domain &&
84		device->businfo.pci->bus == dev->bus &&
85		device->businfo.pci->dev == dev->dev &&
86		device->businfo.pci->func == dev->func);
87}
88
89static Bool amdgpu_kernel_mode_enabled(ScrnInfoPtr pScrn)
90{
91#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
92	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
93	const char *busIdString = pAMDGPUEnt->busid;
94	int ret = drmCheckModesettingSupported(busIdString);
95
96	if (ret) {
97		if (xf86LoadKernelModule("amdgpukms"))
98			ret = drmCheckModesettingSupported(busIdString);
99	}
100	if (ret) {
101		xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 0,
102			       "[KMS] drm report modesetting isn't supported.\n");
103		return FALSE;
104	}
105
106#endif
107	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 0,
108		       "[KMS] Kernel modesetting enabled.\n");
109	return TRUE;
110}
111
112static int amdgpu_kernel_open_fd(ScrnInfoPtr pScrn,
113				 struct pci_device *pci_dev,
114				 struct xf86_platform_device *platform_dev,
115				 AMDGPUEntPtr pAMDGPUEnt)
116{
117#define MAX_DRM_DEVICES 64
118	drmDevicePtr devices[MAX_DRM_DEVICES];
119	struct pci_device *dev;
120	const char *path;
121	int fd = -1, i, ret;
122
123	if (platform_dev)
124		dev = platform_dev->pdev;
125	else
126		dev = pci_dev;
127
128	XNFasprintf(&pAMDGPUEnt->busid, "pci:%04x:%02x:%02x.%u",
129		    dev->domain, dev->bus, dev->dev, dev->func);
130
131	if (platform_dev) {
132#ifdef ODEV_ATTRIB_FD
133		fd = xf86_get_platform_device_int_attrib(platform_dev,
134							 ODEV_ATTRIB_FD, -1);
135		if (fd != -1)
136			return fd;
137#endif
138
139#ifdef ODEV_ATTRIB_PATH
140		path = xf86_get_platform_device_attrib(platform_dev,
141						       ODEV_ATTRIB_PATH);
142
143		fd = open(path, O_RDWR | O_CLOEXEC);
144		if (fd != -1)
145			return fd;
146#endif
147	}
148
149	if (!amdgpu_kernel_mode_enabled(pScrn))
150		return -1;
151
152	ret = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
153	if (ret == -1) {
154		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
155			   "[drm] Failed to retrieve DRM devices information.\n");
156		return -1;
157	}
158	for (i = 0; i < ret; i++) {
159		if (amdgpu_device_matches(devices[i], dev) &&
160		    devices[i]->available_nodes & (1 << DRM_NODE_PRIMARY)) {
161			path = devices[i]->nodes[DRM_NODE_PRIMARY];
162			fd = open(path, O_RDWR | O_CLOEXEC);
163			break;
164		}
165	}
166	drmFreeDevices(devices, ret);
167
168	if (fd == -1)
169		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
170			   "[drm] Failed to open DRM device for %s: %s\n",
171			   pAMDGPUEnt->busid, strerror(errno));
172	return fd;
173#undef MAX_DRM_DEVICES
174}
175
176void amdgpu_kernel_close_fd(AMDGPUEntPtr pAMDGPUEnt)
177{
178#if defined(XSERVER_PLATFORM_BUS) && defined(XF86_PDEV_SERVER_FD)
179	if (!(pAMDGPUEnt->platform_dev &&
180	      pAMDGPUEnt->platform_dev->flags & XF86_PDEV_SERVER_FD))
181#endif
182		close(pAMDGPUEnt->fd);
183	pAMDGPUEnt->fd = -1;
184}
185
186/* Pull a local version of the helper. It's available since 2.4.98 yet
187 * it may be too new for some distributions.
188 */
189static int local_drmIsMaster(int fd)
190{
191	return drmAuthMagic(fd, 0) != -EACCES;
192}
193
194static Bool amdgpu_open_drm_master(ScrnInfoPtr pScrn,
195				   struct pci_device *pci_dev,
196				   struct xf86_platform_device *platform_dev,
197				   AMDGPUEntPtr pAMDGPUEnt)
198{
199	drmSetVersion sv;
200	int err;
201
202	pAMDGPUEnt->fd = amdgpu_kernel_open_fd(pScrn, pci_dev, platform_dev, pAMDGPUEnt);
203	if (pAMDGPUEnt->fd == -1)
204		return FALSE;
205
206	/* Check that what we opened was a master or a master-capable FD,
207	 * by setting the version of the interface we'll use to talk to it.
208	 * (see DRIOpenDRMMaster() in DRI1)
209	 */
210	sv.drm_di_major = 1;
211	sv.drm_di_minor = 1;
212	sv.drm_dd_major = -1;
213	sv.drm_dd_minor = -1;
214	err = drmSetInterfaceVersion(pAMDGPUEnt->fd, &sv);
215	if (err != 0) {
216		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
217			   "[drm] failed to set drm interface version.\n");
218		amdgpu_kernel_close_fd(pAMDGPUEnt);
219		return FALSE;
220	}
221
222	/* Check that what we opened is a master or a master-capable FD */
223	if (!local_drmIsMaster(pAMDGPUEnt->fd)) {
224		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
225			   "[drm] device is not DRM master.\n");
226		amdgpu_kernel_close_fd(pAMDGPUEnt);
227		return FALSE;
228	}
229
230	return TRUE;
231}
232
233static Bool amdgpu_device_setup(ScrnInfoPtr pScrn,
234				struct pci_device *pci_dev,
235				struct xf86_platform_device *platform_dev,
236				AMDGPUEntPtr pAMDGPUEnt)
237{
238	uint32_t major_version;
239	uint32_t minor_version;
240
241	pAMDGPUEnt->platform_dev = platform_dev;
242	if (!amdgpu_open_drm_master(pScrn, pci_dev, platform_dev,
243				    pAMDGPUEnt))
244		return FALSE;
245
246	if (amdgpu_device_initialize(pAMDGPUEnt->fd,
247				     &major_version,
248				     &minor_version,
249				     &pAMDGPUEnt->pDev)) {
250		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
251			   "amdgpu_device_initialize failed\n");
252		goto error_amdgpu;
253	}
254
255	return TRUE;
256
257error_amdgpu:
258	amdgpu_kernel_close_fd(pAMDGPUEnt);
259	return FALSE;
260}
261
262static Bool
263amdgpu_probe(ScrnInfoPtr pScrn, int entity_num,
264	     struct pci_device *pci_dev, struct xf86_platform_device *dev)
265{
266	EntityInfoPtr pEnt = NULL;
267	DevUnion *pPriv;
268	AMDGPUEntPtr pAMDGPUEnt;
269
270	if (!pScrn)
271		return FALSE;
272
273	pScrn->driverVersion = AMDGPU_VERSION_CURRENT;
274	pScrn->driverName = AMDGPU_DRIVER_NAME;
275	pScrn->name = AMDGPU_NAME;
276	pScrn->Probe = NULL;
277	pScrn->PreInit = AMDGPUPreInit_KMS;
278	pScrn->ScreenInit = AMDGPUScreenInit_KMS;
279	pScrn->SwitchMode = AMDGPUSwitchMode_KMS;
280	pScrn->AdjustFrame = AMDGPUAdjustFrame_KMS;
281	pScrn->EnterVT = AMDGPUEnterVT_KMS;
282	pScrn->LeaveVT = AMDGPULeaveVT_KMS;
283	pScrn->FreeScreen = AMDGPUFreeScreen_KMS;
284	pScrn->ValidMode = AMDGPUValidMode;
285
286	pEnt = xf86GetEntityInfo(entity_num);
287
288	/* Create a AMDGPUEntity for all chips, even with old single head
289	 * Radeon, need to use pAMDGPUEnt for new monitor detection routines.
290	 */
291	xf86SetEntitySharable(entity_num);
292
293	if (gAMDGPUEntityIndex == -1)
294		gAMDGPUEntityIndex = xf86AllocateEntityPrivateIndex();
295
296	pPriv = xf86GetEntityPrivate(pEnt->index, gAMDGPUEntityIndex);
297
298	if (!pPriv->ptr) {
299		pPriv->ptr = xnfcalloc(sizeof(AMDGPUEntRec), 1);
300		if (!pPriv->ptr)
301			goto error;
302
303		pAMDGPUEnt = pPriv->ptr;
304		if (!amdgpu_device_setup(pScrn, pci_dev, dev, pAMDGPUEnt))
305			goto error;
306
307		pAMDGPUEnt->fd_ref = 1;
308
309	} else {
310		pAMDGPUEnt = pPriv->ptr;
311
312		if (pAMDGPUEnt->fd_ref == ARRAY_SIZE(pAMDGPUEnt->scrn)) {
313			xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
314				   "Only up to %u Zaphod instances supported\n",
315				   (unsigned)ARRAY_SIZE(pAMDGPUEnt->scrn));
316			goto error;
317		}
318
319		pAMDGPUEnt->fd_ref++;
320	}
321
322	xf86SetEntityInstanceForScreen(pScrn, pEnt->index,
323				       xf86GetNumEntityInstances(pEnt->
324								 index)
325				       - 1);
326	free(pEnt);
327
328	return TRUE;
329
330error:
331	free(pEnt);
332	return FALSE;
333}
334
335static Bool
336amdgpu_pci_probe(DriverPtr pDriver,
337		 int entity_num, struct pci_device *device, intptr_t match_data)
338{
339	ScrnInfoPtr pScrn = xf86ConfigPciEntity(NULL, 0, entity_num, NULL,
340						NULL, NULL, NULL, NULL, NULL);
341
342	return amdgpu_probe(pScrn, entity_num, device, NULL);
343}
344
345static Bool AMDGPUDriverFunc(ScrnInfoPtr scrn, xorgDriverFuncOp op, void *data)
346{
347	xorgHWFlags *flag;
348
349	switch (op) {
350	case GET_REQUIRED_HW_INTERFACES:
351		flag = (CARD32 *) data;
352		(*flag) = 0;
353		return TRUE;
354#if XORG_VERSION_CURRENT > XORG_VERSION_NUMERIC(1,15,99,0,0)
355	case SUPPORTS_SERVER_FDS:
356		return TRUE;
357#endif
358       default:
359		return FALSE;
360	}
361}
362
363#ifdef XSERVER_PLATFORM_BUS
364static Bool
365amdgpu_platform_probe(DriverPtr pDriver,
366		      int entity_num, int flags,
367		      struct xf86_platform_device *dev, intptr_t match_data)
368{
369	ScrnInfoPtr pScrn;
370	int scr_flags = 0;
371
372	if (!dev->pdev)
373		return FALSE;
374
375	if (flags & PLATFORM_PROBE_GPU_SCREEN)
376		scr_flags = XF86_ALLOCATE_GPU_SCREEN;
377
378	pScrn = xf86AllocateScreen(pDriver, scr_flags);
379	if (xf86IsEntitySharable(entity_num))
380		xf86SetEntityShared(entity_num);
381	xf86AddEntityToScreen(pScrn, entity_num);
382
383	return amdgpu_probe(pScrn, entity_num, NULL, dev);
384}
385#endif
386
387static const struct pci_id_match amdgpu_device_match[] = {
388    {0x1002, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0, 0},
389    {0, 0, 0},
390};
391
392DriverRec AMDGPU = {
393	AMDGPU_VERSION_CURRENT,
394	AMDGPU_DRIVER_NAME,
395	AMDGPUIdentify,
396	NULL,
397	AMDGPUAvailableOptions,
398	NULL,
399	0,
400	AMDGPUDriverFunc,
401	amdgpu_device_match,
402	amdgpu_pci_probe,
403#ifdef XSERVER_PLATFORM_BUS
404	amdgpu_platform_probe
405#endif
406};
407