- improve handling of DMA address limited systems

- improve GPU hangcheck
 - fix address space collision on >= 4K CPU pages
 - flush all known writeback caches before memory release
 - various code cleanups
 -----BEGIN PGP SIGNATURE-----
 
 iQFLBAABCAA1FiEEz9DmVLy+XdLQMNIiz8wpfG0KEgsFAmctKccXHGwuc3RhY2hA
 cGVuZ3V0cm9uaXguZGUACgkQz8wpfG0KEgs4nAf/Z0F6feOHGwE4g7j+Y1/0XwJH
 xJNzzrcZb3prObsPhDM9YUuDTn+x9O53Rj1HPT9DMtIDrmaAe14VwAG8STQ2Ryij
 bdg627RMv/uHc1bSiFRXsgR+sKHRPnAcyM00UTXHa0fXawTePJQTxFDlzqr6hX2O
 XSCsI1QFW05wjYVq7LXSxoac76gTH0Y5WVumWeBkWQUTM10RHxRnA32yykIC9MAT
 zPA1ZQ/eheZe8H9lq9fC6OomDxx7XdPQ8NR+AhNt74u+77vk3ZfVHRTXlJuwJB3d
 sBYWO7ChyBO/plTPjSKo3iOgaNkM2Si5gF1rX4pWnLsAFpIa2bjoQ7nqhqaIXw==
 =l4RX
 -----END PGP SIGNATURE-----

Merge tag 'drm-etnaviv-next-2024-11-07' of https://git.pengutronix.de/git/lst/linux into drm-next

- improve handling of DMA address limited systems
- improve GPU hangcheck
- fix address space collision on >= 4K CPU pages
- flush all known writeback caches before memory release
- various code cleanups

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Lucas Stach <l.stach@pengutronix.de>
Link: https://patchwork.freedesktop.org/patch/msgid/c84075a0257e7bee222d008fa3118117422d664e.camel@pengutronix.de
This commit is contained in:
Dave Airlie 2024-11-08 12:05:45 +10:00
commit 35a6e15aab
14 changed files with 107 additions and 91 deletions

View File

@ -482,7 +482,8 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, u32 exec_state,
} else {
CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_CACHE,
VIVS_GL_FLUSH_CACHE_DEPTH |
VIVS_GL_FLUSH_CACHE_COLOR);
VIVS_GL_FLUSH_CACHE_COLOR |
VIVS_GL_FLUSH_CACHE_SHADER_L1);
if (has_blt) {
CMD_LOAD_STATE(buffer, VIVS_BLT_ENABLE, 0x1);
CMD_LOAD_STATE(buffer, VIVS_BLT_SET_COMMAND, 0x1);

View File

@ -5,8 +5,6 @@
#include <linux/dma-mapping.h>
#include <drm/drm_mm.h>
#include "etnaviv_cmdbuf.h"
#include "etnaviv_gem.h"
#include "etnaviv_gpu.h"
@ -55,6 +53,7 @@ etnaviv_cmdbuf_suballoc_new(struct device *dev)
return suballoc;
free_suballoc:
mutex_destroy(&suballoc->lock);
kfree(suballoc);
return ERR_PTR(ret);
@ -79,6 +78,7 @@ void etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc)
{
dma_free_wc(suballoc->dev, SUBALLOC_SIZE, suballoc->vaddr,
suballoc->paddr);
mutex_destroy(&suballoc->lock);
kfree(suballoc);
}

View File

