]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 5.19
authorSasha Levin <sashal@kernel.org>
Fri, 23 Sep 2022 16:54:09 +0000 (12:54 -0400)
committerSasha Levin <sashal@kernel.org>
Fri, 23 Sep 2022 16:54:09 +0000 (12:54 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
28 files changed:
queue-5.19/block-remove-queue_flag_dead.patch [new file with mode: 0644]
queue-5.19/block-simplify-disk-shutdown.patch [new file with mode: 0644]
queue-5.19/block-stop-setting-the-nomerges-flags-in-blk_cleanup.patch [new file with mode: 0644]
queue-5.19/drm-i915-bios-split-parse_driver_features-into-two-p.patch [new file with mode: 0644]
queue-5.19/drm-i915-bios-split-vbt-data-into-per-panel-vs.-glob.patch [new file with mode: 0644]
queue-5.19/drm-i915-bios-split-vbt-parsing-to-global-vs.-panel-.patch [new file with mode: 0644]
queue-5.19/drm-i915-dsi-filter-invalid-backlight-and-cabc-ports.patch [new file with mode: 0644]
queue-5.19/drm-i915-dsi-fix-dual-link-dsi-backlight-and-cabc-po.patch [new file with mode: 0644]
queue-5.19/drm-i915-extract-intel_edp_fixup_vbt_bpp.patch [new file with mode: 0644]
queue-5.19/drm-i915-pps-split-pps_init_delays-into-distinct-par.patch [new file with mode: 0644]
queue-5.19/net-mvpp2-debugfs-fix-memory-leak-when-using-debugfs.patch [new file with mode: 0644]
queue-5.19/revert-usb-add-quirks-for-lenovo-onelink-dock.patch [new file with mode: 0644]
queue-5.19/revert-usb-gadget-udc-xilinx-replace-memcpy-with-mem.patch [new file with mode: 0644]
queue-5.19/scsi-core-fix-a-use-after-free.patch [new file with mode: 0644]
queue-5.19/series [new file with mode: 0644]
queue-5.19/smb3-fix-temporary-data-corruption-in-collapse-range.patch [new file with mode: 0644]
queue-5.19/smb3-fix-temporary-data-corruption-in-insert-range.patch [new file with mode: 0644]
queue-5.19/smb3-move-the-flush-out-of-smb2_copychunk_range-into.patch [new file with mode: 0644]
queue-5.19/smb3-use-filemap_write_and_wait_range-instead-of-fil.patch [new file with mode: 0644]
queue-5.19/usb-add-quirks-for-lenovo-onelink-dock.patch [new file with mode: 0644]
queue-5.19/usb-dwc3-qcom-add-helper-functions-to-enable-disable.patch [new file with mode: 0644]
queue-5.19/usb-dwc3-qcom-configure-wakeup-interrupts-during-sus.patch [new file with mode: 0644]
queue-5.19/usb-dwc3-qcom-fix-gadget-only-builds.patch [new file with mode: 0644]
queue-5.19/usb-dwc3-qcom-fix-peripheral-and-otg-suspend.patch [new file with mode: 0644]
queue-5.19/usb-dwc3-qcom-fix-runtime-pm-wakeup.patch [new file with mode: 0644]
queue-5.19/usb-dwc3-qcom-fix-use-after-free-on-runtime-pm-wakeu.patch [new file with mode: 0644]
queue-5.19/usb-gadget-udc-xilinx-replace-memcpy-with-memcpy_toi.patch [new file with mode: 0644]
queue-5.19/xfrm-fix-xfrma_lastused-comment.patch [new file with mode: 0644]

diff --git a/queue-5.19/block-remove-queue_flag_dead.patch b/queue-5.19/block-remove-queue_flag_dead.patch
new file mode 100644 (file)
index 0000000..5b58a09
--- /dev/null
@@ -0,0 +1,89 @@
+From 7de7e0ebef49d82376f8829aa3ea9e6c790b950f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jun 2022 08:05:49 +0200
+Subject: block: remove QUEUE_FLAG_DEAD
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ Upstream commit 1f90307e5f0d7bc9a336ead528f616a5df8e5944 ]
+
+Disallow setting the blk-mq state on any queue that is already dying as
+setting the state even then is a bad idea, and remove the now unused
+QUEUE_FLAG_DEAD flag.
+
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Hannes Reinecke <hare@suse.de>
+Link: https://lore.kernel.org/r/20220619060552.1850436-4-hch@lst.de
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Stable-dep-of: 8fe4ce5836e9 ("scsi: core: Fix a use-after-free")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ block/blk-core.c       | 3 ---
+ block/blk-mq-debugfs.c | 8 +++-----
+ include/linux/blkdev.h | 2 --
+ 3 files changed, 3 insertions(+), 10 deletions(-)
+
+diff --git a/block/blk-core.c b/block/blk-core.c
+index cc6fbcb6d252..76f070c3a3b0 100644
+--- a/block/blk-core.c
++++ b/block/blk-core.c
+@@ -313,9 +313,6 @@ void blk_cleanup_queue(struct request_queue *q)
+        * after draining finished.
+        */
+       blk_freeze_queue(q);
+-
+-      blk_queue_flag_set(QUEUE_FLAG_DEAD, q);
+-
+       blk_sync_queue(q);
+       if (queue_is_mq(q)) {
+               blk_mq_cancel_work_sync(q);
+diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c
+index 61f179e5f151..28adb01f6441 100644
+--- a/block/blk-mq-debugfs.c
++++ b/block/blk-mq-debugfs.c
+@@ -116,7 +116,6 @@ static const char *const blk_queue_flag_name[] = {
+       QUEUE_FLAG_NAME(NOXMERGES),
+       QUEUE_FLAG_NAME(ADD_RANDOM),
+       QUEUE_FLAG_NAME(SAME_FORCE),
+-      QUEUE_FLAG_NAME(DEAD),
+       QUEUE_FLAG_NAME(INIT_DONE),
+       QUEUE_FLAG_NAME(STABLE_WRITES),
+       QUEUE_FLAG_NAME(POLL),
+@@ -151,11 +150,10 @@ static ssize_t queue_state_write(void *data, const char __user *buf,
+       char opbuf[16] = { }, *op;
+       /*
+-       * The "state" attribute is removed after blk_cleanup_queue() has called
+-       * blk_mq_free_queue(). Return if QUEUE_FLAG_DEAD has been set to avoid
+-       * triggering a use-after-free.
++       * The "state" attribute is removed when the queue is removed.  Don't
++       * allow setting the state on a dying queue to avoid a use-after-free.
+        */
+-      if (blk_queue_dead(q))
++      if (blk_queue_dying(q))
+               return -ENOENT;
+       if (count >= sizeof(opbuf)) {
+diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
+index 62e3ff52ab03..76f77eed58c3 100644
+--- a/include/linux/blkdev.h
++++ b/include/linux/blkdev.h
+@@ -559,7 +559,6 @@ struct request_queue {
+ #define QUEUE_FLAG_NOXMERGES  9       /* No extended merges */
+ #define QUEUE_FLAG_ADD_RANDOM 10      /* Contributes to random pool */
+ #define QUEUE_FLAG_SAME_FORCE 12      /* force complete on same CPU */
+-#define QUEUE_FLAG_DEAD               13      /* queue tear-down finished */
+ #define QUEUE_FLAG_INIT_DONE  14      /* queue is initialized */
+ #define QUEUE_FLAG_STABLE_WRITES 15   /* don't modify blks until WB is done */
+ #define QUEUE_FLAG_POLL               16      /* IO polling enabled if set */
+@@ -587,7 +586,6 @@ bool blk_queue_flag_test_and_set(unsigned int flag, struct request_queue *q);
+ #define blk_queue_stopped(q)  test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags)
+ #define blk_queue_dying(q)    test_bit(QUEUE_FLAG_DYING, &(q)->queue_flags)
+ #define blk_queue_has_srcu(q) test_bit(QUEUE_FLAG_HAS_SRCU, &(q)->queue_flags)
+-#define blk_queue_dead(q)     test_bit(QUEUE_FLAG_DEAD, &(q)->queue_flags)
+ #define blk_queue_init_done(q)        test_bit(QUEUE_FLAG_INIT_DONE, &(q)->queue_flags)
+ #define blk_queue_nomerges(q) test_bit(QUEUE_FLAG_NOMERGES, &(q)->queue_flags)
+ #define blk_queue_noxmerges(q)        \
+-- 
+2.35.1
+
diff --git a/queue-5.19/block-simplify-disk-shutdown.patch b/queue-5.19/block-simplify-disk-shutdown.patch
new file mode 100644 (file)
index 0000000..b10cf53
--- /dev/null
@@ -0,0 +1,859 @@
+From dc7d332c13cc0d553c6918d75a70f4166abc3883 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jun 2022 08:05:51 +0200
+Subject: block: simplify disk shutdown
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ Upstream commit 6f8191fdf41d3a53cc1d63fe2234e812c55a0092 ]
+
+Set the queue dying flag and call blk_mq_exit_queue from del_gendisk for
+all disks that do not have separately allocated queues, and thus remove
+the need to call blk_cleanup_queue for them.
+
+Rename blk_cleanup_disk to blk_mq_destroy_queue to make it clear that
+this function is intended only for separately allocated blk-mq queues.
+
+This saves an extra queue freeze for devices without a separately
+allocated queue.
+
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Hannes Reinecke <hare@suse.de>
+Link: https://lore.kernel.org/r/20220619060552.1850436-6-hch@lst.de
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Stable-dep-of: 8fe4ce5836e9 ("scsi: core: Fix a use-after-free")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ block/blk-core.c                    | 37 -------------------------
+ block/blk-mq.c                      | 43 +++++++++++++++++++++++++++--
+ block/blk-sysfs.c                   |  5 ----
+ block/blk.h                         |  3 ++
+ block/bsg-lib.c                     |  4 +--
+ block/genhd.c                       | 23 ++++++++-------
+ drivers/block/ataflop.c             |  1 -
+ drivers/block/loop.c                |  1 -
+ drivers/block/mtip32xx/mtip32xx.c   |  2 --
+ drivers/block/rnbd/rnbd-clt.c       |  2 +-
+ drivers/block/sx8.c                 |  4 +--
+ drivers/block/virtio_blk.c          |  1 -
+ drivers/block/z2ram.c               |  1 -
+ drivers/cdrom/gdrom.c               |  1 -
+ drivers/memstick/core/ms_block.c    |  1 -
+ drivers/memstick/core/mspro_block.c |  1 -
+ drivers/mmc/core/block.c            |  1 -
+ drivers/mmc/core/queue.c            |  1 -
+ drivers/nvme/host/apple.c           |  2 +-
+ drivers/nvme/host/core.c            |  1 -
+ drivers/nvme/host/fc.c              | 12 ++++----
+ drivers/nvme/host/pci.c             |  2 +-
+ drivers/nvme/host/rdma.c            | 12 ++++----
+ drivers/nvme/host/tcp.c             | 12 ++++----
+ drivers/nvme/target/loop.c          | 12 ++++----
+ drivers/s390/block/dasd.c           |  2 +-
+ drivers/s390/block/dasd_genhd.c     |  4 +--
+ drivers/scsi/scsi_lib.c             |  6 ++--
+ drivers/scsi/scsi_sysfs.c           |  2 +-
+ drivers/scsi/sd.c                   |  4 +--
+ drivers/scsi/sr.c                   |  4 +--
+ drivers/ufs/core/ufshcd.c           |  4 +--
+ include/linux/blk-mq.h              |  3 ++
+ include/linux/blkdev.h              |  4 +--
+ 34 files changed, 105 insertions(+), 113 deletions(-)
+
+diff --git a/block/blk-core.c b/block/blk-core.c
+index b8083decc07f..7743c68177e8 100644
+--- a/block/blk-core.c
++++ b/block/blk-core.c
+@@ -284,43 +284,6 @@ void blk_queue_start_drain(struct request_queue *q)
+       wake_up_all(&q->mq_freeze_wq);
+ }
+-/**
+- * blk_cleanup_queue - shutdown a request queue
+- * @q: request queue to shutdown
+- *
+- * Mark @q DYING, drain all pending requests, mark @q DEAD, destroy and
+- * put it.  All future requests will be failed immediately with -ENODEV.
+- *
+- * Context: can sleep
+- */
+-void blk_cleanup_queue(struct request_queue *q)
+-{
+-      /* cannot be called from atomic context */
+-      might_sleep();
+-
+-      WARN_ON_ONCE(blk_queue_registered(q));
+-
+-      /* mark @q DYING, no new request or merges will be allowed afterwards */
+-      blk_queue_flag_set(QUEUE_FLAG_DYING, q);
+-      blk_queue_start_drain(q);
+-
+-      /*
+-       * Drain all requests queued before DYING marking. Set DEAD flag to
+-       * prevent that blk_mq_run_hw_queues() accesses the hardware queues
+-       * after draining finished.
+-       */
+-      blk_freeze_queue(q);
+-      blk_sync_queue(q);
+-      if (queue_is_mq(q)) {
+-              blk_mq_cancel_work_sync(q);
+-              blk_mq_exit_queue(q);
+-      }
+-
+-      /* @q is and will stay empty, shutdown and put */
+-      blk_put_queue(q);
+-}
+-EXPORT_SYMBOL(blk_cleanup_queue);
+-
+ /**
+  * blk_queue_enter() - try to increase q->q_usage_counter
+  * @q: request queue pointer
+diff --git a/block/blk-mq.c b/block/blk-mq.c
+index 0a299941c622..6e22700dd6cf 100644
+--- a/block/blk-mq.c
++++ b/block/blk-mq.c
+@@ -3896,7 +3896,7 @@ static struct request_queue *blk_mq_init_queue_data(struct blk_mq_tag_set *set,
+       q->queuedata = queuedata;
+       ret = blk_mq_init_allocated_queue(set, q);
+       if (ret) {
+-              blk_cleanup_queue(q);
++              blk_put_queue(q);
+               return ERR_PTR(ret);
+       }
+       return q;
+@@ -3908,6 +3908,35 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set)
+ }
+ EXPORT_SYMBOL(blk_mq_init_queue);
++/**
++ * blk_mq_destroy_queue - shutdown a request queue
++ * @q: request queue to shutdown
++ *
++ * This shuts down a request queue allocated by blk_mq_init_queue() and drops
++ * the initial reference.  All future requests will failed with -ENODEV.
++ *
++ * Context: can sleep
++ */
++void blk_mq_destroy_queue(struct request_queue *q)
++{
++      WARN_ON_ONCE(!queue_is_mq(q));
++      WARN_ON_ONCE(blk_queue_registered(q));
++
++      might_sleep();
++
++      blk_queue_flag_set(QUEUE_FLAG_DYING, q);
++      blk_queue_start_drain(q);
++      blk_freeze_queue(q);
++
++      blk_sync_queue(q);
++      blk_mq_cancel_work_sync(q);
++      blk_mq_exit_queue(q);
++
++      /* @q is and will stay empty, shutdown and put */
++      blk_put_queue(q);
++}
++EXPORT_SYMBOL(blk_mq_destroy_queue);
++
+ struct gendisk *__blk_mq_alloc_disk(struct blk_mq_tag_set *set, void *queuedata,
+               struct lock_class_key *lkclass)
+ {
+@@ -3920,13 +3949,23 @@ struct gendisk *__blk_mq_alloc_disk(struct blk_mq_tag_set *set, void *queuedata,
+       disk = __alloc_disk_node(q, set->numa_node, lkclass);
+       if (!disk) {
+-              blk_cleanup_queue(q);
++              blk_put_queue(q);
+               return ERR_PTR(-ENOMEM);
+       }
++      set_bit(GD_OWNS_QUEUE, &disk->state);
+       return disk;
+ }
+ EXPORT_SYMBOL(__blk_mq_alloc_disk);
++struct gendisk *blk_mq_alloc_disk_for_queue(struct request_queue *q,
++              struct lock_class_key *lkclass)
++{
++      if (!blk_get_queue(q))
++              return NULL;
++      return __alloc_disk_node(q, NUMA_NO_NODE, lkclass);
++}
++EXPORT_SYMBOL(blk_mq_alloc_disk_for_queue);
++
+ static struct blk_mq_hw_ctx *blk_mq_alloc_and_init_hctx(
+               struct blk_mq_tag_set *set, struct request_queue *q,
+               int hctx_idx, int node)
+diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
+index 9b905e9443e4..84d7f8701567 100644
+--- a/block/blk-sysfs.c
++++ b/block/blk-sysfs.c
+@@ -748,11 +748,6 @@ static void blk_free_queue_rcu(struct rcu_head *rcu_head)
+  * decremented with blk_put_queue(). Once the refcount reaches 0 this function
+  * is called.
+  *
+- * For drivers that have a request_queue on a gendisk and added with
+- * __device_add_disk() the refcount to request_queue will reach 0 with
+- * the last put_disk() called by the driver. For drivers which don't use
+- * __device_add_disk() this happens with blk_cleanup_queue().
+- *
+  * Drivers exist which depend on the release of the request_queue to be
+  * synchronous, it should not be deferred.
+  *
+diff --git a/block/blk.h b/block/blk.h
+index 434017701403..0d6668663ab5 100644
+--- a/block/blk.h
++++ b/block/blk.h
+@@ -411,6 +411,9 @@ int bdev_resize_partition(struct gendisk *disk, int partno, sector_t start,
+               sector_t length);
+ void blk_drop_partitions(struct gendisk *disk);
++struct gendisk *__alloc_disk_node(struct request_queue *q, int node_id,
++              struct lock_class_key *lkclass);
++
+ int bio_add_hw_page(struct request_queue *q, struct bio *bio,
+               struct page *page, unsigned int len, unsigned int offset,
+               unsigned int max_sectors, bool *same_page);
+diff --git a/block/bsg-lib.c b/block/bsg-lib.c
+index acfe1357bf6c..fd4cd5e68282 100644
+--- a/block/bsg-lib.c
++++ b/block/bsg-lib.c
+@@ -324,7 +324,7 @@ void bsg_remove_queue(struct request_queue *q)
+                       container_of(q->tag_set, struct bsg_set, tag_set);
+               bsg_unregister_queue(bset->bd);
+-              blk_cleanup_queue(q);
++              blk_mq_destroy_queue(q);
+               blk_mq_free_tag_set(&bset->tag_set);
+               kfree(bset);
+       }
+@@ -399,7 +399,7 @@ struct request_queue *bsg_setup_queue(struct device *dev, const char *name,
+       return q;
+ out_cleanup_queue:
+-      blk_cleanup_queue(q);
++      blk_mq_destroy_queue(q);
+ out_queue:
+       blk_mq_free_tag_set(set);
+ out_tag_set:
+diff --git a/block/genhd.c b/block/genhd.c
+index 278227ba1d53..4d15f828c449 100644
+--- a/block/genhd.c
++++ b/block/genhd.c
+@@ -617,6 +617,8 @@ void del_gendisk(struct gendisk *disk)
+        * Fail any new I/O.
+        */
+       set_bit(GD_DEAD, &disk->state);
++      if (test_bit(GD_OWNS_QUEUE, &disk->state))
++              blk_queue_flag_set(QUEUE_FLAG_DYING, q);
+       set_capacity(disk, 0);
+       /*
+@@ -663,11 +665,16 @@ void del_gendisk(struct gendisk *disk)
+       blk_mq_unquiesce_queue(q);
+       /*
+-       * Allow using passthrough request again after the queue is torn down.
++       * If the disk does not own the queue, allow using passthrough requests
++       * again.  Else leave the queue frozen to fail all I/O.
+        */
+-      blk_queue_flag_clear(QUEUE_FLAG_INIT_DONE, q);
+-      __blk_mq_unfreeze_queue(q, true);
+-
++      if (!test_bit(GD_OWNS_QUEUE, &disk->state)) {
++              blk_queue_flag_clear(QUEUE_FLAG_INIT_DONE, q);
++              __blk_mq_unfreeze_queue(q, true);
++      } else {
++              if (queue_is_mq(q))
++                      blk_mq_exit_queue(q);
++      }
+ }
+ EXPORT_SYMBOL(del_gendisk);
+@@ -1338,9 +1345,6 @@ struct gendisk *__alloc_disk_node(struct request_queue *q, int node_id,
+ {
+       struct gendisk *disk;
+-      if (!blk_get_queue(q))
+-              return NULL;
+-
+       disk = kzalloc_node(sizeof(struct gendisk), GFP_KERNEL, node_id);
+       if (!disk)
+               goto out_put_queue;
+@@ -1391,7 +1395,6 @@ struct gendisk *__alloc_disk_node(struct request_queue *q, int node_id,
+       blk_put_queue(q);
+       return NULL;
+ }
+-EXPORT_SYMBOL(__alloc_disk_node);
+ struct gendisk *__blk_alloc_disk(int node, struct lock_class_key *lkclass)
+ {
+@@ -1404,9 +1407,10 @@ struct gendisk *__blk_alloc_disk(int node, struct lock_class_key *lkclass)
+       disk = __alloc_disk_node(q, node, lkclass);
+       if (!disk) {
+-              blk_cleanup_queue(q);
++              blk_put_queue(q);
+               return NULL;
+       }
++      set_bit(GD_OWNS_QUEUE, &disk->state);
+       return disk;
+ }
+ EXPORT_SYMBOL(__blk_alloc_disk);
+@@ -1439,7 +1443,6 @@ EXPORT_SYMBOL(put_disk);
+  */
+ void blk_cleanup_disk(struct gendisk *disk)
+ {
+-      blk_cleanup_queue(disk->queue);
+       put_disk(disk);
+ }
+ EXPORT_SYMBOL(blk_cleanup_disk);
+diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c
+index e232cc4fd444..c6e41ee18aaa 100644
+--- a/drivers/block/ataflop.c
++++ b/drivers/block/ataflop.c
+@@ -2045,7 +2045,6 @@ static void atari_floppy_cleanup(void)
+                       if (!unit[i].disk[type])
+                               continue;
+                       del_gendisk(unit[i].disk[type]);
+-                      blk_cleanup_queue(unit[i].disk[type]->queue);
+                       put_disk(unit[i].disk[type]);
+               }
+               blk_mq_free_tag_set(&unit[i].tag_set);
+diff --git a/drivers/block/loop.c b/drivers/block/loop.c
+index a59910ef948e..1c036ef686fb 100644
+--- a/drivers/block/loop.c
++++ b/drivers/block/loop.c
+@@ -2062,7 +2062,6 @@ static void loop_remove(struct loop_device *lo)
+ {
+       /* Make this loop device unreachable from pathname. */
+       del_gendisk(lo->lo_disk);
+-      blk_cleanup_queue(lo->lo_disk->queue);
+       blk_mq_free_tag_set(&lo->tag_set);
+       mutex_lock(&loop_ctl_mutex);
+diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
+index 6699e4b2f7f4..06994a35acc7 100644
+--- a/drivers/block/mtip32xx/mtip32xx.c
++++ b/drivers/block/mtip32xx/mtip32xx.c
+@@ -3677,7 +3677,6 @@ static int mtip_block_shutdown(struct driver_data *dd)
+       if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag))
+               del_gendisk(dd->disk);
+-      blk_cleanup_queue(dd->queue);
+       blk_mq_free_tag_set(&dd->tags);
+       put_disk(dd->disk);
+       return 0;
+@@ -4040,7 +4039,6 @@ static void mtip_pci_remove(struct pci_dev *pdev)
+               dev_info(&dd->pdev->dev, "device %s surprise removal\n",
+                                               dd->disk->disk_name);
+-      blk_cleanup_queue(dd->queue);
+       blk_mq_free_tag_set(&dd->tags);
+       /* De-initialize the protocol layer. */
+diff --git a/drivers/block/rnbd/rnbd-clt.c b/drivers/block/rnbd/rnbd-clt.c
+index 409c76b81aed..a4470374f54f 100644
+--- a/drivers/block/rnbd/rnbd-clt.c
++++ b/drivers/block/rnbd/rnbd-clt.c
+@@ -1755,7 +1755,7 @@ static void rnbd_destroy_sessions(void)
+               list_for_each_entry_safe(dev, tn, &sess->devs_list, list) {
+                       /*
+                        * Here unmap happens in parallel for only one reason:
+-                       * blk_cleanup_queue() takes around half a second, so
++                       * del_gendisk() takes around half a second, so
+                        * on huge amount of devices the whole module unload
+                        * procedure takes minutes.
+                        */
+diff --git a/drivers/block/sx8.c b/drivers/block/sx8.c
+index 63b4f6431d2e..75057dbbcfbe 100644
+--- a/drivers/block/sx8.c
++++ b/drivers/block/sx8.c
+@@ -1536,7 +1536,7 @@ static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+               clear_bit(0, &carm_major_alloc);
+       else if (host->major == 161)
+               clear_bit(1, &carm_major_alloc);
+-      blk_cleanup_queue(host->oob_q);
++      blk_mq_destroy_queue(host->oob_q);
+       blk_mq_free_tag_set(&host->tag_set);
+ err_out_dma_free:
+       dma_free_coherent(&pdev->dev, CARM_SHM_SIZE, host->shm, host->shm_dma);
+@@ -1570,7 +1570,7 @@ static void carm_remove_one (struct pci_dev *pdev)
+               clear_bit(0, &carm_major_alloc);
+       else if (host->major == 161)
+               clear_bit(1, &carm_major_alloc);
+-      blk_cleanup_queue(host->oob_q);
++      blk_mq_destroy_queue(host->oob_q);
+       blk_mq_free_tag_set(&host->tag_set);
+       dma_free_coherent(&pdev->dev, CARM_SHM_SIZE, host->shm, host->shm_dma);
+       iounmap(host->mmio);
+diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
+index d756423e0059..59d6d5faf739 100644
+--- a/drivers/block/virtio_blk.c
++++ b/drivers/block/virtio_blk.c
+@@ -1107,7 +1107,6 @@ static void virtblk_remove(struct virtio_device *vdev)
+       flush_work(&vblk->config_work);
+       del_gendisk(vblk->disk);
+-      blk_cleanup_queue(vblk->disk->queue);
+       blk_mq_free_tag_set(&vblk->tag_set);
+       mutex_lock(&vblk->vdev_mutex);
+diff --git a/drivers/block/z2ram.c b/drivers/block/z2ram.c
+index 7a6ed83481b8..18ad43d9933e 100644
+--- a/drivers/block/z2ram.c
++++ b/drivers/block/z2ram.c
+@@ -384,7 +384,6 @@ static void __exit z2_exit(void)
+       for (i = 0; i < Z2MINOR_COUNT; i++) {
+               del_gendisk(z2ram_gendisk[i]);
+-              blk_cleanup_queue(z2ram_gendisk[i]->queue);
+               put_disk(z2ram_gendisk[i]);
+       }
+       blk_mq_free_tag_set(&tag_set);
+diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c
+index 8e78b37d0f6a..f4cc90ea6198 100644
+--- a/drivers/cdrom/gdrom.c
++++ b/drivers/cdrom/gdrom.c
+@@ -831,7 +831,6 @@ static int probe_gdrom(struct platform_device *devptr)
+ static int remove_gdrom(struct platform_device *devptr)
+ {
+-      blk_cleanup_queue(gd.gdrom_rq);
+       blk_mq_free_tag_set(&gd.tag_set);
+       free_irq(HW_EVENT_GDROM_CMD, &gd);
+       free_irq(HW_EVENT_GDROM_DMA, &gd);
+diff --git a/drivers/memstick/core/ms_block.c b/drivers/memstick/core/ms_block.c
+index f8fdf88fb240..ecbc46714e68 100644
+--- a/drivers/memstick/core/ms_block.c
++++ b/drivers/memstick/core/ms_block.c
+@@ -2188,7 +2188,6 @@ static void msb_remove(struct memstick_dev *card)
+       /* Remove the disk */
+       del_gendisk(msb->disk);
+-      blk_cleanup_queue(msb->queue);
+       blk_mq_free_tag_set(&msb->tag_set);
+       msb->queue = NULL;
+diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c
+index 725ba74ded30..72e91c06c618 100644
+--- a/drivers/memstick/core/mspro_block.c
++++ b/drivers/memstick/core/mspro_block.c
+@@ -1294,7 +1294,6 @@ static void mspro_block_remove(struct memstick_dev *card)
+       del_gendisk(msb->disk);
+       dev_dbg(&card->dev, "mspro block remove\n");
+-      blk_cleanup_queue(msb->queue);
+       blk_mq_free_tag_set(&msb->tag_set);
+       msb->queue = NULL;
+diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
+index 912a398a9a76..2f89ae55c177 100644
+--- a/drivers/mmc/core/block.c
++++ b/drivers/mmc/core/block.c
+@@ -2509,7 +2509,6 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
+       return md;
+  err_cleanup_queue:
+-      blk_cleanup_queue(md->disk->queue);
+       blk_mq_free_tag_set(&md->queue.tag_set);
+  err_kfree:
+       kfree(md);
+diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c
+index fa5324ceeebe..f824cfdab75a 100644
+--- a/drivers/mmc/core/queue.c
++++ b/drivers/mmc/core/queue.c
+@@ -494,7 +494,6 @@ void mmc_cleanup_queue(struct mmc_queue *mq)
+       if (blk_queue_quiesced(q))
+               blk_mq_unquiesce_queue(q);
+-      blk_cleanup_queue(q);
+       blk_mq_free_tag_set(&mq->tag_set);
+       /*
+diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c
+index d702d7d60235..2d23b7d41f7e 100644
+--- a/drivers/nvme/host/apple.c
++++ b/drivers/nvme/host/apple.c
+@@ -1502,7 +1502,7 @@ static int apple_nvme_probe(struct platform_device *pdev)
+       if (!blk_get_queue(anv->ctrl.admin_q)) {
+               nvme_start_admin_queue(&anv->ctrl);
+-              blk_cleanup_queue(anv->ctrl.admin_q);
++              blk_mq_destroy_queue(anv->ctrl.admin_q);
+               anv->ctrl.admin_q = NULL;
+               ret = -ENODEV;
+               goto put_dev;
+diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
+index 2f965356f345..6d76fc608b74 100644
+--- a/drivers/nvme/host/core.c
++++ b/drivers/nvme/host/core.c
+@@ -4105,7 +4105,6 @@ static void nvme_ns_remove(struct nvme_ns *ns)
+       if (!nvme_ns_head_multipath(ns->head))
+               nvme_cdev_del(&ns->cdev, &ns->cdev_device);
+       del_gendisk(ns->disk);
+-      blk_cleanup_queue(ns->queue);
+       down_write(&ns->ctrl->namespaces_rwsem);
+       list_del_init(&ns->list);
+diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
+index 4aff83b1b0c0..9a5ce70d7f21 100644
+--- a/drivers/nvme/host/fc.c
++++ b/drivers/nvme/host/fc.c
+@@ -2392,7 +2392,7 @@ nvme_fc_ctrl_free(struct kref *ref)
+       unsigned long flags;
+       if (ctrl->ctrl.tagset) {
+-              blk_cleanup_queue(ctrl->ctrl.connect_q);
++              blk_mq_destroy_queue(ctrl->ctrl.connect_q);
+               blk_mq_free_tag_set(&ctrl->tag_set);
+       }
+@@ -2402,8 +2402,8 @@ nvme_fc_ctrl_free(struct kref *ref)
+       spin_unlock_irqrestore(&ctrl->rport->lock, flags);
+       nvme_start_admin_queue(&ctrl->ctrl);
+-      blk_cleanup_queue(ctrl->ctrl.admin_q);
+-      blk_cleanup_queue(ctrl->ctrl.fabrics_q);
++      blk_mq_destroy_queue(ctrl->ctrl.admin_q);
++      blk_mq_destroy_queue(ctrl->ctrl.fabrics_q);
+       blk_mq_free_tag_set(&ctrl->admin_tag_set);
+       kfree(ctrl->queues);
+@@ -2953,7 +2953,7 @@ nvme_fc_create_io_queues(struct nvme_fc_ctrl *ctrl)
+ out_delete_hw_queues:
+       nvme_fc_delete_hw_io_queues(ctrl);
+ out_cleanup_blk_queue:
+-      blk_cleanup_queue(ctrl->ctrl.connect_q);
++      blk_mq_destroy_queue(ctrl->ctrl.connect_q);
+ out_free_tag_set:
+       blk_mq_free_tag_set(&ctrl->tag_set);
+       nvme_fc_free_io_queues(ctrl);
+@@ -3642,9 +3642,9 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
+       return ERR_PTR(-EIO);
+ out_cleanup_admin_q:
+-      blk_cleanup_queue(ctrl->ctrl.admin_q);
++      blk_mq_destroy_queue(ctrl->ctrl.admin_q);
+ out_cleanup_fabrics_q:
+-      blk_cleanup_queue(ctrl->ctrl.fabrics_q);
++      blk_mq_destroy_queue(ctrl->ctrl.fabrics_q);
+ out_free_admin_tag_set:
+       blk_mq_free_tag_set(&ctrl->admin_tag_set);
+ out_free_queues:
+diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
+index 9f6614f7dbeb..3516678d3754 100644
+--- a/drivers/nvme/host/pci.c
++++ b/drivers/nvme/host/pci.c
+@@ -1760,7 +1760,7 @@ static void nvme_dev_remove_admin(struct nvme_dev *dev)
+                * queue to flush these to completion.
+                */
+               nvme_start_admin_queue(&dev->ctrl);
+-              blk_cleanup_queue(dev->ctrl.admin_q);
++              blk_mq_destroy_queue(dev->ctrl.admin_q);
+               blk_mq_free_tag_set(&dev->admin_tagset);
+       }
+ }
+diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
+index 46c2dcf72f7e..240024dd5d85 100644
+--- a/drivers/nvme/host/rdma.c
++++ b/drivers/nvme/host/rdma.c
+@@ -840,8 +840,8 @@ static void nvme_rdma_destroy_admin_queue(struct nvme_rdma_ctrl *ctrl,
+               bool remove)
+ {
+       if (remove) {
+-              blk_cleanup_queue(ctrl->ctrl.admin_q);
+-              blk_cleanup_queue(ctrl->ctrl.fabrics_q);
++              blk_mq_destroy_queue(ctrl->ctrl.admin_q);
++              blk_mq_destroy_queue(ctrl->ctrl.fabrics_q);
+               blk_mq_free_tag_set(ctrl->ctrl.admin_tagset);
+       }
+       if (ctrl->async_event_sqe.data) {
+@@ -935,10 +935,10 @@ static int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl,
+       nvme_cancel_admin_tagset(&ctrl->ctrl);
+ out_cleanup_queue:
+       if (new)
+-              blk_cleanup_queue(ctrl->ctrl.admin_q);
++              blk_mq_destroy_queue(ctrl->ctrl.admin_q);
+ out_cleanup_fabrics_q:
+       if (new)
+-              blk_cleanup_queue(ctrl->ctrl.fabrics_q);
++              blk_mq_destroy_queue(ctrl->ctrl.fabrics_q);
+ out_free_tagset:
+       if (new)
+               blk_mq_free_tag_set(ctrl->ctrl.admin_tagset);
+@@ -957,7 +957,7 @@ static void nvme_rdma_destroy_io_queues(struct nvme_rdma_ctrl *ctrl,
+               bool remove)
+ {
+       if (remove) {
+-              blk_cleanup_queue(ctrl->ctrl.connect_q);
++              blk_mq_destroy_queue(ctrl->ctrl.connect_q);
+               blk_mq_free_tag_set(ctrl->ctrl.tagset);
+       }
+       nvme_rdma_free_io_queues(ctrl);
+@@ -1012,7 +1012,7 @@ static int nvme_rdma_configure_io_queues(struct nvme_rdma_ctrl *ctrl, bool new)
+ out_cleanup_connect_q:
+       nvme_cancel_tagset(&ctrl->ctrl);
+       if (new)
+-              blk_cleanup_queue(ctrl->ctrl.connect_q);
++              blk_mq_destroy_queue(ctrl->ctrl.connect_q);
+ out_free_tag_set:
+       if (new)
+               blk_mq_free_tag_set(ctrl->ctrl.tagset);
+diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
+index daa0e160e121..d7e5bbdb9b75 100644
+--- a/drivers/nvme/host/tcp.c
++++ b/drivers/nvme/host/tcp.c
+@@ -1881,7 +1881,7 @@ static void nvme_tcp_destroy_io_queues(struct nvme_ctrl *ctrl, bool remove)
+ {
+       nvme_tcp_stop_io_queues(ctrl);
+       if (remove) {
+-              blk_cleanup_queue(ctrl->connect_q);
++              blk_mq_destroy_queue(ctrl->connect_q);
+               blk_mq_free_tag_set(ctrl->tagset);
+       }
+       nvme_tcp_free_io_queues(ctrl);
+@@ -1936,7 +1936,7 @@ static int nvme_tcp_configure_io_queues(struct nvme_ctrl *ctrl, bool new)
+ out_cleanup_connect_q:
+       nvme_cancel_tagset(ctrl);
+       if (new)
+-              blk_cleanup_queue(ctrl->connect_q);
++              blk_mq_destroy_queue(ctrl->connect_q);
+ out_free_tag_set:
+       if (new)
+               blk_mq_free_tag_set(ctrl->tagset);
+@@ -1949,8 +1949,8 @@ static void nvme_tcp_destroy_admin_queue(struct nvme_ctrl *ctrl, bool remove)
+ {
+       nvme_tcp_stop_queue(ctrl, 0);
+       if (remove) {
+-              blk_cleanup_queue(ctrl->admin_q);
+-              blk_cleanup_queue(ctrl->fabrics_q);
++              blk_mq_destroy_queue(ctrl->admin_q);
++              blk_mq_destroy_queue(ctrl->fabrics_q);
+               blk_mq_free_tag_set(ctrl->admin_tagset);
+       }
+       nvme_tcp_free_admin_queue(ctrl);
+@@ -2008,10 +2008,10 @@ static int nvme_tcp_configure_admin_queue(struct nvme_ctrl *ctrl, bool new)
+       nvme_cancel_admin_tagset(ctrl);
+ out_cleanup_queue:
+       if (new)
+-              blk_cleanup_queue(ctrl->admin_q);
++              blk_mq_destroy_queue(ctrl->admin_q);
+ out_cleanup_fabrics_q:
+       if (new)
+-              blk_cleanup_queue(ctrl->fabrics_q);
++              blk_mq_destroy_queue(ctrl->fabrics_q);
+ out_free_tagset:
+       if (new)
+               blk_mq_free_tag_set(ctrl->admin_tagset);
+diff --git a/drivers/nvme/target/loop.c b/drivers/nvme/target/loop.c
+index 59024af2da2e..0f5c77e22a0a 100644
+--- a/drivers/nvme/target/loop.c
++++ b/drivers/nvme/target/loop.c
+@@ -266,8 +266,8 @@ static void nvme_loop_destroy_admin_queue(struct nvme_loop_ctrl *ctrl)
+       if (!test_and_clear_bit(NVME_LOOP_Q_LIVE, &ctrl->queues[0].flags))
+               return;
+       nvmet_sq_destroy(&ctrl->queues[0].nvme_sq);
+-      blk_cleanup_queue(ctrl->ctrl.admin_q);
+-      blk_cleanup_queue(ctrl->ctrl.fabrics_q);
++      blk_mq_destroy_queue(ctrl->ctrl.admin_q);
++      blk_mq_destroy_queue(ctrl->ctrl.fabrics_q);
+       blk_mq_free_tag_set(&ctrl->admin_tag_set);
+ }
+@@ -283,7 +283,7 @@ static void nvme_loop_free_ctrl(struct nvme_ctrl *nctrl)
+       mutex_unlock(&nvme_loop_ctrl_mutex);
+       if (nctrl->tagset) {
+-              blk_cleanup_queue(ctrl->ctrl.connect_q);
++              blk_mq_destroy_queue(ctrl->ctrl.connect_q);
+               blk_mq_free_tag_set(&ctrl->tag_set);
+       }
+       kfree(ctrl->queues);
+@@ -410,9 +410,9 @@ static int nvme_loop_configure_admin_queue(struct nvme_loop_ctrl *ctrl)
+ out_cleanup_queue:
+       clear_bit(NVME_LOOP_Q_LIVE, &ctrl->queues[0].flags);
+-      blk_cleanup_queue(ctrl->ctrl.admin_q);
++      blk_mq_destroy_queue(ctrl->ctrl.admin_q);
+ out_cleanup_fabrics_q:
+-      blk_cleanup_queue(ctrl->ctrl.fabrics_q);
++      blk_mq_destroy_queue(ctrl->ctrl.fabrics_q);
+ out_free_tagset:
+       blk_mq_free_tag_set(&ctrl->admin_tag_set);
+ out_free_sq:
+@@ -554,7 +554,7 @@ static int nvme_loop_create_io_queues(struct nvme_loop_ctrl *ctrl)
+       return 0;
+ out_cleanup_connect_q:
+-      blk_cleanup_queue(ctrl->ctrl.connect_q);
++      blk_mq_destroy_queue(ctrl->ctrl.connect_q);
+ out_free_tagset:
+       blk_mq_free_tag_set(&ctrl->tag_set);
+ out_destroy_queues:
+diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
+index ba6d78789660..e8489331f12b 100644
+--- a/drivers/s390/block/dasd.c
++++ b/drivers/s390/block/dasd.c
+@@ -3280,7 +3280,7 @@ static int dasd_alloc_queue(struct dasd_block *block)
+ static void dasd_free_queue(struct dasd_block *block)
+ {
+       if (block->request_queue) {
+-              blk_cleanup_queue(block->request_queue);
++              blk_mq_destroy_queue(block->request_queue);
+               blk_mq_free_tag_set(&block->tag_set);
+               block->request_queue = NULL;
+       }
+diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c
+index a7a33ebf4bbe..5a83f0a39901 100644
+--- a/drivers/s390/block/dasd_genhd.c
++++ b/drivers/s390/block/dasd_genhd.c
+@@ -41,8 +41,8 @@ int dasd_gendisk_alloc(struct dasd_block *block)
+       if (base->devindex >= DASD_PER_MAJOR)
+               return -EBUSY;
+-      gdp = __alloc_disk_node(block->request_queue, NUMA_NO_NODE,
+-                              &dasd_bio_compl_lkclass);
++      gdp = blk_mq_alloc_disk_for_queue(block->request_queue,
++                                        &dasd_bio_compl_lkclass);
+       if (!gdp)
+               return -ENOMEM;
+diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
+index f5c876d03c1a..0a267d6e2f7c 100644
+--- a/drivers/scsi/scsi_lib.c
++++ b/drivers/scsi/scsi_lib.c
+@@ -168,7 +168,7 @@ static void __scsi_queue_insert(struct scsi_cmnd *cmd, int reason, bool unbusy)
+        * Requeue this command.  It will go before all other commands
+        * that are already in the queue. Schedule requeue work under
+        * lock such that the kblockd_schedule_work() call happens
+-       * before blk_cleanup_queue() finishes.
++       * before blk_mq_destroy_queue() finishes.
+        */
+       cmd->result = 0;
+@@ -429,9 +429,9 @@ static void scsi_starved_list_run(struct Scsi_Host *shost)
+                * it and the queue.  Mitigate by taking a reference to the
+                * queue and never touching the sdev again after we drop the
+                * host lock.  Note: if __scsi_remove_device() invokes
+-               * blk_cleanup_queue() before the queue is run from this
++               * blk_mq_destroy_queue() before the queue is run from this
+                * function then blk_run_queue() will return immediately since
+-               * blk_cleanup_queue() marks the queue with QUEUE_FLAG_DYING.
++               * blk_mq_destroy_queue() marks the queue with QUEUE_FLAG_DYING.
+                */
+               slq = sdev->request_queue;
+               if (!blk_get_queue(slq))
+diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
+index 43949798a2e4..aa70d9282161 100644
+--- a/drivers/scsi/scsi_sysfs.c
++++ b/drivers/scsi/scsi_sysfs.c
+@@ -1475,7 +1475,7 @@ void __scsi_remove_device(struct scsi_device *sdev)
+       scsi_device_set_state(sdev, SDEV_DEL);
+       mutex_unlock(&sdev->state_mutex);
+-      blk_cleanup_queue(sdev->request_queue);
++      blk_mq_destroy_queue(sdev->request_queue);
+       cancel_work_sync(&sdev->requeue_work);
+       if (sdev->host->hostt->slave_destroy)
+diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
+index a1a2ac09066f..cb587e488601 100644
+--- a/drivers/scsi/sd.c
++++ b/drivers/scsi/sd.c
+@@ -3440,8 +3440,8 @@ static int sd_probe(struct device *dev)
+       if (!sdkp)
+               goto out;
+-      gd = __alloc_disk_node(sdp->request_queue, NUMA_NO_NODE,
+-                             &sd_bio_compl_lkclass);
++      gd = blk_mq_alloc_disk_for_queue(sdp->request_queue,
++                                       &sd_bio_compl_lkclass);
+       if (!gd)
+               goto out_free;
+diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
+index 32d3b8274f14..a278b739d0c5 100644
+--- a/drivers/scsi/sr.c
++++ b/drivers/scsi/sr.c
+@@ -624,8 +624,8 @@ static int sr_probe(struct device *dev)
+       if (!cd)
+               goto fail;
+-      disk = __alloc_disk_node(sdev->request_queue, NUMA_NO_NODE,
+-                               &sr_bio_compl_lkclass);
++      disk = blk_mq_alloc_disk_for_queue(sdev->request_queue,
++                                         &sr_bio_compl_lkclass);
+       if (!disk)
+               goto fail_free;
+       mutex_init(&cd->lock);
+diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
+index 829da9cb14a8..55bb0d0422d5 100644
+--- a/drivers/ufs/core/ufshcd.c
++++ b/drivers/ufs/core/ufshcd.c
+@@ -9519,7 +9519,7 @@ void ufshcd_remove(struct ufs_hba *hba)
+       ufs_bsg_remove(hba);
+       ufshpb_remove(hba);
+       ufs_sysfs_remove_nodes(hba->dev);
+-      blk_cleanup_queue(hba->tmf_queue);
++      blk_mq_destroy_queue(hba->tmf_queue);
+       blk_mq_free_tag_set(&hba->tmf_tag_set);
+       scsi_remove_host(hba->host);
+       /* disable interrupts */
+@@ -9815,7 +9815,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
+       return 0;
+ free_tmf_queue:
+-      blk_cleanup_queue(hba->tmf_queue);
++      blk_mq_destroy_queue(hba->tmf_queue);
+ free_tmf_tag_set:
+       blk_mq_free_tag_set(&hba->tmf_tag_set);
+ out_remove_scsi_host:
+diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h
+index e2d9daf7e8dd..0fd96e92c6c6 100644
+--- a/include/linux/blk-mq.h
++++ b/include/linux/blk-mq.h
+@@ -686,10 +686,13 @@ struct gendisk *__blk_mq_alloc_disk(struct blk_mq_tag_set *set, void *queuedata,
+                                                                       \
+       __blk_mq_alloc_disk(set, queuedata, &__key);                    \
+ })
++struct gendisk *blk_mq_alloc_disk_for_queue(struct request_queue *q,
++              struct lock_class_key *lkclass);
+ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *);
+ int blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
+               struct request_queue *q);
+ void blk_mq_unregister_dev(struct device *, struct request_queue *);
++void blk_mq_destroy_queue(struct request_queue *);
+ int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set);
+ int blk_mq_alloc_sq_tag_set(struct blk_mq_tag_set *set,
+diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
+index 76f77eed58c3..83eb8869a8c9 100644
+--- a/include/linux/blkdev.h
++++ b/include/linux/blkdev.h
+@@ -148,6 +148,7 @@ struct gendisk {
+ #define GD_NATIVE_CAPACITY            3
+ #define GD_ADDED                      4
+ #define GD_SUPPRESS_PART_SCAN         5
++#define GD_OWNS_QUEUE                 6
+       struct mutex open_mutex;        /* open/close mutex */
+       unsigned open_partitions;       /* number of open partitions */
+@@ -810,8 +811,6 @@ static inline u64 sb_bdev_nr_blocks(struct super_block *sb)
+ int bdev_disk_changed(struct gendisk *disk, bool invalidate);
+-struct gendisk *__alloc_disk_node(struct request_queue *q, int node_id,
+-              struct lock_class_key *lkclass);
+ void put_disk(struct gendisk *disk);
+ struct gendisk *__blk_alloc_disk(int node, struct lock_class_key *lkclass);
+@@ -953,7 +952,6 @@ static inline unsigned int blk_max_size_offset(struct request_queue *q,
+ /*
+  * Access functions for manipulating queue properties
+  */
+-extern void blk_cleanup_queue(struct request_queue *);
+ void blk_queue_bounce_limit(struct request_queue *q, enum blk_bounce limit);
+ extern void blk_queue_max_hw_sectors(struct request_queue *, unsigned int);
+ extern void blk_queue_chunk_sectors(struct request_queue *, unsigned int);
+-- 
+2.35.1
+
diff --git a/queue-5.19/block-stop-setting-the-nomerges-flags-in-blk_cleanup.patch b/queue-5.19/block-stop-setting-the-nomerges-flags-in-blk_cleanup.patch
new file mode 100644 (file)
index 0000000..3c814a0
--- /dev/null
@@ -0,0 +1,40 @@
+From 5968f92808e5a6ceca57cd07186e2de9aff90f08 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jun 2022 08:05:50 +0200
+Subject: block: stop setting the nomerges flags in blk_cleanup_queue
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ Upstream commit 0e3534022f26ae51f7cf28347a253230604b6f4e ]
+
+These flags only apply to file system I/O, and all file system I/O is
+already drained by del_gendisk and thus can't be in progress when
+blk_cleanup_queue is called.
+
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Hannes Reinecke <hare@suse.de>
+Link: https://lore.kernel.org/r/20220619060552.1850436-5-hch@lst.de
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Stable-dep-of: 8fe4ce5836e9 ("scsi: core: Fix a use-after-free")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ block/blk-core.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/block/blk-core.c b/block/blk-core.c
+index 76f070c3a3b0..b8083decc07f 100644
+--- a/block/blk-core.c
++++ b/block/blk-core.c
+@@ -304,9 +304,6 @@ void blk_cleanup_queue(struct request_queue *q)
+       blk_queue_flag_set(QUEUE_FLAG_DYING, q);
+       blk_queue_start_drain(q);
+-      blk_queue_flag_set(QUEUE_FLAG_NOMERGES, q);
+-      blk_queue_flag_set(QUEUE_FLAG_NOXMERGES, q);
+-
+       /*
+        * Drain all requests queued before DYING marking. Set DEAD flag to
+        * prevent that blk_mq_run_hw_queues() accesses the hardware queues
+-- 
+2.35.1
+
diff --git a/queue-5.19/drm-i915-bios-split-parse_driver_features-into-two-p.patch b/queue-5.19/drm-i915-bios-split-parse_driver_features-into-two-p.patch
new file mode 100644 (file)
index 0000000..4daa516
--- /dev/null
@@ -0,0 +1,58 @@
+From 1438bd4f63cae6ca04173ee98b4350f7190c554d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 10 May 2022 13:42:37 +0300
+Subject: drm/i915/bios: Split parse_driver_features() into two parts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ville Syrjälä <ville.syrjala@linux.intel.com>
+
+[ Upstream commit c3fbcf60bc74b630967f291f47f0d9d0de6fcea7 ]
+
+We use the "driver features" block for two different kinds
+of data: global data, and per panel data. Split the function
+into two parts along that line so that we can start doing the
+parsing in two different locations.
+
+Reviewed-by: Jani Nikula <jani.nikula@intel.com>
+Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20220510104242.6099-11-ville.syrjala@linux.intel.com
+Stable-dep-of: 607f41768a1e ("drm/i915/dsi: filter invalid backlight and CABC ports")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_bios.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c
+index 91caf4523b34..5ceabc380808 100644
+--- a/drivers/gpu/drm/i915/display/intel_bios.c
++++ b/drivers/gpu/drm/i915/display/intel_bios.c
+@@ -1188,6 +1188,16 @@ parse_driver_features(struct drm_i915_private *i915)
+                   driver->lvds_config != BDB_DRIVER_FEATURE_INT_SDVO_LVDS)
+                       i915->vbt.int_lvds_support = 0;
+       }
++}
++
++static void
++parse_panel_driver_features(struct drm_i915_private *i915)
++{
++      const struct bdb_driver_features *driver;
++
++      driver = find_section(i915, BDB_DRIVER_FEATURES);
++      if (!driver)
++              return;
+       if (i915->vbt.version < 228) {
+               drm_dbg_kms(&i915->drm, "DRRS State Enabled:%d\n",
+@@ -2965,6 +2975,7 @@ void intel_bios_init(struct drm_i915_private *i915)
+       parse_lfp_backlight(i915);
+       parse_sdvo_panel_data(i915);
+       parse_driver_features(i915);
++      parse_panel_driver_features(i915);
+       parse_power_conservation_features(i915);
+       parse_edp(i915);
+       parse_psr(i915);
+-- 
+2.35.1
+
diff --git a/queue-5.19/drm-i915-bios-split-vbt-data-into-per-panel-vs.-glob.patch b/queue-5.19/drm-i915-bios-split-vbt-data-into-per-panel-vs.-glob.patch
new file mode 100644 (file)
index 0000000..db312f4
--- /dev/null
@@ -0,0 +1,1969 @@
+From 22bc5f164a8d11123afb423e73f891069d3a5e3b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 10 May 2022 13:42:39 +0300
+Subject: drm/i915/bios: Split VBT data into per-panel vs. global parts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ville Syrjälä <ville.syrjala@linux.intel.com>
+
+[ Upstream commit 3cf050762534cc268a02793ec00240f81c6e2229 ]
+
+Move the panel specific VBT parsing to happen during the
+output probing stage. Needs to be done because the VBT
+parsing will need to look at the EDID to determine
+the correct panel_type on some machines.
+
+We split the parsed VBT data (i915->vbt) along the same
+boundary. For the moment we just hoist all the panel
+specific stuff into connector->panel.vbt since that seems
+like the most convenient place for eg. the backlight code.
+
+Note that we simply drop the drrs type check from
+intel_drrs_frontbuffer_update() since that operates on the whole
+device rather than a specific connector/encoder. But the check
+was just a micro optimization so removing it doesn't actually
+mattter for correctness.
+
+TODO: Lot's of cleanup to be done in the future. Eg. most of
+the DSI stuff could probably be eliminated entirely and just
+parsed on demand during DSI init.
+
+v2: Note the intel_drrs_frontbuffer_update() change
+
+Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20220510104242.6099-13-ville.syrjala@linux.intel.com
+Reviewed-by: Jani Nikula <jani.nikula@intel.com>
+Stable-dep-of: 607f41768a1e ("drm/i915/dsi: filter invalid backlight and CABC ports")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/icl_dsi.c        |  11 +-
+ .../gpu/drm/i915/display/intel_backlight.c    |  23 +-
+ drivers/gpu/drm/i915/display/intel_bios.c     | 371 ++++++++++--------
+ drivers/gpu/drm/i915/display/intel_bios.h     |   5 +-
+ .../drm/i915/display/intel_ddi_buf_trans.c    |   9 +-
+ drivers/gpu/drm/i915/display/intel_display.c  |   1 -
+ .../drm/i915/display/intel_display_types.h    |  69 ++++
+ drivers/gpu/drm/i915/display/intel_dp.c       |  21 +-
+ drivers/gpu/drm/i915/display/intel_dp.h       |   1 +
+ .../drm/i915/display/intel_dp_aux_backlight.c |   6 +-
+ drivers/gpu/drm/i915/display/intel_drrs.c     |   3 -
+ drivers/gpu/drm/i915/display/intel_dsi.c      |   2 +-
+ .../i915/display/intel_dsi_dcs_backlight.c    |   9 +-
+ drivers/gpu/drm/i915/display/intel_dsi_vbt.c  |  56 +--
+ drivers/gpu/drm/i915/display/intel_lvds.c     |   6 +-
+ drivers/gpu/drm/i915/display/intel_panel.c    |  13 +-
+ drivers/gpu/drm/i915/display/intel_pps.c      |   6 +-
+ drivers/gpu/drm/i915/display/intel_psr.c      |  30 +-
+ drivers/gpu/drm/i915/display/intel_sdvo.c     |   3 +
+ drivers/gpu/drm/i915/display/vlv_dsi.c        |  14 +-
+ drivers/gpu/drm/i915/i915_drv.h               |  63 ---
+ 21 files changed, 391 insertions(+), 331 deletions(-)
+
+diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c
+index 5508ebb9eb43..5c46acb46516 100644
+--- a/drivers/gpu/drm/i915/display/icl_dsi.c
++++ b/drivers/gpu/drm/i915/display/icl_dsi.c
+@@ -1864,7 +1864,8 @@ static void icl_dphy_param_init(struct intel_dsi *intel_dsi)
+ {
+       struct drm_device *dev = intel_dsi->base.base.dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+-      struct mipi_config *mipi_config = dev_priv->vbt.dsi.config;
++      struct intel_connector *connector = intel_dsi->attached_connector;
++      struct mipi_config *mipi_config = connector->panel.vbt.dsi.config;
+       u32 tlpx_ns;
+       u32 prepare_cnt, exit_zero_cnt, clk_zero_cnt, trail_cnt;
+       u32 ths_prepare_ns, tclk_trail_ns;
+@@ -2051,6 +2052,8 @@ void icl_dsi_init(struct drm_i915_private *dev_priv)
+       /* attach connector to encoder */
+       intel_connector_attach_encoder(intel_connector, encoder);
++      intel_bios_init_panel(dev_priv, &intel_connector->panel);
++
+       mutex_lock(&dev->mode_config.mutex);
+       intel_panel_add_vbt_lfp_fixed_mode(intel_connector);
+       mutex_unlock(&dev->mode_config.mutex);
+@@ -2064,13 +2067,13 @@ void icl_dsi_init(struct drm_i915_private *dev_priv)
+       intel_backlight_setup(intel_connector, INVALID_PIPE);
+-      if (dev_priv->vbt.dsi.config->dual_link)
++      if (intel_connector->panel.vbt.dsi.config->dual_link)
+               intel_dsi->ports = BIT(PORT_A) | BIT(PORT_B);
+       else
+               intel_dsi->ports = BIT(port);
+-      intel_dsi->dcs_backlight_ports = dev_priv->vbt.dsi.bl_ports;
+-      intel_dsi->dcs_cabc_ports = dev_priv->vbt.dsi.cabc_ports;
++      intel_dsi->dcs_backlight_ports = intel_connector->panel.vbt.dsi.bl_ports;
++      intel_dsi->dcs_cabc_ports = intel_connector->panel.vbt.dsi.cabc_ports;
+       for_each_dsi_port(port, intel_dsi->ports) {
+               struct intel_dsi_host *host;
+diff --git a/drivers/gpu/drm/i915/display/intel_backlight.c b/drivers/gpu/drm/i915/display/intel_backlight.c
+index 3e200a2e4ba2..5182bb66bd28 100644
+--- a/drivers/gpu/drm/i915/display/intel_backlight.c
++++ b/drivers/gpu/drm/i915/display/intel_backlight.c
+@@ -1158,9 +1158,10 @@ static u32 vlv_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
+       return DIV_ROUND_CLOSEST(clock, pwm_freq_hz * mul);
+ }
+-static u16 get_vbt_pwm_freq(struct drm_i915_private *dev_priv)
++static u16 get_vbt_pwm_freq(struct intel_connector *connector)
+ {
+-      u16 pwm_freq_hz = dev_priv->vbt.backlight.pwm_freq_hz;
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      u16 pwm_freq_hz = connector->panel.vbt.backlight.pwm_freq_hz;
+       if (pwm_freq_hz) {
+               drm_dbg_kms(&dev_priv->drm,
+@@ -1180,7 +1181,7 @@ static u32 get_backlight_max_vbt(struct intel_connector *connector)
+ {
+       struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+       struct intel_panel *panel = &connector->panel;
+-      u16 pwm_freq_hz = get_vbt_pwm_freq(dev_priv);
++      u16 pwm_freq_hz = get_vbt_pwm_freq(connector);
+       u32 pwm;
+       if (!panel->backlight.pwm_funcs->hz_to_pwm) {
+@@ -1217,11 +1218,11 @@ static u32 get_backlight_min_vbt(struct intel_connector *connector)
+        * against this by letting the minimum be at most (arbitrarily chosen)
+        * 25% of the max.
+        */
+-      min = clamp_t(int, dev_priv->vbt.backlight.min_brightness, 0, 64);
+-      if (min != dev_priv->vbt.backlight.min_brightness) {
++      min = clamp_t(int, connector->panel.vbt.backlight.min_brightness, 0, 64);
++      if (min != connector->panel.vbt.backlight.min_brightness) {
+               drm_dbg_kms(&dev_priv->drm,
+                           "clamping VBT min backlight %d/255 to %d/255\n",
+-                          dev_priv->vbt.backlight.min_brightness, min);
++                          connector->panel.vbt.backlight.min_brightness, min);
+       }
+       /* vbt value is a coefficient in range [0..255] */
+@@ -1410,7 +1411,7 @@ bxt_setup_backlight(struct intel_connector *connector, enum pipe unused)
+       struct intel_panel *panel = &connector->panel;
+       u32 pwm_ctl, val;
+-      panel->backlight.controller = dev_priv->vbt.backlight.controller;
++      panel->backlight.controller = connector->panel.vbt.backlight.controller;
+       pwm_ctl = intel_de_read(dev_priv,
+                               BXT_BLC_PWM_CTL(panel->backlight.controller));
+@@ -1483,7 +1484,7 @@ static int ext_pwm_setup_backlight(struct intel_connector *connector,
+       u32 level;
+       /* Get the right PWM chip for DSI backlight according to VBT */
+-      if (dev_priv->vbt.dsi.config->pwm_blc == PPS_BLC_PMIC) {
++      if (connector->panel.vbt.dsi.config->pwm_blc == PPS_BLC_PMIC) {
+               panel->backlight.pwm = pwm_get(dev->dev, "pwm_pmic_backlight");
+               desc = "PMIC";
+       } else {
+@@ -1512,11 +1513,11 @@ static int ext_pwm_setup_backlight(struct intel_connector *connector,
+               drm_dbg_kms(&dev_priv->drm, "PWM already enabled at freq %ld, VBT freq %d, level %d\n",
+                           NSEC_PER_SEC / (unsigned long)panel->backlight.pwm_state.period,
+-                          get_vbt_pwm_freq(dev_priv), level);
++                          get_vbt_pwm_freq(connector), level);
+       } else {
+               /* Set period from VBT frequency, leave other settings at 0. */
+               panel->backlight.pwm_state.period =
+-                      NSEC_PER_SEC / get_vbt_pwm_freq(dev_priv);
++                      NSEC_PER_SEC / get_vbt_pwm_freq(connector);
+       }
+       drm_info(&dev_priv->drm, "Using %s PWM for LCD backlight control\n",
+@@ -1601,7 +1602,7 @@ int intel_backlight_setup(struct intel_connector *connector, enum pipe pipe)
+       struct intel_panel *panel = &connector->panel;
+       int ret;
+-      if (!dev_priv->vbt.backlight.present) {
++      if (!connector->panel.vbt.backlight.present) {
+               if (dev_priv->quirks & QUIRK_BACKLIGHT_PRESENT) {
+                       drm_dbg_kms(&dev_priv->drm,
+                                   "no backlight present per VBT, but present per quirk\n");
+diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c
+index d5cae64b916b..d5d20a44f373 100644
+--- a/drivers/gpu/drm/i915/display/intel_bios.c
++++ b/drivers/gpu/drm/i915/display/intel_bios.c
+@@ -682,7 +682,8 @@ static int get_panel_type(struct drm_i915_private *i915)
+ /* Parse general panel options */
+ static void
+-parse_panel_options(struct drm_i915_private *i915)
++parse_panel_options(struct drm_i915_private *i915,
++                  struct intel_panel *panel)
+ {
+       const struct bdb_lvds_options *lvds_options;
+       int panel_type;
+@@ -692,11 +693,11 @@ parse_panel_options(struct drm_i915_private *i915)
+       if (!lvds_options)
+               return;
+-      i915->vbt.lvds_dither = lvds_options->pixel_dither;
++      panel->vbt.lvds_dither = lvds_options->pixel_dither;
+       panel_type = get_panel_type(i915);
+-      i915->vbt.panel_type = panel_type;
++      panel->vbt.panel_type = panel_type;
+       drrs_mode = (lvds_options->dps_panel_type_bits
+                               >> (panel_type * 2)) & MODE_MASK;
+@@ -707,16 +708,16 @@ parse_panel_options(struct drm_i915_private *i915)
+        */
+       switch (drrs_mode) {
+       case 0:
+-              i915->vbt.drrs_type = DRRS_TYPE_STATIC;
++              panel->vbt.drrs_type = DRRS_TYPE_STATIC;
+               drm_dbg_kms(&i915->drm, "DRRS supported mode is static\n");
+               break;
+       case 2:
+-              i915->vbt.drrs_type = DRRS_TYPE_SEAMLESS;
++              panel->vbt.drrs_type = DRRS_TYPE_SEAMLESS;
+               drm_dbg_kms(&i915->drm,
+                           "DRRS supported mode is seamless\n");
+               break;
+       default:
+-              i915->vbt.drrs_type = DRRS_TYPE_NONE;
++              panel->vbt.drrs_type = DRRS_TYPE_NONE;
+               drm_dbg_kms(&i915->drm,
+                           "DRRS not supported (VBT input)\n");
+               break;
+@@ -725,13 +726,14 @@ parse_panel_options(struct drm_i915_private *i915)
+ static void
+ parse_lfp_panel_dtd(struct drm_i915_private *i915,
++                  struct intel_panel *panel,
+                   const struct bdb_lvds_lfp_data *lvds_lfp_data,
+                   const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs)
+ {
+       const struct lvds_dvo_timing *panel_dvo_timing;
+       const struct lvds_fp_timing *fp_timing;
+       struct drm_display_mode *panel_fixed_mode;
+-      int panel_type = i915->vbt.panel_type;
++      int panel_type = panel->vbt.panel_type;
+       panel_dvo_timing = get_lvds_dvo_timing(lvds_lfp_data,
+                                              lvds_lfp_data_ptrs,
+@@ -743,7 +745,7 @@ parse_lfp_panel_dtd(struct drm_i915_private *i915,
+       fill_detail_timing_data(panel_fixed_mode, panel_dvo_timing);
+-      i915->vbt.lfp_lvds_vbt_mode = panel_fixed_mode;
++      panel->vbt.lfp_lvds_vbt_mode = panel_fixed_mode;
+       drm_dbg_kms(&i915->drm,
+                   "Found panel mode in BIOS VBT legacy lfp table: " DRM_MODE_FMT "\n",
+@@ -756,20 +758,21 @@ parse_lfp_panel_dtd(struct drm_i915_private *i915,
+       /* check the resolution, just to be sure */
+       if (fp_timing->x_res == panel_fixed_mode->hdisplay &&
+           fp_timing->y_res == panel_fixed_mode->vdisplay) {
+-              i915->vbt.bios_lvds_val = fp_timing->lvds_reg_val;
++              panel->vbt.bios_lvds_val = fp_timing->lvds_reg_val;
+               drm_dbg_kms(&i915->drm,
+                           "VBT initial LVDS value %x\n",
+-                          i915->vbt.bios_lvds_val);
++                          panel->vbt.bios_lvds_val);
+       }
+ }
+ static void
+-parse_lfp_data(struct drm_i915_private *i915)
++parse_lfp_data(struct drm_i915_private *i915,
++             struct intel_panel *panel)
+ {
+       const struct bdb_lvds_lfp_data *data;
+       const struct bdb_lvds_lfp_data_tail *tail;
+       const struct bdb_lvds_lfp_data_ptrs *ptrs;
+-      int panel_type = i915->vbt.panel_type;
++      int panel_type = panel->vbt.panel_type;
+       ptrs = find_section(i915, BDB_LVDS_LFP_DATA_PTRS);
+       if (!ptrs)
+@@ -779,24 +782,25 @@ parse_lfp_data(struct drm_i915_private *i915)
+       if (!data)
+               return;
+-      if (!i915->vbt.lfp_lvds_vbt_mode)
+-              parse_lfp_panel_dtd(i915, data, ptrs);
++      if (!panel->vbt.lfp_lvds_vbt_mode)
++              parse_lfp_panel_dtd(i915, panel, data, ptrs);
+       tail = get_lfp_data_tail(data, ptrs);
+       if (!tail)
+               return;
+       if (i915->vbt.version >= 188) {
+-              i915->vbt.seamless_drrs_min_refresh_rate =
++              panel->vbt.seamless_drrs_min_refresh_rate =
+                       tail->seamless_drrs_min_refresh_rate[panel_type];
+               drm_dbg_kms(&i915->drm,
+                           "Seamless DRRS min refresh rate: %d Hz\n",
+-                          i915->vbt.seamless_drrs_min_refresh_rate);
++                          panel->vbt.seamless_drrs_min_refresh_rate);
+       }
+ }
+ static void
+-parse_generic_dtd(struct drm_i915_private *i915)
++parse_generic_dtd(struct drm_i915_private *i915,
++                struct intel_panel *panel)
+ {
+       const struct bdb_generic_dtd *generic_dtd;
+       const struct generic_dtd_entry *dtd;
+@@ -831,14 +835,14 @@ parse_generic_dtd(struct drm_i915_private *i915)
+       num_dtd = (get_blocksize(generic_dtd) -
+                  sizeof(struct bdb_generic_dtd)) / generic_dtd->gdtd_size;
+-      if (i915->vbt.panel_type >= num_dtd) {
++      if (panel->vbt.panel_type >= num_dtd) {
+               drm_err(&i915->drm,
+                       "Panel type %d not found in table of %d DTD's\n",
+-                      i915->vbt.panel_type, num_dtd);
++                      panel->vbt.panel_type, num_dtd);
+               return;
+       }
+-      dtd = &generic_dtd->dtd[i915->vbt.panel_type];
++      dtd = &generic_dtd->dtd[panel->vbt.panel_type];
+       panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL);
+       if (!panel_fixed_mode)
+@@ -881,15 +885,16 @@ parse_generic_dtd(struct drm_i915_private *i915)
+                   "Found panel mode in BIOS VBT generic dtd table: " DRM_MODE_FMT "\n",
+                   DRM_MODE_ARG(panel_fixed_mode));
+-      i915->vbt.lfp_lvds_vbt_mode = panel_fixed_mode;
++      panel->vbt.lfp_lvds_vbt_mode = panel_fixed_mode;
+ }
+ static void
+-parse_lfp_backlight(struct drm_i915_private *i915)
++parse_lfp_backlight(struct drm_i915_private *i915,
++                  struct intel_panel *panel)
+ {
+       const struct bdb_lfp_backlight_data *backlight_data;
+       const struct lfp_backlight_data_entry *entry;
+-      int panel_type = i915->vbt.panel_type;
++      int panel_type = panel->vbt.panel_type;
+       u16 level;
+       backlight_data = find_section(i915, BDB_LVDS_BACKLIGHT);
+@@ -905,15 +910,15 @@ parse_lfp_backlight(struct drm_i915_private *i915)
+       entry = &backlight_data->data[panel_type];
+-      i915->vbt.backlight.present = entry->type == BDB_BACKLIGHT_TYPE_PWM;
+-      if (!i915->vbt.backlight.present) {
++      panel->vbt.backlight.present = entry->type == BDB_BACKLIGHT_TYPE_PWM;
++      if (!panel->vbt.backlight.present) {
+               drm_dbg_kms(&i915->drm,
+                           "PWM backlight not present in VBT (type %u)\n",
+                           entry->type);
+               return;
+       }
+-      i915->vbt.backlight.type = INTEL_BACKLIGHT_DISPLAY_DDI;
++      panel->vbt.backlight.type = INTEL_BACKLIGHT_DISPLAY_DDI;
+       if (i915->vbt.version >= 191) {
+               size_t exp_size;
+@@ -928,13 +933,13 @@ parse_lfp_backlight(struct drm_i915_private *i915)
+                       const struct lfp_backlight_control_method *method;
+                       method = &backlight_data->backlight_control[panel_type];
+-                      i915->vbt.backlight.type = method->type;
+-                      i915->vbt.backlight.controller = method->controller;
++                      panel->vbt.backlight.type = method->type;
++                      panel->vbt.backlight.controller = method->controller;
+               }
+       }
+-      i915->vbt.backlight.pwm_freq_hz = entry->pwm_freq_hz;
+-      i915->vbt.backlight.active_low_pwm = entry->active_low_pwm;
++      panel->vbt.backlight.pwm_freq_hz = entry->pwm_freq_hz;
++      panel->vbt.backlight.active_low_pwm = entry->active_low_pwm;
+       if (i915->vbt.version >= 234) {
+               u16 min_level;
+@@ -955,28 +960,29 @@ parse_lfp_backlight(struct drm_i915_private *i915)
+                       drm_warn(&i915->drm, "Brightness min level > 255\n");
+                       level = 255;
+               }
+-              i915->vbt.backlight.min_brightness = min_level;
++              panel->vbt.backlight.min_brightness = min_level;
+-              i915->vbt.backlight.brightness_precision_bits =
++              panel->vbt.backlight.brightness_precision_bits =
+                       backlight_data->brightness_precision_bits[panel_type];
+       } else {
+               level = backlight_data->level[panel_type];
+-              i915->vbt.backlight.min_brightness = entry->min_brightness;
++              panel->vbt.backlight.min_brightness = entry->min_brightness;
+       }
+       drm_dbg_kms(&i915->drm,
+                   "VBT backlight PWM modulation frequency %u Hz, "
+                   "active %s, min brightness %u, level %u, controller %u\n",
+-                  i915->vbt.backlight.pwm_freq_hz,
+-                  i915->vbt.backlight.active_low_pwm ? "low" : "high",
+-                  i915->vbt.backlight.min_brightness,
++                  panel->vbt.backlight.pwm_freq_hz,
++                  panel->vbt.backlight.active_low_pwm ? "low" : "high",
++                  panel->vbt.backlight.min_brightness,
+                   level,
+-                  i915->vbt.backlight.controller);
++                  panel->vbt.backlight.controller);
+ }
+ /* Try to find sdvo panel data */
+ static void
+-parse_sdvo_panel_data(struct drm_i915_private *i915)
++parse_sdvo_panel_data(struct drm_i915_private *i915,
++                    struct intel_panel *panel)
+ {
+       const struct bdb_sdvo_panel_dtds *dtds;
+       struct drm_display_mode *panel_fixed_mode;
+@@ -1009,7 +1015,7 @@ parse_sdvo_panel_data(struct drm_i915_private *i915)
+       fill_detail_timing_data(panel_fixed_mode, &dtds->dtds[index]);
+-      i915->vbt.sdvo_lvds_vbt_mode = panel_fixed_mode;
++      panel->vbt.sdvo_lvds_vbt_mode = panel_fixed_mode;
+       drm_dbg_kms(&i915->drm,
+                   "Found SDVO panel mode in BIOS VBT tables: " DRM_MODE_FMT "\n",
+@@ -1191,7 +1197,8 @@ parse_driver_features(struct drm_i915_private *i915)
+ }
+ static void
+-parse_panel_driver_features(struct drm_i915_private *i915)
++parse_panel_driver_features(struct drm_i915_private *i915,
++                          struct intel_panel *panel)
+ {
+       const struct bdb_driver_features *driver;
+@@ -1209,17 +1216,18 @@ parse_panel_driver_features(struct drm_i915_private *i915)
+                * driver->drrs_enabled=false
+                */
+               if (!driver->drrs_enabled)
+-                      i915->vbt.drrs_type = DRRS_TYPE_NONE;
++                      panel->vbt.drrs_type = DRRS_TYPE_NONE;
+-              i915->vbt.psr.enable = driver->psr_enabled;
++              panel->vbt.psr.enable = driver->psr_enabled;
+       }
+ }
+ static void
+-parse_power_conservation_features(struct drm_i915_private *i915)
++parse_power_conservation_features(struct drm_i915_private *i915,
++                                struct intel_panel *panel)
+ {
+       const struct bdb_lfp_power *power;
+-      u8 panel_type = i915->vbt.panel_type;
++      u8 panel_type = panel->vbt.panel_type;
+       if (i915->vbt.version < 228)
+               return;
+@@ -1228,7 +1236,7 @@ parse_power_conservation_features(struct drm_i915_private *i915)
+       if (!power)
+               return;
+-      i915->vbt.psr.enable = power->psr & BIT(panel_type);
++      panel->vbt.psr.enable = power->psr & BIT(panel_type);
+       /*
+        * If DRRS is not supported, drrs_type has to be set to 0.
+@@ -1237,19 +1245,20 @@ parse_power_conservation_features(struct drm_i915_private *i915)
+        * power->drrs & BIT(panel_type)=false
+        */
+       if (!(power->drrs & BIT(panel_type)))
+-              i915->vbt.drrs_type = DRRS_TYPE_NONE;
++              panel->vbt.drrs_type = DRRS_TYPE_NONE;
+       if (i915->vbt.version >= 232)
+-              i915->vbt.edp.hobl = power->hobl & BIT(panel_type);
++              panel->vbt.edp.hobl = power->hobl & BIT(panel_type);
+ }
+ static void
+-parse_edp(struct drm_i915_private *i915)
++parse_edp(struct drm_i915_private *i915,
++        struct intel_panel *panel)
+ {
+       const struct bdb_edp *edp;
+       const struct edp_power_seq *edp_pps;
+       const struct edp_fast_link_params *edp_link_params;
+-      int panel_type = i915->vbt.panel_type;
++      int panel_type = panel->vbt.panel_type;
+       edp = find_section(i915, BDB_EDP);
+       if (!edp)
+@@ -1257,13 +1266,13 @@ parse_edp(struct drm_i915_private *i915)
+       switch ((edp->color_depth >> (panel_type * 2)) & 3) {
+       case EDP_18BPP:
+-              i915->vbt.edp.bpp = 18;
++              panel->vbt.edp.bpp = 18;
+               break;
+       case EDP_24BPP:
+-              i915->vbt.edp.bpp = 24;
++              panel->vbt.edp.bpp = 24;
+               break;
+       case EDP_30BPP:
+-              i915->vbt.edp.bpp = 30;
++              panel->vbt.edp.bpp = 30;
+               break;
+       }
+@@ -1271,14 +1280,14 @@ parse_edp(struct drm_i915_private *i915)
+       edp_pps = &edp->power_seqs[panel_type];
+       edp_link_params = &edp->fast_link_params[panel_type];
+-      i915->vbt.edp.pps = *edp_pps;
++      panel->vbt.edp.pps = *edp_pps;
+       switch (edp_link_params->rate) {
+       case EDP_RATE_1_62:
+-              i915->vbt.edp.rate = DP_LINK_BW_1_62;
++              panel->vbt.edp.rate = DP_LINK_BW_1_62;
+               break;
+       case EDP_RATE_2_7:
+-              i915->vbt.edp.rate = DP_LINK_BW_2_7;
++              panel->vbt.edp.rate = DP_LINK_BW_2_7;
+               break;
+       default:
+               drm_dbg_kms(&i915->drm,
+@@ -1289,13 +1298,13 @@ parse_edp(struct drm_i915_private *i915)
+       switch (edp_link_params->lanes) {
+       case EDP_LANE_1:
+-              i915->vbt.edp.lanes = 1;
++              panel->vbt.edp.lanes = 1;
+               break;
+       case EDP_LANE_2:
+-              i915->vbt.edp.lanes = 2;
++              panel->vbt.edp.lanes = 2;
+               break;
+       case EDP_LANE_4:
+-              i915->vbt.edp.lanes = 4;
++              panel->vbt.edp.lanes = 4;
+               break;
+       default:
+               drm_dbg_kms(&i915->drm,
+@@ -1306,16 +1315,16 @@ parse_edp(struct drm_i915_private *i915)
+       switch (edp_link_params->preemphasis) {
+       case EDP_PREEMPHASIS_NONE:
+-              i915->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_0;
++              panel->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_0;
+               break;
+       case EDP_PREEMPHASIS_3_5dB:
+-              i915->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_1;
++              panel->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_1;
+               break;
+       case EDP_PREEMPHASIS_6dB:
+-              i915->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_2;
++              panel->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_2;
+               break;
+       case EDP_PREEMPHASIS_9_5dB:
+-              i915->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_3;
++              panel->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_3;
+               break;
+       default:
+               drm_dbg_kms(&i915->drm,
+@@ -1326,16 +1335,16 @@ parse_edp(struct drm_i915_private *i915)
+       switch (edp_link_params->vswing) {
+       case EDP_VSWING_0_4V:
+-              i915->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
++              panel->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
+               break;
+       case EDP_VSWING_0_6V:
+-              i915->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_1;
++              panel->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_1;
+               break;
+       case EDP_VSWING_0_8V:
+-              i915->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
++              panel->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
+               break;
+       case EDP_VSWING_1_2V:
+-              i915->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
++              panel->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
+               break;
+       default:
+               drm_dbg_kms(&i915->drm,
+@@ -1349,24 +1358,25 @@ parse_edp(struct drm_i915_private *i915)
+               /* Don't read from VBT if module parameter has valid value*/
+               if (i915->params.edp_vswing) {
+-                      i915->vbt.edp.low_vswing =
++                      panel->vbt.edp.low_vswing =
+                               i915->params.edp_vswing == 1;
+               } else {
+                       vswing = (edp->edp_vswing_preemph >> (panel_type * 4)) & 0xF;
+-                      i915->vbt.edp.low_vswing = vswing == 0;
++                      panel->vbt.edp.low_vswing = vswing == 0;
+               }
+       }
+-      i915->vbt.edp.drrs_msa_timing_delay =
++      panel->vbt.edp.drrs_msa_timing_delay =
+               (edp->sdrrs_msa_timing_delay >> (panel_type * 2)) & 3;
+ }
+ static void
+-parse_psr(struct drm_i915_private *i915)
++parse_psr(struct drm_i915_private *i915,
++        struct intel_panel *panel)
+ {
+       const struct bdb_psr *psr;
+       const struct psr_table *psr_table;
+-      int panel_type = i915->vbt.panel_type;
++      int panel_type = panel->vbt.panel_type;
+       psr = find_section(i915, BDB_PSR);
+       if (!psr) {
+@@ -1376,11 +1386,11 @@ parse_psr(struct drm_i915_private *i915)
+       psr_table = &psr->psr_table[panel_type];
+-      i915->vbt.psr.full_link = psr_table->full_link;
+-      i915->vbt.psr.require_aux_wakeup = psr_table->require_aux_to_wakeup;
++      panel->vbt.psr.full_link = psr_table->full_link;
++      panel->vbt.psr.require_aux_wakeup = psr_table->require_aux_to_wakeup;
+       /* Allowed VBT values goes from 0 to 15 */
+-      i915->vbt.psr.idle_frames = psr_table->idle_frames < 0 ? 0 :
++      panel->vbt.psr.idle_frames = psr_table->idle_frames < 0 ? 0 :
+               psr_table->idle_frames > 15 ? 15 : psr_table->idle_frames;
+       /*
+@@ -1391,13 +1401,13 @@ parse_psr(struct drm_i915_private *i915)
+           (DISPLAY_VER(i915) >= 9 && !IS_BROXTON(i915))) {
+               switch (psr_table->tp1_wakeup_time) {
+               case 0:
+-                      i915->vbt.psr.tp1_wakeup_time_us = 500;
++                      panel->vbt.psr.tp1_wakeup_time_us = 500;
+                       break;
+               case 1:
+-                      i915->vbt.psr.tp1_wakeup_time_us = 100;
++                      panel->vbt.psr.tp1_wakeup_time_us = 100;
+                       break;
+               case 3:
+-                      i915->vbt.psr.tp1_wakeup_time_us = 0;
++                      panel->vbt.psr.tp1_wakeup_time_us = 0;
+                       break;
+               default:
+                       drm_dbg_kms(&i915->drm,
+@@ -1405,19 +1415,19 @@ parse_psr(struct drm_i915_private *i915)
+                                   psr_table->tp1_wakeup_time);
+                       fallthrough;
+               case 2:
+-                      i915->vbt.psr.tp1_wakeup_time_us = 2500;
++                      panel->vbt.psr.tp1_wakeup_time_us = 2500;
+                       break;
+               }
+               switch (psr_table->tp2_tp3_wakeup_time) {
+               case 0:
+-                      i915->vbt.psr.tp2_tp3_wakeup_time_us = 500;
++                      panel->vbt.psr.tp2_tp3_wakeup_time_us = 500;
+                       break;
+               case 1:
+-                      i915->vbt.psr.tp2_tp3_wakeup_time_us = 100;
++                      panel->vbt.psr.tp2_tp3_wakeup_time_us = 100;
+                       break;
+               case 3:
+-                      i915->vbt.psr.tp2_tp3_wakeup_time_us = 0;
++                      panel->vbt.psr.tp2_tp3_wakeup_time_us = 0;
+                       break;
+               default:
+                       drm_dbg_kms(&i915->drm,
+@@ -1425,12 +1435,12 @@ parse_psr(struct drm_i915_private *i915)
+                                   psr_table->tp2_tp3_wakeup_time);
+                       fallthrough;
+               case 2:
+-                      i915->vbt.psr.tp2_tp3_wakeup_time_us = 2500;
++                      panel->vbt.psr.tp2_tp3_wakeup_time_us = 2500;
+               break;
+               }
+       } else {
+-              i915->vbt.psr.tp1_wakeup_time_us = psr_table->tp1_wakeup_time * 100;
+-              i915->vbt.psr.tp2_tp3_wakeup_time_us = psr_table->tp2_tp3_wakeup_time * 100;
++              panel->vbt.psr.tp1_wakeup_time_us = psr_table->tp1_wakeup_time * 100;
++              panel->vbt.psr.tp2_tp3_wakeup_time_us = psr_table->tp2_tp3_wakeup_time * 100;
+       }
+       if (i915->vbt.version >= 226) {
+@@ -1452,62 +1462,64 @@ parse_psr(struct drm_i915_private *i915)
+                       wakeup_time = 2500;
+                       break;
+               }
+-              i915->vbt.psr.psr2_tp2_tp3_wakeup_time_us = wakeup_time;
++              panel->vbt.psr.psr2_tp2_tp3_wakeup_time_us = wakeup_time;
+       } else {
+               /* Reusing PSR1 wakeup time for PSR2 in older VBTs */
+-              i915->vbt.psr.psr2_tp2_tp3_wakeup_time_us = i915->vbt.psr.tp2_tp3_wakeup_time_us;
++              panel->vbt.psr.psr2_tp2_tp3_wakeup_time_us = panel->vbt.psr.tp2_tp3_wakeup_time_us;
+       }
+ }
+ static void parse_dsi_backlight_ports(struct drm_i915_private *i915,
+-                                    u16 version, enum port port)
++                                    struct intel_panel *panel,
++                                    enum port port)
+ {
+-      if (!i915->vbt.dsi.config->dual_link || version < 197) {
+-              i915->vbt.dsi.bl_ports = BIT(port);
+-              if (i915->vbt.dsi.config->cabc_supported)
+-                      i915->vbt.dsi.cabc_ports = BIT(port);
++      if (!panel->vbt.dsi.config->dual_link || i915->vbt.version < 197) {
++              panel->vbt.dsi.bl_ports = BIT(port);
++              if (panel->vbt.dsi.config->cabc_supported)
++                      panel->vbt.dsi.cabc_ports = BIT(port);
+               return;
+       }
+-      switch (i915->vbt.dsi.config->dl_dcs_backlight_ports) {
++      switch (panel->vbt.dsi.config->dl_dcs_backlight_ports) {
+       case DL_DCS_PORT_A:
+-              i915->vbt.dsi.bl_ports = BIT(PORT_A);
++              panel->vbt.dsi.bl_ports = BIT(PORT_A);
+               break;
+       case DL_DCS_PORT_C:
+-              i915->vbt.dsi.bl_ports = BIT(PORT_C);
++              panel->vbt.dsi.bl_ports = BIT(PORT_C);
+               break;
+       default:
+       case DL_DCS_PORT_A_AND_C:
+-              i915->vbt.dsi.bl_ports = BIT(PORT_A) | BIT(PORT_C);
++              panel->vbt.dsi.bl_ports = BIT(PORT_A) | BIT(PORT_C);
+               break;
+       }
+-      if (!i915->vbt.dsi.config->cabc_supported)
++      if (!panel->vbt.dsi.config->cabc_supported)
+               return;
+-      switch (i915->vbt.dsi.config->dl_dcs_cabc_ports) {
++      switch (panel->vbt.dsi.config->dl_dcs_cabc_ports) {
+       case DL_DCS_PORT_A:
+-              i915->vbt.dsi.cabc_ports = BIT(PORT_A);
++              panel->vbt.dsi.cabc_ports = BIT(PORT_A);
+               break;
+       case DL_DCS_PORT_C:
+-              i915->vbt.dsi.cabc_ports = BIT(PORT_C);
++              panel->vbt.dsi.cabc_ports = BIT(PORT_C);
+               break;
+       default:
+       case DL_DCS_PORT_A_AND_C:
+-              i915->vbt.dsi.cabc_ports =
++              panel->vbt.dsi.cabc_ports =
+                                       BIT(PORT_A) | BIT(PORT_C);
+               break;
+       }
+ }
+ static void
+-parse_mipi_config(struct drm_i915_private *i915)
++parse_mipi_config(struct drm_i915_private *i915,
++                struct intel_panel *panel)
+ {
+       const struct bdb_mipi_config *start;
+       const struct mipi_config *config;
+       const struct mipi_pps_data *pps;
+-      int panel_type = i915->vbt.panel_type;
++      int panel_type = panel->vbt.panel_type;
+       enum port port;
+       /* parse MIPI blocks only if LFP type is MIPI */
+@@ -1515,7 +1527,7 @@ parse_mipi_config(struct drm_i915_private *i915)
+               return;
+       /* Initialize this to undefined indicating no generic MIPI support */
+-      i915->vbt.dsi.panel_id = MIPI_DSI_UNDEFINED_PANEL_ID;
++      panel->vbt.dsi.panel_id = MIPI_DSI_UNDEFINED_PANEL_ID;
+       /* Block #40 is already parsed and panel_fixed_mode is
+        * stored in i915->lfp_lvds_vbt_mode
+@@ -1542,17 +1554,17 @@ parse_mipi_config(struct drm_i915_private *i915)
+       pps = &start->pps[panel_type];
+       /* store as of now full data. Trim when we realise all is not needed */
+-      i915->vbt.dsi.config = kmemdup(config, sizeof(struct mipi_config), GFP_KERNEL);
+-      if (!i915->vbt.dsi.config)
++      panel->vbt.dsi.config = kmemdup(config, sizeof(struct mipi_config), GFP_KERNEL);
++      if (!panel->vbt.dsi.config)
+               return;
+-      i915->vbt.dsi.pps = kmemdup(pps, sizeof(struct mipi_pps_data), GFP_KERNEL);
+-      if (!i915->vbt.dsi.pps) {
+-              kfree(i915->vbt.dsi.config);
++      panel->vbt.dsi.pps = kmemdup(pps, sizeof(struct mipi_pps_data), GFP_KERNEL);
++      if (!panel->vbt.dsi.pps) {
++              kfree(panel->vbt.dsi.config);
+               return;
+       }
+-      parse_dsi_backlight_ports(i915, i915->vbt.version, port);
++      parse_dsi_backlight_ports(i915, panel, port);
+       /* FIXME is the 90 vs. 270 correct? */
+       switch (config->rotation) {
+@@ -1561,25 +1573,25 @@ parse_mipi_config(struct drm_i915_private *i915)
+                * Most (all?) VBTs claim 0 degrees despite having
+                * an upside down panel, thus we do not trust this.
+                */
+-              i915->vbt.dsi.orientation =
++              panel->vbt.dsi.orientation =
+                       DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
+               break;
+       case ENABLE_ROTATION_90:
+-              i915->vbt.dsi.orientation =
++              panel->vbt.dsi.orientation =
+                       DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
+               break;
+       case ENABLE_ROTATION_180:
+-              i915->vbt.dsi.orientation =
++              panel->vbt.dsi.orientation =
+                       DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
+               break;
+       case ENABLE_ROTATION_270:
+-              i915->vbt.dsi.orientation =
++              panel->vbt.dsi.orientation =
+                       DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
+               break;
+       }
+       /* We have mandatory mipi config blocks. Initialize as generic panel */
+-      i915->vbt.dsi.panel_id = MIPI_DSI_GENERIC_PANEL_ID;
++      panel->vbt.dsi.panel_id = MIPI_DSI_GENERIC_PANEL_ID;
+ }
+ /* Find the sequence block and size for the given panel. */
+@@ -1742,13 +1754,14 @@ static int goto_next_sequence_v3(const u8 *data, int index, int total)
+  * Get len of pre-fixed deassert fragment from a v1 init OTP sequence,
+  * skip all delay + gpio operands and stop at the first DSI packet op.
+  */
+-static int get_init_otp_deassert_fragment_len(struct drm_i915_private *i915)
++static int get_init_otp_deassert_fragment_len(struct drm_i915_private *i915,
++                                            struct intel_panel *panel)
+ {
+-      const u8 *data = i915->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP];
++      const u8 *data = panel->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP];
+       int index, len;
+       if (drm_WARN_ON(&i915->drm,
+-                      !data || i915->vbt.dsi.seq_version != 1))
++                      !data || panel->vbt.dsi.seq_version != 1))
+               return 0;
+       /* index = 1 to skip sequence byte */
+@@ -1776,7 +1789,8 @@ static int get_init_otp_deassert_fragment_len(struct drm_i915_private *i915)
+  * these devices we split the init OTP sequence into a deassert sequence and
+  * the actual init OTP part.
+  */
+-static void fixup_mipi_sequences(struct drm_i915_private *i915)
++static void fixup_mipi_sequences(struct drm_i915_private *i915,
++                               struct intel_panel *panel)
+ {
+       u8 *init_otp;
+       int len;
+@@ -1786,18 +1800,18 @@ static void fixup_mipi_sequences(struct drm_i915_private *i915)
+               return;
+       /* Limit this to v1 vid-mode sequences */
+-      if (i915->vbt.dsi.config->is_cmd_mode ||
+-          i915->vbt.dsi.seq_version != 1)
++      if (panel->vbt.dsi.config->is_cmd_mode ||
++          panel->vbt.dsi.seq_version != 1)
+               return;
+       /* Only do this if there are otp and assert seqs and no deassert seq */
+-      if (!i915->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP] ||
+-          !i915->vbt.dsi.sequence[MIPI_SEQ_ASSERT_RESET] ||
+-          i915->vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET])
++      if (!panel->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP] ||
++          !panel->vbt.dsi.sequence[MIPI_SEQ_ASSERT_RESET] ||
++          panel->vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET])
+               return;
+       /* The deassert-sequence ends at the first DSI packet */
+-      len = get_init_otp_deassert_fragment_len(i915);
++      len = get_init_otp_deassert_fragment_len(i915, panel);
+       if (!len)
+               return;
+@@ -1805,25 +1819,26 @@ static void fixup_mipi_sequences(struct drm_i915_private *i915)
+                   "Using init OTP fragment to deassert reset\n");
+       /* Copy the fragment, update seq byte and terminate it */
+-      init_otp = (u8 *)i915->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP];
+-      i915->vbt.dsi.deassert_seq = kmemdup(init_otp, len + 1, GFP_KERNEL);
+-      if (!i915->vbt.dsi.deassert_seq)
++      init_otp = (u8 *)panel->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP];
++      panel->vbt.dsi.deassert_seq = kmemdup(init_otp, len + 1, GFP_KERNEL);
++      if (!panel->vbt.dsi.deassert_seq)
+               return;
+-      i915->vbt.dsi.deassert_seq[0] = MIPI_SEQ_DEASSERT_RESET;
+-      i915->vbt.dsi.deassert_seq[len] = MIPI_SEQ_ELEM_END;
++      panel->vbt.dsi.deassert_seq[0] = MIPI_SEQ_DEASSERT_RESET;
++      panel->vbt.dsi.deassert_seq[len] = MIPI_SEQ_ELEM_END;
+       /* Use the copy for deassert */
+-      i915->vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET] =
+-              i915->vbt.dsi.deassert_seq;
++      panel->vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET] =
++              panel->vbt.dsi.deassert_seq;
+       /* Replace the last byte of the fragment with init OTP seq byte */
+       init_otp[len - 1] = MIPI_SEQ_INIT_OTP;
+       /* And make MIPI_MIPI_SEQ_INIT_OTP point to it */
+-      i915->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP] = init_otp + len - 1;
++      panel->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP] = init_otp + len - 1;
+ }
+ static void
+-parse_mipi_sequence(struct drm_i915_private *i915)
++parse_mipi_sequence(struct drm_i915_private *i915,
++                  struct intel_panel *panel)
+ {
+-      int panel_type = i915->vbt.panel_type;
++      int panel_type = panel->vbt.panel_type;
+       const struct bdb_mipi_sequence *sequence;
+       const u8 *seq_data;
+       u32 seq_size;
+@@ -1831,7 +1846,7 @@ parse_mipi_sequence(struct drm_i915_private *i915)
+       int index = 0;
+       /* Only our generic panel driver uses the sequence block. */
+-      if (i915->vbt.dsi.panel_id != MIPI_DSI_GENERIC_PANEL_ID)
++      if (panel->vbt.dsi.panel_id != MIPI_DSI_GENERIC_PANEL_ID)
+               return;
+       sequence = find_section(i915, BDB_MIPI_SEQUENCE);
+@@ -1877,7 +1892,7 @@ parse_mipi_sequence(struct drm_i915_private *i915)
+                       drm_dbg_kms(&i915->drm,
+                                   "Unsupported sequence %u\n", seq_id);
+-              i915->vbt.dsi.sequence[seq_id] = data + index;
++              panel->vbt.dsi.sequence[seq_id] = data + index;
+               if (sequence->version >= 3)
+                       index = goto_next_sequence_v3(data, index, seq_size);
+@@ -1890,18 +1905,18 @@ parse_mipi_sequence(struct drm_i915_private *i915)
+               }
+       }
+-      i915->vbt.dsi.data = data;
+-      i915->vbt.dsi.size = seq_size;
+-      i915->vbt.dsi.seq_version = sequence->version;
++      panel->vbt.dsi.data = data;
++      panel->vbt.dsi.size = seq_size;
++      panel->vbt.dsi.seq_version = sequence->version;
+-      fixup_mipi_sequences(i915);
++      fixup_mipi_sequences(i915, panel);
+       drm_dbg(&i915->drm, "MIPI related VBT parsing complete\n");
+       return;
+ err:
+       kfree(data);
+-      memset(i915->vbt.dsi.sequence, 0, sizeof(i915->vbt.dsi.sequence));
++      memset(panel->vbt.dsi.sequence, 0, sizeof(panel->vbt.dsi.sequence));
+ }
+ static void
+@@ -2655,15 +2670,6 @@ init_vbt_defaults(struct drm_i915_private *i915)
+ {
+       i915->vbt.crt_ddc_pin = GMBUS_PIN_VGADDC;
+-      /* Default to having backlight */
+-      i915->vbt.backlight.present = true;
+-
+-      /* LFP panel data */
+-      i915->vbt.lvds_dither = 1;
+-
+-      /* SDVO panel data */
+-      i915->vbt.sdvo_lvds_vbt_mode = NULL;
+-
+       /* general features */
+       i915->vbt.int_tv_support = 1;
+       i915->vbt.int_crt_support = 1;
+@@ -2683,6 +2689,17 @@ init_vbt_defaults(struct drm_i915_private *i915)
+                   i915->vbt.lvds_ssc_freq);
+ }
++/* Common defaults which may be overridden by VBT. */
++static void
++init_vbt_panel_defaults(struct intel_panel *panel)
++{
++      /* Default to having backlight */
++      panel->vbt.backlight.present = true;
++
++      /* LFP panel data */
++      panel->vbt.lvds_dither = true;
++}
++
+ /* Defaults to initialize only if there is no VBT. */
+ static void
+ init_vbt_missing_defaults(struct drm_i915_private *i915)
+@@ -2988,19 +3005,22 @@ void intel_bios_init(struct drm_i915_private *i915)
+       kfree(oprom_vbt);
+ }
+-void intel_bios_init_panel(struct drm_i915_private *i915)
++void intel_bios_init_panel(struct drm_i915_private *i915,
++                         struct intel_panel *panel)
+ {
+-      parse_panel_options(i915);
+-      parse_generic_dtd(i915);
+-      parse_lfp_data(i915);
+-      parse_lfp_backlight(i915);
+-      parse_sdvo_panel_data(i915);
+-      parse_panel_driver_features(i915);
+-      parse_power_conservation_features(i915);
+-      parse_edp(i915);
+-      parse_psr(i915);
+-      parse_mipi_config(i915);
+-      parse_mipi_sequence(i915);
++      init_vbt_panel_defaults(panel);
++
++      parse_panel_options(i915, panel);
++      parse_generic_dtd(i915, panel);
++      parse_lfp_data(i915, panel);
++      parse_lfp_backlight(i915, panel);
++      parse_sdvo_panel_data(i915, panel);
++      parse_panel_driver_features(i915, panel);
++      parse_power_conservation_features(i915, panel);
++      parse_edp(i915, panel);
++      parse_psr(i915, panel);
++      parse_mipi_config(i915, panel);
++      parse_mipi_sequence(i915, panel);
+ }
+ /**
+@@ -3022,19 +3042,22 @@ void intel_bios_driver_remove(struct drm_i915_private *i915)
+               list_del(&entry->node);
+               kfree(entry);
+       }
++}
+-      kfree(i915->vbt.sdvo_lvds_vbt_mode);
+-      i915->vbt.sdvo_lvds_vbt_mode = NULL;
+-      kfree(i915->vbt.lfp_lvds_vbt_mode);
+-      i915->vbt.lfp_lvds_vbt_mode = NULL;
+-      kfree(i915->vbt.dsi.data);
+-      i915->vbt.dsi.data = NULL;
+-      kfree(i915->vbt.dsi.pps);
+-      i915->vbt.dsi.pps = NULL;
+-      kfree(i915->vbt.dsi.config);
+-      i915->vbt.dsi.config = NULL;
+-      kfree(i915->vbt.dsi.deassert_seq);
+-      i915->vbt.dsi.deassert_seq = NULL;
++void intel_bios_fini_panel(struct intel_panel *panel)
++{
++      kfree(panel->vbt.sdvo_lvds_vbt_mode);
++      panel->vbt.sdvo_lvds_vbt_mode = NULL;
++      kfree(panel->vbt.lfp_lvds_vbt_mode);
++      panel->vbt.lfp_lvds_vbt_mode = NULL;
++      kfree(panel->vbt.dsi.data);
++      panel->vbt.dsi.data = NULL;
++      kfree(panel->vbt.dsi.pps);
++      panel->vbt.dsi.pps = NULL;
++      kfree(panel->vbt.dsi.config);
++      panel->vbt.dsi.config = NULL;
++      kfree(panel->vbt.dsi.deassert_seq);
++      panel->vbt.dsi.deassert_seq = NULL;
+ }
+ /**
+diff --git a/drivers/gpu/drm/i915/display/intel_bios.h b/drivers/gpu/drm/i915/display/intel_bios.h
+index c744d75fa435..86129f015718 100644
+--- a/drivers/gpu/drm/i915/display/intel_bios.h
++++ b/drivers/gpu/drm/i915/display/intel_bios.h
+@@ -36,6 +36,7 @@ struct drm_i915_private;
+ struct intel_bios_encoder_data;
+ struct intel_crtc_state;
+ struct intel_encoder;
++struct intel_panel;
+ enum port;
+ enum intel_backlight_type {
+@@ -230,7 +231,9 @@ struct mipi_pps_data {
+ } __packed;
+ void intel_bios_init(struct drm_i915_private *dev_priv);
+-void intel_bios_init_panel(struct drm_i915_private *dev_priv);
++void intel_bios_init_panel(struct drm_i915_private *dev_priv,
++                         struct intel_panel *panel);
++void intel_bios_fini_panel(struct intel_panel *panel);
+ void intel_bios_driver_remove(struct drm_i915_private *dev_priv);
+ bool intel_bios_is_valid_vbt(const void *buf, size_t size);
+ bool intel_bios_is_tv_present(struct drm_i915_private *dev_priv);
+diff --git a/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c b/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c
+index 85f58dd3df72..b490acd0ab69 100644
+--- a/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c
++++ b/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c
+@@ -1062,17 +1062,18 @@ bool is_hobl_buf_trans(const struct intel_ddi_buf_trans *table)
+ static bool use_edp_hobl(struct intel_encoder *encoder)
+ {
+-      struct drm_i915_private *i915 = to_i915(encoder->base.dev);
+       struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
++      struct intel_connector *connector = intel_dp->attached_connector;
+-      return i915->vbt.edp.hobl && !intel_dp->hobl_failed;
++      return connector->panel.vbt.edp.hobl && !intel_dp->hobl_failed;
+ }
+ static bool use_edp_low_vswing(struct intel_encoder *encoder)
+ {
+-      struct drm_i915_private *i915 = to_i915(encoder->base.dev);
++      struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
++      struct intel_connector *connector = intel_dp->attached_connector;
+-      return i915->vbt.edp.low_vswing;
++      return connector->panel.vbt.edp.low_vswing;
+ }
+ static const struct intel_ddi_buf_trans *
+diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
+index e384db157f34..806d50b302ab 100644
+--- a/drivers/gpu/drm/i915/display/intel_display.c
++++ b/drivers/gpu/drm/i915/display/intel_display.c
+@@ -9580,7 +9580,6 @@ int intel_modeset_init_noirq(struct drm_i915_private *i915)
+       }
+       intel_bios_init(i915);
+-      intel_bios_init_panel(i915);
+       ret = intel_vga_register(i915);
+       if (ret)
+diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
+index 408152f9f46a..e2561c5d4953 100644
+--- a/drivers/gpu/drm/i915/display/intel_display_types.h
++++ b/drivers/gpu/drm/i915/display/intel_display_types.h
+@@ -279,6 +279,73 @@ struct intel_panel_bl_funcs {
+       u32 (*hz_to_pwm)(struct intel_connector *connector, u32 hz);
+ };
++enum drrs_type {
++      DRRS_TYPE_NONE,
++      DRRS_TYPE_STATIC,
++      DRRS_TYPE_SEAMLESS,
++};
++
++struct intel_vbt_panel_data {
++      struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
++      struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
++
++      /* Feature bits */
++      unsigned int panel_type:4;
++      unsigned int lvds_dither:1;
++      unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */
++
++      u8 seamless_drrs_min_refresh_rate;
++      enum drrs_type drrs_type;
++
++      struct {
++              int rate;
++              int lanes;
++              int preemphasis;
++              int vswing;
++              int bpp;
++              struct edp_power_seq pps;
++              u8 drrs_msa_timing_delay;
++              bool low_vswing;
++              bool initialized;
++              bool hobl;
++      } edp;
++
++      struct {
++              bool enable;
++              bool full_link;
++              bool require_aux_wakeup;
++              int idle_frames;
++              int tp1_wakeup_time_us;
++              int tp2_tp3_wakeup_time_us;
++              int psr2_tp2_tp3_wakeup_time_us;
++      } psr;
++
++      struct {
++              u16 pwm_freq_hz;
++              u16 brightness_precision_bits;
++              bool present;
++              bool active_low_pwm;
++              u8 min_brightness;      /* min_brightness/255 of max */
++              u8 controller;          /* brightness controller number */
++              enum intel_backlight_type type;
++      } backlight;
++
++      /* MIPI DSI */
++      struct {
++              u16 panel_id;
++              struct mipi_config *config;
++              struct mipi_pps_data *pps;
++              u16 bl_ports;
++              u16 cabc_ports;
++              u8 seq_version;
++              u32 size;
++              u8 *data;
++              const u8 *sequence[MIPI_SEQ_MAX];
++              u8 *deassert_seq; /* Used by fixup_mipi_sequences() */
++              enum drm_panel_orientation orientation;
++      } dsi;
++};
++
+ struct intel_panel {
+       struct list_head fixed_modes;
+@@ -318,6 +385,8 @@ struct intel_panel {
+               const struct intel_panel_bl_funcs *pwm_funcs;
+               void (*power)(struct intel_connector *, bool enable);
+       } backlight;
++
++      struct intel_vbt_panel_data vbt;
+ };
+ struct intel_digital_port;
+diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
+index affc820bf8d0..0efec6023fbe 100644
+--- a/drivers/gpu/drm/i915/display/intel_dp.c
++++ b/drivers/gpu/drm/i915/display/intel_dp.c
+@@ -1246,11 +1246,12 @@ static int intel_dp_max_bpp(struct intel_dp *intel_dp,
+       if (intel_dp_is_edp(intel_dp)) {
+               /* Get bpp from vbt only for panels that dont have bpp in edid */
+               if (intel_connector->base.display_info.bpc == 0 &&
+-                  dev_priv->vbt.edp.bpp && dev_priv->vbt.edp.bpp < bpp) {
++                  intel_connector->panel.vbt.edp.bpp &&
++                  intel_connector->panel.vbt.edp.bpp < bpp) {
+                       drm_dbg_kms(&dev_priv->drm,
+                                   "clamping bpp for eDP panel to BIOS-provided %i\n",
+-                                  dev_priv->vbt.edp.bpp);
+-                      bpp = dev_priv->vbt.edp.bpp;
++                                  intel_connector->panel.vbt.edp.bpp);
++                      bpp = intel_connector->panel.vbt.edp.bpp;
+               }
+       }
+@@ -1907,7 +1908,7 @@ intel_dp_drrs_compute_config(struct intel_connector *connector,
+       }
+       if (IS_IRONLAKE(i915) || IS_SANDYBRIDGE(i915) || IS_IVYBRIDGE(i915))
+-              pipe_config->msa_timing_delay = i915->vbt.edp.drrs_msa_timing_delay;
++              pipe_config->msa_timing_delay = connector->panel.vbt.edp.drrs_msa_timing_delay;
+       pipe_config->has_drrs = true;
+@@ -2740,8 +2741,10 @@ static void intel_edp_mso_mode_fixup(struct intel_connector *connector,
+ void intel_edp_fixup_vbt_bpp(struct intel_encoder *encoder, int pipe_bpp)
+ {
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
++      struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
++      struct intel_connector *connector = intel_dp->attached_connector;
+-      if (dev_priv->vbt.edp.bpp && pipe_bpp > dev_priv->vbt.edp.bpp) {
++      if (connector->panel.vbt.edp.bpp && pipe_bpp > connector->panel.vbt.edp.bpp) {
+               /*
+                * This is a big fat ugly hack.
+                *
+@@ -2757,8 +2760,8 @@ void intel_edp_fixup_vbt_bpp(struct intel_encoder *encoder, int pipe_bpp)
+                */
+               drm_dbg_kms(&dev_priv->drm,
+                           "pipe has %d bpp for eDP panel, overriding BIOS-provided max %d bpp\n",
+-                          pipe_bpp, dev_priv->vbt.edp.bpp);
+-              dev_priv->vbt.edp.bpp = pipe_bpp;
++                          pipe_bpp, connector->panel.vbt.edp.bpp);
++              connector->panel.vbt.edp.bpp = pipe_bpp;
+       }
+ }
+@@ -5237,8 +5240,10 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
+       }
+       intel_connector->edid = edid;
++      intel_bios_init_panel(dev_priv, &intel_connector->panel);
++
+       intel_panel_add_edid_fixed_modes(intel_connector,
+-                                       dev_priv->vbt.drrs_type != DRRS_TYPE_NONE);
++                                       intel_connector->panel.vbt.drrs_type != DRRS_TYPE_NONE);
+       /* MSO requires information from the EDID */
+       intel_edp_mso_init(intel_dp);
+diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h
+index e794d910df56..a54902c713a3 100644
+--- a/drivers/gpu/drm/i915/display/intel_dp.h
++++ b/drivers/gpu/drm/i915/display/intel_dp.h
+@@ -29,6 +29,7 @@ struct link_config_limits {
+       int min_bpp, max_bpp;
+ };
++void intel_edp_fixup_vbt_bpp(struct intel_encoder *encoder, int pipe_bpp);
+ void intel_dp_adjust_compliance_config(struct intel_dp *intel_dp,
+                                      struct intel_crtc_state *pipe_config,
+                                      struct link_config_limits *limits);
+diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
+index fb6cf30ee628..c92d5bb2326a 100644
+--- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
++++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
+@@ -370,7 +370,7 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector,
+       int ret;
+       ret = drm_edp_backlight_init(&intel_dp->aux, &panel->backlight.edp.vesa.info,
+-                                   i915->vbt.backlight.pwm_freq_hz, intel_dp->edp_dpcd,
++                                   panel->vbt.backlight.pwm_freq_hz, intel_dp->edp_dpcd,
+                                    &current_level, &current_mode);
+       if (ret < 0)
+               return ret;
+@@ -454,7 +454,7 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector)
+       case INTEL_DP_AUX_BACKLIGHT_OFF:
+               return -ENODEV;
+       case INTEL_DP_AUX_BACKLIGHT_AUTO:
+-              switch (i915->vbt.backlight.type) {
++              switch (panel->vbt.backlight.type) {
+               case INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE:
+                       try_vesa_interface = true;
+                       break;
+@@ -466,7 +466,7 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector)
+               }
+               break;
+       case INTEL_DP_AUX_BACKLIGHT_ON:
+-              if (i915->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE)
++              if (panel->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE)
+                       try_intel_interface = true;
+               try_vesa_interface = true;
+diff --git a/drivers/gpu/drm/i915/display/intel_drrs.c b/drivers/gpu/drm/i915/display/intel_drrs.c
+index 166caf293f7b..7da4a9cbe4ba 100644
+--- a/drivers/gpu/drm/i915/display/intel_drrs.c
++++ b/drivers/gpu/drm/i915/display/intel_drrs.c
+@@ -217,9 +217,6 @@ static void intel_drrs_frontbuffer_update(struct drm_i915_private *dev_priv,
+ {
+       struct intel_crtc *crtc;
+-      if (dev_priv->vbt.drrs_type != DRRS_TYPE_SEAMLESS)
+-              return;
+-
+       for_each_intel_crtc(&dev_priv->drm, crtc) {
+               unsigned int frontbuffer_bits;
+diff --git a/drivers/gpu/drm/i915/display/intel_dsi.c b/drivers/gpu/drm/i915/display/intel_dsi.c
+index 389a8c24cdc1..35e121cd226c 100644
+--- a/drivers/gpu/drm/i915/display/intel_dsi.c
++++ b/drivers/gpu/drm/i915/display/intel_dsi.c
+@@ -102,7 +102,7 @@ intel_dsi_get_panel_orientation(struct intel_connector *connector)
+       struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+       enum drm_panel_orientation orientation;
+-      orientation = dev_priv->vbt.dsi.orientation;
++      orientation = connector->panel.vbt.dsi.orientation;
+       if (orientation != DRM_MODE_PANEL_ORIENTATION_UNKNOWN)
+               return orientation;
+diff --git a/drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c b/drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c
+index 7d234429e71e..1bc7118c56a2 100644
+--- a/drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c
++++ b/drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c
+@@ -160,12 +160,10 @@ static void dcs_enable_backlight(const struct intel_crtc_state *crtc_state,
+ static int dcs_setup_backlight(struct intel_connector *connector,
+                              enum pipe unused)
+ {
+-      struct drm_device *dev = connector->base.dev;
+-      struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_panel *panel = &connector->panel;
+-      if (dev_priv->vbt.backlight.brightness_precision_bits > 8)
+-              panel->backlight.max = (1 << dev_priv->vbt.backlight.brightness_precision_bits) - 1;
++      if (panel->vbt.backlight.brightness_precision_bits > 8)
++              panel->backlight.max = (1 << panel->vbt.backlight.brightness_precision_bits) - 1;
+       else
+               panel->backlight.max = PANEL_PWM_MAX_VALUE;
+@@ -185,11 +183,10 @@ static const struct intel_panel_bl_funcs dcs_bl_funcs = {
+ int intel_dsi_dcs_init_backlight_funcs(struct intel_connector *intel_connector)
+ {
+       struct drm_device *dev = intel_connector->base.dev;
+-      struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_encoder *encoder = intel_attached_encoder(intel_connector);
+       struct intel_panel *panel = &intel_connector->panel;
+-      if (dev_priv->vbt.backlight.type != INTEL_BACKLIGHT_DSI_DCS)
++      if (panel->vbt.backlight.type != INTEL_BACKLIGHT_DSI_DCS)
+               return -ENODEV;
+       if (drm_WARN_ON(dev, encoder->type != INTEL_OUTPUT_DSI))
+diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c
+index dd24aef925f2..75e8cc4337c9 100644
+--- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c
++++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c
+@@ -240,9 +240,10 @@ static const u8 *mipi_exec_delay(struct intel_dsi *intel_dsi, const u8 *data)
+       return data;
+ }
+-static void vlv_exec_gpio(struct drm_i915_private *dev_priv,
++static void vlv_exec_gpio(struct intel_connector *connector,
+                         u8 gpio_source, u8 gpio_index, bool value)
+ {
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+       struct gpio_map *map;
+       u16 pconf0, padval;
+       u32 tmp;
+@@ -256,7 +257,7 @@ static void vlv_exec_gpio(struct drm_i915_private *dev_priv,
+       map = &vlv_gpio_table[gpio_index];
+-      if (dev_priv->vbt.dsi.seq_version >= 3) {
++      if (connector->panel.vbt.dsi.seq_version >= 3) {
+               /* XXX: this assumes vlv_gpio_table only has NC GPIOs. */
+               port = IOSF_PORT_GPIO_NC;
+       } else {
+@@ -287,14 +288,15 @@ static void vlv_exec_gpio(struct drm_i915_private *dev_priv,
+       vlv_iosf_sb_put(dev_priv, BIT(VLV_IOSF_SB_GPIO));
+ }
+-static void chv_exec_gpio(struct drm_i915_private *dev_priv,
++static void chv_exec_gpio(struct intel_connector *connector,
+                         u8 gpio_source, u8 gpio_index, bool value)
+ {
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+       u16 cfg0, cfg1;
+       u16 family_num;
+       u8 port;
+-      if (dev_priv->vbt.dsi.seq_version >= 3) {
++      if (connector->panel.vbt.dsi.seq_version >= 3) {
+               if (gpio_index >= CHV_GPIO_IDX_START_SE) {
+                       /* XXX: it's unclear whether 255->57 is part of SE. */
+                       gpio_index -= CHV_GPIO_IDX_START_SE;
+@@ -340,9 +342,10 @@ static void chv_exec_gpio(struct drm_i915_private *dev_priv,
+       vlv_iosf_sb_put(dev_priv, BIT(VLV_IOSF_SB_GPIO));
+ }
+-static void bxt_exec_gpio(struct drm_i915_private *dev_priv,
++static void bxt_exec_gpio(struct intel_connector *connector,
+                         u8 gpio_source, u8 gpio_index, bool value)
+ {
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+       /* XXX: this table is a quick ugly hack. */
+       static struct gpio_desc *bxt_gpio_table[U8_MAX + 1];
+       struct gpio_desc *gpio_desc = bxt_gpio_table[gpio_index];
+@@ -366,9 +369,11 @@ static void bxt_exec_gpio(struct drm_i915_private *dev_priv,
+       gpiod_set_value(gpio_desc, value);
+ }
+-static void icl_exec_gpio(struct drm_i915_private *dev_priv,
++static void icl_exec_gpio(struct intel_connector *connector,
+                         u8 gpio_source, u8 gpio_index, bool value)
+ {
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++
+       drm_dbg_kms(&dev_priv->drm, "Skipping ICL GPIO element execution\n");
+ }
+@@ -376,18 +381,19 @@ static const u8 *mipi_exec_gpio(struct intel_dsi *intel_dsi, const u8 *data)
+ {
+       struct drm_device *dev = intel_dsi->base.base.dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
++      struct intel_connector *connector = intel_dsi->attached_connector;
+       u8 gpio_source, gpio_index = 0, gpio_number;
+       bool value;
+       drm_dbg_kms(&dev_priv->drm, "\n");
+-      if (dev_priv->vbt.dsi.seq_version >= 3)
++      if (connector->panel.vbt.dsi.seq_version >= 3)
+               gpio_index = *data++;
+       gpio_number = *data++;
+       /* gpio source in sequence v2 only */
+-      if (dev_priv->vbt.dsi.seq_version == 2)
++      if (connector->panel.vbt.dsi.seq_version == 2)
+               gpio_source = (*data >> 1) & 3;
+       else
+               gpio_source = 0;
+@@ -396,13 +402,13 @@ static const u8 *mipi_exec_gpio(struct intel_dsi *intel_dsi, const u8 *data)
+       value = *data++ & 1;
+       if (DISPLAY_VER(dev_priv) >= 11)
+-              icl_exec_gpio(dev_priv, gpio_source, gpio_index, value);
++              icl_exec_gpio(connector, gpio_source, gpio_index, value);
+       else if (IS_VALLEYVIEW(dev_priv))
+-              vlv_exec_gpio(dev_priv, gpio_source, gpio_number, value);
++              vlv_exec_gpio(connector, gpio_source, gpio_number, value);
+       else if (IS_CHERRYVIEW(dev_priv))
+-              chv_exec_gpio(dev_priv, gpio_source, gpio_number, value);
++              chv_exec_gpio(connector, gpio_source, gpio_number, value);
+       else
+-              bxt_exec_gpio(dev_priv, gpio_source, gpio_index, value);
++              bxt_exec_gpio(connector, gpio_source, gpio_index, value);
+       return data;
+ }
+@@ -585,14 +591,15 @@ static void intel_dsi_vbt_exec(struct intel_dsi *intel_dsi,
+                              enum mipi_seq seq_id)
+ {
+       struct drm_i915_private *dev_priv = to_i915(intel_dsi->base.base.dev);
++      struct intel_connector *connector = intel_dsi->attached_connector;
+       const u8 *data;
+       fn_mipi_elem_exec mipi_elem_exec;
+       if (drm_WARN_ON(&dev_priv->drm,
+-                      seq_id >= ARRAY_SIZE(dev_priv->vbt.dsi.sequence)))
++                      seq_id >= ARRAY_SIZE(connector->panel.vbt.dsi.sequence)))
+               return;
+-      data = dev_priv->vbt.dsi.sequence[seq_id];
++      data = connector->panel.vbt.dsi.sequence[seq_id];
+       if (!data)
+               return;
+@@ -605,7 +612,7 @@ static void intel_dsi_vbt_exec(struct intel_dsi *intel_dsi,
+       data++;
+       /* Skip Size of Sequence. */
+-      if (dev_priv->vbt.dsi.seq_version >= 3)
++      if (connector->panel.vbt.dsi.seq_version >= 3)
+               data += 4;
+       while (1) {
+@@ -621,7 +628,7 @@ static void intel_dsi_vbt_exec(struct intel_dsi *intel_dsi,
+                       mipi_elem_exec = NULL;
+               /* Size of Operation. */
+-              if (dev_priv->vbt.dsi.seq_version >= 3)
++              if (connector->panel.vbt.dsi.seq_version >= 3)
+                       operation_size = *data++;
+               if (mipi_elem_exec) {
+@@ -669,10 +676,10 @@ void intel_dsi_vbt_exec_sequence(struct intel_dsi *intel_dsi,
+ void intel_dsi_msleep(struct intel_dsi *intel_dsi, int msec)
+ {
+-      struct drm_i915_private *dev_priv = to_i915(intel_dsi->base.base.dev);
++      struct intel_connector *connector = intel_dsi->attached_connector;
+       /* For v3 VBTs in vid-mode the delays are part of the VBT sequences */
+-      if (is_vid_mode(intel_dsi) && dev_priv->vbt.dsi.seq_version >= 3)
++      if (is_vid_mode(intel_dsi) && connector->panel.vbt.dsi.seq_version >= 3)
+               return;
+       msleep(msec);
+@@ -734,9 +741,10 @@ bool intel_dsi_vbt_init(struct intel_dsi *intel_dsi, u16 panel_id)
+ {
+       struct drm_device *dev = intel_dsi->base.base.dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+-      struct mipi_config *mipi_config = dev_priv->vbt.dsi.config;
+-      struct mipi_pps_data *pps = dev_priv->vbt.dsi.pps;
+-      struct drm_display_mode *mode = dev_priv->vbt.lfp_lvds_vbt_mode;
++      struct intel_connector *connector = intel_dsi->attached_connector;
++      struct mipi_config *mipi_config = connector->panel.vbt.dsi.config;
++      struct mipi_pps_data *pps = connector->panel.vbt.dsi.pps;
++      struct drm_display_mode *mode = connector->panel.vbt.lfp_lvds_vbt_mode;
+       u16 burst_mode_ratio;
+       enum port port;
+@@ -872,7 +880,8 @@ void intel_dsi_vbt_gpio_init(struct intel_dsi *intel_dsi, bool panel_is_on)
+ {
+       struct drm_device *dev = intel_dsi->base.base.dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+-      struct mipi_config *mipi_config = dev_priv->vbt.dsi.config;
++      struct intel_connector *connector = intel_dsi->attached_connector;
++      struct mipi_config *mipi_config = connector->panel.vbt.dsi.config;
+       enum gpiod_flags flags = panel_is_on ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW;
+       bool want_backlight_gpio = false;
+       bool want_panel_gpio = false;
+@@ -927,7 +936,8 @@ void intel_dsi_vbt_gpio_cleanup(struct intel_dsi *intel_dsi)
+ {
+       struct drm_device *dev = intel_dsi->base.base.dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+-      struct mipi_config *mipi_config = dev_priv->vbt.dsi.config;
++      struct intel_connector *connector = intel_dsi->attached_connector;
++      struct mipi_config *mipi_config = connector->panel.vbt.dsi.config;
+       if (intel_dsi->gpio_panel) {
+               gpiod_put(intel_dsi->gpio_panel);
+diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c
+index e8478161f8b9..9f250a70519a 100644
+--- a/drivers/gpu/drm/i915/display/intel_lvds.c
++++ b/drivers/gpu/drm/i915/display/intel_lvds.c
+@@ -809,7 +809,7 @@ static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder)
+       else
+               val &= ~(LVDS_DETECTED | LVDS_PIPE_SEL_MASK);
+       if (val == 0)
+-              val = dev_priv->vbt.bios_lvds_val;
++              val = connector->panel.vbt.bios_lvds_val;
+       return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP;
+ }
+@@ -967,9 +967,11 @@ void intel_lvds_init(struct drm_i915_private *dev_priv)
+       }
+       intel_connector->edid = edid;
++      intel_bios_init_panel(dev_priv, &intel_connector->panel);
++
+       /* Try EDID first */
+       intel_panel_add_edid_fixed_modes(intel_connector,
+-                                       dev_priv->vbt.drrs_type != DRRS_TYPE_NONE);
++                                       intel_connector->panel.vbt.drrs_type != DRRS_TYPE_NONE);
+       /* Failed to get EDID, what about VBT? */
+       if (!intel_panel_preferred_fixed_mode(intel_connector))
+diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c
+index d1d1b59102d6..d055e4118558 100644
+--- a/drivers/gpu/drm/i915/display/intel_panel.c
++++ b/drivers/gpu/drm/i915/display/intel_panel.c
+@@ -75,9 +75,8 @@ const struct drm_display_mode *
+ intel_panel_downclock_mode(struct intel_connector *connector,
+                          const struct drm_display_mode *adjusted_mode)
+ {
+-      struct drm_i915_private *i915 = to_i915(connector->base.dev);
+       const struct drm_display_mode *fixed_mode, *best_mode = NULL;
+-      int min_vrefresh = i915->vbt.seamless_drrs_min_refresh_rate;
++      int min_vrefresh = connector->panel.vbt.seamless_drrs_min_refresh_rate;
+       int max_vrefresh = drm_mode_vrefresh(adjusted_mode);
+       /* pick the fixed_mode with the lowest refresh rate */
+@@ -113,13 +112,11 @@ int intel_panel_get_modes(struct intel_connector *connector)
+ enum drrs_type intel_panel_drrs_type(struct intel_connector *connector)
+ {
+-      struct drm_i915_private *i915 = to_i915(connector->base.dev);
+-
+       if (list_empty(&connector->panel.fixed_modes) ||
+           list_is_singular(&connector->panel.fixed_modes))
+               return DRRS_TYPE_NONE;
+-      return i915->vbt.drrs_type;
++      return connector->panel.vbt.drrs_type;
+ }
+ int intel_panel_compute_config(struct intel_connector *connector,
+@@ -260,7 +257,7 @@ void intel_panel_add_vbt_lfp_fixed_mode(struct intel_connector *connector)
+       struct drm_i915_private *i915 = to_i915(connector->base.dev);
+       const struct drm_display_mode *mode;
+-      mode = i915->vbt.lfp_lvds_vbt_mode;
++      mode = connector->panel.vbt.lfp_lvds_vbt_mode;
+       if (!mode)
+               return;
+@@ -274,7 +271,7 @@ void intel_panel_add_vbt_sdvo_fixed_mode(struct intel_connector *connector)
+       struct drm_i915_private *i915 = to_i915(connector->base.dev);
+       const struct drm_display_mode *mode;
+-      mode = i915->vbt.sdvo_lvds_vbt_mode;
++      mode = connector->panel.vbt.sdvo_lvds_vbt_mode;
+       if (!mode)
+               return;
+@@ -639,6 +636,8 @@ void intel_panel_fini(struct intel_connector *connector)
+       intel_backlight_destroy(panel);
++      intel_bios_fini_panel(panel);
++
+       list_for_each_entry_safe(fixed_mode, next, &panel->fixed_modes, head) {
+               list_del(&fixed_mode->head);
+               drm_mode_destroy(connector->base.dev, fixed_mode);
+diff --git a/drivers/gpu/drm/i915/display/intel_pps.c b/drivers/gpu/drm/i915/display/intel_pps.c
+index 5b72c892a6f2..a226e4e5c569 100644
+--- a/drivers/gpu/drm/i915/display/intel_pps.c
++++ b/drivers/gpu/drm/i915/display/intel_pps.c
+@@ -209,7 +209,8 @@ static int
+ bxt_power_sequencer_idx(struct intel_dp *intel_dp)
+ {
+       struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+-      int backlight_controller = dev_priv->vbt.backlight.controller;
++      struct intel_connector *connector = intel_dp->attached_connector;
++      int backlight_controller = connector->panel.vbt.backlight.controller;
+       lockdep_assert_held(&dev_priv->pps_mutex);
+@@ -1175,8 +1176,9 @@ static void pps_init_delays_vbt(struct intel_dp *intel_dp,
+                               struct edp_power_seq *vbt)
+ {
+       struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
++      struct intel_connector *connector = intel_dp->attached_connector;
+-      *vbt = dev_priv->vbt.edp.pps;
++      *vbt = connector->panel.vbt.edp.pps;
+       /* On Toshiba Satellite P50-C-18C system the VBT T12 delay
+        * of 500ms appears to be too short. Ocassionally the panel
+diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
+index 06db407e2749..195ed9631128 100644
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -399,6 +399,7 @@ static void intel_psr_enable_sink(struct intel_dp *intel_dp)
+ static u32 intel_psr1_get_tp_time(struct intel_dp *intel_dp)
+ {
++      struct intel_connector *connector = intel_dp->attached_connector;
+       struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+       u32 val = 0;
+@@ -411,20 +412,20 @@ static u32 intel_psr1_get_tp_time(struct intel_dp *intel_dp)
+               goto check_tp3_sel;
+       }
+-      if (dev_priv->vbt.psr.tp1_wakeup_time_us == 0)
++      if (connector->panel.vbt.psr.tp1_wakeup_time_us == 0)
+               val |= EDP_PSR_TP1_TIME_0us;
+-      else if (dev_priv->vbt.psr.tp1_wakeup_time_us <= 100)
++      else if (connector->panel.vbt.psr.tp1_wakeup_time_us <= 100)
+               val |= EDP_PSR_TP1_TIME_100us;
+-      else if (dev_priv->vbt.psr.tp1_wakeup_time_us <= 500)
++      else if (connector->panel.vbt.psr.tp1_wakeup_time_us <= 500)
+               val |= EDP_PSR_TP1_TIME_500us;
+       else
+               val |= EDP_PSR_TP1_TIME_2500us;
+-      if (dev_priv->vbt.psr.tp2_tp3_wakeup_time_us == 0)
++      if (connector->panel.vbt.psr.tp2_tp3_wakeup_time_us == 0)
+               val |= EDP_PSR_TP2_TP3_TIME_0us;
+-      else if (dev_priv->vbt.psr.tp2_tp3_wakeup_time_us <= 100)
++      else if (connector->panel.vbt.psr.tp2_tp3_wakeup_time_us <= 100)
+               val |= EDP_PSR_TP2_TP3_TIME_100us;
+-      else if (dev_priv->vbt.psr.tp2_tp3_wakeup_time_us <= 500)
++      else if (connector->panel.vbt.psr.tp2_tp3_wakeup_time_us <= 500)
+               val |= EDP_PSR_TP2_TP3_TIME_500us;
+       else
+               val |= EDP_PSR_TP2_TP3_TIME_2500us;
+@@ -441,13 +442,14 @@ static u32 intel_psr1_get_tp_time(struct intel_dp *intel_dp)
+ static u8 psr_compute_idle_frames(struct intel_dp *intel_dp)
+ {
++      struct intel_connector *connector = intel_dp->attached_connector;
+       struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+       int idle_frames;
+       /* Let's use 6 as the minimum to cover all known cases including the
+        * off-by-one issue that HW has in some cases.
+        */
+-      idle_frames = max(6, dev_priv->vbt.psr.idle_frames);
++      idle_frames = max(6, connector->panel.vbt.psr.idle_frames);
+       idle_frames = max(idle_frames, intel_dp->psr.sink_sync_latency + 1);
+       if (drm_WARN_ON(&dev_priv->drm, idle_frames > 0xf))
+@@ -483,18 +485,19 @@ static void hsw_activate_psr1(struct intel_dp *intel_dp)
+ static u32 intel_psr2_get_tp_time(struct intel_dp *intel_dp)
+ {
++      struct intel_connector *connector = intel_dp->attached_connector;
+       struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+       u32 val = 0;
+       if (dev_priv->params.psr_safest_params)
+               return EDP_PSR2_TP2_TIME_2500us;
+-      if (dev_priv->vbt.psr.psr2_tp2_tp3_wakeup_time_us >= 0 &&
+-          dev_priv->vbt.psr.psr2_tp2_tp3_wakeup_time_us <= 50)
++      if (connector->panel.vbt.psr.psr2_tp2_tp3_wakeup_time_us >= 0 &&
++          connector->panel.vbt.psr.psr2_tp2_tp3_wakeup_time_us <= 50)
+               val |= EDP_PSR2_TP2_TIME_50us;
+-      else if (dev_priv->vbt.psr.psr2_tp2_tp3_wakeup_time_us <= 100)
++      else if (connector->panel.vbt.psr.psr2_tp2_tp3_wakeup_time_us <= 100)
+               val |= EDP_PSR2_TP2_TIME_100us;
+-      else if (dev_priv->vbt.psr.psr2_tp2_tp3_wakeup_time_us <= 500)
++      else if (connector->panel.vbt.psr.psr2_tp2_tp3_wakeup_time_us <= 500)
+               val |= EDP_PSR2_TP2_TIME_500us;
+       else
+               val |= EDP_PSR2_TP2_TIME_2500us;
+@@ -2344,6 +2347,7 @@ void intel_psr_flush(struct drm_i915_private *dev_priv,
+  */
+ void intel_psr_init(struct intel_dp *intel_dp)
+ {
++      struct intel_connector *connector = intel_dp->attached_connector;
+       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+       struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+@@ -2368,13 +2372,13 @@ void intel_psr_init(struct intel_dp *intel_dp)
+       intel_dp->psr.source_support = true;
+       if (dev_priv->params.enable_psr == -1)
+-              if (!dev_priv->vbt.psr.enable)
++              if (!connector->panel.vbt.psr.enable)
+                       dev_priv->params.enable_psr = 0;
+       /* Set link_standby x link_off defaults */
+       if (DISPLAY_VER(dev_priv) < 12)
+               /* For new platforms up to TGL let's respect VBT back again */
+-              intel_dp->psr.link_standby = dev_priv->vbt.psr.full_link;
++              intel_dp->psr.link_standby = connector->panel.vbt.psr.full_link;
+       INIT_WORK(&intel_dp->psr.work, intel_psr_work);
+       INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, tgl_dc3co_disable_work);
+diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c
+index d81855d57cdc..14a64bd61176 100644
+--- a/drivers/gpu/drm/i915/display/intel_sdvo.c
++++ b/drivers/gpu/drm/i915/display/intel_sdvo.c
+@@ -2869,6 +2869,7 @@ static bool
+ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device)
+ {
+       struct drm_encoder *encoder = &intel_sdvo->base.base;
++      struct drm_i915_private *i915 = to_i915(encoder->dev);
+       struct drm_connector *connector;
+       struct intel_connector *intel_connector;
+       struct intel_sdvo_connector *intel_sdvo_connector;
+@@ -2900,6 +2901,8 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device)
+       if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
+               goto err;
++      intel_bios_init_panel(i915, &intel_connector->panel);
++
+       /*
+        * Fetch modes from VBT. For SDVO prefer the VBT mode since some
+        * SDVO->LVDS transcoders can't cope with the EDID mode.
+diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c
+index 1954f07f0d3e..be8fd3c362df 100644
+--- a/drivers/gpu/drm/i915/display/vlv_dsi.c
++++ b/drivers/gpu/drm/i915/display/vlv_dsi.c
+@@ -782,6 +782,7 @@ static void intel_dsi_pre_enable(struct intel_atomic_state *state,
+ {
+       struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder);
+       struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc);
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       enum pipe pipe = crtc->pipe;
+       enum port port;
+@@ -838,7 +839,7 @@ static void intel_dsi_pre_enable(struct intel_atomic_state *state,
+        * the delay in that case. If there is no deassert-seq, then an
+        * unconditional msleep is used to give the panel time to power-on.
+        */
+-      if (dev_priv->vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET]) {
++      if (connector->panel.vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET]) {
+               intel_dsi_msleep(intel_dsi, intel_dsi->panel_on_delay);
+               intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_DEASSERT_RESET);
+       } else {
+@@ -1690,7 +1691,8 @@ static void vlv_dphy_param_init(struct intel_dsi *intel_dsi)
+ {
+       struct drm_device *dev = intel_dsi->base.base.dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+-      struct mipi_config *mipi_config = dev_priv->vbt.dsi.config;
++      struct intel_connector *connector = intel_dsi->attached_connector;
++      struct mipi_config *mipi_config = connector->panel.vbt.dsi.config;
+       u32 tlpx_ns, extra_byte_count, tlpx_ui;
+       u32 ui_num, ui_den;
+       u32 prepare_cnt, exit_zero_cnt, clk_zero_cnt, trail_cnt;
+@@ -1924,13 +1926,15 @@ void vlv_dsi_init(struct drm_i915_private *dev_priv)
+       intel_dsi->panel_power_off_time = ktime_get_boottime();
+-      if (dev_priv->vbt.dsi.config->dual_link)
++      intel_bios_init_panel(dev_priv, &intel_connector->panel);
++
++      if (intel_connector->panel.vbt.dsi.config->dual_link)
+               intel_dsi->ports = BIT(PORT_A) | BIT(PORT_C);
+       else
+               intel_dsi->ports = BIT(port);
+-      intel_dsi->dcs_backlight_ports = dev_priv->vbt.dsi.bl_ports;
+-      intel_dsi->dcs_cabc_ports = dev_priv->vbt.dsi.cabc_ports;
++      intel_dsi->dcs_backlight_ports = intel_connector->panel.vbt.dsi.bl_ports;
++      intel_dsi->dcs_cabc_ports = intel_connector->panel.vbt.dsi.cabc_ports;
+       /* Create a DSI host (and a device) for each port. */
+       for_each_dsi_port(port, intel_dsi->ports) {
+diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
+index 5184d70d4838..554d79bc0312 100644
+--- a/drivers/gpu/drm/i915/i915_drv.h
++++ b/drivers/gpu/drm/i915/i915_drv.h
+@@ -194,12 +194,6 @@ struct drm_i915_display_funcs {
+ #define I915_COLOR_UNEVICTABLE (-1) /* a non-vma sharing the address space */
+-enum drrs_type {
+-      DRRS_TYPE_NONE,
+-      DRRS_TYPE_STATIC,
+-      DRRS_TYPE_SEAMLESS,
+-};
+-
+ #define QUIRK_LVDS_SSC_DISABLE (1<<1)
+ #define QUIRK_INVERT_BRIGHTNESS (1<<2)
+ #define QUIRK_BACKLIGHT_PRESENT (1<<3)
+@@ -308,76 +302,19 @@ struct intel_vbt_data {
+       /* bdb version */
+       u16 version;
+-      struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
+-      struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
+-
+       /* Feature bits */
+       unsigned int int_tv_support:1;
+-      unsigned int lvds_dither:1;
+       unsigned int int_crt_support:1;
+       unsigned int lvds_use_ssc:1;
+       unsigned int int_lvds_support:1;
+       unsigned int display_clock_mode:1;
+       unsigned int fdi_rx_polarity_inverted:1;
+-      unsigned int panel_type:4;
+       int lvds_ssc_freq;
+-      unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */
+       enum drm_panel_orientation orientation;
+       bool override_afc_startup;
+       u8 override_afc_startup_val;
+-      u8 seamless_drrs_min_refresh_rate;
+-      enum drrs_type drrs_type;
+-
+-      struct {
+-              int rate;
+-              int lanes;
+-              int preemphasis;
+-              int vswing;
+-              int bpp;
+-              struct edp_power_seq pps;
+-              u8 drrs_msa_timing_delay;
+-              bool low_vswing;
+-              bool initialized;
+-              bool hobl;
+-      } edp;
+-
+-      struct {
+-              bool enable;
+-              bool full_link;
+-              bool require_aux_wakeup;
+-              int idle_frames;
+-              int tp1_wakeup_time_us;
+-              int tp2_tp3_wakeup_time_us;
+-              int psr2_tp2_tp3_wakeup_time_us;
+-      } psr;
+-
+-      struct {
+-              u16 pwm_freq_hz;
+-              u16 brightness_precision_bits;
+-              bool present;
+-              bool active_low_pwm;
+-              u8 min_brightness;      /* min_brightness/255 of max */
+-              u8 controller;          /* brightness controller number */
+-              enum intel_backlight_type type;
+-      } backlight;
+-
+-      /* MIPI DSI */
+-      struct {
+-              u16 panel_id;
+-              struct mipi_config *config;
+-              struct mipi_pps_data *pps;
+-              u16 bl_ports;
+-              u16 cabc_ports;
+-              u8 seq_version;
+-              u32 size;
+-              u8 *data;
+-              const u8 *sequence[MIPI_SEQ_MAX];
+-              u8 *deassert_seq; /* Used by fixup_mipi_sequences() */
+-              enum drm_panel_orientation orientation;
+-      } dsi;
+-
+       int crt_ddc_pin;
+       struct list_head display_devices;
+-- 
+2.35.1
+
diff --git a/queue-5.19/drm-i915-bios-split-vbt-parsing-to-global-vs.-panel-.patch b/queue-5.19/drm-i915-bios-split-vbt-parsing-to-global-vs.-panel-.patch
new file mode 100644 (file)
index 0000000..42838e6
--- /dev/null
@@ -0,0 +1,103 @@
+From ac9fe905031b0de0c092cb8010ed67a6f8d82f9b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 10 May 2022 13:42:38 +0300
+Subject: drm/i915/bios: Split VBT parsing to global vs. panel specific parts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ville Syrjälä <ville.syrjala@linux.intel.com>
+
+[ Upstream commit c2fdb424d32204faf5be29d55f0086b611c94e38 ]
+
+Parsing the panel specific data (anything that depends on panel_type)
+from VBT is currently happening too early. Split the whole thing
+into global vs. panel specific parts so that we can start doing
+the panel specific parsing at a later time.
+
+v2: Clarify that this is about panel_type (Jani)
+    Split out the leak checks (Jani)
+
+Reviewed-by: Jani Nikula <jani.nikula@intel.com>
+Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20220510104242.6099-12-ville.syrjala@linux.intel.com
+Stable-dep-of: 607f41768a1e ("drm/i915/dsi: filter invalid backlight and CABC ports")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_bios.c    | 26 +++++++++++---------
+ drivers/gpu/drm/i915/display/intel_bios.h    |  1 +
+ drivers/gpu/drm/i915/display/intel_display.c |  1 +
+ 3 files changed, 17 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c
+index 5ceabc380808..d5cae64b916b 100644
+--- a/drivers/gpu/drm/i915/display/intel_bios.c
++++ b/drivers/gpu/drm/i915/display/intel_bios.c
+@@ -2969,18 +2969,7 @@ void intel_bios_init(struct drm_i915_private *i915)
+       /* Grab useful general definitions */
+       parse_general_features(i915);
+       parse_general_definitions(i915);
+-      parse_panel_options(i915);
+-      parse_generic_dtd(i915);
+-      parse_lfp_data(i915);
+-      parse_lfp_backlight(i915);
+-      parse_sdvo_panel_data(i915);
+       parse_driver_features(i915);
+-      parse_panel_driver_features(i915);
+-      parse_power_conservation_features(i915);
+-      parse_edp(i915);
+-      parse_psr(i915);
+-      parse_mipi_config(i915);
+-      parse_mipi_sequence(i915);
+       /* Depends on child device list */
+       parse_compression_parameters(i915);
+@@ -2999,6 +2988,21 @@ void intel_bios_init(struct drm_i915_private *i915)
+       kfree(oprom_vbt);
+ }
++void intel_bios_init_panel(struct drm_i915_private *i915)
++{
++      parse_panel_options(i915);
++      parse_generic_dtd(i915);
++      parse_lfp_data(i915);
++      parse_lfp_backlight(i915);
++      parse_sdvo_panel_data(i915);
++      parse_panel_driver_features(i915);
++      parse_power_conservation_features(i915);
++      parse_edp(i915);
++      parse_psr(i915);
++      parse_mipi_config(i915);
++      parse_mipi_sequence(i915);
++}
++
+ /**
+  * intel_bios_driver_remove - Free any resources allocated by intel_bios_init()
+  * @i915: i915 device instance
+diff --git a/drivers/gpu/drm/i915/display/intel_bios.h b/drivers/gpu/drm/i915/display/intel_bios.h
+index 4709c4d29805..c744d75fa435 100644
+--- a/drivers/gpu/drm/i915/display/intel_bios.h
++++ b/drivers/gpu/drm/i915/display/intel_bios.h
+@@ -230,6 +230,7 @@ struct mipi_pps_data {
+ } __packed;
+ void intel_bios_init(struct drm_i915_private *dev_priv);
++void intel_bios_init_panel(struct drm_i915_private *dev_priv);
+ void intel_bios_driver_remove(struct drm_i915_private *dev_priv);
+ bool intel_bios_is_valid_vbt(const void *buf, size_t size);
+ bool intel_bios_is_tv_present(struct drm_i915_private *dev_priv);
+diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
+index 806d50b302ab..e384db157f34 100644
+--- a/drivers/gpu/drm/i915/display/intel_display.c
++++ b/drivers/gpu/drm/i915/display/intel_display.c
+@@ -9580,6 +9580,7 @@ int intel_modeset_init_noirq(struct drm_i915_private *i915)
+       }
+       intel_bios_init(i915);
++      intel_bios_init_panel(i915);
+       ret = intel_vga_register(i915);
+       if (ret)
+-- 
+2.35.1
+
diff --git a/queue-5.19/drm-i915-dsi-filter-invalid-backlight-and-cabc-ports.patch b/queue-5.19/drm-i915-dsi-filter-invalid-backlight-and-cabc-ports.patch
new file mode 100644 (file)
index 0000000..bd71e06
--- /dev/null
@@ -0,0 +1,66 @@
+From 68d2795bac565378bf29af0db89f74a8634fc98f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Aug 2022 18:37:20 +0300
+Subject: drm/i915/dsi: filter invalid backlight and CABC ports
+
+From: Jani Nikula <jani.nikula@intel.com>
+
+[ Upstream commit 607f41768a1ef9c7721866b00fbdeeea5359bc07 ]
+
+Avoid using ports that aren't initialized in case the VBT backlight or
+CABC ports have invalid values. This fixes a NULL pointer dereference of
+intel_dsi->dsi_hosts[port] in such cases.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Jani Nikula <jani.nikula@intel.com>
+Reviewed-by: Stanislav Lisovskiy <stanislav.lisovskiy@intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/b0f4f087866257d280eb97d6bcfcefd109cc5fa2.1660664162.git.jani.nikula@intel.com
+(cherry picked from commit f4a6c7a454a6e71c5ccf25af82694213a9784013)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/icl_dsi.c | 7 +++++++
+ drivers/gpu/drm/i915/display/vlv_dsi.c | 7 +++++++
+ 2 files changed, 14 insertions(+)
+
+diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c
+index 5c46acb46516..f416499dad6f 100644
+--- a/drivers/gpu/drm/i915/display/icl_dsi.c
++++ b/drivers/gpu/drm/i915/display/icl_dsi.c
+@@ -2072,7 +2072,14 @@ void icl_dsi_init(struct drm_i915_private *dev_priv)
+       else
+               intel_dsi->ports = BIT(port);
++      if (drm_WARN_ON(&dev_priv->drm, intel_connector->panel.vbt.dsi.bl_ports & ~intel_dsi->ports))
++              intel_connector->panel.vbt.dsi.bl_ports &= intel_dsi->ports;
++
+       intel_dsi->dcs_backlight_ports = intel_connector->panel.vbt.dsi.bl_ports;
++
++      if (drm_WARN_ON(&dev_priv->drm, intel_connector->panel.vbt.dsi.cabc_ports & ~intel_dsi->ports))
++              intel_connector->panel.vbt.dsi.cabc_ports &= intel_dsi->ports;
++
+       intel_dsi->dcs_cabc_ports = intel_connector->panel.vbt.dsi.cabc_ports;
+       for_each_dsi_port(port, intel_dsi->ports) {
+diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c
+index be8fd3c362df..02f75e95b2ec 100644
+--- a/drivers/gpu/drm/i915/display/vlv_dsi.c
++++ b/drivers/gpu/drm/i915/display/vlv_dsi.c
+@@ -1933,7 +1933,14 @@ void vlv_dsi_init(struct drm_i915_private *dev_priv)
+       else
+               intel_dsi->ports = BIT(port);
++      if (drm_WARN_ON(&dev_priv->drm, intel_connector->panel.vbt.dsi.bl_ports & ~intel_dsi->ports))
++              intel_connector->panel.vbt.dsi.bl_ports &= intel_dsi->ports;
++
+       intel_dsi->dcs_backlight_ports = intel_connector->panel.vbt.dsi.bl_ports;
++
++      if (drm_WARN_ON(&dev_priv->drm, intel_connector->panel.vbt.dsi.cabc_ports & ~intel_dsi->ports))
++              intel_connector->panel.vbt.dsi.cabc_ports &= intel_dsi->ports;
++
+       intel_dsi->dcs_cabc_ports = intel_connector->panel.vbt.dsi.cabc_ports;
+       /* Create a DSI host (and a device) for each port. */
+-- 
+2.35.1
+
diff --git a/queue-5.19/drm-i915-dsi-fix-dual-link-dsi-backlight-and-cabc-po.patch b/queue-5.19/drm-i915-dsi-fix-dual-link-dsi-backlight-and-cabc-po.patch
new file mode 100644 (file)
index 0000000..fb19139
--- /dev/null
@@ -0,0 +1,72 @@
+From f9b0de5da1b03b733800d36abb9da23ca3b1bf3d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Aug 2022 18:37:21 +0300
+Subject: drm/i915/dsi: fix dual-link DSI backlight and CABC ports for display
+ 11+
+
+From: Jani Nikula <jani.nikula@intel.com>
+
+[ Upstream commit 13393f65b77445d8b0f99c7b605cc9ccc936586f ]
+
+The VBT dual-link DSI backlight and CABC still use ports A and C, both
+in Bspec and code, while display 11+ DSI only supports ports A and
+B. Assume port C actually means port B for display 11+ when parsing VBT.
+
+Bspec: 20154
+Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/6476
+Cc: stable@vger.kernel.org
+Signed-off-by: Jani Nikula <jani.nikula@intel.com>
+Reviewed-by: Stanislav Lisovskiy <stanislav.lisovskiy@intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/8c462718bcc7b36a83e09d0a5eef058b6bc8b1a2.1660664162.git.jani.nikula@intel.com
+(cherry picked from commit ab55165d73a444606af1530cd0d6448b04370f68)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_bios.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c
+index d5d20a44f373..b5de61fe9cc6 100644
+--- a/drivers/gpu/drm/i915/display/intel_bios.c
++++ b/drivers/gpu/drm/i915/display/intel_bios.c
+@@ -1473,6 +1473,8 @@ static void parse_dsi_backlight_ports(struct drm_i915_private *i915,
+                                     struct intel_panel *panel,
+                                     enum port port)
+ {
++      enum port port_bc = DISPLAY_VER(i915) >= 11 ? PORT_B : PORT_C;
++
+       if (!panel->vbt.dsi.config->dual_link || i915->vbt.version < 197) {
+               panel->vbt.dsi.bl_ports = BIT(port);
+               if (panel->vbt.dsi.config->cabc_supported)
+@@ -1486,11 +1488,11 @@ static void parse_dsi_backlight_ports(struct drm_i915_private *i915,
+               panel->vbt.dsi.bl_ports = BIT(PORT_A);
+               break;
+       case DL_DCS_PORT_C:
+-              panel->vbt.dsi.bl_ports = BIT(PORT_C);
++              panel->vbt.dsi.bl_ports = BIT(port_bc);
+               break;
+       default:
+       case DL_DCS_PORT_A_AND_C:
+-              panel->vbt.dsi.bl_ports = BIT(PORT_A) | BIT(PORT_C);
++              panel->vbt.dsi.bl_ports = BIT(PORT_A) | BIT(port_bc);
+               break;
+       }
+@@ -1502,12 +1504,12 @@ static void parse_dsi_backlight_ports(struct drm_i915_private *i915,
+               panel->vbt.dsi.cabc_ports = BIT(PORT_A);
+               break;
+       case DL_DCS_PORT_C:
+-              panel->vbt.dsi.cabc_ports = BIT(PORT_C);
++              panel->vbt.dsi.cabc_ports = BIT(port_bc);
+               break;
+       default:
+       case DL_DCS_PORT_A_AND_C:
+               panel->vbt.dsi.cabc_ports =
+-                                      BIT(PORT_A) | BIT(PORT_C);
++                                      BIT(PORT_A) | BIT(port_bc);
+               break;
+       }
+ }
+-- 
+2.35.1
+
diff --git a/queue-5.19/drm-i915-extract-intel_edp_fixup_vbt_bpp.patch b/queue-5.19/drm-i915-extract-intel_edp_fixup_vbt_bpp.patch
new file mode 100644 (file)
index 0000000..186bcdb
--- /dev/null
@@ -0,0 +1,147 @@
+From 797e7c7efce2c27c112ce4fe231800d0951e00ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 10 May 2022 13:42:29 +0300
+Subject: drm/i915: Extract intel_edp_fixup_vbt_bpp()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ville Syrjälä <ville.syrjala@linux.intel.com>
+
+[ Upstream commit 822e5ae701af2964c5808b6ade1d6f3b1eaec967 ]
+
+We have the same "override eDP VBT bpp with the current bpp" code
+duplciated in two places. Extract it to a helper function.
+
+TODO: Having this in .get_config() is pretty ugly. Should probably
+try to move it somewhere else (setup_hw_state()/etc.)...
+
+Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20220510104242.6099-3-ville.syrjala@linux.intel.com
+Reviewed-by: Jani Nikula <jani.nikula@intel.com>
+Stable-dep-of: 607f41768a1e ("drm/i915/dsi: filter invalid backlight and CABC ports")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/g4x_dp.c    | 22 ++-------------------
+ drivers/gpu/drm/i915/display/intel_ddi.c | 22 ++-------------------
+ drivers/gpu/drm/i915/display/intel_dp.c  | 25 ++++++++++++++++++++++++
+ drivers/gpu/drm/i915/display/intel_dp.h  |  1 +
+ 4 files changed, 30 insertions(+), 40 deletions(-)
+
+diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c
+index 5a957acebfd6..82ad8fe7440c 100644
+--- a/drivers/gpu/drm/i915/display/g4x_dp.c
++++ b/drivers/gpu/drm/i915/display/g4x_dp.c
+@@ -395,26 +395,8 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
+               intel_dotclock_calculate(pipe_config->port_clock,
+                                        &pipe_config->dp_m_n);
+-      if (intel_dp_is_edp(intel_dp) && dev_priv->vbt.edp.bpp &&
+-          pipe_config->pipe_bpp > dev_priv->vbt.edp.bpp) {
+-              /*
+-               * This is a big fat ugly hack.
+-               *
+-               * Some machines in UEFI boot mode provide us a VBT that has 18
+-               * bpp and 1.62 GHz link bandwidth for eDP, which for reasons
+-               * unknown we fail to light up. Yet the same BIOS boots up with
+-               * 24 bpp and 2.7 GHz link. Use the same bpp as the BIOS uses as
+-               * max, not what it tells us to use.
+-               *
+-               * Note: This will still be broken if the eDP panel is not lit
+-               * up by the BIOS, and thus we can't get the mode at module
+-               * load.
+-               */
+-              drm_dbg_kms(&dev_priv->drm,
+-                          "pipe has %d bpp for eDP panel, overriding BIOS-provided max %d bpp\n",
+-                          pipe_config->pipe_bpp, dev_priv->vbt.edp.bpp);
+-              dev_priv->vbt.edp.bpp = pipe_config->pipe_bpp;
+-      }
++      if (intel_dp_is_edp(intel_dp))
++              intel_edp_fixup_vbt_bpp(encoder, pipe_config->pipe_bpp);
+ }
+ static void
+diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
+index 9e6fa59eabba..333871cf3a2c 100644
+--- a/drivers/gpu/drm/i915/display/intel_ddi.c
++++ b/drivers/gpu/drm/i915/display/intel_ddi.c
+@@ -3433,26 +3433,8 @@ static void intel_ddi_get_config(struct intel_encoder *encoder,
+       pipe_config->has_audio =
+               intel_ddi_is_audio_enabled(dev_priv, cpu_transcoder);
+-      if (encoder->type == INTEL_OUTPUT_EDP && dev_priv->vbt.edp.bpp &&
+-          pipe_config->pipe_bpp > dev_priv->vbt.edp.bpp) {
+-              /*
+-               * This is a big fat ugly hack.
+-               *
+-               * Some machines in UEFI boot mode provide us a VBT that has 18
+-               * bpp and 1.62 GHz link bandwidth for eDP, which for reasons
+-               * unknown we fail to light up. Yet the same BIOS boots up with
+-               * 24 bpp and 2.7 GHz link. Use the same bpp as the BIOS uses as
+-               * max, not what it tells us to use.
+-               *
+-               * Note: This will still be broken if the eDP panel is not lit
+-               * up by the BIOS, and thus we can't get the mode at module
+-               * load.
+-               */
+-              drm_dbg_kms(&dev_priv->drm,
+-                          "pipe has %d bpp for eDP panel, overriding BIOS-provided max %d bpp\n",
+-                          pipe_config->pipe_bpp, dev_priv->vbt.edp.bpp);
+-              dev_priv->vbt.edp.bpp = pipe_config->pipe_bpp;
+-      }
++      if (encoder->type == INTEL_OUTPUT_EDP)
++              intel_edp_fixup_vbt_bpp(encoder, pipe_config->pipe_bpp);
+       ddi_dotclock_get(pipe_config);
+diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
+index fe8b6b72970a..affc820bf8d0 100644
+--- a/drivers/gpu/drm/i915/display/intel_dp.c
++++ b/drivers/gpu/drm/i915/display/intel_dp.c
+@@ -2737,6 +2737,31 @@ static void intel_edp_mso_mode_fixup(struct intel_connector *connector,
+                   DRM_MODE_ARG(mode));
+ }
++void intel_edp_fixup_vbt_bpp(struct intel_encoder *encoder, int pipe_bpp)
++{
++      struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
++
++      if (dev_priv->vbt.edp.bpp && pipe_bpp > dev_priv->vbt.edp.bpp) {
++              /*
++               * This is a big fat ugly hack.
++               *
++               * Some machines in UEFI boot mode provide us a VBT that has 18
++               * bpp and 1.62 GHz link bandwidth for eDP, which for reasons
++               * unknown we fail to light up. Yet the same BIOS boots up with
++               * 24 bpp and 2.7 GHz link. Use the same bpp as the BIOS uses as
++               * max, not what it tells us to use.
++               *
++               * Note: This will still be broken if the eDP panel is not lit
++               * up by the BIOS, and thus we can't get the mode at module
++               * load.
++               */
++              drm_dbg_kms(&dev_priv->drm,
++                          "pipe has %d bpp for eDP panel, overriding BIOS-provided max %d bpp\n",
++                          pipe_bpp, dev_priv->vbt.edp.bpp);
++              dev_priv->vbt.edp.bpp = pipe_bpp;
++      }
++}
++
+ static void intel_edp_mso_init(struct intel_dp *intel_dp)
+ {
+       struct drm_i915_private *i915 = dp_to_i915(intel_dp);
+diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h
+index d457e17bdc57..e794d910df56 100644
+--- a/drivers/gpu/drm/i915/display/intel_dp.h
++++ b/drivers/gpu/drm/i915/display/intel_dp.h
+@@ -63,6 +63,7 @@ enum irqreturn intel_dp_hpd_pulse(struct intel_digital_port *dig_port,
+ void intel_edp_backlight_on(const struct intel_crtc_state *crtc_state,
+                           const struct drm_connector_state *conn_state);
+ void intel_edp_backlight_off(const struct drm_connector_state *conn_state);
++void intel_edp_fixup_vbt_bpp(struct intel_encoder *encoder, int pipe_bpp);
+ void intel_dp_mst_suspend(struct drm_i915_private *dev_priv);
+ void intel_dp_mst_resume(struct drm_i915_private *dev_priv);
+ int intel_dp_max_link_rate(struct intel_dp *intel_dp);
+-- 
+2.35.1
+
diff --git a/queue-5.19/drm-i915-pps-split-pps_init_delays-into-distinct-par.patch b/queue-5.19/drm-i915-pps-split-pps_init_delays-into-distinct-par.patch
new file mode 100644 (file)
index 0000000..dddb111
--- /dev/null
@@ -0,0 +1,133 @@
+From f5691f684277ee39b22b19193956ac4af131f923 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 10 May 2022 13:42:30 +0300
+Subject: drm/i915/pps: Split pps_init_delays() into distinct parts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ville Syrjälä <ville.syrjala@linux.intel.com>
+
+[ Upstream commit 75bd0d5e4eadb9ce3e9b6fb71971b6e87c38799e ]
+
+Split each of the hw/vbt/spec PPS delay initialization into
+separate functions to make the whole thing less cluttered.
+
+Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20220510104242.6099-4-ville.syrjala@linux.intel.com
+Reviewed-by: Jani Nikula <jani.nikula@intel.com>
+Stable-dep-of: 607f41768a1e ("drm/i915/dsi: filter invalid backlight and CABC ports")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_pps.c | 66 +++++++++++++++++-------
+ 1 file changed, 48 insertions(+), 18 deletions(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_pps.c b/drivers/gpu/drm/i915/display/intel_pps.c
+index 5a598dd06039..5b72c892a6f2 100644
+--- a/drivers/gpu/drm/i915/display/intel_pps.c
++++ b/drivers/gpu/drm/i915/display/intel_pps.c
+@@ -1159,53 +1159,83 @@ intel_pps_verify_state(struct intel_dp *intel_dp)
+       }
+ }
+-static void pps_init_delays(struct intel_dp *intel_dp)
++static void pps_init_delays_cur(struct intel_dp *intel_dp,
++                              struct edp_power_seq *cur)
+ {
+       struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+-      struct edp_power_seq cur, vbt, spec,
+-              *final = &intel_dp->pps.pps_delays;
+       lockdep_assert_held(&dev_priv->pps_mutex);
+-      /* already initialized? */
+-      if (final->t11_t12 != 0)
+-              return;
++      intel_pps_readout_hw_state(intel_dp, cur);
++
++      intel_pps_dump_state(intel_dp, "cur", cur);
++}
+-      intel_pps_readout_hw_state(intel_dp, &cur);
++static void pps_init_delays_vbt(struct intel_dp *intel_dp,
++                              struct edp_power_seq *vbt)
++{
++      struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+-      intel_pps_dump_state(intel_dp, "cur", &cur);
++      *vbt = dev_priv->vbt.edp.pps;
+-      vbt = dev_priv->vbt.edp.pps;
+       /* On Toshiba Satellite P50-C-18C system the VBT T12 delay
+        * of 500ms appears to be too short. Ocassionally the panel
+        * just fails to power back on. Increasing the delay to 800ms
+        * seems sufficient to avoid this problem.
+        */
+       if (dev_priv->quirks & QUIRK_INCREASE_T12_DELAY) {
+-              vbt.t11_t12 = max_t(u16, vbt.t11_t12, 1300 * 10);
++              vbt->t11_t12 = max_t(u16, vbt->t11_t12, 1300 * 10);
+               drm_dbg_kms(&dev_priv->drm,
+                           "Increasing T12 panel delay as per the quirk to %d\n",
+-                          vbt.t11_t12);
++                          vbt->t11_t12);
+       }
++
+       /* T11_T12 delay is special and actually in units of 100ms, but zero
+        * based in the hw (so we need to add 100 ms). But the sw vbt
+        * table multiplies it with 1000 to make it in units of 100usec,
+        * too. */
+-      vbt.t11_t12 += 100 * 10;
++      vbt->t11_t12 += 100 * 10;
++
++      intel_pps_dump_state(intel_dp, "vbt", vbt);
++}
++
++static void pps_init_delays_spec(struct intel_dp *intel_dp,
++                               struct edp_power_seq *spec)
++{
++      struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
++
++      lockdep_assert_held(&dev_priv->pps_mutex);
+       /* Upper limits from eDP 1.3 spec. Note that we use the clunky units of
+        * our hw here, which are all in 100usec. */
+-      spec.t1_t3 = 210 * 10;
+-      spec.t8 = 50 * 10; /* no limit for t8, use t7 instead */
+-      spec.t9 = 50 * 10; /* no limit for t9, make it symmetric with t8 */
+-      spec.t10 = 500 * 10;
++      spec->t1_t3 = 210 * 10;
++      spec->t8 = 50 * 10; /* no limit for t8, use t7 instead */
++      spec->t9 = 50 * 10; /* no limit for t9, make it symmetric with t8 */
++      spec->t10 = 500 * 10;
+       /* This one is special and actually in units of 100ms, but zero
+        * based in the hw (so we need to add 100 ms). But the sw vbt
+        * table multiplies it with 1000 to make it in units of 100usec,
+        * too. */
+-      spec.t11_t12 = (510 + 100) * 10;
++      spec->t11_t12 = (510 + 100) * 10;
++
++      intel_pps_dump_state(intel_dp, "spec", spec);
++}
++
++static void pps_init_delays(struct intel_dp *intel_dp)
++{
++      struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
++      struct edp_power_seq cur, vbt, spec,
++              *final = &intel_dp->pps.pps_delays;
++
++      lockdep_assert_held(&dev_priv->pps_mutex);
++
++      /* already initialized? */
++      if (final->t11_t12 != 0)
++              return;
+-      intel_pps_dump_state(intel_dp, "vbt", &vbt);
++      pps_init_delays_cur(intel_dp, &cur);
++      pps_init_delays_vbt(intel_dp, &vbt);
++      pps_init_delays_spec(intel_dp, &spec);
+       /* Use the max of the register settings and vbt. If both are
+        * unset, fall back to the spec limits. */
+-- 
+2.35.1
+
diff --git a/queue-5.19/net-mvpp2-debugfs-fix-memory-leak-when-using-debugfs.patch b/queue-5.19/net-mvpp2-debugfs-fix-memory-leak-when-using-debugfs.patch
new file mode 100644 (file)
index 0000000..01faa0e
--- /dev/null
@@ -0,0 +1,51 @@
+From 8e794602201b071b24a348dce799c504c202a5cc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 2 Sep 2022 15:41:11 +0200
+Subject: net: mvpp2: debugfs: fix memory leak when using debugfs_lookup()
+
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+[ Upstream commit fe2c9c61f668cde28dac2b188028c5299cedcc1e ]
+
+When calling debugfs_lookup() the result must have dput() called on it,
+otherwise the memory will leak over time.  Fix this up to be much
+simpler logic and only create the root debugfs directory once when the
+driver is first accessed.  That resolves the memory leak and makes
+things more obvious as to what the intent is.
+
+Cc: Marcin Wojtas <mw@semihalf.com>
+Cc: Russell King <linux@armlinux.org.uk>
+Cc: "David S. Miller" <davem@davemloft.net>
+Cc: Eric Dumazet <edumazet@google.com>
+Cc: Jakub Kicinski <kuba@kernel.org>
+Cc: Paolo Abeni <pabeni@redhat.com>
+Cc: netdev@vger.kernel.org
+Cc: stable <stable@kernel.org>
+Fixes: 21da57a23125 ("net: mvpp2: add a debugfs interface for the Header Parser")
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c
+index 4a3baa7e0142..0eec05d905eb 100644
+--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c
++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c
+@@ -700,10 +700,10 @@ void mvpp2_dbgfs_cleanup(struct mvpp2 *priv)
+ void mvpp2_dbgfs_init(struct mvpp2 *priv, const char *name)
+ {
+-      struct dentry *mvpp2_dir, *mvpp2_root;
++      static struct dentry *mvpp2_root;
++      struct dentry *mvpp2_dir;
+       int ret, i;
+-      mvpp2_root = debugfs_lookup(MVPP2_DRIVER_NAME, NULL);
+       if (!mvpp2_root)
+               mvpp2_root = debugfs_create_dir(MVPP2_DRIVER_NAME, NULL);
+-- 
+2.35.1
+
diff --git a/queue-5.19/revert-usb-add-quirks-for-lenovo-onelink-dock.patch b/queue-5.19/revert-usb-add-quirks-for-lenovo-onelink-dock.patch
new file mode 100644 (file)
index 0000000..747efd8
--- /dev/null
@@ -0,0 +1,43 @@
+From b8fc4a65f65b51bce40d2f1968f65155f1ff759c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 31 Aug 2022 10:34:25 +0200
+Subject: Revert "usb: add quirks for Lenovo OneLink+ Dock"
+
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+[ Upstream commit 58bfe7d8e31014d7ce246788df99c56e3cfe6c68 ]
+
+This reverts commit 3d5f70949f1b1168fbb17d06eb5c57e984c56c58.
+
+The quirk does not work properly, more work is needed to determine what
+should be done here.
+
+Reported-by: Oliver Neukum <oneukum@suse.com>
+Cc: Jean-Francois Le Fillatre <jflf_kernel@gmx.com>
+Cc: stable <stable@kernel.org>
+Fixes: 3d5f70949f1b ("usb: add quirks for Lenovo OneLink+ Dock")
+Link: https://lore.kernel.org/r/9a17ea86-079f-510d-e919-01bc53a6d09f@gmx.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/core/quirks.c | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
+index 999b7c9697fc..f99a65a64588 100644
+--- a/drivers/usb/core/quirks.c
++++ b/drivers/usb/core/quirks.c
+@@ -437,10 +437,6 @@ static const struct usb_device_id usb_quirk_list[] = {
+       { USB_DEVICE(0x1532, 0x0116), .driver_info =
+                       USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
+-      /* Lenovo ThinkPad OneLink+ Dock twin hub controllers (VIA Labs VL812) */
+-      { USB_DEVICE(0x17ef, 0x1018), .driver_info = USB_QUIRK_RESET_RESUME },
+-      { USB_DEVICE(0x17ef, 0x1019), .driver_info = USB_QUIRK_RESET_RESUME },
+-
+       /* Lenovo USB-C to Ethernet Adapter RTL8153-04 */
+       { USB_DEVICE(0x17ef, 0x720c), .driver_info = USB_QUIRK_NO_LPM },
+-- 
+2.35.1
+
diff --git a/queue-5.19/revert-usb-gadget-udc-xilinx-replace-memcpy-with-mem.patch b/queue-5.19/revert-usb-gadget-udc-xilinx-replace-memcpy-with-mem.patch
new file mode 100644 (file)
index 0000000..eaec4f5
--- /dev/null
@@ -0,0 +1,96 @@
+From 2fa098146195e3a8a32a5837e1329d606c7eed6c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 2 Sep 2022 09:10:08 +0200
+Subject: Revert "usb: gadget: udc-xilinx: replace memcpy with memcpy_toio"
+
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+[ Upstream commit fe0a2ac7c627b064c479ad0c3b25e531d342e048 ]
+
+This reverts commit 8cb339f1c1f04baede9d54c1e40ac96247a6393b as it
+throws up a bunch of sparse warnings as reported by the kernel test
+robot.
+
+Reported-by: kernel test robot <lkp@intel.com>
+Link: https://lore.kernel.org/r/202209020044.CX2PfZzM-lkp@intel.com
+Fixes: 8cb339f1c1f0 ("usb: gadget: udc-xilinx: replace memcpy with memcpy_toio")
+Cc: stable@vger.kernel.org
+Cc: Linus Walleij <linus.walleij@linaro.org>
+Cc: Piyush Mehta <piyush.mehta@amd.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/gadget/udc/udc-xilinx.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
+index 054b69dc2f0c..4827e3cd3834 100644
+--- a/drivers/usb/gadget/udc/udc-xilinx.c
++++ b/drivers/usb/gadget/udc/udc-xilinx.c
+@@ -499,11 +499,11 @@ static int xudc_eptxrx(struct xusb_ep *ep, struct xusb_req *req,
+               /* Get the Buffer address and copy the transmit data.*/
+               eprambase = (u32 __force *)(udc->addr + ep->rambase);
+               if (ep->is_in) {
+-                      memcpy_toio(eprambase, bufferptr, bytestosend);
++                      memcpy(eprambase, bufferptr, bytestosend);
+                       udc->write_fn(udc->addr, ep->offset +
+                                     XUSB_EP_BUF0COUNT_OFFSET, bufferlen);
+               } else {
+-                      memcpy_toio(bufferptr, eprambase, bytestosend);
++                      memcpy(bufferptr, eprambase, bytestosend);
+               }
+               /*
+                * Enable the buffer for transmission.
+@@ -517,11 +517,11 @@ static int xudc_eptxrx(struct xusb_ep *ep, struct xusb_req *req,
+               eprambase = (u32 __force *)(udc->addr + ep->rambase +
+                            ep->ep_usb.maxpacket);
+               if (ep->is_in) {
+-                      memcpy_toio(eprambase, bufferptr, bytestosend);
++                      memcpy(eprambase, bufferptr, bytestosend);
+                       udc->write_fn(udc->addr, ep->offset +
+                                     XUSB_EP_BUF1COUNT_OFFSET, bufferlen);
+               } else {
+-                      memcpy_toio(bufferptr, eprambase, bytestosend);
++                      memcpy(bufferptr, eprambase, bytestosend);
+               }
+               /*
+                * Enable the buffer for transmission.
+@@ -1023,7 +1023,7 @@ static int __xudc_ep0_queue(struct xusb_ep *ep0, struct xusb_req *req)
+                          udc->addr);
+               length = req->usb_req.actual = min_t(u32, length,
+                                                    EP0_MAX_PACKET);
+-              memcpy_toio(corebuf, req->usb_req.buf, length);
++              memcpy(corebuf, req->usb_req.buf, length);
+               udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, length);
+               udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+       } else {
+@@ -1752,7 +1752,7 @@ static void xudc_handle_setup(struct xusb_udc *udc)
+       /* Load up the chapter 9 command buffer.*/
+       ep0rambase = (u32 __force *) (udc->addr + XUSB_SETUP_PKT_ADDR_OFFSET);
+-      memcpy_toio(&setup, ep0rambase, 8);
++      memcpy(&setup, ep0rambase, 8);
+       udc->setup = setup;
+       udc->setup.wValue = cpu_to_le16(setup.wValue);
+@@ -1839,7 +1839,7 @@ static void xudc_ep0_out(struct xusb_udc *udc)
+                            (ep0->rambase << 2));
+               buffer = req->usb_req.buf + req->usb_req.actual;
+               req->usb_req.actual = req->usb_req.actual + bytes_to_rx;
+-              memcpy_toio(buffer, ep0rambase, bytes_to_rx);
++              memcpy(buffer, ep0rambase, bytes_to_rx);
+               if (req->usb_req.length == req->usb_req.actual) {
+                       /* Data transfer completed get ready for Status stage */
+@@ -1915,7 +1915,7 @@ static void xudc_ep0_in(struct xusb_udc *udc)
+                                    (ep0->rambase << 2));
+                       buffer = req->usb_req.buf + req->usb_req.actual;
+                       req->usb_req.actual = req->usb_req.actual + length;
+-                      memcpy_toio(ep0rambase, buffer, length);
++                      memcpy(ep0rambase, buffer, length);
+               }
+               udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, count);
+               udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+-- 
+2.35.1
+
diff --git a/queue-5.19/scsi-core-fix-a-use-after-free.patch b/queue-5.19/scsi-core-fix-a-use-after-free.patch
new file mode 100644 (file)
index 0000000..fdabbb6
--- /dev/null
@@ -0,0 +1,201 @@
+From 1ca31cce4a18e6c68ea9817aa6b11e90abd7b180 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 25 Aug 2022 17:26:34 -0700
+Subject: scsi: core: Fix a use-after-free
+
+From: Bart Van Assche <bvanassche@acm.org>
+
+[ Upstream commit 8fe4ce5836e932f5766317cb651c1ff2a4cd0506 ]
+
+There are two .exit_cmd_priv implementations. Both implementations use
+resources associated with the SCSI host. Make sure that these resources are
+still available when .exit_cmd_priv is called by waiting inside
+scsi_remove_host() until the tag set has been freed.
+
+This commit fixes the following use-after-free:
+
+==================================================================
+BUG: KASAN: use-after-free in srp_exit_cmd_priv+0x27/0xd0 [ib_srp]
+Read of size 8 at addr ffff888100337000 by task multipathd/16727
+Call Trace:
+ <TASK>
+ dump_stack_lvl+0x34/0x44
+ print_report.cold+0x5e/0x5db
+ kasan_report+0xab/0x120
+ srp_exit_cmd_priv+0x27/0xd0 [ib_srp]
+ scsi_mq_exit_request+0x4d/0x70
+ blk_mq_free_rqs+0x143/0x410
+ __blk_mq_free_map_and_rqs+0x6e/0x100
+ blk_mq_free_tag_set+0x2b/0x160
+ scsi_host_dev_release+0xf3/0x1a0
+ device_release+0x54/0xe0
+ kobject_put+0xa5/0x120
+ device_release+0x54/0xe0
+ kobject_put+0xa5/0x120
+ scsi_device_dev_release_usercontext+0x4c1/0x4e0
+ execute_in_process_context+0x23/0x90
+ device_release+0x54/0xe0
+ kobject_put+0xa5/0x120
+ scsi_disk_release+0x3f/0x50
+ device_release+0x54/0xe0
+ kobject_put+0xa5/0x120
+ disk_release+0x17f/0x1b0
+ device_release+0x54/0xe0
+ kobject_put+0xa5/0x120
+ dm_put_table_device+0xa3/0x160 [dm_mod]
+ dm_put_device+0xd0/0x140 [dm_mod]
+ free_priority_group+0xd8/0x110 [dm_multipath]
+ free_multipath+0x94/0xe0 [dm_multipath]
+ dm_table_destroy+0xa2/0x1e0 [dm_mod]
+ __dm_destroy+0x196/0x350 [dm_mod]
+ dev_remove+0x10c/0x160 [dm_mod]
+ ctl_ioctl+0x2c2/0x590 [dm_mod]
+ dm_ctl_ioctl+0x5/0x10 [dm_mod]
+ __x64_sys_ioctl+0xb4/0xf0
+ dm_ctl_ioctl+0x5/0x10 [dm_mod]
+ __x64_sys_ioctl+0xb4/0xf0
+ do_syscall_64+0x3b/0x90
+ entry_SYSCALL_64_after_hwframe+0x46/0xb0
+
+Link: https://lore.kernel.org/r/20220826002635.919423-1-bvanassche@acm.org
+Fixes: 65ca846a5314 ("scsi: core: Introduce {init,exit}_cmd_priv()")
+Cc: Ming Lei <ming.lei@redhat.com>
+Cc: Christoph Hellwig <hch@lst.de>
+Cc: Mike Christie <michael.christie@oracle.com>
+Cc: Hannes Reinecke <hare@suse.de>
+Cc: John Garry <john.garry@huawei.com>
+Cc: Li Zhijian <lizhijian@fujitsu.com>
+Reported-by: Li Zhijian <lizhijian@fujitsu.com>
+Tested-by: Li Zhijian <lizhijian@fujitsu.com>
+Signed-off-by: Bart Van Assche <bvanassche@acm.org>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/hosts.c      | 16 +++++++++++++---
+ drivers/scsi/scsi_lib.c   |  6 +++++-
+ drivers/scsi/scsi_priv.h  |  2 +-
+ drivers/scsi/scsi_scan.c  |  1 +
+ drivers/scsi/scsi_sysfs.c |  1 +
+ include/scsi/scsi_host.h  |  2 ++
+ 6 files changed, 23 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
+index 8352f90d997d..ae9a107c520d 100644
+--- a/drivers/scsi/hosts.c
++++ b/drivers/scsi/hosts.c
+@@ -182,6 +182,15 @@ void scsi_remove_host(struct Scsi_Host *shost)
+       mutex_unlock(&shost->scan_mutex);
+       scsi_proc_host_rm(shost);
++      /*
++       * New SCSI devices cannot be attached anymore because of the SCSI host
++       * state so drop the tag set refcnt. Wait until the tag set refcnt drops
++       * to zero because .exit_cmd_priv implementations may need the host
++       * pointer.
++       */
++      kref_put(&shost->tagset_refcnt, scsi_mq_free_tags);
++      wait_for_completion(&shost->tagset_freed);
++
+       spin_lock_irqsave(shost->host_lock, flags);
+       if (scsi_host_set_state(shost, SHOST_DEL))
+               BUG_ON(scsi_host_set_state(shost, SHOST_DEL_RECOVERY));
+@@ -240,6 +249,9 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
+       if (error)
+               goto fail;
++      kref_init(&shost->tagset_refcnt);
++      init_completion(&shost->tagset_freed);
++
+       /*
+        * Increase usage count temporarily here so that calling
+        * scsi_autopm_put_host() will trigger runtime idle if there is
+@@ -312,6 +324,7 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
+       pm_runtime_disable(&shost->shost_gendev);
+       pm_runtime_set_suspended(&shost->shost_gendev);
+       pm_runtime_put_noidle(&shost->shost_gendev);
++      kref_put(&shost->tagset_refcnt, scsi_mq_free_tags);
+  fail:
+       return error;
+ }
+@@ -345,9 +358,6 @@ static void scsi_host_dev_release(struct device *dev)
+               kfree(dev_name(&shost->shost_dev));
+       }
+-      if (shost->tag_set.tags)
+-              scsi_mq_destroy_tags(shost);
+-
+       kfree(shost->shost_data);
+       ida_simple_remove(&host_index_ida, shost->host_no);
+diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
+index 0a267d6e2f7c..7e990f7a9f16 100644
+--- a/drivers/scsi/scsi_lib.c
++++ b/drivers/scsi/scsi_lib.c
+@@ -1995,9 +1995,13 @@ int scsi_mq_setup_tags(struct Scsi_Host *shost)
+       return blk_mq_alloc_tag_set(tag_set);
+ }
+-void scsi_mq_destroy_tags(struct Scsi_Host *shost)
++void scsi_mq_free_tags(struct kref *kref)
+ {
++      struct Scsi_Host *shost = container_of(kref, typeof(*shost),
++                                             tagset_refcnt);
++
+       blk_mq_free_tag_set(&shost->tag_set);
++      complete(&shost->tagset_freed);
+ }
+ /**
+diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
+index 5c4786310a31..a0ee31d55f5f 100644
+--- a/drivers/scsi/scsi_priv.h
++++ b/drivers/scsi/scsi_priv.h
+@@ -94,7 +94,7 @@ extern void scsi_run_host_queues(struct Scsi_Host *shost);
+ extern void scsi_requeue_run_queue(struct work_struct *work);
+ extern void scsi_start_queue(struct scsi_device *sdev);
+ extern int scsi_mq_setup_tags(struct Scsi_Host *shost);
+-extern void scsi_mq_destroy_tags(struct Scsi_Host *shost);
++extern void scsi_mq_free_tags(struct kref *kref);
+ extern void scsi_exit_queue(void);
+ extern void scsi_evt_thread(struct work_struct *work);
+diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
+index 91ac901a6682..5d27f5196de6 100644
+--- a/drivers/scsi/scsi_scan.c
++++ b/drivers/scsi/scsi_scan.c
+@@ -340,6 +340,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
+               kfree(sdev);
+               goto out;
+       }
++      kref_get(&sdev->host->tagset_refcnt);
+       sdev->request_queue = q;
+       q->queuedata = sdev;
+       __scsi_init_queue(sdev->host, q);
+diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
+index aa70d9282161..5d61f58399dc 100644
+--- a/drivers/scsi/scsi_sysfs.c
++++ b/drivers/scsi/scsi_sysfs.c
+@@ -1476,6 +1476,7 @@ void __scsi_remove_device(struct scsi_device *sdev)
+       mutex_unlock(&sdev->state_mutex);
+       blk_mq_destroy_queue(sdev->request_queue);
++      kref_put(&sdev->host->tagset_refcnt, scsi_mq_free_tags);
+       cancel_work_sync(&sdev->requeue_work);
+       if (sdev->host->hostt->slave_destroy)
+diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
+index 667d889b92b5..3e1cea155049 100644
+--- a/include/scsi/scsi_host.h
++++ b/include/scsi/scsi_host.h
+@@ -557,6 +557,8 @@ struct Scsi_Host {
+       struct scsi_host_template *hostt;
+       struct scsi_transport_template *transportt;
++      struct kref             tagset_refcnt;
++      struct completion       tagset_freed;
+       /* Area to keep a shared tag map */
+       struct blk_mq_tag_set   tag_set;
+-- 
+2.35.1
+
diff --git a/queue-5.19/series b/queue-5.19/series
new file mode 100644 (file)
index 0000000..492077e
--- /dev/null
@@ -0,0 +1,27 @@
+usb-dwc3-qcom-add-helper-functions-to-enable-disable.patch
+usb-dwc3-qcom-configure-wakeup-interrupts-during-sus.patch
+usb-dwc3-qcom-fix-use-after-free-on-runtime-pm-wakeu.patch
+drm-i915-extract-intel_edp_fixup_vbt_bpp.patch
+drm-i915-pps-split-pps_init_delays-into-distinct-par.patch
+drm-i915-bios-split-parse_driver_features-into-two-p.patch
+drm-i915-bios-split-vbt-parsing-to-global-vs.-panel-.patch
+drm-i915-bios-split-vbt-data-into-per-panel-vs.-glob.patch
+drm-i915-dsi-filter-invalid-backlight-and-cabc-ports.patch
+drm-i915-dsi-fix-dual-link-dsi-backlight-and-cabc-po.patch
+smb3-move-the-flush-out-of-smb2_copychunk_range-into.patch
+smb3-fix-temporary-data-corruption-in-collapse-range.patch
+smb3-fix-temporary-data-corruption-in-insert-range.patch
+usb-add-quirks-for-lenovo-onelink-dock.patch
+usb-gadget-udc-xilinx-replace-memcpy-with-memcpy_toi.patch
+smb3-use-filemap_write_and_wait_range-instead-of-fil.patch
+revert-usb-add-quirks-for-lenovo-onelink-dock.patch
+revert-usb-gadget-udc-xilinx-replace-memcpy-with-mem.patch
+net-mvpp2-debugfs-fix-memory-leak-when-using-debugfs.patch
+xfrm-fix-xfrma_lastused-comment.patch
+usb-dwc3-qcom-fix-gadget-only-builds.patch
+usb-dwc3-qcom-fix-runtime-pm-wakeup.patch
+usb-dwc3-qcom-fix-peripheral-and-otg-suspend.patch
+block-remove-queue_flag_dead.patch
+block-stop-setting-the-nomerges-flags-in-blk_cleanup.patch
+block-simplify-disk-shutdown.patch
+scsi-core-fix-a-use-after-free.patch
diff --git a/queue-5.19/smb3-fix-temporary-data-corruption-in-collapse-range.patch b/queue-5.19/smb3-fix-temporary-data-corruption-in-collapse-range.patch
new file mode 100644 (file)
index 0000000..2ed431a
--- /dev/null
@@ -0,0 +1,94 @@
+From 1228cadaeb30d892724a74fc186816df199a458f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 23 Aug 2022 14:07:41 +0100
+Subject: smb3: fix temporary data corruption in collapse range
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit fa30a81f255a56cccd89552cd6ce7ea6e8d8acc4 ]
+
+collapse range doesn't discard the affected cached region
+so can risk temporarily corrupting the file data. This
+fixes xfstest generic/031
+
+I also decided to merge a minor cleanup to this into the same patch
+(avoiding rereading inode size repeatedly unnecessarily) to make it
+clearer.
+
+Cc: stable@vger.kernel.org
+Fixes: 5476b5dd82c8b ("cifs: add support for FALLOC_FL_COLLAPSE_RANGE")
+Reported-by: David Howells <dhowells@redhat.com>
+Tested-by: David Howells <dhowells@redhat.com>
+Reviewed-by: David Howells <dhowells@redhat.com>
+cc: Ronnie Sahlberg <lsahlber@redhat.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/cifs/smb2ops.c | 26 ++++++++++++++++----------
+ 1 file changed, 16 insertions(+), 10 deletions(-)
+
+diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
+index ef8cb7fbabeb..8e55495c4f7e 100644
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -3952,41 +3952,47 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
+ {
+       int rc;
+       unsigned int xid;
+-      struct inode *inode;
++      struct inode *inode = file_inode(file);
+       struct cifsFileInfo *cfile = file->private_data;
+-      struct cifsInodeInfo *cifsi;
++      struct cifsInodeInfo *cifsi = CIFS_I(inode);
+       __le64 eof;
++      loff_t old_eof;
+       xid = get_xid();
+-      inode = d_inode(cfile->dentry);
+-      cifsi = CIFS_I(inode);
++      inode_lock(inode);
+-      if (off >= i_size_read(inode) ||
+-          off + len >= i_size_read(inode)) {
++      old_eof = i_size_read(inode);
++      if ((off >= old_eof) ||
++          off + len >= old_eof) {
+               rc = -EINVAL;
+               goto out;
+       }
++      filemap_invalidate_lock(inode->i_mapping);
+       filemap_write_and_wait(inode->i_mapping);
++      truncate_pagecache_range(inode, off, old_eof);
+       rc = smb2_copychunk_range(xid, cfile, cfile, off + len,
+-                                i_size_read(inode) - off - len, off);
++                                old_eof - off - len, off);
+       if (rc < 0)
+-              goto out;
++              goto out_2;
+-      eof = cpu_to_le64(i_size_read(inode) - len);
++      eof = cpu_to_le64(old_eof - len);
+       rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
+                         cfile->fid.volatile_fid, cfile->pid, &eof);
+       if (rc < 0)
+-              goto out;
++              goto out_2;
+       rc = 0;
+       cifsi->server_eof = i_size_read(inode) - len;
+       truncate_setsize(inode, cifsi->server_eof);
+       fscache_resize_cookie(cifs_inode_cookie(inode), cifsi->server_eof);
++out_2:
++      filemap_invalidate_unlock(inode->i_mapping);
+  out:
++      inode_unlock(inode);
+       free_xid(xid);
+       return rc;
+ }
+-- 
+2.35.1
+
diff --git a/queue-5.19/smb3-fix-temporary-data-corruption-in-insert-range.patch b/queue-5.19/smb3-fix-temporary-data-corruption-in-insert-range.patch
new file mode 100644 (file)
index 0000000..e997c33
--- /dev/null
@@ -0,0 +1,84 @@
+From 8b64323e4828fd9b330d892005051e1d4f36dd0f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 23 Aug 2022 14:07:55 +0100
+Subject: smb3: fix temporary data corruption in insert range
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit 9c8b7a293f50253e694f19161c045817a938e551 ]
+
+insert range doesn't discard the affected cached region
+so can risk temporarily corrupting file data.
+
+Also includes some minor cleanup (avoiding rereading
+inode size repeatedly unnecessarily) to make it clearer.
+
+Cc: stable@vger.kernel.org
+Fixes: 7fe6fe95b936 ("cifs: add FALLOC_FL_INSERT_RANGE support")
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: Ronnie Sahlberg <lsahlber@redhat.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/cifs/smb2ops.c | 24 ++++++++++++++++--------
+ 1 file changed, 16 insertions(+), 8 deletions(-)
+
+diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
+index 8e55495c4f7e..774c6e5f6584 100644
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -4005,35 +4005,43 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
+       struct cifsFileInfo *cfile = file->private_data;
+       struct inode *inode = file_inode(file);
+       __le64 eof;
+-      __u64  count;
++      __u64  count, old_eof;
+       xid = get_xid();
+-      if (off >= i_size_read(inode)) {
++      inode_lock(inode);
++
++      old_eof = i_size_read(inode);
++      if (off >= old_eof) {
+               rc = -EINVAL;
+               goto out;
+       }
+-      count = i_size_read(inode) - off;
+-      eof = cpu_to_le64(i_size_read(inode) + len);
++      count = old_eof - off;
++      eof = cpu_to_le64(old_eof + len);
++      filemap_invalidate_lock(inode->i_mapping);
+       filemap_write_and_wait(inode->i_mapping);
++      truncate_pagecache_range(inode, off, old_eof);
+       rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
+                         cfile->fid.volatile_fid, cfile->pid, &eof);
+       if (rc < 0)
+-              goto out;
++              goto out_2;
+       rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len);
+       if (rc < 0)
+-              goto out;
++              goto out_2;
+-      rc = smb3_zero_range(file, tcon, off, len, 1);
++      rc = smb3_zero_data(file, tcon, off, len, xid);
+       if (rc < 0)
+-              goto out;
++              goto out_2;
+       rc = 0;
++out_2:
++      filemap_invalidate_unlock(inode->i_mapping);
+  out:
++      inode_unlock(inode);
+       free_xid(xid);
+       return rc;
+ }
+-- 
+2.35.1
+
diff --git a/queue-5.19/smb3-move-the-flush-out-of-smb2_copychunk_range-into.patch b/queue-5.19/smb3-move-the-flush-out-of-smb2_copychunk_range-into.patch
new file mode 100644 (file)
index 0000000..ddf1d15
--- /dev/null
@@ -0,0 +1,95 @@
+From dd21d6b9e45779fe64c07f4fa39a56502f2dc4eb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 23 Aug 2022 14:07:28 +0100
+Subject: smb3: Move the flush out of smb2_copychunk_range() into its callers
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit c3a72bb213209adfe981a4a92ea5746a778323e4 ]
+
+Move the flush out of smb2_copychunk_range() into its callers.  This will
+allow the pagecache to be invalidated between the flush and the operation
+in smb3_collapse_range() and smb3_insert_range().
+
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: Ronnie Sahlberg <lsahlber@redhat.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: fa30a81f255a ("smb3: fix temporary data corruption in collapse range")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/cifs/cifsfs.c  |  2 ++
+ fs/cifs/smb2ops.c | 20 ++++++++------------
+ 2 files changed, 10 insertions(+), 12 deletions(-)
+
+diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
+index 8f2e003e0590..2b51f0cbf4d2 100644
+--- a/fs/cifs/cifsfs.c
++++ b/fs/cifs/cifsfs.c
+@@ -1203,6 +1203,8 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
+       cifs_dbg(FYI, "copychunk range\n");
++      filemap_write_and_wait(src_inode->i_mapping);
++
+       if (!src_file->private_data || !dst_file->private_data) {
+               rc = -EBADF;
+               cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
+diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
+index e8a8daa82ed7..ef8cb7fbabeb 100644
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -1886,17 +1886,8 @@ smb2_copychunk_range(const unsigned int xid,
+       int chunks_copied = 0;
+       bool chunk_sizes_updated = false;
+       ssize_t bytes_written, total_bytes_written = 0;
+-      struct inode *inode;
+       pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL);
+-
+-      /*
+-       * We need to flush all unwritten data before we can send the
+-       * copychunk ioctl to the server.
+-       */
+-      inode = d_inode(trgtfile->dentry);
+-      filemap_write_and_wait(inode->i_mapping);
+-
+       if (pcchunk == NULL)
+               return -ENOMEM;
+@@ -3977,6 +3968,8 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
+               goto out;
+       }
++      filemap_write_and_wait(inode->i_mapping);
++
+       rc = smb2_copychunk_range(xid, cfile, cfile, off + len,
+                                 i_size_read(inode) - off - len, off);
+       if (rc < 0)
+@@ -4004,18 +3997,21 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
+       int rc;
+       unsigned int xid;
+       struct cifsFileInfo *cfile = file->private_data;
++      struct inode *inode = file_inode(file);
+       __le64 eof;
+       __u64  count;
+       xid = get_xid();
+-      if (off >= i_size_read(file->f_inode)) {
++      if (off >= i_size_read(inode)) {
+               rc = -EINVAL;
+               goto out;
+       }
+-      count = i_size_read(file->f_inode) - off;
+-      eof = cpu_to_le64(i_size_read(file->f_inode) + len);
++      count = i_size_read(inode) - off;
++      eof = cpu_to_le64(i_size_read(inode) + len);
++
++      filemap_write_and_wait(inode->i_mapping);
+       rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
+                         cfile->fid.volatile_fid, cfile->pid, &eof);
+-- 
+2.35.1
+
diff --git a/queue-5.19/smb3-use-filemap_write_and_wait_range-instead-of-fil.patch b/queue-5.19/smb3-use-filemap_write_and_wait_range-instead-of-fil.patch
new file mode 100644 (file)
index 0000000..bc83594
--- /dev/null
@@ -0,0 +1,81 @@
+From 4342751c15e194569c55acfe466e0d57b8ca9e01 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 29 Aug 2022 11:53:41 -0500
+Subject: smb3: use filemap_write_and_wait_range instead of
+ filemap_write_and_wait
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 3e3761f1ec7df67d88cfca5e2ea98538f529e645 ]
+
+When doing insert range and collapse range we should be
+writing out the cached pages for the ranges affected but not
+the whole file.
+
+Fixes: c3a72bb21320 ("smb3: Move the flush out of smb2_copychunk_range() into its callers")
+Cc: stable@vger.kernel.org
+Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
+Reviewed-by: David Howells <dhowells@redhat.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/cifs/cifsfs.c  | 8 ++++++--
+ fs/cifs/smb2ops.c | 9 +++++++--
+ 2 files changed, 13 insertions(+), 4 deletions(-)
+
+diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
+index 2b51f0cbf4d2..97278c43f8dc 100644
+--- a/fs/cifs/cifsfs.c
++++ b/fs/cifs/cifsfs.c
+@@ -1203,8 +1203,6 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
+       cifs_dbg(FYI, "copychunk range\n");
+-      filemap_write_and_wait(src_inode->i_mapping);
+-
+       if (!src_file->private_data || !dst_file->private_data) {
+               rc = -EBADF;
+               cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
+@@ -1234,6 +1232,12 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
+       lock_two_nondirectories(target_inode, src_inode);
+       cifs_dbg(FYI, "about to flush pages\n");
++
++      rc = filemap_write_and_wait_range(src_inode->i_mapping, off,
++                                        off + len - 1);
++      if (rc)
++              goto out;
++
+       /* should we flush first and last page first */
+       truncate_inode_pages(&target_inode->i_data, 0);
+diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
+index 774c6e5f6584..cc180d37b8ce 100644
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -3970,7 +3970,10 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
+       }
+       filemap_invalidate_lock(inode->i_mapping);
+-      filemap_write_and_wait(inode->i_mapping);
++      rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof - 1);
++      if (rc < 0)
++              goto out_2;
++
+       truncate_pagecache_range(inode, off, old_eof);
+       rc = smb2_copychunk_range(xid, cfile, cfile, off + len,
+@@ -4021,7 +4024,9 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
+       eof = cpu_to_le64(old_eof + len);
+       filemap_invalidate_lock(inode->i_mapping);
+-      filemap_write_and_wait(inode->i_mapping);
++      rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof + len - 1);
++      if (rc < 0)
++              goto out_2;
+       truncate_pagecache_range(inode, off, old_eof);
+       rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
+-- 
+2.35.1
+
diff --git a/queue-5.19/usb-add-quirks-for-lenovo-onelink-dock.patch b/queue-5.19/usb-add-quirks-for-lenovo-onelink-dock.patch
new file mode 100644 (file)
index 0000000..5712130
--- /dev/null
@@ -0,0 +1,51 @@
+From 710e110297312ffc42ea62d68331214ac71a7d2c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 24 Aug 2022 21:13:21 +0200
+Subject: usb: add quirks for Lenovo OneLink+ Dock
+
+From: Jean-Francois Le Fillatre <jflf_kernel@gmx.com>
+
+[ Upstream commit 3d5f70949f1b1168fbb17d06eb5c57e984c56c58 ]
+
+The Lenovo OneLink+ Dock contains two VL812 USB3.0 controllers:
+17ef:1018 upstream
+17ef:1019 downstream
+
+Those two controllers both have problems with some USB3.0 devices,
+particularly self-powered ones. Typical error messages include:
+
+  Timeout while waiting for setup device command
+  device not accepting address X, error -62
+  unable to enumerate USB device
+
+By process of elimination the controllers themselves were identified as
+the cause of the problem. Through trial and error the issue was solved
+by using USB_QUIRK_RESET_RESUME for both chips.
+
+Signed-off-by: Jean-Francois Le Fillatre <jflf_kernel@gmx.com>
+Cc: stable <stable@kernel.org>
+Link: https://lore.kernel.org/r/20220824191320.17883-1-jflf_kernel@gmx.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/core/quirks.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
+index f99a65a64588..999b7c9697fc 100644
+--- a/drivers/usb/core/quirks.c
++++ b/drivers/usb/core/quirks.c
+@@ -437,6 +437,10 @@ static const struct usb_device_id usb_quirk_list[] = {
+       { USB_DEVICE(0x1532, 0x0116), .driver_info =
+                       USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
++      /* Lenovo ThinkPad OneLink+ Dock twin hub controllers (VIA Labs VL812) */
++      { USB_DEVICE(0x17ef, 0x1018), .driver_info = USB_QUIRK_RESET_RESUME },
++      { USB_DEVICE(0x17ef, 0x1019), .driver_info = USB_QUIRK_RESET_RESUME },
++
+       /* Lenovo USB-C to Ethernet Adapter RTL8153-04 */
+       { USB_DEVICE(0x17ef, 0x720c), .driver_info = USB_QUIRK_NO_LPM },
+-- 
+2.35.1
+
diff --git a/queue-5.19/usb-dwc3-qcom-add-helper-functions-to-enable-disable.patch b/queue-5.19/usb-dwc3-qcom-add-helper-functions-to-enable-disable.patch
new file mode 100644 (file)
index 0000000..a9d61e7
--- /dev/null
@@ -0,0 +1,108 @@
+From 78f61759ac383ddf34cd93009ea6eae1a75c234f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jun 2022 10:00:52 +0530
+Subject: usb: dwc3: qcom: Add helper functions to enable,disable wake irqs
+
+From: Sandeep Maheswaram <quic_c_sanm@quicinc.com>
+
+[ Upstream commit 360e8230516de94d74d30c64f0cdcf228b8e8b67 ]
+
+Adding helper functions to enable,disable wake irqs to make
+the code simple and readable.
+
+Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
+Reviewed-by: Pavankumar Kondeti <quic_pkondeti@quicinc.com>
+Signed-off-by: Sandeep Maheswaram <quic_c_sanm@quicinc.com>
+Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com>
+Link: https://lore.kernel.org/r/1655094654-24052-4-git-send-email-quic_kriskura@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: a872ab303d5d ("usb: dwc3: qcom: fix use-after-free on runtime-PM wakeup")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/dwc3-qcom.c | 58 ++++++++++++++++--------------------
+ 1 file changed, 26 insertions(+), 32 deletions(-)
+
+diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
+index 3582fd6dfa14..27ff18aeea26 100644
+--- a/drivers/usb/dwc3/dwc3-qcom.c
++++ b/drivers/usb/dwc3/dwc3-qcom.c
+@@ -296,50 +296,44 @@ static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom)
+       icc_put(qcom->icc_path_apps);
+ }
++static void dwc3_qcom_enable_wakeup_irq(int irq)
++{
++      if (!irq)
++              return;
++
++      enable_irq(irq);
++      enable_irq_wake(irq);
++}
++
++static void dwc3_qcom_disable_wakeup_irq(int irq)
++{
++      if (!irq)
++              return;
++
++      disable_irq_wake(irq);
++      disable_irq_nosync(irq);
++}
++
+ static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
+ {
+-      if (qcom->hs_phy_irq) {
+-              disable_irq_wake(qcom->hs_phy_irq);
+-              disable_irq_nosync(qcom->hs_phy_irq);
+-      }
++      dwc3_qcom_disable_wakeup_irq(qcom->hs_phy_irq);
+-      if (qcom->dp_hs_phy_irq) {
+-              disable_irq_wake(qcom->dp_hs_phy_irq);
+-              disable_irq_nosync(qcom->dp_hs_phy_irq);
+-      }
++      dwc3_qcom_disable_wakeup_irq(qcom->dp_hs_phy_irq);
+-      if (qcom->dm_hs_phy_irq) {
+-              disable_irq_wake(qcom->dm_hs_phy_irq);
+-              disable_irq_nosync(qcom->dm_hs_phy_irq);
+-      }
++      dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq);
+-      if (qcom->ss_phy_irq) {
+-              disable_irq_wake(qcom->ss_phy_irq);
+-              disable_irq_nosync(qcom->ss_phy_irq);
+-      }
++      dwc3_qcom_disable_wakeup_irq(qcom->ss_phy_irq);
+ }
+ static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
+ {
+-      if (qcom->hs_phy_irq) {
+-              enable_irq(qcom->hs_phy_irq);
+-              enable_irq_wake(qcom->hs_phy_irq);
+-      }
++      dwc3_qcom_enable_wakeup_irq(qcom->hs_phy_irq);
+-      if (qcom->dp_hs_phy_irq) {
+-              enable_irq(qcom->dp_hs_phy_irq);
+-              enable_irq_wake(qcom->dp_hs_phy_irq);
+-      }
++      dwc3_qcom_enable_wakeup_irq(qcom->dp_hs_phy_irq);
+-      if (qcom->dm_hs_phy_irq) {
+-              enable_irq(qcom->dm_hs_phy_irq);
+-              enable_irq_wake(qcom->dm_hs_phy_irq);
+-      }
++      dwc3_qcom_enable_wakeup_irq(qcom->dm_hs_phy_irq);
+-      if (qcom->ss_phy_irq) {
+-              enable_irq(qcom->ss_phy_irq);
+-              enable_irq_wake(qcom->ss_phy_irq);
+-      }
++      dwc3_qcom_enable_wakeup_irq(qcom->ss_phy_irq);
+ }
+ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom)
+-- 
+2.35.1
+
diff --git a/queue-5.19/usb-dwc3-qcom-configure-wakeup-interrupts-during-sus.patch b/queue-5.19/usb-dwc3-qcom-configure-wakeup-interrupts-during-sus.patch
new file mode 100644 (file)
index 0000000..3b7a2e9
--- /dev/null
@@ -0,0 +1,156 @@
+From 920c841d0403e84b90d9027060cdfa180d9dc5d2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jun 2022 10:00:53 +0530
+Subject: usb: dwc3: qcom: Configure wakeup interrupts during suspend
+
+From: Sandeep Maheswaram <quic_c_sanm@quicinc.com>
+
+[ Upstream commit 6895ea55c385c9afdd2aec1eef27ec24917a112f ]
+
+Configure DP/DM line interrupts based on the USB2 device attached to
+the root hub port. When HS/FS device is connected, configure the DP line
+as falling edge to detect both disconnect and remote wakeup scenarios. When
+LS device is connected, configure DM line as falling edge to detect both
+disconnect and remote wakeup. When no device is connected, configure both
+DP and DM lines as rising edge to detect HS/HS/LS device connect scenario.
+
+Reviewed-by: Pavankumar Kondeti <quic_pkondeti@quicinc.com>
+Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
+Signed-off-by: Sandeep Maheswaram <quic_c_sanm@quicinc.com>
+Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com>
+Link: https://lore.kernel.org/r/1655094654-24052-5-git-send-email-quic_kriskura@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: a872ab303d5d ("usb: dwc3: qcom: fix use-after-free on runtime-PM wakeup")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/dwc3-qcom.c | 72 +++++++++++++++++++++++++++++++-----
+ 1 file changed, 62 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
+index 27ff18aeea26..be2e3dd36440 100644
+--- a/drivers/usb/dwc3/dwc3-qcom.c
++++ b/drivers/usb/dwc3/dwc3-qcom.c
+@@ -20,7 +20,8 @@
+ #include <linux/usb/of.h>
+ #include <linux/reset.h>
+ #include <linux/iopoll.h>
+-
++#include <linux/usb/hcd.h>
++#include <linux/usb.h>
+ #include "core.h"
+ /* USB QSCRATCH Hardware registers */
+@@ -76,6 +77,7 @@ struct dwc3_qcom {
+       int                     dp_hs_phy_irq;
+       int                     dm_hs_phy_irq;
+       int                     ss_phy_irq;
++      enum usb_device_speed   usb2_speed;
+       struct extcon_dev       *edev;
+       struct extcon_dev       *host_edev;
+@@ -296,11 +298,34 @@ static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom)
+       icc_put(qcom->icc_path_apps);
+ }
+-static void dwc3_qcom_enable_wakeup_irq(int irq)
++static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom)
++{
++      struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
++      struct usb_hcd *hcd = platform_get_drvdata(dwc->xhci);
++      struct usb_device *udev;
++
++      /*
++       * It is possible to query the speed of all children of
++       * USB2.0 root hub via usb_hub_for_each_child(). DWC3 code
++       * currently supports only 1 port per controller. So
++       * this is sufficient.
++       */
++      udev = usb_hub_find_child(hcd->self.root_hub, 1);
++
++      if (!udev)
++              return USB_SPEED_UNKNOWN;
++
++      return udev->speed;
++}
++
++static void dwc3_qcom_enable_wakeup_irq(int irq, unsigned int polarity)
+ {
+       if (!irq)
+               return;
++      if (polarity)
++              irq_set_irq_type(irq, polarity);
++
+       enable_irq(irq);
+       enable_irq_wake(irq);
+ }
+@@ -318,22 +343,47 @@ static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
+ {
+       dwc3_qcom_disable_wakeup_irq(qcom->hs_phy_irq);
+-      dwc3_qcom_disable_wakeup_irq(qcom->dp_hs_phy_irq);
+-
+-      dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq);
++      if (qcom->usb2_speed == USB_SPEED_LOW) {
++              dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq);
++      } else if ((qcom->usb2_speed == USB_SPEED_HIGH) ||
++                      (qcom->usb2_speed == USB_SPEED_FULL)) {
++              dwc3_qcom_disable_wakeup_irq(qcom->dp_hs_phy_irq);
++      } else {
++              dwc3_qcom_disable_wakeup_irq(qcom->dp_hs_phy_irq);
++              dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq);
++      }
+       dwc3_qcom_disable_wakeup_irq(qcom->ss_phy_irq);
+ }
+ static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
+ {
+-      dwc3_qcom_enable_wakeup_irq(qcom->hs_phy_irq);
++      dwc3_qcom_enable_wakeup_irq(qcom->hs_phy_irq, 0);
+-      dwc3_qcom_enable_wakeup_irq(qcom->dp_hs_phy_irq);
++      /*
++       * Configure DP/DM line interrupts based on the USB2 device attached to
++       * the root hub port. When HS/FS device is connected, configure the DP line
++       * as falling edge to detect both disconnect and remote wakeup scenarios. When
++       * LS device is connected, configure DM line as falling edge to detect both
++       * disconnect and remote wakeup. When no device is connected, configure both
++       * DP and DM lines as rising edge to detect HS/HS/LS device connect scenario.
++       */
+-      dwc3_qcom_enable_wakeup_irq(qcom->dm_hs_phy_irq);
++      if (qcom->usb2_speed == USB_SPEED_LOW) {
++              dwc3_qcom_enable_wakeup_irq(qcom->dm_hs_phy_irq,
++                                              IRQ_TYPE_EDGE_FALLING);
++      } else if ((qcom->usb2_speed == USB_SPEED_HIGH) ||
++                      (qcom->usb2_speed == USB_SPEED_FULL)) {
++              dwc3_qcom_enable_wakeup_irq(qcom->dp_hs_phy_irq,
++                                              IRQ_TYPE_EDGE_FALLING);
++      } else {
++              dwc3_qcom_enable_wakeup_irq(qcom->dp_hs_phy_irq,
++                                              IRQ_TYPE_EDGE_RISING);
++              dwc3_qcom_enable_wakeup_irq(qcom->dm_hs_phy_irq,
++                                              IRQ_TYPE_EDGE_RISING);
++      }
+-      dwc3_qcom_enable_wakeup_irq(qcom->ss_phy_irq);
++      dwc3_qcom_enable_wakeup_irq(qcom->ss_phy_irq, 0);
+ }
+ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom)
+@@ -355,8 +405,10 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom)
+       if (ret)
+               dev_warn(qcom->dev, "failed to disable interconnect: %d\n", ret);
+-      if (device_may_wakeup(qcom->dev))
++      if (device_may_wakeup(qcom->dev)) {
++              qcom->usb2_speed = dwc3_qcom_read_usb2_speed(qcom);
+               dwc3_qcom_enable_interrupts(qcom);
++      }
+       qcom->is_suspended = true;
+-- 
+2.35.1
+
diff --git a/queue-5.19/usb-dwc3-qcom-fix-gadget-only-builds.patch b/queue-5.19/usb-dwc3-qcom-fix-gadget-only-builds.patch
new file mode 100644 (file)
index 0000000..f1ca5cd
--- /dev/null
@@ -0,0 +1,43 @@
+From 0a5869f3029e815c934296d794467722ed4f533d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Aug 2022 17:09:55 +0200
+Subject: usb: dwc3: qcom: fix gadget-only builds
+
+From: Johan Hovold <johan+linaro@kernel.org>
+
+[ Upstream commit c06795f114a6c4a423b11c9d9bbeb77ecbfbaa8b ]
+
+A recent change added a dependency to the USB host stack and broke
+gadget-only builds of the driver.
+
+Fixes: 6895ea55c385 ("usb: dwc3: qcom: Configure wakeup interrupts during suspend")
+Reported-by: Randy Dunlap <rdunlap@infradead.org>
+Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
+Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
+Link: https://lore.kernel.org/r/20220804151001.23612-4-johan+linaro@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/dwc3-qcom.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
+index b0ae73b0b1a1..6884026b9fad 100644
+--- a/drivers/usb/dwc3/dwc3-qcom.c
++++ b/drivers/usb/dwc3/dwc3-qcom.c
+@@ -318,8 +318,11 @@ static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom)
+        * currently supports only 1 port per controller. So
+        * this is sufficient.
+        */
++#ifdef CONFIG_USB
+       udev = usb_hub_find_child(hcd->self.root_hub, 1);
+-
++#else
++      udev = NULL;
++#endif
+       if (!udev)
+               return USB_SPEED_UNKNOWN;
+-- 
+2.35.1
+
diff --git a/queue-5.19/usb-dwc3-qcom-fix-peripheral-and-otg-suspend.patch b/queue-5.19/usb-dwc3-qcom-fix-peripheral-and-otg-suspend.patch
new file mode 100644 (file)
index 0000000..b0074ff
--- /dev/null
@@ -0,0 +1,78 @@
+From a989a972abf4ae3f79bbff2d9dc9956eda25e2f0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Aug 2022 17:09:58 +0200
+Subject: usb: dwc3: qcom: fix peripheral and OTG suspend
+
+From: Johan Hovold <johan+linaro@kernel.org>
+
+[ Upstream commit c5f14abeb52b0177b940fd734133d383da3521d8 ]
+
+A recent commit implementing wakeup support in host mode instead broke
+suspend for peripheral and OTG mode.
+
+The hack that was added in the suspend path to determine the speed of
+any device connected to the USB2 bus not only accesses internal driver
+data for a child device, but also dereferences a NULL pointer or
+accesses freed data when the controller is not acting as host.
+
+There's no quick fix to the layering violation, but since reverting
+would leave us with broken suspend in host mode with wakeup triggering
+immediately, let's keep the hack for now.
+
+Fix the immediate issues by only checking the host bus speed and
+enabling wakeup interrupts when acting as host.
+
+Fixes: 6895ea55c385 ("usb: dwc3: qcom: Configure wakeup interrupts during suspend")
+Reported-by: kernel test robot <lkp@intel.com>
+Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
+Link: https://lore.kernel.org/r/20220804151001.23612-7-johan+linaro@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/dwc3-qcom.c | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
+index 05b4666fde14..6ae0b7fc4e2c 100644
+--- a/drivers/usb/dwc3/dwc3-qcom.c
++++ b/drivers/usb/dwc3/dwc3-qcom.c
+@@ -309,8 +309,13 @@ static bool dwc3_qcom_is_host(struct dwc3_qcom *qcom)
+ static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom)
+ {
+       struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
+-      struct usb_hcd *hcd = platform_get_drvdata(dwc->xhci);
+       struct usb_device *udev;
++      struct usb_hcd *hcd;
++
++      /*
++       * FIXME: Fix this layering violation.
++       */
++      hcd = platform_get_drvdata(dwc->xhci);
+       /*
+        * It is possible to query the speed of all children of
+@@ -416,7 +421,11 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
+       if (ret)
+               dev_warn(qcom->dev, "failed to disable interconnect: %d\n", ret);
+-      if (wakeup) {
++      /*
++       * The role is stable during suspend as role switching is done from a
++       * freezable workqueue.
++       */
++      if (dwc3_qcom_is_host(qcom) && wakeup) {
+               qcom->usb2_speed = dwc3_qcom_read_usb2_speed(qcom);
+               dwc3_qcom_enable_interrupts(qcom);
+       }
+@@ -434,7 +443,7 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup)
+       if (!qcom->is_suspended)
+               return 0;
+-      if (wakeup)
++      if (dwc3_qcom_is_host(qcom) && wakeup)
+               dwc3_qcom_disable_interrupts(qcom);
+       for (i = 0; i < qcom->num_clocks; i++) {
+-- 
+2.35.1
+
diff --git a/queue-5.19/usb-dwc3-qcom-fix-runtime-pm-wakeup.patch b/queue-5.19/usb-dwc3-qcom-fix-runtime-pm-wakeup.patch
new file mode 100644 (file)
index 0000000..eb05a8b
--- /dev/null
@@ -0,0 +1,110 @@
+From f9697a17ff2d5fe5d3a0052b6482c955060dec07 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Aug 2022 17:09:57 +0200
+Subject: usb: dwc3: qcom: fix runtime PM wakeup
+
+From: Johan Hovold <johan+linaro@kernel.org>
+
+[ Upstream commit 6498a96c8c9ce8ae4078e586a607851491e29a33 ]
+
+A device must enable wakeups during runtime suspend regardless of
+whether it is capable and allowed to wake the system up from system
+suspend.
+
+Fixes: 2664deb09306 ("usb: dwc3: qcom: Honor wakeup enabled/disabled state")
+Tested-by: Matthias Kaehlcke <mka@chromium.org>
+Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
+Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
+Link: https://lore.kernel.org/r/20220804151001.23612-6-johan+linaro@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/dwc3-qcom.c | 19 +++++++++++--------
+ 1 file changed, 11 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
+index 6884026b9fad..05b4666fde14 100644
+--- a/drivers/usb/dwc3/dwc3-qcom.c
++++ b/drivers/usb/dwc3/dwc3-qcom.c
+@@ -397,7 +397,7 @@ static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
+       dwc3_qcom_enable_wakeup_irq(qcom->ss_phy_irq, 0);
+ }
+-static int dwc3_qcom_suspend(struct dwc3_qcom *qcom)
++static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
+ {
+       u32 val;
+       int i, ret;
+@@ -416,7 +416,7 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom)
+       if (ret)
+               dev_warn(qcom->dev, "failed to disable interconnect: %d\n", ret);
+-      if (device_may_wakeup(qcom->dev)) {
++      if (wakeup) {
+               qcom->usb2_speed = dwc3_qcom_read_usb2_speed(qcom);
+               dwc3_qcom_enable_interrupts(qcom);
+       }
+@@ -426,7 +426,7 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom)
+       return 0;
+ }
+-static int dwc3_qcom_resume(struct dwc3_qcom *qcom)
++static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup)
+ {
+       int ret;
+       int i;
+@@ -434,7 +434,7 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom)
+       if (!qcom->is_suspended)
+               return 0;
+-      if (device_may_wakeup(qcom->dev))
++      if (wakeup)
+               dwc3_qcom_disable_interrupts(qcom);
+       for (i = 0; i < qcom->num_clocks; i++) {
+@@ -945,9 +945,11 @@ static int dwc3_qcom_remove(struct platform_device *pdev)
+ static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev)
+ {
+       struct dwc3_qcom *qcom = dev_get_drvdata(dev);
++      bool wakeup = device_may_wakeup(dev);
+       int ret = 0;
+-      ret = dwc3_qcom_suspend(qcom);
++
++      ret = dwc3_qcom_suspend(qcom, wakeup);
+       if (!ret)
+               qcom->pm_suspended = true;
+@@ -957,9 +959,10 @@ static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev)
+ static int __maybe_unused dwc3_qcom_pm_resume(struct device *dev)
+ {
+       struct dwc3_qcom *qcom = dev_get_drvdata(dev);
++      bool wakeup = device_may_wakeup(dev);
+       int ret;
+-      ret = dwc3_qcom_resume(qcom);
++      ret = dwc3_qcom_resume(qcom, wakeup);
+       if (!ret)
+               qcom->pm_suspended = false;
+@@ -970,14 +973,14 @@ static int __maybe_unused dwc3_qcom_runtime_suspend(struct device *dev)
+ {
+       struct dwc3_qcom *qcom = dev_get_drvdata(dev);
+-      return dwc3_qcom_suspend(qcom);
++      return dwc3_qcom_suspend(qcom, true);
+ }
+ static int __maybe_unused dwc3_qcom_runtime_resume(struct device *dev)
+ {
+       struct dwc3_qcom *qcom = dev_get_drvdata(dev);
+-      return dwc3_qcom_resume(qcom);
++      return dwc3_qcom_resume(qcom, true);
+ }
+ static const struct dev_pm_ops dwc3_qcom_dev_pm_ops = {
+-- 
+2.35.1
+
diff --git a/queue-5.19/usb-dwc3-qcom-fix-use-after-free-on-runtime-pm-wakeu.patch b/queue-5.19/usb-dwc3-qcom-fix-use-after-free-on-runtime-pm-wakeu.patch
new file mode 100644 (file)
index 0000000..4071b14
--- /dev/null
@@ -0,0 +1,82 @@
+From 8dbc16b2e11c068a35bba2575b9c8e61b82ac2c0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Aug 2022 17:09:56 +0200
+Subject: usb: dwc3: qcom: fix use-after-free on runtime-PM wakeup
+
+From: Johan Hovold <johan+linaro@kernel.org>
+
+[ Upstream commit a872ab303d5ddd4c965f9cd868677781a33ce35a ]
+
+The Qualcomm dwc3 runtime-PM implementation checks the xhci
+platform-device pointer in the wakeup-interrupt handler to determine
+whether the controller is in host mode and if so triggers a resume.
+
+After a role switch in OTG mode the xhci platform-device would have been
+freed and the next wakeup from runtime suspend would access the freed
+memory.
+
+Note that role switching is executed from a freezable workqueue, which
+guarantees that the pointer is stable during suspend.
+
+Also note that runtime PM has been broken since commit 2664deb09306
+("usb: dwc3: qcom: Honor wakeup enabled/disabled state"), which
+incidentally also prevents this issue from being triggered.
+
+Fixes: a4333c3a6ba9 ("usb: dwc3: Add Qualcomm DWC3 glue driver")
+Cc: stable@vger.kernel.org      # 4.18
+Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
+Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
+Link: https://lore.kernel.org/r/20220804151001.23612-5-johan+linaro@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/dwc3-qcom.c | 14 +++++++++++++-
+ drivers/usb/dwc3/host.c      |  1 +
+ 2 files changed, 14 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
+index be2e3dd36440..b0ae73b0b1a1 100644
+--- a/drivers/usb/dwc3/dwc3-qcom.c
++++ b/drivers/usb/dwc3/dwc3-qcom.c
+@@ -298,6 +298,14 @@ static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom)
+       icc_put(qcom->icc_path_apps);
+ }
++/* Only usable in contexts where the role can not change. */
++static bool dwc3_qcom_is_host(struct dwc3_qcom *qcom)
++{
++      struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
++
++      return dwc->xhci;
++}
++
+ static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom)
+ {
+       struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
+@@ -457,7 +465,11 @@ static irqreturn_t qcom_dwc3_resume_irq(int irq, void *data)
+       if (qcom->pm_suspended)
+               return IRQ_HANDLED;
+-      if (dwc->xhci)
++      /*
++       * This is safe as role switching is done from a freezable workqueue
++       * and the wakeup interrupts are disabled as part of resume.
++       */
++      if (dwc3_qcom_is_host(qcom))
+               pm_runtime_resume(&dwc->xhci->dev);
+       return IRQ_HANDLED;
+diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
+index 06b3d988fbf3..a7154fe8206d 100644
+--- a/drivers/usb/dwc3/host.c
++++ b/drivers/usb/dwc3/host.c
+@@ -145,4 +145,5 @@ int dwc3_host_init(struct dwc3 *dwc)
+ void dwc3_host_exit(struct dwc3 *dwc)
+ {
+       platform_device_unregister(dwc->xhci);
++      dwc->xhci = NULL;
+ }
+-- 
+2.35.1
+
diff --git a/queue-5.19/usb-gadget-udc-xilinx-replace-memcpy-with-memcpy_toi.patch b/queue-5.19/usb-gadget-udc-xilinx-replace-memcpy-with-memcpy_toi.patch
new file mode 100644 (file)
index 0000000..16099cb
--- /dev/null
@@ -0,0 +1,156 @@
+From e6600e3398dae8339649463c395f66e197ebbaca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 24 Aug 2022 12:42:53 +0530
+Subject: usb: gadget: udc-xilinx: replace memcpy with memcpy_toio
+
+From: Piyush Mehta <piyush.mehta@amd.com>
+
+[ Upstream commit 8cb339f1c1f04baede9d54c1e40ac96247a6393b ]
+
+For ARM processor, unaligned access to device memory is not allowed.
+Method memcpy does not take care of alignment.
+
+USB detection failure with the unaligned address of memory access, with
+below kernel crash. To fix the unaligned address the kernel panic issue,
+replace memcpy with memcpy_toio method.
+
+Kernel crash:
+Unable to handle kernel paging request at virtual address ffff80000c05008a
+Mem abort info:
+  ESR = 0x96000061
+  EC = 0x25: DABT (current EL), IL = 32 bits
+  SET = 0, FnV = 0
+  EA = 0, S1PTW = 0
+  FSC = 0x21: alignment fault
+Data abort info:
+  ISV = 0, ISS = 0x00000061
+  CM = 0, WnR = 1
+swapper pgtable: 4k pages, 48-bit VAs, pgdp=000000000143b000
+[ffff80000c05008a] pgd=100000087ffff003, p4d=100000087ffff003,
+pud=100000087fffe003, pmd=1000000800bcc003, pte=00680000a0010713
+Internal error: Oops: 96000061 [#1] SMP
+Modules linked in:
+CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.15.19-xilinx-v2022.1 #1
+Hardware name: ZynqMP ZCU102 Rev1.0 (DT)
+pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
+pc : __memcpy+0x30/0x260
+lr : __xudc_ep0_queue+0xf0/0x110
+sp : ffff800008003d00
+x29: ffff800008003d00 x28: ffff800009474e80 x27: 00000000000000a0
+x26: 0000000000000100 x25: 0000000000000012 x24: ffff000800bc8080
+x23: 0000000000000001 x22: 0000000000000012 x21: ffff000800bc8080
+x20: 0000000000000012 x19: ffff000800bc8080 x18: 0000000000000000
+x17: ffff800876482000 x16: ffff800008004000 x15: 0000000000004000
+x14: 00001f09785d0400 x13: 0103020101005567 x12: 0781400000000200
+x11: 00000000c5672a10 x10: 00000000000008d0 x9 : ffff800009463cf0
+x8 : ffff8000094757b0 x7 : 0201010055670781 x6 : 4000000002000112
+x5 : ffff80000c05009a x4 : ffff000800a15012 x3 : ffff00080362ad80
+x2 : 0000000000000012 x1 : ffff000800a15000 x0 : ffff80000c050088
+Call trace:
+ __memcpy+0x30/0x260
+ xudc_ep0_queue+0x3c/0x60
+ usb_ep_queue+0x38/0x44
+ composite_ep0_queue.constprop.0+0x2c/0xc0
+ composite_setup+0x8d0/0x185c
+ configfs_composite_setup+0x74/0xb0
+ xudc_irq+0x570/0xa40
+ __handle_irq_event_percpu+0x58/0x170
+ handle_irq_event+0x60/0x120
+ handle_fasteoi_irq+0xc0/0x220
+ handle_domain_irq+0x60/0x90
+ gic_handle_irq+0x74/0xa0
+ call_on_irq_stack+0x2c/0x60
+ do_interrupt_handler+0x54/0x60
+ el1_interrupt+0x30/0x50
+ el1h_64_irq_handler+0x18/0x24
+ el1h_64_irq+0x78/0x7c
+ arch_cpu_idle+0x18/0x2c
+ do_idle+0xdc/0x15c
+ cpu_startup_entry+0x28/0x60
+ rest_init+0xc8/0xe0
+ arch_call_rest_init+0x10/0x1c
+ start_kernel+0x694/0x6d4
+ __primary_switched+0xa4/0xac
+
+Fixes: 1f7c51660034 ("usb: gadget: Add xilinx usb2 device support")
+Cc: stable@vger.kernel.org
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: Piyush Mehta <piyush.mehta@amd.com>
+Link: https://lore.kernel.org/r/20220824071253.1261096-1-piyush.mehta@amd.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/gadget/udc/udc-xilinx.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
+index 4827e3cd3834..054b69dc2f0c 100644
+--- a/drivers/usb/gadget/udc/udc-xilinx.c
++++ b/drivers/usb/gadget/udc/udc-xilinx.c
+@@ -499,11 +499,11 @@ static int xudc_eptxrx(struct xusb_ep *ep, struct xusb_req *req,
+               /* Get the Buffer address and copy the transmit data.*/
+               eprambase = (u32 __force *)(udc->addr + ep->rambase);
+               if (ep->is_in) {
+-                      memcpy(eprambase, bufferptr, bytestosend);
++                      memcpy_toio(eprambase, bufferptr, bytestosend);
+                       udc->write_fn(udc->addr, ep->offset +
+                                     XUSB_EP_BUF0COUNT_OFFSET, bufferlen);
+               } else {
+-                      memcpy(bufferptr, eprambase, bytestosend);
++                      memcpy_toio(bufferptr, eprambase, bytestosend);
+               }
+               /*
+                * Enable the buffer for transmission.
+@@ -517,11 +517,11 @@ static int xudc_eptxrx(struct xusb_ep *ep, struct xusb_req *req,
+               eprambase = (u32 __force *)(udc->addr + ep->rambase +
+                            ep->ep_usb.maxpacket);
+               if (ep->is_in) {
+-                      memcpy(eprambase, bufferptr, bytestosend);
++                      memcpy_toio(eprambase, bufferptr, bytestosend);
+                       udc->write_fn(udc->addr, ep->offset +
+                                     XUSB_EP_BUF1COUNT_OFFSET, bufferlen);
+               } else {
+-                      memcpy(bufferptr, eprambase, bytestosend);
++                      memcpy_toio(bufferptr, eprambase, bytestosend);
+               }
+               /*
+                * Enable the buffer for transmission.
+@@ -1023,7 +1023,7 @@ static int __xudc_ep0_queue(struct xusb_ep *ep0, struct xusb_req *req)
+                          udc->addr);
+               length = req->usb_req.actual = min_t(u32, length,
+                                                    EP0_MAX_PACKET);
+-              memcpy(corebuf, req->usb_req.buf, length);
++              memcpy_toio(corebuf, req->usb_req.buf, length);
+               udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, length);
+               udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+       } else {
+@@ -1752,7 +1752,7 @@ static void xudc_handle_setup(struct xusb_udc *udc)
+       /* Load up the chapter 9 command buffer.*/
+       ep0rambase = (u32 __force *) (udc->addr + XUSB_SETUP_PKT_ADDR_OFFSET);
+-      memcpy(&setup, ep0rambase, 8);
++      memcpy_toio(&setup, ep0rambase, 8);
+       udc->setup = setup;
+       udc->setup.wValue = cpu_to_le16(setup.wValue);
+@@ -1839,7 +1839,7 @@ static void xudc_ep0_out(struct xusb_udc *udc)
+                            (ep0->rambase << 2));
+               buffer = req->usb_req.buf + req->usb_req.actual;
+               req->usb_req.actual = req->usb_req.actual + bytes_to_rx;
+-              memcpy(buffer, ep0rambase, bytes_to_rx);
++              memcpy_toio(buffer, ep0rambase, bytes_to_rx);
+               if (req->usb_req.length == req->usb_req.actual) {
+                       /* Data transfer completed get ready for Status stage */
+@@ -1915,7 +1915,7 @@ static void xudc_ep0_in(struct xusb_udc *udc)
+                                    (ep0->rambase << 2));
+                       buffer = req->usb_req.buf + req->usb_req.actual;
+                       req->usb_req.actual = req->usb_req.actual + length;
+-                      memcpy(ep0rambase, buffer, length);
++                      memcpy_toio(ep0rambase, buffer, length);
+               }
+               udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, count);
+               udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+-- 
+2.35.1
+
diff --git a/queue-5.19/xfrm-fix-xfrma_lastused-comment.patch b/queue-5.19/xfrm-fix-xfrma_lastused-comment.patch
new file mode 100644 (file)
index 0000000..5f0b1ce
--- /dev/null
@@ -0,0 +1,35 @@
+From 3ad096e8de321762452e526c210d8e009777836d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 27 Jul 2022 17:40:53 +0200
+Subject: xfrm: fix XFRMA_LASTUSED comment
+
+From: Antony Antony <antony.antony@secunet.com>
+
+[ Upstream commit 36d763509be326bb383b1b1852a129ff58d74e3b ]
+
+It is a __u64, internally time64_t.
+
+Fixes: bf825f81b454 ("xfrm: introduce basic mark infrastructure")
+Signed-off-by: Antony Antony <antony.antony@secunet.com>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/uapi/linux/xfrm.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h
+index 65e13a099b1a..a9f5d884560a 100644
+--- a/include/uapi/linux/xfrm.h
++++ b/include/uapi/linux/xfrm.h
+@@ -296,7 +296,7 @@ enum xfrm_attr_type_t {
+       XFRMA_ETIMER_THRESH,
+       XFRMA_SRCADDR,          /* xfrm_address_t */
+       XFRMA_COADDR,           /* xfrm_address_t */
+-      XFRMA_LASTUSED,         /* unsigned long  */
++      XFRMA_LASTUSED,         /* __u64 */
+       XFRMA_POLICY_TYPE,      /* struct xfrm_userpolicy_type */
+       XFRMA_MIGRATE,
+       XFRMA_ALG_AEAD,         /* struct xfrm_algo_aead */
+-- 
+2.35.1
+