mailweb.openeuler.org
Manage this list

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

Kernel

Threads by month
  • ----- 2026 -----
  • February
  • January
  • ----- 2025 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2022 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2021 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2020 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2019 -----
  • December
kernel@openeuler.org

  • 34 participants
  • 22810 discussions
[PATCH OLK-6.6] mptcp: make fallback action and fallback decision atomic
by Zhang Changzhong 07 Feb '26

07 Feb '26
From: Paolo Abeni <pabeni(a)redhat.com> mainline inclusion from mainline-v6.16-rc7 commit f8a1d9b18c5efc76784f5a326e905f641f839894 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/9321 CVE: CVE-2025-38491 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?… -------------------------------- Syzkaller reported the following splat: WARNING: CPU: 1 PID: 7704 at net/mptcp/protocol.h:1223 __mptcp_do_fallback net/mptcp/protocol.h:1223 [inline] WARNING: CPU: 1 PID: 7704 at net/mptcp/protocol.h:1223 mptcp_do_fallback net/mptcp/protocol.h:1244 [inline] WARNING: CPU: 1 PID: 7704 at net/mptcp/protocol.h:1223 check_fully_established net/mptcp/options.c:982 [inline] WARNING: CPU: 1 PID: 7704 at net/mptcp/protocol.h:1223 mptcp_incoming_options+0x21a8/0x2510 net/mptcp/options.c:1153 Modules linked in: CPU: 1 UID: 0 PID: 7704 Comm: syz.3.1419 Not tainted 6.16.0-rc3-gbd5ce2324dba #20 PREEMPT(voluntary) Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 RIP: 0010:__mptcp_do_fallback net/mptcp/protocol.h:1223 [inline] RIP: 0010:mptcp_do_fallback net/mptcp/protocol.h:1244 [inline] RIP: 0010:check_fully_established net/mptcp/options.c:982 [inline] RIP: 0010:mptcp_incoming_options+0x21a8/0x2510 net/mptcp/options.c:1153 Code: 24 18 e8 bb 2a 00 fd e9 1b df ff ff e8 b1 21 0f 00 e8 ec 5f c4 fc 44 0f b7 ac 24 b0 00 00 00 e9 54 f1 ff ff e8 d9 5f c4 fc 90 <0f> 0b 90 e9 b8 f4 ff ff e8 8b 2a 00 fd e9 8d e6 ff ff e8 81 2a 00 RSP: 0018:ffff8880a3f08448 EFLAGS: 00010246 RAX: 0000000000000000 RBX: ffff8880180a8000 RCX: ffffffff84afcf45 RDX: ffff888090223700 RSI: ffffffff84afdaa7 RDI: 0000000000000001 RBP: ffff888017955780 R08: 0000000000000001 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 R13: ffff8880180a8910 R14: ffff8880a3e9d058 R15: 0000000000000000 FS: 00005555791b8500(0000) GS:ffff88811c495000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 000000110c2800b7 CR3: 0000000058e44000 CR4: 0000000000350ef0 Call Trace: <IRQ> tcp_reset+0x26f/0x2b0 net/ipv4/tcp_input.c:4432 tcp_validate_incoming+0x1057/0x1b60 net/ipv4/tcp_input.c:5975 tcp_rcv_established+0x5b5/0x21f0 net/ipv4/tcp_input.c:6166 tcp_v4_do_rcv+0x5dc/0xa70 net/ipv4/tcp_ipv4.c:1925 tcp_v4_rcv+0x3473/0x44a0 net/ipv4/tcp_ipv4.c:2363 ip_protocol_deliver_rcu+0xba/0x480 net/ipv4/ip_input.c:205 ip_local_deliver_finish+0x2f1/0x500 net/ipv4/ip_input.c:233 NF_HOOK include/linux/netfilter.h:317 [inline] NF_HOOK include/linux/netfilter.h:311 [inline] ip_local_deliver+0x1be/0x560 net/ipv4/ip_input.c:254 dst_input include/net/dst.h:469 [inline] ip_rcv_finish net/ipv4/ip_input.c:447 [inline] NF_HOOK include/linux/netfilter.h:317 [inline] NF_HOOK include/linux/netfilter.h:311 [inline] ip_rcv+0x514/0x810 net/ipv4/ip_input.c:567 __netif_receive_skb_one_core+0x197/0x1e0 net/core/dev.c:5975 __netif_receive_skb+0x1f/0x120 net/core/dev.c:6088 process_backlog+0x301/0x1360 net/core/dev.c:6440 __napi_poll.constprop.0+0xba/0x550 net/core/dev.c:7453 napi_poll net/core/dev.c:7517 [inline] net_rx_action+0xb44/0x1010 net/core/dev.c:7644 handle_softirqs+0x1d0/0x770 kernel/softirq.c:579 do_softirq+0x3f/0x90 kernel/softirq.c:480 </IRQ> <TASK> __local_bh_enable_ip+0xed/0x110 kernel/softirq.c:407 local_bh_enable include/linux/bottom_half.h:33 [inline] inet_csk_listen_stop+0x2c5/0x1070 net/ipv4/inet_connection_sock.c:1524 mptcp_check_listen_stop.part.0+0x1cc/0x220 net/mptcp/protocol.c:2985 mptcp_check_listen_stop net/mptcp/mib.h:118 [inline] __mptcp_close+0x9b9/0xbd0 net/mptcp/protocol.c:3000 mptcp_close+0x2f/0x140 net/mptcp/protocol.c:3066 inet_release+0xed/0x200 net/ipv4/af_inet.c:435 inet6_release+0x4f/0x70 net/ipv6/af_inet6.c:487 __sock_release+0xb3/0x270 net/socket.c:649 sock_close+0x1c/0x30 net/socket.c:1439 __fput+0x402/0xb70 fs/file_table.c:465 task_work_run+0x150/0x240 kernel/task_work.c:227 resume_user_mode_work include/linux/resume_user_mode.h:50 [inline] exit_to_user_mode_loop+0xd4/0xe0 kernel/entry/common.c:114 exit_to_user_mode_prepare include/linux/entry-common.h:330 [inline] syscall_exit_to_user_mode_work include/linux/entry-common.h:414 [inline] syscall_exit_to_user_mode include/linux/entry-common.h:449 [inline] do_syscall_64+0x245/0x360 arch/x86/entry/syscall_64.c:100 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7fc92f8a36ad Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 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 ff ff 73 01 c3 48 c7 c1 b0 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007ffcf52802d8 EFLAGS: 00000246 ORIG_RAX: 00000000000001b4 RAX: 0000000000000000 RBX: 00007ffcf52803a8 RCX: 00007fc92f8a36ad RDX: 0000000000000000 RSI: 000000000000001e RDI: 0000000000000003 RBP: 00007fc92fae7ba0 R08: 0000000000000001 R09: 0000002800000000 R10: 00007fc92f700000 R11: 0000000000000246 R12: 00007fc92fae5fac R13: 00007fc92fae5fa0 R14: 0000000000026d00 R15: 0000000000026c51 </TASK> irq event stamp: 4068 hardirqs last enabled at (4076): [<ffffffff81544816>] __up_console_sem+0x76/0x80 kernel/printk/printk.c:344 hardirqs last disabled at (4085): [<ffffffff815447fb>] __up_console_sem+0x5b/0x80 kernel/printk/printk.c:342 softirqs last enabled at (3096): [<ffffffff840e1be0>] local_bh_enable include/linux/bottom_half.h:33 [inline] softirqs last enabled at (3096): [<ffffffff840e1be0>] inet_csk_listen_stop+0x2c0/0x1070 net/ipv4/inet_connection_sock.c:1524 softirqs last disabled at (3097): [<ffffffff813b6b9f>] do_softirq+0x3f/0x90 kernel/softirq.c:480 Since we need to track the 'fallback is possible' condition and the fallback status separately, there are a few possible races open between the check and the actual fallback action. Add a spinlock to protect the fallback related information and use it close all the possible related races. While at it also remove the too-early clearing of allow_infinite_fallback in __mptcp_subflow_connect(): the field will be correctly cleared by subflow_finish_connect() if/when the connection will complete successfully. If fallback is not possible, as per RFC, reset the current subflow. Since the fallback operation can now fail and return value should be checked, rename the helper accordingly. Fixes: 0530020a7c8f ("mptcp: track and update contiguous data status") Cc: stable(a)vger.kernel.org Reported-by: Matthieu Baerts <matttbe(a)kernel.org> Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/570 Reported-by: syzbot+5cf807c20386d699b524(a)syzkaller.appspotmail.com Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/555 Signed-off-by: Paolo Abeni <pabeni(a)redhat.com> Reviewed-by: Matthieu Baerts (NGI0) <matttbe(a)kernel.org> Signed-off-by: Matthieu Baerts (NGI0) <matttbe(a)kernel.org> Link: https://patch.msgid.link/20250714-net-mptcp-fallback-races-v1-1-391aff96332… Signed-off-by: Jakub Kicinski <kuba(a)kernel.org> Signed-off-by: Zhang Changzhong <zhangchangzhong(a)huawei.com> --- net/mptcp/options.c | 3 ++- net/mptcp/protocol.c | 40 +++++++++++++++++++++++++++++++++++----- net/mptcp/protocol.h | 26 +++++++++++++++++++------- net/mptcp/subflow.c | 11 +++++------ 4 files changed, 61 insertions(+), 19 deletions(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index fc277026..96049db 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -979,8 +979,9 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk, if (subflow->mp_join) goto reset; subflow->mp_capable = 0; + if (!mptcp_try_fallback(ssk)) + goto reset; pr_fallback(msk); - mptcp_do_fallback(ssk); return false; } diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 9f08fca..b5c2b2d 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -624,10 +624,9 @@ static bool mptcp_check_data_fin(struct sock *sk) static void mptcp_dss_corruption(struct mptcp_sock *msk, struct sock *ssk) { - if (READ_ONCE(msk->allow_infinite_fallback)) { + if (mptcp_try_fallback(ssk)) { MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_DSSCORRUPTIONFALLBACK); - mptcp_do_fallback(ssk); } else { MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_DSSCORRUPTIONRESET); mptcp_subflow_reset(ssk); @@ -890,6 +889,14 @@ static bool __mptcp_finish_join(struct mptcp_sock *msk, struct sock *ssk) if (sk->sk_state != TCP_ESTABLISHED) return false; + spin_lock_bh(&msk->fallback_lock); + if (__mptcp_check_fallback(msk)) { + spin_unlock_bh(&msk->fallback_lock); + return false; + } + mptcp_subflow_joined(msk, ssk); + spin_unlock_bh(&msk->fallback_lock); + /* attach to msk socket only after we are sure we will deal with it * at close time */ @@ -898,7 +905,6 @@ static bool __mptcp_finish_join(struct mptcp_sock *msk, struct sock *ssk) mptcp_subflow_ctx(ssk)->subflow_id = msk->subflow_id++; mptcp_sockopt_sync_locked(msk, ssk); - mptcp_subflow_joined(msk, ssk); mptcp_stop_tout_timer(sk); __mptcp_propagate_sndbuf(sk, ssk); return true; @@ -1237,10 +1243,14 @@ static void mptcp_update_infinite_map(struct mptcp_sock *msk, mpext->infinite_map = 1; mpext->data_len = 0; + if (!mptcp_try_fallback(ssk)) { + mptcp_subflow_reset(ssk); + return; + } + MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_INFINITEMAPTX); mptcp_subflow_ctx(ssk)->send_infinite_map = 0; pr_fallback(msk); - mptcp_do_fallback(ssk); } #define MPTCP_MAX_GSO_SIZE (GSO_LEGACY_MAX_SIZE - (MAX_TCP_HEADER + 1)) @@ -2644,9 +2654,9 @@ static void mptcp_check_fastclose(struct mptcp_sock *msk) static void __mptcp_retrans(struct sock *sk) { + struct mptcp_sendmsg_info info = { .data_lock_held = true, }; struct mptcp_sock *msk = mptcp_sk(sk); struct mptcp_subflow_context *subflow; - struct mptcp_sendmsg_info info = {}; struct mptcp_data_frag *dfrag; struct sock *ssk; int ret, err; @@ -2691,6 +2701,18 @@ static void __mptcp_retrans(struct sock *sk) info.sent = 0; info.limit = READ_ONCE(msk->csum_enabled) ? dfrag->data_len : dfrag->already_sent; + + /* + * make the whole retrans decision, xmit, disallow + * fallback atomic + */ + spin_lock_bh(&msk->fallback_lock); + if (__mptcp_check_fallback(msk)) { + spin_unlock_bh(&msk->fallback_lock); + release_sock(ssk); + return; + } + while (info.sent < info.limit) { ret = mptcp_sendmsg_frag(sk, ssk, dfrag, &info); if (ret <= 0) @@ -2706,6 +2728,7 @@ static void __mptcp_retrans(struct sock *sk) info.size_goal); WRITE_ONCE(msk->allow_infinite_fallback, false); } + spin_unlock_bh(&msk->fallback_lock); release_sock(ssk); } @@ -2842,6 +2865,7 @@ static void __mptcp_init_sock(struct sock *sk) msk->last_ack_recv = tcp_jiffies32; mptcp_pm_data_init(msk); + spin_lock_init(&msk->fallback_lock); /* re-use the csk retrans timer for MPTCP-level retrans */ timer_setup(&msk->sk.icsk_retransmit_timer, mptcp_retransmit_timer, 0); @@ -3638,7 +3662,13 @@ bool mptcp_finish_join(struct sock *ssk) /* active subflow, already present inside the conn_list */ if (!list_empty(&subflow->node)) { + spin_lock_bh(&msk->fallback_lock); + if (__mptcp_check_fallback(msk)) { + spin_unlock_bh(&msk->fallback_lock); + return false; + } mptcp_subflow_joined(msk, ssk); + spin_unlock_bh(&msk->fallback_lock); mptcp_propagate_sndbuf(parent, ssk); return true; } diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 2de9dce..a644427 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -345,6 +345,10 @@ struct mptcp_sock { u32 subflow_id; u32 setsockopt_seq; char ca_name[TCP_CA_NAME_MAX]; + + spinlock_t fallback_lock; /* protects fallback and + * allow_infinite_fallback + */ }; #define mptcp_data_lock(sk) spin_lock_bh(&(sk)->sk_lock.slock) @@ -1150,15 +1154,21 @@ static inline bool mptcp_check_fallback(const struct sock *sk) return __mptcp_check_fallback(msk); } -static inline void __mptcp_do_fallback(struct mptcp_sock *msk) +static inline bool __mptcp_try_fallback(struct mptcp_sock *msk) { if (__mptcp_check_fallback(msk)) { pr_debug("TCP fallback already done (msk=%p)\n", msk); - return; + return true; } - if (WARN_ON_ONCE(!READ_ONCE(msk->allow_infinite_fallback))) - return; + spin_lock_bh(&msk->fallback_lock); + if (!msk->allow_infinite_fallback) { + spin_unlock_bh(&msk->fallback_lock); + return false; + } + set_bit(MPTCP_FALLBACK_DONE, &msk->flags); + spin_unlock_bh(&msk->fallback_lock); + return true; } static inline bool __mptcp_has_initial_subflow(const struct mptcp_sock *msk) @@ -1170,14 +1180,15 @@ static inline bool __mptcp_has_initial_subflow(const struct mptcp_sock *msk) TCPF_SYN_RECV | TCPF_LISTEN)); } -static inline void mptcp_do_fallback(struct sock *ssk) +static inline bool mptcp_try_fallback(struct sock *ssk) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk); struct sock *sk = subflow->conn; struct mptcp_sock *msk; msk = mptcp_sk(sk); - __mptcp_do_fallback(msk); + if (!__mptcp_try_fallback(msk)) + return false; if (READ_ONCE(msk->snd_data_fin_enable) && !(ssk->sk_shutdown & SEND_SHUTDOWN)) { gfp_t saved_allocation = ssk->sk_allocation; @@ -1189,6 +1200,7 @@ static inline void mptcp_do_fallback(struct sock *ssk) tcp_shutdown(ssk, SEND_SHUTDOWN); ssk->sk_allocation = saved_allocation; } + return true; } #define pr_fallback(a) pr_debug("%s:fallback to TCP (msk=%p)\n", __func__, a) @@ -1198,7 +1210,7 @@ static inline void mptcp_subflow_early_fallback(struct mptcp_sock *msk, { pr_fallback(msk); subflow->request_mptcp = 0; - __mptcp_do_fallback(msk); + WARN_ON_ONCE(!__mptcp_try_fallback(msk)); } static inline bool mptcp_check_infinite_map(struct sk_buff *skb) diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index f277ec5..f052ea1 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -523,9 +523,11 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb) mptcp_get_options(skb, &mp_opt); if (subflow->request_mptcp) { if (!(mp_opt.suboptions & OPTION_MPTCP_MPC_SYNACK)) { + if (!mptcp_try_fallback(sk)) + goto do_reset; + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPCAPABLEACTIVEFALLBACK); - mptcp_do_fallback(sk); pr_fallback(msk); goto fallback; } @@ -1353,7 +1355,7 @@ static bool subflow_check_data_avail(struct sock *ssk) return true; } - if (!READ_ONCE(msk->allow_infinite_fallback)) { + if (!mptcp_try_fallback(ssk)) { /* fatal protocol error, close the socket. * subflow_error_report() will introduce the appropriate barriers */ @@ -1369,8 +1371,6 @@ static bool subflow_check_data_avail(struct sock *ssk) WRITE_ONCE(subflow->data_avail, false); return false; } - - mptcp_do_fallback(ssk); } skb = skb_peek(&ssk->sk_receive_queue); @@ -1635,7 +1635,6 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_pm_local *local, /* discard the subflow socket */ mptcp_sock_graft(ssk, sk->sk_socket); iput(SOCK_INODE(sf)); - WRITE_ONCE(msk->allow_infinite_fallback, false); mptcp_stop_tout_timer(sk); return 0; @@ -1812,7 +1811,7 @@ static void subflow_state_change(struct sock *sk) msk = mptcp_sk(parent); if (subflow_simultaneous_connect(sk)) { - mptcp_do_fallback(sk); + WARN_ON_ONCE(!mptcp_try_fallback(sk)); pr_fallback(msk); subflow->conn_finished = 1; mptcp_propagate_state(parent, sk, subflow, NULL); -- 2.9.5
2 1
0 0
[PATCH OLK-5.10] ipv6: sr: Fix MAC comparison to be constant-time
by Zhang Changzhong 07 Feb '26

