radeon_drm_winsys.c revision 1b542aca6e998e544a90ccff310f74b2811b8db0
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#ifndef RADEON_INFO_TILING_CONFIG
45#define RADEON_INFO_TILING_CONFIG 6
46#endif
47
48#ifndef RADEON_INFO_WANT_HYPERZ
49#define RADEON_INFO_WANT_HYPERZ 7
50#endif
51
52#ifndef RADEON_INFO_WANT_CMASK
53#define RADEON_INFO_WANT_CMASK 8
54#endif
55
56#ifndef RADEON_INFO_CLOCK_CRYSTAL_FREQ
57#define RADEON_INFO_CLOCK_CRYSTAL_FREQ 9
58#endif
59
60#ifndef RADEON_INFO_NUM_BACKENDS
61#define RADEON_INFO_NUM_BACKENDS 0xa
62#endif
63
64#ifndef RADEON_INFO_NUM_TILE_PIPES
65#define RADEON_INFO_NUM_TILE_PIPES 0xb
66#endif
67
68#ifndef RADEON_INFO_BACKEND_MAP
69#define RADEON_INFO_BACKEND_MAP 0xd
70#endif
71
72/* Enable/disable feature access for one command stream.
73 * If enable == TRUE, return TRUE on success.
74 * Otherwise, return FALSE.
75 *
76 * We basically do the same thing kernel does, because we have to deal
77 * with multiple contexts (here command streams) backed by one winsys. */
78static boolean radeon_set_fd_access(struct radeon_drm_cs *applier,
79                                    struct radeon_drm_cs **owner,
80                                    pipe_mutex *mutex,
81                                    unsigned request, boolean enable)
82{
83    struct drm_radeon_info info = {0};
84    unsigned value = enable ? 1 : 0;
85
86    pipe_mutex_lock(*mutex);
87
88    /* Early exit if we are sure the request will fail. */
89    if (enable) {
90        if (*owner) {
91            pipe_mutex_unlock(*mutex);
92            return FALSE;
93        }
94    } else {
95        if (*owner != applier) {
96            pipe_mutex_unlock(*mutex);
97            return FALSE;
98        }
99    }
100
101    /* Pass through the request to the kernel. */
102    info.value = (unsigned long)&value;
103    info.request = request;
104    if (drmCommandWriteRead(applier->ws->fd, DRM_RADEON_INFO,
105                            &info, sizeof(info)) != 0) {
106        pipe_mutex_unlock(*mutex);
107        return FALSE;
108    }
109
110    /* Update the rights in the winsys. */
111    if (enable) {
112        if (value) {
113            *owner = applier;
114            fprintf(stderr, "radeon: Acquired Hyper-Z.\n");
115            pipe_mutex_unlock(*mutex);
116            return TRUE;
117        }
118    } else {
119        *owner = NULL;
120        fprintf(stderr, "radeon: Released Hyper-Z.\n");
121    }
122
123    pipe_mutex_unlock(*mutex);
124    return FALSE;
125}
126
127static boolean radeon_get_drm_value(int fd, unsigned request,
128                                    const char *errname, uint32_t *out)
129{
130    struct drm_radeon_info info = {0};
131    int retval;
132
133    info.value = (unsigned long)out;
134    info.request = request;
135
136    retval = drmCommandWriteRead(fd, DRM_RADEON_INFO, &info, sizeof(info));
137    if (retval && errname) {
138        fprintf(stderr, "radeon: Failed to get %s, error number %d\n",
139                errname, retval);
140        return FALSE;
141    }
142    return TRUE;
143}
144
145/* Helper function to do the ioctls needed for setup and init. */
146static boolean do_winsys_init(struct radeon_drm_winsys *ws)
147{
148    struct drm_radeon_gem_info gem_info = {0};
149    int retval;
150    drmVersionPtr version;
151
152    /* We do things in a specific order here.
153     *
154     * DRM version first. We need to be sure we're running on a KMS chipset.
155     * This is also for some features.
156     *
157     * Then, the PCI ID. This is essential and should return usable numbers
158     * for all Radeons. If this fails, we probably got handed an FD for some
159     * non-Radeon card.
160     *
161     * The GEM info is actually bogus on the kernel side, as well as our side
162     * (see radeon_gem_info_ioctl in radeon_gem.c) but that's alright because
163     * we don't actually use the info for anything yet.
164     *
165     * The GB and Z pipe requests should always succeed, but they might not
166     * return sensical values for all chipsets, but that's alright because
167     * the pipe drivers already know that.
168     */
169
170    /* Get DRM version. */
171    version = drmGetVersion(ws->fd);
172    if (version->version_major != 2 ||
173        version->version_minor < 3) {
174        fprintf(stderr, "%s: DRM version is %d.%d.%d but this driver is "
175                "only compatible with 2.3.x (kernel 2.6.34) or later.\n",
176                __FUNCTION__,
177                version->version_major,
178                version->version_minor,
179                version->version_patchlevel);
180        drmFreeVersion(version);
181        return FALSE;
182    }
183
184    ws->info.drm_major = version->version_major;
185    ws->info.drm_minor = version->version_minor;
186    ws->info.drm_patchlevel = version->version_patchlevel;
187    drmFreeVersion(version);
188
189    /* Get PCI ID. */
190    if (!radeon_get_drm_value(ws->fd, RADEON_INFO_DEVICE_ID, "PCI ID",
191                              &ws->info.pci_id))
192        return FALSE;
193
194    /* Check PCI ID. */
195    switch (ws->info.pci_id) {
196#define CHIPSET(pci_id, name, family) case pci_id:
197#include "pci_ids/r300_pci_ids.h"
198#undef CHIPSET
199        ws->gen = R300;
200        break;
201
202#define CHIPSET(pci_id, name, family) case pci_id:
203#include "pci_ids/r600_pci_ids.h"
204#undef CHIPSET
205        ws->gen = R600;
206        break;
207
208    default:
209        fprintf(stderr, "radeon: Invalid PCI ID.\n");
210        return FALSE;
211    }
212
213    /* Get GEM info. */
214    retval = drmCommandWriteRead(ws->fd, DRM_RADEON_GEM_INFO,
215            &gem_info, sizeof(gem_info));
216    if (retval) {
217        fprintf(stderr, "radeon: Failed to get MM info, error number %d\n",
218                retval);
219        return FALSE;
220    }
221    ws->info.gart_size = gem_info.gart_size;
222    ws->info.vram_size = gem_info.vram_size;
223
224    ws->num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
225
226    /* Generation-specific queries. */
227    if (ws->gen == R300) {
228        if (!radeon_get_drm_value(ws->fd, RADEON_INFO_NUM_GB_PIPES,
229                                  "GB pipe count",
230                                  &ws->info.r300_num_gb_pipes))
231            return FALSE;
232
233        if (!radeon_get_drm_value(ws->fd, RADEON_INFO_NUM_Z_PIPES,
234                                  "Z pipe count",
235                                  &ws->info.r300_num_z_pipes))
236            return FALSE;
237    }
238    else if (ws->gen == R600) {
239        if (ws->info.drm_minor >= 9 &&
240            !radeon_get_drm_value(ws->fd, RADEON_INFO_NUM_BACKENDS,
241                                  "num backends",
242                                  &ws->info.r600_num_backends))
243            return FALSE;
244
245        /* get the GPU counter frequency, failure is not fatal */
246        radeon_get_drm_value(ws->fd, RADEON_INFO_CLOCK_CRYSTAL_FREQ, NULL,
247                             &ws->info.r600_clock_crystal_freq);
248
249        radeon_get_drm_value(ws->fd, RADEON_INFO_TILING_CONFIG, NULL,
250                             &ws->info.r600_tiling_config);
251
252        if (ws->info.drm_minor >= 11) {
253            radeon_get_drm_value(ws->fd, RADEON_INFO_NUM_TILE_PIPES, NULL,
254                                 &ws->info.r600_num_tile_pipes);
255
256            if (radeon_get_drm_value(ws->fd, RADEON_INFO_BACKEND_MAP, NULL,
257                                      &ws->info.r600_backend_map))
258                ws->info.r600_backend_map_valid = TRUE;
259        }
260    }
261
262    return TRUE;
263}
264
265static void radeon_winsys_destroy(struct radeon_winsys *rws)
266{
267    struct radeon_drm_winsys *ws = (struct radeon_drm_winsys*)rws;
268
269    pipe_mutex_destroy(ws->hyperz_owner_mutex);
270    pipe_mutex_destroy(ws->cmask_owner_mutex);
271
272    ws->cman->destroy(ws->cman);
273    ws->kman->destroy(ws->kman);
274    FREE(rws);
275}
276
277static void radeon_query_info(struct radeon_winsys *rws,
278                              struct radeon_info *info)
279{
280    *info = ((struct radeon_drm_winsys *)rws)->info;
281}
282
283static boolean radeon_cs_request_feature(struct radeon_winsys_cs *rcs,
284                                         enum radeon_feature_id fid,
285                                         boolean enable)
286{
287    struct radeon_drm_cs *cs = radeon_drm_cs(rcs);
288
289    switch (fid) {
290    case RADEON_FID_R300_HYPERZ_ACCESS:
291        if (debug_get_bool_option("RADEON_HYPERZ", FALSE)) {
292            return radeon_set_fd_access(cs, &cs->ws->hyperz_owner,
293                                        &cs->ws->hyperz_owner_mutex,
294                                        RADEON_INFO_WANT_HYPERZ, enable);
295        } else {
296            return FALSE;
297        }
298
299    case RADEON_FID_R300_CMASK_ACCESS:
300        if (debug_get_bool_option("RADEON_CMASK", FALSE)) {
301            return radeon_set_fd_access(cs, &cs->ws->cmask_owner,
302                                        &cs->ws->cmask_owner_mutex,
303                                        RADEON_INFO_WANT_CMASK, enable);
304        } else {
305            return FALSE;
306        }
307    }
308    return FALSE;
309}
310
311struct radeon_winsys *radeon_drm_winsys_create(int fd)
312{
313    struct radeon_drm_winsys *ws = CALLOC_STRUCT(radeon_drm_winsys);
314    if (!ws) {
315        return NULL;
316    }
317
318    ws->fd = fd;
319    ws->info.fd = fd;
320
321    if (!do_winsys_init(ws))
322        goto fail;
323
324    /* Create managers. */
325    ws->kman = radeon_bomgr_create(ws);
326    if (!ws->kman)
327	goto fail;
328    ws->cman = pb_cache_manager_create(ws->kman, 1000000);
329    if (!ws->cman)
330	goto fail;
331
332    /* Set functions. */
333    ws->base.destroy = radeon_winsys_destroy;
334    ws->base.query_info = radeon_query_info;
335    ws->base.cs_request_feature = radeon_cs_request_feature;
336
337    radeon_bomgr_init_functions(ws);
338    radeon_drm_cs_init_functions(ws);
339
340    pipe_mutex_init(ws->hyperz_owner_mutex);
341    pipe_mutex_init(ws->cmask_owner_mutex);
342
343    return &ws->base;
344
345fail:
346    if (ws->cman)
347	ws->cman->destroy(ws->cman);
348    if (ws->kman)
349	ws->kman->destroy(ws->kman);
350    FREE(ws);
351    return NULL;
352}
353