int
nouveau_screen_init(struct nouveau_screen *screen, struct nouveau_device *dev)
{
	struct pipe_screen *pscreen = &screen->base;
	struct nv04_fifo nv04_data = { .vram = 0xbeef0201, .gart = 0xbeef0202 };
	struct nvc0_fifo nvc0_data = { };
	uint64_t time;
	int size, ret;
	void *data;
	union nouveau_bo_config mm_config;

	char *nv_dbg = getenv("NOUVEAU_MESA_DEBUG");
	if (nv_dbg)
	   nouveau_mesa_debug = atoi(nv_dbg);

	/*
	 * this is initialized to 1 in nouveau_drm_screen_create after screen
	 * is fully constructed and added to the global screen list.
	 */
	screen->refcount = -1;

	if (dev->chipset < 0xc0) {
		data = &nv04_data;
		size = sizeof(nv04_data);
	} else {
		data = &nvc0_data;
		size = sizeof(nvc0_data);
	}

	ret = nouveau_object_new(&dev->object, 0, NOUVEAU_FIFO_CHANNEL_CLASS,
				 data, size, &screen->channel);
	if (ret)
		return ret;
	screen->device = dev;

	ret = nouveau_client_new(screen->device, &screen->client);
	if (ret)
		return ret;
	ret = nouveau_pushbuf_new(screen->client, screen->channel,
				  4, 512 * 1024, 1,
				  &screen->pushbuf);
	if (ret)
		return ret;

        /* getting CPU time first appears to be more accurate */
        screen->cpu_gpu_time_delta = os_time_get();

        ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_PTIMER_TIME, &time);
        if (!ret)
           screen->cpu_gpu_time_delta = time - screen->cpu_gpu_time_delta * 1000;

	pscreen->get_name = nouveau_screen_get_name;
	pscreen->get_vendor = nouveau_screen_get_vendor;
	pscreen->get_device_vendor = nouveau_screen_get_device_vendor;

	pscreen->get_timestamp = nouveau_screen_get_timestamp;

	pscreen->fence_reference = nouveau_screen_fence_ref;
	pscreen->fence_signalled = nouveau_screen_fence_signalled;
	pscreen->fence_finish = nouveau_screen_fence_finish;

	util_format_s3tc_init();

	screen->lowmem_bindings = PIPE_BIND_GLOBAL; /* gallium limit */
	screen->vidmem_bindings =
		PIPE_BIND_RENDER_TARGET | PIPE_BIND_DEPTH_STENCIL |
		PIPE_BIND_DISPLAY_TARGET | PIPE_BIND_SCANOUT |
		PIPE_BIND_CURSOR |
		PIPE_BIND_SAMPLER_VIEW |
		PIPE_BIND_SHADER_RESOURCE | PIPE_BIND_COMPUTE_RESOURCE |
		PIPE_BIND_GLOBAL;
	screen->sysmem_bindings =
		PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_STREAM_OUTPUT |
		PIPE_BIND_COMMAND_ARGS_BUFFER;

	memset(&mm_config, 0, sizeof(mm_config));

	screen->mm_GART = nouveau_mm_create(dev,
					    NOUVEAU_BO_GART | NOUVEAU_BO_MAP,
					    &mm_config);
	screen->mm_VRAM = nouveau_mm_create(dev, NOUVEAU_BO_VRAM, &mm_config);
	return 0;
}

