diff options
| author | Dave Airlie <airlied@redhat.com> | 2026-09-08 16:51:58 +1000 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2026-09-08 21:05:35 +1000 |
| commit | 483ece619b64a6596bdaf69479ea0ecbbd197a00 (patch) | |
| tree | 44af1491a66bbce99c99396405552afb77f4512f | |
| parent | 94376186f25bfb86842c9b7551d223415cc6e4f0 (diff) | |
| parent | 99c95ce1b07081d7944d637ba7d72d835c0d520a (diff) | |
| download | linux-next-483ece619b64a6596bdaf69479ea0ecbbd197a00.tar.gz linux-next-483ece619b64a6596bdaf69479ea0ecbbd197a00.zip | |
Merge tag 'drm-misc-next-2026-09-03' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for v7.4:
UAPI Changes:
colorop:
- provide DRM_COLOR_OP_FIXED_MATRIX
Cross-subsystem Changes:
cgroups:
- fix typos
dma-buf:
- fix typos
sound:
- adapt to changes in omapdrm
Core Changes:
atomic:
- convert most of DRM from state reset callbacks to atomic_create_state
- remove drm_simple_encoder_init(); update drivers
buddy:
- improve dirty-page tracking
clients:
- log: Improve vmap handling
display:
- export HDMI SCDC status data via debugfs
edid:
- parse AMD VSDB entries
- parse ALLM/VRR capabilities
log:
- add drm_warn_ratelimited()
sched:
- add missing locking
Driver Changes:
amd:
- display: Use AMD VSDB for FreeSync
- display: Implement YUV-to-RGB with fixed-matrix colorop
amdxdma:
- various fixes
ast:
- support 256-byte EDID data
bridge:
- clean up redundant error reporting
- ti-sn65dsi83: Simplify error condition logic
hyperv_drm:
- remove support for pre-Win10 hosts
komeda:
- fix usage of GLB_CORE_ID
nouveau:
- improve runtime PM on R570 GSP firmware
- various fixes throughout the driver
- dispnv50: Support 2.147 GHz pixel clock in GB20x
omap:
- report HDMI hotplug events to ASoC HDMI codec
panel:
- panel-edp: Support MNE007QS3-F, TM140VDXP15, and KD116N36-30NB-A001
- samsung-s6d16d0: Use mipi_dsi_*_multi() functions
- support Ilitek ILI7836A OLED plus DT bindings
- support Novatek NT36532 plus DT bindings
- convert several drivers to managed cleanup
- fix Kconfig selections
panthor:
- provide gpu_cache_flush tracepoint
- improve dma_fence signalling latency
- improve locking
qaic:
- reject BOs that exceed maximum page count
- add missing include statements
verisilicon:
- fix hardware cursor offsets
vkms:
- implement YUV-to-RGB with fixed-matrix colorop
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patch.msgid.link/20260903130548.GA91506@2a02-2455-9062-2500-3419-2212-e55c-8a45.dyn6.pyur.net
272 files changed, 5621 insertions, 3288 deletions
diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili7836a.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili7836a.yaml new file mode 100644 index 000000000000..962cf257ed8d --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili7836a.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/ilitek,ili7836a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ilitek ILI7836A-based OLED panels + +maintainers: + - Neil Armstrong <neil.armstrong@linaro.org> + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - const: retroidpocket,nova-panel + - const: ilitek,ili7836a + + reg: + maxItems: 1 + description: DSI virtual channel + + vddio-supply: true + avdd-supply: true + + backlight: true + port: true + reset-gpios: true + rotation: true + +required: + - compatible + - reg + - vddio-supply + - avdd-supply + - reset-gpios + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + compatible = "retroidpocket,nova-panel", "ilitek,ili7836a"; + reg = <0>; + vddio-supply = <&vdd_disp_1v8>; + avdd-supply = <&vdd_disp2_2v8>; + reset-gpios = <&tlmm 133 GPIO_ACTIVE_HIGH>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/novatek,nt36532.yaml b/Documentation/devicetree/bindings/display/panel/novatek,nt36532.yaml new file mode 100644 index 000000000000..c1ab793dd718 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/novatek,nt36532.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/novatek,nt36532.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Novatek NT36532 based DSI display Panels + +maintainers: + - Junjie Cao <junjie.cao@linux.dev> + +description: | + The NT36532 IC from Novatek is a Driver IC used to drive MIPI-DSI panels. + +allOf: + - $ref: panel-common-dual.yaml# + +properties: + compatible: + items: + - enum: + - csot,ppc100hb1-1 + - const: novatek,nt36532 + + reg: + maxItems: 1 + + reset-gpios: + maxItems: 1 + description: phandle of gpio for reset line - This should be 8mA + + vddio-supply: + description: regulator that supplies the I/O voltage + + ports: true + +required: + - compatible + - reg + - vddio-supply + - reset-gpios + - ports + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "csot,ppc100hb1-1", "novatek,nt36532"; + reg = <0>; + + reset-gpios = <&tlmm 133 GPIO_ACTIVE_LOW>; + + vddio-supply = <&vreg_l12b_1p8>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + panel_in_0: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + panel_in_1: endpoint { + remote-endpoint = <&dsi1_out>; + }; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa5x01-ams561ra01.yaml b/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa5x01-ams561ra01.yaml index b271de575e15..f04702de1101 100644 --- a/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa5x01-ams561ra01.yaml +++ b/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa5x01-ams561ra01.yaml @@ -34,7 +34,7 @@ required: - compatible - reg -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 14cf37590fc7..b7351467dc74 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -29,21 +29,6 @@ refactorings already and are an expert in the specific area Subsystem-wide refactorings =========================== -Open-code drm_simple_encoder_init() ------------------------------------ - -The helper drm_simple_encoder_init() was supposed to simplify encoder -initialization. Instead it only added an intermediate layer between atomic -modesetting and the DRM driver. - -The task here is to remove drm_simple_encoder_init(). Search for a driver -that calls drm_simple_encoder_init() and inline the helper. The driver will -also need its own instance of drm_encoder_funcs. - -Contact: Thomas Zimmermann, respective driver maintainer - -Level: Easy - Replace struct drm_simple_display_pipe with regular atomic helpers ------------------------------------------------------------------ diff --git a/MAINTAINERS b/MAINTAINERS index 248688a344f6..6abef8f1a59c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8340,6 +8340,13 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: Documentation/devicetree/bindings/display/panel/novatek,nt36523.yaml F: drivers/gpu/drm/panel/panel-novatek-nt36523.c +DRM DRIVER FOR NOVATEK NT36532 PANELS +M: Junjie Cao <junjie.cao@linux.dev> +S: Maintained +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: Documentation/devicetree/bindings/display/panel/novatek,nt36532.yaml +F: drivers/gpu/drm/panel/panel-novatek-nt36532.c + DRM DRIVER FOR NOVATEK NT36672A PANELS M: Sumit Semwal <sumit.semwal@linaro.org> S: Maintained @@ -9069,6 +9076,21 @@ F: include/drm/drm_gpuvm.h F: rust/helpers/drm_gpuvm.c F: rust/kernel/drm/gpuvm/ +DRM GPUSVM +M: Matthew Brost <matthew.brost@intel.com> +R: Thomas Hellström <thomas.hellstrom@linux.intel.com> +R: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com> +R: Honglei Huang <honghuan@amd.com> +L: dri-devel@lists.freedesktop.org +S: Supported +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: drivers/gpu/drm/drm_gpusvm.c +F: drivers/gpu/drm/drm_pagemap.c +F: drivers/gpu/drm/drm_pagemap_util.c +F: include/drm/drm_gpusvm.h +F: include/drm/drm_pagemap.h +F: include/drm/drm_pagemap_util.h + DRM LOG M: Jocelyn Falempe <jfalempe@redhat.com> M: Javier Martinez Canillas <javierm@redhat.com> diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index baf9a8b90a4d..164441e43940 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -799,6 +799,7 @@ free_cmd_bufs: for (i = 0; i < ARRAY_SIZE(priv->cmd_buf); i++) { if (!priv->cmd_buf[i]) continue; + amdxdna_gem_heap_free(client, priv->cmd_buf[i]); drm_gem_object_put(to_gobj(priv->cmd_buf[i])); } amdxdna_gem_unpin(heap); @@ -836,8 +837,14 @@ void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx) drm_sched_fini(&hwctx->priv->sched); aie2_ctx_syncobj_destroy(hwctx); - for (idx = 0; idx < ARRAY_SIZE(hwctx->priv->cmd_buf); idx++) + for (idx = 0; idx < ARRAY_SIZE(hwctx->priv->cmd_buf); idx++) { + /* + * The open/close will never be called for driver allocated + * dev bo. Call amdxdna_gem_heap_free explicitly. + */ + amdxdna_gem_heap_free(hwctx->client, hwctx->priv->cmd_buf[idx]); drm_gem_object_put(to_gobj(hwctx->priv->cmd_buf[idx])); + } amdxdna_gem_unpin(hwctx->priv->heap); drm_gem_object_put(to_gobj(hwctx->priv->heap)); diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 4b0d58d0329b..3f05fa8811af 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -37,6 +37,7 @@ amdxdna_init_dev_bo(struct amdxdna_gem_obj *dev_bo) struct amdxdna_gem_obj *heap; u64 heap_addr, exp_heap_uva; u32 heap_id; + int ret; if (xa_empty(&client->dev_heap_xa)) { XDNA_DBG(xdna, "Empty heap xa"); @@ -58,24 +59,32 @@ amdxdna_init_dev_bo(struct amdxdna_gem_obj *dev_bo) heap = xa_load(&client->dev_heap_xa, heap_id); exp_heap_uva = amdxdna_gem_uva(heap); heap_addr = amdxdna_gem_dev_addr(heap); - dev_bo->heap_start_id = heap_id; dev_bo->mem.uva = dev_bo->mm_node.start - heap_addr + exp_heap_uva; for (; heap_id < client->dev_heap_nid; heap_id++) { heap = xa_load(&client->dev_heap_xa, heap_id); if (!heap) { XDNA_ERR(xdna, "Failed to load heap %d", heap_id); - return -EINVAL; + ret = -EINVAL; + goto cleanup_heap_xa; } heap_addr = amdxdna_gem_uva(heap); if (heap_addr == AMDXDNA_INVALID_ADDR) { XDNA_ERR(xdna, "Heap %d is not mapped", heap_id); - return -EAGAIN; + ret = -EAGAIN; + goto cleanup_heap_xa; } if (heap_addr != exp_heap_uva) { XDNA_ERR(xdna, "Heap %d uva is not contiguous", heap_id); - return -EINVAL; + ret = -EINVAL; + goto cleanup_heap_xa; + } + + ret = xa_insert(&dev_bo->heap_xa, heap_id, heap, GFP_KERNEL); + if (ret) { + ret = -ENOMEM; + goto cleanup_heap_xa; } if (heap->dev_addr + heap->mem.size >= @@ -87,12 +96,15 @@ amdxdna_init_dev_bo(struct amdxdna_gem_obj *dev_bo) if (heap_id == client->dev_heap_nid) { XDNA_DBG(xdna, "Can not find heap end"); - return -EAGAIN; + ret = -EAGAIN; + goto cleanup_heap_xa; } - dev_bo->heap_end_id = heap_id; - return 0; + +cleanup_heap_xa: + xa_destroy(&dev_bo->heap_xa); + return ret; } static int @@ -132,8 +144,7 @@ amdxdna_gem_heap_alloc(struct amdxdna_gem_obj *abo) } client->heap_usage += mem->size; - xa_for_each_range(&client->dev_heap_xa, heap_id, heap, - abo->heap_start_id, abo->heap_end_id) + xa_for_each(&abo->heap_xa, heap_id, heap) drm_gem_object_get(to_gobj(heap)); unlock_out: @@ -142,22 +153,13 @@ unlock_out: return ret; } -static void -amdxdna_gem_heap_free(struct amdxdna_gem_obj *abo) +void amdxdna_gem_heap_free(struct amdxdna_client *client, struct amdxdna_gem_obj *abo) { - struct amdxdna_client *client = abo->client; - struct amdxdna_gem_obj *heap; - unsigned long heap_id; - mutex_lock(&client->mm_lock); drm_mm_remove_node(&abo->mm_node); client->heap_usage -= abo->mem.size; - xa_for_each_range(&client->dev_heap_xa, heap_id, heap, - abo->heap_start_id, abo->heap_end_id) - drm_gem_object_put(to_gobj(heap)); - mutex_unlock(&client->mm_lock); } @@ -180,6 +182,7 @@ amdxdna_gem_create_obj(struct drm_device *dev, size_t size) abo->open_ref = 0; abo->internal = false; INIT_LIST_HEAD(&abo->mem.umap_list); + xa_init_flags(&abo->heap_xa, XA_FLAGS_ALLOC); return abo; } @@ -253,13 +256,16 @@ static bool amdxdna_hmm_invalidate(struct mmu_interval_notifier *mni, struct amdxdna_gem_obj *abo = mapp->abo; struct amdxdna_dev *xdna; + if (!mmu_notifier_range_blockable(range)) + return false; + + if (mapp->unmapped) + return true; + xdna = to_xdna_dev(to_gobj(abo)->dev); XDNA_DBG(xdna, "Invalidating range 0x%lx, 0x%lx, type %d", mapp->range.start, mapp->range.end, abo->type); - if (!mmu_notifier_range_blockable(range)) - return false; - down_write(&xdna->notifier_lock); abo->mem.map_invalid = true; mapp->invalid = true; @@ -301,33 +307,40 @@ static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo, down_write(&xdna->notifier_lock); list_for_each_entry(mapp, &abo->mem.umap_list, node) { - if (!vma || compare_range(mapp, vma->vm_mm, vma->vm_start, vma->vm_end)) { - if (!mapp->unmapped) { - queue_work(xdna->notifier_wq, &mapp->hmm_unreg_work); - mapp->unmapped = true; - } - if (vma) - break; - } + if (!compare_range(mapp, vma->vm_mm, vma->vm_start, vma->vm_end)) + continue; + + queue_work(xdna->notifier_wq, &mapp->hmm_unreg_work); + mapp->unmapped = true; } up_write(&xdna->notifier_lock); } -static void amdxdna_umap_release(struct kref *ref) +static void amdxdna_hmm_unregister_all(struct amdxdna_gem_obj *abo) { - struct amdxdna_umap *mapp = container_of(ref, struct amdxdna_umap, refcnt); - struct amdxdna_gem_obj *abo = mapp->abo; - struct amdxdna_dev *xdna; - - mmu_interval_notifier_remove(&mapp->notifier); + struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev); + struct amdxdna_umap *mapp, *tmp; + LIST_HEAD(dead); - xdna = to_xdna_dev(to_gobj(mapp->abo)->dev); down_write(&xdna->notifier_lock); - list_del(&mapp->node); - if (list_empty(&abo->mem.umap_list)) - abo->mem.uva = AMDXDNA_INVALID_ADDR; + list_for_each_entry_safe(mapp, tmp, &abo->mem.umap_list, node) { + mapp->unmapped = true; + mapp->cleanup = true; + list_move(&mapp->node, &dead); + } up_write(&xdna->notifier_lock); + list_for_each_entry_safe(mapp, tmp, &dead, node) { + cancel_work_sync(&mapp->hmm_unreg_work); + amdxdna_umap_put(mapp); + } +} + +static void amdxdna_umap_release(struct kref *ref) +{ + struct amdxdna_umap *mapp = container_of(ref, struct amdxdna_umap, refcnt); + + mmu_interval_notifier_remove(&mapp->notifier); kvfree(mapp->range.hmm_pfns); kfree(mapp); } @@ -341,6 +354,20 @@ static void amdxdna_hmm_unreg_work(struct work_struct *work) { struct amdxdna_umap *mapp = container_of(work, struct amdxdna_umap, hmm_unreg_work); + struct amdxdna_gem_obj *abo = mapp->abo; + struct amdxdna_dev *xdna; + + xdna = to_xdna_dev(to_gobj(mapp->abo)->dev); + down_write(&xdna->notifier_lock); + if (mapp->cleanup) { + up_write(&xdna->notifier_lock); + return; + } + + list_del(&mapp->node); + if (list_empty(&abo->mem.umap_list)) + abo->mem.uva = AMDXDNA_INVALID_ADDR; + up_write(&xdna->notifier_lock); amdxdna_umap_put(mapp); } @@ -425,13 +452,18 @@ static void amdxdna_gem_dev_obj_free(struct drm_gem_object *gobj) { struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev); struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); + struct amdxdna_gem_obj *heap; + unsigned long heap_id; XDNA_DBG(xdna, "BO type %d xdna_addr 0x%llx", abo->type, amdxdna_gem_dev_addr(abo)); if (abo->pinned) amdxdna_gem_unpin(abo); amdxdna_gem_vunmap(abo); - amdxdna_gem_heap_free(abo); + xa_for_each(&abo->heap_xa, heap_id, heap) + drm_gem_object_put(to_gobj(heap)); + xa_destroy(&abo->heap_xa); + drm_gem_object_release(gobj); amdxdna_gem_destroy_obj(abo); } @@ -643,8 +675,7 @@ static void amdxdna_gem_obj_free(struct drm_gem_object *gobj) struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev); struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); - amdxdna_hmm_unregister(abo, NULL); - flush_workqueue(xdna->notifier_wq); + amdxdna_hmm_unregister_all(abo); if (abo->pinned) amdxdna_gem_unpin(abo); @@ -734,19 +765,72 @@ static void amdxdna_gem_obj_vunmap(struct drm_gem_object *obj, struct iosys_map drm_gem_shmem_object_vunmap(obj, map); } +static int amdxdna_gem_dev_obj_open(struct drm_gem_object *gobj, struct drm_file *filp) +{ + struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); + + guard(mutex)(&abo->lock); + if (filp->driver_priv != abo->client) + return -EPERM; + abo->open_ref++; + + return 0; +} + +static void amdxdna_gem_dev_obj_close(struct drm_gem_object *gobj, struct drm_file *filp) +{ + struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); + struct amdxdna_client *client = NULL; + + mutex_lock(&abo->lock); + abo->open_ref--; + + /* + * Freeing the heap allocation here, when the handle is closed, is + * intentional. DEV BOs are carved out of a per-client drm_mm heap; + * any subsequent allocation that lands on the same device address will + * also belong to the same client. If the user closes the handle while + * a job is still in flight the only consequence is self-inflicted + * corruption within their own context -- it cannot affect other + * processes. The GEM reference held by the in-flight job keeps the + * amdxdna_gem_obj struct alive until the job completes; it does not + * prevent the device address from being reclaimed by the allocator. + * + * Cross-process handle creation for DEV BOs is rejected in + * amdxdna_gem_dev_obj_open(), which prevents the following UAF: + * if a second process shared the handle via GEM flink and the + * original creator exited (freeing client), the importer would later + * reach open_ref == 0 here and call amdxdna_gem_heap_free() with a + * dangling abo->client pointer. Because cross-process opens are + * rejected, the process arriving here is always the owning client, + * which is still alive. abo->client is nulled out afterwards so that + * any code path running on a lingering GEM reference (e.g. an + * in-flight job) cannot silently dereference a stale pointer. + */ + if (abo->open_ref == 0) { + client = abo->client; + abo->client = NULL; + } + mutex_unlock(&abo->lock); + + if (client) + amdxdna_gem_heap_free(client, abo); +} + static int amdxdna_gem_dev_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map) { struct amdxdna_gem_obj *abo = to_xdna_obj(obj); struct amdxdna_gem_obj *heap; + unsigned long index = 0; void *base; u64 offset; - /* vmap dev bo which is across more than 1 heap is not allowed */ - if (abo->heap_start_id != abo->heap_end_id) + heap = xa_find(&abo->heap_xa, &index, ULONG_MAX, XA_PRESENT); + if (!heap) return -ENOMEM; - heap = xa_load(&abo->client->dev_heap_xa, abo->heap_start_id); - if (!heap) + /* vmap dev bo which is across more than 1 heap is not allowed */ + if (xa_find_after(&abo->heap_xa, &index, ULONG_MAX, XA_PRESENT)) return -ENOMEM; base = amdxdna_gem_vmap(heap); @@ -765,6 +849,8 @@ static struct dma_buf *amdxdna_gem_dev_obj_export(struct drm_gem_object *gobj, i static const struct drm_gem_object_funcs amdxdna_gem_dev_obj_funcs = { .free = amdxdna_gem_dev_obj_free, + .open = amdxdna_gem_dev_obj_open, + .close = amdxdna_gem_dev_obj_close, .vmap = amdxdna_gem_dev_obj_vmap, .export = amdxdna_gem_dev_obj_export, }; @@ -1103,6 +1189,8 @@ int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_f args->handle, args->type, amdxdna_gem_uva(abo), amdxdna_gem_dev_addr(abo), abo->mem.size); put_obj: + if (ret && abo->type == AMDXDNA_BO_DEV) + amdxdna_gem_heap_free(client, abo); /* Dereference object reference. Handle holds it now. */ drm_gem_object_put(to_gobj(abo)); return ret; @@ -1136,7 +1224,6 @@ static void amdxdna_bo_unpin(struct amdxdna_gem_obj *abo) int amdxdna_gem_pin_nolock(struct amdxdna_gem_obj *abo) { - struct amdxdna_client *client = abo->client; struct amdxdna_gem_obj *heap; unsigned long heap_id, last = ULONG_MAX; int ret = 0; @@ -1144,17 +1231,15 @@ int amdxdna_gem_pin_nolock(struct amdxdna_gem_obj *abo) if (abo->type != AMDXDNA_BO_DEV) return amdxdna_bo_pin(abo); - xa_for_each_range(&client->dev_heap_xa, heap_id, heap, - abo->heap_start_id, abo->heap_end_id) { + xa_for_each(&abo->heap_xa, heap_id, heap) { ret = amdxdna_bo_pin(heap); if (ret) break; last = heap_id; } - if (ret && last <= abo->heap_end_id) { - xa_for_each_range(&client->dev_heap_xa, heap_id, heap, - abo->heap_start_id, last) + if (ret && last != ULONG_MAX) { + xa_for_each_range(&abo->heap_xa, heap_id, heap, 0, last) amdxdna_bo_unpin(heap); } @@ -1179,8 +1264,7 @@ void amdxdna_gem_unpin(struct amdxdna_gem_obj *abo) struct amdxdna_gem_obj *heap; unsigned long heap_id; - xa_for_each_range(&abo->client->dev_heap_xa, heap_id, heap, - abo->heap_start_id, abo->heap_end_id) + xa_for_each(&abo->heap_xa, heap_id, heap) amdxdna_bo_unpin(heap); } else { amdxdna_bo_unpin(abo); @@ -1300,8 +1384,7 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev, u64 flush_start = bo_start + args->offset; u64 flush_end = flush_start + args->size; - xa_for_each_range(&client->dev_heap_xa, heap_id, heap, - abo->heap_start_id, abo->heap_end_id) { + xa_for_each(&abo->heap_xa, heap_id, heap) { u64 heap_start = amdxdna_gem_dev_addr(heap); u64 heap_end = heap_start + heap->mem.size; u64 start = max(flush_start, heap_start); diff --git a/drivers/accel/amdxdna/amdxdna_gem.h b/drivers/accel/amdxdna/amdxdna_gem.h index 1e90e32bf3cd..5dfefdcf1356 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.h +++ b/drivers/accel/amdxdna/amdxdna_gem.h @@ -20,6 +20,7 @@ struct amdxdna_umap { struct kref refcnt; bool invalid; bool unmapped; + bool cleanup; }; struct amdxdna_mem { @@ -46,8 +47,7 @@ struct amdxdna_gem_obj { /* Below members are initialized when needed */ struct drm_mm_node mm_node; /* For AMDXDNA_BO_DEV */ - u32 heap_start_id; - u32 heap_end_id; + struct xarray heap_xa; u64 dev_addr; /* For heap bo */ u32 assigned_hwctx; struct dma_buf *dma_buf; @@ -104,6 +104,7 @@ static inline u64 amdxdna_obj_dma_addr(struct amdxdna_gem_obj *abo) } void amdxdna_umap_put(struct amdxdna_umap *mapp); +void amdxdna_gem_heap_free(struct amdxdna_client *client, struct amdxdna_gem_obj *abo); struct drm_gem_object * amdxdna_gem_create_shmem_object_cb(struct drm_device *dev, size_t size); diff --git a/drivers/accel/ethosu/ethosu_job.c b/drivers/accel/ethosu/ethosu_job.c index 4ced44a65f23..3bc25324690f 100644 --- a/drivers/accel/ethosu/ethosu_job.c +++ b/drivers/accel/ethosu/ethosu_job.c @@ -345,10 +345,8 @@ int ethosu_job_init(struct ethosu_device *edev) ethosu_job_irq_handler_thread, IRQF_SHARED, KBUILD_MODNAME, edev); - if (ret) { - dev_err(dev, "failed to request irq\n"); + if (ret) return ret; - } edev->fence_context = dma_fence_context_alloc(1); diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h index 86d7c9966cac..8f29f1dc52b9 100644 --- a/drivers/accel/ivpu/ivpu_drv.h +++ b/drivers/accel/ivpu/ivpu_drv.h @@ -91,7 +91,7 @@ drm_warn(&(vdev)->drm, "%s(): " fmt, __func__, ##__VA_ARGS__) #define ivpu_warn_ratelimited(vdev, fmt, ...) \ - drm_err_ratelimited(&(vdev)->drm, "%s(): " fmt, __func__, ##__VA_ARGS__) + drm_warn_ratelimited(&(vdev)->drm, "%s(): " fmt, __func__, ##__VA_ARGS__) #define ivpu_info(vdev, fmt, ...) drm_info(&(vdev)->drm, fmt, ##__VA_ARGS__) diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index ef0351b6dc9b..4d55531bf1c9 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -15,6 +15,7 @@ #include <linux/math64.h> #include <linux/mm.h> #include <linux/moduleparam.h> +#include <linux/overflow.h> #include <linux/scatterlist.h> #include <linux/spinlock.h> #include <linux/srcu.h> @@ -455,7 +456,12 @@ static int create_sgt(struct qaic_device *qdev, struct sg_table **sgt_out, u64 s int order; if (size) { - nr_pages = DIV_ROUND_UP(size, PAGE_SIZE); + u64 nr_pages_u64 = DIV_ROUND_UP_ULL(size, PAGE_SIZE); + + if (overflows_type(nr_pages_u64, int)) + return -E2BIG; + + nr_pages = nr_pages_u64; /* * calculate how much extra we are going to allocate, to remove * later diff --git a/drivers/accel/qaic/qaic_debugfs.c b/drivers/accel/qaic/qaic_debugfs.c index 95c78e12dd61..909a4d774d90 100644 --- a/drivers/accel/qaic/qaic_debugfs.c +++ b/drivers/accel/qaic/qaic_debugfs.c @@ -5,6 +5,7 @@ #include <linux/debugfs.h> #include <linux/device.h> +#include <linux/device-id/mhi.h> #include <linux/fs.h> #include <linux/list.h> #include <linux/mhi.h> diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c index 0acf6cdd44f8..c1bb6db78d12 100644 --- a/drivers/accel/qaic/qaic_drv.c +++ b/drivers/accel/qaic/qaic_drv.c @@ -4,6 +4,7 @@ /* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include <linux/delay.h> +#include <linux/device-id/mhi.h> #include <linux/dma-mapping.h> #include <linux/idr.h> #include <linux/interrupt.h> diff --git a/drivers/accel/qaic/qaic_ras.c b/drivers/accel/qaic/qaic_ras.c index e0205c2cda3d..236e520f708f 100644 --- a/drivers/accel/qaic/qaic_ras.c +++ b/drivers/accel/qaic/qaic_ras.c @@ -5,6 +5,7 @@ #include <asm/byteorder.h> #include <linux/device.h> +#include <linux/device-id/mhi.h> #include <linux/kernel.h> #include <linux/mhi.h> diff --git a/drivers/accel/qaic/qaic_ssr.c b/drivers/accel/qaic/qaic_ssr.c index a98928654959..418f14936f9a 100644 --- a/drivers/accel/qaic/qaic_ssr.c +++ b/drivers/accel/qaic/qaic_ssr.c @@ -8,6 +8,7 @@ #include <drm/drm_managed.h> #include <linux/devcoredump.h> #include <linux/device.h> +#include <linux/device-id/mhi.h> #include <linux/kernel.h> #include <linux/mhi.h> #include <linux/workqueue.h> diff --git a/drivers/accel/qaic/qaic_timesync.c b/drivers/accel/qaic/qaic_timesync.c index 45e5f0728ebe..b5162677ccc4 100644 --- a/drivers/accel/qaic/qaic_timesync.c +++ b/drivers/accel/qaic/qaic_timesync.c @@ -2,6 +2,7 @@ /* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ +#include <linux/device-id/mhi.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/math64.h> diff --git a/drivers/accel/qaic/sahara.c b/drivers/accel/qaic/sahara.c index c7c0b3eb4b65..8476be9b9148 100644 --- a/drivers/accel/qaic/sahara.c +++ b/drivers/accel/qaic/sahara.c @@ -3,6 +3,7 @@ /* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include <linux/devcoredump.h> +#include <linux/device-id/mhi.h> #include <linux/firmware.h> #include <linux/limits.h> #include <linux/mhi.h> diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 4c9add51f9ef..36cdc342627e 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -260,7 +260,7 @@ static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence) /* only support discovering the end of the buffer, * but also allow SEEK_SET to maintain the idiomatic - * SEEK_END(0), SEEK_CUR(0) pattern. + * SEEK_END(0), SEEK_SET(0) pattern. */ if (whence == SEEK_END) base = dmabuf->size; diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c index 39a92d9f2413..2365a6d8ca15 100644 --- a/drivers/dma-buf/dma-resv.c +++ b/drivers/dma-buf/dma-resv.c @@ -633,7 +633,7 @@ int dma_resv_get_singleton(struct dma_resv *obj, enum dma_resv_usage usage, int r; r = dma_resv_get_fences(obj, usage, &count, &fences); - if (r) + if (r) return r; if (count == 0) { diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c index 26e7a48b55f3..2f2aaadafe35 100644 --- a/drivers/gpu/buddy.c +++ b/drivers/gpu/buddy.c @@ -3,11 +3,14 @@ * Copyright © 2021 Intel Corporation */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/bug.h> #include <linux/export.h> #include <linux/kmemleak.h> #include <linux/module.h> #include <linux/sizes.h> +#include <linux/slab.h> #include <linux/gpu_buddy.h> @@ -34,6 +37,430 @@ #endif static struct kmem_cache *slab_blocks; +static struct kmem_cache *slab_extents; + +/* + * Dirty tracker + * ------------- + * + * The dirty tracker maintains an augmented interval rbtree of contiguous + * dirty address ranges, decoupled from the buddy free trees. + * Each node covers a maximal coalesced run; adjacent extents are merged + * on insertion so the tree always holds the smallest possible number of + * extents. The augmentation field @subtree_max_size lets the allocator + * locate the largest dirty extent in O(log E). + * + * Free trees (mm->free_tree[]) + * ---------------------------- + * + * Per-order augmented rbtrees of FREE buddy blocks, keyed by offset. + * Every node carries: + * - subtree_max_alignment: largest natural alignment in the subtree, + * used by aligned/range allocations to skip unsuitable subtrees in + * O(log N). + * - subtree_block_state: the highest clear class (DIRTY < MIXED < CLEAR) + * of any block in the subtree, maintained as a max augment. A value of + * >= MIXED means a clear-or-mixed block exists; == CLEAR means a + * fully-clear block exists. + * + * Block classes + * ------------- + * + * Each FREE block falls into one of three classes, determined in + * mark_free() by querying the dirty tracker for the block's range: + * + * clear -- HEADER_CLEAR set; no dirty extent overlaps the range. + * mixed -- HEADER_CLEAR unset; range has both dirty and clear bytes. + * dirty -- HEADER_CLEAR unset; range is fully dirty. + * + * Clear allocation + * ---------------- + * + * A clear (CLEAR_ALLOCATION) request prefers clear -> mixed -> dirty. + * Climbing from the requested order up to max_order, rbtree_last_clear_free_block() + * returns, in one O(log N) descent per order, the right-most clear-or-mixed block + * (fully-clear preferred over mixed) at the lowest order whose free tree contains + * such a block. Only if no clear-or-mixed block exists at any order >= the + * requested one does it fall back to a dirty block. + * + * Clear state is reported to the driver per whole block via HEADER_CLEAR, so a + * fully-clear block of the requested order lets the driver skip the clear pass. + * + * The effective allocation preference depends on how the driver handles + * freed blocks: + * + * 1) Never clear on free: + * No free block contains clear bytes, so clear allocations always + * fall back to dirty blocks. + * + * 2) Always clear on free: + * Freed blocks become clear while untouched blocks remain dirty. + * Merging clear and dirty buddies produces mixed blocks, which are + * reclassified when split. Over time, clear blocks become dominant, + * so clear allocations are typically satisfied from clear blocks, + * following a clear -> mixed -> dirty preference. + * + * 3) Selective clear on free: + * For each order examined, fully-clear blocks are preferred over + * mixed blocks, and mixed blocks are preferred over dirty blocks. + * If a clear or mixed block is found at an order, it is selected + * without searching higher orders. Dirty blocks are used only when + * no clear or mixed block exists at any eligible order. + */ + +static u64 extent_size(struct gpu_dirty_extent *dirty_extent) +{ + return dirty_extent->end - dirty_extent->start; +} + +RB_DECLARE_CALLBACKS_MAX(static, gpu_dirty_augment_cb, + struct gpu_dirty_extent, rb, + u64, subtree_max_size, + extent_size) + +static struct gpu_dirty_extent *extent_alloc(struct gpu_dirty_tracker *dirty_tracker) +{ + return kmem_cache_alloc(slab_extents, GFP_KERNEL); +} + +static void extent_free(struct gpu_dirty_tracker *dirty_tracker, + struct gpu_dirty_extent *dirty_extent) +{ + kmem_cache_free(slab_extents, dirty_extent); +} + +/* Return the rightmost extent whose start is strictly below @offset. */ +static struct gpu_dirty_extent * +prev_extent(struct gpu_dirty_tracker *dirty_tracker, u64 offset) +{ + struct rb_node *rb = dirty_tracker->root.rb_node; + struct gpu_dirty_extent *dirty_extent = NULL; + + while (rb) { + struct gpu_dirty_extent *tmp_extent = + rb_entry(rb, struct gpu_dirty_extent, rb); + + if (tmp_extent->start < offset) { + dirty_extent = tmp_extent; + rb = rb->rb_right; + } else { + rb = rb->rb_left; + } + } + + return dirty_extent; +} + +/* Return the leftmost extent whose start is at or above @offset. */ +static struct gpu_dirty_extent * +next_extent(struct gpu_dirty_tracker *dirty_tracker, u64 offset) +{ + struct rb_node *rb = dirty_tracker->root.rb_node; + struct gpu_dirty_extent *dirty_extent = NULL; + + while (rb) { + struct gpu_dirty_extent *tmp_extent = + rb_entry(rb, struct gpu_dirty_extent, rb); + + if (tmp_extent->start >= offset) { + dirty_extent = tmp_extent; + rb = rb->rb_left; + } else { + rb = rb->rb_right; + } + } + + return dirty_extent; +} + +static void insert_extent(struct gpu_dirty_tracker *dirty_tracker, + struct gpu_dirty_extent *dirty_extent) +{ + struct rb_node **link = &dirty_tracker->root.rb_node; + struct rb_node *parent = NULL; + u64 size = extent_size(dirty_extent); + + while (*link) { + struct gpu_dirty_extent *tmp_extent; + + parent = *link; + tmp_extent = rb_entry(parent, struct gpu_dirty_extent, rb); + + if (tmp_extent->subtree_max_size < size) + tmp_extent->subtree_max_size = size; + + if (dirty_extent->start < tmp_extent->start) + link = &parent->rb_left; + else + link = &parent->rb_right; + } + + dirty_extent->subtree_max_size = size; + rb_link_node(&dirty_extent->rb, parent, link); + rb_insert_augmented(&dirty_extent->rb, &dirty_tracker->root, &gpu_dirty_augment_cb); +} + +static void remove_extent(struct gpu_dirty_tracker *dirty_tracker, + struct gpu_dirty_extent *dirty_extent) +{ + rb_erase_augmented(&dirty_extent->rb, &dirty_tracker->root, &gpu_dirty_augment_cb); + RB_CLEAR_NODE(&dirty_extent->rb); +} + +static void gpu_dirty_tracker_init(struct gpu_dirty_tracker *dirty_tracker) +{ + dirty_tracker->root = RB_ROOT; + dirty_tracker->total_dirty = 0; +} + +static void gpu_dirty_tracker_empty(struct gpu_dirty_tracker *dirty_tracker) +{ + struct rb_node *rb; + + while ((rb = rb_first(&dirty_tracker->root))) { + struct gpu_dirty_extent *dirty_extent = + rb_entry(rb, struct gpu_dirty_extent, rb); + + remove_extent(dirty_tracker, dirty_extent); + extent_free(dirty_tracker, dirty_extent); + } + + dirty_tracker->total_dirty = 0; +} + +static void gpu_dirty_tracker_fini(struct gpu_dirty_tracker *dirty_tracker) +{ + gpu_dirty_tracker_empty(dirty_tracker); +} + +/* + * Mark the range [start, start + size] as dirty. Merge with the neighbour on + * each side if they are contiguous, so the tree never holds two adjacent ranges. + */ +static void gpu_dirty_tracker_mark_dirty(struct gpu_dirty_tracker *dirty_tracker, + u64 start, u64 size) +{ + struct gpu_dirty_extent *left, *right, *dirty_extent; + u64 end = start + size; + + gpu_buddy_assert(size); + + /* Find contiguous neighbours, if any. */ + left = prev_extent(dirty_tracker, start); + if (left && left->end != start) + left = NULL; + + right = next_extent(dirty_tracker, end); + if (right && right->start != end) + right = NULL; + + if (left && right) { + /* Merge left + new + right into a single extent. */ + remove_extent(dirty_tracker, left); + remove_extent(dirty_tracker, right); + left->end = right->end; + extent_free(dirty_tracker, right); + insert_extent(dirty_tracker, left); + } else if (left) { + /* Extend left neighbour rightwards. */ + remove_extent(dirty_tracker, left); + left->end = end; + insert_extent(dirty_tracker, left); + } else if (right) { + /* Extend right neighbour leftwards. */ + remove_extent(dirty_tracker, right); + right->start = start; + insert_extent(dirty_tracker, right); + } else { + /* Standalone extent. */ + dirty_extent = extent_alloc(dirty_tracker); + if (!dirty_extent) { + pr_warn_once("dirty extent allocation failed, skipping tracker update\n"); + return; + } + dirty_extent->start = start; + dirty_extent->end = end; + insert_extent(dirty_tracker, dirty_extent); + } + + dirty_tracker->total_dirty += size; +} + +/* + * Remove the range [start, start + size] from the dirty tracker. Punch the + * range out of every overlapping dirty extent, splitting one extent in two if + * the removed range falls strictly inside it. + */ +static void gpu_dirty_tracker_remove_range(struct gpu_dirty_tracker *dirty_tracker, + u64 start, u64 size) +{ + struct gpu_dirty_extent *dirty_extent, *next; + u64 end = start + size; + + gpu_buddy_assert(size); + + dirty_extent = prev_extent(dirty_tracker, start + 1); + if (!dirty_extent) + dirty_extent = next_extent(dirty_tracker, start); + + while (dirty_extent && dirty_extent->start < end) { + struct rb_node *next_node = rb_next(&dirty_extent->rb); + u64 extent_start = dirty_extent->start; + u64 extent_end = dirty_extent->end; + + if (next_node) + next = rb_entry(next_node, struct gpu_dirty_extent, rb); + else + next = NULL; + + /* Skip a non-overlapping neighbour returned by prev_extent(). */ + if (extent_end <= start) { + dirty_extent = next; + continue; + } + + if (extent_start < start && extent_end > end) { + /* + * Removed range lies strictly inside this dirty extent: + * split it into the dirty left and right halves. + */ + struct gpu_dirty_extent *right = extent_alloc(dirty_tracker); + + if (!right) { + pr_warn_once("dirty extent allocation failed, skipping tracker update\n"); + dirty_extent = next; + continue; + } + + remove_extent(dirty_tracker, dirty_extent); + + dirty_extent->end = start; + right->start = end; + right->end = extent_end; + + insert_extent(dirty_tracker, dirty_extent); + insert_extent(dirty_tracker, right); + + dirty_tracker->total_dirty -= size; + } else if (extent_start >= start && extent_end <= end) { + /* Extent fully covered: drop it. */ + remove_extent(dirty_tracker, dirty_extent); + extent_free(dirty_tracker, dirty_extent); + + dirty_tracker->total_dirty -= (extent_end - extent_start); + } else if (extent_start < start) { + /* Extent overlaps from the left: trim its right end. */ + remove_extent(dirty_tracker, dirty_extent); + dirty_extent->end = start; + insert_extent(dirty_tracker, dirty_extent); + + dirty_tracker->total_dirty -= (extent_end - start); + } else { + /* Extent overlaps from the right: trim its left end. */ + remove_extent(dirty_tracker, dirty_extent); + dirty_extent->start = end; + insert_extent(dirty_tracker, dirty_extent); + + dirty_tracker->total_dirty -= (end - extent_start); + } + + dirty_extent = next; + } +} + +static enum gpu_block_state +gpu_dirty_range_state(struct gpu_dirty_tracker *dirty_tracker, + u64 start, u64 size) +{ + struct gpu_dirty_extent *dirty_extent; + u64 end = start + size; + + dirty_extent = prev_extent(dirty_tracker, start + 1); + if (dirty_extent) { + if (dirty_extent->start <= start && dirty_extent->end >= end) + return GPU_BLOCK_DIRTY; + if (dirty_extent->start < end && dirty_extent->end > start) + return GPU_BLOCK_MIXED; + } + + dirty_extent = next_extent(dirty_tracker, start); + if (dirty_extent && dirty_extent->start < end) + return GPU_BLOCK_MIXED; + + return GPU_BLOCK_CLEAR; +} + +static struct rb_node * +dirty_tracker_descend_right(struct rb_node *node, u64 min_size) +{ + while (node->rb_right) { + struct gpu_dirty_extent *tmp_extent; + + tmp_extent = rb_entry(node->rb_right, struct gpu_dirty_extent, rb); + + if (tmp_extent->subtree_max_size < min_size) + break; + node = node->rb_right; + } + + return node; +} + +static struct gpu_dirty_extent * +gpu_dirty_tracker_find(struct gpu_dirty_tracker *dirty_tracker, + u64 min_size, u64 *aligned_start_out) +{ + struct rb_node *rb = dirty_tracker->root.rb_node; + struct gpu_dirty_extent *root_extent; + struct rb_node *parent; + + if (!min_size || !is_power_of_2(min_size)) + return NULL; + + if (!rb) + return NULL; + + root_extent = rb_entry(rb, struct gpu_dirty_extent, rb); + if (root_extent->subtree_max_size < min_size) + return NULL; + + rb = dirty_tracker_descend_right(rb, min_size); + + while (rb) { + struct gpu_dirty_extent *dirty_extent; + u64 aligned_start; + + dirty_extent = rb_entry(rb, struct gpu_dirty_extent, rb); + aligned_start = ALIGN(dirty_extent->start, min_size); + + /* Check if a min_size block fits after the alignment skip. */ + if (aligned_start <= dirty_extent->end && + dirty_extent->end - aligned_start >= min_size) { + *aligned_start_out = aligned_start; + return dirty_extent; + } + + if (rb->rb_left) { + struct gpu_dirty_extent *tmp_extent; + + tmp_extent = rb_entry(rb->rb_left, struct gpu_dirty_extent, rb); + if (tmp_extent->subtree_max_size >= min_size) { + rb = dirty_tracker_descend_right(rb->rb_left, min_size); + continue; + } + } + + /* Walk up until we exit a node via its right child. */ + parent = rb_parent(rb); + while (parent && parent->rb_right != rb) { + rb = parent; + parent = rb_parent(rb); + } + rb = parent; + } + + return NULL; +} static unsigned int gpu_buddy_block_state(struct gpu_buddy_block *block) @@ -67,10 +494,97 @@ static unsigned int gpu_buddy_block_offset_alignment(struct gpu_buddy_block *blo return __ffs64(offset); } -RB_DECLARE_CALLBACKS_MAX(static, gpu_buddy_augment_cb, - struct gpu_buddy_block, rb, - unsigned int, subtree_max_alignment, - gpu_buddy_block_offset_alignment); +static inline enum gpu_block_state +gpu_block_cached_state(struct gpu_buddy_block *block) +{ + if (gpu_buddy_block_is_clear(block)) + return GPU_BLOCK_CLEAR; + if (block->has_clear) + return GPU_BLOCK_MIXED; + return GPU_BLOCK_DIRTY; +} + +static inline void gpu_buddy_augment_compute(struct gpu_buddy_block *block) +{ + enum gpu_block_state block_state; + struct gpu_buddy_block *right; + struct gpu_buddy_block *left; + unsigned int max_align; + + max_align = gpu_buddy_block_offset_alignment(block); + block_state = gpu_block_cached_state(block); + + left = rb_entry_safe(block->rb.rb_left, struct gpu_buddy_block, rb); + if (left) { + if (left->subtree_max_alignment > max_align) + max_align = left->subtree_max_alignment; + + block_state = max(block_state, left->subtree_block_state); + } + + right = rb_entry_safe(block->rb.rb_right, struct gpu_buddy_block, rb); + if (right) { + if (right->subtree_max_alignment > max_align) + max_align = right->subtree_max_alignment; + + block_state = max(block_state, right->subtree_block_state); + } + + block->subtree_max_alignment = max_align; + block->subtree_block_state = block_state; +} + +static void gpu_buddy_augment_propagate(struct rb_node *rb, struct rb_node *stop) +{ + while (rb != stop) { + struct gpu_buddy_block *block; + unsigned int old_align; + enum gpu_block_state old_block_state; + + block = rb_entry(rb, struct gpu_buddy_block, rb); + old_align = block->subtree_max_alignment; + old_block_state = block->subtree_block_state; + + gpu_buddy_augment_compute(block); + if (block->subtree_max_alignment == old_align && + block->subtree_block_state == old_block_state) + break; + + rb = rb_parent(&block->rb); + } +} + +static void gpu_buddy_augment_copy(struct rb_node *rb_old, struct rb_node *rb_new) +{ + struct gpu_buddy_block *old; + struct gpu_buddy_block *new; + + old = rb_entry(rb_old, struct gpu_buddy_block, rb); + new = rb_entry(rb_new, struct gpu_buddy_block, rb); + + new->subtree_max_alignment = old->subtree_max_alignment; + new->subtree_block_state = old->subtree_block_state; +} + +static void gpu_buddy_augment_rotate(struct rb_node *rb_old, struct rb_node *rb_new) +{ + struct gpu_buddy_block *old; + struct gpu_buddy_block *new; + + old = rb_entry(rb_old, struct gpu_buddy_block, rb); + new = rb_entry(rb_new, struct gpu_buddy_block, rb); + + new->subtree_max_alignment = old->subtree_max_alignment; + new->subtree_block_state = old->subtree_block_state; + + gpu_buddy_augment_compute(old); +} + +static const struct rb_augment_callbacks gpu_buddy_augment_cb = { + .propagate = gpu_buddy_augment_propagate, + .copy = gpu_buddy_augment_copy, + .rotate = gpu_buddy_augment_rotate, +}; static struct gpu_buddy_block *gpu_block_alloc(struct gpu_buddy *mm, struct gpu_buddy_block *parent, @@ -101,13 +615,6 @@ static void gpu_block_free(struct gpu_buddy *mm, kmem_cache_free(slab_blocks, block); } -static enum gpu_buddy_free_tree -get_block_tree(struct gpu_buddy_block *block) -{ - return gpu_buddy_block_is_clear(block) ? - GPU_BUDDY_CLEAR_TREE : GPU_BUDDY_DIRTY_TREE; -} - static struct gpu_buddy_block * rbtree_get_free_block(const struct rb_node *node) { @@ -120,24 +627,59 @@ rbtree_last_free_block(struct rb_root *root) return rbtree_get_free_block(rb_last(root)); } -static bool rbtree_is_empty(struct rb_root *root) +static struct gpu_buddy_block * +rbtree_last_clear_free_block(struct rb_root *root, + enum gpu_block_state min_block_state) { - return RB_EMPTY_ROOT(root); + struct rb_node *node = root->rb_node; + struct gpu_buddy_block *block = NULL; + struct gpu_buddy_block *root_block; + enum gpu_block_state target_state; + + root_block = rbtree_get_free_block(node); + if (!root_block || root_block->subtree_block_state < min_block_state) + return NULL; + + target_state = root_block->subtree_block_state; + + while (node) { + struct gpu_buddy_block *right_block; + struct gpu_buddy_block *node_block; + + node_block = rbtree_get_free_block(node); + right_block = rbtree_get_free_block(node->rb_right); + + if (right_block && right_block->subtree_block_state >= target_state) { + node = node->rb_right; + continue; + } + + if (gpu_block_cached_state(node_block) == target_state) { + block = node_block; + break; + } + + node = node->rb_left; + } + + return block; } static void rbtree_insert(struct gpu_buddy *mm, - struct gpu_buddy_block *block, - enum gpu_buddy_free_tree tree) + struct gpu_buddy_block *block) { struct rb_node **link, *parent = NULL; - unsigned int block_alignment, order; + enum gpu_block_state block_state; struct gpu_buddy_block *node; + unsigned int block_alignment; struct rb_root *root; + unsigned int order; order = gpu_buddy_block_order(block); block_alignment = gpu_buddy_block_offset_alignment(block); + block_state = gpu_block_cached_state(block); - root = &mm->free_trees[tree][order]; + root = &mm->free_tree[order]; link = &root->rb_node; while (*link) { @@ -147,10 +689,12 @@ static void rbtree_insert(struct gpu_buddy *mm, * Manual augmentation update during insertion traversal. Required * because rb_insert_augmented() only calls rotate callback during * rotations. This ensures all ancestors on the insertion path have - * correct subtree_max_alignment values. + * correct subtree_max_alignment / subtree_block_state values. */ if (node->subtree_max_alignment < block_alignment) node->subtree_max_alignment = block_alignment; + if (node->subtree_block_state < block_state) + node->subtree_block_state = block_state; if (gpu_buddy_block_offset(block) < gpu_buddy_block_offset(node)) link = &parent->rb_left; @@ -159,6 +703,7 @@ static void rbtree_insert(struct gpu_buddy *mm, } block->subtree_max_alignment = block_alignment; + block->subtree_block_state = block_state; rb_link_node(&block->rb, parent, link); rb_insert_augmented(&block->rb, root, &gpu_buddy_augment_cb); } @@ -167,53 +712,55 @@ static void rbtree_remove(struct gpu_buddy *mm, struct gpu_buddy_block *block) { unsigned int order = gpu_buddy_block_order(block); - enum gpu_buddy_free_tree tree; - struct rb_root *root; - - tree = get_block_tree(block); - root = &mm->free_trees[tree][order]; - rb_erase_augmented(&block->rb, root, &gpu_buddy_augment_cb); + rb_erase_augmented(&block->rb, &mm->free_tree[order], &gpu_buddy_augment_cb); RB_CLEAR_NODE(&block->rb); } -static void clear_reset(struct gpu_buddy_block *block) -{ - block->header &= ~GPU_BUDDY_HEADER_CLEAR; -} - -static void mark_cleared(struct gpu_buddy_block *block) -{ - block->header |= GPU_BUDDY_HEADER_CLEAR; -} - static void mark_allocated(struct gpu_buddy *mm, struct gpu_buddy_block *block) { block->header &= ~GPU_BUDDY_HEADER_STATE; block->header |= GPU_BUDDY_ALLOCATED; + block->has_clear = false; + mm->free_scoreboard[gpu_buddy_block_order(block)]--; mm->used_scoreboard[gpu_buddy_block_order(block)]++; rbtree_remove(mm, block); } -static void mark_free(struct gpu_buddy *mm, - struct gpu_buddy_block *block) +static void __mark_free(struct gpu_buddy *mm, + struct gpu_buddy_block *block, + enum gpu_block_state block_state) { - enum gpu_buddy_free_tree tree; - if (gpu_buddy_block_is_allocated(block)) mm->used_scoreboard[gpu_buddy_block_order(block)]--; block->header &= ~GPU_BUDDY_HEADER_STATE; block->header |= GPU_BUDDY_FREE; + block->header &= ~GPU_BUDDY_HEADER_CLEAR; + + block->has_clear = (block_state != GPU_BLOCK_DIRTY); + if (block_state == GPU_BLOCK_CLEAR) + block->header |= GPU_BUDDY_HEADER_CLEAR; + mm->free_scoreboard[gpu_buddy_block_order(block)]++; - tree = get_block_tree(block); - rbtree_insert(mm, block, tree); + rbtree_insert(mm, block); +} + +static void mark_free(struct gpu_buddy *mm, + struct gpu_buddy_block *block) +{ + enum gpu_block_state block_state; + + block_state = gpu_dirty_range_state(&mm->dirty, + gpu_buddy_block_offset(block), + gpu_buddy_block_size(mm, block)); + __mark_free(mm, block, block_state); } static void mark_split(struct gpu_buddy *mm, @@ -253,37 +800,31 @@ __get_buddy(struct gpu_buddy_block *block) } static unsigned int __gpu_buddy_free(struct gpu_buddy *mm, - struct gpu_buddy_block *block, - bool force_merge) + struct gpu_buddy_block *block) { + enum gpu_block_state block_state; struct gpu_buddy_block *parent; unsigned int order; - while ((parent = block->parent)) { - struct gpu_buddy_block *buddy; + block_state = gpu_block_cached_state(block); - buddy = __get_buddy(block); + while ((parent = block->parent)) { + struct gpu_buddy_block *buddy = __get_buddy(block); if (!gpu_buddy_block_is_free(buddy)) break; - if (!force_merge) { - /* - * Check the block and its buddy clear state and exit - * the loop if they both have the dissimilar state. - */ - if (gpu_buddy_block_is_clear(block) != - gpu_buddy_block_is_clear(buddy)) - break; + if (block_state != GPU_BLOCK_MIXED) { + enum gpu_block_state buddy_state; - if (gpu_buddy_block_is_clear(block)) - mark_cleared(parent); + buddy_state = gpu_block_cached_state(buddy); + + if (buddy_state != block_state) + block_state = GPU_BLOCK_MIXED; } rbtree_remove(mm, buddy); mm->free_scoreboard[gpu_buddy_block_order(buddy)]--; - if (force_merge && gpu_buddy_block_is_clear(buddy)) - mm->clear_avail -= gpu_buddy_block_size(mm, buddy); if (gpu_buddy_block_is_allocated(block)) mm->used_scoreboard[gpu_buddy_block_order(block)]--; @@ -295,74 +836,11 @@ static unsigned int __gpu_buddy_free(struct gpu_buddy *mm, } order = gpu_buddy_block_order(block); - mark_free(mm, block); + __mark_free(mm, block, block_state); return order; } -static int __force_merge(struct gpu_buddy *mm, - u64 start, - u64 end, - unsigned int min_order) -{ - unsigned int tree, order; - int i; - - if (!min_order) - return -ENOMEM; - - if (min_order > mm->max_order) - return -EINVAL; - - for_each_free_tree(tree) { - for (i = min_order - 1; i >= 0; i--) { - struct rb_node *iter = rb_last(&mm->free_trees[tree][i]); - - while (iter) { - struct gpu_buddy_block *block, *buddy; - u64 block_start, block_end; - - block = rbtree_get_free_block(iter); - iter = rb_prev(iter); - - if (!block || !block->parent) - continue; - - block_start = gpu_buddy_block_offset(block); - block_end = block_start + gpu_buddy_block_size(mm, block) - 1; - - if (!contains(start, end, block_start, block_end)) - continue; - - buddy = __get_buddy(block); - if (!gpu_buddy_block_is_free(buddy)) - continue; - - gpu_buddy_assert(gpu_buddy_block_is_clear(block) != - gpu_buddy_block_is_clear(buddy)); - - /* - * Advance to the next node when the current node is the buddy, - * as freeing the block will also remove its buddy from the tree. - */ - if (iter == &buddy->rb) - iter = rb_prev(iter); - - rbtree_remove(mm, block); - mm->free_scoreboard[gpu_buddy_block_order(block)]--; - if (gpu_buddy_block_is_clear(block)) - mm->clear_avail -= gpu_buddy_block_size(mm, block); - - order = __gpu_buddy_free(mm, block, true); - if (order >= min_order) - return 0; - } - } - } - - return -ENOMEM; -} - /** * gpu_buddy_init - init memory manager * @@ -377,7 +855,7 @@ static int __force_merge(struct gpu_buddy *mm, */ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size) { - unsigned int i, j, root_count = 0; + unsigned int root_count = 0; u64 offset = 0; if (size < chunk_size) @@ -393,7 +871,6 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size) mm->size = size; mm->avail = size; - mm->clear_avail = 0; mm->chunk_size = chunk_size; mm->max_order = ilog2(size) - ilog2(chunk_size); @@ -411,19 +888,13 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size) if (!mm->used_scoreboard) goto out_free_free_scoreboard; - mm->free_trees = kmalloc_objs(*mm->free_trees, GPU_BUDDY_MAX_FREE_TREES); - if (!mm->free_trees) + mm->free_tree = kcalloc(mm->max_order + 1, + sizeof(struct rb_root), + GFP_KERNEL); + if (!mm->free_tree) goto out_free_used_scoreboard; - for_each_free_tree(i) { - mm->free_trees[i] = kmalloc_objs(struct rb_root, - mm->max_order + 1); - if (!mm->free_trees[i]) - goto out_free_tree; - - for (j = 0; j <= mm->max_order; ++j) - mm->free_trees[i][j] = RB_ROOT; - } + gpu_dirty_tracker_init(&mm->dirty); mm->n_roots = hweight64(size); @@ -447,7 +918,8 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size) if (!root) goto out_free_roots; - mark_free(mm, root); + gpu_dirty_tracker_mark_dirty(&mm->dirty, offset, root_size); + __mark_free(mm, root, GPU_BLOCK_DIRTY); BUG_ON(root_count > mm->max_order); BUG_ON(gpu_buddy_block_size(mm, root) < chunk_size); @@ -469,9 +941,8 @@ out_free_roots: gpu_block_free(mm, mm->roots[root_count]); kfree(mm->roots); out_free_tree: - while (i--) - kfree(mm->free_trees[i]); - kfree(mm->free_trees); + gpu_dirty_tracker_fini(&mm->dirty); + kfree(mm->free_tree); out_free_used_scoreboard: kfree(mm->used_scoreboard); out_free_free_scoreboard: @@ -489,7 +960,7 @@ EXPORT_SYMBOL(gpu_buddy_init); */ void gpu_buddy_fini(struct gpu_buddy *mm) { - u64 root_size, size, start; + u64 root_size, size; unsigned int order; int i; @@ -497,14 +968,10 @@ void gpu_buddy_fini(struct gpu_buddy *mm) for (i = 0; i < mm->n_roots; ++i) { order = ilog2(size) - ilog2(mm->chunk_size); - start = gpu_buddy_block_offset(mm->roots[i]); - __force_merge(mm, start, start + size, order); + root_size = mm->chunk_size << order; gpu_buddy_assert(gpu_buddy_block_is_free(mm->roots[i])); - gpu_block_free(mm, mm->roots[i]); - - root_size = mm->chunk_size << order; size -= root_size; } @@ -513,9 +980,8 @@ void gpu_buddy_fini(struct gpu_buddy *mm) for (i = 0; i <= mm->max_order; ++i) gpu_buddy_assert(!mm->used_scoreboard[i]); - for_each_free_tree(i) - kfree(mm->free_trees[i]); - kfree(mm->free_trees); + gpu_dirty_tracker_fini(&mm->dirty); + kfree(mm->free_tree); kfree(mm->roots); kfree(mm->free_scoreboard); kfree(mm->used_scoreboard); @@ -527,6 +993,7 @@ static int split_block(struct gpu_buddy *mm, { unsigned int block_order = gpu_buddy_block_order(block) - 1; u64 offset = gpu_buddy_block_offset(block); + enum gpu_block_state parent_state; BUG_ON(!gpu_buddy_block_is_free(block)); BUG_ON(!gpu_buddy_block_order(block)); @@ -542,17 +1009,18 @@ static int split_block(struct gpu_buddy *mm, return -ENOMEM; } + parent_state = gpu_block_cached_state(block); + mark_split(mm, block); - if (gpu_buddy_block_is_clear(block)) { - mark_cleared(block->left); - mark_cleared(block->right); - clear_reset(block); + if (parent_state == GPU_BLOCK_MIXED) { + mark_free(mm, block->left); + mark_free(mm, block->right); + } else { + __mark_free(mm, block->left, parent_state); + __mark_free(mm, block->right, parent_state); } - mark_free(mm, block->left); - mark_free(mm, block->right); - return 0; } @@ -567,45 +1035,47 @@ static int split_block(struct gpu_buddy *mm, */ void gpu_buddy_reset_clear(struct gpu_buddy *mm, bool is_clear) { - enum gpu_buddy_free_tree src_tree, dst_tree; - u64 root_size, size, start; - unsigned int order; - int i; + unsigned int i; gpu_buddy_driver_lock_held(mm); - size = mm->size; - for (i = 0; i < mm->n_roots; ++i) { - order = ilog2(size) - ilog2(mm->chunk_size); - start = gpu_buddy_block_offset(mm->roots[i]); - __force_merge(mm, start, start + size, order); - - root_size = mm->chunk_size << order; - size -= root_size; - } - src_tree = is_clear ? GPU_BUDDY_DIRTY_TREE : GPU_BUDDY_CLEAR_TREE; - dst_tree = is_clear ? GPU_BUDDY_CLEAR_TREE : GPU_BUDDY_DIRTY_TREE; + gpu_dirty_tracker_empty(&mm->dirty); for (i = 0; i <= mm->max_order; ++i) { - struct rb_root *root = &mm->free_trees[src_tree][i]; struct gpu_buddy_block *block, *tmp; - rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { - rbtree_remove(mm, block); + rbtree_postorder_for_each_entry_safe(block, tmp, + &mm->free_tree[i], rb) { if (is_clear) { - mark_cleared(block); - mm->clear_avail += gpu_buddy_block_size(mm, block); + if (!gpu_buddy_block_is_clear(block)) + block->header |= GPU_BUDDY_HEADER_CLEAR; + block->has_clear = true; } else { - clear_reset(block); - mm->clear_avail -= gpu_buddy_block_size(mm, block); + block->header &= ~GPU_BUDDY_HEADER_CLEAR; + block->has_clear = false; + gpu_dirty_tracker_mark_dirty(&mm->dirty, + gpu_buddy_block_offset(block), + gpu_buddy_block_size(mm, block)); } - rbtree_insert(mm, block, dst_tree); + gpu_buddy_augment_compute(block); } } } EXPORT_SYMBOL(gpu_buddy_reset_clear); +static void __gpu_buddy_free_block_internal(struct gpu_buddy *mm, + struct gpu_buddy_block *block) +{ + u64 size = gpu_buddy_block_size(mm, block); + + gpu_buddy_driver_lock_held(mm); + BUG_ON(!gpu_buddy_block_is_allocated(block)); + + mm->avail += size; + __gpu_buddy_free(mm, block); +} + /** * gpu_buddy_free_block - free a block * @@ -615,13 +1085,12 @@ EXPORT_SYMBOL(gpu_buddy_reset_clear); void gpu_buddy_free_block(struct gpu_buddy *mm, struct gpu_buddy_block *block) { - gpu_buddy_driver_lock_held(mm); - BUG_ON(!gpu_buddy_block_is_allocated(block)); - mm->avail += gpu_buddy_block_size(mm, block); - if (gpu_buddy_block_is_clear(block)) - mm->clear_avail += gpu_buddy_block_size(mm, block); + if (!gpu_buddy_block_is_clear(block)) + gpu_dirty_tracker_mark_dirty(&mm->dirty, + gpu_buddy_block_offset(block), + gpu_buddy_block_size(mm, block)); - __gpu_buddy_free(mm, block, false); + __gpu_buddy_free_block_internal(mm, block); } EXPORT_SYMBOL(gpu_buddy_free_block); @@ -684,17 +1153,47 @@ static void __gpu_buddy_free_list(struct gpu_buddy *mm, bool mark_dirty) { struct gpu_buddy_block *block, *on; + u64 dirty_start = 0, dirty_size = 0; gpu_buddy_assert(!(mark_dirty && mark_clear)); list_for_each_entry_safe(block, on, objects, link) { + u64 offset = gpu_buddy_block_offset(block); + u64 size = gpu_buddy_block_size(mm, block); + if (mark_clear) - mark_cleared(block); + block->header |= GPU_BUDDY_HEADER_CLEAR; else if (mark_dirty) - clear_reset(block); - gpu_buddy_free_block(mm, block); + block->header &= ~GPU_BUDDY_HEADER_CLEAR; + + /* + * Coalesce contiguous dirty blocks into one extent update so + * a multi-block contiguous free costs a single mark_dirty(). + * Flush the pending extent and start over on a gap. + */ + if (!gpu_buddy_block_is_clear(block)) { + if (dirty_size && + (dirty_start + dirty_size == offset || + offset + size == dirty_start)) { + dirty_start = min(dirty_start, offset); + dirty_size += size; + } else { + if (dirty_size) + gpu_dirty_tracker_mark_dirty(&mm->dirty, + dirty_start, + dirty_size); + dirty_start = offset; + dirty_size = size; + } + } + + __gpu_buddy_free_block_internal(mm, block); cond_resched(); } + + if (dirty_size) + gpu_dirty_tracker_mark_dirty(&mm->dirty, dirty_start, dirty_size); + INIT_LIST_HEAD(objects); } @@ -727,13 +1226,6 @@ void gpu_buddy_free_list(struct gpu_buddy *mm, } EXPORT_SYMBOL(gpu_buddy_free_list); -static bool block_incompatible(struct gpu_buddy_block *block, unsigned int flags) -{ - bool needs_clear = flags & GPU_BUDDY_CLEAR_ALLOCATION; - - return needs_clear != gpu_buddy_block_is_clear(block); -} - static void __gpu_buddy_undo_splits(struct gpu_buddy *mm, struct gpu_buddy_block *block) { @@ -744,7 +1236,7 @@ static void __gpu_buddy_undo_splits(struct gpu_buddy *mm, gpu_buddy_block_is_free(buddy))) { rbtree_remove(mm, block); mm->free_scoreboard[gpu_buddy_block_order(block)]--; - __gpu_buddy_free(mm, block, false); + __gpu_buddy_free(mm, block); } } @@ -752,8 +1244,7 @@ static struct gpu_buddy_block * __alloc_range_bias(struct gpu_buddy *mm, u64 start, u64 end, unsigned int order, - unsigned long flags, - bool fallback) + unsigned long flags) { u64 req_size = mm->chunk_size << order; struct gpu_buddy_block *block; @@ -763,7 +1254,15 @@ __alloc_range_bias(struct gpu_buddy *mm, end = end - 1; - for (i = 0; i < mm->n_roots; ++i) + /* + * This range-constrained search hands back the highest/right-most + * address that satisfies the request: the roots are seeded high-to-low + * and the right (higher-address) child is descended first, making + * top-down the default placement here. A non-top-down clear request is + * the only exception, where the descent is biased towards clear or + * clear-containing subtrees to satisfy the clear preference. + */ + for (i = mm->n_roots - 1; i >= 0; --i) list_add_tail(&mm->roots[i]->tmp_link, &dfs); do { @@ -799,9 +1298,6 @@ __alloc_range_bias(struct gpu_buddy *mm, continue; } - if (!fallback && block_incompatible(block, flags)) - continue; - if (contains(start, end, block_start, block_end) && order == gpu_buddy_block_order(block)) { /* @@ -819,8 +1315,38 @@ __alloc_range_bias(struct gpu_buddy *mm, goto err_undo; } - list_add(&block->right->tmp_link, &dfs); - list_add(&block->left->tmp_link, &dfs); + /* + * Top-down is a strict address-placement policy, so when it is + * requested we ignore clear steering and simply descend the + * right (higher-address) child first. Only a non-top-down clear + * request biases the descent towards clear/has_clear subtrees. + */ + if ((flags & GPU_BUDDY_CLEAR_ALLOCATION) && + !(flags & GPU_BUDDY_TOPDOWN_ALLOCATION)) { + struct gpu_buddy_block *prefer; + + if (gpu_buddy_block_is_clear(block->right)) + prefer = block->right; + else if (gpu_buddy_block_is_clear(block->left)) + prefer = block->left; + else if (block->right->has_clear) + prefer = block->right; + else if (block->left->has_clear) + prefer = block->left; + else + prefer = block->right; + + if (prefer == block->right) { + list_add(&block->left->tmp_link, &dfs); + list_add(&block->right->tmp_link, &dfs); + } else { + list_add(&block->right->tmp_link, &dfs); + list_add(&block->left->tmp_link, &dfs); + } + } else { + list_add(&block->left->tmp_link, &dfs); + list_add(&block->right->tmp_link, &dfs); + } } while (1); return ERR_PTR(-ENOSPC); @@ -835,48 +1361,32 @@ err_undo: return ERR_PTR(err); } -static struct gpu_buddy_block * -__gpu_buddy_alloc_range_bias(struct gpu_buddy *mm, - u64 start, u64 end, - unsigned int order, - unsigned long flags) -{ - struct gpu_buddy_block *block; - bool fallback = false; - - block = __alloc_range_bias(mm, start, end, order, - flags, fallback); - if (IS_ERR(block)) - return __alloc_range_bias(mm, start, end, order, - flags, !fallback); - - return block; -} - +/* Return the highest-address free block of at least @order. */ static struct gpu_buddy_block * get_maxblock(struct gpu_buddy *mm, - unsigned int order, - enum gpu_buddy_free_tree tree) + unsigned int order) { - struct gpu_buddy_block *max_block = NULL, *block = NULL; - struct rb_root *root; + struct gpu_buddy_block *max_block; + struct gpu_buddy_block *block; unsigned int i; + /* + * Top-down allocation is a strict address-placement policy: the block + * is chosen purely by offset, regardless of its clear/dirty state. + * Clear state is re-derived from the dirty tracker once the allocation + * completes, and the driver is responsible for issuing the clear pass + * if a clear region is required. + */ + max_block = NULL; + for (i = order; i <= mm->max_order; ++i) { - root = &mm->free_trees[tree][i]; - block = rbtree_last_free_block(root); + block = rbtree_last_free_block(&mm->free_tree[i]); if (!block) continue; - if (!max_block) { + if (!max_block || + gpu_buddy_block_offset(block) > gpu_buddy_block_offset(max_block)) max_block = block; - continue; - } - - if (gpu_buddy_block_offset(block) > - gpu_buddy_block_offset(max_block)) { - max_block = block; - } } return max_block; @@ -888,45 +1398,34 @@ alloc_from_freetree(struct gpu_buddy *mm, unsigned long flags) { struct gpu_buddy_block *block = NULL; - struct rb_root *root; - enum gpu_buddy_free_tree tree; unsigned int tmp; int err; - tree = (flags & GPU_BUDDY_CLEAR_ALLOCATION) ? - GPU_BUDDY_CLEAR_TREE : GPU_BUDDY_DIRTY_TREE; - if (flags & GPU_BUDDY_TOPDOWN_ALLOCATION) { - block = get_maxblock(mm, order, tree); + block = get_maxblock(mm, order); if (block) - /* Store the obtained block order */ tmp = gpu_buddy_block_order(block); } else { - for (tmp = order; tmp <= mm->max_order; ++tmp) { - /* Get RB tree root for this order and tree */ - root = &mm->free_trees[tree][tmp]; - block = rbtree_last_free_block(root); - if (block) - break; + if (flags & GPU_BUDDY_CLEAR_ALLOCATION) { + for (tmp = order; tmp <= mm->max_order; ++tmp) { + block = rbtree_last_clear_free_block(&mm->free_tree[tmp], + GPU_BLOCK_MIXED); + if (block) + break; + } } - } - - if (!block) { - /* Try allocating from the other tree */ - tree = (tree == GPU_BUDDY_CLEAR_TREE) ? - GPU_BUDDY_DIRTY_TREE : GPU_BUDDY_CLEAR_TREE; - - for (tmp = order; tmp <= mm->max_order; ++tmp) { - root = &mm->free_trees[tree][tmp]; - block = rbtree_last_free_block(root); - if (block) - break; + if (!block) { + for (tmp = order; tmp <= mm->max_order; ++tmp) { + block = rbtree_last_free_block(&mm->free_tree[tmp]); + if (block) + break; + } } - - if (!block) - return ERR_PTR(-ENOSPC); } + if (!block) + return ERR_PTR(-ENOSPC); + BUG_ON(!gpu_buddy_block_is_free(block)); while (tmp != order) { @@ -934,7 +1433,26 @@ alloc_from_freetree(struct gpu_buddy *mm, if (unlikely(err)) goto err_undo; - block = block->right; + if ((flags & GPU_BUDDY_CLEAR_ALLOCATION) && + !(flags & GPU_BUDDY_TOPDOWN_ALLOCATION)) { + bool right_clear, left_clear; + + right_clear = gpu_buddy_block_is_clear(block->right); + left_clear = gpu_buddy_block_is_clear(block->left); + + if (right_clear) + block = block->right; + else if (left_clear) + block = block->left; + else if (block->right->has_clear) + block = block->right; + else if (block->left->has_clear) + block = block->left; + else + block = block->right; + } else { + block = block->right; + } tmp--; } return block; @@ -961,12 +1479,10 @@ static bool gpu_buddy_subtree_can_satisfy(struct rb_node *node, static struct gpu_buddy_block * gpu_buddy_find_block_aligned(struct gpu_buddy *mm, - enum gpu_buddy_free_tree tree, unsigned int order, - unsigned int alignment, - unsigned long flags) + unsigned int alignment) { - struct rb_root *root = &mm->free_trees[tree][order]; + struct rb_root *root = &mm->free_tree[order]; struct rb_node *rb = root->rb_node; while (rb) { @@ -999,12 +1515,10 @@ gpu_buddy_find_block_aligned(struct gpu_buddy *mm, static struct gpu_buddy_block * gpu_buddy_offset_aligned_allocation(struct gpu_buddy *mm, u64 size, - u64 min_block_size, - unsigned long flags) + u64 min_block_size) { struct gpu_buddy_block *block = NULL; unsigned int order, tmp, alignment; - enum gpu_buddy_free_tree tree; unsigned long pages; int err; @@ -1012,19 +1526,15 @@ gpu_buddy_offset_aligned_allocation(struct gpu_buddy *mm, pages = size >> ilog2(mm->chunk_size); order = fls(pages) - 1; - tree = (flags & GPU_BUDDY_CLEAR_ALLOCATION) ? - GPU_BUDDY_CLEAR_TREE : GPU_BUDDY_DIRTY_TREE; - + /* + * Offset-aligned allocation is a strict address-placement policy: the + * block is chosen purely by its offset alignment, regardless of its + * clear/dirty state. Clear state is re-derived from the dirty tracker + * once the allocation completes, and the driver is responsible for + * issuing the clear pass if a clear region is required. + */ for (tmp = order; tmp <= mm->max_order; ++tmp) { - block = gpu_buddy_find_block_aligned(mm, tree, tmp, - alignment, flags); - if (!block) { - tree = (tree == GPU_BUDDY_CLEAR_TREE) ? - GPU_BUDDY_DIRTY_TREE : GPU_BUDDY_CLEAR_TREE; - block = gpu_buddy_find_block_aligned(mm, tree, tmp, - alignment, flags); - } - + block = gpu_buddy_find_block_aligned(mm, tmp, alignment); if (block) break; } @@ -1063,6 +1573,7 @@ err_undo: static int __alloc_range(struct gpu_buddy *mm, struct list_head *dfs, u64 start, u64 size, + unsigned long flags, struct list_head *blocks, u64 *total_allocated_on_err) { @@ -1099,16 +1610,23 @@ static int __alloc_range(struct gpu_buddy *mm, if (contains(start, end, block_start, block_end)) { if (gpu_buddy_block_is_free(block)) { + u64 block_offset; + u64 block_size; + + block_size = gpu_buddy_block_size(mm, block); + block_offset = gpu_buddy_block_offset(block); + + if (!gpu_buddy_block_is_clear(block)) + gpu_dirty_tracker_remove_range(&mm->dirty, + block_offset, + block_size); + mark_allocated(mm, block); - total_allocated += gpu_buddy_block_size(mm, block); - mm->avail -= gpu_buddy_block_size(mm, block); - if (gpu_buddy_block_is_clear(block)) - mm->clear_avail -= gpu_buddy_block_size(mm, block); + total_allocated += block_size; + mm->avail -= block_size; + list_add_tail(&block->link, &allocated); continue; - } else if (!mm->clear_avail) { - err = -ENOSPC; - goto err_free; } } @@ -1153,6 +1671,7 @@ err_free: static int __gpu_buddy_alloc_range(struct gpu_buddy *mm, u64 start, u64 size, + unsigned long flags, u64 *total_allocated_on_err, struct list_head *blocks) { @@ -1162,7 +1681,7 @@ static int __gpu_buddy_alloc_range(struct gpu_buddy *mm, for (i = 0; i < mm->n_roots; ++i) list_add_tail(&mm->roots[i]->tmp_link, &dfs); - return __alloc_range(mm, &dfs, start, size, + return __alloc_range(mm, &dfs, start, size, flags, blocks, total_allocated_on_err); } @@ -1170,85 +1689,89 @@ static int __alloc_contig_aligned_retry(struct gpu_buddy *mm, u64 unaligned_offset, u64 size, u64 min_block_size, + unsigned long flags, struct list_head *blocks) { u64 aligned_offset = round_down(unaligned_offset, min_block_size); - return __gpu_buddy_alloc_range(mm, aligned_offset, size, NULL, blocks); + return __gpu_buddy_alloc_range(mm, aligned_offset, size, flags, + NULL, blocks); } static int __alloc_contig_try_harder(struct gpu_buddy *mm, u64 size, u64 min_block_size, + unsigned long flags, struct list_head *blocks) { u64 rhs_offset, lhs_offset, filled; struct gpu_buddy_block *block; - unsigned int tree, order; + struct rb_root *root; + struct rb_node *iter; + unsigned long pages; + unsigned int order; u64 modify_size; int err; modify_size = rounddown_pow_of_two(size); - order = ilog2(modify_size) - ilog2(mm->chunk_size); + pages = modify_size >> ilog2(mm->chunk_size); + order = fls(pages) - 1; if (order == 0) return -ENOSPC; - for_each_free_tree(tree) { - struct rb_root *root; - struct rb_node *iter; - - root = &mm->free_trees[tree][order]; - if (rbtree_is_empty(root)) - continue; - - iter = rb_last(root); - while (iter) { - block = rbtree_get_free_block(iter); - - rhs_offset = gpu_buddy_block_offset(block); - - /* Allocate blocks traversing RHS */ - err = __gpu_buddy_alloc_range(mm, rhs_offset, size, - &filled, blocks); - if (err && err != -ENOSPC) - return err; - if (!err && IS_ALIGNED(rhs_offset, min_block_size)) - return 0; - if (!err) { - /* Allocate the unaligned RHS offset using round_down */ - gpu_buddy_free_list_internal(mm, blocks); - err = __alloc_contig_aligned_retry(mm, rhs_offset, - size, - min_block_size, - blocks); - if (!err) - return 0; - if (err != -ENOSPC) { - gpu_buddy_free_list_internal(mm, blocks); - return err; - } - goto next; - } - - if (size - filled > rhs_offset) - goto next; - - lhs_offset = rhs_offset - (size - filled); + root = &mm->free_tree[order]; + if (RB_EMPTY_ROOT(root)) + return -ENOSPC; - /* Allocate the unaligned LHS offset using round_down */ + iter = rb_last(root); + while (iter) { + block = rbtree_get_free_block(iter); + + rhs_offset = gpu_buddy_block_offset(block); + + /* Allocate blocks traversing RHS */ + err = __gpu_buddy_alloc_range(mm, rhs_offset, size, + flags, &filled, blocks); + if (err && err != -ENOSPC) + return err; + if (!err && IS_ALIGNED(rhs_offset, min_block_size)) + return 0; + if (!err) { + /* Allocate the unaligned RHS offset using round_down */ gpu_buddy_free_list_internal(mm, blocks); - err = __alloc_contig_aligned_retry(mm, lhs_offset, size, - min_block_size, blocks); + err = __alloc_contig_aligned_retry(mm, rhs_offset, + size, + min_block_size, + flags, blocks); if (!err) return 0; if (err != -ENOSPC) { gpu_buddy_free_list_internal(mm, blocks); return err; } -next: + goto next; + } + + if (size - filled > rhs_offset) + goto next; + + lhs_offset = rhs_offset - (size - filled); + + /* Allocate the unaligned LHS offset using round_down */ + gpu_buddy_free_list_internal(mm, blocks); + err = __alloc_contig_aligned_retry(mm, lhs_offset, + size, + min_block_size, + flags, blocks); + if (!err) + return 0; + if (err != -ENOSPC) { gpu_buddy_free_list_internal(mm, blocks); - iter = rb_prev(iter); + return err; } +next: + gpu_buddy_free_list_internal(mm, blocks); + iter = rb_prev(iter); } return -ENOSPC; @@ -1282,6 +1805,7 @@ int gpu_buddy_block_trim(struct gpu_buddy *mm, struct gpu_buddy_block *block; u64 block_start, block_end; LIST_HEAD(dfs); + bool was_clear; u64 new_start; int err; @@ -1324,22 +1848,35 @@ int gpu_buddy_block_trim(struct gpu_buddy *mm, } list_del(&block->link); - mark_free(mm, block); + + was_clear = gpu_buddy_block_is_clear(block); + + if (!was_clear) + gpu_dirty_tracker_mark_dirty(&mm->dirty, + gpu_buddy_block_offset(block), + gpu_buddy_block_size(mm, block)); + + __mark_free(mm, block, was_clear ? GPU_BLOCK_CLEAR : GPU_BLOCK_DIRTY); mm->avail += gpu_buddy_block_size(mm, block); - if (gpu_buddy_block_is_clear(block)) - mm->clear_avail += gpu_buddy_block_size(mm, block); /* Prevent recursively freeing this node */ parent = block->parent; block->parent = NULL; list_add(&block->tmp_link, &dfs); - err = __alloc_range(mm, &dfs, new_start, new_size, blocks, NULL); + err = __alloc_range(mm, &dfs, new_start, new_size, + was_clear ? GPU_BUDDY_CLEAR_ALLOCATION : 0, + blocks, NULL); if (err) { mark_allocated(mm, block); mm->avail -= gpu_buddy_block_size(mm, block); - if (gpu_buddy_block_is_clear(block)) - mm->clear_avail -= gpu_buddy_block_size(mm, block); + if (!was_clear) { + gpu_dirty_tracker_remove_range(&mm->dirty, + gpu_buddy_block_offset(block), + gpu_buddy_block_size(mm, block)); + } + if (was_clear) + block->header |= GPU_BUDDY_HEADER_CLEAR; list_add(&block->link, blocks); } @@ -1348,6 +1885,22 @@ int gpu_buddy_block_trim(struct gpu_buddy *mm, } EXPORT_SYMBOL(gpu_buddy_block_trim); +static bool dirty_steer_window(struct gpu_buddy *mm, u64 req_size, + u64 *start, u64 *end, unsigned long *flags) +{ + u64 aligned_start; + struct gpu_dirty_extent *ext = + gpu_dirty_tracker_find(&mm->dirty, req_size, &aligned_start); + + if (!ext) + return false; + + *start = aligned_start; + *end = ext->end; + *flags |= GPU_BUDDY_RANGE_ALLOCATION; + return true; +} + static struct gpu_buddy_block * __gpu_buddy_alloc_blocks(struct gpu_buddy *mm, u64 start, u64 end, @@ -1355,18 +1908,36 @@ __gpu_buddy_alloc_blocks(struct gpu_buddy *mm, unsigned int order, unsigned long flags) { - if (flags & GPU_BUDDY_RANGE_ALLOCATION) + struct gpu_buddy_block *block; + bool steered = false; + + /* Allocate from dirty tracker */ + if (!(flags & GPU_BUDDY_RANGE_ALLOCATION) && + !(flags & GPU_BUDDY_CLEAR_ALLOCATION) && + size >= min_block_size && + gpu_buddy_clear_avail(mm) && mm->dirty.total_dirty) { + u64 block_size = mm->chunk_size << order; + + steered = dirty_steer_window(mm, block_size, + &start, &end, &flags); + } + + if (flags & GPU_BUDDY_RANGE_ALLOCATION) { /* Allocate traversing within the range */ - return __gpu_buddy_alloc_range_bias(mm, start, end, - order, flags); - else if (size < min_block_size) + block = __alloc_range_bias(mm, start, end, order, flags); + if (!IS_ERR(block) || !steered) + return block; + + flags &= ~GPU_BUDDY_RANGE_ALLOCATION; + } + + if (size < min_block_size) /* Allocate from an offset-aligned region without size rounding */ return gpu_buddy_offset_aligned_allocation(mm, size, - min_block_size, - flags); - else - /* Allocate from freetree */ - return alloc_from_freetree(mm, order, flags); + min_block_size); + + /* Allocate from freetree */ + return alloc_from_freetree(mm, order, flags); } /** @@ -1427,7 +1998,7 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm, if (!IS_ALIGNED(start | end, min_block_size)) return -EINVAL; - return __gpu_buddy_alloc_range(mm, start, size, NULL, blocks); + return __gpu_buddy_alloc_range(mm, start, size, flags, NULL, blocks); } original_size = size; @@ -1453,7 +2024,8 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm, if ((flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION) && !(flags & GPU_BUDDY_RANGE_ALLOCATION)) return __alloc_contig_try_harder(mm, original_size, - original_min_size, blocks); + original_min_size, + flags, blocks); return -EINVAL; } @@ -1468,8 +2040,6 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm, BUG_ON(size >= min_block_size && order < min_order); do { - unsigned int fallback_order; - block = __gpu_buddy_alloc_blocks(mm, start, end, size, @@ -1479,48 +2049,40 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm, if (!IS_ERR(block)) break; - if (size < min_block_size) { - fallback_order = order; - } else if (order == min_order) { - fallback_order = min_order; - } else { + if (size >= min_block_size && order > min_order) { order--; continue; } - /* Try allocation through force merge method */ - if (mm->clear_avail && - !__force_merge(mm, start, end, fallback_order)) { - block = __gpu_buddy_alloc_blocks(mm, start, - end, - size, - min_block_size, - fallback_order, - flags); - if (!IS_ERR(block)) { - order = fallback_order; - break; - } - } - /* * Try contiguous block allocation through * try harder method. */ if (flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION && - !(flags & GPU_BUDDY_RANGE_ALLOCATION)) - return __alloc_contig_try_harder(mm, - original_size, - original_min_size, - blocks); + !(flags & GPU_BUDDY_RANGE_ALLOCATION)) { + err = __alloc_contig_try_harder(mm, + original_size, + original_min_size, + flags, + blocks); + if (!err) + return 0; + if (err != -ENOSPC) + return err; + goto err_free; + } err = -ENOSPC; goto err_free; } while (1); + if (!gpu_buddy_block_is_clear(block)) + gpu_dirty_tracker_remove_range(&mm->dirty, + gpu_buddy_block_offset(block), + gpu_buddy_block_size(mm, block)); + mark_allocated(mm, block); mm->avail -= gpu_buddy_block_size(mm, block); - if (gpu_buddy_block_is_clear(block)) - mm->clear_avail -= gpu_buddy_block_size(mm, block); + kmemleak_update_trace(block); list_add_tail(&block->link, &allocated); @@ -1594,7 +2156,8 @@ void gpu_buddy_print(struct gpu_buddy *mm) gpu_buddy_driver_lock_held(mm); pr_info("chunk_size: %lluKiB, total: %lluMiB, free: %lluMiB, clear_free: %lluMiB\n", - mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20); + mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, + gpu_buddy_clear_avail(mm) >> 20); for (order = mm->max_order; order >= 0; order--) { u64 free_count = mm->free_scoreboard[order]; @@ -1615,6 +2178,7 @@ EXPORT_SYMBOL(gpu_buddy_print); static void gpu_buddy_module_exit(void) { + kmem_cache_destroy(slab_extents); kmem_cache_destroy(slab_blocks); } @@ -1624,7 +2188,15 @@ static int __init gpu_buddy_module_init(void) if (!slab_blocks) return -ENOMEM; + slab_extents = KMEM_CACHE(gpu_dirty_extent, 0); + if (!slab_extents) + goto err_destroy_blocks; + return 0; + +err_destroy_blocks: + kmem_cache_destroy(slab_blocks); + return -ENOMEM; } module_init(gpu_buddy_module_init); diff --git a/drivers/gpu/drm/adp/adp_drv.c b/drivers/gpu/drm/adp/adp_drv.c index ce9dd6222634..fdf0e6316175 100644 --- a/drivers/gpu/drm/adp/adp_drv.c +++ b/drivers/gpu/drm/adp/adp_drv.c @@ -351,7 +351,7 @@ static const struct drm_crtc_funcs adp_crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .enable_vblank = adp_crtc_enable_vblank, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 2fe934036e36..4426e18f7543 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -36,7 +36,6 @@ #include "dc/inc/hw/dmcu.h" #include "dc/inc/hw/abm.h" #include "dc/dc_dmub_srv.h" -#include "dc/dc_edid_parser.h" #include "dc/dc_stat.h" #include "dc/dc_state.h" #include "amdgpu_dm_trace.h" @@ -2883,7 +2882,8 @@ STATIC_IFN_KUNIT bool modereset_required(struct drm_crtc_state *crtc_state) EXPORT_IF_KUNIT(modereset_required); STATIC_IFN_KUNIT int -fill_plane_color_attributes(const struct drm_plane_state *plane_state, +fill_plane_color_attributes(struct drm_atomic_commit *state, + const struct drm_plane_state *plane_state, const enum surface_pixel_format format, enum dc_color_space *color_space) { @@ -2892,7 +2892,7 @@ fill_plane_color_attributes(const struct drm_plane_state *plane_state, *color_space = COLOR_SPACE_SRGB; /* Ignore properties when DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE is set */ - if (plane_state->state && plane_state->state->plane_color_pipeline) + if (state && state->plane_color_pipeline) return 0; /* DRM color properties only affect non-RGB formats. */ @@ -2933,6 +2933,7 @@ EXPORT_IF_KUNIT(fill_plane_color_attributes); static int fill_dc_plane_info_and_addr(struct amdgpu_device *adev, + struct drm_atomic_commit *state, const struct drm_plane_state *plane_state, struct dc_plane_info *plane_info, struct dc_plane_address *address, @@ -3025,7 +3026,7 @@ fill_dc_plane_info_and_addr(struct amdgpu_device *adev, plane_info->layer_index = plane_state->normalized_zpos; - ret = fill_plane_color_attributes(plane_state, plane_info->format, + ret = fill_plane_color_attributes(state, plane_state, plane_info->format, &plane_info->color_space); if (ret) return ret; @@ -3066,7 +3067,7 @@ static int fill_dc_plane_attributes(struct amdgpu_device *adev, dc_plane_state->clip_rect = scaling_info.clip_rect; dc_plane_state->scaling_quality = scaling_info.scaling_quality; - ret = fill_dc_plane_info_and_addr(adev, plane_state, + ret = fill_dc_plane_info_and_addr(adev, plane_state->state, plane_state, &plane_info, &dc_plane_state->address, afb->tmz_surface); @@ -3903,11 +3904,13 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, } fill_dc_plane_info_and_addr( - dm->adev, new_plane_state, + dm->adev, state, new_plane_state, &bundle->plane_infos[planes_count], &bundle->flip_addrs[planes_count].address, afb->tmz_surface); + bundle->plane_infos[planes_count].color_space = dc_plane->color_space; + drm_dbg_state(state->dev, "plane: id=%d dcc_en=%d\n", new_plane_state->plane->index, bundle->plane_infos[planes_count].dcc.enable); @@ -5897,6 +5900,19 @@ static int dm_update_plane_state(struct dc *dc, if (ret) goto out; + /* + * Recreating the plane re-derives its DC color pipeline from the + * colorop states in this commit. Pull the plane's colorops in so + * the derivation sees the current pipeline; otherwise every stage + * silently defaults to bypass (dropping YUV->RGB, shaper, 3D LUT + * and regamma) when userspace didn't touch color in this commit. + */ + if (new_plane_state->color_pipeline) { + ret = drm_atomic_add_affected_colorops(state, plane); + if (ret) + goto out; + } + WARN_ON(dm_new_plane_state->dc_state); dc_new_plane_state = dc_create_plane_state(dc); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index 3524931451c8..774028dd9ff4 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -1172,9 +1172,10 @@ struct dm_atomic_state *dm_atomic_get_new_state(struct drm_atomic_commit *state) void dm_atomic_destroy_state(struct drm_private_obj *obj, struct drm_private_state *state); int dm_plane_layer_index_cmp(const void *a, const void *b); -int fill_plane_color_attributes(const struct drm_plane_state *plane_state, - const enum surface_pixel_format format, - enum dc_color_space *color_space); +int fill_plane_color_attributes(struct drm_atomic_commit *state, + const struct drm_plane_state *plane_state, + const enum surface_pixel_format format, + enum dc_color_space *color_space); bool modereset_required(struct drm_crtc_state *crtc_state); bool is_scaling_state_different(const struct dm_connector_state *dm_state, const struct dm_connector_state *old_dm_state); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c index d55dc06167a8..1125ebac6ccb 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c @@ -1551,6 +1551,8 @@ __set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state, struct dc_transfer_func *tf = &dc_plane_state->in_transfer_func; struct drm_colorop *colorop = colorop_state->colorop; struct drm_device *drm = colorop->dev; + struct dc_color_caps *color_caps = NULL; + bool is_subsampled_format; if (colorop->type != DRM_COLOROP_1D_CURVE) return -EINVAL; @@ -1564,14 +1566,82 @@ __set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state, } drm_dbg(drm, "Degamma colorop with ID: %d\n", colorop->base.id); - tf->type = TF_TYPE_PREDEFINED; + + /* Check if format requires post-scale color processing (subsampled formats) */ + is_subsampled_format = (dc_plane_state->format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN && + dc_plane_state->format < SURFACE_PIXEL_FORMAT_SUBSAMPLE_END); + tf->tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); + if (dc_plane_state->ctx && dc_plane_state->ctx->dc) + color_caps = &dc_plane_state->ctx->dc->caps.color; + + if (!mod_color_calculate_degamma_params(color_caps, tf, NULL, + is_subsampled_format)) { + drm_err(drm, "Failed to calculate degamma params\n"); + return -EINVAL; + } + return 0; } EXPORT_IF_KUNIT(__set_colorop_in_tf_1d_curve); +static int +__set_dm_plane_colorop_fixed_matrix(struct drm_plane_state *plane_state, + struct dc_plane_state *dc_plane_state, + struct drm_colorop *colorop) +{ + struct drm_colorop *old_colorop; + struct drm_colorop_state *colorop_state = NULL, *new_colorop_state; + struct drm_atomic_commit *state = plane_state->state; + int i = 0; + + old_colorop = colorop; + + for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { + if (new_colorop_state->colorop == old_colorop) { + colorop_state = new_colorop_state; + break; + } + } + + if (!colorop_state) + return -EINVAL; + + if (colorop_state->bypass) { + dc_plane_state->color_space = COLOR_SPACE_SRGB; + return 0; + } + + switch (colorop_state->fixed_matrix_type) { + case DRM_COLOROP_FM_YCBCR601_FULL_RGB: + dc_plane_state->color_space = COLOR_SPACE_YCBCR601; + break; + case DRM_COLOROP_FM_YCBCR601_LIMITED_RGB: + dc_plane_state->color_space = COLOR_SPACE_YCBCR601_LIMITED; + break; + case DRM_COLOROP_FM_YCBCR709_FULL_RGB: + dc_plane_state->color_space = COLOR_SPACE_YCBCR709; + break; + case DRM_COLOROP_FM_YCBCR709_LIMITED_RGB: + dc_plane_state->color_space = COLOR_SPACE_YCBCR709_LIMITED; + break; + case DRM_COLOROP_FM_YCBCR2020_NC_FULL_RGB: + dc_plane_state->color_space = COLOR_SPACE_2020_YCBCR_FULL; + break; + case DRM_COLOROP_FM_YCBCR2020_NC_LIMITED_RGB: + dc_plane_state->color_space = COLOR_SPACE_2020_YCBCR_LIMITED; + break; + default: + return -EINVAL; + } + + dc_plane_state->update_bits.full_update = 1; + + return 0; +} + STATIC_IFN_KUNIT int __set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state, @@ -1976,10 +2046,21 @@ amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state *plane_state, bool has_3dlut = adev->dm.dc->caps.color.dpp.hw_3d_lut || adev->dm.dc->caps.color.mpc.preblend; int ret; - /* 1D Curve - DEGAM TF */ + /* Fixed Matrix (YUV to RGB) */ if (!colorop) return -EINVAL; + ret = __set_dm_plane_colorop_fixed_matrix(plane_state, dc_plane_state, colorop); + if (ret) + return ret; + + /* 1D Curve - DEGAM TF */ + colorop = colorop->next; + if (!colorop) { + drm_dbg(dev, "no degamma colorop found\n"); + return -EINVAL; + } + ret = __set_dm_plane_colorop_degamma(plane_state, dc_plane_state, colorop); if (ret) return ret; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c index fae9007b9998..d66f71336247 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c @@ -55,6 +55,14 @@ const u64 amdgpu_dm_supported_blnd_tfs = BIT(DRM_COLOROP_1D_CURVE_GAMMA22); EXPORT_IF_KUNIT(amdgpu_dm_supported_blnd_tfs); +const u64 amdgpu_dm_supported_fm = + BIT(DRM_COLOROP_FM_YCBCR601_FULL_RGB) | + BIT(DRM_COLOROP_FM_YCBCR601_LIMITED_RGB) | + BIT(DRM_COLOROP_FM_YCBCR709_FULL_RGB) | + BIT(DRM_COLOROP_FM_YCBCR709_LIMITED_RGB) | + BIT(DRM_COLOROP_FM_YCBCR2020_NC_FULL_RGB) | + BIT(DRM_COLOROP_FM_YCBCR2020_NC_LIMITED_RGB); + #define MAX_COLOR_PIPELINE_OPS 10 #define LUT3D_SIZE 17 @@ -73,6 +81,23 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane memset(ops, 0, sizeof(ops)); + /* Fixed Matrix (YUV to RGB) */ + ops[i] = kzalloc_obj(*ops[0]); + if (!ops[i]) { + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_plane_colorop_fixed_matrix_init(dev, ops[i], plane, &dm_colorop_funcs, + amdgpu_dm_supported_fm, + DRM_COLOROP_FLAG_ALLOW_BYPASS); + if (ret) + goto cleanup; + + list->type = ops[i]->base.id; + + i++; + /* 1D curve - DEGAM TF */ ops[i] = kzalloc_obj(*ops[0]); if (!ops[i]) { @@ -86,7 +111,7 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane if (ret) goto cleanup; - list->type = ops[i]->base.id; + drm_colorop_set_next_property(ops[i - 1], ops[i]); i++; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h index 77364d954d3b..7802efa5312d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h @@ -30,6 +30,7 @@ extern const u64 amdgpu_dm_supported_degam_tfs; extern const u64 amdgpu_dm_supported_shaper_tfs; extern const u64 amdgpu_dm_supported_blnd_tfs; +extern const u64 amdgpu_dm_supported_fm; int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c index c8a1ab8c3b16..e7b555fa666f 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c @@ -27,7 +27,6 @@ #include "dm_services_types.h" #include "dc.h" #include "dc/dc_dmub_srv.h" -#include "dc/dc_edid_parser.h" #include "dc/dc_stat.h" #include "dc/dc_state.h" #include "dc/dc_stream.h" @@ -3591,139 +3590,6 @@ void dm_restore_drm_connector_state(struct drm_device *dev, dm_force_atomic_commit(&aconnector->base); } -static bool dm_edid_parser_send_cea(struct amdgpu_display_manager *dm, - unsigned int offset, - unsigned int total_length, - u8 *data, - unsigned int length, - struct amdgpu_hdmi_vsdb_info *vsdb) -{ - bool res; - union dmub_rb_cmd cmd; - struct dmub_cmd_send_edid_cea *input; - struct dmub_cmd_edid_cea_output *output; - - if (length > DMUB_EDID_CEA_DATA_CHUNK_BYTES) - return false; - - memset(&cmd, 0, sizeof(cmd)); - - input = &cmd.edid_cea.data.input; - - cmd.edid_cea.header.type = DMUB_CMD__EDID_CEA; - cmd.edid_cea.header.sub_type = 0; - cmd.edid_cea.header.payload_bytes = - sizeof(cmd.edid_cea) - sizeof(cmd.edid_cea.header); - input->offset = offset; - input->length = length; - input->cea_total_length = total_length; - memcpy(input->payload, data, length); - - res = dc_wake_and_execute_dmub_cmd(dm->dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY); - if (!res) { - drm_err(adev_to_drm(dm->adev), "EDID CEA parser failed\n"); - return false; - } - - output = &cmd.edid_cea.data.output; - - if (output->type == DMUB_CMD__EDID_CEA_ACK) { - if (!output->ack.success) { - drm_err(adev_to_drm(dm->adev), "EDID CEA ack failed at offset %d\n", - output->ack.offset); - } - } else if (output->type == DMUB_CMD__EDID_CEA_AMD_VSDB) { - if (!output->amd_vsdb.vsdb_found) - return false; - - vsdb->freesync_supported = output->amd_vsdb.freesync_supported; - vsdb->amd_vsdb_version = output->amd_vsdb.amd_vsdb_version; - vsdb->min_refresh_rate_hz = output->amd_vsdb.min_frame_rate; - vsdb->max_refresh_rate_hz = output->amd_vsdb.max_frame_rate; - vsdb->freesync_mccs_vcp_code = output->amd_vsdb.freesync_mccs_vcp_code; - } else { - drm_warn(adev_to_drm(dm->adev), "Unknown EDID CEA parser results\n"); - return false; - } - - return true; -} - -static bool parse_edid_cea_dmcu(struct amdgpu_display_manager *dm, - u8 *edid_ext, int len, - struct amdgpu_hdmi_vsdb_info *vsdb_info) -{ - int i; - - /* send extension block to DMCU for parsing */ - for (i = 0; i < len; i += 8) { - bool res; - int offset; - - /* send 8 bytes a time */ - if (!dc_edid_parser_send_cea(dm->dc, i, len, &edid_ext[i], 8)) - return false; - - if (i+8 == len) { - /* EDID block sent completed, expect result */ - int version, min_rate, max_rate; - - res = dc_edid_parser_recv_amd_vsdb(dm->dc, &version, &min_rate, &max_rate); - if (res) { - /* amd vsdb found */ - vsdb_info->freesync_supported = 1; - vsdb_info->amd_vsdb_version = version; - vsdb_info->min_refresh_rate_hz = min_rate; - vsdb_info->max_refresh_rate_hz = max_rate; - /* Not enabled on DMCU*/ - vsdb_info->freesync_mccs_vcp_code = 0; - return true; - } - /* not amd vsdb */ - return false; - } - - /* check for ack*/ - res = dc_edid_parser_recv_cea_ack(dm->dc, &offset); - if (!res) - return false; - } - - return false; -} - -static bool parse_edid_cea_dmub(struct amdgpu_display_manager *dm, - u8 *edid_ext, int len, - struct amdgpu_hdmi_vsdb_info *vsdb_info) -{ - int i; - - /* send extension block to DMCU for parsing */ - for (i = 0; i < len; i += 8) { - /* send 8 bytes a time */ - if (!dm_edid_parser_send_cea(dm, i, len, &edid_ext[i], 8, vsdb_info)) - return false; - } - - return vsdb_info->freesync_supported; -} - -static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector, - u8 *edid_ext, int len, - struct amdgpu_hdmi_vsdb_info *vsdb_info) -{ - struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev); - bool ret; - - mutex_lock(&adev->dm.dc_lock); - if (adev->dm.dmub_srv) - ret = parse_edid_cea_dmub(&adev->dm, edid_ext, len, vsdb_info); - else - ret = parse_edid_cea_dmcu(&adev->dm, edid_ext, len, vsdb_info); - mutex_unlock(&adev->dm.dc_lock); - return ret; -} - STATIC_IFN_KUNIT void parse_edid_displayid_vrr(struct drm_connector *connector, const struct edid *edid) { @@ -3775,44 +3641,15 @@ STATIC_IFN_KUNIT int get_amd_vsdb(struct amdgpu_dm_connector *aconnector, vsdb_info->replay_mode = connector->display_info.amd_vsdb.replay_mode; vsdb_info->amd_vsdb_version = connector->display_info.amd_vsdb.version; + vsdb_info->freesync_supported = connector->display_info.amd_vsdb.freesync_supported; + vsdb_info->min_refresh_rate_hz = connector->display_info.amd_vsdb.min_frame_rate; + vsdb_info->max_refresh_rate_hz = connector->display_info.amd_vsdb.max_frame_rate; + vsdb_info->freesync_mccs_vcp_code = connector->display_info.amd_vsdb.freesync_vcp_code; return connector->display_info.amd_vsdb.version != 0; } EXPORT_IF_KUNIT(get_amd_vsdb); -STATIC_IFN_KUNIT int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector, - const struct edid *edid, - struct amdgpu_hdmi_vsdb_info *vsdb_info) -{ - u8 *edid_ext = NULL; - int i; - bool valid_vsdb_found = false; - - /*----- drm_find_cea_extension() -----*/ - /* No EDID or EDID extensions */ - if (edid == NULL || edid->extensions == 0) - return -ENODEV; - - /* Find CEA extension */ - for (i = 0; i < edid->extensions; i++) { - edid_ext = (uint8_t *)edid + EDID_LENGTH * (i + 1); - if (edid_ext[0] == CEA_EXT) - break; - } - - if (i == edid->extensions) - return -ENODEV; - - /*----- cea_db_offsets() -----*/ - if (edid_ext[0] != CEA_EXT) - return -ENODEV; - - valid_vsdb_found = parse_edid_cea(aconnector, edid_ext, EDID_LENGTH, vsdb_info); - - return valid_vsdb_found ? i : -ENODEV; -} -EXPORT_IF_KUNIT(parse_hdmi_amd_vsdb); - /** * amdgpu_dm_update_freesync_caps - Update Freesync capabilities * @@ -3831,7 +3668,7 @@ EXPORT_IF_KUNIT(parse_hdmi_amd_vsdb); void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, const struct drm_edid *drm_edid, bool do_mccs) { - int i = 0; + bool has_vsdb = 0; struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector); struct dm_connector_state *dm_con_state = NULL; @@ -3876,63 +3713,57 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, connector->display_info.monitor_range.max_vfreq == 0)) parse_edid_displayid_vrr(connector, edid); - if (edid && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT || - sink->sink_signal == SIGNAL_TYPE_EDP)) { - if (amdgpu_dm_connector->dc_link && - amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param) { - amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq; - amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq; - if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) - freesync_capable = true; + has_vsdb = get_amd_vsdb(amdgpu_dm_connector, &vsdb_info) != 0; + + if (has_vsdb) { + amdgpu_dm_connector->vsdb_info = vsdb_info; + + /* copy refresh rate info as long as VSDB advertises FreeSync */ + if (vsdb_info.freesync_supported) { + amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz; + amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz; + + connector->display_info.monitor_range.min_vfreq = + vsdb_info.min_refresh_rate_hz; + connector->display_info.monitor_range.max_vfreq = + vsdb_info.max_refresh_rate_hz; } + } else { + /* fall back to the base EDID range when there is no VSDB */ + amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq; + amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq; + } - get_amd_vsdb(amdgpu_dm_connector, &vsdb_info); + if (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT || + sink->sink_signal == SIGNAL_TYPE_EDP) { + if (amdgpu_dm_connector->dc_link && + amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param && + amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) + freesync_capable = true; if (vsdb_info.replay_mode) { - amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_info.replay_mode; - amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_info.amd_vsdb_version; amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP; } + } else if (has_vsdb && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) { + sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code; - } else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) { - i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); - if (i >= 0) { - amdgpu_dm_connector->vsdb_info = vsdb_info; - sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code; - - if (vsdb_info.freesync_supported) { - amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz; - amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz; - if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) - freesync_capable = true; - - connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz; - connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; - } - } + if (vsdb_info.freesync_supported && + amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) + freesync_capable = true; } if (amdgpu_dm_connector->dc_link) as_type = dm_get_adaptive_sync_support_type(amdgpu_dm_connector->dc_link); - if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) { - i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); - if (i >= 0) { - amdgpu_dm_connector->vsdb_info = vsdb_info; - sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code; - - if (vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) { - amdgpu_dm_connector->pack_sdp_v1_3 = true; - amdgpu_dm_connector->as_type = as_type; + if (has_vsdb && as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) { + sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code; - amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz; - amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz; - if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) - freesync_capable = true; + if (vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) { + amdgpu_dm_connector->pack_sdp_v1_3 = true; + amdgpu_dm_connector->as_type = as_type; - connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz; - connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; - } + if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) + freesync_capable = true; } } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c index 5645866610fc..d9331e999ec3 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c @@ -335,7 +335,7 @@ static void dm_test_fill_color_attr_rgb_format(struct kunit *test) plane_state.color_encoding = DRM_COLOR_YCBCR_BT709; plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE; - ret = fill_plane_color_attributes(&plane_state, + ret = fill_plane_color_attributes(NULL, &plane_state, SURFACE_PIXEL_FORMAT_GRPH_ARGB8888, &color_space); KUNIT_EXPECT_EQ(test, ret, 0); @@ -355,7 +355,7 @@ static void dm_test_fill_color_attr_bt601_full(struct kunit *test) plane_state.color_encoding = DRM_COLOR_YCBCR_BT601; plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE; - ret = fill_plane_color_attributes(&plane_state, + ret = fill_plane_color_attributes(NULL, &plane_state, SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, &color_space); KUNIT_EXPECT_EQ(test, ret, 0); @@ -375,7 +375,7 @@ static void dm_test_fill_color_attr_bt601_limited(struct kunit *test) plane_state.color_encoding = DRM_COLOR_YCBCR_BT601; plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE; - ret = fill_plane_color_attributes(&plane_state, + ret = fill_plane_color_attributes(NULL, &plane_state, SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, &color_space); KUNIT_EXPECT_EQ(test, ret, 0); @@ -396,7 +396,7 @@ static void dm_test_fill_color_attr_bt709_full(struct kunit *test) plane_state.color_encoding = DRM_COLOR_YCBCR_BT709; plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE; - ret = fill_plane_color_attributes(&plane_state, + ret = fill_plane_color_attributes(NULL, &plane_state, SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, &color_space); KUNIT_EXPECT_EQ(test, ret, 0); @@ -416,7 +416,7 @@ static void dm_test_fill_color_attr_bt709_limited(struct kunit *test) plane_state.color_encoding = DRM_COLOR_YCBCR_BT709; plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE; - ret = fill_plane_color_attributes(&plane_state, + ret = fill_plane_color_attributes(NULL, &plane_state, SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, &color_space); KUNIT_EXPECT_EQ(test, ret, 0); @@ -437,7 +437,7 @@ static void dm_test_fill_color_attr_bt2020_full(struct kunit *test) plane_state.color_encoding = DRM_COLOR_YCBCR_BT2020; plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE; - ret = fill_plane_color_attributes(&plane_state, + ret = fill_plane_color_attributes(NULL, &plane_state, SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, &color_space); KUNIT_EXPECT_EQ(test, ret, 0); @@ -458,7 +458,7 @@ static void dm_test_fill_color_attr_bt2020_limited(struct kunit *test) plane_state.color_encoding = DRM_COLOR_YCBCR_BT2020; plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE; - ret = fill_plane_color_attributes(&plane_state, + ret = fill_plane_color_attributes(NULL, &plane_state, SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, &color_space); KUNIT_EXPECT_EQ(test, ret, 0); @@ -479,7 +479,7 @@ static void dm_test_fill_color_attr_invalid_encoding(struct kunit *test) plane_state.color_encoding = 99; plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE; - ret = fill_plane_color_attributes(&plane_state, + ret = fill_plane_color_attributes(NULL, &plane_state, SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, &color_space); KUNIT_EXPECT_EQ(test, ret, -EINVAL); diff --git a/drivers/gpu/drm/amd/display/dc/Makefile b/drivers/gpu/drm/amd/display/dc/Makefile index 93d02956c5eb..e88a9cea8d88 100644 --- a/drivers/gpu/drm/amd/display/dc/Makefile +++ b/drivers/gpu/drm/amd/display/dc/Makefile @@ -63,7 +63,6 @@ include $(AMD_DC) FILES = FILES += dc_dmub_srv.o -FILES += dc_edid_parser.o FILES += dc_fused_io.o FILES += dc_helper.o FILES += core/dc.o diff --git a/drivers/gpu/drm/amd/display/dc/dc_edid_parser.c b/drivers/gpu/drm/amd/display/dc/dc_edid_parser.c deleted file mode 100644 index 0db5b49e9d5e..000000000000 --- a/drivers/gpu/drm/amd/display/dc/dc_edid_parser.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2021 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: AMD - * - */ - -#include "dce/dce_dmcu.h" -#include "dc_edid_parser.h" - -bool dc_edid_parser_send_cea(struct dc *dc, - int offset, - int total_length, - uint8_t *data, - int length) -{ - struct dmcu *dmcu = dc->res_pool->dmcu; - - if (dmcu && - dmcu->funcs->is_dmcu_initialized(dmcu) && - dmcu->funcs->send_edid_cea) { - return dmcu->funcs->send_edid_cea(dmcu, - offset, - total_length, - data, - length); - } - - return false; -} - -bool dc_edid_parser_recv_cea_ack(struct dc *dc, int *offset) -{ - struct dmcu *dmcu = dc->res_pool->dmcu; - - if (dmcu && - dmcu->funcs->is_dmcu_initialized(dmcu) && - dmcu->funcs->recv_edid_cea_ack) { - return dmcu->funcs->recv_edid_cea_ack(dmcu, offset); - } - - return false; -} - -bool dc_edid_parser_recv_amd_vsdb(struct dc *dc, - int *version, - int *min_frame_rate, - int *max_frame_rate) -{ - struct dmcu *dmcu = dc->res_pool->dmcu; - - if (dmcu && - dmcu->funcs->is_dmcu_initialized(dmcu) && - dmcu->funcs->recv_amd_vsdb) { - return dmcu->funcs->recv_amd_vsdb(dmcu, - version, - min_frame_rate, - max_frame_rate); - } - - return false; -} diff --git a/drivers/gpu/drm/amd/display/dc/dc_edid_parser.h b/drivers/gpu/drm/amd/display/dc/dc_edid_parser.h deleted file mode 100644 index da67ec06f0a2..000000000000 --- a/drivers/gpu/drm/amd/display/dc/dc_edid_parser.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2021 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: AMD - * - */ - -#ifndef _DC_EDID_PARSER_H_ -#define _DC_EDID_PARSER_H_ - -#include "core_types.h" - -bool dc_edid_parser_send_cea(struct dc *dc, - int offset, - int total_length, - uint8_t *data, - int length); - -bool dc_edid_parser_recv_cea_ack(struct dc *dc, int *offset); - -bool dc_edid_parser_recv_amd_vsdb(struct dc *dc, - int *version, - int *min_frame_rate, - int *max_frame_rate); - -#endif /* _DC_EDID_PARSER_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c index 25ebd8a52ae4..23de1c793ec3 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c @@ -56,9 +56,6 @@ #define MCP_BL_SET_PWM_FRAC 0x6A /* Enable or disable Fractional PWM */ #define CRC_WIN_NOTIFY 0x92 #define CRC_STOP_UPDATE 0x93 -#define MCP_SEND_EDID_CEA 0xA0 -#define EDID_CEA_CMD_ACK 1 -#define EDID_CEA_CMD_NACK 2 #define MASTER_COMM_CNTL_REG__MASTER_COMM_INTERRUPT_MASK 0x00000001L // PSP FW version @@ -812,121 +809,6 @@ static bool dcn20_unlock_phy(struct dmcu *dmcu) return true; } -static bool dcn10_send_edid_cea(struct dmcu *dmcu, - int offset, - int total_length, - uint8_t *data, - int length) -{ - struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); - uint32_t header, data1, data2; - - /* If microcontroller is not running, do nothing */ - if (dmcu->dmcu_state != DMCU_RUNNING) - return false; - - if (length > 8 || length <= 0) - return false; - - header = ((uint32_t)offset & 0xFFFF) << 16 | (total_length & 0xFFFF); - data1 = (((uint32_t)data[0]) << 24) | (((uint32_t)data[1]) << 16) | - (((uint32_t)data[2]) << 8) | ((uint32_t)data[3]); - data2 = (((uint32_t)data[4]) << 24) | (((uint32_t)data[5]) << 16) | - (((uint32_t)data[6]) << 8) | ((uint32_t)data[7]); - - /* waitDMCUReadyForCmd */ - REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000); - - /* setDMCUParam_Cmd */ - REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, MCP_SEND_EDID_CEA); - - REG_WRITE(MASTER_COMM_DATA_REG1, header); - REG_WRITE(MASTER_COMM_DATA_REG2, data1); - REG_WRITE(MASTER_COMM_DATA_REG3, data2); - - /* notifyDMCUMsg */ - REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); - - /* waitDMCUReadyForCmd */ - REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000); - - return true; -} - -static bool dcn10_get_scp_results(struct dmcu *dmcu, - uint32_t *cmd, - uint32_t *data1, - uint32_t *data2, - uint32_t *data3) -{ - struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); - - /* If microcontroller is not running, do nothing */ - if (dmcu->dmcu_state != DMCU_RUNNING) - return false; - - *cmd = REG_READ(SLAVE_COMM_CMD_REG); - *data1 = REG_READ(SLAVE_COMM_DATA_REG1); - *data2 = REG_READ(SLAVE_COMM_DATA_REG2); - *data3 = REG_READ(SLAVE_COMM_DATA_REG3); - - /* clear SCP interrupt */ - REG_UPDATE(SLAVE_COMM_CNTL_REG, SLAVE_COMM_INTERRUPT, 0); - - return true; -} - -static bool dcn10_recv_amd_vsdb(struct dmcu *dmcu, - int *version, - int *min_frame_rate, - int *max_frame_rate) -{ - uint32_t data[4]; - int cmd, ack, len; - - if (!dcn10_get_scp_results(dmcu, &data[0], &data[1], &data[2], &data[3])) - return false; - - cmd = data[0] & 0x3FF; - len = (data[0] >> 10) & 0x3F; - ack = data[1]; - - if (cmd != MCP_SEND_EDID_CEA || ack != EDID_CEA_CMD_ACK || len != 12) - return false; - - if ((data[2] & 0xFF)) { - *version = (data[2] >> 8) & 0xFF; - *min_frame_rate = (data[3] >> 16) & 0xFFFF; - *max_frame_rate = data[3] & 0xFFFF; - return true; - } - - return false; -} - -static bool dcn10_recv_edid_cea_ack(struct dmcu *dmcu, int *offset) -{ - uint32_t data[4]; - int cmd, ack; - - if (!dcn10_get_scp_results(dmcu, - &data[0], &data[1], &data[2], &data[3])) - return false; - - cmd = data[0] & 0x3FF; - ack = data[1]; - - if (cmd != MCP_SEND_EDID_CEA) - return false; - - if (ack == EDID_CEA_CMD_ACK) - return true; - - *offset = data[2]; /* nack */ - return false; -} - - #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) static void dcn10_forward_crc_window(struct dmcu *dmcu, struct rect *rect, @@ -1030,9 +912,6 @@ static const struct dmcu_funcs dcn10_funcs = { .get_psr_state = dcn10_get_dmcu_psr_state, .set_psr_wait_loop = dcn10_psr_wait_loop, .get_psr_wait_loop = dcn10_get_psr_wait_loop, - .send_edid_cea = dcn10_send_edid_cea, - .recv_amd_vsdb = dcn10_recv_amd_vsdb, - .recv_edid_cea_ack = dcn10_recv_edid_cea_ack, #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) .forward_crc_window = dcn10_forward_crc_window, .stop_crc_win_update = dcn10_stop_crc_win_update, diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h index de3113ecbc77..eebfd177463d 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h @@ -74,16 +74,6 @@ struct dmcu_funcs { bool (*is_dmcu_initialized)(struct dmcu *dmcu); bool (*lock_phy)(struct dmcu *dmcu); bool (*unlock_phy)(struct dmcu *dmcu); - bool (*send_edid_cea)(struct dmcu *dmcu, - int offset, - int total_length, - uint8_t *data, - int length); - bool (*recv_amd_vsdb)(struct dmcu *dmcu, - int *version, - int *min_frame_rate, - int *max_frame_rate); - bool (*recv_edid_cea_ack)(struct dmcu *dmcu, int *offset); #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) void (*forward_crc_window)(struct dmcu *dmcu, struct rect *rect, diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h index f0231f355fa7..7d29599a0b1d 100644 --- a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h +++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h @@ -1924,10 +1924,6 @@ enum dmub_cmd_type { */ DMUB_CMD__DPIA = 77, /** - * Command type used for EDID CEA parsing - */ - DMUB_CMD__EDID_CEA = 79, - /** * Command type used for getting usbc cable ID */ DMUB_CMD_GET_USBC_CABLE_ID = 81, @@ -6887,69 +6883,6 @@ struct dmub_rb_cmd_transmitter_set_phy_fsm { }; /** - * Maximum number of bytes a chunk sent to DMUB for parsing - */ -#define DMUB_EDID_CEA_DATA_CHUNK_BYTES 8 - -/** - * Represent a chunk of CEA blocks sent to DMUB for parsing - */ -struct dmub_cmd_send_edid_cea { - uint16_t offset; /**< offset into the CEA block */ - uint8_t length; /**< number of bytes in payload to copy as part of CEA block */ - uint16_t cea_total_length; /**< total length of the CEA block */ - uint8_t payload[DMUB_EDID_CEA_DATA_CHUNK_BYTES]; /**< data chunk of the CEA block */ - uint8_t pad[3]; /**< padding and for future expansion */ -}; - -/** - * Result of VSDB parsing from CEA block - */ -struct dmub_cmd_edid_cea_amd_vsdb { - uint8_t vsdb_found; /**< 1 if parsing has found valid AMD VSDB */ - uint8_t freesync_supported; /**< 1 if Freesync is supported */ - uint16_t amd_vsdb_version; /**< AMD VSDB version */ - uint16_t min_frame_rate; /**< Maximum frame rate */ - uint16_t max_frame_rate; /**< Minimum frame rate */ - uint8_t freesync_mccs_vcp_code; /**< Freesync MCCS VCP code */ -}; - -/** - * Result of sending a CEA chunk - */ -struct dmub_cmd_edid_cea_ack { - uint16_t offset; /**< offset of the chunk into the CEA block */ - uint8_t success; /**< 1 if this sending of chunk succeeded */ - uint8_t pad; /**< padding and for future expansion */ -}; - -/** - * Specify whether the result is an ACK/NACK or the parsing has finished - */ -enum dmub_cmd_edid_cea_reply_type { - DMUB_CMD__EDID_CEA_AMD_VSDB = 1, /**< VSDB parsing has finished */ - DMUB_CMD__EDID_CEA_ACK = 2, /**< acknowledges the CEA sending is OK or failing */ -}; - -/** - * Definition of a DMUB_CMD__EDID_CEA command. - */ -struct dmub_rb_cmd_edid_cea { - struct dmub_cmd_header header; /**< Command header */ - union dmub_cmd_edid_cea_data { - struct dmub_cmd_send_edid_cea input; /**< input to send CEA chunks */ - struct dmub_cmd_edid_cea_output { /**< output with results */ - uint8_t type; /**< dmub_cmd_edid_cea_reply_type */ - union { - struct dmub_cmd_edid_cea_amd_vsdb amd_vsdb; - struct dmub_cmd_edid_cea_ack ack; - }; - } output; /**< output to retrieve ACK/NACK or VSDB parsing results */ - } data; /**< Command data */ - -}; - -/** * struct dmub_cmd_cable_id_input - Defines the input of DMUB_CMD_GET_USBC_CABLE_ID command. */ struct dmub_cmd_cable_id_input { @@ -7802,10 +7735,6 @@ union dmub_rb_cmd { */ struct dmub_rb_cmd_set_tps_notification set_tps_notification; /** - * Definition of a DMUB_CMD__EDID_CEA command. - */ - struct dmub_rb_cmd_edid_cea edid_cea; - /** * Definition of a DMUB_CMD_GET_USBC_CABLE_ID command. */ struct dmub_rb_cmd_get_usbc_cable_id cable_id; diff --git a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c index 03d88e78165d..5786ef5787ac 100644 --- a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c +++ b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c @@ -1696,7 +1696,8 @@ bool mod_color_calculate_degamma_params(struct dc_color_caps *dc_caps, return true; if (dc_caps != NULL && - dc_caps->dpp.dcn_arch == 1) { + dc_caps->dpp.dcn_arch == 1 && + !map_user_ramp) { if (input_tf->tf == TRANSFER_FUNCTION_PQ && dc_caps->dpp.dgam_rom_caps.pq == 1) diff --git a/drivers/gpu/drm/arm/display/include/malidp_product.h b/drivers/gpu/drm/arm/display/include/malidp_product.h index 6f954bcdf40e..f9a3ee7ba4b7 100644 --- a/drivers/gpu/drm/arm/display/include/malidp_product.h +++ b/drivers/gpu/drm/arm/display/include/malidp_product.h @@ -8,14 +8,18 @@ #define _MALIDP_PRODUCT_H_ /* Product identification */ +/* GLB_CORE_ID fields as per HW specification: + * MINOR is 8 bits ([11:4]) and STATUS is 4 bits ([3:0]). + * Update masks/shifts accordingly. + */ #define MALIDP_CORE_ID(__product, __major, __minor, __status) \ ((((__product) & 0xFFFF) << 16) | (((__major) & 0xF) << 12) | \ - (((__minor) & 0xF) << 8) | ((__status) & 0xFF)) + (((__minor) & 0xFF) << 4) | ((__status) & 0xF)) #define MALIDP_CORE_ID_PRODUCT_ID(__core_id) ((__u32)(__core_id) >> 16) #define MALIDP_CORE_ID_MAJOR(__core_id) (((__u32)(__core_id) >> 12) & 0xF) -#define MALIDP_CORE_ID_MINOR(__core_id) (((__u32)(__core_id) >> 8) & 0xF) -#define MALIDP_CORE_ID_STATUS(__core_id) (((__u32)(__core_id)) & 0xFF) +#define MALIDP_CORE_ID_MINOR(__core_id) (((__u32)(__core_id) >> 4) & 0xFF) +#define MALIDP_CORE_ID_STATUS(__core_id) ((__u32)(__core_id) & 0xF) /* Mali-display product IDs */ #define MALIDP_D71_PRODUCT_ID 0x0071 diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c index e8cb782a6f8e..3d81b413efae 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c @@ -11,9 +11,9 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_encoder.h> #include <drm/drm_print.h> #include <drm/drm_vblank.h> -#include <drm/drm_simple_kms_helper.h> #include <drm/drm_bridge.h> #include "komeda_dev.h" @@ -493,19 +493,17 @@ static const struct drm_crtc_helper_funcs komeda_crtc_helper_funcs = { .mode_fixup = komeda_crtc_mode_fixup, }; -static void komeda_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *komeda_crtc_create_state(struct drm_crtc *crtc) { struct komeda_crtc_state *state; - if (crtc->state) - __drm_atomic_helper_crtc_destroy_state(crtc->state); + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); - kfree(to_kcrtc_st(crtc->state)); - crtc->state = NULL; + __drm_atomic_helper_crtc_state_init(&state->base, crtc); - state = kzalloc_obj(*state); - if (state) - __drm_atomic_helper_crtc_reset(crtc, &state->base); + return &state->base; } static struct drm_crtc_state * @@ -555,7 +553,7 @@ static const struct drm_crtc_funcs komeda_crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = komeda_crtc_reset, + .atomic_create_state = komeda_crtc_create_state, .atomic_duplicate_state = komeda_crtc_atomic_duplicate_state, .atomic_destroy_state = komeda_crtc_atomic_destroy_state, .enable_vblank = komeda_crtc_vblank_enable, @@ -635,6 +633,10 @@ static int komeda_attach_bridge(struct device *dev, return err; } +static const struct drm_encoder_funcs komeda_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + static int komeda_crtc_add(struct komeda_kms_dev *kms, struct komeda_crtc *kcrtc) { @@ -658,7 +660,8 @@ static int komeda_crtc_add(struct komeda_kms_dev *kms, * bridge */ kcrtc->encoder.possible_crtcs = drm_crtc_mask(crtc); - err = drm_simple_encoder_init(base, encoder, DRM_MODE_ENCODER_TMDS); + err = drm_encoder_init(base, encoder, &komeda_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); if (err) return err; diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c index 57a4a0c7d404..955dbef5d34d 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c @@ -132,19 +132,17 @@ static void komeda_plane_destroy(struct drm_plane *plane) kfree(to_kplane(plane)); } -static void komeda_plane_reset(struct drm_plane *plane) +static struct drm_plane_state *komeda_plane_create_state(struct drm_plane *plane) { struct komeda_plane_state *state; - if (plane->state) - __drm_atomic_helper_plane_destroy_state(plane->state); + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); - kfree(plane->state); - plane->state = NULL; + __drm_atomic_helper_plane_state_init(&state->base, plane); - state = kzalloc_obj(*state); - if (state) - __drm_atomic_helper_plane_reset(plane, &state->base); + return &state->base; } static struct drm_plane_state * @@ -188,7 +186,7 @@ static const struct drm_plane_funcs komeda_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = komeda_plane_destroy, - .reset = komeda_plane_reset, + .atomic_create_state = komeda_plane_create_state, .atomic_duplicate_state = komeda_plane_atomic_duplicate_state, .atomic_destroy_state = komeda_plane_atomic_destroy_state, .format_mod_supported = komeda_plane_format_mod_supported, diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c index 41cc3e080dc9..4872253a2449 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c @@ -143,12 +143,17 @@ static int komeda_wb_connector_add(struct komeda_kms_dev *kms, struct komeda_wb_connector *kwb_conn; struct drm_writeback_connector *wb_conn; struct drm_display_info *info; + struct drm_encoder *encoder; + struct drm_encoder *wb_encoder; u32 *formats, n_formats = 0; + u32 crtc_mask; int err; if (!kcrtc->master->wb_layer) return 0; + crtc_mask = drm_crtc_mask(&kcrtc->base); + kwb_conn = kzalloc_obj(*kwb_conn); if (!kwb_conn) return -ENOMEM; @@ -176,6 +181,34 @@ static int komeda_wb_connector_add(struct komeda_kms_dev *kms, return err; } + wb_encoder = &wb_conn->encoder; + + /* + * The writeback connector is associated with a single CRTC. Make its + * encoder clone-compatible only with encoders that can drive that CRTC. + * + * possible_clones must contain the encoder's own bit whenever it is + * non-zero. Add both the encoder itself and the writeback encoder when + * updating the reciprocal clone relationship. + */ + wb_encoder->possible_clones = drm_encoder_mask(wb_encoder); + + drm_for_each_encoder(encoder, &kms->base) { + u32 encoder_mask; + + if (encoder == wb_encoder) + continue; + + if (!(encoder->possible_crtcs & crtc_mask)) + continue; + + encoder_mask = drm_encoder_mask(encoder); + + wb_encoder->possible_clones |= encoder_mask; + encoder->possible_clones |= encoder_mask | + drm_encoder_mask(wb_encoder); + } + drm_connector_helper_add(&wb_conn->base, &komeda_wb_conn_helper_funcs); info = &kwb_conn->base.base.display_info; diff --git a/drivers/gpu/drm/arm/hdlcd_crtc.c b/drivers/gpu/drm/arm/hdlcd_crtc.c index 8565749a3e48..f288a9c7775d 100644 --- a/drivers/gpu/drm/arm/hdlcd_crtc.c +++ b/drivers/gpu/drm/arm/hdlcd_crtc.c @@ -67,7 +67,7 @@ static const struct drm_crtc_funcs hdlcd_crtc_funcs = { .destroy = hdlcd_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .enable_vblank = hdlcd_crtc_enable_vblank, @@ -297,7 +297,7 @@ static const struct drm_plane_helper_funcs hdlcd_plane_helper_funcs = { static const struct drm_plane_funcs hdlcd_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .reset = drm_atomic_helper_plane_reset, + .atomic_create_state = drm_atomic_helper_plane_create_state, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, }; diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c index ebe8e1078777..157ff148efe7 100644 --- a/drivers/gpu/drm/arm/malidp_crtc.c +++ b/drivers/gpu/drm/arm/malidp_crtc.c @@ -476,17 +476,17 @@ static void malidp_crtc_destroy_state(struct drm_crtc *crtc, kfree(mali_state); } -static void malidp_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *malidp_crtc_create_state(struct drm_crtc *crtc) { - struct malidp_crtc_state *state = kzalloc_obj(*state); + struct malidp_crtc_state *state; - if (crtc->state) - malidp_crtc_destroy_state(crtc, crtc->state); + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_crtc_state_init(&state->base, crtc); - if (state) - __drm_atomic_helper_crtc_reset(crtc, &state->base); - else - __drm_atomic_helper_crtc_reset(crtc, NULL); + return &state->base; } static int malidp_crtc_enable_vblank(struct drm_crtc *crtc) @@ -511,7 +511,7 @@ static void malidp_crtc_disable_vblank(struct drm_crtc *crtc) static const struct drm_crtc_funcs malidp_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = malidp_crtc_reset, + .atomic_create_state = malidp_crtc_create_state, .atomic_duplicate_state = malidp_crtc_duplicate_state, .atomic_destroy_state = malidp_crtc_destroy_state, .enable_vblank = malidp_crtc_enable_vblank, diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 60b3f2bb153b..d971ed6eb6eb 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -73,17 +73,17 @@ * allocate a new empty object. We just need enough space to store * a malidp_plane_state instead of a drm_plane_state. */ -static void malidp_plane_reset(struct drm_plane *plane) +static struct drm_plane_state *malidp_plane_create_state(struct drm_plane *plane) { - struct malidp_plane_state *state = to_malidp_plane_state(plane->state); + struct malidp_plane_state *state; - if (state) - __drm_atomic_helper_plane_destroy_state(&state->base); - kfree(state); - plane->state = NULL; state = kzalloc_obj(*state); - if (state) - __drm_atomic_helper_plane_reset(plane, &state->base); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_plane_state_init(&state->base, plane); + + return &state->base; } static struct @@ -252,7 +252,7 @@ static bool malidp_format_mod_supported_per_plane(struct drm_plane *plane, static const struct drm_plane_funcs malidp_de_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .reset = malidp_plane_reset, + .atomic_create_state = malidp_plane_create_state, .atomic_duplicate_state = malidp_duplicate_plane_state, .atomic_destroy_state = malidp_destroy_plane_state, .atomic_print_state = malidp_plane_atomic_print_state, diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 5e006212fea0..d349beaa2990 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -817,7 +817,7 @@ static void armada_drm_crtc_disable_vblank(struct drm_crtc *crtc) } static const struct drm_crtc_funcs armada_crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .cursor_set = armada_drm_crtc_cursor_set, .cursor_move = armada_drm_crtc_cursor_move, .destroy = armada_drm_crtc_destroy, diff --git a/drivers/gpu/drm/ast/ast_dp.c b/drivers/gpu/drm/ast/ast_dp.c index 5f266a05419b..f22c0c215a21 100644 --- a/drivers/gpu/drm/ast/ast_dp.c +++ b/drivers/gpu/drm/ast/ast_dp.c @@ -88,8 +88,14 @@ static int ast_astdp_read_edid_block(void *data, u8 *buf, unsigned int block, si int ret = 0; unsigned int i; - if (block > 0) - return -EIO; /* extension headers not supported */ + if (block > 1) { + /* ASPEED DP's EDID buffer holds blocks 0 and 1 only (256 + * bytes). Report anything beyond that as an all-zero block + * rather than a read failure. + */ + memset(buf, 0, len); + return 0; + } /* * Protect access to I/O registers from concurrent modesetting @@ -154,20 +160,6 @@ static int ast_astdp_read_edid_block(void *data, u8 *buf, unsigned int block, si ediddata[2] = ast_get_index_reg(ast, AST_IO_VGACRI, 0xda); ediddata[3] = ast_get_index_reg(ast, AST_IO_VGACRI, 0xdb); - if (i == 31) { - /* - * For 128-bytes EDID_1.3, - * 1. Add the value of Bytes-126 to Bytes-127. - * The Bytes-127 is Checksum. Sum of all 128bytes should - * equal 0 (mod 256). - * 2. Modify Bytes-126 to be 0. - * The Bytes-126 indicates the Number of extensions to - * follow. 0 represents noextensions. - */ - ediddata[3] = ediddata[3] + ediddata[2]; - ediddata[2] = 0; - } - memcpy(buf, ediddata, min((len - i), 4)); buf += 4; } diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index d5ed8c5c7925..ea0ef8096aa9 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -888,17 +888,17 @@ static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = { .atomic_disable = ast_crtc_helper_atomic_disable, }; -static void ast_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *ast_crtc_create_state(struct drm_crtc *crtc) { - struct ast_crtc_state *ast_state = kzalloc_obj(*ast_state); + struct ast_crtc_state *ast_state; - if (crtc->state) - crtc->funcs->atomic_destroy_state(crtc, crtc->state); + ast_state = kzalloc_obj(*ast_state); + if (!ast_state) + return ERR_PTR(-ENOMEM); - if (ast_state) - __drm_atomic_helper_crtc_reset(crtc, &ast_state->base); - else - __drm_atomic_helper_crtc_reset(crtc, NULL); + __drm_atomic_helper_crtc_state_init(&ast_state->base, crtc); + + return &ast_state->base; } static struct drm_crtc_state * @@ -934,7 +934,7 @@ static void ast_crtc_atomic_destroy_state(struct drm_crtc *crtc, } static const struct drm_crtc_funcs ast_crtc_funcs = { - .reset = ast_crtc_reset, + .atomic_create_state = ast_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index 9dbac2def333..c2c4c9c3086b 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -529,20 +529,17 @@ void atmel_hlcdc_crtc_irq(struct drm_crtc *c) atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c)); } -static void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *atmel_hlcdc_crtc_create_state(struct drm_crtc *crtc) { struct atmel_hlcdc_crtc_state *state; - if (crtc->state) { - __drm_atomic_helper_crtc_destroy_state(crtc->state); - state = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state); - kfree(state); - crtc->state = NULL; - } - state = kzalloc_obj(*state); - if (state) - __drm_atomic_helper_crtc_reset(crtc, &state->base); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_crtc_state_init(&state->base, crtc); + + return &state->base; } static struct drm_crtc_state * @@ -599,7 +596,7 @@ static void atmel_hlcdc_crtc_disable_vblank(struct drm_crtc *c) static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = { .page_flip = drm_atomic_helper_page_flip, .set_config = drm_atomic_helper_set_config, - .reset = atmel_hlcdc_crtc_reset, + .atomic_create_state = atmel_hlcdc_crtc_create_state, .atomic_duplicate_state = atmel_hlcdc_crtc_duplicate_state, .atomic_destroy_state = atmel_hlcdc_crtc_destroy_state, .enable_vblank = atmel_hlcdc_crtc_enable_vblank, diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 4a57d49b4c6d..837db36cdf48 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -225,7 +225,6 @@ config DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW tristate "MegaChips stdp4028-ge-b850v3-fw and stdp2690-ge-b850v3-fw" depends on OF select DRM_KMS_HELPER - select DRM_PANEL help This is a driver for the display bridges of GE B850v3 that convert dual channel LVDS @@ -291,7 +290,7 @@ config DRM_PARADE_PS8640 select DRM_DISPLAY_DP_AUX_BUS select DRM_KMS_HELPER select DRM_MIPI_DSI - select DRM_PANEL + select DRM_PANEL_BRIDGE help Choose this option if you have PS8640 for display The PS8640 is a high-performance and low-power @@ -463,7 +462,7 @@ config DRM_TI_SN65DSI83 depends on OF select DRM_KMS_HELPER select REGMAP_I2C - select DRM_PANEL + select DRM_PANEL_BRIDGE select DRM_MIPI_DSI help Texas Instruments SN65DSI83 and SN65DSI84 DSI to LVDS Bridge driver @@ -476,10 +475,10 @@ config DRM_TI_SN65DSI86 select DRM_BRIDGE_CONNECTOR select DRM_KMS_HELPER select REGMAP_I2C - select DRM_PANEL select DRM_MIPI_DSI select AUXILIARY_BUS select DRM_DISPLAY_DP_AUX_BUS + select DRM_PANEL_BRIDGE help Texas Instruments SN65DSI86 DSI to eDP Bridge driver diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 30c0c0b41d5a..566f1e5eb8cd 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1414,10 +1414,8 @@ analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data) analogix_dp_hardirq, analogix_dp_irq_thread, irq_flags, "analogix-dp", dp); - if (ret) { - dev_err(&pdev->dev, "failed to request irq\n"); + if (ret) return ERR_PTR(ret); - } dp->aux.name = "DP-AUX"; dp->aux.transfer = analogix_dpaux_transfer; diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index 504a3186ebb3..f47717bd1079 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -2343,7 +2343,6 @@ static int cdns_mhdp_probe(struct platform_device *pdev) cdns_mhdp_irq_handler, IRQF_ONESHOT, "mhdp8546", mhdp); if (ret) { - dev_err(dev, "cannot install IRQ %d\n", irq); ret = -EIO; goto plat_fini; } diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index 0f6173c61502..4ec50fb63aba 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -531,7 +531,7 @@ static int chipone_dsi_attach(struct chipone *icn) dsi->hs_rate = 500000000; dsi->lp_rate = 16000000; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) dev_err(icn->dev, "failed to attach dsi\n"); @@ -763,11 +763,6 @@ static int chipone_i2c_probe(struct i2c_client *client) return chipone_dsi_host_attach(icn); } -static void chipone_dsi_remove(struct mipi_dsi_device *dsi) -{ - mipi_dsi_detach(dsi); -} - static const struct of_device_id chipone_of_match[] = { { .compatible = "chipone,icn6211", }, { /* sentinel */ } @@ -776,7 +771,6 @@ MODULE_DEVICE_TABLE(of, chipone_of_match); static struct mipi_dsi_driver chipone_dsi_driver = { .probe = chipone_dsi_probe, - .remove = chipone_dsi_remove, .driver = { .name = "chipone-icn6211", .of_match_table = chipone_of_match, diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 025a19c1e530..e136ac71edbb 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -3607,10 +3607,8 @@ static int it6505_i2c_probe(struct i2c_client *client) IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_NO_AUTOEN, "it6505-intp", it6505); - if (err) { - dev_err(dev, "Failed to request INTP threaded IRQ: %d", err); + if (err) return err; - } INIT_WORK(&it6505->link_works, it6505_link_training_work); INIT_WORK(&it6505->hdcp_wait_ksv_list, it6505_hdcp_wait_ksv_list); diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index fb34f661ee0a..3ebd8992cd94 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -1156,10 +1156,8 @@ static int lt9611_probe(struct i2c_client *client) ret = devm_request_threaded_irq(dev, client->irq, NULL, lt9611_irq_thread_handler, IRQF_ONESHOT, "lt9611", lt9611); - if (ret) { - dev_err(dev, "failed to request irq\n"); + if (ret) goto err_disable_regulators; - } i2c_set_clientdata(client, lt9611); diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c index e2fc69fc51b6..5457a7b02a33 100644 --- a/drivers/gpu/drm/bridge/samsung-dsim.c +++ b/drivers/gpu/drm/bridge/samsung-dsim.c @@ -2184,10 +2184,8 @@ int samsung_dsim_probe(struct platform_device *pdev) samsung_dsim_irq, IRQF_ONESHOT | IRQF_NO_AUTOEN, dev_name(dev), dsi); - if (ret) { - dev_err(dev, "failed to request dsi irq\n"); + if (ret) return ret; - } ret = samsung_dsim_parse_dt(dsi); if (ret) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index b452748ad9db..88f9cd9ba6db 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -2320,8 +2320,7 @@ static int sii8620_probe(struct i2c_client *client) IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "sii8620", ctx); if (ret < 0) - return dev_err_probe(dev, ret, - "failed to install IRQ handler\n"); + return ret; ctx->gpio_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(ctx->gpio_reset)) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c index 8b8529e1723c..86a20a0b9c01 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c @@ -2079,10 +2079,8 @@ struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder, ret = devm_request_threaded_irq(dev, dp->irq, NULL, dw_dp_irq, IRQF_ONESHOT, dev_name(dev), dp); - if (ret) { - dev_err_probe(dev, ret, "failed to request irq\n"); + if (ret) goto unregister_aux; - } return dp; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 34c5f583e910..937d61c42b58 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -3353,7 +3353,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, hdmi->channels = 2; hdmi->disabled = true; hdmi->rxsense = true; - hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE); + hdmi->phy_mask = ~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE) & 0xff; hdmi->mc_clkdis = 0x7f; hdmi->last_connector_result = connector_status_disconnected; diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index e3a57f8228da..ac83af7902cd 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -2530,10 +2530,8 @@ static int tc_probe(struct i2c_client *client) NULL, tc_irq_handler, IRQF_ONESHOT, "tc358767-irq", tc); - if (ret) { - dev_err(dev, "failed to register dp interrupt\n"); + if (ret) return ret; - } tc->have_irq = true; } diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index f9fdbf48c6b3..41c217074cc3 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -825,48 +825,35 @@ static int sn65dsi83_select_lvds_vod_swing(struct device *dev, static int sn65dsi83_parse_lvds_endpoint(struct sn65dsi83 *ctx, int channel) { struct device *dev = ctx->dev; - struct device_node *endpoint; - int endpoint_reg; + int endpoint_reg = (channel == CHANNEL_A) ? 2 : 3; + struct device_node *endpoint __free(device_node) = + of_graph_get_endpoint_by_regs(dev->of_node, endpoint_reg, -1); /* Set so the property can be freely selected if not defined */ u32 lvds_vod_swing_data[2] = { 0, 1000000 }; u32 lvds_vod_swing_clk[2] = { 0, 1000000 }; /* Set default near end terminataion to 200 Ohm */ u32 lvds_term = 200; int lvds_vod_swing_conf; - int ret = 0; int ret_data; int ret_clock; - if (channel == CHANNEL_A) - endpoint_reg = 2; - else - endpoint_reg = 3; - - endpoint = of_graph_get_endpoint_by_regs(dev->of_node, endpoint_reg, -1); - of_property_read_u32(endpoint, "ti,lvds-termination-ohms", &lvds_term); if (lvds_term == 100) ctx->lvds_term_conf[channel] = OHM_100; else if (lvds_term == 200) ctx->lvds_term_conf[channel] = OHM_200; - else { - ret = -EINVAL; - goto exit; - } + else + return -EINVAL; ret_data = of_property_read_u32_array(endpoint, "ti,lvds-vod-swing-data-microvolt", lvds_vod_swing_data, ARRAY_SIZE(lvds_vod_swing_data)); - if (ret_data != 0 && ret_data != -EINVAL) { - ret = ret_data; - goto exit; - } + if (ret_data != 0 && ret_data != -EINVAL) + return ret_data; ret_clock = of_property_read_u32_array(endpoint, "ti,lvds-vod-swing-clock-microvolt", lvds_vod_swing_clk, ARRAY_SIZE(lvds_vod_swing_clk)); - if (ret_clock != 0 && ret_clock != -EINVAL) { - ret = ret_clock; - goto exit; - } + if (ret_clock != 0 && ret_clock != -EINVAL) + return ret_clock; /* Use default value if both properties are NOT defined. */ if (ret_data == -EINVAL && ret_clock == -EINVAL) @@ -876,17 +863,13 @@ static int sn65dsi83_parse_lvds_endpoint(struct sn65dsi83 *ctx, int channel) if (!ret_data || !ret_clock) { lvds_vod_swing_conf = sn65dsi83_select_lvds_vod_swing(dev, lvds_vod_swing_data, lvds_vod_swing_clk, ctx->lvds_term_conf[channel]); - if (lvds_vod_swing_conf < 0) { - ret = lvds_vod_swing_conf; - goto exit; - } + if (lvds_vod_swing_conf < 0) + return lvds_vod_swing_conf; } ctx->lvds_vod_swing_conf[channel] = lvds_vod_swing_conf; - ret = 0; -exit: - of_node_put(endpoint); - return ret; + + return 0; } static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model) @@ -1040,7 +1023,7 @@ static int sn65dsi83_probe(struct i2c_client *client) ret = devm_request_threaded_irq(ctx->dev, ctx->irq, NULL, sn65dsi83_irq, IRQF_ONESHOT, dev_name(ctx->dev), ctx); if (ret) - return dev_err_probe(dev, ret, "failed to request irq\n"); + return ret; } dev_set_drvdata(dev, ctx); diff --git a/drivers/gpu/drm/clients/drm_log.c b/drivers/gpu/drm/clients/drm_log.c index 99062609d9a6..4654091fe11a 100644 --- a/drivers/gpu/drm/clients/drm_log.c +++ b/drivers/gpu/drm/clients/drm_log.c @@ -89,25 +89,21 @@ static void drm_log_blit(struct iosys_map *dst, unsigned int dst_pitch, } } -static void drm_log_clear_line(struct drm_log_scanout *scanout, u32 line) +static void drm_log_clear_line(struct drm_log_scanout *scanout, u32 line, + struct iosys_map map) { struct drm_framebuffer *fb = scanout->buffer->fb; unsigned long height = scanout->scaled_font_h; - struct iosys_map map; struct drm_rect r = DRM_RECT_INIT(0, line * height, fb->width, height); - if (drm_client_buffer_vmap_local(scanout->buffer, &map)) - return; iosys_map_memset(&map, r.y1 * fb->pitches[0], 0, height * fb->pitches[0]); - drm_client_buffer_vunmap_local(scanout->buffer); - drm_client_buffer_flush(scanout->buffer, &r); } static void drm_log_draw_line(struct drm_log_scanout *scanout, const char *s, - unsigned int len, unsigned int prefix_len) + unsigned int len, unsigned int prefix_len, + struct iosys_map map) { struct drm_framebuffer *fb = scanout->buffer->fb; - struct iosys_map map; const struct font_desc *font = scanout->font; size_t font_pitch = DIV_ROUND_UP(font->width, 8); const u8 *src; @@ -116,10 +112,9 @@ static void drm_log_draw_line(struct drm_log_scanout *scanout, const char *s, fb->width, (scanout->line + 1) * scanout->scaled_font_h); u32 i; - if (drm_client_buffer_vmap_local(scanout->buffer, &map)) - return; iosys_map_incr(&map, r.y1 * fb->pitches[0]); + for (i = 0; i < len && i < scanout->columns; i++) { u32 color = (i < prefix_len) ? scanout->prefix_color : scanout->front_color; src = font_data_glyph_buf(font->data, font->width, font->height, @@ -134,21 +129,40 @@ static void drm_log_draw_line(struct drm_log_scanout *scanout, const char *s, scanout->line++; if (scanout->line >= scanout->rows) scanout->line = 0; - drm_client_buffer_vunmap_local(scanout->buffer); - drm_client_buffer_flush(scanout->buffer, &r); } static void drm_log_draw_new_line(struct drm_log_scanout *scanout, - const char *s, unsigned int len, unsigned int prefix_len) + const char *s, unsigned int len, + unsigned int prefix_len) { + struct iosys_map map; + struct drm_framebuffer *fb = scanout->buffer->fb; + u32 height = scanout->scaled_font_h; + u32 line = scanout->line; + u32 y2; + struct drm_rect dirty; + + if (drm_client_buffer_vmap_local(scanout->buffer, &map)) + return; + if (scanout->line == 0) { - drm_log_clear_line(scanout, 0); - drm_log_clear_line(scanout, 1); - drm_log_clear_line(scanout, 2); - } else if (scanout->line + 2 < scanout->rows) - drm_log_clear_line(scanout, scanout->line + 2); + drm_log_clear_line(scanout, 0, map); + drm_log_clear_line(scanout, 1, map); + drm_log_clear_line(scanout, 2, map); + y2 = min(3, scanout->rows) * height; + } else if (scanout->line + 2 < scanout->rows) { + drm_log_clear_line(scanout, scanout->line + 2, map); + y2 = (line + 3) * height; + } else { + y2 = (line + 1) * height; + } + + drm_log_draw_line(scanout, s, len, prefix_len, map); + + drm_client_buffer_vunmap_local(scanout->buffer); - drm_log_draw_line(scanout, s, len, prefix_len); + dirty = DRM_RECT_INIT(0, line * height, fb->width, y2 - line * height); + drm_client_buffer_flush(scanout->buffer, &dirty); } /* diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index 8b54069fa53a..00feb73e63d7 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -263,6 +263,9 @@ static void drm_bridge_connector_debugfs_init(struct drm_connector *connector, if (bridge->funcs->debugfs_init) bridge->funcs->debugfs_init(bridge, root); } + + if (bridge_connector->bridge_hdmi) + drm_hdmi_connector_debugfs_init(connector, root); } static struct drm_connector_state * diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c index d6669e36ca1b..bccc5d7c8eed 100644 --- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT +#include <linux/debugfs.h> #include <linux/export.h> #include <drm/drm_atomic.h> @@ -12,6 +13,7 @@ #include <drm/display/drm_hdmi_cec_helper.h> #include <drm/display/drm_hdmi_helper.h> #include <drm/display/drm_hdmi_state_helper.h> +#include <drm/display/drm_scdc_helper.h> /** * DOC: hdmi helpers @@ -965,6 +967,160 @@ drm_hdmi_connector_mode_valid(struct drm_connector *connector, } EXPORT_SYMBOL(drm_hdmi_connector_mode_valid); +static ssize_t +audio_infoframe_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos) +{ + struct drm_connector_hdmi_infoframe *infoframe; + struct drm_connector *connector; + union hdmi_infoframe *frame; + u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)]; + ssize_t len = 0; + + connector = filp->private_data; + mutex_lock(&connector->hdmi.infoframes.lock); + + infoframe = &connector->hdmi.infoframes.audio; + if (!infoframe->set) + goto out; + + frame = &infoframe->data; + len = hdmi_infoframe_pack(frame, buf, sizeof(buf)); + if (len < 0) + goto out; + + len = simple_read_from_buffer(ubuf, count, ppos, buf, len); + +out: + mutex_unlock(&connector->hdmi.infoframes.lock); + return len; +} + +static const struct file_operations audio_infoframe_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = audio_infoframe_read, +}; + +static int create_hdmi_audio_infoframe_file(struct drm_connector *connector, + struct dentry *parent) +{ + struct dentry *file; + + if (!connector->hdmi.funcs || + !connector->hdmi.funcs->audio.write_infoframe) + return 0; + + file = debugfs_create_file("audio", 0400, parent, connector, &audio_infoframe_fops); + if (IS_ERR(file)) + return PTR_ERR(file); + + return 0; +} + +#define DEFINE_INFOFRAME_FILE(_f) \ +static ssize_t _f##_read_infoframe(struct file *filp, \ + char __user *ubuf, \ + size_t count, \ + loff_t *ppos) \ +{ \ + struct drm_connector_hdmi_infoframe *infoframe; \ + struct drm_connector_state *conn_state; \ + struct drm_connector *connector; \ + union hdmi_infoframe *frame; \ + struct drm_device *dev; \ + u8 buf[HDMI_INFOFRAME_SIZE(MAX)]; \ + ssize_t len = 0; \ + \ + connector = filp->private_data; \ + dev = connector->dev; \ + \ + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); \ + \ + conn_state = connector->state; \ + infoframe = &conn_state->hdmi.infoframes._f; \ + if (!infoframe->set) \ + goto out; \ + \ + frame = &infoframe->data; \ + len = hdmi_infoframe_pack(frame, buf, sizeof(buf)); \ + if (len < 0) \ + goto out; \ + \ + len = simple_read_from_buffer(ubuf, count, ppos, buf, len); \ + \ +out: \ + drm_modeset_unlock(&dev->mode_config.connection_mutex); \ + return len; \ +} \ +\ +static const struct file_operations _f##_infoframe_fops = { \ + .owner = THIS_MODULE, \ + .open = simple_open, \ + .read = _f##_read_infoframe, \ +}; \ +\ +static int create_hdmi_## _f ## _infoframe_file(struct drm_connector *connector, \ + struct dentry *parent) \ +{ \ + struct dentry *file; \ + \ + if (!connector->hdmi.funcs || \ + !connector->hdmi.funcs->_f.write_infoframe) \ + return 0; \ + file = debugfs_create_file(#_f, 0400, parent, connector, &_f ## _infoframe_fops); \ + if (IS_ERR(file)) \ + return PTR_ERR(file); \ + \ + return 0; \ +} + +DEFINE_INFOFRAME_FILE(avi); +DEFINE_INFOFRAME_FILE(hdmi); +DEFINE_INFOFRAME_FILE(hdr_drm); +DEFINE_INFOFRAME_FILE(spd); + +static int create_hdmi_infoframe_files(struct drm_connector *connector, + struct dentry *parent) +{ + int ret; + + ret = create_hdmi_audio_infoframe_file(connector, parent); + if (ret) + return ret; + + ret = create_hdmi_avi_infoframe_file(connector, parent); + if (ret) + return ret; + + ret = create_hdmi_hdmi_infoframe_file(connector, parent); + if (ret) + return ret; + + ret = create_hdmi_hdr_drm_infoframe_file(connector, parent); + if (ret) + return ret; + + ret = create_hdmi_spd_infoframe_file(connector, parent); + if (ret) + return ret; + + return 0; +} + +void drm_hdmi_connector_debugfs_init(struct drm_connector *connector, + struct dentry *root) +{ + struct dentry *dir; + + dir = debugfs_create_dir("infoframes", root); + if (IS_ERR(dir)) + return; + + create_hdmi_infoframe_files(connector, dir); + drm_scdc_debugfs_init(connector, root); +} +EXPORT_SYMBOL(drm_hdmi_connector_debugfs_init); + static int clear_infoframe(struct drm_connector *connector, const struct drm_connector_infoframe_funcs *funcs, const char *type) diff --git a/drivers/gpu/drm/display/drm_scdc_helper.c b/drivers/gpu/drm/display/drm_scdc_helper.c index df878aad4a36..d00da20862fc 100644 --- a/drivers/gpu/drm/display/drm_scdc_helper.c +++ b/drivers/gpu/drm/display/drm_scdc_helper.c @@ -21,10 +21,13 @@ * DEALINGS IN THE SOFTWARE. */ +#include <linux/bitfield.h> #include <linux/export.h> #include <linux/i2c.h> #include <linux/slab.h> +#include <linux/debugfs.h> #include <linux/delay.h> +#include <linux/overflow.h> #include <drm/display/drm_scdc_helper.h> #include <drm/drm_connector.h> @@ -55,6 +58,38 @@ #define SCDC_I2C_SLAVE_ADDRESS 0x54 +static const char *drm_scdc_frl_rate_str(enum drm_scdc_frl_rate rate) +{ + switch (rate) { + case SCDC_FRL_RATE_OFF: + return "Off"; + case SCDC_FRL_RATE_3X3: + return "3 Gbit/s x 3 lanes"; + case SCDC_FRL_RATE_6X3: + return "6 Gbit/s x 3 lanes"; + case SCDC_FRL_RATE_6X4: + return "6 Gbit/s x 4 lanes"; + case SCDC_FRL_RATE_8X4: + return "8 Gbit/s x 4 lanes"; + case SCDC_FRL_RATE_10X4: + return "10 Gbit/s x 4 lanes"; + case SCDC_FRL_RATE_12X4: + return "12 Gbit/s x 4 lanes"; + case SCDC_FRL_RATE_RESV_7: + case SCDC_FRL_RATE_RESV_8: + case SCDC_FRL_RATE_RESV_9: + case SCDC_FRL_RATE_RESV_10: + case SCDC_FRL_RATE_RESV_11: + case SCDC_FRL_RATE_RESV_12: + case SCDC_FRL_RATE_RESV_13: + case SCDC_FRL_RATE_RESV_14: + case SCDC_FRL_RATE_RESV_15: + return "(Reserved)"; + default: + return NULL; + } +} + /** * drm_scdc_read - read a block of data from SCDC * @adapter: I2C controller @@ -67,8 +102,8 @@ * Returns: * 0 on success, negative error code on failure. */ -ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer, - size_t size) +int drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer, + size_t size) { int ret; struct i2c_msg msgs[2] = { @@ -107,8 +142,8 @@ EXPORT_SYMBOL(drm_scdc_read); * Returns: * 0 on success, negative error code on failure. */ -ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset, - const void *buffer, size_t size) +int drm_scdc_write(struct i2c_adapter *adapter, u8 offset, const void *buffer, + size_t size) { struct i2c_msg msg = { .addr = SCDC_I2C_SLAVE_ADDRESS, @@ -276,3 +311,249 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector, return true; } EXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio); + +static void +drm_scdc_parse_status0_flags(u8 val, struct drm_scdc_status_flags *flags) +{ + flags->clock_detected = val & SCDC_CLOCK_DETECT; + flags->ch0_locked = val & SCDC_CH0_LOCK; + flags->ch1_locked = val & SCDC_CH1_LOCK; + flags->ch2_locked = val & SCDC_CH2_LOCK; + flags->ln3_locked = val & SCDC_LN3_LOCK; + flags->flt_ready = val & SCDC_FLT_READY; + flags->dsc_fail = val & SCDC_DSC_FAIL; +} + +static void +drm_scdc_parse_status1_2_flags(u8 val_flag1, u8 val_flag2, + struct drm_scdc_status_flags *flags) +{ + flags->ln0_training_pattern = FIELD_GET(SCDC_LN_EVEN_TRAIN_PTRN, val_flag1); + flags->ln1_training_pattern = FIELD_GET(SCDC_LN_ODD_TRAIN_PTRN, val_flag1); + + flags->ln2_training_pattern = FIELD_GET(SCDC_LN_EVEN_TRAIN_PTRN, val_flag2); + flags->ln3_training_pattern = FIELD_GET(SCDC_LN_ODD_TRAIN_PTRN, val_flag2); +} + +static int drm_scdc_parse_error_counters(const u8 scdc[256], u16 counter[4], + unsigned int num_lanes) +{ + u8 end_reg; + u8 sum = 0; + int i; + + switch (num_lanes) { + case 3: + end_reg = SCDC_ERR_DET_CHECKSUM; + break; + case 4: + end_reg = SCDC_ERR_DET_3_H; + break; + default: + return -EINVAL; + } + + for (i = SCDC_ERR_DET_0_L; i <= end_reg; i++) + sum = wrapping_add(u8, sum, scdc[i]); + + if (sum) + return -EPROTO; + + for (i = 0; i < 3; i++) { + if (scdc[SCDC_ERR_DET_0_H + i * 2] & SCDC_CHANNEL_VALID) + counter[i] = (scdc[SCDC_ERR_DET_0_H + i * 2] & + ~SCDC_CHANNEL_VALID) << 8 | + scdc[SCDC_ERR_DET_0_L + i * 2]; + else + counter[i] = 0; + } + + if (num_lanes == 4 && scdc[SCDC_ERR_DET_3_H] & SCDC_CHANNEL_VALID) + counter[3] = (scdc[SCDC_ERR_DET_3_H] & ~SCDC_CHANNEL_VALID) << 8 | + scdc[SCDC_ERR_DET_3_L]; + else + counter[3] = 0; + + return 0; +} + +/** + * drm_scdc_read_state - Update state from SCDC + * @connector: pointer to a &struct drm_connector on which to operate on + * @state: pointer to a &struct drm_scdc_state to fill + * + * Reads the entire 256 byte SCDC state and parses it. + * + * Returns: %0 on success, negative errno on failure. + */ +int drm_scdc_read_state(struct drm_connector *connector, struct drm_scdc_state *state) +{ + struct i2c_adapter *ddc; + struct drm_scdc *scdc; + u8 *buf = state->scdc; + int num_lanes; + int ret; + + if (!state || !connector) + return -ENODEV; + + scdc = &connector->display_info.hdmi.scdc; + ddc = connector->ddc; + + if (!scdc->supported || !ddc) + return -EOPNOTSUPP; + + /* Read in 128-byte chunks, to work around DP<->HDMI converters with issues. */ + ret = drm_scdc_read(ddc, 0, buf, 128); + if (ret) + return ret; + + ret = drm_scdc_read(ddc, 128, &buf[128], 128); + if (ret) + return ret; + + state->scrambling_enabled = buf[SCDC_TMDS_CONFIG] & SCDC_SCRAMBLING_ENABLE; + state->tmds_bclk_x40 = buf[SCDC_TMDS_CONFIG] & SCDC_TMDS_BIT_CLOCK_RATIO_BY_40; + + state->scrambling_detected = buf[SCDC_SCRAMBLER_STATUS] & SCDC_SCRAMBLING_STATUS; + + state->rate = FIELD_GET(SCDC_FRL_RATE, buf[SCDC_CONFIG_1]); + num_lanes = drm_scdc_num_frl_lanes(state->rate); + if (num_lanes < 0) + return num_lanes; + if (!num_lanes) + num_lanes = 3; + + state->ffe_levels = FIELD_GET(SCDC_FFE_LEVELS, buf[SCDC_CONFIG_1]); + + drm_scdc_parse_status0_flags(buf[SCDC_STATUS_FLAGS_0], &state->stf); + drm_scdc_parse_status1_2_flags(buf[SCDC_STATUS_FLAGS_1], + buf[SCDC_STATUS_FLAGS_2], &state->stf); + ret = drm_scdc_parse_error_counters(buf, state->error_count, num_lanes); + if (ret) + return ret; + + if (state->rate && (buf[SCDC_ERR_DET_RS_H] & SCDC_CHANNEL_VALID)) + state->rs_corrections = (buf[SCDC_ERR_DET_RS_H] & ~SCDC_CHANNEL_VALID) << 8 | + buf[SCDC_ERR_DET_RS_L]; + + return 0; +} +EXPORT_SYMBOL(drm_scdc_read_state); + +#define scdc_print_str(_f, key, s) \ + (seq_printf((_f), "%-30s: %s\n", (key), (s))) +#define scdc_print_flag(_f, key, val) \ + (scdc_print_str((_f), (key), str_yes_no((val)))) +#define scdc_print_dec(_f, key, val) \ + (seq_printf((_f), "%-30s: %d\n", (key), (val))) + +static int scdc_status_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + struct drm_scdc *scdc = &connector->display_info.hdmi.scdc; + struct drm_scdc_state *st; + int i, ret, frl_lanes; + + drm_connector_get(connector); + + ret = mutex_lock_interruptible(&connector->dev->mode_config.mutex); + if (ret) + goto err_conn_put; + + if (connector->status != connector_status_connected) { + ret = -ENODEV; + goto err_unlock; + } + + if (scdc->supported) { + st = kzalloc_obj(*st); + if (!st) { + ret = -ENOMEM; + goto err_unlock; + } + ret = drm_scdc_read_state(connector, st); + if (ret) + goto err_free_state; + + for (i = 0; i < ARRAY_SIZE(st->scdc); i += 16) + seq_printf(m, "%*ph\n", 16, &st->scdc[i]); + + seq_puts(m, "\n----------------\n\n"); + } + + scdc_print_flag(m, "SCDC Supported", scdc->supported); + if (!scdc->supported) { + ret = 0; + goto err_unlock; + } + + scdc_print_flag(m, "Sink Read Request Capable", scdc->read_request); + scdc_print_flag(m, "Scrambling Supported", scdc->scrambling.supported); + scdc_print_flag(m, "Low Rate Scrambling Supported", scdc->scrambling.low_rates); + + mutex_unlock(&connector->dev->mode_config.mutex); + + drm_connector_put(connector); + + frl_lanes = drm_scdc_num_frl_lanes(st->rate); + + scdc_print_flag(m, "Scrambling Enabled", st->scrambling_enabled); + scdc_print_flag(m, "Scrambling Detected", st->scrambling_detected); + scdc_print_str(m, "FRL Rate", drm_scdc_frl_rate_str(st->rate)); + scdc_print_dec(m, "FFE Levels", st->ffe_levels); + + if (st->tmds_bclk_x40) + scdc_print_str(m, "TMDS Bit Clock Ratio", "1/40"); + else + scdc_print_str(m, "TMDS Bit Clock Ratio", "1/10"); + + scdc_print_flag(m, "Clock Detected", st->stf.clock_detected); + scdc_print_flag(m, "Channel 0 Locked", st->stf.ch0_locked); + scdc_print_flag(m, "Channel 1 Locked", st->stf.ch1_locked); + scdc_print_flag(m, "Channel 2 Locked", st->stf.ch2_locked); + if (frl_lanes == 4) + scdc_print_flag(m, "Lane 3 Locked", st->stf.ln3_locked); + + scdc_print_flag(m, "Sink Ready For Link Training", st->stf.flt_ready); + scdc_print_flag(m, "Sink Failed To Decode DSC", st->stf.dsc_fail); + + scdc_print_dec(m, "Channel 0 Errors", st->error_count[0]); + scdc_print_dec(m, "Channel 1 Errors", st->error_count[1]); + scdc_print_dec(m, "Channel 2 Errors", st->error_count[2]); + if (frl_lanes == 4) + scdc_print_dec(m, "Lane 3 Errors", st->error_count[3]); + if (frl_lanes > 0) + scdc_print_dec(m, "Reed-Solomon Corrections", st->rs_corrections); + + kfree(st); + + return 0; + +err_free_state: + kfree(st); +err_unlock: + mutex_unlock(&connector->dev->mode_config.mutex); +err_conn_put: + drm_connector_put(connector); + + return ret; +} +DEFINE_SHOW_ATTRIBUTE(scdc_status); + +/** + * drm_scdc_debugfs_init - Initialize scdc files in connector debugfs + * @connector: pointer to &struct drm_connector to operate on + * @root: debugfs &struct dentry for the debugfs root of @connector + * + * Creates SCDC-related debugfs files for @connector. Must be called after + * @root is already created. + */ +void drm_scdc_debugfs_init(struct drm_connector *connector, struct dentry *root) +{ + if (!root || !connector) + return; + + debugfs_create_file("scdc_status", 0444, root, connector, &scdc_status_fops); +} +EXPORT_SYMBOL(drm_scdc_debugfs_init); diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index e5c8ef06caed..45df6376f408 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -925,6 +925,10 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, drm_get_colorop_lut3d_interpolation_name(state->lut3d_interpolation)); drm_printf_indent(p, 1, "data blob id=%d\n", state->data ? state->data->base.id : 0); break; + case DRM_COLOROP_FIXED_MATRIX: + drm_printf_indent(p, 1, "fixed_matrix_type=%s\n", + drm_get_colorop_fixed_matrix_type_name(state->fixed_matrix_type)); + break; default: break; } diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 5ea593b3a98e..4acfb588c412 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -786,6 +786,11 @@ static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, return drm_atomic_color_set_data_property(colorop, state, property, val, replaced); + } else if (property == colorop->fixed_matrix_type_property) { + if (state->fixed_matrix_type != val) { + state->fixed_matrix_type = val; + *replaced = true; + } } else { drm_dbg_atomic(colorop->dev, "[COLOROP:%d:%d] unknown property [PROP:%d:%s]\n", @@ -818,6 +823,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, *val = state->lut3d_interpolation; else if (property == colorop->data_property) *val = (state->data) ? state->data->base.id : 0; + else if (property == colorop->fixed_matrix_type_property) + *val = state->fixed_matrix_type; else return -EINVAL; diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c index 1536e59c6fe7..02e0460baa7c 100644 --- a/drivers/gpu/drm/drm_buddy.c +++ b/drivers/gpu/drm/drm_buddy.c @@ -44,7 +44,8 @@ void drm_buddy_print(struct gpu_buddy *mm, struct drm_printer *p) gpu_buddy_driver_lock_held(mm); drm_printf(p, "chunk_size: %lluKiB, total: %lluMiB, free: %lluMiB, clear_free: %lluMiB\n", - mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20); + mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, + gpu_buddy_clear_avail(mm) >> 20); for (order = mm->max_order; order >= 0; order--) { u64 free_count = mm->free_scoreboard[order]; diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 682fcc651525..95da03d6c65c 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -68,6 +68,7 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { { DRM_COLOROP_CTM_3X4, "3x4 Matrix"}, { DRM_COLOROP_MULTIPLIER, "Multiplier"}, { DRM_COLOROP_3D_LUT, "3D LUT"}, + { DRM_COLOROP_FIXED_MATRIX, "Fixed Matrix"}, }; static const char * const colorop_curve_1d_type_names[] = { @@ -90,6 +91,15 @@ static const struct drm_prop_enum_list drm_colorop_lut3d_interpolation_list[] = { DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL, "Tetrahedral" }, }; +static const char * const colorop_fixed_matrix_type_names[] = { + [DRM_COLOROP_FM_YCBCR601_FULL_RGB] = "YCbCr 601 Full to RGB", + [DRM_COLOROP_FM_YCBCR601_LIMITED_RGB] = "YCbCr 601 Limited to RGB", + [DRM_COLOROP_FM_YCBCR709_FULL_RGB] = "YCbCr 709 Full to RGB", + [DRM_COLOROP_FM_YCBCR709_LIMITED_RGB] = "YCbCr 709 Limited to RGB", + [DRM_COLOROP_FM_YCBCR2020_NC_FULL_RGB] = "YCbCr 2020 NC Full to RGB", + [DRM_COLOROP_FM_YCBCR2020_NC_LIMITED_RGB] = "YCbCr 2020 NC Limited to RGB", +}; + /* Init Helpers */ static int drm_plane_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, @@ -453,6 +463,80 @@ int drm_plane_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *col } EXPORT_SYMBOL(drm_plane_colorop_3dlut_init); +/** + * drm_plane_colorop_fixed_matrix_init - Initialize a DRM_COLOROP_FIXED_MATRIX + * + * @dev: DRM device + * @colorop: The drm_colorop object to initialize + * @plane: The associated drm_plane + * @funcs: control functions for the new colorop + * @supported_fm: A bitfield of supported drm_colorop_fixed_matrix_type enum values, + * created using BIT(fixed_matrix_type) and combined with the OR '|' + * operator. + * @flags: bitmask of misc, see DRM_COLOROP_FLAG_* defines. + * @return zero on success, -E value on failure + */ +int drm_plane_colorop_fixed_matrix_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, + const struct drm_colorop_funcs *funcs, + u64 supported_fm, uint32_t flags) +{ + struct drm_prop_enum_list enum_list[DRM_COLOROP_FM_COUNT]; + int i, len; + struct drm_property *prop; + int ret; + + if (!supported_fm) { + drm_err(dev, + "No supported FM type op for new Fixed Matrix colorop on [PLANE:%d:%s]\n", + plane->base.id, plane->name); + return -EINVAL; + } + + if ((supported_fm & -BIT(DRM_COLOROP_FM_COUNT)) != 0) { + drm_err(dev, "Unknown Fixed Matrix provided on [PLANE:%d:%s]\n", + plane->base.id, plane->name); + return -EINVAL; + } + + ret = drm_plane_colorop_init(dev, colorop, plane, funcs, DRM_COLOROP_FIXED_MATRIX, flags); + if (ret) + return ret; + + len = 0; + for (i = 0; i < DRM_COLOROP_FM_COUNT; i++) { + if ((supported_fm & BIT(i)) == 0) + continue; + + enum_list[len].type = i; + enum_list[len].name = colorop_fixed_matrix_type_names[i]; + len++; + } + + if (WARN_ON(len <= 0)) + return -EINVAL; + + prop = drm_property_create_enum(dev, DRM_MODE_PROP_ATOMIC, "FIXED_MATRIX_TYPE", + enum_list, len); + + if (!prop) + return -ENOMEM; + + colorop->fixed_matrix_type_property = prop; + /* + * Default to the first supported CSC mode as provided by the driver. + * Intuitively this should be something that keeps the colorop in pixel bypass + * mode but that is already handled via the standard colorop bypass + * property. + */ + drm_object_attach_property(&colorop->base, colorop->fixed_matrix_type_property, + enum_list[0].type); + drm_colorop_reset(colorop); + + return 0; +} +EXPORT_SYMBOL(drm_plane_colorop_fixed_matrix_init); + static void __drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop, struct drm_colorop_state *state) { @@ -533,6 +617,13 @@ static void __drm_colorop_state_init(struct drm_colorop_state *colorop_state, &val)) colorop_state->lut3d_interpolation = val; } + + if (colorop->fixed_matrix_type_property) { + if (!drm_object_property_get_default_value(&colorop->base, + colorop->fixed_matrix_type_property, + &val)) + colorop_state->fixed_matrix_type = val; + } } /** @@ -596,6 +687,7 @@ static const char * const colorop_type_name[] = { [DRM_COLOROP_CTM_3X4] = "3x4 Matrix", [DRM_COLOROP_MULTIPLIER] = "Multiplier", [DRM_COLOROP_3D_LUT] = "3D LUT", + [DRM_COLOROP_FIXED_MATRIX] = "Fixed Matrix", }; static const char * const colorop_lu3d_interpolation_name[] = { @@ -653,6 +745,21 @@ const char *drm_get_colorop_lut3d_interpolation_name(enum drm_colorop_lut3d_inte } /** + * drm_get_colorop_fixed_matrix_type_name: return a string for fixed matrix type + * @type: fixed matrix type to compute name of + * + * In contrast to the other drm_get_*_name functions this one here returns a + * const pointer and hence is threadsafe. + */ +const char *drm_get_colorop_fixed_matrix_type_name(enum drm_colorop_fixed_matrix_type type) +{ + if (WARN_ON(type >= ARRAY_SIZE(colorop_fixed_matrix_type_names))) + return "unknown"; + + return colorop_fixed_matrix_type_names[type]; +} + +/** * drm_colorop_set_next_property - sets the next pointer * @colorop: drm colorop * @next: next colorop diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index ae1c6126c2c5..a72f9723d3a9 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -633,161 +633,6 @@ static const struct file_operations drm_connector_fops = { .write = connector_write }; -static ssize_t -audio_infoframe_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos) -{ - struct drm_connector_hdmi_infoframe *infoframe; - struct drm_connector *connector; - union hdmi_infoframe *frame; - u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)]; - ssize_t len = 0; - - connector = filp->private_data; - mutex_lock(&connector->hdmi.infoframes.lock); - - infoframe = &connector->hdmi.infoframes.audio; - if (!infoframe->set) - goto out; - - frame = &infoframe->data; - len = hdmi_infoframe_pack(frame, buf, sizeof(buf)); - if (len < 0) - goto out; - - len = simple_read_from_buffer(ubuf, count, ppos, buf, len); - -out: - mutex_unlock(&connector->hdmi.infoframes.lock); - return len; -} - -static const struct file_operations audio_infoframe_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = audio_infoframe_read, -}; - -static int create_hdmi_audio_infoframe_file(struct drm_connector *connector, - struct dentry *parent) -{ - struct dentry *file; - - if (!connector->hdmi.funcs || - !connector->hdmi.funcs->audio.write_infoframe) - return 0; - - file = debugfs_create_file("audio", 0400, parent, connector, &audio_infoframe_fops); - if (IS_ERR(file)) - return PTR_ERR(file); - - return 0; -} - -#define DEFINE_INFOFRAME_FILE(_f) \ -static ssize_t _f##_read_infoframe(struct file *filp, \ - char __user *ubuf, \ - size_t count, \ - loff_t *ppos) \ -{ \ - struct drm_connector_hdmi_infoframe *infoframe; \ - struct drm_connector_state *conn_state; \ - struct drm_connector *connector; \ - union hdmi_infoframe *frame; \ - struct drm_device *dev; \ - u8 buf[HDMI_INFOFRAME_SIZE(MAX)]; \ - ssize_t len = 0; \ - \ - connector = filp->private_data; \ - dev = connector->dev; \ - \ - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); \ - \ - conn_state = connector->state; \ - infoframe = &conn_state->hdmi.infoframes._f; \ - if (!infoframe->set) \ - goto out; \ - \ - frame = &infoframe->data; \ - len = hdmi_infoframe_pack(frame, buf, sizeof(buf)); \ - if (len < 0) \ - goto out; \ - \ - len = simple_read_from_buffer(ubuf, count, ppos, buf, len); \ - \ -out: \ - drm_modeset_unlock(&dev->mode_config.connection_mutex); \ - return len; \ -} \ -\ -static const struct file_operations _f##_infoframe_fops = { \ - .owner = THIS_MODULE, \ - .open = simple_open, \ - .read = _f##_read_infoframe, \ -}; \ -\ -static int create_hdmi_## _f ## _infoframe_file(struct drm_connector *connector, \ - struct dentry *parent) \ -{ \ - struct dentry *file; \ - \ - if (!connector->hdmi.funcs || \ - !connector->hdmi.funcs->_f.write_infoframe) \ - return 0; \ - file = debugfs_create_file(#_f, 0400, parent, connector, &_f ## _infoframe_fops); \ - if (IS_ERR(file)) \ - return PTR_ERR(file); \ - \ - return 0; \ -} - -DEFINE_INFOFRAME_FILE(avi); -DEFINE_INFOFRAME_FILE(hdmi); -DEFINE_INFOFRAME_FILE(hdr_drm); -DEFINE_INFOFRAME_FILE(spd); - -static int create_hdmi_infoframe_files(struct drm_connector *connector, - struct dentry *parent) -{ - int ret; - - ret = create_hdmi_audio_infoframe_file(connector, parent); - if (ret) - return ret; - - ret = create_hdmi_avi_infoframe_file(connector, parent); - if (ret) - return ret; - - ret = create_hdmi_hdmi_infoframe_file(connector, parent); - if (ret) - return ret; - - ret = create_hdmi_hdr_drm_infoframe_file(connector, parent); - if (ret) - return ret; - - ret = create_hdmi_spd_infoframe_file(connector, parent); - if (ret) - return ret; - - return 0; -} - -static void hdmi_debugfs_add(struct drm_connector *connector) -{ - struct dentry *dir; - - if (!(connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || - connector->connector_type == DRM_MODE_CONNECTOR_HDMIB)) - return; - - dir = debugfs_create_dir("infoframes", connector->debugfs_entry); - if (IS_ERR(dir)) - return; - - create_hdmi_infoframe_files(connector, dir); -} - void drm_debugfs_connector_add(struct drm_connector *connector) { struct drm_device *dev = connector->dev; @@ -815,8 +660,6 @@ void drm_debugfs_connector_add(struct drm_connector *connector) debugfs_create_file("output_bpc", 0444, root, connector, &output_bpc_fops); - hdmi_debugfs_add(connector); - if (connector->funcs->debugfs_init) connector->funcs->debugfs_init(connector, root); } diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index cb53baa70995..8c0879c336e5 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -36,6 +36,7 @@ #include <linux/mount.h> #include <linux/pseudo_fs.h> #include <linux/sched.h> +#include <linux/seq_buf.h> #include <linux/slab.h> #include <linux/sprintf.h> #include <linux/srcu.h> @@ -576,27 +577,31 @@ static const char *drm_get_wedge_recovery(unsigned int opt) int drm_dev_wedged_event(struct drm_device *dev, unsigned long method, struct drm_wedge_task_info *info) { - char event_string[WEDGE_STR_LEN], pid_string[PID_STR_LEN], comm_string[COMM_STR_LEN]; - char *envp[] = { event_string, NULL, NULL, NULL }; - const char *recovery = NULL; - unsigned int len, opt; + DECLARE_SEQ_BUF(event_string, WEDGE_STR_LEN); + char pid_string[PID_STR_LEN], comm_string[COMM_STR_LEN]; + char *envp[4] = { }; + unsigned int len = 0, opt; - len = scnprintf(event_string, sizeof(event_string), "%s", "WEDGED="); + seq_buf_puts(&event_string, "WEDGED="); + envp[0] = event_string.buffer; for_each_set_bit(opt, &method, BITS_PER_TYPE(method)) { - recovery = drm_get_wedge_recovery(opt); + const char *recovery = drm_get_wedge_recovery(opt); if (drm_WARN_ONCE(dev, !recovery, "invalid recovery method %u\n", opt)) break; - len += scnprintf(event_string + len, sizeof(event_string) - len, "%s,", recovery); + if (drm_WARN_ON_ONCE(dev, seq_buf_printf(&event_string, "%s,", recovery))) + break; + + len = seq_buf_used(&event_string); } - if (recovery) - /* Get rid of trailing comma */ - event_string[len - 1] = '\0'; + if (len) + /* Strip trailing comma; also discards any partial overflow entry */ + event_string.buffer[len - 1] = '\0'; else - /* Caller is unsure about recovery, do the best we can at this point. */ - snprintf(event_string, sizeof(event_string), "%s", "WEDGED=unknown"); + /* No complete entry written, do the best we can at this point. */ + snprintf(event_string.buffer, event_string.size, "%s", "WEDGED=unknown"); drm_info(dev, "device wedged, %s\n", method == DRM_WEDGE_RECOVERY_NONE ? "but no recovery needed" : "needs recovery"); diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 07970e5b5f65..9990a836d0b6 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -101,20 +101,42 @@ enum drm_edid_internal_quirk { #define MICROSOFT_IEEE_OUI 0xca125c #define AMD_IEEE_OUI 0x00001A +#define AMD_VSDB_V1_PAYLOAD_LEN 8 +#define AMD_VSDB_V2_PAYLOAD_LEN 13 #define AMD_VSDB_V3_PAYLOAD_MIN_LEN 15 -#define AMD_VSDB_V3_PAYLOAD_MAX_LEN 20 +#define AMD_VSDB_V3_PAYLOAD_MAX_LEN 21 -struct amd_vsdb_v3_payload { +#define AMD_VSDB_V3_MAX_VFREQ_EXT2_LEN 21 + +struct amd_vsdb_common_payload { u8 oui[3]; u8 version; +} __packed; + +struct amd_vsdb_v1_payload { + struct amd_vsdb_common_payload common; u8 feature_caps; - u8 rsvd0[3]; + u8 min_vfreq; + u8 max_vfreq; + u8 freesync_vcp_code; +} __packed; + +struct amd_vsdb_v2_payload { + struct amd_vsdb_v1_payload v1; u8 cs_eotf_support; u8 lum1_max; u8 lum1_min; u8 lum2_max; u8 lum2_min; - u8 rsvd1[2]; +} __packed; + +struct amd_vsdb_v3_payload { + struct amd_vsdb_v2_payload v2; + u8 max_vfreq_ext_low; + /* + * byte 14: bits 0-1 extend MSB of max refresh rate + */ + u8 max_vfreq_ext_high; /* * Bytes beyond AMD_VSDB_V3_PAYLOAD_MIN_LEN are optional; a * monitor may provide a payload as short as 15 bytes. Always @@ -123,6 +145,12 @@ struct amd_vsdb_v3_payload { u8 extra[AMD_VSDB_V3_PAYLOAD_MAX_LEN - AMD_VSDB_V3_PAYLOAD_MIN_LEN]; } __packed; +static const u8 amd_vsdb_min_len[] = { + [1] = AMD_VSDB_V1_PAYLOAD_LEN, + [2] = AMD_VSDB_V2_PAYLOAD_LEN, + [3] = AMD_VSDB_V3_PAYLOAD_MIN_LEN, +}; + struct detailed_mode_closure { struct drm_connector *connector; const struct drm_edid *drm_edid; @@ -5231,7 +5259,7 @@ static bool cea_db_is_microsoft_vsdb(const struct cea_db *db) static bool cea_db_is_amd_vsdb(const struct cea_db *db) { return cea_db_is_vendor(db, AMD_IEEE_OUI) && - cea_db_payload_len(db) >= AMD_VSDB_V3_PAYLOAD_MIN_LEN && + cea_db_payload_len(db) >= AMD_VSDB_V1_PAYLOAD_LEN && cea_db_payload_len(db) <= AMD_VSDB_V3_PAYLOAD_MAX_LEN; } @@ -6182,6 +6210,33 @@ static void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector, hdmi->y420_dc_modes = dc_mask; } +static void drm_parse_hdmi_gaming_info(struct drm_hdmi_info *hdmi, const u8 *db) +{ + struct drm_hdmi_vrr_cap *vrr = &hdmi->vrr_cap; + + if (cea_db_payload_len(db) < 8) + return; + + hdmi->fapa_start_location = db[8] & DRM_EDID_FAPA_START_LOCATION; + hdmi->allm = db[8] & DRM_EDID_ALLM; + vrr->fva = db[8] & DRM_EDID_FVA; + vrr->cnmvrr = db[8] & DRM_EDID_CNMVRR; + vrr->cinema_vrr = db[8] & DRM_EDID_CINEMA_VRR; + vrr->mdelta = db[8] & DRM_EDID_MDELTA; + + if (cea_db_payload_len(db) < 9) + return; + + vrr->vrr_min = db[9] & DRM_EDID_VRR_MIN_MASK; + vrr->supported = (vrr->vrr_min > 0 && vrr->vrr_min <= 48); + + if (cea_db_payload_len(db) < 10) + return; + + vrr->vrr_max = (db[9] & DRM_EDID_VRR_MAX_UPPER_MASK) << 2 | db[10]; + vrr->supported &= (vrr->vrr_max == 0 || vrr->vrr_max >= 100); +} + static void drm_parse_dsc_info(struct drm_hdmi_dsc_cap *hdmi_dsc, const u8 *hf_scds) { @@ -6308,6 +6363,8 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector, drm_parse_ycbcr420_deep_color_info(connector, hf_scds); + drm_parse_hdmi_gaming_info(&connector->display_info.hdmi, hf_scds); + if (cea_db_payload_len(hf_scds) >= 11 && hf_scds[11]) { drm_parse_dsc_info(hdmi_dsc, hf_scds); dsc_support = true; @@ -6317,6 +6374,19 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector, "[CONNECTOR:%d:%s] HF-VSDB: max TMDS clock: %d KHz, HDMI 2.1 support: %s, DSC 1.2 support: %s\n", connector->base.id, connector->name, max_tmds_clock, str_yes_no(max_frl_rate), str_yes_no(dsc_support)); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] FAPA in blanking: %s, ALLM support: %s, Fast Vactive support: %s\n", + connector->base.id, connector->name, str_yes_no(hdmi->fapa_start_location), + str_yes_no(hdmi->allm), str_yes_no(hdmi->vrr_cap.fva)); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Negative M VRR support: %s, CinemaVRR support: %s, Mdelta: %d\n", + connector->base.id, connector->name, str_yes_no(hdmi->vrr_cap.cnmvrr), + str_yes_no(hdmi->vrr_cap.cinema_vrr), hdmi->vrr_cap.mdelta); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] VRRmin: %u, VRRmax: %u, VRR supported: %s\n", + connector->base.id, connector->name, hdmi->vrr_cap.vrr_min, + hdmi->vrr_cap.vrr_max, str_yes_no(hdmi->vrr_cap.supported)); + } static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, @@ -6431,43 +6501,104 @@ static void drm_parse_microsoft_vsdb(struct drm_connector *connector, connector->base.id, connector->name, version, db[5]); } +static void drm_parse_amd_vsdb_common(struct drm_display_info *info, + const struct amd_vsdb_common_payload *data) +{ + info->amd_vsdb.version = data->version; +} + +static void drm_parse_amd_vsdb_v1(struct drm_display_info *info, + const struct amd_vsdb_v1_payload *data) +{ + info->amd_vsdb.freesync_supported = data->feature_caps & 0x1; + info->amd_vsdb.min_frame_rate = data->min_vfreq; + info->amd_vsdb.max_frame_rate = data->max_vfreq; + info->amd_vsdb.freesync_vcp_code = data->freesync_vcp_code; + + drm_parse_amd_vsdb_common(info, &data->common); +} + +static void drm_parse_amd_vsdb_v2(struct drm_display_info *info, + const struct amd_vsdb_v2_payload *data) +{ + info->amd_vsdb.luminance_range1.max_luminance = data->lum1_max; + info->amd_vsdb.luminance_range1.min_luminance = data->lum1_min; + info->amd_vsdb.luminance_range2.max_luminance = data->lum2_max; + info->amd_vsdb.luminance_range2.min_luminance = data->lum2_min; + + drm_parse_amd_vsdb_v1(info, &data->v1); +} + +static void drm_parse_amd_vsdb_v3(struct drm_display_info *info, + const struct amd_vsdb_v3_payload *data, + int payload_len) +{ + u16 max_frame_rate; + + info->amd_vsdb.replay_mode = data->v2.v1.feature_caps & 0x40; + info->amd_vsdb.panel_type = (data->v2.cs_eotf_support & 0xC0) >> 6; + + drm_parse_amd_vsdb_v2(info, &data->v2); + + /* vfreq is provided in a different set of fields for v3. */ + max_frame_rate = data->max_vfreq_ext_low | + (data->max_vfreq_ext_high & 0x3) << 8; + + /* + * The AMD VSDB v3 payload length is variable (15..21 bytes). + * All fields through max_vfreq_ext_high (byte 14) are always + * present, but data->extra[] (bytes 15+) may not be, so any access + * to extra[] must be guarded with a runtime length check to + * avoid out-of-bounds reads on shorter (but spec-valid) payloads. + */ + if (payload_len >= AMD_VSDB_V3_MAX_VFREQ_EXT2_LEN) + max_frame_rate |= (data->extra[5] & 0x3) << 10; + + info->amd_vsdb.max_frame_rate = max_frame_rate; +} + static void drm_parse_amd_vsdb(struct drm_connector *connector, const struct cea_db *db) { struct drm_display_info *info = &connector->display_info; const u8 *data = cea_db_data(db); - const struct amd_vsdb_v3_payload *p; + int payload_len = cea_db_payload_len(db); + u8 version; - p = (const struct amd_vsdb_v3_payload *)data; + version = ((const struct amd_vsdb_common_payload *)data)->version; - if (p->version != 0x03) { + if (version < 1) { drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Unsupported AMD VSDB version %u\n", - connector->base.id, connector->name, p->version); + connector->base.id, connector->name, version); return; } - info->amd_vsdb.version = p->version; - info->amd_vsdb.replay_mode = p->feature_caps & 0x40; - info->amd_vsdb.panel_type = (p->cs_eotf_support & 0xC0) >> 6; - info->amd_vsdb.luminance_range1.max_luminance = p->lum1_max; - info->amd_vsdb.luminance_range1.min_luminance = p->lum1_min; - info->amd_vsdb.luminance_range2.max_luminance = p->lum2_max; - info->amd_vsdb.luminance_range2.min_luminance = p->lum2_min; + if ((version <= 3 && payload_len < amd_vsdb_min_len[version]) || + (version > 3 && payload_len < AMD_VSDB_V3_PAYLOAD_MIN_LEN)) { + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] AMD VSDB v%u payload too short (%d bytes)\n", + connector->base.id, connector->name, version, payload_len); + return; + } - /* - * The AMD VSDB v3 payload length is variable (15..20 bytes). - * All fields through p->rsvd1 (byte 14) are always present, - * but p->extra[] (bytes 15+) may not be. Any future access to - * extra[] must be guarded with a runtime length check to avoid - * out-of-bounds reads on shorter (but spec-valid) payloads. - * For example: - * - * int len = cea_db_payload_len(db); - * - * if (len > AMD_VSDB_V3_PAYLOAD_MIN_LEN) - * info->amd_vsdb.foo = p->extra[0]; - */ + switch (version) { + default: + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] AMD VSDB v%u is unsupported, VSDB will be parsed as v3.\n", + connector->base.id, connector->name, version); + fallthrough; + case 3: + drm_parse_amd_vsdb_v3(info, (const struct amd_vsdb_v3_payload *)data, + payload_len); + break; + case 2: + drm_parse_amd_vsdb_v2(info, (const struct amd_vsdb_v2_payload *)data); + break; + case 1: + drm_parse_amd_vsdb_v1(info, (const struct amd_vsdb_v1_payload *)data); + break; + } } static void drm_parse_cea_ext(struct drm_connector *connector, diff --git a/drivers/gpu/drm/drm_gem_atomic_helper.c b/drivers/gpu/drm/drm_gem_atomic_helper.c index abef865c5f2c..3bb6bfd560d3 100644 --- a/drivers/gpu/drm/drm_gem_atomic_helper.c +++ b/drivers/gpu/drm/drm_gem_atomic_helper.c @@ -181,6 +181,48 @@ EXPORT_SYMBOL_GPL(drm_gem_plane_helper_prepare_fb); */ /** + * __drm_gem_shadow_plane_state_init - initializes shadow-buffered plane state + * @plane: the plane + * @shadow_plane_state: the shadow-buffered plane state + * + * This function initializes a pre-allocated shadow-buffered plane state. + * Helpful for drivers that subclass struct drm_shadow_plane_state. + */ +void __drm_gem_shadow_plane_state_init(struct drm_plane *plane, + struct drm_shadow_plane_state *shadow_plane_state) +{ + __drm_atomic_helper_plane_state_init(&shadow_plane_state->base, plane); + drm_format_conv_state_init(&shadow_plane_state->fmtcnv_state); +} +EXPORT_SYMBOL(__drm_gem_shadow_plane_state_init); + +/** + * drm_gem_create_shadow_plane_state - creates a shadow-buffered plane state + * @plane: the plane + * + * This function implements struct &drm_plane_funcs.atomic_create_state for + * shadow-buffered planes. It assumes the new state to be of type + * struct drm_shadow_plane_state. + * + * Returns: + * A pointer to a new plane state on success, or an ERR_PTR()-encoded + * error code otherwise. + */ +struct drm_plane_state *drm_gem_create_shadow_plane_state(struct drm_plane *plane) +{ + struct drm_shadow_plane_state *shadow_plane_state; + + shadow_plane_state = kzalloc_obj(*shadow_plane_state); + if (!shadow_plane_state) + return ERR_PTR(-ENOMEM); + + __drm_gem_shadow_plane_state_init(plane, shadow_plane_state); + + return &shadow_plane_state->base; +} +EXPORT_SYMBOL(drm_gem_create_shadow_plane_state); + +/** * __drm_gem_duplicate_shadow_plane_state - duplicates shadow-buffered plane state * @plane: the plane * @new_shadow_plane_state: the new shadow-buffered plane state @@ -298,29 +340,6 @@ void __drm_gem_reset_shadow_plane(struct drm_plane *plane, EXPORT_SYMBOL(__drm_gem_reset_shadow_plane); /** - * drm_gem_reset_shadow_plane - resets a shadow-buffered plane - * @plane: the plane - * - * This function implements struct &drm_plane_funcs.reset_plane for - * shadow-buffered planes. It assumes the current plane state to be - * of type struct drm_shadow_plane and it allocates the new state of - * this type. - */ -void drm_gem_reset_shadow_plane(struct drm_plane *plane) -{ - struct drm_shadow_plane_state *shadow_plane_state; - - if (plane->state) { - drm_gem_destroy_shadow_plane_state(plane, plane->state); - plane->state = NULL; /* must be set to NULL here */ - } - - shadow_plane_state = kzalloc_obj(*shadow_plane_state); - __drm_gem_reset_shadow_plane(plane, shadow_plane_state); -} -EXPORT_SYMBOL(drm_gem_reset_shadow_plane); - -/** * drm_gem_begin_shadow_fb_access - prepares shadow framebuffers for CPU access * @plane: the plane * @plane_state: the plane state of type struct drm_shadow_plane_state @@ -408,17 +427,22 @@ void drm_gem_simple_kms_end_shadow_fb_access(struct drm_simple_display_pipe *pip EXPORT_SYMBOL(drm_gem_simple_kms_end_shadow_fb_access); /** - * drm_gem_simple_kms_reset_shadow_plane - resets a shadow-buffered plane + * drm_gem_simple_kms_create_shadow_plane_state - creates shadow-buffered plane state * @pipe: the simple display pipe * - * This function implements struct drm_simple_display_funcs.reset_plane + * This function implements struct drm_simple_display_pipe_funcs.create_plane_state * for shadow-buffered planes. + * + * Returns: + * A pointer to a new plane state on success, or an ERR_PTR()-encoded + * error code otherwise. */ -void drm_gem_simple_kms_reset_shadow_plane(struct drm_simple_display_pipe *pipe) +struct drm_plane_state * +drm_gem_simple_kms_create_shadow_plane_state(struct drm_simple_display_pipe *pipe) { - drm_gem_reset_shadow_plane(&pipe->plane); + return drm_gem_create_shadow_plane_state(&pipe->plane); } -EXPORT_SYMBOL(drm_gem_simple_kms_reset_shadow_plane); +EXPORT_SYMBOL(drm_gem_simple_kms_create_shadow_plane_state); /** * drm_gem_simple_kms_duplicate_shadow_plane_state - duplicates shadow-buffered plane state diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index e495117735e1..2b53124ded68 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -287,13 +287,12 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, struct drm_panel **panel, struct drm_bridge **bridge) { - int ret = -EPROBE_DEFER; - struct device_node *remote; - if (WARN_ON(!panel)) return -EINVAL; *panel = NULL; + if (bridge) + *bridge = NULL; /* * of_graph_get_remote_node() produces a noisy error message if port @@ -304,30 +303,27 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, if (!of_graph_is_present(np)) return -ENODEV; - remote = of_graph_get_remote_node(np, port, endpoint); + struct device_node *remote __free(device_node) = + of_graph_get_remote_node(np, port, endpoint); if (!remote) return -ENODEV; *panel = of_drm_find_panel(remote); if (!IS_ERR(*panel)) - ret = 0; - else - *panel = NULL; + return 0; + + *panel = NULL; if (bridge) { - if (ret) { - /* No panel found yet, check for a bridge next. */ - *bridge = of_drm_find_bridge(remote); - if (*bridge) - ret = 0; - } else { - *bridge = NULL; - } + /* No panel found yet, check for a bridge next. */ + *bridge = of_drm_find_bridge(remote); + if (*bridge) + return 0; + *bridge = NULL; } - of_node_put(remote); - return ret; + return -EPROBE_DEFER; } EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); @@ -550,8 +546,8 @@ EXPORT_SYMBOL_GPL(drm_of_lvds_get_data_mapping); * * Return: * * min..max - positive integer count of "data-lanes" elements - * * -ve - the "data-lanes" property is missing or invalid - * * -EINVAL - the "data-lanes" property is unsupported + * * -ENODATA - the property does not have a value. + * * -EINVAL - the "data-lanes" property is missing or invalid */ int drm_of_get_data_lanes_count(const struct device_node *endpoint, const unsigned int min, const unsigned int max) @@ -582,8 +578,8 @@ EXPORT_SYMBOL_GPL(drm_of_get_data_lanes_count); * * Return: * * min..max - positive integer count of "data-lanes" elements - * * -EINVAL - the "data-mapping" property is unsupported - * * -ENODEV - the "data-mapping" property is missing + * * -ENODATA - the property does not have a value. + * * -EINVAL - the "data-lanes" property is missing or invalid */ int drm_of_get_data_lanes_count_ep(const struct device_node *port, int port_reg, int reg, diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index 8e1d07b9f1e3..5e324c77ad27 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -20,16 +20,6 @@ static const struct drm_encoder_funcs drm_simple_encoder_funcs_cleanup = { .destroy = drm_encoder_cleanup, }; -int drm_simple_encoder_init(struct drm_device *dev, - struct drm_encoder *encoder, - int encoder_type) -{ - return drm_encoder_init(dev, encoder, - &drm_simple_encoder_funcs_cleanup, - encoder_type, NULL); -} -EXPORT_SYMBOL(drm_simple_encoder_init); - void *__drmm_simple_encoder_alloc(struct drm_device *dev, size_t size, size_t offset, int encoder_type) { @@ -102,17 +92,6 @@ static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = { .atomic_disable = drm_simple_kms_crtc_disable, }; -static void drm_simple_kms_crtc_reset(struct drm_crtc *crtc) -{ - struct drm_simple_display_pipe *pipe; - - pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); - if (!pipe->funcs || !pipe->funcs->reset_crtc) - return drm_atomic_helper_crtc_reset(crtc); - - return pipe->funcs->reset_crtc(pipe); -} - static struct drm_crtc_state *drm_simple_kms_crtc_duplicate_state(struct drm_crtc *crtc) { struct drm_simple_display_pipe *pipe; @@ -158,7 +137,7 @@ static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc) } static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = { - .reset = drm_simple_kms_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, @@ -281,15 +260,16 @@ static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = { .atomic_update = drm_simple_kms_plane_atomic_update, }; -static void drm_simple_kms_plane_reset(struct drm_plane *plane) +static struct drm_plane_state * +drm_simple_kms_plane_create_state(struct drm_plane *plane) { struct drm_simple_display_pipe *pipe; pipe = container_of(plane, struct drm_simple_display_pipe, plane); - if (!pipe->funcs || !pipe->funcs->reset_plane) - return drm_atomic_helper_plane_reset(plane); + if (!pipe->funcs || !pipe->funcs->create_plane_state) + return drm_atomic_helper_plane_create_state(plane); - return pipe->funcs->reset_plane(pipe); + return pipe->funcs->create_plane_state(pipe); } static struct drm_plane_state *drm_simple_kms_plane_duplicate_state(struct drm_plane *plane) @@ -319,7 +299,7 @@ static const struct drm_plane_funcs drm_simple_kms_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_cleanup, - .reset = drm_simple_kms_plane_reset, + .atomic_create_state = drm_simple_kms_plane_create_state, .atomic_duplicate_state = drm_simple_kms_plane_duplicate_state, .atomic_destroy_state = drm_simple_kms_plane_destroy_state, .format_mod_supported = drm_simple_kms_format_mod_supported, @@ -363,7 +343,8 @@ int drm_simple_display_pipe_init(struct drm_device *dev, return ret; encoder->possible_crtcs = drm_crtc_mask(crtc); - ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE); + ret = drm_encoder_init(dev, encoder, &drm_simple_encoder_funcs_cleanup, + DRM_MODE_ENCODER_NONE, NULL); if (ret || !connector) return ret; diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index b071c019670d..458cad64bd07 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -163,7 +163,7 @@ static const struct drm_crtc_funcs exynos_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, .destroy = exynos_drm_crtc_destroy, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .enable_vblank = exynos_drm_crtc_enable_vblank, diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 0683236aa5df..74d3a2bebb74 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -201,8 +201,7 @@ int exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder) ret = drm_encoder_init(dev, encoder, &exynos_dpi_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); if (ret) { - DRM_DEV_ERROR(encoder_to_dpi(encoder)->dev, - "failed to create encoder ret = %d\n", ret); + drm_err(dev, "failed to create encoder ret = %d\n", ret); return ret; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 59dea853d364..077571eae9d6 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -452,8 +452,7 @@ static int vidi_bind(struct device *dev, struct device *master, void *data) ret = drm_encoder_init(drm_dev, encoder, &exynos_vidi_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); if (ret) { - DRM_DEV_ERROR(dev, "failed to initialize encoder ret = %d\n", - ret); + drm_err(drm_dev, "failed to initialize encoder ret = %d\n", ret); return ret; } diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index d4fce39c2b61..055dc04f9b3e 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1869,8 +1869,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) ret = drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); if (ret) { - DRM_DEV_ERROR(dev, "failed to initialize encoder ret = %d\n", - ret); + drm_err(drm_dev, "failed to initialize encoder ret = %d\n", ret); return ret; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c index 9ef16f5eb613..73c0675e6c54 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c @@ -161,7 +161,7 @@ static const struct drm_crtc_funcs fsl_dcu_drm_crtc_funcs = { .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .destroy = drm_crtc_cleanup, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .set_config = drm_atomic_helper_set_config, .enable_vblank = fsl_dcu_drm_crtc_enable_vblank, .disable_vblank = fsl_dcu_drm_crtc_disable_vblank, diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c index ec71fbbb0eb8..c64264541fe2 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c @@ -11,14 +11,18 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> +#include <drm/drm_encoder.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> #include "fsl_dcu_drm_drv.h" #include "fsl_tcon.h" +static const struct drm_encoder_funcs fsl_dcu_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev, struct drm_crtc *crtc) { @@ -31,8 +35,8 @@ int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev, if (fsl_dev->tcon) fsl_tcon_bypass_enable(fsl_dev->tcon); - ret = drm_simple_encoder_init(fsl_dev->drm, encoder, - DRM_MODE_ENCODER_LVDS); + ret = drm_encoder_init(fsl_dev->drm, encoder, &fsl_dcu_encoder_funcs, + DRM_MODE_ENCODER_LVDS, NULL); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/gud/gud_drv.c b/drivers/gpu/drm/gud/gud_drv.c index 3a1b9e2a2eaa..680447f426f3 100644 --- a/drivers/gpu/drm/gud/gud_drv.c +++ b/drivers/gpu/drm/gud/gud_drv.c @@ -347,7 +347,7 @@ static const struct drm_crtc_helper_funcs gud_crtc_helper_funcs = { }; static const struct drm_crtc_funcs gud_crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c index b4ab53db1c08..33402cf54072 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c @@ -491,7 +491,7 @@ static const struct drm_crtc_funcs hibmc_crtc_funcs = { .page_flip = drm_atomic_helper_page_flip, .set_config = drm_atomic_helper_set_config, .destroy = drm_crtc_cleanup, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .enable_vblank = hibmc_crtc_enable_vblank, diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c index 15042365dec0..62c5bd3277da 100644 --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -20,11 +20,11 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_device.h> +#include <drm/drm_encoder.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> #include "dw_dsi_reg.h" @@ -687,6 +687,10 @@ static int dsi_encoder_atomic_check(struct drm_encoder *encoder, return 0; } +static const struct drm_encoder_funcs dw_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + static const struct drm_encoder_helper_funcs dw_encoder_helper_funcs = { .atomic_check = dsi_encoder_atomic_check, .mode_valid = dsi_encoder_mode_valid, @@ -708,7 +712,8 @@ static int dw_drm_encoder_init(struct device *dev, } encoder->possible_crtcs = crtc_mask; - ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI); + ret = drm_encoder_init(drm_dev, encoder, &dw_encoder_funcs, + DRM_MODE_ENCODER_DSI, NULL); if (ret) { DRM_ERROR("failed to init dsi encoder\n"); return ret; diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index 902c5dace4ff..5f4e738bc94e 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -540,7 +540,7 @@ static const struct drm_crtc_funcs ade_crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .enable_vblank = ade_crtc_enable_vblank, diff --git a/drivers/gpu/drm/hyperv/hyperv_drm.h b/drivers/gpu/drm/hyperv/hyperv_drm.h index 78136ec2c2f4..29e6f54af15f 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm.h +++ b/drivers/gpu/drm/hyperv/hyperv_drm.h @@ -29,7 +29,6 @@ struct hv_drm_device { unsigned long fb_base; unsigned long fb_size; struct completion wait; - u32 synthvid_version; u32 mmio_megabytes; bool dirt_needed; diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c index 1855749c1e41..3261e730c69d 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c @@ -126,7 +126,7 @@ static const struct drm_crtc_helper_funcs hv_drm_crtc_helper_funcs = { }; static const struct drm_crtc_funcs hv_drm_crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c index f0ef627b4898..1d09bba1efdd 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c @@ -17,7 +17,7 @@ #define SYNTHVID_VER_GET_MAJOR(ver) (ver & 0x0000ffff) #define SYNTHVID_VER_GET_MINOR(ver) ((ver & 0xffff0000) >> 16) -/* Support for VERSION_WIN7 is removed. #define is retained for reference. */ +/* Support for WIN7 and WIN8 is removed. #define's retained for reference. */ #define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0) #define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2) #define SYNTHVID_VERSION_WIN10 SYNTHVID_VERSION(3, 5) @@ -181,16 +181,6 @@ struct synthvid_msg { }; } __packed; -static inline bool hv_drm_version_ge(u32 ver1, u32 ver2) -{ - if (SYNTHVID_VER_GET_MAJOR(ver1) > SYNTHVID_VER_GET_MAJOR(ver2) || - (SYNTHVID_VER_GET_MAJOR(ver1) == SYNTHVID_VER_GET_MAJOR(ver2) && - SYNTHVID_VER_GET_MINOR(ver1) >= SYNTHVID_VER_GET_MINOR(ver2))) - return true; - - return false; -} - static inline int hv_drm_sendpacket(struct hv_device *hdev, struct synthvid_msg *msg) { static atomic64_t request_id = ATOMIC64_INIT(0); @@ -236,7 +226,6 @@ static int hv_drm_negotiate_version(struct hv_device *hdev, u32 ver) return -ENODEV; } - hv->synthvid_version = ver; drm_info(dev, "Synthvid Version major %d, minor %d\n", SYNTHVID_VER_GET_MAJOR(ver), SYNTHVID_VER_GET_MINOR(ver)); @@ -557,23 +546,12 @@ int hv_drm_connect_vsp(struct hv_device *hdev) return ret; } - /* Negotiate the protocol version with host */ - switch (vmbus_proto_version) { - case VERSION_WIN10: - case VERSION_WIN10_V5: - ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); - if (!ret) - break; - fallthrough; - case VERSION_WIN8: - case VERSION_WIN8_1: - ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN8); - break; - default: - ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); - break; - } - + /* + * Negotiate the protocol version with host. Since support for hosts + * older than WIN10 has been removed from Linux, only negotiate the + * WIN10 version. + */ + ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); if (ret) { drm_err(dev, "Synthetic video device version not accepted %d\n", ret); goto error; @@ -581,11 +559,9 @@ int hv_drm_connect_vsp(struct hv_device *hdev) hv->screen_depth = SYNTHVID_DEPTH_WIN8; - if (hv_drm_version_ge(hv->synthvid_version, SYNTHVID_VERSION_WIN10)) { - ret = hv_drm_get_supported_resolution(hdev); - if (ret) - drm_err(dev, "Failed to get supported resolution from host, use default\n"); - } + ret = hv_drm_get_supported_resolution(hdev); + if (ret) + drm_err(dev, "Failed to get supported resolution from host, use default\n"); if (!hv->screen_width_max) { hv->screen_width_max = SYNTHVID_WIDTH_WIN8; diff --git a/drivers/gpu/drm/imx/dc/dc-crtc.c b/drivers/gpu/drm/imx/dc/dc-crtc.c index 764510ff90a4..0d64186a9901 100644 --- a/drivers/gpu/drm/imx/dc/dc-crtc.c +++ b/drivers/gpu/drm/imx/dc/dc-crtc.c @@ -98,7 +98,7 @@ static void dc_crtc_disable_vblank(struct drm_crtc *crtc) } static const struct drm_crtc_funcs dc_crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/imx/dc/dc-kms.c b/drivers/gpu/drm/imx/dc/dc-kms.c index 0f8cfaf4c4d1..a9adcfc68b84 100644 --- a/drivers/gpu/drm/imx/dc/dc-kms.c +++ b/drivers/gpu/drm/imx/dc/dc-kms.c @@ -17,7 +17,6 @@ #include <drm/drm_mode_config.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> #include "dc-de.h" @@ -30,6 +29,10 @@ static const struct drm_mode_config_funcs dc_drm_mode_config_funcs = { .atomic_commit = drm_atomic_helper_commit, }; +static const struct drm_encoder_funcs dc_kms_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + static int dc_kms_init_encoder_per_crtc(struct dc_drm_device *dc_drm, int crtc_index) { @@ -55,7 +58,8 @@ static int dc_kms_init_encoder_per_crtc(struct dc_drm_device *dc_drm, } encoder = &dc_drm->encoder[crtc_index]; - ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE); + ret = drm_encoder_init(drm, encoder, &dc_kms_encoder_funcs, + DRM_MODE_ENCODER_NONE, NULL); if (ret) { dev_err(dev, "failed to initialize encoder for CRTC%u: %d\n", crtc->index, ret); diff --git a/drivers/gpu/drm/imx/dcss/dcss-crtc.c b/drivers/gpu/drm/imx/dcss/dcss-crtc.c index ab41759a9f52..5e9ee3f09d51 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-crtc.c +++ b/drivers/gpu/drm/imx/dcss/dcss-crtc.c @@ -45,7 +45,7 @@ static const struct drm_crtc_funcs dcss_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .destroy = drm_crtc_cleanup, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .enable_vblank = dcss_enable_vblank, diff --git a/drivers/gpu/drm/imx/dcss/dcss-plane.c b/drivers/gpu/drm/imx/dcss/dcss-plane.c index 1746dc5d4b64..303e93fd036c 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-plane.c +++ b/drivers/gpu/drm/imx/dcss/dcss-plane.c @@ -96,7 +96,7 @@ static const struct drm_plane_funcs dcss_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = dcss_plane_destroy, - .reset = drm_atomic_helper_plane_reset, + .atomic_create_state = drm_atomic_helper_plane_create_state, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, .format_mod_supported = dcss_plane_format_mod_supported, diff --git a/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c index 1405e50ee56b..f4fe1842cfae 100644 --- a/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c @@ -107,19 +107,17 @@ static void ipu_crtc_atomic_disable(struct drm_crtc *crtc, spin_unlock_irq(&crtc->dev->event_lock); } -static void imx_drm_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *imx_drm_crtc_create_state(struct drm_crtc *crtc) { struct imx_crtc_state *state; - if (crtc->state) - __drm_atomic_helper_crtc_destroy_state(crtc->state); + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); - kfree(to_imx_crtc_state(crtc->state)); - crtc->state = NULL; + __drm_atomic_helper_crtc_state_init(&state->base, crtc); - state = kzalloc_obj(*state); - if (state) - __drm_atomic_helper_crtc_reset(crtc, &state->base); + return &state->base; } static struct drm_crtc_state *imx_drm_crtc_duplicate_state(struct drm_crtc *crtc) @@ -164,7 +162,7 @@ static void ipu_disable_vblank(struct drm_crtc *crtc) static const struct drm_crtc_funcs ipu_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = imx_drm_crtc_reset, + .atomic_create_state = imx_drm_crtc_create_state, .atomic_duplicate_state = imx_drm_crtc_duplicate_state, .atomic_destroy_state = imx_drm_crtc_destroy_state, .enable_vblank = ipu_enable_vblank, diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index 738a80b2550f..625ec76cf04d 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -987,7 +987,7 @@ static const struct drm_driver ingenic_drm_driver_data = { static const struct drm_plane_funcs ingenic_drm_primary_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .reset = drm_atomic_helper_plane_reset, + .atomic_create_state = drm_atomic_helper_plane_create_state, .destroy = drm_plane_cleanup, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, @@ -997,7 +997,7 @@ static const struct drm_plane_funcs ingenic_drm_primary_plane_funcs = { static const struct drm_crtc_funcs ingenic_drm_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.c b/drivers/gpu/drm/ingenic/ingenic-ipu.c index 56143a191f36..bf24882e8d9f 100644 --- a/drivers/gpu/drm/ingenic/ingenic-ipu.c +++ b/drivers/gpu/drm/ingenic/ingenic-ipu.c @@ -718,7 +718,7 @@ ingenic_ipu_plane_atomic_set_property(struct drm_plane *plane, static const struct drm_plane_funcs ingenic_ipu_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .reset = drm_atomic_helper_plane_reset, + .atomic_create_state = drm_atomic_helper_plane_create_state, .destroy = drm_plane_cleanup, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, diff --git a/drivers/gpu/drm/kmb/kmb_crtc.c b/drivers/gpu/drm/kmb/kmb_crtc.c index 1dcdc55b0069..06cf5ad60b81 100644 --- a/drivers/gpu/drm/kmb/kmb_crtc.c +++ b/drivers/gpu/drm/kmb/kmb_crtc.c @@ -58,7 +58,7 @@ static const struct drm_crtc_funcs kmb_crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .enable_vblank = kmb_crtc_enable_vblank, diff --git a/drivers/gpu/drm/kmb/kmb_dsi.c b/drivers/gpu/drm/kmb/kmb_dsi.c index 59d0e856392f..13adba96bc78 100644 --- a/drivers/gpu/drm/kmb/kmb_dsi.c +++ b/drivers/gpu/drm/kmb/kmb_dsi.c @@ -14,8 +14,8 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_bridge_connector.h> +#include <drm/drm_encoder.h> #include <drm/drm_mipi_dsi.h> -#include <drm/drm_simple_kms_helper.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> @@ -1427,6 +1427,10 @@ struct kmb_dsi *kmb_dsi_init(struct platform_device *pdev) return kmb_dsi; } +static const struct drm_encoder_funcs kmb_dsi_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + int kmb_dsi_encoder_init(struct drm_device *dev, struct kmb_dsi *kmb_dsi) { struct drm_encoder *encoder; @@ -1437,7 +1441,8 @@ int kmb_dsi_encoder_init(struct drm_device *dev, struct kmb_dsi *kmb_dsi) encoder->possible_crtcs = 1; encoder->possible_clones = 0; - ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_DSI); + ret = drm_encoder_init(dev, encoder, &kmb_dsi_encoder_funcs, + DRM_MODE_ENCODER_DSI, NULL); if (ret) { dev_err(kmb_dsi->dev, "Failed to init encoder %d\n", ret); return ret; diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index 7961139333ad..f82506e10fd5 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -21,12 +21,12 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_bridge_connector.h> +#include <drm/drm_encoder.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> #include "mtk_ddp_comp.h" #include "mtk_disp_drv.h" @@ -914,12 +914,16 @@ void mtk_dsi_ddp_stop(struct device *dev) mtk_dsi_poweroff(dsi); } +static const struct drm_encoder_funcs mtk_dsi_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + static int mtk_dsi_encoder_init(struct drm_device *drm, struct mtk_dsi *dsi) { int ret; - ret = drm_simple_encoder_init(drm, &dsi->encoder, - DRM_MODE_ENCODER_DSI); + ret = drm_encoder_init(drm, &dsi->encoder, &mtk_dsi_encoder_funcs, + DRM_MODE_ENCODER_DSI, NULL); if (ret) { drm_err(drm, "Failed to encoder init to drm\n"); return ret; diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c index b77d2d24ead1..056dbea8deec 100644 --- a/drivers/gpu/drm/meson/meson_crtc.c +++ b/drivers/gpu/drm/meson/meson_crtc.c @@ -74,7 +74,7 @@ static const struct drm_crtc_funcs meson_crtc_funcs = { .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .destroy = drm_crtc_cleanup, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .set_config = drm_atomic_helper_set_config, .enable_vblank = meson_crtc_enable_vblank, .disable_vblank = meson_crtc_disable_vblank, diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.c b/drivers/gpu/drm/meson/meson_encoder_cvbs.c index 22cacb1660c4..cdb84d2283f8 100644 --- a/drivers/gpu/drm/meson/meson_encoder_cvbs.c +++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.c @@ -17,8 +17,8 @@ #include <drm/drm_bridge_connector.h> #include <drm/drm_device.h> #include <drm/drm_edid.h> +#include <drm/drm_encoder.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> #include "meson_registers.h" #include "meson_vclk.h" @@ -218,6 +218,10 @@ static const struct drm_bridge_funcs meson_encoder_cvbs_bridge_funcs = { .atomic_create_state = drm_atomic_helper_bridge_create_state, }; +static const struct drm_encoder_funcs meson_encoder_cvbs_funcs = { + .destroy = drm_encoder_cleanup, +}; + int meson_encoder_cvbs_probe(struct meson_drm *priv) { struct drm_device *drm = priv->drm; @@ -257,8 +261,9 @@ int meson_encoder_cvbs_probe(struct meson_drm *priv) meson_encoder_cvbs->priv = priv; /* Encoder */ - ret = drm_simple_encoder_init(priv->drm, &meson_encoder_cvbs->encoder, - DRM_MODE_ENCODER_TVDAC); + ret = drm_encoder_init(priv->drm, &meson_encoder_cvbs->encoder, + &meson_encoder_cvbs_funcs, + DRM_MODE_ENCODER_TVDAC, NULL); if (ret) return dev_err_probe(priv->dev, ret, "Failed to init CVBS encoder\n"); diff --git a/drivers/gpu/drm/meson/meson_encoder_dsi.c b/drivers/gpu/drm/meson/meson_encoder_dsi.c index 3e422b612f74..faa309cb97a6 100644 --- a/drivers/gpu/drm/meson/meson_encoder_dsi.c +++ b/drivers/gpu/drm/meson/meson_encoder_dsi.c @@ -10,10 +10,10 @@ #include <linux/of_graph.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_simple_kms_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_bridge_connector.h> #include <drm/drm_device.h> +#include <drm/drm_encoder.h> #include <drm/drm_probe_helper.h> #include "meson_drv.h" @@ -99,6 +99,10 @@ static const struct drm_bridge_funcs meson_encoder_dsi_bridge_funcs = { .atomic_create_state = drm_atomic_helper_bridge_create_state, }; +static const struct drm_encoder_funcs meson_encoder_dsi_funcs = { + .destroy = drm_encoder_cleanup, +}; + int meson_encoder_dsi_probe(struct meson_drm *priv) { struct meson_encoder_dsi *meson_encoder_dsi; @@ -133,8 +137,9 @@ int meson_encoder_dsi_probe(struct meson_drm *priv) meson_encoder_dsi->priv = priv; /* Encoder */ - ret = drm_simple_encoder_init(priv->drm, &meson_encoder_dsi->encoder, - DRM_MODE_ENCODER_DSI); + ret = drm_encoder_init(priv->drm, &meson_encoder_dsi->encoder, + &meson_encoder_dsi_funcs, DRM_MODE_ENCODER_DSI, + NULL); if (ret) return dev_err_probe(priv->dev, ret, "Failed to init DSI encoder\n"); diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c index 0c7a72cb514a..c4355c5cc340 100644 --- a/drivers/gpu/drm/meson/meson_encoder_hdmi.c +++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c @@ -23,8 +23,8 @@ #include <drm/drm_bridge_connector.h> #include <drm/drm_device.h> #include <drm/drm_edid.h> +#include <drm/drm_encoder.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> #include <linux/media-bus-format.h> #include <linux/videodev2.h> @@ -369,6 +369,10 @@ static const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = { .atomic_create_state = drm_atomic_helper_bridge_create_state, }; +static const struct drm_encoder_funcs meson_encoder_hdmi_funcs = { + .destroy = drm_encoder_cleanup, +}; + int meson_encoder_hdmi_probe(struct meson_drm *priv) { struct meson_encoder_hdmi *meson_encoder_hdmi; @@ -407,8 +411,9 @@ int meson_encoder_hdmi_probe(struct meson_drm *priv) meson_encoder_hdmi->priv = priv; /* Encoder */ - ret = drm_simple_encoder_init(priv->drm, &meson_encoder_hdmi->encoder, - DRM_MODE_ENCODER_TMDS); + ret = drm_encoder_init(priv->drm, &meson_encoder_hdmi->encoder, + &meson_encoder_hdmi_funcs, + DRM_MODE_ENCODER_TMDS, NULL); if (ret) { dev_err_probe(priv->dev, ret, "Failed to init HDMI encoder\n"); goto err_put_node; diff --git a/drivers/gpu/drm/meson/meson_overlay.c b/drivers/gpu/drm/meson/meson_overlay.c index 7047807f1a3e..930119e18a4f 100644 --- a/drivers/gpu/drm/meson/meson_overlay.c +++ b/drivers/gpu/drm/meson/meson_overlay.c @@ -802,7 +802,7 @@ static const struct drm_plane_funcs meson_overlay_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_cleanup, - .reset = drm_atomic_helper_plane_reset, + .atomic_create_state = drm_atomic_helper_plane_create_state, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, .format_mod_supported = meson_overlay_format_mod_supported, diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c index 2d88fe87a11a..cf8c90158fcf 100644 --- a/drivers/gpu/drm/meson/meson_plane.c +++ b/drivers/gpu/drm/meson/meson_plane.c @@ -466,7 +466,7 @@ static const struct drm_plane_funcs meson_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_cleanup, - .reset = drm_atomic_helper_plane_reset, + .atomic_create_state = drm_atomic_helper_plane_create_state, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, .format_mod_supported = meson_plane_format_mod_supported, diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h index 3732c62351bb..bccc70983d28 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -407,15 +407,15 @@ void mgag200_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic .atomic_enable = mgag200_crtc_helper_atomic_enable, \ .atomic_disable = mgag200_crtc_helper_atomic_disable -void mgag200_crtc_reset(struct drm_crtc *crtc); +struct drm_crtc_state *mgag200_crtc_atomic_create_state(struct drm_crtc *crtc); struct drm_crtc_state *mgag200_crtc_atomic_duplicate_state(struct drm_crtc *crtc); void mgag200_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state); #define MGAG200_CRTC_FUNCS \ - .reset = mgag200_crtc_reset, \ .destroy = drm_crtc_cleanup, \ .set_config = drm_atomic_helper_set_config, \ .page_flip = drm_atomic_helper_page_flip, \ + .atomic_create_state = mgag200_crtc_atomic_create_state, \ .atomic_duplicate_state = mgag200_crtc_atomic_duplicate_state, \ .atomic_destroy_state = mgag200_crtc_atomic_destroy_state diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index 7e07fc3f1a60..4e952655e4f5 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -679,18 +679,17 @@ void mgag200_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic mgag200_disable_display(mdev); } -void mgag200_crtc_reset(struct drm_crtc *crtc) +struct drm_crtc_state *mgag200_crtc_atomic_create_state(struct drm_crtc *crtc) { struct mgag200_crtc_state *mgag200_crtc_state; - if (crtc->state) - crtc->funcs->atomic_destroy_state(crtc, crtc->state); - mgag200_crtc_state = kzalloc_obj(*mgag200_crtc_state); - if (mgag200_crtc_state) - __drm_atomic_helper_crtc_reset(crtc, &mgag200_crtc_state->base); - else - __drm_atomic_helper_crtc_reset(crtc, NULL); + if (!mgag200_crtc_state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_crtc_state_init(&mgag200_crtc_state->base, crtc); + + return &mgag200_crtc_state->base; } struct drm_crtc_state *mgag200_crtc_atomic_duplicate_state(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 42d0a529b4d5..77a2d1f6700d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1140,17 +1140,17 @@ end: DPU_ATRACE_END("crtc_commit"); } -static void dpu_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *dpu_crtc_create_state(struct drm_crtc *crtc) { - struct dpu_crtc_state *cstate = kzalloc_obj(*cstate); + struct dpu_crtc_state *cstate; - if (crtc->state) - dpu_crtc_destroy_state(crtc, crtc->state); + cstate = kzalloc_obj(*cstate); + if (!cstate) + return ERR_PTR(-ENOMEM); - if (cstate) - __drm_atomic_helper_crtc_reset(crtc, &cstate->base); - else - __drm_atomic_helper_crtc_reset(crtc, NULL); + __drm_atomic_helper_crtc_state_init(&cstate->base, crtc); + + return &cstate->base; } /** @@ -1843,7 +1843,7 @@ static int dpu_crtc_late_register(struct drm_crtc *crtc) static const struct drm_crtc_funcs dpu_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = dpu_crtc_reset, + .atomic_create_state = dpu_crtc_create_state, .atomic_duplicate_state = dpu_crtc_duplicate_state, .atomic_destroy_state = dpu_crtc_destroy_state, .atomic_print_state = dpu_crtc_atomic_print_state, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c index 7b92082d35a6..1a471cdf767b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c @@ -1728,32 +1728,28 @@ static void dpu_plane_atomic_print_state(struct drm_printer *p, } } -static void dpu_plane_reset(struct drm_plane *plane) +static struct drm_plane_state *dpu_plane_create_state(struct drm_plane *plane) { struct dpu_plane *pdpu; struct dpu_plane_state *pstate; if (!plane) { DPU_ERROR("invalid plane\n"); - return; + return ERR_PTR(-EINVAL); } pdpu = to_dpu_plane(plane); DPU_DEBUG_PLANE(pdpu, "\n"); - /* remove previous state, if present */ - if (plane->state) { - dpu_plane_destroy_state(plane, plane->state); - plane->state = NULL; - } - pstate = kzalloc_obj(*pstate); if (!pstate) { DPU_ERROR_PLANE(pdpu, "failed to allocate state\n"); - return; + return ERR_PTR(-ENOMEM); } - __drm_atomic_helper_plane_reset(plane, &pstate->base); + __drm_atomic_helper_plane_state_init(&pstate->base, plane); + + return &pstate->base; } #ifdef CONFIG_DEBUG_FS @@ -1796,7 +1792,7 @@ static bool dpu_plane_format_mod_supported(struct drm_plane *plane, static const struct drm_plane_funcs dpu_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .reset = dpu_plane_reset, + .atomic_create_state = dpu_plane_create_state, .atomic_duplicate_state = dpu_plane_duplicate_state, .atomic_destroy_state = dpu_plane_destroy_state, .atomic_print_state = dpu_plane_atomic_print_state, diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c index 57dfce58450b..93050b3c3548 100644 --- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c @@ -477,7 +477,7 @@ static const struct drm_crtc_funcs mdp4_crtc_funcs = { .page_flip = drm_atomic_helper_page_flip, .cursor_set = mdp4_crtc_cursor_set, .cursor_move = mdp4_crtc_cursor_move, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .enable_vblank = msm_crtc_enable_vblank, diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c index 9459f70ce0ba..f9067e508f4c 100644 --- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c @@ -71,7 +71,7 @@ static const struct drm_plane_funcs mdp4_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .set_property = mdp4_plane_set_property, - .reset = drm_atomic_helper_plane_reset, + .atomic_create_state = drm_atomic_helper_plane_create_state, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, }; diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c index 4c4a897fc1ee..1e1fff37380c 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c @@ -1128,23 +1128,23 @@ static void mdp5_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state kfree(mdp5_cstate); } -static void mdp5_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *mdp5_crtc_create_state(struct drm_crtc *crtc) { - struct mdp5_crtc_state *mdp5_cstate = kzalloc_obj(*mdp5_cstate); + struct mdp5_crtc_state *mdp5_cstate; - if (crtc->state) - mdp5_crtc_destroy_state(crtc, crtc->state); + mdp5_cstate = kzalloc_obj(*mdp5_cstate); + if (!mdp5_cstate) + return ERR_PTR(-ENOMEM); - if (mdp5_cstate) - __drm_atomic_helper_crtc_reset(crtc, &mdp5_cstate->base); - else - __drm_atomic_helper_crtc_reset(crtc, NULL); + __drm_atomic_helper_crtc_state_init(&mdp5_cstate->base, crtc); + + return &mdp5_cstate->base; } static const struct drm_crtc_funcs mdp5_crtc_no_lm_cursor_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = mdp5_crtc_reset, + .atomic_create_state = mdp5_crtc_create_state, .atomic_duplicate_state = mdp5_crtc_duplicate_state, .atomic_destroy_state = mdp5_crtc_destroy_state, .atomic_print_state = mdp5_crtc_atomic_print_state, @@ -1157,7 +1157,7 @@ static const struct drm_crtc_funcs mdp5_crtc_no_lm_cursor_funcs = { static const struct drm_crtc_funcs mdp5_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = mdp5_crtc_reset, + .atomic_create_state = mdp5_crtc_create_state, .atomic_duplicate_state = mdp5_crtc_duplicate_state, .atomic_destroy_state = mdp5_crtc_destroy_state, .cursor_set = mdp5_crtc_cursor_set, diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c index 841f444a8d68..7e878c698247 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c @@ -80,19 +80,15 @@ mdp5_plane_atomic_print_state(struct drm_printer *p, drm_printf(p, "\tstage=%s\n", stage2name(pstate->stage)); } -static void mdp5_plane_reset(struct drm_plane *plane) +static struct drm_plane_state *mdp5_plane_create_state(struct drm_plane *plane) { struct mdp5_plane_state *mdp5_state; - - if (plane->state) - __drm_atomic_helper_plane_destroy_state(plane->state); - - kfree(to_mdp5_plane_state(plane->state)); - plane->state = NULL; mdp5_state = kzalloc_obj(*mdp5_state); if (!mdp5_state) - return; - __drm_atomic_helper_plane_reset(plane, &mdp5_state->base); + return ERR_PTR(-ENOMEM); + __drm_atomic_helper_plane_state_init(&mdp5_state->base, plane); + + return &mdp5_state->base; } static struct drm_plane_state * @@ -126,7 +122,7 @@ static void mdp5_plane_destroy_state(struct drm_plane *plane, static const struct drm_plane_funcs mdp5_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .reset = mdp5_plane_reset, + .atomic_create_state = mdp5_plane_create_state, .atomic_duplicate_state = mdp5_plane_duplicate_state, .atomic_destroy_state = mdp5_plane_destroy_state, .atomic_print_state = mdp5_plane_atomic_print_state, diff --git a/drivers/gpu/drm/mxsfb/lcdif_kms.c b/drivers/gpu/drm/mxsfb/lcdif_kms.c index 79e0f483ca20..5f0fbec3a390 100644 --- a/drivers/gpu/drm/mxsfb/lcdif_kms.c +++ b/drivers/gpu/drm/mxsfb/lcdif_kms.c @@ -596,18 +596,17 @@ static void lcdif_crtc_atomic_destroy_state(struct drm_crtc *crtc, kfree(to_lcdif_crtc_state(state)); } -static void lcdif_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *lcdif_crtc_create_state(struct drm_crtc *crtc) { struct lcdif_crtc_state *state; - if (crtc->state) - lcdif_crtc_atomic_destroy_state(crtc, crtc->state); + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); - crtc->state = NULL; + __drm_atomic_helper_crtc_state_init(&state->base, crtc); - state = kzalloc_obj(*state); - if (state) - __drm_atomic_helper_crtc_reset(crtc, &state->base); + return &state->base; } static struct drm_crtc_state * @@ -659,7 +658,7 @@ static const struct drm_crtc_helper_funcs lcdif_crtc_helper_funcs = { }; static const struct drm_crtc_funcs lcdif_crtc_funcs = { - .reset = lcdif_crtc_reset, + .atomic_create_state = lcdif_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index d8ebebc5314b..36ea285182b4 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -484,7 +484,7 @@ static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = { }; static const struct drm_crtc_funcs mxsfb_crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, @@ -495,7 +495,7 @@ static const struct drm_crtc_funcs mxsfb_crtc_funcs = { }; static const struct drm_crtc_funcs mxsfb_crtc_with_crc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/nouveau/dispnv04/dfp.c b/drivers/gpu/drm/nouveau/dispnv04/dfp.c index c9f96ec8455d..00eb2aacbe93 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/dfp.c +++ b/drivers/gpu/drm/nouveau/dispnv04/dfp.c @@ -344,9 +344,8 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder, regp->fp_control |= (2 << 24); if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) { bool duallink = false, dummy; - if (nv_connector->edid && - nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { - duallink = (((u8 *)nv_connector->edid)[121] == 2); + if (nv_connector->spwg_links) { + duallink = nv_connector->spwg_links == 2; } else { nouveau_bios_parse_lvds_table(dev, output_mode->clock, &duallink, &dummy); diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 2c66e480b511..add19d479ebe 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1803,8 +1803,8 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_commit *st lvds_dual = bios->fp.dual_link; lvds_8bpc = bios->fp.if_is_24bit; } else { - if (nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { - if (((u8 *)nv_connector->edid)[121] == 2) + if (nv_connector->spwg_links) { + if (nv_connector->spwg_links == 2) lvds_dual = true; } else if (mode->clock >= bios->fp.duallink_transition_clk) { diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.c b/drivers/gpu/drm/nouveau/dispnv50/head.c index 1affa0378bd0..6a3a93de0cbf 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head.c +++ b/drivers/gpu/drm/nouveau/dispnv50/head.c @@ -491,18 +491,16 @@ nv50_head_atomic_duplicate_state(struct drm_crtc *crtc) return &asyh->state; } -static void -nv50_head_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *nv50_head_create_state(struct drm_crtc *crtc) { struct nv50_head_atom *asyh; if (WARN_ON(!(asyh = kzalloc_obj(*asyh)))) - return; + return ERR_PTR(-ENOMEM); - if (crtc->state) - nv50_head_atomic_destroy_state(crtc, crtc->state); + __drm_atomic_helper_crtc_state_init(&asyh->state, crtc); - __drm_atomic_helper_crtc_reset(crtc, &asyh->state); + return &asyh->state; } static int @@ -525,7 +523,7 @@ nv50_head_destroy(struct drm_crtc *crtc) static const struct drm_crtc_funcs nv50_head_func = { - .reset = nv50_head_reset, + .atomic_create_state = nv50_head_create_state, .destroy = nv50_head_destroy, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, @@ -539,7 +537,7 @@ nv50_head_func = { static const struct drm_crtc_funcs nvd9_head_func = { - .reset = nv50_head_reset, + .atomic_create_state = nv50_head_create_state, .destroy = nv50_head_destroy, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/nouveau/dispnv50/headca7d.c b/drivers/gpu/drm/nouveau/dispnv50/headca7d.c index eeaeb15aa664..678cbc78ca9f 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headca7d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headca7d.c @@ -219,10 +219,11 @@ headca7d_mode(struct nv50_head *head, struct nv50_head_atom *asyh) { struct nvif_push *push = &head->disp->core->chan.push; struct nv50_head_mode *m = &asyh->mode; + const u64 hz = (u64)m->clock * 1000; const int i = head->base.index; int ret; - ret = PUSH_WAIT(push, 11); + ret = PUSH_WAIT(push, 14); if (ret) return ret; @@ -245,11 +246,25 @@ headca7d_mode(struct nv50_head *head, struct nv50_head_atom *asyh) PUSH_MTHD(push, NVCA7D, HEAD_SET_CONTROL(i), NVDEF(NVCA7D, HEAD_SET_CONTROL, STRUCTURE, PROGRESSIVE)); + /* The FREQUENCY methods carry only 31 HERTZ bits; the upper bits + * of anything past 2.147GHz live in the HI methods + * (EvoSetRasterParams9()). Truncating would scan out at pclk modulo 2^31. + */ PUSH_MTHD(push, NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY(i), - NVVAL(NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY, HERTZ, m->clock * 1000)); + NVVAL(NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY, HERTZ, + (u32)(hz & 0x7fffffff))); PUSH_MTHD(push, NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX(i), - NVVAL(NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, HERTZ, m->clock * 1000)); + NVVAL(NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, HERTZ, + (u32)(hz & 0x7fffffff))); + + PUSH_MTHD(push, NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI(i), + NVVAL(NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI, HERTZ, + (u32)(hz >> 31)), + + HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI_MAX(i), + NVVAL(NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI_MAX, HERTZ, + (u32)(hz >> 31))); return 0; } diff --git a/drivers/gpu/drm/nouveau/include/nvhw/class/clca7d.h b/drivers/gpu/drm/nouveau/include/nvhw/class/clca7d.h index 0fec6fc21d44..5754878ddc69 100644 --- a/drivers/gpu/drm/nouveau/include/nvhw/class/clca7d.h +++ b/drivers/gpu/drm/nouveau/include/nvhw/class/clca7d.h @@ -734,6 +734,10 @@ #define NVCA7D_HEAD_SET_CONTROL_CURSOR_COMPOSITION_BYPASS 20:20 #define NVCA7D_HEAD_SET_CONTROL_CURSOR_COMPOSITION_BYPASS_DISABLE (0x00000000) #define NVCA7D_HEAD_SET_CONTROL_CURSOR_COMPOSITION_BYPASS_ENABLE (0x00000001) +#define NVCA7D_HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI(a) (0x000020C0 + (a)*0x00000800) +#define NVCA7D_HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI_HERTZ 3:0 +#define NVCA7D_HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI_MAX(a) (0x000020C4 + (a)*0x00000800) +#define NVCA7D_HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI_MAX_HERTZ 3:0 #define NVCA7D_HEAD_SET_SURFACE_ADDRESS_HI_CRC(a) (0x00002150 + (a)*0x00000800) #define NVCA7D_HEAD_SET_SURFACE_ADDRESS_HI_CRC_ADDRESS_HI 31:0 #define NVCA7D_HEAD_SET_SURFACE_ADDRESS_LO_CRC(a) (0x00002154 + (a)*0x00000800) diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h index 112b674ed9c8..0172e0d200f8 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h @@ -46,6 +46,7 @@ int nv4c_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct n int g84_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **); int g92_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **); int g94_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **); +int mcp79_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **); int gf100_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **); int gf106_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **); int gk104_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **); diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index 4542d5f4ded8..e907c1d07887 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -666,6 +666,10 @@ nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS) if (unlikely(device->info.family >= NV_DEVICE_INFO_V0_FERMI)) return nouveau_abi16_put(abi16, -EINVAL); + /* zero size yields a zero-length node, underflowing args.limit */ + if (unlikely(!info->size)) + return nouveau_abi16_put(abi16, -EINVAL); + chan = nouveau_abi16_chan(abi16, info->channel); if (!chan) return nouveau_abi16_put(abi16, -ENOENT); diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index 21b56cc7605c..9e6906490db5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -360,9 +360,10 @@ void nouveau_unregister_dsm_handler(void) {} void nouveau_switcheroo_optimus_dsm(void) {} #endif -void * +const struct drm_edid * nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) { + const struct drm_edid *drm_edid; struct acpi_device *acpidev; int type, ret; void *edid; @@ -384,7 +385,34 @@ nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) if (ret < 0) return NULL; - return edid; + /* Take what _DDC returned as-is first as its length is the only one that + * can describe an HF-EEODB EDID, whose block count is not the byte 126 + * one. Only if that does not validate fall back to trimming the buffer + * to the byte 126 size, which is what the padding some firmware appends + * needs. + */ + drm_edid = drm_edid_alloc(edid, ret); + if (drm_edid && !drm_edid_valid(drm_edid)) { + drm_edid_free(drm_edid); + drm_edid = NULL; + + if (ret > EDID_LENGTH) { + int trimmed = EDID_LENGTH * + (1 + ((const struct edid *)edid)->extensions); + + if (trimmed < ret) + drm_edid = drm_edid_alloc(edid, trimmed); + if (drm_edid && !drm_edid_valid(drm_edid)) { + drm_edid_free(drm_edid); + drm_edid = NULL; + } + } + } + kfree(edid); + + if (!drm_edid) + drm_dbg_kms(dev, "Invalid EDID from ACPI _DDC\n"); + return drm_edid; } bool nouveau_acpi_video_backlight_use_native(void) diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.h b/drivers/gpu/drm/nouveau/nouveau_acpi.h index e39dd8b94b8b..bc3ccca7b338 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.h +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.h @@ -4,13 +4,15 @@ #define ROM_BIOS_PAGE 4096 +struct drm_edid; + #if defined(CONFIG_ACPI) && defined(CONFIG_X86) bool nouveau_is_optimus(void); bool nouveau_is_v1_dsm(void); void nouveau_register_dsm_handler(void); void nouveau_unregister_dsm_handler(void); void nouveau_switcheroo_optimus_dsm(void); -void *nouveau_acpi_edid(struct drm_device *, struct drm_connector *); +const struct drm_edid *nouveau_acpi_edid(struct drm_device *, struct drm_connector *); bool nouveau_acpi_video_backlight_use_native(void); void nouveau_acpi_video_register_backlight(void); #else @@ -19,7 +21,11 @@ static inline bool nouveau_is_v1_dsm(void) { return false; }; static inline void nouveau_register_dsm_handler(void) {} static inline void nouveau_unregister_dsm_handler(void) {} static inline void nouveau_switcheroo_optimus_dsm(void) {} -static inline void *nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) { return NULL; } +static inline const struct drm_edid * +nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) +{ + return NULL; +} static inline bool nouveau_acpi_video_backlight_use_native(void) { return true; } static inline void nouveau_acpi_video_register_backlight(void) {} #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index b0b0ad9a0c24..cc3caf8de64b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -397,7 +397,8 @@ nouveau_connector_destroy(struct drm_connector *connector) struct nouveau_connector *nv_connector = nouveau_connector(connector); nvif_event_dtor(&nv_connector->irq); nvif_event_dtor(&nv_connector->hpd); - kfree(nv_connector->edid); + cancel_work_sync(&nv_connector->irq_work); + drm_edid_free(nv_connector->drm_edid); drm_connector_unregister(connector); drm_connector_cleanup(connector); if (nv_connector->aux.transfer) @@ -469,6 +470,36 @@ nouveau_connector_ddc_detect(struct drm_connector *connector) return found; } +static void +nouveau_connector_set_edid(struct nouveau_connector *nv_connector, + const struct drm_edid *drm_edid) +{ + if (nv_connector->drm_edid == drm_edid) + return; + + /* Updates the EDID property and display_info with HF-EEODB-aware + * sizing. The legacy helpers truncate both to what EDID byte 126 + * admits, hiding the DisplayID extension blocks that carry the + * high-refresh timings. + */ + drm_edid_connector_update(&nv_connector->base, drm_edid); + + drm_edid_free(nv_connector->drm_edid); + nv_connector->drm_edid = drm_edid; + + /* The SPWG link-count byte lives in a vendor descriptor drm has no + * accessor for. Peek at it once here so nothing else needs the raw + * EDID. + */ + nv_connector->spwg_links = 0; + if (nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { + const u8 *raw = (const u8 *)drm_edid_raw(drm_edid); + + if (raw) + nv_connector->spwg_links = raw[121] == 2 ? 2 : 1; + } +} + static struct nouveau_encoder * nouveau_connector_of_detect(struct drm_connector *connector) { @@ -490,8 +521,17 @@ nouveau_connector_of_detect(struct drm_connector *connector) int idx = name ? name[strlen(name) - 1] - 'A' : 0; if (nv_encoder->dcb->i2c_index == idx && edid) { - nv_connector->edid = - kmemdup(edid, EDID_LENGTH, GFP_KERNEL); + const struct drm_edid *drm_edid = + drm_edid_alloc(edid, EDID_LENGTH); + + /* Firmware-provided, so validate it like the DDC + * readers would. + */ + if (drm_edid && !drm_edid_valid(drm_edid)) { + drm_edid_free(drm_edid); + drm_edid = NULL; + } + nouveau_connector_set_edid(nv_connector, drm_edid); return nv_encoder; } } @@ -546,17 +586,23 @@ nouveau_connector_set_encoder(struct drm_connector *connector, } } -static void -nouveau_connector_set_edid(struct nouveau_connector *nv_connector, - struct edid *edid) +struct nouveau_rm_edid { + u8 *data; + size_t size; +}; + +static int +nouveau_connector_rm_edid_block(void *context, u8 *buf, unsigned int block, + size_t len) { - if (nv_connector->edid != edid) { - struct edid *old_edid = nv_connector->edid; + struct nouveau_rm_edid *rm = context; + size_t offset = (size_t)block * EDID_LENGTH; - drm_connector_update_edid_property(&nv_connector->base, edid); - kfree(old_edid); - nv_connector->edid = edid; - } + if (offset + len > rm->size) + return -EINVAL; + + memcpy(buf, rm->data + offset, len); + return 0; } static enum drm_connector_status @@ -590,22 +636,37 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) nv_encoder = nouveau_connector_ddc_detect(connector); if (nv_encoder) { - struct edid *new_edid = NULL; + const struct drm_edid *new_edid = NULL; if (nv_encoder->i2c) { if ((vga_switcheroo_handler_flags() & VGA_SWITCHEROO_CAN_SWITCH_DDC) && nv_connector->type == DCB_CONNECTOR_LVDS) - new_edid = drm_get_edid_switcheroo(connector, nv_encoder->i2c); + new_edid = drm_edid_read_switcheroo(connector, nv_encoder->i2c); else - new_edid = drm_get_edid(connector, nv_encoder->i2c); + new_edid = drm_edid_read_ddc(connector, nv_encoder->i2c); } else { - ret = nvif_outp_edid_get(&nv_encoder->outp, (u8 **)&new_edid); - if (ret < 0) - return connector_status_disconnected; + struct nouveau_rm_edid rm = {}; + + /* RM (which owns the DDC pads on GSP boards) reads the + * EDID whole and returns its true size, which for an + * HF-EEODB EDID exceeds what byte 126 admits. Serve it + * through drm's block reader so EEODB sizing, block + * validation, and the debugfs EDID override all apply. + * A failed read is treated like an empty DDC read, + * which releases the runtime-PM reference. + */ + ret = nvif_outp_edid_get(&nv_encoder->outp, &rm.data); + if (ret >= 0) { + rm.size = ret; + new_edid = drm_edid_read_custom(connector, + nouveau_connector_rm_edid_block, + &rm); + kfree(rm.data); + } } nouveau_connector_set_edid(nv_connector, new_edid); - if (!nv_connector->edid) { + if (!nv_connector->drm_edid) { NV_ERROR(drm, "DDC responded, but no EDID for %s\n", connector->name); goto detect_analog; @@ -626,7 +687,7 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) nv_partner->dcb->type == DCB_OUTPUT_TMDS) || (nv_encoder->dcb->type == DCB_OUTPUT_TMDS && nv_partner->dcb->type == DCB_OUTPUT_ANALOG))) { - if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL) + if (drm_edid_is_digital(nv_connector->drm_edid)) type = DCB_OUTPUT_TMDS; else type = DCB_OUTPUT_ANALOG; @@ -638,7 +699,8 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) conn_status = connector_status_connected; if (nv_encoder->dcb->type == DCB_OUTPUT_DP) - drm_dp_cec_set_edid(&nv_connector->aux, nv_connector->edid); + drm_dp_cec_attach(&nv_connector->aux, + connector->display_info.source_physical_address); goto out; } else { @@ -670,7 +732,7 @@ detect_analog: } out: - if (!nv_connector->edid) + if (!nv_connector->drm_edid) drm_dp_cec_unset_edid(&nv_connector->aux); pm_runtime_mark_last_busy(dev->dev); @@ -686,7 +748,7 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force) struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = NULL; - struct edid *edid = NULL; + const struct drm_edid *edid = NULL; enum drm_connector_status status = connector_status_disconnected; nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS); @@ -697,7 +759,7 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force) if (!drm->vbios.fp_no_ddc) { status = nouveau_connector_detect(connector, force); if (status == connector_status_connected) { - edid = nv_connector->edid; + edid = nv_connector->drm_edid; goto out; } } @@ -733,11 +795,20 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force) * stored for the panel stored in them. */ if (!drm->vbios.fp_no_ddc) { - edid = (struct edid *)nouveau_bios_embedded_edid(dev); - if (edid) { - edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL); + const void *embedded = nouveau_bios_embedded_edid(dev); + + if (embedded) { + edid = drm_edid_alloc(embedded, EDID_LENGTH); + /* The panel is there either way, so report it + * connected and let the probe helper fall back to a + * default mode if the EDID itself is unusable. + */ if (edid) status = connector_status_connected; + if (edid && !drm_edid_valid(edid)) { + drm_edid_free(edid); + edid = NULL; + } } } @@ -886,7 +957,7 @@ nouveau_connector_detect_depth(struct drm_connector *connector) bool duallink; /* if the edid is feeling nice enough to provide this info, use it */ - if (nv_connector->edid && connector->display_info.bpc) + if (nv_connector->drm_edid && connector->display_info.bpc) return; /* EDID 1.4 is *supposed* to be supported on eDP, but, Apple... */ @@ -913,9 +984,8 @@ nouveau_connector_detect_depth(struct drm_connector *connector) /* LVDS: DDC panel, need to first determine the number of links to * know which if_is_24bit flag to check... */ - if (nv_connector->edid && - nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) - duallink = ((u8 *)nv_connector->edid)[121] == 2; + if (nv_connector->spwg_links) + duallink = nv_connector->spwg_links == 2; else duallink = mode->clock >= bios->fp.duallink_transition_clk; @@ -973,12 +1043,17 @@ nouveau_connector_get_modes(struct drm_connector *connector) nv_connector->native_mode = NULL; } - if (nv_connector->edid) - ret = drm_add_edid_modes(connector, nv_connector->edid); - else - if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS && - (nv_encoder->dcb->lvdsconf.use_straps_for_mode || - drm->vbios.fp_no_ddc) && nouveau_bios_fp_mode(dev, NULL)) { + if (nv_connector->drm_edid) { + /* The probe helper clears the property and display_info for + * a forced-off connector without calling detect(). Re-sync + * from our copy then, since add_modes() reads the property. + */ + if (!connector->edid_blob_ptr) + drm_edid_connector_update(connector, nv_connector->drm_edid); + ret = drm_edid_connector_add_modes(connector); + } else if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS && + (nv_encoder->dcb->lvdsconf.use_straps_for_mode || + drm->vbios.fp_no_ddc) && nouveau_bios_fp_mode(dev, NULL)) { struct drm_display_mode mode; nouveau_bios_fp_mode(dev, &mode); diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 0608cabed058..eb292d2ba4bc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -43,7 +43,7 @@ struct nvkm_i2c_port; struct dcb_output; -struct edid; +struct drm_edid; #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT struct nouveau_backlight { @@ -121,6 +121,13 @@ struct nouveau_connector { struct drm_connector base; enum dcb_connector_type type; u8 index; + /* LVDS_SPWG panels state their link count in EDID descriptor 4 (SPWG + * byte 0x79), cached by nouveau_connector_set_edid() so nothing else + * needs the raw EDID. 0 = unknown (not an SPWG panel, or no EDID) and + * callers fall back to their transition-clock/VBIOS heuristics; + * 1 = single link; 2 = dual link. + */ + u8 spwg_links; struct nvif_conn conn; u64 hpd_pending; @@ -137,7 +144,8 @@ struct nouveau_connector { int scaling_mode; struct nouveau_encoder *detected_encoder; - struct edid *edid; + /* Owner of the sink's EDID, HF-EEODB-complete. */ + const struct drm_edid *drm_edid; struct drm_display_mode *native_mode; #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT struct nouveau_backlight *backlight; diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 55691ec44aba..738802358d85 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -486,7 +486,7 @@ nouveau_dp_irq(struct work_struct *work) container_of(work, typeof(*nv_connector), irq_work); struct drm_connector *connector = &nv_connector->base; struct nouveau_encoder *outp = find_encoder(connector, DCB_OUTPUT_DP); - struct nouveau_drm *drm = nouveau_drm(outp->base.base.dev); + struct nouveau_drm *drm; struct nv50_mstm *mstm; u64 hpd = 0; int ret; @@ -494,6 +494,8 @@ nouveau_dp_irq(struct work_struct *work) if (!outp) return; + drm = nouveau_drm(outp->base.base.dev); + mstm = outp->dp.mstm; NV_DEBUG(drm, "service %s\n", connector->name); diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 4d1ad718e09b..b0f9fb10a74d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -103,17 +103,17 @@ MODULE_PARM_DESC(modeset, "enable driver (default: auto, " int nouveau_modeset = -1; module_param_named(modeset, nouveau_modeset, int, 0400); -MODULE_PARM_DESC(atomic, "Expose atomic ioctl (default: disabled)"); -static int nouveau_atomic = 0; +MODULE_PARM_DESC(atomic, "Expose atomic ioctl (default: auto, " + "0 = disabled, 1 = enabled)"); +static int nouveau_atomic = -1; module_param_named(atomic, nouveau_atomic, int, 0400); MODULE_PARM_DESC(runpm, "disable (0), force enable (1), optimus only default (-1)"); static int nouveau_runtime_pm = -1; module_param_named(runpm, nouveau_runtime_pm, int, 0400); -static struct drm_driver driver_stub; -static struct drm_driver driver_pci; -static struct drm_driver driver_platform; +static const struct drm_driver driver_legacy_kms; +static const struct drm_driver driver_atomic_kms; #ifdef CONFIG_DEBUG_FS struct dentry *nouveau_debugfs_root; @@ -727,8 +727,7 @@ nouveau_drm_device_del(struct nouveau_drm *drm) } static struct nouveau_drm * -nouveau_drm_device_new(const struct drm_driver *drm_driver, struct device *parent, - struct nvkm_device *device) +nouveau_drm_device_new(struct device *parent, struct nvkm_device *device) { static const struct nvif_mclass mmus[] = { @@ -737,19 +736,32 @@ nouveau_drm_device_new(const struct drm_driver *drm_driver, struct device *paren { NVIF_CLASS_MMU_NV04 , -1 }, {} }; + const struct drm_driver *driver; struct nouveau_drm *drm; int ret; + if (device->card_type >= NV_50) { + if (nouveau_atomic) + driver = &driver_atomic_kms; + else + driver = &driver_legacy_kms; + } else { + if (nouveau_atomic == 1) + dev_warn(parent, "Atomic modesetting not supported (needs nv50+)\n"); + + driver = &driver_legacy_kms; + } + drm = kzalloc_obj(*drm); if (!drm) return ERR_PTR(-ENOMEM); drm->nvkm = device; - - drm->dev = drm_dev_alloc(drm_driver, parent); + drm->dev = drm_dev_alloc(driver, parent); if (IS_ERR(drm->dev)) { ret = PTR_ERR(drm->dev); - goto done; + kfree(drm); + return ERR_PTR(ret); } drm->dev->dev_private = drm; @@ -873,16 +885,13 @@ static int nouveau_drm_probe(struct pci_dev *pdev, return ret; /* Remove conflicting drivers (vesafb, efifb etc). */ - ret = aperture_remove_conflicting_pci_devices(pdev, driver_pci.name); + ret = aperture_remove_conflicting_pci_devices(pdev, DRIVER_NAME); if (ret) goto fail_nvkm; pci_set_master(pdev); - if (nouveau_atomic) - driver_pci.driver_features |= DRIVER_ATOMIC; - - drm = nouveau_drm_device_new(&driver_pci, &pdev->dev, device); + drm = nouveau_drm_device_new(&pdev->dev, device); if (IS_ERR(drm)) { ret = PTR_ERR(drm); goto fail_nvkm; @@ -1359,35 +1368,54 @@ nouveau_driver_fops = { .fop_flags = FOP_UNSIGNED_OFFSET, }; -static struct drm_driver -driver_stub = { - .driver_features = DRIVER_GEM | - DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE | - DRIVER_MODESET | - DRIVER_RENDER, - .open = nouveau_drm_open, - .postclose = nouveau_drm_postclose, - -#if defined(CONFIG_DEBUG_FS) - .debugfs_init = nouveau_drm_debugfs_init, +#ifdef CONFIG_DEBUG_FS +#define NOUVEAU_DEBUGFS_OPS .debugfs_init = nouveau_drm_debugfs_init, +#else +#define NOUVEAU_DEBUGFS_OPS #endif - .ioctls = nouveau_ioctls, - .num_ioctls = ARRAY_SIZE(nouveau_ioctls), - .fops = &nouveau_driver_fops, - - .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table, - - .dumb_create = nouveau_display_dumb_create, - .dumb_map_offset = drm_gem_ttm_dumb_map_offset, - - DRM_FBDEV_TTM_DRIVER_OPS, +#define NOUVEAU_DRIVER_OPS \ + .open = nouveau_drm_open, \ + .postclose = nouveau_drm_postclose, \ + \ + NOUVEAU_DEBUGFS_OPS \ + \ + .ioctls = nouveau_ioctls, \ + .num_ioctls = ARRAY_SIZE(nouveau_ioctls), \ + .fops = &nouveau_driver_fops, \ + \ + .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table, \ + \ + .dumb_create = nouveau_display_dumb_create, \ + .dumb_map_offset = drm_gem_ttm_dumb_map_offset, \ + \ + DRM_FBDEV_TTM_DRIVER_OPS, \ + \ + .name = DRIVER_NAME, \ + .desc = DRIVER_DESC, \ + .major = DRIVER_MAJOR, \ + .minor = DRIVER_MINOR, \ + .patchlevel = DRIVER_PATCHLEVEL + +static const struct drm_driver +driver_legacy_kms = { + .driver_features = DRIVER_GEM + | DRIVER_SYNCOBJ + | DRIVER_SYNCOBJ_TIMELINE + | DRIVER_MODESET + | DRIVER_RENDER, + NOUVEAU_DRIVER_OPS, +}; - .name = DRIVER_NAME, - .desc = DRIVER_DESC, - .major = DRIVER_MAJOR, - .minor = DRIVER_MINOR, - .patchlevel = DRIVER_PATCHLEVEL, +static const struct drm_driver +driver_atomic_kms = { + .driver_features = DRIVER_GEM + | DRIVER_SYNCOBJ + | DRIVER_SYNCOBJ_TIMELINE + | DRIVER_MODESET + | DRIVER_RENDER + | DRIVER_ATOMIC, + NOUVEAU_DRIVER_OPS, }; static struct pci_device_id @@ -1416,6 +1444,7 @@ static void nouveau_display_options(void) DRM_DEBUG_DRIVER("... debug : %s\n", nouveau_debug); DRM_DEBUG_DRIVER("... noaccel : %d\n", nouveau_noaccel); DRM_DEBUG_DRIVER("... modeset : %d\n", nouveau_modeset); + DRM_DEBUG_DRIVER("... atomic : %d\n", nouveau_atomic); DRM_DEBUG_DRIVER("... runpm : %d\n", nouveau_runtime_pm); DRM_DEBUG_DRIVER("... vram_pushbuf : %d\n", nouveau_vram_pushbuf); DRM_DEBUG_DRIVER("... hdmimhz : %d\n", nouveau_hdmimhz); @@ -1455,7 +1484,7 @@ nouveau_platform_device_create(const struct nvkm_device_tegra_func *func, if (err) goto err_free; - drm = nouveau_drm_device_new(&driver_platform, &pdev->dev, *pdevice); + drm = nouveau_drm_device_new(&pdev->dev, *pdevice); if (IS_ERR(drm)) { err = PTR_ERR(drm); goto err_free; @@ -1480,9 +1509,6 @@ nouveau_drm_init(void) { int ret; - driver_pci = driver_stub; - driver_platform = driver_stub; - nouveau_display_options(); if (nouveau_modeset == -1) { diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index edbe9e08ba0f..11e95c37ce50 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -96,7 +96,7 @@ nouveau_fence_context_kill(struct nouveau_fence_chan *fctx, int error) void nouveau_fence_context_del(struct nouveau_fence_chan *fctx) { - cancel_work_sync(&fctx->uevent_work); + disable_work_sync(&fctx->uevent_work); nouveau_fence_context_kill(fctx, 0); nvif_event_dtor(&fctx->event); fctx->dead = 1; diff --git a/drivers/gpu/drm/nouveau/nouveau_svm.c b/drivers/gpu/drm/nouveau/nouveau_svm.c index 58735446d783..c519c28164fa 100644 --- a/drivers/gpu/drm/nouveau/nouveau_svm.c +++ b/drivers/gpu/drm/nouveau/nouveau_svm.c @@ -304,6 +304,19 @@ nouveau_svmm_fini(struct nouveau_svmm **psvmm) { struct nouveau_svmm *svmm = *psvmm; if (svmm) { + struct nouveau_svm *svm = svmm->vmm->cli->drm->svm; + + /* The fault handler caches svmm pointers under svm->mutex and + * dereferences them after dropping it, across blocking faults. + * The instance is already unlinked (nouveau_svmm_part), so drain + * the handler before the free to release any in-flight reference. + * + * On device teardown nouveau_svm_fini() ran first and freed + * drm->svm (NULL) after draining the buffer; nothing to flush. + */ + if (svm) + flush_work(&svm->buffer[0].work); + mutex_lock(&svmm->mutex); svmm->vmm = NULL; mutex_unlock(&svmm->mutex); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index 96c8a5b29999..a2ed8c4fe52a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -1237,7 +1237,7 @@ nvac_chipset = { .mc = { 0x00000001, g98_mc_new }, .mmu = { 0x00000001, mcp77_mmu_new }, .mxm = { 0x00000001, nv50_mxm_new }, - .pci = { 0x00000001, g94_pci_new }, + .pci = { 0x00000001, mcp79_pci_new }, .therm = { 0x00000001, g84_therm_new }, .timer = { 0x00000001, nv41_timer_new }, .volt = { 0x00000001, nv40_volt_new }, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h index ea5b0bab4cce..7a93f9f40715 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h @@ -27,7 +27,7 @@ #define KHZ (1000) #define MHZ (KHZ * 1000) -#define MASK(w) ((1 << (w)) - 1) +#define MASK(w) ((1U << (w)) - 1) #define GK20A_CLK_GPC_MDIV 1000 @@ -79,7 +79,7 @@ #define GPC2CLK_OUT (SYS_GPCPLL_CFG_BASE + 0x250) #define GPC2CLK_OUT_SDIV14_INDIV4_WIDTH 1 #define GPC2CLK_OUT_SDIV14_INDIV4_SHIFT 31 -#define GPC2CLK_OUT_SDIV14_INDIV4_MODE 1 +#define GPC2CLK_OUT_SDIV14_INDIV4_MODE 1U #define GPC2CLK_OUT_VCODIV_WIDTH 6 #define GPC2CLK_OUT_VCODIV_SHIFT 8 #define GPC2CLK_OUT_VCODIV1 0 diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/fbsr.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/fbsr.c index f128330f30d7..40bf83ea33ac 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/fbsr.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/fbsr.c @@ -208,7 +208,7 @@ r535_fbsr_resume(struct nvkm_gsp *gsp) } static int -r535_fbsr_suspend(struct nvkm_gsp *gsp, bool runtime) +r535_fbsr_suspend(struct nvkm_gsp *gsp) { struct nvkm_subdev *subdev = &gsp->subdev; struct nvkm_device *device = subdev->device; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gsp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gsp.c index f544afa12b6b..94925f1590ea 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gsp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gsp.c @@ -1749,7 +1749,7 @@ r535_gsp_fini(struct nvkm_gsp *gsp, enum nvkm_suspend_state suspend) sr->sysmemAddrOfSuspendResumeData = gsp->sr.radix3.lvl0.addr; sr->sizeOfSuspendResumeData = len; - ret = rm->api->fbsr->suspend(gsp, suspend == NVKM_RUNTIME_SUSPEND); + ret = rm->api->fbsr->suspend(gsp); if (ret) { nvkm_gsp_mem_dtor(&gsp->sr.meta); nvkm_gsp_radix3_dtor(gsp, &gsp->sr.radix3); @@ -1761,8 +1761,12 @@ r535_gsp_fini(struct nvkm_gsp *gsp, enum nvkm_suspend_state suspend) * TODO: Debug the GSP firmware / RPC handling to find out why * without this Turing (but none of the other architectures) * ends up resetting all channels after resume. + * Additionally, runtime suspend on other architectures quickly + * becomes unreliable without this sleep. If you're experiencing + * issues with runtime suspend, try bumping this delay up and + * sending a patch if it fixes your GPU. */ - msleep(50); + msleep(200); } ret = r535_gsp_rpc_unloading_guest_driver(gsp, suspend); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/fbsr.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/fbsr.c index 8ef8b4f65588..af5aa5065c3d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/fbsr.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/fbsr.c @@ -62,7 +62,7 @@ r570_fbsr_resume(struct nvkm_gsp *gsp) } static int -r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size, bool runtime) +r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size) { NV2080_CTRL_INTERNAL_FBSR_INIT_PARAMS *ctrl; struct nvkm_gsp_object memlist; @@ -81,7 +81,7 @@ r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size, bool runtim ctrl->hClient = gsp->internal.client.object.handle; ctrl->hSysMem = memlist.handle; ctrl->sysmemAddrOfSuspendResumeData = gsp->sr.meta.addr; - ctrl->bEnteringGcoffState = runtime ? 1 : 0; + ctrl->bEnteringGcoffState = 0; ret = nvkm_gsp_rm_ctrl_wr(&gsp->internal.device.subdevice, ctrl); if (ret) @@ -92,7 +92,7 @@ r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size, bool runtim } static int -r570_fbsr_suspend(struct nvkm_gsp *gsp, bool runtime) +r570_fbsr_suspend(struct nvkm_gsp *gsp) { struct nvkm_subdev *subdev = &gsp->subdev; struct nvkm_device *device = subdev->device; @@ -133,7 +133,7 @@ r570_fbsr_suspend(struct nvkm_gsp *gsp, bool runtime) return ret; /* Initialise FBSR on RM. */ - ret = r570_fbsr_init(gsp, &gsp->sr.fbsr, size, runtime); + ret = r570_fbsr_init(gsp, &gsp->sr.fbsr, size); if (ret) { nvkm_gsp_sg_free(device, &gsp->sr.fbsr); return ret; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c index 1488771c63fc..b45781cd0dfd 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c @@ -207,7 +207,8 @@ r570_gsp_set_rmargs(struct nvkm_gsp *gsp, bool resume) args->srInitArguments.bInPMTransition = 0; } else { args->srInitArguments.oldLevel = NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3; - args->srInitArguments.flags = 0; + args->srInitArguments.flags = + GPU_STATE_FLAGS_PRESERVING | GPU_STATE_FLAGS_PM_TRANSITION; args->srInitArguments.bInPMTransition = 1; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/gsp.h b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/gsp.h index b6075021e74f..c458569af9d7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/gsp.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/gsp.h @@ -523,6 +523,14 @@ typedef struct #define NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3 (0x00000003U) +#define GPU_STATE_FLAGS_PRESERVING BIT(0) // GPU state is preserved +#define GPU_STATE_FLAGS_VGA_TRANSITION BIT(1) // To be used with GPU_STATE_FLAGS_PRESERVING. +#define GPU_STATE_FLAGS_PM_TRANSITION BIT(2) // To be used with GPU_STATE_FLAGS_PRESERVING. +#define GPU_STATE_FLAGS_PM_SUSPEND BIT(3) +#define GPU_STATE_FLAGS_PM_HIBERNATE BIT(4) +#define GPU_STATE_FLAGS_GC6_TRANSITION BIT(5) // To be used with GPU_STATE_FLAGS_PRESERVING. +#define GPU_STATE_FLAGS_FAST_UNLOAD BIT(6) // Used during windows restart, skips stateDestroy steps + typedef struct { // Magic for verification by secure ucode diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h index fcd0221dcea1..e9ac47d86b69 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h @@ -79,7 +79,7 @@ struct nvkm_rm_api { } *device; const struct nvkm_rm_api_fbsr { - int (*suspend)(struct nvkm_gsp *, bool runtime); + int (*suspend)(struct nvkm_gsp *); void (*resume)(struct nvkm_gsp *); } *fbsr; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild index a14ea0f7b1c8..90f03baeb22a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild @@ -9,6 +9,7 @@ nvkm-y += nvkm/subdev/pci/nv4c.o nvkm-y += nvkm/subdev/pci/g84.o nvkm-y += nvkm/subdev/pci/g92.o nvkm-y += nvkm/subdev/pci/g94.o +nvkm-y += nvkm/subdev/pci/mcp79.o nvkm-y += nvkm/subdev/pci/gf100.o nvkm-y += nvkm/subdev/pci/gf106.o nvkm-y += nvkm/subdev/pci/gk104.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/mcp79.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/mcp79.c new file mode 100644 index 000000000000..e2ae242f1aa7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/mcp79.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2026 Marek Czernohous + * + * MCP79/MCP7A (NVAC): like g94, but MSI re-arm goes through real PCI + * config space. The MMIO-mirror re-arm is unreliable on this IGP and a + * missed re-arm kills the interrupt line (see the nv46 comment; g92 + * already re-arms through config space for the same reason). + */ +#include "priv.h" + +static const struct nvkm_pci_func +mcp79_pci_func = { + .cfg = { .addr = 0x088000, .size = 0x1000 }, + + .init = g84_pci_init, + .msi_rearm = nv46_pci_msi_rearm, + + .pcie.init = g84_pcie_init, + .pcie.set_link = g84_pcie_set_link, + + .pcie.max_speed = g84_pcie_max_speed, + .pcie.cur_speed = g84_pcie_cur_speed, + + .pcie.set_version = g84_pcie_set_version, + .pcie.version = g84_pcie_version, + .pcie.version_supported = g92_pcie_version_supported, +}; + +int +mcp79_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&mcp79_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index 6da720e8732d..a57f5df5c830 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -59,9 +59,6 @@ static void dsi_uninit_dispc(struct dsi_data *dsi); static int dsi_vc_send_null(struct dsi_data *dsi, int vc, int channel); -static ssize_t _omap_dsi_host_transfer(struct dsi_data *dsi, int vc, - const struct mipi_dsi_msg *msg); - #ifdef DSI_PERF_MEASURE static bool dsi_perf; module_param(dsi_perf, bool, 0644); @@ -2190,28 +2187,45 @@ static int dsi_vc_send_null(struct dsi_data *dsi, int vc, int channel) static int dsi_vc_write_common(struct omap_dss_device *dssdev, int vc, const struct mipi_dsi_msg *msg) { + DECLARE_COMPLETION_ONSTACK(completion); struct dsi_data *dsi = to_dsi_data(dssdev); + u32 err; int r; + /* wait for IRQ for packet transmission confirmation */ + r = dsi_register_isr_vc(dsi, vc, dsi_completion_handler, + &completion, DSI_VC_IRQ_PACKET_SENT); + if (r) + return r; + if (mipi_dsi_packet_format_is_short(msg->type)) r = dsi_vc_send_short(dsi, vc, msg); else r = dsi_vc_send_long(dsi, vc, msg); - if (r < 0) + if ((!r) && wait_for_completion_timeout(&completion, + msecs_to_jiffies(500)) == 0) + r = -EIO; + + dsi_unregister_isr_vc(dsi, vc, dsi_completion_handler, + &completion, DSI_VC_IRQ_PACKET_SENT); + if (r) return r; - /* - * TODO: we do not always have to do the BTA sync, for example - * we can improve performance by setting the update window - * information without sending BTA sync between the commands. - * In that case we can return early. - */ + /* TODO: find out if more needs to be done for MIPI_DSI_MSG_REQ_ACK */ - r = dsi_vc_send_bta_sync(dssdev, vc); - if (r) { - DSSERR("bta sync failed\n"); - return r; + if (msg->flags & MIPI_DSI_MSG_REQ_ACK) { + r = dsi_vc_send_bta_sync(dssdev, vc); + if (r) { + DSSERR("bta sync failed\n"); + return r; + } + } else { + err = dsi_get_errors(dsi); + if (err) { + DSSERR("Error while sending: %x\n", err); + return -EIO; + } } /* RX_FIFO_NOT_EMPTY */ @@ -3229,21 +3243,6 @@ static int _dsi_update(struct dsi_data *dsi) return 0; } -static int _dsi_send_nop(struct dsi_data *dsi, int vc, int channel) -{ - const u8 payload[] = { MIPI_DCS_NOP }; - const struct mipi_dsi_msg msg = { - .channel = channel, - .type = MIPI_DSI_DCS_SHORT_WRITE, - .tx_len = 1, - .tx_buf = payload, - }; - - WARN_ON(!dsi_bus_is_locked(dsi)); - - return _omap_dsi_host_transfer(dsi, vc, &msg); -} - static int dsi_update_channel(struct omap_dss_device *dssdev, int vc) { struct dsi_data *dsi = to_dsi_data(dssdev); @@ -3264,13 +3263,13 @@ static int dsi_update_channel(struct omap_dss_device *dssdev, int vc) DSSDBG("dsi_update_channel: %d", vc); /* - * Send NOP between the frames. If we don't send something here, the + * Transition to LP here. If we don't send something here, the * updates stop working. This is probably related to DSI spec stating * that the DSI host should transition to LP at least once per frame. */ - r = _dsi_send_nop(dsi, VC_CMD, dsi->dsidev->channel); + r = dsi_vc_send_bta_sync(dssdev, VC_CMD); if (r < 0) { - DSSWARN("failed to send nop between frames: %d\n", r); + DSSWARN("failed to send bta sync between frames: %d\n", r); goto err; } diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi.h b/drivers/gpu/drm/omapdrm/dss/hdmi.h index c4a4e07f0b99..a79d4590611f 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi.h +++ b/drivers/gpu/drm/omapdrm/dss/hdmi.h @@ -15,6 +15,7 @@ #include <sound/omap-hdmi-audio.h> #include <media/cec.h> #include <drm/drm_bridge.h> +#include <drm/drm_connector.h> #include "omapdss.h" #include "dss.h" @@ -368,6 +369,8 @@ struct omap_hdmi { struct drm_bridge bridge; struct platform_device *audio_pdev; + struct mutex audio_lock; + void (*audio_abort_cb)(struct device *dev); int wp_idlemode; @@ -380,6 +383,9 @@ struct omap_hdmi { bool display_enabled; }; +void hdmi_audio_hpd_notify(struct omap_hdmi *hdmi, + enum drm_connector_status status); + #define drm_bridge_to_hdmi(b) container_of(b, struct omap_hdmi, bridge) #endif diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index 61cfd003569c..bd6c4d2dcdc8 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -433,8 +433,15 @@ static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge, { struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); - if (status == connector_status_disconnected) + hdmi_audio_hpd_notify(hdmi, status); + + if (status == connector_status_disconnected) { + if (hdmi_runtime_get(hdmi)) + return; + hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); + hdmi_runtime_put(hdmi); + } } static const struct drm_edid *hdmi4_bridge_edid_read(struct drm_bridge *bridge, @@ -626,12 +633,17 @@ static int hdmi_audio_register(struct omap_hdmi *hdmi) .ops = &hdmi_audio_ops, }; + guard(mutex)(&hdmi->audio_lock); hdmi->audio_pdev = platform_device_register_data( &hdmi->pdev->dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO, &pdata, sizeof(pdata)); - if (IS_ERR(hdmi->audio_pdev)) - return PTR_ERR(hdmi->audio_pdev); + if (IS_ERR(hdmi->audio_pdev)) { + int err = PTR_ERR(hdmi->audio_pdev); + + hdmi->audio_pdev = NULL; + return err; + } return 0; } @@ -688,8 +700,14 @@ static void hdmi4_unbind(struct device *dev, struct device *master, void *data) dss_debugfs_remove_file(hdmi->debugfs); - if (hdmi->audio_pdev) - platform_device_unregister(hdmi->audio_pdev); + scoped_guard(mutex, &hdmi->audio_lock) { + if (hdmi->audio_pdev) { + struct platform_device *pdev = hdmi->audio_pdev; + + hdmi->audio_pdev = NULL; + platform_device_unregister(pdev); + } + } hdmi4_cec_uninit(&hdmi->core); hdmi_pll_uninit(&hdmi->pll); @@ -770,6 +788,7 @@ static int hdmi4_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, hdmi); mutex_init(&hdmi->lock); + mutex_init(&hdmi->audio_lock); spin_lock_init(&hdmi->audio_playing_lock); r = hdmi4_probe_of(hdmi); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index 1ee3bbe20583..c542ba6ce7d3 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -425,6 +425,15 @@ static void hdmi5_bridge_disable(struct drm_bridge *bridge, mutex_unlock(&hdmi->lock); } +static void hdmi5_bridge_hpd_notify(struct drm_bridge *bridge, + struct drm_connector *connector, + enum drm_connector_status status) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + + hdmi_audio_hpd_notify(hdmi, status); +} + static const struct drm_edid *hdmi5_bridge_edid_read(struct drm_bridge *bridge, struct drm_connector *connector) { @@ -475,6 +484,7 @@ static const struct drm_bridge_funcs hdmi5_bridge_funcs = { .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_enable = hdmi5_bridge_enable, .atomic_disable = hdmi5_bridge_disable, + .hpd_notify = hdmi5_bridge_hpd_notify, .edid_read = hdmi5_bridge_edid_read, }; @@ -601,12 +611,18 @@ static int hdmi_audio_register(struct omap_hdmi *hdmi) .ops = &hdmi_audio_ops, }; - hdmi->audio_pdev = platform_device_register_data( - &hdmi->pdev->dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO, - &pdata, sizeof(pdata)); + scoped_guard(mutex, &hdmi->audio_lock) { + hdmi->audio_pdev = platform_device_register_data( + &hdmi->pdev->dev, "omap-hdmi-audio", + PLATFORM_DEVID_AUTO, &pdata, sizeof(pdata)); - if (IS_ERR(hdmi->audio_pdev)) - return PTR_ERR(hdmi->audio_pdev); + if (IS_ERR(hdmi->audio_pdev)) { + int err = PTR_ERR(hdmi->audio_pdev); + + hdmi->audio_pdev = NULL; + return err; + } + } hdmi_runtime_get(hdmi); hdmi->wp_idlemode = @@ -654,8 +670,14 @@ static void hdmi5_unbind(struct device *dev, struct device *master, void *data) dss_debugfs_remove_file(hdmi->debugfs); - if (hdmi->audio_pdev) - platform_device_unregister(hdmi->audio_pdev); + scoped_guard(mutex, &hdmi->audio_lock) { + if (hdmi->audio_pdev) { + struct platform_device *pdev = hdmi->audio_pdev; + + hdmi->audio_pdev = NULL; + platform_device_unregister(pdev); + } + } hdmi_pll_uninit(&hdmi->pll); } @@ -735,6 +757,7 @@ static int hdmi5_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, hdmi); mutex_init(&hdmi->lock); + mutex_init(&hdmi->audio_lock); spin_lock_init(&hdmi->audio_playing_lock); r = hdmi5_probe_of(hdmi); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_common.c b/drivers/gpu/drm/omapdrm/dss/hdmi_common.c index 3ecde23ac604..e9adc5775913 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi_common.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi_common.c @@ -147,3 +147,17 @@ int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts) return 0; } + +void hdmi_audio_hpd_notify(struct omap_hdmi *hdmi, + enum drm_connector_status status) +{ + guard(mutex)(&hdmi->audio_lock); + + if (hdmi->audio_pdev) { + struct device *dev = &hdmi->audio_pdev->dev; + struct omap_hdmi_audio_pdata *ha = dev_get_platdata(dev); + + if (ha->audio_hpd) + ha->audio_hpd(dev, status == connector_status_connected); + } +} diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index c1b41e010a00..a9f6a76ebb5e 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -707,18 +707,17 @@ static int omap_crtc_atomic_get_property(struct drm_crtc *crtc, return 0; } -static void omap_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *omap_crtc_create_state(struct drm_crtc *crtc) { struct omap_crtc_state *state; - if (crtc->state) - __drm_atomic_helper_crtc_destroy_state(crtc->state); + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); - kfree(crtc->state); + __drm_atomic_helper_crtc_state_init(&state->base, crtc); - state = kzalloc_obj(*state); - if (state) - __drm_atomic_helper_crtc_reset(crtc, &state->base); + return &state->base; } static struct drm_crtc_state * @@ -745,7 +744,7 @@ omap_crtc_duplicate_state(struct drm_crtc *crtc) } static const struct drm_crtc_funcs omap_crtc_funcs = { - .reset = omap_crtc_reset, + .atomic_create_state = omap_crtc_create_state, .set_config = drm_atomic_helper_set_config, .destroy = omap_crtc_destroy, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index cbdf7b8f7f7a..747f47347521 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -298,6 +298,17 @@ config DRM_PANEL_ILITEK_ILI7807S If M is selected the module will be called panel-ilitek-ili7807s. +config DRM_PANEL_ILITEK_ILI7836A + tristate "Ilitek ILI7836A-based panels" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y if you want to enable support for panels based on the + Ilitek ILI7836A DDIC. + + If M is selected the module will be called panel-ilitek-ili7836a. + config DRM_PANEL_ILITEK_IL9322 tristate "Ilitek ILI9322 320x240 QVGA panels" depends on OF && SPI @@ -664,6 +675,20 @@ config DRM_PANEL_NOVATEK_NT36523 around the Novatek NT36523 display controller, such as some Boe panels used in Xiaomi Mi Pad 5 and 5 Pro tablets. +config DRM_PANEL_NOVATEK_NT36532 + tristate "Novatek NT36532-based MIPI-DSI panels" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_DSC_HELPER + select DRM_DISPLAY_HELPER + select DRM_KMS_HELPER + help + Say Y here if you want to enable support for the panels built + around the Novatek NT36532 display controller, such as some + CSOT panels used in OnePlus Pad 2 tablets. These panels are + typically dual-DSI and may use DSC (Display Stream Compression). + config DRM_PANEL_NOVATEK_NT36536 tristate "Novatek NT36536 panel driver" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 3b523cf37833..f2c9c80a218f 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_DRM_PANEL_HIMAX_HX83121A) += panel-himax-hx83121a.o obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o obj-$(CONFIG_DRM_PANEL_HYDIS_HV101HD1) += panel-hydis-hv101hd1.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI7807S) += panel-ilitek-ili7807s.o +obj-$(CONFIG_DRM_PANEL_ILITEK_ILI7836A) += panel-ilitek-ili7836a.o obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9488) += panel-ilitek-ili9488.o @@ -64,6 +65,7 @@ obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35532) += panel-novatek-nt35532.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35560) += panel-novatek-nt35560.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35950) += panel-novatek-nt35950.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36523) += panel-novatek-nt36523.o +obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36532) += panel-novatek-nt36532.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36536) += panel-novatek-nt36536.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672E) += panel-novatek-nt36672e.o diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 2b455369ed70..bfb49b647b96 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -2060,6 +2060,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('B', 'O', 'E', 0x0af2, &delay_200_500_e50_p2e80, "NE140QDM-NX2"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b09, &delay_200_500_e50_po2e200, "NV140FHM-NZ"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b1e, &delay_200_500_e80, "NE140QDM-N6A"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b21, &delay_200_500_e80, "QV116FHB-N81"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b34, &delay_200_500_e80_d50, "NV122WUM-N41"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b43, &delay_200_500_e200, "NV140FHM-T09"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b56, &delay_200_500_e80, "NT140FHM-N47"), @@ -2143,6 +2144,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('C', 'S', 'W', 0x146e, &delay_80_500_e50_d50, "MNE007QB3-1"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x147c, &delay_200_500_e50_d100, "MNE007QB3-1"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x1486, &delay_200_500_e50_d100, "MNE001BS6-2"), + EDP_PANEL_ENTRY('C', 'S', 'W', 0x1487, &delay_200_500_e50, "MNE007QS3-F"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x1519, &delay_200_500_e80_d50, "MNF601BS1-3"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x1529, &delay_200_500_e80_d50, "MNF307QS3-2"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x153f, &delay_200_500_e200_d50, "MNF601BS4-1"), @@ -2203,6 +2205,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('T', 'M', 'A', 0x0811, &delay_200_500_e80_d50, "TM140VDXP01-04"), EDP_PANEL_ENTRY('T', 'M', 'A', 0x2094, &delay_200_500_e50_d100, "TL140VDMS03-01"), + EDP_PANEL_ENTRY('T', 'M', 'A', 0x2137, &delay_200_500_e50, "TM140VDXP15"), EDP_PANEL_ENTRY('T', 'M', 'A', 0x2139, &delay_200_500_e80_pu100, "TM156VDXP25"), { /* sentinal */ } diff --git a/drivers/gpu/drm/panel/panel-himax-hx83102.c b/drivers/gpu/drm/panel/panel-himax-hx83102.c index 6a0851ccf9bb..3ef052571ce0 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx83102.c +++ b/drivers/gpu/drm/panel/panel-himax-hx83102.c @@ -1284,9 +1284,7 @@ static int hx83102_panel_add(struct hx83102 *ctx) ctx->base.funcs = &hx83102_drm_funcs; ctx->base.dev = &ctx->dsi->dev; - drm_panel_add(&ctx->base); - - return 0; + return devm_drm_panel_add(dev, &ctx->base); } static int hx83102_probe(struct mipi_dsi_device *dsi) @@ -1318,24 +1316,7 @@ static int hx83102_probe(struct mipi_dsi_device *dsi) mipi_dsi_set_drvdata(dsi, ctx); - ret = mipi_dsi_attach(dsi); - if (ret) - drm_panel_remove(&ctx->base); - - return ret; -} - -static void hx83102_remove(struct mipi_dsi_device *dsi) -{ - struct hx83102 *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); - - if (ctx->base.dev) - drm_panel_remove(&ctx->base); + return devm_mipi_dsi_attach(&dsi->dev, dsi); } static const struct of_device_id hx83102_of_match[] = { @@ -1369,7 +1350,6 @@ MODULE_DEVICE_TABLE(of, hx83102_of_match); static struct mipi_dsi_driver hx83102_driver = { .probe = hx83102_probe, - .remove = hx83102_remove, .driver = { .name = "panel-himax-hx83102", .of_match_table = hx83102_of_match, diff --git a/drivers/gpu/drm/panel/panel-himax-hx83112a.c b/drivers/gpu/drm/panel/panel-himax-hx83112a.c index 142cb1cc067a..36ebfad7a3e9 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx83112a.c +++ b/drivers/gpu/drm/panel/panel-himax-hx83112a.c @@ -303,28 +303,15 @@ static int hx83112a_probe(struct mipi_dsi_device *dsi) if (ret) return dev_err_probe(dev, ret, "Failed to get backlight\n"); - drm_panel_add(&ctx->panel); - - ret = mipi_dsi_attach(dsi); - if (ret < 0) { - dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); - drm_panel_remove(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) return ret; - } - return 0; -} - -static void hx83112a_remove(struct mipi_dsi_device *dsi) -{ - struct hx83112a_panel *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); - drm_panel_remove(&ctx->panel); + return 0; } static const struct of_device_id hx83112a_of_match[] = { @@ -335,7 +322,6 @@ MODULE_DEVICE_TABLE(of, hx83112a_of_match); static struct mipi_dsi_driver hx83112a_driver = { .probe = hx83112a_probe, - .remove = hx83112a_remove, .driver = { .name = "panel-himax-hx83112a", .of_match_table = hx83112a_of_match, diff --git a/drivers/gpu/drm/panel/panel-himax-hx83112b.c b/drivers/gpu/drm/panel/panel-himax-hx83112b.c index 41f21f8c1373..569ed2dec87c 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx83112b.c +++ b/drivers/gpu/drm/panel/panel-himax-hx83112b.c @@ -386,29 +386,17 @@ static int hx83112b_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), "Failed to create backlight\n"); - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); - if (ret < 0) { - drm_panel_remove(&ctx->panel); + ret = devm_mipi_dsi_attach(dev, dsi); + if (ret < 0) return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); - } return 0; } -static void hx83112b_remove(struct mipi_dsi_device *dsi) -{ - struct hx83112b_panel *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id hx83112b_of_match[] = { { .compatible = "djn,98-03057-6598b-i" }, { /* sentinel */ } @@ -417,7 +405,6 @@ MODULE_DEVICE_TABLE(of, hx83112b_of_match); static struct mipi_dsi_driver hx83112b_driver = { .probe = hx83112b_probe, - .remove = hx83112b_remove, .driver = { .name = "panel-himax-hx83112b", .of_match_table = hx83112b_of_match, diff --git a/drivers/gpu/drm/panel/panel-himax-hx8394.c b/drivers/gpu/drm/panel/panel-himax-hx8394.c index 09124610fdc8..4180d4aadf5e 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx8394.c +++ b/drivers/gpu/drm/panel/panel-himax-hx8394.c @@ -1023,14 +1023,13 @@ static int hx8394_probe(struct mipi_dsi_device *dsi) ctx->panel.prepare_prev_first = true; - drm_panel_add(&ctx->panel); - - ret = mipi_dsi_attach(dsi); - if (ret < 0) { - dev_err_probe(dev, ret, "mipi_dsi_attach failed\n"); - drm_panel_remove(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) return ret; - } + + ret = devm_mipi_dsi_attach(dev, dsi); + if (ret < 0) + return dev_err_probe(dev, ret, "mipi_dsi_attach failed\n"); dev_dbg(dev, "%ux%u@%u %ubpp dsi %udl - ready\n", ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay, @@ -1040,18 +1039,6 @@ static int hx8394_probe(struct mipi_dsi_device *dsi) return 0; } -static void hx8394_remove(struct mipi_dsi_device *dsi) -{ - struct hx8394 *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id hx8394_of_match[] = { { .compatible = "hannstar,hsd060bhw4", .data = &hsd060bhw4_desc }, { .compatible = "huiling,hl055fhav028c", .data = &hl055fhav028c_desc }, @@ -1065,7 +1052,6 @@ MODULE_DEVICE_TABLE(of, hx8394_of_match); static struct mipi_dsi_driver hx8394_driver = { .probe = hx8394_probe, - .remove = hx8394_remove, .driver = { .name = DRV_NAME, .of_match_table = hx8394_of_match, diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili7836a.c b/drivers/gpu/drm/panel/panel-ilitek-ili7836a.c new file mode 100644 index 000000000000..f6331129dadf --- /dev/null +++ b/drivers/gpu/drm/panel/panel-ilitek-ili7836a.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h> +#include <drm/drm_probe_helper.h> + +#include <video/mipi_display.h> + +static const struct regulator_bulk_data ili7836a_supplies[] = { + { .supply = "vddio", }, + { .supply = "avdd", }, +}; + +struct ili7836a_panel { + struct drm_panel panel; + struct drm_connector *connector; + struct mipi_dsi_device *dsi; + struct regulator_bulk_data *supplies; + struct gpio_desc *reset_gpio; + struct ili7836a_desc *desc; + enum drm_panel_orientation orientation; +}; + +struct ili7836a_desc { + unsigned int width_mm; + unsigned int height_mm; + unsigned int bpc; + + const struct drm_display_mode *modes; + unsigned int num_modes; +}; + +static inline struct ili7836a_panel *to_ili7836a_panel(struct drm_panel *panel) +{ + return container_of(panel, struct ili7836a_panel, panel); +} + +static void ili7836a_reset(struct ili7836a_panel *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + msleep(20); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + msleep(20); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + msleep(20); +} + +static int ili7836a_on(struct ili7836a_panel *ctx) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; + + ctx->dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0xa5, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc8, 0x62); + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0xa5, 0x21); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xa4, 0x38); + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0xa5, 0x23); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x85, 0x15); + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0xa5, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x20); + + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 120); + + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 20); + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0xa5, 0x22); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe1, 0x01); + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0xa5, 0x00); + + return dsi_ctx.accum_err; +} + +static int ili7836a_disable(struct drm_panel *panel) +{ + struct ili7836a_panel *ctx = to_ili7836a_panel(panel); + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; + + ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 20); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 120); + + return dsi_ctx.accum_err; +} + +static int ili7836a_prepare(struct drm_panel *panel) +{ + struct ili7836a_panel *ctx = to_ili7836a_panel(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ili7836a_supplies), ctx->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + ili7836a_reset(ctx); + + ret = ili7836a_on(ctx); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(ili7836a_supplies), ctx->supplies); + return ret; + } + + return 0; +} + +static int ili7836a_unprepare(struct drm_panel *panel) +{ + struct ili7836a_panel *ctx = to_ili7836a_panel(panel); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(ili7836a_supplies), ctx->supplies); + + return 0; +} + +static const struct drm_display_mode nova_modes[] = { + { + /* 120Hz */ + .clock = (1280 + 12 + 2 + 8) * (960 + 12 + 2 + 24) * 120 / 1000, + .hdisplay = 1280, + .hsync_start = 1280 + 12, + .hsync_end = 1280 + 12 + 2, + .htotal = 1280 + 12 + 2 + 8, + .vdisplay = 960, + .vsync_start = 960 + 12, + .vsync_end = 960 + 12 + 2, + .vtotal = 960 + 12 + 2 + 24, + } +}; + +static struct ili7836a_desc nova_desc = { + .modes = nova_modes, + .num_modes = ARRAY_SIZE(nova_modes), + .width_mm = 91, + .height_mm = 68, + .bpc = 8, +}; + +static int ili7836a_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct ili7836a_panel *ctx = to_ili7836a_panel(panel); + + return drm_connector_helper_get_modes_fixed(connector, ctx->desc->modes); +} + +static enum drm_panel_orientation ili7836a_get_orientation(struct drm_panel *panel) +{ + struct ili7836a_panel *ctx = to_ili7836a_panel(panel); + + return ctx->orientation; +} + +static const struct drm_panel_funcs ili7836a_panel_funcs = { + .prepare = ili7836a_prepare, + .unprepare = ili7836a_unprepare, + .disable = ili7836a_disable, + .get_modes = ili7836a_get_modes, + .get_orientation = ili7836a_get_orientation, +}; + +static int ili7836a_bl_update_status(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + u16 brightness = backlight_get_brightness(bl); + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); + if (ret < 0) + return ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + return 0; +} + +static const struct backlight_ops ili7836a_bl_ops = { + .update_status = ili7836a_bl_update_status, +}; + +static struct backlight_device * +ili7836a_create_backlight(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + const struct backlight_properties props = { + .type = BACKLIGHT_RAW, + .brightness = 3445, + .max_brightness = 3445, + }; + + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, + &ili7836a_bl_ops, &props); +} + +static int ili7836a_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct ili7836a_panel *ctx; + int ret; + + ctx = devm_drm_panel_alloc(dev, __typeof(*ctx), panel, + &ili7836a_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + ctx->desc = (struct ili7836a_desc *)of_device_get_match_data(dev); + if (!ctx->desc) + return -ENODEV; + + ret = devm_regulator_bulk_get_const(dev, + ARRAY_SIZE(ili7836a_supplies), + ili7836a_supplies, + &ctx->supplies); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "Failed to get reset-gpios\n"); + + ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation); + if (ret < 0) { + dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret); + return ret; + } + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_HSE | + MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_CLOCK_NON_CONTINUOUS; + + ctx->panel.prepare_prev_first = true; + + ctx->panel.backlight = ili7836a_create_backlight(dsi); + if (IS_ERR(ctx->panel.backlight)) + return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), + "Failed to create backlight\n"); + + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; + + return devm_mipi_dsi_attach(dev, dsi); +} + +static const struct of_device_id ili7836a_of_match[] = { + { .compatible = "retroidpocket,nova-panel", .data = &nova_desc }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ili7836a_of_match); + +static struct mipi_dsi_driver ili7836a_driver = { + .probe = ili7836a_probe, + .driver = { + .name = "panel-ili7836a-amoled", + .of_match_table = ili7836a_of_match, + }, +}; +module_mipi_dsi_driver(ili7836a_driver); + +MODULE_DESCRIPTION("DRM driver for ILI7836A DSI panels"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9805.c b/drivers/gpu/drm/panel/panel-ilitek-ili9805.c index 5610a225f77b..976305991b7f 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9805.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9805.c @@ -305,31 +305,19 @@ static int ili9805_dsi_probe(struct mipi_dsi_device *dsi) if (ret) return ret; - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(&dsi->dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(&dsi->dev, dsi); if (ret < 0) { dev_err(&dsi->dev, "mipi_dsi_attach failed: %d\n", ret); - drm_panel_remove(&ctx->panel); return ret; } return 0; } -static void ili9805_dsi_remove(struct mipi_dsi_device *dsi) -{ - struct ili9805 *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", - ret); - - drm_panel_remove(&ctx->panel); -} - static const struct ili9805_desc gpm1780a0_desc = { .init = gpm1780a0_init, .mode = &gpm1780a0_timing, @@ -353,7 +341,6 @@ MODULE_DEVICE_TABLE(of, ili9805_of_match); static struct mipi_dsi_driver ili9805_dsi_driver = { .probe = ili9805_dsi_probe, - .remove = ili9805_dsi_remove, .driver = { .name = "ili9805-dsi", .of_match_table = ili9805_of_match, diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c index 53e25d1086db..81d48c402aec 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c @@ -112,21 +112,11 @@ int ili9806e_probe(struct device *dev, void *transport, if (set_prepare_prev_first) ctx->panel.prepare_prev_first = true; - drm_panel_add(&ctx->panel); - - return 0; + return devm_drm_panel_add(dev, &ctx->panel); } EXPORT_SYMBOL_GPL(ili9806e_probe); -void ili9806e_remove(struct device *dev) -{ - struct ili9806e *ctx = dev_get_drvdata(dev); - - drm_panel_remove(&ctx->panel); -} -EXPORT_SYMBOL_GPL(ili9806e_remove); - MODULE_AUTHOR("Dario Binacchi <dario.binacchi@amarulasolutions.com>"); MODULE_AUTHOR("Gunnar Dibbern <gunnar.dibbern@lht.dlh.de>"); MODULE_AUTHOR("Michael Walle <mwalle@kernel.org>"); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.h b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.h index dddece62cf42..fded4b6793e7 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.h +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.h @@ -10,6 +10,5 @@ int ili9806e_power_on(struct device *dev); int ili9806e_probe(struct device *dev, void *transport, const struct drm_panel_funcs *funcs, int connector_type); -void ili9806e_remove(struct device *dev); #endif /* _PANEL_ILITEK_ILI9806E_CORE_H */ diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c index 0fb06c425545..89915d58f69d 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c @@ -141,20 +141,7 @@ static int ili9806e_dsi_probe(struct mipi_dsi_device *dsi) if (ret) return ret; - ret = mipi_dsi_attach(dsi); - if (ret < 0) { - dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); - ili9806e_remove(dev); - return ret; - } - - return 0; -} - -static void ili9806e_dsi_remove(struct mipi_dsi_device *dsi) -{ - mipi_dsi_detach(dsi); - ili9806e_remove(&dsi->dev); + return devm_mipi_dsi_attach(dev, dsi); } static void com35h3p70ulc_init(struct mipi_dsi_multi_context *ctx) @@ -490,7 +477,6 @@ static struct mipi_dsi_driver ili9806e_dsi_driver = { .of_match_table = ili9806e_dsi_of_match, }, .probe = ili9806e_dsi_probe, - .remove = ili9806e_dsi_remove, }; module_mipi_dsi_driver(ili9806e_dsi_driver); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c index 9d10b0d28f52..187bcb084dd9 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c @@ -124,11 +124,6 @@ static int ili9806e_spi_probe(struct spi_device *spi) DRM_MODE_CONNECTOR_DPI); } -static void ili9806e_spi_remove(struct spi_device *spi) -{ - ili9806e_remove(&spi->dev); -} - static void rk050hr345_ct106a_init(struct ili9806e_spi_panel *ctx) { struct mipi_dbi *dbi = &ctx->dbi; @@ -313,7 +308,6 @@ static struct spi_driver ili9806e_spi_driver = { .of_match_table = ili9806e_spi_of_match, }, .probe = ili9806e_spi_probe, - .remove = ili9806e_spi_remove, .id_table = ili9806e_spi_ids, }; module_spi_driver(ili9806e_spi_driver); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index c3d5ab67121d..7bf38e96678a 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -2594,21 +2594,15 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi) if (ret) return ret; - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(&dsi->dev, &ctx->panel); + if (ret) + return ret; dsi->mode_flags = ctx->desc->mode_flags; dsi->format = MIPI_DSI_FMT_RGB888; dsi->lanes = ctx->desc->lanes; - return mipi_dsi_attach(dsi); -} - -static void ili9881c_dsi_remove(struct mipi_dsi_device *dsi) -{ - struct ili9881c *ctx = mipi_dsi_get_drvdata(dsi); - - mipi_dsi_detach(dsi); - drm_panel_remove(&ctx->panel); + return devm_mipi_dsi_attach(&dsi->dev, dsi); } static const struct ili9881c_desc lhr050h41_desc = { @@ -2722,7 +2716,6 @@ MODULE_DEVICE_TABLE(of, ili9881c_of_match); static struct mipi_dsi_driver ili9881c_dsi_driver = { .probe = ili9881c_dsi_probe, - .remove = ili9881c_dsi_remove, .driver = { .name = "ili9881c-dsi", .of_match_table = ili9881c_of_match, diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c index 6d07fe901357..d4b383138488 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c @@ -809,9 +809,7 @@ static int ili9882t_add(struct ili9882t *ili) ili->base.funcs = &ili9882t_funcs; ili->base.dev = &ili->dsi->dev; - drm_panel_add(&ili->base); - - return 0; + return devm_drm_panel_add(dev, &ili->base); } static int ili9882t_probe(struct mipi_dsi_device *dsi) @@ -844,24 +842,7 @@ static int ili9882t_probe(struct mipi_dsi_device *dsi) mipi_dsi_set_drvdata(dsi, ili); - ret = mipi_dsi_attach(dsi); - if (ret) - drm_panel_remove(&ili->base); - - return ret; -} - -static void ili9882t_remove(struct mipi_dsi_device *dsi) -{ - struct ili9882t *ili = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); - - if (ili->base.dev) - drm_panel_remove(&ili->base); + return devm_mipi_dsi_attach(&dsi->dev, dsi); } static const struct of_device_id ili9882t_of_match[] = { @@ -881,7 +862,6 @@ static struct mipi_dsi_driver ili9882t_driver = { .of_match_table = ili9882t_of_match, }, .probe = ili9882t_probe, - .remove = ili9882t_remove, }; module_mipi_dsi_driver(ili9882t_driver); diff --git a/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c b/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c index cbe354b51bce..caf18d309849 100644 --- a/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c +++ b/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c @@ -200,9 +200,11 @@ static int jdi_fhd_r63452_probe(struct mipi_dsi_device *dsi) if (ret) return dev_err_probe(dev, ret, "Failed to get backlight\n"); - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "Failed to attach to DSI host: %d\n", ret); return ret; @@ -211,18 +213,6 @@ static int jdi_fhd_r63452_probe(struct mipi_dsi_device *dsi) return 0; } -static void jdi_fhd_r63452_remove(struct mipi_dsi_device *dsi) -{ - struct jdi_fhd_r63452 *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id jdi_fhd_r63452_of_match[] = { { .compatible = "jdi,fhd-r63452" }, { /* sentinel */ } @@ -231,7 +221,6 @@ MODULE_DEVICE_TABLE(of, jdi_fhd_r63452_of_match); static struct mipi_dsi_driver jdi_fhd_r63452_driver = { .probe = jdi_fhd_r63452_probe, - .remove = jdi_fhd_r63452_remove, .driver = { .name = "panel-jdi-fhd-r63452", .of_match_table = jdi_fhd_r63452_of_match, diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c index 01bd748aecec..e91b41743bec 100644 --- a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c @@ -345,15 +345,7 @@ static int jdi_panel_add(struct jdi_panel *jdi) return dev_err_probe(dev, PTR_ERR(jdi->backlight), "failed to register backlight %d\n", ret); - drm_panel_add(&jdi->base); - - return 0; -} - -static void jdi_panel_del(struct jdi_panel *jdi) -{ - if (jdi->base.dev) - drm_panel_remove(&jdi->base); + return devm_drm_panel_add(dev, &jdi->base); } static int jdi_panel_probe(struct mipi_dsi_device *dsi) @@ -380,26 +372,7 @@ static int jdi_panel_probe(struct mipi_dsi_device *dsi) if (ret < 0) return ret; - ret = mipi_dsi_attach(dsi); - if (ret < 0) { - jdi_panel_del(jdi); - return ret; - } - - return 0; -} - -static void jdi_panel_remove(struct mipi_dsi_device *dsi) -{ - struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", - ret); - - jdi_panel_del(jdi); + return devm_mipi_dsi_attach(&dsi->dev, dsi); } static struct mipi_dsi_driver jdi_panel_driver = { @@ -408,7 +381,6 @@ static struct mipi_dsi_driver jdi_panel_driver = { .of_match_table = jdi_of_match, }, .probe = jdi_panel_probe, - .remove = jdi_panel_remove, }; module_mipi_dsi_driver(jdi_panel_driver); diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c index 0856df5a6ee2..8a02e92061f5 100644 --- a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c +++ b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c @@ -583,30 +583,19 @@ static int ltk050h3146w_probe(struct mipi_dsi_device *dsi) if (ret) return ret; - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "mipi_dsi_attach failed: %d\n", ret); - drm_panel_remove(&ctx->panel); return ret; } return 0; } -static void ltk050h3146w_remove(struct mipi_dsi_device *dsi) -{ - struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id ltk050h3146w_of_match[] = { { .compatible = "leadtek,ltk050h3146w", @@ -630,7 +619,6 @@ static struct mipi_dsi_driver ltk050h3146w_driver = { .of_match_table = ltk050h3146w_of_match, }, .probe = ltk050h3146w_probe, - .remove = ltk050h3146w_remove, }; module_mipi_dsi_driver(ltk050h3146w_driver); diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c index 7f19fd5b8060..ff35b62a327e 100644 --- a/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c +++ b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c @@ -649,30 +649,19 @@ static int ltk500hd1829_probe(struct mipi_dsi_device *dsi) if (ret) return ret; - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "mipi_dsi_attach failed: %d\n", ret); - drm_panel_remove(&ctx->panel); return ret; } return 0; } -static void ltk500hd1829_remove(struct mipi_dsi_device *dsi) -{ - struct ltk500hd1829 *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id ltk500hd1829_of_match[] = { { .compatible = "leadtek,ltk101b4029w", @@ -692,7 +681,6 @@ static struct mipi_dsi_driver ltk500hd1829_driver = { .of_match_table = ltk500hd1829_of_match, }, .probe = ltk500hd1829_probe, - .remove = ltk500hd1829_remove, }; module_mipi_dsi_driver(ltk500hd1829_driver); diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36532.c b/drivers/gpu/drm/panel/panel-novatek-nt36532.c new file mode 100644 index 000000000000..41d2721e0513 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-novatek-nt36532.c @@ -0,0 +1,431 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Novatek NT36532 DriverIC panels driver + * Based on the template generated by linux-mdss-dsi-panel-driver-generator + * + * Copyright (c) 2025, 2026 Junjie Cao <junjie.cao@linux.dev> + */ + +#include <linux/delay.h> +#include <linux/device-id/of.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/display/drm_dsc.h> +#include <drm/display/drm_dsc_helper.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_probe_helper.h> + +struct nt36532 { + struct drm_panel panel; + struct mipi_dsi_device *dsi[2]; + const struct panel_info *panel_info; + struct drm_dsc_config dsc; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data *supplies; +}; + +struct panel_info { + unsigned int lanes; + enum mipi_dsi_pixel_format format; + unsigned long mode_flags; + + const struct drm_display_mode display_mode; + + const struct drm_dsc_config *dsc_cfg; + + int (*init_sequence)(struct nt36532 *ctx); + + bool is_dual_dsi; +}; + +static const struct regulator_bulk_data nt36532_supplies[] = { + { .supply = "vddio" }, /* 1.8v */ +}; + +static inline struct nt36532 *to_nt36532(struct drm_panel *panel) +{ + return container_of(panel, struct nt36532, panel); +} + +static inline struct mipi_dsi_device *to_primary_dsi(struct nt36532 *ctx) +{ + /* Sync on DSI1 for dual dsi */ + return ctx->panel_info->is_dual_dsi ? ctx->dsi[1] : ctx->dsi[0]; +} + +static void nt36532_reset(struct nt36532 *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(10000, 10100); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + usleep_range(5000, 5100); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(15000, 15100); +} + +static int nt36532_prepare(struct drm_panel *panel) +{ + struct nt36532 *ctx = to_nt36532(panel); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(nt36532_supplies), + ctx->supplies); + if (ret < 0) + return ret; + + nt36532_reset(ctx); + + ret = ctx->panel_info->init_sequence(ctx); + if (ret < 0) { + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(nt36532_supplies), + ctx->supplies); + return ret; + } + + return 0; +} + +static int nt36532_off(struct nt36532 *ctx) +{ + struct mipi_dsi_device *dsi = to_primary_dsi(ctx); + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; + + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + mipi_dsi_usleep_range(&dsi_ctx, 10000, 10100); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 65); + + return dsi_ctx.accum_err; +} + +static int nt36532_unprepare(struct drm_panel *panel) +{ + struct nt36532 *ctx = to_nt36532(panel); + struct device *dev = &ctx->dsi[0]->dev; + int ret; + + ret = nt36532_off(ctx); + if (ret < 0) + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(nt36532_supplies), ctx->supplies); + + return 0; +} + +static int nt36532_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct nt36532 *ctx = to_nt36532(panel); + const struct panel_info *panel_info = ctx->panel_info; + + return drm_connector_helper_get_modes_fixed(connector, + &panel_info->display_mode); +} + +static const struct drm_panel_funcs nt36532_panel_funcs = { + .prepare = nt36532_prepare, + .unprepare = nt36532_unprepare, + .get_modes = nt36532_get_modes, +}; + +static int csot_init_sequence(struct nt36532 *ctx) +{ + struct mipi_dsi_device *dsi = to_primary_dsi(ctx); + struct drm_dsc_picture_parameter_set pps; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x22); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2d, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x24); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbc, + 0x00, 0x00, 0x03, 0x22, 0x00, 0x41); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x23); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x60); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x20); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x5a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x0c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0xe1); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0xa9); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x16); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0a, 0x8e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0b, 0x8e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x8e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0d, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x0c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1d, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x14); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x18); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x1c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x20); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x24); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x2c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x30); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x34); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x38); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x3c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2a, 0x20); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x20); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0xff); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0xfd); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0xfc); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0xfa); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0xf8); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0xf6); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0xf4); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0xf2); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0xf0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0xee); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0xec); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0xea); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0xe9); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0xe8); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0xe7); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xe6); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0xff); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0xf9); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0xf6); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0xf2); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0xf0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4a, 0xec); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0xe8); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4c, 0xe4); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0xe0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4e, 0xde); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4f, 0xd9); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0xd6); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0xd4); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0xc2); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0xd0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0xcd); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0xff); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0xf6); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0xf0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0xeb); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0xe8); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0xe5); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0xe3); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0xe0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0xde); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0xda); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0xd7); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0xd4); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0xd2); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0xd0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0xcc); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0xc8); + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x27); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0xf0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x2a, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd0, 0x31); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd1, 0x54); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xde, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xdf, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x2a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc4, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xf0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfa, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x16); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x23); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xba, 0xaa, 0x2a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbb, 0xa0, 0x2a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x27); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x8c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, + 0x07, 0xff); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, + 0x2c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, + 0x03, 0xd2, 0x1a, 0x04, 0x04, 0x00); + + /* Enable DSC */ + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x90, 0x03); + drm_dsc_pps_payload_pack(&pps, &ctx->dsc); + mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9d, 0x01); + + /* Program refresh rate control registers */ + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x91); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb3, 0x40); + + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 120); + + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + mipi_dsi_usleep_range(&dsi_ctx, 10000, 10100); + + return dsi_ctx.accum_err; +} + +static const struct drm_display_mode csot_display_mode = { + /* 120Hz, the typical value */ + .clock = (1500 + 118 + 20 + 100) * 2 * (2120 + 26 + 2 + 208) * 120 / 1000, + .hdisplay = 1500 * 2, + .hsync_start = (1500 + 118) * 2, + .hsync_end = (1500 + 118 + 20) * 2, + .htotal = (1500 + 118 + 20 + 100) * 2, + .vdisplay = 2120, + .vsync_start = 2120 + 26, + .vsync_end = 2120 + 26 + 2, + .vtotal = 2120 + 26 + 2 + 208, + .width_mm = 250, + .height_mm = 177, +}; + +static const struct drm_dsc_config csot_dsc_cfg = { + .dsc_version_major = 1, + .dsc_version_minor = 2, + .slice_height = 20, + .slice_width = 750, + .slice_count = 2, + .bits_per_component = 8, + .bits_per_pixel = 8 << 4, + .block_pred_enable = true, +}; + +static int nt36532_probe(struct mipi_dsi_device *dsi) +{ + struct mipi_dsi_device_info dsi_info = {"nt36532-secondary", 0, NULL}; + const struct panel_info *panel_info; + struct mipi_dsi_host *dsi1_host; + struct device *dev = &dsi->dev; + struct device_node *dsi1; + struct nt36532 *ctx; + int num_dsi = 1; + int ret, i; + + ctx = devm_drm_panel_alloc(dev, struct nt36532, panel, + &nt36532_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + ret = devm_regulator_bulk_get_const(&dsi->dev, + ARRAY_SIZE(nt36532_supplies), + nt36532_supplies, &ctx->supplies); + if (ret < 0) + return ret; + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "Failed to get reset-gpios\n"); + + ctx->panel_info = of_device_get_match_data(dev); + panel_info = ctx->panel_info; + if (!panel_info) + return -ENODEV; + + ctx->dsc = *panel_info->dsc_cfg; + + if (panel_info->is_dual_dsi) { + num_dsi = 2; + dsi1 = of_graph_get_remote_node(dsi->dev.of_node, 1, -1); + if (!dsi1) { + dev_err(dev, "cannot get secondary DSI node.\n"); + return -ENODEV; + } + + dsi1_host = of_find_mipi_dsi_host_by_node(dsi1); + of_node_put(dsi1); + if (!dsi1_host) + return dev_err_probe(dev, -EPROBE_DEFER, + "cannot get secondary DSI host\n"); + + ctx->dsi[1] = devm_mipi_dsi_device_register_full(dev, dsi1_host, + &dsi_info); + if (IS_ERR(ctx->dsi[1])) { + dev_err(dev, "cannot get secondary DSI device\n"); + return PTR_ERR(ctx->dsi[1]); + } + + mipi_dsi_set_drvdata(ctx->dsi[1], ctx); + } + + ctx->dsi[0] = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + ctx->panel.prepare_prev_first = true; + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to add panel\n"); + + for (i = 0; i < num_dsi; i++) { + ctx->dsi[i]->lanes = panel_info->lanes; + ctx->dsi[i]->format = panel_info->format; + ctx->dsi[i]->mode_flags = panel_info->mode_flags; + ctx->dsi[i]->dsc = &ctx->dsc; + ret = devm_mipi_dsi_attach(dev, ctx->dsi[i]); + if (ret < 0) { + return dev_err_probe(dev, ret, + "Failed to attach to DSI host\n"); + } + } + + return 0; +} + +static const struct panel_info csot_panel_info = { + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS | + MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT, + .display_mode = csot_display_mode, + .dsc_cfg = &csot_dsc_cfg, + .init_sequence = csot_init_sequence, + .is_dual_dsi = true, +}; + +static const struct of_device_id nt36532_of_match[] = { + { .compatible = "csot,ppc100hb1-1", .data = &csot_panel_info }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, nt36532_of_match); + +static struct mipi_dsi_driver nt36532_driver = { + .probe = nt36532_probe, + .driver = { + .name = "panel-novatek-nt36532", + .of_match_table = nt36532_of_match, + }, +}; +module_mipi_dsi_driver(nt36532_driver); + +MODULE_AUTHOR("Junjie Cao <junjie.cao@linux.dev>"); +MODULE_DESCRIPTION("DRM driver for Novatek NT36532 based MIPI DSI panels"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c index 54a65abf7e89..e6ad1e876070 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c @@ -48,14 +48,10 @@ static int s6d16d0_unprepare(struct drm_panel *panel) { struct s6d16d0 *s6 = panel_to_s6d16d0(panel); struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev); - int ret; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; - /* Enter sleep mode */ - ret = mipi_dsi_dcs_enter_sleep_mode(dsi); - if (ret) { - dev_err(s6->dev, "failed to enter sleep mode (%d)\n", ret); - return ret; - } + /* Enter sleep mode; error does not prevent power-off */ + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); /* Assert RESET */ gpiod_set_value_cansleep(s6->reset_gpio, 1); @@ -68,6 +64,7 @@ static int s6d16d0_prepare(struct drm_panel *panel) { struct s6d16d0 *s6 = panel_to_s6d16d0(panel); struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev); + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; int ret; ret = regulator_enable(s6->supply); @@ -83,57 +80,39 @@ static int s6d16d0_prepare(struct drm_panel *panel) gpiod_set_value_cansleep(s6->reset_gpio, 0); msleep(120); - /* Enabe tearing mode: send TE (tearing effect) at VBLANK */ - ret = mipi_dsi_dcs_set_tear_on(dsi, + /* Enable tearing mode: send TE (tearing effect) at VBLANK */ + mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); - if (ret) { - dev_err(s6->dev, "failed to enable vblank TE (%d)\n", ret); - goto err_power_off; - } /* Exit sleep mode and power on */ - ret = mipi_dsi_dcs_exit_sleep_mode(dsi); - if (ret) { - dev_err(s6->dev, "failed to exit sleep mode (%d)\n", ret); - goto err_power_off; + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + if (dsi_ctx.accum_err) { + gpiod_set_value_cansleep(s6->reset_gpio, 1); + regulator_disable(s6->supply); } - return 0; - -err_power_off: - gpiod_set_value_cansleep(s6->reset_gpio, 1); - regulator_disable(s6->supply); - - return ret; + return dsi_ctx.accum_err; } static int s6d16d0_enable(struct drm_panel *panel) { struct s6d16d0 *s6 = panel_to_s6d16d0(panel); struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev); - int ret; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; - ret = mipi_dsi_dcs_set_display_on(dsi); - if (ret) { - dev_err(s6->dev, "failed to turn display on (%d)\n", ret); - return ret; - } + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); - return 0; + return dsi_ctx.accum_err; } static int s6d16d0_disable(struct drm_panel *panel) { struct s6d16d0 *s6 = panel_to_s6d16d0(panel); struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev); - int ret; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; - ret = mipi_dsi_dcs_set_display_off(dsi); - if (ret) { - dev_err(s6->dev, "failed to turn display off (%d)\n", ret); - return ret; - } + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); - return 0; + return dsi_ctx.accum_err; } static int s6d16d0_get_modes(struct drm_panel *panel, @@ -207,21 +186,11 @@ static int s6d16d0_probe(struct mipi_dsi_device *dsi) return ret; } - drm_panel_add(&s6->panel); - - ret = mipi_dsi_attach(dsi); - if (ret < 0) - drm_panel_remove(&s6->panel); - - return ret; -} - -static void s6d16d0_remove(struct mipi_dsi_device *dsi) -{ - struct s6d16d0 *s6 = mipi_dsi_get_drvdata(dsi); + ret = devm_drm_panel_add(dev, &s6->panel); + if (ret) + return ret; - mipi_dsi_detach(dsi); - drm_panel_remove(&s6->panel); + return devm_mipi_dsi_attach(dev, dsi); } static const struct of_device_id s6d16d0_of_match[] = { @@ -232,7 +201,6 @@ MODULE_DEVICE_TABLE(of, s6d16d0_of_match); static struct mipi_dsi_driver s6d16d0_driver = { .probe = s6d16d0_probe, - .remove = s6d16d0_remove, .driver = { .name = "panel-samsung-s6d16d0", .of_match_table = s6d16d0_of_match, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c b/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c index 692020081524..5a4cfb21e2fd 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c @@ -436,30 +436,19 @@ static int s6d7aa0_probe(struct mipi_dsi_device *dsi) "Failed to create backlight\n"); } - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "Failed to attach to DSI host: %d\n", ret); - drm_panel_remove(&ctx->panel); return ret; } return 0; } -static void s6d7aa0_remove(struct mipi_dsi_device *dsi) -{ - struct s6d7aa0 *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id s6d7aa0_of_match[] = { { .compatible = "samsung,lsl080al02", @@ -479,7 +468,6 @@ MODULE_DEVICE_TABLE(of, s6d7aa0_of_match); static struct mipi_dsi_driver s6d7aa0_driver = { .probe = s6d7aa0_probe, - .remove = s6d7aa0_remove, .driver = { .name = "panel-samsung-s6d7aa0", .of_match_table = s6d7aa0_of_match, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3fa7.c b/drivers/gpu/drm/panel/panel-samsung-s6e3fa7.c index f4d75eca3cdf..9cd92d7a823b 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e3fa7.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3fa7.c @@ -211,30 +211,19 @@ static int s6e3fa7_panel_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), "Failed to create backlight\n"); - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "Failed to attach to DSI host: %d\n", ret); - drm_panel_remove(&ctx->panel); return ret; } return 0; } -static void s6e3fa7_panel_remove(struct mipi_dsi_device *dsi) -{ - struct s6e3fa7_panel *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id s6e3fa7_panel_of_match[] = { { .compatible = "samsung,s6e3fa7-ams559nk06" }, { /* sentinel */ } @@ -243,7 +232,6 @@ MODULE_DEVICE_TABLE(of, s6e3fa7_panel_of_match); static struct mipi_dsi_driver s6e3fa7_panel_driver = { .probe = s6e3fa7_panel_probe, - .remove = s6e3fa7_panel_remove, .driver = { .name = "panel-samsung-s6e3fa7", .of_match_table = s6e3fa7_panel_of_match, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c b/drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c index e63080204af7..8dfe654f36ec 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c @@ -340,30 +340,19 @@ static int s6e3fc2x01_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), "Failed to create backlight\n"); - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "Failed to attach to DSI host: %d\n", ret); - drm_panel_remove(&ctx->panel); return ret; } return 0; } -static void s6e3fc2x01_remove(struct mipi_dsi_device *dsi) -{ - struct samsung_s6e3fc2x01 *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id s6e3fc2x01_of_match[] = { { .compatible = "samsung,s6e3fc2x01-ams641rw", .data = &ams641rw_mode }, { /* sentinel */ } @@ -372,7 +361,6 @@ MODULE_DEVICE_TABLE(of, s6e3fc2x01_of_match); static struct mipi_dsi_driver s6e3fc2x01_driver = { .probe = s6e3fc2x01_probe, - .remove = s6e3fc2x01_remove, .driver = { .name = "panel-samsung-s6e3fc2x01", .of_match_table = s6e3fc2x01_of_match, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c index 1db0c63b1131..cede897ff69d 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c @@ -722,8 +722,8 @@ static int s6e3ha2_probe(struct mipi_dsi_device *dsi) return PTR_ERR(ctx->enable_gpio); } - ctx->bl_dev = backlight_device_register("s6e3ha2", dev, ctx, - &s6e3ha2_bl_ops, NULL); + ctx->bl_dev = devm_backlight_device_register(dev, "s6e3ha2", dev, ctx, + &s6e3ha2_bl_ops, NULL); if (IS_ERR(ctx->bl_dev)) { dev_err(dev, "failed to register backlight device\n"); return PTR_ERR(ctx->bl_dev); @@ -735,28 +735,11 @@ static int s6e3ha2_probe(struct mipi_dsi_device *dsi) ctx->panel.prepare_prev_first = true; - drm_panel_add(&ctx->panel); - - ret = mipi_dsi_attach(dsi); - if (ret < 0) - goto remove_panel; - - return ret; - -remove_panel: - drm_panel_remove(&ctx->panel); - backlight_device_unregister(ctx->bl_dev); - - return ret; -} - -static void s6e3ha2_remove(struct mipi_dsi_device *dsi) -{ - struct s6e3ha2 *ctx = mipi_dsi_get_drvdata(dsi); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - mipi_dsi_detach(dsi); - drm_panel_remove(&ctx->panel); - backlight_device_unregister(ctx->bl_dev); + return devm_mipi_dsi_attach(dev, dsi); } static const struct of_device_id s6e3ha2_of_match[] = { @@ -768,7 +751,6 @@ MODULE_DEVICE_TABLE(of, s6e3ha2_of_match); static struct mipi_dsi_driver s6e3ha2_driver = { .probe = s6e3ha2_probe, - .remove = s6e3ha2_remove, .driver = { .name = "panel-samsung-s6e3ha2", .of_match_table = s6e3ha2_of_match, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c index 550e9ef9bb71..c09ff71b9c39 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c @@ -283,7 +283,9 @@ static int s6e3ha8_amb577px01_wqhd_probe(struct mipi_dsi_device *dsi) priv->panel.prepare_prev_first = true; - drm_panel_add(&priv->panel); + ret = devm_drm_panel_add(dev, &priv->panel); + if (ret) + return ret; /* This panel only supports DSC; unconditionally enable it */ dsi->dsc = &priv->dsc; @@ -299,28 +301,15 @@ static int s6e3ha8_amb577px01_wqhd_probe(struct mipi_dsi_device *dsi) priv->dsc.bits_per_pixel = 8 << 4; /* 4 fractional bits */ priv->dsc.block_pred_enable = true; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "Failed to attach to DSI host: %d\n", ret); - drm_panel_remove(&priv->panel); return ret; } return 0; } -static void s6e3ha8_amb577px01_wqhd_remove(struct mipi_dsi_device *dsi) -{ - struct s6e3ha8 *priv = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&priv->panel); -} - static const struct of_device_id s6e3ha8_amb577px01_wqhd_of_match[] = { { .compatible = "samsung,s6e3ha8" }, { /* sentinel */ } @@ -329,7 +318,6 @@ MODULE_DEVICE_TABLE(of, s6e3ha8_amb577px01_wqhd_of_match); static struct mipi_dsi_driver s6e3ha8_amb577px01_wqhd_driver = { .probe = s6e3ha8_amb577px01_wqhd_probe, - .remove = s6e3ha8_amb577px01_wqhd_remove, .driver = { .name = "panel-s6e3ha8", .of_match_table = s6e3ha8_amb577px01_wqhd_of_match, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c index e05199ce14ee..1f825d995686 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c @@ -465,8 +465,8 @@ static int s6e63j0x03_probe(struct mipi_dsi_device *dsi) ctx->panel.prepare_prev_first = true; - ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx, - &s6e63j0x03_bl_ops, NULL); + ctx->bl_dev = devm_backlight_device_register(dev, "s6e63j0x03", dev, ctx, + &s6e63j0x03_bl_ops, NULL); if (IS_ERR(ctx->bl_dev)) return dev_err_probe(dev, PTR_ERR(ctx->bl_dev), "failed to register backlight device\n"); @@ -475,29 +475,11 @@ static int s6e63j0x03_probe(struct mipi_dsi_device *dsi) ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS; ctx->bl_dev->props.power = BACKLIGHT_POWER_OFF; - drm_panel_add(&ctx->panel); - - ret = mipi_dsi_attach(dsi); - if (ret < 0) - goto remove_panel; - - return ret; - -remove_panel: - drm_panel_remove(&ctx->panel); - backlight_device_unregister(ctx->bl_dev); - - return ret; -} - -static void s6e63j0x03_remove(struct mipi_dsi_device *dsi) -{ - struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi); - - mipi_dsi_detach(dsi); - drm_panel_remove(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - backlight_device_unregister(ctx->bl_dev); + return devm_mipi_dsi_attach(dev, dsi); } static const struct of_device_id s6e63j0x03_of_match[] = { @@ -508,7 +490,6 @@ MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match); static struct mipi_dsi_driver s6e63j0x03_driver = { .probe = s6e63j0x03_probe, - .remove = s6e63j0x03_remove, .driver = { .name = "panel_samsung_s6e63j0x03", .of_match_table = s6e63j0x03_of_match, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c index 2630975c111b..c3842449c895 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c @@ -105,17 +105,7 @@ static int s6e63m0_dsi_probe(struct mipi_dsi_device *dsi) if (ret) return ret; - ret = mipi_dsi_attach(dsi); - if (ret < 0) - s6e63m0_remove(dev); - - return ret; -} - -static void s6e63m0_dsi_remove(struct mipi_dsi_device *dsi) -{ - mipi_dsi_detach(dsi); - s6e63m0_remove(&dsi->dev); + return devm_mipi_dsi_attach(dev, dsi); } static const struct of_device_id s6e63m0_dsi_of_match[] = { @@ -126,7 +116,6 @@ MODULE_DEVICE_TABLE(of, s6e63m0_dsi_of_match); static struct mipi_dsi_driver s6e63m0_dsi_driver = { .probe = s6e63m0_dsi_probe, - .remove = s6e63m0_dsi_remove, .driver = { .name = "panel-samsung-s6e63m0", .of_match_table = s6e63m0_dsi_of_match, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c index d99afcc672ca..c8bec2b1e7c1 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c @@ -62,11 +62,6 @@ static int s6e63m0_spi_probe(struct spi_device *spi) s6e63m0_spi_dcs_write, false); } -static void s6e63m0_spi_remove(struct spi_device *spi) -{ - s6e63m0_remove(&spi->dev); -} - static const struct of_device_id s6e63m0_spi_of_match[] = { { .compatible = "samsung,s6e63m0" }, { /* sentinel */ } @@ -75,7 +70,6 @@ MODULE_DEVICE_TABLE(of, s6e63m0_spi_of_match); static struct spi_driver s6e63m0_spi_driver = { .probe = s6e63m0_spi_probe, - .remove = s6e63m0_spi_remove, .driver = { .name = "panel-samsung-s6e63m0", .of_match_table = s6e63m0_spi_of_match, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c index 997115b1e541..1990a7c23219 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c @@ -720,20 +720,10 @@ int s6e63m0_probe(struct device *dev, void *trsp, if (ret < 0) return ret; - drm_panel_add(&ctx->panel); - - return 0; + return devm_drm_panel_add(dev, &ctx->panel); } EXPORT_SYMBOL_GPL(s6e63m0_probe); -void s6e63m0_remove(struct device *dev) -{ - struct s6e63m0 *ctx = dev_get_drvdata(dev); - - drm_panel_remove(&ctx->panel); -} -EXPORT_SYMBOL_GPL(s6e63m0_remove); - MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>"); MODULE_DESCRIPTION("s6e63m0 LCD Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h index c926eca1c817..7dae6772899c 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h @@ -35,6 +35,5 @@ int s6e63m0_probe(struct device *dev, void *trsp, const u8 *data, size_t len), bool dsi_mode); -void s6e63m0_remove(struct device *dev); #endif /* _PANEL_SAMSUNG_S6E63M0_H */ diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c index 77fee36dbb55..b48a2f9578e7 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c @@ -722,30 +722,19 @@ static int s6e88a0_ams427ap24_probe(struct mipi_dsi_device *dsi) if (ret < 0) return ret; - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "Failed to attach to DSI host: %d\n", ret); - drm_panel_remove(&ctx->panel); return ret; } return 0; } -static void s6e88a0_ams427ap24_remove(struct mipi_dsi_device *dsi) -{ - struct s6e88a0_ams427ap24 *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id s6e88a0_ams427ap24_of_match[] = { { .compatible = "samsung,s6e88a0-ams427ap24" }, { /* sentinel */ }, @@ -754,7 +743,6 @@ MODULE_DEVICE_TABLE(of, s6e88a0_ams427ap24_of_match); static struct mipi_dsi_driver s6e88a0_ams427ap24_driver = { .probe = s6e88a0_ams427ap24_probe, - .remove = s6e88a0_ams427ap24_remove, .driver = { .name = "panel-s6e88a0-ams427ap24", .of_match_table = s6e88a0_ams427ap24_of_match, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c index ca5cad41ff1d..bbb86ea86d5e 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c @@ -194,30 +194,19 @@ static int s6e88a0_ams452ef01_probe(struct mipi_dsi_device *dsi) dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST; - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "Failed to attach to DSI host: %d\n", ret); - drm_panel_remove(&ctx->panel); return ret; } return 0; } -static void s6e88a0_ams452ef01_remove(struct mipi_dsi_device *dsi) -{ - struct s6e88a0_ams452ef01 *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id s6e88a0_ams452ef01_of_match[] = { { .compatible = "samsung,s6e88a0-ams452ef01" }, { /* sentinel */ }, @@ -226,7 +215,6 @@ MODULE_DEVICE_TABLE(of, s6e88a0_ams452ef01_of_match); static struct mipi_dsi_driver s6e88a0_ams452ef01_driver = { .probe = s6e88a0_ams452ef01_probe, - .remove = s6e88a0_ams452ef01_remove, .driver = { .name = "panel-s6e88a0-ams452ef01", .of_match_table = s6e88a0_ams452ef01_of_match, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c index 1b5c500d4f4e..8f160ca0a94e 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c @@ -1018,21 +1018,11 @@ static int s6e8aa0_probe(struct mipi_dsi_device *dsi) ctx->panel.prepare_prev_first = true; - drm_panel_add(&ctx->panel); - - ret = mipi_dsi_attach(dsi); - if (ret < 0) - drm_panel_remove(&ctx->panel); - - return ret; -} - -static void s6e8aa0_remove(struct mipi_dsi_device *dsi) -{ - struct s6e8aa0 *ctx = mipi_dsi_get_drvdata(dsi); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - mipi_dsi_detach(dsi); - drm_panel_remove(&ctx->panel); + return devm_mipi_dsi_attach(dev, dsi); } static const struct of_device_id s6e8aa0_of_match[] = { @@ -1043,7 +1033,6 @@ MODULE_DEVICE_TABLE(of, s6e8aa0_of_match); static struct mipi_dsi_driver s6e8aa0_driver = { .probe = s6e8aa0_probe, - .remove = s6e8aa0_remove, .driver = { .name = "panel-samsung-s6e8aa0", .of_match_table = s6e8aa0_of_match, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e8fc0-m1906f9.c b/drivers/gpu/drm/panel/panel-samsung-s6e8fc0-m1906f9.c index 2fae0dc6c424..0e797a2ec14f 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e8fc0-m1906f9.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e8fc0-m1906f9.c @@ -256,29 +256,17 @@ static int s6e8fc0_m1906f9_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), "Failed to create backlight\n"); - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); - if (ret < 0) { - drm_panel_remove(&ctx->panel); + ret = devm_mipi_dsi_attach(dev, dsi); + if (ret < 0) return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); - } return 0; } -static void s6e8fc0_remove(struct mipi_dsi_device *dsi) -{ - struct s6e8fc0_ctx *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id samsung_s6e8fc0_of_match[] = { { .compatible = "samsung,s6e8fc0-m1906f9" }, { /* sentinel */ } @@ -287,7 +275,6 @@ MODULE_DEVICE_TABLE(of, samsung_s6e8fc0_of_match); static struct mipi_dsi_driver s6e8fc0_driver = { .probe = s6e8fc0_m1906f9_probe, - .remove = s6e8fc0_remove, .driver = { .name = "panel-samsung-s6e8fc0-m1906f9", .of_match_table = samsung_s6e8fc0_of_match, diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef00.c b/drivers/gpu/drm/panel/panel-samsung-sofef00.c index f07d6533ed69..da02955f5f42 100644 --- a/drivers/gpu/drm/panel/panel-samsung-sofef00.c +++ b/drivers/gpu/drm/panel/panel-samsung-sofef00.c @@ -250,30 +250,19 @@ static int sofef00_panel_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), "Failed to create backlight\n"); - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "Failed to attach to DSI host: %d\n", ret); - drm_panel_remove(&ctx->panel); return ret; } return 0; } -static void sofef00_panel_remove(struct mipi_dsi_device *dsi) -{ - struct sofef00_panel *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id sofef00_panel_of_match[] = { { .compatible = "samsung,sofef00" }, /* legacy */ { .compatible = "samsung,sofef00-ams628nw01" }, @@ -283,7 +272,6 @@ MODULE_DEVICE_TABLE(of, sofef00_panel_of_match); static struct mipi_dsi_driver sofef00_panel_driver = { .probe = sofef00_panel_probe, - .remove = sofef00_panel_remove, .driver = { .name = "panel-samsung-sofef00", .of_match_table = sofef00_panel_of_match, diff --git a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c index 989b030ea22d..5077893dab7c 100644 --- a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c +++ b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c @@ -205,15 +205,7 @@ static int sharp_nt_panel_add(struct sharp_nt_panel *sharp_nt) if (ret) return ret; - drm_panel_add(&sharp_nt->base); - - return 0; -} - -static void sharp_nt_panel_del(struct sharp_nt_panel *sharp_nt) -{ - if (sharp_nt->base.dev) - drm_panel_remove(&sharp_nt->base); + return devm_drm_panel_add(dev, &sharp_nt->base); } static int sharp_nt_panel_probe(struct mipi_dsi_device *dsi) @@ -244,25 +236,7 @@ static int sharp_nt_panel_probe(struct mipi_dsi_device *dsi) if (ret < 0) return ret; - ret = mipi_dsi_attach(dsi); - if (ret < 0) { - sharp_nt_panel_del(sharp_nt); - return ret; - } - - return 0; -} - -static void sharp_nt_panel_remove(struct mipi_dsi_device *dsi) -{ - struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); - - sharp_nt_panel_del(sharp_nt); + return devm_mipi_dsi_attach(&dsi->dev, dsi); } static const struct of_device_id sharp_nt_of_match[] = { @@ -277,7 +251,6 @@ static struct mipi_dsi_driver sharp_nt_panel_driver = { .of_match_table = sharp_nt_of_match, }, .probe = sharp_nt_panel_probe, - .remove = sharp_nt_panel_remove, }; module_mipi_dsi_driver(sharp_nt_panel_driver); diff --git a/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c b/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c index 0456f3d705e7..e59d9d32b1e0 100644 --- a/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c +++ b/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c @@ -233,30 +233,19 @@ static int sharp_ls060_probe(struct mipi_dsi_device *dsi) if (ret) return dev_err_probe(dev, ret, "Failed to get backlight\n"); - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "Failed to attach to DSI host: %d\n", ret); - drm_panel_remove(&ctx->panel); return ret; } return 0; } -static void sharp_ls060_remove(struct mipi_dsi_device *dsi) -{ - struct sharp_ls060 *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id sharp_ls060t1sx01_of_match[] = { { .compatible = "sharp,ls060t1sx01" }, { /* sentinel */ } @@ -265,7 +254,6 @@ MODULE_DEVICE_TABLE(of, sharp_ls060t1sx01_of_match); static struct mipi_dsi_driver sharp_ls060_driver = { .probe = sharp_ls060_probe, - .remove = sharp_ls060_remove, .driver = { .name = "panel-sharp-ls060t1sx01", .of_match_table = sharp_ls060t1sx01_of_match, diff --git a/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c b/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c index a14c86c60d19..285f0d033efd 100644 --- a/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c +++ b/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c @@ -214,30 +214,19 @@ static int sony_td4353_jdi_probe(struct mipi_dsi_device *dsi) ctx->panel.prepare_prev_first = true; - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "Failed to attach to DSI host: %d\n", ret); - drm_panel_remove(&ctx->panel); return ret; } return 0; } -static void sony_td4353_jdi_remove(struct mipi_dsi_device *dsi) -{ - struct sony_td4353_jdi *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id sony_td4353_jdi_of_match[] = { { .compatible = "sony,td4353-jdi-tama", .data = (void *)TYPE_TAMA_60HZ }, { /* sentinel */ } @@ -246,7 +235,6 @@ MODULE_DEVICE_TABLE(of, sony_td4353_jdi_of_match); static struct mipi_dsi_driver sony_td4353_jdi_driver = { .probe = sony_td4353_jdi_probe, - .remove = sony_td4353_jdi_remove, .driver = { .name = "panel-sony-td4353-jdi", .of_match_table = sony_td4353_jdi_of_match, diff --git a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c index 216a6ad8696e..0ebd9836364e 100644 --- a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c +++ b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c @@ -472,30 +472,19 @@ static int truly_nt35521_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), "Failed to create backlight\n"); - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "Failed to attach to DSI host: %d\n", ret); - drm_panel_remove(&ctx->panel); return ret; } return 0; } -static void truly_nt35521_remove(struct mipi_dsi_device *dsi) -{ - struct truly_nt35521 *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id truly_nt35521_of_match[] = { { .compatible = "sony,tulip-truly-nt35521" }, { /* sentinel */ } @@ -504,7 +493,6 @@ MODULE_DEVICE_TABLE(of, truly_nt35521_of_match); static struct mipi_dsi_driver truly_nt35521_driver = { .probe = truly_nt35521_probe, - .remove = truly_nt35521_remove, .driver = { .name = "panel-truly-nt35521", .of_match_table = truly_nt35521_of_match, diff --git a/drivers/gpu/drm/panel/panel-visionox-r66451.c b/drivers/gpu/drm/panel/panel-visionox-r66451.c index 690cccedd438..1efb139bb711 100644 --- a/drivers/gpu/drm/panel/panel-visionox-r66451.c +++ b/drivers/gpu/drm/panel/panel-visionox-r66451.c @@ -304,29 +304,17 @@ static int visionox_r66451_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), "Failed to create backlight\n"); - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; - ret = mipi_dsi_attach(dsi); - if (ret < 0) { + ret = devm_mipi_dsi_attach(dev, dsi); + if (ret < 0) dev_err(dev, "Failed to attach to DSI host: %d\n", ret); - drm_panel_remove(&ctx->panel); - } return ret; } -static void visionox_r66451_remove(struct mipi_dsi_device *dsi) -{ - struct visionox_r66451 *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach DSI host: %d\n", ret); - - drm_panel_remove(&ctx->panel); -} - static const struct of_device_id visionox_r66451_of_match[] = { {.compatible = "visionox,r66451"}, { /*sentinel*/ } @@ -335,7 +323,6 @@ MODULE_DEVICE_TABLE(of, visionox_r66451_of_match); static struct mipi_dsi_driver visionox_r66451_driver = { .probe = visionox_r66451_probe, - .remove = visionox_r66451_remove, .driver = { .name = "panel-visionox-r66451", .of_match_table = visionox_r66451_of_match, diff --git a/drivers/gpu/drm/panel/panel-visionox-rm69299.c b/drivers/gpu/drm/panel/panel-visionox-rm69299.c index 50f8a84537ca..4309e04c35c4 100644 --- a/drivers/gpu/drm/panel/panel-visionox-rm69299.c +++ b/drivers/gpu/drm/panel/panel-visionox-rm69299.c @@ -382,31 +382,21 @@ static int visionox_rm69299_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), "Failed to create backlight\n"); - drm_panel_add(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; dsi->lanes = 4; dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "dsi attach failed ret = %d\n", ret); - goto err_dsi_attach; + return ret; } return 0; - -err_dsi_attach: - drm_panel_remove(&ctx->panel); - return ret; -} - -static void visionox_rm69299_remove(struct mipi_dsi_device *dsi) -{ - struct visionox_rm69299 *ctx = mipi_dsi_get_drvdata(dsi); - - mipi_dsi_detach(ctx->dsi); - drm_panel_remove(&ctx->panel); } const struct visionox_rm69299_panel_desc visionox_rm69299_1080p_display_desc = { @@ -438,7 +428,6 @@ static struct mipi_dsi_driver visionox_rm69299_driver = { .of_match_table = visionox_rm69299_of_match, }, .probe = visionox_rm69299_probe, - .remove = visionox_rm69299_remove, }; module_mipi_dsi_driver(visionox_rm69299_driver); diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c index b51c30778811..0fe81d259274 100644 --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c @@ -78,7 +78,7 @@ static int panfrost_devfreq_get_dev_status(struct device *dev, dev_dbg(pfdev->base.dev, "busy %lu total %lu %lu %% freq %lu MHz\n", status->busy_time, status->total_time, - status->busy_time / (status->total_time / 100), + status->busy_time * 100 / MAX(status->total_time, 1), status->current_frequency / 1000 / 1000); return 0; diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c index d0a23c51be39..485349faf251 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.c +++ b/drivers/gpu/drm/panfrost/panfrost_device.c @@ -11,7 +11,6 @@ #include "panfrost_device.h" #include "panfrost_devfreq.h" -#include "panfrost_features.h" #include "panfrost_gem.h" #include "panfrost_issues.h" #include "panfrost_gpu.h" diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 36cc2e67a308..96a66d673ba6 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -662,7 +662,7 @@ static int panfrost_ioctl_query_bo_info(struct drm_device *dev, void *data, if (bo->is_heap) args->create_flags |= PANFROST_BO_HEAP; - if (!bo->base.map_wc) + if (bo->wb_mmap) args->create_flags |= PANFROST_BO_WB_MMAP; } diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h index 0fda64fbe5f2..b55a3f9edd41 100644 --- a/drivers/gpu/drm/panthor/panthor_device.h +++ b/drivers/gpu/drm/panthor/panthor_device.h @@ -92,17 +92,21 @@ struct panthor_irq { u32 mask; /** - * @mask_lock: protects modifications to _INT_MASK and @mask. + * @lock: protects modifications to _INT_MASK, @mask and @state. * * In paths where _INT_MASK is updated based on a state * transition/check, it's crucial for the state update/check to be * inside the locked section, otherwise it introduces a race window * leading to potential _INT_MASK inconsistencies. */ - spinlock_t mask_lock; + spinlock_t lock; - /** @state: one of &enum panthor_irq_state reflecting the current state. */ - atomic_t state; + /** + * @state: one of &enum panthor_irq_state reflecting the current state. + * + * Must be accessed with lock held. + */ + enum panthor_irq_state state; }; /** @@ -491,150 +495,6 @@ panthor_exception_is_fault(u32 exception_code) const char *panthor_exception_name(struct panthor_device *ptdev, u32 exception_code); -#define INT_RAWSTAT 0x0 -#define INT_CLEAR 0x4 -#define INT_MASK 0x8 -#define INT_STAT 0xc - -/** - * PANTHOR_IRQ_HANDLER() - Define interrupt handlers and the interrupt - * registration function. - * - * The boiler-plate to gracefully deal with shared interrupts is - * auto-generated. All you have to do is call PANTHOR_IRQ_HANDLER() - * just after the actual handler. The handler prototype is: - * - * void (*handler)(struct panthor_device *, u32 status); - */ -#define PANTHOR_IRQ_HANDLER(__name, __handler) \ -static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data) \ -{ \ - struct panthor_irq *pirq = data; \ - enum panthor_irq_state old_state; \ - \ - guard(spinlock_irqsave)(&pirq->mask_lock); \ - old_state = atomic_cmpxchg(&pirq->state, \ - PANTHOR_IRQ_STATE_ACTIVE, \ - PANTHOR_IRQ_STATE_PROCESSING); \ - if (old_state != PANTHOR_IRQ_STATE_ACTIVE) \ - return IRQ_NONE; \ - \ - if (!gpu_read(pirq->iomem, INT_STAT)) { \ - atomic_cmpxchg(&pirq->state, \ - PANTHOR_IRQ_STATE_PROCESSING, \ - PANTHOR_IRQ_STATE_ACTIVE); \ - return IRQ_NONE; \ - } \ - \ - gpu_write(pirq->iomem, INT_MASK, 0); \ - return IRQ_WAKE_THREAD; \ -} \ - \ -static irqreturn_t panthor_ ## __name ## _irq_threaded_handler(int irq, void *data) \ -{ \ - struct panthor_irq *pirq = data; \ - struct panthor_device *ptdev = pirq->ptdev; \ - irqreturn_t ret = IRQ_NONE; \ - \ - while (true) { \ - /* It's safe to access pirq->mask without the lock held here. If a new \ - * event gets added to the mask and the corresponding IRQ is pending, \ - * we'll process it right away instead of adding an extra raw -> threaded \ - * round trip. If an event is removed and the status bit is set, it will \ - * be ignored, just like it would have been if the mask had been adjusted \ - * right before the HW event kicks in. TLDR; it's all expected races we're \ - * covered for. \ - */ \ - u32 status = gpu_read(pirq->iomem, INT_RAWSTAT) & pirq->mask; \ - \ - if (!status) \ - break; \ - \ - __handler(ptdev, status); \ - ret = IRQ_HANDLED; \ - } \ - \ - scoped_guard(spinlock_irqsave, &pirq->mask_lock) { \ - enum panthor_irq_state old_state; \ - \ - old_state = atomic_cmpxchg(&pirq->state, \ - PANTHOR_IRQ_STATE_PROCESSING, \ - PANTHOR_IRQ_STATE_ACTIVE); \ - if (old_state == PANTHOR_IRQ_STATE_PROCESSING) \ - gpu_write(pirq->iomem, INT_MASK, pirq->mask); \ - } \ - \ - return ret; \ -} \ - \ -static inline void panthor_ ## __name ## _irq_suspend(struct panthor_irq *pirq) \ -{ \ - scoped_guard(spinlock_irqsave, &pirq->mask_lock) { \ - atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDING); \ - gpu_write(pirq->iomem, INT_MASK, 0); \ - } \ - synchronize_irq(pirq->irq); \ - atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDED); \ -} \ - \ -static inline void panthor_ ## __name ## _irq_resume(struct panthor_irq *pirq) \ -{ \ - guard(spinlock_irqsave)(&pirq->mask_lock); \ - \ - atomic_set(&pirq->state, PANTHOR_IRQ_STATE_ACTIVE); \ - gpu_write(pirq->iomem, INT_CLEAR, pirq->mask); \ - gpu_write(pirq->iomem, INT_MASK, pirq->mask); \ -} \ - \ -static int panthor_request_ ## __name ## _irq(struct panthor_device *ptdev, \ - struct panthor_irq *pirq, \ - int irq, void __iomem *iomem) \ -{ \ - pirq->ptdev = ptdev; \ - pirq->irq = irq; \ - pirq->mask = 0; \ - pirq->iomem = iomem; \ - spin_lock_init(&pirq->mask_lock); \ - atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDED); \ - gpu_write(pirq->iomem, INT_MASK, 0); \ - \ - return devm_request_threaded_irq(ptdev->base.dev, irq, \ - panthor_ ## __name ## _irq_raw_handler, \ - panthor_ ## __name ## _irq_threaded_handler, \ - IRQF_SHARED, KBUILD_MODNAME "-" # __name, \ - pirq); \ -} \ - \ -static inline void panthor_ ## __name ## _irq_enable_events(struct panthor_irq *pirq, u32 mask) \ -{ \ - guard(spinlock_irqsave)(&pirq->mask_lock); \ - pirq->mask |= mask; \ - \ - /* The only situation where we need to write the new mask is if the IRQ is active. \ - * If it's being processed, the mask will be restored for us in _irq_threaded_handler() \ - * on the PROCESSING -> ACTIVE transition. \ - * If the IRQ is suspended/suspending, the mask is restored at resume time. \ - */ \ - if (atomic_read(&pirq->state) == PANTHOR_IRQ_STATE_ACTIVE) \ - gpu_write(pirq->iomem, INT_MASK, pirq->mask); \ -} \ - \ -static inline void panthor_ ## __name ## _irq_disable_events(struct panthor_irq *pirq, u32 mask)\ -{ \ - guard(spinlock_irqsave)(&pirq->mask_lock); \ - pirq->mask &= ~mask; \ - \ - /* The only situation where we need to write the new mask is if the IRQ is active. \ - * If it's being processed, the mask will be restored for us in _irq_threaded_handler() \ - * on the PROCESSING -> ACTIVE transition. \ - * If the IRQ is suspended/suspending, the mask is restored at resume time. \ - */ \ - if (atomic_read(&pirq->state) == PANTHOR_IRQ_STATE_ACTIVE) \ - gpu_write(pirq->iomem, INT_MASK, pirq->mask); \ -} - -extern struct workqueue_struct *panthor_cleanup_wq; - static inline void gpu_write(void __iomem *iomem, u32 reg, u32 data) { writel(data, iomem + reg); @@ -706,4 +566,134 @@ static inline u64 gpu_read64_counter(void __iomem *iomem, u32 reg) read_poll_timeout(gpu_read64_relaxed, val, cond, delay_us, timeout_us, \ false, iomem, reg) +#define INT_RAWSTAT 0x0 +#define INT_CLEAR 0x4 +#define INT_MASK 0x8 +#define INT_STAT 0xc + +static inline irqreturn_t panthor_irq_default_raw_handler(int irq, void *data) +{ + struct panthor_irq *pirq = data; + + guard(spinlock_irqsave)(&pirq->lock); + if (pirq->state != PANTHOR_IRQ_STATE_ACTIVE) + return IRQ_NONE; + + if (!gpu_read(pirq->iomem, INT_STAT)) + return IRQ_NONE; + + pirq->state = PANTHOR_IRQ_STATE_PROCESSING; + gpu_write(pirq->iomem, INT_MASK, 0); + return IRQ_WAKE_THREAD; +} + +static __always_inline irqreturn_t +panthor_irq_default_threaded_handler(void *data, + void (*slow_handler)(struct panthor_irq *, u32)) +{ + struct panthor_irq *pirq = data; + irqreturn_t ret = IRQ_NONE; + + while (true) { + /* It's safe to access pirq->mask without the lock held here. If a new + * event gets added to the mask and the corresponding IRQ is pending, + * we'll process it right away instead of adding an extra raw -> threaded + * round trip. If an event is removed and the status bit is set, it will + * be ignored, just like it would have been if the mask had been adjusted + * right before the HW event kicks in. TLDR; it's all expected races we're + * covered for. + */ + u32 status = gpu_read(pirq->iomem, INT_RAWSTAT) & pirq->mask; + + if (!status) + break; + + slow_handler(pirq, status); + ret = IRQ_HANDLED; + } + + scoped_guard(spinlock_irqsave, &pirq->lock) { + if (pirq->state == PANTHOR_IRQ_STATE_PROCESSING) { + pirq->state = PANTHOR_IRQ_STATE_ACTIVE; + gpu_write(pirq->iomem, INT_MASK, pirq->mask); + } + } + + return ret; +} + +static inline void panthor_irq_suspend(struct panthor_irq *pirq) +{ + scoped_guard(spinlock_irqsave, &pirq->lock) { + pirq->state = PANTHOR_IRQ_STATE_SUSPENDING; + gpu_write(pirq->iomem, INT_MASK, 0); + } + synchronize_irq(pirq->irq); + scoped_guard(spinlock_irqsave, &pirq->lock) + pirq->state = PANTHOR_IRQ_STATE_SUSPENDED; +} + +static inline void panthor_irq_resume(struct panthor_irq *pirq) +{ + guard(spinlock_irqsave)(&pirq->lock); + pirq->state = PANTHOR_IRQ_STATE_ACTIVE; + gpu_write(pirq->iomem, INT_CLEAR, pirq->mask); + gpu_write(pirq->iomem, INT_MASK, pirq->mask); +} + +static inline void panthor_irq_enable_events(struct panthor_irq *pirq, u32 mask) +{ + guard(spinlock_irqsave)(&pirq->lock); + pirq->mask |= mask; + + /* The only situation where we need to write the new mask is if the IRQ is active. + * If it's being processed, the mask will be restored for us in _irq_threaded_handler() + * on the PROCESSING -> ACTIVE transition. + * If the IRQ is suspended/suspending, the mask is restored at resume time. + */ + if (pirq->state == PANTHOR_IRQ_STATE_ACTIVE) + gpu_write(pirq->iomem, INT_MASK, pirq->mask); +} + +static inline void panthor_irq_disable_events(struct panthor_irq *pirq, u32 mask) +{ + guard(spinlock_irqsave)(&pirq->lock); + pirq->mask &= ~mask; + + /* The only situation where we need to write the new mask is if the IRQ is active. + * If it's being processed, the mask will be restored for us in _irq_threaded_handler() + * on the PROCESSING -> ACTIVE transition. + * If the IRQ is suspended/suspending, the mask is restored at resume time. + */ + if (pirq->state == PANTHOR_IRQ_STATE_ACTIVE) + gpu_write(pirq->iomem, INT_MASK, pirq->mask); +} + +static inline int +panthor_irq_request(struct panthor_device *ptdev, struct panthor_irq *pirq, + int irq, void __iomem *iomem, const char *name, + irqreturn_t (*threaded_handler)(int, void *data)) +{ + const char *full_name; + + pirq->ptdev = ptdev; + pirq->irq = irq; + pirq->mask = 0; + pirq->iomem = iomem; + spin_lock_init(&pirq->lock); + pirq->state = PANTHOR_IRQ_STATE_SUSPENDED; + + full_name = devm_kasprintf(ptdev->base.dev, GFP_KERNEL, KBUILD_MODNAME "-%s", name); + if (!full_name) + return -ENOMEM; + + gpu_write(pirq->iomem, INT_MASK, 0); + return devm_request_threaded_irq(ptdev->base.dev, irq, + panthor_irq_default_raw_handler, + threaded_handler, + IRQF_SHARED, full_name, pirq); +} + +extern struct workqueue_struct *panthor_cleanup_wq; + #endif diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index 10b7e12c1f1e..4f1fab66a13b 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -1095,8 +1095,9 @@ static void panthor_fw_init_global_iface(struct panthor_device *ptdev) msecs_to_jiffies(PING_INTERVAL_MS)); } -static void panthor_job_irq_handler(struct panthor_device *ptdev, u32 status) +static void panthor_job_irq_handler(struct panthor_irq *pirq, u32 status) { + struct panthor_device *ptdev = pirq->ptdev; u32 duration; u64 start = 0; @@ -1122,7 +1123,11 @@ static void panthor_job_irq_handler(struct panthor_device *ptdev, u32 status) trace_gpu_job_irq(ptdev->base.dev, status, duration); } } -PANTHOR_IRQ_HANDLER(job, panthor_job_irq_handler); + +static irqreturn_t panthor_job_irq_threaded_handler(int irq, void *data) +{ + return panthor_irq_default_threaded_handler(data, panthor_job_irq_handler); +} static int panthor_fw_start(struct panthor_device *ptdev) { @@ -1130,8 +1135,8 @@ static int panthor_fw_start(struct panthor_device *ptdev) bool timedout = false; ptdev->fw->booted = false; - panthor_job_irq_enable_events(&ptdev->fw->irq, ~0); - panthor_job_irq_resume(&ptdev->fw->irq); + panthor_irq_enable_events(&ptdev->fw->irq, ~0); + panthor_irq_resume(&ptdev->fw->irq); gpu_write(fw->iomem, MCU_CONTROL, MCU_CONTROL_AUTO); if (!wait_event_timeout(ptdev->fw->req_waitqueue, @@ -1241,7 +1246,7 @@ void panthor_fw_pre_reset(struct panthor_device *ptdev, bool on_hang) ptdev->reset.fast = true; } - panthor_job_irq_suspend(&ptdev->fw->irq); + panthor_irq_suspend(&ptdev->fw->irq); panthor_fw_stop(ptdev); } @@ -1310,7 +1315,7 @@ void panthor_fw_unplug(struct panthor_device *ptdev) if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) { /* Make sure the IRQ handler cannot be called after that point. */ - panthor_job_irq_suspend(&ptdev->fw->irq); + panthor_irq_suspend(&ptdev->fw->irq); panthor_fw_stop(ptdev); } @@ -1505,8 +1510,9 @@ int panthor_fw_init(struct panthor_device *ptdev) if (irq <= 0) return -ENODEV; - ret = panthor_request_job_irq(ptdev, &fw->irq, irq, - ptdev->iomem + JOB_INT_BASE); + ret = panthor_irq_request(ptdev, &fw->irq, irq, + ptdev->iomem + JOB_INT_BASE, "job", + panthor_job_irq_threaded_handler); if (ret) { drm_err(&ptdev->base, "failed to request job irq"); return ret; diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c index c013d6bf9a59..a383b04f101e 100644 --- a/drivers/gpu/drm/panthor/panthor_gpu.c +++ b/drivers/gpu/drm/panthor/panthor_gpu.c @@ -86,8 +86,9 @@ static void panthor_gpu_l2_config_set(struct panthor_device *ptdev) gpu_write(gpu->iomem, GPU_L2_CONFIG, l2_config); } -static void panthor_gpu_irq_handler(struct panthor_device *ptdev, u32 status) +static void panthor_gpu_irq_handler(struct panthor_irq *pirq, u32 status) { + struct panthor_device *ptdev = pirq->ptdev; struct panthor_gpu *gpu = ptdev->gpu; gpu_write(gpu->irq.iomem, INT_CLEAR, status); @@ -116,7 +117,11 @@ static void panthor_gpu_irq_handler(struct panthor_device *ptdev, u32 status) } spin_unlock(&ptdev->gpu->reqs_lock); } -PANTHOR_IRQ_HANDLER(gpu, panthor_gpu_irq_handler); + +static irqreturn_t panthor_gpu_irq_threaded_handler(int irq, void *data) +{ + return panthor_irq_default_threaded_handler(data, panthor_gpu_irq_handler); +} /** * panthor_gpu_unplug() - Called when the GPU is unplugged. @@ -128,7 +133,7 @@ void panthor_gpu_unplug(struct panthor_device *ptdev) /* Make sure the IRQ handler is not running after that point. */ if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) - panthor_gpu_irq_suspend(&ptdev->gpu->irq); + panthor_irq_suspend(&ptdev->gpu->irq); /* Wake-up all waiters. */ spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); @@ -169,13 +174,14 @@ int panthor_gpu_init(struct panthor_device *ptdev) if (irq < 0) return irq; - ret = panthor_request_gpu_irq(ptdev, &ptdev->gpu->irq, irq, - ptdev->iomem + GPU_INT_BASE); + ret = panthor_irq_request(ptdev, &ptdev->gpu->irq, irq, + ptdev->iomem + GPU_INT_BASE, "gpu", + panthor_gpu_irq_threaded_handler); if (ret) return ret; - panthor_gpu_irq_enable_events(&ptdev->gpu->irq, GPU_INTERRUPTS_MASK); - panthor_gpu_irq_resume(&ptdev->gpu->irq); + panthor_irq_enable_events(&ptdev->gpu->irq, GPU_INTERRUPTS_MASK); + panthor_irq_resume(&ptdev->gpu->irq); return 0; } @@ -183,7 +189,7 @@ int panthor_gpu_power_changed_on(struct panthor_device *ptdev) { guard(pm_runtime_active)(ptdev->base.dev); - panthor_gpu_irq_enable_events(&ptdev->gpu->irq, GPU_POWER_INTERRUPTS_MASK); + panthor_irq_enable_events(&ptdev->gpu->irq, GPU_POWER_INTERRUPTS_MASK); return 0; } @@ -192,7 +198,7 @@ void panthor_gpu_power_changed_off(struct panthor_device *ptdev) { guard(pm_runtime_active)(ptdev->base.dev); - panthor_gpu_irq_disable_events(&ptdev->gpu->irq, GPU_POWER_INTERRUPTS_MASK); + panthor_irq_disable_events(&ptdev->gpu->irq, GPU_POWER_INTERRUPTS_MASK); } /** @@ -317,6 +323,21 @@ int panthor_gpu_l2_power_on(struct panthor_device *ptdev) return panthor_gpu_power_on(ptdev, L2, 1, 20000); } +static inline void panthor_gpu_emit_flush_caches_tp(struct panthor_device *ptdev, + u64 start, u32 l2, u32 lsc, + u32 other, int ret) +{ + u32 duration; + + if (!tracepoint_enabled(gpu_cache_flush) || !start) + return; + + if (check_sub_overflow(ktime_get_ns(), start, &duration)) + duration = U32_MAX; + + trace_gpu_cache_flush(ptdev->base.dev, l2, lsc, other, duration, ret); +} + /** * panthor_gpu_flush_caches() - Flush caches * @ptdev: Device. @@ -330,36 +351,40 @@ int panthor_gpu_flush_caches(struct panthor_device *ptdev, u32 l2, u32 lsc, u32 other) { struct panthor_gpu *gpu = ptdev->gpu; - unsigned long flags; + u64 start = 0; int ret = 0; /* Serialize cache flush operations. */ guard(mutex)(&ptdev->gpu->cache_flush_lock); - spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); - if (!(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED)) { - ptdev->gpu->pending_reqs |= GPU_IRQ_CLEAN_CACHES_COMPLETED; - gpu_write(gpu->iomem, GPU_CMD, GPU_FLUSH_CACHES(l2, lsc, other)); - } else { - ret = -EIO; + scoped_guard(spinlock, &ptdev->gpu->reqs_lock) { + if (tracepoint_enabled(gpu_cache_flush)) + start = ktime_get_ns(); + + if (!(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED)) { + ptdev->gpu->pending_reqs |= GPU_IRQ_CLEAN_CACHES_COMPLETED; + gpu_write(gpu->iomem, GPU_CMD, GPU_FLUSH_CACHES(l2, lsc, other)); + } else { + panthor_gpu_emit_flush_caches_tp(ptdev, start, l2, lsc, + other, -EIO); + return -EIO; + } } - spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags); - - if (ret) - return ret; if (!wait_event_timeout(ptdev->gpu->reqs_acked, !(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED), msecs_to_jiffies(100))) { - spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); - if ((ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED) != 0 && - !(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_CLEAN_CACHES_COMPLETED)) - ret = -ETIMEDOUT; - else - ptdev->gpu->pending_reqs &= ~GPU_IRQ_CLEAN_CACHES_COMPLETED; - spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags); + scoped_guard(spinlock, &ptdev->gpu->reqs_lock) { + if ((ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED) != 0 && + !(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_CLEAN_CACHES_COMPLETED)) + ret = -ETIMEDOUT; + else + ptdev->gpu->pending_reqs &= ~GPU_IRQ_CLEAN_CACHES_COMPLETED; + } } + panthor_gpu_emit_flush_caches_tp(ptdev, start, l2, lsc, other, ret); + if (ret) { panthor_device_schedule_reset(ptdev); drm_err(&ptdev->base, "Flush caches timeout"); @@ -378,27 +403,26 @@ int panthor_gpu_soft_reset(struct panthor_device *ptdev) { struct panthor_gpu *gpu = ptdev->gpu; bool timedout = false; - unsigned long flags; - spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); - if (!drm_WARN_ON(&ptdev->base, - ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED)) { - ptdev->gpu->pending_reqs |= GPU_IRQ_RESET_COMPLETED; - gpu_write(gpu->irq.iomem, INT_CLEAR, GPU_IRQ_RESET_COMPLETED); - gpu_write(gpu->iomem, GPU_CMD, GPU_SOFT_RESET); + scoped_guard(spinlock, &ptdev->gpu->reqs_lock) { + if (!drm_WARN_ON(&ptdev->base, + ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED)) { + ptdev->gpu->pending_reqs |= GPU_IRQ_RESET_COMPLETED; + gpu_write(gpu->irq.iomem, INT_CLEAR, GPU_IRQ_RESET_COMPLETED); + gpu_write(gpu->iomem, GPU_CMD, GPU_SOFT_RESET); + } } - spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags); if (!wait_event_timeout(ptdev->gpu->reqs_acked, !(ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED), msecs_to_jiffies(100))) { - spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); - if ((ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED) != 0 && - !(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_RESET_COMPLETED)) - timedout = true; - else - ptdev->gpu->pending_reqs &= ~GPU_IRQ_RESET_COMPLETED; - spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags); + scoped_guard(spinlock, &ptdev->gpu->reqs_lock) { + if ((ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED) != 0 && + !(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_RESET_COMPLETED)) + timedout = true; + else + ptdev->gpu->pending_reqs &= ~GPU_IRQ_RESET_COMPLETED; + } } if (timedout) { @@ -406,7 +430,10 @@ int panthor_gpu_soft_reset(struct panthor_device *ptdev) return -ETIMEDOUT; } - ptdev->gpu->pending_reqs = 0; + scoped_guard(spinlock, &ptdev->gpu->reqs_lock) { + ptdev->gpu->pending_reqs = 0; + } + return 0; } @@ -425,7 +452,7 @@ void panthor_gpu_suspend(struct panthor_device *ptdev) else panthor_hw_l2_power_off(ptdev); - panthor_gpu_irq_suspend(&ptdev->gpu->irq); + panthor_irq_suspend(&ptdev->gpu->irq); } /** @@ -437,7 +464,7 @@ void panthor_gpu_suspend(struct panthor_device *ptdev) */ void panthor_gpu_resume(struct panthor_device *ptdev) { - panthor_gpu_irq_resume(&ptdev->gpu->irq); + panthor_irq_resume(&ptdev->gpu->irq); panthor_hw_l2_power_on(ptdev); } diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index 9f63a048df61..6f7b83dca6a5 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -601,17 +601,13 @@ static u32 panthor_mmu_as_fault_mask(struct panthor_device *ptdev, u32 as) return BIT(as); } -/* Forward declaration to call helpers within as_enable/disable */ -static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status); -PANTHOR_IRQ_HANDLER(mmu, panthor_mmu_irq_handler); - static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr, u64 transtab, u64 transcfg, u64 memattr) { struct panthor_mmu *mmu = ptdev->mmu; - panthor_mmu_irq_enable_events(&ptdev->mmu->irq, - panthor_mmu_as_fault_mask(ptdev, as_nr)); + panthor_irq_enable_events(&ptdev->mmu->irq, + panthor_mmu_as_fault_mask(ptdev, as_nr)); gpu_write64(mmu->iomem, AS_TRANSTAB(as_nr), transtab); gpu_write64(mmu->iomem, AS_MEMATTR(as_nr), memattr); @@ -629,8 +625,8 @@ static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr, lockdep_assert_held(&ptdev->mmu->as.slots_lock); - panthor_mmu_irq_disable_events(&ptdev->mmu->irq, - panthor_mmu_as_fault_mask(ptdev, as_nr)); + panthor_irq_disable_events(&ptdev->mmu->irq, + panthor_mmu_as_fault_mask(ptdev, as_nr)); /* Flush+invalidate RW caches, invalidate RO ones. */ ret = panthor_gpu_flush_caches(ptdev, CACHE_CLEAN | CACHE_INV, @@ -1859,8 +1855,9 @@ static void panthor_vm_unlock_region(struct panthor_vm *vm) mutex_unlock(&ptdev->mmu->as.slots_lock); } -static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status) +static void panthor_mmu_irq_handler(struct panthor_irq *pirq, u32 status) { + struct panthor_device *ptdev = pirq->ptdev; struct panthor_mmu *mmu = ptdev->mmu; bool has_unhandled_faults = false; @@ -1923,6 +1920,11 @@ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status) panthor_sched_report_mmu_fault(ptdev); } +static irqreturn_t panthor_mmu_irq_threaded_handler(int irq, void *data) +{ + return panthor_irq_default_threaded_handler(data, panthor_mmu_irq_handler); +} + /** * panthor_mmu_suspend() - Suspend the MMU logic * @ptdev: Device. @@ -1947,7 +1949,7 @@ void panthor_mmu_suspend(struct panthor_device *ptdev) } mutex_unlock(&ptdev->mmu->as.slots_lock); - panthor_mmu_irq_suspend(&ptdev->mmu->irq); + panthor_irq_suspend(&ptdev->mmu->irq); } /** @@ -1966,7 +1968,7 @@ void panthor_mmu_resume(struct panthor_device *ptdev) ptdev->mmu->as.faulty_mask = 0; mutex_unlock(&ptdev->mmu->as.slots_lock); - panthor_mmu_irq_resume(&ptdev->mmu->irq); + panthor_irq_resume(&ptdev->mmu->irq); } /** @@ -1983,7 +1985,7 @@ void panthor_mmu_pre_reset(struct panthor_device *ptdev) { struct panthor_vm *vm; - panthor_mmu_irq_suspend(&ptdev->mmu->irq); + panthor_irq_suspend(&ptdev->mmu->irq); mutex_lock(&ptdev->mmu->vm.lock); ptdev->mmu->vm.reset_in_progress = true; @@ -2020,7 +2022,7 @@ void panthor_mmu_post_reset(struct panthor_device *ptdev) mutex_unlock(&ptdev->mmu->as.slots_lock); - panthor_mmu_irq_resume(&ptdev->mmu->irq); + panthor_irq_resume(&ptdev->mmu->irq); /* Restart the VM_BIND queues. */ mutex_lock(&ptdev->mmu->vm.lock); @@ -3353,7 +3355,7 @@ panthor_mmu_reclaim_priv_bos(struct panthor_device *ptdev, void panthor_mmu_unplug(struct panthor_device *ptdev) { if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) - panthor_mmu_irq_suspend(&ptdev->mmu->irq); + panthor_irq_suspend(&ptdev->mmu->irq); mutex_lock(&ptdev->mmu->as.slots_lock); for (u32 i = 0; i < ARRAY_SIZE(ptdev->mmu->as.slots); i++) { @@ -3414,8 +3416,9 @@ int panthor_mmu_init(struct panthor_device *ptdev) if (irq <= 0) return -ENODEV; - ret = panthor_request_mmu_irq(ptdev, &mmu->irq, irq, - ptdev->iomem + MMU_INT_BASE); + ret = panthor_irq_request(ptdev, &mmu->irq, irq, + ptdev->iomem + MMU_INT_BASE, "mmu", + panthor_mmu_irq_threaded_handler); if (ret) return ret; @@ -3436,8 +3439,8 @@ int panthor_mmu_init(struct panthor_device *ptdev) if (ret) return ret; - panthor_mmu_irq_enable_events(&mmu->irq, panthor_mmu_fault_mask(ptdev, ~0)); - panthor_mmu_irq_resume(&mmu->irq); + panthor_irq_enable_events(&mmu->irq, panthor_mmu_fault_mask(ptdev, ~0)); + panthor_irq_resume(&mmu->irq); return 0; } diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c index f2c2c3000590..dd7b6ef8ea20 100644 --- a/drivers/gpu/drm/panthor/panthor_pwr.c +++ b/drivers/gpu/drm/panthor/panthor_pwr.c @@ -56,8 +56,9 @@ struct panthor_pwr { wait_queue_head_t reqs_acked; }; -static void panthor_pwr_irq_handler(struct panthor_device *ptdev, u32 status) +static void panthor_pwr_irq_handler(struct panthor_irq *pirq, u32 status) { + struct panthor_device *ptdev = pirq->ptdev; struct panthor_pwr *pwr = ptdev->pwr; spin_lock(&ptdev->pwr->reqs_lock); @@ -75,7 +76,11 @@ static void panthor_pwr_irq_handler(struct panthor_device *ptdev, u32 status) } spin_unlock(&ptdev->pwr->reqs_lock); } -PANTHOR_IRQ_HANDLER(pwr, panthor_pwr_irq_handler); + +static irqreturn_t panthor_pwr_irq_threaded_handler(int irq, void *data) +{ + return panthor_irq_default_threaded_handler(data, panthor_pwr_irq_handler); +} static void panthor_pwr_write_command(struct panthor_device *ptdev, u32 command, u64 args) { @@ -454,7 +459,7 @@ void panthor_pwr_unplug(struct panthor_device *ptdev) /* Make sure the IRQ handler is not running after that point. */ if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) - panthor_pwr_irq_suspend(&ptdev->pwr->irq); + panthor_irq_suspend(&ptdev->pwr->irq); /* Wake-up all waiters. */ spin_lock_irqsave(&ptdev->pwr->reqs_lock, flags); @@ -484,13 +489,14 @@ int panthor_pwr_init(struct panthor_device *ptdev) if (irq < 0) return irq; - err = panthor_request_pwr_irq(ptdev, &pwr->irq, irq, - pwr->iomem + PWR_INT_BASE); + err = panthor_irq_request(ptdev, &pwr->irq, irq, + pwr->iomem + PWR_INT_BASE, "pwr", + panthor_pwr_irq_threaded_handler); if (err) return err; - panthor_pwr_irq_enable_events(&pwr->irq, PWR_INTERRUPTS_MASK); - panthor_pwr_irq_resume(&pwr->irq); + panthor_irq_enable_events(&pwr->irq, PWR_INTERRUPTS_MASK); + panthor_irq_resume(&pwr->irq); return 0; } @@ -566,7 +572,7 @@ void panthor_pwr_suspend(struct panthor_device *ptdev) if (!ptdev->pwr) return; - panthor_pwr_irq_suspend(&ptdev->pwr->irq); + panthor_irq_suspend(&ptdev->pwr->irq); } void panthor_pwr_resume(struct panthor_device *ptdev) @@ -574,5 +580,5 @@ void panthor_pwr_resume(struct panthor_device *ptdev) if (!ptdev->pwr) return; - panthor_pwr_irq_resume(&ptdev->pwr->irq); + panthor_irq_resume(&ptdev->pwr->irq); } diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 5778e23e20a0..60b2417deb81 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -178,23 +178,6 @@ struct panthor_scheduler { struct work_struct sync_upd_work; /** - * @fw_events_work: Work used to process FW events outside the interrupt path. - * - * Even if the interrupt is threaded, we need any event processing - * that require taking the panthor_scheduler::lock to be processed - * outside the interrupt path so we don't block the tick logic when - * it calls panthor_fw_{csg,wait}_wait_acks(). Since most of the - * event processing requires taking this lock, we just delegate all - * FW event processing to the scheduler workqueue. - */ - struct work_struct fw_events_work; - - /** - * @fw_events: Bitmask encoding pending FW events. - */ - atomic_t fw_events; - - /** * @resched_target: When the next tick should occur. * * Expressed in jiffies. @@ -255,7 +238,21 @@ struct panthor_scheduler { } groups; /** + * @events_lock: Lock taken when processing events. + * + * This also needs to be taken when csg_slots are updated, to make sure + * the event processing logic doesn't touch groups that have left the CSG + * slot. + */ + spinlock_t events_lock; + + /** * @csg_slots: FW command stream group slots. + * + * Updates to the group binding must happen with both + * panthor_scheduler::lock and panthor_scheduler::events_lock held. + * The group binding may therefore be read while holding either lock. + * Other slot fields are protected by panthor_scheduler::lock. */ struct panthor_csg_slot csg_slots[MAX_CSGS]; @@ -565,11 +562,13 @@ struct panthor_group { /** @idle_queues: Bitmask reflecting the idle queues. */ u32 idle_queues; - /** @fatal_lock: Lock used to protect access to fatal fields. */ - spinlock_t fatal_lock; - - /** @fatal_queues: Bitmask reflecting the queues that hit a fatal exception. */ - u32 fatal_queues; + /** + * @fatal_queues: Bitmask reflecting the queues that hit a fatal exception. + * + * This is an atomic because we don't want to acquire the events_lock + * every time we need to check the group state. + */ + atomic_t fatal_queues; /** @tiler_oom: Mask of queues that have a tiler OOM event to process. */ atomic_t tiler_oom; @@ -605,8 +604,14 @@ struct panthor_group { * any timeout situation is unrecoverable, and the group becomes useless. We * simply wait for all references to be dropped so we can release the group * object. + * + * This is an atomic because it can be set from both a scheduling context + * (protected with panthor_scheduler::lock) and an event processing context + * (protected with panthor_scheduler::events_lock). We could protect access + * with the events_lock, but this is simpler to make it an atomic since the + * only allowed transition is false -> true. */ - bool timedout; + atomic_t timedout; /** * @innocent: True when the group becomes unusable because the group suspension @@ -676,9 +681,6 @@ struct panthor_group { */ struct panthor_kernel_bo *protm_suspend_buf; - /** @sync_upd_work: Work used to check/signal job fences. */ - struct work_struct sync_upd_work; - /** @tiler_oom_work: Work used to process tiler OOM events happening on this group. */ struct work_struct tiler_oom_work; @@ -988,87 +990,6 @@ group_get(struct panthor_group *group) return group; } -/** - * group_bind_locked() - Bind a group to a group slot - * @group: Group. - * @csg_id: Slot. - * - * Return: 0 on success, a negative error code otherwise. - */ -static int -group_bind_locked(struct panthor_group *group, u32 csg_id) -{ - struct panthor_device *ptdev = group->ptdev; - struct panthor_csg_slot *csg_slot; - int ret; - - lockdep_assert_held(&ptdev->scheduler->lock); - - if (drm_WARN_ON(&ptdev->base, group->csg_id != -1 || csg_id >= MAX_CSGS || - ptdev->scheduler->csg_slots[csg_id].group)) - return -EINVAL; - - ret = panthor_vm_active(group->vm); - if (ret) - return ret; - - csg_slot = &ptdev->scheduler->csg_slots[csg_id]; - group_get(group); - group->csg_id = csg_id; - - /* Dummy doorbell allocation: doorbell is assigned to the group and - * all queues use the same doorbell. - * - * TODO: Implement LRU-based doorbell assignment, so the most often - * updated queues get their own doorbell, thus avoiding useless checks - * on queues belonging to the same group that are rarely updated. - */ - for (u32 i = 0; i < group->queue_count; i++) - group->queues[i]->doorbell_id = csg_id + 1; - - csg_slot->group = group; - - return 0; -} - -/** - * group_unbind_locked() - Unbind a group from a slot. - * @group: Group to unbind. - * - * Return: 0 on success, a negative error code otherwise. - */ -static int -group_unbind_locked(struct panthor_group *group) -{ - struct panthor_device *ptdev = group->ptdev; - struct panthor_csg_slot *slot; - - lockdep_assert_held(&ptdev->scheduler->lock); - - if (drm_WARN_ON(&ptdev->base, group->csg_id < 0 || group->csg_id >= MAX_CSGS)) - return -EINVAL; - - if (drm_WARN_ON(&ptdev->base, group->state == PANTHOR_CS_GROUP_ACTIVE)) - return -EINVAL; - - slot = &ptdev->scheduler->csg_slots[group->csg_id]; - panthor_vm_idle(group->vm); - group->csg_id = -1; - - /* Tiler OOM events will be re-issued next time the group is scheduled. */ - atomic_set(&group->tiler_oom, 0); - if (cancel_work(&group->tiler_oom_work)) - group_put(group); - - for (u32 i = 0; i < group->queue_count; i++) - group->queues[i]->doorbell_id = -1; - - slot->group = NULL; - - group_put(group); - return 0; -} - static bool group_is_idle(struct panthor_group *group) { @@ -1082,8 +1003,9 @@ group_can_run(struct panthor_group *group) { return group->state != PANTHOR_CS_GROUP_TERMINATED && group->state != PANTHOR_CS_GROUP_UNKNOWN_STATE && - !group->destroyed && group->fatal_queues == 0 && - !group->timedout; + !group->destroyed && + !atomic_read(&group->fatal_queues) && + !atomic_read(&group->timedout); } static bool @@ -1482,7 +1404,7 @@ cs_slot_process_fatal_event_locked(struct panthor_device *ptdev, u32 fatal; u64 info; - lockdep_assert_held(&sched->lock); + lockdep_assert_held(&sched->events_lock); cs_iface = panthor_fw_get_cs_iface(ptdev, csg_id, cs_id); fatal = cs_iface->output->fatal; @@ -1492,7 +1414,7 @@ cs_slot_process_fatal_event_locked(struct panthor_device *ptdev, drm_warn(&ptdev->base, "CS_FATAL: pid=%d, comm=%s\n", group->task_info.pid, group->task_info.comm); - group->fatal_queues |= BIT(cs_id); + atomic_or(BIT(cs_id), &group->fatal_queues); } if (CS_EXCEPTION_TYPE(fatal) == DRM_PANTHOR_EXCEPTION_CS_UNRECOVERABLE) { @@ -1530,7 +1452,7 @@ cs_slot_process_fault_event_locked(struct panthor_device *ptdev, u32 fault; u64 info; - lockdep_assert_held(&sched->lock); + lockdep_assert_held(&sched->events_lock); cs_iface = panthor_fw_get_cs_iface(ptdev, csg_id, cs_id); fault = cs_iface->output->fault; @@ -1622,7 +1544,7 @@ static int group_process_tiler_oom(struct panthor_group *group, u32 cs_id) */ if (ret && ret != -ENOMEM) { drm_warn(&ptdev->base, "Failed to extend the tiler heap\n"); - group->fatal_queues |= BIT(cs_id); + atomic_or(BIT(cs_id), &group->fatal_queues); sched_queue_delayed_work(sched, tick, 0); goto out_put_heap_pool; } @@ -1682,7 +1604,7 @@ cs_slot_process_tiler_oom_event_locked(struct panthor_device *ptdev, struct panthor_csg_slot *csg_slot = &sched->csg_slots[csg_id]; struct panthor_group *group = csg_slot->group; - lockdep_assert_held(&sched->lock); + lockdep_assert_held(&sched->events_lock); if (drm_WARN_ON(&ptdev->base, !group)) return; @@ -1703,7 +1625,7 @@ static bool cs_slot_process_irq_locked(struct panthor_device *ptdev, struct panthor_fw_cs_iface *cs_iface; u32 req, ack, events; - lockdep_assert_held(&ptdev->scheduler->lock); + lockdep_assert_held(&ptdev->scheduler->events_lock); cs_iface = panthor_fw_get_cs_iface(ptdev, csg_id, cs_id); req = cs_iface->input->req; @@ -1731,9 +1653,7 @@ static void csg_slot_process_idle_event_locked(struct panthor_device *ptdev, u32 { struct panthor_scheduler *sched = ptdev->scheduler; - lockdep_assert_held(&sched->lock); - - sched->might_have_idle_groups = true; + lockdep_assert_held(&sched->events_lock); /* Schedule a tick so we can evict idle groups and schedule non-idle * ones. This will also update runtime PM and devfreq busy/idle states, @@ -1742,16 +1662,102 @@ static void csg_slot_process_idle_event_locked(struct panthor_device *ptdev, u32 sched_queue_delayed_work(sched, tick, 0); } +static void update_fdinfo_stats(struct panthor_job *job) +{ + struct panthor_group *group = job->group; + struct panthor_queue *queue = group->queues[job->queue_idx]; + struct panthor_gpu_usage *fdinfo = &group->fdinfo.data; + struct panthor_job_profiling_data *slots = queue->profiling.slots->kmap; + struct panthor_job_profiling_data *data = &slots[job->profiling.slot]; + + scoped_guard(spinlock, &group->fdinfo.lock) { + if (job->profiling.mask & PANTHOR_DEVICE_PROFILING_CYCLES) + fdinfo->cycles += data->cycles.after - data->cycles.before; + if (job->profiling.mask & PANTHOR_DEVICE_PROFILING_TIMESTAMP) + fdinfo->time += data->time.after - data->time.before; + } +} + +static bool queue_check_job_completion(struct panthor_queue *queue) +{ + struct panthor_syncobj_64b *syncobj = NULL; + struct panthor_job *job, *job_tmp; + bool cookie, progress = false; + LIST_HEAD(done_jobs); + + cookie = dma_fence_begin_signalling(); + scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) { + list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) { + if (!syncobj) { + struct panthor_group *group = job->group; + + syncobj = group->syncobjs->kmap + + (job->queue_idx * sizeof(*syncobj)); + } + + if (syncobj->seqno < job->done_fence->seqno) + break; + + list_move_tail(&job->node, &done_jobs); + dma_fence_signal_locked(job->done_fence); + } + + if (list_empty(&queue->fence_ctx.in_flight_jobs)) { + /* If we have no job left, we cancel the timer, and reset remaining + * time to its default so it can be restarted next time + * queue_resume_timeout() is called. + */ + queue_suspend_timeout_locked(queue); + + /* If there's no job pending, we consider it progress to avoid a + * spurious timeout if the timeout handler and the sync update + * handler raced. + */ + progress = true; + } else if (!list_empty(&done_jobs)) { + queue_reset_timeout_locked(queue); + progress = true; + } + } + dma_fence_end_signalling(cookie); + + list_for_each_entry_safe(job, job_tmp, &done_jobs, node) { + if (job->profiling.mask) + update_fdinfo_stats(job); + list_del_init(&job->node); + panthor_job_put(&job->base); + } + + return progress; +} + +static void group_check_job_completion(struct panthor_group *group) +{ + u32 queue_idx; + bool cookie; + + cookie = dma_fence_begin_signalling(); + for (queue_idx = 0; queue_idx < group->queue_count; queue_idx++) { + struct panthor_queue *queue = group->queues[queue_idx]; + + if (!queue) + continue; + + queue_check_job_completion(queue); + } + dma_fence_end_signalling(cookie); +} + static void csg_slot_sync_update_locked(struct panthor_device *ptdev, u32 csg_id) { struct panthor_csg_slot *csg_slot = &ptdev->scheduler->csg_slots[csg_id]; struct panthor_group *group = csg_slot->group; - lockdep_assert_held(&ptdev->scheduler->lock); + lockdep_assert_held(&ptdev->scheduler->events_lock); if (group) - group_queue_work(group, sync_upd); + group_check_job_completion(group); sched_queue_work(ptdev->scheduler, sync_upd); } @@ -1763,14 +1769,14 @@ csg_slot_process_progress_timer_event_locked(struct panthor_device *ptdev, u32 c struct panthor_csg_slot *csg_slot = &sched->csg_slots[csg_id]; struct panthor_group *group = csg_slot->group; - lockdep_assert_held(&sched->lock); + lockdep_assert_held(&sched->events_lock); group = csg_slot->group; if (!drm_WARN_ON(&ptdev->base, !group)) { drm_warn(&ptdev->base, "CSG_PROGRESS_TIMER_EVENT: pid=%d, comm=%s\n", group->task_info.pid, group->task_info.comm); - group->timedout = true; + atomic_set(&group->timedout, true); } drm_warn(&ptdev->base, "CSG slot %d progress timeout\n", csg_id); @@ -1784,7 +1790,7 @@ static void sched_process_csg_irq_locked(struct panthor_device *ptdev, u32 csg_i struct panthor_fw_csg_iface *csg_iface; u32 ring_cs_db_mask = 0; - lockdep_assert_held(&ptdev->scheduler->lock); + lockdep_assert_held(&ptdev->scheduler->events_lock); if (drm_WARN_ON(&ptdev->base, csg_id >= ptdev->scheduler->csg_slot_count)) return; @@ -1842,7 +1848,7 @@ static void sched_process_idle_event_locked(struct panthor_device *ptdev) { struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev); - lockdep_assert_held(&ptdev->scheduler->lock); + lockdep_assert_held(&ptdev->scheduler->events_lock); /* Acknowledge the idle event and schedule a tick. */ panthor_fw_update_reqs(glb_iface, req, glb_iface->output->ack, GLB_IDLE); @@ -1858,7 +1864,7 @@ static void sched_process_global_irq_locked(struct panthor_device *ptdev) struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev); u32 req, ack, evts; - lockdep_assert_held(&ptdev->scheduler->lock); + lockdep_assert_held(&ptdev->scheduler->events_lock); req = READ_ONCE(glb_iface->input->req); ack = READ_ONCE(glb_iface->output->ack); @@ -1868,14 +1874,17 @@ static void sched_process_global_irq_locked(struct panthor_device *ptdev) sched_process_idle_event_locked(ptdev); } -static void process_fw_events_work(struct work_struct *work) +/** + * panthor_sched_report_fw_events() - Report FW events to the scheduler. + * @ptdev: Device. + * @events: Bitmask of pending FW events to report. + */ +void panthor_sched_report_fw_events(struct panthor_device *ptdev, u32 events) { - struct panthor_scheduler *sched = container_of(work, struct panthor_scheduler, - fw_events_work); - u32 events = atomic_xchg(&sched->fw_events, 0); - struct panthor_device *ptdev = sched->ptdev; + if (!ptdev->scheduler) + return; - mutex_lock(&sched->lock); + guard(spinlock)(&ptdev->scheduler->events_lock); if (events & JOB_INT_GLOBAL_IF) { sched_process_global_irq_locked(ptdev); @@ -1888,22 +1897,89 @@ static void process_fw_events_work(struct work_struct *work) sched_process_csg_irq_locked(ptdev, csg_id); events &= ~BIT(csg_id); } +} - mutex_unlock(&sched->lock); +/** + * group_bind_locked() - Bind a group to a group slot + * @group: Group. + * @csg_id: Slot. + * + * Return: 0 on success, a negative error code otherwise. + */ +static int +group_bind_locked(struct panthor_group *group, u32 csg_id) +{ + struct panthor_device *ptdev = group->ptdev; + int ret; + + lockdep_assert_held(&ptdev->scheduler->lock); + + if (drm_WARN_ON(&ptdev->base, group->csg_id != -1 || csg_id >= MAX_CSGS || + ptdev->scheduler->csg_slots[csg_id].group)) + return -EINVAL; + + ret = panthor_vm_active(group->vm); + if (ret) + return ret; + + group_get(group); + + /* Dummy doorbell allocation: doorbell is assigned to the group and + * all queues use the same doorbell. + * + * TODO: Implement LRU-based doorbell assignment, so the most often + * updated queues get their own doorbell, thus avoiding useless checks + * on queues belonging to the same group that are rarely updated. + */ + for (u32 i = 0; i < group->queue_count; i++) + group->queues[i]->doorbell_id = csg_id + 1; + + scoped_guard(spinlock, &ptdev->scheduler->events_lock) { + ptdev->scheduler->csg_slots[csg_id].group = group; + group->csg_id = csg_id; + } + + return 0; } /** - * panthor_sched_report_fw_events() - Report FW events to the scheduler. - * @ptdev: Device. - * @events: Bitmask of pending FW events to report. + * group_unbind_locked() - Unbind a group from a slot. + * @group: Group to unbind. + * + * Return: 0 on success, a negative error code otherwise. */ -void panthor_sched_report_fw_events(struct panthor_device *ptdev, u32 events) +static int +group_unbind_locked(struct panthor_group *group) { - if (!ptdev->scheduler) - return; + struct panthor_device *ptdev = group->ptdev; + + lockdep_assert_held(&ptdev->scheduler->lock); + + if (drm_WARN_ON(&ptdev->base, group->csg_id < 0 || group->csg_id >= MAX_CSGS)) + return -EINVAL; - atomic_or(events, &ptdev->scheduler->fw_events); - sched_queue_work(ptdev->scheduler, fw_events); + if (drm_WARN_ON(&ptdev->base, group->state == PANTHOR_CS_GROUP_ACTIVE)) + return -EINVAL; + + scoped_guard(spinlock, &ptdev->scheduler->events_lock) { + /* Process all pending IRQs before returning the slot. */ + sched_process_csg_irq_locked(ptdev, group->csg_id); + ptdev->scheduler->csg_slots[group->csg_id].group = NULL; + group->csg_id = -1; + } + + panthor_vm_idle(group->vm); + + /* Tiler OOM events will be re-issued next time the group is scheduled. */ + atomic_set(&group->tiler_oom, 0); + if (cancel_work(&group->tiler_oom_work)) + group_put(group); + + for (u32 i = 0; i < group->queue_count; i++) + group->queues[i]->doorbell_id = -1; + + group_put(group); + return 0; } static const char *fence_get_driver_name(struct dma_fence *fence) @@ -2136,11 +2212,12 @@ tick_ctx_init(struct panthor_scheduler *sched, * CSG IRQs, so we can flag the faulty queue. */ if (panthor_vm_has_unhandled_faults(group->vm)) { - sched_process_csg_irq_locked(ptdev, i); + scoped_guard(spinlock, &sched->events_lock) + sched_process_csg_irq_locked(ptdev, i); /* No fatal fault reported, flag all queues as faulty. */ - if (!group->fatal_queues) - group->fatal_queues |= GENMASK(group->queue_count - 1, 0); + atomic_cmpxchg(&group->fatal_queues, 0, + GENMASK(group->queue_count - 1, 0)); } tick_ctx_insert_old_group(sched, ctx, group); @@ -2173,9 +2250,9 @@ group_term_post_processing(struct panthor_group *group) struct panthor_syncobj_64b *syncobj; int err; - if (group->fatal_queues & BIT(i)) + if (atomic_read(&group->fatal_queues) & BIT(i)) err = -EINVAL; - else if (group->timedout) + else if (atomic_read(&group->timedout)) err = -ETIMEDOUT; else err = -ECANCELED; @@ -2331,16 +2408,8 @@ tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *c /* Unbind evicted groups. */ for (prio = PANTHOR_CSG_PRIORITY_COUNT - 1; prio >= 0; prio--) { - list_for_each_entry(group, &ctx->old_groups[prio], run_node) { - /* This group is gone. Process interrupts to clear - * any pending interrupts before we start the new - * group. - */ - if (group->csg_id >= 0) - sched_process_csg_irq_locked(ptdev, group->csg_id); - + list_for_each_entry(group, &ctx->old_groups[prio], run_node) group_unbind_locked(group); - } } for (i = 0; i < sched->csg_slot_count; i++) { @@ -2866,7 +2935,7 @@ void panthor_sched_suspend(struct panthor_device *ptdev) /* We consider group suspension failures as fatal and flag the * group as unusable by setting timedout=true. */ - csg_slot->group->timedout = true; + atomic_set(&csg_slot->group->timedout, true); csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id, CSG_STATE_TERMINATE, @@ -2915,10 +2984,12 @@ void panthor_sched_suspend(struct panthor_device *ptdev) u32 csg_id = ffs(slot_mask) - 1; struct panthor_csg_slot *csg_slot = &sched->csg_slots[csg_id]; - if (flush_caches_failed) + if (flush_caches_failed) { csg_slot->group->state = PANTHOR_CS_GROUP_TERMINATED; - else + } else { + guard(spinlock)(&sched->events_lock); csg_slot_sync_update_locked(ptdev, csg_id); + } slot_mask &= ~BIT(csg_id); } @@ -2932,10 +3003,6 @@ void panthor_sched_suspend(struct panthor_device *ptdev) continue; group_get(group); - - if (group->csg_id >= 0) - sched_process_csg_irq_locked(ptdev, group->csg_id); - group_unbind_locked(group); drm_WARN_ON(&group->ptdev->base, !list_empty(&group->run_node)); @@ -3018,22 +3085,6 @@ void panthor_sched_post_reset(struct panthor_device *ptdev, bool reset_failed) } } -static void update_fdinfo_stats(struct panthor_job *job) -{ - struct panthor_group *group = job->group; - struct panthor_queue *queue = group->queues[job->queue_idx]; - struct panthor_gpu_usage *fdinfo = &group->fdinfo.data; - struct panthor_job_profiling_data *slots = queue->profiling.slots->kmap; - struct panthor_job_profiling_data *data = &slots[job->profiling.slot]; - - scoped_guard(spinlock, &group->fdinfo.lock) { - if (job->profiling.mask & PANTHOR_DEVICE_PROFILING_CYCLES) - fdinfo->cycles += data->cycles.after - data->cycles.before; - if (job->profiling.mask & PANTHOR_DEVICE_PROFILING_TIMESTAMP) - fdinfo->time += data->time.after - data->time.before; - } -} - void panthor_fdinfo_gather_group_samples(struct panthor_file *pfile) { struct panthor_group_pool *gpool = pfile->groups; @@ -3054,80 +3105,6 @@ void panthor_fdinfo_gather_group_samples(struct panthor_file *pfile) xa_unlock(&gpool->xa); } -static bool queue_check_job_completion(struct panthor_queue *queue) -{ - struct panthor_syncobj_64b *syncobj = NULL; - struct panthor_job *job, *job_tmp; - bool cookie, progress = false; - LIST_HEAD(done_jobs); - - cookie = dma_fence_begin_signalling(); - scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) { - list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) { - if (!syncobj) { - struct panthor_group *group = job->group; - - syncobj = group->syncobjs->kmap + - (job->queue_idx * sizeof(*syncobj)); - } - - if (syncobj->seqno < job->done_fence->seqno) - break; - - list_move_tail(&job->node, &done_jobs); - dma_fence_signal_locked(job->done_fence); - } - - if (list_empty(&queue->fence_ctx.in_flight_jobs)) { - /* If we have no job left, we cancel the timer, and reset remaining - * time to its default so it can be restarted next time - * queue_resume_timeout() is called. - */ - queue_suspend_timeout_locked(queue); - - /* If there's no job pending, we consider it progress to avoid a - * spurious timeout if the timeout handler and the sync update - * handler raced. - */ - progress = true; - } else if (!list_empty(&done_jobs)) { - queue_reset_timeout_locked(queue); - progress = true; - } - } - dma_fence_end_signalling(cookie); - - list_for_each_entry_safe(job, job_tmp, &done_jobs, node) { - if (job->profiling.mask) - update_fdinfo_stats(job); - list_del_init(&job->node); - panthor_job_put(&job->base); - } - - return progress; -} - -static void group_sync_upd_work(struct work_struct *work) -{ - struct panthor_group *group = - container_of(work, struct panthor_group, sync_upd_work); - u32 queue_idx; - bool cookie; - - cookie = dma_fence_begin_signalling(); - for (queue_idx = 0; queue_idx < group->queue_count; queue_idx++) { - struct panthor_queue *queue = group->queues[queue_idx]; - - if (!queue) - continue; - - queue_check_job_completion(queue); - } - dma_fence_end_signalling(cookie); - - group_put(group); -} - struct panthor_job_ringbuf_instrs { u64 buffer[MAX_INSTRS_PER_JOB]; u32 count; @@ -3426,7 +3403,7 @@ queue_timedout_job(struct drm_sched_job *sched_job) queue_stop(queue, job); mutex_lock(&sched->lock); - group->timedout = true; + atomic_set(&group->timedout, true); if (group->csg_id >= 0) { sched_queue_delayed_work(ptdev->scheduler, tick, 0); } else { @@ -3678,7 +3655,6 @@ int panthor_group_create(struct panthor_file *pfile, if (!group) return -ENOMEM; - spin_lock_init(&group->fatal_lock); kref_init(&group->refcount); group->state = PANTHOR_CS_GROUP_CREATED; group->csg_id = -1; @@ -3695,7 +3671,6 @@ int panthor_group_create(struct panthor_file *pfile, INIT_LIST_HEAD(&group->wait_node); INIT_LIST_HEAD(&group->run_node); INIT_WORK(&group->term_work, group_term_work); - INIT_WORK(&group->sync_upd_work, group_sync_upd_work); INIT_WORK(&group->tiler_oom_work, group_tiler_oom_work); INIT_WORK(&group->release_work, group_release_work); @@ -3850,12 +3825,13 @@ int panthor_group_get_state(struct panthor_file *pfile, memset(get_state, 0, sizeof(*get_state)); mutex_lock(&sched->lock); - if (group->timedout) + if (atomic_read(&group->timedout)) get_state->state |= DRM_PANTHOR_GROUP_STATE_TIMEDOUT; - if (group->fatal_queues) { + + get_state->fatal_queues = atomic_read(&group->fatal_queues); + if (get_state->fatal_queues) get_state->state |= DRM_PANTHOR_GROUP_STATE_FATAL_FAULT; - get_state->fatal_queues = group->fatal_queues; - } + if (group->innocent) get_state->state |= DRM_PANTHOR_GROUP_STATE_INNOCENT; mutex_unlock(&sched->lock); @@ -4066,7 +4042,6 @@ void panthor_sched_unplug(struct panthor_device *ptdev) struct panthor_scheduler *sched = ptdev->scheduler; disable_delayed_work_sync(&sched->tick_work); - disable_work_sync(&sched->fw_events_work); disable_work_sync(&sched->sync_upd_work); mutex_lock(&sched->lock); @@ -4151,7 +4126,8 @@ int panthor_sched_init(struct panthor_device *ptdev) sched->tick_period = msecs_to_jiffies(10); INIT_DELAYED_WORK(&sched->tick_work, tick_work); INIT_WORK(&sched->sync_upd_work, sync_upd_work); - INIT_WORK(&sched->fw_events_work, process_fw_events_work); + + spin_lock_init(&sched->events_lock); ret = drmm_mutex_init(&ptdev->base, &sched->lock); if (ret) diff --git a/drivers/gpu/drm/panthor/panthor_trace.h b/drivers/gpu/drm/panthor/panthor_trace.h index 6ffeb4fe6599..bd8652549ab4 100644 --- a/drivers/gpu/drm/panthor/panthor_trace.h +++ b/drivers/gpu/drm/panthor/panthor_trace.h @@ -76,6 +76,44 @@ TRACE_EVENT(gpu_job_irq, __entry->events, __entry->duration_ns) ); +/** + * gpu_cache_flush - emitted after cache flush completes + * @dev: pointer to the &struct device, for printing the device name + * @l2: "l2" flush flags + * @lsc: "lsc" flush flags + * @other: "other" flush flags + * @duration_ns: how long the cache flush operation took, in nanoseconds + * @ret: return status, 0 == success, negative errno on error + * + * Begins measuring after any initial lock contention around the locks needed + * for flushing caches, but before the actual cache flush is requested. Stops + * measuring and is emitted after flush operation is over. + */ +TRACE_EVENT(gpu_cache_flush, + TP_PROTO(const struct device *dev, u32 l2, u32 lsc, u32 other, + u32 duration_ns, int ret), + TP_ARGS(dev, l2, lsc, other, duration_ns, ret), + TP_STRUCT__entry( + __string(dev_name, dev_name(dev)) + __field(u32, l2) + __field(u32, lsc) + __field(u32, other) + __field(u32, duration_ns) + __field(int, ret) + ), + TP_fast_assign( + __assign_str(dev_name); + __entry->l2 = l2; + __entry->lsc = lsc; + __entry->other = other; + __entry->duration_ns = duration_ns; + __entry->ret = ret; + ), + TP_printk("%s: l2=0x%x lsc=0x%x other=0x%x duration_ns=%u ret=%d", + __get_str(dev_name), __entry->l2, __entry->lsc, + __entry->other, __entry->duration_ns, __entry->ret) +); + #endif /* __PANTHOR_TRACE_H__ */ #undef TRACE_INCLUDE_PATH diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 2fc41fb90aaa..f657fe24fc88 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -418,7 +418,7 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .destroy = qxl_crtc_destroy, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, DRM_CRTC_VBLANK_TIMER_FUNCS, diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c index 6c07c406f725..59c2a4b76137 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c @@ -997,23 +997,20 @@ static void rcar_du_crtc_cleanup(struct drm_crtc *crtc) drm_crtc_cleanup(crtc); } -static void rcar_du_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *rcar_du_crtc_create_state(struct drm_crtc *crtc) { struct rcar_du_crtc_state *state; - if (crtc->state) { - rcar_du_crtc_atomic_destroy_state(crtc, crtc->state); - crtc->state = NULL; - } - state = kzalloc_obj(*state); if (state == NULL) - return; + return ERR_PTR(-ENOMEM); state->crc.source = VSP1_DU_CRC_NONE; state->crc.index = 0; - __drm_atomic_helper_crtc_reset(crtc, &state->state); + __drm_atomic_helper_crtc_state_init(&state->state, crtc); + + return &state->state; } static int rcar_du_crtc_enable_vblank(struct drm_crtc *crtc) @@ -1155,7 +1152,7 @@ unlock: } static const struct drm_crtc_funcs crtc_funcs_gen2 = { - .reset = rcar_du_crtc_reset, + .atomic_create_state = rcar_du_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, @@ -1166,7 +1163,7 @@ static const struct drm_crtc_funcs crtc_funcs_gen2 = { }; static const struct drm_crtc_funcs crtc_funcs_gen3 = { - .reset = rcar_du_crtc_reset, + .atomic_create_state = rcar_du_crtc_create_state, .destroy = rcar_du_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c index d0f01aa642a7..ab2f24cc7683 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c @@ -332,20 +332,17 @@ static void rzg2l_du_crtc_atomic_destroy_state(struct drm_crtc *crtc, kfree(to_rzg2l_crtc_state(state)); } -static void rzg2l_du_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *rzg2l_du_crtc_create_state(struct drm_crtc *crtc) { struct rzg2l_du_crtc_state *state; - if (crtc->state) { - rzg2l_du_crtc_atomic_destroy_state(crtc, crtc->state); - crtc->state = NULL; - } - state = kzalloc_obj(*state); if (!state) - return; + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_crtc_state_init(&state->state, crtc); - __drm_atomic_helper_crtc_reset(crtc, &state->state); + return &state->state; } static int rzg2l_du_crtc_enable_vblank(struct drm_crtc *crtc) @@ -365,7 +362,7 @@ static void rzg2l_du_crtc_disable_vblank(struct drm_crtc *crtc) } static const struct drm_crtc_funcs crtc_funcs_rz = { - .reset = rzg2l_du_crtc_reset, + .atomic_create_state = rzg2l_du_crtc_create_state, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, .atomic_duplicate_state = rzg2l_du_crtc_atomic_duplicate_state, diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c index 1a2b9b68af6f..328a36215e39 100644 --- a/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c @@ -21,6 +21,7 @@ #include <drm/drm_bridge_connector.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_encoder.h> #include <drm/drm_fb_dma_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> @@ -29,7 +30,6 @@ #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> #include <video/videomode.h> @@ -364,7 +364,7 @@ static void shmob_drm_disable_vblank(struct drm_crtc *crtc) } static const struct drm_crtc_funcs crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = shmob_drm_crtc_page_flip, @@ -436,6 +436,10 @@ static const struct drm_encoder_helper_funcs encoder_helper_funcs = { .mode_fixup = shmob_drm_encoder_mode_fixup, }; +static const struct drm_encoder_funcs shmob_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + /* ----------------------------------------------------------------------------- * Encoder */ @@ -448,8 +452,8 @@ int shmob_drm_encoder_create(struct shmob_drm_device *sdev) encoder->possible_crtcs = 1; - ret = drm_simple_encoder_init(&sdev->ddev, encoder, - DRM_MODE_ENCODER_DPI); + ret = drm_encoder_init(&sdev->ddev, encoder, &shmob_encoder_funcs, + DRM_MODE_ENCODER_DPI, NULL); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 0090d8ff0c79..bd02597dc2d7 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -1656,17 +1656,17 @@ static void vop_crtc_destroy_state(struct drm_crtc *crtc, kfree(s); } -static void vop_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *vop_crtc_create_state(struct drm_crtc *crtc) { - struct rockchip_crtc_state *crtc_state = kzalloc_obj(*crtc_state); + struct rockchip_crtc_state *crtc_state; - if (crtc->state) - vop_crtc_destroy_state(crtc, crtc->state); + crtc_state = kzalloc_obj(*crtc_state); + if (!crtc_state) + return ERR_PTR(-ENOMEM); - if (crtc_state) - __drm_atomic_helper_crtc_reset(crtc, &crtc_state->base); - else - __drm_atomic_helper_crtc_reset(crtc, NULL); + __drm_atomic_helper_crtc_state_init(&crtc_state->base, crtc); + + return &crtc_state->base; } #ifdef CONFIG_DRM_ANALOGIX_DP @@ -1738,7 +1738,7 @@ static const struct drm_crtc_funcs vop_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, .destroy = drm_crtc_cleanup, - .reset = vop_crtc_reset, + .atomic_create_state = vop_crtc_create_state, .atomic_duplicate_state = vop_crtc_duplicate_state, .atomic_destroy_state = vop_crtc_destroy_state, .enable_vblank = vop_crtc_enable_vblank, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 4cce3e336f5b..b2f3a579bbe5 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -2300,24 +2300,24 @@ static void vop2_crtc_destroy_state(struct drm_crtc *crtc, kfree(vcstate); } -static void vop2_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *vop2_crtc_create_state(struct drm_crtc *crtc) { - struct rockchip_crtc_state *vcstate = kzalloc_obj(*vcstate); + struct rockchip_crtc_state *vcstate; - if (crtc->state) - vop2_crtc_destroy_state(crtc, crtc->state); + vcstate = kzalloc_obj(*vcstate); + if (!vcstate) + return ERR_PTR(-ENOMEM); - if (vcstate) - __drm_atomic_helper_crtc_reset(crtc, &vcstate->base); - else - __drm_atomic_helper_crtc_reset(crtc, NULL); + __drm_atomic_helper_crtc_state_init(&vcstate->base, crtc); + + return &vcstate->base; } static const struct drm_crtc_funcs vop2_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, .destroy = drm_crtc_cleanup, - .reset = vop2_crtc_reset, + .atomic_create_state = vop2_crtc_create_state, .atomic_duplicate_state = vop2_crtc_duplicate_state, .atomic_destroy_state = vop2_crtc_destroy_state, .enable_vblank = vop2_crtc_enable_vblank, diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index a4a7efdbf229..bf97508a45b9 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -207,14 +207,18 @@ EXPORT_SYMBOL(drm_sched_entity_modify_sched); static bool drm_sched_entity_is_idle(struct drm_sched_entity *entity) { - rmb(); /* for list_empty to work without lock */ + bool idle = false; + + spin_lock(&entity->lock); if (list_empty(&entity->list) || spsc_queue_count(&entity->job_queue) == 0 || entity->stopped) - return true; + idle = true; - return false; + spin_unlock(&entity->lock); + + return idle; } /** diff --git a/drivers/gpu/drm/scheduler/tests/tests_basic.c b/drivers/gpu/drm/scheduler/tests/tests_basic.c index da5c4952780f..34bbb2c27913 100644 --- a/drivers/gpu/drm/scheduler/tests/tests_basic.c +++ b/drivers/gpu/drm/scheduler/tests/tests_basic.c @@ -90,7 +90,7 @@ static const struct drm_sched_basic_params drm_sched_basic_cases[] = { .num_entities = 1, }, { - .description = "A chain of dependent jobs across multiple entities", + .description = "A chain of dependent jobs in a single entity", .queue_depth = 100, .job_us = 1000, .num_entities = 1, diff --git a/drivers/gpu/drm/sitronix/st7571.c b/drivers/gpu/drm/sitronix/st7571.c index 20954c33eca9..fc3ba5e8600f 100644 --- a/drivers/gpu/drm/sitronix/st7571.c +++ b/drivers/gpu/drm/sitronix/st7571.c @@ -424,7 +424,7 @@ static const struct drm_crtc_helper_funcs st7571_crtc_helper_funcs = { }; static const struct drm_crtc_funcs st7571_crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/sitronix/st7920.c b/drivers/gpu/drm/sitronix/st7920.c index d320391801f3..0a75e9319080 100644 --- a/drivers/gpu/drm/sitronix/st7920.c +++ b/drivers/gpu/drm/sitronix/st7920.c @@ -453,17 +453,17 @@ static void st7920_primary_plane_atomic_disable(struct drm_plane *plane, } /* Called during init to allocate the plane's atomic state. */ -static void st7920_primary_plane_reset(struct drm_plane *plane) +static struct drm_plane_state *st7920_primary_plane_create_state(struct drm_plane *plane) { struct st7920_plane_state *st7920_state; - drm_WARN_ON_ONCE(plane->dev, plane->state); - st7920_state = kzalloc_obj(*st7920_state); if (!st7920_state) - return; + return ERR_PTR(-ENOMEM); + + __drm_gem_shadow_plane_state_init(plane, &st7920_state->base); - __drm_gem_reset_shadow_plane(plane, &st7920_state->base); + return &(&st7920_state->base)->base; } static struct drm_plane_state *st7920_primary_plane_duplicate_state(struct drm_plane *plane) @@ -507,7 +507,7 @@ static const struct drm_plane_helper_funcs st7920_primary_plane_helper_funcs = { static const struct drm_plane_funcs st7920_primary_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .reset = st7920_primary_plane_reset, + .atomic_create_state = st7920_primary_plane_create_state, .atomic_duplicate_state = st7920_primary_plane_duplicate_state, .atomic_destroy_state = st7920_primary_plane_destroy_state, .destroy = drm_plane_cleanup, diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index 0b0fc6fe3df2..feec4933e3e6 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -1310,17 +1310,17 @@ static void ssd133x_primary_plane_atomic_disable(struct drm_plane *plane, } /* Called during init to allocate the plane's atomic state. */ -static void ssd130x_primary_plane_reset(struct drm_plane *plane) +static struct drm_plane_state *ssd130x_primary_plane_create_state(struct drm_plane *plane) { struct ssd130x_plane_state *ssd130x_state; - drm_WARN_ON_ONCE(plane->dev, plane->state); - ssd130x_state = kzalloc_obj(*ssd130x_state); if (!ssd130x_state) - return; + return ERR_PTR(-ENOMEM); + + __drm_gem_shadow_plane_state_init(plane, &ssd130x_state->base); - __drm_gem_reset_shadow_plane(plane, &ssd130x_state->base); + return &(&ssd130x_state->base)->base; } static struct drm_plane_state *ssd130x_primary_plane_duplicate_state(struct drm_plane *plane) @@ -1383,7 +1383,7 @@ static const struct drm_plane_helper_funcs ssd130x_primary_plane_helper_funcs[] static const struct drm_plane_funcs ssd130x_primary_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .reset = ssd130x_primary_plane_reset, + .atomic_create_state = ssd130x_primary_plane_create_state, .atomic_duplicate_state = ssd130x_primary_plane_duplicate_state, .atomic_destroy_state = ssd130x_primary_plane_destroy_state, .destroy = drm_plane_cleanup, @@ -1467,7 +1467,7 @@ static int ssd133x_crtc_atomic_check(struct drm_crtc *crtc, } /* Called during init to allocate the CRTC's atomic state. */ -static void ssd130x_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *ssd130x_crtc_create_state(struct drm_crtc *crtc) { struct ssd130x_crtc_state *ssd130x_state; @@ -1475,9 +1475,11 @@ static void ssd130x_crtc_reset(struct drm_crtc *crtc) ssd130x_state = kzalloc_obj(*ssd130x_state); if (!ssd130x_state) - return; + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_crtc_state_init(&ssd130x_state->base, crtc); - __drm_atomic_helper_crtc_reset(crtc, &ssd130x_state->base); + return &ssd130x_state->base; } static struct drm_crtc_state *ssd130x_crtc_duplicate_state(struct drm_crtc *crtc) @@ -1534,7 +1536,7 @@ static const struct drm_crtc_helper_funcs ssd130x_crtc_helper_funcs[] = { }; static const struct drm_crtc_funcs ssd130x_crtc_funcs = { - .reset = ssd130x_crtc_reset, + .atomic_create_state = ssd130x_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c index 66990398b903..7f0bc1e31076 100644 --- a/drivers/gpu/drm/sprd/sprd_dpu.c +++ b/drivers/gpu/drm/sprd/sprd_dpu.c @@ -707,7 +707,7 @@ static const struct drm_crtc_funcs sprd_crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .enable_vblank = sprd_crtc_enable_vblank, diff --git a/drivers/gpu/drm/sti/sti_crtc.c b/drivers/gpu/drm/sti/sti_crtc.c index f0e98653d35a..c69612728f2b 100644 --- a/drivers/gpu/drm/sti/sti_crtc.c +++ b/drivers/gpu/drm/sti/sti_crtc.c @@ -328,7 +328,7 @@ static const struct drm_crtc_funcs sti_crtc_funcs = { .page_flip = drm_atomic_helper_page_flip, .destroy = sti_crtc_destroy, .set_property = sti_crtc_set_property, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .late_register = sti_crtc_late_register, diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index 8818dcf98da7..775846bb502b 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -1209,7 +1209,7 @@ static void ltdc_crtc_atomic_print_state(struct drm_printer *p, static const struct drm_crtc_funcs ltdc_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .enable_vblank = ltdc_crtc_enable_vblank, @@ -1221,7 +1221,7 @@ static const struct drm_crtc_funcs ltdc_crtc_funcs = { static const struct drm_crtc_funcs ltdc_crtc_with_crc_support_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .enable_vblank = ltdc_crtc_enable_vblank, diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c index 3fa1f4fda0f0..e6a20e2f0ade 100644 --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c @@ -183,7 +183,7 @@ static const struct drm_crtc_funcs sun4i_crtc_funcs = { .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .destroy = drm_crtc_cleanup, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .set_config = drm_atomic_helper_set_config, .enable_vblank = sun4i_crtc_enable_vblank, .disable_vblank = sun4i_crtc_disable_vblank, diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c index 97253d631b2b..91844a396a2b 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c @@ -292,6 +292,7 @@ static void sun4i_hdmi_connector_reset(struct drm_connector *connector) } static const struct drm_connector_funcs sun4i_hdmi_connector_funcs = { + .debugfs_init = drm_hdmi_connector_debugfs_init, .detect = sun4i_hdmi_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .reset = sun4i_hdmi_connector_reset, diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c index 00756ab78b4e..bad102134726 100644 --- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c @@ -213,7 +213,7 @@ static const struct drm_plane_funcs sun8i_ui_layer_funcs = { .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .destroy = drm_plane_cleanup, .disable_plane = drm_atomic_helper_disable_plane, - .reset = drm_atomic_helper_plane_reset, + .atomic_create_state = drm_atomic_helper_plane_create_state, .update_plane = drm_atomic_helper_update_plane, }; diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c index 09f668c8af24..2e9cda45c04e 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c @@ -293,7 +293,7 @@ static const struct drm_plane_funcs sun8i_vi_layer_funcs = { .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .destroy = drm_plane_cleanup, .disable_plane = drm_atomic_helper_disable_plane, - .reset = drm_atomic_helper_plane_reset, + .atomic_create_state = drm_atomic_helper_plane_create_state, .update_plane = drm_atomic_helper_update_plane, }; diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h index 547f2327af5e..f2a3717be670 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h +++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h @@ -134,15 +134,15 @@ int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane, .atomic_disable = drm_sysfb_plane_helper_atomic_disable, \ .get_scanout_buffer = drm_sysfb_plane_helper_get_scanout_buffer -void drm_sysfb_plane_reset(struct drm_plane *plane); +struct drm_plane_state *drm_sysfb_plane_atomic_create_state(struct drm_plane *plane); struct drm_plane_state *drm_sysfb_plane_atomic_duplicate_state(struct drm_plane *plane); void drm_sysfb_plane_atomic_destroy_state(struct drm_plane *plane, struct drm_plane_state *plane_state); #define DRM_SYSFB_PLANE_FUNCS \ - .reset = drm_sysfb_plane_reset, \ .update_plane = drm_atomic_helper_update_plane, \ .disable_plane = drm_atomic_helper_disable_plane, \ + .atomic_create_state = drm_sysfb_plane_atomic_create_state, \ .atomic_duplicate_state = drm_sysfb_plane_atomic_duplicate_state, \ .atomic_destroy_state = drm_sysfb_plane_atomic_destroy_state @@ -171,12 +171,12 @@ int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_ .mode_valid = drm_sysfb_crtc_helper_mode_valid, \ .atomic_check = drm_sysfb_crtc_helper_atomic_check -void drm_sysfb_crtc_reset(struct drm_crtc *crtc); +struct drm_crtc_state *drm_sysfb_crtc_create_state(struct drm_crtc *crtc); struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc); void drm_sysfb_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state); #define DRM_SYSFB_CRTC_FUNCS \ - .reset = drm_sysfb_crtc_reset, \ + .atomic_create_state = drm_sysfb_crtc_create_state, \ .set_config = drm_atomic_helper_set_config, \ .page_flip = drm_atomic_helper_page_flip, \ .atomic_duplicate_state = drm_sysfb_crtc_atomic_duplicate_state, \ diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c index d2de29caf89e..b92cc4b7d2ad 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c +++ b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c @@ -376,28 +376,28 @@ void drm_sysfb_plane_helper_atomic_disable(struct drm_plane *plane, struct drm_device *dev = plane->dev; struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); struct iosys_map dst = sysfb->fb_addr; - struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */ unsigned int dst_pitch = sysfb->fb_pitch; const struct drm_format_info *dst_format = sysfb->fb_format; struct drm_rect dst_clip; - unsigned long lines, linepixels, i; + unsigned long lines, linepixels, dst_size, i; int idx; - drm_rect_init(&dst_clip, - plane_state->src_x >> 16, plane_state->src_y >> 16, - plane_state->src_w >> 16, plane_state->src_h >> 16); + drm_rect_init(&dst_clip, 0, 0, + sysfb->fb_mode.hdisplay, sysfb->fb_mode.vdisplay); lines = drm_rect_height(&dst_clip); linepixels = drm_rect_width(&dst_clip); + dst_size = min_t(unsigned long, + drm_format_info_min_pitch(dst_format, 0, linepixels), + dst_pitch); if (!drm_dev_enter(dev, &idx)) return; /* Clear buffer to black if disabled */ - dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip); for (i = 0; i < lines; ++i) { - memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]); + memset_io(dst_vmap, 0, dst_size); dst_vmap += dst_pitch; } @@ -420,20 +420,32 @@ int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane, } EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer); -void drm_sysfb_plane_reset(struct drm_plane *plane) +/** + * drm_sysfb_plane_atomic_create_state - creates sysfb plane state + * @plane: the plane + * + * This function implements struct &drm_plane_funcs.atomic_create_state for + * sysfb planes. It allocates the plane state of type + * struct drm_sysfb_plane_state. + * + * Returns: + * A pointer to a new plane state on success, or an ERR_PTR()-encoded + * error code otherwise. + */ +struct drm_plane_state * +drm_sysfb_plane_atomic_create_state(struct drm_plane *plane) { struct drm_sysfb_plane_state *sysfb_plane_state; - if (plane->state) - drm_sysfb_plane_state_destroy(to_drm_sysfb_plane_state(plane->state)); - sysfb_plane_state = kzalloc_obj(*sysfb_plane_state); - if (sysfb_plane_state) - __drm_gem_reset_shadow_plane(plane, &sysfb_plane_state->base); - else - __drm_gem_reset_shadow_plane(plane, NULL); + if (!sysfb_plane_state) + return ERR_PTR(-ENOMEM); + + __drm_gem_shadow_plane_state_init(plane, &sysfb_plane_state->base); + + return &sysfb_plane_state->base.base; } -EXPORT_SYMBOL(drm_sysfb_plane_reset); +EXPORT_SYMBOL(drm_sysfb_plane_atomic_create_state); struct drm_plane_state *drm_sysfb_plane_atomic_duplicate_state(struct drm_plane *plane) { @@ -515,23 +527,21 @@ int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_ } EXPORT_SYMBOL(drm_sysfb_crtc_helper_atomic_check); -void drm_sysfb_crtc_reset(struct drm_crtc *crtc) +struct drm_crtc_state *drm_sysfb_crtc_create_state(struct drm_crtc *crtc) { struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev); struct drm_sysfb_crtc_state *sysfb_crtc_state; - if (crtc->state) - drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc->state)); - sysfb_crtc_state = kzalloc_obj(*sysfb_crtc_state); - if (sysfb_crtc_state) { - sysfb_crtc_state->format = sysfb->fb_format; - __drm_atomic_helper_crtc_reset(crtc, &sysfb_crtc_state->base); - } else { - __drm_atomic_helper_crtc_reset(crtc, NULL); - } + if (!sysfb_crtc_state) + return ERR_PTR(-ENOMEM); + + sysfb_crtc_state->format = sysfb->fb_format; + __drm_atomic_helper_crtc_state_init(&sysfb_crtc_state->base, crtc); + + return &sysfb_crtc_state->base; } -EXPORT_SYMBOL(drm_sysfb_crtc_reset); +EXPORT_SYMBOL(drm_sysfb_crtc_create_state); struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc) { diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index fefc3761a4bc..b0bfa946e697 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1492,17 +1492,17 @@ static void tegra_dc_destroy(struct drm_crtc *crtc) drm_crtc_cleanup(crtc); } -static void tegra_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *tegra_crtc_create_state(struct drm_crtc *crtc) { - struct tegra_dc_state *state = kzalloc_obj(*state); + struct tegra_dc_state *state; - if (crtc->state) - tegra_crtc_atomic_destroy_state(crtc, crtc->state); + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); - if (state) - __drm_atomic_helper_crtc_reset(crtc, &state->base); - else - __drm_atomic_helper_crtc_reset(crtc, NULL); + __drm_atomic_helper_crtc_state_init(&state->base, crtc); + + return &state->base; } static struct drm_crtc_state * @@ -1911,7 +1911,7 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = { .page_flip = drm_atomic_helper_page_flip, .set_config = drm_atomic_helper_set_config, .destroy = tegra_dc_destroy, - .reset = tegra_crtc_reset, + .atomic_create_state = tegra_crtc_create_state, .atomic_duplicate_state = tegra_crtc_atomic_duplicate_state, .atomic_destroy_state = tegra_crtc_atomic_destroy_state, .late_register = tegra_dc_late_register, diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index cb88aafbd36f..7b595310e737 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -20,11 +20,11 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_debugfs.h> +#include <drm/drm_encoder.h> #include <drm/drm_file.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_panel.h> #include <drm/drm_print.h> -#include <drm/drm_simple_kms_helper.h> #include "dc.h" #include "drm.h" @@ -1055,6 +1055,10 @@ tegra_dsi_encoder_atomic_check(struct drm_encoder *encoder, return err; } +static const struct drm_encoder_funcs tegra_dsi_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = { .disable = tegra_dsi_encoder_disable, .enable = tegra_dsi_encoder_enable, @@ -1078,8 +1082,14 @@ static int tegra_dsi_init(struct host1x_client *client) &tegra_dsi_connector_helper_funcs); dsi->output.connector.dpms = DRM_MODE_DPMS_OFF; - drm_simple_encoder_init(drm, &dsi->output.encoder, - DRM_MODE_ENCODER_DSI); + err = drm_encoder_init(drm, &dsi->output.encoder, + &tegra_dsi_encoder_funcs, + DRM_MODE_ENCODER_DSI, NULL); + if (err) { + drm_err(drm, "failed to initialize encoder: %d\n", err); + return err; + } + drm_encoder_helper_add(&dsi->output.encoder, &tegra_dsi_encoder_helper_funcs); diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index 0cb30910773f..2148b0c59815 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c @@ -24,28 +24,24 @@ static void tegra_plane_destroy(struct drm_plane *plane) kfree(p); } -static void tegra_plane_reset(struct drm_plane *plane) +static struct drm_plane_state *tegra_plane_atomic_create_state(struct drm_plane *plane) { struct tegra_plane *p = to_tegra_plane(plane); struct tegra_plane_state *state; unsigned int i; - if (plane->state) - __drm_atomic_helper_plane_destroy_state(plane->state); + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); - kfree(plane->state); - plane->state = NULL; + state->base.plane = plane; + state->base.zpos = p->index; + state->base.normalized_zpos = p->index; - state = kzalloc_obj(*state); - if (state) { - plane->state = &state->base; - plane->state->plane = plane; - plane->state->zpos = p->index; - plane->state->normalized_zpos = p->index; - - for (i = 0; i < 3; i++) - state->iova[i] = DMA_MAPPING_ERROR; - } + for (i = 0; i < 3; i++) + state->iova[i] = DMA_MAPPING_ERROR; + + return &state->base; } static struct drm_plane_state * @@ -131,7 +127,7 @@ const struct drm_plane_funcs tegra_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = tegra_plane_destroy, - .reset = tegra_plane_reset, + .atomic_create_state = tegra_plane_atomic_create_state, .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, .atomic_destroy_state = tegra_plane_atomic_destroy_state, .format_mod_supported = tegra_plane_format_mod_supported, diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index e67fbb2362e6..bc1c93c7554c 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -9,7 +9,8 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge_connector.h> -#include <drm/drm_simple_kms_helper.h> +#include <drm/drm_encoder.h> +#include <drm/drm_print.h> #include "drm.h" #include "dc.h" @@ -194,6 +195,10 @@ tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder, return err; } +static const struct drm_encoder_funcs tegra_rgb_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = { .disable = tegra_rgb_encoder_disable, .enable = tegra_rgb_encoder_enable, @@ -305,7 +310,13 @@ int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc) if (!dc->rgb) return -ENODEV; - drm_simple_encoder_init(drm, &output->encoder, DRM_MODE_ENCODER_LVDS); + err = drm_encoder_init(drm, &output->encoder, &tegra_rgb_encoder_funcs, + DRM_MODE_ENCODER_LVDS, NULL); + if (err) { + drm_err(drm, "failed to initialize encoder: %d\n", err); + return err; + } + drm_encoder_helper_add(&output->encoder, &tegra_rgb_encoder_helper_funcs); diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index 727766444d63..1afa9b162355 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -217,7 +217,7 @@ static const struct drm_crtc_helper_funcs default_crtc_helper_funcs = { static const struct drm_crtc_funcs default_crtc_funcs = { .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, }; /** diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c index 58d5eb033bdb..2a0ec11a6c90 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.c +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -2157,47 +2157,15 @@ int dispc_plane_check(struct dispc_device *dispc, u32 hw_plane, return 0; } -static -dma_addr_t dispc_plane_state_dma_addr(const struct drm_plane_state *state) -{ - struct drm_framebuffer *fb = state->fb; - struct drm_gem_dma_object *gem; - u32 x = state->src_x >> 16; - u32 y = state->src_y >> 16; - - gem = drm_fb_dma_get_gem_obj(state->fb, 0); - - return gem->dma_addr + fb->offsets[0] + x * fb->format->cpp[0] + - y * fb->pitches[0]; -} - -static -dma_addr_t dispc_plane_state_p_uv_addr(const struct drm_plane_state *state) -{ - struct drm_framebuffer *fb = state->fb; - struct drm_gem_dma_object *gem; - u32 x = state->src_x >> 16; - u32 y = state->src_y >> 16; - - if (WARN_ON(state->fb->format->num_planes != 2)) - return 0; - - gem = drm_fb_dma_get_gem_obj(fb, 1); - - return gem->dma_addr + fb->offsets[1] + - (x * fb->format->cpp[1] / fb->format->hsub) + - (y * fb->pitches[1] / fb->format->vsub); -} - void dispc_plane_setup(struct dispc_device *dispc, u32 hw_plane, - const struct drm_plane_state *state, + struct drm_plane_state *state, u32 hw_videoport) { bool lite = dispc->feat->vid_info[hw_plane].is_lite; u32 fourcc = state->fb->format->format; u16 cpp = state->fb->format->cpp[0]; u32 fb_width = state->fb->pitches[0] / cpp; - dma_addr_t dma_addr = dispc_plane_state_dma_addr(state); + dma_addr_t dma_addr = drm_fb_dma_get_gem_addr(state->fb, state, 0); struct dispc_scaling_params scale; dispc_vid_calc_scaling(dispc, state, &scale, lite); @@ -2229,7 +2197,7 @@ void dispc_plane_setup(struct dispc_device *dispc, u32 hw_plane, if (state->fb->format->num_planes == 2) { u16 cpp_uv = state->fb->format->cpp[1]; u32 fb_width_uv = state->fb->pitches[1] / cpp_uv; - dma_addr_t p_uv_addr = dispc_plane_state_p_uv_addr(state); + dma_addr_t p_uv_addr = drm_fb_dma_get_gem_addr(state->fb, state, 1); dispc_vid_write(dispc, hw_plane, DISPC_VID_BA_UV_0, p_uv_addr & 0xffffffff); diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h index 739d211d0018..2183254f1ab7 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.h +++ b/drivers/gpu/drm/tidss/tidss_dispc.h @@ -140,7 +140,7 @@ int dispc_plane_check(struct dispc_device *dispc, u32 hw_plane, const struct drm_plane_state *state, u32 hw_videoport); void dispc_plane_setup(struct dispc_device *dispc, u32 hw_plane, - const struct drm_plane_state *state, + struct drm_plane_state *state, u32 hw_videoport); void dispc_plane_enable(struct dispc_device *dispc, u32 hw_plane, bool enable); const u32 *dispc_plane_formats(struct dispc_device *dispc, unsigned int *len); diff --git a/drivers/gpu/drm/tidss/tidss_encoder.c b/drivers/gpu/drm/tidss/tidss_encoder.c index 698f8d964ca0..10dbcc6cdf6a 100644 --- a/drivers/gpu/drm/tidss/tidss_encoder.c +++ b/drivers/gpu/drm/tidss/tidss_encoder.c @@ -9,11 +9,11 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_bridge_connector.h> +#include <drm/drm_encoder.h> #include <drm/drm_crtc.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_panel.h> #include <drm/drm_of.h> -#include <drm/drm_simple_kms_helper.h> #include "tidss_crtc.h" #include "tidss_drv.h" @@ -81,6 +81,10 @@ static const struct drm_bridge_funcs tidss_bridge_funcs = { .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, }; +static const struct drm_encoder_funcs tidss_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + int tidss_encoder_create(struct tidss_device *tidss, struct drm_bridge *next_bridge, u32 encoder_type, u32 possible_crtcs) @@ -95,8 +99,8 @@ int tidss_encoder_create(struct tidss_device *tidss, if (IS_ERR(t_enc)) return PTR_ERR(t_enc); - ret = drm_simple_encoder_init(&tidss->ddev, &t_enc->encoder, - encoder_type); + ret = drm_encoder_init(&tidss->ddev, &t_enc->encoder, + &tidss_encoder_funcs, encoder_type, NULL); if (ret) return ret; diff --git a/drivers/gpu/drm/tiny/appletbdrm.c b/drivers/gpu/drm/tiny/appletbdrm.c index ef71b9957961..4297d9c1125e 100644 --- a/drivers/gpu/drm/tiny/appletbdrm.c +++ b/drivers/gpu/drm/tiny/appletbdrm.c @@ -505,17 +505,17 @@ static void appletbdrm_primary_plane_helper_atomic_disable(struct drm_plane *pla drm_dev_exit(idx); } -static void appletbdrm_primary_plane_reset(struct drm_plane *plane) +static struct drm_plane_state *appletbdrm_primary_plane_create_state(struct drm_plane *plane) { struct appletbdrm_plane_state *appletbdrm_state; - WARN_ON(plane->state); - appletbdrm_state = kzalloc_obj(*appletbdrm_state); if (!appletbdrm_state) - return; + return ERR_PTR(-ENOMEM); + + __drm_gem_shadow_plane_state_init(plane, &appletbdrm_state->base); - __drm_gem_reset_shadow_plane(plane, &appletbdrm_state->base); + return &(&appletbdrm_state->base)->base; } static struct drm_plane_state *appletbdrm_primary_plane_duplicate_state(struct drm_plane *plane) @@ -568,7 +568,7 @@ static const struct drm_plane_helper_funcs appletbdrm_primary_plane_helper_funcs static const struct drm_plane_funcs appletbdrm_primary_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .reset = appletbdrm_primary_plane_reset, + .atomic_create_state = appletbdrm_primary_plane_create_state, .atomic_duplicate_state = appletbdrm_primary_plane_duplicate_state, .atomic_destroy_state = appletbdrm_primary_plane_destroy_state, .destroy = drm_plane_cleanup, @@ -605,7 +605,7 @@ static const struct drm_crtc_helper_funcs appletbdrm_crtc_helper_funcs = { }; static const struct drm_crtc_funcs appletbdrm_crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c index e2d957e51505..524d387f1d53 100644 --- a/drivers/gpu/drm/tiny/bochs.c +++ b/drivers/gpu/drm/tiny/bochs.c @@ -550,7 +550,7 @@ static const struct drm_crtc_helper_funcs bochs_crtc_helper_funcs = { }; static const struct drm_crtc_funcs bochs_crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/tiny/cirrus-qemu.c b/drivers/gpu/drm/tiny/cirrus-qemu.c index 3bf23fcf6574..63c242e25662 100644 --- a/drivers/gpu/drm/tiny/cirrus-qemu.c +++ b/drivers/gpu/drm/tiny/cirrus-qemu.c @@ -419,7 +419,7 @@ static const struct drm_crtc_helper_funcs cirrus_crtc_helper_funcs = { }; static const struct drm_crtc_funcs cirrus_crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/tiny/pixpaper.c b/drivers/gpu/drm/tiny/pixpaper.c index d02ac26d007c..b69e7c30607e 100644 --- a/drivers/gpu/drm/tiny/pixpaper.c +++ b/drivers/gpu/drm/tiny/pixpaper.c @@ -963,7 +963,7 @@ static const struct drm_plane_helper_funcs pixpaper_plane_helper_funcs = { static const struct drm_crtc_funcs pixpaper_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, diff --git a/drivers/gpu/drm/tiny/sharp-memory.c b/drivers/gpu/drm/tiny/sharp-memory.c index 7efd7b567f3b..434e73bd25a5 100644 --- a/drivers/gpu/drm/tiny/sharp-memory.c +++ b/drivers/gpu/drm/tiny/sharp-memory.c @@ -345,7 +345,7 @@ static const struct drm_crtc_helper_funcs sharp_memory_crtc_helper_funcs = { }; static const struct drm_crtc_funcs sharp_memory_crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index ef56c18ded1b..1b9477381a1d 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -506,7 +506,6 @@ struct ttm_bo_alloc_state { * * @bo: The buffer to allocate the backing store of * @place: The place to attempt allocation in - * @ctx: ttm_operation_ctx associated with this allocation * @force_space: If we should evict buffers to force space * @res: On allocation success, the resulting struct ttm_resource. * @alloc_state: Object holding allocation state such as charged cgroups. @@ -1450,7 +1449,7 @@ out: return ret; } -const struct ttm_lru_walk_ops ttm_swap_ops = { +static const struct ttm_lru_walk_ops ttm_swap_ops = { .process_bo = ttm_bo_swapout_cb, }; diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index 289711035b67..dee085406ac2 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -400,7 +400,7 @@ static const struct drm_crtc_helper_funcs udl_crtc_helper_funcs = { }; static const struct drm_crtc_funcs udl_crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/vboxvideo/vbox_mode.c b/drivers/gpu/drm/vboxvideo/vbox_mode.c index 8e4e5fc9d3c5..d2d75367b077 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_mode.c +++ b/drivers/gpu/drm/vboxvideo/vbox_mode.c @@ -250,7 +250,7 @@ static const struct drm_crtc_funcs vbox_crtc_funcs = { .page_flip = drm_atomic_helper_page_flip, /* .gamma_set = vbox_crtc_gamma_set, */ .destroy = vbox_crtc_destroy, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, }; diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c b/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c index ade3309ae042..247863f8e2c5 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c +++ b/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c @@ -14,7 +14,7 @@ static const struct drm_crtc_helper_funcs vc4_dummy_crtc_helper_funcs = { static const struct drm_crtc_funcs vc4_dummy_crtc_funcs = { .atomic_destroy_state = vc4_crtc_destroy_state, .atomic_duplicate_state = vc4_crtc_duplicate_state, - .reset = vc4_crtc_reset, + .atomic_create_state = vc4_crtc_create_state, }; struct vc4_dummy_crtc *vc4_mock_pv(struct kunit *test, diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 4572b0b9bae1..f42cd5353230 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -1135,21 +1135,19 @@ void vc4_crtc_destroy_state(struct drm_crtc *crtc, drm_atomic_helper_crtc_destroy_state(crtc, state); } -void vc4_crtc_reset(struct drm_crtc *crtc) +struct drm_crtc_state *vc4_crtc_create_state(struct drm_crtc *crtc) { struct vc4_crtc_state *vc4_crtc_state; - if (crtc->state) - vc4_crtc_destroy_state(crtc, crtc->state); - vc4_crtc_state = kzalloc_obj(*vc4_crtc_state); if (!vc4_crtc_state) { - crtc->state = NULL; - return; + return ERR_PTR(-ENOMEM); } vc4_crtc_state->assigned_channel = VC4_HVS_CHANNEL_DISABLED; - __drm_atomic_helper_crtc_reset(crtc, &vc4_crtc_state->base); + __drm_atomic_helper_crtc_state_init(&vc4_crtc_state->base, crtc); + + return &vc4_crtc_state->base; } int vc4_crtc_late_register(struct drm_crtc *crtc) @@ -1170,7 +1168,7 @@ static const struct drm_crtc_funcs vc4_crtc_funcs = { .set_property = NULL, .cursor_set = NULL, /* handled by drm_mode_cursor_universal */ .cursor_move = NULL, /* handled by drm_mode_cursor_universal */ - .reset = vc4_crtc_reset, + .atomic_create_state = vc4_crtc_create_state, .atomic_duplicate_state = vc4_crtc_duplicate_state, .atomic_destroy_state = vc4_crtc_destroy_state, .enable_vblank = vc4_enable_vblank, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 0f5958c1e6b6..649032174dbd 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -949,7 +949,7 @@ int vc4_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc); void vc4_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state); -void vc4_crtc_reset(struct drm_crtc *crtc); +struct drm_crtc_state *vc4_crtc_create_state(struct drm_crtc *crtc); void vc4_crtc_handle_vblank(struct vc4_crtc *crtc); void vc4_crtc_send_vblank(struct drm_crtc *crtc); int vc4_crtc_late_register(struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 17c8635c5afa..ea1f2013b20e 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -513,6 +513,7 @@ static void vc4_hdmi_connector_reset(struct drm_connector *connector) } static const struct drm_connector_funcs vc4_hdmi_connector_funcs = { + .debugfs_init = drm_hdmi_connector_debugfs_init, .force = drm_atomic_helper_connector_hdmi_force, .fill_modes = drm_helper_probe_single_connector_modes, .reset = vc4_hdmi_connector_reset, diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 6c433270214d..20c9fc9283a8 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -363,20 +363,17 @@ static void vc4_plane_destroy_state(struct drm_plane *plane, } /* Called during init to allocate the plane's atomic state. */ -static void vc4_plane_reset(struct drm_plane *plane) +static struct drm_plane_state *vc4_plane_create_state(struct drm_plane *plane) { struct vc4_plane_state *vc4_state; - if (plane->state) - __drm_atomic_helper_plane_destroy_state(plane->state); - - kfree(plane->state); - vc4_state = kzalloc_obj(*vc4_state); if (!vc4_state) - return; + return ERR_PTR(-ENOMEM); - __drm_atomic_helper_plane_reset(plane, &vc4_state->base); + __drm_atomic_helper_plane_state_init(&vc4_state->base, plane); + + return &vc4_state->base; } static void vc4_dlist_counter_increment(struct vc4_plane_state *vc4_state) @@ -2495,7 +2492,7 @@ static bool vc4_format_mod_supported(struct drm_plane *plane, static const struct drm_plane_funcs vc4_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .reset = vc4_plane_reset, + .atomic_create_state = vc4_plane_create_state, .atomic_duplicate_state = vc4_plane_duplicate_state, .atomic_destroy_state = vc4_plane_destroy_state, .format_mod_supported = vc4_format_mod_supported, diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index bc3f366fc3e6..c469f8b1068b 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -425,7 +425,7 @@ static void vc4_txp_disable_vblank(struct drm_crtc *crtc) {} static const struct drm_crtc_funcs vc4_txp_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = vc4_page_flip, - .reset = vc4_crtc_reset, + .atomic_create_state = vc4_crtc_create_state, .atomic_duplicate_state = vc4_crtc_duplicate_state, .atomic_destroy_state = vc4_crtc_destroy_state, .enable_vblank = vc4_txp_enable_vblank, diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c index 0b8a35d09cd2..c24c1588cbe6 100644 --- a/drivers/gpu/drm/verisilicon/vs_crtc.c +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c @@ -149,7 +149,7 @@ static const struct drm_crtc_funcs vs_crtc_funcs = { .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .set_config = drm_atomic_helper_set_config, .enable_vblank = vs_crtc_enable_vblank, .disable_vblank = vs_crtc_disable_vblank, diff --git a/drivers/gpu/drm/verisilicon/vs_cursor_plane.c b/drivers/gpu/drm/verisilicon/vs_cursor_plane.c index fa4f601dd0c8..05e7a04e614c 100644 --- a/drivers/gpu/drm/verisilicon/vs_cursor_plane.c +++ b/drivers/gpu/drm/verisilicon/vs_cursor_plane.c @@ -12,6 +12,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> +#include <drm/drm_fb_dma_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_atomic_helper.h> @@ -176,7 +177,8 @@ static void vs_cursor_plane_atomic_update(struct drm_plane *plane, break; } - dma_addr = vs_fb_get_dma_addr(fb, &state->src); + /* hardware handles clipping as seen below */ + dma_addr = drm_fb_dma_get_gem_addr(fb, state, 0); regmap_write(dc->regs, VSDC_CURSOR_ADDRESS(output), lower_32_bits(dma_addr)); @@ -199,7 +201,7 @@ static void vs_cursor_plane_atomic_update(struct drm_plane *plane, } else { regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), VSDC_CURSOR_CONFIG_X_OFF_MASK, - -state->crtc_x); + VSDC_CURSOR_CONFIG_X_OFF(-state->crtc_x)); regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output), VSDC_CURSOR_LOCATION_X_MASK, 0); } @@ -213,7 +215,7 @@ static void vs_cursor_plane_atomic_update(struct drm_plane *plane, } else { regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), VSDC_CURSOR_CONFIG_Y_OFF_MASK, - -state->crtc_y); + VSDC_CURSOR_CONFIG_Y_OFF(-state->crtc_y)); regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output), VSDC_CURSOR_LOCATION_Y_MASK, 0); } diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c index 7ddb9d2dcb83..f189c0bba5b7 100644 --- a/drivers/gpu/drm/verisilicon/vs_plane.c +++ b/drivers/gpu/drm/verisilicon/vs_plane.c @@ -107,26 +107,6 @@ int drm_format_to_vs_format(u32 drm_format, struct vs_format *vs_format) return 0; } -dma_addr_t vs_fb_get_dma_addr(struct drm_framebuffer *fb, - const struct drm_rect *src_rect) -{ - struct drm_gem_dma_object *gem; - dma_addr_t dma_addr; - - /* Get the physical address of the buffer in memory */ - gem = drm_fb_dma_get_gem_obj(fb, 0); - - /* Compute the start of the displayed memory */ - dma_addr = gem->dma_addr + fb->offsets[0]; - - /* Fixup framebuffer address for src coordinates */ - dma_addr += drm_format_info_min_pitch(fb->format, 0, - src_rect->x1 >> 16); - dma_addr += (src_rect->y1 >> 16) * fb->pitches[0]; - - return dma_addr; -} - struct drm_plane_state *vs_plane_duplicate_state(struct drm_plane *plane) { struct vs_plane_state *vs_state, *vs_state_old; diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h index 32ed1d70f3b8..e4f82f1fc6e9 100644 --- a/drivers/gpu/drm/verisilicon/vs_plane.h +++ b/drivers/gpu/drm/verisilicon/vs_plane.h @@ -75,8 +75,6 @@ static inline struct vs_plane_state *to_vs_plane_state(struct drm_plane_state *s } int drm_format_to_vs_format(u32 drm_format, struct vs_format *vs_format); -dma_addr_t vs_fb_get_dma_addr(struct drm_framebuffer *fb, - const struct drm_rect *src_rect); struct drm_plane_state *vs_plane_duplicate_state(struct drm_plane *plane); void vs_plane_destroy_state(struct drm_plane *plane, diff --git a/drivers/gpu/drm/verisilicon/vs_primary_plane.c b/drivers/gpu/drm/verisilicon/vs_primary_plane.c index 1f2be41ae496..2750016a7f2c 100644 --- a/drivers/gpu/drm/verisilicon/vs_primary_plane.c +++ b/drivers/gpu/drm/verisilicon/vs_primary_plane.c @@ -8,6 +8,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> +#include <drm/drm_fb_dma_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_atomic_helper.h> @@ -126,7 +127,11 @@ static void vs_primary_plane_atomic_update(struct drm_plane *plane, VSDC_FB_CONFIG_UV_SWIZZLE_EN, vs_state->format.uv_swizzle); - dma_addr = vs_fb_get_dma_addr(fb, &state->src); + /* + * Primary plane cannot be moved, no clipping is involved, + * so the non-clipped framebuffer address can be used. + */ + dma_addr = drm_fb_dma_get_gem_addr(fb, state, 0); regmap_write(dc->regs, VSDC_FB_ADDRESS(output), lower_32_bits(dma_addr)); diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index a1a875a0c706..b60aab07e78d 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -28,11 +28,11 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_damage_helper.h> #include <drm/drm_edid.h> +#include <drm/drm_encoder.h> #include <drm/drm_fourcc.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> #include <drm/drm_vblank_helper.h> @@ -55,7 +55,7 @@ static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = { .destroy = drm_crtc_cleanup, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, DRM_CRTC_VBLANK_TIMER_FUNCS, @@ -232,6 +232,10 @@ static enum drm_mode_status virtio_gpu_conn_mode_valid(struct drm_connector *con return MODE_BAD; } +static const struct drm_encoder_funcs virtio_gpu_enc_funcs = { + .destroy = drm_encoder_cleanup, +}; + static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = { .mode_set = virtio_gpu_enc_mode_set, .enable = virtio_gpu_enc_enable, @@ -306,7 +310,11 @@ static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index) if (vgdev->has_edid) drm_connector_attach_edid_property(connector); - drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_VIRTUAL); + ret = drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs, + DRM_MODE_ENCODER_VIRTUAL, NULL); + if (ret) + return ret; + drm_encoder_helper_add(encoder, &virtio_gpu_enc_helper_funcs); encoder->possible_crtcs = 1 << index; diff --git a/drivers/gpu/drm/vkms/tests/gen_yuv_conversion.py b/drivers/gpu/drm/vkms/tests/gen_yuv_conversion.py new file mode 100755 index 000000000000..f4d226137cc2 --- /dev/null +++ b/drivers/gpu/drm/vkms/tests/gen_yuv_conversion.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0+ +""" +Derive the VKMS YCbCr -> RGB conversion constants and the vkms-format KUnit +reference values. + +It produces: + 1. The S31.32 fixed-point conversion matrices used in + drivers/gpu/drm/vkms/vkms_formats.c and asserted by + vkms_format_test_conversion_matrix. + 2. The 16-bit reference YCbCr inputs for the limited-range + yuv_u16_to_argb_u16 round-trip cases (the values changed by the fix). + +Requires numpy. +""" + +import numpy as np + +FP = 1 << 32 # S31.32 scale: fixed = round(coeff * 2^32) + +# ITU-R luma weights (Kr, Kb); Kg = 1 - Kr - Kb. +ENCODINGS = { + "BT601": (0.299, 0.114), + "BT709": (0.2126, 0.0722), + "BT2020": (0.2627, 0.0593), # non-constant luminance +} + +# 8-bit studio-range levels: luma 16..235 (range 219), chroma 16..240 +# (range 224, neutral 128); full-range maximum is 2^n - 1 = 255. +FULL_MAX = 255 +Y_RANGE = 235 - 16 # 219 +C_RANGE = 240 - 16 # 224 + +# 16-bit RGB test colors. +COLORS = { + "white": (0xffff, 0xffff, 0xffff), + "gray": (0x8080, 0x8080, 0x8080), + "black": (0x0000, 0x0000, 0x0000), + "red": (0xffff, 0x0000, 0x0000), + "green": (0x0000, 0xffff, 0x0000), + "blue": (0x0000, 0x0000, 0xffff), +} + + +def rgb_matrix(kr, kb, limited): + """Normalized YCbCr -> RGB (Y in [0,1], Cb/Cr in [-0.5, 0.5]).""" + kg = 1.0 - kr - kb + # Standard full-range relations. + m = [[1.0, 0.0, 2 * (1 - kr)], + [1.0, -2 * (1 - kb) * kb / kg, -2 * (1 - kr) * kr / kg], + [1.0, 2 * (1 - kb), 0.0]] + if limited: + # Expand studio input range to full range: luma by 255/219, + # chroma by 255/224 (relative to full-range maximum 2^n - 1). + ys, cs = FULL_MAX / Y_RANGE, FULL_MAX / C_RANGE + m = [[r[0] * ys, r[1] * cs, r[2] * cs] for r in m] + return m + + +def ref_yuv(m, y_offset): + """16-bit YCbCr that VKMS decodes back to each RGB color. + + VKMS decode: [R,G,B] = M . [Y - y_offset*257, Cb - 128*257, Cr - 128*257] + (8-bit offsets lifted to 16-bit by *257 = 65535/255), so invert and + re-add the offsets. + """ + inv = np.linalg.inv(np.array(m, float)) + off = np.array([y_offset * 257, 128 * 257, 128 * 257]) + return {name: tuple(int(min(max(round(v), 0), 0xffff)) + for v in inv @ np.array(rgb, float) + off) + for name, rgb in COLORS.items()} + +print(f"== RGB REFERENCE VALUES ==") +for name, rgb in COLORS.items(): + print(f" {name:6} {{ {rgb[0]:#06x}, {rgb[1]:#06x}, {rgb[2]:#06x} }}") +print() + +for enc, (kr, kb) in ENCODINGS.items(): + for limited in (False, True): + m = rgb_matrix(kr, kb, limited) + y_offset = 16 if limited else 0 + print(f"== {enc} {'LIMITED' if limited else 'FULL'} (y_offset={y_offset}) ==") + for row in m: + print(" { " + ", ".join(str(round(v * FP)) for v in row) + " },") + for name, yuv in ref_yuv(m, y_offset).items(): + print(f" {name:6} {{ {yuv[0]:#06x}, {yuv[1]:#06x}, {yuv[2]:#06x} }}") + print() diff --git a/drivers/gpu/drm/vkms/tests/vkms_format_test.c b/drivers/gpu/drm/vkms/tests/vkms_format_test.c index a7788fbc45dc..bbb1ad18c99f 100644 --- a/drivers/gpu/drm/vkms/tests/vkms_format_test.c +++ b/drivers/gpu/drm/vkms/tests/vkms_format_test.c @@ -54,6 +54,8 @@ struct yuv_u16_to_argb_u16_case { * The YUV color representation were acquired via the colour python framework. * Below are the function calls used for generating each case. * + * The limited-range cases are generated by gen_yuv_conversion.py. + * * For more information got to the docs: * https://colour.readthedocs.io/en/master/generated/colour.RGB_to_YCbCr.html */ @@ -101,12 +103,12 @@ static struct yuv_u16_to_argb_u16_case yuv_u16_to_argb_u16_cases[] = { .range = DRM_COLOR_YCBCR_LIMITED_RANGE, .n_colors = 6, .colors = { - { "white", { 0xeb00, 0x8000, 0x8000 }, { 0xffff, 0xffff, 0xffff, 0xffff }}, - { "gray", { 0x7dee, 0x8000, 0x8000 }, { 0xffff, 0x8080, 0x8080, 0x8080 }}, - { "black", { 0x1000, 0x8000, 0x8000 }, { 0xffff, 0x0000, 0x0000, 0x0000 }}, - { "red", { 0x517b, 0x5a34, 0xf000 }, { 0xffff, 0xffff, 0x0000, 0x0000 }}, - { "green", { 0x908e, 0x35cc, 0x2237 }, { 0xffff, 0x0000, 0xffff, 0x0000 }}, - { "blue", { 0x28f7, 0xf000, 0x6dc9 }, { 0xffff, 0x0000, 0x0000, 0xffff }}, + { "white", { 0xebeb, 0x8080, 0x8080 }, { 0xffff, 0xffff, 0xffff, 0xffff }}, + { "gray", { 0x7e6c, 0x8080, 0x8080 }, { 0xffff, 0x8080, 0x8080, 0x8080 }}, + { "black", { 0x1010, 0x8080, 0x8080 }, { 0xffff, 0x0000, 0x0000, 0x0000 }}, + { "red", { 0x51cd, 0x5a8e, 0xf0f0 }, { 0xffff, 0xffff, 0x0000, 0x0000 }}, + { "green", { 0x911e, 0x3602, 0x2259 }, { 0xffff, 0x0000, 0xffff, 0x0000 }}, + { "blue", { 0x2920, 0xf0f0, 0x6e37 }, { 0xffff, 0x0000, 0x0000, 0xffff }}, } }, /* @@ -151,12 +153,12 @@ static struct yuv_u16_to_argb_u16_case yuv_u16_to_argb_u16_cases[] = { .range = DRM_COLOR_YCBCR_LIMITED_RANGE, .n_colors = 6, .colors = { - { "white", { 0xeb00, 0x8000, 0x8000 }, { 0xffff, 0xffff, 0xffff, 0xffff }}, - { "gray", { 0x7dee, 0x8000, 0x8000 }, { 0xffff, 0x8080, 0x8080, 0x8080 }}, - { "black", { 0x1000, 0x8000, 0x8000 }, { 0xffff, 0x0000, 0x0000, 0x0000 }}, - { "red", { 0x3e8f, 0x6656, 0xf000 }, { 0xffff, 0xffff, 0x0000, 0x0000 }}, - { "green", { 0xaca1, 0x29aa, 0x1a45 }, { 0xffff, 0x0000, 0xffff, 0x0000 }}, - { "blue", { 0x1fd0, 0xf000, 0x75bb }, { 0xffff, 0x0000, 0x0000, 0xffff }}, + { "white", { 0xebeb, 0x8080, 0x8080 }, { 0xffff, 0xffff, 0xffff, 0xffff }}, + { "gray", { 0x7e6c, 0x8080, 0x8080 }, { 0xffff, 0x8080, 0x8080, 0x8080 }}, + { "black", { 0x1010, 0x8080, 0x8080 }, { 0xffff, 0x0000, 0x0000, 0x0000 }}, + { "red", { 0x3ece, 0x66bc, 0xf0f0 }, { 0xffff, 0xffff, 0x0000, 0x0000 }}, + { "green", { 0xad4e, 0x29d4, 0x1a5f }, { 0xffff, 0x0000, 0xffff, 0x0000 }}, + { "blue", { 0x1ff0, 0xf0f0, 0x7631 }, { 0xffff, 0x0000, 0x0000, 0xffff }}, } }, /* @@ -201,12 +203,12 @@ static struct yuv_u16_to_argb_u16_case yuv_u16_to_argb_u16_cases[] = { .range = DRM_COLOR_YCBCR_LIMITED_RANGE, .n_colors = 6, .colors = { - { "white", { 0xeb00, 0x8000, 0x8000 }, { 0xffff, 0xffff, 0xffff, 0xffff }}, - { "gray", { 0x7dee, 0x8000, 0x8000 }, { 0xffff, 0x8080, 0x8080, 0x8080 }}, - { "black", { 0x1000, 0x8000, 0x8000 }, { 0xffff, 0x0000, 0x0000, 0x0000 }}, - { "red", { 0x4988, 0x60b9, 0xf000 }, { 0xffff, 0xffff, 0x0000, 0x0000 }}, - { "green", { 0xa47b, 0x2f47, 0x1902 }, { 0xffff, 0x0000, 0xffff, 0x0000 }}, - { "blue", { 0x1cfd, 0xf000, 0x76fe }, { 0xffff, 0x0000, 0x0000, 0xffff }}, + { "white", { 0xebeb, 0x8080, 0x8080 }, { 0xffff, 0xffff, 0xffff, 0xffff }}, + { "gray", { 0x7e6c, 0x8080, 0x8080 }, { 0xffff, 0x8080, 0x8080, 0x8080 }}, + { "black", { 0x1010, 0x8080, 0x8080 }, { 0xffff, 0x0000, 0x0000, 0x0000 }}, + { "red", { 0x49d2, 0x611a, 0xf0f0 }, { 0xffff, 0xffff, 0x0000, 0x0000 }}, + { "green", { 0xa520, 0x2f76, 0x191b }, { 0xffff, 0x0000, 0xffff, 0x0000 }}, + { "blue", { 0x1d1a, 0xf0f0, 0x7775 }, { 0xffff, 0x0000, 0x0000, 0xffff }}, } }, }; @@ -232,7 +234,7 @@ static void vkms_format_test_yuv_u16_to_argb_u16(struct kunit *test) struct conversion_matrix matrix; get_conversion_matrix_to_argb_u16 - (DRM_FORMAT_NV12, param->encoding, param->range, &matrix); + (DRM_FORMAT_NV12, param->encoding, param->range, false, &matrix); argb = argb_u16_from_yuv161616(&matrix, color->yuv.y, color->yuv.u, color->yuv.v); diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c b/drivers/gpu/drm/vkms/vkms_colorop.c index 071f3a8d2e7c..9424b1994239 100644 --- a/drivers/gpu/drm/vkms/vkms_colorop.c +++ b/drivers/gpu/drm/vkms/vkms_colorop.c @@ -12,11 +12,19 @@ static const u64 supported_tfs = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF); +static const u64 supported_fm = + BIT(DRM_COLOROP_FM_YCBCR601_FULL_RGB) | + BIT(DRM_COLOROP_FM_YCBCR601_LIMITED_RGB) | + BIT(DRM_COLOROP_FM_YCBCR709_FULL_RGB) | + BIT(DRM_COLOROP_FM_YCBCR709_LIMITED_RGB) | + BIT(DRM_COLOROP_FM_YCBCR2020_NC_FULL_RGB) | + BIT(DRM_COLOROP_FM_YCBCR2020_NC_LIMITED_RGB); + static const struct drm_colorop_funcs vkms_colorop_funcs = { .destroy = drm_colorop_destroy, }; -#define MAX_COLOR_PIPELINE_OPS 4 +#define MAX_COLOR_PIPELINE_OPS 5 static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list) { @@ -27,7 +35,25 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr memset(ops, 0, sizeof(ops)); - /* 1st op: 1d curve */ + /* 1st op: Fixed Matrix (YUV to RGB) */ + ops[i] = kzalloc_obj(*ops[i]); + if (!ops[i]) { + drm_err(dev, "KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_plane_colorop_fixed_matrix_init(dev, ops[i], plane, &vkms_colorop_funcs, + supported_fm, + DRM_COLOROP_FLAG_ALLOW_BYPASS); + if (ret) + goto cleanup; + + list->type = ops[i]->base.id; + + i++; + + /* 2nd op: 1d curve */ ops[i] = kzalloc_obj(*ops[i]); if (!ops[i]) { drm_err(dev, "KMS: Failed to allocate colorop\n"); @@ -41,11 +67,11 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr if (ret) goto cleanup; - list->type = ops[i]->base.id; + drm_colorop_set_next_property(ops[i - 1], ops[i]); i++; - /* 2nd op: 3x4 matrix */ + /* 3rd op: 3x4 matrix */ ops[i] = kzalloc_obj(*ops[i]); if (!ops[i]) { drm_err(dev, "KMS: Failed to allocate colorop\n"); @@ -62,7 +88,7 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr i++; - /* 3rd op: 3x4 matrix */ + /* 4th op: 3x4 matrix */ ops[i] = kzalloc_obj(*ops[i]); if (!ops[i]) { drm_err(dev, "KMS: Failed to allocate colorop\n"); @@ -79,7 +105,7 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr i++; - /* 4th op: 1d curve */ + /* 5th op: 1d curve */ ops[i] = kzalloc_obj(*ops[i]); if (!ops[i]) { drm_err(dev, "KMS: Failed to allocate colorop\n"); diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index 83d217085ad0..899120cd07ac 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -187,6 +187,12 @@ static void apply_colorop(struct pixel_argb_s32 *pixel, struct drm_colorop *colo if (colorop_state->data) apply_3x4_matrix(pixel, (struct drm_color_ctm_3x4 *)colorop_state->data->data); + } else if (colorop->type == DRM_COLOROP_FIXED_MATRIX) { + /* + * Fixed Matrix is a no-op here. YUV-to-RGB conversion + * is handled at format read time via the + * conversion_matrix set in vkms_plane_atomic_update(). + */ } } diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c index 079abfba427d..fe17f49305a9 100644 --- a/drivers/gpu/drm/vkms/vkms_crtc.c +++ b/drivers/gpu/drm/vkms/vkms_crtc.c @@ -86,22 +86,24 @@ static void vkms_atomic_crtc_destroy_state(struct drm_crtc *crtc, kfree(vkms_state); } -static void vkms_atomic_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *vkms_atomic_crtc_create_state(struct drm_crtc *crtc) { - struct vkms_crtc_state *vkms_state = kzalloc_obj(*vkms_state); + struct vkms_crtc_state *vkms_state; + + vkms_state = kzalloc_obj(*vkms_state); + if (!vkms_state) + return ERR_PTR(-ENOMEM); - if (crtc->state) - vkms_atomic_crtc_destroy_state(crtc, crtc->state); + __drm_atomic_helper_crtc_state_init(&vkms_state->base, crtc); + INIT_WORK(&vkms_state->composer_work, vkms_composer_worker); - __drm_atomic_helper_crtc_reset(crtc, &vkms_state->base); - if (vkms_state) - INIT_WORK(&vkms_state->composer_work, vkms_composer_worker); + return &vkms_state->base; } static const struct drm_crtc_funcs vkms_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = vkms_atomic_crtc_reset, + .atomic_create_state = vkms_atomic_crtc_create_state, .atomic_duplicate_state = vkms_atomic_crtc_duplicate_state, .atomic_destroy_state = vkms_atomic_crtc_destroy_state, DRM_CRTC_VBLANK_TIMER_FUNCS, diff --git a/drivers/gpu/drm/vkms/vkms_formats.c b/drivers/gpu/drm/vkms/vkms_formats.c index dfb8e13cba87..964b574d9ed7 100644 --- a/drivers/gpu/drm/vkms/vkms_formats.c +++ b/drivers/gpu/drm/vkms/vkms_formats.c @@ -788,15 +788,36 @@ static const struct conversion_matrix yuv_bt601_full = { }; /* - * numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.601"], - * is_legal = True, - * bits = 8) * 2**32).astype(int) + * BT.601 limited-/studio-range YCbCr to full-range RGB. + * + * The coefficients are derived as follows: + * + * 1. Take the standard ITU-R YCbCr -> RGB relations for luma weights + * Kr, Kb (Kg = 1 - Kr - Kb), with Y in [0, 1] and Cb, Cr in + * [-0.5, 0.5]. For BT.601 Kr = 0.299 and Kb = 0.114: + * + * R = Y + 2 * (1 - Kr) * Cr + * G = Y - 2 * (1 - Kb) * Kb / Kg * Cb - 2 * (1 - Kr) * Kr / Kg * Cr + * B = Y + 2 * (1 - Kb) * Cb + * + * These are exactly the yuv_bt601_full coefficients above. + * + * 2. Expand the studio input range to full range, relative to a + * full-range maximum of 2^n - 1 (255 for 8-bit): the luma + * coefficient is scaled by 255/(235 - 16) and the chroma + * coefficients by 255/(240 - 16). This matches the DRM UAPI + * definition and IGT's igt_ycbcr_to_rgb_matrix(). Note this differs + * from colour.matrix_YCbCr(is_legal=True), which normalises by 2^n + * and is thus off by a factor of 256/255. + * + * 3. Convert each coefficient to S31.32 fixed point, i.e. + * round(coeff * 2^32). */ static const struct conversion_matrix yuv_bt601_limited = { .matrix = { - { 5020601039, 0, 6881764740 }, - { 5020601039, -1689204679, -3505362278 }, - { 5020601039, 8697922339, 0 }, + { 5000989317, 0, 6854882848 }, + { 5000989317, -1682606224, -3491669458 }, + { 5000989317, 8663946082, 0 }, }, .y_offset = 16, }; @@ -816,15 +837,14 @@ static const struct conversion_matrix yuv_bt709_full = { }; /* - * numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.709"], - * is_legal = True, - * bits = 8) * 2**32).astype(int) + * BT.709 limited-range YCbCr to full-range RGB (Kr = 0.2126, Kb = 0.0722). + * Derived as described for yuv_bt601_limited. */ static const struct conversion_matrix yuv_bt709_limited = { .matrix = { - { 5020601039, 0, 7729959424 }, - { 5020601039, -919487572, -2297803934 }, - { 5020601039, 9108275786, 0 }, + { 5000989317, 0, 7699764272 }, + { 5000989317, -915895824, -2288828138 }, + { 5000989317, 9072696586, 0 }, }, .y_offset = 16, }; @@ -844,15 +864,14 @@ static const struct conversion_matrix yuv_bt2020_full = { }; /* - * numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.2020"], - * is_legal = True, - * bits = 8) * 2**32).astype(int) + * BT.2020 non-constant-luminance limited-range YCbCr to full-range RGB + * (Kr = 0.2627, Kb = 0.0593). Derived as described for yuv_bt601_limited. */ static const struct conversion_matrix yuv_bt2020_limited = { .matrix = { - { 5020601039, 0, 7238124312 }, - { 5020601039, -807714626, -2804506279 }, - { 5020601039, 9234915964, 0 }, + { 5000989317, 0, 7209850391 }, + { 5000989317, -804559491, -2793551177 }, + { 5000989317, 9198842076, 0 }, }, .y_offset = 16, }; @@ -876,16 +895,25 @@ static void swap_uv_columns(struct conversion_matrix *matrix) * @format: DRM_FORMAT_* value for which to obtain a conversion function (see [drm_fourcc.h]) * @encoding: DRM_COLOR_* value for which to obtain a conversion matrix * @range: DRM_COLOR_*_RANGE value for which to obtain a conversion matrix + * @bypass: If true, return an identity (no-op) matrix that passes the samples + * through unchanged, ignoring @encoding and @range. Used when a fixed + * matrix colorop is present but bypassed. * @matrix: Pointer to store the value into */ void get_conversion_matrix_to_argb_u16(u32 format, enum drm_color_encoding encoding, enum drm_color_range range, + bool bypass, struct conversion_matrix *matrix) { const struct conversion_matrix *matrix_to_copy; bool limited_range; + if (bypass) { + memcpy(matrix, &no_operation, sizeof(no_operation)); + return; + } + switch (range) { case DRM_COLOR_YCBCR_LIMITED_RANGE: limited_range = true; diff --git a/drivers/gpu/drm/vkms/vkms_formats.h b/drivers/gpu/drm/vkms/vkms_formats.h index eeb208cdd6b1..d969cc669a5e 100644 --- a/drivers/gpu/drm/vkms/vkms_formats.h +++ b/drivers/gpu/drm/vkms/vkms_formats.h @@ -10,7 +10,7 @@ pixel_read_line_t get_pixel_read_line_function(u32 format); pixel_write_t get_pixel_write_function(u32 format); void get_conversion_matrix_to_argb_u16(u32 format, enum drm_color_encoding encoding, - enum drm_color_range range, + enum drm_color_range range, bool bypass, struct conversion_matrix *matrix); #if IS_ENABLED(CONFIG_KUNIT) diff --git a/drivers/gpu/drm/vkms/vkms_plane.c b/drivers/gpu/drm/vkms/vkms_plane.c index 68cb2a3335e6..6ee5c3f3207c 100644 --- a/drivers/gpu/drm/vkms/vkms_plane.c +++ b/drivers/gpu/drm/vkms/vkms_plane.c @@ -6,6 +6,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_blend.h> +#include <drm/drm_colorop.h> #include <drm/drm_fourcc.h> #include <drm/drm_gem_atomic_helper.h> #include <drm/drm_gem_framebuffer_helper.h> @@ -131,6 +132,9 @@ static void vkms_plane_atomic_update(struct drm_plane *plane, struct drm_framebuffer *fb = new_state->fb; struct vkms_frame_info *frame_info; u32 fmt; + enum drm_color_encoding encoding = new_state->color_encoding; + enum drm_color_range range = new_state->color_range; + bool bypass = false; if (!new_state->crtc || !fb) return; @@ -148,7 +152,49 @@ static void vkms_plane_atomic_update(struct drm_plane *plane, frame_info->rotation = new_state->rotation; vkms_plane_state->pixel_read_line = get_pixel_read_line_function(fmt); - get_conversion_matrix_to_argb_u16(fmt, new_state->color_encoding, new_state->color_range, + + if (new_state->color_pipeline) { + struct drm_colorop *colorop = new_state->color_pipeline; + struct drm_colorop_state *colorop_state; + + colorop_state = drm_atomic_get_new_colorop_state(state, colorop); + bypass = !colorop_state || colorop_state->bypass; + + if (!bypass) { + switch (colorop_state->fixed_matrix_type) { + case DRM_COLOROP_FM_YCBCR601_FULL_RGB: + encoding = DRM_COLOR_YCBCR_BT601; + range = DRM_COLOR_YCBCR_FULL_RANGE; + break; + case DRM_COLOROP_FM_YCBCR601_LIMITED_RGB: + encoding = DRM_COLOR_YCBCR_BT601; + range = DRM_COLOR_YCBCR_LIMITED_RANGE; + break; + case DRM_COLOROP_FM_YCBCR709_FULL_RGB: + encoding = DRM_COLOR_YCBCR_BT709; + range = DRM_COLOR_YCBCR_FULL_RANGE; + break; + case DRM_COLOROP_FM_YCBCR709_LIMITED_RGB: + encoding = DRM_COLOR_YCBCR_BT709; + range = DRM_COLOR_YCBCR_LIMITED_RANGE; + break; + case DRM_COLOROP_FM_YCBCR2020_NC_FULL_RGB: + encoding = DRM_COLOR_YCBCR_BT2020; + range = DRM_COLOR_YCBCR_FULL_RANGE; + break; + case DRM_COLOROP_FM_YCBCR2020_NC_LIMITED_RGB: + encoding = DRM_COLOR_YCBCR_BT2020; + range = DRM_COLOR_YCBCR_LIMITED_RANGE; + break; + default: + encoding = DRM_COLOR_YCBCR_BT709; + range = DRM_COLOR_YCBCR_LIMITED_RANGE; + break; + } + } + } + + get_conversion_matrix_to_argb_u16(fmt, encoding, range, bypass, &vkms_plane_state->conversion_matrix); } @@ -175,6 +221,13 @@ static int vkms_plane_atomic_check(struct drm_plane *plane, if (ret != 0) return ret; + if (new_plane_state->color_pipeline && new_plane_state->fb->format->is_yuv) { + struct drm_colorop *colorop = new_plane_state->color_pipeline; + + if (colorop->type != DRM_COLOROP_FIXED_MATRIX) + return -EINVAL; + } + return 0; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 1b407b61f683..0b470809661c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -206,32 +206,24 @@ vmw_du_crtc_duplicate_state(struct drm_crtc *crtc) /** - * vmw_du_crtc_reset - creates a blank vmw crtc state + * vmw_du_crtc_create_state - creates a blank vmw crtc state * @crtc: DRM crtc * - * Resets the atomic state for @crtc by freeing the state pointer (which - * might be NULL, e.g. at driver load time) and allocating a new empty state - * object. + * Allocates a new empty state object for @crtc. */ -void vmw_du_crtc_reset(struct drm_crtc *crtc) +struct drm_crtc_state *vmw_du_crtc_create_state(struct drm_crtc *crtc) { struct vmw_crtc_state *vcs; - - if (crtc->state) { - __drm_atomic_helper_crtc_destroy_state(crtc->state); - - kfree(vmw_crtc_state_to_vcs(crtc->state)); - } - vcs = kzalloc_obj(*vcs); - if (!vcs) { DRM_ERROR("Cannot allocate vmw_crtc_state\n"); - return; + return ERR_PTR(-ENOMEM); } - __drm_atomic_helper_crtc_reset(crtc, &vcs->base); + __drm_atomic_helper_crtc_state_init(&vcs->base, crtc); + + return &vcs->base; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index 2224d7d91d1b..e641f33341e6 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -421,7 +421,7 @@ int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_commit *state); void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_commit *state); -void vmw_du_crtc_reset(struct drm_crtc *crtc); +struct drm_crtc_state *vmw_du_crtc_create_state(struct drm_crtc *crtc); struct drm_crtc_state *vmw_du_crtc_duplicate_state(struct drm_crtc *crtc); void vmw_du_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index af3e32174563..a4b28341f622 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -249,7 +249,7 @@ static void vmw_ldu_crtc_mode_set_nofb(struct drm_crtc *crtc) static const struct drm_crtc_funcs vmw_legacy_crtc_funcs = { .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_ldu_crtc_destroy, - .reset = vmw_du_crtc_reset, + .atomic_create_state = vmw_du_crtc_create_state, .atomic_duplicate_state = vmw_du_crtc_duplicate_state, .atomic_destroy_state = vmw_du_crtc_destroy_state, .set_config = drm_atomic_helper_set_config, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index c83061cf7455..a05f935f1841 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -305,7 +305,7 @@ static void vmw_sou_crtc_atomic_disable(struct drm_crtc *crtc, static const struct drm_crtc_funcs vmw_screen_object_crtc_funcs = { .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_sou_crtc_destroy, - .reset = vmw_du_crtc_reset, + .atomic_create_state = vmw_du_crtc_create_state, .atomic_duplicate_state = vmw_du_crtc_duplicate_state, .atomic_destroy_state = vmw_du_crtc_destroy_state, .set_config = drm_atomic_helper_set_config, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 4139837f4caf..939da0e11dbb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -778,7 +778,7 @@ out_unref: static const struct drm_crtc_funcs vmw_stdu_crtc_funcs = { .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_stdu_crtc_destroy, - .reset = vmw_du_crtc_reset, + .atomic_create_state = vmw_du_crtc_create_state, .atomic_duplicate_state = vmw_du_crtc_duplicate_state, .atomic_destroy_state = vmw_du_crtc_destroy_state, .set_config = drm_atomic_helper_set_config, diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c index d5f922450565..0755066a81e7 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_kms.c +++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c @@ -29,8 +29,8 @@ #include <drm/drm_managed.h> #include <drm/drm_mode_config.h> #include <drm/drm_plane.h> +#include <drm/drm_print.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> #include <linux/clk.h> @@ -308,7 +308,7 @@ static const struct drm_crtc_funcs zynqmp_dpsub_crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .reset = drm_atomic_helper_crtc_reset, + .atomic_create_state = drm_atomic_helper_crtc_create_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .enable_vblank = zynqmp_dpsub_crtc_enable_vblank, @@ -417,6 +417,10 @@ static const struct drm_driver zynqmp_dpsub_drm_driver = { .minor = 0, }; +static const struct drm_encoder_funcs zynqmp_dpsub_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + static int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub) { struct drm_encoder *encoder = &dpsub->drm->encoder; @@ -436,7 +440,13 @@ static int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub) /* Create the encoder and attach the bridge. */ encoder->possible_crtcs |= drm_crtc_mask(&dpsub->drm->crtc); - drm_simple_encoder_init(&dpsub->drm->dev, encoder, DRM_MODE_ENCODER_NONE); + ret = drm_encoder_init(&dpsub->drm->dev, encoder, + &zynqmp_dpsub_encoder_funcs, + DRM_MODE_ENCODER_NONE, NULL); + if (ret) { + drm_err(&dpsub->drm->dev, "failed to initialize encoder\n"); + return ret; + } ret = drm_bridge_attach(encoder, dpsub->bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); diff --git a/drivers/gpu/tests/gpu_buddy_test.c b/drivers/gpu/tests/gpu_buddy_test.c index ed4c1c3acb3c..b75d32ca6ca0 100644 --- a/drivers/gpu/tests/gpu_buddy_test.c +++ b/drivers/gpu/tests/gpu_buddy_test.c @@ -38,7 +38,7 @@ static void gpu_test_buddy_subtree_offset_alignment_stress(struct kunit *test) }; struct list_head allocated[ARRAY_SIZE(alignments)]; unsigned int i, max_subtree_align = 0; - int ret, tree, order; + int ret, order; struct gpu_buddy mm; KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K), @@ -78,15 +78,11 @@ static void gpu_test_buddy_subtree_offset_alignment_stress(struct kunit *test) } for (order = mm.max_order; order >= 0 && !root; order--) { - for (tree = 0; tree < 2; tree++) { - node = mm.free_trees[tree][order].rb_node; - if (node) { - root = container_of(node, - struct gpu_buddy_block, - rb); - break; - } - } + node = mm.free_tree[order].rb_node; + if (node) + root = container_of(node, + struct gpu_buddy_block, + rb); } KUNIT_ASSERT_NOT_NULL(test, root); @@ -97,15 +93,13 @@ static void gpu_test_buddy_subtree_offset_alignment_stress(struct kunit *test) gpu_buddy_free_list(&mm, &allocated[i], 0); for (order = 0; order <= mm.max_order; order++) { - for (tree = 0; tree < 2; tree++) { - node = mm.free_trees[tree][order].rb_node; - if (!node) - continue; - - block = container_of(node, struct gpu_buddy_block, rb); - max_subtree_align = max(max_subtree_align, - block->subtree_max_alignment); - } + node = mm.free_tree[order].rb_node; + if (!node) + continue; + + block = container_of(node, struct gpu_buddy_block, rb); + max_subtree_align = max(max_subtree_align, + block->subtree_max_alignment); } KUNIT_EXPECT_GE(test, max_subtree_align, ilog2(alignments[i])); @@ -289,6 +283,121 @@ static void gpu_test_buddy_fragmentation_performance(struct kunit *test) gpu_buddy_fini(&mm); } +static void gpu_test_buddy_dirty_tracker_performance(struct kunit *test) +{ + struct gpu_buddy_block *block, *tmp; + unsigned long elapsed_ms; + LIST_HEAD(clear_blocks); + LIST_HEAD(dirty_blocks); + LIST_HEAD(allocated); + struct gpu_buddy mm; + LIST_HEAD(results); + ktime_t start, end; + int i, count; + + /* + * Contiguous alloc latency after alternating clear/dirty fragmentation + * + * Fill a 4 GiB pool with 4 KiB allocations, partition them into + * alternating cleared and dirty sets, then free both. In the old + * dual-tree design every adjacent buddy pair has one cleared half and + * one dirty half, so the pair sits on opposite sides of the clear/dirty + * merge barrier and cannot be coalesced at free() time. The pool + * stays fully fragmented and the subsequent contiguous 2 GiB allocation + * (smaller than the 4 GiB span so it routes through the contiguous + * allocator rather than the exact-range fast path) has to invoke + * __force_merge() to rebuild a 2 GiB block before it can succeed. With + * the dirty-tracker design buddy pairs coalesce unconditionally during + * free(), so the pool is already at max_order before the timed alloc + * begins and __force_merge() is not needed. + */ + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, SZ_4G, SZ_4K), + "buddy_init failed\n"); + + for (i = 0; i < SZ_4G / SZ_4K; i++) + KUNIT_ASSERT_FALSE_MSG(test, + gpu_buddy_alloc_blocks(&mm, 0, SZ_4G, SZ_4K, SZ_4K, + &allocated, 0), + "buddy_alloc hit an error size=%u\n", SZ_4K); + + count = 0; + list_for_each_entry_safe(block, tmp, &allocated, link) { + if (count++ % 2 == 0) + list_move_tail(&block->link, &clear_blocks); + else + list_move_tail(&block->link, &dirty_blocks); + } + + gpu_buddy_free_list(&mm, &clear_blocks, GPU_BUDDY_CLEARED); + gpu_buddy_free_list(&mm, &dirty_blocks, 0); + + start = ktime_get(); + KUNIT_ASSERT_FALSE_MSG(test, + gpu_buddy_alloc_blocks(&mm, 0, SZ_4G, SZ_2G, SZ_2G, + &results, + GPU_BUDDY_CONTIGUOUS_ALLOCATION), + "contiguous alloc failed\n"); + end = ktime_get(); + elapsed_ms = ktime_to_ms(ktime_sub(end, start)); + + kunit_info(test, "Contiguous alloc after fragmentation: %lu ms\n", + elapsed_ms); + + gpu_buddy_free_list(&mm, &results, 0); + gpu_buddy_fini(&mm); + + /* + * Repeated alloc throughput from a maximally fragmented pool + * + * Fill a 4 GiB pool with 4 KiB allocations, free even-indexed blocks + * as cleared and odd-indexed blocks as dirty. The alternating pattern + * ensures every adjacent buddy pair has one cleared half and one dirty + * half, so each pair lands on opposite sides of the old merge barrier. + * Each of the 16 384 x 256 KiB allocations in the timed loop asks for a + * single contiguous block (size == min_block_size, CONTIGUOUS flag), so + * it cannot be satisfied by 64 stray 4 KiB blocks: under the old design + * every alloc has to pay the __force_merge() cost to rebuild a 256 KiB + * block. With the dirty-tracker design the pool collapses to one + * max_order block during free(), so each alloc is a simple O(log N) + * split. + */ + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, SZ_4G, SZ_4K), + "buddy_init failed\n"); + + for (i = 0; i < SZ_4G / SZ_4K; i++) + KUNIT_ASSERT_FALSE_MSG(test, + gpu_buddy_alloc_blocks(&mm, 0, SZ_4G, SZ_4K, SZ_4K, + &allocated, 0), + "buddy_alloc hit an error size=%u\n", SZ_4K); + + count = 0; + list_for_each_entry_safe(block, tmp, &allocated, link) { + if (count++ % 2 == 0) + list_move_tail(&block->link, &clear_blocks); + else + list_move_tail(&block->link, &dirty_blocks); + } + + gpu_buddy_free_list(&mm, &clear_blocks, GPU_BUDDY_CLEARED); + gpu_buddy_free_list(&mm, &dirty_blocks, 0); + + start = ktime_get(); + for (i = 0; i < SZ_4G / SZ_256K; i++) + KUNIT_ASSERT_FALSE_MSG(test, + gpu_buddy_alloc_blocks(&mm, 0, SZ_4G, SZ_256K, SZ_256K, + &results, + GPU_BUDDY_CONTIGUOUS_ALLOCATION), + "buddy_alloc hit an error size=%u\n", SZ_256K); + end = ktime_get(); + elapsed_ms = ktime_to_ms(ktime_sub(end, start)); + + kunit_info(test, "Repeated 256 KiB allocs from fragmented pool: %lu ms\n", + elapsed_ms); + + gpu_buddy_free_list(&mm, &results, 0); + gpu_buddy_fini(&mm); +} + static void gpu_test_buddy_alloc_range_bias(struct kunit *test) { u32 mm_size, size, ps, bias_size, bias_start, bias_end, bias_rem; @@ -931,10 +1040,12 @@ static void gpu_test_buddy_alloc_clear(struct kunit *test) /* * Create a new mm. Intentionally fragment the address space by creating * two alternating lists. Free both lists, one as dirty the other as clean. - * Try to allocate double the previous size with matching min_page_size. The - * allocation should never fail as it calls the force_merge. Also check that - * the page is always dirty after force_merge. Free the page as dirty, then - * repeat the whole thing, increment the order until we hit the max_order. + * Try to allocate double the previous size with matching min_page_size. + * The allocation should never fail because buddy pairs coalesce + * unconditionally at free() time, rebuilding the larger block. Also check + * that the page is always dirty (a merged block spanning a dirty half is + * dirty). Free the page as dirty, then repeat the whole thing, increment + * the order until we hit the max_order. */ i = 0; @@ -1027,7 +1138,7 @@ static void gpu_test_buddy_alloc_clear(struct kunit *test) gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED); gpu_buddy_free_list(&mm, &dirty, 0); gpu_buddy_reset_clear(&mm, false); - KUNIT_EXPECT_EQ(test, mm.clear_avail, 0); + KUNIT_EXPECT_EQ(test, gpu_buddy_clear_avail(&mm), 0); gpu_buddy_fini(&mm); /* @@ -1050,7 +1161,7 @@ static void gpu_test_buddy_alloc_clear(struct kunit *test) gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED); gpu_buddy_free_list(&mm, &dirty, 0); gpu_buddy_reset_clear(&mm, true); - KUNIT_EXPECT_EQ(test, mm.clear_avail, mm_size); + KUNIT_EXPECT_EQ(test, gpu_buddy_clear_avail(&mm), mm_size); gpu_buddy_fini(&mm); } @@ -1496,6 +1607,7 @@ static struct kunit_case gpu_buddy_tests[] = { KUNIT_CASE(gpu_test_buddy_alloc_range), KUNIT_CASE(gpu_test_buddy_alloc_range_bias), KUNIT_CASE_SLOW(gpu_test_buddy_fragmentation_performance), + KUNIT_CASE_SLOW(gpu_test_buddy_dirty_tracker_performance), KUNIT_CASE(gpu_test_buddy_alloc_exceeds_max_order), KUNIT_CASE(gpu_test_buddy_offset_aligned_allocation), KUNIT_CASE(gpu_test_buddy_subtree_offset_alignment_stress), diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h index 8b0ca36083dd..e63c7f075f54 100644 --- a/include/drm/display/drm_dp.h +++ b/include/drm/display/drm_dp.h @@ -1035,6 +1035,7 @@ # define DP_EDP_14a 0x04 /* eDP 1.4a */ # define DP_EDP_14b 0x05 /* eDP 1.4b */ # define DP_EDP_15 0x06 /* eDP 1.5 */ +# define DP_EDP_20 0x07 /* eDP 2.0 */ #define DP_EDP_GENERAL_CAP_1 0x701 # define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP (1 << 0) diff --git a/include/drm/display/drm_hdmi_state_helper.h b/include/drm/display/drm_hdmi_state_helper.h index 13375bd0f4ae..596c57d9d250 100644 --- a/include/drm/display/drm_hdmi_state_helper.h +++ b/include/drm/display/drm_hdmi_state_helper.h @@ -3,6 +3,7 @@ #ifndef DRM_HDMI_STATE_HELPER_H_ #define DRM_HDMI_STATE_HELPER_H_ +struct dentry; struct drm_atomic_commit; struct drm_connector; struct drm_connector_state; @@ -29,5 +30,7 @@ void drm_atomic_helper_connector_hdmi_force(struct drm_connector *connector); enum drm_mode_status drm_hdmi_connector_mode_valid(struct drm_connector *connector, const struct drm_display_mode *mode); +void drm_hdmi_connector_debugfs_init(struct drm_connector *connector, + struct dentry *root); #endif // DRM_HDMI_STATE_HELPER_H_ diff --git a/include/drm/display/drm_scdc.h b/include/drm/display/drm_scdc.h index 3d58f37e8ed8..7f0b05b2f280 100644 --- a/include/drm/display/drm_scdc.h +++ b/include/drm/display/drm_scdc.h @@ -29,6 +29,8 @@ #define SCDC_SOURCE_VERSION 0x02 #define SCDC_UPDATE_0 0x10 +#define SCDC_RSED_UPDATE (1 << 6) +#define SCDC_FLT_UPDATE (1 << 5) #define SCDC_READ_REQUEST_TEST (1 << 2) #define SCDC_CED_UPDATE (1 << 1) #define SCDC_STATUS_UPDATE (1 << 0) @@ -46,14 +48,25 @@ #define SCDC_CONFIG_0 0x30 #define SCDC_READ_REQUEST_ENABLE (1 << 0) +#define SCDC_CONFIG_1 0x31 +#define SCDC_FRL_RATE 0x0f +#define SCDC_FFE_LEVELS 0xf0 + #define SCDC_STATUS_FLAGS_0 0x40 +#define SCDC_DSC_FAIL (1 << 7) +#define SCDC_FLT_READY (1 << 6) +#define SCDC_LN3_LOCK (1 << 4) #define SCDC_CH2_LOCK (1 << 3) #define SCDC_CH1_LOCK (1 << 2) #define SCDC_CH0_LOCK (1 << 1) -#define SCDC_CH_LOCK_MASK (SCDC_CH2_LOCK | SCDC_CH1_LOCK | SCDC_CH0_LOCK) +#define SCDC_CH_LOCK_MASK (SCDC_LN3_LOCK | SCDC_CH2_LOCK | SCDC_CH1_LOCK | \ + SCDC_CH0_LOCK) #define SCDC_CLOCK_DETECT (1 << 0) #define SCDC_STATUS_FLAGS_1 0x41 +#define SCDC_LN_EVEN_TRAIN_PTRN 0x0f +#define SCDC_LN_ODD_TRAIN_PTRN 0xf0 +#define SCDC_STATUS_FLAGS_2 0x42 #define SCDC_ERR_DET_0_L 0x50 #define SCDC_ERR_DET_0_H 0x51 @@ -65,6 +78,12 @@ #define SCDC_ERR_DET_CHECKSUM 0x56 +#define SCDC_ERR_DET_3_L 0x57 +#define SCDC_ERR_DET_3_H 0x58 + +#define SCDC_ERR_DET_RS_L 0x59 +#define SCDC_ERR_DET_RS_H 0x5a + #define SCDC_TEST_CONFIG_0 0xc0 #define SCDC_TEST_READ_REQUEST (1 << 7) #define SCDC_TEST_READ_REQUEST_DELAY(x) ((x) & 0x7f) diff --git a/include/drm/display/drm_scdc_helper.h b/include/drm/display/drm_scdc_helper.h index 34600476a1b9..a3b20adaac7e 100644 --- a/include/drm/display/drm_scdc_helper.h +++ b/include/drm/display/drm_scdc_helper.h @@ -24,17 +24,108 @@ #ifndef DRM_SCDC_HELPER_H #define DRM_SCDC_HELPER_H +#include <linux/errno.h> #include <linux/types.h> #include <drm/display/drm_scdc.h> struct drm_connector; struct i2c_adapter; +struct dentry; -ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer, - size_t size); -ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset, - const void *buffer, size_t size); +struct drm_scdc_status_flags { + /* Status Register 0 */ + bool clock_detected; + bool ch0_locked; + bool ch1_locked; + bool ch2_locked; + bool ln3_locked; + bool flt_ready; + bool dsc_fail; + + /* Status Register 1 */ + u8 ln0_training_pattern : 4; + u8 ln1_training_pattern : 4; + + /* Status Register 2 */ + u8 ln2_training_pattern : 4; + u8 ln3_training_pattern : 4; +}; + +enum drm_scdc_frl_rate { + SCDC_FRL_RATE_OFF = 0, + SCDC_FRL_RATE_3X3 = 1, + SCDC_FRL_RATE_6X3 = 2, + SCDC_FRL_RATE_6X4 = 3, + SCDC_FRL_RATE_8X4 = 4, + SCDC_FRL_RATE_10X4 = 5, + SCDC_FRL_RATE_12X4 = 6, + SCDC_FRL_RATE_RESV_7 = 7, + SCDC_FRL_RATE_RESV_8 = 8, + SCDC_FRL_RATE_RESV_9 = 9, + SCDC_FRL_RATE_RESV_10 = 10, + SCDC_FRL_RATE_RESV_11 = 11, + SCDC_FRL_RATE_RESV_12 = 12, + SCDC_FRL_RATE_RESV_13 = 13, + SCDC_FRL_RATE_RESV_14 = 14, + SCDC_FRL_RATE_RESV_15 = 15 +}; + +/** + * drm_scdc_num_frl_lanes - get number of lanes for a given FRL rate + * @rate: one of &enum drm_scdc_frl_rate + * + * For a given @rate, return the number of lanes it uses. + * + * Returns: %-EINVAL if @rate is not a valid FRL rate, or the number of lanes + * for a given &enum drm_scdc_frl_rate on success (including %0 for "off") + */ +static inline __pure int drm_scdc_num_frl_lanes(enum drm_scdc_frl_rate rate) +{ + switch (rate) { + case SCDC_FRL_RATE_OFF: + return 0; + case SCDC_FRL_RATE_3X3: + case SCDC_FRL_RATE_6X3: + return 3; + case SCDC_FRL_RATE_6X4: + case SCDC_FRL_RATE_8X4: + case SCDC_FRL_RATE_10X4: + case SCDC_FRL_RATE_12X4: + return 4; + default: + return -EINVAL; + } +} + +struct drm_scdc_state { + /** @stf: contents of the status flag registers */ + struct drm_scdc_status_flags stf; + /** @scramling_enabled: true if TMDS scrambling is on */ + bool scrambling_enabled; + /** @scrambling_detected: true if the sink actually detected scrambling */ + bool scrambling_detected; + /** + * @tmds_bclk_x40: true if TMDS bit period is 1/40th of the TMDS + * clock period, false if it's 1/10th of the clock period. + */ + bool tmds_bclk_x40; + /** @rate: FRL rate set by the source */ + enum drm_scdc_frl_rate rate : 4; + /** @ffe_levels: The FFE levels for @rate set by the source */ + u8 ffe_levels : 4; + /** @error_count: character error counts for each channel/link */ + u16 error_count[4]; + /** @rs_corrections: number of Reed-Solomon Corrections */ + u16 rs_corrections; + /** @scdc: raw SCDC data buffer */ + u8 scdc[256]; +}; + +int drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer, + size_t size); +int drm_scdc_write(struct i2c_adapter *adapter, u8 offset, const void *buffer, + size_t size); /** * drm_scdc_readb - read a single byte from SCDC @@ -77,4 +168,8 @@ bool drm_scdc_get_scrambling_status(struct drm_connector *connector); bool drm_scdc_set_scrambling(struct drm_connector *connector, bool enable); bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector, bool set); +int drm_scdc_read_state(struct drm_connector *connector, + struct drm_scdc_state *state); +void drm_scdc_debugfs_init(struct drm_connector *connector, struct dentry *root); + #endif diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index 224fae40ed2b..26ef67c835e2 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -135,6 +135,114 @@ enum drm_colorop_curve_1d_type { }; /** + * enum drm_colorop_fixed_matrix_type - type of Fixed Matrix + * + * Describes a Fixed Matrix operation to be applied by the DRM_COLOROP_FIXED_MATRIX + */ +enum drm_colorop_fixed_matrix_type { + /** + * @DRM_COLOROP_FM_YCBCR601_FULL_RGB: + * + * enum string "YCbCr 601 Full to RGB" + * + * Converts full-range YCbCr into full-range RGB using the BT.601 + * coefficients. Y is normalized to [0, 1] and Cb, Cr are centered + * at 0 (the nominal 0.5 offset removed) before the matrix:: + * + * | R | | 1.0 0.000000 1.402000 | | Y | + * | G | = | 1.0 -0.344136 -0.714136 | x | Cb | + * | B | | 1.0 1.772000 0.000000 | | Cr | + */ + DRM_COLOROP_FM_YCBCR601_FULL_RGB, + + /** + * @DRM_COLOROP_FM_YCBCR601_LIMITED_RGB: + * + * enum string "YCbCr 601 Limited to RGB" + * + * Converts limited- (narrow-) range YCbCr into full-range RGB using + * the BT.601 coefficients. Before the matrix Y is offset by 16/255 + * and Cb, Cr are centered at 0, following the studio-range + * convention of ITU-R BT.601:: + * + * | R | | 1.164384 0.000000 1.596027 | | Y - 16/255 | + * | G | = | 1.164384 -0.391762 -0.812968 | x | Cb | + * | B | | 1.164384 2.017232 0.000000 | | Cr | + */ + DRM_COLOROP_FM_YCBCR601_LIMITED_RGB, + + /** + * @DRM_COLOROP_FM_YCBCR709_FULL_RGB: + * + * enum string "YCbCr 709 Full to RGB" + * + * Converts full-range YCbCr into full-range RGB using the BT.709 + * coefficients. Y is normalized to [0, 1] and Cb, Cr are centered + * at 0 (the nominal 0.5 offset removed) before the matrix:: + * + * | R | | 1.0 0.000000 1.574800 | | Y | + * | G | = | 1.0 -0.187324 -0.468124 | x | Cb | + * | B | | 1.0 1.855600 0.000000 | | Cr | + */ + DRM_COLOROP_FM_YCBCR709_FULL_RGB, + + /** + * @DRM_COLOROP_FM_YCBCR709_LIMITED_RGB: + * + * enum string "YCbCr 709 Limited to RGB" + * + * Converts limited- (narrow-) range YCbCr into full-range RGB using + * the BT.709 coefficients. Before the matrix Y is offset by 16/255 + * and Cb, Cr are centered at 0, following the studio-range + * convention of ITU-R BT.709:: + * + * | R | | 1.164384 0.000000 1.792741 | | Y - 16/255 | + * | G | = | 1.164384 -0.213249 -0.532909 | x | Cb | + * | B | | 1.164384 2.112402 0.000000 | | Cr | + */ + DRM_COLOROP_FM_YCBCR709_LIMITED_RGB, + + /** + * @DRM_COLOROP_FM_YCBCR2020_NC_FULL_RGB: + * + * enum string "YCbCr 2020 NC Full to RGB" + * + * Converts full-range YCbCr into full-range RGB using the BT.2020 + * non-constant luminance coefficients. Y is normalized to [0, 1] + * and Cb, Cr are centered at 0 (the nominal 0.5 offset removed) + * before the matrix:: + * + * | R | | 1.0 0.000000 1.474600 | | Y | + * | G | = | 1.0 -0.164553 -0.571353 | x | Cb | + * | B | | 1.0 1.881400 0.000000 | | Cr | + */ + DRM_COLOROP_FM_YCBCR2020_NC_FULL_RGB, + + /** + * @DRM_COLOROP_FM_YCBCR2020_NC_LIMITED_RGB: + * + * enum string "YCbCr 2020 NC Limited to RGB" + * + * Converts limited- (narrow-) range YCbCr into full-range RGB using + * the BT.2020 non-constant luminance coefficients. Before the matrix + * Y is offset by 16/255 and Cb, Cr are centered at 0, following the + * studio-range convention of ITU-R BT.2020:: + * + * | R | | 1.164384 0.000000 1.678674 | | Y - 16/255 | + * | G | = | 1.164384 -0.187326 -0.650424 | x | Cb | + * | B | | 1.164384 2.141772 0.000000 | | Cr | + */ + DRM_COLOROP_FM_YCBCR2020_NC_LIMITED_RGB, + + /** + * @DRM_COLOROP_FM_COUNT: + * + * enum value denoting the size of the enum + */ + DRM_COLOROP_FM_COUNT +}; + +/** * struct drm_colorop_state - mutable colorop state */ struct drm_colorop_state { @@ -197,6 +305,13 @@ struct drm_colorop_state { */ enum drm_colorop_lut3d_interpolation_type lut3d_interpolation; + /** + * @fixed_matrix_type: + * + * Type of Fixed Matrix operation. + */ + enum drm_colorop_fixed_matrix_type fixed_matrix_type; + /** @state: backpointer to global drm_atomic_commit */ struct drm_atomic_commit *state; }; @@ -367,6 +482,13 @@ struct drm_colorop { struct drm_property *data_property; /** + * @fixed_matrix_type_property: + * + * Sub-type for DRM_COLOROP_FIXED_MATRIX type. + */ + struct drm_property *fixed_matrix_type_property; + + /** * @next_property: * * Read-only property to next colorop in the pipeline @@ -422,6 +544,10 @@ int drm_plane_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *col uint32_t lut_size, enum drm_colorop_lut3d_interpolation_type interpolation, uint32_t flags); +int drm_plane_colorop_fixed_matrix_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, + const struct drm_colorop_funcs *funcs, + u64 supported_fm, uint32_t flags); struct drm_colorop_state * drm_atomic_helper_colorop_create_state(struct drm_colorop *colorop); @@ -480,6 +606,7 @@ drm_get_colorop_lut1d_interpolation_name(enum drm_colorop_lut1d_interpolation_ty const char * drm_get_colorop_lut3d_interpolation_name(enum drm_colorop_lut3d_interpolation_type type); +const char *drm_get_colorop_fixed_matrix_type_name(enum drm_colorop_fixed_matrix_type type); void drm_colorop_set_next_property(struct drm_colorop *colorop, struct drm_colorop *next); diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index a0cf0268de48..c61345e6fb08 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -255,6 +255,44 @@ struct drm_scdc { }; /** + * struct drm_hdmi_vrr_cap - Information about VRR capabilities of a HDMI sink + * + * Describes the VRR support provided by HDMI 2.1 sink. The information is + * fetched fom additional HFVSDB blocks defined for HDMI 2.1. + */ +struct drm_hdmi_vrr_cap { + /** @fva: flag for Fast VActive (Quick Frame Transport) support */ + bool fva; + + /** @mcnmvrr: flag for Negative M VRR support */ + bool cnmvrr; + + /** @mcinema_vrr: flag for Cinema VRR support */ + bool cinema_vrr; + + /** @mdelta: flag for limited frame-to-frame compensation support */ + bool mdelta; + + /** + * @vrr_min : minimum supported variable refresh rate in Hz. + * Valid values only inide 1 - 48 range + */ + u16 vrr_min; + + /** + * @vrr_max : maximum supported variable refresh rate in Hz (optional). + * Valid values are either 0 (max based on video mode) or >= 100 + */ + u16 vrr_max; + + /** + * @supported: flag for vrr support based on checking for VRRmin and + * VRRmax values having correct values. + */ + bool supported; +}; + +/** * struct drm_hdmi_dsc_cap - DSC capabilities of HDMI sink * * Describes the DSC support provided by HDMI 2.1 sink. @@ -330,6 +368,15 @@ struct drm_hdmi_info { /** @max_lanes: supported by sink */ u8 max_lanes; + /** @fapa_start_location: flag for the FAPA in blanking support */ + bool fapa_start_location; + + /** @allm: flag for Auto Low Latency Mode support by sink */ + bool allm; + + /** @vrr_cap: VRR capabilities of the sink */ + struct drm_hdmi_vrr_cap vrr_cap; + /** @dsc_cap: DSC capabilities of the sink */ struct drm_hdmi_dsc_cap dsc_cap; }; @@ -765,7 +812,7 @@ enum drm_bus_flags { * struct drm_amd_vsdb_info - AMD-specific VSDB information * * This structure holds information parsed from the AMD Vendor-Specific Data - * Block (VSDB) version 3. + * Block (VSDB) versions 1 to 3. */ struct drm_amd_vsdb_info { /** @@ -774,6 +821,26 @@ struct drm_amd_vsdb_info { u8 version; /** + * @freesync_supported: FreeSync (variable refresh rate) supported by panel + */ + bool freesync_supported; + + /** + * @min_frame_rate: FreeSync minimum refresh rate in Hz + */ + u16 min_frame_rate; + + /** + * @max_frame_rate: FreeSync maximum refresh rate in Hz + */ + u16 max_frame_rate; + + /** + * @freesync_vcp_code: MCCS VCP code for FreeSync + */ + u8 freesync_vcp_code; + + /** * @replay_mode: Panel Replay supported */ bool replay_mode; diff --git a/include/drm/drm_gem_atomic_helper.h b/include/drm/drm_gem_atomic_helper.h index 3e01c619a25e..2deb0cd9ebbe 100644 --- a/include/drm/drm_gem_atomic_helper.h +++ b/include/drm/drm_gem_atomic_helper.h @@ -94,7 +94,9 @@ void __drm_gem_destroy_shadow_plane_state(struct drm_shadow_plane_state *shadow_ void __drm_gem_reset_shadow_plane(struct drm_plane *plane, struct drm_shadow_plane_state *shadow_plane_state); -void drm_gem_reset_shadow_plane(struct drm_plane *plane); +void __drm_gem_shadow_plane_state_init(struct drm_plane *plane, + struct drm_shadow_plane_state *shadow_plane_state); +struct drm_plane_state *drm_gem_create_shadow_plane_state(struct drm_plane *plane); struct drm_plane_state *drm_gem_duplicate_shadow_plane_state(struct drm_plane *plane); void drm_gem_destroy_shadow_plane_state(struct drm_plane *plane, struct drm_plane_state *plane_state); @@ -107,7 +109,7 @@ void drm_gem_destroy_shadow_plane_state(struct drm_plane *plane, * macro initializes struct drm_plane_funcs to use the rsp helper functions. */ #define DRM_GEM_SHADOW_PLANE_FUNCS \ - .reset = drm_gem_reset_shadow_plane, \ + .atomic_create_state = drm_gem_create_shadow_plane_state, \ .atomic_duplicate_state = drm_gem_duplicate_shadow_plane_state, \ .atomic_destroy_state = drm_gem_destroy_shadow_plane_state @@ -130,7 +132,8 @@ int drm_gem_simple_kms_begin_shadow_fb_access(struct drm_simple_display_pipe *pi struct drm_plane_state *plane_state); void drm_gem_simple_kms_end_shadow_fb_access(struct drm_simple_display_pipe *pipe, struct drm_plane_state *plane_state); -void drm_gem_simple_kms_reset_shadow_plane(struct drm_simple_display_pipe *pipe); +struct drm_plane_state * +drm_gem_simple_kms_create_shadow_plane_state(struct drm_simple_display_pipe *pipe); struct drm_plane_state * drm_gem_simple_kms_duplicate_shadow_plane_state(struct drm_simple_display_pipe *pipe); void drm_gem_simple_kms_destroy_shadow_plane_state(struct drm_simple_display_pipe *pipe, @@ -147,7 +150,7 @@ void drm_gem_simple_kms_destroy_shadow_plane_state(struct drm_simple_display_pip #define DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS \ .begin_fb_access = drm_gem_simple_kms_begin_shadow_fb_access, \ .end_fb_access = drm_gem_simple_kms_end_shadow_fb_access, \ - .reset_plane = drm_gem_simple_kms_reset_shadow_plane, \ + .create_plane_state = drm_gem_simple_kms_create_shadow_plane_state, \ .duplicate_plane_state = drm_gem_simple_kms_duplicate_shadow_plane_state, \ .destroy_plane_state = drm_gem_simple_kms_destroy_shadow_plane_state diff --git a/include/drm/drm_mipi_dbi.h b/include/drm/drm_mipi_dbi.h index 78ab23d844ce..d93e25fdc2e1 100644 --- a/include/drm/drm_mipi_dbi.h +++ b/include/drm/drm_mipi_dbi.h @@ -234,7 +234,7 @@ void drm_mipi_dbi_plane_helper_atomic_update(struct drm_plane *plane, */ #define DRM_MIPI_DBI_CRTC_FUNCS \ - .reset = drm_atomic_helper_crtc_reset, \ + .atomic_create_state = drm_atomic_helper_crtc_create_state, \ .set_config = drm_atomic_helper_set_config, \ .page_flip = drm_atomic_helper_page_flip, \ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, \ diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index 2adc5ac688e1..0805903c093b 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -644,6 +644,9 @@ static inline struct device *__drm_to_dev(const struct drm_device *drm) #define drm_err_ratelimited(drm, fmt, ...) \ __drm_printk((drm), err, _ratelimited, "*ERROR* " fmt, ##__VA_ARGS__) +#define drm_warn_ratelimited(drm, fmt, ...) \ + __drm_printk((drm), warn, _ratelimited, fmt, ##__VA_ARGS__) + #define drm_dbg_core(drm, fmt, ...) \ drm_dev_dbg(__drm_to_dev(drm), DRM_UT_CORE, fmt, ##__VA_ARGS__) diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h index cb672ce0e856..4a4a840d84f1 100644 --- a/include/drm/drm_simple_kms_helper.h +++ b/include/drm/drm_simple_kms_helper.h @@ -39,11 +39,11 @@ struct drm_simple_display_pipe_funcs { struct drm_plane_state *plane_state); int (*enable_vblank)(struct drm_simple_display_pipe *pipe); void (*disable_vblank)(struct drm_simple_display_pipe *pipe); - void (*reset_crtc)(struct drm_simple_display_pipe *pipe); struct drm_crtc_state * (*duplicate_crtc_state)(struct drm_simple_display_pipe *pipe); void (*destroy_crtc_state)(struct drm_simple_display_pipe *pipe, struct drm_crtc_state *crtc_state); void (*reset_plane)(struct drm_simple_display_pipe *pipe); + struct drm_plane_state * (*create_plane_state)(struct drm_simple_display_pipe *pipe); struct drm_plane_state * (*duplicate_plane_state)(struct drm_simple_display_pipe *pipe); void (*destroy_plane_state)(struct drm_simple_display_pipe *pipe, struct drm_plane_state *plane_state); @@ -68,10 +68,6 @@ int drm_simple_display_pipe_init(struct drm_device *dev, const uint64_t *format_modifiers, struct drm_connector *connector); -int drm_simple_encoder_init(struct drm_device *dev, - struct drm_encoder *encoder, - int encoder_type); - void *__drmm_simple_encoder_alloc(struct drm_device *dev, size_t size, size_t offset, int encoder_type); diff --git a/include/linux/gpu_buddy.h b/include/linux/gpu_buddy.h index 2c36124bb696..ddc4eca84175 100644 --- a/include/linux/gpu_buddy.h +++ b/include/linux/gpu_buddy.h @@ -43,8 +43,8 @@ /** * GPU_BUDDY_CLEAR_ALLOCATION - Prefer pre-cleared (zeroed) memory * - * Attempt to allocate from the clear tree first. If insufficient clear - * memory is available, falls back to dirty memory. Useful when the + * Attempt to allocate outside dirty-tracked ranges first. If insufficient + * clear memory is available, falls back to dirty memory. Useful when the * caller needs zeroed memory and wants to avoid GPU clear operations. */ #define GPU_BUDDY_CLEAR_ALLOCATION BIT(3) @@ -53,8 +53,8 @@ * GPU_BUDDY_CLEARED - Mark returned blocks as cleared * * Used with gpu_buddy_free_list() to indicate that the memory being - * freed has been cleared (zeroed). The blocks will be placed in the - * clear tree for future GPU_BUDDY_CLEAR_ALLOCATION requests. + * freed has been cleared (zeroed). The blocks will be removed from the + * dirty tracker for future GPU_BUDDY_CLEAR_ALLOCATION requests. */ #define GPU_BUDDY_CLEARED BIT(4) @@ -67,15 +67,17 @@ */ #define GPU_BUDDY_TRIM_DISABLE BIT(5) -enum gpu_buddy_free_tree { - GPU_BUDDY_CLEAR_TREE = 0, - GPU_BUDDY_DIRTY_TREE, - GPU_BUDDY_MAX_FREE_TREES, +/* + * Clear/dirty state of a free block. Ordered so a numerically larger value + * is "more clear" (DIRTY < MIXED < CLEAR) which lets subtree_block_state be + * maintained as a simple max-augment over the per-order free tree. + */ +enum gpu_block_state { + GPU_BLOCK_DIRTY = 0, + GPU_BLOCK_MIXED = 1, + GPU_BLOCK_CLEAR = 2, }; -#define for_each_free_tree(tree) \ - for ((tree) = 0; (tree) < GPU_BUDDY_MAX_FREE_TREES; (tree)++) - /** * struct gpu_buddy_block - Block within a buddy allocator * @@ -103,6 +105,13 @@ struct gpu_buddy_block { #define GPU_BUDDY_ALLOCATED (1 << 10) #define GPU_BUDDY_FREE (2 << 10) #define GPU_BUDDY_SPLIT (3 << 10) +/* + * GPU_BUDDY_HEADER_CLEAR has two roles: + * - FREE state: set when the block's full range is cleared (dirty + * tracker confirmed no overlap). + * - ALLOCATED state: set when the block was served from cleared memory, + * informing the caller that no GPU clear pass is needed. + */ #define GPU_BUDDY_HEADER_CLEAR GENMASK_ULL(9, 9) /* Free to be used, if needed in the future */ #define GPU_BUDDY_HEADER_UNUSED GENMASK_ULL(8, 6) @@ -128,14 +137,45 @@ struct gpu_buddy_block { struct list_head link; }; /* private: */ - struct list_head tmp_link; + enum gpu_block_state subtree_block_state; unsigned int subtree_max_alignment; + struct list_head tmp_link; + bool has_clear; }; /* Order-zero must be at least SZ_4K */ #define GPU_BUDDY_MAX_ORDER (63 - 12) /** + * struct gpu_dirty_extent - a contiguous dirty address range + * + * Tracks a single contiguous address range whose memory content is known + * to be dirty. Extents are non-overlapping and stored in an augmented + * red-black tree sorted by @start. The augmented value @subtree_max_size + * allows O(log N) search for an extent of at least a given size. + */ +struct gpu_dirty_extent { +/* private: */ + struct rb_node rb; + u64 start; + u64 end; + u64 subtree_max_size; +}; + +/** + * struct gpu_dirty_tracker - tracks dirty address intervals + * + * Maintains a set of non-overlapping dirty extents as an augmented + * red-black tree. + */ +struct gpu_dirty_tracker { +/* private: */ + struct rb_root root; + /* Total bytes of dirty memory currently tracked. */ + u64 total_dirty; +}; + +/** * struct gpu_buddy - GPU binary buddy allocator * * The buddy allocator provides efficient power-of-two memory allocation @@ -152,20 +192,21 @@ struct gpu_buddy_block { * @chunk_size: Minimum allocation granularity in bytes. Must be at least SZ_4K. * @size: Total size of the address space managed by this allocator in bytes. * @avail: Total free space currently available for allocation in bytes. - * @clear_avail: Free space available in the clear tree (zeroed memory) in bytes. - * This is a subset of @avail. * @lock_dep_map: Annotates gpu_buddy API with a driver provided lock. */ struct gpu_buddy { /* private: */ + /* Tracker of dirty address ranges (decoupled from free_tree). */ + struct gpu_dirty_tracker dirty; /* - * Array of red-black trees for free block management. - * Indexed as free_trees[clear/dirty][order] where: - * - Index 0 (GPU_BUDDY_CLEAR_TREE): blocks with zeroed content - * - Index 1 (GPU_BUDDY_DIRTY_TREE): blocks with unknown content - * Each tree holds free blocks of the corresponding order. + * One RB-tree per order containing all free blocks (clear and + * dirty alike). The augment field subtree_block_state (a max over + * the subtree of each block's state) lets clear allocations + * find the right-most fully-clear or mixed block in O(log N). + * Dirty free blocks coexist here but are also indexed by the + * @dirty tracker for fast dirty allocation lookups. */ - struct rb_root **free_trees; + struct rb_root *free_tree; /* * Array of root blocks representing the top-level blocks of the * binary tree(s). Multiple roots exist when the total size is not @@ -194,7 +235,6 @@ struct gpu_buddy { u64 chunk_size; u64 size; u64 avail; - u64 clear_avail; #ifdef CONFIG_LOCKDEP struct lockdep_map *lock_dep_map; #endif @@ -226,17 +266,32 @@ struct gpu_buddy { * * Ensure driver lock is held. */ -static inline void gpu_buddy_driver_lock_held(struct gpu_buddy *mm) +static inline void gpu_buddy_driver_lock_held(const struct gpu_buddy *mm) { if (mm->lock_dep_map) lockdep_assert(lock_is_held_type(mm->lock_dep_map, 0)); } #else -static inline void gpu_buddy_driver_lock_held(struct gpu_buddy *mm) +static inline void gpu_buddy_driver_lock_held(const struct gpu_buddy *mm) { } #endif +/** + * gpu_buddy_clear_avail - free space that is clear (zeroed), in bytes + * @mm: gpu buddy allocator + * + * A subset of @mm->avail. Derived on demand as @mm->avail minus the bytes + * the dirty tracker records as dirty, so it is always consistent with the + * tracker without a cached field to keep in sync. Zero for a fresh pool, + * which is fully dirty. + */ +static inline u64 gpu_buddy_clear_avail(const struct gpu_buddy *mm) +{ + gpu_buddy_driver_lock_held(mm); + return mm->avail - mm->dirty.total_dirty; +} + static inline u64 gpu_buddy_block_offset(const struct gpu_buddy_block *block) { diff --git a/include/sound/omap-hdmi-audio.h b/include/sound/omap-hdmi-audio.h index e5f82044a404..8dca92360ee0 100644 --- a/include/sound/omap-hdmi-audio.h +++ b/include/sound/omap-hdmi-audio.h @@ -34,6 +34,7 @@ struct omap_hdmi_audio_pdata { phys_addr_t audio_dma_addr; const struct omap_hdmi_audio_ops *ops; + void (*audio_hpd)(struct device *dev, bool connected); }; #endif /* __OMAP_HDMI_AUDIO_H__ */ diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index bd435effdcee..ed1a660a3dfd 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -955,6 +955,18 @@ enum drm_colorop_type { * color = lut3d[index] */ DRM_COLOROP_3D_LUT, + + /** + * @DRM_COLOROP_FIXED_MATRIX: + * + * enum string "Fixed Matrix" + * + * A Colorop block that performs a pre-defined matrix operation selected + * via the FIXED_MATRIX_TYPE enum property. The driver advertises the supported + * operations through this property. + */ + DRM_COLOROP_FIXED_MATRIX, + }; /** diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c index 4683f3d68022..47a41646de4b 100644 --- a/kernel/cgroup/dmem.c +++ b/kernel/cgroup/dmem.c @@ -28,7 +28,7 @@ DEFINE_STATIC_SRCU(dmemcg_srcu); struct dmem_cgroup_region { /** * @ref: References keeping the region alive. - * Keeps the region reference alive after a succesful RCU lookup. + * Keeps the region reference alive after a successful RCU lookup. */ struct kref ref; @@ -696,7 +696,7 @@ EXPORT_SYMBOL_GPL(dmem_cgroup_uncharge); * dmem_cgroup_try_charge() - Try charging a new allocation to a region. * @region: dmem region to charge * @size: Size (in bytes) to charge. - * @ret_pool: On succesfull allocation, the pool that is charged. + * @ret_pool: On successful allocation, the pool that is charged. * @ret_limit_pool: On a failed allocation, the limiting pool. * * This function charges the @region region for a size of @size bytes. diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c index c3034a8979dd..c32ec7c0b0cf 100644 --- a/sound/soc/ti/omap-hdmi.c +++ b/sound/soc/ti/omap-hdmi.c @@ -17,6 +17,7 @@ #include <sound/dmaengine_pcm.h> #include <uapi/sound/asound.h> #include <sound/asoundef.h> +#include <sound/jack.h> #include <sound/omap-hdmi-audio.h> #include "sdma-pcm.h" @@ -33,6 +34,8 @@ struct hdmi_audio_data { struct mutex current_stream_lock; struct snd_pcm_substream *current_stream; + struct snd_soc_jack jack; + atomic_t jack_state; }; static @@ -260,6 +263,34 @@ static void hdmi_dai_shutdown(struct snd_pcm_substream *substream, ad->current_stream = NULL; } +static void hdmi_audio_hpd(struct device *dev, bool connected) +{ + struct hdmi_audio_data *ad = dev_get_drvdata(dev); + + if (atomic_xchg(&ad->jack_state, connected) == connected) + return; + + snd_soc_jack_report(&ad->jack, + connected ? SND_JACK_AVOUT : 0, SND_JACK_AVOUT); + dev_dbg(dev, "HDMI %s\n", connected ? "CONNECTED" : "DISCONNECTED"); +} + +static int hdmi_audio_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct hdmi_audio_data *ad = snd_soc_card_get_drvdata(rtd->card); + int ret; + + ret = snd_soc_card_jack_new( + card, "HDMI", SND_JACK_AVOUT, &ad->jack); + if (ret < 0) { + dev_err(card->dev, "Cannot create HDMI jack: %i\n", ret); + return ret; + } + + return 0; +} + static const struct snd_soc_dai_ops hdmi_dai_ops = { .startup = hdmi_dai_startup, .hw_params = hdmi_dai_hw_params, @@ -369,25 +400,42 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) card->dai_link->stream_name = card->name; card->dai_link->cpus->dai_name = dev_name(ad->dssdev); card->dai_link->platforms->name = dev_name(ad->dssdev); + card->dai_link->init = hdmi_audio_dai_init; card->num_links = 1; card->dev = dev; + atomic_set(&ad->jack_state, -1); + ad->card = card; + + snd_soc_card_set_drvdata(card, ad); ret = devm_snd_soc_register_card(dev, card); if (ret) return dev_err_probe(dev, ret, "snd_soc_register_card() failed\n"); - snd_soc_card_set_drvdata(card, ad); - dev_set_drvdata(dev, ad); + ha->audio_hpd = hdmi_audio_hpd; + return 0; } +static void omap_hdmi_audio_remove(struct platform_device *pdev) +{ + struct omap_hdmi_audio_pdata *ha = pdev->dev.platform_data; + + /* + * hdmi4_unbind() holds audio lock across platform_device_unregister(), + * so audio_hpd() cannot run concurrently with this callback. + */ + ha->audio_hpd = NULL; +} + static struct platform_driver hdmi_audio_driver = { .driver = { .name = DRV_NAME, }, .probe = omap_hdmi_audio_probe, + .remove = omap_hdmi_audio_remove, }; module_platform_driver(hdmi_audio_driver); |
