+#include "nv30_transfer.h"
+
+/* Various helper functions to transfer different types of data in a number
+ * of different ways.
+ */
+
+static INLINE boolean
+nv30_transfer_scaled(struct nv30_rect *src, struct nv30_rect *dst)
+{
+ if (src->x1 - src->x0 != dst->x1 - dst->x0)
+ return TRUE;
+ if (src->y1 - src->y0 != dst->y1 - dst->y0)
+ return TRUE;
+ return FALSE;
+}
+
+static INLINE boolean
+nv30_transfer_blit(XFER_ARGS)
+{
+ if (nv30->screen->eng3d->oclass < NV40_3D_CLASS)
+ return FALSE;
+ if (dst->offset & 63 || dst->pitch & 63 || dst->d > 1)
+ return FALSE;
+ if (dst->w < 2 || dst->h < 2)
+ return FALSE;
+ if (dst->cpp > 4 || (dst->cpp == 1 && !dst->pitch))
+ return FALSE;
+ if (src->cpp > 4)
+ return FALSE;
+ return TRUE;
+}
+
+static INLINE struct nouveau_heap *
+nv30_transfer_rect_vertprog(struct nv30_context *nv30)
+{
+ struct nouveau_heap *heap = nv30->screen->vp_exec_heap;
+ struct nouveau_heap *vp;
+
+ vp = nv30->blit_vp;
+ if (!vp) {
+ if (nouveau_heap_alloc(heap, 2, &nv30->blit_vp, &nv30->blit_vp)) {
+ while (heap->next && heap->size < 2) {
+ struct nouveau_heap **evict = heap->next->priv;
+ nouveau_heap_free(evict);
+ }
+
+ if (nouveau_heap_alloc(heap, 2, &nv30->blit_vp, &nv30->blit_vp))
+ return NULL;
+ }
+
+ vp = nv30->blit_vp;
+ if (vp) {
+ struct nouveau_pushbuf *push = nv30->base.pushbuf;
+
+ BEGIN_NV04(push, NV30_3D(VP_UPLOAD_FROM_ID), 1);
+ PUSH_DATA (push, vp->start);
+ BEGIN_NV04(push, NV30_3D(VP_UPLOAD_INST(0)), 4);
+ PUSH_DATA (push, 0x401f9c6c); /* mov o[hpos], a[0]; */
+ PUSH_DATA (push, 0x0040000d);
+ PUSH_DATA (push, 0x8106c083);
+ PUSH_DATA (push, 0x6041ff80);
+ BEGIN_NV04(push, NV30_3D(VP_UPLOAD_INST(0)), 4);
+ PUSH_DATA (push, 0x401f9c6c); /* mov o[tex0], a[8]; end; */
+ PUSH_DATA (push, 0x0040080d);
+ PUSH_DATA (push, 0x8106c083);
+ PUSH_DATA (push, 0x6041ff9d);
+ }
+ }
+
+ return vp;
+}
+
+
+static INLINE struct nv04_resource *
+nv30_transfer_rect_fragprog(struct nv30_context *nv30)
+{
+ struct nv04_resource *fp = nv04_resource(nv30->blit_fp);
+ struct pipe_context *pipe = &nv30->base.pipe;
+
+ if (!fp) {
+ nv30->blit_fp = pipe_buffer_create(pipe->screen, 0, 0, 12 * 4);
+ if (nv30->blit_fp) {
+ struct pipe_transfer *transfer;
+ u32 *map = pipe_buffer_map(pipe, nv30->blit_fp,
+ PIPE_TRANSFER_WRITE, &transfer);
+ if (map) {
+ map[0] = 0x17009e00; /* texr r0, i[tex0], texture[0]; end; */
+ map[1] = 0x1c9dc801;
+ map[2] = 0x0001c800;
+ map[3] = 0x3fe1c800;
+ map[4] = 0x01401e81; /* end; */
+ map[5] = 0x1c9dc800;
+ map[6] = 0x0001c800;
+ map[7] = 0x0001c800;
+ pipe_buffer_unmap(pipe, transfer);
+ }
+
+ fp = nv04_resource(nv30->blit_fp);
+ nouveau_buffer_migrate(&nv30->base, fp, NOUVEAU_BO_VRAM);
+ }
+ }
+
+ return fp;
+}
+
+static void
+nv30_transfer_rect_blit(XFER_ARGS)
+{
+ struct nv04_resource *fp = nv30_transfer_rect_fragprog(nv30);
+ struct nouveau_heap *vp = nv30_transfer_rect_vertprog(nv30);
+ struct nouveau_pushbuf *push = nv30->base.pushbuf;
+ struct nouveau_pushbuf_refn refs[] = {
+ { fp->bo, fp->domain | NOUVEAU_BO_RD },
+ { src->bo, src->domain | NOUVEAU_BO_RD },
+ { dst->bo, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR },
+ };
+ u32 texfmt, texswz;
+ u32 format, stride;
+
+ if (nouveau_pushbuf_space(push, 512, 8, 0) ||
+ nouveau_pushbuf_refn (push, refs, sizeof(refs) / sizeof(refs[0])))
+ return;
+
+ /* various switches depending on cpp of the transfer */
+ switch (dst->cpp) {
+ case 4:
+ format = NV30_3D_RT_FORMAT_COLOR_A8R8G8B8 |
+ NV30_3D_RT_FORMAT_ZETA_Z24S8;
+ texfmt = NV40_3D_TEX_FORMAT_FORMAT_A8R8G8B8;
+ texswz = 0x0000aae4;
+ break;
+ case 2:
+ format = NV30_3D_RT_FORMAT_COLOR_R5G6B5 |
+ NV30_3D_RT_FORMAT_ZETA_Z16;
+ texfmt = NV40_3D_TEX_FORMAT_FORMAT_R5G6B5;
+ texswz = 0x0000a9e4;
+ break;
+ case 1:
+ format = NV30_3D_RT_FORMAT_COLOR_B8 |
+ NV30_3D_RT_FORMAT_ZETA_Z16;
+ texfmt = NV40_3D_TEX_FORMAT_FORMAT_L8;
+ texswz = 0x0000aaff;
+ break;
+ default:
+ assert(0);
+ return;
+ }
+
+ /* render target */
+ if (!dst->pitch) {
+ format |= NV30_3D_RT_FORMAT_TYPE_SWIZZLED;
+ format |= util_logbase2(dst->w) << 16;
+ format |= util_logbase2(dst->h) << 24;
+ stride = 64;
+ } else {
+ format |= NV30_3D_RT_FORMAT_TYPE_LINEAR;
+ stride = dst->pitch;
+ }
+
+ BEGIN_NV04(push, NV30_3D(VIEWPORT_HORIZ), 2);
+ PUSH_DATA (push, dst->w << 16);
+ PUSH_DATA (push, dst->h << 16);
+ BEGIN_NV04(push, NV30_3D(RT_HORIZ), 5);
+ PUSH_DATA (push, dst->w << 16);
+ PUSH_DATA (push, dst->h << 16);
+ PUSH_DATA (push, format);
+ PUSH_DATA (push, stride);
+ PUSH_RELOC(push, dst->bo, dst->offset, NOUVEAU_BO_LOW, 0, 0);
+ BEGIN_NV04(push, NV30_3D(RT_ENABLE), 1);
+ PUSH_DATA (push, NV30_3D_RT_ENABLE_COLOR0);
+
+ nv30->dirty |= NV30_NEW_FRAMEBUFFER;
+
+ /* viewport state */
+ BEGIN_NV04(push, NV30_3D(VIEWPORT_TRANSLATE_X), 8);
+ PUSH_DATAf(push, 0.0);
+ PUSH_DATAf(push, 0.0);
+ PUSH_DATAf(push, 0.0);
+ PUSH_DATAf(push, 0.0);
+ PUSH_DATAf(push, 1.0);
+ PUSH_DATAf(push, 1.0);
+ PUSH_DATAf(push, 1.0);
+ PUSH_DATAf(push, 1.0);
+ BEGIN_NV04(push, NV30_3D(DEPTH_RANGE_NEAR), 2);
+ PUSH_DATAf(push, 0.0);
+ PUSH_DATAf(push, 1.0);
+
+ nv30->dirty |= NV30_NEW_VIEWPORT;
+
+ /* blend state */
+ BEGIN_NV04(push, NV30_3D(COLOR_LOGIC_OP_ENABLE), 1);
+ PUSH_DATA (push, 0);
+ BEGIN_NV04(push, NV30_3D(DITHER_ENABLE), 1);
+ PUSH_DATA (push, 0);
+ BEGIN_NV04(push, NV30_3D(BLEND_FUNC_ENABLE), 1);
+ PUSH_DATA (push, 0);
+ BEGIN_NV04(push, NV30_3D(COLOR_MASK), 1);
+ PUSH_DATA (push, 0x01010101);
+
+ nv30->dirty |= NV30_NEW_BLEND;
+
+ /* depth-stencil-alpha state */
+ BEGIN_NV04(push, NV30_3D(DEPTH_WRITE_ENABLE), 2);
+ PUSH_DATA (push, 0);
+ PUSH_DATA (push, 0);
+ BEGIN_NV04(push, NV30_3D(STENCIL_ENABLE(0)), 1);
+ PUSH_DATA (push, 0);
+ BEGIN_NV04(push, NV30_3D(STENCIL_ENABLE(1)), 1);
+ PUSH_DATA (push, 0);
+ BEGIN_NV04(push, NV30_3D(ALPHA_FUNC_ENABLE), 1);
+ PUSH_DATA (push, 0);
+
+ nv30->dirty |= NV30_NEW_ZSA;
+
+ /* rasterizer state */
+ BEGIN_NV04(push, NV30_3D(SHADE_MODEL), 1);
+ PUSH_DATA (push, NV30_3D_SHADE_MODEL_FLAT);
+ BEGIN_NV04(push, NV30_3D(CULL_FACE_ENABLE), 1);
+ PUSH_DATA (push, 0);
+ BEGIN_NV04(push, NV30_3D(POLYGON_MODE_FRONT), 2);
+ PUSH_DATA (push, NV30_3D_POLYGON_MODE_FRONT_FILL);
+ PUSH_DATA (push, NV30_3D_POLYGON_MODE_BACK_FILL);
+ BEGIN_NV04(push, NV30_3D(POLYGON_OFFSET_FILL_ENABLE), 1);
+ PUSH_DATA (push, 0);
+ BEGIN_NV04(push, NV30_3D(POLYGON_STIPPLE_ENABLE), 1);
+ PUSH_DATA (push, 0);
+
+ nv30->state.scissor_off = 0;
+ nv30->dirty |= NV30_NEW_RASTERIZER;
+
+ /* vertex program */
+ BEGIN_NV04(push, NV30_3D(VP_START_FROM_ID), 1);
+ PUSH_DATA (push, vp->start);
+ BEGIN_NV04(push, NV40_3D(VP_ATTRIB_EN), 2);
+ PUSH_DATA (push, 0x00000101); /* attrib: 0, 8 */
+ PUSH_DATA (push, 0x00004000); /* result: hpos, tex0 */
+ BEGIN_NV04(push, NV30_3D(ENGINE), 1);
+ PUSH_DATA (push, 0x00000103);
+ BEGIN_NV04(push, NV30_3D(VP_CLIP_PLANES_ENABLE), 1);
+ PUSH_DATA (push, 0x00000000);
+
+ nv30->dirty |= NV30_NEW_VERTPROG;
+ nv30->dirty |= NV30_NEW_CLIP;
+
+ /* fragment program */
+ BEGIN_NV04(push, NV30_3D(FP_ACTIVE_PROGRAM), 1);
+ PUSH_RELOC(push, fp->bo, fp->offset, fp->domain |
+ NOUVEAU_BO_LOW | NOUVEAU_BO_OR,
+ NV30_3D_FP_ACTIVE_PROGRAM_DMA0,
+ NV30_3D_FP_ACTIVE_PROGRAM_DMA1);
+ BEGIN_NV04(push, NV30_3D(FP_CONTROL), 1);
+ PUSH_DATA (push, 0x02000000);
+
+ nv30->state.fragprog = NULL;
+ nv30->dirty |= NV30_NEW_FRAGPROG;
+
+ /* texture */
+ texfmt |= 1 << NV40_3D_TEX_FORMAT_MIPMAP_COUNT__SHIFT;
+ texfmt |= NV30_3D_TEX_FORMAT_NO_BORDER;
+ texfmt |= NV40_3D_TEX_FORMAT_RECT;
+ texfmt |= 0x00008000;
+ if (src->d < 2)
+ texfmt |= NV30_3D_TEX_FORMAT_DIMS_2D;
+ else
+ texfmt |= NV30_3D_TEX_FORMAT_DIMS_3D;
+ if (src->pitch)
+ texfmt |= NV40_3D_TEX_FORMAT_LINEAR;
+
+ BEGIN_NV04(push, NV30_3D(TEX_OFFSET(0)), 8);
+ PUSH_RELOC(push, src->bo, src->offset, NOUVEAU_BO_LOW, 0, 0);
+ PUSH_RELOC(push, src->bo, texfmt, NOUVEAU_BO_OR,
+ NV30_3D_TEX_FORMAT_DMA0, NV30_3D_TEX_FORMAT_DMA1);
+ PUSH_DATA (push, NV30_3D_TEX_WRAP_S_CLAMP_TO_EDGE |
+ NV30_3D_TEX_WRAP_T_CLAMP_TO_EDGE |
+ NV30_3D_TEX_WRAP_R_CLAMP_TO_EDGE);
+ PUSH_DATA (push, NV40_3D_TEX_ENABLE_ENABLE);
+ PUSH_DATA (push, texswz);
+ switch (filter) {
+ case BILINEAR:
+ PUSH_DATA (push, NV30_3D_TEX_FILTER_MIN_LINEAR |
+ NV30_3D_TEX_FILTER_MAG_LINEAR | 0x00002000);
+ break;
+ default:
+ PUSH_DATA (push, NV30_3D_TEX_FILTER_MIN_NEAREST |
+ NV30_3D_TEX_FILTER_MAG_NEAREST | 0x00002000);
+ break;
+ }
+ PUSH_DATA (push, (src->w << 16) | src->h);
+ PUSH_DATA (push, 0x00000000);
+ BEGIN_NV04(push, NV40_3D(TEX_SIZE1(0)), 1);
+ PUSH_DATA (push, 0x00100000 | src->pitch);
+ BEGIN_NV04(push, SUBC_3D(0x0b40), 1);
+ PUSH_DATA (push, src->d < 2 ? 0x00000001 : 0x00000000);
+ BEGIN_NV04(push, NV40_3D(TEX_CACHE_CTL), 1);
+ PUSH_DATA (push, 1);
+
+ nv30->fragprog.dirty_samplers |= 1;
+ nv30->dirty |= NV30_NEW_FRAGTEX;
+
+ /* blit! */
+ BEGIN_NV04(push, NV30_3D(SCISSOR_HORIZ), 2);
+ PUSH_DATA (push, (dst->x1 - dst->x0) << 16 | dst->x0);
+ PUSH_DATA (push, (dst->y1 - dst->y0) << 16 | dst->y0);
+ BEGIN_NV04(push, NV30_3D(VERTEX_BEGIN_END), 1);
+ PUSH_DATA (push, NV30_3D_VERTEX_BEGIN_END_QUADS);
+ BEGIN_NV04(push, NV30_3D(VTX_ATTR_3F(8)), 3);
+ PUSH_DATAf(push, src->x0);
+ PUSH_DATAf(push, src->y0);
+ PUSH_DATAf(push, src->z);
+ BEGIN_NV04(push, NV30_3D(VTX_ATTR_2I(0)), 1);
+ PUSH_DATA (push, (dst->y0 << 16) | dst->x0);
+ BEGIN_NV04(push, NV30_3D(VTX_ATTR_3F(8)), 3);
+ PUSH_DATAf(push, src->x1);
+ PUSH_DATAf(push, src->y0);
+ PUSH_DATAf(push, src->z);
+ BEGIN_NV04(push, NV30_3D(VTX_ATTR_2I(0)), 1);
+ PUSH_DATA (push, (dst->y0 << 16) | dst->x1);
+ BEGIN_NV04(push, NV30_3D(VTX_ATTR_3F(8)), 3);
+ PUSH_DATAf(push, src->x1);
+ PUSH_DATAf(push, src->y1);
+ PUSH_DATAf(push, src->z);
+ BEGIN_NV04(push, NV30_3D(VTX_ATTR_2I(0)), 1);
+ PUSH_DATA (push, (dst->y1 << 16) | dst->x1);
+ BEGIN_NV04(push, NV30_3D(VTX_ATTR_3F(8)), 3);
+ PUSH_DATAf(push, src->x0);
+ PUSH_DATAf(push, src->y1);
+ PUSH_DATAf(push, src->z);
+ BEGIN_NV04(push, NV30_3D(VTX_ATTR_2I(0)), 1);
+ PUSH_DATA (push, (dst->y1 << 16) | dst->x0);
+ BEGIN_NV04(push, NV30_3D(VERTEX_BEGIN_END), 1);
+ PUSH_DATA (push, NV30_3D_VERTEX_BEGIN_END_STOP);
+}
+
+static boolean
+nv30_transfer_sifm(XFER_ARGS)
+{
+ if (!src->pitch || (src->w | src->h) > 1024 || src->w < 2 || src->h < 2)
+ return FALSE;
+
+ if (src->d > 1 || dst->d > 1)
+ return FALSE;
+
+ if (dst->offset & 63)
+ return FALSE;