Re: [PATCH 1/3] pwm: Change prototype of .get_state() callback to return an error

2022-09-28 Thread Uwe Kleine-König
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

2022-09-28 Thread Oded Gabbay
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

2022-09-28 Thread liweishi
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)

2022-09-28 Thread Randy Dunlap



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()

2022-09-28 Thread 葉星辰
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

2022-09-28 Thread Niranjana Vishwanathapura

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

2022-09-28 Thread Niranjana Vishwanathapura

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()

2022-09-28 Thread Niranjana Vishwanathapura

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()

2022-09-28 Thread Niranjana Vishwanathapura

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

2022-09-28 Thread Michael Ellerman
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

2022-09-28 Thread Christian Zigotzky
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()

2022-09-28 Thread kernel test robot
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

2022-09-28 Thread John . C . Harrison
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

2022-09-28 Thread John . C . Harrison
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

2022-09-28 Thread John . C . Harrison
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

2022-09-28 Thread John . C . Harrison
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

2022-09-28 Thread John . C . Harrison
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

2022-09-28 Thread Yuan Can
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

2022-09-28 Thread Yuan Can
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

2022-09-28 Thread Yuan Can
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

2022-09-28 Thread Alistair Popple


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 ***

2022-09-28 Thread allen
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

2022-09-28 Thread allen
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

2022-09-28 Thread allen
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

2022-09-28 Thread Jason Yen
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-09-28 Thread Yuan Can



在 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

2022-09-28 Thread Stephen Boyd
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

2022-09-28 Thread Michael Ellerman
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

2022-09-28 Thread Kuogee Hsieh
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

2022-09-28 Thread 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.

-Doug


Re: [PATCH 02/10] drm/panel: panel-edp: Use dev_err_probe() to simplify code

2022-09-28 Thread Doug Anderson
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

2022-09-28 Thread Laurent Pinchart
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()

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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()

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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()

2022-09-28 Thread Laurent Pinchart
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()

2022-09-28 Thread Laurent Pinchart
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()

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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()

2022-09-28 Thread Laurent Pinchart
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()

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Laurent Pinchart
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

2022-09-28 Thread Vlastimil Babka

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

2022-09-28 Thread Lyude Paul
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

2022-09-28 Thread Lyude Paul
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

2022-09-28 Thread Lyude Paul
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+

2022-09-28 Thread Rodrigo Vivi
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

2022-09-28 Thread Welty, Brian




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

2022-09-28 Thread Aradhya Bhatia
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

2022-09-28 Thread Aradhya Bhatia
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Dmitry Osipenko
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

2022-09-28 Thread Luben Tuikov
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

  1   2   3   >