* [PATCH v4 02/13] dma-direct: use DMA_ATTR_CC_SHARED in alloc/free paths
From: Aneesh Kumar K.V (Arm) @ 2026-05-12 9:03 UTC (permalink / raw)
To: iommu, linux-arm-kernel, linux-kernel, linux-coco
Cc: Aneesh Kumar K.V (Arm), Robin Murphy, Marek Szyprowski,
Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
Catalin Marinas, Jiri Pirko, Jason Gunthorpe, Mostafa Saleh,
Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260512090408.794195-1-aneesh.kumar@kernel.org>
Propagate force_dma_unencrypted() into DMA_ATTR_CC_SHARED in the
dma-direct allocation path and use the attribute to drive the related
decisions.
This updates dma_direct_alloc(), dma_direct_free(), and
dma_direct_alloc_pages() to fold the forced unencrypted case into attrs.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 44 ++++++++++++++++++++++++++++++++++++--------
1 file changed, 36 insertions(+), 8 deletions(-)
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index b958f150718a..0c2e1f8436ce 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -201,16 +201,31 @@ void *dma_direct_alloc(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
{
bool remap = false, set_uncached = false;
- bool mark_mem_decrypt = true;
+ bool mark_mem_decrypt = false;
struct page *page;
void *ret;
+ /*
+ * DMA_ATTR_CC_SHARED is not a caller-visible dma_alloc_*()
+ * attribute. The direct allocator uses it internally after it has
+ * decided that the backing pages must be shared/decrypted, so the
+ * rest of the allocation path can consistently select DMA addresses,
+ * choose compatible pools and restore encryption on free.
+ */
+ if (attrs & DMA_ATTR_CC_SHARED)
+ return NULL;
+
+ if (force_dma_unencrypted(dev)) {
+ attrs |= DMA_ATTR_CC_SHARED;
+ mark_mem_decrypt = true;
+ }
+
size = PAGE_ALIGN(size);
if (attrs & DMA_ATTR_NO_WARN)
gfp |= __GFP_NOWARN;
- if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) &&
- !force_dma_unencrypted(dev) && !is_swiotlb_for_alloc(dev))
+ if (((attrs & (DMA_ATTR_NO_KERNEL_MAPPING | DMA_ATTR_CC_SHARED)) ==
+ DMA_ATTR_NO_KERNEL_MAPPING) && !is_swiotlb_for_alloc(dev))
return dma_direct_alloc_no_mapping(dev, size, dma_handle, gfp);
if (!dev_is_dma_coherent(dev)) {
@@ -244,7 +259,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
* Remapping or decrypting memory may block, allocate the memory from
* the atomic pools instead if we aren't allowed block.
*/
- if ((remap || force_dma_unencrypted(dev)) &&
+ if ((remap || (attrs & DMA_ATTR_CC_SHARED)) &&
dma_direct_use_pool(dev, gfp))
return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);
@@ -318,11 +333,20 @@ void *dma_direct_alloc(struct device *dev, size_t size,
void dma_direct_free(struct device *dev, size_t size,
void *cpu_addr, dma_addr_t dma_addr, unsigned long attrs)
{
- bool mark_mem_encrypted = true;
+ bool mark_mem_encrypted = false;
unsigned int page_order = get_order(size);
- if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) &&
- !force_dma_unencrypted(dev) && !is_swiotlb_for_alloc(dev)) {
+ /*
+ * if the device had requested for an unencrypted buffer,
+ * convert it to encrypted on free
+ */
+ if (force_dma_unencrypted(dev)) {
+ attrs |= DMA_ATTR_CC_SHARED;
+ mark_mem_encrypted = true;
+ }
+
+ if (((attrs & (DMA_ATTR_NO_KERNEL_MAPPING | DMA_ATTR_CC_SHARED)) ==
+ DMA_ATTR_NO_KERNEL_MAPPING) && !is_swiotlb_for_alloc(dev)) {
/* cpu_addr is a struct page cookie, not a kernel address */
dma_free_contiguous(dev, cpu_addr, size);
return;
@@ -365,10 +389,14 @@ void dma_direct_free(struct device *dev, size_t size,
struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)
{
+ unsigned long attrs = 0;
struct page *page;
void *ret;
- if (force_dma_unencrypted(dev) && dma_direct_use_pool(dev, gfp))
+ if (force_dma_unencrypted(dev))
+ attrs |= DMA_ATTR_CC_SHARED;
+
+ if ((attrs & DMA_ATTR_CC_SHARED) && dma_direct_use_pool(dev, gfp))
return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);
if (is_swiotlb_for_alloc(dev)) {
--
2.43.0
^ permalink raw reply related
* [PATCH v4 03/13] dma-pool: track decrypted atomic pools and select them via attrs
From: Aneesh Kumar K.V (Arm) @ 2026-05-12 9:03 UTC (permalink / raw)
To: iommu, linux-arm-kernel, linux-kernel, linux-coco
Cc: Aneesh Kumar K.V (Arm), Robin Murphy, Marek Szyprowski,
Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
Catalin Marinas, Jiri Pirko, Jason Gunthorpe, Mostafa Saleh,
Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260512090408.794195-1-aneesh.kumar@kernel.org>
Teach the atomic DMA pool code to distinguish between encrypted and
decrypted pools, and make pool allocation select the matching pool based
on DMA attributes.
Introduce a dma_gen_pool wrapper that records whether a pool is
decrypted, initialize that state when the atomic pools are created, and
use it when expanding and resizing the pools. Update dma_alloc_from_pool()
to take attrs and skip pools whose encrypted/decrypted state does not
match DMA_ATTR_CC_SHARED. Update dma_free_from_pool() accordingly.
Also pass DMA_ATTR_CC_SHARED from the swiotlb atomic allocation path
so decrypted swiotlb allocations are taken from the correct atomic pool.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/iommu/dma-iommu.c | 2 +-
include/linux/dma-map-ops.h | 2 +-
kernel/dma/direct.c | 11 ++-
kernel/dma/pool.c | 163 +++++++++++++++++++++++-------------
kernel/dma/swiotlb.c | 7 +-
5 files changed, 122 insertions(+), 63 deletions(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 54d96e847f16..c2595bee3d41 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -1673,7 +1673,7 @@ void *iommu_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
if (IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
!gfpflags_allow_blocking(gfp) && !coherent)
page = dma_alloc_from_pool(dev, PAGE_ALIGN(size), &cpu_addr,
- gfp, NULL);
+ gfp, attrs, NULL);
else
cpu_addr = iommu_dma_alloc_pages(dev, size, &page, gfp, attrs);
if (!cpu_addr)
diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h
index 6a1832a73cad..696b2c3a2305 100644
--- a/include/linux/dma-map-ops.h
+++ b/include/linux/dma-map-ops.h
@@ -212,7 +212,7 @@ void *dma_common_pages_remap(struct page **pages, size_t size, pgprot_t prot,
void dma_common_free_remap(void *cpu_addr, size_t size);
struct page *dma_alloc_from_pool(struct device *dev, size_t size,
- void **cpu_addr, gfp_t flags,
+ void **cpu_addr, gfp_t flags, unsigned long attrs,
bool (*phys_addr_ok)(struct device *, phys_addr_t, size_t));
bool dma_free_from_pool(struct device *dev, void *start, size_t size);
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 0c2e1f8436ce..dc2907439b3d 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -162,7 +162,7 @@ static bool dma_direct_use_pool(struct device *dev, gfp_t gfp)
}
static void *dma_direct_alloc_from_pool(struct device *dev, size_t size,
- dma_addr_t *dma_handle, gfp_t gfp)
+ dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
{
struct page *page;
u64 phys_limit;
@@ -172,7 +172,8 @@ static void *dma_direct_alloc_from_pool(struct device *dev, size_t size,
return NULL;
gfp |= dma_direct_optimal_gfp_mask(dev, &phys_limit);
- page = dma_alloc_from_pool(dev, size, &ret, gfp, dma_coherent_ok);
+ page = dma_alloc_from_pool(dev, size, &ret, gfp, attrs,
+ dma_coherent_ok);
if (!page)
return NULL;
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
@@ -261,7 +262,8 @@ void *dma_direct_alloc(struct device *dev, size_t size,
*/
if ((remap || (attrs & DMA_ATTR_CC_SHARED)) &&
dma_direct_use_pool(dev, gfp))
- return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);
+ return dma_direct_alloc_from_pool(dev, size, dma_handle,
+ gfp, attrs);
if (is_swiotlb_for_alloc(dev)) {
page = dma_direct_alloc_swiotlb(dev, size);
@@ -397,7 +399,8 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
attrs |= DMA_ATTR_CC_SHARED;
if ((attrs & DMA_ATTR_CC_SHARED) && dma_direct_use_pool(dev, gfp))
- return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);
+ return dma_direct_alloc_from_pool(dev, size, dma_handle,
+ gfp, attrs);
if (is_swiotlb_for_alloc(dev)) {
page = dma_direct_alloc_swiotlb(dev, size);
diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c
index 2b2fbb709242..75f0eba48a23 100644
--- a/kernel/dma/pool.c
+++ b/kernel/dma/pool.c
@@ -12,12 +12,18 @@
#include <linux/set_memory.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include <linux/cc_platform.h>
-static struct gen_pool *atomic_pool_dma __ro_after_init;
+struct dma_gen_pool {
+ bool unencrypted;
+ struct gen_pool *pool;
+};
+
+static struct dma_gen_pool atomic_pool_dma __ro_after_init;
static unsigned long pool_size_dma;
-static struct gen_pool *atomic_pool_dma32 __ro_after_init;
+static struct dma_gen_pool atomic_pool_dma32 __ro_after_init;
static unsigned long pool_size_dma32;
-static struct gen_pool *atomic_pool_kernel __ro_after_init;
+static struct dma_gen_pool atomic_pool_kernel __ro_after_init;
static unsigned long pool_size_kernel;
/* Size can be defined by the coherent_pool command line */
@@ -76,7 +82,7 @@ static bool cma_in_zone(gfp_t gfp)
return true;
}
-static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
+static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
gfp_t gfp)
{
unsigned int order;
@@ -113,12 +119,15 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
* Memory in the atomic DMA pools must be unencrypted, the pools do not
* shrink so no re-encryption occurs in dma_direct_free().
*/
- ret = set_memory_decrypted((unsigned long)page_to_virt(page),
+ if (dma_pool->unencrypted) {
+ ret = set_memory_decrypted((unsigned long)page_to_virt(page),
1 << order);
- if (ret)
- goto remove_mapping;
- ret = gen_pool_add_virt(pool, (unsigned long)addr, page_to_phys(page),
- pool_size, NUMA_NO_NODE);
+ if (ret)
+ goto remove_mapping;
+ }
+
+ ret = gen_pool_add_virt(dma_pool->pool, (unsigned long)addr,
+ page_to_phys(page), pool_size, NUMA_NO_NODE);
if (ret)
goto encrypt_mapping;
@@ -126,11 +135,15 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
return 0;
encrypt_mapping:
- ret = set_memory_encrypted((unsigned long)page_to_virt(page),
- 1 << order);
- if (WARN_ON_ONCE(ret)) {
- /* Decrypt succeeded but encrypt failed, purposely leak */
- goto out;
+ if (dma_pool->unencrypted) {
+ int rc;
+
+ rc = set_memory_encrypted((unsigned long)page_to_virt(page),
+ 1 << order);
+ if (WARN_ON_ONCE(rc)) {
+ /* Decrypt succeeded but encrypt failed, purposely leak */
+ goto out;
+ }
}
remove_mapping:
#ifdef CONFIG_DMA_DIRECT_REMAP
@@ -142,46 +155,52 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
return ret;
}
-static void atomic_pool_resize(struct gen_pool *pool, gfp_t gfp)
+static void atomic_pool_resize(struct dma_gen_pool *dma_pool, gfp_t gfp)
{
- if (pool && gen_pool_avail(pool) < atomic_pool_size)
- atomic_pool_expand(pool, gen_pool_size(pool), gfp);
+ if (dma_pool->pool && gen_pool_avail(dma_pool->pool) < atomic_pool_size)
+ atomic_pool_expand(dma_pool, gen_pool_size(dma_pool->pool), gfp);
}
static void atomic_pool_work_fn(struct work_struct *work)
{
if (IS_ENABLED(CONFIG_ZONE_DMA))
- atomic_pool_resize(atomic_pool_dma,
+ atomic_pool_resize(&atomic_pool_dma,
GFP_KERNEL | GFP_DMA);
if (IS_ENABLED(CONFIG_ZONE_DMA32))
- atomic_pool_resize(atomic_pool_dma32,
+ atomic_pool_resize(&atomic_pool_dma32,
GFP_KERNEL | GFP_DMA32);
- atomic_pool_resize(atomic_pool_kernel, GFP_KERNEL);
+ atomic_pool_resize(&atomic_pool_kernel, GFP_KERNEL);
}
-static __init struct gen_pool *__dma_atomic_pool_init(size_t pool_size,
- gfp_t gfp)
+static __init struct dma_gen_pool *__dma_atomic_pool_init(struct dma_gen_pool *dma_pool,
+ size_t pool_size, gfp_t gfp)
{
- struct gen_pool *pool;
int ret;
- pool = gen_pool_create(PAGE_SHIFT, NUMA_NO_NODE);
- if (!pool)
+ dma_pool->pool = gen_pool_create(PAGE_SHIFT, NUMA_NO_NODE);
+ if (!dma_pool->pool)
return NULL;
- gen_pool_set_algo(pool, gen_pool_first_fit_order_align, NULL);
+ gen_pool_set_algo(dma_pool->pool, gen_pool_first_fit_order_align, NULL);
+
+ /* if platform is using memory encryption atomic pools are by default decrypted. */
+ if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
+ dma_pool->unencrypted = true;
+ else
+ dma_pool->unencrypted = false;
- ret = atomic_pool_expand(pool, pool_size, gfp);
+ ret = atomic_pool_expand(dma_pool, pool_size, gfp);
if (ret) {
- gen_pool_destroy(pool);
+ gen_pool_destroy(dma_pool->pool);
+ dma_pool->pool = NULL;
pr_err("DMA: failed to allocate %zu KiB %pGg pool for atomic allocation\n",
pool_size >> 10, &gfp);
return NULL;
}
pr_info("DMA: preallocated %zu KiB %pGg pool for atomic allocations\n",
- gen_pool_size(pool) >> 10, &gfp);
- return pool;
+ gen_pool_size(dma_pool->pool) >> 10, &gfp);
+ return dma_pool;
}
#ifdef CONFIG_ZONE_DMA32
@@ -207,21 +226,22 @@ static int __init dma_atomic_pool_init(void)
/* All memory might be in the DMA zone(s) to begin with */
if (has_managed_zone(ZONE_NORMAL)) {
- atomic_pool_kernel = __dma_atomic_pool_init(atomic_pool_size,
- GFP_KERNEL);
- if (!atomic_pool_kernel)
+ __dma_atomic_pool_init(&atomic_pool_kernel, atomic_pool_size, GFP_KERNEL);
+ if (!atomic_pool_kernel.pool)
ret = -ENOMEM;
}
+
if (has_managed_dma()) {
- atomic_pool_dma = __dma_atomic_pool_init(atomic_pool_size,
- GFP_KERNEL | GFP_DMA);
- if (!atomic_pool_dma)
+ __dma_atomic_pool_init(&atomic_pool_dma, atomic_pool_size,
+ GFP_KERNEL | GFP_DMA);
+ if (!atomic_pool_dma.pool)
ret = -ENOMEM;
}
+
if (has_managed_dma32) {
- atomic_pool_dma32 = __dma_atomic_pool_init(atomic_pool_size,
- GFP_KERNEL | GFP_DMA32);
- if (!atomic_pool_dma32)
+ __dma_atomic_pool_init(&atomic_pool_dma32, atomic_pool_size,
+ GFP_KERNEL | GFP_DMA32);
+ if (!atomic_pool_dma32.pool)
ret = -ENOMEM;
}
@@ -230,19 +250,44 @@ static int __init dma_atomic_pool_init(void)
}
postcore_initcall(dma_atomic_pool_init);
-static inline struct gen_pool *dma_guess_pool(struct gen_pool *prev, gfp_t gfp)
+static inline struct dma_gen_pool *__dma_guess_pool(struct dma_gen_pool *first,
+ struct dma_gen_pool *second, struct dma_gen_pool *third)
+{
+ if (first->pool)
+ return first;
+ if (second && second->pool)
+ return second;
+ if (third && third->pool)
+ return third;
+ return NULL;
+}
+
+static inline struct dma_gen_pool *dma_guess_pool(struct dma_gen_pool *prev,
+ gfp_t gfp)
{
- if (prev == NULL) {
+ if (!prev) {
if (gfp & GFP_DMA)
- return atomic_pool_dma ?: atomic_pool_dma32 ?: atomic_pool_kernel;
+ return __dma_guess_pool(&atomic_pool_dma,
+ &atomic_pool_dma32,
+ &atomic_pool_kernel);
+
if (gfp & GFP_DMA32)
- return atomic_pool_dma32 ?: atomic_pool_dma ?: atomic_pool_kernel;
- return atomic_pool_kernel ?: atomic_pool_dma32 ?: atomic_pool_dma;
+ return __dma_guess_pool(&atomic_pool_dma32,
+ &atomic_pool_dma,
+ &atomic_pool_kernel);
+
+ return __dma_guess_pool(&atomic_pool_kernel,
+ &atomic_pool_dma32,
+ &atomic_pool_dma);
}
- if (prev == atomic_pool_kernel)
- return atomic_pool_dma32 ? atomic_pool_dma32 : atomic_pool_dma;
- if (prev == atomic_pool_dma32)
- return atomic_pool_dma;
+
+ if (prev == &atomic_pool_kernel)
+ return __dma_guess_pool(&atomic_pool_dma32,
+ &atomic_pool_dma, NULL);
+
+ if (prev == &atomic_pool_dma32)
+ return __dma_guess_pool(&atomic_pool_dma, NULL, NULL);
+
return NULL;
}
@@ -272,16 +317,20 @@ static struct page *__dma_alloc_from_pool(struct device *dev, size_t size,
}
struct page *dma_alloc_from_pool(struct device *dev, size_t size,
- void **cpu_addr, gfp_t gfp,
+ void **cpu_addr, gfp_t gfp, unsigned long attrs,
bool (*phys_addr_ok)(struct device *, phys_addr_t, size_t))
{
- struct gen_pool *pool = NULL;
+ struct dma_gen_pool *dma_pool = NULL;
struct page *page;
bool pool_found = false;
- while ((pool = dma_guess_pool(pool, gfp))) {
+ while ((dma_pool = dma_guess_pool(dma_pool, gfp))) {
+
+ if (dma_pool->unencrypted != !!(attrs & DMA_ATTR_CC_SHARED))
+ continue;
+
pool_found = true;
- page = __dma_alloc_from_pool(dev, size, pool, cpu_addr,
+ page = __dma_alloc_from_pool(dev, size, dma_pool->pool, cpu_addr,
phys_addr_ok);
if (page)
return page;
@@ -296,12 +345,14 @@ struct page *dma_alloc_from_pool(struct device *dev, size_t size,
bool dma_free_from_pool(struct device *dev, void *start, size_t size)
{
- struct gen_pool *pool = NULL;
+ struct dma_gen_pool *dma_pool = NULL;
+
+ while ((dma_pool = dma_guess_pool(dma_pool, 0))) {
- while ((pool = dma_guess_pool(pool, 0))) {
- if (!gen_pool_has_addr(pool, (unsigned long)start, size))
+ if (!gen_pool_has_addr(dma_pool->pool, (unsigned long)start, size))
continue;
- gen_pool_free(pool, (unsigned long)start, size);
+
+ gen_pool_free(dma_pool->pool, (unsigned long)start, size);
return true;
}
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 1abd3e6146f4..ab4eccbaa076 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -612,6 +612,7 @@ static struct page *swiotlb_alloc_tlb(struct device *dev, size_t bytes,
u64 phys_limit, gfp_t gfp)
{
struct page *page;
+ unsigned long attrs = 0;
/*
* Allocate from the atomic pools if memory is encrypted and
@@ -623,8 +624,12 @@ static struct page *swiotlb_alloc_tlb(struct device *dev, size_t bytes,
if (!IS_ENABLED(CONFIG_DMA_COHERENT_POOL))
return NULL;
+ /* swiotlb considered decrypted by default */
+ if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
+ attrs = DMA_ATTR_CC_SHARED;
+
return dma_alloc_from_pool(dev, bytes, &vaddr, gfp,
- dma_coherent_ok);
+ attrs, dma_coherent_ok);
}
gfp &= ~GFP_ZONEMASK;
--
2.43.0
^ permalink raw reply related
* [PATCH v4 04/13] dma: swiotlb: track pool encryption state and honor DMA_ATTR_CC_SHARED
From: Aneesh Kumar K.V (Arm) @ 2026-05-12 9:03 UTC (permalink / raw)
To: iommu, linux-arm-kernel, linux-kernel, linux-coco
Cc: Aneesh Kumar K.V (Arm), Robin Murphy, Marek Szyprowski,
Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
Catalin Marinas, Jiri Pirko, Jason Gunthorpe, Mostafa Saleh,
Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260512090408.794195-1-aneesh.kumar@kernel.org>
Teach swiotlb to distinguish between encrypted and decrypted bounce
buffer pools, and make allocation and mapping paths select a pool whose
state matches the requested DMA attributes.
Add a decrypted flag to io_tlb_mem, initialize it for the default and
restricted pools, and propagate DMA_ATTR_CC_SHARED into swiotlb pool
allocation. Reject swiotlb alloc/map requests when the selected pool does
not match the required encrypted/decrypted state.
Also return DMA addresses with the matching phys_to_dma_{encrypted,
unencrypted} helper so the DMA address encoding stays consistent with the
chosen pool.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
include/linux/dma-direct.h | 10 ++++
include/linux/swiotlb.h | 8 ++-
kernel/dma/direct.c | 14 +++--
kernel/dma/swiotlb.c | 108 +++++++++++++++++++++++++++----------
4 files changed, 107 insertions(+), 33 deletions(-)
diff --git a/include/linux/dma-direct.h b/include/linux/dma-direct.h
index c249912456f9..94fad4e7c11e 100644
--- a/include/linux/dma-direct.h
+++ b/include/linux/dma-direct.h
@@ -77,6 +77,10 @@ static inline dma_addr_t dma_range_map_max(const struct bus_dma_region *map)
#ifndef phys_to_dma_unencrypted
#define phys_to_dma_unencrypted phys_to_dma
#endif
+
+#ifndef phys_to_dma_encrypted
+#define phys_to_dma_encrypted phys_to_dma
+#endif
#else
static inline dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr)
{
@@ -90,6 +94,12 @@ static inline dma_addr_t phys_to_dma_unencrypted(struct device *dev,
{
return dma_addr_unencrypted(__phys_to_dma(dev, paddr));
}
+
+static inline dma_addr_t phys_to_dma_encrypted(struct device *dev,
+ phys_addr_t paddr)
+{
+ return dma_addr_encrypted(__phys_to_dma(dev, paddr));
+}
/*
* If memory encryption is supported, phys_to_dma will set the memory encryption
* bit in the DMA address, and dma_to_phys will clear it.
diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h
index 3dae0f592063..b3fa3c6e0169 100644
--- a/include/linux/swiotlb.h
+++ b/include/linux/swiotlb.h
@@ -81,6 +81,7 @@ struct io_tlb_pool {
struct list_head node;
struct rcu_head rcu;
bool transient;
+ bool unencrypted;
#endif
};
@@ -111,6 +112,7 @@ struct io_tlb_mem {
struct dentry *debugfs;
bool force_bounce;
bool for_alloc;
+ bool unencrypted;
#ifdef CONFIG_SWIOTLB_DYNAMIC
bool can_grow;
u64 phys_limit;
@@ -282,7 +284,8 @@ static inline void swiotlb_sync_single_for_cpu(struct device *dev,
extern void swiotlb_print_info(void);
#ifdef CONFIG_DMA_RESTRICTED_POOL
-struct page *swiotlb_alloc(struct device *dev, size_t size);
+struct page *swiotlb_alloc(struct device *dev, size_t size,
+ unsigned long attrs);
bool swiotlb_free(struct device *dev, struct page *page, size_t size);
static inline bool is_swiotlb_for_alloc(struct device *dev)
@@ -290,7 +293,8 @@ static inline bool is_swiotlb_for_alloc(struct device *dev)
return dev->dma_io_tlb_mem->for_alloc;
}
#else
-static inline struct page *swiotlb_alloc(struct device *dev, size_t size)
+static inline struct page *swiotlb_alloc(struct device *dev, size_t size,
+ unsigned long attrs)
{
return NULL;
}
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index dc2907439b3d..97ae4fa10521 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -104,9 +104,10 @@ static void __dma_direct_free_pages(struct device *dev, struct page *page,
dma_free_contiguous(dev, page, size);
}
-static struct page *dma_direct_alloc_swiotlb(struct device *dev, size_t size)
+static struct page *dma_direct_alloc_swiotlb(struct device *dev, size_t size,
+ unsigned long attrs)
{
- struct page *page = swiotlb_alloc(dev, size);
+ struct page *page = swiotlb_alloc(dev, size, attrs);
if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) {
swiotlb_free(dev, page, size);
@@ -266,8 +267,12 @@ void *dma_direct_alloc(struct device *dev, size_t size,
gfp, attrs);
if (is_swiotlb_for_alloc(dev)) {
- page = dma_direct_alloc_swiotlb(dev, size);
+ page = dma_direct_alloc_swiotlb(dev, size, attrs);
if (page) {
+ /*
+ * swiotlb allocations comes from pool already marked
+ * decrypted
+ */
mark_mem_decrypt = false;
goto setup_page;
}
@@ -374,6 +379,7 @@ void dma_direct_free(struct device *dev, size_t size,
return;
if (swiotlb_find_pool(dev, dma_to_phys(dev, dma_addr)))
+ /* Swiotlb doesn't need a page attribute update on free */
mark_mem_encrypted = false;
if (is_vmalloc_addr(cpu_addr)) {
@@ -403,7 +409,7 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
gfp, attrs);
if (is_swiotlb_for_alloc(dev)) {
- page = dma_direct_alloc_swiotlb(dev, size);
+ page = dma_direct_alloc_swiotlb(dev, size, attrs);
if (!page)
return NULL;
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index ab4eccbaa076..065663be282c 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -259,10 +259,21 @@ void __init swiotlb_update_mem_attributes(void)
struct io_tlb_pool *mem = &io_tlb_default_mem.defpool;
unsigned long bytes;
+ /*
+ * if platform support memory encryption, swiotlb buffers are
+ * decrypted by default.
+ */
+ if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
+ io_tlb_default_mem.unencrypted = true;
+ else
+ io_tlb_default_mem.unencrypted = false;
+
if (!mem->nslabs || mem->late_alloc)
return;
bytes = PAGE_ALIGN(mem->nslabs << IO_TLB_SHIFT);
- set_memory_decrypted((unsigned long)mem->vaddr, bytes >> PAGE_SHIFT);
+
+ if (io_tlb_default_mem.unencrypted)
+ set_memory_decrypted((unsigned long)mem->vaddr, bytes >> PAGE_SHIFT);
}
static void swiotlb_init_io_tlb_pool(struct io_tlb_pool *mem, phys_addr_t start,
@@ -505,8 +516,10 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
if (!mem->slots)
goto error_slots;
- set_memory_decrypted((unsigned long)vstart,
- (nslabs << IO_TLB_SHIFT) >> PAGE_SHIFT);
+ if (io_tlb_default_mem.unencrypted)
+ set_memory_decrypted((unsigned long)vstart,
+ (nslabs << IO_TLB_SHIFT) >> PAGE_SHIFT);
+
swiotlb_init_io_tlb_pool(mem, virt_to_phys(vstart), nslabs, true,
nareas);
add_mem_pool(&io_tlb_default_mem, mem);
@@ -539,7 +552,9 @@ void __init swiotlb_exit(void)
tbl_size = PAGE_ALIGN(mem->end - mem->start);
slots_size = PAGE_ALIGN(array_size(sizeof(*mem->slots), mem->nslabs));
- set_memory_encrypted(tbl_vaddr, tbl_size >> PAGE_SHIFT);
+ if (io_tlb_default_mem.unencrypted)
+ set_memory_encrypted(tbl_vaddr, tbl_size >> PAGE_SHIFT);
+
if (mem->late_alloc) {
area_order = get_order(array_size(sizeof(*mem->areas),
mem->nareas));
@@ -563,6 +578,7 @@ void __init swiotlb_exit(void)
* @gfp: GFP flags for the allocation.
* @bytes: Size of the buffer.
* @phys_limit: Maximum allowed physical address of the buffer.
+ * @unencrypted: true to allocate unencrypted memory, false for encrypted memory
*
* Allocate pages from the buddy allocator. If successful, make the allocated
* pages decrypted that they can be used for DMA.
@@ -570,7 +586,8 @@ void __init swiotlb_exit(void)
* Return: Decrypted pages, %NULL on allocation failure, or ERR_PTR(-EAGAIN)
* if the allocated physical address was above @phys_limit.
*/
-static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes, u64 phys_limit)
+static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes,
+ u64 phys_limit, bool unencrypted)
{
unsigned int order = get_order(bytes);
struct page *page;
@@ -588,13 +605,13 @@ static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes, u64 phys_limit)
}
vaddr = phys_to_virt(paddr);
- if (set_memory_decrypted((unsigned long)vaddr, PFN_UP(bytes)))
+ if (unencrypted && set_memory_decrypted((unsigned long)vaddr, PFN_UP(bytes)))
goto error;
return page;
error:
/* Intentional leak if pages cannot be encrypted again. */
- if (!set_memory_encrypted((unsigned long)vaddr, PFN_UP(bytes)))
+ if (unencrypted && !set_memory_encrypted((unsigned long)vaddr, PFN_UP(bytes)))
__free_pages(page, order);
return NULL;
}
@@ -604,30 +621,26 @@ static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes, u64 phys_limit)
* @dev: Device for which a memory pool is allocated.
* @bytes: Size of the buffer.
* @phys_limit: Maximum allowed physical address of the buffer.
+ * @attrs: DMA attributes for the allocation.
* @gfp: GFP flags for the allocation.
*
* Return: Allocated pages, or %NULL on allocation failure.
*/
static struct page *swiotlb_alloc_tlb(struct device *dev, size_t bytes,
- u64 phys_limit, gfp_t gfp)
+ u64 phys_limit, unsigned long attrs, gfp_t gfp)
{
struct page *page;
- unsigned long attrs = 0;
/*
* Allocate from the atomic pools if memory is encrypted and
* the allocation is atomic, because decrypting may block.
*/
- if (!gfpflags_allow_blocking(gfp) && dev && force_dma_unencrypted(dev)) {
+ if (!gfpflags_allow_blocking(gfp) && (attrs & DMA_ATTR_CC_SHARED)) {
void *vaddr;
if (!IS_ENABLED(CONFIG_DMA_COHERENT_POOL))
return NULL;
- /* swiotlb considered decrypted by default */
- if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
- attrs = DMA_ATTR_CC_SHARED;
-
return dma_alloc_from_pool(dev, bytes, &vaddr, gfp,
attrs, dma_coherent_ok);
}
@@ -638,7 +651,8 @@ static struct page *swiotlb_alloc_tlb(struct device *dev, size_t bytes,
else if (phys_limit <= DMA_BIT_MASK(32))
gfp |= __GFP_DMA32;
- while (IS_ERR(page = alloc_dma_pages(gfp, bytes, phys_limit))) {
+ while (IS_ERR(page = alloc_dma_pages(gfp, bytes, phys_limit,
+ !!(attrs & DMA_ATTR_CC_SHARED)))) {
if (IS_ENABLED(CONFIG_ZONE_DMA32) &&
phys_limit < DMA_BIT_MASK(64) &&
!(gfp & (__GFP_DMA32 | __GFP_DMA)))
@@ -657,15 +671,18 @@ static struct page *swiotlb_alloc_tlb(struct device *dev, size_t bytes,
* swiotlb_free_tlb() - free a dynamically allocated IO TLB buffer
* @vaddr: Virtual address of the buffer.
* @bytes: Size of the buffer.
+ * @unencrypted: true if @vaddr was allocated decrypted and must be
+ * re-encrypted before being freed
*/
-static void swiotlb_free_tlb(void *vaddr, size_t bytes)
+static void swiotlb_free_tlb(void *vaddr, size_t bytes, bool unencrypted)
{
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
dma_free_from_pool(NULL, vaddr, bytes))
return;
/* Intentional leak if pages cannot be encrypted again. */
- if (!set_memory_encrypted((unsigned long)vaddr, PFN_UP(bytes)))
+ if (!unencrypted ||
+ !set_memory_encrypted((unsigned long)vaddr, PFN_UP(bytes)))
__free_pages(virt_to_page(vaddr), get_order(bytes));
}
@@ -676,6 +693,7 @@ static void swiotlb_free_tlb(void *vaddr, size_t bytes)
* @nslabs: Desired (maximum) number of slabs.
* @nareas: Number of areas.
* @phys_limit: Maximum DMA buffer physical address.
+ * @attrs: DMA attributes for the allocation.
* @gfp: GFP flags for the allocations.
*
* Allocate and initialize a new IO TLB memory pool. The actual number of
@@ -686,7 +704,8 @@ static void swiotlb_free_tlb(void *vaddr, size_t bytes)
*/
static struct io_tlb_pool *swiotlb_alloc_pool(struct device *dev,
unsigned long minslabs, unsigned long nslabs,
- unsigned int nareas, u64 phys_limit, gfp_t gfp)
+ unsigned int nareas, u64 phys_limit, unsigned long attrs,
+ gfp_t gfp)
{
struct io_tlb_pool *pool;
unsigned int slot_order;
@@ -704,9 +723,10 @@ static struct io_tlb_pool *swiotlb_alloc_pool(struct device *dev,
if (!pool)
goto error;
pool->areas = (void *)pool + sizeof(*pool);
+ pool->unencrypted = !!(attrs & DMA_ATTR_CC_SHARED);
tlb_size = nslabs << IO_TLB_SHIFT;
- while (!(tlb = swiotlb_alloc_tlb(dev, tlb_size, phys_limit, gfp))) {
+ while (!(tlb = swiotlb_alloc_tlb(dev, tlb_size, phys_limit, attrs, gfp))) {
if (nslabs <= minslabs)
goto error_tlb;
nslabs = ALIGN(nslabs >> 1, IO_TLB_SEGSIZE);
@@ -724,7 +744,8 @@ static struct io_tlb_pool *swiotlb_alloc_pool(struct device *dev,
return pool;
error_slots:
- swiotlb_free_tlb(page_address(tlb), tlb_size);
+ swiotlb_free_tlb(page_address(tlb), tlb_size,
+ !!(attrs & DMA_ATTR_CC_SHARED));
error_tlb:
kfree(pool);
error:
@@ -742,7 +763,9 @@ static void swiotlb_dyn_alloc(struct work_struct *work)
struct io_tlb_pool *pool;
pool = swiotlb_alloc_pool(NULL, IO_TLB_MIN_SLABS, default_nslabs,
- default_nareas, mem->phys_limit, GFP_KERNEL);
+ default_nareas, mem->phys_limit,
+ mem->unencrypted ? DMA_ATTR_CC_SHARED : 0,
+ GFP_KERNEL);
if (!pool) {
pr_warn_ratelimited("Failed to allocate new pool");
return;
@@ -762,7 +785,7 @@ static void swiotlb_dyn_free(struct rcu_head *rcu)
size_t tlb_size = pool->end - pool->start;
free_pages((unsigned long)pool->slots, get_order(slots_size));
- swiotlb_free_tlb(pool->vaddr, tlb_size);
+ swiotlb_free_tlb(pool->vaddr, tlb_size, pool->unencrypted);
kfree(pool);
}
@@ -1232,6 +1255,7 @@ static int swiotlb_find_slots(struct device *dev, phys_addr_t orig_addr,
nslabs = nr_slots(alloc_size);
phys_limit = min_not_zero(*dev->dma_mask, dev->bus_dma_limit);
pool = swiotlb_alloc_pool(dev, nslabs, nslabs, 1, phys_limit,
+ mem->unencrypted ? DMA_ATTR_CC_SHARED : 0,
GFP_NOWAIT);
if (!pool)
return -1;
@@ -1394,6 +1418,7 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
enum dma_data_direction dir, unsigned long attrs)
{
struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
+ bool require_decrypted = false;
unsigned int offset;
struct io_tlb_pool *pool;
unsigned int i;
@@ -1411,6 +1436,16 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");
+ /*
+ * if we are trying to swiotlb map a decrypted paddr or the paddr is encrypted
+ * but the device is forcing decryption, use decrypted io_tlb_mem
+ */
+ if ((attrs & DMA_ATTR_CC_SHARED) || force_dma_unencrypted(dev))
+ require_decrypted = true;
+
+ if (require_decrypted != mem->unencrypted)
+ return (phys_addr_t)DMA_MAPPING_ERROR;
+
/*
* The default swiotlb memory pool is allocated with PAGE_SIZE
* alignment. If a mapping is requested with larger alignment,
@@ -1608,8 +1643,14 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size,
if (swiotlb_addr == (phys_addr_t)DMA_MAPPING_ERROR)
return DMA_MAPPING_ERROR;
- /* Ensure that the address returned is DMA'ble */
- dma_addr = phys_to_dma_unencrypted(dev, swiotlb_addr);
+ /*
+ * Use the allocated io_tlb_mem encryption type to determine dma addr.
+ */
+ if (dev->dma_io_tlb_mem->unencrypted)
+ dma_addr = phys_to_dma_unencrypted(dev, swiotlb_addr);
+ else
+ dma_addr = phys_to_dma_encrypted(dev, swiotlb_addr);
+
if (unlikely(!dma_capable(dev, dma_addr, size, true))) {
__swiotlb_tbl_unmap_single(dev, swiotlb_addr, size, dir,
attrs | DMA_ATTR_SKIP_CPU_SYNC,
@@ -1773,7 +1814,8 @@ static inline void swiotlb_create_debugfs_files(struct io_tlb_mem *mem,
#ifdef CONFIG_DMA_RESTRICTED_POOL
-struct page *swiotlb_alloc(struct device *dev, size_t size)
+struct page *swiotlb_alloc(struct device *dev, size_t size,
+ unsigned long attrs)
{
struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
struct io_tlb_pool *pool;
@@ -1784,6 +1826,9 @@ struct page *swiotlb_alloc(struct device *dev, size_t size)
if (!mem)
return NULL;
+ if (mem->unencrypted != !!(attrs & DMA_ATTR_CC_SHARED))
+ return NULL;
+
align = (1 << (get_order(size) + PAGE_SHIFT)) - 1;
index = swiotlb_find_slots(dev, 0, size, align, &pool);
if (index == -1)
@@ -1853,9 +1898,18 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
kfree(mem);
return -ENOMEM;
}
+ /*
+ * if platform supports memory encryption,
+ * restricted mem pool is decrypted by default
+ */
+ if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {
+ mem->unencrypted = true;
+ set_memory_decrypted((unsigned long)phys_to_virt(rmem->base),
+ rmem->size >> PAGE_SHIFT);
+ } else {
+ mem->unencrypted = false;
+ }
- set_memory_decrypted((unsigned long)phys_to_virt(rmem->base),
- rmem->size >> PAGE_SHIFT);
swiotlb_init_io_tlb_pool(pool, rmem->base, nslabs,
false, nareas);
mem->force_bounce = true;
--
2.43.0
^ permalink raw reply related
* [PATCH v4 05/13] dma-mapping: make dma_pgprot() honor DMA_ATTR_CC_SHARED
From: Aneesh Kumar K.V (Arm) @ 2026-05-12 9:04 UTC (permalink / raw)
To: iommu, linux-arm-kernel, linux-kernel, linux-coco
Cc: Aneesh Kumar K.V (Arm), Robin Murphy, Marek Szyprowski,
Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
Catalin Marinas, Jiri Pirko, Jason Gunthorpe, Mostafa Saleh,
Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260512090408.794195-1-aneesh.kumar@kernel.org>
Fold encrypted/decrypted pgprot selection into dma_pgprot() so callers
do not need to adjust the page protection separately.
Update dma_pgprot() to apply pgprot_decrypted() when
DMA_ATTR_CC_SHARED is set and pgprot_encrypted() otherwise Convert
the dma-direct allocation and mmap paths to pass DMA_ATTR_CC_SHARED
instead of open-coding force_dma_unencrypted() handling around
dma_pgprot().
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 8 +++-----
kernel/dma/mapping.c | 16 ++++++++++++----
2 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 97ae4fa10521..ac315dd046c4 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -298,9 +298,6 @@ void *dma_direct_alloc(struct device *dev, size_t size,
if (remap) {
pgprot_t prot = dma_pgprot(dev, PAGE_KERNEL, attrs);
- if (force_dma_unencrypted(dev))
- prot = pgprot_decrypted(prot);
-
/* remove any dirty cache lines on the kernel alias */
arch_dma_prep_coherent(page, size);
@@ -603,9 +600,10 @@ int dma_direct_mmap(struct device *dev, struct vm_area_struct *vma,
unsigned long pfn = PHYS_PFN(dma_to_phys(dev, dma_addr));
int ret = -ENXIO;
- vma->vm_page_prot = dma_pgprot(dev, vma->vm_page_prot, attrs);
if (force_dma_unencrypted(dev))
- vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
+ attrs |= DMA_ATTR_CC_SHARED;
+
+ vma->vm_page_prot = dma_pgprot(dev, vma->vm_page_prot, attrs);
if (dma_mmap_from_dev_coherent(dev, vma, cpu_addr, size, &ret))
return ret;
diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c
index 23ed8eb9233e..44f715f3aa2d 100644
--- a/kernel/dma/mapping.c
+++ b/kernel/dma/mapping.c
@@ -543,13 +543,21 @@ EXPORT_SYMBOL(dma_get_sgtable_attrs);
*/
pgprot_t dma_pgprot(struct device *dev, pgprot_t prot, unsigned long attrs)
{
+ pgprot_t dma_prot;
+
if (dev_is_dma_coherent(dev))
- return prot;
+ dma_prot = prot;
#ifdef CONFIG_ARCH_HAS_DMA_WRITE_COMBINE
- if (attrs & DMA_ATTR_WRITE_COMBINE)
- return pgprot_writecombine(prot);
+ else if (attrs & DMA_ATTR_WRITE_COMBINE)
+ dma_prot = pgprot_writecombine(prot);
#endif
- return pgprot_dmacoherent(prot);
+ else
+ dma_prot = pgprot_dmacoherent(prot);
+
+ if (attrs & DMA_ATTR_CC_SHARED)
+ return pgprot_decrypted(dma_prot);
+ else
+ return pgprot_encrypted(dma_prot);
}
#endif /* CONFIG_MMU */
--
2.43.0
^ permalink raw reply related
* [PATCH v4 06/13] dma-direct: pass attrs to dma_capable() for DMA_ATTR_CC_SHARED checks
From: Aneesh Kumar K.V (Arm) @ 2026-05-12 9:04 UTC (permalink / raw)
To: iommu, linux-arm-kernel, linux-kernel, linux-coco
Cc: Aneesh Kumar K.V (Arm), Robin Murphy, Marek Szyprowski,
Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
Catalin Marinas, Jiri Pirko, Jason Gunthorpe, Mostafa Saleh,
Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260512090408.794195-1-aneesh.kumar@kernel.org>
Teach dma_capable() about DMA_ATTR_CC_SHARED so the capability
check can reject encrypted DMA addresses for devices that require
unencrypted/shared DMA.
Also propagate DMA_ATTR_CC_SHARED in swiotlb_map() when the selected
SWIOTLB pool is decrypted so the capability check sees the correct DMA
address attribute.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/x86/kernel/amd_gart_64.c | 30 ++++++++++++++++--------------
drivers/xen/swiotlb-xen.c | 10 +++++++---
include/linux/dma-direct.h | 9 ++++++++-
kernel/dma/direct.h | 6 +++---
kernel/dma/swiotlb.c | 8 +++++---
5 files changed, 39 insertions(+), 24 deletions(-)
diff --git a/arch/x86/kernel/amd_gart_64.c b/arch/x86/kernel/amd_gart_64.c
index e8000a56732e..b5f1f031d45b 100644
--- a/arch/x86/kernel/amd_gart_64.c
+++ b/arch/x86/kernel/amd_gart_64.c
@@ -180,22 +180,23 @@ static void iommu_full(struct device *dev, size_t size, int dir)
}
static inline int
-need_iommu(struct device *dev, unsigned long addr, size_t size)
+need_iommu(struct device *dev, unsigned long addr, size_t size, unsigned long attrs)
{
- return force_iommu || !dma_capable(dev, addr, size, true);
+ return force_iommu || !dma_capable(dev, addr, size, true, attrs);
}
static inline int
-nonforced_iommu(struct device *dev, unsigned long addr, size_t size)
+nonforced_iommu(struct device *dev, unsigned long addr, size_t size,
+ unsigned long attrs)
{
- return !dma_capable(dev, addr, size, true);
+ return !dma_capable(dev, addr, size, true, attrs);
}
/* Map a single continuous physical area into the IOMMU.
* Caller needs to check if the iommu is needed and flush.
*/
static dma_addr_t dma_map_area(struct device *dev, dma_addr_t phys_mem,
- size_t size, int dir, unsigned long align_mask)
+ size_t size, int dir, unsigned long align_mask, unsigned long attrs)
{
unsigned long npages = iommu_num_pages(phys_mem, size, PAGE_SIZE);
unsigned long iommu_page;
@@ -206,7 +207,7 @@ static dma_addr_t dma_map_area(struct device *dev, dma_addr_t phys_mem,
iommu_page = alloc_iommu(dev, npages, align_mask);
if (iommu_page == -1) {
- if (!nonforced_iommu(dev, phys_mem, size))
+ if (!nonforced_iommu(dev, phys_mem, size, attrs))
return phys_mem;
if (panic_on_overflow)
panic("dma_map_area overflow %lu bytes\n", size);
@@ -231,10 +232,10 @@ static dma_addr_t gart_map_phys(struct device *dev, phys_addr_t paddr,
if (unlikely(attrs & DMA_ATTR_MMIO))
return DMA_MAPPING_ERROR;
- if (!need_iommu(dev, paddr, size))
+ if (!need_iommu(dev, paddr, size, attrs))
return paddr;
- bus = dma_map_area(dev, paddr, size, dir, 0);
+ bus = dma_map_area(dev, paddr, size, dir, 0, attrs);
flush_gart();
return bus;
@@ -289,7 +290,7 @@ static void gart_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
/* Fallback for dma_map_sg in case of overflow */
static int dma_map_sg_nonforce(struct device *dev, struct scatterlist *sg,
- int nents, int dir)
+ int nents, int dir, unsigned long attrs)
{
struct scatterlist *s;
int i;
@@ -301,8 +302,8 @@ static int dma_map_sg_nonforce(struct device *dev, struct scatterlist *sg,
for_each_sg(sg, s, nents, i) {
unsigned long addr = sg_phys(s);
- if (nonforced_iommu(dev, addr, s->length)) {
- addr = dma_map_area(dev, addr, s->length, dir, 0);
+ if (nonforced_iommu(dev, addr, s->length, attrs)) {
+ addr = dma_map_area(dev, addr, s->length, dir, 0, attrs);
if (addr == DMA_MAPPING_ERROR) {
if (i > 0)
gart_unmap_sg(dev, sg, i, dir, 0);
@@ -401,7 +402,7 @@ static int gart_map_sg(struct device *dev, struct scatterlist *sg, int nents,
s->dma_address = addr;
BUG_ON(s->length == 0);
- nextneed = need_iommu(dev, addr, s->length);
+ nextneed = need_iommu(dev, addr, s->length, attrs);
/* Handle the previous not yet processed entries */
if (i > start) {
@@ -449,7 +450,7 @@ static int gart_map_sg(struct device *dev, struct scatterlist *sg, int nents,
/* When it was forced or merged try again in a dumb way */
if (force_iommu || iommu_merge) {
- out = dma_map_sg_nonforce(dev, sg, nents, dir);
+ out = dma_map_sg_nonforce(dev, sg, nents, dir, attrs);
if (out > 0)
return out;
}
@@ -473,7 +474,8 @@ gart_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_addr,
return vaddr;
*dma_addr = dma_map_area(dev, virt_to_phys(vaddr), size,
- DMA_BIDIRECTIONAL, (1UL << get_order(size)) - 1);
+ DMA_BIDIRECTIONAL,
+ (1UL << get_order(size)) - 1, attrs);
flush_gart();
if (unlikely(*dma_addr == DMA_MAPPING_ERROR))
goto out_free;
diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c
index 2cbf2b588f5b..fa6734461d4c 100644
--- a/drivers/xen/swiotlb-xen.c
+++ b/drivers/xen/swiotlb-xen.c
@@ -212,7 +212,7 @@ static dma_addr_t xen_swiotlb_map_phys(struct device *dev, phys_addr_t phys,
BUG_ON(dir == DMA_NONE);
if (attrs & DMA_ATTR_MMIO) {
- if (unlikely(!dma_capable(dev, phys, size, false))) {
+ if (unlikely(!dma_capable(dev, phys, size, false, attrs))) {
dev_err_once(
dev,
"DMA addr %pa+%zu overflow (mask %llx, bus limit %llx).\n",
@@ -231,7 +231,7 @@ static dma_addr_t xen_swiotlb_map_phys(struct device *dev, phys_addr_t phys,
* we can safely return the device addr and not worry about bounce
* buffering it.
*/
- if (dma_capable(dev, dev_addr, size, true) &&
+ if (dma_capable(dev, dev_addr, size, true, attrs) &&
!dma_kmalloc_needs_bounce(dev, size, dir) &&
!range_straddles_page_boundary(phys, size) &&
!xen_arch_need_swiotlb(dev, phys, dev_addr) &&
@@ -248,12 +248,16 @@ static dma_addr_t xen_swiotlb_map_phys(struct device *dev, phys_addr_t phys,
return DMA_MAPPING_ERROR;
phys = map;
+ /* This always return an encrypted addr */
dev_addr = xen_phys_to_dma(dev, map);
+ if (WARN_ON(dev->dma_io_tlb_mem->unencrypted))
+ attrs |= DMA_ATTR_CC_SHARED;
+
/*
* Ensure that the address returned is DMA'ble
*/
- if (unlikely(!dma_capable(dev, dev_addr, size, true))) {
+ if (unlikely(!dma_capable(dev, dev_addr, size, true, attrs))) {
__swiotlb_tbl_unmap_single(dev, map, size, dir,
attrs | DMA_ATTR_SKIP_CPU_SYNC,
swiotlb_find_pool(dev, map));
diff --git a/include/linux/dma-direct.h b/include/linux/dma-direct.h
index 94fad4e7c11e..9dbe198b2c4a 100644
--- a/include/linux/dma-direct.h
+++ b/include/linux/dma-direct.h
@@ -135,12 +135,19 @@ static inline bool force_dma_unencrypted(struct device *dev)
#endif /* CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED */
static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size,
- bool is_ram)
+ bool is_ram, unsigned long attrs)
{
dma_addr_t end = addr + size - 1;
if (addr == DMA_MAPPING_ERROR)
return false;
+ /*
+ * if phys addr attribute is encrypted but the
+ * device is forcing an unencrypted dma addr
+ */
+ if (!(attrs & DMA_ATTR_CC_SHARED) && force_dma_unencrypted(dev))
+ return false;
+
if (is_ram && !IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
return false;
diff --git a/kernel/dma/direct.h b/kernel/dma/direct.h
index 7140c208c123..e05dc7649366 100644
--- a/kernel/dma/direct.h
+++ b/kernel/dma/direct.h
@@ -101,15 +101,15 @@ static inline dma_addr_t dma_direct_map_phys(struct device *dev,
if (attrs & DMA_ATTR_MMIO) {
dma_addr = phys;
- if (unlikely(!dma_capable(dev, dma_addr, size, false)))
+ if (unlikely(!dma_capable(dev, dma_addr, size, false, attrs)))
goto err_overflow;
} else if (attrs & DMA_ATTR_CC_SHARED) {
dma_addr = phys_to_dma_unencrypted(dev, phys);
- if (unlikely(!dma_capable(dev, dma_addr, size, false)))
+ if (unlikely(!dma_capable(dev, dma_addr, size, false, attrs)))
goto err_overflow;
} else {
dma_addr = phys_to_dma(dev, phys);
- if (unlikely(!dma_capable(dev, dma_addr, size, true)) ||
+ if (unlikely(!dma_capable(dev, dma_addr, size, true, attrs)) ||
dma_kmalloc_needs_bounce(dev, size, dir)) {
if (is_swiotlb_active(dev) &&
!(attrs & DMA_ATTR_REQUIRE_COHERENT))
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 065663be282c..9f87ebe42797 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -1646,12 +1646,14 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size,
/*
* Use the allocated io_tlb_mem encryption type to determine dma addr.
*/
- if (dev->dma_io_tlb_mem->unencrypted)
+ if (dev->dma_io_tlb_mem->unencrypted) {
dma_addr = phys_to_dma_unencrypted(dev, swiotlb_addr);
- else
+ attrs |= DMA_ATTR_CC_SHARED;
+ } else {
dma_addr = phys_to_dma_encrypted(dev, swiotlb_addr);
+ }
- if (unlikely(!dma_capable(dev, dma_addr, size, true))) {
+ if (unlikely(!dma_capable(dev, dma_addr, size, true, attrs))) {
__swiotlb_tbl_unmap_single(dev, swiotlb_addr, size, dir,
attrs | DMA_ATTR_SKIP_CPU_SYNC,
swiotlb_find_pool(dev, swiotlb_addr));
--
2.43.0
^ permalink raw reply related
* [PATCH] clk: imx: Add audio PLL debugfs for K-divider control
From: Jacky Bai @ 2026-05-12 9:08 UTC (permalink / raw)
To: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd,
Brian Masney, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam
Cc: linux-clk, imx, linux-arm-kernel, Jacky Bai
Add debugfs support for runtime tuning of the audio PLL K divider,
which enables fine-grained frequency adjustments for audio PLL.
This is used for:
- Audio clock calibration and testing
- Debugging audio synchronization issues
Two debug interfaces are exported to userspace:
- delta_k: It is used to adjust the K divider in PLL based on small
steps
- pll_parameter: It is used for get PLL's current M-divider,
P-divider, S-divider & K-divider setting in PLL register
examples for the usage of the two interfaces:
1): Get the current PLL setting of dividers
cat /sys/kernel/debug/audio_pll_monitor/audio_pll1/pll_parameter
2): if want to adjust the K-divider by a delta_k '1', then
echo 1 > /sys/kernel/debug/audio_pll_monitor/audio_pll1/delta_k;
3): if want to adjust the K-divider by a delta_k '-1', then
echo -1 > /sys/kernel/debug/audio_pll_monitor/audio_pll1/delta_k;
Signed-off-by: Jacky Bai <ping.bai@nxp.com>
---
drivers/clk/imx/clk-imx8mm.c | 6 ++++
drivers/clk/imx/clk-pll14xx.c | 79 +++++++++++++++++++++++++++++++++++++++++++
drivers/clk/imx/clk.h | 1 +
3 files changed, 86 insertions(+)
diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c
index 319af4deec01c188524807d39dff92bbd08f3601..6a031d7cd031c8b5f5b01e5531ce54360724ba44 100644
--- a/drivers/clk/imx/clk-imx8mm.c
+++ b/drivers/clk/imx/clk-imx8mm.c
@@ -298,6 +298,7 @@ static struct clk_hw **hws;
static int imx8mm_clocks_probe(struct platform_device *pdev)
{
+ struct clk_hw *audio_pll_hws[2];
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
void __iomem *base;
@@ -610,6 +611,11 @@ static int imx8mm_clocks_probe(struct platform_device *pdev)
imx_register_uart_clocks();
+ /* Add debug interface for audio PLLs */
+ audio_pll_hws[0] = hws[IMX8MM_AUDIO_PLL1];
+ audio_pll_hws[1] = hws[IMX8MM_AUDIO_PLL2];
+ audio_pll_debug_init(audio_pll_hws, ARRAY_SIZE(audio_pll_hws));
+
return 0;
unregister_hws:
diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c
index 39600ee22be3066683b562aa6f2a8b750d19c4cc..59f126f35474116bdad0aeda59777b9eb7f246cf 100644
--- a/drivers/clk/imx/clk-pll14xx.c
+++ b/drivers/clk/imx/clk-pll14xx.c
@@ -8,6 +8,7 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk-provider.h>
+#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/io.h>
@@ -551,3 +552,81 @@ struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name,
return hw;
}
EXPORT_SYMBOL_GPL(imx_dev_clk_hw_pll14xx);
+
+/*
+ * Debugfs interface for Audio PLL runtime monitoring and control
+ *
+ * This interface allows dynamic adjustment of the Audio PLL
+ * K-divider for precise frequency tuning, particularly useful
+ * for audio applications.
+ *
+ * examples for the usage of the two interfaces:
+ * 1): Get the current PLL setting of dividers
+ * cat /sys/kernel/debug/audio_pll_monitor/audio_pll1/pll_parameter
+ *
+ * 2): if want to adjust the K-divider by a delta_k '1', then
+ * echo 1 > /sys/kernel/debug/audio_pll_monitor/audio_pll1/delta_k;
+ *
+ * 3): if want to adjust the K-divider by a delta_k '-1', then
+ * echo -1 > /sys/kernel/debug/audio_pll_monitor/audio_pll1/delta_k;
+ */
+#ifdef CONFIG_DEBUG_FS
+static int pll_delta_k_set(void *data, u64 val)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(data);
+ s16 delta_k = (s16)val;
+ u32 div_ctl1;
+ s16 kdiv;
+
+ div_ctl1 = readl_relaxed(pll->base + DIV_CTL1);
+ kdiv = FIELD_GET(KDIV_MASK, div_ctl1) + delta_k;
+ writel_relaxed(FIELD_PREP(KDIV_MASK, kdiv), pll->base + DIV_CTL1);
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(delta_k_fops, NULL, pll_delta_k_set, "%lld\n");
+
+static int pll_setting_show(struct seq_file *s, void *data)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(s->private);
+ u32 div_ctl0, div_ctl1;
+ u32 mdiv, pdiv, sdiv, kdiv;
+
+ div_ctl0 = readl_relaxed(pll->base + DIV_CTL0);
+ div_ctl1 = readl_relaxed(pll->base + DIV_CTL1);
+
+ mdiv = FIELD_GET(MDIV_MASK, div_ctl0);
+ pdiv = FIELD_GET(PDIV_MASK, div_ctl0);
+ sdiv = FIELD_GET(SDIV_MASK, div_ctl0);
+ kdiv = FIELD_GET(KDIV_MASK, div_ctl1);
+
+ seq_printf(s, "Mdiv: 0x%x; Pdiv: 0x%x; Sdiv: 0x%x; Kdiv: 0x%x\n",
+ mdiv, pdiv, sdiv, kdiv);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(pll_setting);
+
+void audio_pll_debug_init(struct clk_hw *hws[], unsigned int num_plls)
+{
+ struct dentry *rootdir, *audio_pll_dir;
+ const char *pll_name;
+ int i;
+
+ rootdir = debugfs_create_dir("audio_pll_monitor", NULL);
+
+ for (i = 0; i < num_plls; i++) {
+ pll_name = clk_hw_get_name(hws[i]);
+ audio_pll_dir = debugfs_create_dir(pll_name, rootdir);
+ debugfs_create_file_unsafe("delta_k", 0200, audio_pll_dir,
+ hws[i], &delta_k_fops);
+ debugfs_create_file("pll_parameter", 0444, audio_pll_dir,
+ hws[i], &pll_setting_fops);
+ }
+}
+#else /* !CONFIG_DEBUG_FS */
+void audio_pll_debug_init(struct clk_hw *hws[], unsigned int num_plls)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+EXPORT_SYMBOL_GPL(audio_pll_debug_init);
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index aa5202f284f3d1b7c1b4bf65e2329831832b43a5..9611ab127dad6f23770c18e22e1acbe2fc22bd4e 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -487,4 +487,5 @@ struct clk_hw *imx_clk_gpr_mux(const char *name, const char *compatible,
u32 reg, const char **parent_names,
u8 num_parents, const u32 *mux_table, u32 mask);
+void audio_pll_debug_init(struct clk_hw **hws, unsigned int num_plls);
#endif
---
base-commit: e98d21c170b01ddef366f023bbfcf6b31509fa83
change-id: 20260421-imx8m_pll_debugfs-246d0fbbb617
Best regards,
--
Jacky Bai <ping.bai@nxp.com>
^ permalink raw reply related
* [PATCH v4 07/13] dma-direct: make dma_direct_map_phys() honor DMA_ATTR_CC_SHARED
From: Aneesh Kumar K.V (Arm) @ 2026-05-12 9:04 UTC (permalink / raw)
To: iommu, linux-arm-kernel, linux-kernel, linux-coco
Cc: Aneesh Kumar K.V (Arm), Robin Murphy, Marek Szyprowski,
Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
Catalin Marinas, Jiri Pirko, Jason Gunthorpe, Mostafa Saleh,
Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260512090408.794195-1-aneesh.kumar@kernel.org>
Teach dma_direct_map_phys() to select the DMA address encoding based on
DMA_ATTR_CC_SHARED.
Use phys_to_dma_unencrypted() for decrypted mappings and
phys_to_dma_encrypted() otherwise. If a device requires unencrypted DMA
but the source physical address is still encrypted, force the mapping
through swiotlb so the DMA address and backing memory attributes remain
consistent.
Update the arm64, x86, s390 and powerpc secure-guest setup to not use
swiotlb force option
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
Changes from v3:
* Handle DMA_ATTR_MMIO
---
arch/arm64/mm/init.c | 4 +--
arch/powerpc/platforms/pseries/svm.c | 2 +-
arch/s390/mm/init.c | 2 +-
arch/x86/kernel/pci-dma.c | 4 +--
kernel/dma/direct.c | 4 ++-
kernel/dma/direct.h | 38 +++++++++++++---------------
6 files changed, 24 insertions(+), 30 deletions(-)
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 97987f850a33..acf67c7064db 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -338,10 +338,8 @@ void __init arch_mm_preinit(void)
unsigned int flags = SWIOTLB_VERBOSE;
bool swiotlb = max_pfn > PFN_DOWN(arm64_dma_phys_limit);
- if (is_realm_world()) {
+ if (is_realm_world())
swiotlb = true;
- flags |= SWIOTLB_FORCE;
- }
if (IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC) && !swiotlb) {
/*
diff --git a/arch/powerpc/platforms/pseries/svm.c b/arch/powerpc/platforms/pseries/svm.c
index 384c9dc1899a..7a403dbd35ee 100644
--- a/arch/powerpc/platforms/pseries/svm.c
+++ b/arch/powerpc/platforms/pseries/svm.c
@@ -29,7 +29,7 @@ static int __init init_svm(void)
* need to use the SWIOTLB buffer for DMA even if dma_capable() says
* otherwise.
*/
- ppc_swiotlb_flags |= SWIOTLB_ANY | SWIOTLB_FORCE;
+ ppc_swiotlb_flags |= SWIOTLB_ANY;
/* Share the SWIOTLB buffer with the host. */
swiotlb_update_mem_attributes();
diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c
index 1f72efc2a579..843dbd445124 100644
--- a/arch/s390/mm/init.c
+++ b/arch/s390/mm/init.c
@@ -149,7 +149,7 @@ static void __init pv_init(void)
virtio_set_mem_acc_cb(virtio_require_restricted_mem_acc);
/* make sure bounce buffers are shared */
- swiotlb_init(true, SWIOTLB_FORCE | SWIOTLB_VERBOSE);
+ swiotlb_init(true, SWIOTLB_VERBOSE);
swiotlb_update_mem_attributes();
}
diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c
index 6267363e0189..75cf8f6ae8cd 100644
--- a/arch/x86/kernel/pci-dma.c
+++ b/arch/x86/kernel/pci-dma.c
@@ -59,10 +59,8 @@ static void __init pci_swiotlb_detect(void)
* bounce buffers as the hypervisor can't access arbitrary VM memory
* that is not explicitly shared with it.
*/
- if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
+ if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT))
x86_swiotlb_enable = true;
- x86_swiotlb_flags |= SWIOTLB_FORCE;
- }
}
#else
static inline void __init pci_swiotlb_detect(void)
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index ac315dd046c4..5aaa813c5509 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -691,8 +691,10 @@ size_t dma_direct_max_mapping_size(struct device *dev)
{
/* If SWIOTLB is active, use its maximum mapping size */
if (is_swiotlb_active(dev) &&
- (dma_addressing_limited(dev) || is_swiotlb_force_bounce(dev)))
+ (dma_addressing_limited(dev) || is_swiotlb_force_bounce(dev) ||
+ force_dma_unencrypted(dev)))
return swiotlb_max_mapping_size(dev);
+
return SIZE_MAX;
}
diff --git a/kernel/dma/direct.h b/kernel/dma/direct.h
index e05dc7649366..4e35264ab6f8 100644
--- a/kernel/dma/direct.h
+++ b/kernel/dma/direct.h
@@ -89,36 +89,32 @@ static inline dma_addr_t dma_direct_map_phys(struct device *dev,
dma_addr_t dma_addr;
if (is_swiotlb_force_bounce(dev)) {
- if (!(attrs & DMA_ATTR_CC_SHARED)) {
- if (attrs & (DMA_ATTR_MMIO | DMA_ATTR_REQUIRE_COHERENT))
- return DMA_MAPPING_ERROR;
+ if (attrs & (DMA_ATTR_MMIO | DMA_ATTR_REQUIRE_COHERENT))
+ return DMA_MAPPING_ERROR;
- return swiotlb_map(dev, phys, size, dir, attrs);
- }
- } else if (attrs & DMA_ATTR_CC_SHARED) {
- return DMA_MAPPING_ERROR;
+ return swiotlb_map(dev, phys, size, dir, attrs);
}
- if (attrs & DMA_ATTR_MMIO) {
- dma_addr = phys;
- if (unlikely(!dma_capable(dev, dma_addr, size, false, attrs)))
- goto err_overflow;
- } else if (attrs & DMA_ATTR_CC_SHARED) {
+ if (attrs & DMA_ATTR_CC_SHARED)
dma_addr = phys_to_dma_unencrypted(dev, phys);
+ else
+ dma_addr = phys_to_dma_encrypted(dev, phys);
+
+ if (attrs & DMA_ATTR_MMIO) {
if (unlikely(!dma_capable(dev, dma_addr, size, false, attrs)))
goto err_overflow;
- } else {
- dma_addr = phys_to_dma(dev, phys);
- if (unlikely(!dma_capable(dev, dma_addr, size, true, attrs)) ||
- dma_kmalloc_needs_bounce(dev, size, dir)) {
- if (is_swiotlb_active(dev) &&
- !(attrs & DMA_ATTR_REQUIRE_COHERENT))
- return swiotlb_map(dev, phys, size, dir, attrs);
+ goto dma_mapped;
+ }
- goto err_overflow;
- }
+ if (unlikely(!dma_capable(dev, dma_addr, size, true, attrs)) ||
+ dma_kmalloc_needs_bounce(dev, size, dir)) {
+ if (is_swiotlb_active(dev) &&
+ !(attrs & DMA_ATTR_REQUIRE_COHERENT))
+ return swiotlb_map(dev, phys, size, dir, attrs);
+ goto err_overflow;
}
+dma_mapped:
if (!dev_is_dma_coherent(dev) &&
!(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO))) {
arch_sync_dma_for_device(phys, size, dir);
--
2.43.0
^ permalink raw reply related
* [PATCH v4 08/13] dma-direct: set decrypted flag for remapped DMA allocations
From: Aneesh Kumar K.V (Arm) @ 2026-05-12 9:04 UTC (permalink / raw)
To: iommu, linux-arm-kernel, linux-kernel, linux-coco
Cc: Aneesh Kumar K.V (Arm), Robin Murphy, Marek Szyprowski,
Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
Catalin Marinas, Jiri Pirko, Jason Gunthorpe, Mostafa Saleh,
Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260512090408.794195-1-aneesh.kumar@kernel.org>
Devices that are DMA non-coherent and require a remap were skipping
dma_set_decrypted(), leaving DMA buffers encrypted even when the device
requires unencrypted access. Move the call after the if (remap) branch
so that both the direct and remapped allocation paths correctly mark the
allocation as decrypted (or fail cleanly) before use.
Fix dma_direct_alloc() and dma_direct_free() to apply set_memory_*() to
the linear-map alias of the backing pages instead of the remapped CPU
address. Also disallow highmem pages for DMA_ATTR_CC_SHARED, because
highmem buffers do not provide a usable linear-map address.
Fixes: f3c962226dbe ("dma-direct: clean up the remapping checks in dma_direct_alloc")
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 56 ++++++++++++++++++++++++++++++++++++---------
1 file changed, 45 insertions(+), 11 deletions(-)
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 5aaa813c5509..f5da6e992d83 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -204,6 +204,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
{
bool remap = false, set_uncached = false;
bool mark_mem_decrypt = false;
+ bool allow_highmem = true;
struct page *page;
void *ret;
@@ -222,6 +223,15 @@ void *dma_direct_alloc(struct device *dev, size_t size,
mark_mem_decrypt = true;
}
+ if (attrs & DMA_ATTR_CC_SHARED)
+ /*
+ * Unencrypted/shared DMA requires a linear-mapped buffer
+ * address to look up the PFN and set architecture-required PFN
+ * attributes. This is not possible with HighMem. Avoid HighMem
+ * allocation.
+ */
+ allow_highmem = false;
+
size = PAGE_ALIGN(size);
if (attrs & DMA_ATTR_NO_WARN)
gfp |= __GFP_NOWARN;
@@ -280,7 +290,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
}
/* we always manually zero the memory once we are done */
- page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, true);
+ page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, allow_highmem);
if (!page)
return NULL;
@@ -295,6 +305,14 @@ void *dma_direct_alloc(struct device *dev, size_t size,
set_uncached = false;
}
+ if (mark_mem_decrypt) {
+ void *lm_addr;
+
+ lm_addr = page_address(page);
+ if (set_memory_decrypted((unsigned long)lm_addr, PFN_UP(size)))
+ goto out_leak_pages;
+ }
+
if (remap) {
pgprot_t prot = dma_pgprot(dev, PAGE_KERNEL, attrs);
@@ -305,31 +323,39 @@ void *dma_direct_alloc(struct device *dev, size_t size,
ret = dma_common_contiguous_remap(page, size, prot,
__builtin_return_address(0));
if (!ret)
- goto out_free_pages;
+ goto out_encrypt_pages;
} else {
ret = page_address(page);
- if (mark_mem_decrypt && dma_set_decrypted(dev, ret, size))
- goto out_leak_pages;
}
memset(ret, 0, size);
if (set_uncached) {
+ void *uncached_cpu_addr;
+
arch_dma_prep_coherent(page, size);
- ret = arch_dma_set_uncached(ret, size);
- if (IS_ERR(ret))
- goto out_encrypt_pages;
+ uncached_cpu_addr = arch_dma_set_uncached(ret, size);
+ if (IS_ERR(uncached_cpu_addr))
+ goto out_free_remap_pages;
+ ret = uncached_cpu_addr;
}
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
return ret;
+
+out_free_remap_pages:
+ if (remap)
+ dma_common_free_remap(ret, size);
+
out_encrypt_pages:
- if (mark_mem_decrypt && dma_set_encrypted(dev, page_address(page), size))
- return NULL;
-out_free_pages:
+ if (mark_mem_decrypt &&
+ dma_set_encrypted(dev, page_address(page), size))
+ goto out_leak_pages;
+
__dma_direct_free_pages(dev, page, size);
return NULL;
+
out_leak_pages:
return NULL;
}
@@ -384,8 +410,16 @@ void dma_direct_free(struct device *dev, size_t size,
} else {
if (IS_ENABLED(CONFIG_ARCH_HAS_DMA_CLEAR_UNCACHED))
arch_dma_clear_uncached(cpu_addr, size);
- if (mark_mem_encrypted && dma_set_encrypted(dev, cpu_addr, size))
+ }
+
+ if (mark_mem_encrypted) {
+ void *lm_addr;
+
+ lm_addr = phys_to_virt(dma_to_phys(dev, dma_addr));
+ if (set_memory_encrypted((unsigned long)lm_addr, PFN_UP(size))) {
+ pr_warn_ratelimited("leaking DMA memory that can't be re-encrypted\n");
return;
+ }
}
__dma_direct_free_pages(dev, dma_direct_to_page(dev, dma_addr), size);
--
2.43.0
^ permalink raw reply related
* [PATCH v4 09/13] dma-direct: select DMA address encoding from DMA_ATTR_CC_SHARED
From: Aneesh Kumar K.V (Arm) @ 2026-05-12 9:04 UTC (permalink / raw)
To: iommu, linux-arm-kernel, linux-kernel, linux-coco
Cc: Aneesh Kumar K.V (Arm), Robin Murphy, Marek Szyprowski,
Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
Catalin Marinas, Jiri Pirko, Jason Gunthorpe, Mostafa Saleh,
Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260512090408.794195-1-aneesh.kumar@kernel.org>
Make the dma-direct helpers derive the DMA address encoding from
DMA_ATTR_CC_SHARED instead of implicitly relying on
force_dma_unencrypted() inside phys_to_dma_direct()
Pass an explicit unencrypted/decrypted state into phys_to_dma_direct(),
make the alloc paths return DMA addresses that match the requested
buffer encryption state. Also only call dma_set_decrypted()
DMA_ATTR_CC_SHARED is actually set.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 48 ++++++++++++++++++++++++++++-----------------
1 file changed, 30 insertions(+), 18 deletions(-)
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index f5da6e992d83..1e9f9ff7b9d3 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -24,11 +24,11 @@
u64 zone_dma_limit __ro_after_init = DMA_BIT_MASK(24);
static inline dma_addr_t phys_to_dma_direct(struct device *dev,
- phys_addr_t phys)
+ phys_addr_t phys, bool unencrypted)
{
- if (force_dma_unencrypted(dev))
+ if (unencrypted)
return phys_to_dma_unencrypted(dev, phys);
- return phys_to_dma(dev, phys);
+ return phys_to_dma_encrypted(dev, phys);
}
static inline struct page *dma_direct_to_page(struct device *dev,
@@ -39,8 +39,9 @@ static inline struct page *dma_direct_to_page(struct device *dev,
u64 dma_direct_get_required_mask(struct device *dev)
{
+ bool require_decrypted = force_dma_unencrypted(dev);
phys_addr_t phys = (phys_addr_t)(max_pfn - 1) << PAGE_SHIFT;
- u64 max_dma = phys_to_dma_direct(dev, phys);
+ u64 max_dma = phys_to_dma_direct(dev, phys, require_decrypted);
return (1ULL << (fls64(max_dma) - 1)) * 2 - 1;
}
@@ -69,7 +70,8 @@ static gfp_t dma_direct_optimal_gfp_mask(struct device *dev, u64 *phys_limit)
bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
{
- dma_addr_t dma_addr = phys_to_dma_direct(dev, phys);
+ bool require_decrypted = force_dma_unencrypted(dev);
+ dma_addr_t dma_addr = phys_to_dma_direct(dev, phys, require_decrypted);
if (dma_addr == DMA_MAPPING_ERROR)
return false;
@@ -79,17 +81,18 @@ bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
static int dma_set_decrypted(struct device *dev, void *vaddr, size_t size)
{
- if (!force_dma_unencrypted(dev))
- return 0;
- return set_memory_decrypted((unsigned long)vaddr, PFN_UP(size));
+ int ret;
+
+ ret = set_memory_decrypted((unsigned long)vaddr, PFN_UP(size));
+ if (ret)
+ pr_warn_ratelimited("leaking DMA memory that can't be decrypted\n");
+ return ret;
}
static int dma_set_encrypted(struct device *dev, void *vaddr, size_t size)
{
int ret;
- if (!force_dma_unencrypted(dev))
- return 0;
ret = set_memory_encrypted((unsigned long)vaddr, PFN_UP(size));
if (ret)
pr_warn_ratelimited("leaking DMA memory that can't be re-encrypted\n");
@@ -177,7 +180,8 @@ static void *dma_direct_alloc_from_pool(struct device *dev, size_t size,
dma_coherent_ok);
if (!page)
return NULL;
- *dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
+ *dma_handle = phys_to_dma_direct(dev, page_to_phys(page),
+ !!(attrs & DMA_ATTR_CC_SHARED));
return ret;
}
@@ -193,9 +197,11 @@ static void *dma_direct_alloc_no_mapping(struct device *dev, size_t size,
/* remove any dirty cache lines on the kernel alias */
if (!PageHighMem(page))
arch_dma_prep_coherent(page, size);
-
- /* return the page pointer as the opaque cookie */
- *dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
+ /*
+ * return the page pointer as the opaque cookie.
+ * Never used for unencrypted allocation
+ */
+ *dma_handle = phys_to_dma_encrypted(dev, page_to_phys(page));
return page;
}
@@ -340,7 +346,8 @@ void *dma_direct_alloc(struct device *dev, size_t size,
ret = uncached_cpu_addr;
}
- *dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
+ *dma_handle = phys_to_dma_direct(dev, page_to_phys(page),
+ !!(attrs & DMA_ATTR_CC_SHARED));
return ret;
@@ -457,11 +464,12 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
return NULL;
ret = page_address(page);
- if (dma_set_decrypted(dev, ret, size))
+ if ((attrs & DMA_ATTR_CC_SHARED) && dma_set_decrypted(dev, ret, size))
goto out_leak_pages;
setup_page:
memset(ret, 0, size);
- *dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
+ *dma_handle = phys_to_dma_direct(dev, page_to_phys(page),
+ !!(attrs & DMA_ATTR_CC_SHARED));
return page;
out_leak_pages:
return NULL;
@@ -471,8 +479,12 @@ void dma_direct_free_pages(struct device *dev, size_t size,
struct page *page, dma_addr_t dma_addr,
enum dma_data_direction dir)
{
+ /*
+ * if the device had requested for an unencrypted buffer,
+ * convert it to encrypted on free
+ */
+ bool mark_mem_encrypted = force_dma_unencrypted(dev);
void *vaddr = page_address(page);
- bool mark_mem_encrypted = true;
/* If cpu_addr is not from an atomic pool, dma_free_from_pool() fails */
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
--
2.43.0
^ permalink raw reply related
* [PATCH v4 10/13] dma-pool: fix page leak in atomic_pool_expand() cleanup
From: Aneesh Kumar K.V (Arm) @ 2026-05-12 9:04 UTC (permalink / raw)
To: iommu, linux-arm-kernel, linux-kernel, linux-coco
Cc: Aneesh Kumar K.V (Arm), Robin Murphy, Marek Szyprowski,
Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
Catalin Marinas, Jiri Pirko, Jason Gunthorpe, Mostafa Saleh,
Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260512090408.794195-1-aneesh.kumar@kernel.org>
atomic_pool_expand() frees the allocated pages from the remove_mapping
error path only when CONFIG_DMA_DIRECT_REMAP is enabled.
When CONFIG_DMA_DIRECT_REMAP is disabled, failures after page allocation,
such as gen_pool_add_virt(), jump to remove_mapping and return without
freeing the pages.
Move __free_pages(page, order) out of the CONFIG_DMA_DIRECT_REMAP block so
that cleanup paths always release the allocation.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/pool.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c
index 75f0eba48a23..5abd30c5119f 100644
--- a/kernel/dma/pool.c
+++ b/kernel/dma/pool.c
@@ -149,8 +149,8 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
#ifdef CONFIG_DMA_DIRECT_REMAP
dma_common_free_remap(addr, pool_size);
free_page:
- __free_pages(page, order);
#endif
+ __free_pages(page, order);
out:
return ret;
}
--
2.43.0
^ permalink raw reply related
* Re: [PATCH 4/4] drm/mediatek: mtk_hdmi_ddc: Fix non-static global variable
From: CK Hu (胡俊光) @ 2026-05-12 9:06 UTC (permalink / raw)
To: chunkuang.hu@kernel.org, simona@ffwll.ch, Alexandre Mergnat,
AngeloGioacchino Del Regno, airlied@gmail.com,
p.zabel@pengutronix.de, matthias.bgg@gmail.com,
Louis-Alexis Eyraud
Cc: dri-devel@lists.freedesktop.org,
linux-mediatek@lists.infradead.org,
linux-arm-kernel@lists.infradead.org, kernel@collabora.com,
linux-kernel@vger.kernel.org
In-Reply-To: <20260429-mediatek-drm-fix-sparse-warnings-v1-4-d95c4d118b83@collabora.com>
On Wed, 2026-04-29 at 11:59 +0200, Louis-Alexis Eyraud wrote:
> The struct 'mtk_hdmi_ddc_driver' is not used outside of the
> mtk_hdmi_ddc.c file, so make it static to silence sparse warning:
> ```
> drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c:331:24: sparse: warning: symbol
> 'mtk_hdmi_ddc_driver' was not declared. Should it be static?
> ```
Reviewed-by: CK Hu <ck.hu@mediatek.com>
>
> Fixes: c241118b6216 ("drm/mediatek: mtk_hdmi_ddc: Switch to register as module_platform_driver")
> Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
> ---
> drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c
> index 6358e1af69b4..2acbdb025d89 100644
> --- a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c
> +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c
> @@ -328,7 +328,7 @@ static const struct of_device_id mtk_hdmi_ddc_match[] = {
> };
> MODULE_DEVICE_TABLE(of, mtk_hdmi_ddc_match);
>
> -struct platform_driver mtk_hdmi_ddc_driver = {
> +static struct platform_driver mtk_hdmi_ddc_driver = {
> .probe = mtk_hdmi_ddc_probe,
> .remove = mtk_hdmi_ddc_remove,
> .driver = {
>
^ permalink raw reply
* [PATCH v4 11/13] dma-direct: rename ret to cpu_addr in alloc helpers
From: Aneesh Kumar K.V (Arm) @ 2026-05-12 9:04 UTC (permalink / raw)
To: iommu, linux-arm-kernel, linux-kernel, linux-coco
Cc: Aneesh Kumar K.V (Arm), Robin Murphy, Marek Szyprowski,
Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
Catalin Marinas, Jiri Pirko, Jason Gunthorpe, Mostafa Saleh,
Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260512090408.794195-1-aneesh.kumar@kernel.org>
ret in dma_direct_alloc() and dma_direct_alloc_pages() holds the returned
CPU mapping, not a generic return value. Rename it to cpu_addr and update
the remaining uses to match.
This makes the allocation paths easier to follow and keeps the local naming
consistent with what the variable actually represents.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 31 +++++++++++++++----------------
1 file changed, 15 insertions(+), 16 deletions(-)
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 1e9f9ff7b9d3..902008e40ea1 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -212,7 +212,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
bool mark_mem_decrypt = false;
bool allow_highmem = true;
struct page *page;
- void *ret;
+ void *cpu_addr;
/*
* DMA_ATTR_CC_SHARED is not a caller-visible dma_alloc_*()
@@ -326,34 +326,33 @@ void *dma_direct_alloc(struct device *dev, size_t size,
arch_dma_prep_coherent(page, size);
/* create a coherent mapping */
- ret = dma_common_contiguous_remap(page, size, prot,
- __builtin_return_address(0));
- if (!ret)
+ cpu_addr = dma_common_contiguous_remap(page, size, prot,
+ __builtin_return_address(0));
+ if (!cpu_addr)
goto out_encrypt_pages;
} else {
- ret = page_address(page);
+ cpu_addr = page_address(page);
}
- memset(ret, 0, size);
+ memset(cpu_addr, 0, size);
if (set_uncached) {
void *uncached_cpu_addr;
arch_dma_prep_coherent(page, size);
- uncached_cpu_addr = arch_dma_set_uncached(ret, size);
+ uncached_cpu_addr = arch_dma_set_uncached(cpu_addr, size);
if (IS_ERR(uncached_cpu_addr))
goto out_free_remap_pages;
- ret = uncached_cpu_addr;
+ cpu_addr = uncached_cpu_addr;
}
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page),
!!(attrs & DMA_ATTR_CC_SHARED));
- return ret;
-
+ return cpu_addr;
out_free_remap_pages:
if (remap)
- dma_common_free_remap(ret, size);
+ dma_common_free_remap(cpu_addr, size);
out_encrypt_pages:
if (mark_mem_decrypt &&
@@ -437,7 +436,7 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
{
unsigned long attrs = 0;
struct page *page;
- void *ret;
+ void *cpu_addr;
if (force_dma_unencrypted(dev))
attrs |= DMA_ATTR_CC_SHARED;
@@ -455,7 +454,7 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
swiotlb_free(dev, page, size);
return NULL;
}
- ret = page_address(page);
+ cpu_addr = page_address(page);
goto setup_page;
}
@@ -463,11 +462,11 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
if (!page)
return NULL;
- ret = page_address(page);
- if ((attrs & DMA_ATTR_CC_SHARED) && dma_set_decrypted(dev, ret, size))
+ cpu_addr = page_address(page);
+ if ((attrs & DMA_ATTR_CC_SHARED) && dma_set_decrypted(dev, cpu_addr, size))
goto out_leak_pages;
setup_page:
- memset(ret, 0, size);
+ memset(cpu_addr, 0, size);
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page),
!!(attrs & DMA_ATTR_CC_SHARED));
return page;
--
2.43.0
^ permalink raw reply related
* [PATCH v4 12/13] dma-direct: return struct page from dma_direct_alloc_from_pool()
From: Aneesh Kumar K.V (Arm) @ 2026-05-12 9:04 UTC (permalink / raw)
To: iommu, linux-arm-kernel, linux-kernel, linux-coco
Cc: Aneesh Kumar K.V (Arm), Robin Murphy, Marek Szyprowski,
Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
Catalin Marinas, Jiri Pirko, Jason Gunthorpe, Mostafa Saleh,
Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86, stable
In-Reply-To: <20260512090408.794195-1-aneesh.kumar@kernel.org>
Commit 5b138c534fda ("dma-direct: factor out a dma_direct_alloc_from_pool
helper") changed dma_direct_alloc_from_pool() to return the CPU address
from dma_alloc_from_pool(). That fits dma_direct_alloc(), but
dma_direct_alloc_pages() also uses the helper and expects a struct page *.
Fix this by making dma_direct_alloc_from_pool() return the struct page *
again, and pass the CPU address back through an out-parameter for the
dma_direct_alloc() caller.
Fixes: 5b138c534fda ("dma-direct: factor out a dma_direct_alloc_from_pool helper")
Cc: stable@vger.kernel.org
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 902008e40ea1..5103a04df99f 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -165,24 +165,24 @@ static bool dma_direct_use_pool(struct device *dev, gfp_t gfp)
return !gfpflags_allow_blocking(gfp) && !is_swiotlb_for_alloc(dev);
}
-static void *dma_direct_alloc_from_pool(struct device *dev, size_t size,
- dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
+static struct page *dma_direct_alloc_from_pool(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, void **cpu_addr, gfp_t gfp,
+ unsigned long attrs)
{
struct page *page;
u64 phys_limit;
- void *ret;
if (WARN_ON_ONCE(!IS_ENABLED(CONFIG_DMA_COHERENT_POOL)))
return NULL;
gfp |= dma_direct_optimal_gfp_mask(dev, &phys_limit);
- page = dma_alloc_from_pool(dev, size, &ret, gfp, attrs,
+ page = dma_alloc_from_pool(dev, size, cpu_addr, gfp, attrs,
dma_coherent_ok);
if (!page)
return NULL;
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page),
!!(attrs & DMA_ATTR_CC_SHARED));
- return ret;
+ return page;
}
static void *dma_direct_alloc_no_mapping(struct device *dev, size_t size,
@@ -278,9 +278,12 @@ void *dma_direct_alloc(struct device *dev, size_t size,
* the atomic pools instead if we aren't allowed block.
*/
if ((remap || (attrs & DMA_ATTR_CC_SHARED)) &&
- dma_direct_use_pool(dev, gfp))
- return dma_direct_alloc_from_pool(dev, size, dma_handle,
- gfp, attrs);
+ dma_direct_use_pool(dev, gfp)) {
+ page = dma_direct_alloc_from_pool(dev, size,
+ dma_handle, &cpu_addr,
+ gfp, attrs);
+ return page ? cpu_addr : NULL;
+ }
if (is_swiotlb_for_alloc(dev)) {
page = dma_direct_alloc_swiotlb(dev, size, attrs);
@@ -443,7 +446,7 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
if ((attrs & DMA_ATTR_CC_SHARED) && dma_direct_use_pool(dev, gfp))
return dma_direct_alloc_from_pool(dev, size, dma_handle,
- gfp, attrs);
+ &cpu_addr, gfp, attrs);
if (is_swiotlb_for_alloc(dev)) {
page = dma_direct_alloc_swiotlb(dev, size, attrs);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH 2/2] drm/verisilicon: add support for Nuvoton MA35D1 DCUltra Lite display controller
From: Joey Lu @ 2026-05-12 9:06 UTC (permalink / raw)
To: Icenowy Zheng, maarten.lankhorst, mripard, tzimmermann, airlied,
simona, robh, krzk+dt, conor+dt
Cc: ychuang3, schung, yclu4, dri-devel, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <3b94806073de8bd1d79aa7ec956493f67679e46b.camel@iscas.ac.cn>
On 5/12/2026 4:11 PM, Icenowy Zheng wrote:
> 在 2026-05-12二的 15:45 +0800,Joey Lu写道:
>> On 5/11/2026 5:47 PM, Icenowy Zheng wrote:
>>> 在 2026-05-11一的 15:51 +0800,Joey Lu写道:
>>>> The Nuvoton MA35D1 SoC integrates a Verisilicon DCUltra Lite
>>>> display
>>>> controller, which is a previous generation of the DC8000 series.
>>>> While
>>>> the general register layout is similar to the DC8000, there are
>>>> several
>>>> key differences that require per-variant handling in the driver.
>>>>
>>>> Add a vs_dc_info platform data structure (in vs_hwdb.h) to
>>>> describe
>>>> per-IP-variant capabilities, and use it throughout the driver to
>>>> select
>>>> the correct code paths at runtime.
>>>>
>>>> Key differences between DC8000 and DCUltra Lite handled:
>>> What the driver supports now is DC8200, DC8000 have the following
>>> point
>>> 1~4 the same with DCUltraLite (different to DC8200).
>> Understood. I'll rename all `vs_dc8000_*` identifiers to
>> `vs_dc8200_*` in v2. The commit message will also be corrected to say
>> that points 1~4 are differences from DC8200, not DC8000.
>>>> 1. No chip identity registers (0x0020-0x0030): DCUltra Lite uses
>>>> static
>>>> platform data instead of reading model/revision/customer_id
>>>> from
>>>> HW.
>>> My test shows that revision and customer_id is correctly present,
>>> only
>>> model is 0 -- I think this can be also considered as a valid model
>>> value because the IP name has also no model number.
>>>
>>> The revision number is 0x5560 and customer id is 0x305 .
>> Thank you for testing. I'll drop the `has_chip_id` flag
>> entirely. In v2, `vs_fill_chip_identity()` will be called for all
>> variants. A new entry will be added to `vs_chip_identities[]` in
>> vs_hwdb.c with model=0x0, revision=0x5560, customer_id=0x305,
>> display_count=1, and `vs_formats_no_yuv444`.
>> e.g. verisilicon-dc 40260000.display: Found DC0 rev 5560 customer 305
>>>> 2. No CONFIG_EX commit mechanism: DC8000 uses registers at 0x1CC0
>>>> (FB_CONFIG_EX), 0x24D8 (FB_TOP_LEFT), 0x24E0
>>>> (FB_BOTTOM_RIGHT),
>>>> 0x2510 (FB_BLEND_CONFIG), 0x2518 (PANEL_CONFIG_EX). DCUltra
>>>> Lite
>>>> omits all of these and instead uses enable/reset bits in
>>>> FB_CONFIG
>>>> (bit 0 = enable, bit 4 = reset) for direct framebuffer
>>>> updates.
>>>>
>>>> 3. No PANEL_START register (0x1CCC): DCUltra Lite panel output
>>>> starts
>>>> when PANEL_CONFIG.RUNNING is set; no separate multi-display
>>>> sync
>>>> start register is needed.
>>>>
>>>> 4. Different IRQ registers: DCUltra Lite uses 0x147C (IRQ_STA) /
>>>> 0x1480 (IRQ_EN); DC8000 uses 0x0010 (IRQ_ACK) / 0x0014
>>>> (IRQ_EN).
>>>>
>>>> 5. Different clock/reset topology: DCUltra Lite requires only
>>>> "core"
>>>> (bus gate) and "pix0" (pixel divider) clocks with no reset
>>>> lines
>>>> managed by the driver. DC8000 needs core/axi/ahb clocks and
>>>> three
>>>> resets.
>>> It's possible that your SoC integration combines core clock gate
>>> with
>>> bus clock gate instead of bus clock gate not existing.
>> Agreed. In v2 I'll remove the family-gated clock handling and
>> instead use `devm_clk_get_optional_enabled()` for the axi and ahb
>> clocks, so they are simply skipped if not present in DT. Resets are
>> already optional. This keeps the probe path uniform and handles any
>> SoC-specific clock combinations naturally.
> Maybe this could be splitted as another patch, to make single commits
> smaller.
Got it.
>>>> 6. Single output only: DCUltra Lite has one display output; per-
>>>> output
>>>> index logic is still in place but display_count is fixed at
>>>> 1.
>>>>
>>>> 7. Reduced register space: max_register is 0x2000 vs DC8000's
>>>> 0x2544.
>>>>
>>>> Add the "nuvoton,ma35d1-dcu" compatible string to the OF match
>>>> table,
>>>> extend Kconfig to allow building on ARCH_MA35 platforms, and
>>>> expose
>>>> vs_formats_no_yuv444 as the default format table for DCUltra Lite
>>>> (YUV444 blending is a DC8000-only feature).
>>>>
>>>> All changes have been tested on Nuvoton MA35D1 hardware and are
>>>> functioning correctly.
>>>>
>>>> Signed-off-by: Joey Lu <a0987203069@gmail.com>
>>>> ---
>>>> drivers/gpu/drm/verisilicon/Kconfig | 2 +-
>>>> drivers/gpu/drm/verisilicon/vs_bridge.c | 28 ++--
>>>> drivers/gpu/drm/verisilicon/vs_crtc.c | 13 +-
>>>> drivers/gpu/drm/verisilicon/vs_dc.c | 129
>>>> ++++++++++++----
>>>> --
>>>> drivers/gpu/drm/verisilicon/vs_dc.h | 1 +
>>>> drivers/gpu/drm/verisilicon/vs_drm.c | 16 ++-
>>>> drivers/gpu/drm/verisilicon/vs_hwdb.c | 2 +-
>>>> drivers/gpu/drm/verisilicon/vs_hwdb.h | 25 ++++
>>>> .../gpu/drm/verisilicon/vs_primary_plane.c | 43 +++---
>>>> .../drm/verisilicon/vs_primary_plane_regs.h | 2 +
>>>> 10 files changed, 187 insertions(+), 74 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/verisilicon/Kconfig
>>>> b/drivers/gpu/drm/verisilicon/Kconfig
>>>> index 7cce86ec8603..295d246eb4b4 100644
>>>> --- a/drivers/gpu/drm/verisilicon/Kconfig
>>>> +++ b/drivers/gpu/drm/verisilicon/Kconfig
>>>> @@ -2,7 +2,7 @@
>>>> config DRM_VERISILICON_DC
>>>> tristate "DRM Support for Verisilicon DC-series display
>>>> controllers"
>>>> depends on DRM && COMMON_CLK
>>>> - depends on RISCV || COMPILE_TEST
>>>> + depends on RISCV || ARCH_MA35 || COMPILE_TEST
>>>> select DRM_BRIDGE_CONNECTOR
>>>> select DRM_CLIENT_SELECTION
>>>> select DRM_DISPLAY_HELPER
>>>> diff --git a/drivers/gpu/drm/verisilicon/vs_bridge.c
>>>> b/drivers/gpu/drm/verisilicon/vs_bridge.c
>>>> index 7a93049368db..225af322de32 100644
>>>> --- a/drivers/gpu/drm/verisilicon/vs_bridge.c
>>>> +++ b/drivers/gpu/drm/verisilicon/vs_bridge.c
>>>> @@ -164,13 +164,16 @@ static void vs_bridge_enable_common(struct
>>>> vs_crtc *crtc,
>>>> VSDC_DISP_PANEL_CONFIG_CLK_EN);
>>>> regmap_set_bits(dc->regs,
>>>> VSDC_DISP_PANEL_CONFIG(output),
>>>> VSDC_DISP_PANEL_CONFIG_RUNNING);
>>>> - regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
>>>> -
>>>> VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
>>>> - regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
>>>> - VSDC_DISP_PANEL_START_RUNNING(output));
>>>>
>>>> - regmap_set_bits(dc->regs,
>>>> VSDC_DISP_PANEL_CONFIG_EX(crtc-
>>>>> id),
>>>> - VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
>>>> + if (dc->info->has_config_ex) {
>>>> + regmap_clear_bits(dc->regs,
>>>> VSDC_DISP_PANEL_START,
>>>> +
>>>> VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
>>>> + regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
>>>> + VSDC_DISP_PANEL_START_RUNNING(ou
>>>> tput
>>>> ));
>>>> +
>>>> + regmap_set_bits(dc->regs,
>>>> VSDC_DISP_PANEL_CONFIG_EX(crtc->id),
>>>> + VSDC_DISP_PANEL_CONFIG_EX_COMMIT
>>>> );
>>> Should the commit operation happen on DC8000/DCUltraLite too? (By
>>> writing to DcregFrameBufferConfig0.VALID).
>>>
>>> Many registers written has "Note: This field is double buffered" in
>>> the
>>> DCUltraLite documentation.
>>>
>>> I suggest create a static function for commit -- write to the
>>> corresponding commit bit on DC8200, and write to
>>> DcregFrameBufferConfig0.VALID on DC8000/DCUltraLite.
>> [a] There is no commit operation for DCUltra Lite.
>> I'll not add a `VSDC_FB_CONFIG_VALID` macro. VALID (BIT(3)) is a
>> hardware-managed double-buffer status bit: hardware writes 1=PENDING
>> when a new register set is ready and clears to 0=WORKING after the
>> VBLANK copy. Software must never write it, and there is no polling
>> use
> It seems to be writable and controls whether register buffering is
> enabled, see [1].
>
> The description of this bit in MA35D1 TRM says "This ensures a frame
> will always start with a valid working set if this register is
> programmed last, which reduces the need for SW to wait for the start of
> a VBLANK signal in order to ensure all states are loaded before the
> next VBLANK", which indicates some kind of "committing write", although
> the code at [1] seems to indicate that double buffering is only enabled
> when bit is cleared.
>
> Anyway this bit should be programmable, and "Software must never write
> it" contradicts with the MA35D1 TRM.
>
> Thanks,
> Icenowy
>
> [1]
> https://github.com/rockos-riscv/rockos-kernel/blob/rockos-v6.6.y/drivers/gpu/drm/eswin/es_dc_hw.c#L993
Thank you for the correction. I'll add
`#define VSDC_FB_CONFIG_VALID BIT(3)` to vs_primary_plane_regs.h and
write it in `vs_primary_plane_commit()` for non-config_ex variants.
>> case in the driver that requires a named constant. For non-config_ex
>> variants, `vs_primary_plane_commit()` performs no commit operation —
>> `VSDC_FB_CONFIG_ENABLE` (OUTPUT, BIT(0)) is set in
>> `vs_crtc_atomic_enable()` and `VSDC_FB_CONFIG_RESET` (BIT(4)) is
>> set/cleared in the bridge enable/disable paths.
> ========= 8< ==========
>
^ permalink raw reply
* [PATCH v4 13/13] x86/amd-gart: preserve the direct DMA address until GART mapping succeeds
From: Aneesh Kumar K.V (Arm) @ 2026-05-12 9:04 UTC (permalink / raw)
To: iommu, linux-arm-kernel, linux-kernel, linux-coco
Cc: Aneesh Kumar K.V (Arm), Robin Murphy, Marek Szyprowski,
Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
Catalin Marinas, Jiri Pirko, Jason Gunthorpe, Mostafa Saleh,
Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260512090408.794195-1-aneesh.kumar@kernel.org>
gart_alloc_coherent() first allocates memory through dma_direct_alloc(),
which returns a direct-mapped DMA address in dma_addr. When force_iommu is
enabled, the buffer is then remapped.
Do not overwrite dma_addr before dma_map_area() has succeeded. Keep the
dma_map_area result in a temporary variable so the direct DMA address
remains available for dma_direct_free() on the error path.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/x86/kernel/amd_gart_64.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/arch/x86/kernel/amd_gart_64.c b/arch/x86/kernel/amd_gart_64.c
index b5f1f031d45b..a109649c5649 100644
--- a/arch/x86/kernel/amd_gart_64.c
+++ b/arch/x86/kernel/amd_gart_64.c
@@ -467,18 +467,20 @@ gart_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_addr,
gfp_t flag, unsigned long attrs)
{
void *vaddr;
+ dma_addr_t dma_map_addr;
vaddr = dma_direct_alloc(dev, size, dma_addr, flag, attrs);
if (!vaddr ||
!force_iommu || dev->coherent_dma_mask <= DMA_BIT_MASK(24))
return vaddr;
- *dma_addr = dma_map_area(dev, virt_to_phys(vaddr), size,
- DMA_BIDIRECTIONAL,
- (1UL << get_order(size)) - 1, attrs);
+ dma_map_addr = dma_map_area(dev, virt_to_phys(vaddr), size,
+ DMA_BIDIRECTIONAL,
+ (1UL << get_order(size)) - 1, attrs);
flush_gart();
- if (unlikely(*dma_addr == DMA_MAPPING_ERROR))
+ if (unlikely(dma_map_addr == DMA_MAPPING_ERROR))
goto out_free;
+ *dma_addr = dma_map_addr;
return vaddr;
out_free:
dma_direct_free(dev, size, vaddr, *dma_addr, attrs);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH 2/2] drm/verisilicon: add support for Nuvoton MA35D1 DCUltra Lite display controller
From: Joey Lu @ 2026-05-12 9:10 UTC (permalink / raw)
To: Thomas Zimmermann, zhengxingda, maarten.lankhorst, mripard,
airlied, simona, robh, krzk+dt, conor+dt
Cc: ychuang3, schung, yclu4, dri-devel, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <5157ab4d-3195-49f3-a3e9-aef6d58e869e@suse.de>
On 5/12/2026 4:24 PM, Thomas Zimmermann wrote:
> Hi,
>
> instead of this if-else branchery, I strongly recommend to add a new
> file with a new mode-setting pipeline for the new chipset. You can
> share existing helpers where possible, and implement variants where
> chips differ. It's a bit more code, but will be a lot easier to
> maintain in the future.
>
> See ast and mgag200 for example. Both drivers support various
> revisions of their chipset, where each rev has its oddities.
>
> Best regards
> Thomas
Thank you for the detailed suggestion.
I see your point and will refactor by adding a new file with a
mode-setting pipeline for the chipset, reusing helpers where possible
and handling variants as needed.
>
> Am 11.05.26 um 09:51 schrieb Joey Lu:
>> The Nuvoton MA35D1 SoC integrates a Verisilicon DCUltra Lite display
>> controller, which is a previous generation of the DC8000 series. While
>> the general register layout is similar to the DC8000, there are several
>> key differences that require per-variant handling in the driver.
>>
>> Add a vs_dc_info platform data structure (in vs_hwdb.h) to describe
>> per-IP-variant capabilities, and use it throughout the driver to select
>> the correct code paths at runtime.
>>
>> Key differences between DC8000 and DCUltra Lite handled:
>>
>> 1. No chip identity registers (0x0020-0x0030): DCUltra Lite uses static
>> platform data instead of reading model/revision/customer_id from HW.
>>
>> 2. No CONFIG_EX commit mechanism: DC8000 uses registers at 0x1CC0
>> (FB_CONFIG_EX), 0x24D8 (FB_TOP_LEFT), 0x24E0 (FB_BOTTOM_RIGHT),
>> 0x2510 (FB_BLEND_CONFIG), 0x2518 (PANEL_CONFIG_EX). DCUltra Lite
>> omits all of these and instead uses enable/reset bits in FB_CONFIG
>> (bit 0 = enable, bit 4 = reset) for direct framebuffer updates.
>>
>> 3. No PANEL_START register (0x1CCC): DCUltra Lite panel output starts
>> when PANEL_CONFIG.RUNNING is set; no separate multi-display sync
>> start register is needed.
>>
>> 4. Different IRQ registers: DCUltra Lite uses 0x147C (IRQ_STA) /
>> 0x1480 (IRQ_EN); DC8000 uses 0x0010 (IRQ_ACK) / 0x0014 (IRQ_EN).
>>
>> 5. Different clock/reset topology: DCUltra Lite requires only "core"
>> (bus gate) and "pix0" (pixel divider) clocks with no reset lines
>> managed by the driver. DC8000 needs core/axi/ahb clocks and three
>> resets.
>>
>> 6. Single output only: DCUltra Lite has one display output; per-output
>> index logic is still in place but display_count is fixed at 1.
>>
>> 7. Reduced register space: max_register is 0x2000 vs DC8000's 0x2544.
>>
>> Add the "nuvoton,ma35d1-dcu" compatible string to the OF match table,
>> extend Kconfig to allow building on ARCH_MA35 platforms, and expose
>> vs_formats_no_yuv444 as the default format table for DCUltra Lite
>> (YUV444 blending is a DC8000-only feature).
>>
>> All changes have been tested on Nuvoton MA35D1 hardware and are
>> functioning correctly.
>>
>> Signed-off-by: Joey Lu <a0987203069@gmail.com>
>> ---
>> drivers/gpu/drm/verisilicon/Kconfig | 2 +-
>> drivers/gpu/drm/verisilicon/vs_bridge.c | 28 ++--
>> drivers/gpu/drm/verisilicon/vs_crtc.c | 13 +-
>> drivers/gpu/drm/verisilicon/vs_dc.c | 129 ++++++++++++------
>> drivers/gpu/drm/verisilicon/vs_dc.h | 1 +
>> drivers/gpu/drm/verisilicon/vs_drm.c | 16 ++-
>> drivers/gpu/drm/verisilicon/vs_hwdb.c | 2 +-
>> drivers/gpu/drm/verisilicon/vs_hwdb.h | 25 ++++
>> .../gpu/drm/verisilicon/vs_primary_plane.c | 43 +++---
>> .../drm/verisilicon/vs_primary_plane_regs.h | 2 +
>> 10 files changed, 187 insertions(+), 74 deletions(-)
>>
>>
>
^ permalink raw reply
* Re: [PATCH 2/2] arm64: dts: mediatek: mt8188-geralt: enable Wi-Fi card
From: Chen-Yu Tsai @ 2026-05-12 9:10 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Linus Walleij, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Matthias Brugger, AngeloGioacchino Del Regno, Hui Liu, linux-gpio,
devicetree, linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <4a43edb8c7ae3f96f77272db89ec6450ffa28876.camel@iscas.ac.cn>
On Wed, May 6, 2026 at 10:14 PM Icenowy Zheng <zhengxingda@iscas.ac.cn> wrote:
>
> 在 2026-05-04一的 15:34 +0800,Chen-Yu Tsai写道:
> > Hi,
> >
> > On Mon, May 4, 2026 at 3:28 PM Icenowy Zheng
> > <zhengxingda@iscas.ac.cn> wrote:
> > >
> > > The mainline pcie-mediatek-gen3 driver does not have code managing
> > > downstream device power / reset.
> > >
> > > As the Wi-Fi card on ciri is a fixed device, set the related
> > > regulator
> > > to always-on and use GPIO hog to set the status of its reset pin.
> >
> > The plan now is to model it as an M.2 E-key slot (even though the
> > chip
> > is actually soldered on the main board).
>
> Interestingly I saw a "PCI_PWRCTRL_GENERIC" driver in 7.1, although it
> does not support toggling #PERST now -- maybe this should be done and
> used instead? (Well it looks like the driver had existed for some time,
> but it was for "slots" previously)
#PERST currently is still left to the PCI controller drivers, since some
of them have dedicated functions for it, while others use the GPIO API.
If you check the history of the "PCI pwrctrl framework" [1], you will
see the design choice.
[1] https://lore.kernel.org/all/20260115-pci-pwrctrl-rework-v5-0-9d26da3ce903@oss.qualcomm.com/
> Thanks,
> Icenowy
>
> >
> > I have some of the patches ready, but I'm still working out the USB
> > side of it.
> >
> >
> > ChenYu
> >
> > > Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
> > > ---
> > > arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi | 11 +++++++++++
> > > 1 file changed, 11 insertions(+)
> > >
> > > diff --git a/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi
> > > b/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi
> > > index 8e423504ec052..c25780098103b 100644
> > > --- a/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi
> > > +++ b/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi
> > > @@ -544,6 +544,11 @@ &mt6359codec {
> > > mediatek,mic-type-2 = <2>; /* DMIC */
> > > };
> > >
> > > +&mt6359_vcn18_ldo_reg {
> > > + /* Used by WLAN */
> > > + regulator-always-on;
> > > +};
> > > +
> > > &mt6359_vcore_buck_reg {
> > > regulator-always-on;
> > > };
> > > @@ -1145,6 +1150,12 @@ pins-en-pp3300-wlan {
> > > output-low;
> > > };
> > > };
> > > +
> > > + wlan-reset-hog {
> > > + gpio-hog;
> > > + gpios = <145 GPIO_ACTIVE_HIGH>;
> > > + output-high;
> > > + };
> > > };
> > >
> > > &pmic {
> > > --
> > > 2.52.0
> > >
> > >
>
^ permalink raw reply
* [PATCH v2] drm/bridge: imx93-mipi-dsi: Fix mode validation
From: Liu Ying @ 2026-05-12 9:18 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Luca Ceresoli
Cc: Dmitry Baryshkov, dri-devel, imx, linux-arm-kernel, linux-kernel,
Liu Ying
i.MX93 MIPI DPHY PLL has limitation for matching with some pixel clock
rates, e.g., the best DPHY PLL frequency is 445.333333MHz for a typical
1920x1080p@60Hz CEA/DMT display modes with a pixel clock rate running
at 148.5MHz with 4 data lanes + RGB888 pixel in MIPI DSI sync pulse mode,
while the expected PLL frequency is (148.5 * 24) / 4 / 2 MHz = 445.5MHz.
Fortunately, VESA Display Monitor Timing Standard allows +/-0.5% pixel
clock rate deviation for timings. So, for those display modes read
from EDID through a bridge with DRM_BRIDGE_OP_DETECT and DRM_BRIDGE_OP_EDID
operation bit masks set, pixel clock rate could be adjusted to match
with the PLL frequency(for the above example, the pixel clock rate is
adjusted to be 148.444444MHz with about -0.03% deviation from the 148.5MHz
nominal rate so that the adjusted rate matches with the 445.333333MHz PLL
frequency).
Instead of checking the last bridge's operation bit masks against
DRM_BRIDGE_OP_DETECT and DRM_BRIDGE_OP_EDID to determine if allowing
+/-0.5% pixel clock rate deviation, check any bridge after this bridge,
because the last bridge is usually a display connector bridge without
any operation bit mask when the clock rate deviation is allowed.
Fixes: ce62f8ea7e3f ("drm/bridge: imx: Add i.MX93 MIPI DSI support")
Fixes: 5849eff7f067 ("drm/bridge: imx93-mipi-dsi: use drm_bridge_chain_get_last_bridge()")
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
Changes in v2:
- Collect Frank's R-b tag.
- Add an explanation to commit message about the reason why mode validation
checks bridge's operation bit masks. (Dmitry)
- Copy Dmitry.
- Link to v1: https://lore.kernel.org/r/20260227-imx93-mipi-dsi-fix-mode-validation-v1-1-a9cd67991280@nxp.com
To: Liu Ying <victor.liu@nxp.com>
To: Andrzej Hajda <andrzej.hajda@intel.com>
To: Neil Armstrong <neil.armstrong@linaro.org>
To: Robert Foss <rfoss@kernel.org>
To: Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
To: Jonas Karlman <jonas@kwiboo.se>
To: Jernej Skrabec <jernej.skrabec@gmail.com>
To: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
To: Maxime Ripard <mripard@kernel.org>
To: Thomas Zimmermann <tzimmermann@suse.de>
To: David Airlie <airlied@gmail.com>
To: Simona Vetter <simona@ffwll.ch>
To: Frank Li <Frank.Li@nxp.com>
To: Sascha Hauer <s.hauer@pengutronix.de>
To: Pengutronix Kernel Team <kernel@pengutronix.de>
To: Fabio Estevam <festevam@gmail.com>
To: Luca Ceresoli <luca.ceresoli@bootlin.com>
Cc: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Cc: dri-devel@lists.freedesktop.org
Cc: imx@lists.linux.dev
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
---
drivers/gpu/drm/bridge/imx/imx93-mipi-dsi.c | 29 ++++++++++++++++-------------
1 file changed, 16 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/bridge/imx/imx93-mipi-dsi.c b/drivers/gpu/drm/bridge/imx/imx93-mipi-dsi.c
index 8f312f9edf97..6d65df9ed970 100644
--- a/drivers/gpu/drm/bridge/imx/imx93-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/imx/imx93-mipi-dsi.c
@@ -493,21 +493,24 @@ static enum drm_mode_status
imx93_dsi_validate_mode(struct imx93_dsi *dsi, const struct drm_display_mode *mode)
{
struct drm_bridge *dmd_bridge = dw_mipi_dsi_get_bridge(dsi->dmd);
- struct drm_bridge *last_bridge __free(drm_bridge_put) =
- drm_bridge_chain_get_last_bridge(dmd_bridge->encoder);
- if ((last_bridge->ops & DRM_BRIDGE_OP_DETECT) &&
- (last_bridge->ops & DRM_BRIDGE_OP_EDID)) {
- unsigned long pixel_clock_rate = mode->clock * 1000;
- unsigned long rounded_rate;
+ drm_for_each_bridge_in_chain_from(dmd_bridge, bridge) {
+ if ((bridge->ops & DRM_BRIDGE_OP_DETECT) &&
+ (bridge->ops & DRM_BRIDGE_OP_EDID)) {
+ unsigned long pixel_clock_rate = mode->clock * 1000;
+ unsigned long rounded_rate;
- /* Allow +/-0.5% pixel clock rate deviation */
- rounded_rate = clk_round_rate(dsi->clk_pixel, pixel_clock_rate);
- if (rounded_rate < pixel_clock_rate * 995 / 1000 ||
- rounded_rate > pixel_clock_rate * 1005 / 1000) {
- dev_dbg(dsi->dev, "failed to round clock for mode " DRM_MODE_FMT "\n",
- DRM_MODE_ARG(mode));
- return MODE_NOCLOCK;
+ /* Allow +/-0.5% pixel clock rate deviation */
+ rounded_rate = clk_round_rate(dsi->clk_pixel, pixel_clock_rate);
+ if (rounded_rate < pixel_clock_rate * 995 / 1000 ||
+ rounded_rate > pixel_clock_rate * 1005 / 1000) {
+ dev_dbg(dsi->dev,
+ "failed to round clock for mode " DRM_MODE_FMT "\n",
+ DRM_MODE_ARG(mode));
+ return MODE_NOCLOCK;
+ }
+
+ break;
}
}
---
base-commit: 877552aa875839314afad7154b5a561889e87ea9
change-id: 20260227-imx93-mipi-dsi-fix-mode-validation-425c872a2493
Best regards,
--
Regards,
Liu Ying
^ permalink raw reply related
* Re: [PATCH v3 1/5] arm_mpam: resctrl: Pick classes for use as mbm counters
From: Ben Horgan @ 2026-05-12 9:21 UTC (permalink / raw)
To: Shaopeng Tan (Fujitsu)
Cc: amitsinght@marvell.com, baisheng.gao@unisoc.com,
baolin.wang@linux.alibaba.com, carl@os.amperecomputing.com,
dave.martin@arm.com, david@kernel.org, dfustini@baylibre.com,
fenghuay@nvidia.com, gshan@redhat.com, james.morse@arm.com,
jonathan.cameron@huawei.com, kobak@nvidia.com,
lcherian@marvell.com, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, peternewman@google.com,
punit.agrawal@oss.qualcomm.com, quic_jiles@quicinc.com,
reinette.chatre@intel.com, rohit.mathew@arm.com,
scott@os.amperecomputing.com, sdonthineni@nvidia.com,
xhao@linux.alibaba.com, zengheng4@huawei.com, x86@kernel.org
In-Reply-To: <TY4PR01MB1693044EDBBF023317F9059A98B392@TY4PR01MB16930.jpnprd01.prod.outlook.com>
Hi Shaopeng,
On 5/12/26 07:50, Shaopeng Tan (Fujitsu) wrote:
> Hello Ben,
>
>> From: James Morse <james.morse@arm.com>
>>
>> resctrl has two types of counters, NUMA-local and global. MPAM can only
>> count global either using MSC at the L3 cache or in the memory controllers.
>> When global and local equate to the same thing continue just to call it
>> global.
>>
>> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
>> Tested-by: Zeng Heng <zengheng4@huawei.com>
>> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
>> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
>> Signed-off-by: James Morse <james.morse@arm.com>
>> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
>> ---
>> Changes since rfc v1:
>> Move finding any_mon_comp into monitor boilerplate patch
>> Move mpam_resctrl_get_domain_from_cpu() into monitor boilerplate
>> Remove free running check
>> Trim commit message
>> ---
>> drivers/resctrl/mpam_resctrl.c | 26 ++++++++++++++++++++++++++
>> 1 file changed, 26 insertions(+)
>>
>> diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
>> index 226ff6f532fa..f70fa65d39e4 100644
>> --- a/drivers/resctrl/mpam_resctrl.c
>> +++ b/drivers/resctrl/mpam_resctrl.c
>> @@ -606,6 +606,16 @@ static bool cache_has_usable_csu(struct mpam_class *class)
>> return true;
>> }
>>
>> +static bool class_has_usable_mbwu(struct mpam_class *class)
>> +{
>> + struct mpam_props *cprops = &class->props;
>> +
>> + if (!mpam_has_feature(mpam_feat_msmon_mbwu, cprops))
>> + return false;
>> +
>> + return true;
>> +}
>> +
>> /*
>> * Calculate the worst-case percentage change from each implemented step
>> * in the control.
>> @@ -983,6 +993,22 @@ static void mpam_resctrl_pick_counters(void)
>> break;
>> }
>> }
>> +
>> + if (class_has_usable_mbwu(class) &&
>> + topology_matches_l3(class) &&
>> + traffic_matches_l3(class)) {
>> + pr_debug("class %u has usable MBWU, and matches L3 topology and traffic\n",
>> + class->level);
>> +
>> + /*
>> + * We can't distinguish traffic by destination so
>> + * we don't know if it's staying on the same NUMA
>> + * node. Hence, we can't calculate mbm_local except
>> + * when we only have one L3 and it's equivalent to
>> + * mbm_total and so always use mbm_total.
>> + */
>> + counter_update_class(QOS_L3_MBM_TOTAL_EVENT_ID, class);
>> + }
>> }
>> }
>>
>> --
>> 2.43.0
>
> https://lore.kernel.org/lkml/599617aa-aade-4fde-9efa-79d592f1ff3f@arm.com/
>
> This concerns the comment I received last time.
> I may not have fully understood it, so I'd like to clarify it once more.
I'll try and explain better.
>
> Even if the system as a whole has multiple L3 caches and multiple NUMA nodes,
> ABMC will be enabled as long as there is a single L3 cache and a single corresponding NUMA node.
> Is my understanding correct?
> If my understanding is correct, within the 'traffic_matches_l3()' function,
> ABMC is enabled only when the entire system has a single NUMA node and a single L3 cache.
These restrictions only apply when the MSC containing the bandwidth counters is
at the memory, as advertised by ACPI. When the counters are on the L3 cache
there can be multiple L3 and multiple NUMA nodes and a domain with AMBC memory
bandwidth counters will be exposed for each instance of the L3 cache.
As resctrl, currently, expects all monitors to be on the L3 cache we can only
use counters if they can be considered to be at the L3. The user just sees an
L3_MON directory. When the monitors/counters are at the memory we can only
pretend they are at the L3 when there is a single L3 and a single NUMA node.
This is because the topology needs to match, the same cpus are affine to the L3
instance and corresponding NUMA instance and the traffic measured also needs to
match. If there is multiple NUMA and L3 then cross NUMA traffic means that the
traffic seen at the NUMA node is different from what is seen at the L3.
If a workload runs on cpus affine to the L3, instance A, but allows cross NUMA
traffic then the memory bandwidth leaving the L3, instance A, will be different
from that entering NUMA instance A.
L3_A --> NUMA_A
\
\
🡖
L3_B NUMA_B
This is still the case if the traffic goes via the L3 to the other NUMA node.
L3_A --> NUMA_A
|
|
⌄
L3_B --> NUMA_B
The future plan, is to add support for monitoring scoped to the NUMA node in
resctrl. This means we can we can more accurately expose the counters later on
without being held back by inaccurate descriptions. Ideally, we would have added
proper support for monitors at the memory scoped by NUMA node rather than adding
traffic_matches_l3() and topology_matches_l3(), which are there to allow us to
support platforms where the traffic entering the memory controller is the same
as that leaving the L3. To cope with the case when memory is powered down we
need to introduce memory hotplug locking to resctrl as well as the support for
understanding the NUMA scope.
>
> 870 static bool traffic_matches_l3(struct mpam_class *class)
> 871 {
> ...
> 901
> 902 if (!cpumask_equal(tmp_cpumask, cpu_possible_mask)) {
> 903 pr_debug("There is more than one L3\n");
> 904 return false; *
> 905 }
> ...
> 912
> 913 if (num_possible_nodes() > 1) {
> 914 pr_debug("There is more than one numa node\n");
> 915 return false; *
> 916 }
> 917
> ...
> 926 }
>
>
> Also, I'd also like to confirm one more thing.
> The mpam_resctrl_pick_mba() function also calls traffic_matches_l3().
> This suggests that, except in scenarios where the entire system has a single L3 cache and a single NUMA node (ABMC is disabled),
> the Memory Bandwidth allocation will also be disabled.
> Is this the intended behavior? If so, could you explain why?
Yes, but only when the memory allocation control, mbw_max, is on the memory
controller and not the L3 cache. There is no restriction when the MSC is on the
L3 cache. This is for the same reasons as for the memory bandwidth counters. The
traffic that we say is coming from the L3 may not be the same as that entering
the memory controller and resctrl assumes that the MBA is at the L3. Memory
hotplug locking in resctrl would also be needed here.
Reinete is creating a proof of concept for a structured way to add new schemata
into resctrl, some discussion of initial ideas [1]. I'm also looking to see how
the generic schemata ideas can fit with MPAM and what resctrl support we'd require.
Zeng has indicated [2] that he might look into adding the MB support at NUMA nodes.
I hope this makes things a bit clearer.
[1] https://lore.kernel.org/lkml/fb1e2686-237b-4536-acd6-15159abafcba@intel.com/
[2]
https://lore.kernel.org/linux-arm-kernel/f6f865bc-319c-8944-9989-4fd83a59d4b8@huawei.com/
Thanks,
Ben
>
> Best regards,
> Shaopeng TAN
>
>
>
>
^ permalink raw reply
* Re: [PATCH 3/4] remoteproc: add helper for optional ELF resource tables
From: Arnaud POULIQUEN @ 2026-05-12 9:22 UTC (permalink / raw)
To: Daniel Baluta, Ben Levinsky, Bjorn Andersson, Mathieu Poirier,
linux-remoteproc
Cc: Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Geert Uytterhoeven, Magnus Damm, Patrice Chotard, Maxime Coquelin,
Alexandre Torgue, imx, linux-arm-kernel, linux-kernel,
linux-renesas-soc, linux-stm32, tanmay.shah
In-Reply-To: <3b7f009c-dc4b-4fc0-becc-4d07eb4ff016@oss.nxp.com>
On 5/12/26 09:55, Daniel Baluta wrote:
> On 5/12/26 00:18, Ben Levinsky wrote:
>> [You don't often get email from ben.levinsky@amd.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>>
>> Add a small helper around rproc_elf_load_rsc_table() for remoteproc
>> drivers that treat a missing ELF resource table as optional. The helper
>> returns success on -EINVAL and propagates other failures unchanged.
>>
>> Signed-off-by: Ben Levinsky <ben.levinsky@amd.com>
>> ---
>> drivers/remoteproc/remoteproc_internal.h | 12 ++++++++++++
>> 1 file changed, 12 insertions(+)
>>
>> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
>> index 3724a47a9748..dff87e468837 100644
>> --- a/drivers/remoteproc/remoteproc_internal.h
>> +++ b/drivers/remoteproc/remoteproc_internal.h
>> @@ -146,6 +146,18 @@ static inline int rproc_mem_entry_iounmap(struct rproc *rproc,
>> return 0;
>> }
>>
>> +static inline int rproc_elf_load_rsc_table_optional(struct rproc *rproc,
>> + const struct firmware *fw)
>> +{
>> + int ret;
>> +
>> + ret = rproc_elf_load_rsc_table(rproc, fw);
>> + if (ret == -EINVAL)
>> + dev_dbg(&rproc->dev, "no resource table found\n");
>
> You are changing loglevel here. Initial drivers use dev_info or dev_warn. At least I'm used
> with seeing this messages in the logs.
>
> So, what do you think on adding at least dev_info to this instead of dev_dbg?
+1 for dev_info (dev_warn is used in stm32_rproc_parse_fw(), but ok to
move to dev_info)
Regards,
Arnaud
>
>> +
>> + return ret == -EINVAL ? 0 : ret;
>> +}
>> +
>> static inline int rproc_prepare_device(struct rproc *rproc)
>> {
>> if (rproc->ops->prepare)
>> --
>> 2.34.1
>>
>>
>
>
^ permalink raw reply
* [PATCH] arm64: dts: rockchip: fix emmc reset polarity on px30-cobra
From: Jakob Unterwurzacher @ 2026-05-12 9:22 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner,
Quentin Schulz, Jakob Unterwurzacher
Cc: stable, Heiko Stuebner, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
Technically, the reset signal is active low - it's called RST_n after all.
But it is ignored completely unless RST_n_FUNCTION=1 (byte 162 in extcsd)
is set in the emmc. It is 0 per default.
For emmcs that have RST_n_FUNCTION=1 we failed like this:
[ 3.074480] mmc1: Failed to initialize a non-removable card
With this change they work normally.
Cc: stable@vger.kernel.org
Fixes: bb510ddc9d3e ("arm64: dts: rockchip: add px30-cobra base dtsi and board variants")
Signed-off-by: Jakob Unterwurzacher <jakob.unterwurzacher@cherry.de>
---
arch/arm64/boot/dts/rockchip/px30-cobra.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi b/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi
index b7e669d8ba4d..90751b04f95c 100644
--- a/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi
+++ b/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi
@@ -35,7 +35,7 @@ emmc_pwrseq: emmc-pwrseq {
compatible = "mmc-pwrseq-emmc";
pinctrl-0 = <&emmc_reset>;
pinctrl-names = "default";
- reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_LOW>;
};
gpio-leds {
--
2.47.3
^ permalink raw reply related
* Re: [PATCH v4 2/2] drm/mediatek: dsi: Add compatible for mt8167-dsi
From: CK Hu (胡俊光) @ 2026-05-12 9:26 UTC (permalink / raw)
To: linux-mediatek@lists.infradead.org, l.scorcia@gmail.com
Cc: dri-devel@lists.freedesktop.org, chunkuang.hu@kernel.org,
simona@ffwll.ch, AngeloGioacchino Del Regno, robh@kernel.org,
airlied@gmail.com, krzk+dt@kernel.org,
linux-arm-kernel@lists.infradead.org, p.zabel@pengutronix.de,
conor+dt@kernel.org, matthias.bgg@gmail.com,
linux-kernel@vger.kernel.org, devicetree@vger.kernel.org
In-Reply-To: <20260505214541.333657-3-l.scorcia@gmail.com>
On Tue, 2026-05-05 at 22:45 +0100, Luca Leonardo Scorcia wrote:
> External email : Please do not click links or open attachments until you have verified the sender or the content.
>
>
> The mt8167 DSI controller is fully compatible with the one found in
> mt2701. Unfortunately the device tree has a dedicated compatible for
> mt8167 since 2022 and it cannot be changed with a fallback nor removed at
> this point. The only way to get the device to work is to add the
> compatible to the driver.
Reviewed-by: CK Hu <ck.hu@mediatek.com>
>
> Signed-off-by: Luca Leonardo Scorcia <l.scorcia@gmail.com>
> ---
> drivers/gpu/drm/mediatek/mtk_dsi.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
> index 5aa71fcdcfab..167e33fef025 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dsi.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
> @@ -1305,6 +1305,7 @@ static const struct mtk_dsi_driver_data mt8188_dsi_driver_data = {
>
> static const struct of_device_id mtk_dsi_of_match[] = {
> { .compatible = "mediatek,mt2701-dsi", .data = &mt2701_dsi_driver_data },
> + { .compatible = "mediatek,mt8167-dsi", .data = &mt2701_dsi_driver_data },
> { .compatible = "mediatek,mt8173-dsi", .data = &mt8173_dsi_driver_data },
> { .compatible = "mediatek,mt8183-dsi", .data = &mt8183_dsi_driver_data },
> { .compatible = "mediatek,mt8186-dsi", .data = &mt8186_dsi_driver_data },
> --
> 2.43.0
>
>
^ permalink raw reply
* [PATCH v5] media: verisilicon: Create AV1 helper library
From: Benjamin Gaignard @ 2026-05-12 7:59 UTC (permalink / raw)
To: nicolas.dufresne, p.zabel, mchehab, heiko
Cc: linux-kernel, linux-media, linux-rockchip, linux-arm-kernel,
kernel, Benjamin Gaignard
Regroup all none hardware related AV1 functions into a helper library.
The goal is to avoid code duplication for future AV1 codecs.
Tested on rock 5b board Fluster score remains the same 204/241.
Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
---
version 5:
- be more strict about checking arrays range and the value returned
by hantro_av1_get_frame_index()
- rebased on v7.1-rc3 tag
version 4:
- change functions prototypes to use a structure.
- rebased on v7.1-rc2 tag.
drivers/media/platform/verisilicon/Makefile | 7 +-
.../media/platform/verisilicon/hantro_av1.c | 790 +++++++++++++
.../media/platform/verisilicon/hantro_av1.h | 64 ++
...entropymode.c => hantro_av1_entropymode.c} | 18 +-
...entropymode.h => hantro_av1_entropymode.h} | 18 +-
...av1_filmgrain.c => hantro_av1_filmgrain.c} | 125 +-
.../verisilicon/hantro_av1_filmgrain.h | 48 +
.../media/platform/verisilicon/hantro_hw.h | 7 +-
.../verisilicon/rockchip_av1_filmgrain.h | 36 -
.../verisilicon/rockchip_vpu981_hw_av1_dec.c | 1014 ++---------------
.../platform/verisilicon/rockchip_vpu_hw.c | 7 +-
11 files changed, 1115 insertions(+), 1019 deletions(-)
create mode 100644 drivers/media/platform/verisilicon/hantro_av1.c
create mode 100644 drivers/media/platform/verisilicon/hantro_av1.h
rename drivers/media/platform/verisilicon/{rockchip_av1_entropymode.c => hantro_av1_entropymode.c} (99%)
rename drivers/media/platform/verisilicon/{rockchip_av1_entropymode.h => hantro_av1_entropymode.h} (95%)
rename drivers/media/platform/verisilicon/{rockchip_av1_filmgrain.c => hantro_av1_filmgrain.c} (85%)
create mode 100644 drivers/media/platform/verisilicon/hantro_av1_filmgrain.h
delete mode 100644 drivers/media/platform/verisilicon/rockchip_av1_filmgrain.h
diff --git a/drivers/media/platform/verisilicon/Makefile b/drivers/media/platform/verisilicon/Makefile
index f6f019d04ff0..a1dd6c2d29be 100644
--- a/drivers/media/platform/verisilicon/Makefile
+++ b/drivers/media/platform/verisilicon/Makefile
@@ -19,7 +19,10 @@ hantro-vpu-y += \
hantro_hevc.o \
hantro_mpeg2.o \
hantro_vp8.o \
- hantro_vp9.o
+ hantro_vp9.o \
+ hantro_av1.o \
+ hantro_av1_filmgrain.o \
+ hantro_av1_entropymode.o
hantro-vpu-$(CONFIG_VIDEO_HANTRO_IMX8M) += \
imx8m_vpu_hw.o
@@ -33,8 +36,6 @@ hantro-vpu-$(CONFIG_VIDEO_HANTRO_ROCKCHIP) += \
rockchip_vpu2_hw_mpeg2_dec.o \
rockchip_vpu2_hw_vp8_dec.o \
rockchip_vpu981_hw_av1_dec.o \
- rockchip_av1_filmgrain.o \
- rockchip_av1_entropymode.o \
rockchip_vpu_hw.o
hantro-vpu-$(CONFIG_VIDEO_HANTRO_SUNXI) += \
diff --git a/drivers/media/platform/verisilicon/hantro_av1.c b/drivers/media/platform/verisilicon/hantro_av1.c
new file mode 100644
index 000000000000..ac7ae8e87f50
--- /dev/null
+++ b/drivers/media/platform/verisilicon/hantro_av1.c
@@ -0,0 +1,790 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026, Collabora
+ *
+ * Author: Benjamin Gaignard <benjamin.gaignard@collabora.com>
+ */
+
+#include <linux/types.h>
+#include <media/v4l2-h264.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "hantro.h"
+#include "hantro_av1.h"
+#include "hantro_hw.h"
+
+#define GM_GLOBAL_MODELS_PER_FRAME 7
+#define GLOBAL_MODEL_TOTAL_SIZE (6 * 4 + 4 * 2)
+#define GLOBAL_MODEL_SIZE ALIGN(GM_GLOBAL_MODELS_PER_FRAME * GLOBAL_MODEL_TOTAL_SIZE, 2048)
+#define AV1_MAX_TILES 128
+#define AV1_TILE_INFO_SIZE (AV1_MAX_TILES * 16)
+#define AV1_TILE_SIZE ALIGN(32 * 128, 4096)
+
+#define SUPERRES_SCALE_BITS 3
+
+#define DIV_LUT_PREC_BITS 14
+#define DIV_LUT_BITS 8
+#define DIV_LUT_NUM BIT(DIV_LUT_BITS)
+#define WARP_PARAM_REDUCE_BITS 6
+#define WARPEDMODEL_PREC_BITS 16
+
+#define AV1_DIV_ROUND_UP_POW2(value, n) \
+({ \
+ typeof(n) _n = n; \
+ typeof(value) _value = value; \
+ (_value + (BIT(_n) >> 1)) >> _n; \
+})
+
+#define AV1_DIV_ROUND_UP_POW2_SIGNED(value, n) \
+({ \
+ typeof(n) _n_ = n; \
+ typeof(value) _value_ = value; \
+ (((_value_) < 0) ? -AV1_DIV_ROUND_UP_POW2(-(_value_), (_n_)) \
+ : AV1_DIV_ROUND_UP_POW2((_value_), (_n_))); \
+})
+
+static const short div_lut[DIV_LUT_NUM + 1] = {
+ 16384, 16320, 16257, 16194, 16132, 16070, 16009, 15948, 15888, 15828, 15768,
+ 15709, 15650, 15592, 15534, 15477, 15420, 15364, 15308, 15252, 15197, 15142,
+ 15087, 15033, 14980, 14926, 14873, 14821, 14769, 14717, 14665, 14614, 14564,
+ 14513, 14463, 14413, 14364, 14315, 14266, 14218, 14170, 14122, 14075, 14028,
+ 13981, 13935, 13888, 13843, 13797, 13752, 13707, 13662, 13618, 13574, 13530,
+ 13487, 13443, 13400, 13358, 13315, 13273, 13231, 13190, 13148, 13107, 13066,
+ 13026, 12985, 12945, 12906, 12866, 12827, 12788, 12749, 12710, 12672, 12633,
+ 12596, 12558, 12520, 12483, 12446, 12409, 12373, 12336, 12300, 12264, 12228,
+ 12193, 12157, 12122, 12087, 12053, 12018, 11984, 11950, 11916, 11882, 11848,
+ 11815, 11782, 11749, 11716, 11683, 11651, 11619, 11586, 11555, 11523, 11491,
+ 11460, 11429, 11398, 11367, 11336, 11305, 11275, 11245, 11215, 11185, 11155,
+ 11125, 11096, 11067, 11038, 11009, 10980, 10951, 10923, 10894, 10866, 10838,
+ 10810, 10782, 10755, 10727, 10700, 10673, 10645, 10618, 10592, 10565, 10538,
+ 10512, 10486, 10460, 10434, 10408, 10382, 10356, 10331, 10305, 10280, 10255,
+ 10230, 10205, 10180, 10156, 10131, 10107, 10082, 10058, 10034, 10010, 9986,
+ 9963, 9939, 9916, 9892, 9869, 9846, 9823, 9800, 9777, 9754, 9732,
+ 9709, 9687, 9664, 9642, 9620, 9598, 9576, 9554, 9533, 9511, 9489,
+ 9468, 9447, 9425, 9404, 9383, 9362, 9341, 9321, 9300, 9279, 9259,
+ 9239, 9218, 9198, 9178, 9158, 9138, 9118, 9098, 9079, 9059, 9039,
+ 9020, 9001, 8981, 8962, 8943, 8924, 8905, 8886, 8867, 8849, 8830,
+ 8812, 8793, 8775, 8756, 8738, 8720, 8702, 8684, 8666, 8648, 8630,
+ 8613, 8595, 8577, 8560, 8542, 8525, 8508, 8490, 8473, 8456, 8439,
+ 8422, 8405, 8389, 8372, 8355, 8339, 8322, 8306, 8289, 8273, 8257,
+ 8240, 8224, 8208, 8192,
+};
+
+enum hantro_av1_tx_mode {
+ HANTRO_AV1_TX_MODE_ONLY_4X4 = 0,
+ HANTRO_AV1_TX_MODE_8X8 = 1,
+ HANTRO_AV1_TX_MODE_16x16 = 2,
+ HANTRO_AV1_TX_MODE_32x32 = 3,
+ HANTRO_AV1_TX_MODE_SELECT = 4,
+};
+
+enum hantro_av1_inter_prediction_filter_type {
+ HANTRO_AV1_EIGHT_TAP_SMOOTH = 0,
+ HANTRO_AV1_EIGHT_TAP = 1,
+ HANTRO_AV1_EIGHT_TAP_SHARP = 2,
+ HANTRO_AV1_BILINEAR = 3,
+ HANTRO_AV1_SWITCHABLE = 4,
+};
+
+int hantro_av1_get_hardware_tx_mode(enum v4l2_av1_tx_mode tx_mode)
+{
+ switch (tx_mode) {
+ case V4L2_AV1_TX_MODE_ONLY_4X4:
+ return HANTRO_AV1_TX_MODE_ONLY_4X4;
+ case V4L2_AV1_TX_MODE_LARGEST:
+ return HANTRO_AV1_TX_MODE_32x32;
+ case V4L2_AV1_TX_MODE_SELECT:
+ return HANTRO_AV1_TX_MODE_SELECT;
+ }
+
+ return HANTRO_AV1_TX_MODE_32x32;
+}
+
+int hantro_av1_get_hardware_mcomp_filt_type(int interpolation_filter)
+{
+ switch (interpolation_filter) {
+ case V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP:
+ return HANTRO_AV1_EIGHT_TAP;
+ case V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP_SMOOTH:
+ return HANTRO_AV1_EIGHT_TAP_SMOOTH;
+ case V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP_SHARP:
+ return HANTRO_AV1_EIGHT_TAP_SHARP;
+ case V4L2_AV1_INTERPOLATION_FILTER_BILINEAR:
+ return HANTRO_AV1_BILINEAR;
+ case V4L2_AV1_INTERPOLATION_FILTER_SWITCHABLE:
+ return HANTRO_AV1_SWITCHABLE;
+ }
+
+ return HANTRO_AV1_EIGHT_TAP_SMOOTH;
+}
+
+int hantro_av1_get_frame_index(struct hantro_ctx *ctx, int ref)
+{
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+ struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+ const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
+ u64 timestamp;
+ int i, idx;
+
+ if (ref < 0 || ref >= V4L2_AV1_REFS_PER_FRAME)
+ return AV1_INVALID_IDX;
+
+ idx = frame->ref_frame_idx[ref];
+
+ if (idx >= V4L2_AV1_TOTAL_REFS_PER_FRAME || idx < 0)
+ return AV1_INVALID_IDX;
+
+ timestamp = frame->reference_frame_ts[idx];
+ for (i = 0; i < AV1_MAX_FRAME_BUF_COUNT; i++) {
+ if (!av1_dec->frame_refs[i].used)
+ continue;
+ if (av1_dec->frame_refs[i].timestamp == timestamp)
+ return i;
+ }
+
+ return AV1_INVALID_IDX;
+}
+
+int hantro_av1_get_order_hint(struct hantro_ctx *ctx, int ref)
+{
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+ int idx = hantro_av1_get_frame_index(ctx, ref);
+
+ if (idx != AV1_INVALID_IDX)
+ return av1_dec->frame_refs[idx].order_hint;
+
+ return 0;
+}
+
+int hantro_av1_frame_ref(struct hantro_ctx *ctx, u64 timestamp)
+{
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+ struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+ const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
+ int i;
+
+ for (i = 0; i < AV1_MAX_FRAME_BUF_COUNT; i++) {
+ int j;
+
+ if (av1_dec->frame_refs[i].used)
+ continue;
+
+ av1_dec->frame_refs[i].width = frame->frame_width_minus_1 + 1;
+ av1_dec->frame_refs[i].height = frame->frame_height_minus_1 + 1;
+ av1_dec->frame_refs[i].mi_cols = DIV_ROUND_UP(frame->frame_width_minus_1 + 1, 8);
+ av1_dec->frame_refs[i].mi_rows = DIV_ROUND_UP(frame->frame_height_minus_1 + 1, 8);
+ av1_dec->frame_refs[i].timestamp = timestamp;
+ av1_dec->frame_refs[i].frame_type = frame->frame_type;
+ av1_dec->frame_refs[i].order_hint = frame->order_hint;
+ av1_dec->frame_refs[i].vb2_ref = hantro_get_dst_buf(ctx);
+
+ for (j = 0; j < V4L2_AV1_TOTAL_REFS_PER_FRAME; j++)
+ av1_dec->frame_refs[i].order_hints[j] = frame->order_hints[j];
+ av1_dec->frame_refs[i].used = true;
+ av1_dec->current_frame_index = i;
+
+ return i;
+ }
+
+ return AV1_INVALID_IDX;
+}
+
+static void hantro_av1_frame_unref(struct hantro_ctx *ctx, int idx)
+{
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+
+ if (idx >= 0)
+ av1_dec->frame_refs[idx].used = false;
+}
+
+void hantro_av1_clean_refs(struct hantro_ctx *ctx)
+{
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+ struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+
+ int ref, idx;
+
+ for (idx = 0; idx < AV1_MAX_FRAME_BUF_COUNT; idx++) {
+ u64 timestamp = av1_dec->frame_refs[idx].timestamp;
+ bool used = false;
+
+ if (!av1_dec->frame_refs[idx].used)
+ continue;
+
+ for (ref = 0; ref < V4L2_AV1_TOTAL_REFS_PER_FRAME; ref++) {
+ if (ctrls->frame->reference_frame_ts[ref] == timestamp)
+ used = true;
+ }
+
+ if (!used)
+ hantro_av1_frame_unref(ctx, idx);
+ }
+}
+
+size_t hantro_av1_luma_size(struct hantro_ctx *ctx)
+{
+ return ctx->ref_fmt.plane_fmt[0].bytesperline * ctx->ref_fmt.height;
+}
+
+size_t hantro_av1_chroma_size(struct hantro_ctx *ctx)
+{
+ size_t cr_offset = hantro_av1_luma_size(ctx);
+
+ return ALIGN((cr_offset * 3) / 2, 64);
+}
+
+static void hantro_av1_tiles_free(struct hantro_ctx *ctx)
+{
+ struct hantro_dev *vpu = ctx->dev;
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+
+ if (av1_dec->db_data_col.cpu)
+ dma_free_coherent(vpu->dev, av1_dec->db_data_col.size,
+ av1_dec->db_data_col.cpu,
+ av1_dec->db_data_col.dma);
+ av1_dec->db_data_col.cpu = NULL;
+
+ if (av1_dec->db_ctrl_col.cpu)
+ dma_free_coherent(vpu->dev, av1_dec->db_ctrl_col.size,
+ av1_dec->db_ctrl_col.cpu,
+ av1_dec->db_ctrl_col.dma);
+ av1_dec->db_ctrl_col.cpu = NULL;
+
+ if (av1_dec->cdef_col.cpu)
+ dma_free_coherent(vpu->dev, av1_dec->cdef_col.size,
+ av1_dec->cdef_col.cpu, av1_dec->cdef_col.dma);
+ av1_dec->cdef_col.cpu = NULL;
+
+ if (av1_dec->sr_col.cpu)
+ dma_free_coherent(vpu->dev, av1_dec->sr_col.size,
+ av1_dec->sr_col.cpu, av1_dec->sr_col.dma);
+ av1_dec->sr_col.cpu = NULL;
+
+ if (av1_dec->lr_col.cpu)
+ dma_free_coherent(vpu->dev, av1_dec->lr_col.size,
+ av1_dec->lr_col.cpu, av1_dec->lr_col.dma);
+ av1_dec->lr_col.cpu = NULL;
+}
+
+static int hantro_av1_tiles_reallocate(struct hantro_ctx *ctx)
+{
+ struct hantro_dev *vpu = ctx->dev;
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+ struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+ const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info;
+ unsigned int num_tile_cols = tile_info->tile_cols;
+ unsigned int height = ALIGN(ctrls->frame->frame_height_minus_1 + 1, 64);
+ unsigned int height_in_sb = height / 64;
+ unsigned int stripe_num = ((height + 8) + 63) / 64;
+ size_t size;
+
+ if (av1_dec->db_data_col.size >=
+ ALIGN(height * 12 * ctx->bit_depth / 8, 128) * num_tile_cols)
+ return 0;
+
+ hantro_av1_tiles_free(ctx);
+
+ size = ALIGN(height * 12 * ctx->bit_depth / 8, 128) * num_tile_cols;
+ av1_dec->db_data_col.cpu = dma_alloc_coherent(vpu->dev, size,
+ &av1_dec->db_data_col.dma,
+ GFP_KERNEL);
+ if (!av1_dec->db_data_col.cpu)
+ goto buffer_allocation_error;
+ av1_dec->db_data_col.size = size;
+
+ size = ALIGN(height * 2 * 16 / 4, 128) * num_tile_cols;
+ av1_dec->db_ctrl_col.cpu = dma_alloc_coherent(vpu->dev, size,
+ &av1_dec->db_ctrl_col.dma,
+ GFP_KERNEL);
+ if (!av1_dec->db_ctrl_col.cpu)
+ goto buffer_allocation_error;
+ av1_dec->db_ctrl_col.size = size;
+
+ size = ALIGN(height_in_sb * 44 * ctx->bit_depth * 16 / 8, 128) * num_tile_cols;
+ av1_dec->cdef_col.cpu = dma_alloc_coherent(vpu->dev, size,
+ &av1_dec->cdef_col.dma,
+ GFP_KERNEL);
+ if (!av1_dec->cdef_col.cpu)
+ goto buffer_allocation_error;
+ av1_dec->cdef_col.size = size;
+
+ size = ALIGN(height_in_sb * (3040 + 1280), 128) * num_tile_cols;
+ av1_dec->sr_col.cpu = dma_alloc_coherent(vpu->dev, size,
+ &av1_dec->sr_col.dma,
+ GFP_KERNEL);
+ if (!av1_dec->sr_col.cpu)
+ goto buffer_allocation_error;
+ av1_dec->sr_col.size = size;
+
+ size = ALIGN(stripe_num * 1536 * ctx->bit_depth / 8, 128) * num_tile_cols;
+ av1_dec->lr_col.cpu = dma_alloc_coherent(vpu->dev, size,
+ &av1_dec->lr_col.dma,
+ GFP_KERNEL);
+ if (!av1_dec->lr_col.cpu)
+ goto buffer_allocation_error;
+ av1_dec->lr_col.size = size;
+
+ av1_dec->num_tile_cols_allocated = num_tile_cols;
+ return 0;
+
+buffer_allocation_error:
+ hantro_av1_tiles_free(ctx);
+ return -ENOMEM;
+}
+
+void hantro_av1_exit(struct hantro_ctx *ctx)
+{
+ struct hantro_dev *vpu = ctx->dev;
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+
+ if (av1_dec->global_model.cpu)
+ dma_free_coherent(vpu->dev, av1_dec->global_model.size,
+ av1_dec->global_model.cpu,
+ av1_dec->global_model.dma);
+ av1_dec->global_model.cpu = NULL;
+
+ if (av1_dec->tile_info.cpu)
+ dma_free_coherent(vpu->dev, av1_dec->tile_info.size,
+ av1_dec->tile_info.cpu,
+ av1_dec->tile_info.dma);
+ av1_dec->tile_info.cpu = NULL;
+
+ if (av1_dec->film_grain.cpu)
+ dma_free_coherent(vpu->dev, av1_dec->film_grain.size,
+ av1_dec->film_grain.cpu,
+ av1_dec->film_grain.dma);
+ av1_dec->film_grain.cpu = NULL;
+
+ if (av1_dec->prob_tbl.cpu)
+ dma_free_coherent(vpu->dev, av1_dec->prob_tbl.size,
+ av1_dec->prob_tbl.cpu, av1_dec->prob_tbl.dma);
+ av1_dec->prob_tbl.cpu = NULL;
+
+ if (av1_dec->prob_tbl_out.cpu)
+ dma_free_coherent(vpu->dev, av1_dec->prob_tbl_out.size,
+ av1_dec->prob_tbl_out.cpu,
+ av1_dec->prob_tbl_out.dma);
+ av1_dec->prob_tbl_out.cpu = NULL;
+
+ if (av1_dec->tile_buf.cpu)
+ dma_free_coherent(vpu->dev, av1_dec->tile_buf.size,
+ av1_dec->tile_buf.cpu, av1_dec->tile_buf.dma);
+ av1_dec->tile_buf.cpu = NULL;
+
+ hantro_av1_tiles_free(ctx);
+}
+
+int hantro_av1_init(struct hantro_ctx *ctx)
+{
+ struct hantro_dev *vpu = ctx->dev;
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+
+ memset(av1_dec, 0, sizeof(*av1_dec));
+
+ av1_dec->global_model.cpu = dma_alloc_coherent(vpu->dev, GLOBAL_MODEL_SIZE,
+ &av1_dec->global_model.dma,
+ GFP_KERNEL);
+ if (!av1_dec->global_model.cpu)
+ return -ENOMEM;
+ av1_dec->global_model.size = GLOBAL_MODEL_SIZE;
+
+ av1_dec->tile_info.cpu = dma_alloc_coherent(vpu->dev, AV1_TILE_INFO_SIZE,
+ &av1_dec->tile_info.dma,
+ GFP_KERNEL);
+ if (!av1_dec->tile_info.cpu)
+ return -ENOMEM;
+ av1_dec->tile_info.size = AV1_TILE_INFO_SIZE;
+
+ av1_dec->film_grain.cpu = dma_alloc_coherent(vpu->dev,
+ ALIGN(sizeof(struct hantro_av1_film_grain),
+ 2048),
+ &av1_dec->film_grain.dma,
+ GFP_KERNEL);
+ if (!av1_dec->film_grain.cpu)
+ return -ENOMEM;
+ av1_dec->film_grain.size = ALIGN(sizeof(struct hantro_av1_film_grain), 2048);
+
+ av1_dec->prob_tbl.cpu = dma_alloc_coherent(vpu->dev,
+ ALIGN(sizeof(struct av1cdfs), 2048),
+ &av1_dec->prob_tbl.dma,
+ GFP_KERNEL);
+ if (!av1_dec->prob_tbl.cpu)
+ return -ENOMEM;
+ av1_dec->prob_tbl.size = ALIGN(sizeof(struct av1cdfs), 2048);
+
+ av1_dec->prob_tbl_out.cpu = dma_alloc_coherent(vpu->dev,
+ ALIGN(sizeof(struct av1cdfs), 2048),
+ &av1_dec->prob_tbl_out.dma,
+ GFP_KERNEL);
+ if (!av1_dec->prob_tbl_out.cpu)
+ return -ENOMEM;
+ av1_dec->prob_tbl_out.size = ALIGN(sizeof(struct av1cdfs), 2048);
+ av1_dec->cdfs = &av1_dec->default_cdfs;
+ av1_dec->cdfs_ndvc = &av1_dec->default_cdfs_ndvc;
+
+ hantro_av1_set_default_cdfs(av1_dec->cdfs, av1_dec->cdfs_ndvc);
+
+ av1_dec->tile_buf.cpu = dma_alloc_coherent(vpu->dev,
+ AV1_TILE_SIZE,
+ &av1_dec->tile_buf.dma,
+ GFP_KERNEL);
+ if (!av1_dec->tile_buf.cpu)
+ return -ENOMEM;
+ av1_dec->tile_buf.size = AV1_TILE_SIZE;
+
+ return 0;
+}
+
+int hantro_av1_prepare_run(struct hantro_ctx *ctx)
+{
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+ struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+
+ ctrls->sequence = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_SEQUENCE);
+ if (WARN_ON(!ctrls->sequence))
+ return -EINVAL;
+
+ ctrls->tile_group_entry =
+ hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY);
+ if (WARN_ON(!ctrls->tile_group_entry))
+ return -EINVAL;
+
+ ctrls->frame = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FRAME);
+ if (WARN_ON(!ctrls->frame))
+ return -EINVAL;
+
+ ctrls->film_grain =
+ hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FILM_GRAIN);
+
+ return hantro_av1_tiles_reallocate(ctx);
+}
+
+static int hantro_av1_get_msb(u32 n)
+{
+ if (n == 0)
+ return 0;
+ return 31 ^ __builtin_clz(n);
+}
+
+static short hantro_av1_resolve_divisor_32(u32 d, short *shift)
+{
+ int f;
+ u64 e;
+
+ *shift = hantro_av1_get_msb(d);
+ /* e is obtained from D after resetting the most significant 1 bit. */
+ e = d - ((u32)1 << *shift);
+ /* Get the most significant DIV_LUT_BITS (8) bits of e into f */
+ if (*shift > DIV_LUT_BITS)
+ f = AV1_DIV_ROUND_UP_POW2(e, *shift - DIV_LUT_BITS);
+ else
+ f = e << (DIV_LUT_BITS - *shift);
+ if (f > DIV_LUT_NUM)
+ return -1;
+ *shift += DIV_LUT_PREC_BITS;
+ /* Use f as lookup into the precomputed table of multipliers */
+ return div_lut[f];
+}
+
+static void hantro_av1_get_shear_params(const u32 *params, s64 *alpha,
+ s64 *beta, s64 *gamma, s64 *delta)
+{
+ const int *mat = params;
+ short shift;
+ short y;
+ long long gv, dv;
+
+ if (mat[2] <= 0)
+ return;
+
+ *alpha = clamp_val(mat[2] - (1 << WARPEDMODEL_PREC_BITS), S16_MIN, S16_MAX);
+ *beta = clamp_val(mat[3], S16_MIN, S16_MAX);
+
+ y = hantro_av1_resolve_divisor_32(abs(mat[2]), &shift) * (mat[2] < 0 ? -1 : 1);
+
+ gv = ((long long)mat[4] * (1 << WARPEDMODEL_PREC_BITS)) * y;
+
+ *gamma = clamp_val((int)AV1_DIV_ROUND_UP_POW2_SIGNED(gv, shift), S16_MIN, S16_MAX);
+
+ dv = ((long long)mat[3] * mat[4]) * y;
+ *delta = clamp_val(mat[5] -
+ (int)AV1_DIV_ROUND_UP_POW2_SIGNED(dv, shift) - (1 << WARPEDMODEL_PREC_BITS),
+ S16_MIN, S16_MAX);
+
+ *alpha = AV1_DIV_ROUND_UP_POW2_SIGNED(*alpha, WARP_PARAM_REDUCE_BITS)
+ * (1 << WARP_PARAM_REDUCE_BITS);
+ *beta = AV1_DIV_ROUND_UP_POW2_SIGNED(*beta, WARP_PARAM_REDUCE_BITS)
+ * (1 << WARP_PARAM_REDUCE_BITS);
+ *gamma = AV1_DIV_ROUND_UP_POW2_SIGNED(*gamma, WARP_PARAM_REDUCE_BITS)
+ * (1 << WARP_PARAM_REDUCE_BITS);
+ *delta = AV1_DIV_ROUND_UP_POW2_SIGNED(*delta, WARP_PARAM_REDUCE_BITS)
+ * (1 << WARP_PARAM_REDUCE_BITS);
+}
+
+void hantro_av1_set_global_model(struct hantro_ctx *ctx)
+{
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+ struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+ const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
+ const struct v4l2_av1_global_motion *gm = &frame->global_motion;
+ u8 *dst = av1_dec->global_model.cpu;
+ int ref_frame, i;
+
+ memset(dst, 0, GLOBAL_MODEL_SIZE);
+ for (ref_frame = 0; ref_frame < V4L2_AV1_REFS_PER_FRAME; ++ref_frame) {
+ s64 alpha = 0, beta = 0, gamma = 0, delta = 0;
+
+ for (i = 0; i < 6; ++i) {
+ if (i == 2)
+ *(s32 *)dst =
+ gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][3];
+ else if (i == 3)
+ *(s32 *)dst =
+ gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][2];
+ else
+ *(s32 *)dst =
+ gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][i];
+ dst += 4;
+ }
+
+ if (gm->type[V4L2_AV1_REF_LAST_FRAME + ref_frame] <= V4L2_AV1_WARP_MODEL_AFFINE)
+ hantro_av1_get_shear_params(&gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][0],
+ &alpha, &beta, &gamma, &delta);
+
+ *(s16 *)dst = alpha;
+ dst += 2;
+ *(s16 *)dst = beta;
+ dst += 2;
+ *(s16 *)dst = gamma;
+ dst += 2;
+ *(s16 *)dst = delta;
+ dst += 2;
+ }
+}
+
+int hantro_av1_tile_log2(int target)
+{
+ int k;
+
+ /*
+ * returns the smallest value for k such that 1 << k is greater
+ * than or equal to target
+ */
+ for (k = 0; (1 << k) < target; k++)
+ ;
+
+ return k;
+}
+
+int hantro_av1_get_dist(struct hantro_ctx *ctx, int a, int b)
+{
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+ struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+ int bits = ctrls->sequence->order_hint_bits - 1;
+ int diff, m;
+
+ if (!ctrls->sequence->order_hint_bits)
+ return 0;
+
+ diff = a - b;
+ m = 1 << bits;
+ diff = (diff & (m - 1)) - (diff & m);
+
+ return diff;
+}
+
+void hantro_av1_set_frame_sign_bias(struct hantro_ctx *ctx)
+{
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+ struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+ const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
+ const struct v4l2_ctrl_av1_sequence *sequence = ctrls->sequence;
+ int i;
+
+ if (!sequence->order_hint_bits || IS_INTRA(frame->frame_type)) {
+ for (i = 0; i < V4L2_AV1_TOTAL_REFS_PER_FRAME; i++)
+ av1_dec->ref_frame_sign_bias[i] = 0;
+
+ return;
+ }
+ // Identify the nearest forward and backward references.
+ for (i = 0; i < V4L2_AV1_TOTAL_REFS_PER_FRAME - 1; i++) {
+ if (hantro_av1_get_frame_index(ctx, i) >= 0) {
+ int rel_off =
+ hantro_av1_get_dist(ctx,
+ hantro_av1_get_order_hint(ctx, i),
+ frame->order_hint);
+ av1_dec->ref_frame_sign_bias[i + 1] = (rel_off <= 0) ? 0 : 1;
+ }
+ }
+}
+
+void hantro_av1_init_scaling_function(const u8 *values, const u8 *scaling,
+ u8 num_points, u8 *scaling_lut)
+{
+ int i, point;
+
+ if (num_points == 0) {
+ memset(scaling_lut, 0, 256);
+ return;
+ }
+
+ for (point = 0; point < num_points - 1; point++) {
+ int x;
+ s32 delta_y = scaling[point + 1] - scaling[point];
+ s32 delta_x = values[point + 1] - values[point];
+ s64 delta =
+ delta_x ? delta_y * ((65536 + (delta_x >> 1)) /
+ delta_x) : 0;
+
+ for (x = 0; x < delta_x; x++) {
+ scaling_lut[values[point] + x] =
+ scaling[point] +
+ (s32)((x * delta + 32768) >> 16);
+ }
+ }
+
+ for (i = values[num_points - 1]; i < 256; i++)
+ scaling_lut[i] = scaling[num_points - 1];
+}
+
+void hantro_av1_set_tile_info(struct hantro_ctx *ctx)
+{
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+ struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+ const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info;
+ const struct v4l2_ctrl_av1_tile_group_entry *group_entry =
+ ctrls->tile_group_entry;
+ u8 *dst = av1_dec->tile_info.cpu;
+ int tile0, tile1;
+
+ memset(dst, 0, av1_dec->tile_info.size);
+
+ for (tile0 = 0; tile0 < tile_info->tile_cols; tile0++) {
+ for (tile1 = 0; tile1 < tile_info->tile_rows; tile1++) {
+ int tile_id = tile1 * tile_info->tile_cols + tile0;
+ u32 start, end;
+ u32 y0 =
+ tile_info->height_in_sbs_minus_1[tile1] + 1;
+ u32 x0 = tile_info->width_in_sbs_minus_1[tile0] + 1;
+
+ /* tile size in SB units (width,height) */
+ *dst++ = x0;
+ *dst++ = 0;
+ *dst++ = 0;
+ *dst++ = 0;
+ *dst++ = y0;
+ *dst++ = 0;
+ *dst++ = 0;
+ *dst++ = 0;
+
+ /* tile start position */
+ start = group_entry[tile_id].tile_offset - group_entry[0].tile_offset;
+ *dst++ = start & 255;
+ *dst++ = (start >> 8) & 255;
+ *dst++ = (start >> 16) & 255;
+ *dst++ = (start >> 24) & 255;
+
+ /* number of bytes in tile data */
+ end = start + group_entry[tile_id].tile_size;
+ *dst++ = end & 255;
+ *dst++ = (end >> 8) & 255;
+ *dst++ = (end >> 16) & 255;
+ *dst++ = (end >> 24) & 255;
+ }
+ }
+}
+
+bool hantro_av1_is_lossless(struct hantro_ctx *ctx)
+{
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+ struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+ const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
+ const struct v4l2_av1_segmentation *segmentation = &frame->segmentation;
+ const struct v4l2_av1_quantization *quantization = &frame->quantization;
+ int i;
+
+ for (i = 0; i < V4L2_AV1_MAX_SEGMENTS; i++) {
+ int qindex = quantization->base_q_idx;
+
+ if (segmentation->feature_enabled[i] &
+ V4L2_AV1_SEGMENT_FEATURE_ENABLED(V4L2_AV1_SEG_LVL_ALT_Q)) {
+ qindex += segmentation->feature_data[i][V4L2_AV1_SEG_LVL_ALT_Q];
+ }
+ qindex = clamp(qindex, 0, 255);
+
+ if (qindex ||
+ quantization->delta_q_y_dc ||
+ quantization->delta_q_u_dc ||
+ quantization->delta_q_u_ac ||
+ quantization->delta_q_v_dc ||
+ quantization->delta_q_v_ac)
+ return false;
+ }
+
+ return true;
+}
+
+void hantro_av1_update_prob(struct hantro_ctx *ctx)
+{
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+ struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+ const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
+ bool frame_is_intra = IS_INTRA(frame->frame_type);
+ struct av1cdfs *out_cdfs = (struct av1cdfs *)av1_dec->prob_tbl_out.cpu;
+ int i;
+
+ if (frame->flags & V4L2_AV1_FRAME_FLAG_DISABLE_FRAME_END_UPDATE_CDF)
+ return;
+
+ for (i = 0; i < NUM_REF_FRAMES; i++) {
+ if (frame->refresh_frame_flags & BIT(i)) {
+ struct mvcdfs stored_mv_cdf;
+
+ hantro_av1_get_cdfs(ctx, i);
+ stored_mv_cdf = av1_dec->cdfs->mv_cdf;
+ *av1_dec->cdfs = *out_cdfs;
+ if (frame_is_intra) {
+ av1_dec->cdfs->mv_cdf = stored_mv_cdf;
+ *av1_dec->cdfs_ndvc = out_cdfs->mv_cdf;
+ }
+ hantro_av1_store_cdfs(ctx, frame->refresh_frame_flags);
+ break;
+ }
+ }
+}
+
+void hantro_av1_set_prob(struct hantro_ctx *ctx)
+{
+ struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
+ struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+ const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
+ const struct v4l2_av1_quantization *quantization = &frame->quantization;
+ bool error_resilient_mode =
+ !!(frame->flags & V4L2_AV1_FRAME_FLAG_ERROR_RESILIENT_MODE);
+ bool frame_is_intra = IS_INTRA(frame->frame_type);
+
+ if (error_resilient_mode || frame_is_intra ||
+ frame->primary_ref_frame == AV1_PRIMARY_REF_NONE) {
+ av1_dec->cdfs = &av1_dec->default_cdfs;
+ av1_dec->cdfs_ndvc = &av1_dec->default_cdfs_ndvc;
+ hantro_av1_default_coeff_probs(quantization->base_q_idx,
+ av1_dec->cdfs);
+ } else {
+ if (frame->primary_ref_frame < 0 ||
+ frame->primary_ref_frame >= V4L2_AV1_REFS_PER_FRAME)
+ return;
+
+ hantro_av1_get_cdfs(ctx, frame->ref_frame_idx[frame->primary_ref_frame]);
+ }
+ hantro_av1_store_cdfs(ctx, frame->refresh_frame_flags);
+
+ memcpy(av1_dec->prob_tbl.cpu, av1_dec->cdfs, sizeof(struct av1cdfs));
+
+ if (frame_is_intra) {
+ int mv_offset = offsetof(struct av1cdfs, mv_cdf);
+ /* Overwrite MV context area with intrabc MV context */
+ memcpy(av1_dec->prob_tbl.cpu + mv_offset, av1_dec->cdfs_ndvc,
+ sizeof(struct mvcdfs));
+ }
+}
diff --git a/drivers/media/platform/verisilicon/hantro_av1.h b/drivers/media/platform/verisilicon/hantro_av1.h
new file mode 100644
index 000000000000..d91c814eefcb
--- /dev/null
+++ b/drivers/media/platform/verisilicon/hantro_av1.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _HANTRO_AV1_H_
+#define _HANTRO_AV1_H_
+
+#define AV1_PRIMARY_REF_NONE 7
+#define AV1_REF_SCALE_SHIFT 14
+#define MAX_FRAME_DISTANCE 31
+#define AV1DEC_MAX_PIC_BUFFERS 24
+
+#define SCALE_NUMERATOR 8
+#define SUPERRES_SCALE_DENOMINATOR_MIN (SCALE_NUMERATOR + 1)
+
+#define RS_SCALE_SUBPEL_BITS 14
+#define RS_SCALE_SUBPEL_MASK ((1 << RS_SCALE_SUBPEL_BITS) - 1)
+#define RS_SUBPEL_BITS 6
+#define RS_SUBPEL_MASK ((1 << RS_SUBPEL_BITS) - 1)
+#define RS_SCALE_EXTRA_BITS (RS_SCALE_SUBPEL_BITS - RS_SUBPEL_BITS)
+#define RS_SCALE_EXTRA_OFF (1 << (RS_SCALE_EXTRA_BITS - 1))
+
+/*
+ * These 3 values aren't defined enum v4l2_av1_segment_feature because
+ * they are not part of the specification
+ */
+#define V4L2_AV1_SEG_LVL_ALT_LF_Y_H 2
+#define V4L2_AV1_SEG_LVL_ALT_LF_U 3
+#define V4L2_AV1_SEG_LVL_ALT_LF_V 4
+
+#define IS_INTRA(type) ((type == V4L2_AV1_KEY_FRAME) || (type == V4L2_AV1_INTRA_ONLY_FRAME))
+
+#define LST_BUF_IDX (V4L2_AV1_REF_LAST_FRAME - V4L2_AV1_REF_LAST_FRAME)
+#define LST2_BUF_IDX (V4L2_AV1_REF_LAST2_FRAME - V4L2_AV1_REF_LAST_FRAME)
+#define LST3_BUF_IDX (V4L2_AV1_REF_LAST3_FRAME - V4L2_AV1_REF_LAST_FRAME)
+#define GLD_BUF_IDX (V4L2_AV1_REF_GOLDEN_FRAME - V4L2_AV1_REF_LAST_FRAME)
+#define BWD_BUF_IDX (V4L2_AV1_REF_BWDREF_FRAME - V4L2_AV1_REF_LAST_FRAME)
+#define ALT2_BUF_IDX (V4L2_AV1_REF_ALTREF2_FRAME - V4L2_AV1_REF_LAST_FRAME)
+#define ALT_BUF_IDX (V4L2_AV1_REF_ALTREF_FRAME - V4L2_AV1_REF_LAST_FRAME)
+
+#define AV1_INVALID_IDX -1
+
+int hantro_av1_get_frame_index(struct hantro_ctx *ctx, int ref);
+int hantro_av1_get_order_hint(struct hantro_ctx *ctx, int ref);
+int hantro_av1_frame_ref(struct hantro_ctx *ctx, u64 timestamp);
+void hantro_av1_clean_refs(struct hantro_ctx *ctx);
+size_t hantro_av1_luma_size(struct hantro_ctx *ctx);
+size_t hantro_av1_chroma_size(struct hantro_ctx *ctx);
+void hantro_av1_exit(struct hantro_ctx *ctx);
+int hantro_av1_init(struct hantro_ctx *ctx);
+int hantro_av1_prepare_run(struct hantro_ctx *ctx);
+void hantro_av1_set_global_model(struct hantro_ctx *ctx);
+int hantro_av1_tile_log2(int target);
+int hantro_av1_get_dist(struct hantro_ctx *ctx, int a, int b);
+void hantro_av1_set_frame_sign_bias(struct hantro_ctx *ctx);
+void hantro_av1_init_scaling_function(const u8 *values, const u8 *scaling,
+ u8 num_points, u8 *scaling_lut);
+void hantro_av1_set_tile_info(struct hantro_ctx *ctx);
+bool hantro_av1_is_lossless(struct hantro_ctx *ctx);
+void hantro_av1_update_prob(struct hantro_ctx *ctx);
+void hantro_av1_set_prob(struct hantro_ctx *ctx);
+
+int hantro_av1_get_hardware_mcomp_filt_type(int interpolation_filter);
+int hantro_av1_get_hardware_tx_mode(enum v4l2_av1_tx_mode tx_mode);
+
+#endif
diff --git a/drivers/media/platform/verisilicon/rockchip_av1_entropymode.c b/drivers/media/platform/verisilicon/hantro_av1_entropymode.c
similarity index 99%
rename from drivers/media/platform/verisilicon/rockchip_av1_entropymode.c
rename to drivers/media/platform/verisilicon/hantro_av1_entropymode.c
index b1ae72ad675e..4f7bfec73668 100644
--- a/drivers/media/platform/verisilicon/rockchip_av1_entropymode.c
+++ b/drivers/media/platform/verisilicon/hantro_av1_entropymode.c
@@ -11,7 +11,7 @@
*/
#include "hantro.h"
-#include "rockchip_av1_entropymode.h"
+#include "hantro_av1_entropymode.h"
#define AOM_ICDF ICDF
#define AOM_CDF2(a0) AOM_ICDF(a0)
@@ -4195,7 +4195,7 @@ static const u16 default_bits_cdf[][10] = {
}
};
-static int rockchip_av1_get_q_ctx(int q)
+static int hantro_av1_get_q_ctx(int q)
{
if (q <= 20)
return 0;
@@ -4206,10 +4206,10 @@ static int rockchip_av1_get_q_ctx(int q)
return 3;
}
-void rockchip_av1_default_coeff_probs(u32 base_qindex, void *ptr)
+void hantro_av1_default_coeff_probs(u32 base_qindex, void *ptr)
{
struct av1cdfs *cdfs = (struct av1cdfs *)ptr;
- const int index = rockchip_av1_get_q_ctx(base_qindex);
+ const int index = hantro_av1_get_q_ctx(base_qindex);
memcpy(cdfs->txb_skip_cdf, av1_default_txb_skip_cdfs[index],
sizeof(av1_default_txb_skip_cdfs[0]));
@@ -4240,8 +4240,8 @@ void rockchip_av1_default_coeff_probs(u32 base_qindex, void *ptr)
sizeof(av1_default_eob_multi1024_cdfs[0]));
}
-void rockchip_av1_set_default_cdfs(struct av1cdfs *cdfs,
- struct mvcdfs *cdfs_ndvc)
+void hantro_av1_set_default_cdfs(struct av1cdfs *cdfs,
+ struct mvcdfs *cdfs_ndvc)
{
memcpy(cdfs->partition_cdf, default_partition_cdf,
sizeof(cdfs->partition_cdf));
@@ -4398,7 +4398,7 @@ void rockchip_av1_set_default_cdfs(struct av1cdfs *cdfs,
sizeof(cdfs->compound_idx_cdf));
}
-void rockchip_av1_get_cdfs(struct hantro_ctx *ctx, u32 ref_idx)
+void hantro_av1_get_cdfs(struct hantro_ctx *ctx, u32 ref_idx)
{
struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
@@ -4406,8 +4406,8 @@ void rockchip_av1_get_cdfs(struct hantro_ctx *ctx, u32 ref_idx)
av1_dec->cdfs_ndvc = &av1_dec->cdfs_last_ndvc[ref_idx];
}
-void rockchip_av1_store_cdfs(struct hantro_ctx *ctx,
- u32 refresh_frame_flags)
+void hantro_av1_store_cdfs(struct hantro_ctx *ctx,
+ u32 refresh_frame_flags)
{
struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
int i;
diff --git a/drivers/media/platform/verisilicon/rockchip_av1_entropymode.h b/drivers/media/platform/verisilicon/hantro_av1_entropymode.h
similarity index 95%
rename from drivers/media/platform/verisilicon/rockchip_av1_entropymode.h
rename to drivers/media/platform/verisilicon/hantro_av1_entropymode.h
index bbf8424c7d2c..abbc660ecce3 100644
--- a/drivers/media/platform/verisilicon/rockchip_av1_entropymode.h
+++ b/drivers/media/platform/verisilicon/hantro_av1_entropymode.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-#ifndef _ROCKCHIP_AV1_ENTROPYMODE_H_
-#define _ROCKCHIP_AV1_ENTROPYMODE_H_
+#ifndef _HANTRO_AV1_ENTROPYMODE_H_
+#define _HANTRO_AV1_ENTROPYMODE_H_
#include <linux/types.h>
@@ -262,11 +262,11 @@ struct av1cdfs {
u16 dummy3[16];
};
-void rockchip_av1_store_cdfs(struct hantro_ctx *ctx,
- u32 refresh_frame_flags);
-void rockchip_av1_get_cdfs(struct hantro_ctx *ctx, u32 ref_idx);
-void rockchip_av1_set_default_cdfs(struct av1cdfs *cdfs,
- struct mvcdfs *cdfs_ndvc);
-void rockchip_av1_default_coeff_probs(u32 base_qindex, void *ptr);
+void hantro_av1_store_cdfs(struct hantro_ctx *ctx,
+ u32 refresh_frame_flags);
+void hantro_av1_get_cdfs(struct hantro_ctx *ctx, u32 ref_idx);
+void hantro_av1_set_default_cdfs(struct av1cdfs *cdfs,
+ struct mvcdfs *cdfs_ndvc);
+void hantro_av1_default_coeff_probs(u32 base_qindex, void *ptr);
-#endif /* _ROCKCHIP_AV1_ENTROPYMODE_H_ */
+#endif /* _HANTRO_AV1_ENTROPYMODE_H_ */
diff --git a/drivers/media/platform/verisilicon/rockchip_av1_filmgrain.c b/drivers/media/platform/verisilicon/hantro_av1_filmgrain.c
similarity index 85%
rename from drivers/media/platform/verisilicon/rockchip_av1_filmgrain.c
rename to drivers/media/platform/verisilicon/hantro_av1_filmgrain.c
index f64dea797eff..a1fc40e0218a 100644
--- a/drivers/media/platform/verisilicon/rockchip_av1_filmgrain.c
+++ b/drivers/media/platform/verisilicon/hantro_av1_filmgrain.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only OR Apache-2.0
-#include "rockchip_av1_filmgrain.h"
+#include "hantro_av1_filmgrain.h"
static const s32 gaussian_sequence[2048] = {
56, 568, -180, 172, 124, -84, 172, -64, -900, 24, 820,
@@ -204,8 +204,8 @@ static inline s32 round_power_of_two(const s32 val, s32 n)
return (val + a) >> n;
}
-static void rockchip_av1_init_random_generator(u8 luma_num, u16 seed,
- u16 *random_register)
+static void hantro_av1_init_random_generator(u8 luma_num, u16 seed,
+ u16 *random_register)
{
u16 random_reg = seed;
@@ -214,7 +214,7 @@ static void rockchip_av1_init_random_generator(u8 luma_num, u16 seed,
*random_register = random_reg;
}
-static inline void rockchip_av1_update_random_register(u16 *random_register)
+static inline void hantro_av1_update_random_register(u16 *random_register)
{
u16 bit;
u16 random_reg = *random_register;
@@ -224,21 +224,20 @@ static inline void rockchip_av1_update_random_register(u16 *random_register)
*random_register = (random_reg >> 1) | (bit << 15);
}
-static inline s32 rockchip_av1_get_random_number(u16 random_register)
+static inline s32 hantro_av1_get_random_number(u16 random_register)
{
return (random_register >> 5) & ((1 << 11) - 1);
}
-void rockchip_av1_generate_luma_grain_block(s32 (*luma_grain_block)[73][82],
- s32 bitdepth,
- u8 num_y_points,
- s32 grain_scale_shift,
- s32 ar_coeff_lag,
- s32 (*ar_coeffs_y)[24],
- s32 ar_coeff_shift,
- s32 grain_min,
- s32 grain_max,
- u16 random_seed)
+void hantro_av1_generate_luma_grain_block(struct hantro_av1_coeffs_grain_block *coeffs,
+ s32 bitdepth,
+ u8 num_y_points,
+ s32 grain_scale_shift,
+ s32 ar_coeff_lag,
+ s32 ar_coeff_shift,
+ s32 grain_min,
+ s32 grain_max,
+ u16 random_seed)
{
s32 gauss_sec_shift = 12 - bitdepth + grain_scale_shift;
u16 grain_random_register = random_seed;
@@ -247,15 +246,15 @@ void rockchip_av1_generate_luma_grain_block(s32 (*luma_grain_block)[73][82],
for (i = 0; i < 73; i++) {
for (j = 0; j < 82; j++) {
if (num_y_points > 0) {
- rockchip_av1_update_random_register
+ hantro_av1_update_random_register
(&grain_random_register);
- (*luma_grain_block)[i][j] =
+ coeffs->luma_grain_block[i][j] =
round_power_of_two(gaussian_sequence
- [rockchip_av1_get_random_number
+ [hantro_av1_get_random_number
(grain_random_register)],
gauss_sec_shift);
} else {
- (*luma_grain_block)[i][j] = 0;
+ coeffs->luma_grain_block[i][j] = 0;
}
}
}
@@ -272,72 +271,68 @@ void rockchip_av1_generate_luma_grain_block(s32 (*luma_grain_block)[73][82],
deltacol <= ar_coeff_lag; deltacol++) {
if (deltarow == 0 && deltacol == 0)
break;
- wsum = wsum + (*ar_coeffs_y)[pos] *
- (*luma_grain_block)[i + deltarow][j + deltacol];
+ wsum = wsum + coeffs->ar_coeffs_y[pos] *
+ coeffs->luma_grain_block[i + deltarow][j + deltacol];
++pos;
}
}
- (*luma_grain_block)[i][j] =
- clamp((*luma_grain_block)[i][j] +
+ coeffs->luma_grain_block[i][j] =
+ clamp(coeffs->luma_grain_block[i][j] +
round_power_of_two(wsum, ar_coeff_shift),
grain_min, grain_max);
}
}
// Calculate chroma grain noise once per frame
-void rockchip_av1_generate_chroma_grain_block(s32 (*luma_grain_block)[73][82],
- s32 (*cb_grain_block)[38][44],
- s32 (*cr_grain_block)[38][44],
- s32 bitdepth,
- u8 num_y_points,
- u8 num_cb_points,
- u8 num_cr_points,
- s32 grain_scale_shift,
- s32 ar_coeff_lag,
- s32 (*ar_coeffs_cb)[25],
- s32 (*ar_coeffs_cr)[25],
- s32 ar_coeff_shift,
- s32 grain_min,
- s32 grain_max,
- u8 chroma_scaling_from_luma,
- u16 random_seed)
+void hantro_av1_generate_chroma_grain_block(struct hantro_av1_coeffs_grain_block *coeffs,
+ s32 bitdepth,
+ u8 num_y_points,
+ u8 num_cb_points,
+ u8 num_cr_points,
+ s32 grain_scale_shift,
+ s32 ar_coeff_lag,
+ s32 ar_coeff_shift,
+ s32 grain_min,
+ s32 grain_max,
+ u8 chroma_scaling_from_luma,
+ u16 random_seed)
{
s32 gauss_sec_shift = 12 - bitdepth + grain_scale_shift;
u16 grain_random_register = 0;
s32 i, j;
- rockchip_av1_init_random_generator(7, random_seed,
- &grain_random_register);
+ hantro_av1_init_random_generator(7, random_seed,
+ &grain_random_register);
for (i = 0; i < 38; i++) {
for (j = 0; j < 44; j++) {
if (num_cb_points || chroma_scaling_from_luma) {
- rockchip_av1_update_random_register
+ hantro_av1_update_random_register
(&grain_random_register);
- (*cb_grain_block)[i][j] =
+ coeffs->cb_grain_block[i][j] =
round_power_of_two(gaussian_sequence
- [rockchip_av1_get_random_number
+ [hantro_av1_get_random_number
(grain_random_register)],
gauss_sec_shift);
} else {
- (*cb_grain_block)[i][j] = 0;
+ coeffs->cb_grain_block[i][j] = 0;
}
}
}
- rockchip_av1_init_random_generator(11, random_seed,
- &grain_random_register);
+ hantro_av1_init_random_generator(11, random_seed,
+ &grain_random_register);
for (i = 0; i < 38; i++) {
for (j = 0; j < 44; j++) {
if (num_cr_points || chroma_scaling_from_luma) {
- rockchip_av1_update_random_register
+ hantro_av1_update_random_register
(&grain_random_register);
- (*cr_grain_block)[i][j] =
+ coeffs->cr_grain_block[i][j] =
round_power_of_two(gaussian_sequence
- [rockchip_av1_get_random_number
+ [hantro_av1_get_random_number
(grain_random_register)],
gauss_sec_shift);
} else {
- (*cr_grain_block)[i][j] = 0;
+ coeffs->cr_grain_block[i][j] = 0;
}
}
}
@@ -355,12 +350,12 @@ void rockchip_av1_generate_chroma_grain_block(s32 (*luma_grain_block)[73][82],
deltacol <= ar_coeff_lag; deltacol++) {
if (deltarow == 0 && deltacol == 0)
break;
- wsum_cb = wsum_cb + (*ar_coeffs_cb)[pos] *
- (*cb_grain_block)[i + deltarow][j + deltacol];
+ wsum_cb = wsum_cb + coeffs->ar_coeffs_cb[pos] *
+ coeffs->cb_grain_block[i + deltarow][j + deltacol];
wsum_cr =
wsum_cr +
- (*ar_coeffs_cr)[pos] *
- (*cr_grain_block)[i + deltarow][j + deltacol];
+ coeffs->ar_coeffs_cr[pos] *
+ coeffs->cr_grain_block[i + deltarow][j + deltacol];
++pos;
}
}
@@ -371,28 +366,28 @@ void rockchip_av1_generate_chroma_grain_block(s32 (*luma_grain_block)[73][82],
s32 luma_coord_x = (j << 1) - 3;
av_luma +=
- (*luma_grain_block)[luma_coord_y][luma_coord_x];
+ coeffs->luma_grain_block[luma_coord_y][luma_coord_x];
av_luma +=
- (*luma_grain_block)[luma_coord_y][luma_coord_x + 1];
+ coeffs->luma_grain_block[luma_coord_y][luma_coord_x + 1];
av_luma +=
- (*luma_grain_block)[luma_coord_y + 1][luma_coord_x];
+ coeffs->luma_grain_block[luma_coord_y + 1][luma_coord_x];
av_luma +=
- (*luma_grain_block)[(luma_coord_y + 1)][luma_coord_x + 1];
+ coeffs->luma_grain_block[(luma_coord_y + 1)][luma_coord_x + 1];
av_luma = round_power_of_two(av_luma, 2);
- wsum_cb = wsum_cb + (*ar_coeffs_cb)[pos] * av_luma;
- wsum_cr = wsum_cr + (*ar_coeffs_cr)[pos] * av_luma;
+ wsum_cb = wsum_cb + coeffs->ar_coeffs_cb[pos] * av_luma;
+ wsum_cr = wsum_cr + coeffs->ar_coeffs_cr[pos] * av_luma;
}
if (num_cb_points || chroma_scaling_from_luma) {
- (*cb_grain_block)[i][j] =
- clamp((*cb_grain_block)[i][j] +
+ coeffs->cb_grain_block[i][j] =
+ clamp(coeffs->cb_grain_block[i][j] +
round_power_of_two(wsum_cb, ar_coeff_shift),
grain_min, grain_max);
}
if (num_cr_points || chroma_scaling_from_luma) {
- (*cr_grain_block)[i][j] =
- clamp((*cr_grain_block)[i][j] +
+ coeffs->cr_grain_block[i][j] =
+ clamp(coeffs->cr_grain_block[i][j] +
round_power_of_two(wsum_cr, ar_coeff_shift),
grain_min, grain_max);
}
diff --git a/drivers/media/platform/verisilicon/hantro_av1_filmgrain.h b/drivers/media/platform/verisilicon/hantro_av1_filmgrain.h
new file mode 100644
index 000000000000..d80be7c011a0
--- /dev/null
+++ b/drivers/media/platform/verisilicon/hantro_av1_filmgrain.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _HANTRO_AV1_FILMGRAIN_H_
+#define _HANTRO_AV1_FILMGRAIN_H_
+
+#include <linux/types.h>
+
+struct hantro_av1_film_grain {
+ u8 scaling_lut_y[256];
+ u8 scaling_lut_cb[256];
+ u8 scaling_lut_cr[256];
+ s16 cropped_luma_grain_block[4096];
+ s16 cropped_chroma_grain_block[1024 * 2];
+};
+
+struct hantro_av1_coeffs_grain_block {
+ s32 ar_coeffs_y[24];
+ s32 ar_coeffs_cb[25];
+ s32 ar_coeffs_cr[25];
+ s32 luma_grain_block[73][82];
+ s32 cb_grain_block[38][44];
+ s32 cr_grain_block[38][44];
+};
+
+void hantro_av1_generate_luma_grain_block(struct hantro_av1_coeffs_grain_block *coeffs,
+ s32 bitdepth,
+ u8 num_y_points,
+ s32 grain_scale_shift,
+ s32 ar_coeff_lag,
+ s32 ar_coeff_shift,
+ s32 grain_min,
+ s32 grain_max,
+ u16 random_seed);
+
+void hantro_av1_generate_chroma_grain_block(struct hantro_av1_coeffs_grain_block *coeffs,
+ s32 bitdepth,
+ u8 num_y_points,
+ u8 num_cb_points,
+ u8 num_cr_points,
+ s32 grain_scale_shift,
+ s32 ar_coeff_lag,
+ s32 ar_coeff_shift,
+ s32 grain_min,
+ s32 grain_max,
+ u8 chroma_scaling_from_luma,
+ u16 random_seed);
+
+#endif
diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h
index 5f2011529f02..d1d39d1df5d2 100644
--- a/drivers/media/platform/verisilicon/hantro_hw.h
+++ b/drivers/media/platform/verisilicon/hantro_hw.h
@@ -15,8 +15,8 @@
#include <media/v4l2-vp9.h>
#include <media/videobuf2-core.h>
-#include "rockchip_av1_entropymode.h"
-#include "rockchip_av1_filmgrain.h"
+#include "hantro_av1_entropymode.h"
+#include "hantro_av1_filmgrain.h"
#define DEC_8190_ALIGN_MASK 0x07U
@@ -459,10 +459,7 @@ void hantro_hevc_ref_init(struct hantro_ctx *ctx);
dma_addr_t hantro_hevc_get_ref_buf(struct hantro_ctx *ctx, s32 poc);
int hantro_hevc_add_ref_buf(struct hantro_ctx *ctx, int poc, dma_addr_t addr);
-int rockchip_vpu981_av1_dec_init(struct hantro_ctx *ctx);
-void rockchip_vpu981_av1_dec_exit(struct hantro_ctx *ctx);
int rockchip_vpu981_av1_dec_run(struct hantro_ctx *ctx);
-void rockchip_vpu981_av1_dec_done(struct hantro_ctx *ctx);
static inline unsigned short hantro_vp9_num_sbs(unsigned short dimension)
{
diff --git a/drivers/media/platform/verisilicon/rockchip_av1_filmgrain.h b/drivers/media/platform/verisilicon/rockchip_av1_filmgrain.h
deleted file mode 100644
index 31a8b7920c31..000000000000
--- a/drivers/media/platform/verisilicon/rockchip_av1_filmgrain.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-
-#ifndef _ROCKCHIP_AV1_FILMGRAIN_H_
-#define _ROCKCHIP_AV1_FILMGRAIN_H_
-
-#include <linux/types.h>
-
-void rockchip_av1_generate_luma_grain_block(s32 (*luma_grain_block)[73][82],
- s32 bitdepth,
- u8 num_y_points,
- s32 grain_scale_shift,
- s32 ar_coeff_lag,
- s32 (*ar_coeffs_y)[24],
- s32 ar_coeff_shift,
- s32 grain_min,
- s32 grain_max,
- u16 random_seed);
-
-void rockchip_av1_generate_chroma_grain_block(s32 (*luma_grain_block)[73][82],
- s32 (*cb_grain_block)[38][44],
- s32 (*cr_grain_block)[38][44],
- s32 bitdepth,
- u8 num_y_points,
- u8 num_cb_points,
- u8 num_cr_points,
- s32 grain_scale_shift,
- s32 ar_coeff_lag,
- s32 (*ar_coeffs_cb)[25],
- s32 (*ar_coeffs_cr)[25],
- s32 ar_coeff_shift,
- s32 grain_min,
- s32 grain_max,
- u8 chroma_scaling_from_luma,
- u16 random_seed);
-
-#endif
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
index e4e21ad37323..61e30338422d 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
@@ -7,622 +7,35 @@
#include <media/v4l2-mem2mem.h>
#include "hantro.h"
+#include "hantro_av1.h"
#include "hantro_v4l2.h"
#include "rockchip_vpu981_regs.h"
#define AV1_DEC_MODE 17
-#define GM_GLOBAL_MODELS_PER_FRAME 7
-#define GLOBAL_MODEL_TOTAL_SIZE (6 * 4 + 4 * 2)
-#define GLOBAL_MODEL_SIZE ALIGN(GM_GLOBAL_MODELS_PER_FRAME * GLOBAL_MODEL_TOTAL_SIZE, 2048)
-#define AV1_MAX_TILES 128
-#define AV1_TILE_INFO_SIZE (AV1_MAX_TILES * 16)
-#define AV1DEC_MAX_PIC_BUFFERS 24
-#define AV1_REF_SCALE_SHIFT 14
-#define AV1_INVALID_IDX -1
-#define MAX_FRAME_DISTANCE 31
-#define AV1_PRIMARY_REF_NONE 7
-#define AV1_TILE_SIZE ALIGN(32 * 128, 4096)
-/*
- * These 3 values aren't defined enum v4l2_av1_segment_feature because
- * they are not part of the specification
- */
-#define V4L2_AV1_SEG_LVL_ALT_LF_Y_H 2
-#define V4L2_AV1_SEG_LVL_ALT_LF_U 3
-#define V4L2_AV1_SEG_LVL_ALT_LF_V 4
-
-#define SUPERRES_SCALE_BITS 3
-#define SCALE_NUMERATOR 8
-#define SUPERRES_SCALE_DENOMINATOR_MIN (SCALE_NUMERATOR + 1)
-
-#define RS_SUBPEL_BITS 6
-#define RS_SUBPEL_MASK ((1 << RS_SUBPEL_BITS) - 1)
-#define RS_SCALE_SUBPEL_BITS 14
-#define RS_SCALE_SUBPEL_MASK ((1 << RS_SCALE_SUBPEL_BITS) - 1)
-#define RS_SCALE_EXTRA_BITS (RS_SCALE_SUBPEL_BITS - RS_SUBPEL_BITS)
-#define RS_SCALE_EXTRA_OFF (1 << (RS_SCALE_EXTRA_BITS - 1))
-
-#define IS_INTRA(type) ((type == V4L2_AV1_KEY_FRAME) || (type == V4L2_AV1_INTRA_ONLY_FRAME))
-
-#define LST_BUF_IDX (V4L2_AV1_REF_LAST_FRAME - V4L2_AV1_REF_LAST_FRAME)
-#define LST2_BUF_IDX (V4L2_AV1_REF_LAST2_FRAME - V4L2_AV1_REF_LAST_FRAME)
-#define LST3_BUF_IDX (V4L2_AV1_REF_LAST3_FRAME - V4L2_AV1_REF_LAST_FRAME)
-#define GLD_BUF_IDX (V4L2_AV1_REF_GOLDEN_FRAME - V4L2_AV1_REF_LAST_FRAME)
-#define BWD_BUF_IDX (V4L2_AV1_REF_BWDREF_FRAME - V4L2_AV1_REF_LAST_FRAME)
-#define ALT2_BUF_IDX (V4L2_AV1_REF_ALTREF2_FRAME - V4L2_AV1_REF_LAST_FRAME)
-#define ALT_BUF_IDX (V4L2_AV1_REF_ALTREF_FRAME - V4L2_AV1_REF_LAST_FRAME)
-
-#define DIV_LUT_PREC_BITS 14
-#define DIV_LUT_BITS 8
-#define DIV_LUT_NUM BIT(DIV_LUT_BITS)
-#define WARP_PARAM_REDUCE_BITS 6
-#define WARPEDMODEL_PREC_BITS 16
-
-#define AV1_DIV_ROUND_UP_POW2(value, n) \
-({ \
- typeof(n) _n = n; \
- typeof(value) _value = value; \
- (_value + (BIT(_n) >> 1)) >> _n; \
-})
-
-#define AV1_DIV_ROUND_UP_POW2_SIGNED(value, n) \
-({ \
- typeof(n) _n_ = n; \
- typeof(value) _value_ = value; \
- (((_value_) < 0) ? -AV1_DIV_ROUND_UP_POW2(-(_value_), (_n_)) \
- : AV1_DIV_ROUND_UP_POW2((_value_), (_n_))); \
-})
-
-enum rockchip_av1_tx_mode {
- ROCKCHIP_AV1_TX_MODE_ONLY_4X4 = 0,
- ROCKCHIP_AV1_TX_MODE_8X8 = 1,
- ROCKCHIP_AV1_TX_MODE_16x16 = 2,
- ROCKCHIP_AV1_TX_MODE_32x32 = 3,
- ROCKCHIP_AV1_TX_MODE_SELECT = 4,
-};
-
-struct rockchip_av1_film_grain {
- u8 scaling_lut_y[256];
- u8 scaling_lut_cb[256];
- u8 scaling_lut_cr[256];
- s16 cropped_luma_grain_block[4096];
- s16 cropped_chroma_grain_block[1024 * 2];
-};
-
-static const short div_lut[DIV_LUT_NUM + 1] = {
- 16384, 16320, 16257, 16194, 16132, 16070, 16009, 15948, 15888, 15828, 15768,
- 15709, 15650, 15592, 15534, 15477, 15420, 15364, 15308, 15252, 15197, 15142,
- 15087, 15033, 14980, 14926, 14873, 14821, 14769, 14717, 14665, 14614, 14564,
- 14513, 14463, 14413, 14364, 14315, 14266, 14218, 14170, 14122, 14075, 14028,
- 13981, 13935, 13888, 13843, 13797, 13752, 13707, 13662, 13618, 13574, 13530,
- 13487, 13443, 13400, 13358, 13315, 13273, 13231, 13190, 13148, 13107, 13066,
- 13026, 12985, 12945, 12906, 12866, 12827, 12788, 12749, 12710, 12672, 12633,
- 12596, 12558, 12520, 12483, 12446, 12409, 12373, 12336, 12300, 12264, 12228,
- 12193, 12157, 12122, 12087, 12053, 12018, 11984, 11950, 11916, 11882, 11848,
- 11815, 11782, 11749, 11716, 11683, 11651, 11619, 11586, 11555, 11523, 11491,
- 11460, 11429, 11398, 11367, 11336, 11305, 11275, 11245, 11215, 11185, 11155,
- 11125, 11096, 11067, 11038, 11009, 10980, 10951, 10923, 10894, 10866, 10838,
- 10810, 10782, 10755, 10727, 10700, 10673, 10645, 10618, 10592, 10565, 10538,
- 10512, 10486, 10460, 10434, 10408, 10382, 10356, 10331, 10305, 10280, 10255,
- 10230, 10205, 10180, 10156, 10131, 10107, 10082, 10058, 10034, 10010, 9986,
- 9963, 9939, 9916, 9892, 9869, 9846, 9823, 9800, 9777, 9754, 9732,
- 9709, 9687, 9664, 9642, 9620, 9598, 9576, 9554, 9533, 9511, 9489,
- 9468, 9447, 9425, 9404, 9383, 9362, 9341, 9321, 9300, 9279, 9259,
- 9239, 9218, 9198, 9178, 9158, 9138, 9118, 9098, 9079, 9059, 9039,
- 9020, 9001, 8981, 8962, 8943, 8924, 8905, 8886, 8867, 8849, 8830,
- 8812, 8793, 8775, 8756, 8738, 8720, 8702, 8684, 8666, 8648, 8630,
- 8613, 8595, 8577, 8560, 8542, 8525, 8508, 8490, 8473, 8456, 8439,
- 8422, 8405, 8389, 8372, 8355, 8339, 8322, 8306, 8289, 8273, 8257,
- 8240, 8224, 8208, 8192,
-};
-
-static int rockchip_vpu981_get_frame_index(struct hantro_ctx *ctx, int ref)
-{
- struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
- struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
- const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
- u64 timestamp;
- int i, idx = frame->ref_frame_idx[ref];
-
- if (idx >= V4L2_AV1_TOTAL_REFS_PER_FRAME || idx < 0)
- return AV1_INVALID_IDX;
-
- timestamp = frame->reference_frame_ts[idx];
- for (i = 0; i < AV1_MAX_FRAME_BUF_COUNT; i++) {
- if (!av1_dec->frame_refs[i].used)
- continue;
- if (av1_dec->frame_refs[i].timestamp == timestamp)
- return i;
- }
-
- return AV1_INVALID_IDX;
-}
-
-static int rockchip_vpu981_get_order_hint(struct hantro_ctx *ctx, int ref)
-{
- struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
- int idx = rockchip_vpu981_get_frame_index(ctx, ref);
-
- if (idx != AV1_INVALID_IDX)
- return av1_dec->frame_refs[idx].order_hint;
-
- return 0;
-}
-
-static int rockchip_vpu981_av1_dec_frame_ref(struct hantro_ctx *ctx,
- u64 timestamp)
-{
- struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
- struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
- const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
- int i;
-
- for (i = 0; i < AV1_MAX_FRAME_BUF_COUNT; i++) {
- int j;
-
- if (av1_dec->frame_refs[i].used)
- continue;
-
- av1_dec->frame_refs[i].width = frame->frame_width_minus_1 + 1;
- av1_dec->frame_refs[i].height = frame->frame_height_minus_1 + 1;
- av1_dec->frame_refs[i].mi_cols = DIV_ROUND_UP(frame->frame_width_minus_1 + 1, 8);
- av1_dec->frame_refs[i].mi_rows = DIV_ROUND_UP(frame->frame_height_minus_1 + 1, 8);
- av1_dec->frame_refs[i].timestamp = timestamp;
- av1_dec->frame_refs[i].frame_type = frame->frame_type;
- av1_dec->frame_refs[i].order_hint = frame->order_hint;
- av1_dec->frame_refs[i].vb2_ref = hantro_get_dst_buf(ctx);
-
- for (j = 0; j < V4L2_AV1_TOTAL_REFS_PER_FRAME; j++)
- av1_dec->frame_refs[i].order_hints[j] = frame->order_hints[j];
- av1_dec->frame_refs[i].used = true;
- av1_dec->current_frame_index = i;
-
- return i;
- }
-
- return AV1_INVALID_IDX;
-}
-
-static void rockchip_vpu981_av1_dec_frame_unref(struct hantro_ctx *ctx, int idx)
-{
- struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
-
- if (idx >= 0)
- av1_dec->frame_refs[idx].used = false;
-}
-
-static void rockchip_vpu981_av1_dec_clean_refs(struct hantro_ctx *ctx)
-{
- struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
- struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
-
- int ref, idx;
-
- for (idx = 0; idx < AV1_MAX_FRAME_BUF_COUNT; idx++) {
- u64 timestamp = av1_dec->frame_refs[idx].timestamp;
- bool used = false;
-
- if (!av1_dec->frame_refs[idx].used)
- continue;
-
- for (ref = 0; ref < V4L2_AV1_TOTAL_REFS_PER_FRAME; ref++) {
- if (ctrls->frame->reference_frame_ts[ref] == timestamp)
- used = true;
- }
-
- if (!used)
- rockchip_vpu981_av1_dec_frame_unref(ctx, idx);
- }
-}
-
-static size_t rockchip_vpu981_av1_dec_luma_size(struct hantro_ctx *ctx)
-{
- return ctx->dst_fmt.width * ctx->dst_fmt.height * ctx->bit_depth / 8;
-}
-
-static size_t rockchip_vpu981_av1_dec_chroma_size(struct hantro_ctx *ctx)
-{
- size_t cr_offset = rockchip_vpu981_av1_dec_luma_size(ctx);
-
- return ALIGN((cr_offset * 3) / 2, 64);
-}
-
-static void rockchip_vpu981_av1_dec_tiles_free(struct hantro_ctx *ctx)
-{
- struct hantro_dev *vpu = ctx->dev;
- struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
-
- if (av1_dec->db_data_col.cpu)
- dma_free_coherent(vpu->dev, av1_dec->db_data_col.size,
- av1_dec->db_data_col.cpu,
- av1_dec->db_data_col.dma);
- av1_dec->db_data_col.cpu = NULL;
-
- if (av1_dec->db_ctrl_col.cpu)
- dma_free_coherent(vpu->dev, av1_dec->db_ctrl_col.size,
- av1_dec->db_ctrl_col.cpu,
- av1_dec->db_ctrl_col.dma);
- av1_dec->db_ctrl_col.cpu = NULL;
-
- if (av1_dec->cdef_col.cpu)
- dma_free_coherent(vpu->dev, av1_dec->cdef_col.size,
- av1_dec->cdef_col.cpu, av1_dec->cdef_col.dma);
- av1_dec->cdef_col.cpu = NULL;
-
- if (av1_dec->sr_col.cpu)
- dma_free_coherent(vpu->dev, av1_dec->sr_col.size,
- av1_dec->sr_col.cpu, av1_dec->sr_col.dma);
- av1_dec->sr_col.cpu = NULL;
-
- if (av1_dec->lr_col.cpu)
- dma_free_coherent(vpu->dev, av1_dec->lr_col.size,
- av1_dec->lr_col.cpu, av1_dec->lr_col.dma);
- av1_dec->lr_col.cpu = NULL;
-}
-
-static int rockchip_vpu981_av1_dec_tiles_reallocate(struct hantro_ctx *ctx)
-{
- struct hantro_dev *vpu = ctx->dev;
- struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
- struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
- const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info;
- unsigned int num_tile_cols = tile_info->tile_cols;
- unsigned int height = ALIGN(ctrls->frame->frame_height_minus_1 + 1, 64);
- unsigned int height_in_sb = height / 64;
- unsigned int stripe_num = ((height + 8) + 63) / 64;
- size_t size;
-
- if (av1_dec->db_data_col.size >=
- ALIGN(height * 12 * ctx->bit_depth / 8, 128) * num_tile_cols)
- return 0;
-
- rockchip_vpu981_av1_dec_tiles_free(ctx);
-
- size = ALIGN(height * 12 * ctx->bit_depth / 8, 128) * num_tile_cols;
- av1_dec->db_data_col.cpu = dma_alloc_coherent(vpu->dev, size,
- &av1_dec->db_data_col.dma,
- GFP_KERNEL);
- if (!av1_dec->db_data_col.cpu)
- goto buffer_allocation_error;
- av1_dec->db_data_col.size = size;
-
- size = ALIGN(height * 2 * 16 / 4, 128) * num_tile_cols;
- av1_dec->db_ctrl_col.cpu = dma_alloc_coherent(vpu->dev, size,
- &av1_dec->db_ctrl_col.dma,
- GFP_KERNEL);
- if (!av1_dec->db_ctrl_col.cpu)
- goto buffer_allocation_error;
- av1_dec->db_ctrl_col.size = size;
-
- size = ALIGN(height_in_sb * 44 * ctx->bit_depth * 16 / 8, 128) * num_tile_cols;
- av1_dec->cdef_col.cpu = dma_alloc_coherent(vpu->dev, size,
- &av1_dec->cdef_col.dma,
- GFP_KERNEL);
- if (!av1_dec->cdef_col.cpu)
- goto buffer_allocation_error;
- av1_dec->cdef_col.size = size;
-
- size = ALIGN(height_in_sb * (3040 + 1280), 128) * num_tile_cols;
- av1_dec->sr_col.cpu = dma_alloc_coherent(vpu->dev, size,
- &av1_dec->sr_col.dma,
- GFP_KERNEL);
- if (!av1_dec->sr_col.cpu)
- goto buffer_allocation_error;
- av1_dec->sr_col.size = size;
-
- size = ALIGN(stripe_num * 1536 * ctx->bit_depth / 8, 128) * num_tile_cols;
- av1_dec->lr_col.cpu = dma_alloc_coherent(vpu->dev, size,
- &av1_dec->lr_col.dma,
- GFP_KERNEL);
- if (!av1_dec->lr_col.cpu)
- goto buffer_allocation_error;
- av1_dec->lr_col.size = size;
-
- av1_dec->num_tile_cols_allocated = num_tile_cols;
- return 0;
-
-buffer_allocation_error:
- rockchip_vpu981_av1_dec_tiles_free(ctx);
- return -ENOMEM;
-}
-
-void rockchip_vpu981_av1_dec_exit(struct hantro_ctx *ctx)
-{
- struct hantro_dev *vpu = ctx->dev;
- struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
-
- if (av1_dec->global_model.cpu)
- dma_free_coherent(vpu->dev, av1_dec->global_model.size,
- av1_dec->global_model.cpu,
- av1_dec->global_model.dma);
- av1_dec->global_model.cpu = NULL;
-
- if (av1_dec->tile_info.cpu)
- dma_free_coherent(vpu->dev, av1_dec->tile_info.size,
- av1_dec->tile_info.cpu,
- av1_dec->tile_info.dma);
- av1_dec->tile_info.cpu = NULL;
-
- if (av1_dec->film_grain.cpu)
- dma_free_coherent(vpu->dev, av1_dec->film_grain.size,
- av1_dec->film_grain.cpu,
- av1_dec->film_grain.dma);
- av1_dec->film_grain.cpu = NULL;
-
- if (av1_dec->prob_tbl.cpu)
- dma_free_coherent(vpu->dev, av1_dec->prob_tbl.size,
- av1_dec->prob_tbl.cpu, av1_dec->prob_tbl.dma);
- av1_dec->prob_tbl.cpu = NULL;
-
- if (av1_dec->prob_tbl_out.cpu)
- dma_free_coherent(vpu->dev, av1_dec->prob_tbl_out.size,
- av1_dec->prob_tbl_out.cpu,
- av1_dec->prob_tbl_out.dma);
- av1_dec->prob_tbl_out.cpu = NULL;
-
- if (av1_dec->tile_buf.cpu)
- dma_free_coherent(vpu->dev, av1_dec->tile_buf.size,
- av1_dec->tile_buf.cpu, av1_dec->tile_buf.dma);
- av1_dec->tile_buf.cpu = NULL;
-
- rockchip_vpu981_av1_dec_tiles_free(ctx);
-}
-
-int rockchip_vpu981_av1_dec_init(struct hantro_ctx *ctx)
-{
- struct hantro_dev *vpu = ctx->dev;
- struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
-
- memset(av1_dec, 0, sizeof(*av1_dec));
-
- av1_dec->global_model.cpu = dma_alloc_coherent(vpu->dev, GLOBAL_MODEL_SIZE,
- &av1_dec->global_model.dma,
- GFP_KERNEL);
- if (!av1_dec->global_model.cpu)
- return -ENOMEM;
- av1_dec->global_model.size = GLOBAL_MODEL_SIZE;
-
- av1_dec->tile_info.cpu = dma_alloc_coherent(vpu->dev, AV1_TILE_INFO_SIZE,
- &av1_dec->tile_info.dma,
- GFP_KERNEL);
- if (!av1_dec->tile_info.cpu)
- return -ENOMEM;
- av1_dec->tile_info.size = AV1_TILE_INFO_SIZE;
-
- av1_dec->film_grain.cpu = dma_alloc_coherent(vpu->dev,
- ALIGN(sizeof(struct rockchip_av1_film_grain), 2048),
- &av1_dec->film_grain.dma,
- GFP_KERNEL);
- if (!av1_dec->film_grain.cpu)
- return -ENOMEM;
- av1_dec->film_grain.size = ALIGN(sizeof(struct rockchip_av1_film_grain), 2048);
-
- av1_dec->prob_tbl.cpu = dma_alloc_coherent(vpu->dev,
- ALIGN(sizeof(struct av1cdfs), 2048),
- &av1_dec->prob_tbl.dma,
- GFP_KERNEL);
- if (!av1_dec->prob_tbl.cpu)
- return -ENOMEM;
- av1_dec->prob_tbl.size = ALIGN(sizeof(struct av1cdfs), 2048);
-
- av1_dec->prob_tbl_out.cpu = dma_alloc_coherent(vpu->dev,
- ALIGN(sizeof(struct av1cdfs), 2048),
- &av1_dec->prob_tbl_out.dma,
- GFP_KERNEL);
- if (!av1_dec->prob_tbl_out.cpu)
- return -ENOMEM;
- av1_dec->prob_tbl_out.size = ALIGN(sizeof(struct av1cdfs), 2048);
- av1_dec->cdfs = &av1_dec->default_cdfs;
- av1_dec->cdfs_ndvc = &av1_dec->default_cdfs_ndvc;
-
- rockchip_av1_set_default_cdfs(av1_dec->cdfs, av1_dec->cdfs_ndvc);
-
- av1_dec->tile_buf.cpu = dma_alloc_coherent(vpu->dev,
- AV1_TILE_SIZE,
- &av1_dec->tile_buf.dma,
- GFP_KERNEL);
- if (!av1_dec->tile_buf.cpu)
- return -ENOMEM;
- av1_dec->tile_buf.size = AV1_TILE_SIZE;
-
- return 0;
-}
-
-static int rockchip_vpu981_av1_dec_prepare_run(struct hantro_ctx *ctx)
-{
- struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
- struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
-
- ctrls->sequence = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_SEQUENCE);
- if (WARN_ON(!ctrls->sequence))
- return -EINVAL;
-
- ctrls->tile_group_entry =
- hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY);
- if (WARN_ON(!ctrls->tile_group_entry))
- return -EINVAL;
-
- ctrls->frame = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FRAME);
- if (WARN_ON(!ctrls->frame))
- return -EINVAL;
-
- ctrls->film_grain =
- hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FILM_GRAIN);
-
- return rockchip_vpu981_av1_dec_tiles_reallocate(ctx);
-}
-
-static inline int rockchip_vpu981_av1_dec_get_msb(u32 n)
-{
- if (n == 0)
- return 0;
- return 31 ^ __builtin_clz(n);
-}
-
-static short rockchip_vpu981_av1_dec_resolve_divisor_32(u32 d, short *shift)
-{
- int f;
- u64 e;
-
- *shift = rockchip_vpu981_av1_dec_get_msb(d);
- /* e is obtained from D after resetting the most significant 1 bit. */
- e = d - ((u32)1 << *shift);
- /* Get the most significant DIV_LUT_BITS (8) bits of e into f */
- if (*shift > DIV_LUT_BITS)
- f = AV1_DIV_ROUND_UP_POW2(e, *shift - DIV_LUT_BITS);
- else
- f = e << (DIV_LUT_BITS - *shift);
- if (f > DIV_LUT_NUM)
- return -1;
- *shift += DIV_LUT_PREC_BITS;
- /* Use f as lookup into the precomputed table of multipliers */
- return div_lut[f];
-}
-
-static void
-rockchip_vpu981_av1_dec_get_shear_params(const u32 *params, s64 *alpha,
- s64 *beta, s64 *gamma, s64 *delta)
-{
- const int *mat = params;
- short shift;
- short y;
- long long gv, dv;
-
- if (mat[2] <= 0)
- return;
-
- *alpha = clamp_val(mat[2] - (1 << WARPEDMODEL_PREC_BITS), S16_MIN, S16_MAX);
- *beta = clamp_val(mat[3], S16_MIN, S16_MAX);
-
- y = rockchip_vpu981_av1_dec_resolve_divisor_32(abs(mat[2]), &shift) * (mat[2] < 0 ? -1 : 1);
-
- gv = ((long long)mat[4] * (1 << WARPEDMODEL_PREC_BITS)) * y;
-
- *gamma = clamp_val((int)AV1_DIV_ROUND_UP_POW2_SIGNED(gv, shift), S16_MIN, S16_MAX);
-
- dv = ((long long)mat[3] * mat[4]) * y;
- *delta = clamp_val(mat[5] -
- (int)AV1_DIV_ROUND_UP_POW2_SIGNED(dv, shift) - (1 << WARPEDMODEL_PREC_BITS),
- S16_MIN, S16_MAX);
-
- *alpha = AV1_DIV_ROUND_UP_POW2_SIGNED(*alpha, WARP_PARAM_REDUCE_BITS)
- * (1 << WARP_PARAM_REDUCE_BITS);
- *beta = AV1_DIV_ROUND_UP_POW2_SIGNED(*beta, WARP_PARAM_REDUCE_BITS)
- * (1 << WARP_PARAM_REDUCE_BITS);
- *gamma = AV1_DIV_ROUND_UP_POW2_SIGNED(*gamma, WARP_PARAM_REDUCE_BITS)
- * (1 << WARP_PARAM_REDUCE_BITS);
- *delta = AV1_DIV_ROUND_UP_POW2_SIGNED(*delta, WARP_PARAM_REDUCE_BITS)
- * (1 << WARP_PARAM_REDUCE_BITS);
-}
static void rockchip_vpu981_av1_dec_set_global_model(struct hantro_ctx *ctx)
{
struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
- struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
- const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
- const struct v4l2_av1_global_motion *gm = &frame->global_motion;
- u8 *dst = av1_dec->global_model.cpu;
struct hantro_dev *vpu = ctx->dev;
- int ref_frame, i;
-
- memset(dst, 0, GLOBAL_MODEL_SIZE);
- for (ref_frame = 0; ref_frame < V4L2_AV1_REFS_PER_FRAME; ++ref_frame) {
- s64 alpha = 0, beta = 0, gamma = 0, delta = 0;
-
- for (i = 0; i < 6; ++i) {
- if (i == 2)
- *(s32 *)dst =
- gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][3];
- else if (i == 3)
- *(s32 *)dst =
- gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][2];
- else
- *(s32 *)dst =
- gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][i];
- dst += 4;
- }
-
- if (gm->type[V4L2_AV1_REF_LAST_FRAME + ref_frame] <= V4L2_AV1_WARP_MODEL_AFFINE)
- rockchip_vpu981_av1_dec_get_shear_params(&gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][0],
- &alpha, &beta, &gamma, &delta);
-
- *(s16 *)dst = alpha;
- dst += 2;
- *(s16 *)dst = beta;
- dst += 2;
- *(s16 *)dst = gamma;
- dst += 2;
- *(s16 *)dst = delta;
- dst += 2;
- }
+ hantro_av1_set_global_model(ctx);
hantro_write_addr(vpu, AV1_GLOBAL_MODEL, av1_dec->global_model.dma);
}
-static int rockchip_vpu981_av1_tile_log2(int target)
-{
- int k;
-
- /*
- * returns the smallest value for k such that 1 << k is greater
- * than or equal to target
- */
- for (k = 0; (1 << k) < target; k++);
-
- return k;
-}
-
static void rockchip_vpu981_av1_dec_set_tile_info(struct hantro_ctx *ctx)
{
struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info;
- const struct v4l2_ctrl_av1_tile_group_entry *group_entry =
- ctrls->tile_group_entry;
int context_update_y =
tile_info->context_update_tile_id / tile_info->tile_cols;
int context_update_x =
tile_info->context_update_tile_id % tile_info->tile_cols;
int context_update_tile_id =
context_update_x * tile_info->tile_rows + context_update_y;
- u8 *dst = av1_dec->tile_info.cpu;
struct hantro_dev *vpu = ctx->dev;
- int tile0, tile1;
-
- memset(dst, 0, av1_dec->tile_info.size);
-
- for (tile0 = 0; tile0 < tile_info->tile_cols; tile0++) {
- for (tile1 = 0; tile1 < tile_info->tile_rows; tile1++) {
- int tile_id = tile1 * tile_info->tile_cols + tile0;
- u32 start, end;
- u32 y0 =
- tile_info->height_in_sbs_minus_1[tile1] + 1;
- u32 x0 = tile_info->width_in_sbs_minus_1[tile0] + 1;
-
- /* tile size in SB units (width,height) */
- *dst++ = x0;
- *dst++ = 0;
- *dst++ = 0;
- *dst++ = 0;
- *dst++ = y0;
- *dst++ = 0;
- *dst++ = 0;
- *dst++ = 0;
-
- /* tile start position */
- start = group_entry[tile_id].tile_offset - group_entry[0].tile_offset;
- *dst++ = start & 255;
- *dst++ = (start >> 8) & 255;
- *dst++ = (start >> 16) & 255;
- *dst++ = (start >> 24) & 255;
-
- /* number of bytes in tile data */
- end = start + group_entry[tile_id].tile_size;
- *dst++ = end & 255;
- *dst++ = (end >> 8) & 255;
- *dst++ = (end >> 16) & 255;
- *dst++ = (end >> 24) & 255;
- }
- }
+
+ hantro_av1_set_tile_info(ctx);
hantro_reg_write(vpu, &av1_multicore_expect_context_update, !!(context_update_x == 0));
hantro_reg_write(vpu, &av1_tile_enable,
@@ -631,8 +44,8 @@ static void rockchip_vpu981_av1_dec_set_tile_info(struct hantro_ctx *ctx)
hantro_reg_write(vpu, &av1_num_tile_rows_8k, tile_info->tile_rows);
hantro_reg_write(vpu, &av1_context_update_tile_id, context_update_tile_id);
hantro_reg_write(vpu, &av1_tile_transpose, 1);
- if (rockchip_vpu981_av1_tile_log2(tile_info->tile_cols) ||
- rockchip_vpu981_av1_tile_log2(tile_info->tile_rows))
+ if (hantro_av1_tile_log2(tile_info->tile_cols) ||
+ hantro_av1_tile_log2(tile_info->tile_rows))
hantro_reg_write(vpu, &av1_dec_tile_size_mag, tile_info->tile_size_bytes - 1);
else
hantro_reg_write(vpu, &av1_dec_tile_size_mag, 3);
@@ -640,50 +53,6 @@ static void rockchip_vpu981_av1_dec_set_tile_info(struct hantro_ctx *ctx)
hantro_write_addr(vpu, AV1_TILE_BASE, av1_dec->tile_info.dma);
}
-static int rockchip_vpu981_av1_dec_get_dist(struct hantro_ctx *ctx,
- int a, int b)
-{
- struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
- struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
- int bits = ctrls->sequence->order_hint_bits - 1;
- int diff, m;
-
- if (!ctrls->sequence->order_hint_bits)
- return 0;
-
- diff = a - b;
- m = 1 << bits;
- diff = (diff & (m - 1)) - (diff & m);
-
- return diff;
-}
-
-static void rockchip_vpu981_av1_dec_set_frame_sign_bias(struct hantro_ctx *ctx)
-{
- struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
- struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
- const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
- const struct v4l2_ctrl_av1_sequence *sequence = ctrls->sequence;
- int i;
-
- if (!sequence->order_hint_bits || IS_INTRA(frame->frame_type)) {
- for (i = 0; i < V4L2_AV1_TOTAL_REFS_PER_FRAME; i++)
- av1_dec->ref_frame_sign_bias[i] = 0;
-
- return;
- }
- // Identify the nearest forward and backward references.
- for (i = 0; i < V4L2_AV1_TOTAL_REFS_PER_FRAME - 1; i++) {
- if (rockchip_vpu981_get_frame_index(ctx, i) >= 0) {
- int rel_off =
- rockchip_vpu981_av1_dec_get_dist(ctx,
- rockchip_vpu981_get_order_hint(ctx, i),
- frame->order_hint);
- av1_dec->ref_frame_sign_bias[i + 1] = (rel_off <= 0) ? 0 : 1;
- }
- }
-}
-
static bool
rockchip_vpu981_av1_dec_set_ref(struct hantro_ctx *ctx, int ref, int idx,
int width, int height)
@@ -806,12 +175,12 @@ static void rockchip_vpu981_av1_dec_set_segmentation(struct hantro_ctx *ctx)
if (!!(seg->flags & V4L2_AV1_SEGMENTATION_FLAG_ENABLED) &&
frame->primary_ref_frame < V4L2_AV1_REFS_PER_FRAME) {
- int idx = rockchip_vpu981_get_frame_index(ctx, frame->primary_ref_frame);
+ int idx = hantro_av1_get_frame_index(ctx, frame->primary_ref_frame);
- if (idx >= 0) {
+ if (idx != AV1_INVALID_IDX) {
dma_addr_t luma_addr, mv_addr = 0;
struct hantro_decoded_buffer *seg;
- size_t mv_offset = rockchip_vpu981_av1_dec_chroma_size(ctx);
+ size_t mv_offset = hantro_av1_chroma_size(ctx);
seg = vb2_to_hantro_decoded_buf(&av1_dec->frame_refs[idx].vb2_ref->vb2_buf);
luma_addr = hantro_get_dec_buf_addr(ctx, &seg->base.vb.vb2_buf);
@@ -1041,35 +410,6 @@ static void rockchip_vpu981_av1_dec_set_segmentation(struct hantro_ctx *ctx)
segval[7][V4L2_AV1_SEG_LVL_REF_GLOBALMV]);
}
-static bool rockchip_vpu981_av1_dec_is_lossless(struct hantro_ctx *ctx)
-{
- struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
- struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
- const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
- const struct v4l2_av1_segmentation *segmentation = &frame->segmentation;
- const struct v4l2_av1_quantization *quantization = &frame->quantization;
- int i;
-
- for (i = 0; i < V4L2_AV1_MAX_SEGMENTS; i++) {
- int qindex = quantization->base_q_idx;
-
- if (segmentation->feature_enabled[i] &
- V4L2_AV1_SEGMENT_FEATURE_ENABLED(V4L2_AV1_SEG_LVL_ALT_Q)) {
- qindex += segmentation->feature_data[i][V4L2_AV1_SEG_LVL_ALT_Q];
- }
- qindex = clamp(qindex, 0, 255);
-
- if (qindex ||
- quantization->delta_q_y_dc ||
- quantization->delta_q_u_dc ||
- quantization->delta_q_u_ac ||
- quantization->delta_q_v_dc ||
- quantization->delta_q_v_ac)
- return false;
- }
- return true;
-}
-
static void rockchip_vpu981_av1_dec_set_loopfilter(struct hantro_ctx *ctx)
{
struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
@@ -1089,7 +429,7 @@ static void rockchip_vpu981_av1_dec_set_loopfilter(struct hantro_ctx *ctx)
hantro_reg_write(vpu, &av1_filt_level3, loop_filter->level[3]);
if (loop_filter->flags & V4L2_AV1_LOOP_FILTER_FLAG_DELTA_ENABLED &&
- !rockchip_vpu981_av1_dec_is_lossless(ctx) &&
+ !hantro_av1_is_lossless(ctx) &&
!(frame->flags & V4L2_AV1_FRAME_FLAG_ALLOW_INTRABC)) {
hantro_reg_write(vpu, &av1_filt_ref_adj_0,
loop_filter->ref_deltas[0]);
@@ -1128,121 +468,27 @@ static void rockchip_vpu981_av1_dec_set_loopfilter(struct hantro_ctx *ctx)
hantro_write_addr(vpu, AV1_DB_CTRL_COL, av1_dec->db_ctrl_col.dma);
}
-static void rockchip_vpu981_av1_dec_update_prob(struct hantro_ctx *ctx)
-{
- struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
- struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
- const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
- bool frame_is_intra = IS_INTRA(frame->frame_type);
- struct av1cdfs *out_cdfs = (struct av1cdfs *)av1_dec->prob_tbl_out.cpu;
- int i;
-
- if (frame->flags & V4L2_AV1_FRAME_FLAG_DISABLE_FRAME_END_UPDATE_CDF)
- return;
-
- for (i = 0; i < NUM_REF_FRAMES; i++) {
- if (frame->refresh_frame_flags & BIT(i)) {
- struct mvcdfs stored_mv_cdf;
-
- rockchip_av1_get_cdfs(ctx, i);
- stored_mv_cdf = av1_dec->cdfs->mv_cdf;
- *av1_dec->cdfs = *out_cdfs;
- if (frame_is_intra) {
- av1_dec->cdfs->mv_cdf = stored_mv_cdf;
- *av1_dec->cdfs_ndvc = out_cdfs->mv_cdf;
- }
- rockchip_av1_store_cdfs(ctx,
- frame->refresh_frame_flags);
- break;
- }
- }
-}
-
-void rockchip_vpu981_av1_dec_done(struct hantro_ctx *ctx)
-{
- rockchip_vpu981_av1_dec_update_prob(ctx);
-}
-
static void rockchip_vpu981_av1_dec_set_prob(struct hantro_ctx *ctx)
{
struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
- struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
- const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
- const struct v4l2_av1_quantization *quantization = &frame->quantization;
struct hantro_dev *vpu = ctx->dev;
- bool error_resilient_mode =
- !!(frame->flags & V4L2_AV1_FRAME_FLAG_ERROR_RESILIENT_MODE);
- bool frame_is_intra = IS_INTRA(frame->frame_type);
-
- if (error_resilient_mode || frame_is_intra ||
- frame->primary_ref_frame == AV1_PRIMARY_REF_NONE) {
- av1_dec->cdfs = &av1_dec->default_cdfs;
- av1_dec->cdfs_ndvc = &av1_dec->default_cdfs_ndvc;
- rockchip_av1_default_coeff_probs(quantization->base_q_idx,
- av1_dec->cdfs);
- } else {
- rockchip_av1_get_cdfs(ctx, frame->ref_frame_idx[frame->primary_ref_frame]);
- }
- rockchip_av1_store_cdfs(ctx, frame->refresh_frame_flags);
-
- memcpy(av1_dec->prob_tbl.cpu, av1_dec->cdfs, sizeof(struct av1cdfs));
-
- if (frame_is_intra) {
- int mv_offset = offsetof(struct av1cdfs, mv_cdf);
- /* Overwrite MV context area with intrabc MV context */
- memcpy(av1_dec->prob_tbl.cpu + mv_offset, av1_dec->cdfs_ndvc,
- sizeof(struct mvcdfs));
- }
+ hantro_av1_set_prob(ctx);
hantro_write_addr(vpu, AV1_PROP_TABLE_OUT, av1_dec->prob_tbl_out.dma);
hantro_write_addr(vpu, AV1_PROP_TABLE, av1_dec->prob_tbl.dma);
}
-static void
-rockchip_vpu981_av1_dec_init_scaling_function(const u8 *values, const u8 *scaling,
- u8 num_points, u8 *scaling_lut)
-{
- int i, point;
-
- if (num_points == 0) {
- memset(scaling_lut, 0, 256);
- return;
- }
-
- for (point = 0; point < num_points - 1; point++) {
- int x;
- s32 delta_y = scaling[point + 1] - scaling[point];
- s32 delta_x = values[point + 1] - values[point];
- s64 delta =
- delta_x ? delta_y * ((65536 + (delta_x >> 1)) /
- delta_x) : 0;
-
- for (x = 0; x < delta_x; x++) {
- scaling_lut[values[point] + x] =
- scaling[point] +
- (s32)((x * delta + 32768) >> 16);
- }
- }
-
- for (i = values[num_points - 1]; i < 256; i++)
- scaling_lut[i] = scaling[num_points - 1];
-}
static void rockchip_vpu981_av1_dec_set_fgs(struct hantro_ctx *ctx)
{
struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
const struct v4l2_ctrl_av1_film_grain *film_grain = ctrls->film_grain;
- struct rockchip_av1_film_grain *fgmem = av1_dec->film_grain.cpu;
+ struct hantro_av1_film_grain *fgmem = av1_dec->film_grain.cpu;
struct hantro_dev *vpu = ctx->dev;
bool scaling_from_luma =
!!(film_grain->flags & V4L2_AV1_FILM_GRAIN_FLAG_CHROMA_SCALING_FROM_LUMA);
- s32 (*ar_coeffs_y)[24];
- s32 (*ar_coeffs_cb)[25];
- s32 (*ar_coeffs_cr)[25];
- s32 (*luma_grain_block)[73][82];
- s32 (*cb_grain_block)[38][44];
- s32 (*cr_grain_block)[38][44];
+ struct hantro_av1_coeffs_grain_block *coeffs;
s32 ar_coeff_lag, ar_coeff_shift;
s32 grain_scale_shift, bitdepth;
s32 grain_center, grain_min, grain_max;
@@ -1269,18 +515,9 @@ static void rockchip_vpu981_av1_dec_set_fgs(struct hantro_ctx *ctx)
return;
}
- ar_coeffs_y = kzalloc(sizeof(int32_t) * 24, GFP_KERNEL);
- ar_coeffs_cb = kzalloc(sizeof(int32_t) * 25, GFP_KERNEL);
- ar_coeffs_cr = kzalloc(sizeof(int32_t) * 25, GFP_KERNEL);
- luma_grain_block = kzalloc(sizeof(int32_t) * 73 * 82, GFP_KERNEL);
- cb_grain_block = kzalloc(sizeof(int32_t) * 38 * 44, GFP_KERNEL);
- cr_grain_block = kzalloc(sizeof(int32_t) * 38 * 44, GFP_KERNEL);
-
- if (!ar_coeffs_y || !ar_coeffs_cb || !ar_coeffs_cr ||
- !luma_grain_block || !cb_grain_block || !cr_grain_block) {
- pr_warn("Fail allocating memory for film grain parameters\n");
- goto alloc_fail;
- }
+ coeffs = kzalloc(sizeof(*coeffs), GFP_KERNEL);
+ if (!coeffs)
+ return;
hantro_reg_write(vpu, &av1_apply_grain, 1);
@@ -1316,10 +553,10 @@ static void rockchip_vpu981_av1_dec_set_fgs(struct hantro_ctx *ctx)
hantro_reg_write(vpu, &av1_chroma_scaling_from_luma, scaling_from_luma);
hantro_reg_write(vpu, &av1_random_seed, film_grain->grain_seed);
- rockchip_vpu981_av1_dec_init_scaling_function(film_grain->point_y_value,
- film_grain->point_y_scaling,
- film_grain->num_y_points,
- fgmem->scaling_lut_y);
+ hantro_av1_init_scaling_function(film_grain->point_y_value,
+ film_grain->point_y_scaling,
+ film_grain->num_y_points,
+ fgmem->scaling_lut_y);
if (film_grain->flags &
V4L2_AV1_FILM_GRAIN_FLAG_CHROMA_SCALING_FROM_LUMA) {
@@ -1328,19 +565,19 @@ static void rockchip_vpu981_av1_dec_set_fgs(struct hantro_ctx *ctx)
memcpy(fgmem->scaling_lut_cr, fgmem->scaling_lut_y,
sizeof(*fgmem->scaling_lut_y) * 256);
} else {
- rockchip_vpu981_av1_dec_init_scaling_function
+ hantro_av1_init_scaling_function
(film_grain->point_cb_value, film_grain->point_cb_scaling,
film_grain->num_cb_points, fgmem->scaling_lut_cb);
- rockchip_vpu981_av1_dec_init_scaling_function
+ hantro_av1_init_scaling_function
(film_grain->point_cr_value, film_grain->point_cr_scaling,
film_grain->num_cr_points, fgmem->scaling_lut_cr);
}
for (i = 0; i < V4L2_AV1_AR_COEFFS_SIZE; i++) {
if (i < 24)
- (*ar_coeffs_y)[i] = film_grain->ar_coeffs_y_plus_128[i] - 128;
- (*ar_coeffs_cb)[i] = film_grain->ar_coeffs_cb_plus_128[i] - 128;
- (*ar_coeffs_cr)[i] = film_grain->ar_coeffs_cr_plus_128[i] - 128;
+ coeffs->ar_coeffs_y[i] = film_grain->ar_coeffs_y_plus_128[i] - 128;
+ coeffs->ar_coeffs_cb[i] = film_grain->ar_coeffs_cb_plus_128[i] - 128;
+ coeffs->ar_coeffs_cr[i] = film_grain->ar_coeffs_cr_plus_128[i] - 128;
}
ar_coeff_lag = film_grain->ar_coeff_lag;
@@ -1351,46 +588,38 @@ static void rockchip_vpu981_av1_dec_set_fgs(struct hantro_ctx *ctx)
grain_min = 0 - grain_center;
grain_max = (256 << (bitdepth - 8)) - 1 - grain_center;
- rockchip_av1_generate_luma_grain_block(luma_grain_block, bitdepth,
- film_grain->num_y_points, grain_scale_shift,
- ar_coeff_lag, ar_coeffs_y, ar_coeff_shift,
- grain_min, grain_max, film_grain->grain_seed);
-
- rockchip_av1_generate_chroma_grain_block(luma_grain_block, cb_grain_block,
- cr_grain_block, bitdepth,
- film_grain->num_y_points,
- film_grain->num_cb_points,
- film_grain->num_cr_points,
- grain_scale_shift, ar_coeff_lag, ar_coeffs_cb,
- ar_coeffs_cr, ar_coeff_shift, grain_min,
- grain_max,
- scaling_from_luma,
- film_grain->grain_seed);
+ hantro_av1_generate_luma_grain_block(coeffs, bitdepth,
+ film_grain->num_y_points, grain_scale_shift,
+ ar_coeff_lag, ar_coeff_shift,
+ grain_min, grain_max, film_grain->grain_seed);
+
+ hantro_av1_generate_chroma_grain_block(coeffs, bitdepth,
+ film_grain->num_y_points,
+ film_grain->num_cb_points,
+ film_grain->num_cr_points,
+ grain_scale_shift, ar_coeff_lag,
+ ar_coeff_shift, grain_min,
+ grain_max,
+ scaling_from_luma,
+ film_grain->grain_seed);
for (i = 0; i < 64; i++) {
for (j = 0; j < 64; j++)
fgmem->cropped_luma_grain_block[i * 64 + j] =
- (*luma_grain_block)[i + 9][j + 9];
+ coeffs->luma_grain_block[i + 9][j + 9];
}
for (i = 0; i < 32; i++) {
for (j = 0; j < 32; j++) {
fgmem->cropped_chroma_grain_block[i * 64 + 2 * j] =
- (*cb_grain_block)[i + 6][j + 6];
+ coeffs->cb_grain_block[i + 6][j + 6];
fgmem->cropped_chroma_grain_block[i * 64 + 2 * j + 1] =
- (*cr_grain_block)[i + 6][j + 6];
+ coeffs->cr_grain_block[i + 6][j + 6];
}
}
+ kfree(coeffs);
hantro_write_addr(vpu, AV1_FILM_GRAIN, av1_dec->film_grain.dma);
-
-alloc_fail:
- kfree(ar_coeffs_y);
- kfree(ar_coeffs_cb);
- kfree(ar_coeffs_cr);
- kfree(luma_grain_block);
- kfree(cb_grain_block);
- kfree(cr_grain_block);
}
static void rockchip_vpu981_av1_dec_set_cdef(struct hantro_ctx *ctx)
@@ -1617,13 +846,13 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
int ref_ind = 0;
int rf, idx;
- alt_frame_offset = rockchip_vpu981_get_order_hint(ctx, ALT_BUF_IDX);
- gld_frame_offset = rockchip_vpu981_get_order_hint(ctx, GLD_BUF_IDX);
- bwd_frame_offset = rockchip_vpu981_get_order_hint(ctx, BWD_BUF_IDX);
- alt2_frame_offset = rockchip_vpu981_get_order_hint(ctx, ALT2_BUF_IDX);
+ alt_frame_offset = hantro_av1_get_order_hint(ctx, ALT_BUF_IDX);
+ gld_frame_offset = hantro_av1_get_order_hint(ctx, GLD_BUF_IDX);
+ bwd_frame_offset = hantro_av1_get_order_hint(ctx, BWD_BUF_IDX);
+ alt2_frame_offset = hantro_av1_get_order_hint(ctx, ALT2_BUF_IDX);
- idx = rockchip_vpu981_get_frame_index(ctx, LST_BUF_IDX);
- if (idx >= 0) {
+ idx = hantro_av1_get_frame_index(ctx, LST_BUF_IDX);
+ if (idx != AV1_INVALID_IDX) {
int alt_frame_offset_in_lst =
av1_dec->frame_refs[idx].order_hints[V4L2_AV1_REF_ALTREF_FRAME];
bool is_lst_overlay =
@@ -1644,8 +873,9 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
ref_stamp--;
}
- idx = rockchip_vpu981_get_frame_index(ctx, BWD_BUF_IDX);
- if (rockchip_vpu981_av1_dec_get_dist(ctx, bwd_frame_offset, cur_frame_offset) > 0) {
+ idx = hantro_av1_get_frame_index(ctx, BWD_BUF_IDX);
+ if (idx != AV1_INVALID_IDX &&
+ hantro_av1_get_dist(ctx, bwd_frame_offset, cur_frame_offset) > 0) {
int bwd_mi_cols = av1_dec->frame_refs[idx].mi_cols;
int bwd_mi_rows = av1_dec->frame_refs[idx].mi_rows;
bool bwd_intra_only =
@@ -1659,8 +889,9 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
}
}
- idx = rockchip_vpu981_get_frame_index(ctx, ALT2_BUF_IDX);
- if (rockchip_vpu981_av1_dec_get_dist(ctx, alt2_frame_offset, cur_frame_offset) > 0) {
+ idx = hantro_av1_get_frame_index(ctx, ALT2_BUF_IDX);
+ if (idx != AV1_INVALID_IDX &&
+ hantro_av1_get_dist(ctx, alt2_frame_offset, cur_frame_offset) > 0) {
int alt2_mi_cols = av1_dec->frame_refs[idx].mi_cols;
int alt2_mi_rows = av1_dec->frame_refs[idx].mi_rows;
bool alt2_intra_only =
@@ -1674,8 +905,9 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
}
}
- idx = rockchip_vpu981_get_frame_index(ctx, ALT_BUF_IDX);
- if (rockchip_vpu981_av1_dec_get_dist(ctx, alt_frame_offset, cur_frame_offset) > 0 &&
+ idx = hantro_av1_get_frame_index(ctx, ALT_BUF_IDX);
+ if (idx != AV1_INVALID_IDX &&
+ hantro_av1_get_dist(ctx, alt_frame_offset, cur_frame_offset) > 0 &&
ref_stamp >= 0) {
int alt_mi_cols = av1_dec->frame_refs[idx].mi_cols;
int alt_mi_rows = av1_dec->frame_refs[idx].mi_rows;
@@ -1690,8 +922,8 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
}
}
- idx = rockchip_vpu981_get_frame_index(ctx, LST2_BUF_IDX);
- if (idx >= 0 && ref_stamp >= 0) {
+ idx = hantro_av1_get_frame_index(ctx, LST2_BUF_IDX);
+ if (idx != AV1_INVALID_IDX && ref_stamp >= 0) {
int lst2_mi_cols = av1_dec->frame_refs[idx].mi_cols;
int lst2_mi_rows = av1_dec->frame_refs[idx].mi_rows;
bool lst2_intra_only =
@@ -1706,14 +938,14 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
}
for (rf = 0; rf < V4L2_AV1_TOTAL_REFS_PER_FRAME - 1; ++rf) {
- idx = rockchip_vpu981_get_frame_index(ctx, rf);
- if (idx >= 0) {
- int rf_order_hint = rockchip_vpu981_get_order_hint(ctx, rf);
+ idx = hantro_av1_get_frame_index(ctx, rf);
+ if (idx != AV1_INVALID_IDX) {
+ int rf_order_hint = hantro_av1_get_order_hint(ctx, rf);
cur_offset[rf] =
- rockchip_vpu981_av1_dec_get_dist(ctx, cur_frame_offset, rf_order_hint);
+ hantro_av1_get_dist(ctx, cur_frame_offset, rf_order_hint);
cur_roffset[rf] =
- rockchip_vpu981_av1_dec_get_dist(ctx, rf_order_hint, cur_frame_offset);
+ hantro_av1_get_dist(ctx, rf_order_hint, cur_frame_offset);
} else {
cur_offset[rf] = 0;
cur_roffset[rf] = 0;
@@ -1736,35 +968,41 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
if (use_ref_frame_mvs && ref_ind > 0 &&
cur_offset[mf_types[0] - V4L2_AV1_REF_LAST_FRAME] <= MAX_FRAME_DISTANCE &&
cur_offset[mf_types[0] - V4L2_AV1_REF_LAST_FRAME] >= -MAX_FRAME_DISTANCE) {
- int rf = rockchip_vpu981_get_order_hint(ctx, refs_selected[0]);
- int idx = rockchip_vpu981_get_frame_index(ctx, refs_selected[0]);
- u32 *oh = av1_dec->frame_refs[idx].order_hints;
+ int rf = hantro_av1_get_order_hint(ctx, refs_selected[0]);
+ int idx = hantro_av1_get_frame_index(ctx, refs_selected[0]);
+ u32 *oh;
int val;
+ if (idx == AV1_INVALID_IDX)
+ goto second_ref;
+
+ oh = av1_dec->frame_refs[idx].order_hints;
+
hantro_reg_write(vpu, &av1_use_temporal0_mvs, 1);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
hantro_reg_write(vpu, &av1_mf1_last_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
hantro_reg_write(vpu, &av1_mf1_last2_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
hantro_reg_write(vpu, &av1_mf1_last3_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
hantro_reg_write(vpu, &av1_mf1_golden_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
hantro_reg_write(vpu, &av1_mf1_bwdref_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
hantro_reg_write(vpu, &av1_mf1_altref2_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
hantro_reg_write(vpu, &av1_mf1_altref_offset, val);
}
+second_ref:
hantro_reg_write(vpu, &av1_mf2_last_offset, 0);
hantro_reg_write(vpu, &av1_mf2_last2_offset, 0);
hantro_reg_write(vpu, &av1_mf2_last3_offset, 0);
@@ -1776,35 +1014,41 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
if (use_ref_frame_mvs && ref_ind > 1 &&
cur_offset[mf_types[1] - V4L2_AV1_REF_LAST_FRAME] <= MAX_FRAME_DISTANCE &&
cur_offset[mf_types[1] - V4L2_AV1_REF_LAST_FRAME] >= -MAX_FRAME_DISTANCE) {
- int rf = rockchip_vpu981_get_order_hint(ctx, refs_selected[1]);
- int idx = rockchip_vpu981_get_frame_index(ctx, refs_selected[1]);
- u32 *oh = av1_dec->frame_refs[idx].order_hints;
+ int rf = hantro_av1_get_order_hint(ctx, refs_selected[1]);
+ int idx = hantro_av1_get_frame_index(ctx, refs_selected[1]);
+ u32 *oh;
int val;
+ if (idx == AV1_INVALID_IDX)
+ goto third_ref;
+
+ oh = av1_dec->frame_refs[idx].order_hints;
+
hantro_reg_write(vpu, &av1_use_temporal1_mvs, 1);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
hantro_reg_write(vpu, &av1_mf2_last_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
hantro_reg_write(vpu, &av1_mf2_last2_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
hantro_reg_write(vpu, &av1_mf2_last3_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
hantro_reg_write(vpu, &av1_mf2_golden_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
hantro_reg_write(vpu, &av1_mf2_bwdref_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
hantro_reg_write(vpu, &av1_mf2_altref2_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
hantro_reg_write(vpu, &av1_mf2_altref_offset, val);
}
+third_ref:
hantro_reg_write(vpu, &av1_mf3_last_offset, 0);
hantro_reg_write(vpu, &av1_mf3_last2_offset, 0);
hantro_reg_write(vpu, &av1_mf3_last3_offset, 0);
@@ -1816,35 +1060,41 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
if (use_ref_frame_mvs && ref_ind > 2 &&
cur_offset[mf_types[2] - V4L2_AV1_REF_LAST_FRAME] <= MAX_FRAME_DISTANCE &&
cur_offset[mf_types[2] - V4L2_AV1_REF_LAST_FRAME] >= -MAX_FRAME_DISTANCE) {
- int rf = rockchip_vpu981_get_order_hint(ctx, refs_selected[2]);
- int idx = rockchip_vpu981_get_frame_index(ctx, refs_selected[2]);
- u32 *oh = av1_dec->frame_refs[idx].order_hints;
+ int rf = hantro_av1_get_order_hint(ctx, refs_selected[2]);
+ int idx = hantro_av1_get_frame_index(ctx, refs_selected[2]);
+ u32 *oh;
int val;
+ if (idx == AV1_INVALID_IDX)
+ goto offsets;
+
+ oh = av1_dec->frame_refs[idx].order_hints;
+
hantro_reg_write(vpu, &av1_use_temporal2_mvs, 1);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
hantro_reg_write(vpu, &av1_mf3_last_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
hantro_reg_write(vpu, &av1_mf3_last2_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
hantro_reg_write(vpu, &av1_mf3_last3_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
hantro_reg_write(vpu, &av1_mf3_golden_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
hantro_reg_write(vpu, &av1_mf3_bwdref_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
hantro_reg_write(vpu, &av1_mf3_altref2_offset, val);
- val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
+ val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
hantro_reg_write(vpu, &av1_mf3_altref_offset, val);
}
+offsets:
hantro_reg_write(vpu, &av1_cur_last_offset, cur_offset[0]);
hantro_reg_write(vpu, &av1_cur_last2_offset, cur_offset[1]);
hantro_reg_write(vpu, &av1_cur_last3_offset, cur_offset[2]);
@@ -1883,9 +1133,9 @@ static void rockchip_vpu981_av1_dec_set_reference_frames(struct hantro_ctx *ctx)
if (!allow_intrabc) {
for (i = 0; i < V4L2_AV1_REFS_PER_FRAME; i++) {
- int idx = rockchip_vpu981_get_frame_index(ctx, i);
+ int idx = hantro_av1_get_frame_index(ctx, i);
- if (idx >= 0)
+ if (idx != AV1_INVALID_IDX)
ref_count[idx]++;
}
@@ -1898,7 +1148,7 @@ static void rockchip_vpu981_av1_dec_set_reference_frames(struct hantro_ctx *ctx)
}
hantro_reg_write(vpu, &av1_ref_frames, ref_frames);
- rockchip_vpu981_av1_dec_set_frame_sign_bias(ctx);
+ hantro_av1_set_frame_sign_bias(ctx);
for (i = V4L2_AV1_REF_LAST_FRAME; i < V4L2_AV1_TOTAL_REFS_PER_FRAME; i++) {
u32 ref = i - 1;
@@ -1910,8 +1160,8 @@ static void rockchip_vpu981_av1_dec_set_reference_frames(struct hantro_ctx *ctx)
width = frame->frame_width_minus_1 + 1;
height = frame->frame_height_minus_1 + 1;
} else {
- if (rockchip_vpu981_get_frame_index(ctx, ref) > 0)
- idx = rockchip_vpu981_get_frame_index(ctx, ref);
+ if (hantro_av1_get_frame_index(ctx, ref) != AV1_INVALID_IDX)
+ idx = hantro_av1_get_frame_index(ctx, ref);
width = av1_dec->frame_refs[idx].width;
height = av1_dec->frame_refs[idx].height;
}
@@ -1943,20 +1193,6 @@ static void rockchip_vpu981_av1_dec_set_reference_frames(struct hantro_ctx *ctx)
rockchip_vpu981_av1_dec_set_other_frames(ctx);
}
-static int rockchip_vpu981_av1_get_hardware_tx_mode(enum v4l2_av1_tx_mode tx_mode)
-{
- switch (tx_mode) {
- case V4L2_AV1_TX_MODE_ONLY_4X4:
- return ROCKCHIP_AV1_TX_MODE_ONLY_4X4;
- case V4L2_AV1_TX_MODE_LARGEST:
- return ROCKCHIP_AV1_TX_MODE_32x32;
- case V4L2_AV1_TX_MODE_SELECT:
- return ROCKCHIP_AV1_TX_MODE_SELECT;
- }
-
- return ROCKCHIP_AV1_TX_MODE_32x32;
-}
-
static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx)
{
struct hantro_dev *vpu = ctx->dev;
@@ -2029,7 +1265,7 @@ static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx)
hantro_reg_write(vpu, &av1_comp_pred_mode,
(ctrls->frame->flags & V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT) ? 2 : 0);
- tx_mode = rockchip_vpu981_av1_get_hardware_tx_mode(ctrls->frame->tx_mode);
+ tx_mode = hantro_av1_get_hardware_tx_mode(ctrls->frame->tx_mode);
hantro_reg_write(vpu, &av1_transform_mode, tx_mode);
hantro_reg_write(vpu, &av1_max_cb_size,
(ctrls->sequence->flags
@@ -2061,7 +1297,7 @@ static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx)
hantro_reg_write(vpu, &av1_qmlevel_v, 0xff);
}
- hantro_reg_write(vpu, &av1_lossless_e, rockchip_vpu981_av1_dec_is_lossless(ctx));
+ hantro_reg_write(vpu, &av1_lossless_e, hantro_av1_is_lossless(ctx));
hantro_reg_write(vpu, &av1_quant_delta_v_dc, ctrls->frame->quantization.delta_q_v_dc);
hantro_reg_write(vpu, &av1_quant_delta_v_ac, ctrls->frame->quantization.delta_q_v_ac);
@@ -2109,8 +1345,8 @@ rockchip_vpu981_av1_dec_set_output_buffer(struct hantro_ctx *ctx)
struct hantro_decoded_buffer *dst;
struct vb2_v4l2_buffer *vb2_dst;
dma_addr_t luma_addr, chroma_addr, mv_addr = 0;
- size_t cr_offset = rockchip_vpu981_av1_dec_luma_size(ctx);
- size_t mv_offset = rockchip_vpu981_av1_dec_chroma_size(ctx);
+ size_t cr_offset = hantro_av1_luma_size(ctx);
+ size_t mv_offset = hantro_av1_chroma_size(ctx);
vb2_dst = av1_dec->frame_refs[av1_dec->current_frame_index].vb2_ref;
dst = vb2_to_hantro_decoded_buf(&vb2_dst->vb2_buf);
@@ -2134,7 +1370,7 @@ int rockchip_vpu981_av1_dec_run(struct hantro_ctx *ctx)
hantro_start_prepare_run(ctx);
- ret = rockchip_vpu981_av1_dec_prepare_run(ctx);
+ ret = hantro_av1_prepare_run(ctx);
if (ret)
goto prepare_error;
@@ -2144,8 +1380,8 @@ int rockchip_vpu981_av1_dec_run(struct hantro_ctx *ctx)
goto prepare_error;
}
- rockchip_vpu981_av1_dec_clean_refs(ctx);
- rockchip_vpu981_av1_dec_frame_ref(ctx, vb2_src->vb2_buf.timestamp);
+ hantro_av1_clean_refs(ctx);
+ hantro_av1_frame_ref(ctx, vb2_src->vb2_buf.timestamp);
rockchip_vpu981_av1_dec_set_parameters(ctx);
rockchip_vpu981_av1_dec_set_global_model(ctx);
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
index 02673be9878e..f50a3e38097e 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
@@ -9,6 +9,7 @@
#include <linux/clk.h>
#include "hantro.h"
+#include "hantro_av1.h"
#include "hantro_jpeg.h"
#include "hantro_g1_regs.h"
#include "hantro_h1_regs.h"
@@ -608,9 +609,9 @@ static const struct hantro_codec_ops rk3568_vepu_codec_ops[] = {
static const struct hantro_codec_ops rk3588_vpu981_codec_ops[] = {
[HANTRO_MODE_AV1_DEC] = {
.run = rockchip_vpu981_av1_dec_run,
- .init = rockchip_vpu981_av1_dec_init,
- .exit = rockchip_vpu981_av1_dec_exit,
- .done = rockchip_vpu981_av1_dec_done,
+ .init = hantro_av1_init,
+ .exit = hantro_av1_exit,
+ .done = hantro_av1_update_prob,
},
};
/*
--
2.43.0
^ permalink raw reply related
* Re: [PATCH 2/3] [v5 net-next] p54spi: convert to devicetree
From: Simon Horman @ 2026-05-12 9:40 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Arnd Bergmann, Netdev, Aaro Koskinen, Andreas Kemnade,
Bartosz Golaszewski, Benoît Cousson, David S . Miller,
Dmitry Torokhov, Eric Dumazet, Felipe Balbi, Jakub Kicinski,
Johannes Berg, Kevin Hilman, krzk+dt, Linus Walleij, Paolo Abeni,
Rob Herring, Roger Quadros, Tony Lindgren, linux-wireless,
devicetree, linux-kernel, linux-arm-kernel,
open list:GPIO SUBSYSTEM, Linux-OMAP, Krzysztof Kozlowski
In-Reply-To: <edc4d6d6-7a43-442e-adb5-148e37402924@app.fastmail.com>
On Mon, May 11, 2026 at 09:45:18PM +0200, Arnd Bergmann wrote:
> On Mon, May 11, 2026, at 18:12, Simon Horman wrote:
> >
> > Simon says: As a pre-existing issue I don't think we need to be concerned
> > about this in the scope of this patch-set. But it may
> > be a potential for follow-up.
> >
> > This problem wasn't introduced by this patch, but I noticed a pre-existing
> > issue in the context here. Does the p54spi_spi_read() function perform an SPI
> > transfer using a stack-allocated buffer?
> >
> > Looking at p54spi_spi_read(), the addr variable is allocated on the stack and
> > passed as a transfer buffer to spi_sync(). The SPI framework maps transfer
> > buffers for DMA. Passing a stack-allocated buffer is illegal, particularly with
> > CONFIG_VMAP_STACK enabled, because stack memory resides in vmalloc space and
> > cannot be safely DMA-mapped.
> >
> > Could this cause memory corruption or a kernel panic during the transfer?
>
> I already commented on this earlier: This is indeed a theoretical
> bug, but it works in practice because DMA-capable SPI controllers
> tend to all use MMIO for short transfers instead of DMA.
>
> SPI is very fragile this way, and I do have some patches to improve
> that overall, but it seems out of scope for this one driver.
Thanks, I agree this is out of scope.
> >> -
> >> - gpio_direction_output(p54spi_gpio_power, 0);
> >> - gpio_direction_input(p54spi_gpio_irq);
> >> -
> >> - ret = request_irq(gpio_to_irq(p54spi_gpio_irq),
> >> - p54spi_interrupt, IRQF_NO_AUTOEN, "p54spi",
> >> - priv->spi);
> >> + ret = request_irq(spi->irq, p54spi_interrupt, IRQF_NO_AUTOEN, "p54spi", priv->spi);
> >
> > Simon says: This one seems more concerning in the context of this patch-set.
> >
> > Is there missing validation for spi->irq before calling request_irq()?
> >
> > If the device tree node lacks an interrupts property, the SPI core may set
> > spi->irq to 0 or a negative error code.
> >
> > Passing this directly to request_irq() without validation can successfully
> > hijack a core system interrupt like the system timer on some architectures,
> > leading to system instability or deadlocks.
> >
> > Should there be a check like if (spi->irq <= 0) to fail the probe gracefully?
>
> I also commented on this: request_irq() already fails gracefully
> with -EINVAL when presented with an invalid IRQ. IRQ 0 is guaranteed
> to be invalid on any target that uses devicetree.
Thanks, and sorry for not seeing your earlier comment.
Or realising it is a false positive.
^ permalink raw reply
* Re: [PATCH net-next 1/2] net: ti: icssg: Derive stats array lengths from ARRAY_SIZE
From: MD Danish Anwar @ 2026-05-12 9:40 UTC (permalink / raw)
To: David CARLIER
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Shuah Khan, Roger Quadros,
Andrew Lunn, Jacob Keller, Meghana Malladi, Kevin Hao,
Vadim Fedorenko, netdev, linux-doc, linux-kernel,
linux-arm-kernel, Vignesh Raghavendra
In-Reply-To: <CA+XhMqykBWcMdk+iNnOtUxM4MX6jpDyUwfuAVZFbjAShO9_v7Q@mail.gmail.com>
Hi David,
On 12/05/26 1:28 pm, David CARLIER wrote:
> Hi MD,
>
> On Tue, 12 May 2026 at 07:06, MD Danish Anwar <danishanwar@ti.com> wrote:
>>
>> Replace the manually maintained ICSSG_NUM_MIIG_STATS and
>> ICSSG_NUM_PA_STATS constants with ARRAY_SIZE() expressions derived
>> directly from the corresponding stat descriptor arrays, so that adding
>> new entries to icssg_all_miig_stats[] or icssg_all_pa_stats[] no longer
>> requires a separate update to a numeric constant.
>>
>> To make this self-contained, break the circular include dependency
>> between icssg_stats.h and icssg_prueth.h:
>>
>> - icssg_stats.h previously included icssg_prueth.h (transitively
>> pulling in icssg_switch_map.h and ETH_GSTRING_LEN). Replace that
>> with direct includes of <linux/ethtool.h>, <linux/kernel.h> and
>> "icssg_switch_map.h".
>>
>> - icssg_prueth.h now includes icssg_stats.h, giving it access to
>> the ARRAY_SIZE-based ICSSG_NUM_MIIG_STATS and ICSSG_NUM_PA_STATS
>> before they are used in the prueth_emac struct and ICSSG_NUM_STATS.
>>
>> Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
>> ---
>> drivers/net/ethernet/ti/icssg/icssg_prueth.h | 3 +--
>> drivers/net/ethernet/ti/icssg/icssg_stats.h | 7 ++++++-
>> 2 files changed, 7 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
>> index df93d15c5b78..e2ccecb0a0dd 100644
>> --- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
>> +++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
>> @@ -43,6 +43,7 @@
>>
>> #include "icssg_config.h"
>> #include "icss_iep.h"
>> +#include "icssg_stats.h"
>> #include "icssg_switch_map.h"
>>
>> #define PRUETH_MAX_MTU (2000 - ETH_HLEN - ETH_FCS_LEN)
>> @@ -57,8 +58,6 @@
>>
>> #define ICSSG_MAX_RFLOWS 8 /* per slice */
>>
>> -#define ICSSG_NUM_PA_STATS 32
>> -#define ICSSG_NUM_MIIG_STATS 60
>> /* Number of ICSSG related stats */
>> #define ICSSG_NUM_STATS (ICSSG_NUM_MIIG_STATS + ICSSG_NUM_PA_STATS)
>> #define ICSSG_NUM_STANDARD_STATS 31
>> diff --git a/drivers/net/ethernet/ti/icssg/icssg_stats.h b/drivers/net/ethernet/ti/icssg/icssg_stats.h
>> index 5ec0b38e0c67..b854eb587c1e 100644
>> --- a/drivers/net/ethernet/ti/icssg/icssg_stats.h
>> +++ b/drivers/net/ethernet/ti/icssg/icssg_stats.h
>> @@ -8,10 +8,15 @@
>> #ifndef __NET_TI_ICSSG_STATS_H
>> #define __NET_TI_ICSSG_STATS_H
>>
>> -#include "icssg_prueth.h"
>> +#include <linux/ethtool.h>
>> +#include <linux/kernel.h>
>> +#include "icssg_switch_map.h"
>>
>> #define STATS_TIME_LIMIT_1G_MS 25000 /* 25 seconds @ 1G */
>>
>> +#define ICSSG_NUM_MIIG_STATS ARRAY_SIZE(icssg_all_miig_stats)
>> +#define ICSSG_NUM_PA_STATS ARRAY_SIZE(icssg_all_pa_stats)
>> +
>> struct miig_stats_regs {
>> /* Rx */
>> u32 rx_packets;
>> --
>> 2.34.1
>>
>
> One thing that caught my eye: icssg_all_miig_stats[] and
> icssg_all_pa_stats[] are 'static const' arrays in icssg_stats.h with
> ETH_GSTRING_LEN name buffers per entry. Right now only icssg_stats.c
> and icssg_ethtool.c pull them in. After this patch icssg_prueth.h
> includes icssg_stats.h, so every .c in the driver (classifier,
> common, config, mii_cfg, queues, switchdev, ...) ends up with its own
> static-const copy of both tables.
>
> Would a static_assert() work for what you're after? Something like:
>
While adding more stats manually, The ARRAY_SIZE() approach was
explicitly requested by maintainer [1]:
This patch is a direct response to that feedback. static_assert() would
still require updating the numeric constant on every array change. The
goal here is to eliminate the need of manually incrementing stats count
whenever new stats are added
Your concern about multiple copies of table is noted and valid. Could
you advise on the preferred way to reconcile these two requirements? I
am happy to restructure if there is an approach that satisfies both.
[1]
https://lore.kernel.org/all/20260112181436.4s5ceywwembn674r@skbuf/#:~:text=Can%27t%20this%20be%20expressed%20as%20ARRAY_SIZE(icssg_all_pa_stats)%3F%20It%20is%20very%0Afragile%20to%20have%20to%20count%20and%20update%20this%20manually.
> static const struct icssg_miig_stats icssg_all_miig_stats[] = {
> ...
> };
> static_assert(ARRAY_SIZE(icssg_all_miig_stats) == ICSSG_NUM_MIIG_STATS);
>
> next to each array, keeping the numeric #defines as-is. Then 2/2 fails
> to build the moment a new entry is added without bumping the count,
> which is the case you're guarding against — without touching the
> include graph.
>
> What do you think ?
>
> Cheers.
--
Thanks and Regards,
Danish
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox