Move nv30, nv50 and nvc0 to nouveau.
[mesa.git] / src / gallium / drivers / nouveau / nvc0 / nvc0_video.c
diff --git a/src/gallium/drivers/nouveau/nvc0/nvc0_video.c b/src/gallium/drivers/nouveau/nvc0/nvc0_video.c
new file mode 100644 (file)
index 0000000..5871f59
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2011-2013 Maarten Lankhorst
+ *
+ * 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 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.
+ */
+
+#include "nvc0/nvc0_video.h"
+
+#include "util/u_sampler.h"
+#include "util/u_format.h"
+
+static void
+nvc0_decoder_decode_bitstream(struct pipe_video_codec *decoder,
+                              struct pipe_video_buffer *video_target,
+                              struct pipe_picture_desc *picture,
+                              unsigned num_buffers,
+                              const void *const *data,
+                              const unsigned *num_bytes)
+{
+   struct nouveau_vp3_decoder *dec = (struct nouveau_vp3_decoder *)decoder;
+   struct nouveau_vp3_video_buffer *target = (struct nouveau_vp3_video_buffer *)video_target;
+   uint32_t comm_seq = ++dec->fence_seq;
+   union pipe_desc desc;
+
+   unsigned vp_caps, is_ref, ret;
+   struct nouveau_vp3_video_buffer *refs[16] = {};
+
+   desc.base = picture;
+
+   assert(target->base.buffer_format == PIPE_FORMAT_NV12);
+
+   ret = nvc0_decoder_bsp(dec, desc, target, comm_seq,
+                          num_buffers, data, num_bytes,
+                          &vp_caps, &is_ref, refs);
+
+   /* did we decode bitstream correctly? */
+   assert(ret == 2);
+
+   nvc0_decoder_vp(dec, desc, target, comm_seq, vp_caps, is_ref, refs);
+   nvc0_decoder_ppp(dec, desc, target, comm_seq);
+}
+
+struct pipe_video_codec *
+nvc0_create_decoder(struct pipe_context *context,
+                    const struct pipe_video_codec *templ)
+{
+   struct nouveau_screen *screen = &((struct nvc0_context *)context)->screen->base;
+   struct nouveau_vp3_decoder *dec;
+   struct nouveau_pushbuf **push;
+   union nouveau_bo_config cfg;
+   bool kepler = screen->device->chipset >= 0xe0;
+
+   cfg.nvc0.tile_mode = 0x10;
+   cfg.nvc0.memtype = 0xfe;
+
+   int ret, i;
+   uint32_t codec = 1, ppp_codec = 3;
+   uint32_t timeout;
+   u32 tmp_size = 0;
+
+   if (getenv("XVMC_VL"))
+       return vl_create_decoder(context, templ);
+
+   if (templ->entrypoint != PIPE_VIDEO_ENTRYPOINT_BITSTREAM) {
+      debug_printf("%x\n", templ->entrypoint);
+      return NULL;
+   }
+
+   dec = CALLOC_STRUCT(nouveau_vp3_decoder);
+   if (!dec)
+      return NULL;
+   dec->client = screen->client;
+   dec->base = *templ;
+   nouveau_vp3_decoder_init_common(&dec->base);
+
+   if (!kepler) {
+      dec->bsp_idx = 5;
+      dec->vp_idx = 6;
+      dec->ppp_idx = 7;
+   } else {
+      dec->bsp_idx = 2;
+      dec->vp_idx = 2;
+      dec->ppp_idx = 2;
+   }
+
+   for (i = 0; i < 3; ++i)
+      if (i && !kepler) {
+         dec->channel[i] = dec->channel[0];
+         dec->pushbuf[i] = dec->pushbuf[0];
+      } else {
+         void *data;
+         u32 size;
+         struct nvc0_fifo nvc0_args = {};
+         struct nve0_fifo nve0_args = {};
+
+         if (!kepler) {
+            size = sizeof(nvc0_args);
+            data = &nvc0_args;
+         } else {
+            unsigned engine[] = {
+               NVE0_FIFO_ENGINE_BSP,
+               NVE0_FIFO_ENGINE_VP,
+               NVE0_FIFO_ENGINE_PPP
+            };
+
+            nve0_args.engine = engine[i];
+            size = sizeof(nve0_args);
+            data = &nve0_args;
+         }
+
+         ret = nouveau_object_new(&screen->device->object, 0,
+                                  NOUVEAU_FIFO_CHANNEL_CLASS,
+                                  data, size, &dec->channel[i]);
+
+         if (!ret)
+            ret = nouveau_pushbuf_new(screen->client, dec->channel[i], 4,
+                                   32 * 1024, true, &dec->pushbuf[i]);
+         if (ret)
+            break;
+      }
+   push = dec->pushbuf;
+
+   if (!kepler) {
+      if (!ret)
+         ret = nouveau_object_new(dec->channel[0], 0x390b1, 0x90b1, NULL, 0, &dec->bsp);
+      if (!ret)
+         ret = nouveau_object_new(dec->channel[1], 0x190b2, 0x90b2, NULL, 0, &dec->vp);
+      if (!ret)
+         ret = nouveau_object_new(dec->channel[2], 0x290b3, 0x90b3, NULL, 0, &dec->ppp);
+   } else {
+      if (!ret)
+         ret = nouveau_object_new(dec->channel[0], 0x95b1, 0x95b1, NULL, 0, &dec->bsp);
+      if (!ret)
+         ret = nouveau_object_new(dec->channel[1], 0x95b2, 0x95b2, NULL, 0, &dec->vp);
+      if (!ret)
+         ret = nouveau_object_new(dec->channel[2], 0x90b3, 0x90b3, NULL, 0, &dec->ppp);
+   }
+   if (ret)
+      goto fail;
+
+   BEGIN_NVC0(push[0], SUBC_BSP(NV01_SUBCHAN_OBJECT), 1);
+   PUSH_DATA (push[0], dec->bsp->handle);
+
+   BEGIN_NVC0(push[1], SUBC_VP(NV01_SUBCHAN_OBJECT), 1);
+   PUSH_DATA (push[1], dec->vp->handle);
+
+   BEGIN_NVC0(push[2], SUBC_PPP(NV01_SUBCHAN_OBJECT), 1);
+   PUSH_DATA (push[2], dec->ppp->handle);
+
+   dec->base.context = context;
+   dec->base.decode_bitstream = nvc0_decoder_decode_bitstream;
+
+   for (i = 0; i < NOUVEAU_VP3_VIDEO_QDEPTH && !ret; ++i)
+      ret = nouveau_bo_new(screen->device, NOUVEAU_BO_VRAM,
+                           0, 1 << 20, &cfg, &dec->bsp_bo[i]);
+   if (!ret)
+      ret = nouveau_bo_new(screen->device, NOUVEAU_BO_VRAM,
+                           0x100, 4 << 20, &cfg, &dec->inter_bo[0]);
+   if (!ret) {
+      if (!kepler)
+         nouveau_bo_ref(dec->inter_bo[0], &dec->inter_bo[1]);
+      else
+         ret = nouveau_bo_new(screen->device, NOUVEAU_BO_VRAM,
+                              0x100, dec->inter_bo[0]->size, &cfg,
+                              &dec->inter_bo[1]);
+   }
+   if (ret)
+      goto fail;
+
+   switch (u_reduce_video_profile(templ->profile)) {
+   case PIPE_VIDEO_FORMAT_MPEG12: {
+      codec = 1;
+      assert(templ->max_references <= 2);
+      break;
+   }
+   case PIPE_VIDEO_FORMAT_MPEG4: {
+      codec = 4;
+      tmp_size = mb(templ->height)*16 * mb(templ->width)*16;
+      assert(templ->max_references <= 2);
+      break;
+   }
+   case PIPE_VIDEO_FORMAT_VC1: {
+      ppp_codec = codec = 2;
+      tmp_size = mb(templ->height)*16 * mb(templ->width)*16;
+      assert(templ->max_references <= 2);
+      break;
+   }
+   case PIPE_VIDEO_FORMAT_MPEG4_AVC: {
+      codec = 3;
+      dec->tmp_stride = 16 * mb_half(templ->width) * nouveau_vp3_video_align(templ->height) * 3 / 2;
+      tmp_size = dec->tmp_stride * (templ->max_references + 1);
+      assert(templ->max_references <= 16);
+      break;
+   }
+   default:
+      fprintf(stderr, "invalid codec\n");
+      goto fail;
+   }
+
+   if (screen->device->chipset < 0xd0) {
+      ret = nouveau_bo_new(screen->device, NOUVEAU_BO_VRAM, 0,
+                           0x4000, &cfg, &dec->fw_bo);
+      if (ret)
+         goto fail;
+
+      ret = nouveau_vp3_load_firmware(dec, templ->profile, screen->device->chipset);
+      if (ret)
+         goto fw_fail;
+   }
+
+   if (codec != 3) {
+      ret = nouveau_bo_new(screen->device, NOUVEAU_BO_VRAM, 0,
+                           0x400, &cfg, &dec->bitplane_bo);
+      if (ret)
+         goto fail;
+   }
+
+   dec->ref_stride = mb(templ->width)*16 * (mb_half(templ->height)*32 + nouveau_vp3_video_align(templ->height)/2);
+   ret = nouveau_bo_new(screen->device, NOUVEAU_BO_VRAM, 0,
+                        dec->ref_stride * (templ->max_references+2) + tmp_size,
+                        &cfg, &dec->ref_bo);
+   if (ret)
+      goto fail;
+
+   timeout = 0;
+
+   BEGIN_NVC0(push[0], SUBC_BSP(0x200), 2);
+   PUSH_DATA (push[0], codec);
+   PUSH_DATA (push[0], timeout);
+
+   BEGIN_NVC0(push[1], SUBC_VP(0x200), 2);
+   PUSH_DATA (push[1], codec);
+   PUSH_DATA (push[1], timeout);
+
+   BEGIN_NVC0(push[2], SUBC_PPP(0x200), 2);
+   PUSH_DATA (push[2], ppp_codec);
+   PUSH_DATA (push[2], timeout);
+
+   ++dec->fence_seq;
+
+#if NOUVEAU_VP3_DEBUG_FENCE
+   ret = nouveau_bo_new(screen->device, NOUVEAU_BO_GART|NOUVEAU_BO_MAP,
+                        0, 0x1000, NULL, &dec->fence_bo);
+   if (ret)
+      goto fail;
+
+   nouveau_bo_map(dec->fence_bo, NOUVEAU_BO_RDWR, screen->client);
+   dec->fence_map = dec->fence_bo->map;
+   dec->fence_map[0] = dec->fence_map[4] = dec->fence_map[8] = 0;
+   dec->comm = (struct comm *)(dec->fence_map + (COMM_OFFSET/sizeof(*dec->fence_map)));
+
+   /* So lets test if the fence is working? */
+   nouveau_pushbuf_space(push[0], 6, 1, 0);
+   PUSH_REFN (push[0], dec->fence_bo, NOUVEAU_BO_GART|NOUVEAU_BO_RDWR);
+   BEGIN_NVC0(push[0], SUBC_BSP(0x240), 3);
+   PUSH_DATAh(push[0], dec->fence_bo->offset);
+   PUSH_DATA (push[0], dec->fence_bo->offset);
+   PUSH_DATA (push[0], dec->fence_seq);
+
+   BEGIN_NVC0(push[0], SUBC_BSP(0x304), 1);
+   PUSH_DATA (push[0], 0);
+   PUSH_KICK (push[0]);
+
+   nouveau_pushbuf_space(push[1], 6, 1, 0);
+   PUSH_REFN (push[1], dec->fence_bo, NOUVEAU_BO_GART|NOUVEAU_BO_RDWR);
+   BEGIN_NVC0(push[1], SUBC_VP(0x240), 3);
+   PUSH_DATAh(push[1], (dec->fence_bo->offset + 0x10));
+   PUSH_DATA (push[1], (dec->fence_bo->offset + 0x10));
+   PUSH_DATA (push[1], dec->fence_seq);
+
+   BEGIN_NVC0(push[1], SUBC_VP(0x304), 1);
+   PUSH_DATA (push[1], 0);
+   PUSH_KICK (push[1]);
+
+   nouveau_pushbuf_space(push[2], 6, 1, 0);
+   PUSH_REFN (push[2], dec->fence_bo, NOUVEAU_BO_GART|NOUVEAU_BO_RDWR);
+   BEGIN_NVC0(push[2], SUBC_PPP(0x240), 3);
+   PUSH_DATAh(push[2], (dec->fence_bo->offset + 0x20));
+   PUSH_DATA (push[2], (dec->fence_bo->offset + 0x20));
+   PUSH_DATA (push[2], dec->fence_seq);
+
+   BEGIN_NVC0(push[2], SUBC_PPP(0x304), 1);
+   PUSH_DATA (push[2], 0);
+   PUSH_KICK (push[2]);
+
+   usleep(100);
+   while (dec->fence_seq > dec->fence_map[0] ||
+          dec->fence_seq > dec->fence_map[4] ||
+          dec->fence_seq > dec->fence_map[8]) {
+      debug_printf("%u: %u %u %u\n", dec->fence_seq, dec->fence_map[0], dec->fence_map[4], dec->fence_map[8]);
+      usleep(100);
+   }
+   debug_printf("%u: %u %u %u\n", dec->fence_seq, dec->fence_map[0], dec->fence_map[4], dec->fence_map[8]);
+#endif
+
+   return &dec->base;
+
+fw_fail:
+   debug_printf("Cannot create decoder without firmware..\n");
+   dec->base.destroy(&dec->base);
+   return NULL;
+
+fail:
+   debug_printf("Creation failed: %s (%i)\n", strerror(-ret), ret);
+   dec->base.destroy(&dec->base);
+   return NULL;
+}
+
+struct pipe_video_buffer *
+nvc0_video_buffer_create(struct pipe_context *pipe,
+                         const struct pipe_video_buffer *templat)
+{
+   return nouveau_vp3_video_buffer_create(
+         pipe, templat, NVC0_RESOURCE_FLAG_VIDEO);
+}