void
nouveau_screen_fini(struct nouveau_screen *screen)
{
	nouveau_mm_destroy(screen->mm_GART);
	nouveau_mm_destroy(screen->mm_VRAM);

	nouveau_pushbuf_del(&screen->pushbuf);

	nouveau_client_del(&screen->client);
	nouveau_object_del(&screen->channel);

	nouveau_device_del(&screen->device);
}
struct pipe_video_decoder *
nvc0_create_decoder(struct pipe_context *context,
                    enum pipe_video_profile profile,
                    enum pipe_video_entrypoint entrypoint,
                    enum pipe_video_chroma_format chroma_format,
                    unsigned width, unsigned height, unsigned max_references,
                    bool chunked_decode)
{
   struct nouveau_screen *screen = &((struct nvc0_context *)context)->screen->base;
   struct nvc0_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, profile, entrypoint,
                                chroma_format, width, height,
                                max_references, chunked_decode);

   if (entrypoint != PIPE_VIDEO_ENTRYPOINT_BITSTREAM) {
      debug_printf("%x\n", entrypoint);
      return NULL;
   }

   dec = CALLOC_STRUCT(nvc0_decoder);
   if (!dec)
      return NULL;
   dec->client = screen->client;

   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.profile = profile;
   dec->base.entrypoint = entrypoint;
   dec->base.chroma_format = chroma_format;
   dec->base.width = width;
   dec->base.height = height;
   dec->base.max_references = max_references;
   dec->base.destroy = nvc0_decoder_destroy;
   dec->base.flush = nvc0_decoder_flush;
   dec->base.decode_bitstream = nvc0_decoder_decode_bitstream;
   dec->base.begin_frame = nvc0_decoder_begin_frame;
   dec->base.end_frame = nvc0_decoder_end_frame;

   for (i = 0; i < NVC0_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(profile)) {
   case PIPE_VIDEO_CODEC_MPEG12: {
      codec = 1;
      assert(max_references <= 2);
      break;
   }
   case PIPE_VIDEO_CODEC_MPEG4: {
      codec = 4;
      tmp_size = mb(height)*16 * mb(width)*16;
      assert(max_references <= 2);
      break;
   }
   case PIPE_VIDEO_CODEC_VC1: {
      ppp_codec = codec = 2;
      tmp_size = mb(height)*16 * mb(width)*16;
      assert(max_references <= 2);
      break;
   }
   case PIPE_VIDEO_CODEC_MPEG4_AVC: {
      codec = 3;
      dec->tmp_stride = 16 * mb_half(width) * nvc0_video_align(height) * 3 / 2;
      tmp_size = dec->tmp_stride * (max_references + 1);
      assert(max_references <= 16);
      break;
   }
   default:
      fprintf(stderr, "invalid codec\n");
      goto fail;
   }

   if (screen->device->chipset < 0xd0) {
      int fd;
      char path[PATH_MAX];
      ssize_t r;
      uint32_t *end, endval;

      ret = nouveau_bo_new(screen->device, NOUVEAU_BO_VRAM, 0,
                           0x4000, &cfg, &dec->fw_bo);
      if (!ret)
         ret = nouveau_bo_map(dec->fw_bo, NOUVEAU_BO_WR, dec->client);
      if (ret)
         goto fail;

      nvc0_video_getpath(profile, path);

      fd = open(path, O_RDONLY | O_CLOEXEC);
      if (fd < 0) {
         fprintf(stderr, "opening firmware file %s failed: %m\n", path);
         goto fw_fail;
      }
      r = read(fd, dec->fw_bo->map, 0x4000);
      close(fd);

      if (r < 0) {
         fprintf(stderr, "reading firmware file %s failed: %m\n", path);
         goto fw_fail;
      }

      if (r == 0x4000) {
         fprintf(stderr, "firmware file %s too large!\n", path);
         goto fw_fail;
      }

      if (r & 0xff) {
         fprintf(stderr, "firmware file %s wrong size!\n", path);
         goto fw_fail;
      }

      end = dec->fw_bo->map + r - 4;
      endval = *end;
      while (endval == *end)
         end--;

      r = (intptr_t)end - (intptr_t)dec->fw_bo->map + 4;

      switch (u_reduce_video_profile(profile)) {
      case PIPE_VIDEO_CODEC_MPEG12: {
         assert((r & 0xff) == 0xe0);
         dec->fw_sizes = (0x2e0<<16) | (r - 0x2e0);
         break;
      }
      case PIPE_VIDEO_CODEC_MPEG4: {
         assert((r & 0xff) == 0xe0);
         dec->fw_sizes = (0x2e0<<16) | (r - 0x2e0);
         break;
      }
      case PIPE_VIDEO_CODEC_VC1: {
         assert((r & 0xff) == 0xac);
         dec->fw_sizes = (0x3ac<<16) | (r - 0x3ac);
         break;
      }
      case PIPE_VIDEO_CODEC_MPEG4_AVC: {
         assert((r & 0xff) == 0x70);
         dec->fw_sizes = (0x370<<16) | (r - 0x370);
         break;
      }
      default:
         goto fw_fail;
      }
      munmap(dec->fw_bo->map, dec->fw_bo->size);
      dec->fw_bo->map = NULL;
   }

   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(width)*16 * (mb_half(height)*32 + nvc0_video_align(height)/2);
   ret = nouveau_bo_new(screen->device, NOUVEAU_BO_VRAM, 0,
                        dec->ref_stride * (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 NVC0_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");
   nvc0_decoder_destroy(&dec->base);
   return NULL;

fail:
   debug_printf("Creation failed: %s (%i)\n", strerror(-ret), ret);
   nvc0_decoder_destroy(&dec->base);
   return NULL;
}
int
nouveau_screen_init(struct nouveau_screen *screen, struct nouveau_device *dev)
{
   struct pipe_screen *pscreen = &screen->base;
   struct nv04_fifo nv04_data = { .vram = 0xbeef0201, .gart = 0xbeef0202 };
   struct nvc0_fifo nvc0_data = { };
   uint64_t time;
   int size, ret;
   void *data;
   union nouveau_bo_config mm_config;

   char *nv_dbg = getenv("NOUVEAU_MESA_DEBUG");
   if (nv_dbg)
      nouveau_mesa_debug = atoi(nv_dbg);

   screen->prefer_nir = debug_get_bool_option("NV50_PROG_USE_NIR", false);

   /* These must be set before any failure is possible, as the cleanup
    * paths assume they're responsible for deleting them.
    */
   screen->drm = nouveau_drm(&dev->object);
   screen->device = dev;

   /*
    * this is initialized to 1 in nouveau_drm_screen_create after screen
    * is fully constructed and added to the global screen list.
    */
   screen->refcount = -1;

   if (dev->chipset < 0xc0) {
      data = &nv04_data;
      size = sizeof(nv04_data);
   } else {
      data = &nvc0_data;
      size = sizeof(nvc0_data);
   }

   /*
    * Set default VRAM domain if not overridden
    */
   if (!screen->vram_domain) {
      if (dev->vram_size > 0)
         screen->vram_domain = NOUVEAU_BO_VRAM;
      else
         screen->vram_domain = NOUVEAU_BO_GART;
   }

   ret = nouveau_object_new(&dev->object, 0, NOUVEAU_FIFO_CHANNEL_CLASS,
                            data, size, &screen->channel);
   if (ret)
      return ret;

   ret = nouveau_client_new(screen->device, &screen->client);
   if (ret)
      return ret;
   ret = nouveau_pushbuf_new(screen->client, screen->channel,
                             4, 512 * 1024, 1,
                             &screen->pushbuf);
   if (ret)
      return ret;

   /* getting CPU time first appears to be more accurate */
   screen->cpu_gpu_time_delta = os_time_get();

   ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_PTIMER_TIME, &time);
   if (!ret)
      screen->cpu_gpu_time_delta = time - screen->cpu_gpu_time_delta * 1000;

   pscreen->get_name = nouveau_screen_get_name;
   pscreen->get_vendor = nouveau_screen_get_vendor;
   pscreen->get_device_vendor = nouveau_screen_get_device_vendor;
   pscreen->get_disk_shader_cache = nouveau_screen_get_disk_shader_cache;

   pscreen->get_timestamp = nouveau_screen_get_timestamp;

   pscreen->fence_reference = nouveau_screen_fence_ref;
   pscreen->fence_finish = nouveau_screen_fence_finish;

   nouveau_disk_cache_create(screen);

   screen->transfer_pushbuf_threshold = 192;
   screen->lowmem_bindings = PIPE_BIND_GLOBAL; /* gallium limit */
   screen->vidmem_bindings =
      PIPE_BIND_RENDER_TARGET | PIPE_BIND_DEPTH_STENCIL |
      PIPE_BIND_DISPLAY_TARGET | PIPE_BIND_SCANOUT |
      PIPE_BIND_CURSOR |
      PIPE_BIND_SAMPLER_VIEW |
      PIPE_BIND_SHADER_BUFFER | PIPE_BIND_SHADER_IMAGE |
      PIPE_BIND_COMPUTE_RESOURCE |
      PIPE_BIND_GLOBAL;
   screen->sysmem_bindings =
      PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_STREAM_OUTPUT |
      PIPE_BIND_COMMAND_ARGS_BUFFER;

   memset(&mm_config, 0, sizeof(mm_config));

   screen->mm_GART = nouveau_mm_create(dev,
                                       NOUVEAU_BO_GART | NOUVEAU_BO_MAP,
                                       &mm_config);
   screen->mm_VRAM = nouveau_mm_create(dev, NOUVEAU_BO_VRAM, &mm_config);
   return 0;
}

void
nouveau_screen_fini(struct nouveau_screen *screen)
{
   int fd = screen->drm->fd;

   nouveau_mm_destroy(screen->mm_GART);
   nouveau_mm_destroy(screen->mm_VRAM);

   nouveau_pushbuf_del(&screen->pushbuf);

   nouveau_client_del(&screen->client);
   nouveau_object_del(&screen->channel);

   nouveau_device_del(&screen->device);
   nouveau_drm_del(&screen->drm);
   close(fd);

   disk_cache_destroy(screen->disk_shader_cache);
}
Exemple #4
0
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) {
      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;
}