In the etnaviv_gem_vmap_impl(), update the page property from writecombine to PAGE_KERNEL on cached mapping. Previously, it use writecombine page property to vmap cached buffer unconditionally. Many modern CPUs choose to define the peripheral devices as DMA coherent by default, to be specific, the peripheral devices are capable of snooping CPU's cache. Therefore, cached buffers should be accessed with cached mapping.
While at it, probe cached coherent support at the host platform with the dev_is_dma_coherent() function. This allows userspace to query on the runtime, which avoid compile-time macros (#ifdefs). Modern CPUs choose to define the peripheral devices as DMA coherent by default, In other words, the peripheral devices are capable of snooping CPU's cache. This means that device drivers do not need to maintain the coherency issue between a processor and an peripheral I/O for the cached mapping buffers. Such a hardware feature is implementation-defined by host CPU platform, not the vivante GPU IP core itself. X86-64, LoongArch and Loongson Mips CPU and some ARM64 CPU has the hardware maintain cache coherency, but ARM CPU is not. So provide mechanism to let userspace know. Signed-off-by: Sui Jingfeng <sui.jingf...@linux.dev> --- drivers/gpu/drm/etnaviv/etnaviv_drv.c | 3 +++ drivers/gpu/drm/etnaviv/etnaviv_drv.h | 8 ++++++++ drivers/gpu/drm/etnaviv/etnaviv_gem.c | 16 ++++++++++++++-- drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 4 ++++ include/uapi/drm/etnaviv_drm.h | 1 + 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 5ba2b3a386b3..e3a05b8b9330 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -5,6 +5,7 @@ #include <linux/component.h> #include <linux/dma-mapping.h> +#include <linux/dma-map-ops.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> @@ -57,6 +58,8 @@ static int etnaviv_private_init(struct device *dev, return -ENOMEM; } + priv->cached_coherent = dev_is_dma_coherent(dev); + return 0; } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index 1f9b50b5a6aa..9c05f503747a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -46,6 +46,14 @@ struct etnaviv_drm_private { struct xarray active_contexts; u32 next_context_id; + /* + * If true, the cached mapping is consistent for all CPU cores and + * peripheral bus masters in the system. It means that vboth of the + * CPU and GPU will see the same data if the buffer being access is + * cached. And coherency is guaranteed by the arch specific hardware. + */ + bool cached_coherent; + /* list of GEM objects: */ struct mutex gem_lock; struct list_head gem_list; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index aa95a5e98374..eed98bb9e446 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -342,6 +342,7 @@ void *etnaviv_gem_vmap(struct drm_gem_object *obj) static void *etnaviv_gem_vmap_impl(struct etnaviv_gem_object *obj) { struct page **pages; + pgprot_t prot; lockdep_assert_held(&obj->lock); @@ -349,8 +350,19 @@ static void *etnaviv_gem_vmap_impl(struct etnaviv_gem_object *obj) if (IS_ERR(pages)) return NULL; - return vmap(pages, obj->base.size >> PAGE_SHIFT, - VM_MAP, pgprot_writecombine(PAGE_KERNEL)); + switch (obj->flags) { + case ETNA_BO_CACHED: + prot = PAGE_KERNEL; + break; + case ETNA_BO_UNCACHED: + prot = pgprot_noncached(PAGE_KERNEL); + break; + case ETNA_BO_WC: + default: + prot = pgprot_writecombine(PAGE_KERNEL); + } + + return vmap(pages, obj->base.size >> PAGE_SHIFT, VM_MAP, prot); } static inline enum dma_data_direction etnaviv_op_to_dma_dir(u32 op) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index a407c2c9e140..306973660653 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -184,6 +184,10 @@ int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value) *value = gpu->identity.axi_sram_size; break; + case ETNAVIV_PARAM_CACHED_COHERENT: + *value = priv->cached_coherent; + break; + default: DBG("%s: invalid param: %u", dev_name(gpu->dev), param); return -EINVAL; diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h index d87410a8443a..7c5d988a5b9f 100644 --- a/include/uapi/drm/etnaviv_drm.h +++ b/include/uapi/drm/etnaviv_drm.h @@ -82,6 +82,7 @@ struct drm_etnaviv_timespec { #define ETNAVIV_PARAM_GPU_TP_CORE_COUNT 0x21 #define ETNAVIV_PARAM_GPU_ON_CHIP_SRAM_SIZE 0x22 #define ETNAVIV_PARAM_GPU_AXI_SRAM_SIZE 0x23 +#define ETNAVIV_PARAM_CACHED_COHERENT 0x24 #define ETNA_MAX_PIPES 4 -- 2.34.1