]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 5.15
authorSasha Levin <sashal@kernel.org>
Mon, 5 Sep 2022 12:52:09 +0000 (08:52 -0400)
committerSasha Levin <sashal@kernel.org>
Mon, 5 Sep 2022 12:52:09 +0000 (08:52 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
36 files changed:
queue-5.15/alsa-hda-intel-nhlt-correct-the-handling-of-fmt_conf.patch [new file with mode: 0644]
queue-5.15/alsa-hda-intel-nhlt-remove-use-of-__func__-in-dev_db.patch [new file with mode: 0644]
queue-5.15/bpf-cgroup-fix-kernel-bug-in-purge_effective_progs.patch [new file with mode: 0644]
queue-5.15/bpf-restrict-bpf_sys_bpf-to-cap_perfmon.patch [new file with mode: 0644]
queue-5.15/drm-i915-backlight-extract-backlight-code-to-a-separ.patch [new file with mode: 0644]
queue-5.15/drm-i915-display-avoid-warnings-when-registering-dua.patch [new file with mode: 0644]
queue-5.15/drm-msm-dp-delete-dp_recovered_clock_out_en-to-fix-t.patch [new file with mode: 0644]
queue-5.15/drm-msm-dsi-fix-number-of-regulators-for-msm8996_dsi.patch [new file with mode: 0644]
queue-5.15/drm-msm-dsi-fix-number-of-regulators-for-sdm660.patch [new file with mode: 0644]
queue-5.15/drm-msm-dsi-fix-the-inconsistent-indenting.patch [new file with mode: 0644]
queue-5.15/ethernet-rocker-fix-sleep-in-atomic-context-bug-in-n.patch [new file with mode: 0644]
queue-5.15/ieee802154-adf7242-defer-destroy_workqueue-call.patch [new file with mode: 0644]
queue-5.15/iio-adc-mcp3911-make-use-of-the-sign-bit.patch [new file with mode: 0644]
queue-5.15/kcm-fix-strp_init-order-and-cleanup.patch [new file with mode: 0644]
queue-5.15/mlxbf_gige-compute-mdio-period-based-on-i1clk.patch [new file with mode: 0644]
queue-5.15/net-dsa-xrs700x-use-irqsave-variant-for-u64-stats-up.patch [new file with mode: 0644]
queue-5.15/net-sched-fix-netdevice-reference-leaks-in-attach_de.patch [new file with mode: 0644]
queue-5.15/net-sched-tbf-don-t-call-qdisc_put-while-holding-tre.patch [new file with mode: 0644]
queue-5.15/net-smc-remove-redundant-refcount-increase.patch [new file with mode: 0644]
queue-5.15/net-smsc911x-stop-and-start-phy-during-suspend-and-r.patch [new file with mode: 0644]
queue-5.15/net-sparx5-fix-handling-uneven-length-packets-in-man.patch [new file with mode: 0644]
queue-5.15/openvswitch-fix-memory-leak-at-failed-datapath-creat.patch [new file with mode: 0644]
queue-5.15/platform-x86-pmc_atom-fix-slp_typx-bitfield-mask.patch [new file with mode: 0644]
queue-5.15/revert-sch_cake-return-__net_xmit_stolen-when-consum.patch [new file with mode: 0644]
queue-5.15/revert-xhci-turn-off-port-power-in-shutdown.patch [new file with mode: 0644]
queue-5.15/sch_cake-return-__net_xmit_stolen-when-consuming-enq.patch [new file with mode: 0644]
queue-5.15/series [new file with mode: 0644]
queue-5.15/skmsg-fix-wrong-last-sg-check-in-sk_msg_recvmsg.patch [new file with mode: 0644]
queue-5.15/soundwire-qcom-fix-device-status-array-range.patch [new file with mode: 0644]
queue-5.15/tcp-annotate-data-race-around-challenge_timestamp.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-qcom-add-helper-functions-to-enable-disable.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-qcom-configure-wakeup-interrupts-during-sus.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-qcom-fix-peripheral-and-otg-suspend.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-qcom-fix-runtime-pm-wakeup.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-qcom-fix-use-after-free-on-runtime-pm-wakeu.patch [new file with mode: 0644]
queue-5.15/wifi-cfg80211-debugfs-fix-return-type-in-ht40allow_m.patch [new file with mode: 0644]

diff --git a/queue-5.15/alsa-hda-intel-nhlt-correct-the-handling-of-fmt_conf.patch b/queue-5.15/alsa-hda-intel-nhlt-correct-the-handling-of-fmt_conf.patch
new file mode 100644 (file)
index 0000000..98d9180
--- /dev/null
@@ -0,0 +1,57 @@
+From bb25f6efc7b064d170fdb5883040a66146420390 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 23 Aug 2022 15:24:05 +0300
+Subject: ALSA: hda: intel-nhlt: Correct the handling of fmt_config flexible
+ array
+
+From: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+
+[ Upstream commit 2e6481a3f3ee6234ce577454e1d88aca55f51d47 ]
+
+The struct nhlt_format's fmt_config is a flexible array, it must not be
+used as normal array.
+When moving to the next nhlt_fmt_cfg we need to take into account the data
+behind the ->config.caps (indicated by ->config.size).
+
+Fixes: a864e8f159b13 ("ALSA: hda: intel-nhlt: verify config type")
+Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+Reviewed-by: Jaska Uimonen <jaska.uimonen@linux.intel.com>
+Link: https://lore.kernel.org/r/20220823122405.18464-1-peter.ujfalusi@linux.intel.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/hda/intel-nhlt.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c
+index 5e04fedaec49e..8714891f50b0a 100644
+--- a/sound/hda/intel-nhlt.c
++++ b/sound/hda/intel-nhlt.c
+@@ -55,16 +55,22 @@ int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
+               /* find max number of channels based on format_configuration */
+               if (fmt_configs->fmt_count) {
++                      struct nhlt_fmt_cfg *fmt_cfg = fmt_configs->fmt_config;
++
+                       dev_dbg(dev, "found %d format definitions\n",
+                               fmt_configs->fmt_count);
+                       for (i = 0; i < fmt_configs->fmt_count; i++) {
+                               struct wav_fmt_ext *fmt_ext;
+-                              fmt_ext = &fmt_configs->fmt_config[i].fmt_ext;
++                              fmt_ext = &fmt_cfg->fmt_ext;
+                               if (fmt_ext->fmt.channels > max_ch)
+                                       max_ch = fmt_ext->fmt.channels;
++
++                              /* Move to the next nhlt_fmt_cfg */
++                              fmt_cfg = (struct nhlt_fmt_cfg *)(fmt_cfg->config.caps +
++                                                                fmt_cfg->config.size);
+                       }
+                       dev_dbg(dev, "max channels found %d\n", max_ch);
+               } else {
+-- 
+2.35.1
+
diff --git a/queue-5.15/alsa-hda-intel-nhlt-remove-use-of-__func__-in-dev_db.patch b/queue-5.15/alsa-hda-intel-nhlt-remove-use-of-__func__-in-dev_db.patch
new file mode 100644 (file)
index 0000000..5a5c4dc
--- /dev/null
@@ -0,0 +1,79 @@
+From e0c7863907895686180253a50869a72ae75bbe54 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jun 2022 17:05:59 -0500
+Subject: ALSA: hda: intel-nhlt: remove use of __func__ in dev_dbg
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+
+[ Upstream commit 6376ab02374822e1e8758a848ee736a182786a2e ]
+
+The module and function information can be added with
+'modprobe foo dyndbg=+pmf'
+
+Suggested-by: Greg KH <gregkh@linuxfoundation.org>
+Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
+Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+Link: https://lore.kernel.org/r/20220616220559.136160-1-pierre-louis.bossart@linux.intel.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/hda/intel-nhlt.c | 17 ++++++++---------
+ 1 file changed, 8 insertions(+), 9 deletions(-)
+
+diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c
+index e2237239d922a..5e04fedaec49e 100644
+--- a/sound/hda/intel-nhlt.c
++++ b/sound/hda/intel-nhlt.c
+@@ -55,8 +55,8 @@ int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
+               /* find max number of channels based on format_configuration */
+               if (fmt_configs->fmt_count) {
+-                      dev_dbg(dev, "%s: found %d format definitions\n",
+-                              __func__, fmt_configs->fmt_count);
++                      dev_dbg(dev, "found %d format definitions\n",
++                              fmt_configs->fmt_count);
+                       for (i = 0; i < fmt_configs->fmt_count; i++) {
+                               struct wav_fmt_ext *fmt_ext;
+@@ -66,9 +66,9 @@ int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
+                               if (fmt_ext->fmt.channels > max_ch)
+                                       max_ch = fmt_ext->fmt.channels;
+                       }
+-                      dev_dbg(dev, "%s: max channels found %d\n", __func__, max_ch);
++                      dev_dbg(dev, "max channels found %d\n", max_ch);
+               } else {
+-                      dev_dbg(dev, "%s: No format information found\n", __func__);
++                      dev_dbg(dev, "No format information found\n");
+               }
+               if (cfg->device_config.config_type != NHLT_CONFIG_TYPE_MIC_ARRAY) {
+@@ -95,17 +95,16 @@ int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
+                       }
+                       if (dmic_geo > 0) {
+-                              dev_dbg(dev, "%s: Array with %d dmics\n", __func__, dmic_geo);
++                              dev_dbg(dev, "Array with %d dmics\n", dmic_geo);
+                       }
+                       if (max_ch > dmic_geo) {
+-                              dev_dbg(dev, "%s: max channels %d exceed dmic number %d\n",
+-                                      __func__, max_ch, dmic_geo);
++                              dev_dbg(dev, "max channels %d exceed dmic number %d\n",
++                                      max_ch, dmic_geo);
+                       }
+               }
+       }
+-      dev_dbg(dev, "%s: dmic number %d max_ch %d\n",
+-              __func__, dmic_geo, max_ch);
++      dev_dbg(dev, "dmic number %d max_ch %d\n", dmic_geo, max_ch);
+       return dmic_geo;
+ }
+-- 
+2.35.1
+
diff --git a/queue-5.15/bpf-cgroup-fix-kernel-bug-in-purge_effective_progs.patch b/queue-5.15/bpf-cgroup-fix-kernel-bug-in-purge_effective_progs.patch
new file mode 100644 (file)
index 0000000..9f9ea13
--- /dev/null
@@ -0,0 +1,107 @@
+From fe8d36b8fcee5ee12cd36bcc69497c7daec0799d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 13 Aug 2022 21:40:30 +0800
+Subject: bpf, cgroup: Fix kernel BUG in purge_effective_progs
+
+From: Pu Lehui <pulehui@huawei.com>
+
+[ Upstream commit 7d6620f107bae6ed687ff07668e8e8f855487aa9 ]
+
+Syzkaller reported a triggered kernel BUG as follows:
+
+  ------------[ cut here ]------------
+  kernel BUG at kernel/bpf/cgroup.c:925!
+  invalid opcode: 0000 [#1] PREEMPT SMP NOPTI
+  CPU: 1 PID: 194 Comm: detach Not tainted 5.19.0-14184-g69dac8e431af #8
+  Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS
+  rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014
+  RIP: 0010:__cgroup_bpf_detach+0x1f2/0x2a0
+  Code: 00 e8 92 60 30 00 84 c0 75 d8 4c 89 e0 31 f6 85 f6 74 19 42 f6 84
+  28 48 05 00 00 02 75 0e 48 8b 80 c0 00 00 00 48 85 c0 75 e5 <0f> 0b 48
+  8b 0c5
+  RSP: 0018:ffffc9000055bdb0 EFLAGS: 00000246
+  RAX: 0000000000000000 RBX: ffff888100ec0800 RCX: ffffc900000f1000
+  RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffff888100ec4578
+  RBP: 0000000000000000 R08: ffff888100ec0800 R09: 0000000000000040
+  R10: 0000000000000000 R11: 0000000000000000 R12: ffff888100ec4000
+  R13: 000000000000000d R14: ffffc90000199000 R15: ffff888100effb00
+  FS:  00007f68213d2b80(0000) GS:ffff88813bc80000(0000)
+  knlGS:0000000000000000
+  CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
+  CR2: 000055f74a0e5850 CR3: 0000000102836000 CR4: 00000000000006e0
+  Call Trace:
+   <TASK>
+   cgroup_bpf_prog_detach+0xcc/0x100
+   __sys_bpf+0x2273/0x2a00
+   __x64_sys_bpf+0x17/0x20
+   do_syscall_64+0x3b/0x90
+   entry_SYSCALL_64_after_hwframe+0x63/0xcd
+  RIP: 0033:0x7f68214dbcb9
+  Code: 08 44 89 e0 5b 41 5c c3 66 0f 1f 84 00 00 00 00 00 48 89 f8 48 89
+  f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01
+  f0 ff8
+  RSP: 002b:00007ffeb487db68 EFLAGS: 00000246 ORIG_RAX: 0000000000000141
+  RAX: ffffffffffffffda RBX: 000000000000000b RCX: 00007f68214dbcb9
+  RDX: 0000000000000090 RSI: 00007ffeb487db70 RDI: 0000000000000009
+  RBP: 0000000000000003 R08: 0000000000000012 R09: 0000000b00000003
+  R10: 00007ffeb487db70 R11: 0000000000000246 R12: 00007ffeb487dc20
+  R13: 0000000000000004 R14: 0000000000000001 R15: 000055f74a1011b0
+   </TASK>
+  Modules linked in:
+  ---[ end trace 0000000000000000 ]---
+
+Repetition steps:
+
+For the following cgroup tree,
+
+  root
+   |
+  cg1
+   |
+  cg2
+
+  1. attach prog2 to cg2, and then attach prog1 to cg1, both bpf progs
+     attach type is NONE or OVERRIDE.
+  2. write 1 to /proc/thread-self/fail-nth for failslab.
+  3. detach prog1 for cg1, and then kernel BUG occur.
+
+Failslab injection will cause kmalloc fail and fall back to
+purge_effective_progs. The problem is that cg2 have attached another prog,
+so when go through cg2 layer, iteration will add pos to 1, and subsequent
+operations will be skipped by the following condition, and cg will meet
+NULL in the end.
+
+  `if (pos && !(cg->bpf.flags[atype] & BPF_F_ALLOW_MULTI))`
+
+The NULL cg means no link or prog match, this is as expected, and it's not
+a bug. So here just skip the no match situation.
+
+Fixes: 4c46091ee985 ("bpf: Fix KASAN use-after-free Read in compute_effective_progs")
+Signed-off-by: Pu Lehui <pulehui@huawei.com>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Acked-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/bpf/20220813134030.1972696-1-pulehui@huawei.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/bpf/cgroup.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
+index 565e4c59db660..eb3e787a3a977 100644
+--- a/kernel/bpf/cgroup.c
++++ b/kernel/bpf/cgroup.c
+@@ -709,8 +709,10 @@ static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog,
+                               pos++;
+                       }
+               }
++
++              /* no link or prog match, skip the cgroup of this layer */
++              continue;
+ found:
+-              BUG_ON(!cg);
+               progs = rcu_dereference_protected(
+                               desc->bpf.effective[atype],
+                               lockdep_is_held(&cgroup_mutex));
+-- 
+2.35.1
+
diff --git a/queue-5.15/bpf-restrict-bpf_sys_bpf-to-cap_perfmon.patch b/queue-5.15/bpf-restrict-bpf_sys_bpf-to-cap_perfmon.patch
new file mode 100644 (file)
index 0000000..2f828ca
--- /dev/null
@@ -0,0 +1,41 @@
+From ced0af0a708c1851905310ca658e59fd7d7f6bec Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Aug 2022 20:55:16 +0000
+Subject: bpf: Restrict bpf_sys_bpf to CAP_PERFMON
+
+From: YiFei Zhu <zhuyifei@google.com>
+
+[ Upstream commit 14b20b784f59bdd95f6f1cfb112c9818bcec4d84 ]
+
+The verifier cannot perform sufficient validation of any pointers passed
+into bpf_attr and treats them as integers rather than pointers. The helper
+will then read from arbitrary pointers passed into it. Restrict the helper
+to CAP_PERFMON since the security model in BPF of arbitrary kernel read is
+CAP_BPF + CAP_PERFMON.
+
+Fixes: af2ac3e13e45 ("bpf: Prepare bpf syscall to be used from kernel and user space.")
+Signed-off-by: YiFei Zhu <zhuyifei@google.com>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Acked-by: Alexei Starovoitov <ast@kernel.org>
+Link: https://lore.kernel.org/bpf/20220816205517.682470-1-zhuyifei@google.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/bpf/syscall.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
+index 48e02a725563f..99ce46f518893 100644
+--- a/kernel/bpf/syscall.c
++++ b/kernel/bpf/syscall.c
+@@ -4785,7 +4785,7 @@ syscall_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
+ {
+       switch (func_id) {
+       case BPF_FUNC_sys_bpf:
+-              return &bpf_sys_bpf_proto;
++              return !perfmon_capable() ? NULL : &bpf_sys_bpf_proto;
+       case BPF_FUNC_btf_find_by_name_kind:
+               return &bpf_btf_find_by_name_kind_proto;
+       case BPF_FUNC_sys_close:
+-- 
+2.35.1
+
diff --git a/queue-5.15/drm-i915-backlight-extract-backlight-code-to-a-separ.patch b/queue-5.15/drm-i915-backlight-extract-backlight-code-to-a-separ.patch
new file mode 100644 (file)
index 0000000..b14c5c3
--- /dev/null
@@ -0,0 +1,3887 @@
+From fb5afdd520f004988c4a59da63a466c5ca7e8936 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 25 Aug 2021 14:06:50 +0300
+Subject: drm/i915/backlight: extract backlight code to a separate file
+
+From: Jani Nikula <jani.nikula@intel.com>
+
+[ Upstream commit 6cc42fbeb150ff33b17cbf108713ca4be23994d8 ]
+
+In a long overdue refactoring, split out backlight code to new
+intel_backlight.[ch]. Simple code movement, leave renames for follow-up
+work. No functional changes.
+
+Cc: Lyude Paul <lyude@redhat.com>
+Reviewed-by: Lyude Paul <lyude@redhat.com>
+Signed-off-by: Jani Nikula <jani.nikula@intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/97d310848f03061473b9b2328e2c5c4dcf263cfa.1629888677.git.jani.nikula@intel.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/Makefile                 |    1 +
+ drivers/gpu/drm/i915/display/g4x_dp.c         |    2 +-
+ drivers/gpu/drm/i915/display/icl_dsi.c        |    1 +
+ .../gpu/drm/i915/display/intel_backlight.c    | 1778 +++++++++++++++++
+ .../gpu/drm/i915/display/intel_backlight.h    |   51 +
+ .../gpu/drm/i915/display/intel_connector.c    |    4 +-
+ drivers/gpu/drm/i915/display/intel_ddi.c      |    2 +-
+ drivers/gpu/drm/i915/display/intel_dp.c       |    1 +
+ .../drm/i915/display/intel_dp_aux_backlight.c |    2 +-
+ drivers/gpu/drm/i915/display/intel_lvds.c     |    1 +
+ drivers/gpu/drm/i915/display/intel_opregion.c |    3 +-
+ drivers/gpu/drm/i915/display/intel_panel.c    | 1767 +---------------
+ drivers/gpu/drm/i915/display/intel_panel.h    |   34 +-
+ drivers/gpu/drm/i915/display/vlv_dsi.c        |    1 +
+ 14 files changed, 1843 insertions(+), 1805 deletions(-)
+ create mode 100644 drivers/gpu/drm/i915/display/intel_backlight.c
+ create mode 100644 drivers/gpu/drm/i915/display/intel_backlight.h
+
+diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
+index 26cf754229451..9d371be7dc5cd 100644
+--- a/drivers/gpu/drm/i915/Makefile
++++ b/drivers/gpu/drm/i915/Makefile
+@@ -249,6 +249,7 @@ i915-y += \
+       display/g4x_dp.o \
+       display/g4x_hdmi.o \
+       display/icl_dsi.o \
++      display/intel_backlight.o \
+       display/intel_crt.o \
+       display/intel_ddi.o \
+       display/intel_ddi_buf_trans.o \
+diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c
+index de0f358184aa3..29c0eca647e34 100644
+--- a/drivers/gpu/drm/i915/display/g4x_dp.c
++++ b/drivers/gpu/drm/i915/display/g4x_dp.c
+@@ -7,6 +7,7 @@
+ #include "g4x_dp.h"
+ #include "intel_audio.h"
++#include "intel_backlight.h"
+ #include "intel_connector.h"
+ #include "intel_de.h"
+ #include "intel_display_types.h"
+@@ -16,7 +17,6 @@
+ #include "intel_fifo_underrun.h"
+ #include "intel_hdmi.h"
+ #include "intel_hotplug.h"
+-#include "intel_panel.h"
+ #include "intel_pps.h"
+ #include "intel_sideband.h"
+diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c
+index 638a00b2dc2d2..2601873e15466 100644
+--- a/drivers/gpu/drm/i915/display/icl_dsi.c
++++ b/drivers/gpu/drm/i915/display/icl_dsi.c
+@@ -29,6 +29,7 @@
+ #include <drm/drm_mipi_dsi.h>
+ #include "intel_atomic.h"
++#include "intel_backlight.h"
+ #include "intel_combo_phy.h"
+ #include "intel_connector.h"
+ #include "intel_crtc.h"
+diff --git a/drivers/gpu/drm/i915/display/intel_backlight.c b/drivers/gpu/drm/i915/display/intel_backlight.c
+new file mode 100644
+index 0000000000000..4b0086ee48519
+--- /dev/null
++++ b/drivers/gpu/drm/i915/display/intel_backlight.c
+@@ -0,0 +1,1778 @@
++// SPDX-License-Identifier: MIT
++/*
++ * Copyright © 2021 Intel Corporation
++ */
++
++#include <linux/kernel.h>
++#include <linux/pwm.h>
++
++#include "intel_backlight.h"
++#include "intel_connector.h"
++#include "intel_de.h"
++#include "intel_display_types.h"
++#include "intel_dp_aux_backlight.h"
++#include "intel_dsi_dcs_backlight.h"
++#include "intel_panel.h"
++
++/**
++ * scale - scale values from one range to another
++ * @source_val: value in range [@source_min..@source_max]
++ * @source_min: minimum legal value for @source_val
++ * @source_max: maximum legal value for @source_val
++ * @target_min: corresponding target value for @source_min
++ * @target_max: corresponding target value for @source_max
++ *
++ * Return @source_val in range [@source_min..@source_max] scaled to range
++ * [@target_min..@target_max].
++ */
++static u32 scale(u32 source_val,
++               u32 source_min, u32 source_max,
++               u32 target_min, u32 target_max)
++{
++      u64 target_val;
++
++      WARN_ON(source_min > source_max);
++      WARN_ON(target_min > target_max);
++
++      /* defensive */
++      source_val = clamp(source_val, source_min, source_max);
++
++      /* avoid overflows */
++      target_val = mul_u32_u32(source_val - source_min,
++                               target_max - target_min);
++      target_val = DIV_ROUND_CLOSEST_ULL(target_val, source_max - source_min);
++      target_val += target_min;
++
++      return target_val;
++}
++
++/*
++ * Scale user_level in range [0..user_max] to [0..hw_max], clamping the result
++ * to [hw_min..hw_max].
++ */
++static u32 clamp_user_to_hw(struct intel_connector *connector,
++                          u32 user_level, u32 user_max)
++{
++      struct intel_panel *panel = &connector->panel;
++      u32 hw_level;
++
++      hw_level = scale(user_level, 0, user_max, 0, panel->backlight.max);
++      hw_level = clamp(hw_level, panel->backlight.min, panel->backlight.max);
++
++      return hw_level;
++}
++
++/* Scale hw_level in range [hw_min..hw_max] to [0..user_max]. */
++static u32 scale_hw_to_user(struct intel_connector *connector,
++                          u32 hw_level, u32 user_max)
++{
++      struct intel_panel *panel = &connector->panel;
++
++      return scale(hw_level, panel->backlight.min, panel->backlight.max,
++                   0, user_max);
++}
++
++u32 intel_panel_invert_pwm_level(struct intel_connector *connector, u32 val)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++
++      drm_WARN_ON(&dev_priv->drm, panel->backlight.pwm_level_max == 0);
++
++      if (dev_priv->params.invert_brightness < 0)
++              return val;
++
++      if (dev_priv->params.invert_brightness > 0 ||
++          dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) {
++              return panel->backlight.pwm_level_max - val + panel->backlight.pwm_level_min;
++      }
++
++      return val;
++}
++
++void intel_panel_set_pwm_level(const struct drm_connector_state *conn_state, u32 val)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *i915 = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++
++      drm_dbg_kms(&i915->drm, "set backlight PWM = %d\n", val);
++      panel->backlight.pwm_funcs->set(conn_state, val);
++}
++
++u32 intel_panel_backlight_level_to_pwm(struct intel_connector *connector, u32 val)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++
++      drm_WARN_ON_ONCE(&dev_priv->drm,
++                       panel->backlight.max == 0 || panel->backlight.pwm_level_max == 0);
++
++      val = scale(val, panel->backlight.min, panel->backlight.max,
++                  panel->backlight.pwm_level_min, panel->backlight.pwm_level_max);
++
++      return intel_panel_invert_pwm_level(connector, val);
++}
++
++u32 intel_panel_backlight_level_from_pwm(struct intel_connector *connector, u32 val)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++
++      drm_WARN_ON_ONCE(&dev_priv->drm,
++                       panel->backlight.max == 0 || panel->backlight.pwm_level_max == 0);
++
++      if (dev_priv->params.invert_brightness > 0 ||
++          (dev_priv->params.invert_brightness == 0 && dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS))
++              val = panel->backlight.pwm_level_max - (val - panel->backlight.pwm_level_min);
++
++      return scale(val, panel->backlight.pwm_level_min, panel->backlight.pwm_level_max,
++                   panel->backlight.min, panel->backlight.max);
++}
++
++static u32 lpt_get_backlight(struct intel_connector *connector, enum pipe unused)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++
++      return intel_de_read(dev_priv, BLC_PWM_PCH_CTL2) & BACKLIGHT_DUTY_CYCLE_MASK;
++}
++
++static u32 pch_get_backlight(struct intel_connector *connector, enum pipe unused)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++
++      return intel_de_read(dev_priv, BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
++}
++
++static u32 i9xx_get_backlight(struct intel_connector *connector, enum pipe unused)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 val;
++
++      val = intel_de_read(dev_priv, BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
++      if (DISPLAY_VER(dev_priv) < 4)
++              val >>= 1;
++
++      if (panel->backlight.combination_mode) {
++              u8 lbpc;
++
++              pci_read_config_byte(to_pci_dev(dev_priv->drm.dev), LBPC, &lbpc);
++              val *= lbpc;
++      }
++
++      return val;
++}
++
++static u32 vlv_get_backlight(struct intel_connector *connector, enum pipe pipe)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++
++      if (drm_WARN_ON(&dev_priv->drm, pipe != PIPE_A && pipe != PIPE_B))
++              return 0;
++
++      return intel_de_read(dev_priv, VLV_BLC_PWM_CTL(pipe)) & BACKLIGHT_DUTY_CYCLE_MASK;
++}
++
++static u32 bxt_get_backlight(struct intel_connector *connector, enum pipe unused)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++
++      return intel_de_read(dev_priv,
++                           BXT_BLC_PWM_DUTY(panel->backlight.controller));
++}
++
++static u32 ext_pwm_get_backlight(struct intel_connector *connector, enum pipe unused)
++{
++      struct intel_panel *panel = &connector->panel;
++      struct pwm_state state;
++
++      pwm_get_state(panel->backlight.pwm, &state);
++      return pwm_get_relative_duty_cycle(&state, 100);
++}
++
++static void lpt_set_backlight(const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++
++      u32 val = intel_de_read(dev_priv, BLC_PWM_PCH_CTL2) & ~BACKLIGHT_DUTY_CYCLE_MASK;
++      intel_de_write(dev_priv, BLC_PWM_PCH_CTL2, val | level);
++}
++
++static void pch_set_backlight(const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      u32 tmp;
++
++      tmp = intel_de_read(dev_priv, BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
++      intel_de_write(dev_priv, BLC_PWM_CPU_CTL, tmp | level);
++}
++
++static void i9xx_set_backlight(const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 tmp, mask;
++
++      drm_WARN_ON(&dev_priv->drm, panel->backlight.pwm_level_max == 0);
++
++      if (panel->backlight.combination_mode) {
++              u8 lbpc;
++
++              lbpc = level * 0xfe / panel->backlight.pwm_level_max + 1;
++              level /= lbpc;
++              pci_write_config_byte(to_pci_dev(dev_priv->drm.dev), LBPC, lbpc);
++      }
++
++      if (DISPLAY_VER(dev_priv) == 4) {
++              mask = BACKLIGHT_DUTY_CYCLE_MASK;
++      } else {
++              level <<= 1;
++              mask = BACKLIGHT_DUTY_CYCLE_MASK_PNV;
++      }
++
++      tmp = intel_de_read(dev_priv, BLC_PWM_CTL) & ~mask;
++      intel_de_write(dev_priv, BLC_PWM_CTL, tmp | level);
++}
++
++static void vlv_set_backlight(const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      enum pipe pipe = to_intel_crtc(conn_state->crtc)->pipe;
++      u32 tmp;
++
++      tmp = intel_de_read(dev_priv, VLV_BLC_PWM_CTL(pipe)) & ~BACKLIGHT_DUTY_CYCLE_MASK;
++      intel_de_write(dev_priv, VLV_BLC_PWM_CTL(pipe), tmp | level);
++}
++
++static void bxt_set_backlight(const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++
++      intel_de_write(dev_priv,
++                     BXT_BLC_PWM_DUTY(panel->backlight.controller), level);
++}
++
++static void ext_pwm_set_backlight(const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_panel *panel = &to_intel_connector(conn_state->connector)->panel;
++
++      pwm_set_relative_duty_cycle(&panel->backlight.pwm_state, level, 100);
++      pwm_apply_state(panel->backlight.pwm, &panel->backlight.pwm_state);
++}
++
++static void
++intel_panel_actually_set_backlight(const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *i915 = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++
++      drm_dbg_kms(&i915->drm, "set backlight level = %d\n", level);
++
++      panel->backlight.funcs->set(conn_state, level);
++}
++
++/* set backlight brightness to level in range [0..max], assuming hw min is
++ * respected.
++ */
++void intel_panel_set_backlight_acpi(const struct drm_connector_state *conn_state,
++                                  u32 user_level, u32 user_max)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 hw_level;
++
++      /*
++       * Lack of crtc may occur during driver init because
++       * connection_mutex isn't held across the entire backlight
++       * setup + modeset readout, and the BIOS can issue the
++       * requests at any time.
++       */
++      if (!panel->backlight.present || !conn_state->crtc)
++              return;
++
++      mutex_lock(&dev_priv->backlight_lock);
++
++      drm_WARN_ON(&dev_priv->drm, panel->backlight.max == 0);
++
++      hw_level = clamp_user_to_hw(connector, user_level, user_max);
++      panel->backlight.level = hw_level;
++
++      if (panel->backlight.device)
++              panel->backlight.device->props.brightness =
++                      scale_hw_to_user(connector,
++                                       panel->backlight.level,
++                                       panel->backlight.device->props.max_brightness);
++
++      if (panel->backlight.enabled)
++              intel_panel_actually_set_backlight(conn_state, hw_level);
++
++      mutex_unlock(&dev_priv->backlight_lock);
++}
++
++static void lpt_disable_backlight(const struct drm_connector_state *old_conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      u32 tmp;
++
++      intel_panel_set_pwm_level(old_conn_state, level);
++
++      /*
++       * Although we don't support or enable CPU PWM with LPT/SPT based
++       * systems, it may have been enabled prior to loading the
++       * driver. Disable to avoid warnings on LCPLL disable.
++       *
++       * This needs rework if we need to add support for CPU PWM on PCH split
++       * platforms.
++       */
++      tmp = intel_de_read(dev_priv, BLC_PWM_CPU_CTL2);
++      if (tmp & BLM_PWM_ENABLE) {
++              drm_dbg_kms(&dev_priv->drm,
++                          "cpu backlight was enabled, disabling\n");
++              intel_de_write(dev_priv, BLC_PWM_CPU_CTL2,
++                             tmp & ~BLM_PWM_ENABLE);
++      }
++
++      tmp = intel_de_read(dev_priv, BLC_PWM_PCH_CTL1);
++      intel_de_write(dev_priv, BLC_PWM_PCH_CTL1, tmp & ~BLM_PCH_PWM_ENABLE);
++}
++
++static void pch_disable_backlight(const struct drm_connector_state *old_conn_state, u32 val)
++{
++      struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      u32 tmp;
++
++      intel_panel_set_pwm_level(old_conn_state, val);
++
++      tmp = intel_de_read(dev_priv, BLC_PWM_CPU_CTL2);
++      intel_de_write(dev_priv, BLC_PWM_CPU_CTL2, tmp & ~BLM_PWM_ENABLE);
++
++      tmp = intel_de_read(dev_priv, BLC_PWM_PCH_CTL1);
++      intel_de_write(dev_priv, BLC_PWM_PCH_CTL1, tmp & ~BLM_PCH_PWM_ENABLE);
++}
++
++static void i9xx_disable_backlight(const struct drm_connector_state *old_conn_state, u32 val)
++{
++      intel_panel_set_pwm_level(old_conn_state, val);
++}
++
++static void i965_disable_backlight(const struct drm_connector_state *old_conn_state, u32 val)
++{
++      struct drm_i915_private *dev_priv = to_i915(old_conn_state->connector->dev);
++      u32 tmp;
++
++      intel_panel_set_pwm_level(old_conn_state, val);
++
++      tmp = intel_de_read(dev_priv, BLC_PWM_CTL2);
++      intel_de_write(dev_priv, BLC_PWM_CTL2, tmp & ~BLM_PWM_ENABLE);
++}
++
++static void vlv_disable_backlight(const struct drm_connector_state *old_conn_state, u32 val)
++{
++      struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      enum pipe pipe = to_intel_crtc(old_conn_state->crtc)->pipe;
++      u32 tmp;
++
++      intel_panel_set_pwm_level(old_conn_state, val);
++
++      tmp = intel_de_read(dev_priv, VLV_BLC_PWM_CTL2(pipe));
++      intel_de_write(dev_priv, VLV_BLC_PWM_CTL2(pipe),
++                     tmp & ~BLM_PWM_ENABLE);
++}
++
++static void bxt_disable_backlight(const struct drm_connector_state *old_conn_state, u32 val)
++{
++      struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 tmp;
++
++      intel_panel_set_pwm_level(old_conn_state, val);
++
++      tmp = intel_de_read(dev_priv,
++                          BXT_BLC_PWM_CTL(panel->backlight.controller));
++      intel_de_write(dev_priv, BXT_BLC_PWM_CTL(panel->backlight.controller),
++                     tmp & ~BXT_BLC_PWM_ENABLE);
++
++      if (panel->backlight.controller == 1) {
++              val = intel_de_read(dev_priv, UTIL_PIN_CTL);
++              val &= ~UTIL_PIN_ENABLE;
++              intel_de_write(dev_priv, UTIL_PIN_CTL, val);
++      }
++}
++
++static void cnp_disable_backlight(const struct drm_connector_state *old_conn_state, u32 val)
++{
++      struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 tmp;
++
++      intel_panel_set_pwm_level(old_conn_state, val);
++
++      tmp = intel_de_read(dev_priv,
++                          BXT_BLC_PWM_CTL(panel->backlight.controller));
++      intel_de_write(dev_priv, BXT_BLC_PWM_CTL(panel->backlight.controller),
++                     tmp & ~BXT_BLC_PWM_ENABLE);
++}
++
++static void ext_pwm_disable_backlight(const struct drm_connector_state *old_conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
++      struct intel_panel *panel = &connector->panel;
++
++      panel->backlight.pwm_state.enabled = false;
++      pwm_apply_state(panel->backlight.pwm, &panel->backlight.pwm_state);
++}
++
++void intel_panel_disable_backlight(const struct drm_connector_state *old_conn_state)
++{
++      struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++
++      if (!panel->backlight.present)
++              return;
++
++      /*
++       * Do not disable backlight on the vga_switcheroo path. When switching
++       * away from i915, the other client may depend on i915 to handle the
++       * backlight. This will leave the backlight on unnecessarily when
++       * another client is not activated.
++       */
++      if (dev_priv->drm.switch_power_state == DRM_SWITCH_POWER_CHANGING) {
++              drm_dbg_kms(&dev_priv->drm,
++                          "Skipping backlight disable on vga switch\n");
++              return;
++      }
++
++      mutex_lock(&dev_priv->backlight_lock);
++
++      if (panel->backlight.device)
++              panel->backlight.device->props.power = FB_BLANK_POWERDOWN;
++      panel->backlight.enabled = false;
++      panel->backlight.funcs->disable(old_conn_state, 0);
++
++      mutex_unlock(&dev_priv->backlight_lock);
++}
++
++static void lpt_enable_backlight(const struct intel_crtc_state *crtc_state,
++                               const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 pch_ctl1, pch_ctl2, schicken;
++
++      pch_ctl1 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL1);
++      if (pch_ctl1 & BLM_PCH_PWM_ENABLE) {
++              drm_dbg_kms(&dev_priv->drm, "pch backlight already enabled\n");
++              pch_ctl1 &= ~BLM_PCH_PWM_ENABLE;
++              intel_de_write(dev_priv, BLC_PWM_PCH_CTL1, pch_ctl1);
++      }
++
++      if (HAS_PCH_LPT(dev_priv)) {
++              schicken = intel_de_read(dev_priv, SOUTH_CHICKEN2);
++              if (panel->backlight.alternate_pwm_increment)
++                      schicken |= LPT_PWM_GRANULARITY;
++              else
++                      schicken &= ~LPT_PWM_GRANULARITY;
++              intel_de_write(dev_priv, SOUTH_CHICKEN2, schicken);
++      } else {
++              schicken = intel_de_read(dev_priv, SOUTH_CHICKEN1);
++              if (panel->backlight.alternate_pwm_increment)
++                      schicken |= SPT_PWM_GRANULARITY;
++              else
++                      schicken &= ~SPT_PWM_GRANULARITY;
++              intel_de_write(dev_priv, SOUTH_CHICKEN1, schicken);
++      }
++
++      pch_ctl2 = panel->backlight.pwm_level_max << 16;
++      intel_de_write(dev_priv, BLC_PWM_PCH_CTL2, pch_ctl2);
++
++      pch_ctl1 = 0;
++      if (panel->backlight.active_low_pwm)
++              pch_ctl1 |= BLM_PCH_POLARITY;
++
++      /* After LPT, override is the default. */
++      if (HAS_PCH_LPT(dev_priv))
++              pch_ctl1 |= BLM_PCH_OVERRIDE_ENABLE;
++
++      intel_de_write(dev_priv, BLC_PWM_PCH_CTL1, pch_ctl1);
++      intel_de_posting_read(dev_priv, BLC_PWM_PCH_CTL1);
++      intel_de_write(dev_priv, BLC_PWM_PCH_CTL1,
++                     pch_ctl1 | BLM_PCH_PWM_ENABLE);
++
++      /* This won't stick until the above enable. */
++      intel_panel_set_pwm_level(conn_state, level);
++}
++
++static void pch_enable_backlight(const struct intel_crtc_state *crtc_state,
++                               const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
++      u32 cpu_ctl2, pch_ctl1, pch_ctl2;
++
++      cpu_ctl2 = intel_de_read(dev_priv, BLC_PWM_CPU_CTL2);
++      if (cpu_ctl2 & BLM_PWM_ENABLE) {
++              drm_dbg_kms(&dev_priv->drm, "cpu backlight already enabled\n");
++              cpu_ctl2 &= ~BLM_PWM_ENABLE;
++              intel_de_write(dev_priv, BLC_PWM_CPU_CTL2, cpu_ctl2);
++      }
++
++      pch_ctl1 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL1);
++      if (pch_ctl1 & BLM_PCH_PWM_ENABLE) {
++              drm_dbg_kms(&dev_priv->drm, "pch backlight already enabled\n");
++              pch_ctl1 &= ~BLM_PCH_PWM_ENABLE;
++              intel_de_write(dev_priv, BLC_PWM_PCH_CTL1, pch_ctl1);
++      }
++
++      if (cpu_transcoder == TRANSCODER_EDP)
++              cpu_ctl2 = BLM_TRANSCODER_EDP;
++      else
++              cpu_ctl2 = BLM_PIPE(cpu_transcoder);
++      intel_de_write(dev_priv, BLC_PWM_CPU_CTL2, cpu_ctl2);
++      intel_de_posting_read(dev_priv, BLC_PWM_CPU_CTL2);
++      intel_de_write(dev_priv, BLC_PWM_CPU_CTL2, cpu_ctl2 | BLM_PWM_ENABLE);
++
++      /* This won't stick until the above enable. */
++      intel_panel_set_pwm_level(conn_state, level);
++
++      pch_ctl2 = panel->backlight.pwm_level_max << 16;
++      intel_de_write(dev_priv, BLC_PWM_PCH_CTL2, pch_ctl2);
++
++      pch_ctl1 = 0;
++      if (panel->backlight.active_low_pwm)
++              pch_ctl1 |= BLM_PCH_POLARITY;
++
++      intel_de_write(dev_priv, BLC_PWM_PCH_CTL1, pch_ctl1);
++      intel_de_posting_read(dev_priv, BLC_PWM_PCH_CTL1);
++      intel_de_write(dev_priv, BLC_PWM_PCH_CTL1,
++                     pch_ctl1 | BLM_PCH_PWM_ENABLE);
++}
++
++static void i9xx_enable_backlight(const struct intel_crtc_state *crtc_state,
++                                const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 ctl, freq;
++
++      ctl = intel_de_read(dev_priv, BLC_PWM_CTL);
++      if (ctl & BACKLIGHT_DUTY_CYCLE_MASK_PNV) {
++              drm_dbg_kms(&dev_priv->drm, "backlight already enabled\n");
++              intel_de_write(dev_priv, BLC_PWM_CTL, 0);
++      }
++
++      freq = panel->backlight.pwm_level_max;
++      if (panel->backlight.combination_mode)
++              freq /= 0xff;
++
++      ctl = freq << 17;
++      if (panel->backlight.combination_mode)
++              ctl |= BLM_LEGACY_MODE;
++      if (IS_PINEVIEW(dev_priv) && panel->backlight.active_low_pwm)
++              ctl |= BLM_POLARITY_PNV;
++
++      intel_de_write(dev_priv, BLC_PWM_CTL, ctl);
++      intel_de_posting_read(dev_priv, BLC_PWM_CTL);
++
++      /* XXX: combine this into above write? */
++      intel_panel_set_pwm_level(conn_state, level);
++
++      /*
++       * Needed to enable backlight on some 855gm models. BLC_HIST_CTL is
++       * 855gm only, but checking for gen2 is safe, as 855gm is the only gen2
++       * that has backlight.
++       */
++      if (DISPLAY_VER(dev_priv) == 2)
++              intel_de_write(dev_priv, BLC_HIST_CTL, BLM_HISTOGRAM_ENABLE);
++}
++
++static void i965_enable_backlight(const struct intel_crtc_state *crtc_state,
++                                const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      enum pipe pipe = to_intel_crtc(conn_state->crtc)->pipe;
++      u32 ctl, ctl2, freq;
++
++      ctl2 = intel_de_read(dev_priv, BLC_PWM_CTL2);
++      if (ctl2 & BLM_PWM_ENABLE) {
++              drm_dbg_kms(&dev_priv->drm, "backlight already enabled\n");
++              ctl2 &= ~BLM_PWM_ENABLE;
++              intel_de_write(dev_priv, BLC_PWM_CTL2, ctl2);
++      }
++
++      freq = panel->backlight.pwm_level_max;
++      if (panel->backlight.combination_mode)
++              freq /= 0xff;
++
++      ctl = freq << 16;
++      intel_de_write(dev_priv, BLC_PWM_CTL, ctl);
++
++      ctl2 = BLM_PIPE(pipe);
++      if (panel->backlight.combination_mode)
++              ctl2 |= BLM_COMBINATION_MODE;
++      if (panel->backlight.active_low_pwm)
++              ctl2 |= BLM_POLARITY_I965;
++      intel_de_write(dev_priv, BLC_PWM_CTL2, ctl2);
++      intel_de_posting_read(dev_priv, BLC_PWM_CTL2);
++      intel_de_write(dev_priv, BLC_PWM_CTL2, ctl2 | BLM_PWM_ENABLE);
++
++      intel_panel_set_pwm_level(conn_state, level);
++}
++
++static void vlv_enable_backlight(const struct intel_crtc_state *crtc_state,
++                               const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe;
++      u32 ctl, ctl2;
++
++      ctl2 = intel_de_read(dev_priv, VLV_BLC_PWM_CTL2(pipe));
++      if (ctl2 & BLM_PWM_ENABLE) {
++              drm_dbg_kms(&dev_priv->drm, "backlight already enabled\n");
++              ctl2 &= ~BLM_PWM_ENABLE;
++              intel_de_write(dev_priv, VLV_BLC_PWM_CTL2(pipe), ctl2);
++      }
++
++      ctl = panel->backlight.pwm_level_max << 16;
++      intel_de_write(dev_priv, VLV_BLC_PWM_CTL(pipe), ctl);
++
++      /* XXX: combine this into above write? */
++      intel_panel_set_pwm_level(conn_state, level);
++
++      ctl2 = 0;
++      if (panel->backlight.active_low_pwm)
++              ctl2 |= BLM_POLARITY_I965;
++      intel_de_write(dev_priv, VLV_BLC_PWM_CTL2(pipe), ctl2);
++      intel_de_posting_read(dev_priv, VLV_BLC_PWM_CTL2(pipe));
++      intel_de_write(dev_priv, VLV_BLC_PWM_CTL2(pipe),
++                     ctl2 | BLM_PWM_ENABLE);
++}
++
++static void bxt_enable_backlight(const struct intel_crtc_state *crtc_state,
++                               const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe;
++      u32 pwm_ctl, val;
++
++      /* Controller 1 uses the utility pin. */
++      if (panel->backlight.controller == 1) {
++              val = intel_de_read(dev_priv, UTIL_PIN_CTL);
++              if (val & UTIL_PIN_ENABLE) {
++                      drm_dbg_kms(&dev_priv->drm,
++                                  "util pin already enabled\n");
++                      val &= ~UTIL_PIN_ENABLE;
++                      intel_de_write(dev_priv, UTIL_PIN_CTL, val);
++              }
++
++              val = 0;
++              if (panel->backlight.util_pin_active_low)
++                      val |= UTIL_PIN_POLARITY;
++              intel_de_write(dev_priv, UTIL_PIN_CTL,
++                             val | UTIL_PIN_PIPE(pipe) | UTIL_PIN_MODE_PWM | UTIL_PIN_ENABLE);
++      }
++
++      pwm_ctl = intel_de_read(dev_priv,
++                              BXT_BLC_PWM_CTL(panel->backlight.controller));
++      if (pwm_ctl & BXT_BLC_PWM_ENABLE) {
++              drm_dbg_kms(&dev_priv->drm, "backlight already enabled\n");
++              pwm_ctl &= ~BXT_BLC_PWM_ENABLE;
++              intel_de_write(dev_priv,
++                             BXT_BLC_PWM_CTL(panel->backlight.controller),
++                             pwm_ctl);
++      }
++
++      intel_de_write(dev_priv,
++                     BXT_BLC_PWM_FREQ(panel->backlight.controller),
++                     panel->backlight.pwm_level_max);
++
++      intel_panel_set_pwm_level(conn_state, level);
++
++      pwm_ctl = 0;
++      if (panel->backlight.active_low_pwm)
++              pwm_ctl |= BXT_BLC_PWM_POLARITY;
++
++      intel_de_write(dev_priv, BXT_BLC_PWM_CTL(panel->backlight.controller),
++                     pwm_ctl);
++      intel_de_posting_read(dev_priv,
++                            BXT_BLC_PWM_CTL(panel->backlight.controller));
++      intel_de_write(dev_priv, BXT_BLC_PWM_CTL(panel->backlight.controller),
++                     pwm_ctl | BXT_BLC_PWM_ENABLE);
++}
++
++static void cnp_enable_backlight(const struct intel_crtc_state *crtc_state,
++                               const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 pwm_ctl;
++
++      pwm_ctl = intel_de_read(dev_priv,
++                              BXT_BLC_PWM_CTL(panel->backlight.controller));
++      if (pwm_ctl & BXT_BLC_PWM_ENABLE) {
++              drm_dbg_kms(&dev_priv->drm, "backlight already enabled\n");
++              pwm_ctl &= ~BXT_BLC_PWM_ENABLE;
++              intel_de_write(dev_priv,
++                             BXT_BLC_PWM_CTL(panel->backlight.controller),
++                             pwm_ctl);
++      }
++
++      intel_de_write(dev_priv,
++                     BXT_BLC_PWM_FREQ(panel->backlight.controller),
++                     panel->backlight.pwm_level_max);
++
++      intel_panel_set_pwm_level(conn_state, level);
++
++      pwm_ctl = 0;
++      if (panel->backlight.active_low_pwm)
++              pwm_ctl |= BXT_BLC_PWM_POLARITY;
++
++      intel_de_write(dev_priv, BXT_BLC_PWM_CTL(panel->backlight.controller),
++                     pwm_ctl);
++      intel_de_posting_read(dev_priv,
++                            BXT_BLC_PWM_CTL(panel->backlight.controller));
++      intel_de_write(dev_priv, BXT_BLC_PWM_CTL(panel->backlight.controller),
++                     pwm_ctl | BXT_BLC_PWM_ENABLE);
++}
++
++static void ext_pwm_enable_backlight(const struct intel_crtc_state *crtc_state,
++                                   const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct intel_panel *panel = &connector->panel;
++
++      pwm_set_relative_duty_cycle(&panel->backlight.pwm_state, level, 100);
++      panel->backlight.pwm_state.enabled = true;
++      pwm_apply_state(panel->backlight.pwm, &panel->backlight.pwm_state);
++}
++
++static void __intel_panel_enable_backlight(const struct intel_crtc_state *crtc_state,
++                                         const struct drm_connector_state *conn_state)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct intel_panel *panel = &connector->panel;
++
++      WARN_ON(panel->backlight.max == 0);
++
++      if (panel->backlight.level <= panel->backlight.min) {
++              panel->backlight.level = panel->backlight.max;
++              if (panel->backlight.device)
++                      panel->backlight.device->props.brightness =
++                              scale_hw_to_user(connector,
++                                               panel->backlight.level,
++                                               panel->backlight.device->props.max_brightness);
++      }
++
++      panel->backlight.funcs->enable(crtc_state, conn_state, panel->backlight.level);
++      panel->backlight.enabled = true;
++      if (panel->backlight.device)
++              panel->backlight.device->props.power = FB_BLANK_UNBLANK;
++}
++
++void intel_panel_enable_backlight(const struct intel_crtc_state *crtc_state,
++                                const struct drm_connector_state *conn_state)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe;
++
++      if (!panel->backlight.present)
++              return;
++
++      drm_dbg_kms(&dev_priv->drm, "pipe %c\n", pipe_name(pipe));
++
++      mutex_lock(&dev_priv->backlight_lock);
++
++      __intel_panel_enable_backlight(crtc_state, conn_state);
++
++      mutex_unlock(&dev_priv->backlight_lock);
++}
++
++#if IS_ENABLED(CONFIG_BACKLIGHT_CLASS_DEVICE)
++static u32 intel_panel_get_backlight(struct intel_connector *connector)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 val = 0;
++
++      mutex_lock(&dev_priv->backlight_lock);
++
++      if (panel->backlight.enabled)
++              val = panel->backlight.funcs->get(connector, intel_connector_get_pipe(connector));
++
++      mutex_unlock(&dev_priv->backlight_lock);
++
++      drm_dbg_kms(&dev_priv->drm, "get backlight PWM = %d\n", val);
++      return val;
++}
++
++/* Scale user_level in range [0..user_max] to [hw_min..hw_max]. */
++static u32 scale_user_to_hw(struct intel_connector *connector,
++                          u32 user_level, u32 user_max)
++{
++      struct intel_panel *panel = &connector->panel;
++
++      return scale(user_level, 0, user_max,
++                   panel->backlight.min, panel->backlight.max);
++}
++
++/* set backlight brightness to level in range [0..max], scaling wrt hw min */
++static void intel_panel_set_backlight(const struct drm_connector_state *conn_state,
++                                    u32 user_level, u32 user_max)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 hw_level;
++
++      if (!panel->backlight.present)
++              return;
++
++      mutex_lock(&dev_priv->backlight_lock);
++
++      drm_WARN_ON(&dev_priv->drm, panel->backlight.max == 0);
++
++      hw_level = scale_user_to_hw(connector, user_level, user_max);
++      panel->backlight.level = hw_level;
++
++      if (panel->backlight.enabled)
++              intel_panel_actually_set_backlight(conn_state, hw_level);
++
++      mutex_unlock(&dev_priv->backlight_lock);
++}
++
++static int intel_backlight_device_update_status(struct backlight_device *bd)
++{
++      struct intel_connector *connector = bl_get_data(bd);
++      struct intel_panel *panel = &connector->panel;
++      struct drm_device *dev = connector->base.dev;
++
++      drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
++      DRM_DEBUG_KMS("updating intel_backlight, brightness=%d/%d\n",
++                    bd->props.brightness, bd->props.max_brightness);
++      intel_panel_set_backlight(connector->base.state, bd->props.brightness,
++                                bd->props.max_brightness);
++
++      /*
++       * Allow flipping bl_power as a sub-state of enabled. Sadly the
++       * backlight class device does not make it easy to differentiate
++       * between callbacks for brightness and bl_power, so our backlight_power
++       * callback needs to take this into account.
++       */
++      if (panel->backlight.enabled) {
++              if (panel->backlight.power) {
++                      bool enable = bd->props.power == FB_BLANK_UNBLANK &&
++                              bd->props.brightness != 0;
++                      panel->backlight.power(connector, enable);
++              }
++      } else {
++              bd->props.power = FB_BLANK_POWERDOWN;
++      }
++
++      drm_modeset_unlock(&dev->mode_config.connection_mutex);
++      return 0;
++}
++
++static int intel_backlight_device_get_brightness(struct backlight_device *bd)
++{
++      struct intel_connector *connector = bl_get_data(bd);
++      struct drm_device *dev = connector->base.dev;
++      struct drm_i915_private *dev_priv = to_i915(dev);
++      intel_wakeref_t wakeref;
++      int ret = 0;
++
++      with_intel_runtime_pm(&dev_priv->runtime_pm, wakeref) {
++              u32 hw_level;
++
++              drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
++
++              hw_level = intel_panel_get_backlight(connector);
++              ret = scale_hw_to_user(connector,
++                                     hw_level, bd->props.max_brightness);
++
++              drm_modeset_unlock(&dev->mode_config.connection_mutex);
++      }
++
++      return ret;
++}
++
++static const struct backlight_ops intel_backlight_device_ops = {
++      .update_status = intel_backlight_device_update_status,
++      .get_brightness = intel_backlight_device_get_brightness,
++};
++
++int intel_backlight_device_register(struct intel_connector *connector)
++{
++      struct drm_i915_private *i915 = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      struct backlight_properties props;
++      struct backlight_device *bd;
++      const char *name;
++      int ret = 0;
++
++      if (WARN_ON(panel->backlight.device))
++              return -ENODEV;
++
++      if (!panel->backlight.present)
++              return 0;
++
++      WARN_ON(panel->backlight.max == 0);
++
++      memset(&props, 0, sizeof(props));
++      props.type = BACKLIGHT_RAW;
++
++      /*
++       * Note: Everything should work even if the backlight device max
++       * presented to the userspace is arbitrarily chosen.
++       */
++      props.max_brightness = panel->backlight.max;
++      props.brightness = scale_hw_to_user(connector,
++                                          panel->backlight.level,
++                                          props.max_brightness);
++
++      if (panel->backlight.enabled)
++              props.power = FB_BLANK_UNBLANK;
++      else
++              props.power = FB_BLANK_POWERDOWN;
++
++      name = kstrdup("intel_backlight", GFP_KERNEL);
++      if (!name)
++              return -ENOMEM;
++
++      bd = backlight_device_register(name, connector->base.kdev, connector,
++                                     &intel_backlight_device_ops, &props);
++
++      /*
++       * Using the same name independent of the drm device or connector
++       * prevents registration of multiple backlight devices in the
++       * driver. However, we need to use the default name for backward
++       * compatibility. Use unique names for subsequent backlight devices as a
++       * fallback when the default name already exists.
++       */
++      if (IS_ERR(bd) && PTR_ERR(bd) == -EEXIST) {
++              kfree(name);
++              name = kasprintf(GFP_KERNEL, "card%d-%s-backlight",
++                               i915->drm.primary->index, connector->base.name);
++              if (!name)
++                      return -ENOMEM;
++
++              bd = backlight_device_register(name, connector->base.kdev, connector,
++                                             &intel_backlight_device_ops, &props);
++      }
++
++      if (IS_ERR(bd)) {
++              drm_err(&i915->drm,
++                      "[CONNECTOR:%d:%s] backlight device %s register failed: %ld\n",
++                      connector->base.base.id, connector->base.name, name, PTR_ERR(bd));
++              ret = PTR_ERR(bd);
++              goto out;
++      }
++
++      panel->backlight.device = bd;
++
++      drm_dbg_kms(&i915->drm,
++                  "[CONNECTOR:%d:%s] backlight device %s registered\n",
++                  connector->base.base.id, connector->base.name, name);
++
++out:
++      kfree(name);
++
++      return ret;
++}
++
++void intel_backlight_device_unregister(struct intel_connector *connector)
++{
++      struct intel_panel *panel = &connector->panel;
++
++      if (panel->backlight.device) {
++              backlight_device_unregister(panel->backlight.device);
++              panel->backlight.device = NULL;
++      }
++}
++#endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
++
++/*
++ * CNP: PWM clock frequency is 19.2 MHz or 24 MHz.
++ *      PWM increment = 1
++ */
++static u32 cnp_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++
++      return DIV_ROUND_CLOSEST(KHz(RUNTIME_INFO(dev_priv)->rawclk_freq),
++                               pwm_freq_hz);
++}
++
++/*
++ * BXT: PWM clock frequency = 19.2 MHz.
++ */
++static u32 bxt_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
++{
++      return DIV_ROUND_CLOSEST(KHz(19200), pwm_freq_hz);
++}
++
++/*
++ * SPT: This value represents the period of the PWM stream in clock periods
++ * multiplied by 16 (default increment) or 128 (alternate increment selected in
++ * SCHICKEN_1 bit 0). PWM clock is 24 MHz.
++ */
++static u32 spt_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
++{
++      struct intel_panel *panel = &connector->panel;
++      u32 mul;
++
++      if (panel->backlight.alternate_pwm_increment)
++              mul = 128;
++      else
++              mul = 16;
++
++      return DIV_ROUND_CLOSEST(MHz(24), pwm_freq_hz * mul);
++}
++
++/*
++ * LPT: This value represents the period of the PWM stream in clock periods
++ * multiplied by 128 (default increment) or 16 (alternate increment, selected in
++ * LPT SOUTH_CHICKEN2 register bit 5).
++ */
++static u32 lpt_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 mul, clock;
++
++      if (panel->backlight.alternate_pwm_increment)
++              mul = 16;
++      else
++              mul = 128;
++
++      if (HAS_PCH_LPT_H(dev_priv))
++              clock = MHz(135); /* LPT:H */
++      else
++              clock = MHz(24); /* LPT:LP */
++
++      return DIV_ROUND_CLOSEST(clock, pwm_freq_hz * mul);
++}
++
++/*
++ * ILK/SNB/IVB: This value represents the period of the PWM stream in PCH
++ * display raw clocks multiplied by 128.
++ */
++static u32 pch_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++
++      return DIV_ROUND_CLOSEST(KHz(RUNTIME_INFO(dev_priv)->rawclk_freq),
++                               pwm_freq_hz * 128);
++}
++
++/*
++ * Gen2: This field determines the number of time base events (display core
++ * clock frequency/32) in total for a complete cycle of modulated backlight
++ * control.
++ *
++ * Gen3: A time base event equals the display core clock ([DevPNV] HRAW clock)
++ * divided by 32.
++ */
++static u32 i9xx_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      int clock;
++
++      if (IS_PINEVIEW(dev_priv))
++              clock = KHz(RUNTIME_INFO(dev_priv)->rawclk_freq);
++      else
++              clock = KHz(dev_priv->cdclk.hw.cdclk);
++
++      return DIV_ROUND_CLOSEST(clock, pwm_freq_hz * 32);
++}
++
++/*
++ * Gen4: This value represents the period of the PWM stream in display core
++ * clocks ([DevCTG] HRAW clocks) multiplied by 128.
++ *
++ */
++static u32 i965_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      int clock;
++
++      if (IS_G4X(dev_priv))
++              clock = KHz(RUNTIME_INFO(dev_priv)->rawclk_freq);
++      else
++              clock = KHz(dev_priv->cdclk.hw.cdclk);
++
++      return DIV_ROUND_CLOSEST(clock, pwm_freq_hz * 128);
++}
++
++/*
++ * VLV: This value represents the period of the PWM stream in display core
++ * clocks ([DevCTG] 200MHz HRAW clocks) multiplied by 128 or 25MHz S0IX clocks
++ * multiplied by 16. CHV uses a 19.2MHz S0IX clock.
++ */
++static u32 vlv_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      int mul, clock;
++
++      if ((intel_de_read(dev_priv, CBR1_VLV) & CBR_PWM_CLOCK_MUX_SELECT) == 0) {
++              if (IS_CHERRYVIEW(dev_priv))
++                      clock = KHz(19200);
++              else
++                      clock = MHz(25);
++              mul = 16;
++      } else {
++              clock = KHz(RUNTIME_INFO(dev_priv)->rawclk_freq);
++              mul = 128;
++      }
++
++      return DIV_ROUND_CLOSEST(clock, pwm_freq_hz * mul);
++}
++
++static u16 get_vbt_pwm_freq(struct drm_i915_private *dev_priv)
++{
++      u16 pwm_freq_hz = dev_priv->vbt.backlight.pwm_freq_hz;
++
++      if (pwm_freq_hz) {
++              drm_dbg_kms(&dev_priv->drm,
++                          "VBT defined backlight frequency %u Hz\n",
++                          pwm_freq_hz);
++      } else {
++              pwm_freq_hz = 200;
++              drm_dbg_kms(&dev_priv->drm,
++                          "default backlight frequency %u Hz\n",
++                          pwm_freq_hz);
++      }
++
++      return pwm_freq_hz;
++}
++
++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);
++      u32 pwm;
++
++      if (!panel->backlight.pwm_funcs->hz_to_pwm) {
++              drm_dbg_kms(&dev_priv->drm,
++                          "backlight frequency conversion not supported\n");
++              return 0;
++      }
++
++      pwm = panel->backlight.pwm_funcs->hz_to_pwm(connector, pwm_freq_hz);
++      if (!pwm) {
++              drm_dbg_kms(&dev_priv->drm,
++                          "backlight frequency conversion failed\n");
++              return 0;
++      }
++
++      return pwm;
++}
++
++/*
++ * Note: The setup hooks can't assume pipe is set!
++ */
++static u32 get_backlight_min_vbt(struct intel_connector *connector)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      int min;
++
++      drm_WARN_ON(&dev_priv->drm, panel->backlight.pwm_level_max == 0);
++
++      /*
++       * XXX: If the vbt value is 255, it makes min equal to max, which leads
++       * to problems. There are such machines out there. Either our
++       * interpretation is wrong or the vbt has bogus data. Or both. Safeguard
++       * 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) {
++              drm_dbg_kms(&dev_priv->drm,
++                          "clamping VBT min backlight %d/255 to %d/255\n",
++                          dev_priv->vbt.backlight.min_brightness, min);
++      }
++
++      /* vbt value is a coefficient in range [0..255] */
++      return scale(min, 0, 255, 0, panel->backlight.pwm_level_max);
++}
++
++static int lpt_setup_backlight(struct intel_connector *connector, enum pipe unused)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 cpu_ctl2, pch_ctl1, pch_ctl2, val;
++      bool alt, cpu_mode;
++
++      if (HAS_PCH_LPT(dev_priv))
++              alt = intel_de_read(dev_priv, SOUTH_CHICKEN2) & LPT_PWM_GRANULARITY;
++      else
++              alt = intel_de_read(dev_priv, SOUTH_CHICKEN1) & SPT_PWM_GRANULARITY;
++      panel->backlight.alternate_pwm_increment = alt;
++
++      pch_ctl1 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL1);
++      panel->backlight.active_low_pwm = pch_ctl1 & BLM_PCH_POLARITY;
++
++      pch_ctl2 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL2);
++      panel->backlight.pwm_level_max = pch_ctl2 >> 16;
++
++      cpu_ctl2 = intel_de_read(dev_priv, BLC_PWM_CPU_CTL2);
++
++      if (!panel->backlight.pwm_level_max)
++              panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
++
++      if (!panel->backlight.pwm_level_max)
++              return -ENODEV;
++
++      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
++
++      panel->backlight.pwm_enabled = pch_ctl1 & BLM_PCH_PWM_ENABLE;
++
++      cpu_mode = panel->backlight.pwm_enabled && HAS_PCH_LPT(dev_priv) &&
++                 !(pch_ctl1 & BLM_PCH_OVERRIDE_ENABLE) &&
++                 (cpu_ctl2 & BLM_PWM_ENABLE);
++
++      if (cpu_mode) {
++              val = pch_get_backlight(connector, unused);
++
++              drm_dbg_kms(&dev_priv->drm,
++                          "CPU backlight register was enabled, switching to PCH override\n");
++
++              /* Write converted CPU PWM value to PCH override register */
++              lpt_set_backlight(connector->base.state, val);
++              intel_de_write(dev_priv, BLC_PWM_PCH_CTL1,
++                             pch_ctl1 | BLM_PCH_OVERRIDE_ENABLE);
++
++              intel_de_write(dev_priv, BLC_PWM_CPU_CTL2,
++                             cpu_ctl2 & ~BLM_PWM_ENABLE);
++      }
++
++      return 0;
++}
++
++static int pch_setup_backlight(struct intel_connector *connector, enum pipe unused)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 cpu_ctl2, pch_ctl1, pch_ctl2;
++
++      pch_ctl1 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL1);
++      panel->backlight.active_low_pwm = pch_ctl1 & BLM_PCH_POLARITY;
++
++      pch_ctl2 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL2);
++      panel->backlight.pwm_level_max = pch_ctl2 >> 16;
++
++      if (!panel->backlight.pwm_level_max)
++              panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
++
++      if (!panel->backlight.pwm_level_max)
++              return -ENODEV;
++
++      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
++
++      cpu_ctl2 = intel_de_read(dev_priv, BLC_PWM_CPU_CTL2);
++      panel->backlight.pwm_enabled = (cpu_ctl2 & BLM_PWM_ENABLE) &&
++              (pch_ctl1 & BLM_PCH_PWM_ENABLE);
++
++      return 0;
++}
++
++static int i9xx_setup_backlight(struct intel_connector *connector, enum pipe unused)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 ctl, val;
++
++      ctl = intel_de_read(dev_priv, BLC_PWM_CTL);
++
++      if (DISPLAY_VER(dev_priv) == 2 || IS_I915GM(dev_priv) || IS_I945GM(dev_priv))
++              panel->backlight.combination_mode = ctl & BLM_LEGACY_MODE;
++
++      if (IS_PINEVIEW(dev_priv))
++              panel->backlight.active_low_pwm = ctl & BLM_POLARITY_PNV;
++
++      panel->backlight.pwm_level_max = ctl >> 17;
++
++      if (!panel->backlight.pwm_level_max) {
++              panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
++              panel->backlight.pwm_level_max >>= 1;
++      }
++
++      if (!panel->backlight.pwm_level_max)
++              return -ENODEV;
++
++      if (panel->backlight.combination_mode)
++              panel->backlight.pwm_level_max *= 0xff;
++
++      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
++
++      val = i9xx_get_backlight(connector, unused);
++      val = intel_panel_invert_pwm_level(connector, val);
++      val = clamp(val, panel->backlight.pwm_level_min, panel->backlight.pwm_level_max);
++
++      panel->backlight.pwm_enabled = val != 0;
++
++      return 0;
++}
++
++static int i965_setup_backlight(struct intel_connector *connector, enum pipe unused)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 ctl, ctl2;
++
++      ctl2 = intel_de_read(dev_priv, BLC_PWM_CTL2);
++      panel->backlight.combination_mode = ctl2 & BLM_COMBINATION_MODE;
++      panel->backlight.active_low_pwm = ctl2 & BLM_POLARITY_I965;
++
++      ctl = intel_de_read(dev_priv, BLC_PWM_CTL);
++      panel->backlight.pwm_level_max = ctl >> 16;
++
++      if (!panel->backlight.pwm_level_max)
++              panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
++
++      if (!panel->backlight.pwm_level_max)
++              return -ENODEV;
++
++      if (panel->backlight.combination_mode)
++              panel->backlight.pwm_level_max *= 0xff;
++
++      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
++
++      panel->backlight.pwm_enabled = ctl2 & BLM_PWM_ENABLE;
++
++      return 0;
++}
++
++static int vlv_setup_backlight(struct intel_connector *connector, enum pipe pipe)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 ctl, ctl2;
++
++      if (drm_WARN_ON(&dev_priv->drm, pipe != PIPE_A && pipe != PIPE_B))
++              return -ENODEV;
++
++      ctl2 = intel_de_read(dev_priv, VLV_BLC_PWM_CTL2(pipe));
++      panel->backlight.active_low_pwm = ctl2 & BLM_POLARITY_I965;
++
++      ctl = intel_de_read(dev_priv, VLV_BLC_PWM_CTL(pipe));
++      panel->backlight.pwm_level_max = ctl >> 16;
++
++      if (!panel->backlight.pwm_level_max)
++              panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
++
++      if (!panel->backlight.pwm_level_max)
++              return -ENODEV;
++
++      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
++
++      panel->backlight.pwm_enabled = ctl2 & BLM_PWM_ENABLE;
++
++      return 0;
++}
++
++static int
++bxt_setup_backlight(struct intel_connector *connector, enum pipe unused)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 pwm_ctl, val;
++
++      panel->backlight.controller = dev_priv->vbt.backlight.controller;
++
++      pwm_ctl = intel_de_read(dev_priv,
++                              BXT_BLC_PWM_CTL(panel->backlight.controller));
++
++      /* Controller 1 uses the utility pin. */
++      if (panel->backlight.controller == 1) {
++              val = intel_de_read(dev_priv, UTIL_PIN_CTL);
++              panel->backlight.util_pin_active_low =
++                                      val & UTIL_PIN_POLARITY;
++      }
++
++      panel->backlight.active_low_pwm = pwm_ctl & BXT_BLC_PWM_POLARITY;
++      panel->backlight.pwm_level_max =
++              intel_de_read(dev_priv, BXT_BLC_PWM_FREQ(panel->backlight.controller));
++
++      if (!panel->backlight.pwm_level_max)
++              panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
++
++      if (!panel->backlight.pwm_level_max)
++              return -ENODEV;
++
++      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
++
++      panel->backlight.pwm_enabled = pwm_ctl & BXT_BLC_PWM_ENABLE;
++
++      return 0;
++}
++
++static int
++cnp_setup_backlight(struct intel_connector *connector, enum pipe unused)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++      u32 pwm_ctl;
++
++      /*
++       * CNP has the BXT implementation of backlight, but with only one
++       * controller. TODO: ICP has multiple controllers but we only use
++       * controller 0 for now.
++       */
++      panel->backlight.controller = 0;
++
++      pwm_ctl = intel_de_read(dev_priv,
++                              BXT_BLC_PWM_CTL(panel->backlight.controller));
++
++      panel->backlight.active_low_pwm = pwm_ctl & BXT_BLC_PWM_POLARITY;
++      panel->backlight.pwm_level_max =
++              intel_de_read(dev_priv, BXT_BLC_PWM_FREQ(panel->backlight.controller));
++
++      if (!panel->backlight.pwm_level_max)
++              panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
++
++      if (!panel->backlight.pwm_level_max)
++              return -ENODEV;
++
++      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
++
++      panel->backlight.pwm_enabled = pwm_ctl & BXT_BLC_PWM_ENABLE;
++
++      return 0;
++}
++
++static int ext_pwm_setup_backlight(struct intel_connector *connector,
++                                 enum pipe pipe)
++{
++      struct drm_device *dev = connector->base.dev;
++      struct drm_i915_private *dev_priv = to_i915(dev);
++      struct intel_panel *panel = &connector->panel;
++      const char *desc;
++      u32 level;
++
++      /* Get the right PWM chip for DSI backlight according to VBT */
++      if (dev_priv->vbt.dsi.config->pwm_blc == PPS_BLC_PMIC) {
++              panel->backlight.pwm = pwm_get(dev->dev, "pwm_pmic_backlight");
++              desc = "PMIC";
++      } else {
++              panel->backlight.pwm = pwm_get(dev->dev, "pwm_soc_backlight");
++              desc = "SoC";
++      }
++
++      if (IS_ERR(panel->backlight.pwm)) {
++              drm_err(&dev_priv->drm, "Failed to get the %s PWM chip\n",
++                      desc);
++              panel->backlight.pwm = NULL;
++              return -ENODEV;
++      }
++
++      panel->backlight.pwm_level_max = 100; /* 100% */
++      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
++
++      if (pwm_is_enabled(panel->backlight.pwm)) {
++              /* PWM is already enabled, use existing settings */
++              pwm_get_state(panel->backlight.pwm, &panel->backlight.pwm_state);
++
++              level = pwm_get_relative_duty_cycle(&panel->backlight.pwm_state,
++                                                  100);
++              level = intel_panel_invert_pwm_level(connector, level);
++              panel->backlight.pwm_enabled = true;
++
++              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);
++      } 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);
++      }
++
++      drm_info(&dev_priv->drm, "Using %s PWM for LCD backlight control\n",
++               desc);
++      return 0;
++}
++
++static void intel_pwm_set_backlight(const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct intel_panel *panel = &connector->panel;
++
++      panel->backlight.pwm_funcs->set(conn_state,
++                                     intel_panel_invert_pwm_level(connector, level));
++}
++
++static u32 intel_pwm_get_backlight(struct intel_connector *connector, enum pipe pipe)
++{
++      struct intel_panel *panel = &connector->panel;
++
++      return intel_panel_invert_pwm_level(connector,
++                                          panel->backlight.pwm_funcs->get(connector, pipe));
++}
++
++static void intel_pwm_enable_backlight(const struct intel_crtc_state *crtc_state,
++                                     const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct intel_panel *panel = &connector->panel;
++
++      panel->backlight.pwm_funcs->enable(crtc_state, conn_state,
++                                         intel_panel_invert_pwm_level(connector, level));
++}
++
++static void intel_pwm_disable_backlight(const struct drm_connector_state *conn_state, u32 level)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct intel_panel *panel = &connector->panel;
++
++      panel->backlight.pwm_funcs->disable(conn_state,
++                                          intel_panel_invert_pwm_level(connector, level));
++}
++
++static int intel_pwm_setup_backlight(struct intel_connector *connector, enum pipe pipe)
++{
++      struct intel_panel *panel = &connector->panel;
++      int ret = panel->backlight.pwm_funcs->setup(connector, pipe);
++
++      if (ret < 0)
++              return ret;
++
++      panel->backlight.min = panel->backlight.pwm_level_min;
++      panel->backlight.max = panel->backlight.pwm_level_max;
++      panel->backlight.level = intel_pwm_get_backlight(connector, pipe);
++      panel->backlight.enabled = panel->backlight.pwm_enabled;
++
++      return 0;
++}
++
++void intel_panel_update_backlight(struct intel_atomic_state *state,
++                                struct intel_encoder *encoder,
++                                const struct intel_crtc_state *crtc_state,
++                                const struct drm_connector_state *conn_state)
++{
++      struct intel_connector *connector = to_intel_connector(conn_state->connector);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++      struct intel_panel *panel = &connector->panel;
++
++      if (!panel->backlight.present)
++              return;
++
++      mutex_lock(&dev_priv->backlight_lock);
++      if (!panel->backlight.enabled)
++              __intel_panel_enable_backlight(crtc_state, conn_state);
++
++      mutex_unlock(&dev_priv->backlight_lock);
++}
++
++int intel_panel_setup_backlight(struct drm_connector *connector, enum pipe pipe)
++{
++      struct drm_i915_private *dev_priv = to_i915(connector->dev);
++      struct intel_connector *intel_connector = to_intel_connector(connector);
++      struct intel_panel *panel = &intel_connector->panel;
++      int ret;
++
++      if (!dev_priv->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");
++              } else {
++                      drm_dbg_kms(&dev_priv->drm,
++                                  "no backlight present per VBT\n");
++                      return 0;
++              }
++      }
++
++      /* ensure intel_panel has been initialized first */
++      if (drm_WARN_ON(&dev_priv->drm, !panel->backlight.funcs))
++              return -ENODEV;
++
++      /* set level and max in panel struct */
++      mutex_lock(&dev_priv->backlight_lock);
++      ret = panel->backlight.funcs->setup(intel_connector, pipe);
++      mutex_unlock(&dev_priv->backlight_lock);
++
++      if (ret) {
++              drm_dbg_kms(&dev_priv->drm,
++                          "failed to setup backlight for connector %s\n",
++                          connector->name);
++              return ret;
++      }
++
++      panel->backlight.present = true;
++
++      drm_dbg_kms(&dev_priv->drm,
++                  "Connector %s backlight initialized, %s, brightness %u/%u\n",
++                  connector->name,
++                  enableddisabled(panel->backlight.enabled),
++                  panel->backlight.level, panel->backlight.max);
++
++      return 0;
++}
++
++void intel_panel_destroy_backlight(struct intel_panel *panel)
++{
++      /* dispose of the pwm */
++      if (panel->backlight.pwm)
++              pwm_put(panel->backlight.pwm);
++
++      panel->backlight.present = false;
++}
++
++static const struct intel_panel_bl_funcs bxt_pwm_funcs = {
++      .setup = bxt_setup_backlight,
++      .enable = bxt_enable_backlight,
++      .disable = bxt_disable_backlight,
++      .set = bxt_set_backlight,
++      .get = bxt_get_backlight,
++      .hz_to_pwm = bxt_hz_to_pwm,
++};
++
++static const struct intel_panel_bl_funcs cnp_pwm_funcs = {
++      .setup = cnp_setup_backlight,
++      .enable = cnp_enable_backlight,
++      .disable = cnp_disable_backlight,
++      .set = bxt_set_backlight,
++      .get = bxt_get_backlight,
++      .hz_to_pwm = cnp_hz_to_pwm,
++};
++
++static const struct intel_panel_bl_funcs lpt_pwm_funcs = {
++      .setup = lpt_setup_backlight,
++      .enable = lpt_enable_backlight,
++      .disable = lpt_disable_backlight,
++      .set = lpt_set_backlight,
++      .get = lpt_get_backlight,
++      .hz_to_pwm = lpt_hz_to_pwm,
++};
++
++static const struct intel_panel_bl_funcs spt_pwm_funcs = {
++      .setup = lpt_setup_backlight,
++      .enable = lpt_enable_backlight,
++      .disable = lpt_disable_backlight,
++      .set = lpt_set_backlight,
++      .get = lpt_get_backlight,
++      .hz_to_pwm = spt_hz_to_pwm,
++};
++
++static const struct intel_panel_bl_funcs pch_pwm_funcs = {
++      .setup = pch_setup_backlight,
++      .enable = pch_enable_backlight,
++      .disable = pch_disable_backlight,
++      .set = pch_set_backlight,
++      .get = pch_get_backlight,
++      .hz_to_pwm = pch_hz_to_pwm,
++};
++
++static const struct intel_panel_bl_funcs ext_pwm_funcs = {
++      .setup = ext_pwm_setup_backlight,
++      .enable = ext_pwm_enable_backlight,
++      .disable = ext_pwm_disable_backlight,
++      .set = ext_pwm_set_backlight,
++      .get = ext_pwm_get_backlight,
++};
++
++static const struct intel_panel_bl_funcs vlv_pwm_funcs = {
++      .setup = vlv_setup_backlight,
++      .enable = vlv_enable_backlight,
++      .disable = vlv_disable_backlight,
++      .set = vlv_set_backlight,
++      .get = vlv_get_backlight,
++      .hz_to_pwm = vlv_hz_to_pwm,
++};
++
++static const struct intel_panel_bl_funcs i965_pwm_funcs = {
++      .setup = i965_setup_backlight,
++      .enable = i965_enable_backlight,
++      .disable = i965_disable_backlight,
++      .set = i9xx_set_backlight,
++      .get = i9xx_get_backlight,
++      .hz_to_pwm = i965_hz_to_pwm,
++};
++
++static const struct intel_panel_bl_funcs i9xx_pwm_funcs = {
++      .setup = i9xx_setup_backlight,
++      .enable = i9xx_enable_backlight,
++      .disable = i9xx_disable_backlight,
++      .set = i9xx_set_backlight,
++      .get = i9xx_get_backlight,
++      .hz_to_pwm = i9xx_hz_to_pwm,
++};
++
++static const struct intel_panel_bl_funcs pwm_bl_funcs = {
++      .setup = intel_pwm_setup_backlight,
++      .enable = intel_pwm_enable_backlight,
++      .disable = intel_pwm_disable_backlight,
++      .set = intel_pwm_set_backlight,
++      .get = intel_pwm_get_backlight,
++};
++
++/* Set up chip specific backlight functions */
++void
++intel_panel_init_backlight_funcs(struct intel_panel *panel)
++{
++      struct intel_connector *connector =
++              container_of(panel, struct intel_connector, panel);
++      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
++
++      if (connector->base.connector_type == DRM_MODE_CONNECTOR_DSI &&
++          intel_dsi_dcs_init_backlight_funcs(connector) == 0)
++              return;
++
++      if (IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv)) {
++              panel->backlight.pwm_funcs = &bxt_pwm_funcs;
++      } else if (INTEL_PCH_TYPE(dev_priv) >= PCH_CNP) {
++              panel->backlight.pwm_funcs = &cnp_pwm_funcs;
++      } else if (INTEL_PCH_TYPE(dev_priv) >= PCH_LPT) {
++              if (HAS_PCH_LPT(dev_priv))
++                      panel->backlight.pwm_funcs = &lpt_pwm_funcs;
++              else
++                      panel->backlight.pwm_funcs = &spt_pwm_funcs;
++      } else if (HAS_PCH_SPLIT(dev_priv)) {
++              panel->backlight.pwm_funcs = &pch_pwm_funcs;
++      } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
++              if (connector->base.connector_type == DRM_MODE_CONNECTOR_DSI) {
++                      panel->backlight.pwm_funcs = &ext_pwm_funcs;
++              } else {
++                      panel->backlight.pwm_funcs = &vlv_pwm_funcs;
++              }
++      } else if (DISPLAY_VER(dev_priv) == 4) {
++              panel->backlight.pwm_funcs = &i965_pwm_funcs;
++      } else {
++              panel->backlight.pwm_funcs = &i9xx_pwm_funcs;
++      }
++
++      if (connector->base.connector_type == DRM_MODE_CONNECTOR_eDP &&
++          intel_dp_aux_init_backlight_funcs(connector) == 0)
++              return;
++
++      /* We're using a standard PWM backlight interface */
++      panel->backlight.funcs = &pwm_bl_funcs;
++}
+diff --git a/drivers/gpu/drm/i915/display/intel_backlight.h b/drivers/gpu/drm/i915/display/intel_backlight.h
+new file mode 100644
+index 0000000000000..282020cb47d5b
+--- /dev/null
++++ b/drivers/gpu/drm/i915/display/intel_backlight.h
+@@ -0,0 +1,51 @@
++/* SPDX-License-Identifier: MIT */
++/*
++ * Copyright © 2021 Intel Corporation
++ */
++
++#ifndef __INTEL_BACKLIGHT_H__
++#define __INTEL_BACKLIGHT_H__
++
++#include <linux/types.h>
++
++struct drm_connector;
++struct drm_connector_state;
++struct intel_atomic_state;
++struct intel_connector;
++struct intel_crtc_state;
++struct intel_encoder;
++struct intel_panel;
++enum pipe;
++
++void intel_panel_init_backlight_funcs(struct intel_panel *panel);
++void intel_panel_destroy_backlight(struct intel_panel *panel);
++void intel_panel_set_backlight_acpi(const struct drm_connector_state *conn_state,
++                                  u32 level, u32 max);
++int intel_panel_setup_backlight(struct drm_connector *connector,
++                              enum pipe pipe);
++void intel_panel_enable_backlight(const struct intel_crtc_state *crtc_state,
++                                const struct drm_connector_state *conn_state);
++void intel_panel_update_backlight(struct intel_atomic_state *state,
++                                struct intel_encoder *encoder,
++                                const struct intel_crtc_state *crtc_state,
++                                const struct drm_connector_state *conn_state);
++void intel_panel_disable_backlight(const struct drm_connector_state *old_conn_state);
++void intel_panel_set_pwm_level(const struct drm_connector_state *conn_state, u32 level);
++u32 intel_panel_invert_pwm_level(struct intel_connector *connector, u32 level);
++u32 intel_panel_backlight_level_to_pwm(struct intel_connector *connector, u32 level);
++u32 intel_panel_backlight_level_from_pwm(struct intel_connector *connector, u32 val);
++
++#if IS_ENABLED(CONFIG_BACKLIGHT_CLASS_DEVICE)
++int intel_backlight_device_register(struct intel_connector *connector);
++void intel_backlight_device_unregister(struct intel_connector *connector);
++#else /* CONFIG_BACKLIGHT_CLASS_DEVICE */
++static inline int intel_backlight_device_register(struct intel_connector *connector)
++{
++      return 0;
++}
++static inline void intel_backlight_device_unregister(struct intel_connector *connector)
++{
++}
++#endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
++
++#endif /* __INTEL_BACKLIGHT_H__ */
+diff --git a/drivers/gpu/drm/i915/display/intel_connector.c b/drivers/gpu/drm/i915/display/intel_connector.c
+index 9bed1ccecea0d..4f49d782eca23 100644
+--- a/drivers/gpu/drm/i915/display/intel_connector.c
++++ b/drivers/gpu/drm/i915/display/intel_connector.c
+@@ -29,13 +29,13 @@
+ #include <drm/drm_atomic_helper.h>
+ #include <drm/drm_edid.h>
+-#include "display/intel_panel.h"
+-
+ #include "i915_drv.h"
++#include "intel_backlight.h"
+ #include "intel_connector.h"
+ #include "intel_display_debugfs.h"
+ #include "intel_display_types.h"
+ #include "intel_hdcp.h"
++#include "intel_panel.h"
+ int intel_connector_init(struct intel_connector *connector)
+ {
+diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
+index f61901e26409e..68489c7298302 100644
+--- a/drivers/gpu/drm/i915/display/intel_ddi.c
++++ b/drivers/gpu/drm/i915/display/intel_ddi.c
+@@ -29,6 +29,7 @@
+ #include "i915_drv.h"
+ #include "intel_audio.h"
++#include "intel_backlight.h"
+ #include "intel_combo_phy.h"
+ #include "intel_connector.h"
+ #include "intel_crtc.h"
+@@ -49,7 +50,6 @@
+ #include "intel_hdmi.h"
+ #include "intel_hotplug.h"
+ #include "intel_lspcon.h"
+-#include "intel_panel.h"
+ #include "intel_pps.h"
+ #include "intel_psr.h"
+ #include "intel_snps_phy.h"
+diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
+index 631cf7d4323c8..f87e4d510ea5e 100644
+--- a/drivers/gpu/drm/i915/display/intel_dp.c
++++ b/drivers/gpu/drm/i915/display/intel_dp.c
+@@ -45,6 +45,7 @@
+ #include "i915_drv.h"
+ #include "intel_atomic.h"
+ #include "intel_audio.h"
++#include "intel_backlight.h"
+ #include "intel_connector.h"
+ #include "intel_ddi.h"
+ #include "intel_de.h"
+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 e7b90863aa43d..0a77f0e48aa11 100644
+--- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
++++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
+@@ -34,10 +34,10 @@
+  * for some reason.
+  */
++#include "intel_backlight.h"
+ #include "intel_display_types.h"
+ #include "intel_dp.h"
+ #include "intel_dp_aux_backlight.h"
+-#include "intel_panel.h"
+ /* TODO:
+  * Implement HDR, right now we just implement the bare minimum to bring us back into SDR mode so we
+diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c
+index e0381b0fce914..8f5741ebd58dd 100644
+--- a/drivers/gpu/drm/i915/display/intel_lvds.c
++++ b/drivers/gpu/drm/i915/display/intel_lvds.c
+@@ -40,6 +40,7 @@
+ #include "i915_drv.h"
+ #include "intel_atomic.h"
++#include "intel_backlight.h"
+ #include "intel_connector.h"
+ #include "intel_de.h"
+ #include "intel_display_types.h"
+diff --git a/drivers/gpu/drm/i915/display/intel_opregion.c b/drivers/gpu/drm/i915/display/intel_opregion.c
+index f7f49b69830fa..aad5c1cd3898e 100644
+--- a/drivers/gpu/drm/i915/display/intel_opregion.c
++++ b/drivers/gpu/drm/i915/display/intel_opregion.c
+@@ -30,10 +30,9 @@
+ #include <linux/firmware.h>
+ #include <acpi/video.h>
+-#include "display/intel_panel.h"
+-
+ #include "i915_drv.h"
+ #include "intel_acpi.h"
++#include "intel_backlight.h"
+ #include "intel_display_types.h"
+ #include "intel_opregion.h"
+diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c
+index 7d7a60b4d2de7..ad54767440c15 100644
+--- a/drivers/gpu/drm/i915/display/intel_panel.c
++++ b/drivers/gpu/drm/i915/display/intel_panel.c
+@@ -28,17 +28,13 @@
+  *      Chris Wilson <chris@chris-wilson.co.uk>
+  */
+-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+-
+ #include <linux/kernel.h>
+-#include <linux/moduleparam.h>
+ #include <linux/pwm.h>
++#include "intel_backlight.h"
+ #include "intel_connector.h"
+ #include "intel_de.h"
+ #include "intel_display_types.h"
+-#include "intel_dp_aux_backlight.h"
+-#include "intel_dsi_dcs_backlight.h"
+ #include "intel_panel.h"
+ void
+@@ -456,1767 +452,6 @@ int intel_gmch_panel_fitting(struct intel_crtc_state *crtc_state,
+       return 0;
+ }
+-/**
+- * scale - scale values from one range to another
+- * @source_val: value in range [@source_min..@source_max]
+- * @source_min: minimum legal value for @source_val
+- * @source_max: maximum legal value for @source_val
+- * @target_min: corresponding target value for @source_min
+- * @target_max: corresponding target value for @source_max
+- *
+- * Return @source_val in range [@source_min..@source_max] scaled to range
+- * [@target_min..@target_max].
+- */
+-static u32 scale(u32 source_val,
+-               u32 source_min, u32 source_max,
+-               u32 target_min, u32 target_max)
+-{
+-      u64 target_val;
+-
+-      WARN_ON(source_min > source_max);
+-      WARN_ON(target_min > target_max);
+-
+-      /* defensive */
+-      source_val = clamp(source_val, source_min, source_max);
+-
+-      /* avoid overflows */
+-      target_val = mul_u32_u32(source_val - source_min,
+-                               target_max - target_min);
+-      target_val = DIV_ROUND_CLOSEST_ULL(target_val, source_max - source_min);
+-      target_val += target_min;
+-
+-      return target_val;
+-}
+-
+-/* Scale user_level in range [0..user_max] to [0..hw_max], clamping the result
+- * to [hw_min..hw_max]. */
+-static u32 clamp_user_to_hw(struct intel_connector *connector,
+-                          u32 user_level, u32 user_max)
+-{
+-      struct intel_panel *panel = &connector->panel;
+-      u32 hw_level;
+-
+-      hw_level = scale(user_level, 0, user_max, 0, panel->backlight.max);
+-      hw_level = clamp(hw_level, panel->backlight.min, panel->backlight.max);
+-
+-      return hw_level;
+-}
+-
+-/* Scale hw_level in range [hw_min..hw_max] to [0..user_max]. */
+-static u32 scale_hw_to_user(struct intel_connector *connector,
+-                          u32 hw_level, u32 user_max)
+-{
+-      struct intel_panel *panel = &connector->panel;
+-
+-      return scale(hw_level, panel->backlight.min, panel->backlight.max,
+-                   0, user_max);
+-}
+-
+-u32 intel_panel_invert_pwm_level(struct intel_connector *connector, u32 val)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      drm_WARN_ON(&dev_priv->drm, panel->backlight.pwm_level_max == 0);
+-
+-      if (dev_priv->params.invert_brightness < 0)
+-              return val;
+-
+-      if (dev_priv->params.invert_brightness > 0 ||
+-          dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) {
+-              return panel->backlight.pwm_level_max - val + panel->backlight.pwm_level_min;
+-      }
+-
+-      return val;
+-}
+-
+-void intel_panel_set_pwm_level(const struct drm_connector_state *conn_state, u32 val)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *i915 = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      drm_dbg_kms(&i915->drm, "set backlight PWM = %d\n", val);
+-      panel->backlight.pwm_funcs->set(conn_state, val);
+-}
+-
+-u32 intel_panel_backlight_level_to_pwm(struct intel_connector *connector, u32 val)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      drm_WARN_ON_ONCE(&dev_priv->drm,
+-                       panel->backlight.max == 0 || panel->backlight.pwm_level_max == 0);
+-
+-      val = scale(val, panel->backlight.min, panel->backlight.max,
+-                  panel->backlight.pwm_level_min, panel->backlight.pwm_level_max);
+-
+-      return intel_panel_invert_pwm_level(connector, val);
+-}
+-
+-u32 intel_panel_backlight_level_from_pwm(struct intel_connector *connector, u32 val)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      drm_WARN_ON_ONCE(&dev_priv->drm,
+-                       panel->backlight.max == 0 || panel->backlight.pwm_level_max == 0);
+-
+-      if (dev_priv->params.invert_brightness > 0 ||
+-          (dev_priv->params.invert_brightness == 0 && dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS))
+-              val = panel->backlight.pwm_level_max - (val - panel->backlight.pwm_level_min);
+-
+-      return scale(val, panel->backlight.pwm_level_min, panel->backlight.pwm_level_max,
+-                   panel->backlight.min, panel->backlight.max);
+-}
+-
+-static u32 lpt_get_backlight(struct intel_connector *connector, enum pipe unused)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-
+-      return intel_de_read(dev_priv, BLC_PWM_PCH_CTL2) & BACKLIGHT_DUTY_CYCLE_MASK;
+-}
+-
+-static u32 pch_get_backlight(struct intel_connector *connector, enum pipe unused)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-
+-      return intel_de_read(dev_priv, BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
+-}
+-
+-static u32 i9xx_get_backlight(struct intel_connector *connector, enum pipe unused)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 val;
+-
+-      val = intel_de_read(dev_priv, BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
+-      if (DISPLAY_VER(dev_priv) < 4)
+-              val >>= 1;
+-
+-      if (panel->backlight.combination_mode) {
+-              u8 lbpc;
+-
+-              pci_read_config_byte(to_pci_dev(dev_priv->drm.dev), LBPC, &lbpc);
+-              val *= lbpc;
+-      }
+-
+-      return val;
+-}
+-
+-static u32 vlv_get_backlight(struct intel_connector *connector, enum pipe pipe)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-
+-      if (drm_WARN_ON(&dev_priv->drm, pipe != PIPE_A && pipe != PIPE_B))
+-              return 0;
+-
+-      return intel_de_read(dev_priv, VLV_BLC_PWM_CTL(pipe)) & BACKLIGHT_DUTY_CYCLE_MASK;
+-}
+-
+-static u32 bxt_get_backlight(struct intel_connector *connector, enum pipe unused)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      return intel_de_read(dev_priv,
+-                           BXT_BLC_PWM_DUTY(panel->backlight.controller));
+-}
+-
+-static u32 ext_pwm_get_backlight(struct intel_connector *connector, enum pipe unused)
+-{
+-      struct intel_panel *panel = &connector->panel;
+-      struct pwm_state state;
+-
+-      pwm_get_state(panel->backlight.pwm, &state);
+-      return pwm_get_relative_duty_cycle(&state, 100);
+-}
+-
+-static void lpt_set_backlight(const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-
+-      u32 val = intel_de_read(dev_priv, BLC_PWM_PCH_CTL2) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+-      intel_de_write(dev_priv, BLC_PWM_PCH_CTL2, val | level);
+-}
+-
+-static void pch_set_backlight(const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      u32 tmp;
+-
+-      tmp = intel_de_read(dev_priv, BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+-      intel_de_write(dev_priv, BLC_PWM_CPU_CTL, tmp | level);
+-}
+-
+-static void i9xx_set_backlight(const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 tmp, mask;
+-
+-      drm_WARN_ON(&dev_priv->drm, panel->backlight.pwm_level_max == 0);
+-
+-      if (panel->backlight.combination_mode) {
+-              u8 lbpc;
+-
+-              lbpc = level * 0xfe / panel->backlight.pwm_level_max + 1;
+-              level /= lbpc;
+-              pci_write_config_byte(to_pci_dev(dev_priv->drm.dev), LBPC, lbpc);
+-      }
+-
+-      if (DISPLAY_VER(dev_priv) == 4) {
+-              mask = BACKLIGHT_DUTY_CYCLE_MASK;
+-      } else {
+-              level <<= 1;
+-              mask = BACKLIGHT_DUTY_CYCLE_MASK_PNV;
+-      }
+-
+-      tmp = intel_de_read(dev_priv, BLC_PWM_CTL) & ~mask;
+-      intel_de_write(dev_priv, BLC_PWM_CTL, tmp | level);
+-}
+-
+-static void vlv_set_backlight(const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      enum pipe pipe = to_intel_crtc(conn_state->crtc)->pipe;
+-      u32 tmp;
+-
+-      tmp = intel_de_read(dev_priv, VLV_BLC_PWM_CTL(pipe)) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+-      intel_de_write(dev_priv, VLV_BLC_PWM_CTL(pipe), tmp | level);
+-}
+-
+-static void bxt_set_backlight(const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      intel_de_write(dev_priv,
+-                     BXT_BLC_PWM_DUTY(panel->backlight.controller), level);
+-}
+-
+-static void ext_pwm_set_backlight(const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_panel *panel = &to_intel_connector(conn_state->connector)->panel;
+-
+-      pwm_set_relative_duty_cycle(&panel->backlight.pwm_state, level, 100);
+-      pwm_apply_state(panel->backlight.pwm, &panel->backlight.pwm_state);
+-}
+-
+-static void
+-intel_panel_actually_set_backlight(const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *i915 = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      drm_dbg_kms(&i915->drm, "set backlight level = %d\n", level);
+-
+-      panel->backlight.funcs->set(conn_state, level);
+-}
+-
+-/* set backlight brightness to level in range [0..max], assuming hw min is
+- * respected.
+- */
+-void intel_panel_set_backlight_acpi(const struct drm_connector_state *conn_state,
+-                                  u32 user_level, u32 user_max)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 hw_level;
+-
+-      /*
+-       * Lack of crtc may occur during driver init because
+-       * connection_mutex isn't held across the entire backlight
+-       * setup + modeset readout, and the BIOS can issue the
+-       * requests at any time.
+-       */
+-      if (!panel->backlight.present || !conn_state->crtc)
+-              return;
+-
+-      mutex_lock(&dev_priv->backlight_lock);
+-
+-      drm_WARN_ON(&dev_priv->drm, panel->backlight.max == 0);
+-
+-      hw_level = clamp_user_to_hw(connector, user_level, user_max);
+-      panel->backlight.level = hw_level;
+-
+-      if (panel->backlight.device)
+-              panel->backlight.device->props.brightness =
+-                      scale_hw_to_user(connector,
+-                                       panel->backlight.level,
+-                                       panel->backlight.device->props.max_brightness);
+-
+-      if (panel->backlight.enabled)
+-              intel_panel_actually_set_backlight(conn_state, hw_level);
+-
+-      mutex_unlock(&dev_priv->backlight_lock);
+-}
+-
+-static void lpt_disable_backlight(const struct drm_connector_state *old_conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      u32 tmp;
+-
+-      intel_panel_set_pwm_level(old_conn_state, level);
+-
+-      /*
+-       * Although we don't support or enable CPU PWM with LPT/SPT based
+-       * systems, it may have been enabled prior to loading the
+-       * driver. Disable to avoid warnings on LCPLL disable.
+-       *
+-       * This needs rework if we need to add support for CPU PWM on PCH split
+-       * platforms.
+-       */
+-      tmp = intel_de_read(dev_priv, BLC_PWM_CPU_CTL2);
+-      if (tmp & BLM_PWM_ENABLE) {
+-              drm_dbg_kms(&dev_priv->drm,
+-                          "cpu backlight was enabled, disabling\n");
+-              intel_de_write(dev_priv, BLC_PWM_CPU_CTL2,
+-                             tmp & ~BLM_PWM_ENABLE);
+-      }
+-
+-      tmp = intel_de_read(dev_priv, BLC_PWM_PCH_CTL1);
+-      intel_de_write(dev_priv, BLC_PWM_PCH_CTL1, tmp & ~BLM_PCH_PWM_ENABLE);
+-}
+-
+-static void pch_disable_backlight(const struct drm_connector_state *old_conn_state, u32 val)
+-{
+-      struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      u32 tmp;
+-
+-      intel_panel_set_pwm_level(old_conn_state, val);
+-
+-      tmp = intel_de_read(dev_priv, BLC_PWM_CPU_CTL2);
+-      intel_de_write(dev_priv, BLC_PWM_CPU_CTL2, tmp & ~BLM_PWM_ENABLE);
+-
+-      tmp = intel_de_read(dev_priv, BLC_PWM_PCH_CTL1);
+-      intel_de_write(dev_priv, BLC_PWM_PCH_CTL1, tmp & ~BLM_PCH_PWM_ENABLE);
+-}
+-
+-static void i9xx_disable_backlight(const struct drm_connector_state *old_conn_state, u32 val)
+-{
+-      intel_panel_set_pwm_level(old_conn_state, val);
+-}
+-
+-static void i965_disable_backlight(const struct drm_connector_state *old_conn_state, u32 val)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(old_conn_state->connector->dev);
+-      u32 tmp;
+-
+-      intel_panel_set_pwm_level(old_conn_state, val);
+-
+-      tmp = intel_de_read(dev_priv, BLC_PWM_CTL2);
+-      intel_de_write(dev_priv, BLC_PWM_CTL2, tmp & ~BLM_PWM_ENABLE);
+-}
+-
+-static void vlv_disable_backlight(const struct drm_connector_state *old_conn_state, u32 val)
+-{
+-      struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      enum pipe pipe = to_intel_crtc(old_conn_state->crtc)->pipe;
+-      u32 tmp;
+-
+-      intel_panel_set_pwm_level(old_conn_state, val);
+-
+-      tmp = intel_de_read(dev_priv, VLV_BLC_PWM_CTL2(pipe));
+-      intel_de_write(dev_priv, VLV_BLC_PWM_CTL2(pipe),
+-                     tmp & ~BLM_PWM_ENABLE);
+-}
+-
+-static void bxt_disable_backlight(const struct drm_connector_state *old_conn_state, u32 val)
+-{
+-      struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 tmp;
+-
+-      intel_panel_set_pwm_level(old_conn_state, val);
+-
+-      tmp = intel_de_read(dev_priv,
+-                          BXT_BLC_PWM_CTL(panel->backlight.controller));
+-      intel_de_write(dev_priv, BXT_BLC_PWM_CTL(panel->backlight.controller),
+-                     tmp & ~BXT_BLC_PWM_ENABLE);
+-
+-      if (panel->backlight.controller == 1) {
+-              val = intel_de_read(dev_priv, UTIL_PIN_CTL);
+-              val &= ~UTIL_PIN_ENABLE;
+-              intel_de_write(dev_priv, UTIL_PIN_CTL, val);
+-      }
+-}
+-
+-static void cnp_disable_backlight(const struct drm_connector_state *old_conn_state, u32 val)
+-{
+-      struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 tmp;
+-
+-      intel_panel_set_pwm_level(old_conn_state, val);
+-
+-      tmp = intel_de_read(dev_priv,
+-                          BXT_BLC_PWM_CTL(panel->backlight.controller));
+-      intel_de_write(dev_priv, BXT_BLC_PWM_CTL(panel->backlight.controller),
+-                     tmp & ~BXT_BLC_PWM_ENABLE);
+-}
+-
+-static void ext_pwm_disable_backlight(const struct drm_connector_state *old_conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      panel->backlight.pwm_state.enabled = false;
+-      pwm_apply_state(panel->backlight.pwm, &panel->backlight.pwm_state);
+-}
+-
+-void intel_panel_disable_backlight(const struct drm_connector_state *old_conn_state)
+-{
+-      struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      if (!panel->backlight.present)
+-              return;
+-
+-      /*
+-       * Do not disable backlight on the vga_switcheroo path. When switching
+-       * away from i915, the other client may depend on i915 to handle the
+-       * backlight. This will leave the backlight on unnecessarily when
+-       * another client is not activated.
+-       */
+-      if (dev_priv->drm.switch_power_state == DRM_SWITCH_POWER_CHANGING) {
+-              drm_dbg_kms(&dev_priv->drm,
+-                          "Skipping backlight disable on vga switch\n");
+-              return;
+-      }
+-
+-      mutex_lock(&dev_priv->backlight_lock);
+-
+-      if (panel->backlight.device)
+-              panel->backlight.device->props.power = FB_BLANK_POWERDOWN;
+-      panel->backlight.enabled = false;
+-      panel->backlight.funcs->disable(old_conn_state, 0);
+-
+-      mutex_unlock(&dev_priv->backlight_lock);
+-}
+-
+-static void lpt_enable_backlight(const struct intel_crtc_state *crtc_state,
+-                               const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 pch_ctl1, pch_ctl2, schicken;
+-
+-      pch_ctl1 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL1);
+-      if (pch_ctl1 & BLM_PCH_PWM_ENABLE) {
+-              drm_dbg_kms(&dev_priv->drm, "pch backlight already enabled\n");
+-              pch_ctl1 &= ~BLM_PCH_PWM_ENABLE;
+-              intel_de_write(dev_priv, BLC_PWM_PCH_CTL1, pch_ctl1);
+-      }
+-
+-      if (HAS_PCH_LPT(dev_priv)) {
+-              schicken = intel_de_read(dev_priv, SOUTH_CHICKEN2);
+-              if (panel->backlight.alternate_pwm_increment)
+-                      schicken |= LPT_PWM_GRANULARITY;
+-              else
+-                      schicken &= ~LPT_PWM_GRANULARITY;
+-              intel_de_write(dev_priv, SOUTH_CHICKEN2, schicken);
+-      } else {
+-              schicken = intel_de_read(dev_priv, SOUTH_CHICKEN1);
+-              if (panel->backlight.alternate_pwm_increment)
+-                      schicken |= SPT_PWM_GRANULARITY;
+-              else
+-                      schicken &= ~SPT_PWM_GRANULARITY;
+-              intel_de_write(dev_priv, SOUTH_CHICKEN1, schicken);
+-      }
+-
+-      pch_ctl2 = panel->backlight.pwm_level_max << 16;
+-      intel_de_write(dev_priv, BLC_PWM_PCH_CTL2, pch_ctl2);
+-
+-      pch_ctl1 = 0;
+-      if (panel->backlight.active_low_pwm)
+-              pch_ctl1 |= BLM_PCH_POLARITY;
+-
+-      /* After LPT, override is the default. */
+-      if (HAS_PCH_LPT(dev_priv))
+-              pch_ctl1 |= BLM_PCH_OVERRIDE_ENABLE;
+-
+-      intel_de_write(dev_priv, BLC_PWM_PCH_CTL1, pch_ctl1);
+-      intel_de_posting_read(dev_priv, BLC_PWM_PCH_CTL1);
+-      intel_de_write(dev_priv, BLC_PWM_PCH_CTL1,
+-                     pch_ctl1 | BLM_PCH_PWM_ENABLE);
+-
+-      /* This won't stick until the above enable. */
+-      intel_panel_set_pwm_level(conn_state, level);
+-}
+-
+-static void pch_enable_backlight(const struct intel_crtc_state *crtc_state,
+-                               const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
+-      u32 cpu_ctl2, pch_ctl1, pch_ctl2;
+-
+-      cpu_ctl2 = intel_de_read(dev_priv, BLC_PWM_CPU_CTL2);
+-      if (cpu_ctl2 & BLM_PWM_ENABLE) {
+-              drm_dbg_kms(&dev_priv->drm, "cpu backlight already enabled\n");
+-              cpu_ctl2 &= ~BLM_PWM_ENABLE;
+-              intel_de_write(dev_priv, BLC_PWM_CPU_CTL2, cpu_ctl2);
+-      }
+-
+-      pch_ctl1 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL1);
+-      if (pch_ctl1 & BLM_PCH_PWM_ENABLE) {
+-              drm_dbg_kms(&dev_priv->drm, "pch backlight already enabled\n");
+-              pch_ctl1 &= ~BLM_PCH_PWM_ENABLE;
+-              intel_de_write(dev_priv, BLC_PWM_PCH_CTL1, pch_ctl1);
+-      }
+-
+-      if (cpu_transcoder == TRANSCODER_EDP)
+-              cpu_ctl2 = BLM_TRANSCODER_EDP;
+-      else
+-              cpu_ctl2 = BLM_PIPE(cpu_transcoder);
+-      intel_de_write(dev_priv, BLC_PWM_CPU_CTL2, cpu_ctl2);
+-      intel_de_posting_read(dev_priv, BLC_PWM_CPU_CTL2);
+-      intel_de_write(dev_priv, BLC_PWM_CPU_CTL2, cpu_ctl2 | BLM_PWM_ENABLE);
+-
+-      /* This won't stick until the above enable. */
+-      intel_panel_set_pwm_level(conn_state, level);
+-
+-      pch_ctl2 = panel->backlight.pwm_level_max << 16;
+-      intel_de_write(dev_priv, BLC_PWM_PCH_CTL2, pch_ctl2);
+-
+-      pch_ctl1 = 0;
+-      if (panel->backlight.active_low_pwm)
+-              pch_ctl1 |= BLM_PCH_POLARITY;
+-
+-      intel_de_write(dev_priv, BLC_PWM_PCH_CTL1, pch_ctl1);
+-      intel_de_posting_read(dev_priv, BLC_PWM_PCH_CTL1);
+-      intel_de_write(dev_priv, BLC_PWM_PCH_CTL1,
+-                     pch_ctl1 | BLM_PCH_PWM_ENABLE);
+-}
+-
+-static void i9xx_enable_backlight(const struct intel_crtc_state *crtc_state,
+-                                const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 ctl, freq;
+-
+-      ctl = intel_de_read(dev_priv, BLC_PWM_CTL);
+-      if (ctl & BACKLIGHT_DUTY_CYCLE_MASK_PNV) {
+-              drm_dbg_kms(&dev_priv->drm, "backlight already enabled\n");
+-              intel_de_write(dev_priv, BLC_PWM_CTL, 0);
+-      }
+-
+-      freq = panel->backlight.pwm_level_max;
+-      if (panel->backlight.combination_mode)
+-              freq /= 0xff;
+-
+-      ctl = freq << 17;
+-      if (panel->backlight.combination_mode)
+-              ctl |= BLM_LEGACY_MODE;
+-      if (IS_PINEVIEW(dev_priv) && panel->backlight.active_low_pwm)
+-              ctl |= BLM_POLARITY_PNV;
+-
+-      intel_de_write(dev_priv, BLC_PWM_CTL, ctl);
+-      intel_de_posting_read(dev_priv, BLC_PWM_CTL);
+-
+-      /* XXX: combine this into above write? */
+-      intel_panel_set_pwm_level(conn_state, level);
+-
+-      /*
+-       * Needed to enable backlight on some 855gm models. BLC_HIST_CTL is
+-       * 855gm only, but checking for gen2 is safe, as 855gm is the only gen2
+-       * that has backlight.
+-       */
+-      if (DISPLAY_VER(dev_priv) == 2)
+-              intel_de_write(dev_priv, BLC_HIST_CTL, BLM_HISTOGRAM_ENABLE);
+-}
+-
+-static void i965_enable_backlight(const struct intel_crtc_state *crtc_state,
+-                                const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      enum pipe pipe = to_intel_crtc(conn_state->crtc)->pipe;
+-      u32 ctl, ctl2, freq;
+-
+-      ctl2 = intel_de_read(dev_priv, BLC_PWM_CTL2);
+-      if (ctl2 & BLM_PWM_ENABLE) {
+-              drm_dbg_kms(&dev_priv->drm, "backlight already enabled\n");
+-              ctl2 &= ~BLM_PWM_ENABLE;
+-              intel_de_write(dev_priv, BLC_PWM_CTL2, ctl2);
+-      }
+-
+-      freq = panel->backlight.pwm_level_max;
+-      if (panel->backlight.combination_mode)
+-              freq /= 0xff;
+-
+-      ctl = freq << 16;
+-      intel_de_write(dev_priv, BLC_PWM_CTL, ctl);
+-
+-      ctl2 = BLM_PIPE(pipe);
+-      if (panel->backlight.combination_mode)
+-              ctl2 |= BLM_COMBINATION_MODE;
+-      if (panel->backlight.active_low_pwm)
+-              ctl2 |= BLM_POLARITY_I965;
+-      intel_de_write(dev_priv, BLC_PWM_CTL2, ctl2);
+-      intel_de_posting_read(dev_priv, BLC_PWM_CTL2);
+-      intel_de_write(dev_priv, BLC_PWM_CTL2, ctl2 | BLM_PWM_ENABLE);
+-
+-      intel_panel_set_pwm_level(conn_state, level);
+-}
+-
+-static void vlv_enable_backlight(const struct intel_crtc_state *crtc_state,
+-                               const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe;
+-      u32 ctl, ctl2;
+-
+-      ctl2 = intel_de_read(dev_priv, VLV_BLC_PWM_CTL2(pipe));
+-      if (ctl2 & BLM_PWM_ENABLE) {
+-              drm_dbg_kms(&dev_priv->drm, "backlight already enabled\n");
+-              ctl2 &= ~BLM_PWM_ENABLE;
+-              intel_de_write(dev_priv, VLV_BLC_PWM_CTL2(pipe), ctl2);
+-      }
+-
+-      ctl = panel->backlight.pwm_level_max << 16;
+-      intel_de_write(dev_priv, VLV_BLC_PWM_CTL(pipe), ctl);
+-
+-      /* XXX: combine this into above write? */
+-      intel_panel_set_pwm_level(conn_state, level);
+-
+-      ctl2 = 0;
+-      if (panel->backlight.active_low_pwm)
+-              ctl2 |= BLM_POLARITY_I965;
+-      intel_de_write(dev_priv, VLV_BLC_PWM_CTL2(pipe), ctl2);
+-      intel_de_posting_read(dev_priv, VLV_BLC_PWM_CTL2(pipe));
+-      intel_de_write(dev_priv, VLV_BLC_PWM_CTL2(pipe),
+-                     ctl2 | BLM_PWM_ENABLE);
+-}
+-
+-static void bxt_enable_backlight(const struct intel_crtc_state *crtc_state,
+-                               const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe;
+-      u32 pwm_ctl, val;
+-
+-      /* Controller 1 uses the utility pin. */
+-      if (panel->backlight.controller == 1) {
+-              val = intel_de_read(dev_priv, UTIL_PIN_CTL);
+-              if (val & UTIL_PIN_ENABLE) {
+-                      drm_dbg_kms(&dev_priv->drm,
+-                                  "util pin already enabled\n");
+-                      val &= ~UTIL_PIN_ENABLE;
+-                      intel_de_write(dev_priv, UTIL_PIN_CTL, val);
+-              }
+-
+-              val = 0;
+-              if (panel->backlight.util_pin_active_low)
+-                      val |= UTIL_PIN_POLARITY;
+-              intel_de_write(dev_priv, UTIL_PIN_CTL,
+-                             val | UTIL_PIN_PIPE(pipe) | UTIL_PIN_MODE_PWM | UTIL_PIN_ENABLE);
+-      }
+-
+-      pwm_ctl = intel_de_read(dev_priv,
+-                              BXT_BLC_PWM_CTL(panel->backlight.controller));
+-      if (pwm_ctl & BXT_BLC_PWM_ENABLE) {
+-              drm_dbg_kms(&dev_priv->drm, "backlight already enabled\n");
+-              pwm_ctl &= ~BXT_BLC_PWM_ENABLE;
+-              intel_de_write(dev_priv,
+-                             BXT_BLC_PWM_CTL(panel->backlight.controller),
+-                             pwm_ctl);
+-      }
+-
+-      intel_de_write(dev_priv,
+-                     BXT_BLC_PWM_FREQ(panel->backlight.controller),
+-                     panel->backlight.pwm_level_max);
+-
+-      intel_panel_set_pwm_level(conn_state, level);
+-
+-      pwm_ctl = 0;
+-      if (panel->backlight.active_low_pwm)
+-              pwm_ctl |= BXT_BLC_PWM_POLARITY;
+-
+-      intel_de_write(dev_priv, BXT_BLC_PWM_CTL(panel->backlight.controller),
+-                     pwm_ctl);
+-      intel_de_posting_read(dev_priv,
+-                            BXT_BLC_PWM_CTL(panel->backlight.controller));
+-      intel_de_write(dev_priv, BXT_BLC_PWM_CTL(panel->backlight.controller),
+-                     pwm_ctl | BXT_BLC_PWM_ENABLE);
+-}
+-
+-static void cnp_enable_backlight(const struct intel_crtc_state *crtc_state,
+-                               const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 pwm_ctl;
+-
+-      pwm_ctl = intel_de_read(dev_priv,
+-                              BXT_BLC_PWM_CTL(panel->backlight.controller));
+-      if (pwm_ctl & BXT_BLC_PWM_ENABLE) {
+-              drm_dbg_kms(&dev_priv->drm, "backlight already enabled\n");
+-              pwm_ctl &= ~BXT_BLC_PWM_ENABLE;
+-              intel_de_write(dev_priv,
+-                             BXT_BLC_PWM_CTL(panel->backlight.controller),
+-                             pwm_ctl);
+-      }
+-
+-      intel_de_write(dev_priv,
+-                     BXT_BLC_PWM_FREQ(panel->backlight.controller),
+-                     panel->backlight.pwm_level_max);
+-
+-      intel_panel_set_pwm_level(conn_state, level);
+-
+-      pwm_ctl = 0;
+-      if (panel->backlight.active_low_pwm)
+-              pwm_ctl |= BXT_BLC_PWM_POLARITY;
+-
+-      intel_de_write(dev_priv, BXT_BLC_PWM_CTL(panel->backlight.controller),
+-                     pwm_ctl);
+-      intel_de_posting_read(dev_priv,
+-                            BXT_BLC_PWM_CTL(panel->backlight.controller));
+-      intel_de_write(dev_priv, BXT_BLC_PWM_CTL(panel->backlight.controller),
+-                     pwm_ctl | BXT_BLC_PWM_ENABLE);
+-}
+-
+-static void ext_pwm_enable_backlight(const struct intel_crtc_state *crtc_state,
+-                                   const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      pwm_set_relative_duty_cycle(&panel->backlight.pwm_state, level, 100);
+-      panel->backlight.pwm_state.enabled = true;
+-      pwm_apply_state(panel->backlight.pwm, &panel->backlight.pwm_state);
+-}
+-
+-static void __intel_panel_enable_backlight(const struct intel_crtc_state *crtc_state,
+-                                         const struct drm_connector_state *conn_state)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      WARN_ON(panel->backlight.max == 0);
+-
+-      if (panel->backlight.level <= panel->backlight.min) {
+-              panel->backlight.level = panel->backlight.max;
+-              if (panel->backlight.device)
+-                      panel->backlight.device->props.brightness =
+-                              scale_hw_to_user(connector,
+-                                               panel->backlight.level,
+-                                               panel->backlight.device->props.max_brightness);
+-      }
+-
+-      panel->backlight.funcs->enable(crtc_state, conn_state, panel->backlight.level);
+-      panel->backlight.enabled = true;
+-      if (panel->backlight.device)
+-              panel->backlight.device->props.power = FB_BLANK_UNBLANK;
+-}
+-
+-void intel_panel_enable_backlight(const struct intel_crtc_state *crtc_state,
+-                                const struct drm_connector_state *conn_state)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe;
+-
+-      if (!panel->backlight.present)
+-              return;
+-
+-      drm_dbg_kms(&dev_priv->drm, "pipe %c\n", pipe_name(pipe));
+-
+-      mutex_lock(&dev_priv->backlight_lock);
+-
+-      __intel_panel_enable_backlight(crtc_state, conn_state);
+-
+-      mutex_unlock(&dev_priv->backlight_lock);
+-}
+-
+-#if IS_ENABLED(CONFIG_BACKLIGHT_CLASS_DEVICE)
+-static u32 intel_panel_get_backlight(struct intel_connector *connector)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 val = 0;
+-
+-      mutex_lock(&dev_priv->backlight_lock);
+-
+-      if (panel->backlight.enabled)
+-              val = panel->backlight.funcs->get(connector, intel_connector_get_pipe(connector));
+-
+-      mutex_unlock(&dev_priv->backlight_lock);
+-
+-      drm_dbg_kms(&dev_priv->drm, "get backlight PWM = %d\n", val);
+-      return val;
+-}
+-
+-/* Scale user_level in range [0..user_max] to [hw_min..hw_max]. */
+-static u32 scale_user_to_hw(struct intel_connector *connector,
+-                          u32 user_level, u32 user_max)
+-{
+-      struct intel_panel *panel = &connector->panel;
+-
+-      return scale(user_level, 0, user_max,
+-                   panel->backlight.min, panel->backlight.max);
+-}
+-
+-/* set backlight brightness to level in range [0..max], scaling wrt hw min */
+-static void intel_panel_set_backlight(const struct drm_connector_state *conn_state,
+-                                    u32 user_level, u32 user_max)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 hw_level;
+-
+-      if (!panel->backlight.present)
+-              return;
+-
+-      mutex_lock(&dev_priv->backlight_lock);
+-
+-      drm_WARN_ON(&dev_priv->drm, panel->backlight.max == 0);
+-
+-      hw_level = scale_user_to_hw(connector, user_level, user_max);
+-      panel->backlight.level = hw_level;
+-
+-      if (panel->backlight.enabled)
+-              intel_panel_actually_set_backlight(conn_state, hw_level);
+-
+-      mutex_unlock(&dev_priv->backlight_lock);
+-}
+-
+-static int intel_backlight_device_update_status(struct backlight_device *bd)
+-{
+-      struct intel_connector *connector = bl_get_data(bd);
+-      struct intel_panel *panel = &connector->panel;
+-      struct drm_device *dev = connector->base.dev;
+-
+-      drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+-      DRM_DEBUG_KMS("updating intel_backlight, brightness=%d/%d\n",
+-                    bd->props.brightness, bd->props.max_brightness);
+-      intel_panel_set_backlight(connector->base.state, bd->props.brightness,
+-                                bd->props.max_brightness);
+-
+-      /*
+-       * Allow flipping bl_power as a sub-state of enabled. Sadly the
+-       * backlight class device does not make it easy to to differentiate
+-       * between callbacks for brightness and bl_power, so our backlight_power
+-       * callback needs to take this into account.
+-       */
+-      if (panel->backlight.enabled) {
+-              if (panel->backlight.power) {
+-                      bool enable = bd->props.power == FB_BLANK_UNBLANK &&
+-                              bd->props.brightness != 0;
+-                      panel->backlight.power(connector, enable);
+-              }
+-      } else {
+-              bd->props.power = FB_BLANK_POWERDOWN;
+-      }
+-
+-      drm_modeset_unlock(&dev->mode_config.connection_mutex);
+-      return 0;
+-}
+-
+-static int intel_backlight_device_get_brightness(struct backlight_device *bd)
+-{
+-      struct intel_connector *connector = bl_get_data(bd);
+-      struct drm_device *dev = connector->base.dev;
+-      struct drm_i915_private *dev_priv = to_i915(dev);
+-      intel_wakeref_t wakeref;
+-      int ret = 0;
+-
+-      with_intel_runtime_pm(&dev_priv->runtime_pm, wakeref) {
+-              u32 hw_level;
+-
+-              drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+-
+-              hw_level = intel_panel_get_backlight(connector);
+-              ret = scale_hw_to_user(connector,
+-                                     hw_level, bd->props.max_brightness);
+-
+-              drm_modeset_unlock(&dev->mode_config.connection_mutex);
+-      }
+-
+-      return ret;
+-}
+-
+-static const struct backlight_ops intel_backlight_device_ops = {
+-      .update_status = intel_backlight_device_update_status,
+-      .get_brightness = intel_backlight_device_get_brightness,
+-};
+-
+-int intel_backlight_device_register(struct intel_connector *connector)
+-{
+-      struct drm_i915_private *i915 = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      struct backlight_properties props;
+-      struct backlight_device *bd;
+-      const char *name;
+-      int ret = 0;
+-
+-      if (WARN_ON(panel->backlight.device))
+-              return -ENODEV;
+-
+-      if (!panel->backlight.present)
+-              return 0;
+-
+-      WARN_ON(panel->backlight.max == 0);
+-
+-      memset(&props, 0, sizeof(props));
+-      props.type = BACKLIGHT_RAW;
+-
+-      /*
+-       * Note: Everything should work even if the backlight device max
+-       * presented to the userspace is arbitrarily chosen.
+-       */
+-      props.max_brightness = panel->backlight.max;
+-      props.brightness = scale_hw_to_user(connector,
+-                                          panel->backlight.level,
+-                                          props.max_brightness);
+-
+-      if (panel->backlight.enabled)
+-              props.power = FB_BLANK_UNBLANK;
+-      else
+-              props.power = FB_BLANK_POWERDOWN;
+-
+-      name = kstrdup("intel_backlight", GFP_KERNEL);
+-      if (!name)
+-              return -ENOMEM;
+-
+-      bd = backlight_device_register(name, connector->base.kdev, connector,
+-                                     &intel_backlight_device_ops, &props);
+-
+-      /*
+-       * Using the same name independent of the drm device or connector
+-       * prevents registration of multiple backlight devices in the
+-       * driver. However, we need to use the default name for backward
+-       * compatibility. Use unique names for subsequent backlight devices as a
+-       * fallback when the default name already exists.
+-       */
+-      if (IS_ERR(bd) && PTR_ERR(bd) == -EEXIST) {
+-              kfree(name);
+-              name = kasprintf(GFP_KERNEL, "card%d-%s-backlight",
+-                               i915->drm.primary->index, connector->base.name);
+-              if (!name)
+-                      return -ENOMEM;
+-
+-              bd = backlight_device_register(name, connector->base.kdev, connector,
+-                                             &intel_backlight_device_ops, &props);
+-      }
+-
+-      if (IS_ERR(bd)) {
+-              drm_err(&i915->drm,
+-                      "[CONNECTOR:%d:%s] backlight device %s register failed: %ld\n",
+-                      connector->base.base.id, connector->base.name, name, PTR_ERR(bd));
+-              ret = PTR_ERR(bd);
+-              goto out;
+-      }
+-
+-      panel->backlight.device = bd;
+-
+-      drm_dbg_kms(&i915->drm,
+-                  "[CONNECTOR:%d:%s] backlight device %s registered\n",
+-                  connector->base.base.id, connector->base.name, name);
+-
+-out:
+-      kfree(name);
+-
+-      return ret;
+-}
+-
+-void intel_backlight_device_unregister(struct intel_connector *connector)
+-{
+-      struct intel_panel *panel = &connector->panel;
+-
+-      if (panel->backlight.device) {
+-              backlight_device_unregister(panel->backlight.device);
+-              panel->backlight.device = NULL;
+-      }
+-}
+-#endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
+-
+-/*
+- * CNP: PWM clock frequency is 19.2 MHz or 24 MHz.
+- *      PWM increment = 1
+- */
+-static u32 cnp_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-
+-      return DIV_ROUND_CLOSEST(KHz(RUNTIME_INFO(dev_priv)->rawclk_freq),
+-                               pwm_freq_hz);
+-}
+-
+-/*
+- * BXT: PWM clock frequency = 19.2 MHz.
+- */
+-static u32 bxt_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
+-{
+-      return DIV_ROUND_CLOSEST(KHz(19200), pwm_freq_hz);
+-}
+-
+-/*
+- * SPT: This value represents the period of the PWM stream in clock periods
+- * multiplied by 16 (default increment) or 128 (alternate increment selected in
+- * SCHICKEN_1 bit 0). PWM clock is 24 MHz.
+- */
+-static u32 spt_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
+-{
+-      struct intel_panel *panel = &connector->panel;
+-      u32 mul;
+-
+-      if (panel->backlight.alternate_pwm_increment)
+-              mul = 128;
+-      else
+-              mul = 16;
+-
+-      return DIV_ROUND_CLOSEST(MHz(24), pwm_freq_hz * mul);
+-}
+-
+-/*
+- * LPT: This value represents the period of the PWM stream in clock periods
+- * multiplied by 128 (default increment) or 16 (alternate increment, selected in
+- * LPT SOUTH_CHICKEN2 register bit 5).
+- */
+-static u32 lpt_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 mul, clock;
+-
+-      if (panel->backlight.alternate_pwm_increment)
+-              mul = 16;
+-      else
+-              mul = 128;
+-
+-      if (HAS_PCH_LPT_H(dev_priv))
+-              clock = MHz(135); /* LPT:H */
+-      else
+-              clock = MHz(24); /* LPT:LP */
+-
+-      return DIV_ROUND_CLOSEST(clock, pwm_freq_hz * mul);
+-}
+-
+-/*
+- * ILK/SNB/IVB: This value represents the period of the PWM stream in PCH
+- * display raw clocks multiplied by 128.
+- */
+-static u32 pch_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-
+-      return DIV_ROUND_CLOSEST(KHz(RUNTIME_INFO(dev_priv)->rawclk_freq),
+-                               pwm_freq_hz * 128);
+-}
+-
+-/*
+- * Gen2: This field determines the number of time base events (display core
+- * clock frequency/32) in total for a complete cycle of modulated backlight
+- * control.
+- *
+- * Gen3: A time base event equals the display core clock ([DevPNV] HRAW clock)
+- * divided by 32.
+- */
+-static u32 i9xx_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      int clock;
+-
+-      if (IS_PINEVIEW(dev_priv))
+-              clock = KHz(RUNTIME_INFO(dev_priv)->rawclk_freq);
+-      else
+-              clock = KHz(dev_priv->cdclk.hw.cdclk);
+-
+-      return DIV_ROUND_CLOSEST(clock, pwm_freq_hz * 32);
+-}
+-
+-/*
+- * Gen4: This value represents the period of the PWM stream in display core
+- * clocks ([DevCTG] HRAW clocks) multiplied by 128.
+- *
+- */
+-static u32 i965_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      int clock;
+-
+-      if (IS_G4X(dev_priv))
+-              clock = KHz(RUNTIME_INFO(dev_priv)->rawclk_freq);
+-      else
+-              clock = KHz(dev_priv->cdclk.hw.cdclk);
+-
+-      return DIV_ROUND_CLOSEST(clock, pwm_freq_hz * 128);
+-}
+-
+-/*
+- * VLV: This value represents the period of the PWM stream in display core
+- * clocks ([DevCTG] 200MHz HRAW clocks) multiplied by 128 or 25MHz S0IX clocks
+- * multiplied by 16. CHV uses a 19.2MHz S0IX clock.
+- */
+-static u32 vlv_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      int mul, clock;
+-
+-      if ((intel_de_read(dev_priv, CBR1_VLV) & CBR_PWM_CLOCK_MUX_SELECT) == 0) {
+-              if (IS_CHERRYVIEW(dev_priv))
+-                      clock = KHz(19200);
+-              else
+-                      clock = MHz(25);
+-              mul = 16;
+-      } else {
+-              clock = KHz(RUNTIME_INFO(dev_priv)->rawclk_freq);
+-              mul = 128;
+-      }
+-
+-      return DIV_ROUND_CLOSEST(clock, pwm_freq_hz * mul);
+-}
+-
+-static u16 get_vbt_pwm_freq(struct drm_i915_private *dev_priv)
+-{
+-      u16 pwm_freq_hz = dev_priv->vbt.backlight.pwm_freq_hz;
+-
+-      if (pwm_freq_hz) {
+-              drm_dbg_kms(&dev_priv->drm,
+-                          "VBT defined backlight frequency %u Hz\n",
+-                          pwm_freq_hz);
+-      } else {
+-              pwm_freq_hz = 200;
+-              drm_dbg_kms(&dev_priv->drm,
+-                          "default backlight frequency %u Hz\n",
+-                          pwm_freq_hz);
+-      }
+-
+-      return pwm_freq_hz;
+-}
+-
+-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);
+-      u32 pwm;
+-
+-      if (!panel->backlight.pwm_funcs->hz_to_pwm) {
+-              drm_dbg_kms(&dev_priv->drm,
+-                          "backlight frequency conversion not supported\n");
+-              return 0;
+-      }
+-
+-      pwm = panel->backlight.pwm_funcs->hz_to_pwm(connector, pwm_freq_hz);
+-      if (!pwm) {
+-              drm_dbg_kms(&dev_priv->drm,
+-                          "backlight frequency conversion failed\n");
+-              return 0;
+-      }
+-
+-      return pwm;
+-}
+-
+-/*
+- * Note: The setup hooks can't assume pipe is set!
+- */
+-static u32 get_backlight_min_vbt(struct intel_connector *connector)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      int min;
+-
+-      drm_WARN_ON(&dev_priv->drm, panel->backlight.pwm_level_max == 0);
+-
+-      /*
+-       * XXX: If the vbt value is 255, it makes min equal to max, which leads
+-       * to problems. There are such machines out there. Either our
+-       * interpretation is wrong or the vbt has bogus data. Or both. Safeguard
+-       * 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) {
+-              drm_dbg_kms(&dev_priv->drm,
+-                          "clamping VBT min backlight %d/255 to %d/255\n",
+-                          dev_priv->vbt.backlight.min_brightness, min);
+-      }
+-
+-      /* vbt value is a coefficient in range [0..255] */
+-      return scale(min, 0, 255, 0, panel->backlight.pwm_level_max);
+-}
+-
+-static int lpt_setup_backlight(struct intel_connector *connector, enum pipe unused)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 cpu_ctl2, pch_ctl1, pch_ctl2, val;
+-      bool alt, cpu_mode;
+-
+-      if (HAS_PCH_LPT(dev_priv))
+-              alt = intel_de_read(dev_priv, SOUTH_CHICKEN2) & LPT_PWM_GRANULARITY;
+-      else
+-              alt = intel_de_read(dev_priv, SOUTH_CHICKEN1) & SPT_PWM_GRANULARITY;
+-      panel->backlight.alternate_pwm_increment = alt;
+-
+-      pch_ctl1 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL1);
+-      panel->backlight.active_low_pwm = pch_ctl1 & BLM_PCH_POLARITY;
+-
+-      pch_ctl2 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL2);
+-      panel->backlight.pwm_level_max = pch_ctl2 >> 16;
+-
+-      cpu_ctl2 = intel_de_read(dev_priv, BLC_PWM_CPU_CTL2);
+-
+-      if (!panel->backlight.pwm_level_max)
+-              panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
+-
+-      if (!panel->backlight.pwm_level_max)
+-              return -ENODEV;
+-
+-      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
+-
+-      panel->backlight.pwm_enabled = pch_ctl1 & BLM_PCH_PWM_ENABLE;
+-
+-      cpu_mode = panel->backlight.pwm_enabled && HAS_PCH_LPT(dev_priv) &&
+-                 !(pch_ctl1 & BLM_PCH_OVERRIDE_ENABLE) &&
+-                 (cpu_ctl2 & BLM_PWM_ENABLE);
+-
+-      if (cpu_mode) {
+-              val = pch_get_backlight(connector, unused);
+-
+-              drm_dbg_kms(&dev_priv->drm,
+-                          "CPU backlight register was enabled, switching to PCH override\n");
+-
+-              /* Write converted CPU PWM value to PCH override register */
+-              lpt_set_backlight(connector->base.state, val);
+-              intel_de_write(dev_priv, BLC_PWM_PCH_CTL1,
+-                             pch_ctl1 | BLM_PCH_OVERRIDE_ENABLE);
+-
+-              intel_de_write(dev_priv, BLC_PWM_CPU_CTL2,
+-                             cpu_ctl2 & ~BLM_PWM_ENABLE);
+-      }
+-
+-      return 0;
+-}
+-
+-static int pch_setup_backlight(struct intel_connector *connector, enum pipe unused)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 cpu_ctl2, pch_ctl1, pch_ctl2;
+-
+-      pch_ctl1 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL1);
+-      panel->backlight.active_low_pwm = pch_ctl1 & BLM_PCH_POLARITY;
+-
+-      pch_ctl2 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL2);
+-      panel->backlight.pwm_level_max = pch_ctl2 >> 16;
+-
+-      if (!panel->backlight.pwm_level_max)
+-              panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
+-
+-      if (!panel->backlight.pwm_level_max)
+-              return -ENODEV;
+-
+-      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
+-
+-      cpu_ctl2 = intel_de_read(dev_priv, BLC_PWM_CPU_CTL2);
+-      panel->backlight.pwm_enabled = (cpu_ctl2 & BLM_PWM_ENABLE) &&
+-              (pch_ctl1 & BLM_PCH_PWM_ENABLE);
+-
+-      return 0;
+-}
+-
+-static int i9xx_setup_backlight(struct intel_connector *connector, enum pipe unused)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 ctl, val;
+-
+-      ctl = intel_de_read(dev_priv, BLC_PWM_CTL);
+-
+-      if (DISPLAY_VER(dev_priv) == 2 || IS_I915GM(dev_priv) || IS_I945GM(dev_priv))
+-              panel->backlight.combination_mode = ctl & BLM_LEGACY_MODE;
+-
+-      if (IS_PINEVIEW(dev_priv))
+-              panel->backlight.active_low_pwm = ctl & BLM_POLARITY_PNV;
+-
+-      panel->backlight.pwm_level_max = ctl >> 17;
+-
+-      if (!panel->backlight.pwm_level_max) {
+-              panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
+-              panel->backlight.pwm_level_max >>= 1;
+-      }
+-
+-      if (!panel->backlight.pwm_level_max)
+-              return -ENODEV;
+-
+-      if (panel->backlight.combination_mode)
+-              panel->backlight.pwm_level_max *= 0xff;
+-
+-      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
+-
+-      val = i9xx_get_backlight(connector, unused);
+-      val = intel_panel_invert_pwm_level(connector, val);
+-      val = clamp(val, panel->backlight.pwm_level_min, panel->backlight.pwm_level_max);
+-
+-      panel->backlight.pwm_enabled = val != 0;
+-
+-      return 0;
+-}
+-
+-static int i965_setup_backlight(struct intel_connector *connector, enum pipe unused)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 ctl, ctl2;
+-
+-      ctl2 = intel_de_read(dev_priv, BLC_PWM_CTL2);
+-      panel->backlight.combination_mode = ctl2 & BLM_COMBINATION_MODE;
+-      panel->backlight.active_low_pwm = ctl2 & BLM_POLARITY_I965;
+-
+-      ctl = intel_de_read(dev_priv, BLC_PWM_CTL);
+-      panel->backlight.pwm_level_max = ctl >> 16;
+-
+-      if (!panel->backlight.pwm_level_max)
+-              panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
+-
+-      if (!panel->backlight.pwm_level_max)
+-              return -ENODEV;
+-
+-      if (panel->backlight.combination_mode)
+-              panel->backlight.pwm_level_max *= 0xff;
+-
+-      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
+-
+-      panel->backlight.pwm_enabled = ctl2 & BLM_PWM_ENABLE;
+-
+-      return 0;
+-}
+-
+-static int vlv_setup_backlight(struct intel_connector *connector, enum pipe pipe)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 ctl, ctl2;
+-
+-      if (drm_WARN_ON(&dev_priv->drm, pipe != PIPE_A && pipe != PIPE_B))
+-              return -ENODEV;
+-
+-      ctl2 = intel_de_read(dev_priv, VLV_BLC_PWM_CTL2(pipe));
+-      panel->backlight.active_low_pwm = ctl2 & BLM_POLARITY_I965;
+-
+-      ctl = intel_de_read(dev_priv, VLV_BLC_PWM_CTL(pipe));
+-      panel->backlight.pwm_level_max = ctl >> 16;
+-
+-      if (!panel->backlight.pwm_level_max)
+-              panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
+-
+-      if (!panel->backlight.pwm_level_max)
+-              return -ENODEV;
+-
+-      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
+-
+-      panel->backlight.pwm_enabled = ctl2 & BLM_PWM_ENABLE;
+-
+-      return 0;
+-}
+-
+-static int
+-bxt_setup_backlight(struct intel_connector *connector, enum pipe unused)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 pwm_ctl, val;
+-
+-      panel->backlight.controller = dev_priv->vbt.backlight.controller;
+-
+-      pwm_ctl = intel_de_read(dev_priv,
+-                              BXT_BLC_PWM_CTL(panel->backlight.controller));
+-
+-      /* Controller 1 uses the utility pin. */
+-      if (panel->backlight.controller == 1) {
+-              val = intel_de_read(dev_priv, UTIL_PIN_CTL);
+-              panel->backlight.util_pin_active_low =
+-                                      val & UTIL_PIN_POLARITY;
+-      }
+-
+-      panel->backlight.active_low_pwm = pwm_ctl & BXT_BLC_PWM_POLARITY;
+-      panel->backlight.pwm_level_max =
+-              intel_de_read(dev_priv, BXT_BLC_PWM_FREQ(panel->backlight.controller));
+-
+-      if (!panel->backlight.pwm_level_max)
+-              panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
+-
+-      if (!panel->backlight.pwm_level_max)
+-              return -ENODEV;
+-
+-      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
+-
+-      panel->backlight.pwm_enabled = pwm_ctl & BXT_BLC_PWM_ENABLE;
+-
+-      return 0;
+-}
+-
+-static int
+-cnp_setup_backlight(struct intel_connector *connector, enum pipe unused)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-      u32 pwm_ctl;
+-
+-      /*
+-       * CNP has the BXT implementation of backlight, but with only one
+-       * controller. TODO: ICP has multiple controllers but we only use
+-       * controller 0 for now.
+-       */
+-      panel->backlight.controller = 0;
+-
+-      pwm_ctl = intel_de_read(dev_priv,
+-                              BXT_BLC_PWM_CTL(panel->backlight.controller));
+-
+-      panel->backlight.active_low_pwm = pwm_ctl & BXT_BLC_PWM_POLARITY;
+-      panel->backlight.pwm_level_max =
+-              intel_de_read(dev_priv, BXT_BLC_PWM_FREQ(panel->backlight.controller));
+-
+-      if (!panel->backlight.pwm_level_max)
+-              panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
+-
+-      if (!panel->backlight.pwm_level_max)
+-              return -ENODEV;
+-
+-      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
+-
+-      panel->backlight.pwm_enabled = pwm_ctl & BXT_BLC_PWM_ENABLE;
+-
+-      return 0;
+-}
+-
+-static int ext_pwm_setup_backlight(struct intel_connector *connector,
+-                                 enum pipe pipe)
+-{
+-      struct drm_device *dev = connector->base.dev;
+-      struct drm_i915_private *dev_priv = to_i915(dev);
+-      struct intel_panel *panel = &connector->panel;
+-      const char *desc;
+-      u32 level;
+-
+-      /* Get the right PWM chip for DSI backlight according to VBT */
+-      if (dev_priv->vbt.dsi.config->pwm_blc == PPS_BLC_PMIC) {
+-              panel->backlight.pwm = pwm_get(dev->dev, "pwm_pmic_backlight");
+-              desc = "PMIC";
+-      } else {
+-              panel->backlight.pwm = pwm_get(dev->dev, "pwm_soc_backlight");
+-              desc = "SoC";
+-      }
+-
+-      if (IS_ERR(panel->backlight.pwm)) {
+-              drm_err(&dev_priv->drm, "Failed to get the %s PWM chip\n",
+-                      desc);
+-              panel->backlight.pwm = NULL;
+-              return -ENODEV;
+-      }
+-
+-      panel->backlight.pwm_level_max = 100; /* 100% */
+-      panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
+-
+-      if (pwm_is_enabled(panel->backlight.pwm)) {
+-              /* PWM is already enabled, use existing settings */
+-              pwm_get_state(panel->backlight.pwm, &panel->backlight.pwm_state);
+-
+-              level = pwm_get_relative_duty_cycle(&panel->backlight.pwm_state,
+-                                                  100);
+-              level = intel_panel_invert_pwm_level(connector, level);
+-              panel->backlight.pwm_enabled = true;
+-
+-              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);
+-      } 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);
+-      }
+-
+-      drm_info(&dev_priv->drm, "Using %s PWM for LCD backlight control\n",
+-               desc);
+-      return 0;
+-}
+-
+-static void intel_pwm_set_backlight(const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      panel->backlight.pwm_funcs->set(conn_state,
+-                                     intel_panel_invert_pwm_level(connector, level));
+-}
+-
+-static u32 intel_pwm_get_backlight(struct intel_connector *connector, enum pipe pipe)
+-{
+-      struct intel_panel *panel = &connector->panel;
+-
+-      return intel_panel_invert_pwm_level(connector,
+-                                          panel->backlight.pwm_funcs->get(connector, pipe));
+-}
+-
+-static void intel_pwm_enable_backlight(const struct intel_crtc_state *crtc_state,
+-                                     const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      panel->backlight.pwm_funcs->enable(crtc_state, conn_state,
+-                                         intel_panel_invert_pwm_level(connector, level));
+-}
+-
+-static void intel_pwm_disable_backlight(const struct drm_connector_state *conn_state, u32 level)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      panel->backlight.pwm_funcs->disable(conn_state,
+-                                          intel_panel_invert_pwm_level(connector, level));
+-}
+-
+-static int intel_pwm_setup_backlight(struct intel_connector *connector, enum pipe pipe)
+-{
+-      struct intel_panel *panel = &connector->panel;
+-      int ret = panel->backlight.pwm_funcs->setup(connector, pipe);
+-
+-      if (ret < 0)
+-              return ret;
+-
+-      panel->backlight.min = panel->backlight.pwm_level_min;
+-      panel->backlight.max = panel->backlight.pwm_level_max;
+-      panel->backlight.level = intel_pwm_get_backlight(connector, pipe);
+-      panel->backlight.enabled = panel->backlight.pwm_enabled;
+-
+-      return 0;
+-}
+-
+-void intel_panel_update_backlight(struct intel_atomic_state *state,
+-                                struct intel_encoder *encoder,
+-                                const struct intel_crtc_state *crtc_state,
+-                                const struct drm_connector_state *conn_state)
+-{
+-      struct intel_connector *connector = to_intel_connector(conn_state->connector);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-      struct intel_panel *panel = &connector->panel;
+-
+-      if (!panel->backlight.present)
+-              return;
+-
+-      mutex_lock(&dev_priv->backlight_lock);
+-      if (!panel->backlight.enabled)
+-              __intel_panel_enable_backlight(crtc_state, conn_state);
+-
+-      mutex_unlock(&dev_priv->backlight_lock);
+-}
+-
+-int intel_panel_setup_backlight(struct drm_connector *connector, enum pipe pipe)
+-{
+-      struct drm_i915_private *dev_priv = to_i915(connector->dev);
+-      struct intel_connector *intel_connector = to_intel_connector(connector);
+-      struct intel_panel *panel = &intel_connector->panel;
+-      int ret;
+-
+-      if (!dev_priv->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");
+-              } else {
+-                      drm_dbg_kms(&dev_priv->drm,
+-                                  "no backlight present per VBT\n");
+-                      return 0;
+-              }
+-      }
+-
+-      /* ensure intel_panel has been initialized first */
+-      if (drm_WARN_ON(&dev_priv->drm, !panel->backlight.funcs))
+-              return -ENODEV;
+-
+-      /* set level and max in panel struct */
+-      mutex_lock(&dev_priv->backlight_lock);
+-      ret = panel->backlight.funcs->setup(intel_connector, pipe);
+-      mutex_unlock(&dev_priv->backlight_lock);
+-
+-      if (ret) {
+-              drm_dbg_kms(&dev_priv->drm,
+-                          "failed to setup backlight for connector %s\n",
+-                          connector->name);
+-              return ret;
+-      }
+-
+-      panel->backlight.present = true;
+-
+-      drm_dbg_kms(&dev_priv->drm,
+-                  "Connector %s backlight initialized, %s, brightness %u/%u\n",
+-                  connector->name,
+-                  enableddisabled(panel->backlight.enabled),
+-                  panel->backlight.level, panel->backlight.max);
+-
+-      return 0;
+-}
+-
+-static void intel_panel_destroy_backlight(struct intel_panel *panel)
+-{
+-      /* dispose of the pwm */
+-      if (panel->backlight.pwm)
+-              pwm_put(panel->backlight.pwm);
+-
+-      panel->backlight.present = false;
+-}
+-
+-static const struct intel_panel_bl_funcs bxt_pwm_funcs = {
+-      .setup = bxt_setup_backlight,
+-      .enable = bxt_enable_backlight,
+-      .disable = bxt_disable_backlight,
+-      .set = bxt_set_backlight,
+-      .get = bxt_get_backlight,
+-      .hz_to_pwm = bxt_hz_to_pwm,
+-};
+-
+-static const struct intel_panel_bl_funcs cnp_pwm_funcs = {
+-      .setup = cnp_setup_backlight,
+-      .enable = cnp_enable_backlight,
+-      .disable = cnp_disable_backlight,
+-      .set = bxt_set_backlight,
+-      .get = bxt_get_backlight,
+-      .hz_to_pwm = cnp_hz_to_pwm,
+-};
+-
+-static const struct intel_panel_bl_funcs lpt_pwm_funcs = {
+-      .setup = lpt_setup_backlight,
+-      .enable = lpt_enable_backlight,
+-      .disable = lpt_disable_backlight,
+-      .set = lpt_set_backlight,
+-      .get = lpt_get_backlight,
+-      .hz_to_pwm = lpt_hz_to_pwm,
+-};
+-
+-static const struct intel_panel_bl_funcs spt_pwm_funcs = {
+-      .setup = lpt_setup_backlight,
+-      .enable = lpt_enable_backlight,
+-      .disable = lpt_disable_backlight,
+-      .set = lpt_set_backlight,
+-      .get = lpt_get_backlight,
+-      .hz_to_pwm = spt_hz_to_pwm,
+-};
+-
+-static const struct intel_panel_bl_funcs pch_pwm_funcs = {
+-      .setup = pch_setup_backlight,
+-      .enable = pch_enable_backlight,
+-      .disable = pch_disable_backlight,
+-      .set = pch_set_backlight,
+-      .get = pch_get_backlight,
+-      .hz_to_pwm = pch_hz_to_pwm,
+-};
+-
+-static const struct intel_panel_bl_funcs ext_pwm_funcs = {
+-      .setup = ext_pwm_setup_backlight,
+-      .enable = ext_pwm_enable_backlight,
+-      .disable = ext_pwm_disable_backlight,
+-      .set = ext_pwm_set_backlight,
+-      .get = ext_pwm_get_backlight,
+-};
+-
+-static const struct intel_panel_bl_funcs vlv_pwm_funcs = {
+-      .setup = vlv_setup_backlight,
+-      .enable = vlv_enable_backlight,
+-      .disable = vlv_disable_backlight,
+-      .set = vlv_set_backlight,
+-      .get = vlv_get_backlight,
+-      .hz_to_pwm = vlv_hz_to_pwm,
+-};
+-
+-static const struct intel_panel_bl_funcs i965_pwm_funcs = {
+-      .setup = i965_setup_backlight,
+-      .enable = i965_enable_backlight,
+-      .disable = i965_disable_backlight,
+-      .set = i9xx_set_backlight,
+-      .get = i9xx_get_backlight,
+-      .hz_to_pwm = i965_hz_to_pwm,
+-};
+-
+-static const struct intel_panel_bl_funcs i9xx_pwm_funcs = {
+-      .setup = i9xx_setup_backlight,
+-      .enable = i9xx_enable_backlight,
+-      .disable = i9xx_disable_backlight,
+-      .set = i9xx_set_backlight,
+-      .get = i9xx_get_backlight,
+-      .hz_to_pwm = i9xx_hz_to_pwm,
+-};
+-
+-static const struct intel_panel_bl_funcs pwm_bl_funcs = {
+-      .setup = intel_pwm_setup_backlight,
+-      .enable = intel_pwm_enable_backlight,
+-      .disable = intel_pwm_disable_backlight,
+-      .set = intel_pwm_set_backlight,
+-      .get = intel_pwm_get_backlight,
+-};
+-
+-/* Set up chip specific backlight functions */
+-static void
+-intel_panel_init_backlight_funcs(struct intel_panel *panel)
+-{
+-      struct intel_connector *connector =
+-              container_of(panel, struct intel_connector, panel);
+-      struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+-
+-      if (connector->base.connector_type == DRM_MODE_CONNECTOR_DSI &&
+-          intel_dsi_dcs_init_backlight_funcs(connector) == 0)
+-              return;
+-
+-      if (IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv)) {
+-              panel->backlight.pwm_funcs = &bxt_pwm_funcs;
+-      } else if (INTEL_PCH_TYPE(dev_priv) >= PCH_CNP) {
+-              panel->backlight.pwm_funcs = &cnp_pwm_funcs;
+-      } else if (INTEL_PCH_TYPE(dev_priv) >= PCH_LPT) {
+-              if (HAS_PCH_LPT(dev_priv))
+-                      panel->backlight.pwm_funcs = &lpt_pwm_funcs;
+-              else
+-                      panel->backlight.pwm_funcs = &spt_pwm_funcs;
+-      } else if (HAS_PCH_SPLIT(dev_priv)) {
+-              panel->backlight.pwm_funcs = &pch_pwm_funcs;
+-      } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
+-              if (connector->base.connector_type == DRM_MODE_CONNECTOR_DSI) {
+-                      panel->backlight.pwm_funcs = &ext_pwm_funcs;
+-              } else {
+-                      panel->backlight.pwm_funcs = &vlv_pwm_funcs;
+-              }
+-      } else if (DISPLAY_VER(dev_priv) == 4) {
+-              panel->backlight.pwm_funcs = &i965_pwm_funcs;
+-      } else {
+-              panel->backlight.pwm_funcs = &i9xx_pwm_funcs;
+-      }
+-
+-      if (connector->base.connector_type == DRM_MODE_CONNECTOR_eDP &&
+-          intel_dp_aux_init_backlight_funcs(connector) == 0)
+-              return;
+-
+-      /* We're using a standard PWM backlight interface */
+-      panel->backlight.funcs = &pwm_bl_funcs;
+-}
+-
+ enum drm_connector_status
+ intel_panel_detect(struct drm_connector *connector, bool force)
+ {
+diff --git a/drivers/gpu/drm/i915/display/intel_panel.h b/drivers/gpu/drm/i915/display/intel_panel.h
+index 1d340f77bffc7..67dbb15026bf1 100644
+--- a/drivers/gpu/drm/i915/display/intel_panel.h
++++ b/drivers/gpu/drm/i915/display/intel_panel.h
+@@ -8,15 +8,13 @@
+ #include <linux/types.h>
+-#include "intel_display.h"
+-
++enum drm_connector_status;
+ struct drm_connector;
+ struct drm_connector_state;
+ struct drm_display_mode;
++struct drm_i915_private;
+ struct intel_connector;
+-struct intel_crtc;
+ struct intel_crtc_state;
+-struct intel_encoder;
+ struct intel_panel;
+ int intel_panel_init(struct intel_panel *panel,
+@@ -31,17 +29,6 @@ int intel_pch_panel_fitting(struct intel_crtc_state *crtc_state,
+                           const struct drm_connector_state *conn_state);
+ int intel_gmch_panel_fitting(struct intel_crtc_state *crtc_state,
+                            const struct drm_connector_state *conn_state);
+-void intel_panel_set_backlight_acpi(const struct drm_connector_state *conn_state,
+-                                  u32 level, u32 max);
+-int intel_panel_setup_backlight(struct drm_connector *connector,
+-                              enum pipe pipe);
+-void intel_panel_enable_backlight(const struct intel_crtc_state *crtc_state,
+-                                const struct drm_connector_state *conn_state);
+-void intel_panel_update_backlight(struct intel_atomic_state *state,
+-                                struct intel_encoder *encoder,
+-                                const struct intel_crtc_state *crtc_state,
+-                                const struct drm_connector_state *conn_state);
+-void intel_panel_disable_backlight(const struct drm_connector_state *old_conn_state);
+ struct drm_display_mode *
+ intel_panel_edid_downclock_mode(struct intel_connector *connector,
+                               const struct drm_display_mode *fixed_mode);
+@@ -49,22 +36,5 @@ struct drm_display_mode *
+ intel_panel_edid_fixed_mode(struct intel_connector *connector);
+ struct drm_display_mode *
+ intel_panel_vbt_fixed_mode(struct intel_connector *connector);
+-void intel_panel_set_pwm_level(const struct drm_connector_state *conn_state, u32 level);
+-u32 intel_panel_invert_pwm_level(struct intel_connector *connector, u32 level);
+-u32 intel_panel_backlight_level_to_pwm(struct intel_connector *connector, u32 level);
+-u32 intel_panel_backlight_level_from_pwm(struct intel_connector *connector, u32 val);
+-
+-#if IS_ENABLED(CONFIG_BACKLIGHT_CLASS_DEVICE)
+-int intel_backlight_device_register(struct intel_connector *connector);
+-void intel_backlight_device_unregister(struct intel_connector *connector);
+-#else /* CONFIG_BACKLIGHT_CLASS_DEVICE */
+-static inline int intel_backlight_device_register(struct intel_connector *connector)
+-{
+-      return 0;
+-}
+-static inline void intel_backlight_device_unregister(struct intel_connector *connector)
+-{
+-}
+-#endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
+ #endif /* __INTEL_PANEL_H__ */
+diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c
+index 0ee4ff341e25d..b27738df447d0 100644
+--- a/drivers/gpu/drm/i915/display/vlv_dsi.c
++++ b/drivers/gpu/drm/i915/display/vlv_dsi.c
+@@ -32,6 +32,7 @@
+ #include "i915_drv.h"
+ #include "intel_atomic.h"
++#include "intel_backlight.h"
+ #include "intel_connector.h"
+ #include "intel_crtc.h"
+ #include "intel_de.h"
+-- 
+2.35.1
+
diff --git a/queue-5.15/drm-i915-display-avoid-warnings-when-registering-dua.patch b/queue-5.15/drm-i915-display-avoid-warnings-when-registering-dua.patch
new file mode 100644 (file)
index 0000000..a47611a
--- /dev/null
@@ -0,0 +1,83 @@
+From be05bfb8b91878d9e5864a1e42e6887273a71748 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 8 Aug 2022 09:27:50 +0530
+Subject: drm/i915/display: avoid warnings when registering dual panel
+ backlight
+
+From: Arun R Murthy <arun.r.murthy@intel.com>
+
+[ Upstream commit 868e8e5156a1f8d92ca83fdbac6fd52798650792 ]
+
+Commit 20f85ef89d94 ("drm/i915/backlight: use unique backlight device
+names") added support for multiple backlight devices on dual panel
+systems, but did so with error handling on -EEXIST from
+backlight_device_register(). Unfortunately, that triggered a warning in
+dmesg all the way down from sysfs_add_file_mode_ns() and
+sysfs_warn_dup().
+
+Instead of optimistically always attempting to register with the default
+name ("intel_backlight", which we have to retain for backward
+compatibility), check if a backlight device with the name exists first,
+and, if so, use the card and connector based name.
+
+v2: reworked on top of the patch commit 20f85ef89d94
+("drm/i915/backlight: use unique backlight device names")
+v3: fixed the ref count leak(Jani N)
+
+Fixes: 20f85ef89d94 ("drm/i915/backlight: use unique backlight device names")
+Signed-off-by: Arun R Murthy <arun.r.murthy@intel.com>
+Signed-off-by: Jani Nikula <jani.nikula@intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20220808035750.3111046-1-arun.r.murthy@intel.com
+(cherry picked from commit 4234ea30051200fc6016de10e4d58369e60b38f1)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../gpu/drm/i915/display/intel_backlight.c    | 26 +++++++++----------
+ 1 file changed, 12 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_backlight.c b/drivers/gpu/drm/i915/display/intel_backlight.c
+index 4b0086ee48519..60f91ac7d1427 100644
+--- a/drivers/gpu/drm/i915/display/intel_backlight.c
++++ b/drivers/gpu/drm/i915/display/intel_backlight.c
+@@ -966,26 +966,24 @@ int intel_backlight_device_register(struct intel_connector *connector)
+       if (!name)
+               return -ENOMEM;
+-      bd = backlight_device_register(name, connector->base.kdev, connector,
+-                                     &intel_backlight_device_ops, &props);
+-
+-      /*
+-       * Using the same name independent of the drm device or connector
+-       * prevents registration of multiple backlight devices in the
+-       * driver. However, we need to use the default name for backward
+-       * compatibility. Use unique names for subsequent backlight devices as a
+-       * fallback when the default name already exists.
+-       */
+-      if (IS_ERR(bd) && PTR_ERR(bd) == -EEXIST) {
++      bd = backlight_device_get_by_name(name);
++      if (bd) {
++              put_device(&bd->dev);
++              /*
++               * Using the same name independent of the drm device or connector
++               * prevents registration of multiple backlight devices in the
++               * driver. However, we need to use the default name for backward
++               * compatibility. Use unique names for subsequent backlight devices as a
++               * fallback when the default name already exists.
++               */
+               kfree(name);
+               name = kasprintf(GFP_KERNEL, "card%d-%s-backlight",
+                                i915->drm.primary->index, connector->base.name);
+               if (!name)
+                       return -ENOMEM;
+-
+-              bd = backlight_device_register(name, connector->base.kdev, connector,
+-                                             &intel_backlight_device_ops, &props);
+       }
++      bd = backlight_device_register(name, connector->base.kdev, connector,
++                                     &intel_backlight_device_ops, &props);
+       if (IS_ERR(bd)) {
+               drm_err(&i915->drm,
+-- 
+2.35.1
+
diff --git a/queue-5.15/drm-msm-dp-delete-dp_recovered_clock_out_en-to-fix-t.patch b/queue-5.15/drm-msm-dp-delete-dp_recovered_clock_out_en-to-fix-t.patch
new file mode 100644 (file)
index 0000000..b32c128
--- /dev/null
@@ -0,0 +1,65 @@
+From ca7ac3d9b199a3cc914f38d1551e37e8b1f5b992 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 11 Aug 2022 15:57:50 -0700
+Subject: drm/msm/dp: delete DP_RECOVERED_CLOCK_OUT_EN to fix tps4
+
+From: Kuogee Hsieh <quic_khsieh@quicinc.com>
+
+[ Upstream commit 032d57960176ac01cc5adff5bcc5eb51317f8781 ]
+
+Data Symbols scrambled is required for tps4 at link training 2.
+Therefore SCRAMBLING_DISABLE bit should not be set for tps4 to
+work.
+
+RECOVERED_CLOCK_OUT_EN is for enable simple EYE test for jitter
+measurement with minimal equipment for embedded applications purpose
+and is not required to be set during normal operation. Current
+implementation always have RECOVERED_CLOCK_OUT_EN bit set which
+cause SCRAMBLING_DISABLE bit wrongly set at tps4 which prevent
+tps4 from working.
+
+This patch delete setting RECOVERED_CLOCK_OUT_EN to fix
+SCRAMBLING_DISABLE be wrongly set at tps4.
+
+Changes in v2:
+-- fix Fixes tag
+
+Changes in v3:
+-- revise commit text
+
+Changes in v4:
+-- fix commit text newline
+
+Changes in v5:
+-- fix commit text line over 75 chars
+
+Fixes: c943b4948b58 ("drm/msm/dp: add displayPort driver support")
+Signed-off-by: Kuogee Hsieh <quic_khsieh@quicinc.com>
+
+Reviewed-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
+Reviewed-by: Stephen Boyd <swboyd@chromium.org>
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Patchwork: https://patchwork.freedesktop.org/patch/497194/
+Link: https://lore.kernel.org/r/1660258670-4200-1-git-send-email-quic_khsieh@quicinc.com
+Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/msm/dp/dp_ctrl.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
+index b6f4ce2a48afe..6d9eec98e0d38 100644
+--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
++++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
+@@ -1198,7 +1198,7 @@ static int dp_ctrl_link_train_2(struct dp_ctrl_private *ctrl,
+       if (ret)
+               return ret;
+-      dp_ctrl_train_pattern_set(ctrl, pattern | DP_RECOVERED_CLOCK_OUT_EN);
++      dp_ctrl_train_pattern_set(ctrl, pattern);
+       for (tries = 0; tries <= maximum_retries; tries++) {
+               drm_dp_link_train_channel_eq_delay(ctrl->aux, ctrl->panel->dpcd);
+-- 
+2.35.1
+
diff --git a/queue-5.15/drm-msm-dsi-fix-number-of-regulators-for-msm8996_dsi.patch b/queue-5.15/drm-msm-dsi-fix-number-of-regulators-for-msm8996_dsi.patch
new file mode 100644 (file)
index 0000000..6ec29f0
--- /dev/null
@@ -0,0 +1,39 @@
+From 454fd222a940ed3b358f88364e2931b2092ea4f3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Aug 2022 07:38:48 -0700
+Subject: drm/msm/dsi: Fix number of regulators for msm8996_dsi_cfg
+
+From: Douglas Anderson <dianders@chromium.org>
+
+[ Upstream commit 1e00d6ac8a3422765bae37aeac2002dfd3c0bda6 ]
+
+3 regulators are listed but the number 2 is specified. Fix it.
+
+Fixes: 3a3ff88a0fc1 ("drm/msm/dsi: Add 8x96 info in dsi_cfg")
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Reviewed-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
+Patchwork: https://patchwork.freedesktop.org/patch/496318/
+Link: https://lore.kernel.org/r/20220804073608.v4.1.I1056ee3f77f71287f333279efe4c85f88d403f65@changeid
+Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/msm/dsi/dsi_cfg.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.c b/drivers/gpu/drm/msm/dsi/dsi_cfg.c
+index 96bbc8b6d0092..2981dd7c79cc8 100644
+--- a/drivers/gpu/drm/msm/dsi/dsi_cfg.c
++++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.c
+@@ -109,7 +109,7 @@ static const char * const dsi_8996_bus_clk_names[] = {
+ static const struct msm_dsi_config msm8996_dsi_cfg = {
+       .io_offset = DSI_6G_REG_SHIFT,
+       .reg_cfg = {
+-              .num = 2,
++              .num = 3,
+               .regs = {
+                       {"vdda", 18160, 1 },    /* 1.25 V */
+                       {"vcca", 17000, 32 },   /* 0.925 V */
+-- 
+2.35.1
+
diff --git a/queue-5.15/drm-msm-dsi-fix-number-of-regulators-for-sdm660.patch b/queue-5.15/drm-msm-dsi-fix-number-of-regulators-for-sdm660.patch
new file mode 100644 (file)
index 0000000..12bce94
--- /dev/null
@@ -0,0 +1,41 @@
+From 781a71c8103583ce3fcfde10e0c7eb23f692a578 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Aug 2022 07:38:49 -0700
+Subject: drm/msm/dsi: Fix number of regulators for SDM660
+
+From: Douglas Anderson <dianders@chromium.org>
+
+[ Upstream commit a1653a75987749ba6dba94fa2e62f0f36b387d1a ]
+
+1 regulator is listed but the number 2 is specified. This presumably
+means we try to get a regulator with no name. Fix it.
+
+Fixes: 462f7017a691 ("drm/msm/dsi: Fix DSI and DSI PHY regulator config from SDM660")
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Reviewed-by: Marijn Suijten <marijn.suijten@somainline.org>
+Reviewed-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
+Patchwork: https://patchwork.freedesktop.org/patch/496323/
+Link: https://lore.kernel.org/r/20220804073608.v4.2.I94b3c3e412b7c208061349f05659e126483171b1@changeid
+Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/msm/dsi/dsi_cfg.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.c b/drivers/gpu/drm/msm/dsi/dsi_cfg.c
+index 2981dd7c79cc8..ce3901439c69c 100644
+--- a/drivers/gpu/drm/msm/dsi/dsi_cfg.c
++++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.c
+@@ -148,7 +148,7 @@ static const char * const dsi_sdm660_bus_clk_names[] = {
+ static const struct msm_dsi_config sdm660_dsi_cfg = {
+       .io_offset = DSI_6G_REG_SHIFT,
+       .reg_cfg = {
+-              .num = 2,
++              .num = 1,
+               .regs = {
+                       {"vdda", 12560, 4 },    /* 1.2 V */
+               },
+-- 
+2.35.1
+
diff --git a/queue-5.15/drm-msm-dsi-fix-the-inconsistent-indenting.patch b/queue-5.15/drm-msm-dsi-fix-the-inconsistent-indenting.patch
new file mode 100644 (file)
index 0000000..03d5def
--- /dev/null
@@ -0,0 +1,43 @@
+From adb9e9003ccf90de02b83722f30ec2250f85f0d4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 Jul 2022 09:56:22 +0800
+Subject: drm/msm/dsi: fix the inconsistent indenting
+
+From: sunliming <sunliming@kylinos.cn>
+
+[ Upstream commit 2f25a1fb4ec516c5ad67afd754334b491b9f09a5 ]
+
+Fix the inconsistent indenting in function msm_dsi_dphy_timing_calc_v3().
+
+Fix the following smatch warnings:
+
+drivers/gpu/drm/msm/dsi/phy/dsi_phy.c:350 msm_dsi_dphy_timing_calc_v3() warn: inconsistent indenting
+
+Fixes: f1fa7ff44056 ("drm/msm/dsi: implement auto PHY timing calculator for 10nm PHY")
+Reported-by: kernel test robot <lkp@intel.com>
+Signed-off-by: sunliming <sunliming@kylinos.cn>
+Reviewed-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
+Patchwork: https://patchwork.freedesktop.org/patch/494662/
+Link: https://lore.kernel.org/r/20220719015622.646718-1-sunliming@kylinos.cn
+Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/msm/dsi/phy/dsi_phy.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c
+index a878b8b079c64..6a917fe69a833 100644
+--- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c
++++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c
+@@ -347,7 +347,7 @@ int msm_dsi_dphy_timing_calc_v3(struct msm_dsi_dphy_timing *timing,
+       } else {
+               timing->shared_timings.clk_pre =
+                       linear_inter(tmax, tmin, pcnt2, 0, false);
+-                      timing->shared_timings.clk_pre_inc_by_2 = 0;
++              timing->shared_timings.clk_pre_inc_by_2 = 0;
+       }
+       timing->ta_go = 3;
+-- 
+2.35.1
+
diff --git a/queue-5.15/ethernet-rocker-fix-sleep-in-atomic-context-bug-in-n.patch b/queue-5.15/ethernet-rocker-fix-sleep-in-atomic-context-bug-in-n.patch
new file mode 100644 (file)
index 0000000..83258f3
--- /dev/null
@@ -0,0 +1,57 @@
+From b432a3b691504b025869e2219ecab99b42a48591 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 27 Aug 2022 23:38:15 +0800
+Subject: ethernet: rocker: fix sleep in atomic context bug in
+ neigh_timer_handler
+
+From: Duoming Zhou <duoming@zju.edu.cn>
+
+[ Upstream commit c0955bf957be4bead01fae1d791476260da7325d ]
+
+The function neigh_timer_handler() is a timer handler that runs in an
+atomic context. When used by rocker, neigh_timer_handler() calls
+"kzalloc(.., GFP_KERNEL)" that may sleep. As a result, the sleep in
+atomic context bug will happen. One of the processes is shown below:
+
+ofdpa_fib4_add()
+ ...
+ neigh_add_timer()
+
+(wait a timer)
+
+neigh_timer_handler()
+ neigh_release()
+  neigh_destroy()
+   rocker_port_neigh_destroy()
+    rocker_world_port_neigh_destroy()
+     ofdpa_port_neigh_destroy()
+      ofdpa_port_ipv4_neigh()
+       kzalloc(sizeof(.., GFP_KERNEL) //may sleep
+
+This patch changes the gfp_t parameter of kzalloc() from GFP_KERNEL to
+GFP_ATOMIC in order to mitigate the bug.
+
+Fixes: 00fc0c51e35b ("rocker: Change world_ops API and implementation to be switchdev independant")
+Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/rocker/rocker_ofdpa.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c
+index bc70c6abd6a5b..58cf7cc54f408 100644
+--- a/drivers/net/ethernet/rocker/rocker_ofdpa.c
++++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c
+@@ -1273,7 +1273,7 @@ static int ofdpa_port_ipv4_neigh(struct ofdpa_port *ofdpa_port,
+       bool removing;
+       int err = 0;
+-      entry = kzalloc(sizeof(*entry), GFP_KERNEL);
++      entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+       if (!entry)
+               return -ENOMEM;
+-- 
+2.35.1
+
diff --git a/queue-5.15/ieee802154-adf7242-defer-destroy_workqueue-call.patch b/queue-5.15/ieee802154-adf7242-defer-destroy_workqueue-call.patch
new file mode 100644 (file)
index 0000000..7b5e5fb
--- /dev/null
@@ -0,0 +1,60 @@
+From b42c9968c6c51551b8285ebb12c736f0a471012c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 8 Aug 2022 11:42:24 +0800
+Subject: ieee802154/adf7242: defer destroy_workqueue call
+
+From: Lin Ma <linma@zju.edu.cn>
+
+[ Upstream commit afe7116f6d3b888778ed6d95e3cf724767b9aedf ]
+
+There is a possible race condition (use-after-free) like below
+
+  (FREE)                     |  (USE)
+  adf7242_remove             |  adf7242_channel
+   cancel_delayed_work_sync  |
+    destroy_workqueue (1)    |   adf7242_cmd_rx
+                             |    mod_delayed_work (2)
+                             |
+
+The root cause for this race is that the upper layer (ieee802154) is
+unaware of this detaching event and the function adf7242_channel can
+be called without any checks.
+
+To fix this, we can add a flag write at the beginning of adf7242_remove
+and add flag check in adf7242_channel. Or we can just defer the
+destructive operation like other commit 3e0588c291d6 ("hamradio: defer
+ax25 kfree after unregister_netdev") which let the
+ieee802154_unregister_hw() to handle the synchronization. This patch
+takes the second option.
+
+Fixes: 58e9683d1475 ("net: ieee802154: adf7242: Fix OCL calibration
+runs")
+Signed-off-by: Lin Ma <linma@zju.edu.cn>
+Acked-by: Michael Hennerich <michael.hennerich@analog.com>
+Link: https://lore.kernel.org/r/20220808034224.12642-1-linma@zju.edu.cn
+Signed-off-by: Stefan Schmidt <stefan@datenfreihafen.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ieee802154/adf7242.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ieee802154/adf7242.c b/drivers/net/ieee802154/adf7242.c
+index 7db9cbd0f5ded..07adbeec19787 100644
+--- a/drivers/net/ieee802154/adf7242.c
++++ b/drivers/net/ieee802154/adf7242.c
+@@ -1310,10 +1310,11 @@ static int adf7242_remove(struct spi_device *spi)
+       debugfs_remove_recursive(lp->debugfs_root);
++      ieee802154_unregister_hw(lp->hw);
++
+       cancel_delayed_work_sync(&lp->work);
+       destroy_workqueue(lp->wqueue);
+-      ieee802154_unregister_hw(lp->hw);
+       mutex_destroy(&lp->bmux);
+       ieee802154_free_hw(lp->hw);
+-- 
+2.35.1
+
diff --git a/queue-5.15/iio-adc-mcp3911-make-use-of-the-sign-bit.patch b/queue-5.15/iio-adc-mcp3911-make-use-of-the-sign-bit.patch
new file mode 100644 (file)
index 0000000..03c271b
--- /dev/null
@@ -0,0 +1,37 @@
+From 6b8eb16fd92ae4c0fef11065f7ad3bd0c025af61 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 Jul 2022 15:07:18 +0200
+Subject: iio: adc: mcp3911: make use of the sign bit
+
+From: Marcus Folkesson <marcus.folkesson@gmail.com>
+
+[ Upstream commit 8f89e33bf040bbef66386c426198622180233178 ]
+
+The device supports negative values as well.
+
+Fixes: 3a89b289df5d ("iio: adc: add support for mcp3911")
+Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Link: https://lore.kernel.org/r/20220722130726.7627-2-marcus.folkesson@gmail.com
+Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/iio/adc/mcp3911.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c
+index e573da5397bb3..81eeb00842112 100644
+--- a/drivers/iio/adc/mcp3911.c
++++ b/drivers/iio/adc/mcp3911.c
+@@ -111,6 +111,8 @@ static int mcp3911_read_raw(struct iio_dev *indio_dev,
+               if (ret)
+                       goto out;
++              *val = sign_extend32(*val, 23);
++
+               ret = IIO_VAL_INT;
+               break;
+-- 
+2.35.1
+
diff --git a/queue-5.15/kcm-fix-strp_init-order-and-cleanup.patch b/queue-5.15/kcm-fix-strp_init-order-and-cleanup.patch
new file mode 100644 (file)
index 0000000..4bfb291
--- /dev/null
@@ -0,0 +1,74 @@
+From 5acdd07c35d39bb1becfb97823b09340a08e969e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 27 Aug 2022 11:13:14 -0700
+Subject: kcm: fix strp_init() order and cleanup
+
+From: Cong Wang <cong.wang@bytedance.com>
+
+[ Upstream commit 8fc29ff3910f3af08a7c40a75d436b5720efe2bf ]
+
+strp_init() is called just a few lines above this csk->sk_user_data
+check, it also initializes strp->work etc., therefore, it is
+unnecessary to call strp_done() to cancel the freshly initialized
+work.
+
+And if sk_user_data is already used by KCM, psock->strp should not be
+touched, particularly strp->work state, so we need to move strp_init()
+after the csk->sk_user_data check.
+
+This also makes a lockdep warning reported by syzbot go away.
+
+Reported-and-tested-by: syzbot+9fc084a4348493ef65d2@syzkaller.appspotmail.com
+Reported-by: syzbot+e696806ef96cdd2d87cd@syzkaller.appspotmail.com
+Fixes: e5571240236c ("kcm: Check if sk_user_data already set in kcm_attach")
+Fixes: dff8baa26117 ("kcm: Call strp_stop before strp_done in kcm_attach")
+Cc: Tom Herbert <tom@herbertland.com>
+Signed-off-by: Cong Wang <cong.wang@bytedance.com>
+Link: https://lore.kernel.org/r/20220827181314.193710-1-xiyou.wangcong@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/kcm/kcmsock.c | 15 +++++++--------
+ 1 file changed, 7 insertions(+), 8 deletions(-)
+
+diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c
+index 11a715d76a4f1..f780fbe82e7dc 100644
+--- a/net/kcm/kcmsock.c
++++ b/net/kcm/kcmsock.c
+@@ -1411,12 +1411,6 @@ static int kcm_attach(struct socket *sock, struct socket *csock,
+       psock->sk = csk;
+       psock->bpf_prog = prog;
+-      err = strp_init(&psock->strp, csk, &cb);
+-      if (err) {
+-              kmem_cache_free(kcm_psockp, psock);
+-              goto out;
+-      }
+-
+       write_lock_bh(&csk->sk_callback_lock);
+       /* Check if sk_user_data is already by KCM or someone else.
+@@ -1424,13 +1418,18 @@ static int kcm_attach(struct socket *sock, struct socket *csock,
+        */
+       if (csk->sk_user_data) {
+               write_unlock_bh(&csk->sk_callback_lock);
+-              strp_stop(&psock->strp);
+-              strp_done(&psock->strp);
+               kmem_cache_free(kcm_psockp, psock);
+               err = -EALREADY;
+               goto out;
+       }
++      err = strp_init(&psock->strp, csk, &cb);
++      if (err) {
++              write_unlock_bh(&csk->sk_callback_lock);
++              kmem_cache_free(kcm_psockp, psock);
++              goto out;
++      }
++
+       psock->save_data_ready = csk->sk_data_ready;
+       psock->save_write_space = csk->sk_write_space;
+       psock->save_state_change = csk->sk_state_change;
+-- 
+2.35.1
+
diff --git a/queue-5.15/mlxbf_gige-compute-mdio-period-based-on-i1clk.patch b/queue-5.15/mlxbf_gige-compute-mdio-period-based-on-i1clk.patch
new file mode 100644 (file)
index 0000000..f3750b6
--- /dev/null
@@ -0,0 +1,247 @@
+From feb5439be3de1652ce17391baf310249709eeecd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Aug 2022 11:59:16 -0400
+Subject: mlxbf_gige: compute MDIO period based on i1clk
+
+From: David Thompson <davthompson@nvidia.com>
+
+[ Upstream commit 3a1a274e933fca73fdc960cb1f60636cd285a265 ]
+
+This patch adds logic to compute the MDIO period based on
+the i1clk, and thereafter write the MDIO period into the YU
+MDIO config register. The i1clk resource from the ACPI table
+is used to provide addressing to YU bootrecord PLL registers.
+The values in these registers are used to compute MDIO period.
+If the i1clk resource is not present in the ACPI table, then
+the current default hardcorded value of 430Mhz is used.
+The i1clk clock value of 430MHz is only accurate for boards
+with BF2 mid bin and main bin SoCs. The BF2 high bin SoCs
+have i1clk = 500MHz, but can support a slower MDIO period.
+
+Fixes: f92e1869d74e ("Add Mellanox BlueField Gigabit Ethernet driver")
+Reviewed-by: Asmaa Mnebhi <asmaa@nvidia.com>
+Signed-off-by: David Thompson <davthompson@nvidia.com>
+Link: https://lore.kernel.org/r/20220826155916.12491-1-davthompson@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../ethernet/mellanox/mlxbf_gige/mlxbf_gige.h |   4 +-
+ .../mellanox/mlxbf_gige/mlxbf_gige_mdio.c     | 122 +++++++++++++++---
+ .../mellanox/mlxbf_gige/mlxbf_gige_regs.h     |   2 +
+ 3 files changed, 110 insertions(+), 18 deletions(-)
+
+diff --git a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige.h b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige.h
+index e3509e69ed1c6..3e8725b7f0b70 100644
+--- a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige.h
++++ b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige.h
+@@ -80,6 +80,7 @@ struct mlxbf_gige {
+       struct net_device *netdev;
+       struct platform_device *pdev;
+       void __iomem *mdio_io;
++      void __iomem *clk_io;
+       struct mii_bus *mdiobus;
+       void __iomem *gpio_io;
+       struct irq_domain *irqdomain;
+@@ -149,7 +150,8 @@ enum mlxbf_gige_res {
+       MLXBF_GIGE_RES_MDIO9,
+       MLXBF_GIGE_RES_GPIO0,
+       MLXBF_GIGE_RES_LLU,
+-      MLXBF_GIGE_RES_PLU
++      MLXBF_GIGE_RES_PLU,
++      MLXBF_GIGE_RES_CLK
+ };
+ /* Version of register data returned by mlxbf_gige_get_regs() */
+diff --git a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_mdio.c b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_mdio.c
+index 7905179a95753..f979ba7e5effc 100644
+--- a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_mdio.c
++++ b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_mdio.c
+@@ -22,10 +22,23 @@
+ #include <linux/property.h>
+ #include "mlxbf_gige.h"
++#include "mlxbf_gige_regs.h"
+ #define MLXBF_GIGE_MDIO_GW_OFFSET     0x0
+ #define MLXBF_GIGE_MDIO_CFG_OFFSET    0x4
++#define MLXBF_GIGE_MDIO_FREQ_REFERENCE 156250000ULL
++#define MLXBF_GIGE_MDIO_COREPLL_CONST  16384ULL
++#define MLXBF_GIGE_MDC_CLK_NS          400
++#define MLXBF_GIGE_MDIO_PLL_I1CLK_REG1 0x4
++#define MLXBF_GIGE_MDIO_PLL_I1CLK_REG2 0x8
++#define MLXBF_GIGE_MDIO_CORE_F_SHIFT   0
++#define MLXBF_GIGE_MDIO_CORE_F_MASK    GENMASK(25, 0)
++#define MLXBF_GIGE_MDIO_CORE_R_SHIFT   26
++#define MLXBF_GIGE_MDIO_CORE_R_MASK    GENMASK(31, 26)
++#define MLXBF_GIGE_MDIO_CORE_OD_SHIFT  0
++#define MLXBF_GIGE_MDIO_CORE_OD_MASK   GENMASK(3, 0)
++
+ /* Support clause 22 */
+ #define MLXBF_GIGE_MDIO_CL22_ST1      0x1
+ #define MLXBF_GIGE_MDIO_CL22_WRITE    0x1
+@@ -50,27 +63,76 @@
+ #define MLXBF_GIGE_MDIO_CFG_MDIO_IN_SAMP_MASK         GENMASK(23, 16)
+ #define MLXBF_GIGE_MDIO_CFG_MDIO_OUT_SAMP_MASK                GENMASK(31, 24)
++#define MLXBF_GIGE_MDIO_CFG_VAL (FIELD_PREP(MLXBF_GIGE_MDIO_CFG_MDIO_MODE_MASK, 1) | \
++                               FIELD_PREP(MLXBF_GIGE_MDIO_CFG_MDIO3_3_MASK, 1) | \
++                               FIELD_PREP(MLXBF_GIGE_MDIO_CFG_MDIO_FULL_DRIVE_MASK, 1) | \
++                               FIELD_PREP(MLXBF_GIGE_MDIO_CFG_MDIO_IN_SAMP_MASK, 6) | \
++                               FIELD_PREP(MLXBF_GIGE_MDIO_CFG_MDIO_OUT_SAMP_MASK, 13))
++
++#define MLXBF_GIGE_BF2_COREPLL_ADDR 0x02800c30
++#define MLXBF_GIGE_BF2_COREPLL_SIZE 0x0000000c
++
++static struct resource corepll_params[] = {
++      [MLXBF_GIGE_VERSION_BF2] = {
++              .start = MLXBF_GIGE_BF2_COREPLL_ADDR,
++              .end = MLXBF_GIGE_BF2_COREPLL_ADDR + MLXBF_GIGE_BF2_COREPLL_SIZE - 1,
++              .name = "COREPLL_RES"
++      },
++};
++
++/* Returns core clock i1clk in Hz */
++static u64 calculate_i1clk(struct mlxbf_gige *priv)
++{
++      u8 core_od, core_r;
++      u64 freq_output;
++      u32 reg1, reg2;
++      u32 core_f;
++
++      reg1 = readl(priv->clk_io + MLXBF_GIGE_MDIO_PLL_I1CLK_REG1);
++      reg2 = readl(priv->clk_io + MLXBF_GIGE_MDIO_PLL_I1CLK_REG2);
++
++      core_f = (reg1 & MLXBF_GIGE_MDIO_CORE_F_MASK) >>
++              MLXBF_GIGE_MDIO_CORE_F_SHIFT;
++      core_r = (reg1 & MLXBF_GIGE_MDIO_CORE_R_MASK) >>
++              MLXBF_GIGE_MDIO_CORE_R_SHIFT;
++      core_od = (reg2 & MLXBF_GIGE_MDIO_CORE_OD_MASK) >>
++              MLXBF_GIGE_MDIO_CORE_OD_SHIFT;
++
++      /* Compute PLL output frequency as follow:
++       *
++       *                                     CORE_F / 16384
++       * freq_output = freq_reference * ----------------------------
++       *                              (CORE_R + 1) * (CORE_OD + 1)
++       */
++      freq_output = div_u64((MLXBF_GIGE_MDIO_FREQ_REFERENCE * core_f),
++                            MLXBF_GIGE_MDIO_COREPLL_CONST);
++      freq_output = div_u64(freq_output, (core_r + 1) * (core_od + 1));
++
++      return freq_output;
++}
++
+ /* Formula for encoding the MDIO period. The encoded value is
+  * passed to the MDIO config register.
+  *
+- * mdc_clk = 2*(val + 1)*i1clk
++ * mdc_clk = 2*(val + 1)*(core clock in sec)
+  *
+- * 400 ns = 2*(val + 1)*(((1/430)*1000) ns)
++ * i1clk is in Hz:
++ * 400 ns = 2*(val + 1)*(1/i1clk)
+  *
+- * val = (((400 * 430 / 1000) / 2) - 1)
++ * val = (((400/10^9) / (1/i1clk) / 2) - 1)
++ * val = (400/2 * i1clk)/10^9 - 1
+  */
+-#define MLXBF_GIGE_I1CLK_MHZ          430
+-#define MLXBF_GIGE_MDC_CLK_NS         400
++static u8 mdio_period_map(struct mlxbf_gige *priv)
++{
++      u8 mdio_period;
++      u64 i1clk;
+-#define MLXBF_GIGE_MDIO_PERIOD        (((MLXBF_GIGE_MDC_CLK_NS * MLXBF_GIGE_I1CLK_MHZ / 1000) / 2) - 1)
++      i1clk = calculate_i1clk(priv);
+-#define MLXBF_GIGE_MDIO_CFG_VAL (FIELD_PREP(MLXBF_GIGE_MDIO_CFG_MDIO_MODE_MASK, 1) | \
+-                               FIELD_PREP(MLXBF_GIGE_MDIO_CFG_MDIO3_3_MASK, 1) | \
+-                               FIELD_PREP(MLXBF_GIGE_MDIO_CFG_MDIO_FULL_DRIVE_MASK, 1) | \
+-                               FIELD_PREP(MLXBF_GIGE_MDIO_CFG_MDC_PERIOD_MASK, \
+-                                          MLXBF_GIGE_MDIO_PERIOD) | \
+-                               FIELD_PREP(MLXBF_GIGE_MDIO_CFG_MDIO_IN_SAMP_MASK, 6) | \
+-                               FIELD_PREP(MLXBF_GIGE_MDIO_CFG_MDIO_OUT_SAMP_MASK, 13))
++      mdio_period = div_u64((MLXBF_GIGE_MDC_CLK_NS >> 1) * i1clk, 1000000000) - 1;
++
++      return mdio_period;
++}
+ static u32 mlxbf_gige_mdio_create_cmd(u16 data, int phy_add,
+                                     int phy_reg, u32 opcode)
+@@ -123,9 +185,9 @@ static int mlxbf_gige_mdio_write(struct mii_bus *bus, int phy_add,
+                                int phy_reg, u16 val)
+ {
+       struct mlxbf_gige *priv = bus->priv;
++      u32 temp;
+       u32 cmd;
+       int ret;
+-      u32 temp;
+       if (phy_reg & MII_ADDR_C45)
+               return -EOPNOTSUPP;
+@@ -142,18 +204,44 @@ static int mlxbf_gige_mdio_write(struct mii_bus *bus, int phy_add,
+       return ret;
+ }
++static void mlxbf_gige_mdio_cfg(struct mlxbf_gige *priv)
++{
++      u8 mdio_period;
++      u32 val;
++
++      mdio_period = mdio_period_map(priv);
++
++      val = MLXBF_GIGE_MDIO_CFG_VAL;
++      val |= FIELD_PREP(MLXBF_GIGE_MDIO_CFG_MDC_PERIOD_MASK, mdio_period);
++      writel(val, priv->mdio_io + MLXBF_GIGE_MDIO_CFG_OFFSET);
++}
++
+ int mlxbf_gige_mdio_probe(struct platform_device *pdev, struct mlxbf_gige *priv)
+ {
+       struct device *dev = &pdev->dev;
++      struct resource *res;
+       int ret;
+       priv->mdio_io = devm_platform_ioremap_resource(pdev, MLXBF_GIGE_RES_MDIO9);
+       if (IS_ERR(priv->mdio_io))
+               return PTR_ERR(priv->mdio_io);
+-      /* Configure mdio parameters */
+-      writel(MLXBF_GIGE_MDIO_CFG_VAL,
+-             priv->mdio_io + MLXBF_GIGE_MDIO_CFG_OFFSET);
++      /* clk resource shared with other drivers so cannot use
++       * devm_platform_ioremap_resource
++       */
++      res = platform_get_resource(pdev, IORESOURCE_MEM, MLXBF_GIGE_RES_CLK);
++      if (!res) {
++              /* For backward compatibility with older ACPI tables, also keep
++               * CLK resource internal to the driver.
++               */
++              res = &corepll_params[MLXBF_GIGE_VERSION_BF2];
++      }
++
++      priv->clk_io = devm_ioremap(dev, res->start, resource_size(res));
++      if (IS_ERR(priv->clk_io))
++              return PTR_ERR(priv->clk_io);
++
++      mlxbf_gige_mdio_cfg(priv);
+       priv->mdiobus = devm_mdiobus_alloc(dev);
+       if (!priv->mdiobus) {
+diff --git a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_regs.h b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_regs.h
+index 5fb33c9294bf9..7be3a793984d5 100644
+--- a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_regs.h
++++ b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_regs.h
+@@ -8,6 +8,8 @@
+ #ifndef __MLXBF_GIGE_REGS_H__
+ #define __MLXBF_GIGE_REGS_H__
++#define MLXBF_GIGE_VERSION                            0x0000
++#define MLXBF_GIGE_VERSION_BF2                        0x0
+ #define MLXBF_GIGE_STATUS                             0x0010
+ #define MLXBF_GIGE_STATUS_READY                       BIT(0)
+ #define MLXBF_GIGE_INT_STATUS                         0x0028
+-- 
+2.35.1
+
diff --git a/queue-5.15/net-dsa-xrs700x-use-irqsave-variant-for-u64-stats-up.patch b/queue-5.15/net-dsa-xrs700x-use-irqsave-variant-for-u64-stats-up.patch
new file mode 100644 (file)
index 0000000..14d25b4
--- /dev/null
@@ -0,0 +1,65 @@
+From abf4845d4b3d4b6823f09f832fefc3ab5e1d97dc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 25 Aug 2022 13:36:44 +0200
+Subject: net: dsa: xrs700x: Use irqsave variant for u64 stats update
+
+From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+
+[ Upstream commit 3f8ae9fe0409698799e173f698b714f34570b64b ]
+
+xrs700x_read_port_counters() updates the stats from a worker using the
+u64_stats_update_begin() version. This is okay on 32-UP since on the
+reader side preemption is disabled.
+On 32bit-SMP the writer can be preempted by the reader at which point
+the reader will spin on the seqcount until writer continues and
+completes the update.
+
+Assigning the mib_mutex mutex to the underlying seqcount would ensure
+proper synchronisation. The API for that on the u64_stats_init() side
+isn't available. Since it is the only user, just use disable interrupts
+during the update.
+
+Use u64_stats_update_begin_irqsave() on the writer side to ensure an
+uninterrupted update.
+
+Fixes: ee00b24f32eb8 ("net: dsa: add Arrow SpeedChips XRS700x driver")
+Cc: Andrew Lunn <andrew@lunn.ch>
+Cc: Florian Fainelli <f.fainelli@gmail.com>
+Cc: George McCollister <george.mccollister@gmail.com>
+Cc: Vivien Didelot <vivien.didelot@gmail.com>
+Cc: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Acked-by: George McCollister <george.mccollister@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/dsa/xrs700x/xrs700x.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/dsa/xrs700x/xrs700x.c b/drivers/net/dsa/xrs700x/xrs700x.c
+index 469420941054e..cf363d5a30020 100644
+--- a/drivers/net/dsa/xrs700x/xrs700x.c
++++ b/drivers/net/dsa/xrs700x/xrs700x.c
+@@ -108,6 +108,7 @@ static void xrs700x_read_port_counters(struct xrs700x *priv, int port)
+ {
+       struct xrs700x_port *p = &priv->ports[port];
+       struct rtnl_link_stats64 stats;
++      unsigned long flags;
+       int i;
+       memset(&stats, 0, sizeof(stats));
+@@ -137,9 +138,9 @@ static void xrs700x_read_port_counters(struct xrs700x *priv, int port)
+        */
+       stats.rx_packets += stats.multicast;
+-      u64_stats_update_begin(&p->syncp);
++      flags = u64_stats_update_begin_irqsave(&p->syncp);
+       p->stats64 = stats;
+-      u64_stats_update_end(&p->syncp);
++      u64_stats_update_end_irqrestore(&p->syncp, flags);
+       mutex_unlock(&p->mib_mutex);
+ }
+-- 
+2.35.1
+
diff --git a/queue-5.15/net-sched-fix-netdevice-reference-leaks-in-attach_de.patch b/queue-5.15/net-sched-fix-netdevice-reference-leaks-in-attach_de.patch
new file mode 100644 (file)
index 0000000..cfc63eb
--- /dev/null
@@ -0,0 +1,111 @@
+From f99eaa1592a5503d57d42d49a3542d7ed4ad1d75 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Aug 2022 17:00:55 +0800
+Subject: net/sched: fix netdevice reference leaks in attach_default_qdiscs()
+
+From: Wang Hai <wanghai38@huawei.com>
+
+[ Upstream commit f612466ebecb12a00d9152344ddda6f6345f04dc ]
+
+In attach_default_qdiscs(), if a dev has multiple queues and queue 0 fails
+to attach qdisc because there is no memory in attach_one_default_qdisc().
+Then dev->qdisc will be noop_qdisc by default. But the other queues may be
+able to successfully attach to default qdisc.
+
+In this case, the fallback to noqueue process will be triggered. If the
+original attached qdisc is not released and a new one is directly
+attached, this will cause netdevice reference leaks.
+
+The following is the bug log:
+
+veth0: default qdisc (fq_codel) fail, fallback to noqueue
+unregister_netdevice: waiting for veth0 to become free. Usage count = 32
+leaked reference.
+ qdisc_alloc+0x12e/0x210
+ qdisc_create_dflt+0x62/0x140
+ attach_one_default_qdisc.constprop.41+0x44/0x70
+ dev_activate+0x128/0x290
+ __dev_open+0x12a/0x190
+ __dev_change_flags+0x1a2/0x1f0
+ dev_change_flags+0x23/0x60
+ do_setlink+0x332/0x1150
+ __rtnl_newlink+0x52f/0x8e0
+ rtnl_newlink+0x43/0x70
+ rtnetlink_rcv_msg+0x140/0x3b0
+ netlink_rcv_skb+0x50/0x100
+ netlink_unicast+0x1bb/0x290
+ netlink_sendmsg+0x37c/0x4e0
+ sock_sendmsg+0x5f/0x70
+ ____sys_sendmsg+0x208/0x280
+
+Fix this bug by clearing any non-noop qdiscs that may have been assigned
+before trying to re-attach.
+
+Fixes: bf6dba76d278 ("net: sched: fallback to qdisc noqueue if default qdisc setup fail")
+Signed-off-by: Wang Hai <wanghai38@huawei.com>
+Link: https://lore.kernel.org/r/20220826090055.24424-1-wanghai38@huawei.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/sch_generic.c | 31 ++++++++++++++++---------------
+ 1 file changed, 16 insertions(+), 15 deletions(-)
+
+diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
+index 250d87d993cb7..02299785209c1 100644
+--- a/net/sched/sch_generic.c
++++ b/net/sched/sch_generic.c
+@@ -1083,6 +1083,21 @@ struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue,
+ }
+ EXPORT_SYMBOL(dev_graft_qdisc);
++static void shutdown_scheduler_queue(struct net_device *dev,
++                                   struct netdev_queue *dev_queue,
++                                   void *_qdisc_default)
++{
++      struct Qdisc *qdisc = dev_queue->qdisc_sleeping;
++      struct Qdisc *qdisc_default = _qdisc_default;
++
++      if (qdisc) {
++              rcu_assign_pointer(dev_queue->qdisc, qdisc_default);
++              dev_queue->qdisc_sleeping = qdisc_default;
++
++              qdisc_put(qdisc);
++      }
++}
++
+ static void attach_one_default_qdisc(struct net_device *dev,
+                                    struct netdev_queue *dev_queue,
+                                    void *_unused)
+@@ -1130,6 +1145,7 @@ static void attach_default_qdiscs(struct net_device *dev)
+       if (qdisc == &noop_qdisc) {
+               netdev_warn(dev, "default qdisc (%s) fail, fallback to %s\n",
+                           default_qdisc_ops->id, noqueue_qdisc_ops.id);
++              netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc);
+               dev->priv_flags |= IFF_NO_QUEUE;
+               netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL);
+               qdisc = txq->qdisc_sleeping;
+@@ -1384,21 +1400,6 @@ void dev_init_scheduler(struct net_device *dev)
+       timer_setup(&dev->watchdog_timer, dev_watchdog, 0);
+ }
+-static void shutdown_scheduler_queue(struct net_device *dev,
+-                                   struct netdev_queue *dev_queue,
+-                                   void *_qdisc_default)
+-{
+-      struct Qdisc *qdisc = dev_queue->qdisc_sleeping;
+-      struct Qdisc *qdisc_default = _qdisc_default;
+-
+-      if (qdisc) {
+-              rcu_assign_pointer(dev_queue->qdisc, qdisc_default);
+-              dev_queue->qdisc_sleeping = qdisc_default;
+-
+-              qdisc_put(qdisc);
+-      }
+-}
+-
+ void dev_shutdown(struct net_device *dev)
+ {
+       netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc);
+-- 
+2.35.1
+
diff --git a/queue-5.15/net-sched-tbf-don-t-call-qdisc_put-while-holding-tre.patch b/queue-5.15/net-sched-tbf-don-t-call-qdisc_put-while-holding-tre.patch
new file mode 100644 (file)
index 0000000..ffe8e8e
--- /dev/null
@@ -0,0 +1,54 @@
+From f996b465c7504c309cc2584195aa11ff0599fe60 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Aug 2022 09:39:30 +0800
+Subject: net: sched: tbf: don't call qdisc_put() while holding tree lock
+
+From: Zhengchao Shao <shaozhengchao@huawei.com>
+
+[ Upstream commit b05972f01e7d30419987a1f221b5593668fd6448 ]
+
+The issue is the same to commit c2999f7fb05b ("net: sched: multiq: don't
+call qdisc_put() while holding tree lock"). Qdiscs call qdisc_put() while
+holding sch tree spinlock, which results sleeping-while-atomic BUG.
+
+Fixes: c266f64dbfa2 ("net: sched: protect block state with mutex")
+Signed-off-by: Zhengchao Shao <shaozhengchao@huawei.com>
+Link: https://lore.kernel.org/r/20220826013930.340121-1-shaozhengchao@huawei.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/sch_tbf.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c
+index 78e79029dc631..6eb17004a9e44 100644
+--- a/net/sched/sch_tbf.c
++++ b/net/sched/sch_tbf.c
+@@ -342,6 +342,7 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt,
+       struct nlattr *tb[TCA_TBF_MAX + 1];
+       struct tc_tbf_qopt *qopt;
+       struct Qdisc *child = NULL;
++      struct Qdisc *old = NULL;
+       struct psched_ratecfg rate;
+       struct psched_ratecfg peak;
+       u64 max_size;
+@@ -433,7 +434,7 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt,
+       sch_tree_lock(sch);
+       if (child) {
+               qdisc_tree_flush_backlog(q->qdisc);
+-              qdisc_put(q->qdisc);
++              old = q->qdisc;
+               q->qdisc = child;
+       }
+       q->limit = qopt->limit;
+@@ -453,6 +454,7 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt,
+       memcpy(&q->peak, &peak, sizeof(struct psched_ratecfg));
+       sch_tree_unlock(sch);
++      qdisc_put(old);
+       err = 0;
+       tbf_offload_change(sch);
+-- 
+2.35.1
+
diff --git a/queue-5.15/net-smc-remove-redundant-refcount-increase.patch b/queue-5.15/net-smc-remove-redundant-refcount-increase.patch
new file mode 100644 (file)
index 0000000..e4f3f82
--- /dev/null
@@ -0,0 +1,37 @@
+From 70f2b492605705f5c9ae83d2c8a7b4a19742ae34 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 30 Aug 2022 23:23:14 +0800
+Subject: net/smc: Remove redundant refcount increase
+
+From: Yacan Liu <liuyacan@corp.netease.com>
+
+[ Upstream commit a8424a9b4522a3ab9f32175ad6d848739079071f ]
+
+For passive connections, the refcount increment has been done in
+smc_clcsock_accept()-->smc_sock_alloc().
+
+Fixes: 3b2dec2603d5 ("net/smc: restructure client and server code in af_smc")
+Signed-off-by: Yacan Liu <liuyacan@corp.netease.com>
+Reviewed-by: Tony Lu <tonylu@linux.alibaba.com>
+Link: https://lore.kernel.org/r/20220830152314.838736-1-liuyacan@corp.netease.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/smc/af_smc.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c
+index 2ddd7b34b4ce5..26f81e2e1dfba 100644
+--- a/net/smc/af_smc.c
++++ b/net/smc/af_smc.c
+@@ -1490,7 +1490,6 @@ static void smc_listen_out_connected(struct smc_sock *new_smc)
+ {
+       struct sock *newsmcsk = &new_smc->sk;
+-      sk_refcnt_debug_inc(newsmcsk);
+       if (newsmcsk->sk_state == SMC_INIT)
+               newsmcsk->sk_state = SMC_ACTIVE;
+-- 
+2.35.1
+
diff --git a/queue-5.15/net-smsc911x-stop-and-start-phy-during-suspend-and-r.patch b/queue-5.15/net-smsc911x-stop-and-start-phy-during-suspend-and-r.patch
new file mode 100644 (file)
index 0000000..1ae0f1a
--- /dev/null
@@ -0,0 +1,61 @@
+From 0f61d12fddc9b60884591c74f9f7700205a3b18b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 24 Aug 2022 19:39:51 -0700
+Subject: net: smsc911x: Stop and start PHY during suspend and resume
+
+From: Florian Fainelli <f.fainelli@gmail.com>
+
+[ Upstream commit 3ce9f2bef75528936c78a7053301f5725f622f3a ]
+
+Commit 744d23c71af3 ("net: phy: Warn about incorrect
+mdio_bus_phy_resume() state") unveiled that the smsc911x driver was not
+properly stopping and restarting the PHY during suspend/resume. Correct
+that by indicating that the MAC is in charge of PHY PM operations and
+ensure that all MDIO bus activity is quiescent during suspend.
+
+Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Fixes: fba863b81604 ("net: phy: make PHY PM ops a no-op if MAC driver manages PHY PM")
+Fixes: 2aa70f864955 ("net: smsc911x: Quieten netif during suspend")
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+Link: https://lore.kernel.org/r/20220825023951.3220-1-f.fainelli@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/smsc/smsc911x.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
+index 592e191adbf7d..63b99dd8ca51c 100644
+--- a/drivers/net/ethernet/smsc/smsc911x.c
++++ b/drivers/net/ethernet/smsc/smsc911x.c
+@@ -1037,6 +1037,8 @@ static int smsc911x_mii_probe(struct net_device *dev)
+               return ret;
+       }
++      /* Indicate that the MAC is responsible for managing PHY PM */
++      phydev->mac_managed_pm = true;
+       phy_attached_info(phydev);
+       phy_set_max_speed(phydev, SPEED_100);
+@@ -2584,6 +2586,8 @@ static int smsc911x_suspend(struct device *dev)
+       if (netif_running(ndev)) {
+               netif_stop_queue(ndev);
+               netif_device_detach(ndev);
++              if (!device_may_wakeup(dev))
++                      phy_stop(ndev->phydev);
+       }
+       /* enable wake on LAN, energy detection and the external PME
+@@ -2625,6 +2629,8 @@ static int smsc911x_resume(struct device *dev)
+       if (netif_running(ndev)) {
+               netif_device_attach(ndev);
+               netif_start_queue(ndev);
++              if (!device_may_wakeup(dev))
++                      phy_start(ndev->phydev);
+       }
+       return 0;
+-- 
+2.35.1
+
diff --git a/queue-5.15/net-sparx5-fix-handling-uneven-length-packets-in-man.patch b/queue-5.15/net-sparx5-fix-handling-uneven-length-packets-in-man.patch
new file mode 100644 (file)
index 0000000..6d254ab
--- /dev/null
@@ -0,0 +1,40 @@
+From d36959132d1f0f12bba80a9b66abac4de9376345 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 25 Aug 2022 10:49:55 +0200
+Subject: net: sparx5: fix handling uneven length packets in manual extraction
+
+From: Casper Andersson <casper.casan@gmail.com>
+
+[ Upstream commit 7498a457ecf7ff2c4d379360aa8f24566bb1543e ]
+
+Packets that are not of length divisible by 4 (e.g. 77, 78, 79) would
+have the checksum included up to next multiple of 4 (a 77 bytes packet
+would have 3 bytes of ethernet checksum included). The check for the
+value expects it in host (Little) endian.
+
+Fixes: f3cad2611a77 ("net: sparx5: add hostmode with phylink support")
+Signed-off-by: Casper Andersson <casper.casan@gmail.com>
+Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
+Link: https://lore.kernel.org/r/20220825084955.684637-1-casper.casan@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/microchip/sparx5/sparx5_packet.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
+index 148d431fcde42..c460168131c26 100644
+--- a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
+@@ -107,6 +107,8 @@ static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap)
+                       /* This assumes STATUS_WORD_POS == 1, Status
+                        * just after last data
+                        */
++                      if (!byte_swap)
++                              val = ntohl((__force __be32)val);
+                       byte_cnt -= (4 - XTR_VALID_BYTES(val));
+                       eof_flag = true;
+                       break;
+-- 
+2.35.1
+
diff --git a/queue-5.15/openvswitch-fix-memory-leak-at-failed-datapath-creat.patch b/queue-5.15/openvswitch-fix-memory-leak-at-failed-datapath-creat.patch
new file mode 100644 (file)
index 0000000..84ee649
--- /dev/null
@@ -0,0 +1,73 @@
+From 08f080516d413b89410baf6d640048c48b075ba2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 25 Aug 2022 05:03:26 +0300
+Subject: openvswitch: fix memory leak at failed datapath creation
+
+From: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
+
+[ Upstream commit a87406f4adee9c53b311d8a1ba2849c69e29a6d0 ]
+
+ovs_dp_cmd_new()->ovs_dp_change()->ovs_dp_set_upcall_portids()
+allocates array via kmalloc.
+If for some reason new_vport() fails during ovs_dp_cmd_new()
+dp->upcall_portids must be freed.
+Add missing kfree.
+
+Kmemleak example:
+unreferenced object 0xffff88800c382500 (size 64):
+  comm "dump_state", pid 323, jiffies 4294955418 (age 104.347s)
+  hex dump (first 32 bytes):
+    5e c2 79 e4 1f 7a 38 c7 09 21 38 0c 80 88 ff ff  ^.y..z8..!8.....
+    03 00 00 00 0a 00 00 00 14 00 00 00 28 00 00 00  ............(...
+  backtrace:
+    [<0000000071bebc9f>] ovs_dp_set_upcall_portids+0x38/0xa0
+    [<000000000187d8bd>] ovs_dp_change+0x63/0xe0
+    [<000000002397e446>] ovs_dp_cmd_new+0x1f0/0x380
+    [<00000000aa06f36e>] genl_family_rcv_msg_doit+0xea/0x150
+    [<000000008f583bc4>] genl_rcv_msg+0xdc/0x1e0
+    [<00000000fa10e377>] netlink_rcv_skb+0x50/0x100
+    [<000000004959cece>] genl_rcv+0x24/0x40
+    [<000000004699ac7f>] netlink_unicast+0x23e/0x360
+    [<00000000c153573e>] netlink_sendmsg+0x24e/0x4b0
+    [<000000006f4aa380>] sock_sendmsg+0x62/0x70
+    [<00000000d0068654>] ____sys_sendmsg+0x230/0x270
+    [<0000000012dacf7d>] ___sys_sendmsg+0x88/0xd0
+    [<0000000011776020>] __sys_sendmsg+0x59/0xa0
+    [<000000002e8f2dc1>] do_syscall_64+0x3b/0x90
+    [<000000003243e7cb>] entry_SYSCALL_64_after_hwframe+0x63/0xcd
+
+Fixes: b83d23a2a38b ("openvswitch: Introduce per-cpu upcall dispatch")
+Acked-by: Aaron Conole <aconole@redhat.com>
+Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
+Link: https://lore.kernel.org/r/20220825020326.664073-1-andrey.zhadchenko@virtuozzo.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/openvswitch/datapath.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
+index 67ad08320886b..5e2c83cb7b129 100644
+--- a/net/openvswitch/datapath.c
++++ b/net/openvswitch/datapath.c
+@@ -1801,7 +1801,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
+                               ovs_dp_reset_user_features(skb, info);
+               }
+-              goto err_unlock_and_destroy_meters;
++              goto err_destroy_portids;
+       }
+       err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
+@@ -1816,6 +1816,8 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
+       ovs_notify(&dp_datapath_genl_family, reply, info);
+       return 0;
++err_destroy_portids:
++      kfree(rcu_dereference_raw(dp->upcall_portids));
+ err_unlock_and_destroy_meters:
+       ovs_unlock();
+       ovs_meters_exit(dp);
+-- 
+2.35.1
+
diff --git a/queue-5.15/platform-x86-pmc_atom-fix-slp_typx-bitfield-mask.patch b/queue-5.15/platform-x86-pmc_atom-fix-slp_typx-bitfield-mask.patch
new file mode 100644 (file)
index 0000000..cb1d6da
--- /dev/null
@@ -0,0 +1,67 @@
+From dd3ab1ec2734b34ef78108206376b75f2847b4c4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 1 Aug 2022 14:37:31 +0300
+Subject: platform/x86: pmc_atom: Fix SLP_TYPx bitfield mask
+
+From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+
+[ Upstream commit 0a90ed8d0cfa29735a221eba14d9cb6c735d35b6 ]
+
+On Intel hardware the SLP_TYPx bitfield occupies bits 10-12 as per ACPI
+specification (see Table 4.13 "PM1 Control Registers Fixed Hardware
+Feature Control Bits" for the details).
+
+Fix the mask and other related definitions accordingly.
+
+Fixes: 93e5eadd1f6e ("x86/platform: New Intel Atom SOC power management controller driver")
+Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://lore.kernel.org/r/20220801113734.36131-1-andriy.shevchenko@linux.intel.com
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/platform/x86/pmc_atom.c            | 2 +-
+ include/linux/platform_data/x86/pmc_atom.h | 6 ++++--
+ 2 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/platform/x86/pmc_atom.c b/drivers/platform/x86/pmc_atom.c
+index a9d2a4b98e570..4b0739f95f8b9 100644
+--- a/drivers/platform/x86/pmc_atom.c
++++ b/drivers/platform/x86/pmc_atom.c
+@@ -244,7 +244,7 @@ static void pmc_power_off(void)
+       pm1_cnt_port = acpi_base_addr + PM1_CNT;
+       pm1_cnt_value = inl(pm1_cnt_port);
+-      pm1_cnt_value &= SLEEP_TYPE_MASK;
++      pm1_cnt_value &= ~SLEEP_TYPE_MASK;
+       pm1_cnt_value |= SLEEP_TYPE_S5;
+       pm1_cnt_value |= SLEEP_ENABLE;
+diff --git a/include/linux/platform_data/x86/pmc_atom.h b/include/linux/platform_data/x86/pmc_atom.h
+index 022bcea9edec5..99a9b09dc839d 100644
+--- a/include/linux/platform_data/x86/pmc_atom.h
++++ b/include/linux/platform_data/x86/pmc_atom.h
+@@ -7,6 +7,8 @@
+ #ifndef PMC_ATOM_H
+ #define PMC_ATOM_H
++#include <linux/bits.h>
++
+ /* ValleyView Power Control Unit PCI Device ID */
+ #define       PCI_DEVICE_ID_VLV_PMC   0x0F1C
+ /* CherryTrail Power Control Unit PCI Device ID */
+@@ -139,9 +141,9 @@
+ #define       ACPI_MMIO_REG_LEN       0x100
+ #define       PM1_CNT                 0x4
+-#define       SLEEP_TYPE_MASK         0xFFFFECFF
++#define       SLEEP_TYPE_MASK         GENMASK(12, 10)
+ #define       SLEEP_TYPE_S5           0x1C00
+-#define       SLEEP_ENABLE            0x2000
++#define       SLEEP_ENABLE            BIT(13)
+ extern int pmc_atom_read(int offset, u32 *value);
+ extern int pmc_atom_write(int offset, u32 value);
+-- 
+2.35.1
+
diff --git a/queue-5.15/revert-sch_cake-return-__net_xmit_stolen-when-consum.patch b/queue-5.15/revert-sch_cake-return-__net_xmit_stolen-when-consum.patch
new file mode 100644 (file)
index 0000000..c0cdeea
--- /dev/null
@@ -0,0 +1,54 @@
+From 9a05f009e34afcf4805c8961de6cac96e65f3f17 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 31 Aug 2022 20:01:32 -0700
+Subject: Revert "sch_cake: Return __NET_XMIT_STOLEN when consuming enqueued
+ skb"
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ Upstream commit 0b4f688d53fdc2a731b9d9cdf0c96255bc024ea6 ]
+
+This reverts commit 90fabae8a2c225c4e4936723c38857887edde5cc.
+
+Patch was applied hastily, revert and let the v2 be reviewed.
+
+Fixes: 90fabae8a2c2 ("sch_cake: Return __NET_XMIT_STOLEN when consuming enqueued skb")
+Link: https://lore.kernel.org/all/87wnao2ha3.fsf@toke.dk/
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/sch_cake.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
+index 6944c669731c4..857aaebd49f43 100644
+--- a/net/sched/sch_cake.c
++++ b/net/sched/sch_cake.c
+@@ -1713,7 +1713,6 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
+       }
+       idx--;
+       flow = &b->flows[idx];
+-      ret = NET_XMIT_SUCCESS;
+       /* ensure shaper state isn't stale */
+       if (!b->tin_backlog) {
+@@ -1772,7 +1771,6 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
+               qdisc_tree_reduce_backlog(sch, 1-numsegs, len-slen);
+               consume_skb(skb);
+-              ret |= __NET_XMIT_STOLEN;
+       } else {
+               /* not splitting */
+               cobalt_set_enqueue_time(skb, now);
+@@ -1906,7 +1904,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
+               }
+               b->drop_overlimit += dropped;
+       }
+-      return ret;
++      return NET_XMIT_SUCCESS;
+ }
+ static struct sk_buff *cake_dequeue_one(struct Qdisc *sch)
+-- 
+2.35.1
+
diff --git a/queue-5.15/revert-xhci-turn-off-port-power-in-shutdown.patch b/queue-5.15/revert-xhci-turn-off-port-power-in-shutdown.patch
new file mode 100644 (file)
index 0000000..750b96b
--- /dev/null
@@ -0,0 +1,94 @@
+From 86c89b04d8baee917f1856055895ee70dcd5abce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 25 Aug 2022 18:08:40 +0300
+Subject: Revert "xhci: turn off port power in shutdown"
+
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+
+[ Upstream commit 8531aa1659f7278d4f2ec7408cc000eaa8d85217 ]
+
+This reverts commit 83810f84ecf11dfc5a9414a8b762c3501b328185.
+
+Turning off port power in shutdown did cause issues such as a laptop not
+proprly powering off, and some specific usb devies failing to enumerate the
+subsequent boot after a warm reset.
+
+So revert this.
+
+Fixes: 83810f84ecf1 ("xhci: turn off port power in shutdown")
+Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
+Link: https://lore.kernel.org/r/20220825150840.132216-4-mathias.nyman@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/host/xhci-hub.c |  2 +-
+ drivers/usb/host/xhci.c     | 15 ++-------------
+ drivers/usb/host/xhci.h     |  2 --
+ 3 files changed, 3 insertions(+), 16 deletions(-)
+
+diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
+index fc322a9526c8c..f65f1ba2b5929 100644
+--- a/drivers/usb/host/xhci-hub.c
++++ b/drivers/usb/host/xhci-hub.c
+@@ -652,7 +652,7 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd)
+  * It will release and re-aquire the lock while calling ACPI
+  * method.
+  */
+-void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd,
++static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd,
+                               u16 index, bool on, unsigned long *flags)
+       __must_hold(&xhci->lock)
+ {
+diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
+index d76c10f9ad807..e3767651c9a9e 100644
+--- a/drivers/usb/host/xhci.c
++++ b/drivers/usb/host/xhci.c
+@@ -776,8 +776,6 @@ static void xhci_stop(struct usb_hcd *hcd)
+ void xhci_shutdown(struct usb_hcd *hcd)
+ {
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+-      unsigned long flags;
+-      int i;
+       if (xhci->quirks & XHCI_SPURIOUS_REBOOT)
+               usb_disable_xhci_ports(to_pci_dev(hcd->self.sysdev));
+@@ -793,21 +791,12 @@ void xhci_shutdown(struct usb_hcd *hcd)
+               del_timer_sync(&xhci->shared_hcd->rh_timer);
+       }
+-      spin_lock_irqsave(&xhci->lock, flags);
++      spin_lock_irq(&xhci->lock);
+       xhci_halt(xhci);
+-
+-      /* Power off USB2 ports*/
+-      for (i = 0; i < xhci->usb2_rhub.num_ports; i++)
+-              xhci_set_port_power(xhci, xhci->main_hcd, i, false, &flags);
+-
+-      /* Power off USB3 ports*/
+-      for (i = 0; i < xhci->usb3_rhub.num_ports; i++)
+-              xhci_set_port_power(xhci, xhci->shared_hcd, i, false, &flags);
+-
+       /* Workaround for spurious wakeups at shutdown with HSW */
+       if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
+               xhci_reset(xhci, XHCI_RESET_SHORT_USEC);
+-      spin_unlock_irqrestore(&xhci->lock, flags);
++      spin_unlock_irq(&xhci->lock);
+       xhci_cleanup_msix(xhci);
+diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
+index 101f1956a96ca..81e1bfdf83988 100644
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -2174,8 +2174,6 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
+ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
+ int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
+ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
+-void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd, u16 index,
+-                       bool on, unsigned long *flags);
+ void xhci_hc_died(struct xhci_hcd *xhci);
+-- 
+2.35.1
+
diff --git a/queue-5.15/sch_cake-return-__net_xmit_stolen-when-consuming-enq.patch b/queue-5.15/sch_cake-return-__net_xmit_stolen-when-consuming-enq.patch
new file mode 100644 (file)
index 0000000..8b62942
--- /dev/null
@@ -0,0 +1,61 @@
+From 17edf90ae74b516759a3670f2f9b1b5e654ad6ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 31 Aug 2022 11:21:03 +0200
+Subject: sch_cake: Return __NET_XMIT_STOLEN when consuming enqueued skb
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Toke Høiland-Jørgensen <toke@toke.dk>
+
+[ Upstream commit 90fabae8a2c225c4e4936723c38857887edde5cc ]
+
+When the GSO splitting feature of sch_cake is enabled, GSO superpackets
+will be broken up and the resulting segments enqueued in place of the
+original skb. In this case, CAKE calls consume_skb() on the original skb,
+but still returns NET_XMIT_SUCCESS. This can confuse parent qdiscs into
+assuming the original skb still exists, when it really has been freed. Fix
+this by adding the __NET_XMIT_STOLEN flag to the return value in this case.
+
+Fixes: 0c850344d388 ("sch_cake: Conditionally split GSO segments")
+Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
+Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-18231
+Link: https://lore.kernel.org/r/20220831092103.442868-1-toke@toke.dk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/sch_cake.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
+index 857aaebd49f43..6944c669731c4 100644
+--- a/net/sched/sch_cake.c
++++ b/net/sched/sch_cake.c
+@@ -1713,6 +1713,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
+       }
+       idx--;
+       flow = &b->flows[idx];
++      ret = NET_XMIT_SUCCESS;
+       /* ensure shaper state isn't stale */
+       if (!b->tin_backlog) {
+@@ -1771,6 +1772,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
+               qdisc_tree_reduce_backlog(sch, 1-numsegs, len-slen);
+               consume_skb(skb);
++              ret |= __NET_XMIT_STOLEN;
+       } else {
+               /* not splitting */
+               cobalt_set_enqueue_time(skb, now);
+@@ -1904,7 +1906,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
+               }
+               b->drop_overlimit += dropped;
+       }
+-      return NET_XMIT_SUCCESS;
++      return ret;
+ }
+ static struct sk_buff *cake_dequeue_one(struct Qdisc *sch)
+-- 
+2.35.1
+
diff --git a/queue-5.15/series b/queue-5.15/series
new file mode 100644 (file)
index 0000000..fa44250
--- /dev/null
@@ -0,0 +1,35 @@
+drm-msm-dsi-fix-the-inconsistent-indenting.patch
+drm-msm-dp-delete-dp_recovered_clock_out_en-to-fix-t.patch
+drm-msm-dsi-fix-number-of-regulators-for-msm8996_dsi.patch
+drm-msm-dsi-fix-number-of-regulators-for-sdm660.patch
+platform-x86-pmc_atom-fix-slp_typx-bitfield-mask.patch
+iio-adc-mcp3911-make-use-of-the-sign-bit.patch
+skmsg-fix-wrong-last-sg-check-in-sk_msg_recvmsg.patch
+bpf-restrict-bpf_sys_bpf-to-cap_perfmon.patch
+usb-dwc3-qcom-add-helper-functions-to-enable-disable.patch
+usb-dwc3-qcom-configure-wakeup-interrupts-during-sus.patch
+usb-dwc3-qcom-fix-runtime-pm-wakeup.patch
+usb-dwc3-qcom-fix-use-after-free-on-runtime-pm-wakeu.patch
+usb-dwc3-qcom-fix-peripheral-and-otg-suspend.patch
+bpf-cgroup-fix-kernel-bug-in-purge_effective_progs.patch
+ieee802154-adf7242-defer-destroy_workqueue-call.patch
+drm-i915-backlight-extract-backlight-code-to-a-separ.patch
+drm-i915-display-avoid-warnings-when-registering-dua.patch
+alsa-hda-intel-nhlt-remove-use-of-__func__-in-dev_db.patch
+alsa-hda-intel-nhlt-correct-the-handling-of-fmt_conf.patch
+wifi-cfg80211-debugfs-fix-return-type-in-ht40allow_m.patch
+revert-xhci-turn-off-port-power-in-shutdown.patch
+net-sparx5-fix-handling-uneven-length-packets-in-man.patch
+net-smsc911x-stop-and-start-phy-during-suspend-and-r.patch
+openvswitch-fix-memory-leak-at-failed-datapath-creat.patch
+net-dsa-xrs700x-use-irqsave-variant-for-u64-stats-up.patch
+net-sched-tbf-don-t-call-qdisc_put-while-holding-tre.patch
+net-sched-fix-netdevice-reference-leaks-in-attach_de.patch
+ethernet-rocker-fix-sleep-in-atomic-context-bug-in-n.patch
+mlxbf_gige-compute-mdio-period-based-on-i1clk.patch
+kcm-fix-strp_init-order-and-cleanup.patch
+sch_cake-return-__net_xmit_stolen-when-consuming-enq.patch
+tcp-annotate-data-race-around-challenge_timestamp.patch
+revert-sch_cake-return-__net_xmit_stolen-when-consum.patch
+net-smc-remove-redundant-refcount-increase.patch
+soundwire-qcom-fix-device-status-array-range.patch
diff --git a/queue-5.15/skmsg-fix-wrong-last-sg-check-in-sk_msg_recvmsg.patch b/queue-5.15/skmsg-fix-wrong-last-sg-check-in-sk_msg_recvmsg.patch
new file mode 100644 (file)
index 0000000..d9c9def
--- /dev/null
@@ -0,0 +1,67 @@
+From cbe86be450993c4447efeec83be1e58f6b2f6be2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 9 Aug 2022 17:49:15 +0800
+Subject: skmsg: Fix wrong last sg check in sk_msg_recvmsg()
+
+From: Liu Jian <liujian56@huawei.com>
+
+[ Upstream commit 583585e48d965338e73e1eb383768d16e0922d73 ]
+
+Fix one kernel NULL pointer dereference as below:
+
+[  224.462334] Call Trace:
+[  224.462394]  __tcp_bpf_recvmsg+0xd3/0x380
+[  224.462441]  ? sock_has_perm+0x78/0xa0
+[  224.462463]  tcp_bpf_recvmsg+0x12e/0x220
+[  224.462494]  inet_recvmsg+0x5b/0xd0
+[  224.462534]  __sys_recvfrom+0xc8/0x130
+[  224.462574]  ? syscall_trace_enter+0x1df/0x2e0
+[  224.462606]  ? __do_page_fault+0x2de/0x500
+[  224.462635]  __x64_sys_recvfrom+0x24/0x30
+[  224.462660]  do_syscall_64+0x5d/0x1d0
+[  224.462709]  entry_SYSCALL_64_after_hwframe+0x65/0xca
+
+In commit 9974d37ea75f ("skmsg: Fix invalid last sg check in
+sk_msg_recvmsg()"), we change last sg check to sg_is_last(),
+but in sockmap redirection case (without stream_parser/stream_verdict/
+skb_verdict), we did not mark the end of the scatterlist. Check the
+sk_msg_alloc, sk_msg_page_add, and bpf_msg_push_data functions, they all
+do not mark the end of sg. They are expected to use sg.end for end
+judgment. So the judgment of '(i != msg_rx->sg.end)' is added back here.
+
+Fixes: 9974d37ea75f ("skmsg: Fix invalid last sg check in sk_msg_recvmsg()")
+Signed-off-by: Liu Jian <liujian56@huawei.com>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Acked-by: John Fastabend <john.fastabend@gmail.com>
+Acked-by: Jakub Sitnicki <jakub@cloudflare.com>
+Link: https://lore.kernel.org/bpf/20220809094915.150391-1-liujian56@huawei.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/core/skmsg.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/net/core/skmsg.c b/net/core/skmsg.c
+index 4ddcfac344984..054073c7cbb95 100644
+--- a/net/core/skmsg.c
++++ b/net/core/skmsg.c
+@@ -462,7 +462,7 @@ int sk_msg_recvmsg(struct sock *sk, struct sk_psock *psock, struct msghdr *msg,
+                       if (copied == len)
+                               break;
+-              } while (!sg_is_last(sge));
++              } while ((i != msg_rx->sg.end) && !sg_is_last(sge));
+               if (unlikely(peek)) {
+                       msg_rx = sk_psock_next_msg(psock, msg_rx);
+@@ -472,7 +472,7 @@ int sk_msg_recvmsg(struct sock *sk, struct sk_psock *psock, struct msghdr *msg,
+               }
+               msg_rx->sg.start = i;
+-              if (!sge->length && sg_is_last(sge)) {
++              if (!sge->length && (i == msg_rx->sg.end || sg_is_last(sge))) {
+                       msg_rx = sk_psock_dequeue_msg(psock);
+                       kfree_sk_msg(msg_rx);
+               }
+-- 
+2.35.1
+
diff --git a/queue-5.15/soundwire-qcom-fix-device-status-array-range.patch b/queue-5.15/soundwire-qcom-fix-device-status-array-range.patch
new file mode 100644 (file)
index 0000000..dacb548
--- /dev/null
@@ -0,0 +1,59 @@
+From 1e63879a1e0abdde9c84ae51e3e1bf4080e430b6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 Jul 2022 11:47:47 +0100
+Subject: soundwire: qcom: fix device status array range
+
+From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+
+[ Upstream commit 4ef3f2aff1267bfa6d5a90c42a30b927b8aa239b ]
+
+This patch updates device status array range from 11 to 12 as we will
+be reading status from device number 0 to device number 11 inclusive.
+
+Without this patch we can potentially access status array out of range
+during auto-enumeration.
+
+Fixes: aa1262ca6695 ("soundwire: qcom: Check device status before reading devid")
+Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220708104747.8722-1-srinivas.kandagatla@linaro.org
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/soundwire/qcom.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c
+index 2adc0a75c0515..1ce6f948e9a42 100644
+--- a/drivers/soundwire/qcom.c
++++ b/drivers/soundwire/qcom.c
+@@ -148,7 +148,7 @@ struct qcom_swrm_ctrl {
+       u8 wcmd_id;
+       struct qcom_swrm_port_config pconfig[QCOM_SDW_MAX_PORTS];
+       struct sdw_stream_runtime *sruntime[SWRM_MAX_DAIS];
+-      enum sdw_slave_status status[SDW_MAX_DEVICES];
++      enum sdw_slave_status status[SDW_MAX_DEVICES + 1];
+       int (*reg_read)(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val);
+       int (*reg_write)(struct qcom_swrm_ctrl *ctrl, int reg, int val);
+       u32 slave_status;
+@@ -391,7 +391,7 @@ static int qcom_swrm_get_alert_slave_dev_num(struct qcom_swrm_ctrl *ctrl)
+       ctrl->reg_read(ctrl, SWRM_MCP_SLV_STATUS, &val);
+-      for (dev_num = 0; dev_num < SDW_MAX_DEVICES; dev_num++) {
++      for (dev_num = 0; dev_num <= SDW_MAX_DEVICES; dev_num++) {
+               status = (val >> (dev_num * SWRM_MCP_SLV_STATUS_SZ));
+               if ((status & SWRM_MCP_SLV_STATUS_MASK) == SDW_SLAVE_ALERT) {
+@@ -411,7 +411,7 @@ static void qcom_swrm_get_device_status(struct qcom_swrm_ctrl *ctrl)
+       ctrl->reg_read(ctrl, SWRM_MCP_SLV_STATUS, &val);
+       ctrl->slave_status = val;
+-      for (i = 0; i < SDW_MAX_DEVICES; i++) {
++      for (i = 0; i <= SDW_MAX_DEVICES; i++) {
+               u32 s;
+               s = (val >> (i * 2));
+-- 
+2.35.1
+
diff --git a/queue-5.15/tcp-annotate-data-race-around-challenge_timestamp.patch b/queue-5.15/tcp-annotate-data-race-around-challenge_timestamp.patch
new file mode 100644 (file)
index 0000000..b45cb25
--- /dev/null
@@ -0,0 +1,47 @@
+From 49ba11fa77213b9f2d201b4a67a9bd949a22951d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 30 Aug 2022 11:56:55 -0700
+Subject: tcp: annotate data-race around challenge_timestamp
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 8c70521238b7863c2af607e20bcba20f974c969b ]
+
+challenge_timestamp can be read an written by concurrent threads.
+
+This was expected, but we need to annotate the race to avoid potential issues.
+
+Following patch moves challenge_timestamp and challenge_count
+to per-netns storage to provide better isolation.
+
+Fixes: 354e4aa391ed ("tcp: RFC 5961 5.2 Blind Data Injection Attack Mitigation")
+Reported-by: syzbot <syzkaller@googlegroups.com>
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Acked-by: Neal Cardwell <ncardwell@google.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/tcp_input.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
+index a33e6aa42a4c5..7fd7e7cba0c92 100644
+--- a/net/ipv4/tcp_input.c
++++ b/net/ipv4/tcp_input.c
+@@ -3623,11 +3623,11 @@ static void tcp_send_challenge_ack(struct sock *sk, const struct sk_buff *skb)
+       /* Then check host-wide RFC 5961 rate limit. */
+       now = jiffies / HZ;
+-      if (now != challenge_timestamp) {
++      if (now != READ_ONCE(challenge_timestamp)) {
+               u32 ack_limit = READ_ONCE(net->ipv4.sysctl_tcp_challenge_ack_limit);
+               u32 half = (ack_limit + 1) >> 1;
+-              challenge_timestamp = now;
++              WRITE_ONCE(challenge_timestamp, now);
+               WRITE_ONCE(challenge_count, half + prandom_u32_max(ack_limit));
+       }
+       count = READ_ONCE(challenge_count);
+-- 
+2.35.1
+
diff --git a/queue-5.15/usb-dwc3-qcom-add-helper-functions-to-enable-disable.patch b/queue-5.15/usb-dwc3-qcom-add-helper-functions-to-enable-disable.patch
new file mode 100644 (file)
index 0000000..29d54ad
--- /dev/null
@@ -0,0 +1,107 @@
+From 6c4f7ff5731662959b2642de424a88a90aaeec42 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>
+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 873bf5041117f..74895cf57dd54 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.15/usb-dwc3-qcom-configure-wakeup-interrupts-during-sus.patch b/queue-5.15/usb-dwc3-qcom-configure-wakeup-interrupts-during-sus.patch
new file mode 100644 (file)
index 0000000..bc78abb
--- /dev/null
@@ -0,0 +1,155 @@
+From 4aff51bbc66b1e5bc2c398f3ab7f6e53d334ca6e 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>
+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 74895cf57dd54..a48b0a6a3f6a4 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.15/usb-dwc3-qcom-fix-peripheral-and-otg-suspend.patch b/queue-5.15/usb-dwc3-qcom-fix-peripheral-and-otg-suspend.patch
new file mode 100644 (file)
index 0000000..d79068c
--- /dev/null
@@ -0,0 +1,78 @@
+From 68eb295c5451893d7e7d2ab7d2e4aaf7dc11a135 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 ccbdf7ae906ea..057e7c64bfa9b 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
+@@ -413,7 +418,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);
+       }
+@@ -431,7 +440,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.15/usb-dwc3-qcom-fix-runtime-pm-wakeup.patch b/queue-5.15/usb-dwc3-qcom-fix-runtime-pm-wakeup.patch
new file mode 100644 (file)
index 0000000..4ff8e9f
--- /dev/null
@@ -0,0 +1,110 @@
+From 6e7e7d9bf670c5626effbddd2bfbab5eed451e01 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 a48b0a6a3f6a4..1a742642211f1 100644
+--- a/drivers/usb/dwc3/dwc3-qcom.c
++++ b/drivers/usb/dwc3/dwc3-qcom.c
+@@ -386,7 +386,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;
+@@ -405,7 +405,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);
+       }
+@@ -415,7 +415,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;
+@@ -423,7 +423,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++) {
+@@ -924,9 +924,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;
+@@ -936,9 +938,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;
+@@ -949,14 +952,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.15/usb-dwc3-qcom-fix-use-after-free-on-runtime-pm-wakeu.patch b/queue-5.15/usb-dwc3-qcom-fix-use-after-free-on-runtime-pm-wakeu.patch
new file mode 100644 (file)
index 0000000..5bcf472
--- /dev/null
@@ -0,0 +1,82 @@
+From 856410b417bf56d5ea5abf861125d4571480ba1b 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 1a742642211f1..ccbdf7ae906ea 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 f29a264635aa1..2078e9d702923 100644
+--- a/drivers/usb/dwc3/host.c
++++ b/drivers/usb/dwc3/host.c
+@@ -130,4 +130,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.15/wifi-cfg80211-debugfs-fix-return-type-in-ht40allow_m.patch b/queue-5.15/wifi-cfg80211-debugfs-fix-return-type-in-ht40allow_m.patch
new file mode 100644 (file)
index 0000000..4cd899b
--- /dev/null
@@ -0,0 +1,41 @@
+From 62b7d69b6fb04c083e65b868bcde6b449ef5008d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Aug 2022 10:03:21 +0300
+Subject: wifi: cfg80211: debugfs: fix return type in ht40allow_map_read()
+
+From: Dan Carpenter <dan.carpenter@oracle.com>
+
+[ Upstream commit d776763f48084926b5d9e25507a3ddb7c9243d5e ]
+
+The return type is supposed to be ssize_t, which is signed long,
+but "r" was declared as unsigned int.  This means that on 64 bit systems
+we return positive values instead of negative error codes.
+
+Fixes: 80a3511d70e8 ("cfg80211: add debugfs HT40 allow map")
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Link: https://lore.kernel.org/r/YutvOQeJm0UjLhwU@kili
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/wireless/debugfs.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c
+index aab43469a2f04..0878b162890af 100644
+--- a/net/wireless/debugfs.c
++++ b/net/wireless/debugfs.c
+@@ -65,9 +65,10 @@ static ssize_t ht40allow_map_read(struct file *file,
+ {
+       struct wiphy *wiphy = file->private_data;
+       char *buf;
+-      unsigned int offset = 0, buf_size = PAGE_SIZE, i, r;
++      unsigned int offset = 0, buf_size = PAGE_SIZE, i;
+       enum nl80211_band band;
+       struct ieee80211_supported_band *sband;
++      ssize_t r;
+       buf = kzalloc(buf_size, GFP_KERNEL);
+       if (!buf)
+-- 
+2.35.1
+