122944501Smrg/*
222944501Smrg * Copyright © 2009 Red Hat Inc.
322944501Smrg * All Rights Reserved.
422944501Smrg *
522944501Smrg * Permission is hereby granted, free of charge, to any person obtaining
622944501Smrg * a copy of this software and associated documentation files (the
722944501Smrg * "Software"), to deal in the Software without restriction, including
822944501Smrg * without limitation the rights to use, copy, modify, merge, publish,
922944501Smrg * distribute, sub license, and/or sell copies of the Software, and to
1022944501Smrg * permit persons to whom the Software is furnished to do so, subject to
1122944501Smrg * the following conditions:
1222944501Smrg *
1322944501Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1422944501Smrg * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
1522944501Smrg * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1622944501Smrg * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
1722944501Smrg * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1822944501Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1922944501Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2022944501Smrg * USE OR OTHER DEALINGS IN THE SOFTWARE.
2122944501Smrg *
2222944501Smrg * The above copyright notice and this permission notice (including the
2322944501Smrg * next paragraph) shall be included in all copies or substantial portions
2422944501Smrg * of the Software.
2522944501Smrg */
2622944501Smrg/*
2722944501Smrg */
2822944501Smrg#include <assert.h>
2922944501Smrg#include <errno.h>
3022944501Smrg#include <stdlib.h>
31e6188e58Smrg#include "libdrm_macros.h"
3222944501Smrg#include "radeon_cs.h"
3322944501Smrg#include "radeon_bo_int.h"
3422944501Smrg#include "radeon_cs_int.h"
3522944501Smrg
3622944501Smrgstruct rad_sizes {
3722944501Smrg    int32_t op_read;
3822944501Smrg    int32_t op_gart_write;
3922944501Smrg    int32_t op_vram_write;
4022944501Smrg};
4122944501Smrg
4222944501Smrgstatic inline int radeon_cs_setup_bo(struct radeon_cs_space_check *sc, struct rad_sizes *sizes)
4322944501Smrg{
4422944501Smrg    uint32_t read_domains, write_domain;
4522944501Smrg    struct radeon_bo_int *bo;
4622944501Smrg
4722944501Smrg    bo = sc->bo;
4822944501Smrg    sc->new_accounted = 0;
4922944501Smrg    read_domains = sc->read_domains;
5022944501Smrg    write_domain = sc->write_domain;
5122944501Smrg
5222944501Smrg    /* legacy needs a static check */
5322944501Smrg    if (radeon_bo_is_static((struct radeon_bo *)sc->bo)) {
5422944501Smrg        bo->space_accounted = sc->new_accounted = (read_domains << 16) | write_domain;
5522944501Smrg        return 0;
5622944501Smrg    }
5722944501Smrg
5822944501Smrg    /* already accounted this bo */
5922944501Smrg    if (write_domain && (write_domain == bo->space_accounted)) {
6022944501Smrg        sc->new_accounted = bo->space_accounted;
6122944501Smrg        return 0;
6222944501Smrg    }
6322944501Smrg    if (read_domains && ((read_domains << 16) == bo->space_accounted)) {
6422944501Smrg        sc->new_accounted = bo->space_accounted;
6522944501Smrg        return 0;
6622944501Smrg    }
6722944501Smrg
6822944501Smrg    if (bo->space_accounted == 0) {
69e88f27b3Smrg        if (write_domain) {
70e88f27b3Smrg            if (write_domain == RADEON_GEM_DOMAIN_VRAM)
71e88f27b3Smrg                sizes->op_vram_write += bo->size;
72e88f27b3Smrg            else if (write_domain == RADEON_GEM_DOMAIN_GTT)
73e88f27b3Smrg                sizes->op_gart_write += bo->size;
74e88f27b3Smrg            sc->new_accounted = write_domain;
75e88f27b3Smrg        } else {
7622944501Smrg            sizes->op_read += bo->size;
77e88f27b3Smrg            sc->new_accounted = read_domains << 16;
78e88f27b3Smrg        }
7922944501Smrg    } else {
8022944501Smrg        uint16_t old_read, old_write;
8122944501Smrg
8222944501Smrg        old_read = bo->space_accounted >> 16;
8322944501Smrg        old_write = bo->space_accounted & 0xffff;
8422944501Smrg
8522944501Smrg        if (write_domain && (old_read & write_domain)) {
8622944501Smrg            sc->new_accounted = write_domain;
8722944501Smrg            /* moving from read to a write domain */
8822944501Smrg            if (write_domain == RADEON_GEM_DOMAIN_VRAM) {
8922944501Smrg                sizes->op_read -= bo->size;
9022944501Smrg                sizes->op_vram_write += bo->size;
9122944501Smrg            } else if (write_domain == RADEON_GEM_DOMAIN_GTT) {
9222944501Smrg                sizes->op_read -= bo->size;
9322944501Smrg                sizes->op_gart_write += bo->size;
9422944501Smrg            }
9522944501Smrg        } else if (read_domains & old_write) {
9622944501Smrg            sc->new_accounted = bo->space_accounted & 0xffff;
9722944501Smrg        } else {
9822944501Smrg            /* rewrite the domains */
9922944501Smrg            if (write_domain != old_write)
10022944501Smrg                fprintf(stderr,"WRITE DOMAIN RELOC FAILURE 0x%x %d %d\n", bo->handle, write_domain, old_write);
10122944501Smrg            if (read_domains != old_read)
10222944501Smrg               fprintf(stderr,"READ DOMAIN RELOC FAILURE 0x%x %d %d\n", bo->handle, read_domains, old_read);
10322944501Smrg            return RADEON_CS_SPACE_FLUSH;
10422944501Smrg        }
10522944501Smrg    }
10622944501Smrg    return 0;
10722944501Smrg}
10822944501Smrg
10922944501Smrgstatic int radeon_cs_do_space_check(struct radeon_cs_int *cs, struct radeon_cs_space_check *new_tmp)
11022944501Smrg{
11122944501Smrg    struct radeon_cs_manager *csm = cs->csm;
11222944501Smrg    int i;
11322944501Smrg    struct radeon_bo_int *bo;
11422944501Smrg    struct rad_sizes sizes;
11522944501Smrg    int ret;
11622944501Smrg
11722944501Smrg    /* check the totals for this operation */
11822944501Smrg
11922944501Smrg    if (cs->bo_count == 0 && !new_tmp)
12022944501Smrg        return 0;
12122944501Smrg
12222944501Smrg    memset(&sizes, 0, sizeof(struct rad_sizes));
12322944501Smrg
12422944501Smrg    /* prepare */
12522944501Smrg    for (i = 0; i < cs->bo_count; i++) {
12622944501Smrg        ret = radeon_cs_setup_bo(&cs->bos[i], &sizes);
12722944501Smrg        if (ret)
12822944501Smrg            return ret;
12922944501Smrg    }
13022944501Smrg
13122944501Smrg    if (new_tmp) {
13222944501Smrg        ret = radeon_cs_setup_bo(new_tmp, &sizes);
13322944501Smrg        if (ret)
13422944501Smrg            return ret;
13522944501Smrg    }
13622944501Smrg
13722944501Smrg    if (sizes.op_read < 0)
13822944501Smrg        sizes.op_read = 0;
13922944501Smrg
14022944501Smrg    /* check sizes - operation first */
14122944501Smrg    if ((sizes.op_read + sizes.op_gart_write > csm->gart_limit) ||
14222944501Smrg        (sizes.op_vram_write > csm->vram_limit)) {
14322944501Smrg        return RADEON_CS_SPACE_OP_TO_BIG;
14422944501Smrg    }
14522944501Smrg
14622944501Smrg    if (((csm->vram_write_used + sizes.op_vram_write) > csm->vram_limit) ||
14722944501Smrg        ((csm->read_used + csm->gart_write_used + sizes.op_gart_write + sizes.op_read) > csm->gart_limit)) {
14822944501Smrg        return RADEON_CS_SPACE_FLUSH;
14922944501Smrg    }
15022944501Smrg
15122944501Smrg    csm->gart_write_used += sizes.op_gart_write;
15222944501Smrg    csm->vram_write_used += sizes.op_vram_write;
15322944501Smrg    csm->read_used += sizes.op_read;
15422944501Smrg    /* commit */
15522944501Smrg    for (i = 0; i < cs->bo_count; i++) {
15622944501Smrg        bo = cs->bos[i].bo;
15722944501Smrg        bo->space_accounted = cs->bos[i].new_accounted;
15822944501Smrg    }
15922944501Smrg    if (new_tmp)
16022944501Smrg        new_tmp->bo->space_accounted = new_tmp->new_accounted;
16122944501Smrg
16222944501Smrg    return RADEON_CS_SPACE_OK;
16322944501Smrg}
16422944501Smrg
1657cdc0497Smrgdrm_public void
166baaff307Smrgradeon_cs_space_add_persistent_bo(struct radeon_cs *cs, struct radeon_bo *bo,
167baaff307Smrg                                  uint32_t read_domains, uint32_t write_domain)
16822944501Smrg{
16922944501Smrg    struct radeon_cs_int *csi = (struct radeon_cs_int *)cs;
17022944501Smrg    struct radeon_bo_int *boi = (struct radeon_bo_int *)bo;
17122944501Smrg    int i;
17222944501Smrg    for (i = 0; i < csi->bo_count; i++) {
17322944501Smrg        if (csi->bos[i].bo == boi &&
17422944501Smrg            csi->bos[i].read_domains == read_domains &&
17522944501Smrg            csi->bos[i].write_domain == write_domain)
17622944501Smrg            return;
17722944501Smrg    }
17822944501Smrg    radeon_bo_ref(bo);
17922944501Smrg    i = csi->bo_count;
18022944501Smrg    csi->bos[i].bo = boi;
18122944501Smrg    csi->bos[i].read_domains = read_domains;
18222944501Smrg    csi->bos[i].write_domain = write_domain;
18322944501Smrg    csi->bos[i].new_accounted = 0;
18422944501Smrg    csi->bo_count++;
18522944501Smrg
18622944501Smrg    assert(csi->bo_count < MAX_SPACE_BOS);
18722944501Smrg}
18822944501Smrg
18922944501Smrgstatic int radeon_cs_check_space_internal(struct radeon_cs_int *cs,
19022944501Smrg                      struct radeon_cs_space_check *tmp_bo)
19122944501Smrg{
19222944501Smrg    int ret;
19322944501Smrg    int flushed = 0;
19422944501Smrg
19522944501Smrgagain:
19622944501Smrg    ret = radeon_cs_do_space_check(cs, tmp_bo);
19722944501Smrg    if (ret == RADEON_CS_SPACE_OP_TO_BIG)
19822944501Smrg        return -1;
19922944501Smrg    if (ret == RADEON_CS_SPACE_FLUSH) {
20022944501Smrg        (*cs->space_flush_fn)(cs->space_flush_data);
20122944501Smrg        if (flushed)
20222944501Smrg            return -1;
20322944501Smrg        flushed = 1;
20422944501Smrg        goto again;
20522944501Smrg    }
20622944501Smrg    return 0;
20722944501Smrg}
20822944501Smrg
2097cdc0497Smrgdrm_public int
210baaff307Smrgradeon_cs_space_check_with_bo(struct radeon_cs *cs, struct radeon_bo *bo,
211baaff307Smrg                              uint32_t read_domains, uint32_t write_domain)
21222944501Smrg{
21322944501Smrg    struct radeon_cs_int *csi = (struct radeon_cs_int *)cs;
21422944501Smrg    struct radeon_bo_int *boi = (struct radeon_bo_int *)bo;
21522944501Smrg    struct radeon_cs_space_check temp_bo;
21622944501Smrg
21722944501Smrg    int ret = 0;
21822944501Smrg
21922944501Smrg    if (bo) {
22022944501Smrg        temp_bo.bo = boi;
22122944501Smrg        temp_bo.read_domains = read_domains;
22222944501Smrg        temp_bo.write_domain = write_domain;
22322944501Smrg        temp_bo.new_accounted = 0;
22422944501Smrg    }
22522944501Smrg
22622944501Smrg    ret = radeon_cs_check_space_internal(csi, bo ? &temp_bo : NULL);
22722944501Smrg    return ret;
22822944501Smrg}
22922944501Smrg
2307cdc0497Smrgdrm_public int radeon_cs_space_check(struct radeon_cs *cs)
23122944501Smrg{
23222944501Smrg    struct radeon_cs_int *csi = (struct radeon_cs_int *)cs;
23322944501Smrg    return radeon_cs_check_space_internal(csi, NULL);
23422944501Smrg}
23522944501Smrg
2367cdc0497Smrgdrm_public void radeon_cs_space_reset_bos(struct radeon_cs *cs)
23722944501Smrg{
23822944501Smrg    struct radeon_cs_int *csi = (struct radeon_cs_int *)cs;
23922944501Smrg    int i;
24022944501Smrg    for (i = 0; i < csi->bo_count; i++) {
24122944501Smrg        radeon_bo_unref((struct radeon_bo *)csi->bos[i].bo);
24222944501Smrg        csi->bos[i].bo = NULL;
24322944501Smrg        csi->bos[i].read_domains = 0;
24422944501Smrg        csi->bos[i].write_domain = 0;
24522944501Smrg        csi->bos[i].new_accounted = 0;
24622944501Smrg    }
24722944501Smrg    csi->bo_count = 0;
24822944501Smrg}
249