Merge tag 'upstream-3.9-rc1' of git://git.infradead.org/linux-ubifs
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / radeon / radeon_cs.c
index 5407459e56d2b5dd9619e698c2ec306c56104f4a..70d38241b083afeb7e8a2d32053b2cc0f33086a7 100644 (file)
@@ -29,9 +29,6 @@
 #include "radeon_reg.h"
 #include "radeon.h"
 
-void r100_cs_dump_packet(struct radeon_cs_parser *p,
-                        struct radeon_cs_packet *pkt);
-
 static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
 {
        struct drm_device *ddev = p->rdev->ddev;
@@ -128,18 +125,6 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority
        return 0;
 }
 
-static void radeon_cs_sync_to(struct radeon_cs_parser *p,
-                             struct radeon_fence *fence)
-{
-       struct radeon_fence *other;
-
-       if (!fence)
-               return;
-
-       other = p->ib.sync_to[fence->ring];
-       p->ib.sync_to[fence->ring] = radeon_fence_later(fence, other);
-}
-
 static void radeon_cs_sync_rings(struct radeon_cs_parser *p)
 {
        int i;
@@ -148,7 +133,7 @@ static void radeon_cs_sync_rings(struct radeon_cs_parser *p)
                if (!p->relocs[i].robj)
                        continue;
 
-               radeon_cs_sync_to(p, p->relocs[i].robj->tbo.sync_obj);
+               radeon_ib_sync_to(&p->ib, p->relocs[i].robj->tbo.sync_obj);
        }
 }
 
@@ -203,7 +188,7 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
                p->chunks[i].length_dw = user_chunk.length_dw;
                p->chunks[i].kdata = NULL;
                p->chunks[i].chunk_id = user_chunk.chunk_id;
-
+               p->chunks[i].user_ptr = (void __user *)(unsigned long)user_chunk.chunk_data;
                if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS) {
                        p->chunk_relocs_idx = i;
                }
@@ -226,9 +211,6 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
                                return -EINVAL;
                }
 
-               p->chunks[i].length_dw = user_chunk.length_dw;
-               p->chunks[i].user_ptr = (void __user *)(unsigned long)user_chunk.chunk_data;
-
                cdata = (uint32_t *)(unsigned long)user_chunk.chunk_data;
                if ((p->chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS) ||
                    (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS)) {
@@ -478,8 +460,9 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev,
                goto out;
        }
        radeon_cs_sync_rings(parser);
-       radeon_cs_sync_to(parser, vm->fence);
-       radeon_cs_sync_to(parser, radeon_vm_grab_id(rdev, vm, parser->ring));
+       radeon_ib_sync_to(&parser->ib, vm->fence);
+       radeon_ib_sync_to(&parser->ib, radeon_vm_grab_id(
+               rdev, vm, parser->ring));
 
        if ((rdev->family >= CHIP_TAHITI) &&
            (parser->chunk_const_ib_idx != -1)) {
@@ -648,3 +631,152 @@ u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx)
        idx_value = ibc->kpage[new_page][pg_offset/4];
        return idx_value;
 }
