radeon_drm_winsys.c revision 2f14202f52c9f61f5bb5bfb6beaf954ef5c18de9
1/*
2 * Copyright © 2009 Corbin Simpson
3 * Copyright © 2011 Marek Olšák <maraeo@gmail.com>
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a 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 SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
16 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
18 * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 * The above copyright notice and this permission notice (including the
24 * next paragraph) shall be included in all copies or substantial portions
25 * of the Software.
26 */
27/*
28 * Authors:
29 *      Corbin Simpson <MostAwesomeDude@gmail.com>
30 *      Joakim Sindholt <opensource@zhasha.com>
31 *      Marek Olšák <maraeo@gmail.com>
32 */
33
34#include "radeon_drm_bo.h"
35#include "radeon_drm_cs.h"
36#include "radeon_drm_public.h"
37
38#include "pipebuffer/pb_bufmgr.h"
39#include "util/u_memory.h"
40
41#include <xf86drm.h>
42#include <stdio.h>
43
44/*
45 * this are copy from radeon_drm, once an updated libdrm is released
46 * we should bump configure.ac requirement for it and remove the following
47 * field
48 */
49#ifndef RADEON_INFO_TILING_CONFIG
50#define RADEON_INFO_TILING_CONFIG 6
51#endif
52
53#ifndef RADEON_INFO_WANT_HYPERZ
54#define RADEON_INFO_WANT_HYPERZ 7
55#endif
56
57#ifndef RADEON_INFO_WANT_CMASK
58#define RADEON_INFO_WANT_CMASK 8
59#endif
60
61#ifndef RADEON_INFO_CLOCK_CRYSTAL_FREQ
62#define RADEON_INFO_CLOCK_CRYSTAL_FREQ 9
63#endif
64
65#ifndef RADEON_INFO_NUM_BACKENDS
66#define RADEON_INFO_NUM_BACKENDS 0xa
67#endif
68
69#ifndef RADEON_INFO_NUM_TILE_PIPES
70#define RADEON_INFO_NUM_TILE_PIPES 0xb
71#endif
72
73#ifndef RADEON_INFO_BACKEND_MAP
74#define RADEON_INFO_BACKEND_MAP 0xd
75#endif
76
77#ifndef RADEON_INFO_VA_START
78/* virtual address start, va < start are reserved by the kernel */
79#define RADEON_INFO_VA_START        0x0e
80/* maximum size of ib using the virtual memory cs */
81#define RADEON_INFO_IB_VM_MAX_SIZE  0x0f
82#endif
83
84#ifndef RADEON_INFO_MAX_PIPES
85#define RADEON_INFO_MAX_PIPES 0x10
86#endif
87
88
89/* Enable/disable feature access for one command stream.
90 * If enable == TRUE, return TRUE on success.
91 * Otherwise, return FALSE.
92 *
93 * We basically do the same thing kernel does, because we have to deal
94 * with multiple contexts (here command streams) backed by one winsys. */
95static boolean radeon_set_fd_access(struct radeon_drm_cs *applier,
96                                    struct radeon_drm_cs **owner,
97                                    pipe_mutex *mutex,
98                                    unsigned request, boolean enable)
99{
100    struct drm_radeon_info info;
101    unsigned value = enable ? 1 : 0;
102
103    memset(&info, 0, sizeof(info));
104
105    pipe_mutex_lock(*mutex);
106
107    /* Early exit if we are sure the request will fail. */
108    if (enable) {
109        if (*owner) {
110            pipe_mutex_unlock(*mutex);
111            return FALSE;
112        }
113    } else {
114        if (*owner != applier) {
115            pipe_mutex_unlock(*mutex);
116            return FALSE;
117        }
118    }
119
120    /* Pass through the request to the kernel. */
121    info.value = (unsigned long)&value;
122    info.request = request;
123    if (drmCommandWriteRead(applier->ws->fd, DRM_RADEON_INFO,
124                            &info, sizeof(info)) != 0) {
125        pipe_mutex_unlock(*mutex);
126        return FALSE;
127    }
128
129    /* Update the rights in the winsys. */
130    if (enable) {
131        if (value) {
132            *owner = applier;
133            fprintf(stderr, "radeon: Acquired Hyper-Z.\n");
134            pipe_mutex_unlock(*mutex);
135            return TRUE;
136        }
137    } else {
138        *owner = NULL;
139        fprintf(stderr, "radeon: Released Hyper-Z.\n");
140    }
141
142    pipe_mutex_unlock(*mutex);
143    return FALSE;
144}
145
146static boolean radeon_get_drm_value(int fd, unsigned request,
147                                    const char *errname, uint32_t *out)
148{
149    struct drm_radeon_info info;
150    int retval;
151
152    memset(&info, 0, sizeof(info));
153
154    info.value = (unsigned long)out;
155    info.request = request;
156
157    retval = drmCommandWriteRead(fd, DRM_RADEON_INFO, &info, sizeof(info));
158    if (retval) {
159        if (errname) {
160            fprintf(stderr, "radeon: Failed to get %s, error number %d\n",
161                    errname, retval);
162        }
163        return FALSE;
164    }
165    return TRUE;
166}
167
168/* Helper function to do the ioctls needed for setup and init. */
169static boolean do_winsys_init(struct radeon_drm_winsys *ws)
170{
171    struct drm_radeon_gem_info gem_info;
172    int retval;
173    drmVersionPtr version;
174
175    memset(&gem_info, 0, sizeof(gem_info));
176
177    /* We do things in a specific order here.
178     *
179     * DRM version first. We need to be sure we're running on a KMS chipset.
180     * This is also for some features.
181     *
182     * Then, the PCI ID. This is essential and should return usable numbers
183     * for all Radeons. If this fails, we probably got handed an FD for some
184     * non-Radeon card.
185     *
186     * The GEM info is actually bogus on the kernel side, as well as our side
187     * (see radeon_gem_info_ioctl in radeon_gem.c) but that's alright because
188     * we don't actually use the info for anything yet.
189     *
190     * The GB and Z pipe requests should always succeed, but they might not
191     * return sensical values for all chipsets, but that's alright because
192     * the pipe drivers already know that.
193     */
194
195    /* Get DRM version. */
196    version = drmGetVersion(ws->fd);
197    if (version->version_major != 2 ||
198        version->version_minor < 3) {
199        fprintf(stderr, "%s: DRM version is %d.%d.%d but this driver is "
200                "only compatible with 2.3.x (kernel 2.6.34) or later.\n",
201                __FUNCTION__,
202                version->version_major,
203                version->version_minor,
204                version->version_patchlevel);
205        drmFreeVersion(version);
206        return FALSE;
207    }
208
209    ws->info.drm_major = version->version_major;
210    ws->info.drm_minor = version->version_minor;
211    ws->info.drm_patchlevel = version->version_patchlevel;
212    drmFreeVersion(version);
213
214    /* Get PCI ID. */
215    if (!radeon_get_drm_value(ws->fd, RADEON_INFO_DEVICE_ID, "PCI ID",
216                              &ws->info.pci_id))
217        return FALSE;
218
219    /* Check PCI ID. */
220    switch (ws->info.pci_id) {
221#define CHIPSET(pci_id, name, family) case pci_id:
222#include "pci_ids/r300_pci_ids.h"
223#undef CHIPSET
224        ws->gen = R300;
225        break;
226
227#define CHIPSET(pci_id, name, family) case pci_id:
228#include "pci_ids/r600_pci_ids.h"
229#undef CHIPSET
230        ws->gen = R600;
231        break;
232
233#define CHIPSET(pci_id, name, family) case pci_id:
234#include "pci_ids/radeonsi_pci_ids.h"
235#undef CHIPSET
236        ws->gen = SI;
237        break;
238
239    default:
240        fprintf(stderr, "radeon: Invalid PCI ID.\n");
241        return FALSE;
242    }
243
244    /* Get GEM info. */
245    retval = drmCommandWriteRead(ws->fd, DRM_RADEON_GEM_INFO,
246            &gem_info, sizeof(gem_info));
247    if (retval) {
248        fprintf(stderr, "radeon: Failed to get MM info, error number %d\n",
249                retval);
250        return FALSE;
251    }
252    ws->info.gart_size = gem_info.gart_size;
253    ws->info.vram_size = gem_info.vram_size;
254
255    ws->num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
256
257    /* Generation-specific queries. */
258    if (ws->gen == R300) {
259        if (!radeon_get_drm_value(ws->fd, RADEON_INFO_NUM_GB_PIPES,
260                                  "GB pipe count",
261                                  &ws->info.r300_num_gb_pipes))
262            return FALSE;
263
264        if (!radeon_get_drm_value(ws->fd, RADEON_INFO_NUM_Z_PIPES,
265                                  "Z pipe count",
266                                  &ws->info.r300_num_z_pipes))
267            return FALSE;
268    }
269    else if (ws->gen >= R600) {
270        if (ws->info.drm_minor >= 9 &&
271            !radeon_get_drm_value(ws->fd, RADEON_INFO_NUM_BACKENDS,
272                                  "num backends",
273                                  &ws->info.r600_num_backends))
274            return FALSE;
275
276        /* get the GPU counter frequency, failure is not fatal */
277        radeon_get_drm_value(ws->fd, RADEON_INFO_CLOCK_CRYSTAL_FREQ, NULL,
278                             &ws->info.r600_clock_crystal_freq);
279
280        radeon_get_drm_value(ws->fd, RADEON_INFO_TILING_CONFIG, NULL,
281                             &ws->info.r600_tiling_config);
282
283        if (ws->info.drm_minor >= 11) {
284            radeon_get_drm_value(ws->fd, RADEON_INFO_NUM_TILE_PIPES, NULL,
285                                 &ws->info.r600_num_tile_pipes);
286
287            if (radeon_get_drm_value(ws->fd, RADEON_INFO_BACKEND_MAP, NULL,
288                                      &ws->info.r600_backend_map))
289                ws->info.r600_backend_map_valid = TRUE;
290        }
291
292        ws->info.r600_virtual_address = FALSE;
293        if (ws->info.drm_minor >= 13) {
294            ws->info.r600_virtual_address = TRUE;
295            if (!radeon_get_drm_value(ws->fd, RADEON_INFO_VA_START, NULL,
296                                      &ws->info.r600_va_start))
297                ws->info.r600_virtual_address = FALSE;
298            if (!radeon_get_drm_value(ws->fd, RADEON_INFO_IB_VM_MAX_SIZE, NULL,
299                                      &ws->info.r600_ib_vm_max_size))
300                ws->info.r600_virtual_address = FALSE;
301        }
302    }
303
304    /* Get max pipes, this is only needed for compute shaders.  All evergreen+
305     * chips have at least 2 pipes, so we use 2 as a default. */
306    ws->info.r600_max_pipes = 2;
307    radeon_get_drm_value(ws->fd, RADEON_INFO_MAX_PIPES, NULL,
308                         &ws->info.r600_max_pipes);
309
310    return TRUE;
311}
312
313static void radeon_winsys_destroy(struct radeon_winsys *rws)
314{
315    struct radeon_drm_winsys *ws = (struct radeon_drm_winsys*)rws;
316
317    pipe_mutex_destroy(ws->hyperz_owner_mutex);
318    pipe_mutex_destroy(ws->cmask_owner_mutex);
319
320    ws->cman->destroy(ws->cman);
321    ws->kman->destroy(ws->kman);
322    if (ws->gen >= R600) {
323        radeon_surface_manager_free(ws->surf_man);
324    }
325    FREE(rws);
326}
327
328static void radeon_query_info(struct radeon_winsys *rws,
329                              struct radeon_info *info)
330{
331    *info = ((struct radeon_drm_winsys *)rws)->info;
332}
333
334static boolean radeon_cs_request_feature(struct radeon_winsys_cs *rcs,
335                                         enum radeon_feature_id fid,
336                                         boolean enable)
337{
338    struct radeon_drm_cs *cs = radeon_drm_cs(rcs);
339
340    switch (fid) {
341    case RADEON_FID_R300_HYPERZ_ACCESS:
342        if (debug_get_bool_option("RADEON_HYPERZ", FALSE)) {
343            return radeon_set_fd_access(cs, &cs->ws->hyperz_owner,
344                                        &cs->ws->hyperz_owner_mutex,
345                                        RADEON_INFO_WANT_HYPERZ, enable);
346        } else {
347            return FALSE;
348        }
349
350    case RADEON_FID_R300_CMASK_ACCESS:
351        if (debug_get_bool_option("RADEON_CMASK", FALSE)) {
352            return radeon_set_fd_access(cs, &cs->ws->cmask_owner,
353                                        &cs->ws->cmask_owner_mutex,
354                                        RADEON_INFO_WANT_CMASK, enable);
355        } else {
356            return FALSE;
357        }
358    }
359    return FALSE;
360}
361
362static int radeon_drm_winsys_surface_init(struct radeon_winsys *rws,
363                                          struct radeon_surface *surf)
364{
365    struct radeon_drm_winsys *ws = (struct radeon_drm_winsys*)rws;
366
367    return radeon_surface_init(ws->surf_man, surf);
368}
369
370static int radeon_drm_winsys_surface_best(struct radeon_winsys *rws,
371                                          struct radeon_surface *surf)
372{
373    struct radeon_drm_winsys *ws = (struct radeon_drm_winsys*)rws;
374
375    return radeon_surface_best(ws->surf_man, surf);
376}
377
378struct radeon_winsys *radeon_drm_winsys_create(int fd)
379{
380    struct radeon_drm_winsys *ws = CALLOC_STRUCT(radeon_drm_winsys);
381    if (!ws) {
382        return NULL;
383    }
384
385    ws->fd = fd;
386
387    if (!do_winsys_init(ws))
388        goto fail;
389
390    /* Create managers. */
391    ws->kman = radeon_bomgr_create(ws);
392    if (!ws->kman)
393        goto fail;
394    ws->cman = pb_cache_manager_create(ws->kman, 1000000);
395    if (!ws->cman)
396        goto fail;
397
398    if (ws->gen >= R600) {
399        ws->surf_man = radeon_surface_manager_new(fd);
400        if (!ws->surf_man)
401            goto fail;
402    }
403
404    /* Set functions. */
405    ws->base.destroy = radeon_winsys_destroy;
406    ws->base.query_info = radeon_query_info;
407    ws->base.cs_request_feature = radeon_cs_request_feature;
408    ws->base.surface_init = radeon_drm_winsys_surface_init;
409    ws->base.surface_best = radeon_drm_winsys_surface_best;
410
411    radeon_bomgr_init_functions(ws);
412    radeon_drm_cs_init_functions(ws);
413
414    pipe_mutex_init(ws->hyperz_owner_mutex);
415    pipe_mutex_init(ws->cmask_owner_mutex);
416
417    return &ws->base;
418
419fail:
420    if (ws->cman)
421        ws->cman->destroy(ws->cman);
422    if (ws->kman)
423        ws->kman->destroy(ws->kman);
424    if (ws->surf_man)
425        radeon_surface_manager_free(ws->surf_man);
426    FREE(ws);
427    return NULL;
428}
429