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 "state_tracker/drm_driver.h"
28#include "util/u_inlines.h"
29#include "util/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
41nvc0_mt_choose_storage_type(struct nv50_miptree *mt, bool compressed)
42{
43   const unsigned ms = util_logbase2(mt->base.base.nr_samples);
44
45   uint32_t tile_flags;
46
47   if (unlikely(mt->base.base.bind & PIPE_BIND_CURSOR))
48      return 0;
49   if (unlikely(mt->base.base.flags & NOUVEAU_RESOURCE_FLAG_LINEAR))
50      return 0;
51
52   switch (mt->base.base.format) {
53   case PIPE_FORMAT_Z16_UNORM:
54      if (compressed)
55         tile_flags = 0x02 + ms;
56      else
57         tile_flags = 0x01;
58      break;
59   case PIPE_FORMAT_X8Z24_UNORM:
60   case PIPE_FORMAT_S8X24_UINT:
61   case PIPE_FORMAT_S8_UINT_Z24_UNORM:
62      if (compressed)
63         tile_flags = 0x51 + ms;
64      else
65         tile_flags = 0x46;
66      break;
67   case PIPE_FORMAT_X24S8_UINT:
68   case PIPE_FORMAT_Z24X8_UNORM:
69   case PIPE_FORMAT_Z24_UNORM_S8_UINT:
70      if (compressed)
71         tile_flags = 0x17 + ms;
72      else
73         tile_flags = 0x11;
74      break;
75   case PIPE_FORMAT_Z32_FLOAT:
76      if (compressed)
77         tile_flags = 0x86 + ms;
78      else
79         tile_flags = 0x7b;
80      break;
81   case PIPE_FORMAT_X32_S8X24_UINT:
82   case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
83      if (compressed)
84         tile_flags = 0xce + ms;
85      else
86         tile_flags = 0xc3;
87      break;
88   default:
89      switch (util_format_get_blocksizebits(mt->base.base.format)) {
90      case 128:
91         if (compressed)
92            tile_flags = 0xf4 + ms * 2;
93         else
94            tile_flags = 0xfe;
95         break;
96      case 64:
97         if (compressed) {
98            switch (ms) {
99            case 0: tile_flags = 0xe6; break;
100            case 1: tile_flags = 0xeb; break;
101            case 2: tile_flags = 0xed; break;
102            case 3: tile_flags = 0xf2; break;
103            default:
104               return 0;
105            }
106         } else {
107            tile_flags = 0xfe;
108         }
109         break;
110      case 32:
111         if (compressed && ms) {
112            switch (ms) {
113               /* This one makes things blurry:
114            case 0: tile_flags = 0xdb; break;
115               */
116            case 1: tile_flags = 0xdd; break;
117            case 2: tile_flags = 0xdf; break;
118            case 3: tile_flags = 0xe4; break;
119            default:
120               return 0;
121            }
122         } else {
123            tile_flags = 0xfe;
124         }
125         break;
126      case 16:
127      case 8:
128         tile_flags = 0xfe;
129         break;
130      default:
131         return 0;
132      }
133      break;
134   }
135
136   return tile_flags;
137}
138
139static inline bool
140nvc0_miptree_init_ms_mode(struct nv50_miptree *mt)
141{
142   switch (mt->base.base.nr_samples) {
143   case 8:
144      mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS8;
145      mt->ms_x = 2;
146      mt->ms_y = 1;
147      break;
148   case 4:
149      mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS4;
150      mt->ms_x = 1;
151      mt->ms_y = 1;
152      break;
153   case 2:
154      mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS2;
155      mt->ms_x = 1;
156      break;
157   case 1:
158   case 0:
159      mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS1;
160      break;
161   default:
162      NOUVEAU_ERR("invalid nr_samples: %u\n", mt->base.base.nr_samples);
163      return false;
164   }
165   return true;
166}
167
168static void
169nvc0_miptree_init_layout_video(struct nv50_miptree *mt)
170{
171   const struct pipe_resource *pt = &mt->base.base;
172   const unsigned blocksize = util_format_get_blocksize(pt->format);
173
174   assert(pt->last_level == 0);
175   assert(mt->ms_x == 0 && mt->ms_y == 0);
176   assert(!util_format_is_compressed(pt->format));
177
178   mt->layout_3d = pt->target == PIPE_TEXTURE_3D;
179
180   mt->level[0].tile_mode = 0x10;
181   mt->level[0].pitch = align(pt->width0 * blocksize, 64);
182   mt->total_size = align(pt->height0, 16) * mt->level[0].pitch * (mt->layout_3d ? pt->depth0 : 1);
183
184   if (pt->array_size > 1) {
185      mt->layer_stride = align(mt->total_size, NVC0_TILE_SIZE(0x10));
186      mt->total_size = mt->layer_stride * pt->array_size;
187   }
188}
189
190static void
191nvc0_miptree_init_layout_tiled(struct nv50_miptree *mt)
192{
193   struct pipe_resource *pt = &mt->base.base;
194   unsigned w, h, d, l;
195   const unsigned blocksize = util_format_get_blocksize(pt->format);
196
197   mt->layout_3d = pt->target == PIPE_TEXTURE_3D;
198
199   w = pt->width0 << mt->ms_x;
200   h = pt->height0 << mt->ms_y;
201
202   /* For 3D textures, a mipmap is spanned by all the layers, for array
203    * textures and cube maps, each layer contains its own mipmaps.
204    */
205   d = mt->layout_3d ? pt->depth0 : 1;
206
207   assert(!mt->ms_mode || !pt->last_level);
208
209   for (l = 0; l <= pt->last_level; ++l) {
210      struct nv50_miptree_level *lvl = &mt->level[l];
211      unsigned tsx, tsy, tsz;
212      unsigned nbx = util_format_get_nblocksx(pt->format, w);
213      unsigned nby = util_format_get_nblocksy(pt->format, h);
214
215      lvl->offset = mt->total_size;
216
217      lvl->tile_mode = nvc0_tex_choose_tile_dims(nbx, nby, d, mt->layout_3d);
218
219      tsx = NVC0_TILE_SIZE_X(lvl->tile_mode); /* x is tile row pitch in bytes */
220      tsy = NVC0_TILE_SIZE_Y(lvl->tile_mode);
221      tsz = NVC0_TILE_SIZE_Z(lvl->tile_mode);
222
223      lvl->pitch = align(nbx * blocksize, tsx);
224
225      mt->total_size += lvl->pitch * align(nby, tsy) * align(d, tsz);
226
227      w = u_minify(w, 1);
228      h = u_minify(h, 1);
229      d = u_minify(d, 1);
230   }
231
232   if (pt->array_size > 1) {
233      mt->layer_stride = align(mt->total_size,
234                               NVC0_TILE_SIZE(mt->level[0].tile_mode));
235      mt->total_size = mt->layer_stride * pt->array_size;
236   }
237}
238
239static uint64_t nvc0_miptree_get_modifier(struct nv50_miptree *mt)
240{
241   union nouveau_bo_config *config = &mt->base.bo->config;
242   uint64_t modifier;
243
244   if (mt->layout_3d)
245      return DRM_FORMAT_MOD_INVALID;
246
247   switch (config->nvc0.memtype) {
248   case 0x00:
249      modifier = DRM_FORMAT_MOD_LINEAR;
250      break;
251
252   case 0xfe:
253      switch (NVC0_TILE_MODE_Y(config->nvc0.tile_mode)) {
254      case 0:
255         modifier = DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_ONE_GOB;
256         break;
257
258      case 1:
259         modifier = DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_TWO_GOB;
260         break;
261
262      case 2:
263         modifier = DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_FOUR_GOB;
264         break;
265
266      case 3:
267         modifier = DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_EIGHT_GOB;
268         break;
269
270      case 4:
271         modifier = DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB;
272         break;
273
274      case 5:
275         modifier = DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB;
276         break;
277
278      default:
279         modifier = DRM_FORMAT_MOD_INVALID;
280         break;
281      }
282      break;
283
284   default:
285      modifier = DRM_FORMAT_MOD_INVALID;
286      break;
287   }
288
289   return modifier;
290}
291
292static boolean
293nvc0_miptree_get_handle(struct pipe_screen *pscreen,
294                        struct pipe_resource *pt,
295                        struct winsys_handle *whandle)
296{
297   struct nv50_miptree *mt = nv50_miptree(pt);
298   boolean ret;
299
300   ret = nv50_miptree_get_handle(pscreen, pt, whandle);
301   if (!ret)
302      return ret;
303
304   whandle->modifier = nvc0_miptree_get_modifier(mt);
305
306   return true;
307}
308
309const struct u_resource_vtbl nvc0_miptree_vtbl =
310{
311   nvc0_miptree_get_handle,         /* get_handle */
312   nv50_miptree_destroy,            /* resource_destroy */
313   nvc0_miptree_transfer_map,       /* transfer_map */
314   u_default_transfer_flush_region, /* transfer_flush_region */
315   nvc0_miptree_transfer_unmap,     /* transfer_unmap */
316};
317
318struct pipe_resource *
319nvc0_miptree_create(struct pipe_screen *pscreen,
320                    const struct pipe_resource *templ,
321                    const uint64_t *modifiers, unsigned int count)
322{
323   struct nouveau_device *dev = nouveau_screen(pscreen)->device;
324   struct nouveau_drm *drm = nouveau_screen(pscreen)->drm;
325   struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree);
326   struct pipe_resource *pt = &mt->base.base;
327   bool compressed = drm->version >= 0x01000101;
328   int ret;
329   union nouveau_bo_config bo_config;
330   uint32_t bo_flags;
331
332   if (!mt)
333      return NULL;
334
335   mt->base.vtbl = &nvc0_miptree_vtbl;
336   *pt = *templ;
337   pipe_reference_init(&pt->reference, 1);
338   pt->screen = pscreen;
339
340   if (pt->usage == PIPE_USAGE_STAGING) {
341      switch (pt->target) {
342      case PIPE_TEXTURE_2D:
343      case PIPE_TEXTURE_RECT:
344         if (pt->last_level == 0 &&
345             !util_format_is_depth_or_stencil(pt->format) &&
346             pt->nr_samples <= 1)
347            pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR;
348         break;
349      default:
350         break;
351      }
352   }
353
354   if (count == 1 && modifiers[0] == DRM_FORMAT_MOD_LINEAR)
355      pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR;
356
357   if (pt->bind & PIPE_BIND_LINEAR)
358      pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR;
359
360   bo_config.nvc0.memtype = nvc0_mt_choose_storage_type(mt, compressed);
361
362   if (!nvc0_miptree_init_ms_mode(mt)) {
363      FREE(mt);
364      return NULL;
365   }
366
367   if (unlikely(pt->flags & NVC0_RESOURCE_FLAG_VIDEO)) {
368      nvc0_miptree_init_layout_video(mt);
369   } else
370   if (likely(bo_config.nvc0.memtype)) {
371      nvc0_miptree_init_layout_tiled(mt);
372   } else
373   if (!nv50_miptree_init_layout_linear(mt, 128)) {
374      FREE(mt);
375      return NULL;
376   }
377   bo_config.nvc0.tile_mode = mt->level[0].tile_mode;
378
379   if (!bo_config.nvc0.memtype && (pt->usage == PIPE_USAGE_STAGING || pt->bind & PIPE_BIND_SHARED))
380      mt->base.domain = NOUVEAU_BO_GART;
381   else
382      mt->base.domain = NV_VRAM_DOMAIN(nouveau_screen(pscreen));
383
384   bo_flags = mt->base.domain | NOUVEAU_BO_NOSNOOP;
385
386   if (mt->base.base.bind & (PIPE_BIND_CURSOR | PIPE_BIND_DISPLAY_TARGET))
387      bo_flags |= NOUVEAU_BO_CONTIG;
388
389   ret = nouveau_bo_new(dev, bo_flags, 4096, mt->total_size, &bo_config,
390                        &mt->base.bo);
391   if (ret) {
392      FREE(mt);
393      return NULL;
394   }
395   mt->base.address = mt->base.bo->offset;
396
397   NOUVEAU_DRV_STAT(nouveau_screen(pscreen), tex_obj_current_count, 1);
398   NOUVEAU_DRV_STAT(nouveau_screen(pscreen), tex_obj_current_bytes,
399                    mt->total_size);
400
401   return pt;
402}
403
404/* Offset of zslice @z from start of level @l. */
405inline unsigned
406nvc0_mt_zslice_offset(const struct nv50_miptree *mt, unsigned l, unsigned z)
407{
408   const struct pipe_resource *pt = &mt->base.base;
409
410   unsigned tds = NVC0_TILE_SHIFT_Z(mt->level[l].tile_mode);
411   unsigned ths = NVC0_TILE_SHIFT_Y(mt->level[l].tile_mode);
412
413   unsigned nby = util_format_get_nblocksy(pt->format,
414                                           u_minify(pt->height0, l));
415
416   /* to next 2D tile slice within a 3D tile */
417   unsigned stride_2d = NVC0_TILE_SIZE_2D(mt->level[l].tile_mode);
418
419   /* to slice in the next (in z direction) 3D tile */
420   unsigned stride_3d = (align(nby, (1 << ths)) * mt->level[l].pitch) << tds;
421
422   return (z & (1 << (tds - 1))) * stride_2d + (z >> tds) * stride_3d;
423}
424
425/* Surface functions.
426 */
427
428struct pipe_surface *
429nvc0_miptree_surface_new(struct pipe_context *pipe,
430                         struct pipe_resource *pt,
431                         const struct pipe_surface *templ)
432{
433   struct nv50_surface *ns = nv50_surface_from_miptree(nv50_miptree(pt), templ);
434   if (!ns)
435      return NULL;
436   ns->base.context = pipe;
437   return &ns->base;
438}
439