07 Feb '26
From: Eric Biggers <ebiggers(a)kernel.org> mainline inclusion from mainline-v6.17-rc3 commit a458b2902115b26a25d67393b12ddd57d1216aaa category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/9027 CVE: CVE-2025-39702 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?… -------------------------------- To prevent timing attacks, MACs need to be compared in constant time. Use the appropriate helper function for this. Fixes: bf355b8d2c30 ("ipv6: sr: add core files for SR HMAC support") Cc: stable(a)vger.kernel.org Signed-off-by: Eric Biggers <ebiggers(a)kernel.org> Reviewed-by: Andrea Mayer <andrea.mayer(a)uniroma2.it> Link: https://patch.msgid.link/20250818202724.15713-1-ebiggers@kernel.org Signed-off-by: Jakub Kicinski <kuba(a)kernel.org> Conflicts: net/ipv6/seg6_hmac.c [conflicts due to not merge c616fb0cbae8 ("crypto: lib/utils - Move utilities into new header")] Signed-off-by: Wang Liang <wangliang74(a)huawei.com> Signed-off-by: Zhang Changzhong <zhangchangzhong(a)huawei.com> Reviewed-by: Yue Haibing <yuehaibing(a)huawei.com> Reviewed-by: Yue Haibing <yuehaibing(a)huawei.com> --- net/ipv6/seg6_hmac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c index 3c38002..dd7406a 100644 --- a/net/ipv6/seg6_hmac.c +++ b/net/ipv6/seg6_hmac.c @@ -35,6 +35,7 @@ #include <net/xfrm.h> #include <crypto/hash.h> +#include <crypto/algapi.h> #include <net/seg6.h> #include <net/genetlink.h> #include <net/seg6_hmac.h> @@ -269,7 +270,7 @@ bool seg6_hmac_validate_skb(struct sk_buff *skb) if (seg6_hmac_compute(hinfo, srh, &ipv6_hdr(skb)->saddr, hmac_output)) return false; - if (memcmp(hmac_output, tlv->hmac, SEG6_HMAC_FIELD_LEN) != 0) + if (crypto_memneq(hmac_output, tlv->hmac, SEG6_HMAC_FIELD_LEN)) return false; return true; -- 2.9.5
2 1
0 0
[PATCH OLK-5.10] ipv6: sr: Fix MAC comparison to be constant-time
by Zhang Changzhong 07 Feb '26

07 Feb '26
From: Eric Biggers <ebiggers(a)kernel.org> stable inclusion from stable-v6.6.103 commit 3ddd55cf19ed6cc62def5e3af10c2a9df1b861c3 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/9027 CVE: CVE-2025-39702 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id… -------------------------------- commit a458b2902115b26a25d67393b12ddd57d1216aaa upstream. To prevent timing attacks, MACs need to be compared in constant time. Use the appropriate helper function for this. Fixes: bf355b8d2c30 ("ipv6: sr: add core files for SR HMAC support") Cc: stable(a)vger.kernel.org Signed-off-by: Eric Biggers <ebiggers(a)kernel.org> Reviewed-by: Andrea Mayer <andrea.mayer(a)uniroma2.it> Link: https://patch.msgid.link/20250818202724.15713-1-ebiggers@kernel.org Signed-off-by: Jakub Kicinski <kuba(a)kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh(a)linuxfoundation.org> Signed-off-by: Wang Hai <wanghai38(a)huawei.com> Signed-off-by: Zhang Changzhong <zhangchangzhong(a)huawei.com> --- net/ipv6/seg6_hmac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c index 3c38002..22a5006 100644 --- a/net/ipv6/seg6_hmac.c +++ b/net/ipv6/seg6_hmac.c @@ -35,6 +35,7 @@ #include <net/xfrm.h> #include <crypto/hash.h> +#include <crypto/utils.h> #include <net/seg6.h> #include <net/genetlink.h> #include <net/seg6_hmac.h> @@ -269,7 +270,7 @@ bool seg6_hmac_validate_skb(struct sk_buff *skb) if (seg6_hmac_compute(hinfo, srh, &ipv6_hdr(skb)->saddr, hmac_output)) return false; - if (memcmp(hmac_output, tlv->hmac, SEG6_HMAC_FIELD_LEN) != 0) + if (crypto_memneq(hmac_output, tlv->hmac, SEG6_HMAC_FIELD_LEN)) return false; return true; -- 2.9.5
2 1
0 0
[PATCH OLK-6.6] tcp_bpf: Call sk_msg_free() when tcp_bpf_send_verdict() fails to allocate psock->cork.
by Zhang Changzhong 07 Feb '26

07 Feb '26
From: Kuniyuki Iwashima <kuniyu(a)google.com> stable inclusion from stable-v6.6.107 commit 66bcb04a441fbf15d66834b7e3eefb313dd750c8 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/8357 CVE: CVE-2025-39913 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id… -------------------------------- [ Upstream commit a3967baad4d533dc254c31e0d221e51c8d223d58 ] syzbot reported the splat below. [0] The repro does the following: 1. Load a sk_msg prog that calls bpf_msg_cork_bytes(msg, cork_bytes) 2. Attach the prog to a SOCKMAP 3. Add a socket to the SOCKMAP 4. Activate fault injection 5. Send data less than cork_bytes At 5., the data is carried over to the next sendmsg() as it is smaller than the cork_bytes specified by bpf_msg_cork_bytes(). Then, tcp_bpf_send_verdict() tries to allocate psock->cork to hold the data, but this fails silently due to fault injection + __GFP_NOWARN. If the allocation fails, we need to revert the sk->sk_forward_alloc change done by sk_msg_alloc(). Let's call sk_msg_free() when tcp_bpf_send_verdict fails to allocate psock->cork. The "*copied" also needs to be updated such that a proper error can be returned to the caller, sendmsg. It fails to allocate psock->cork. Nothing has been corked so far, so this patch simply sets "*copied" to 0. [0]: WARNING: net/ipv4/af_inet.c:156 at inet_sock_destruct+0x623/0x730 net/ipv4/af_inet.c:156, CPU#1: syz-executor/5983 Modules linked in: CPU: 1 UID: 0 PID: 5983 Comm: syz-executor Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/12/2025 RIP: 0010:inet_sock_destruct+0x623/0x730 net/ipv4/af_inet.c:156 Code: 0f 0b 90 e9 62 fe ff ff e8 7a db b5 f7 90 0f 0b 90 e9 95 fe ff ff e8 6c db b5 f7 90 0f 0b 90 e9 bb fe ff ff e8 5e db b5 f7 90 <0f> 0b 90 e9 e1 fe ff ff 89 f9 80 e1 07 80 c1 03 38 c1 0f 8c 9f fc RSP: 0018:ffffc90000a08b48 EFLAGS: 00010246 RAX: ffffffff8a09d0b2 RBX: dffffc0000000000 RCX: ffff888024a23c80 RDX: 0000000000000100 RSI: 0000000000000fff RDI: 0000000000000000 RBP: 0000000000000fff R08: ffff88807e07c627 R09: 1ffff1100fc0f8c4 R10: dffffc0000000000 R11: ffffed100fc0f8c5 R12: ffff88807e07c380 R13: dffffc0000000000 R14: ffff88807e07c60c R15: 1ffff1100fc0f872 FS: 00005555604c4500(0000) GS:ffff888125af1000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00005555604df5c8 CR3: 0000000032b06000 CR4: 00000000003526f0 Call Trace: <IRQ> __sk_destruct+0x86/0x660 net/core/sock.c:2339 rcu_do_batch kernel/rcu/tree.c:2605 [inline] rcu_core+0xca8/0x1770 kernel/rcu/tree.c:2861 handle_softirqs+0x286/0x870 kernel/softirq.c:579 __do_softirq kernel/softirq.c:613 [inline] invoke_softirq kernel/softirq.c:453 [inline] __irq_exit_rcu+0xca/0x1f0 kernel/softirq.c:680 irq_exit_rcu+0x9/0x30 kernel/softirq.c:696 instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1052 [inline] sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1052 </IRQ> Fixes: 4f738adba30a ("bpf: create tcp_bpf_ulp allowing BPF to monitor socket TX/RX data") Reported-by: syzbot+4cabd1d2fa917a456db8(a)syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/68c0b6b5.050a0220.3c6139.0013.GAE@google.com/ Signed-off-by: Kuniyuki Iwashima <kuniyu(a)google.com> Signed-off-by: Martin KaFai Lau <martin.lau(a)kernel.org> Link: https://patch.msgid.link/20250909232623.4151337-1-kuniyu@google.com Signed-off-by: Sasha Levin <sashal(a)kernel.org> Signed-off-by: Wang Liang <wangliang74(a)huawei.com> Signed-off-by: Zhang Changzhong <zhangchangzhong(a)huawei.com> --- net/ipv4/tcp_bpf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index e5845d0..72974e4 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -417,8 +417,11 @@ static int tcp_bpf_send_verdict(struct sock *sk, struct sk_psock *psock, if (!psock->cork) { psock->cork = kzalloc(sizeof(*psock->cork), GFP_ATOMIC | __GFP_NOWARN); - if (!psock->cork) + if (!psock->cork) { + sk_msg_free(sk, msg); + *copied = 0; return -ENOMEM; + } } memcpy(psock->cork, msg, sizeof(*msg)); return 0; -- 2.9.5
2 1
0 0
[PATCH OLK-5.10] tcp_bpf: Call sk_msg_free() when tcp_bpf_send_verdict() fails to allocate psock->cork.
by Zhang Changzhong 07 Feb '26

07 Feb '26
From: Kuniyuki Iwashima <kuniyu(a)google.com> stable inclusion from stable-v5.10.245 commit 05366527f44cf4b884f3d9462ae8009be9665856 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/8357 CVE: CVE-2025-39913 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id… -------------------------------- [ Upstream commit a3967baad4d533dc254c31e0d221e51c8d223d58 ] syzbot reported the splat below. [0] The repro does the following: 1. Load a sk_msg prog that calls bpf_msg_cork_bytes(msg, cork_bytes) 2. Attach the prog to a SOCKMAP 3. Add a socket to the SOCKMAP 4. Activate fault injection 5. Send data less than cork_bytes At 5., the data is carried over to the next sendmsg() as it is smaller than the cork_bytes specified by bpf_msg_cork_bytes(). Then, tcp_bpf_send_verdict() tries to allocate psock->cork to hold the data, but this fails silently due to fault injection + __GFP_NOWARN. If the allocation fails, we need to revert the sk->sk_forward_alloc change done by sk_msg_alloc(). Let's call sk_msg_free() when tcp_bpf_send_verdict fails to allocate psock->cork. The "*copied" also needs to be updated such that a proper error can be returned to the caller, sendmsg. It fails to allocate psock->cork. Nothing has been corked so far, so this patch simply sets "*copied" to 0. [0]: WARNING: net/ipv4/af_inet.c:156 at inet_sock_destruct+0x623/0x730 net/ipv4/af_inet.c:156, CPU#1: syz-executor/5983 Modules linked in: CPU: 1 UID: 0 PID: 5983 Comm: syz-executor Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/12/2025 RIP: 0010:inet_sock_destruct+0x623/0x730 net/ipv4/af_inet.c:156 Code: 0f 0b 90 e9 62 fe ff ff e8 7a db b5 f7 90 0f 0b 90 e9 95 fe ff ff e8 6c db b5 f7 90 0f 0b 90 e9 bb fe ff ff e8 5e db b5 f7 90 <0f> 0b 90 e9 e1 fe ff ff 89 f9 80 e1 07 80 c1 03 38 c1 0f 8c 9f fc RSP: 0018:ffffc90000a08b48 EFLAGS: 00010246 RAX: ffffffff8a09d0b2 RBX: dffffc0000000000 RCX: ffff888024a23c80 RDX: 0000000000000100 RSI: 0000000000000fff RDI: 0000000000000000 RBP: 0000000000000fff R08: ffff88807e07c627 R09: 1ffff1100fc0f8c4 R10: dffffc0000000000 R11: ffffed100fc0f8c5 R12: ffff88807e07c380 R13: dffffc0000000000 R14: ffff88807e07c60c R15: 1ffff1100fc0f872 FS: 00005555604c4500(0000) GS:ffff888125af1000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00005555604df5c8 CR3: 0000000032b06000 CR4: 00000000003526f0 Call Trace: <IRQ> __sk_destruct+0x86/0x660 net/core/sock.c:2339 rcu_do_batch kernel/rcu/tree.c:2605 [inline] rcu_core+0xca8/0x1770 kernel/rcu/tree.c:2861 handle_softirqs+0x286/0x870 kernel/softirq.c:579 __do_softirq kernel/softirq.c:613 [inline] invoke_softirq kernel/softirq.c:453 [inline] __irq_exit_rcu+0xca/0x1f0 kernel/softirq.c:680 irq_exit_rcu+0x9/0x30 kernel/softirq.c:696 instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1052 [inline] sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1052 </IRQ> Fixes: 4f738adba30a ("bpf: create tcp_bpf_ulp allowing BPF to monitor socket TX/RX data") Reported-by: syzbot+4cabd1d2fa917a456db8(a)syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/68c0b6b5.050a0220.3c6139.0013.GAE@google.com/ Signed-off-by: Kuniyuki Iwashima <kuniyu(a)google.com> Signed-off-by: Martin KaFai Lau <martin.lau(a)kernel.org> Link: https://patch.msgid.link/20250909232623.4151337-1-kuniyu@google.com Signed-off-by: Sasha Levin <sashal(a)kernel.org> Signed-off-by: Zhang Changzhong <zhangchangzhong(a)huawei.com> --- net/ipv4/tcp_bpf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 3841214..f780ba0 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -412,8 +412,11 @@ static int tcp_bpf_send_verdict(struct sock *sk, struct sk_psock *psock, if (!psock->cork) { psock->cork = kzalloc(sizeof(*psock->cork), GFP_ATOMIC | __GFP_NOWARN); - if (!psock->cork) + if (!psock->cork) { + sk_msg_free(sk, msg); + *copied = 0; return -ENOMEM; + } } memcpy(psock->cork, msg, sizeof(*msg)); return 0; -- 2.9.5
2 1
0 0
[PATCH OLK-5.10] i40e: Fix potential invalid access when MAC list is empty
by Zhang Changzhong 07 Feb '26

07 Feb '26
From: Zhen Ni <zhen.ni(a)easystack.cn> stable inclusion from stable-v5.10.243 commit 3c6fb929afa313d9d11f780451d113f73922fe5d category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/8427 CVE: CVE-2025-39853 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id… -------------------------------- [ Upstream commit a556f06338e1d5a85af0e32ecb46e365547f92b9 ] list_first_entry() never returns NULL - if the list is empty, it still returns a pointer to an invalid object, leading to potential invalid memory access when dereferenced. Fix this by using list_first_entry_or_null instead of list_first_entry. Fixes: e3219ce6a775 ("i40e: Add support for client interface for IWARP driver") Signed-off-by: Zhen Ni <zhen.ni(a)easystack.cn> Reviewed-by: Paul Menzel <pmenzel(a)molgen.mpg.de> Signed-off-by: Tony Nguyen <anthony.l.nguyen(a)intel.com> Signed-off-by: Sasha Levin <sashal(a)kernel.org> Signed-off-by: Zhang Changzhong <zhangchangzhong(a)huawei.com> --- drivers/net/ethernet/intel/i40e/i40e_client.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.c b/drivers/net/ethernet/intel/i40e/i40e_client.c index b3cb5d1..af65321 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.c +++ b/drivers/net/ethernet/intel/i40e/i40e_client.c @@ -318,8 +318,8 @@ static void i40e_client_add_instance(struct i40e_pf *pf) return; } - mac = list_first_entry(&cdev->lan_info.netdev->dev_addrs.list, - struct netdev_hw_addr, list); + mac = list_first_entry_or_null(&cdev->lan_info.netdev->dev_addrs.list, + struct netdev_hw_addr, list); if (mac) ether_addr_copy(cdev->lan_info.lanmac, mac->addr); else -- 2.9.5
2 1
0 0
[PATCH OLK-5.10] Bluetooth: Fix use-after-free in l2cap_sock_cleanup_listen()
by Zhang Changzhong 07 Feb '26

