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] != DRM_FORMAT_MOD_INVALID) {
427            if (modifiers[i] == DRM_FORMAT_MOD_INVALID ||
428                prio_supported_mods[p] == modifiers[i]) {
429               if (top_mod_slot > p) top_mod_slot = p;
430               break;
431            }
432         }
433      }
434   }
435
436   if (top_mod_slot >= ARRAY_SIZE(prio_supported_mods))
437       return DRM_FORMAT_MOD_INVALID;
438
439   return prio_supported_mods[top_mod_slot];
440}
441
442struct pipe_resource *
443nvc0_miptree_create(struct pipe_screen *pscreen,
444                    const struct pipe_resource *templ,
445                    const uint64_t *modifiers, unsigned int count)
446{
447   struct nouveau_device *dev = nouveau_screen(pscreen)->device;
448   struct nouveau_drm *drm = nouveau_screen(pscreen)->drm;
449   struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree);
450   struct pipe_resource *pt = &mt->base.base;
451   bool compressed = drm->version >= 0x01000101;
452   int ret;
453   union nouveau_bo_config bo_config;
454   uint32_t bo_flags;
455   unsigned pitch_align;
456   uint64_t modifier = DRM_FORMAT_MOD_INVALID;
457
458   if (!mt)
459      return NULL;
460
461   *pt = *templ;
462   pipe_reference_init(&pt->reference, 1);
463   pt->screen = pscreen;
464
465   if (pt->usage == PIPE_USAGE_STAGING) {
466      /* PIPE_USAGE_STAGING, and usage in general, should not be specified when
467       * modifiers are used. */
468      assert(count == 0);
469      switch (pt->target) {
470      case PIPE_TEXTURE_2D:
471      case PIPE_TEXTURE_RECT:
472         if (pt->last_level == 0 &&
473             !util_format_is_depth_or_stencil(pt->format) &&
474             pt->nr_samples <= 1)
475            pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR;
476         break;
477      default:
478         break;
479      }
480   }
481
482   if (pt->bind & PIPE_BIND_LINEAR)
483      pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR;
484
485   if (count > 0) {
486      modifier = nvc0_miptree_select_best_modifier(pscreen, mt,
487                                                   modifiers, count);
488
489      if (modifier == DRM_FORMAT_MOD_INVALID) {
490         FREE(mt);
491         return NULL;
492      }
493
494      if (modifier == DRM_FORMAT_MOD_LINEAR) {
495         pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR;
496         bo_config.nvc0.memtype = 0;
497      } else {
498         bo_config.nvc0.memtype = (modifier >> 12) & 0xff;
499      }
500   } else {
501      bo_config.nvc0.memtype = nvc0_mt_choose_storage_type(pscreen, mt, compressed);
502   }
503
504   if (!nvc0_miptree_init_ms_mode(mt)) {
505      FREE(mt);
506      return NULL;
507   }
508
509   if (unlikely(pt->flags & NVC0_RESOURCE_FLAG_VIDEO)) {
510      assert(modifier == DRM_FORMAT_MOD_INVALID);
511      nvc0_miptree_init_layout_video(mt);
512   } else
513   if (likely(bo_config.nvc0.memtype)) {
514      nvc0_miptree_init_layout_tiled(mt, modifier);
515   } else {
516      /* When modifiers are supplied, usage is zero. TODO: detect the
517       * modifiers+cursor case. */
518      if (pt->usage & PIPE_BIND_CURSOR)
519         pitch_align = 1;
520      else if ((pt->usage & PIPE_BIND_SCANOUT) || count > 0)
521         pitch_align = 256;
522      else
523         pitch_align = 128;
524      if (!nv50_miptree_init_layout_linear(mt, pitch_align)) {
525         FREE(mt);
526         return NULL;
527      }
528   }
529   bo_config.nvc0.tile_mode = mt->level[0].tile_mode;
530
531   if (!bo_config.nvc0.memtype && (pt->usage == PIPE_USAGE_STAGING || pt->bind & PIPE_BIND_SHARED))
532      mt->base.domain = NOUVEAU_BO_GART;
533   else
534      mt->base.domain = NV_VRAM_DOMAIN(nouveau_screen(pscreen));
535
536   bo_flags = mt->base.domain | NOUVEAU_BO_NOSNOOP;
537
538   if (mt->base.base.bind & (PIPE_BIND_CURSOR | PIPE_BIND_DISPLAY_TARGET))
539      bo_flags |= NOUVEAU_BO_CONTIG;
540
541   ret = nouveau_bo_new(dev, bo_flags, 4096, mt->total_size, &bo_config,
542                        &mt->base.bo);
543   if (ret) {
544      FREE(mt);
545      return NULL;
546   }
547   mt->base.address = mt->base.bo->offset;
548
549   NOUVEAU_DRV_STAT(nouveau_screen(pscreen), tex_obj_current_count, 1);
550   NOUVEAU_DRV_STAT(nouveau_screen(pscreen), tex_obj_current_bytes,
551                    mt->total_size);
552
553   return pt;
554}
555
556/* Offset of zslice @z from start of level @l. */
557inline unsigned
558nvc0_mt_zslice_offset(const struct nv50_miptree *mt, unsigned l, unsigned z)
559{
560   const struct pipe_resource *pt = &mt->base.base;
561
562   unsigned tds = NVC0_TILE_SHIFT_Z(mt->level[l].tile_mode);
563   unsigned ths = NVC0_TILE_SHIFT_Y(mt->level[l].tile_mode);
564
565   unsigned nby = util_format_get_nblocksy(pt->format,
566                                           u_minify(pt->height0, l));
567
568   /* to next 2D tile slice within a 3D tile */
569   unsigned stride_2d = NVC0_TILE_SIZE_2D(mt->level[l].tile_mode);
570
571   /* to slice in the next (in z direction) 3D tile */
572   unsigned stride_3d = (align(nby, (1 << ths)) * mt->level[l].pitch) << tds;
573
574   return (z & (1 << (tds - 1))) * stride_2d + (z >> tds) * stride_3d;
575}
576
577/* Surface functions.
578 */
579
580struct pipe_surface *
581nvc0_miptree_surface_new(struct pipe_context *pipe,
582                         struct pipe_resource *pt,
583                         const struct pipe_surface *templ)
584{
585   struct nv50_surface *ns = nv50_surface_from_miptree(nv50_miptree(pt), templ);
586   if (!ns)
587      return NULL;
588   ns->base.context = pipe;
589   return &ns->base;
590}
591