From: Sasha Levin Date: Mon, 5 Sep 2022 12:52:09 +0000 (-0400) Subject: Fixes for 5.15 X-Git-Tag: v5.10.142~88 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=171fd9758c9a039c1c7532530845cc0a2ad66701;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 5.15 Signed-off-by: Sasha Levin --- 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 index 00000000000..98d91801ad8 --- /dev/null +++ b/queue-5.15/alsa-hda-intel-nhlt-correct-the-handling-of-fmt_conf.patch @@ -0,0 +1,57 @@ +From bb25f6efc7b064d170fdb5883040a66146420390 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reviewed-by: Pierre-Louis Bossart +Reviewed-by: Jaska Uimonen +Link: https://lore.kernel.org/r/20220823122405.18464-1-peter.ujfalusi@linux.intel.com +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..5a5c4dc192c --- /dev/null +++ b/queue-5.15/alsa-hda-intel-nhlt-remove-use-of-__func__-in-dev_db.patch @@ -0,0 +1,79 @@ +From e0c7863907895686180253a50869a72ae75bbe54 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ Upstream commit 6376ab02374822e1e8758a848ee736a182786a2e ] + +The module and function information can be added with +'modprobe foo dyndbg=+pmf' + +Suggested-by: Greg KH +Signed-off-by: Pierre-Louis Bossart +Reviewed-by: Ranjani Sridharan +Reviewed-by: Péter Ujfalusi +Reviewed-by: Bard Liao +Link: https://lore.kernel.org/r/20220616220559.136160-1-pierre-louis.bossart@linux.intel.com +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..9f9ea134bb4 --- /dev/null +++ b/queue-5.15/bpf-cgroup-fix-kernel-bug-in-purge_effective_progs.patch @@ -0,0 +1,107 @@ +From fe8d36b8fcee5ee12cd36bcc69497c7daec0799d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 13 Aug 2022 21:40:30 +0800 +Subject: bpf, cgroup: Fix kernel BUG in purge_effective_progs + +From: Pu Lehui + +[ 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: + + 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 + + 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 +Signed-off-by: Daniel Borkmann +Acked-by: Andrii Nakryiko +Link: https://lore.kernel.org/bpf/20220813134030.1972696-1-pulehui@huawei.com +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..2f828cad5fb --- /dev/null +++ b/queue-5.15/bpf-restrict-bpf_sys_bpf-to-cap_perfmon.patch @@ -0,0 +1,41 @@ +From ced0af0a708c1851905310ca658e59fd7d7f6bec Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 16 Aug 2022 20:55:16 +0000 +Subject: bpf: Restrict bpf_sys_bpf to CAP_PERFMON + +From: YiFei Zhu + +[ 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 +Signed-off-by: Daniel Borkmann +Acked-by: Alexei Starovoitov +Link: https://lore.kernel.org/bpf/20220816205517.682470-1-zhuyifei@google.com +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..b14c5c3a9d9 --- /dev/null +++ b/queue-5.15/drm-i915-backlight-extract-backlight-code-to-a-separ.patch @@ -0,0 +1,3887 @@ +From fb5afdd520f004988c4a59da63a466c5ca7e8936 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 25 Aug 2021 14:06:50 +0300 +Subject: drm/i915/backlight: extract backlight code to a separate file + +From: Jani Nikula + +[ 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 +Reviewed-by: Lyude Paul +Signed-off-by: Jani Nikula +Link: https://patchwork.freedesktop.org/patch/msgid/97d310848f03061473b9b2328e2c5c4dcf263cfa.1629888677.git.jani.nikula@intel.com +Signed-off-by: Sasha Levin +--- + 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 + + #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 ++#include ++ ++#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 ++ ++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 + #include + +-#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 + #include + +-#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 + */ + +-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +- + #include +-#include + #include + ++#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 + +-#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 index 00000000000..a47611af96d --- /dev/null +++ b/queue-5.15/drm-i915-display-avoid-warnings-when-registering-dua.patch @@ -0,0 +1,83 @@ +From be05bfb8b91878d9e5864a1e42e6887273a71748 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Aug 2022 09:27:50 +0530 +Subject: drm/i915/display: avoid warnings when registering dual panel + backlight + +From: Arun R Murthy + +[ 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 +Signed-off-by: Jani Nikula +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 +Signed-off-by: Sasha Levin +--- + .../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 index 00000000000..b32c12808fe --- /dev/null +++ b/queue-5.15/drm-msm-dp-delete-dp_recovered_clock_out_en-to-fix-t.patch @@ -0,0 +1,65 @@ +From ca7ac3d9b199a3cc914f38d1551e37e8b1f5b992 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 + +Reviewed-by: Abhinav Kumar +Reviewed-by: Stephen Boyd +Reviewed-by: Dmitry Baryshkov +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 +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..6ec29f08a7c --- /dev/null +++ b/queue-5.15/drm-msm-dsi-fix-number-of-regulators-for-msm8996_dsi.patch @@ -0,0 +1,39 @@ +From 454fd222a940ed3b358f88364e2931b2092ea4f3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 4 Aug 2022 07:38:48 -0700 +Subject: drm/msm/dsi: Fix number of regulators for msm8996_dsi_cfg + +From: Douglas Anderson + +[ 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 +Reviewed-by: Dmitry Baryshkov +Reviewed-by: Abhinav Kumar +Patchwork: https://patchwork.freedesktop.org/patch/496318/ +Link: https://lore.kernel.org/r/20220804073608.v4.1.I1056ee3f77f71287f333279efe4c85f88d403f65@changeid +Signed-off-by: Abhinav Kumar +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..12bce94dbab --- /dev/null +++ b/queue-5.15/drm-msm-dsi-fix-number-of-regulators-for-sdm660.patch @@ -0,0 +1,41 @@ +From 781a71c8103583ce3fcfde10e0c7eb23f692a578 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 4 Aug 2022 07:38:49 -0700 +Subject: drm/msm/dsi: Fix number of regulators for SDM660 + +From: Douglas Anderson + +[ 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 +Reviewed-by: Dmitry Baryshkov +Reviewed-by: Marijn Suijten +Reviewed-by: Abhinav Kumar +Patchwork: https://patchwork.freedesktop.org/patch/496323/ +Link: https://lore.kernel.org/r/20220804073608.v4.2.I94b3c3e412b7c208061349f05659e126483171b1@changeid +Signed-off-by: Abhinav Kumar +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..03d5def1aa4 --- /dev/null +++ b/queue-5.15/drm-msm-dsi-fix-the-inconsistent-indenting.patch @@ -0,0 +1,43 @@ +From adb9e9003ccf90de02b83722f30ec2250f85f0d4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 Jul 2022 09:56:22 +0800 +Subject: drm/msm/dsi: fix the inconsistent indenting + +From: sunliming + +[ 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 +Signed-off-by: sunliming +Reviewed-by: Abhinav Kumar +Patchwork: https://patchwork.freedesktop.org/patch/494662/ +Link: https://lore.kernel.org/r/20220719015622.646718-1-sunliming@kylinos.cn +Signed-off-by: Abhinav Kumar +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..83258f3fd99 --- /dev/null +++ b/queue-5.15/ethernet-rocker-fix-sleep-in-atomic-context-bug-in-n.patch @@ -0,0 +1,57 @@ +From b432a3b691504b025869e2219ecab99b42a48591 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..7b5e5fbb095 --- /dev/null +++ b/queue-5.15/ieee802154-adf7242-defer-destroy_workqueue-call.patch @@ -0,0 +1,60 @@ +From b42c9968c6c51551b8285ebb12c736f0a471012c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Aug 2022 11:42:24 +0800 +Subject: ieee802154/adf7242: defer destroy_workqueue call + +From: Lin Ma + +[ 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 +Acked-by: Michael Hennerich +Link: https://lore.kernel.org/r/20220808034224.12642-1-linma@zju.edu.cn +Signed-off-by: Stefan Schmidt +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..03c271b6e66 --- /dev/null +++ b/queue-5.15/iio-adc-mcp3911-make-use-of-the-sign-bit.patch @@ -0,0 +1,37 @@ +From 6b8eb16fd92ae4c0fef11065f7ad3bd0c025af61 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 Jul 2022 15:07:18 +0200 +Subject: iio: adc: mcp3911: make use of the sign bit + +From: Marcus Folkesson + +[ Upstream commit 8f89e33bf040bbef66386c426198622180233178 ] + +The device supports negative values as well. + +Fixes: 3a89b289df5d ("iio: adc: add support for mcp3911") +Signed-off-by: Marcus Folkesson +Reviewed-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20220722130726.7627-2-marcus.folkesson@gmail.com +Signed-off-by: Jonathan Cameron +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..4bfb29100df --- /dev/null +++ b/queue-5.15/kcm-fix-strp_init-order-and-cleanup.patch @@ -0,0 +1,74 @@ +From 5acdd07c35d39bb1becfb97823b09340a08e969e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 27 Aug 2022 11:13:14 -0700 +Subject: kcm: fix strp_init() order and cleanup + +From: Cong Wang + +[ 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 +Signed-off-by: Cong Wang +Link: https://lore.kernel.org/r/20220827181314.193710-1-xiyou.wangcong@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..f3750b6a039 --- /dev/null +++ b/queue-5.15/mlxbf_gige-compute-mdio-period-based-on-i1clk.patch @@ -0,0 +1,247 @@ +From feb5439be3de1652ce17391baf310249709eeecd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Aug 2022 11:59:16 -0400 +Subject: mlxbf_gige: compute MDIO period based on i1clk + +From: David Thompson + +[ 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 +Signed-off-by: David Thompson +Link: https://lore.kernel.org/r/20220826155916.12491-1-davthompson@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + .../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 + + #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 index 00000000000..14d25b432f9 --- /dev/null +++ b/queue-5.15/net-dsa-xrs700x-use-irqsave-variant-for-u64-stats-up.patch @@ -0,0 +1,65 @@ +From abf4845d4b3d4b6823f09f832fefc3ab5e1d97dc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 25 Aug 2022 13:36:44 +0200 +Subject: net: dsa: xrs700x: Use irqsave variant for u64 stats update + +From: Sebastian Andrzej Siewior + +[ 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 +Cc: Florian Fainelli +Cc: George McCollister +Cc: Vivien Didelot +Cc: Vladimir Oltean +Signed-off-by: Sebastian Andrzej Siewior +Acked-by: George McCollister +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..cfc63ebe59f --- /dev/null +++ b/queue-5.15/net-sched-fix-netdevice-reference-leaks-in-attach_de.patch @@ -0,0 +1,111 @@ +From f99eaa1592a5503d57d42d49a3542d7ed4ad1d75 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Aug 2022 17:00:55 +0800 +Subject: net/sched: fix netdevice reference leaks in attach_default_qdiscs() + +From: Wang Hai + +[ 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 +Link: https://lore.kernel.org/r/20220826090055.24424-1-wanghai38@huawei.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..ffe8e8e061b --- /dev/null +++ b/queue-5.15/net-sched-tbf-don-t-call-qdisc_put-while-holding-tre.patch @@ -0,0 +1,54 @@ +From f996b465c7504c309cc2584195aa11ff0599fe60 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Link: https://lore.kernel.org/r/20220826013930.340121-1-shaozhengchao@huawei.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..e4f3f82922e --- /dev/null +++ b/queue-5.15/net-smc-remove-redundant-refcount-increase.patch @@ -0,0 +1,37 @@ +From 70f2b492605705f5c9ae83d2c8a7b4a19742ae34 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 30 Aug 2022 23:23:14 +0800 +Subject: net/smc: Remove redundant refcount increase + +From: Yacan Liu + +[ 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 +Reviewed-by: Tony Lu +Link: https://lore.kernel.org/r/20220830152314.838736-1-liuyacan@corp.netease.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..1ae0f1ab334 --- /dev/null +++ b/queue-5.15/net-smsc911x-stop-and-start-phy-during-suspend-and-r.patch @@ -0,0 +1,61 @@ +From 0f61d12fddc9b60884591c74f9f7700205a3b18b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 24 Aug 2022 19:39:51 -0700 +Subject: net: smsc911x: Stop and start PHY during suspend and resume + +From: Florian Fainelli + +[ 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 +Tested-by: Marek Szyprowski +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 +Link: https://lore.kernel.org/r/20220825023951.3220-1-f.fainelli@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..6d254ab1f75 --- /dev/null +++ b/queue-5.15/net-sparx5-fix-handling-uneven-length-packets-in-man.patch @@ -0,0 +1,40 @@ +From d36959132d1f0f12bba80a9b66abac4de9376345 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 25 Aug 2022 10:49:55 +0200 +Subject: net: sparx5: fix handling uneven length packets in manual extraction + +From: Casper Andersson + +[ 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 +Reviewed-by: Steen Hegelund +Link: https://lore.kernel.org/r/20220825084955.684637-1-casper.casan@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..84ee6499169 --- /dev/null +++ b/queue-5.15/openvswitch-fix-memory-leak-at-failed-datapath-creat.patch @@ -0,0 +1,73 @@ +From 08f080516d413b89410baf6d640048c48b075ba2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 25 Aug 2022 05:03:26 +0300 +Subject: openvswitch: fix memory leak at failed datapath creation + +From: Andrey Zhadchenko + +[ 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 +Signed-off-by: Andrey Zhadchenko +Link: https://lore.kernel.org/r/20220825020326.664073-1-andrey.zhadchenko@virtuozzo.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..cb1d6dafd52 --- /dev/null +++ b/queue-5.15/platform-x86-pmc_atom-fix-slp_typx-bitfield-mask.patch @@ -0,0 +1,67 @@ +From dd3ab1ec2734b34ef78108206376b75f2847b4c4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 1 Aug 2022 14:37:31 +0300 +Subject: platform/x86: pmc_atom: Fix SLP_TYPx bitfield mask + +From: Andy Shevchenko + +[ 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 +Link: https://lore.kernel.org/r/20220801113734.36131-1-andriy.shevchenko@linux.intel.com +Reviewed-by: Hans de Goede +Signed-off-by: Hans de Goede +Signed-off-by: Sasha Levin +--- + 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 ++ + /* 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 index 00000000000..c0cdeea6153 --- /dev/null +++ b/queue-5.15/revert-sch_cake-return-__net_xmit_stolen-when-consum.patch @@ -0,0 +1,54 @@ +From 9a05f009e34afcf4805c8961de6cac96e65f3f17 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 31 Aug 2022 20:01:32 -0700 +Subject: Revert "sch_cake: Return __NET_XMIT_STOLEN when consuming enqueued + skb" + +From: Jakub Kicinski + +[ 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 +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..750b96babb9 --- /dev/null +++ b/queue-5.15/revert-xhci-turn-off-port-power-in-shutdown.patch @@ -0,0 +1,94 @@ +From 86c89b04d8baee917f1856055895ee70dcd5abce Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 25 Aug 2022 18:08:40 +0300 +Subject: Revert "xhci: turn off port power in shutdown" + +From: Mathias Nyman + +[ 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 +Link: https://lore.kernel.org/r/20220825150840.132216-4-mathias.nyman@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..8b629421ccb --- /dev/null +++ b/queue-5.15/sch_cake-return-__net_xmit_stolen-when-consuming-enq.patch @@ -0,0 +1,61 @@ +From 17edf90ae74b516759a3670f2f9b1b5e654ad6ac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +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 +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..fa44250e87e --- /dev/null +++ b/queue-5.15/series @@ -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 index 00000000000..d9c9def0478 --- /dev/null +++ b/queue-5.15/skmsg-fix-wrong-last-sg-check-in-sk_msg_recvmsg.patch @@ -0,0 +1,67 @@ +From cbe86be450993c4447efeec83be1e58f6b2f6be2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 9 Aug 2022 17:49:15 +0800 +Subject: skmsg: Fix wrong last sg check in sk_msg_recvmsg() + +From: Liu Jian + +[ 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 +Signed-off-by: Daniel Borkmann +Acked-by: John Fastabend +Acked-by: Jakub Sitnicki +Link: https://lore.kernel.org/bpf/20220809094915.150391-1-liujian56@huawei.com +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..dacb54801e9 --- /dev/null +++ b/queue-5.15/soundwire-qcom-fix-device-status-array-range.patch @@ -0,0 +1,59 @@ +From 1e63879a1e0abdde9c84ae51e3e1bf4080e430b6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 Jul 2022 11:47:47 +0100 +Subject: soundwire: qcom: fix device status array range + +From: Srinivas Kandagatla + +[ 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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20220708104747.8722-1-srinivas.kandagatla@linaro.org +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..b45cb2562cc --- /dev/null +++ b/queue-5.15/tcp-annotate-data-race-around-challenge_timestamp.patch @@ -0,0 +1,47 @@ +From 49ba11fa77213b9f2d201b4a67a9bd949a22951d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 30 Aug 2022 11:56:55 -0700 +Subject: tcp: annotate data-race around challenge_timestamp + +From: Eric Dumazet + +[ 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 +Signed-off-by: Eric Dumazet +Acked-by: Neal Cardwell +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..29d54ad038f --- /dev/null +++ b/queue-5.15/usb-dwc3-qcom-add-helper-functions-to-enable-disable.patch @@ -0,0 +1,107 @@ +From 6c4f7ff5731662959b2642de424a88a90aaeec42 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jun 2022 10:00:52 +0530 +Subject: usb: dwc3: qcom: Add helper functions to enable,disable wake irqs + +From: Sandeep Maheswaram + +[ Upstream commit 360e8230516de94d74d30c64f0cdcf228b8e8b67 ] + +Adding helper functions to enable,disable wake irqs to make +the code simple and readable. + +Reviewed-by: Matthias Kaehlcke +Reviewed-by: Pavankumar Kondeti +Signed-off-by: Sandeep Maheswaram +Signed-off-by: Krishna Kurapati +Link: https://lore.kernel.org/r/1655094654-24052-4-git-send-email-quic_kriskura@quicinc.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..bc78abbc2a3 --- /dev/null +++ b/queue-5.15/usb-dwc3-qcom-configure-wakeup-interrupts-during-sus.patch @@ -0,0 +1,155 @@ +From 4aff51bbc66b1e5bc2c398f3ab7f6e53d334ca6e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jun 2022 10:00:53 +0530 +Subject: usb: dwc3: qcom: Configure wakeup interrupts during suspend + +From: Sandeep Maheswaram + +[ 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 +Reviewed-by: Matthias Kaehlcke +Signed-off-by: Sandeep Maheswaram +Signed-off-by: Krishna Kurapati +Link: https://lore.kernel.org/r/1655094654-24052-5-git-send-email-quic_kriskura@quicinc.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + 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 + #include + #include +- ++#include ++#include + #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 index 00000000000..d79068c1e91 --- /dev/null +++ b/queue-5.15/usb-dwc3-qcom-fix-peripheral-and-otg-suspend.patch @@ -0,0 +1,78 @@ +From 68eb295c5451893d7e7d2ab7d2e4aaf7dc11a135 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 4 Aug 2022 17:09:58 +0200 +Subject: usb: dwc3: qcom: fix peripheral and OTG suspend + +From: Johan Hovold + +[ 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 +Signed-off-by: Johan Hovold +Link: https://lore.kernel.org/r/20220804151001.23612-7-johan+linaro@kernel.org +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..4ff8e9fc5ff --- /dev/null +++ b/queue-5.15/usb-dwc3-qcom-fix-runtime-pm-wakeup.patch @@ -0,0 +1,110 @@ +From 6e7e7d9bf670c5626effbddd2bfbab5eed451e01 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 4 Aug 2022 17:09:57 +0200 +Subject: usb: dwc3: qcom: fix runtime PM wakeup + +From: Johan Hovold + +[ 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 +Reviewed-by: Matthias Kaehlcke +Reviewed-by: Manivannan Sadhasivam +Signed-off-by: Johan Hovold +Link: https://lore.kernel.org/r/20220804151001.23612-6-johan+linaro@kernel.org +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..5bcf472b859 --- /dev/null +++ b/queue-5.15/usb-dwc3-qcom-fix-use-after-free-on-runtime-pm-wakeu.patch @@ -0,0 +1,82 @@ +From 856410b417bf56d5ea5abf861125d4571480ba1b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 4 Aug 2022 17:09:56 +0200 +Subject: usb: dwc3: qcom: fix use-after-free on runtime-PM wakeup + +From: Johan Hovold + +[ 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 +Reviewed-by: Manivannan Sadhasivam +Signed-off-by: Johan Hovold +Link: https://lore.kernel.org/r/20220804151001.23612-5-johan+linaro@kernel.org +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..4cd899b0f16 --- /dev/null +++ b/queue-5.15/wifi-cfg80211-debugfs-fix-return-type-in-ht40allow_m.patch @@ -0,0 +1,41 @@ +From 62b7d69b6fb04c083e65b868bcde6b449ef5008d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 4 Aug 2022 10:03:21 +0300 +Subject: wifi: cfg80211: debugfs: fix return type in ht40allow_map_read() + +From: Dan Carpenter + +[ 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 +Link: https://lore.kernel.org/r/YutvOQeJm0UjLhwU@kili +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + 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 +