/*
 * Copyright © 2019 Google, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef FD6_PACK_H
#define FD6_PACK_H

#include "a6xx.xml.h"

struct fd_reg_pair {
   uint32_t reg;
   uint64_t value;
   struct fd_bo *bo;
   bool is_address;
   bool bo_write;
   uint32_t bo_offset;
   uint32_t bo_shift;
};

#define __bo_type struct fd_bo *

#include "a6xx-pack.xml.h"
#include "adreno-pm4-pack.xml.h"

#define __assert_eq(a, b)                                                      \
   do {                                                                        \
      if ((a) != (b)) {                                                        \
         fprintf(stderr, "assert failed: " #a " (0x%x) != " #b " (0x%x)\n", a, \
                 b);                                                           \
         assert((a) == (b));                                                   \
      }                                                                        \
   } while (0)

#define __ONE_REG(i, ...)                                                      \
   do {                                                                        \
      const struct fd_reg_pair regs[] = {__VA_ARGS__};                         \
      /* NOTE: allow regs[0].reg==0, this happens in OUT_PKT() */              \
      if (i < ARRAY_SIZE(regs) && (i == 0 || regs[i].reg > 0)) {               \
         __assert_eq(regs[0].reg + i, regs[i].reg);                            \
         if (regs[i].bo) {                                                     \
            ring->cur = p;                                                     \
            p += 2;                                                            \
            OUT_RELOC(ring, regs[i].bo, regs[i].bo_offset, regs[i].value,      \
                      regs[i].bo_shift);                                       \
         } else {                                                              \
            *p++ = regs[i].value;                                              \
            if (regs[i].is_address)                                            \
               *p++ = regs[i].value >> 32;                                     \
         }                                                                     \
      }                                                                        \
   } while (0)

#define OUT_REG(ring, ...)                                                     \
   do {                                                                        \
      const struct fd_reg_pair regs[] = {__VA_ARGS__};                         \
      unsigned count = ARRAY_SIZE(regs);                                       \
                                                                               \
      STATIC_ASSERT(count > 0);                                                \
      STATIC_ASSERT(count <= 16);                                              \
                                                                               \
      BEGIN_RING(ring, count + 1);                                             \
      uint32_t *p = ring->cur;                                                 \
      *p++ = pm4_pkt4_hdr(regs[0].reg, count);                                 \
                                                                               \
      __ONE_REG(0, __VA_ARGS__);                                               \
      __ONE_REG(1, __VA_ARGS__);                                               \
      __ONE_REG(2, __VA_ARGS__);                                               \
      __ONE_REG(3, __VA_ARGS__);                                               \
      __ONE_REG(4, __VA_ARGS__);                                               \
      __ONE_REG(5, __VA_ARGS__);                                               \
      __ONE_REG(6, __VA_ARGS__);                                               \
      __ONE_REG(7, __VA_ARGS__);                                               \
      __ONE_REG(8, __VA_ARGS__);                                               \
      __ONE_REG(9, __VA_ARGS__);                                               \
      __ONE_REG(10, __VA_ARGS__);                                              \
      __ONE_REG(11, __VA_ARGS__);                                              \
      __ONE_REG(12, __VA_ARGS__);                                              \
      __ONE_REG(13, __VA_ARGS__);                                              \
      __ONE_REG(14, __VA_ARGS__);                                              \
      __ONE_REG(15, __VA_ARGS__);                                              \
      ring->cur = p;                                                           \
   } while (0)

#define OUT_PKT(ring, opcode, ...)                                             \
   do {                                                                        \
      const struct fd_reg_pair regs[] = {__VA_ARGS__};                         \
      unsigned count = ARRAY_SIZE(regs);                                       \
                                                                               \
      STATIC_ASSERT(count <= 16);                                              \
                                                                               \
      BEGIN_RING(ring, count + 1);                                             \
      uint32_t *p = ring->cur;                                                 \
      *p++ = pm4_pkt7_hdr(opcode, count);                                      \
                                                                               \
      __ONE_REG(0, __VA_ARGS__);                                               \
      __ONE_REG(1, __VA_ARGS__);                                               \
      __ONE_REG(2, __VA_ARGS__);                                               \
      __ONE_REG(3, __VA_ARGS__);                                               \
      __ONE_REG(4, __VA_ARGS__);                                               \
      __ONE_REG(5, __VA_ARGS__);                                               \
      __ONE_REG(6, __VA_ARGS__);                                               \
      __ONE_REG(7, __VA_ARGS__);                                               \
      __ONE_REG(8, __VA_ARGS__);                                               \
      __ONE_REG(9, __VA_ARGS__);                                               \
      __ONE_REG(10, __VA_ARGS__);                                              \
      __ONE_REG(11, __VA_ARGS__);                                              \
      __ONE_REG(12, __VA_ARGS__);                                              \
      __ONE_REG(13, __VA_ARGS__);                                              \
      __ONE_REG(14, __VA_ARGS__);                                              \
      __ONE_REG(15, __VA_ARGS__);                                              \
      ring->cur = p;                                                           \
   } while (0)

/* similar to OUT_PKT() but appends specified # of dwords
 * copied for buf to the end of the packet (ie. for use-
 * cases like CP_LOAD_STATE)
 */
#define OUT_PKTBUF(ring, opcode, dwords, sizedwords, ...)                      \
   do {                                                                        \
      const struct fd_reg_pair regs[] = {__VA_ARGS__};                         \
      unsigned count = ARRAY_SIZE(regs);                                       \
                                                                               \
      STATIC_ASSERT(count <= 16);                                              \
      count += sizedwords;                                                     \
                                                                               \
      BEGIN_RING(ring, count + 1);                                             \
      uint32_t *p = ring->cur;                                                 \
      *p++ = pm4_pkt7_hdr(opcode, count);                                      \
                                                                               \
      __ONE_REG(0, __VA_ARGS__);                                               \
      __ONE_REG(1, __VA_ARGS__);                                               \
      __ONE_REG(2, __VA_ARGS__);                                               \
      __ONE_REG(3, __VA_ARGS__);                                               \
      __ONE_REG(4, __VA_ARGS__);                                               \
      __ONE_REG(5, __VA_ARGS__);                                               \
      __ONE_REG(6, __VA_ARGS__);                                               \
      __ONE_REG(7, __VA_ARGS__);                                               \
      __ONE_REG(8, __VA_ARGS__);                                               \
      __ONE_REG(9, __VA_ARGS__);                                               \
      __ONE_REG(10, __VA_ARGS__);                                              \
      __ONE_REG(11, __VA_ARGS__);                                              \
      __ONE_REG(12, __VA_ARGS__);                                              \
      __ONE_REG(13, __VA_ARGS__);                                              \
      __ONE_REG(14, __VA_ARGS__);                                              \
      __ONE_REG(15, __VA_ARGS__);                                              \
      memcpy(p, dwords, 4 * sizedwords);                                       \
      p += sizedwords;                                                         \
      ring->cur = p;                                                           \
   } while (0)

#endif /* FD6_PACK_H */