07 Feb '26
From: Kuniyuki Iwashima <kuniyu(a)google.com> stable inclusion from stable-v5.10.243 commit 83e1d9892ef51785cf0760b7681436760dda435a category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/8438 CVE: CVE-2025-39860 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id… -------------------------------- [ Upstream commit 862c628108562d8c7a516a900034823b381d3cba ] syzbot reported the splat below without a repro. In the splat, a single thread calling bt_accept_dequeue() freed sk and touched it after that. The root cause would be the racy l2cap_sock_cleanup_listen() call added by the cited commit. bt_accept_dequeue() is called under lock_sock() except for l2cap_sock_release(). Two threads could see the same socket during the list iteration in bt_accept_dequeue(): CPU1 CPU2 (close()) ---- ---- sock_hold(sk) sock_hold(sk); lock_sock(sk) <-- block close() sock_put(sk) bt_accept_unlink(sk) sock_put(sk) <-- refcnt by bt_accept_enqueue() release_sock(sk) lock_sock(sk) sock_put(sk) bt_accept_unlink(sk) sock_put(sk) <-- last refcnt bt_accept_unlink(sk) <-- UAF Depending on the timing, the other thread could show up in the "Freed by task" part. Let's call l2cap_sock_cleanup_listen() under lock_sock() in l2cap_sock_release(). [0]: BUG: KASAN: slab-use-after-free in debug_spin_lock_before kernel/locking/spinlock_debug.c:86 [inline] BUG: KASAN: slab-use-after-free in do_raw_spin_lock+0x26f/0x2b0 kernel/locking/spinlock_debug.c:115 Read of size 4 at addr ffff88803b7eb1c4 by task syz.5.3276/16995 CPU: 3 UID: 0 PID: 16995 Comm: syz.5.3276 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2~bpo12+1 04/01/2014 Call Trace: <TASK> __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xcd/0x630 mm/kasan/report.c:482 kasan_report+0xe0/0x110 mm/kasan/report.c:595 debug_spin_lock_before kernel/locking/spinlock_debug.c:86 [inline] do_raw_spin_lock+0x26f/0x2b0 kernel/locking/spinlock_debug.c:115 spin_lock_bh include/linux/spinlock.h:356 [inline] release_sock+0x21/0x220 net/core/sock.c:3746 bt_accept_dequeue+0x505/0x600 net/bluetooth/af_bluetooth.c:312 l2cap_sock_cleanup_listen+0x5c/0x2a0 net/bluetooth/l2cap_sock.c:1451 l2cap_sock_release+0x5c/0x210 net/bluetooth/l2cap_sock.c:1425 __sock_release+0xb3/0x270 net/socket.c:649 sock_close+0x1c/0x30 net/socket.c:1439 __fput+0x3ff/0xb70 fs/file_table.c:468 task_work_run+0x14d/0x240 kernel/task_work.c:227 resume_user_mode_work include/linux/resume_user_mode.h:50 [inline] exit_to_user_mode_loop+0xeb/0x110 kernel/entry/common.c:43 exit_to_user_mode_prepare include/linux/irq-entry-common.h:225 [inline] syscall_exit_to_user_mode_work include/linux/entry-common.h:175 [inline] syscall_exit_to_user_mode include/linux/entry-common.h:210 [inline] do_syscall_64+0x3f6/0x4c0 arch/x86/entry/syscall_64.c:100 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f2accf8ebe9 Code: ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 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 ff ff 73 01 c3 48 c7 c1 a8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007ffdb6cb1378 EFLAGS: 00000246 ORIG_RAX: 00000000000001b4 RAX: 0000000000000000 RBX: 00000000000426fb RCX: 00007f2accf8ebe9 RDX: 0000000000000000 RSI: 000000000000001e RDI: 0000000000000003 RBP: 00007f2acd1b7da0 R08: 0000000000000001 R09: 00000012b6cb166f R10: 0000001b30e20000 R11: 0000000000000246 R12: 00007f2acd1b609c R13: 00007f2acd1b6090 R14: ffffffffffffffff R15: 00007ffdb6cb1490 </TASK> Allocated by task 5326: kasan_save_stack+0x33/0x60 mm/kasan/common.c:47 kasan_save_track+0x14/0x30 mm/kasan/common.c:68 poison_kmalloc_redzone mm/kasan/common.c:388 [inline] __kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:405 kasan_kmalloc include/linux/kasan.h:260 [inline] __do_kmalloc_node mm/slub.c:4365 [inline] __kmalloc_noprof+0x223/0x510 mm/slub.c:4377 kmalloc_noprof include/linux/slab.h:909 [inline] sk_prot_alloc+0x1a8/0x2a0 net/core/sock.c:2239 sk_alloc+0x36/0xc20 net/core/sock.c:2295 bt_sock_alloc+0x3b/0x3a0 net/bluetooth/af_bluetooth.c:151 l2cap_sock_alloc.constprop.0+0x33/0x1d0 net/bluetooth/l2cap_sock.c:1894 l2cap_sock_new_connection_cb+0x101/0x240 net/bluetooth/l2cap_sock.c:1482 l2cap_connect_cfm+0x4c4/0xf80 net/bluetooth/l2cap_core.c:7287 hci_connect_cfm include/net/bluetooth/hci_core.h:2050 [inline] hci_remote_features_evt+0x4dd/0x970 net/bluetooth/hci_event.c:3712 hci_event_func net/bluetooth/hci_event.c:7519 [inline] hci_event_packet+0xa0d/0x11c0 net/bluetooth/hci_event.c:7573 hci_rx_work+0x2c5/0x16b0 net/bluetooth/hci_core.c:4071 process_one_work+0x9cf/0x1b70 kernel/workqueue.c:3236 process_scheduled_works kernel/workqueue.c:3319 [inline] worker_thread+0x6c8/0xf10 kernel/workqueue.c:3400 kthread+0x3c2/0x780 kernel/kthread.c:463 ret_from_fork+0x5d7/0x6f0 arch/x86/kernel/process.c:148 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 Freed by task 16995: kasan_save_stack+0x33/0x60 mm/kasan/common.c:47 kasan_save_track+0x14/0x30 mm/kasan/common.c:68 kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:576 poison_slab_object mm/kasan/common.c:243 [inline] __kasan_slab_free+0x60/0x70 mm/kasan/common.c:275 kasan_slab_free include/linux/kasan.h:233 [inline] slab_free_hook mm/slub.c:2417 [inline] slab_free mm/slub.c:4680 [inline] kfree+0x2b4/0x4d0 mm/slub.c:4879 sk_prot_free net/core/sock.c:2278 [inline] __sk_destruct+0x75f/0x9a0 net/core/sock.c:2373 sk_destruct+0xc2/0xf0 net/core/sock.c:2401 __sk_free+0xf4/0x3e0 net/core/sock.c:2412 sk_free+0x6a/0x90 net/core/sock.c:2423 sock_put include/net/sock.h:1960 [inline] bt_accept_unlink+0x245/0x2e0 net/bluetooth/af_bluetooth.c:262 bt_accept_dequeue+0x517/0x600 net/bluetooth/af_bluetooth.c:308 l2cap_sock_cleanup_listen+0x5c/0x2a0 net/bluetooth/l2cap_sock.c:1451 l2cap_sock_release+0x5c/0x210 net/bluetooth/l2cap_sock.c:1425 __sock_release+0xb3/0x270 net/socket.c:649 sock_close+0x1c/0x30 net/socket.c:1439 __fput+0x3ff/0xb70 fs/file_table.c:468 task_work_run+0x14d/0x240 kernel/task_work.c:227 resume_user_mode_work include/linux/resume_user_mode.h:50 [inline] exit_to_user_mode_loop+0xeb/0x110 kernel/entry/common.c:43 exit_to_user_mode_prepare include/linux/irq-entry-common.h:225 [inline] syscall_exit_to_user_mode_work include/linux/entry-common.h:175 [inline] syscall_exit_to_user_mode include/linux/entry-common.h:210 [inline] do_syscall_64+0x3f6/0x4c0 arch/x86/entry/syscall_64.c:100 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: 1728137b33c0 ("Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_ready_cb") Reported-by: syzbot+e5e64cdf8e92046dd3e1(a)syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-bluetooth/68af6b9d.a70a0220.3cafd4.0032.GAE@g… Signed-off-by: Kuniyuki Iwashima <kuniyu(a)google.com> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz(a)intel.com> Signed-off-by: Sasha Levin <sashal(a)kernel.org> Signed-off-by: Zhang Changzhong <zhangchangzhong(a)huawei.com> --- net/bluetooth/l2cap_sock.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 6537d08..86bd815 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1408,7 +1408,10 @@ static int l2cap_sock_release(struct socket *sock) if (!sk) return 0; + lock_sock_nested(sk, L2CAP_NESTING_PARENT); l2cap_sock_cleanup_listen(sk); + release_sock(sk); + bt_sock_unlink(&l2cap_sk_list, sk); err = l2cap_sock_shutdown(sock, SHUT_RDWR); -- 2.9.5
2 1
0 0
[PATCH OLK-6.6] x86/CPU/AMD: Add RDSEED fix for Zen5
by Bowen You 06 Feb '26

06 Feb '26
From: Gregory Price <gourry(a)gourry.net> stable inclusion from stable-v6.12.57 commit e980de2ff109dacb6d9d3a77f01b27c467115ecb category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/11500 CVE: CVE-2025-68313 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id… -------------------------------- commit 607b9fb2ce248cc5b633c5949e0153838992c152 upstream. There's an issue with RDSEED's 16-bit and 32-bit register output variants on Zen5 which return a random value of 0 "at a rate inconsistent with randomness while incorrectly signaling success (CF=1)". Search the web for AMD-SB-7055 for more detail. Add a fix glue which checks microcode revisions. [ bp: Add microcode revisions checking, rewrite. ] Cc: stable(a)vger.kernel.org Signed-off-by: Gregory Price <gourry(a)gourry.net> Signed-off-by: Borislav Petkov (AMD) <bp(a)alien8.de> Link: https://lore.kernel.org/r/20251018024010.4112396-1-gourry@gourry.net [ bp: 6.12 backport: use the alternative microcode version checking. ] Signed-off-by: Borislav Petkov (AMD) <bp(a)alien8.de> Signed-off-by: Greg Kroah-Hartman <gregkh(a)linuxfoundation.org> Conflicts: arch/x86/include/asm/cpu.h arch/x86/kernel/cpu/microcode/amd.c [move the definition of zen_patch_rev] Signed-off-by: Bowen You <youbowen2(a)huawei.com> --- arch/x86/include/asm/cpu.h | 12 ++++++++++ arch/x86/kernel/cpu/amd.c | 35 +++++++++++++++++++++++++++++ arch/x86/kernel/cpu/microcode/amd.c | 12 ---------- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h index 43a228e11209..d252aa7f04fb 100644 --- a/arch/x86/include/asm/cpu.h +++ b/arch/x86/include/asm/cpu.h @@ -84,4 +84,16 @@ int intel_microcode_sanity_check(void *mc, bool print_err, int hdr_type); extern struct cpumask cpus_stop_mask; +union zen_patch_rev { + struct { + __u32 rev : 8, + stepping : 4, + model : 4, + __reserved : 4, + ext_model : 4, + ext_fam : 8; + }; + __u32 ucode_rev; +}; + #endif /* _ASM_X86_CPU_H */ diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index f89dbe6f48e6..5a46bf0cce5d 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -1135,8 +1135,43 @@ static void init_amd_zen4(struct cpuinfo_x86 *c) } } +static bool check_rdseed_microcode(void) +{ + struct cpuinfo_x86 *c = &boot_cpu_data; + union zen_patch_rev p; + u32 min_rev = 0; + + p.ext_fam = c->x86 - 0xf; + p.model = c->x86_model; + p.ext_model = c->x86_model >> 4; + p.stepping = c->x86_stepping; + /* reserved bits are expected to be 0 in test below */ + p.__reserved = 0; + + if (cpu_has(c, X86_FEATURE_ZEN5)) { + switch (p.ucode_rev >> 8) { + case 0xb0021: min_rev = 0xb00215a; break; + case 0xb1010: min_rev = 0xb101054; break; + default: + pr_debug("%s: ucode_rev: 0x%x, current revision: 0x%x\n", + __func__, p.ucode_rev, c->microcode); + return false; + } + } + + if (!min_rev) + return false; + + return c->microcode >= min_rev; +} + static void init_amd_zen5(struct cpuinfo_x86 *c) { + if (!check_rdseed_microcode()) { + clear_cpu_cap(c, X86_FEATURE_RDSEED); + msr_clear_bit(MSR_AMD64_CPUID_FN_7, 18); + pr_emerg_once("RDSEED32 is broken. Disabling the corresponding CPUID bit.\n"); + } } static void init_amd(struct cpuinfo_x86 *c) diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c index b6990dc26274..75746a991f8e 100644 --- a/arch/x86/kernel/cpu/microcode/amd.c +++ b/arch/x86/kernel/cpu/microcode/amd.c @@ -96,18 +96,6 @@ static struct equiv_cpu_table { struct equiv_cpu_entry *entry; } equiv_table; -union zen_patch_rev { - struct { - __u32 rev : 8, - stepping : 4, - model : 4, - __reserved : 4, - ext_model : 4, - ext_fam : 8; - }; - __u32 ucode_rev; -}; - union cpuid_1_eax { struct { __u32 stepping : 4, -- 2.34.1
2 1
0 0
[PATCH OLK-5.10] fbdev: Fix vmalloc out-of-bounds write in fast_imageblit
by Jiacheng Yu 06 Feb '26

