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
34static uint32_t
35nvc0_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
40static uint32_t
41tu102_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
87uint32_t
88nvc0_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
185static uint32_t
186nvc0_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
200static inline bool
201nvc0_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
229static void
230nvc0_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
251static void
252nvc0_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
312static uint64_t
313nvc0_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
342bool
343nvc0_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
361static uint64_t
362nvc0_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
439struct pipe_resource *
440nvc0_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. */
554inline unsigned
555nvc0_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
577struct pipe_surface *
578nvc0_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