@ -538,6 +538,16 @@ static int etnaviv_bind(struct device *dev)
priv->num_gpus = 0;
priv->shm_gfp_mask = GFP_HIGHUSER | __GFP_RETRY_MAYFAIL | __GFP_NOWARN;
/*
* If the GPU is part of a system with DMA addressing limitations,
* request pages for our SHM backend buffers from the DMA32 zone to
* hopefully avoid performance killing SWIOTLB bounce buffering.
*/
if (dma_addressing_limited(dev)) {
priv->shm_gfp_mask |= GFP_DMA32;
priv->shm_gfp_mask &= ~__GFP_HIGHMEM;
}
priv->cmdbuf_suballoc = etnaviv_cmdbuf_suballoc_new(drm->dev);
if (IS_ERR(priv->cmdbuf_suballoc)) {
dev_err(drm->dev, "Failed to create cmdbuf suballocator\n");
@ -564,6 +574,7 @@ static int etnaviv_bind(struct device *dev)
out_destroy_suballoc:
etnaviv_cmdbuf_suballoc_destroy(priv->cmdbuf_suballoc);
out_free_priv:
mutex_destroy(&priv->gem_lock);
kfree(priv);
out_put:
drm_dev_put(drm);
@ -608,7 +619,7 @@ static int etnaviv_pdev_probe(struct platform_device *pdev)
if (!of_device_is_available(core_node))
continue;
drm_of_component_match_add(&pdev->dev, &match,
drm_of_component_match_add(dev, &match,
component_compare_of, core_node);
}
} else {
@ -631,9 +642,9 @@ static int etnaviv_pdev_probe(struct platform_device *pdev)
* bit to make sure we are allocating the command buffers and
* TLBs in the lower 4 GiB address space.
*/
if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)) ||
dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32))) {
dev_dbg(&pdev->dev, "No suitable DMA available\n");
if (dma_set_mask(dev, DMA_BIT_MASK(40)) ||
dma_set_coherent_mask(dev, DMA_BIT_MASK(32))) {
dev_dbg(dev, "No suitable DMA available\n");
return -ENODEV;
}
@ -644,7 +655,7 @@ static int etnaviv_pdev_probe(struct platform_device *pdev)
*/
first_node = etnaviv_of_first_available_node();
if (first_node) {
of_dma_configure(&pdev->dev, first_node, true);
of_dma_configure(dev, first_node, true);
of_node_put(first_node);
}

View File

@ -514,6 +514,7 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj)
etnaviv_obj->ops->release(etnaviv_obj);
drm_gem_object_release(obj);
mutex_destroy(&etnaviv_obj->lock);
kfree(etnaviv_obj);
}
@ -543,7 +544,7 @@ static const struct drm_gem_object_funcs etnaviv_gem_object_funcs = {
.vm_ops = &vm_ops,
};
static int etnaviv_gem_new_impl(struct drm_device *dev, u32 flags,
static int etnaviv_gem_new_impl(struct drm_device *dev, u32 size, u32 flags,
const struct etnaviv_gem_ops *ops, struct drm_gem_object **obj)
{
struct etnaviv_gem_object *etnaviv_obj;
@ -570,6 +571,7 @@ static int etnaviv_gem_new_impl(struct drm_device *dev, u32 flags,
if (!etnaviv_obj)
return -ENOMEM;
etnaviv_obj->size = ALIGN(size, SZ_4K);
etnaviv_obj->flags = flags;
etnaviv_obj->ops = ops;
@ -590,15 +592,13 @@ int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
struct drm_gem_object *obj = NULL;
int ret;
size = PAGE_ALIGN(size);
ret = etnaviv_gem_new_impl(dev, flags, &etnaviv_gem_shmem_ops, &obj);
ret = etnaviv_gem_new_impl(dev, size, flags, &etnaviv_gem_shmem_ops, &obj);
if (ret)
goto fail;
lockdep_set_class(&to_etnaviv_bo(obj)->lock, &etnaviv_shm_lock_class);
ret = drm_gem_object_init(dev, obj, size);
ret = drm_gem_object_init(dev, obj, PAGE_ALIGN(size));
if (ret)
goto fail;
@ -627,7 +627,7 @@ int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags,
struct drm_gem_object *obj;
int ret;
ret = etnaviv_gem_new_impl(dev, flags, ops, &obj);
ret = etnaviv_gem_new_impl(dev, size, flags, ops, &obj);
if (ret)
return ret;
@ -686,7 +686,7 @@ static void etnaviv_gem_userptr_release(struct etnaviv_gem_object *etnaviv_obj)
kfree(etnaviv_obj->sgt);
}
if (etnaviv_obj->pages) {
int npages = etnaviv_obj->base.size >> PAGE_SHIFT;
unsigned int npages = etnaviv_obj->base.size >> PAGE_SHIFT;
unpin_user_pages(etnaviv_obj->pages, npages);
kvfree(etnaviv_obj->pages);

View File

@ -36,6 +36,11 @@ struct etnaviv_gem_object {
const struct etnaviv_gem_ops *ops;
struct mutex lock;
/*
* The actual size that is visible to the GPU, not necessarily
* PAGE_SIZE aligned, but should be aligned to GPU page size.
*/
u32 size;
u32 flags;
struct list_head gem_node;

View File

@ -17,7 +17,7 @@ static struct lock_class_key etnaviv_prime_lock_class;
struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj)
{
struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
int npages = obj->size >> PAGE_SHIFT;
unsigned int npages = obj->size >> PAGE_SHIFT;
if (WARN_ON(!etnaviv_obj->pages)) /* should have already pinned! */
return ERR_PTR(-EINVAL);

View File

@ -6,7 +6,6 @@
#include <drm/drm_file.h>
#include <linux/dma-fence-array.h>
#include <linux/file.h>
#include <linux/pm_runtime.h>
#include <linux/dma-resv.h>
#include <linux/sync_file.h>
#include <linux/uaccess.h>

View File

@ -574,8 +574,8 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
continue;
}
/* disable debug registers, as they are not normally needed */
control |= VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS;
/* enable debug register access */
control &= ~VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS;
gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
failed = false;
@ -839,17 +839,8 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
if (ret)
goto fail;
/*
* If the GPU is part of a system with DMA addressing limitations,
* request pages for our SHM backend buffers from the DMA32 zone to
* hopefully avoid performance killing SWIOTLB bounce buffering.
*/
if (dma_addressing_limited(gpu->dev))
priv->shm_gfp_mask |= GFP_DMA32;
/* Create buffer: */
ret = etnaviv_cmdbuf_init(priv->cmdbuf_suballoc, &gpu->buffer,
PAGE_SIZE);
ret = etnaviv_cmdbuf_init(priv->cmdbuf_suballoc, &gpu->buffer, SZ_4K);
if (ret) {
dev_err(gpu->dev, "could not create command buffer\n");
goto fail;
@ -1330,17 +1321,16 @@ static void sync_point_perfmon_sample_pre(struct etnaviv_gpu *gpu,
{
u32 val;
mutex_lock(&gpu->lock);
/* disable clock gating */
val = gpu_read_power(gpu, VIVS_PM_POWER_CONTROLS);
val &= ~VIVS_PM_POWER_CONTROLS_ENABLE_MODULE_CLOCK_GATING;
gpu_write_power(gpu, VIVS_PM_POWER_CONTROLS, val);
/* enable debug register */
val = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
val &= ~VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS;
gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, val);
sync_point_perfmon_sample(gpu, event, ETNA_PM_PROCESS_PRE);
mutex_unlock(&gpu->lock);
}
static void sync_point_perfmon_sample_post(struct etnaviv_gpu *gpu,
@ -1350,23 +1340,22 @@ static void sync_point_perfmon_sample_post(struct etnaviv_gpu *gpu,
unsigned int i;
u32 val;
mutex_lock(&gpu->lock);
sync_point_perfmon_sample(gpu, event, ETNA_PM_PROCESS_POST);
/* enable clock gating */
val = gpu_read_power(gpu, VIVS_PM_POWER_CONTROLS);
val |= VIVS_PM_POWER_CONTROLS_ENABLE_MODULE_CLOCK_GATING;
gpu_write_power(gpu, VIVS_PM_POWER_CONTROLS, val);
mutex_unlock(&gpu->lock);
for (i = 0; i < submit->nr_pmrs; i++) {
const struct etnaviv_perfmon_request *pmr = submit->pmrs + i;
*pmr->bo_vma = pmr->sequence;
}
/* disable debug register */
val = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
val |= VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS;
gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, val);
/* enable clock gating */
val = gpu_read_power(gpu, VIVS_PM_POWER_CONTROLS);
val |= VIVS_PM_POWER_CONTROLS_ENABLE_MODULE_CLOCK_GATING;
gpu_write_power(gpu, VIVS_PM_POWER_CONTROLS, val);
}
@ -1862,7 +1851,7 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
if (!gpu)
return -ENOMEM;
gpu->dev = &pdev->dev;
gpu->dev = dev;
mutex_init(&gpu->lock);
mutex_init(&gpu->sched_lock);
@ -1876,8 +1865,8 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
if (gpu->irq < 0)
return gpu->irq;
err = devm_request_irq(&pdev->dev, gpu->irq, irq_handler, 0,
dev_name(gpu->dev), gpu);
err = devm_request_irq(dev, gpu->irq, irq_handler, 0,
dev_name(dev), gpu);
if (err) {
dev_err(dev, "failed to request IRQ%u: %d\n", gpu->irq, err);
return err;
@ -1914,13 +1903,13 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
* autosuspend delay is rather arbitary: no measurements have
* yet been performed to determine an appropriate value.
*/
pm_runtime_use_autosuspend(gpu->dev);
pm_runtime_set_autosuspend_delay(gpu->dev, 200);
pm_runtime_enable(gpu->dev);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_autosuspend_delay(dev, 200);
pm_runtime_enable(dev);
err = component_add(&pdev->dev, &gpu_ops);
err = component_add(dev, &gpu_ops);
if (err < 0) {
dev_err(&pdev->dev, "failed to register component: %d\n", err);
dev_err(dev, "failed to register component: %d\n", err);
return err;
}
@ -1929,8 +1918,13 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
static void etnaviv_gpu_platform_remove(struct platform_device *pdev)
{
struct etnaviv_gpu *gpu = dev_get_drvdata(&pdev->dev);
component_del(&pdev->dev, &gpu_ops);
pm_runtime_disable(&pdev->dev);
mutex_destroy(&gpu->lock);
mutex_destroy(&gpu->sched_lock);
}
static int etnaviv_gpu_rpm_suspend(struct device *dev)

View File

@ -144,6 +144,7 @@ struct etnaviv_gpu {
/* hang detection */
u32 hangcheck_dma_addr;
u32 hangcheck_primid;
u32 hangcheck_fence;
void __iomem *mmio;

View File

@ -69,9 +69,11 @@ static int etnaviv_context_map(struct etnaviv_iommu_context *context,
return ret;
}
static int etnaviv_iommu_map(struct etnaviv_iommu_context *context, u32 iova,
static int etnaviv_iommu_map(struct etnaviv_iommu_context *context,
u32 iova, unsigned int va_len,
struct sg_table *sgt, int prot)
{ struct scatterlist *sg;
{
struct scatterlist *sg;
unsigned int da = iova;
unsigned int i;
int ret;
@ -81,14 +83,16 @@ static int etnaviv_iommu_map(struct etnaviv_iommu_context *context, u32 iova,
for_each_sgtable_dma_sg(sgt, sg, i) {
phys_addr_t pa = sg_dma_address(sg) - sg->offset;
size_t bytes = sg_dma_len(sg) + sg->offset;
unsigned int da_len = sg_dma_len(sg) + sg->offset;
unsigned int bytes = min_t(unsigned int, da_len, va_len);
VERB("map[%d]: %08x %pap(%zx)", i, iova, &pa, bytes);
VERB("map[%d]: %08x %pap(%x)", i, iova, &pa, bytes);
ret = etnaviv_context_map(context, da, pa, bytes, prot);
if (ret)
goto fail;
va_len -= bytes;
da += bytes;
}
@ -104,21 +108,7 @@ static int etnaviv_iommu_map(struct etnaviv_iommu_context *context, u32 iova,
static void etnaviv_iommu_unmap(struct etnaviv_iommu_context *context, u32 iova,
struct sg_table *sgt, unsigned len)
{
struct scatterlist *sg;
unsigned int da = iova;
int i;
for_each_sgtable_dma_sg(sgt, sg, i) {
size_t bytes = sg_dma_len(sg) + sg->offset;
etnaviv_context_unmap(context, da, bytes);
VERB("unmap[%d]: %08x(%zx)", i, iova, bytes);
BUG_ON(!PAGE_ALIGNED(bytes));
da += bytes;
}
etnaviv_context_unmap(context, iova, len);
context->flush_seq++;
}
@ -131,7 +121,7 @@ static void etnaviv_iommu_remove_mapping(struct etnaviv_iommu_context *context,
lockdep_assert_held(&context->lock);
etnaviv_iommu_unmap(context, mapping->vram_node.start,
etnaviv_obj->sgt, etnaviv_obj->base.size);
etnaviv_obj->sgt, etnaviv_obj->size);
drm_mm_remove_node(&mapping->vram_node);
}
@ -305,16 +295,14 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu_context *context,
node = &mapping->vram_node;
if (va)
ret = etnaviv_iommu_insert_exact(context, node,
etnaviv_obj->base.size, va);
ret = etnaviv_iommu_insert_exact(context, node, etnaviv_obj->size, va);
else
ret = etnaviv_iommu_find_iova(context, node,
etnaviv_obj->base.size);
ret = etnaviv_iommu_find_iova(context, node, etnaviv_obj->size);
if (ret < 0)
goto unlock;
mapping->iova = node->start;
ret = etnaviv_iommu_map(context, node->start, sgt,
ret = etnaviv_iommu_map(context, node->start, etnaviv_obj->size, sgt,
ETNAVIV_PROT_READ | ETNAVIV_PROT_WRITE);
if (ret < 0) {
@ -358,7 +346,7 @@ static void etnaviv_iommu_context_free(struct kref *kref)
container_of(kref, struct etnaviv_iommu_context, refcount);
etnaviv_cmdbuf_suballoc_unmap(context, &context->cmdbuf_mapping);
mutex_destroy(&context->lock);
context->global->ops->free(context);
}
void etnaviv_iommu_context_put(struct etnaviv_iommu_context *context)

View File

@ -61,7 +61,6 @@ struct etnaviv_iommu_global {
/* P(age) T(able) A(rray) */
u64 *pta_cpu;
dma_addr_t pta_dma;
struct spinlock pta_lock;
DECLARE_BITMAP(pta_alloc, ETNAVIV_PTA_ENTRIES);
} v2;
};

View File

@ -62,6 +62,8 @@ static u32 pipe_perf_reg_read(struct etnaviv_gpu *gpu,
u32 value = 0;
unsigned i;
lockdep_assert_held(&gpu->lock);
for (i = 0; i < gpu->identity.pixel_pipes; i++) {
pipe_select(gpu, clock, i);
value += perf_reg_read(gpu, domain, signal);
@ -81,6 +83,8 @@ static u32 pipe_reg_read(struct etnaviv_gpu *gpu,
u32 value = 0;
unsigned i;
lockdep_assert_held(&gpu->lock);
for (i = 0; i < gpu->identity.pixel_pipes; i++) {
pipe_select(gpu, clock, i);
value += gpu_read(gpu, signal->data);

View File

@ -11,6 +11,7 @@
#include "etnaviv_gpu.h"
#include "etnaviv_sched.h"
#include "state.xml.h"
#include "state_hi.xml.h"
static int etnaviv_job_hang_limit = 0;
module_param_named(job_hang_limit, etnaviv_job_hang_limit, int , 0444);
@ -35,7 +36,7 @@ static enum drm_gpu_sched_stat etnaviv_sched_timedout_job(struct drm_sched_job
{
struct etnaviv_gem_submit *submit = to_etnaviv_submit(sched_job);
struct etnaviv_gpu *gpu = submit->gpu;
u32 dma_addr;
u32 dma_addr, primid = 0;
int change;
/*
@ -52,10 +53,22 @@ static enum drm_gpu_sched_stat etnaviv_sched_timedout_job(struct drm_sched_job
*/
dma_addr = gpu_read(gpu, VIVS_FE_DMA_ADDRESS);
change = dma_addr - gpu->hangcheck_dma_addr;
if (submit->exec_state == ETNA_PIPE_3D) {
/* guard against concurrent usage from perfmon_sample */
mutex_lock(&gpu->lock);
gpu_write(gpu, VIVS_MC_PROFILE_CONFIG0,
VIVS_MC_PROFILE_CONFIG0_FE_CURRENT_PRIM <<
VIVS_MC_PROFILE_CONFIG0_FE__SHIFT);
primid = gpu_read(gpu, VIVS_MC_PROFILE_FE_READ);
mutex_unlock(&gpu->lock);
}
if (gpu->state == ETNA_GPU_STATE_RUNNING &&
(gpu->completed_fence != gpu->hangcheck_fence ||
change < 0 || change > 16)) {
change < 0 || change > 16 ||
(submit->exec_state == ETNA_PIPE_3D &&
gpu->hangcheck_primid != primid))) {
gpu->hangcheck_dma_addr = dma_addr;
gpu->hangcheck_primid = primid;
gpu->hangcheck_fence = gpu->completed_fence;
goto out_no_timeout;
}

View File

@ -8,17 +8,17 @@ This file was generated by the rules-ng-ng headergen tool in this git repository
git clone git://0x04.net/rules-ng-ng
The rules-ng-ng source files this header was generated from are:
- state.xml ( 29355 bytes, from 2024-01-19 10:18:54)
- common.xml ( 35664 bytes, from 2023-12-06 10:55:32)
- common_3d.xml ( 15069 bytes, from 2023-11-22 10:05:24)
- state_hi.xml ( 35854 bytes, from 2023-12-11 15:50:17)
- copyright.xml ( 1597 bytes, from 2016-11-10 13:58:32)
- state_2d.xml ( 52271 bytes, from 2023-06-02 12:35:03)
- state_3d.xml ( 89522 bytes, from 2024-01-19 10:18:54)
- state_blt.xml ( 14592 bytes, from 2023-11-22 10:05:09)
- state_vg.xml ( 5975 bytes, from 2016-11-10 13:58:32)
- state.xml ( 30729 bytes, from 2024-06-21 11:31:54)
- common.xml ( 35664 bytes, from 2023-12-13 09:33:18)
- common_3d.xml ( 15069 bytes, from 2023-12-13 09:33:18)
- state_hi.xml ( 35909 bytes, from 2024-06-21 11:31:54)
- copyright.xml ( 1597 bytes, from 2020-10-28 12:56:03)
- state_2d.xml ( 52271 bytes, from 2023-05-30 20:50:02)
- state_3d.xml ( 89626 bytes, from 2024-06-21 11:32:57)
- state_blt.xml ( 14592 bytes, from 2023-12-13 09:33:18)
- state_vg.xml ( 5975 bytes, from 2020-10-28 12:56:03)
Copyright (C) 2012-2023 by the following authors:
Copyright (C) 2012-2024 by the following authors:
- Wladimir J. van der Laan <laanwj@gmail.com>
- Christian Gmeiner <christian.gmeiner@gmail.com>
- Lucas Stach <l.stach@pengutronix.de>
@ -467,6 +467,7 @@ DEALINGS IN THE SOFTWARE.
#define VIVS_MC_PROFILE_CONFIG0 0x00000470
#define VIVS_MC_PROFILE_CONFIG0_FE__MASK 0x000000ff
#define VIVS_MC_PROFILE_CONFIG0_FE__SHIFT 0
#define VIVS_MC_PROFILE_CONFIG0_FE_CURRENT_PRIM 0x00000009
#define VIVS_MC_PROFILE_CONFIG0_FE_DRAW_COUNT 0x0000000a
#define VIVS_MC_PROFILE_CONFIG0_FE_OUT_VERTEX_COUNT 0x0000000b
#define VIVS_MC_PROFILE_CONFIG0_FE_CACHE_MISS_COUNT 0x0000000c