1 /*
2  * Copyright 2008 Ben Skeggs
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "drm-uapi/drm_fourcc.h"
24 
25 #include "pipe/p_state.h"
26 #include "pipe/p_defines.h"
27 #include "frontend/drm_driver.h"
28 #include "util/u_inlines.h"
29 #include "util/format/u_format.h"
30 
31 #include "nvc0/nvc0_context.h"
32 #include "nvc0/nvc0_resource.h"
33 
34 static uint32_t
nvc0_tex_choose_tile_dims(unsigned nx,unsigned ny,unsigned nz,bool is_3d)35 nvc0_tex_choose_tile_dims(unsigned nx, unsigned ny, unsigned nz, bool is_3d)
36 {
37    return nv50_tex_choose_tile_dims_helper(nx, ny, nz, is_3d);
38 }
39 
40 static uint32_t
tu102_choose_tiled_storage_type(enum pipe_format format,unsigned ms,bool compressed)41 tu102_choose_tiled_storage_type(enum pipe_format format,
42                                 unsigned ms,
43                                 bool compressed)
44 
45 {
46    uint32_t kind;
47 
48    switch (format) {
49    case PIPE_FORMAT_Z16_UNORM:
50       if (compressed)
51          kind = 0x0b; // NV_MMU_PTE_KIND_Z16_COMPRESSIBLE_DISABLE_PLC
52       else
53          kind = 0x01; // NV_MMU_PTE_KIND_Z16
54       break;
55    case PIPE_FORMAT_X8Z24_UNORM:
56    case PIPE_FORMAT_S8X24_UINT:
57    case PIPE_FORMAT_S8_UINT_Z24_UNORM:
58       if (compressed)
59          kind = 0x0e; // NV_MMU_PTE_KIND_Z24S8_COMPRESSIBLE_DISABLE_PLC
60       else
61          kind = 0x05; // NV_MMU_PTE_KIND_Z24S8
62       break;
63    case PIPE_FORMAT_X24S8_UINT:
64    case PIPE_FORMAT_Z24X8_UNORM:
65    case PIPE_FORMAT_Z24_UNORM_S8_UINT:
66       if (compressed)
67          kind = 0x0c; // NV_MMU_PTE_KIND_S8Z24_COMPRESSIBLE_DISABLE_PLC
68       else
69          kind = 0x03; // NV_MMU_PTE_KIND_S8Z24
70       break;
71    case PIPE_FORMAT_X32_S8X24_UINT:
72    case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
73       if (compressed)
74          kind = 0x0d; // NV_MMU_PTE_KIND_ZF32_X24S8_COMPRESSIBLE_DISABLE_PLC
75       else
76          kind = 0x04; // NV_MMU_PTE_KIND_ZF32_X24S8
77       break;
78    case PIPE_FORMAT_Z32_FLOAT:
79    default:
80       kind = 0x06;
81       break;
82    }
83 
84    return kind;
85 }
86 
87 uint32_t
nvc0_choose_tiled_storage_type(struct pipe_screen * pscreen,enum pipe_format format,unsigned ms,bool compressed)88 nvc0_choose_tiled_storage_type(struct pipe_screen *pscreen,
89                                enum pipe_format format,
90                                unsigned ms,
91                                bool compressed)
92 {
93    uint32_t tile_flags;
94 
95    if (nouveau_screen(pscreen)->device->chipset >= 0x160)
96       return tu102_choose_tiled_storage_type(format, ms, compressed);
97 
98    switch (format) {
99    case PIPE_FORMAT_Z16_UNORM:
100       if (compressed)
101          tile_flags = 0x02 + ms;
102       else
103          tile_flags = 0x01;
104       break;
105    case PIPE_FORMAT_X8Z24_UNORM:
106    case PIPE_FORMAT_S8X24_UINT:
107    case PIPE_FORMAT_S8_UINT_Z24_UNORM:
108       if (compressed)
109          tile_flags = 0x51 + ms;
110       else
111          tile_flags = 0x46;
112       break;
113    case PIPE_FORMAT_X24S8_UINT:
114    case PIPE_FORMAT_Z24X8_UNORM:
115    case PIPE_FORMAT_Z24_UNORM_S8_UINT:
116       if (compressed)
117          tile_flags = 0x17 + ms;
118       else
119          tile_flags = 0x11;
120       break;
121    case PIPE_FORMAT_Z32_FLOAT:
122       if (compressed)
123          tile_flags = 0x86 + ms;
124       else
125          tile_flags = 0x7b;
126       break;
127    case PIPE_FORMAT_X32_S8X24_UINT:
128    case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
129       if (compressed)
130          tile_flags = 0xce + ms;
131       else
132          tile_flags = 0xc3;
133       break;
134    default:
135       switch (util_format_get_blocksizebits(format)) {
136       case 128:
137          if (compressed)
138             tile_flags = 0xf4 + ms * 2;
139          else
140             tile_flags = 0xfe;
141          break;
142       case 64:
143          if (compressed) {
144             switch (ms) {
145             case 0: tile_flags = 0xe6; break;
146             case 1: tile_flags = 0xeb; break;
147             case 2: tile_flags = 0xed; break;
148             case 3: tile_flags = 0xf2; break;
149             default:
150                return 0;
151             }
152          } else {
153             tile_flags = 0xfe;
154          }
155          break;
156       case 32:
157          if (compressed && ms) {
158             switch (ms) {
159                /* This one makes things blurry:
160             case 0: tile_flags = 0xdb; break;
161                */
162             case 1: tile_flags = 0xdd; break;
163             case 2: tile_flags = 0xdf; break;
164             case 3: tile_flags = 0xe4; break;
165             default:
166                return 0;
167             }
168          } else {
169             tile_flags = 0xfe;
170          }
171          break;
172       case 16:
173       case 8:
174          tile_flags = 0xfe;
175          break;
176       default:
177          return 0;
178       }
179       break;
180    }
181 
182    return tile_flags;
183 }
184 
185 static uint32_t
nvc0_mt_choose_storage_type(struct pipe_screen * pscreen,const struct nv50_miptree * mt,bool compressed)186 nvc0_mt_choose_storage_type(struct pipe_screen *pscreen,
187                             const struct nv50_miptree *mt,
188                             bool compressed)
189 {
190    const unsigned ms = util_logbase2(mt->base.base.nr_samples);
191 
192    if (unlikely(mt->base.base.bind & PIPE_BIND_CURSOR))
193       return 0;
194    if (unlikely(mt->base.base.flags & NOUVEAU_RESOURCE_FLAG_LINEAR))
195       return 0;
196 
197    return nvc0_choose_tiled_storage_type(pscreen, mt->base.base.format, ms, compressed);
198 }
199 
200 static inline bool
nvc0_miptree_init_ms_mode(struct nv50_miptree * mt)201 nvc0_miptree_init_ms_mode(struct nv50_miptree *mt)
202 {
203    switch (mt->base.base.nr_samples) {
204    case 8:
205       mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS8;
206       mt->ms_x = 2;
207       mt->ms_y = 1;
208       break;
209    case 4:
210       mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS4;
211       mt->ms_x = 1;
212       mt->ms_y = 1;
213       break;
214    case 2:
215       mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS2;
216       mt->ms_x = 1;
217       break;
218    case 1:
219    case 0:
220       mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS1;
221       break;
222    default:
223       NOUVEAU_ERR("invalid nr_samples: %u\n", mt->base.base.nr_samples);
224       return false;
225    }
226    return true;
227 }
228 
229 static void
nvc0_miptree_init_layout_video(struct nv50_miptree * mt)230 nvc0_miptree_init_layout_video(struct nv50_miptree *mt)
231 {
232    const struct pipe_resource *pt = &mt->base.base;
233    const unsigned blocksize = util_format_get_blocksize(pt->format);
234 
235    assert(pt->last_level == 0);
236    assert(mt->ms_x == 0 && mt->ms_y == 0);
237    assert(!util_format_is_compressed(pt->format));
238 
239    mt->layout_3d = pt->target == PIPE_TEXTURE_3D;
240 
241    mt->level[0].tile_mode = 0x10;
242    mt->level[0].pitch = align(pt->width0 * blocksize, 64);
243    mt->total_size = align(pt->height0, 16) * mt->level[0].pitch * (mt->layout_3d ? pt->depth0 : 1);
244 
245    if (pt->array_size > 1) {
246       mt->layer_stride = align(mt->total_size, NVC0_TILE_SIZE(0x10));
247       mt->total_size = mt->layer_stride * pt->array_size;
248    }
249 }
250 
251 static void
nvc0_miptree_init_layout_tiled(struct nv50_miptree * mt,uint64_t modifier)252 nvc0_miptree_init_layout_tiled(struct nv50_miptree *mt, uint64_t modifier)
253 {
254    struct pipe_resource *pt = &mt->base.base;
255    unsigned w, h, d, l;
256    const unsigned blocksize = util_format_get_blocksize(pt->format);
257 
258    mt->layout_3d = pt->target == PIPE_TEXTURE_3D;
259 
260    w = pt->width0 << mt->ms_x;
261    h = pt->height0 << mt->ms_y;
262 
263    /* For 3D textures, a mipmap is spanned by all the layers, for array
264     * textures and cube maps, each layer contains its own mipmaps.
265     */
266    d = mt->layout_3d ? pt->depth0 : 1;
267 
268    assert(!mt->ms_mode || !pt->last_level);
269    assert(modifier == DRM_FORMAT_MOD_INVALID ||
270           (!pt->last_level && !mt->layout_3d));
271    assert(modifier != DRM_FORMAT_MOD_LINEAR);
272 
273    for (l = 0; l <= pt->last_level; ++l) {
274       struct nv50_miptree_level *lvl = &mt->level[l];
275       unsigned tsx, tsy, tsz;
276       unsigned nbx = util_format_get_nblocksx(pt->format, w);
277       unsigned nby = util_format_get_nblocksy(pt->format, h);
278 
279       lvl->offset = mt->total_size;
280 
281       if (modifier != DRM_FORMAT_MOD_INVALID)
282          /* Extract the log2(block height) field from the modifier and pack it
283           * into tile_mode's y field. Other tile dimensions are always 1
284           * (represented using 0 here) for 2D surfaces, and non-2D surfaces are
285           * not supported by the current modifiers (asserted above). Note the
286           * modifier must be validated prior to calling this function.
287           */
288          lvl->tile_mode = ((uint32_t)modifier & 0xf) << 4;
289       else
290          lvl->tile_mode = nvc0_tex_choose_tile_dims(nbx, nby, d, mt->layout_3d);
291 
292       tsx = NVC0_TILE_SIZE_X(lvl->tile_mode); /* x is tile row pitch in bytes */
293       tsy = NVC0_TILE_SIZE_Y(lvl->tile_mode);
294       tsz = NVC0_TILE_SIZE_Z(lvl->tile_mode);
295 
296       lvl->pitch = align(nbx * blocksize, tsx);
297 
298       mt->total_size += lvl->pitch * align(nby, tsy) * align(d, tsz);
299 
300       w = u_minify(w, 1);
301       h = u_minify(h, 1);
302       d = u_minify(d, 1);
303    }
304 
305    if (pt->array_size > 1) {
306       mt->layer_stride = align(mt->total_size,
307                                NVC0_TILE_SIZE(mt->level[0].tile_mode));
308       mt->total_size = mt->layer_stride * pt->array_size;
309    }
310 }
311 
312 static uint64_t
nvc0_miptree_get_modifier(struct pipe_screen * pscreen,struct nv50_miptree * mt)313 nvc0_miptree_get_modifier(struct pipe_screen *pscreen, struct nv50_miptree *mt)
314 {
315    const union nouveau_bo_config *config = &mt->base.bo->config;
316    const uint32_t uc_kind =
317       nvc0_choose_tiled_storage_type(pscreen,
318                                      mt->base.base.format,
319                                      mt->base.base.nr_samples,
320                                      false);
321    const uint32_t kind_gen = nvc0_get_kind_generation(pscreen);
322 
323    if (mt->layout_3d)
324       return DRM_FORMAT_MOD_INVALID;
325    if (mt->base.base.nr_samples > 1)
326       return DRM_FORMAT_MOD_INVALID;
327    if (config->nvc0.memtype == 0x00)
328       return DRM_FORMAT_MOD_LINEAR;
329    if (NVC0_TILE_MODE_Y(config->nvc0.tile_mode) > 5)
330       return DRM_FORMAT_MOD_INVALID;
331    if (config->nvc0.memtype != uc_kind)
332       return DRM_FORMAT_MOD_INVALID;
333 
334    return DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(
335              0,
336              nouveau_screen(pscreen)->tegra_sector_layout ? 0 : 1,
337              kind_gen,
338              config->nvc0.memtype,
339              NVC0_TILE_MODE_Y(config->nvc0.tile_mode));
340 }
341 
342 bool
nvc0_miptree_get_handle(struct pipe_screen * pscreen,struct pipe_context * context,struct pipe_resource * pt,struct winsys_handle * whandle,unsigned usage)343 nvc0_miptree_get_handle(struct pipe_screen *pscreen,
344                         struct pipe_context *context,
345                         struct pipe_resource *pt,
346                         struct winsys_handle *whandle,
347                         unsigned usage)
348 {
349    struct nv50_miptree *mt = nv50_miptree(pt);
350    bool ret;
351 
352    ret = nv50_miptree_get_handle(pscreen, context, pt, whandle, usage);
353    if (!ret)
354       return ret;
355 
356    whandle->modifier = nvc0_miptree_get_modifier(pscreen, mt);
357 
358    return true;
359 }
360 
361 static uint64_t
nvc0_miptree_select_best_modifier(struct pipe_screen * pscreen,const struct nv50_miptree * mt,const uint64_t * modifiers,unsigned int count)362 nvc0_miptree_select_best_modifier(struct pipe_screen *pscreen,
363                                   const struct nv50_miptree *mt,
364                                   const uint64_t *modifiers,
365                                   unsigned int count)
366 {
367    /*
368     * Supported block heights are 1,2,4,8,16,32, stored as log2() their
369     * value. Reserve one slot for each, as well as the linear modifier.
370     */
371    uint64_t prio_supported_mods[] = {
372       DRM_FORMAT_MOD_INVALID,
373       DRM_FORMAT_MOD_INVALID,
374       DRM_FORMAT_MOD_INVALID,
375       DRM_FORMAT_MOD_INVALID,
376       DRM_FORMAT_MOD_INVALID,
377       DRM_FORMAT_MOD_INVALID,
378       DRM_FORMAT_MOD_LINEAR,
379    };
380    const uint32_t uc_kind = nvc0_mt_choose_storage_type(pscreen, mt, false);
381    int top_mod_slot = ARRAY_SIZE(prio_supported_mods);
382    const uint32_t kind_gen = nvc0_get_kind_generation(pscreen);
383    unsigned int i;
384    int p;
385 
386    if (uc_kind != 0u) {
387       const struct pipe_resource *pt = &mt->base.base;
388       const unsigned nbx = util_format_get_nblocksx(pt->format, pt->width0);
389       const unsigned nby = util_format_get_nblocksy(pt->format, pt->height0);
390       const uint32_t lbh_preferred =
391          NVC0_TILE_MODE_Y(nvc0_tex_choose_tile_dims(nbx, nby, 1u, false));
392       uint32_t lbh = lbh_preferred;
393       bool dec_lbh = true;
394       const uint8_t s = nouveau_screen(pscreen)->tegra_sector_layout ? 0 : 1;
395 
396       for (i = 0; i < ARRAY_SIZE(prio_supported_mods) - 1; i++) {
397          assert(lbh <= 5u);
398          prio_supported_mods[i] =
399             DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, s, kind_gen, uc_kind, lbh);
400 
401          /*
402           * The preferred block height is the largest block size that doesn't
403           * waste excessive space with unused padding bytes relative to the
404           * height of the image.  Construct the priority array such that
405           * the preferred block height is highest priority, followed by
406           * progressively smaller block sizes down to a block height of one,
407           * followed by progressively larger (more wasteful) block sizes up
408           * to 5.
409           */
410          if (lbh == 0u) {
411             lbh = lbh_preferred + 1u;
412             dec_lbh = false;
413          } else if (dec_lbh) {
414             lbh--;
415          } else {
416             lbh++;
417          }
418       }
419    }
420 
421    assert(prio_supported_mods[ARRAY_SIZE(prio_supported_mods) - 1] ==
422           DRM_FORMAT_MOD_LINEAR);
423 
424    for (i = 0u; i < count; i++) {
425       for (p = 0; p < ARRAY_SIZE(prio_supported_mods); p++) {
426          if (prio_supported_mods[p] == modifiers[i]) {
427             if (top_mod_slot > p) top_mod_slot = p;
428             break;
429          }
430       }
431    }
432 
433    if (top_mod_slot >= ARRAY_SIZE(prio_supported_mods))
434        return DRM_FORMAT_MOD_INVALID;
435 
436    return prio_supported_mods[top_mod_slot];
437 }
438 
439 struct pipe_resource *
nvc0_miptree_create(struct pipe_screen * pscreen,const struct pipe_resource * templ,const uint64_t * modifiers,unsigned int count)440 nvc0_miptree_create(struct pipe_screen *pscreen,
441                     const struct pipe_resource *templ,
442                     const uint64_t *modifiers, unsigned int count)
443 {
444    struct nouveau_device *dev = nouveau_screen(pscreen)->device;
445    struct nouveau_drm *drm = nouveau_screen(pscreen)->drm;
446    struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree);
447    struct pipe_resource *pt = &mt->base.base;
448    bool compressed = drm->version >= 0x01000101;
449    int ret;
450    union nouveau_bo_config bo_config;
451    uint32_t bo_flags;
452    unsigned pitch_align;
453    uint64_t modifier = DRM_FORMAT_MOD_INVALID;
454 
455    if (!mt)
456       return NULL;
457 
458    *pt = *templ;
459    pipe_reference_init(&pt->reference, 1);
460    pt->screen = pscreen;
461 
462    if (pt->usage == PIPE_USAGE_STAGING) {
463       /* PIPE_USAGE_STAGING, and usage in general, should not be specified when
464        * modifiers are used. */
465       assert(count == 0);
466       switch (pt->target) {
467       case PIPE_TEXTURE_2D:
468       case PIPE_TEXTURE_RECT:
469          if (pt->last_level == 0 &&
470              !util_format_is_depth_or_stencil(pt->format) &&
471              pt->nr_samples <= 1)
472             pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR;
473          break;
474       default:
475          break;
476       }
477    }
478 
479    if (pt->bind & PIPE_BIND_LINEAR)
480       pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR;
481 
482    if (count > 0) {
483       modifier = nvc0_miptree_select_best_modifier(pscreen, mt,
484                                                    modifiers, count);
485 
486       if (modifier == DRM_FORMAT_MOD_INVALID) {
487          FREE(mt);
488          return NULL;
489       }
490 
491       if (modifier == DRM_FORMAT_MOD_LINEAR) {
492          pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR;
493          bo_config.nvc0.memtype = 0;
494       } else {
495          bo_config.nvc0.memtype = (modifier >> 12) & 0xff;
496       }
497    } else {
498       bo_config.nvc0.memtype = nvc0_mt_choose_storage_type(pscreen, mt, compressed);
499    }
500 
501    if (!nvc0_miptree_init_ms_mode(mt)) {
502       FREE(mt);
503       return NULL;
504    }
505 
506    if (unlikely(pt->flags & NVC0_RESOURCE_FLAG_VIDEO)) {
507       assert(modifier == DRM_FORMAT_MOD_INVALID);
508       nvc0_miptree_init_layout_video(mt);
509    } else
510    if (likely(bo_config.nvc0.memtype)) {
511       nvc0_miptree_init_layout_tiled(mt, modifier);
512    } else {
513       /* When modifiers are supplied, usage is zero. TODO: detect the
514        * modifiers+cursor case. */
515       if (pt->usage & PIPE_BIND_CURSOR)
516          pitch_align = 1;
517       else if ((pt->usage & PIPE_BIND_SCANOUT) || count > 0)
518          pitch_align = 256;
519       else
520          pitch_align = 128;
521       if (!nv50_miptree_init_layout_linear(mt, pitch_align)) {
522          FREE(mt);
523          return NULL;
524       }
525    }
526    bo_config.nvc0.tile_mode = mt->level[0].tile_mode;
527 
528    if (!bo_config.nvc0.memtype && (pt->usage == PIPE_USAGE_STAGING || pt->bind & PIPE_BIND_SHARED))
529       mt->base.domain = NOUVEAU_BO_GART;
530    else
531       mt->base.domain = NV_VRAM_DOMAIN(nouveau_screen(pscreen));
532 
533    bo_flags = mt->base.domain | NOUVEAU_BO_NOSNOOP;
534 
535    if (mt->base.base.bind & (PIPE_BIND_CURSOR | PIPE_BIND_DISPLAY_TARGET))
536       bo_flags |= NOUVEAU_BO_CONTIG;
537 
538    ret = nouveau_bo_new(dev, bo_flags, 4096, mt->total_size, &bo_config,
539                         &mt->base.bo);
540    if (ret) {
541       FREE(mt);
542       return NULL;
543    }
544    mt->base.address = mt->base.bo->offset;
545 
546    NOUVEAU_DRV_STAT(nouveau_screen(pscreen), tex_obj_current_count, 1);
547    NOUVEAU_DRV_STAT(nouveau_screen(pscreen), tex_obj_current_bytes,
548                     mt->total_size);
549 
550    return pt;
551 }
552 
553 /* Offset of zslice @z from start of level @l. */
554 inline unsigned
nvc0_mt_zslice_offset(const struct nv50_miptree * mt,unsigned l,unsigned z)555 nvc0_mt_zslice_offset(const struct nv50_miptree *mt, unsigned l, unsigned z)
556 {
557    const struct pipe_resource *pt = &mt->base.base;
558 
559    unsigned tds = NVC0_TILE_SHIFT_Z(mt->level[l].tile_mode);
560    unsigned ths = NVC0_TILE_SHIFT_Y(mt->level[l].tile_mode);
561 
562    unsigned nby = util_format_get_nblocksy(pt->format,
563                                            u_minify(pt->height0, l));
564 
565    /* to next 2D tile slice within a 3D tile */
566    unsigned stride_2d = NVC0_TILE_SIZE_2D(mt->level[l].tile_mode);
567 
568    /* to slice in the next (in z direction) 3D tile */
569    unsigned stride_3d = (align(nby, (1 << ths)) * mt->level[l].pitch) << tds;
570 
571    return (z & (1 << (tds - 1))) * stride_2d + (z >> tds) * stride_3d;
572 }
573 
574 /* Surface functions.
575  */
576 
577 struct pipe_surface *
nvc0_miptree_surface_new(struct pipe_context * pipe,struct pipe_resource * pt,const struct pipe_surface * templ)578 nvc0_miptree_surface_new(struct pipe_context *pipe,
579                          struct pipe_resource *pt,
580                          const struct pipe_surface *templ)
581 {
582    struct nv50_surface *ns = nv50_surface_from_miptree(nv50_miptree(pt), templ);
583    if (!ns)
584       return NULL;
585    ns->base.context = pipe;
586    return &ns->base;
587 }
588