int
nouveau_fifo_channel_create_(struct nouveau_object *parent,
			     struct nouveau_object *engine,
			     struct nouveau_oclass *oclass,
			     int bar, u32 addr, u32 size, u32 pushbuf,
			     u64 engmask, int len, void **ptr)
{
	struct nouveau_device *device = nv_device(engine);
	struct nouveau_fifo *priv = (void *)engine;
	struct nouveau_fifo_chan *chan;
	struct nouveau_dmaeng *dmaeng;
	unsigned long flags;
	int ret;

	/* create base object class */
	ret = nouveau_namedb_create_(parent, engine, oclass, 0, NULL,
				     engmask, len, ptr);
	chan = *ptr;
	if (ret)
		return ret;

	/* validate dma object representing push buffer */
	chan->pushdma = (void *)nouveau_handle_ref(parent, pushbuf);
	if (!chan->pushdma)
		return -ENOENT;

	dmaeng = (void *)chan->pushdma->base.engine;
	switch (chan->pushdma->base.oclass->handle) {
	case NV_DMA_FROM_MEMORY_CLASS:
	case NV_DMA_IN_MEMORY_CLASS:
		break;
	default:
		return -EINVAL;
	}

	ret = dmaeng->bind(dmaeng, parent, chan->pushdma, &chan->pushgpu);
	if (ret)
		return ret;

	/* find a free fifo channel */
	spin_lock_irqsave(&priv->lock, flags);
	for (chan->chid = priv->min; chan->chid < priv->max; chan->chid++) {
		if (!priv->channel[chan->chid]) {
			priv->channel[chan->chid] = nv_object(chan);
			break;
		}
	}
	spin_unlock_irqrestore(&priv->lock, flags);

	if (chan->chid == priv->max) {
		nv_error(priv, "no free channels\n");
		return -ENOSPC;
	}

	/* map fifo control registers */
#ifdef __NetBSD__
	if (bar == 0) {
		/*
		 * We already map BAR 0 in the engine device base, so
		 * grab a subregion of that.
		 */
		bus_space_tag_t mmiot = nv_subdev(device)->mmiot;
		bus_space_handle_t mmioh = nv_subdev(device)->mmioh;
		bus_size_t mmiosz = nv_subdev(device)->mmiosz;

		/* Check whether it lies inside the region.  */
		if (mmiosz < addr ||
		    mmiosz - addr < chan->chid*size ||
		    mmiosz - addr - chan->chid*size < size) {
			ret = EIO;
			nv_error(priv, "fifo channel out of range:"
			    " addr 0x%"PRIxMAX
			    " chid 0x%"PRIxMAX" size 0x%"PRIxMAX
			    " mmiosz 0x%"PRIxMAX"\n",
			    (uintmax_t)addr,
			    (uintmax_t)chan->chid, (uintmax_t)size,
			    (uintmax_t)mmiosz);
			return ret;
		}

		/* Grab a subregion.  */
		/* XXX errno NetBSD->Linux */
		ret = -bus_space_subregion(mmiot, mmioh,
		    (addr + chan->chid*size), size, &chan->bsh);
		if (ret) {
			nv_error(priv, "bus_space_subregion failed: %d\n",
			    ret);
			return ret;
		}

		/* Success!  No need to unmap a subregion.  */
		chan->mapped = false;
		chan->bst = mmiot;
	} else {
		chan->bst = nv_device_resource_tag(device, bar);
		/* XXX errno NetBSD->Linux */
		ret = -bus_space_map(chan->bst,
		    (nv_device_resource_start(device, bar) +
			addr + (chan->chid * size)),
		    size, 0, &chan->bsh);
		if (ret) {
			nv_error(priv, "failed to map fifo channel:"
			    " bar %d addr %"PRIxMAX" + %"PRIxMAX
			    " + (%"PRIxMAX" * %"PRIxMAX") = %"PRIxMAX
			    " size %"PRIxMAX": %d\n",
			    bar,
			    (uintmax_t)nv_device_resource_start(device, bar),
			    (uintmax_t)addr,
			    (uintmax_t)chan->chid,
			    (uintmax_t)size,
			    (uintmax_t)(nv_device_resource_start(device, bar) +
				addr + (chan->chid * size)),
			    (uintmax_t)size,
			    ret);
			return ret;
		}
		chan->mapped = true;
	}
#else
	chan->user = ioremap(nv_device_resource_start(device, bar) + addr +
			     (chan->chid * size), size);
	if (!chan->user)
		return -EFAULT;
#endif

	nouveau_event_trigger(priv->cevent, 0);

	chan->size = size;
	return 0;
}
int
nouveau_fifo_channel_create_(struct nouveau_object *parent,
			     struct nouveau_object *engine,
			     struct nouveau_oclass *oclass,
			     int bar, u32 addr, u32 size, u32 pushbuf,
			     u64 engmask, int len, void **ptr)
{
	struct nouveau_device *device = nv_device(engine);
	struct nouveau_fifo *priv = (void *)engine;
	struct nouveau_fifo_chan *chan;
	struct nouveau_dmaeng *dmaeng;
	unsigned long flags;
	int ret;

	/* create base object class */
	ret = nouveau_namedb_create_(parent, engine, oclass, 0, NULL,
				     engmask, len, ptr);
	chan = *ptr;
	if (ret)
		return ret;

	/* validate dma object representing push buffer */
	chan->pushdma = (void *)nouveau_handle_ref(parent, pushbuf);
	if (!chan->pushdma)
		return -ENOENT;

	dmaeng = (void *)chan->pushdma->base.engine;
	switch (chan->pushdma->base.oclass->handle) {
	case NV_DMA_FROM_MEMORY_CLASS:
	case NV_DMA_IN_MEMORY_CLASS:
		break;
	default:
		return -EINVAL;
	}

	ret = dmaeng->bind(dmaeng, parent, chan->pushdma, &chan->pushgpu);
	if (ret)
		return ret;

	/* find a free fifo channel */
	spin_lock_irqsave(&priv->lock, flags);
	for (chan->chid = priv->min; chan->chid < priv->max; chan->chid++) {
		if (!priv->channel[chan->chid]) {
			priv->channel[chan->chid] = nv_object(chan);
			break;
		}
	}
	spin_unlock_irqrestore(&priv->lock, flags);

	if (chan->chid == priv->max) {
		nv_error(priv, "no free channels\n");
		return -ENOSPC;
	}

	/* map fifo control registers */
#ifdef __NetBSD__
	chan->bst = nv_device_resource_tag(device, bar);
	/* XXX errno NetBSD->Linux */
	ret = -bus_space_map(chan->bst, nv_device_resource_start(device, bar) +
	    addr + (chan->chid * size), size, 0, &chan->bsh);
	if (ret)
		return ret;
	chan->mapped = true;
#else
	chan->user = ioremap(nv_device_resource_start(device, bar) + addr +
			     (chan->chid * size), size);
	if (!chan->user)
		return -EFAULT;
#endif

	nouveau_event_trigger(priv->cevent, 0);

	chan->size = size;
	return 0;
}