Re: [PATCH 1/3] pwm: Change prototype of .get_state() callback to return an error
On Wed, Sep 28, 2022 at 02:49:00PM +0200, Thierry Reding wrote: > On Fri, Sep 16, 2022 at 05:15:04PM +0200, Uwe Kleine-König wrote: > [...] > > diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c > > index 7b357d1cf642..811e6f424927 100644 > > --- a/drivers/pwm/pwm-crc.c > > +++ b/drivers/pwm/pwm-crc.c > > @@ -121,8 +121,8 @@ static int crc_pwm_apply(struct pwm_chip *chip, struct > > pwm_device *pwm, > > return 0; > > } > > > > -static void crc_pwm_get_state(struct pwm_chip *chip, struct pwm_device > > *pwm, > > - struct pwm_state *state) > > +static int crc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, > > +struct pwm_state *state) > > { > > struct crystalcove_pwm *crc_pwm = to_crc_pwm(chip); > > struct device *dev = crc_pwm->chip.dev; > > @@ -132,13 +132,13 @@ static void crc_pwm_get_state(struct pwm_chip *chip, > > struct pwm_device *pwm, > > error = regmap_read(crc_pwm->regmap, PWM0_CLK_DIV, &clk_div_reg); > > if (error) { > > dev_err(dev, "Error reading PWM0_CLK_DIV %d\n", error); > > - return; > > + return -EIO; > > } > > > > error = regmap_read(crc_pwm->regmap, PWM0_DUTY_CYCLE, &duty_cycle_reg); > > if (error) { > > dev_err(dev, "Error reading PWM0_DUTY_CYCLE %d\n", error); > > - return; > > + return -EIO; > > } > > In other drivers you propagate errors from regmap_read(), why not here? Oh, this is indeed wrong and should be "return error". > > > diff --git a/drivers/pwm/pwm-sprd.c b/drivers/pwm/pwm-sprd.c > > index 7004f55bbf11..aa06b3ce81a6 100644 > > --- a/drivers/pwm/pwm-sprd.c > > +++ b/drivers/pwm/pwm-sprd.c > > @@ -65,8 +65,8 @@ static void sprd_pwm_write(struct sprd_pwm_chip *spc, u32 > > hwid, > > writel_relaxed(val, spc->base + offset); > > } > > > > -static void sprd_pwm_get_state(struct pwm_chip *chip, struct pwm_device > > *pwm, > > - struct pwm_state *state) > > +static int sprd_pwm_get_state(struct pwm_chip *chip, struct pwm_device > > *pwm, > > + struct pwm_state *state) > > { > > struct sprd_pwm_chip *spc = > > container_of(chip, struct sprd_pwm_chip, chip); > > @@ -80,11 +80,8 @@ static void sprd_pwm_get_state(struct pwm_chip *chip, > > struct pwm_device *pwm, > > * reading to the registers. > > */ > > ret = clk_bulk_prepare_enable(SPRD_PWM_CHN_CLKS_NUM, chn->clks); > > - if (ret) { > > - dev_err(spc->dev, "failed to enable pwm%u clocks\n", > > - pwm->hwpwm); > > This might be useful information, so perhaps leave it in? Ok, I don't like .get_state emitting an error, but agreed, that's an orthogonal issue that shouldn't be addressed en passant in this change. > [...] > > diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c > > index c8445b0a3339..ead909400e64 100644 > > --- a/drivers/pwm/pwm-sun4i.c > > +++ b/drivers/pwm/pwm-sun4i.c > > @@ -108,9 +108,9 @@ static inline void sun4i_pwm_writel(struct > > sun4i_pwm_chip *chip, > > writel(val, chip->base + offset); > > } > > > > -static void sun4i_pwm_get_state(struct pwm_chip *chip, > > - struct pwm_device *pwm, > > - struct pwm_state *state) > > +static int sun4i_pwm_get_state(struct pwm_chip *chip, > > + struct pwm_device *pwm, > > + struct pwm_state *state) > > { > > struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); > > u64 clk_rate, tmp; > > @@ -132,7 +132,7 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip, > > state->duty_cycle = DIV_ROUND_UP_ULL(state->period, 2); > > state->polarity = PWM_POLARITY_NORMAL; > > state->enabled = true; > > - return; > > + return 0; > > } > > > > if ((PWM_REG_PRESCAL(val, pwm->hwpwm) == PWM_PRESCAL_MASK) && > > @@ -142,7 +142,8 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip, > > prescaler = prescaler_table[PWM_REG_PRESCAL(val, pwm->hwpwm)]; > > > > if (prescaler == 0) > > - return; > > + /* huh? is this an error? */ > > + return 0; > > Yeah, I think this would count as an error. The prescaler value returned > from that table is 0 in what seems to be "invalid" configurations. If > you look at how this is used in sun4i_pwm_calculate(), these entries are > skipped for the computation of the duty cycle. So I would expect this to > happen in either an invalidly configured or completely unconfigured PWM. > > That raises the question about what to do in these cases. If we return > an error, that could potentially throw off consumers. So perhaps the > closest would be to return a disabled PWM? Or perhaps it'd be up to the > consumer to provide some fallback configuration for invalidly configured > or unconfigured PWMs. This is something I
Re: New subsystem for acceleration devices
On Mon, Sep 26, 2022 at 11:16 AM Christoph Hellwig wrote: > > Btw, there is another interesting thing around on the block: > > NVMe Computational Storage devices. Don't be fooled by the name, much > of it is not about neither computation not storage, but it allows to > use the existing NVMe queuing model model to allow access to arbitrary > accelerators, including a way to expose access to on-device memory. > > The probably most current version is here: > > https://www.snia.org/educational-library/nvme-computational-storage-update-standard-2022 Thanks for the link. Indeed, there were some people in the BOF that mentioned computational storage as something that is relevant. I'll watch the presentation to understand the direction it is going and how it maps to what we were planning to do. > > The first version will be rather limited and miss some important > functionality like directly accessing host DRAM or CXL integration, > but much of that is planned. The initial version also probably won't > be able to be supported by Linux at all, but we need to think hard about > how to support it. > > It woud also be really elpful to get more people with accelerator > experience into the NVMe working group. I will be happy to help and contribute. Oded
[PATCH] drm/virtio: fix the black screen error when waking
Mainline: NA Category: Bugfix CVE: NA When the system wakes up from sleeping, all virtio devices will be reset. However, restting virtio gpu device will delete the virtqueue and resources saved on the virtio gpu backend, making it impossible for the virtio gpu driver to communicate with the virtio gpu backend and causing a black screen error. Rebuild the virtqueue and resources can avoid this problem. Signed-off-by: liweishi Suggested-by: Ming Xie --- drivers/gpu/drm/virtio/virtgpu_drv.c| 22 +++- drivers/gpu/drm/virtio/virtgpu_drv.h| 12 +++ drivers/gpu/drm/virtio/virtgpu_kms.c| 47 + drivers/gpu/drm/virtio/virtgpu_object.c | 40 + 4 files changed, 120 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index 0035affc3e59..910b146d7e52 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -129,6 +129,23 @@ static void virtio_gpu_config_changed(struct virtio_device *vdev) schedule_work(&vgdev->config_changed_work); } +#ifdef CONFIG_PM_SLEEP +/* + * when the system wakes up from sleeping, all virtio devices + * will be reset. However, restting virtio gpu device will delete + * the virtqueue and resources saved on the virtio gpu backend, + * making it impossible for the virtio gpu driver to communicate + * with the virtio gpu backend and causing a black screen problem. + * rebuild the virtqueue and resources can avoid this problem. + */ +static int virtio_gpu_restore(struct virtio_device *vdev) +{ + struct drm_device *dev = vdev->priv; + + return virtio_gpu_rebuild(dev); +} +#endif + static struct virtio_device_id id_table[] = { { VIRTIO_ID_GPU, VIRTIO_DEV_ANY_ID }, { 0 }, @@ -156,7 +173,10 @@ static struct virtio_driver virtio_gpu_driver = { .id_table = id_table, .probe = virtio_gpu_probe, .remove = virtio_gpu_remove, - .config_changed = virtio_gpu_config_changed + .config_changed = virtio_gpu_config_changed, +#ifdef CONFIG_PM_SLEEP + .restore = virtio_gpu_restore, +#endif }; module_virtio_driver(virtio_gpu_driver); diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 9b98470593b0..b652b3790bb1 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -212,6 +212,13 @@ struct virtio_gpu_drv_cap_cache { atomic_t is_valid; }; +struct virtio_gpu_object_resource { + struct list_head head; + struct virtio_gpu_object *bo; + /* parameters used in resource creation */ + struct virtio_gpu_object_params params; +}; + struct virtio_gpu_device { struct drm_device *ddev; @@ -262,6 +269,8 @@ struct virtio_gpu_device { spinlock_t resource_export_lock; /* protects map state and host_visible_mm */ spinlock_t host_visible_lock; + /* stores resource creation information */ + struct list_head res_list; }; struct virtio_gpu_fpriv { @@ -285,6 +294,7 @@ void virtio_gpu_deinit(struct drm_device *dev); void virtio_gpu_release(struct drm_device *dev); int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file); void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file); +int virtio_gpu_rebuild(struct drm_device *dev); /* virtgpu_gem.c */ int virtio_gpu_gem_object_open(struct drm_gem_object *obj, @@ -456,6 +466,8 @@ bool virtio_gpu_is_shmem(struct virtio_gpu_object *bo); int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, uint32_t *resid); +void virtio_gpu_recreate_resource(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object_resource *vgor); /* virtgpu_prime.c */ int virtio_gpu_resource_assign_uuid(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *bo); diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index 27b7f14dae89..72a6bb3e9502 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -149,6 +149,7 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) spin_lock_init(&vgdev->fence_drv.lock); INIT_LIST_HEAD(&vgdev->fence_drv.fences); INIT_LIST_HEAD(&vgdev->cap_cache); + INIT_LIST_HEAD(&vgdev->res_list); INIT_WORK(&vgdev->config_changed_work, virtio_gpu_config_changed_work_func); @@ -271,6 +272,15 @@ static void virtio_gpu_cleanup_cap_cache(struct virtio_gpu_device *vgdev) } } +static void virtio_gpu_cleanup_object_resources(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_object_resource *vgor, *tmp; + + list_for_each_entry_safe(vgor, tmp, &vgdev->res_list, head) { + kfree(vgor); + } +} + void virtio_gpu_deinit(struct dr
Re: linux-next: Tree for Sep 28 (drivers/gpu/drm/msm/msm_gem_shrinker.c)
On 9/28/22 12:26, broo...@kernel.org wrote: > Hi all, > > Changes since 20220927: > on x86_64: ../drivers/gpu/drm/msm/msm_gem_shrinker.c: In function ‘can_block’: ../drivers/gpu/drm/msm/msm_gem_shrinker.c:29:28: error: ‘__GFP_ATOMIC’ undeclared (first use in this function); did you mean ‘GFP_ATOMIC’? 29 | if (sc->gfp_mask & __GFP_ATOMIC) |^~~~ |GFP_ATOMIC -- ~Randy
Re: [PATCH linux-next v2] backlight: use sysfs_emit() to instead of scnprintf()
Thanks for your reply. This patch about lp8788 fixes have already applied in: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/?id=61e86018aebffd172c9da967f284469924d5e947 So this patch just fixed the rest. Best Regards, YE XINGCHEN
Re: [Intel-gfx] [PATCH 05/16] drm/i915/vm_bind: Implement bind and unbind of object
On Wed, Sep 28, 2022 at 01:06:18PM -0700, Welty, Brian wrote: On 9/27/2022 11:19 PM, Niranjana Vishwanathapura wrote: Add uapi and implement support for bind and unbind of an object at the specified GPU virtual addresses. The vm_bind mode is not supported in legacy execbuf2 ioctl. It will be supported only in the newer execbuf3 ioctl. Signed-off-by: Niranjana Vishwanathapura Signed-off-by: Prathap Kumar Valsan Signed-off-by: Andi Shyti --- drivers/gpu/drm/i915/Makefile | 1 + .../gpu/drm/i915/gem/i915_gem_execbuffer.c| 5 + drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h | 26 ++ .../drm/i915/gem/i915_gem_vm_bind_object.c| 306 ++ drivers/gpu/drm/i915/gt/intel_gtt.c | 10 + drivers/gpu/drm/i915/gt/intel_gtt.h | 17 + drivers/gpu/drm/i915/i915_driver.c| 3 + drivers/gpu/drm/i915/i915_vma.c | 1 + drivers/gpu/drm/i915/i915_vma_types.h | 14 + include/uapi/drm/i915_drm.h | 112 +++ 10 files changed, 495 insertions(+) create mode 100644 drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h create mode 100644 drivers/gpu/drm/i915/gem/i915_gem_vm_bind_object.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index a26edcdadc21..9bf939ef18ea 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -166,6 +166,7 @@ gem-y += \ gem/i915_gem_ttm_move.o \ gem/i915_gem_ttm_pm.o \ gem/i915_gem_userptr.o \ + gem/i915_gem_vm_bind_object.o \ gem/i915_gem_wait.o \ gem/i915_gemfs.o i915-y += \ diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index cd75b0ca2555..f85f10cf9c34 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -781,6 +781,11 @@ static int eb_select_context(struct i915_execbuffer *eb) if (unlikely(IS_ERR(ctx))) return PTR_ERR(ctx); + if (ctx->vm->vm_bind_mode) { + i915_gem_context_put(ctx); + return -EOPNOTSUPP; + } + eb->gem_context = ctx; if (i915_gem_context_has_full_ppgtt(ctx)) eb->invalid_flags |= EXEC_OBJECT_NEEDS_GTT; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h b/drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h new file mode 100644 index ..36262a6357b5 --- /dev/null +++ b/drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2022 Intel Corporation + */ + +#ifndef __I915_GEM_VM_BIND_H +#define __I915_GEM_VM_BIND_H + +#include + +struct drm_device; +struct drm_file; +struct i915_address_space; +struct i915_vma; + +struct i915_vma * +i915_gem_vm_bind_lookup_vma(struct i915_address_space *vm, u64 va); + +int i915_gem_vm_bind_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); +int i915_gem_vm_unbind_ioctl(struct drm_device *dev, void *data, +struct drm_file *file); + +void i915_gem_vm_unbind_all(struct i915_address_space *vm); + +#endif /* __I915_GEM_VM_BIND_H */ diff --git a/drivers/gpu/drm/i915/gem/i915_gem_vm_bind_object.c b/drivers/gpu/drm/i915/gem/i915_gem_vm_bind_object.c new file mode 100644 index ..e529162abd2c --- /dev/null +++ b/drivers/gpu/drm/i915/gem/i915_gem_vm_bind_object.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2022 Intel Corporation + */ + +#include + +#include + +#include "gem/i915_gem_context.h" +#include "gem/i915_gem_vm_bind.h" + +#include "gt/intel_gpu_commands.h" + +#define START(node) ((node)->start) +#define LAST(node) ((node)->last) + +INTERVAL_TREE_DEFINE(struct i915_vma, rb, u64, __subtree_last, +START, LAST, static inline, i915_vm_bind_it) + +#undef START +#undef LAST + +/** + * DOC: VM_BIND/UNBIND ioctls + * + * DRM_I915_GEM_VM_BIND/UNBIND ioctls allows UMD to bind/unbind GEM buffer + * objects (BOs) or sections of a BOs at specified GPU virtual addresses on a + * specified address space (VM). Multiple mappings can map to the same physical + * pages of an object (aliasing). These mappings (also referred to as persistent + * mappings) will be persistent across multiple GPU submissions (execbuf calls) + * issued by the UMD, without user having to provide a list of all required + * mappings during each submission (as required by older execbuf mode). + * + * The VM_BIND/UNBIND calls allow UMDs to request a timeline out fence for + * signaling the completion of bind/unbind operation. + * + * VM_BIND feature is advertised to user via I915_PARAM_VM_BIND_VERSION. + * User has to opt-in for VM_BIND mode of binding for an address space (VM) + * during VM creation time via I915_VM_CREATE_FLAGS_USE_VM_BIND extension. + * + * VM_BIND/UNBIND ioctl calls executed on different CPU threads concurrently + * are not ordered. Furthermore, p
Re: [PATCH 05/16] drm/i915/vm_bind: Implement bind and unbind of object
On Wed, Sep 28, 2022 at 06:52:21PM +0100, Matthew Auld wrote: On 28/09/2022 07:19, Niranjana Vishwanathapura wrote: Add uapi and implement support for bind and unbind of an object at the specified GPU virtual addresses. The vm_bind mode is not supported in legacy execbuf2 ioctl. It will be supported only in the newer execbuf3 ioctl. Signed-off-by: Niranjana Vishwanathapura Signed-off-by: Prathap Kumar Valsan Signed-off-by: Andi Shyti --- drivers/gpu/drm/i915/Makefile | 1 + .../gpu/drm/i915/gem/i915_gem_execbuffer.c| 5 + drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h | 26 ++ .../drm/i915/gem/i915_gem_vm_bind_object.c| 306 ++ drivers/gpu/drm/i915/gt/intel_gtt.c | 10 + drivers/gpu/drm/i915/gt/intel_gtt.h | 17 + drivers/gpu/drm/i915/i915_driver.c| 3 + drivers/gpu/drm/i915/i915_vma.c | 1 + drivers/gpu/drm/i915/i915_vma_types.h | 14 + include/uapi/drm/i915_drm.h | 112 +++ 10 files changed, 495 insertions(+) create mode 100644 drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h create mode 100644 drivers/gpu/drm/i915/gem/i915_gem_vm_bind_object.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index a26edcdadc21..9bf939ef18ea 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -166,6 +166,7 @@ gem-y += \ gem/i915_gem_ttm_move.o \ gem/i915_gem_ttm_pm.o \ gem/i915_gem_userptr.o \ + gem/i915_gem_vm_bind_object.o \ gem/i915_gem_wait.o \ gem/i915_gemfs.o i915-y += \ diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index cd75b0ca2555..f85f10cf9c34 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -781,6 +781,11 @@ static int eb_select_context(struct i915_execbuffer *eb) if (unlikely(IS_ERR(ctx))) return PTR_ERR(ctx); + if (ctx->vm->vm_bind_mode) { + i915_gem_context_put(ctx); + return -EOPNOTSUPP; + } + eb->gem_context = ctx; if (i915_gem_context_has_full_ppgtt(ctx)) eb->invalid_flags |= EXEC_OBJECT_NEEDS_GTT; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h b/drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h new file mode 100644 index ..36262a6357b5 --- /dev/null +++ b/drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2022 Intel Corporation + */ + +#ifndef __I915_GEM_VM_BIND_H +#define __I915_GEM_VM_BIND_H + +#include + +struct drm_device; +struct drm_file; +struct i915_address_space; +struct i915_vma; + +struct i915_vma * +i915_gem_vm_bind_lookup_vma(struct i915_address_space *vm, u64 va); + +int i915_gem_vm_bind_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); +int i915_gem_vm_unbind_ioctl(struct drm_device *dev, void *data, +struct drm_file *file); + +void i915_gem_vm_unbind_all(struct i915_address_space *vm); + +#endif /* __I915_GEM_VM_BIND_H */ diff --git a/drivers/gpu/drm/i915/gem/i915_gem_vm_bind_object.c b/drivers/gpu/drm/i915/gem/i915_gem_vm_bind_object.c new file mode 100644 index ..e529162abd2c --- /dev/null +++ b/drivers/gpu/drm/i915/gem/i915_gem_vm_bind_object.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2022 Intel Corporation + */ + +#include + +#include + +#include "gem/i915_gem_context.h" +#include "gem/i915_gem_vm_bind.h" + +#include "gt/intel_gpu_commands.h" + +#define START(node) ((node)->start) +#define LAST(node) ((node)->last) + +INTERVAL_TREE_DEFINE(struct i915_vma, rb, u64, __subtree_last, +START, LAST, static inline, i915_vm_bind_it) + +#undef START +#undef LAST + +/** + * DOC: VM_BIND/UNBIND ioctls + * + * DRM_I915_GEM_VM_BIND/UNBIND ioctls allows UMD to bind/unbind GEM buffer + * objects (BOs) or sections of a BOs at specified GPU virtual addresses on a + * specified address space (VM). Multiple mappings can map to the same physical + * pages of an object (aliasing). These mappings (also referred to as persistent + * mappings) will be persistent across multiple GPU submissions (execbuf calls) + * issued by the UMD, without user having to provide a list of all required + * mappings during each submission (as required by older execbuf mode). + * + * The VM_BIND/UNBIND calls allow UMDs to request a timeline out fence for + * signaling the completion of bind/unbind operation. + * + * VM_BIND feature is advertised to user via I915_PARAM_VM_BIND_VERSION. + * User has to opt-in for VM_BIND mode of binding for an address space (VM) + * during VM creation time via I915_VM_CREATE_FLAGS_USE_VM_BIND extension. + * + * VM_BIND/UNBIND ioctl calls executed on different CPU threads concurrently + * are not ordered. Furthermore, parts
Re: [PATCH 03/16] drm/i915/vm_bind: Expose i915_gem_object_max_page_size()
On Wed, Sep 28, 2022 at 06:40:01PM +0100, Matthew Auld wrote: On 28/09/2022 07:19, Niranjana Vishwanathapura wrote: Expose i915_gem_object_max_page_size() function non-static which will be used by the vm_bind feature. Signed-off-by: Niranjana Vishwanathapura Signed-off-by: Andi Shyti --- drivers/gpu/drm/i915/gem/i915_gem_create.c | 19 ++- drivers/gpu/drm/i915/gem/i915_gem_object.h | 2 ++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_create.c b/drivers/gpu/drm/i915/gem/i915_gem_create.c index 33673fe7ee0a..4aa7b5582b8e 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_create.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_create.c @@ -15,10 +15,19 @@ #include "i915_trace.h" #include "i915_user_extensions.h" -static u32 object_max_page_size(struct intel_memory_region **placements, - unsigned int n_placements) +/** + * i915_gem_object_max_page_size() - max of min_page_size of the regions + * @placements: list of regions + * @n_placements: number of the placements + * + * Calculates the max of the min_page_size of a list of placements passed in. + * + * Return: max of the min_page_size "max of the min_page_size, or I915_GTT_PAGE_SIZE_4K if zero placements." ok, will update. Regards, Niranjana Acked-by: Matthew Auld + */ +u32 i915_gem_object_max_page_size(struct intel_memory_region **placements, + unsigned int n_placements) { - u32 max_page_size = 0; + u32 max_page_size = I915_GTT_PAGE_SIZE_4K; int i; for (i = 0; i < n_placements; i++) { @@ -28,7 +37,6 @@ static u32 object_max_page_size(struct intel_memory_region **placements, max_page_size = max_t(u32, max_page_size, mr->min_page_size); } - GEM_BUG_ON(!max_page_size); return max_page_size; } @@ -99,7 +107,8 @@ __i915_gem_object_create_user_ext(struct drm_i915_private *i915, u64 size, i915_gem_flush_free_objects(i915); - size = round_up(size, object_max_page_size(placements, n_placements)); + size = round_up(size, i915_gem_object_max_page_size(placements, + n_placements)); if (size == 0) return ERR_PTR(-EINVAL); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h index a3b7551a57fc..d53d01b1860a 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object.h +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h @@ -47,6 +47,8 @@ static inline bool i915_gem_object_size_2big(u64 size) } void i915_gem_init__objects(struct drm_i915_private *i915); +u32 i915_gem_object_max_page_size(struct intel_memory_region **placements, + unsigned int n_placements); void i915_objects_module_exit(void); int i915_objects_module_init(void);
Re: [PATCH 02/16] drm/i915/vm_bind: Add __i915_sw_fence_await_reservation()
On Wed, Sep 28, 2022 at 06:39:05PM +0100, Matthew Auld wrote: On 28/09/2022 07:19, Niranjana Vishwanathapura wrote: Add function __i915_sw_fence_await_reservation() for asynchronous wait on a dma-resv object with specified dma_resv_usage. This is required for async vma unbind with vm_bind. Signed-off-by: Niranjana Vishwanathapura --- drivers/gpu/drm/i915/i915_sw_fence.c | 28 +--- drivers/gpu/drm/i915/i915_sw_fence.h | 23 +-- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_sw_fence.c b/drivers/gpu/drm/i915/i915_sw_fence.c index cc2a8821d22a..b7a10c374a08 100644 --- a/drivers/gpu/drm/i915/i915_sw_fence.c +++ b/drivers/gpu/drm/i915/i915_sw_fence.c @@ -7,7 +7,6 @@ #include #include #include -#include #include "i915_sw_fence.h" #include "i915_selftest.h" @@ -569,11 +568,26 @@ int __i915_sw_fence_await_dma_fence(struct i915_sw_fence *fence, return ret; } -int i915_sw_fence_await_reservation(struct i915_sw_fence *fence, - struct dma_resv *resv, - bool write, - unsigned long timeout, - gfp_t gfp) +/** + * __i915_sw_fence_await_reservation() - Setup a fence to wait on a dma-resv + * object with specified usage. + * @fence: the fence that needs to wait + * @resv: dma-resv object + * @usage: dma_resv_usage (See enum dma_resv_usage) + * @timeout: how long to wait in jiffies + * @gfp: allocation mode + * + * Setup the @fence to asynchronously wait on dma-resv object @resv for usage + * @usage to complete before signaling. s/usage @usage/@usage/ ? Thanks for the review Matt. Sure, will fix. + * + * Returns 0 if there is nothing to wait on, -ve upon error and >0 upon What does "-ve" mean btw? -ve error code. Will update Regards, Niranjana Acked-by: Matthew Auld + * successfully setting up the wait. + */ +int __i915_sw_fence_await_reservation(struct i915_sw_fence *fence, + struct dma_resv *resv, + enum dma_resv_usage usage, + unsigned long timeout, + gfp_t gfp) { struct dma_resv_iter cursor; struct dma_fence *f; @@ -582,7 +596,7 @@ int i915_sw_fence_await_reservation(struct i915_sw_fence *fence, debug_fence_assert(fence); might_sleep_if(gfpflags_allow_blocking(gfp)); - dma_resv_iter_begin(&cursor, resv, dma_resv_usage_rw(write)); + dma_resv_iter_begin(&cursor, resv, usage); dma_resv_for_each_fence_unlocked(&cursor, f) { pending = i915_sw_fence_await_dma_fence(fence, f, timeout, gfp); diff --git a/drivers/gpu/drm/i915/i915_sw_fence.h b/drivers/gpu/drm/i915/i915_sw_fence.h index f752bfc7c6e1..9c4859dc4c0d 100644 --- a/drivers/gpu/drm/i915/i915_sw_fence.h +++ b/drivers/gpu/drm/i915/i915_sw_fence.h @@ -10,13 +10,13 @@ #define _I915_SW_FENCE_H_ #include +#include #include #include #include /* for NOTIFY_DONE */ #include struct completion; -struct dma_resv; struct i915_sw_fence; enum i915_sw_fence_notify { @@ -89,11 +89,22 @@ int i915_sw_fence_await_dma_fence(struct i915_sw_fence *fence, unsigned long timeout, gfp_t gfp); -int i915_sw_fence_await_reservation(struct i915_sw_fence *fence, - struct dma_resv *resv, - bool write, - unsigned long timeout, - gfp_t gfp); +int __i915_sw_fence_await_reservation(struct i915_sw_fence *fence, + struct dma_resv *resv, + enum dma_resv_usage usage, + unsigned long timeout, + gfp_t gfp); + +static inline int i915_sw_fence_await_reservation(struct i915_sw_fence *fence, + struct dma_resv *resv, + bool write, + unsigned long timeout, + gfp_t gfp) +{ + return __i915_sw_fence_await_reservation(fence, resv, +dma_resv_usage_rw(write), +timeout, gfp); +} bool i915_sw_fence_await(struct i915_sw_fence *fence); void i915_sw_fence_complete(struct i915_sw_fence *fence);
Re: [PATCH 1/7] mm/memory.c: Fix race when faulting a device private page
Alistair Popple writes: > Michael Ellerman writes: >> Alistair Popple writes: >>> When the CPU tries to access a device private page the migrate_to_ram() >>> callback associated with the pgmap for the page is called. However no >>> reference is taken on the faulting page. Therefore a concurrent >>> migration of the device private page can free the page and possibly the >>> underlying pgmap. This results in a race which can crash the kernel due >>> to the migrate_to_ram() function pointer becoming invalid. It also means >>> drivers can't reliably read the zone_device_data field because the page >>> may have been freed with memunmap_pages(). >>> >>> Close the race by getting a reference on the page while holding the ptl >>> to ensure it has not been freed. Unfortunately the elevated reference >>> count will cause the migration required to handle the fault to fail. To >>> avoid this failure pass the faulting page into the migrate_vma functions >>> so that if an elevated reference count is found it can be checked to see >>> if it's expected or not. >>> >>> Signed-off-by: Alistair Popple >>> --- >>> arch/powerpc/kvm/book3s_hv_uvmem.c | 15 ++- >>> drivers/gpu/drm/amd/amdkfd/kfd_migrate.c | 17 +++-- >>> drivers/gpu/drm/amd/amdkfd/kfd_migrate.h | 2 +- >>> drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 11 +--- >>> include/linux/migrate.h | 8 ++- >>> lib/test_hmm.c | 7 ++--- >>> mm/memory.c | 16 +++- >>> mm/migrate.c | 34 ++--- >>> mm/migrate_device.c | 18 + >>> 9 files changed, 87 insertions(+), 41 deletions(-) >>> >>> diff --git a/arch/powerpc/kvm/book3s_hv_uvmem.c >>> b/arch/powerpc/kvm/book3s_hv_uvmem.c >>> index 5980063..d4eacf4 100644 >>> --- a/arch/powerpc/kvm/book3s_hv_uvmem.c >>> +++ b/arch/powerpc/kvm/book3s_hv_uvmem.c >>> @@ -508,10 +508,10 @@ unsigned long kvmppc_h_svm_init_start(struct kvm *kvm) ... >>> @@ -994,7 +997,7 @@ static vm_fault_t kvmppc_uvmem_migrate_to_ram(struct >>> vm_fault *vmf) >>> >>> if (kvmppc_svm_page_out(vmf->vma, vmf->address, >>> vmf->address + PAGE_SIZE, PAGE_SHIFT, >>> - pvt->kvm, pvt->gpa)) >>> + pvt->kvm, pvt->gpa, vmf->page)) >>> return VM_FAULT_SIGBUS; >>> else >>> return 0; >> >> I don't have a UV test system, but as-is it doesn't even compile :) > > Ugh, thanks. I did get as far as installing a PPC cross-compiler and > building a kernel. Apparently I did not get as far as enabling > CONFIG_PPC_UV :) No worries, that's really on us. If we're going to keep the code in the tree then it should really be enabled in at least one of our defconfigs. cheers
Bug in the VirtIO GPU driver since the RC7 of kernel 6.0
Hi All, I have found the issue. I cross compiled this kernel with GCC 11.2.0 on Ubuntu 22.04.1. I cross compiled the same kernel with GCC 9.4.0 again. This time on Ubuntu 20.04.5. KVM with the VirtIO GPU works with the GCC 9.4.0 compiled kernel. — Christian I wrote: Hello, Xorg doesn't start anymore in a virtual e5500 QEMU KVM HV machine with the VirtIO GPU [1] since the RC7 of kernel 6.0. [2] Please find attached the kernel config. Thanks, Christian [1] qemu-system-ppc64 -M ppce500 -cpu e5500 -m 1024 -kernel uImage-6.0 -drive format=raw,file=void-live-powerpc-20220129.img,index=0,if=virtio -netdev user,id=mynet0 -device virtio-net,netdev=mynet0 -append "rw root=/dev/vda2" -device virtio-gpu -device virtio-mouse-pci -device virtio-keyboard-pci -device pci-ohci,id=newusb -audiodev id=sndbe,driver=pa,server=/run/user/1000/pulse/native -device usb-audio,bus=newusb.0 -enable-kvm -smp 4 -fsdev local,security_model=passthrough,id=fsdev0,path=/home/amigaone/Music -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=hostshare [2] Error messages in a virtual Void PPC machine: [drm] pci: virtio-gpu-pci detected at :00:02.0 [drm] features: -virgl +edid -resource_blob -host_visible [drm] features: -context_init [drm] number of scanouts: 1 [drm] number of cap sets: 0 [drm] Initialized virtio_gpu 0.1.0 0 for virtio1 on minor 0 BUG: Kernel NULL pointer dereference on read at 0x Faulting instruction address: 0xc00c9934 Oops: Kernel access of bad area, sig: 11 [#1] BE PAGE_SIZE=4K SMP NR_CPUS=4 QEMU e500 Modules linked in: CPU: 1 PID: 1 Comm: swapper/0 Not tainted 6.0.0-rc7_A-EON_X5000 #1 NIP: c00c9934 LR: c00c9f58 CTR: REGS: c208ab20 TRAP: 0300 Not tainted (6.0.0-rc7_A-EON_X5000) MSR: 90029002 CR: 84008242 XER: DEAR: ESR: IRQMASK: 0 GPR00: c06f0060 c208adc0 c1ac3500 c25f0010 GPR04: c19908b0 GPR08: 0105 0180 GPR12: 24008242 c0003fff9500 c0001384 GPR16: GPR20: c169021f c208b088 GPR24: c2336800 GPR28: c2a48000 c2336800 c25f0010 NIP [c00c9934] .dma_map_direct+0x8/0x10 LR [c00c9f58] .dma_max_mapping_size+0x24/0x78 Call Trace: [c208adc0] [c208ae80] 0xc208ae80 (unreliable) [c208ae40] [c06f0060] .drm_prime_pages_to_sg+0xa0/0xb8 [c208aed0] [c070f96c] .drm_gem_shmem_get_sg_table+0x28/0x3c [c208af40] [c0808c8c] .virtio_gpu_object_create+0x134/0x3a8 [c208b010] [c0804c34] .virtio_gpu_mode_dumb_create+0xe4/0x15c [c208b110] [c06ff7f4] .drm_mode_create_dumb+0xcc/0xec [c208b180] [c0707748] .drm_client_framebuffer_create+0x98/0x1f0 [c208b260] [c071fb6c] .drm_fb_helper_generic_probe+0x78/0x1a0 [c208b320] [c071ef08] .__drm_fb_helper_initial_config_and_unlock+0x428/0x54c [c208b410] [c071f9dc] .drm_fbdev_client_hotplug+0xec/0x128 [c208b4a0] [c071fdec] .drm_fbdev_generic_setup+0x158/0x198 [c208b530] [c0803dc4] .virtio_gpu_probe+0x1ac/0x1e0 [c208b5f0] [c069e11c] .virtio_dev_probe+0x2d0/0x3d4 [c208b690] [c0815f34] .really_probe+0x1a0/0x344 [c208b720] [c08161c8] .__driver_probe_device+0xf0/0x100 [c208b7b0] [c081620c] .driver_probe_device+0x34/0xac [c208b840] [c0816774] .__driver_attach+0x124/0x134 [c208b8d0] [c0813974] .bus_for_each_dev+0x8c/0xd0 [c208b980] [c08154a4] .driver_attach+0x24/0x38 [c208b9f0] [c0814dd4] .bus_add_driver+0xd8/0x210 [c208baa0] [c0816fd4] .driver_register+0xe0/0x134 [c208bb20] [c069d8a8] .register_virtio_driver+0x40/0x54 hrtimer: interrupt took 4631040 ns [c208bb90] [c195] .virtio_gpu_driver_init+0x18/0x2c [c208bc00] [c0001044] .do_one_initcall+0x7c/0x1c0 [c208bce0] [c1925710] .kernel_init_freeable+0x23c/0x240 [c208bd90] [c00013ac] .kernel_init+0x28/0x14c [c208be10] [c5a0] .ret_from_kernel_thread+0x58/0x60 Instruction dump: 3921 7c23f840 38210080 7d20485e 792307e0 48d551d8 7c9f2378 4bdc 792307e0 4e800020 e92301f8 7c852378 4b7c 7c0802a6 28060003 ---[ end trace ]--- Kernel panic - not syncing: Attempted to kill init! exitcode=0x000b Rebooting in 180 seconds..
Re: [Intel-gfx] [PATCH v2] overflow: Introduce overflows_type() and castable_to_type()
Hi Kees, I love your patch! Yet something to improve: [auto build test ERROR on kees/for-next/hardening] [also build test ERROR on next-20220928] [cannot apply to drm-tip/drm-tip drm-intel/for-linux-next drm-misc/drm-misc-next linus/master v6.0-rc7] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Kees-Cook/overflow-Introduce-overflows_type-and-castable_to_type/20220927-094847 base: https://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening config: i386-defconfig compiler: gcc-11 (Debian 11.3.0-5) 11.3.0 reproduce (this is a W=1 build): # https://github.com/intel-lab-lkp/linux/commit/ffc9129a19eb65b2d20780558b0c1af24d66434a git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Kees-Cook/overflow-Introduce-overflows_type-and-castable_to_type/20220927-094847 git checkout ffc9129a19eb65b2d20780558b0c1af24d66434a # save the config file mkdir build_dir && cp config build_dir/.config make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot All errors (new ones prefixed by >>): In file included from drivers/gpu/drm/i915/i915_utils.h:29, from drivers/gpu/drm/i915/i915_user_extensions.c:14: drivers/gpu/drm/i915/i915_user_extensions.c: In function 'i915_user_extensions': >> include/linux/overflow.h:33:40: error: invalid operands to binary << (have >> 'struct i915_user_extension *' and 'unsigned int') 33 | #define __type_half_max(type) ((type)1 << (8*sizeof(type) - 1 - is_signed_type(type))) |^~ ~~~ | | | unsigned int include/linux/overflow.h:34:27: note: in expansion of macro '__type_half_max' 34 | #define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T))) | ^~~ include/linux/overflow.h:132:23: note: in expansion of macro 'type_max' 132 | (x) > type_max(typeof(T)) ? 1 : 0 \ | ^~~~ include/linux/overflow.h:159:31: note: in expansion of macro '__overflows_type_constexpr' 159 | __overflows_type_constexpr(n, T), \ | ^~ drivers/gpu/drm/i915/i915_user_extensions.c:54:21: note: in expansion of macro 'overflows_type' 54 | overflows_type(next, ext)) | ^~ >> include/linux/overflow.h:33:40: error: invalid operands to binary << (have >> 'struct i915_user_extension *' and 'unsigned int') 33 | #define __type_half_max(type) ((type)1 << (8*sizeof(type) - 1 - is_signed_type(type))) |^~ ~~~ | | | unsigned int include/linux/overflow.h:34:53: note: in expansion of macro '__type_half_max' 34 | #define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T))) | ^~~ include/linux/overflow.h:132:23: note: in expansion of macro 'type_max' 132 | (x) > type_max(typeof(T)) ? 1 : 0 \ | ^~~~ include/linux/overflow.h:159:31: note: in expansion of macro '__overflows_type_constexpr' 159 | __overflows_type_constexpr(n, T), \ | ^~ drivers/gpu/drm/i915/i915_user_extensions.c:54:21: note: in expansion of macro 'overflows_type' 54 | overflows_type(next, ext)) | ^~ >> include/linux/overflow.h:33:40: error: invalid operands to binary << (have >> 'struct i915_user_extension *' and 'unsigned int') 33 | #define __type_half_max(type) ((type)1 << (8*sizeof(type) - 1 - is_signed_type(type))) |^~ ~~~ |
[PATCH v4 3/4] drm/i915: Make the heartbeat play nice with long pre-emption timeouts
From: John Harrison Compute workloads are inherently not pre-emptible for long periods on current hardware. As a workaround for this, the pre-emption timeout for compute capable engines was disabled. This is undesirable with GuC submission as it prevents per engine reset of hung contexts. Hence the next patch will re-enable the timeout but bumped up by an order of magnitude. However, the heartbeat might not respect that. Depending upon current activity, a pre-emption to the heartbeat pulse might not even be attempted until the last heartbeat period. Which means that only one period is granted for the pre-emption to occur. With the aforesaid bump, the pre-emption timeout could be significantly larger than this heartbeat period. So adjust the heartbeat code to take the pre-emption timeout into account. When it reaches the final (high priority) period, it now ensures the delay before hitting reset is bigger than the pre-emption timeout. v2: Fix for selftests which adjust the heartbeat period manually. Signed-off-by: John Harrison --- .../gpu/drm/i915/gt/intel_engine_heartbeat.c | 19 +++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c index a3698f611f457..823a790a0e2ae 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c @@ -22,9 +22,28 @@ static bool next_heartbeat(struct intel_engine_cs *engine) { + struct i915_request *rq; long delay; delay = READ_ONCE(engine->props.heartbeat_interval_ms); + + rq = engine->heartbeat.systole; + + if (rq && rq->sched.attr.priority >= I915_PRIORITY_BARRIER && + delay == engine->defaults.heartbeat_interval_ms) { + long longer; + + /* +* The final try is at the highest priority possible. Up until now +* a pre-emption might not even have been attempted. So make sure +* this last attempt allows enough time for a pre-emption to occur. +*/ + longer = READ_ONCE(engine->props.preempt_timeout_ms) * 2; + longer = intel_clamp_heartbeat_interval_ms(engine, longer); + if (longer > delay) + delay = longer; + } + if (!delay) return false; -- 2.37.3
[PATCH v4 4/4] drm/i915: Improve long running compute w/a for GuC submission
From: John Harrison A workaround was added to the driver to allow compute workloads to run 'forever' by disabling pre-emption on the RCS engine for Gen12. It is not totally unbound as the heartbeat will kick in eventually and cause a reset of the hung engine. However, this does not work well in GuC submission mode. In GuC mode, the pre-emption timeout is how GuC detects hung contexts and triggers a per engine reset. Thus, disabling the timeout means also losing all per engine reset ability. A full GT reset will still occur when the heartbeat finally expires, but that is a much more destructive and undesirable mechanism. The purpose of the workaround is actually to give compute tasks longer to reach a pre-emption point after a pre-emption request has been issued. This is necessary because Gen12 does not support mid-thread pre-emption and compute tasks can have long running threads. So, rather than disabling the timeout completely, just set it to a 'long' value. v2: Review feedback from Tvrtko - must hard code the 'long' value instead of determining it algorithmically. So make it an extra CONFIG definition. Also, remove the execlist centric comment from the existing pre-emption timeout CONFIG option given that it applies to more than just execlists. Signed-off-by: John Harrison Reviewed-by: Daniele Ceraolo Spurio (v1) Acked-by: Michal Mrozek --- drivers/gpu/drm/i915/Kconfig.profile | 26 +++ drivers/gpu/drm/i915/gt/intel_engine_cs.c | 9 ++-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/Kconfig.profile b/drivers/gpu/drm/i915/Kconfig.profile index 39328567c2007..7cc38d25ee5c8 100644 --- a/drivers/gpu/drm/i915/Kconfig.profile +++ b/drivers/gpu/drm/i915/Kconfig.profile @@ -57,10 +57,28 @@ config DRM_I915_PREEMPT_TIMEOUT default 640 # milliseconds help How long to wait (in milliseconds) for a preemption event to occur - when submitting a new context via execlists. If the current context - does not hit an arbitration point and yield to HW before the timer - expires, the HW will be reset to allow the more important context - to execute. + when submitting a new context. If the current context does not hit + an arbitration point and yield to HW before the timer expires, the + HW will be reset to allow the more important context to execute. + + This is adjustable via + /sys/class/drm/card?/engine/*/preempt_timeout_ms + + May be 0 to disable the timeout. + + The compiled in default may get overridden at driver probe time on + certain platforms and certain engines which will be reflected in the + sysfs control. + +config DRM_I915_PREEMPT_TIMEOUT_COMPUTE + int "Preempt timeout for compute engines (ms, jiffy granularity)" + default 7500 # milliseconds + help + How long to wait (in milliseconds) for a preemption event to occur + when submitting a new context to a compute capable engine. If the + current context does not hit an arbitration point and yield to HW + before the timer expires, the HW will be reset to allow the more + important context to execute. This is adjustable via /sys/class/drm/card?/engine/*/preempt_timeout_ms diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c index fcbccd8d244e9..c1257723d1949 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c @@ -508,9 +508,14 @@ static int intel_engine_setup(struct intel_gt *gt, enum intel_engine_id id, engine->props.timeslice_duration_ms = CONFIG_DRM_I915_TIMESLICE_DURATION; - /* Override to uninterruptible for OpenCL workloads. */ + /* +* Mid-thread pre-emption is not available in Gen12. Unfortunately, +* some compute workloads run quite long threads. That means they get +* reset due to not pre-empting in a timely manner. So, bump the +* pre-emption timeout value to be much higher for compute engines. +*/ if (GRAPHICS_VER(i915) == 12 && (engine->flags & I915_ENGINE_HAS_RCS_REG_STATE)) - engine->props.preempt_timeout_ms = 0; + engine->props.preempt_timeout_ms = CONFIG_DRM_I915_PREEMPT_TIMEOUT_COMPUTE; /* Cap properties according to any system limits */ #define CLAMP_PROP(field) \ -- 2.37.3
[PATCH v4 1/4] drm/i915/guc: Limit scheduling properties to avoid overflow
From: John Harrison GuC converts the pre-emption timeout and timeslice quantum values into clock ticks internally. That significantly reduces the point of 32bit overflow. On current platforms, worst case scenario is approximately 110 seconds. Rather than allowing the user to set higher values and then get confused by early timeouts, add limits when setting these values. v2: Add helper functions for clamping (review feedback from Tvrtko). v3: Add a bunch of BUG_ON range checks in addition to the checks already in the clamping functions (Tvrtko) Signed-off-by: John Harrison Reviewed-by: Daniele Ceraolo Spurio (v1) --- drivers/gpu/drm/i915/gt/intel_engine.h| 6 ++ drivers/gpu/drm/i915/gt/intel_engine_cs.c | 69 +++ drivers/gpu/drm/i915/gt/sysfs_engines.c | 25 --- drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h | 21 ++ .../gpu/drm/i915/gt/uc/intel_guc_submission.c | 8 +++ 5 files changed, 119 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/intel_engine.h b/drivers/gpu/drm/i915/gt/intel_engine.h index 04e435bce79bd..cbc8b857d5f7a 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine.h +++ b/drivers/gpu/drm/i915/gt/intel_engine.h @@ -348,4 +348,10 @@ intel_engine_get_hung_context(struct intel_engine_cs *engine) return engine->hung_ce; } +u64 intel_clamp_heartbeat_interval_ms(struct intel_engine_cs *engine, u64 value); +u64 intel_clamp_max_busywait_duration_ns(struct intel_engine_cs *engine, u64 value); +u64 intel_clamp_preempt_timeout_ms(struct intel_engine_cs *engine, u64 value); +u64 intel_clamp_stop_timeout_ms(struct intel_engine_cs *engine, u64 value); +u64 intel_clamp_timeslice_duration_ms(struct intel_engine_cs *engine, u64 value); + #endif /* _INTEL_RINGBUFFER_H_ */ diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c index 2ddcad497fa30..8f16955f0821e 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c @@ -512,6 +512,26 @@ static int intel_engine_setup(struct intel_gt *gt, enum intel_engine_id id, engine->flags |= I915_ENGINE_HAS_EU_PRIORITY; } + /* Cap properties according to any system limits */ +#define CLAMP_PROP(field) \ + do { \ + u64 clamp = intel_clamp_##field(engine, engine->props.field); \ + if (clamp != engine->props.field) { \ + drm_notice(&engine->i915->drm, \ + "Warning, clamping %s to %lld to prevent overflow\n", \ + #field, clamp); \ + engine->props.field = clamp; \ + } \ + } while (0) + + CLAMP_PROP(heartbeat_interval_ms); + CLAMP_PROP(max_busywait_duration_ns); + CLAMP_PROP(preempt_timeout_ms); + CLAMP_PROP(stop_timeout_ms); + CLAMP_PROP(timeslice_duration_ms); + +#undef CLAMP_PROP + engine->defaults = engine->props; /* never to change again */ engine->context_size = intel_engine_context_size(gt, engine->class); @@ -534,6 +554,55 @@ static int intel_engine_setup(struct intel_gt *gt, enum intel_engine_id id, return 0; } +u64 intel_clamp_heartbeat_interval_ms(struct intel_engine_cs *engine, u64 value) +{ + value = min_t(u64, value, jiffies_to_msecs(MAX_SCHEDULE_TIMEOUT)); + + return value; +} + +u64 intel_clamp_max_busywait_duration_ns(struct intel_engine_cs *engine, u64 value) +{ + value = min(value, jiffies_to_nsecs(2)); + + return value; +} + +u64 intel_clamp_preempt_timeout_ms(struct intel_engine_cs *engine, u64 value) +{ + /* +* NB: The GuC API only supports 32bit values. However, the limit is further +* reduced due to internal calculations which would otherwise overflow. +*/ + if (intel_guc_submission_is_wanted(&engine->gt->uc.guc)) + value = min_t(u64, value, guc_policy_max_preempt_timeout_ms()); + + value = min_t(u64, value, jiffies_to_msecs(MAX_SCHEDULE_TIMEOUT)); + + return value; +} + +u64 intel_clamp_stop_timeout_ms(struct intel_engine_cs *engine, u64 value) +{ + value = min_t(u64, value, jiffies_to_msecs(MAX_SCHEDULE_TIMEOUT)); + + return value; +} + +u64 intel_clamp_timeslice_duration_ms(struct intel_engine_cs *engine, u64 value) +{ + /* +* NB: The GuC API only supports 32bit values. However, the limit is further +* reduced due to internal calculations which would otherwise overflow. +*/ + if (intel_guc_submission_is_wanted(&engine->gt->uc.guc)) + value = min_t(u64, value, guc_policy_max_exec_quantum_ms()); + + value = min_t(u64, value, jiffies_to_msecs(MAX_SCHEDULE_TIMEOUT)); + + return value; +} + static void __setup_engine_capabilities(struct intel_engine_cs *engine) { struct drm_i915_private *i915 = engine->i915; diff --git a/drivers/gpu/drm/i915/gt/sysfs_engines.c
[PATCH v4 2/4] drm/i915: Fix compute pre-emption w/a to apply to compute engines
From: John Harrison An earlier patch added support for compute engines. However, it missed enabling the anti-pre-emption w/a for the new engine class. So move the 'compute capable' flag earlier and use it for the pre-emption w/a test. Fixes: c674c5b9342e ("drm/i915/xehp: CCS should use RCS setup functions") Cc: Tvrtko Ursulin Cc: Daniele Ceraolo Spurio Cc: Aravind Iddamsetty Cc: Matt Roper Cc: Tvrtko Ursulin Cc: Daniel Vetter Cc: Maarten Lankhorst Cc: Lucas De Marchi Cc: John Harrison Cc: Jason Ekstrand Cc: "Michał Winiarski" Cc: Matthew Brost Cc: Chris Wilson Cc: Tejas Upadhyay Cc: Umesh Nerlige Ramappa Cc: "Thomas Hellström" Cc: Stuart Summers Cc: Matthew Auld Cc: Jani Nikula Cc: Ramalingam C Cc: Akeem G Abodunrin Signed-off-by: John Harrison Reviewed-by: Matt Roper --- drivers/gpu/drm/i915/gt/intel_engine_cs.c | 24 +++ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c index 8f16955f0821e..fcbccd8d244e9 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c @@ -486,6 +486,17 @@ static int intel_engine_setup(struct intel_gt *gt, enum intel_engine_id id, engine->logical_mask = BIT(logical_instance); __sprint_engine_name(engine); + if ((engine->class == COMPUTE_CLASS && !RCS_MASK(engine->gt) && +__ffs(CCS_MASK(engine->gt)) == engine->instance) || +engine->class == RENDER_CLASS) + engine->flags |= I915_ENGINE_FIRST_RENDER_COMPUTE; + + /* features common between engines sharing EUs */ + if (engine->class == RENDER_CLASS || engine->class == COMPUTE_CLASS) { + engine->flags |= I915_ENGINE_HAS_RCS_REG_STATE; + engine->flags |= I915_ENGINE_HAS_EU_PRIORITY; + } + engine->props.heartbeat_interval_ms = CONFIG_DRM_I915_HEARTBEAT_INTERVAL; engine->props.max_busywait_duration_ns = @@ -498,20 +509,9 @@ static int intel_engine_setup(struct intel_gt *gt, enum intel_engine_id id, CONFIG_DRM_I915_TIMESLICE_DURATION; /* Override to uninterruptible for OpenCL workloads. */ - if (GRAPHICS_VER(i915) == 12 && engine->class == RENDER_CLASS) + if (GRAPHICS_VER(i915) == 12 && (engine->flags & I915_ENGINE_HAS_RCS_REG_STATE)) engine->props.preempt_timeout_ms = 0; - if ((engine->class == COMPUTE_CLASS && !RCS_MASK(engine->gt) && -__ffs(CCS_MASK(engine->gt)) == engine->instance) || -engine->class == RENDER_CLASS) - engine->flags |= I915_ENGINE_FIRST_RENDER_COMPUTE; - - /* features common between engines sharing EUs */ - if (engine->class == RENDER_CLASS || engine->class == COMPUTE_CLASS) { - engine->flags |= I915_ENGINE_HAS_RCS_REG_STATE; - engine->flags |= I915_ENGINE_HAS_EU_PRIORITY; - } - /* Cap properties according to any system limits */ #define CLAMP_PROP(field) \ do { \ -- 2.37.3
[PATCH v4 0/4] Improve anti-pre-emption w/a for compute workloads
From: John Harrison Compute workloads are inherently not pre-emptible on current hardware. Thus the pre-emption timeout was disabled as a workaround to prevent unwanted resets. Instead, the hang detection was left to the heartbeat and its (longer) timeout. This is undesirable with GuC submission as the heartbeat is a full GT reset rather than a per engine reset and so is much more destructive. Instead, just bump the pre-emption timeout to a big value. Also, update the heartbeat to allow such a long pre-emption delay in the final heartbeat period. v2: Add clamping helpers. v3: Remove long timeout algorithm and replace with hard coded value (review feedback from Tvrtko). Also, fix execlist selftest failure and fix bug in compute enabling patch related to pre-emption timeouts. v4: Add multiple BUG_ONs to re-check already range checked values (Tvrtko) Signed-off-by: John Harrison John Harrison (4): drm/i915/guc: Limit scheduling properties to avoid overflow drm/i915: Fix compute pre-emption w/a to apply to compute engines drm/i915: Make the heartbeat play nice with long pre-emption timeouts drm/i915: Improve long running compute w/a for GuC submission drivers/gpu/drm/i915/Kconfig.profile | 26 - drivers/gpu/drm/i915/gt/intel_engine.h| 6 ++ drivers/gpu/drm/i915/gt/intel_engine_cs.c | 102 +++--- .../gpu/drm/i915/gt/intel_engine_heartbeat.c | 19 drivers/gpu/drm/i915/gt/sysfs_engines.c | 25 +++-- drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h | 21 .../gpu/drm/i915/gt/uc/intel_guc_submission.c | 8 ++ 7 files changed, 179 insertions(+), 28 deletions(-) -- 2.37.3
[PATCH v2 2/2] drm/panel: simple: Use dev_err_probe() to simplify code
In the probe path, dev_err() can be replaced with dev_err_probe() which will check if error code is -EPROBE_DEFER and prints the error name. It also sets the defer probe reason which can be checked later through debugfs. Signed-off-by: Yuan Can Reviewed-by: Douglas Anderson --- drivers/gpu/drm/panel/panel-simple.c | 9 +++-- 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 0cb3be26e2e6..1607824dc2b3 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -575,12 +575,9 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc) panel->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(panel->enable_gpio)) { - err = PTR_ERR(panel->enable_gpio); - if (err != -EPROBE_DEFER) - dev_err(dev, "failed to request GPIO: %d\n", err); - return err; - } + if (IS_ERR(panel->enable_gpio)) + return dev_err_probe(dev, PTR_ERR(panel->enable_gpio), +"failed to request GPIO\n"); err = of_drm_get_panel_orientation(dev->of_node, &panel->orientation); if (err) { -- 2.17.1
[PATCH v2 1/2] drm/panel: panel-edp: Use dev_err_probe() to simplify code
In the probe path, dev_err() can be replaced with dev_err_probe() which will check if error code is -EPROBE_DEFER and prints the error name. It also sets the defer probe reason which can be checked later through debugfs. Signed-off-by: Yuan Can Reviewed-by: Douglas Anderson --- drivers/gpu/drm/panel/panel-edp.c | 22 ++ 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 7b90016b8408..e323a6331143 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -403,17 +403,10 @@ static int panel_edp_unprepare(struct drm_panel *panel) static int panel_edp_get_hpd_gpio(struct device *dev, struct panel_edp *p) { - int err; - p->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN); - if (IS_ERR(p->hpd_gpio)) { - err = PTR_ERR(p->hpd_gpio); - - if (err != -EPROBE_DEFER) - dev_err(dev, "failed to get 'hpd' GPIO: %d\n", err); - - return err; - } + if (IS_ERR(p->hpd_gpio)) + return dev_err_probe(dev, PTR_ERR(p->hpd_gpio), +"failed to get 'hpd' GPIO\n"); return 0; } @@ -832,12 +825,9 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc, panel->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(panel->enable_gpio)) { - err = PTR_ERR(panel->enable_gpio); - if (err != -EPROBE_DEFER) - dev_err(dev, "failed to request GPIO: %d\n", err); - return err; - } + if (IS_ERR(panel->enable_gpio)) + return dev_err_probe(dev, PTR_ERR(panel->enable_gpio), +"failed to request GPIO\n"); err = of_drm_get_panel_orientation(dev->of_node, &panel->orientation); if (err) { -- 2.17.1
[PATCH v2 0/2] drm/panel: Use dev_err_probe() to simplify code
This series contains two patchs simplify the error handling in probe function by switching from dev_err() to dev_err_probe(). --- changes in v2: - simplify the same case in panel_edp_probe - add Reviewed-by from Douglas Anderson Yuan Can (2): drm/panel: panel-edp: Use dev_err_probe() to simplify code drm/panel: simple: Use dev_err_probe() to simplify code drivers/gpu/drm/panel/panel-edp.c| 22 ++ drivers/gpu/drm/panel/panel-simple.c | 9 +++-- 2 files changed, 9 insertions(+), 22 deletions(-) -- 2.17.1
Re: [PATCH 1/7] mm/memory.c: Fix race when faulting a device private page
Michael Ellerman writes: > Alistair Popple writes: >> When the CPU tries to access a device private page the migrate_to_ram() >> callback associated with the pgmap for the page is called. However no >> reference is taken on the faulting page. Therefore a concurrent >> migration of the device private page can free the page and possibly the >> underlying pgmap. This results in a race which can crash the kernel due >> to the migrate_to_ram() function pointer becoming invalid. It also means >> drivers can't reliably read the zone_device_data field because the page >> may have been freed with memunmap_pages(). >> >> Close the race by getting a reference on the page while holding the ptl >> to ensure it has not been freed. Unfortunately the elevated reference >> count will cause the migration required to handle the fault to fail. To >> avoid this failure pass the faulting page into the migrate_vma functions >> so that if an elevated reference count is found it can be checked to see >> if it's expected or not. >> >> Signed-off-by: Alistair Popple >> --- >> arch/powerpc/kvm/book3s_hv_uvmem.c | 15 ++- >> drivers/gpu/drm/amd/amdkfd/kfd_migrate.c | 17 +++-- >> drivers/gpu/drm/amd/amdkfd/kfd_migrate.h | 2 +- >> drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 11 +--- >> include/linux/migrate.h | 8 ++- >> lib/test_hmm.c | 7 ++--- >> mm/memory.c | 16 +++- >> mm/migrate.c | 34 ++--- >> mm/migrate_device.c | 18 + >> 9 files changed, 87 insertions(+), 41 deletions(-) >> >> diff --git a/arch/powerpc/kvm/book3s_hv_uvmem.c >> b/arch/powerpc/kvm/book3s_hv_uvmem.c >> index 5980063..d4eacf4 100644 >> --- a/arch/powerpc/kvm/book3s_hv_uvmem.c >> +++ b/arch/powerpc/kvm/book3s_hv_uvmem.c >> @@ -508,10 +508,10 @@ unsigned long kvmppc_h_svm_init_start(struct kvm *kvm) >> static int __kvmppc_svm_page_out(struct vm_area_struct *vma, >> unsigned long start, >> unsigned long end, unsigned long page_shift, >> -struct kvm *kvm, unsigned long gpa) >> +struct kvm *kvm, unsigned long gpa, struct page *fault_page) >> { >> unsigned long src_pfn, dst_pfn = 0; >> -struct migrate_vma mig; >> +struct migrate_vma mig = { 0 }; >> struct page *dpage, *spage; >> struct kvmppc_uvmem_page_pvt *pvt; >> unsigned long pfn; >> @@ -525,6 +525,7 @@ static int __kvmppc_svm_page_out(struct vm_area_struct >> *vma, >> mig.dst = &dst_pfn; >> mig.pgmap_owner = &kvmppc_uvmem_pgmap; >> mig.flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE; >> +mig.fault_page = fault_page; >> >> /* The requested page is already paged-out, nothing to do */ >> if (!kvmppc_gfn_is_uvmem_pfn(gpa >> page_shift, kvm, NULL)) >> @@ -580,12 +581,14 @@ static int __kvmppc_svm_page_out(struct vm_area_struct >> *vma, >> static inline int kvmppc_svm_page_out(struct vm_area_struct *vma, >>unsigned long start, unsigned long end, >>unsigned long page_shift, >> - struct kvm *kvm, unsigned long gpa) >> + struct kvm *kvm, unsigned long gpa, >> + struct page *fault_page) >> { >> int ret; >> >> mutex_lock(&kvm->arch.uvmem_lock); >> -ret = __kvmppc_svm_page_out(vma, start, end, page_shift, kvm, gpa); >> +ret = __kvmppc_svm_page_out(vma, start, end, page_shift, kvm, gpa, >> +fault_page); >> mutex_unlock(&kvm->arch.uvmem_lock); >> >> return ret; >> @@ -736,7 +739,7 @@ static int kvmppc_svm_page_in(struct vm_area_struct *vma, >> bool pagein) >> { >> unsigned long src_pfn, dst_pfn = 0; >> -struct migrate_vma mig; >> +struct migrate_vma mig = { 0 }; >> struct page *spage; >> unsigned long pfn; >> struct page *dpage; >> @@ -994,7 +997,7 @@ static vm_fault_t kvmppc_uvmem_migrate_to_ram(struct >> vm_fault *vmf) >> >> if (kvmppc_svm_page_out(vmf->vma, vmf->address, >> vmf->address + PAGE_SIZE, PAGE_SHIFT, >> -pvt->kvm, pvt->gpa)) >> +pvt->kvm, pvt->gpa, vmf->page)) >> return VM_FAULT_SIGBUS; >> else >> return 0; > > I don't have a UV test system, but as-is it doesn't even compile :) Ugh, thanks. I did get as far as installing a PPC cross-compiler and building a kernel. Apparently I did not get as far as enabling CONFIG_PPC_UV :) > kvmppc_svm_page_out() is called via some paths other than the > migrate_to_ram callback. > > I think it's correct to just pass fault_page = NULL when it's not called > from the migrate_to_ram callback? > > Incremental diff below. > > cheers > > > diff --git a/arch/powerpc/kvm/book3s_hv_uvmem.c > b/arc
[PATCH v2 0/2] *** IT6505 driver read dt properties ***
From: allen chen This series let driver can read properties from dt to restrict dp output bandwidth. allen chen (2): dt-bindings: it6505: add properties to restrict output bandwidth drm/bridge: add it6505 driver to read data-lanes and max-pixel-clock-khz from dt .../bindings/display/bridge/ite,it6505.yaml | 10 ++ drivers/gpu/drm/bridge/ite-it6505.c | 35 +-- 2 files changed, 42 insertions(+), 3 deletions(-) -- 2.25.1
[PATCH v2 1/2] dt-bindings: it6505: add properties to restrict output bandwidth
From: allen chen Add properties to restrict dp output data-lanes and clock. Signed-off-by: Pin-Yen Lin Signed-off-by: Allen Chen --- .../devicetree/bindings/display/bridge/ite,it6505.yaml | 10 ++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml b/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml index 833d11b2303a..62b9f2192202 100644 --- a/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml +++ b/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml @@ -52,6 +52,14 @@ properties: maxItems: 1 description: extcon specifier for the Power Delivery + data-lanes: +maxItems: 1 +description: restrict the dp output data-lanes with value of 1-4 + + max-pixel-clock-khz: +maxItems: 1 +description: restrict max pixel clock + port: $ref: /schemas/graph.yaml#/properties/port description: A port node pointing to DPI host port node @@ -84,6 +92,8 @@ examples: pwr18-supply = <&it6505_pp18_reg>; reset-gpios = <&pio 179 1>; extcon = <&usbc_extcon>; +data-lanes = <2>; +max-pixel-clock-khz = <15>; port { it6505_in: endpoint { -- 2.25.1
[PATCH v2 2/2] drm/bridge: add it6505 driver to read data-lanes and max-pixel-clock-khz from dt
From: allen chen Add driver to read data-lanes and max-pixel-clock-khz from dt property to restrict output bandwidth. Signed-off-by: Allen chen Signed-off-by: Pin-yen Lin --- drivers/gpu/drm/bridge/ite-it6505.c | 35 ++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 2767b70fa2cb..cfa25a176a29 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -436,6 +436,8 @@ struct it6505 { bool powered; bool hpd_state; u32 afe_setting; + u32 max_dpi_pixel_clock; + u32 max_lane_count; enum hdcp_state hdcp_status; struct delayed_work hdcp_work; struct work_struct hdcp_wait_ksv_list; @@ -1475,7 +1477,8 @@ static void it6505_parse_link_capabilities(struct it6505 *it6505) it6505->lane_count = link->num_lanes; DRM_DEV_DEBUG_DRIVER(dev, "Sink support %d lanes training", it6505->lane_count); - it6505->lane_count = min_t(int, it6505->lane_count, MAX_LANE_COUNT); + it6505->lane_count = min_t(int, it6505->lane_count, + it6505->max_lane_count); it6505->branch_device = drm_dp_is_branch(it6505->dpcd); DRM_DEV_DEBUG_DRIVER(dev, "Sink %sbranch device", @@ -2901,7 +2904,7 @@ it6505_bridge_mode_valid(struct drm_bridge *bridge, if (mode->flags & DRM_MODE_FLAG_INTERLACE) return MODE_NO_INTERLACE; - if (mode->clock > DPI_PIXEL_CLK_MAX) + if (mode->clock > it6505->max_dpi_pixel_clock) return MODE_CLOCK_HIGH; it6505->video_info.clock = mode->clock; @@ -3066,6 +3069,8 @@ static void it6505_parse_dt(struct it6505 *it6505) { struct device *dev = &it6505->client->dev; u32 *afe_setting = &it6505->afe_setting; + u32 *max_lane_count = &it6505->max_lane_count; + u32 *max_dpi_pixel_clock = &it6505->max_dpi_pixel_clock; it6505->lane_swap_disabled = device_property_read_bool(dev, "no-laneswap"); @@ -3081,7 +3086,31 @@ static void it6505_parse_dt(struct it6505 *it6505) } else { *afe_setting = 0; } - DRM_DEV_DEBUG_DRIVER(dev, "using afe_setting: %d", *afe_setting); + + if (device_property_read_u32(dev, "data-lanes", +max_lane_count) == 0) { + if (*max_lane_count > 4 || *max_lane_count == 3) { + dev_err(dev, "max lane count error, use default"); + *max_lane_count = MAX_LANE_COUNT; + } + } else { + *max_lane_count = MAX_LANE_COUNT; + } + + if (device_property_read_u32(dev, "max-pixel-clock-khz", +max_dpi_pixel_clock) == 0) { + if (*max_dpi_pixel_clock > 297000) { + dev_err(dev, "max pixel clock error, use default"); + *max_dpi_pixel_clock = DPI_PIXEL_CLK_MAX; + } + } else { + *max_dpi_pixel_clock = DPI_PIXEL_CLK_MAX; + } + + DRM_DEV_DEBUG_DRIVER(dev, "using afe_setting: %u, max_lane_count: %u", +it6505->afe_setting, it6505->max_lane_count); + DRM_DEV_DEBUG_DRIVER(dev, "using max_dpi_pixel_clock: %u kHz", +it6505->max_dpi_pixel_clock); } static ssize_t receive_timing_debugfs_show(struct file *file, char __user *buf, -- 2.25.1
[PATCH] drm/bridge: ps8640: Add software to support aux defer
This chip can not handle aux defer if the host directly program its aux registers to access edid/dpcd. So we need let software to handle the aux defer situation. Signed-off-by: Jason Yen --- drivers/gpu/drm/bridge/parade-ps8640.c | 8 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index 31e88cb39f8a..967dec840b91 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -303,6 +303,14 @@ static ssize_t ps8640_aux_transfer_msg(struct drm_dp_aux *aux, case SWAUX_STATUS_ACKM: len = data & SWAUX_M_MASK; break; + case SWAUX_STATUS_DEFER: + case SWAUX_STATUS_I2C_DEFER: + if (is_native_aux) + msg->reply |= DP_AUX_NATIVE_REPLY_DEFER; + else + msg->reply |= DP_AUX_I2C_REPLY_DEFER; + len = data & SWAUX_M_MASK; + break; case SWAUX_STATUS_INVALID: return -EOPNOTSUPP; case SWAUX_STATUS_TIMEOUT: -- 2.34.1
Re: [PATCH 10/10] drm/panel: simple: Use dev_err_probe() to simplify code
在 2022/9/29 7:03, Doug Anderson 写道: Hi, On Fri, Sep 23, 2022 at 6:59 PM Yuan Can wrote: In the probe path, dev_err() can be replaced with dev_err_probe() which will check if error code is -EPROBE_DEFER and prints the error name. It also sets the defer probe reason which can be checked later through debugfs. Signed-off-by: Yuan Can --- drivers/gpu/drm/panel/panel-simple.c | 9 +++-- 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 0cb3be26e2e6..1607824dc2b3 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -575,12 +575,9 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc) panel->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(panel->enable_gpio)) { - err = PTR_ERR(panel->enable_gpio); - if (err != -EPROBE_DEFER) - dev_err(dev, "failed to request GPIO: %d\n", err); - return err; - } + if (IS_ERR(panel->enable_gpio)) + return dev_err_probe(dev, PTR_ERR(panel->enable_gpio), +"failed to request GPIO\n"); Reviewed-by: Douglas Anderson I'd be happy to land this patch and the panel-edp one into drm-misc just because I've touched those panel drivers in the past. I'd tend to leave the other panel drivers to others unless you really get stuck. For now I'll sit tight because I think you can make a 2nd fix to the panel-edp one and put them into the same patch. Thanks for the notice, I will fix the case in panel_edp_probe in the v2 patch and send it ASAP. -- Best regards, Yuan Can
Re: [PATCH v2 3/7] firmware: raspberrypi: Provide a helper to query a clock max rate
Quoting Maxime Ripard (2022-09-20 05:50:22) > The firmware allows to query for its clocks the operating range of a > given clock. We'll need this for some drivers (KMS, in particular) to > infer the state of some configuration options, so let's create a > function to do so. > > Signed-off-by: Maxime Ripard > Acked-by: Stephen Boyd
Re: [PATCH 1/7] mm/memory.c: Fix race when faulting a device private page
Alistair Popple writes: > When the CPU tries to access a device private page the migrate_to_ram() > callback associated with the pgmap for the page is called. However no > reference is taken on the faulting page. Therefore a concurrent > migration of the device private page can free the page and possibly the > underlying pgmap. This results in a race which can crash the kernel due > to the migrate_to_ram() function pointer becoming invalid. It also means > drivers can't reliably read the zone_device_data field because the page > may have been freed with memunmap_pages(). > > Close the race by getting a reference on the page while holding the ptl > to ensure it has not been freed. Unfortunately the elevated reference > count will cause the migration required to handle the fault to fail. To > avoid this failure pass the faulting page into the migrate_vma functions > so that if an elevated reference count is found it can be checked to see > if it's expected or not. > > Signed-off-by: Alistair Popple > --- > arch/powerpc/kvm/book3s_hv_uvmem.c | 15 ++- > drivers/gpu/drm/amd/amdkfd/kfd_migrate.c | 17 +++-- > drivers/gpu/drm/amd/amdkfd/kfd_migrate.h | 2 +- > drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 11 +--- > include/linux/migrate.h | 8 ++- > lib/test_hmm.c | 7 ++--- > mm/memory.c | 16 +++- > mm/migrate.c | 34 ++--- > mm/migrate_device.c | 18 + > 9 files changed, 87 insertions(+), 41 deletions(-) > > diff --git a/arch/powerpc/kvm/book3s_hv_uvmem.c > b/arch/powerpc/kvm/book3s_hv_uvmem.c > index 5980063..d4eacf4 100644 > --- a/arch/powerpc/kvm/book3s_hv_uvmem.c > +++ b/arch/powerpc/kvm/book3s_hv_uvmem.c > @@ -508,10 +508,10 @@ unsigned long kvmppc_h_svm_init_start(struct kvm *kvm) > static int __kvmppc_svm_page_out(struct vm_area_struct *vma, > unsigned long start, > unsigned long end, unsigned long page_shift, > - struct kvm *kvm, unsigned long gpa) > + struct kvm *kvm, unsigned long gpa, struct page *fault_page) > { > unsigned long src_pfn, dst_pfn = 0; > - struct migrate_vma mig; > + struct migrate_vma mig = { 0 }; > struct page *dpage, *spage; > struct kvmppc_uvmem_page_pvt *pvt; > unsigned long pfn; > @@ -525,6 +525,7 @@ static int __kvmppc_svm_page_out(struct vm_area_struct > *vma, > mig.dst = &dst_pfn; > mig.pgmap_owner = &kvmppc_uvmem_pgmap; > mig.flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE; > + mig.fault_page = fault_page; > > /* The requested page is already paged-out, nothing to do */ > if (!kvmppc_gfn_is_uvmem_pfn(gpa >> page_shift, kvm, NULL)) > @@ -580,12 +581,14 @@ static int __kvmppc_svm_page_out(struct vm_area_struct > *vma, > static inline int kvmppc_svm_page_out(struct vm_area_struct *vma, > unsigned long start, unsigned long end, > unsigned long page_shift, > - struct kvm *kvm, unsigned long gpa) > + struct kvm *kvm, unsigned long gpa, > + struct page *fault_page) > { > int ret; > > mutex_lock(&kvm->arch.uvmem_lock); > - ret = __kvmppc_svm_page_out(vma, start, end, page_shift, kvm, gpa); > + ret = __kvmppc_svm_page_out(vma, start, end, page_shift, kvm, gpa, > + fault_page); > mutex_unlock(&kvm->arch.uvmem_lock); > > return ret; > @@ -736,7 +739,7 @@ static int kvmppc_svm_page_in(struct vm_area_struct *vma, > bool pagein) > { > unsigned long src_pfn, dst_pfn = 0; > - struct migrate_vma mig; > + struct migrate_vma mig = { 0 }; > struct page *spage; > unsigned long pfn; > struct page *dpage; > @@ -994,7 +997,7 @@ static vm_fault_t kvmppc_uvmem_migrate_to_ram(struct > vm_fault *vmf) > > if (kvmppc_svm_page_out(vmf->vma, vmf->address, > vmf->address + PAGE_SIZE, PAGE_SHIFT, > - pvt->kvm, pvt->gpa)) > + pvt->kvm, pvt->gpa, vmf->page)) > return VM_FAULT_SIGBUS; > else > return 0; I don't have a UV test system, but as-is it doesn't even compile :) kvmppc_svm_page_out() is called via some paths other than the migrate_to_ram callback. I think it's correct to just pass fault_page = NULL when it's not called from the migrate_to_ram callback? Incremental diff below. cheers diff --git a/arch/powerpc/kvm/book3s_hv_uvmem.c b/arch/powerpc/kvm/book3s_hv_uvmem.c index d4eacf410956..965c9e9e500b 100644 --- a/arch/powerpc/kvm/book3s_hv_uvmem.c +++ b/arch/powerpc/kvm/book3s_hv_uvmem.c @@ -637,7 +637,7 @@ void kvmppc_uvmem_drop_pages(const struct kvm_memory_slot *slot,
[PATCH v6] drm/msm/dp: add atomic_check to bridge ops
DRM commit_tails() will disable downstream crtc/encoder/bridge if both disable crtc is required and crtc->active is set before pushing a new frame downstream. There is a rare case that user space display manager issue an extra screen update immediately followed by close DRM device while down stream display interface is disabled. This extra screen update will timeout due to the downstream interface is disabled but will cause crtc->active be set. Hence the followed commit_tails() called by drm_release() will pass the disable downstream crtc/encoder/bridge conditions checking even downstream interface is disabled. This cause the crash to happen at dp_bridge_disable() due to it trying to access the main link register to push the idle pattern out while main link clocks is disabled. This patch adds atomic_check to prevent the extra frame will not be pushed down if display interface is down so that crtc->active will not be set neither. This will fail the conditions checking of disabling down stream crtc/encoder/bridge which prevent drm_release() from calling dp_bridge_disable() so that crash at dp_bridge_disable() prevented. There is no protection in the DRM framework to check if the display pipeline has been already disabled before trying again. The only check is the crtc_state->active but this is controlled by usermode using UAPI. Hence if the usermode sets this and then crashes, the driver needs to protect against double disable. SError Interrupt on CPU7, code 0xbe000411 -- SError CPU: 7 PID: 3878 Comm: Xorg Not tainted 5.19.0-stb-cbq #19 Hardware name: Google Lazor (rev3 - 8) (DT) pstate: a04000c9 (NzCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : __cmpxchg_case_acq_32+0x14/0x2c lr : do_raw_spin_lock+0xa4/0xdc sp : ffc01092b6a0 x29: ffc01092b6a0 x28: 0028 x27: 0038 x26: 0004 x25: ffd2973dce48 x24: x23: x22: x21: ffd2978d0008 x20: ffd2978d0008 x19: ff80ff759fc0 x18: x17: 004800a501260460 x16: 0441043b04600438 x15: 0438089807d0 x14: 07b0089807800780 x13: x12: x11: 0438 x10: 07d0 x9 : ffd2973e09e4 x8 : ff8092d53300 x7 : ff808902e8b8 x6 : 0001 x5 : ff808902e880 x4 : x3 : ff80ff759fc0 x2 : 0001 x1 : x0 : ff80ff759fc0 Kernel panic - not syncing: Asynchronous SError Interrupt CPU: 7 PID: 3878 Comm: Xorg Not tainted 5.19.0-stb-cbq #19 Hardware name: Google Lazor (rev3 - 8) (DT) Call trace: dump_backtrace.part.0+0xbc/0xe4 show_stack+0x24/0x70 dump_stack_lvl+0x68/0x84 dump_stack+0x18/0x34 panic+0x14c/0x32c nmi_panic+0x58/0x7c arm64_serror_panic+0x78/0x84 do_serror+0x40/0x64 el1h_64_error_handler+0x30/0x48 el1h_64_error+0x68/0x6c __cmpxchg_case_acq_32+0x14/0x2c _raw_spin_lock_irqsave+0x38/0x4c lock_timer_base+0x40/0x78 __mod_timer+0xf4/0x25c schedule_timeout+0xd4/0xfc __wait_for_common+0xac/0x140 wait_for_completion_timeout+0x2c/0x54 dp_ctrl_push_idle+0x40/0x88 dp_bridge_disable+0x24/0x30 drm_atomic_bridge_chain_disable+0x90/0xbc drm_atomic_helper_commit_modeset_disables+0x198/0x444 msm_atomic_commit_tail+0x1d0/0x374 commit_tail+0x80/0x108 drm_atomic_helper_commit+0x118/0x11c drm_atomic_commit+0xb4/0xe0 drm_client_modeset_commit_atomic+0x184/0x224 drm_client_modeset_commit_locked+0x58/0x160 drm_client_modeset_commit+0x3c/0x64 __drm_fb_helper_restore_fbdev_mode_unlocked+0x98/0xac drm_fb_helper_set_par+0x74/0x80 drm_fb_helper_hotplug_event+0xdc/0xe0 __drm_fb_helper_restore_fbdev_mode_unlocked+0x7c/0xac drm_fb_helper_restore_fbdev_mode_unlocked+0x20/0x2c drm_fb_helper_lastclose+0x20/0x2c drm_lastclose+0x44/0x6c drm_release+0x88/0xd4 __fput+0x104/0x220 fput+0x1c/0x28 task_work_run+0x8c/0x100 do_exit+0x450/0x8d0 do_group_exit+0x40/0xac __wake_up_parent+0x0/0x38 invoke_syscall+0x84/0x11c el0_svc_common.constprop.0+0xb8/0xe4 do_el0_svc+0x8c/0xb8 el0_svc+0x2c/0x54 el0t_64_sync_handler+0x120/0x1c0 el0t_64_sync+0x190/0x194 SMP: stopping secondary CPUs Kernel Offset: 0x128e80 from 0xffc00800 PHYS_OFFSET: 0x8000 CPU features: 0x800,00c2a015,19801c82 Memory Limit: none Changes in v2: -- add more commit text Changes in v3: -- add comments into dp_bridge_atomic_check() Changes in v4: -- rewording the comment into dp_bridge_atomic_check() Changes in v5: -- removed quote x at end of commit text Changes in v6: -- removed quote x at end of comment in dp_bridge_atomic_check() Fixes: 8a3b4c17f863 ("drm/msm/dp: employ bridge mechanism for display enable and disable") Reported-by: Leonard Lausen Suggested-by: Rob Clark Closes: https://gitlab.freedesktop.org/drm/msm/-/issues/17 Signed-off-by: Kuogee Hsieh Reviewed-by: Abhinav Kumar --- drivers/gpu/drm/msm/dp/dp_drm.c | 34 ++ 1 file changed, 34 insertions(+) diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c
Re: [PATCH 10/10] drm/panel: simple: Use dev_err_probe() to simplify code
Hi, On Fri, Sep 23, 2022 at 6:59 PM Yuan Can wrote: > > In the probe path, dev_err() can be replaced with dev_err_probe() > which will check if error code is -EPROBE_DEFER and prints the > error name. It also sets the defer probe reason which can be > checked later through debugfs. > > Signed-off-by: Yuan Can > --- > drivers/gpu/drm/panel/panel-simple.c | 9 +++-- > 1 file changed, 3 insertions(+), 6 deletions(-) > > diff --git a/drivers/gpu/drm/panel/panel-simple.c > b/drivers/gpu/drm/panel/panel-simple.c > index 0cb3be26e2e6..1607824dc2b3 100644 > --- a/drivers/gpu/drm/panel/panel-simple.c > +++ b/drivers/gpu/drm/panel/panel-simple.c > @@ -575,12 +575,9 @@ static int panel_simple_probe(struct device *dev, const > struct panel_desc *desc) > > panel->enable_gpio = devm_gpiod_get_optional(dev, "enable", > GPIOD_OUT_LOW); > - if (IS_ERR(panel->enable_gpio)) { > - err = PTR_ERR(panel->enable_gpio); > - if (err != -EPROBE_DEFER) > - dev_err(dev, "failed to request GPIO: %d\n", err); > - return err; > - } > + if (IS_ERR(panel->enable_gpio)) > + return dev_err_probe(dev, PTR_ERR(panel->enable_gpio), > +"failed to request GPIO\n"); Reviewed-by: Douglas Anderson I'd be happy to land this patch and the panel-edp one into drm-misc just because I've touched those panel drivers in the past. I'd tend to leave the other panel drivers to others unless you really get stuck. For now I'll sit tight because I think you can make a 2nd fix to the panel-edp one and put them into the same patch. -Doug
Re: [PATCH 02/10] drm/panel: panel-edp: Use dev_err_probe() to simplify code
Hi, On Fri, Sep 23, 2022 at 6:58 PM Yuan Can wrote: > > In the probe path, dev_err() can be replaced with dev_err_probe() > which will check if error code is -EPROBE_DEFER and prints the > error name. It also sets the defer probe reason which can be > checked later through debugfs. > > Signed-off-by: Yuan Can > --- > drivers/gpu/drm/panel/panel-edp.c | 13 +++-- > 1 file changed, 3 insertions(+), 10 deletions(-) > > diff --git a/drivers/gpu/drm/panel/panel-edp.c > b/drivers/gpu/drm/panel/panel-edp.c > index c57e8f9e2d47..84557ec19a16 100644 > --- a/drivers/gpu/drm/panel/panel-edp.c > +++ b/drivers/gpu/drm/panel/panel-edp.c > @@ -403,17 +403,10 @@ static int panel_edp_unprepare(struct drm_panel *panel) > > static int panel_edp_get_hpd_gpio(struct device *dev, struct panel_edp *p) > { > - int err; > - > p->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN); > - if (IS_ERR(p->hpd_gpio)) { > - err = PTR_ERR(p->hpd_gpio); > - > - if (err != -EPROBE_DEFER) > - dev_err(dev, "failed to get 'hpd' GPIO: %d\n", err); > - > - return err; > - } > + if (IS_ERR(p->hpd_gpio)) > + return dev_err_probe(dev, PTR_ERR(p->hpd_gpio), > +"failed to get 'hpd' GPIO\n"); This is a fine improvement but I'm a bit curious why you only fixed one of the two cases? You could do the same thing for the "enable" GPIO in panel_edp_probe(). -Doug
[PATCH v2 19/37] drm: xlnx: zynqmp_dpsub: Move pclk from zynqmp_disp to zynqmp_dpsub
The video clock is an external resource from the DPSUB point of view, not a resource internal to the display controller. Move it to the zynqmp_dpsub structure, to allow accessing it from outside the disp code. While at it, rename the fields from pclk and pclk_from_ps to vid_clk and vid_clk_from_ps, to better reflect their purpose and match the documentation. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 36 ++--- drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 17 ++ drivers/gpu/drm/xlnx/zynqmp_dpsub.h | 4 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 0a2fa96bc126..62f9daf93465 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -170,8 +170,6 @@ struct zynqmp_disp_layer { * @audio.clk: Audio clock * @audio.clk_from_ps: True of the audio clock comes from PS, false from PL * @layers: Layers (planes) - * @pclk: Pixel clock - * @pclk_from_ps: True of the video clock comes from PS, false from PL */ struct zynqmp_disp { struct device *dev; @@ -193,9 +191,6 @@ struct zynqmp_disp { } audio; struct zynqmp_disp_layer layers[ZYNQMP_DISP_NUM_LAYERS]; - - struct clk *pclk; - bool pclk_from_ps; }; /* - @@ -1413,7 +1408,7 @@ static void zynqmp_disp_enable(struct zynqmp_disp *disp) zynqmp_disp_avbuf_enable(disp); /* Choose clock source based on the DT clock handle. */ - zynqmp_disp_avbuf_set_clocks_sources(disp, disp->pclk_from_ps, + zynqmp_disp_avbuf_set_clocks_sources(disp, disp->dpsub->vid_clk_from_ps, disp->audio.clk_from_ps, true); zynqmp_disp_avbuf_enable_channels(disp); zynqmp_disp_avbuf_enable_audio(disp); @@ -1441,13 +1436,13 @@ static int zynqmp_disp_setup_clock(struct zynqmp_disp *disp, long diff; int ret; - ret = clk_set_rate(disp->pclk, mode_clock); + ret = clk_set_rate(disp->dpsub->vid_clk, mode_clock); if (ret) { - dev_err(disp->dev, "failed to set a pixel clock\n"); + dev_err(disp->dev, "failed to set the video clock\n"); return ret; } - rate = clk_get_rate(disp->pclk); + rate = clk_get_rate(disp->dpsub->vid_clk); diff = rate - mode_clock; if (abs(diff) > mode_clock / 20) dev_info(disp->dev, @@ -1478,9 +1473,9 @@ zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc, zynqmp_disp_setup_clock(disp, adjusted_mode->clock * 1000); - ret = clk_prepare_enable(disp->pclk); + ret = clk_prepare_enable(disp->dpsub->vid_clk); if (ret) { - dev_err(disp->dev, "failed to enable a pixel clock\n"); + dev_err(disp->dev, "failed to enable the video clock\n"); pm_runtime_put_sync(disp->dev); return; } @@ -1520,7 +1515,7 @@ zynqmp_disp_crtc_atomic_disable(struct drm_crtc *crtc, } spin_unlock_irq(&crtc->dev->event_lock); - clk_disable_unprepare(disp->pclk); + clk_disable_unprepare(disp->dpsub->vid_clk); pm_runtime_put_sync(disp->dev); } @@ -1675,23 +1670,6 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) if (IS_ERR(disp->audio.base)) return PTR_ERR(disp->audio.base); - /* Try the live PL video clock */ - disp->pclk = devm_clk_get(disp->dev, "dp_live_video_in_clk"); - if (!IS_ERR(disp->pclk)) - disp->pclk_from_ps = false; - else if (PTR_ERR(disp->pclk) == -EPROBE_DEFER) - return PTR_ERR(disp->pclk); - - /* If the live PL video clock is not valid, fall back to PS clock */ - if (IS_ERR_OR_NULL(disp->pclk)) { - disp->pclk = devm_clk_get(disp->dev, "dp_vtc_pixel_clk_in"); - if (IS_ERR(disp->pclk)) { - dev_err(disp->dev, "failed to init any video clock\n"); - return PTR_ERR(disp->pclk); - } - disp->pclk_from_ps = true; - } - zynqmp_disp_audio_init(disp); ret = zynqmp_disp_create_layers(disp); diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c index 26f2e1a7a46a..a278a73e1713 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c @@ -214,6 +214,23 @@ static int zynqmp_dpsub_init_clocks(struct zynqmp_dpsub *dpsub) return ret; } + /* Try the live PL video clock */ + dpsub->vid_clk = devm_clk_get(dpsub->dev, "dp_live_video_in_clk"); + if (!IS_ERR(dpsub->vid_clk)) + dpsub->vid_clk_from_ps = false; + else if (PTR_ERR(dpsub->vid_clk) == -EPROBE_DEFER) + return PTR_ERR
[PATCH v2 14/37] drm: xlnx: zynqmp_dpsub: Configure blender in zynqmp_disp_enable()
To prepare for control of the blender outside of the CRTC code, move the setup of the blender to the zynqmp_disp_enable() function. This doesn't introduce any functional change. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 14ecc58f5470..c21405e8f9dd 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -1387,6 +1387,9 @@ static int zynqmp_disp_create_layers(struct zynqmp_disp *disp) */ static void zynqmp_disp_enable(struct zynqmp_disp *disp) { + zynqmp_disp_blend_set_output_format(disp, ZYNQMP_DPSUB_FORMAT_RGB); + zynqmp_disp_blend_set_bg_color(disp, 0, 0, 0); + zynqmp_disp_avbuf_enable(disp); /* Choose clock source based on the DT clock handle. */ zynqmp_disp_avbuf_set_clocks_sources(disp, disp->pclk_from_ps, @@ -1461,9 +1464,6 @@ zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc, return; } - zynqmp_disp_blend_set_output_format(disp, ZYNQMP_DPSUB_FORMAT_RGB); - zynqmp_disp_blend_set_bg_color(disp, 0, 0, 0); - zynqmp_disp_enable(disp); /* Delay of 3 vblank intervals for timing gen to be stable */ -- Regards, Laurent Pinchart
[PATCH v2 36/37] arm64: dts: zynqmp: Add ports for the DisplayPort subsystem
The DPSUB DT bindings now specify ports to model the connections with the programmable logic and the DisplayPort output. Add them to the device tree. Signed-off-by: Laurent Pinchart --- arch/arm64/boot/dts/xilinx/zynqmp.dtsi | 24 1 file changed, 24 insertions(+) diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi index a549265e55f6..307c76cd8544 100644 --- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi +++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi @@ -930,6 +930,30 @@ zynqmp_dpsub: display@fd4a { <&zynqmp_dpdma ZYNQMP_DPDMA_VIDEO1>, <&zynqmp_dpdma ZYNQMP_DPDMA_VIDEO2>, <&zynqmp_dpdma ZYNQMP_DPDMA_GRAPHICS>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + }; + port@1 { + reg = <1>; + }; + port@2 { + reg = <2>; + }; + port@3 { + reg = <3>; + }; + port@4 { + reg = <4>; + }; + port@5 { + reg = <5>; + }; + }; }; }; }; -- Regards, Laurent Pinchart
[PATCH v2 21/37] drm: xlnx: zynqmp_dpsub: Move CRTC to zynqmp_dpsub structure
Decouple the zynqmp_disp, which handles the hardware configuration, from the DRM CRTC by moving the CRTC to the zynqmp_dpsub structure. The CRTC handling code will be moved to a separate file in a subsequent step. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 20 +--- drivers/gpu/drm/xlnx/zynqmp_dpsub.h | 3 +++ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 640be60c4214..94073cdfd714 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -163,7 +163,6 @@ struct zynqmp_disp_layer { * @dev: Device structure * @drm: DRM core * @dpsub: Display subsystem - * @crtc: DRM CRTC * @blend.base: Register I/O base address for the blender * @avbuf.base: Register I/O base address for the audio/video buffer manager * @audio.base: Registers I/O base address for the audio mixer @@ -174,8 +173,6 @@ struct zynqmp_disp { struct drm_device *drm; struct zynqmp_dpsub *dpsub; - struct drm_crtc crtc; - struct { void __iomem *base; } blend; @@ -902,7 +899,7 @@ static void zynqmp_disp_audio_disable(struct zynqmp_disp *disp) */ void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp) { - struct drm_crtc *crtc = &disp->crtc; + struct drm_crtc *crtc = &disp->dpsub->crtc; drm_crtc_handle_vblank(crtc); } @@ -915,7 +912,7 @@ void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp) */ uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp) { - return drm_crtc_mask(&disp->crtc); + return drm_crtc_mask(&disp->dpsub->crtc); } /* - @@ -1410,7 +1407,7 @@ static int zynqmp_disp_setup_clock(struct zynqmp_disp *disp, static inline struct zynqmp_disp *crtc_to_disp(struct drm_crtc *crtc) { - return container_of(crtc, struct zynqmp_disp, crtc); + return container_of(crtc, struct zynqmp_dpsub, crtc)->disp; } static void @@ -1458,7 +1455,7 @@ zynqmp_disp_crtc_atomic_disable(struct drm_crtc *crtc, zynqmp_disp_disable(disp); - drm_crtc_vblank_off(&disp->crtc); + drm_crtc_vblank_off(crtc); spin_lock_irq(&crtc->dev->event_lock); if (crtc->state->event) { @@ -1543,24 +1540,25 @@ static const struct drm_crtc_funcs zynqmp_disp_crtc_funcs = { static int zynqmp_disp_create_crtc(struct zynqmp_disp *disp) { struct drm_plane *plane = &disp->layers[ZYNQMP_DISP_LAYER_GFX].plane; + struct drm_crtc *crtc = &disp->dpsub->crtc; int ret; - ret = drm_crtc_init_with_planes(disp->drm, &disp->crtc, plane, + ret = drm_crtc_init_with_planes(disp->drm, crtc, plane, NULL, &zynqmp_disp_crtc_funcs, NULL); if (ret < 0) return ret; - drm_crtc_helper_add(&disp->crtc, &zynqmp_disp_crtc_helper_funcs); + drm_crtc_helper_add(crtc, &zynqmp_disp_crtc_helper_funcs); /* Start with vertical blanking interrupt reporting disabled. */ - drm_crtc_vblank_off(&disp->crtc); + drm_crtc_vblank_off(crtc); return 0; } static void zynqmp_disp_map_crtc_to_plane(struct zynqmp_disp *disp) { - u32 possible_crtcs = drm_crtc_mask(&disp->crtc); + u32 possible_crtcs = drm_crtc_mask(&disp->dpsub->crtc); unsigned int i; for (i = 0; i < ARRAY_SIZE(disp->layers); i++) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h index 5bd42e192e17..4ee0dc69ebee 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h @@ -12,6 +12,7 @@ #ifndef _ZYNQMP_DPSUB_H_ #define _ZYNQMP_DPSUB_H_ +#include #include struct clk; @@ -37,6 +38,7 @@ enum zynqmp_dpsub_format { * @vid_clk_from_ps: True of the video clock comes from PS, false from PL * @aud_clk: Audio clock * @aud_clk_from_ps: True of the audio clock comes from PS, false from PL + * @crtc: The DRM CRTC * @encoder: The dummy DRM encoder * @bridge: The DP encoder bridge * @disp: The display controller @@ -53,6 +55,7 @@ struct zynqmp_dpsub { struct clk *aud_clk; bool aud_clk_from_ps; + struct drm_crtc crtc; struct drm_encoder encoder; struct drm_bridge *bridge; -- Regards, Laurent Pinchart
[PATCH v2 04/37] drm: xlnx: zynqmp_dpsub: Create DRM bridge to model DP encoder
The DP encoder is currently modelled as a DRM encoder and DRM connector. This doesn't support system configurations where the DP encoder is driven by the FPGA programmable logic, using the live video input to the DP subsystem. To enable such use cases, we need to model the encoder as a DRM bridge. As a first step, create a DRM bridge in the DP encoder driver. Move and delegate the implementation of the DRM encoder and connector operations to the bridge to prepare for the transition. The bridge will be registered with the DRM core as a separate change. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 333 +-- 1 file changed, 225 insertions(+), 108 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index a2d87543d637..8acd5cfbbd1d 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -285,6 +285,7 @@ struct zynqmp_dp_config { * @iomem: device I/O memory for register access * @reset: reset controller * @irq: irq + * @bridge: DRM bridge for the DP encoder * @config: IP core configuration from DTS * @aux: aux channel * @phy: PHY handles for DP lanes @@ -307,6 +308,8 @@ struct zynqmp_dp { struct reset_control *reset; int irq; + struct drm_bridge bridge; + struct zynqmp_dp_config config; struct drm_dp_aux aux; struct phy *phy[ZYNQMP_DP_MAX_LANES]; @@ -331,6 +334,11 @@ static inline struct zynqmp_dp *connector_to_dp(struct drm_connector *connector) return container_of(connector, struct zynqmp_dp, connector); } +static inline struct zynqmp_dp *bridge_to_dp(struct drm_bridge *bridge) +{ + return container_of(bridge, struct zynqmp_dp, bridge); +} + static void zynqmp_dp_write(struct zynqmp_dp *dp, int offset, u32 val) { writel(val, dp->iomem + offset); @@ -1255,7 +1263,7 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp, zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_VSTART, mode->vtotal - mode->vsync_start); - /* In synchronous mode, set the diviers */ + /* In synchronous mode, set the dividers */ if (dp->config.misc0 & ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK) { reg = drm_dp_bw_code_to_link_rate(dp->mode.bw_code); zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_N_VID, reg); @@ -1281,13 +1289,167 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp, } /* - - * DRM Connector + * DRM Bridge */ -static enum drm_connector_status -zynqmp_dp_connector_detect(struct drm_connector *connector, bool force) +static int zynqmp_dp_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { - struct zynqmp_dp *dp = connector_to_dp(connector); + return 0; +} + +static int zynqmp_dp_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + struct zynqmp_dp *dp = bridge_to_dp(bridge); + int rate; + + if (mode->clock > ZYNQMP_MAX_FREQ) { + dev_dbg(dp->dev, "filtered mode %s for high pixel rate\n", + mode->name); + drm_mode_debug_printmodeline(mode); + return MODE_CLOCK_HIGH; + } + + /* Check with link rate and lane count */ + rate = zynqmp_dp_max_rate(dp->link_config.max_rate, + dp->link_config.max_lanes, dp->config.bpp); + if (mode->clock > rate) { + dev_dbg(dp->dev, "filtered mode %s for high pixel rate\n", + mode->name); + drm_mode_debug_printmodeline(mode); + return MODE_CLOCK_HIGH; + } + + return MODE_OK; +} + +static void zynqmp_dp_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct zynqmp_dp *dp = bridge_to_dp(bridge); + struct drm_atomic_state *state = old_bridge_state->base.state; + const struct drm_crtc_state *crtc_state; + const struct drm_display_mode *adjusted_mode; + const struct drm_display_mode *mode; + struct drm_connector *connector; + struct drm_crtc *crtc; + unsigned int i; + int rate; + int ret; + + pm_runtime_get_sync(dp->dev); + + /* +* Retrieve the CRTC mode and adjusted mode. This requires a little +* dance to go from the bridge to the encoder, to the connector and to +* the CRTC. +*/ + connector = drm_atomic_get_new_connector_for_encoder(state, +bridge->encoder); + crtc = drm_atomic_get_new_connector_state(state, conn
[PATCH v2 34/37] drm: xlnx: zynqmp_dpsub: Support operation without DMA engine
To prepare for usage of the DPSUB as a DisplayPort bridge without creating a DRM device, make initialization and usage of the DMA engine optional. The flag that controls this feature is currently hardcoded to operating with the DMA engine, this will be made dynamic based on the device tree configuration in a subsequent change. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 26 -- drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 3 +++ drivers/gpu/drm/xlnx/zynqmp_dpsub.h | 3 +++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 1c46f25001b7..3deed843f2ea 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -926,8 +926,10 @@ void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer) { unsigned int i; - for (i = 0; i < layer->drm_fmt->num_planes; i++) - dmaengine_terminate_sync(layer->dmas[i].chan); + if (layer->disp->dpsub->dma_enabled) { + for (i = 0; i < layer->drm_fmt->num_planes; i++) + dmaengine_terminate_sync(layer->dmas[i].chan); + } zynqmp_disp_avbuf_disable_video(layer->disp, layer); zynqmp_disp_blend_layer_disable(layer->disp, layer); @@ -950,6 +952,9 @@ void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, zynqmp_disp_avbuf_set_format(layer->disp, layer, layer->disp_fmt); + if (!layer->disp->dpsub->dma_enabled) + return; + /* * Set pconfig for each DMA channel to indicate they're part of a * video group. @@ -985,6 +990,9 @@ int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer, const struct drm_format_info *info = layer->drm_fmt; unsigned int i; + if (!layer->disp->dpsub->dma_enabled) + return 0; + for (i = 0; i < info->num_planes; i++) { unsigned int width = state->crtc_w / (i ? info->hsub : 1); unsigned int height = state->crtc_h / (i ? info->vsub : 1); @@ -1032,7 +1040,7 @@ static void zynqmp_disp_layer_release_dma(struct zynqmp_disp *disp, { unsigned int i; - if (!layer->info) + if (!layer->info || !disp->dpsub->dma_enabled) return; for (i = 0; i < layer->info->num_channels; i++) { @@ -1075,6 +1083,9 @@ static int zynqmp_disp_layer_request_dma(struct zynqmp_disp *disp, unsigned int i; int ret; + if (!disp->dpsub->dma_enabled) + return 0; + for (i = 0; i < layer->info->num_channels; i++) { struct zynqmp_disp_layer_dma *dma = &layer->dmas[i]; char dma_channel_name[16]; @@ -1217,7 +1228,6 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub) { struct platform_device *pdev = to_platform_device(dpsub->dev); struct zynqmp_disp *disp; - struct zynqmp_disp_layer *layer; struct resource *res; int ret; @@ -1253,8 +1263,12 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub) if (ret) goto error; - layer = &disp->layers[ZYNQMP_DPSUB_LAYER_VID]; - dpsub->dma_align = 1 << layer->dmas[0].chan->device->copy_align; + if (disp->dpsub->dma_enabled) { + struct zynqmp_disp_layer *layer; + + layer = &disp->layers[ZYNQMP_DPSUB_LAYER_VID]; + dpsub->dma_align = 1 << layer->dmas[0].chan->device->copy_align; + } dpsub->disp = disp; diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c index 6627a1ec7791..6e4cd4479de1 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c @@ -158,6 +158,7 @@ static int zynqmp_dpsub_parse_dt(struct zynqmp_dpsub *dpsub) if (!np) { dev_warn(dpsub->dev, "missing ports, update DT bindings\n"); dpsub->connected_ports = BIT(ZYNQMP_DPSUB_PORT_OUT_DP); + dpsub->dma_enabled = true; return 0; } @@ -177,6 +178,8 @@ static int zynqmp_dpsub_parse_dt(struct zynqmp_dpsub *dpsub) (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))) dev_warn(dpsub->dev, "live video unsupported, ignoring\n"); + dpsub->dma_enabled = true; + if (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_AUDIO)) dev_warn(dpsub->dev, "live audio unsupported, ignoring\n"); diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h index 6ded6e45ac0a..09ea01878f2a 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h @@ -48,6 +48,8 @@ enum zynqmp_dpsub_format { * @aud_clk: Audio clock * @aud_clk_from_ps: True of the audio clock comes from PS, false from PL * @connected_ports: Bitmask of connected ports in the device tree + * @dma_enabled: True if the D
[PATCH v2 26/37] drm: xlnx: zynqmp_dpsub: Register AUX bus at bridge attach time
To prepare for operating as a standalone DP bridge with the DRM device implemented in the PL, move registration of the AUX bus to bridge attach time, as that's the earliest point when a DRM device is available. The DRM device pointer stored in zynqmp_dp isn't used anymore, drop it. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 41 +++- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 7ad6a5d96c5f..544b062ce723 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -276,7 +276,6 @@ struct zynqmp_dp_config { * struct zynqmp_dp - Xilinx DisplayPort core * @dev: device structure * @dpsub: Display subsystem - * @drm: DRM core * @iomem: device I/O memory for register access * @reset: reset controller * @irq: irq @@ -297,7 +296,6 @@ struct zynqmp_dp_config { struct zynqmp_dp { struct device *dev; struct zynqmp_dpsub *dpsub; - struct drm_device *drm; void __iomem *iomem; struct reset_control *reset; int irq; @@ -1057,7 +1055,7 @@ static int zynqmp_dp_aux_init(struct zynqmp_dp *dp) dp->aux.name = "ZynqMP DP AUX"; dp->aux.dev = dp->dev; - dp->aux.drm_dev = dp->drm; + dp->aux.drm_dev = dp->bridge.dev; dp->aux.transfer = zynqmp_dp_aux_transfer; return drm_dp_aux_register(&dp->aux); @@ -1283,14 +1281,35 @@ static int zynqmp_dp_bridge_attach(struct drm_bridge *bridge, struct zynqmp_dp *dp = bridge_to_dp(bridge); int ret; + /* Initialize and register the AUX adapter. */ + ret = zynqmp_dp_aux_init(dp); + if (ret) { + dev_err(dp->dev, "failed to initialize DP aux\n"); + return ret; + } + if (dp->next_bridge) { ret = drm_bridge_attach(bridge->encoder, dp->next_bridge, bridge, flags); if (ret < 0) - return ret; + goto error; } + /* Now that initialisation is complete, enable interrupts. */ + zynqmp_dp_write(dp, ZYNQMP_DP_INT_EN, ZYNQMP_DP_INT_ALL); + return 0; + +error: + zynqmp_dp_aux_cleanup(dp); + return ret; +} + +static void zynqmp_dp_bridge_detach(struct drm_bridge *bridge) +{ + struct zynqmp_dp *dp = bridge_to_dp(bridge); + + zynqmp_dp_aux_cleanup(dp); } static int zynqmp_dp_bridge_mode_valid(struct drm_bridge *bridge, @@ -1495,6 +1514,7 @@ static struct edid *zynqmp_dp_bridge_get_edid(struct drm_bridge *bridge, static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = { .attach = zynqmp_dp_bridge_attach, + .detach = zynqmp_dp_bridge_detach, .mode_valid = zynqmp_dp_bridge_mode_valid, .atomic_enable = zynqmp_dp_bridge_atomic_enable, .atomic_disable = zynqmp_dp_bridge_atomic_disable, @@ -1594,7 +1614,6 @@ int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub) { struct zynqmp_dp *dp = dpsub->dp; struct drm_bridge *bridge = &dp->bridge; - int ret; dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK; zynqmp_dp_set_format(dp, NULL, ZYNQMP_DPSUB_FORMAT_RGB, 8); @@ -1606,16 +1625,6 @@ int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub) bridge->type = DRM_MODE_CONNECTOR_DisplayPort; dpsub->bridge = bridge; - /* Initialize and register the AUX adapter. */ - ret = zynqmp_dp_aux_init(dp); - if (ret) { - dev_err(dp->dev, "failed to initialize DP aux\n"); - return ret; - } - - /* Now that initialisation is complete, enable interrupts. */ - zynqmp_dp_write(dp, ZYNQMP_DP_INT_EN, ZYNQMP_DP_INT_ALL); - return 0; } @@ -1633,7 +1642,6 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) dp->dev = &pdev->dev; dp->dpsub = dpsub; dp->status = connector_status_disconnected; - dp->drm = drm; INIT_DELAYED_WORK(&dp->hpd_work, zynqmp_dp_hpd_work_func); @@ -1719,7 +1727,6 @@ void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub) disable_irq(dp->irq); cancel_delayed_work_sync(&dp->hpd_work); - zynqmp_dp_aux_cleanup(dp); zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMITTER_ENABLE, 0); zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, 0x); -- Regards, Laurent Pinchart
[PATCH v2 31/37] drm: xlnx: zynqmp_dpsub: Rename zynqmp_dpsub_handle_vblank with DRM prefix
The better convey its purpose, rename the zynqmp_dpsub_handle_vblank() function that belongs to the DRM layer to zynqmp_dpsub_drm_handle_vblank(). Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 2 +- drivers/gpu/drm/xlnx/zynqmp_kms.c | 4 ++-- drivers/gpu/drm/xlnx/zynqmp_kms.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 001bc24f92bd..5424f955be28 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -1581,7 +1581,7 @@ static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data) zynqmp_dp_write(dp, ZYNQMP_DP_INT_STATUS, status); if (status & ZYNQMP_DP_INT_VBLANK_START) - zynqmp_dpsub_handle_vblank(dp->dpsub); + zynqmp_dpsub_drm_handle_vblank(dp->dpsub); if (status & ZYNQMP_DP_INT_HPD_EVENT) schedule_delayed_work(&dp->hpd_work, 0); diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c index 746eeb824d30..df7f8e0c9eba 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_kms.c +++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c @@ -339,13 +339,13 @@ static void zynqmp_dpsub_map_crtc_to_plane(struct zynqmp_dpsub *dpsub) } /** - * zynqmp_dpsub_handle_vblank - Handle the vblank event + * zynqmp_dpsub_drm_handle_vblank - Handle the vblank event * @dpsub: DisplayPort subsystem * * This function handles the vblank interrupt, and sends an event to * CRTC object. This will be called by the DP vblank interrupt handler. */ -void zynqmp_dpsub_handle_vblank(struct zynqmp_dpsub *dpsub) +void zynqmp_dpsub_drm_handle_vblank(struct zynqmp_dpsub *dpsub) { drm_crtc_handle_vblank(&dpsub->drm->crtc); } diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.h b/drivers/gpu/drm/xlnx/zynqmp_kms.h index b5cb27159162..01be96b00e3f 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_kms.h +++ b/drivers/gpu/drm/xlnx/zynqmp_kms.h @@ -38,7 +38,7 @@ struct zynqmp_dpsub_drm { struct drm_encoder encoder; }; -void zynqmp_dpsub_handle_vblank(struct zynqmp_dpsub *dpsub); +void zynqmp_dpsub_drm_handle_vblank(struct zynqmp_dpsub *dpsub); int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub); void zynqmp_dpsub_drm_cleanup(struct zynqmp_dpsub *dpsub); -- Regards, Laurent Pinchart
[PATCH v2 37/37] arm64: dts: zynqmp: zcu106a: Describe DisplayPort connector
Add a device tree node to describe the DisplayPort connector, and connect it to the DPSUB output. Signed-off-by: Laurent Pinchart --- .../boot/dts/xilinx/zynqmp-zcu106-revA.dts| 20 +++ 1 file changed, 20 insertions(+) diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts b/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts index e2dd72fe33ce..24a252317150 100644 --- a/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts +++ b/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts @@ -150,6 +150,18 @@ refhdmi: refhdmi { #clock-cells = <0>; clock-frequency = <114285000>; }; + + dpcon { + compatible = "dp-connector"; + label = "P11"; + type = "full-size"; + + port { + dpcon_in: endpoint { + remote-endpoint = <&dpsub_dp_out>; + }; + }; + }; }; &can1 { @@ -1015,4 +1027,12 @@ &zynqmp_dpsub { phy-names = "dp-phy0", "dp-phy1"; phys = <&psgtr 1 PHY_TYPE_DP 0 3>, <&psgtr 0 PHY_TYPE_DP 1 3>; + + ports { + port@5 { + dpsub_dp_out: endpoint { + remote-endpoint = <&dpcon_in>; + }; + }; + }; }; -- Regards, Laurent Pinchart
[PATCH v2 27/37] drm: xlnx: zynqmp_dpsub: Move DP bridge init to zynqmp_dp_probe()
There's no need to delay bridge initialization, move it to zynqmp_dp_probe() and drop the zynqmp_dp_drm_init() function. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 30 -- drivers/gpu/drm/xlnx/zynqmp_dp.h | 1 - drivers/gpu/drm/xlnx/zynqmp_kms.c | 6 +- 3 files changed, 13 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 544b062ce723..0c7add926da3 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -1610,27 +1610,10 @@ static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data) * Initialization & Cleanup */ -int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub) -{ - struct zynqmp_dp *dp = dpsub->dp; - struct drm_bridge *bridge = &dp->bridge; - - dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK; - zynqmp_dp_set_format(dp, NULL, ZYNQMP_DPSUB_FORMAT_RGB, 8); - - /* Initialize the bridge. */ - bridge->funcs = &zynqmp_dp_bridge_funcs; - bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID - | DRM_BRIDGE_OP_HPD; - bridge->type = DRM_MODE_CONNECTOR_DisplayPort; - dpsub->bridge = bridge; - - return 0; -} - int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) { struct platform_device *pdev = to_platform_device(dpsub->dev); + struct drm_bridge *bridge; struct zynqmp_dp *dp; struct resource *res; int ret; @@ -1673,6 +1656,14 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) if (ret) goto err_reset; + /* Initialize the bridge. */ + bridge = &dp->bridge; + bridge->funcs = &zynqmp_dp_bridge_funcs; + bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID + | DRM_BRIDGE_OP_HPD; + bridge->type = DRM_MODE_CONNECTOR_DisplayPort; + dpsub->bridge = bridge; + /* * Acquire the next bridge in the chain. Ignore errors caused by port@5 * not being connected for backward-compatibility with older DTs. @@ -1683,6 +1674,9 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) goto err_reset; /* Initialize the hardware. */ + dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK; + zynqmp_dp_set_format(dp, NULL, ZYNQMP_DPSUB_FORMAT_RGB, 8); + zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN, ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL); zynqmp_dp_set(dp, ZYNQMP_DP_PHY_RESET, ZYNQMP_DP_PHY_RESET_ALL_RESET); diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.h b/drivers/gpu/drm/xlnx/zynqmp_dp.h index 4507740093f6..736d810fa16f 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.h +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.h @@ -20,7 +20,6 @@ struct zynqmp_dpsub; void zynqmp_dp_enable_vblank(struct zynqmp_dp *dp); void zynqmp_dp_disable_vblank(struct zynqmp_dp *dp); -int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub); int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm); void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub); diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c index 1445337de478..d96544855c14 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_kms.c +++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c @@ -348,7 +348,7 @@ int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub) struct drm_connector *connector; int ret; - /* Create the planes and the CRTC, and initialize the DP encoder. */ + /* Create the planes and the CRTC. */ ret = zynqmp_dpsub_create_planes(dpsub); if (ret) return ret; @@ -359,10 +359,6 @@ int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub) zynqmp_dpsub_map_crtc_to_plane(dpsub); - ret = zynqmp_dp_drm_init(dpsub); - if (ret) - return ret; - /* Create the encoder and attach the bridge. */ encoder->possible_crtcs |= drm_crtc_mask(&dpsub->crtc); drm_simple_encoder_init(&dpsub->drm, encoder, DRM_MODE_ENCODER_NONE); -- Regards, Laurent Pinchart
[PATCH v2 33/37] drm: xlnx: zynqmp_dpsub: Allow configuration of layer mode
Add a mode parameter to the zynqmp_disp_layer_enable() to set the layer mode, to prepare for live mode support. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 30 +- drivers/gpu/drm/xlnx/zynqmp_disp.h | 13 - drivers/gpu/drm/xlnx/zynqmp_kms.c | 2 +- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index b6fb168b6bc3..1c46f25001b7 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -78,16 +78,6 @@ struct zynqmp_disp_format { const u32 *sf; }; -/** - * enum zynqmp_disp_layer_mode - Layer mode - * @ZYNQMP_DISP_LAYER_NONLIVE: non-live (memory) mode - * @ZYNQMP_DISP_LAYER_LIVE: live (stream) mode - */ -enum zynqmp_disp_layer_mode { - ZYNQMP_DISP_LAYER_NONLIVE, - ZYNQMP_DISP_LAYER_LIVE -}; - /** * struct zynqmp_disp_layer_dma - DMA channel for one data plane of a layer * @chan: DMA channel @@ -131,7 +121,7 @@ struct zynqmp_disp_layer { const struct zynqmp_disp_format *disp_fmt; const struct drm_format_info *drm_fmt; - enum zynqmp_disp_layer_mode mode; + enum zynqmp_dpsub_layer_mode mode; }; /** @@ -519,27 +509,25 @@ static void zynqmp_disp_avbuf_disable_audio(struct zynqmp_disp *disp) * zynqmp_disp_avbuf_enable_video - Enable a video layer * @disp: Display controller * @layer: The layer - * @mode: Operating mode of layer * * Enable the video/graphics buffer for @layer. */ static void zynqmp_disp_avbuf_enable_video(struct zynqmp_disp *disp, - struct zynqmp_disp_layer *layer, - enum zynqmp_disp_layer_mode mode) + struct zynqmp_disp_layer *layer) { u32 val; val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_OUTPUT); if (zynqmp_disp_layer_is_video(layer)) { val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; - if (mode == ZYNQMP_DISP_LAYER_NONLIVE) + if (layer->mode == ZYNQMP_DPSUB_LAYER_NONLIVE) val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MEM; else val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_LIVE; } else { val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK; val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM; - if (mode == ZYNQMP_DISP_LAYER_NONLIVE) + if (layer->mode == ZYNQMP_DPSUB_LAYER_NONLIVE) val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM; else val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_LIVE; @@ -914,17 +902,17 @@ u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer, /** * zynqmp_disp_layer_enable - Enable a layer * @layer: The layer + * @mode: Operating mode of layer * * Enable the @layer in the audio/video buffer manager and the blender. DMA * channels are started separately by zynqmp_disp_layer_update(). */ -void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer) +void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer, + enum zynqmp_dpsub_layer_mode mode) { - zynqmp_disp_avbuf_enable_video(layer->disp, layer, - ZYNQMP_DISP_LAYER_NONLIVE); + layer->mode = mode; + zynqmp_disp_avbuf_enable_video(layer->disp, layer); zynqmp_disp_blend_layer_enable(layer->disp, layer); - - layer->mode = ZYNQMP_DISP_LAYER_NONLIVE; } /** diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.h b/drivers/gpu/drm/xlnx/zynqmp_disp.h index 9b8b202224d9..123cffac08be 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.h +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.h @@ -42,6 +42,16 @@ enum zynqmp_dpsub_layer_id { ZYNQMP_DPSUB_LAYER_GFX, }; +/** + * enum zynqmp_dpsub_layer_mode - Layer mode + * @ZYNQMP_DPSUB_LAYER_NONLIVE: non-live (memory) mode + * @ZYNQMP_DPSUB_LAYER_LIVE: live (stream) mode + */ +enum zynqmp_dpsub_layer_mode { + ZYNQMP_DPSUB_LAYER_NONLIVE, + ZYNQMP_DPSUB_LAYER_LIVE, +}; + void zynqmp_disp_enable(struct zynqmp_disp *disp); void zynqmp_disp_disable(struct zynqmp_disp *disp); int zynqmp_disp_setup_clock(struct zynqmp_disp *disp, @@ -52,7 +62,8 @@ void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp *disp, u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer, unsigned int *num_formats); -void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer); +void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer, + enum zynqmp_dpsub_layer_mode mode); void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer); void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, const struct drm_format_info *info); diff --git a/drivers/gpu/
[PATCH v2 30/37] drm: xlnx: zynqmp_dpsub: Decouple DRM device from zynqmp_dpsub
To complete the decoupling of the DRM device from the zynqmp_dpsub, group all DRM-related structures in a zynqmp_dpsub_drm structure and allocate it separately from the zynqmp_dpsub. The DRM managed allocation of the drm_device now doesn't cover the zynqmp_dpsub anymore, so we need to register a cleanup action to release the zynqmp_dpsub when the drm_device is released. The will allow usage of the DisplayPort encoder as a standalone bridge, without registering a DRM device in this driver. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 41 ++ drivers/gpu/drm/xlnx/zynqmp_dpsub.h | 18 +++- drivers/gpu/drm/xlnx/zynqmp_kms.c | 66 - drivers/gpu/drm/xlnx/zynqmp_kms.h | 24 ++- 4 files changed, 99 insertions(+), 50 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c index 946f58124e6f..86faa6edda4b 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c @@ -18,8 +18,6 @@ #include #include -#include -#include #include #include @@ -36,14 +34,20 @@ static int __maybe_unused zynqmp_dpsub_suspend(struct device *dev) { struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev); - return drm_mode_config_helper_suspend(&dpsub->drm); + if (!dpsub->drm) + return 0; + + return drm_mode_config_helper_suspend(&dpsub->drm->dev); } static int __maybe_unused zynqmp_dpsub_resume(struct device *dev) { struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev); - return drm_mode_config_helper_resume(&dpsub->drm); + if (!dpsub->drm) + return 0; + + return drm_mode_config_helper_resume(&dpsub->drm->dev); } static const struct dev_pm_ops zynqmp_dpsub_pm_ops = { @@ -138,12 +142,11 @@ static int zynqmp_dpsub_init_clocks(struct zynqmp_dpsub *dpsub) return 0; } -static void zynqmp_dpsub_release(struct drm_device *drm, void *res) +void zynqmp_dpsub_release(struct zynqmp_dpsub *dpsub) { - struct zynqmp_dpsub *dpsub = res; - kfree(dpsub->disp); kfree(dpsub->dp); + kfree(dpsub); } static int zynqmp_dpsub_probe(struct platform_device *pdev) @@ -152,14 +155,9 @@ static int zynqmp_dpsub_probe(struct platform_device *pdev) int ret; /* Allocate private data. */ - dpsub = devm_drm_dev_alloc(&pdev->dev, &zynqmp_dpsub_drm_driver, - struct zynqmp_dpsub, drm); - if (IS_ERR(dpsub)) - return PTR_ERR(dpsub); - - ret = drmm_add_action(&dpsub->drm, zynqmp_dpsub_release, dpsub); - if (ret < 0) - return ret; + dpsub = kzalloc(sizeof(*dpsub), GFP_KERNEL); + if (!dpsub) + return -ENOMEM; dpsub->dev = &pdev->dev; platform_set_drvdata(pdev, dpsub); @@ -204,6 +202,8 @@ static int zynqmp_dpsub_probe(struct platform_device *pdev) clk_disable_unprepare(dpsub->apb_clk); err_mem: of_reserved_mem_device_release(&pdev->dev); + if (!dpsub->drm) + zynqmp_dpsub_release(dpsub); return ret; } @@ -211,7 +211,8 @@ static int zynqmp_dpsub_remove(struct platform_device *pdev) { struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev); - zynqmp_dpsub_drm_cleanup(dpsub); + if (dpsub->drm) + zynqmp_dpsub_drm_cleanup(dpsub); zynqmp_disp_remove(dpsub); zynqmp_dp_remove(dpsub); @@ -220,6 +221,9 @@ static int zynqmp_dpsub_remove(struct platform_device *pdev) clk_disable_unprepare(dpsub->apb_clk); of_reserved_mem_device_release(&pdev->dev); + if (!dpsub->drm) + zynqmp_dpsub_release(dpsub); + return 0; } @@ -227,7 +231,10 @@ static void zynqmp_dpsub_shutdown(struct platform_device *pdev) { struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev); - drm_atomic_helper_shutdown(&dpsub->drm); + if (!dpsub->drm) + return; + + drm_atomic_helper_shutdown(&dpsub->drm->dev); } static const struct of_device_id zynqmp_dpsub_of_match[] = { diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h index 012dd05cf7bb..6c6029ad9bc5 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h @@ -12,17 +12,13 @@ #ifndef _ZYNQMP_DPSUB_H_ #define _ZYNQMP_DPSUB_H_ -#include -#include -#include - struct clk; struct device; struct drm_bridge; -struct drm_device; struct zynqmp_disp; struct zynqmp_disp_layer; struct zynqmp_dp; +struct zynqmp_dpsub_drm; #define ZYNQMP_DPSUB_NUM_LAYERS2 @@ -35,23 +31,19 @@ enum zynqmp_dpsub_format { /** * struct zynqmp_dpsub - ZynqMP DisplayPort Subsystem - * @drm: The DRM/KMS device * @dev: The physical device * @apb_clk: The APB clock * @vid_clk: Video clock * @vid_clk_from_ps: True of the video clock comes from
[PATCH v2 16/37] drm: xlnx: zynqmp_dpsub: Pass format info to zynqmp_disp_layer_set_format()
The zynqmp_disp_layer_set_format() function only needs format information, not a full plane state. Get the necessary info from the plane state in the caller and pass it to zynqmp_disp_layer_set_format(). This prepares for calling the function from non-DRM code. This doesn't introduce any functional change. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 10 -- 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 1d7642b38f1f..426e1a3b795f 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -1038,15 +1038,13 @@ static void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer) /** * zynqmp_disp_layer_set_format - Set the layer format * @layer: The layer - * @state: The plane state + * @info: The format info * - * Set the format for @layer based on @state->fb->format. The layer must be - * disabled. + * Set the format for @layer to @info. The layer must be disabled. */ static void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, -struct drm_plane_state *state) +const struct drm_format_info *info) { - const struct drm_format_info *info = state->fb->format; unsigned int i; layer->disp_fmt = zynqmp_disp_layer_find_format(layer, info->format); @@ -1191,7 +1189,7 @@ zynqmp_disp_plane_atomic_update(struct drm_plane *plane, if (old_state->fb) zynqmp_disp_layer_disable(layer); - zynqmp_disp_layer_set_format(layer, new_state); + zynqmp_disp_layer_set_format(layer, new_state->fb->format); } zynqmp_disp_layer_update(layer, new_state); -- Regards, Laurent Pinchart
[PATCH v2 17/37] drm: xlnx: zynqmp_dpsub: Remplace hardcoded values with ARRAY_SIZE()
Use the ARRAY_SIZE() macro to iterate over arrays, instead of hardcoding their size. This makes the code less error-prone should the array size change. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 8 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 426e1a3b795f..a88313ac29a7 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -1223,7 +1223,7 @@ static int zynqmp_disp_create_planes(struct zynqmp_disp *disp) unsigned int i, j; int ret; - for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { + for (i = 0; i < ARRAY_SIZE(disp->layers); i++) { struct zynqmp_disp_layer *layer = &disp->layers[i]; enum drm_plane_type type; u32 *drm_formats; @@ -1294,7 +1294,7 @@ static void zynqmp_disp_destroy_layers(struct zynqmp_disp *disp) { unsigned int i; - for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) + for (i = 0; i < ARRAY_SIZE(disp->layers); i++) zynqmp_disp_layer_release_dma(disp, &disp->layers[i]); } @@ -1356,7 +1356,7 @@ static int zynqmp_disp_create_layers(struct zynqmp_disp *disp) unsigned int i; int ret; - for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { + for (i = 0; i < ARRAY_SIZE(disp->layers); i++) { struct zynqmp_disp_layer *layer = &disp->layers[i]; layer->id = i; @@ -1593,7 +1593,7 @@ static void zynqmp_disp_map_crtc_to_plane(struct zynqmp_disp *disp) u32 possible_crtcs = drm_crtc_mask(&disp->crtc); unsigned int i; - for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) + for (i = 0; i < ARRAY_SIZE(disp->layers); i++) disp->layers[i].plane.possible_crtcs = possible_crtcs; } -- Regards, Laurent Pinchart
[PATCH v2 15/37] drm: xlnx: zynqmp_dpsub: Use local variable in zynqmp_disp_layer_update()
Reuse the local info variable instead of going through the layer pointer in zynqmp_disp_layer_update(). This doesn't introduce any functional change. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index c21405e8f9dd..1d7642b38f1f 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -1089,7 +1089,7 @@ static int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer, const struct drm_format_info *info = layer->drm_fmt; unsigned int i; - for (i = 0; i < layer->drm_fmt->num_planes; i++) { + for (i = 0; i < info->num_planes; i++) { unsigned int width = state->crtc_w / (i ? info->hsub : 1); unsigned int height = state->crtc_h / (i ? info->vsub : 1); struct zynqmp_disp_layer_dma *dma = &layer->dmas[i]; -- Regards, Laurent Pinchart
[PATCH v2 23/37] drm: xlnx: zynqmp_dpsub: Move DRM/KMS initialization to separate file
Start preparation for using the DPSUB as a standalone DisplayPort encoder without a display controller by moving the DRM/KMS initialization to a new zynqmp_kms.c file. No functional change intended. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/Makefile | 2 +- drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 43 ++ drivers/gpu/drm/xlnx/zynqmp_kms.c | 70 + drivers/gpu/drm/xlnx/zynqmp_kms.h | 19 4 files changed, 93 insertions(+), 41 deletions(-) create mode 100644 drivers/gpu/drm/xlnx/zynqmp_kms.c create mode 100644 drivers/gpu/drm/xlnx/zynqmp_kms.h diff --git a/drivers/gpu/drm/xlnx/Makefile b/drivers/gpu/drm/xlnx/Makefile index 51c24b72217b..ea1422a39502 100644 --- a/drivers/gpu/drm/xlnx/Makefile +++ b/drivers/gpu/drm/xlnx/Makefile @@ -1,2 +1,2 @@ -zynqmp-dpsub-y := zynqmp_disp.o zynqmp_dpsub.o zynqmp_dp.o +zynqmp-dpsub-y := zynqmp_disp.o zynqmp_dpsub.o zynqmp_dp.o zynqmp_kms.o obj-$(CONFIG_DRM_ZYNQMP_DPSUB) += zynqmp-dpsub.o diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c index cb01548f2b8c..e4cb7b82556b 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c @@ -17,9 +17,7 @@ #include #include -#include #include -#include #include #include #include @@ -30,12 +28,12 @@ #include #include #include -#include #include #include "zynqmp_disp.h" #include "zynqmp_dp.h" #include "zynqmp_dpsub.h" +#include "zynqmp_kms.h" /* - * Dumb Buffer & Framebuffer Allocation @@ -98,8 +96,6 @@ static const struct drm_driver zynqmp_dpsub_drm_driver = { static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub) { - struct drm_encoder *encoder = &dpsub->encoder; - struct drm_connector *connector; struct drm_device *drm = &dpsub->drm; int ret; @@ -120,43 +116,10 @@ static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub) drm_kms_helper_poll_init(drm); - /* -* Initialize the DISP and DP components. This will creates planes, -* CRTC, and a bridge for the DP encoder. -*/ - ret = zynqmp_disp_drm_init(dpsub); - if (ret) + ret = zynqmp_dpsub_kms_init(dpsub); + if (ret < 0) goto err_poll_fini; - ret = zynqmp_dp_drm_init(dpsub); - if (ret) - goto err_poll_fini; - - /* Create the encoder and attach the bridge. */ - encoder->possible_crtcs |= zynqmp_disp_get_crtc_mask(dpsub->disp); - drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE); - - ret = drm_bridge_attach(encoder, dpsub->bridge, NULL, - DRM_BRIDGE_ATTACH_NO_CONNECTOR); - if (ret) { - dev_err(dpsub->dev, "failed to attach bridge to encoder\n"); - goto err_poll_fini; - } - - /* Create the connector for the chain of bridges. */ - connector = drm_bridge_connector_init(drm, encoder); - if (IS_ERR(connector)) { - dev_err(dpsub->dev, "failed to created connector\n"); - ret = PTR_ERR(connector); - goto err_poll_fini; - } - - ret = drm_connector_attach_encoder(connector, encoder); - if (ret < 0) { - dev_err(dpsub->dev, "failed to attach connector to encoder\n"); - goto err_poll_fini; - } - /* Reset all components and register the DRM device. */ drm_mode_config_reset(drm); diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c new file mode 100644 index ..e4e7f8fd96d2 --- /dev/null +++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ZynqMP DisplayPort Subsystem - KMS API + * + * Copyright (C) 2017 - 2021 Xilinx, Inc. + * + * Authors: + * - Hyun Woo Kwon + * - Laurent Pinchart + */ + +#include +#include +#include +#include +#include + +#include "zynqmp_disp.h" +#include "zynqmp_dp.h" +#include "zynqmp_dpsub.h" +#include "zynqmp_kms.h" + +/* - + * Initialization + */ + +int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub) +{ + struct drm_encoder *encoder = &dpsub->encoder; + struct drm_connector *connector; + int ret; + + /* +* Initialize the DISP and DP components. This will creates planes, +* CRTC, and a bridge for the DP encoder. +*/ + ret = zynqmp_disp_drm_init(dpsub); + if (ret) + return ret; + + ret = zynqmp_dp_drm_init(dpsub); + if (ret) + return ret; + + /* Create the encoder and attach the bridge. */ + encoder->possible_crtcs |= zynqmp_disp_get_crtc_mask(dpsub->disp); + drm_simple_encoder_init(&dpsub->drm, encoder, DRM_MODE_ENCODER_NONE); + + ret = drm_bridge_
[PATCH v2 25/37] drm: xlnx: zynqmp_dpsub: Move planes handling to zynqmp_kms.c
Decouple the planes handling from the display controller programming by moving the corresponding code from zynqmp_disp.c to zynqmp_kms.c. This prepares for using the DPSUB with a live video input, without creating DRM planes in the DPSUB driver. While at it, fix a typo in a comment. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 170 ++-- drivers/gpu/drm/xlnx/zynqmp_disp.h | 19 +++- drivers/gpu/drm/xlnx/zynqmp_dpsub.h | 2 + drivers/gpu/drm/xlnx/zynqmp_kms.c | 145 +++- 4 files changed, 167 insertions(+), 169 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 7bf7e2113ef0..2df946f93575 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -9,16 +9,11 @@ * - Laurent Pinchart */ -#include -#include -#include -#include #include #include #include #include #include -#include #include #include @@ -142,7 +137,6 @@ struct zynqmp_disp_layer { /** * struct zynqmp_disp - Display controller * @dev: Device structure - * @drm: DRM core * @dpsub: Display subsystem * @blend.base: Register I/O base address for the blender * @avbuf.base: Register I/O base address for the audio/video buffer manager @@ -151,7 +145,6 @@ struct zynqmp_disp_layer { */ struct zynqmp_disp { struct device *dev; - struct drm_device *drm; struct zynqmp_dpsub *dpsub; struct { @@ -381,11 +374,6 @@ static void zynqmp_disp_avbuf_write(struct zynqmp_disp *disp, int reg, u32 val) writel(val, disp->avbuf.base + reg); } -static bool zynqmp_disp_layer_is_gfx(const struct zynqmp_disp_layer *layer) -{ - return layer->id == ZYNQMP_DPSUB_LAYER_GFX; -} - static bool zynqmp_disp_layer_is_video(const struct zynqmp_disp_layer *layer) { return layer->id == ZYNQMP_DPSUB_LAYER_VID; @@ -723,8 +711,8 @@ static void zynqmp_disp_blend_set_bg_color(struct zynqmp_disp *disp, * @enable: True to enable global alpha blending * @alpha: Global alpha value (ignored if @enabled is false) */ -static void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp *disp, - bool enable, u32 alpha) +void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp *disp, + bool enable, u32 alpha) { zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA, ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_VALUE(alpha) | @@ -905,8 +893,8 @@ zynqmp_disp_layer_find_format(struct zynqmp_disp_layer *layer, * supported by the layer. The number of formats in the array is returned * through the num_formats argument. */ -static u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer, - unsigned int *num_formats) +u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer, + unsigned int *num_formats) { unsigned int i; u32 *formats; @@ -930,7 +918,7 @@ static u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer, * Enable the @layer in the audio/video buffer manager and the blender. DMA * channels are started separately by zynqmp_disp_layer_update(). */ -static void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer) +void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer) { zynqmp_disp_avbuf_enable_video(layer->disp, layer, ZYNQMP_DISP_LAYER_NONLIVE); @@ -946,7 +934,7 @@ static void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer) * Disable the layer by stopping its DMA channels and disabling it in the * audio/video buffer manager and the blender. */ -static void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer) +void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer) { unsigned int i; @@ -964,8 +952,8 @@ static void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer) * * Set the format for @layer to @info. The layer must be disabled. */ -static void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, -const struct drm_format_info *info) +void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, + const struct drm_format_info *info) { unsigned int i; @@ -1003,8 +991,8 @@ static void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, * * Return: 0 on success, or the DMA descriptor failure error otherwise */ -static int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer, - struct drm_plane_state *state) +int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer, +struct drm_plane_state *state) { const struct drm_format_info *info = layer->drm_fmt; unsigned in
[PATCH v2 22/37] drm: xlnx: zynqmp_dpsub: Move planes to zynqmp_dpsub structure
Decouple the zynqmp_disp, which handles the hardware configuration, from the DRM planes by moving the planes to the zynqmp_dpsub structure. The planes handling code will be moved to a separate file in a subsequent step. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 32 - drivers/gpu/drm/xlnx/zynqmp_dpsub.h | 5 + 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 94073cdfd714..3b3aef42a390 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -73,7 +73,6 @@ #define ZYNQMP_DISP_AV_BUF_NUM_VID_GFX_BUFFERS 4 #define ZYNQMP_DISP_AV_BUF_NUM_BUFFERS 6 -#define ZYNQMP_DISP_NUM_LAYERS 2 #define ZYNQMP_DISP_MAX_NUM_SUB_PLANES 3 /** @@ -135,8 +134,7 @@ struct zynqmp_disp_layer_info { }; /** - * struct zynqmp_disp_layer - Display layer (DRM plane) - * @plane: DRM plane + * struct zynqmp_disp_layer - Display layer * @id: Layer ID * @disp: Back pointer to struct zynqmp_disp * @info: Static layer information @@ -146,7 +144,6 @@ struct zynqmp_disp_layer_info { * @mode: Current operation mode */ struct zynqmp_disp_layer { - struct drm_plane plane; enum zynqmp_disp_layer_id id; struct zynqmp_disp *disp; const struct zynqmp_disp_layer_info *info; @@ -183,7 +180,7 @@ struct zynqmp_disp { void __iomem *base; } audio; - struct zynqmp_disp_layer layers[ZYNQMP_DISP_NUM_LAYERS]; + struct zynqmp_disp_layer layers[ZYNQMP_DPSUB_NUM_LAYERS]; }; /* - @@ -1092,11 +1089,6 @@ static int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer, return 0; } -static inline struct zynqmp_disp_layer *plane_to_layer(struct drm_plane *plane) -{ - return container_of(plane, struct zynqmp_disp_layer, plane); -} - static int zynqmp_disp_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state) @@ -1125,7 +1117,8 @@ zynqmp_disp_plane_atomic_disable(struct drm_plane *plane, { struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); - struct zynqmp_disp_layer *layer = plane_to_layer(plane); + struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(plane->dev); + struct zynqmp_disp_layer *layer = &dpsub->disp->layers[plane->index]; if (!old_state->fb) return; @@ -1143,7 +1136,8 @@ zynqmp_disp_plane_atomic_update(struct drm_plane *plane, { struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); - struct zynqmp_disp_layer *layer = plane_to_layer(plane); + struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(plane->dev); + struct zynqmp_disp_layer *layer = &dpsub->disp->layers[plane->index]; bool format_changed = false; if (!old_state->fb || @@ -1195,6 +1189,7 @@ static int zynqmp_disp_create_planes(struct zynqmp_disp *disp) for (i = 0; i < ARRAY_SIZE(disp->layers); i++) { struct zynqmp_disp_layer *layer = &disp->layers[i]; + struct drm_plane *plane = &disp->dpsub->planes[i]; enum drm_plane_type type; unsigned int num_formats; u32 *formats; @@ -1206,7 +1201,7 @@ static int zynqmp_disp_create_planes(struct zynqmp_disp *disp) /* Graphics layer is primary, and video layer is overlay. */ type = zynqmp_disp_layer_is_video(layer) ? DRM_PLANE_TYPE_OVERLAY : DRM_PLANE_TYPE_PRIMARY; - ret = drm_universal_plane_init(disp->drm, &layer->plane, 0, + ret = drm_universal_plane_init(disp->drm, plane, 0, &zynqmp_disp_plane_funcs, formats, num_formats, NULL, type, NULL); @@ -1214,12 +1209,11 @@ static int zynqmp_disp_create_planes(struct zynqmp_disp *disp) if (ret) return ret; - drm_plane_helper_add(&layer->plane, -&zynqmp_disp_plane_helper_funcs); + drm_plane_helper_add(plane, &zynqmp_disp_plane_helper_funcs); - drm_plane_create_zpos_immutable_property(&layer->plane, i); + drm_plane_create_zpos_immutable_property(plane, i); if (zynqmp_disp_layer_is_gfx(layer)) - drm_plane_create_alpha_property(&layer->plane); + drm_plane_create_alpha_property(plane); } return 0; @@ -1539
[PATCH v2 32/37] drm: xlnx: zynqmp_dpsub: Parse DT to find connected ports
To prepare for live video input support, parse the device tree to find the connected ports. Warn about unsupported configurations, and error out when invalid. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 54 + drivers/gpu/drm/xlnx/zynqmp_dpsub.h | 13 +++ 2 files changed, 67 insertions(+) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c index 86faa6edda4b..6627a1ec7791 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -142,6 +143,55 @@ static int zynqmp_dpsub_init_clocks(struct zynqmp_dpsub *dpsub) return 0; } +static int zynqmp_dpsub_parse_dt(struct zynqmp_dpsub *dpsub) +{ + struct device_node *np; + unsigned int i; + + /* +* For backward compatibility with old device trees that don't contain +* ports, consider that only the DP output port is connected if no +* ports child no exists. +*/ + np = of_get_child_by_name(dpsub->dev->of_node, "ports"); + of_node_put(np); + if (!np) { + dev_warn(dpsub->dev, "missing ports, update DT bindings\n"); + dpsub->connected_ports = BIT(ZYNQMP_DPSUB_PORT_OUT_DP); + return 0; + } + + /* Check which ports are connected. */ + for (i = 0; i < ZYNQMP_DPSUB_NUM_PORTS; ++i) { + struct device_node *np; + + np = of_graph_get_remote_node(dpsub->dev->of_node, i, -1); + if (np) { + dpsub->connected_ports |= BIT(i); + of_node_put(np); + } + } + + /* Sanity checks. */ + if ((dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO)) || + (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))) + dev_warn(dpsub->dev, "live video unsupported, ignoring\n"); + + if (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_AUDIO)) + dev_warn(dpsub->dev, "live audio unsupported, ignoring\n"); + + if ((dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_OUT_VIDEO)) || + (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_OUT_AUDIO))) + dev_warn(dpsub->dev, "output to PL unsupported, ignoring\n"); + + if (!(dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_OUT_DP))) { + dev_err(dpsub->dev, "DP output port not connected\n"); + return -EINVAL; + } + + return 0; +} + void zynqmp_dpsub_release(struct zynqmp_dpsub *dpsub) { kfree(dpsub->disp); @@ -171,6 +221,10 @@ static int zynqmp_dpsub_probe(struct platform_device *pdev) if (ret < 0) goto err_mem; + ret = zynqmp_dpsub_parse_dt(dpsub); + if (ret < 0) + goto err_mem; + pm_runtime_enable(&pdev->dev); /* diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h index 6c6029ad9bc5..6ded6e45ac0a 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h @@ -22,6 +22,16 @@ struct zynqmp_dpsub_drm; #define ZYNQMP_DPSUB_NUM_LAYERS2 +enum zynqmp_dpsub_port { + ZYNQMP_DPSUB_PORT_LIVE_VIDEO, + ZYNQMP_DPSUB_PORT_LIVE_GFX, + ZYNQMP_DPSUB_PORT_LIVE_AUDIO, + ZYNQMP_DPSUB_PORT_OUT_VIDEO, + ZYNQMP_DPSUB_PORT_OUT_AUDIO, + ZYNQMP_DPSUB_PORT_OUT_DP, + ZYNQMP_DPSUB_NUM_PORTS, +}; + enum zynqmp_dpsub_format { ZYNQMP_DPSUB_FORMAT_RGB, ZYNQMP_DPSUB_FORMAT_YCRCB444, @@ -37,6 +47,7 @@ enum zynqmp_dpsub_format { * @vid_clk_from_ps: True of the video clock comes from PS, false from PL * @aud_clk: Audio clock * @aud_clk_from_ps: True of the audio clock comes from PS, false from PL + * @connected_ports: Bitmask of connected ports in the device tree * @drm: The DRM/KMS device data * @bridge: The DP encoder bridge * @disp: The display controller @@ -52,6 +63,8 @@ struct zynqmp_dpsub { struct clk *aud_clk; bool aud_clk_from_ps; + unsigned int connected_ports; + struct zynqmp_dpsub_drm *drm; struct drm_bridge *bridge; -- Regards, Laurent Pinchart
[PATCH v2 12/37] drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp_format.bus_fmt field
The bus_fmt field of the zynqmp_disp_format structure is unused. Drop it. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 31b52f01c32d..eae28a3c6d45 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -80,14 +80,12 @@ * struct zynqmp_disp_format - Display subsystem format information * @drm_fmt: DRM format (4CC) * @buf_fmt: AV buffer format - * @bus_fmt: Media bus formats (live formats) * @swap: Flag to swap R & B for RGB formats, and U & V for YUV formats * @sf: Scaling factors for color components */ struct zynqmp_disp_format { u32 drm_fmt; u32 buf_fmt; - u32 bus_fmt; bool swap; const u32 *sf; }; -- Regards, Laurent Pinchart
[PATCH v2 35/37] drm: xlnx: zynqmp_dpsub: Add support for live video input
Add partial live video support, with a single video input that bypasses blending. Skip registration of the DRM device in that case, but register the DRM bridge instead. The DRM device will be created by the driver for the display controller in the PL. Full live video mode with concurrent usage of the video and gfx inputs, and blending in the DPSUB video pipeline, is currently unsupported. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c| 55 + drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 32 + 2 files changed, 80 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 5424f955be28..7c9ae167eac7 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -1271,6 +1272,55 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp, zynqmp_dp_write(dp, ZYNQMP_DP_USER_DATA_COUNT_PER_LANE, reg); } +/* - + * DISP Configuration + */ + +static void zynqmp_dp_disp_enable(struct zynqmp_dp *dp, + struct drm_bridge_state *old_bridge_state) +{ + enum zynqmp_dpsub_layer_id layer_id; + struct zynqmp_disp_layer *layer; + const struct drm_format_info *info; + + if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO)) + layer_id = ZYNQMP_DPSUB_LAYER_VID; + else if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX)) + layer_id = ZYNQMP_DPSUB_LAYER_GFX; + else + return; + + layer = dp->dpsub->layers[layer_id]; + + /* TODO: Make the format configurable. */ + info = drm_format_info(DRM_FORMAT_YUV422); + zynqmp_disp_layer_set_format(layer, info); + zynqmp_disp_layer_enable(layer, ZYNQMP_DPSUB_LAYER_LIVE); + + if (layer_id == ZYNQMP_DPSUB_LAYER_GFX) + zynqmp_disp_blend_set_global_alpha(dp->dpsub->disp, true, 255); + else + zynqmp_disp_blend_set_global_alpha(dp->dpsub->disp, false, 0); + + zynqmp_disp_enable(dp->dpsub->disp); +} + +static void zynqmp_dp_disp_disable(struct zynqmp_dp *dp, + struct drm_bridge_state *old_bridge_state) +{ + struct zynqmp_disp_layer *layer; + + if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO)) + layer = dp->dpsub->layers[ZYNQMP_DPSUB_LAYER_VID]; + else if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX)) + layer = dp->dpsub->layers[ZYNQMP_DPSUB_LAYER_GFX]; + else + return; + + zynqmp_disp_disable(dp->dpsub->disp); + zynqmp_disp_layer_disable(layer); +} + /* - * DRM Bridge */ @@ -1355,6 +1405,8 @@ static void zynqmp_dp_bridge_atomic_enable(struct drm_bridge *bridge, pm_runtime_get_sync(dp->dev); + zynqmp_dp_disp_enable(dp, old_bridge_state); + /* * Retrieve the CRTC mode and adjusted mode. This requires a little * dance to go from the bridge to the encoder, to the connector and to @@ -1428,6 +1480,9 @@ static void zynqmp_dp_bridge_atomic_disable(struct drm_bridge *bridge, ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL); if (zynqmp_dpsub_audio_enabled(dp->dpsub)) zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 0); + + zynqmp_dp_disp_disable(dp, old_bridge_state); + pm_runtime_put_sync(dp->dev); } diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c index 6e4cd4479de1..bab862484d42 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -174,11 +175,22 @@ static int zynqmp_dpsub_parse_dt(struct zynqmp_dpsub *dpsub) } /* Sanity checks. */ + if ((dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO)) && + (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))) { + dev_err(dpsub->dev, "only one live video input is supported\n"); + return -EINVAL; + } + if ((dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO)) || - (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))) - dev_warn(dpsub->dev, "live video unsupported, ignoring\n"); - - dpsub->dma_enabled = true; + (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))) { + if (dpsub->vid_clk_from_ps) { + dev_err(dpsub->dev, + "live video input requires PL clock\n"); + return -EINVAL; + } + } else { + dps
[PATCH v2 29/37] drm: xlnx: zynqmp_dpsub: Move all DRM init and cleanup to zynqmp_kms.c
Continue the isolation of DRM/KMS code by moving all DRM init and cleanup from zynqmp_dpsub.c to zynqmp_kms.c. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 117 +- drivers/gpu/drm/xlnx/zynqmp_dpsub.h | 5 -- drivers/gpu/drm/xlnx/zynqmp_kms.c | 123 +++- drivers/gpu/drm/xlnx/zynqmp_kms.h | 5 +- 4 files changed, 127 insertions(+), 123 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c index 1d9e63a54ecb..946f58124e6f 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c @@ -18,126 +18,16 @@ #include #include -#include -#include #include -#include -#include -#include -#include #include -#include +#include #include -#include -#include #include "zynqmp_disp.h" #include "zynqmp_dp.h" #include "zynqmp_dpsub.h" #include "zynqmp_kms.h" -/* - - * Dumb Buffer & Framebuffer Allocation - */ - -static int zynqmp_dpsub_dumb_create(struct drm_file *file_priv, - struct drm_device *drm, - struct drm_mode_create_dumb *args) -{ - struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(drm); - unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8); - - /* Enforce the alignment constraints of the DMA engine. */ - args->pitch = ALIGN(pitch, dpsub->dma_align); - - return drm_gem_cma_dumb_create_internal(file_priv, drm, args); -} - -static struct drm_framebuffer * -zynqmp_dpsub_fb_create(struct drm_device *drm, struct drm_file *file_priv, - const struct drm_mode_fb_cmd2 *mode_cmd) -{ - struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(drm); - struct drm_mode_fb_cmd2 cmd = *mode_cmd; - unsigned int i; - - /* Enforce the alignment constraints of the DMA engine. */ - for (i = 0; i < ARRAY_SIZE(cmd.pitches); ++i) - cmd.pitches[i] = ALIGN(cmd.pitches[i], dpsub->dma_align); - - return drm_gem_fb_create(drm, file_priv, &cmd); -} - -static const struct drm_mode_config_funcs zynqmp_dpsub_mode_config_funcs = { - .fb_create = zynqmp_dpsub_fb_create, - .atomic_check = drm_atomic_helper_check, - .atomic_commit = drm_atomic_helper_commit, -}; - -/* - - * DRM/KMS Driver - */ - -DEFINE_DRM_GEM_CMA_FOPS(zynqmp_dpsub_drm_fops); - -static const struct drm_driver zynqmp_dpsub_drm_driver = { - .driver_features= DRIVER_MODESET | DRIVER_GEM | - DRIVER_ATOMIC, - - DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(zynqmp_dpsub_dumb_create), - - .fops = &zynqmp_dpsub_drm_fops, - - .name = "zynqmp-dpsub", - .desc = "Xilinx DisplayPort Subsystem Driver", - .date = "20130509", - .major = 1, - .minor = 0, -}; - -static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub) -{ - struct drm_device *drm = &dpsub->drm; - int ret; - - /* Initialize mode config, vblank and the KMS poll helper. */ - ret = drmm_mode_config_init(drm); - if (ret < 0) - return ret; - - drm->mode_config.funcs = &zynqmp_dpsub_mode_config_funcs; - drm->mode_config.min_width = 0; - drm->mode_config.min_height = 0; - drm->mode_config.max_width = ZYNQMP_DISP_MAX_WIDTH; - drm->mode_config.max_height = ZYNQMP_DISP_MAX_HEIGHT; - - ret = drm_vblank_init(drm, 1); - if (ret) - return ret; - - drm_kms_helper_poll_init(drm); - - ret = zynqmp_dpsub_kms_init(dpsub); - if (ret < 0) - goto err_poll_fini; - - /* Reset all components and register the DRM device. */ - drm_mode_config_reset(drm); - - ret = drm_dev_register(drm, 0); - if (ret < 0) - goto err_poll_fini; - - /* Initialize fbdev generic emulation. */ - drm_fbdev_generic_setup(drm, 24); - - return 0; - -err_poll_fini: - drm_kms_helper_poll_fini(drm); - return ret; -} - /* - * Power Management */ @@ -320,11 +210,8 @@ static int zynqmp_dpsub_probe(struct platform_device *pdev) static int zynqmp_dpsub_remove(struct platform_device *pdev) { struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev); - struct drm_device *drm = &dpsub->drm; - drm_dev_unregister(drm); - drm_atomic_helper_shutdown(drm); - drm_kms_helper_poll_fini(drm); + zynqmp_dpsub_drm_cleanup(dpsub); zynqmp_disp_remove(dpsub); zynqmp_dp_remove(dpsub);
[PATCH v2 07/37] drm: xlnx: zynqmp_dpsub: Move encoder to DPSUB core
As part of the transitition of the DP encoder to a DRM bridge, turn the DRM encoder into a dummy encoder and move it out of the DP code, to the DPSUB core. DP encoder operations are handled by the DP bridge, which is now attached to the encoder. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c| 79 ++--- drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 16 +- drivers/gpu/drm/xlnx/zynqmp_dpsub.h | 8 +++ 3 files changed, 25 insertions(+), 78 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 65b1ab4e4d2d..28f92b5d8385 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -15,12 +15,10 @@ #include #include #include -#include #include #include #include #include -#include #include #include @@ -277,7 +275,6 @@ struct zynqmp_dp_config { /** * struct zynqmp_dp - Xilinx DisplayPort core - * @encoder: the drm encoder structure * @connector: the drm connector structure * @dev: device structure * @dpsub: Display subsystem @@ -299,7 +296,6 @@ struct zynqmp_dp_config { * @train_set: set of training data */ struct zynqmp_dp { - struct drm_encoder encoder; struct drm_connector connector; struct device *dev; struct zynqmp_dpsub *dpsub; @@ -324,11 +320,6 @@ struct zynqmp_dp { u8 train_set[ZYNQMP_DP_MAX_LANES]; }; -static inline struct zynqmp_dp *encoder_to_dp(struct drm_encoder *encoder) -{ - return container_of(encoder, struct zynqmp_dp, encoder); -} - static inline struct zynqmp_dp *connector_to_dp(struct drm_connector *connector) { return container_of(connector, struct zynqmp_dp, connector); @@ -1566,7 +1557,7 @@ zynqmp_dp_connector_best_encoder(struct drm_connector *connector) { struct zynqmp_dp *dp = connector_to_dp(connector); - return &dp->encoder; + return &dp->dpsub->encoder; } static int zynqmp_dp_connector_mode_valid(struct drm_connector *connector, @@ -1594,55 +1585,6 @@ zynqmp_dp_connector_helper_funcs = { .mode_valid = zynqmp_dp_connector_mode_valid, }; -/* - - * DRM Encoder - */ - -static void zynqmp_dp_encoder_atomic_enable(struct drm_encoder *encoder, - struct drm_atomic_state *state) -{ - struct zynqmp_dp *dp = encoder_to_dp(encoder); - struct drm_bridge_state bridge_state; - - bridge_state.base.state = state; - zynqmp_dp_bridge_atomic_enable(&dp->bridge, &bridge_state); -} - -static void zynqmp_dp_encoder_atomic_disable(struct drm_encoder *encoder, -struct drm_atomic_state *state) -{ - struct zynqmp_dp *dp = encoder_to_dp(encoder); - struct drm_bridge_state bridge_state; - - bridge_state.base.state = state; - zynqmp_dp_bridge_atomic_disable(&dp->bridge, &bridge_state); -} - -static void -zynqmp_dp_encoder_atomic_mode_set(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *connector_state) -{ -} - -static int -zynqmp_dp_encoder_atomic_check(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) -{ - struct zynqmp_dp *dp = encoder_to_dp(encoder); - - return zynqmp_dp_bridge_atomic_check(&dp->bridge, NULL, crtc_state, -conn_state); -} - -static const struct drm_encoder_helper_funcs zynqmp_dp_encoder_helper_funcs = { - .atomic_enable = zynqmp_dp_encoder_atomic_enable, - .atomic_disable = zynqmp_dp_encoder_atomic_disable, - .atomic_mode_set= zynqmp_dp_encoder_atomic_mode_set, - .atomic_check = zynqmp_dp_encoder_atomic_check, -}; - /* - * Interrupt Handling */ @@ -1731,32 +1673,17 @@ int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub) { struct zynqmp_dp *dp = dpsub->dp; struct drm_bridge *bridge = &dp->bridge; - struct drm_encoder *encoder = &dp->encoder; int ret; dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK; zynqmp_dp_set_format(dp, NULL, ZYNQMP_DPSUB_FORMAT_RGB, 8); - /* -* Initialize the bridge. Setting the device and encoder manually is a -* hack, to be removed once the bridge will get attached to the encoder -* using the bridge API. -*/ - bridge->dev = dp->drm; - bridge->encoder = &dp->encoder; + /* Initialize the bridge. */ bridge->funcs = &zynqmp_dp_bridge_funcs; bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; bridge->type = DRM_MODE_CO
[PATCH v2 18/37] drm: xlnx: zynqmp_dpsub: Don't use drmm_kcalloc() for temporary data
The array of formats passed to drm_universal_plane_init() doesn't need to outlive the function call, as it's copied internally. Use kcalloc() instead of drmm_kcalloc() to allocate it, and free it right after usage. While at it, move the allocation and initialization of the formats array to a separate function, to prepare for splitting the DRM plane handling to a separate file. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 45 ++ 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index a88313ac29a7..0a2fa96bc126 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -1001,6 +1001,33 @@ zynqmp_disp_layer_find_format(struct zynqmp_disp_layer *layer, return NULL; } +/** + * zynqmp_disp_layer_drm_formats - Return the DRM formats supported by the layer + * @layer: The layer + * @num_formats: Pointer to the returned number of formats + * + * Return: A newly allocated u32 array that stores all the DRM formats + * supported by the layer. The number of formats in the array is returned + * through the num_formats argument. + */ +static u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer, + unsigned int *num_formats) +{ + unsigned int i; + u32 *formats; + + formats = kcalloc(layer->info->num_formats, sizeof(*formats), + GFP_KERNEL); + if (!formats) + return NULL; + + for (i = 0; i < layer->info->num_formats; ++i) + formats[i] = layer->info->formats[i].drm_fmt; + + *num_formats = layer->info->num_formats; + return formats; +} + /** * zynqmp_disp_layer_enable - Enable a layer * @layer: The layer @@ -1220,31 +1247,27 @@ static const struct drm_plane_funcs zynqmp_disp_plane_funcs = { static int zynqmp_disp_create_planes(struct zynqmp_disp *disp) { - unsigned int i, j; + unsigned int i; int ret; for (i = 0; i < ARRAY_SIZE(disp->layers); i++) { struct zynqmp_disp_layer *layer = &disp->layers[i]; enum drm_plane_type type; - u32 *drm_formats; + unsigned int num_formats; + u32 *formats; - drm_formats = drmm_kcalloc(disp->drm, sizeof(*drm_formats), - layer->info->num_formats, - GFP_KERNEL); - if (!drm_formats) + formats = zynqmp_disp_layer_drm_formats(layer, &num_formats); + if (!formats) return -ENOMEM; - for (j = 0; j < layer->info->num_formats; ++j) - drm_formats[j] = layer->info->formats[j].drm_fmt; - /* Graphics layer is primary, and video layer is overlay. */ type = zynqmp_disp_layer_is_video(layer) ? DRM_PLANE_TYPE_OVERLAY : DRM_PLANE_TYPE_PRIMARY; ret = drm_universal_plane_init(disp->drm, &layer->plane, 0, &zynqmp_disp_plane_funcs, - drm_formats, - layer->info->num_formats, + formats, num_formats, NULL, type, NULL); + kfree(formats); if (ret) return ret; -- Regards, Laurent Pinchart
[PATCH v2 02/37] drm: xlnx: zynqmp_dpsub: Switch to atomic encoder enable/disable
To prepare for the transition to the DRM bridge API, switch the encoder operations to the atomic versions of .enable() and .disable(). This doesn't cause any functional change by itself. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 10 ++ 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index d14612b34796..10e469c92e73 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -1400,7 +1400,8 @@ zynqmp_dp_connector_helper_funcs = { * DRM Encoder */ -static void zynqmp_dp_encoder_enable(struct drm_encoder *encoder) +static void zynqmp_dp_encoder_atomic_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) { struct zynqmp_dp *dp = encoder_to_dp(encoder); unsigned int i; @@ -1432,7 +1433,8 @@ static void zynqmp_dp_encoder_enable(struct drm_encoder *encoder) zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 1); } -static void zynqmp_dp_encoder_disable(struct drm_encoder *encoder) +static void zynqmp_dp_encoder_atomic_disable(struct drm_encoder *encoder, +struct drm_atomic_state *state) { struct zynqmp_dp *dp = encoder_to_dp(encoder); @@ -1509,8 +1511,8 @@ zynqmp_dp_encoder_atomic_check(struct drm_encoder *encoder, } static const struct drm_encoder_helper_funcs zynqmp_dp_encoder_helper_funcs = { - .enable = zynqmp_dp_encoder_enable, - .disable= zynqmp_dp_encoder_disable, + .atomic_enable = zynqmp_dp_encoder_atomic_enable, + .atomic_disable = zynqmp_dp_encoder_atomic_disable, .atomic_mode_set= zynqmp_dp_encoder_atomic_mode_set, .atomic_check = zynqmp_dp_encoder_atomic_check, }; -- Regards, Laurent Pinchart
[PATCH v2 20/37] drm: xlnx: zynqmp_dpsub: Move audio clk from zynqmp_disp to zynqmp_dpsub
The audio clock is an external resource from the DPSUB point of view, not a resource internal to the display controller. Move it to the zynqmp_dpsub structure, to allow accessing it from outside the disp code. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 54 ++--- drivers/gpu/drm/xlnx/zynqmp_disp.h | 2 -- drivers/gpu/drm/xlnx/zynqmp_dp.c| 8 ++--- drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 54 +++-- drivers/gpu/drm/xlnx/zynqmp_dpsub.h | 7 5 files changed, 65 insertions(+), 60 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 62f9daf93465..640be60c4214 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -167,8 +167,6 @@ struct zynqmp_disp_layer { * @blend.base: Register I/O base address for the blender * @avbuf.base: Register I/O base address for the audio/video buffer manager * @audio.base: Registers I/O base address for the audio mixer - * @audio.clk: Audio clock - * @audio.clk_from_ps: True of the audio clock comes from PS, false from PL * @layers: Layers (planes) */ struct zynqmp_disp { @@ -186,8 +184,6 @@ struct zynqmp_disp { } avbuf; struct { void __iomem *base; - struct clk *clk; - bool clk_from_ps; } audio; struct zynqmp_disp_layer layers[ZYNQMP_DISP_NUM_LAYERS]; @@ -893,25 +889,6 @@ static void zynqmp_disp_audio_disable(struct zynqmp_disp *disp) ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST); } -static void zynqmp_disp_audio_init(struct zynqmp_disp *disp) -{ - /* Try the live PL audio clock. */ - disp->audio.clk = devm_clk_get(disp->dev, "dp_live_audio_aclk"); - if (!IS_ERR(disp->audio.clk)) { - disp->audio.clk_from_ps = false; - return; - } - - /* If the live PL audio clock is not valid, fall back to PS clock. */ - disp->audio.clk = devm_clk_get(disp->dev, "dp_aud_clk"); - if (!IS_ERR(disp->audio.clk)) { - disp->audio.clk_from_ps = true; - return; - } - - dev_err(disp->dev, "audio disabled due to missing clock\n"); -} - /* - * ZynqMP Display external functions for zynqmp_dp */ @@ -930,32 +907,6 @@ void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp) drm_crtc_handle_vblank(crtc); } -/** - * zynqmp_disp_audio_enabled - If the audio is enabled - * @disp: Display controller - * - * Return if the audio is enabled depending on the audio clock. - * - * Return: true if audio is enabled, or false. - */ -bool zynqmp_disp_audio_enabled(struct zynqmp_disp *disp) -{ - return !!disp->audio.clk; -} - -/** - * zynqmp_disp_get_audio_clk_rate - Get the current audio clock rate - * @disp: Display controller - * - * Return: the current audio clock rate. - */ -unsigned int zynqmp_disp_get_audio_clk_rate(struct zynqmp_disp *disp) -{ - if (zynqmp_disp_audio_enabled(disp)) - return 0; - return clk_get_rate(disp->audio.clk); -} - /** * zynqmp_disp_get_crtc_mask - Return the CRTC bit mask * @disp: Display controller @@ -1409,7 +1360,8 @@ static void zynqmp_disp_enable(struct zynqmp_disp *disp) zynqmp_disp_avbuf_enable(disp); /* Choose clock source based on the DT clock handle. */ zynqmp_disp_avbuf_set_clocks_sources(disp, disp->dpsub->vid_clk_from_ps, -disp->audio.clk_from_ps, true); +disp->dpsub->aud_clk_from_ps, +true); zynqmp_disp_avbuf_enable_channels(disp); zynqmp_disp_avbuf_enable_audio(disp); @@ -1670,8 +1622,6 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) if (IS_ERR(disp->audio.base)) return PTR_ERR(disp->audio.base); - zynqmp_disp_audio_init(disp); - ret = zynqmp_disp_create_layers(disp); if (ret) return ret; diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.h b/drivers/gpu/drm/xlnx/zynqmp_disp.h index f402901afb23..1b7f90a81857 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.h +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.h @@ -31,8 +31,6 @@ struct zynqmp_disp; struct zynqmp_dpsub; void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp); -bool zynqmp_disp_audio_enabled(struct zynqmp_disp *disp); -unsigned int zynqmp_disp_get_audio_clk_rate(struct zynqmp_disp *disp); uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp); int zynqmp_disp_drm_init(struct zynqmp_dpsub *dpsub); diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 33fd69ed7550..3e4d164d40fe 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -1252,7 +1252,7 @@ static void zynqmp_d
[PATCH v2 09/37] drm: xlnx: zynqmp_dpsub: Use DRM connector bridge helper
Replace the manual connector implementation and registration in the DP encoder with the DRM connector bridge helper. This removes boilerplate code and simplifies the driver. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c| 90 + drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 20 ++- 2 files changed, 20 insertions(+), 90 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 0aa810ebdeca..a18cb979be52 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -275,7 +274,6 @@ struct zynqmp_dp_config { /** * struct zynqmp_dp - Xilinx DisplayPort core - * @connector: the drm connector structure * @dev: device structure * @dpsub: Display subsystem * @drm: DRM core @@ -297,7 +295,6 @@ struct zynqmp_dp_config { * @train_set: set of training data */ struct zynqmp_dp { - struct drm_connector connector; struct device *dev; struct zynqmp_dpsub *dpsub; struct drm_device *drm; @@ -322,11 +319,6 @@ struct zynqmp_dp { u8 train_set[ZYNQMP_DP_MAX_LANES]; }; -static inline struct zynqmp_dp *connector_to_dp(struct drm_connector *connector) -{ - return container_of(connector, struct zynqmp_dp, connector); -} - static inline struct zynqmp_dp *bridge_to_dp(struct drm_bridge *bridge) { return container_of(bridge, struct zynqmp_dp, bridge); @@ -1285,33 +1277,15 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp, * DRM Bridge */ -static const struct drm_connector_funcs zynqmp_dp_connector_funcs; -static const struct drm_connector_helper_funcs zynqmp_dp_connector_helper_funcs; - static int zynqmp_dp_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) { struct zynqmp_dp *dp = bridge_to_dp(bridge); - struct drm_connector *connector = &dp->connector; int ret; - /* Create the DRM connector. */ - connector->polled = DRM_CONNECTOR_POLL_HPD; - ret = drm_connector_init(dp->drm, connector, -&zynqmp_dp_connector_funcs, -DRM_MODE_CONNECTOR_DisplayPort); - if (ret) { - dev_err(dp->dev, "failed to create the DRM connector\n"); - return ret; - } - - drm_connector_helper_add(connector, &zynqmp_dp_connector_helper_funcs); - drm_connector_register(connector); - drm_connector_attach_encoder(connector, bridge->encoder); - if (dp->next_bridge) { ret = drm_bridge_attach(bridge->encoder, dp->next_bridge, - bridge, DRM_BRIDGE_ATTACH_NO_CONNECTOR); + bridge, flags); if (ret < 0) return ret; } @@ -1532,68 +1506,6 @@ static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = { .get_edid = zynqmp_dp_bridge_get_edid, }; -/* - - * DRM Connector - */ - -static enum drm_connector_status -zynqmp_dp_connector_detect(struct drm_connector *connector, bool force) -{ - struct zynqmp_dp *dp = connector_to_dp(connector); - - return zynqmp_dp_bridge_detect(&dp->bridge); -} - -static int zynqmp_dp_connector_get_modes(struct drm_connector *connector) -{ - struct zynqmp_dp *dp = connector_to_dp(connector); - struct edid *edid; - int ret; - - edid = zynqmp_dp_bridge_get_edid(&dp->bridge, connector); - if (!edid) - return 0; - - drm_connector_update_edid_property(connector, edid); - ret = drm_add_edid_modes(connector, edid); - kfree(edid); - - return ret; -} - -static struct drm_encoder * -zynqmp_dp_connector_best_encoder(struct drm_connector *connector) -{ - struct zynqmp_dp *dp = connector_to_dp(connector); - - return &dp->dpsub->encoder; -} - -static int zynqmp_dp_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct zynqmp_dp *dp = connector_to_dp(connector); - - return zynqmp_dp_bridge_mode_valid(&dp->bridge, &connector->display_info, - mode); -} - -static const struct drm_connector_funcs zynqmp_dp_connector_funcs = { - .detect = zynqmp_dp_connector_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy= drm_connector_cleanup, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, - .reset = drm_atomic_helper_connector_reset, -}; - -static const struct drm_connector_helper_fun
[PATCH v2 24/37] drm: xlnx: zynqmp_dpsub: Move CRTC handling to zynqmp_kms.c
Decouple the CRTC handling from the display controller programming by moving the corresponding code from zynqmp_disp.c to zynqmp_kms.c. This prepares for using the DPSUB with a live video input, without creating a DRM CRTC in the DPSUB driver. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 251 +++-- drivers/gpu/drm/xlnx/zynqmp_disp.h | 21 ++- drivers/gpu/drm/xlnx/zynqmp_dp.c | 3 +- drivers/gpu/drm/xlnx/zynqmp_kms.c | 190 +- drivers/gpu/drm/xlnx/zynqmp_kms.h | 2 + 5 files changed, 232 insertions(+), 235 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 3b3aef42a390..7bf7e2113ef0 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -11,9 +11,7 @@ #include #include -#include #include -#include #include #include #include @@ -21,18 +19,14 @@ #include #include #include -#include #include -#include #include #include #include #include #include #include -#include -#include #include "zynqmp_disp.h" #include "zynqmp_disp_regs.h" @@ -89,16 +83,6 @@ struct zynqmp_disp_format { const u32 *sf; }; -/** - * enum zynqmp_disp_layer_id - Layer identifier - * @ZYNQMP_DISP_LAYER_VID: Video layer - * @ZYNQMP_DISP_LAYER_GFX: Graphics layer - */ -enum zynqmp_disp_layer_id { - ZYNQMP_DISP_LAYER_VID, - ZYNQMP_DISP_LAYER_GFX -}; - /** * enum zynqmp_disp_layer_mode - Layer mode * @ZYNQMP_DISP_LAYER_NONLIVE: non-live (memory) mode @@ -144,7 +128,7 @@ struct zynqmp_disp_layer_info { * @mode: Current operation mode */ struct zynqmp_disp_layer { - enum zynqmp_disp_layer_id id; + enum zynqmp_dpsub_layer_id id; struct zynqmp_disp *disp; const struct zynqmp_disp_layer_info *info; @@ -399,12 +383,12 @@ static void zynqmp_disp_avbuf_write(struct zynqmp_disp *disp, int reg, u32 val) static bool zynqmp_disp_layer_is_gfx(const struct zynqmp_disp_layer *layer) { - return layer->id == ZYNQMP_DISP_LAYER_GFX; + return layer->id == ZYNQMP_DPSUB_LAYER_GFX; } static bool zynqmp_disp_layer_is_video(const struct zynqmp_disp_layer *layer) { - return layer->id == ZYNQMP_DISP_LAYER_VID; + return layer->id == ZYNQMP_DPSUB_LAYER_VID; } /** @@ -883,35 +867,6 @@ static void zynqmp_disp_audio_disable(struct zynqmp_disp *disp) ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST); } -/* - - * ZynqMP Display external functions for zynqmp_dp - */ - -/** - * zynqmp_disp_handle_vblank - Handle the vblank event - * @disp: Display controller - * - * This function handles the vblank interrupt, and sends an event to - * CRTC object. This will be called by the DP vblank interrupt handler. - */ -void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp) -{ - struct drm_crtc *crtc = &disp->dpsub->crtc; - - drm_crtc_handle_vblank(crtc); -} - -/** - * zynqmp_disp_get_crtc_mask - Return the CRTC bit mask - * @disp: Display controller - * - * Return: the crtc mask of the zyqnmp_disp CRTC. - */ -uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp) -{ - return drm_crtc_mask(&disp->dpsub->crtc); -} - /* - * ZynqMP Display Layer & DRM Plane */ @@ -,7 +1066,7 @@ zynqmp_disp_plane_atomic_check(struct drm_plane *plane, false, false); } -static void +void zynqmp_disp_plane_atomic_disable(struct drm_plane *plane, struct drm_atomic_state *state) { @@ -1301,12 +1256,12 @@ static int zynqmp_disp_layer_request_dma(struct zynqmp_disp *disp, static int zynqmp_disp_create_layers(struct zynqmp_disp *disp) { static const struct zynqmp_disp_layer_info layer_info[] = { - [ZYNQMP_DISP_LAYER_VID] = { + [ZYNQMP_DPSUB_LAYER_VID] = { .formats = avbuf_vid_fmts, .num_formats = ARRAY_SIZE(avbuf_vid_fmts), .num_channels = 3, }, - [ZYNQMP_DISP_LAYER_GFX] = { + [ZYNQMP_DPSUB_LAYER_GFX] = { .formats = avbuf_gfx_fmts, .num_formats = ARRAY_SIZE(avbuf_gfx_fmts), .num_channels = 1, @@ -1336,14 +1291,14 @@ static int zynqmp_disp_create_layers(struct zynqmp_disp *disp) } /* - - * ZynqMP Display & DRM CRTC + * ZynqMP Display */ /** * zynqmp_disp_enable - Enable the display controller * @disp: Display controller */ -static void zynqmp_disp_enable(struct zynqmp_disp *disp) +void zynqmp_disp_enable(struct zynqmp_disp *disp) { zynqmp_disp_blend_set_output_format(disp, ZYNQMP_DPSUB_FORMA
[PATCH v2 28/37] drm: xlnx: zynqmp_dpsub: Manage DP and DISP allocations manually
The zynqmp_disp and zynqmp_dp structures are allocated with drmm_kzalloc(). While this simplifies management of memory, it requires a DRM device, which will not be available at probe time when the DP bridge will be used standalone, with a DRM device in the PL. To prepare for this, switch to manual allocation for zynqmp_disp and zynqmp_dp. The cleanup still uses the DRM managed infrastructure, but one level up, at the top level. This will be addressed separately. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 34 +++-- drivers/gpu/drm/xlnx/zynqmp_disp.h | 3 +-- drivers/gpu/drm/xlnx/zynqmp_dp.c| 30 +++-- drivers/gpu/drm/xlnx/zynqmp_dp.h| 3 +-- drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 17 +-- 5 files changed, 57 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 2df946f93575..b6fb168b6bc3 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -22,6 +21,7 @@ #include #include #include +#include #include "zynqmp_disp.h" #include "zynqmp_disp_regs.h" @@ -1225,7 +1225,7 @@ int zynqmp_disp_setup_clock(struct zynqmp_disp *disp, * Initialization & Cleanup */ -int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) +int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub) { struct platform_device *pdev = to_platform_device(dpsub->dev); struct zynqmp_disp *disp; @@ -1233,38 +1233,48 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) struct resource *res; int ret; - disp = drmm_kzalloc(drm, sizeof(*disp), GFP_KERNEL); + disp = kzalloc(sizeof(*disp), GFP_KERNEL); if (!disp) return -ENOMEM; disp->dev = &pdev->dev; disp->dpsub = dpsub; - dpsub->disp = disp; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "blend"); disp->blend.base = devm_ioremap_resource(disp->dev, res); - if (IS_ERR(disp->blend.base)) - return PTR_ERR(disp->blend.base); + if (IS_ERR(disp->blend.base)) { + ret = PTR_ERR(disp->blend.base); + goto error; + } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "av_buf"); disp->avbuf.base = devm_ioremap_resource(disp->dev, res); - if (IS_ERR(disp->avbuf.base)) - return PTR_ERR(disp->avbuf.base); + if (IS_ERR(disp->avbuf.base)) { + ret = PTR_ERR(disp->avbuf.base); + goto error; + } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aud"); disp->audio.base = devm_ioremap_resource(disp->dev, res); - if (IS_ERR(disp->audio.base)) - return PTR_ERR(disp->audio.base); + if (IS_ERR(disp->audio.base)) { + ret = PTR_ERR(disp->audio.base); + goto error; + } ret = zynqmp_disp_create_layers(disp); if (ret) - return ret; + goto error; layer = &disp->layers[ZYNQMP_DPSUB_LAYER_VID]; dpsub->dma_align = 1 << layer->dmas[0].chan->device->copy_align; + dpsub->disp = disp; + return 0; + +error: + kfree(disp); + return ret; } void zynqmp_disp_remove(struct zynqmp_dpsub *dpsub) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.h b/drivers/gpu/drm/xlnx/zynqmp_disp.h index 663f7d67c78f..9b8b202224d9 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.h +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.h @@ -25,7 +25,6 @@ #define ZYNQMP_DISP_MAX_DMA_BIT44 struct device; -struct drm_device; struct drm_format_info; struct drm_plane_state; struct platform_device; @@ -60,7 +59,7 @@ void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer, struct drm_plane_state *state); -int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm); +int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub); void zynqmp_disp_remove(struct zynqmp_dpsub *dpsub); #endif /* _ZYNQMP_DISP_H_ */ diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 0c7add926da3..001bc24f92bd 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -27,6 +26,7 @@ #include #include #include +#include #include "zynqmp_disp.h" #include "zynqmp_dp.h" @@ -1610,7 +1610,7 @@ static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data) * Initialization & Cleanup */ -int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) +int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub) { struct platform_d
[PATCH v2 06/37] drm: xlnx: zynqmp_dpsub: Move connector registration to bridge attach
Connector creation requires the DRM encoder, and it thus typically performed in the bridge attach operation. Move it there, to prepare for registration of the DRM bridge. For now the zynqmp_dp_bridge_attach() is called manually at initialization time. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 37 +--- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index ca566553e547..65b1ab4e4d2d 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -1292,9 +1292,30 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp, * DRM Bridge */ +static const struct drm_connector_funcs zynqmp_dp_connector_funcs; +static const struct drm_connector_helper_funcs zynqmp_dp_connector_helper_funcs; + static int zynqmp_dp_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) { + struct zynqmp_dp *dp = bridge_to_dp(bridge); + struct drm_connector *connector = &dp->connector; + int ret; + + /* Create the DRM connector. */ + connector->polled = DRM_CONNECTOR_POLL_HPD; + ret = drm_connector_init(dp->drm, connector, +&zynqmp_dp_connector_funcs, +DRM_MODE_CONNECTOR_DisplayPort); + if (ret) { + dev_err(dp->dev, "failed to create the DRM connector\n"); + return ret; + } + + drm_connector_helper_add(connector, &zynqmp_dp_connector_helper_funcs); + drm_connector_register(connector); + drm_connector_attach_encoder(connector, bridge->encoder); + return 0; } @@ -1711,7 +1732,6 @@ int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub) struct zynqmp_dp *dp = dpsub->dp; struct drm_bridge *bridge = &dp->bridge; struct drm_encoder *encoder = &dp->encoder; - struct drm_connector *connector = &dp->connector; int ret; dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK; @@ -1729,23 +1749,14 @@ int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub) | DRM_BRIDGE_OP_HPD; bridge->type = DRM_MODE_CONNECTOR_DisplayPort; - /* Create the DRM encoder and connector. */ + /* Create the DRM encoder and attach the bridge. */ encoder->possible_crtcs |= zynqmp_disp_get_crtc_mask(dpsub->disp); drm_simple_encoder_init(dp->drm, encoder, DRM_MODE_ENCODER_TMDS); drm_encoder_helper_add(encoder, &zynqmp_dp_encoder_helper_funcs); - connector->polled = DRM_CONNECTOR_POLL_HPD; - ret = drm_connector_init(encoder->dev, connector, -&zynqmp_dp_connector_funcs, -DRM_MODE_CONNECTOR_DisplayPort); - if (ret) { - dev_err(dp->dev, "failed to create the DRM connector\n"); + ret = zynqmp_dp_bridge_attach(bridge, 0); + if (ret < 0) return ret; - } - - drm_connector_helper_add(connector, &zynqmp_dp_connector_helper_funcs); - drm_connector_register(connector); - drm_connector_attach_encoder(connector, encoder); /* Initialize and register the AUX adapter. */ ret = zynqmp_dp_aux_init(dp); -- Regards, Laurent Pinchart
[PATCH v2 11/37] drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp.event field
The event field of the zynqmp_disp structure is unused. Drop it. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index cc32aa89cf8f..31b52f01c32d 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -172,7 +172,6 @@ struct zynqmp_disp_layer { * @audio.clk: Audio clock * @audio.clk_from_ps: True of the audio clock comes from PS, false from PL * @layers: Layers (planes) - * @event: Pending vblank event request * @pclk: Pixel clock * @pclk_from_ps: True of the video clock comes from PS, false from PL */ @@ -197,8 +196,6 @@ struct zynqmp_disp { struct zynqmp_disp_layer layers[ZYNQMP_DISP_NUM_LAYERS]; - struct drm_pending_vblank_event *event; - struct clk *pclk; bool pclk_from_ps; }; -- Regards, Laurent Pinchart
[PATCH v2 08/37] drm: xlnx: zynqmp_dpsub: Attach to the next bridge
The next component in the display chain, after the DP encoder, is most likely a DP connector. The display connector driver registers a bridge for it. That bridge doesn't need to be controlled, but is needed in order to use the DRM connector bridge helper. Retrieve it at init time, and attach to it in the DP bridge attach handler. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 18 ++ 1 file changed, 18 insertions(+) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 28f92b5d8385..0aa810ebdeca 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -283,6 +283,7 @@ struct zynqmp_dp_config { * @reset: reset controller * @irq: irq * @bridge: DRM bridge for the DP encoder + * @next_bridge: The downstream bridge * @config: IP core configuration from DTS * @aux: aux channel * @phy: PHY handles for DP lanes @@ -305,6 +306,7 @@ struct zynqmp_dp { int irq; struct drm_bridge bridge; + struct drm_bridge *next_bridge; struct zynqmp_dp_config config; struct drm_dp_aux aux; @@ -1307,6 +1309,13 @@ static int zynqmp_dp_bridge_attach(struct drm_bridge *bridge, drm_connector_register(connector); drm_connector_attach_encoder(connector, bridge->encoder); + if (dp->next_bridge) { + ret = drm_bridge_attach(bridge->encoder, dp->next_bridge, + bridge, DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret < 0) + return ret; + } + return 0; } @@ -1744,6 +1753,15 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) if (ret) goto err_reset; + /* +* Acquire the next bridge in the chain. Ignore errors caused by port@5 +* not being connected for backward-compatibility with older DTs. +*/ + ret = drm_of_find_panel_or_bridge(dp->dev->of_node, 5, 0, NULL, + &dp->next_bridge); + if (ret < 0 && ret != -ENODEV) + goto err_reset; + /* Initialize the hardware. */ zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN, ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL); -- Regards, Laurent Pinchart
[PATCH v2 13/37] drm: xlnx: zynqmp_dpsub: Don't pass CRTC to zynqmp_disp_setup_clock()
To prepare for usage of the clock setup function outside of the CRTC code, replace the DRM-specific structures passed as parameters with a pointer to the zynqmp_disp and the requested clock rate. This doesn't introduce any functional change. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 18 -- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index eae28a3c6d45..14ecc58f5470 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -1410,16 +1410,9 @@ static void zynqmp_disp_disable(struct zynqmp_disp *disp) zynqmp_disp_avbuf_disable(disp); } -static inline struct zynqmp_disp *crtc_to_disp(struct drm_crtc *crtc) +static int zynqmp_disp_setup_clock(struct zynqmp_disp *disp, + unsigned long mode_clock) { - return container_of(crtc, struct zynqmp_disp, crtc); -} - -static int zynqmp_disp_crtc_setup_clock(struct drm_crtc *crtc, - struct drm_display_mode *adjusted_mode) -{ - struct zynqmp_disp *disp = crtc_to_disp(crtc); - unsigned long mode_clock = adjusted_mode->clock * 1000; unsigned long rate; long diff; int ret; @@ -1444,6 +1437,11 @@ static int zynqmp_disp_crtc_setup_clock(struct drm_crtc *crtc, return 0; } +static inline struct zynqmp_disp *crtc_to_disp(struct drm_crtc *crtc) +{ + return container_of(crtc, struct zynqmp_disp, crtc); +} + static void zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state) @@ -1454,7 +1452,7 @@ zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc, pm_runtime_get_sync(disp->dev); - zynqmp_disp_crtc_setup_clock(crtc, adjusted_mode); + zynqmp_disp_setup_clock(disp, adjusted_mode->clock * 1000); ret = clk_prepare_enable(disp->pclk); if (ret) { -- Regards, Laurent Pinchart
[PATCH v2 05/37] drm: xlnx: zynqmp_dpsub: Don't access connector in zynqmp_dp_set_format()
To prepare for the removal of the connector from the DP encoder, pass the display info pointer to the zynqmp_dp_set_format() function instead of accessing the connector internally. The display info is NULL when the function is called at initialization time, as we have no display info at that point. This doesn't change the existing behaviour, given that the zynqmp_dp_set_format() was already handling this as a special case (the display info isn't initialized at init time and is all zeroes). Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 15 --- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 8acd5cfbbd1d..ca566553e547 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -1109,6 +1109,7 @@ static void zynqmp_dp_update_misc(struct zynqmp_dp *dp) /** * zynqmp_dp_set_format - Set the input format * @dp: DisplayPort IP core structure + * @info: Display info * @format: input format * @bpc: bits per component * @@ -1117,10 +1118,10 @@ static void zynqmp_dp_update_misc(struct zynqmp_dp *dp) * Return: 0 on success, or -EINVAL. */ static int zynqmp_dp_set_format(struct zynqmp_dp *dp, + const struct drm_display_info *info, enum zynqmp_dpsub_format format, unsigned int bpc) { - static const struct drm_display_info *display; struct zynqmp_dp_config *config = &dp->config; unsigned int num_colors; @@ -1153,12 +1154,11 @@ static int zynqmp_dp_set_format(struct zynqmp_dp *dp, return -EINVAL; } - display = &dp->connector.display_info; - if (display->bpc && bpc > display->bpc) { + if (info && info->bpc && bpc > info->bpc) { dev_warn(dp->dev, "downgrading requested %ubpc to display limit %ubpc\n", -bpc, display->bpc); - bpc = display->bpc; +bpc, info->bpc); + bpc = info->bpc; } config->misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_MASK; @@ -1353,7 +1353,8 @@ static void zynqmp_dp_bridge_atomic_enable(struct drm_bridge *bridge, adjusted_mode = &crtc_state->adjusted_mode; mode = &crtc_state->mode; - zynqmp_dp_set_format(dp, ZYNQMP_DPSUB_FORMAT_RGB, 8); + zynqmp_dp_set_format(dp, &connector->display_info, +ZYNQMP_DPSUB_FORMAT_RGB, 8); /* Check again as bpp or format might have been changed */ rate = zynqmp_dp_max_rate(dp->link_config.max_rate, @@ -1714,7 +1715,7 @@ int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub) int ret; dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK; - zynqmp_dp_set_format(dp, ZYNQMP_DPSUB_FORMAT_RGB, 8); + zynqmp_dp_set_format(dp, NULL, ZYNQMP_DPSUB_FORMAT_RGB, 8); /* * Initialize the bridge. Setting the device and encoder manually is a -- Regards, Laurent Pinchart
[PATCH v2 10/37] drm: xlnx: zynqmp_dpsub: Report HPD through the bridge
Now that the driver uses the connector bridge helper, HPD can be reported directly for the connector through the drm_bridge_hpd_notify() function. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 11 +-- 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index a18cb979be52..33fd69ed7550 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -1534,12 +1533,12 @@ void zynqmp_dp_disable_vblank(struct zynqmp_dp *dp) static void zynqmp_dp_hpd_work_func(struct work_struct *work) { - struct zynqmp_dp *dp; + struct zynqmp_dp *dp = container_of(work, struct zynqmp_dp, + hpd_work.work); + enum drm_connector_status status; - dp = container_of(work, struct zynqmp_dp, hpd_work.work); - - if (dp->drm) - drm_helper_hpd_irq_event(dp->drm); + status = zynqmp_dp_bridge_detect(&dp->bridge); + drm_bridge_hpd_notify(&dp->bridge, status); } static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data) -- Regards, Laurent Pinchart
[PATCH v2 03/37] drm: xlnx: zynqmp_dpsub: Constify mode argument to function
The zynqmp_dp_encoder_mode_set_transfer_unit() function takes a mode pointer argument that it doesn't need to modify. Make it const. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 10e469c92e73..a2d87543d637 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -1195,7 +1195,7 @@ static int zynqmp_dp_set_format(struct zynqmp_dp *dp, */ static void zynqmp_dp_encoder_mode_set_transfer_unit(struct zynqmp_dp *dp, -struct drm_display_mode *mode) +const struct drm_display_mode *mode) { u32 tu = ZYNQMP_DP_MSA_TRANSFER_UNIT_SIZE_TU_SIZE_DEF; u32 bw, vid_kbytes, avg_bytes_per_tu, init_wait; -- Regards, Laurent Pinchart
[PATCH v2 00/37] drm: xlnx: zynqmp_dpsub: Initial live video input support
Hello, The DPSUB is the DisplayPort subsystem, a set of hard IP cores found in the ZynqMP family of SoCs. It combines a DisplayPort encoder, a video blender with two input channels, and a DMA engine. The zynqmp_dpsub driver exposes this as a DRM device with one CRTC and two planes. In addition to those features, the DPSUB can interface with the programmable logic (PL) found in the ZynqMP SoC. Each input to the video blender can come from the PL instead of the DMA engine, and the blender output can also be routed to the PL. This creates a very configurable device that can accommodate lots of use cases, but it also makes it difficult to model it as a DRM/KMS device. This patch series implements initial support for live video inputs, by restricting the supported use cases to a single live video input. In that mode, the video blender is configured in pass-through mode, with the whole DPSUB essentially operating as a DisplayPort encoder only. The CRTC and plane functions are then implemented by IP cores in the PL. To support this, the series start with patch 01/37 to model the connections to the PL in DT using OF graph bindings. This fixes a historical mistake that forgot to model the connection to the DP connector in DT. With that in place, patches 02/37 to 10/37 refactor the driver to turn the DisplayPort encoder implementation, modelled as a DRM encoder, into a DRM bridge. Please see individual patches for details. The rework is internal only, simplifies the code by making use of the DRM bridge connector helper, but doesn't bring any functional change. Patches 11/37 to 31/37 continue refactoring of the driver, to cleanly separate the DRM planes, CRTC, encoder and connector from the DRM bridge implementation. The goal is to make the latter available to a DRM driver for the PL display pipeline without registering any DRM device in the DPSUB driver itself. Patches 32/37 to 35/37 implement this, reading information about the connection to the PL from the device tree to decide in which mode to operate. Finally, patch 36/37 and 37/37 update the ZynqMP core and ZCU106A board device tree files to create ports and connect the DPSUB to the DisplayPort connector. I have tested the whole series without these two patches to ensure that backward compatibility with older DT isn't broken. With this series applied, the DPSUB can be used as a DisplayPort encoder by a PL display pipeline. A careful reviewer may ask me where drivers for such a display pipelines are, and that would be a very good question. PL display pipelines are currently supported in the Xilinx downstream kernel only, which is something I want to address next. That road will be full of challenges, as in theory anything can be implemented in the PL, including pipelines that connect cameras and displays together. If anynoe is interested in discussing this topic, please let me know. Laurent Pinchart (37): dt-bindings: display: xlnx: zynqmp-dpsub: Add OF graph ports drm: xlnx: zynqmp_dpsub: Switch to atomic encoder enable/disable drm: xlnx: zynqmp_dpsub: Constify mode argument to function drm: xlnx: zynqmp_dpsub: Create DRM bridge to model DP encoder drm: xlnx: zynqmp_dpsub: Don't access connector in zynqmp_dp_set_format() drm: xlnx: zynqmp_dpsub: Move connector registration to bridge attach drm: xlnx: zynqmp_dpsub: Move encoder to DPSUB core drm: xlnx: zynqmp_dpsub: Attach to the next bridge drm: xlnx: zynqmp_dpsub: Use DRM connector bridge helper drm: xlnx: zynqmp_dpsub: Report HPD through the bridge drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp.event field drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp_format.bus_fmt field drm: xlnx: zynqmp_dpsub: Don't pass CRTC to zynqmp_disp_setup_clock() drm: xlnx: zynqmp_dpsub: Configure blender in zynqmp_disp_enable() drm: xlnx: zynqmp_dpsub: Use local variable in zynqmp_disp_layer_update() drm: xlnx: zynqmp_dpsub: Pass format info to zynqmp_disp_layer_set_format() drm: xlnx: zynqmp_dpsub: Remplace hardcoded values with ARRAY_SIZE() drm: xlnx: zynqmp_dpsub: Don't use drmm_kcalloc() for temporary data drm: xlnx: zynqmp_dpsub: Move pclk from zynqmp_disp to zynqmp_dpsub drm: xlnx: zynqmp_dpsub: Move audio clk from zynqmp_disp to zynqmp_dpsub drm: xlnx: zynqmp_dpsub: Move CRTC to zynqmp_dpsub structure drm: xlnx: zynqmp_dpsub: Move planes to zynqmp_dpsub structure drm: xlnx: zynqmp_dpsub: Move DRM/KMS initialization to separate file drm: xlnx: zynqmp_dpsub: Move CRTC handling to zynqmp_kms.c drm: xlnx: zynqmp_dpsub: Move planes handling to zynqmp_kms.c drm: xlnx: zynqmp_dpsub: Register AUX bus at bridge attach time drm: xlnx: zynqmp_dpsub: Move DP bridge init to zynqmp_dp_probe() drm: xlnx: zynqmp_dpsub: Manage DP and DISP allocations manually drm: xlnx: zynqmp_dpsub: Move all DRM init and cleanup to zynqmp_kms.c drm: xlnx: zynqmp_dpsub: Decouple DRM device from zynqmp_dpsub drm: xlnx: zynqmp_dpsub: Rename zynqmp_dpsub_handle_vblank
[PATCH v2 01/37] dt-bindings: display: xlnx: zynqmp-dpsub: Add OF graph ports
The DPSUB doesn't live in isolation, but is connected to the programmable logic for live inputs and outputs, and also has a DisplayPort output. Model all those using OF graph. Signed-off-by: Laurent Pinchart Reviewed-by: Rob Herring --- .../display/xlnx/xlnx,zynqmp-dpsub.yaml | 67 +++ 1 file changed, 67 insertions(+) diff --git a/Documentation/devicetree/bindings/display/xlnx/xlnx,zynqmp-dpsub.yaml b/Documentation/devicetree/bindings/display/xlnx/xlnx,zynqmp-dpsub.yaml index 10ec78ca1c65..554f9d5809d4 100644 --- a/Documentation/devicetree/bindings/display/xlnx/xlnx,zynqmp-dpsub.yaml +++ b/Documentation/devicetree/bindings/display/xlnx/xlnx,zynqmp-dpsub.yaml @@ -117,6 +117,45 @@ properties: - const: dp-phy0 - const: dp-phy1 + ports: +$ref: /schemas/graph.yaml#/properties/ports +description: | + Connections to the programmable logic and the DisplayPort PHYs. Each port + shall have a single endpoint. + +properties: + port@0: +$ref: /schemas/graph.yaml#/properties/port +description: The live video input from the programmable logic + + port@1: +$ref: /schemas/graph.yaml#/properties/port +description: The live graphics input from the programmable logic + + port@2: +$ref: /schemas/graph.yaml#/properties/port +description: The live audio input from the programmable logic + + port@3: +$ref: /schemas/graph.yaml#/properties/port +description: The blended video output to the programmable logic + + port@4: +$ref: /schemas/graph.yaml#/properties/port +description: The mixed audio output to the programmable logic + + port@5: +$ref: /schemas/graph.yaml#/properties/port +description: The DisplayPort output + +required: + - port@0 + - port@1 + - port@2 + - port@3 + - port@4 + - port@5 + required: - compatible - reg @@ -130,6 +169,7 @@ required: - dma-names - phys - phy-names + - ports additionalProperties: false @@ -164,6 +204,33 @@ examples: <&psgtr 0 PHY_TYPE_DP 1 3>; phy-names = "dp-phy0", "dp-phy1"; + +ports { +#address-cells = <1>; +#size-cells = <0>; + +port@0 { +reg = <0>; +}; +port@1 { +reg = <1>; +}; +port@2 { +reg = <2>; +}; +port@3 { +reg = <3>; +}; +port@4 { +reg = <4>; +}; +port@5 { +reg = <5>; +dpsub_dp_out: endpoint { +remote-endpoint = <&dp_connector>; +}; +}; +}; }; ... -- Regards, Laurent Pinchart
Re: [PATCH v2 01/16] slab: Remove __malloc attribute from realloc functions
On 9/28/22 19:13, Kees Cook wrote: On Wed, Sep 28, 2022 at 09:26:15AM +0200, Geert Uytterhoeven wrote: Hi Kees, On Fri, Sep 23, 2022 at 10:35 PM Kees Cook wrote: The __malloc attribute should not be applied to "realloc" functions, as the returned pointer may alias the storage of the prior pointer. Instead of splitting __malloc from __alloc_size, which would be a huge amount of churn, just create __realloc_size for the few cases where it is needed. Additionally removes the conditional test for __alloc_size__, which is always defined now. Cc: Christoph Lameter Cc: Pekka Enberg Cc: David Rientjes Cc: Joonsoo Kim Cc: Andrew Morton Cc: Vlastimil Babka Cc: Roman Gushchin Cc: Hyeonggon Yoo <42.hye...@gmail.com> Cc: Marco Elver Cc: linux...@kvack.org Signed-off-by: Kees Cook Thanks for your patch, which is now commit 63caa04ec60583b1 ("slab: Remove __malloc attribute from realloc functions") in next-20220927. nore...@ellerman.id.au reported all gcc8-based builds to fail (e.g. [1], more at [2]): In file included from : ./include/linux/percpu.h: In function ‘__alloc_reserved_percpu’: ././include/linux/compiler_types.h:279:30: error: expected declaration specifiers before ‘__alloc_size__’ #define __alloc_size(x, ...) __alloc_size__(x, ## __VA_ARGS__) __malloc ^~ ./include/linux/percpu.h:120:74: note: in expansion of macro ‘__alloc_size’ [...] It's building fine with e.g. gcc-9 (which is my usual m68k cross-compiler). Reverting this commit on next-20220927 fixes the issue. [1] http://kisskb.ellerman.id.au/kisskb/buildresult/14803908/ [2] http://kisskb.ellerman.id.au/kisskb/head/1bd8b75fe6adeaa89d02968bdd811ffe708cf839/ Eek! Thanks for letting me know. I'm confused about this -- __alloc_size__ wasn't optional in compiler_attributes.h -- but obviously I broke something! I'll go figure this out. Even in latest next I can see at the end of include/linux/compiler-gcc.h /* * Prior to 9.1, -Wno-alloc-size-larger-than (and therefore the "alloc_size" * attribute) do not work, and must be disabled. */ #if GCC_VERSION < 90100 #undef __alloc_size__ #endif -Kees
Re: [PATCH 6/7] nouveau/dmem: Evict device private memory during release
Re comments about infinite retry: gotcha, makes sense to me. On Tue, 2022-09-27 at 09:45 +1000, Alistair Popple wrote: > John Hubbard writes: > > > On 9/26/22 14:35, Lyude Paul wrote: > > > > + for (i = 0; i < npages; i++) { > > > > + if (src_pfns[i] & MIGRATE_PFN_MIGRATE) { > > > > + struct page *dpage; > > > > + > > > > + /* > > > > +* _GFP_NOFAIL because the GPU is going away > > > > and there > > > > +* is nothing sensible we can do if we can't > > > > copy the > > > > +* data back. > > > > +*/ > > > > > > You'll have to excuse me for a moment since this area of nouveau isn't > > > one of > > > my strongpoints, but are we sure about this? IIRC __GFP_NOFAIL means > > > infinite > > > retry, in the case of a GPU hotplug event I would assume we would rather > > > just > > > stop trying to migrate things to the GPU and just drop the data instead of > > > hanging on infinite retries. > > > > > No problem, thanks for taking a look! > > > Hi Lyude! > > > > Actually, I really think it's better in this case to keep trying > > (presumably not necessarily infinitely, but only until memory becomes > > available), rather than failing out and corrupting data. > > > > That's because I'm not sure it's completely clear that this memory is > > discardable. And at some point, we're going to make this all work with > > file-backed memory, which will *definitely* not be discardable--I > > realize that we're not there yet, of course. > > > > But here, it's reasonable to commit to just retrying indefinitely, > > really. Memory should eventually show up. And if it doesn't, then > > restarting the machine is better than corrupting data, generally. > > The memory is definitely not discardable here if the migration failed > because that implies it is still mapped into some userspace process. > > We could avoid restarting the machine by doing something similar to what > happens during memory failure and killing every process that maps the > page(s). But overall I think it's better to retry until memory is > available, because that allows things like reclaim to work and in the > worst case allows the OOM killer to select an appropriate task to kill. > It also won't cause data corruption if/when we have file-backed memory. > > > thanks, > -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat
Re: [PATCH v2 7/8] nouveau/dmem: Evict device private memory during release
Reviewed-by: Lyude Paul On Wed, 2022-09-28 at 22:01 +1000, Alistair Popple wrote: > When the module is unloaded or a GPU is unbound from the module it is > possible for device private pages to still be mapped in currently > running processes. This can lead to a hangs and RCU stall warnings when > unbinding the device as memunmap_pages() will wait in an uninterruptible > state until all device pages have been freed which may never happen. > > Fix this by migrating device mappings back to normal CPU memory prior to > freeing the GPU memory chunks and associated device private pages. > > Signed-off-by: Alistair Popple > Cc: Lyude Paul > Cc: Ben Skeggs > Cc: Ralph Campbell > Cc: John Hubbard > --- > drivers/gpu/drm/nouveau/nouveau_dmem.c | 48 +++- > 1 file changed, 48 insertions(+) > > diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c > b/drivers/gpu/drm/nouveau/nouveau_dmem.c > index 65f51fb..5fe2091 100644 > --- a/drivers/gpu/drm/nouveau/nouveau_dmem.c > +++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c > @@ -367,6 +367,52 @@ nouveau_dmem_suspend(struct nouveau_drm *drm) > mutex_unlock(&drm->dmem->mutex); > } > > +/* > + * Evict all pages mapping a chunk. > + */ > +static void > +nouveau_dmem_evict_chunk(struct nouveau_dmem_chunk *chunk) > +{ > + unsigned long i, npages = range_len(&chunk->pagemap.range) >> > PAGE_SHIFT; > + unsigned long *src_pfns, *dst_pfns; > + dma_addr_t *dma_addrs; > + struct nouveau_fence *fence; > + > + src_pfns = kcalloc(npages, sizeof(*src_pfns), GFP_KERNEL); > + dst_pfns = kcalloc(npages, sizeof(*dst_pfns), GFP_KERNEL); > + dma_addrs = kcalloc(npages, sizeof(*dma_addrs), GFP_KERNEL); > + > + migrate_device_range(src_pfns, chunk->pagemap.range.start >> PAGE_SHIFT, > + npages); > + > + for (i = 0; i < npages; i++) { > + if (src_pfns[i] & MIGRATE_PFN_MIGRATE) { > + struct page *dpage; > + > + /* > + * _GFP_NOFAIL because the GPU is going away and there > + * is nothing sensible we can do if we can't copy the > + * data back. > + */ > + dpage = alloc_page(GFP_HIGHUSER | __GFP_NOFAIL); > + dst_pfns[i] = migrate_pfn(page_to_pfn(dpage)); > + nouveau_dmem_copy_one(chunk->drm, > + migrate_pfn_to_page(src_pfns[i]), dpage, > + &dma_addrs[i]); > + } > + } > + > + nouveau_fence_new(chunk->drm->dmem->migrate.chan, false, &fence); > + migrate_device_pages(src_pfns, dst_pfns, npages); > + nouveau_dmem_fence_done(&fence); > + migrate_device_finalize(src_pfns, dst_pfns, npages); > + kfree(src_pfns); > + kfree(dst_pfns); > + for (i = 0; i < npages; i++) > + dma_unmap_page(chunk->drm->dev->dev, dma_addrs[i], PAGE_SIZE, > DMA_BIDIRECTIONAL); > + kfree(dma_addrs); > +} > + > void > nouveau_dmem_fini(struct nouveau_drm *drm) > { > @@ -378,8 +424,10 @@ nouveau_dmem_fini(struct nouveau_drm *drm) > mutex_lock(&drm->dmem->mutex); > > list_for_each_entry_safe(chunk, tmp, &drm->dmem->chunks, list) { > + nouveau_dmem_evict_chunk(chunk); > nouveau_bo_unpin(chunk->bo); > nouveau_bo_ref(NULL, &chunk->bo); > + WARN_ON(chunk->callocated); > list_del(&chunk->list); > memunmap_pages(&chunk->pagemap); > release_mem_region(chunk->pagemap.range.start, -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat
Re: [PATCH 6/7] nouveau/dmem: Evict device private memory during release
On Tue, 2022-09-27 at 11:39 +1000, Alistair Popple wrote: > Felix Kuehling writes: > > > On 2022-09-26 17:35, Lyude Paul wrote: > > > On Mon, 2022-09-26 at 16:03 +1000, Alistair Popple wrote: > > > > When the module is unloaded or a GPU is unbound from the module it is > > > > possible for device private pages to be left mapped in currently running > > > > processes. This leads to a kernel crash when the pages are either freed > > > > or accessed from the CPU because the GPU and associated data structures > > > > and callbacks have all been freed. > > > > > > > > Fix this by migrating any mappings back to normal CPU memory prior to > > > > freeing the GPU memory chunks and associated device private pages. > > > > > > > > Signed-off-by: Alistair Popple > > > > > > > > --- > > > > > > > > I assume the AMD driver might have a similar issue. However I can't see > > > > where device private (or coherent) pages actually get unmapped/freed > > > > during teardown as I couldn't find any relevant calls to > > > > devm_memunmap(), memunmap(), devm_release_mem_region() or > > > > release_mem_region(). So it appears that ZONE_DEVICE pages are not being > > > > properly freed during module unload, unless I'm missing something? > > > I've got no idea, will poke Ben to see if they know the answer to this > > > > I guess we're relying on devm to release the region. Isn't the whole point > > of > > using devm_request_free_mem_region that we don't have to remember to > > explicitly > > release it when the device gets destroyed? I believe we had an explicit free > > call at some point by mistake, and that caused a double-free during module > > unload. See this commit for reference: > > Argh, thanks for that pointer. I was not so familiar with > devm_request_free_mem_region()/devm_memremap_pages() as currently > Nouveau explicitly manages that itself. Mhm, TBH I feel like this was going to happen eventually anyway but there's another reason for nouveau to start using the managed versions of these functions at some point > > > commit 22f4f4faf337d5fb2d2750aff13215726814273e > > Author: Philip Yang > > Date: Mon Sep 20 17:25:52 2021 -0400 > > > > drm/amdkfd: fix svm_migrate_fini warning > > Device manager releases device-specific resources when a driver > > disconnects from a device, devm_memunmap_pages and > > devm_release_mem_region calls in svm_migrate_fini are redundant. > > It causes below warning trace after patch "drm/amdgpu: Split > > amdgpu_device_fini into early and late", so remove function > > svm_migrate_fini. > > BUG: https://gitlab.freedesktop.org/drm/amd/-/issues/1718 > > WARNING: CPU: 1 PID: 3646 at drivers/base/devres.c:795 > > devm_release_action+0x51/0x60 > > Call Trace: > > ? memunmap_pages+0x360/0x360 > > svm_migrate_fini+0x2d/0x60 [amdgpu] > > kgd2kfd_device_exit+0x23/0xa0 [amdgpu] > > amdgpu_amdkfd_device_fini_sw+0x1d/0x30 [amdgpu] > > amdgpu_device_fini_sw+0x45/0x290 [amdgpu] > > amdgpu_driver_release_kms+0x12/0x30 [amdgpu] > > drm_dev_release+0x20/0x40 [drm] > > release_nodes+0x196/0x1e0 > > device_release_driver_internal+0x104/0x1d0 > > driver_detach+0x47/0x90 > > bus_remove_driver+0x7a/0xd0 > > pci_unregister_driver+0x3d/0x90 > > amdgpu_exit+0x11/0x20 [amdgpu] > > Signed-off-by: Philip Yang > > Reviewed-by: Felix Kuehling > > Signed-off-by: Alex Deucher > > > > Furthermore, I guess we are assuming that nobody is using the GPU when the > > module is unloaded. As long as any processes have /dev/kfd open, you won't > > be > > able to unload the module (except by force-unload). I suppose with > > ZONE_DEVICE > > memory, we can have references to device memory pages even when user mode > > has > > closed /dev/kfd. We do have a cleanup handler that runs in an > > MMU-free-notifier. > > In theory that should run after all the pages in the mm_struct have been > > freed. > > It releases all sorts of other device resources and needs the driver to > > still be > > there. I'm not sure if there is anything preventing a module unload before > > the > > free-notifier runs. I'll look into that. > > Right - module unload (or device unbind) is one of the other ways we can > hit this issue in Nouveau at least. You can end up with ZONE_DEVICE > pages mapped in a running process after the module has unloaded. > Although now you mention it that seems a bit wrong - the pgmap refcount > should provide some protection against that. Will have to look into > that too. > > > Regards, > > Felix > > > > > > > > > > > --- > > > > drivers/gpu/drm/nouveau/nouveau_dmem.c | 48 > > > > +++- > > > > 1 file changed, 48 insertions(+) > > > > > > > > diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c > > > > b/drivers/gpu/drm/nouveau/nouveau_dmem.c > > > > index 66ebbd4..3b247b8 100644 > > >
Re: [PATCH] drm/i915/gt: Perf_limit_reasons are only available for Gen11+
On Wed, Sep 28, 2022 at 12:02:12PM -0700, Ashutosh Dixit wrote: > Register GT0_PERF_LIMIT_REASONS (0x1381a8) is available only for > Gen11+. Therefore ensure perf_limit_reasons sysfs files are created only > for Gen11+. Otherwise on Gen < 5 accessing these files results in the > following oops: > > <1> [88.829420] BUG: unable to handle page fault for address: c9bb81a8 > <1> [88.829438] #PF: supervisor read access in kernel mode > <1> [88.829447] #PF: error_code(0x) - not-present page > > This patch is a backport of the drm-tip commit 0d2d201095e9 to > drm-intel-fixes. The backport is not identical to 0d2d201095e9, it only > includes the sysfs portions of 0d2d201095e9. The debugfs portion of > 0d2d201095e9 is not available in drm-intel-fixes so has not been > backported. Thanks for the backport. While pushing it, I adjusted the commit message to make checkpatch happier and to also ensure we included the previous reviews and patchwork link to the backport. > > Bspec: 20008 > Bug: https://gitlab.freedesktop.org/drm/intel/-/issues/6863 > Fixes: fa68bff7cf27 ("drm/i915/gt: Add sysfs throttle frequency interfaces") > Signed-off-by: Ashutosh Dixit > --- > drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c | 15 +++ > 1 file changed, 11 insertions(+), 4 deletions(-) > > diff --git a/drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c > b/drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c > index 73a8b46e0234..d09a0e845d09 100644 > --- a/drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c > +++ b/drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c > @@ -545,8 +545,7 @@ static INTEL_GT_RPS_BOOL_ATTR_RO(throttle_reason_ratl, > RATL_MASK); > static INTEL_GT_RPS_BOOL_ATTR_RO(throttle_reason_vr_thermalert, > VR_THERMALERT_MASK); > static INTEL_GT_RPS_BOOL_ATTR_RO(throttle_reason_vr_tdc, VR_TDC_MASK); > > -static const struct attribute *freq_attrs[] = { > - &dev_attr_punit_req_freq_mhz.attr, > +static const struct attribute *throttle_reason_attrs[] = { > &attr_throttle_reason_status.attr, > &attr_throttle_reason_pl1.attr, > &attr_throttle_reason_pl2.attr, > @@ -763,12 +762,20 @@ void intel_gt_sysfs_pm_init(struct intel_gt *gt, struct > kobject *kobj) > if (!is_object_gt(kobj)) > return; > > - ret = sysfs_create_files(kobj, freq_attrs); > + ret = sysfs_create_file(kobj, &dev_attr_punit_req_freq_mhz.attr); > if (ret) > drm_warn(>->i915->drm, > - "failed to create gt%u throttle sysfs files (%pe)", > + "failed to create gt%u punit_req_freq_mhz sysfs (%pe)", >gt->info.id, ERR_PTR(ret)); > > + if (GRAPHICS_VER(gt->i915) >= 11) { > + ret = sysfs_create_files(kobj, throttle_reason_attrs); > + if (ret) > + drm_warn(>->i915->drm, > + "failed to create gt%u throttle sysfs files > (%pe)", > + gt->info.id, ERR_PTR(ret)); > + } > + > if (HAS_MEDIA_RATIO_MODE(gt->i915) && intel_uc_uses_guc_slpc(>->uc)) { > ret = sysfs_create_files(kobj, media_perf_power_attrs); > if (ret) > -- > 2.34.1 >
Re: [Intel-gfx] [PATCH 05/16] drm/i915/vm_bind: Implement bind and unbind of object
On 9/27/2022 11:19 PM, Niranjana Vishwanathapura wrote: Add uapi and implement support for bind and unbind of an object at the specified GPU virtual addresses. The vm_bind mode is not supported in legacy execbuf2 ioctl. It will be supported only in the newer execbuf3 ioctl. Signed-off-by: Niranjana Vishwanathapura Signed-off-by: Prathap Kumar Valsan Signed-off-by: Andi Shyti --- drivers/gpu/drm/i915/Makefile | 1 + .../gpu/drm/i915/gem/i915_gem_execbuffer.c| 5 + drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h | 26 ++ .../drm/i915/gem/i915_gem_vm_bind_object.c| 306 ++ drivers/gpu/drm/i915/gt/intel_gtt.c | 10 + drivers/gpu/drm/i915/gt/intel_gtt.h | 17 + drivers/gpu/drm/i915/i915_driver.c| 3 + drivers/gpu/drm/i915/i915_vma.c | 1 + drivers/gpu/drm/i915/i915_vma_types.h | 14 + include/uapi/drm/i915_drm.h | 112 +++ 10 files changed, 495 insertions(+) create mode 100644 drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h create mode 100644 drivers/gpu/drm/i915/gem/i915_gem_vm_bind_object.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index a26edcdadc21..9bf939ef18ea 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -166,6 +166,7 @@ gem-y += \ gem/i915_gem_ttm_move.o \ gem/i915_gem_ttm_pm.o \ gem/i915_gem_userptr.o \ + gem/i915_gem_vm_bind_object.o \ gem/i915_gem_wait.o \ gem/i915_gemfs.o i915-y += \ diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index cd75b0ca2555..f85f10cf9c34 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -781,6 +781,11 @@ static int eb_select_context(struct i915_execbuffer *eb) if (unlikely(IS_ERR(ctx))) return PTR_ERR(ctx); + if (ctx->vm->vm_bind_mode) { + i915_gem_context_put(ctx); + return -EOPNOTSUPP; + } + eb->gem_context = ctx; if (i915_gem_context_has_full_ppgtt(ctx)) eb->invalid_flags |= EXEC_OBJECT_NEEDS_GTT; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h b/drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h new file mode 100644 index ..36262a6357b5 --- /dev/null +++ b/drivers/gpu/drm/i915/gem/i915_gem_vm_bind.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2022 Intel Corporation + */ + +#ifndef __I915_GEM_VM_BIND_H +#define __I915_GEM_VM_BIND_H + +#include + +struct drm_device; +struct drm_file; +struct i915_address_space; +struct i915_vma; + +struct i915_vma * +i915_gem_vm_bind_lookup_vma(struct i915_address_space *vm, u64 va); + +int i915_gem_vm_bind_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); +int i915_gem_vm_unbind_ioctl(struct drm_device *dev, void *data, +struct drm_file *file); + +void i915_gem_vm_unbind_all(struct i915_address_space *vm); + +#endif /* __I915_GEM_VM_BIND_H */ diff --git a/drivers/gpu/drm/i915/gem/i915_gem_vm_bind_object.c b/drivers/gpu/drm/i915/gem/i915_gem_vm_bind_object.c new file mode 100644 index ..e529162abd2c --- /dev/null +++ b/drivers/gpu/drm/i915/gem/i915_gem_vm_bind_object.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2022 Intel Corporation + */ + +#include + +#include + +#include "gem/i915_gem_context.h" +#include "gem/i915_gem_vm_bind.h" + +#include "gt/intel_gpu_commands.h" + +#define START(node) ((node)->start) +#define LAST(node) ((node)->last) + +INTERVAL_TREE_DEFINE(struct i915_vma, rb, u64, __subtree_last, +START, LAST, static inline, i915_vm_bind_it) + +#undef START +#undef LAST + +/** + * DOC: VM_BIND/UNBIND ioctls + * + * DRM_I915_GEM_VM_BIND/UNBIND ioctls allows UMD to bind/unbind GEM buffer + * objects (BOs) or sections of a BOs at specified GPU virtual addresses on a + * specified address space (VM). Multiple mappings can map to the same physical + * pages of an object (aliasing). These mappings (also referred to as persistent + * mappings) will be persistent across multiple GPU submissions (execbuf calls) + * issued by the UMD, without user having to provide a list of all required + * mappings during each submission (as required by older execbuf mode). + * + * The VM_BIND/UNBIND calls allow UMDs to request a timeline out fence for + * signaling the completion of bind/unbind operation. + * + * VM_BIND feature is advertised to user via I915_PARAM_VM_BIND_VERSION. + * User has to opt-in for VM_BIND mode of binding for an address space (VM) + * during VM creation time via I915_VM_CREATE_FLAGS_USE_VM_BIND extension. + * + * VM_BIND/UNBIND ioctl calls executed on different CPU threads concurrently + * are not ordered. Furthermore, parts of the VM_BIND/UNBIND operations can be + * d
[RFC PATCH v5 0/6] Add DSS support for AM625 SoC
This patch series adds a new compatible for the Display SubSyetem controller on TI's AM625 SoC. It further adds the required support for the same in the tidss driver. The AM625-DSS is a newer version of the DSS from the AM65X version with the major change being the addition of another OLDI TX. With the help of 2 OLDI TXes, the AM625 DSS supports OLDI displays with a resolution of upto 2K. This patch set further adds support for the OLDI on AM625, and is, in essence, the second version of the following patch series: https://patchwork.kernel.org/project/dri-devel/list/?series=660970&state=%2A&archive=both The changes in the above-mentioned series forced some re-works in this series, and are better reviewed as a single set. TODO: - Support for OLDI Bridges that work on clone / dual-link modes is yet to be added. - The pixel clock for the OLDI VP passes through a clock divider, which was being explicitly set in previous series, but that was not the right way. That patch has been dropped and a newer implementation is in works. Note: - Due to lack of hardware, only dual-link mode has been tested. Changelog: V5: - Rebase for current merge window - Add max DT ports in DSS features - Combine the OLDI support series (Changes from OLDI support series v1) - Address Tomi Valkeinen's comments 1. Update the OLDI link detection approach 2. Add port #3 for 2nd OLDI TX 3. Configure 2 panel-bridges for cloned panels 4. Drop the OLDI clock set patch 5. Drop rgb565-to-888 patch V4: - Rebase for current merge window - Add acked and reviewed by tags V3: - Change yaml enum in alphabetical order - Correct a typo V2: - Remove redundant regsiter array Aradhya Bhatia (6): dt-bindings: display: ti,am65x-dss: Add am625 dss compatible dt-bindings: display: ti: am65x-dss: Add new port for am625-dss drm/tidss: Add support for AM625 DSS drm/tidss: Add support to configure OLDI mode for am625-dss. drm/tidss: Add IO CTRL and Power support for OLDI TX in am625 drm/tidss: Enable Dual and Duplicate Modes for OLDI .../bindings/display/ti/ti,am65x-dss.yaml | 22 ++- drivers/gpu/drm/tidss/tidss_dispc.c | 155 -- drivers/gpu/drm/tidss/tidss_dispc.h | 11 ++ drivers/gpu/drm/tidss/tidss_dispc_regs.h | 6 + drivers/gpu/drm/tidss/tidss_drv.c | 1 + drivers/gpu/drm/tidss/tidss_drv.h | 3 + drivers/gpu/drm/tidss/tidss_kms.c | 146 ++--- 7 files changed, 304 insertions(+), 40 deletions(-) -- 2.37.0
[RFC PATCH v5 5/6] drm/tidss: Add IO CTRL and Power support for OLDI TX in am625
The ctrl mmr module of the AM625 is different from the AM65X SoC. Thus the ctrl mmr registers that supported the OLDI TX power have become different in AM625 SoC. Add IO CTRL support and control the OLDI TX power for AM625. Signed-off-by: Aradhya Bhatia --- drivers/gpu/drm/tidss/tidss_dispc.c | 55 ++-- drivers/gpu/drm/tidss/tidss_dispc_regs.h | 6 +++ 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c index 88008ad39b55..68444e0cd8d7 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.c +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -921,21 +921,52 @@ int dispc_vp_bus_check(struct dispc_device *dispc, u32 hw_videoport, static void dispc_oldi_tx_power(struct dispc_device *dispc, bool power) { - u32 val = power ? 0 : OLDI_PWRDN_TX; + u32 val; if (WARN_ON(!dispc->oldi_io_ctrl)) return; - regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT0_IO_CTRL, - OLDI_PWRDN_TX, val); - regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT1_IO_CTRL, - OLDI_PWRDN_TX, val); - regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT2_IO_CTRL, - OLDI_PWRDN_TX, val); - regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT3_IO_CTRL, - OLDI_PWRDN_TX, val); - regmap_update_bits(dispc->oldi_io_ctrl, OLDI_CLK_IO_CTRL, - OLDI_PWRDN_TX, val); + if (dispc->feat->subrev == DISPC_AM65X) { + val = power ? 0 : OLDI_PWRDN_TX; + + regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT0_IO_CTRL, + OLDI_PWRDN_TX, val); + regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT1_IO_CTRL, + OLDI_PWRDN_TX, val); + regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT2_IO_CTRL, + OLDI_PWRDN_TX, val); + regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT3_IO_CTRL, + OLDI_PWRDN_TX, val); + regmap_update_bits(dispc->oldi_io_ctrl, OLDI_CLK_IO_CTRL, + OLDI_PWRDN_TX, val); + + } else if (dispc->feat->subrev == DISPC_AM625) { + if (power) { + switch (dispc->oldi_mode) { + case OLDI_SINGLE_LINK_SINGLE_MODE: + /* Power down OLDI TX 1 */ + val = OLDI1_PWRDN_TX; + break; + + case OLDI_SINGLE_LINK_CLONE_MODE: + case OLDI_DUAL_LINK_MODE: + /* No Power down */ + val = 0; + break; + + default: + /* Power down both the OLDI TXes */ + val = OLDI0_PWRDN_TX | OLDI1_PWRDN_TX; + break; + } + } else { + /* Power down both the OLDI TXes */ + val = OLDI0_PWRDN_TX | OLDI1_PWRDN_TX; + } + + regmap_update_bits(dispc->oldi_io_ctrl, OLDI_PD_CTRL, + OLDI0_PWRDN_TX | OLDI1_PWRDN_TX, val); + } } static void dispc_set_num_datalines(struct dispc_device *dispc, @@ -2831,7 +2862,7 @@ int dispc_init(struct tidss_device *tidss) dispc->vp_data[i].gamma_table = gamma_table; } - if (feat->subrev == DISPC_AM65X) { + if (feat->subrev == DISPC_AM65X || feat->subrev == DISPC_AM625) { r = dispc_init_am65x_oldi_io_ctrl(dev, dispc); if (r) return r; diff --git a/drivers/gpu/drm/tidss/tidss_dispc_regs.h b/drivers/gpu/drm/tidss/tidss_dispc_regs.h index 13feedfe5d6d..510bee70b3b8 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc_regs.h +++ b/drivers/gpu/drm/tidss/tidss_dispc_regs.h @@ -238,6 +238,12 @@ enum dispc_common_regs { #define OLDI_DAT3_IO_CTRL 0x0C #define OLDI_CLK_IO_CTRL 0x10 +/* Only for AM625 OLDI TX */ +#define OLDI_PD_CTRL 0x100 +#define OLDI_LB_CTRL 0x104 + #define OLDI_PWRDN_TX BIT(8) +#define OLDI0_PWRDN_TX BIT(0) +#define OLDI1_PWRDN_TX BIT(1) #endif /* __TIDSS_DISPC_REGS_H */ -- 2.37.0
[PATCH v6 11/21] misc: fastrpc: Prepare to dynamic dma-buf locking specification
Prepare fastrpc to the common dynamic dma-buf locking convention by starting to use the unlocked versions of dma-buf API functions. Acked-by: Christian König Acked-by: Srinivas Kandagatla Signed-off-by: Dmitry Osipenko --- drivers/misc/fastrpc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 7ff0b63c25e3..1ad580865525 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -310,8 +310,8 @@ static void fastrpc_free_map(struct kref *ref) return; } } - dma_buf_unmap_attachment(map->attach, map->table, -DMA_BIDIRECTIONAL); + dma_buf_unmap_attachment_unlocked(map->attach, map->table, + DMA_BIDIRECTIONAL); dma_buf_detach(map->buf, map->attach); dma_buf_put(map->buf); } @@ -726,7 +726,7 @@ static int fastrpc_map_create(struct fastrpc_user *fl, int fd, goto attach_err; } - map->table = dma_buf_map_attachment(map->attach, DMA_BIDIRECTIONAL); + map->table = dma_buf_map_attachment_unlocked(map->attach, DMA_BIDIRECTIONAL); if (IS_ERR(map->table)) { err = PTR_ERR(map->table); goto map_err; -- 2.37.3
[PATCH v6 10/21] RDMA/umem: Prepare to dynamic dma-buf locking specification
Prepare InfiniBand drivers to the common dynamic dma-buf locking convention by starting to use the unlocked versions of dma-buf API functions. Acked-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/infiniband/core/umem_dmabuf.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/core/umem_dmabuf.c b/drivers/infiniband/core/umem_dmabuf.c index 04c04e6d24c3..43b26bc12288 100644 --- a/drivers/infiniband/core/umem_dmabuf.c +++ b/drivers/infiniband/core/umem_dmabuf.c @@ -26,7 +26,8 @@ int ib_umem_dmabuf_map_pages(struct ib_umem_dmabuf *umem_dmabuf) if (umem_dmabuf->sgt) goto wait_fence; - sgt = dma_buf_map_attachment(umem_dmabuf->attach, DMA_BIDIRECTIONAL); + sgt = dma_buf_map_attachment_unlocked(umem_dmabuf->attach, + DMA_BIDIRECTIONAL); if (IS_ERR(sgt)) return PTR_ERR(sgt); @@ -102,8 +103,8 @@ void ib_umem_dmabuf_unmap_pages(struct ib_umem_dmabuf *umem_dmabuf) umem_dmabuf->last_sg_trim = 0; } - dma_buf_unmap_attachment(umem_dmabuf->attach, umem_dmabuf->sgt, -DMA_BIDIRECTIONAL); + dma_buf_unmap_attachment_unlocked(umem_dmabuf->attach, umem_dmabuf->sgt, + DMA_BIDIRECTIONAL); umem_dmabuf->sgt = NULL; } -- 2.37.3
[PATCH v6 08/21] drm/tegra: Prepare to dynamic dma-buf locking specification
Prepare Tegra DRM driver to the common dynamic dma-buf locking convention by starting to use the unlocked versions of dma-buf API functions. Acked-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/gpu/drm/tegra/gem.c | 17 + 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 81991090adcc..b09b8ab40ae4 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -84,7 +84,7 @@ static struct host1x_bo_mapping *tegra_bo_pin(struct device *dev, struct host1x_ goto free; } - map->sgt = dma_buf_map_attachment(map->attach, direction); + map->sgt = dma_buf_map_attachment_unlocked(map->attach, direction); if (IS_ERR(map->sgt)) { dma_buf_detach(buf, map->attach); err = PTR_ERR(map->sgt); @@ -160,7 +160,8 @@ static struct host1x_bo_mapping *tegra_bo_pin(struct device *dev, struct host1x_ static void tegra_bo_unpin(struct host1x_bo_mapping *map) { if (map->attach) { - dma_buf_unmap_attachment(map->attach, map->sgt, map->direction); + dma_buf_unmap_attachment_unlocked(map->attach, map->sgt, + map->direction); dma_buf_detach(map->attach->dmabuf, map->attach); } else { dma_unmap_sgtable(map->dev, map->sgt, map->direction, 0); @@ -181,7 +182,7 @@ static void *tegra_bo_mmap(struct host1x_bo *bo) if (obj->vaddr) { return obj->vaddr; } else if (obj->gem.import_attach) { - ret = dma_buf_vmap(obj->gem.import_attach->dmabuf, &map); + ret = dma_buf_vmap_unlocked(obj->gem.import_attach->dmabuf, &map); return ret ? NULL : map.vaddr; } else { return vmap(obj->pages, obj->num_pages, VM_MAP, @@ -197,7 +198,7 @@ static void tegra_bo_munmap(struct host1x_bo *bo, void *addr) if (obj->vaddr) return; else if (obj->gem.import_attach) - dma_buf_vunmap(obj->gem.import_attach->dmabuf, &map); + dma_buf_vunmap_unlocked(obj->gem.import_attach->dmabuf, &map); else vunmap(addr); } @@ -461,7 +462,7 @@ static struct tegra_bo *tegra_bo_import(struct drm_device *drm, get_dma_buf(buf); - bo->sgt = dma_buf_map_attachment(attach, DMA_TO_DEVICE); + bo->sgt = dma_buf_map_attachment_unlocked(attach, DMA_TO_DEVICE); if (IS_ERR(bo->sgt)) { err = PTR_ERR(bo->sgt); goto detach; @@ -479,7 +480,7 @@ static struct tegra_bo *tegra_bo_import(struct drm_device *drm, detach: if (!IS_ERR_OR_NULL(bo->sgt)) - dma_buf_unmap_attachment(attach, bo->sgt, DMA_TO_DEVICE); + dma_buf_unmap_attachment_unlocked(attach, bo->sgt, DMA_TO_DEVICE); dma_buf_detach(buf, attach); dma_buf_put(buf); @@ -508,8 +509,8 @@ void tegra_bo_free_object(struct drm_gem_object *gem) tegra_bo_iommu_unmap(tegra, bo); if (gem->import_attach) { - dma_buf_unmap_attachment(gem->import_attach, bo->sgt, -DMA_TO_DEVICE); + dma_buf_unmap_attachment_unlocked(gem->import_attach, bo->sgt, + DMA_TO_DEVICE); drm_prime_gem_destroy(gem, NULL); } else { tegra_bo_free(gem->dev, bo); -- 2.37.3
[PATCH v6 18/21] dma-buf: Move dma_buf_mmap() to dynamic locking specification
Move dma_buf_mmap() function to the dynamic locking specification by taking the reservation lock. Neither of the today's drivers take the reservation lock within the mmap() callback, hence it's safe to enforce the locking. Acked-by: Sumit Semwal Acked-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/dma-buf/dma-buf.c | 8 +++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index bff5a70b8735..2452b4c82584 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -1390,6 +1390,8 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_end_cpu_access, DMA_BUF); int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, unsigned long pgoff) { + int ret; + if (WARN_ON(!dmabuf || !vma)) return -EINVAL; @@ -1410,7 +1412,11 @@ int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, vma_set_file(vma, dmabuf->file); vma->vm_pgoff = pgoff; - return dmabuf->ops->mmap(dmabuf, vma); + dma_resv_lock(dmabuf->resv, NULL); + ret = dmabuf->ops->mmap(dmabuf, vma); + dma_resv_unlock(dmabuf->resv); + + return ret; } EXPORT_SYMBOL_NS_GPL(dma_buf_mmap, DMA_BUF); -- 2.37.3
[PATCH v6 16/21] dma-buf: Move dma_buf_attach() to dynamic locking specification
Move dma-buf attachment API functions to the dynamic locking specification by taking the reservation lock around the mapping operations. The strict locking convention prevents deadlock situations for dma-buf importers and exporters. Acked-by: Sumit Semwal Reviewed-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/dma-buf/dma-buf.c | 20 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 23656f334735..d60585bbb529 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -859,8 +859,8 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev, dma_buf_is_dynamic(dmabuf)) { struct sg_table *sgt; + dma_resv_lock(attach->dmabuf->resv, NULL); if (dma_buf_is_dynamic(attach->dmabuf)) { - dma_resv_lock(attach->dmabuf->resv, NULL); ret = dmabuf->ops->pin(attach); if (ret) goto err_unlock; @@ -873,8 +873,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev, ret = PTR_ERR(sgt); goto err_unpin; } - if (dma_buf_is_dynamic(attach->dmabuf)) - dma_resv_unlock(attach->dmabuf->resv); + dma_resv_unlock(attach->dmabuf->resv); attach->sgt = sgt; attach->dir = DMA_BIDIRECTIONAL; } @@ -890,8 +889,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev, dmabuf->ops->unpin(attach); err_unlock: - if (dma_buf_is_dynamic(attach->dmabuf)) - dma_resv_unlock(attach->dmabuf->resv); + dma_resv_unlock(attach->dmabuf->resv); dma_buf_detach(dmabuf, attach); return ERR_PTR(ret); @@ -937,21 +935,19 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) if (WARN_ON(!dmabuf || !attach)) return; + dma_resv_lock(attach->dmabuf->resv, NULL); + if (attach->sgt) { - if (dma_buf_is_dynamic(attach->dmabuf)) - dma_resv_lock(attach->dmabuf->resv, NULL); __unmap_dma_buf(attach, attach->sgt, attach->dir); - if (dma_buf_is_dynamic(attach->dmabuf)) { + if (dma_buf_is_dynamic(attach->dmabuf)) dmabuf->ops->unpin(attach); - dma_resv_unlock(attach->dmabuf->resv); - } } - - dma_resv_lock(dmabuf->resv, NULL); list_del(&attach->node); + dma_resv_unlock(dmabuf->resv); + if (dmabuf->ops->detach) dmabuf->ops->detach(dmabuf, attach); -- 2.37.3
[PATCH v6 19/21] dma-buf: Document dynamic locking convention
Add documentation for the dynamic locking convention. The documentation tells dma-buf API users when they should take the reservation lock and when not. Acked-by: Sumit Semwal Reviewed-by: Christian König Signed-off-by: Dmitry Osipenko --- Documentation/driver-api/dma-buf.rst | 6 +++ drivers/dma-buf/dma-buf.c| 64 2 files changed, 70 insertions(+) diff --git a/Documentation/driver-api/dma-buf.rst b/Documentation/driver-api/dma-buf.rst index 36a76cbe9095..622b8156d212 100644 --- a/Documentation/driver-api/dma-buf.rst +++ b/Documentation/driver-api/dma-buf.rst @@ -119,6 +119,12 @@ DMA Buffer ioctls .. kernel-doc:: include/uapi/linux/dma-buf.h +DMA-BUF locking convention +~ + +.. kernel-doc:: drivers/dma-buf/dma-buf.c + :doc: locking convention + Kernel Functions and Structures Reference ~ diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 2452b4c82584..e04d504441a5 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -795,6 +795,70 @@ static struct sg_table * __map_dma_buf(struct dma_buf_attachment *attach, return sg_table; } +/** + * DOC: locking convention + * + * In order to avoid deadlock situations between dma-buf exports and importers, + * all dma-buf API users must follow the common dma-buf locking convention. + * + * Convention for importers + * + * 1. Importers must hold the dma-buf reservation lock when calling these + *functions: + * + * - dma_buf_pin() + * - dma_buf_unpin() + * - dma_buf_map_attachment() + * - dma_buf_unmap_attachment() + * - dma_buf_vmap() + * - dma_buf_vunmap() + * + * 2. Importers must not hold the dma-buf reservation lock when calling these + *functions: + * + * - dma_buf_attach() + * - dma_buf_dynamic_attach() + * - dma_buf_detach() + * - dma_buf_export( + * - dma_buf_fd() + * - dma_buf_get() + * - dma_buf_put() + * - dma_buf_mmap() + * - dma_buf_begin_cpu_access() + * - dma_buf_end_cpu_access() + * - dma_buf_map_attachment_unlocked() + * - dma_buf_unmap_attachment_unlocked() + * - dma_buf_vmap_unlocked() + * - dma_buf_vunmap_unlocked() + * + * Convention for exporters + * + * 1. These &dma_buf_ops callbacks are invoked with unlocked dma-buf + *reservation and exporter can take the lock: + * + * - &dma_buf_ops.attach() + * - &dma_buf_ops.detach() + * - &dma_buf_ops.release() + * - &dma_buf_ops.begin_cpu_access() + * - &dma_buf_ops.end_cpu_access() + * + * 2. These &dma_buf_ops callbacks are invoked with locked dma-buf + *reservation and exporter can't take the lock: + * + * - &dma_buf_ops.pin() + * - &dma_buf_ops.unpin() + * - &dma_buf_ops.map_dma_buf() + * - &dma_buf_ops.unmap_dma_buf() + * - &dma_buf_ops.mmap() + * - &dma_buf_ops.vmap() + * - &dma_buf_ops.vunmap() + * + * 3. Exporters must hold the dma-buf reservation lock when calling these + *functions: + * + * - dma_buf_move_notify() + */ + /** * dma_buf_dynamic_attach - Add the device to dma_buf's attachments list * @dmabuf:[in]buffer to attach device to. -- 2.37.3
[PATCH v6 14/21] media: tegra-vde: Prepare to dynamic dma-buf locking specification
Prepare Tegra video decoder driver to the common dynamic dma-buf locking convention by starting to use the unlocked versions of dma-buf API functions. Acked-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c b/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c index 69c346148070..1c5b94989aec 100644 --- a/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c +++ b/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c @@ -38,7 +38,7 @@ static void tegra_vde_release_entry(struct tegra_vde_cache_entry *entry) if (entry->vde->domain) tegra_vde_iommu_unmap(entry->vde, entry->iova); - dma_buf_unmap_attachment(entry->a, entry->sgt, entry->dma_dir); + dma_buf_unmap_attachment_unlocked(entry->a, entry->sgt, entry->dma_dir); dma_buf_detach(dmabuf, entry->a); dma_buf_put(dmabuf); @@ -102,7 +102,7 @@ int tegra_vde_dmabuf_cache_map(struct tegra_vde *vde, goto err_unlock; } - sgt = dma_buf_map_attachment(attachment, dma_dir); + sgt = dma_buf_map_attachment_unlocked(attachment, dma_dir); if (IS_ERR(sgt)) { dev_err(dev, "Failed to get dmabufs sg_table\n"); err = PTR_ERR(sgt); @@ -152,7 +152,7 @@ int tegra_vde_dmabuf_cache_map(struct tegra_vde *vde, err_free: kfree(entry); err_unmap: - dma_buf_unmap_attachment(attachment, sgt, dma_dir); + dma_buf_unmap_attachment_unlocked(attachment, sgt, dma_dir); err_detach: dma_buf_detach(dmabuf, attachment); err_unlock: -- 2.37.3
[PATCH v6 15/21] dma-buf: Move dma_buf_vmap() to dynamic locking specification
Move dma_buf_vmap/vunmap() functions to the dynamic locking specification by asserting that the reservation lock is held. Acked-by: Sumit Semwal Acked-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/dma-buf/dma-buf.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index f349613790a6..23656f334735 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -1450,6 +1450,8 @@ int dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map) if (WARN_ON(!dmabuf)) return -EINVAL; + dma_resv_assert_held(dmabuf->resv); + if (!dmabuf->ops->vmap) return -EINVAL; @@ -1513,6 +1515,8 @@ void dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map) if (WARN_ON(!dmabuf)) return; + dma_resv_assert_held(dmabuf->resv); + BUG_ON(iosys_map_is_null(&dmabuf->vmap_ptr)); BUG_ON(dmabuf->vmapping_counter == 0); BUG_ON(!iosys_map_is_equal(&dmabuf->vmap_ptr, map)); -- 2.37.3
[PATCH v6 20/21] media: videobuf2: Stop using internal dma-buf lock
All drivers that use dma-bufs have been moved to the updated locking specification and now dma-buf reservation is guaranteed to be locked by importers during the mapping operations. There is no need to take the internal dma-buf lock anymore. Remove locking from the videobuf2 memory allocators. Acked-by: Tomasz Figa Acked-by: Hans Verkuil Acked-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/media/common/videobuf2/videobuf2-dma-contig.c | 11 +-- drivers/media/common/videobuf2/videobuf2-dma-sg.c | 11 +-- drivers/media/common/videobuf2/videobuf2-vmalloc.c| 11 +-- 3 files changed, 3 insertions(+), 30 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index 79f4d8301fbb..555bd40fa472 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -382,18 +382,12 @@ static struct sg_table *vb2_dc_dmabuf_ops_map( struct dma_buf_attachment *db_attach, enum dma_data_direction dma_dir) { struct vb2_dc_attachment *attach = db_attach->priv; - /* stealing dmabuf mutex to serialize map/unmap operations */ - struct mutex *lock = &db_attach->dmabuf->lock; struct sg_table *sgt; - mutex_lock(lock); - sgt = &attach->sgt; /* return previously mapped sg table */ - if (attach->dma_dir == dma_dir) { - mutex_unlock(lock); + if (attach->dma_dir == dma_dir) return sgt; - } /* release any previous cache */ if (attach->dma_dir != DMA_NONE) { @@ -409,14 +403,11 @@ static struct sg_table *vb2_dc_dmabuf_ops_map( if (dma_map_sgtable(db_attach->dev, sgt, dma_dir, DMA_ATTR_SKIP_CPU_SYNC)) { pr_err("failed to map scatterlist\n"); - mutex_unlock(lock); return ERR_PTR(-EIO); } attach->dma_dir = dma_dir; - mutex_unlock(lock); - return sgt; } diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index 36ecdea8d707..36981a5b5c53 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -424,18 +424,12 @@ static struct sg_table *vb2_dma_sg_dmabuf_ops_map( struct dma_buf_attachment *db_attach, enum dma_data_direction dma_dir) { struct vb2_dma_sg_attachment *attach = db_attach->priv; - /* stealing dmabuf mutex to serialize map/unmap operations */ - struct mutex *lock = &db_attach->dmabuf->lock; struct sg_table *sgt; - mutex_lock(lock); - sgt = &attach->sgt; /* return previously mapped sg table */ - if (attach->dma_dir == dma_dir) { - mutex_unlock(lock); + if (attach->dma_dir == dma_dir) return sgt; - } /* release any previous cache */ if (attach->dma_dir != DMA_NONE) { @@ -446,14 +440,11 @@ static struct sg_table *vb2_dma_sg_dmabuf_ops_map( /* mapping to the client with new direction */ if (dma_map_sgtable(db_attach->dev, sgt, dma_dir, 0)) { pr_err("failed to map scatterlist\n"); - mutex_unlock(lock); return ERR_PTR(-EIO); } attach->dma_dir = dma_dir; - mutex_unlock(lock); - return sgt; } diff --git a/drivers/media/common/videobuf2/videobuf2-vmalloc.c b/drivers/media/common/videobuf2/videobuf2-vmalloc.c index 7831bf545874..41db707e43a4 100644 --- a/drivers/media/common/videobuf2/videobuf2-vmalloc.c +++ b/drivers/media/common/videobuf2/videobuf2-vmalloc.c @@ -267,18 +267,12 @@ static struct sg_table *vb2_vmalloc_dmabuf_ops_map( struct dma_buf_attachment *db_attach, enum dma_data_direction dma_dir) { struct vb2_vmalloc_attachment *attach = db_attach->priv; - /* stealing dmabuf mutex to serialize map/unmap operations */ - struct mutex *lock = &db_attach->dmabuf->lock; struct sg_table *sgt; - mutex_lock(lock); - sgt = &attach->sgt; /* return previously mapped sg table */ - if (attach->dma_dir == dma_dir) { - mutex_unlock(lock); + if (attach->dma_dir == dma_dir) return sgt; - } /* release any previous cache */ if (attach->dma_dir != DMA_NONE) { @@ -289,14 +283,11 @@ static struct sg_table *vb2_vmalloc_dmabuf_ops_map( /* mapping to the client with new direction */ if (dma_map_sgtable(db_attach->dev, sgt, dma_dir, 0)) { pr_err("failed to map scatterlist\n"); - mutex_unlock(lock); return ERR_PTR(-EIO); } attach->dma_dir = dma_dir; - mutex_unlock(lock); - return sgt; } -- 2.37.3
[PATCH v6 17/21] dma-buf: Move dma_buf_map_attachment() to dynamic locking specification
Move dma-buf attachment mapping functions to the dynamic locking specification by asserting that the reservation lock is held. Acked-by: Sumit Semwal Reviewed-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/dma-buf/dma-buf.c | 10 ++ 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index d60585bbb529..bff5a70b8735 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -1038,8 +1038,7 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, if (WARN_ON(!attach || !attach->dmabuf)) return ERR_PTR(-EINVAL); - if (dma_buf_attachment_is_dynamic(attach)) - dma_resv_assert_held(attach->dmabuf->resv); + dma_resv_assert_held(attach->dmabuf->resv); if (attach->sgt) { /* @@ -1054,7 +1053,6 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, } if (dma_buf_is_dynamic(attach->dmabuf)) { - dma_resv_assert_held(attach->dmabuf->resv); if (!IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY)) { r = attach->dmabuf->ops->pin(attach); if (r) @@ -1143,15 +1141,11 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach, if (WARN_ON(!attach || !attach->dmabuf || !sg_table)) return; - if (dma_buf_attachment_is_dynamic(attach)) - dma_resv_assert_held(attach->dmabuf->resv); + dma_resv_assert_held(attach->dmabuf->resv); if (attach->sgt == sg_table) return; - if (dma_buf_is_dynamic(attach->dmabuf)) - dma_resv_assert_held(attach->dmabuf->resv); - __unmap_dma_buf(attach, sg_table, direction); if (dma_buf_is_dynamic(attach->dmabuf) && -- 2.37.3
[PATCH v6 21/21] dma-buf: Remove obsoleted internal lock
The internal dma-buf lock isn't needed anymore because the updated locking specification claims that dma-buf reservation must be locked by importers, and thus, the internal data is already protected by the reservation lock. Remove the obsoleted internal lock. Acked-by: Sumit Semwal Acked-by: Christian König Reviewed-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/dma-buf/dma-buf.c | 14 -- include/linux/dma-buf.h | 9 - 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index e04d504441a5..82f72b5647f8 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -657,7 +657,6 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info) dmabuf->file = file; - mutex_init(&dmabuf->lock); INIT_LIST_HEAD(&dmabuf->attachments); mutex_lock(&db_list.lock); @@ -1503,7 +1502,7 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_mmap, DMA_BUF); int dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map) { struct iosys_map ptr; - int ret = 0; + int ret; iosys_map_clear(map); @@ -1515,28 +1514,25 @@ int dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map) if (!dmabuf->ops->vmap) return -EINVAL; - mutex_lock(&dmabuf->lock); if (dmabuf->vmapping_counter) { dmabuf->vmapping_counter++; BUG_ON(iosys_map_is_null(&dmabuf->vmap_ptr)); *map = dmabuf->vmap_ptr; - goto out_unlock; + return 0; } BUG_ON(iosys_map_is_set(&dmabuf->vmap_ptr)); ret = dmabuf->ops->vmap(dmabuf, &ptr); if (WARN_ON_ONCE(ret)) - goto out_unlock; + return ret; dmabuf->vmap_ptr = ptr; dmabuf->vmapping_counter = 1; *map = dmabuf->vmap_ptr; -out_unlock: - mutex_unlock(&dmabuf->lock); - return ret; + return 0; } EXPORT_SYMBOL_NS_GPL(dma_buf_vmap, DMA_BUF); @@ -1581,13 +1577,11 @@ void dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map) BUG_ON(dmabuf->vmapping_counter == 0); BUG_ON(!iosys_map_is_equal(&dmabuf->vmap_ptr, map)); - mutex_lock(&dmabuf->lock); if (--dmabuf->vmapping_counter == 0) { if (dmabuf->ops->vunmap) dmabuf->ops->vunmap(dmabuf, map); iosys_map_clear(&dmabuf->vmap_ptr); } - mutex_unlock(&dmabuf->lock); } EXPORT_SYMBOL_NS_GPL(dma_buf_vunmap, DMA_BUF); diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index f11b5bbc2f37..6fa8d4e29719 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -326,15 +326,6 @@ struct dma_buf { /** @ops: dma_buf_ops associated with this buffer object. */ const struct dma_buf_ops *ops; - /** -* @lock: -* -* Used internally to serialize list manipulation, attach/detach and -* vmap/unmap. Note that in many cases this is superseeded by -* dma_resv_lock() on @resv. -*/ - struct mutex lock; - /** * @vmapping_counter: * -- 2.37.3
[PATCH v6 13/21] media: videobuf2: Prepare to dynamic dma-buf locking specification
Prepare V4L2 memory allocators to the common dynamic dma-buf locking convention by starting to use the unlocked versions of dma-buf API functions. Acked-by: Tomasz Figa Acked-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/media/common/videobuf2/videobuf2-dma-contig.c | 11 ++- drivers/media/common/videobuf2/videobuf2-dma-sg.c | 8 drivers/media/common/videobuf2/videobuf2-vmalloc.c| 6 +++--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index 678b359717c4..79f4d8301fbb 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -101,7 +101,7 @@ static void *vb2_dc_vaddr(struct vb2_buffer *vb, void *buf_priv) if (buf->db_attach) { struct iosys_map map; - if (!dma_buf_vmap(buf->db_attach->dmabuf, &map)) + if (!dma_buf_vmap_unlocked(buf->db_attach->dmabuf, &map)) buf->vaddr = map.vaddr; return buf->vaddr; @@ -711,7 +711,7 @@ static int vb2_dc_map_dmabuf(void *mem_priv) } /* get the associated scatterlist for this buffer */ - sgt = dma_buf_map_attachment(buf->db_attach, buf->dma_dir); + sgt = dma_buf_map_attachment_unlocked(buf->db_attach, buf->dma_dir); if (IS_ERR(sgt)) { pr_err("Error getting dmabuf scatterlist\n"); return -EINVAL; @@ -722,7 +722,8 @@ static int vb2_dc_map_dmabuf(void *mem_priv) if (contig_size < buf->size) { pr_err("contiguous chunk is too small %lu/%lu\n", contig_size, buf->size); - dma_buf_unmap_attachment(buf->db_attach, sgt, buf->dma_dir); + dma_buf_unmap_attachment_unlocked(buf->db_attach, sgt, + buf->dma_dir); return -EFAULT; } @@ -750,10 +751,10 @@ static void vb2_dc_unmap_dmabuf(void *mem_priv) } if (buf->vaddr) { - dma_buf_vunmap(buf->db_attach->dmabuf, &map); + dma_buf_vunmap_unlocked(buf->db_attach->dmabuf, &map); buf->vaddr = NULL; } - dma_buf_unmap_attachment(buf->db_attach, sgt, buf->dma_dir); + dma_buf_unmap_attachment_unlocked(buf->db_attach, sgt, buf->dma_dir); buf->dma_addr = 0; buf->dma_sgt = NULL; diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index fa69158a65b1..36ecdea8d707 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -309,7 +309,7 @@ static void *vb2_dma_sg_vaddr(struct vb2_buffer *vb, void *buf_priv) if (!buf->vaddr) { if (buf->db_attach) { - ret = dma_buf_vmap(buf->db_attach->dmabuf, &map); + ret = dma_buf_vmap_unlocked(buf->db_attach->dmabuf, &map); buf->vaddr = ret ? NULL : map.vaddr; } else { buf->vaddr = vm_map_ram(buf->pages, buf->num_pages, -1); @@ -565,7 +565,7 @@ static int vb2_dma_sg_map_dmabuf(void *mem_priv) } /* get the associated scatterlist for this buffer */ - sgt = dma_buf_map_attachment(buf->db_attach, buf->dma_dir); + sgt = dma_buf_map_attachment_unlocked(buf->db_attach, buf->dma_dir); if (IS_ERR(sgt)) { pr_err("Error getting dmabuf scatterlist\n"); return -EINVAL; @@ -594,10 +594,10 @@ static void vb2_dma_sg_unmap_dmabuf(void *mem_priv) } if (buf->vaddr) { - dma_buf_vunmap(buf->db_attach->dmabuf, &map); + dma_buf_vunmap_unlocked(buf->db_attach->dmabuf, &map); buf->vaddr = NULL; } - dma_buf_unmap_attachment(buf->db_attach, sgt, buf->dma_dir); + dma_buf_unmap_attachment_unlocked(buf->db_attach, sgt, buf->dma_dir); buf->dma_sgt = NULL; } diff --git a/drivers/media/common/videobuf2/videobuf2-vmalloc.c b/drivers/media/common/videobuf2/videobuf2-vmalloc.c index 948152f1596b..7831bf545874 100644 --- a/drivers/media/common/videobuf2/videobuf2-vmalloc.c +++ b/drivers/media/common/videobuf2/videobuf2-vmalloc.c @@ -376,7 +376,7 @@ static int vb2_vmalloc_map_dmabuf(void *mem_priv) struct iosys_map map; int ret; - ret = dma_buf_vmap(buf->dbuf, &map); + ret = dma_buf_vmap_unlocked(buf->dbuf, &map); if (ret) return -EFAULT; buf->vaddr = map.vaddr; @@ -389,7 +389,7 @@ static void vb2_vmalloc_unmap_dmabuf(void *mem_priv) struct vb2_vmalloc_buf *buf = mem_priv; struct iosys_map map = IOSYS_MAP_INIT_VADDR(buf->vaddr); - dma_buf_vunmap(buf->dbuf, &map); + dma_buf_vunmap_unlocked(buf->dbuf
[PATCH v6 05/21] drm/armada: Prepare to dynamic dma-buf locking specification
Prepare Armada driver to the common dynamic dma-buf locking convention by starting to use the unlocked versions of dma-buf API functions. Acked-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/gpu/drm/armada/armada_gem.c | 8 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c index 5430265ad458..26d10065d534 100644 --- a/drivers/gpu/drm/armada/armada_gem.c +++ b/drivers/gpu/drm/armada/armada_gem.c @@ -66,8 +66,8 @@ void armada_gem_free_object(struct drm_gem_object *obj) if (dobj->obj.import_attach) { /* We only ever display imported data */ if (dobj->sgt) - dma_buf_unmap_attachment(dobj->obj.import_attach, -dobj->sgt, DMA_TO_DEVICE); + dma_buf_unmap_attachment_unlocked(dobj->obj.import_attach, + dobj->sgt, DMA_TO_DEVICE); drm_prime_gem_destroy(&dobj->obj, NULL); } @@ -539,8 +539,8 @@ int armada_gem_map_import(struct armada_gem_object *dobj) { int ret; - dobj->sgt = dma_buf_map_attachment(dobj->obj.import_attach, - DMA_TO_DEVICE); + dobj->sgt = dma_buf_map_attachment_unlocked(dobj->obj.import_attach, + DMA_TO_DEVICE); if (IS_ERR(dobj->sgt)) { ret = PTR_ERR(dobj->sgt); dobj->sgt = NULL; -- 2.37.3
[PATCH v6 12/21] xen/gntdev: Prepare to dynamic dma-buf locking specification
Prepare gntdev driver to the common dynamic dma-buf locking convention by starting to use the unlocked versions of dma-buf API functions. Acked-by: Juergen Gross Acked-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/xen/gntdev-dmabuf.c | 8 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/xen/gntdev-dmabuf.c b/drivers/xen/gntdev-dmabuf.c index 940e5e9e8a54..4440e626b797 100644 --- a/drivers/xen/gntdev-dmabuf.c +++ b/drivers/xen/gntdev-dmabuf.c @@ -600,7 +600,7 @@ dmabuf_imp_to_refs(struct gntdev_dmabuf_priv *priv, struct device *dev, gntdev_dmabuf->u.imp.attach = attach; - sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + sgt = dma_buf_map_attachment_unlocked(attach, DMA_BIDIRECTIONAL); if (IS_ERR(sgt)) { ret = ERR_CAST(sgt); goto fail_detach; @@ -658,7 +658,7 @@ dmabuf_imp_to_refs(struct gntdev_dmabuf_priv *priv, struct device *dev, fail_end_access: dmabuf_imp_end_foreign_access(gntdev_dmabuf->u.imp.refs, count); fail_unmap: - dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); + dma_buf_unmap_attachment_unlocked(attach, sgt, DMA_BIDIRECTIONAL); fail_detach: dma_buf_detach(dma_buf, attach); fail_free_obj: @@ -708,8 +708,8 @@ static int dmabuf_imp_release(struct gntdev_dmabuf_priv *priv, u32 fd) attach = gntdev_dmabuf->u.imp.attach; if (gntdev_dmabuf->u.imp.sgt) - dma_buf_unmap_attachment(attach, gntdev_dmabuf->u.imp.sgt, -DMA_BIDIRECTIONAL); + dma_buf_unmap_attachment_unlocked(attach, gntdev_dmabuf->u.imp.sgt, + DMA_BIDIRECTIONAL); dma_buf = attach->dmabuf; dma_buf_detach(attach->dmabuf, attach); dma_buf_put(dma_buf); -- 2.37.3
[PATCH v6 09/21] drm/etnaviv: Prepare to dynamic dma-buf locking specification
Prepare Etnaviv driver to the common dynamic dma-buf locking convention by starting to use the unlocked versions of dma-buf API functions. Acked-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c index 3fa2da149639..7031db145a77 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c @@ -65,7 +65,7 @@ static void etnaviv_gem_prime_release(struct etnaviv_gem_object *etnaviv_obj) struct iosys_map map = IOSYS_MAP_INIT_VADDR(etnaviv_obj->vaddr); if (etnaviv_obj->vaddr) - dma_buf_vunmap(etnaviv_obj->base.import_attach->dmabuf, &map); + dma_buf_vunmap_unlocked(etnaviv_obj->base.import_attach->dmabuf, &map); /* Don't drop the pages for imported dmabuf, as they are not * ours, just free the array we allocated: -- 2.37.3
[PATCH v6 07/21] drm/omapdrm: Prepare to dynamic dma-buf locking specification
Prepare OMAP DRM driver to the common dynamic dma-buf locking convention by starting to use the unlocked versions of dma-buf API functions. Acked-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c index 393f82e26927..8e194dbc9506 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c @@ -125,7 +125,7 @@ struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, get_dma_buf(dma_buf); - sgt = dma_buf_map_attachment(attach, DMA_TO_DEVICE); + sgt = dma_buf_map_attachment_unlocked(attach, DMA_TO_DEVICE); if (IS_ERR(sgt)) { ret = PTR_ERR(sgt); goto fail_detach; @@ -142,7 +142,7 @@ struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, return obj; fail_unmap: - dma_buf_unmap_attachment(attach, sgt, DMA_TO_DEVICE); + dma_buf_unmap_attachment_unlocked(attach, sgt, DMA_TO_DEVICE); fail_detach: dma_buf_detach(dma_buf, attach); dma_buf_put(dma_buf); -- 2.37.3
[PATCH v6 03/21] drm/gem: Take reservation lock for vmap/vunmap operations
The new common dma-buf locking convention will require buffer importers to hold the reservation lock around mapping operations. Make DRM GEM core to take the lock around the vmapping operations and update DRM drivers to use the locked functions for the case where DRM core now holds the lock. This patch prepares DRM core and drivers to the common dynamic dma-buf locking convention. Acked-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/gpu/drm/drm_client.c | 4 ++-- drivers/gpu/drm/drm_gem.c| 24 drivers/gpu/drm/drm_gem_dma_helper.c | 6 ++--- drivers/gpu/drm/drm_gem_framebuffer_helper.c | 6 ++--- drivers/gpu/drm/drm_gem_ttm_helper.c | 9 +--- drivers/gpu/drm/lima/lima_sched.c| 4 ++-- drivers/gpu/drm/panfrost/panfrost_dump.c | 4 ++-- drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 6 ++--- drivers/gpu/drm/qxl/qxl_object.c | 17 +++--- drivers/gpu/drm/qxl/qxl_prime.c | 4 ++-- include/drm/drm_gem.h| 3 +++ 11 files changed, 54 insertions(+), 33 deletions(-) diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index 2b230b4d6942..fbcb1e995384 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -323,7 +323,7 @@ drm_client_buffer_vmap(struct drm_client_buffer *buffer, * fd_install step out of the driver backend hooks, to make that * final step optional for internal users. */ - ret = drm_gem_vmap(buffer->gem, map); + ret = drm_gem_vmap_unlocked(buffer->gem, map); if (ret) return ret; @@ -345,7 +345,7 @@ void drm_client_buffer_vunmap(struct drm_client_buffer *buffer) { struct iosys_map *map = &buffer->map; - drm_gem_vunmap(buffer->gem, map); + drm_gem_vunmap_unlocked(buffer->gem, map); } EXPORT_SYMBOL(drm_client_buffer_vunmap); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 8b68a3c1e6ab..b8db675e7fb5 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1158,6 +1158,8 @@ int drm_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map) { int ret; + dma_resv_assert_held(obj->resv); + if (!obj->funcs->vmap) return -EOPNOTSUPP; @@ -1173,6 +1175,8 @@ EXPORT_SYMBOL(drm_gem_vmap); void drm_gem_vunmap(struct drm_gem_object *obj, struct iosys_map *map) { + dma_resv_assert_held(obj->resv); + if (iosys_map_is_null(map)) return; @@ -1184,6 +1188,26 @@ void drm_gem_vunmap(struct drm_gem_object *obj, struct iosys_map *map) } EXPORT_SYMBOL(drm_gem_vunmap); +int drm_gem_vmap_unlocked(struct drm_gem_object *obj, struct iosys_map *map) +{ + int ret; + + dma_resv_lock(obj->resv, NULL); + ret = drm_gem_vmap(obj, map); + dma_resv_unlock(obj->resv); + + return ret; +} +EXPORT_SYMBOL(drm_gem_vmap_unlocked); + +void drm_gem_vunmap_unlocked(struct drm_gem_object *obj, struct iosys_map *map) +{ + dma_resv_lock(obj->resv, NULL); + drm_gem_vunmap(obj, map); + dma_resv_unlock(obj->resv); +} +EXPORT_SYMBOL(drm_gem_vunmap_unlocked); + /** * drm_gem_lock_reservations - Sets up the ww context and acquires * the lock on an array of GEM objects. diff --git a/drivers/gpu/drm/drm_gem_dma_helper.c b/drivers/gpu/drm/drm_gem_dma_helper.c index f6901ff97bbb..1e658c448366 100644 --- a/drivers/gpu/drm/drm_gem_dma_helper.c +++ b/drivers/gpu/drm/drm_gem_dma_helper.c @@ -230,7 +230,7 @@ void drm_gem_dma_free(struct drm_gem_dma_object *dma_obj) if (gem_obj->import_attach) { if (dma_obj->vaddr) - dma_buf_vunmap(gem_obj->import_attach->dmabuf, &map); + dma_buf_vunmap_unlocked(gem_obj->import_attach->dmabuf, &map); drm_prime_gem_destroy(gem_obj, dma_obj->sgt); } else if (dma_obj->vaddr) { if (dma_obj->map_noncoherent) @@ -581,7 +581,7 @@ drm_gem_dma_prime_import_sg_table_vmap(struct drm_device *dev, struct iosys_map map; int ret; - ret = dma_buf_vmap(attach->dmabuf, &map); + ret = dma_buf_vmap_unlocked(attach->dmabuf, &map); if (ret) { DRM_ERROR("Failed to vmap PRIME buffer\n"); return ERR_PTR(ret); @@ -589,7 +589,7 @@ drm_gem_dma_prime_import_sg_table_vmap(struct drm_device *dev, obj = drm_gem_dma_prime_import_sg_table(dev, attach, sgt); if (IS_ERR(obj)) { - dma_buf_vunmap(attach->dmabuf, &map); + dma_buf_vunmap_unlocked(attach->dmabuf, &map); return obj; } diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 880a4975507f..e35e224e6303 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -354,7
[PATCH v6 06/21] drm/i915: Prepare to dynamic dma-buf locking specification
Prepare i915 driver to the common dynamic dma-buf locking convention by starting to use the unlocked versions of dma-buf API functions and handling cases where importer now holds the reservation lock. Acked-by: Christian König Reviewed-by: Michael J. Ruhl Signed-off-by: Dmitry Osipenko --- drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 2 +- drivers/gpu/drm/i915/gem/i915_gem_object.c | 14 ++ .../gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c | 16 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c index f5062d0c6333..07eee1c09aaf 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c @@ -72,7 +72,7 @@ static int i915_gem_dmabuf_vmap(struct dma_buf *dma_buf, struct drm_i915_gem_object *obj = dma_buf_to_obj(dma_buf); void *vaddr; - vaddr = i915_gem_object_pin_map_unlocked(obj, I915_MAP_WB); + vaddr = i915_gem_object_pin_map(obj, I915_MAP_WB); if (IS_ERR(vaddr)) return PTR_ERR(vaddr); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c index 7ff9c7877bec..3e3f63f86629 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c @@ -290,7 +290,21 @@ void __i915_gem_object_pages_fini(struct drm_i915_gem_object *obj) __i915_gem_object_free_mmaps(obj); atomic_set(&obj->mm.pages_pin_count, 0); + + /* +* dma_buf_unmap_attachment() requires reservation to be +* locked. The imported GEM shouldn't share reservation lock +* and ttm_bo_cleanup_memtype_use() shouldn't be invoked for +* dma-buf, so it's safe to take the lock. +*/ + if (obj->base.import_attach) + i915_gem_object_lock(obj, NULL); + __i915_gem_object_put_pages(obj); + + if (obj->base.import_attach) + i915_gem_object_unlock(obj); + GEM_BUG_ON(i915_gem_object_has_pages(obj)); } diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c index 51ed824b020c..f2f3cfad807b 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c @@ -213,7 +213,7 @@ static int igt_dmabuf_import_same_driver(struct drm_i915_private *i915, goto out_import; } - st = dma_buf_map_attachment(import_attach, DMA_BIDIRECTIONAL); + st = dma_buf_map_attachment_unlocked(import_attach, DMA_BIDIRECTIONAL); if (IS_ERR(st)) { err = PTR_ERR(st); goto out_detach; @@ -226,7 +226,7 @@ static int igt_dmabuf_import_same_driver(struct drm_i915_private *i915, timeout = -ETIME; } err = timeout > 0 ? 0 : timeout; - dma_buf_unmap_attachment(import_attach, st, DMA_BIDIRECTIONAL); + dma_buf_unmap_attachment_unlocked(import_attach, st, DMA_BIDIRECTIONAL); out_detach: dma_buf_detach(dmabuf, import_attach); out_import: @@ -296,7 +296,7 @@ static int igt_dmabuf_import(void *arg) goto out_obj; } - err = dma_buf_vmap(dmabuf, &map); + err = dma_buf_vmap_unlocked(dmabuf, &map); dma_map = err ? NULL : map.vaddr; if (!dma_map) { pr_err("dma_buf_vmap failed\n"); @@ -337,7 +337,7 @@ static int igt_dmabuf_import(void *arg) err = 0; out_dma_map: - dma_buf_vunmap(dmabuf, &map); + dma_buf_vunmap_unlocked(dmabuf, &map); out_obj: i915_gem_object_put(obj); out_dmabuf: @@ -358,7 +358,7 @@ static int igt_dmabuf_import_ownership(void *arg) if (IS_ERR(dmabuf)) return PTR_ERR(dmabuf); - err = dma_buf_vmap(dmabuf, &map); + err = dma_buf_vmap_unlocked(dmabuf, &map); ptr = err ? NULL : map.vaddr; if (!ptr) { pr_err("dma_buf_vmap failed\n"); @@ -367,7 +367,7 @@ static int igt_dmabuf_import_ownership(void *arg) } memset(ptr, 0xc5, PAGE_SIZE); - dma_buf_vunmap(dmabuf, &map); + dma_buf_vunmap_unlocked(dmabuf, &map); obj = to_intel_bo(i915_gem_prime_import(&i915->drm, dmabuf)); if (IS_ERR(obj)) { @@ -418,7 +418,7 @@ static int igt_dmabuf_export_vmap(void *arg) } i915_gem_object_put(obj); - err = dma_buf_vmap(dmabuf, &map); + err = dma_buf_vmap_unlocked(dmabuf, &map); ptr = err ? NULL : map.vaddr; if (!ptr) { pr_err("dma_buf_vmap failed\n"); @@ -435,7 +435,7 @@ static int igt_dmabuf_export_vmap(void *arg) memset(ptr, 0xc5, dmabuf->size); err = 0; - dma_buf_vunmap(dmabuf, &map); + dma_buf_vunmap_unlocked(dmabuf, &map); out: dma_buf_put(dmabuf); return err; -- 2.37.3
[PATCH v6 01/21] dma-buf: Add unlocked variant of vmapping functions
Add unlocked variant of dma_buf_vmap/vunmap() that will be utilized by drivers that don't take the reservation lock explicitly. Acked-by: Sumit Semwal Acked-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/dma-buf/dma-buf.c | 41 +++ include/linux/dma-buf.h | 2 ++ 2 files changed, 43 insertions(+) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index dd0f83ee505b..0427f3b88170 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -1425,6 +1425,31 @@ int dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map) } EXPORT_SYMBOL_NS_GPL(dma_buf_vmap, DMA_BUF); +/** + * dma_buf_vmap_unlocked - Create virtual mapping for the buffer object into kernel + * address space. Same restrictions as for vmap and friends apply. + * @dmabuf:[in]buffer to vmap + * @map: [out] returns the vmap pointer + * + * Unlocked version of dma_buf_vmap() + * + * Returns 0 on success, or a negative errno code otherwise. + */ +int dma_buf_vmap_unlocked(struct dma_buf *dmabuf, struct iosys_map *map) +{ + int ret; + + if (WARN_ON(!dmabuf)) + return -EINVAL; + + dma_resv_lock(dmabuf->resv, NULL); + ret = dma_buf_vmap(dmabuf, map); + dma_resv_unlock(dmabuf->resv); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(dma_buf_vmap_unlocked, DMA_BUF); + /** * dma_buf_vunmap - Unmap a vmap obtained by dma_buf_vmap. * @dmabuf:[in]buffer to vunmap @@ -1449,6 +1474,22 @@ void dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map) } EXPORT_SYMBOL_NS_GPL(dma_buf_vunmap, DMA_BUF); +/** + * dma_buf_vunmap_unlocked - Unmap a vmap obtained by dma_buf_vmap. + * @dmabuf:[in]buffer to vunmap + * @map: [in]vmap pointer to vunmap + */ +void dma_buf_vunmap_unlocked(struct dma_buf *dmabuf, struct iosys_map *map) +{ + if (WARN_ON(!dmabuf)) + return; + + dma_resv_lock(dmabuf->resv, NULL); + dma_buf_vunmap(dmabuf, map); + dma_resv_unlock(dmabuf->resv); +} +EXPORT_SYMBOL_NS_GPL(dma_buf_vunmap_unlocked, DMA_BUF); + #ifdef CONFIG_DEBUG_FS static int dma_buf_debug_show(struct seq_file *s, void *unused) { diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 71731796c8c3..8daa054dd7fe 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -632,4 +632,6 @@ int dma_buf_mmap(struct dma_buf *, struct vm_area_struct *, unsigned long); int dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map); void dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map); +int dma_buf_vmap_unlocked(struct dma_buf *dmabuf, struct iosys_map *map); +void dma_buf_vunmap_unlocked(struct dma_buf *dmabuf, struct iosys_map *map); #endif /* __DMA_BUF_H__ */ -- 2.37.3
[PATCH v6 04/21] drm/prime: Prepare to dynamic dma-buf locking specification
Prepare DRM prime core to the common dynamic dma-buf locking convention by starting to use the unlocked versions of dma-buf API functions. Reviewed-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/gpu/drm/drm_prime.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index eb09e86044c6..20e109a802ae 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -940,7 +940,7 @@ struct drm_gem_object *drm_gem_prime_import_dev(struct drm_device *dev, get_dma_buf(dma_buf); - sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + sgt = dma_buf_map_attachment_unlocked(attach, DMA_BIDIRECTIONAL); if (IS_ERR(sgt)) { ret = PTR_ERR(sgt); goto fail_detach; @@ -958,7 +958,7 @@ struct drm_gem_object *drm_gem_prime_import_dev(struct drm_device *dev, return obj; fail_unmap: - dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); + dma_buf_unmap_attachment_unlocked(attach, sgt, DMA_BIDIRECTIONAL); fail_detach: dma_buf_detach(dma_buf, attach); dma_buf_put(dma_buf); @@ -1056,7 +1056,7 @@ void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg) attach = obj->import_attach; if (sg) - dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); + dma_buf_unmap_attachment_unlocked(attach, sg, DMA_BIDIRECTIONAL); dma_buf = attach->dmabuf; dma_buf_detach(attach->dmabuf, attach); /* remove the reference */ -- 2.37.3
[PATCH v6 02/21] dma-buf: Add unlocked variant of attachment-mapping functions
Add unlocked variant of dma_buf_map/unmap_attachment() that will be used by drivers that don't take the reservation lock explicitly. Acked-by: Sumit Semwal Acked-by: Christian König Signed-off-by: Dmitry Osipenko --- drivers/dma-buf/dma-buf.c | 53 +++ include/linux/dma-buf.h | 6 + 2 files changed, 59 insertions(+) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 0427f3b88170..f349613790a6 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -1100,6 +1100,34 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, } EXPORT_SYMBOL_NS_GPL(dma_buf_map_attachment, DMA_BUF); +/** + * dma_buf_map_attachment_unlocked - Returns the scatterlist table of the attachment; + * mapped into _device_ address space. Is a wrapper for map_dma_buf() of the + * dma_buf_ops. + * @attach:[in]attachment whose scatterlist is to be returned + * @direction: [in]direction of DMA transfer + * + * Unlocked variant of dma_buf_map_attachment(). + */ +struct sg_table * +dma_buf_map_attachment_unlocked(struct dma_buf_attachment *attach, + enum dma_data_direction direction) +{ + struct sg_table *sg_table; + + might_sleep(); + + if (WARN_ON(!attach || !attach->dmabuf)) + return ERR_PTR(-EINVAL); + + dma_resv_lock(attach->dmabuf->resv, NULL); + sg_table = dma_buf_map_attachment(attach, direction); + dma_resv_unlock(attach->dmabuf->resv); + + return sg_table; +} +EXPORT_SYMBOL_NS_GPL(dma_buf_map_attachment_unlocked, DMA_BUF); + /** * dma_buf_unmap_attachment - unmaps and decreases usecount of the buffer;might * deallocate the scatterlist associated. Is a wrapper for unmap_dma_buf() of @@ -1136,6 +1164,31 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach, } EXPORT_SYMBOL_NS_GPL(dma_buf_unmap_attachment, DMA_BUF); +/** + * dma_buf_unmap_attachment_unlocked - unmaps and decreases usecount of the buffer;might + * deallocate the scatterlist associated. Is a wrapper for unmap_dma_buf() of + * dma_buf_ops. + * @attach:[in]attachment to unmap buffer from + * @sg_table: [in]scatterlist info of the buffer to unmap + * @direction: [in]direction of DMA transfer + * + * Unlocked variant of dma_buf_unmap_attachment(). + */ +void dma_buf_unmap_attachment_unlocked(struct dma_buf_attachment *attach, + struct sg_table *sg_table, + enum dma_data_direction direction) +{ + might_sleep(); + + if (WARN_ON(!attach || !attach->dmabuf || !sg_table)) + return; + + dma_resv_lock(attach->dmabuf->resv, NULL); + dma_buf_unmap_attachment(attach, sg_table, direction); + dma_resv_unlock(attach->dmabuf->resv); +} +EXPORT_SYMBOL_NS_GPL(dma_buf_unmap_attachment_unlocked, DMA_BUF); + /** * dma_buf_move_notify - notify attachments that DMA-buf is moving * diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 8daa054dd7fe..f11b5bbc2f37 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -627,6 +627,12 @@ int dma_buf_begin_cpu_access(struct dma_buf *dma_buf, enum dma_data_direction dir); int dma_buf_end_cpu_access(struct dma_buf *dma_buf, enum dma_data_direction dir); +struct sg_table * +dma_buf_map_attachment_unlocked(struct dma_buf_attachment *attach, + enum dma_data_direction direction); +void dma_buf_unmap_attachment_unlocked(struct dma_buf_attachment *attach, + struct sg_table *sg_table, + enum dma_data_direction direction); int dma_buf_mmap(struct dma_buf *, struct vm_area_struct *, unsigned long); -- 2.37.3
[PATCH v6 00/21] Move all drivers to a common dma-buf locking convention
Hello, This series moves all drivers to a dynamic dma-buf locking specification. >From now on all dma-buf importers are made responsible for holding dma-buf's reservation lock around all operations performed over dma-bufs in accordance to the locking specification. This allows us to utilize reservation lock more broadly around kernel without fearing of a potential deadlocks. This patchset passes all i915 selftests. It was also tested using VirtIO, Panfrost, Lima, Tegra, udmabuf, AMDGPU and Nouveau drivers. I tested cases of display+GPU, display+V4L and GPU+V4L dma-buf sharing (where appropriate), which covers majority of kernel drivers since rest of the drivers share same or similar code paths. Changelog: v6: - Added r-b from Michael Ruhl to the i915 patch. - Added acks from Sumit Semwal and updated commit message of the "Move dma_buf_vmap() to dynamic locking specification" patch like was suggested by Sumit. - Added "!dmabuf" check to dma_buf_vmap_unlocked() to match the locked variant of the function, for consistency. v5: - Added acks and r-bs that were given to v4. - Changed i915 preparation patch like was suggested by Michael Ruhl. The scope of reservation locking is smaller now. v4: - Added dma_buf_mmap() to the "locking convention" documentation, which was missed by accident in v3. - Added acks from Christian König, Tomasz Figa and Hans Verkuil that they gave to couple v3 patches. - Dropped the "_unlocked" postfix from function names that don't have the locked variant, as was requested by Christian König. - Factored out the per-driver preparations into separate patches to ease reviewing of the changes, which is now doable without the global dma-buf functions renaming. - Factored out the dynamic locking convention enforcements into separate patches which add the final dma_resv_assert_held(dmabuf->resv) to the dma-buf API functions. v3: - Factored out dma_buf_mmap_unlocked() and attachment functions into aseparate patches, like was suggested by Christian König. - Corrected and factored out dma-buf locking documentation into a separate patch, like was suggested by Christian König. - Intel driver dropped the reservation locking fews days ago from its BO-release code path, but we need that locking for the imported GEMs because in the end that code path unmaps the imported GEM. So I added back the locking needed by the imported GEMs, updating the "dma-buf attachment locking specification" patch appropriately. - Tested Nouveau+Intel dma-buf import/export combo. - Tested udmabuf import to i915/Nouveau/AMDGPU. - Fixed few places in Etnaviv, Panfrost and Lima drivers that I missed to switch to locked dma-buf vmapping in the drm/gem: Take reservation lock for vmap/vunmap operations" patch. In a result invalidated the Christian's r-b that he gave to v2. - Added locked dma-buf vmap/vunmap functions that are needed for fixing vmappping of Etnaviv, Panfrost and Lima drivers mentioned above. I actually had this change stashed for the drm-shmem shrinker patchset, but then realized that it's already needed by the dma-buf patches. Also improved my tests to better cover these code paths. v2: - Changed locking specification to avoid problems with a cross-driver ww locking, like was suggested by Christian König. Now the attach/detach callbacks are invoked without the held lock and exporter should take the lock. - Added "locking convention" documentation that explains which dma-buf functions and callbacks are locked/unlocked for importers and exporters, which was requested by Christian König. - Added ack from Tomasz Figa to the V4L patches that he gave to v1. Dmitry Osipenko (21): dma-buf: Add unlocked variant of vmapping functions dma-buf: Add unlocked variant of attachment-mapping functions drm/gem: Take reservation lock for vmap/vunmap operations drm/prime: Prepare to dynamic dma-buf locking specification drm/armada: Prepare to dynamic dma-buf locking specification drm/i915: Prepare to dynamic dma-buf locking specification drm/omapdrm: Prepare to dynamic dma-buf locking specification drm/tegra: Prepare to dynamic dma-buf locking specification drm/etnaviv: Prepare to dynamic dma-buf locking specification RDMA/umem: Prepare to dynamic dma-buf locking specification misc: fastrpc: Prepare to dynamic dma-buf locking specification xen/gntdev: Prepare to dynamic dma-buf locking specification media: videobuf2: Prepare to dynamic dma-buf locking specification media: tegra-vde: Prepare to dynamic dma-buf locking specification dma-buf: Move dma_buf_vmap() to dynamic locking specification dma-buf: Move dma_buf_attach() to dynamic locking specification dma-buf: Move dma_buf_map_attachment() to dynamic locking specification dma-buf: Move dma
Re: [PATCH v5] drm/sched: Add FIFO sched policy to run queue
Inlined: On 2022-09-28 14:49, Andrey Grodzovsky wrote: > When many entities are competing for the same run queue > on the same scheduler, we observe an unusually long wait > times and some jobs get starved. This has been observed on GPUVis. > > The issue is due to the Round Robin policy used by schedulers > to pick up the next entity's job queue for execution. Under stress > of many entities and long job queues within entity some > jobs could be stuck for very long time in it's entity's > queue before being popped from the queue and executed > while for other entities with smaller job queues a job > might execute earlier even though that job arrived later > then the job in the long queue. > > Fix: > Add FIFO selection policy to entities in run queue, chose next entity > on run queue in such order that if job on one entity arrived > earlier then job on another entity the first job will start > executing earlier regardless of the length of the entity's job > queue. > > v2: > Switch to rb tree structure for entities based on TS of > oldest job waiting in the job queue of an entity. Improves next > entity extraction to O(1). Entity TS update > O(log N) where N is the number of entities in the run-queue > > Drop default option in module control parameter. > > v3: > Various cosmetical fixes and minor refactoring of fifo update function. > (Luben) "Cosmetic" > > v4: > Switch drm_sched_rq_select_entity_fifo to in order search (Luben) > > v5: Fix up drm_sched_rq_select_entity_fifo loop v5 is actually a major major update of the whole patch and working of the patch. It fixes the entity selection from ready-first, timestamp-second, to oldest-first and ready-second, i.e. oldest-ready entity, as I suggested. The v5 blurb should read: v5: Fix drm_sched_rq_select_entity_fifo loop from reordering and timestamping on every selection, and picking the last ready first, timestamp-second, to picking the oldest-ready entity. No reinsertion. (Luben) > > Signed-off-by: Andrey Grodzovsky > Tested-by: Li Yunxiang (Teddy) > --- > drivers/gpu/drm/scheduler/sched_entity.c | 26 ++- > drivers/gpu/drm/scheduler/sched_main.c | 99 +++- > include/drm/gpu_scheduler.h | 32 > 3 files changed, 151 insertions(+), 6 deletions(-) > > diff --git a/drivers/gpu/drm/scheduler/sched_entity.c > b/drivers/gpu/drm/scheduler/sched_entity.c > index 6b25b2f4f5a3..f3ffce3c9304 100644 > --- a/drivers/gpu/drm/scheduler/sched_entity.c > +++ b/drivers/gpu/drm/scheduler/sched_entity.c > @@ -73,6 +73,7 @@ int drm_sched_entity_init(struct drm_sched_entity *entity, > entity->priority = priority; > entity->sched_list = num_sched_list > 1 ? sched_list : NULL; > entity->last_scheduled = NULL; > + RB_CLEAR_NODE(&entity->rb_tree_node); > > if(num_sched_list) > entity->rq = &sched_list[0]->sched_rq[entity->priority]; > @@ -417,14 +418,16 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct > drm_sched_entity *entity) > > sched_job = to_drm_sched_job(spsc_queue_peek(&entity->job_queue)); > if (!sched_job) > - return NULL; > + goto skip; > > while ((entity->dependency = > drm_sched_job_dependency(sched_job, entity))) { > trace_drm_sched_job_wait_dep(sched_job, entity->dependency); > > - if (drm_sched_entity_add_dependency_cb(entity)) > - return NULL; > + if (drm_sched_entity_add_dependency_cb(entity)) { > + sched_job = NULL; > + goto skip; > + } > } > > /* skip jobs from entity that marked guilty */ > @@ -443,6 +446,16 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct > drm_sched_entity *entity) > smp_wmb(); > > spsc_queue_pop(&entity->job_queue); > + > + /* > + * It's when head job is extracted we can access the next job (or empty) > + * queue and update the entity location in the min heap accordingly. > + */ > +skip: > + if (drm_sched_policy == DRM_SCHED_POLICY_FIFO) > + drm_sched_rq_update_fifo(entity, > + (sched_job ? sched_job->submit_ts : > ktime_get())); > + > return sched_job; > } > > @@ -502,11 +515,13 @@ void drm_sched_entity_push_job(struct drm_sched_job > *sched_job) > { > struct drm_sched_entity *entity = sched_job->entity; > bool first; > + ktime_t ts = ktime_get(); > > trace_drm_sched_job(sched_job, entity); > atomic_inc(entity->rq->sched->score); > WRITE_ONCE(entity->last_user, current->group_leader); > first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node); > + sched_job->submit_ts = ts; > > /* first job wakes up scheduler */ > if (first) { > @@ -518,8 +533,13 @@ void drm_sched_entity_push_job(struct drm_sched_job > *sched_job) > DRM_ERR