06 Feb '26
From: Sravan Kumar Gundu <sravankumarlpu(a)gmail.com> mainline inclusion from mainline-v6.17-rc1 commit af0db3c1f898144846d4c172531a199bb3ca375d category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/9085 CVE: CVE-2025-38685 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?… -------------------------------- This issue triggers when a userspace program does an ioctl FBIOPUT_CON2FBMAP by passing console number and frame buffer number. Ideally this maps console to frame buffer and updates the screen if console is visible. As part of mapping it has to do resize of console according to frame buffer info. if this resize fails and returns from vc_do_resize() and continues further. At this point console and new frame buffer are mapped and sets display vars. Despite failure still it continue to proceed updating the screen at later stages where vc_data is related to previous frame buffer and frame buffer info and display vars are mapped to new frame buffer and eventully leading to out-of-bounds write in fast_imageblit(). This bheviour is excepted only when fg_console is equal to requested console which is a visible console and updates screen with invalid struct references in fbcon_putcs(). Reported-and-tested-by: syzbot+c4b7aa0513823e2ea880(a)syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=c4b7aa0513823e2ea880 Signed-off-by: Sravan Kumar Gundu <sravankumarlpu(a)gmail.com> Cc: stable(a)vger.kernel.org Signed-off-by: Helge Deller <deller(a)gmx.de> Conflicts: drivers/video/fbdev/core/fbcon.c [Context conflicts.] Signed-off-by: Jiacheng Yu <yujiacheng3(a)huawei.com> --- drivers/video/fbdev/core/fbcon.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 81bf4f7bd9e8..66ecb2a044c6 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -827,7 +827,8 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, fg_vc->vc_rows); } - update_screen(vc_cons[fg_console].d); + if (fg_console != unit) + update_screen(vc_cons[fg_console].d); } /** @@ -1366,6 +1367,7 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, struct vc_data *svc; struct fbcon_ops *ops = info->fbcon_par; int rows, cols, charcnt = 256; + unsigned long ret = 0; p = &fb_display[unit]; @@ -1417,11 +1419,10 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); cols /= vc->vc_font.width; rows /= vc->vc_font.height; - vc_resize(vc, cols, rows); + ret = vc_resize(vc, cols, rows); - if (con_is_visible(vc)) { + if (con_is_visible(vc) && !ret) update_screen(vc); - } } static __inline__ void ywrap_up(struct vc_data *vc, int count) -- 2.43.0
2 1
0 0
[PATCH OLK-5.10] Revert "!19950 [OLK-5.10] Drivers GPU Patches from 6.6"
by Tengda Wu 06 Feb '26

06 Feb '26
This reverts commit 84c1c0818c45cdfbbaa4b5ba4b8a552a52f5760e, reversing changes made to 67fd5a0f7d0f1baceab00f0d3901a94803f0aee1. Commit 70dcc68078ac introduced a null-pointer dereference exception, causing kernel boot failure: Unable to handle kernel NULL pointer dereference at virtual address 0000000000000048 Call trace: hibmc_debugfs_init+0x38/0x68 [hibmc_drm] hibmc_pci_probe+0xdc/0x1ac [hibmc_drm] local_pci_probe+0x48/0xb0 work_for_cpu_fn+0x24/0x50 process_one_work+0x1d8/0x4c0 worker_thread+0x25c/0x470 kthread+0x108/0x150 Temporarily revert the entire associated patch series as a precautionary measure to circumvent the issue. Fixes: 70dcc68078ac ("drm/hisilicon/hibmc: hibmc-drm bugfix for DP") Signed-off-by: Tengda Wu <wutengda2(a)huawei.com> --- drivers/gpu/drm/drm_connector.c | 9 - drivers/gpu/drm/drm_internal.h | 2 - drivers/gpu/drm/drm_probe_helper.c | 29 -- drivers/gpu/drm/drm_sysfs.c | 17 +- drivers/gpu/drm/hisilicon/hibmc/Kconfig | 2 - drivers/gpu/drm/hisilicon/hibmc/Makefile | 4 +- drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c | 167 ------- drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h | 73 --- .../gpu/drm/hisilicon/hibmc/dp/dp_config.h | 21 - drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c | 338 -------------- drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h | 74 ---- drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c | 415 ------------------ drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h | 140 ------ .../gpu/drm/hisilicon/hibmc/dp/dp_serdes.c | 59 --- .../drm/hisilicon/hibmc/hibmc_drm_debugfs.c | 149 ------- .../gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c | 257 ----------- .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 127 ++---- .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 31 +- .../gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c | 48 +- .../gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 60 +-- include/drm/drm_probe_helper.h | 3 - 21 files changed, 79 insertions(+), 1946 deletions(-) delete mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c delete mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h delete mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h delete mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c delete mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h delete mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c delete mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h delete mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c delete mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_debugfs.c delete mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index fa9675396287..9c3bbe2c3e6f 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -525,10 +525,6 @@ int drm_connector_register(struct drm_connector *connector) goto err_debugfs; } - ret = drm_sysfs_connector_add_late(connector); - if (ret) - goto err_late_register; - drm_mode_object_register(connector->dev, &connector->base); connector->registration_state = DRM_CONNECTOR_REGISTERED; @@ -538,9 +534,6 @@ int drm_connector_register(struct drm_connector *connector) goto unlock; -err_late_register: - if (connector->funcs->early_unregister) - connector->funcs->early_unregister(connector); err_debugfs: drm_debugfs_connector_remove(connector); drm_sysfs_connector_remove(connector); @@ -567,8 +560,6 @@ void drm_connector_unregister(struct drm_connector *connector) return; } - drm_sysfs_connector_remove_early(connector); - if (connector->funcs->early_unregister) connector->funcs->early_unregister(connector); diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index fbad756446a2..41efe40bc70f 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -165,8 +165,6 @@ int drm_sysfs_init(void); void drm_sysfs_destroy(void); struct device *drm_sysfs_minor_alloc(struct drm_minor *minor); int drm_sysfs_connector_add(struct drm_connector *connector); -int drm_sysfs_connector_add_late(struct drm_connector *connector); -void drm_sysfs_connector_remove_early(struct drm_connector *connector); void drm_sysfs_connector_remove(struct drm_connector *connector); void drm_sysfs_lease_event(struct drm_device *dev); diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index ef4eb87fa599..d3f0d048594e 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -875,32 +875,3 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) return changed; } EXPORT_SYMBOL(drm_helper_hpd_irq_event); - -/** - * drm_connector_helper_detect_from_ddc - Read EDID and detect connector status. - * @connector: The connector - * @ctx: Acquire context - * @force: Perform screen-destructive operations, if necessary - * - * Detects the connector status by reading the EDID using drm_probe_ddc(), - * which requires connector->ddc to be set. Returns connector_status_connected - * on success or connector_status_disconnected on failure. - * - * Returns: - * The connector status as defined by enum drm_connector_status. - */ -int drm_connector_helper_detect_from_ddc(struct drm_connector *connector, - struct drm_modeset_acquire_ctx *ctx, - bool force) -{ - struct i2c_adapter *ddc = connector->ddc; - - if (!ddc) - return connector_status_unknown; - - if (drm_probe_ddc(ddc)) - return connector_status_connected; - - return connector_status_disconnected; -} -EXPORT_SYMBOL(drm_connector_helper_detect_from_ddc); diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 5f65981bdaf4..f0336c804639 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -291,29 +291,20 @@ int drm_sysfs_connector_add(struct drm_connector *connector) return PTR_ERR(connector->kdev); } - return 0; -} - -int drm_sysfs_connector_add_late(struct drm_connector *connector) -{ if (connector->ddc) return sysfs_create_link(&connector->kdev->kobj, - &connector->ddc->dev.kobj, "ddc"); - + &connector->ddc->dev.kobj, "ddc"); return 0; } -void drm_sysfs_connector_remove_early(struct drm_connector *connector) -{ - if (connector->ddc) - sysfs_remove_link(&connector->kdev->kobj, "ddc"); -} - void drm_sysfs_connector_remove(struct drm_connector *connector) { if (!connector->kdev) return; + if (connector->ddc) + sysfs_remove_link(&connector->kdev->kobj, "ddc"); + DRM_DEBUG("removing \"%s\" from sysfs\n", connector->name); diff --git a/drivers/gpu/drm/hisilicon/hibmc/Kconfig b/drivers/gpu/drm/hisilicon/hibmc/Kconfig index 44c83c80aa68..4e41c144a290 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/Kconfig +++ b/drivers/gpu/drm/hisilicon/hibmc/Kconfig @@ -3,8 +3,6 @@ config DRM_HISI_HIBMC tristate "DRM Support for Hisilicon Hibmc" depends on DRM && PCI && (ARM64 || COMPILE_TEST) depends on MMU - select DRM_DISPLAY_HELPER - select DRM_DISPLAY_DP_HELPER select DRM_KMS_HELPER select DRM_VRAM_HELPER select DRM_TTM diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile index 43fe471caee3..684ef794eb7c 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/Makefile +++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile @@ -1,6 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_ttm.o hibmc_drm_i2c.o \ - dp/dp_aux.o dp/dp_link.o dp/dp_hw.o dp/dp_serdes.o hibmc_drm_dp.o \ - hibmc_drm_debugfs.o +hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_ttm.o hibmc_drm_i2c.o obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c deleted file mode 100644 index 533611a211ad..000000000000 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c +++ /dev/null @@ -1,167 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// Copyright (c) 2024 Hisilicon Limited. - -#include <linux/io.h> -#include <linux/iopoll.h> -#include <linux/minmax.h> -#include <drm/drm_device.h> -#include <drm/drm_print.h> -#include "dp_comm.h" -#include "dp_reg.h" -#include "dp_hw.h" - -#define HIBMC_AUX_CMD_REQ_LEN GENMASK(7, 4) -#define HIBMC_AUX_CMD_ADDR GENMASK(27, 8) -#define HIBMC_AUX_CMD_I2C_ADDR_ONLY BIT(28) -#define HIBMC_BYTES_IN_U32 4 -#define HIBMC_AUX_I2C_WRITE_SUCCESS 0x1 -#define HIBMC_DP_MIN_PULSE_NUM 0x9 -#define BITS_IN_U8 8 - -static inline void hibmc_dp_aux_reset(struct hibmc_dp_dev *dp) -{ - hibmc_dp_reg_write_field(dp, HIBMC_DP_DPTX_RST_CTRL, HIBMC_DP_CFG_AUX_RST_N, 0x0); - usleep_range(10, 15); - hibmc_dp_reg_write_field(dp, HIBMC_DP_DPTX_RST_CTRL, HIBMC_DP_CFG_AUX_RST_N, 0x1); -} - -static void hibmc_dp_aux_read_data(struct hibmc_dp_dev *dp, u8 *buf, u8 size) -{ - u32 reg_num; - u32 value; - u32 num; - u8 i, j; - - reg_num = DIV_ROUND_UP(size, HIBMC_BYTES_IN_U32); - for (i = 0; i < reg_num; i++) { - /* number of bytes read from a single register */ - num = min(size - i * HIBMC_BYTES_IN_U32, HIBMC_BYTES_IN_U32); - value = readl(dp->base + HIBMC_DP_AUX_RD_DATA0 + i * HIBMC_BYTES_IN_U32); - /* convert the 32-bit value of the register to the buffer. */ - for (j = 0; j < num; j++) - buf[i * HIBMC_BYTES_IN_U32 + j] = value >> (j * BITS_IN_U8); - } -} - -static void hibmc_dp_aux_write_data(struct hibmc_dp_dev *dp, u8 *buf, u8 size) -{ - u32 reg_num; - u32 value; - u32 num; - u8 i, j; - - reg_num = DIV_ROUND_UP(size, HIBMC_BYTES_IN_U32); - for (i = 0; i < reg_num; i++) { - /* number of bytes written to a single register */ - num = min_t(u8, size - i * HIBMC_BYTES_IN_U32, HIBMC_BYTES_IN_U32); - value = 0; - /* obtain the 32-bit value written to a single register. */ - for (j = 0; j < num; j++) - value |= buf[i * HIBMC_BYTES_IN_U32 + j] << (j * BITS_IN_U8); - /* writing data to a single register */ - writel(value, dp->base + HIBMC_DP_AUX_WR_DATA0 + i * HIBMC_BYTES_IN_U32); - } -} - -static u32 hibmc_dp_aux_build_cmd(const struct drm_dp_aux_msg *msg) -{ - u32 aux_cmd = msg->request; - - if (msg->size) - aux_cmd |= FIELD_PREP(HIBMC_AUX_CMD_REQ_LEN, (msg->size - 1)); - else - aux_cmd |= FIELD_PREP(HIBMC_AUX_CMD_I2C_ADDR_ONLY, 1); - - aux_cmd |= FIELD_PREP(HIBMC_AUX_CMD_ADDR, msg->address); - - return aux_cmd; -} - -/* ret >= 0, ret is size; ret < 0, ret is err code */ -static int hibmc_dp_aux_parse_xfer(struct hibmc_dp_dev *dp, struct drm_dp_aux_msg *msg) -{ - u32 buf_data_cnt; - u32 aux_status; - - aux_status = readl(dp->base + HIBMC_DP_AUX_STATUS); - msg->reply = FIELD_GET(HIBMC_DP_CFG_AUX_STATUS, aux_status); - - if (aux_status & HIBMC_DP_CFG_AUX_TIMEOUT) - return -ETIMEDOUT; - - /* only address */ - if (!msg->size) - return 0; - - if (msg->reply != DP_AUX_NATIVE_REPLY_ACK) - return -EIO; - - buf_data_cnt = FIELD_GET(HIBMC_DP_CFG_AUX_READY_DATA_BYTE, aux_status); - - switch (msg->request) { - case DP_AUX_NATIVE_WRITE: - return msg->size; - case DP_AUX_I2C_WRITE | DP_AUX_I2C_MOT: - if (buf_data_cnt == HIBMC_AUX_I2C_WRITE_SUCCESS) - return msg->size; - else - return FIELD_GET(HIBMC_DP_CFG_AUX, aux_status); - case DP_AUX_NATIVE_READ: - case DP_AUX_I2C_READ | DP_AUX_I2C_MOT: - buf_data_cnt--; - if (buf_data_cnt != msg->size) { - /* only the successful part of data is read */ - return -EBUSY; - } - - /* all data is successfully read */ - hibmc_dp_aux_read_data(dp, msg->buffer, msg->size); - return msg->size; - default: - return -EINVAL; - } -} - -/* ret >= 0 ,ret is size; ret < 0, ret is err code */ -static ssize_t hibmc_dp_aux_xfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) -{ - struct hibmc_dp *dp_priv = container_of(aux, struct hibmc_dp, aux); - struct hibmc_dp_dev *dp = dp_priv->dp_dev; - u32 aux_cmd; - int ret; - u32 val; /* val will be assigned at the beginning of readl_poll_timeout function */ - - writel(0, dp->base + HIBMC_DP_AUX_WR_DATA0); - writel(0, dp->base + HIBMC_DP_AUX_WR_DATA1); - writel(0, dp->base + HIBMC_DP_AUX_WR_DATA2); - writel(0, dp->base + HIBMC_DP_AUX_WR_DATA3); - - hibmc_dp_aux_write_data(dp, msg->buffer, msg->size); - - aux_cmd = hibmc_dp_aux_build_cmd(msg); - writel(aux_cmd, dp->base + HIBMC_DP_AUX_CMD_ADDR); - - /* enable aux transfer */ - hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_REQ, 0x1); - ret = readl_poll_timeout(dp->base + HIBMC_DP_AUX_REQ, val, - !(val & HIBMC_DP_CFG_AUX_REQ), 50, 5000); - if (ret) { - hibmc_dp_aux_reset(dp); - return ret; - } - - return hibmc_dp_aux_parse_xfer(dp, msg); -} - -void hibmc_dp_aux_init(struct hibmc_dp *dp) -{ - hibmc_dp_reg_write_field(dp->dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_SYNC_LEN_SEL, 0x0); - hibmc_dp_reg_write_field(dp->dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_TIMER_TIMEOUT, 0x1); - hibmc_dp_reg_write_field(dp->dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_MIN_PULSE_NUM, - HIBMC_DP_MIN_PULSE_NUM); - - dp->aux.transfer = hibmc_dp_aux_xfer; - dp->aux.name = "HIBMC DRM dp aux"; - drm_dp_aux_init(&dp->aux); - dp->dp_dev->aux = &dp->aux; -} diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h deleted file mode 100644 index 1107e10b2047..000000000000 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* Copyright (c) 2024 Hisilicon Limited. */ - -#ifndef DP_COMM_H -#define DP_COMM_H - -#include <linux/types.h> -#include <linux/bitops.h> -#include <linux/errno.h> -#include <linux/mutex.h> -#include <linux/kernel.h> -#include <linux/bitfield.h> -#include <linux/io.h> -#include <drm/drm_dp_helper.h> - -#include "dp_hw.h" - -#define HIBMC_DP_LINK_RATE_CAL 27 -#define HIBMC_DP_LANE_NUM_MAX 2 - -struct hibmc_link_status { - bool clock_recovered; - bool channel_equalized; -}; - -struct hibmc_link_cap { - u8 link_rate; - u8 lanes; - int rx_dpcd_revision; - bool is_tps3; - bool is_tps4; -}; - -struct hibmc_dp_link { - struct hibmc_link_status status; - u8 train_set[HIBMC_DP_LANE_NUM_MAX]; - struct hibmc_link_cap cap; -}; - -struct hibmc_dp_dev { - struct drm_dp_aux *aux; - struct drm_device *dev; - void __iomem *base; - struct mutex lock; /* protects concurrent RW in hibmc_dp_reg_write_field() */ - struct hibmc_dp_link link; - u8 dpcd[DP_RECEIVER_CAP_SIZE]; - void __iomem *serdes_base; -}; - -#define dp_field_modify(reg_value, mask, val) \ - do { \ - (reg_value) &= ~(mask); \ - (reg_value) |= FIELD_PREP(mask, val); \ - } while (0) \ - -#define hibmc_dp_reg_write_field(dp, offset, mask, val) \ - do { \ - u32 reg_value; \ - typeof(dp) _dp = dp; \ - typeof(_dp->base) addr = (_dp->base + (offset)); \ - mutex_lock(&_dp->lock); \ - reg_value = readl(addr); \ - dp_field_modify(reg_value, mask, val); \ - writel(reg_value, addr); \ - mutex_unlock(&_dp->lock); \ - } while (0) - -void hibmc_dp_aux_init(struct hibmc_dp *dp); -int hibmc_dp_link_training(struct hibmc_dp_dev *dp); -int hibmc_dp_serdes_rate_switch(u8 rate, struct hibmc_dp_dev *dp); -int hibmc_dp_serdes_set_tx_cfg(struct hibmc_dp_dev *dp, u8 train_set[HIBMC_DP_LANE_NUM_MAX]); - -#endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h deleted file mode 100644 index 08f9e1caf7fc..000000000000 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* Copyright (c) 2024 Hisilicon Limited. */ - -#ifndef DP_CONFIG_H -#define DP_CONFIG_H - -#define HIBMC_DP_BPP 24 -#define HIBMC_DP_SYMBOL_PER_FCLK 4 -#define HIBMC_DP_MSA1 0x20 -#define HIBMC_DP_MSA2 0x845c00 -#define HIBMC_DP_OFFSET 0x1e0000 -#define HIBMC_DP_HDCP 0x2 -#define HIBMC_DP_INT_RST 0xffff -#define HIBMC_DP_DPTX_RST 0x3ff -#define HIBMC_DP_CLK_EN 0x7 -#define HIBMC_DP_SYNC_EN_MASK 0x3 -#define HIBMC_DP_LINK_RATE_CAL 27 -#define HIBMC_DP_SYNC_DELAY(lanes) ((lanes) == 0x2 ? 86 : 46) -#define HIBMC_DP_INT_ENABLE 0xc - -#endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c deleted file mode 100644 index 9e8f95d957f2..000000000000 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c +++ /dev/null @@ -1,338 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// Copyright (c) 2024 Hisilicon Limited. - -#include <linux/io.h> -#include <linux/delay.h> -#include "dp_config.h" -#include "dp_comm.h" -#include "dp_reg.h" -#include "dp_hw.h" - -static void hibmc_dp_set_tu(struct hibmc_dp_dev *dp, struct drm_display_mode *mode) -{ - u32 tu_symbol_frac_size; - u32 tu_symbol_size; - u32 rate_ks; - u8 lane_num; - u32 value; - u32 bpp; - - lane_num = dp->link.cap.lanes; - if (lane_num == 0) { - drm_err(dp->dev, "set tu failed, lane num cannot be 0!\n"); - return; - } - - bpp = HIBMC_DP_BPP; - rate_ks = dp->link.cap.link_rate * HIBMC_DP_LINK_RATE_CAL; - value = (mode->clock * bpp * 5) / (61 * lane_num * rate_ks); - - if (value % 10 == 9) { /* 9 carry */ - tu_symbol_size = value / 10 + 1; - tu_symbol_frac_size = 0; - } else { - tu_symbol_size = value / 10; - tu_symbol_frac_size = value % 10 + 1; - } - - drm_dbg_dp(dp->dev, "tu value: %u.%u value: %u\n", - tu_symbol_size, tu_symbol_frac_size, value); - - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_PACKET, - HIBMC_DP_CFG_STREAM_TU_SYMBOL_SIZE, tu_symbol_size); - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_PACKET, - HIBMC_DP_CFG_STREAM_TU_SYMBOL_FRAC_SIZE, tu_symbol_frac_size); -} - -static void hibmc_dp_set_sst(struct hibmc_dp_dev *dp, struct drm_display_mode *mode) -{ - u32 hblank_size; - u32 htotal_size; - u32 htotal_int; - u32 hblank_int; - u32 fclk; /* flink_clock */ - - fclk = dp->link.cap.link_rate * HIBMC_DP_LINK_RATE_CAL; - - /* Considering the effect of spread spectrum, the value may be deviated. - * The coefficient (0.9947) is used to offset the deviation. - */ - htotal_int = mode->htotal * 9947 / 10000; - htotal_size = htotal_int * fclk / (HIBMC_DP_SYMBOL_PER_FCLK * (mode->clock / 1000)); - - hblank_int = mode->htotal - mode->hdisplay - mode->hdisplay * 53 / 10000; - hblank_size = hblank_int * fclk * 9947 / - (mode->clock * 10 * HIBMC_DP_SYMBOL_PER_FCLK); - - drm_dbg_dp(dp->dev, "h_active %u v_active %u htotal_size %u hblank_size %u", - mode->hdisplay, mode->vdisplay, htotal_size, hblank_size); - drm_dbg_dp(dp->dev, "flink_clock %u pixel_clock %d", fclk, mode->clock / 1000); - - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_HORIZONTAL_SIZE, - HIBMC_DP_CFG_STREAM_HTOTAL_SIZE, htotal_size); - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_HORIZONTAL_SIZE, - HIBMC_DP_CFG_STREAM_HBLANK_SIZE, hblank_size); - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_PACKET, - HIBMC_DP_CFG_STREAM_SYNC_CALIBRATION, - HIBMC_DP_SYNC_DELAY(dp->link.cap.lanes)); -} - -static void hibmc_dp_link_cfg(struct hibmc_dp_dev *dp, struct drm_display_mode *mode) -{ - u32 timing_delay; - u32 vblank; - u32 hstart; - u32 vstart; - - vblank = mode->vtotal - mode->vdisplay; - timing_delay = mode->htotal - mode->hsync_start; - hstart = mode->htotal - mode->hsync_start; - vstart = mode->vtotal - mode->vsync_start; - - hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG0, - HIBMC_DP_CFG_TIMING_GEN0_HBLANK, mode->htotal - mode->hdisplay); - hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG0, - HIBMC_DP_CFG_TIMING_GEN0_HACTIVE, mode->hdisplay); - - hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG2, - HIBMC_DP_CFG_TIMING_GEN0_VBLANK, vblank); - hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG2, - HIBMC_DP_CFG_TIMING_GEN0_VACTIVE, mode->vdisplay); - hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG3, - HIBMC_DP_CFG_TIMING_GEN0_VFRONT_PORCH, - mode->vsync_start - mode->vdisplay); - - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG0, - HIBMC_DP_CFG_STREAM_HACTIVE, mode->hdisplay); - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG0, - HIBMC_DP_CFG_STREAM_HBLANK, mode->htotal - mode->hdisplay); - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG2, - HIBMC_DP_CFG_STREAM_HSYNC_WIDTH, - mode->hsync_end - mode->hsync_start); - - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG1, - HIBMC_DP_CFG_STREAM_VACTIVE, mode->vdisplay); - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG1, - HIBMC_DP_CFG_STREAM_VBLANK, vblank); - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG3, - HIBMC_DP_CFG_STREAM_VFRONT_PORCH, - mode->vsync_start - mode->vdisplay); - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG3, - HIBMC_DP_CFG_STREAM_VSYNC_WIDTH, - mode->vsync_end - mode->vsync_start); - - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_MSA0, - HIBMC_DP_CFG_STREAM_VSTART, vstart); - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_MSA0, - HIBMC_DP_CFG_STREAM_HSTART, hstart); - - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_VSYNC_POLARITY, - mode->flags & DRM_MODE_FLAG_PVSYNC ? 1 : 0); - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_HSYNC_POLARITY, - mode->flags & DRM_MODE_FLAG_PHSYNC ? 1 : 0); - - /* MSA mic 0 and 1 */ - writel(HIBMC_DP_MSA1, dp->base + HIBMC_DP_VIDEO_MSA1); - writel(HIBMC_DP_MSA2, dp->base + HIBMC_DP_VIDEO_MSA2); - - hibmc_dp_set_tu(dp, mode); - - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_RGB_ENABLE, 0x1); - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_VIDEO_MAPPING, 0); - - /* divide 2: up even */ - if (timing_delay % 2) - timing_delay++; - - hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_MODEL_CTRL, - HIBMC_DP_CFG_PIXEL_NUM_TIMING_MODE_SEL1, timing_delay); - - hibmc_dp_set_sst(dp, mode); -} - -int hibmc_dp_hw_init(struct hibmc_dp *dp) -{ - struct drm_device *drm_dev = dp->drm_dev; - struct hibmc_dp_dev *dp_dev; - - dp_dev = devm_kzalloc(drm_dev->dev, sizeof(struct hibmc_dp_dev), GFP_KERNEL); - if (!dp_dev) - return -ENOMEM; - - mutex_init(&dp_dev->lock); - - dp->dp_dev = dp_dev; - - dp_dev->dev = drm_dev; - dp_dev->base = dp->mmio + HIBMC_DP_OFFSET; - dp_dev->serdes_base = dp_dev->base + HIBMC_DP_HOST_OFFSET; - - hibmc_dp_aux_init(dp); - - dp_dev->link.cap.lanes = 0x2; - dp_dev->link.cap.link_rate = DP_LINK_BW_8_1; - - /* int init */ - writel(0, dp_dev->base + HIBMC_DP_INTR_ENABLE); - writel(HIBMC_DP_INT_RST, dp_dev->base + HIBMC_DP_INTR_ORIGINAL_STATUS); - /* rst */ - writel(0, dp_dev->base + HIBMC_DP_DPTX_RST_CTRL); - usleep_range(30, 50); - writel(HIBMC_DP_DPTX_RST, dp_dev->base + HIBMC_DP_DPTX_RST_CTRL); - /* hdcp data */ - writel(HIBMC_DP_HDCP, dp_dev->base + HIBMC_DP_HDCP_CFG); - /* clock enable */ - writel(HIBMC_DP_CLK_EN, dp_dev->base + HIBMC_DP_DPTX_CLK_CTRL); - - return 0; -} - -void hibmc_dp_enable_int(struct hibmc_dp *dp) -{ - struct hibmc_dp_dev *dp_dev = dp->dp_dev; - - writel(HIBMC_DP_INT_ENABLE, dp_dev->base + HIBMC_DP_INTR_ENABLE); -} - -void hibmc_dp_disable_int(struct hibmc_dp *dp) -{ - struct hibmc_dp_dev *dp_dev = dp->dp_dev; - - writel(0, dp_dev->base + HIBMC_DP_INTR_ENABLE); - writel(HIBMC_DP_INT_RST, dp_dev->base + HIBMC_DP_INTR_ORIGINAL_STATUS); -} - -void hibmc_dp_hpd_cfg(struct hibmc_dp *dp) -{ - struct hibmc_dp_dev *dp_dev = dp->dp_dev; - - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_SYNC_LEN_SEL, 0x0); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_TIMER_TIMEOUT, 0x1); - hibmc_dp_reg_write_field(dp->dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_MIN_PULSE_NUM, 0x9); - writel(HIBMC_DP_HDCP, dp_dev->base + HIBMC_DP_HDCP_CFG); - writel(0, dp_dev->base + HIBMC_DP_INTR_ENABLE); - writel(HIBMC_DP_INT_RST, dp_dev->base + HIBMC_DP_INTR_ORIGINAL_STATUS); - writel(HIBMC_DP_INT_ENABLE, dp_dev->base + HIBMC_DP_INTR_ENABLE); - writel(HIBMC_DP_DPTX_RST, dp_dev->base + HIBMC_DP_DPTX_RST_CTRL); - writel(HIBMC_DP_CLK_EN, dp_dev->base + HIBMC_DP_DPTX_CLK_CTRL); -} - -void hibmc_dp_display_en(struct hibmc_dp *dp, bool enable) -{ - struct hibmc_dp_dev *dp_dev = dp->dp_dev; - - if (enable) { - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_VIDEO_CTRL, - HIBMC_DP_CFG_MST_ENABLE, 0x1); - writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_DPTX_GCTL0, - HIBMC_DP_CFG_TIMING_GEN_ENABLE, 0x1); - writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); - } else { - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_DPTX_GCTL0, - HIBMC_DP_CFG_TIMING_GEN_ENABLE, 0); - writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_MST_ENABLE, 0); - writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); - } - - msleep(50); -} - -int hibmc_dp_mode_set(struct hibmc_dp *dp, struct drm_display_mode *mode) -{ - struct hibmc_dp_dev *dp_dev = dp->dp_dev; - int ret; - - if (!dp_dev->link.status.channel_equalized) { - ret = hibmc_dp_link_training(dp_dev); - if (ret) { - drm_err(dp->drm_dev, "dp link training failed, ret: %d\n", ret); - return ret; - } - } - - hibmc_dp_display_en(dp, false); - hibmc_dp_link_cfg(dp_dev, mode); - - return 0; -} - -u8 hibmc_dp_get_link_rate(struct hibmc_dp *dp) -{ - if (!dp->dp_dev) - return 0; - - return dp->dp_dev->link.cap.link_rate; -} - -u8 hibmc_dp_get_lanes(struct hibmc_dp *dp) -{ - if (!dp->dp_dev) - return 0; - - return dp->dp_dev->link.cap.lanes; -} - -int hibmc_dp_get_dpcd(struct hibmc_dp *dp) -{ - if (!dp->dp_dev) - return 0; - - return dp->dp_dev->link.cap.rx_dpcd_revision; -} - -void hibmc_dp_reset_link(struct hibmc_dp *dp) -{ - if (dp->dp_dev) { - dp->dp_dev->link.status.clock_recovered = false; - dp->dp_dev->link.status.channel_equalized = false; - } -} - -static const struct hibmc_dp_color_raw g_rgb_raw[] = { - {CBAR_COLOR_BAR, 0x000, 0x000, 0x000}, - {CBAR_WHITE, 0xfff, 0xfff, 0xfff}, - {CBAR_RED, 0xfff, 0x000, 0x000}, - {CBAR_ORANGE, 0xfff, 0x800, 0x000}, - {CBAR_YELLOW, 0xfff, 0xfff, 0x000}, - {CBAR_GREEN, 0x000, 0xfff, 0x000}, - {CBAR_CYAN, 0x000, 0x800, 0x800}, - {CBAR_BLUE, 0x000, 0x000, 0xfff}, - {CBAR_PURPLE, 0x800, 0x000, 0x800}, - {CBAR_BLACK, 0x000, 0x000, 0x000}, -}; - -void hibmc_dp_set_cbar(struct hibmc_dp *dp, const struct hibmc_dp_cbar_cfg *cfg) -{ - struct hibmc_dp_dev *dp_dev = dp->dp_dev; - struct hibmc_dp_color_raw raw_data; - - if (cfg->enable) { - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, - HIBMC_DP_COLOR_BAR_TIMING_SEL_M, cfg->self_timing); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, - HIBMC_DP_COLOR_BAR_CTRL_M, cfg->dynamic_rate); - if (cfg->pattern == CBAR_COLOR_BAR) { - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, - HIBMC_DP_COLOR_BAR_PATTERN_SEL_M, 0); - } else { - raw_data = g_rgb_raw[cfg->pattern]; - drm_dbg_dp(dp->drm_dev, "r:%x g:%x b:%x\n", raw_data.r_value, - raw_data.g_value, raw_data.b_value); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, - HIBMC_DP_COLOR_BAR_PATTERN_SEL_M, 1); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, - HIBMC_DP_COLOR_BAR_DATA_R_M, raw_data.r_value); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL1, - HIBMC_DP_COLOR_BAR_DATA_G_M, raw_data.g_value); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL1, - HIBMC_DP_COLOR_BAR_DATA_B_M, raw_data.b_value); - } - } - - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, - HIBMC_DP_COLOR_BAR_ENABLE_M, cfg->enable); - writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); -} - diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h deleted file mode 100644 index 4740db06e1b0..000000000000 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h +++ /dev/null @@ -1,74 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* Copyright (c) 2024 Hisilicon Limited. */ - -#ifndef DP_KAPI_H -#define DP_KAPI_H - -#include <linux/types.h> -#include <linux/delay.h> -#include <drm/drm_device.h> -#include <drm/drm_encoder.h> -#include <drm/drm_connector.h> -#include <drm/drm_print.h> -#include <drm/drm_dp_helper.h> - -// 27 * 10000000 * 80% = 216000000 -#define DP_MODE_VALI_CAL 216000000 -#define BPP_24 24 - -struct hibmc_dp_dev; - -enum hibmc_dp_cbar_pattern { - CBAR_COLOR_BAR, - CBAR_WHITE, - CBAR_RED, - CBAR_ORANGE, - CBAR_YELLOW, - CBAR_GREEN, - CBAR_CYAN, - CBAR_BLUE, - CBAR_PURPLE, - CBAR_BLACK, -}; - -struct hibmc_dp_color_raw { - enum hibmc_dp_cbar_pattern pattern; - u32 r_value; - u32 g_value; - u32 b_value; -}; - -struct hibmc_dp_cbar_cfg { - u8 enable; - u8 self_timing; - u8 dynamic_rate; /* 0:static, 1-255(frame):dynamic */ - enum hibmc_dp_cbar_pattern pattern; -}; - -struct hibmc_dp { - struct hibmc_dp_dev *dp_dev; - struct drm_device *drm_dev; - struct drm_encoder encoder; - struct drm_connector connector; - void __iomem *mmio; - struct drm_dp_aux aux; - struct hibmc_dp_cbar_cfg cfg; - u32 irq_status; - int hpd_status; - bool is_connected; -}; - -int hibmc_dp_hw_init(struct hibmc_dp *dp); -int hibmc_dp_mode_set(struct hibmc_dp *dp, struct drm_display_mode *mode); -void hibmc_dp_display_en(struct hibmc_dp *dp, bool enable); -struct edid *hibmc_dp_get_edid(struct hibmc_dp *dp); -int hibmc_dp_get_dpcd(struct hibmc_dp *dp); -u8 hibmc_dp_get_link_rate(struct hibmc_dp *dp); -u8 hibmc_dp_get_lanes(struct hibmc_dp *dp); -void hibmc_dp_set_cbar(struct hibmc_dp *dp, const struct hibmc_dp_cbar_cfg *cfg); -void hibmc_dp_reset_link(struct hibmc_dp *dp); -void hibmc_dp_hpd_cfg(struct hibmc_dp *dp); -void hibmc_dp_enable_int(struct hibmc_dp *dp); -void hibmc_dp_disable_int(struct hibmc_dp *dp); - -#endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c deleted file mode 100644 index 9b632428fec6..000000000000 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c +++ /dev/null @@ -1,415 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// Copyright (c) 2024 Hisilicon Limited. - -#include <linux/delay.h> -#include <drm/drm_device.h> -#include <drm/drm_print.h> -#include "dp_comm.h" -#include "dp_reg.h" - -#define HIBMC_EQ_MAX_RETRY 5 - -static inline int hibmc_dp_get_serdes_rate_cfg(struct hibmc_dp_dev *dp) -{ - switch (dp->link.cap.link_rate) { - case DP_LINK_BW_1_62: - return DP_SERDES_BW_1_62; - case DP_LINK_BW_2_7: - return DP_SERDES_BW_2_7; - case DP_LINK_BW_5_4: - return DP_SERDES_BW_5_4; - case DP_LINK_BW_8_1: - return DP_SERDES_BW_8_1; - default: - return -EINVAL; - } -} - -static int hibmc_dp_link_training_configure(struct hibmc_dp_dev *dp) -{ - u8 buf[2]; - int ret; - - /* DP 2 lane */ - hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_LANE_DATA_EN, - dp->link.cap.lanes == 0x2 ? 0x3 : 0x1); - hibmc_dp_reg_write_field(dp, HIBMC_DP_DPTX_GCTL0, HIBMC_DP_CFG_PHY_LANE_NUM, - dp->link.cap.lanes == 0x2 ? 0x1 : 0); - - /* enhanced frame */ - hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_FRAME_MODE, 0x1); - - ret = hibmc_dp_get_serdes_rate_cfg(dp); - if (ret < 0) - return ret; - - ret = hibmc_dp_serdes_rate_switch(ret, dp); - if (ret) - return ret; - - /* set rate and lane count */ - buf[0] = dp->link.cap.link_rate; - buf[1] = DP_LANE_COUNT_ENHANCED_FRAME_EN | dp->link.cap.lanes; - ret = drm_dp_dpcd_write(dp->aux, DP_LINK_BW_SET, buf, sizeof(buf)); - if (ret != sizeof(buf)) { - drm_dbg_dp(dp->dev, "dp aux write link rate and lanes failed, ret: %d\n", ret); - return ret >= 0 ? -EIO : ret; - } - - /* set 8b/10b and downspread */ - buf[0] = DP_SPREAD_AMP_0_5; - buf[1] = DP_SET_ANSI_8B10B; - ret = drm_dp_dpcd_write(dp->aux, DP_DOWNSPREAD_CTRL, buf, sizeof(buf)); - if (ret != sizeof(buf)) { - drm_dbg_dp(dp->dev, "dp aux write 8b/10b and downspread failed, ret: %d\n", ret); - return ret >= 0 ? -EIO : ret; - } - - return 0; -} - -static int hibmc_dp_link_set_pattern(struct hibmc_dp_dev *dp, int pattern) -{ - int ret; - u8 val; - u8 buf; - - buf = (u8)pattern; - if (pattern != DP_TRAINING_PATTERN_DISABLE && pattern != DP_TRAINING_PATTERN_4) { - buf |= DP_LINK_SCRAMBLING_DISABLE; - hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_SCRAMBLE_EN, 0x1); - } else { - hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_SCRAMBLE_EN, 0); - } - - switch (pattern) { - case DP_TRAINING_PATTERN_DISABLE: - val = 0; - break; - case DP_TRAINING_PATTERN_1: - val = 1; - break; - case DP_TRAINING_PATTERN_2: - val = 2; - break; - case DP_TRAINING_PATTERN_3: - val = 3; - break; - case DP_TRAINING_PATTERN_4: - val = 4; - break; - default: - return -EINVAL; - } - - hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_PAT_SEL, val); - - ret = drm_dp_dpcd_write(dp->aux, DP_TRAINING_PATTERN_SET, &buf, sizeof(buf)); - if (ret != sizeof(buf)) { - drm_dbg_dp(dp->dev, "dp aux write training pattern set failed\n"); - return ret >= 0 ? -EIO : ret; - } - - return 0; -} - -static int hibmc_dp_link_training_cr_pre(struct hibmc_dp_dev *dp) -{ - u8 *train_set = dp->link.train_set; - int ret; - u8 i; - - ret = hibmc_dp_link_training_configure(dp); - if (ret) - return ret; - - ret = hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_1); - if (ret) - return ret; - - for (i = 0; i < dp->link.cap.lanes; i++) - train_set[i] = DP_TRAIN_VOLTAGE_SWING_LEVEL_0; - - ret = hibmc_dp_serdes_set_tx_cfg(dp, dp->link.train_set); - if (ret) - return ret; - - ret = drm_dp_dpcd_write(dp->aux, DP_TRAINING_LANE0_SET, train_set, dp->link.cap.lanes); - if (ret != dp->link.cap.lanes) { - drm_dbg_dp(dp->dev, "dp aux write training lane set failed\n"); - return ret >= 0 ? -EIO : ret; - } - - return 0; -} - -static bool hibmc_dp_link_get_adjust_train(struct hibmc_dp_dev *dp, - u8 lane_status[DP_LINK_STATUS_SIZE]) -{ - u8 train_set[HIBMC_DP_LANE_NUM_MAX] = {0}; - u8 lane; - - for (lane = 0; lane < dp->link.cap.lanes; lane++) - train_set[lane] = drm_dp_get_adjust_request_voltage(lane_status, lane) | - drm_dp_get_adjust_request_pre_emphasis(lane_status, lane); - - if (memcmp(dp->link.train_set, train_set, HIBMC_DP_LANE_NUM_MAX)) { - memcpy(dp->link.train_set, train_set, HIBMC_DP_LANE_NUM_MAX); - return true; - } - - return false; -} - -static int hibmc_dp_link_reduce_rate(struct hibmc_dp_dev *dp) -{ - int ret; - - switch (dp->link.cap.link_rate) { - case DP_LINK_BW_2_7: - dp->link.cap.link_rate = DP_LINK_BW_1_62; - break; - case DP_LINK_BW_5_4: - dp->link.cap.link_rate = DP_LINK_BW_2_7; - break; - case DP_LINK_BW_8_1: - dp->link.cap.link_rate = DP_LINK_BW_5_4; - break; - default: - return -EINVAL; - } - - ret = hibmc_dp_get_serdes_rate_cfg(dp); - if (ret < 0) - return ret; - - return hibmc_dp_serdes_rate_switch(ret, dp); -} - -static inline int hibmc_dp_link_reduce_lane(struct hibmc_dp_dev *dp) -{ - switch (dp->link.cap.lanes) { - case 0x2: - dp->link.cap.lanes--; - drm_dbg_dp(dp->dev, "dp link training reduce to 1 lane\n"); - break; - case 0x1: - drm_err(dp->dev, "dp link training reduce lane failed, already reach minimum\n"); - return -EIO; - default: - return -EINVAL; - } - - return 0; -} - -static int hibmc_dp_link_training_cr(struct hibmc_dp_dev *dp) -{ - u8 lane_status[DP_LINK_STATUS_SIZE] = {0}; - bool level_changed; - u32 voltage_tries; - u32 cr_tries; - u32 max_cr; - int ret; - - /* - * DP 1.4 spec define 10 for maxtries value, for pre DP 1.4 version set a limit of 80 - * (4 voltage levels x 4 preemphasis levels x 5 identical voltage retries) - */ - max_cr = dp->link.cap.rx_dpcd_revision >= DP_DPCD_REV_14 ? 10 : 80; - - voltage_tries = 1; - for (cr_tries = 0; cr_tries < max_cr; cr_tries++) { - drm_dp_link_train_clock_recovery_delay(dp->dpcd); - - ret = drm_dp_dpcd_read_link_status(dp->aux, lane_status); - if (ret != DP_LINK_STATUS_SIZE) { - drm_err(dp->dev, "Get lane status failed, ret: %d\n", ret); - return ret >= 0 ? -EIO : ret; - } - - if (drm_dp_clock_recovery_ok(lane_status, dp->link.cap.lanes)) { - drm_dbg_dp(dp->dev, "dp link training cr done\n"); - dp->link.status.clock_recovered = true; - return 0; - } - - if (voltage_tries == 5) { - drm_dbg_dp(dp->dev, "same voltage tries 5 times\n"); - dp->link.status.clock_recovered = false; - return 0; - } - - level_changed = hibmc_dp_link_get_adjust_train(dp, lane_status); - - ret = hibmc_dp_serdes_set_tx_cfg(dp, dp->link.train_set); - if (ret) - return ret; - - ret = drm_dp_dpcd_write(dp->aux, DP_TRAINING_LANE0_SET, dp->link.train_set, - dp->link.cap.lanes); - if (ret != dp->link.cap.lanes) { - drm_dbg_dp(dp->dev, "Update link training failed\n"); - return ret >= 0 ? -EIO : ret; - } - - voltage_tries = level_changed ? 1 : voltage_tries + 1; - } - - drm_err(dp->dev, "dp link training clock recovery %u timers failed\n", max_cr); - dp->link.status.clock_recovered = false; - - return 0; -} - -static int hibmc_dp_link_training_channel_eq(struct hibmc_dp_dev *dp) -{ - u8 lane_status[DP_LINK_STATUS_SIZE] = {0}; - u8 eq_tries; - int tps; - int ret; - - if (dp->link.cap.is_tps4) - tps = DP_TRAINING_PATTERN_4; - else if (dp->link.cap.is_tps3) - tps = DP_TRAINING_PATTERN_3; - else - tps = DP_TRAINING_PATTERN_2; - - ret = hibmc_dp_link_set_pattern(dp, tps); - if (ret) - return ret; - - for (eq_tries = 0; eq_tries < HIBMC_EQ_MAX_RETRY; eq_tries++) { - drm_dp_link_train_channel_eq_delay(dp->dpcd); - - ret = drm_dp_dpcd_read_link_status(dp->aux, lane_status); - if (ret != DP_LINK_STATUS_SIZE) { - drm_err(dp->dev, "get lane status failed\n"); - break; - } - - if (!drm_dp_clock_recovery_ok(lane_status, dp->link.cap.lanes)) { - drm_dbg_dp(dp->dev, "clock recovery check failed\n"); - drm_dbg_dp(dp->dev, "cannot continue channel equalization\n"); - dp->link.status.clock_recovered = false; - break; - } - - if (drm_dp_channel_eq_ok(lane_status, dp->link.cap.lanes)) { - dp->link.status.channel_equalized = true; - drm_dbg_dp(dp->dev, "dp link training eq done\n"); - break; - } - - hibmc_dp_link_get_adjust_train(dp, lane_status); - - ret = hibmc_dp_serdes_set_tx_cfg(dp, dp->link.train_set); - if (ret) - break; - - ret = drm_dp_dpcd_write(dp->aux, DP_TRAINING_LANE0_SET, - dp->link.train_set, dp->link.cap.lanes); - if (ret != dp->link.cap.lanes) { - drm_dbg_dp(dp->dev, "Update link training failed\n"); - ret = (ret >= 0) ? -EIO : ret; - break; - } - } - - if (eq_tries == HIBMC_EQ_MAX_RETRY) - drm_err(dp->dev, "channel equalization failed %u times\n", eq_tries); - - hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE); - - return ret < 0 ? ret : 0; -} - -static int hibmc_dp_link_downgrade_training_cr(struct hibmc_dp_dev *dp) -{ - if (hibmc_dp_link_reduce_rate(dp)) - return hibmc_dp_link_reduce_lane(dp); - - return 0; -} - -static int hibmc_dp_link_downgrade_training_eq(struct hibmc_dp_dev *dp) -{ - u8 status[DP_LINK_STATUS_SIZE] = {0}; - int ret; - - ret = drm_dp_dpcd_read_link_status(dp->aux, status); - if (ret != DP_LINK_STATUS_SIZE) { - drm_err(dp->dev, "get lane status failed\n"); - return ret >= 0 ? -EIO : ret; - } - - if ((dp->link.status.clock_recovered && !dp->link.status.channel_equalized) || - (status[0] != 0 && !dp->link.status.clock_recovered)) { // at least one cr_done - if (!hibmc_dp_link_reduce_lane(dp)) - return 0; - } - - return hibmc_dp_link_reduce_rate(dp); -} - -static void hibmc_dp_update_caps(struct hibmc_dp_dev *dp) -{ - dp->link.cap.rx_dpcd_revision = dp->dpcd[DP_DPCD_REV]; - - dp->link.cap.is_tps3 = (dp->dpcd[DP_DPCD_REV] >= DP_DPCD_REV_13) && - (dp->dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED); - dp->link.cap.is_tps4 = (dp->dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14) && - (dp->dpcd[DP_MAX_DOWNSPREAD] & DP_TPS4_SUPPORTED); - dp->link.cap.link_rate = dp->dpcd[DP_MAX_LINK_RATE]; - dp->link.cap.lanes = dp->dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; - if (dp->link.cap.lanes > HIBMC_DP_LANE_NUM_MAX) - dp->link.cap.lanes = HIBMC_DP_LANE_NUM_MAX; -} - -int hibmc_dp_link_training(struct hibmc_dp_dev *dp) -{ - struct hibmc_dp_link *link = &dp->link; - int ret; - - ret = drm_dp_read_dpcd_caps(dp->aux, dp->dpcd); - if (ret) - drm_err(dp->dev, "dp aux read dpcd failed, ret: %d\n", ret); - - hibmc_dp_update_caps(dp); - - while (true) { - ret = hibmc_dp_link_training_cr_pre(dp); - if (ret) - goto err; - - ret = hibmc_dp_link_training_cr(dp); - if (ret) - goto err; - - if (!link->status.clock_recovered) { - ret = hibmc_dp_link_downgrade_training_cr(dp); - if (ret) - goto err; - continue; - } - - ret = hibmc_dp_link_training_channel_eq(dp); - if (ret) - goto err; - - if (!link->status.channel_equalized) { - ret = hibmc_dp_link_downgrade_training_eq(dp); - if (ret) - goto err; - continue; - } - - return 0; - } - -err: - hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE); - - return ret; -} diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h deleted file mode 100644 index 156e5d63c47f..000000000000 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h +++ /dev/null @@ -1,140 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* Copyright (c) 2024 Hisilicon Limited. */ - -#ifndef DP_REG_H -#define DP_REG_H - -#define HIBMC_DP_AUX_CMD_ADDR 0x50 - -#define HIBMC_DP_AUX_WR_DATA0 0x54 -#define HIBMC_DP_AUX_WR_DATA1 0x58 -#define HIBMC_DP_AUX_WR_DATA2 0x5c -#define HIBMC_DP_AUX_WR_DATA3 0x60 -#define HIBMC_DP_AUX_RD_DATA0 0x64 - -#define HIBMC_DP_AUX_REQ 0x74 -#define HIBMC_DP_CFG_AUX_REQ BIT(0) -#define HIBMC_DP_CFG_AUX_SYNC_LEN_SEL BIT(1) -#define HIBMC_DP_CFG_AUX_TIMER_TIMEOUT BIT(2) -#define HIBMC_DP_CFG_AUX_MIN_PULSE_NUM GENMASK(13, 9) - -#define HIBMC_DP_AUX_STATUS 0x78 -#define HIBMC_DP_CFG_AUX_TIMEOUT BIT(0) -#define HIBMC_DP_CFG_AUX_STATUS GENMASK(11, 4) -#define HIBMC_DP_CFG_AUX_READY_DATA_BYTE GENMASK(16, 12) -#define HIBMC_DP_CFG_AUX GENMASK(24, 17) - -#define HIBMC_DP_PHYIF_CTRL0 0xa0 -#define HIBMC_DP_CFG_SCRAMBLE_EN BIT(0) -#define HIBMC_DP_CFG_PAT_SEL GENMASK(7, 4) -#define HIBMC_DP_CFG_LANE_DATA_EN GENMASK(11, 8) - -#define HIBMC_DP_VIDEO_CTRL 0x100 -#define HIBMC_DP_CFG_STREAM_RGB_ENABLE BIT(1) -#define HIBMC_DP_CFG_STREAM_VIDEO_MAPPING GENMASK(5, 2) -#define HIBMC_DP_CFG_STREAM_FRAME_MODE BIT(6) -#define HIBMC_DP_CFG_STREAM_HSYNC_POLARITY BIT(7) -#define HIBMC_DP_CFG_STREAM_VSYNC_POLARITY BIT(8) - -#define HIBMC_DP_VIDEO_CONFIG0 0x104 -#define HIBMC_DP_CFG_STREAM_HACTIVE GENMASK(31, 16) -#define HIBMC_DP_CFG_STREAM_HBLANK GENMASK(15, 0) - -#define HIBMC_DP_VIDEO_CONFIG1 0x108 -#define HIBMC_DP_CFG_STREAM_VACTIVE GENMASK(31, 16) -#define HIBMC_DP_CFG_STREAM_VBLANK GENMASK(15, 0) - -#define HIBMC_DP_VIDEO_CONFIG2 0x10c -#define HIBMC_DP_CFG_STREAM_HSYNC_WIDTH GENMASK(15, 0) - -#define HIBMC_DP_VIDEO_CONFIG3 0x110 -#define HIBMC_DP_CFG_STREAM_VSYNC_WIDTH GENMASK(15, 0) -#define HIBMC_DP_CFG_STREAM_VFRONT_PORCH GENMASK(31, 16) - -#define HIBMC_DP_VIDEO_PACKET 0x114 -#define HIBMC_DP_CFG_STREAM_TU_SYMBOL_SIZE GENMASK(5, 0) -#define HIBMC_DP_CFG_STREAM_TU_SYMBOL_FRAC_SIZE GENMASK(9, 6) -#define HIBMC_DP_CFG_STREAM_SYNC_CALIBRATION GENMASK(31, 20) - -#define HIBMC_DP_VIDEO_MSA0 0x118 -#define HIBMC_DP_CFG_STREAM_VSTART GENMASK(31, 16) -#define HIBMC_DP_CFG_STREAM_HSTART GENMASK(15, 0) - -#define HIBMC_DP_VIDEO_MSA1 0x11c -#define HIBMC_DP_VIDEO_MSA2 0x120 - -#define HIBMC_DP_VIDEO_HORIZONTAL_SIZE 0X124 -#define HIBMC_DP_CFG_STREAM_HTOTAL_SIZE GENMASK(31, 16) -#define HIBMC_DP_CFG_STREAM_HBLANK_SIZE GENMASK(15, 0) - -#define HIBMC_DP_COLOR_BAR_CTRL 0x260 -#define HIBMC_DP_COLOR_BAR_ENABLE_M BIT(0) -#define HIBMC_DP_COLOR_BAR_CTRL_M GENMASK(8, 1) -#define HIBMC_DP_COLOR_BAR_TIMING_SEL_M BIT(9) -#define HIBMC_DP_COLOR_BAR_PATTERN_SEL_M BIT(10) -#define HIBMC_DP_COLOR_BAR_DATA_R_M GENMASK(23, 12) -#define HIBMC_DP_COLOR_BAR_CTRL1 0x264 -#define HIBMC_DP_COLOR_BAR_DATA_B_M GENMASK(11, 0) -#define HIBMC_DP_COLOR_BAR_DATA_G_M GENMASK(23, 12) -#define HIBMC_DP_TIMING_GEN_CONFIG0 0x26c -#define HIBMC_DP_CFG_TIMING_GEN0_HACTIVE GENMASK(31, 16) -#define HIBMC_DP_CFG_TIMING_GEN0_HBLANK GENMASK(15, 0) - -#define HIBMC_DP_TIMING_GEN_CONFIG2 0x274 -#define HIBMC_DP_CFG_TIMING_GEN0_VACTIVE GENMASK(31, 16) -#define HIBMC_DP_CFG_TIMING_GEN0_VBLANK GENMASK(15, 0) - -#define HIBMC_DP_TIMING_GEN_CONFIG3 0x278 -#define HIBMC_DP_CFG_TIMING_GEN0_VFRONT_PORCH GENMASK(31, 16) - -#define HIBMC_DP_HDCP_CFG 0x600 - -#define HIBMC_DP_DPTX_RST_CTRL 0x700 -#define HIBMC_DP_CFG_AUX_RST_N BIT(4) - -#define HIBMC_DP_DPTX_CLK_CTRL 0x704 - -#define HIBMC_DP_DPTX_GCTL0 0x708 -#define HIBMC_DP_CFG_PHY_LANE_NUM GENMASK(2, 1) -#define HIBMC_DP_CFG_TIMING_GEN_ENABLE BIT(10) -#define HIBMC_DP_CFG_MST_ENABLE BIT(0) - -#define HIBMC_DP_INTR_ENABLE 0x720 -#define HIBMC_DP_INTR_ORIGINAL_STATUS 0x728 - -#define HIBMC_DP_TIMING_MODEL_CTRL 0x884 -#define HIBMC_DP_CFG_PIXEL_NUM_TIMING_MODE_SEL1 GENMASK(31, 16) - -#define HIBMC_DP_TIMING_SYNC_CTRL 0xFF0 - -#define HIBMC_DP_INTSTAT 0x1e0724 -#define HIBMC_DP_INTCLR 0x1e0728 - -/* dp serdes reg */ -#define HIBMC_DP_HOST_OFFSET 0x10000 -#define HIBMC_DP_LANE0_RATE_OFFSET 0x4 -#define HIBMC_DP_LANE1_RATE_OFFSET 0xc -#define HIBMC_DP_LANE_STATUS_OFFSET 0x10 -#define HIBMC_DP_PMA_LANE0_OFFSET 0x18 -#define HIBMC_DP_PMA_LANE1_OFFSET 0x1c -#define HIBMC_DP_HOST_SERDES_CTRL 0x1f001c -#define HIBMC_DP_PMA_TXDEEMPH GENMASK(18, 1) -#define DP_SERDES_DONE 0x3 - -/* dp serdes TX-Deempth Configuration */ -#define DP_SERDES_VOL0_PRE0 0x280 -#define DP_SERDES_VOL0_PRE1 0x2300 -#define DP_SERDES_VOL0_PRE2 0x53c0 -#define DP_SERDES_VOL0_PRE3 0x8400 -#define DP_SERDES_VOL1_PRE0 0x380 -#define DP_SERDES_VOL1_PRE1 0x3440 -#define DP_SERDES_VOL1_PRE2 0x6480 -#define DP_SERDES_VOL2_PRE0 0x4c1 -#define DP_SERDES_VOL2_PRE1 0x4500 -#define DP_SERDES_VOL3_PRE0 0x600 -#define DP_SERDES_BW_8_1 0x3 -#define DP_SERDES_BW_5_4 0x2 -#define DP_SERDES_BW_2_7 0x1 -#define DP_SERDES_BW_1_62 0x0 - -#endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c deleted file mode 100644 index 8191233aa965..000000000000 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// Copyright (c) 2025 Hisilicon Limited. - -#include <linux/delay.h> -#include <drm/drm_device.h> -#include <drm/drm_print.h> -#include "dp_comm.h" -#include "dp_config.h" -#include "dp_reg.h" - -int hibmc_dp_serdes_set_tx_cfg(struct hibmc_dp_dev *dp, u8 train_set[HIBMC_DP_LANE_NUM_MAX]) -{ - static const u32 serdes_tx_cfg[4][4] = { {DP_SERDES_VOL0_PRE0, DP_SERDES_VOL0_PRE1, - DP_SERDES_VOL0_PRE2, DP_SERDES_VOL0_PRE3}, - {DP_SERDES_VOL1_PRE0, DP_SERDES_VOL1_PRE1, - DP_SERDES_VOL1_PRE2}, {DP_SERDES_VOL2_PRE0, - DP_SERDES_VOL2_PRE1}, {DP_SERDES_VOL3_PRE0}}; - int cfg[2]; - int i; - - for (i = 0; i < HIBMC_DP_LANE_NUM_MAX; i++) { - cfg[i] = serdes_tx_cfg[FIELD_GET(DP_TRAIN_VOLTAGE_SWING_MASK, train_set[i])] - [FIELD_GET(DP_TRAIN_PRE_EMPHASIS_MASK, train_set[i])]; - if (!cfg[i]) - return -EINVAL; - - /* lane1 offset is 4 */ - writel(FIELD_PREP(HIBMC_DP_PMA_TXDEEMPH, cfg[i]), - dp->serdes_base + HIBMC_DP_PMA_LANE0_OFFSET + i * 4); - } - - usleep_range(300, 500); - - if (readl(dp->serdes_base + HIBMC_DP_LANE_STATUS_OFFSET) != DP_SERDES_DONE) { - drm_dbg_dp(dp->dev, "dp serdes cfg failed\n"); - return -EAGAIN; - } - - return 0; -} - -int hibmc_dp_serdes_rate_switch(u8 rate, struct hibmc_dp_dev *dp) -{ - writel(rate, dp->serdes_base + HIBMC_DP_LANE0_RATE_OFFSET); - writel(rate, dp->serdes_base + HIBMC_DP_LANE1_RATE_OFFSET); - - usleep_range(300, 500); - - if (readl(dp->serdes_base + HIBMC_DP_LANE_STATUS_OFFSET) != DP_SERDES_DONE) { - drm_dbg_dp(dp->dev, "dp serdes rate switching failed\n"); - return -EAGAIN; - } - - if (rate < DP_SERDES_BW_8_1) - drm_dbg_dp(dp->dev, "reducing serdes rate to :%d\n", - rate ? rate * HIBMC_DP_LINK_RATE_CAL * 10 : 162); - - return 0; -} diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_debugfs.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_debugfs.c deleted file mode 100644 index 6c6ff3255d15..000000000000 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_debugfs.c +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// Copyright (c) 2024 Hisilicon Limited. - -#include <linux/debugfs.h> -#include <linux/device.h> -#include <linux/seq_file.h> -#include <linux/pci.h> - -#include <drm/drm_drv.h> -#include <drm/drm_file.h> -#include <drm/drm_debugfs.h> -#include <drm/drm_edid.h> - -#include "hibmc_drm_drv.h" - -#define MAX_BUF_SIZE 12 - -static int hibmc_dp_show(struct seq_file *m, void *arg) -{ - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); - int idx, rate; - - if (!drm_dev_enter(dev, &idx)) - return -ENODEV; - - switch (hibmc_dp_get_link_rate(&priv->dp)) { - case 0x1e: - rate = 810; // 8.1Gbps - break; - case 0x14: - rate = 540; // 5.4Gbps - break; - case 0xA: - rate = 270; // 2.7Gbps - break; - case 0x6: - rate = 162; // 1.62Gbps - break; - default: - rate = 0; - } - - seq_printf(m, "enable lanes: %u\n", hibmc_dp_get_lanes(&priv->dp)); - seq_printf(m, "link rate: %d\n", rate); - seq_printf(m, "vfresh: %d\n", drm_mode_vrefresh(&priv->crtc.mode)); - seq_printf(m, "dpcd version: 0x%x\n", hibmc_dp_get_dpcd(&priv->dp)); - seq_printf(m, "hpd status: %d\n", priv->dp.hpd_status); - - drm_dev_exit(idx); - - return 0; -} - -static ssize_t hibmc_control_write(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hibmc_drm_private *priv = file_inode(file)->i_private; - struct hibmc_dp_cbar_cfg *cfg = &priv->dp.cfg; - int ret, idx; - u8 buf[MAX_BUF_SIZE]; - - if (count >= MAX_BUF_SIZE) - return -EINVAL; - - if (copy_from_user(buf, user_buf, count)) - return -EFAULT; - - buf[count] = '\0'; - - /* Only 4 parameters is allowed, the ranger are as follow: - * [0] enable/disable colorbar feature - 0: enable colorbar, 1: disable colorbar - * [1] the timing source of colorbar displaying - 0: timing follows XDP, 1: internal self timing - * [2] the movment of colorbar displaying - 0: static colorbar image, - * 1~255: right shifting a type of color per (1~255)frames - * [3] the color type of colorbar displaying - 0~9: color bar, white, red, orange, - * yellow, green, cyan, bule, pupper, black - */ - if (sscanf(buf, "%hhu %hhu %hhu %u", &cfg->enable, &cfg->self_timing, - &cfg->dynamic_rate, &cfg->pattern) != 4) { - return -EINVAL; - } - - if (cfg->pattern > 9 || cfg->enable > 1 || cfg->self_timing > 1) - return -EINVAL; - - ret = drm_dev_enter(priv->dev, &idx); - if (!ret) - return -ENODEV; - - hibmc_dp_set_cbar(&priv->dp, cfg); - - drm_dev_exit(idx); - - return count; -} - -static int hibmc_dp_dbgfs_show(struct seq_file *m, void *arg) -{ - struct hibmc_drm_private *priv = m->private; - struct hibmc_dp_cbar_cfg *cfg = &priv->dp.cfg; - int idx; - - if (!drm_dev_enter(priv->dev, &idx)) - return -ENODEV; - - seq_printf(m, "hibmc dp colorbar cfg: %u %u %u %u\n", cfg->enable, cfg->self_timing, - cfg->dynamic_rate, cfg->pattern); - - drm_dev_exit(idx); - - return 0; -} - -static int hibmc_open(struct inode *inode, struct file *filp) -{ - return single_open(filp, hibmc_dp_dbgfs_show, inode->i_private); -} - -static const struct file_operations hibmc_dbg_fops = { - .owner = THIS_MODULE, - .write = hibmc_control_write, - .read = seq_read, - .open = hibmc_open, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct drm_info_list hibmc_debugfs_list[] = { - { "hibmc-dp", hibmc_dp_show }, -}; - -void hibmc_debugfs_init(struct drm_connector *connector, struct dentry *root) -{ - struct drm_device *dev = connector->dev; - struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); - struct drm_minor *minor = dev->primary; - - /* create the file in drm directory, so we don't need to remove manually */ - debugfs_create_file("colorbar-cfg", 0200, root, priv, &hibmc_dbg_fops); - - drm_debugfs_create_files(hibmc_debugfs_list, ARRAY_SIZE(hibmc_debugfs_list), - minor->debugfs_root, minor); -} diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c deleted file mode 100644 index ae6f5c235e02..000000000000 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c +++ /dev/null @@ -1,257 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// Copyright (c) 2024 Hisilicon Limited. - -#include <linux/io.h> - -#include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_modes.h> -#include <drm/drm_drv.h> -#include <drm/drm_edid.h> - -#include "hibmc_drm_drv.h" -#include "hibmc_drm_regs.h" -#include "dp/dp_hw.h" - -#define HIBMC_DP_MASKED_SINK_HPD_PLUG_INT BIT(2) -#define HIBMC_DP_MASKED_SINK_HPD_UNPLUG_INT BIT(3) - -static int hibmc_dp_connector_get_modes(struct drm_connector *connector) -{ - int count; - struct hibmc_dp *dp = to_hibmc_dp(connector); - struct edid *edid; - - - edid = drm_get_edid(connector, &dp->aux.ddc); - if (edid) { - drm_connector_update_edid_property(connector, edid); - count = drm_add_edid_modes(connector, edid); - if (count) { - dp->is_connected = true; - goto out; - } - } - - - dp->is_connected = false; - - count = drm_add_modes_noedid(connector, connector->dev->mode_config.max_width, - connector->dev->mode_config.max_height); - drm_set_preferred_mode(connector, 1024, 768); // temporary implementation - -out: - kfree(edid); - - return count; -} - -static int hibmc_dp_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode, - struct drm_modeset_acquire_ctx *ctx, - enum drm_mode_status *status) -{ - struct hibmc_dp *dp = to_hibmc_dp(connector); - u64 cur_val, max_val; - - if (!dp->is_connected) { - *status = MODE_OK; - return 0; - } - - cur_val = (u64)mode->htotal * mode->vtotal * drm_mode_vrefresh(mode) * BPP_24; - max_val = (u64)hibmc_dp_get_link_rate(dp) * DP_MODE_VALI_CAL * hibmc_dp_get_lanes(dp); - if (cur_val > max_val) - *status = MODE_CLOCK_HIGH; - else - *status = MODE_OK; - - return 0; -} - -static int hibmc_dp_detect(struct drm_connector *connector, - struct drm_modeset_acquire_ctx *ctx, bool force) -{ - struct hibmc_dp *dp = to_hibmc_dp(connector); - - if (dp->hpd_status) - return connector_status_connected; - else - return connector_status_disconnected; -} - -static const struct drm_connector_helper_funcs hibmc_dp_conn_helper_funcs = { - .get_modes = hibmc_dp_connector_get_modes, - .mode_valid_ctx = hibmc_dp_mode_valid, - .detect_ctx = hibmc_dp_detect, -}; - -static int hibmc_dp_late_register(struct drm_connector *connector) -{ - struct hibmc_dp *dp = to_hibmc_dp(connector); - - hibmc_dp_enable_int(dp); - - return drm_dp_aux_register(&dp->aux); -} - -static void hibmc_dp_early_unregister(struct drm_connector *connector) -{ - struct hibmc_dp *dp = to_hibmc_dp(connector); - - drm_dp_aux_unregister(&dp->aux); - - hibmc_dp_disable_int(dp); -} - -static const struct drm_connector_funcs hibmc_dp_conn_funcs = { - .reset = drm_atomic_helper_connector_reset, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = drm_connector_cleanup, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, - .late_register = hibmc_dp_late_register, - .early_unregister = hibmc_dp_early_unregister, -}; - -static inline int hibmc_dp_prepare(struct hibmc_dp *dp, struct drm_display_mode *mode) -{ - int ret; - - hibmc_dp_display_en(dp, false); - - ret = hibmc_dp_mode_set(dp, mode); - if (ret) - drm_err(dp->drm_dev, "hibmc dp mode set failed: %d\n", ret); - - return ret; -} - -static void hibmc_dp_encoder_enable(struct drm_encoder *drm_encoder, - struct drm_atomic_state *state) -{ - struct hibmc_dp *dp = container_of(drm_encoder, struct hibmc_dp, encoder); - struct drm_display_mode *mode = &drm_encoder->crtc->state->mode; - - if (hibmc_dp_prepare(dp, mode)) - return; - - hibmc_dp_display_en(dp, true); -} - -static void hibmc_dp_encoder_disable(struct drm_encoder *drm_encoder, - struct drm_atomic_state *state) -{ - struct hibmc_dp *dp = container_of(drm_encoder, struct hibmc_dp, encoder); - - hibmc_dp_display_en(dp, false); -} - -static void hibmc_dp_encoder_mode_set(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) -{ - u32 reg; - struct drm_device *dev = encoder->dev; - struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); - - reg = readl(priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); - reg |= HIBMC_DISPLAY_CONTROL_FPVDDEN(1); - reg |= HIBMC_DISPLAY_CONTROL_PANELDATE(1); - reg |= HIBMC_DISPLAY_CONTROL_FPEN(1); - reg |= HIBMC_DISPLAY_CONTROL_VBIASEN(1); - writel(reg, priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); -} - -static const struct drm_encoder_helper_funcs hibmc_dp_encoder_helper_funcs = { - .atomic_enable = hibmc_dp_encoder_enable, - .atomic_disable = hibmc_dp_encoder_disable, - .atomic_mode_set = hibmc_dp_encoder_mode_set, -}; - -static const struct drm_encoder_funcs hibmc_encoder_funcs = { - .destroy = drm_encoder_cleanup, -}; - -irqreturn_t hibmc_dp_hpd_isr(int irq, void *arg) -{ - struct drm_device *dev = (struct drm_device *)arg; - struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); - struct hibmc_dp *dp = &priv->dp; - int idx; - - if (!drm_dev_enter(dev, &idx)) - return -ENODEV; - - if (dp->hpd_status) { /* only check unplug int when the last status is HPD in */ - if ((dp->irq_status & HIBMC_DP_MASKED_SINK_HPD_UNPLUG_INT)) { - drm_dbg_dp(dev, "HPD OUT isr occur!\n"); - hibmc_dp_reset_link(dp); - dp->hpd_status = 0; - if (dev->registered) - drm_helper_hpd_irq_event(dev); - } else { - drm_dbg_dp(dev, "HPD OUT occur but err!\n"); - } - } else { - if (dp->irq_status & HIBMC_DP_MASKED_SINK_HPD_PLUG_INT) { - drm_dbg_dp(priv->dev, "HPD IN isr occur!\n"); - hibmc_dp_hpd_cfg(dp); - dp->hpd_status = 1; - if (dev->registered) - drm_helper_hpd_irq_event(dev); - } else { - drm_dbg_dp(dev, "HPD IN occur but err!\n"); - } - } - - drm_dev_exit(idx); - - return IRQ_HANDLED; -} - -int hibmc_dp_init(struct hibmc_drm_private *priv) -{ - struct drm_device *dev = priv->dev; - struct drm_crtc *crtc = &priv->crtc; - struct hibmc_dp *dp = &priv->dp; - struct drm_connector *connector = &dp->connector; - struct drm_encoder *encoder = &dp->encoder; - int ret; - - dp->mmio = priv->mmio; - dp->drm_dev = dev; - - ret = hibmc_dp_hw_init(&priv->dp); - if (ret) { - drm_err(dev, "hibmc dp hw init failed: %d\n", ret); - return ret; - } - - hibmc_dp_display_en(&priv->dp, false); - - encoder->possible_crtcs = drm_crtc_mask(crtc); - ret = drm_encoder_init(dev, encoder, &hibmc_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); - if (ret) { - drm_err(dev, "init dp encoder failed: %d\n", ret); - return ret; - } - - drm_encoder_helper_add(encoder, &hibmc_dp_encoder_helper_funcs); - - ret = drm_connector_init_with_ddc(dev, connector, &hibmc_dp_conn_funcs, - DRM_MODE_CONNECTOR_DisplayPort, &dp->aux.ddc); - if (ret) { - drm_err(dev, "init dp connector failed: %d\n", ret); - return ret; - } - - drm_connector_helper_add(connector, &hibmc_dp_conn_helper_funcs); - - drm_connector_attach_encoder(connector, encoder); - - connector->polled = DRM_CONNECTOR_POLL_HPD; - - return 0; -} diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index b9398a78c26c..2723b4ae9856 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -20,17 +20,12 @@ #include <drm/drm_irq.h> #include <drm/drm_managed.h> #include <drm/drm_vblank.h> -#include <drm/drm_probe_helper.h> #include "hibmc_drm_drv.h" #include "hibmc_drm_regs.h" -#include "dp/dp_reg.h" - DEFINE_DRM_GEM_FOPS(hibmc_fops); -static const char *g_irqs_names_map[HIBMC_MAX_VECTORS] = { "hibmc-vblank", "hibmc-hpd" }; - static irqreturn_t hibmc_drm_interrupt(int irq, void *arg) { struct drm_device *dev = (struct drm_device *)arg; @@ -49,22 +44,6 @@ static irqreturn_t hibmc_drm_interrupt(int irq, void *arg) return IRQ_HANDLED; } -static irqreturn_t hibmc_dp_interrupt(int irq, void *arg) -{ - struct drm_device *dev = (struct drm_device *)arg; - struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); - u32 status; - - status = readl(priv->mmio + HIBMC_DP_INTSTAT); - if (status) { - priv->dp.irq_status = status; - writel(status, priv->mmio + HIBMC_DP_INTCLR); - return IRQ_WAKE_THREAD; - } - - return IRQ_HANDLED; -} - static struct drm_driver hibmc_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &hibmc_fops, @@ -120,35 +99,16 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv) ret = hibmc_de_init(priv); if (ret) { drm_err(priv->dev, "failed to init de: %d\n", ret); - goto err; - } - - /* - * If the serdes reg is readable and is not equal to 0, - * DP block exists and initializes it. - */ - ret = readl(priv->mmio + HIBMC_DP_HOST_SERDES_CTRL); - if (ret) { - ret = hibmc_dp_init(priv); - if (ret) { - drm_err(priv->dev, "failed to init dp: %d\n", ret); - goto err; - } + return ret; } ret = hibmc_vdac_init(priv); if (ret) { drm_err(priv->dev, "failed to init vdac: %d\n", ret); - goto err; + return ret; } - drm_kms_helper_poll_init(priv->dev); return 0; - -err: - drm_atomic_helper_shutdown(priv->dev); - - return ret; } static void hibmc_kms_fini(struct hibmc_drm_private *priv) @@ -297,47 +257,20 @@ static int hibmc_hw_init(struct hibmc_drm_private *priv) return 0; } -static void hibmc_unload(struct drm_device *dev) -{ - drm_atomic_helper_shutdown(dev); - drm_kms_helper_poll_fini(dev); -} - -static int hibmc_msi_init(struct drm_device *dev) +static int hibmc_unload(struct drm_device *dev) { - struct pci_dev *pdev = to_pci_dev(dev->dev); - int valid_irq_num; - int irq; - int ret; - int i; + struct hibmc_drm_private *priv = dev->dev_private; - ret = pci_alloc_irq_vectors(pdev, HIBMC_MIN_VECTORS, - HIBMC_MAX_VECTORS, PCI_IRQ_MSI); - if (ret < 0) { - drm_err(dev, "enabling MSI failed: %d\n", ret); - return ret; - } + drm_atomic_helper_shutdown(dev); - valid_irq_num = ret; - - for (i = 0; i < valid_irq_num; i++) { - irq = pci_irq_vector(pdev, i); - - if (i) - /* PCI devices require shared interrupts. */ - ret = devm_request_threaded_irq(&pdev->dev, irq, - hibmc_dp_interrupt, - hibmc_dp_hpd_isr, - IRQF_SHARED, g_irqs_names_map[i], dev); - else - ret = devm_request_irq(&pdev->dev, irq, hibmc_drm_interrupt, - IRQF_SHARED, g_irqs_names_map[i], dev); - if (ret) { - drm_err(dev, "install irq failed: %d\n", ret); - return ret; - } - } + if (dev->irq_enabled) + drm_irq_uninstall(dev); + pci_disable_msi(dev->pdev); + hibmc_kms_fini(priv); + hibmc_mm_fini(priv); + hibmc_hw_unmap(priv); + dev->dev_private = NULL; return 0; } @@ -355,19 +288,21 @@ static int hibmc_load(struct drm_device *dev) priv->dev = dev; ret = hibmc_hw_init(priv); - if (ret) - return ret; + if (ret) { + drm_err(dev, "failed to initialize hardware: %d\n", ret); + goto err_alloc; + } ret = hibmc_mm_init(priv); if (ret) { - drm_err(dev, "Error initializing VRAM MM; %d\n", ret); - return ret; + drm_err(dev, "failed to initialize mm: %d\n", ret); + goto err_hw; } ret = hibmc_kms_init(priv); if (ret) { - drm_err(dev, "hibmc kms init failed, ret:%d\n", ret); - return ret; + drm_err(dev, "failed to initialize kms: %d\n", ret); + goto err_mm; } ret = drm_vblank_init(dev, dev->mode_config.num_crtc); @@ -376,10 +311,13 @@ static int hibmc_load(struct drm_device *dev) goto err_kms; } - ret = hibmc_msi_init(dev); + ret = pci_enable_msi(dev->pdev); if (ret) { - drm_err(dev, "hibmc msi init failed, ret:%d\n", ret); - goto err_kms; + drm_warn(dev, "enabling MSI failed: %d\n", ret); + } else { + ret = drm_irq_install(dev, dev->pdev->irq); + if (ret) + drm_warn(dev, "install irq failed: %d\n", ret); } /* reset all the states of crtc/plane/encoder/connector */ @@ -389,8 +327,11 @@ static int hibmc_load(struct drm_device *dev) err_kms: hibmc_kms_fini(priv); +err_mm: hibmc_mm_fini(priv); +err_hw: hibmc_hw_unmap(priv); +err_alloc: dev->dev_private = NULL; return ret; @@ -400,7 +341,6 @@ static int hibmc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct drm_device *dev; - struct hibmc_drm_private *priv; int ret; ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, @@ -423,8 +363,6 @@ static int hibmc_pci_probe(struct pci_dev *pdev, goto err_free; } - pci_set_master(pdev); - ret = hibmc_load(dev); if (ret) { drm_err(dev, "failed to load hibmc: %d\n", ret); @@ -440,9 +378,6 @@ static int hibmc_pci_probe(struct pci_dev *pdev, drm_fbdev_generic_setup(dev, dev->mode_config.preferred_depth); - priv = to_hibmc_drm_private(dev); - hibmc_debugfs_init(&priv->dp.connector, dev->primary->debugfs_root); - return 0; err_unload: @@ -480,8 +415,8 @@ static struct pci_driver hibmc_pci_driver = { .id_table = hibmc_pci_table, .probe = hibmc_pci_probe, .remove = hibmc_pci_remove, - .shutdown = hibmc_pci_shutdown, - .driver.pm = &hibmc_pm_ops, + .shutdown = hibmc_pci_shutdown, + .driver.pm = &hibmc_pm_ops, }; module_pci_driver(hibmc_pci_driver); diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index 617585268009..87d2aad0bb5e 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -21,16 +21,12 @@ #include <drm/drm_edid.h> #include <drm/drm_fb_helper.h> #include <drm/drm_framebuffer.h> -#include "dp/dp_hw.h" struct drm_device; -#define HIBMC_MIN_VECTORS 1 -#define HIBMC_MAX_VECTORS 2 -struct hibmc_vdac { - struct drm_device *dev; - struct drm_encoder encoder; - struct drm_connector connector; +struct hibmc_connector { + struct drm_connector base; + struct i2c_adapter adapter; struct i2c_algo_bit_data bit_data; }; @@ -46,19 +42,14 @@ struct hibmc_drm_private { struct drm_device *dev; struct drm_plane primary_plane; struct drm_crtc crtc; + struct drm_encoder encoder; + struct hibmc_connector connector; bool mode_config_initialized; - struct hibmc_vdac vdac; - struct hibmc_dp dp; }; -static inline struct hibmc_vdac *to_hibmc_vdac(struct drm_connector *connector) -{ - return container_of(connector, struct hibmc_vdac, connector); -} - -static inline struct hibmc_dp *to_hibmc_dp(struct drm_connector *connector) +static inline struct hibmc_connector *to_hibmc_connector(struct drm_connector *connector) { - return container_of(connector, struct hibmc_dp, connector); + return container_of(connector, struct hibmc_connector, base); } static inline struct hibmc_drm_private *to_hibmc_drm_private(struct drm_device *dev) @@ -78,14 +69,8 @@ int hibmc_mm_init(struct hibmc_drm_private *hibmc); void hibmc_mm_fini(struct hibmc_drm_private *hibmc); int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args); -int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_vdac *connector); -void hibmc_ddc_del(struct hibmc_vdac *vdac); -int hibmc_dp_init(struct hibmc_drm_private *priv); +int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_connector *connector); extern const struct drm_mode_config_funcs hibmc_mode_funcs; -void hibmc_debugfs_init(struct drm_connector *connector, struct dentry *root); - -irqreturn_t hibmc_dp_hpd_isr(int irq, void *arg); - #endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c index 2e0fcd2b3bc1..86d712090d87 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c @@ -25,8 +25,8 @@ static void hibmc_set_i2c_signal(void *data, u32 mask, int value) { - struct hibmc_vdac *vdac = data; - struct hibmc_drm_private *priv = to_hibmc_drm_private(vdac->connector.dev); + struct hibmc_connector *hibmc_connector = data; + struct hibmc_drm_private *priv = to_hibmc_drm_private(hibmc_connector->base.dev); u32 tmp_dir = readl(priv->mmio + GPIO_DATA_DIRECTION); if (value) { @@ -45,8 +45,8 @@ static void hibmc_set_i2c_signal(void *data, u32 mask, int value) static int hibmc_get_i2c_signal(void *data, u32 mask) { - struct hibmc_vdac *vdac = data; - struct hibmc_drm_private *priv = to_hibmc_drm_private(vdac->connector.dev); + struct hibmc_connector *hibmc_connector = data; + struct hibmc_drm_private *priv = to_hibmc_drm_private(hibmc_connector->base.dev); u32 tmp_dir = readl(priv->mmio + GPIO_DATA_DIRECTION); if ((tmp_dir & mask) != mask) { @@ -77,27 +77,23 @@ static int hibmc_ddc_getscl(void *data) return hibmc_get_i2c_signal(data, I2C_SCL_MASK); } -int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_vdac *vdac) +int hibmc_ddc_create(struct drm_device *drm_dev, + struct hibmc_connector *connector) { - vdac->adapter.owner = THIS_MODULE; - vdac->adapter.class = I2C_CLASS_DDC; - snprintf(vdac->adapter.name, I2C_NAME_SIZE, "HIS i2c bit bus"); - vdac->adapter.dev.parent = drm_dev->dev; - i2c_set_adapdata(&vdac->adapter, vdac); - vdac->adapter.algo_data = &vdac->bit_data; - - vdac->bit_data.udelay = 20; - vdac->bit_data.timeout = usecs_to_jiffies(2000); - vdac->bit_data.data = vdac; - vdac->bit_data.setsda = hibmc_ddc_setsda; - vdac->bit_data.setscl = hibmc_ddc_setscl; - vdac->bit_data.getsda = hibmc_ddc_getsda; - vdac->bit_data.getscl = hibmc_ddc_getscl; - - return i2c_bit_add_bus(&vdac->adapter); -} - -void hibmc_ddc_del(struct hibmc_vdac *vdac) -{ - i2c_del_adapter(&vdac->adapter); + connector->adapter.owner = THIS_MODULE; + connector->adapter.class = I2C_CLASS_DDC; + snprintf(connector->adapter.name, I2C_NAME_SIZE, "HIS i2c bit bus"); + connector->adapter.dev.parent = &drm_dev->pdev->dev; + i2c_set_adapdata(&connector->adapter, connector); + connector->adapter.algo_data = &connector->bit_data; + + connector->bit_data.udelay = 20; + connector->bit_data.timeout = usecs_to_jiffies(2000); + connector->bit_data.data = connector; + connector->bit_data.setsda = hibmc_ddc_setsda; + connector->bit_data.setscl = hibmc_ddc_setscl; + connector->bit_data.getsda = hibmc_ddc_getsda; + connector->bit_data.getscl = hibmc_ddc_getscl; + + return i2c_bit_add_bus(&connector->adapter); } diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c index 9c0ec6e54df0..fb761853289c 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -42,9 +42,9 @@ static int hibmc_connector_get_modes(struct drm_connector *connector) { int count; void *edid; - struct hibmc_vdac *vdac = to_hibmc_vdac(connector); + struct hibmc_connector *hibmc_connector = to_hibmc_connector(connector); - edid = drm_get_edid(connector, &vdac->adapter); + edid = drm_get_edid(connector, &hibmc_connector->adapter); if (edid) { drm_connector_update_edid_property(connector, edid); count = drm_add_edid_modes(connector, edid); @@ -88,53 +88,26 @@ static enum drm_mode_status hibmc_connector_mode_valid( return hibmc_valid_mode(mode->hdisplay, mode->vdisplay); } -static void hibmc_vdac_connector_destroy(struct drm_connector *connector) +static void hibmc_connector_destroy(struct drm_connector *connector) { - struct hibmc_vdac *vdac = to_hibmc_vdac(connector); + struct hibmc_connector *hibmc_connector = to_hibmc_connector(connector); - i2c_del_adapter(&vdac->adapter); + i2c_del_adapter(&hibmc_connector->adapter); drm_connector_cleanup(connector); } -static int hibmc_vdac_detect(struct drm_connector *connector, struct drm_modeset_acquire_ctx *ctx, - bool force) -{ - struct hibmc_drm_private *priv = to_hibmc_drm_private(connector->dev); - struct hibmc_dp *dp = &priv->dp; - - if (dp->hpd_status) - return connector_status_disconnected; - - return connector_status_connected; -} - static const struct drm_connector_helper_funcs hibmc_connector_helper_funcs = { .get_modes = hibmc_connector_get_modes, .mode_valid = hibmc_connector_mode_valid, - .detect_ctx = hibmc_vdac_detect, }; -static void hibmc_vdac_force(struct drm_connector *connector) -{ - struct hibmc_drm_private *priv = to_hibmc_drm_private(connector->dev); - struct hibmc_dp *dp = &priv->dp; - - if (dp->hpd_status) { - connector->status = connector_status_disconnected; - return; - } - - connector->status = connector_status_connected; -} - static const struct drm_connector_funcs hibmc_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = hibmc_vdac_connector_destroy, + .destroy = hibmc_connector_destroy, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, - .force = hibmc_vdac_force, }; static void hibmc_encoder_mode_set(struct drm_encoder *encoder, @@ -164,12 +137,12 @@ static const struct drm_encoder_funcs hibmc_encoder_funcs = { int hibmc_vdac_init(struct hibmc_drm_private *priv) { struct drm_device *dev = priv->dev; - struct hibmc_vdac *vdac = &priv->vdac; - struct drm_encoder *encoder = &vdac->encoder; - struct drm_connector *connector = &vdac->connector; + struct hibmc_connector *hibmc_connector = &priv->connector; + struct drm_encoder *encoder = &priv->encoder; + struct drm_connector *connector = &hibmc_connector->base; int ret; - ret = hibmc_ddc_create(dev, vdac); + ret = hibmc_ddc_create(dev, hibmc_connector); if (ret) { drm_err(dev, "failed to create ddc: %d\n", ret); return ret; @@ -180,7 +153,7 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv) DRM_MODE_ENCODER_DAC, NULL); if (ret) { drm_err(dev, "failed to init encoder: %d\n", ret); - goto err; + return ret; } drm_encoder_helper_add(encoder, &hibmc_encoder_helper_funcs); @@ -188,21 +161,14 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv) ret = drm_connector_init_with_ddc(dev, connector, &hibmc_connector_funcs, DRM_MODE_CONNECTOR_VGA, - &vdac->adapter); + &hibmc_connector->adapter); if (ret) { drm_err(dev, "failed to init connector: %d\n", ret); - goto err; + return ret; } drm_connector_helper_add(connector, &hibmc_connector_helper_funcs); drm_connector_attach_encoder(connector, encoder); - connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; - return 0; - -err: - hibmc_ddc_del(vdac); - - return ret; } diff --git a/include/drm/drm_probe_helper.h b/include/drm/drm_probe_helper.h index 621885d5de84..8d3ed2834d34 100644 --- a/include/drm/drm_probe_helper.h +++ b/include/drm/drm_probe_helper.h @@ -24,7 +24,4 @@ void drm_kms_helper_poll_disable(struct drm_device *dev); void drm_kms_helper_poll_enable(struct drm_device *dev); bool drm_kms_helper_is_poll_worker(void); -int drm_connector_helper_detect_from_ddc(struct drm_connector *connector, - struct drm_modeset_acquire_ctx *ctx, - bool force); #endif -- 2.34.1
2 1
0 0
  • ← Newer
  • 1
  • 2
  • 3
  • 4
  • 5
  • ...
  • 2281
  • Older →

HyperKitty Powered by HyperKitty