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 + +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 + + 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 + +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 + + 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 +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 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 +R: Thomas Hellström +R: Himal Prasad Ghimiray +R: Honglei Huang +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 M: Javier Martinez Canillas 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 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx) 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 @@ amdxdna_gem_heap_alloc(struct amdxdna_gem_obj *abo) 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_hmm_unregister_all(struct amdxdna_gem_obj *abo) +{ + struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev); + struct amdxdna_umap *mapp, *tmp; + LIST_HEAD(dead); + + down_write(&xdna->notifier_lock); + 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); - struct amdxdna_gem_obj *abo = mapp->abo; - struct amdxdna_dev *xdna; mmu_interval_notifier_remove(&mapp->notifier); - - 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; - up_write(&xdna->notifier_lock); - 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 #include #include +#include #include #include #include @@ -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 #include +#include #include #include #include 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 +#include #include #include #include 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 #include +#include #include #include 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 #include #include +#include #include #include #include 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 #include #include #include 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 +#include #include #include #include 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 #include #include #include #include +#include #include @@ -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 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size) 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 @@ __alloc_range_bias(struct gpu_buddy *mm, 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,44 +1398,33 @@ 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) { + for (tmp = order; tmp <= mm->max_order; ++tmp) { + block = rbtree_last_free_block(&mm->free_tree[tmp]); + 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) - return ERR_PTR(-ENOSPC); - } + if (!block) + return ERR_PTR(-ENOSPC); BUG_ON(!gpu_buddy_block_is_free(block)); @@ -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 @@ gpu_buddy_offset_aligned_allocation(struct gpu_buddy *mm, 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 @@ static int __alloc_range(struct gpu_buddy *mm, 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_tree[order]; + if (RB_EMPTY_ROOT(root)) + return -ENOSPC; - root = &mm->free_trees[tree][order]; - if (rbtree_is_empty(root)) - continue; + iter = rb_last(root); + while (iter) { + block = rbtree_get_free_block(iter); - iter = rb_last(root); - while (iter) { - block = rbtree_get_free_block(iter); + rhs_offset = gpu_buddy_block_offset(block); - 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); - - /* Allocate the unaligned LHS offset using round_down */ + /* 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: - gpu_buddy_free_list_internal(mm, blocks); - iter = rb_prev(iter); + 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); + 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; - get_amd_vsdb(amdgpu_dm_connector, &vsdb_info); + 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; + } + + 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 (has_vsdb && as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) { + 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 (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; - 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 (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 @@ -1923,10 +1923,6 @@ enum dmub_cmd_type { * Command type used for interfacing with DPIA. */ DMUB_CMD__DPIA = 77, - /** - * Command type used for EDID CEA parsing - */ - DMUB_CMD__EDID_CEA = 79, /** * Command type used for getting usbc cable ID */ @@ -6886,69 +6882,6 @@ struct dmub_rb_cmd_transmitter_set_phy_fsm { struct dmub_rb_cmd_transmitter_set_phy_fsm_data data; /**< payload */ }; -/** - * 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. */ @@ -7801,10 +7734,6 @@ union dmub_rb_cmd { * Definition of a DMUB_CMD__DPIA_SET_TPS_NOTIFICATION command. */ 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. */ 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 #include +#include #include #include -#include #include #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); - - kfree(to_kcrtc_st(crtc->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 * @@ -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); - - kfree(plane->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 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); - 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 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) { - 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); + 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; - drm_log_draw_line(scanout, s, len, prefix_len); + if (drm_client_buffer_vmap_local(scanout->buffer, &map)) + return; + + if (scanout->line == 0) { + 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); + + 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 #include #include @@ -12,6 +13,7 @@ #include #include #include +#include /** * 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 #include #include #include +#include #include +#include #include #include @@ -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[] = { @@ -652,6 +744,21 @@ const char *drm_get_colorop_lut3d_interpolation_name(enum drm_colorop_lut3d_inte return colorop_lu3d_interpolation_name[type]; } +/** + * 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 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 #include #include +#include #include #include #include @@ -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 @@ -180,6 +180,48 @@ EXPORT_SYMBOL_GPL(drm_gem_plane_helper_prepare_fb); * Shadow-buffered Planes */ +/** + * __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 @@ -297,29 +339,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 @@ -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 #include +#include #include #include #include -#include #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 #include #include +#include #include #include #include #include -#include #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 #include #include -#include #include #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); - - kfree(to_imx_crtc_state(crtc->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 *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 #include #include +#include #include -#include #include #include @@ -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 #include #include +#include #include #include #include #include #include -#include #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 #include #include +#include #include -#include #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 #include -#include #include #include #include +#include #include #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 #include #include +#include #include -#include #include #include @@ -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 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc) 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); - - 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 * @@ -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) -{ - if (nv_connector->edid != edid) { - struct edid *old_edid = nv_connector->edid; +struct nouveau_rm_edid { + u8 *data; + size_t size; +}; - drm_connector_update_edid_property(&nv_connector->base, edid); - kfree(old_edid); - nv_connector->edid = edid; - } +static int +nouveau_connector_rm_edid_block(void *context, u8 *buf, unsigned int block, + size_t len) +{ + struct nouveau_rm_edid *rm = context; + size_t offset = (size_t)block * EDID_LENGTH; + + 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 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) } 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, +#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 - .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table, +static const struct drm_driver +driver_legacy_kms = { + .driver_features = DRIVER_GEM + | DRIVER_SYNCOBJ + | DRIVER_SYNCOBJ_TIMELINE + | DRIVER_MODESET + | DRIVER_RENDER, + NOUVEAU_DRIVER_OPS, +}; - .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_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 #include #include +#include #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); - - kfree(crtc->state); - 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 * @@ -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,30 +303,17 @@ 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; - } + + 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 hx83112a_remove(struct mipi_dsi_device *dsi) -{ - struct hx83112a_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 hx83112a_of_match[] = { { .compatible = "djn,9a-3r063-1102b" }, { /* sentinel */ } @@ -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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include