Re: [PATCH v3] drm/etnaviv: Request pages from DMA32 zone on addressing_limited
Humble ping ? On 9/3/24 10:08, Xiaolei Wang wrote: Remove __GFP_HIGHMEM when requesting a page from DMA32 zone, and since all vivante GPUs in the system will share the same DMA constraints, move the check of whether to get a page from DMA32 to etnaviv_bind(). Fixes: b72af445cd38 ("drm/etnaviv: request pages from DMA32 zone when needed") Suggested-by: Sui Jingfeng Signed-off-by: Xiaolei Wang --- change log v1: https://patchwork.kernel.org/project/dri-devel/patch/20240806104733.2018783-1-xiaolei.w...@windriver.com/ v2: Modify the issue of not retaining GFP_USER in v1 and update the commit log. v3: Use "priv->shm_gfp_mask = GFP_USER | __GFP_RETRY_MAYFAIL | __GFP_NOWARN;" instead of "priv->shm_gfp_mask = GFP_HIGHUSER | __GFP_RETRY_MAYFAIL | __GFP_NOWARN;" and move the check of whether to get a page from DMA32 to etnaviv_bind(). drivers/gpu/drm/etnaviv/etnaviv_drv.c | 10 +- drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 8 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 6500f3999c5f..8cb2c3ec8e5d 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -536,7 +536,15 @@ static int etnaviv_bind(struct device *dev) mutex_init(&priv->gem_lock); INIT_LIST_HEAD(&priv->gem_list); priv->num_gpus = 0; - priv->shm_gfp_mask = GFP_HIGHUSER | __GFP_RETRY_MAYFAIL | __GFP_NOWARN; + priv->shm_gfp_mask = GFP_USER | __GFP_RETRY_MAYFAIL | __GFP_NOWARN; + + /* +* If the GPU is part of a system with DMA addressing limitations, +* request pages for our SHM backend buffers from the DMA32 zone to +* hopefully avoid performance killing SWIOTLB bounce buffering. +*/ + if (dma_addressing_limited(dev)) + priv->shm_gfp_mask |= GFP_DMA32; priv->cmdbuf_suballoc = etnaviv_cmdbuf_suballoc_new(drm->dev); if (IS_ERR(priv->cmdbuf_suballoc)) { diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 7c7f97793ddd..5e753dd42f72 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -839,14 +839,6 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) if (ret) goto fail; - /* -* If the GPU is part of a system with DMA addressing limitations, -* request pages for our SHM backend buffers from the DMA32 zone to -* hopefully avoid performance killing SWIOTLB bounce buffering. -*/ - if (dma_addressing_limited(gpu->dev)) - priv->shm_gfp_mask |= GFP_DMA32; - /* Create buffer: */ ret = etnaviv_cmdbuf_init(priv->cmdbuf_suballoc, &gpu->buffer, PAGE_SIZE); -- Best regards Sui
[PATCH v2 5/5] drm/etnaviv: Replace the '&pdev->dev' with 'dev'
In the etnaviv_pdev_probe() and etnaviv_gpu_platform_probe() function, the value of '&pdev->dev' has been cached to the local auto variable 'dev'. But some callers use 'dev', while the rest use '&pdev->dev'. To keep it consistent, use 'dev' uniformly. Tested-by: Christian Gmeiner Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_drv.c | 10 +- drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 16 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 7844cd961a29..6591e420a051 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -609,7 +609,7 @@ static int etnaviv_pdev_probe(struct platform_device *pdev) if (!of_device_is_available(core_node)) continue; - drm_of_component_match_add(&pdev->dev, &match, + drm_of_component_match_add(dev, &match, component_compare_of, core_node); } } else { @@ -632,9 +632,9 @@ static int etnaviv_pdev_probe(struct platform_device *pdev) * bit to make sure we are allocating the command buffers and * TLBs in the lower 4 GiB address space. */ - if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)) || - dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32))) { - dev_dbg(&pdev->dev, "No suitable DMA available\n"); + if (dma_set_mask(dev, DMA_BIT_MASK(40)) || + dma_set_coherent_mask(dev, DMA_BIT_MASK(32))) { + dev_dbg(dev, "No suitable DMA available\n"); return -ENODEV; } @@ -645,7 +645,7 @@ static int etnaviv_pdev_probe(struct platform_device *pdev) */ first_node = etnaviv_of_first_available_node(); if (first_node) { - of_dma_configure(&pdev->dev, first_node, true); + of_dma_configure(dev, first_node, true); of_node_put(first_node); } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 3c869970cba4..d0df5c53a829 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -1862,7 +1862,7 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) if (!gpu) return -ENOMEM; - gpu->dev = &pdev->dev; + gpu->dev = dev; mutex_init(&gpu->lock); mutex_init(&gpu->sched_lock); @@ -1876,8 +1876,8 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) if (gpu->irq < 0) return gpu->irq; - err = devm_request_irq(&pdev->dev, gpu->irq, irq_handler, 0, - dev_name(gpu->dev), gpu); + err = devm_request_irq(dev, gpu->irq, irq_handler, 0, + dev_name(dev), gpu); if (err) { dev_err(dev, "failed to request IRQ%u: %d\n", gpu->irq, err); return err; @@ -1914,13 +1914,13 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) * autosuspend delay is rather arbitary: no measurements have * yet been performed to determine an appropriate value. */ - pm_runtime_use_autosuspend(gpu->dev); - pm_runtime_set_autosuspend_delay(gpu->dev, 200); - pm_runtime_enable(gpu->dev); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 200); + pm_runtime_enable(dev); - err = component_add(&pdev->dev, &gpu_ops); + err = component_add(dev, &gpu_ops); if (err < 0) { - dev_err(&pdev->dev, "failed to register component: %d\n", err); + dev_err(dev, "failed to register component: %d\n", err); return err; } -- 2.43.0
[PATCH v2 4/5] drm/etnaviv: Fix missing mutex_destroy()
Currently, the calling of mutex_destroy() is ignored on error handling code path. It is safe for now, since mutex_destroy() actually does nothing in non-debug builds. But the mutex_destroy() is used to mark the mutex uninitialized on debug builds, and any subsequent use of the mutex is forbidden. It also could lead to problems if mutex_destroy() gets extended, add missing mutex_destroy() to eliminate potential concerns. Reviewed-by: Christian Gmeiner Signed-off-by: Sui Jingfeng --- v2: Pick up tags and fix one more missing mutex_destroy() drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c | 2 ++ drivers/gpu/drm/etnaviv/etnaviv_drv.c| 1 + drivers/gpu/drm/etnaviv/etnaviv_gem.c| 1 + drivers/gpu/drm/etnaviv/etnaviv_gpu.c| 5 + drivers/gpu/drm/etnaviv/etnaviv_mmu.c| 2 +- 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c index 721d633aece9..66a407f1b3ee 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c @@ -55,6 +55,7 @@ etnaviv_cmdbuf_suballoc_new(struct device *dev) return suballoc; free_suballoc: + mutex_destroy(&suballoc->lock); kfree(suballoc); return ERR_PTR(ret); @@ -79,6 +80,7 @@ void etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc) { dma_free_wc(suballoc->dev, SUBALLOC_SIZE, suballoc->vaddr, suballoc->paddr); + mutex_destroy(&suballoc->lock); kfree(suballoc); } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 6500f3999c5f..7844cd961a29 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -564,6 +564,7 @@ static int etnaviv_bind(struct device *dev) out_destroy_suballoc: etnaviv_cmdbuf_suballoc_destroy(priv->cmdbuf_suballoc); out_free_priv: + mutex_destroy(&priv->gem_lock); kfree(priv); out_put: drm_dev_put(drm); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 4ce3d2ea7767..4247a10f8d4f 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -514,6 +514,7 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj) etnaviv_obj->ops->release(etnaviv_obj); drm_gem_object_release(obj); + mutex_destroy(&etnaviv_obj->lock); kfree(etnaviv_obj); } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 7c7f97793ddd..3c869970cba4 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -1929,8 +1929,13 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) static void etnaviv_gpu_platform_remove(struct platform_device *pdev) { + struct etnaviv_gpu *gpu = dev_get_drvdata(&pdev->dev); + component_del(&pdev->dev, &gpu_ops); pm_runtime_disable(&pdev->dev); + + mutex_destroy(&gpu->lock); + mutex_destroy(&gpu->sched_lock); } static int etnaviv_gpu_rpm_suspend(struct device *dev) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c index 1661d589bf3e..02d9408d41bc 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c @@ -358,7 +358,7 @@ static void etnaviv_iommu_context_free(struct kref *kref) container_of(kref, struct etnaviv_iommu_context, refcount); etnaviv_cmdbuf_suballoc_unmap(context, &context->cmdbuf_mapping); - + mutex_destroy(&context->lock); context->global->ops->free(context); } void etnaviv_iommu_context_put(struct etnaviv_iommu_context *context) -- 2.43.0
[PATCH v2 3/5] drm/etnaviv: Drop the header
Currently, the etnaviv_gem_submit.c isn't call any runtime power management functions. So drop this unused header, we can include it back when it really get used though. Reviewed-by: Christian Gmeiner Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index 3d0f8d182506..3c0a5c3e0e3d 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include -- 2.43.0
[PATCH v2 2/5] drm/etnaviv: Use 'unsigned' type to count the number of pages
The unpin_user_pages() function takes an 'unsigned long' argument to store the number of userspace pages, and the struct drm_gem_object::size is a size_t type. The number of pages can not be negative, hence, use 'unsigned' variable to count the number of pages. Reviewed-by: Christian Gmeiner Signed-off-by: Sui Jingfeng --- v2: Pick up tags and improve commit message drivers/gpu/drm/etnaviv/etnaviv_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 5ffc31f32ac9..4ce3d2ea7767 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -686,7 +686,7 @@ static void etnaviv_gem_userptr_release(struct etnaviv_gem_object *etnaviv_obj) kfree(etnaviv_obj->sgt); } if (etnaviv_obj->pages) { - int npages = etnaviv_obj->base.size >> PAGE_SHIFT; + unsigned int npages = etnaviv_obj->base.size >> PAGE_SHIFT; unpin_user_pages(etnaviv_obj->pages, npages); kvfree(etnaviv_obj->pages); -- 2.43.0
[PATCH v2 1/5] drm/etnaviv: Use unsigned type to count the number of pages
The drm_prime_pages_to_sg() function takes an 'unsigned int' argument to store the length of the page vector. The size of the object in number of CPU pages can not be negative, hence, use 'unsigned' variable to store the number of pages, instead of the 'signed' one. Reviewed-by: Christian Gmeiner Signed-off-by: Sui Jingfeng --- v2: Pick up tags and improve commit message drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c index 3524b5811682..6b98200068e4 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c @@ -17,7 +17,7 @@ static struct lock_class_key etnaviv_prime_lock_class; struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj) { struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); - int npages = obj->size >> PAGE_SHIFT; + unsigned int npages = obj->size >> PAGE_SHIFT; if (WARN_ON(!etnaviv_obj->pages)) /* should have already pinned! */ return ERR_PTR(-EINVAL); -- 2.43.0
[PATCH v15 17/19] drm/etnaviv: Support to manage dedicated VRAM base on drm_mm
The dedicated VRAM is seen as a kind of device memory (IOMEM) at present, we should use ioremap() to make device memory CPU accessible, we should also need to implement mmap() driver for userspace. Therefore, we add a simple drm-mm based range allocator for the upper (GEM object function) layer. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/Makefile | 1 + drivers/gpu/drm/etnaviv/etnaviv_drv.c | 12 + drivers/gpu/drm/etnaviv/etnaviv_drv.h | 11 + drivers/gpu/drm/etnaviv/etnaviv_gem.h | 5 + drivers/gpu/drm/etnaviv/etnaviv_gem_vram.c | 258 + drivers/gpu/drm/etnaviv/etnaviv_gem_vram.h | 12 + drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c | 13 ++ drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h | 6 + 8 files changed, 318 insertions(+) create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gem_vram.c create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gem_vram.h diff --git a/drivers/gpu/drm/etnaviv/Makefile b/drivers/gpu/drm/etnaviv/Makefile index 383f181bfc4c..aba2578966ff 100644 --- a/drivers/gpu/drm/etnaviv/Makefile +++ b/drivers/gpu/drm/etnaviv/Makefile @@ -6,6 +6,7 @@ etnaviv-y := \ etnaviv_drv.o \ etnaviv_dump.o \ etnaviv_gem_prime.o \ + etnaviv_gem_vram.o \ etnaviv_gem_submit.o \ etnaviv_gem.o \ etnaviv_gpu.o \ diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 7fc654f051a3..f10661fe079f 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -23,6 +23,7 @@ #include "etnaviv_drv.h" #include "etnaviv_gpu.h" #include "etnaviv_gem.h" +#include "etnaviv_gem_vram.h" #include "etnaviv_mmu.h" #include "etnaviv_pci_drv.h" #include "etnaviv_perfmon.h" @@ -46,6 +47,8 @@ static struct device_node *etnaviv_of_first_available_node(void) static int etnaviv_private_init(struct device *dev, struct etnaviv_drm_private *priv) { + int ret; + xa_init_flags(&priv->active_contexts, XA_FLAGS_ALLOC); mutex_init(&priv->gem_lock); @@ -61,6 +64,10 @@ static int etnaviv_private_init(struct device *dev, priv->cached_coherent = dev_is_dma_coherent(dev); + ret = etnaviv_init_dedicated_vram(dev, priv); + if (ret) + dev_err(dev, "Failed to init dedicated vram\n"); + return 0; } @@ -74,6 +81,11 @@ static void etnaviv_private_fini(struct etnaviv_drm_private *priv) xa_destroy(&priv->active_contexts); mutex_destroy(&priv->gem_lock); + + if (priv->dedicated_vram) { + drm_mm_takedown(&priv->vram.mm); + priv->dedicated_vram = false; + } } static void load_gpu(struct drm_device *dev) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index 84f2e79f0a53..b093f832599f 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -58,6 +58,17 @@ struct etnaviv_drm_private { */ bool cached_coherent; + /* +* dedicated VRAM (device memory) resource +*/ + struct { + struct drm_mm mm; + u64 gpu_base; + u64 cpu_base; + u64 size; + } vram; + bool dedicated_vram; + /* list of GEM objects: */ struct mutex gem_lock; struct list_head gem_list; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h index 8d8fc5b3a541..60c2dd19 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h @@ -59,6 +59,9 @@ struct etnaviv_gem_object { u32 last_cpu_prep_op; struct etnaviv_gem_userptr userptr; + + /* dedicated vram, which is physically contiguous */ + struct drm_mm_node *vram_np; }; static inline @@ -129,4 +132,6 @@ struct etnaviv_vram_mapping *etnaviv_gem_mapping_get( u64 va); void etnaviv_gem_mapping_unreference(struct etnaviv_vram_mapping *mapping); +u64 etnaviv_obj_gpu_phys_addr(struct etnaviv_gem_object *etnaviv_obj); + #endif /* __ETNAVIV_GEM_H__ */ diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_vram.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_vram.c new file mode 100644 index ..c2942317a64e --- /dev/null +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_vram.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#include "etnaviv_drv.h" +#include "etnaviv_gem.h" +#include "etnaviv_pci_drv.h" + +static struct lock_class_key etnaviv_vram_lock_class; + +static u64 etnaviv_obj_cpu_phys_addr(struct etnaviv_gem_object *etnaviv_obj) +{ + struct drm_gem_object *obj = &etnaviv_obj->base; + struct etnaviv_drm_private *priv = to_etnaviv_priv(obj->dev); + +
[PATCH v15 19/19] drm/etnaviv: Expose basic sanity tests via debugfs
To test the correctess of the implemented vmap() and vunmap() operations, to see if is works correctly on dedicated VRAM. Usage: cd /sys/kernel/debug/dri/etnaviv cat sanity My test is able to pass on x86-64 with a JM9230P card, see below log: Test Write to VRAM 8294400 bytes Write to VRAM Passed: 8294400 bytes Test Write to SHMEM 8294400 bytes Write to SHMEM Passed: 8294400 bytes Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/Makefile | 1 + drivers/gpu/drm/etnaviv/etnaviv_debugfs.c | 118 + drivers/gpu/drm/etnaviv/etnaviv_debugfs.h | 15 +++ drivers/gpu/drm/etnaviv/etnaviv_drv.c | 12 +++ drivers/gpu/drm/etnaviv/etnaviv_gem.c | 5 + drivers/gpu/drm/etnaviv/etnaviv_gem.h | 2 + drivers/gpu/drm/etnaviv/etnaviv_gem_vram.c | 6 ++ drivers/gpu/drm/etnaviv/etnaviv_gem_vram.h | 2 + 8 files changed, 161 insertions(+) create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_debugfs.c create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_debugfs.h diff --git a/drivers/gpu/drm/etnaviv/Makefile b/drivers/gpu/drm/etnaviv/Makefile index aba2578966ff..f278e75ee7cd 100644 --- a/drivers/gpu/drm/etnaviv/Makefile +++ b/drivers/gpu/drm/etnaviv/Makefile @@ -17,6 +17,7 @@ etnaviv-y := \ etnaviv_perfmon.o \ etnaviv_sched.o +etnaviv-$(CONFIG_DEBUG_FS) += etnaviv_debugfs.o etnaviv-$(CONFIG_DRM_ETNAVIV_PCI_DRIVER) += etnaviv_pci_drv.o \ pcie_ip_setup.o diff --git a/drivers/gpu/drm/etnaviv/etnaviv_debugfs.c b/drivers/gpu/drm/etnaviv/etnaviv_debugfs.c new file mode 100644 index ..0cfedbc6574c --- /dev/null +++ b/drivers/gpu/drm/etnaviv/etnaviv_debugfs.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#include "etnaviv_debugfs.h" +#include "etnaviv_drv.h" +#include "etnaviv_gem.h" +#include "etnaviv_gem_vram.h" + +static void bo_test_write_to_vram_by_cpu(void *addr, unsigned int num) +{ + u32 val = 0; + + while (num--) { + writel(val, addr); + ++val; + addr += 4; + } +} + +static unsigned int bo_test_read_from_vram_by_cpu(void *addr, unsigned int num) +{ + unsigned int i = 0; + + while (i < num) { + u32 val = readl(addr); + + if (val != i) + return i; + + addr += 4; + ++i; + } + + return 0; +} + +void etnaviv_sanity_test_vram_impl(struct drm_device *drm, struct drm_printer *p) +{ + struct etnaviv_gem_object *etnaviv_obj; + unsigned int size = 1920 * 1080 * 4; + void *addr; + int ret; + + size = ALIGN(size, PAGE_SIZE); + + drm_printf(p, "Test Write to VRAM %u bytes\n", size); + + ret = etnaviv_gem_new_private(drm, size, ETNA_BO_UNCACHED, false, + etnaviv_gem_get_vram_ops(), + &etnaviv_obj); + if (ret) { + drm_printf(p, "create dst bo failed\n"); + return; + } + + addr = etnaviv_gem_vmap(&etnaviv_obj->base); + if (!addr) { + drm_printf(p, "write to vram by cpu failed: vmap\n"); + goto out; + } + + etnaviv_gem_vunmap(&etnaviv_obj->base); + + addr = etnaviv_gem_vmap(&etnaviv_obj->base); + + bo_test_write_to_vram_by_cpu(addr, size / 4); + + ret = bo_test_read_from_vram_by_cpu(addr, size / 4); + + drm_printf(p, "Write to VRAM %s: %u bytes\n", + ret ? "not pass" : "Passed", ret ? ret : size); + + etnaviv_gem_vunmap(&etnaviv_obj->base); +out: + drm_gem_object_put(&etnaviv_obj->base); +} + +void etnaviv_sanity_test_shmem_impl(struct drm_device *drm, struct drm_printer *p) +{ + struct etnaviv_gem_object *etnaviv_obj; + unsigned int size = 1920 * 1080 * 4; + void *addr; + int ret; + + size = ALIGN(size, PAGE_SIZE); + + drm_printf(p, "Test Write to SHMEM %u bytes\n", size); + + ret = etnaviv_gem_new_private(drm, size, ETNA_BO_CACHED, true, + etnaviv_gem_get_shmem_ops(), + &etnaviv_obj); + if (ret) { + drm_printf(p, "create dst bo failed\n"); + return; + } + + addr = etnaviv_gem_vmap(&etnaviv_obj->base); + if (!addr) { + drm_printf(p, "write to shmem by cpu failed: vmap\n"); + goto out; + } + + etnaviv_gem_vunmap(&etnaviv_obj->base); + + addr = etnaviv_gem_vmap(&etnaviv_obj->base); + + bo_test_write_to_vram_by_cpu(addr, size / 4); + + ret = bo_test_read_from_vram_by_cpu(addr, size / 4); + + drm_printf(p, "Write to SHMEM %s: %u bytes\n&qu
[PATCH v15 18/19] drm/etnaviv: Allow userspace specify the domain of etnaviv GEM buffer object
Otherwise we don't know where a etnaviv GEM buffer object should put when we create it at userspace. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_drv.c | 9 + include/uapi/drm/etnaviv_drm.h| 12 2 files changed, 21 insertions(+) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index f10661fe079f..cdc62f64b200 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -331,11 +331,20 @@ static int etnaviv_ioctl_gem_new(struct drm_device *dev, void *data, struct drm_file *file) { struct drm_etnaviv_gem_new *args = data; + u32 domain; + + domain = args->flags & ETNA_BO_DOMAIN_MASK; + + args->flags &= ~ETNA_BO_DOMAIN_MASK; if (args->flags & ~(ETNA_BO_CACHED | ETNA_BO_WC | ETNA_BO_UNCACHED | ETNA_BO_FORCE_MMU)) return -EINVAL; + if (domain == ETNA_BO_PL_VRAM) + return etnaviv_gem_new_vram(dev, file, args->size, + args->flags, &args->handle); + return etnaviv_gem_new_handle(dev, file, args->size, args->flags, &args->handle); } diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h index 61eaa8cd0f5e..00e778c9d312 100644 --- a/include/uapi/drm/etnaviv_drm.h +++ b/include/uapi/drm/etnaviv_drm.h @@ -99,6 +99,18 @@ struct drm_etnaviv_param { /* map flags */ #define ETNA_BO_FORCE_MMU0x0010 +/* domain (placement) flags */ +#define ETNA_BO_DOMAIN_MASK 0x00f0 + +/* CPU accessible, GPU accessible pages in dedicated VRAM */ +#define ETNA_BO_PL_VRAM 0x0100 +/* CPU accessible, GPU accessible pages in SHMEM */ +#define ETNA_BO_PL_GTT 0x0200 +/* Userspace allocated memory, at least CPU accessible */ +#define ETNA_BO_PL_USERPTR 0x0800 +/* GPU accessible but CPU not accessible private VRAM pages */ +#define ETNA_BO_PL_PRIV 0x0400 + struct drm_etnaviv_gem_new { __u64 size; /* in */ __u32 flags; /* in, mask of ETNA_BO_x */ -- 2.43.0
[PATCH v15 16/19] drm/etnaviv: Call etnaviv_gem_obj_add() in ernaviv_gem_new_private()
The etnaviv_gem_obj_add() a common operation, the 'etnaviv_drm_private:: gem_list' is being used to record(track) all of the etnaviv GEM buffer object created in this driver. Once a etnaviv GEM buffer object has been allocated successfully, we should add it into the global etnaviv_drm_private::gem_list'. Because we need to free it and remove it free the list if the invoke of the subsequent functions fail. The only way that destroy etnaviv GEM buffer object is the implementation of etnaviv_gem_free_object() function. The etnaviv_gem_free_object() first remove the etnaviv GEM object from the list, then destroy its mapping and finally free its memory footprint. Therefore, in order to corresponding this, we add the freshly created etnaviv GEM buffer object immediately after it was successfully created. A benifit is that we only need to call etnaviv_gem_obj_add() once now, since the ernaviv_gem_new_private() has been unified. Make the etnaviv_gem_obj_add() static is a next nature thing. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem.c | 8 +++- drivers/gpu/drm/etnaviv/etnaviv_gem.h | 1 - drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 2 -- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 27e4a93c981c..ee799c02d0aa 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -584,7 +584,7 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj) kfree(etnaviv_obj); } -void etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj) +static void etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj) { struct etnaviv_drm_private *priv = to_etnaviv_priv(dev); struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); @@ -719,8 +719,6 @@ int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file, */ mapping_set_gfp_mask(obj->filp->f_mapping, priv->shm_gfp_mask); - etnaviv_gem_obj_add(dev, obj); - ret = drm_gem_handle_create(file, obj, handle); /* drop reference from allocate - handle holds it now */ @@ -751,6 +749,8 @@ int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags, drm_gem_private_object_init(dev, obj, size); } + etnaviv_gem_obj_add(dev, obj); + *res = to_etnaviv_bo(obj); return 0; @@ -849,8 +849,6 @@ int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file, etnaviv_obj->userptr.mm = current->mm; etnaviv_obj->userptr.ro = !(flags & ETNA_USERPTR_WRITE); - etnaviv_gem_obj_add(dev, &etnaviv_obj->base); - ret = drm_gem_handle_create(file, &etnaviv_obj->base, handle); /* drop reference from allocate - handle holds it now */ diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h index b174a9e4cc48..8d8fc5b3a541 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h @@ -121,7 +121,6 @@ int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags, bool shmem, const struct etnaviv_gem_ops *ops, struct etnaviv_gem_object **res); -void etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj); struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *obj); void etnaviv_gem_put_pages(struct etnaviv_gem_object *obj); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c index 64a858a0b0cf..04ee31461b8c 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c @@ -127,8 +127,6 @@ struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev, if (ret) goto fail; - etnaviv_gem_obj_add(dev, &etnaviv_obj->base); - return &etnaviv_obj->base; fail: -- 2.43.0
[PATCH v15 14/19] drm/etnaviv: Add PCIe IP setup code
Because some PCIe IP need special setup before its VRAM bar can be usable, do this with instance specific object function. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/Makefile | 3 +- drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c | 19 drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h | 9 ++ drivers/gpu/drm/etnaviv/pcie_ip_setup.c | 109 ++ 4 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/etnaviv/pcie_ip_setup.c diff --git a/drivers/gpu/drm/etnaviv/Makefile b/drivers/gpu/drm/etnaviv/Makefile index 6829e1ebf2db..383f181bfc4c 100644 --- a/drivers/gpu/drm/etnaviv/Makefile +++ b/drivers/gpu/drm/etnaviv/Makefile @@ -16,6 +16,7 @@ etnaviv-y := \ etnaviv_perfmon.o \ etnaviv_sched.o -etnaviv-$(CONFIG_DRM_ETNAVIV_PCI_DRIVER) += etnaviv_pci_drv.o +etnaviv-$(CONFIG_DRM_ETNAVIV_PCI_DRIVER) += etnaviv_pci_drv.o \ + pcie_ip_setup.o obj-$(CONFIG_DRM_ETNAVIV) += etnaviv.o diff --git a/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c index f13f3208120f..9911bfdc41a9 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c @@ -5,6 +5,11 @@ #include "etnaviv_drv.h" #include "etnaviv_pci_drv.h" +static const struct etnaviv_pcie_ip_funcs jemo_9_gpu_pcie_ip_funcs = { + .init = jemo_pcie_init, + .fini = NULL, +}; + static const struct etnaviv_pci_gpu_data gccore_platform_data[GCCORE_PCI_CHIP_ID_LAST] = { { @@ -18,7 +23,9 @@ gccore_platform_data[GCCORE_PCI_CHIP_ID_LAST] = { .mmio_bar = 1, .ip_block = {{0, 0x0090, 0x4000, "etnaviv-gpu,3d"},}, .has_dedicated_vram = true, + .has_iatu = true, .has_display = true, + .pcie_ip_funcs = &jemo_9_gpu_pcie_ip_funcs, .market_name = "JingJia Micro JM9100", }, { @@ -30,7 +37,9 @@ gccore_platform_data[GCCORE_PCI_CHIP_ID_LAST] = { .ip_block = {{0, 0x0090, 0x4000, "etnaviv-gpu,3d"}, {1, 0x0091, 0x4000, "etnaviv-gpu,3d"},}, .has_dedicated_vram = true, + .has_iatu = true, .has_display = true, + .pcie_ip_funcs = &jemo_9_gpu_pcie_ip_funcs, .market_name = "JingJia Micro JD9230P", }, { @@ -42,6 +51,7 @@ gccore_platform_data[GCCORE_PCI_CHIP_ID_LAST] = { .ip_block = {{0, 0x0004, 0x4000, "etnaviv-gpu,3d"}, {0, 0x000C, 0x4000, "etnaviv-gpu,2d"},}, .has_dedicated_vram = true, + .has_iatu = false, .has_display = true, .market_name = "LingJiu GP102", }, @@ -53,6 +63,7 @@ gccore_platform_data[GCCORE_PCI_CHIP_ID_LAST] = { .mmio_bar = 0, .ip_block = {{0, 0, 0x4000, "etnaviv-gpu,3d"},}, .has_dedicated_vram = true, + .has_iatu = false, .has_display = false, .market_name = "GC1000 in LS7A1000", }, @@ -83,6 +94,7 @@ static int etnaviv_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { const struct etnaviv_pci_gpu_data *pdata; + const struct etnaviv_pcie_ip_funcs *pcie_ip_funcs; struct device *dev = &pdev->dev; unsigned int i; unsigned int num_core; @@ -102,6 +114,13 @@ static int etnaviv_pci_probe(struct pci_dev *pdev, if (!pdata) return -ENODEV; + pcie_ip_funcs = pdata->pcie_ip_funcs; + if (pcie_ip_funcs) { + ret = pcie_ip_funcs->init(pdev); + if (ret) + return ret; + } + num_core = pdata->num_core; dev_info(dev, "%s has %u GPU cores\n", pdata->market_name, num_core); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h index eae8cdea5674..39eb2851355a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h @@ -23,6 +23,11 @@ struct vivante_gc_ip_block { char compatible[20]; }; +struct etnaviv_pcie_ip_funcs { + int (*init)(struct pci_dev *pdev); + void (*fini)(struct pci_dev *pdev); +}; + struct etnaviv_pci_gpu_data { enum etnaviv_pci_chip_id chip_id; u32 num_core; @@ -31,13 +36,17 @@ struct etnaviv_pci_gpu_data { u32 mmio_bar; struct vivante_gc_ip_block ip_block[ETNA_MAX_IP_BLOCK]; bool has_dedicated_vram; + bool has_iatu; bool has_display; + const struct etnaviv_pcie_ip_funcs *pcie_ip_funcs; char ma
[PATCH v15 15/19] drm/etnaviv: Make more use of the etnaviv_gem_new_private() function
Add the 'bool shmem' as the 4th argument of etnaviv_gem_new_private(), then call etnaviv_gem_new_handle() to allocate the etnaviv_gem_object instance for us. A small benefit is to reduce code duplication across different etnaviv GEM buffer objects. This allow us to reuse etnaviv_gem_new_private() everywhere, increasing code reusage. We also should call drm_gem_private_object_fini() to uninitialize an already allocated GEM object when it initialized failed. Now etnaviv_gem_new_private() handle this trouble for us, the upper caller can just use it, no need to worry about error handling anymore. if true, the drm_gem_object_init() will allocate backing storage for us, then this is a shmem buffer object. if false, we have to implement driver specific backing storage. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem.c | 28 + drivers/gpu/drm/etnaviv/etnaviv_gem.h | 4 ++- drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 2 +- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 3732288ff530..27e4a93c981c 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -697,21 +697,20 @@ int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file, u32 size, u32 flags, u32 *handle) { struct etnaviv_drm_private *priv = to_etnaviv_priv(dev); - struct drm_gem_object *obj = NULL; + struct etnaviv_gem_object *etnaviv_obj; + struct drm_gem_object *obj; int ret; size = PAGE_ALIGN(size); - ret = etnaviv_gem_new_impl(dev, flags, &etnaviv_gem_shmem_ops, &obj); + ret = etnaviv_gem_new_private(dev, size, flags, true, + &etnaviv_gem_shmem_ops, &etnaviv_obj); if (ret) goto fail; - lockdep_set_class(&to_etnaviv_bo(obj)->lock, &etnaviv_shm_lock_class); - - ret = drm_gem_object_init(dev, obj, size); - if (ret) - goto fail; + lockdep_set_class(&etnaviv_obj->lock, &etnaviv_shm_lock_class); + obj = &etnaviv_obj->base; /* * Our buffers are kept pinned, so allocating them from the MOVABLE * zone is a really bad idea, and conflicts with CMA. See comments @@ -732,7 +731,8 @@ int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file, } int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags, - const struct etnaviv_gem_ops *ops, struct etnaviv_gem_object **res) + bool shmem, const struct etnaviv_gem_ops *ops, + struct etnaviv_gem_object **res) { struct drm_gem_object *obj; int ret; @@ -741,7 +741,15 @@ int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags, if (ret) return ret; - drm_gem_private_object_init(dev, obj, size); + if (shmem) { + ret = drm_gem_object_init(dev, obj, size); + if (ret) { + drm_gem_private_object_fini(obj); + return ret; + } + } else { + drm_gem_private_object_init(dev, obj, size); + } *res = to_etnaviv_bo(obj); @@ -830,7 +838,7 @@ int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file, struct etnaviv_gem_object *etnaviv_obj; int ret; - ret = etnaviv_gem_new_private(dev, size, ETNA_BO_CACHED, + ret = etnaviv_gem_new_private(dev, size, ETNA_BO_CACHED, false, &etnaviv_gem_userptr_ops, &etnaviv_obj); if (ret) return ret; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h index f2ac64d8e90b..b174a9e4cc48 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h @@ -118,7 +118,9 @@ void etnaviv_submit_put(struct etnaviv_gem_submit * submit); int etnaviv_gem_wait_bo(struct etnaviv_gpu *gpu, struct drm_gem_object *obj, struct drm_etnaviv_timespec *timeout); int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags, - const struct etnaviv_gem_ops *ops, struct etnaviv_gem_object **res); + bool shmem, const struct etnaviv_gem_ops *ops, + struct etnaviv_gem_object **res); + void etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj); struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *obj); void etnaviv_gem_put_pages(struct etnaviv_gem_object *obj); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c index 0062d808d6a9..64a858a0b0cf 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +++ b/drivers/gpu/drm/etnavi
[PATCH v15 13/19] drm/etnaviv: Add support for vivante GPU cores attached via PCIe device
Previously, component framework is being used to bind multiple platform (DT) devices to a virtual master. The virtual master is manually created during probe time. This is fine and works well for embedded SoCs, yet there have some hardware venders that integrate Vivante GPU IP cores into their PCIe card. This driver lacks support for PCI(e) devices. Creating platform device as logical denotation for each GPU IP core, the manually created platform devices are functional as subcomponen. To reflects the actual hardware layout, make all of them be child of the PCIe master device. The master is real for PCIe devices, which is working at background and spread the device seeds out. Creating yet an another platform device as component master, offload the component binding/unbinding affairs to it. The virtual component master works at foreground, the PCIe device behind it works at background. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/Kconfig | 9 ++ drivers/gpu/drm/etnaviv/Makefile | 2 + drivers/gpu/drm/etnaviv/etnaviv_drv.c | 45 +- drivers/gpu/drm/etnaviv/etnaviv_drv.h | 11 ++ drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 73 +++-- drivers/gpu/drm/etnaviv/etnaviv_gpu.h | 4 + drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c | 185 ++ drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h | 48 ++ 8 files changed, 355 insertions(+), 22 deletions(-) create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h diff --git a/drivers/gpu/drm/etnaviv/Kconfig b/drivers/gpu/drm/etnaviv/Kconfig index faa7fc68b009..7cb44f72d512 100644 --- a/drivers/gpu/drm/etnaviv/Kconfig +++ b/drivers/gpu/drm/etnaviv/Kconfig @@ -15,6 +15,15 @@ config DRM_ETNAVIV help DRM driver for Vivante GPUs. +config DRM_ETNAVIV_PCI_DRIVER + bool "enable ETNAVIV PCI driver support" + depends on DRM_ETNAVIV + depends on PCI + default n + help + Compile in support for Vivante GPUs attached via PCIe card. + Say Y if you have such hardwares. + config DRM_ETNAVIV_THERMAL bool "enable ETNAVIV thermal throttling" depends on DRM_ETNAVIV diff --git a/drivers/gpu/drm/etnaviv/Makefile b/drivers/gpu/drm/etnaviv/Makefile index 46e5ffad69a6..6829e1ebf2db 100644 --- a/drivers/gpu/drm/etnaviv/Makefile +++ b/drivers/gpu/drm/etnaviv/Makefile @@ -16,4 +16,6 @@ etnaviv-y := \ etnaviv_perfmon.o \ etnaviv_sched.o +etnaviv-$(CONFIG_DRM_ETNAVIV_PCI_DRIVER) += etnaviv_pci_drv.o + obj-$(CONFIG_DRM_ETNAVIV) += etnaviv.o diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 18686b573d77..7fc654f051a3 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -24,6 +24,7 @@ #include "etnaviv_gpu.h" #include "etnaviv_gem.h" #include "etnaviv_mmu.h" +#include "etnaviv_pci_drv.h" #include "etnaviv_perfmon.h" /* @@ -568,6 +569,10 @@ static int etnaviv_bind(struct device *dev) if (ret < 0) goto out_free_priv; + ret = etnaviv_register_irq_handler(dev, priv); + if (ret) + goto out_unbind; + load_gpu(drm); ret = drm_dev_register(drm, 0); @@ -670,16 +675,32 @@ static struct platform_driver etnaviv_platform_driver = { }, }; -static int etnaviv_create_platform_device(const char *name, - struct platform_device **ppdev) +int etnaviv_create_platform_device(struct device *parent, + const char *name, int id, + struct resource *pres, + void *pdata, unsigned int ndata, + struct platform_device **ppdev) { struct platform_device *pdev; int ret; - pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE); + pdev = platform_device_alloc(name, id); if (!pdev) return -ENOMEM; + pdev->dev.parent = parent; + + if (pres) { + ret = platform_device_add_resources(pdev, pres, 1); + if (ret) { + platform_device_put(pdev); + return ret; + } + } + + if (pdata && ndata) + platform_device_add_data(pdev, pdata, ndata); + ret = platform_device_add(pdev); if (ret) { platform_device_put(pdev); @@ -691,7 +712,7 @@ static int etnaviv_create_platform_device(const char *name, return 0; } -static void etnaviv_destroy_platform_device(struct platform_device **ppdev) +void etnaviv_destroy_platform_device(struct platform_device **ppdev) { struct platform_device *pdev = *ppdev; @@ -703,7 +724,7 @@ static void etnaviv_destroy_platfor
[PATCH v15 12/19] drm/etnaviv: Add support for cached coherent caching mode
Many modern CPUs and/or platforms choose to define their peripheral devices as cached coherent by default, to be specific, the PCH is capable of snooping CPU's cache. When hit, the peripheral devices will access data directly from CPU's cache. This means that device drivers do not need to maintain the coherency issue between a processor and peripheral I/O for the cached buffers. Hence, it dosen't need us to sync manually on the software side, which is useful to avoid some overheads, especially for userspace, but userspace is not known this yet. Probe the hardware maintained cached coherent support with the dev_is_dma_coherent() function, and store the result in the etnaviv_drm_private structure. Since this is a host and/or platform implementation-defined hardware feature, and is again meant to be shared by all GPU cores. The probe function can be extended in the future if it not reflect the hardware perfectly. Expose it via etnaviv parameter mechanism to let userspace know. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_drv.c | 3 +++ drivers/gpu/drm/etnaviv/etnaviv_drv.h | 9 + drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 4 include/uapi/drm/etnaviv_drm.h| 1 + 4 files changed, 17 insertions(+) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 32ec1b5452ba..18686b573d77 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -57,6 +58,8 @@ static int etnaviv_private_init(struct device *dev, return -ENOMEM; } + priv->cached_coherent = dev_is_dma_coherent(dev); + return 0; } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index e2a991160cb3..02d706b34752 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -46,6 +46,15 @@ struct etnaviv_drm_private { struct xarray active_contexts; u32 next_context_id; + /* +* If true, the cached mapping is consistent for all CPU cores and +* peripheral bus masters in the system. It means that both of the +* CPU and GPU will see the same data if the buffer being accessed +* is cached. And the coherency is guaranteed by the host platform +* specific hardwares. +*/ + bool cached_coherent; + /* list of GEM objects: */ struct mutex gem_lock; struct list_head gem_list; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 0c8e394b569c..89fb36aee779 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -164,6 +164,10 @@ int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value) *value = gpu->identity.eco_id; break; + case ETNAVIV_PARAM_CACHED_COHERENT: + *value = priv->cached_coherent; + break; + default: DBG("%s: invalid param: %u", dev_name(gpu->dev), param); return -EINVAL; diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h index af024d90453d..61eaa8cd0f5e 100644 --- a/include/uapi/drm/etnaviv_drm.h +++ b/include/uapi/drm/etnaviv_drm.h @@ -77,6 +77,7 @@ struct drm_etnaviv_timespec { #define ETNAVIV_PARAM_GPU_PRODUCT_ID0x1c #define ETNAVIV_PARAM_GPU_CUSTOMER_ID 0x1d #define ETNAVIV_PARAM_GPU_ECO_ID0x1e +#define ETNAVIV_PARAM_CACHED_COHERENT 0x1f #define ETNA_MAX_PIPES 4 -- 2.43.0
[PATCH v15 11/19] drm/etnaviv: Add etnaviv_gem_obj_remove() helper
Which is corresonding to the etnaviv_gem_obj_add() Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem.c | 17 + 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 39cfece67b90..3732288ff530 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -19,6 +19,8 @@ static struct lock_class_key etnaviv_shm_lock_class; static struct lock_class_key etnaviv_userptr_lock_class; +static void etnaviv_gem_obj_remove(struct drm_gem_object *obj); + static void etnaviv_gem_scatter_map(struct etnaviv_gem_object *etnaviv_obj) { struct drm_device *dev = etnaviv_obj->base.dev; @@ -555,15 +557,12 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj) { struct drm_device *drm = obj->dev; struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); - struct etnaviv_drm_private *priv = obj->dev->dev_private; struct etnaviv_vram_mapping *mapping, *tmp; /* object should not be active */ drm_WARN_ON(drm, is_active(etnaviv_obj)); - mutex_lock(&priv->gem_lock); - list_del(&etnaviv_obj->gem_node); - mutex_unlock(&priv->gem_lock); + etnaviv_gem_obj_remove(obj); list_for_each_entry_safe(mapping, tmp, &etnaviv_obj->vram_list, obj_node) { @@ -595,6 +594,16 @@ void etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj) mutex_unlock(&priv->gem_lock); } +static void etnaviv_gem_obj_remove(struct drm_gem_object *obj) +{ + struct etnaviv_drm_private *priv = to_etnaviv_priv(obj->dev); + struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); + + mutex_lock(&priv->gem_lock); + list_del(&etnaviv_obj->gem_node); + mutex_unlock(&priv->gem_lock); +} + static const struct vm_operations_struct vm_ops = { .fault = etnaviv_gem_fault, .open = drm_gem_vm_open, -- 2.43.0
[PATCH v15 10/19] drm/etnaviv: Embed struct drm_device into struct etnaviv_drm_private
Both the instance of struct drm_device and the instance of struct etnaviv_drm_private are intended to be shared by all GPU cores, both have only one instance created across drm/etnaviv driver. After embedded in, the whole structure can be allocated with devm_drm_dev_alloc(). And the DRM device created is automatically put on driver detach, so we don't need to call drm_dev_put() explicitly on driver leave. It's also eliminate the need to use the .dev_private member, which is deprecated according to the drm document. We can also use container_of() to retrieve pointer for the containing structure. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_drv.c| 65 drivers/gpu/drm/etnaviv/etnaviv_drv.h| 7 +++ drivers/gpu/drm/etnaviv/etnaviv_gem.c| 4 +- drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c | 2 +- drivers/gpu/drm/etnaviv/etnaviv_gpu.c| 6 +- drivers/gpu/drm/etnaviv/etnaviv_mmu.c| 4 +- 6 files changed, 39 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 809e5db85df4..32ec1b5452ba 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -41,14 +41,9 @@ static struct device_node *etnaviv_of_first_available_node(void) return NULL; } -static struct etnaviv_drm_private *etnaviv_alloc_private(struct device *dev) +static int etnaviv_private_init(struct device *dev, + struct etnaviv_drm_private *priv) { - struct etnaviv_drm_private *priv; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return ERR_PTR(-ENOMEM); - xa_init_flags(&priv->active_contexts, XA_FLAGS_ALLOC); mutex_init(&priv->gem_lock); @@ -58,15 +53,14 @@ static struct etnaviv_drm_private *etnaviv_alloc_private(struct device *dev) priv->cmdbuf_suballoc = etnaviv_cmdbuf_suballoc_new(dev); if (IS_ERR(priv->cmdbuf_suballoc)) { - kfree(priv); dev_err(dev, "Failed to create cmdbuf suballocator\n"); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } - return priv; + return 0; } -static void etnaviv_free_private(struct etnaviv_drm_private *priv) +static void etnaviv_private_fini(struct etnaviv_drm_private *priv) { if (!priv) return; @@ -76,13 +70,11 @@ static void etnaviv_free_private(struct etnaviv_drm_private *priv) xa_destroy(&priv->active_contexts); mutex_destroy(&priv->gem_lock); - - kfree(priv); } static void load_gpu(struct drm_device *dev) { - struct etnaviv_drm_private *priv = dev->dev_private; + struct etnaviv_drm_private *priv = to_etnaviv_priv(dev); unsigned int i; for (i = 0; i < ETNA_MAX_PIPES; i++) { @@ -100,7 +92,7 @@ static void load_gpu(struct drm_device *dev) static int etnaviv_open(struct drm_device *dev, struct drm_file *file) { - struct etnaviv_drm_private *priv = dev->dev_private; + struct etnaviv_drm_private *priv = to_etnaviv_priv(dev); struct etnaviv_file_private *ctx; int ret, i; @@ -143,7 +135,7 @@ static int etnaviv_open(struct drm_device *dev, struct drm_file *file) static void etnaviv_postclose(struct drm_device *dev, struct drm_file *file) { - struct etnaviv_drm_private *priv = dev->dev_private; + struct etnaviv_drm_private *priv = to_etnaviv_priv(dev); struct etnaviv_file_private *ctx = file->driver_priv; unsigned int i; @@ -168,7 +160,7 @@ static void etnaviv_postclose(struct drm_device *dev, struct drm_file *file) #ifdef CONFIG_DEBUG_FS static int etnaviv_gem_show(struct drm_device *dev, struct seq_file *m) { - struct etnaviv_drm_private *priv = dev->dev_private; + struct etnaviv_drm_private *priv = to_etnaviv_priv(dev); etnaviv_gem_describe_objects(priv, m); @@ -262,7 +254,7 @@ static int show_each_gpu(struct seq_file *m, void *arg) { struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; - struct etnaviv_drm_private *priv = dev->dev_private; + struct etnaviv_drm_private *priv = to_etnaviv_priv(dev); struct etnaviv_gpu *gpu; int (*show)(struct etnaviv_gpu *gpu, struct seq_file *m) = node->info_ent->data; @@ -305,7 +297,7 @@ static void etnaviv_debugfs_init(struct drm_minor *minor) static int etnaviv_ioctl_get_param(struct drm_device *dev, void *data, struct drm_file *file) { - struct etnaviv_drm_private *priv = dev->dev_private; + struct etnaviv_drm_private *priv = to_etnaviv_priv(dev); struct drm_etnaviv_param *args = data; struct etnaviv_gpu *gpu; @@ -398,7 +390,7 @@ static int etnavi
[PATCH v15 09/19] drm/etnaviv: Add constructor and destructor for the etnaviv_drm_private structure
Because there are a lot of data members in the struct etnaviv_drm_private, which are intended to be shared by all GPU cores. It can be lengthy and daunting on error handling, the 'gem_lock' of struct etnaviv_drm_private just be forgeten to destroy on driver leave. Switch to use the dedicated helpers introduced, etnaviv_bind() and etnaviv_unbind() gets simplified. Another potential benefit is that we could put the struct drm_device into struct etnaviv_drm_private in the future, which made them share the same life time. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_drv.c | 73 +-- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 6591e420a051..809e5db85df4 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -41,6 +41,45 @@ static struct device_node *etnaviv_of_first_available_node(void) return NULL; } +static struct etnaviv_drm_private *etnaviv_alloc_private(struct device *dev) +{ + struct etnaviv_drm_private *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return ERR_PTR(-ENOMEM); + + xa_init_flags(&priv->active_contexts, XA_FLAGS_ALLOC); + + mutex_init(&priv->gem_lock); + INIT_LIST_HEAD(&priv->gem_list); + priv->num_gpus = 0; + priv->shm_gfp_mask = GFP_HIGHUSER | __GFP_RETRY_MAYFAIL | __GFP_NOWARN; + + priv->cmdbuf_suballoc = etnaviv_cmdbuf_suballoc_new(dev); + if (IS_ERR(priv->cmdbuf_suballoc)) { + kfree(priv); + dev_err(dev, "Failed to create cmdbuf suballocator\n"); + return ERR_PTR(-ENOMEM); + } + + return priv; +} + +static void etnaviv_free_private(struct etnaviv_drm_private *priv) +{ + if (!priv) + return; + + etnaviv_cmdbuf_suballoc_destroy(priv->cmdbuf_suballoc); + + xa_destroy(&priv->active_contexts); + + mutex_destroy(&priv->gem_lock); + + kfree(priv); +} + static void load_gpu(struct drm_device *dev) { struct etnaviv_drm_private *priv = dev->dev_private; @@ -521,35 +560,21 @@ static int etnaviv_bind(struct device *dev) if (IS_ERR(drm)) return PTR_ERR(drm); - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(dev, "failed to allocate private data\n"); - ret = -ENOMEM; + priv = etnaviv_alloc_private(dev); + if (IS_ERR(priv)) { + ret = PTR_ERR(priv); goto out_put; } + drm->dev_private = priv; dma_set_max_seg_size(dev, SZ_2G); - xa_init_flags(&priv->active_contexts, XA_FLAGS_ALLOC); - - mutex_init(&priv->gem_lock); - INIT_LIST_HEAD(&priv->gem_list); - priv->num_gpus = 0; - priv->shm_gfp_mask = GFP_HIGHUSER | __GFP_RETRY_MAYFAIL | __GFP_NOWARN; - - priv->cmdbuf_suballoc = etnaviv_cmdbuf_suballoc_new(drm->dev); - if (IS_ERR(priv->cmdbuf_suballoc)) { - dev_err(drm->dev, "Failed to create cmdbuf suballocator\n"); - ret = PTR_ERR(priv->cmdbuf_suballoc); - goto out_free_priv; - } - dev_set_drvdata(dev, drm); ret = component_bind_all(dev, drm); if (ret < 0) - goto out_destroy_suballoc; + goto out_free_priv; load_gpu(drm); @@ -561,11 +586,8 @@ static int etnaviv_bind(struct device *dev) out_unbind: component_unbind_all(dev, drm); -out_destroy_suballoc: - etnaviv_cmdbuf_suballoc_destroy(priv->cmdbuf_suballoc); out_free_priv: - mutex_destroy(&priv->gem_lock); - kfree(priv); + etnaviv_free_private(priv); out_put: drm_dev_put(drm); @@ -581,12 +603,9 @@ static void etnaviv_unbind(struct device *dev) component_unbind_all(dev, drm); - etnaviv_cmdbuf_suballoc_destroy(priv->cmdbuf_suballoc); - - xa_destroy(&priv->active_contexts); + etnaviv_free_private(priv); drm->dev_private = NULL; - kfree(priv); drm_dev_put(drm); } -- 2.43.0
[PATCH v15 08/19] drm/etnaviv: Fix wrong caching mode being used for non writecombine buffers
In the etnaviv_gem_vmap_impl() function, the driver vmap whatever buffers with write combine(WC) page property. This is incorrect, as some platforms are cached coherent. Cached buffers should be mapped with cached page property. Fixes: a0a5ab3e99b8 ("drm/etnaviv: call correct function when trying to vmap a DMABUF") Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem.c | 16 ++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 1fd2cff20ef4..b899aea64e22 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -393,6 +393,7 @@ static void etnaviv_gem_object_vunmap(struct drm_gem_object *obj, static void *etnaviv_gem_vmap_impl(struct etnaviv_gem_object *obj) { struct page **pages; + pgprot_t prot; lockdep_assert_held(&obj->lock); @@ -400,8 +401,19 @@ static void *etnaviv_gem_vmap_impl(struct etnaviv_gem_object *obj) if (IS_ERR(pages)) return NULL; - return vmap(pages, obj->base.size >> PAGE_SHIFT, - VM_MAP, pgprot_writecombine(PAGE_KERNEL)); + switch (obj->flags) { + case ETNA_BO_CACHED: + prot = PAGE_KERNEL; + break; + case ETNA_BO_UNCACHED: + prot = pgprot_noncached(PAGE_KERNEL); + break; + case ETNA_BO_WC: + default: + prot = pgprot_writecombine(PAGE_KERNEL); + } + + return vmap(pages, obj->base.size >> PAGE_SHIFT, VM_MAP, prot); } static inline enum dma_data_direction etnaviv_op_to_dma_dir(u32 op) -- 2.43.0
[PATCH v15 07/19] drm/etnaviv: Add a dedicated helper function to get various clocks
Because the current implementation is DT-based, this only works when the host platform has the DT support. The problem is that some host platforms does not provide DT-based clocks drivers, as a result, the driver rage quit. PLL hardwares are typically provided by the host platform, which is part of the entire clock tree. The PLL hardware provide clock pulse to the GPU core, but it's not belong to the GPU corei itself. PLL registers can be manipulated directly by the device driver. Hence, it may need dedicated clock driver. Add a the etnaviv_gpu_clk_get() function to group similar code blocks, which make it easier to call this function on the platform where it works. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 53 --- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 4599dd995e11..eca6a06e9ade 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -1609,6 +1609,35 @@ static irqreturn_t irq_handler(int irq, void *data) return ret; } +static int etnaviv_gpu_clk_get(struct etnaviv_gpu *gpu) +{ + struct device *dev = gpu->dev; + + gpu->clk_reg = devm_clk_get_optional(dev, "reg"); + DBG("clk_reg: %p", gpu->clk_reg); + if (IS_ERR(gpu->clk_reg)) + return PTR_ERR(gpu->clk_reg); + + gpu->clk_bus = devm_clk_get_optional(dev, "bus"); + DBG("clk_bus: %p", gpu->clk_bus); + if (IS_ERR(gpu->clk_bus)) + return PTR_ERR(gpu->clk_bus); + + gpu->clk_core = devm_clk_get(dev, "core"); + DBG("clk_core: %p", gpu->clk_core); + if (IS_ERR(gpu->clk_core)) + return PTR_ERR(gpu->clk_core); + gpu->base_rate_core = clk_get_rate(gpu->clk_core); + + gpu->clk_shader = devm_clk_get_optional(dev, "shader"); + DBG("clk_shader: %p", gpu->clk_shader); + if (IS_ERR(gpu->clk_shader)) + return PTR_ERR(gpu->clk_shader); + gpu->base_rate_shader = clk_get_rate(gpu->clk_shader); + + return 0; +} + static int etnaviv_gpu_clk_enable(struct etnaviv_gpu *gpu) { int ret; @@ -1884,27 +1913,9 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) } /* Get Clocks: */ - gpu->clk_reg = devm_clk_get_optional(&pdev->dev, "reg"); - DBG("clk_reg: %p", gpu->clk_reg); - if (IS_ERR(gpu->clk_reg)) - return PTR_ERR(gpu->clk_reg); - - gpu->clk_bus = devm_clk_get_optional(&pdev->dev, "bus"); - DBG("clk_bus: %p", gpu->clk_bus); - if (IS_ERR(gpu->clk_bus)) - return PTR_ERR(gpu->clk_bus); - - gpu->clk_core = devm_clk_get(&pdev->dev, "core"); - DBG("clk_core: %p", gpu->clk_core); - if (IS_ERR(gpu->clk_core)) - return PTR_ERR(gpu->clk_core); - gpu->base_rate_core = clk_get_rate(gpu->clk_core); - - gpu->clk_shader = devm_clk_get_optional(&pdev->dev, "shader"); - DBG("clk_shader: %p", gpu->clk_shader); - if (IS_ERR(gpu->clk_shader)) - return PTR_ERR(gpu->clk_shader); - gpu->base_rate_shader = clk_get_rate(gpu->clk_shader); + err = etnaviv_gpu_clk_get(gpu); + if (err) + return err; /* TODO: figure out max mapped size */ dev_set_drvdata(dev, gpu); -- 2.43.0
[PATCH v15 06/19] drm/etnaviv: Prefer drm_device based drm_WARN_ON() over regular WARN_ON()
drm_WARN_ON() acts like WARN_ON(), but with the key difference of using device specific information so that we know from which device the warning is originating from. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem.c | 9 + drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 2 +- drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 6 +++--- drivers/gpu/drm/etnaviv/etnaviv_mmu.c | 7 +-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 55004fa9fabd..1fd2cff20ef4 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -256,7 +256,7 @@ void etnaviv_gem_mapping_unreference(struct etnaviv_vram_mapping *mapping) struct etnaviv_gem_object *etnaviv_obj = mapping->object; mutex_lock(&etnaviv_obj->lock); - WARN_ON(mapping->use == 0); + drm_WARN_ON(etnaviv_obj->base.dev, mapping->use == 0); mapping->use -= 1; mutex_unlock(&etnaviv_obj->lock); @@ -463,7 +463,7 @@ int etnaviv_gem_cpu_fini(struct drm_gem_object *obj) if (etnaviv_obj->flags & ETNA_BO_CACHED) { /* fini without a prep is almost certainly a userspace error */ - WARN_ON(etnaviv_obj->last_cpu_prep_op == 0); + drm_WARN_ON(dev, etnaviv_obj->last_cpu_prep_op == 0); dma_sync_sgtable_for_device(dev->dev, etnaviv_obj->sgt, etnaviv_op_to_dma_dir(etnaviv_obj->last_cpu_prep_op)); etnaviv_obj->last_cpu_prep_op = 0; @@ -541,12 +541,13 @@ static const struct etnaviv_gem_ops etnaviv_gem_shmem_ops = { void etnaviv_gem_free_object(struct drm_gem_object *obj) { + struct drm_device *drm = obj->dev; struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); struct etnaviv_drm_private *priv = obj->dev->dev_private; struct etnaviv_vram_mapping *mapping, *tmp; /* object should not be active */ - WARN_ON(is_active(etnaviv_obj)); + drm_WARN_ON(drm, is_active(etnaviv_obj)); mutex_lock(&priv->gem_lock); list_del(&etnaviv_obj->gem_node); @@ -556,7 +557,7 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj) obj_node) { struct etnaviv_iommu_context *context = mapping->context; - WARN_ON(mapping->use); + drm_WARN_ON(drm, mapping->use); if (context) etnaviv_iommu_unmap_gem(context, mapping); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c index 8f523cbee60a..0062d808d6a9 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c @@ -19,7 +19,7 @@ struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj) struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); unsigned int npages = obj->size >> PAGE_SHIFT; - if (WARN_ON(!etnaviv_obj->pages)) /* should have already pinned! */ + if (drm_WARN_ON(obj->dev, !etnaviv_obj->pages)) /* should have already pinned! */ return ERR_PTR(-EINVAL); return drm_prime_pages_to_sg(obj->dev, etnaviv_obj->pages, npages); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index d0df5c53a829..4599dd995e11 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -686,7 +686,7 @@ static void etnaviv_gpu_start_fe_idleloop(struct etnaviv_gpu *gpu, u16 prefetch; u32 address; - WARN_ON(gpu->state != ETNA_GPU_STATE_INITIALIZED); + drm_WARN_ON(gpu->drm, gpu->state != ETNA_GPU_STATE_INITIALIZED); /* setup the MMU */ etnaviv_iommu_restore(gpu, context); @@ -734,8 +734,8 @@ static void etnaviv_gpu_setup_pulse_eater(struct etnaviv_gpu *gpu) static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu) { - WARN_ON(!(gpu->state == ETNA_GPU_STATE_IDENTIFIED || - gpu->state == ETNA_GPU_STATE_RESET)); + drm_WARN_ON(gpu->drm, !(gpu->state == ETNA_GPU_STATE_IDENTIFIED || + gpu->state == ETNA_GPU_STATE_RESET)); if ((etnaviv_is_model_rev(gpu, 0x320, 0x5007) || etnaviv_is_model_rev(gpu, 0x320, 0x5220)) && diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c index 02d9408d41bc..ed6c42384856 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c @@ -138,9 +138,10 @@ static void etnaviv_iommu_remove_mapping(struct etnaviv_iommu_context *context, void etnaviv_iommu_reap_mapping(struct etnaviv_vram_mapping *mapping) { struct etnaviv_io
[PATCH v15 05/19] drm/etnaviv: Add contructor and destructor for etnaviv_gem_get_mapping structure
Because this make the code more easier to understand, When GPU access the VRAM, it will allocate a new mapping to use if there don't have one. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem.c | 40 +++ drivers/gpu/drm/etnaviv/etnaviv_gem.h | 6 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 85d4e7c87a6a..55004fa9fabd 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -227,6 +227,30 @@ etnaviv_gem_get_vram_mapping(struct etnaviv_gem_object *obj, return NULL; } +static struct etnaviv_vram_mapping * +etnaviv_gem_vram_mapping_new(struct etnaviv_gem_object *etnaviv_obj) +{ + struct etnaviv_vram_mapping *mapping; + + mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); + if (!mapping) + return NULL; + + INIT_LIST_HEAD(&mapping->scan_node); + mapping->object = etnaviv_obj; + mapping->use = 1; + + list_add_tail(&mapping->obj_node, &etnaviv_obj->vram_list); + + return mapping; +} + +static void etnaviv_gem_vram_mapping_destroy(struct etnaviv_vram_mapping *mapping) +{ + list_del(&mapping->obj_node); + kfree(mapping); +} + void etnaviv_gem_mapping_unreference(struct etnaviv_vram_mapping *mapping) { struct etnaviv_gem_object *etnaviv_obj = mapping->object; @@ -289,27 +313,20 @@ struct etnaviv_vram_mapping *etnaviv_gem_mapping_get( */ mapping = etnaviv_gem_get_vram_mapping(etnaviv_obj, NULL); if (!mapping) { - mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); + mapping = etnaviv_gem_vram_mapping_new(etnaviv_obj); if (!mapping) { ret = -ENOMEM; goto out; } - - INIT_LIST_HEAD(&mapping->scan_node); - mapping->object = etnaviv_obj; } else { - list_del(&mapping->obj_node); + mapping->use = 1; } - mapping->use = 1; - ret = etnaviv_iommu_map_gem(mmu_context, etnaviv_obj, mmu_context->global->memory_base, mapping, va); if (ret < 0) - kfree(mapping); - else - list_add_tail(&mapping->obj_node, &etnaviv_obj->vram_list); + etnaviv_gem_vram_mapping_destroy(mapping); out: mutex_unlock(&etnaviv_obj->lock); @@ -544,8 +561,7 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj) if (context) etnaviv_iommu_unmap_gem(context, mapping); - list_del(&mapping->obj_node); - kfree(mapping); + etnaviv_gem_vram_mapping_destroy(mapping); } etnaviv_obj->ops->vunmap(etnaviv_obj); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h index d4965de3007c..f2ac64d8e90b 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h @@ -31,6 +31,12 @@ struct etnaviv_vram_mapping { u32 iova; }; +static inline struct etnaviv_vram_mapping * +to_etnaviv_vram_mapping(struct drm_mm_node *vram) +{ + return container_of(vram, struct etnaviv_vram_mapping, vram_node); +} + struct etnaviv_gem_object { struct drm_gem_object base; const struct etnaviv_gem_ops *ops; -- 2.43.0
[PATCH v15 04/19] drm/etnaviv: Make etnaviv_gem_prime_vmap() a static function
The etnaviv_gem_prime_vmap() function has no caller in the etnaviv_gem_prime.c file, move it into etnaviv_gem.c file. While at it, rename it as etnaviv_gem_object_vmap(), since it is a intermidiate layer function, it has no direct relation ship with the PRIME. The etnaviv_gem_prime.c file already has etnaviv_gem_prime_vmap_impl() as the implementation to vmap a imported GEM buffer object. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_drv.h | 1 - drivers/gpu/drm/etnaviv/etnaviv_gem.c | 16 +++- drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 12 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index 2eb2ff13f6e8..c217b54b214c 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -55,7 +55,6 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, u64 *offset); struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj); -int etnaviv_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map); struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev, struct dma_buf_attachment *attach, struct sg_table *sg); int etnaviv_gem_prime_pin(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index fad23494d08e..85d4e7c87a6a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -340,6 +341,19 @@ void *etnaviv_gem_vmap(struct drm_gem_object *obj) return etnaviv_obj->vaddr; } +static int etnaviv_gem_object_vmap(struct drm_gem_object *obj, + struct iosys_map *map) +{ + void *vaddr; + + vaddr = etnaviv_gem_vmap(obj); + if (!vaddr) + return -ENOMEM; + iosys_map_set_vaddr(map, vaddr); + + return 0; +} + void etnaviv_gem_vunmap(struct drm_gem_object *obj) { struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); @@ -595,7 +609,7 @@ static const struct drm_gem_object_funcs etnaviv_gem_object_funcs = { .pin = etnaviv_gem_prime_pin, .unpin = etnaviv_gem_prime_unpin, .get_sg_table = etnaviv_gem_prime_get_sg_table, - .vmap = etnaviv_gem_prime_vmap, + .vmap = etnaviv_gem_object_vmap, .vunmap = etnaviv_gem_object_vunmap, .mmap = etnaviv_gem_mmap, .vm_ops = &vm_ops, diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c index bea50d720450..8f523cbee60a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c @@ -25,18 +25,6 @@ struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj) return drm_prime_pages_to_sg(obj->dev, etnaviv_obj->pages, npages); } -int etnaviv_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map) -{ - void *vaddr; - - vaddr = etnaviv_gem_vmap(obj); - if (!vaddr) - return -ENOMEM; - iosys_map_set_vaddr(map, vaddr); - - return 0; -} - int etnaviv_gem_prime_pin(struct drm_gem_object *obj) { if (!obj->import_attach) { -- 2.43.0
[PATCH v15 03/19] drm/etnaviv: Implement drm_gem_object_funcs::vunmap()
The vunmap() can be used to release virtual mapping obtained by vmap(), While the vmap() is used to make a long duration mapping of multiple physical pages into a contiguous virtual space. Make the implementation-specific vunmap() operation untangled with the etnaviv_gem_xxx_release() function. As then, the etnaviv_gem_xxx_release() only need to responsible for the release page works. The etnaviv_gem_vunmap() is added for driver internal usa case, where no DRM GEM framework is involved. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_drv.h | 1 + drivers/gpu/drm/etnaviv/etnaviv_gem.c | 38 - drivers/gpu/drm/etnaviv/etnaviv_gem.h | 1 + drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 13 --- 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index b3eb1662e90c..2eb2ff13f6e8 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -61,6 +61,7 @@ struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev, int etnaviv_gem_prime_pin(struct drm_gem_object *obj); void etnaviv_gem_prime_unpin(struct drm_gem_object *obj); void *etnaviv_gem_vmap(struct drm_gem_object *obj); +void etnaviv_gem_vunmap(struct drm_gem_object *obj); int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op, struct drm_etnaviv_timespec *timeout); int etnaviv_gem_cpu_fini(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 6bdf72cd9e85..fad23494d08e 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -340,6 +340,25 @@ void *etnaviv_gem_vmap(struct drm_gem_object *obj) return etnaviv_obj->vaddr; } +void etnaviv_gem_vunmap(struct drm_gem_object *obj) +{ + struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); + + if (!etnaviv_obj->vaddr) + return; + + mutex_lock(&etnaviv_obj->lock); + etnaviv_obj->ops->vunmap(etnaviv_obj); + etnaviv_obj->vaddr = NULL; + mutex_unlock(&etnaviv_obj->lock); +} + +static void etnaviv_gem_object_vunmap(struct drm_gem_object *obj, + struct iosys_map *map) +{ + etnaviv_gem_vunmap(obj); +} + static void *etnaviv_gem_vmap_impl(struct etnaviv_gem_object *obj) { struct page **pages; @@ -471,14 +490,21 @@ void etnaviv_gem_describe_objects(struct etnaviv_drm_private *priv, static void etnaviv_gem_shmem_release(struct etnaviv_gem_object *etnaviv_obj) { - vunmap(etnaviv_obj->vaddr); put_pages(etnaviv_obj); } +static void etnaviv_gem_shmem_vunmap(struct etnaviv_gem_object *etnaviv_obj) +{ + lockdep_assert_held(&etnaviv_obj->lock); + + vunmap(etnaviv_obj->vaddr); +} + static const struct etnaviv_gem_ops etnaviv_gem_shmem_ops = { .get_pages = etnaviv_gem_shmem_get_pages, .release = etnaviv_gem_shmem_release, .vmap = etnaviv_gem_vmap_impl, + .vunmap = etnaviv_gem_shmem_vunmap, .mmap = etnaviv_gem_mmap_obj, }; @@ -508,6 +534,7 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj) kfree(mapping); } + etnaviv_obj->ops->vunmap(etnaviv_obj); etnaviv_obj->ops->release(etnaviv_obj); drm_gem_object_release(obj); @@ -569,6 +596,7 @@ static const struct drm_gem_object_funcs etnaviv_gem_object_funcs = { .unpin = etnaviv_gem_prime_unpin, .get_sg_table = etnaviv_gem_prime_get_sg_table, .vmap = etnaviv_gem_prime_vmap, + .vunmap = etnaviv_gem_object_vunmap, .mmap = etnaviv_gem_mmap, .vm_ops = &vm_ops, }; @@ -723,6 +751,13 @@ static void etnaviv_gem_userptr_release(struct etnaviv_gem_object *etnaviv_obj) } } +static void etnaviv_gem_userptr_vunmap(struct etnaviv_gem_object *etnaviv_obj) +{ + lockdep_assert_held(&etnaviv_obj->lock); + + vunmap(etnaviv_obj->vaddr); +} + static int etnaviv_gem_userptr_mmap_obj(struct etnaviv_gem_object *etnaviv_obj, struct vm_area_struct *vma) { @@ -733,6 +768,7 @@ static const struct etnaviv_gem_ops etnaviv_gem_userptr_ops = { .get_pages = etnaviv_gem_userptr_get_pages, .release = etnaviv_gem_userptr_release, .vmap = etnaviv_gem_vmap_impl, + .vunmap = etnaviv_gem_userptr_vunmap, .mmap = etnaviv_gem_userptr_mmap_obj, }; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h index 3f8fe19a77cc..d4965de3007c 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h @@ -65,6 +65,7 @@ struct etnaviv_gem_ops { int (*get_pages)(struct etnaviv_gem_object *); void (*release)(struct etnaviv_gem_object *); void
[PATCH v15 02/19] drm/etnaviv: Export drm_gem_print_info() and use it
This will make the newly implemented etnaviv_gem_object_funcs::print_info get in use, which improves code sharing and simplifies debugfs. Achieve better humen readability for debug log. Use container_of_const() if 'struct etnaviv_gem_object *etnaviv_obj' is a constant pointer. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/drm_gem.c | 1 + drivers/gpu/drm/etnaviv/etnaviv_gem.c | 13 + include/drm/drm_gem.h | 2 ++ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index d4bbc5d109c8..9c5c971c1b23 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1160,6 +1160,7 @@ void drm_gem_print_info(struct drm_printer *p, unsigned int indent, if (obj->funcs->print_info) obj->funcs->print_info(p, indent, obj); } +EXPORT_SYMBOL(drm_gem_print_info); int drm_gem_pin_locked(struct drm_gem_object *obj) { diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 543d881585b3..6bdf72cd9e85 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -3,6 +3,7 @@ * Copyright (C) 2015-2018 Etnaviv Project */ +#include #include #include #include @@ -432,15 +433,11 @@ int etnaviv_gem_wait_bo(struct etnaviv_gpu *gpu, struct drm_gem_object *obj, #ifdef CONFIG_DEBUG_FS static void etnaviv_gem_describe(struct drm_gem_object *obj, struct seq_file *m) { - struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); + struct drm_printer p = drm_seq_file_printer(m); struct dma_resv *robj = obj->resv; - unsigned long off = drm_vma_node_start(&obj->vma_node); int r; - seq_printf(m, "%08x: %c %2d (%2d) %08lx %p %zd\n", - etnaviv_obj->flags, is_active(etnaviv_obj) ? 'A' : 'I', - obj->name, kref_read(&obj->refcount), - off, etnaviv_obj->vaddr, obj->size); + drm_gem_print_info(&p, 1, obj); r = dma_resv_lock(robj, NULL); if (r) @@ -461,7 +458,7 @@ void etnaviv_gem_describe_objects(struct etnaviv_drm_private *priv, list_for_each_entry(etnaviv_obj, &priv->gem_list, gem_node) { struct drm_gem_object *obj = &etnaviv_obj->base; - seq_puts(m, " "); + seq_printf(m, "obj[%d]:\n", count); etnaviv_gem_describe(obj, m); count++; size += obj->size; @@ -556,7 +553,7 @@ static void etnaviv_gem_object_info(struct drm_printer *p, { const struct etnaviv_gem_object *etnaviv_obj; - etnaviv_obj = container_of(obj, struct etnaviv_gem_object, base); + etnaviv_obj = container_of_const(obj, struct etnaviv_gem_object, base); drm_printf_indent(p, indent, "caching mode=%s\n", etnaviv_gem_obj_caching_info(etnaviv_obj->flags)); diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index bae4865b2101..0791566fab53 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -480,6 +480,8 @@ void drm_gem_vm_close(struct vm_area_struct *vma); int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, struct vm_area_struct *vma); int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); +void drm_gem_print_info(struct drm_printer *p, unsigned int indent, + const struct drm_gem_object *obj); /** * drm_gem_object_get - acquire a GEM buffer object reference -- 2.43.0
[PATCH v15 01/19] drm/etnaviv: Implement drm_gem_object_funcs::print_info()
It will be called by drm_gem_print_info() if implemented, and it can provide more information about the framebuffer objects. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem.c | 32 +++ drivers/gpu/drm/etnaviv/etnaviv_gem.h | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 4247a10f8d4f..543d881585b3 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -534,8 +534,40 @@ static const struct vm_operations_struct vm_ops = { .close = drm_gem_vm_close, }; +static const char *etnaviv_gem_obj_caching_info(u32 flags) +{ + switch (flags & ETNA_BO_CACHE_MASK) { + case ETNA_BO_CACHED: + return "cached"; + case ETNA_BO_UNCACHED: + return "uncached"; + case ETNA_BO_WC: + return "write-combine"; + default: + break; + } + + return "unknown"; +} + +static void etnaviv_gem_object_info(struct drm_printer *p, + unsigned int indent, + const struct drm_gem_object *obj) +{ + const struct etnaviv_gem_object *etnaviv_obj; + + etnaviv_obj = container_of(obj, struct etnaviv_gem_object, base); + + drm_printf_indent(p, indent, "caching mode=%s\n", + etnaviv_gem_obj_caching_info(etnaviv_obj->flags)); + drm_printf_indent(p, indent, "active=%s\n", + str_yes_no(is_active(etnaviv_obj))); + drm_printf_indent(p, indent, "vaddr=%p\n", etnaviv_obj->vaddr); +} + static const struct drm_gem_object_funcs etnaviv_gem_object_funcs = { .free = etnaviv_gem_free_object, + .print_info = etnaviv_gem_object_info, .pin = etnaviv_gem_prime_pin, .unpin = etnaviv_gem_prime_unpin, .get_sg_table = etnaviv_gem_prime_get_sg_table, diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h index a42d260cac2c..3f8fe19a77cc 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h @@ -68,7 +68,7 @@ struct etnaviv_gem_ops { int (*mmap)(struct etnaviv_gem_object *, struct vm_area_struct *); }; -static inline bool is_active(struct etnaviv_gem_object *etnaviv_obj) +static inline bool is_active(const struct etnaviv_gem_object *etnaviv_obj) { return atomic_read(&etnaviv_obj->gpu_active) != 0; } -- 2.43.0
[PATCH v15 00/19] drm/etnaviv: Add driver wrapper for vivante GPUs attached on PCI(e) device
drm/etnaviv use the component framework to bind multiple GPU cores to a virtual master, the virtual master is manually create during driver load time. This may works well for SoCs, yet there are some PCIe card has the vivante GPU cores integrated. The driver lacks support for PCIe devices currently. Adds PCIe driver wrapper on the top of drm/etnaviv, the component framework is still being used to bind subdevices, even though there is only one GPU core. But the process is going to be reversed, we create virtual platform device for each of the vivante GPU IP core that is shipped by the PCIe card. Select the PCIe device as parent, generate a virtual platform device as component master to take over the bind actions. Sui Jingfeng (19): drm/etnaviv: Implement drm_gem_object_funcs::print_info() drm/etnaviv: Export drm_gem_print_info() and use it drm/etnaviv: Implement drm_gem_object_funcs::vunmap() drm/etnaviv: Make etnaviv_gem_prime_vmap() a static function drm/etnaviv: Add contructor and destructor for etnaviv_gem_get_mapping structure drm/etnaviv: Prefer drm_device based drm_WARN_ON() over regular WARN_ON() drm/etnaviv: Add a dedicated helper function to get various clocks drm/etnaviv: Fix wrong caching mode being used for non writecombine buffers drm/etnaviv: Add constructor and destructor for the etnaviv_drm_private structure drm/etnaviv: Embed struct drm_device into struct etnaviv_drm_private drm/etnaviv: Add etnaviv_gem_obj_remove() helper drm/etnaviv: Add support for cached coherent caching mode drm/etnaviv: Add support for vivante GPU cores attached via PCIe device drm/etnaviv: Add PCIe IP setup code drm/etnaviv: Make more use of the etnaviv_gem_new_private() function drm/etnaviv: Call etnaviv_gem_obj_add() in ernaviv_gem_new_private() drm/etnaviv: Support to manage dedicated VRAM base on drm_mm drm/etnaviv: Allow userspace specify the domain of etnaviv GEM buffer object drm/etnaviv: Expose basic sanity tests via debugfs v10: * Add one more cleanup patch * Resolve the conflict with a patch from Rob * Make the dummy PCI stub inlined * Print only if the platform is dma-coherrent V11: * Process reviews since V10. * Provide a side by side implement V12: * Create a virtual platform device for the subcomponent GPU cores * Bind all subordinate GPU cores to the real PCI master via component. V13: * Drop the non-component code path, always use the component framework to bind subcomponent GPU core. Even though there is only one core. * Defer the irq handler register. * Rebase and improve the commit message V14: * Rebase onto etnaviv-next and improve commit message. V15: * Plug in a drm-mm based dedicated VRAM range allocator. drivers/gpu/drm/drm_gem.c| 1 + drivers/gpu/drm/etnaviv/Kconfig | 9 + drivers/gpu/drm/etnaviv/Makefile | 5 + drivers/gpu/drm/etnaviv/etnaviv_debugfs.c| 118 + drivers/gpu/drm/etnaviv/etnaviv_debugfs.h| 15 ++ drivers/gpu/drm/etnaviv/etnaviv_drv.c| 183 + drivers/gpu/drm/etnaviv/etnaviv_drv.h| 40 ++- drivers/gpu/drm/etnaviv/etnaviv_gem.c| 224 drivers/gpu/drm/etnaviv/etnaviv_gem.h| 21 +- drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 31 +-- drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c | 2 +- drivers/gpu/drm/etnaviv/etnaviv_gem_vram.c | 264 +++ drivers/gpu/drm/etnaviv/etnaviv_gem_vram.h | 14 + drivers/gpu/drm/etnaviv/etnaviv_gpu.c| 136 +++--- drivers/gpu/drm/etnaviv/etnaviv_gpu.h| 4 + drivers/gpu/drm/etnaviv/etnaviv_mmu.c| 11 +- drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c| 217 +++ drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h| 63 + drivers/gpu/drm/etnaviv/pcie_ip_setup.c | 109 include/drm/drm_gem.h| 2 + include/uapi/drm/etnaviv_drm.h | 13 + 21 files changed, 1308 insertions(+), 174 deletions(-) create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_debugfs.c create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_debugfs.h create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gem_vram.c create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gem_vram.h create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h create mode 100644 drivers/gpu/drm/etnaviv/pcie_ip_setup.c -- 2.43.0
[PATCH] drm/etnaviv: Print error message when driver can't get pages
This error could happen when the GFP_HIGHUSER flag is set, such an error can also be seen on the X86 platform. According to the kernel document in gfp_types.h, "the GFP_HIGHUSER is for userspace allocations that may be mapped to userspace, it do not need to be directly accessible by the kernel." However, drm/etnaviv will use the pages to implement vmap and mmap operations of the GEM object function. The flag still set at present. When we can't get pages, it certainly is a bug. Hence, we should print this kind of error with drm_err() instead of dev_dbg(). Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 5c0c9d4e3be1..5ffc31f32ac9 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -61,7 +61,7 @@ static int etnaviv_gem_shmem_get_pages(struct etnaviv_gem_object *etnaviv_obj) struct page **p = drm_gem_get_pages(&etnaviv_obj->base); if (IS_ERR(p)) { - dev_dbg(dev->dev, "could not get pages: %ld\n", PTR_ERR(p)); + drm_err(dev, "could not get pages: %ld\n", PTR_ERR(p)); return PTR_ERR(p); } -- 2.43.0
Re: [v2] drm/etnaviv: Clear the __GFP_HIGHMEM bit in GFP_HIGHUSER with 32 address
Hi, Xiaolei Thanks for your nice catch! I have more to say. On 2024/8/16 09:55, Wang, Xiaolei wrote: Ping ... 32 address -> 32-bit address, Perhaps, we could improve the commit title a little bit by writing a more accurate sentence if possible, say: drm/etnaviv: Properly request pages from DMA32 zone when needed or drm/etnaviv: Request pages from DMA32 zone on addressing_limited thanks xiaolei Vivante GPU is a 32-bit GPU, it do can access 40-bit physical address via its MMU(IOMMU). But this is only possible *after* the MMU has been setup(initialized). Before GPU page table is setup(and flush-ed into the GPU's TLB), the device can only access 32-bit physical addresses and the addresses has to be physical continues in ranges. The GPU page tables (GART) and command buffer has to reside in low 4GB address. diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 7c7f97793ddd..0e6bdf2d028b 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -844,8 +844,10 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) * request pages for our SHM backend buffers from the DMA32 zone to * hopefully avoid performance killing SWIOTLB bounce buffering. */ - if (dma_addressing_limited(gpu->dev)) + if (dma_addressing_limited(gpu->dev)) { priv->shm_gfp_mask |= GFP_DMA32; + priv->shm_gfp_mask &= ~__GFP_HIGHMEM; + } The code here still looks itchy and risky, because for a i.MX8 SoC with multiple vivante GPU core. We will modify priv->shm_gfp_mask *multiple* time. For the 2D core and the 3D core have different DMA addressing constraint. Then, only the last(latest) modify will be effective. This lead to the probe order dependent. However this may not be a problem in practice, as usually, all vivante GPUs in the system will share the same DMA constraints. And the driver assume that. But then, we probably still should not modify the global shared GFP mask multiple time. Now that we do assume that all vivante GPUs in the system share the same DMA constraints. And the DMA constraints information has been assigned to the virtual master. The right time to modify the `priv->shm_gfp_mask` should be in the etnaviv_bind() function. as this can eliminate overlap(repeat) stores. Please consider move the entire if() {} to etnaviv_bind(), just below where the 'priv->shm_gfp_mask' was initially initialized. or alternatively we can just hard-code to use low 4GM memmory only: priv->shm_gfp_mask = GFP_USER | GFP_DMA32 | __GFP_RETRY_MAYFAIL | __GFP_NOWARN; Best regards, Sui /* Create buffer: */ ret = etnaviv_cmdbuf_init(priv->cmdbuf_suballoc, &gpu->buffer,
Re: [v2] drm/etnaviv: Clear the __GFP_HIGHMEM bit in GFP_HIGHUSER with 32 address
On 2024/8/31 03:40, Sui Jingfeng wrote: Hi, Xiaolei On 2024/8/16 09:55, Wang, Xiaolei wrote: Ping ... I think, the more proper fix that Lucas hint is to modify the 'priv->shm_gfp_mask' variable in the|etnaviv_bind() function|. Say: |Use "priv->shm_gfp_mask = GFP_USER | __GFP_RETRY_MAYFAIL | __GFP_NOWARN;"| instead of |"priv->shm_gfp_mask = ||GFP_HIGHUSER||| __GFP_RETRY_MAYFAIL | __GFP_NOWARN;|" Oops, please ignore the irrelevant(superfluous) "|" characters in my reply, my Thunderbird mail client has some problem, generate them unreasonably. Should be: priv->shm_gfp_mask = GFP_USER | __GFP_RETRY_MAYFAIL | __GFP_NOWARN; Right? thanks xiaolei diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 7c7f97793ddd..0e6bdf2d028b 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -844,8 +844,10 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) * request pages for our SHM backend buffers from the DMA32 zone to * hopefully avoid performance killing SWIOTLB bounce buffering. */ - if (dma_addressing_limited(gpu->dev)) + if (dma_addressing_limited(gpu->dev)) { priv->shm_gfp_mask |= GFP_DMA32; + priv->shm_gfp_mask &= ~__GFP_HIGHMEM; + } /* Create buffer: */ ret = etnaviv_cmdbuf_init(priv->cmdbuf_suballoc, &gpu->buffer, -- Best regards, Sui
Re: [v2] drm/etnaviv: Clear the __GFP_HIGHMEM bit in GFP_HIGHUSER with 32 address
Hi, Xiaolei On 2024/8/16 09:55, Wang, Xiaolei wrote: Ping ... I think, the more proper fix that Lucas hint is to modify the 'priv->shm_gfp_mask' variable in the|etnaviv_bind() function|. Say: |Use "priv->shm_gfp_mask = GFP_USER | __GFP_RETRY_MAYFAIL | __GFP_NOWARN;"| instead of |"priv->shm_gfp_mask = ||GFP_HIGHUSER||| __GFP_RETRY_MAYFAIL | __GFP_NOWARN;|" Right? thanks xiaolei diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 7c7f97793ddd..0e6bdf2d028b 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -844,8 +844,10 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) * request pages for our SHM backend buffers from the DMA32 zone to * hopefully avoid performance killing SWIOTLB bounce buffering. */ - if (dma_addressing_limited(gpu->dev)) + if (dma_addressing_limited(gpu->dev)) { priv->shm_gfp_mask |= GFP_DMA32; + priv->shm_gfp_mask &= ~__GFP_HIGHMEM; + } /* Create buffer: */ ret = etnaviv_cmdbuf_init(priv->cmdbuf_suballoc, &gpu->buffer, -- Best regards, Sui
[PATCH 0/2] drm/etnaviv: Implement drm_gem_object_funcs::print_info()
It will be called by drm_gem_print_info() if have implemented, and this can provide more information about the framebuffer objects. In order to make the newly implemented etnaviv_gem_object_funcs::print_info() get in use, we make the drm_gem_print_info() exported, then we re-implement the etnaviv_gem_describe() base on it. Sample Testing Information: [root@fedora 0]# ls clients DPI-1 encoder-1gem_names mmring crtc-0 DPI-2 framebuffer gpu mmu state crtc-1 encoder-0 gem internal_clients name [root@fedora 0]# cat framebuffer framebuffer[49]: allocated by = Xorg refcount=1 format=AR24 little-endian (0x34325241) modifier=0x0 size=32x32 layers: size[0]=32x32 pitch[0]=128 offset[0]=0 obj[0]: name=0 refcount=3 start=00040096 size=16384 imported=no caching mode=write-combine active=no vaddr= framebuffer[47]: allocated by = Xorg refcount=2 format=XR24 little-endian (0x34325258) modifier=0x0 size=1024x600 layers: size[0]=1024x600 pitch[0]=4096 offset[0]=0 obj[0]: name=0 refcount=3 start=0004 size=2457600 imported=no caching mode=write-combine active=no vaddr= [root@fedora 0]# cat gem obj[0]: name=0 refcount=3 start=0004 size=2457600 imported=no caching mode=write-combine active=no vaddr= obj[1]: name=0 refcount=3 start=00040096 size=16384 imported=no caching mode=write-combine active=no vaddr= obj[2]: name=0 refcount=2 start=00040097 size=16384 imported=no caching mode=write-combine active=no vaddr= Total 3 objects, 2490368 bytes Sui Jingfeng (2): drm/etnaviv: Implement drm_gem_object_funcs::print_info() drm/etnaviv: Export drm_gem_print_info() and use it drivers/gpu/drm/drm_gem.c | 1 + drivers/gpu/drm/etnaviv/etnaviv_gem.c | 43 ++- drivers/gpu/drm/etnaviv/etnaviv_gem.h | 2 +- include/drm/drm_gem.h | 2 ++ 4 files changed, 40 insertions(+), 8 deletions(-) -- 2.34.1
[PATCH 2/2] drm/etnaviv: Export drm_gem_print_info() and use it
This will make the newly implemented etnaviv_gem_object_funcs::print_info get in use, which improves code sharing and simplifies debugfs. Achieve better humen readability for debug log. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/drm_gem.c | 1 + drivers/gpu/drm/etnaviv/etnaviv_gem.c | 11 --- include/drm/drm_gem.h | 2 ++ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index d4bbc5d109c8..9c5c971c1b23 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1160,6 +1160,7 @@ void drm_gem_print_info(struct drm_printer *p, unsigned int indent, if (obj->funcs->print_info) obj->funcs->print_info(p, indent, obj); } +EXPORT_SYMBOL(drm_gem_print_info); int drm_gem_pin_locked(struct drm_gem_object *obj) { diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 9a688c95f34d..f2f446d46921 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -3,6 +3,7 @@ * Copyright (C) 2015-2018 Etnaviv Project */ +#include #include #include #include @@ -432,15 +433,11 @@ int etnaviv_gem_wait_bo(struct etnaviv_gpu *gpu, struct drm_gem_object *obj, #ifdef CONFIG_DEBUG_FS static void etnaviv_gem_describe(struct drm_gem_object *obj, struct seq_file *m) { - struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); + struct drm_printer p = drm_seq_file_printer(m); struct dma_resv *robj = obj->resv; - unsigned long off = drm_vma_node_start(&obj->vma_node); int r; - seq_printf(m, "%08x: %c %2d (%2d) %08lx %p %zd\n", - etnaviv_obj->flags, is_active(etnaviv_obj) ? 'A' : 'I', - obj->name, kref_read(&obj->refcount), - off, etnaviv_obj->vaddr, obj->size); + drm_gem_print_info(&p, 1, obj); r = dma_resv_lock(robj, NULL); if (r) @@ -461,7 +458,7 @@ void etnaviv_gem_describe_objects(struct etnaviv_drm_private *priv, list_for_each_entry(etnaviv_obj, &priv->gem_list, gem_node) { struct drm_gem_object *obj = &etnaviv_obj->base; - seq_puts(m, " "); + seq_printf(m, "obj[%d]:\n", count); etnaviv_gem_describe(obj, m); count++; size += obj->size; diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index bae4865b2101..0791566fab53 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -480,6 +480,8 @@ void drm_gem_vm_close(struct vm_area_struct *vma); int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, struct vm_area_struct *vma); int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); +void drm_gem_print_info(struct drm_printer *p, unsigned int indent, + const struct drm_gem_object *obj); /** * drm_gem_object_get - acquire a GEM buffer object reference -- 2.34.1
[PATCH 1/2] drm/etnaviv: Implement drm_gem_object_funcs::print_info()
It will be called by drm_gem_print_info() if have implemented, and this can provide more information about the framebuffer objects. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem.c | 32 +++ drivers/gpu/drm/etnaviv/etnaviv_gem.h | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 5c0c9d4e3be1..9a688c95f34d 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -533,8 +533,40 @@ static const struct vm_operations_struct vm_ops = { .close = drm_gem_vm_close, }; +static const char *etnaviv_gem_obj_caching_info(u32 flags) +{ + switch (flags & ETNA_BO_CACHE_MASK) { + case ETNA_BO_CACHED: + return "cached"; + case ETNA_BO_UNCACHED: + return "uncached"; + case ETNA_BO_WC: + return "write-combine"; + default: + break; + } + + return "unknown"; +} + +static void etnaviv_gem_object_info(struct drm_printer *p, + unsigned int indent, + const struct drm_gem_object *obj) +{ + const struct etnaviv_gem_object *etnaviv_obj; + + etnaviv_obj = container_of(obj, struct etnaviv_gem_object, base); + + drm_printf_indent(p, indent, "caching mode=%s\n", + etnaviv_gem_obj_caching_info(etnaviv_obj->flags)); + drm_printf_indent(p, indent, "active=%s\n", + str_yes_no(is_active(etnaviv_obj))); + drm_printf_indent(p, indent, "vaddr=%p\n", etnaviv_obj->vaddr); +} + static const struct drm_gem_object_funcs etnaviv_gem_object_funcs = { .free = etnaviv_gem_free_object, + .print_info = etnaviv_gem_object_info, .pin = etnaviv_gem_prime_pin, .unpin = etnaviv_gem_prime_unpin, .get_sg_table = etnaviv_gem_prime_get_sg_table, diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h index a42d260cac2c..3f8fe19a77cc 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h @@ -68,7 +68,7 @@ struct etnaviv_gem_ops { int (*mmap)(struct etnaviv_gem_object *, struct vm_area_struct *); }; -static inline bool is_active(struct etnaviv_gem_object *etnaviv_obj) +static inline bool is_active(const struct etnaviv_gem_object *etnaviv_obj) { return atomic_read(&etnaviv_obj->gpu_active) != 0; } -- 2.34.1
[PATCH] drm/etnaviv: Use unsigned type to count the number of userspace pages
The unpin_user_pages() function takes an unsigned long argument to store length of the number of user space pages, and struct drm_gem_object::size is a size_t type. The number of pages can not be negative, hence, use an unsigned variable to store the number of pages. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index ce9c9233c4a6..fa0d193cec26 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -695,7 +695,7 @@ static void etnaviv_gem_userptr_release(struct etnaviv_gem_object *etnaviv_obj) kfree(etnaviv_obj->sgt); } if (etnaviv_obj->pages) { - int npages = etnaviv_obj->base.size >> PAGE_SHIFT; + unsigned int npages = etnaviv_obj->base.size >> PAGE_SHIFT; unpin_user_pages(etnaviv_obj->pages, npages); kvfree(etnaviv_obj->pages); -- 2.34.1
[PATCH] drm/etnaviv: Use unsigned type to count the number of pages
The drm_prime_pages_to_sg() function takes unsigned int argument to store length of the page vector, and the type of struct drm_gem_object::size is a size_t. The size of the object in CPU pages can not be negative, hence, use unsigned variable to store the number of pages, instead of the signed type. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c index 3524b5811682..6b98200068e4 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c @@ -17,7 +17,7 @@ static struct lock_class_key etnaviv_prime_lock_class; struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj) { struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); - int npages = obj->size >> PAGE_SHIFT; + unsigned int npages = obj->size >> PAGE_SHIFT; if (WARN_ON(!etnaviv_obj->pages)) /* should have already pinned! */ return ERR_PTR(-EINVAL); -- 2.34.1
[PATCH] drm/etnaviv: Drop the header
Currently, the etnaviv_gem_submit.c isn't call any runtime power management functions. So drop it, we can re-include it when the header really get used though. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index 3d0f8d182506..3c0a5c3e0e3d 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include -- 2.34.1
[PATCH] drm/etnaviv: Fix missing mutex_destroy()
Currently, the calling of mutex_destroy() is ignored on error handling code path. It is safe for now, since mutex_destroy() actually does nothing in non-debug builds. But the mutex_destroy() is used to mark the mutex uninitialized on debug builds, and any subsequent use of the mutex is forbidden. It also could lead to problems if mutex_destroy() gets extended, add missing mutex_destroy() to eliminate potential concerns. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c | 3 +++ drivers/gpu/drm/etnaviv/etnaviv_drv.c| 1 + drivers/gpu/drm/etnaviv/etnaviv_gem.c| 1 + drivers/gpu/drm/etnaviv/etnaviv_gpu.c| 5 + drivers/gpu/drm/etnaviv/etnaviv_mmu.c| 2 +- 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c index 721d633aece9..1edc02022be4 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c @@ -79,6 +79,9 @@ void etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc) { dma_free_wc(suballoc->dev, SUBALLOC_SIZE, suballoc->vaddr, suballoc->paddr); + + mutex_destroy(&suballoc->lock); + kfree(suballoc); } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 6500f3999c5f..7844cd961a29 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -564,6 +564,7 @@ static int etnaviv_bind(struct device *dev) out_destroy_suballoc: etnaviv_cmdbuf_suballoc_destroy(priv->cmdbuf_suballoc); out_free_priv: + mutex_destroy(&priv->gem_lock); kfree(priv); out_put: drm_dev_put(drm); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index fe665ca20c02..b68e3b235a7d 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -515,6 +515,7 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj) etnaviv_obj->ops->release(etnaviv_obj); drm_gem_object_release(obj); + mutex_destroy(&etnaviv_obj->lock); kfree(etnaviv_obj); } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index af52922ff494..d6acc4c68102 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -1929,8 +1929,13 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) static void etnaviv_gpu_platform_remove(struct platform_device *pdev) { + struct etnaviv_gpu *gpu = dev_get_drvdata(&pdev->dev); + component_del(&pdev->dev, &gpu_ops); pm_runtime_disable(&pdev->dev); + + mutex_destroy(&gpu->lock); + mutex_destroy(&gpu->sched_lock); } static int etnaviv_gpu_rpm_suspend(struct device *dev) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c index e3be16165c86..ed6c42384856 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c @@ -361,7 +361,7 @@ static void etnaviv_iommu_context_free(struct kref *kref) container_of(kref, struct etnaviv_iommu_context, refcount); etnaviv_cmdbuf_suballoc_unmap(context, &context->cmdbuf_mapping); - + mutex_destroy(&context->lock); context->global->ops->free(context); } void etnaviv_iommu_context_put(struct etnaviv_iommu_context *context) -- 2.34.1
[PATCH] drm/etnaviv: Prefer drm_device based drm_WARN_ON() over regular WARN_ON()
drm_WARN_ON() acts like WARN_ON(), but with the key difference of using device specific information so that we know from which device warning is originating from. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem.c | 9 + drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 2 +- drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 6 +++--- drivers/gpu/drm/etnaviv/etnaviv_mmu.c | 7 +-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 5c0c9d4e3be1..fe665ca20c02 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -230,7 +230,7 @@ void etnaviv_gem_mapping_unreference(struct etnaviv_vram_mapping *mapping) struct etnaviv_gem_object *etnaviv_obj = mapping->object; mutex_lock(&etnaviv_obj->lock); - WARN_ON(mapping->use == 0); + drm_WARN_ON(etnaviv_obj->base.dev, mapping->use == 0); mapping->use -= 1; mutex_unlock(&etnaviv_obj->lock); @@ -412,7 +412,7 @@ int etnaviv_gem_cpu_fini(struct drm_gem_object *obj) if (etnaviv_obj->flags & ETNA_BO_CACHED) { /* fini without a prep is almost certainly a userspace error */ - WARN_ON(etnaviv_obj->last_cpu_prep_op == 0); + drm_WARN_ON(dev, etnaviv_obj->last_cpu_prep_op == 0); dma_sync_sgtable_for_device(dev->dev, etnaviv_obj->sgt, etnaviv_op_to_dma_dir(etnaviv_obj->last_cpu_prep_op)); etnaviv_obj->last_cpu_prep_op = 0; @@ -487,12 +487,13 @@ static const struct etnaviv_gem_ops etnaviv_gem_shmem_ops = { void etnaviv_gem_free_object(struct drm_gem_object *obj) { + struct drm_device *drm = obj->dev; struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); struct etnaviv_drm_private *priv = obj->dev->dev_private; struct etnaviv_vram_mapping *mapping, *tmp; /* object should not be active */ - WARN_ON(is_active(etnaviv_obj)); + drm_WARN_ON(drm, is_active(etnaviv_obj)); mutex_lock(&priv->gem_lock); list_del(&etnaviv_obj->gem_node); @@ -502,7 +503,7 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj) obj_node) { struct etnaviv_iommu_context *context = mapping->context; - WARN_ON(mapping->use); + drm_WARN_ON(drm, mapping->use); if (context) etnaviv_iommu_unmap_gem(context, mapping); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c index 3524b5811682..c651188095b6 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c @@ -19,7 +19,7 @@ struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj) struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); int npages = obj->size >> PAGE_SHIFT; - if (WARN_ON(!etnaviv_obj->pages)) /* should have already pinned! */ + if (drm_WARN_ON(obj->dev, !etnaviv_obj->pages)) /* should have already pinned! */ return ERR_PTR(-EINVAL); return drm_prime_pages_to_sg(obj->dev, etnaviv_obj->pages, npages); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 7c7f97793ddd..af52922ff494 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -686,7 +686,7 @@ static void etnaviv_gpu_start_fe_idleloop(struct etnaviv_gpu *gpu, u16 prefetch; u32 address; - WARN_ON(gpu->state != ETNA_GPU_STATE_INITIALIZED); + drm_WARN_ON(gpu->drm, gpu->state != ETNA_GPU_STATE_INITIALIZED); /* setup the MMU */ etnaviv_iommu_restore(gpu, context); @@ -734,8 +734,8 @@ static void etnaviv_gpu_setup_pulse_eater(struct etnaviv_gpu *gpu) static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu) { - WARN_ON(!(gpu->state == ETNA_GPU_STATE_IDENTIFIED || - gpu->state == ETNA_GPU_STATE_RESET)); + drm_WARN_ON(gpu->drm, !(gpu->state == ETNA_GPU_STATE_IDENTIFIED || + gpu->state == ETNA_GPU_STATE_RESET)); if ((etnaviv_is_model_rev(gpu, 0x320, 0x5007) || etnaviv_is_model_rev(gpu, 0x320, 0x5220)) && diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c index 1661d589bf3e..e3be16165c86 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c @@ -138,9 +138,10 @@ static void etnaviv_iommu_remove_mapping(struct etnaviv_iommu_context *context, void etnaviv_iommu_reap_mapping(struct etnaviv_vram_mapping *mapping) { struct etnaviv_iommu_context *co
Re: [82/86] drm/i915: Move custom hotplug code into separate callback
Hi, On 2024/8/20 15:39, Thomas Zimmermann wrote: Hi Am 19.08.24 um 10:52 schrieb Sui Jingfeng: Hi, Thomas I love your patch, yet ... On 2024/8/16 20:23, Thomas Zimmermann wrote: i915's fbdev contains additional code for hotplugging a display that cannot be ported to the common fbdev client. Introduce the callback struct drm_fb_helper.fb_hotplug and implement it for i915. The fbdev helpers invoke the callback before handing the hotplug event. Signed-off-by: Thomas Zimmermann Cc: Jani Nikula Cc: Rodrigo Vivi Cc: Joonas Lahtinen Cc: Tvrtko Ursulin Cc: Lucas De Marchi Cc: "Thomas Hellström" --- drivers/gpu/drm/drm_fb_helper.c | 6 +++ drivers/gpu/drm/i915/display/intel_fbdev.c | 43 -- include/drm/drm_fb_helper.h | 13 +++ 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index d9e539b0fd1a..92926cb02dfb 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1938,6 +1938,12 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) if (!drm_fbdev_emulation || !fb_helper) return 0; + if (fb_helper->funcs->fb_hotplug) { We seems need to check the existence on the 'fb_helper->funcs' here, For example: if (fb_helper->funcs && fb_helper->funcs->fb_hotplug) { Otherwise, it will de-reference NULL pointer. Can be observed on a trivial driver though, with no monitor(display) connected. Indeed. That needs to be fixed. Thank you for noting. Thanks for you efforts then. To give some context: I was hoping to remove drm_fb_helper_funcs at some point. Yeah, too many helper functions may make peoples daze. fb_probe is now gone with these patches and fb_dirty can certainly be replaced as well. (I once had prototype patches to do that). Well, the grammar of "ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);" looks strange, It's lengthy and I observed you have cleaned it up at the last patch. Which also eliminates one pair "if and else" clause, the codes looks more fluent now. This leaves the new callbacks for 915, for which I don't have a good alternative solution. So it seems that drm_fb_helper_funcs will only be used by i915/xe in the long term. Well, since it is a DRM client now, maybe we could try to drop it into struct drm_driver. Just like the '.fbdev_probe' callback, this may help to achieve a 100% DRM-based console/logger IMO. Besides, a lot of DRM driver instances has the DMA/2D acceleration hardware, promote it into drm_driver structure may has the potential to utilize hardware acceleration. Drivers will more easily to have custom implementation. I'm not 100% sure if it will only be used by i915 in the future. Best regards, Sui
Re: [68/86] drm/loongson: Run DRM default client setup
Hi, On 2024/8/16 20:23, Thomas Zimmermann wrote: Call drm_client_setup() to run the kernel's default client setup for DRM. Set fbdev_probe in struct drm_driver, so that the client setup can start the common fbdev client. The loongson driver specifies a preferred color mode of 32. As this is the default if no format has been given, leave it out entirely. Signed-off-by: Thomas Zimmermann Thanks, Acked-by: Sui Jingfeng -- Best regards, Sui
Re: [82/86] drm/i915: Move custom hotplug code into separate callback
Hi, Thomas I love your patch, yet ... On 2024/8/16 20:23, Thomas Zimmermann wrote: i915's fbdev contains additional code for hotplugging a display that cannot be ported to the common fbdev client. Introduce the callback struct drm_fb_helper.fb_hotplug and implement it for i915. The fbdev helpers invoke the callback before handing the hotplug event. Signed-off-by: Thomas Zimmermann Cc: Jani Nikula Cc: Rodrigo Vivi Cc: Joonas Lahtinen Cc: Tvrtko Ursulin Cc: Lucas De Marchi Cc: "Thomas Hellström" --- drivers/gpu/drm/drm_fb_helper.c| 6 +++ drivers/gpu/drm/i915/display/intel_fbdev.c | 43 -- include/drm/drm_fb_helper.h| 13 +++ 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index d9e539b0fd1a..92926cb02dfb 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1938,6 +1938,12 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) if (!drm_fbdev_emulation || !fb_helper) return 0; + if (fb_helper->funcs->fb_hotplug) { We seems need to check the existence on the 'fb_helper->funcs' here, For example: if (fb_helper->funcs && fb_helper->funcs->fb_hotplug) { Otherwise, it will de-reference NULL pointer. Can be observed on a trivial driver though, with no monitor(display) connected. + err = fb_helper->funcs->fb_hotplug(fb_helper); + if (err) + return err; + } + mutex_lock(&fb_helper->lock); if (fb_helper->deferred_setup) { err = __drm_fb_helper_initial_config_and_unlock(fb_helper); diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c index c03fb2a4..abe77ef0bd84 100644 --- a/drivers/gpu/drm/i915/display/intel_fbdev.c +++ b/drivers/gpu/drm/i915/display/intel_fbdev.c @@ -305,10 +305,32 @@ static void intelfb_restore(struct drm_fb_helper *fb_helper) intel_fbdev_invalidate(ifbdev); } +static int intelfb_hotplug(struct drm_fb_helper *fb_helper) +{ + struct drm_device *dev = fb_helper->client.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_fbdev *ifbdev = dev_priv->display.fbdev.fbdev; + bool send_hpd; + + if (!ifbdev) + return -EINVAL; + + mutex_lock(&ifbdev->hpd_lock); + send_hpd = !ifbdev->hpd_suspended; + ifbdev->hpd_waiting = true; + mutex_unlock(&ifbdev->hpd_lock); + + if (!send_hpd || !(ifbdev->vma || dev->fb_helper->deferred_setup)) + return -EAGAIN; + + return 0; +} + static const struct drm_fb_helper_funcs intel_fb_helper_funcs = { .fb_probe = intelfb_create, .fb_dirty = intelfb_dirty, .fb_restore = intelfb_restore, + .fb_hotplug = intelfb_hotplug, }; /* @@ -557,25 +579,6 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous intel_fbdev_hpd_set_suspend(dev_priv, state); } -static int intel_fbdev_output_poll_changed(struct drm_device *dev) -{ - struct intel_fbdev *ifbdev = to_i915(dev)->display.fbdev.fbdev; - bool send_hpd; - - if (!ifbdev) - return -EINVAL; - - mutex_lock(&ifbdev->hpd_lock); - send_hpd = !ifbdev->hpd_suspended; - ifbdev->hpd_waiting = true; - mutex_unlock(&ifbdev->hpd_lock); - - if (send_hpd && (ifbdev->vma || dev->fb_helper->deferred_setup)) - drm_fb_helper_hotplug_event(dev->fb_helper); - - return 0; -} - static int intel_fbdev_restore_mode(struct drm_i915_private *dev_priv) { struct intel_fbdev *ifbdev = dev_priv->display.fbdev.fbdev; @@ -637,7 +640,7 @@ static int intel_fbdev_client_hotplug(struct drm_client_dev *client) int ret; if (dev->fb_helper) - return intel_fbdev_output_poll_changed(dev); + return drm_fb_helper_hotplug_event(fb_helper); ret = drm_fb_helper_init(dev, fb_helper); if (ret) diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 34eb77c18a13..3dcb9a60e408 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -112,6 +112,19 @@ struct drm_fb_helper_funcs { * TODO: Fix i915 to not require this callback. */ void (*fb_restore)(struct drm_fb_helper *helper); + + /** +* @fb_hotplug: +* +* Driver callback to prepare hotplug event. If set, fbdev +* emulation will invoke this callback before sending a hotplug +* event. +* +* Only for i915. Do not use in new code. +* +* TODO: Fix i915 to not require this callback. +*/ + int (*fb_hotplug)(struct drm_fb_helper *helper); }; /** -- Best regards, Sui
Re: drm/mgag200: Fix VBLANK interrupt handling
Hi, On 2024/7/31 15:09, Thomas Zimmermann wrote: Fix support for VBLANK interrupts on G200ER, G200EV and G200SE, which use a slightly different implementation than the others. The original commits forgot to update the custom helpers when adding interrupt handling for VBLANK events. Signed-off-by: Thomas Zimmermann LGTM, Acked-by: sui.jingf...@linux.dev -- Best regards, Sui
Re: [PATCH v5 2/2] drm/loongson: Add dummy gpu driver as a subcomponent
Hi, On 2024/7/29 15:15, Markus Elfring wrote: … the driver is loaded, drm/loongson driver still need to wait all of needs to wait on …? … design. Therefore, add a dummy driver for the GPU, … Is there a need to reconsider the categorisation and usability descriptions another bit for such a software component? For honoring the framework and testing the framework purpose. The GPU can possibly be the master of the all sub-components, this definitely need some extra consideration and negotiation with the DC and the fake master device. Depend on configuration and user space expectation. The problem should be solved earlier. Regards, Markus -- Best regards, Sui
Re: [PATCH v5 1/2] drm/loongson: Introduce component framework support
Hi, On 2024/7/29 14:40, Markus Elfring wrote: … +++ b/drivers/gpu/drm/loongson/loongson_drv.h @@ -0,0 +1,108 @@ … +#ifndef __LOONGSON_DRV_H__ +#define __LOONGSON_DRV_H__ … I suggest to omit leading underscores from such identifiers. https://wiki.sei.cmu.edu/confluence/display/c/DCL37-C.+Do+not+declare+or+define+a+reserved+identifier I suggest add this rules to the checkpatch.pl script, It's more safe to follow *after* your suggestion is accepted. After all, the checkpatch.pl is de-facto standard. checkpatch.pl is happy about my patch for now. Regards, Markus -- Best regards, Sui
Re: [PATCH v5 1/2] drm/loongson: Introduce component framework support
Hi, On 2024/7/29 15:37, Markus Elfring wrote: … +++ b/drivers/gpu/drm/loongson/loongson_drv.c @@ -0,0 +1,298 @@ … +static int loongson_drm_driver_probe(struct platform_device *pdev) +{ … + dev_info(&pdev->dev, "probed\n"); … +} … Do you find such information really relevant? This helps to see the probe order of all drivers, this also help us to know the name of the fake master device. Regards, Markus -- Best regards, Sui
[PATCH v5 2/2] drm/loongson: Add dummy gpu driver as a subcomponent
Loongson Graphics are PCIe multifunctional devices, the GPU and the display controller are two distinct devices. Despite hardware units that come along with PCIe are already ready to be driven by the time the driver is loaded, drm/loongson driver still need to wait all of its dependencies ready before it can register DRM service to user space. The newly introduced component framework allow us to use loose coupling design. Therefore, add a dummy driver for the GPU, it is functional as a subcomponent as well. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/Makefile | 3 + drivers/gpu/drm/loongson/loonggpu_pci_drv.c | 163 drivers/gpu/drm/loongson/loonggpu_pci_drv.h | 35 + drivers/gpu/drm/loongson/loongson_drv.c | 1 + drivers/gpu/drm/loongson/loongson_module.c | 4 + drivers/gpu/drm/loongson/loongson_module.h | 1 + 6 files changed, 207 insertions(+) create mode 100644 drivers/gpu/drm/loongson/loonggpu_pci_drv.c create mode 100644 drivers/gpu/drm/loongson/loonggpu_pci_drv.h diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile index 90c6239bd77d..cdc60ec975e4 100644 --- a/drivers/gpu/drm/loongson/Makefile +++ b/drivers/gpu/drm/loongson/Makefile @@ -17,6 +17,9 @@ loongson-y := \ lsdc_probe.o \ lsdc_ttm.o +loongson-y += \ + loonggpu_pci_drv.o + loongson-y += loongson_device.o \ loongson_drv.o \ loongson_module.o diff --git a/drivers/gpu/drm/loongson/loonggpu_pci_drv.c b/drivers/gpu/drm/loongson/loonggpu_pci_drv.c new file mode 100644 index ..52785a130b83 --- /dev/null +++ b/drivers/gpu/drm/loongson/loonggpu_pci_drv.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Authors: + * Sui Jingfeng + */ + +#include +#include + +#include +#include + +#include "loongson_drv.h" +#include "loongson_module.h" +#include "loonggpu_pci_drv.h" + +static int loonggpu_get_version(struct loonggpu_device *gpu) +{ + u32 hw_info = loong_rreg32(gpu, 0x8C); + u8 host_id; + u8 revision; + + /* LoongGPU hardware info */ + gpu->ver_major = (hw_info >> 8) & 0x0F; + gpu->ver_minor = (hw_info & 0xF0) >> 4; + revision = hw_info & 0x0F; + host_id = (hw_info >> 16) & 0xFF; + + drm_info(gpu->drm, "LoongGPU(TM): LG%x%x0, revision: %x, Host: %s\n", +gpu->ver_major, gpu->ver_minor, revision, +host_id ? "LS2K2000" : "LS7A2000"); + + return 0; +} + +static irqreturn_t loonggpu_irq_handler(int irq, void *arg) +{ + struct loonggpu_device *gpu = arg; + + drm_dbg(gpu->drm, "LoongGPU(TM) interrupted\n"); + + return IRQ_HANDLED; +} + +static int loonggpu_pci_component_bind(struct device *dev, + struct device *master, + void *data) +{ + struct loonggpu_device *gpu = dev_get_drvdata(dev); + struct drm_device *drm = data; + struct loongson_drm *ldrm = to_loongson_drm(drm); + struct pci_dev *pdev = to_pci_dev(dev); + int ret; + + gpu->drm = drm; + ldrm->loonggpu = gpu; + + loonggpu_get_version(gpu); + + ret = devm_request_irq(dev, + pdev->irq, + loonggpu_irq_handler, + IRQF_SHARED, + dev_name(dev), + gpu); + if (ret) + return ret; + + drm_info(gpu->drm, "LoongGPU(TM) irq: %d\n", pdev->irq); + + return 0; +} + +static void loonggpu_pci_component_unbind(struct device *dev, + struct device *master, + void *data) +{ + dev_dbg(dev, "LoongGPU(TM) unbind\n"); +} + +static const struct component_ops loonggpu_pci_component_ops = { + .bind = loonggpu_pci_component_bind, + .unbind = loonggpu_pci_component_unbind, +}; + +static int loonggpu_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct loonggpu_device *gpu; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + pci_set_master(pdev); + + gpu = devm_kzalloc(&pdev->dev, sizeof(*gpu), GFP_KERNEL); + if (!gpu) + return -ENOMEM; + + gpu->pdev = pdev; + + gpu->reg_base = pcim_iomap(pdev, 0, 0); + if (!gpu->reg_base) + return -ENOMEM; + + pci_set_drvdata(pdev, gpu); + + dev_info(&pdev->dev, "LoongGPU(TM) PCI driver probed\n"); + + return component_add(&pdev->dev, &loonggpu_pci_component_ops); +} + +static void loonggpu_pci_remove(struct pci_dev *pdev) +{
[PATCH v5 1/2] drm/loongson: Introduce component framework support
In some display subsystems, the functionality of a PCIe device might be too complex to manage with a single driver. A split of the functionality into child devices can help to achieve better modularity. For example, KMS drivers who have dependencies on external modules will suffer from the deferral probe problem. The problem is that the DRM driver has to tear down everything when it receives '-EPROBE_DEFER', even though the independent part of the driver is not related to this error. The idea is to migrate (offload) the dependency to submodules, creating submodule by creating platform devices manually during driver load time. Submodules are functional as agents, which will be responsible for attaching needed external modules for the main body of the whole KMS driver. Submodules are also responsible for returning '-EPROBE_DEFER' to the driver core if it needs to do so. Make LSDC PCI driver as a subcomponent and creating a platform device as a DRM proxy, which will attach the common DRM routines to our hardware after all submodules bound. The proxy is a fake master which is working at the foreground. Move DRM-related codes into the loongson_drm_master_bind(), move output related things into the submodule, because the output hardware units are relatively standalone IPs. Initialize the GPIO-I2Cs in the driver probe() of the LSDC PCIe device, tie its lifetime to the LSDC. Assign GFX PLL, TTM memory manager part, and power management part to the DRM proxy, as those entities do not belong to the LSDC itself. They are common resources. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/Makefile | 2 + drivers/gpu/drm/loongson/loongson_device.c| 30 ++ drivers/gpu/drm/loongson/loongson_drv.c | 298 +++ drivers/gpu/drm/loongson/loongson_drv.h | 108 ++ drivers/gpu/drm/loongson/loongson_module.c| 80 +++- drivers/gpu/drm/loongson/loongson_module.h| 31 ++ drivers/gpu/drm/loongson/lsdc_benchmark.c | 12 +- drivers/gpu/drm/loongson/lsdc_benchmark.h | 2 +- drivers/gpu/drm/loongson/lsdc_crtc.c | 4 +- drivers/gpu/drm/loongson/lsdc_debugfs.c | 41 +-- drivers/gpu/drm/loongson/lsdc_drv.c | 346 +- drivers/gpu/drm/loongson/lsdc_drv.h | 89 + drivers/gpu/drm/loongson/lsdc_gem.c | 42 ++- drivers/gpu/drm/loongson/lsdc_gem.h | 13 + drivers/gpu/drm/loongson/lsdc_gfxpll.c| 33 +- drivers/gpu/drm/loongson/lsdc_gfxpll.h| 3 +- drivers/gpu/drm/loongson/lsdc_i2c.c | 55 ++- drivers/gpu/drm/loongson/lsdc_i2c.h | 21 +- drivers/gpu/drm/loongson/lsdc_output.c| 183 + drivers/gpu/drm/loongson/lsdc_output.h| 33 +- drivers/gpu/drm/loongson/lsdc_output_7a1000.c | 6 +- drivers/gpu/drm/loongson/lsdc_output_7a2000.c | 20 +- drivers/gpu/drm/loongson/lsdc_pixpll.c| 4 +- drivers/gpu/drm/loongson/lsdc_plane.c | 4 +- drivers/gpu/drm/loongson/lsdc_ttm.c | 70 ++-- drivers/gpu/drm/loongson/lsdc_ttm.h | 4 +- 26 files changed, 1026 insertions(+), 508 deletions(-) create mode 100644 drivers/gpu/drm/loongson/loongson_drv.c create mode 100644 drivers/gpu/drm/loongson/loongson_drv.h create mode 100644 drivers/gpu/drm/loongson/lsdc_output.c diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile index 91e72bd900c1..90c6239bd77d 100644 --- a/drivers/gpu/drm/loongson/Makefile +++ b/drivers/gpu/drm/loongson/Makefile @@ -9,6 +9,7 @@ loongson-y := \ lsdc_gfxpll.o \ lsdc_i2c.o \ lsdc_irq.o \ + lsdc_output.o \ lsdc_output_7a1000.o \ lsdc_output_7a2000.o \ lsdc_plane.o \ @@ -17,6 +18,7 @@ loongson-y := \ lsdc_ttm.o loongson-y += loongson_device.o \ + loongson_drv.o \ loongson_module.o obj-$(CONFIG_DRM_LOONGSON) += loongson.o diff --git a/drivers/gpu/drm/loongson/loongson_device.c b/drivers/gpu/drm/loongson/loongson_device.c index 9986c8a2a255..51de71a9b692 100644 --- a/drivers/gpu/drm/loongson/loongson_device.c +++ b/drivers/gpu/drm/loongson/loongson_device.c @@ -4,6 +4,7 @@ */ #include +#include #include "lsdc_drv.h" @@ -100,3 +101,32 @@ lsdc_device_probe(struct pci_dev *pdev, enum loongson_chip_id chip_id) { return __chip_id_desc_table[chip_id]; } + +static void loongson_device_postfini(void *data) +{ + struct platform_device *proxy = data; + + platform_device_unregister(proxy); +} + +int loongson_device_preinit(struct device *parent) +{ + struct platform_device *proxy; + int ret; + + proxy = platform_device_register_resndata(parent, + "loongson.drm.proxy", + PLATFORM_DEVID_NONE, + NULL, 0, +
[PATCH v5 0/2] drm/loongson: Introduce component framework support
125MB/s Copy bo of 8100KiB 60 times from GTT to VRAM in 104ms: 4673MB/s Copy bo of 8100KiB 60 times from VRAM to GTT in 13480ms: 36MB/s Also run IGT kms_flip and fbdev tests, no obvious problems found. Sui Jingfeng (2): drm/loongson: Introduce component framework support drm/loongson: Add dummy gpu driver as a subcomponent drivers/gpu/drm/loongson/Makefile | 5 + drivers/gpu/drm/loongson/loonggpu_pci_drv.c | 163 + drivers/gpu/drm/loongson/loonggpu_pci_drv.h | 35 ++ drivers/gpu/drm/loongson/loongson_device.c| 30 ++ drivers/gpu/drm/loongson/loongson_drv.c | 299 +++ drivers/gpu/drm/loongson/loongson_drv.h | 108 ++ drivers/gpu/drm/loongson/loongson_module.c| 84 - drivers/gpu/drm/loongson/loongson_module.h| 32 ++ drivers/gpu/drm/loongson/lsdc_benchmark.c | 12 +- drivers/gpu/drm/loongson/lsdc_benchmark.h | 2 +- drivers/gpu/drm/loongson/lsdc_crtc.c | 4 +- drivers/gpu/drm/loongson/lsdc_debugfs.c | 41 +-- drivers/gpu/drm/loongson/lsdc_drv.c | 346 +- drivers/gpu/drm/loongson/lsdc_drv.h | 89 + drivers/gpu/drm/loongson/lsdc_gem.c | 42 ++- drivers/gpu/drm/loongson/lsdc_gem.h | 13 + drivers/gpu/drm/loongson/lsdc_gfxpll.c| 33 +- drivers/gpu/drm/loongson/lsdc_gfxpll.h| 3 +- drivers/gpu/drm/loongson/lsdc_i2c.c | 55 ++- drivers/gpu/drm/loongson/lsdc_i2c.h | 21 +- drivers/gpu/drm/loongson/lsdc_output.c| 183 + drivers/gpu/drm/loongson/lsdc_output.h| 33 +- drivers/gpu/drm/loongson/lsdc_output_7a1000.c | 6 +- drivers/gpu/drm/loongson/lsdc_output_7a2000.c | 20 +- drivers/gpu/drm/loongson/lsdc_pixpll.c| 4 +- drivers/gpu/drm/loongson/lsdc_plane.c | 4 +- drivers/gpu/drm/loongson/lsdc_ttm.c | 70 ++-- drivers/gpu/drm/loongson/lsdc_ttm.h | 4 +- 28 files changed, 1233 insertions(+), 508 deletions(-) create mode 100644 drivers/gpu/drm/loongson/loonggpu_pci_drv.c create mode 100644 drivers/gpu/drm/loongson/loonggpu_pci_drv.h create mode 100644 drivers/gpu/drm/loongson/loongson_drv.c create mode 100644 drivers/gpu/drm/loongson/loongson_drv.h create mode 100644 drivers/gpu/drm/loongson/lsdc_output.c -- 2.43.0
Re: [PATCH v3 00/19] Add Freescale i.MX8qxp Display Controller support
Hi, On 7/28/24 04:28, Dmitry Baryshkov wrote: On Sun, Jul 28, 2024 at 03:10:21AM GMT, Sui Jingfeng wrote: Hi, On 7/28/24 00:39, Dmitry Baryshkov wrote: Hi, This patch series aims to add Freescale i.MX8qxp Display Controller support. The controller is comprised of three main components that include a blit engine for 2D graphics accelerations, display controller for display output processing, as well as a command sequencer. Previous patch series attempts to do that can be found at: https://patchwork.freedesktop.org/series/84524/ This series addresses Maxime's comments on the previous one: a. Split the display controller into multiple internal devices. 1) List display engine, pixel engine, interrupt controller and more as the controller's child devices. 2) List display engine and pixel engine's processing units as their child devices. b. Add minimal feature support. Only support two display pipelines with primary planes with XR24 fb, backed by two fetchunits. No fetchunit dynamic allocation logic(to be done when necessary). c. Use drm_dev_{enter, exit}(). Since this series changes a lot comparing to the previous one, I choose to send it with a new patch series, not a new version. I'm sorry, I have started reviewing v2 without noticing that there is a v3 already. Let me summarize my comments: - You are using OF aliases. Are they documented and acked by DT maintainers? - I generally feel that the use of so many small devices to declare functional blocks is an abuse of the DT. Please consider creating _small_ units from the driver code directly rather than going throught the components. Well, I really don't think so. I don't agree. I have checked the DTSpec[1] before type, the spec isn't define how many is considered to be "many", and the spec isn't define to what extent is think to be "small" as well. Yeah. However _usually_ we are not defining DT devices for sub-device components. I guess, this depended on their hardware (i.MX8qxp) layout, reflecting exactly what their hardware's layout is perfectly valid. It also depend on if specific part of those sub-device will be re-visioned or not. If only a small part of the whole is re-versioned in the future, we can still re-using this same driver with slightly modify(update) the DTS. The point is to controll the granularity and forward compatibility. So at least such decisions ought to be described and explained in the cover letter. Agree, but I see 08/19 patch has a beautiful schematic. I have learned a lot when reading it. I can't see any abuse of the DT through this bulk series anyway. Comments below are not revelant to Ying's patch series itself. /**/ By the way, the last time that I have ever seen and feel abuse of the DT is the aux-bridge.c[1] and aux-hpd-bridge.c[2]. I strongly feel that those two *small* programs are abuses to the DT and possibily abuse to the auxiliary bus framework. 1) It's so *small* that it don't even have a hardware entity (physical device) to corresponding with. As far as I can see, all hardware units in this patch series are bigger than yours. Because your HPD bridge is basically a "virtual wire". An non-physical-exist wire hold reference to several device node, this is the most awful abuse to the DT I have ever seen. In other words, despite you want to solve some software problems, but then, you could put a device not in the DTS, and let the 'OF' system create a device for you. Just like what this series do. 2) I feel your HPD fake bridge driver abuse to the philosophy of auxiliary bus [3]. The document of auxiliary bus tell us that "These individual devices split from the core cannot live on the platform bus as they are not physical devices that are controlled by DT/ACPI" Which is nearly equivalent to say that devices that are controlled by DT/ACPI have better ways to enforce the control. When using auxiliary bus, we *generally* should not messed with DT. See golden examples[4][5]. At least, their code are able to run on X86, while the code you write just can't. [0] https://patchwork.freedesktop.org/patch/60/?series=135786&rev=3 [1] https://elixir.bootlin.com/linux/v6.10.2/source/drivers/gpu/drm/bridge/aux-bridge.c [2] https://elixir.bootlin.com/linux/v6.10.2/source/drivers/gpu/drm/bridge/aux-hpd-bridge.c [3] https://www.kernel.org/doc/html/latest/driver-api/auxiliary_bus.html [4] https://patchwork.freedesktop.org/series/136431/ [5] https://patchwork.freedesktop.org/series/134837/ Best regards Sui [1] https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.4 -- Best regards Sui
Re: [PATCH v3 00/19] Add Freescale i.MX8qxp Display Controller support
Hi, On 7/28/24 00:39, Dmitry Baryshkov wrote: Hi, This patch series aims to add Freescale i.MX8qxp Display Controller support. The controller is comprised of three main components that include a blit engine for 2D graphics accelerations, display controller for display output processing, as well as a command sequencer. Previous patch series attempts to do that can be found at: https://patchwork.freedesktop.org/series/84524/ This series addresses Maxime's comments on the previous one: a. Split the display controller into multiple internal devices. 1) List display engine, pixel engine, interrupt controller and more as the controller's child devices. 2) List display engine and pixel engine's processing units as their child devices. b. Add minimal feature support. Only support two display pipelines with primary planes with XR24 fb, backed by two fetchunits. No fetchunit dynamic allocation logic(to be done when necessary). c. Use drm_dev_{enter, exit}(). Since this series changes a lot comparing to the previous one, I choose to send it with a new patch series, not a new version. I'm sorry, I have started reviewing v2 without noticing that there is a v3 already. Let me summarize my comments: - You are using OF aliases. Are they documented and acked by DT maintainers? - I generally feel that the use of so many small devices to declare functional blocks is an abuse of the DT. Please consider creating _small_ units from the driver code directly rather than going throught the components. Well, I really don't think so. I don't agree. I have checked the DTSpec[1] before type, the spec isn't define how many is considered to be "many", and the spec isn't define to what extent is think to be "small" as well. [1] https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.4 -- Best regards Sui
Re: [PATCH v4] drm/loongson: Introduce component framework support
Hi, On 7/24/24 15:30, Markus Elfring wrote: In some display subsystems, the functionality of a PCIe device may too might be? … of the dirver is loaded, … driver? … its dependencies ready before it can register the service to userspace. user space? … submodule by creating platform devices manually during driverload time. driver load? … device as a DRM proxy, which will attach the common drm routines to our DRM? … While at it, also do some cleanups. I find such information suspicious. Is there any need to offer adjustments as separate update steps? https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/submitting-patches.rst?h=v6.10#n81 Thanks a lot for review, I will try to fix those problems in the future. Regards, Markus
[PATCH v4 1/1] drm/loongson: Introduce component framework support
In some display subsystems, the functionality of a PCIe device may too complex to be managed by a single driver. A split of the functionality into child devices can help to achieve better modularity. For example, KMS drivers who have dependencies on external modules will suffer from the deferral probe problem. The problem is that the DRM driver has to tear down everything when it receives '-EPROBE_DEFER', even though the independent part of the driver is not related to this error. Loongson Graphics are PCIe multifunctional devices, the GPU and the display controller are two distinct devices. Despite all hardware units that come along with PCIe are actually ready to be driven by the time of the dirver is loaded, drm/loongson driver still need to wait all of its dependencies ready before it can register the service to userspace. The idea is to migrate (offload) the dependency to submodules, creating submodule by creating platform devices manually during driverload time. Submodules are functional as agents, which will be responsible for attaching needed external modules for the main body of the whole KMS driver. Submodules are also responsible for returning '-EPROBE_DEFER' to the driver core if it needs to do so. Add a dummy GPU driver as a subcomponent, make LSDC PCI driver as a subcomponent as well. Introduce the component framework to bind those scatter software and/or hardware entities into one. Creating a platform device as a DRM proxy, which will attach the common drm routines to our hardware after all submodules bound. The proxy is the explicit executant working at the foreground, while the LSDC PCIe device is the master. Move DRM-related codes into the loongson_drm_master_bind(), move output related things into the submodule, because the output hardware units are relatively standalone IPs and the encoder/connector/transcoder may depend on external modules. While the display controller itself and GPIO-I2Cs have no dependency on external modules, therefore go with the LSDC PCIe device. Also assign GFX PLL, TTM memory manager part, and power management part to loongson drm proxy, as they do not belong to the LSDC itself. While at it, also do some cleanups. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/Makefile | 6 + drivers/gpu/drm/loongson/loonggpu_pci_drv.c | 163 drivers/gpu/drm/loongson/loonggpu_pci_drv.h | 35 ++ drivers/gpu/drm/loongson/loongson_device.c| 30 ++ drivers/gpu/drm/loongson/loongson_drv.c | 293 + drivers/gpu/drm/loongson/loongson_drv.h | 90 drivers/gpu/drm/loongson/loongson_module.c| 84 +++- drivers/gpu/drm/loongson/loongson_module.h| 32 ++ drivers/gpu/drm/loongson/lsdc_benchmark.c | 12 +- drivers/gpu/drm/loongson/lsdc_benchmark.h | 2 +- drivers/gpu/drm/loongson/lsdc_component.c | 159 +++ drivers/gpu/drm/loongson/lsdc_crtc.c | 4 +- drivers/gpu/drm/loongson/lsdc_debugfs.c | 44 +- drivers/gpu/drm/loongson/lsdc_drv.c | 394 ++ drivers/gpu/drm/loongson/lsdc_drv.h | 83 +--- drivers/gpu/drm/loongson/lsdc_gem.c | 44 +- drivers/gpu/drm/loongson/lsdc_gem.h | 19 +- drivers/gpu/drm/loongson/lsdc_gfxpll.c| 29 +- drivers/gpu/drm/loongson/lsdc_gfxpll.h| 3 +- drivers/gpu/drm/loongson/lsdc_i2c.c | 43 +- drivers/gpu/drm/loongson/lsdc_i2c.h | 12 +- drivers/gpu/drm/loongson/lsdc_output.c| 183 drivers/gpu/drm/loongson/lsdc_output.h| 36 +- drivers/gpu/drm/loongson/lsdc_output_7a1000.c | 14 +- drivers/gpu/drm/loongson/lsdc_output_7a2000.c | 20 +- drivers/gpu/drm/loongson/lsdc_plane.c | 4 +- drivers/gpu/drm/loongson/lsdc_ttm.c | 84 ++-- drivers/gpu/drm/loongson/lsdc_ttm.h | 4 +- 28 files changed, 1318 insertions(+), 608 deletions(-) create mode 100644 drivers/gpu/drm/loongson/loonggpu_pci_drv.c create mode 100644 drivers/gpu/drm/loongson/loonggpu_pci_drv.h create mode 100644 drivers/gpu/drm/loongson/loongson_drv.c create mode 100644 drivers/gpu/drm/loongson/loongson_drv.h create mode 100644 drivers/gpu/drm/loongson/lsdc_component.c create mode 100644 drivers/gpu/drm/loongson/lsdc_output.c diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile index 91e72bd900c1..1c496713baa9 100644 --- a/drivers/gpu/drm/loongson/Makefile +++ b/drivers/gpu/drm/loongson/Makefile @@ -2,6 +2,7 @@ loongson-y := \ lsdc_benchmark.o \ + lsdc_component.o \ lsdc_crtc.o \ lsdc_debugfs.o \ lsdc_drv.o \ @@ -9,6 +10,7 @@ loongson-y := \ lsdc_gfxpll.o \ lsdc_i2c.o \ lsdc_irq.o \ + lsdc_output.o \ lsdc_output_7a1000.o \ lsdc_output_7a2000.o \ lsdc_plane.o \ @@ -16,7 +18,11 @@ loongson-y := \ lsdc_probe.o \ lsdc_ttm.o +loongson-y += \ + loonggpu_pci
[PATCH v4 0/1] drm/loongson: Introduce component framework support
fedora :00:06.1]# cat benchmark Copy bo of 8100KiB 60 times from GTT to GTT in 48ms: 10125MB/s Copy bo of 8100KiB 60 times from GTT to VRAM in 104ms: 4673MB/s Copy bo of 8100KiB 60 times from VRAM to GTT in 13480ms: 36MB/s Also run IGT kms_flip and fbdev tests, no obvious problem found. Sui Jingfeng (1): drm/loongson: Introduce component framework support drivers/gpu/drm/loongson/Makefile | 6 + drivers/gpu/drm/loongson/loonggpu_pci_drv.c | 163 drivers/gpu/drm/loongson/loonggpu_pci_drv.h | 35 ++ drivers/gpu/drm/loongson/loongson_device.c| 30 ++ drivers/gpu/drm/loongson/loongson_drv.c | 293 + drivers/gpu/drm/loongson/loongson_drv.h | 90 drivers/gpu/drm/loongson/loongson_module.c| 84 +++- drivers/gpu/drm/loongson/loongson_module.h| 32 ++ drivers/gpu/drm/loongson/lsdc_benchmark.c | 12 +- drivers/gpu/drm/loongson/lsdc_benchmark.h | 2 +- drivers/gpu/drm/loongson/lsdc_component.c | 159 +++ drivers/gpu/drm/loongson/lsdc_crtc.c | 4 +- drivers/gpu/drm/loongson/lsdc_debugfs.c | 44 +- drivers/gpu/drm/loongson/lsdc_drv.c | 394 ++ drivers/gpu/drm/loongson/lsdc_drv.h | 83 +--- drivers/gpu/drm/loongson/lsdc_gem.c | 44 +- drivers/gpu/drm/loongson/lsdc_gem.h | 19 +- drivers/gpu/drm/loongson/lsdc_gfxpll.c| 29 +- drivers/gpu/drm/loongson/lsdc_gfxpll.h| 3 +- drivers/gpu/drm/loongson/lsdc_i2c.c | 43 +- drivers/gpu/drm/loongson/lsdc_i2c.h | 12 +- drivers/gpu/drm/loongson/lsdc_output.c| 183 drivers/gpu/drm/loongson/lsdc_output.h| 36 +- drivers/gpu/drm/loongson/lsdc_output_7a1000.c | 14 +- drivers/gpu/drm/loongson/lsdc_output_7a2000.c | 20 +- drivers/gpu/drm/loongson/lsdc_plane.c | 4 +- drivers/gpu/drm/loongson/lsdc_ttm.c | 84 ++-- drivers/gpu/drm/loongson/lsdc_ttm.h | 4 +- 28 files changed, 1318 insertions(+), 608 deletions(-) create mode 100644 drivers/gpu/drm/loongson/loonggpu_pci_drv.c create mode 100644 drivers/gpu/drm/loongson/loonggpu_pci_drv.h create mode 100644 drivers/gpu/drm/loongson/loongson_drv.c create mode 100644 drivers/gpu/drm/loongson/loongson_drv.h create mode 100644 drivers/gpu/drm/loongson/lsdc_component.c create mode 100644 drivers/gpu/drm/loongson/lsdc_output.c -- 2.43.0
[PATCH v3 0/1]drm/loongson: Introduce component framework support
:00:06.1 (ops lsdc_component_ops [loongson]) [ 10.891851] loongson :00:06.1: [drm] Total 2 outputs [ 10.910159] loongson :00:06.1: [drm] VRAM: 4096 pages ready [ 10.916131] loongson :00:06.1: [drm] GTT: 32768 pages ready [ 10.922391] [drm] Initialized loongson 1.0.0 for :00:06.1 on minor 0 [ 379.378990] Console: switching to colour frame buffer device 128x37 [ 379.385261] loongson :00:06.1: [drm] fb0: loongsondrmfb frame buffer device Sui Jingfeng (1): drm/loongson: Introduce component framework support drivers/gpu/drm/loongson/Makefile | 5 + drivers/gpu/drm/loongson/loonggpu_pci_drv.c | 156 drivers/gpu/drm/loongson/loonggpu_pci_drv.h | 33 +++ drivers/gpu/drm/loongson/loongson_device.c| 1 + drivers/gpu/drm/loongson/loongson_drv.c | 157 drivers/gpu/drm/loongson/loongson_module.c| 38 ++- drivers/gpu/drm/loongson/loongson_module.h| 17 ++ drivers/gpu/drm/loongson/lsdc_drv.c | 235 -- drivers/gpu/drm/loongson/lsdc_drv.h | 56 ++--- drivers/gpu/drm/loongson/lsdc_i2c.c | 43 ++-- drivers/gpu/drm/loongson/lsdc_i2c.h | 11 +- drivers/gpu/drm/loongson/lsdc_irq.c | 21 +- drivers/gpu/drm/loongson/lsdc_output.c| 208 drivers/gpu/drm/loongson/lsdc_output.h| 36 ++- drivers/gpu/drm/loongson/lsdc_output_7a1000.c | 7 +- drivers/gpu/drm/loongson/lsdc_output_7a2000.c | 17 +- drivers/gpu/drm/loongson/lsdc_ttm.c | 4 +- drivers/gpu/drm/loongson/lsdc_ttm.h | 2 +- 18 files changed, 832 insertions(+), 215 deletions(-) create mode 100644 drivers/gpu/drm/loongson/loonggpu_pci_drv.c create mode 100644 drivers/gpu/drm/loongson/loonggpu_pci_drv.h create mode 100644 drivers/gpu/drm/loongson/loongson_drv.c create mode 100644 drivers/gpu/drm/loongson/lsdc_output.c -- 2.43.0
[PATCH v3 1/1] drm/loongson: Introduce component framework support
In some display subsystems, the functionality of a PCIe device may too complex to be managed by a single driver. A split of the functionality into child devices can help to achieve better modularity. For example, KMS drivers who has a dependency on external modules will suffer from the deferral probe problem of those modules. The problem is that the DRM driver has to tear down everything when it receives '-EPROBE_DEFER', even though the independent part of the entire driver is not very closely related. For Loongson Graphics, the GPU and the display controller are two distinct PCIe devices. Despite hardware units that come along with PCIe are actually all ready to be driven, DRM driver still need to wait all of its dependency ready before it can register the service to userspace. The idea is to migrate(offload) the dependency to submodule, creating submodule by creating platform device manually. Such submodules are functional as agents, which will be responsible for attaching needed external modules for the main body of entire KMS drive. It's also for better modularity, as output hardware units are relatively standalone IPs. Add dummy GPU driver as a subcomponent, use the component framework to bind those scatter software/hardware entities into one. Move DRM-related codes into the loongson_drm_master_bind() function. Move output-related things into the submodule, since the encoder/connector/ transcoder are sometimes depend on external module. The display controller and GPIO-I2Cs go with the PCIe master, as they have no dependency on external modules. Make lsdc PCI driver as a subcomponent as well, creating a platform device as proxy. The proxy will responsible attach the common drm routines to our hardware, which keep the whole drm driver balanced (and fair) enough for both the DC and GPU. The lsdc PCI device is the haidden(implicit) parent, while the proxy is the explicit master, which deal with drm related affairs for its parent. The master won't bound until all submodules are ready, submodules are responsible to return '-EPROBE_DEFER' back to the driver core if it needs to do so. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/Makefile | 5 + drivers/gpu/drm/loongson/loonggpu_pci_drv.c | 156 drivers/gpu/drm/loongson/loonggpu_pci_drv.h | 33 +++ drivers/gpu/drm/loongson/loongson_device.c| 1 + drivers/gpu/drm/loongson/loongson_drv.c | 157 drivers/gpu/drm/loongson/loongson_module.c| 38 ++- drivers/gpu/drm/loongson/loongson_module.h| 17 ++ drivers/gpu/drm/loongson/lsdc_drv.c | 235 -- drivers/gpu/drm/loongson/lsdc_drv.h | 56 ++--- drivers/gpu/drm/loongson/lsdc_i2c.c | 43 ++-- drivers/gpu/drm/loongson/lsdc_i2c.h | 11 +- drivers/gpu/drm/loongson/lsdc_irq.c | 21 +- drivers/gpu/drm/loongson/lsdc_output.c| 208 drivers/gpu/drm/loongson/lsdc_output.h| 36 ++- drivers/gpu/drm/loongson/lsdc_output_7a1000.c | 7 +- drivers/gpu/drm/loongson/lsdc_output_7a2000.c | 17 +- drivers/gpu/drm/loongson/lsdc_ttm.c | 4 +- drivers/gpu/drm/loongson/lsdc_ttm.h | 2 +- 18 files changed, 832 insertions(+), 215 deletions(-) create mode 100644 drivers/gpu/drm/loongson/loonggpu_pci_drv.c create mode 100644 drivers/gpu/drm/loongson/loonggpu_pci_drv.h create mode 100644 drivers/gpu/drm/loongson/loongson_drv.c create mode 100644 drivers/gpu/drm/loongson/lsdc_output.c diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile index 91e72bd900c1..cdc60ec975e4 100644 --- a/drivers/gpu/drm/loongson/Makefile +++ b/drivers/gpu/drm/loongson/Makefile @@ -9,6 +9,7 @@ loongson-y := \ lsdc_gfxpll.o \ lsdc_i2c.o \ lsdc_irq.o \ + lsdc_output.o \ lsdc_output_7a1000.o \ lsdc_output_7a2000.o \ lsdc_plane.o \ @@ -16,7 +17,11 @@ loongson-y := \ lsdc_probe.o \ lsdc_ttm.o +loongson-y += \ + loonggpu_pci_drv.o + loongson-y += loongson_device.o \ + loongson_drv.o \ loongson_module.o obj-$(CONFIG_DRM_LOONGSON) += loongson.o diff --git a/drivers/gpu/drm/loongson/loonggpu_pci_drv.c b/drivers/gpu/drm/loongson/loonggpu_pci_drv.c new file mode 100644 index ..a34c213171e0 --- /dev/null +++ b/drivers/gpu/drm/loongson/loonggpu_pci_drv.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Authors: + * Sui Jingfeng + */ + +#include +#include + +#include +#include + +#include "loongson_module.h" +#include "loonggpu_pci_drv.h" + +static int loonggpu_get_version(struct loonggpu_device *gpu) +{ + u32 hw_info = loong_rreg32(gpu, 0x8C); + u8 host_id; + u8 revision; + + /* LoongGPU hardware info */ + gpu->ver_major = (hw_info >> 8) & 0x0F; + gpu->ver_minor = (hw_info & 0xF0) >
Re: [etnaviv-next v14 0/8] drm/etnaviv: Add driver wrapper for vivante GPUs attached on PCI(e) device
Hi, On 6/25/24 11:18, Icenowy Zheng wrote: 在 2024-05-20星期一的 00:53 +0800,Sui Jingfeng写道: drm/etnaviv use the component framework to bind multiple GPU cores to a virtual master, the virtual master is manually create during driver load time. This works well for various SoCs, yet there are some PCIe card has the vivante GPU cores integrated. The driver lacks the support for PCIe devices currently. Adds PCIe driver wrapper on the top of what drm/etnaviv already has, the component framework is still being used to bind subdevices, even though there is only one GPU core. But the process is going to be reversed, we create virtual platform device for each of the vivante GPU IP core shipped by the PCIe master. The PCIe master is real, bind all the virtual child to the master with component framework. v6: * Fix build issue on system without CONFIG_PCI enabled v7: * Add a separate patch for the platform driver rearrangement (Bjorn) * Switch to runtime check if the GPU is dma coherent or not (Lucas) * Add ETNAVIV_PARAM_GPU_COHERENT to allow userspace to query (Lucas) * Remove etnaviv_gpu.no_clk member (Lucas) * Fix Various typos and coding style fixed (Bjorn) v8: * Fix typos and remove unnecessary header included (Bjorn). * Add a dedicated function to create the virtual master platform device. v9: * Use PCI_VDEVICE() macro (Bjorn) * Add trivial stubs for the PCI driver (Bjorn) * Remove a redundant dev_err() usage (Bjorn) * Clean up etnaviv_pdev_probe() with etnaviv_of_first_available_node() v10: * Add one more cleanup patch * Resolve the conflict with a patch from Rob * Make the dummy PCI stub inlined * Print only if the platform is dma-coherrent V11: * Drop unnecessary changes (Lucas) * Tweak according to other reviews of v10. V12: * Create a virtual platform device for the subcomponent GPU cores * Bind all subordinate GPU cores to the real PCI master via component. V13: * Drop the non-component code path, always use the component framework to bind subcomponent GPU core. Even though there is only one core. * Defer the irq handler register. * Rebase and improve the commit message V14: * Rebase onto etnaviv-next and improve commit message. Tested with JD9230P GPU and LingJiu GP102 GPU. BTW how should VRAM and displayed related parts be handled on these dGPUs? Emm, I can only say I have no ideas. Thanks for Christian's tested-by, but I'm a bit worry about if etnaviv folks really like(or need) this. In the past, we started to contribute before we know the policy/framework very well. I have to managed to make things work before knowing the full picture. We developing drivers in a rather rapid way and rather wild. Sometime, we do it just for fun. As the card don't has a usable driver, we want do something and we do have already learned the framework and knowledge. But now as we know a bit more, I actually don't intend to brings concerns to other people. So only first 6 patch (or only part of them) are requested to be merged, patch 0007 or patch 0008 can just leave it there to be reviewed a bit longer if something is unsure. Its totally up to etnaviv folks, I don't mind. Thanks for the nice project. -- Best regards Sui
Re: [PATCH v3] software node: Implement device_get_match_data fwnode callback
Hi, On 6/23/24 03:29, Dmitry Torokhov wrote: In case of non-OF match (which includes the case where you use software nodes) the match data is coming from matching spi_device_id entry in the driver. We don't care about much how it is probed now, rather, after the driver probed by a non-OF way, how does the additional devices properties can be get? Say: 1) "device_property_read_u32(dev, "rotation", &rotation);" and 2) "!device_property_read_string(dev, "pervasive,thermal-zone", &thermal_zone))" For those spi/i2c/platform devices, what we argues are that those drivers really should just depend on "OF" before we have a reliable fwnode API backend to redirect to. They are working fine without such restriction now, You still *NOT* answer where the additional devices properties[1][2] can be acquire. [1] device_property_read_u32(dev, "rotation", &rotation) [2] device_property_read_string(dev, "pervasive,thermal-zone", &thermal_zone)) so I see absolutely no reason imposing this restriction. The reason is rigorous. You are acclaiming that works by hardcode or by ignoring the flaws is fine, then all driver are working fine by *your* standard. Your personal standard has nothing to do with this patch. Where the additional device_property_read_() calls redirect to? What if users want to invoke more device_property_read_() function? They are being directed to first the primary FW node instance, which may be either OF, ACPI, or SW node, and then, if property is not present there, to the secondary FW node, which can be either again. What I'm asking is, on the non-OF and no-ACPI cases, where's those device_property_read_xxx() calls can be directed to? At no point ->device_get_match_data() callback in involved in this process. The patch is written for people who need it, not for people who don't. It will be involved if the device is associated with software node. Its for fwnode API user to get a consistent experience, that is to get a matching data without introduce extra/duplicated match mechanism. The patch is focus on fixing the undefined behavior, is discussing the correct way to consolidate the fwnode API. Its not going to discuss how does the those *old" and/or how does those non-fwnode systems works. Its NOT discussing how does the driver itself can be probed, a driver can be probed multiple way and is another question. Being probed and extract matching data can two different thing and is perfectly valid. Your problem is that you are not fully understand what other people does before you rush into the discussion. You are putting restrictions onto other people, while leaving the problem itself there unsolved. Its not a place to express your personal value or you personal status, such as, you are "ready" or "not ready" for something. Or persuading somebody should get used to what or teaching people to talks with a whatever tone like a God. None of those junk words are technical, I can not see constructive ideas. Thanks.
Re: [PATCH v3] software node: Implement device_get_match_data fwnode callback
Hi, On 6/22/24 03:58, Dmitry Torokhov wrote: Hi Sui, On Sun, Apr 28, 2024 at 04:36:50AM +0800, Sui Jingfeng wrote: Because the software node backend of the fwnode API framework lacks an implementation for the .device_get_match_data function callback. This makes it difficult to use(and/or test) a few drivers that originates from DT world on the non-DT platform. Implement the .device_get_match_data fwnode callback, which helps to keep the three backends of the fwnode API aligned as much as possible. This is also a fundamental step to make a few drivers OF-independent truely possible. Device drivers or platform setup codes are expected to provide a software node string property, named as "compatible". At this moment, the value of this string property is being used to match against the compatible entries in the of_device_id table. It can be extended in the future though. I am sorry, but this is not really correct. I fine if the maintainers of fwnode API want to reject this, but got rejected is not really equals to "not correct". Software nodes are used to augment missing or incomplete parameters, but are never primary objects in the matching process. Sometimes "compatible" property is used with software nodes, but it does not participate in the matching process. > There are several ways for various buses to match a device and a driver, but none of them operate on software nodes. It's not participate in the matching process in the *past*, but what we present is something *new*. I fine if you adhere to *old* and/or *subsystem-dependent* approach, but there really no need to persuade other people to follow your "old" idea. Consider for example how devices on SPI bus are matched (see drivers/spi/spi.c::spi_match_device()): This only make the driver be able to probed in a non-DT way, but it doesn't tell how does the *additional device properties* can be get. This is the key point. 1. OF/device tree based match. It *requires* the device to have dev->of_node which is coming from a DTB. It does not work on software nodes. In case of match the match data should come from of_device_id entry. 2. ACPI-based match. The match is done based either on OF-compatible data (which includes "compatible" property) in _DSD (if driver supports OF-based matching), or based on HID/CID data. In the latter case the match data is coming from acpi_device_id entry. 3. Name-based match, typically used for board-instantiated devices. In this case match is done by comparing device name under which it was instantiated against names listed in the drivers id_table. The match data is coming from spi_device_id entry. The statements here sound right, but it's useless. Because the problems isn't solved yet, nor does you words point out a practical approach. Similar matching processes are implemented for i2c and platform buses, as well as others. Your patch is effectively hijacks the #3 matching process and substitutes the bus-specific match data (from spi_device_id/i2c_device_id/etc) with OF data. This is not expected and Please stop *contaminating* other people's patch, if you have better idea you can posting it. My patch open a new door, and there do have programmer in requesting(need) this in the past. while we may want this in a long term (so we can eventually remove these bus-specific device ids and only have ACPI/OF ones) I do not think we are ready for this yet. At the very least this needs to be very clearly documented. This is your *personal* wants, if you want to remove something, just do it. Keep quiet if you are not ready. Exposing your concerns doesn't help to solve any problems. Or, if you want it to be clear, you can contribute to Documentation/ gpu/todo.rst. Other peoples help you to become clear there, thanks. Please note that we are talking about the completeness of the fwnode APIs, what's you say above has nothing to do the device fwnode APIs. Hence, is not revelant to my patch, again is out of scope. Fixes: ffb42e64561e ("drm/tiny/repaper: Make driver OF-independent") Fixes: 5703d6ae9573 ("drm/tiny/st7735r: Make driver OF-independent") As other people mentioned this patch does not fix the aforementioned commits because they are not broken. You still not really understand what other people does, I'm not saying it broken. I'm talking about the completeness. In case of non-OF match (which includes the case where you use software nodes) the match data is coming from matching spi_device_id entry in the driver. We don't care about much how it is probed now, rather, after the driver probed by a non-OF way, how does the additional devices properties can be get? Say: 1) "device_property_read_u32(dev, "rotation", &rotation);" and 2) "!device_property_read_string(dev, "pervasive,thermal-zone", &thermal_zone))&qu
Re: [PATCH v8 3/3] drm/mediatek: Implement OF graphs support for display paths
Hi, On 6/18/24 18:17, AngeloGioacchino Del Regno wrote: It is impossible to add each and every possible DDP path combination for each and every possible combination of SoC and board: right now, this driver hardcodes configuration for 10 SoCs and this is going to grow larger and larger, and with new hacks like the introduction of mtk_drm_route which is anyway not enough for all final routes as the DSI cannot be connected to MERGE if it's not a dual-DSI, or enabling DSC preventively doesn't work if the display doesn't support it, or others. Since practically all display IPs in MediaTek SoCs support being interconnected with different instances of other, or the same, IPs or with different IPs and in different combinations, the final DDP pipeline is effectively a board specific configuration. Implement OF graphs support to the mediatek-drm drivers, allowing to stop hardcoding the paths, and preventing this driver to get a huge amount of arrays for each board and SoC combination, also paving the way to share the same mtk_mmsys_driver_data between multiple SoCs, making it more straightforward to add support for new chips. Reviewed-by: Alexandre Mergnat Tested-by: Alexandre Mergnat Signed-off-by: AngeloGioacchino Del Regno Acked-by: Sui Jingfeng
Re: [v4,1/2] drm/bridge: sii902x: Fix mode_valid hook
Hi, Jayesh On 5/31/24 21:33, Sam Ravnborg wrote: Hi Jayesh, + static const struct drm_bridge_funcs sii902x_bridge_funcs = { .attach = sii902x_bridge_attach, .mode_set = sii902x_bridge_mode_set, @@ -516,6 +529,7 @@ static const struct drm_bridge_funcs sii902x_bridge_funcs = { .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_input_bus_fmts = sii902x_bridge_atomic_get_input_bus_fmts, .atomic_check = sii902x_bridge_atomic_check, + .mode_valid = sii902x_bridge_mode_valid, As you have the possibility to test the driver, it would be nice with a follow-up patch that replaces the use of enable() / disable() with the atomic counterparts. enable() / disable() are deprecated, so it is nice to reduce their use. I agree with Sam. Please using atomic uniformally with a follow-up patch, the mixed using of atomic API and non atomic API is a little bit confusing IMO. Sam
Re: [v4,1/2] drm/bridge: sii902x: Fix mode_valid hook
Hi, On 5/30/24 17:29, Jayesh Choudhary wrote: Currently, mode_valid hook returns all mode as valid and it is defined only in drm_connector_helper_funcs. With the introduction of 'DRM_BRIDGE_ATTACH_NO_CONNECTOR', connector is not initialized in bridge_attach call for cases when the encoder has this flag enabled. So move the mode_valid hook to drm_bridge_funcs with proper clock checks for maximum and minimum pixel clock supported by the bridge. Signed-off-by: Jayesh Choudhary Acked-by: Sui Jingfeng --- drivers/gpu/drm/bridge/sii902x.c | 32 +++- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index 2fbeda9025bf..6a6055a4ccf9 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -163,6 +163,14 @@ #define SII902X_AUDIO_PORT_INDEX 3 +/* + * The maximum resolution supported by the HDMI bridge is 1080p@60Hz + * and 1920x1200 requiring a pixel clock of 165MHz and the minimum + * resolution supported is 480p@60Hz requiring a pixel clock of 25MHz + */ +#define SII902X_MIN_PIXEL_CLOCK_KHZ25000 +#define SII902X_MAX_PIXEL_CLOCK_KHZ165000 + This bridge can drive 2560x1080@75Hz monitor(LG 34BL650), the pixel clock can up to 181250 kHz. I remember that I have tested the native mode with LS2K1000 SoC, and it do works in practice. And there are also has 320x240 panels, maybe it's also usuable with this HDMI transmitter Well, the datasheet mentioned that it supports up to 165 MHz dual-edge and single-edge modes. So I'm not against your patch, just mention it to let you know. struct sii902x { struct i2c_client *i2c; struct regmap *regmap; @@ -310,17 +318,8 @@ static int sii902x_get_modes(struct drm_connector *connector) return num; } -static enum drm_mode_status sii902x_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - /* TODO: check mode */ - - return MODE_OK; -} - static const struct drm_connector_helper_funcs sii902x_connector_helper_funcs = { .get_modes = sii902x_get_modes, - .mode_valid = sii902x_mode_valid, }; static void sii902x_bridge_disable(struct drm_bridge *bridge) @@ -504,6 +503,20 @@ static int sii902x_bridge_atomic_check(struct drm_bridge *bridge, return 0; } +static enum drm_mode_status +sii902x_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + if (mode->clock < SII902X_MIN_PIXEL_CLOCK_KHZ) + return MODE_CLOCK_LOW; + + if (mode->clock > SII902X_MAX_PIXEL_CLOCK_KHZ) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + static const struct drm_bridge_funcs sii902x_bridge_funcs = { .attach = sii902x_bridge_attach, .mode_set = sii902x_bridge_mode_set, @@ -516,6 +529,7 @@ static const struct drm_bridge_funcs sii902x_bridge_funcs = { .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_input_bus_fmts = sii902x_bridge_atomic_get_input_bus_fmts, .atomic_check = sii902x_bridge_atomic_check, + .mode_valid = sii902x_bridge_mode_valid, }; static int sii902x_mute(struct sii902x *sii902x, bool mute) -- Best regards Sui
Re: [PATCH v4 0/2] Add mode_valid and atomic_check hooks for sii902x bridge
Hi, On 5/30/24 17:29, Jayesh Choudhary wrote: Move the mode_valid hook to drm_bridge_funcs structure to take care of the case when the encoder attaches the bridge chain with the DRM_BRIDGE_ATTACH_NO_CONNECTOR flag in which case, the connector is not initialized in the bridge's attach call and mode_valid is not called. Good catch, as modes being supported is actually a capability of the bridge itself. The move make the driver reflect the hardware more.
Re: MAINTAINERS: drm: Drop sam as panel reviewer
Hi, On 5/31/24 05:14, Sam Ravnborg wrote: Drop myself as reviewer of panel patches, to reflect the reality. We lost one kindness reviewer for drivers of panel, unhappy! Not sure if it is proper to give you a NAK here. :( Best regards, Sui Signed-off-by: Sam Ravnborg Cc: Neil Armstrong --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index ac285040578e..38978699bef5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7567,7 +7567,6 @@ F:include/drm/gpu_scheduler.h DRM PANEL DRIVERS M:Neil Armstrong R:Jessica Zhang -R: Sam Ravnborg L:dri-devel@lists.freedesktop.org S:Maintained T:git https://gitlab.freedesktop.org/drm/misc/kernel.git
Re: [resend,v2] drm: renesas: shmobile: Call drm_helper_force_disable_all() at shutdown time
Hi, On 2024/5/29 20:16, Geert Uytterhoeven wrote: From: Douglas Anderson Based on grepping through the source code, this driver appears to be missing a call to drm_atomic_helper_shutdown() at system shutdown time. This is important because drm_helper_force_disable_all() will cause panels to get disabled cleanly which may be important for their power sequencing. Future changes will remove any custom powering off in individual panel drivers so the DRM drivers need to start getting this right. The fact that we should call drm_atomic_helper_shutdown() in the case of OS shutdown comes straight out of the kernel doc "driver instance overview" in drm_drv.c. True, looks safer. Suggested-by: Maxime Ripard Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20230901164111.RFT.15.Iaf638a1d4c8b3c307a6192efabb4cbb06b195f15@changeid [geert: s/drm_helper_force_disable_all/drm_atomic_helper_shutdown/] [geert: shmob_drm_remove() already calls drm_atomic_helper_shutdown] Signed-off-by: Geert Uytterhoeven Reviewed-by: Laurent Pinchart Reviewed-by: Sui Jingfeng Best regards, Sui
Re: drm: renesas: rcar-du: Add drm_panic support for non-vsp
Hi, On 5/27/24 21:35, Geert Uytterhoeven wrote: Add support for the drm_panic module for DU variants not using the VSP-compositor, to display a message on the screen when a kernel panic occurs. Signed-off-by: Geert Uytterhoeven Reviewed-by: Jocelyn Falempe After all concerns resolved: Acked-by: Sui Jingfeng --- Tested on Koelsch (R-Car M2-W). Support for DU variants using the VSP-compositor is more convoluted, and left to the DU experts. --- drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c | 14 -- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c index e445fac8e0b46c21..c546ab0805d656f6 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c @@ -680,6 +680,12 @@ static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = { .atomic_update = rcar_du_plane_atomic_update, }; +static const struct drm_plane_helper_funcs rcar_du_primary_plane_helper_funcs = { + .atomic_check = rcar_du_plane_atomic_check, + .atomic_update = rcar_du_plane_atomic_update, + .get_scanout_buffer = drm_fb_dma_get_scanout_buffer, +}; + static struct drm_plane_state * rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane) { @@ -812,8 +818,12 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp) if (ret < 0) return ret; - drm_plane_helper_add(&plane->plane, -&rcar_du_plane_helper_funcs); + if (type == DRM_PLANE_TYPE_PRIMARY) + drm_plane_helper_add(&plane->plane, + &rcar_du_primary_plane_helper_funcs); + else + drm_plane_helper_add(&plane->plane, +&rcar_du_plane_helper_funcs); Maybe we could do some untangle, but this is not a strong requirement. Thanks. Best regards Sui drm_plane_create_alpha_property(&plane->plane);
Re: drm: renesas: shmobile: Add drm_panic support
Hi, On 5/27/24 21:34, Geert Uytterhoeven wrote: Add support for the drm_panic module, which displays a message on the screen when a kernel panic occurs. Signed-off-by: Geert Uytterhoeven Reviewed-by: Jocelyn Falempe Acked-by: Sui Jingfeng --- Tested on Armadillo-800-EVA. --- drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c | 14 +- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c index 07ad17d24294d5e6..9d166ab2af8bd231 100644 --- a/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c @@ -273,6 +273,13 @@ static const struct drm_plane_helper_funcs shmob_drm_plane_helper_funcs = { .atomic_disable = shmob_drm_plane_atomic_disable, }; +static const struct drm_plane_helper_funcs shmob_drm_primary_plane_helper_funcs = { + .atomic_check = shmob_drm_plane_atomic_check, + .atomic_update = shmob_drm_plane_atomic_update, + .atomic_disable = shmob_drm_plane_atomic_disable, + .get_scanout_buffer = drm_fb_dma_get_scanout_buffer, +}; + static const struct drm_plane_funcs shmob_drm_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, @@ -310,7 +317,12 @@ struct drm_plane *shmob_drm_plane_create(struct shmob_drm_device *sdev, Maybe a shmob_drm_plane_create_primary_plane() plus a shmob_drm_plane_create_overlay(). I remember Thomas told this way or something similiar, call untangle. splane->index = index; - drm_plane_helper_add(&splane->base, &shmob_drm_plane_helper_funcs); + if (type == DRM_PLANE_TYPE_PRIMARY) + drm_plane_helper_add(&splane->base, +&shmob_drm_primary_plane_helper_funcs); + else + drm_plane_helper_add(&splane->base, +&shmob_drm_plane_helper_funcs); return &splane->base; } Anyway, it looks good to me. Best regards Sui
Re: drm/tests: Add a missing Kconfig select
Hi, On 5/29/24 17:19, Thomas Hellström wrote: Fix the following warning: WARNING: unmet direct dependencies detected for DRM_DISPLAY_HDMI_STATE_HELPER Depends on [n]: HAS_IOMEM [=y] && DRM [=y] && DRM_DISPLAY_HELPER [=y] && DRM_DISPLAY_HDMI_HELPER [=n] Selected by [y]: - DRM_KUNIT_TEST [=y] && HAS_IOMEM [=y] && DRM [=y] && KUNIT [=y] && MMU [=y] Signed-off-by: Thomas Hellström Fixes: 54cb39e2293b ("drm/connector: hdmi: Create an HDMI sub-state") Cc: Maxime Ripard Cc: dri-devel@lists.freedesktop.org Acked-by: Sui Jingfeng
Re: [v15,06/29] drm/tests: Add output bpc tests
Hi, On 5/27/24 21:57, Maxime Ripard wrote: Now that we're tracking the output bpc count in the connector state, let's add a few tests to make sure it works as expected. Reviewed-by: Dave Stevenson Reviewed-by: Dmitry Baryshkov Signed-off-by: Maxime Ripard Tested-by: Sui Jingfeng
Re: [v15,06/29] drm/tests: Add output bpc tests
0x02, 0x03, 0x1b, 0x81, + 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x6d, 0x03, 0x0c, + 0x00, 0x12, 0x34, 0x00, 0x28, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xd0 +}; + +#endif // DRM_KUNIT_EDID_H_ -- Best regards Sui Jingfeng
Re: [PATCH v6 02/10] drm/bridge: Set firmware node of drm_bridge instances automatically
Hi, On 5/27/24 07:33, kernel test robot wrote: Hi Sui, kernel test robot noticed the following build errors: [auto build test ERROR on drm-exynos/exynos-drm-next] [also build test ERROR on linus/master v6.10-rc1 next-20240523] [cannot apply to shawnguo/for-next] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Sui-Jingfeng/drm-bridge-Allow-using-fwnode-APIs-to-get-the-next-bridge/20240527-042402 base: https://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git exynos-drm-next patch link: https://lore.kernel.org/r/20240526202115.129049-3-sui.jingfeng%40linux.dev patch subject: [PATCH v6 02/10] drm/bridge: Set firmware node of drm_bridge instances automatically config: arm-defconfig (https://download.01.org/0day-ci/archive/20240527/202405270622.vdmbp9fr-...@intel.com/config) compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240527/202405270622.vdmbp9fr-...@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot | Closes: https://lore.kernel.org/oe-kbuild-all/202405270622.vdmbp9fr-...@intel.com/ All errors (new ones prefixed by >>): drivers/gpu/drm/omapdrm/dss/hdmi5.c:487:49: error: expected identifier drm_bridge_add(&hdmi->bridge, &hdmi->pdev->dev.); ^ 1 error generated. vim +487 drivers/gpu/drm/omapdrm/dss/hdmi5.c 480 481 static void hdmi5_bridge_init(struct omap_hdmi *hdmi) 482 { 483 hdmi->bridge.funcs = &hdmi5_bridge_funcs; 484 hdmi->bridge.ops = DRM_BRIDGE_OP_EDID; 485 hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; 486 > 487 drm_bridge_add(&hdmi->bridge, &hdmi->pdev->dev.); 488 } 489 Sorry, my bad. I have do compile test on ARM64 before posting. checkpatch.pl report a style problem, then I manually modify this patch, accidentally add the tail '.' character. Will be fixed at the next version. -- Best regards Sui
Re: [PATCH v6 02/10] drm/bridge: Set firmware node of drm_bridge instances automatically
Hi, On 5/27/24 05:19, Dmitry Baryshkov wrote: On Mon, May 27, 2024 at 04:21:07AM +0800, Sui Jingfeng wrote: Normally, the drm_bridge::of_node won't be used by bridge driver instances themselves. Rather, it is mainly used by other modules to find associated drm bridge drvier. Therefore, adding a drm bridge to the global bridge list and setting 'of_node' field of a drm bridge share the same goal. Both are for finding purpose, therefore better to group them to one function. Update the drm_bridge_add() interface and implementation to achieve such goal atomically, new implementation will fetch the device node from the backing device of the drm bridge driver automatically. For the majority cases, which is one device backing one drm bridge driver, this model works well. Drivers still can set it manually by passing NULL if this model doesn't fit. While at it, Add a 'struct device *' pointer to the drm_bridge structure. As it already being passed in by both of drm_bridge_add() and devm_drm_bridge_add(). A lot of driver instances has already added it into their derived structure, promote it into drm_bridge core helps to reduce a batch of boilerplates. Signed-off-by: Sui Jingfeng --- [trimmed] @@ -231,7 +243,7 @@ static void drm_bridge_remove_void(void *bridge) */ int devm_drm_bridge_add(struct device *dev, struct drm_bridge *bridge) { - drm_bridge_add(bridge); + drm_bridge_add(bridge, dev); return devm_add_action_or_reset(dev, drm_bridge_remove_void, bridge); This breaks aux-hpd-bridge, which gets of_node as an external pointer rather than dev->of_node. Yes, you are right. I forget to modify that driver. My bad, will be fixed at the next version. -- Best regards Sui
[PATCH v6 10/10] drm/bridge: ch7033: Switch to use fwnode based APIs
Use the freshly created helper to replace the use of DT-dependent APIs, also print error log if the fwnode graph is not complete which is benefit to debug. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/bridge/chrontel-ch7033.c | 12 +--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/chrontel-ch7033.c b/drivers/gpu/drm/bridge/chrontel-ch7033.c index c6374440af7f..35dd2e6ba6c0 100644 --- a/drivers/gpu/drm/bridge/chrontel-ch7033.c +++ b/drivers/gpu/drm/bridge/chrontel-ch7033.c @@ -531,6 +531,7 @@ static const struct regmap_config ch7033_regmap_config = { static int ch7033_probe(struct i2c_client *client) { struct device *dev = &client->dev; + struct fwnode_handle *fwnode = dev_fwnode(dev); struct ch7033_priv *priv; unsigned int val; int ret; @@ -541,10 +542,15 @@ static int ch7033_probe(struct i2c_client *client) dev_set_drvdata(dev, priv); - ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, NULL, - &priv->next_bridge); - if (ret) + priv->next_bridge = drm_bridge_find_next_bridge_by_fwnode(fwnode, 1); + if (IS_ERR(priv->next_bridge)) { + ret = PTR_ERR(priv->next_bridge); + dev_err(dev, "Error in founding the next bridge: %d\n", ret); return ret; + } else if (!priv->next_bridge) { + dev_dbg(dev, "Next bridge not found, deferring probe\n"); + return -EPROBE_DEFER; + } priv->regmap = devm_regmap_init_i2c(client, &ch7033_regmap_config); if (IS_ERR(priv->regmap)) { -- 2.34.1
[PATCH v6 08/10] drm/bridge: tfp410: Use fwnode APIs to acquire device properties
Make this driver less DT-dependent by calling the newly created helpers, also switch to use fwnode APIs to acquire additional device properties. No functional changes for DT-based systems. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/bridge/ti-tfp410.c | 39 +++--- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index 04a341133488..a1fae5e9dafd 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -261,8 +261,9 @@ static const struct drm_bridge_timings tfp410_default_timings = { static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c) { + struct fwnode_handle *fwnode = dev_fwnode(dvi->dev); struct drm_bridge_timings *timings = &dvi->timings; - struct device_node *ep; + struct fwnode_handle *ep; u32 pclk_sample = 0; u32 bus_width = 24; u32 deskew = 0; @@ -283,14 +284,14 @@ static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c) * and EDGE pins. They are specified in DT through endpoint properties * and vendor-specific properties. */ - ep = of_graph_get_endpoint_by_regs(dvi->dev->of_node, 0, 0); + ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0); if (!ep) return -EINVAL; /* Get the sampling edge from the endpoint. */ - of_property_read_u32(ep, "pclk-sample", &pclk_sample); - of_property_read_u32(ep, "bus-width", &bus_width); - of_node_put(ep); + fwnode_property_read_u32(ep, "pclk-sample", &pclk_sample); + fwnode_property_read_u32(ep, "bus-width", &bus_width); + fwnode_handle_put(ep); timings->input_bus_flags = DRM_BUS_FLAG_DE_HIGH; @@ -319,7 +320,7 @@ static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c) } /* Get the setup and hold time from vendor-specific properties. */ - of_property_read_u32(dvi->dev->of_node, "ti,deskew", &deskew); + fwnode_property_read_u32(fwnode, "ti,deskew", &deskew); if (deskew > 7) return -EINVAL; @@ -331,12 +332,12 @@ static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c) static int tfp410_init(struct device *dev, bool i2c) { - struct device_node *node; + struct fwnode_handle *fwnode = dev_fwnode(dev); struct tfp410 *dvi; int ret; - if (!dev->of_node) { - dev_err(dev, "device-tree data is missing\n"); + if (!fwnode) { + dev_err(dev, "firmware data is missing\n"); return -ENXIO; } @@ -356,15 +357,15 @@ static int tfp410_init(struct device *dev, bool i2c) return ret; /* Get the next bridge, connected to port@1. */ - node = of_graph_get_remote_node(dev->of_node, 1, -1); - if (!node) - return -ENODEV; - - dvi->next_bridge = of_drm_find_bridge(node); - of_node_put(node); - - if (!dvi->next_bridge) + dvi->next_bridge = drm_bridge_find_next_bridge_by_fwnode(fwnode, 1); + if (IS_ERR(dvi->next_bridge)) { + ret = PTR_ERR(dvi->next_bridge); + dev_err(dev, "Error in founding the next bridge: %d\n", ret); + return ret; + } else if (!dvi->next_bridge) { + dev_dbg(dev, "Next bridge not found, deferring probe\n"); return -EPROBE_DEFER; + } /* Get the powerdown GPIO. */ dvi->powerdown = devm_gpiod_get_optional(dev, "powerdown", @@ -416,10 +417,10 @@ static struct platform_driver tfp410_platform_driver = { /* There is currently no i2c functionality. */ static int tfp410_i2c_probe(struct i2c_client *client) { + struct fwnode_handle *fwnode = dev_fwnode(&client->dev); int reg; - if (!client->dev.of_node || - of_property_read_u32(client->dev.of_node, "reg", ®)) { + if (!fwnode || fwnode_property_read_u32(fwnode, "reg", ®)) { dev_err(&client->dev, "Can't get i2c reg property from device-tree\n"); return -ENXIO; -- 2.34.1
[PATCH v6 09/10] drm/bridge: sii9234: Use fwnode APIs to abstract DT dependent API away
Switch to use the freshly created drm_bridge_set_node() helper, no functional changes. The reason behind of this introduction is that the name 'of_node' itself has a smell of DT dependent, and it is a internal memeber, when there has helper function, we should use the revelant helper and avoid directly referencing and/or dereferencing it. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/bridge/sii9234.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/sii9234.c b/drivers/gpu/drm/bridge/sii9234.c index 7d2bbc31bac9..d930c093abb3 100644 --- a/drivers/gpu/drm/bridge/sii9234.c +++ b/drivers/gpu/drm/bridge/sii9234.c @@ -817,10 +817,11 @@ static int sii9234_init_resources(struct sii9234 *ctx, struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; + struct fwnode_handle *fwnode = dev_fwnode(ctx->dev); int ret; - if (!ctx->dev->of_node) { - dev_err(ctx->dev, "not DT device\n"); + if (!fwnode) { + dev_err(ctx->dev, "firmware data is missing\n"); return -ENODEV; } -- 2.34.1
[PATCH v6 07/10] drm-bridge: it66121: Use fwnode APIs to acquire device properties
Make this driver less DT-dependent by calling the newly created helpers, also switch to use fwnode APIs to acquire additional device properties. A side benifit is that boilerplates get reduced, no functional changes for DT-based systems. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/bridge/ite-it66121.c | 55 +--- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 8e4fc082bb8c..3fa650f7fec9 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -1480,7 +1479,7 @@ static int it66121_audio_codec_init(struct it66121_ctx *ctx, struct device *dev) dev_dbg(dev, "%s\n", __func__); - if (!of_property_read_bool(dev->of_node, "#sound-dai-cells")) { + if (!fwnode_property_present(dev_fwnode(dev), "#sound-dai-cells")) { dev_info(dev, "No \"#sound-dai-cells\", no audio\n"); return 0; } @@ -1503,13 +1502,36 @@ static const char * const it66121_supplies[] = { "vcn33", "vcn18", "vrf12" }; +static int it66121_read_bus_width(struct fwnode_handle *fwnode, u32 *bus_width) +{ + struct fwnode_handle *endpoint; + u32 val; + int ret; + + endpoint = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0); + if (!endpoint) + return -EINVAL; + + ret = fwnode_property_read_u32(endpoint, "bus-width", &val); + fwnode_handle_put(endpoint); + if (ret) + return ret; + + if (val != 12 && val != 24) + return -EINVAL; + + *bus_width = val; + + return 0; +} + static int it66121_probe(struct i2c_client *client) { u32 revision_id, vendor_ids[2] = { 0 }, device_ids[2] = { 0 }; - struct device_node *ep; int ret; struct it66121_ctx *ctx; struct device *dev = &client->dev; + struct fwnode_handle *fwnode = dev_fwnode(dev); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(dev, "I2C check functionality failed.\n"); @@ -1520,29 +1542,20 @@ static int it66121_probe(struct i2c_client *client) if (!ctx) return -ENOMEM; - ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); - if (!ep) - return -EINVAL; - ctx->dev = dev; ctx->client = client; ctx->info = i2c_get_match_data(client); - of_property_read_u32(ep, "bus-width", &ctx->bus_width); - of_node_put(ep); - - if (ctx->bus_width != 12 && ctx->bus_width != 24) - return -EINVAL; - - ep = of_graph_get_remote_node(dev->of_node, 1, -1); - if (!ep) { - dev_err(ctx->dev, "The endpoint is unconnected\n"); - return -EINVAL; - } + ret = it66121_read_bus_width(fwnode, &ctx->bus_width); + if (ret) + return ret; - ctx->next_bridge = of_drm_find_bridge(ep); - of_node_put(ep); - if (!ctx->next_bridge) { + ctx->next_bridge = drm_bridge_find_next_bridge_by_fwnode(fwnode, 1); + if (IS_ERR(ctx->next_bridge)) { + ret = PTR_ERR(ctx->next_bridge); + dev_err(dev, "Error in founding the next bridge: %d\n", ret); + return ret; + } else if (!ctx->next_bridge) { dev_dbg(ctx->dev, "Next bridge not found, deferring probe\n"); return -EPROBE_DEFER; } -- 2.34.1
[PATCH v6 06/10] drm/bridge: sii902x: Switch to use fwnode APIs to acquire device properties
Make this driver less DT-dependent by calling the freshly created helpers, also switch to use fwnode APIs to acquire additional device properties. One side benifit is that boilerplates get reduced, no functional changes for DT-based systems. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/bridge/sii902x.c | 43 +++- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index f4808838717a..975511d623a4 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -827,20 +827,17 @@ static int sii902x_audio_codec_init(struct sii902x *sii902x, .spdif = 0, .max_i2s_channels = 0, }; + struct fwnode_handle *fwnode = dev_fwnode(dev); u8 lanes[4]; int num_lanes, i; - if (!of_property_read_bool(dev->of_node, "#sound-dai-cells")) { + if (!fwnode_property_present(fwnode, "#sound-dai-cells")) { dev_dbg(dev, "%s: No \"#sound-dai-cells\", no audio\n", __func__); return 0; } - num_lanes = of_property_read_variable_u8_array(dev->of_node, - "sil,i2s-data-lanes", - lanes, 1, - ARRAY_SIZE(lanes)); - + num_lanes = fwnode_property_count_u8(fwnode, "sil,i2s-data-lanes"); if (num_lanes == -EINVAL) { dev_dbg(dev, "%s: No \"sil,i2s-data-lanes\", use default <0>\n", @@ -852,7 +849,11 @@ static int sii902x_audio_codec_init(struct sii902x *sii902x, "%s: Error gettin \"sil,i2s-data-lanes\": %d\n", __func__, num_lanes); return num_lanes; + } else { + fwnode_property_read_u8_array(fwnode, "sil,i2s-data-lanes", + lanes, num_lanes); } + codec_data.max_i2s_channels = 2 * num_lanes; for (i = 0; i < num_lanes; i++) @@ -1117,7 +1118,6 @@ static int sii902x_init(struct sii902x *sii902x) static int sii902x_probe(struct i2c_client *client) { struct device *dev = &client->dev; - struct device_node *endpoint; struct sii902x *sii902x; static const char * const supplies[] = {"iovcc", "cvcc12"}; int ret; @@ -1146,27 +1146,14 @@ static int sii902x_probe(struct i2c_client *client) return PTR_ERR(sii902x->reset_gpio); } - endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1); - if (endpoint) { - struct device_node *remote = of_graph_get_remote_port_parent(endpoint); - - of_node_put(endpoint); - if (!remote) { - dev_err(dev, "Endpoint in port@1 unconnected\n"); - return -ENODEV; - } - - if (!of_device_is_available(remote)) { - dev_err(dev, "port@1 remote device is disabled\n"); - of_node_put(remote); - return -ENODEV; - } - - sii902x->next_bridge = of_drm_find_bridge(remote); - of_node_put(remote); - if (!sii902x->next_bridge) - return dev_err_probe(dev, -EPROBE_DEFER, -"Failed to find remote bridge\n"); + sii902x->next_bridge = drm_bridge_find_next_bridge_by_fwnode(dev_fwnode(dev), 1); + if (!sii902x->next_bridge) { + return dev_err_probe(dev, -EPROBE_DEFER, +"Failed to find the next bridge\n"); + } else if (IS_ERR(sii902x->next_bridge)) { + ret = PTR_ERR(sii902x->next_bridge); + dev_err(dev, "Error on find the next bridge: %d\n", ret); + return ret; } mutex_init(&sii902x->mutex); -- 2.34.1
[PATCH v6 05/10] drm/bridge: display-connector: Use fwnode APIs to acquire device properties
Switch to use the fwnode APIs, which is a fundamental step to make this driver OF-independent possible. No functional changes for DT-based systems. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/bridge/display-connector.c | 23 +++--- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c index fb1e97d385fe..7436e7cd53fc 100644 --- a/drivers/gpu/drm/bridge/display-connector.c +++ b/drivers/gpu/drm/bridge/display-connector.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -204,6 +203,7 @@ static int display_connector_get_supply(struct platform_device *pdev, static int display_connector_probe(struct platform_device *pdev) { + struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); struct display_connector *conn; unsigned int type; const char *label = NULL; @@ -215,15 +215,15 @@ static int display_connector_probe(struct platform_device *pdev) platform_set_drvdata(pdev, conn); - type = (uintptr_t)of_device_get_match_data(&pdev->dev); + type = (uintptr_t)device_get_match_data(&pdev->dev); /* Get the exact connector type. */ switch (type) { case DRM_MODE_CONNECTOR_DVII: { bool analog, digital; - analog = of_property_read_bool(pdev->dev.of_node, "analog"); - digital = of_property_read_bool(pdev->dev.of_node, "digital"); + analog = fwnode_property_present(fwnode, "analog"); + digital = fwnode_property_present(fwnode, "digital"); if (analog && !digital) { conn->bridge.type = DRM_MODE_CONNECTOR_DVIA; } else if (!analog && digital) { @@ -240,8 +240,7 @@ static int display_connector_probe(struct platform_device *pdev) case DRM_MODE_CONNECTOR_HDMIA: { const char *hdmi_type; - ret = of_property_read_string(pdev->dev.of_node, "type", - &hdmi_type); + ret = fwnode_property_read_string(fwnode, "type", &hdmi_type); if (ret < 0) { dev_err(&pdev->dev, "HDMI connector with no type\n"); return -EINVAL; @@ -271,7 +270,7 @@ static int display_connector_probe(struct platform_device *pdev) conn->bridge.interlace_allowed = true; /* Get the optional connector label. */ - of_property_read_string(pdev->dev.of_node, "label", &label); + fwnode_property_read_string(fwnode, "label", &label); /* * Get the HPD GPIO for DVI, HDMI and DP connectors. If the GPIO can provide @@ -309,12 +308,12 @@ static int display_connector_probe(struct platform_device *pdev) if (type == DRM_MODE_CONNECTOR_DVII || type == DRM_MODE_CONNECTOR_HDMIA || type == DRM_MODE_CONNECTOR_VGA) { - struct device_node *phandle; + struct fwnode_handle *phandle; - phandle = of_parse_phandle(pdev->dev.of_node, "ddc-i2c-bus", 0); - if (phandle) { - conn->bridge.ddc = of_get_i2c_adapter_by_node(phandle); - of_node_put(phandle); + phandle = fwnode_find_reference(fwnode, "ddc-i2c-bus", 0); + if (!IS_ERR(phandle)) { + conn->bridge.ddc = i2c_get_adapter_by_fwnode(phandle); + fwnode_handle_put(phandle); if (!conn->bridge.ddc) return -EPROBE_DEFER; } else { -- 2.34.1
[PATCH v6 02/10] drm/bridge: Set firmware node of drm_bridge instances automatically
Normally, the drm_bridge::of_node won't be used by bridge driver instances themselves. Rather, it is mainly used by other modules to find associated drm bridge drvier. Therefore, adding a drm bridge to the global bridge list and setting 'of_node' field of a drm bridge share the same goal. Both are for finding purpose, therefore better to group them to one function. Update the drm_bridge_add() interface and implementation to achieve such goal atomically, new implementation will fetch the device node from the backing device of the drm bridge driver automatically. For the majority cases, which is one device backing one drm bridge driver, this model works well. Drivers still can set it manually by passing NULL if this model doesn't fit. While at it, Add a 'struct device *' pointer to the drm_bridge structure. As it already being passed in by both of drm_bridge_add() and devm_drm_bridge_add(). A lot of driver instances has already added it into their derived structure, promote it into drm_bridge core helps to reduce a batch of boilerplates. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 3 +-- .../gpu/drm/bridge/analogix/analogix-anx6345.c | 4 +--- .../gpu/drm/bridge/analogix/analogix-anx78xx.c | 4 +--- drivers/gpu/drm/bridge/analogix/anx7625.c| 3 +-- drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c | 3 +-- .../gpu/drm/bridge/cadence/cdns-mhdp8546-core.c | 3 +-- drivers/gpu/drm/bridge/chipone-icn6211.c | 5 ++--- drivers/gpu/drm/bridge/chrontel-ch7033.c | 3 +-- drivers/gpu/drm/bridge/cros-ec-anx7688.c | 4 +--- drivers/gpu/drm/bridge/display-connector.c | 3 +-- drivers/gpu/drm/bridge/fsl-ldb.c | 3 +-- drivers/gpu/drm/bridge/imx/imx-ldb-helper.c | 3 ++- drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c | 3 +-- .../gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c | 3 ++- drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c | 3 +-- drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c | 3 +-- drivers/gpu/drm/bridge/ite-it6505.c | 3 +-- drivers/gpu/drm/bridge/ite-it66121.c | 3 +-- drivers/gpu/drm/bridge/lontium-lt8912b.c | 3 +-- drivers/gpu/drm/bridge/lontium-lt9211.c | 3 +-- drivers/gpu/drm/bridge/lontium-lt9611.c | 3 +-- drivers/gpu/drm/bridge/lontium-lt9611uxc.c | 3 +-- drivers/gpu/drm/bridge/lvds-codec.c | 3 +-- .../drm/bridge/megachips-stdp-ge-b850v3-fw.c | 3 +-- drivers/gpu/drm/bridge/microchip-lvds.c | 3 +-- drivers/gpu/drm/bridge/nwl-dsi.c | 3 +-- drivers/gpu/drm/bridge/nxp-ptn3460.c | 3 +-- drivers/gpu/drm/bridge/panel.c | 3 +-- drivers/gpu/drm/bridge/parade-ps8622.c | 3 +-- drivers/gpu/drm/bridge/parade-ps8640.c | 1 - drivers/gpu/drm/bridge/samsung-dsim.c| 3 +-- drivers/gpu/drm/bridge/sii902x.c | 3 +-- drivers/gpu/drm/bridge/sii9234.c | 3 +-- drivers/gpu/drm/bridge/sil-sii8620.c | 3 +-- drivers/gpu/drm/bridge/simple-bridge.c | 3 +-- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c| 3 +-- drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c| 3 +-- drivers/gpu/drm/bridge/tc358762.c| 3 +-- drivers/gpu/drm/bridge/tc358764.c| 3 +-- drivers/gpu/drm/bridge/tc358767.c| 3 +-- drivers/gpu/drm/bridge/tc358768.c| 3 +-- drivers/gpu/drm/bridge/tc358775.c| 3 +-- drivers/gpu/drm/bridge/thc63lvd1024.c| 3 +-- drivers/gpu/drm/bridge/ti-dlpc3433.c | 3 +-- drivers/gpu/drm/bridge/ti-sn65dsi83.c| 3 +-- drivers/gpu/drm/bridge/ti-sn65dsi86.c| 3 +-- drivers/gpu/drm/bridge/ti-tfp410.c | 3 +-- drivers/gpu/drm/bridge/ti-tpd12s015.c| 3 +-- drivers/gpu/drm/drm_bridge.c | 16 ++-- drivers/gpu/drm/exynos/exynos_drm_mic.c | 3 +-- drivers/gpu/drm/i2c/tda998x_drv.c| 5 + drivers/gpu/drm/mcde/mcde_dsi.c | 3 +-- drivers/gpu/drm/mediatek/mtk_dsi.c | 3 +-- drivers/gpu/drm/mediatek/mtk_hdmi.c | 3 +-- drivers/gpu/drm/meson/meson_encoder_cvbs.c | 3 +-- drivers/gpu/drm/meson/meson_encoder_dsi.c| 3 +-- drivers/gpu/drm/meson/meson_encoder_hdmi.c | 3 +-- drivers/gpu/drm/omapdrm/dss/dpi.c| 3 +-- drivers/gpu/drm/omapdrm/dss/dsi.c| 3 +-- drivers/gpu/drm/omapdrm/dss/hdmi4.c | 3 +-- drivers/gpu/drm/omapdrm/dss/hdmi5.c | 3 +-- drivers/gpu/drm/omapdrm/dss/sdi.c| 3 +-- drivers/gpu/drm/omapdrm/dss/venc.c | 3 +-- drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c | 3 +-- drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c | 3 +-- drivers/gpu/drm/renesas
[PATCH v6 04/10] drm/bridge: simple-bridge: Use fwnode APIs to acquire device properties
Make this driver less DT-dependent by calling the newly created helpers, also switch to use fwnode APIs to acquire additional device properties. A side benifit is that boilerplates get reduced, no functional changes for DT-based systems. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/bridge/simple-bridge.c | 21 + 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c index fd7de9b6f80e..e7348ee6daf7 100644 --- a/drivers/gpu/drm/bridge/simple-bridge.c +++ b/drivers/gpu/drm/bridge/simple-bridge.c @@ -8,8 +8,6 @@ #include #include -#include -#include #include #include @@ -164,33 +162,32 @@ static const struct drm_bridge_funcs simple_bridge_bridge_funcs = { static int simple_bridge_probe(struct platform_device *pdev) { + struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); struct simple_bridge *sbridge; - struct device_node *remote; + int ret; sbridge = devm_kzalloc(&pdev->dev, sizeof(*sbridge), GFP_KERNEL); if (!sbridge) return -ENOMEM; platform_set_drvdata(pdev, sbridge); - sbridge->info = of_device_get_match_data(&pdev->dev); + sbridge->info = device_get_match_data(&pdev->dev); /* Get the next bridge in the pipeline. */ - remote = of_graph_get_remote_node(pdev->dev.of_node, 1, -1); - if (!remote) - return -EINVAL; - - sbridge->next_bridge = of_drm_find_bridge(remote); - of_node_put(remote); - + sbridge->next_bridge = drm_bridge_find_next_bridge_by_fwnode(fwnode, 1); if (!sbridge->next_bridge) { dev_dbg(&pdev->dev, "Next bridge not found, deferring probe\n"); return -EPROBE_DEFER; + } else if (IS_ERR(sbridge->next_bridge)) { + ret = PTR_ERR(sbridge->next_bridge); + dev_err(&pdev->dev, "Error on finding the next bridge: %d\n", ret); + return ret; } /* Get the regulator and GPIO resources. */ sbridge->vdd = devm_regulator_get_optional(&pdev->dev, "vdd"); if (IS_ERR(sbridge->vdd)) { - int ret = PTR_ERR(sbridge->vdd); + ret = PTR_ERR(sbridge->vdd); if (ret == -EPROBE_DEFER) return -EPROBE_DEFER; sbridge->vdd = NULL; -- 2.34.1
[PATCH v6 03/10] drm/bridge: Implement of_drm_find_bridge() on the top of drm_bridge_find_by_fwnode()
Before applying this patch, people may worry about the OF and non-OF API will have a risk to diverge. Eliminate the risk by reimplement the of_drm_find_bridge() on the top of drm_bridge_find_by_fwnode(). As for now the fundamental searching method is unique. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/drm_bridge.c | 29 - include/drm/drm_bridge.h | 14 +- 2 files changed, 5 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 7759ca066db4..4c5584922d3c 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1346,35 +1346,6 @@ void drm_bridge_hpd_notify(struct drm_bridge *bridge, } EXPORT_SYMBOL_GPL(drm_bridge_hpd_notify); -#ifdef CONFIG_OF -/** - * of_drm_find_bridge - find the bridge corresponding to the device node in - * the global bridge list - * - * @np: device node - * - * RETURNS: - * drm_bridge control struct on success, NULL on failure - */ -struct drm_bridge *of_drm_find_bridge(struct device_node *np) -{ - struct drm_bridge *bridge; - - mutex_lock(&bridge_lock); - - list_for_each_entry(bridge, &bridge_list, list) { - if (bridge->of_node == np) { - mutex_unlock(&bridge_lock); - return bridge; - } - } - - mutex_unlock(&bridge_lock); - return NULL; -} -EXPORT_SYMBOL(of_drm_find_bridge); -#endif - /** * drm_bridge_find_by_fwnode - Find the bridge corresponding to the fwnode * diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 7b592cf30340..8d743dfe782c 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -791,21 +791,17 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, struct drm_bridge *previous, enum drm_bridge_attach_flags flags); -#ifdef CONFIG_OF -struct drm_bridge *of_drm_find_bridge(struct device_node *np); -#else -static inline struct drm_bridge *of_drm_find_bridge(struct device_node *np) -{ - return NULL; -} -#endif - struct drm_bridge * drm_bridge_find_by_fwnode(struct fwnode_handle *fwnode); struct drm_bridge * drm_bridge_find_next_bridge_by_fwnode(struct fwnode_handle *fwnode, u32 port); +static inline struct drm_bridge *of_drm_find_bridge(struct device_node *np) +{ + return drm_bridge_find_by_fwnode(of_fwnode_handle(np)); +} + /** * drm_bridge_get_next_bridge() - Get the next bridge in the chain * @bridge: bridge object -- 2.34.1
[PATCH v6 01/10] drm/bridge: Allow using fwnode APIs to get the next bridge
The various display bridge drivers rely on 'OF' infrastructures to works very well, yet there are some platforms and/or devices lack of 'OF' support. Such as virtual display drivers, USB display apapters and ACPI based systems etc. Add fwnode based helpers to fill the niche, this allows part of display bridge drivers to work across systems. As the fwnode APIs has wider coverage than DT counterpart, and fwnode graphs are compatible with OF graphs. So the newly created helpers can be used on all systems in theory, assumed that there has valid OF/fwnode graphs established. Note, the involved drm bridge instance should also has the fwnode assigned, so that the user of it could find it via the fwnode handle. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/drm_bridge.c | 74 include/drm/drm_bridge.h | 11 +- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 584d109330ab..cef5bc88ee60 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1363,6 +1363,80 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np) EXPORT_SYMBOL(of_drm_find_bridge); #endif +/** + * drm_bridge_find_by_fwnode - Find the bridge corresponding to the fwnode + * + * @fwnode: fwnode for which to find the matching drm_bridge + * + * This function looks up a drm_bridge in the global bridge list, based on + * its associated fwnode. Drivers who want to use this function should has + * fwnode handle assigned to the fwnode member of the struct drm_bridge + * instance. + * + * Returns: + * * A reference to the requested drm_bridge object on success, or + * * %NULL otherwise (object does not exist or the driver of requested + *bridge not probed yet). + */ +struct drm_bridge *drm_bridge_find_by_fwnode(struct fwnode_handle *fwnode) +{ + struct drm_bridge *ret = NULL; + struct drm_bridge *bridge; + + if (!fwnode) + return NULL; + + mutex_lock(&bridge_lock); + + list_for_each_entry(bridge, &bridge_list, list) { + if (bridge->fwnode == fwnode) { + ret = bridge; + break; + } + } + + mutex_unlock(&bridge_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(drm_bridge_find_by_fwnode); + +/** + * drm_bridge_find_next_bridge_by_fwnode - Find the next bridge by fwnode + * @fwnode: fwnode pointer to the current device. + * @port: identifier of the port node of the next bridge is connected. + * + * This function find the next bridge of the current node the fwnode + * pointed to, assumed that the fwnode graph has been well established. + * + * Returns: + * * A reference to the requested drm_bridge object on success, or + * * -%ENODEV if the fwnode graph or OF graph is not complete, or + * * %NULL if object does not exist or the next bridge is not ready yet. + */ +struct drm_bridge * +drm_bridge_find_next_bridge_by_fwnode(struct fwnode_handle *fwnode, u32 port) +{ + struct drm_bridge *bridge; + struct fwnode_handle *ep; + struct fwnode_handle *remote; + + ep = fwnode_graph_get_endpoint_by_id(fwnode, port, 0, 0); + if (!ep) + return ERR_PTR(-ENODEV); + + remote = fwnode_graph_get_remote_port_parent(ep); + fwnode_handle_put(ep); + if (!remote) + return ERR_PTR(-ENODEV); + + bridge = drm_bridge_find_by_fwnode(remote); + fwnode_handle_put(remote); + + return bridge; +} +EXPORT_SYMBOL_GPL(drm_bridge_find_next_bridge_by_fwnode); + MODULE_AUTHOR("Ajay Kumar "); MODULE_DESCRIPTION("DRM bridge infrastructure"); MODULE_LICENSE("GPL and additional rights"); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 4baca0d9107b..725d6dddaf36 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -26,14 +26,13 @@ #include #include #include +#include #include #include #include #include -struct device_node; - struct drm_bridge; struct drm_bridge_timings; struct drm_connector; @@ -721,6 +720,8 @@ struct drm_bridge { struct list_head chain_node; /** @of_node: device node pointer to the bridge */ struct device_node *of_node; + /** @fwnode: fwnode pointer to the bridge */ + struct fwnode_handle *fwnode; /** @list: to keep track of all added bridges */ struct list_head list; /** @@ -797,6 +798,12 @@ static inline struct drm_bridge *of_drm_find_bridge(struct device_node *np) } #endif +struct drm_bridge * +drm_bridge_find_by_fwnode(struct fwnode_handle *fwnode); + +struct drm_bridge * +drm_bridge_find_next_bridge_by_fwnode(struct fwnode_handle *fwnode, u32 port); + /** * drm_bridge_get_next_bridge() - Get the next bridge in the chain * @bridge: bridge object -- 2.34.1
[PATCH v6 00/10] drm/bridge: Allow using fwnode API to get the next bridge
Currently, the various display bridge drivers rely on OF infrastructures to works very well, yet there are platforms and/or devices absence of 'OF' support. Such as virtual display drivers, USB display apapters and ACPI based systems etc. Add fwnode based helpers to fill the niche, this allows part of the display bridge drivers to work across systems. As the fwnode API has wider coverage than DT counterpart and the fwnode graphs are compatible with the OF graph, so the provided helpers can be used on all systems in theory. Assumed that fwnode graphs are well established on the system. Tested on TI BeaglePlay board. v1 -> v2: * Modify it66121 to switch togather * Drop the 'side-by-side' implement * Add drm_bridge_find_next_bridge_by_fwnode() helper * Add drm_bridge_set_node() helper v2 -> v3: * Read kernel-doc and improve function comments (Dmitry) * Drop the 'port' argument of it66121_read_bus_width() (Dmitry) * drm-bridge: sii902x get nuked v3 -> v4: * drm-bridge: tfp410 get nuked * Add platform module alias * Rebase and improve commit message and function comments v4 -> v5: * Modify sii9234, ch7033 and ANX7688 * Trivial fixes v5 -> v6: * Implement the same thing with no boilerplate introduced * Add 'struct device *' field to the drm_bridge structure * Re-implement of_drm_find_bridge() with drm_bridge_find_by_fwnode() Sui Jingfeng (10): drm/bridge: Allow using fwnode APIs to get the next bridge drm/bridge: Set firmware node of drm_bridge instances automatically drm/bridge: Implement of_drm_find_bridge() on the top of drm_bridge_find_by_fwnode() drm/bridge: simple-bridge: Use fwnode APIs to acquire device properties drm/bridge: display-connector: Use fwnode APIs to acquire device properties drm/bridge: sii902x: Switch to use fwnode APIs to acquire device properties drm-bridge: it66121: Use fwnode APIs to acquire device properties drm/bridge: tfp410: Use fwnode APIs to acquire device properties drm/bridge: sii9234: Use fwnode APIs to abstract DT dependent API away drm/bridge: ch7033: Switch to use fwnode based APIs drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 3 +- .../drm/bridge/analogix/analogix-anx6345.c| 4 +- .../drm/bridge/analogix/analogix-anx78xx.c| 4 +- drivers/gpu/drm/bridge/analogix/anx7625.c | 3 +- .../gpu/drm/bridge/cadence/cdns-dsi-core.c| 3 +- .../drm/bridge/cadence/cdns-mhdp8546-core.c | 3 +- drivers/gpu/drm/bridge/chipone-icn6211.c | 5 +- drivers/gpu/drm/bridge/chrontel-ch7033.c | 15 ++-- drivers/gpu/drm/bridge/cros-ec-anx7688.c | 4 +- drivers/gpu/drm/bridge/display-connector.c| 26 +++--- drivers/gpu/drm/bridge/fsl-ldb.c | 3 +- drivers/gpu/drm/bridge/imx/imx-ldb-helper.c | 3 +- drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c | 3 +- .../drm/bridge/imx/imx8qxp-pixel-combiner.c | 3 +- .../gpu/drm/bridge/imx/imx8qxp-pixel-link.c | 3 +- drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c | 3 +- drivers/gpu/drm/bridge/ite-it6505.c | 3 +- drivers/gpu/drm/bridge/ite-it66121.c | 58 - drivers/gpu/drm/bridge/lontium-lt8912b.c | 3 +- drivers/gpu/drm/bridge/lontium-lt9211.c | 3 +- drivers/gpu/drm/bridge/lontium-lt9611.c | 3 +- drivers/gpu/drm/bridge/lontium-lt9611uxc.c| 3 +- drivers/gpu/drm/bridge/lvds-codec.c | 3 +- .../bridge/megachips-stdp-ge-b850v3-fw.c | 3 +- drivers/gpu/drm/bridge/microchip-lvds.c | 3 +- drivers/gpu/drm/bridge/nwl-dsi.c | 3 +- drivers/gpu/drm/bridge/nxp-ptn3460.c | 3 +- drivers/gpu/drm/bridge/panel.c| 3 +- drivers/gpu/drm/bridge/parade-ps8622.c| 3 +- drivers/gpu/drm/bridge/parade-ps8640.c| 1 - drivers/gpu/drm/bridge/samsung-dsim.c | 3 +- drivers/gpu/drm/bridge/sii902x.c | 46 -- drivers/gpu/drm/bridge/sii9234.c | 8 +- drivers/gpu/drm/bridge/sil-sii8620.c | 3 +- drivers/gpu/drm/bridge/simple-bridge.c| 24 +++-- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 3 +- drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 3 +- drivers/gpu/drm/bridge/tc358762.c | 3 +- drivers/gpu/drm/bridge/tc358764.c | 3 +- drivers/gpu/drm/bridge/tc358767.c | 3 +- drivers/gpu/drm/bridge/tc358768.c | 3 +- drivers/gpu/drm/bridge/tc358775.c | 3 +- drivers/gpu/drm/bridge/thc63lvd1024.c | 3 +- drivers/gpu/drm/bridge/ti-dlpc3433.c | 3 +- drivers/gpu/drm/bridge/ti-sn65dsi83.c | 3 +- drivers/gpu/drm/bridge/ti-sn65dsi86.c | 3 +- drivers/gpu/drm/bridge/ti-tfp410.c| 42 - drivers/gpu/drm/bridge/ti-tpd12s015.c | 3 +- drivers/gpu/drm/drm_bridge.c
[PATCH v2 3/3] drm/loongson: Add dummy gpu driver as a subcomponent
Loongson Graphics are PCIe multi-functional devices, the GPU device and the display controller are two distinct devices. Drivers of them should loose coupling, but still be able to works togather to provide a unified service to userspace. Add a dummy driver for the GPU, it functional as a subcomponent as well. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/Makefile| 3 + drivers/gpu/drm/loongson/loong_gpu_pci_drv.c | 90 drivers/gpu/drm/loongson/loong_gpu_pci_drv.h | 27 ++ drivers/gpu/drm/loongson/loongson_module.c | 9 ++ drivers/gpu/drm/loongson/loongson_module.h | 7 ++ drivers/gpu/drm/loongson/lsdc_drv.c | 12 ++- drivers/gpu/drm/loongson/lsdc_drv.h | 8 +- 7 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 drivers/gpu/drm/loongson/loong_gpu_pci_drv.c create mode 100644 drivers/gpu/drm/loongson/loong_gpu_pci_drv.h diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile index e15cb9bff378..4f4c1c42bbba 100644 --- a/drivers/gpu/drm/loongson/Makefile +++ b/drivers/gpu/drm/loongson/Makefile @@ -17,6 +17,9 @@ loongson-y := \ lsdc_probe.o \ lsdc_ttm.o +loongson-y += \ + loong_gpu_pci_drv.o + loongson-y += loongson_device.o \ loongson_module.o diff --git a/drivers/gpu/drm/loongson/loong_gpu_pci_drv.c b/drivers/gpu/drm/loongson/loong_gpu_pci_drv.c new file mode 100644 index ..4ae6a5807d1d --- /dev/null +++ b/drivers/gpu/drm/loongson/loong_gpu_pci_drv.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include + +#include +#include + +#include "loongson_module.h" +#include "loong_gpu_pci_drv.h" + +static int loong_gpu_bind(struct device *dev, struct device *master, void *data) +{ + struct drm_device *drm = data; + struct loong_gpu_device *gpu; + u32 hw_info; + u8 host_id; + u8 revision; + + gpu = devm_kzalloc(dev, sizeof(*gpu), GFP_KERNEL); + if (!gpu) + return -ENOMEM; + + gpu->reg_base = pcim_iomap(to_pci_dev(dev), 0, 0); + if (!gpu->reg_base) + return -ENOMEM; + + hw_info = loong_rreg32(gpu, 0x8C); + + gpu->ver_major = (hw_info >> 8) * 0x0F; + gpu->ver_minor = (hw_info & 0xF0) >> 4; + revision = hw_info & 0x0F; + host_id = (hw_info >> 16) & 0xFF; + + drm_info(drm, "Found LoongGPU: LG%x%x0, revision: %x, Host: %s\n", +gpu->ver_major, gpu->ver_minor, revision, +host_id ? "LS2K2000" : "LS7A2000"); + + dev_set_drvdata(dev, gpu); + + return 0; +} + +static void loong_gpu_unbind(struct device *dev, struct device *master, void *data) +{ + struct loong_gpu_device *gpu = dev_get_drvdata(dev); + + if (gpu) { + pcim_iounmap(to_pci_dev(dev), gpu->reg_base); + devm_kfree(dev, gpu); + } +} + +static const struct component_ops loong_gpu_component_ops = { + .bind = loong_gpu_bind, + .unbind = loong_gpu_unbind, +}; + +static int loong_gpu_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + pci_set_master(pdev); + + return component_add(&pdev->dev, &loong_gpu_component_ops); +} + +static void loong_gpu_pci_remove(struct pci_dev *pdev) +{ + component_del(&pdev->dev, &loong_gpu_component_ops); +} + +static const struct pci_device_id loong_gpu_pci_id_list[] = { + {PCI_VDEVICE(LOONGSON, 0x7a25), CHIP_LS7A2000}, + { }, +}; + +struct pci_driver loong_gpu_pci_driver = { + .name = "loong", + .id_table = loong_gpu_pci_id_list, + .probe = loong_gpu_pci_probe, + .remove = loong_gpu_pci_remove, +}; + +MODULE_DEVICE_TABLE(pci, loong_gpu_pci_id_list); diff --git a/drivers/gpu/drm/loongson/loong_gpu_pci_drv.h b/drivers/gpu/drm/loongson/loong_gpu_pci_drv.h new file mode 100644 index ..f620820ab263 --- /dev/null +++ b/drivers/gpu/drm/loongson/loong_gpu_pci_drv.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __LOONG_GPU_PCI_DRV_H__ +#define __LOONG_GPU_PCI_DRV_H__ + +#include + +struct loong_gpu_device { + struct pci_dev *pdev; + void __iomem *reg_base; + + u32 ver_major; + u32 ver_minor; + u32 revision; +}; + +static inline u32 loong_rreg32(struct loong_gpu_device *ldev, u32 offset) +{ + return readl(ldev->reg_base + offset); +} + +static inline void loong_wreg32(struct loong_gpu_device *ldev, u32 offset, u32 val) +{ + writel(val, ldev->reg_base + offset); +} + +#endif diff --git a/drivers/gpu/drm/loongson/loongson_module.c b/drivers/gpu/drm/loongson/loongson_module.c index 037fa7ffe9c9..d4c0d5ce
[PATCH v2 2/3] drm/loongson: Introduce component framework support
Hardware units come with PCIe are actually all ready to be driven, but there has some board specific modules could return '-EPROBE_DEFER'. However, the driver needs all of the subcompoments ready to use before it can register the drm service to userspace. Introduce the component framework to tackle such problems, move DRM device related code into loongson_drm_master_bind() function. Move output related things into subdriver. Display controller and GPIO-I2C goes with the PCIe master, sinch they has no dependency on exterinal modules. While the outputs drivers, such as encoders and conectors, may has some dependency on exterinal modules. Those hardware units are relatively independent hardware IPs from the CRTC. Hence, offload them to submodules. This design allows subdriver return '-EPROBE_DEFER' to the driver core if it need to do so, the master drvier won't bind until all submodules are ready. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/Makefile | 1 + drivers/gpu/drm/loongson/loongson_module.c| 17 +- drivers/gpu/drm/loongson/loongson_module.h| 1 + drivers/gpu/drm/loongson/lsdc_drv.c | 205 +++--- drivers/gpu/drm/loongson/lsdc_drv.h | 31 +-- drivers/gpu/drm/loongson/lsdc_i2c.c | 5 +- drivers/gpu/drm/loongson/lsdc_i2c.h | 3 - drivers/gpu/drm/loongson/lsdc_output.c| 176 +++ drivers/gpu/drm/loongson/lsdc_output.h| 38 +++- drivers/gpu/drm/loongson/lsdc_output_7a1000.c | 3 +- drivers/gpu/drm/loongson/lsdc_output_7a2000.c | 17 +- 11 files changed, 367 insertions(+), 130 deletions(-) create mode 100644 drivers/gpu/drm/loongson/lsdc_output.c diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile index 91e72bd900c1..e15cb9bff378 100644 --- a/drivers/gpu/drm/loongson/Makefile +++ b/drivers/gpu/drm/loongson/Makefile @@ -9,6 +9,7 @@ loongson-y := \ lsdc_gfxpll.o \ lsdc_i2c.o \ lsdc_irq.o \ + lsdc_output.o \ lsdc_output_7a1000.o \ lsdc_output_7a2000.o \ lsdc_plane.o \ diff --git a/drivers/gpu/drm/loongson/loongson_module.c b/drivers/gpu/drm/loongson/loongson_module.c index d2a51bd395f6..037fa7ffe9c9 100644 --- a/drivers/gpu/drm/loongson/loongson_module.c +++ b/drivers/gpu/drm/loongson/loongson_module.c @@ -4,6 +4,7 @@ */ #include +#include #include @@ -19,15 +20,29 @@ module_param_named(vblank, loongson_vblank, int, 0400); static int __init loongson_module_init(void) { + int ret; + if (!loongson_modeset || video_firmware_drivers_only()) return -ENODEV; - return pci_register_driver(&lsdc_pci_driver); + ret = platform_driver_register(&lsdc_output_port_platform_driver); + if (ret) + return ret; + + ret = pci_register_driver(&lsdc_pci_driver); + if (ret) { + platform_driver_unregister(&lsdc_output_port_platform_driver); + return ret; + } + + return 0; } module_init(loongson_module_init); static void __exit loongson_module_exit(void) { pci_unregister_driver(&lsdc_pci_driver); + + platform_driver_unregister(&lsdc_output_port_platform_driver); } module_exit(loongson_module_exit); diff --git a/drivers/gpu/drm/loongson/loongson_module.h b/drivers/gpu/drm/loongson/loongson_module.h index 931c17521bf0..8dc71b98f5cc 100644 --- a/drivers/gpu/drm/loongson/loongson_module.h +++ b/drivers/gpu/drm/loongson/loongson_module.h @@ -8,5 +8,6 @@ extern int loongson_vblank; extern struct pci_driver lsdc_pci_driver; +extern struct platform_driver lsdc_output_port_platform_driver; #endif diff --git a/drivers/gpu/drm/loongson/lsdc_drv.c b/drivers/gpu/drm/loongson/lsdc_drv.c index adc7344d2f80..02429c95bd1a 100644 --- a/drivers/gpu/drm/loongson/lsdc_drv.c +++ b/drivers/gpu/drm/loongson/lsdc_drv.c @@ -3,7 +3,9 @@ * Copyright (C) 2023 Loongson Technology Corporation Limited */ +#include #include +#include #include #include @@ -67,31 +69,6 @@ static int lsdc_modeset_init(struct lsdc_device *ldev, unsigned int i; int ret; - for (i = 0; i < num_crtc; i++) { - dispipe = &ldev->dispipe[i]; - - /* We need an index before crtc is initialized */ - dispipe->index = i; - - ret = funcs->create_i2c(ddev, dispipe, i); - if (ret) - return ret; - } - - for (i = 0; i < num_crtc; i++) { - struct i2c_adapter *ddc = NULL; - - dispipe = &ldev->dispipe[i]; - if (dispipe->li2c) - ddc = &dispipe->li2c->adapter; - - ret = funcs->output_init(ddev, dispipe, ddc, i); - if (ret) - return ret; - - ldev->num_output++; -
[PATCH v2 1/3] drm/loongson: Add a helper for creating child devices
In some display subsystems, the functionality of a PCIe device may too complex to be managed by a single driver. A split of the functionality into child devices can helpful to achieve better modularity. Another benefit is that we could migrate the dependency on exterinal modules to a submodule level with the helper created. For example, it's not uncommon that some exterinal module will return -EPROBE_DEFER to our driver during probe time. KMS driver has to tear down everything when it receives -EPROBE_DEFER, the problem is that it's completely not necessary and rising cyclic dependency problems if not process correctly. Add the loongson_create_platform_device() function, which allows the KMS driver to create sub-devices for it. The manually created decice acts as agents for the principal part, migrate the potential issue to submodule. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/loongson_device.c | 42 ++ drivers/gpu/drm/loongson/lsdc_drv.h| 6 2 files changed, 48 insertions(+) diff --git a/drivers/gpu/drm/loongson/loongson_device.c b/drivers/gpu/drm/loongson/loongson_device.c index 9986c8a2a255..b268549d643e 100644 --- a/drivers/gpu/drm/loongson/loongson_device.c +++ b/drivers/gpu/drm/loongson/loongson_device.c @@ -4,6 +4,7 @@ */ #include +#include #include "lsdc_drv.h" @@ -100,3 +101,44 @@ lsdc_device_probe(struct pci_dev *pdev, enum loongson_chip_id chip_id) { return __chip_id_desc_table[chip_id]; } + +int loongson_create_platform_device(struct device *parent, + const char *name, int id, + struct resource *pres, + void *data, + struct platform_device **ppdev) +{ + struct platform_device *pdev; + int ret; + + pdev = platform_device_alloc(name, id); + if (!pdev) + return -ENOMEM; + + pdev->dev.parent = parent; + + if (pres) { + ret = platform_device_add_resources(pdev, pres, 1); + if (ret) { + platform_device_put(pdev); + return ret; + } + } + + if (data) { + void *pdata = kmalloc(sizeof(void *), GFP_KERNEL); + + *(void **)pdata = data; + pdev->dev.platform_data = pdata; + } + + ret = platform_device_add(pdev); + if (ret) { + platform_device_put(pdev); + return ret; + } + + *ppdev = pdev; + + return 0; +} diff --git a/drivers/gpu/drm/loongson/lsdc_drv.h b/drivers/gpu/drm/loongson/lsdc_drv.h index fbf2d760ef27..a2c6b496a69f 100644 --- a/drivers/gpu/drm/loongson/lsdc_drv.h +++ b/drivers/gpu/drm/loongson/lsdc_drv.h @@ -47,6 +47,12 @@ enum loongson_chip_id { const struct lsdc_desc * lsdc_device_probe(struct pci_dev *pdev, enum loongson_chip_id chip); +int loongson_create_platform_device(struct device *parent, + const char *name, int id, + struct resource *pres, + void *data, + struct platform_device **ppdev); + struct lsdc_kms_funcs; /* DC specific */ -- 2.34.1
[PATCH v2 0/3] drm/loongson: Introduce component framework support
Introduce component framework to bind child and sibling devices, for better modularity and offload the deferral probe issue to submodule if it need to attach exterinal module someday. Also for better reflect the hardware layout. Hardware units that come with PCIe are all ready to drive, but there are some board specific modules will return -EPROBE_DEFER to us. We need all submodules ready to use before we can register the drm device to userspace. The idea is to device the exterinal module dependent part and exterinal module independent part. For example, the display controller and the GPIO-I2C just belong to exterinal module independent part. While the outputs are just belong to exterinal module dependent part. We abstract the output ports as child devices, the output ports may consists of encoder phy and level shifter. Well, the GPU are standalone siblings relative to the DC. Those units are relatively separated hardware units from display controller itself. By design, the display controller PCI(e) is selected as master, gpio-i2c go with master. Manually created virtual subdevice functional as agents for the master, it could return the -EPROBE_DEFER back to the drvier core. This allows the master don't have to tear down everything, thereore majority setups work can be preserved. The potential cyclic dependency problem can be solved then. v1 -> v2: * Squash patch 0002 and patch 0003 into one * Fill type and improve commit message Sui Jingfeng (3): drm/loongson: Add a helper for creating child devices drm/loongson: Introduce component framework support drm/loongson: Add dummy gpu driver as a subcomponent drivers/gpu/drm/loongson/Makefile | 4 + drivers/gpu/drm/loongson/loong_gpu_pci_drv.c | 90 drivers/gpu/drm/loongson/loong_gpu_pci_drv.h | 27 +++ drivers/gpu/drm/loongson/loongson_device.c| 42 drivers/gpu/drm/loongson/loongson_module.c| 26 ++- drivers/gpu/drm/loongson/loongson_module.h| 8 + drivers/gpu/drm/loongson/lsdc_drv.c | 217 +++--- drivers/gpu/drm/loongson/lsdc_drv.h | 45 +--- drivers/gpu/drm/loongson/lsdc_i2c.c | 5 +- drivers/gpu/drm/loongson/lsdc_i2c.h | 3 - drivers/gpu/drm/loongson/lsdc_output.c| 176 ++ drivers/gpu/drm/loongson/lsdc_output.h| 38 ++- drivers/gpu/drm/loongson/lsdc_output_7a1000.c | 3 +- drivers/gpu/drm/loongson/lsdc_output_7a2000.c | 17 +- 14 files changed, 564 insertions(+), 137 deletions(-) create mode 100644 drivers/gpu/drm/loongson/loong_gpu_pci_drv.c create mode 100644 drivers/gpu/drm/loongson/loong_gpu_pci_drv.h create mode 100644 drivers/gpu/drm/loongson/lsdc_output.c -- 2.34.1
Re: [v14,06/28] drm/tests: Add output bpc tests
Hi, Maxime I love you patch, yet it generates warnning calltrace. Despite it's just a warning but it can overwhelm when we run kunit tests. Hence, I suggest switch to the drm_atomic_connector_get_property() function. Logs are pasted as below for easier to ready. [ cut here ] WARNING: CPU: 3 PID: 1264 at drivers/gpu/drm/drm_mode_object.c:354 drm_object_property_get_value+0x2c/0x34 Modules linked in: drm_connector_test drm_display_helper drm_kunit_helpers kunit rfkill ip_set nf_tables nfnetlink vfat fat uas usb_storage kvm efi_pstore pstore spi_loongson_pci spi_loongson_core fuse efivarfs [last unloaded: drm_connector_test] CPU: 3 PID: 1264 Comm: kunit_try_catch Tainted: G N 6.9.0+ #443 Hardware name: Loongson Loongson-3A6000-HV-7A2000-XA61200/Loongson-3A6000-HV-7A2000-XA61200, BIOS Loongson-UDK2018-V4.0.05636-stable202311 12/ pc 93469fec ra 8225afdc tp 90011fc54000 sp 90011fc57d80 a0 90010aa84658 a1 900104432a00 a2 90011fc57d98 a3 90011fc57d98 a4 900104432a4c a5 93f14e98 a6 0008 a7 fffe t0 0010 t1 90010aa84000 t2 t3 c0c0c0c0 t4 c0c0c0c0 t5 0220 t6 0001 t7 00107203 t8 00107303 u0 0008 s9 9001000ebe60 s0 90010aa84000 s1 9001470679c8 s2 900104432a00 s3 82284000 s4 90010aa84658 s5 90010aa84618 s6 1000 s7 0001 s8 ra: 8225afdc drm_test_connector_hdmi_init_bpc_8+0xcc/0x2d0 [drm_connector_test] ERA: 93469fec drm_object_property_get_value+0x2c/0x34 CRMD: 00b0 (PLV0 -IE -DA +PG DACF=CC DACM=CC -WE) PRMD: 0004 (PPLV0 +PIE -PWE) EUEN: (-FPE -SXE -ASXE -BTE) ECFG: 00071c1c (LIE=2-4,10-12 VS=7) ESTAT: 000c [BRK] (IS= ECode=12 EsubCode=0) PRID: 0014d000 (Loongson-64bit, Loongson-3A6000-HV) CPU: 3 PID: 1264 Comm: kunit_try_catch Tainted: G N 6.9.0+ #443 Hardware name: Loongson Loongson-3A6000-HV-7A2000-XA61200/Loongson-3A6000-HV-7A2000-XA61200, BIOS Loongson-UDK2018-V4.0.05636-stable202311 12/ Stack : 94065000 92ac339c 90011fc54000 90011fc579f0 90011fc579f8 90011fc57b38 90011fc57b30 90011fc57b30 90011fc57940 0001 0001 90011fc579f8 18e7bf3ffb6e59df 900100328a00 0001 0003 0434 4c206e6f73676e6f 6f4c203a656d616e 000d0ad3 0704c000 9001000ebe60 93ee6ab0 94065000 90010aa84618 1000 0001 92ac33b4 7dd80078 00b0 0004 00071c1c ... Call Trace: [<92ac33b4>] show_stack+0x5c/0x180 [<93b1ed2c>] dump_stack_lvl+0x70/0xa0 [<93b01fd8>] __warn+0x84/0xc8 [<93ad282c>] report_bug+0x19c/0x204 [<93b1fe00>] do_bp+0x264/0x2b4 [<>] 0x0 [<93469fec>] drm_object_property_get_value+0x2c/0x34 [] drm_test_connector_hdmi_init_bpc_8+0xcc/0x2d0 [drm_connector_test] [] kunit_try_run_case+0x7c/0x18c [kunit] [] kunit_generic_run_threadfn_adapter+0x1c/0x28 [kunit] [<92b06238>] kthread+0x124/0x130 [<92ac1248>] ret_from_kernel_thread+0xc/0xa4 ---[ end trace ]--- [ cut here ] On 5/21/24 18:13, Maxime Ripard wrote: Now that we're tracking the output bpc count in the connector state, let's add a few tests to make sure it works as expected. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Reviewed-by: Dmitry Baryshkov --- drivers/gpu/drm/Kconfig| 1 + drivers/gpu/drm/tests/Makefile | 1 + drivers/gpu/drm/tests/drm_connector_test.c | 140 +++ drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c | 438 + drivers/gpu/drm/tests/drm_kunit_edid.h | 106 + 5 files changed, 686 insertions(+) [...] + +/* + * Test that the registration of a connector with a maximum bpc count of + * 8 succeeds, registers the max bpc property, but doesn't register the + * HDR output metadata one. + */ +static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + struct drm_connector *connector = &priv->connector; + struct drm_property *prop; + uint64_t val; + int ret; + + ret = drmm_connector_hdmi_init(&priv->drm, connector, + &dummy_funcs, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc, +
Re: [v5,3/3] drm/mediatek: Implement OF graphs support for display paths
Hi, On 5/21/24 15:57, AngeloGioacchino Del Regno wrote: +static int mtk_drm_of_ddp_path_build(struct device *dev, struct device_node *node, +struct mtk_mmsys_driver_data *data) +{ + struct device_node *ep_node; + struct of_endpoint of_ep; + bool output_present[MAX_CRTC] = { false }; + int ret; + + for_each_endpoint_of_node(node, ep_node) { + ret = of_graph_parse_endpoint(ep_node, &of_ep); + of_node_put(ep_node); There is going to *double* decline the reference counter, as the __of_get_next_child() will decrease the reference counter for us on the next iteration. + if (ret) { + dev_err_probe(dev, ret, "Cannot parse endpoint\n"); + break; + } Move the 'of_node_put(ep_node)' into brace '{}' here, if we really cares about the reference count. + + if (of_ep.id >= MAX_CRTC) { ditto ? + ret = dev_err_probe(dev, -EINVAL, + "Invalid endpoint%u number\n", of_ep.port); + break; + } + + output_present[of_ep.id] = true; + } + + if (ret) + return ret; + + if (output_present[CRTC_MAIN]) { + ret = mtk_drm_of_ddp_path_build_one(dev, CRTC_MAIN, + &data->main_path, &data->main_len); + if (ret) + return ret; + } + + if (output_present[CRTC_EXT]) { + ret = mtk_drm_of_ddp_path_build_one(dev, CRTC_EXT, + &data->ext_path, &data->ext_len); + if (ret) + return ret; + } + + if (output_present[CRTC_THIRD]) { + ret = mtk_drm_of_ddp_path_build_one(dev, CRTC_THIRD, + &data->third_path, &data->third_len); + if (ret) + return ret; + } + + return 0; +} + -- Best regards Sui Jingfeng
Re: [v5,3/3] drm/mediatek: Implement OF graphs support for display paths
Hi, On 5/21/24 15:57, AngeloGioacchino Del Regno wrote: +static int mtk_drm_of_get_ddp_comp_type(struct device_node *node, enum mtk_ddp_comp_type *ctype) +{ + const struct of_device_id *of_id = of_match_node(mtk_ddp_comp_dt_ids, node); + + if (!of_id) + return -EINVAL; + + *ctype = (enum mtk_ddp_comp_type)((uintptr_t)of_id->data); + + return 0; +} + +static int mtk_drm_of_get_ddp_ep_cid(struct device_node *node, +int output_port, enum mtk_crtc_path crtc_path, +struct device_node **next, unsigned int *cid) +{ + struct device_node *ep_dev_node, *ep_out; + enum mtk_ddp_comp_type comp_type; + int ret; + + ep_out = of_graph_get_endpoint_by_regs(node, output_port, crtc_path); + if (!ep_out) + return -ENOENT; + + ep_dev_node = of_graph_get_remote_port_parent(ep_out); below here, 'ep_out' will not be used anymore. of_node_put(ep_out); Maybe we should call it under a error handling tag? But this is trivial problem. + if (!ep_dev_node) + return -EINVAL; + + /* +* Pass the next node pointer regardless of failures in the later code +* so that if this function is called in a loop it will walk through all +* of the subsequent endpoints anyway. +*/ + *next = ep_dev_node; + + if (!of_device_is_available(ep_dev_node)) + return -ENODEV; + + ret = mtk_drm_of_get_ddp_comp_type(ep_dev_node, &comp_type); + if (ret) { + if (mtk_ovl_adaptor_is_comp_present(ep_dev_node)) { + *cid = (unsigned int)DDP_COMPONENT_DRM_OVL_ADAPTOR; + return 0; + } + return ret; + } + + ret = mtk_ddp_comp_get_id(ep_dev_node, comp_type); + if (ret < 0) + return ret; + + /* All ok! Pass the Component ID to the caller. */ + *cid = (unsigned int)ret; + + return 0; +} + -- Best regards Sui Jingfeng
Re: [v5,3/3] drm/mediatek: Implement OF graphs support for display paths
Hi, On 5/22/24 19:48, Sui Jingfeng wrote: if the not bridge is not ready 'not' -> 'next'
Re: [v5,3/3] drm/mediatek: Implement OF graphs support for display paths
Hi, Looks good to me in overall! On 5/21/24 15:57, AngeloGioacchino Del Regno wrote: It is impossible to add each and every possible DDP path combination for each and every possible combination of SoC and board: right now, this driver hardcodes configuration for 10 SoCs and this is going to grow larger and larger, and with new hacks like the introduction of mtk_drm_route which is anyway not enough for all final routes as the DSI cannot be connected to MERGE if it's not a dual-DSI, or enabling DSC preventively doesn't work if the display doesn't support it, or others. Since practically all display IPs in MediaTek SoCs support being interconnected with different instances of other, or the same, IPs or with different IPs and in different combinations, the final DDP pipeline is effectively a board specific configuration. Implement OF graphs support to the mediatek-drm drivers, allowing to stop hardcoding the paths, and preventing this driver to get a huge amount of arrays for each board and SoC combination, also paving the way to share the same mtk_mmsys_driver_data between multiple SoCs, making it more straightforward to add support for new chips. Reviewed-by: Alexandre Mergnat Tested-by: Alexandre Mergnat Signed-off-by: AngeloGioacchino Del Regno --- drivers/gpu/drm/mediatek/mtk_disp_drv.h | 1 + .../gpu/drm/mediatek/mtk_disp_ovl_adaptor.c | 40 ++- drivers/gpu/drm/mediatek/mtk_dpi.c| 16 +- drivers/gpu/drm/mediatek/mtk_drm_drv.c| 282 -- drivers/gpu/drm/mediatek/mtk_drm_drv.h| 2 +- drivers/gpu/drm/mediatek/mtk_dsi.c| 10 +- 6 files changed, 313 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_disp_drv.h b/drivers/gpu/drm/mediatek/mtk_disp_drv.h index 082ac18fe04a..94843974851f 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_drv.h +++ b/drivers/gpu/drm/mediatek/mtk_disp_drv.h @@ -108,6 +108,7 @@ size_t mtk_ovl_get_num_formats(struct device *dev); void mtk_ovl_adaptor_add_comp(struct device *dev, struct mtk_mutex *mutex); void mtk_ovl_adaptor_remove_comp(struct device *dev, struct mtk_mutex *mutex); +bool mtk_ovl_adaptor_is_comp_present(struct device_node *node); void mtk_ovl_adaptor_connect(struct device *dev, struct device *mmsys_dev, unsigned int next); void mtk_ovl_adaptor_disconnect(struct device *dev, struct device *mmsys_dev, diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c index 02dd7dcdfedb..400519d1ca1f 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c @@ -491,6 +491,38 @@ static int compare_of(struct device *dev, void *data) return dev->of_node == data; } +static int ovl_adaptor_of_get_ddp_comp_type(struct device_node *node, + enum mtk_ovl_adaptor_comp_type *ctype) +{ + const struct of_device_id *of_id = of_match_node(mtk_ovl_adaptor_comp_dt_ids, node); + + if (!of_id) + return -EINVAL; + + *ctype = (enum mtk_ovl_adaptor_comp_type)((uintptr_t)of_id->data); + + return 0; +} + +bool mtk_ovl_adaptor_is_comp_present(struct device_node *node) +{ + enum mtk_ovl_adaptor_comp_type type; + int ret; + + ret = ovl_adaptor_of_get_ddp_comp_type(node, &type); + if (ret) + return false; + + if (type >= OVL_ADAPTOR_TYPE_NUM) + return false; + + /* +* ETHDR and Padding are used exclusively in OVL Adaptor: if this +* component is not one of those, it's likely not an OVL Adaptor path. +*/ + return type == OVL_ADAPTOR_TYPE_ETHDR || type == OVL_ADAPTOR_TYPE_PADDING; +} + static int ovl_adaptor_comp_init(struct device *dev, struct component_match **match) { struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev); @@ -500,12 +532,11 @@ static int ovl_adaptor_comp_init(struct device *dev, struct component_match **ma parent = dev->parent->parent->of_node->parent; for_each_child_of_node(parent, node) { - const struct of_device_id *of_id; enum mtk_ovl_adaptor_comp_type type; - int id; + int id, ret; - of_id = of_match_node(mtk_ovl_adaptor_comp_dt_ids, node); - if (!of_id) + ret = ovl_adaptor_of_get_ddp_comp_type(node, &type); + if (ret) continue; if (!of_device_is_available(node)) { @@ -514,7 +545,6 @@ static int ovl_adaptor_comp_init(struct device *dev, struct component_match **ma continue; } - type = (enum mtk_ovl_adaptor_comp_type)(uintptr_t)of_id->data; id = ovl_adaptor_comp_get_id(dev, node, type); if (id < 0) { dev_warn(dev, "Skipping unknown component %pOF\n", diff --git a/drivers/gpu/dr
Re: [PATCH] drm/bridge: adv7511: Exit interrupt handling when necessary
Hi, On 5/20/24 19:13, Dmitry Baryshkov wrote: On Mon, 20 May 2024 at 14:11, Sui Jingfeng wrote: Hi, On 5/20/24 06:11, Dmitry Baryshkov wrote: On Thu, May 16, 2024 at 06:10:06PM +0800, Liu Ying wrote: Commit f3d9683346d6 ("drm/bridge: adv7511: Allow IRQ to share GPIO pins") fails to consider the case where adv7511->i2c_main->irq is zero, i.e., no interrupt requested at all. Without interrupt, adv7511_wait_for_edid() could return -EIO sometimes, because it polls adv7511->edid_read flag by calling adv7511_irq_process() a few times, but adv7511_irq_process() happens to refuse to handle interrupt by returning -ENODATA. Hence, EDID retrieval fails randomly. Fix the issue by checking adv7511->i2c_main->irq before exiting interrupt handling from adv7511_irq_process(). Fixes: f3d9683346d6 ("drm/bridge: adv7511: Allow IRQ to share GPIO pins") Signed-off-by: Liu Ying --- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 6089b0bb9321..2074fa3c1b7b 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -479,7 +479,8 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) return ret; /* If there is no IRQ to handle, exit indicating no IRQ data */ -if (!(irq0 & (ADV7511_INT0_HPD | ADV7511_INT0_EDID_READY)) && +if (adv7511->i2c_main->irq && +!(irq0 & (ADV7511_INT0_HPD | ADV7511_INT0_EDID_READY)) && !(irq1 & ADV7511_INT1_DDC_ERROR)) return -ENODATA; I think it might be better to handle -ENODATA in adv7511_wait_for_edid() instead. WDYT? I think this is may deserve another patch. My point is that the IRQ handler is fine to remove -ENODATA here, [...] there is no pending IRQ that can be handled. But there may has other things need to do in the adv7511_irq_process() function. So instead of continuing the execution when we know that IRQ bits are not set, Even when IRQ bits are not set, it just means that there is no HPD and no EDID ready-to-read signal. HDMI CEC interrupts still need to process. it's better to ignore -ENODATA in the calling code and go on with msleep(). So, It's confusing to ignore the -ENODATA here. -- Best regards Sui
Re: [PATCH] drm/bridge: adv7511: Exit interrupt handling when necessary
Hi, On 5/20/24 06:11, Dmitry Baryshkov wrote: On Thu, May 16, 2024 at 06:10:06PM +0800, Liu Ying wrote: Commit f3d9683346d6 ("drm/bridge: adv7511: Allow IRQ to share GPIO pins") fails to consider the case where adv7511->i2c_main->irq is zero, i.e., no interrupt requested at all. Without interrupt, adv7511_wait_for_edid() could return -EIO sometimes, because it polls adv7511->edid_read flag by calling adv7511_irq_process() a few times, but adv7511_irq_process() happens to refuse to handle interrupt by returning -ENODATA. Hence, EDID retrieval fails randomly. Fix the issue by checking adv7511->i2c_main->irq before exiting interrupt handling from adv7511_irq_process(). Fixes: f3d9683346d6 ("drm/bridge: adv7511: Allow IRQ to share GPIO pins") Signed-off-by: Liu Ying --- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 6089b0bb9321..2074fa3c1b7b 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -479,7 +479,8 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) return ret; /* If there is no IRQ to handle, exit indicating no IRQ data */ - if (!(irq0 & (ADV7511_INT0_HPD | ADV7511_INT0_EDID_READY)) && + if (adv7511->i2c_main->irq && + !(irq0 & (ADV7511_INT0_HPD | ADV7511_INT0_EDID_READY)) && !(irq1 & ADV7511_INT1_DDC_ERROR)) return -ENODATA; I think it might be better to handle -ENODATA in adv7511_wait_for_edid() instead. WDYT? I think this is may deserve another patch. -- Best regards Sui
[etnaviv-next v14 8/8] drm/etnaviv: Add support for vivante GPU cores attached via PCIe device
Previouly, the component framework is being used to bind multiple platform GPU devices to a virtual master. The virtual master is manually created by the driver, and is also a platform device. This is fine and works well for various SoCs, yet there some hardware venders integrate Vivante GPU cores into PCIe card and the driver lacks the support for PCIe devices. Create virtual platform devices as a representation for each GPU IP core, the manually created platform devices are functional as subcomponent, and all of them are child of the PCIe master device. The master is real for PCIe devices, as the PCIe device has already been created by the time the etnaviv.ko is loaded. Hence, bind all of the virtual child to the real master, this design reflects the hardware layout perfectly and is extensible. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/Kconfig | 9 ++ drivers/gpu/drm/etnaviv/Makefile | 2 + drivers/gpu/drm/etnaviv/etnaviv_drv.c | 12 +- drivers/gpu/drm/etnaviv/etnaviv_drv.h | 2 + drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 75 -- drivers/gpu/drm/etnaviv/etnaviv_gpu.h | 4 + drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c | 161 ++ drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h | 44 ++ 8 files changed, 293 insertions(+), 16 deletions(-) create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h diff --git a/drivers/gpu/drm/etnaviv/Kconfig b/drivers/gpu/drm/etnaviv/Kconfig index faa7fc68b009..7cb44f72d512 100644 --- a/drivers/gpu/drm/etnaviv/Kconfig +++ b/drivers/gpu/drm/etnaviv/Kconfig @@ -15,6 +15,15 @@ config DRM_ETNAVIV help DRM driver for Vivante GPUs. +config DRM_ETNAVIV_PCI_DRIVER + bool "enable ETNAVIV PCI driver support" + depends on DRM_ETNAVIV + depends on PCI + default n + help + Compile in support for Vivante GPUs attached via PCIe card. + Say Y if you have such hardwares. + config DRM_ETNAVIV_THERMAL bool "enable ETNAVIV thermal throttling" depends on DRM_ETNAVIV diff --git a/drivers/gpu/drm/etnaviv/Makefile b/drivers/gpu/drm/etnaviv/Makefile index 46e5ffad69a6..6829e1ebf2db 100644 --- a/drivers/gpu/drm/etnaviv/Makefile +++ b/drivers/gpu/drm/etnaviv/Makefile @@ -16,4 +16,6 @@ etnaviv-y := \ etnaviv_perfmon.o \ etnaviv_sched.o +etnaviv-$(CONFIG_DRM_ETNAVIV_PCI_DRIVER) += etnaviv_pci_drv.o + obj-$(CONFIG_DRM_ETNAVIV) += etnaviv.o diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index dc3556aad134..90ee60b00c24 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -24,6 +24,7 @@ #include "etnaviv_gpu.h" #include "etnaviv_gem.h" #include "etnaviv_mmu.h" +#include "etnaviv_pci_drv.h" #include "etnaviv_perfmon.h" /* @@ -568,6 +569,10 @@ static int etnaviv_bind(struct device *dev) if (ret < 0) goto out_free_priv; + ret = etnaviv_register_irq_handler(dev, priv); + if (ret) + goto out_unbind; + load_gpu(drm); ret = drm_dev_register(drm, 0); @@ -596,7 +601,7 @@ static void etnaviv_unbind(struct device *dev) etnaviv_private_fini(priv); } -static const struct component_master_ops etnaviv_master_ops = { +const struct component_master_ops etnaviv_master_ops = { .bind = etnaviv_bind, .unbind = etnaviv_unbind, }; @@ -740,6 +745,10 @@ static int __init etnaviv_init(void) if (ret != 0) goto unregister_gpu_driver; + ret = etnaviv_register_pci_driver(); + if (ret) + goto unregister_platform_driver; + /* * If the DT contains at least one available GPU device, instantiate * the DRM platform device. @@ -769,6 +778,7 @@ module_init(etnaviv_init); static void __exit etnaviv_exit(void) { etnaviv_destroy_platform_device(&etnaviv_drm); + etnaviv_unregister_pci_driver(); platform_driver_unregister(&etnaviv_platform_driver); platform_driver_unregister(&etnaviv_gpu_driver); } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index 4612843ff9f6..6db26d384cbe 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -27,6 +27,8 @@ struct etnaviv_gem_object; struct etnaviv_gem_submit; struct etnaviv_iommu_global; +extern const struct component_master_ops etnaviv_master_ops; + #define ETNAVIV_SOFTPIN_START_ADDRESS SZ_4M /* must be >= SUBALLOC_SIZE */ struct etnaviv_file_private { diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 3a14e187388a..2b5955693fbb 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -1
[etnaviv-next v14 7/8] drm/etnaviv: Allow creating subdevices and pass platform specific data
Because some hardware are too complex to be managed by a monolithic driver, a split of the functionality into child devices can helps to achieve better modularity. We will use this function to create subdevice as a repensentation of a single hardware ip block, so that the same modular approach that works for ARM-SoC can also works for PCIe cards. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_drv.c | 33 +++ drivers/gpu/drm/etnaviv/etnaviv_drv.h | 9 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 863faac2ea19..dc3556aad134 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -670,16 +670,36 @@ static struct platform_driver etnaviv_platform_driver = { }, }; -static int etnaviv_create_platform_device(const char *name, - struct platform_device **ppdev) +int etnaviv_create_platform_device(struct device *parent, + const char *name, int id, + struct resource *pres, + void *data, + struct platform_device **ppdev) { struct platform_device *pdev; int ret; - pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE); + pdev = platform_device_alloc(name, id); if (!pdev) return -ENOMEM; + pdev->dev.parent = parent; + + if (pres) { + ret = platform_device_add_resources(pdev, pres, 1); + if (ret) { + platform_device_put(pdev); + return ret; + } + } + + if (data) { + void *pdata = kmalloc(sizeof(void *), GFP_KERNEL); + + *(void **)pdata = data; + pdev->dev.platform_data = pdata; + } + ret = platform_device_add(pdev); if (ret) { platform_device_put(pdev); @@ -691,7 +711,7 @@ static int etnaviv_create_platform_device(const char *name, return 0; } -static void etnaviv_destroy_platform_device(struct platform_device **ppdev) +void etnaviv_destroy_platform_device(struct platform_device **ppdev) { struct platform_device *pdev = *ppdev; @@ -728,7 +748,10 @@ static int __init etnaviv_init(void) if (np) { of_node_put(np); - ret = etnaviv_create_platform_device("etnaviv", &etnaviv_drm); + ret = etnaviv_create_platform_device(NULL, "etnaviv", +PLATFORM_DEVID_NONE, +NULL, NULL, +&etnaviv_drm); if (ret) goto unregister_platform_driver; } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index 4b59fdb457b7..4612843ff9f6 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -98,6 +99,14 @@ bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu, u32 *stream, unsigned int size, struct drm_etnaviv_gem_submit_reloc *relocs, unsigned int reloc_size); +int etnaviv_create_platform_device(struct device *parent, + const char *name, int id, + struct resource *pres, + void *data, + struct platform_device **ppdev); + +void etnaviv_destroy_platform_device(struct platform_device **ppdev); + #ifdef CONFIG_DEBUG_FS void etnaviv_gem_describe_objects(struct etnaviv_drm_private *priv, struct seq_file *m); -- 2.34.1
[etnaviv-next v14 6/8] drm/etnaviv: Replace the '&pdev->dev' with 'dev'
In the etnaviv_pdev_probe(), etnaviv_gpu_platform_probe() function, the value of '&pdev->dev' has been cached to the 'dev' local auto variable. But part of callers use 'dev' as argument, while the rest use '&pdev->dev'. To keep it consistent, use 'dev' uniformly. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_drv.c | 10 +- drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 16 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 986fd68b489a..863faac2ea19 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -614,7 +614,7 @@ static int etnaviv_pdev_probe(struct platform_device *pdev) if (!of_device_is_available(core_node)) continue; - drm_of_component_match_add(&pdev->dev, &match, + drm_of_component_match_add(dev, &match, component_compare_of, core_node); } } else { @@ -637,9 +637,9 @@ static int etnaviv_pdev_probe(struct platform_device *pdev) * bit to make sure we are allocating the command buffers and * TLBs in the lower 4 GiB address space. */ - if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)) || - dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32))) { - dev_dbg(&pdev->dev, "No suitable DMA available\n"); + if (dma_set_mask(dev, DMA_BIT_MASK(40)) || + dma_set_coherent_mask(dev, DMA_BIT_MASK(32))) { + dev_dbg(dev, "No suitable DMA available\n"); return -ENODEV; } @@ -650,7 +650,7 @@ static int etnaviv_pdev_probe(struct platform_device *pdev) */ first_node = etnaviv_of_first_available_node(); if (first_node) { - of_dma_configure(&pdev->dev, first_node, true); + of_dma_configure(dev, first_node, true); of_node_put(first_node); } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index aa15682f94db..3a14e187388a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -1891,7 +1891,7 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) if (!gpu) return -ENOMEM; - gpu->dev = &pdev->dev; + gpu->dev = dev; mutex_init(&gpu->lock); mutex_init(&gpu->sched_lock); @@ -1905,8 +1905,8 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) if (gpu->irq < 0) return gpu->irq; - err = devm_request_irq(&pdev->dev, gpu->irq, irq_handler, 0, - dev_name(gpu->dev), gpu); + err = devm_request_irq(dev, gpu->irq, irq_handler, 0, + dev_name(dev), gpu); if (err) { dev_err(dev, "failed to request IRQ%u: %d\n", gpu->irq, err); return err; @@ -1925,13 +1925,13 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) * autosuspend delay is rather arbitary: no measurements have * yet been performed to determine an appropriate value. */ - pm_runtime_use_autosuspend(gpu->dev); - pm_runtime_set_autosuspend_delay(gpu->dev, 200); - pm_runtime_enable(gpu->dev); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 200); + pm_runtime_enable(dev); - err = component_add(&pdev->dev, &gpu_ops); + err = component_add(dev, &gpu_ops); if (err < 0) { - dev_err(&pdev->dev, "failed to register component: %d\n", err); + dev_err(dev, "failed to register component: %d\n", err); return err; } -- 2.34.1
[etnaviv-next v14 5/8] drm/etnaviv: Add support for cached coherent caching mode
Many modern CPUs and/or platforms choose to define their peripheral devices as cached coherent by default, to be specific, the PCH is capable of snooping CPU's cache. When hit the peripheral devices will access data directly from CPU's cache. This means that device drivers do not need to maintain the coherency issue between a processor and peripheral I/O for the cached buffers. Hence, it dosen't need us to sync manually on the software side, which is useful to avoid some overheads, especially for userspace, but userspace is not known yet. Probe the hardware maintained cached coherent support of the host platform with the dev_is_dma_coherent() function, and store the result in struct etnaviv_drm_private. As this is a platform implementation-defined hardware feature and again is meant to be shared by all GPU cores. And expose it via etnaviv parameter mechanism to let userspace know. Please note that write-combine mapping out of scope of the discussion and therefore is not being addressed. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_drv.c | 3 +++ drivers/gpu/drm/etnaviv/etnaviv_drv.h | 9 + drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 4 include/uapi/drm/etnaviv_drm.h| 1 + 4 files changed, 17 insertions(+) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index e3eb31ba9a2b..986fd68b489a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -57,6 +58,8 @@ static int etnaviv_private_init(struct device *dev, return -ENOMEM; } + priv->cached_coherent = dev_is_dma_coherent(dev); + return 0; } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index 1f9b50b5a6aa..4b59fdb457b7 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -46,6 +46,15 @@ struct etnaviv_drm_private { struct xarray active_contexts; u32 next_context_id; + /* +* If true, the cached mapping is consistent for all CPU cores and +* peripheral bus masters in the system. It means that both of the +* CPU and GPU will see the same data if the buffer being accessed +* is cached. And the coherency is guaranteed by the host platform +* specific hardwares. +*/ + bool cached_coherent; + /* list of GEM objects: */ struct mutex gem_lock; struct list_head gem_list; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 02d7efdc82c0..aa15682f94db 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -164,6 +164,10 @@ int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value) *value = gpu->identity.eco_id; break; + case ETNAVIV_PARAM_CACHED_COHERENT: + *value = priv->cached_coherent; + break; + default: DBG("%s: invalid param: %u", dev_name(gpu->dev), param); return -EINVAL; diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h index af024d90453d..61eaa8cd0f5e 100644 --- a/include/uapi/drm/etnaviv_drm.h +++ b/include/uapi/drm/etnaviv_drm.h @@ -77,6 +77,7 @@ struct drm_etnaviv_timespec { #define ETNAVIV_PARAM_GPU_PRODUCT_ID0x1c #define ETNAVIV_PARAM_GPU_CUSTOMER_ID 0x1d #define ETNAVIV_PARAM_GPU_ECO_ID0x1e +#define ETNAVIV_PARAM_CACHED_COHERENT 0x1f #define ETNA_MAX_PIPES 4 -- 2.34.1
[etnaviv-next v14 4/8] drm/etnaviv: Fix wrong cache property being used for vmap()
In the etnaviv_gem_vmap_impl() function, the driver vmap whatever buffers with Write-Combine page property. This is unreasonable, as some platforms are cached coherent. And cached buffers should be mapped with cached page property. Fixes: a0a5ab3e99b8 ("drm/etnaviv: call correct function when trying to vmap a DMABUF") Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_gem.c | 16 ++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index aa95a5e98374..eed98bb9e446 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -342,6 +342,7 @@ void *etnaviv_gem_vmap(struct drm_gem_object *obj) static void *etnaviv_gem_vmap_impl(struct etnaviv_gem_object *obj) { struct page **pages; + pgprot_t prot; lockdep_assert_held(&obj->lock); @@ -349,8 +350,19 @@ static void *etnaviv_gem_vmap_impl(struct etnaviv_gem_object *obj) if (IS_ERR(pages)) return NULL; - return vmap(pages, obj->base.size >> PAGE_SHIFT, - VM_MAP, pgprot_writecombine(PAGE_KERNEL)); + switch (obj->flags) { + case ETNA_BO_CACHED: + prot = PAGE_KERNEL; + break; + case ETNA_BO_UNCACHED: + prot = pgprot_noncached(PAGE_KERNEL); + break; + case ETNA_BO_WC: + default: + prot = pgprot_writecombine(PAGE_KERNEL); + } + + return vmap(pages, obj->base.size >> PAGE_SHIFT, VM_MAP, prot); } static inline enum dma_data_direction etnaviv_op_to_dma_dir(u32 op) -- 2.34.1
[etnaviv-next v14 3/8] drm/etnaviv: Embed struct drm_device into struct etnaviv_drm_private
Both the instance of struct drm_device and the instance of struct etnaviv_drm_private are intended to be shared by all GPU cores, both have only one instance created across drm/etnaviv driver. After embedded in, the whole structure can be allocated with devm_drm_dev_alloc(). And the DRM device created is automatically put on driver detach, so we don't need to call drm_dev_put() explicitly on driver leave. It's also eliminate the need to use the .dev_private member, which is deprecated according to the drm document. We can also use container_of() to retrieve pointer for the containing structure. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_drv.c| 65 drivers/gpu/drm/etnaviv/etnaviv_drv.h| 7 +++ drivers/gpu/drm/etnaviv/etnaviv_gem.c| 6 +- drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c | 2 +- drivers/gpu/drm/etnaviv/etnaviv_gpu.c| 6 +- drivers/gpu/drm/etnaviv/etnaviv_mmu.c| 4 +- 6 files changed, 40 insertions(+), 50 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 22c78bc944c4..e3eb31ba9a2b 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -41,14 +41,9 @@ static struct device_node *etnaviv_of_first_available_node(void) return NULL; } -static struct etnaviv_drm_private *etnaviv_alloc_private(struct device *dev) +static int etnaviv_private_init(struct device *dev, + struct etnaviv_drm_private *priv) { - struct etnaviv_drm_private *priv; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return ERR_PTR(-ENOMEM); - xa_init_flags(&priv->active_contexts, XA_FLAGS_ALLOC); mutex_init(&priv->gem_lock); @@ -58,15 +53,14 @@ static struct etnaviv_drm_private *etnaviv_alloc_private(struct device *dev) priv->cmdbuf_suballoc = etnaviv_cmdbuf_suballoc_new(dev); if (IS_ERR(priv->cmdbuf_suballoc)) { - kfree(priv); dev_err(dev, "Failed to create cmdbuf suballocator\n"); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } - return priv; + return 0; } -static void etnaviv_free_private(struct etnaviv_drm_private *priv) +static void etnaviv_private_fini(struct etnaviv_drm_private *priv) { if (!priv) return; @@ -76,13 +70,11 @@ static void etnaviv_free_private(struct etnaviv_drm_private *priv) etnaviv_cmdbuf_suballoc_destroy(priv->cmdbuf_suballoc); xa_destroy(&priv->active_contexts); - - kfree(priv); } static void load_gpu(struct drm_device *dev) { - struct etnaviv_drm_private *priv = dev->dev_private; + struct etnaviv_drm_private *priv = to_etnaviv_priv(dev); unsigned int i; for (i = 0; i < ETNA_MAX_PIPES; i++) { @@ -100,7 +92,7 @@ static void load_gpu(struct drm_device *dev) static int etnaviv_open(struct drm_device *dev, struct drm_file *file) { - struct etnaviv_drm_private *priv = dev->dev_private; + struct etnaviv_drm_private *priv = to_etnaviv_priv(dev); struct etnaviv_file_private *ctx; int ret, i; @@ -143,7 +135,7 @@ static int etnaviv_open(struct drm_device *dev, struct drm_file *file) static void etnaviv_postclose(struct drm_device *dev, struct drm_file *file) { - struct etnaviv_drm_private *priv = dev->dev_private; + struct etnaviv_drm_private *priv = to_etnaviv_priv(dev); struct etnaviv_file_private *ctx = file->driver_priv; unsigned int i; @@ -168,7 +160,7 @@ static void etnaviv_postclose(struct drm_device *dev, struct drm_file *file) #ifdef CONFIG_DEBUG_FS static int etnaviv_gem_show(struct drm_device *dev, struct seq_file *m) { - struct etnaviv_drm_private *priv = dev->dev_private; + struct etnaviv_drm_private *priv = to_etnaviv_priv(dev); etnaviv_gem_describe_objects(priv, m); @@ -262,7 +254,7 @@ static int show_each_gpu(struct seq_file *m, void *arg) { struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; - struct etnaviv_drm_private *priv = dev->dev_private; + struct etnaviv_drm_private *priv = to_etnaviv_priv(dev); struct etnaviv_gpu *gpu; int (*show)(struct etnaviv_gpu *gpu, struct seq_file *m) = node->info_ent->data; @@ -305,7 +297,7 @@ static void etnaviv_debugfs_init(struct drm_minor *minor) static int etnaviv_ioctl_get_param(struct drm_device *dev, void *data, struct drm_file *file) { - struct etnaviv_drm_private *priv = dev->dev_private; + struct etnaviv_drm_private *priv = to_etnaviv_priv(dev); struct drm_etnaviv_param *args = data; struct etnaviv_gpu *gpu; @@
[etnaviv-next v14 2/8] drm/etnaviv: Add constructor and destructor for the etnaviv_drm_private structure
Because there are a lot of data members in the struct etnaviv_drm_private, which are intended to be shared by all GPU cores. It can be lengthy and daunting on error handling, the 'gem_lock' of struct etnaviv_drm_private just be forgeten to destroy on driver leave. Switch to use the dedicated helpers introduced, etnaviv_bind() and etnaviv_unbind() gets simplified. Another potential benefit is that we could put the struct drm_device into struct etnaviv_drm_private in the future, which made them share the same life time. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/etnaviv/etnaviv_drv.c | 72 +-- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 6500f3999c5f..22c78bc944c4 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -41,6 +41,45 @@ static struct device_node *etnaviv_of_first_available_node(void) return NULL; } +static struct etnaviv_drm_private *etnaviv_alloc_private(struct device *dev) +{ + struct etnaviv_drm_private *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return ERR_PTR(-ENOMEM); + + xa_init_flags(&priv->active_contexts, XA_FLAGS_ALLOC); + + mutex_init(&priv->gem_lock); + INIT_LIST_HEAD(&priv->gem_list); + priv->num_gpus = 0; + priv->shm_gfp_mask = GFP_HIGHUSER | __GFP_RETRY_MAYFAIL | __GFP_NOWARN; + + priv->cmdbuf_suballoc = etnaviv_cmdbuf_suballoc_new(dev); + if (IS_ERR(priv->cmdbuf_suballoc)) { + kfree(priv); + dev_err(dev, "Failed to create cmdbuf suballocator\n"); + return ERR_PTR(-ENOMEM); + } + + return priv; +} + +static void etnaviv_free_private(struct etnaviv_drm_private *priv) +{ + if (!priv) + return; + + mutex_destroy(&priv->gem_lock); + + etnaviv_cmdbuf_suballoc_destroy(priv->cmdbuf_suballoc); + + xa_destroy(&priv->active_contexts); + + kfree(priv); +} + static void load_gpu(struct drm_device *dev) { struct etnaviv_drm_private *priv = dev->dev_private; @@ -521,35 +560,21 @@ static int etnaviv_bind(struct device *dev) if (IS_ERR(drm)) return PTR_ERR(drm); - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(dev, "failed to allocate private data\n"); - ret = -ENOMEM; + priv = etnaviv_alloc_private(dev); + if (IS_ERR(priv)) { + ret = PTR_ERR(priv); goto out_put; } + drm->dev_private = priv; dma_set_max_seg_size(dev, SZ_2G); - xa_init_flags(&priv->active_contexts, XA_FLAGS_ALLOC); - - mutex_init(&priv->gem_lock); - INIT_LIST_HEAD(&priv->gem_list); - priv->num_gpus = 0; - priv->shm_gfp_mask = GFP_HIGHUSER | __GFP_RETRY_MAYFAIL | __GFP_NOWARN; - - priv->cmdbuf_suballoc = etnaviv_cmdbuf_suballoc_new(drm->dev); - if (IS_ERR(priv->cmdbuf_suballoc)) { - dev_err(drm->dev, "Failed to create cmdbuf suballocator\n"); - ret = PTR_ERR(priv->cmdbuf_suballoc); - goto out_free_priv; - } - dev_set_drvdata(dev, drm); ret = component_bind_all(dev, drm); if (ret < 0) - goto out_destroy_suballoc; + goto out_free_priv; load_gpu(drm); @@ -561,10 +586,8 @@ static int etnaviv_bind(struct device *dev) out_unbind: component_unbind_all(dev, drm); -out_destroy_suballoc: - etnaviv_cmdbuf_suballoc_destroy(priv->cmdbuf_suballoc); out_free_priv: - kfree(priv); + etnaviv_free_private(priv); out_put: drm_dev_put(drm); @@ -580,12 +603,9 @@ static void etnaviv_unbind(struct device *dev) component_unbind_all(dev, drm); - etnaviv_cmdbuf_suballoc_destroy(priv->cmdbuf_suballoc); - - xa_destroy(&priv->active_contexts); + etnaviv_free_private(priv); drm->dev_private = NULL; - kfree(priv); drm_dev_put(drm); } -- 2.34.1