+
+/**
+ * radeon_cs_packet_parse() - parse cp packet and point ib index to next packet
+ * @parser:    parser structure holding parsing context.
+ * @pkt:       where to store packet information
+ *
+ * Assume that chunk_ib_index is properly set. Will return -EINVAL
+ * if packet is bigger than remaining ib size. or if packets is unknown.
+ **/
+int radeon_cs_packet_parse(struct radeon_cs_parser *p,
+                          struct radeon_cs_packet *pkt,
+                          unsigned idx)
+{
+       struct radeon_cs_chunk *ib_chunk = &p->chunks[p->chunk_ib_idx];
+       struct radeon_device *rdev = p->rdev;
+       uint32_t header;
+
+       if (idx >= ib_chunk->length_dw) {
+               DRM_ERROR("Can not parse packet at %d after CS end %d !\n",
+                         idx, ib_chunk->length_dw);
+               return -EINVAL;
+       }
+       header = radeon_get_ib_value(p, idx);
+       pkt->idx = idx;
+       pkt->type = RADEON_CP_PACKET_GET_TYPE(header);
+       pkt->count = RADEON_CP_PACKET_GET_COUNT(header);
+       pkt->one_reg_wr = 0;
+       switch (pkt->type) {
+       case RADEON_PACKET_TYPE0:
+               if (rdev->family < CHIP_R600) {
+                       pkt->reg = R100_CP_PACKET0_GET_REG(header);
+                       pkt->one_reg_wr =
+                               RADEON_CP_PACKET0_GET_ONE_REG_WR(header);
+               } else
+                       pkt->reg = R600_CP_PACKET0_GET_REG(header);
+               break;
+       case RADEON_PACKET_TYPE3:
+               pkt->opcode = RADEON_CP_PACKET3_GET_OPCODE(header);
+               break;
+       case RADEON_PACKET_TYPE2:
+               pkt->count = -1;
+               break;
+       default:
+               DRM_ERROR("Unknown packet type %d at %d !\n", pkt->type, idx);
+               return -EINVAL;
+       }
+       if ((pkt->count + 1 + pkt->idx) >= ib_chunk->length_dw) {
+               DRM_ERROR("Packet (%d:%d:%d) end after CS buffer (%d) !\n",
+                         pkt->idx, pkt->type, pkt->count, ib_chunk->length_dw);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/**
+ * radeon_cs_packet_next_is_pkt3_nop() - test if the next packet is P3 NOP
+ * @p:         structure holding the parser context.
+ *
+ * Check if the next packet is NOP relocation packet3.
+ **/
+bool radeon_cs_packet_next_is_pkt3_nop(struct radeon_cs_parser *p)
+{
+       struct radeon_cs_packet p3reloc;
+       int r;
+
+       r = radeon_cs_packet_parse(p, &p3reloc, p->idx);
+       if (r)
+               return false;
+       if (p3reloc.type != RADEON_PACKET_TYPE3)
+               return false;
+       if (p3reloc.opcode != RADEON_PACKET3_NOP)
+               return false;
+       return true;
+}
+
+/**
+ * radeon_cs_dump_packet() - dump raw packet context
+ * @p:         structure holding the parser context.
+ * @pkt:       structure holding the packet.
+ *
+ * Used mostly for debugging and error reporting.
+ **/
+void radeon_cs_dump_packet(struct radeon_cs_parser *p,
+                          struct radeon_cs_packet *pkt)
+{
+       volatile uint32_t *ib;
+       unsigned i;
+       unsigned idx;
+
+       ib = p->ib.ptr;
+       idx = pkt->idx;
+       for (i = 0; i <= (pkt->count + 1); i++, idx++)
+               DRM_INFO("ib[%d]=0x%08X\n", idx, ib[idx]);
+}
+
+/**
+ * radeon_cs_packet_next_reloc() - parse next (should be reloc) packet
+ * @parser:            parser structure holding parsing context.
+ * @data:              pointer to relocation data
+ * @offset_start:      starting offset
+ * @offset_mask:       offset mask (to align start offset on)
+ * @reloc:             reloc informations
+ *
+ * Check if next packet is relocation packet3, do bo validation and compute
+ * GPU offset using the provided start.
+ **/
+int radeon_cs_packet_next_reloc(struct radeon_cs_parser *p,
+                               struct radeon_cs_reloc **cs_reloc,
+                               int nomm)
+{
+       struct radeon_cs_chunk *relocs_chunk;
+       struct radeon_cs_packet p3reloc;
+       unsigned idx;
+       int r;
+
+       if (p->chunk_relocs_idx == -1) {
+               DRM_ERROR("No relocation chunk !\n");
+               return -EINVAL;
+       }
+       *cs_reloc = NULL;
+       relocs_chunk = &p->chunks[p->chunk_relocs_idx];
+       r = radeon_cs_packet_parse(p, &p3reloc, p->idx);
+       if (r)
+               return r;
+       p->idx += p3reloc.count + 2;
+       if (p3reloc.type != RADEON_PACKET_TYPE3 ||
+           p3reloc.opcode != RADEON_PACKET3_NOP) {
+               DRM_ERROR("No packet3 for relocation for packet at %d.\n",
+                         p3reloc.idx);
+               radeon_cs_dump_packet(p, &p3reloc);
+               return -EINVAL;
+       }
+       idx = radeon_get_ib_value(p, p3reloc.idx + 1);
+       if (idx >= relocs_chunk->length_dw) {
+               DRM_ERROR("Relocs at %d after relocations chunk end %d !\n",
+                         idx, relocs_chunk->length_dw);
+               radeon_cs_dump_packet(p, &p3reloc);
+               return -EINVAL;
+       }
+       /* FIXME: we assume reloc size is 4 dwords */
+       if (nomm) {
+               *cs_reloc = p->relocs;
+               (*cs_reloc)->lobj.gpu_offset =
+                       (u64)relocs_chunk->kdata[idx + 3] << 32;
+               (*cs_reloc)->lobj.gpu_offset |= relocs_chunk->kdata[idx + 0];
+       } else
+               *cs_reloc = p->relocs_ptr[(idx / 4)];
+       return 0;
+}