From dcbef373c443246b751c17463ae36770940f85b7 Mon Sep 17 00:00:00 2001 From: chayleaf Date: Tue, 21 Nov 2023 04:46:52 +0700 Subject: [PATCH] update inputs --- flake.lock | 101 +- flake.nix | 8 +- home/common/firefox.nix | 2 +- home/common/gui.nix | 8 +- home/hosts/nixmsi.nix | 2 +- pkgs/_sources/generated.json | 16 +- pkgs/_sources/generated.nix | 14 +- pkgs/certspotter/configurable-sendmail.patch | 71 - pkgs/certspotter/default.nix | 41 - pkgs/default.nix | 53 +- pkgs/firefox-addons/generated.nix | 6 +- pkgs/libaribcaption/default.nix | 33 - pkgs/pineapplebot.nix | 34 - pkgs/postgresql-packages/default.nix | 45 - pkgs/postgresql-packages/tsja.nix | 39 - pkgs/rizin/rz-ghidra.nix | 54 - pkgs/rizin/wrapper.nix | 29 - system/devices/radxa-rock5a-server.nix | 2 + system/hardware/bpi-r3/default.nix | 2 +- system/hardware/msi-delta-15/default.nix | 8 +- .../revert-amd-ucode-update-fam19h.patch | 24 +- system/hardware/radxa-rock5a/default.nix | 22 +- system/hardware/radxa-rock5a/linux_6.7.patch | 17516 ++++++++++++++++ system/hosts/server/files.nix | 1 - system/hosts/server/home.nix | 19 +- system/hosts/server/maubot.nix | 6 +- system/hosts/server/options.nix | 4 - system/modules/certspotter.nix | 115 - 28 files changed, 17649 insertions(+), 626 deletions(-) delete mode 100644 pkgs/certspotter/configurable-sendmail.patch delete mode 100644 pkgs/certspotter/default.nix delete mode 100644 pkgs/libaribcaption/default.nix delete mode 100644 pkgs/pineapplebot.nix delete mode 100644 pkgs/postgresql-packages/default.nix delete mode 100644 pkgs/postgresql-packages/tsja.nix delete mode 100644 pkgs/rizin/rz-ghidra.nix delete mode 100644 pkgs/rizin/wrapper.nix create mode 100644 system/hardware/radxa-rock5a/linux_6.7.patch delete mode 100644 system/modules/certspotter.nix diff --git a/flake.lock b/flake.lock index c1cf634..0e4d085 100644 --- a/flake.lock +++ b/flake.lock @@ -69,11 +69,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1693611461, - "narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=", + "lastModified": 1698882062, + "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca", + "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877", "type": "github" }, "original": { @@ -107,11 +107,11 @@ ] }, "locked": { - "lastModified": 1696446489, - "narHash": "sha256-xSjMKdNR+q/3hdSPyg/LUMsZT/WIoUi8dcm5zT4SMUQ=", + "lastModified": 1700553346, + "narHash": "sha256-kW7uWsCv/lxuA824Ng6EYD9hlVYRyjuFn0xBbYltAeQ=", "owner": "nix-community", "repo": "home-manager", - "rev": "68f7d8c0fb0bfc67d1916dd7f06288424360d43a", + "rev": "1aabb0a31b25ad83cfaa37c3fe29053417cd9a0f", "type": "github" }, "original": { @@ -122,11 +122,11 @@ }, "impermanence": { "locked": { - "lastModified": 1694622745, - "narHash": "sha256-z397+eDhKx9c2qNafL1xv75lC0Q4nOaFlhaU1TINqb8=", + "lastModified": 1697303681, + "narHash": "sha256-caJ0rXeagaih+xTgRduYtYKL1rZ9ylh06CIrt1w5B4g=", "owner": "nix-community", "repo": "impermanence", - "rev": "e9643d08d0d193a2e074a19d4d90c67a874d932e", + "rev": "0f317c2e9e56550ce12323eb39302d251618f5b5", "type": "github" }, "original": { @@ -143,11 +143,11 @@ ] }, "locked": { - "lastModified": 1697331506, - "narHash": "sha256-N6RD9EudU+i7SJO3z3S309XQRhp81iqaN9G9sxRtVts=", + "lastModified": 1700661503, + "narHash": "sha256-2GGbVFmAC8G1FLxIabCBYhfbUeUIZM/3p2VW9Eia60Q=", "owner": "chayleaf", "repo": "maubot.nix", - "rev": "cf32a2873523c80cebdd1ee409c45593040944b8", + "rev": "71d397c5897233c592d35be6c4f28c295ce2e79d", "type": "github" }, "original": { @@ -181,11 +181,11 @@ ] }, "locked": { - "lastModified": 1696468271, - "narHash": "sha256-ZpzAIqs8VmgRDz+rBe28+TErlXkhzrgPKg3YKYraReE=", + "lastModified": 1700616016, + "narHash": "sha256-GCD2U3jMWmBqJccDDXr8pf2Ia2NnFiIYqnm9wK1DxLk=", "owner": "fufexan", "repo": "nix-gaming", - "rev": "cc55064e30efdf1b1ad3df4d39983314ef440aae", + "rev": "7d81bdbf62936d50906609097b1fd6e68e59daa7", "type": "github" }, "original": { @@ -196,11 +196,11 @@ }, "nixos-hardware": { "locked": { - "lastModified": 1696614066, - "narHash": "sha256-nAyYhO7TCr1tikacP37O9FnGr2USOsVBD3IgvndUYjM=", + "lastModified": 1700559156, + "narHash": "sha256-gL4epO/qf+wo30JjC3g+b5Bs8UrpxzkhNBBsUYxpw2g=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "bb2db418b616fea536b1be7f6ee72fb45c11afe0", + "rev": "c3abafb01cd7045dba522af29b625bd1e170c2fb", "type": "github" }, "original": { @@ -225,11 +225,11 @@ "utils": "utils" }, "locked": { - "lastModified": 1689976554, - "narHash": "sha256-uWJq3sIhkqfzPmfB2RWd5XFVooGFfSuJH9ER/r302xQ=", + "lastModified": 1700085753, + "narHash": "sha256-qtib7f3eRwfaUF+VziJXiBcZFqpHCAXS4HlrFsnzzl4=", "owner": "simple-nixos-mailserver", "repo": "nixos-mailserver", - "rev": "c63f6e7b053c18325194ff0e274dba44e8d2271e", + "rev": "008d78cc21959e33d0d31f375b88353a7d7121ae", "type": "gitlab" }, "original": { @@ -245,11 +245,11 @@ ] }, "locked": { - "lastModified": 1698227887, - "narHash": "sha256-QDVR3tZ5ugxtyCb9TlZLmqNTdAAH6wMUU8sGnPtduTA=", + "lastModified": 1700524221, + "narHash": "sha256-YQGjhwhd68N9fILRwZXlT3z6yXP5kRH8B6bxD2uQq14=", "owner": "chayleaf", "repo": "nixos-router", - "rev": "7d9669390a87da7e67dabcbce34681630e67cf32", + "rev": "e9d2ec7ad1f34cb9f1f71c1400430af817431a3b", "type": "github" }, "original": { @@ -260,11 +260,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1697804921, - "narHash": "sha256-PAoThb0U52HGscrU/Qp1GKwidqM6xnWxgovJCXNpjCc=", + "lastModified": 1700647334, + "narHash": "sha256-0F5B7oJAAJ4u4sq97nIhcH/pVFkFjYY5JFxXXHOBJQ4=", "owner": "chayleaf", "repo": "nixpkgs", - "rev": "77ba48251d2b629d347e566c888000a379711ce0", + "rev": "e70edbbc30bca7d90c4a1e8c653ceb1607cc2858", "type": "github" }, "original": { @@ -276,11 +276,11 @@ "nixpkgs-lib": { "locked": { "dir": "lib", - "lastModified": 1693471703, - "narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=", + "lastModified": 1698611440, + "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85", + "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735", "type": "github" }, "original": { @@ -291,22 +291,6 @@ "type": "github" } }, - "nixpkgs2": { - "locked": { - "lastModified": 1696696817, - "narHash": "sha256-K8/YirUEkUD1Xd9Qg5R9czYU03M8wDN5W3DYns9F0rc=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "0df1d6c8cac8e8dc08f42bfe062a1025555c9b6a", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "master", - "repo": "nixpkgs", - "type": "github" - } - }, "notlua": { "inputs": { "nixpkgs": [ @@ -314,11 +298,11 @@ ] }, "locked": { - "lastModified": 1691609126, - "narHash": "sha256-InbGoENdL8LNT/09pl7AW5uv2ZSDburqr5LgvkJDfj0=", + "lastModified": 1697413333, + "narHash": "sha256-2nmu/+QhR/VhxFFr54l0Ok/yVhLCrrYVuTgeD4LHEhE=", "owner": "chayleaf", "repo": "notlua", - "rev": "0e972a0d23f2faa511b9a3f6d445204e18cd5020", + "rev": "ef7cdb7a883fe87238c9fff13bc14ad1fd06f4ba", "type": "github" }, "original": { @@ -334,11 +318,11 @@ ] }, "locked": { - "lastModified": 1691616520, - "narHash": "sha256-loZuL2YnMNwgH5GEZfXgXZadvo5P3Sp+YZSf9L3Wpu8=", + "lastModified": 1700483422, + "narHash": "sha256-ni6niOmObnG9EVGtaeT1I7ULz5+EkEewGTJVeFuWNuc=", "owner": "chayleaf", "repo": "notnft", - "rev": "118e25deeb741ba7963931212f02c96c50898578", + "rev": "b3e6a023a13a81d70a6a30997e2f1aaf36feafb3", "type": "github" }, "original": { @@ -349,11 +333,11 @@ }, "nur": { "locked": { - "lastModified": 1696624462, - "narHash": "sha256-lGmf7IPqWLfxvEQcPujB8dzu+++NHqGYQkmC05y3ByA=", + "lastModified": 1700660661, + "narHash": "sha256-1+//5oLdqYo8ptS/ZpaGEzgnQ6FWJOjLPyTuiD6mPjY=", "owner": "nix-community", "repo": "NUR", - "rev": "560b6a71f7fe0353dc19bc366a5ace71fbda51d1", + "rev": "0707dd061f4fb82393f3c96c6ed10c60396d7f9c", "type": "github" }, "original": { @@ -374,7 +358,6 @@ "nixos-mailserver": "nixos-mailserver", "nixos-router": "nixos-router", "nixpkgs": "nixpkgs", - "nixpkgs2": "nixpkgs2", "notlua": "notlua", "notnft": "notnft", "nur": "nur", @@ -389,11 +372,11 @@ ] }, "locked": { - "lastModified": 1696558324, - "narHash": "sha256-TnnP4LGwDB8ZGE7h2n4nA9Faee8xPkMdNcyrzJ57cbw=", + "lastModified": 1700619457, + "narHash": "sha256-zjmlh8xo4UsNdw7nMyiHgQg1xXNcJnpdMLvyunnnitQ=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "fdb37574a04df04aaa8cf7708f94a9309caebe2b", + "rev": "7c94410d52d4e8bd72803fc1fe6c51fe179edaf5", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 292f75c..1c4e3a2 100644 --- a/flake.nix +++ b/flake.nix @@ -2,10 +2,9 @@ description = "NixOS + Home Manager configuration of chayleaf"; inputs = { - #nixpkgs.url = "github:nixos/nixpkgs/3dc2b4f8166f744c3b3e9ff8224e7c5d74a5424f"; - # nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + #nixpkgs.url = "github:NixOS/nixpkgs/3dc2b4f8166f744c3b3e9ff8224e7c5d74a5424f"; + # nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs.url = "github:chayleaf/nixpkgs"; - nixpkgs2.url = "github:nixos/nixpkgs/master"; nixos-hardware.url = "github:NixOS/nixos-hardware"; mobile-nixos = { # url = "github:NixOS/mobile-nixos"; @@ -59,7 +58,6 @@ outputs = inputs@ { self , nixpkgs - , nixpkgs2 , nixos-hardware , mobile-nixos , impermanence @@ -157,7 +155,6 @@ ./system/devices/radxa-rock5a-server.nix (if devMaubot then import /${devPath}/maubot.nix/module else maubot.nixosModules.default) ./system/modules/scanservjs.nix - ./system/modules/certspotter.nix ]; }; server-cross = crossConfig server; @@ -172,7 +169,6 @@ notlua = notlua.lib.${system}; }; home.user = [ - { _module.args.pkgs2 = import nixpkgs2 { inherit system; overlays = [ overlay ]; }; } nur.nixosModules.nur ./home/hosts/nixmsi.nix ]; diff --git a/home/common/firefox.nix b/home/common/firefox.nix index e8119e3..42652ba 100644 --- a/home/common/firefox.nix +++ b/home/common/firefox.nix @@ -10,7 +10,7 @@ inherit (pkgs.librewolf-unwrapped) extraPrefsFiles extraPoliciesFiles; wmClass = "LibreWolf"; libName = "librewolf"; - cfg.enableKeePassXC = true; + nativeMessagingHosts = with pkgs; [ keepassxc ]; }; profiles.chayleaf = { extensions = (with config.nur.repos.rycee.firefox-addons; [ diff --git a/home/common/gui.nix b/home/common/gui.nix index f17f508..4ae4022 100644 --- a/home/common/gui.nix +++ b/home/common/gui.nix @@ -1,4 +1,4 @@ -{ config, pkgs, pkgs2, lib, ... }: +{ config, pkgs, lib, ... }: { imports = [ ./terminal.nix ]; i18n.inputMethod = let fcitx5-qt = pkgs.libsForQt5.fcitx5-qt; in { @@ -180,7 +180,7 @@ # profiles = { }; package = pkgs.wrapMpv ((pkgs.mpv-unwrapped.override { # webp support - ffmpeg_5 = pkgs.ffmpeg-custom; + ffmpeg = pkgs.ffmpeg-custom; }).overrideAttrs (old: { patches = old.patches or [] ++ [ (pkgs.fetchpatch { @@ -251,7 +251,7 @@ keepassxc nheko qbittorrent mumble nextcloud-client gnome.zenity kdeconnect # cli tools - imagemagick ffmpeg_5-full xdg-utils + imagemagick ffmpeg-full xdg-utils # fonts noto-fonts noto-fonts-cjk-sans noto-fonts-cjk-serif noto-fonts-emoji noto-fonts-extra @@ -261,7 +261,7 @@ # for working with nix nix-init - pkgs2.nvfetcher + nvfetcher config.nur.repos.rycee.mozilla-addons-to-nix anki-bin diff --git a/home/hosts/nixmsi.nix b/home/hosts/nixmsi.nix index cadf4ec..23ed59d 100644 --- a/home/hosts/nixmsi.nix +++ b/home/hosts/nixmsi.nix @@ -67,7 +67,7 @@ CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS = "-C link-arg=--ld-path=${pkgs.mold}/bin/mold"; }; home.packages = with pkgs; [ - gimp krita blender-hip + (gimp.overrideAttrs (old: { doCheck = false; })) krita blender-hip kdenlive glaxnimate mediainfo ghidra (cutter.withPlugins (p: with p; [ sigdb rz-ghidra ])) openrgb piper diff --git a/pkgs/_sources/generated.json b/pkgs/_sources/generated.json index bc4d4a8..68ccc66 100644 --- a/pkgs/_sources/generated.json +++ b/pkgs/_sources/generated.json @@ -22,24 +22,24 @@ "pinned": false, "src": { "name": null, - "sha256": "sha256-DcS5ov656f/l1zWPt+UYKxarDGcAWd6zTvi50Lsa1s8=", + "sha256": "sha256-72jxUJdn4j0FV1qFH0r7UEVrAvSwrWgWsxCXyT1N/1A=", "type": "url", - "url": "https://github.com/GloriousEggroll/proton-ge-custom/releases/download/GE-Proton8-16/GE-Proton8-16.tar.gz" + "url": "https://github.com/GloriousEggroll/proton-ge-custom/releases/download/GE-Proton8-24/GE-Proton8-24.tar.gz" }, - "version": "GE-Proton8-16" + "version": "GE-Proton8-24" }, "searxng": { "cargoLocks": null, - "date": "2023-10-06", + "date": "2023-11-14", "extract": null, "name": "searxng", "passthru": null, "pinned": false, "src": { - "sha256": "sha256-/blIZOaeOwQMp6T6GkNh8Fvtzh3Ik5UiPwuGjViENuE=", + "sha256": "sha256-vgDQ7cdWN79TFEbJGq0AdvC8p2YOmogk9iVViDkZDXw=", "type": "tarball", - "url": "https://github.com/searxng/searxng/archive/ce270961e82585971579844c64d7cde5f5d855ec.tar.gz" + "url": "https://github.com/searxng/searxng/archive/b3d29cb86db4cc1a4e6320016529d1361451e1f1.tar.gz" }, - "version": "ce270961e82585971579844c64d7cde5f5d855ec" + "version": "b3d29cb86db4cc1a4e6320016529d1361451e1f1" } -} +} \ No newline at end of file diff --git a/pkgs/_sources/generated.nix b/pkgs/_sources/generated.nix index eee9ac6..bbc0dc7 100644 --- a/pkgs/_sources/generated.nix +++ b/pkgs/_sources/generated.nix @@ -12,19 +12,19 @@ }; proton-ge = { pname = "proton-ge"; - version = "GE-Proton8-16"; + version = "GE-Proton8-24"; src = fetchurl { - url = "https://github.com/GloriousEggroll/proton-ge-custom/releases/download/GE-Proton8-16/GE-Proton8-16.tar.gz"; - sha256 = "sha256-DcS5ov656f/l1zWPt+UYKxarDGcAWd6zTvi50Lsa1s8="; + url = "https://github.com/GloriousEggroll/proton-ge-custom/releases/download/GE-Proton8-24/GE-Proton8-24.tar.gz"; + sha256 = "sha256-72jxUJdn4j0FV1qFH0r7UEVrAvSwrWgWsxCXyT1N/1A="; }; }; searxng = { pname = "searxng"; - version = "ce270961e82585971579844c64d7cde5f5d855ec"; + version = "b3d29cb86db4cc1a4e6320016529d1361451e1f1"; src = fetchTarball { - url = "https://github.com/searxng/searxng/archive/ce270961e82585971579844c64d7cde5f5d855ec.tar.gz"; - sha256 = "sha256-/blIZOaeOwQMp6T6GkNh8Fvtzh3Ik5UiPwuGjViENuE="; + url = "https://github.com/searxng/searxng/archive/b3d29cb86db4cc1a4e6320016529d1361451e1f1.tar.gz"; + sha256 = "sha256-vgDQ7cdWN79TFEbJGq0AdvC8p2YOmogk9iVViDkZDXw="; }; - date = "2023-10-06"; + date = "2023-11-14"; }; } diff --git a/pkgs/certspotter/configurable-sendmail.patch b/pkgs/certspotter/configurable-sendmail.patch deleted file mode 100644 index c895a76..0000000 --- a/pkgs/certspotter/configurable-sendmail.patch +++ /dev/null @@ -1,71 +0,0 @@ -diff --git a/cmd/certspotter/main.go b/cmd/certspotter/main.go -index 9730789..f2eb081 100644 ---- a/cmd/certspotter/main.go -+++ b/cmd/certspotter/main.go -@@ -163,6 +163,7 @@ func main() { - logs string - noSave bool - script string -+ sendmail string - startAtEnd bool - stateDir string - stdout bool -@@ -176,6 +177,7 @@ func main() { - flag.StringVar(&flags.logs, "logs", defaultLogList, "File path or URL of JSON list of logs to monitor") - flag.BoolVar(&flags.noSave, "no_save", false, "Do not save a copy of matching certificates in state directory") - flag.StringVar(&flags.script, "script", "", "Program to execute when a matching certificate is discovered") -+ flag.StringVar(&flags.sendmail, "sendmail", "/usr/sbin/sendmail", "Path to the sendmail-compatible program to use") - flag.BoolVar(&flags.startAtEnd, "start_at_end", false, "Start monitoring logs from the end rather than the beginning (saves considerable bandwidth)") - flag.StringVar(&flags.stateDir, "state_dir", defaultStateDir(), "Directory for storing log position and discovered certificates") - flag.BoolVar(&flags.stdout, "stdout", false, "Write matching certificates to stdout") -@@ -201,6 +203,7 @@ func main() { - Verbose: flags.verbose, - Script: flags.script, - ScriptDir: defaultScriptDir(), -+ SendmailPath: flags.sendmail, - Email: flags.email, - Stdout: flags.stdout, - HealthCheckInterval: flags.healthcheck, -diff --git a/monitor/config.go b/monitor/config.go -index 1e0d60c..d1bc430 100644 ---- a/monitor/config.go -+++ b/monitor/config.go -@@ -20,6 +20,7 @@ type Config struct { - WatchList WatchList - Verbose bool - SaveCerts bool -+ SendmailPath string - Script string - ScriptDir string - Email []string -diff --git a/monitor/notify.go b/monitor/notify.go -index 8fc6d09..86cabca 100644 ---- a/monitor/notify.go -+++ b/monitor/notify.go -@@ -36,7 +36,7 @@ func notify(ctx context.Context, config *Config, notif notification) error { - } - - if len(config.Email) > 0 { -- if err := sendEmail(ctx, config.Email, notif); err != nil { -+ if err := sendEmail(ctx, config.SendmailPath, config.Email, notif); err != nil { - return err - } - } -@@ -62,7 +62,7 @@ func writeToStdout(notif notification) { - os.Stdout.WriteString(notif.Text() + "\n") - } - --func sendEmail(ctx context.Context, to []string, notif notification) error { -+func sendEmail(ctx context.Context, sendmailPath string, to []string, notif notification) error { - stdin := new(bytes.Buffer) - stderr := new(bytes.Buffer) - -@@ -77,7 +77,7 @@ func sendEmail(ctx context.Context, to []string, notif notification) error { - args := []string{"-i", "--"} - args = append(args, to...) - -- sendmail := exec.CommandContext(ctx, "/usr/sbin/sendmail", args...) -+ sendmail := exec.CommandContext(ctx, sendmailPath, args...) - sendmail.Stdin = stdin - sendmail.Stderr = stderr - diff --git a/pkgs/certspotter/default.nix b/pkgs/certspotter/default.nix deleted file mode 100644 index 30904c8..0000000 --- a/pkgs/certspotter/default.nix +++ /dev/null @@ -1,41 +0,0 @@ -{ lib -, buildGoModule -, fetchFromGitHub -, lowdown -}: - -buildGoModule rec { - pname = "certspotter"; - version = "0.16.0"; - - src = fetchFromGitHub { - owner = "SSLMate"; - repo = "certspotter"; - rev = "v${version}"; - hash = "sha256-0+7GWxbV4j2vVdmool8J9hqRqUi8O/yKedCyynWJDkE="; - }; - - vendorHash = "sha256-haYmWc2FWZNFwMhmSy3DAtj9oW5G82dX0fxpGqI8Hbw="; - - patches = [ ./configurable-sendmail.patch ]; - - ldflags = [ "-s" "-w" ]; - - nativeBuildInputs = [ lowdown ]; - - postInstall = '' - cd man - make - mkdir -p $out/share/man/man8 - mv *.8 $out/share/man/man8 - ''; - - meta = with lib; { - description = "Certificate Transparency Log Monitor"; - homepage = "https://github.com/SSLMate/certspotter"; - changelog = "https://github.com/SSLMate/certspotter/blob/${src.rev}/CHANGELOG.md"; - license = licenses.mpl20; - mainProgram = "certspotter"; - maintainers = with maintainers; [ chayleaf ]; - }; -} diff --git a/pkgs/default.nix b/pkgs/default.nix index 86dead0..007d892 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -10,7 +10,7 @@ let sources = import ./_sources/generated.nix { inherit (pkgs) fetchgit fetchurl fetchFromGitHub dockerTools; }; - nixForNixPlugins = pkgs.nixVersions.nix_2_17; + nixForNixPlugins = pkgs.nixVersions.nix_2_18; in { @@ -22,16 +22,16 @@ in unstable = nixForNixPlugins; }); # Various patches to change Nix version of existing packages so they don't error out because of nix-plugins in nix.conf - nix-plugins = pkgs.nix-plugins.override { nix = nixForNixPlugins; }; /*.overrideAttrs (old: { - version = "12.0.0"; + nix-plugins = (pkgs.nix-plugins.override { nix = nixForNixPlugins; }).overrideAttrs (old: { + version = "13.0.0"; patches = [ (pkgs.fetchpatch { - # pull 17 - url = "https://github.com/shlevy/nix-plugins/commit/f7534b96e70ca056ef793918733d1820af89a433.patch"; - hash = "sha256-ePRAnZAobasF6jA3QC73p8zyzayXORuodhus96V+crs="; + # pull 16 + url = "https://github.com/chayleaf/nix-plugins/commit/8f945cadad7f2e60e8f308b2f498ec5e16961ede.patch"; + hash = "sha256-pOogMtjXYkSDtXW12TmBpGr/plnizJtud2nP3q2UldQ="; }) ]; - });*/ + }); harmonia = (pkgs.harmonia.override { nix = nixForNixPlugins; }); /*.overrideAttrs { patches = [ (pkgs.fetchpatch { @@ -48,38 +48,39 @@ in # TODO: remove when https://github.com/NixOS/nix/issues/8796 is fixed or hydra code stops needing a fix configureFlags = builtins.filter (x: x != "--enable-lto") (old.configureFlags or []); });*/ - });/*.overrideAttrs (old: { + }).overrideAttrs (old: { + # who cares about failing tests amirite + doCheck = false; patches = (old.patches or [ ]) ++ [ (pkgs.fetchpatch { - url = "https://github.com/NixOS/hydra/pull/1296/commits/b23431a657d8a9b2f478c95dd81034780751a262.patch"; - hash = "sha256-ruTAIPUrPtfy8JkXYK2qigBrSa6KPXpJlORTNkUYrG0="; + url = "https://github.com/chayleaf/hydra/commit/e9da80fff6234fab2458173272ee0bedbe8935c3.patch"; + hash = "sha256-PS8rwe5lIzvaVlh/DogYmW5OccVfpKQ6JehTQibx2XQ="; }) ]; - });*/ - nurl = pkgs.nurl.override { nix = nixForNixPlugins; }; - nvfetcher = pkgs.nvfetcher.overrideAttrs (old: { - meta = builtins.removeAttrs old.meta [ "broken" ]; }); + nurl = pkgs.nurl.override { nix = nixForNixPlugins; }; + /*nvfetcher = pkgs.nvfetcher.overrideAttrs (old: { + meta = builtins.removeAttrs old.meta [ "broken" ]; + });*/ - certspotter = callPackage ./certspotter { }; clang-tools_latest = pkgs.clang-tools_16; clang_latest = pkgs.clang_16; /*ghidra = pkgs.ghidra.overrideAttrs (old: { patches = old.patches ++ [ ./ghidra-stdcall.patch ]; });*/ - ffmpeg-custom = (pkgs'.ffmpeg_6-full.override { + ffmpeg-custom = (pkgs.callPackage (import /${pkgs.path}/pkgs/development/libraries/ffmpeg/generic.nix { + version = "6.1"; + sha256 = "sha256-NzhD2D16bCVCyCXo0TRwZYp3Ta5eFSfoQPa+iRkeNZg="; + }) { + ffmpegVariant = "full"; withCuda = false; withCudaLLVM = false; withNvdec = false; withNvenc = false; + inherit (pkgs'.darwin.apple_sdk.frameworks) + Cocoa CoreServices CoreAudio CoreMedia AVFoundation MediaToolbox + VideoDecodeAcceleration VideoToolbox; }).overrideAttrs (old: { - version = "unstable-20231031"; - src = pkgs'.fetchgit { - url = "https://git.ffmpeg.org/ffmpeg.git"; - rev = "4e5f3e6b8e1132354eed810dfdadf87f45c5de27"; - hash = "sha256-fiWkU9fK8qPmxl2MOADKdlFf6XjHGKFhi8uaWltphCE="; - }; - patches = [ ]; postPatch = '' ${old.postPatch or ""} substituteInPlace libavutil/hwcontext_vulkan.c \ @@ -88,12 +89,11 @@ in --replace FF_VK_KHR_VIDEO_DECODE_H265 FF_VK_EXT_VIDEO_DECODE_H265 \ --replace FF_VK_KHR_VIDEO_DECODE_AV1 FF_VK_EXT_VIDEO_DECODE_AV1 ''; - buildInputs = old.buildInputs ++ [ pkgs'.libaribcaption ]; + buildInputs = old.buildInputs ++ [ pkgs.libaribcaption ]; configureFlags = old.configureFlags ++ [ "--enable-libaribcaption" ]; }); gimp = callPackage ./gimp { inherit (pkgs) gimp; }; home-daemon = callPackage ./home-daemon { }; - libaribcaption = callPackage ./libaribcaption { }; # pin version looking-glass-client = pkgs.looking-glass-client.overrideAttrs (old: { version = "B6"; @@ -108,7 +108,6 @@ in kvmfrOverlay = kvmfr: kvmfr.overrideAttrs (old: { inherit (pkgs'.looking-glass-client) version src; }); - pineapplebot = callPackage ./pineapplebot.nix { }; proton-ge = pkgs.stdenvNoCC.mkDerivation { inherit (sources.proton-ge) pname version src; installPhase = '' @@ -121,6 +120,7 @@ in searxng = pkgs'.python3.pkgs.toPythonModule (pkgs.searxng.overrideAttrs (old: { inherit (sources.searxng) src; version = "unstable-" + sources.searxng.date; + postInstall = builtins.replaceStrings [ "/botdetection" ] [ "" ] old.postInstall; })); techmino = callPackage ./techmino { }; @@ -153,6 +153,5 @@ in stdenv = pkgs'.ccacheStdenv; }; } -// import ./postgresql-packages { inherit pkgs pkgs' lib sources isOverlay; } // import ./ccache.nix { inherit pkgs pkgs' lib sources; } // import ../system/hardware/bpi-r3/pkgs.nix { inherit pkgs pkgs' lib sources; } diff --git a/pkgs/firefox-addons/generated.nix b/pkgs/firefox-addons/generated.nix index aea8923..347f232 100644 --- a/pkgs/firefox-addons/generated.nix +++ b/pkgs/firefox-addons/generated.nix @@ -63,10 +63,10 @@ }; "youtube-nonstop" = buildFirefoxXpiAddon { pname = "youtube-nonstop"; - version = "0.9.1"; + version = "0.9.2"; addonId = "{0d7cafdd-501c-49ca-8ebb-e3341caaa55e}"; - url = "https://addons.mozilla.org/firefox/downloads/file/3848483/youtube_nonstop-0.9.1.xpi"; - sha256 = "8340d57622a663949ec1768eb37d47651c809fadf0ffaa5ff546c48fdd28e33d"; + url = "https://addons.mozilla.org/firefox/downloads/file/4187690/youtube_nonstop-0.9.2.xpi"; + sha256 = "7659d180f76ea908ea81b84ed9bdd188624eaaa62b88accbe6d8ad4e8caeff38"; meta = with lib; { homepage = "https://github.com/lawfx/YoutubeNonStop"; diff --git a/pkgs/libaribcaption/default.nix b/pkgs/libaribcaption/default.nix deleted file mode 100644 index 354b300..0000000 --- a/pkgs/libaribcaption/default.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ lib -, stdenv -, fetchFromGitHub -, cmake - -, fontconfig -, freetype -}: - -stdenv.mkDerivation rec { - pname = "libaribcaption"; - version = "1.1.1"; - - src = fetchFromGitHub { - owner = "xqq"; - repo = "libaribcaption"; - rev = "v${version}"; - hash = "sha256-x6l0ZrTktSsqfDLVRXpQtUOruhfc8RF3yT991UVZiKA="; - }; - - nativeBuildInputs = [ cmake ]; - - cmakeFlags = [ "-DBUILD_SHARED_LIBS=ON" ]; - - buildInputs = lib.optionals (!stdenv.isDarwin) [ fontconfig freetype ]; - - meta = with lib; { - description = "Portable ARIB STD-B24 Caption Decoder/Renderer"; - homepage = "https://github.com/xqq/libaribcaption"; - license = licenses.mit; - maintainers = with maintainers; [ chayleaf ]; - }; -} diff --git a/pkgs/pineapplebot.nix b/pkgs/pineapplebot.nix deleted file mode 100644 index 66787fd..0000000 --- a/pkgs/pineapplebot.nix +++ /dev/null @@ -1,34 +0,0 @@ -{ python3 -, fetchFromGitHub -, rustPlatform -, magic ? "" -, ... }: - -python3.pkgs.buildPythonPackage rec { - pname = "pineapplebot"; - version = "0.1.0"; - src = fetchFromGitHub { - owner = "chayleaf"; - repo = "pizzabot_v3"; - rev = "master"; - sha256 = "sha256-ZLskMlllZfmqIlbSr0pNHHJehDycohiwqgYbuEYP7Qc="; - }; - preBuild = '' - head -n13 Cargo.toml > Cargo.toml.new - mv Cargo.toml.new Cargo.toml - ''; - sourceRoot = "source/pineapplebot"; - cargoDeps = rustPlatform.fetchCargoTarball { - inherit src sourceRoot; - name = "${pname}-${version}"; - sha256 = "14jxgykwg1apy97gy1j8mz7ny2cqg4q9s03a2bk9kx2y6ibm4668"; - }; - nativeBuildInputs = with rustPlatform; [ - cargoSetupHook - maturinBuildHook - ]; - doCheck = false; - doInstallCheck = true; - pythonImportsCheck = [ "pineapplebot" ]; - PIZZABOT_MAGIC = magic; -} diff --git a/pkgs/postgresql-packages/default.nix b/pkgs/postgresql-packages/default.nix deleted file mode 100644 index cfb188f..0000000 --- a/pkgs/postgresql-packages/default.nix +++ /dev/null @@ -1,45 +0,0 @@ -{ pkgs -, pkgs' -, isOverlay -, lib -, ... }: - -let - inherit (pkgs') callPackage; - - extraPackages = { - tsja = callPackage ./tsja.nix { }; - }; - gen' = postgresql: builtins.mapAttrs (k: v: v.override { inherit postgresql; }) extraPackages; - gen = ver: - lib.optionalAttrs isOverlay pkgs."postgresql${toString ver}Packages" - // gen' pkgs."postgresql${if ver == "" then "" else "_" + toString ver}"; - psql = ver: let - old = pkgs."postgresql${if ver == "" then "" else "_" + toString ver}"; - in old // { pkgs = old.pkgs // gen' old; }; - self = { - mecab = pkgs.mecab.overrideAttrs (old: { - postInstall = '' - mkdir -p $out/lib/mecab/dic - ln -s ${callPackage /${pkgs.path}/pkgs/tools/text/mecab/ipadic.nix { - mecab-nodic = callPackage /${pkgs.path}/pkgs/tools/text/mecab/nodic.nix { }; - }} $out/lib/mecab/dic/ipadic - ''; - }); - postgresqlPackages = gen ""; - postgresql11Packages = gen 11; - postgresql12Packages = gen 12; - postgresql13Packages = gen 13; - postgresql14Packages = gen 14; - postgresql15Packages = gen 15; - postgresql16Packages = gen 16; - } // lib.optionalAttrs isOverlay { - postgresql = psql ""; - postgresql_11 = psql 11; - postgresql_12 = psql 12; - postgresql_13 = psql 13; - postgresql_14 = psql 14; - postgresql_15 = psql 15; - postgresql_16 = psql 16; - }; -in self diff --git a/pkgs/postgresql-packages/tsja.nix b/pkgs/postgresql-packages/tsja.nix deleted file mode 100644 index 8985840..0000000 --- a/pkgs/postgresql-packages/tsja.nix +++ /dev/null @@ -1,39 +0,0 @@ -{ lib -, stdenv -, postgresql -, mecab -}: - -stdenv.mkDerivation rec { - pname = "tsja"; - version = "0.5.0"; - - src = fetchTarball { - url = "https://www.amris.jp/tsja/tsja-${version}.tar.xz"; - sha256 = "0hx4iygnqw1ay3nwrf3x2izflw4ip9i8i0yny26vivdz862m97w7"; - }; - - postPatch = '' - substituteInPlace Makefile \ - --replace /usr/local/pgsql ${postgresql} \ - --replace -L/usr/local/lib "" \ - --replace -I/usr/local/include "" - substituteInPlace tsja.c --replace /usr/local/lib/mecab ${mecab}/lib/mecab - ''; - - buildInputs = [ postgresql mecab ]; - - installPhase = '' - mkdir -p $out/lib $out/share/postgresql/extension - cp libtsja.so $out/lib - cp dbinit_libtsja.txt $out/share/postgresql/extension/libtsja_dbinit.sql - ''; - - meta = with lib; { - description = "PostgreSQL extension implementing Japanese text search"; - homepage = "https://www.amris.jp/tsja/index.html"; - maintainers = with maintainers; [ chayleaf ]; - platforms = postgresql.meta.platforms; - license = licenses.postgresql; - }; -} diff --git a/pkgs/rizin/rz-ghidra.nix b/pkgs/rizin/rz-ghidra.nix deleted file mode 100644 index b3b813e..0000000 --- a/pkgs/rizin/rz-ghidra.nix +++ /dev/null @@ -1,54 +0,0 @@ -{ lib -, stdenv -, fetchFromGitHub -, cmake -# buildInputs -, rizin -, openssl -, pugixml -# optional buildInputs -, enableCutterPlugin ? true -, cutter -, qtbase -, qtsvg -}: - -stdenv.mkDerivation rec { - pname = "rz-ghidra"; - version = "0.5.0"; - - src = fetchFromGitHub { - owner = "rizinorg"; - repo = "rz-ghidra"; - rev = "v${version}"; - hash = "sha256-2QQEj4TIBmiZgbb66R7q6iEp2WitUc8Ui6Nr71JelXs="; - fetchSubmodules = true; - }; - - nativeBuildInputs = [ cmake ]; - buildInputs = [ - openssl - pugixml - rizin - ] ++ lib.optionals enableCutterPlugin [ - cutter - qtbase - qtsvg - ]; - - dontWrapQtApps = true; - - cmakeFlags = [ - "-DUSE_SYSTEM_PUGIXML=ON" - ] ++ lib.optionals enableCutterPlugin [ - "-DBUILD_CUTTER_PLUGIN=ON" - "-DCUTTER_INSTALL_PLUGDIR=share/rizin/cutter/plugins/native" - ]; - - meta = with lib; { - description = "Deep ghidra decompiler and sleigh disassembler integration for rizin"; - homepage = src.meta.homepage; - license = licenses.lgpl3; - maintainers = with maintainers; [ chayleaf ]; - }; -} diff --git a/pkgs/rizin/wrapper.nix b/pkgs/rizin/wrapper.nix deleted file mode 100644 index 575f11e..0000000 --- a/pkgs/rizin/wrapper.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ makeWrapper -, symlinkJoin -, unwrapped -}: - -plugins: - -symlinkJoin { - name = "cutter-with-plugins"; - - paths = [ unwrapped ] ++ plugins; - - nativeBuildInputs = [ makeWrapper ]; - - passthru = { - inherit unwrapped; - }; - - postBuild = '' - rm $out/bin/* - wrapperArgs=(--set RZ_LIBR_PLUGINS $out/lib/rizin/plugins) - if [ -d $out/share/rizin/cutter ]; then - wrapperArgs+=(--prefix XDG_DATA_DIRS : $out/share) - fi - for binary in $(ls ${unwrapped}/bin); do - makeWrapper ${unwrapped}/bin/$binary $out/bin/$binary "''${wrapperArgs[@]}" - done - ''; -} diff --git a/system/devices/radxa-rock5a-server.nix b/system/devices/radxa-rock5a-server.nix index e2ac8a5..9dde489 100644 --- a/system/devices/radxa-rock5a-server.nix +++ b/system/devices/radxa-rock5a-server.nix @@ -59,6 +59,8 @@ in }; }; + boot.supportedFilesystems = [ "bcachefs" ]; + fileSystems = let device = rootPart; fsType = "btrfs"; diff --git a/system/hardware/bpi-r3/default.nix b/system/hardware/bpi-r3/default.nix index 2d8772c..0e30100 100644 --- a/system/hardware/bpi-r3/default.nix +++ b/system/hardware/bpi-r3/default.nix @@ -9,7 +9,7 @@ }; #boot.kernelPackages = config._module.args.fromSourcePkgs.linuxPackages_bpiR3_ccache or pkgs.linuxPackages_bpiR3_ccache; - boot.kernelPackages = config._module.args.fromSourcePkgs.linuxPackages_bpiR3 or pkgs.linuxPackages_bpiR3; + boot.kernelPackages = pkgs.linuxPackagesFor (pkgs.buildLinuxWithCcache (config._module.args.fromSourcePkgs.linux_bpiR3 or pkgs.linux_bpiR3)); hardware.deviceTree.enable = true; hardware.deviceTree.filter = "mt7986a-bananapi-bpi-r3.dtb"; diff --git a/system/hardware/msi-delta-15/default.nix b/system/hardware/msi-delta-15/default.nix index 77d0ab7..cd5977c 100644 --- a/system/hardware/msi-delta-15/default.nix +++ b/system/hardware/msi-delta-15/default.nix @@ -28,10 +28,8 @@ (final: prev: { amd-ucode = prev.amd-ucode.override { inherit (final) linux-firmware; }; linux-firmware = prev.stdenvNoCC.mkDerivation { - inherit (prev.linux-firmware) pname version meta src; - dontFixup = true; + inherit (prev.linux-firmware) pname version meta src dontFixup installFlags nativeBuildInputs; passthru = { inherit (prev.linux-firmware) version; }; - installFlags = [ "DESTDIR=$(out)" ]; # revert microcode updates which break boot for me patches = [ @@ -58,10 +56,8 @@ (final: prev: { amd-ucode = prev.amd-ucode.override { inherit (final) linux-firmware; }; linux-firmware = prev.stdenvNoCC.mkDerivation { - inherit (prev.linux-firmware) pname version meta src; - dontFixup = true; + inherit (prev.linux-firmware) pname version meta src dontFixup installFlags nativeBuildInputs; passthru = { inherit (prev.linux-firmware) version; }; - installFlags = [ "DESTDIR=$(out)" ]; patches = [ ]; postPatch = ""; }; diff --git a/system/hardware/msi-delta-15/revert-amd-ucode-update-fam19h.patch b/system/hardware/msi-delta-15/revert-amd-ucode-update-fam19h.patch index 698c50a..f26dd2a 100644 --- a/system/hardware/msi-delta-15/revert-amd-ucode-update-fam19h.patch +++ b/system/hardware/msi-delta-15/revert-amd-ucode-update-fam19h.patch @@ -6,7 +6,7 @@ index dbcdced..dd7b8d5 100644 RawFile: amd-ucode/microcode_amd_fam17h.bin Version: 2023-07-19 RawFile: amd-ucode/microcode_amd_fam19h.bin --Version: 2023-08-08 +-Version: 2023-10-19 +Version: 2023-07-18 File: amd-ucode/README @@ -19,13 +19,13 @@ index f47743c..6a9ff1e 100644 Family=0x17 Model=0x01 Stepping=0x02: Patch=0x0800126e Length=3200 bytes Microcode patches in microcode_amd_fam19h.bin: -- Family=0x19 Model=0x11 Stepping=0x01: Patch=0x0a10113e Length=5568 bytes -- Family=0x19 Model=0x11 Stepping=0x02: Patch=0x0a10123e Length=5568 bytes -- Family=0x19 Model=0xa0 Stepping=0x02: Patch=0x0aa00212 Length=5568 bytes +- Family=0x19 Model=0x11 Stepping=0x02: Patch=0x0a101244 Length=5568 bytes Family=0x19 Model=0x01 Stepping=0x01: Patch=0x0a0011d1 Length=5568 bytes Family=0x19 Model=0x01 Stepping=0x00: Patch=0x0a001079 Length=5568 bytes +- Family=0x19 Model=0xa0 Stepping=0x02: Patch=0x0aa00213 Length=5568 bytes Family=0x19 Model=0x01 Stepping=0x02: Patch=0x0a001234 Length=5568 bytes - Family=0x19 Model=0xa0 Stepping=0x01: Patch=0x0aa00116 Length=5568 bytes +- Family=0x19 Model=0x11 Stepping=0x01: Patch=0x0a101144 Length=5568 bytes - -NOTE: For Genoa (Family=0x19 Model=0x11) and Bergamo (Family=0x19 Model=0xa0), -either AGESA version >= 1.0.0.8 OR a kernel with the following commit is @@ -45,14 +45,14 @@ index 8cff901..a32b4d6 100644 @@ -1,11 +1,11 @@ -----BEGIN PGP SIGNATURE----- --iQEzBAABCgAdFiEE/HxsUF2vzBRxg1fK5L5TOfMornMFAmTEYrcACgkQ5L5TOfMo --rnN4IQf/QKbOezXZ4OYzaPANvsZQEAzLNfuylC/aQMwrPaO7daz5/zmCN4HU5XkH --dDT8DYfPg+fQHIgxAw0/L24xPOm5Op/QuLVDyDqVr4qvL8+65eeI+JqxD/wXMXYN --V34kkLM2p8iuyY1Nc8IDLXu4X75KGNPbKZlMRKMU3Pr7ai5O4ihmiAM+N6qv1KEJ --YToNN6vrg0qt1cv0SLM8sa4e7L1+oblUrg/o0FViYE8pxsU3ZRRVSJMUg+lKjvl/ --1ZPGKOdD80fcNJ+ItYGHNNs3eCc3WgW7Kc/E668eH75Yu9Zt7ewWZX8Sg/mygleY --OzMwhbPJg4bF4zm7C/Pku7i1T2Omcg== --=km2X +-iQEzBAABCgAdFiEE/HxsUF2vzBRxg1fK5L5TOfMornMFAmUoW6AACgkQ5L5TOfMo +-rnMHAAf/SxaKEu5l7FGXR+QJYc2oSJDpf9ZsHTkVnxqF1I3ReItEGAR3iqSWrsRw +-KA4niP9Ihr8EqwhOaOtqkRKKF9D5yg+DksnRWbh2VTUECO4KQxjHNrPp3JWEzBwb +-Xn+vRVP02ZRi3u4MCYbnDC4AfUSnKnldY3TTlNi/6HUaGS2pcw8Vjli/C06zwfgh +-WwUAoFMQl4SDJhbGfC9cb93MKjBl/0Hv4uhK5W8fJ1iUkMvY8Ijna/oDTZCNPqP0 +-0AgOwdAdzoyOYWjbUXcwofz2Umpz12xmJW8yXNwdv1pmaCvv9aCJz1L49lGwFH9E +-lhhoFQ1SQL3hhPjTXO6DbeeT9+fjOg== +-=9Xav +iQEzBAABCgAdFiEE/HxsUF2vzBRxg1fK5L5TOfMornMFAmS3F00ACgkQ5L5TOfMo +rnNEhQgAizSV8IFpvaYNytaJKLA4uevrZneGPV4czjCXnnj1yHpfQmCTyZQnoLnx +7gyzf7K5271zO51FBQ5z2Nm48a3XPUhMbQLNP4BZdekLiA3bRpMtSyHct6zD0ULm diff --git a/system/hardware/radxa-rock5a/default.nix b/system/hardware/radxa-rock5a/default.nix index 21edda0..bbb7394 100644 --- a/system/hardware/radxa-rock5a/default.nix +++ b/system/hardware/radxa-rock5a/default.nix @@ -5,20 +5,14 @@ { boot.initrd.availableKernelModules = [ "ahci" "usbhid" "usb_storage" ]; - # TODO: switch to upstream when PCIe support works - # boot.kernelPackages = pkgs.linuxPackages_testing; - boot.kernelPackages = pkgs.linuxPackagesFor (pkgs.buildLinux { - version = "6.6.0-rc1"; - kernelPatches = [ ]; - src = pkgs.fetchFromGitLab { - domain = "gitlab.collabora.com"; - group = "hardware-enablement"; - owner = "rockchip-3588"; - repo = "linux"; - rev = "f04271158aee35d270748301c5077231a75bc589"; - hash = "sha256-B85162plbt92p51f/M82y2zOg3/TqrBWqgw80ksJVGc="; - }; - }); + # TODO: switch to mainline when PCIe support works + boot.kernelPackages = pkgs.linuxPackagesFor (pkgs.buildLinuxWithCcache pkgs.linux_testing); + boot.kernelPatches = [ + { + name = "linux_6.7.patch"; + patch = ./linux_6.7.patch; + } + ]; boot.kernelParams = [ "dtb=/${config.hardware.deviceTree.name}" ]; hardware.deviceTree.enable = true; diff --git a/system/hardware/radxa-rock5a/linux_6.7.patch b/system/hardware/radxa-rock5a/linux_6.7.patch new file mode 100644 index 0000000..4094026 --- /dev/null +++ b/system/hardware/radxa-rock5a/linux_6.7.patch @@ -0,0 +1,17516 @@ +diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml +new file mode 100644 +index 000000000..dcca84d57 +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml +@@ -0,0 +1,166 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/phy/phy-rockchip-usbdp.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Rockchip USBDP Combo PHY with Samsung IP block ++ ++maintainers: ++ - Frank Wang ++ - Zhang Yubing ++ ++properties: ++ compatible: ++ enum: ++ - rockchip,rk3588-usbdp-phy ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ maxItems: 4 ++ ++ clock-names: ++ items: ++ - const: refclk ++ - const: immortal ++ - const: pclk ++ - const: utmi ++ ++ resets: ++ maxItems: 5 ++ ++ reset-names: ++ items: ++ - const: init ++ - const: cmn ++ - const: lane ++ - const: pcs_apb ++ - const: pma_apb ++ ++ rockchip,dp-lane-mux: ++ $ref: /schemas/types.yaml#/definitions/uint32-array ++ minItems: 2 ++ maxItems: 4 ++ description: ++ An array of physical Tyep-C lanes indexes. Position of an entry determines ++ the dp lane index, while the value of an entry indicater physical Type-C lane. ++ The support dp lanes number are 2 or 4. e.g. for 2 lanes dp lanes map, we could ++ have "rockchip,dp-lane-mux = <2, 3>;", assuming dp lane0 on Type-C phy lane2, ++ dp lane1 on Type-C phy lane3. For 4 lanes dp lanes map, we could have ++ "rockchip,dp-lane-mux = <0, 1, 2, 3>;", assuming dp lane0 on Type-C phy lane0, ++ dp lane1 on Type-C phy lane1, dp lane2 on Type-C phy lane2, dp lane3 on Type-C ++ phy lane3. If dp lane map by DisplayPort Alt mode, this property is not need. ++ ++ rockchip,u2phy-grf: ++ $ref: /schemas/types.yaml#/definitions/phandle ++ description: ++ Phandle to the syscon managing the 'usb2 phy general register files'. ++ ++ rockchip,usb-grf: ++ $ref: /schemas/types.yaml#/definitions/phandle ++ description: ++ Phandle to the syscon managing the 'usb general register files'. ++ ++ rockchip,usbdpphy-grf: ++ $ref: /schemas/types.yaml#/definitions/phandle ++ description: ++ Phandle to the syscon managing the 'usbdp phy general register files'. ++ ++ rockchip,vo-grf: ++ $ref: /schemas/types.yaml#/definitions/phandle ++ description: ++ Phandle to the syscon managing the 'video output general register files'. ++ When select the dp lane mapping will request its phandle. ++ ++ sbu1-dc-gpios: ++ description: ++ GPIO connected to the SBU1 line of the USB-C connector via a big resistor ++ (~100K) to apply a DC offset for signalling the connector orientation. ++ ++ sbu2-dc-gpios: ++ description: ++ GPIO connected to the SBU2 line of the USB-C connector via a big resistor ++ (~100K) to apply a DC offset for signalling the connector orientation. ++ ++ orientation-switch: ++ description: Flag the port as possible handler of orientation switching ++ type: boolean ++ ++ mode-switch: ++ description: Flag the port as possible handle of altmode switching ++ type: boolean ++ ++ dp-port: ++ type: object ++ additionalProperties: false ++ ++ properties: ++ "#phy-cells": ++ const: 0 ++ ++ required: ++ - "#phy-cells" ++ ++ usb3-port: ++ type: object ++ additionalProperties: false ++ ++ properties: ++ "#phy-cells": ++ const: 0 ++ ++ required: ++ - "#phy-cells" ++ ++ port: ++ $ref: /schemas/graph.yaml#/properties/port ++ description: ++ A port node to link the PHY to a TypeC controller for the purpose of ++ handling orientation switching. ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - clock-names ++ - resets ++ - reset-names ++ - dp-port ++ - usb3-port ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ ++ usbdp_phy0: phy@fed80000 { ++ compatible = "rockchip,rk3588-usbdp-phy"; ++ reg = <0x0 0xfed80000 0x0 0x10000>; ++ rockchip,u2phy-grf = <&usb2phy0_grf>; ++ rockchip,usb-grf = <&usb_grf>; ++ rockchip,usbdpphy-grf = <&usbdpphy0_grf>; ++ rockchip,vo-grf = <&vo0_grf>; ++ clocks = <&cru CLK_USBDPPHY_MIPIDCPPHY_REF>, ++ <&cru CLK_USBDP_PHY0_IMMORTAL>, ++ <&cru PCLK_USBDPPHY0>; ++ clock-names = "refclk", "immortal", "pclk"; ++ resets = <&cru SRST_USBDP_COMBO_PHY0_INIT>, ++ <&cru SRST_USBDP_COMBO_PHY0_CMN>, ++ <&cru SRST_USBDP_COMBO_PHY0_LANE>, ++ <&cru SRST_USBDP_COMBO_PHY0_PCS>, ++ <&cru SRST_P_USBDPPHY0>; ++ reset-names = "init", "cmn", "lane", "pcs_apb", "pma_apb"; ++ status = "disabled"; ++ ++ usbdp_phy0_dp: dp-port { ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ usbdp_phy0_u3: usb3-port { ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; +diff --git a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml +index e4fa6a07b..ce1fd5b0d 100644 +--- a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml ++++ b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml +@@ -28,6 +28,9 @@ properties: + - rockchip,rk3588-sys-grf + - rockchip,rk3588-pcie3-phy-grf + - rockchip,rk3588-pcie3-pipe-grf ++ - rockchip,rk3588-usb-grf ++ - rockchip,rk3588-usbdpphy-grf ++ - rockchip,rk3588-vo-grf + - rockchip,rv1108-usbgrf + - const: syscon + - items: +@@ -64,6 +67,9 @@ properties: + reg: + maxItems: 1 + ++ clocks: ++ maxItems: 1 ++ + "#address-cells": + const: 1 + +@@ -245,6 +251,22 @@ allOf: + + unevaluatedProperties: false + ++ - if: ++ properties: ++ compatible: ++ contains: ++ enum: ++ - rockchip,rk3588-vo-grf ++ ++ then: ++ required: ++ - clocks ++ ++ else: ++ properties: ++ clocks: false ++ ++ + examples: + - | + #include +diff --git a/Documentation/devicetree/bindings/sound/es8328.txt b/Documentation/devicetree/bindings/sound/es8328.txt +deleted file mode 100644 +index 33fbf058c..000000000 +--- a/Documentation/devicetree/bindings/sound/es8328.txt ++++ /dev/null +@@ -1,38 +0,0 @@ +-Everest ES8328 audio CODEC +- +-This device supports both I2C and SPI. +- +-Required properties: +- +- - compatible : Should be "everest,es8328" or "everest,es8388" +- - DVDD-supply : Regulator providing digital core supply voltage 1.8 - 3.6V +- - AVDD-supply : Regulator providing analog supply voltage 3.3V +- - PVDD-supply : Regulator providing digital IO supply voltage 1.8 - 3.6V +- - IPVDD-supply : Regulator providing analog output voltage 3.3V +- - clocks : A 22.5792 or 11.2896 MHz clock +- - reg : the I2C address of the device for I2C, the chip select number for SPI +- +-Pins on the device (for linking into audio routes): +- +- * LOUT1 +- * LOUT2 +- * ROUT1 +- * ROUT2 +- * LINPUT1 +- * RINPUT1 +- * LINPUT2 +- * RINPUT2 +- * Mic Bias +- +- +-Example: +- +-codec: es8328@11 { +- compatible = "everest,es8328"; +- DVDD-supply = <®_3p3v>; +- AVDD-supply = <®_3p3v>; +- PVDD-supply = <®_3p3v>; +- HPVDD-supply = <®_3p3v>; +- clocks = <&clks 169>; +- reg = <0x11>; +-}; +diff --git a/Documentation/devicetree/bindings/sound/everest,es8328.yaml b/Documentation/devicetree/bindings/sound/everest,es8328.yaml +new file mode 100644 +index 000000000..a0f4670fa +--- /dev/null ++++ b/Documentation/devicetree/bindings/sound/everest,es8328.yaml +@@ -0,0 +1,77 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/sound/everest,es8328.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Everest ES8328 audio CODEC ++ ++description: ++ Everest Audio Codec, which can be connected via I2C or SPI. ++ Pins on the device (for linking into audio routes) are ++ * LOUT1 ++ * LOUT2 ++ * ROUT1 ++ * ROUT2 ++ * LINPUT1 ++ * RINPUT1 ++ * LINPUT2 ++ * RINPUT2 ++ * Mic Bias ++ ++maintainers: ++ - David Yang ++ ++properties: ++ compatible: ++ enum: ++ - everest,es8328 ++ - everest,es8388 ++ ++ reg: ++ maxItems: 1 ++ ++ "#sound-dai-cells": ++ const: 0 ++ ++ clocks: ++ items: ++ - description: A 22.5792 or 11.2896 MHz clock ++ ++ DVDD-supply: ++ description: Regulator providing digital core supply voltage 1.8 - 3.6V ++ ++ AVDD-supply: ++ description: Regulator providing analog supply voltage 3.3V ++ ++ PVDD-supply: ++ description: Regulator providing digital IO supply voltage 1.8 - 3.6V ++ ++ HPVDD-supply: ++ description: Regulator providing analog output voltage 3.3V ++ ++required: ++ - compatible ++ - clocks ++ - DVDD-supply ++ - AVDD-supply ++ - PVDD-supply ++ - HPVDD-supply ++ ++additionalProperties: false ++ ++examples: ++ - | ++ i2c { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ es8328: codec@11 { ++ compatible = "everest,es8328"; ++ reg = <0x11>; ++ AVDD-supply = <®_3p3v>; ++ DVDD-supply = <®_3p3v>; ++ HPVDD-supply = <®_3p3v>; ++ PVDD-supply = <®_3p3v>; ++ clocks = <&clks 169>; ++ }; ++ }; +diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts +index b9d789d57..2ef5c98c4 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include "rk3588.dtsi" + + / { +@@ -56,12 +57,78 @@ button-escape { + }; + }; + ++ analog-sound { ++ compatible = "simple-audio-card"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hp_detect>; ++ simple-audio-card,name = "RK3588 EVB1 Audio"; ++ simple-audio-card,aux-devs = <&_headphone>, <&_speaker>; ++ simple-audio-card,bitclock-master = <&masterdai>; ++ simple-audio-card,format = "i2s"; ++ simple-audio-card,frame-master = <&masterdai>; ++ simple-audio-card,hp-det-gpio = <&gpio1 RK_PD5 GPIO_ACTIVE_LOW>; ++ simple-audio-card,mclk-fs = <256>; ++ simple-audio-card,pin-switches = "Headphones", "Speaker"; ++ simple-audio-card,routing = ++ "Speaker Amplifier INL", "LOUT2", ++ "Speaker Amplifier INR", "ROUT2", ++ "Speaker", "Speaker Amplifier OUTL", ++ "Speaker", "Speaker Amplifier OUTR", ++ "Headphones Amplifier INL", "LOUT1", ++ "Headphones Amplifier INR", "ROUT1", ++ "Headphones", "Headphones Amplifier OUTL", ++ "Headphones", "Headphones Amplifier OUTR", ++ "LINPUT1", "Onboard Microphone", ++ "RINPUT1", "Onboard Microphone", ++ "LINPUT2", "Microphone Jack", ++ "RINPUT2", "Microphone Jack"; ++ simple-audio-card,widgets = ++ "Microphone", "Microphone Jack", ++ "Microphone", "Onboard Microphone", ++ "Headphone", "Headphones", ++ "Speaker", "Speaker"; ++ ++ simple-audio-card,cpu { ++ sound-dai = <&i2s0_8ch>; ++ }; ++ ++ masterdai: simple-audio-card,codec { ++ sound-dai = <&es8388>; ++ system-clock-frequency = <12288000>; ++ }; ++ }; ++ ++ amp_headphone: headphone-amplifier { ++ compatible = "simple-audio-amplifier"; ++ enable-gpios = <&gpio1 RK_PD2 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&headphone_amplifier_en>; ++ sound-name-prefix = "Headphones Amplifier"; ++ }; ++ ++ amp_speaker: speaker-amplifier { ++ compatible = "simple-audio-amplifier"; ++ enable-gpios = <&gpio1 RK_PD3 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&speaker_amplifier_en>; ++ sound-name-prefix = "Speaker Amplifier"; ++ }; ++ + backlight: backlight { + compatible = "pwm-backlight"; + power-supply = <&vcc12v_dcin>; + pwms = <&pwm2 0 25000 0>; + }; + ++ wlan-rfkill { ++ compatible = "rfkill-gpio"; ++ label = "rfkill-pcie-wlan"; ++ radio-type = "wlan"; ++ shutdown-gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_LOW>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&wifi_pwren>, <&wifi_host_wake_irq>; ++ }; ++ + pcie20_avdd0v85: pcie20-avdd0v85-regulator { + compatible = "regulator-fixed"; + regulator-name = "pcie20_avdd0v85"; +@@ -167,46 +234,75 @@ vcc5v0_usb: vcc5v0-usb-regulator { + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc5v0_usbdcin>; + }; ++ ++ vbus5v0_typec: vbus5v0-typec { ++ compatible = "regulator-fixed"; ++ regulator-name = "vbus5v0_typec"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ enable-active-high; ++ gpio = <&gpio4 RK_PD0 GPIO_ACTIVE_HIGH>; ++ vin-supply = <&vcc5v0_usb>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&typec5v_pwren>; ++ }; + }; + + &combphy0_ps { + status = "okay"; + }; + ++&combphy1_ps { ++ status = "okay"; ++}; ++ + &combphy2_psu { + status = "okay"; + }; + + &cpu_b0 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_mem_s0>; + }; + + &cpu_b1 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_mem_s0>; + }; + + &cpu_b2 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_mem_s0>; + }; + + &cpu_b3 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_mem_s0>; + }; + + &cpu_l0 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l1 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l2 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l3 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; ++}; ++ ++&display_subsystem { ++ clocks = <&hdptxphy_hdmi_clk0>; ++ clock-names = "hdmi0_phy_pll"; + }; + + &gmac0 { +@@ -227,6 +323,56 @@ &gmac0_rgmii_clk + &i2c2 { + status = "okay"; + ++ usbc0: usb-typec@22 { ++ compatible = "fcs,fusb302"; ++ reg = <0x22>; ++ interrupt-parent = <&gpio3>; ++ interrupts = ; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usbc0_int>; ++ vbus-supply = <&vbus5v0_typec>; ++ status = "okay"; ++ ++ usb_con: connector { ++ compatible = "usb-c-connector"; ++ label = "USB-C"; ++ data-role = "dual"; ++ power-role = "dual"; ++ try-power-role = "sink"; ++ op-sink-microwatt = <1000000>; ++ sink-pdos = ++ ; ++ source-pdos = ++ ; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ usbc0_orien_sw: endpoint { ++ remote-endpoint = <&usbdp_phy0_orientation_switch>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ usbc0_role_sw: endpoint { ++ remote-endpoint = <&dwc3_0_role_switch>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ dp_altmode_mux: endpoint { ++ remote-endpoint = <&usbdp_phy0_dp_altmode_mux>; ++ }; ++ }; ++ }; ++ }; ++ }; ++ + hym8563: rtc@51 { + compatible = "haoyu,hym8563"; + reg = <0x51>; +@@ -240,6 +386,32 @@ hym8563: rtc@51 { + }; + }; + ++&i2c7 { ++ status = "okay"; ++ ++ es8388: audio-codec@11 { ++ compatible = "everest,es8388"; ++ reg = <0x11>; ++ clocks = <&cru I2S0_8CH_MCLKOUT>; ++ assigned-clocks = <&cru I2S0_8CH_MCLKOUT>; ++ assigned-clock-rates = <12288000>; ++ AVDD-supply = <&avcc_1v8_codec_s0>; ++ DVDD-supply = <&avcc_1v8_codec_s0>; ++ HPVDD-supply = <&vcc_3v3_s0>; ++ PVDD-supply = <&vcc_3v3_s0>; ++ #sound-dai-cells = <0>; ++ }; ++}; ++ ++&i2s0_8ch { ++ pinctrl-0 = <&i2s0_lrck ++ &i2s0_mclk ++ &i2s0_sclk ++ &i2s0_sdi0 ++ &i2s0_sdo0>; ++ status = "okay"; ++}; ++ + &mdio0 { + rgmii_phy: ethernet-phy@1 { + /* RTL8211F */ +@@ -253,6 +425,12 @@ rgmii_phy: ethernet-phy@1 { + }; + }; + ++&pcie2x1l0 { ++ reset-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_HIGH>; ++ pinctrl-0 = <&pcie2_0_rst>; ++ status = "okay"; ++}; ++ + &pcie2x1l1 { + reset-gpios = <&gpio4 RK_PA2 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; +@@ -273,6 +451,20 @@ &pcie3x4 { + }; + + &pinctrl { ++ audio { ++ hp_detect: headphone-detect { ++ rockchip,pins = <1 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ headphone_amplifier_en: headphone-amplifier-en { ++ rockchip,pins = <1 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ speaker_amplifier_en: speaker-amplifier-en { ++ rockchip,pins = <1 RK_PD3 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ + rtl8111 { + rtl8111_isolate: rtl8111-isolate { + rockchip,pins = <1 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>; +@@ -293,6 +485,10 @@ hym8563_int: hym8563-int { + }; + + pcie2 { ++ pcie2_0_rst: pcie2-0-rst { ++ rockchip,pins = <4 RK_PA5 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ + pcie2_1_rst: pcie2-1-rst { + rockchip,pins = <4 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; + }; +@@ -313,6 +509,26 @@ vcc5v0_host_en: vcc5v0-host-en { + rockchip,pins = <4 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; ++ ++ usb-typec { ++ usbc0_int: usbc0-int { ++ rockchip,pins = <3 RK_PB4 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ ++ typec5v_pwren: typec5v-pwren { ++ rockchip,pins = <4 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ wlan { ++ wifi_host_wake_irq: wifi-host-wake-irq { ++ rockchip,pins = <3 RK_PA7 RK_FUNC_GPIO &pcfg_pull_down>; ++ }; ++ ++ wifi_pwren: wifi-pwren { ++ rockchip,pins = <3 RK_PB1 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ }; + }; + + &pwm2 { +@@ -943,6 +1159,22 @@ &sata0 { + status = "okay"; + }; + ++&u2phy0 { ++ status = "okay"; ++}; ++ ++&u2phy0_otg { ++ status = "okay"; ++}; ++ ++&u2phy1 { ++ status = "okay"; ++}; ++ ++&u2phy1_otg { ++ status = "okay"; ++}; ++ + &u2phy2 { + status = "okay"; + }; +@@ -981,3 +1213,82 @@ &usb_host1_ehci { + &usb_host1_ohci { + status = "okay"; + }; ++ ++&usbdp_phy0 { ++ orientation-switch; ++ mode-switch; ++ sbu1-dc-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>; ++ sbu2-dc-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_HIGH>; ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ usbdp_phy0_orientation_switch: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&usbc0_orien_sw>; ++ }; ++ ++ usbdp_phy0_dp_altmode_mux: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&dp_altmode_mux>; ++ }; ++ }; ++}; ++ ++&usbdp_phy0_u3 { ++ status = "okay"; ++}; ++ ++&usbdp_phy1 { ++ rockchip,dp-lane-mux = <2 3>; ++ status = "okay"; ++}; ++ ++&usbdp_phy1_u3 { ++ status = "okay"; ++}; ++ ++&usb_host0_xhci { ++ dr_mode = "otg"; ++ usb-role-switch; ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ dwc3_0_role_switch: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&usbc0_role_sw>; ++ }; ++ }; ++}; ++ ++&usb_host1_xhci { ++ status = "okay"; ++}; ++ ++&hdmi0 { ++ status = "okay"; ++}; ++ ++&hdmi0_in_vp0 { ++ status = "okay"; ++}; ++ ++&hdptxphy_hdmi0 { ++ status = "okay"; ++}; ++ ++&hdptxphy_hdmi_clk0 { ++ status = "okay"; ++}; ++ ++&vop_mmu { ++ status = "okay"; ++}; ++ ++&vop { ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +index 741f631db..dacf6a4d8 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +@@ -4,6 +4,7 @@ + + #include + #include ++#include + #include "rk3588.dtsi" + + / { +@@ -59,6 +60,15 @@ fan: pwm-fan { + #cooling-cells = <2>; + }; + ++ vcc12v_dcin: vcc12v-dcin-regulator { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc12v_dcin"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <12000000>; ++ regulator-max-microvolt = <12000000>; ++ }; ++ + vcc3v3_pcie2x1l0: vcc3v3-pcie2x1l0-regulator { + compatible = "regulator-fixed"; + enable-active-high; +@@ -117,6 +127,7 @@ vcc5v0_sys: vcc5v0-sys-regulator { + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; ++ vin-supply = <&vcc12v_dcin>; + }; + + vcc_1v1_nldo_s3: vcc-1v1-nldo-s3-regulator { +@@ -138,36 +149,53 @@ &combphy1_ps { + status = "okay"; + }; + ++&combphy2_psu { ++ status = "okay"; ++}; ++ + &cpu_b0 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_s0>; + }; + + &cpu_b1 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_s0>; + }; + + &cpu_b2 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_s0>; + }; + + &cpu_b3 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_s0>; + }; + + &cpu_l0 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l1 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l2 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l3 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; ++}; ++ ++&display_subsystem { ++ clocks = <&hdptxphy_hdmi_clk0>; ++ clock-names = "hdmi0_phy_pll"; + }; + + &i2c0 { +@@ -210,6 +238,61 @@ regulator-state-mem { + }; + }; + ++&i2c4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c4m1_xfer>; ++ status = "okay"; ++ ++ usbc0: usb-typec@22 { ++ compatible = "fcs,fusb302"; ++ reg = <0x22>; ++ interrupt-parent = <&gpio3>; ++ interrupts = ; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usbc0_int>; ++ vbus-supply = <&vcc12v_dcin>; ++ status = "okay"; ++ ++ usb_con: connector { ++ compatible = "usb-c-connector"; ++ label = "USB-C"; ++ data-role = "dual"; ++ power-role = "sink"; ++ try-power-role = "sink"; ++ op-sink-microwatt = <1000000>; ++ sink-pdos = ++ , ++ ; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ usbc0_hs: endpoint { ++ remote-endpoint = <&usb_host0_xhci_drd_sw>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ usbc0_ss: endpoint { ++ remote-endpoint = <&usbdp_phy0_typec_ss>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ usbc0_sbu: endpoint { ++ remote-endpoint = <&usbdp_phy0_typec_sbu>; ++ }; ++ }; ++ }; ++ }; ++ }; ++}; ++ + &i2c6 { + status = "okay"; + +@@ -339,6 +422,10 @@ usb { + vcc5v0_host_en: vcc5v0-host-en { + rockchip,pins = <4 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; + }; ++ ++ usbc0_int: usbc0-int { ++ rockchip,pins = <3 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; + }; + }; + +@@ -731,6 +818,22 @@ &uart2 { + status = "okay"; + }; + ++&u2phy0 { ++ status = "okay"; ++}; ++ ++&u2phy0_otg { ++ status = "okay"; ++}; ++ ++&u2phy1 { ++ status = "okay"; ++}; ++ ++&u2phy1_otg { ++ status = "okay"; ++}; ++ + &u2phy2 { + status = "okay"; + }; +@@ -750,6 +853,41 @@ &u2phy3_host { + status = "okay"; + }; + ++&usbdp_phy1 { ++ status = "okay"; ++}; ++ ++&usbdp_phy1_u3 { ++ status = "okay"; ++}; ++ ++&usbdp_phy0 { ++ orientation-switch; ++ mode-switch; ++ sbu1-dc-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>; ++ sbu2-dc-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_HIGH>; ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ usbdp_phy0_typec_ss: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&usbc0_ss>; ++ }; ++ ++ usbdp_phy0_typec_sbu: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&usbc0_sbu>; ++ }; ++ }; ++}; ++ ++&usbdp_phy0_u3 { ++ status = "okay"; ++}; ++ + &usb_host0_ehci { + status = "okay"; + }; +@@ -758,6 +896,20 @@ &usb_host0_ohci { + status = "okay"; + }; + ++&usb_host0_xhci { ++ usb-role-switch; ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ usb_host0_xhci_drd_sw: endpoint { ++ remote-endpoint = <&usbc0_hs>; ++ }; ++ }; ++}; ++ + &usb_host1_ehci { + status = "okay"; + }; +@@ -765,3 +917,35 @@ &usb_host1_ehci { + &usb_host1_ohci { + status = "okay"; + }; ++ ++&usb_host1_xhci { ++ status = "okay"; ++}; ++ ++&usb_host2_xhci { ++ status = "okay"; ++}; ++ ++&hdmi0 { ++ status = "okay"; ++}; ++ ++&hdmi0_in_vp0 { ++ status = "okay"; ++}; ++ ++&hdptxphy_hdmi0 { ++ status = "okay"; ++}; ++ ++&hdptxphy_hdmi_clk0 { ++ status = "okay"; ++}; ++ ++&vop_mmu { ++ status = "okay"; ++}; ++ ++&vop { ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/rockchip/rk3588.dtsi b/arch/arm64/boot/dts/rockchip/rk3588.dtsi +index 5519c1430..900ac0300 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3588.dtsi +@@ -7,6 +7,26 @@ + #include "rk3588-pinctrl.dtsi" + + / { ++ usb_host1_xhci: usb@fc400000 { ++ compatible = "rockchip,rk3588-dwc3", "snps,dwc3"; ++ reg = <0x0 0xfc400000 0x0 0x400000>; ++ interrupts = ; ++ clocks = <&cru REF_CLK_USB3OTG1>, <&cru SUSPEND_CLK_USB3OTG1>, ++ <&cru ACLK_USB3OTG1>; ++ clock-names = "ref_clk", "suspend_clk", "bus_clk"; ++ dr_mode = "host"; ++ phys = <&u2phy1_otg>, <&usbdp_phy1_u3>; ++ phy-names = "usb2-phy", "usb3-phy"; ++ phy_type = "utmi_wide"; ++ power-domains = <&power RK3588_PD_USB>; ++ resets = <&cru SRST_A_USB3OTG1>; ++ snps,dis_enblslpm_quirk; ++ snps,dis-u2-freeclk-exists-quirk; ++ snps,dis-del-phy-power-chg-quirk; ++ snps,dis-tx-ipgap-linecheck-quirk; ++ status = "disabled"; ++ }; ++ + pcie30_phy_grf: syscon@fd5b8000 { + compatible = "rockchip,rk3588-pcie3-phy-grf", "syscon"; + reg = <0x0 0xfd5b8000 0x0 0x10000>; +@@ -17,6 +37,37 @@ pipe_phy1_grf: syscon@fd5c0000 { + reg = <0x0 0xfd5c0000 0x0 0x100>; + }; + ++ usbdpphy1_grf: syscon@fd5cc000 { ++ compatible = "rockchip,rk3588-usbdpphy-grf", "syscon"; ++ reg = <0x0 0xfd5cc000 0x0 0x4000>; ++ }; ++ ++ usb2phy1_grf: syscon@fd5d4000 { ++ compatible = "rockchip,rk3588-usb2phy-grf", "syscon", ++ "simple-mfd"; ++ reg = <0x0 0xfd5d4000 0x0 0x4000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ u2phy1: usb2-phy@4000 { ++ compatible = "rockchip,rk3588-usb2phy"; ++ reg = <0x4000 0x10>; ++ interrupts = ; ++ resets = <&cru SRST_OTGPHY_U3_1>, <&cru SRST_P_USB2PHY_U3_1_GRF0>; ++ reset-names = "phy", "apb"; ++ clocks = <&cru CLK_USB2PHY_HDPTXRXPHY_REF>; ++ clock-names = "phyclk"; ++ clock-output-names = "usb480m_phy1"; ++ #clock-cells = <0>; ++ status = "disabled"; ++ ++ u2phy1_otg: otg-port { ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ }; ++ }; ++ + i2s8_8ch: i2s@fddc8000 { + compatible = "rockchip,rk3588-i2s-tdm"; + reg = <0x0 0xfddc8000 0x0 0x1000>; +@@ -310,6 +361,37 @@ sata-port@0 { + }; + }; + ++ usbdp_phy1: phy@fed90000 { ++ compatible = "rockchip,rk3588-usbdp-phy"; ++ reg = <0x0 0xfed90000 0x0 0x10000>; ++ rockchip,u2phy-grf = <&usb2phy1_grf>; ++ rockchip,usb-grf = <&usb_grf>; ++ rockchip,usbdpphy-grf = <&usbdpphy1_grf>; ++ rockchip,vo-grf = <&vo0_grf>; ++ clocks = <&cru CLK_USBDPPHY_MIPIDCPPHY_REF>, ++ <&cru CLK_USBDP_PHY1_IMMORTAL>, ++ <&cru PCLK_USBDPPHY1>, ++ <&u2phy1>; ++ clock-names = "refclk", "immortal", "pclk", "utmi"; ++ resets = <&cru SRST_USBDP_COMBO_PHY1_INIT>, ++ <&cru SRST_USBDP_COMBO_PHY1_CMN>, ++ <&cru SRST_USBDP_COMBO_PHY1_LANE>, ++ <&cru SRST_USBDP_COMBO_PHY1_PCS>, ++ <&cru SRST_P_USBDPPHY1>; ++ reset-names = "init", "cmn", "lane", "pcs_apb", "pma_apb"; ++ status = "disabled"; ++ ++ usbdp_phy1_dp: dp-port { ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ usbdp_phy1_u3: usb3-port { ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ }; ++ + combphy1_ps: phy@fee10000 { + compatible = "rockchip,rk3588-naneng-combphy"; + reg = <0x0 0xfee10000 0x0 0x100>; +diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts +index 8347adcbd..58c58ec03 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts +@@ -114,36 +114,48 @@ vcc_1v1_nldo_s3: vcc-1v1-nldo-s3-regulator { + }; + }; + ++&combphy2_psu { ++ status = "okay"; ++}; ++ + &cpu_b0 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_s0>; + }; + + &cpu_b1 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_s0>; + }; + + &cpu_b2 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_s0>; + }; + + &cpu_b3 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_s0>; + }; + + &cpu_l0 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l1 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l2 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l3 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &i2c0 { +@@ -694,6 +706,14 @@ regulator-state-mem { + }; + }; + ++&u2phy0 { ++ status = "okay"; ++}; ++ ++&u2phy0_otg { ++ status = "okay"; ++}; ++ + &u2phy2 { + status = "okay"; + }; +@@ -717,6 +737,15 @@ &uart2 { + status = "okay"; + }; + ++&usbdp_phy0 { ++ status = "okay"; ++ rockchip,dp-lane-mux = <2 3>; ++}; ++ ++&usbdp_phy0_u3 { ++ status = "okay"; ++}; ++ + &usb_host0_ehci { + status = "okay"; + pinctrl-names = "default"; +@@ -727,6 +756,11 @@ &usb_host0_ohci { + status = "okay"; + }; + ++&usb_host0_xhci { ++ dr_mode = "host"; ++ status = "okay"; ++}; ++ + &usb_host1_ehci { + status = "okay"; + }; +@@ -734,3 +768,7 @@ &usb_host1_ehci { + &usb_host1_ohci { + status = "okay"; + }; ++ ++&usb_host2_xhci { ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +index 7064c0e91..4481a2e57 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +@@ -8,8 +8,10 @@ + #include + #include + #include ++#include + #include + #include ++#include + + / { + compatible = "rockchip,rk3588"; +@@ -18,6 +20,215 @@ / { + #address-cells = <2>; + #size-cells = <2>; + ++ cluster0_opp_table: opp-table-cluster0 { ++ compatible = "operating-points-v2"; ++ opp-shared; ++ ++ opp-408000000 { ++ opp-hz = /bits/ 64 <408000000>; ++ opp-microvolt = <750000 750000 950000>, ++ <750000 750000 950000>; ++ clock-latency-ns = <40000>; ++ opp-suspend; ++ }; ++ opp-600000000 { ++ opp-hz = /bits/ 64 <600000000>; ++ opp-microvolt = <750000 750000 950000>, ++ <750000 750000 950000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-816000000 { ++ opp-hz = /bits/ 64 <816000000>; ++ opp-microvolt = <750000 750000 950000>, ++ <750000 750000 950000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1008000000 { ++ opp-hz = /bits/ 64 <1008000000>; ++ opp-microvolt = <750000 750000 950000>, ++ <750000 750000 950000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1200000000 { ++ opp-hz = /bits/ 64 <1200000000>; ++ opp-microvolt = <775000 775000 950000>, ++ <775000 775000 950000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1416000000 { ++ opp-hz = /bits/ 64 <1416000000>; ++ opp-microvolt = <825000 825000 950000>, ++ <825000 825000 950000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1608000000 { ++ opp-hz = /bits/ 64 <1608000000>; ++ opp-microvolt = <875000 875000 950000>, ++ <875000 875000 950000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1800000000 { ++ opp-hz = /bits/ 64 <1800000000>; ++ opp-microvolt = <950000 950000 950000>, ++ <950000 950000 950000>; ++ clock-latency-ns = <40000>; ++ }; ++ }; ++ ++ cluster1_opp_table: opp-table-cluster1 { ++ compatible = "operating-points-v2"; ++ opp-shared; ++ ++ rockchip,grf = <&bigcore0_grf>; ++ rockchip,volt-mem-read-margin = < ++ 855000 1 ++ 765000 2 ++ 675000 3 ++ 495000 4 ++ >; ++ ++ rockchip,reboot-freq = <1800000000>; ++ ++ opp-408000000 { ++ opp-hz = /bits/ 64 <408000000>; ++ opp-microvolt = <600000 600000 1000000>, ++ <675000 675000 1000000>; ++ clock-latency-ns = <40000>; ++ opp-suspend; ++ }; ++ opp-600000000 { ++ opp-hz = /bits/ 64 <600000000>; ++ opp-microvolt = <600000 600000 1000000>, ++ <675000 675000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-816000000 { ++ opp-hz = /bits/ 64 <816000000>; ++ opp-microvolt = <600000 600000 1000000>, ++ <675000 675000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1008000000 { ++ opp-hz = /bits/ 64 <1008000000>; ++ opp-microvolt = <625000 625000 1000000>, ++ <675000 675000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1200000000 { ++ opp-hz = /bits/ 64 <1200000000>; ++ opp-microvolt = <650000 650000 1000000>, ++ <675000 675000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1416000000 { ++ opp-hz = /bits/ 64 <1416000000>; ++ opp-microvolt = <675000 675000 1000000>, ++ <675000 675000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1608000000 { ++ opp-hz = /bits/ 64 <1608000000>; ++ opp-microvolt = <700000 700000 1000000>, ++ <700000 700000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1800000000 { ++ opp-hz = /bits/ 64 <1800000000>; ++ opp-microvolt = <775000 775000 1000000>, ++ <775000 775000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-2016000000 { ++ opp-hz = /bits/ 64 <2016000000>; ++ opp-microvolt = <850000 850000 1000000>, ++ <850000 850000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-2208000000 { ++ opp-hz = /bits/ 64 <2208000000>; ++ opp-microvolt = <925000 925000 1000000>, ++ <925000 925000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ }; ++ ++ cluster2_opp_table: opp-table-cluster2 { ++ compatible = "operating-points-v2"; ++ opp-shared; ++ ++ rockchip,grf = <&bigcore1_grf>; ++ rockchip,volt-mem-read-margin = < ++ 855000 1 ++ 765000 2 ++ 675000 3 ++ 495000 4 ++ >; ++ ++ rockchip,reboot-freq = <1800000000>; ++ ++ opp-408000000 { ++ opp-hz = /bits/ 64 <408000000>; ++ opp-microvolt = <600000 600000 1000000>, ++ <675000 675000 1000000>; ++ clock-latency-ns = <40000>; ++ opp-suspend; ++ }; ++ opp-600000000 { ++ opp-hz = /bits/ 64 <600000000>; ++ opp-microvolt = <600000 600000 1000000>, ++ <675000 675000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-816000000 { ++ opp-hz = /bits/ 64 <816000000>; ++ opp-microvolt = <600000 600000 1000000>, ++ <675000 675000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1008000000 { ++ opp-hz = /bits/ 64 <1008000000>; ++ opp-microvolt = <625000 625000 1000000>, ++ <675000 675000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1200000000 { ++ opp-hz = /bits/ 64 <1200000000>; ++ opp-microvolt = <650000 650000 1000000>, ++ <675000 675000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1416000000 { ++ opp-hz = /bits/ 64 <1416000000>; ++ opp-microvolt = <675000 675000 1000000>, ++ <675000 675000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1608000000 { ++ opp-hz = /bits/ 64 <1608000000>; ++ opp-microvolt = <700000 700000 1000000>, ++ <700000 700000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-1800000000 { ++ opp-hz = /bits/ 64 <1800000000>; ++ opp-microvolt = <775000 775000 1000000>, ++ <775000 775000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-2016000000 { ++ opp-hz = /bits/ 64 <2016000000>; ++ opp-microvolt = <850000 850000 1000000>, ++ <850000 850000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ opp-2208000000 { ++ opp-hz = /bits/ 64 <2208000000>; ++ opp-microvolt = <925000 925000 1000000>, ++ <925000 925000 1000000>; ++ clock-latency-ns = <40000>; ++ }; ++ }; ++ + cpus { + #address-cells = <1>; + #size-cells = <0>; +@@ -64,6 +275,7 @@ cpu_l0: cpu@0 { + clocks = <&scmi_clk SCMI_CLK_CPUL>; + assigned-clocks = <&scmi_clk SCMI_CLK_CPUL>; + assigned-clock-rates = <816000000>; ++ operating-points-v2 = <&cluster0_opp_table>; + cpu-idle-states = <&CPU_SLEEP>; + i-cache-size = <32768>; + i-cache-line-size = <64>; +@@ -83,6 +295,7 @@ cpu_l1: cpu@100 { + enable-method = "psci"; + capacity-dmips-mhz = <530>; + clocks = <&scmi_clk SCMI_CLK_CPUL>; ++ operating-points-v2 = <&cluster0_opp_table>; + cpu-idle-states = <&CPU_SLEEP>; + i-cache-size = <32768>; + i-cache-line-size = <64>; +@@ -102,6 +315,7 @@ cpu_l2: cpu@200 { + enable-method = "psci"; + capacity-dmips-mhz = <530>; + clocks = <&scmi_clk SCMI_CLK_CPUL>; ++ operating-points-v2 = <&cluster0_opp_table>; + cpu-idle-states = <&CPU_SLEEP>; + i-cache-size = <32768>; + i-cache-line-size = <64>; +@@ -121,6 +335,7 @@ cpu_l3: cpu@300 { + enable-method = "psci"; + capacity-dmips-mhz = <530>; + clocks = <&scmi_clk SCMI_CLK_CPUL>; ++ operating-points-v2 = <&cluster0_opp_table>; + cpu-idle-states = <&CPU_SLEEP>; + i-cache-size = <32768>; + i-cache-line-size = <64>; +@@ -142,6 +357,7 @@ cpu_b0: cpu@400 { + clocks = <&scmi_clk SCMI_CLK_CPUB01>; + assigned-clocks = <&scmi_clk SCMI_CLK_CPUB01>; + assigned-clock-rates = <816000000>; ++ operating-points-v2 = <&cluster1_opp_table>; + cpu-idle-states = <&CPU_SLEEP>; + i-cache-size = <65536>; + i-cache-line-size = <64>; +@@ -161,6 +377,7 @@ cpu_b1: cpu@500 { + enable-method = "psci"; + capacity-dmips-mhz = <1024>; + clocks = <&scmi_clk SCMI_CLK_CPUB01>; ++ operating-points-v2 = <&cluster1_opp_table>; + cpu-idle-states = <&CPU_SLEEP>; + i-cache-size = <65536>; + i-cache-line-size = <64>; +@@ -182,6 +399,7 @@ cpu_b2: cpu@600 { + clocks = <&scmi_clk SCMI_CLK_CPUB23>; + assigned-clocks = <&scmi_clk SCMI_CLK_CPUB23>; + assigned-clock-rates = <816000000>; ++ operating-points-v2 = <&cluster2_opp_table>; + cpu-idle-states = <&CPU_SLEEP>; + i-cache-size = <65536>; + i-cache-line-size = <64>; +@@ -201,6 +419,7 @@ cpu_b3: cpu@700 { + enable-method = "psci"; + capacity-dmips-mhz = <1024>; + clocks = <&scmi_clk SCMI_CLK_CPUB23>; ++ operating-points-v2 = <&cluster2_opp_table>; + cpu-idle-states = <&CPU_SLEEP>; + i-cache-size = <65536>; + i-cache-line-size = <64>; +@@ -362,6 +581,235 @@ spll: clock-0 { + #clock-cells = <0>; + }; + ++ display_subsystem: display-subsystem { ++ compatible = "rockchip,display-subsystem"; ++ ports = <&vop_out>; ++ }; ++ ++ thermal_zones: thermal-zones { ++ soc_thermal: soc-thermal { ++ polling-delay-passive = <20>; /* milliseconds */ ++ polling-delay = <1000>; /* milliseconds */ ++ sustainable-power = <2100>; /* milliwatts */ ++ ++ thermal-sensors = <&tsadc 0>; ++ trips { ++ trip-point-0 { ++ temperature = <75000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ soc_target: trip-point-1 { ++ temperature = <85000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ trip-point-2 { ++ /* millicelsius */ ++ temperature = <115000>; ++ /* millicelsius */ ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; ++ ++ cooling-maps { ++ map0 { ++ trip = <&soc_target>; ++ cooling-device = <&cpu_l0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_l1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_l2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_l3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_b0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_b1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_b2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_b3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; ++ contribution = <1024>; ++ }; ++ }; ++ }; ++ ++ bigcore0_thermal: bigcore0-thermal { ++ polling-delay-passive = <20>; /* milliseconds */ ++ polling-delay = <1000>; /* milliseconds */ ++ thermal-sensors = <&tsadc 1>; ++ ++ trips { ++ trip-point-0 { ++ temperature = <75000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ b0_target: trip-point-1 { ++ temperature = <85000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ trip-point-2 { ++ /* millicelsius */ ++ temperature = <115000>; ++ /* millicelsius */ ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; ++ ++ cooling-maps { ++ map0 { ++ trip = <&b0_target>; ++ cooling-device = <&cpu_b0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_b1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; ++ contribution = <1024>; ++ }; ++ }; ++ }; ++ ++ bigcore1_thermal: bigcore1-thermal { ++ polling-delay-passive = <20>; /* milliseconds */ ++ polling-delay = <1000>; /* milliseconds */ ++ thermal-sensors = <&tsadc 2>; ++ trips { ++ trip-point-0 { ++ temperature = <75000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ b1_target: trip-point-1 { ++ temperature = <85000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ trip-point-2 { ++ /* millicelsius */ ++ temperature = <115000>; ++ /* millicelsius */ ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; ++ ++ cooling-maps { ++ map0 { ++ trip = <&b1_target>; ++ cooling-device = <&cpu_b2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_b3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; ++ contribution = <1024>; ++ }; ++ }; ++ }; ++ ++ little_core_thermal: littlecore-thermal { ++ polling-delay-passive = <20>; /* milliseconds */ ++ polling-delay = <1000>; /* milliseconds */ ++ thermal-sensors = <&tsadc 3>; ++ trips { ++ trip-point-0 { ++ temperature = <75000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ l0_target: trip-point-1 { ++ temperature = <85000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ trip-point-2 { ++ /* millicelsius */ ++ temperature = <115000>; ++ /* millicelsius */ ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; ++ ++ cooling-maps { ++ map0 { ++ trip = <&l0_target>; ++ cooling-device = <&cpu_l0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_l1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_l2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, ++ <&cpu_l3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; ++ contribution = <1024>; ++ }; ++ }; ++ }; ++ ++ center_thermal: center-thermal { ++ polling-delay-passive = <20>; /* milliseconds */ ++ polling-delay = <1000>; /* milliseconds */ ++ thermal-sensors = <&tsadc 4>; ++ trips { ++ trip-point-0 { ++ temperature = <75000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ trip-point-1 { ++ temperature = <85000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ trip-point-2 { ++ /* millicelsius */ ++ temperature = <115000>; ++ /* millicelsius */ ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ ++ gpu_thermal: gpu-thermal { ++ polling-delay-passive = <20>; /* milliseconds */ ++ polling-delay = <1000>; /* milliseconds */ ++ thermal-sensors = <&tsadc 5>; ++ trips { ++ trip-point-0 { ++ temperature = <75000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ trip-point-1 { ++ temperature = <85000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ trip-point-2 { ++ /* millicelsius */ ++ temperature = <115000>; ++ /* millicelsius */ ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ ++ npu_thermal: npu-thermal { ++ polling-delay-passive = <20>; /* milliseconds */ ++ polling-delay = <1000>; /* milliseconds */ ++ thermal-sensors = <&tsadc 6>; ++ trips { ++ trip-point-0 { ++ temperature = <75000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ trip-point-1 { ++ temperature = <85000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ trip-point-2 { ++ /* millicelsius */ ++ temperature = <115000>; ++ /* millicelsius */ ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ }; ++ + timer { + compatible = "arm,armv8-timer"; + interrupts = , +@@ -399,6 +847,28 @@ scmi_shmem: sram@0 { + }; + }; + ++ usb_host0_xhci: usb@fc000000 { ++ compatible = "rockchip,rk3588-dwc3", "snps,dwc3"; ++ reg = <0x0 0xfc000000 0x0 0x400000>; ++ interrupts = ; ++ clocks = <&cru REF_CLK_USB3OTG0>, <&cru SUSPEND_CLK_USB3OTG0>, ++ <&cru ACLK_USB3OTG0>; ++ clock-names = "ref_clk", "suspend_clk", "bus_clk"; ++ dr_mode = "otg"; ++ phys = <&u2phy0_otg>, <&usbdp_phy0_u3>; ++ phy-names = "usb2-phy", "usb3-phy"; ++ phy_type = "utmi_wide"; ++ power-domains = <&power RK3588_PD_USB>; ++ resets = <&cru SRST_A_USB3OTG0>; ++ snps,dis_enblslpm_quirk; ++ snps,dis-u1-entry-quirk; ++ snps,dis-u2-entry-quirk; ++ snps,dis-u2-freeclk-exists-quirk; ++ snps,dis-del-phy-power-chg-quirk; ++ snps,dis-tx-ipgap-linecheck-quirk; ++ status = "disabled"; ++ }; ++ + usb_host0_ehci: usb@fc800000 { + compatible = "rockchip,rk3588-ehci", "generic-ehci"; + reg = <0x0 0xfc800000 0x0 0x40000>; +@@ -474,6 +944,16 @@ sys_grf: syscon@fd58c000 { + reg = <0x0 0xfd58c000 0x0 0x1000>; + }; + ++ bigcore0_grf: syscon@fd590000 { ++ compatible = "rockchip,rk3588-bigcore0-grf", "syscon"; ++ reg = <0x0 0xfd590000 0x0 0x100>; ++ }; ++ ++ bigcore1_grf: syscon@fd592000 { ++ compatible = "rockchip,rk3588-bigcore1-grf", "syscon"; ++ reg = <0x0 0xfd592000 0x0 0x100>; ++ }; ++ + php_grf: syscon@fd5b0000 { + compatible = "rockchip,rk3588-php-grf", "syscon"; + reg = <0x0 0xfd5b0000 0x0 0x1000>; +@@ -489,6 +969,37 @@ pipe_phy2_grf: syscon@fd5c4000 { + reg = <0x0 0xfd5c4000 0x0 0x100>; + }; + ++ usbdpphy0_grf: syscon@fd5c8000 { ++ compatible = "rockchip,rk3588-usbdpphy-grf", "syscon"; ++ reg = <0x0 0xfd5c8000 0x0 0x4000>; ++ }; ++ ++ usb2phy0_grf: syscon@fd5d0000 { ++ compatible = "rockchip,rk3588-usb2phy-grf", "syscon", ++ "simple-mfd"; ++ reg = <0x0 0xfd5d0000 0x0 0x4000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ u2phy0: usb2-phy@0 { ++ compatible = "rockchip,rk3588-usb2phy"; ++ reg = <0x0 0x10>; ++ interrupts = ; ++ resets = <&cru SRST_OTGPHY_U3_0>, <&cru SRST_P_USB2PHY_U3_0_GRF0>; ++ reset-names = "phy", "apb"; ++ clocks = <&cru CLK_USB2PHY_HDPTXRXPHY_REF>; ++ clock-names = "phyclk"; ++ clock-output-names = "usb480m_phy0"; ++ #clock-cells = <0>; ++ status = "disabled"; ++ ++ u2phy0_otg: otg-port { ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ }; ++ }; ++ + usb2phy2_grf: syscon@fd5d8000 { + compatible = "rockchip,rk3588-usb2phy-grf", "syscon", "simple-mfd"; + reg = <0x0 0xfd5d8000 0x0 0x4000>; +@@ -514,6 +1025,28 @@ u2phy2_host: host-port { + }; + }; + ++ vop_grf: syscon@fd5a4000 { ++ compatible = "rockchip,rk3588-vop-grf", "syscon"; ++ reg = <0x0 0xfd5a4000 0x0 0x2000>; ++ }; ++ ++ vo0_grf: syscon@fd5a6000 { ++ compatible = "rockchip,rk3588-vo-grf", "syscon"; ++ reg = <0x0 0xfd5a6000 0x0 0x2000>; ++ clocks = <&cru PCLK_VO0GRF>; ++ }; ++ ++ vo1_grf: syscon@fd5a8000 { ++ compatible = "rockchip,rk3588-vo-grf", "syscon"; ++ reg = <0x0 0xfd5a8000 0x0 0x100>; ++ clocks = <&cru PCLK_VO1GRF>; ++ }; ++ ++ usb_grf: syscon@fd5ac000 { ++ compatible = "rockchip,rk3588-usb-grf", "syscon"; ++ reg = <0x0 0xfd5ac000 0x0 0x4000>; ++ }; ++ + usb2phy3_grf: syscon@fd5dc000 { + compatible = "rockchip,rk3588-usb2phy-grf", "syscon", "simple-mfd"; + reg = <0x0 0xfd5dc000 0x0 0x4000>; +@@ -539,6 +1072,11 @@ u2phy3_host: host-port { + }; + }; + ++ hdptxphy0_grf: syscon@fd5e0000 { ++ compatible = "rockchip,rk3588-hdptxphy-grf", "syscon"; ++ reg = <0x0 0xfd5e0000 0x0 0x100>; ++ }; ++ + ioc: syscon@fd5f0000 { + compatible = "rockchip,rk3588-ioc", "syscon"; + reg = <0x0 0xfd5f0000 0x0 0x10000>; +@@ -962,6 +1500,112 @@ power-domain@RK3588_PD_SDMMC { + }; + }; + ++ vop: vop@fdd90000 { ++ compatible = "rockchip,rk3588-vop"; ++ reg = <0x0 0xfdd90000 0x0 0x4200>, <0x0 0xfdd95000 0x0 0x1000>; ++ reg-names = "vop", "gamma_lut"; ++ interrupts = ; ++ clocks = <&cru ACLK_VOP>, ++ <&cru HCLK_VOP>, ++ <&cru DCLK_VOP0>, ++ <&cru DCLK_VOP1>, ++ <&cru DCLK_VOP2>, ++ <&cru DCLK_VOP3>, ++ <&cru PCLK_VOP_ROOT>, ++ <&cru DCLK_VOP0_SRC>, ++ <&cru DCLK_VOP1_SRC>, ++ <&cru DCLK_VOP2_SRC>; ++ clock-names = "aclk", ++ "hclk", ++ "dclk_vp0", ++ "dclk_vp1", ++ "dclk_vp2", ++ "dclk_vp3", ++ "pclk", ++ "dclk_src_vp0", ++ "dclk_src_vp1", ++ "dclk_src_vp2"; ++ resets = <&cru SRST_A_VOP>, ++ <&cru SRST_H_VOP>, ++ <&cru SRST_D_VOP0>, ++ <&cru SRST_D_VOP1>, ++ <&cru SRST_D_VOP2>, ++ <&cru SRST_D_VOP3>; ++ reset-names = "axi", ++ "ahb", ++ "dclk_vp0", ++ "dclk_vp1", ++ "dclk_vp2", ++ "dclk_vp3"; ++ iommus = <&vop_mmu>; ++ power-domains = <&power RK3588_PD_VOP>; ++ rockchip,grf = <&sys_grf>; ++ rockchip,vop-grf = <&vop_grf>; ++ rockchip,vo1-grf = <&vo1_grf>; ++ rockchip,pmu = <&pmu>; ++ ++ status = "disabled"; ++ ++ // [CC: move endpoints to rock5b dts, see rock-3a] ++ vop_out: ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ vp0: port@0 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0>; ++ ++ vp0_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 { ++ reg = ; ++ remote-endpoint = <&hdmi0_in_vp0>; ++ }; ++ }; ++ ++ vp1: port@1 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <1>; ++ ++ vp1_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 { ++ reg = ; ++ remote-endpoint = <&hdmi0_in_vp1>; ++ }; ++ }; ++ ++ vp2: port@2 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <2>; ++ ++ assigned-clocks = <&cru DCLK_VOP2_SRC>; ++ assigned-clock-parents = <&cru PLL_V0PLL>; ++ ++ vp2_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 { ++ reg = ; ++ remote-endpoint = <&hdmi0_in_vp2>; ++ }; ++ }; ++ ++ vp3: port@3 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <3>; ++ }; ++ }; ++ }; ++ ++ vop_mmu: iommu@fdd97e00 { ++ compatible = "rockchip,rk3568-iommu"; ++ reg = <0x0 0xfdd97e00 0x0 0x100>, <0x0 0xfdd97f00 0x0 0x100>; ++ interrupts = ; ++ interrupt-names = "vop_mmu"; ++ clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>; ++ clock-names = "aclk", "iface"; ++ #iommu-cells = <0>; ++ status = "disabled"; ++ }; ++ + i2s4_8ch: i2s@fddc0000 { + compatible = "rockchip,rk3588-i2s-tdm"; + reg = <0x0 0xfddc0000 0x0 0x1000>; +@@ -1013,6 +1657,77 @@ i2s9_8ch: i2s@fddfc000 { + status = "disabled"; + }; + ++ hdmi0: hdmi@fde80000 { ++ compatible = "rockchip,rk3588-dw-hdmi"; ++ reg = <0x0 0xfde80000 0x0 0x20000>; ++ interrupts = , ++ , ++ , ++ , ++ ; ++ clocks = <&cru PCLK_HDMITX0>, ++ <&cru CLK_HDMIHDP0>, ++ <&cru CLK_HDMITX0_EARC>, ++ <&cru CLK_HDMITX0_REF>, ++ <&cru MCLK_I2S5_8CH_TX>, ++ <&cru DCLK_VOP0>, ++ <&cru DCLK_VOP1>, ++ <&cru DCLK_VOP2>, ++ <&cru DCLK_VOP3>, ++ <&cru HCLK_VO1>; ++ clock-names = "pclk", ++ "hpd", ++ "earc", ++ "hdmitx_ref", ++ "aud", ++ "dclk_vp0", ++ "dclk_vp1", ++ "dclk_vp2", ++ "dclk_vp3", ++ "hclk_vo1"; ++ resets = <&cru SRST_HDMITX0_REF>, <&cru SRST_HDMIHDP0>; ++ reset-names = "ref", "hdp"; ++ power-domains = <&power RK3588_PD_VO1>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hdmim0_tx0_cec &hdmim0_tx0_hpd &hdmim0_tx0_scl &hdmim0_tx0_sda>; ++ reg-io-width = <4>; ++ rockchip,grf = <&sys_grf>; ++ rockchip,vo1_grf = <&vo1_grf>; ++ phys = <&hdptxphy_hdmi0>; ++ phy-names = "hdmi"; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hdmi0_in: port@0 { ++ reg = <0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hdmi0_in_vp0: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&vp0_out_hdmi0>; ++ status = "disabled"; ++ }; ++ ++ hdmi0_in_vp1: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&vp1_out_hdmi0>; ++ status = "disabled"; ++ }; ++ ++ hdmi0_in_vp2: endpoint@2 { ++ reg = <2>; ++ remote-endpoint = <&vp2_out_hdmi0>; ++ status = "disabled"; ++ }; ++ }; ++ }; ++ }; ++ + qos_gpu_m0: qos@fdf35000 { + compatible = "rockchip,rk3588-qos", "syscon"; + reg = <0x0 0xfdf35000 0x0 0x20>; +@@ -2110,7 +2825,6 @@ tsadc: tsadc@fec00000 { + pinctrl-1 = <&tsadc_shut>; + pinctrl-names = "gpio", "otpout"; + #thermal-sensor-cells = <1>; +- status = "disabled"; + }; + + saradc: adc@fec10000 { +@@ -2245,6 +2959,58 @@ dmac2: dma-controller@fed10000 { + #dma-cells = <1>; + }; + ++ hdptxphy_hdmi0: hdmiphy@fed60000 { ++ compatible = "rockchip,rk3588-hdptx-phy-hdmi"; ++ reg = <0x0 0xfed60000 0x0 0x2000>; ++ clocks = <&cru CLK_USB2PHY_HDPTXRXPHY_REF>, <&cru PCLK_HDPTX0>; ++ clock-names = "ref", "apb"; ++ resets = <&cru SRST_HDPTX0>, <&cru SRST_P_HDPTX0>, ++ <&cru SRST_HDPTX0_INIT>, <&cru SRST_HDPTX0_CMN>, ++ <&cru SRST_HDPTX0_LANE>, <&cru SRST_HDPTX0_ROPLL>, ++ <&cru SRST_HDPTX0_LCPLL>; ++ reset-names = "phy", "apb", "init", "cmn", "lane", "ropll", ++ "lcpll"; ++ rockchip,grf = <&hdptxphy0_grf>; ++ #phy-cells = <0>; ++ status = "disabled"; ++ ++ hdptxphy_hdmi_clk0: clk-port { ++ #clock-cells = <0>; ++ status = "disabled"; ++ }; ++ }; ++ ++ usbdp_phy0: phy@fed80000 { ++ compatible = "rockchip,rk3588-usbdp-phy"; ++ reg = <0x0 0xfed80000 0x0 0x10000>; ++ rockchip,u2phy-grf = <&usb2phy0_grf>; ++ rockchip,usb-grf = <&usb_grf>; ++ rockchip,usbdpphy-grf = <&usbdpphy0_grf>; ++ rockchip,vo-grf = <&vo0_grf>; ++ clocks = <&cru CLK_USBDPPHY_MIPIDCPPHY_REF>, ++ <&cru CLK_USBDP_PHY0_IMMORTAL>, ++ <&cru PCLK_USBDPPHY0>, ++ <&u2phy0>; ++ clock-names = "refclk", "immortal", "pclk", "utmi"; ++ resets = <&cru SRST_USBDP_COMBO_PHY0_INIT>, ++ <&cru SRST_USBDP_COMBO_PHY0_CMN>, ++ <&cru SRST_USBDP_COMBO_PHY0_LANE>, ++ <&cru SRST_USBDP_COMBO_PHY0_PCS>, ++ <&cru SRST_P_USBDPPHY0>; ++ reset-names = "init", "cmn", "lane", "pcs_apb", "pma_apb"; ++ status = "disabled"; ++ ++ usbdp_phy0_dp: dp-port { ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ usbdp_phy0_u3: usb3-port { ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ }; ++ + combphy0_ps: phy@fee00000 { + compatible = "rockchip,rk3588-naneng-combphy"; + reg = <0x0 0xfee00000 0x0 0x100>; +diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig +index b60aa1f89..906f2f21a 100644 +--- a/arch/arm64/configs/defconfig ++++ b/arch/arm64/configs/defconfig +@@ -1449,8 +1449,9 @@ CONFIG_PHY_ROCKCHIP_EMMC=y + CONFIG_PHY_ROCKCHIP_INNO_HDMI=m + CONFIG_PHY_ROCKCHIP_INNO_USB2=y + CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY=m +-CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY=m ++CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY=y + CONFIG_PHY_ROCKCHIP_PCIE=m ++CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX_HDMI=m + CONFIG_PHY_ROCKCHIP_SNPS_PCIE3=y + CONFIG_PHY_ROCKCHIP_TYPEC=y + CONFIG_PHY_SAMSUNG_UFS=y +diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c +index 66759fe28..478a4e594 100644 +--- a/drivers/clk/clk-composite.c ++++ b/drivers/clk/clk-composite.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + #include + + static u8 clk_composite_get_parent(struct clk_hw *hw) +@@ -119,10 +120,7 @@ static int clk_composite_determine_rate(struct clk_hw *hw, + if (ret) + continue; + +- if (req->rate >= tmp_req.rate) +- rate_diff = req->rate - tmp_req.rate; +- else +- rate_diff = tmp_req.rate - req->rate; ++ rate_diff = abs_diff(req->rate, tmp_req.rate); + + if (!rate_diff || !req->best_parent_hw + || best_rate_diff > rate_diff) { +diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c +index a2c2b5203..94b4fb66a 100644 +--- a/drivers/clk/clk-divider.c ++++ b/drivers/clk/clk-divider.c +@@ -220,7 +220,7 @@ static int _div_round_up(const struct clk_div_table *table, + unsigned long parent_rate, unsigned long rate, + unsigned long flags) + { +- int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); ++ int div = DIV_ROUND_UP_NO_OVERFLOW(parent_rate, rate); + + if (flags & CLK_DIVIDER_POWER_OF_TWO) + div = __roundup_pow_of_two(div); +@@ -237,7 +237,7 @@ static int _div_round_closest(const struct clk_div_table *table, + int up, down; + unsigned long up_rate, down_rate; + +- up = DIV_ROUND_UP_ULL((u64)parent_rate, rate); ++ up = DIV_ROUND_UP_NO_OVERFLOW(parent_rate, rate); + down = parent_rate / rate; + + if (flags & CLK_DIVIDER_POWER_OF_TWO) { +@@ -473,7 +473,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate, + { + unsigned int div, value; + +- div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); ++ div = DIV_ROUND_UP_NO_OVERFLOW(parent_rate, rate); + + if (!_is_valid_div(table, div, flags)) + return -EINVAL; +diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c +index 6994165e0..c6b605d6d 100644 +--- a/drivers/clk/rockchip/clk-rk3588.c ++++ b/drivers/clk/rockchip/clk-rk3588.c +@@ -1851,8 +1851,6 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = { + RK3588_CLKGATE_CON(56), 0, GFLAGS), + GATE(PCLK_TRNG0, "pclk_trng0", "pclk_vo0_root", 0, + RK3588_CLKGATE_CON(56), 1, GFLAGS), +- GATE(PCLK_VO0GRF, "pclk_vo0grf", "pclk_vo0_root", CLK_IGNORE_UNUSED, +- RK3588_CLKGATE_CON(55), 10, GFLAGS), + COMPOSITE(CLK_I2S4_8CH_TX_SRC, "clk_i2s4_8ch_tx_src", gpll_aupll_p, 0, + RK3588_CLKSEL_CON(118), 5, 1, MFLAGS, 0, 5, DFLAGS, + RK3588_CLKGATE_CON(56), 11, GFLAGS), +@@ -1998,8 +1996,6 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = { + RK3588_CLKGATE_CON(60), 9, GFLAGS), + GATE(PCLK_TRNG1, "pclk_trng1", "pclk_vo1_root", 0, + RK3588_CLKGATE_CON(60), 10, GFLAGS), +- GATE(0, "pclk_vo1grf", "pclk_vo1_root", CLK_IGNORE_UNUSED, +- RK3588_CLKGATE_CON(59), 12, GFLAGS), + GATE(PCLK_S_EDP0, "pclk_s_edp0", "pclk_vo1_s_root", 0, + RK3588_CLKGATE_CON(59), 14, GFLAGS), + GATE(PCLK_S_EDP1, "pclk_s_edp1", "pclk_vo1_s_root", 0, +@@ -2447,12 +2443,15 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = { + GATE_LINK(HCLK_RKVDEC1_PRE, "hclk_rkvdec1_pre", "hclk_rkvdec1_root", "hclk_vdpu_root", 0, RK3588_CLKGATE_CON(41), 4, GFLAGS), + GATE_LINK(ACLK_RKVDEC1_PRE, "aclk_rkvdec1_pre", "aclk_rkvdec1_root", "aclk_vdpu_root", 0, RK3588_CLKGATE_CON(41), 5, GFLAGS), + GATE_LINK(ACLK_HDCP0_PRE, "aclk_hdcp0_pre", "aclk_vo0_root", "aclk_vop_low_root", 0, RK3588_CLKGATE_CON(55), 9, GFLAGS), +- GATE_LINK(HCLK_VO0, "hclk_vo0", "hclk_vo0_root", "hclk_vop_root", 0, RK3588_CLKGATE_CON(55), 5, GFLAGS), ++ GATE_LINK(HCLK_VO0, "hclk_vo0", "hclk_vo0_root", "hclk_vop_root", RK3588_LINKED_CLK, RK3588_CLKGATE_CON(55), 5, GFLAGS), + GATE_LINK(ACLK_HDCP1_PRE, "aclk_hdcp1_pre", "aclk_hdcp1_root", "aclk_vo1usb_top_root", 0, RK3588_CLKGATE_CON(59), 6, GFLAGS), +- GATE_LINK(HCLK_VO1, "hclk_vo1", "hclk_vo1_root", "hclk_vo1usb_top_root", 0, RK3588_CLKGATE_CON(59), 9, GFLAGS), ++ GATE_LINK(HCLK_VO1, "hclk_vo1", "hclk_vo1_root", "hclk_vo1usb_top_root", RK3588_LINKED_CLK, RK3588_CLKGATE_CON(59), 9, GFLAGS), + GATE_LINK(ACLK_AV1_PRE, "aclk_av1_pre", "aclk_av1_root", "aclk_vdpu_root", 0, RK3588_CLKGATE_CON(68), 1, GFLAGS), + GATE_LINK(PCLK_AV1_PRE, "pclk_av1_pre", "pclk_av1_root", "hclk_vdpu_root", 0, RK3588_CLKGATE_CON(68), 4, GFLAGS), + GATE_LINK(HCLK_SDIO_PRE, "hclk_sdio_pre", "hclk_sdio_root", "hclk_nvm", 0, RK3588_CLKGATE_CON(75), 1, GFLAGS), ++ GATE_LINK(PCLK_VO0GRF, "pclk_vo0grf", "pclk_vo0_root", "hclk_vo0", CLK_IGNORE_UNUSED, RK3588_CLKGATE_CON(55), 10, GFLAGS), ++ GATE_LINK(PCLK_VO1GRF, "pclk_vo1grf", "pclk_vo1_root", "hclk_vo1", CLK_IGNORE_UNUSED, RK3588_CLKGATE_CON(59), 12, GFLAGS), ++ + }; + + static void __init rk3588_clk_init(struct device_node *np) +diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm +index f91160689..1e2552108 100644 +--- a/drivers/cpufreq/Kconfig.arm ++++ b/drivers/cpufreq/Kconfig.arm +@@ -189,6 +189,16 @@ config ARM_RASPBERRYPI_CPUFREQ + + If in doubt, say N. + ++config ARM_ROCKCHIP_CPUFREQ ++ tristate "Rockchip CPUfreq driver" ++ depends on ARCH_ROCKCHIP && CPUFREQ_DT ++ select PM_OPP ++ help ++ This adds the CPUFreq driver support for Rockchip SoCs, ++ based on cpufreq-dt. ++ ++ If in doubt, say N. ++ + config ARM_S3C64XX_CPUFREQ + bool "Samsung S3C64XX" + depends on CPU_S3C6410 +diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile +index 8d141c71b..14fb48863 100644 +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -71,6 +71,7 @@ obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o + obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW) += qcom-cpufreq-hw.o + obj-$(CONFIG_ARM_QCOM_CPUFREQ_NVMEM) += qcom-cpufreq-nvmem.o + obj-$(CONFIG_ARM_RASPBERRYPI_CPUFREQ) += raspberrypi-cpufreq.o ++obj-$(CONFIG_ARM_ROCKCHIP_CPUFREQ) += rockchip-cpufreq.o + obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o + obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o + obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o +diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c +index bd1e1357c..cfd35aa52 100644 +--- a/drivers/cpufreq/cpufreq-dt-platdev.c ++++ b/drivers/cpufreq/cpufreq-dt-platdev.c +@@ -168,6 +168,8 @@ static const struct of_device_id blocklist[] __initconst = { + { .compatible = "qcom,sm8450", }, + { .compatible = "qcom,sm8550", }, + ++ { .compatible = "rockchip,rk3588", }, ++ + { .compatible = "st,stih407", }, + { .compatible = "st,stih410", }, + { .compatible = "st,stih418", }, +diff --git a/drivers/cpufreq/rockchip-cpufreq.c b/drivers/cpufreq/rockchip-cpufreq.c +new file mode 100644 +index 000000000..0bf57ac85 +--- /dev/null ++++ b/drivers/cpufreq/rockchip-cpufreq.c +@@ -0,0 +1,645 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Rockchip CPUFreq Driver. This is similar to the generic DT ++ * cpufreq driver, but handles the following platform specific ++ * quirks: ++ * ++ * * support for two regulators - one for the CPU core and one ++ * for the memory interface ++ * * reboot handler to setup the reboot frequency ++ * * handling of read margin registers ++ * ++ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd ++ * Copyright (C) 2023 Collabora Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cpufreq-dt.h" ++ ++#define RK3588_MEMCFG_HSSPRF_LOW 0x20 ++#define RK3588_MEMCFG_HSDPRF_LOW 0x28 ++#define RK3588_MEMCFG_HSDPRF_HIGH 0x2c ++#define RK3588_CPU_CTRL 0x30 ++ ++#define VOLT_RM_TABLE_END ~1 ++ ++static struct platform_device *cpufreq_pdev; ++static LIST_HEAD(priv_list); ++ ++struct volt_rm_table { ++ uint32_t volt; ++ uint32_t rm; ++}; ++ ++struct rockchip_opp_info { ++ const struct rockchip_opp_data *data; ++ struct volt_rm_table *volt_rm_tbl; ++ struct regmap *grf; ++ u32 current_rm; ++ u32 reboot_freq; ++}; ++ ++struct private_data { ++ struct list_head node; ++ ++ cpumask_var_t cpus; ++ struct device *cpu_dev; ++ struct cpufreq_frequency_table *freq_table; ++}; ++ ++struct rockchip_opp_data { ++ int (*set_read_margin)(struct device *dev, struct rockchip_opp_info *opp_info, ++ unsigned long volt); ++}; ++ ++struct cluster_info { ++ struct list_head list_head; ++ struct rockchip_opp_info opp_info; ++ cpumask_t cpus; ++}; ++static LIST_HEAD(cluster_info_list); ++ ++static int rk3588_cpu_set_read_margin(struct device *dev, struct rockchip_opp_info *opp_info, ++ unsigned long volt) ++{ ++ bool is_found = false; ++ u32 rm; ++ int i; ++ ++ if (!opp_info->volt_rm_tbl) ++ return 0; ++ ++ for (i = 0; opp_info->volt_rm_tbl[i].rm != VOLT_RM_TABLE_END; i++) { ++ if (volt >= opp_info->volt_rm_tbl[i].volt) { ++ rm = opp_info->volt_rm_tbl[i].rm; ++ is_found = true; ++ break; ++ } ++ } ++ ++ if (!is_found) ++ return 0; ++ if (rm == opp_info->current_rm) ++ return 0; ++ if (!opp_info->grf) ++ return 0; ++ ++ dev_dbg(dev, "set rm to %d\n", rm); ++ regmap_write(opp_info->grf, RK3588_MEMCFG_HSSPRF_LOW, 0x001c0000 | (rm << 2)); ++ regmap_write(opp_info->grf, RK3588_MEMCFG_HSDPRF_LOW, 0x003c0000 | (rm << 2)); ++ regmap_write(opp_info->grf, RK3588_MEMCFG_HSDPRF_HIGH, 0x003c0000 | (rm << 2)); ++ regmap_write(opp_info->grf, RK3588_CPU_CTRL, 0x00200020); ++ udelay(1); ++ regmap_write(opp_info->grf, RK3588_CPU_CTRL, 0x00200000); ++ ++ opp_info->current_rm = rm; ++ ++ return 0; ++} ++ ++static const struct rockchip_opp_data rk3588_cpu_opp_data = { ++ .set_read_margin = rk3588_cpu_set_read_margin, ++}; ++ ++static const struct of_device_id rockchip_cpufreq_of_match[] = { ++ { ++ .compatible = "rockchip,rk3588", ++ .data = (void *)&rk3588_cpu_opp_data, ++ }, ++ {}, ++}; ++ ++static struct cluster_info *rockchip_cluster_info_lookup(int cpu) ++{ ++ struct cluster_info *cluster; ++ ++ list_for_each_entry(cluster, &cluster_info_list, list_head) { ++ if (cpumask_test_cpu(cpu, &cluster->cpus)) ++ return cluster; ++ } ++ ++ return NULL; ++} ++ ++static int rockchip_cpufreq_set_volt(struct device *dev, ++ struct regulator *reg, ++ struct dev_pm_opp_supply *supply) ++{ ++ int ret; ++ ++ ret = regulator_set_voltage_triplet(reg, supply->u_volt_min, ++ supply->u_volt, supply->u_volt_max); ++ if (ret) ++ dev_err(dev, "%s: failed to set voltage (%lu %lu %lu uV): %d\n", ++ __func__, supply->u_volt_min, supply->u_volt, ++ supply->u_volt_max, ret); ++ ++ return ret; ++} ++ ++static int rockchip_cpufreq_set_read_margin(struct device *dev, ++ struct rockchip_opp_info *opp_info, ++ unsigned long volt) ++{ ++ if (opp_info->data && opp_info->data->set_read_margin) { ++ opp_info->data->set_read_margin(dev, opp_info, volt); ++ } ++ ++ return 0; ++} ++ ++static int rk_opp_config_regulators(struct device *dev, ++ struct dev_pm_opp *old_opp, struct dev_pm_opp *new_opp, ++ struct regulator **regulators, unsigned int count) ++{ ++ struct dev_pm_opp_supply old_supplies[2]; ++ struct dev_pm_opp_supply new_supplies[2]; ++ struct regulator *vdd_reg = regulators[0]; ++ struct regulator *mem_reg = regulators[1]; ++ struct rockchip_opp_info *opp_info; ++ struct cluster_info *cluster; ++ int ret = 0; ++ unsigned long old_freq = dev_pm_opp_get_freq(old_opp); ++ unsigned long new_freq = dev_pm_opp_get_freq(new_opp); ++ ++ /* We must have two regulators here */ ++ WARN_ON(count != 2); ++ ++ ret = dev_pm_opp_get_supplies(old_opp, old_supplies); ++ if (ret) ++ return ret; ++ ++ ret = dev_pm_opp_get_supplies(new_opp, new_supplies); ++ if (ret) ++ return ret; ++ ++ cluster = rockchip_cluster_info_lookup(dev->id); ++ if (!cluster) ++ return -EINVAL; ++ opp_info = &cluster->opp_info; ++ ++ if (new_freq >= old_freq) { ++ ret = rockchip_cpufreq_set_volt(dev, mem_reg, &new_supplies[1]); ++ if (ret) ++ goto error; ++ ret = rockchip_cpufreq_set_volt(dev, vdd_reg, &new_supplies[0]); ++ if (ret) ++ goto error; ++ rockchip_cpufreq_set_read_margin(dev, opp_info, new_supplies[0].u_volt); ++ } else { ++ rockchip_cpufreq_set_read_margin(dev, opp_info, new_supplies[0].u_volt); ++ ret = rockchip_cpufreq_set_volt(dev, vdd_reg, &new_supplies[0]); ++ if (ret) ++ goto error; ++ ret = rockchip_cpufreq_set_volt(dev, mem_reg, &new_supplies[1]); ++ if (ret) ++ goto error; ++ } ++ ++ return 0; ++ ++error: ++ rockchip_cpufreq_set_read_margin(dev, opp_info, old_supplies[0].u_volt); ++ rockchip_cpufreq_set_volt(dev, mem_reg, &old_supplies[1]); ++ rockchip_cpufreq_set_volt(dev, vdd_reg, &old_supplies[0]); ++ return ret; ++} ++ ++static void rockchip_get_opp_data(const struct of_device_id *matches, ++ struct rockchip_opp_info *info) ++{ ++ const struct of_device_id *match; ++ struct device_node *node; ++ ++ node = of_find_node_by_path("/"); ++ match = of_match_node(matches, node); ++ if (match && match->data) ++ info->data = match->data; ++ of_node_put(node); ++} ++ ++static int rockchip_get_volt_rm_table(struct device *dev, struct device_node *np, ++ char *porp_name, struct volt_rm_table **table) ++{ ++ struct volt_rm_table *rm_table; ++ const struct property *prop; ++ int count, i; ++ ++ prop = of_find_property(np, porp_name, NULL); ++ if (!prop) ++ return -EINVAL; ++ ++ if (!prop->value) ++ return -ENODATA; ++ ++ count = of_property_count_u32_elems(np, porp_name); ++ if (count < 0) ++ return -EINVAL; ++ ++ if (count % 2) ++ return -EINVAL; ++ ++ rm_table = devm_kzalloc(dev, sizeof(*rm_table) * (count / 2 + 1), ++ GFP_KERNEL); ++ if (!rm_table) ++ return -ENOMEM; ++ ++ for (i = 0; i < count / 2; i++) { ++ of_property_read_u32_index(np, porp_name, 2 * i, ++ &rm_table[i].volt); ++ of_property_read_u32_index(np, porp_name, 2 * i + 1, ++ &rm_table[i].rm); ++ } ++ ++ rm_table[i].volt = 0; ++ rm_table[i].rm = VOLT_RM_TABLE_END; ++ ++ *table = rm_table; ++ ++ return 0; ++} ++ ++static int rockchip_cpufreq_reboot(struct notifier_block *notifier, unsigned long event, void *cmd) ++{ ++ struct cluster_info *cluster; ++ struct device *dev; ++ int freq, ret, cpu; ++ ++ if (event != SYS_RESTART) ++ return NOTIFY_DONE; ++ ++ for_each_possible_cpu(cpu) { ++ cluster = rockchip_cluster_info_lookup(cpu); ++ if (!cluster) ++ continue; ++ ++ dev = get_cpu_device(cpu); ++ if (!dev) ++ continue; ++ ++ freq = cluster->opp_info.reboot_freq; ++ ++ if (freq) { ++ ret = dev_pm_opp_set_rate(dev, freq); ++ if (ret) ++ dev_err(dev, "Failed setting reboot freq for cpu %d to %d: %d\n", ++ cpu, freq, ret); ++ dev_pm_opp_remove_table(dev); ++ } ++ } ++ ++ return NOTIFY_DONE; ++} ++ ++static int rockchip_cpufreq_cluster_init(int cpu, struct cluster_info *cluster) ++{ ++ struct rockchip_opp_info *opp_info = &cluster->opp_info; ++ int reg_table_token = -EINVAL; ++ int opp_table_token = -EINVAL; ++ struct device_node *np; ++ struct device *dev; ++ const char * const reg_names[] = { "cpu", "mem", NULL }; ++ int ret = 0; ++ ++ dev = get_cpu_device(cpu); ++ if (!dev) ++ return -ENODEV; ++ ++ if (!of_find_property(dev->of_node, "cpu-supply", NULL)) ++ return -ENOENT; ++ ++ np = of_parse_phandle(dev->of_node, "operating-points-v2", 0); ++ if (!np) { ++ dev_warn(dev, "OPP-v2 not supported\n"); ++ return -ENOENT; ++ } ++ ++ reg_table_token = dev_pm_opp_set_regulators(dev, reg_names); ++ if (reg_table_token < 0) { ++ ret = reg_table_token; ++ dev_err_probe(dev, ret, "Failed to set opp regulators\n"); ++ goto np_err; ++ } ++ ++ ret = dev_pm_opp_of_get_sharing_cpus(dev, &cluster->cpus); ++ if (ret) { ++ dev_err_probe(dev, ret, "Failed to get sharing cpus\n"); ++ goto np_err; ++ } ++ ++ rockchip_get_opp_data(rockchip_cpufreq_of_match, opp_info); ++ if (opp_info->data && opp_info->data->set_read_margin) { ++ opp_info->current_rm = UINT_MAX; ++ opp_info->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); ++ if (IS_ERR(opp_info->grf)) ++ opp_info->grf = NULL; ++ rockchip_get_volt_rm_table(dev, np, "rockchip,volt-mem-read-margin", &opp_info->volt_rm_tbl); ++ ++ of_property_read_u32(np, "rockchip,reboot-freq", &opp_info->reboot_freq); ++ } ++ ++ opp_table_token = dev_pm_opp_set_config_regulators(dev, rk_opp_config_regulators); ++ if (opp_table_token < 0) { ++ ret = opp_table_token; ++ dev_err(dev, "Failed to set opp config regulators\n"); ++ goto reg_opp_table; ++ } ++ ++ of_node_put(np); ++ ++ return 0; ++ ++reg_opp_table: ++ if (reg_table_token >= 0) ++ dev_pm_opp_put_regulators(reg_table_token); ++np_err: ++ of_node_put(np); ++ ++ return ret; ++} ++ ++static struct notifier_block rockchip_cpufreq_reboot_notifier = { ++ .notifier_call = rockchip_cpufreq_reboot, ++ .priority = 0, ++}; ++ ++static struct freq_attr *cpufreq_rockchip_attr[] = { ++ &cpufreq_freq_attr_scaling_available_freqs, ++ NULL, ++}; ++ ++static int cpufreq_online(struct cpufreq_policy *policy) ++{ ++ /* We did light-weight tear down earlier, nothing to do here */ ++ return 0; ++} ++ ++static int cpufreq_offline(struct cpufreq_policy *policy) ++{ ++ /* ++ * Preserve policy->driver_data and don't free resources on light-weight ++ * tear down. ++ */ ++ return 0; ++} ++ ++static struct private_data *rockchip_cpufreq_find_data(int cpu) ++{ ++ struct private_data *priv; ++ ++ list_for_each_entry(priv, &priv_list, node) { ++ if (cpumask_test_cpu(cpu, priv->cpus)) ++ return priv; ++ } ++ ++ return NULL; ++} ++ ++static int cpufreq_init(struct cpufreq_policy *policy) ++{ ++ struct private_data *priv; ++ struct device *cpu_dev; ++ struct clk *cpu_clk; ++ unsigned int transition_latency; ++ int ret; ++ ++ priv = rockchip_cpufreq_find_data(policy->cpu); ++ if (!priv) { ++ pr_err("failed to find data for cpu%d\n", policy->cpu); ++ return -ENODEV; ++ } ++ cpu_dev = priv->cpu_dev; ++ ++ cpu_clk = clk_get(cpu_dev, NULL); ++ if (IS_ERR(cpu_clk)) { ++ ret = PTR_ERR(cpu_clk); ++ dev_err(cpu_dev, "%s: failed to get clk: %d\n", __func__, ret); ++ return ret; ++ } ++ ++ transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev); ++ if (!transition_latency) ++ transition_latency = CPUFREQ_ETERNAL; ++ ++ cpumask_copy(policy->cpus, priv->cpus); ++ policy->driver_data = priv; ++ policy->clk = cpu_clk; ++ policy->freq_table = priv->freq_table; ++ policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000; ++ policy->cpuinfo.transition_latency = transition_latency; ++ policy->dvfs_possible_from_any_cpu = true; ++ ++ return 0; ++} ++ ++static int cpufreq_exit(struct cpufreq_policy *policy) ++{ ++ clk_put(policy->clk); ++ return 0; ++} ++ ++static int set_target(struct cpufreq_policy *policy, unsigned int index) ++{ ++ struct private_data *priv = policy->driver_data; ++ unsigned long freq = policy->freq_table[index].frequency; ++ ++ return dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000); ++} ++ ++static struct cpufreq_driver rockchip_cpufreq_driver = { ++ .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK | ++ CPUFREQ_IS_COOLING_DEV | ++ CPUFREQ_HAVE_GOVERNOR_PER_POLICY, ++ .verify = cpufreq_generic_frequency_table_verify, ++ .target_index = set_target, ++ .get = cpufreq_generic_get, ++ .init = cpufreq_init, ++ .exit = cpufreq_exit, ++ .online = cpufreq_online, ++ .offline = cpufreq_offline, ++ .register_em = cpufreq_register_em_with_opp, ++ .name = "rockchip-cpufreq", ++ .attr = cpufreq_rockchip_attr, ++ .suspend = cpufreq_generic_suspend, ++}; ++ ++static int rockchip_cpufreq_init(struct device *dev, int cpu) ++{ ++ struct private_data *priv; ++ struct device *cpu_dev; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ if (!alloc_cpumask_var(&priv->cpus, GFP_KERNEL)) ++ return -ENOMEM; ++ ++ cpumask_set_cpu(cpu, priv->cpus); ++ ++ cpu_dev = get_cpu_device(cpu); ++ if (!cpu_dev) ++ return -EPROBE_DEFER; ++ priv->cpu_dev = cpu_dev; ++ ++ ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, priv->cpus); ++ if (ret) ++ return ret; ++ ++ ret = dev_pm_opp_of_cpumask_add_table(priv->cpus); ++ if (ret) ++ return ret; ++ ++ ret = dev_pm_opp_get_opp_count(cpu_dev); ++ if (ret <= 0) ++ return dev_err_probe(cpu_dev, -ENODEV, "OPP table can't be empty\n"); ++ ++ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &priv->freq_table); ++ if (ret) ++ return dev_err_probe(cpu_dev, ret, "failed to init cpufreq table\n"); ++ ++ list_add(&priv->node, &priv_list); ++ ++ return 0; ++} ++ ++static void rockchip_cpufreq_free_list(void *data) ++{ ++ struct cluster_info *cluster, *pos; ++ ++ list_for_each_entry_safe(cluster, pos, &cluster_info_list, list_head) { ++ list_del(&cluster->list_head); ++ } ++} ++ ++static int rockchip_cpufreq_init_list(struct device *dev) ++{ ++ struct cluster_info *cluster; ++ int cpu, ret; ++ ++ for_each_possible_cpu(cpu) { ++ cluster = rockchip_cluster_info_lookup(cpu); ++ if (cluster) ++ continue; ++ ++ cluster = devm_kzalloc(dev, sizeof(*cluster), GFP_KERNEL); ++ if (!cluster) { ++ ret = -ENOMEM; ++ goto release_cluster_info; ++ } ++ ++ ret = rockchip_cpufreq_cluster_init(cpu, cluster); ++ if (ret) { ++ dev_err_probe(dev, ret, "Failed to initialize dvfs info cpu%d\n", cpu); ++ goto release_cluster_info; ++ } ++ list_add(&cluster->list_head, &cluster_info_list); ++ } ++ ++ return 0; ++ ++release_cluster_info: ++ rockchip_cpufreq_free_list(NULL); ++ return ret; ++} ++ ++static void rockchip_cpufreq_unregister(void *data) ++{ ++ cpufreq_unregister_driver(&rockchip_cpufreq_driver); ++} ++ ++static int rockchip_cpufreq_probe(struct platform_device *pdev) ++{ ++ int ret, cpu; ++ ++ ret = rockchip_cpufreq_init_list(&pdev->dev); ++ if (ret) ++ return ret; ++ ++ ret = devm_add_action_or_reset(&pdev->dev, rockchip_cpufreq_free_list, NULL); ++ if (ret) ++ return ret; ++ ++ ret = devm_register_reboot_notifier(&pdev->dev, &rockchip_cpufreq_reboot_notifier); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, "Failed to register reboot handler\n"); ++ ++ for_each_possible_cpu(cpu) { ++ ret = rockchip_cpufreq_init(&pdev->dev, cpu); ++ if (ret) ++ return ret; ++ } ++ ++ ret = cpufreq_register_driver(&rockchip_cpufreq_driver); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, "failed register driver\n"); ++ ++ ret = devm_add_action_or_reset(&pdev->dev, rockchip_cpufreq_unregister, NULL); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static struct platform_driver rockchip_cpufreq_platdrv = { ++ .driver = { ++ .name = "rockchip-cpufreq", ++ }, ++ .probe = rockchip_cpufreq_probe, ++}; ++ ++static int __init rockchip_cpufreq_driver_init(void) ++{ ++ int ret; ++ ++ if (!of_machine_is_compatible("rockchip,rk3588") && ++ !of_machine_is_compatible("rockchip,rk3588s")) { ++ return -ENODEV; ++ } ++ ++ ret = platform_driver_register(&rockchip_cpufreq_platdrv); ++ if (ret) ++ return ret; ++ ++ cpufreq_pdev = platform_device_register_data(NULL, "rockchip-cpufreq", -1, ++ NULL, 0); ++ if (IS_ERR(cpufreq_pdev)) { ++ pr_err("failed to register rockchip-cpufreq platform device\n"); ++ ret = PTR_ERR(cpufreq_pdev); ++ goto unregister_platform_driver; ++ } ++ ++ return 0; ++ ++unregister_platform_driver: ++ platform_driver_unregister(&rockchip_cpufreq_platdrv); ++ return ret; ++} ++module_init(rockchip_cpufreq_driver_init); ++ ++static void __exit rockchip_cpufreq_driver_exit(void) ++{ ++ platform_device_unregister(cpufreq_pdev); ++ platform_driver_unregister(&rockchip_cpufreq_platdrv); ++} ++module_exit(rockchip_cpufreq_driver_exit) ++ ++MODULE_AUTHOR("Finley Xiao "); ++MODULE_DESCRIPTION("Rockchip cpufreq driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile +index ce715562e..8354e4879 100644 +--- a/drivers/gpu/drm/bridge/synopsys/Makefile ++++ b/drivers/gpu/drm/bridge/synopsys/Makefile +@@ -1,5 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0-only +-obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o ++obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o dw-hdmi-qp.o + obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o + obj-$(CONFIG_DRM_DW_HDMI_GP_AUDIO) += dw-hdmi-gp-audio.o + obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +new file mode 100644 +index 000000000..935b116c2 +--- /dev/null ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +@@ -0,0 +1,2997 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) Rockchip Electronics Co.Ltd ++ * Author: ++ * Algea Cao ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "dw-hdmi-qp.h" ++// #include "dw-hdmi-qp-audio.h" ++// #include "dw-hdmi-qp-cec.h" ++ ++#include ++ ++#define DDC_CI_ADDR 0x37 ++#define DDC_SEGMENT_ADDR 0x30 ++ ++#define HDMI_EDID_LEN 512 ++ ++/* DW-HDMI Controller >= 0x200a are at least compliant with SCDC version 1 */ ++#define SCDC_MIN_SOURCE_VERSION 0x1 ++ ++#define HDMI14_MAX_TMDSCLK 340000000 ++#define HDMI20_MAX_TMDSCLK_KHZ 600000 ++ ++static const unsigned int dw_hdmi_cable[] = { ++ EXTCON_DISP_HDMI, ++ EXTCON_NONE, ++}; ++ ++static const struct drm_display_mode dw_hdmi_default_modes[] = { ++ /* 16 - 1920x1080@60Hz 16:9 */ ++ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, ++ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, ++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, ++ /* 2 - 720x480@60Hz 4:3 */ ++ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, ++ 798, 858, 0, 480, 489, 495, 525, 0, ++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, ++ /* 4 - 1280x720@60Hz 16:9 */ ++ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, ++ 1430, 1650, 0, 720, 725, 730, 750, 0, ++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, ++ /* 31 - 1920x1080@50Hz 16:9 */ ++ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, ++ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, ++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, ++ /* 19 - 1280x720@50Hz 16:9 */ ++ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720, ++ 1760, 1980, 0, 720, 725, 730, 750, 0, ++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, ++ /* 17 - 720x576@50Hz 4:3 */ ++ { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, ++ 796, 864, 0, 576, 581, 586, 625, 0, ++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, ++ /* 2 - 720x480@60Hz 4:3 */ ++ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, ++ 798, 858, 0, 480, 489, 495, 525, 0, ++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, ++}; ++ ++enum frl_mask { ++ FRL_3GBPS_3LANE = 1, ++ FRL_6GBPS_3LANE, ++ FRL_6GBPS_4LANE, ++ FRL_8GBPS_4LANE, ++ FRL_10GBPS_4LANE, ++ FRL_12GBPS_4LANE, ++}; ++ ++struct hdmi_vmode_qp { ++ bool mdataenablepolarity; ++ ++ unsigned int previous_pixelclock; ++ unsigned long mpixelclock; ++ unsigned int mpixelrepetitioninput; ++ unsigned int mpixelrepetitionoutput; ++ unsigned long previous_tmdsclock; ++ unsigned int mtmdsclock; ++}; ++ ++struct hdmi_qp_data_info { ++ unsigned int enc_in_bus_format; ++ unsigned int enc_out_bus_format; ++ unsigned int enc_in_encoding; ++ unsigned int enc_out_encoding; ++ unsigned int quant_range; ++ unsigned int pix_repet_factor; ++ struct hdmi_vmode_qp video_mode; ++ bool update; ++}; ++ ++struct dw_hdmi_qp_i2c { ++ struct i2c_adapter adap; ++ ++ struct mutex lock; /* used to serialize data transfers */ ++ struct completion cmp; ++ u32 stat; ++ ++ u8 slave_reg; ++ bool is_regaddr; ++ bool is_segment; ++ ++ unsigned int scl_high_ns; ++ unsigned int scl_low_ns; ++}; ++ ++// struct dw_hdmi_phy_data { ++// enum dw_hdmi_phy_type type; ++// const char *name; ++// unsigned int gen; ++// bool has_svsret; ++// int (*configure)(struct dw_hdmi_qp *hdmi, ++// const struct dw_hdmi_plat_data *pdata, ++// unsigned long mpixelclock); ++// }; ++ ++struct dw_hdmi_qp { ++ struct drm_connector connector; ++ struct drm_bridge bridge; ++ struct platform_device *hdcp_dev; ++ struct platform_device *audio; ++ struct platform_device *cec; ++ struct device *dev; ++ struct dw_hdmi_qp_i2c *i2c; ++ ++ struct hdmi_qp_data_info hdmi_data; ++ const struct dw_hdmi_plat_data *plat_data; ++ ++ int vic; ++ int main_irq; ++ int avp_irq; ++ int earc_irq; ++ ++ u8 edid[HDMI_EDID_LEN]; ++ ++ struct { ++ const struct dw_hdmi_qp_phy_ops *ops; ++ const char *name; ++ void *data; ++ bool enabled; ++ } phy; ++ ++ struct drm_display_mode previous_mode; ++ ++ struct i2c_adapter *ddc; ++ void __iomem *regs; ++ bool sink_is_hdmi; ++ bool sink_has_audio; ++ bool dclk_en; ++ ++ struct mutex mutex; /* for state below and previous_mode */ ++ //[CC:] curr_conn should be removed ++ struct drm_connector *curr_conn;/* current connector (only valid when !disabled) */ ++ enum drm_connector_force force; /* mutex-protected force state */ ++ bool disabled; /* DRM has disabled our bridge */ ++ bool bridge_is_on; /* indicates the bridge is on */ ++ bool rxsense; /* rxsense state */ ++ u8 phy_mask; /* desired phy int mask settings */ ++ u8 mc_clkdis; /* clock disable register */ ++ ++ u32 scdc_intr; ++ u32 flt_intr; ++ u32 earc_intr; ++ ++ struct mutex audio_mutex; ++ unsigned int sample_rate; ++ unsigned int audio_cts; ++ unsigned int audio_n; ++ bool audio_enable; ++ void (*enable_audio)(struct dw_hdmi_qp *hdmi); ++ void (*disable_audio)(struct dw_hdmi_qp *hdmi); ++ ++ struct dentry *debugfs_dir; ++ bool scramble_low_rates; ++ ++ struct extcon_dev *extcon; ++ ++ struct regmap *regm; ++ ++ bool initialized; /* hdmi is enabled before bind */ ++ struct completion flt_cmp; ++ struct completion earc_cmp; ++ ++ struct cec_notifier *cec_notifier; ++ struct cec_adapter *cec_adap; ++ struct mutex cec_notifier_mutex; ++ ++ hdmi_codec_plugged_cb plugged_cb; ++ struct device *codec_dev; ++ enum drm_connector_status last_connector_result; ++}; ++ ++static inline void hdmi_writel(struct dw_hdmi_qp *hdmi, u32 val, int offset) ++{ ++ regmap_write(hdmi->regm, offset, val); ++} ++ ++static inline u32 hdmi_readl(struct dw_hdmi_qp *hdmi, int offset) ++{ ++ unsigned int val = 0; ++ ++ regmap_read(hdmi->regm, offset, &val); ++ ++ return val; ++} ++ ++static void handle_plugged_change(struct dw_hdmi_qp *hdmi, bool plugged) ++{ ++ if (hdmi->plugged_cb && hdmi->codec_dev) ++ hdmi->plugged_cb(hdmi->codec_dev, plugged); ++} ++ ++int dw_hdmi_qp_set_plugged_cb(struct dw_hdmi_qp *hdmi, hdmi_codec_plugged_cb fn, ++ struct device *codec_dev) ++{ ++ bool plugged; ++ ++ mutex_lock(&hdmi->mutex); ++ hdmi->plugged_cb = fn; ++ hdmi->codec_dev = codec_dev; ++ plugged = hdmi->last_connector_result == connector_status_connected; ++ handle_plugged_change(hdmi, plugged); ++ mutex_unlock(&hdmi->mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_plugged_cb); ++ ++static void hdmi_modb(struct dw_hdmi_qp *hdmi, u32 data, u32 mask, u32 reg) ++{ ++ regmap_update_bits(hdmi->regm, reg, mask, data); ++} ++ ++static void hdmi_set_cts_n(struct dw_hdmi_qp *hdmi, unsigned int cts, ++ unsigned int n) ++{ ++ /* Set N */ ++ hdmi_modb(hdmi, n, AUDPKT_ACR_N_VALUE, AUDPKT_ACR_CONTROL0); ++ ++ /* Set CTS */ ++ if (cts) ++ hdmi_modb(hdmi, AUDPKT_ACR_CTS_OVR_EN, AUDPKT_ACR_CTS_OVR_EN_MSK, ++ AUDPKT_ACR_CONTROL1); ++ else ++ hdmi_modb(hdmi, 0, AUDPKT_ACR_CTS_OVR_EN_MSK, ++ AUDPKT_ACR_CONTROL1); ++ ++ hdmi_modb(hdmi, AUDPKT_ACR_CTS_OVR_VAL(cts), AUDPKT_ACR_CTS_OVR_VAL_MSK, ++ AUDPKT_ACR_CONTROL1); ++} ++ ++// static int hdmi_match_tmds_n_table(struct dw_hdmi_qp *hdmi, ++// unsigned long pixel_clk, ++// unsigned long freq) ++// { ++// const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data; ++// const struct dw_hdmi_audio_tmds_n *tmds_n = NULL; ++// int i; ++// ++// if (plat_data->tmds_n_table) { ++// for (i = 0; plat_data->tmds_n_table[i].tmds != 0; i++) { ++// if (pixel_clk == plat_data->tmds_n_table[i].tmds) { ++// tmds_n = &plat_data->tmds_n_table[i]; ++// break; ++// } ++// } ++// } ++// ++// if (tmds_n == NULL) { ++// for (i = 0; common_tmds_n_table[i].tmds != 0; i++) { ++// if (pixel_clk == common_tmds_n_table[i].tmds) { ++// tmds_n = &common_tmds_n_table[i]; ++// break; ++// } ++// } ++// } ++// ++// if (tmds_n == NULL) ++// return -ENOENT; ++// ++// switch (freq) { ++// case 32000: ++// return tmds_n->n_32k; ++// case 44100: ++// case 88200: ++// case 176400: ++// return (freq / 44100) * tmds_n->n_44k1; ++// case 48000: ++// case 96000: ++// case 192000: ++// return (freq / 48000) * tmds_n->n_48k; ++// default: ++// return -ENOENT; ++// } ++// } ++// ++static u64 hdmi_audio_math_diff(unsigned int freq, unsigned int n, ++ unsigned int pixel_clk) ++{ ++ u64 final, diff; ++ u64 cts; ++ ++ final = (u64)pixel_clk * n; ++ ++ cts = final; ++ do_div(cts, 128 * freq); ++ ++ diff = final - (u64)cts * (128 * freq); ++ ++ return diff; ++} ++ ++static unsigned int hdmi_compute_n(struct dw_hdmi_qp *hdmi, ++ unsigned long pixel_clk, ++ unsigned long freq) ++{ ++ unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500); ++ unsigned int max_n = (128 * freq) / 300; ++ unsigned int ideal_n = (128 * freq) / 1000; ++ unsigned int best_n_distance = ideal_n; ++ unsigned int best_n = 0; ++ u64 best_diff = U64_MAX; ++ int n; ++ ++ /* If the ideal N could satisfy the audio math, then just take it */ ++ if (hdmi_audio_math_diff(freq, ideal_n, pixel_clk) == 0) ++ return ideal_n; ++ ++ for (n = min_n; n <= max_n; n++) { ++ u64 diff = hdmi_audio_math_diff(freq, n, pixel_clk); ++ ++ if (diff < best_diff || (diff == best_diff && ++ abs(n - ideal_n) < best_n_distance)) { ++ best_n = n; ++ best_diff = diff; ++ best_n_distance = abs(best_n - ideal_n); ++ } ++ ++ /* ++ * The best N already satisfy the audio math, and also be ++ * the closest value to ideal N, so just cut the loop. ++ */ ++ if ((best_diff == 0) && (abs(n - ideal_n) > best_n_distance)) ++ break; ++ } ++ ++ return best_n; ++} ++ ++static unsigned int hdmi_find_n(struct dw_hdmi_qp *hdmi, unsigned long pixel_clk, ++ unsigned long sample_rate) ++{ ++ int n; ++ ++ // n = hdmi_match_tmds_n_table(hdmi, pixel_clk, sample_rate); ++ n = 0; ++ if (n > 0) ++ return n; ++ ++ dev_warn(hdmi->dev, "Rate %lu missing; compute N dynamically\n", ++ pixel_clk); ++ ++ return hdmi_compute_n(hdmi, pixel_clk, sample_rate); ++} ++ ++/* ++ * When transmitting IEC60958 linear PCM audio, these registers allow to ++ * configure the channel status information of all the channel status ++ * bits in the IEC60958 frame. For the moment this configuration is only ++ * used when the I2S audio interface, General Purpose Audio (GPA), ++ * or AHB audio DMA (AHBAUDDMA) interface is active ++ * (for S/PDIF interface this information comes from the stream). ++ */ ++void dw_hdmi_qp_set_channel_status(struct dw_hdmi_qp *hdmi, ++ u8 *channel_status, bool ref2stream) ++{ ++ mutex_lock(&hdmi->audio_mutex); ++ if (!hdmi->dclk_en) { ++ mutex_unlock(&hdmi->audio_mutex); ++ return; ++ } ++ ++ /* Set channel status */ ++ hdmi_writel(hdmi, channel_status[3] | (channel_status[4] << 8), ++ AUDPKT_CHSTATUS_OVR1); ++ ++ if (ref2stream) ++ hdmi_modb(hdmi, 0, ++ AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK, ++ AUDPKT_CONTROL0); ++ else ++ hdmi_modb(hdmi, AUDPKT_PBIT_FORCE_EN | AUDPKT_CHSTATUS_OVR_EN, ++ AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK, ++ AUDPKT_CONTROL0); ++ ++ mutex_unlock(&hdmi->audio_mutex); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_channel_status); ++ ++static void hdmi_set_clk_regenerator(struct dw_hdmi_qp *hdmi, ++ unsigned long pixel_clk, unsigned int sample_rate) ++{ ++ unsigned int n = 0, cts = 0; ++ ++ n = hdmi_find_n(hdmi, pixel_clk, sample_rate); ++ ++ hdmi->audio_n = n; ++ hdmi->audio_cts = cts; ++ hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0); ++} ++ ++static void hdmi_init_clk_regenerator(struct dw_hdmi_qp *hdmi) ++{ ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->dclk_en) ++ hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate); ++ mutex_unlock(&hdmi->audio_mutex); ++} ++ ++static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi_qp *hdmi) ++{ ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->dclk_en) ++ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock, ++ hdmi->sample_rate); ++ mutex_unlock(&hdmi->audio_mutex); ++} ++ ++void dw_hdmi_qp_set_sample_rate(struct dw_hdmi_qp *hdmi, unsigned int rate) ++{ ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->dclk_en) { ++ hdmi->sample_rate = rate; ++ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock, ++ hdmi->sample_rate); ++ } ++ mutex_unlock(&hdmi->audio_mutex); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_sample_rate); ++ ++void dw_hdmi_qp_set_channel_count(struct dw_hdmi_qp *hdmi, unsigned int cnt) ++{ ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_channel_count); ++ ++void dw_hdmi_qp_set_channel_allocation(struct dw_hdmi_qp *hdmi, unsigned int ca) ++{ ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_channel_allocation); ++ ++void dw_hdmi_qp_set_audio_infoframe(struct dw_hdmi_qp *hdmi, ++ struct hdmi_codec_params *hparms) ++{ ++ u8 infoframe_buf[HDMI_INFOFRAME_SIZE(AUDIO)]; ++ int ret = 0; ++ ++ ret = hdmi_audio_infoframe_pack(&hparms->cea, infoframe_buf, ++ sizeof(infoframe_buf)); ++ if (!ret) { ++ dev_err(hdmi->dev, "%s: Failed to pack audio infoframe: %d\n", ++ __func__, ret); ++ return; ++ } ++ ++ mutex_lock(&hdmi->audio_mutex); ++ if (!hdmi->dclk_en) { ++ mutex_unlock(&hdmi->audio_mutex); ++ return; ++ } ++ ++ /* ++ * AUDI_CONTENTS0: { RSV, HB2, HB1, RSV } ++ * AUDI_CONTENTS1: { PB3, PB2, PB1, PB0 } ++ * AUDI_CONTENTS2: { PB7, PB6, PB5, PB4 } ++ * ++ * PB0: CheckSum ++ * PB1: | CT3 | CT2 | CT1 | CT0 | F13 | CC2 | CC1 | CC0 | ++ * PB2: | F27 | F26 | F25 | SF2 | SF1 | SF0 | SS1 | SS0 | ++ * PB3: | F37 | F36 | F35 | F34 | F33 | F32 | F31 | F30 | ++ * PB4: | CA7 | CA6 | CA5 | CA4 | CA3 | CA2 | CA1 | CA0 | ++ * PB5: | DM_INH | LSV3 | LSV2 | LSV1 | LSV0 | F52 | F51 | F50 | ++ * PB6~PB10: Reserved ++ * ++ * AUDI_CONTENTS0 default value defined by HDMI specification, ++ * and shall only be changed for debug purposes. ++ * So, we only configure payload byte from PB0~PB7(2 word total). ++ */ ++ regmap_bulk_write(hdmi->regm, PKT_AUDI_CONTENTS1, &infoframe_buf[3], 2); ++ ++ /* Enable ACR, AUDI, AMD */ ++ hdmi_modb(hdmi, ++ PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, ++ PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, ++ PKTSCHED_PKT_EN); ++ ++ /* Enable AUDS */ ++ hdmi_modb(hdmi, PKTSCHED_AUDS_TX_EN, PKTSCHED_AUDS_TX_EN, PKTSCHED_PKT_EN); ++ mutex_unlock(&hdmi->audio_mutex); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_audio_infoframe); ++ ++static void hdmi_enable_audio_clk(struct dw_hdmi_qp *hdmi, bool enable) ++{ ++ if (enable) ++ hdmi_modb(hdmi, 0, ++ AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, GLOBAL_SWDISABLE); ++ else ++ hdmi_modb(hdmi, AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, ++ AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, GLOBAL_SWDISABLE); ++} ++ ++// static void dw_hdmi_i2s_audio_enable(struct dw_hdmi_qp *hdmi) ++// { ++// hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); ++// hdmi_enable_audio_clk(hdmi, true); ++// } ++// ++// static void dw_hdmi_i2s_audio_disable(struct dw_hdmi_qp *hdmi) ++// { ++// /* Disable AUDS, ACR, AUDI, AMD */ ++// hdmi_modb(hdmi, 0, ++// PKTSCHED_ACR_TX_EN | PKTSCHED_AUDS_TX_EN | ++// PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, ++// PKTSCHED_PKT_EN); ++// ++// hdmi_enable_audio_clk(hdmi, false); ++// } ++ ++void dw_hdmi_qp_audio_enable(struct dw_hdmi_qp *hdmi) ++{ ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->dclk_en) { ++ hdmi->audio_enable = true; ++ if (hdmi->enable_audio) ++ hdmi->enable_audio(hdmi); ++ } ++ mutex_unlock(&hdmi->audio_mutex); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_audio_enable); ++ ++void dw_hdmi_qp_audio_disable(struct dw_hdmi_qp *hdmi) ++{ ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->dclk_en) { ++ hdmi->audio_enable = false; ++ if (hdmi->disable_audio) ++ hdmi->disable_audio(hdmi); ++ } ++ mutex_unlock(&hdmi->audio_mutex); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_audio_disable); ++ ++static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_YUV12_1X36: ++ case MEDIA_BUS_FMT_YUV16_1X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: ++ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static int hdmi_bus_fmt_color_depth(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ return 8; ++ ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ return 10; ++ ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_YUV12_1X36: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: ++ return 12; ++ ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ case MEDIA_BUS_FMT_YUV16_1X48: ++ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: ++ return 16; ++ ++ default: ++ return 0; ++ } ++} ++ ++static void dw_hdmi_i2c_init(struct dw_hdmi_qp *hdmi) ++{ ++ /* Software reset */ ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ ++ hdmi_writel(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0); ++ ++ hdmi_modb(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0); ++ ++ /* Clear DONE and ERROR interrupts */ ++ hdmi_writel(hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR, ++ MAINUNIT_1_INT_CLEAR); ++} ++ ++static int dw_hdmi_i2c_read(struct dw_hdmi_qp *hdmi, ++ unsigned char *buf, unsigned int length) ++{ ++ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; ++ int stat; ++ ++ if (!i2c->is_regaddr) { ++ dev_dbg(hdmi->dev, "set read register address to 0\n"); ++ i2c->slave_reg = 0x00; ++ i2c->is_regaddr = true; ++ } ++ ++ while (length--) { ++ reinit_completion(&i2c->cmp); ++ ++ hdmi_modb(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR, ++ I2CM_INTERFACE_CONTROL0); ++ ++ hdmi_modb(hdmi, I2CM_FM_READ, I2CM_WR_MASK, ++ I2CM_INTERFACE_CONTROL0); ++ ++ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); ++ if (!stat) { ++ dev_err(hdmi->dev, "i2c read time out!\n"); ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ return -EAGAIN; ++ } ++ ++ /* Check for error condition on the bus */ ++ if (i2c->stat & I2CM_NACK_RCVD_IRQ) { ++ dev_err(hdmi->dev, "i2c read err!\n"); ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ return -EIO; ++ } ++ ++ *buf++ = hdmi_readl(hdmi, I2CM_INTERFACE_RDDATA_0_3) & 0xff; ++ // dev_dbg(hdmi->dev, "i2c read done! i2c->stat:%02x 0x%02x\n", ++ // i2c->stat, hdmi_readl(hdmi, I2CM_INTERFACE_RDDATA_0_3)); ++ hdmi_modb(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); ++ } ++ i2c->is_segment = false; ++ ++ return 0; ++} ++ ++static int dw_hdmi_i2c_write(struct dw_hdmi_qp *hdmi, ++ unsigned char *buf, unsigned int length) ++{ ++ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; ++ int stat; ++ ++ if (!i2c->is_regaddr) { ++ /* Use the first write byte as register address */ ++ i2c->slave_reg = buf[0]; ++ length--; ++ buf++; ++ i2c->is_regaddr = true; ++ } ++ ++ while (length--) { ++ reinit_completion(&i2c->cmp); ++ ++ hdmi_writel(hdmi, *buf++, I2CM_INTERFACE_WRDATA_0_3); ++ hdmi_modb(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR, ++ I2CM_INTERFACE_CONTROL0); ++ hdmi_modb(hdmi, I2CM_FM_WRITE, I2CM_WR_MASK, ++ I2CM_INTERFACE_CONTROL0); ++ ++ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); ++ if (!stat) { ++ dev_err(hdmi->dev, "i2c write time out!\n"); ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ return -EAGAIN; ++ } ++ ++ /* Check for error condition on the bus */ ++ if (i2c->stat & I2CM_NACK_RCVD_IRQ) { ++ dev_err(hdmi->dev, "i2c write nack!\n"); ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ return -EIO; ++ } ++ hdmi_modb(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); ++ } ++ // dev_dbg(hdmi->dev, "i2c write done!\n"); ++ return 0; ++} ++ ++static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap, ++ struct i2c_msg *msgs, int num) ++{ ++ struct dw_hdmi_qp *hdmi = i2c_get_adapdata(adap); ++ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; ++ u8 addr = msgs[0].addr; ++ int i, ret = 0; ++ ++ if (addr == DDC_CI_ADDR) ++ /* ++ * The internal I2C controller does not support the multi-byte ++ * read and write operations needed for DDC/CI. ++ * TOFIX: Blacklist the DDC/CI address until we filter out ++ * unsupported I2C operations. ++ */ ++ return -EOPNOTSUPP; ++ ++ // dev_dbg(hdmi->dev, "i2c xfer: num: %d, addr: %#x\n", num, addr); ++ ++ for (i = 0; i < num; i++) { ++ if (msgs[i].len == 0) { ++ dev_err(hdmi->dev, ++ "unsupported transfer %d/%d, no data\n", ++ i + 1, num); ++ return -EOPNOTSUPP; ++ } ++ } ++ ++ mutex_lock(&i2c->lock); ++ ++ /* Unmute DONE and ERROR interrupts */ ++ hdmi_modb(hdmi, I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, ++ I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, ++ MAINUNIT_1_INT_MASK_N); ++ ++ /* Set slave device address taken from the first I2C message */ ++ if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1) ++ addr = DDC_ADDR; ++ ++ hdmi_modb(hdmi, addr << 5, I2CM_SLVADDR, I2CM_INTERFACE_CONTROL0); ++ ++ /* Set slave device register address on transfer */ ++ i2c->is_regaddr = false; ++ ++ /* Set segment pointer for I2C extended read mode operation */ ++ i2c->is_segment = false; ++ ++ for (i = 0; i < num; i++) { ++ // dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n", ++ // i + 1, num, msgs[i].len, msgs[i].flags); ++ ++ if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) { ++ i2c->is_segment = true; ++ hdmi_modb(hdmi, DDC_SEGMENT_ADDR, I2CM_SEG_ADDR, ++ I2CM_INTERFACE_CONTROL1); ++ hdmi_modb(hdmi, *msgs[i].buf, I2CM_SEG_PTR, ++ I2CM_INTERFACE_CONTROL1); ++ } else { ++ if (msgs[i].flags & I2C_M_RD) ++ ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, ++ msgs[i].len); ++ else ++ ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, ++ msgs[i].len); ++ } ++ if (ret < 0) ++ break; ++ } ++ ++ if (!ret) ++ ret = num; ++ ++ /* Mute DONE and ERROR interrupts */ ++ hdmi_modb(hdmi, 0, I2CM_OP_DONE_MASK_N | I2CM_NACK_RCVD_MASK_N, ++ MAINUNIT_1_INT_MASK_N); ++ ++ mutex_unlock(&i2c->lock); ++ ++ return ret; ++} ++ ++static u32 dw_hdmi_i2c_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; ++} ++ ++static const struct i2c_algorithm dw_hdmi_algorithm = { ++ .master_xfer = dw_hdmi_i2c_xfer, ++ .functionality = dw_hdmi_i2c_func, ++}; ++ ++static struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi_qp *hdmi) ++{ ++ struct i2c_adapter *adap; ++ struct dw_hdmi_qp_i2c *i2c; ++ int ret; ++ ++ i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL); ++ if (!i2c) ++ return ERR_PTR(-ENOMEM); ++ ++ mutex_init(&i2c->lock); ++ init_completion(&i2c->cmp); ++ ++ adap = &i2c->adap; ++ adap->class = I2C_CLASS_DDC; ++ adap->owner = THIS_MODULE; ++ adap->dev.parent = hdmi->dev; ++ adap->algo = &dw_hdmi_algorithm; ++ strscpy(adap->name, "ddc", sizeof(adap->name)); ++ i2c_set_adapdata(adap, hdmi); ++ ++ ret = i2c_add_adapter(adap); ++ if (ret) { ++ dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name); ++ devm_kfree(hdmi->dev, i2c); ++ return ERR_PTR(ret); ++ } ++ ++ hdmi->i2c = i2c; ++ ++ dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name); ++ ++ return adap; ++} ++ ++#define HDMI_PHY_EARC_MASK BIT(29) ++ ++int dw_hdmi_qp_set_earc(struct dw_hdmi_qp *hdmi) ++{ ++ u32 stat, ret; ++ ++ /* set hdmi phy earc mode */ ++ hdmi->phy.ops->set_mode(hdmi, hdmi->phy.data, HDMI_PHY_EARC_MASK, ++ true); ++ ++ ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, ++ &hdmi->previous_mode); ++ if (ret) ++ return ret; ++ ++ reinit_completion(&hdmi->earc_cmp); ++ ++ hdmi_modb(hdmi, EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ | ++ EARCRX_CMDC_DISCOVERY_DONE_IRQ, ++ EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ | ++ EARCRX_CMDC_DISCOVERY_DONE_IRQ, EARCRX_0_INT_MASK_N); ++ ++ /* start discovery */ ++ hdmi_modb(hdmi, EARCRX_CMDC_DISCOVERY_EN, EARCRX_CMDC_DISCOVERY_EN, ++ EARCRX_CMDC_CONTROL); ++ ++ /* ++ * The eARC TX device drives a logic-high-voltage-level ++ * pulse on the physical HPD connector pin, after ++ * at least 100 ms of low voltage level to start the ++ * eARC Discovery process. ++ */ ++ hdmi_modb(hdmi, EARCRX_CONNECTOR_HPD, EARCRX_CONNECTOR_HPD, ++ EARCRX_CMDC_CONTROL); ++ ++ stat = wait_for_completion_timeout(&hdmi->earc_cmp, HZ / 10); ++ if (!stat) ++ return -EAGAIN; ++ ++ if (hdmi->earc_intr & EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ) { ++ dev_err(hdmi->dev, "discovery timeout\n"); ++ return -ETIMEDOUT; ++ } else if (hdmi->earc_intr & EARCRX_CMDC_DISCOVERY_DONE_IRQ) { ++ dev_info(hdmi->dev, "discovery done\n"); ++ } else { ++ dev_err(hdmi->dev, "discovery failed\n"); ++ return -EINVAL; ++ } ++ ++ hdmi_writel(hdmi, 1, EARCRX_DMAC_PHY_CONTROL); ++ hdmi_modb(hdmi, EARCRX_CMDC_SWINIT_P, EARCRX_CMDC_SWINIT_P, ++ EARCRX_CMDC_CONFIG0); ++ ++ hdmi_writel(hdmi, 0xf3, EARCRX_DMAC_CONFIG); ++ hdmi_writel(hdmi, 0x63, EARCRX_DMAC_CONTROL0); ++ hdmi_writel(hdmi, 0xff, EARCRX_DMAC_CONTROL1); ++ ++ hdmi_modb(hdmi, EARCRX_XACTREAD_STOP_CFG | EARCRX_XACTREAD_RETRY_CFG | ++ EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 | EARCRX_CMDC_XACT_RESTART_EN, ++ EARCRX_XACTREAD_STOP_CFG | EARCRX_XACTREAD_RETRY_CFG | ++ EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 | EARCRX_CMDC_XACT_RESTART_EN, ++ EARCRX_CMDC_CONFIG0); ++ ++ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER0); ++ hdmi_writel(hdmi, 0x1b0e, EARCRX_DMAC_CHSTATUS_STREAMER1); ++ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER2); ++ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER3); ++ hdmi_writel(hdmi, 0xf2000000, EARCRX_DMAC_CHSTATUS_STREAMER4); ++ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER5); ++ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER6); ++ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER7); ++ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER8); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_earc); ++ ++/* ----------------------------------------------------------------------------- ++ * HDMI TX Setup ++ */ ++ ++static void hdmi_infoframe_set_checksum(u8 *ptr, int size) ++{ ++ u8 csum = 0; ++ int i; ++ ++ ptr[3] = 0; ++ /* compute checksum */ ++ for (i = 0; i < size; i++) ++ csum += ptr[i]; ++ ++ ptr[3] = 256 - csum; ++} ++ ++static void hdmi_config_AVI(struct dw_hdmi_qp *hdmi, ++ const struct drm_connector *connector, ++ const struct drm_display_mode *mode) ++{ ++ struct hdmi_avi_infoframe frame; ++ u32 val, i, j; ++ u8 buff[17]; ++ enum hdmi_quantization_range rgb_quant_range = ++ hdmi->hdmi_data.quant_range; ++ ++ /* Initialise info frame from DRM mode */ ++ drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode); ++ ++ /* ++ * Ignore monitor selectable quantization, use quantization set ++ * by the user ++ */ ++ drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode, rgb_quant_range); ++ if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) ++ frame.colorspace = HDMI_COLORSPACE_YUV444; ++ else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) ++ frame.colorspace = HDMI_COLORSPACE_YUV422; ++ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ frame.colorspace = HDMI_COLORSPACE_YUV420; ++ else ++ frame.colorspace = HDMI_COLORSPACE_RGB; ++ ++ /* Set up colorimetry */ ++ if (!hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) { ++ switch (hdmi->hdmi_data.enc_out_encoding) { ++ case V4L2_YCBCR_ENC_601: ++ if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV601) ++ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; ++ else ++ frame.colorimetry = HDMI_COLORIMETRY_ITU_601; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; ++ break; ++ case V4L2_YCBCR_ENC_709: ++ if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV709) ++ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; ++ else ++ frame.colorimetry = HDMI_COLORIMETRY_ITU_709; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_XV_YCC_709; ++ break; ++ case V4L2_YCBCR_ENC_BT2020: ++ if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_BT2020) ++ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; ++ else ++ frame.colorimetry = HDMI_COLORIMETRY_ITU_709; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_BT2020; ++ break; ++ default: /* Carries no data */ ++ frame.colorimetry = HDMI_COLORIMETRY_ITU_601; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; ++ break; ++ } ++ } else { ++ if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_BT2020) { ++ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_BT2020; ++ } else { ++ frame.colorimetry = HDMI_COLORIMETRY_NONE; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; ++ } ++ } ++ ++ frame.scan_mode = HDMI_SCAN_MODE_NONE; ++ frame.video_code = hdmi->vic; ++ ++ hdmi_avi_infoframe_pack_only(&frame, buff, 17); ++ ++ /* mode which vic >= 128 must use avi version 3 */ ++ if (hdmi->vic >= 128) { ++ frame.version = 3; ++ buff[1] = frame.version; ++ buff[4] &= 0x1f; ++ buff[4] |= ((frame.colorspace & 0x7) << 5); ++ buff[7] = frame.video_code; ++ hdmi_infoframe_set_checksum(buff, 17); ++ } ++ ++ /* ++ * The Designware IP uses a different byte format from standard ++ * AVI info frames, though generally the bits are in the correct ++ * bytes. ++ */ ++ ++ val = (frame.version << 8) | (frame.length << 16); ++ hdmi_writel(hdmi, val, PKT_AVI_CONTENTS0); ++ ++ for (i = 0; i < 4; i++) { ++ for (j = 0; j < 4; j++) { ++ if (i * 4 + j >= 14) ++ break; ++ if (!j) ++ val = buff[i * 4 + j + 3]; ++ val |= buff[i * 4 + j + 3] << (8 * j); ++ } ++ ++ hdmi_writel(hdmi, val, PKT_AVI_CONTENTS1 + i * 4); ++ } ++ ++ hdmi_modb(hdmi, 0, PKTSCHED_AVI_FIELDRATE, PKTSCHED_PKT_CONFIG1); ++ ++ hdmi_modb(hdmi, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, ++ PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, ++ PKTSCHED_PKT_EN); ++} ++ ++static void hdmi_config_CVTEM(struct dw_hdmi_qp *hdmi) ++{ ++ u8 ds_type = 0; ++ u8 sync = 1; ++ u8 vfr = 1; ++ u8 afr = 0; ++ u8 new = 1; ++ u8 end = 0; ++ u8 data_set_length = 136; ++ u8 hb1[6] = { 0x80, 0, 0, 0, 0, 0x40 }; ++ u8 *pps_body; ++ u32 val, i, reg; ++ struct drm_display_mode *mode = &hdmi->previous_mode; ++ int hsync, hfront, hback; ++ struct dw_hdmi_link_config *link_cfg; ++ void *data = hdmi->plat_data->phy_data; ++ ++ hdmi_modb(hdmi, 0, PKTSCHED_EMP_CVTEM_TX_EN, PKTSCHED_PKT_EN); ++ ++ if (hdmi->plat_data->get_link_cfg) { ++ link_cfg = hdmi->plat_data->get_link_cfg(data); ++ } else { ++ dev_err(hdmi->dev, "can't get frl link cfg\n"); ++ return; ++ } ++ ++ if (!link_cfg->dsc_mode) { ++ dev_info(hdmi->dev, "don't use dsc mode\n"); ++ return; ++ } ++ ++ pps_body = link_cfg->pps_payload; ++ ++ hsync = mode->hsync_end - mode->hsync_start; ++ hback = mode->htotal - mode->hsync_end; ++ hfront = mode->hsync_start - mode->hdisplay; ++ ++ for (i = 0; i < 6; i++) { ++ val = i << 16 | hb1[i] << 8; ++ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS0 + i * 0x20); ++ } ++ ++ val = new << 7 | end << 6 | ds_type << 4 | afr << 3 | ++ vfr << 2 | sync << 1; ++ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS1); ++ ++ val = data_set_length << 16 | pps_body[0] << 24; ++ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS2); ++ ++ reg = PKT0_EMP_CVTEM_CONTENTS3; ++ for (i = 1; i < 125; i++) { ++ if (reg == PKT1_EMP_CVTEM_CONTENTS0 || ++ reg == PKT2_EMP_CVTEM_CONTENTS0 || ++ reg == PKT3_EMP_CVTEM_CONTENTS0 || ++ reg == PKT4_EMP_CVTEM_CONTENTS0 || ++ reg == PKT5_EMP_CVTEM_CONTENTS0) { ++ reg += 4; ++ i--; ++ continue; ++ } ++ if (i % 4 == 1) ++ val = pps_body[i]; ++ if (i % 4 == 2) ++ val |= pps_body[i] << 8; ++ if (i % 4 == 3) ++ val |= pps_body[i] << 16; ++ if (!(i % 4)) { ++ val |= pps_body[i] << 24; ++ hdmi_writel(hdmi, val, reg); ++ reg += 4; ++ } ++ } ++ ++ val = (hfront & 0xff) << 24 | pps_body[127] << 16 | ++ pps_body[126] << 8 | pps_body[125]; ++ hdmi_writel(hdmi, val, PKT4_EMP_CVTEM_CONTENTS6); ++ ++ val = (hback & 0xff) << 24 | ((hsync >> 8) & 0xff) << 16 | ++ (hsync & 0xff) << 8 | ((hfront >> 8) & 0xff); ++ hdmi_writel(hdmi, val, PKT4_EMP_CVTEM_CONTENTS7); ++ ++ val = link_cfg->hcactive << 8 | ((hback >> 8) & 0xff); ++ hdmi_writel(hdmi, val, PKT5_EMP_CVTEM_CONTENTS1); ++ ++ for (i = PKT5_EMP_CVTEM_CONTENTS2; i <= PKT5_EMP_CVTEM_CONTENTS7; i += 4) ++ hdmi_writel(hdmi, 0, i); ++ ++ hdmi_modb(hdmi, PKTSCHED_EMP_CVTEM_TX_EN, PKTSCHED_EMP_CVTEM_TX_EN, ++ PKTSCHED_PKT_EN); ++} ++ ++static void hdmi_config_drm_infoframe(struct dw_hdmi_qp *hdmi, ++ const struct drm_connector *connector) ++{ ++ const struct drm_connector_state *conn_state = connector->state; ++ struct hdr_output_metadata *hdr_metadata; ++ struct hdmi_drm_infoframe frame; ++ u8 buffer[30]; ++ ssize_t err; ++ int i; ++ u32 val; ++ ++ if (!hdmi->plat_data->use_drm_infoframe) ++ return; ++ ++ hdmi_modb(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); ++ ++ if (!hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf) { ++ DRM_DEBUG("No need to set HDR metadata in infoframe\n"); ++ return; ++ } ++ ++ if (!conn_state->hdr_output_metadata) { ++ DRM_DEBUG("source metadata not set yet\n"); ++ return; ++ } ++ ++ hdr_metadata = (struct hdr_output_metadata *) ++ conn_state->hdr_output_metadata->data; ++ ++ if (!(hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf & ++ BIT(hdr_metadata->hdmi_metadata_type1.eotf))) { ++ DRM_ERROR("Not support EOTF %d\n", ++ hdr_metadata->hdmi_metadata_type1.eotf); ++ return; ++ } ++ ++ err = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state); ++ if (err < 0) ++ return; ++ ++ err = hdmi_drm_infoframe_pack(&frame, buffer, sizeof(buffer)); ++ if (err < 0) { ++ dev_err(hdmi->dev, "Failed to pack drm infoframe: %zd\n", err); ++ return; ++ } ++ ++ val = (frame.version << 8) | (frame.length << 16); ++ hdmi_writel(hdmi, val, PKT_DRMI_CONTENTS0); ++ ++ for (i = 0; i <= frame.length; i++) { ++ if (i % 4 == 0) ++ val = buffer[3 + i]; ++ val |= buffer[3 + i] << ((i % 4) * 8); ++ ++ if (i % 4 == 3 || (i == (frame.length))) ++ hdmi_writel(hdmi, val, PKT_DRMI_CONTENTS1 + ((i / 4) * 4)); ++ } ++ ++ hdmi_modb(hdmi, 0, PKTSCHED_DRMI_FIELDRATE, PKTSCHED_PKT_CONFIG1); ++ ++ hdmi_modb(hdmi, PKTSCHED_DRMI_TX_EN, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); ++ ++ DRM_DEBUG("%s eotf %d end\n", __func__, ++ hdr_metadata->hdmi_metadata_type1.eotf); ++} ++ ++/* Filter out invalid setups to avoid configuring SCDC and scrambling */ ++static bool dw_hdmi_support_scdc(struct dw_hdmi_qp *hdmi, ++ const struct drm_display_info *display) ++{ ++ /* Disable if no DDC bus */ ++ if (!hdmi->ddc) ++ return false; ++ ++ /* Disable if SCDC is not supported, or if an HF-VSDB block is absent */ ++ if (!display->hdmi.scdc.supported || ++ !display->hdmi.scdc.scrambling.supported) ++ return false; ++ ++ /* ++ * Disable if display only support low TMDS rates and scrambling ++ * for low rates is not supported either ++ */ ++ if (!display->hdmi.scdc.scrambling.low_rates && ++ display->max_tmds_clock <= 340000) ++ return false; ++ ++ return true; ++} ++ ++static int hdmi_set_frl_mask(int frl_rate) ++{ ++ switch (frl_rate) { ++ case 48: ++ return FRL_12GBPS_4LANE; ++ case 40: ++ return FRL_10GBPS_4LANE; ++ case 32: ++ return FRL_8GBPS_4LANE; ++ case 24: ++ return FRL_6GBPS_4LANE; ++ case 18: ++ return FRL_6GBPS_3LANE; ++ case 9: ++ return FRL_3GBPS_3LANE; ++ } ++ ++ return 0; ++} ++ ++static int hdmi_start_flt(struct dw_hdmi_qp *hdmi, u8 rate) ++{ ++ u8 val; ++ u8 ffe_lv = 0; ++ int i = 0, stat; ++ ++ /* FLT_READY & FFE_LEVELS read */ ++ for (i = 0; i < 20; i++) { ++ drm_scdc_readb(hdmi->ddc, SCDC_STATUS_FLAGS_0, &val); ++ if (val & BIT(6)) ++ break; ++ msleep(20); ++ } ++ ++ if (i == 20) { ++ dev_err(hdmi->dev, "sink flt isn't ready\n"); ++ return -EINVAL; ++ } ++ ++ hdmi_modb(hdmi, SCDC_UPD_FLAGS_RD_IRQ, SCDC_UPD_FLAGS_RD_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ hdmi_modb(hdmi, SCDC_UPD_FLAGS_POLL_EN | SCDC_UPD_FLAGS_AUTO_CLR, ++ SCDC_UPD_FLAGS_POLL_EN | SCDC_UPD_FLAGS_AUTO_CLR, ++ SCDC_CONFIG0); ++ ++ /* max ffe level 3 */ ++ val = 3 << 4 | hdmi_set_frl_mask(rate); ++ drm_scdc_writeb(hdmi->ddc, 0x31, val); ++ ++ /* select FRL_RATE & FFE_LEVELS */ ++ hdmi_writel(hdmi, ffe_lv, FLT_CONFIG0); ++ ++ /* Start LTS_3 state in source DUT */ ++ reinit_completion(&hdmi->flt_cmp); ++ hdmi_modb(hdmi, FLT_EXIT_TO_LTSP_IRQ, FLT_EXIT_TO_LTSP_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ hdmi_writel(hdmi, 1, FLT_CONTROL0); ++ ++ /* wait for completed link training at source side */ ++ stat = wait_for_completion_timeout(&hdmi->flt_cmp, HZ * 2); ++ if (!stat) { ++ dev_err(hdmi->dev, "wait lts3 finish time out\n"); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN | ++ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ return -EAGAIN; ++ } ++ ++ if (!(hdmi->flt_intr & FLT_EXIT_TO_LTSP_IRQ)) { ++ dev_err(hdmi->dev, "not to ltsp\n"); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN | ++ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++#define HDMI_MODE_FRL_MASK BIT(30) ++ ++static void hdmi_set_op_mode(struct dw_hdmi_qp *hdmi, ++ struct dw_hdmi_link_config *link_cfg, ++ const struct drm_connector *connector) ++{ ++ int frl_rate; ++ int i; ++ ++ /* set sink frl mode disable and wait sink ready */ ++ hdmi_writel(hdmi, 0, FLT_CONFIG0); ++ if (dw_hdmi_support_scdc(hdmi, &connector->display_info)) ++ drm_scdc_writeb(hdmi->ddc, 0x31, 0); ++ /* ++ * some TVs must wait a while before switching frl mode resolution, ++ * or the signal may not be recognized. ++ */ ++ msleep(200); ++ ++ if (!link_cfg->frl_mode) { ++ dev_info(hdmi->dev, "dw hdmi qp use tmds mode\n"); ++ hdmi_modb(hdmi, 0, OPMODE_FRL, LINK_CONFIG0); ++ hdmi_modb(hdmi, 0, OPMODE_FRL_4LANES, LINK_CONFIG0); ++ return; ++ } ++ ++ if (link_cfg->frl_lanes == 4) ++ hdmi_modb(hdmi, OPMODE_FRL_4LANES, OPMODE_FRL_4LANES, ++ LINK_CONFIG0); ++ else ++ hdmi_modb(hdmi, 0, OPMODE_FRL_4LANES, LINK_CONFIG0); ++ ++ hdmi_modb(hdmi, 1, OPMODE_FRL, LINK_CONFIG0); ++ ++ frl_rate = link_cfg->frl_lanes * link_cfg->rate_per_lane; ++ hdmi_start_flt(hdmi, frl_rate); ++ ++ for (i = 0; i < 50; i++) { ++ hdmi_modb(hdmi, PKTSCHED_NULL_TX_EN, PKTSCHED_NULL_TX_EN, PKTSCHED_PKT_EN); ++ mdelay(1); ++ hdmi_modb(hdmi, 0, PKTSCHED_NULL_TX_EN, PKTSCHED_PKT_EN); ++ } ++} ++ ++static unsigned long ++hdmi_get_tmdsclock(struct dw_hdmi_qp *hdmi, unsigned long mpixelclock) ++{ ++ unsigned long tmdsclock = mpixelclock; ++ unsigned int depth = ++ hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); ++ ++ if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) { ++ switch (depth) { ++ case 16: ++ tmdsclock = mpixelclock * 2; ++ break; ++ case 12: ++ tmdsclock = mpixelclock * 3 / 2; ++ break; ++ case 10: ++ tmdsclock = mpixelclock * 5 / 4; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return tmdsclock; ++} ++ ++//[CC:] is connector param different from hdmi->connector? ++//[CC:] probably it possible to hook the whole implementation into dw-hdmi.c ++static int dw_hdmi_qp_setup(struct dw_hdmi_qp *hdmi, ++ struct drm_connector *connector, ++ struct drm_display_mode *mode) ++{ ++ int ret; ++ void *data = hdmi->plat_data->phy_data; ++ struct hdmi_vmode_qp *vmode = &hdmi->hdmi_data.video_mode; ++ struct dw_hdmi_link_config *link_cfg; ++ u8 bytes = 0; ++ ++ hdmi->vic = drm_match_cea_mode(mode); ++ if (!hdmi->vic) ++ dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n"); ++ else ++ dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic); ++ ++ if (hdmi->plat_data->get_enc_out_encoding) ++ hdmi->hdmi_data.enc_out_encoding = ++ hdmi->plat_data->get_enc_out_encoding(data); ++ else if ((hdmi->vic == 6) || (hdmi->vic == 7) || ++ (hdmi->vic == 21) || (hdmi->vic == 22) || ++ (hdmi->vic == 2) || (hdmi->vic == 3) || ++ (hdmi->vic == 17) || (hdmi->vic == 18)) ++ hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601; ++ else ++ hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709; ++ ++ if (mode->flags & DRM_MODE_FLAG_DBLCLK) { ++ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1; ++ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1; ++ } else { ++ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0; ++ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0; ++ } ++ /* Get input format from plat data or fallback to RGB888 */ ++ if (hdmi->plat_data->get_input_bus_format) ++ hdmi->hdmi_data.enc_in_bus_format = ++ hdmi->plat_data->get_input_bus_format(data); ++ else if (hdmi->plat_data->input_bus_format) ++ hdmi->hdmi_data.enc_in_bus_format = ++ hdmi->plat_data->input_bus_format; ++ else ++ hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24; ++ ++ /* Default to RGB888 output format */ ++ if (hdmi->plat_data->get_output_bus_format) ++ hdmi->hdmi_data.enc_out_bus_format = ++ hdmi->plat_data->get_output_bus_format(data); ++ else ++ hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24; ++ ++ /* Get input encoding from plat data or fallback to none */ ++ if (hdmi->plat_data->get_enc_in_encoding) ++ hdmi->hdmi_data.enc_in_encoding = ++ hdmi->plat_data->get_enc_in_encoding(data); ++ else if (hdmi->plat_data->input_bus_encoding) ++ hdmi->hdmi_data.enc_in_encoding = ++ hdmi->plat_data->input_bus_encoding; ++ else ++ hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT; ++ ++ if (hdmi->plat_data->get_quant_range) ++ hdmi->hdmi_data.quant_range = ++ hdmi->plat_data->get_quant_range(data); ++ else ++ hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_DEFAULT; ++ ++ if (hdmi->plat_data->get_link_cfg) ++ link_cfg = hdmi->plat_data->get_link_cfg(data); ++ else ++ return -EINVAL; ++ ++ hdmi->phy.ops->set_mode(hdmi, hdmi->phy.data, HDMI_MODE_FRL_MASK, ++ link_cfg->frl_mode); ++ ++ /* ++ * According to the dw-hdmi specification 6.4.2 ++ * vp_pr_cd[3:0]: ++ * 0000b: No pixel repetition (pixel sent only once) ++ * 0001b: Pixel sent two times (pixel repeated once) ++ */ ++ hdmi->hdmi_data.pix_repet_factor = ++ (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0; ++ hdmi->hdmi_data.video_mode.mdataenablepolarity = true; ++ ++ vmode->previous_pixelclock = vmode->mpixelclock; ++ //[CC:] no split mode ++ // if (hdmi->plat_data->split_mode) ++ // mode->crtc_clock /= 2; ++ vmode->mpixelclock = mode->crtc_clock * 1000; ++ if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) ++ vmode->mpixelclock *= 2; ++ dev_dbg(hdmi->dev, "final pixclk = %ld\n", vmode->mpixelclock); ++ vmode->previous_tmdsclock = vmode->mtmdsclock; ++ vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock); ++ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ vmode->mtmdsclock /= 2; ++ dev_info(hdmi->dev, "final tmdsclk = %d\n", vmode->mtmdsclock); ++ ++ ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, &hdmi->previous_mode); ++ if (ret) ++ return ret; ++ ++ if (hdmi->plat_data->set_grf_cfg) ++ hdmi->plat_data->set_grf_cfg(data); ++ ++ if (hdmi->sink_has_audio) { ++ dev_dbg(hdmi->dev, "sink has audio support\n"); ++ ++ /* HDMI Initialization Step E - Configure audio */ ++ hdmi_clk_regenerator_update_pixel_clock(hdmi); ++ hdmi_enable_audio_clk(hdmi, hdmi->audio_enable); ++ } ++ ++ /* not for DVI mode */ ++ if (hdmi->sink_is_hdmi) { ++ dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__); ++ hdmi_modb(hdmi, 0, OPMODE_DVI, LINK_CONFIG0); ++ hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); ++ if (!link_cfg->frl_mode) { ++ if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK) { ++ drm_scdc_readb(hdmi->ddc, SCDC_SINK_VERSION, &bytes); ++ drm_scdc_writeb(hdmi->ddc, SCDC_SOURCE_VERSION, ++ min_t(u8, bytes, SCDC_MIN_SOURCE_VERSION)); ++ //[CC:] use dw_hdmi_set_high_tmds_clock_ratio() ++ drm_scdc_set_high_tmds_clock_ratio(connector, 1); ++ drm_scdc_set_scrambling(connector, 1); ++ hdmi_writel(hdmi, 1, SCRAMB_CONFIG0); ++ } else { ++ if (dw_hdmi_support_scdc(hdmi, &connector->display_info)) { ++ drm_scdc_set_high_tmds_clock_ratio(connector, 0); ++ drm_scdc_set_scrambling(connector, 0); ++ } ++ hdmi_writel(hdmi, 0, SCRAMB_CONFIG0); ++ } ++ } ++ /* HDMI Initialization Step F - Configure AVI InfoFrame */ ++ hdmi_config_AVI(hdmi, connector, mode); ++ hdmi_config_CVTEM(hdmi); ++ hdmi_config_drm_infoframe(hdmi, connector); ++ hdmi_set_op_mode(hdmi, link_cfg, connector); ++ } else { ++ hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); ++ hdmi_modb(hdmi, OPMODE_DVI, OPMODE_DVI, LINK_CONFIG0); ++ dev_info(hdmi->dev, "%s DVI mode\n", __func__); ++ } ++ ++ return 0; ++} ++ ++static enum drm_connector_status ++dw_hdmi_connector_detect(struct drm_connector *connector, bool force) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ struct dw_hdmi_qp *secondary = NULL; ++ enum drm_connector_status result, result_secondary; ++ ++ mutex_lock(&hdmi->mutex); ++ hdmi->force = DRM_FORCE_UNSPECIFIED; ++ mutex_unlock(&hdmi->mutex); ++ ++ if (hdmi->plat_data->left) ++ secondary = hdmi->plat_data->left; ++ else if (hdmi->plat_data->right) ++ secondary = hdmi->plat_data->right; ++ ++ result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); ++ ++ if (secondary) { ++ result_secondary = secondary->phy.ops->read_hpd(secondary, secondary->phy.data); ++ if (result == connector_status_connected && ++ result_secondary == connector_status_connected) ++ result = connector_status_connected; ++ else ++ result = connector_status_disconnected; ++ } ++ ++ mutex_lock(&hdmi->mutex); ++ if (result != hdmi->last_connector_result) { ++ dev_dbg(hdmi->dev, "read_hpd result: %d", result); ++ handle_plugged_change(hdmi, ++ result == connector_status_connected); ++ hdmi->last_connector_result = result; ++ } ++ mutex_unlock(&hdmi->mutex); ++ ++ return result; ++} ++ ++static int ++dw_hdmi_update_hdr_property(struct drm_connector *connector) ++{ ++ struct drm_device *dev = connector->dev; ++ struct dw_hdmi_qp *hdmi = container_of(connector, struct dw_hdmi_qp, ++ connector); ++ void *data = hdmi->plat_data->phy_data; ++ const struct hdr_static_metadata *metadata = ++ &connector->hdr_sink_metadata.hdmi_type1; ++ size_t size = sizeof(*metadata); ++ struct drm_property *property; ++ struct drm_property_blob *blob; ++ int ret; ++ ++ if (hdmi->plat_data->get_hdr_property) ++ property = hdmi->plat_data->get_hdr_property(data); ++ else ++ return -EINVAL; ++ ++ if (hdmi->plat_data->get_hdr_blob) ++ blob = hdmi->plat_data->get_hdr_blob(data); ++ else ++ return -EINVAL; ++ ++ ret = drm_property_replace_global_blob(dev, &blob, size, metadata, ++ &connector->base, property); ++ return ret; ++} ++ ++static int dw_hdmi_connector_get_modes(struct drm_connector *connector) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ struct hdr_static_metadata *metedata = ++ &connector->hdr_sink_metadata.hdmi_type1; ++ struct edid *edid; ++ struct drm_display_mode *mode; ++ struct drm_display_info *info = &connector->display_info; ++ void *data = hdmi->plat_data->phy_data; ++ int i, ret = 0; ++ ++ if (!hdmi->ddc) ++ return 0; ++ ++ memset(metedata, 0, sizeof(*metedata)); ++ edid = drm_get_edid(connector, hdmi->ddc); ++ if (edid) { ++ dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n", ++ edid->width_cm, edid->height_cm); ++ ++ hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid); ++ hdmi->sink_has_audio = drm_detect_monitor_audio(edid); ++ drm_connector_update_edid_property(connector, edid); ++ cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid); ++ if (hdmi->plat_data->get_edid_dsc_info) ++ hdmi->plat_data->get_edid_dsc_info(data, edid); ++ ret = drm_add_edid_modes(connector, edid); ++ dw_hdmi_update_hdr_property(connector); ++ if (ret > 0 && hdmi->plat_data->split_mode) { ++ struct dw_hdmi_qp *secondary = NULL; ++ void *secondary_data; ++ ++ if (hdmi->plat_data->left) ++ secondary = hdmi->plat_data->left; ++ else if (hdmi->plat_data->right) ++ secondary = hdmi->plat_data->right; ++ ++ if (!secondary) ++ return -ENOMEM; ++ secondary_data = secondary->plat_data->phy_data; ++ ++ list_for_each_entry(mode, &connector->probed_modes, head) ++ hdmi->plat_data->convert_to_split_mode(mode); ++ ++ secondary->sink_is_hdmi = drm_detect_hdmi_monitor(edid); ++ secondary->sink_has_audio = drm_detect_monitor_audio(edid); ++ cec_notifier_set_phys_addr_from_edid(secondary->cec_notifier, edid); ++ if (secondary->plat_data->get_edid_dsc_info) ++ secondary->plat_data->get_edid_dsc_info(secondary_data, edid); ++ } ++ kfree(edid); ++ } else { ++ hdmi->sink_is_hdmi = true; ++ hdmi->sink_has_audio = true; ++ ++ if (hdmi->plat_data->split_mode) { ++ if (hdmi->plat_data->left) { ++ hdmi->plat_data->left->sink_is_hdmi = true; ++ hdmi->plat_data->left->sink_has_audio = true; ++ } else if (hdmi->plat_data->right) { ++ hdmi->plat_data->right->sink_is_hdmi = true; ++ hdmi->plat_data->right->sink_has_audio = true; ++ } ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(dw_hdmi_default_modes); i++) { ++ const struct drm_display_mode *ptr = ++ &dw_hdmi_default_modes[i]; ++ ++ mode = drm_mode_duplicate(connector->dev, ptr); ++ if (mode) { ++ if (!i) ++ mode->type = DRM_MODE_TYPE_PREFERRED; ++ drm_mode_probed_add(connector, mode); ++ ret++; ++ } ++ } ++ if (ret > 0 && hdmi->plat_data->split_mode) { ++ struct drm_display_mode *mode; ++ ++ list_for_each_entry(mode, &connector->probed_modes, head) ++ hdmi->plat_data->convert_to_split_mode(mode); ++ } ++ info->edid_hdmi_rgb444_dc_modes = 0; ++ info->hdmi.y420_dc_modes = 0; ++ info->color_formats = 0; ++ ++ dev_info(hdmi->dev, "failed to get edid\n"); ++ } ++ ++ return ret; ++} ++ ++static int ++dw_hdmi_atomic_connector_set_property(struct drm_connector *connector, ++ struct drm_connector_state *state, ++ struct drm_property *property, ++ uint64_t val) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops; ++ ++ if (ops && ops->set_property) ++ return ops->set_property(connector, state, property, ++ val, hdmi->plat_data->phy_data); ++ else ++ return -EINVAL; ++} ++ ++static int ++dw_hdmi_atomic_connector_get_property(struct drm_connector *connector, ++ const struct drm_connector_state *state, ++ struct drm_property *property, ++ uint64_t *val) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops; ++ ++ if (ops && ops->get_property) ++ return ops->get_property(connector, state, property, ++ val, hdmi->plat_data->phy_data); ++ else ++ return -EINVAL; ++} ++ ++static int ++dw_hdmi_connector_set_property(struct drm_connector *connector, ++ struct drm_property *property, uint64_t val) ++{ ++ return dw_hdmi_atomic_connector_set_property(connector, NULL, ++ property, val); ++} ++ ++static void dw_hdmi_attach_properties(struct dw_hdmi_qp *hdmi) ++{ ++ unsigned int color = MEDIA_BUS_FMT_RGB888_1X24; ++ const struct dw_hdmi_property_ops *ops = ++ hdmi->plat_data->property_ops; ++ ++ if (ops && ops->attach_properties) ++ return ops->attach_properties(&hdmi->connector, color, 0, ++ hdmi->plat_data->phy_data); ++} ++ ++static void dw_hdmi_destroy_properties(struct dw_hdmi_qp *hdmi) ++{ ++ const struct dw_hdmi_property_ops *ops = ++ hdmi->plat_data->property_ops; ++ ++ if (ops && ops->destroy_properties) ++ return ops->destroy_properties(&hdmi->connector, ++ hdmi->plat_data->phy_data); ++} ++ ++static struct drm_encoder * ++dw_hdmi_connector_best_encoder(struct drm_connector *connector) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ ++ return hdmi->bridge.encoder; ++} ++ ++static bool dw_hdmi_color_changed(struct drm_connector *connector, ++ struct drm_atomic_state *state) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ void *data = hdmi->plat_data->phy_data; ++ struct drm_connector_state *old_state = ++ drm_atomic_get_old_connector_state(state, connector); ++ struct drm_connector_state *new_state = ++ drm_atomic_get_new_connector_state(state, connector); ++ bool ret = false; ++ ++ if (hdmi->plat_data->get_color_changed) ++ ret = hdmi->plat_data->get_color_changed(data); ++ ++ if (new_state->colorspace != old_state->colorspace) ++ ret = true; ++ ++ return ret; ++} ++ ++static bool hdr_metadata_equal(const struct drm_connector_state *old_state, ++ const struct drm_connector_state *new_state) ++{ ++ struct drm_property_blob *old_blob = old_state->hdr_output_metadata; ++ struct drm_property_blob *new_blob = new_state->hdr_output_metadata; ++ ++ if (!old_blob || !new_blob) ++ return old_blob == new_blob; ++ ++ if (old_blob->length != new_blob->length) ++ return false; ++ ++ return !memcmp(old_blob->data, new_blob->data, old_blob->length); ++} ++ ++static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, ++ struct drm_atomic_state *state) ++{ ++ struct drm_connector_state *old_state = ++ drm_atomic_get_old_connector_state(state, connector); ++ struct drm_connector_state *new_state = ++ drm_atomic_get_new_connector_state(state, connector); ++ struct drm_crtc *crtc = new_state->crtc; ++ struct drm_crtc_state *crtc_state; ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ struct drm_display_mode *mode = NULL; ++ void *data = hdmi->plat_data->phy_data; ++ struct hdmi_vmode_qp *vmode = &hdmi->hdmi_data.video_mode; ++ ++ if (!crtc) ++ return 0; ++ ++ crtc_state = drm_atomic_get_crtc_state(state, crtc); ++ if (IS_ERR(crtc_state)) ++ return PTR_ERR(crtc_state); ++ ++ /* ++ * If HDMI is enabled in uboot, it's need to record ++ * drm_display_mode and set phy status to enabled. ++ */ ++ if (!vmode->mpixelclock) { ++ crtc_state = drm_atomic_get_crtc_state(state, crtc); ++ if (hdmi->plat_data->get_enc_in_encoding) ++ hdmi->hdmi_data.enc_in_encoding = ++ hdmi->plat_data->get_enc_in_encoding(data); ++ if (hdmi->plat_data->get_enc_out_encoding) ++ hdmi->hdmi_data.enc_out_encoding = ++ hdmi->plat_data->get_enc_out_encoding(data); ++ if (hdmi->plat_data->get_input_bus_format) ++ hdmi->hdmi_data.enc_in_bus_format = ++ hdmi->plat_data->get_input_bus_format(data); ++ if (hdmi->plat_data->get_output_bus_format) ++ hdmi->hdmi_data.enc_out_bus_format = ++ hdmi->plat_data->get_output_bus_format(data); ++ ++ mode = &crtc_state->mode; ++ if (hdmi->plat_data->split_mode) { ++ hdmi->plat_data->convert_to_origin_mode(mode); ++ mode->crtc_clock /= 2; ++ } ++ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); ++ vmode->mpixelclock = mode->crtc_clock * 1000; ++ vmode->previous_pixelclock = mode->clock; ++ vmode->previous_tmdsclock = mode->clock; ++ vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, ++ vmode->mpixelclock); ++ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ vmode->mtmdsclock /= 2; ++ ++ /* ++ * If uboot logo enabled, atomic_enable won't be called, ++ * but atomic_disable will be called when hdmi plug out. ++ * That will cause dclk enable count is incorrect. So ++ * we should check ipi/link/video clk to determine whether ++ * uboot logo is enabled. ++ */ ++ if (hdmi->initialized && !hdmi->dclk_en) { ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->plat_data->dclk_set) ++ hdmi->plat_data->dclk_set(data, true); ++ hdmi->dclk_en = true; ++ mutex_unlock(&hdmi->audio_mutex); ++ } ++ } ++ ++ if (!hdr_metadata_equal(old_state, new_state) || ++ dw_hdmi_color_changed(connector, state)) { ++ crtc_state = drm_atomic_get_crtc_state(state, crtc); ++ if (IS_ERR(crtc_state)) ++ return PTR_ERR(crtc_state); ++ ++ crtc_state->mode_changed = true; ++ } ++ ++ return 0; ++} ++ ++static void dw_hdmi_connector_force(struct drm_connector *connector) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ ++ mutex_lock(&hdmi->mutex); ++ ++ if (hdmi->force != connector->force) { ++ if (!hdmi->disabled && connector->force == DRM_FORCE_OFF) ++ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, ++ false); ++ else if (hdmi->disabled && connector->force == DRM_FORCE_ON) ++ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, ++ true); ++ } ++ ++ hdmi->force = connector->force; ++ mutex_unlock(&hdmi->mutex); ++} ++ ++static int dw_hdmi_qp_fill_modes(struct drm_connector *connector, u32 max_x, ++ u32 max_y) ++{ ++ return drm_helper_probe_single_connector_modes(connector, 9000, 9000); ++} ++ ++static const struct drm_connector_funcs dw_hdmi_connector_funcs = { ++ .fill_modes = dw_hdmi_qp_fill_modes, ++ .detect = dw_hdmi_connector_detect, ++ .destroy = drm_connector_cleanup, ++ .force = dw_hdmi_connector_force, ++ .reset = drm_atomic_helper_connector_reset, ++ .set_property = dw_hdmi_connector_set_property, ++ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, ++ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, ++ .atomic_set_property = dw_hdmi_atomic_connector_set_property, ++ .atomic_get_property = dw_hdmi_atomic_connector_get_property, ++}; ++ ++static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { ++ .get_modes = dw_hdmi_connector_get_modes, ++ .best_encoder = dw_hdmi_connector_best_encoder, ++ .atomic_check = dw_hdmi_connector_atomic_check, ++}; ++ ++static int dw_hdmi_qp_bridge_attach(struct drm_bridge *bridge, ++ enum drm_bridge_attach_flags flags) ++{ ++ struct dw_hdmi_qp *hdmi = bridge->driver_private; ++ struct drm_encoder *encoder = bridge->encoder; ++ struct drm_connector *connector = &hdmi->connector; ++ struct cec_connector_info conn_info; ++ struct cec_notifier *notifier; ++ ++ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) ++ return 0; ++ ++ connector->interlace_allowed = 1; ++ connector->polled = DRM_CONNECTOR_POLL_HPD; ++ ++ drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs); ++ ++ // [CC:] use drm_connector_init_with_ddc or drmm_connector_init ++ // to provide ddc reference ++ drm_connector_init_with_ddc(bridge->dev, connector, ++ &dw_hdmi_connector_funcs, ++ DRM_MODE_CONNECTOR_HDMIA, ++ hdmi->ddc); ++ ++ drm_connector_attach_encoder(connector, encoder); ++ dw_hdmi_attach_properties(hdmi); ++ ++ cec_fill_conn_info_from_drm(&conn_info, connector); ++ notifier = cec_notifier_conn_register(hdmi->dev, NULL, &conn_info); ++ if (!notifier) ++ return -ENOMEM; ++ ++ mutex_lock(&hdmi->cec_notifier_mutex); ++ hdmi->cec_notifier = notifier; ++ mutex_unlock(&hdmi->cec_notifier_mutex); ++ ++ return 0; ++} ++ ++static void dw_hdmi_qp_bridge_detach(struct drm_bridge *bridge) ++{ ++ struct dw_hdmi_qp *hdmi = bridge->driver_private; ++ ++ mutex_lock(&hdmi->cec_notifier_mutex); ++ cec_notifier_conn_unregister(hdmi->cec_notifier); ++ hdmi->cec_notifier = NULL; ++ mutex_unlock(&hdmi->cec_notifier_mutex); ++} ++ ++static void dw_hdmi_qp_bridge_mode_set(struct drm_bridge *bridge, ++ const struct drm_display_mode *orig_mode, ++ const struct drm_display_mode *mode) ++{ ++ struct dw_hdmi_qp *hdmi = bridge->driver_private; ++ ++ mutex_lock(&hdmi->mutex); ++ ++ /* Store the display mode for plugin/DKMS poweron events */ ++ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); ++ if (hdmi->plat_data->split_mode) ++ hdmi->plat_data->convert_to_origin_mode(&hdmi->previous_mode); ++ ++ mutex_unlock(&hdmi->mutex); ++} ++ ++static enum drm_mode_status ++dw_hdmi_qp_bridge_mode_valid(struct drm_bridge *bridge, ++ const struct drm_display_info *info, ++ const struct drm_display_mode *mode) ++{ ++ return MODE_OK; ++} ++ ++static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge, ++ struct drm_bridge_state *old_state) ++{ ++ struct dw_hdmi_qp *hdmi = bridge->driver_private; ++ struct drm_atomic_state *state = old_state->base.state; ++ struct drm_connector *connector; ++ void *data = hdmi->plat_data->phy_data; ++ ++ connector = drm_atomic_get_new_connector_for_encoder(state, ++ bridge->encoder); ++ ++ mutex_lock(&hdmi->mutex); ++ hdmi->curr_conn = connector; ++ dw_hdmi_qp_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); ++ hdmi->disabled = false; ++ mutex_unlock(&hdmi->mutex); ++ ++ if (!hdmi->dclk_en) { ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->plat_data->dclk_set) ++ hdmi->plat_data->dclk_set(data, true); ++ hdmi->dclk_en = true; ++ mutex_unlock(&hdmi->audio_mutex); ++ } ++ ++ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, true); ++ handle_plugged_change(hdmi, true); ++} ++ ++static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge, ++ struct drm_bridge_state *old_state) ++{ ++ struct dw_hdmi_qp *hdmi = bridge->driver_private; ++ void *data = hdmi->plat_data->phy_data; ++ ++ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false); ++ handle_plugged_change(hdmi, false); ++ mutex_lock(&hdmi->mutex); ++ ++ hdmi->curr_conn = NULL; ++ ++ if (hdmi->dclk_en) { ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->plat_data->dclk_set) ++ hdmi->plat_data->dclk_set(data, false); ++ hdmi->dclk_en = false; ++ mutex_unlock(&hdmi->audio_mutex); ++ }; ++ ++ if (hdmi->phy.ops->disable) ++ hdmi->phy.ops->disable(hdmi, hdmi->phy.data); ++ hdmi->disabled = true; ++ mutex_unlock(&hdmi->mutex); ++} ++ ++static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { ++ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, ++ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, ++ .atomic_reset = drm_atomic_helper_bridge_reset, ++ .attach = dw_hdmi_qp_bridge_attach, ++ .detach = dw_hdmi_qp_bridge_detach, ++ .mode_set = dw_hdmi_qp_bridge_mode_set, ++ .mode_valid = dw_hdmi_qp_bridge_mode_valid, ++ .atomic_enable = dw_hdmi_qp_bridge_atomic_enable, ++ .atomic_disable = dw_hdmi_qp_bridge_atomic_disable, ++}; ++ ++void dw_hdmi_qp_set_cec_adap(struct dw_hdmi_qp *hdmi, struct cec_adapter *adap) ++{ ++ hdmi->cec_adap = adap; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_cec_adap); ++ ++static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, MAINUNIT_1_INT_STATUS); ++ ++ i2c->stat = stat & (I2CM_OP_DONE_IRQ | I2CM_READ_REQUEST_IRQ | ++ I2CM_NACK_RCVD_IRQ); ++ hdmi->scdc_intr = stat & (SCDC_UPD_FLAGS_RD_IRQ | ++ SCDC_UPD_FLAGS_CHG_IRQ | ++ SCDC_UPD_FLAGS_CLR_IRQ | ++ SCDC_RR_REPLY_STOP_IRQ | ++ SCDC_NACK_RCVD_IRQ); ++ hdmi->flt_intr = stat & (FLT_EXIT_TO_LTSP_IRQ | ++ FLT_EXIT_TO_LTS4_IRQ | ++ FLT_EXIT_TO_LTSL_IRQ); ++ ++ // dev_dbg(hdmi->dev, "i2c main unit irq:%#x\n", stat); ++ if (i2c->stat) { ++ hdmi_writel(hdmi, i2c->stat, MAINUNIT_1_INT_CLEAR); ++ complete(&i2c->cmp); ++ } ++ ++ if (hdmi->flt_intr) { ++ dev_dbg(hdmi->dev, "i2c flt irq:%#x\n", hdmi->flt_intr); ++ hdmi_writel(hdmi, hdmi->flt_intr, MAINUNIT_1_INT_CLEAR); ++ complete(&hdmi->flt_cmp); ++ } ++ ++ if (hdmi->scdc_intr) { ++ u8 val; ++ ++ dev_dbg(hdmi->dev, "i2c scdc irq:%#x\n", hdmi->scdc_intr); ++ hdmi_writel(hdmi, hdmi->scdc_intr, MAINUNIT_1_INT_CLEAR); ++ val = hdmi_readl(hdmi, SCDC_STATUS0); ++ ++ /* frl start */ ++ if (val & BIT(4)) { ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN | ++ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ dev_info(hdmi->dev, "frl start\n"); ++ } ++ ++ } ++ ++ if (stat) ++ return IRQ_HANDLED; ++ ++ return IRQ_NONE; ++} ++ ++static irqreturn_t dw_hdmi_qp_avp_hardirq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, AVP_1_INT_STATUS); ++ if (stat) { ++ dev_dbg(hdmi->dev, "HDCP irq %#x\n", stat); ++ stat &= ~stat; ++ hdmi_writel(hdmi, stat, AVP_1_INT_MASK_N); ++ return IRQ_WAKE_THREAD; ++ } ++ ++ return IRQ_NONE; ++} ++ ++static irqreturn_t dw_hdmi_qp_earc_hardirq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, EARCRX_0_INT_STATUS); ++ if (stat) { ++ dev_dbg(hdmi->dev, "earc irq %#x\n", stat); ++ stat &= ~stat; ++ hdmi_writel(hdmi, stat, EARCRX_0_INT_MASK_N); ++ return IRQ_WAKE_THREAD; ++ } ++ ++ return IRQ_NONE; ++} ++ ++static irqreturn_t dw_hdmi_qp_avp_irq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, AVP_1_INT_STATUS); ++ ++ if (!stat) ++ return IRQ_NONE; ++ ++ hdmi_writel(hdmi, stat, AVP_1_INT_CLEAR); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t dw_hdmi_qp_earc_irq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, EARCRX_0_INT_STATUS); ++ ++ if (!stat) ++ return IRQ_NONE; ++ ++ hdmi_writel(hdmi, stat, EARCRX_0_INT_CLEAR); ++ ++ hdmi->earc_intr = stat; ++ complete(&hdmi->earc_cmp); ++ ++ return IRQ_HANDLED; ++} ++ ++static int dw_hdmi_detect_phy(struct dw_hdmi_qp *hdmi) ++{ ++ u8 phy_type; ++ ++ phy_type = hdmi->plat_data->phy_force_vendor ? ++ DW_HDMI_PHY_VENDOR_PHY : 0; ++ ++ if (phy_type == DW_HDMI_PHY_VENDOR_PHY) { ++ /* Vendor PHYs require support from the glue layer. */ ++ if (!hdmi->plat_data->qp_phy_ops || !hdmi->plat_data->phy_name) { ++ dev_err(hdmi->dev, ++ "Vendor HDMI PHY not supported by glue layer\n"); ++ return -ENODEV; ++ } ++ ++ hdmi->phy.ops = hdmi->plat_data->qp_phy_ops; ++ hdmi->phy.data = hdmi->plat_data->phy_data; ++ hdmi->phy.name = hdmi->plat_data->phy_name; ++ } ++ ++ return 0; ++} ++ ++void dw_hdmi_qp_cec_set_hpd(struct dw_hdmi_qp *hdmi, bool plug_in, bool change) ++{ ++ enum drm_connector_status status = plug_in ? ++ connector_status_connected : connector_status_disconnected; ++ ++ if (!plug_in) ++ cec_notifier_set_phys_addr(hdmi->cec_notifier, ++ CEC_PHYS_ADDR_INVALID); ++ ++ if (hdmi->bridge.dev) { ++ if (change && hdmi->cec_adap && hdmi->cec_adap->devnode.registered) ++ cec_queue_pin_hpd_event(hdmi->cec_adap, plug_in, ktime_get()); ++ drm_bridge_hpd_notify(&hdmi->bridge, status); ++ } ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_cec_set_hpd); ++ ++// static void dw_hdmi_qp_cec_enable(struct dw_hdmi_qp *hdmi) ++// { ++// mutex_lock(&hdmi->mutex); ++// hdmi_modb(hdmi, 0, CEC_SWDISABLE, GLOBAL_SWDISABLE); ++// mutex_unlock(&hdmi->mutex); ++// } ++// ++// static void dw_hdmi_qp_cec_disable(struct dw_hdmi_qp *hdmi) ++// { ++// mutex_lock(&hdmi->mutex); ++// hdmi_modb(hdmi, CEC_SWDISABLE, CEC_SWDISABLE, GLOBAL_SWDISABLE); ++// mutex_unlock(&hdmi->mutex); ++// } ++// ++// static const struct dw_hdmi_qp_cec_ops dw_hdmi_qp_cec_ops = { ++// .enable = dw_hdmi_qp_cec_enable, ++// .disable = dw_hdmi_qp_cec_disable, ++// .write = hdmi_writel, ++// .read = hdmi_readl, ++// }; ++ ++static const struct regmap_config hdmi_regmap_config = { ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = EARCRX_1_INT_FORCE, ++}; ++ ++struct dw_hdmi_qp_reg_table { ++ int reg_base; ++ int reg_end; ++}; ++ ++static const struct dw_hdmi_qp_reg_table hdmi_reg_table[] = { ++ {0x0, 0xc}, ++ {0x14, 0x1c}, ++ {0x44, 0x48}, ++ {0x50, 0x58}, ++ {0x80, 0x84}, ++ {0xa0, 0xc4}, ++ {0xe0, 0xe8}, ++ {0xf0, 0x118}, ++ {0x140, 0x140}, ++ {0x150, 0x150}, ++ {0x160, 0x168}, ++ {0x180, 0x180}, ++ {0x800, 0x800}, ++ {0x808, 0x808}, ++ {0x814, 0x814}, ++ {0x81c, 0x824}, ++ {0x834, 0x834}, ++ {0x840, 0x864}, ++ {0x86c, 0x86c}, ++ {0x880, 0x89c}, ++ {0x8e0, 0x8e8}, ++ {0x900, 0x900}, ++ {0x908, 0x90c}, ++ {0x920, 0x938}, ++ {0x920, 0x938}, ++ {0x960, 0x960}, ++ {0x968, 0x968}, ++ {0xa20, 0xa20}, ++ {0xa30, 0xa30}, ++ {0xa40, 0xa40}, ++ {0xa54, 0xa54}, ++ {0xa80, 0xaac}, ++ {0xab4, 0xab8}, ++ {0xb00, 0xcbc}, ++ {0xce0, 0xce0}, ++ {0xd00, 0xddc}, ++ {0xe20, 0xe24}, ++ {0xe40, 0xe44}, ++ {0xe4c, 0xe4c}, ++ {0xe60, 0xe80}, ++ {0xea0, 0xf24}, ++ {0x1004, 0x100c}, ++ {0x1020, 0x1030}, ++ {0x1040, 0x1050}, ++ {0x1060, 0x1068}, ++ {0x1800, 0x1820}, ++ {0x182c, 0x182c}, ++ {0x1840, 0x1940}, ++ {0x1960, 0x1a60}, ++ {0x1b00, 0x1b00}, ++ {0x1c00, 0x1c00}, ++ {0x3000, 0x3000}, ++ {0x3010, 0x3014}, ++ {0x3020, 0x3024}, ++ {0x3800, 0x3800}, ++ {0x3810, 0x3814}, ++ {0x3820, 0x3824}, ++ {0x3830, 0x3834}, ++ {0x3840, 0x3844}, ++ {0x3850, 0x3854}, ++ {0x3860, 0x3864}, ++ {0x3870, 0x3874}, ++ {0x4000, 0x4004}, ++ {0x4800, 0x4800}, ++ {0x4810, 0x4814}, ++}; ++ ++static int dw_hdmi_ctrl_show(struct seq_file *s, void *v) ++{ ++ struct dw_hdmi_qp *hdmi = s->private; ++ u32 i = 0, j = 0, val = 0; ++ ++ seq_puts(s, "\n---------------------------------------------------"); ++ ++ for (i = 0; i < ARRAY_SIZE(hdmi_reg_table); i++) { ++ for (j = hdmi_reg_table[i].reg_base; ++ j <= hdmi_reg_table[i].reg_end; j += 4) { ++ val = hdmi_readl(hdmi, j); ++ ++ if ((j - hdmi_reg_table[i].reg_base) % 16 == 0) ++ seq_printf(s, "\n>>>hdmi_ctl %04x:", j); ++ seq_printf(s, " %08x", val); ++ } ++ } ++ seq_puts(s, "\n---------------------------------------------------\n"); ++ ++ return 0; ++} ++ ++static int dw_hdmi_ctrl_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, dw_hdmi_ctrl_show, inode->i_private); ++} ++ ++static ssize_t ++dw_hdmi_ctrl_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dw_hdmi_qp *hdmi = ++ ((struct seq_file *)file->private_data)->private; ++ u32 reg, val; ++ char kbuf[25]; ++ ++ if (count > 24) { ++ dev_err(hdmi->dev, "out of buf range\n"); ++ return count; ++ } ++ ++ if (copy_from_user(kbuf, buf, count)) ++ return -EFAULT; ++ kbuf[count - 1] = '\0'; ++ ++ if (sscanf(kbuf, "%x %x", ®, &val) == -1) ++ return -EFAULT; ++ if (reg > EARCRX_1_INT_FORCE) { ++ dev_err(hdmi->dev, "it is no a hdmi register\n"); ++ return count; ++ } ++ dev_info(hdmi->dev, "/**********hdmi register config******/"); ++ dev_info(hdmi->dev, "\n reg=%x val=%x\n", reg, val); ++ hdmi_writel(hdmi, val, reg); ++ return count; ++} ++ ++static const struct file_operations dw_hdmi_ctrl_fops = { ++ .owner = THIS_MODULE, ++ .open = dw_hdmi_ctrl_open, ++ .read = seq_read, ++ .write = dw_hdmi_ctrl_write, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int dw_hdmi_status_show(struct seq_file *s, void *v) ++{ ++ struct dw_hdmi_qp *hdmi = s->private; ++ u32 val; ++ ++ seq_puts(s, "PHY: "); ++ if (hdmi->disabled) { ++ seq_puts(s, "disabled\n"); ++ return 0; ++ } ++ seq_puts(s, "enabled\t\t\tMode: "); ++ if (hdmi->sink_is_hdmi) ++ seq_puts(s, "HDMI\n"); ++ else ++ seq_puts(s, "DVI\n"); ++ ++ if (hdmi->hdmi_data.video_mode.mpixelclock > 600000000) { ++ seq_printf(s, "FRL Mode Pixel Clk: %luHz\n", ++ hdmi->hdmi_data.video_mode.mpixelclock); ++ } else { ++ if (hdmi->hdmi_data.video_mode.mtmdsclock > 340000000) ++ val = hdmi->hdmi_data.video_mode.mtmdsclock / 4; ++ else ++ val = hdmi->hdmi_data.video_mode.mtmdsclock; ++ seq_printf(s, "TMDS Mode Pixel Clk: %luHz\t\tTMDS Clk: %uHz\n", ++ hdmi->hdmi_data.video_mode.mpixelclock, val); ++ } ++ ++ seq_puts(s, "Color Format: "); ++ if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) ++ seq_puts(s, "RGB"); ++ else if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) ++ seq_puts(s, "YUV444"); ++ else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) ++ seq_puts(s, "YUV422"); ++ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ seq_puts(s, "YUV420"); ++ else ++ seq_puts(s, "UNKNOWN"); ++ val = hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); ++ seq_printf(s, "\t\tColor Depth: %d bit\n", val); ++ seq_puts(s, "Colorimetry: "); ++ switch (hdmi->hdmi_data.enc_out_encoding) { ++ case V4L2_YCBCR_ENC_601: ++ seq_puts(s, "ITU.BT601"); ++ break; ++ case V4L2_YCBCR_ENC_709: ++ seq_puts(s, "ITU.BT709"); ++ break; ++ case V4L2_YCBCR_ENC_BT2020: ++ seq_puts(s, "ITU.BT2020"); ++ break; ++ default: /* Carries no data */ ++ seq_puts(s, "ITU.BT601"); ++ break; ++ } ++ ++ seq_puts(s, "\t\tEOTF: "); ++ ++ val = hdmi_readl(hdmi, PKTSCHED_PKT_EN); ++ if (!(val & PKTSCHED_DRMI_TX_EN)) { ++ seq_puts(s, "Off\n"); ++ return 0; ++ } ++ ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS1); ++ val = (val >> 8) & 0x7; ++ switch (val) { ++ case HDMI_EOTF_TRADITIONAL_GAMMA_SDR: ++ seq_puts(s, "SDR"); ++ break; ++ case HDMI_EOTF_TRADITIONAL_GAMMA_HDR: ++ seq_puts(s, "HDR"); ++ break; ++ case HDMI_EOTF_SMPTE_ST2084: ++ seq_puts(s, "ST2084"); ++ break; ++ case HDMI_EOTF_BT_2100_HLG: ++ seq_puts(s, "HLG"); ++ break; ++ default: ++ seq_puts(s, "Not Defined\n"); ++ return 0; ++ } ++ ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS1); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "\nx0: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS2); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\t\ty0: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS2); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "x1: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS3); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\t\ty1: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS3); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "x2: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS4); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\t\ty2: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS4); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "white x: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS5); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\twhite y: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS5); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "max lum: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS6); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\tmin lum: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS6); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "max cll: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS7); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\tmax fall: %d\n", val); ++ return 0; ++} ++ ++static int dw_hdmi_status_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, dw_hdmi_status_show, inode->i_private); ++} ++ ++static const struct file_operations dw_hdmi_status_fops = { ++ .owner = THIS_MODULE, ++ .open = dw_hdmi_status_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static void dw_hdmi_register_debugfs(struct device *dev, struct dw_hdmi_qp *hdmi) ++{ ++ u8 buf[11]; ++ ++ snprintf(buf, sizeof(buf), "dw-hdmi%d", hdmi->plat_data->id); ++ hdmi->debugfs_dir = debugfs_create_dir(buf, NULL); ++ if (IS_ERR(hdmi->debugfs_dir)) { ++ dev_err(dev, "failed to create debugfs dir!\n"); ++ return; ++ } ++ ++ debugfs_create_file("status", 0400, hdmi->debugfs_dir, ++ hdmi, &dw_hdmi_status_fops); ++ debugfs_create_file("ctrl", 0600, hdmi->debugfs_dir, ++ hdmi, &dw_hdmi_ctrl_fops); ++} ++ ++static struct dw_hdmi_qp * ++__dw_hdmi_probe(struct platform_device *pdev, ++ const struct dw_hdmi_plat_data *plat_data) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct device_node *ddc_node; ++ struct dw_hdmi_qp *hdmi; ++ // struct dw_hdmi_qp_i2s_audio_data audio; ++ // struct platform_device_info pdevinfo; ++ // struct dw_hdmi_qp_cec_data cec; ++ struct resource *iores = NULL; ++ int irq; ++ int ret; ++ ++ hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); ++ if (!hdmi) ++ return ERR_PTR(-ENOMEM); ++ ++ hdmi->connector.stereo_allowed = 1; ++ hdmi->plat_data = plat_data; ++ hdmi->dev = dev; ++ hdmi->sample_rate = 48000; ++ hdmi->disabled = true; ++ ++ mutex_init(&hdmi->mutex); ++ mutex_init(&hdmi->audio_mutex); ++ mutex_init(&hdmi->cec_notifier_mutex); ++ ++ ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); ++ if (ddc_node) { ++ hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node); ++ of_node_put(ddc_node); ++ if (!hdmi->ddc) { ++ dev_dbg(hdmi->dev, "failed to read ddc node\n"); ++ return ERR_PTR(-EPROBE_DEFER); ++ } ++ ++ } else { ++ dev_dbg(hdmi->dev, "no ddc property found\n"); ++ } ++ ++ if (!plat_data->regm) { ++ const struct regmap_config *reg_config; ++ ++ reg_config = &hdmi_regmap_config; ++ ++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ hdmi->regs = devm_ioremap_resource(dev, iores); ++ if (IS_ERR(hdmi->regs)) { ++ ret = PTR_ERR(hdmi->regs); ++ goto err_res; ++ } ++ ++ hdmi->regm = devm_regmap_init_mmio(dev, hdmi->regs, reg_config); ++ if (IS_ERR(hdmi->regm)) { ++ dev_err(dev, "Failed to configure regmap\n"); ++ ret = PTR_ERR(hdmi->regm); ++ goto err_res; ++ } ++ } else { ++ hdmi->regm = plat_data->regm; ++ } ++ ++ ret = dw_hdmi_detect_phy(hdmi); ++ if (ret < 0) ++ goto err_res; ++ ++ hdmi_writel(hdmi, 0, MAINUNIT_0_INT_MASK_N); ++ hdmi_writel(hdmi, 0, MAINUNIT_1_INT_MASK_N); ++ hdmi_writel(hdmi, 428571429, TIMER_BASE_CONFIG0); ++ if ((hdmi_readl(hdmi, CMU_STATUS) & DISPLAY_CLK_MONITOR) == DISPLAY_CLK_LOCKED) { ++ hdmi->initialized = true; ++ hdmi->disabled = false; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ ret = irq; ++ goto err_res; ++ } ++ ++ hdmi->avp_irq = irq; ++ ret = devm_request_threaded_irq(dev, hdmi->avp_irq, ++ dw_hdmi_qp_avp_hardirq, ++ dw_hdmi_qp_avp_irq, IRQF_SHARED, ++ dev_name(dev), hdmi); ++ if (ret) ++ goto err_res; ++ ++ irq = platform_get_irq(pdev, 1); ++ if (irq < 0) { ++ ret = irq; ++ goto err_res; ++ } ++ ++ // cec.irq = irq; ++ ++ irq = platform_get_irq(pdev, 2); ++ if (irq < 0) { ++ ret = irq; ++ goto err_res; ++ } ++ ++ hdmi->earc_irq = irq; ++ ret = devm_request_threaded_irq(dev, hdmi->earc_irq, ++ dw_hdmi_qp_earc_hardirq, ++ dw_hdmi_qp_earc_irq, IRQF_SHARED, ++ dev_name(dev), hdmi); ++ if (ret) ++ goto err_res; ++ ++ irq = platform_get_irq(pdev, 3); ++ if (irq < 0) { ++ ret = irq; ++ goto err_res; ++ } ++ ++ hdmi->main_irq = irq; ++ ret = devm_request_threaded_irq(dev, hdmi->main_irq, ++ dw_hdmi_qp_main_hardirq, NULL, ++ IRQF_SHARED, dev_name(dev), hdmi); ++ if (ret) ++ goto err_res; ++ ++ hdmi_init_clk_regenerator(hdmi); ++ ++ /* If DDC bus is not specified, try to register HDMI I2C bus */ ++ if (!hdmi->ddc) { ++ hdmi->ddc = dw_hdmi_i2c_adapter(hdmi); ++ if (IS_ERR(hdmi->ddc)) ++ hdmi->ddc = NULL; ++ /* ++ * Read high and low time from device tree. If not available use ++ * the default timing scl clock rate is about 99.6KHz. ++ */ ++ if (of_property_read_u32(np, "ddc-i2c-scl-high-time-ns", ++ &hdmi->i2c->scl_high_ns)) ++ hdmi->i2c->scl_high_ns = 4708; ++ if (of_property_read_u32(np, "ddc-i2c-scl-low-time-ns", ++ &hdmi->i2c->scl_low_ns)) ++ hdmi->i2c->scl_low_ns = 4916; ++ } ++ ++ hdmi->bridge.driver_private = hdmi; ++ hdmi->bridge.funcs = &dw_hdmi_bridge_funcs; ++#ifdef CONFIG_OF ++ hdmi->bridge.of_node = pdev->dev.of_node; ++#endif ++ ++ if (hdmi->phy.ops->setup_hpd) ++ hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); ++ ++ hdmi->connector.ycbcr_420_allowed = hdmi->plat_data->ycbcr_420_allowed; ++ ++ // audio.hdmi = hdmi; ++ // audio.eld = hdmi->connector.eld; ++ // audio.write = hdmi_writel; ++ // audio.read = hdmi_readl; ++ // audio.mod = hdmi_modb; ++ // hdmi->enable_audio = dw_hdmi_i2s_audio_enable; ++ // hdmi->disable_audio = dw_hdmi_i2s_audio_disable; ++ ++ // memset(&pdevinfo, 0, sizeof(pdevinfo)); ++ // pdevinfo.parent = dev; ++ // pdevinfo.id = PLATFORM_DEVID_AUTO; ++ // pdevinfo.name = "dw-hdmi-qp-i2s-audio"; ++ // pdevinfo.data = &audio; ++ // pdevinfo.size_data = sizeof(audio); ++ // pdevinfo.dma_mask = DMA_BIT_MASK(32); ++ // hdmi->audio = platform_device_register_full(&pdevinfo); ++ ++ hdmi->extcon = devm_extcon_dev_allocate(hdmi->dev, dw_hdmi_cable); ++ if (IS_ERR(hdmi->extcon)) { ++ dev_err(hdmi->dev, "allocate extcon failed\n"); ++ ret = PTR_ERR(hdmi->extcon); ++ goto err_res; ++ } ++ ++ ret = devm_extcon_dev_register(hdmi->dev, hdmi->extcon); ++ if (ret) { ++ dev_err(hdmi->dev, "failed to register extcon: %d\n", ret); ++ goto err_res; ++ } ++ ++ ret = extcon_set_property_capability(hdmi->extcon, EXTCON_DISP_HDMI, ++ EXTCON_PROP_DISP_HPD); ++ if (ret) { ++ dev_err(hdmi->dev, ++ "failed to set USB property capability: %d\n", ret); ++ goto err_res; ++ } ++ ++ // cec.hdmi = hdmi; ++ // cec.ops = &dw_hdmi_qp_cec_ops; ++ // pdevinfo.name = "dw-hdmi-qp-cec"; ++ // pdevinfo.data = &cec; ++ // pdevinfo.size_data = sizeof(cec); ++ // pdevinfo.dma_mask = 0; ++ // hdmi->cec = platform_device_register_full(&pdevinfo); ++ ++ /* Reset HDMI DDC I2C master controller and mute I2CM interrupts */ ++ if (hdmi->i2c) ++ dw_hdmi_i2c_init(hdmi); ++ ++ init_completion(&hdmi->flt_cmp); ++ init_completion(&hdmi->earc_cmp); ++ ++ if (of_property_read_bool(np, "scramble-low-rates")) ++ hdmi->scramble_low_rates = true; ++ ++ dw_hdmi_register_debugfs(dev, hdmi); ++ ++ return hdmi; ++ ++err_res: ++ if (hdmi->i2c) ++ i2c_del_adapter(&hdmi->i2c->adap); ++ else ++ i2c_put_adapter(hdmi->ddc); ++ ++ return ERR_PTR(ret); ++} ++ ++static void __dw_hdmi_remove(struct dw_hdmi_qp *hdmi) ++{ ++ if (hdmi->avp_irq) ++ disable_irq(hdmi->avp_irq); ++ ++ if (hdmi->main_irq) ++ disable_irq(hdmi->main_irq); ++ ++ if (hdmi->earc_irq) ++ disable_irq(hdmi->earc_irq); ++ ++ debugfs_remove_recursive(hdmi->debugfs_dir); ++ ++ if (!hdmi->plat_data->first_screen) { ++ dw_hdmi_destroy_properties(hdmi); ++ hdmi->connector.funcs->destroy(&hdmi->connector); ++ } ++ ++ if (hdmi->audio && !IS_ERR(hdmi->audio)) ++ platform_device_unregister(hdmi->audio); ++ ++ // [CC:] dw_hdmi_rockchip_unbind() also calls drm_encoder_cleanup() ++ // and causes a seg fault due to NULL ptr dererence ++ // if (hdmi->bridge.encoder && !hdmi->plat_data->first_screen) ++ // hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder); ++ // ++ if (!IS_ERR(hdmi->cec)) ++ platform_device_unregister(hdmi->cec); ++ if (hdmi->i2c) ++ i2c_del_adapter(&hdmi->i2c->adap); ++ else ++ i2c_put_adapter(hdmi->ddc); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Bind/unbind API, used from platforms based on the component framework. ++ */ ++struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, ++ struct drm_encoder *encoder, ++ struct dw_hdmi_plat_data *plat_data) ++{ ++ struct dw_hdmi_qp *hdmi; ++ int ret; ++ ++ hdmi = __dw_hdmi_probe(pdev, plat_data); ++ if (IS_ERR(hdmi)) ++ return hdmi; ++ ++ if (!plat_data->first_screen) { ++ ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0); ++ if (ret) { ++ __dw_hdmi_remove(hdmi); ++ dev_err(hdmi->dev, "Failed to initialize bridge with drm\n"); ++ return ERR_PTR(ret); ++ } ++ ++ plat_data->connector = &hdmi->connector; ++ } ++ ++ if (plat_data->split_mode && !hdmi->plat_data->first_screen) { ++ struct dw_hdmi_qp *secondary = NULL; ++ ++ if (hdmi->plat_data->left) ++ secondary = hdmi->plat_data->left; ++ else if (hdmi->plat_data->right) ++ secondary = hdmi->plat_data->right; ++ ++ if (!secondary) ++ return ERR_PTR(-ENOMEM); ++ ret = drm_bridge_attach(encoder, &secondary->bridge, &hdmi->bridge, ++ DRM_BRIDGE_ATTACH_NO_CONNECTOR); ++ if (ret) ++ return ERR_PTR(ret); ++ } ++ ++ return hdmi; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_bind); ++ ++void dw_hdmi_qp_unbind(struct dw_hdmi_qp *hdmi) ++{ ++ __dw_hdmi_remove(hdmi); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_unbind); ++ ++void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi) ++{ ++ if (!hdmi) { ++ dev_warn(dev, "Hdmi has not been initialized\n"); ++ return; ++ } ++ ++ mutex_lock(&hdmi->mutex); ++ ++ /* ++ * When system shutdown, hdmi should be disabled. ++ * When system suspend, dw_hdmi_qp_bridge_disable will disable hdmi first. ++ * To prevent duplicate operation, we should determine whether hdmi ++ * has been disabled. ++ */ ++ if (!hdmi->disabled) ++ hdmi->disabled = true; ++ mutex_unlock(&hdmi->mutex); ++ ++ if (hdmi->avp_irq) ++ disable_irq(hdmi->avp_irq); ++ ++ if (hdmi->main_irq) ++ disable_irq(hdmi->main_irq); ++ ++ if (hdmi->earc_irq) ++ disable_irq(hdmi->earc_irq); ++ ++ pinctrl_pm_select_sleep_state(dev); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_suspend); ++ ++void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi) ++{ ++ if (!hdmi) { ++ dev_warn(dev, "Hdmi has not been initialized\n"); ++ return; ++ } ++ ++ hdmi_writel(hdmi, 0, MAINUNIT_0_INT_MASK_N); ++ hdmi_writel(hdmi, 0, MAINUNIT_1_INT_MASK_N); ++ hdmi_writel(hdmi, 428571429, TIMER_BASE_CONFIG0); ++ ++ pinctrl_pm_select_default_state(dev); ++ ++ hdmi->cec_adap->ops->adap_enable(hdmi->cec_adap, true); ++ ++ mutex_lock(&hdmi->mutex); ++ if (hdmi->i2c) ++ dw_hdmi_i2c_init(hdmi); ++ if (hdmi->avp_irq) ++ enable_irq(hdmi->avp_irq); ++ ++ if (hdmi->main_irq) ++ enable_irq(hdmi->main_irq); ++ ++ if (hdmi->earc_irq) ++ enable_irq(hdmi->earc_irq); ++ ++ mutex_unlock(&hdmi->mutex); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_resume); ++ ++MODULE_AUTHOR("Algea Cao "); ++MODULE_DESCRIPTION("DW HDMI QP transmitter driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:dw-hdmi-qp"); +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h +new file mode 100644 +index 000000000..4cac70f2d +--- /dev/null ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h +@@ -0,0 +1,831 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) Rockchip Electronics Co.Ltd ++ * Author: ++ * Algea Cao ++ */ ++#ifndef __DW_HDMI_QP_H__ ++#define __DW_HDMI_QP_H__ ++/* Main Unit Registers */ ++#define CORE_ID 0x0 ++#define VER_NUMBER 0x4 ++#define VER_TYPE 0x8 ++#define CONFIG_REG 0xc ++#define CONFIG_CEC BIT(28) ++#define CONFIG_AUD_UD BIT(23) ++#define CORE_TIMESTAMP_HHMM 0x14 ++#define CORE_TIMESTAMP_MMDD 0x18 ++#define CORE_TIMESTAMP_YYYY 0x1c ++/* Reset Manager Registers */ ++#define GLOBAL_SWRESET_REQUEST 0x40 ++#define EARCRX_CMDC_SWINIT_P BIT(27) ++#define AVP_DATAPATH_PACKET_AUDIO_SWINIT_P BIT(10) ++#define GLOBAL_SWDISABLE 0x44 ++#define CEC_SWDISABLE BIT(17) ++#define AVP_DATAPATH_PACKET_AUDIO_SWDISABLE BIT(10) ++#define AVP_DATAPATH_VIDEO_SWDISABLE BIT(6) ++#define RESET_MANAGER_CONFIG0 0x48 ++#define RESET_MANAGER_STATUS0 0x50 ++#define RESET_MANAGER_STATUS1 0x54 ++#define RESET_MANAGER_STATUS2 0x58 ++/* Timer Base Registers */ ++#define TIMER_BASE_CONFIG0 0x80 ++#define TIMER_BASE_STATUS0 0x84 ++/* CMU Registers */ ++#define CMU_CONFIG0 0xa0 ++#define CMU_CONFIG1 0xa4 ++#define CMU_CONFIG2 0xa8 ++#define CMU_CONFIG3 0xac ++#define CMU_STATUS 0xb0 ++#define DISPLAY_CLK_MONITOR 0x3f ++#define DISPLAY_CLK_LOCKED 0X15 ++#define EARC_BPCLK_OFF BIT(9) ++#define AUDCLK_OFF BIT(7) ++#define LINKQPCLK_OFF BIT(5) ++#define VIDQPCLK_OFF BIT(3) ++#define IPI_CLK_OFF BIT(1) ++#define CMU_IPI_CLK_FREQ 0xb4 ++#define CMU_VIDQPCLK_FREQ 0xb8 ++#define CMU_LINKQPCLK_FREQ 0xbc ++#define CMU_AUDQPCLK_FREQ 0xc0 ++#define CMU_EARC_BPCLK_FREQ 0xc4 ++/* I2CM Registers */ ++#define I2CM_SM_SCL_CONFIG0 0xe0 ++#define I2CM_FM_SCL_CONFIG0 0xe4 ++#define I2CM_CONFIG0 0xe8 ++#define I2CM_CONTROL0 0xec ++#define I2CM_STATUS0 0xf0 ++#define I2CM_INTERFACE_CONTROL0 0xf4 ++#define I2CM_ADDR 0xff000 ++#define I2CM_SLVADDR 0xfe0 ++#define I2CM_WR_MASK 0x1e ++#define I2CM_EXT_READ BIT(4) ++#define I2CM_SHORT_READ BIT(3) ++#define I2CM_FM_READ BIT(2) ++#define I2CM_FM_WRITE BIT(1) ++#define I2CM_FM_EN BIT(0) ++#define I2CM_INTERFACE_CONTROL1 0xf8 ++#define I2CM_SEG_PTR 0x7f80 ++#define I2CM_SEG_ADDR 0x7f ++#define I2CM_INTERFACE_WRDATA_0_3 0xfc ++#define I2CM_INTERFACE_WRDATA_4_7 0x100 ++#define I2CM_INTERFACE_WRDATA_8_11 0x104 ++#define I2CM_INTERFACE_WRDATA_12_15 0x108 ++#define I2CM_INTERFACE_RDDATA_0_3 0x10c ++#define I2CM_INTERFACE_RDDATA_4_7 0x110 ++#define I2CM_INTERFACE_RDDATA_8_11 0x114 ++#define I2CM_INTERFACE_RDDATA_12_15 0x118 ++/* SCDC Registers */ ++#define SCDC_CONFIG0 0x140 ++#define SCDC_I2C_FM_EN BIT(12) ++#define SCDC_UPD_FLAGS_AUTO_CLR BIT(6) ++#define SCDC_UPD_FLAGS_POLL_EN BIT(4) ++#define SCDC_CONTROL0 0x148 ++#define SCDC_STATUS0 0x150 ++#define STATUS_UPDATE BIT(0) ++#define FRL_START BIT(4) ++#define FLT_UPDATE BIT(5) ++/* FLT Registers */ ++#define FLT_CONFIG0 0x160 ++#define FLT_CONFIG1 0x164 ++#define FLT_CONFIG2 0x168 ++#define FLT_CONTROL0 0x170 ++/* Main Unit 2 Registers */ ++#define MAINUNIT_STATUS0 0x180 ++/* Video Interface Registers */ ++#define VIDEO_INTERFACE_CONFIG0 0x800 ++#define VIDEO_INTERFACE_CONFIG1 0x804 ++#define VIDEO_INTERFACE_CONFIG2 0x808 ++#define VIDEO_INTERFACE_CONTROL0 0x80c ++#define VIDEO_INTERFACE_STATUS0 0x814 ++/* Video Packing Registers */ ++#define VIDEO_PACKING_CONFIG0 0x81c ++/* Audio Interface Registers */ ++#define AUDIO_INTERFACE_CONFIG0 0x820 ++#define AUD_IF_SEL_MSK 0x3 ++#define AUD_IF_SPDIF 0x2 ++#define AUD_IF_I2S 0x1 ++#define AUD_IF_PAI 0x0 ++#define AUD_FIFO_INIT_ON_OVF_MSK BIT(2) ++#define AUD_FIFO_INIT_ON_OVF_EN BIT(2) ++#define I2S_LINES_EN_MSK GENMASK(7, 4) ++#define I2S_LINES_EN(x) BIT(x + 4) ++#define I2S_BPCUV_RCV_MSK BIT(12) ++#define I2S_BPCUV_RCV_EN BIT(12) ++#define I2S_BPCUV_RCV_DIS 0 ++#define SPDIF_LINES_EN GENMASK(19, 16) ++#define AUD_FORMAT_MSK GENMASK(26, 24) ++#define AUD_3DOBA (0x7 << 24) ++#define AUD_3DASP (0x6 << 24) ++#define AUD_MSOBA (0x5 << 24) ++#define AUD_MSASP (0x4 << 24) ++#define AUD_HBR (0x3 << 24) ++#define AUD_DST (0x2 << 24) ++#define AUD_OBA (0x1 << 24) ++#define AUD_ASP (0x0 << 24) ++#define AUDIO_INTERFACE_CONFIG1 0x824 ++#define AUDIO_INTERFACE_CONTROL0 0x82c ++#define AUDIO_FIFO_CLR_P BIT(0) ++#define AUDIO_INTERFACE_STATUS0 0x834 ++/* Frame Composer Registers */ ++#define FRAME_COMPOSER_CONFIG0 0x840 ++#define FRAME_COMPOSER_CONFIG1 0x844 ++#define FRAME_COMPOSER_CONFIG2 0x848 ++#define FRAME_COMPOSER_CONFIG3 0x84c ++#define FRAME_COMPOSER_CONFIG4 0x850 ++#define FRAME_COMPOSER_CONFIG5 0x854 ++#define FRAME_COMPOSER_CONFIG6 0x858 ++#define FRAME_COMPOSER_CONFIG7 0x85c ++#define FRAME_COMPOSER_CONFIG8 0x860 ++#define FRAME_COMPOSER_CONFIG9 0x864 ++#define FRAME_COMPOSER_CONTROL0 0x86c ++/* Video Monitor Registers */ ++#define VIDEO_MONITOR_CONFIG0 0x880 ++#define VIDEO_MONITOR_STATUS0 0x884 ++#define VIDEO_MONITOR_STATUS1 0x888 ++#define VIDEO_MONITOR_STATUS2 0x88c ++#define VIDEO_MONITOR_STATUS3 0x890 ++#define VIDEO_MONITOR_STATUS4 0x894 ++#define VIDEO_MONITOR_STATUS5 0x898 ++#define VIDEO_MONITOR_STATUS6 0x89c ++/* HDCP2 Logic Registers */ ++#define HDCP2LOGIC_CONFIG0 0x8e0 ++#define HDCP2_BYPASS BIT(0) ++#define HDCP2LOGIC_ESM_GPIO_IN 0x8e4 ++#define HDCP2LOGIC_ESM_GPIO_OUT 0x8e8 ++/* HDCP14 Registers */ ++#define HDCP14_CONFIG0 0x900 ++#define HDCP14_CONFIG1 0x904 ++#define HDCP14_CONFIG2 0x908 ++#define HDCP14_CONFIG3 0x90c ++#define HDCP14_KEY_SEED 0x914 ++#define HDCP14_KEY_H 0x918 ++#define HDCP14_KEY_L 0x91c ++#define HDCP14_KEY_STATUS 0x920 ++#define HDCP14_AKSV_H 0x924 ++#define HDCP14_AKSV_L 0x928 ++#define HDCP14_AN_H 0x92c ++#define HDCP14_AN_L 0x930 ++#define HDCP14_STATUS0 0x934 ++#define HDCP14_STATUS1 0x938 ++/* Scrambler Registers */ ++#define SCRAMB_CONFIG0 0x960 ++/* Video Configuration Registers */ ++#define LINK_CONFIG0 0x968 ++#define OPMODE_FRL_4LANES BIT(8) ++#define OPMODE_DVI BIT(4) ++#define OPMODE_FRL BIT(0) ++/* TMDS FIFO Registers */ ++#define TMDS_FIFO_CONFIG0 0x970 ++#define TMDS_FIFO_CONTROL0 0x974 ++/* FRL RSFEC Registers */ ++#define FRL_RSFEC_CONFIG0 0xa20 ++#define FRL_RSFEC_STATUS0 0xa30 ++/* FRL Packetizer Registers */ ++#define FRL_PKTZ_CONFIG0 0xa40 ++#define FRL_PKTZ_CONTROL0 0xa44 ++#define FRL_PKTZ_CONTROL1 0xa50 ++#define FRL_PKTZ_STATUS1 0xa54 ++/* Packet Scheduler Registers */ ++#define PKTSCHED_CONFIG0 0xa80 ++#define PKTSCHED_PRQUEUE0_CONFIG0 0xa84 ++#define PKTSCHED_PRQUEUE1_CONFIG0 0xa88 ++#define PKTSCHED_PRQUEUE2_CONFIG0 0xa8c ++#define PKTSCHED_PRQUEUE2_CONFIG1 0xa90 ++#define PKTSCHED_PRQUEUE2_CONFIG2 0xa94 ++#define PKTSCHED_PKT_CONFIG0 0xa98 ++#define PKTSCHED_PKT_CONFIG1 0xa9c ++#define PKTSCHED_DRMI_FIELDRATE BIT(13) ++#define PKTSCHED_AVI_FIELDRATE BIT(12) ++#define PKTSCHED_PKT_CONFIG2 0xaa0 ++#define PKTSCHED_PKT_CONFIG3 0xaa4 ++#define PKTSCHED_PKT_EN 0xaa8 ++#define PKTSCHED_DRMI_TX_EN BIT(17) ++#define PKTSCHED_AUDI_TX_EN BIT(15) ++#define PKTSCHED_AVI_TX_EN BIT(13) ++#define PKTSCHED_EMP_CVTEM_TX_EN BIT(10) ++#define PKTSCHED_AMD_TX_EN BIT(8) ++#define PKTSCHED_GCP_TX_EN BIT(3) ++#define PKTSCHED_AUDS_TX_EN BIT(2) ++#define PKTSCHED_ACR_TX_EN BIT(1) ++#define PKTSCHED_NULL_TX_EN BIT(0) ++#define PKTSCHED_PKT_CONTROL0 0xaac ++#define PKTSCHED_PKT_SEND 0xab0 ++#define PKTSCHED_PKT_STATUS0 0xab4 ++#define PKTSCHED_PKT_STATUS1 0xab8 ++#define PKT_NULL_CONTENTS0 0xb00 ++#define PKT_NULL_CONTENTS1 0xb04 ++#define PKT_NULL_CONTENTS2 0xb08 ++#define PKT_NULL_CONTENTS3 0xb0c ++#define PKT_NULL_CONTENTS4 0xb10 ++#define PKT_NULL_CONTENTS5 0xb14 ++#define PKT_NULL_CONTENTS6 0xb18 ++#define PKT_NULL_CONTENTS7 0xb1c ++#define PKT_ACP_CONTENTS0 0xb20 ++#define PKT_ACP_CONTENTS1 0xb24 ++#define PKT_ACP_CONTENTS2 0xb28 ++#define PKT_ACP_CONTENTS3 0xb2c ++#define PKT_ACP_CONTENTS4 0xb30 ++#define PKT_ACP_CONTENTS5 0xb34 ++#define PKT_ACP_CONTENTS6 0xb38 ++#define PKT_ACP_CONTENTS7 0xb3c ++#define PKT_ISRC1_CONTENTS0 0xb40 ++#define PKT_ISRC1_CONTENTS1 0xb44 ++#define PKT_ISRC1_CONTENTS2 0xb48 ++#define PKT_ISRC1_CONTENTS3 0xb4c ++#define PKT_ISRC1_CONTENTS4 0xb50 ++#define PKT_ISRC1_CONTENTS5 0xb54 ++#define PKT_ISRC1_CONTENTS6 0xb58 ++#define PKT_ISRC1_CONTENTS7 0xb5c ++#define PKT_ISRC2_CONTENTS0 0xb60 ++#define PKT_ISRC2_CONTENTS1 0xb64 ++#define PKT_ISRC2_CONTENTS2 0xb68 ++#define PKT_ISRC2_CONTENTS3 0xb6c ++#define PKT_ISRC2_CONTENTS4 0xb70 ++#define PKT_ISRC2_CONTENTS5 0xb74 ++#define PKT_ISRC2_CONTENTS6 0xb78 ++#define PKT_ISRC2_CONTENTS7 0xb7c ++#define PKT_GMD_CONTENTS0 0xb80 ++#define PKT_GMD_CONTENTS1 0xb84 ++#define PKT_GMD_CONTENTS2 0xb88 ++#define PKT_GMD_CONTENTS3 0xb8c ++#define PKT_GMD_CONTENTS4 0xb90 ++#define PKT_GMD_CONTENTS5 0xb94 ++#define PKT_GMD_CONTENTS6 0xb98 ++#define PKT_GMD_CONTENTS7 0xb9c ++#define PKT_AMD_CONTENTS0 0xba0 ++#define PKT_AMD_CONTENTS1 0xba4 ++#define PKT_AMD_CONTENTS2 0xba8 ++#define PKT_AMD_CONTENTS3 0xbac ++#define PKT_AMD_CONTENTS4 0xbb0 ++#define PKT_AMD_CONTENTS5 0xbb4 ++#define PKT_AMD_CONTENTS6 0xbb8 ++#define PKT_AMD_CONTENTS7 0xbbc ++#define PKT_VSI_CONTENTS0 0xbc0 ++#define PKT_VSI_CONTENTS1 0xbc4 ++#define PKT_VSI_CONTENTS2 0xbc8 ++#define PKT_VSI_CONTENTS3 0xbcc ++#define PKT_VSI_CONTENTS4 0xbd0 ++#define PKT_VSI_CONTENTS5 0xbd4 ++#define PKT_VSI_CONTENTS6 0xbd8 ++#define PKT_VSI_CONTENTS7 0xbdc ++#define PKT_AVI_CONTENTS0 0xbe0 ++#define HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT BIT(4) ++#define HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR 0x04 ++#define HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR 0x08 ++#define HDMI_FC_AVICONF2_IT_CONTENT_VALID 0x80 ++#define PKT_AVI_CONTENTS1 0xbe4 ++#define PKT_AVI_CONTENTS2 0xbe8 ++#define PKT_AVI_CONTENTS3 0xbec ++#define PKT_AVI_CONTENTS4 0xbf0 ++#define PKT_AVI_CONTENTS5 0xbf4 ++#define PKT_AVI_CONTENTS6 0xbf8 ++#define PKT_AVI_CONTENTS7 0xbfc ++#define PKT_SPDI_CONTENTS0 0xc00 ++#define PKT_SPDI_CONTENTS1 0xc04 ++#define PKT_SPDI_CONTENTS2 0xc08 ++#define PKT_SPDI_CONTENTS3 0xc0c ++#define PKT_SPDI_CONTENTS4 0xc10 ++#define PKT_SPDI_CONTENTS5 0xc14 ++#define PKT_SPDI_CONTENTS6 0xc18 ++#define PKT_SPDI_CONTENTS7 0xc1c ++#define PKT_AUDI_CONTENTS0 0xc20 ++#define PKT_AUDI_CONTENTS1 0xc24 ++#define PKT_AUDI_CONTENTS2 0xc28 ++#define PKT_AUDI_CONTENTS3 0xc2c ++#define PKT_AUDI_CONTENTS4 0xc30 ++#define PKT_AUDI_CONTENTS5 0xc34 ++#define PKT_AUDI_CONTENTS6 0xc38 ++#define PKT_AUDI_CONTENTS7 0xc3c ++#define PKT_NVI_CONTENTS0 0xc40 ++#define PKT_NVI_CONTENTS1 0xc44 ++#define PKT_NVI_CONTENTS2 0xc48 ++#define PKT_NVI_CONTENTS3 0xc4c ++#define PKT_NVI_CONTENTS4 0xc50 ++#define PKT_NVI_CONTENTS5 0xc54 ++#define PKT_NVI_CONTENTS6 0xc58 ++#define PKT_NVI_CONTENTS7 0xc5c ++#define PKT_DRMI_CONTENTS0 0xc60 ++#define PKT_DRMI_CONTENTS1 0xc64 ++#define PKT_DRMI_CONTENTS2 0xc68 ++#define PKT_DRMI_CONTENTS3 0xc6c ++#define PKT_DRMI_CONTENTS4 0xc70 ++#define PKT_DRMI_CONTENTS5 0xc74 ++#define PKT_DRMI_CONTENTS6 0xc78 ++#define PKT_DRMI_CONTENTS7 0xc7c ++#define PKT_GHDMI1_CONTENTS0 0xc80 ++#define PKT_GHDMI1_CONTENTS1 0xc84 ++#define PKT_GHDMI1_CONTENTS2 0xc88 ++#define PKT_GHDMI1_CONTENTS3 0xc8c ++#define PKT_GHDMI1_CONTENTS4 0xc90 ++#define PKT_GHDMI1_CONTENTS5 0xc94 ++#define PKT_GHDMI1_CONTENTS6 0xc98 ++#define PKT_GHDMI1_CONTENTS7 0xc9c ++#define PKT_GHDMI2_CONTENTS0 0xca0 ++#define PKT_GHDMI2_CONTENTS1 0xca4 ++#define PKT_GHDMI2_CONTENTS2 0xca8 ++#define PKT_GHDMI2_CONTENTS3 0xcac ++#define PKT_GHDMI2_CONTENTS4 0xcb0 ++#define PKT_GHDMI2_CONTENTS5 0xcb4 ++#define PKT_GHDMI2_CONTENTS6 0xcb8 ++#define PKT_GHDMI2_CONTENTS7 0xcbc ++/* EMP Packetizer Registers */ ++#define PKT_EMP_CONFIG0 0xce0 ++#define PKT_EMP_CONTROL0 0xcec ++#define PKT_EMP_CONTROL1 0xcf0 ++#define PKT_EMP_CONTROL2 0xcf4 ++#define PKT_EMP_VTEM_CONTENTS0 0xd00 ++#define PKT_EMP_VTEM_CONTENTS1 0xd04 ++#define PKT_EMP_VTEM_CONTENTS2 0xd08 ++#define PKT_EMP_VTEM_CONTENTS3 0xd0c ++#define PKT_EMP_VTEM_CONTENTS4 0xd10 ++#define PKT_EMP_VTEM_CONTENTS5 0xd14 ++#define PKT_EMP_VTEM_CONTENTS6 0xd18 ++#define PKT_EMP_VTEM_CONTENTS7 0xd1c ++#define PKT0_EMP_CVTEM_CONTENTS0 0xd20 ++#define PKT0_EMP_CVTEM_CONTENTS1 0xd24 ++#define PKT0_EMP_CVTEM_CONTENTS2 0xd28 ++#define PKT0_EMP_CVTEM_CONTENTS3 0xd2c ++#define PKT0_EMP_CVTEM_CONTENTS4 0xd30 ++#define PKT0_EMP_CVTEM_CONTENTS5 0xd34 ++#define PKT0_EMP_CVTEM_CONTENTS6 0xd38 ++#define PKT0_EMP_CVTEM_CONTENTS7 0xd3c ++#define PKT1_EMP_CVTEM_CONTENTS0 0xd40 ++#define PKT1_EMP_CVTEM_CONTENTS1 0xd44 ++#define PKT1_EMP_CVTEM_CONTENTS2 0xd48 ++#define PKT1_EMP_CVTEM_CONTENTS3 0xd4c ++#define PKT1_EMP_CVTEM_CONTENTS4 0xd50 ++#define PKT1_EMP_CVTEM_CONTENTS5 0xd54 ++#define PKT1_EMP_CVTEM_CONTENTS6 0xd58 ++#define PKT1_EMP_CVTEM_CONTENTS7 0xd5c ++#define PKT2_EMP_CVTEM_CONTENTS0 0xd60 ++#define PKT2_EMP_CVTEM_CONTENTS1 0xd64 ++#define PKT2_EMP_CVTEM_CONTENTS2 0xd68 ++#define PKT2_EMP_CVTEM_CONTENTS3 0xd6c ++#define PKT2_EMP_CVTEM_CONTENTS4 0xd70 ++#define PKT2_EMP_CVTEM_CONTENTS5 0xd74 ++#define PKT2_EMP_CVTEM_CONTENTS6 0xd78 ++#define PKT2_EMP_CVTEM_CONTENTS7 0xd7c ++#define PKT3_EMP_CVTEM_CONTENTS0 0xd80 ++#define PKT3_EMP_CVTEM_CONTENTS1 0xd84 ++#define PKT3_EMP_CVTEM_CONTENTS2 0xd88 ++#define PKT3_EMP_CVTEM_CONTENTS3 0xd8c ++#define PKT3_EMP_CVTEM_CONTENTS4 0xd90 ++#define PKT3_EMP_CVTEM_CONTENTS5 0xd94 ++#define PKT3_EMP_CVTEM_CONTENTS6 0xd98 ++#define PKT3_EMP_CVTEM_CONTENTS7 0xd9c ++#define PKT4_EMP_CVTEM_CONTENTS0 0xda0 ++#define PKT4_EMP_CVTEM_CONTENTS1 0xda4 ++#define PKT4_EMP_CVTEM_CONTENTS2 0xda8 ++#define PKT4_EMP_CVTEM_CONTENTS3 0xdac ++#define PKT4_EMP_CVTEM_CONTENTS4 0xdb0 ++#define PKT4_EMP_CVTEM_CONTENTS5 0xdb4 ++#define PKT4_EMP_CVTEM_CONTENTS6 0xdb8 ++#define PKT4_EMP_CVTEM_CONTENTS7 0xdbc ++#define PKT5_EMP_CVTEM_CONTENTS0 0xdc0 ++#define PKT5_EMP_CVTEM_CONTENTS1 0xdc4 ++#define PKT5_EMP_CVTEM_CONTENTS2 0xdc8 ++#define PKT5_EMP_CVTEM_CONTENTS3 0xdcc ++#define PKT5_EMP_CVTEM_CONTENTS4 0xdd0 ++#define PKT5_EMP_CVTEM_CONTENTS5 0xdd4 ++#define PKT5_EMP_CVTEM_CONTENTS6 0xdd8 ++#define PKT5_EMP_CVTEM_CONTENTS7 0xddc ++/* Audio Packetizer Registers */ ++#define AUDPKT_CONTROL0 0xe20 ++#define AUDPKT_PBIT_FORCE_EN_MASK BIT(12) ++#define AUDPKT_PBIT_FORCE_EN BIT(12) ++#define AUDPKT_CHSTATUS_OVR_EN_MASK BIT(0) ++#define AUDPKT_CHSTATUS_OVR_EN BIT(0) ++#define AUDPKT_CONTROL1 0xe24 ++#define AUDPKT_ACR_CONTROL0 0xe40 ++#define AUDPKT_ACR_N_VALUE 0xfffff ++#define AUDPKT_ACR_CONTROL1 0xe44 ++#define AUDPKT_ACR_CTS_OVR_VAL_MSK GENMASK(23, 4) ++#define AUDPKT_ACR_CTS_OVR_VAL(x) ((x) << 4) ++#define AUDPKT_ACR_CTS_OVR_EN_MSK BIT(1) ++#define AUDPKT_ACR_CTS_OVR_EN BIT(1) ++#define AUDPKT_ACR_STATUS0 0xe4c ++#define AUDPKT_CHSTATUS_OVR0 0xe60 ++#define AUDPKT_CHSTATUS_OVR1 0xe64 ++/* IEC60958 Byte 3: Sampleing frenuency Bits 24 to 27 */ ++#define AUDPKT_CHSTATUS_SR_MASK GENMASK(3, 0) ++#define AUDPKT_CHSTATUS_SR_22050 0x4 ++#define AUDPKT_CHSTATUS_SR_24000 0x6 ++#define AUDPKT_CHSTATUS_SR_32000 0x3 ++#define AUDPKT_CHSTATUS_SR_44100 0x0 ++#define AUDPKT_CHSTATUS_SR_48000 0x2 ++#define AUDPKT_CHSTATUS_SR_88200 0x8 ++#define AUDPKT_CHSTATUS_SR_96000 0xa ++#define AUDPKT_CHSTATUS_SR_176400 0xc ++#define AUDPKT_CHSTATUS_SR_192000 0xe ++#define AUDPKT_CHSTATUS_SR_768000 0x9 ++#define AUDPKT_CHSTATUS_SR_NOT_INDICATED 0x1 ++/* IEC60958 Byte 4: Original Sampleing frenuency Bits 36 to 39 */ ++#define AUDPKT_CHSTATUS_0SR_MASK GENMASK(15, 12) ++#define AUDPKT_CHSTATUS_OSR_8000 0x6 ++#define AUDPKT_CHSTATUS_OSR_11025 0xa ++#define AUDPKT_CHSTATUS_OSR_12000 0x2 ++#define AUDPKT_CHSTATUS_OSR_16000 0x8 ++#define AUDPKT_CHSTATUS_OSR_22050 0xb ++#define AUDPKT_CHSTATUS_OSR_24000 0x9 ++#define AUDPKT_CHSTATUS_OSR_32000 0xc ++#define AUDPKT_CHSTATUS_OSR_44100 0xf ++#define AUDPKT_CHSTATUS_OSR_48000 0xd ++#define AUDPKT_CHSTATUS_OSR_88200 0x7 ++#define AUDPKT_CHSTATUS_OSR_96000 0x5 ++#define AUDPKT_CHSTATUS_OSR_176400 0x3 ++#define AUDPKT_CHSTATUS_OSR_192000 0x1 ++#define AUDPKT_CHSTATUS_OSR_NOT_INDICATED 0x0 ++#define AUDPKT_CHSTATUS_OVR2 0xe68 ++#define AUDPKT_CHSTATUS_OVR3 0xe6c ++#define AUDPKT_CHSTATUS_OVR4 0xe70 ++#define AUDPKT_CHSTATUS_OVR5 0xe74 ++#define AUDPKT_CHSTATUS_OVR6 0xe78 ++#define AUDPKT_CHSTATUS_OVR7 0xe7c ++#define AUDPKT_CHSTATUS_OVR8 0xe80 ++#define AUDPKT_CHSTATUS_OVR9 0xe84 ++#define AUDPKT_CHSTATUS_OVR10 0xe88 ++#define AUDPKT_CHSTATUS_OVR11 0xe8c ++#define AUDPKT_CHSTATUS_OVR12 0xe90 ++#define AUDPKT_CHSTATUS_OVR13 0xe94 ++#define AUDPKT_CHSTATUS_OVR14 0xe98 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC0 0xea0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC1 0xea4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC2 0xea8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC3 0xeac ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC4 0xeb0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC5 0xeb4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC6 0xeb8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC7 0xebc ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC8 0xec0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC9 0xec4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC10 0xec8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC11 0xecc ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC12 0xed0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC13 0xed4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC14 0xed8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC15 0xedc ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC16 0xee0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC17 0xee4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC18 0xee8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC19 0xeec ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC20 0xef0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC21 0xef4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC22 0xef8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC23 0xefc ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC24 0xf00 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC25 0xf04 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC26 0xf08 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC27 0xf0c ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC28 0xf10 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC29 0xf14 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC30 0xf18 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC31 0xf1c ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC32 0xf20 ++#define AUDPKT_VBIT_OVR0 0xf24 ++/* CEC Registers */ ++#define CEC_TX_CONTROL 0x1000 ++#define CEC_STATUS 0x1004 ++#define CEC_CONFIG 0x1008 ++#define CEC_ADDR 0x100c ++#define CEC_TX_COUNT 0x1020 ++#define CEC_TX_DATA3_0 0x1024 ++#define CEC_TX_DATA7_4 0x1028 ++#define CEC_TX_DATA11_8 0x102c ++#define CEC_TX_DATA15_12 0x1030 ++#define CEC_RX_COUNT_STATUS 0x1040 ++#define CEC_RX_DATA3_0 0x1044 ++#define CEC_RX_DATA7_4 0x1048 ++#define CEC_RX_DATA11_8 0x104c ++#define CEC_RX_DATA15_12 0x1050 ++#define CEC_LOCK_CONTROL 0x1054 ++#define CEC_RXQUAL_BITTIME_CONFIG 0x1060 ++#define CEC_RX_BITTIME_CONFIG 0x1064 ++#define CEC_TX_BITTIME_CONFIG 0x1068 ++/* eARC RX CMDC Registers */ ++#define EARCRX_CMDC_CONFIG0 0x1800 ++#define EARCRX_XACTREAD_STOP_CFG BIT(26) ++#define EARCRX_XACTREAD_RETRY_CFG BIT(25) ++#define EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 BIT(24) ++#define EARCRX_CMDC_XACT_RESTART_EN BIT(18) ++#define EARCRX_CMDC_CONFIG1 0x1804 ++#define EARCRX_CMDC_CONTROL 0x1808 ++#define EARCRX_CMDC_HEARTBEAT_LOSS_EN BIT(4) ++#define EARCRX_CMDC_DISCOVERY_EN BIT(3) ++#define EARCRX_CONNECTOR_HPD BIT(1) ++#define EARCRX_CMDC_WHITELIST0_CONFIG 0x180c ++#define EARCRX_CMDC_WHITELIST1_CONFIG 0x1810 ++#define EARCRX_CMDC_WHITELIST2_CONFIG 0x1814 ++#define EARCRX_CMDC_WHITELIST3_CONFIG 0x1818 ++#define EARCRX_CMDC_STATUS 0x181c ++#define EARCRX_CMDC_XACT_INFO 0x1820 ++#define EARCRX_CMDC_XACT_ACTION 0x1824 ++#define EARCRX_CMDC_HEARTBEAT_RXSTAT_SE 0x1828 ++#define EARCRX_CMDC_HEARTBEAT_STATUS 0x182c ++#define EARCRX_CMDC_XACT_WR0 0x1840 ++#define EARCRX_CMDC_XACT_WR1 0x1844 ++#define EARCRX_CMDC_XACT_WR2 0x1848 ++#define EARCRX_CMDC_XACT_WR3 0x184c ++#define EARCRX_CMDC_XACT_WR4 0x1850 ++#define EARCRX_CMDC_XACT_WR5 0x1854 ++#define EARCRX_CMDC_XACT_WR6 0x1858 ++#define EARCRX_CMDC_XACT_WR7 0x185c ++#define EARCRX_CMDC_XACT_WR8 0x1860 ++#define EARCRX_CMDC_XACT_WR9 0x1864 ++#define EARCRX_CMDC_XACT_WR10 0x1868 ++#define EARCRX_CMDC_XACT_WR11 0x186c ++#define EARCRX_CMDC_XACT_WR12 0x1870 ++#define EARCRX_CMDC_XACT_WR13 0x1874 ++#define EARCRX_CMDC_XACT_WR14 0x1878 ++#define EARCRX_CMDC_XACT_WR15 0x187c ++#define EARCRX_CMDC_XACT_WR16 0x1880 ++#define EARCRX_CMDC_XACT_WR17 0x1884 ++#define EARCRX_CMDC_XACT_WR18 0x1888 ++#define EARCRX_CMDC_XACT_WR19 0x188c ++#define EARCRX_CMDC_XACT_WR20 0x1890 ++#define EARCRX_CMDC_XACT_WR21 0x1894 ++#define EARCRX_CMDC_XACT_WR22 0x1898 ++#define EARCRX_CMDC_XACT_WR23 0x189c ++#define EARCRX_CMDC_XACT_WR24 0x18a0 ++#define EARCRX_CMDC_XACT_WR25 0x18a4 ++#define EARCRX_CMDC_XACT_WR26 0x18a8 ++#define EARCRX_CMDC_XACT_WR27 0x18ac ++#define EARCRX_CMDC_XACT_WR28 0x18b0 ++#define EARCRX_CMDC_XACT_WR29 0x18b4 ++#define EARCRX_CMDC_XACT_WR30 0x18b8 ++#define EARCRX_CMDC_XACT_WR31 0x18bc ++#define EARCRX_CMDC_XACT_WR32 0x18c0 ++#define EARCRX_CMDC_XACT_WR33 0x18c4 ++#define EARCRX_CMDC_XACT_WR34 0x18c8 ++#define EARCRX_CMDC_XACT_WR35 0x18cc ++#define EARCRX_CMDC_XACT_WR36 0x18d0 ++#define EARCRX_CMDC_XACT_WR37 0x18d4 ++#define EARCRX_CMDC_XACT_WR38 0x18d8 ++#define EARCRX_CMDC_XACT_WR39 0x18dc ++#define EARCRX_CMDC_XACT_WR40 0x18e0 ++#define EARCRX_CMDC_XACT_WR41 0x18e4 ++#define EARCRX_CMDC_XACT_WR42 0x18e8 ++#define EARCRX_CMDC_XACT_WR43 0x18ec ++#define EARCRX_CMDC_XACT_WR44 0x18f0 ++#define EARCRX_CMDC_XACT_WR45 0x18f4 ++#define EARCRX_CMDC_XACT_WR46 0x18f8 ++#define EARCRX_CMDC_XACT_WR47 0x18fc ++#define EARCRX_CMDC_XACT_WR48 0x1900 ++#define EARCRX_CMDC_XACT_WR49 0x1904 ++#define EARCRX_CMDC_XACT_WR50 0x1908 ++#define EARCRX_CMDC_XACT_WR51 0x190c ++#define EARCRX_CMDC_XACT_WR52 0x1910 ++#define EARCRX_CMDC_XACT_WR53 0x1914 ++#define EARCRX_CMDC_XACT_WR54 0x1918 ++#define EARCRX_CMDC_XACT_WR55 0x191c ++#define EARCRX_CMDC_XACT_WR56 0x1920 ++#define EARCRX_CMDC_XACT_WR57 0x1924 ++#define EARCRX_CMDC_XACT_WR58 0x1928 ++#define EARCRX_CMDC_XACT_WR59 0x192c ++#define EARCRX_CMDC_XACT_WR60 0x1930 ++#define EARCRX_CMDC_XACT_WR61 0x1934 ++#define EARCRX_CMDC_XACT_WR62 0x1938 ++#define EARCRX_CMDC_XACT_WR63 0x193c ++#define EARCRX_CMDC_XACT_WR64 0x1940 ++#define EARCRX_CMDC_XACT_RD0 0x1960 ++#define EARCRX_CMDC_XACT_RD1 0x1964 ++#define EARCRX_CMDC_XACT_RD2 0x1968 ++#define EARCRX_CMDC_XACT_RD3 0x196c ++#define EARCRX_CMDC_XACT_RD4 0x1970 ++#define EARCRX_CMDC_XACT_RD5 0x1974 ++#define EARCRX_CMDC_XACT_RD6 0x1978 ++#define EARCRX_CMDC_XACT_RD7 0x197c ++#define EARCRX_CMDC_XACT_RD8 0x1980 ++#define EARCRX_CMDC_XACT_RD9 0x1984 ++#define EARCRX_CMDC_XACT_RD10 0x1988 ++#define EARCRX_CMDC_XACT_RD11 0x198c ++#define EARCRX_CMDC_XACT_RD12 0x1990 ++#define EARCRX_CMDC_XACT_RD13 0x1994 ++#define EARCRX_CMDC_XACT_RD14 0x1998 ++#define EARCRX_CMDC_XACT_RD15 0x199c ++#define EARCRX_CMDC_XACT_RD16 0x19a0 ++#define EARCRX_CMDC_XACT_RD17 0x19a4 ++#define EARCRX_CMDC_XACT_RD18 0x19a8 ++#define EARCRX_CMDC_XACT_RD19 0x19ac ++#define EARCRX_CMDC_XACT_RD20 0x19b0 ++#define EARCRX_CMDC_XACT_RD21 0x19b4 ++#define EARCRX_CMDC_XACT_RD22 0x19b8 ++#define EARCRX_CMDC_XACT_RD23 0x19bc ++#define EARCRX_CMDC_XACT_RD24 0x19c0 ++#define EARCRX_CMDC_XACT_RD25 0x19c4 ++#define EARCRX_CMDC_XACT_RD26 0x19c8 ++#define EARCRX_CMDC_XACT_RD27 0x19cc ++#define EARCRX_CMDC_XACT_RD28 0x19d0 ++#define EARCRX_CMDC_XACT_RD29 0x19d4 ++#define EARCRX_CMDC_XACT_RD30 0x19d8 ++#define EARCRX_CMDC_XACT_RD31 0x19dc ++#define EARCRX_CMDC_XACT_RD32 0x19e0 ++#define EARCRX_CMDC_XACT_RD33 0x19e4 ++#define EARCRX_CMDC_XACT_RD34 0x19e8 ++#define EARCRX_CMDC_XACT_RD35 0x19ec ++#define EARCRX_CMDC_XACT_RD36 0x19f0 ++#define EARCRX_CMDC_XACT_RD37 0x19f4 ++#define EARCRX_CMDC_XACT_RD38 0x19f8 ++#define EARCRX_CMDC_XACT_RD39 0x19fc ++#define EARCRX_CMDC_XACT_RD40 0x1a00 ++#define EARCRX_CMDC_XACT_RD41 0x1a04 ++#define EARCRX_CMDC_XACT_RD42 0x1a08 ++#define EARCRX_CMDC_XACT_RD43 0x1a0c ++#define EARCRX_CMDC_XACT_RD44 0x1a10 ++#define EARCRX_CMDC_XACT_RD45 0x1a14 ++#define EARCRX_CMDC_XACT_RD46 0x1a18 ++#define EARCRX_CMDC_XACT_RD47 0x1a1c ++#define EARCRX_CMDC_XACT_RD48 0x1a20 ++#define EARCRX_CMDC_XACT_RD49 0x1a24 ++#define EARCRX_CMDC_XACT_RD50 0x1a28 ++#define EARCRX_CMDC_XACT_RD51 0x1a2c ++#define EARCRX_CMDC_XACT_RD52 0x1a30 ++#define EARCRX_CMDC_XACT_RD53 0x1a34 ++#define EARCRX_CMDC_XACT_RD54 0x1a38 ++#define EARCRX_CMDC_XACT_RD55 0x1a3c ++#define EARCRX_CMDC_XACT_RD56 0x1a40 ++#define EARCRX_CMDC_XACT_RD57 0x1a44 ++#define EARCRX_CMDC_XACT_RD58 0x1a48 ++#define EARCRX_CMDC_XACT_RD59 0x1a4c ++#define EARCRX_CMDC_XACT_RD60 0x1a50 ++#define EARCRX_CMDC_XACT_RD61 0x1a54 ++#define EARCRX_CMDC_XACT_RD62 0x1a58 ++#define EARCRX_CMDC_XACT_RD63 0x1a5c ++#define EARCRX_CMDC_XACT_RD64 0x1a60 ++#define EARCRX_CMDC_SYNC_CONFIG 0x1b00 ++/* eARC RX DMAC Registers */ ++#define EARCRX_DMAC_PHY_CONTROL 0x1c00 ++#define EARCRX_DMAC_CONFIG 0x1c08 ++#define EARCRX_DMAC_CONTROL0 0x1c0c ++#define EARCRX_DMAC_AUDIO_EN BIT(1) ++#define EARCRX_DMAC_EN BIT(0) ++#define EARCRX_DMAC_CONTROL1 0x1c10 ++#define EARCRX_DMAC_STATUS 0x1c14 ++#define EARCRX_DMAC_CHSTATUS0 0x1c18 ++#define EARCRX_DMAC_CHSTATUS1 0x1c1c ++#define EARCRX_DMAC_CHSTATUS2 0x1c20 ++#define EARCRX_DMAC_CHSTATUS3 0x1c24 ++#define EARCRX_DMAC_CHSTATUS4 0x1c28 ++#define EARCRX_DMAC_CHSTATUS5 0x1c2c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC0 0x1c30 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC1 0x1c34 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC2 0x1c38 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC3 0x1c3c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC4 0x1c40 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC5 0x1c44 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC6 0x1c48 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC7 0x1c4c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC8 0x1c50 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC9 0x1c54 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC10 0x1c58 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC11 0x1c5c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT0 0x1c60 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT1 0x1c64 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT2 0x1c68 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT3 0x1c6c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT4 0x1c70 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT5 0x1c74 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT6 0x1c78 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT7 0x1c7c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT8 0x1c80 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT9 0x1c84 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT10 0x1c88 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT11 0x1c8c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT0 0x1c90 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT1 0x1c94 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT2 0x1c98 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT3 0x1c9c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT4 0x1ca0 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT5 0x1ca4 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT6 0x1ca8 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT7 0x1cac ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT8 0x1cb0 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT9 0x1cb4 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT10 0x1cb8 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT11 0x1cbc ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC0 0x1cc0 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC1 0x1cc4 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC2 0x1cc8 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC3 0x1ccc ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC4 0x1cd0 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC5 0x1cd4 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC6 0x1cd8 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC7 0x1cdc ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC8 0x1ce0 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC9 0x1ce4 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC10 0x1ce8 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC11 0x1cec ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC12 0x1cf0 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC13 0x1cf4 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC14 0x1cf8 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC15 0x1cfc ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC16 0x1d00 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC17 0x1d04 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC18 0x1d08 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC19 0x1d0c ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC20 0x1d10 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC21 0x1d14 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC22 0x1d18 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC23 0x1d1c ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC24 0x1d20 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC25 0x1d24 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC26 0x1d28 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC27 0x1d2c ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC28 0x1d30 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC29 0x1d34 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC30 0x1d38 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC31 0x1d3c ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC32 0x1d40 ++#define EARCRX_DMAC_CHSTATUS_STREAMER0 0x1d44 ++#define EARCRX_DMAC_CHSTATUS_STREAMER1 0x1d48 ++#define EARCRX_DMAC_CHSTATUS_STREAMER2 0x1d4c ++#define EARCRX_DMAC_CHSTATUS_STREAMER3 0x1d50 ++#define EARCRX_DMAC_CHSTATUS_STREAMER4 0x1d54 ++#define EARCRX_DMAC_CHSTATUS_STREAMER5 0x1d58 ++#define EARCRX_DMAC_CHSTATUS_STREAMER6 0x1d5c ++#define EARCRX_DMAC_CHSTATUS_STREAMER7 0x1d60 ++#define EARCRX_DMAC_CHSTATUS_STREAMER8 0x1d64 ++#define EARCRX_DMAC_CHSTATUS_STREAMER9 0x1d68 ++#define EARCRX_DMAC_CHSTATUS_STREAMER10 0x1d6c ++#define EARCRX_DMAC_CHSTATUS_STREAMER11 0x1d70 ++#define EARCRX_DMAC_CHSTATUS_STREAMER12 0x1d74 ++#define EARCRX_DMAC_CHSTATUS_STREAMER13 0x1d78 ++#define EARCRX_DMAC_CHSTATUS_STREAMER14 0x1d7c ++#define EARCRX_DMAC_USRDATA_STREAMER0 0x1d80 ++/* Main Unit Interrupt Registers */ ++#define MAIN_INTVEC_INDEX 0x3000 ++#define MAINUNIT_0_INT_STATUS 0x3010 ++#define MAINUNIT_0_INT_MASK_N 0x3014 ++#define MAINUNIT_0_INT_CLEAR 0x3018 ++#define MAINUNIT_0_INT_FORCE 0x301c ++#define MAINUNIT_1_INT_STATUS 0x3020 ++#define FLT_EXIT_TO_LTSL_IRQ BIT(22) ++#define FLT_EXIT_TO_LTS4_IRQ BIT(21) ++#define FLT_EXIT_TO_LTSP_IRQ BIT(20) ++#define SCDC_NACK_RCVD_IRQ BIT(12) ++#define SCDC_RR_REPLY_STOP_IRQ BIT(11) ++#define SCDC_UPD_FLAGS_CLR_IRQ BIT(10) ++#define SCDC_UPD_FLAGS_CHG_IRQ BIT(9) ++#define SCDC_UPD_FLAGS_RD_IRQ BIT(8) ++#define I2CM_NACK_RCVD_IRQ BIT(2) ++#define I2CM_READ_REQUEST_IRQ BIT(1) ++#define I2CM_OP_DONE_IRQ BIT(0) ++#define MAINUNIT_1_INT_MASK_N 0x3024 ++#define I2CM_NACK_RCVD_MASK_N BIT(2) ++#define I2CM_READ_REQUEST_MASK_N BIT(1) ++#define I2CM_OP_DONE_MASK_N BIT(0) ++#define MAINUNIT_1_INT_CLEAR 0x3028 ++#define I2CM_NACK_RCVD_CLEAR BIT(2) ++#define I2CM_READ_REQUEST_CLEAR BIT(1) ++#define I2CM_OP_DONE_CLEAR BIT(0) ++#define MAINUNIT_1_INT_FORCE 0x302c ++/* AVPUNIT Interrupt Registers */ ++#define AVP_INTVEC_INDEX 0x3800 ++#define AVP_0_INT_STATUS 0x3810 ++#define AVP_0_INT_MASK_N 0x3814 ++#define AVP_0_INT_CLEAR 0x3818 ++#define AVP_0_INT_FORCE 0x381c ++#define AVP_1_INT_STATUS 0x3820 ++#define AVP_1_INT_MASK_N 0x3824 ++#define HDCP14_AUTH_CHG_MASK_N BIT(6) ++#define AVP_1_INT_CLEAR 0x3828 ++#define AVP_1_INT_FORCE 0x382c ++#define AVP_2_INT_STATUS 0x3830 ++#define AVP_2_INT_MASK_N 0x3834 ++#define AVP_2_INT_CLEAR 0x3838 ++#define AVP_2_INT_FORCE 0x383c ++#define AVP_3_INT_STATUS 0x3840 ++#define AVP_3_INT_MASK_N 0x3844 ++#define AVP_3_INT_CLEAR 0x3848 ++#define AVP_3_INT_FORCE 0x384c ++#define AVP_4_INT_STATUS 0x3850 ++#define AVP_4_INT_MASK_N 0x3854 ++#define AVP_4_INT_CLEAR 0x3858 ++#define AVP_4_INT_FORCE 0x385c ++#define AVP_5_INT_STATUS 0x3860 ++#define AVP_5_INT_MASK_N 0x3864 ++#define AVP_5_INT_CLEAR 0x3868 ++#define AVP_5_INT_FORCE 0x386c ++#define AVP_6_INT_STATUS 0x3870 ++#define AVP_6_INT_MASK_N 0x3874 ++#define AVP_6_INT_CLEAR 0x3878 ++#define AVP_6_INT_FORCE 0x387c ++/* CEC Interrupt Registers */ ++#define CEC_INT_STATUS 0x4000 ++#define CEC_INT_MASK_N 0x4004 ++#define CEC_INT_CLEAR 0x4008 ++#define CEC_INT_FORCE 0x400c ++/* eARC RX Interrupt Registers */ ++#define EARCRX_INTVEC_INDEX 0x4800 ++#define EARCRX_0_INT_STATUS 0x4810 ++#define EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ BIT(9) ++#define EARCRX_CMDC_DISCOVERY_DONE_IRQ BIT(8) ++#define EARCRX_0_INT_MASK_N 0x4814 ++#define EARCRX_0_INT_CLEAR 0x4818 ++#define EARCRX_0_INT_FORCE 0x481c ++#define EARCRX_1_INT_STATUS 0x4820 ++#define EARCRX_1_INT_MASK_N 0x4824 ++#define EARCRX_1_INT_CLEAR 0x4828 ++#define EARCRX_1_INT_FORCE 0x482c ++ ++#endif /* __DW_HDMI_QP_H__ */ +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +index 52d91a0df..0d6fd9578 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +@@ -162,6 +162,8 @@ struct dw_hdmi { + void __iomem *regs; + bool sink_is_hdmi; + bool sink_has_audio; ++ bool support_hdmi; ++ int force_output; + + struct pinctrl *pinctrl; + struct pinctrl_state *default_state; +@@ -254,6 +256,25 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, + hdmi_modb(hdmi, data << shift, mask, reg); + } + ++static bool dw_hdmi_check_output_type_changed(struct dw_hdmi *hdmi) ++{ ++ bool sink_hdmi; ++ ++ sink_hdmi = hdmi->sink_is_hdmi; ++ ++ if (hdmi->force_output == 1) ++ hdmi->sink_is_hdmi = true; ++ else if (hdmi->force_output == 2) ++ hdmi->sink_is_hdmi = false; ++ else ++ hdmi->sink_is_hdmi = hdmi->support_hdmi; ++ ++ if (sink_hdmi != hdmi->sink_is_hdmi) ++ return true; ++ ++ return false; ++} ++ + static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) + { + hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, +@@ -2532,6 +2553,45 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, + return 0; + } + ++void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi) ++{ ++ if (!hdmi->bridge_is_on) ++ return; ++ ++ hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP); ++ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); ++ hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_set_quant_range); ++ ++void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val) ++{ ++ hdmi->force_output = val; ++ ++ if (!dw_hdmi_check_output_type_changed(hdmi)) ++ return; ++ ++ if (!hdmi->bridge_is_on) ++ return; ++ ++ hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP); ++ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); ++ hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_set_output_type); ++ ++bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi) ++{ ++ return hdmi->sink_is_hdmi; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_get_output_whether_hdmi); ++ ++int dw_hdmi_get_output_type_cap(struct dw_hdmi *hdmi) ++{ ++ return hdmi->support_hdmi; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_get_output_type_cap); ++ + static void dw_hdmi_connector_force(struct drm_connector *connector) + { + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, +@@ -3683,6 +3743,35 @@ void dw_hdmi_unbind(struct dw_hdmi *hdmi) + } + EXPORT_SYMBOL_GPL(dw_hdmi_unbind); + ++void dw_hdmi_suspend(struct dw_hdmi *hdmi) ++{ ++ if (!hdmi) ++ return; ++ ++ mutex_lock(&hdmi->mutex); ++ ++ /* ++ * When system shutdown, hdmi should be disabled. ++ * When system suspend, dw_hdmi_bridge_disable will disable hdmi first. ++ * To prevent duplicate operation, we should determine whether hdmi ++ * has been disabled. ++ */ ++ if (!hdmi->disabled) { ++ hdmi->disabled = true; ++ dw_hdmi_update_power(hdmi); ++ dw_hdmi_update_phy_mask(hdmi); ++ } ++ mutex_unlock(&hdmi->mutex); ++ ++ //[CC: needed?] ++ // if (hdmi->irq) ++ // disable_irq(hdmi->irq); ++ // cancel_delayed_work(&hdmi->work); ++ // flush_workqueue(hdmi->workqueue); ++ pinctrl_pm_select_sleep_state(hdmi->dev); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_suspend); ++ + void dw_hdmi_resume(struct dw_hdmi *hdmi) + { + dw_hdmi_init_hw(hdmi); +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +index af43a0414..8ebdec725 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +@@ -851,6 +851,10 @@ enum { + HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00, + HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04, + ++/* HDMI_FC_GCP */ ++ HDMI_FC_GCP_SET_AVMUTE = 0x2, ++ HDMI_FC_GCP_CLEAR_AVMUTE = 0x1, ++ + /* FC_DBGFORCE field values */ + HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10, + HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1, +diff --git a/drivers/gpu/drm/drm_displayid.c b/drivers/gpu/drm/drm_displayid.c +index 9edc111be..a578f918f 100644 +--- a/drivers/gpu/drm/drm_displayid.c ++++ b/drivers/gpu/drm/drm_displayid.c +@@ -3,6 +3,7 @@ + * Copyright © 2021 Intel Corporation + */ + ++#include "linux/export.h" + #include + #include + #include +@@ -79,6 +80,7 @@ void displayid_iter_edid_begin(const struct drm_edid *drm_edid, + + iter->drm_edid = drm_edid; + } ++EXPORT_SYMBOL_GPL(displayid_iter_edid_begin); + + static const struct displayid_block * + displayid_iter_block(const struct displayid_iter *iter) +@@ -154,11 +156,13 @@ __displayid_iter_next(struct displayid_iter *iter) + return block; + } + } ++EXPORT_SYMBOL_GPL(__displayid_iter_next); + + void displayid_iter_end(struct displayid_iter *iter) + { + memset(iter, 0, sizeof(*iter)); + } ++EXPORT_SYMBOL_GPL(displayid_iter_end); + + /* DisplayID Structure Version/Revision from the Base Section. */ + u8 displayid_version(const struct displayid_iter *iter) +diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +index 341550199..52f457b16 100644 +--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c ++++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +@@ -4,22 +4,33 @@ + */ + + #include ++#include ++#include + #include + #include + #include + #include ++#include + #include + #include + ++#include ++#include ++#include ++#include + #include + #include + #include + #include + #include + ++#include ++ + #include "rockchip_drm_drv.h" + #include "rockchip_drm_vop.h" + ++#define HIWORD_UPDATE(val, mask) (val | (mask) << 16) ++ + #define RK3228_GRF_SOC_CON2 0x0408 + #define RK3228_HDMI_SDAIN_MSK BIT(14) + #define RK3228_HDMI_SCLIN_MSK BIT(13) +@@ -30,8 +41,11 @@ + + #define RK3288_GRF_SOC_CON6 0x025C + #define RK3288_HDMI_LCDC_SEL BIT(4) +-#define RK3328_GRF_SOC_CON2 0x0408 ++#define RK3288_GRF_SOC_CON16 0x03a8 ++#define RK3288_HDMI_LCDC0_YUV420 BIT(2) ++#define RK3288_HDMI_LCDC1_YUV420 BIT(3) + ++#define RK3328_GRF_SOC_CON2 0x0408 + #define RK3328_HDMI_SDAIN_MSK BIT(11) + #define RK3328_HDMI_SCLIN_MSK BIT(10) + #define RK3328_HDMI_HPD_IOE BIT(2) +@@ -55,32 +69,154 @@ + #define RK3568_HDMI_SDAIN_MSK BIT(15) + #define RK3568_HDMI_SCLIN_MSK BIT(14) + +-#define HIWORD_UPDATE(val, mask) (val | (mask) << 16) ++#define RK3588_GRF_SOC_CON2 0x0308 ++#define RK3588_HDMI1_HPD_INT_MSK BIT(15) ++#define RK3588_HDMI1_HPD_INT_CLR BIT(14) ++#define RK3588_HDMI0_HPD_INT_MSK BIT(13) ++#define RK3588_HDMI0_HPD_INT_CLR BIT(12) ++#define RK3588_GRF_SOC_CON7 0x031c ++#define RK3588_SET_HPD_PATH_MASK (0x3 << 12) ++#define RK3588_GRF_SOC_STATUS1 0x0384 ++#define RK3588_HDMI0_LOW_MORETHAN100MS BIT(20) ++#define RK3588_HDMI0_HPD_PORT_LEVEL BIT(19) ++#define RK3588_HDMI0_IHPD_PORT BIT(18) ++#define RK3588_HDMI0_OHPD_INT BIT(17) ++#define RK3588_HDMI0_LEVEL_INT BIT(16) ++#define RK3588_HDMI0_INTR_CHANGE_CNT (0x7 << 13) ++#define RK3588_HDMI1_LOW_MORETHAN100MS BIT(28) ++#define RK3588_HDMI1_HPD_PORT_LEVEL BIT(27) ++#define RK3588_HDMI1_IHPD_PORT BIT(26) ++#define RK3588_HDMI1_OHPD_INT BIT(25) ++#define RK3588_HDMI1_LEVEL_INT BIT(24) ++#define RK3588_HDMI1_INTR_CHANGE_CNT (0x7 << 21) ++ ++#define RK3588_GRF_VO1_CON3 0x000c ++#define RK3588_COLOR_FORMAT_MASK 0xf ++#define RK3588_YUV444 0x2 ++#define RK3588_YUV420 0x3 ++#define RK3588_COMPRESSED_DATA 0xb ++#define RK3588_COLOR_DEPTH_MASK (0xf << 4) ++#define RK3588_8BPC (0x5 << 4) ++#define RK3588_10BPC (0x6 << 4) ++#define RK3588_CECIN_MASK BIT(8) ++#define RK3588_SCLIN_MASK BIT(9) ++#define RK3588_SDAIN_MASK BIT(10) ++#define RK3588_MODE_MASK BIT(11) ++#define RK3588_COMPRESS_MODE_MASK BIT(12) ++#define RK3588_I2S_SEL_MASK BIT(13) ++#define RK3588_SPDIF_SEL_MASK BIT(14) ++#define RK3588_GRF_VO1_CON4 0x0010 ++#define RK3588_HDMI21_MASK BIT(0) ++#define RK3588_GRF_VO1_CON9 0x0024 ++#define RK3588_HDMI0_GRANT_SEL BIT(10) ++#define RK3588_HDMI0_GRANT_SW BIT(11) ++#define RK3588_HDMI1_GRANT_SEL BIT(12) ++#define RK3588_HDMI1_GRANT_SW BIT(13) ++#define RK3588_GRF_VO1_CON6 0x0018 ++#define RK3588_GRF_VO1_CON7 0x001c ++ ++#define COLOR_DEPTH_10BIT BIT(31) ++#define HDMI_FRL_MODE BIT(30) ++#define HDMI_EARC_MODE BIT(29) ++ ++#define HDMI20_MAX_RATE 600000 ++#define HDMI_8K60_RATE 2376000 + + /** + * struct rockchip_hdmi_chip_data - splite the grf setting of kind of chips + * @lcdsel_grf_reg: grf register offset of lcdc select ++ * @ddc_en_reg: grf register offset of hdmi ddc enable + * @lcdsel_big: reg value of selecting vop big for HDMI + * @lcdsel_lit: reg value of selecting vop little for HDMI + */ + struct rockchip_hdmi_chip_data { + int lcdsel_grf_reg; ++ int ddc_en_reg; + u32 lcdsel_big; + u32 lcdsel_lit; ++ bool split_mode; ++}; ++ ++enum hdmi_frl_rate_per_lane { ++ FRL_12G_PER_LANE = 12, ++ FRL_10G_PER_LANE = 10, ++ FRL_8G_PER_LANE = 8, ++ FRL_6G_PER_LANE = 6, ++ FRL_3G_PER_LANE = 3, + }; + + struct rockchip_hdmi { + struct device *dev; + struct regmap *regmap; ++ struct regmap *vo1_regmap; + struct rockchip_encoder encoder; ++ struct drm_device *drm_dev; + const struct rockchip_hdmi_chip_data *chip_data; +- const struct dw_hdmi_plat_data *plat_data; ++ struct dw_hdmi_plat_data *plat_data; ++ struct clk *aud_clk; + struct clk *ref_clk; + struct clk *grf_clk; ++ struct clk *hclk_vio; ++ struct clk *hclk_vo1; ++ struct clk *hclk_vop; ++ struct clk *hpd_clk; ++ struct clk *pclk; ++ struct clk *earc_clk; ++ struct clk *hdmitx_ref; + struct dw_hdmi *hdmi; ++ struct dw_hdmi_qp *hdmi_qp; ++ + struct regulator *avdd_0v9; + struct regulator *avdd_1v8; + struct phy *phy; ++ ++ u32 max_tmdsclk; ++ bool unsupported_yuv_input; ++ bool unsupported_deep_color; ++ bool skip_check_420_mode; ++ u8 force_output; ++ u8 id; ++ bool hpd_stat; ++ bool is_hdmi_qp; ++ bool user_split_mode; ++ ++ unsigned long bus_format; ++ unsigned long output_bus_format; ++ unsigned long enc_out_encoding; ++ int color_changed; ++ int hpd_irq; ++ int vp_id; ++ ++ struct drm_property *color_depth_property; ++ struct drm_property *hdmi_output_property; ++ struct drm_property *colordepth_capacity; ++ struct drm_property *outputmode_capacity; ++ struct drm_property *quant_range; ++ struct drm_property *hdr_panel_metadata_property; ++ struct drm_property *next_hdr_sink_data_property; ++ struct drm_property *output_hdmi_dvi; ++ struct drm_property *output_type_capacity; ++ struct drm_property *user_split_mode_prop; ++ ++ struct drm_property_blob *hdr_panel_blob_ptr; ++ struct drm_property_blob *next_hdr_data_ptr; ++ ++ unsigned int colordepth; ++ unsigned int colorimetry; ++ unsigned int hdmi_quant_range; ++ unsigned int phy_bus_width; ++ enum rk_if_color_format hdmi_output; ++ struct rockchip_drm_sub_dev sub_dev; ++ ++ u8 max_frl_rate_per_lane; ++ u8 max_lanes; ++ struct rockchip_drm_dsc_cap dsc_cap; ++ struct next_hdr_sink_data next_hdr_data; ++ struct dw_hdmi_link_config link_cfg; ++ struct gpio_desc *enable_gpio; ++ ++ struct delayed_work work; ++ struct workqueue_struct *workqueue; + }; + + static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder) +@@ -203,13 +339,834 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = { + /*pixelclk symbol term vlev*/ + { 74250000, 0x8009, 0x0004, 0x0272}, + { 148500000, 0x802b, 0x0004, 0x028d}, ++ { 165000000, 0x802b, 0x0004, 0x0209}, + { 297000000, 0x8039, 0x0005, 0x028d}, ++ { 594000000, 0x8039, 0x0000, 0x019d}, + { ~0UL, 0x0000, 0x0000, 0x0000} + }; + ++enum ROW_INDEX_BPP { ++ ROW_INDEX_6BPP = 0, ++ ROW_INDEX_8BPP, ++ ROW_INDEX_10BPP, ++ ROW_INDEX_12BPP, ++ ROW_INDEX_23BPP, ++ MAX_ROW_INDEX ++}; ++ ++enum COLUMN_INDEX_BPC { ++ COLUMN_INDEX_8BPC = 0, ++ COLUMN_INDEX_10BPC, ++ COLUMN_INDEX_12BPC, ++ COLUMN_INDEX_14BPC, ++ COLUMN_INDEX_16BPC, ++ MAX_COLUMN_INDEX ++}; ++ ++#define PPS_TABLE_LEN 8 ++#define PPS_BPP_LEN 4 ++#define PPS_BPC_LEN 2 ++ ++struct pps_data { ++ u32 pic_width; ++ u32 pic_height; ++ u32 slice_width; ++ u32 slice_height; ++ bool convert_rgb; ++ u8 bpc; ++ u8 bpp; ++ u8 raw_pps[128]; ++}; ++ ++/* ++ * Selected Rate Control Related Parameter Recommended Values ++ * from DSC_v1.11 spec & C Model release: DSC_model_20161212 ++ */ ++static struct pps_data pps_datas[PPS_TABLE_LEN] = { ++ { ++ /* 7680x4320/960X96 rgb 8bpc 12bpp */ ++ 7680, 4320, 960, 96, 1, 8, 192, ++ { ++ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xc0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0, ++ 0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9, ++ 0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa, ++ 0x08, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00, ++ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x00, 0x82, 0x00, 0xc0, 0x09, 0x00, ++ 0x09, 0x7e, 0x19, 0xbc, 0x19, 0xba, 0x19, 0xf8, ++ 0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76, ++ 0x2a, 0x76, 0x2a, 0x74, 0x3a, 0xb4, 0x52, 0xf4, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 8bpc 11bpp */ ++ 7680, 4320, 960, 96, 1, 8, 176, ++ { ++ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xb0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28, ++ 0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0, ++ 0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33, ++ 0x0f, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00, ++ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x00, 0x82, 0x01, 0x00, 0x09, 0x40, ++ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, ++ 0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76, ++ 0x2a, 0x76, 0x2a, 0xb4, 0x3a, 0xb4, 0x52, 0xf4, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 8bpc 10bpp */ ++ 7680, 4320, 960, 96, 1, 8, 160, ++ { ++ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xa0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0, ++ 0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0, ++ 0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb, ++ 0x16, 0x00, 0x10, 0xec, 0x03, 0x0c, 0x20, 0x00, ++ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40, ++ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, ++ 0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6, ++ 0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x5b, 0x34, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 8bpc 9bpp */ ++ 7680, 4320, 960, 96, 1, 8, 144, ++ { ++ 0x12, 0x00, 0x00, 0x8d, 0x30, 0x90, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38, ++ 0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7, ++ 0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa, ++ 0x17, 0x00, 0x10, 0xf1, 0x03, 0x0c, 0x20, 0x00, ++ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40, ++ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, ++ 0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6, ++ 0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x63, 0x74, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 10bpc 12bpp */ ++ 7680, 4320, 960, 96, 1, 10, 192, ++ { ++ 0x12, 0x00, 0x00, 0xad, 0x30, 0xc0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0, ++ 0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9, ++ 0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa, ++ 0x08, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00, ++ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x01, 0x02, 0x11, 0x80, 0x22, 0x00, ++ 0x22, 0x7e, 0x32, 0xbc, 0x32, 0xba, 0x3a, 0xf8, ++ 0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76, ++ 0x4b, 0x76, 0x4b, 0x74, 0x5b, 0xb4, 0x73, 0xf4, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 10bpc 11bpp */ ++ 7680, 4320, 960, 96, 1, 10, 176, ++ { ++ 0x12, 0x00, 0x00, 0xad, 0x30, 0xb0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28, ++ 0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0, ++ 0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33, ++ 0x0f, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00, ++ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x01, 0x42, 0x19, 0xc0, 0x2a, 0x40, ++ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, ++ 0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76, ++ 0x4b, 0x76, 0x4b, 0xb4, 0x5b, 0xb4, 0x73, 0xf4, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 10bpc 10bpp */ ++ 7680, 4320, 960, 96, 1, 10, 160, ++ { ++ 0x12, 0x00, 0x00, 0xad, 0x30, 0xa0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0, ++ 0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0, ++ 0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb, ++ 0x16, 0x00, 0x10, 0xec, 0x07, 0x10, 0x20, 0x00, ++ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40, ++ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, ++ 0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6, ++ 0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x7c, 0x34, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 10bpc 9bpp */ ++ 7680, 4320, 960, 96, 1, 10, 144, ++ { ++ 0x12, 0x00, 0x00, 0xad, 0x30, 0x90, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38, ++ 0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7, ++ 0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa, ++ 0x17, 0x00, 0x10, 0xf1, 0x07, 0x10, 0x20, 0x00, ++ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40, ++ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, ++ 0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6, ++ 0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x84, 0x74, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++}; ++ ++static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_YUV12_1X36: ++ case MEDIA_BUS_FMT_YUV16_1X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: ++ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static int hdmi_bus_fmt_color_depth(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ return 8; ++ ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ return 10; ++ ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_YUV12_1X36: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: ++ return 12; ++ ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ case MEDIA_BUS_FMT_YUV16_1X48: ++ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: ++ return 16; ++ ++ default: ++ return 0; ++ } ++} ++ ++static unsigned int ++hdmi_get_tmdsclock(struct rockchip_hdmi *hdmi, unsigned long pixelclock) ++{ ++ unsigned int tmdsclock = pixelclock; ++ unsigned int depth = ++ hdmi_bus_fmt_color_depth(hdmi->output_bus_format); ++ ++ if (!hdmi_bus_fmt_is_yuv422(hdmi->output_bus_format)) { ++ switch (depth) { ++ case 16: ++ tmdsclock = pixelclock * 2; ++ break; ++ case 12: ++ tmdsclock = pixelclock * 3 / 2; ++ break; ++ case 10: ++ tmdsclock = pixelclock * 5 / 4; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return tmdsclock; ++} ++ ++static int rockchip_hdmi_match_by_id(struct device *dev, const void *data) ++{ ++ struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); ++ const unsigned int *id = data; ++ ++ return hdmi->id == *id; ++} ++ ++static struct rockchip_hdmi * ++rockchip_hdmi_find_by_id(struct device_driver *drv, unsigned int id) ++{ ++ struct device *dev; ++ ++ dev = driver_find_device(drv, NULL, &id, rockchip_hdmi_match_by_id); ++ if (!dev) ++ return NULL; ++ ++ return dev_get_drvdata(dev); ++} ++ ++static void hdmi_select_link_config(struct rockchip_hdmi *hdmi, ++ struct drm_crtc_state *crtc_state, ++ unsigned int tmdsclk) ++{ ++ struct drm_display_mode mode; ++ int max_lanes, max_rate_per_lane; ++ int max_dsc_lanes, max_dsc_rate_per_lane; ++ unsigned long max_frl_rate; ++ ++ drm_mode_copy(&mode, &crtc_state->mode); ++ if (hdmi->plat_data->split_mode) ++ drm_mode_convert_to_origin_mode(&mode); ++ ++ max_lanes = hdmi->max_lanes; ++ max_rate_per_lane = hdmi->max_frl_rate_per_lane; ++ max_frl_rate = max_lanes * max_rate_per_lane * 1000000; ++ ++ hdmi->link_cfg.dsc_mode = false; ++ hdmi->link_cfg.frl_lanes = max_lanes; ++ hdmi->link_cfg.rate_per_lane = max_rate_per_lane; ++ ++ if (!max_frl_rate || (tmdsclk < HDMI20_MAX_RATE && mode.clock < HDMI20_MAX_RATE)) { ++ dev_info(hdmi->dev, "use tmds mode\n"); ++ hdmi->link_cfg.frl_mode = false; ++ return; ++ } ++ ++ hdmi->link_cfg.frl_mode = true; ++ ++ if (!hdmi->dsc_cap.v_1p2) ++ return; ++ ++ max_dsc_lanes = hdmi->dsc_cap.max_lanes; ++ max_dsc_rate_per_lane = ++ hdmi->dsc_cap.max_frl_rate_per_lane; ++ ++ if (mode.clock >= HDMI_8K60_RATE && ++ !hdmi_bus_fmt_is_yuv420(hdmi->bus_format) && ++ !hdmi_bus_fmt_is_yuv422(hdmi->bus_format)) { ++ hdmi->link_cfg.dsc_mode = true; ++ hdmi->link_cfg.frl_lanes = max_dsc_lanes; ++ hdmi->link_cfg.rate_per_lane = max_dsc_rate_per_lane; ++ } else { ++ hdmi->link_cfg.dsc_mode = false; ++ hdmi->link_cfg.frl_lanes = max_lanes; ++ hdmi->link_cfg.rate_per_lane = max_rate_per_lane; ++ } ++} ++ ++///////////////////////////////////////////////////////////////////////////////////// ++ ++static int hdmi_dsc_get_slice_height(int vactive) ++{ ++ int slice_height; ++ ++ /* ++ * Slice Height determination : HDMI2.1 Section 7.7.5.2 ++ * Select smallest slice height >=96, that results in a valid PPS and ++ * requires minimum padding lines required for final slice. ++ * ++ * Assumption : Vactive is even. ++ */ ++ for (slice_height = 96; slice_height <= vactive; slice_height += 2) ++ if (vactive % slice_height == 0) ++ return slice_height; ++ ++ return 0; ++} ++ ++static int hdmi_dsc_get_num_slices(struct rockchip_hdmi *hdmi, ++ struct drm_crtc_state *crtc_state, ++ int src_max_slices, int src_max_slice_width, ++ int hdmi_max_slices, int hdmi_throughput) ++{ ++/* Pixel rates in KPixels/sec */ ++#define HDMI_DSC_PEAK_PIXEL_RATE 2720000 ++/* ++ * Rates at which the source and sink are required to process pixels in each ++ * slice, can be two levels: either at least 340000KHz or at least 40000KHz. ++ */ ++#define HDMI_DSC_MAX_ENC_THROUGHPUT_0 340000 ++#define HDMI_DSC_MAX_ENC_THROUGHPUT_1 400000 ++ ++/* Spec limits the slice width to 2720 pixels */ ++#define MAX_HDMI_SLICE_WIDTH 2720 ++ int kslice_adjust; ++ int adjusted_clk_khz; ++ int min_slices; ++ int target_slices; ++ int max_throughput; /* max clock freq. in khz per slice */ ++ int max_slice_width; ++ int slice_width; ++ int pixel_clock = crtc_state->mode.clock; ++ ++ if (!hdmi_throughput) ++ return 0; ++ ++ /* ++ * Slice Width determination : HDMI2.1 Section 7.7.5.1 ++ * kslice_adjust factor for 4:2:0, and 4:2:2 formats is 0.5, where as ++ * for 4:4:4 is 1.0. Multiplying these factors by 10 and later ++ * dividing adjusted clock value by 10. ++ */ ++ if (hdmi_bus_fmt_is_yuv444(hdmi->output_bus_format) || ++ hdmi_bus_fmt_is_rgb(hdmi->output_bus_format)) ++ kslice_adjust = 10; ++ else ++ kslice_adjust = 5; ++ ++ /* ++ * As per spec, the rate at which the source and the sink process ++ * the pixels per slice are at two levels: at least 340Mhz or 400Mhz. ++ * This depends upon the pixel clock rate and output formats ++ * (kslice adjust). ++ * If pixel clock * kslice adjust >= 2720MHz slices can be processed ++ * at max 340MHz, otherwise they can be processed at max 400MHz. ++ */ ++ ++ adjusted_clk_khz = DIV_ROUND_UP(kslice_adjust * pixel_clock, 10); ++ ++ if (adjusted_clk_khz <= HDMI_DSC_PEAK_PIXEL_RATE) ++ max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_0; ++ else ++ max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_1; ++ ++ /* ++ * Taking into account the sink's capability for maximum ++ * clock per slice (in MHz) as read from HF-VSDB. ++ */ ++ max_throughput = min(max_throughput, hdmi_throughput * 1000); ++ ++ min_slices = DIV_ROUND_UP(adjusted_clk_khz, max_throughput); ++ max_slice_width = min(MAX_HDMI_SLICE_WIDTH, src_max_slice_width); ++ ++ /* ++ * Keep on increasing the num of slices/line, starting from min_slices ++ * per line till we get such a number, for which the slice_width is ++ * just less than max_slice_width. The slices/line selected should be ++ * less than or equal to the max horizontal slices that the combination ++ * of PCON encoder and HDMI decoder can support. ++ */ ++ do { ++ if (min_slices <= 1 && src_max_slices >= 1 && hdmi_max_slices >= 1) ++ target_slices = 1; ++ else if (min_slices <= 2 && src_max_slices >= 2 && hdmi_max_slices >= 2) ++ target_slices = 2; ++ else if (min_slices <= 4 && src_max_slices >= 4 && hdmi_max_slices >= 4) ++ target_slices = 4; ++ else if (min_slices <= 8 && src_max_slices >= 8 && hdmi_max_slices >= 8) ++ target_slices = 8; ++ else if (min_slices <= 12 && src_max_slices >= 12 && hdmi_max_slices >= 12) ++ target_slices = 12; ++ else if (min_slices <= 16 && src_max_slices >= 16 && hdmi_max_slices >= 16) ++ target_slices = 16; ++ else ++ return 0; ++ ++ slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, target_slices); ++ if (slice_width > max_slice_width) ++ min_slices = target_slices + 1; ++ } while (slice_width > max_slice_width); ++ ++ return target_slices; ++} ++ ++static int hdmi_dsc_slices(struct rockchip_hdmi *hdmi, ++ struct drm_crtc_state *crtc_state) ++{ ++ int hdmi_throughput = hdmi->dsc_cap.clk_per_slice; ++ int hdmi_max_slices = hdmi->dsc_cap.max_slices; ++ int rk_max_slices = 8; ++ int rk_max_slice_width = 2048; ++ ++ return hdmi_dsc_get_num_slices(hdmi, crtc_state, rk_max_slices, ++ rk_max_slice_width, ++ hdmi_max_slices, hdmi_throughput); ++} ++ ++static int ++hdmi_dsc_get_bpp(struct rockchip_hdmi *hdmi, int src_fractional_bpp, ++ int slice_width, int num_slices, bool hdmi_all_bpp, ++ int hdmi_max_chunk_bytes) ++{ ++ int max_dsc_bpp, min_dsc_bpp; ++ int target_bytes; ++ bool bpp_found = false; ++ int bpp_decrement_x16; ++ int bpp_target; ++ int bpp_target_x16; ++ ++ /* ++ * Get min bpp and max bpp as per Table 7.23, in HDMI2.1 spec ++ * Start with the max bpp and keep on decrementing with ++ * fractional bpp, if supported by PCON DSC encoder ++ * ++ * for each bpp we check if no of bytes can be supported by HDMI sink ++ */ ++ ++ /* only 9\10\12 bpp was tested */ ++ min_dsc_bpp = 9; ++ max_dsc_bpp = 12; ++ ++ /* ++ * Taking into account if all dsc_all_bpp supported by HDMI2.1 sink ++ * Section 7.7.34 : Source shall not enable compressed Video ++ * Transport with bpp_target settings above 12 bpp unless ++ * DSC_all_bpp is set to 1. ++ */ ++ if (!hdmi_all_bpp) ++ max_dsc_bpp = min(max_dsc_bpp, 12); ++ ++ /* ++ * The Sink has a limit of compressed data in bytes for a scanline, ++ * as described in max_chunk_bytes field in HFVSDB block of edid. ++ * The no. of bytes depend on the target bits per pixel that the ++ * source configures. So we start with the max_bpp and calculate ++ * the target_chunk_bytes. We keep on decrementing the target_bpp, ++ * till we get the target_chunk_bytes just less than what the sink's ++ * max_chunk_bytes, or else till we reach the min_dsc_bpp. ++ * ++ * The decrement is according to the fractional support from PCON DSC ++ * encoder. For fractional BPP we use bpp_target as a multiple of 16. ++ * ++ * bpp_target_x16 = bpp_target * 16 ++ * So we need to decrement by {1, 2, 4, 8, 16} for fractional bpps ++ * {1/16, 1/8, 1/4, 1/2, 1} respectively. ++ */ ++ ++ bpp_target = max_dsc_bpp; ++ ++ /* src does not support fractional bpp implies decrement by 16 for bppx16 */ ++ if (!src_fractional_bpp) ++ src_fractional_bpp = 1; ++ bpp_decrement_x16 = DIV_ROUND_UP(16, src_fractional_bpp); ++ bpp_target_x16 = bpp_target * 16; ++ ++ while (bpp_target_x16 > (min_dsc_bpp * 16)) { ++ int bpp; ++ ++ bpp = DIV_ROUND_UP(bpp_target_x16, 16); ++ target_bytes = DIV_ROUND_UP((num_slices * slice_width * bpp), 8); ++ if (target_bytes <= hdmi_max_chunk_bytes) { ++ bpp_found = true; ++ break; ++ } ++ bpp_target_x16 -= bpp_decrement_x16; ++ } ++ if (bpp_found) ++ return bpp_target_x16; ++ ++ return 0; ++} ++ ++static int ++dw_hdmi_dsc_bpp(struct rockchip_hdmi *hdmi, ++ int num_slices, int slice_width) ++{ ++ bool hdmi_all_bpp = hdmi->dsc_cap.all_bpp; ++ int fractional_bpp = 0; ++ int hdmi_max_chunk_bytes = hdmi->dsc_cap.total_chunk_kbytes * 1024; ++ ++ return hdmi_dsc_get_bpp(hdmi, fractional_bpp, slice_width, ++ num_slices, hdmi_all_bpp, ++ hdmi_max_chunk_bytes); ++} ++ ++static int dw_hdmi_qp_set_link_cfg(struct rockchip_hdmi *hdmi, ++ u16 pic_width, u16 pic_height, ++ u16 slice_width, u16 slice_height, ++ u16 bits_per_pixel, u8 bits_per_component) ++{ ++ int i; ++ ++ for (i = 0; i < PPS_TABLE_LEN; i++) ++ if (pic_width == pps_datas[i].pic_width && ++ pic_height == pps_datas[i].pic_height && ++ slice_width == pps_datas[i].slice_width && ++ slice_height == pps_datas[i].slice_height && ++ bits_per_component == pps_datas[i].bpc && ++ bits_per_pixel == pps_datas[i].bpp && ++ hdmi_bus_fmt_is_rgb(hdmi->output_bus_format) == pps_datas[i].convert_rgb) ++ break; ++ ++ if (i == PPS_TABLE_LEN) { ++ dev_err(hdmi->dev, "can't find pps cfg!\n"); ++ return -EINVAL; ++ } ++ ++ memcpy(hdmi->link_cfg.pps_payload, pps_datas[i].raw_pps, 128); ++ hdmi->link_cfg.hcactive = DIV_ROUND_UP(slice_width * (bits_per_pixel / 16), 8) * ++ (pic_width / slice_width); ++ ++ return 0; ++} ++ ++static void dw_hdmi_qp_dsc_configure(struct rockchip_hdmi *hdmi, ++ struct rockchip_crtc_state *s, ++ struct drm_crtc_state *crtc_state) ++{ ++ int ret; ++ int slice_height; ++ int slice_width; ++ int bits_per_pixel; ++ int slice_count; ++ bool hdmi_is_dsc_1_2; ++ unsigned int depth = hdmi_bus_fmt_color_depth(hdmi->output_bus_format); ++ ++ if (!crtc_state) ++ return; ++ ++ hdmi_is_dsc_1_2 = hdmi->dsc_cap.v_1p2; ++ ++ if (!hdmi_is_dsc_1_2) ++ return; ++ ++ slice_height = hdmi_dsc_get_slice_height(crtc_state->mode.vdisplay); ++ if (!slice_height) ++ return; ++ ++ slice_count = hdmi_dsc_slices(hdmi, crtc_state); ++ if (!slice_count) ++ return; ++ ++ slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, slice_count); ++ ++ bits_per_pixel = dw_hdmi_dsc_bpp(hdmi, slice_count, slice_width); ++ if (!bits_per_pixel) ++ return; ++ ++ ret = dw_hdmi_qp_set_link_cfg(hdmi, crtc_state->mode.hdisplay, ++ crtc_state->mode.vdisplay, slice_width, ++ slice_height, bits_per_pixel, depth); ++ ++ if (ret) { ++ dev_err(hdmi->dev, "set vdsc cfg failed\n"); ++ return; ++ } ++ dev_info(hdmi->dev, "dsc_enable\n"); ++ s->dsc_enable = 1; ++ s->dsc_sink_cap.version_major = 1; ++ s->dsc_sink_cap.version_minor = 2; ++ s->dsc_sink_cap.slice_width = slice_width; ++ s->dsc_sink_cap.slice_height = slice_height; ++ s->dsc_sink_cap.target_bits_per_pixel_x16 = bits_per_pixel; ++ s->dsc_sink_cap.block_pred = 1; ++ s->dsc_sink_cap.native_420 = 0; ++ ++ memcpy(&s->pps, hdmi->link_cfg.pps_payload, 128); ++} ++///////////////////////////////////////////////////////////////////////////////////////// ++ ++// static int rockchip_hdmi_update_phy_table(struct rockchip_hdmi *hdmi, ++// u32 *config, ++// int phy_table_size) ++// { ++// int i; ++// ++// if (phy_table_size > ARRAY_SIZE(rockchip_phy_config)) { ++// dev_err(hdmi->dev, "phy table array number is out of range\n"); ++// return -E2BIG; ++// } ++// ++// for (i = 0; i < phy_table_size; i++) { ++// if (config[i * 4] != 0) ++// rockchip_phy_config[i].mpixelclock = (u64)config[i * 4]; ++// else ++// rockchip_phy_config[i].mpixelclock = ~0UL; ++// rockchip_phy_config[i].sym_ctr = (u16)config[i * 4 + 1]; ++// rockchip_phy_config[i].term = (u16)config[i * 4 + 2]; ++// rockchip_phy_config[i].vlev_ctr = (u16)config[i * 4 + 3]; ++// } ++// ++// return 0; ++// } ++ ++static void repo_hpd_event(struct work_struct *p_work) ++{ ++ struct rockchip_hdmi *hdmi = container_of(p_work, struct rockchip_hdmi, work.work); ++ bool change; ++ ++ change = drm_helper_hpd_irq_event(hdmi->drm_dev); ++ if (change) { ++ dev_dbg(hdmi->dev, "hpd stat changed:%d\n", hdmi->hpd_stat); ++ // dw_hdmi_qp_cec_set_hpd(hdmi->hdmi_qp, hdmi->hpd_stat, change); ++ } ++} ++ ++static irqreturn_t rockchip_hdmi_hardirq(int irq, void *dev_id) ++{ ++ struct rockchip_hdmi *hdmi = dev_id; ++ u32 intr_stat, val; ++ ++ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat); ++ ++ if (intr_stat) { ++ dev_dbg(hdmi->dev, "hpd irq %#x\n", intr_stat); ++ ++ if (!hdmi->id) ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, ++ RK3588_HDMI0_HPD_INT_MSK); ++ else ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, ++ RK3588_HDMI1_HPD_INT_MSK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++ return IRQ_WAKE_THREAD; ++ } ++ ++ return IRQ_NONE; ++} ++ ++static irqreturn_t rockchip_hdmi_irq(int irq, void *dev_id) ++{ ++ struct rockchip_hdmi *hdmi = dev_id; ++ u32 intr_stat, val; ++ int msecs; ++ bool stat; ++ ++ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat); ++ ++ if (!intr_stat) ++ return IRQ_NONE; ++ ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, ++ RK3588_HDMI0_HPD_INT_CLR); ++ if (intr_stat & RK3588_HDMI0_LEVEL_INT) ++ stat = true; ++ else ++ stat = false; ++ } else { ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, ++ RK3588_HDMI1_HPD_INT_CLR); ++ if (intr_stat & RK3588_HDMI1_LEVEL_INT) ++ stat = true; ++ else ++ stat = false; ++ } ++ ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++ ++ if (stat) { ++ hdmi->hpd_stat = true; ++ msecs = 150; ++ } else { ++ hdmi->hpd_stat = false; ++ msecs = 20; ++ } ++ mod_delayed_work(hdmi->workqueue, &hdmi->work, msecs_to_jiffies(msecs)); ++ ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, ++ RK3588_HDMI0_HPD_INT_CLR) | ++ HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK); ++ } else { ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, ++ RK3588_HDMI1_HPD_INT_CLR) | ++ HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK); ++ } ++ ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++ ++ return IRQ_HANDLED; ++} ++ ++static void init_hpd_work(struct rockchip_hdmi *hdmi) ++{ ++ hdmi->workqueue = create_workqueue("hpd_queue"); ++ INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event); ++} ++ + static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + { + struct device_node *np = hdmi->dev->of_node; ++ int ret; + + hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(hdmi->regmap)) { +@@ -217,6 +1174,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + return PTR_ERR(hdmi->regmap); + } + ++ if (hdmi->is_hdmi_qp) { ++ hdmi->vo1_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,vo1_grf"); ++ if (IS_ERR(hdmi->vo1_regmap)) { ++ DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,vo1_grf\n"); ++ return PTR_ERR(hdmi->vo1_regmap); ++ } ++ } ++ + hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "ref"); + if (!hdmi->ref_clk) + hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "vpll"); +@@ -246,6 +1211,79 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + if (IS_ERR(hdmi->avdd_1v8)) + return PTR_ERR(hdmi->avdd_1v8); + ++ hdmi->hclk_vio = devm_clk_get(hdmi->dev, "hclk_vio"); ++ if (PTR_ERR(hdmi->hclk_vio) == -ENOENT) { ++ hdmi->hclk_vio = NULL; ++ } else if (PTR_ERR(hdmi->hclk_vio) == -EPROBE_DEFER) { ++ return -EPROBE_DEFER; ++ } else if (IS_ERR(hdmi->hclk_vio)) { ++ dev_err(hdmi->dev, "failed to get hclk_vio clock\n"); ++ return PTR_ERR(hdmi->hclk_vio); ++ } ++ ++ hdmi->hclk_vop = devm_clk_get(hdmi->dev, "hclk"); ++ if (PTR_ERR(hdmi->hclk_vop) == -ENOENT) { ++ hdmi->hclk_vop = NULL; ++ } else if (PTR_ERR(hdmi->hclk_vop) == -EPROBE_DEFER) { ++ return -EPROBE_DEFER; ++ } else if (IS_ERR(hdmi->hclk_vop)) { ++ dev_err(hdmi->dev, "failed to get hclk_vop clock\n"); ++ return PTR_ERR(hdmi->hclk_vop); ++ } ++ ++ hdmi->aud_clk = devm_clk_get_optional(hdmi->dev, "aud"); ++ if (IS_ERR(hdmi->aud_clk)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->aud_clk), ++ "failed to get aud_clk clock\n"); ++ return PTR_ERR(hdmi->aud_clk); ++ } ++ ++ hdmi->hpd_clk = devm_clk_get_optional(hdmi->dev, "hpd"); ++ if (IS_ERR(hdmi->hpd_clk)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hpd_clk), ++ "failed to get hpd_clk clock\n"); ++ return PTR_ERR(hdmi->hpd_clk); ++ } ++ ++ hdmi->hclk_vo1 = devm_clk_get_optional(hdmi->dev, "hclk_vo1"); ++ if (IS_ERR(hdmi->hclk_vo1)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hclk_vo1), ++ "failed to get hclk_vo1 clock\n"); ++ return PTR_ERR(hdmi->hclk_vo1); ++ } ++ ++ hdmi->earc_clk = devm_clk_get_optional(hdmi->dev, "earc"); ++ if (IS_ERR(hdmi->earc_clk)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->earc_clk), ++ "failed to get earc_clk clock\n"); ++ return PTR_ERR(hdmi->earc_clk); ++ } ++ ++ hdmi->hdmitx_ref = devm_clk_get_optional(hdmi->dev, "hdmitx_ref"); ++ if (IS_ERR(hdmi->hdmitx_ref)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hdmitx_ref), ++ "failed to get hdmitx_ref clock\n"); ++ return PTR_ERR(hdmi->hdmitx_ref); ++ } ++ ++ hdmi->pclk = devm_clk_get_optional(hdmi->dev, "pclk"); ++ if (IS_ERR(hdmi->pclk)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->pclk), ++ "failed to get pclk clock\n"); ++ return PTR_ERR(hdmi->pclk); ++ } ++ ++ hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable", ++ GPIOD_OUT_HIGH); ++ if (IS_ERR(hdmi->enable_gpio)) { ++ ret = PTR_ERR(hdmi->enable_gpio); ++ dev_err(hdmi->dev, "failed to request enable GPIO: %d\n", ret); ++ return ret; ++ } ++ ++ hdmi->skip_check_420_mode = ++ of_property_read_bool(np, "skip-check-420-mode"); ++ + return 0; + } + +@@ -284,9 +1322,114 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data, + + return MODE_BAD; + } ++/* [CC:] enable downstream mode_valid() */ ++// static enum drm_mode_status ++// dw_hdmi_rockchip_mode_valid(struct drm_connector *connector, void *data, ++// const struct drm_display_info *info, ++// const struct drm_display_mode *mode) ++// { ++// struct drm_encoder *encoder = connector->encoder; ++// enum drm_mode_status status = MODE_OK; ++// struct drm_device *dev = connector->dev; ++// struct rockchip_drm_private *priv = dev->dev_private; ++// struct drm_crtc *crtc; ++// struct rockchip_hdmi *hdmi; ++// ++// /* ++// * Pixel clocks we support are always < 2GHz and so fit in an ++// * int. We should make sure source rate does too so we don't get ++// * overflow when we multiply by 1000. ++// */ ++// if (mode->clock > INT_MAX / 1000) ++// return MODE_BAD; ++// ++// if (!encoder) { ++// const struct drm_connector_helper_funcs *funcs; ++// ++// funcs = connector->helper_private; ++// if (funcs->atomic_best_encoder) ++// encoder = funcs->atomic_best_encoder(connector, ++// connector->state); ++// else ++// encoder = funcs->best_encoder(connector); ++// } ++// ++// if (!encoder || !encoder->possible_crtcs) ++// return MODE_BAD; ++// ++// hdmi = to_rockchip_hdmi(encoder); ++// ++// /* ++// * If sink max TMDS clock < 340MHz, we should check the mode pixel ++// * clock > 340MHz is YCbCr420 or not and whether the platform supports ++// * YCbCr420. ++// */ ++// if (!hdmi->skip_check_420_mode) { ++// if (mode->clock > 340000 && ++// connector->display_info.max_tmds_clock < 340000 && ++// (!drm_mode_is_420(&connector->display_info, mode) || ++// !connector->ycbcr_420_allowed)) ++// return MODE_BAD; ++// ++// if (hdmi->max_tmdsclk <= 340000 && mode->clock > 340000 && ++// !drm_mode_is_420(&connector->display_info, mode)) ++// return MODE_BAD; ++// }; ++// ++// if (hdmi->phy) { ++// if (hdmi->is_hdmi_qp) ++// phy_set_bus_width(hdmi->phy, mode->clock * 10); ++// else ++// phy_set_bus_width(hdmi->phy, 8); ++// } ++// ++// /* ++// * ensure all drm display mode can work, if someone want support more ++// * resolutions, please limit the possible_crtc, only connect to ++// * needed crtc. ++// */ ++// drm_for_each_crtc(crtc, connector->dev) { ++// int pipe = drm_crtc_index(crtc); ++// const struct rockchip_crtc_funcs *funcs = ++// priv->crtc_funcs[pipe]; ++// ++// if (!(encoder->possible_crtcs & drm_crtc_mask(crtc))) ++// continue; ++// if (!funcs || !funcs->mode_valid) ++// continue; ++// ++// status = funcs->mode_valid(crtc, mode, ++// DRM_MODE_CONNECTOR_HDMIA); ++// if (status != MODE_OK) ++// return status; ++// } ++// ++// return status; ++// } ++// + + static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder) + { ++ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); ++ struct drm_crtc *crtc = encoder->crtc; ++ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); ++ ++ if (crtc->state->active_changed) { ++ if (hdmi->plat_data->split_mode) { ++ s->output_if &= ~(VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1); ++ } else { ++ if (!hdmi->id) ++ s->output_if &= ~VOP_OUTPUT_IF_HDMI1; ++ else ++ s->output_if &= ~VOP_OUTPUT_IF_HDMI0; ++ } ++ } ++ /* ++ * when plug out hdmi it will be switch cvbs and then phy bus width ++ * must be set as 8 ++ */ ++ if (hdmi->phy) ++ phy_set_bus_width(hdmi->phy, 8); + } + + static bool +@@ -302,6 +1445,27 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *adj_mode) + { + struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); ++ struct drm_crtc *crtc; ++ struct rockchip_crtc_state *s; ++ ++ if (!encoder->crtc) ++ return; ++ crtc = encoder->crtc; ++ ++ if (!crtc->state) ++ return; ++ s = to_rockchip_crtc_state(crtc->state); ++ ++ if (!s) ++ return; ++ ++ if (hdmi->is_hdmi_qp) { ++ s->dsc_enable = 0; ++ if (hdmi->link_cfg.dsc_mode) ++ dw_hdmi_qp_dsc_configure(hdmi, s, crtc->state); ++ ++ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); ++ } + + clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000); + } +@@ -309,14 +1473,25 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, + static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) + { + struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); ++ struct drm_crtc *crtc = encoder->crtc; + u32 val; ++ int mux; + int ret; + ++ if (WARN_ON(!crtc || !crtc->state)) ++ return; ++ ++ if (hdmi->phy) ++ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); ++ ++ clk_set_rate(hdmi->ref_clk, ++ crtc->state->adjusted_mode.crtc_clock * 1000); ++ + if (hdmi->chip_data->lcdsel_grf_reg < 0) + return; + +- ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); +- if (ret) ++ mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); ++ if (mux) + val = hdmi->chip_data->lcdsel_lit; + else + val = hdmi->chip_data->lcdsel_big; +@@ -331,24 +1506,1018 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) + if (ret != 0) + DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret); + ++ if (hdmi->chip_data->lcdsel_grf_reg == RK3288_GRF_SOC_CON6) { ++ struct rockchip_crtc_state *s = ++ to_rockchip_crtc_state(crtc->state); ++ u32 mode_mask = mux ? RK3288_HDMI_LCDC1_YUV420 : ++ RK3288_HDMI_LCDC0_YUV420; ++ ++ if (s->output_mode == ROCKCHIP_OUT_MODE_YUV420) ++ val = HIWORD_UPDATE(mode_mask, mode_mask); ++ else ++ val = HIWORD_UPDATE(0, mode_mask); ++ ++ regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON16, val); ++ } ++ + clk_disable_unprepare(hdmi->grf_clk); + DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n", + ret ? "LIT" : "BIG"); + } + +-static int +-dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder, +- struct drm_crtc_state *crtc_state, +- struct drm_connector_state *conn_state) ++static void rk3588_set_link_mode(struct rockchip_hdmi *hdmi) + { +- struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); ++ int val; ++ bool is_hdmi0; + +- s->output_mode = ROCKCHIP_OUT_MODE_AAAA; +- s->output_type = DRM_MODE_CONNECTOR_HDMIA; ++ if (!hdmi->id) ++ is_hdmi0 = true; ++ else ++ is_hdmi0 = false; ++ ++ if (!hdmi->link_cfg.frl_mode) { ++ val = HIWORD_UPDATE(0, RK3588_HDMI21_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val); ++ ++ val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ ++ return; ++ } ++ ++ val = HIWORD_UPDATE(RK3588_HDMI21_MASK, RK3588_HDMI21_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val); ++ ++ if (hdmi->link_cfg.dsc_mode) { ++ val = HIWORD_UPDATE(RK3588_COMPRESS_MODE_MASK | RK3588_COMPRESSED_DATA, ++ RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ } else { ++ val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ } ++} ++ ++static void rk3588_set_color_format(struct rockchip_hdmi *hdmi, u64 bus_format, ++ u32 depth) ++{ ++ u32 val = 0; ++ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ val = HIWORD_UPDATE(0, RK3588_COLOR_FORMAT_MASK); ++ break; ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ val = HIWORD_UPDATE(RK3588_YUV420, RK3588_COLOR_FORMAT_MASK); ++ break; ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ val = HIWORD_UPDATE(RK3588_YUV444, RK3588_COLOR_FORMAT_MASK); ++ break; ++ default: ++ dev_err(hdmi->dev, "can't set correct color format\n"); ++ return; ++ } ++ ++ if (hdmi->link_cfg.dsc_mode) ++ val = HIWORD_UPDATE(RK3588_COMPRESSED_DATA, RK3588_COLOR_FORMAT_MASK); ++ ++ if (depth == 8) ++ val |= HIWORD_UPDATE(RK3588_8BPC, RK3588_COLOR_DEPTH_MASK); ++ else ++ val |= HIWORD_UPDATE(RK3588_10BPC, RK3588_COLOR_DEPTH_MASK); ++ ++ if (!hdmi->id) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++} ++ ++static void rk3588_set_grf_cfg(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ int color_depth; ++ ++ rk3588_set_link_mode(hdmi); ++ color_depth = hdmi_bus_fmt_color_depth(hdmi->bus_format); ++ rk3588_set_color_format(hdmi, hdmi->bus_format, color_depth); ++} ++ ++static void ++dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state, ++ struct drm_crtc_state *crtc_state, ++ struct rockchip_hdmi *hdmi, ++ unsigned int *color_format, ++ unsigned int *output_mode, ++ unsigned long *bus_format, ++ unsigned int *bus_width, ++ unsigned long *enc_out_encoding, ++ unsigned int *eotf) ++{ ++ struct drm_display_info *info = &conn_state->connector->display_info; ++ struct drm_display_mode mode; ++ struct hdr_output_metadata *hdr_metadata; ++ u32 vic; ++ unsigned long tmdsclock, pixclock; ++ unsigned int color_depth; ++ bool support_dc = false; ++ bool sink_is_hdmi = true; ++ u32 max_tmds_clock = info->max_tmds_clock; ++ int output_eotf; ++ ++ drm_mode_copy(&mode, &crtc_state->mode); ++ pixclock = mode.crtc_clock; ++ if (hdmi->plat_data->split_mode) { ++ drm_mode_convert_to_origin_mode(&mode); ++ pixclock /= 2; ++ } ++ ++ vic = drm_match_cea_mode(&mode); ++ ++ if (!hdmi->is_hdmi_qp) ++ sink_is_hdmi = dw_hdmi_get_output_whether_hdmi(hdmi->hdmi); ++ ++ *color_format = RK_IF_FORMAT_RGB; ++ ++ switch (hdmi->hdmi_output) { ++ case RK_IF_FORMAT_YCBCR_HQ: ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ *color_format = RK_IF_FORMAT_YCBCR444; ++ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *color_format = RK_IF_FORMAT_YCBCR422; ++ else if (conn_state->connector->ycbcr_420_allowed && ++ drm_mode_is_420(info, &mode) && ++ (pixclock >= 594000 && !hdmi->is_hdmi_qp)) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ break; ++ case RK_IF_FORMAT_YCBCR_LQ: ++ if (conn_state->connector->ycbcr_420_allowed && ++ drm_mode_is_420(info, &mode) && pixclock >= 594000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *color_format = RK_IF_FORMAT_YCBCR422; ++ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ *color_format = RK_IF_FORMAT_YCBCR444; ++ break; ++ case RK_IF_FORMAT_YCBCR420: ++ if (conn_state->connector->ycbcr_420_allowed && ++ drm_mode_is_420(info, &mode) && pixclock >= 594000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ break; ++ case RK_IF_FORMAT_YCBCR422: ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *color_format = RK_IF_FORMAT_YCBCR422; ++ break; ++ case RK_IF_FORMAT_YCBCR444: ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ *color_format = RK_IF_FORMAT_YCBCR444; ++ break; ++ case RK_IF_FORMAT_RGB: ++ default: ++ break; ++ } ++ ++ if (*color_format == RK_IF_FORMAT_RGB && ++ info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30) ++ support_dc = true; ++ if (*color_format == RK_IF_FORMAT_YCBCR444 && ++ info->edid_hdmi_rgb444_dc_modes & ++ (DRM_EDID_HDMI_DC_Y444 | DRM_EDID_HDMI_DC_30)) ++ support_dc = true; ++ if (*color_format == RK_IF_FORMAT_YCBCR422) ++ support_dc = true; ++ if (*color_format == RK_IF_FORMAT_YCBCR420 && ++ info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) ++ support_dc = true; ++ ++ if (hdmi->colordepth > 8 && support_dc) ++ color_depth = 10; ++ else ++ color_depth = 8; ++ ++ if (!sink_is_hdmi) { ++ *color_format = RK_IF_FORMAT_RGB; ++ color_depth = 8; ++ } ++ ++ *eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR; ++ if (conn_state->hdr_output_metadata) { ++ hdr_metadata = (struct hdr_output_metadata *) ++ conn_state->hdr_output_metadata->data; ++ output_eotf = hdr_metadata->hdmi_metadata_type1.eotf; ++ if (output_eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR && ++ output_eotf <= HDMI_EOTF_BT_2100_HLG) ++ *eotf = output_eotf; ++ } ++ ++ hdmi->colorimetry = conn_state->colorspace; ++ ++ if ((*eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR && ++ conn_state->connector->hdr_sink_metadata.hdmi_type1.eotf & ++ BIT(*eotf)) || ((hdmi->colorimetry >= DRM_MODE_COLORIMETRY_BT2020_CYCC) && ++ (hdmi->colorimetry <= DRM_MODE_COLORIMETRY_BT2020_YCC))) ++ *enc_out_encoding = V4L2_YCBCR_ENC_BT2020; ++ else if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) || ++ (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18)) ++ *enc_out_encoding = V4L2_YCBCR_ENC_601; ++ else ++ *enc_out_encoding = V4L2_YCBCR_ENC_709; ++ ++ if (*enc_out_encoding == V4L2_YCBCR_ENC_BT2020) { ++ /* BT2020 require color depth at lest 10bit */ ++ color_depth = 10; ++ /* We prefer use YCbCr422 to send 10bit */ ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *color_format = RK_IF_FORMAT_YCBCR422; ++ if (hdmi->is_hdmi_qp) { ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR420) { ++ if (mode.clock >= 340000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ else ++ *color_format = RK_IF_FORMAT_RGB; ++ } else { ++ *color_format = RK_IF_FORMAT_RGB; ++ } ++ } ++ } ++ ++ if (mode.flags & DRM_MODE_FLAG_DBLCLK) ++ pixclock *= 2; ++ if ((mode.flags & DRM_MODE_FLAG_3D_MASK) == ++ DRM_MODE_FLAG_3D_FRAME_PACKING) ++ pixclock *= 2; ++ ++ if (*color_format == RK_IF_FORMAT_YCBCR422 || color_depth == 8) ++ tmdsclock = pixclock; ++ else ++ tmdsclock = pixclock * (color_depth) / 8; ++ ++ if (*color_format == RK_IF_FORMAT_YCBCR420) ++ tmdsclock /= 2; ++ ++ /* XXX: max_tmds_clock of some sink is 0, we think it is 340MHz. */ ++ if (!max_tmds_clock) ++ max_tmds_clock = 340000; ++ ++ max_tmds_clock = min(max_tmds_clock, hdmi->max_tmdsclk); ++ ++ if ((tmdsclock > max_tmds_clock) && !hdmi->is_hdmi_qp) { ++ if (max_tmds_clock >= 594000) { ++ color_depth = 8; ++ } else if (max_tmds_clock > 340000) { ++ if (drm_mode_is_420(info, &mode) || tmdsclock >= 594000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ } else { ++ color_depth = 8; ++ if (drm_mode_is_420(info, &mode) || tmdsclock >= 594000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ } ++ } ++ ++ if (hdmi->is_hdmi_qp) { ++ if (mode.clock >= 340000) { ++ if (drm_mode_is_420(info, &mode)) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ else ++ *color_format = RK_IF_FORMAT_RGB; ++ } else if (tmdsclock > max_tmds_clock) { ++ color_depth = 8; ++ if (drm_mode_is_420(info, &mode)) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ } ++ } ++ ++ if (*color_format == RK_IF_FORMAT_YCBCR420) { ++ *output_mode = ROCKCHIP_OUT_MODE_YUV420; ++ if (color_depth > 8) ++ *bus_format = MEDIA_BUS_FMT_UYYVYY10_0_5X30; ++ else ++ *bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24; ++ *bus_width = color_depth / 2; ++ } else { ++ *output_mode = ROCKCHIP_OUT_MODE_AAAA; ++ if (color_depth > 8) { ++ if (*color_format != RK_IF_FORMAT_RGB && ++ !hdmi->unsupported_yuv_input) ++ *bus_format = MEDIA_BUS_FMT_YUV10_1X30; ++ else ++ *bus_format = MEDIA_BUS_FMT_RGB101010_1X30; ++ } else { ++ if (*color_format != RK_IF_FORMAT_RGB && ++ !hdmi->unsupported_yuv_input) ++ *bus_format = MEDIA_BUS_FMT_YUV8_1X24; ++ else ++ *bus_format = MEDIA_BUS_FMT_RGB888_1X24; ++ } ++ if (*color_format == RK_IF_FORMAT_YCBCR422) ++ *bus_width = 8; ++ else ++ *bus_width = color_depth; ++ } ++ ++ hdmi->bus_format = *bus_format; ++ ++ if (*color_format == RK_IF_FORMAT_YCBCR422) { ++ if (color_depth == 12) ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24; ++ else if (color_depth == 10) ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20; ++ else ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16; ++ } else { ++ hdmi->output_bus_format = *bus_format; ++ } ++} ++ ++static bool ++dw_hdmi_rockchip_check_color(struct drm_connector_state *conn_state, ++ struct rockchip_hdmi *hdmi) ++{ ++ struct drm_crtc_state *crtc_state = conn_state->crtc->state; ++ unsigned int colorformat; ++ unsigned long bus_format; ++ unsigned long output_bus_format = hdmi->output_bus_format; ++ unsigned long enc_out_encoding = hdmi->enc_out_encoding; ++ unsigned int eotf, bus_width; ++ unsigned int output_mode; ++ ++ dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi, ++ &colorformat, ++ &output_mode, &bus_format, &bus_width, ++ &hdmi->enc_out_encoding, &eotf); ++ ++ if (output_bus_format != hdmi->output_bus_format || ++ enc_out_encoding != hdmi->enc_out_encoding) ++ return true; ++ else ++ return false; ++} ++ ++static int ++dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder, ++ struct drm_crtc_state *crtc_state, ++ struct drm_connector_state *conn_state) ++{ ++ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); ++ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); ++ unsigned int colorformat, bus_width, tmdsclk; ++ struct drm_display_mode mode; ++ unsigned int output_mode; ++ unsigned long bus_format; ++ int color_depth; ++ bool secondary = false; ++ ++ /* ++ * There are two hdmi but only one encoder in split mode, ++ * so we need to check twice. ++ */ ++secondary: ++ drm_mode_copy(&mode, &crtc_state->mode); ++ ++ hdmi->vp_id = s->vp_id; ++ if (hdmi->plat_data->split_mode) ++ drm_mode_convert_to_origin_mode(&mode); ++ ++ dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi, ++ &colorformat, ++ &output_mode, &bus_format, &bus_width, ++ &hdmi->enc_out_encoding, &s->eotf); ++ ++ s->bus_format = bus_format; ++ if (hdmi->is_hdmi_qp) { ++ color_depth = hdmi_bus_fmt_color_depth(bus_format); ++ tmdsclk = hdmi_get_tmdsclock(hdmi, crtc_state->mode.clock); ++ if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format)) ++ tmdsclk /= 2; ++ hdmi_select_link_config(hdmi, crtc_state, tmdsclk); ++ ++ if (hdmi->link_cfg.frl_mode) { ++ gpiod_set_value(hdmi->enable_gpio, 0); ++ /* in the current version, support max 40G frl */ ++ if (hdmi->link_cfg.rate_per_lane >= 10) { ++ hdmi->link_cfg.frl_lanes = 4; ++ hdmi->link_cfg.rate_per_lane = 10; ++ } ++ bus_width = hdmi->link_cfg.frl_lanes * ++ hdmi->link_cfg.rate_per_lane * 1000000; ++ /* 10 bit color depth and frl mode */ ++ if (color_depth == 10) ++ bus_width |= ++ COLOR_DEPTH_10BIT | HDMI_FRL_MODE; ++ else ++ bus_width |= HDMI_FRL_MODE; ++ } else { ++ gpiod_set_value(hdmi->enable_gpio, 1); ++ bus_width = hdmi_get_tmdsclock(hdmi, mode.clock * 10); ++ if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format)) ++ bus_width /= 2; ++ ++ if (color_depth == 10) ++ bus_width |= COLOR_DEPTH_10BIT; ++ } ++ } ++ ++ hdmi->phy_bus_width = bus_width; ++ ++ if (hdmi->phy) ++ phy_set_bus_width(hdmi->phy, bus_width); ++ ++ s->output_type = DRM_MODE_CONNECTOR_HDMIA; ++ s->tv_state = &conn_state->tv; ++ ++ if (hdmi->plat_data->split_mode) { ++ s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE; ++ if (hdmi->plat_data->right && hdmi->id) ++ s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP; ++ s->output_if |= VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1; ++ } else { ++ if (!hdmi->id) ++ s->output_if |= VOP_OUTPUT_IF_HDMI0; ++ else ++ s->output_if |= VOP_OUTPUT_IF_HDMI1; ++ } ++ ++ s->output_mode = output_mode; ++ hdmi->bus_format = s->bus_format; ++ ++ if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_BT2020) ++ s->color_space = V4L2_COLORSPACE_BT2020; ++ else if (colorformat == RK_IF_FORMAT_RGB) ++ s->color_space = V4L2_COLORSPACE_DEFAULT; ++ else if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_709) ++ s->color_space = V4L2_COLORSPACE_REC709; ++ else ++ s->color_space = V4L2_COLORSPACE_SMPTE170M; ++ ++ if (hdmi->plat_data->split_mode && !secondary) { ++ hdmi = rockchip_hdmi_find_by_id(hdmi->dev->driver, !hdmi->id); ++ secondary = true; ++ goto secondary; ++ } ++ ++ return 0; ++} ++ ++static unsigned long ++dw_hdmi_rockchip_get_input_bus_format(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->bus_format; ++} ++ ++static unsigned long ++dw_hdmi_rockchip_get_output_bus_format(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->output_bus_format; ++} ++ ++static unsigned long ++dw_hdmi_rockchip_get_enc_in_encoding(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->enc_out_encoding; ++} ++ ++static unsigned long ++dw_hdmi_rockchip_get_enc_out_encoding(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->enc_out_encoding; ++} ++ ++static unsigned long ++dw_hdmi_rockchip_get_quant_range(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->hdmi_quant_range; ++} ++ ++static struct drm_property * ++dw_hdmi_rockchip_get_hdr_property(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->hdr_panel_metadata_property; ++} ++ ++static struct drm_property_blob * ++dw_hdmi_rockchip_get_hdr_blob(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->hdr_panel_blob_ptr; ++} ++ ++static bool ++dw_hdmi_rockchip_get_color_changed(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ bool ret = false; ++ ++ if (hdmi->color_changed) ++ ret = true; ++ hdmi->color_changed = 0; ++ ++ return ret; ++} ++ ++static int ++dw_hdmi_rockchip_get_yuv422_format(struct drm_connector *connector, ++ struct edid *edid) ++{ ++ if (!connector || !edid) ++ return -EINVAL; ++ ++ return rockchip_drm_get_yuv422_format(connector, edid); ++} ++ ++static int ++dw_hdmi_rockchip_get_edid_dsc_info(void *data, struct edid *edid) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ if (!edid) ++ return -EINVAL; ++ ++ return rockchip_drm_parse_cea_ext(&hdmi->dsc_cap, ++ &hdmi->max_frl_rate_per_lane, ++ &hdmi->max_lanes, edid); ++} ++ ++static int ++dw_hdmi_rockchip_get_next_hdr_data(void *data, struct edid *edid, ++ struct drm_connector *connector) ++{ ++ int ret; ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ struct next_hdr_sink_data *sink_data = &hdmi->next_hdr_data; ++ size_t size = sizeof(*sink_data); ++ struct drm_property *property = hdmi->next_hdr_sink_data_property; ++ struct drm_property_blob *blob = hdmi->hdr_panel_blob_ptr; ++ ++ if (!edid) ++ return -EINVAL; ++ ++ rockchip_drm_parse_next_hdr(sink_data, edid); ++ ++ ret = drm_property_replace_global_blob(connector->dev, &blob, size, sink_data, ++ &connector->base, property); ++ ++ return ret; ++}; ++ ++static ++struct dw_hdmi_link_config *dw_hdmi_rockchip_get_link_cfg(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return &hdmi->link_cfg; ++} ++ ++static int dw_hdmi_dclk_set(void *data, bool enable) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ char clk_name[16]; ++ struct clk *dclk; ++ int ret; ++ ++ snprintf(clk_name, sizeof(clk_name), "dclk_vp%d", hdmi->vp_id); ++ ++ dclk = devm_clk_get(hdmi->dev, clk_name); ++ if (IS_ERR(dclk)) { ++ DRM_DEV_ERROR(hdmi->dev, "failed to get %s\n", clk_name); ++ return PTR_ERR(dclk); ++ } ++ ++ if (enable) { ++ ret = clk_prepare_enable(dclk); ++ if (ret < 0) ++ DRM_DEV_ERROR(hdmi->dev, "failed to enable dclk for video port%d - %d\n", ++ hdmi->vp_id, ret); ++ } else { ++ clk_disable_unprepare(dclk); ++ } + + return 0; + } + ++static const struct drm_prop_enum_list color_depth_enum_list[] = { ++ { 0, "Automatic" }, /* Prefer highest color depth */ ++ { 8, "24bit" }, ++ { 10, "30bit" }, ++}; ++ ++static const struct drm_prop_enum_list drm_hdmi_output_enum_list[] = { ++ { RK_IF_FORMAT_RGB, "rgb" }, ++ { RK_IF_FORMAT_YCBCR444, "ycbcr444" }, ++ { RK_IF_FORMAT_YCBCR422, "ycbcr422" }, ++ { RK_IF_FORMAT_YCBCR420, "ycbcr420" }, ++ { RK_IF_FORMAT_YCBCR_HQ, "ycbcr_high_subsampling" }, ++ { RK_IF_FORMAT_YCBCR_LQ, "ycbcr_low_subsampling" }, ++ { RK_IF_FORMAT_MAX, "invalid_output" }, ++}; ++ ++static const struct drm_prop_enum_list quant_range_enum_list[] = { ++ { HDMI_QUANTIZATION_RANGE_DEFAULT, "default" }, ++ { HDMI_QUANTIZATION_RANGE_LIMITED, "limit" }, ++ { HDMI_QUANTIZATION_RANGE_FULL, "full" }, ++}; ++ ++static const struct drm_prop_enum_list output_hdmi_dvi_enum_list[] = { ++ { 0, "auto" }, ++ { 1, "force_hdmi" }, ++ { 2, "force_dvi" }, ++}; ++ ++static const struct drm_prop_enum_list output_type_cap_list[] = { ++ { 0, "DVI" }, ++ { 1, "HDMI" }, ++}; ++ ++static void ++dw_hdmi_rockchip_attach_properties(struct drm_connector *connector, ++ unsigned int color, int version, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ struct drm_property *prop; ++ struct rockchip_drm_private *private = connector->dev->dev_private; ++ ++ switch (color) { ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ hdmi->hdmi_output = RK_IF_FORMAT_RGB; ++ hdmi->colordepth = 10; ++ break; ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR444; ++ hdmi->colordepth = 8; ++ break; ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR444; ++ hdmi->colordepth = 10; ++ break; ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR422; ++ hdmi->colordepth = 10; ++ break; ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR422; ++ hdmi->colordepth = 8; ++ break; ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR420; ++ hdmi->colordepth = 8; ++ break; ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR420; ++ hdmi->colordepth = 10; ++ break; ++ default: ++ hdmi->hdmi_output = RK_IF_FORMAT_RGB; ++ hdmi->colordepth = 8; ++ } ++ ++ hdmi->bus_format = color; ++ ++ if (hdmi->hdmi_output == RK_IF_FORMAT_YCBCR422) { ++ if (hdmi->colordepth == 12) ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24; ++ else if (hdmi->colordepth == 10) ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20; ++ else ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16; ++ } else { ++ hdmi->output_bus_format = hdmi->bus_format; ++ } ++ ++ /* RK3368 does not support deep color mode */ ++ if (!hdmi->color_depth_property && !hdmi->unsupported_deep_color) { ++ prop = drm_property_create_enum(connector->dev, 0, ++ RK_IF_PROP_COLOR_DEPTH, ++ color_depth_enum_list, ++ ARRAY_SIZE(color_depth_enum_list)); ++ if (prop) { ++ hdmi->color_depth_property = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ } ++ ++ prop = drm_property_create_enum(connector->dev, 0, RK_IF_PROP_COLOR_FORMAT, ++ drm_hdmi_output_enum_list, ++ ARRAY_SIZE(drm_hdmi_output_enum_list)); ++ if (prop) { ++ hdmi->hdmi_output_property = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_range(connector->dev, 0, ++ RK_IF_PROP_COLOR_DEPTH_CAPS, ++ 0, 0xff); ++ if (prop) { ++ hdmi->colordepth_capacity = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_range(connector->dev, 0, ++ RK_IF_PROP_COLOR_FORMAT_CAPS, ++ 0, 0xf); ++ if (prop) { ++ hdmi->outputmode_capacity = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create(connector->dev, ++ DRM_MODE_PROP_BLOB | ++ DRM_MODE_PROP_IMMUTABLE, ++ "HDR_PANEL_METADATA", 0); ++ if (prop) { ++ hdmi->hdr_panel_metadata_property = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create(connector->dev, ++ DRM_MODE_PROP_BLOB | ++ DRM_MODE_PROP_IMMUTABLE, ++ "NEXT_HDR_SINK_DATA", 0); ++ if (prop) { ++ hdmi->next_hdr_sink_data_property = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_bool(connector->dev, DRM_MODE_PROP_IMMUTABLE, ++ "USER_SPLIT_MODE"); ++ if (prop) { ++ hdmi->user_split_mode_prop = prop; ++ drm_object_attach_property(&connector->base, prop, ++ hdmi->user_split_mode ? 1 : 0); ++ } ++ ++ if (!hdmi->is_hdmi_qp) { ++ prop = drm_property_create_enum(connector->dev, 0, ++ "output_hdmi_dvi", ++ output_hdmi_dvi_enum_list, ++ ARRAY_SIZE(output_hdmi_dvi_enum_list)); ++ if (prop) { ++ hdmi->output_hdmi_dvi = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_enum(connector->dev, 0, ++ "output_type_capacity", ++ output_type_cap_list, ++ ARRAY_SIZE(output_type_cap_list)); ++ if (prop) { ++ hdmi->output_type_capacity = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_enum(connector->dev, 0, ++ "hdmi_quant_range", ++ quant_range_enum_list, ++ ARRAY_SIZE(quant_range_enum_list)); ++ if (prop) { ++ hdmi->quant_range = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ } ++ ++ prop = connector->dev->mode_config.hdr_output_metadata_property; ++ if (version >= 0x211a || hdmi->is_hdmi_qp) ++ drm_object_attach_property(&connector->base, prop, 0); ++ ++ if (!drm_mode_create_hdmi_colorspace_property(connector, 0)) ++ drm_object_attach_property(&connector->base, ++ connector->colorspace_property, 0); ++ ++ // [CC:] if this is not needed, also drop connector_id_prop ++ if (!private->connector_id_prop) ++ private->connector_id_prop = drm_property_create_range(connector->dev, ++ DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_IMMUTABLE, ++ "CONNECTOR_ID", 0, 0xf); ++ if (private->connector_id_prop) ++ drm_object_attach_property(&connector->base, private->connector_id_prop, hdmi->id); ++} ++ ++static void ++dw_hdmi_rockchip_destroy_properties(struct drm_connector *connector, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ if (hdmi->color_depth_property) { ++ drm_property_destroy(connector->dev, ++ hdmi->color_depth_property); ++ hdmi->color_depth_property = NULL; ++ } ++ ++ if (hdmi->hdmi_output_property) { ++ drm_property_destroy(connector->dev, ++ hdmi->hdmi_output_property); ++ hdmi->hdmi_output_property = NULL; ++ } ++ ++ if (hdmi->colordepth_capacity) { ++ drm_property_destroy(connector->dev, ++ hdmi->colordepth_capacity); ++ hdmi->colordepth_capacity = NULL; ++ } ++ ++ if (hdmi->outputmode_capacity) { ++ drm_property_destroy(connector->dev, ++ hdmi->outputmode_capacity); ++ hdmi->outputmode_capacity = NULL; ++ } ++ ++ if (hdmi->quant_range) { ++ drm_property_destroy(connector->dev, ++ hdmi->quant_range); ++ hdmi->quant_range = NULL; ++ } ++ ++ if (hdmi->hdr_panel_metadata_property) { ++ drm_property_destroy(connector->dev, ++ hdmi->hdr_panel_metadata_property); ++ hdmi->hdr_panel_metadata_property = NULL; ++ } ++ ++ if (hdmi->next_hdr_sink_data_property) { ++ drm_property_destroy(connector->dev, ++ hdmi->next_hdr_sink_data_property); ++ hdmi->next_hdr_sink_data_property = NULL; ++ } ++ ++ if (hdmi->output_hdmi_dvi) { ++ drm_property_destroy(connector->dev, ++ hdmi->output_hdmi_dvi); ++ hdmi->output_hdmi_dvi = NULL; ++ } ++ ++ if (hdmi->output_type_capacity) { ++ drm_property_destroy(connector->dev, ++ hdmi->output_type_capacity); ++ hdmi->output_type_capacity = NULL; ++ } ++ ++ if (hdmi->user_split_mode_prop) { ++ drm_property_destroy(connector->dev, ++ hdmi->user_split_mode_prop); ++ hdmi->user_split_mode_prop = NULL; ++ } ++} ++ ++static int ++dw_hdmi_rockchip_set_property(struct drm_connector *connector, ++ struct drm_connector_state *state, ++ struct drm_property *property, ++ u64 val, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ struct drm_mode_config *config = &connector->dev->mode_config; ++ ++ if (property == hdmi->color_depth_property) { ++ hdmi->colordepth = val; ++ /* If hdmi is disconnected, state->crtc is null */ ++ if (!state->crtc) ++ return 0; ++ if (dw_hdmi_rockchip_check_color(state, hdmi)) ++ hdmi->color_changed++; ++ return 0; ++ } else if (property == hdmi->hdmi_output_property) { ++ hdmi->hdmi_output = val; ++ if (!state->crtc) ++ return 0; ++ if (dw_hdmi_rockchip_check_color(state, hdmi)) ++ hdmi->color_changed++; ++ return 0; ++ } else if (property == hdmi->quant_range) { ++ u64 quant_range = hdmi->hdmi_quant_range; ++ ++ hdmi->hdmi_quant_range = val; ++ if (quant_range != hdmi->hdmi_quant_range) ++ dw_hdmi_set_quant_range(hdmi->hdmi); ++ return 0; ++ } else if (property == config->hdr_output_metadata_property) { ++ return 0; ++ } else if (property == hdmi->output_hdmi_dvi) { ++ if (hdmi->force_output != val) ++ hdmi->color_changed++; ++ hdmi->force_output = val; ++ dw_hdmi_set_output_type(hdmi->hdmi, val); ++ return 0; ++ } else if (property == hdmi->colordepth_capacity) { ++ return 0; ++ } else if (property == hdmi->outputmode_capacity) { ++ return 0; ++ } else if (property == hdmi->output_type_capacity) { ++ return 0; ++ } ++ ++ DRM_ERROR("Unknown property [PROP:%d:%s]\n", ++ property->base.id, property->name); ++ ++ return -EINVAL; ++} ++ ++static int ++dw_hdmi_rockchip_get_property(struct drm_connector *connector, ++ const struct drm_connector_state *state, ++ struct drm_property *property, ++ u64 *val, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ struct drm_display_info *info = &connector->display_info; ++ struct drm_mode_config *config = &connector->dev->mode_config; ++ ++ if (property == hdmi->color_depth_property) { ++ *val = hdmi->colordepth; ++ return 0; ++ } else if (property == hdmi->hdmi_output_property) { ++ *val = hdmi->hdmi_output; ++ return 0; ++ } else if (property == hdmi->colordepth_capacity) { ++ *val = BIT(RK_IF_DEPTH_8); ++ /* RK3368 only support 8bit */ ++ if (hdmi->unsupported_deep_color) ++ return 0; ++ if (info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30) ++ *val |= BIT(RK_IF_DEPTH_10); ++ if (info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36) ++ *val |= BIT(RK_IF_DEPTH_12); ++ if (info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_48) ++ *val |= BIT(RK_IF_DEPTH_16); ++ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) ++ *val |= BIT(RK_IF_DEPTH_420_10); ++ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36) ++ *val |= BIT(RK_IF_DEPTH_420_12); ++ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48) ++ *val |= BIT(RK_IF_DEPTH_420_16); ++ return 0; ++ } else if (property == hdmi->outputmode_capacity) { ++ *val = BIT(RK_IF_FORMAT_RGB); ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ *val |= BIT(RK_IF_FORMAT_YCBCR444); ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *val |= BIT(RK_IF_FORMAT_YCBCR422); ++ if (connector->ycbcr_420_allowed && ++ info->color_formats & DRM_COLOR_FORMAT_YCBCR420) ++ *val |= BIT(RK_IF_FORMAT_YCBCR420); ++ return 0; ++ } else if (property == hdmi->quant_range) { ++ *val = hdmi->hdmi_quant_range; ++ return 0; ++ } else if (property == config->hdr_output_metadata_property) { ++ *val = state->hdr_output_metadata ? ++ state->hdr_output_metadata->base.id : 0; ++ return 0; ++ } else if (property == hdmi->output_hdmi_dvi) { ++ *val = hdmi->force_output; ++ return 0; ++ } else if (property == hdmi->output_type_capacity) { ++ *val = dw_hdmi_get_output_type_cap(hdmi->hdmi); ++ return 0; ++ } else if (property == hdmi->user_split_mode_prop) { ++ *val = hdmi->user_split_mode; ++ return 0; ++ } ++ ++ DRM_ERROR("Unknown property [PROP:%d:%s]\n", ++ property->base.id, property->name); ++ ++ return -EINVAL; ++} ++ ++static const struct dw_hdmi_property_ops dw_hdmi_rockchip_property_ops = { ++ .attach_properties = dw_hdmi_rockchip_attach_properties, ++ .destroy_properties = dw_hdmi_rockchip_destroy_properties, ++ .set_property = dw_hdmi_rockchip_set_property, ++ .get_property = dw_hdmi_rockchip_get_property, ++}; ++ + static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = { + .mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup, + .mode_set = dw_hdmi_rockchip_encoder_mode_set, +@@ -357,20 +2526,24 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun + .atomic_check = dw_hdmi_rockchip_encoder_atomic_check, + }; + +-static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data, +- const struct drm_display_info *display, +- const struct drm_display_mode *mode) ++static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data) + { + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + +- return phy_power_on(hdmi->phy); ++ while (hdmi->phy->power_count > 0) ++ phy_power_off(hdmi->phy); + } + +-static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data) ++static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data, ++ const struct drm_display_info *display, ++ const struct drm_display_mode *mode) + { + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + +- phy_power_off(hdmi->phy); ++ dw_hdmi_rockchip_genphy_disable(dw_hdmi, data); ++ dw_hdmi_set_high_tmds_clock_ratio(dw_hdmi, display); ++ ++ return phy_power_on(hdmi->phy); + } + + static void dw_hdmi_rk3228_setup_hpd(struct dw_hdmi *dw_hdmi, void *data) +@@ -437,6 +2610,90 @@ static void dw_hdmi_rk3328_setup_hpd(struct dw_hdmi *dw_hdmi, void *data) + RK3328_HDMI_HPD_IOE)); + } + ++static void dw_hdmi_qp_rockchip_phy_disable(struct dw_hdmi_qp *dw_hdmi, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ while (hdmi->phy->power_count > 0) ++ phy_power_off(hdmi->phy); ++} ++ ++static int dw_hdmi_qp_rockchip_genphy_init(struct dw_hdmi_qp *dw_hdmi, void *data, ++ struct drm_display_mode *mode) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ dw_hdmi_qp_rockchip_phy_disable(dw_hdmi, data); ++ ++ return phy_power_on(hdmi->phy); ++} ++ ++static enum drm_connector_status ++dw_hdmi_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) ++{ ++ u32 val; ++ int ret; ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val); ++ ++ if (!hdmi->id) { ++ if (val & RK3588_HDMI0_LEVEL_INT) { ++ hdmi->hpd_stat = true; ++ ret = connector_status_connected; ++ } else { ++ hdmi->hpd_stat = false; ++ ret = connector_status_disconnected; ++ } ++ } else { ++ if (val & RK3588_HDMI1_LEVEL_INT) { ++ hdmi->hpd_stat = true; ++ ret = connector_status_connected; ++ } else { ++ hdmi->hpd_stat = false; ++ ret = connector_status_disconnected; ++ } ++ } ++ ++ return ret; ++} ++ ++static void dw_hdmi_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ u32 val; ++ ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, ++ RK3588_HDMI0_HPD_INT_CLR) | ++ HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK); ++ } else { ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, ++ RK3588_HDMI1_HPD_INT_CLR) | ++ HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK); ++ } ++ ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++} ++ ++static void dw_hdmi_rk3588_phy_set_mode(struct dw_hdmi_qp *dw_hdmi, void *data, ++ u32 mode_mask, bool enable) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ if (!hdmi->phy) ++ return; ++ ++ /* set phy earc/frl mode */ ++ if (enable) ++ hdmi->phy_bus_width |= mode_mask; ++ else ++ hdmi->phy_bus_width &= ~mode_mask; ++ ++ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); ++} ++ + static const struct dw_hdmi_phy_ops rk3228_hdmi_phy_ops = { + .init = dw_hdmi_rockchip_genphy_init, + .disable = dw_hdmi_rockchip_genphy_disable, +@@ -526,6 +2783,30 @@ static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = { + .use_drm_infoframe = true, + }; + ++static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = { ++ .init = dw_hdmi_qp_rockchip_genphy_init, ++ .disable = dw_hdmi_qp_rockchip_phy_disable, ++ .read_hpd = dw_hdmi_rk3588_read_hpd, ++ .setup_hpd = dw_hdmi_rk3588_setup_hpd, ++ .set_mode = dw_hdmi_rk3588_phy_set_mode, ++}; ++ ++struct rockchip_hdmi_chip_data rk3588_hdmi_chip_data = { ++ .lcdsel_grf_reg = -1, ++ .ddc_en_reg = RK3588_GRF_VO1_CON3, ++ .split_mode = true, ++}; ++ ++static const struct dw_hdmi_plat_data rk3588_hdmi_drv_data = { ++ .phy_data = &rk3588_hdmi_chip_data, ++ .qp_phy_ops = &rk3588_hdmi_phy_ops, ++ .phy_name = "samsung_hdptx_phy", ++ .phy_force_vendor = true, ++ .ycbcr_420_allowed = true, ++ .is_hdmi_qp = true, ++ .use_drm_infoframe = true, ++}; ++ + static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { + { .compatible = "rockchip,rk3228-dw-hdmi", + .data = &rk3228_hdmi_drv_data +@@ -542,6 +2823,9 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { + { .compatible = "rockchip,rk3568-dw-hdmi", + .data = &rk3568_hdmi_drv_data + }, ++ { .compatible = "rockchip,rk3588-dw-hdmi", ++ .data = &rk3588_hdmi_drv_data ++ }, + {}, + }; + MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids); +@@ -551,44 +2835,107 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + { + struct platform_device *pdev = to_platform_device(dev); + struct dw_hdmi_plat_data *plat_data; +- const struct of_device_id *match; + struct drm_device *drm = data; + struct drm_encoder *encoder; + struct rockchip_hdmi *hdmi; ++ struct rockchip_hdmi *secondary; + int ret; ++ u32 val; + + if (!pdev->dev.of_node) + return -ENODEV; + +- hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); ++ hdmi = platform_get_drvdata(pdev); + if (!hdmi) + return -ENOMEM; + +- match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); +- plat_data = devm_kmemdup(&pdev->dev, match->data, +- sizeof(*plat_data), GFP_KERNEL); +- if (!plat_data) +- return -ENOMEM; ++ plat_data = hdmi->plat_data; ++ hdmi->drm_dev = drm; + +- hdmi->dev = &pdev->dev; +- hdmi->plat_data = plat_data; +- hdmi->chip_data = plat_data->phy_data; + plat_data->phy_data = hdmi; +- plat_data->priv_data = hdmi; +- encoder = &hdmi->encoder.encoder; ++ plat_data->get_input_bus_format = ++ dw_hdmi_rockchip_get_input_bus_format; ++ plat_data->get_output_bus_format = ++ dw_hdmi_rockchip_get_output_bus_format; ++ plat_data->get_enc_in_encoding = ++ dw_hdmi_rockchip_get_enc_in_encoding; ++ plat_data->get_enc_out_encoding = ++ dw_hdmi_rockchip_get_enc_out_encoding; ++ plat_data->get_quant_range = ++ dw_hdmi_rockchip_get_quant_range; ++ plat_data->get_hdr_property = ++ dw_hdmi_rockchip_get_hdr_property; ++ plat_data->get_hdr_blob = ++ dw_hdmi_rockchip_get_hdr_blob; ++ plat_data->get_color_changed = ++ dw_hdmi_rockchip_get_color_changed; ++ plat_data->get_yuv422_format = ++ dw_hdmi_rockchip_get_yuv422_format; ++ plat_data->get_edid_dsc_info = ++ dw_hdmi_rockchip_get_edid_dsc_info; ++ plat_data->get_next_hdr_data = ++ dw_hdmi_rockchip_get_next_hdr_data; ++ plat_data->get_link_cfg = dw_hdmi_rockchip_get_link_cfg; ++ plat_data->set_grf_cfg = rk3588_set_grf_cfg; ++ plat_data->convert_to_split_mode = drm_mode_convert_to_split_mode; ++ plat_data->convert_to_origin_mode = drm_mode_convert_to_origin_mode; ++ plat_data->dclk_set = dw_hdmi_dclk_set; ++ ++ plat_data->property_ops = &dw_hdmi_rockchip_property_ops; ++ ++ secondary = rockchip_hdmi_find_by_id(dev->driver, !hdmi->id); ++ /* If don't enable hdmi0 and hdmi1, we don't enable split mode */ ++ if (hdmi->chip_data->split_mode && secondary) { + +- encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); +- rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder, +- dev->of_node, 0, 0); ++ /* ++ * hdmi can only attach bridge and init encoder/connector in the ++ * last bind hdmi in split mode, or hdmi->hdmi_qp will not be initialized ++ * and plat_data->left/right will be null pointer. we must check if split ++ * mode is on and determine the sequence of hdmi bind. ++ */ ++ if (device_property_read_bool(dev, "split-mode") || ++ device_property_read_bool(secondary->dev, "split-mode")) { ++ plat_data->split_mode = true; ++ secondary->plat_data->split_mode = true; ++ if (!secondary->plat_data->first_screen) ++ plat_data->first_screen = true; ++ } ++ ++ if (device_property_read_bool(dev, "user-split-mode") || ++ device_property_read_bool(secondary->dev, "user-split-mode")) { ++ hdmi->user_split_mode = true; ++ secondary->user_split_mode = true; ++ } ++ } + +- /* +- * If we failed to find the CRTC(s) which this encoder is +- * supposed to be connected to, it's because the CRTC has +- * not been registered yet. Defer probing, and hope that +- * the required CRTC is added later. +- */ +- if (encoder->possible_crtcs == 0) +- return -EPROBE_DEFER; ++ if (!plat_data->first_screen) { ++ encoder = &hdmi->encoder.encoder; ++ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); ++ rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder, ++ dev->of_node, 0, 0); ++ /* ++ * If we failed to find the CRTC(s) which this encoder is ++ * supposed to be connected to, it's because the CRTC has ++ * not been registered yet. Defer probing, and hope that ++ * the required CRTC is added later. ++ */ ++ if (encoder->possible_crtcs == 0) ++ return -EPROBE_DEFER; ++ ++ drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); ++ // [CC:] consider using drmm_simple_encoder_alloc() ++ drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); ++ } ++ ++ if (!plat_data->max_tmdsclk) ++ hdmi->max_tmdsclk = 594000; ++ else ++ hdmi->max_tmdsclk = plat_data->max_tmdsclk; ++ ++ hdmi->is_hdmi_qp = plat_data->is_hdmi_qp; ++ ++ hdmi->unsupported_yuv_input = plat_data->unsupported_yuv_input; ++ hdmi->unsupported_deep_color = plat_data->unsupported_deep_color; + + ret = rockchip_hdmi_parse_dt(hdmi); + if (ret) { +@@ -597,34 +2944,44 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + return ret; + } + +- hdmi->phy = devm_phy_optional_get(dev, "hdmi"); +- if (IS_ERR(hdmi->phy)) { +- ret = PTR_ERR(hdmi->phy); +- if (ret != -EPROBE_DEFER) +- DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n"); ++ ret = clk_prepare_enable(hdmi->aud_clk); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI aud_clk: %d\n", ret); + return ret; + } + +- ret = regulator_enable(hdmi->avdd_0v9); ++ ret = clk_prepare_enable(hdmi->hpd_clk); + if (ret) { +- DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret); +- goto err_avdd_0v9; ++ dev_err(hdmi->dev, "Failed to enable HDMI hpd_clk: %d\n", ret); ++ return ret; + } + +- ret = regulator_enable(hdmi->avdd_1v8); ++ ret = clk_prepare_enable(hdmi->hclk_vo1); + if (ret) { +- DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret); +- goto err_avdd_1v8; ++ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vo1: %d\n", ret); ++ return ret; + } + +- ret = clk_prepare_enable(hdmi->ref_clk); ++ ret = clk_prepare_enable(hdmi->earc_clk); + if (ret) { +- DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n", +- ret); +- goto err_clk; ++ dev_err(hdmi->dev, "Failed to enable HDMI earc_clk: %d\n", ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(hdmi->hdmitx_ref); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI hdmitx_ref: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(hdmi->pclk); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI pclk: %d\n", ret); ++ return ret; + } + +- if (hdmi->chip_data == &rk3568_chip_data) { ++ if (hdmi->chip_data->ddc_en_reg == RK3568_GRF_VO_CON1) { + regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1, + HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK | + RK3568_HDMI_SCLIN_MSK, +@@ -632,12 +2989,132 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + RK3568_HDMI_SCLIN_MSK)); + } + +- drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); +- drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); ++ if (hdmi->is_hdmi_qp) { ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | ++ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | ++ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | ++ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ ++ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, ++ RK3588_SET_HPD_PATH_MASK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); ++ ++ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, ++ RK3588_HDMI0_GRANT_SEL); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); ++ } else { ++ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | ++ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | ++ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | ++ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ ++ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, ++ RK3588_SET_HPD_PATH_MASK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); ++ ++ val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL, ++ RK3588_HDMI1_GRANT_SEL); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); ++ } ++ init_hpd_work(hdmi); ++ } + +- platform_set_drvdata(pdev, hdmi); ++ ret = clk_prepare_enable(hdmi->hclk_vio); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vio: %d\n", ++ ret); ++ return ret; ++ } + +- hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data); ++ ret = clk_prepare_enable(hdmi->hclk_vop); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vop: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(hdmi->ref_clk); ++ if (ret) { ++ DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n", ++ ret); ++ goto err_clk; ++ } ++ ++ ret = regulator_enable(hdmi->avdd_0v9); ++ if (ret) { ++ DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret); ++ goto err_avdd_0v9; ++ } ++ ++ ret = regulator_enable(hdmi->avdd_1v8); ++ if (ret) { ++ DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret); ++ goto err_avdd_1v8; ++ } ++ ++ if (!hdmi->id) ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK); ++ else ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, RK3588_HDMI1_HPD_INT_MSK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++ ++ if (hdmi->is_hdmi_qp) { ++ hdmi->hpd_irq = platform_get_irq(pdev, 4); ++ if (hdmi->hpd_irq < 0) ++ return hdmi->hpd_irq; ++ ++ ret = devm_request_threaded_irq(hdmi->dev, hdmi->hpd_irq, ++ rockchip_hdmi_hardirq, ++ rockchip_hdmi_irq, ++ IRQF_SHARED, "dw-hdmi-qp-hpd", ++ hdmi); ++ if (ret) ++ return ret; ++ } ++ ++ hdmi->phy = devm_phy_optional_get(dev, "hdmi"); ++ if (IS_ERR(hdmi->phy)) { ++ hdmi->phy = devm_phy_optional_get(dev, "hdmi_phy"); ++ if (IS_ERR(hdmi->phy)) { ++ ret = PTR_ERR(hdmi->phy); ++ if (ret != -EPROBE_DEFER) ++ DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n"); ++ return ret; ++ } ++ } ++ ++ if (hdmi->is_hdmi_qp) { ++ // [CC:] do proper error handling, e.g. clk_disable_unprepare ++ hdmi->hdmi_qp = dw_hdmi_qp_bind(pdev, &hdmi->encoder.encoder, plat_data); ++ ++ if (IS_ERR(hdmi->hdmi_qp)) { ++ ret = PTR_ERR(hdmi->hdmi_qp); ++ drm_encoder_cleanup(&hdmi->encoder.encoder); ++ } ++ ++ if (plat_data->connector) { ++ hdmi->sub_dev.connector = plat_data->connector; ++ hdmi->sub_dev.of_node = dev->of_node; ++ rockchip_drm_register_sub_dev(&hdmi->sub_dev); ++ } ++ ++ if (plat_data->split_mode && secondary) { ++ if (device_property_read_bool(dev, "split-mode")) { ++ plat_data->right = secondary->hdmi_qp; ++ secondary->plat_data->left = hdmi->hdmi_qp; ++ } else { ++ plat_data->left = secondary->hdmi_qp; ++ secondary->plat_data->right = hdmi->hdmi_qp; ++ } ++ } ++ ++ return ret; ++ } ++ ++ hdmi->hdmi = dw_hdmi_bind(pdev, &hdmi->encoder.encoder, plat_data); + + /* + * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(), +@@ -648,11 +3125,24 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + goto err_bind; + } + ++ if (plat_data->connector) { ++ hdmi->sub_dev.connector = plat_data->connector; ++ hdmi->sub_dev.of_node = dev->of_node; ++ rockchip_drm_register_sub_dev(&hdmi->sub_dev); ++ } ++ + return 0; + + err_bind: +- drm_encoder_cleanup(encoder); ++ drm_encoder_cleanup(&hdmi->encoder.encoder); ++ clk_disable_unprepare(hdmi->aud_clk); + clk_disable_unprepare(hdmi->ref_clk); ++ clk_disable_unprepare(hdmi->hclk_vop); ++ clk_disable_unprepare(hdmi->hpd_clk); ++ clk_disable_unprepare(hdmi->hclk_vo1); ++ clk_disable_unprepare(hdmi->earc_clk); ++ clk_disable_unprepare(hdmi->hdmitx_ref); ++ clk_disable_unprepare(hdmi->pclk); + err_clk: + regulator_disable(hdmi->avdd_1v8); + err_avdd_1v8: +@@ -666,9 +3156,30 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, + { + struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); + +- dw_hdmi_unbind(hdmi->hdmi); ++ if (hdmi->is_hdmi_qp) { ++ cancel_delayed_work(&hdmi->work); ++ flush_workqueue(hdmi->workqueue); ++ destroy_workqueue(hdmi->workqueue); ++ } ++ ++ if (hdmi->sub_dev.connector) ++ rockchip_drm_unregister_sub_dev(&hdmi->sub_dev); ++ ++ if (hdmi->is_hdmi_qp) ++ dw_hdmi_qp_unbind(hdmi->hdmi_qp); ++ else ++ dw_hdmi_unbind(hdmi->hdmi); ++ + drm_encoder_cleanup(&hdmi->encoder.encoder); ++ ++ clk_disable_unprepare(hdmi->aud_clk); + clk_disable_unprepare(hdmi->ref_clk); ++ clk_disable_unprepare(hdmi->hclk_vop); ++ clk_disable_unprepare(hdmi->hpd_clk); ++ clk_disable_unprepare(hdmi->hclk_vo1); ++ clk_disable_unprepare(hdmi->earc_clk); ++ clk_disable_unprepare(hdmi->hdmitx_ref); ++ clk_disable_unprepare(hdmi->pclk); + + regulator_disable(hdmi->avdd_1v8); + regulator_disable(hdmi->avdd_0v9); +@@ -681,30 +3192,131 @@ static const struct component_ops dw_hdmi_rockchip_ops = { + + static int dw_hdmi_rockchip_probe(struct platform_device *pdev) + { ++ struct rockchip_hdmi *hdmi; ++ const struct of_device_id *match; ++ struct dw_hdmi_plat_data *plat_data; ++ int id; ++ ++ hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); ++ if (!hdmi) ++ return -ENOMEM; ++ ++ id = of_alias_get_id(pdev->dev.of_node, "hdmi"); ++ if (id < 0) ++ id = 0; ++ ++ hdmi->id = id; ++ hdmi->dev = &pdev->dev; ++ ++ match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); ++ plat_data = devm_kmemdup(&pdev->dev, match->data, ++ sizeof(*plat_data), GFP_KERNEL); ++ if (!plat_data) ++ return -ENOMEM; ++ ++ plat_data->id = hdmi->id; ++ hdmi->plat_data = plat_data; ++ hdmi->chip_data = plat_data->phy_data; ++ ++ platform_set_drvdata(pdev, hdmi); ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_get_sync(&pdev->dev); ++ + return component_add(&pdev->dev, &dw_hdmi_rockchip_ops); + } + ++static void dw_hdmi_rockchip_shutdown(struct platform_device *pdev) ++{ ++ struct rockchip_hdmi *hdmi = dev_get_drvdata(&pdev->dev); ++ ++ if (!hdmi) ++ return; ++ ++ if (hdmi->is_hdmi_qp) { ++ cancel_delayed_work(&hdmi->work); ++ flush_workqueue(hdmi->workqueue); ++ dw_hdmi_qp_suspend(hdmi->dev, hdmi->hdmi_qp); ++ } else { ++ dw_hdmi_suspend(hdmi->hdmi); ++ } ++ pm_runtime_put_sync(&pdev->dev); ++} ++ + static void dw_hdmi_rockchip_remove(struct platform_device *pdev) + { + component_del(&pdev->dev, &dw_hdmi_rockchip_ops); ++ pm_runtime_disable(&pdev->dev); ++} ++ ++static int dw_hdmi_rockchip_suspend(struct device *dev) ++{ ++ struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); ++ ++ if (hdmi->is_hdmi_qp) ++ dw_hdmi_qp_suspend(dev, hdmi->hdmi_qp); ++ else ++ dw_hdmi_suspend(hdmi->hdmi); ++ ++ pm_runtime_put_sync(dev); ++ ++ return 0; + } + + static int __maybe_unused dw_hdmi_rockchip_resume(struct device *dev) + { + struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); ++ u32 val; + +- dw_hdmi_resume(hdmi->hdmi); ++ if (hdmi->is_hdmi_qp) { ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | ++ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | ++ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | ++ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ ++ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, ++ RK3588_SET_HPD_PATH_MASK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); ++ ++ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, ++ RK3588_HDMI0_GRANT_SEL); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); ++ } else { ++ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | ++ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | ++ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | ++ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ ++ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, ++ RK3588_SET_HPD_PATH_MASK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); ++ ++ val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL, ++ RK3588_HDMI1_GRANT_SEL); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); ++ } ++ ++ dw_hdmi_qp_resume(dev, hdmi->hdmi_qp); ++ drm_helper_hpd_irq_event(hdmi->drm_dev); ++ } else { ++ dw_hdmi_resume(hdmi->hdmi); ++ } ++ pm_runtime_get_sync(dev); + + return 0; + } + + static const struct dev_pm_ops dw_hdmi_rockchip_pm = { +- SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_rockchip_resume) ++ SET_SYSTEM_SLEEP_PM_OPS(dw_hdmi_rockchip_suspend, ++ dw_hdmi_rockchip_resume) + }; + + struct platform_driver dw_hdmi_rockchip_pltfm_driver = { + .probe = dw_hdmi_rockchip_probe, + .remove_new = dw_hdmi_rockchip_remove, ++ .shutdown = dw_hdmi_rockchip_shutdown, + .driver = { + .name = "dwhdmi-rockchip", + .pm = &dw_hdmi_rockchip_pm, +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +index ab55d7132..44d49c86b 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +@@ -17,8 +17,12 @@ + #include + + #include ++#include ++#include ++#include + #include + #include ++//#include + #include + #include + #include +@@ -44,6 +48,629 @@ + + static const struct drm_driver rockchip_drm_driver; + ++void drm_mode_convert_to_split_mode(struct drm_display_mode *mode) ++{ ++ u16 hactive, hfp, hsync, hbp; ++ ++ hactive = mode->hdisplay; ++ hfp = mode->hsync_start - mode->hdisplay; ++ hsync = mode->hsync_end - mode->hsync_start; ++ hbp = mode->htotal - mode->hsync_end; ++ ++ mode->clock *= 2; ++ mode->hdisplay = hactive * 2; ++ mode->hsync_start = mode->hdisplay + hfp * 2; ++ mode->hsync_end = mode->hsync_start + hsync * 2; ++ mode->htotal = mode->hsync_end + hbp * 2; ++ drm_mode_set_name(mode); ++} ++EXPORT_SYMBOL(drm_mode_convert_to_split_mode); ++ ++void drm_mode_convert_to_origin_mode(struct drm_display_mode *mode) ++{ ++ u16 hactive, hfp, hsync, hbp; ++ ++ hactive = mode->hdisplay; ++ hfp = mode->hsync_start - mode->hdisplay; ++ hsync = mode->hsync_end - mode->hsync_start; ++ hbp = mode->htotal - mode->hsync_end; ++ ++ mode->clock /= 2; ++ mode->hdisplay = hactive / 2; ++ mode->hsync_start = mode->hdisplay + hfp / 2; ++ mode->hsync_end = mode->hsync_start + hsync / 2; ++ mode->htotal = mode->hsync_end + hbp / 2; ++} ++EXPORT_SYMBOL(drm_mode_convert_to_origin_mode); ++ ++static DEFINE_MUTEX(rockchip_drm_sub_dev_lock); ++static LIST_HEAD(rockchip_drm_sub_dev_list); ++ ++void rockchip_drm_register_sub_dev(struct rockchip_drm_sub_dev *sub_dev) ++{ ++ mutex_lock(&rockchip_drm_sub_dev_lock); ++ list_add_tail(&sub_dev->list, &rockchip_drm_sub_dev_list); ++ mutex_unlock(&rockchip_drm_sub_dev_lock); ++} ++EXPORT_SYMBOL(rockchip_drm_register_sub_dev); ++ ++void rockchip_drm_unregister_sub_dev(struct rockchip_drm_sub_dev *sub_dev) ++{ ++ mutex_lock(&rockchip_drm_sub_dev_lock); ++ list_del(&sub_dev->list); ++ mutex_unlock(&rockchip_drm_sub_dev_lock); ++} ++EXPORT_SYMBOL(rockchip_drm_unregister_sub_dev); ++ ++static int ++cea_db_tag(const u8 *db) ++{ ++ return db[0] >> 5; ++} ++ ++static int ++cea_db_payload_len(const u8 *db) ++{ ++ return db[0] & 0x1f; ++} ++ ++#define for_each_cea_db(cea, i, start, end) \ ++ for ((i) = (start); \ ++ (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); \ ++ (i) += cea_db_payload_len(&(cea)[(i)]) + 1) ++ ++#define HDMI_NEXT_HDR_VSDB_OUI 0xd04601 ++ ++static bool cea_db_is_hdmi_next_hdr_block(const u8 *db) ++{ ++ unsigned int oui; ++ ++ if (cea_db_tag(db) != 0x07) ++ return false; ++ ++ if (cea_db_payload_len(db) < 11) ++ return false; ++ ++ oui = db[3] << 16 | db[2] << 8 | db[1]; ++ ++ return oui == HDMI_NEXT_HDR_VSDB_OUI; ++} ++ ++static bool cea_db_is_hdmi_forum_vsdb(const u8 *db) ++{ ++ unsigned int oui; ++ ++ if (cea_db_tag(db) != 0x03) ++ return false; ++ ++ if (cea_db_payload_len(db) < 7) ++ return false; ++ ++ oui = db[3] << 16 | db[2] << 8 | db[1]; ++ ++ return oui == HDMI_FORUM_IEEE_OUI; ++} ++ ++static int ++cea_db_offsets(const u8 *cea, int *start, int *end) ++{ ++ /* DisplayID CTA extension blocks and top-level CEA EDID ++ * block header definitions differ in the following bytes: ++ * 1) Byte 2 of the header specifies length differently, ++ * 2) Byte 3 is only present in the CEA top level block. ++ * ++ * The different definitions for byte 2 follow. ++ * ++ * DisplayID CTA extension block defines byte 2 as: ++ * Number of payload bytes ++ * ++ * CEA EDID block defines byte 2 as: ++ * Byte number (decimal) within this block where the 18-byte ++ * DTDs begin. If no non-DTD data is present in this extension ++ * block, the value should be set to 04h (the byte after next). ++ * If set to 00h, there are no DTDs present in this block and ++ * no non-DTD data. ++ */ ++ if (cea[0] == 0x81) { ++ /* ++ * for_each_displayid_db() has already verified ++ * that these stay within expected bounds. ++ */ ++ *start = 3; ++ *end = *start + cea[2]; ++ } else if (cea[0] == 0x02) { ++ /* Data block offset in CEA extension block */ ++ *start = 4; ++ *end = cea[2]; ++ if (*end == 0) ++ *end = 127; ++ if (*end < 4 || *end > 127) ++ return -ERANGE; ++ } else { ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static u8 *find_edid_extension(const struct edid *edid, ++ int ext_id, int *ext_index) ++{ ++ u8 *edid_ext = NULL; ++ int i; ++ ++ /* No EDID or EDID extensions */ ++ if (edid == NULL || edid->extensions == 0) ++ return NULL; ++ ++ /* Find CEA extension */ ++ for (i = *ext_index; i < edid->extensions; i++) { ++ edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); ++ if (edid_ext[0] == ext_id) ++ break; ++ } ++ ++ if (i >= edid->extensions) ++ return NULL; ++ ++ *ext_index = i + 1; ++ ++ return edid_ext; ++} ++ ++static int validate_displayid(u8 *displayid, int length, int idx) ++{ ++ int i, dispid_length; ++ u8 csum = 0; ++ struct displayid_header *base; ++ ++ base = (struct displayid_header *)&displayid[idx]; ++ ++ DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n", ++ base->rev, base->bytes, base->prod_id, base->ext_count); ++ ++ /* +1 for DispID checksum */ ++ dispid_length = sizeof(*base) + base->bytes + 1; ++ if (dispid_length > length - idx) ++ return -EINVAL; ++ ++ for (i = 0; i < dispid_length; i++) ++ csum += displayid[idx + i]; ++ if (csum) { ++ DRM_NOTE("DisplayID checksum invalid, remainder is %d\n", csum); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static u8 *find_displayid_extension(const struct edid *edid, ++ int *length, int *idx, ++ int *ext_index) ++{ ++ u8 *displayid = find_edid_extension(edid, 0x70, ext_index); ++ struct displayid_header *base; ++ int ret; ++ ++ if (!displayid) ++ return NULL; ++ ++ /* EDID extensions block checksum isn't for us */ ++ *length = EDID_LENGTH - 1; ++ *idx = 1; ++ ++ ret = validate_displayid(displayid, *length, *idx); ++ if (ret) ++ return NULL; ++ ++ base = (struct displayid_header *)&displayid[*idx]; ++ *length = *idx + sizeof(*base) + base->bytes; ++ ++ return displayid; ++} ++ ++static u8 *find_cea_extension(const struct edid *edid) ++{ ++ int length, idx; ++ const struct displayid_block *block; ++ u8 *cea; ++ u8 *displayid; ++ int ext_index; ++ ++ /* Look for a top level CEA extension block */ ++ /* FIXME: make callers iterate through multiple CEA ext blocks? */ ++ ext_index = 0; ++ cea = find_edid_extension(edid, 0x02, &ext_index); ++ if (cea) ++ return cea; ++ ++ /* CEA blocks can also be found embedded in a DisplayID block */ ++ ext_index = 0; ++ for (;;) { ++ struct displayid_iter iter; ++ const struct drm_edid *drm_edid; ++ int edid_size = (edid->extensions + 1) * EDID_LENGTH; ++ ++ drm_edid = drm_edid_alloc(edid, edid_size); ++ if (!drm_edid) ++ return NULL; ++ displayid = find_displayid_extension(edid, &length, &idx, ++ &ext_index); ++ if (!displayid) { ++ drm_edid_free(drm_edid); ++ return NULL; ++ } ++ ++ displayid_iter_edid_begin(drm_edid, &iter); ++ idx += sizeof(struct displayid_header); ++ displayid_iter_for_each(block, &iter) { ++ if (block->tag == 0x81) { ++ displayid_iter_end(&iter); ++ drm_edid_free(drm_edid); ++ return (u8 *)block; ++ } ++ } ++ ++ displayid_iter_end(&iter); ++ drm_edid_free(drm_edid); ++ } ++ ++ return NULL; ++} ++ ++#define EDID_CEA_YCRCB422 (1 << 4) ++ ++int rockchip_drm_get_yuv422_format(struct drm_connector *connector, ++ struct edid *edid) ++{ ++ struct drm_display_info *info; ++ const u8 *edid_ext; ++ ++ if (!connector || !edid) ++ return -EINVAL; ++ ++ info = &connector->display_info; ++ ++ edid_ext = find_cea_extension(edid); ++ if (!edid_ext) ++ return -EINVAL; ++ ++ if (edid_ext[3] & EDID_CEA_YCRCB422) ++ info->color_formats |= DRM_COLOR_FORMAT_YCBCR422; ++ ++ return 0; ++} ++EXPORT_SYMBOL(rockchip_drm_get_yuv422_format); ++ ++static ++void get_max_frl_rate(int max_frl_rate, u8 *max_lanes, u8 *max_rate_per_lane) ++{ ++ switch (max_frl_rate) { ++ case 1: ++ *max_lanes = 3; ++ *max_rate_per_lane = 3; ++ break; ++ case 2: ++ *max_lanes = 3; ++ *max_rate_per_lane = 6; ++ break; ++ case 3: ++ *max_lanes = 4; ++ *max_rate_per_lane = 6; ++ break; ++ case 4: ++ *max_lanes = 4; ++ *max_rate_per_lane = 8; ++ break; ++ case 5: ++ *max_lanes = 4; ++ *max_rate_per_lane = 10; ++ break; ++ case 6: ++ *max_lanes = 4; ++ *max_rate_per_lane = 12; ++ break; ++ case 0: ++ default: ++ *max_lanes = 0; ++ *max_rate_per_lane = 0; ++ } ++} ++ ++#define EDID_DSC_10BPC (1 << 0) ++#define EDID_DSC_12BPC (1 << 1) ++#define EDID_DSC_16BPC (1 << 2) ++#define EDID_DSC_ALL_BPP (1 << 3) ++#define EDID_DSC_NATIVE_420 (1 << 6) ++#define EDID_DSC_1P2 (1 << 7) ++#define EDID_DSC_MAX_FRL_RATE_MASK 0xf0 ++#define EDID_DSC_MAX_SLICES 0xf ++#define EDID_DSC_TOTAL_CHUNK_KBYTES 0x3f ++#define EDID_MAX_FRL_RATE_MASK 0xf0 ++ ++static ++void parse_edid_forum_vsdb(struct rockchip_drm_dsc_cap *dsc_cap, ++ u8 *max_frl_rate_per_lane, u8 *max_lanes, ++ const u8 *hf_vsdb) ++{ ++ u8 max_frl_rate; ++ u8 dsc_max_frl_rate; ++ u8 dsc_max_slices; ++ ++ if (!hf_vsdb[7]) ++ return; ++ ++ DRM_DEBUG_KMS("hdmi_21 sink detected. parsing edid\n"); ++ max_frl_rate = (hf_vsdb[7] & EDID_MAX_FRL_RATE_MASK) >> 4; ++ get_max_frl_rate(max_frl_rate, max_lanes, ++ max_frl_rate_per_lane); ++ ++ if (cea_db_payload_len(hf_vsdb) < 13) ++ return; ++ ++ dsc_cap->v_1p2 = hf_vsdb[11] & EDID_DSC_1P2; ++ ++ if (!dsc_cap->v_1p2) ++ return; ++ ++ dsc_cap->native_420 = hf_vsdb[11] & EDID_DSC_NATIVE_420; ++ dsc_cap->all_bpp = hf_vsdb[11] & EDID_DSC_ALL_BPP; ++ ++ if (hf_vsdb[11] & EDID_DSC_16BPC) ++ dsc_cap->bpc_supported = 16; ++ else if (hf_vsdb[11] & EDID_DSC_12BPC) ++ dsc_cap->bpc_supported = 12; ++ else if (hf_vsdb[11] & EDID_DSC_10BPC) ++ dsc_cap->bpc_supported = 10; ++ else ++ dsc_cap->bpc_supported = 0; ++ ++ dsc_max_frl_rate = (hf_vsdb[12] & EDID_DSC_MAX_FRL_RATE_MASK) >> 4; ++ get_max_frl_rate(dsc_max_frl_rate, &dsc_cap->max_lanes, ++ &dsc_cap->max_frl_rate_per_lane); ++ dsc_cap->total_chunk_kbytes = hf_vsdb[13] & EDID_DSC_TOTAL_CHUNK_KBYTES; ++ ++ dsc_max_slices = hf_vsdb[12] & EDID_DSC_MAX_SLICES; ++ switch (dsc_max_slices) { ++ case 1: ++ dsc_cap->max_slices = 1; ++ dsc_cap->clk_per_slice = 340; ++ break; ++ case 2: ++ dsc_cap->max_slices = 2; ++ dsc_cap->clk_per_slice = 340; ++ break; ++ case 3: ++ dsc_cap->max_slices = 4; ++ dsc_cap->clk_per_slice = 340; ++ break; ++ case 4: ++ dsc_cap->max_slices = 8; ++ dsc_cap->clk_per_slice = 340; ++ break; ++ case 5: ++ dsc_cap->max_slices = 8; ++ dsc_cap->clk_per_slice = 400; ++ break; ++ case 6: ++ dsc_cap->max_slices = 12; ++ dsc_cap->clk_per_slice = 400; ++ break; ++ case 7: ++ dsc_cap->max_slices = 16; ++ dsc_cap->clk_per_slice = 400; ++ break; ++ case 0: ++ default: ++ dsc_cap->max_slices = 0; ++ dsc_cap->clk_per_slice = 0; ++ } ++} ++ ++enum { ++ VER_26_BYTE_V0, ++ VER_15_BYTE_V1, ++ VER_12_BYTE_V1, ++ VER_12_BYTE_V2, ++}; ++ ++static int check_next_hdr_version(const u8 *next_hdr_db) ++{ ++ u16 ver; ++ ++ ver = (next_hdr_db[5] & 0xf0) << 8 | next_hdr_db[0]; ++ ++ switch (ver) { ++ case 0x00f9: ++ return VER_26_BYTE_V0; ++ case 0x20ee: ++ return VER_15_BYTE_V1; ++ case 0x20eb: ++ return VER_12_BYTE_V1; ++ case 0x40eb: ++ return VER_12_BYTE_V2; ++ default: ++ return -ENOENT; ++ } ++} ++ ++static void parse_ver_26_v0_data(struct ver_26_v0 *hdr, const u8 *data) ++{ ++ hdr->yuv422_12bit = data[5] & BIT(0); ++ hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1; ++ hdr->global_dimming = (data[5] & BIT(2)) >> 2; ++ ++ hdr->dm_major_ver = (data[21] & 0xf0) >> 4; ++ hdr->dm_minor_ver = data[21] & 0xf; ++ ++ hdr->t_min_pq = (data[19] << 4) | ((data[18] & 0xf0) >> 4); ++ hdr->t_max_pq = (data[20] << 4) | (data[18] & 0xf); ++ ++ hdr->rx = (data[7] << 4) | ((data[6] & 0xf0) >> 4); ++ hdr->ry = (data[8] << 4) | (data[6] & 0xf); ++ hdr->gx = (data[10] << 4) | ((data[9] & 0xf0) >> 4); ++ hdr->gy = (data[11] << 4) | (data[9] & 0xf); ++ hdr->bx = (data[13] << 4) | ((data[12] & 0xf0) >> 4); ++ hdr->by = (data[14] << 4) | (data[12] & 0xf); ++ hdr->wx = (data[16] << 4) | ((data[15] & 0xf0) >> 4); ++ hdr->wy = (data[17] << 4) | (data[15] & 0xf); ++} ++ ++static void parse_ver_15_v1_data(struct ver_15_v1 *hdr, const u8 *data) ++{ ++ hdr->yuv422_12bit = data[5] & BIT(0); ++ hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1; ++ hdr->global_dimming = data[6] & BIT(0); ++ ++ hdr->dm_version = (data[5] & 0x1c) >> 2; ++ ++ hdr->colorimetry = data[7] & BIT(0); ++ ++ hdr->t_max_lum = (data[6] & 0xfe) >> 1; ++ hdr->t_min_lum = (data[7] & 0xfe) >> 1; ++ ++ hdr->rx = data[9]; ++ hdr->ry = data[10]; ++ hdr->gx = data[11]; ++ hdr->gy = data[12]; ++ hdr->bx = data[13]; ++ hdr->by = data[14]; ++} ++ ++static void parse_ver_12_v1_data(struct ver_12_v1 *hdr, const u8 *data) ++{ ++ hdr->yuv422_12bit = data[5] & BIT(0); ++ hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1; ++ hdr->global_dimming = data[6] & BIT(0); ++ ++ hdr->dm_version = (data[5] & 0x1c) >> 2; ++ ++ hdr->colorimetry = data[7] & BIT(0); ++ ++ hdr->t_max_lum = (data[6] & 0xfe) >> 1; ++ hdr->t_min_lum = (data[7] & 0xfe) >> 1; ++ ++ hdr->low_latency = data[8] & 0x3; ++ ++ hdr->unique_rx = (data[11] & 0xf8) >> 3; ++ hdr->unique_ry = (data[11] & 0x7) << 2 | (data[10] & BIT(0)) << 1 | ++ (data[9] & BIT(0)); ++ hdr->unique_gx = (data[9] & 0xfe) >> 1; ++ hdr->unique_gy = (data[10] & 0xfe) >> 1; ++ hdr->unique_bx = (data[8] & 0xe0) >> 5; ++ hdr->unique_by = (data[8] & 0x1c) >> 2; ++} ++ ++static void parse_ver_12_v2_data(struct ver_12_v2 *hdr, const u8 *data) ++{ ++ hdr->yuv422_12bit = data[5] & BIT(0); ++ hdr->backlt_ctrl = (data[5] & BIT(1)) >> 1; ++ hdr->global_dimming = (data[6] & BIT(2)) >> 2; ++ ++ hdr->dm_version = (data[5] & 0x1c) >> 2; ++ hdr->backlt_min_luma = data[6] & 0x3; ++ hdr->interface = data[7] & 0x3; ++ hdr->yuv444_10b_12b = (data[8] & BIT(0)) << 1 | (data[9] & BIT(0)); ++ ++ hdr->t_min_pq_v2 = (data[6] & 0xf8) >> 3; ++ hdr->t_max_pq_v2 = (data[7] & 0xf8) >> 3; ++ ++ hdr->unique_rx = (data[10] & 0xf8) >> 3; ++ hdr->unique_ry = (data[11] & 0xf8) >> 3; ++ hdr->unique_gx = (data[8] & 0xfe) >> 1; ++ hdr->unique_gy = (data[9] & 0xfe) >> 1; ++ hdr->unique_bx = data[10] & 0x7; ++ hdr->unique_by = data[11] & 0x7; ++} ++ ++static ++void parse_next_hdr_block(struct next_hdr_sink_data *sink_data, ++ const u8 *next_hdr_db) ++{ ++ int version; ++ ++ version = check_next_hdr_version(next_hdr_db); ++ if (version < 0) ++ return; ++ ++ sink_data->version = version; ++ ++ switch (version) { ++ case VER_26_BYTE_V0: ++ parse_ver_26_v0_data(&sink_data->ver_26_v0, next_hdr_db); ++ break; ++ case VER_15_BYTE_V1: ++ parse_ver_15_v1_data(&sink_data->ver_15_v1, next_hdr_db); ++ break; ++ case VER_12_BYTE_V1: ++ parse_ver_12_v1_data(&sink_data->ver_12_v1, next_hdr_db); ++ break; ++ case VER_12_BYTE_V2: ++ parse_ver_12_v2_data(&sink_data->ver_12_v2, next_hdr_db); ++ break; ++ default: ++ break; ++ } ++} ++ ++int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap, ++ u8 *max_frl_rate_per_lane, u8 *max_lanes, ++ const struct edid *edid) ++{ ++ const u8 *edid_ext; ++ int i, start, end; ++ ++ if (!dsc_cap || !max_frl_rate_per_lane || !max_lanes || !edid) ++ return -EINVAL; ++ ++ edid_ext = find_cea_extension(edid); ++ if (!edid_ext) ++ return -EINVAL; ++ ++ if (cea_db_offsets(edid_ext, &start, &end)) ++ return -EINVAL; ++ ++ for_each_cea_db(edid_ext, i, start, end) { ++ const u8 *db = &edid_ext[i]; ++ ++ if (cea_db_is_hdmi_forum_vsdb(db)) ++ parse_edid_forum_vsdb(dsc_cap, max_frl_rate_per_lane, ++ max_lanes, db); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(rockchip_drm_parse_cea_ext); ++ ++int rockchip_drm_parse_next_hdr(struct next_hdr_sink_data *sink_data, ++ const struct edid *edid) ++{ ++ const u8 *edid_ext; ++ int i, start, end; ++ ++ if (!sink_data || !edid) ++ return -EINVAL; ++ ++ memset(sink_data, 0, sizeof(struct next_hdr_sink_data)); ++ ++ edid_ext = find_cea_extension(edid); ++ if (!edid_ext) ++ return -EINVAL; ++ ++ if (cea_db_offsets(edid_ext, &start, &end)) ++ return -EINVAL; ++ ++ for_each_cea_db(edid_ext, i, start, end) { ++ const u8 *db = &edid_ext[i]; ++ ++ if (cea_db_is_hdmi_next_hdr_block(db)) ++ parse_next_hdr_block(sink_data, db); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(rockchip_drm_parse_next_hdr); ++ + /* + * Attach a (component) device to the shared drm dma mapping from master drm + * device. This is used by the VOPs to map GEM buffers to a common DMA +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +index aeb03a572..581496f04 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +@@ -10,8 +10,11 @@ + #define _ROCKCHIP_DRM_DRV_H + + #include ++#include ++#include ++#include + #include +- ++#include + #include + #include + #include +@@ -19,25 +22,367 @@ + #define ROCKCHIP_MAX_FB_BUFFER 3 + #define ROCKCHIP_MAX_CONNECTOR 2 + #define ROCKCHIP_MAX_CRTC 4 ++#define ROCKCHIP_MAX_LAYER 16 + + struct drm_device; + struct drm_connector; + struct iommu_domain; + ++#define VOP_COLOR_KEY_NONE (0 << 31) ++#define VOP_COLOR_KEY_MASK (1 << 31) ++ ++#define VOP_OUTPUT_IF_RGB BIT(0) ++#define VOP_OUTPUT_IF_BT1120 BIT(1) ++#define VOP_OUTPUT_IF_BT656 BIT(2) ++#define VOP_OUTPUT_IF_LVDS0 BIT(3) ++#define VOP_OUTPUT_IF_LVDS1 BIT(4) ++#define VOP_OUTPUT_IF_MIPI0 BIT(5) ++#define VOP_OUTPUT_IF_MIPI1 BIT(6) ++#define VOP_OUTPUT_IF_eDP0 BIT(7) ++#define VOP_OUTPUT_IF_eDP1 BIT(8) ++#define VOP_OUTPUT_IF_DP0 BIT(9) ++#define VOP_OUTPUT_IF_DP1 BIT(10) ++#define VOP_OUTPUT_IF_HDMI0 BIT(11) ++#define VOP_OUTPUT_IF_HDMI1 BIT(12) ++ ++#ifndef DRM_FORMAT_NV20 ++#define DRM_FORMAT_NV20 fourcc_code('N', 'V', '2', '0') /* 2x1 subsampled Cr:Cb plane */ ++#endif ++ ++#ifndef DRM_FORMAT_NV30 ++#define DRM_FORMAT_NV30 fourcc_code('N', 'V', '3', '0') /* non-subsampled Cr:Cb plane */ ++#endif ++ ++#define RK_IF_PROP_COLOR_DEPTH "color_depth" ++#define RK_IF_PROP_COLOR_FORMAT "color_format" ++#define RK_IF_PROP_COLOR_DEPTH_CAPS "color_depth_caps" ++#define RK_IF_PROP_COLOR_FORMAT_CAPS "color_format_caps" ++ ++enum rk_if_color_depth { ++ RK_IF_DEPTH_8, ++ RK_IF_DEPTH_10, ++ RK_IF_DEPTH_12, ++ RK_IF_DEPTH_16, ++ RK_IF_DEPTH_420_10, ++ RK_IF_DEPTH_420_12, ++ RK_IF_DEPTH_420_16, ++ RK_IF_DEPTH_6, ++ RK_IF_DEPTH_MAX, ++}; ++ ++enum rk_if_color_format { ++ RK_IF_FORMAT_RGB, /* default RGB */ ++ RK_IF_FORMAT_YCBCR444, /* YCBCR 444 */ ++ RK_IF_FORMAT_YCBCR422, /* YCBCR 422 */ ++ RK_IF_FORMAT_YCBCR420, /* YCBCR 420 */ ++ RK_IF_FORMAT_YCBCR_HQ, /* Highest subsampled YUV */ ++ RK_IF_FORMAT_YCBCR_LQ, /* Lowest subsampled YUV */ ++ RK_IF_FORMAT_MAX, ++}; ++ ++struct rockchip_drm_sub_dev { ++ struct list_head list; ++ struct drm_connector *connector; ++ struct device_node *of_node; ++ void (*loader_protect)(struct drm_encoder *encoder, bool on); ++ void (*oob_hotplug_event)(struct drm_connector *connector); ++}; ++ ++struct rockchip_sdr2hdr_state { ++ int sdr2hdr_func; ++ ++ bool bt1886eotf_pre_conv_en; ++ bool rgb2rgb_pre_conv_en; ++ bool rgb2rgb_pre_conv_mode; ++ bool st2084oetf_pre_conv_en; ++ ++ bool bt1886eotf_post_conv_en; ++ bool rgb2rgb_post_conv_en; ++ bool rgb2rgb_post_conv_mode; ++ bool st2084oetf_post_conv_en; ++}; ++ ++struct rockchip_hdr_state { ++ bool pre_overlay; ++ bool hdr2sdr_en; ++ struct rockchip_sdr2hdr_state sdr2hdr_state; ++}; ++ ++struct rockchip_bcsh_state { ++ int brightness; ++ int contrast; ++ int saturation; ++ int sin_hue; ++ int cos_hue; ++}; ++ ++struct rockchip_crtc { ++ struct drm_crtc crtc; ++#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) ++ /** ++ * @vop_dump_status the status of vop dump control ++ * @vop_dump_list_head the list head of vop dump list ++ * @vop_dump_list_init_flag init once ++ * @vop_dump_times control the dump times ++ * @frme_count the frame of dump buf ++ */ ++ enum vop_dump_status vop_dump_status; ++ struct list_head vop_dump_list_head; ++ bool vop_dump_list_init_flag; ++ int vop_dump_times; ++ int frame_count; ++#endif ++}; ++ ++struct rockchip_dsc_sink_cap { ++ /** ++ * @slice_width: the number of pixel columns that comprise the slice width ++ * @slice_height: the number of pixel rows that comprise the slice height ++ * @block_pred: Does block prediction ++ * @native_420: Does sink support DSC with 4:2:0 compression ++ * @bpc_supported: compressed bpc supported by sink : 10, 12 or 16 bpc ++ * @version_major: DSC major version ++ * @version_minor: DSC minor version ++ * @target_bits_per_pixel_x16: bits num after compress and multiply 16 ++ */ ++ u16 slice_width; ++ u16 slice_height; ++ bool block_pred; ++ bool native_420; ++ u8 bpc_supported; ++ u8 version_major; ++ u8 version_minor; ++ u16 target_bits_per_pixel_x16; ++}; ++ + struct rockchip_crtc_state { + struct drm_crtc_state base; ++ int vp_id; + int output_type; + int output_mode; + int output_bpc; + int output_flags; + bool enable_afbc; ++ ++ //[CC:] vop2 related change ++ /** ++ * @splice_mode: enabled when display a hdisplay > 4096 on rk3588 ++ */ ++ bool splice_mode; ++ ++ /** ++ * @hold_mode: enabled when it's: ++ * (1) mcu hold mode ++ * (2) mipi dsi cmd mode ++ * (3) edp psr mode ++ */ ++ bool hold_mode; ++ ++ struct drm_tv_connector_state *tv_state; ++ int left_margin; ++ int right_margin; ++ int top_margin; ++ int bottom_margin; ++ int vdisplay; ++ int afbdc_win_format; ++ int afbdc_win_width; ++ int afbdc_win_height; ++ int afbdc_win_ptr; ++ int afbdc_win_id; ++ int afbdc_en; ++ int afbdc_win_vir_width; ++ int afbdc_win_xoffset; ++ int afbdc_win_yoffset; ++ int dsp_layer_sel; ++ u32 output_if; + u32 bus_format; + u32 bus_flags; ++ int yuv_overlay; ++ int post_r2y_en; ++ int post_y2r_en; ++ int post_csc_mode; ++ int bcsh_en; + int color_space; ++ int eotf; ++ u32 background; ++ u32 line_flag; ++ u8 mode_update; ++ u8 dsc_id; ++ ++ //[CC:] vop2 related changes ++ u8 dsc_enable; ++ unsigned long dsc_clk; ++ ++ u8 dsc_slice_num; ++ u8 dsc_pixel_num; ++ ++ u64 dsc_txp_clk_rate; ++ u64 dsc_pxl_clk_rate; ++ u64 dsc_cds_clk_rate; ++ ++ struct drm_dsc_picture_parameter_set pps; ++ struct rockchip_dsc_sink_cap dsc_sink_cap; ++ struct rockchip_hdr_state hdr; + }; ++ + #define to_rockchip_crtc_state(s) \ + container_of(s, struct rockchip_crtc_state, base) + ++struct rockchip_drm_vcnt { ++ struct drm_pending_vblank_event *event; ++ __u32 sequence; ++ int pipe; ++}; ++ ++struct rockchip_logo { ++ dma_addr_t dma_addr; ++ struct drm_mm_node logo_reserved_node; ++ void *kvaddr; ++ phys_addr_t start; ++ phys_addr_t size; ++ int count; ++}; ++ ++struct loader_cubic_lut { ++ bool enable; ++ u32 offset; ++}; ++ ++struct rockchip_drm_dsc_cap { ++ bool v_1p2; ++ bool native_420; ++ bool all_bpp; ++ u8 bpc_supported; ++ u8 max_slices; ++ u8 max_lanes; ++ u8 max_frl_rate_per_lane; ++ u8 total_chunk_kbytes; ++ int clk_per_slice; ++}; ++ ++struct ver_26_v0 { ++ u8 yuv422_12bit; ++ u8 support_2160p_60; ++ u8 global_dimming; ++ u8 dm_major_ver; ++ u8 dm_minor_ver; ++ u16 t_min_pq; ++ u16 t_max_pq; ++ u16 rx; ++ u16 ry; ++ u16 gx; ++ u16 gy; ++ u16 bx; ++ u16 by; ++ u16 wx; ++ u16 wy; ++} __packed; ++ ++struct ver_15_v1 { ++ u8 yuv422_12bit; ++ u8 support_2160p_60; ++ u8 global_dimming; ++ u8 dm_version; ++ u8 colorimetry; ++ u8 t_max_lum; ++ u8 t_min_lum; ++ u8 rx; ++ u8 ry; ++ u8 gx; ++ u8 gy; ++ u8 bx; ++ u8 by; ++} __packed; ++ ++struct ver_12_v1 { ++ u8 yuv422_12bit; ++ u8 support_2160p_60; ++ u8 global_dimming; ++ u8 dm_version; ++ u8 colorimetry; ++ u8 low_latency; ++ u8 t_max_lum; ++ u8 t_min_lum; ++ u8 unique_rx; ++ u8 unique_ry; ++ u8 unique_gx; ++ u8 unique_gy; ++ u8 unique_bx; ++ u8 unique_by; ++} __packed; ++ ++struct ver_12_v2 { ++ u8 yuv422_12bit; ++ u8 backlt_ctrl; ++ u8 global_dimming; ++ u8 dm_version; ++ u8 backlt_min_luma; ++ u8 interface; ++ u8 yuv444_10b_12b; ++ u8 t_min_pq_v2; ++ u8 t_max_pq_v2; ++ u8 unique_rx; ++ u8 unique_ry; ++ u8 unique_gx; ++ u8 unique_gy; ++ u8 unique_bx; ++ u8 unique_by; ++} __packed; ++ ++struct next_hdr_sink_data { ++ u8 version; ++ struct ver_26_v0 ver_26_v0; ++ struct ver_15_v1 ver_15_v1; ++ struct ver_12_v1 ver_12_v1; ++ struct ver_12_v2 ver_12_v2; ++} __packed; ++ ++//[CC:] drop struct dmcfreq_vop_info ++struct dmcfreq_vop_info; ++ ++/* ++ * Rockchip drm private crtc funcs. ++ * @loader_protect: protect loader logo crtc's power ++ * @enable_vblank: enable crtc vblank irq. ++ * @disable_vblank: disable crtc vblank irq. ++ * @bandwidth: report present crtc bandwidth consume. ++ * @cancel_pending_vblank: cancel pending vblank. ++ * @debugfs_init: init crtc debugfs. ++ * @debugfs_dump: debugfs to dump crtc and plane state. ++ * @regs_dump: dump vop current register config. ++ * @mode_valid: verify that the current mode is supported. ++ * @crtc_close: close vop. ++ * @crtc_send_mcu_cmd: send mcu panel init cmd. ++ * @te_handler: soft te hand for cmd mode panel. ++ * @wait_vact_end: wait the last active line. ++ */ ++struct rockchip_crtc_funcs { ++ int (*loader_protect)(struct drm_crtc *crtc, bool on); ++ int (*enable_vblank)(struct drm_crtc *crtc); ++ void (*disable_vblank)(struct drm_crtc *crtc); ++ size_t (*bandwidth)(struct drm_crtc *crtc, ++ struct drm_crtc_state *crtc_state, ++ struct dmcfreq_vop_info *vop_bw_info); ++ void (*cancel_pending_vblank)(struct drm_crtc *crtc, ++ struct drm_file *file_priv); ++ int (*debugfs_init)(struct drm_minor *minor, struct drm_crtc *crtc); ++ int (*debugfs_dump)(struct drm_crtc *crtc, struct seq_file *s); ++ void (*regs_dump)(struct drm_crtc *crtc, struct seq_file *s); ++ enum drm_mode_status (*mode_valid)(struct drm_crtc *crtc, ++ const struct drm_display_mode *mode, ++ int output_type); ++ void (*crtc_close)(struct drm_crtc *crtc); ++ void (*crtc_send_mcu_cmd)(struct drm_crtc *crtc, u32 type, u32 value); ++ void (*te_handler)(struct drm_crtc *crtc); ++ int (*wait_vact_end)(struct drm_crtc *crtc, unsigned int mstimeout); ++ void (*crtc_standby)(struct drm_crtc *crtc, bool standby); ++}; ++ ++struct rockchip_dclk_pll { ++ struct clk *pll; ++ unsigned int use_count; ++}; ++ + /* + * Rockchip drm private structure. + * +@@ -46,10 +391,56 @@ struct rockchip_crtc_state { + * @mm_lock: protect drm_mm on multi-threads. + */ + struct rockchip_drm_private { ++ struct rockchip_logo *logo; ++ struct drm_fb_helper *fbdev_helper; ++ struct drm_gem_object *fbdev_bo; + struct iommu_domain *domain; ++ struct gen_pool *secure_buffer_pool; + struct device *iommu_dev; + struct mutex mm_lock; + struct drm_mm mm; ++ struct list_head psr_list; ++ struct mutex psr_list_lock; ++ struct mutex commit_lock; ++ ++ /* private crtc prop */ ++ struct drm_property *soc_id_prop; ++ struct drm_property *port_id_prop; ++ struct drm_property *aclk_prop; ++ struct drm_property *bg_prop; ++ struct drm_property *line_flag_prop; ++ ++ /* private plane prop */ ++ struct drm_property *eotf_prop; ++ struct drm_property *color_space_prop; ++ struct drm_property *async_commit_prop; ++ struct drm_property *share_id_prop; ++ ++ /* private connector prop */ ++ struct drm_property *connector_id_prop; ++ ++ const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC]; ++ ++ struct rockchip_dclk_pll default_pll; ++ struct rockchip_dclk_pll hdmi_pll; ++ ++ /* ++ * protect some shared overlay resource ++ * OVL_LAYER_SEL/OVL_PORT_SEL ++ */ ++ struct mutex ovl_lock; ++ ++ struct rockchip_drm_vcnt vcnt[ROCKCHIP_MAX_CRTC]; ++ /** ++ * @loader_protect ++ * ignore restore_fbdev_mode_atomic when in logo on state ++ */ ++ bool loader_protect; ++ ++ dma_addr_t cubic_lut_dma_addr; ++ void *cubic_lut_kvaddr; ++ struct drm_mm_node *clut_reserved_node; ++ struct loader_cubic_lut cubic_lut[ROCKCHIP_MAX_CRTC]; + }; + + struct rockchip_encoder { +@@ -66,16 +457,52 @@ void rockchip_drm_dma_init_device(struct drm_device *drm_dev, + int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout); + int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rencoder, + struct device_node *np, int port, int reg); ++ ++int rockchip_register_crtc_funcs(struct drm_crtc *crtc, ++ const struct rockchip_crtc_funcs *crtc_funcs); ++void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc); ++void rockchip_drm_crtc_standby(struct drm_crtc *crtc, bool standby); ++ ++void rockchip_drm_register_sub_dev(struct rockchip_drm_sub_dev *sub_dev); ++void rockchip_drm_unregister_sub_dev(struct rockchip_drm_sub_dev *sub_dev); ++struct rockchip_drm_sub_dev *rockchip_drm_get_sub_dev(struct device_node *node); ++int rockchip_drm_add_modes_noedid(struct drm_connector *connector); ++void rockchip_drm_te_handle(struct drm_crtc *crtc); ++void drm_mode_convert_to_split_mode(struct drm_display_mode *mode); ++void drm_mode_convert_to_origin_mode(struct drm_display_mode *mode); ++#if IS_REACHABLE(CONFIG_DRM_ROCKCHIP) ++int rockchip_drm_get_sub_dev_type(void); ++#else ++static inline int rockchip_drm_get_sub_dev_type(void) ++{ ++ return DRM_MODE_CONNECTOR_Unknown; ++} ++#endif ++ + int rockchip_drm_endpoint_is_subdriver(struct device_node *ep); ++uint32_t rockchip_drm_get_bpp(const struct drm_format_info *info); ++int rockchip_drm_get_yuv422_format(struct drm_connector *connector, ++ struct edid *edid); ++int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap, ++ u8 *max_frl_rate_per_lane, u8 *max_lanes, ++ const struct edid *edid); ++int rockchip_drm_parse_next_hdr(struct next_hdr_sink_data *sink_data, ++ const struct edid *edid); ++ + extern struct platform_driver cdn_dp_driver; + extern struct platform_driver dw_hdmi_rockchip_pltfm_driver; + extern struct platform_driver dw_mipi_dsi_rockchip_driver; ++extern struct platform_driver dw_mipi_dsi2_rockchip_driver; + extern struct platform_driver inno_hdmi_driver; + extern struct platform_driver rockchip_dp_driver; + extern struct platform_driver rockchip_lvds_driver; + extern struct platform_driver vop_platform_driver; +-extern struct platform_driver rk3066_hdmi_driver; + extern struct platform_driver vop2_platform_driver; ++extern struct platform_driver rk3066_hdmi_driver; ++extern struct platform_driver rockchip_rgb_driver; ++extern struct platform_driver dw_dp_driver; ++extern struct platform_driver vconn_platform_driver; ++extern struct platform_driver vvop_platform_driver; + + static inline struct rockchip_encoder *to_rockchip_encoder(struct drm_encoder *encoder) + { +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +index 4b2daefeb..6ebea5c36 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +@@ -15,6 +15,16 @@ + #define VOP_MAJOR(version) ((version) >> 8) + #define VOP_MINOR(version) ((version) & 0xff) + ++//[CC:] vop2 related changes ++#define VOP_VERSION_RK3568 VOP_VERSION(0x40, 0x15) ++#define VOP_VERSION_RK3588 VOP_VERSION(0x40, 0x17) ++ ++#define ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE BIT(0) ++#define ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE BIT(1) ++#define ROCKCHIP_OUTPUT_DATA_SWAP BIT(2) ++/* MIPI DSI DataStream(cmd) mode on rk3588 */ ++#define ROCKCHIP_OUTPUT_MIPI_DS_MODE BIT(3) ++ + #define NUM_YUV2YUV_COEFFICIENTS 12 + + /* AFBC supports a number of configurable modes. Relevant to us is block size +@@ -280,11 +290,16 @@ struct vop_data { + /* + * display output interface supported by rockchip lcdc + */ +-#define ROCKCHIP_OUT_MODE_P888 0 +-#define ROCKCHIP_OUT_MODE_P666 1 +-#define ROCKCHIP_OUT_MODE_P565 2 ++#define ROCKCHIP_OUT_MODE_P888 0 ++#define ROCKCHIP_OUT_MODE_BT1120 0 ++#define ROCKCHIP_OUT_MODE_P666 1 ++#define ROCKCHIP_OUT_MODE_P565 2 ++#define ROCKCHIP_OUT_MODE_BT656 5 ++#define ROCKCHIP_OUT_MODE_S888 8 ++#define ROCKCHIP_OUT_MODE_S888_DUMMY 12 ++#define ROCKCHIP_OUT_MODE_YUV420 14 + /* for use special outface */ +-#define ROCKCHIP_OUT_MODE_AAAA 15 ++#define ROCKCHIP_OUT_MODE_AAAA 15 + + /* output flags */ + #define ROCKCHIP_OUTPUT_DSI_DUAL BIT(0) +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +index 6862fb146..a073d1d5c 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +@@ -5,6 +5,8 @@ + */ + #include + #include ++#include ++#include + #include + #include + #include +@@ -17,6 +19,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -159,6 +162,8 @@ struct vop2_video_port { + struct drm_crtc crtc; + struct vop2 *vop2; + struct clk *dclk; ++ struct reset_control *dclk_rst; ++ struct clk *dclk_parent; + unsigned int id; + const struct vop2_video_port_data *data; + +@@ -191,6 +196,8 @@ struct vop2 { + struct regmap *map; + + struct regmap *grf; ++ struct regmap *vop_grf; ++ struct regmap *vo1_grf; + + /* physical map length of vop2 register */ + u32 len; +@@ -209,14 +216,43 @@ struct vop2 { + unsigned int enable_count; + struct clk *hclk; + struct clk *aclk; ++ struct clk *pclk; ++ // [CC:] handle all display modes ++ struct clk *hdmi0_phy_pll; ++ struct reset_control *ahb_rst; ++ struct reset_control *axi_rst; + + /* optional internal rgb encoder */ + struct rockchip_rgb *rgb; + ++ /* list_head of internal clk */ ++ struct list_head clk_list_head; ++ + /* must be put at the end of the struct */ + struct vop2_win win[]; + }; + ++struct vop2_clk { ++ struct vop2 *vop2; ++ struct list_head list; ++ unsigned long rate; ++ struct clk_hw hw; ++ struct clk_divider div; ++ int div_val; ++ u8 parent_index; ++}; ++ ++#define to_vop2_clk(_hw) container_of(_hw, struct vop2_clk, hw) ++ ++#define VOP2_MAX_DCLK_RATE 600000 /* kHz */ ++ ++#define vop2_output_if_is_hdmi(x) (x == ROCKCHIP_VOP2_EP_HDMI0 || x == ROCKCHIP_VOP2_EP_HDMI1) ++#define vop2_output_if_is_dp(x) (x == ROCKCHIP_VOP2_EP_DP0 || x == ROCKCHIP_VOP2_EP_DP1) ++#define vop2_output_if_is_edp(x) (x == ROCKCHIP_VOP2_EP_EDP0 || x == ROCKCHIP_VOP2_EP_EDP1) ++#define vop2_output_if_is_mipi(x) (x == ROCKCHIP_VOP2_EP_MIPI0 || x == ROCKCHIP_VOP2_EP_MIPI1) ++#define vop2_output_if_is_lvds(x) (x == ROCKCHIP_VOP2_EP_LVDS0 || x == ROCKCHIP_VOP2_EP_LVDS1) ++#define vop2_output_if_is_dpi(x) (x == ROCKCHIP_VOP2_EP_RGB0) ++ + static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc) + { + return container_of(crtc, struct vop2_video_port, crtc); +@@ -269,9 +305,16 @@ static bool vop2_cluster_window(const struct vop2_win *win) + static void vop2_cfg_done(struct vop2_video_port *vp) + { + struct vop2 *vop2 = vp->vop2; ++ unsigned int bits = BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN; + +- regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE, +- BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN); ++ if (vop2->data->soc_id == 3588) { ++ bits |= BIT(vp->id) << 16; ++ // [CC:] handle splice_mode ++ // if (vcstate->splice_mode) ++ // bits |= BIT(vp_data->splice_vp_id) | (BIT(vp_data->splice_vp_id) << 16); ++ } ++ ++ regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE, bits); + } + + static void vop2_win_disable(struct vop2_win *win) +@@ -846,16 +889,36 @@ static int vop2_core_clks_prepare_enable(struct vop2 *vop2) + ret = clk_prepare_enable(vop2->aclk); + if (ret < 0) { + drm_err(vop2->drm, "failed to enable aclk - %d\n", ret); +- goto err; ++ goto err_aclk; ++ } ++ ++ ret = clk_prepare_enable(vop2->pclk); ++ if (ret < 0) { ++ drm_err(vop2->drm, "failed to enable pclk - %d\n", ret); ++ goto err_pclk; + } + + return 0; +-err: ++ ++err_pclk: ++ clk_disable_unprepare(vop2->aclk); ++err_aclk: + clk_disable_unprepare(vop2->hclk); + + return ret; + } + ++static void vop2_power_domain_all_on(struct vop2 *vop2) ++{ ++ u32 pd; ++ ++ pd = vop2_readl(vop2, RK3588_SYS_PD_CTRL); ++ pd |= VOP2_PD_CLUSTER0 | VOP2_PD_CLUSTER1 | VOP2_PD_CLUSTER2 | ++ VOP2_PD_CLUSTER3 | VOP2_PD_ESMART; ++ ++ vop2_writel(vop2, RK3588_SYS_PD_CTRL, pd); ++} ++ + static void vop2_enable(struct vop2 *vop2) + { + int ret; +@@ -883,6 +946,9 @@ static void vop2_enable(struct vop2 *vop2) + if (vop2->data->soc_id == 3566) + vop2_writel(vop2, RK3568_OTP_WIN_EN, 1); + ++ if (vop2->data->soc_id == 3588) ++ vop2_power_domain_all_on(vop2); ++ + vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN); + + /* +@@ -910,63 +976,11 @@ static void vop2_disable(struct vop2 *vop2) + + regcache_mark_dirty(vop2->map); + ++ clk_disable_unprepare(vop2->pclk); + clk_disable_unprepare(vop2->aclk); + clk_disable_unprepare(vop2->hclk); + } + +-static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, +- struct drm_atomic_state *state) +-{ +- struct vop2_video_port *vp = to_vop2_video_port(crtc); +- struct vop2 *vop2 = vp->vop2; +- struct drm_crtc_state *old_crtc_state; +- int ret; +- +- vop2_lock(vop2); +- +- old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); +- drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); +- +- drm_crtc_vblank_off(crtc); +- +- /* +- * Vop standby will take effect at end of current frame, +- * if dsp hold valid irq happen, it means standby complete. +- * +- * we must wait standby complete when we want to disable aclk, +- * if not, memory bus maybe dead. +- */ +- reinit_completion(&vp->dsp_hold_completion); +- +- vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID); +- +- vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY); +- +- ret = wait_for_completion_timeout(&vp->dsp_hold_completion, +- msecs_to_jiffies(50)); +- if (!ret) +- drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id); +- +- vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID); +- +- clk_disable_unprepare(vp->dclk); +- +- vop2->enable_count--; +- +- if (!vop2->enable_count) +- vop2_disable(vop2); +- +- vop2_unlock(vop2); +- +- if (crtc->state->event && !crtc->state->active) { +- spin_lock_irq(&crtc->dev->event_lock); +- drm_crtc_send_vblank_event(crtc, crtc->state->event); +- spin_unlock_irq(&crtc->dev->event_lock); +- +- crtc->state->event = NULL; +- } +-} +- + static int vop2_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *astate) + { +@@ -1004,6 +1018,7 @@ static int vop2_plane_atomic_check(struct drm_plane *plane, + if (!pstate->visible) + return 0; + ++ // [CC:] Drop format var since it's not used anywhere, use ret var instead + format = vop2_convert_format(fb->format->format); + if (format < 0) + return format; +@@ -1027,6 +1042,35 @@ static int vop2_plane_atomic_check(struct drm_plane *plane, + return -EINVAL; + } + ++ // [CC:] do we need the checks below? ++ bool afbc_en = rockchip_afbc(plane, fb->modifier); ++ struct vop2_win *win = to_vop2_win(plane); ++ ++ /* ++ * This is special feature at rk356x, the cluster layer only can support ++ * afbc format and can't support linear format; ++ */ ++ if (vp->vop2->data->soc_id == 3568) { ++ if (vop2_cluster_window(win) && !afbc_en) { ++ DRM_ERROR("Unsupported linear format at %s\n", win->data->name); ++ return -EINVAL; ++ } ++ } ++ ++ if (vp->vop2->data->soc_id > 3568) { ++ if (vop2_cluster_window(win) && !afbc_en && fb->format->is_yuv) { ++ DRM_ERROR("Unsupported linear yuv format at %s\n", win->data->name); ++ return -EINVAL; ++ } ++ ++ if (vop2_cluster_window(win) && !afbc_en && ++ (win->data->supported_rotations & pstate->rotation)) { ++ DRM_ERROR("Unsupported linear rotation(%d) format at %s\n", ++ pstate->rotation, win->data->name); ++ return -EINVAL; ++ } ++ } ++ + /* + * Src.x1 can be odd when do clip, but yuv plane start point + * need align with 2 pixel. +@@ -1107,6 +1151,7 @@ static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key) + vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b); + } + ++// [CC:] fix BUG: sleeping function called from invalid context + static void vop2_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) + { +@@ -1272,7 +1317,12 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, + vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1); + vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format); + vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap); +- vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0); ++ ++ if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568) ++ vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0); ++ else ++ vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 1); ++ + vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0); + if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) { + vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0); +@@ -1376,9 +1426,30 @@ static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) + { ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ struct drm_connector *connector; ++ struct drm_connector_list_iter conn_iter; ++ struct drm_crtc_state *new_crtc_state = container_of(mode, struct drm_crtc_state, mode); + drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V | + CRTC_STEREO_DOUBLE); + ++ if (mode->flags & DRM_MODE_FLAG_DBLCLK) ++ adj_mode->crtc_clock *= 2; ++ ++ drm_connector_list_iter_begin(crtc->dev, &conn_iter); ++ drm_for_each_connector_iter(connector, &conn_iter) { ++ if ((new_crtc_state->connector_mask & drm_connector_mask(connector)) && ++ ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) || ++ (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA))) { ++ drm_connector_list_iter_end(&conn_iter); ++ return true; ++ } ++ } ++ drm_connector_list_iter_end(&conn_iter); ++ ++ if (adj_mode->crtc_clock <= VOP2_MAX_DCLK_RATE) ++ adj_mode->crtc_clock = DIV_ROUND_UP(clk_round_rate(vp->dclk, ++ adj_mode->crtc_clock * 1000), 1000); + return true; + } + +@@ -1462,7 +1533,7 @@ static void vop2_post_config(struct drm_crtc *crtc) + } + + static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id, +- u32 polflags) ++ u32 polflags, u32 invpolflags) + { + struct vop2 *vop2 = vp->vop2; + u32 die, dip; +@@ -1477,6 +1548,7 @@ static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id, + FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id); + dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL; + dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags); ++ // [CC:] use .grf_dclk_inv = VOP_REG(RK3588_GRF_SOC_CON1, 0x1, 14) for 3588 variant + if (polflags & POLFLAG_DCLK_INV) + regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3)); + else +@@ -1487,8 +1559,18 @@ static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id, + die |= RK3568_SYS_DSP_INFACE_EN_HDMI | + FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id); + dip &= ~RK3568_DSP_IF_POL__HDMI_PIN_POL; +- dip |= FIELD_PREP(RK3568_DSP_IF_POL__HDMI_PIN_POL, polflags); ++ dip |= FIELD_PREP(RK3568_DSP_IF_POL__HDMI_PIN_POL, invpolflags); ++ ++ /* grf_hdmi0_en */ ++ if (vop2->vop_grf) ++ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, BIT(1 + 16) | BIT(1)); ++ /* hdmi0_pin_pol */ ++ if (vop2->vo1_grf) ++ regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0, ++ RK3588_GRF_VO1_CON0__HDMI0_PIN_POL << 16 | ++ FIELD_PREP(RK3588_GRF_VO1_CON0__HDMI0_PIN_POL, invpolflags)); + break; ++ // [CC:] TODO: ROCKCHIP_VOP2_EP_HDMI1 + case ROCKCHIP_VOP2_EP_EDP0: + die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX; + die |= RK3568_SYS_DSP_INFACE_EN_EDP | +@@ -1535,11 +1617,354 @@ static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id, + vop2_writel(vop2, RK3568_DSP_IF_POL, dip); + } + ++/* ++ * calc the dclk on rk3588 ++ * the available div of dclk is 1, 2, 4 ++ */ ++static unsigned long vop2_calc_dclk(unsigned long child_clk, unsigned long max_dclk) ++{ ++ if (child_clk * 4 <= max_dclk) ++ return child_clk * 4; ++ else if (child_clk * 2 <= max_dclk) ++ return child_clk * 2; ++ else if (child_clk <= max_dclk) ++ return child_clk; ++ else ++ return 0; ++} ++ ++static struct vop2_clk *vop2_clk_get(struct vop2 *vop2, const char *name); ++ ++static int vop2_cru_set_rate(struct vop2_clk *if_pixclk, struct vop2_clk *if_dclk) ++{ ++ int ret = 0; ++ ++ if (if_pixclk) { ++ ret = clk_set_rate(if_pixclk->hw.clk, if_pixclk->rate); ++ if (ret < 0) { ++ DRM_DEV_ERROR(if_pixclk->vop2->dev, "set %s to %ld failed: %d\n", ++ clk_hw_get_name(&if_pixclk->hw), if_pixclk->rate, ret); ++ return ret; ++ } ++ } ++ ++ if (if_dclk) { ++ ret = clk_set_rate(if_dclk->hw.clk, if_dclk->rate); ++ if (ret < 0) ++ DRM_DEV_ERROR(if_dclk->vop2->dev, "set %s to %ld failed %d\n", ++ clk_hw_get_name(&if_dclk->hw), if_dclk->rate, ret); ++ } ++ ++ return ret; ++} ++ ++/* ++ * 4 pixclk/cycle on rk3588 ++ * RGB/eDP/HDMI: if_pixclk >= dclk_core ++ * DP: dp_pixclk = dclk_out <= dclk_core ++ * DSI: mipi_pixclk <= dclk_out <= dclk_core ++ */ ++static unsigned long vop2_calc_cru_cfg(struct vop2_video_port *vp, int id, ++ int *dclk_core_div, int *dclk_out_div, ++ int *if_pixclk_div, int *if_dclk_div) ++{ ++ struct vop2 *vop2 = vp->vop2; ++ struct drm_crtc *crtc = &vp->crtc; ++ struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; ++ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); ++ int output_mode = vcstate->output_mode; ++ unsigned long v_pixclk = adjusted_mode->crtc_clock * 1000LL; /* video timing pixclk */ ++ unsigned long dclk_core_rate = v_pixclk >> 2; ++ unsigned long dclk_rate = v_pixclk; ++ unsigned long dclk_out_rate; ++ unsigned long if_dclk_rate; ++ unsigned long if_pixclk_rate; ++ int K = 1; ++ ++ if (vop2_output_if_is_hdmi(id)) { ++ if (vop2->data->soc_id == 3588 && id == ROCKCHIP_VOP2_EP_HDMI0) { ++ const char *clk_src_name = "hdmi_edp0_clk_src"; ++ const char *clk_parent_name = "dclk"; ++ const char *pixclk_name = "hdmi_edp0_pixclk"; ++ const char *dclk_name = "hdmi_edp0_dclk"; ++ struct vop2_clk *if_clk_src, *if_clk_parent, *if_pixclk, *if_dclk, *dclk, *dclk_core, *dclk_out; ++ char clk_name[32]; ++ int ret; ++ ++ if_clk_src = vop2_clk_get(vop2, clk_src_name); ++ snprintf(clk_name, sizeof(clk_name), "%s%d", clk_parent_name, vp->id); ++ if_clk_parent = vop2_clk_get(vop2, clk_name); ++ if_pixclk = vop2_clk_get(vop2, pixclk_name); ++ if_dclk = vop2_clk_get(vop2, dclk_name); ++ if (!if_pixclk || !if_clk_parent) { ++ DRM_DEV_ERROR(vop2->dev, "failed to get connector interface clk\n"); ++ return -ENODEV; ++ } ++ ++ ret = clk_set_parent(if_clk_src->hw.clk, if_clk_parent->hw.clk); ++ if (ret < 0) { ++ DRM_DEV_ERROR(vop2->dev, "failed to set parent(%s) for %s: %d\n", ++ __clk_get_name(if_clk_parent->hw.clk), ++ __clk_get_name(if_clk_src->hw.clk), ret); ++ return ret; ++ } ++ ++ if (output_mode == ROCKCHIP_OUT_MODE_YUV420) ++ K = 2; ++ ++ if_pixclk->rate = (dclk_core_rate << 1) / K; ++ if_dclk->rate = dclk_core_rate / K; ++ ++ snprintf(clk_name, sizeof(clk_name), "dclk_core%d", vp->id); ++ dclk_core = vop2_clk_get(vop2, clk_name); ++ ++ snprintf(clk_name, sizeof(clk_name), "dclk_out%d", vp->id); ++ dclk_out = vop2_clk_get(vop2, clk_name); ++ ++ snprintf(clk_name, sizeof(clk_name), "dclk%d", vp->id); ++ dclk = vop2_clk_get(vop2, clk_name); ++ if (v_pixclk <= (VOP2_MAX_DCLK_RATE * 1000)) { ++ if (output_mode == ROCKCHIP_OUT_MODE_YUV420) ++ v_pixclk = v_pixclk >> 1; ++ } else { ++ v_pixclk = v_pixclk >> 2; ++ } ++ clk_set_rate(dclk->hw.clk, v_pixclk); ++ ++ if (dclk_core_rate > if_pixclk->rate) { ++ clk_set_rate(dclk_core->hw.clk, dclk_core_rate); ++ ret = vop2_cru_set_rate(if_pixclk, if_dclk); ++ } else { ++ ret = vop2_cru_set_rate(if_pixclk, if_dclk); ++ clk_set_rate(dclk_core->hw.clk, dclk_core_rate); ++ } ++ ++ *dclk_core_div = dclk_core->div_val; ++ *dclk_out_div = dclk_out->div_val; ++ *if_pixclk_div = if_pixclk->div_val; ++ *if_dclk_div = if_dclk->div_val; ++ ++ return dclk->rate; ++ } ++ ++ /* ++ * K = 2: dclk_core = if_pixclk_rate > if_dclk_rate ++ * K = 1: dclk_core = hdmie_edp_dclk > if_pixclk_rate ++ */ ++ if (output_mode == ROCKCHIP_OUT_MODE_YUV420) { ++ dclk_rate = dclk_rate >> 1; ++ K = 2; ++ } ++ ++ if_pixclk_rate = (dclk_core_rate << 1) / K; ++ if_dclk_rate = dclk_core_rate / K; ++ ++ *if_pixclk_div = dclk_rate / if_pixclk_rate; ++ *if_dclk_div = dclk_rate / if_dclk_rate; ++ *dclk_core_div = dclk_rate / dclk_core_rate; ++ } else if (vop2_output_if_is_edp(id)) { ++ /* edp_pixclk = edp_dclk > dclk_core */ ++ if_pixclk_rate = v_pixclk / K; ++ if_dclk_rate = v_pixclk / K; ++ dclk_rate = if_pixclk_rate * K; ++ *dclk_core_div = dclk_rate / dclk_core_rate; ++ *if_pixclk_div = dclk_rate / if_pixclk_rate; ++ *if_dclk_div = *if_pixclk_div; ++ } else if (vop2_output_if_is_dp(id)) { ++ if (output_mode == ROCKCHIP_OUT_MODE_YUV420) ++ dclk_out_rate = v_pixclk >> 3; ++ else ++ dclk_out_rate = v_pixclk >> 2; ++ ++ dclk_rate = vop2_calc_dclk(dclk_out_rate, 600000); ++ if (!dclk_rate) { ++ drm_err(vop2->drm, "DP dclk_out_rate out of range(max_dclk: 600 KHZ, dclk_out_rate: %ld KHZ)\n", ++ dclk_out_rate); ++ return -EINVAL; ++ } ++ *dclk_out_div = dclk_rate / dclk_out_rate; ++ *dclk_core_div = dclk_rate / dclk_core_rate; ++ } else if (vop2_output_if_is_mipi(id)) { ++ if_pixclk_rate = dclk_core_rate / K; ++ /* dclk_core = dclk_out * K = if_pixclk * K = v_pixclk / 4 */ ++ dclk_out_rate = if_pixclk_rate; ++ /* dclk_rate = N * dclk_core_rate N = (1,2,4 ), we get a little factor here */ ++ dclk_rate = vop2_calc_dclk(dclk_out_rate, 600000); ++ if (!dclk_rate) { ++ drm_err(vop2->drm, "MIPI dclk out of range(max_dclk: 600 KHZ, dclk_out_rate: %ld KHZ)\n", ++ dclk_out_rate); ++ return -EINVAL; ++ } ++ *dclk_out_div = dclk_rate / dclk_out_rate; ++ *dclk_core_div = dclk_rate / dclk_core_rate; ++ *if_pixclk_div = 1; /*mipi pixclk == dclk_out*/ ++ } else if (vop2_output_if_is_dpi(id)) { ++ dclk_rate = v_pixclk; ++ *dclk_core_div = dclk_rate / dclk_core_rate; ++ } ++ ++ *if_pixclk_div = ilog2(*if_pixclk_div); ++ *if_dclk_div = ilog2(*if_dclk_div); ++ *dclk_core_div = ilog2(*dclk_core_div); ++ *dclk_out_div = ilog2(*dclk_out_div); ++ ++ drm_dbg(vop2->drm, "dclk:%ld,if_pixclk_div;%d,if_dclk_div:%d\n", dclk_rate, *if_pixclk_div, *if_dclk_div); ++ ++ return dclk_rate; ++} ++ ++/* ++ * MIPI port mux on rk3588: ++ * 0: Video Port2 ++ * 1: Video Port3 ++ * 3: Video Port 1(MIPI1 only) ++ */ ++static u32 rk3588_get_mipi_port_mux(int vp_id) ++{ ++ if (vp_id == 1) ++ return 3; ++ else if (vp_id == 3) ++ return 1; ++ else ++ return 0; ++} ++ ++static u32 rk3588_get_hdmi_pol(u32 flags) ++{ ++ u32 val; ++ ++ val = (flags & DRM_MODE_FLAG_NHSYNC) ? BIT(HSYNC_POSITIVE) : 0; ++ val |= (flags & DRM_MODE_FLAG_NVSYNC) ? BIT(VSYNC_POSITIVE) : 0; ++ ++ return val; ++} ++ ++static void rk3588_set_intf_mux(struct vop2_video_port *vp, int id, u32 polflags) ++{ ++ struct vop2 *vop2 = vp->vop2; ++ int dclk_core_div, dclk_out_div, if_pixclk_div, if_dclk_div; ++ u32 die, dip, div, vp_clk_div, val; ++ ++ vop2_calc_cru_cfg(vp, id, &dclk_core_div, &dclk_out_div, &if_pixclk_div, &if_dclk_div); ++ ++ vp_clk_div = FIELD_PREP(RK3588_VP_CLK_CTRL__DCLK_CORE_DIV, dclk_core_div); ++ vp_clk_div |= FIELD_PREP(RK3588_VP_CLK_CTRL__DCLK_OUT_DIV, dclk_out_div); ++ ++ die = vop2_readl(vop2, RK3568_DSP_IF_EN); ++ dip = vop2_readl(vop2, RK3568_DSP_IF_POL); ++ div = vop2_readl(vop2, RK3568_DSP_IF_CTRL); ++ ++ switch (id) { ++ case ROCKCHIP_VOP2_EP_HDMI0: ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div); ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div); ++ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX; ++ die |= RK3588_SYS_DSP_INFACE_EN_HDMI0 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX, vp->id); ++ val = rk3588_get_hdmi_pol(polflags); ++ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 1, 1)); ++ regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0, HIWORD_UPDATE(val, 6, 5)); ++ break; ++ case ROCKCHIP_VOP2_EP_HDMI1: ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV, if_dclk_div); ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV, if_pixclk_div); ++ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX; ++ die |= RK3588_SYS_DSP_INFACE_EN_HDMI1 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX, vp->id); ++ val = rk3588_get_hdmi_pol(polflags); ++ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 4, 4)); ++ regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0, HIWORD_UPDATE(val, 8, 7)); ++ break; ++ case ROCKCHIP_VOP2_EP_EDP0: ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div); ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div); ++ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX; ++ die |= RK3588_SYS_DSP_INFACE_EN_EDP0 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX, vp->id); ++ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 0, 0)); ++ break; ++ case ROCKCHIP_VOP2_EP_EDP1: ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div); ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div); ++ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX; ++ die |= RK3588_SYS_DSP_INFACE_EN_EDP1 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX, vp->id); ++ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 3, 3)); ++ break; ++ case ROCKCHIP_VOP2_EP_MIPI0: ++ div |= FIELD_PREP(RK3588_DSP_IF_MIPI0_PCLK_DIV, if_pixclk_div); ++ die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX; ++ val = rk3588_get_mipi_port_mux(vp->id); ++ die |= RK3588_SYS_DSP_INFACE_EN_MIPI0 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX, !!val); ++ break; ++ case ROCKCHIP_VOP2_EP_MIPI1: ++ div |= FIELD_PREP(RK3588_DSP_IF_MIPI1_PCLK_DIV, if_pixclk_div); ++ die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX; ++ val = rk3588_get_mipi_port_mux(vp->id); ++ die |= RK3588_SYS_DSP_INFACE_EN_MIPI1 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX, val); ++ break; ++ case ROCKCHIP_VOP2_EP_DP0: ++ die &= ~RK3588_SYS_DSP_INFACE_EN_DP0_MUX; ++ die |= RK3588_SYS_DSP_INFACE_EN_DP0 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_DP0_MUX, vp->id); ++ dip &= ~RK3588_DSP_IF_POL__DP0_PIN_POL; ++ dip |= FIELD_PREP(RK3588_DSP_IF_POL__DP0_PIN_POL, polflags); ++ break; ++ case ROCKCHIP_VOP2_EP_DP1: ++ die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX; ++ die |= RK3588_SYS_DSP_INFACE_EN_MIPI1 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id); ++ dip &= ~RK3588_DSP_IF_POL__DP1_PIN_POL; ++ dip |= FIELD_PREP(RK3588_DSP_IF_POL__DP1_PIN_POL, polflags); ++ break; ++ default: ++ drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id); ++ return; ++ } ++ ++ dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD; ++ ++ vop2_vp_write(vp, RK3588_VP_CLK_CTRL, vp_clk_div); ++ vop2_writel(vop2, RK3568_DSP_IF_EN, die); ++ vop2_writel(vop2, RK3568_DSP_IF_CTRL, div); ++ vop2_writel(vop2, RK3568_DSP_IF_POL, dip); ++} ++ ++static void vop2_set_intf_mux(struct vop2_video_port *vp, int ep_id, u32 polflags) ++{ ++ struct vop2 *vop2 = vp->vop2; ++ ++ if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568) { ++ // [CC:] drop 2nd polflags arg ++ rk3568_set_intf_mux(vp, ep_id, polflags, polflags); ++ } else if(vop2->data->soc_id == 3588) { ++ rk3588_set_intf_mux(vp, ep_id, polflags); ++ } ++} ++ + static int us_to_vertical_line(struct drm_display_mode *mode, int us) + { + return us * mode->clock / mode->htotal / 1000; + } + ++// [CC:] rework virtual clock ++static struct vop2_clk *vop2_clk_get(struct vop2 *vop2, const char *name) ++{ ++ struct vop2_clk *clk, *n; ++ ++ if (!name) ++ return NULL; ++ ++ list_for_each_entry_safe(clk, n, &vop2->clk_list_head, list) { ++ if (!strcmp(clk_hw_get_name(&clk->hw), name)) ++ return clk; ++ } ++ ++ return NULL; ++} ++ + static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) + { +@@ -1564,9 +1989,11 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, + u8 out_mode; + u32 dsp_ctrl = 0; + int act_end; +- u32 val, polflags; ++ u32 val, polflags, invpolflags; + int ret; + struct drm_encoder *encoder; ++ char clk_name[32]; ++ struct vop2_clk *dclk; + + drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n", + hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p", +@@ -1597,10 +2024,20 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, + if (mode->flags & DRM_MODE_FLAG_PVSYNC) + polflags |= BIT(VSYNC_POSITIVE); + ++ /* RK3588 uses inverted HDMI V/HSYNC polarity */ ++ if (vop2->data->soc_id == 3588) { ++ invpolflags = 0; ++ if (mode->flags & DRM_MODE_FLAG_NHSYNC) ++ invpolflags |= BIT(HSYNC_POSITIVE); ++ if (mode->flags & DRM_MODE_FLAG_NVSYNC) ++ invpolflags |= BIT(VSYNC_POSITIVE); ++ } else { ++ invpolflags = polflags; ++ } ++ + drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) { + struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); +- +- rk3568_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags); ++ vop2_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags); + } + + if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA && +@@ -1651,13 +2088,44 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, + + vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len); + +- if (mode->flags & DRM_MODE_FLAG_DBLCLK) { +- dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV; +- clock *= 2; ++ if (vop2->data->soc_id == 3568) { ++ if (mode->flags & DRM_MODE_FLAG_DBLCLK) { ++ dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV; ++ // [CC:] done via mode_fixup ++ // clock *= 2; ++ } + } + + vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0); + ++ snprintf(clk_name, sizeof(clk_name), "dclk%d", vp->id); ++ dclk = vop2_clk_get(vop2, clk_name); ++ if (dclk) { ++ /* ++ * use HDMI_PHY_PLL as dclk source under 4K@60 if it is available, ++ * otherwise use system cru as dclk source. ++ */ ++ drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) { ++ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); ++ ++ // [CC:] Using PHY PLL to handle all display modes ++ if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI0) { ++ clk_get_rate(vop2->hdmi0_phy_pll); ++ ++ if (mode->crtc_clock > VOP2_MAX_DCLK_RATE) ++ ret = clk_set_parent(vp->dclk, vp->dclk_parent); ++ else ++ ret = clk_set_parent(vp->dclk, vop2->hdmi0_phy_pll); ++ ++ if (ret < 0) ++ DRM_WARN("failed to set clock parent for %s\n", ++ __clk_get_name(vp->dclk)); ++ ++ clock = dclk->rate; ++ } ++ } ++ } ++ + clk_set_rate(vp->dclk, clock); + + vop2_post_config(crtc); +@@ -1668,9 +2136,71 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, + + drm_crtc_vblank_on(crtc); + ++ // [CC:] needed? ++ ret = reset_control_assert(vp->dclk_rst); ++ if (ret < 0) ++ drm_warn(vop2->drm, "failed to assert reset: %d\n", ret); ++ udelay(10); ++ ret = reset_control_deassert(vp->dclk_rst); ++ if (ret < 0) ++ drm_warn(vop2->drm, "failed to deassert reset: %d\n", ret); ++ + vop2_unlock(vop2); + } + ++static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, ++ struct drm_atomic_state *state) ++{ ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ struct vop2 *vop2 = vp->vop2; ++ struct drm_crtc_state *old_crtc_state; ++ int ret; ++ ++ vop2_lock(vop2); ++ ++ old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); ++ drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); ++ ++ drm_crtc_vblank_off(crtc); ++ ++ /* ++ * Vop standby will take effect at end of current frame, ++ * if dsp hold valid irq happen, it means standby complete. ++ * ++ * we must wait standby complete when we want to disable aclk, ++ * if not, memory bus maybe dead. ++ */ ++ reinit_completion(&vp->dsp_hold_completion); ++ ++ vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID); ++ ++ vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY); ++ ++ ret = wait_for_completion_timeout(&vp->dsp_hold_completion, ++ msecs_to_jiffies(50)); ++ if (!ret) ++ drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id); ++ ++ vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID); ++ ++ clk_disable_unprepare(vp->dclk); ++ ++ vop2->enable_count--; ++ ++ if (!vop2->enable_count) ++ vop2_disable(vop2); ++ ++ vop2_unlock(vop2); ++ ++ if (crtc->state->event && !crtc->state->active) { ++ spin_lock_irq(&crtc->dev->event_lock); ++ drm_crtc_send_vblank_event(crtc, crtc->state->event); ++ spin_unlock_irq(&crtc->dev->event_lock); ++ ++ crtc->state->event = NULL; ++ } ++} ++ + static int vop2_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *state) + { +@@ -2104,7 +2634,43 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, + spin_unlock_irq(&crtc->dev->event_lock); + } + ++static enum drm_mode_status ++vop2_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) ++{ ++ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ struct vop2 *vop2 = vp->vop2; ++ const struct vop2_data *vop2_data = vop2->data; ++ const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; ++ int request_clock = mode->clock; ++ int clock; ++ ++ if (mode->hdisplay > vp_data->max_output.width) ++ return MODE_BAD_HVALUE; ++ ++ if (mode->flags & DRM_MODE_FLAG_DBLCLK) ++ request_clock *= 2; ++ ++ if (request_clock <= VOP2_MAX_DCLK_RATE) { ++ clock = request_clock; ++ } else { ++ request_clock = request_clock >> 2; ++ clock = clk_round_rate(vp->dclk, request_clock * 1000) / 1000; ++ } ++ ++ /* ++ * Hdmi or DisplayPort request a Accurate clock. ++ */ ++ if (vcstate->output_type == DRM_MODE_CONNECTOR_HDMIA || ++ vcstate->output_type == DRM_MODE_CONNECTOR_DisplayPort) ++ if (clock != request_clock) ++ return MODE_CLOCK_RANGE; ++ ++ return MODE_OK; ++} ++ + static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = { ++ .mode_valid = vop2_crtc_mode_valid, + .mode_fixup = vop2_crtc_mode_fixup, + .atomic_check = vop2_crtc_atomic_check, + .atomic_begin = vop2_crtc_atomic_begin, +@@ -2299,7 +2865,7 @@ static int vop2_create_crtcs(struct vop2 *vop2) + for (i = 0; i < vop2_data->nr_vps; i++) { + const struct vop2_video_port_data *vp_data; + struct device_node *np; +- char dclk_name[9]; ++ char clk_name[16]; + + vp_data = &vop2_data->vp[i]; + vp = &vop2->vps[i]; +@@ -2307,13 +2873,26 @@ static int vop2_create_crtcs(struct vop2 *vop2) + vp->id = vp_data->id; + vp->data = vp_data; + +- snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id); +- vp->dclk = devm_clk_get(vop2->dev, dclk_name); ++ snprintf(clk_name, sizeof(clk_name), "dclk_vp%d", vp->id); ++ vp->dclk = devm_clk_get(vop2->dev, clk_name); + if (IS_ERR(vp->dclk)) { +- drm_err(vop2->drm, "failed to get %s\n", dclk_name); ++ drm_err(vop2->drm, "failed to get %s\n", clk_name); + return PTR_ERR(vp->dclk); + } + ++ vp->dclk_rst = devm_reset_control_get_optional(vop2->dev, clk_name); ++ if (IS_ERR(vp->dclk_rst)) { ++ drm_err(vop2->drm, "failed to get dclk reset\n"); ++ return PTR_ERR(vp->dclk_rst); ++ } ++ ++ snprintf(clk_name, sizeof(clk_name), "dclk_src_vp%d", vp->id); ++ vp->dclk_parent = devm_clk_get_optional(vop2->dev, clk_name); ++ if (IS_ERR(vp->dclk_parent)) { ++ drm_err(vop2->drm, "failed to get %s\n", clk_name); ++ return PTR_ERR(vp->dclk_parent); ++ } ++ + np = of_graph_get_remote_node(dev->of_node, i, -1); + if (!np) { + drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i); +@@ -2674,6 +3253,336 @@ static const struct regmap_config vop2_regmap_config = { + .cache_type = REGCACHE_MAPLE, + }; + ++/* ++ * BEGIN virtual clock ++ */ ++#define PLL_RATE_MIN 30000000 ++ ++#define cru_dbg(format, ...) do { \ ++ if (cru_debug) \ ++ pr_info("%s: " format, __func__, ## __VA_ARGS__); \ ++ } while (0) ++ ++#define PNAME(x) static const char *const x[] ++ ++enum vop_clk_branch_type { ++ branch_mux, ++ branch_divider, ++ branch_factor, ++ branch_virtual, ++}; ++ ++#define VIR(cname) \ ++ { \ ++ .branch_type = branch_virtual, \ ++ .name = cname, \ ++ } ++ ++ ++#define MUX(cname, pnames, f) \ ++ { \ ++ .branch_type = branch_mux, \ ++ .name = cname, \ ++ .parent_names = pnames, \ ++ .num_parents = ARRAY_SIZE(pnames), \ ++ .flags = f, \ ++ } ++ ++#define FACTOR(cname, pname, f) \ ++ { \ ++ .branch_type = branch_factor, \ ++ .name = cname, \ ++ .parent_names = (const char *[]){ pname }, \ ++ .num_parents = 1, \ ++ .flags = f, \ ++ } ++ ++#define DIV(cname, pname, f, w) \ ++ { \ ++ .branch_type = branch_divider, \ ++ .name = cname, \ ++ .parent_names = (const char *[]){ pname }, \ ++ .num_parents = 1, \ ++ .flags = f, \ ++ .div_width = w, \ ++ } ++ ++struct vop2_clk_branch { ++ enum vop_clk_branch_type branch_type; ++ const char *name; ++ const char *const *parent_names; ++ u8 num_parents; ++ unsigned long flags; ++ u8 div_shift; ++ u8 div_width; ++ u8 div_flags; ++}; ++ ++PNAME(mux_port0_dclk_src_p) = { "dclk0", "dclk1" }; ++PNAME(mux_port2_dclk_src_p) = { "dclk2", "dclk1" }; ++PNAME(mux_dp_pixclk_p) = { "dclk_out0", "dclk_out1", "dclk_out2" }; ++PNAME(mux_hdmi_edp_clk_src_p) = { "dclk0", "dclk1", "dclk2" }; ++PNAME(mux_mipi_clk_src_p) = { "dclk_out1", "dclk_out2", "dclk_out3" }; ++PNAME(mux_dsc_8k_clk_src_p) = { "dclk0", "dclk1", "dclk2", "dclk3" }; ++PNAME(mux_dsc_4k_clk_src_p) = { "dclk0", "dclk1", "dclk2", "dclk3" }; ++ ++/* ++ * We only use this clk driver calculate the div ++ * of dclk_core/dclk_out/if_pixclk/if_dclk and ++ * the rate of the dclk from the soc. ++ * ++ * We don't touch the cru in the vop here, as ++ * these registers has special read andy write ++ * limits. ++ */ ++static struct vop2_clk_branch rk3588_vop_clk_branches[] = { ++ VIR("dclk0"), ++ VIR("dclk1"), ++ VIR("dclk2"), ++ VIR("dclk3"), ++ ++ MUX("port0_dclk_src", mux_port0_dclk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("dclk_core0", "port0_dclk_src", CLK_SET_RATE_PARENT, 2), ++ DIV("dclk_out0", "port0_dclk_src", CLK_SET_RATE_PARENT, 2), ++ ++ FACTOR("port1_dclk_src", "dclk1", CLK_SET_RATE_PARENT), ++ DIV("dclk_core1", "port1_dclk_src", CLK_SET_RATE_PARENT, 2), ++ DIV("dclk_out1", "port1_dclk_src", CLK_SET_RATE_PARENT, 2), ++ ++ MUX("port2_dclk_src", mux_port2_dclk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("dclk_core2", "port2_dclk_src", CLK_SET_RATE_PARENT, 2), ++ DIV("dclk_out2", "port2_dclk_src", CLK_SET_RATE_PARENT, 2), ++ ++ FACTOR("port3_dclk_src", "dclk3", CLK_SET_RATE_PARENT), ++ DIV("dclk_core3", "port3_dclk_src", CLK_SET_RATE_PARENT, 2), ++ DIV("dclk_out3", "port3_dclk_src", CLK_SET_RATE_PARENT, 2), ++ ++ MUX("dp0_pixclk", mux_dp_pixclk_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ MUX("dp1_pixclk", mux_dp_pixclk_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ ++ MUX("hdmi_edp0_clk_src", mux_hdmi_edp_clk_src_p, ++ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("hdmi_edp0_dclk", "hdmi_edp0_clk_src", 0, 2), ++ DIV("hdmi_edp0_pixclk", "hdmi_edp0_clk_src", CLK_SET_RATE_PARENT, 1), ++ ++ MUX("hdmi_edp1_clk_src", mux_hdmi_edp_clk_src_p, ++ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("hdmi_edp1_dclk", "hdmi_edp1_clk_src", 0, 2), ++ DIV("hdmi_edp1_pixclk", "hdmi_edp1_clk_src", CLK_SET_RATE_PARENT, 1), ++ ++ MUX("mipi0_clk_src", mux_mipi_clk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("mipi0_pixclk", "mipi0_clk_src", CLK_SET_RATE_PARENT, 2), ++ ++ MUX("mipi1_clk_src", mux_mipi_clk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("mipi1_pixclk", "mipi1_clk_src", CLK_SET_RATE_PARENT, 2), ++ ++ FACTOR("rgb_pixclk", "port3_dclk_src", CLK_SET_RATE_PARENT), ++ ++ MUX("dsc_8k_txp_clk_src", mux_dsc_8k_clk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("dsc_8k_txp_clk", "dsc_8k_txp_clk_src", 0, 2), ++ DIV("dsc_8k_pxl_clk", "dsc_8k_txp_clk_src", 0, 2), ++ DIV("dsc_8k_cds_clk", "dsc_8k_txp_clk_src", 0, 2), ++ ++ MUX("dsc_4k_txp_clk_src", mux_dsc_4k_clk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("dsc_4k_txp_clk", "dsc_4k_txp_clk_src", 0, 2), ++ DIV("dsc_4k_pxl_clk", "dsc_4k_txp_clk_src", 0, 2), ++ DIV("dsc_4k_cds_clk", "dsc_4k_txp_clk_src", 0, 2), ++}; ++ ++static unsigned long clk_virtual_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct vop2_clk *vop2_clk = to_vop2_clk(hw); ++ ++ return (unsigned long)vop2_clk->rate; ++} ++ ++static long clk_virtual_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *prate) ++{ ++ struct vop2_clk *vop2_clk = to_vop2_clk(hw); ++ ++ vop2_clk->rate = rate; ++ ++ return rate; ++} ++ ++static int clk_virtual_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ return 0; ++} ++ ++const struct clk_ops clk_virtual_ops = { ++ .round_rate = clk_virtual_round_rate, ++ .set_rate = clk_virtual_set_rate, ++ .recalc_rate = clk_virtual_recalc_rate, ++}; ++ ++static u8 vop2_mux_get_parent(struct clk_hw *hw) ++{ ++ struct vop2_clk *vop2_clk = to_vop2_clk(hw); ++ ++ // cru_dbg("%s index: %d\n", clk_hw_get_name(hw), vop2_clk->parent_index); ++ return vop2_clk->parent_index; ++} ++ ++static int vop2_mux_set_parent(struct clk_hw *hw, u8 index) ++{ ++ struct vop2_clk *vop2_clk = to_vop2_clk(hw); ++ ++ vop2_clk->parent_index = index; ++ ++ // cru_dbg("%s index: %d\n", clk_hw_get_name(hw), index); ++ return 0; ++} ++ ++static int vop2_clk_mux_determine_rate(struct clk_hw *hw, ++ struct clk_rate_request *req) ++{ ++ // cru_dbg("%s %ld(min: %ld max: %ld)\n", ++ // clk_hw_get_name(hw), req->rate, req->min_rate, req->max_rate); ++ return __clk_mux_determine_rate(hw, req); ++} ++ ++static const struct clk_ops vop2_mux_clk_ops = { ++ .get_parent = vop2_mux_get_parent, ++ .set_parent = vop2_mux_set_parent, ++ .determine_rate = vop2_clk_mux_determine_rate, ++}; ++ ++#define div_mask(width) ((1 << (width)) - 1) ++ ++static int vop2_div_get_val(unsigned long rate, unsigned long parent_rate) ++{ ++ unsigned int div, value; ++ ++ div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); ++ ++ value = ilog2(div); ++ ++ return value; ++} ++ ++static unsigned long vop2_clk_div_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct vop2_clk *vop2_clk = to_vop2_clk(hw); ++ unsigned long rate; ++ unsigned int div; ++ ++ div = 1 << vop2_clk->div_val; ++ rate = parent_rate / div; ++ ++ // cru_dbg("%s rate: %ld(prate: %ld)\n", clk_hw_get_name(hw), rate, parent_rate); ++ return rate; ++} ++ ++static long vop2_clk_div_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *prate) ++{ ++ struct vop2_clk *vop2_clk = to_vop2_clk(hw); ++ ++ if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { ++ if (*prate < rate) ++ *prate = rate; ++ if ((*prate >> vop2_clk->div.width) > rate) ++ *prate = rate; ++ ++ if ((*prate % rate)) ++ *prate = rate; ++ ++ /* SOC PLL can't output a too low pll freq */ ++ if (*prate < PLL_RATE_MIN) ++ *prate = rate << vop2_clk->div.width; ++ } ++ ++ // cru_dbg("%s rate: %ld(prate: %ld)\n", clk_hw_get_name(hw), rate, *prate); ++ return rate; ++} ++ ++static int vop2_clk_div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) ++{ ++ struct vop2_clk *vop2_clk = to_vop2_clk(hw); ++ int div_val; ++ ++ div_val = vop2_div_get_val(rate, parent_rate); ++ vop2_clk->div_val = div_val; ++ ++ // cru_dbg("%s prate: %ld rate: %ld div_val: %d\n", ++ // clk_hw_get_name(hw), parent_rate, rate, div_val); ++ return 0; ++} ++ ++static const struct clk_ops vop2_div_clk_ops = { ++ .recalc_rate = vop2_clk_div_recalc_rate, ++ .round_rate = vop2_clk_div_round_rate, ++ .set_rate = vop2_clk_div_set_rate, ++}; ++ ++static struct clk *vop2_clk_register(struct vop2 *vop2, struct vop2_clk_branch *branch) ++{ ++ struct clk_init_data init = {}; ++ struct vop2_clk *vop2_clk; ++ struct clk *clk; ++ ++ vop2_clk = devm_kzalloc(vop2->dev, sizeof(*vop2_clk), GFP_KERNEL); ++ if (!vop2_clk) ++ return ERR_PTR(-ENOMEM); ++ ++ vop2_clk->vop2 = vop2; ++ vop2_clk->hw.init = &init; ++ vop2_clk->div.shift = branch->div_shift; ++ vop2_clk->div.width = branch->div_width; ++ ++ init.name = branch->name; ++ init.flags = branch->flags; ++ init.num_parents = branch->num_parents; ++ init.parent_names = branch->parent_names; ++ if (branch->branch_type == branch_divider) { ++ init.ops = &vop2_div_clk_ops; ++ } else if (branch->branch_type == branch_virtual) { ++ init.ops = &clk_virtual_ops; ++ init.num_parents = 0; ++ init.parent_names = NULL; ++ } else { ++ init.ops = &vop2_mux_clk_ops; ++ } ++ ++ clk = devm_clk_register(vop2->dev, &vop2_clk->hw); ++ if (!IS_ERR(clk)) ++ list_add_tail(&vop2_clk->list, &vop2->clk_list_head); ++ else ++ DRM_DEV_ERROR(vop2->dev, "Register %s failed\n", branch->name); ++ ++ return clk; ++} ++ ++static int vop2_clk_init(struct vop2 *vop2) ++{ ++ struct vop2_clk_branch *branch = rk3588_vop_clk_branches; ++ unsigned int nr_clk = ARRAY_SIZE(rk3588_vop_clk_branches); ++ unsigned int idx; ++ struct vop2_clk *clk, *n; ++ ++ INIT_LIST_HEAD(&vop2->clk_list_head); ++ ++ if (vop2->data->soc_id < 3588) ++ return 0; ++ ++ list_for_each_entry_safe(clk, n, &vop2->clk_list_head, list) { ++ list_del(&clk->list); ++ } ++ ++ for (idx = 0; idx < nr_clk; idx++, branch++) ++ vop2_clk_register(vop2, branch); ++ ++ return 0; ++} ++/* ++ * END virtual clock ++ */ ++ + static int vop2_bind(struct device *dev, struct device *master, void *data) + { + struct platform_device *pdev = to_platform_device(dev); +@@ -2726,7 +3635,12 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) + return PTR_ERR(vop2->lut_regs); + } + ++ // [CC:] grf -> sys_grf in downstream + vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); ++ // [CC:] vop_grf -> grf in downstream ++ // [CC:] make use of new grf's ++ vop2->vop_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vop-grf"); ++ vop2->vo1_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vo1-grf"); + + vop2->hclk = devm_clk_get(vop2->dev, "hclk"); + if (IS_ERR(vop2->hclk)) { +@@ -2740,6 +3654,31 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) + return PTR_ERR(vop2->aclk); + } + ++ vop2->pclk = devm_clk_get_optional(vop2->dev, "pclk"); ++ if (IS_ERR(vop2->pclk)) { ++ DRM_DEV_ERROR(vop2->dev, "failed to get pclk source\n"); ++ return PTR_ERR(vop2->pclk); ++ } ++ ++ vop2->hdmi0_phy_pll = devm_clk_get_optional(vop2->drm->dev, "hdmi0_phy_pll"); ++ if (IS_ERR(vop2->hdmi0_phy_pll)) { ++ DRM_DEV_ERROR(vop2->dev, "failed to get hdmi0_phy_pll source\n"); ++ return PTR_ERR(vop2->hdmi0_phy_pll); ++ } ++ ++ // [CC:] drop ahb_rst & axi_rst ++ vop2->ahb_rst = devm_reset_control_get_optional(vop2->dev, "ahb"); ++ if (IS_ERR(vop2->ahb_rst)) { ++ DRM_DEV_ERROR(vop2->dev, "failed to get ahb reset\n"); ++ return PTR_ERR(vop2->ahb_rst); ++ } ++ ++ vop2->axi_rst = devm_reset_control_get_optional(vop2->dev, "axi"); ++ if (IS_ERR(vop2->axi_rst)) { ++ DRM_DEV_ERROR(vop2->dev, "failed to get axi reset\n"); ++ return PTR_ERR(vop2->axi_rst); ++ } ++ + vop2->irq = platform_get_irq(pdev, 0); + if (vop2->irq < 0) { + drm_err(vop2->drm, "cannot find irq for vop2\n"); +@@ -2756,6 +3695,9 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) + if (ret) + return ret; + ++ // [CC:] rework virtual clock ++ vop2_clk_init(vop2); ++ + ret = vop2_find_rgb_encoder(vop2); + if (ret >= 0) { + vop2->rgb = rockchip_rgb_init(dev, &vop2->vps[ret].crtc, +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h +index 56fd31e05..cae71df81 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h +@@ -12,10 +12,18 @@ + #include + #include + +-#define VOP_FEATURE_OUTPUT_10BIT BIT(0) ++/* a feature to splice two windows and two vps to support resolution > 4096 */ ++#define VOP_FEATURE_SPLICE BIT(5) ++//[CC:] dupplicate of #define VOP_FEATURE_OUTPUT_RGB10 BIT(0) in vop.h ++#define VOP_FEATURE_OUTPUT_10BIT BIT(0) + ++//[CC:] downstream uses #define WIN_FEATURE_AFBDC BIT(3) + #define WIN_FEATURE_AFBDC BIT(0) + #define WIN_FEATURE_CLUSTER BIT(1) ++/* Left win in splice mode */ ++#define WIN_FEATURE_SPLICE_LEFT BIT(6) ++ ++#define HIWORD_UPDATE(v, h, l) ((GENMASK(h, l) << 16) | ((v) << (l))) + + /* + * the delay number of a window in different mode. +@@ -39,6 +47,18 @@ enum vop2_scale_down_mode { + VOP2_SCALE_DOWN_AVG, + }; + ++/* ++ * vop2 internal power domain id, ++ * should be all none zero, 0 will be treat as invalid; ++ */ ++#define VOP2_PD_CLUSTER0 BIT(0) ++#define VOP2_PD_CLUSTER1 BIT(1) ++#define VOP2_PD_CLUSTER2 BIT(2) ++#define VOP2_PD_CLUSTER3 BIT(3) ++#define VOP2_PD_DSC_8K BIT(5) ++#define VOP2_PD_DSC_4K BIT(6) ++#define VOP2_PD_ESMART BIT(7) ++ + enum vop2_win_regs { + VOP2_WIN_ENABLE, + VOP2_WIN_FORMAT, +@@ -107,6 +127,7 @@ enum vop2_win_regs { + struct vop2_win_data { + const char *name; + unsigned int phys_id; ++ uint8_t splice_win_id; + + u32 base; + enum drm_plane_type type; +@@ -129,9 +150,11 @@ struct vop2_win_data { + + struct vop2_video_port_data { + unsigned int id; ++ uint8_t splice_vp_id; + u32 feature; + u16 gamma_lut_len; + u16 cubic_lut_len; ++ unsigned long dclk_max; + struct vop_rect max_output; + const u8 pre_scan_max_dly[4]; + unsigned int offset; +@@ -145,7 +168,13 @@ struct vop2_data { + struct vop_rect max_output; + + unsigned int win_size; ++ // [CC:] convert to an enum as it is used in conditional statements + unsigned int soc_id; ++ ++ uint8_t nr_dscs; ++ uint8_t nr_conns; ++ const struct vop2_dsc_data *dsc; ++ const struct vop2_connector_if_data *conn; + }; + + /* interrupt define */ +@@ -450,6 +479,45 @@ enum dst_factor_mode { + + #define POLFLAG_DCLK_INV BIT(3) + ++/* RK3588 registers */ ++#define RK3588_GRF_SOC_CON1 0x0304 ++#define RK3588_GRF_VOP_CON2 0x08 ++#define RK3588_GRF_VO1_CON0 0x00 ++ ++#define RK3588_GRF_VO1_CON0__HDMI0_PIN_POL GENMASK(6, 5) ++ ++#define RK3588_SYS_PD_CTRL 0x034 ++#define RK3588_VP_CLK_CTRL 0x0c ++ ++#define RK3588_VP_CLK_CTRL__DCLK_OUT_DIV GENMASK(3, 2) ++#define RK3588_VP_CLK_CTRL__DCLK_CORE_DIV GENMASK(1, 0) ++ ++#define RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX GENMASK(22, 21) ++#define RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX GENMASK(20, 20) ++#define RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX GENMASK(19, 18) ++#define RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX GENMASK(17, 16) ++#define RK3588_SYS_DSP_INFACE_EN_DP1_MUX GENMASK(15, 14) ++#define RK3588_SYS_DSP_INFACE_EN_DP0_MUX GENMASK(13, 12) ++#define RK3588_SYS_DSP_INFACE_EN_DPI GENMASK(9, 8) ++#define RK3588_SYS_DSP_INFACE_EN_MIPI1 BIT(7) ++#define RK3588_SYS_DSP_INFACE_EN_MIPI0 BIT(6) ++#define RK3588_SYS_DSP_INFACE_EN_HDMI1 BIT(5) ++#define RK3588_SYS_DSP_INFACE_EN_EDP1 BIT(4) ++#define RK3588_SYS_DSP_INFACE_EN_HDMI0 BIT(3) ++#define RK3588_SYS_DSP_INFACE_EN_EDP0 BIT(2) ++#define RK3588_SYS_DSP_INFACE_EN_DP1 BIT(1) ++#define RK3588_SYS_DSP_INFACE_EN_DP0 BIT(0) ++ ++#define RK3588_DSP_IF_MIPI1_PCLK_DIV GENMASK(27, 26) ++#define RK3588_DSP_IF_MIPI0_PCLK_DIV GENMASK(25, 24) ++#define RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV GENMASK(22, 22) ++#define RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV GENMASK(21, 20) ++#define RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV GENMASK(18, 18) ++#define RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV GENMASK(17, 16) ++ ++#define RK3588_DSP_IF_POL__DP1_PIN_POL GENMASK(14, 12) ++#define RK3588_DSP_IF_POL__DP0_PIN_POL GENMASK(10, 8) ++ + enum vop2_layer_phy_id { + ROCKCHIP_VOP2_CLUSTER0 = 0, + ROCKCHIP_VOP2_CLUSTER1, +diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +index 22288ad7f..2bc1baf5c 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c ++++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +@@ -131,6 +131,49 @@ static const struct vop2_video_port_data rk3568_vop_video_ports[] = { + }, + }; + ++static const struct vop2_video_port_data rk3588_vop_video_ports[] = { ++ { ++ .id = 0, ++ // .splice_vp_id = 1, ++ // .lut_dma_rid = 1, ++ .feature = VOP_FEATURE_OUTPUT_10BIT, ++ .gamma_lut_len = 1024, ++ .cubic_lut_len = 9 * 9 * 9, ++ // .dclk_max = 600000000, ++ .max_output = { 7680, 4320 }, ++ .pre_scan_max_dly = { 76, 65, 65, 54 }, ++ .offset = 0xc00, ++ }, { ++ .id = 1, ++ // .lut_dma_rid = 14, ++ .feature = VOP_FEATURE_OUTPUT_10BIT, ++ .gamma_lut_len = 1024, ++ .cubic_lut_len = 9 * 9 * 9, ++ // .dclk_max = 600000000, ++ .max_output = { 4096, 2304 }, ++ .pre_scan_max_dly = { 76, 65, 65, 54 }, ++ .offset = 0xd00, ++ }, { ++ .id = 2, ++ // .lut_dma_rid = 14, ++ .feature = VOP_FEATURE_OUTPUT_10BIT, ++ .gamma_lut_len = 1024, ++ .cubic_lut_len = 17 * 17 * 17, ++ // .dclk_max = 600000000, ++ .max_output = { 4096, 2304 }, ++ .pre_scan_max_dly = { 52, 52, 52, 52 }, ++ .offset = 0xe00, ++ }, { ++ .id = 3, ++ // .lut_dma_rid = 14, ++ .gamma_lut_len = 1024, ++ // .dclk_max = 200000000, ++ .max_output = { 2048, 1536 }, ++ .pre_scan_max_dly = { 52, 52, 52, 52 }, ++ .offset = 0xf00, ++ }, ++}; ++ + /* + * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win. + * Every cluster can work as 4K win or split into two win. +@@ -234,6 +277,174 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { + }, + }; + ++/* ++ * rk3588 vop with 4 cluster, 4 esmart win. ++ * Every cluster can work as 4K win or split into two win. ++ * All win in cluster support AFBCD. ++ * ++ * Every esmart win and smart win support 4 Multi-region. ++ * ++ * Scale filter mode: ++ * ++ * * Cluster: bicubic for horizontal scale up, others use bilinear ++ * * ESmart: ++ * * nearest-neighbor/bilinear/bicubic for scale up ++ * * nearest-neighbor/bilinear/average for scale down ++ * ++ * AXI Read ID assignment: ++ * Two AXI bus: ++ * AXI0 is a read/write bus with a higher performance. ++ * AXI1 is a read only bus. ++ * ++ * Every window on a AXI bus must assigned two unique ++ * read id(yrgb_id/uv_id, valid id are 0x1~0xe). ++ * ++ * AXI0: ++ * Cluster0/1, Esmart0/1, WriteBack ++ * ++ * AXI 1: ++ * Cluster2/3, Esmart2/3 ++ */ ++static const struct vop2_win_data rk3588_vop_win_data[] = { ++ { ++ .name = "Cluster0-win0", ++ .phys_id = ROCKCHIP_VOP2_CLUSTER0, ++ //[CC:] .splice_win_id = ROCKCHIP_VOP2_CLUSTER1, ++ .base = 0x1000, ++ .formats = formats_cluster, ++ .nformats = ARRAY_SIZE(formats_cluster), ++ .format_modifiers = format_modifiers_afbc, ++ .layer_sel_id = 0, ++ .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | ++ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_OVERLAY, ++ // .pd_id = VOP2_PD_CLUSTER0, ++ // .axi_id = 0, ++ // .axi_yrgb_id = 2, ++ // .axi_uv_id = 3, ++ .max_upscale_factor = 4, ++ .max_downscale_factor = 4, ++ .dly = { 4, 26, 29 }, ++ //[CC:] WIN_FEATURE_SPLICE_LEFT ++ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER, ++ }, { ++ .name = "Cluster1-win0", ++ .phys_id = ROCKCHIP_VOP2_CLUSTER1, ++ .base = 0x1200, ++ .formats = formats_cluster, ++ .nformats = ARRAY_SIZE(formats_cluster), ++ .format_modifiers = format_modifiers_afbc, ++ .layer_sel_id = 1, ++ .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | ++ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_OVERLAY, ++ // .pd_id = VOP2_PD_CLUSTER1, ++ // .axi_id = 0, ++ // .axi_yrgb_id = 6, ++ // .axi_uv_id = 7, ++ .max_upscale_factor = 4, ++ .max_downscale_factor = 4, ++ .dly = { 4, 26, 29 }, ++ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER, ++ }, { ++ .name = "Cluster2-win0", ++ .phys_id = ROCKCHIP_VOP2_CLUSTER2, ++ // [CC:] .splice_win_id = ROCKCHIP_VOP2_CLUSTER3, ++ .base = 0x1400, ++ .formats = formats_cluster, ++ .nformats = ARRAY_SIZE(formats_cluster), ++ .format_modifiers = format_modifiers_afbc, ++ .layer_sel_id = 4, ++ .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | ++ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_OVERLAY, ++ // .pd_id = VOP2_PD_CLUSTER2, ++ // .axi_id = 1, ++ // .axi_yrgb_id = 2, ++ // .axi_uv_id = 3, ++ .max_upscale_factor = 4, ++ .max_downscale_factor = 4, ++ .dly = { 4, 26, 29 }, ++ //[CC:] WIN_FEATURE_SPLICE_LEFT ++ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER, ++ }, { ++ .name = "Cluster3-win0", ++ .phys_id = ROCKCHIP_VOP2_CLUSTER3, ++ .base = 0x1600, ++ .formats = formats_cluster, ++ .nformats = ARRAY_SIZE(formats_cluster), ++ .format_modifiers = format_modifiers_afbc, ++ .layer_sel_id = 5, ++ .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | ++ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_OVERLAY, ++ // .pd_id = VOP2_PD_CLUSTER3, ++ // .axi_id = 1, ++ // .axi_yrgb_id = 6, ++ // .axi_uv_id = 7, ++ .max_upscale_factor = 4, ++ .max_downscale_factor = 4, ++ .dly = { 4, 26, 29 }, ++ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER, ++ }, { ++ .name = "Esmart0-win0", ++ .phys_id = ROCKCHIP_VOP2_ESMART0, ++ // [CC:] .splice_win_id = ROCKCHIP_VOP2_ESMART1, ++ .formats = formats_rk356x_esmart, ++ .nformats = ARRAY_SIZE(formats_rk356x_esmart), ++ .format_modifiers = format_modifiers, ++ .base = 0x1800, ++ .layer_sel_id = 2, ++ .supported_rotations = DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_PRIMARY, ++ // [CC:] .pd_id missing in downstream code ++ // .axi_id = 0, ++ // .axi_yrgb_id = 0x0a, ++ // .axi_uv_id = 0x0b, ++ .max_upscale_factor = 8, ++ .max_downscale_factor = 8, ++ .dly = { 23, 45, 48 }, ++ // .feature = WIN_FEATURE_SPLICE_LEFT | WIN_FEATURE_MULTI_AREA, ++ }, { ++ .name = "Esmart2-win0", ++ .phys_id = ROCKCHIP_VOP2_ESMART2, ++ // [CC:] .splice_win_id = ROCKCHIP_VOP2_ESMART3, ++ .formats = formats_rk356x_esmart, ++ .nformats = ARRAY_SIZE(formats_rk356x_esmart), ++ .format_modifiers = format_modifiers, ++ .base = 0x1c00, ++ .layer_sel_id = 6, ++ .supported_rotations = DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_PRIMARY, ++ // .pd_id = VOP2_PD_ESMART, ++ // .axi_id = 1, ++ // .axi_yrgb_id = 0x0a, ++ // .axi_uv_id = 0x0b, ++ .max_upscale_factor = 8, ++ .max_downscale_factor = 8, ++ .dly = { 23, 45, 48 }, ++ // .feature = WIN_FEATURE_SPLICE_LEFT | WIN_FEATURE_MULTI_AREA, ++ }, { ++ .name = "Esmart1-win0", ++ .phys_id = ROCKCHIP_VOP2_ESMART1, ++ .formats = formats_rk356x_esmart, ++ .nformats = ARRAY_SIZE(formats_rk356x_esmart), ++ .format_modifiers = format_modifiers, ++ .base = 0x1a00, ++ .layer_sel_id = 3, ++ .supported_rotations = DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_PRIMARY, ++ // .pd_id = VOP2_PD_ESMART, ++ // .axi_id = 0, ++ // .axi_yrgb_id = 0x0c, ++ // .axi_uv_id = 0x0d, ++ .max_upscale_factor = 8, ++ .max_downscale_factor = 8, ++ .dly = { 23, 45, 48 }, ++ // .feature = WIN_FEATURE_MULTI_AREA, ++ }, ++}; ++ + static const struct vop2_data rk3566_vop = { + .nr_vps = 3, + .max_input = { 4096, 2304 }, +@@ -246,14 +457,27 @@ static const struct vop2_data rk3566_vop = { + + static const struct vop2_data rk3568_vop = { + .nr_vps = 3, +- .max_input = { 4096, 2304 }, +- .max_output = { 4096, 2304 }, ++ .max_input = { 4096, 4320 }, ++ .max_output = { 4096, 4320 }, + .vp = rk3568_vop_video_ports, + .win = rk3568_vop_win_data, + .win_size = ARRAY_SIZE(rk3568_vop_win_data), + .soc_id = 3568, + }; + ++static const struct vop2_data rk3588_vop = { ++ // .feature = VOP_FEATURE_SPLICE, ++ .nr_vps = 4, ++ .max_input = { 4096, 2304 }, ++ .max_output = { 4096, 2304 }, ++ .vp = rk3588_vop_video_ports, ++ .win = rk3588_vop_win_data, ++ .win_size = ARRAY_SIZE(rk3588_vop_win_data), ++ // .conn = rk3588_conn_if_data, ++ // .nr_conns = ARRAY_SIZE(rk3588_conn_if_data), ++ .soc_id = 3588, ++}; ++ + static const struct of_device_id vop2_dt_match[] = { + { + .compatible = "rockchip,rk3566-vop", +@@ -261,6 +485,9 @@ static const struct of_device_id vop2_dt_match[] = { + }, { + .compatible = "rockchip,rk3568-vop", + .data = &rk3568_vop, ++ }, { ++ .compatible = "rockchip,rk3588-vop", ++ .data = &rk3588_vop, + }, { + }, + }; +diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig +index 94360fc96..62c18e25b 100644 +--- a/drivers/phy/rockchip/Kconfig ++++ b/drivers/phy/rockchip/Kconfig +@@ -83,6 +83,13 @@ config PHY_ROCKCHIP_PCIE + help + Enable this to support the Rockchip PCIe PHY. + ++config PHY_ROCKCHIP_SAMSUNG_HDPTX_HDMI ++ tristate "Rockchip Samsung HDMI/DP Combo PHY HDMI driver" ++ depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST) ++ select GENERIC_PHY ++ help ++ Support for Rockchip HDMI/DP Combo PHY with Samsung IP block. ++ + config PHY_ROCKCHIP_SNPS_PCIE3 + tristate "Rockchip Snps PCIe3 PHY Driver" + depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST +@@ -107,3 +114,15 @@ config PHY_ROCKCHIP_USB + select GENERIC_PHY + help + Enable this to support the Rockchip USB 2.0 PHY. ++ ++config PHY_ROCKCHIP_USBDP ++ tristate "Rockchip USBDP COMBO PHY Driver" ++ depends on ARCH_ROCKCHIP && OF ++ select GENERIC_PHY ++ select TYPEC ++ help ++ Enable this to support the Rockchip USB3.0/DP combo PHY with ++ Samsung IP block. This is required for USB3 support on RK3588. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called phy-rockchip-usbdp +diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile +index 7eab12923..d266414a1 100644 +--- a/drivers/phy/rockchip/Makefile ++++ b/drivers/phy/rockchip/Makefile +@@ -8,6 +8,8 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o + obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o + obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o + obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o ++obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX_HDMI) += phy-rockchip-samsung-hdptx-hdmi.o + obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o + obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o + obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o ++obj-$(CONFIG_PHY_ROCKCHIP_USBDP) += phy-rockchip-usbdp.o +diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c +new file mode 100644 +index 000000000..036db0877 +--- /dev/null ++++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c +@@ -0,0 +1,2347 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) Rockchip Electronics Co.Ltd ++ * Author: ++ * Algea Cao ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l))) ++ ++#define GRF_HDPTX_CON0 0x00 ++#define HDPTX_I_PLL_EN BIT(7) ++#define HDPTX_I_BIAS_EN BIT(6) ++#define HDPTX_I_BGR_EN BIT(5) ++#define GRF_HDPTX_STATUS 0x80 ++#define HDPTX_O_PLL_LOCK_DONE BIT(3) ++#define HDPTX_O_PHY_CLK_RDY BIT(2) ++#define HDPTX_O_PHY_RDY BIT(1) ++#define HDPTX_O_SB_RDY BIT(0) ++ ++#define CMN_REG0000 0x0000 ++#define CMN_REG0001 0x0004 ++#define CMN_REG0002 0x0008 ++#define CMN_REG0003 0x000C ++#define CMN_REG0004 0x0010 ++#define CMN_REG0005 0x0014 ++#define CMN_REG0006 0x0018 ++#define CMN_REG0007 0x001C ++#define CMN_REG0008 0x0020 ++#define LCPLL_EN_MASK BIT(6) ++#define LCPLL_EN(x) UPDATE(x, 4, 4) ++#define LCPLL_LCVCO_MODE_EN_MASK BIT(4) ++#define LCPLL_LCVCO_MODE_EN(x) UPDATE(x, 4, 4) ++#define CMN_REG0009 0x0024 ++#define CMN_REG000A 0x0028 ++#define CMN_REG000B 0x002C ++#define CMN_REG000C 0x0030 ++#define CMN_REG000D 0x0034 ++#define CMN_REG000E 0x0038 ++#define CMN_REG000F 0x003C ++#define CMN_REG0010 0x0040 ++#define CMN_REG0011 0x0044 ++#define CMN_REG0012 0x0048 ++#define CMN_REG0013 0x004C ++#define CMN_REG0014 0x0050 ++#define CMN_REG0015 0x0054 ++#define CMN_REG0016 0x0058 ++#define CMN_REG0017 0x005C ++#define CMN_REG0018 0x0060 ++#define CMN_REG0019 0x0064 ++#define CMN_REG001A 0x0068 ++#define CMN_REG001B 0x006C ++#define CMN_REG001C 0x0070 ++#define CMN_REG001D 0x0074 ++#define CMN_REG001E 0x0078 ++#define LCPLL_PI_EN_MASK BIT(5) ++#define LCPLL_PI_EN(x) UPDATE(x, 5, 5) ++#define LCPLL_100M_CLK_EN_MASK BIT(0) ++#define LCPLL_100M_CLK_EN(x) UPDATE(x, 0, 0) ++#define CMN_REG001F 0x007C ++#define CMN_REG0020 0x0080 ++#define CMN_REG0021 0x0084 ++#define CMN_REG0022 0x0088 ++#define CMN_REG0023 0x008C ++#define CMN_REG0024 0x0090 ++#define CMN_REG0025 0x0094 ++#define LCPLL_PMS_IQDIV_RSTN BIT(4) ++#define CMN_REG0026 0x0098 ++#define CMN_REG0027 0x009C ++#define CMN_REG0028 0x00A0 ++#define LCPLL_SDC_FRAC_EN BIT(2) ++#define LCPLL_SDC_FRAC_RSTN BIT(0) ++#define CMN_REG0029 0x00A4 ++#define CMN_REG002A 0x00A8 ++#define CMN_REG002B 0x00AC ++#define CMN_REG002C 0x00B0 ++#define CMN_REG002D 0x00B4 ++#define LCPLL_SDC_N_MASK GENMASK(3, 1) ++#define LCPLL_SDC_N(x) UPDATE(x, 3, 1) ++#define CMN_REG002E 0x00B8 ++#define LCPLL_SDC_NUMBERATOR_MASK GENMASK(5, 0) ++#define LCPLL_SDC_NUMBERATOR(x) UPDATE(x, 5, 0) ++#define CMN_REG002F 0x00BC ++#define LCPLL_SDC_DENOMINATOR_MASK GENMASK(7, 2) ++#define LCPLL_SDC_DENOMINATOR(x) UPDATE(x, 7, 2) ++#define LCPLL_SDC_NDIV_RSTN BIT(0) ++#define CMN_REG0030 0x00C0 ++#define CMN_REG0031 0x00C4 ++#define CMN_REG0032 0x00C8 ++#define CMN_REG0033 0x00CC ++#define CMN_REG0034 0x00D0 ++#define CMN_REG0035 0x00D4 ++#define CMN_REG0036 0x00D8 ++#define CMN_REG0037 0x00DC ++#define CMN_REG0038 0x00E0 ++#define CMN_REG0039 0x00E4 ++#define CMN_REG003A 0x00E8 ++#define CMN_REG003B 0x00EC ++#define CMN_REG003C 0x00F0 ++#define CMN_REG003D 0x00F4 ++#define ROPLL_LCVCO_EN BIT(4) ++#define CMN_REG003E 0x00F8 ++#define CMN_REG003F 0x00FC ++#define CMN_REG0040 0x0100 ++#define CMN_REG0041 0x0104 ++#define CMN_REG0042 0x0108 ++#define CMN_REG0043 0x010C ++#define CMN_REG0044 0x0110 ++#define CMN_REG0045 0x0114 ++#define CMN_REG0046 0x0118 ++#define CMN_REG0047 0x011C ++#define CMN_REG0048 0x0120 ++#define CMN_REG0049 0x0124 ++#define CMN_REG004A 0x0128 ++#define CMN_REG004B 0x012C ++#define CMN_REG004C 0x0130 ++#define CMN_REG004D 0x0134 ++#define CMN_REG004E 0x0138 ++#define ROPLL_PI_EN BIT(5) ++#define CMN_REG004F 0x013C ++#define CMN_REG0050 0x0140 ++#define CMN_REG0051 0x0144 ++#define CMN_REG0052 0x0148 ++#define CMN_REG0053 0x014C ++#define CMN_REG0054 0x0150 ++#define CMN_REG0055 0x0154 ++#define CMN_REG0056 0x0158 ++#define CMN_REG0057 0x015C ++#define CMN_REG0058 0x0160 ++#define CMN_REG0059 0x0164 ++#define CMN_REG005A 0x0168 ++#define CMN_REG005B 0x016C ++#define CMN_REG005C 0x0170 ++#define ROPLL_PMS_IQDIV_RSTN BIT(5) ++#define CMN_REG005D 0x0174 ++#define CMN_REG005E 0x0178 ++#define ROPLL_SDM_EN_MASK BIT(6) ++#define ROPLL_SDM_EN(x) UPDATE(x, 6, 6) ++#define ROPLL_SDM_FRAC_EN_RBR BIT(3) ++#define ROPLL_SDM_FRAC_EN_HBR BIT(2) ++#define ROPLL_SDM_FRAC_EN_HBR2 BIT(1) ++#define ROPLL_SDM_FRAC_EN_HBR3 BIT(0) ++#define CMN_REG005F 0x017C ++#define CMN_REG0060 0x0180 ++#define CMN_REG0061 0x0184 ++#define CMN_REG0062 0x0188 ++#define CMN_REG0063 0x018C ++#define CMN_REG0064 0x0190 ++#define ROPLL_SDM_NUM_SIGN_RBR_MASK BIT(3) ++#define ROPLL_SDM_NUM_SIGN_RBR(x) UPDATE(x, 3, 3) ++#define CMN_REG0065 0x0194 ++#define CMN_REG0066 0x0198 ++#define CMN_REG0067 0x019C ++#define CMN_REG0068 0x01A0 ++#define CMN_REG0069 0x01A4 ++#define ROPLL_SDC_N_RBR_MASK GENMASK(2, 0) ++#define ROPLL_SDC_N_RBR(x) UPDATE(x, 2, 0) ++#define CMN_REG006A 0x01A8 ++#define CMN_REG006B 0x01AC ++#define CMN_REG006C 0x01B0 ++#define CMN_REG006D 0x01B4 ++#define CMN_REG006E 0x01B8 ++#define CMN_REG006F 0x01BC ++#define CMN_REG0070 0x01C0 ++#define CMN_REG0071 0x01C4 ++#define CMN_REG0072 0x01C8 ++#define CMN_REG0073 0x01CC ++#define CMN_REG0074 0x01D0 ++#define ROPLL_SDC_NDIV_RSTN BIT(2) ++#define ROPLL_SSC_EN BIT(0) ++#define CMN_REG0075 0x01D4 ++#define CMN_REG0076 0x01D8 ++#define CMN_REG0077 0x01DC ++#define CMN_REG0078 0x01E0 ++#define CMN_REG0079 0x01E4 ++#define CMN_REG007A 0x01E8 ++#define CMN_REG007B 0x01EC ++#define CMN_REG007C 0x01F0 ++#define CMN_REG007D 0x01F4 ++#define CMN_REG007E 0x01F8 ++#define CMN_REG007F 0x01FC ++#define CMN_REG0080 0x0200 ++#define CMN_REG0081 0x0204 ++#define OVRD_PLL_CD_CLK_EN BIT(8) ++#define PLL_CD_HSCLK_EAST_EN BIT(0) ++#define CMN_REG0082 0x0208 ++#define CMN_REG0083 0x020C ++#define CMN_REG0084 0x0210 ++#define CMN_REG0085 0x0214 ++#define CMN_REG0086 0x0218 ++#define PLL_PCG_POSTDIV_SEL_MASK GENMASK(7, 4) ++#define PLL_PCG_POSTDIV_SEL(x) UPDATE(x, 7, 4) ++#define PLL_PCG_CLK_SEL_MASK GENMASK(3, 1) ++#define PLL_PCG_CLK_SEL(x) UPDATE(x, 3, 1) ++#define PLL_PCG_CLK_EN BIT(0) ++#define CMN_REG0087 0x021C ++#define PLL_FRL_MODE_EN BIT(3) ++#define PLL_TX_HS_CLK_EN BIT(2) ++#define CMN_REG0088 0x0220 ++#define CMN_REG0089 0x0224 ++#define LCPLL_ALONE_MODE BIT(1) ++#define CMN_REG008A 0x0228 ++#define CMN_REG008B 0x022C ++#define CMN_REG008C 0x0230 ++#define CMN_REG008D 0x0234 ++#define CMN_REG008E 0x0238 ++#define CMN_REG008F 0x023C ++#define CMN_REG0090 0x0240 ++#define CMN_REG0091 0x0244 ++#define CMN_REG0092 0x0248 ++#define CMN_REG0093 0x024C ++#define CMN_REG0094 0x0250 ++#define CMN_REG0095 0x0254 ++#define CMN_REG0096 0x0258 ++#define CMN_REG0097 0x025C ++#define DIG_CLK_SEL BIT(1) ++#define ROPLL_REF BIT(1) ++#define LCPLL_REF 0 ++#define CMN_REG0098 0x0260 ++#define CMN_REG0099 0x0264 ++#define CMN_ROPLL_ALONE_MODE BIT(2) ++#define ROPLL_ALONE_MODE BIT(2) ++#define CMN_REG009A 0x0268 ++#define HS_SPEED_SEL BIT(0) ++#define DIV_10_CLOCK BIT(0) ++#define CMN_REG009B 0x026C ++#define IS_SPEED_SEL BIT(4) ++#define LINK_SYMBOL_CLOCK BIT(4) ++#define LINK_SYMBOL_CLOCK1_2 0 ++#define CMN_REG009C 0x0270 ++#define CMN_REG009D 0x0274 ++#define CMN_REG009E 0x0278 ++#define CMN_REG009F 0x027C ++#define CMN_REG00A0 0x0280 ++#define CMN_REG00A1 0x0284 ++#define CMN_REG00A2 0x0288 ++#define CMN_REG00A3 0x028C ++#define CMN_REG00AD 0x0290 ++#define CMN_REG00A5 0x0294 ++#define CMN_REG00A6 0x0298 ++#define CMN_REG00A7 0x029C ++#define SB_REG0100 0x0400 ++#define SB_REG0101 0x0404 ++#define SB_REG0102 0x0408 ++#define OVRD_SB_RXTERM_EN_MASK BIT(5) ++#define OVRD_SB_RXTERM_EN(x) UPDATE(x, 5, 5) ++#define SB_RXTERM_EN_MASK BIT(4) ++#define SB_RXTERM_EN(x) UPDATE(x, 4, 4) ++#define ANA_SB_RXTERM_OFFSP_MASK GENMASK(3, 0) ++#define ANA_SB_RXTERM_OFFSP(x) UPDATE(x, 3, 0) ++#define SB_REG0103 0x040C ++#define ANA_SB_RXTERM_OFFSN_MASK GENMASK(6, 3) ++#define ANA_SB_RXTERM_OFFSN(x) UPDATE(x, 6, 3) ++#define OVRD_SB_RX_RESCAL_DONE_MASK BIT(1) ++#define OVRD_SB_RX_RESCAL_DONE(x) UPDATE(x, 1, 1) ++#define SB_RX_RESCAL_DONE_MASK BIT(0) ++#define SB_RX_RESCAL_DONE(x) UPDATE(x, 0, 0) ++#define SB_REG0104 0x0410 ++#define OVRD_SB_EN_MASK BIT(5) ++#define OVRD_SB_EN(x) UPDATE(x, 5, 5) ++#define SB_EN_MASK BIT(4) ++#define SB_EN(x) UPDATE(x, 4, 4) ++#define SB_REG0105 0x0414 ++#define OVRD_SB_EARC_CMDC_EN_MASK BIT(6) ++#define OVRD_SB_EARC_CMDC_EN(x) UPDATE(x, 6, 6) ++#define SB_EARC_CMDC_EN_MASK BIT(5) ++#define SB_EARC_CMDC_EN(x) UPDATE(x, 5, 5) ++#define ANA_SB_TX_HLVL_PROG_MASK GENMASK(2, 0) ++#define ANA_SB_TX_HLVL_PROG(x) UPDATE(x, 2, 0) ++#define SB_REG0106 0x0418 ++#define ANA_SB_TX_LLVL_PROG_MASK GENMASK(6, 4) ++#define ANA_SB_TX_LLVL_PROG(x) UPDATE(x, 6, 4) ++#define SB_REG0107 0x041C ++#define SB_REG0108 0x0420 ++#define SB_REG0109 0x0424 ++#define ANA_SB_DMRX_AFC_DIV_RATIO_MASK GENMASK(2, 0) ++#define ANA_SB_DMRX_AFC_DIV_RATIO(x) UPDATE(x, 2, 0) ++#define SB_REG010A 0x0428 ++#define SB_REG010B 0x042C ++#define SB_REG010C 0x0430 ++#define SB_REG010D 0x0434 ++#define SB_REG010E 0x0438 ++#define SB_REG010F 0x043C ++#define OVRD_SB_VREG_EN_MASK BIT(7) ++#define OVRD_SB_VREG_EN(x) UPDATE(x, 7, 7) ++#define SB_VREG_EN_MASK BIT(6) ++#define SB_VREG_EN(x) UPDATE(x, 6, 6) ++#define OVRD_SB_VREG_LPF_BYPASS_MASK BIT(5) ++#define OVRD_SB_VREG_LPF_BYPASS(x) UPDATE(x, 5, 5) ++#define SB_VREG_LPF_BYPASS_MASK BIT(4) ++#define SB_VREG_LPF_BYPASS(x) UPDATE(x, 4, 4) ++#define ANA_SB_VREG_GAIN_CTRL_MASK GENMASK(3, 0) ++#define ANA_SB_VREG_GAIN_CTRL(x) UPDATE(x, 3, 0) ++#define SB_REG0110 0x0440 ++#define ANA_SB_VREG_REF_SEL_MASK BIT(0) ++#define ANA_SB_VREG_REF_SEL(x) UPDATE(x, 0, 0) ++#define SB_REG0111 0x0444 ++#define SB_REG0112 0x0448 ++#define SB_REG0113 0x044C ++#define SB_RX_RCAL_OPT_CODE_MASK GENMASK(5, 4) ++#define SB_RX_RCAL_OPT_CODE(x) UPDATE(x, 5, 4) ++#define SB_RX_RTERM_CTRL_MASK GENMASK(3, 0) ++#define SB_RX_RTERM_CTRL(x) UPDATE(x, 3, 0) ++#define SB_REG0114 0x0450 ++#define SB_TG_SB_EN_DELAY_TIME_MASK GENMASK(5, 3) ++#define SB_TG_SB_EN_DELAY_TIME(x) UPDATE(x, 5, 3) ++#define SB_TG_RXTERM_EN_DELAY_TIME_MASK GENMASK(2, 0) ++#define SB_TG_RXTERM_EN_DELAY_TIME(x) UPDATE(x, 2, 0) ++#define SB_REG0115 0x0454 ++#define SB_READY_DELAY_TIME_MASK GENMASK(5, 3) ++#define SB_READY_DELAY_TIME(x) UPDATE(x, 5, 3) ++#define SB_TG_OSC_EN_DELAY_TIME_MASK GENMASK(2, 0) ++#define SB_TG_OSC_EN_DELAY_TIME(x) UPDATE(x, 2, 0) ++#define SB_REG0116 0x0458 ++#define AFC_RSTN_DELAY_TIME_MASK GENMASK(6, 4) ++#define AFC_RSTN_DELAY_TIME(x) UPDATE(x, 6, 4) ++#define SB_REG0117 0x045C ++#define FAST_PULSE_TIME_MASK GENMASK(3, 0) ++#define FAST_PULSE_TIME(x) UPDATE(x, 3, 0) ++#define SB_REG0118 0x0460 ++#define SB_REG0119 0x0464 ++#define SB_REG011A 0x0468 ++#define SB_REG011B 0x046C ++#define SB_EARC_SIG_DET_BYPASS_MASK BIT(4) ++#define SB_EARC_SIG_DET_BYPASS(x) UPDATE(x, 4, 4) ++#define SB_AFC_TOL_MASK GENMASK(3, 0) ++#define SB_AFC_TOL(x) UPDATE(x, 3, 0) ++#define SB_REG011C 0x0470 ++#define SB_REG011D 0x0474 ++#define SB_REG011E 0x0478 ++#define SB_REG011F 0x047C ++#define SB_PWM_AFC_CTRL_MASK GENMASK(7, 2) ++#define SB_PWM_AFC_CTRL(x) UPDATE(x, 7, 2) ++#define SB_RCAL_RSTN_MASK BIT(1) ++#define SB_RCAL_RSTN(x) UPDATE(x, 1, 1) ++#define SB_REG0120 0x0480 ++#define SB_EARC_EN_MASK BIT(1) ++#define SB_EARC_EN(x) UPDATE(x, 1, 1) ++#define SB_EARC_AFC_EN_MASK BIT(2) ++#define SB_EARC_AFC_EN(x) UPDATE(x, 2, 2) ++#define SB_REG0121 0x0484 ++#define SB_REG0122 0x0488 ++#define SB_REG0123 0x048C ++#define OVRD_SB_READY_MASK BIT(5) ++#define OVRD_SB_READY(x) UPDATE(x, 5, 5) ++#define SB_READY_MASK BIT(4) ++#define SB_READY(x) UPDATE(x, 4, 4) ++#define SB_REG0124 0x0490 ++#define SB_REG0125 0x0494 ++#define SB_REG0126 0x0498 ++#define SB_REG0127 0x049C ++#define SB_REG0128 0x04A0 ++#define SB_REG0129 0x04AD ++#define LNTOP_REG0200 0x0800 ++#define PROTOCOL_SEL BIT(2) ++#define HDMI_MODE BIT(2) ++#define HDMI_TMDS_FRL_SEL BIT(1) ++#define LNTOP_REG0201 0x0804 ++#define LNTOP_REG0202 0x0808 ++#define LNTOP_REG0203 0x080C ++#define LNTOP_REG0204 0x0810 ++#define LNTOP_REG0205 0x0814 ++#define LNTOP_REG0206 0x0818 ++#define DATA_BUS_WIDTH (0x3 << 1) ++#define WIDTH_40BIT (0x3 << 1) ++#define WIDTH_36BIT (0x2 << 1) ++#define DATA_BUS_SEL BIT(0) ++#define DATA_BUS_36_40 BIT(0) ++#define LNTOP_REG0207 0x081C ++#define LANE_EN 0xf ++#define ALL_LANE_EN 0xf ++#define LNTOP_REG0208 0x0820 ++#define LNTOP_REG0209 0x0824 ++#define LNTOP_REG020A 0x0828 ++#define LNTOP_REG020B 0x082C ++#define LNTOP_REG020C 0x0830 ++#define LNTOP_REG020D 0x0834 ++#define LNTOP_REG020E 0x0838 ++#define LNTOP_REG020F 0x083C ++#define LNTOP_REG0210 0x0840 ++#define LNTOP_REG0211 0x0844 ++#define LNTOP_REG0212 0x0848 ++#define LNTOP_REG0213 0x084C ++#define LNTOP_REG0214 0x0850 ++#define LNTOP_REG0215 0x0854 ++#define LNTOP_REG0216 0x0858 ++#define LNTOP_REG0217 0x085C ++#define LNTOP_REG0218 0x0860 ++#define LNTOP_REG0219 0x0864 ++#define LNTOP_REG021A 0x0868 ++#define LNTOP_REG021B 0x086C ++#define LNTOP_REG021C 0x0870 ++#define LNTOP_REG021D 0x0874 ++#define LNTOP_REG021E 0x0878 ++#define LNTOP_REG021F 0x087C ++#define LNTOP_REG0220 0x0880 ++#define LNTOP_REG0221 0x0884 ++#define LNTOP_REG0222 0x0888 ++#define LNTOP_REG0223 0x088C ++#define LNTOP_REG0224 0x0890 ++#define LNTOP_REG0225 0x0894 ++#define LNTOP_REG0226 0x0898 ++#define LNTOP_REG0227 0x089C ++#define LNTOP_REG0228 0x08A0 ++#define LNTOP_REG0229 0x08A4 ++#define LANE_REG0300 0x0C00 ++#define LANE_REG0301 0x0C04 ++#define LANE_REG0302 0x0C08 ++#define LANE_REG0303 0x0C0C ++#define LANE_REG0304 0x0C10 ++#define LANE_REG0305 0x0C14 ++#define LANE_REG0306 0x0C18 ++#define LANE_REG0307 0x0C1C ++#define LANE_REG0308 0x0C20 ++#define LANE_REG0309 0x0C24 ++#define LANE_REG030A 0x0C28 ++#define LANE_REG030B 0x0C2C ++#define LANE_REG030C 0x0C30 ++#define LANE_REG030D 0x0C34 ++#define LANE_REG030E 0x0C38 ++#define LANE_REG030F 0x0C3C ++#define LANE_REG0310 0x0C40 ++#define LANE_REG0311 0x0C44 ++#define LANE_REG0312 0x0C48 ++#define LN0_TX_SER_RATE_SEL_RBR BIT(5) ++#define LN0_TX_SER_RATE_SEL_HBR BIT(4) ++#define LN0_TX_SER_RATE_SEL_HBR2 BIT(3) ++#define LN0_TX_SER_RATE_SEL_HBR3 BIT(2) ++#define LANE_REG0313 0x0C4C ++#define LANE_REG0314 0x0C50 ++#define LANE_REG0315 0x0C54 ++#define LANE_REG0316 0x0C58 ++#define LANE_REG0317 0x0C5C ++#define LANE_REG0318 0x0C60 ++#define LANE_REG0319 0x0C64 ++#define LANE_REG031A 0x0C68 ++#define LANE_REG031B 0x0C6C ++#define LANE_REG031C 0x0C70 ++#define LANE_REG031D 0x0C74 ++#define LANE_REG031E 0x0C78 ++#define LANE_REG031F 0x0C7C ++#define LANE_REG0320 0x0C80 ++#define LANE_REG0321 0x0C84 ++#define LANE_REG0322 0x0C88 ++#define LANE_REG0323 0x0C8C ++#define LANE_REG0324 0x0C90 ++#define LANE_REG0325 0x0C94 ++#define LANE_REG0326 0x0C98 ++#define LANE_REG0327 0x0C9C ++#define LANE_REG0328 0x0CA0 ++#define LANE_REG0329 0x0CA4 ++#define LANE_REG032A 0x0CA8 ++#define LANE_REG032B 0x0CAC ++#define LANE_REG032C 0x0CB0 ++#define LANE_REG032D 0x0CB4 ++#define LANE_REG0400 0x1000 ++#define LANE_REG0401 0x1004 ++#define LANE_REG0402 0x1008 ++#define LANE_REG0403 0x100C ++#define LANE_REG0404 0x1010 ++#define LANE_REG0405 0x1014 ++#define LANE_REG0406 0x1018 ++#define LANE_REG0407 0x101C ++#define LANE_REG0408 0x1020 ++#define LANE_REG0409 0x1024 ++#define LANE_REG040A 0x1028 ++#define LANE_REG040B 0x102C ++#define LANE_REG040C 0x1030 ++#define LANE_REG040D 0x1034 ++#define LANE_REG040E 0x1038 ++#define LANE_REG040F 0x103C ++#define LANE_REG0410 0x1040 ++#define LANE_REG0411 0x1044 ++#define LANE_REG0412 0x1048 ++#define LN1_TX_SER_RATE_SEL_RBR BIT(5) ++#define LN1_TX_SER_RATE_SEL_HBR BIT(4) ++#define LN1_TX_SER_RATE_SEL_HBR2 BIT(3) ++#define LN1_TX_SER_RATE_SEL_HBR3 BIT(2) ++#define LANE_REG0413 0x104C ++#define LANE_REG0414 0x1050 ++#define LANE_REG0415 0x1054 ++#define LANE_REG0416 0x1058 ++#define LANE_REG0417 0x105C ++#define LANE_REG0418 0x1060 ++#define LANE_REG0419 0x1064 ++#define LANE_REG041A 0x1068 ++#define LANE_REG041B 0x106C ++#define LANE_REG041C 0x1070 ++#define LANE_REG041D 0x1074 ++#define LANE_REG041E 0x1078 ++#define LANE_REG041F 0x107C ++#define LANE_REG0420 0x1080 ++#define LANE_REG0421 0x1084 ++#define LANE_REG0422 0x1088 ++#define LANE_REG0423 0x108C ++#define LANE_REG0424 0x1090 ++#define LANE_REG0425 0x1094 ++#define LANE_REG0426 0x1098 ++#define LANE_REG0427 0x109C ++#define LANE_REG0428 0x10A0 ++#define LANE_REG0429 0x10A4 ++#define LANE_REG042A 0x10A8 ++#define LANE_REG042B 0x10AC ++#define LANE_REG042C 0x10B0 ++#define LANE_REG042D 0x10B4 ++#define LANE_REG0500 0x1400 ++#define LANE_REG0501 0x1404 ++#define LANE_REG0502 0x1408 ++#define LANE_REG0503 0x140C ++#define LANE_REG0504 0x1410 ++#define LANE_REG0505 0x1414 ++#define LANE_REG0506 0x1418 ++#define LANE_REG0507 0x141C ++#define LANE_REG0508 0x1420 ++#define LANE_REG0509 0x1424 ++#define LANE_REG050A 0x1428 ++#define LANE_REG050B 0x142C ++#define LANE_REG050C 0x1430 ++#define LANE_REG050D 0x1434 ++#define LANE_REG050E 0x1438 ++#define LANE_REG050F 0x143C ++#define LANE_REG0510 0x1440 ++#define LANE_REG0511 0x1444 ++#define LANE_REG0512 0x1448 ++#define LN2_TX_SER_RATE_SEL_RBR BIT(5) ++#define LN2_TX_SER_RATE_SEL_HBR BIT(4) ++#define LN2_TX_SER_RATE_SEL_HBR2 BIT(3) ++#define LN2_TX_SER_RATE_SEL_HBR3 BIT(2) ++#define LANE_REG0513 0x144C ++#define LANE_REG0514 0x1450 ++#define LANE_REG0515 0x1454 ++#define LANE_REG0516 0x1458 ++#define LANE_REG0517 0x145C ++#define LANE_REG0518 0x1460 ++#define LANE_REG0519 0x1464 ++#define LANE_REG051A 0x1468 ++#define LANE_REG051B 0x146C ++#define LANE_REG051C 0x1470 ++#define LANE_REG051D 0x1474 ++#define LANE_REG051E 0x1478 ++#define LANE_REG051F 0x147C ++#define LANE_REG0520 0x1480 ++#define LANE_REG0521 0x1484 ++#define LANE_REG0522 0x1488 ++#define LANE_REG0523 0x148C ++#define LANE_REG0524 0x1490 ++#define LANE_REG0525 0x1494 ++#define LANE_REG0526 0x1498 ++#define LANE_REG0527 0x149C ++#define LANE_REG0528 0x14A0 ++#define LANE_REG0529 0x14AD ++#define LANE_REG052A 0x14A8 ++#define LANE_REG052B 0x14AC ++#define LANE_REG052C 0x14B0 ++#define LANE_REG052D 0x14B4 ++#define LANE_REG0600 0x1800 ++#define LANE_REG0601 0x1804 ++#define LANE_REG0602 0x1808 ++#define LANE_REG0603 0x180C ++#define LANE_REG0604 0x1810 ++#define LANE_REG0605 0x1814 ++#define LANE_REG0606 0x1818 ++#define LANE_REG0607 0x181C ++#define LANE_REG0608 0x1820 ++#define LANE_REG0609 0x1824 ++#define LANE_REG060A 0x1828 ++#define LANE_REG060B 0x182C ++#define LANE_REG060C 0x1830 ++#define LANE_REG060D 0x1834 ++#define LANE_REG060E 0x1838 ++#define LANE_REG060F 0x183C ++#define LANE_REG0610 0x1840 ++#define LANE_REG0611 0x1844 ++#define LANE_REG0612 0x1848 ++#define LN3_TX_SER_RATE_SEL_RBR BIT(5) ++#define LN3_TX_SER_RATE_SEL_HBR BIT(4) ++#define LN3_TX_SER_RATE_SEL_HBR2 BIT(3) ++#define LN3_TX_SER_RATE_SEL_HBR3 BIT(2) ++#define LANE_REG0613 0x184C ++#define LANE_REG0614 0x1850 ++#define LANE_REG0615 0x1854 ++#define LANE_REG0616 0x1858 ++#define LANE_REG0617 0x185C ++#define LANE_REG0618 0x1860 ++#define LANE_REG0619 0x1864 ++#define LANE_REG061A 0x1868 ++#define LANE_REG061B 0x186C ++#define LANE_REG061C 0x1870 ++#define LANE_REG061D 0x1874 ++#define LANE_REG061E 0x1878 ++#define LANE_REG061F 0x187C ++#define LANE_REG0620 0x1880 ++#define LANE_REG0621 0x1884 ++#define LANE_REG0622 0x1888 ++#define LANE_REG0623 0x188C ++#define LANE_REG0624 0x1890 ++#define LANE_REG0625 0x1894 ++#define LANE_REG0626 0x1898 ++#define LANE_REG0627 0x189C ++#define LANE_REG0628 0x18A0 ++#define LANE_REG0629 0x18A4 ++#define LANE_REG062A 0x18A8 ++#define LANE_REG062B 0x18AC ++#define LANE_REG062C 0x18B0 ++#define LANE_REG062D 0x18B4 ++ ++#define HDMI20_MAX_RATE 600000000 ++#define DATA_RATE_MASK 0xFFFFFFF ++#define COLOR_DEPTH_MASK BIT(31) ++#define HDMI_MODE_MASK BIT(30) ++#define HDMI_EARC_MASK BIT(29) ++ ++enum hdptx_combphy_type { ++ SS_HDMI, ++ SS_DP ++}; ++ ++ ++struct lcpll_config { ++ u32 bit_rate; ++ u8 lcvco_mode_en; ++ u8 pi_en; ++ u8 clk_en_100m; ++ u8 pms_mdiv; ++ u8 pms_mdiv_afc; ++ u8 pms_pdiv; ++ u8 pms_refdiv; ++ u8 pms_sdiv; ++ u8 pi_cdiv_rstn; ++ u8 pi_cdiv_sel; ++ u8 sdm_en; ++ u8 sdm_rstn; ++ u8 sdc_frac_en; ++ u8 sdc_rstn; ++ u8 sdm_deno; ++ u8 sdm_num_sign; ++ u8 sdm_num; ++ u8 sdc_n; ++ u8 sdc_n2; ++ u8 sdc_num; ++ u8 sdc_deno; ++ u8 sdc_ndiv_rstn; ++ u8 ssc_en; ++ u8 ssc_fm_dev; ++ u8 ssc_fm_freq; ++ u8 ssc_clk_div_sel; ++ u8 cd_tx_ser_rate_sel; ++}; ++ ++struct ropll_config { ++ u32 bit_rate; ++ u8 pms_mdiv; ++ u8 pms_mdiv_afc; ++ u8 pms_pdiv; ++ u8 pms_refdiv; ++ u8 pms_sdiv; ++ u8 pms_iqdiv_rstn; ++ u8 ref_clk_sel; ++ u8 sdm_en; ++ u8 sdm_rstn; ++ u8 sdc_frac_en; ++ u8 sdc_rstn; ++ u8 sdm_clk_div; ++ u8 sdm_deno; ++ u8 sdm_num_sign; ++ u8 sdm_num; ++ u8 sdc_n; ++ u8 sdc_num; ++ u8 sdc_deno; ++ u8 sdc_ndiv_rstn; ++ u8 ssc_en; ++ u8 ssc_fm_dev; ++ u8 ssc_fm_freq; ++ u8 ssc_clk_div_sel; ++ u8 ana_cpp_ctrl; ++ u8 ana_lpf_c_sel; ++ u8 cd_tx_ser_rate_sel; ++}; ++ ++struct rockchip_hdptx_phy { ++ struct device *dev; ++ struct regmap *regmap; ++ struct regmap *grf; ++ ++ int irq; ++ int id; ++ ++ struct phy *phy; ++ struct clk_bulk_data *clks; ++ int nr_clks; ++ struct phy_config *phy_cfg; ++ ++ /* clk provider */ ++ struct clk_hw hw; ++ struct clk *dclk; ++ unsigned long rate; ++ ++ struct reset_control *phy_reset; ++ struct reset_control *apb_reset; ++ struct reset_control *cmn_reset; ++ struct reset_control *init_reset; ++ struct reset_control *lane_reset; ++ struct reset_control *ropll_reset; ++ struct reset_control *lcpll_reset; ++ ++ bool earc_en; ++ int count; ++}; ++ ++struct lcpll_config lcpll_cfg[] = { ++ { 48000000, 1, 0, 0, 0x7d, 0x7d, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 2, ++ 0, 0x13, 0x18, 1, 0, 0x20, 0x0c, 1, 0, ++ }, ++ { 40000000, 1, 1, 0, 0x68, 0x68, 1, 1, 0, 0, 0, 1, 1, 1, 1, 9, 0, 1, 1, ++ 0, 2, 3, 1, 0, 0x20, 0x0c, 1, 0, ++ }, ++ { 32000000, 1, 1, 1, 0x6b, 0x6b, 1, 1, 0, 1, 2, 1, 1, 1, 1, 9, 1, 2, 1, ++ 0, 0x0d, 0x18, 1, 0, 0x20, 0x0c, 1, 1, ++ }, ++ { ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, ++ }, ++}; ++ ++struct ropll_config ropll_frl_cfg[] = { ++ { 24000000, 0x19, 0x19, 1, 1, 0, 1, 2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, ++ 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 18000000, 0x7d, 0x7d, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, ++ 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 9000000, 0x7d, 0x7d, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, ++ 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ }, ++}; ++ ++struct ropll_config ropll_tmds_cfg[] = { ++ { 5940000, 124, 124, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 3712500, 155, 155, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 2970000, 124, 124, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1620000, 135, 135, 1, 1, 3, 1, 1, 0, 1, 1, 1, 1, 4, 0, 3, 5, 5, 0x10, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1856250, 155, 155, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1540000, 193, 193, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 193, 1, 32, 2, 1, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1485000, 0x7b, 0x7b, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, 0, 3, 5, 5, 0x10, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1462500, 122, 122, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 244, 1, 16, 2, 1, 1, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1190000, 149, 149, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 149, 1, 16, 2, 1, 1, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1065000, 89, 89, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 89, 1, 16, 1, 0, 1, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1080000, 135, 135, 1, 1, 5, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, 0x14, ++ 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 855000, 214, 214, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 214, 1, 16, 2, 1, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 835000, 105, 105, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 42, 1, 16, 1, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 928125, 155, 155, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 742500, 124, 124, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 650000, 162, 162, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 54, 0, 16, 4, 1, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 337500, 0x70, 0x70, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 0x2, 0, 0x01, 5, 1, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 400000, 100, 100, 1, 1, 11, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, 0x14, ++ 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 270000, 0x5a, 0x5a, 1, 1, 0xf, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, 0x14, ++ 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 251750, 84, 84, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 168, 1, 16, 4, 1, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ }, ++}; ++ ++static bool rockchip_hdptx_phy_is_accissible_reg(struct device *dev, ++ unsigned int reg) ++{ ++ switch (reg) { ++ case 0x0000 ... 0x029c: ++ case 0x0400 ... 0x04a4: ++ case 0x0800 ... 0x08a4: ++ case 0x0c00 ... 0x0cb4: ++ case 0x1000 ... 0x10b4: ++ case 0x1400 ... 0x14b4: ++ case 0x1800 ... 0x18b4: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static const struct regmap_config rockchip_hdptx_phy_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .fast_io = true, ++ .max_register = 0x18b4, ++ .name = "hdptx-combphy", ++ ++ .readable_reg = rockchip_hdptx_phy_is_accissible_reg, ++ .writeable_reg = rockchip_hdptx_phy_is_accissible_reg, ++}; ++ ++static inline struct rockchip_hdptx_phy *to_rockchip_hdptx_phy(struct clk_hw *hw) ++{ ++ return container_of(hw, struct rockchip_hdptx_phy, hw); ++} ++ ++static inline void hdptx_write(struct rockchip_hdptx_phy *hdptx, u32 reg, u8 val) ++{ ++ regmap_write(hdptx->regmap, reg, val); ++} ++ ++static inline u8 hdptx_read(struct rockchip_hdptx_phy *hdptx, u32 reg) ++{ ++ u32 val; ++ ++ regmap_read(hdptx->regmap, reg, &val); ++ ++ return val; ++} ++ ++static inline void hdptx_update_bits(struct rockchip_hdptx_phy *hdptx, u32 reg, ++ u8 mask, u8 val) ++{ ++ regmap_update_bits(hdptx->regmap, reg, mask, val); ++} ++ ++static inline void hdptx_grf_write(struct rockchip_hdptx_phy *hdptx, u32 reg, u32 val) ++{ ++ regmap_write(hdptx->grf, reg, val); ++} ++ ++static inline u8 hdptx_grf_read(struct rockchip_hdptx_phy *hdptx, u32 reg) ++{ ++ u32 val; ++ ++ regmap_read(hdptx->grf, reg, &val); ++ ++ return val; ++} ++ ++static void hdptx_pre_power_up(struct rockchip_hdptx_phy *hdptx) ++{ ++ u32 val = 0; ++ ++ reset_control_assert(hdptx->apb_reset); ++ udelay(20); ++ reset_control_deassert(hdptx->apb_reset); ++ ++ reset_control_assert(hdptx->lane_reset); ++ reset_control_assert(hdptx->cmn_reset); ++ reset_control_assert(hdptx->init_reset); ++ ++ val = (HDPTX_I_PLL_EN | HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16; ++ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); ++} ++ ++static int hdptx_post_enable_lane(struct rockchip_hdptx_phy *hdptx) ++{ ++ u32 val = 0; ++ int i; ++ ++ reset_control_deassert(hdptx->lane_reset); ++ ++ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | HDPTX_I_BIAS_EN | ++ HDPTX_I_BGR_EN; ++ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); ++ ++ for (i = 0; i < 50; i++) { ++ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS); ++ ++ if (val & HDPTX_O_PHY_RDY && val & HDPTX_O_PLL_LOCK_DONE) ++ break; ++ udelay(100); ++ } ++ ++ if (i == 50) { ++ dev_err(hdptx->dev, "hdptx phy lane can't ready!\n"); ++ return -EINVAL; ++ } ++ ++ dev_err(hdptx->dev, "hdptx phy lane locked!\n"); ++ ++ return 0; ++} ++ ++static int hdptx_post_enable_pll(struct rockchip_hdptx_phy *hdptx) ++{ ++ u32 val = 0; ++ int i; ++ ++ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | HDPTX_I_BIAS_EN | ++ HDPTX_I_BGR_EN; ++ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); ++ udelay(10); ++ reset_control_deassert(hdptx->init_reset); ++ udelay(10); ++ val = HDPTX_I_PLL_EN << 16 | HDPTX_I_PLL_EN; ++ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); ++ udelay(10); ++ reset_control_deassert(hdptx->cmn_reset); ++ ++ for (i = 0; i < 20; i++) { ++ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS); ++ ++ if (val & HDPTX_O_PHY_CLK_RDY) ++ break; ++ udelay(20); ++ } ++ ++ if (i == 20) { ++ dev_err(hdptx->dev, "hdptx phy pll can't lock!\n"); ++ return -EINVAL; ++ } ++ ++ dev_err(hdptx->dev, "hdptx phy pll locked!\n"); ++ ++ return 0; ++} ++ ++static int hdptx_post_power_up(struct rockchip_hdptx_phy *hdptx) ++{ ++ u32 val = 0; ++ int i; ++ ++ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | HDPTX_I_BIAS_EN | ++ HDPTX_I_BGR_EN; ++ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); ++ udelay(10); ++ reset_control_deassert(hdptx->init_reset); ++ udelay(10); ++ val = HDPTX_I_PLL_EN << 16 | HDPTX_I_PLL_EN; ++ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); ++ udelay(10); ++ reset_control_deassert(hdptx->cmn_reset); ++ ++ for (i = 0; i < 20; i++) { ++ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS); ++ ++ if (val & HDPTX_O_PLL_LOCK_DONE) ++ break; ++ udelay(20); ++ } ++ ++ if (i == 20) { ++ dev_err(hdptx->dev, "hdptx phy can't lock!\n"); ++ return -EINVAL; ++ } ++ ++ udelay(20); ++ ++ reset_control_deassert(hdptx->lane_reset); ++ ++ for (i = 0; i < 50; i++) { ++ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS); ++ ++ if (val & HDPTX_O_PHY_RDY) ++ break; ++ udelay(100); ++ } ++ ++ if (i == 50) { ++ dev_err(hdptx->dev, "hdptx phy can't ready!\n"); ++ return -EINVAL; ++ } ++ ++ dev_err(hdptx->dev, "hdptx phy locked!\n"); ++ ++ return 0; ++} ++ ++static void hdptx_phy_disable(struct rockchip_hdptx_phy *hdptx) ++{ ++ u32 val; ++ ++ /* reset phy and apb, or phy locked flag may keep 1 */ ++ reset_control_assert(hdptx->phy_reset); ++ udelay(20); ++ reset_control_deassert(hdptx->phy_reset); ++ ++ reset_control_assert(hdptx->apb_reset); ++ udelay(20); ++ reset_control_deassert(hdptx->apb_reset); ++ ++ hdptx_write(hdptx, LANE_REG0300, 0x82); ++ hdptx_write(hdptx, SB_REG010F, 0xc1); ++ hdptx_write(hdptx, SB_REG0110, 0x1); ++ hdptx_write(hdptx, LANE_REG0301, 0x80); ++ hdptx_write(hdptx, LANE_REG0401, 0x80); ++ hdptx_write(hdptx, LANE_REG0501, 0x80); ++ hdptx_write(hdptx, LANE_REG0601, 0x80); ++ ++ reset_control_assert(hdptx->lane_reset); ++ reset_control_assert(hdptx->cmn_reset); ++ reset_control_assert(hdptx->init_reset); ++ ++ val = (HDPTX_I_PLL_EN | HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16; ++ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); ++} ++ ++static void hdptx_earc_config(struct rockchip_hdptx_phy *hdptx) ++{ ++ hdptx_update_bits(hdptx, SB_REG0113, SB_RX_RCAL_OPT_CODE_MASK, ++ SB_RX_RCAL_OPT_CODE(1)); ++ hdptx_write(hdptx, SB_REG011C, 0x04); ++ hdptx_update_bits(hdptx, SB_REG011B, SB_AFC_TOL_MASK, ++ SB_AFC_TOL(3)); ++ hdptx_write(hdptx, SB_REG0109, 0x05); ++ hdptx_update_bits(hdptx, SB_REG0120, SB_EARC_EN_MASK | SB_EARC_AFC_EN_MASK, ++ SB_EARC_EN(1) | SB_EARC_AFC_EN(1)); ++ hdptx_update_bits(hdptx, SB_REG011B, SB_EARC_SIG_DET_BYPASS_MASK, ++ SB_EARC_SIG_DET_BYPASS(1)); ++ hdptx_update_bits(hdptx, SB_REG011F, SB_PWM_AFC_CTRL_MASK | SB_RCAL_RSTN_MASK, ++ SB_PWM_AFC_CTRL(0xc) | SB_RCAL_RSTN(1)); ++ hdptx_update_bits(hdptx, SB_REG0115, SB_READY_DELAY_TIME_MASK, ++ SB_READY_DELAY_TIME(2)); ++ hdptx_update_bits(hdptx, SB_REG0113, SB_RX_RTERM_CTRL_MASK, ++ SB_RX_RTERM_CTRL(3)); ++ hdptx_update_bits(hdptx, SB_REG0102, ANA_SB_RXTERM_OFFSP_MASK, ++ ANA_SB_RXTERM_OFFSP(3)); ++ hdptx_update_bits(hdptx, SB_REG0103, ANA_SB_RXTERM_OFFSN_MASK, ++ ANA_SB_RXTERM_OFFSN(3)); ++ hdptx_write(hdptx, SB_REG011A, 0x03); ++ hdptx_write(hdptx, SB_REG0118, 0x0a); ++ hdptx_write(hdptx, SB_REG011E, 0x6a); ++ hdptx_write(hdptx, SB_REG011D, 0x67); ++ hdptx_update_bits(hdptx, SB_REG0117, FAST_PULSE_TIME_MASK, ++ FAST_PULSE_TIME(4)); ++ hdptx_update_bits(hdptx, SB_REG0114, SB_TG_SB_EN_DELAY_TIME_MASK | ++ SB_TG_RXTERM_EN_DELAY_TIME_MASK, ++ SB_TG_SB_EN_DELAY_TIME(2) | ++ SB_TG_RXTERM_EN_DELAY_TIME(2)); ++ hdptx_update_bits(hdptx, SB_REG0105, ANA_SB_TX_HLVL_PROG_MASK, ++ ANA_SB_TX_HLVL_PROG(7)); ++ hdptx_update_bits(hdptx, SB_REG0106, ANA_SB_TX_LLVL_PROG_MASK, ++ ANA_SB_TX_LLVL_PROG(7)); ++ hdptx_update_bits(hdptx, SB_REG010F, ANA_SB_VREG_GAIN_CTRL_MASK, ++ ANA_SB_VREG_GAIN_CTRL(0)); ++ hdptx_update_bits(hdptx, SB_REG0110, ANA_SB_VREG_REF_SEL_MASK, ++ ANA_SB_VREG_REF_SEL(1)); ++ hdptx_update_bits(hdptx, SB_REG0115, SB_TG_OSC_EN_DELAY_TIME_MASK, ++ SB_TG_OSC_EN_DELAY_TIME(2)); ++ hdptx_update_bits(hdptx, SB_REG0116, AFC_RSTN_DELAY_TIME_MASK, ++ AFC_RSTN_DELAY_TIME(2)); ++ hdptx_update_bits(hdptx, SB_REG0109, ANA_SB_DMRX_AFC_DIV_RATIO_MASK, ++ ANA_SB_DMRX_AFC_DIV_RATIO(5)); ++ hdptx_update_bits(hdptx, SB_REG0103, OVRD_SB_RX_RESCAL_DONE_MASK, ++ OVRD_SB_RX_RESCAL_DONE(1)); ++ hdptx_update_bits(hdptx, SB_REG0104, OVRD_SB_EN_MASK, ++ OVRD_SB_EN(1)); ++ hdptx_update_bits(hdptx, SB_REG0102, OVRD_SB_RXTERM_EN_MASK, ++ OVRD_SB_RXTERM_EN(1)); ++ hdptx_update_bits(hdptx, SB_REG0105, OVRD_SB_EARC_CMDC_EN_MASK, ++ OVRD_SB_EARC_CMDC_EN(1)); ++ hdptx_update_bits(hdptx, SB_REG010F, OVRD_SB_VREG_EN_MASK | ++ OVRD_SB_VREG_LPF_BYPASS_MASK, ++ OVRD_SB_VREG_EN(1) | OVRD_SB_VREG_LPF_BYPASS(1)); ++ hdptx_update_bits(hdptx, SB_REG0123, OVRD_SB_READY_MASK, ++ OVRD_SB_READY(1)); ++ udelay(1000); ++ hdptx_update_bits(hdptx, SB_REG0103, SB_RX_RESCAL_DONE_MASK, ++ SB_RX_RESCAL_DONE(1)); ++ udelay(50); ++ hdptx_update_bits(hdptx, SB_REG0104, SB_EN_MASK, SB_EN(1)); ++ udelay(50); ++ hdptx_update_bits(hdptx, SB_REG0102, SB_RXTERM_EN_MASK, ++ SB_RXTERM_EN(1)); ++ udelay(50); ++ hdptx_update_bits(hdptx, SB_REG0105, SB_EARC_CMDC_EN_MASK, ++ SB_EARC_CMDC_EN(1)); ++ hdptx_update_bits(hdptx, SB_REG010F, SB_VREG_EN_MASK, ++ SB_VREG_EN(1)); ++ udelay(50); ++ hdptx_update_bits(hdptx, SB_REG010F, OVRD_SB_VREG_LPF_BYPASS_MASK, ++ OVRD_SB_VREG_LPF_BYPASS(1)); ++ udelay(250); ++ hdptx_update_bits(hdptx, SB_REG010F, OVRD_SB_VREG_LPF_BYPASS_MASK, ++ OVRD_SB_VREG_LPF_BYPASS(0)); ++ udelay(100); ++ hdptx_update_bits(hdptx, SB_REG0123, SB_READY_MASK, SB_READY(1)); ++} ++ ++static bool hdptx_phy_clk_pll_calc(unsigned int data_rate, ++ struct ropll_config *cfg) ++{ ++ unsigned int fref = 24000; ++ unsigned int sdc; ++ unsigned int fout = data_rate / 2; ++ unsigned int fvco; ++ u32 mdiv, sdiv, n = 8; ++ unsigned long k = 0, lc, k_sub, lc_sub; ++ ++ for (sdiv = 16; sdiv >= 1; sdiv--) { ++ if (sdiv % 2 && sdiv != 1) ++ continue; ++ ++ fvco = fout * sdiv; ++ ++ if (fvco < 2000000 || fvco > 4000000) ++ continue; ++ ++ mdiv = DIV_ROUND_UP(fvco, fref); ++ if (mdiv < 20 || mdiv > 255) ++ continue; ++ ++ if (fref * mdiv - fvco) { ++ for (sdc = 264000; sdc <= 750000; sdc += fref) ++ if (sdc * n > fref * mdiv) ++ break; ++ ++ if (sdc > 750000) ++ continue; ++ ++ rational_best_approximation(fref * mdiv - fvco, ++ sdc / 16, ++ GENMASK(6, 0), ++ GENMASK(7, 0), ++ &k, &lc); ++ ++ rational_best_approximation(sdc * n - fref * mdiv, ++ sdc, ++ GENMASK(6, 0), ++ GENMASK(7, 0), ++ &k_sub, &lc_sub); ++ } ++ ++ break; ++ } ++ ++ if (sdiv < 1) ++ return false; ++ ++ if (cfg) { ++ cfg->pms_mdiv = mdiv; ++ cfg->pms_mdiv_afc = mdiv; ++ cfg->pms_pdiv = 1; ++ cfg->pms_refdiv = 1; ++ cfg->pms_sdiv = sdiv - 1; ++ ++ cfg->sdm_en = k > 0 ? 1 : 0; ++ if (cfg->sdm_en) { ++ cfg->sdm_deno = lc; ++ cfg->sdm_num_sign = 1; ++ cfg->sdm_num = k; ++ cfg->sdc_n = n - 3; ++ cfg->sdc_num = k_sub; ++ cfg->sdc_deno = lc_sub; ++ } ++ } ++ ++ return true; ++} ++ ++static int hdptx_ropll_cmn_config(struct rockchip_hdptx_phy *hdptx, unsigned long bit_rate) ++{ ++ int bus_width = phy_get_bus_width(hdptx->phy); ++ u8 color_depth = (bus_width & COLOR_DEPTH_MASK) ? 1 : 0; ++ struct ropll_config *cfg = ropll_tmds_cfg; ++ struct ropll_config rc = {0}; ++ ++ dev_info(hdptx->dev, "%s bus_width:%x rate:%lu\n", __func__, bus_width, bit_rate); ++ hdptx->rate = bit_rate * 100; ++ ++ if (color_depth) ++ bit_rate = bit_rate * 10 / 8; ++ ++ for (; cfg->bit_rate != ~0; cfg++) ++ if (bit_rate == cfg->bit_rate) ++ break; ++ ++ if (cfg->bit_rate == ~0) { ++ if (hdptx_phy_clk_pll_calc(bit_rate, &rc)) { ++ cfg = &rc; ++ } else { ++ dev_err(hdptx->dev, "%s can't find pll cfg\n", __func__); ++ return -EINVAL; ++ } ++ } ++ ++ dev_dbg(hdptx->dev, "mdiv=%u, sdiv=%u\n", ++ cfg->pms_mdiv, cfg->pms_sdiv + 1); ++ dev_dbg(hdptx->dev, "sdm_en=%u, k_sign=%u, k=%u, lc=%u", ++ cfg->sdm_en, cfg->sdm_num_sign, cfg->sdm_num, cfg->sdm_deno); ++ dev_dbg(hdptx->dev, "n=%u, k_sub=%u, lc_sub=%u\n", ++ cfg->sdc_n + 3, cfg->sdc_num, cfg->sdc_deno); ++ ++ hdptx_pre_power_up(hdptx); ++ ++ reset_control_assert(hdptx->ropll_reset); ++ udelay(20); ++ reset_control_deassert(hdptx->ropll_reset); ++ ++ hdptx_write(hdptx, CMN_REG0008, 0x00); ++ hdptx_write(hdptx, CMN_REG0009, 0x0c); ++ hdptx_write(hdptx, CMN_REG000A, 0x83); ++ hdptx_write(hdptx, CMN_REG000B, 0x06); ++ hdptx_write(hdptx, CMN_REG000C, 0x20); ++ hdptx_write(hdptx, CMN_REG000D, 0xb8); ++ hdptx_write(hdptx, CMN_REG000E, 0x0f); ++ hdptx_write(hdptx, CMN_REG000F, 0x0f); ++ hdptx_write(hdptx, CMN_REG0010, 0x04); ++ hdptx_write(hdptx, CMN_REG0011, 0x01); ++ hdptx_write(hdptx, CMN_REG0012, 0x26); ++ hdptx_write(hdptx, CMN_REG0013, 0x22); ++ hdptx_write(hdptx, CMN_REG0014, 0x24); ++ hdptx_write(hdptx, CMN_REG0015, 0x77); ++ hdptx_write(hdptx, CMN_REG0016, 0x08); ++ hdptx_write(hdptx, CMN_REG0017, 0x20); ++ hdptx_write(hdptx, CMN_REG0018, 0x04); ++ hdptx_write(hdptx, CMN_REG0019, 0x48); ++ hdptx_write(hdptx, CMN_REG001A, 0x01); ++ hdptx_write(hdptx, CMN_REG001B, 0x00); ++ hdptx_write(hdptx, CMN_REG001C, 0x01); ++ hdptx_write(hdptx, CMN_REG001D, 0x64); ++ hdptx_write(hdptx, CMN_REG001E, 0x14); ++ hdptx_write(hdptx, CMN_REG001F, 0x00); ++ hdptx_write(hdptx, CMN_REG0020, 0x00); ++ hdptx_write(hdptx, CMN_REG0021, 0x00); ++ hdptx_write(hdptx, CMN_REG0022, 0x11); ++ hdptx_write(hdptx, CMN_REG0023, 0x00); ++ hdptx_write(hdptx, CMN_REG0024, 0x00); ++ hdptx_write(hdptx, CMN_REG0025, 0x53); ++ hdptx_write(hdptx, CMN_REG0026, 0x00); ++ hdptx_write(hdptx, CMN_REG0027, 0x00); ++ hdptx_write(hdptx, CMN_REG0028, 0x01); ++ hdptx_write(hdptx, CMN_REG0029, 0x01); ++ hdptx_write(hdptx, CMN_REG002A, 0x00); ++ hdptx_write(hdptx, CMN_REG002B, 0x00); ++ hdptx_write(hdptx, CMN_REG002C, 0x00); ++ hdptx_write(hdptx, CMN_REG002D, 0x00); ++ hdptx_write(hdptx, CMN_REG002E, 0x04); ++ hdptx_write(hdptx, CMN_REG002F, 0x00); ++ hdptx_write(hdptx, CMN_REG0030, 0x20); ++ hdptx_write(hdptx, CMN_REG0031, 0x30); ++ hdptx_write(hdptx, CMN_REG0032, 0x0b); ++ hdptx_write(hdptx, CMN_REG0033, 0x23); ++ hdptx_write(hdptx, CMN_REG0034, 0x00); ++ hdptx_write(hdptx, CMN_REG0035, 0x00); ++ hdptx_write(hdptx, CMN_REG0038, 0x00); ++ hdptx_write(hdptx, CMN_REG0039, 0x00); ++ hdptx_write(hdptx, CMN_REG003A, 0x00); ++ hdptx_write(hdptx, CMN_REG003B, 0x00); ++ hdptx_write(hdptx, CMN_REG003C, 0x80); ++ hdptx_write(hdptx, CMN_REG003D, 0x40); ++ hdptx_write(hdptx, CMN_REG003E, 0x0c); ++ hdptx_write(hdptx, CMN_REG003F, 0x83); ++ hdptx_write(hdptx, CMN_REG0040, 0x06); ++ hdptx_write(hdptx, CMN_REG0041, 0x20); ++ hdptx_write(hdptx, CMN_REG0042, 0x78); ++ hdptx_write(hdptx, CMN_REG0043, 0x00); ++ hdptx_write(hdptx, CMN_REG0044, 0x46); ++ hdptx_write(hdptx, CMN_REG0045, 0x24); ++ hdptx_write(hdptx, CMN_REG0046, 0xff); ++ hdptx_write(hdptx, CMN_REG0047, 0x00); ++ hdptx_write(hdptx, CMN_REG0048, 0x44); ++ hdptx_write(hdptx, CMN_REG0049, 0xfa); ++ hdptx_write(hdptx, CMN_REG004A, 0x08); ++ hdptx_write(hdptx, CMN_REG004B, 0x00); ++ hdptx_write(hdptx, CMN_REG004C, 0x01); ++ hdptx_write(hdptx, CMN_REG004D, 0x64); ++ hdptx_write(hdptx, CMN_REG004E, 0x34); ++ hdptx_write(hdptx, CMN_REG004F, 0x00); ++ hdptx_write(hdptx, CMN_REG0050, 0x00); ++ ++ hdptx_write(hdptx, CMN_REG0051, cfg->pms_mdiv); ++ hdptx_write(hdptx, CMN_REG0055, cfg->pms_mdiv_afc); ++ ++ hdptx_write(hdptx, CMN_REG0059, (cfg->pms_pdiv << 4) | cfg->pms_refdiv); ++ ++ hdptx_write(hdptx, CMN_REG005A, (cfg->pms_sdiv << 4)); ++ ++ hdptx_write(hdptx, CMN_REG005C, 0x25); ++ hdptx_write(hdptx, CMN_REG005D, 0x0c); ++ hdptx_write(hdptx, CMN_REG005E, 0x4f); ++ hdptx_update_bits(hdptx, CMN_REG005E, ROPLL_SDM_EN_MASK, ++ ROPLL_SDM_EN(cfg->sdm_en)); ++ if (!cfg->sdm_en) ++ hdptx_update_bits(hdptx, CMN_REG005E, 0xf, 0); ++ ++ hdptx_write(hdptx, CMN_REG005F, 0x01); ++ ++ hdptx_update_bits(hdptx, CMN_REG0064, ROPLL_SDM_NUM_SIGN_RBR_MASK, ++ ROPLL_SDM_NUM_SIGN_RBR(cfg->sdm_num_sign)); ++ hdptx_write(hdptx, CMN_REG0065, cfg->sdm_num); ++ hdptx_write(hdptx, CMN_REG0060, cfg->sdm_deno); ++ ++ hdptx_update_bits(hdptx, CMN_REG0069, ROPLL_SDC_N_RBR_MASK, ++ ROPLL_SDC_N_RBR(cfg->sdc_n)); ++ ++ hdptx_write(hdptx, CMN_REG006C, cfg->sdc_num); ++ hdptx_write(hdptx, CMN_REG0070, cfg->sdc_deno); ++ ++ hdptx_write(hdptx, CMN_REG006B, 0x04); ++ ++ hdptx_write(hdptx, CMN_REG0073, 0x30); ++ hdptx_write(hdptx, CMN_REG0074, 0x04); ++ hdptx_write(hdptx, CMN_REG0075, 0x20); ++ hdptx_write(hdptx, CMN_REG0076, 0x30); ++ hdptx_write(hdptx, CMN_REG0077, 0x08); ++ hdptx_write(hdptx, CMN_REG0078, 0x0c); ++ hdptx_write(hdptx, CMN_REG0079, 0x00); ++ hdptx_write(hdptx, CMN_REG007B, 0x00); ++ hdptx_write(hdptx, CMN_REG007C, 0x00); ++ hdptx_write(hdptx, CMN_REG007D, 0x00); ++ hdptx_write(hdptx, CMN_REG007E, 0x00); ++ hdptx_write(hdptx, CMN_REG007F, 0x00); ++ hdptx_write(hdptx, CMN_REG0080, 0x00); ++ hdptx_write(hdptx, CMN_REG0081, 0x01); ++ hdptx_write(hdptx, CMN_REG0082, 0x04); ++ hdptx_write(hdptx, CMN_REG0083, 0x24); ++ hdptx_write(hdptx, CMN_REG0084, 0x20); ++ hdptx_write(hdptx, CMN_REG0085, 0x03); ++ ++ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_POSTDIV_SEL_MASK, ++ PLL_PCG_POSTDIV_SEL(cfg->pms_sdiv)); ++ ++ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_SEL_MASK, ++ PLL_PCG_CLK_SEL(color_depth)); ++ ++ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_EN, PLL_PCG_CLK_EN); ++ ++ hdptx_write(hdptx, CMN_REG0087, 0x04); ++ hdptx_write(hdptx, CMN_REG0089, 0x00); ++ hdptx_write(hdptx, CMN_REG008A, 0x55); ++ hdptx_write(hdptx, CMN_REG008B, 0x25); ++ hdptx_write(hdptx, CMN_REG008C, 0x2c); ++ hdptx_write(hdptx, CMN_REG008D, 0x22); ++ hdptx_write(hdptx, CMN_REG008E, 0x14); ++ hdptx_write(hdptx, CMN_REG008F, 0x20); ++ hdptx_write(hdptx, CMN_REG0090, 0x00); ++ hdptx_write(hdptx, CMN_REG0091, 0x00); ++ hdptx_write(hdptx, CMN_REG0092, 0x00); ++ hdptx_write(hdptx, CMN_REG0093, 0x00); ++ hdptx_write(hdptx, CMN_REG0095, 0x00); ++ hdptx_write(hdptx, CMN_REG0097, 0x02); ++ hdptx_write(hdptx, CMN_REG0099, 0x04); ++ hdptx_write(hdptx, CMN_REG009A, 0x11); ++ hdptx_write(hdptx, CMN_REG009B, 0x00); ++ ++ return hdptx_post_enable_pll(hdptx); ++} ++ ++static int hdptx_ropll_tmds_mode_config(struct rockchip_hdptx_phy *hdptx, u32 rate) ++{ ++ u32 bit_rate = rate & DATA_RATE_MASK; ++ ++ if (!(hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE)) { ++ int ret; ++ ++ ret = hdptx_ropll_cmn_config(hdptx, bit_rate); ++ if (ret) ++ return ret; ++ } ++ ++ hdptx_write(hdptx, SB_REG0114, 0x00); ++ hdptx_write(hdptx, SB_REG0115, 0x00); ++ hdptx_write(hdptx, SB_REG0116, 0x00); ++ hdptx_write(hdptx, SB_REG0117, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0200, 0x06); ++ ++ if (bit_rate >= 3400000) { ++ /* For 1/40 bitrate clk */ ++ hdptx_write(hdptx, LNTOP_REG0201, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0202, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0203, 0x0f); ++ hdptx_write(hdptx, LNTOP_REG0204, 0xff); ++ hdptx_write(hdptx, LNTOP_REG0205, 0xff); ++ } else { ++ /* For 1/10 bitrate clk */ ++ hdptx_write(hdptx, LNTOP_REG0201, 0x07); ++ hdptx_write(hdptx, LNTOP_REG0202, 0xc1); ++ hdptx_write(hdptx, LNTOP_REG0203, 0xf0); ++ hdptx_write(hdptx, LNTOP_REG0204, 0x7c); ++ hdptx_write(hdptx, LNTOP_REG0205, 0x1f); ++ } ++ ++ hdptx_write(hdptx, LNTOP_REG0206, 0x07); ++ hdptx_write(hdptx, LNTOP_REG0207, 0x0f); ++ hdptx_write(hdptx, LANE_REG0303, 0x0c); ++ hdptx_write(hdptx, LANE_REG0307, 0x20); ++ hdptx_write(hdptx, LANE_REG030A, 0x17); ++ hdptx_write(hdptx, LANE_REG030B, 0x77); ++ hdptx_write(hdptx, LANE_REG030C, 0x77); ++ hdptx_write(hdptx, LANE_REG030D, 0x77); ++ hdptx_write(hdptx, LANE_REG030E, 0x38); ++ hdptx_write(hdptx, LANE_REG0310, 0x03); ++ hdptx_write(hdptx, LANE_REG0311, 0x0f); ++ hdptx_write(hdptx, LANE_REG0312, 0x00); ++ hdptx_write(hdptx, LANE_REG0316, 0x02); ++ hdptx_write(hdptx, LANE_REG031B, 0x01); ++ hdptx_write(hdptx, LANE_REG031E, 0x00); ++ hdptx_write(hdptx, LANE_REG031F, 0x15); ++ hdptx_write(hdptx, LANE_REG0320, 0xa0); ++ hdptx_write(hdptx, LANE_REG0403, 0x0c); ++ hdptx_write(hdptx, LANE_REG0407, 0x20); ++ hdptx_write(hdptx, LANE_REG040A, 0x17); ++ hdptx_write(hdptx, LANE_REG040B, 0x77); ++ hdptx_write(hdptx, LANE_REG040C, 0x77); ++ hdptx_write(hdptx, LANE_REG040D, 0x77); ++ hdptx_write(hdptx, LANE_REG040E, 0x38); ++ hdptx_write(hdptx, LANE_REG0410, 0x03); ++ hdptx_write(hdptx, LANE_REG0411, 0x0f); ++ hdptx_write(hdptx, LANE_REG0412, 0x00); ++ hdptx_write(hdptx, LANE_REG0416, 0x02); ++ hdptx_write(hdptx, LANE_REG041B, 0x01); ++ hdptx_write(hdptx, LANE_REG041E, 0x00); ++ hdptx_write(hdptx, LANE_REG041F, 0x15); ++ hdptx_write(hdptx, LANE_REG0420, 0xa0); ++ hdptx_write(hdptx, LANE_REG0503, 0x0c); ++ hdptx_write(hdptx, LANE_REG0507, 0x20); ++ hdptx_write(hdptx, LANE_REG050A, 0x17); ++ hdptx_write(hdptx, LANE_REG050B, 0x77); ++ hdptx_write(hdptx, LANE_REG050C, 0x77); ++ hdptx_write(hdptx, LANE_REG050D, 0x77); ++ hdptx_write(hdptx, LANE_REG050E, 0x38); ++ hdptx_write(hdptx, LANE_REG0510, 0x03); ++ hdptx_write(hdptx, LANE_REG0511, 0x0f); ++ hdptx_write(hdptx, LANE_REG0512, 0x00); ++ hdptx_write(hdptx, LANE_REG0516, 0x02); ++ hdptx_write(hdptx, LANE_REG051B, 0x01); ++ hdptx_write(hdptx, LANE_REG051E, 0x00); ++ hdptx_write(hdptx, LANE_REG051F, 0x15); ++ hdptx_write(hdptx, LANE_REG0520, 0xa0); ++ hdptx_write(hdptx, LANE_REG0603, 0x0c); ++ hdptx_write(hdptx, LANE_REG0607, 0x20); ++ hdptx_write(hdptx, LANE_REG060A, 0x17); ++ hdptx_write(hdptx, LANE_REG060B, 0x77); ++ hdptx_write(hdptx, LANE_REG060C, 0x77); ++ hdptx_write(hdptx, LANE_REG060D, 0x77); ++ hdptx_write(hdptx, LANE_REG060E, 0x38); ++ hdptx_write(hdptx, LANE_REG0610, 0x03); ++ hdptx_write(hdptx, LANE_REG0611, 0x0f); ++ hdptx_write(hdptx, LANE_REG0612, 0x00); ++ hdptx_write(hdptx, LANE_REG0616, 0x02); ++ hdptx_write(hdptx, LANE_REG061B, 0x01); ++ hdptx_write(hdptx, LANE_REG061E, 0x08); ++ hdptx_write(hdptx, LANE_REG061F, 0x15); ++ hdptx_write(hdptx, LANE_REG0620, 0xa0); ++ ++ hdptx_write(hdptx, LANE_REG0303, 0x2f); ++ hdptx_write(hdptx, LANE_REG0403, 0x2f); ++ hdptx_write(hdptx, LANE_REG0503, 0x2f); ++ hdptx_write(hdptx, LANE_REG0603, 0x2f); ++ hdptx_write(hdptx, LANE_REG0305, 0x03); ++ hdptx_write(hdptx, LANE_REG0405, 0x03); ++ hdptx_write(hdptx, LANE_REG0505, 0x03); ++ hdptx_write(hdptx, LANE_REG0605, 0x03); ++ hdptx_write(hdptx, LANE_REG0306, 0x1c); ++ hdptx_write(hdptx, LANE_REG0406, 0x1c); ++ hdptx_write(hdptx, LANE_REG0506, 0x1c); ++ hdptx_write(hdptx, LANE_REG0606, 0x1c); ++ ++ if (hdptx->earc_en) ++ hdptx_earc_config(hdptx); ++ ++ return hdptx_post_enable_lane(hdptx); ++} ++ ++static int hdptx_ropll_frl_mode_config(struct rockchip_hdptx_phy *hdptx, u32 rate) ++{ ++ u32 bit_rate = rate & DATA_RATE_MASK; ++ u8 color_depth = (rate & COLOR_DEPTH_MASK) ? 1 : 0; ++ struct ropll_config *cfg = ropll_frl_cfg; ++ ++ for (; cfg->bit_rate != ~0; cfg++) ++ if (bit_rate == cfg->bit_rate) ++ break; ++ ++ if (cfg->bit_rate == ~0) { ++ dev_err(hdptx->dev, "%s can't find pll cfg\n", __func__); ++ return -EINVAL; ++ } ++ ++ hdptx_pre_power_up(hdptx); ++ ++ reset_control_assert(hdptx->ropll_reset); ++ usleep_range(10, 20); ++ reset_control_deassert(hdptx->ropll_reset); ++ ++ hdptx_write(hdptx, CMN_REG0008, 0x00); ++ hdptx_write(hdptx, CMN_REG0009, 0x0c); ++ hdptx_write(hdptx, CMN_REG000A, 0x83); ++ hdptx_write(hdptx, CMN_REG000B, 0x06); ++ hdptx_write(hdptx, CMN_REG000C, 0x20); ++ hdptx_write(hdptx, CMN_REG000D, 0xb8); ++ hdptx_write(hdptx, CMN_REG000E, 0x0f); ++ hdptx_write(hdptx, CMN_REG000F, 0x0f); ++ hdptx_write(hdptx, CMN_REG0010, 0x04); ++ hdptx_write(hdptx, CMN_REG0011, 0x00); ++ hdptx_write(hdptx, CMN_REG0012, 0x26); ++ hdptx_write(hdptx, CMN_REG0013, 0x22); ++ hdptx_write(hdptx, CMN_REG0014, 0x24); ++ hdptx_write(hdptx, CMN_REG0015, 0x77); ++ hdptx_write(hdptx, CMN_REG0016, 0x08); ++ hdptx_write(hdptx, CMN_REG0017, 0x00); ++ hdptx_write(hdptx, CMN_REG0018, 0x04); ++ hdptx_write(hdptx, CMN_REG0019, 0x48); ++ hdptx_write(hdptx, CMN_REG001A, 0x01); ++ hdptx_write(hdptx, CMN_REG001B, 0x00); ++ hdptx_write(hdptx, CMN_REG001C, 0x01); ++ hdptx_write(hdptx, CMN_REG001D, 0x64); ++ hdptx_write(hdptx, CMN_REG001E, 0x14); ++ hdptx_write(hdptx, CMN_REG001F, 0x00); ++ hdptx_write(hdptx, CMN_REG0020, 0x00); ++ hdptx_write(hdptx, CMN_REG0021, 0x00); ++ hdptx_write(hdptx, CMN_REG0022, 0x11); ++ hdptx_write(hdptx, CMN_REG0023, 0x00); ++ hdptx_write(hdptx, CMN_REG0025, 0x00); ++ hdptx_write(hdptx, CMN_REG0026, 0x53); ++ hdptx_write(hdptx, CMN_REG0027, 0x00); ++ hdptx_write(hdptx, CMN_REG0028, 0x00); ++ hdptx_write(hdptx, CMN_REG0029, 0x01); ++ hdptx_write(hdptx, CMN_REG002A, 0x01); ++ hdptx_write(hdptx, CMN_REG002B, 0x00); ++ hdptx_write(hdptx, CMN_REG002C, 0x00); ++ hdptx_write(hdptx, CMN_REG002D, 0x00); ++ hdptx_write(hdptx, CMN_REG002E, 0x00); ++ hdptx_write(hdptx, CMN_REG002F, 0x04); ++ hdptx_write(hdptx, CMN_REG0030, 0x00); ++ hdptx_write(hdptx, CMN_REG0031, 0x20); ++ hdptx_write(hdptx, CMN_REG0032, 0x30); ++ hdptx_write(hdptx, CMN_REG0033, 0x0b); ++ hdptx_write(hdptx, CMN_REG0034, 0x23); ++ hdptx_write(hdptx, CMN_REG0035, 0x00); ++ hdptx_write(hdptx, CMN_REG0038, 0x00); ++ hdptx_write(hdptx, CMN_REG0039, 0x00); ++ hdptx_write(hdptx, CMN_REG003A, 0x00); ++ hdptx_write(hdptx, CMN_REG003B, 0x00); ++ hdptx_write(hdptx, CMN_REG003C, 0x80); ++ hdptx_write(hdptx, CMN_REG003D, 0x40); ++ hdptx_write(hdptx, CMN_REG003E, 0x0c); ++ hdptx_write(hdptx, CMN_REG003F, 0x83); ++ hdptx_write(hdptx, CMN_REG0040, 0x06); ++ hdptx_write(hdptx, CMN_REG0041, 0x20); ++ hdptx_write(hdptx, CMN_REG0042, 0xb8); ++ hdptx_write(hdptx, CMN_REG0043, 0x00); ++ hdptx_write(hdptx, CMN_REG0044, 0x46); ++ hdptx_write(hdptx, CMN_REG0045, 0x24); ++ hdptx_write(hdptx, CMN_REG0046, 0xff); ++ hdptx_write(hdptx, CMN_REG0047, 0x00); ++ hdptx_write(hdptx, CMN_REG0048, 0x44); ++ hdptx_write(hdptx, CMN_REG0049, 0xfa); ++ hdptx_write(hdptx, CMN_REG004A, 0x08); ++ hdptx_write(hdptx, CMN_REG004B, 0x00); ++ hdptx_write(hdptx, CMN_REG004C, 0x01); ++ hdptx_write(hdptx, CMN_REG004D, 0x64); ++ hdptx_write(hdptx, CMN_REG004E, 0x14); ++ hdptx_write(hdptx, CMN_REG004F, 0x00); ++ hdptx_write(hdptx, CMN_REG0050, 0x00); ++ hdptx_write(hdptx, CMN_REG0051, cfg->pms_mdiv); ++ hdptx_write(hdptx, CMN_REG0055, cfg->pms_mdiv_afc); ++ hdptx_write(hdptx, CMN_REG0059, (cfg->pms_pdiv << 4) | cfg->pms_refdiv); ++ hdptx_write(hdptx, CMN_REG005A, (cfg->pms_sdiv << 4)); ++ hdptx_write(hdptx, CMN_REG005C, 0x25); ++ hdptx_write(hdptx, CMN_REG005D, 0x0c); ++ hdptx_update_bits(hdptx, CMN_REG005E, ROPLL_SDM_EN_MASK, ++ ROPLL_SDM_EN(cfg->sdm_en)); ++ if (!cfg->sdm_en) ++ hdptx_update_bits(hdptx, CMN_REG005E, 0xf, 0); ++ hdptx_write(hdptx, CMN_REG005F, 0x01); ++ hdptx_update_bits(hdptx, CMN_REG0064, ROPLL_SDM_NUM_SIGN_RBR_MASK, ++ ROPLL_SDM_NUM_SIGN_RBR(cfg->sdm_num_sign)); ++ hdptx_write(hdptx, CMN_REG0065, cfg->sdm_num); ++ hdptx_write(hdptx, CMN_REG0060, cfg->sdm_deno); ++ hdptx_update_bits(hdptx, CMN_REG0069, ROPLL_SDC_N_RBR_MASK, ++ ROPLL_SDC_N_RBR(cfg->sdc_n)); ++ hdptx_write(hdptx, CMN_REG006C, cfg->sdc_num); ++ hdptx_write(hdptx, CMN_REG0070, cfg->sdc_deno); ++ hdptx_write(hdptx, CMN_REG006B, 0x04); ++ hdptx_write(hdptx, CMN_REG0073, 0x30); ++ hdptx_write(hdptx, CMN_REG0074, 0x00); ++ hdptx_write(hdptx, CMN_REG0075, 0x20); ++ hdptx_write(hdptx, CMN_REG0076, 0x30); ++ hdptx_write(hdptx, CMN_REG0077, 0x08); ++ hdptx_write(hdptx, CMN_REG0078, 0x0c); ++ hdptx_write(hdptx, CMN_REG0079, 0x00); ++ hdptx_write(hdptx, CMN_REG007B, 0x00); ++ hdptx_write(hdptx, CMN_REG007C, 0x00); ++ hdptx_write(hdptx, CMN_REG007D, 0x00); ++ hdptx_write(hdptx, CMN_REG007E, 0x00); ++ hdptx_write(hdptx, CMN_REG007F, 0x00); ++ hdptx_write(hdptx, CMN_REG0080, 0x00); ++ hdptx_write(hdptx, CMN_REG0081, 0x09); ++ hdptx_write(hdptx, CMN_REG0082, 0x04); ++ hdptx_write(hdptx, CMN_REG0083, 0x24); ++ hdptx_write(hdptx, CMN_REG0084, 0x20); ++ hdptx_write(hdptx, CMN_REG0085, 0x03); ++ hdptx_write(hdptx, CMN_REG0086, 0x01); ++ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_POSTDIV_SEL_MASK, ++ PLL_PCG_POSTDIV_SEL(cfg->pms_sdiv)); ++ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_SEL_MASK, ++ PLL_PCG_CLK_SEL(color_depth)); ++ hdptx_write(hdptx, CMN_REG0087, 0x0c); ++ hdptx_write(hdptx, CMN_REG0089, 0x00); ++ hdptx_write(hdptx, CMN_REG008A, 0x55); ++ hdptx_write(hdptx, CMN_REG008B, 0x25); ++ hdptx_write(hdptx, CMN_REG008C, 0x2c); ++ hdptx_write(hdptx, CMN_REG008D, 0x22); ++ hdptx_write(hdptx, CMN_REG008E, 0x14); ++ hdptx_write(hdptx, CMN_REG008F, 0x20); ++ hdptx_write(hdptx, CMN_REG0090, 0x00); ++ hdptx_write(hdptx, CMN_REG0091, 0x00); ++ hdptx_write(hdptx, CMN_REG0092, 0x00); ++ hdptx_write(hdptx, CMN_REG0093, 0x00); ++ hdptx_write(hdptx, CMN_REG0094, 0x00); ++ hdptx_write(hdptx, CMN_REG0097, 0x02); ++ hdptx_write(hdptx, CMN_REG0099, 0x04); ++ hdptx_write(hdptx, CMN_REG009A, 0x11); ++ hdptx_write(hdptx, CMN_REG009B, 0x10); ++ hdptx_write(hdptx, SB_REG0114, 0x00); ++ hdptx_write(hdptx, SB_REG0115, 0x00); ++ hdptx_write(hdptx, SB_REG0116, 0x00); ++ hdptx_write(hdptx, SB_REG0117, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0200, 0x04); ++ hdptx_write(hdptx, LNTOP_REG0201, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0202, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0203, 0xf0); ++ hdptx_write(hdptx, LNTOP_REG0204, 0xff); ++ hdptx_write(hdptx, LNTOP_REG0205, 0xff); ++ hdptx_write(hdptx, LNTOP_REG0206, 0x05); ++ hdptx_write(hdptx, LNTOP_REG0207, 0x0f); ++ hdptx_write(hdptx, LANE_REG0303, 0x0c); ++ hdptx_write(hdptx, LANE_REG0307, 0x20); ++ hdptx_write(hdptx, LANE_REG030A, 0x17); ++ hdptx_write(hdptx, LANE_REG030B, 0x77); ++ hdptx_write(hdptx, LANE_REG030C, 0x77); ++ hdptx_write(hdptx, LANE_REG030D, 0x77); ++ hdptx_write(hdptx, LANE_REG030E, 0x38); ++ hdptx_write(hdptx, LANE_REG0310, 0x03); ++ hdptx_write(hdptx, LANE_REG0311, 0x0f); ++ hdptx_write(hdptx, LANE_REG0312, 0x3c); ++ hdptx_write(hdptx, LANE_REG0316, 0x02); ++ hdptx_write(hdptx, LANE_REG031B, 0x01); ++ hdptx_write(hdptx, LANE_REG031F, 0x15); ++ hdptx_write(hdptx, LANE_REG0320, 0xa0); ++ hdptx_write(hdptx, LANE_REG0403, 0x0c); ++ hdptx_write(hdptx, LANE_REG0407, 0x20); ++ hdptx_write(hdptx, LANE_REG040A, 0x17); ++ hdptx_write(hdptx, LANE_REG040B, 0x77); ++ hdptx_write(hdptx, LANE_REG040C, 0x77); ++ hdptx_write(hdptx, LANE_REG040D, 0x77); ++ hdptx_write(hdptx, LANE_REG040E, 0x38); ++ hdptx_write(hdptx, LANE_REG0410, 0x03); ++ hdptx_write(hdptx, LANE_REG0411, 0x0f); ++ hdptx_write(hdptx, LANE_REG0412, 0x3c); ++ hdptx_write(hdptx, LANE_REG0416, 0x02); ++ hdptx_write(hdptx, LANE_REG041B, 0x01); ++ hdptx_write(hdptx, LANE_REG041F, 0x15); ++ hdptx_write(hdptx, LANE_REG0420, 0xa0); ++ hdptx_write(hdptx, LANE_REG0503, 0x0c); ++ hdptx_write(hdptx, LANE_REG0507, 0x20); ++ hdptx_write(hdptx, LANE_REG050A, 0x17); ++ hdptx_write(hdptx, LANE_REG050B, 0x77); ++ hdptx_write(hdptx, LANE_REG050C, 0x77); ++ hdptx_write(hdptx, LANE_REG050D, 0x77); ++ hdptx_write(hdptx, LANE_REG050E, 0x38); ++ hdptx_write(hdptx, LANE_REG0510, 0x03); ++ hdptx_write(hdptx, LANE_REG0511, 0x0f); ++ hdptx_write(hdptx, LANE_REG0512, 0x3c); ++ hdptx_write(hdptx, LANE_REG0516, 0x02); ++ hdptx_write(hdptx, LANE_REG051B, 0x01); ++ hdptx_write(hdptx, LANE_REG051F, 0x15); ++ hdptx_write(hdptx, LANE_REG0520, 0xa0); ++ hdptx_write(hdptx, LANE_REG0603, 0x0c); ++ hdptx_write(hdptx, LANE_REG0607, 0x20); ++ hdptx_write(hdptx, LANE_REG060A, 0x17); ++ hdptx_write(hdptx, LANE_REG060B, 0x77); ++ hdptx_write(hdptx, LANE_REG060C, 0x77); ++ hdptx_write(hdptx, LANE_REG060D, 0x77); ++ hdptx_write(hdptx, LANE_REG060E, 0x38); ++ hdptx_write(hdptx, LANE_REG0610, 0x03); ++ hdptx_write(hdptx, LANE_REG0611, 0x0f); ++ hdptx_write(hdptx, LANE_REG0612, 0x3c); ++ hdptx_write(hdptx, LANE_REG0616, 0x02); ++ hdptx_write(hdptx, LANE_REG061B, 0x01); ++ hdptx_write(hdptx, LANE_REG061F, 0x15); ++ hdptx_write(hdptx, LANE_REG0620, 0xa0); ++ ++ if (hdptx->earc_en) ++ hdptx_earc_config(hdptx); ++ ++ return hdptx_post_power_up(hdptx); ++} ++ ++static int hdptx_lcpll_frl_mode_config(struct rockchip_hdptx_phy *hdptx, u32 rate) ++{ ++ u32 bit_rate = rate & DATA_RATE_MASK; ++ u8 color_depth = (rate & COLOR_DEPTH_MASK) ? 1 : 0; ++ struct lcpll_config *cfg = lcpll_cfg; ++ ++ for (; cfg->bit_rate != ~0; cfg++) ++ if (bit_rate == cfg->bit_rate) ++ break; ++ ++ if (cfg->bit_rate == ~0) ++ return -EINVAL; ++ ++ hdptx_pre_power_up(hdptx); ++ ++ hdptx_update_bits(hdptx, CMN_REG0008, LCPLL_EN_MASK | ++ LCPLL_LCVCO_MODE_EN_MASK, LCPLL_EN(1) | ++ LCPLL_LCVCO_MODE_EN(cfg->lcvco_mode_en)); ++ hdptx_write(hdptx, CMN_REG0009, 0x0c); ++ hdptx_write(hdptx, CMN_REG000A, 0x83); ++ hdptx_write(hdptx, CMN_REG000B, 0x06); ++ hdptx_write(hdptx, CMN_REG000C, 0x20); ++ hdptx_write(hdptx, CMN_REG000D, 0xb8); ++ hdptx_write(hdptx, CMN_REG000E, 0x0f); ++ hdptx_write(hdptx, CMN_REG000F, 0x0f); ++ hdptx_write(hdptx, CMN_REG0010, 0x04); ++ hdptx_write(hdptx, CMN_REG0011, 0x00); ++ hdptx_write(hdptx, CMN_REG0012, 0x26); ++ hdptx_write(hdptx, CMN_REG0013, 0x22); ++ hdptx_write(hdptx, CMN_REG0014, 0x24); ++ hdptx_write(hdptx, CMN_REG0015, 0x77); ++ hdptx_write(hdptx, CMN_REG0016, 0x08); ++ hdptx_write(hdptx, CMN_REG0017, 0x00); ++ hdptx_write(hdptx, CMN_REG0018, 0x04); ++ hdptx_write(hdptx, CMN_REG0019, 0x48); ++ hdptx_write(hdptx, CMN_REG001A, 0x01); ++ hdptx_write(hdptx, CMN_REG001B, 0x00); ++ hdptx_write(hdptx, CMN_REG001C, 0x01); ++ hdptx_write(hdptx, CMN_REG001D, 0x64); ++ hdptx_update_bits(hdptx, CMN_REG001E, LCPLL_PI_EN_MASK | ++ LCPLL_100M_CLK_EN_MASK, ++ LCPLL_PI_EN(cfg->pi_en) | ++ LCPLL_100M_CLK_EN(cfg->clk_en_100m)); ++ hdptx_write(hdptx, CMN_REG001F, 0x00); ++ hdptx_write(hdptx, CMN_REG0020, cfg->pms_mdiv); ++ hdptx_write(hdptx, CMN_REG0021, cfg->pms_mdiv_afc); ++ hdptx_write(hdptx, CMN_REG0022, (cfg->pms_pdiv << 4) | cfg->pms_refdiv); ++ hdptx_write(hdptx, CMN_REG0023, (cfg->pms_sdiv << 4) | cfg->pms_sdiv); ++ hdptx_write(hdptx, CMN_REG0025, 0x10); ++ hdptx_write(hdptx, CMN_REG0026, 0x53); ++ hdptx_write(hdptx, CMN_REG0027, 0x01); ++ hdptx_write(hdptx, CMN_REG0028, 0x0d); ++ hdptx_write(hdptx, CMN_REG0029, 0x01); ++ hdptx_write(hdptx, CMN_REG002A, cfg->sdm_deno); ++ hdptx_write(hdptx, CMN_REG002B, cfg->sdm_num_sign); ++ hdptx_write(hdptx, CMN_REG002C, cfg->sdm_num); ++ hdptx_update_bits(hdptx, CMN_REG002D, LCPLL_SDC_N_MASK, ++ LCPLL_SDC_N(cfg->sdc_n)); ++ hdptx_write(hdptx, CMN_REG002E, 0x02); ++ hdptx_write(hdptx, CMN_REG002F, 0x0d); ++ hdptx_write(hdptx, CMN_REG0030, 0x00); ++ hdptx_write(hdptx, CMN_REG0031, 0x20); ++ hdptx_write(hdptx, CMN_REG0032, 0x30); ++ hdptx_write(hdptx, CMN_REG0033, 0x0b); ++ hdptx_write(hdptx, CMN_REG0034, 0x23); ++ hdptx_write(hdptx, CMN_REG0035, 0x00); ++ hdptx_write(hdptx, CMN_REG0038, 0x00); ++ hdptx_write(hdptx, CMN_REG0039, 0x00); ++ hdptx_write(hdptx, CMN_REG003A, 0x00); ++ hdptx_write(hdptx, CMN_REG003B, 0x00); ++ hdptx_write(hdptx, CMN_REG003C, 0x80); ++ hdptx_write(hdptx, CMN_REG003D, 0x00); ++ hdptx_write(hdptx, CMN_REG003E, 0x0c); ++ hdptx_write(hdptx, CMN_REG003F, 0x83); ++ hdptx_write(hdptx, CMN_REG0040, 0x06); ++ hdptx_write(hdptx, CMN_REG0041, 0x20); ++ hdptx_write(hdptx, CMN_REG0042, 0xb8); ++ hdptx_write(hdptx, CMN_REG0043, 0x00); ++ hdptx_write(hdptx, CMN_REG0044, 0x46); ++ hdptx_write(hdptx, CMN_REG0045, 0x24); ++ hdptx_write(hdptx, CMN_REG0046, 0xff); ++ hdptx_write(hdptx, CMN_REG0047, 0x00); ++ hdptx_write(hdptx, CMN_REG0048, 0x44); ++ hdptx_write(hdptx, CMN_REG0049, 0xfa); ++ hdptx_write(hdptx, CMN_REG004A, 0x08); ++ hdptx_write(hdptx, CMN_REG004B, 0x00); ++ hdptx_write(hdptx, CMN_REG004C, 0x01); ++ hdptx_write(hdptx, CMN_REG004D, 0x64); ++ hdptx_write(hdptx, CMN_REG004E, 0x14); ++ hdptx_write(hdptx, CMN_REG004F, 0x00); ++ hdptx_write(hdptx, CMN_REG0050, 0x00); ++ hdptx_write(hdptx, CMN_REG0051, 0x00); ++ hdptx_write(hdptx, CMN_REG0055, 0x00); ++ hdptx_write(hdptx, CMN_REG0059, 0x11); ++ hdptx_write(hdptx, CMN_REG005A, 0x03); ++ hdptx_write(hdptx, CMN_REG005C, 0x05); ++ hdptx_write(hdptx, CMN_REG005D, 0x0c); ++ hdptx_write(hdptx, CMN_REG005E, 0x07); ++ hdptx_write(hdptx, CMN_REG005F, 0x01); ++ hdptx_write(hdptx, CMN_REG0060, 0x01); ++ hdptx_write(hdptx, CMN_REG0064, 0x07); ++ hdptx_write(hdptx, CMN_REG0065, 0x00); ++ hdptx_write(hdptx, CMN_REG0069, 0x00); ++ hdptx_write(hdptx, CMN_REG006B, 0x04); ++ hdptx_write(hdptx, CMN_REG006C, 0x00); ++ hdptx_write(hdptx, CMN_REG0070, 0x01); ++ hdptx_write(hdptx, CMN_REG0073, 0x30); ++ hdptx_write(hdptx, CMN_REG0074, 0x00); ++ hdptx_write(hdptx, CMN_REG0075, 0x20); ++ hdptx_write(hdptx, CMN_REG0076, 0x30); ++ hdptx_write(hdptx, CMN_REG0077, 0x08); ++ hdptx_write(hdptx, CMN_REG0078, 0x0c); ++ hdptx_write(hdptx, CMN_REG0079, 0x00); ++ hdptx_write(hdptx, CMN_REG007B, 0x00); ++ hdptx_write(hdptx, CMN_REG007C, 0x00); ++ hdptx_write(hdptx, CMN_REG007D, 0x00); ++ hdptx_write(hdptx, CMN_REG007E, 0x00); ++ hdptx_write(hdptx, CMN_REG007F, 0x00); ++ hdptx_write(hdptx, CMN_REG0080, 0x00); ++ hdptx_write(hdptx, CMN_REG0081, 0x09); ++ hdptx_write(hdptx, CMN_REG0082, 0x04); ++ hdptx_write(hdptx, CMN_REG0083, 0x24); ++ hdptx_write(hdptx, CMN_REG0084, 0x20); ++ hdptx_write(hdptx, CMN_REG0085, 0x03); ++ hdptx_write(hdptx, CMN_REG0086, 0x01); ++ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_POSTDIV_SEL_MASK, ++ PLL_PCG_POSTDIV_SEL(cfg->pms_sdiv)); ++ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_SEL_MASK, ++ PLL_PCG_CLK_SEL(color_depth)); ++ hdptx_write(hdptx, CMN_REG0087, 0x0c); ++ hdptx_write(hdptx, CMN_REG0089, 0x02); ++ hdptx_write(hdptx, CMN_REG008A, 0x55); ++ hdptx_write(hdptx, CMN_REG008B, 0x25); ++ hdptx_write(hdptx, CMN_REG008C, 0x2c); ++ hdptx_write(hdptx, CMN_REG008D, 0x22); ++ hdptx_write(hdptx, CMN_REG008E, 0x14); ++ hdptx_write(hdptx, CMN_REG008F, 0x20); ++ hdptx_write(hdptx, CMN_REG0090, 0x00); ++ hdptx_write(hdptx, CMN_REG0091, 0x00); ++ hdptx_write(hdptx, CMN_REG0092, 0x00); ++ hdptx_write(hdptx, CMN_REG0093, 0x00); ++ hdptx_write(hdptx, CMN_REG0095, 0x00); ++ hdptx_write(hdptx, CMN_REG0097, 0x00); ++ hdptx_write(hdptx, CMN_REG0099, 0x00); ++ hdptx_write(hdptx, CMN_REG009A, 0x11); ++ hdptx_write(hdptx, CMN_REG009B, 0x10); ++ hdptx_write(hdptx, SB_REG0114, 0x00); ++ hdptx_write(hdptx, SB_REG0115, 0x00); ++ hdptx_write(hdptx, SB_REG0116, 0x00); ++ hdptx_write(hdptx, SB_REG0117, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0200, 0x04); ++ hdptx_write(hdptx, LNTOP_REG0201, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0202, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0203, 0xf0); ++ hdptx_write(hdptx, LNTOP_REG0204, 0xff); ++ hdptx_write(hdptx, LNTOP_REG0205, 0xff); ++ hdptx_write(hdptx, LNTOP_REG0206, 0x05); ++ hdptx_write(hdptx, LNTOP_REG0207, 0x0f); ++ hdptx_write(hdptx, LANE_REG0303, 0x0c); ++ hdptx_write(hdptx, LANE_REG0307, 0x20); ++ hdptx_write(hdptx, LANE_REG030A, 0x17); ++ hdptx_write(hdptx, LANE_REG030B, 0x77); ++ hdptx_write(hdptx, LANE_REG030C, 0x77); ++ hdptx_write(hdptx, LANE_REG030D, 0x77); ++ hdptx_write(hdptx, LANE_REG030E, 0x38); ++ hdptx_write(hdptx, LANE_REG0310, 0x03); ++ hdptx_write(hdptx, LANE_REG0311, 0x0f); ++ hdptx_write(hdptx, LANE_REG0312, 0x3c); ++ hdptx_write(hdptx, LANE_REG0316, 0x02); ++ hdptx_write(hdptx, LANE_REG031B, 0x01); ++ hdptx_write(hdptx, LANE_REG031F, 0x15); ++ hdptx_write(hdptx, LANE_REG0320, 0xa0); ++ hdptx_write(hdptx, LANE_REG0403, 0x0c); ++ hdptx_write(hdptx, LANE_REG0407, 0x20); ++ hdptx_write(hdptx, LANE_REG040A, 0x17); ++ hdptx_write(hdptx, LANE_REG040B, 0x77); ++ hdptx_write(hdptx, LANE_REG040C, 0x77); ++ hdptx_write(hdptx, LANE_REG040D, 0x77); ++ hdptx_write(hdptx, LANE_REG040E, 0x38); ++ hdptx_write(hdptx, LANE_REG0410, 0x03); ++ hdptx_write(hdptx, LANE_REG0411, 0x0f); ++ hdptx_write(hdptx, LANE_REG0412, 0x3c); ++ hdptx_write(hdptx, LANE_REG0416, 0x02); ++ hdptx_write(hdptx, LANE_REG041B, 0x01); ++ hdptx_write(hdptx, LANE_REG041F, 0x15); ++ hdptx_write(hdptx, LANE_REG0420, 0xa0); ++ hdptx_write(hdptx, LANE_REG0503, 0x0c); ++ hdptx_write(hdptx, LANE_REG0507, 0x20); ++ hdptx_write(hdptx, LANE_REG050A, 0x17); ++ hdptx_write(hdptx, LANE_REG050B, 0x77); ++ hdptx_write(hdptx, LANE_REG050C, 0x77); ++ hdptx_write(hdptx, LANE_REG050D, 0x77); ++ hdptx_write(hdptx, LANE_REG050E, 0x38); ++ hdptx_write(hdptx, LANE_REG0510, 0x03); ++ hdptx_write(hdptx, LANE_REG0511, 0x0f); ++ hdptx_write(hdptx, LANE_REG0512, 0x3c); ++ hdptx_write(hdptx, LANE_REG0516, 0x02); ++ hdptx_write(hdptx, LANE_REG051B, 0x01); ++ hdptx_write(hdptx, LANE_REG051F, 0x15); ++ hdptx_write(hdptx, LANE_REG0520, 0xa0); ++ hdptx_write(hdptx, LANE_REG0603, 0x0c); ++ hdptx_write(hdptx, LANE_REG0607, 0x20); ++ hdptx_write(hdptx, LANE_REG060A, 0x17); ++ hdptx_write(hdptx, LANE_REG060B, 0x77); ++ hdptx_write(hdptx, LANE_REG060C, 0x77); ++ hdptx_write(hdptx, LANE_REG060D, 0x77); ++ hdptx_write(hdptx, LANE_REG060E, 0x38); ++ hdptx_write(hdptx, LANE_REG0610, 0x03); ++ hdptx_write(hdptx, LANE_REG0611, 0x0f); ++ hdptx_write(hdptx, LANE_REG0612, 0x3c); ++ hdptx_write(hdptx, LANE_REG0616, 0x02); ++ hdptx_write(hdptx, LANE_REG061B, 0x01); ++ hdptx_write(hdptx, LANE_REG061F, 0x15); ++ hdptx_write(hdptx, LANE_REG0620, 0xa0); ++ ++ hdptx_write(hdptx, LANE_REG0303, 0x2f); ++ hdptx_write(hdptx, LANE_REG0403, 0x2f); ++ hdptx_write(hdptx, LANE_REG0503, 0x2f); ++ hdptx_write(hdptx, LANE_REG0603, 0x2f); ++ hdptx_write(hdptx, LANE_REG0305, 0x03); ++ hdptx_write(hdptx, LANE_REG0405, 0x03); ++ hdptx_write(hdptx, LANE_REG0505, 0x03); ++ hdptx_write(hdptx, LANE_REG0605, 0x03); ++ hdptx_write(hdptx, LANE_REG0306, 0xfc); ++ hdptx_write(hdptx, LANE_REG0406, 0xfc); ++ hdptx_write(hdptx, LANE_REG0506, 0xfc); ++ hdptx_write(hdptx, LANE_REG0606, 0xfc); ++ ++ hdptx_write(hdptx, LANE_REG0305, 0x4f); ++ hdptx_write(hdptx, LANE_REG0405, 0x4f); ++ hdptx_write(hdptx, LANE_REG0505, 0x4f); ++ hdptx_write(hdptx, LANE_REG0605, 0x4f); ++ hdptx_write(hdptx, LANE_REG0304, 0x14); ++ hdptx_write(hdptx, LANE_REG0404, 0x14); ++ hdptx_write(hdptx, LANE_REG0504, 0x14); ++ hdptx_write(hdptx, LANE_REG0604, 0x14); ++ ++ if (hdptx->earc_en) ++ hdptx_earc_config(hdptx); ++ ++ return hdptx_post_power_up(hdptx); ++} ++ ++static int rockchip_hdptx_phy_power_on(struct phy *phy) ++{ ++ struct rockchip_hdptx_phy *hdptx = phy_get_drvdata(phy); ++ int bus_width = phy_get_bus_width(hdptx->phy); ++ int bit_rate = bus_width & DATA_RATE_MASK; ++ int ret; ++ ++ if (!hdptx->count) { ++ ret = clk_bulk_enable(hdptx->nr_clks, hdptx->clks); ++ if (ret) { ++ dev_err(hdptx->dev, "failed to enable clocks\n"); ++ return ret; ++ } ++ } ++ ++ dev_info(hdptx->dev, "bus_width:0x%x,bit_rate:%d\n", bus_width, bit_rate); ++ if (bus_width & HDMI_EARC_MASK) ++ hdptx->earc_en = true; ++ else ++ hdptx->earc_en = false; ++ ++ if (bus_width & HDMI_MODE_MASK) { ++ if (bit_rate > 24000000) ++ return hdptx_lcpll_frl_mode_config(hdptx, bus_width); ++ else ++ return hdptx_ropll_frl_mode_config(hdptx, bus_width); ++ } else { ++ return hdptx_ropll_tmds_mode_config(hdptx, bus_width); ++ } ++} ++ ++static int rockchip_hdptx_phy_power_off(struct phy *phy) ++{ ++ struct rockchip_hdptx_phy *hdptx = phy_get_drvdata(phy); ++ ++ if (hdptx->count) ++ return 0; ++ ++ if (!(hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE)) ++ return 0; ++ ++ hdptx_phy_disable(hdptx); ++ clk_bulk_disable(hdptx->nr_clks, hdptx->clks); ++ ++ return 0; ++} ++ ++static const struct phy_ops rockchip_hdptx_phy_ops = { ++ .owner = THIS_MODULE, ++ .power_on = rockchip_hdptx_phy_power_on, ++ .power_off = rockchip_hdptx_phy_power_off, ++}; ++ ++static const struct of_device_id rockchip_hdptx_phy_of_match[] = { ++ { .compatible = "rockchip,rk3588-hdptx-phy-hdmi", ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, rockchip_hdptx_phy_of_match); ++ ++static void rockchip_hdptx_phy_runtime_disable(void *data) ++{ ++ struct rockchip_hdptx_phy *hdptx = data; ++ ++ clk_bulk_unprepare(hdptx->nr_clks, hdptx->clks); ++ pm_runtime_disable(hdptx->dev); ++} ++ ++static unsigned long hdptx_phy_clk_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw); ++ ++ return hdptx->rate; ++} ++ ++static long hdptx_phy_clk_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ struct ropll_config *cfg = ropll_tmds_cfg; ++ u32 bit_rate = rate / 100; ++ ++ if (rate > HDMI20_MAX_RATE) ++ return rate; ++ ++ for (; cfg->bit_rate != ~0; cfg++) ++ if (bit_rate == cfg->bit_rate) ++ break; ++ ++ if (cfg->bit_rate == ~0 && !hdptx_phy_clk_pll_calc(bit_rate, NULL)) ++ return -EINVAL; ++ ++ return rate; ++} ++ ++static int hdptx_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw); ++ ++ if (hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE) ++ hdptx_phy_disable(hdptx); ++ ++ rate = rate / 100; ++ ++ return hdptx_ropll_cmn_config(hdptx, rate); ++} ++ ++static int hdptx_phy_clk_enable(struct clk_hw *hw) ++{ ++ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw); ++ int ret; ++ ++ if (hdptx->count) { ++ hdptx->count++; ++ return 0; ++ } ++ ++ ret = clk_bulk_enable(hdptx->nr_clks, hdptx->clks); ++ if (ret) { ++ dev_err(hdptx->dev, "failed to enable clocks\n"); ++ return ret; ++ } ++ ++ if (hdptx->rate) { ++ ret = hdptx_ropll_cmn_config(hdptx, hdptx->rate / 100); ++ if (ret < 0) { ++ dev_err(hdptx->dev, "hdmi phy pll init failed\n"); ++ return ret; ++ } ++ } ++ ++ hdptx->count++; ++ ++ return 0; ++} ++ ++static void hdptx_phy_clk_disable(struct clk_hw *hw) ++{ ++ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw); ++ ++ if (hdptx->count > 1) { ++ hdptx->count--; ++ return; ++ } ++ ++ if (hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE) ++ hdptx_phy_disable(hdptx); ++ clk_bulk_disable(hdptx->nr_clks, hdptx->clks); ++ hdptx->count--; ++} ++ ++static const struct clk_ops hdptx_phy_clk_ops = { ++ .recalc_rate = hdptx_phy_clk_recalc_rate, ++ .round_rate = hdptx_phy_clk_round_rate, ++ .set_rate = hdptx_phy_clk_set_rate, ++ .enable = hdptx_phy_clk_enable, ++ .disable = hdptx_phy_clk_disable, ++}; ++ ++static int rockchip_hdptx_phy_clk_register(struct rockchip_hdptx_phy *hdptx) ++{ ++ struct device *dev = hdptx->dev; ++ struct device_node *np = dev->of_node; ++ struct device_node *clk_np; ++ struct platform_device *pdev; ++ struct clk_init_data init = {}; ++ struct clk *refclk; ++ const char *parent_name; ++ int ret; ++ ++ clk_np = of_get_child_by_name(np, "clk-port"); ++ if (!clk_np) ++ return 0; ++ ++ pdev = of_platform_device_create(clk_np, NULL, dev); ++ if (!pdev) ++ return 0; ++ ++ refclk = devm_clk_get(dev, "ref"); ++ if (IS_ERR(refclk)) { ++ dev_err(dev, "failed to get ref clock\n"); ++ return PTR_ERR(refclk); ++ } ++ ++ parent_name = __clk_get_name(refclk); ++ ++ init.parent_names = &parent_name; ++ init.num_parents = 1; ++ init.flags = CLK_GET_RATE_NOCACHE; ++ if (!hdptx->id) ++ init.name = "clk_hdmiphy_pixel0"; ++ else ++ init.name = "clk_hdmiphy_pixel1"; ++ init.ops = &hdptx_phy_clk_ops; ++ ++ /* optional override of the clock name */ ++ of_property_read_string(np, "clock-output-names", &init.name); ++ ++ hdptx->hw.init = &init; ++ ++ hdptx->dclk = devm_clk_register(&pdev->dev, &hdptx->hw); ++ if (IS_ERR(hdptx->dclk)) { ++ ret = PTR_ERR(hdptx->dclk); ++ dev_err(dev, "failed to register clock: %d\n", ret); ++ return ret; ++ } ++ ++ ret = of_clk_add_provider(clk_np, of_clk_src_simple_get, hdptx->dclk); ++ if (ret) { ++ dev_err(dev, "failed to register OF clock provider: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int rockchip_hdptx_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct rockchip_hdptx_phy *hdptx; ++ struct phy_provider *phy_provider; ++ struct resource *res; ++ void __iomem *regs; ++ int ret; ++ ++ hdptx = devm_kzalloc(dev, sizeof(*hdptx), GFP_KERNEL); ++ if (!hdptx) ++ return -ENOMEM; ++ ++ hdptx->dev = dev; ++ ++ hdptx->id = of_alias_get_id(dev->of_node, "hdptxhdmi"); ++ if (hdptx->id < 0) ++ hdptx->id = 0; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); ++ ++ ret = devm_clk_bulk_get_all(dev, &hdptx->clks); ++ if (ret < 1) ++ return dev_err_probe(dev, ret, "failed to get clocks\n"); ++ ++ hdptx->nr_clks = ret; ++ ++ ret = clk_bulk_prepare(hdptx->nr_clks, hdptx->clks); ++ if (ret) { ++ dev_err(hdptx->dev, "failed to prepare clocks\n"); ++ return ret; ++ } ++ ++ hdptx->regmap = devm_regmap_init_mmio(dev, regs, ++ &rockchip_hdptx_phy_regmap_config); ++ if (IS_ERR(hdptx->regmap)) { ++ ret = PTR_ERR(hdptx->regmap); ++ dev_err(dev, "failed to init regmap: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->phy_reset = devm_reset_control_get(dev, "phy"); ++ if (IS_ERR(hdptx->phy_reset)) { ++ ret = PTR_ERR(hdptx->phy_reset); ++ dev_err(dev, "failed to get phy reset: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->apb_reset = devm_reset_control_get(dev, "apb"); ++ if (IS_ERR(hdptx->apb_reset)) { ++ ret = PTR_ERR(hdptx->apb_reset); ++ dev_err(dev, "failed to get apb reset: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->init_reset = devm_reset_control_get(dev, "init"); ++ if (IS_ERR(hdptx->init_reset)) { ++ ret = PTR_ERR(hdptx->init_reset); ++ dev_err(dev, "failed to get init reset: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->cmn_reset = devm_reset_control_get(dev, "cmn"); ++ if (IS_ERR(hdptx->cmn_reset)) { ++ ret = PTR_ERR(hdptx->cmn_reset); ++ dev_err(dev, "failed to get apb reset: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->lane_reset = devm_reset_control_get(dev, "lane"); ++ if (IS_ERR(hdptx->lane_reset)) { ++ ret = PTR_ERR(hdptx->lane_reset); ++ dev_err(dev, "failed to get lane reset: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->ropll_reset = devm_reset_control_get(dev, "ropll"); ++ if (IS_ERR(hdptx->ropll_reset)) { ++ ret = PTR_ERR(hdptx->ropll_reset); ++ dev_err(dev, "failed to get ropll reset: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->lcpll_reset = devm_reset_control_get(dev, "lcpll"); ++ if (IS_ERR(hdptx->lcpll_reset)) { ++ ret = PTR_ERR(hdptx->lcpll_reset); ++ dev_err(dev, "failed to get lcpll reset: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); ++ if (IS_ERR(hdptx->grf)) { ++ ret = PTR_ERR(hdptx->grf); ++ dev_err(hdptx->dev, "Unable to get rockchip,grf\n"); ++ goto err_regsmap; ++ } ++ ++ hdptx->phy = devm_phy_create(dev, NULL, &rockchip_hdptx_phy_ops); ++ if (IS_ERR(hdptx->phy)) { ++ dev_err(dev, "failed to create HDMI PHY\n"); ++ ret = PTR_ERR(hdptx->phy); ++ goto err_regsmap; ++ } ++ ++ phy_set_drvdata(hdptx->phy, hdptx); ++ phy_set_bus_width(hdptx->phy, 8); ++ ++ pm_runtime_enable(dev); ++ ret = devm_add_action_or_reset(dev, rockchip_hdptx_phy_runtime_disable, ++ hdptx); ++ if (ret) ++ goto err_regsmap; ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) { ++ dev_err(dev, "failed to register PHY provider\n"); ++ ret = PTR_ERR(phy_provider); ++ goto err_regsmap; ++ } ++ ++ reset_control_deassert(hdptx->apb_reset); ++ reset_control_deassert(hdptx->cmn_reset); ++ reset_control_deassert(hdptx->init_reset); ++ ++ ret = rockchip_hdptx_phy_clk_register(hdptx); ++ if (ret) ++ goto err_regsmap; ++ ++ platform_set_drvdata(pdev, hdptx); ++ dev_info(dev, "hdptx phy init success\n"); ++ return 0; ++ ++err_regsmap: ++ clk_bulk_unprepare(hdptx->nr_clks, hdptx->clks); ++ return ret; ++} ++ ++static struct platform_driver rockchip_hdptx_phy_driver = { ++ .probe = rockchip_hdptx_phy_probe, ++ .driver = { ++ .name = "rockchip-hdptx-phy-hdmi", ++ .of_match_table = of_match_ptr(rockchip_hdptx_phy_of_match), ++ }, ++}; ++ ++module_platform_driver(rockchip_hdptx_phy_driver); ++ ++MODULE_DESCRIPTION("Samsung HDMI-DP Transmitter Combphy Driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c +new file mode 100644 +index 000000000..bb0beafb8 +--- /dev/null ++++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c +@@ -0,0 +1,1749 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Rockchip USBDP Combo PHY with Samsung IP block driver ++ * ++ * Copyright (C) 2021 Rockchip Electronics Co., Ltd ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* RK3588 USBDP PHY Register Definitions */ ++ ++#define UDPHY_PCS 0x4000 ++#define UDPHY_PMA 0x8000 ++ ++/* VO0 GRF Registers */ ++#define RK3588_GRF_VO0_CON0 0x0000 ++#define RK3588_GRF_VO0_CON2 0x0008 ++#define DP_SINK_HPD_CFG BIT(11) ++#define DP_SINK_HPD_SEL BIT(10) ++#define DP_AUX_DIN_SEL BIT(9) ++#define DP_AUX_DOUT_SEL BIT(8) ++#define DP_LANE_SEL_N(n) GENMASK(2 * (n) + 1, 2 * (n)) ++#define DP_LANE_SEL_ALL GENMASK(7, 0) ++#define PHY_AUX_DP_DATA_POL_NORMAL 0 ++#define PHY_AUX_DP_DATA_POL_INVERT 1 ++ ++/* PMA CMN Registers */ ++#define CMN_LANE_MUX_AND_EN_OFFSET 0x0288 /* cmn_reg00A2 */ ++#define CMN_DP_LANE_MUX_N(n) BIT((n) + 4) ++#define CMN_DP_LANE_EN_N(n) BIT(n) ++#define CMN_DP_LANE_MUX_ALL GENMASK(7, 4) ++#define CMN_DP_LANE_EN_ALL GENMASK(3, 0) ++#define PHY_LANE_MUX_USB 0 ++#define PHY_LANE_MUX_DP 1 ++ ++#define CMN_DP_LINK_OFFSET 0x28c /*cmn_reg00A3 */ ++#define CMN_DP_TX_LINK_BW GENMASK(6, 5) ++#define CMN_DP_TX_LANE_SWAP_EN BIT(2) ++ ++#define CMN_SSC_EN_OFFSET 0x2d0 /* cmn_reg00B4 */ ++#define CMN_ROPLL_SSC_EN BIT(1) ++#define CMN_LCPLL_SSC_EN BIT(0) ++ ++#define CMN_ANA_LCPLL_DONE_OFFSET 0x0350 /* cmn_reg00D4 */ ++#define CMN_ANA_LCPLL_LOCK_DONE BIT(7) ++#define CMN_ANA_LCPLL_AFC_DONE BIT(6) ++ ++#define CMN_ANA_ROPLL_DONE_OFFSET 0x0354 /* cmn_reg00D5 */ ++#define CMN_ANA_ROPLL_LOCK_DONE BIT(1) ++#define CMN_ANA_ROPLL_AFC_DONE BIT(0) ++ ++#define CMN_DP_RSTN_OFFSET 0x038c /* cmn_reg00E3 */ ++#define CMN_DP_INIT_RSTN BIT(3) ++#define CMN_DP_CMN_RSTN BIT(2) ++#define CMN_CDR_WTCHDG_EN BIT(1) ++#define CMN_CDR_WTCHDG_MSK_CDR_EN BIT(0) ++ ++#define TRSV_ANA_TX_CLK_OFFSET_N(n) (0x854 + (n) * 0x800) /* trsv_reg0215 */ ++#define LN_ANA_TX_SER_TXCLK_INV BIT(1) ++ ++#define TRSV_LN0_MON_RX_CDR_DONE_OFFSET 0x0b84 /* trsv_reg02E1 */ ++#define TRSV_LN0_MON_RX_CDR_LOCK_DONE BIT(0) ++ ++#define TRSV_LN2_MON_RX_CDR_DONE_OFFSET 0x1b84 /* trsv_reg06E1 */ ++#define TRSV_LN2_MON_RX_CDR_LOCK_DONE BIT(0) ++ ++ ++#define BIT_WRITEABLE_SHIFT 16 ++ ++enum { ++ DP_BW_RBR, ++ DP_BW_HBR, ++ DP_BW_HBR2, ++ DP_BW_HBR3, ++}; ++ ++enum { ++ UDPHY_MODE_NONE = 0, ++ UDPHY_MODE_USB = BIT(0), ++ UDPHY_MODE_DP = BIT(1), ++ UDPHY_MODE_DP_USB = BIT(1) | BIT(0), ++}; ++ ++struct udphy_grf_reg { ++ unsigned int offset; ++ unsigned int bitend; ++ unsigned int bitstart; ++ unsigned int disable; ++ unsigned int enable; ++}; ++ ++struct udphy_grf_cfg { ++ /* u2phy-grf */ ++ struct udphy_grf_reg bvalid_phy_con; ++ struct udphy_grf_reg bvalid_grf_con; ++ ++ /* usb-grf */ ++ struct udphy_grf_reg usb3otg0_cfg; ++ struct udphy_grf_reg usb3otg1_cfg; ++ ++ /* usbdpphy-grf */ ++ struct udphy_grf_reg low_pwrn; ++ struct udphy_grf_reg rx_lfps; ++}; ++ ++struct udphy_vogrf_cfg { ++ /* vo-grf */ ++ struct udphy_grf_reg hpd_trigger; ++}; ++ ++struct dp_tx_drv_ctrl { ++ u32 trsv_reg0204; ++ u32 trsv_reg0205; ++ u32 trsv_reg0206; ++ u32 trsv_reg0207; ++}; ++ ++struct rockchip_udphy; ++ ++struct rockchip_udphy_cfg { ++ /* resets to be requested */ ++ const char * const *rst_list; ++ int num_rsts; ++ ++ struct udphy_grf_cfg grfcfg; ++ struct udphy_vogrf_cfg vogrfcfg[2]; ++ const struct dp_tx_drv_ctrl (*dp_tx_ctrl_cfg[4])[4]; ++ const struct dp_tx_drv_ctrl (*dp_tx_ctrl_cfg_typec[4])[4]; ++ int (*combophy_init)(struct rockchip_udphy *udphy); ++ int (*dp_phy_set_rate)(struct rockchip_udphy *udphy, ++ struct phy_configure_opts_dp *dp); ++ int (*dp_phy_set_voltages)(struct rockchip_udphy *udphy, ++ struct phy_configure_opts_dp *dp); ++ int (*hpd_event_trigger)(struct rockchip_udphy *udphy, bool hpd); ++ int (*dplane_enable)(struct rockchip_udphy *udphy, int dp_lanes); ++ int (*dplane_select)(struct rockchip_udphy *udphy); ++}; ++ ++struct rockchip_udphy { ++ struct device *dev; ++ struct regmap *pma_regmap; ++ struct regmap *u2phygrf; ++ struct regmap *udphygrf; ++ struct regmap *usbgrf; ++ struct regmap *vogrf; ++ struct typec_switch_dev *sw; ++ struct typec_mux_dev *mux; ++ struct mutex mutex; /* mutex to protect access to individual PHYs */ ++ ++ /* clocks and rests */ ++ int num_clks; ++ struct clk_bulk_data *clks; ++ struct clk *refclk; ++ struct reset_control **rsts; ++ ++ /* PHY status management */ ++ bool flip; ++ bool mode_change; ++ u8 mode; ++ u8 status; ++ ++ /* utilized for USB */ ++ bool hs; /* flag for high-speed */ ++ ++ /* utilized for DP */ ++ struct gpio_desc *sbu1_dc_gpio; ++ struct gpio_desc *sbu2_dc_gpio; ++ u32 lane_mux_sel[4]; ++ u32 dp_lane_sel[4]; ++ u32 dp_aux_dout_sel; ++ u32 dp_aux_din_sel; ++ bool dp_sink_hpd_sel; ++ bool dp_sink_hpd_cfg; ++ u8 bw; ++ int id; ++ ++ /* PHY const config */ ++ const struct rockchip_udphy_cfg *cfgs; ++}; ++ ++static const struct dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_rbr_hbr[4][4] = { ++ /* voltage swing 0, pre-emphasis 0->3 */ ++ { ++ { 0x20, 0x10, 0x42, 0xe5 }, ++ { 0x26, 0x14, 0x42, 0xe5 }, ++ { 0x29, 0x18, 0x42, 0xe5 }, ++ { 0x2b, 0x1c, 0x43, 0xe7 }, ++ }, ++ ++ /* voltage swing 1, pre-emphasis 0->2 */ ++ { ++ { 0x23, 0x10, 0x42, 0xe7 }, ++ { 0x2a, 0x17, 0x43, 0xe7 }, ++ { 0x2b, 0x1a, 0x43, 0xe7 }, ++ }, ++ ++ /* voltage swing 2, pre-emphasis 0->1 */ ++ { ++ { 0x27, 0x10, 0x42, 0xe7 }, ++ { 0x2b, 0x17, 0x43, 0xe7 }, ++ }, ++ ++ /* voltage swing 3, pre-emphasis 0 */ ++ { ++ { 0x29, 0x10, 0x43, 0xe7 }, ++ }, ++}; ++ ++static const struct dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_rbr_hbr_typec[4][4] = { ++ /* voltage swing 0, pre-emphasis 0->3 */ ++ { ++ { 0x20, 0x10, 0x42, 0xe5 }, ++ { 0x26, 0x14, 0x42, 0xe5 }, ++ { 0x29, 0x18, 0x42, 0xe5 }, ++ { 0x2b, 0x1c, 0x43, 0xe7 }, ++ }, ++ ++ /* voltage swing 1, pre-emphasis 0->2 */ ++ { ++ { 0x23, 0x10, 0x42, 0xe7 }, ++ { 0x2a, 0x17, 0x43, 0xe7 }, ++ { 0x2b, 0x1a, 0x43, 0xe7 }, ++ }, ++ ++ /* voltage swing 2, pre-emphasis 0->1 */ ++ { ++ { 0x27, 0x10, 0x43, 0x67 }, ++ { 0x2b, 0x17, 0x43, 0xe7 }, ++ }, ++ ++ /* voltage swing 3, pre-emphasis 0 */ ++ { ++ { 0x29, 0x10, 0x43, 0xe7 }, ++ }, ++}; ++ ++static const struct dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_hbr2[4][4] = { ++ /* voltage swing 0, pre-emphasis 0->3 */ ++ { ++ { 0x21, 0x10, 0x42, 0xe5 }, ++ { 0x26, 0x14, 0x42, 0xe5 }, ++ { 0x26, 0x16, 0x43, 0xe5 }, ++ { 0x2a, 0x19, 0x43, 0xe7 }, ++ }, ++ ++ /* voltage swing 1, pre-emphasis 0->2 */ ++ { ++ { 0x24, 0x10, 0x42, 0xe7 }, ++ { 0x2a, 0x17, 0x43, 0xe7 }, ++ { 0x2b, 0x1a, 0x43, 0xe7 }, ++ }, ++ ++ /* voltage swing 2, pre-emphasis 0->1 */ ++ { ++ { 0x28, 0x10, 0x42, 0xe7 }, ++ { 0x2b, 0x17, 0x43, 0xe7 }, ++ }, ++ ++ /* voltage swing 3, pre-emphasis 0 */ ++ { ++ { 0x28, 0x10, 0x43, 0xe7 }, ++ }, ++}; ++ ++static const struct dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_hbr3[4][4] = { ++ /* voltage swing 0, pre-emphasis 0->3 */ ++ { ++ { 0x21, 0x10, 0x42, 0xe5 }, ++ { 0x26, 0x14, 0x42, 0xe5 }, ++ { 0x26, 0x16, 0x43, 0xe5 }, ++ { 0x29, 0x18, 0x43, 0xe7 }, ++ }, ++ ++ /* voltage swing 1, pre-emphasis 0->2 */ ++ { ++ { 0x24, 0x10, 0x42, 0xe7 }, ++ { 0x2a, 0x18, 0x43, 0xe7 }, ++ { 0x2b, 0x1b, 0x43, 0xe7 } ++ }, ++ ++ /* voltage swing 2, pre-emphasis 0->1 */ ++ { ++ { 0x27, 0x10, 0x42, 0xe7 }, ++ { 0x2b, 0x18, 0x43, 0xe7 } ++ }, ++ ++ /* voltage swing 3, pre-emphasis 0 */ ++ { ++ { 0x28, 0x10, 0x43, 0xe7 }, ++ }, ++}; ++ ++static const struct reg_sequence rk3588_udphy_24m_refclk_cfg[] = { ++ {0x0090, 0x68}, {0x0094, 0x68}, ++ {0x0128, 0x24}, {0x012c, 0x44}, ++ {0x0130, 0x3f}, {0x0134, 0x44}, ++ {0x015c, 0xa9}, {0x0160, 0x71}, ++ {0x0164, 0x71}, {0x0168, 0xa9}, ++ {0x0174, 0xa9}, {0x0178, 0x71}, ++ {0x017c, 0x71}, {0x0180, 0xa9}, ++ {0x018c, 0x41}, {0x0190, 0x00}, ++ {0x0194, 0x05}, {0x01ac, 0x2a}, ++ {0x01b0, 0x17}, {0x01b4, 0x17}, ++ {0x01b8, 0x2a}, {0x01c8, 0x04}, ++ {0x01cc, 0x08}, {0x01d0, 0x08}, ++ {0x01d4, 0x04}, {0x01d8, 0x20}, ++ {0x01dc, 0x01}, {0x01e0, 0x09}, ++ {0x01e4, 0x03}, {0x01f0, 0x29}, ++ {0x01f4, 0x02}, {0x01f8, 0x02}, ++ {0x01fc, 0x29}, {0x0208, 0x2a}, ++ {0x020c, 0x17}, {0x0210, 0x17}, ++ {0x0214, 0x2a}, {0x0224, 0x20}, ++ {0x03f0, 0x0a}, {0x03f4, 0x07}, ++ {0x03f8, 0x07}, {0x03fc, 0x0c}, ++ {0x0404, 0x12}, {0x0408, 0x1a}, ++ {0x040c, 0x1a}, {0x0410, 0x3f}, ++ {0x0ce0, 0x68}, {0x0ce8, 0xd0}, ++ {0x0cf0, 0x87}, {0x0cf8, 0x70}, ++ {0x0d00, 0x70}, {0x0d08, 0xa9}, ++ {0x1ce0, 0x68}, {0x1ce8, 0xd0}, ++ {0x1cf0, 0x87}, {0x1cf8, 0x70}, ++ {0x1d00, 0x70}, {0x1d08, 0xa9}, ++ {0x0a3c, 0xd0}, {0x0a44, 0xd0}, ++ {0x0a48, 0x01}, {0x0a4c, 0x0d}, ++ {0x0a54, 0xe0}, {0x0a5c, 0xe0}, ++ {0x0a64, 0xa8}, {0x1a3c, 0xd0}, ++ {0x1a44, 0xd0}, {0x1a48, 0x01}, ++ {0x1a4c, 0x0d}, {0x1a54, 0xe0}, ++ {0x1a5c, 0xe0}, {0x1a64, 0xa8} ++}; ++ ++static const struct reg_sequence rk3588_udphy_26m_refclk_cfg[] = { ++ {0x0830, 0x07}, {0x085c, 0x80}, ++ {0x1030, 0x07}, {0x105c, 0x80}, ++ {0x1830, 0x07}, {0x185c, 0x80}, ++ {0x2030, 0x07}, {0x205c, 0x80}, ++ {0x0228, 0x38}, {0x0104, 0x44}, ++ {0x0248, 0x44}, {0x038C, 0x02}, ++ {0x0878, 0x04}, {0x1878, 0x04}, ++ {0x0898, 0x77}, {0x1898, 0x77}, ++ {0x0054, 0x01}, {0x00e0, 0x38}, ++ {0x0060, 0x24}, {0x0064, 0x77}, ++ {0x0070, 0x76}, {0x0234, 0xE8}, ++ {0x0AF4, 0x15}, {0x1AF4, 0x15}, ++ {0x081C, 0xE5}, {0x181C, 0xE5}, ++ {0x099C, 0x48}, {0x199C, 0x48}, ++ {0x09A4, 0x07}, {0x09A8, 0x22}, ++ {0x19A4, 0x07}, {0x19A8, 0x22}, ++ {0x09B8, 0x3E}, {0x19B8, 0x3E}, ++ {0x09E4, 0x02}, {0x19E4, 0x02}, ++ {0x0A34, 0x1E}, {0x1A34, 0x1E}, ++ {0x0A98, 0x2F}, {0x1A98, 0x2F}, ++ {0x0c30, 0x0E}, {0x0C48, 0x06}, ++ {0x1C30, 0x0E}, {0x1C48, 0x06}, ++ {0x028C, 0x18}, {0x0AF0, 0x00}, ++ {0x1AF0, 0x00} ++}; ++ ++static const struct reg_sequence rk3588_udphy_init_sequence[] = { ++ {0x0104, 0x44}, {0x0234, 0xE8}, ++ {0x0248, 0x44}, {0x028C, 0x18}, ++ {0x081C, 0xE5}, {0x0878, 0x00}, ++ {0x0994, 0x1C}, {0x0AF0, 0x00}, ++ {0x181C, 0xE5}, {0x1878, 0x00}, ++ {0x1994, 0x1C}, {0x1AF0, 0x00}, ++ {0x0428, 0x60}, {0x0D58, 0x33}, ++ {0x1D58, 0x33}, {0x0990, 0x74}, ++ {0x0D64, 0x17}, {0x08C8, 0x13}, ++ {0x1990, 0x74}, {0x1D64, 0x17}, ++ {0x18C8, 0x13}, {0x0D90, 0x40}, ++ {0x0DA8, 0x40}, {0x0DC0, 0x40}, ++ {0x0DD8, 0x40}, {0x1D90, 0x40}, ++ {0x1DA8, 0x40}, {0x1DC0, 0x40}, ++ {0x1DD8, 0x40}, {0x03C0, 0x30}, ++ {0x03C4, 0x06}, {0x0E10, 0x00}, ++ {0x1E10, 0x00}, {0x043C, 0x0F}, ++ {0x0D2C, 0xFF}, {0x1D2C, 0xFF}, ++ {0x0D34, 0x0F}, {0x1D34, 0x0F}, ++ {0x08FC, 0x2A}, {0x0914, 0x28}, ++ {0x0A30, 0x03}, {0x0E38, 0x05}, ++ {0x0ECC, 0x27}, {0x0ED0, 0x22}, ++ {0x0ED4, 0x26}, {0x18FC, 0x2A}, ++ {0x1914, 0x28}, {0x1A30, 0x03}, ++ {0x1E38, 0x05}, {0x1ECC, 0x27}, ++ {0x1ED0, 0x22}, {0x1ED4, 0x26}, ++ {0x0048, 0x0F}, {0x0060, 0x3C}, ++ {0x0064, 0xF7}, {0x006C, 0x20}, ++ {0x0070, 0x7D}, {0x0074, 0x68}, ++ {0x0AF4, 0x1A}, {0x1AF4, 0x1A}, ++ {0x0440, 0x3F}, {0x10D4, 0x08}, ++ {0x20D4, 0x08}, {0x00D4, 0x30}, ++ {0x0024, 0x6e}, ++}; ++ ++static inline int grfreg_write(struct regmap *base, ++ const struct udphy_grf_reg *reg, bool en) ++{ ++ u32 val, mask, tmp; ++ ++ tmp = en ? reg->enable : reg->disable; ++ mask = GENMASK(reg->bitend, reg->bitstart); ++ val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); ++ ++ return regmap_write(base, reg->offset, val); ++} ++ ++static int udphy_clk_init(struct rockchip_udphy *udphy, struct device *dev) ++{ ++ int i; ++ ++ udphy->num_clks = devm_clk_bulk_get_all(dev, &udphy->clks); ++ if (udphy->num_clks < 1) ++ return -ENODEV; ++ ++ /* used for configure phy reference clock frequency */ ++ for (i = 0; i < udphy->num_clks; i++) { ++ if (!strncmp(udphy->clks[i].id, "refclk", 6)) { ++ udphy->refclk = udphy->clks[i].clk; ++ break; ++ } ++ } ++ ++ if (!udphy->refclk) ++ dev_warn(udphy->dev, "no refclk found\n"); ++ ++ return 0; ++} ++ ++static int udphy_reset_init(struct rockchip_udphy *udphy, struct device *dev) ++{ ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ int idx; ++ ++ udphy->rsts = devm_kcalloc(dev, cfg->num_rsts, ++ sizeof(*udphy->rsts), GFP_KERNEL); ++ if (!udphy->rsts) ++ return -ENOMEM; ++ ++ for (idx = 0; idx < cfg->num_rsts; idx++) { ++ struct reset_control *rst; ++ const char *name = cfg->rst_list[idx]; ++ ++ rst = devm_reset_control_get(dev, name); ++ if (IS_ERR(rst)) { ++ dev_err(dev, "failed to get %s reset\n", name); ++ devm_kfree(dev, (void *)udphy->rsts); ++ return PTR_ERR(rst); ++ } ++ ++ udphy->rsts[idx] = rst; ++ } ++ ++ return 0; ++} ++ ++static int udphy_reset_assert_all(struct rockchip_udphy *udphy) ++{ ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ int idx, ret; ++ ++ for (idx = 0; idx < cfg->num_rsts; idx++) { ++ ret = reset_control_assert(udphy->rsts[idx]); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int udphy_reset_deassert_all(struct rockchip_udphy *udphy) ++{ ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ int idx, ret; ++ ++ for (idx = 0; idx < cfg->num_rsts; idx++) { ++ ret = reset_control_deassert(udphy->rsts[idx]); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int udphy_get_rst_idx(const char * const *list, int num, char *name) ++{ ++ int idx; ++ ++ for (idx = 0; idx < num; idx++) { ++ if (!strcmp(list[idx], name)) ++ return idx; ++ } ++ ++ return -EINVAL; ++} ++ ++static int udphy_reset_assert(struct rockchip_udphy *udphy, char *name) ++{ ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ int idx; ++ ++ idx = udphy_get_rst_idx(cfg->rst_list, cfg->num_rsts, name); ++ if (idx < 0) ++ return idx; ++ ++ return reset_control_assert(udphy->rsts[idx]); ++} ++ ++static int udphy_reset_deassert(struct rockchip_udphy *udphy, char *name) ++{ ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ int idx; ++ ++ idx = udphy_get_rst_idx(cfg->rst_list, cfg->num_rsts, name); ++ if (idx < 0) ++ return idx; ++ ++ return reset_control_deassert(udphy->rsts[idx]); ++} ++ ++static void udphy_u3_port_disable(struct rockchip_udphy *udphy, u8 disable) ++{ ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ const struct udphy_grf_reg *preg; ++ ++ preg = udphy->id ? &cfg->grfcfg.usb3otg1_cfg : &cfg->grfcfg.usb3otg0_cfg; ++ grfreg_write(udphy->usbgrf, preg, disable); ++} ++ ++static void udphy_usb_bvalid_enable(struct rockchip_udphy *udphy, u8 enable) ++{ ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ ++ grfreg_write(udphy->u2phygrf, &cfg->grfcfg.bvalid_phy_con, enable); ++ grfreg_write(udphy->u2phygrf, &cfg->grfcfg.bvalid_grf_con, enable); ++} ++ ++/* ++ * In usb/dp combo phy driver, here are 2 ways to mapping lanes. ++ * ++ * 1 Type-C Mapping table (DP_Alt_Mode V1.0b remove ABF pin mapping) ++ * --------------------------------------------------------------------------- ++ * Type-C Pin B11-B10 A2-A3 A11-A10 B2-B3 ++ * PHY Pad ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx) ++ * C/E(Normal) dpln3 dpln2 dpln0 dpln1 ++ * C/E(Flip ) dpln0 dpln1 dpln3 dpln2 ++ * D/F(Normal) usbrx usbtx dpln0 dpln1 ++ * D/F(Flip ) dpln0 dpln1 usbrx usbtx ++ * A(Normal ) dpln3 dpln1 dpln2 dpln0 ++ * A(Flip ) dpln2 dpln0 dpln3 dpln1 ++ * B(Normal ) usbrx usbtx dpln1 dpln0 ++ * B(Flip ) dpln1 dpln0 usbrx usbtx ++ * --------------------------------------------------------------------------- ++ * ++ * 2 Mapping the lanes in dtsi ++ * if all 4 lane assignment for dp function, define rockchip,dp-lane-mux = ; ++ * sample as follow: ++ * --------------------------------------------------------------------------- ++ * B11-B10 A2-A3 A11-A10 B2-B3 ++ * rockchip,dp-lane-mux ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx) ++ * <0 1 2 3> dpln0 dpln1 dpln2 dpln3 ++ * <2 3 0 1> dpln2 dpln3 dpln0 dpln1 ++ * --------------------------------------------------------------------------- ++ * if 2 lane for dp function, 2 lane for usb function, define rockchip,dp-lane-mux = ; ++ * sample as follow: ++ * --------------------------------------------------------------------------- ++ * B11-B10 A2-A3 A11-A10 B2-B3 ++ * rockchip,dp-lane-mux ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx) ++ * <0 1> dpln0 dpln1 usbrx usbtx ++ * <2 3> usbrx usbtx dpln0 dpln1 ++ * --------------------------------------------------------------------------- ++ */ ++ ++static int udphy_dplane_select(struct rockchip_udphy *udphy) ++{ ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ ++ if (cfg->dplane_select) ++ return cfg->dplane_select(udphy); ++ ++ return 0; ++} ++ ++static int udphy_dplane_get(struct rockchip_udphy *udphy) ++{ ++ int dp_lanes; ++ ++ switch (udphy->mode) { ++ case UDPHY_MODE_DP: ++ dp_lanes = 4; ++ break; ++ case UDPHY_MODE_DP_USB: ++ dp_lanes = 2; ++ break; ++ case UDPHY_MODE_USB: ++ fallthrough; ++ default: ++ dp_lanes = 0; ++ break; ++ } ++ ++ return dp_lanes; ++} ++ ++static int udphy_dplane_enable(struct rockchip_udphy *udphy, int dp_lanes) ++{ ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ int ret = 0; ++ ++ if (cfg->dplane_enable) ++ ret = cfg->dplane_enable(udphy, dp_lanes); ++ ++ return ret; ++} ++ ++static int upphy_set_typec_default_mapping(struct rockchip_udphy *udphy) ++{ ++ if (udphy->flip) { ++ udphy->dp_lane_sel[0] = 0; ++ udphy->dp_lane_sel[1] = 1; ++ udphy->dp_lane_sel[2] = 3; ++ udphy->dp_lane_sel[3] = 2; ++ udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP; ++ udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP; ++ udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB; ++ udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB; ++ udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_INVERT; ++ udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_INVERT; ++ gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 1); ++ gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 0); ++ } else { ++ udphy->dp_lane_sel[0] = 2; ++ udphy->dp_lane_sel[1] = 3; ++ udphy->dp_lane_sel[2] = 1; ++ udphy->dp_lane_sel[3] = 0; ++ udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB; ++ udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB; ++ udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP; ++ udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP; ++ udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_NORMAL; ++ udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_NORMAL; ++ gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0); ++ gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 1); ++ } ++ ++ udphy->mode = UDPHY_MODE_DP_USB; ++ ++ return 0; ++} ++ ++static int udphy_orien_sw_set(struct typec_switch_dev *sw, ++ enum typec_orientation orien) ++{ ++ struct rockchip_udphy *udphy = typec_switch_get_drvdata(sw); ++ ++ mutex_lock(&udphy->mutex); ++ ++ if (orien == TYPEC_ORIENTATION_NONE) { ++ gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0); ++ gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 0); ++ /* unattached */ ++ udphy_usb_bvalid_enable(udphy, false); ++ goto unlock_ret; ++ } ++ ++ udphy->flip = (orien == TYPEC_ORIENTATION_REVERSE) ? true : false; ++ upphy_set_typec_default_mapping(udphy); ++ udphy_usb_bvalid_enable(udphy, true); ++ ++unlock_ret: ++ mutex_unlock(&udphy->mutex); ++ return 0; ++} ++ ++static int udphy_setup_orien_switch(struct rockchip_udphy *udphy) ++{ ++ struct typec_switch_desc sw_desc = { }; ++ ++ sw_desc.drvdata = udphy; ++ sw_desc.fwnode = dev_fwnode(udphy->dev); ++ sw_desc.set = udphy_orien_sw_set; ++ ++ udphy->sw = typec_switch_register(udphy->dev, &sw_desc); ++ if (IS_ERR(udphy->sw)) { ++ dev_err(udphy->dev, "Error register typec orientation switch: %ld\n", ++ PTR_ERR(udphy->sw)); ++ return PTR_ERR(udphy->sw); ++ } ++ ++ return 0; ++} ++ ++static void udphy_orien_switch_unregister(void *data) ++{ ++ struct rockchip_udphy *udphy = data; ++ ++ typec_switch_unregister(udphy->sw); ++} ++ ++static int udphy_setup(struct rockchip_udphy *udphy) ++{ ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ int ret = 0; ++ ++ ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks); ++ if (ret) { ++ dev_err(udphy->dev, "failed to enable clk\n"); ++ return ret; ++ } ++ ++ if (cfg->combophy_init) { ++ ret = cfg->combophy_init(udphy); ++ if (ret) { ++ dev_err(udphy->dev, "failed to init combophy\n"); ++ clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int udphy_disable(struct rockchip_udphy *udphy) ++{ ++ clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks); ++ udphy_reset_assert_all(udphy); ++ ++ return 0; ++} ++ ++static int udphy_parse_lane_mux_data(struct rockchip_udphy *udphy, struct device_node *np) ++{ ++ struct property *prop; ++ int ret, i, len, num_lanes; ++ ++ prop = of_find_property(np, "rockchip,dp-lane-mux", &len); ++ if (!prop) { ++ dev_dbg(udphy->dev, "failed to find dp lane mux, following dp alt mode\n"); ++ udphy->mode = UDPHY_MODE_USB; ++ return 0; ++ } ++ ++ num_lanes = len / sizeof(u32); ++ ++ if (num_lanes != 2 && num_lanes != 4) { ++ dev_err(udphy->dev, "invalid number of lane mux\n"); ++ return -EINVAL; ++ } ++ ++ ret = of_property_read_u32_array(np, "rockchip,dp-lane-mux", udphy->dp_lane_sel, num_lanes); ++ if (ret) { ++ dev_err(udphy->dev, "get dp lane mux failed\n"); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < num_lanes; i++) { ++ int j; ++ ++ if (udphy->dp_lane_sel[i] > 3) { ++ dev_err(udphy->dev, "lane mux between 0 and 3, exceeding the range\n"); ++ return -EINVAL; ++ } ++ ++ udphy->lane_mux_sel[udphy->dp_lane_sel[i]] = PHY_LANE_MUX_DP; ++ ++ for (j = i + 1; j < num_lanes; j++) { ++ if (udphy->dp_lane_sel[i] == udphy->dp_lane_sel[j]) { ++ dev_err(udphy->dev, "set repeat lane mux value\n"); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ udphy->mode = UDPHY_MODE_DP; ++ if (num_lanes == 2) { ++ udphy->mode |= UDPHY_MODE_USB; ++ udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP); ++ } ++ ++ return 0; ++} ++ ++static int udphy_get_initial_status(struct rockchip_udphy *udphy) ++{ ++ int ret; ++ u32 value; ++ ++ ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks); ++ if (ret) { ++ dev_err(udphy->dev, "failed to enable clk\n"); ++ return ret; ++ } ++ ++ udphy_reset_deassert_all(udphy); ++ ++ regmap_read(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, &value); ++ if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value)) ++ udphy->status = UDPHY_MODE_DP; ++ else ++ udphy_disable(udphy); ++ ++ return 0; ++} ++ ++static int udphy_parse_dt(struct rockchip_udphy *udphy, struct device *dev) ++{ ++ struct device_node *np = dev->of_node; ++ enum usb_device_speed maximum_speed; ++ int ret; ++ ++ udphy->u2phygrf = syscon_regmap_lookup_by_phandle(np, "rockchip,u2phy-grf"); ++ if (IS_ERR(udphy->u2phygrf)) { ++ if (PTR_ERR(udphy->u2phygrf) == -ENODEV) { ++ dev_warn(dev, "missing u2phy-grf dt node\n"); ++ udphy->u2phygrf = NULL; ++ } else { ++ return PTR_ERR(udphy->u2phygrf); ++ } ++ } ++ ++ udphy->udphygrf = syscon_regmap_lookup_by_phandle(np, "rockchip,usbdpphy-grf"); ++ if (IS_ERR(udphy->udphygrf)) { ++ if (PTR_ERR(udphy->udphygrf) == -ENODEV) { ++ dev_warn(dev, "missing usbdpphy-grf dt node\n"); ++ udphy->udphygrf = NULL; ++ } else { ++ return PTR_ERR(udphy->udphygrf); ++ } ++ } ++ ++ udphy->usbgrf = syscon_regmap_lookup_by_phandle(np, "rockchip,usb-grf"); ++ if (IS_ERR(udphy->usbgrf)) { ++ if (PTR_ERR(udphy->usbgrf) == -ENODEV) { ++ dev_warn(dev, "missing usb-grf dt node\n"); ++ udphy->usbgrf = NULL; ++ } else { ++ return PTR_ERR(udphy->usbgrf); ++ } ++ } ++ ++ udphy->vogrf = syscon_regmap_lookup_by_phandle(np, "rockchip,vo-grf"); ++ if (IS_ERR(udphy->vogrf)) { ++ if (PTR_ERR(udphy->vogrf) == -ENODEV) { ++ dev_warn(dev, "missing vo-grf dt node\n"); ++ udphy->vogrf = NULL; ++ } else { ++ return PTR_ERR(udphy->vogrf); ++ } ++ } ++ ++ ret = udphy_parse_lane_mux_data(udphy, np); ++ if (ret) ++ return ret; ++ ++ udphy->sbu1_dc_gpio = devm_gpiod_get_optional(dev, "sbu1-dc", GPIOD_OUT_LOW); ++ if (IS_ERR(udphy->sbu1_dc_gpio)) ++ return PTR_ERR(udphy->sbu1_dc_gpio); ++ ++ udphy->sbu2_dc_gpio = devm_gpiod_get_optional(dev, "sbu2-dc", GPIOD_OUT_LOW); ++ if (IS_ERR(udphy->sbu2_dc_gpio)) ++ return PTR_ERR(udphy->sbu2_dc_gpio); ++ ++ if (device_property_present(dev, "maximum-speed")) { ++ maximum_speed = usb_get_maximum_speed(dev); ++ udphy->hs = maximum_speed <= USB_SPEED_HIGH ? true : false; ++ } ++ ++ ret = udphy_clk_init(udphy, dev); ++ if (ret) ++ return ret; ++ ++ ret = udphy_reset_init(udphy, dev); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int udphy_power_on(struct rockchip_udphy *udphy, u8 mode) ++{ ++ int ret; ++ ++ if (!(udphy->mode & mode)) { ++ dev_info(udphy->dev, "mode 0x%02x is not support\n", mode); ++ return 0; ++ } ++ ++ if (udphy->status == UDPHY_MODE_NONE) { ++ udphy->mode_change = false; ++ ret = udphy_setup(udphy); ++ if (ret) ++ return ret; ++ ++ if (udphy->mode & UDPHY_MODE_USB) ++ udphy_u3_port_disable(udphy, false); ++ } else if (udphy->mode_change) { ++ udphy->mode_change = false; ++ udphy->status = UDPHY_MODE_NONE; ++ if (udphy->mode == UDPHY_MODE_DP) ++ udphy_u3_port_disable(udphy, true); ++ ++ ret = udphy_disable(udphy); ++ if (ret) ++ return ret; ++ ret = udphy_setup(udphy); ++ if (ret) ++ return ret; ++ } ++ ++ udphy->status |= mode; ++ ++ return 0; ++} ++ ++static int udphy_power_off(struct rockchip_udphy *udphy, u8 mode) ++{ ++ int ret; ++ ++ if (!(udphy->mode & mode)) { ++ dev_info(udphy->dev, "mode 0x%02x is not support\n", mode); ++ return 0; ++ } ++ ++ if (!udphy->status) ++ return 0; ++ ++ udphy->status &= ~mode; ++ ++ if (udphy->status == UDPHY_MODE_NONE) { ++ ret = udphy_disable(udphy); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int rockchip_dp_phy_power_on(struct phy *phy) ++{ ++ struct rockchip_udphy *udphy = phy_get_drvdata(phy); ++ int ret, dp_lanes; ++ ++ mutex_lock(&udphy->mutex); ++ ++ dp_lanes = udphy_dplane_get(udphy); ++ phy_set_bus_width(phy, dp_lanes); ++ ++ ret = udphy_power_on(udphy, UDPHY_MODE_DP); ++ if (ret) ++ goto unlock; ++ ++ ret = udphy_dplane_enable(udphy, dp_lanes); ++ if (ret) ++ goto unlock; ++ ++ ret = udphy_dplane_select(udphy); ++ ++unlock: ++ mutex_unlock(&udphy->mutex); ++ /* ++ * If data send by aux channel too fast after phy power on, ++ * the aux may be not ready which will cause aux error. Adding ++ * delay to avoid this issue. ++ */ ++ usleep_range(10000, 11000); ++ return ret; ++} ++ ++static int rockchip_dp_phy_power_off(struct phy *phy) ++{ ++ struct rockchip_udphy *udphy = phy_get_drvdata(phy); ++ int ret; ++ ++ mutex_lock(&udphy->mutex); ++ ret = udphy_dplane_enable(udphy, 0); ++ if (ret) ++ goto unlock; ++ ++ ret = udphy_power_off(udphy, UDPHY_MODE_DP); ++ ++unlock: ++ mutex_unlock(&udphy->mutex); ++ return ret; ++} ++ ++static int rockchip_dp_phy_verify_link_rate(unsigned int link_rate) ++{ ++ switch (link_rate) { ++ case 1620: ++ case 2700: ++ case 5400: ++ case 8100: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int rockchip_dp_phy_verify_config(struct rockchip_udphy *udphy, ++ struct phy_configure_opts_dp *dp) ++{ ++ int i, ret; ++ ++ /* If changing link rate was required, verify it's supported. */ ++ ret = rockchip_dp_phy_verify_link_rate(dp->link_rate); ++ if (ret) ++ return ret; ++ ++ /* Verify lane count. */ ++ switch (dp->lanes) { ++ case 1: ++ case 2: ++ case 4: ++ /* valid lane count. */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* ++ * If changing voltages is required, check swing and pre-emphasis ++ * levels, per-lane. ++ */ ++ if (dp->set_voltages) { ++ /* Lane count verified previously. */ ++ for (i = 0; i < dp->lanes; i++) { ++ if (dp->voltage[i] > 3 || dp->pre[i] > 3) ++ return -EINVAL; ++ ++ /* ++ * Sum of voltage swing and pre-emphasis levels cannot ++ * exceed 3. ++ */ ++ if (dp->voltage[i] + dp->pre[i] > 3) ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int rockchip_dp_phy_configure(struct phy *phy, ++ union phy_configure_opts *opts) ++{ ++ struct rockchip_udphy *udphy = phy_get_drvdata(phy); ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ int ret; ++ ++ ret = rockchip_dp_phy_verify_config(udphy, &opts->dp); ++ if (ret) ++ return ret; ++ ++ if (opts->dp.set_rate && cfg->dp_phy_set_rate) { ++ ret = cfg->dp_phy_set_rate(udphy, &opts->dp); ++ if (ret) { ++ dev_err(udphy->dev, ++ "rockchip_hdptx_phy_set_rate failed\n"); ++ return ret; ++ } ++ } ++ ++ if (opts->dp.set_voltages && cfg->dp_phy_set_voltages) { ++ ret = cfg->dp_phy_set_voltages(udphy, &opts->dp); ++ if (ret) { ++ dev_err(udphy->dev, ++ "rockchip_dp_phy_set_voltages failed\n"); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static const struct phy_ops rockchip_dp_phy_ops = { ++ .power_on = rockchip_dp_phy_power_on, ++ .power_off = rockchip_dp_phy_power_off, ++ .configure = rockchip_dp_phy_configure, ++ .owner = THIS_MODULE, ++}; ++ ++static int rockchip_u3phy_init(struct phy *phy) ++{ ++ struct rockchip_udphy *udphy = phy_get_drvdata(phy); ++ int ret = 0; ++ ++ mutex_lock(&udphy->mutex); ++ /* DP only or high-speed, disable U3 port */ ++ if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) { ++ udphy_u3_port_disable(udphy, true); ++ goto unlock; ++ } ++ ++ ret = udphy_power_on(udphy, UDPHY_MODE_USB); ++ ++unlock: ++ mutex_unlock(&udphy->mutex); ++ return ret; ++} ++ ++static int rockchip_u3phy_exit(struct phy *phy) ++{ ++ struct rockchip_udphy *udphy = phy_get_drvdata(phy); ++ int ret = 0; ++ ++ mutex_lock(&udphy->mutex); ++ /* DP only or high-speed */ ++ if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) ++ goto unlock; ++ ++ ret = udphy_power_off(udphy, UDPHY_MODE_USB); ++ ++unlock: ++ mutex_unlock(&udphy->mutex); ++ return ret; ++} ++ ++static const struct phy_ops rockchip_u3phy_ops = { ++ .init = rockchip_u3phy_init, ++ .exit = rockchip_u3phy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int usbdp_typec_mux_set(struct typec_mux_dev *mux, ++ struct typec_mux_state *state) ++{ ++ struct rockchip_udphy *udphy = typec_mux_get_drvdata(mux); ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ u8 mode; ++ ++ mutex_lock(&udphy->mutex); ++ ++ switch (state->mode) { ++ case TYPEC_DP_STATE_C: ++ fallthrough; ++ case TYPEC_DP_STATE_E: ++ udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP; ++ udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP; ++ udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP; ++ udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP; ++ mode = UDPHY_MODE_DP; ++ break; ++ case TYPEC_DP_STATE_D: ++ fallthrough; ++ default: ++ if (udphy->flip) { ++ udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP; ++ udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP; ++ udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB; ++ udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB; ++ } else { ++ udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB; ++ udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB; ++ udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP; ++ udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP; ++ } ++ mode = UDPHY_MODE_DP_USB; ++ break; ++ } ++ ++ if (state->alt && state->alt->svid == USB_TYPEC_DP_SID) { ++ struct typec_displayport_data *data = state->data; ++ ++ if (!data) { ++ if (cfg->hpd_event_trigger) ++ cfg->hpd_event_trigger(udphy, false); ++ } else if (data->status & DP_STATUS_IRQ_HPD) { ++ if (cfg->hpd_event_trigger) { ++ cfg->hpd_event_trigger(udphy, false); ++ usleep_range(750, 800); ++ cfg->hpd_event_trigger(udphy, true); ++ } ++ } else if (data->status & DP_STATUS_HPD_STATE) { ++ if (udphy->mode != mode) { ++ udphy->mode = mode; ++ udphy->mode_change = true; ++ } ++ if (cfg->hpd_event_trigger) ++ cfg->hpd_event_trigger(udphy, true); ++ } else { ++ if (cfg->hpd_event_trigger) ++ cfg->hpd_event_trigger(udphy, false); ++ } ++ } ++ ++ mutex_unlock(&udphy->mutex); ++ return 0; ++} ++ ++static int udphy_setup_typec_mux(struct rockchip_udphy *udphy) ++{ ++ struct typec_mux_desc mux_desc = {}; ++ ++ mux_desc.drvdata = udphy; ++ mux_desc.fwnode = dev_fwnode(udphy->dev); ++ mux_desc.set = usbdp_typec_mux_set; ++ ++ udphy->mux = typec_mux_register(udphy->dev, &mux_desc); ++ if (IS_ERR(udphy->mux)) { ++ dev_err(udphy->dev, "Error register typec mux: %ld\n", ++ PTR_ERR(udphy->mux)); ++ return PTR_ERR(udphy->mux); ++ } ++ ++ return 0; ++} ++ ++static void udphy_typec_mux_unregister(void *data) ++{ ++ struct rockchip_udphy *udphy = data; ++ ++ typec_mux_unregister(udphy->mux); ++} ++ ++static u32 udphy_dp_get_max_link_rate(struct rockchip_udphy *udphy, struct device_node *np) ++{ ++ u32 max_link_rate; ++ int ret; ++ ++ ret = of_property_read_u32(np, "max-link-rate", &max_link_rate); ++ if (ret) ++ return 8100; ++ ++ ret = rockchip_dp_phy_verify_link_rate(max_link_rate); ++ if (ret) { ++ dev_warn(udphy->dev, "invalid max-link-rate value:%d\n", max_link_rate); ++ max_link_rate = 8100; ++ } ++ ++ return max_link_rate; ++} ++ ++static const struct regmap_config rockchip_udphy_pma_regmap_cfg = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .fast_io = true, ++ .max_register = 0x20dc, ++}; ++ ++static int rockchip_udphy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct device_node *child_np; ++ struct phy_provider *phy_provider; ++ struct resource *res; ++ struct rockchip_udphy *udphy; ++ const struct rockchip_udphy_cfg *phy_cfgs; ++ void __iomem *base; ++ int id, ret; ++ ++ udphy = devm_kzalloc(dev, sizeof(*udphy), GFP_KERNEL); ++ if (!udphy) ++ return -ENOMEM; ++ ++ id = of_alias_get_id(dev->of_node, "usbdp"); ++ if (id < 0) ++ id = 0; ++ udphy->id = id; ++ ++ phy_cfgs = device_get_match_data(dev); ++ if (!phy_cfgs) { ++ dev_err(dev, "missing match data\n"); ++ return -EINVAL; ++ } ++ ++ udphy->cfgs = phy_cfgs; ++ ++ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ udphy->pma_regmap = devm_regmap_init_mmio(dev, base + UDPHY_PMA, ++ &rockchip_udphy_pma_regmap_cfg); ++ if (IS_ERR(udphy->pma_regmap)) ++ return PTR_ERR(udphy->pma_regmap); ++ ++ ret = udphy_parse_dt(udphy, dev); ++ if (ret) ++ return ret; ++ ++ ret = udphy_get_initial_status(udphy); ++ if (ret) ++ return ret; ++ ++ mutex_init(&udphy->mutex); ++ udphy->dev = dev; ++ platform_set_drvdata(pdev, udphy); ++ ++ if (device_property_present(dev, "orientation-switch")) { ++ ret = udphy_setup_orien_switch(udphy); ++ if (ret) ++ return ret; ++ ++ ret = devm_add_action_or_reset(dev, udphy_orien_switch_unregister, udphy); ++ if (ret) ++ return ret; ++ } ++ ++ if (device_property_present(dev, "mode-switch")) { ++ ret = udphy_setup_typec_mux(udphy); ++ if (ret) ++ return ret; ++ ++ ret = devm_add_action_or_reset(dev, udphy_typec_mux_unregister, udphy); ++ if (ret) ++ return ret; ++ } ++ ++ for_each_available_child_of_node(np, child_np) { ++ struct phy *phy; ++ ++ if (of_node_name_eq(child_np, "dp-port")) { ++ phy = devm_phy_create(dev, child_np, &rockchip_dp_phy_ops); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create dp phy: %pOFn\n", child_np); ++ goto put_child; ++ } ++ ++ phy_set_bus_width(phy, udphy_dplane_get(udphy)); ++ phy->attrs.max_link_rate = udphy_dp_get_max_link_rate(udphy, child_np); ++ } else if (of_node_name_eq(child_np, "usb3-port")) { ++ phy = devm_phy_create(dev, child_np, &rockchip_u3phy_ops); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create usb phy: %pOFn\n", child_np); ++ goto put_child; ++ } ++ } else ++ continue; ++ ++ phy_set_drvdata(phy, udphy); ++ } ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) { ++ dev_err(dev, "failed to register phy provider\n"); ++ goto put_child; ++ } ++ ++ return 0; ++ ++put_child: ++ of_node_put(child_np); ++ return ret; ++} ++ ++static int rk3588_udphy_refclk_set(struct rockchip_udphy *udphy) ++{ ++ unsigned long rate; ++ int ret; ++ ++ /* configure phy reference clock */ ++ rate = clk_get_rate(udphy->refclk); ++ dev_dbg(udphy->dev, "refclk freq %ld\n", rate); ++ ++ switch (rate) { ++ case 24000000: ++ ret = regmap_multi_reg_write(udphy->pma_regmap, rk3588_udphy_24m_refclk_cfg, ++ ARRAY_SIZE(rk3588_udphy_24m_refclk_cfg)); ++ if (ret) ++ return ret; ++ break; ++ case 26000000: ++ /* register default is 26MHz */ ++ ret = regmap_multi_reg_write(udphy->pma_regmap, rk3588_udphy_26m_refclk_cfg, ++ ARRAY_SIZE(rk3588_udphy_26m_refclk_cfg)); ++ if (ret) ++ return ret; ++ break; ++ default: ++ dev_err(udphy->dev, "unsupported refclk freq %ld\n", rate); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int rk3588_udphy_status_check(struct rockchip_udphy *udphy) ++{ ++ unsigned int val; ++ int ret; ++ ++ /* LCPLL check */ ++ if (udphy->mode & UDPHY_MODE_USB) { ++ ret = regmap_read_poll_timeout(udphy->pma_regmap, CMN_ANA_LCPLL_DONE_OFFSET, ++ val, (val & CMN_ANA_LCPLL_AFC_DONE) && ++ (val & CMN_ANA_LCPLL_LOCK_DONE), 200, 100000); ++ if (ret) { ++ dev_err(udphy->dev, "cmn ana lcpll lock timeout\n"); ++ return ret; ++ } ++ ++ if (!udphy->flip) { ++ ret = regmap_read_poll_timeout(udphy->pma_regmap, ++ TRSV_LN0_MON_RX_CDR_DONE_OFFSET, val, ++ val & TRSV_LN0_MON_RX_CDR_LOCK_DONE, ++ 200, 100000); ++ if (ret) ++ dev_err(udphy->dev, "trsv ln0 mon rx cdr lock timeout\n"); ++ } else { ++ ret = regmap_read_poll_timeout(udphy->pma_regmap, ++ TRSV_LN2_MON_RX_CDR_DONE_OFFSET, val, ++ val & TRSV_LN2_MON_RX_CDR_LOCK_DONE, ++ 200, 100000); ++ if (ret) ++ dev_err(udphy->dev, "trsv ln2 mon rx cdr lock timeout\n"); ++ } ++ } ++ ++ return 0; ++} ++ ++static int rk3588_udphy_init(struct rockchip_udphy *udphy) ++{ ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ int ret; ++ ++ /* enable rx lfps for usb */ ++ if (udphy->mode & UDPHY_MODE_USB) ++ grfreg_write(udphy->udphygrf, &cfg->grfcfg.rx_lfps, true); ++ ++ /* Step 1: power on pma and deassert apb rstn */ ++ grfreg_write(udphy->udphygrf, &cfg->grfcfg.low_pwrn, true); ++ ++ udphy_reset_deassert(udphy, "pma_apb"); ++ udphy_reset_deassert(udphy, "pcs_apb"); ++ ++ /* Step 2: set init sequence and phy refclk */ ++ ret = regmap_multi_reg_write(udphy->pma_regmap, rk3588_udphy_init_sequence, ++ ARRAY_SIZE(rk3588_udphy_init_sequence)); ++ if (ret) { ++ dev_err(udphy->dev, "init sequence set error %d\n", ret); ++ goto assert_apb; ++ } ++ ++ ret = rk3588_udphy_refclk_set(udphy); ++ if (ret) { ++ dev_err(udphy->dev, "refclk set error %d\n", ret); ++ goto assert_apb; ++ } ++ ++ /* Step 3: configure lane mux */ ++ regmap_update_bits(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, ++ CMN_DP_LANE_MUX_ALL | CMN_DP_LANE_EN_ALL, ++ FIELD_PREP(CMN_DP_LANE_MUX_N(3), udphy->lane_mux_sel[3]) | ++ FIELD_PREP(CMN_DP_LANE_MUX_N(2), udphy->lane_mux_sel[2]) | ++ FIELD_PREP(CMN_DP_LANE_MUX_N(1), udphy->lane_mux_sel[1]) | ++ FIELD_PREP(CMN_DP_LANE_MUX_N(0), udphy->lane_mux_sel[0]) | ++ FIELD_PREP(CMN_DP_LANE_EN_ALL, 0)); ++ ++ /* Step 4: deassert init rstn and wait for 200ns from datasheet */ ++ if (udphy->mode & UDPHY_MODE_USB) ++ udphy_reset_deassert(udphy, "init"); ++ ++ if (udphy->mode & UDPHY_MODE_DP) { ++ regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET, ++ CMN_DP_INIT_RSTN, ++ FIELD_PREP(CMN_DP_INIT_RSTN, 0x1)); ++ } ++ ++ udelay(1); ++ ++ /* Step 5: deassert cmn/lane rstn */ ++ if (udphy->mode & UDPHY_MODE_USB) { ++ udphy_reset_deassert(udphy, "cmn"); ++ udphy_reset_deassert(udphy, "lane"); ++ } ++ ++ /* Step 6: wait for lock done of pll */ ++ ret = rk3588_udphy_status_check(udphy); ++ if (ret) ++ goto assert_phy; ++ ++ return 0; ++ ++assert_phy: ++ udphy_reset_assert(udphy, "init"); ++ udphy_reset_assert(udphy, "cmn"); ++ udphy_reset_assert(udphy, "lane"); ++ ++assert_apb: ++ udphy_reset_assert(udphy, "pma_apb"); ++ udphy_reset_assert(udphy, "pcs_apb"); ++ return ret; ++} ++ ++static int rk3588_udphy_hpd_event_trigger(struct rockchip_udphy *udphy, bool hpd) ++{ ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ ++ udphy->dp_sink_hpd_sel = true; ++ udphy->dp_sink_hpd_cfg = hpd; ++ ++ grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, hpd); ++ ++ return 0; ++} ++ ++static int rk3588_udphy_dplane_enable(struct rockchip_udphy *udphy, int dp_lanes) ++{ ++ int i; ++ u32 val = 0; ++ ++ for (i = 0; i < dp_lanes; i++) ++ val |= BIT(udphy->dp_lane_sel[i]); ++ ++ regmap_update_bits(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, CMN_DP_LANE_EN_ALL, ++ FIELD_PREP(CMN_DP_LANE_EN_ALL, val)); ++ ++ if (!dp_lanes) ++ regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET, ++ CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0)); ++ ++ return 0; ++} ++ ++static int rk3588_udphy_dplane_select(struct rockchip_udphy *udphy) ++{ ++ u32 value = 0; ++ ++ switch (udphy->mode) { ++ case UDPHY_MODE_DP: ++ value |= 2 << udphy->dp_lane_sel[2] * 2; ++ value |= 3 << udphy->dp_lane_sel[3] * 2; ++ fallthrough; ++ case UDPHY_MODE_DP_USB: ++ value |= 0 << udphy->dp_lane_sel[0] * 2; ++ value |= 1 << udphy->dp_lane_sel[1] * 2; ++ break; ++ case UDPHY_MODE_USB: ++ break; ++ default: ++ break; ++ } ++ ++ regmap_write(udphy->vogrf, udphy->id ? RK3588_GRF_VO0_CON2 : RK3588_GRF_VO0_CON0, ++ ((DP_AUX_DIN_SEL | DP_AUX_DOUT_SEL | DP_LANE_SEL_ALL) << 16) | ++ FIELD_PREP(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel) | ++ FIELD_PREP(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel) | value); ++ ++ return 0; ++} ++ ++static int rk3588_dp_phy_set_rate(struct rockchip_udphy *udphy, ++ struct phy_configure_opts_dp *dp) ++{ ++ u32 val; ++ int ret; ++ ++ regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET, ++ CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0)); ++ ++ switch (dp->link_rate) { ++ case 1620: ++ udphy->bw = DP_BW_RBR; ++ break; ++ case 2700: ++ udphy->bw = DP_BW_HBR; ++ break; ++ case 5400: ++ udphy->bw = DP_BW_HBR2; ++ break; ++ case 8100: ++ udphy->bw = DP_BW_HBR3; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(udphy->pma_regmap, CMN_DP_LINK_OFFSET, CMN_DP_TX_LINK_BW, ++ FIELD_PREP(CMN_DP_TX_LINK_BW, udphy->bw)); ++ regmap_update_bits(udphy->pma_regmap, CMN_SSC_EN_OFFSET, CMN_ROPLL_SSC_EN, ++ FIELD_PREP(CMN_ROPLL_SSC_EN, dp->ssc)); ++ regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET, CMN_DP_CMN_RSTN, ++ FIELD_PREP(CMN_DP_CMN_RSTN, 0x1)); ++ ++ ret = regmap_read_poll_timeout(udphy->pma_regmap, CMN_ANA_ROPLL_DONE_OFFSET, val, ++ FIELD_GET(CMN_ANA_ROPLL_LOCK_DONE, val) && ++ FIELD_GET(CMN_ANA_ROPLL_AFC_DONE, val), ++ 0, 1000); ++ if (ret) { ++ dev_err(udphy->dev, "ROPLL is not lock\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void rk3588_dp_phy_set_voltage(struct rockchip_udphy *udphy, u8 bw, ++ u32 voltage, u32 pre, u32 lane) ++{ ++ u32 offset = 0x800 * lane; ++ u32 val; ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ const struct dp_tx_drv_ctrl (*dp_ctrl)[4]; ++ ++ dp_ctrl = udphy->mux ? cfg->dp_tx_ctrl_cfg_typec[bw] : cfg->dp_tx_ctrl_cfg[bw]; ++ val = dp_ctrl[voltage][pre].trsv_reg0204; ++ regmap_write(udphy->pma_regmap, 0x0810 + offset, val); ++ ++ val = dp_ctrl[voltage][pre].trsv_reg0205; ++ regmap_write(udphy->pma_regmap, 0x0814 + offset, val); ++ ++ val = dp_ctrl[voltage][pre].trsv_reg0206; ++ regmap_write(udphy->pma_regmap, 0x0818 + offset, val); ++ ++ val = dp_ctrl[voltage][pre].trsv_reg0207; ++ regmap_write(udphy->pma_regmap, 0x081c + offset, val); ++} ++ ++static int rk3588_dp_phy_set_voltages(struct rockchip_udphy *udphy, ++ struct phy_configure_opts_dp *dp) ++{ ++ u32 i, lane; ++ ++ for (i = 0; i < dp->lanes; i++) { ++ lane = udphy->dp_lane_sel[i]; ++ switch (dp->link_rate) { ++ case 1620: ++ case 2700: ++ regmap_update_bits(udphy->pma_regmap, TRSV_ANA_TX_CLK_OFFSET_N(lane), ++ LN_ANA_TX_SER_TXCLK_INV, ++ FIELD_PREP(LN_ANA_TX_SER_TXCLK_INV, ++ udphy->lane_mux_sel[lane])); ++ break; ++ case 5400: ++ case 8100: ++ regmap_update_bits(udphy->pma_regmap, TRSV_ANA_TX_CLK_OFFSET_N(lane), ++ LN_ANA_TX_SER_TXCLK_INV, ++ FIELD_PREP(LN_ANA_TX_SER_TXCLK_INV, 0x0)); ++ break; ++ } ++ ++ rk3588_dp_phy_set_voltage(udphy, udphy->bw, dp->voltage[i], dp->pre[i], lane); ++ } ++ ++ return 0; ++} ++ ++static int __maybe_unused udphy_resume(struct device *dev) ++{ ++ struct rockchip_udphy *udphy = dev_get_drvdata(dev); ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ ++ if (udphy->dp_sink_hpd_sel) ++ cfg->hpd_event_trigger(udphy, udphy->dp_sink_hpd_cfg); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops udphy_pm_ops = { ++ SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, udphy_resume) ++}; ++ ++static const char * const rk3588_udphy_rst_l[] = { ++ "init", "cmn", "lane", "pcs_apb", "pma_apb" ++}; ++ ++static const struct rockchip_udphy_cfg rk3588_udphy_cfgs = { ++ .num_rsts = ARRAY_SIZE(rk3588_udphy_rst_l), ++ .rst_list = rk3588_udphy_rst_l, ++ .grfcfg = { ++ /* u2phy-grf */ ++ .bvalid_phy_con = { 0x0008, 1, 0, 0x2, 0x3 }, ++ .bvalid_grf_con = { 0x0010, 3, 2, 0x2, 0x3 }, ++ ++ /* usb-grf */ ++ .usb3otg0_cfg = { 0x001c, 15, 0, 0x1100, 0x0188 }, ++ .usb3otg1_cfg = { 0x0034, 15, 0, 0x1100, 0x0188 }, ++ ++ /* usbdpphy-grf */ ++ .low_pwrn = { 0x0004, 13, 13, 0, 1 }, ++ .rx_lfps = { 0x0004, 14, 14, 0, 1 }, ++ }, ++ .vogrfcfg = { ++ { ++ .hpd_trigger = { 0x0000, 11, 10, 1, 3 }, ++ }, ++ { ++ .hpd_trigger = { 0x0008, 11, 10, 1, 3 }, ++ }, ++ }, ++ .dp_tx_ctrl_cfg = { ++ rk3588_dp_tx_drv_ctrl_rbr_hbr, ++ rk3588_dp_tx_drv_ctrl_rbr_hbr, ++ rk3588_dp_tx_drv_ctrl_hbr2, ++ rk3588_dp_tx_drv_ctrl_hbr3, ++ }, ++ .dp_tx_ctrl_cfg_typec = { ++ rk3588_dp_tx_drv_ctrl_rbr_hbr_typec, ++ rk3588_dp_tx_drv_ctrl_rbr_hbr_typec, ++ rk3588_dp_tx_drv_ctrl_hbr2, ++ rk3588_dp_tx_drv_ctrl_hbr3, ++ }, ++ .combophy_init = rk3588_udphy_init, ++ .dp_phy_set_rate = rk3588_dp_phy_set_rate, ++ .dp_phy_set_voltages = rk3588_dp_phy_set_voltages, ++ .hpd_event_trigger = rk3588_udphy_hpd_event_trigger, ++ .dplane_enable = rk3588_udphy_dplane_enable, ++ .dplane_select = rk3588_udphy_dplane_select, ++}; ++ ++static const struct of_device_id rockchip_udphy_dt_match[] = { ++ { ++ .compatible = "rockchip,rk3588-usbdp-phy", ++ .data = &rk3588_udphy_cfgs ++ }, ++ { /* sentinel */ } ++}; ++ ++MODULE_DEVICE_TABLE(of, rockchip_udphy_dt_match); ++ ++static struct platform_driver rockchip_udphy_driver = { ++ .probe = rockchip_udphy_probe, ++ .driver = { ++ .name = "rockchip-usbdp-phy", ++ .of_match_table = rockchip_udphy_dt_match, ++ .pm = &udphy_pm_ops, ++ }, ++}; ++ ++module_platform_driver(rockchip_udphy_driver); ++ ++MODULE_AUTHOR("Frank Wang "); ++MODULE_AUTHOR("Zhang Yubing "); ++MODULE_DESCRIPTION("Rockchip USBDP Combo PHY driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c +index 058d5b853..a3ef7d703 100644 +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -6588,9 +6588,9 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) + port->partner_desc.identity = &port->partner_ident; + port->port_type = port->typec_caps.type; + +- port->role_sw = usb_role_switch_get(port->dev); ++ port->role_sw = fwnode_usb_role_switch_get(tcpc->fwnode); + if (!port->role_sw) +- port->role_sw = fwnode_usb_role_switch_get(tcpc->fwnode); ++ port->role_sw = usb_role_switch_get(port->dev); + if (IS_ERR(port->role_sw)) { + err = PTR_ERR(port->role_sw); + goto out_destroy_wq; +diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h +index 6a46baa07..9042039f2 100644 +--- a/include/drm/bridge/dw_hdmi.h ++++ b/include/drm/bridge/dw_hdmi.h +@@ -6,12 +6,14 @@ + #ifndef __DW_HDMI__ + #define __DW_HDMI__ + ++#include + #include + + struct drm_display_info; + struct drm_display_mode; + struct drm_encoder; + struct dw_hdmi; ++struct dw_hdmi_qp; + struct platform_device; + + /** +@@ -92,6 +94,13 @@ enum dw_hdmi_phy_type { + DW_HDMI_PHY_VENDOR_PHY = 0xfe, + }; + ++struct dw_hdmi_audio_tmds_n { ++ unsigned long tmds; ++ unsigned int n_32k; ++ unsigned int n_44k1; ++ unsigned int n_48k; ++}; ++ + struct dw_hdmi_mpll_config { + unsigned long mpixelclock; + struct { +@@ -112,6 +121,15 @@ struct dw_hdmi_phy_config { + u16 vlev_ctr; /* voltage level control */ + }; + ++struct dw_hdmi_link_config { ++ bool dsc_mode; ++ bool frl_mode; ++ int frl_lanes; ++ int rate_per_lane; ++ int hcactive; ++ u8 pps_payload[128]; ++}; ++ + struct dw_hdmi_phy_ops { + int (*init)(struct dw_hdmi *hdmi, void *data, + const struct drm_display_info *display, +@@ -123,14 +141,52 @@ struct dw_hdmi_phy_ops { + void (*setup_hpd)(struct dw_hdmi *hdmi, void *data); + }; + ++struct dw_hdmi_qp_phy_ops { ++ int (*init)(struct dw_hdmi_qp *hdmi, void *data, ++ struct drm_display_mode *mode); ++ void (*disable)(struct dw_hdmi_qp *hdmi, void *data); ++ enum drm_connector_status (*read_hpd)(struct dw_hdmi_qp *hdmi, ++ void *data); ++ void (*update_hpd)(struct dw_hdmi_qp *hdmi, void *data, ++ bool force, bool disabled, bool rxsense); ++ void (*setup_hpd)(struct dw_hdmi_qp *hdmi, void *data); ++ void (*set_mode)(struct dw_hdmi_qp *dw_hdmi, void *data, ++ u32 mode_mask, bool enable); ++}; ++ ++struct dw_hdmi_property_ops { ++ void (*attach_properties)(struct drm_connector *connector, ++ unsigned int color, int version, ++ void *data); ++ void (*destroy_properties)(struct drm_connector *connector, ++ void *data); ++ int (*set_property)(struct drm_connector *connector, ++ struct drm_connector_state *state, ++ struct drm_property *property, ++ u64 val, ++ void *data); ++ int (*get_property)(struct drm_connector *connector, ++ const struct drm_connector_state *state, ++ struct drm_property *property, ++ u64 *val, ++ void *data); ++}; ++ + struct dw_hdmi_plat_data { + struct regmap *regm; + ++ //[CC:] not in dowstream + unsigned int output_port; + ++ unsigned long input_bus_format; + unsigned long input_bus_encoding; ++ unsigned int max_tmdsclk; ++ int id; + bool use_drm_infoframe; + bool ycbcr_420_allowed; ++ bool unsupported_yuv_input; ++ bool unsupported_deep_color; ++ bool is_hdmi_qp; + + /* + * Private data passed to all the .mode_valid() and .configure_phy() +@@ -139,6 +195,7 @@ struct dw_hdmi_plat_data { + void *priv_data; + + /* Platform-specific mode validation (optional). */ ++ //[CC:] downstream changed "struct dw_hdmi *hdmi" to "struct drm_connector *connector" + enum drm_mode_status (*mode_valid)(struct dw_hdmi *hdmi, void *data, + const struct drm_display_info *info, + const struct drm_display_mode *mode); +@@ -150,18 +207,51 @@ struct dw_hdmi_plat_data { + + /* Vendor PHY support */ + const struct dw_hdmi_phy_ops *phy_ops; ++ const struct dw_hdmi_qp_phy_ops *qp_phy_ops; + const char *phy_name; + void *phy_data; + unsigned int phy_force_vendor; + ++ /* split mode */ ++ bool split_mode; ++ bool first_screen; ++ struct dw_hdmi_qp *left; ++ struct dw_hdmi_qp *right; ++ + /* Synopsys PHY support */ + const struct dw_hdmi_mpll_config *mpll_cfg; ++ const struct dw_hdmi_mpll_config *mpll_cfg_420; + const struct dw_hdmi_curr_ctrl *cur_ctr; + const struct dw_hdmi_phy_config *phy_config; + int (*configure_phy)(struct dw_hdmi *hdmi, void *data, + unsigned long mpixelclock); + + unsigned int disable_cec : 1; ++ ++ //[CC:] 7b29b5f29585 ("drm/rockchip: dw_hdmi: Support HDMI 2.0 YCbCr 4:2:0") ++ unsigned long (*get_input_bus_format)(void *data); ++ unsigned long (*get_output_bus_format)(void *data); ++ unsigned long (*get_enc_in_encoding)(void *data); ++ unsigned long (*get_enc_out_encoding)(void *data); ++ ++ unsigned long (*get_quant_range)(void *data); ++ struct drm_property *(*get_hdr_property)(void *data); ++ struct drm_property_blob *(*get_hdr_blob)(void *data); ++ bool (*get_color_changed)(void *data); ++ int (*get_yuv422_format)(struct drm_connector *connector, ++ struct edid *edid); ++ int (*get_edid_dsc_info)(void *data, struct edid *edid); ++ int (*get_next_hdr_data)(void *data, struct edid *edid, ++ struct drm_connector *connector); ++ struct dw_hdmi_link_config *(*get_link_cfg)(void *data); ++ void (*set_grf_cfg)(void *data); ++ void (*convert_to_split_mode)(struct drm_display_mode *mode); ++ void (*convert_to_origin_mode)(struct drm_display_mode *mode); ++ int (*dclk_set)(void *data, bool enable); ++ ++ /* Vendor Property support */ ++ const struct dw_hdmi_property_ops *property_ops; ++ struct drm_connector *connector; + }; + + struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, +@@ -172,6 +262,7 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev, + struct drm_encoder *encoder, + const struct dw_hdmi_plat_data *plat_data); + ++void dw_hdmi_suspend(struct dw_hdmi *hdmi); + void dw_hdmi_resume(struct dw_hdmi *hdmi); + + void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense); +@@ -205,6 +296,32 @@ enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, + void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data, + bool force, bool disabled, bool rxsense); + void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data); ++void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi); ++void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val); ++bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi); ++int dw_hdmi_get_output_type_cap(struct dw_hdmi *hdmi); ++//void dw_hdmi_set_cec_adap(struct dw_hdmi *hdmi, struct cec_adapter *adap); ++ ++void dw_hdmi_qp_unbind(struct dw_hdmi_qp *hdmi); ++struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, ++ struct drm_encoder *encoder, ++ struct dw_hdmi_plat_data *plat_data); ++void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi); ++void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi); ++//void dw_hdmi_qp_cec_set_hpd(struct dw_hdmi_qp *hdmi, bool plug_in, bool change); ++//void dw_hdmi_qp_set_cec_adap(struct dw_hdmi_qp *hdmi, struct cec_adapter *adap); ++int dw_hdmi_qp_set_earc(struct dw_hdmi_qp *hdmi); ++void dw_hdmi_qp_set_sample_rate(struct dw_hdmi_qp *hdmi, unsigned int rate); ++void dw_hdmi_qp_set_channel_count(struct dw_hdmi_qp *hdmi, unsigned int cnt); ++void dw_hdmi_qp_set_channel_status(struct dw_hdmi_qp *hdmi, u8 *channel_status, ++ bool ref2stream); ++void dw_hdmi_qp_set_channel_allocation(struct dw_hdmi_qp *hdmi, unsigned int ca); ++//void dw_hdmi_qp_set_audio_infoframe(struct dw_hdmi_qp *hdmi, ++// struct hdmi_codec_params *hparms); ++//void dw_hdmi_qp_audio_enable(struct dw_hdmi_qp *hdmi); ++//void dw_hdmi_qp_audio_disable(struct dw_hdmi_qp *hdmi); ++int dw_hdmi_qp_set_plugged_cb(struct dw_hdmi_qp *hdmi, hdmi_codec_plugged_cb fn, ++ struct device *codec_dev); + + bool dw_hdmi_bus_fmt_is_420(struct dw_hdmi *hdmi); + +diff --git a/include/dt-bindings/clock/rockchip,rk3588-cru.h b/include/dt-bindings/clock/rockchip,rk3588-cru.h +index 5790b1391..50ba72980 100644 +--- a/include/dt-bindings/clock/rockchip,rk3588-cru.h ++++ b/include/dt-bindings/clock/rockchip,rk3588-cru.h +@@ -733,8 +733,9 @@ + #define ACLK_AV1_PRE 718 + #define PCLK_AV1_PRE 719 + #define HCLK_SDIO_PRE 720 ++#define PCLK_VO1GRF 721 + +-#define CLK_NR_CLKS (HCLK_SDIO_PRE + 1) ++#define CLK_NR_CLKS (PCLK_VO1GRF + 1) + + /* scmi-clocks indices */ + +diff --git a/include/dt-bindings/soc/rockchip,vop2.h b/include/dt-bindings/soc/rockchip,vop2.h +index 6e66a802b..668f199df 100644 +--- a/include/dt-bindings/soc/rockchip,vop2.h ++++ b/include/dt-bindings/soc/rockchip,vop2.h +@@ -10,5 +10,9 @@ + #define ROCKCHIP_VOP2_EP_LVDS0 5 + #define ROCKCHIP_VOP2_EP_MIPI1 6 + #define ROCKCHIP_VOP2_EP_LVDS1 7 ++#define ROCKCHIP_VOP2_EP_HDMI1 8 ++#define ROCKCHIP_VOP2_EP_EDP1 9 ++#define ROCKCHIP_VOP2_EP_DP0 10 ++#define ROCKCHIP_VOP2_EP_DP1 11 + + #endif /* __DT_BINDINGS_ROCKCHIP_VOP2_H */ +diff --git a/include/linux/math.h b/include/linux/math.h +index dd4152711..f80bfb375 100644 +--- a/include/linux/math.h ++++ b/include/linux/math.h +@@ -36,6 +36,17 @@ + + #define DIV_ROUND_UP __KERNEL_DIV_ROUND_UP + ++/** ++ * DIV_ROUND_UP_NO_OVERFLOW - divide two numbers and always round up ++ * @n: numerator / dividend ++ * @d: denominator / divisor ++ * ++ * This functions does the same as DIV_ROUND_UP, but internally uses a ++ * division and a modulo operation instead of math tricks. This way it ++ * avoids overflowing when handling big numbers. ++ */ ++#define DIV_ROUND_UP_NO_OVERFLOW(n, d) (((n) / (d)) + !!((n) % (d))) ++ + #define DIV_ROUND_DOWN_ULL(ll, d) \ + ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; }) + +diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h +new file mode 100644 +index 000000000..246192fa2 +--- /dev/null ++++ b/include/uapi/drm/rockchip_drm.h +@@ -0,0 +1,134 @@ ++/* ++ * ++ * Copyright (c) Fuzhou Rockchip Electronics Co.Ltd ++ * Authors: ++ * Mark Yao ++ * ++ * base on exynos_drm.h ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#ifndef _UAPI_ROCKCHIP_DRM_H ++#define _UAPI_ROCKCHIP_DRM_H ++ ++#include ++#include ++ ++/* ++ * Send vcnt event instead of blocking, ++ * like _DRM_VBLANK_EVENT ++ */ ++#define _DRM_ROCKCHIP_VCNT_EVENT 0x80000000 ++#define DRM_EVENT_ROCKCHIP_CRTC_VCNT 0xf ++ ++/* memory type definitions. */ ++enum drm_rockchip_gem_mem_type { ++ /* Physically Continuous memory. */ ++ ROCKCHIP_BO_CONTIG = 1 << 0, ++ /* cachable mapping. */ ++ ROCKCHIP_BO_CACHABLE = 1 << 1, ++ /* write-combine mapping. */ ++ ROCKCHIP_BO_WC = 1 << 2, ++ ROCKCHIP_BO_SECURE = 1 << 3, ++ /* keep kmap for cma buffer or alloc kmap for other type memory */ ++ ROCKCHIP_BO_ALLOC_KMAP = 1 << 4, ++ ROCKCHIP_BO_MASK = ROCKCHIP_BO_CONTIG | ROCKCHIP_BO_CACHABLE | ++ ROCKCHIP_BO_WC | ROCKCHIP_BO_SECURE | ROCKCHIP_BO_ALLOC_KMAP, ++}; ++ ++/** ++ * User-desired buffer creation information structure. ++ * ++ * @size: user-desired memory allocation size. ++ * @flags: user request for setting memory type or cache attributes. ++ * @handle: returned a handle to created gem object. ++ * - this handle will be set by gem module of kernel side. ++ */ ++struct drm_rockchip_gem_create { ++ uint64_t size; ++ uint32_t flags; ++ uint32_t handle; ++}; ++ ++struct drm_rockchip_gem_phys { ++ uint32_t handle; ++ uint32_t phy_addr; ++}; ++ ++/** ++ * A structure for getting buffer offset. ++ * ++ * @handle: a pointer to gem object created. ++ * @pad: just padding to be 64-bit aligned. ++ * @offset: relatived offset value of the memory region allocated. ++ * - this value should be set by user. ++ */ ++struct drm_rockchip_gem_map_off { ++ uint32_t handle; ++ uint32_t pad; ++ uint64_t offset; ++}; ++ ++/* acquire type definitions. */ ++enum drm_rockchip_gem_cpu_acquire_type { ++ DRM_ROCKCHIP_GEM_CPU_ACQUIRE_SHARED = 0x0, ++ DRM_ROCKCHIP_GEM_CPU_ACQUIRE_EXCLUSIVE = 0x1, ++}; ++ ++enum rockchip_crtc_feture { ++ ROCKCHIP_DRM_CRTC_FEATURE_ALPHA_SCALE, ++ ROCKCHIP_DRM_CRTC_FEATURE_HDR10, ++ ROCKCHIP_DRM_CRTC_FEATURE_NEXT_HDR, ++}; ++ ++enum rockchip_plane_feture { ++ ROCKCHIP_DRM_PLANE_FEATURE_SCALE, ++ ROCKCHIP_DRM_PLANE_FEATURE_ALPHA, ++ ROCKCHIP_DRM_PLANE_FEATURE_HDR2SDR, ++ ROCKCHIP_DRM_PLANE_FEATURE_SDR2HDR, ++ ROCKCHIP_DRM_PLANE_FEATURE_AFBDC, ++ ROCKCHIP_DRM_PLANE_FEATURE_PDAF_POS, ++ ROCKCHIP_DRM_PLANE_FEATURE_MAX, ++}; ++ ++enum rockchip_cabc_mode { ++ ROCKCHIP_DRM_CABC_MODE_DISABLE, ++ ROCKCHIP_DRM_CABC_MODE_NORMAL, ++ ROCKCHIP_DRM_CABC_MODE_LOWPOWER, ++ ROCKCHIP_DRM_CABC_MODE_USERSPACE, ++}; ++ ++struct drm_rockchip_vcnt_event { ++ struct drm_pending_event base; ++}; ++ ++#define DRM_ROCKCHIP_GEM_CREATE 0x00 ++#define DRM_ROCKCHIP_GEM_MAP_OFFSET 0x01 ++#define DRM_ROCKCHIP_GEM_CPU_ACQUIRE 0x02 ++#define DRM_ROCKCHIP_GEM_CPU_RELEASE 0x03 ++#define DRM_ROCKCHIP_GEM_GET_PHYS 0x04 ++#define DRM_ROCKCHIP_GET_VCNT_EVENT 0x05 ++ ++#define DRM_IOCTL_ROCKCHIP_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ ++ DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create) ++ ++#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET DRM_IOWR(DRM_COMMAND_BASE + \ ++ DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off) ++ ++#define DRM_IOCTL_ROCKCHIP_GEM_CPU_ACQUIRE DRM_IOWR(DRM_COMMAND_BASE + \ ++ DRM_ROCKCHIP_GEM_CPU_ACQUIRE, struct drm_rockchip_gem_cpu_acquire) ++ ++#define DRM_IOCTL_ROCKCHIP_GEM_CPU_RELEASE DRM_IOWR(DRM_COMMAND_BASE + \ ++ DRM_ROCKCHIP_GEM_CPU_RELEASE, struct drm_rockchip_gem_cpu_release) ++ ++#define DRM_IOCTL_ROCKCHIP_GEM_GET_PHYS DRM_IOWR(DRM_COMMAND_BASE + \ ++ DRM_ROCKCHIP_GEM_GET_PHYS, struct drm_rockchip_gem_phys) ++ ++#define DRM_IOCTL_ROCKCHIP_GET_VCNT_EVENT DRM_IOWR(DRM_COMMAND_BASE + \ ++ DRM_ROCKCHIP_GET_VCNT_EVENT, union drm_wait_vblank) ++ ++#endif /* _UAPI_ROCKCHIP_DRM_H */ diff --git a/system/hosts/server/files.nix b/system/hosts/server/files.nix index f0ea8aa..be8c814 100644 --- a/system/hosts/server/files.nix +++ b/system/hosts/server/files.nix @@ -65,7 +65,6 @@ in { }; services.nextcloud = { enable = true; - enableBrokenCiphersForSSE = false; package = pkgs.nextcloud27; autoUpdateApps.enable = true; # TODO: use socket auth and remove the next line diff --git a/system/hosts/server/home.nix b/system/hosts/server/home.nix index 899a19a..87c2503 100644 --- a/system/hosts/server/home.nix +++ b/system/hosts/server/home.nix @@ -12,6 +12,9 @@ let (x: "127.0.0.1:${toString x.port}") (builtins.attrValues (lib.filterAttrs (k: v: builtins.elem k names && v.enable) config.services.prometheus.exporters)); + hplip = pkgs.hplipWithPlugin.override { + withQt5 = false; + }; in { # a bunch of services for personal use not intended for the public # TODO: keycloakify this @@ -89,11 +92,11 @@ in { signKeyPath = "/secrets/cache-priv-key.pem"; settings.bind = "[::1]:5000"; }; - nix.settings.allowed-users = [ "nix-serve" "harmonia" "hydra" "hydra-www" ]; + nix.settings.allowed-users = [ "nix-serve" "harmonia" ] ++ lib.optionals config.services.hydra.enable [ "hydra" "hydra-www" ]; # make sure only hydra has access to this file # so normal nix evals don't have access to builtins nix.settings.extra-builtins-file = "/etc/nixos/extra-builtins.nix"; - impermanence.directories = [ + impermanence.directories = lib.mkIf config.services.hydra.enable [ { directory = /etc/nixos; user = "hydra"; group = "hydra"; mode = "0700"; } ]; nix.settings.allowed-uris = [ @@ -161,14 +164,16 @@ in { } ]; nix.distributedBuilds = true; - # limit CI CPU usage to 30% since I'm running everything else off this server too - systemd.services.nix-daemon.serviceConfig.CPUQuota = "240%"; + # limit CI CPU usage to 30% + # systemd.services.nix-daemon.serviceConfig.CPUQuota = "240%"; + # TODO: check if LimitNICE should be used instead + systemd.services.nix-daemon.serviceConfig.Nice = "19"; nix.daemonCPUSchedPolicy = "idle"; nix.daemonIOSchedClass = "idle"; systemd.services.hydra-evaluator = lib.mkIf config.services.hydra.enable { # https://github.com/NixOS/hydra/issues/1186 environment.GC_DONT_GC = "1"; - serviceConfig.CPUQuota = "240%"; + # serviceConfig.CPUQuota = "240%"; serviceConfig.CPUSchedulingPolicy = "idle"; serviceConfig.IOSchedulingClass = "idle"; }; @@ -386,7 +391,7 @@ in { ''; listenAddresses = [ "*:631" ]; defaultShared = true; - drivers = [ pkgs.hplip ]; + drivers = [ hplip ]; startWhenNeeded = false; }; services.avahi = { @@ -398,7 +403,7 @@ in { }; hardware.sane = { enable = true; - extraBackends = with pkgs; [ hplipWithPlugin ]; + extraBackends = [ hplip ]; }; nixpkgs.config.allowUnfreePredicate = pkg: lib.getName pkg == "hplip"; services.scanservjs.enable = true; diff --git a/system/hosts/server/maubot.nix b/system/hosts/server/maubot.nix index aba4940..ffe0bc5 100644 --- a/system/hosts/server/maubot.nix +++ b/system/hosts/server/maubot.nix @@ -61,9 +61,7 @@ in { translate rss ]; - services.maubot.pythonPackages = [ - (pkgs.pineapplebot.override { magic = cfg.pizzabotMagic; }) - ] ++ (with pkgs.python3.pkgs; [ + services.maubot.pythonPackages = with pkgs.python3.pkgs; [ levenshtein - ]); + ]; } diff --git a/system/hosts/server/options.nix b/system/hosts/server/options.nix index d7fbad4..0e70ced 100644 --- a/system/hosts/server/options.nix +++ b/system/hosts/server/options.nix @@ -57,10 +57,6 @@ description = "unhashed noreply password for internal access only. \ This should be different from the password that is hashed for better security"; }; - pizzabotMagic = mkOption { - type = types.str; - default = ""; - }; }; }; description = "server settings"; diff --git a/system/modules/certspotter.nix b/system/modules/certspotter.nix deleted file mode 100644 index a0a54a3..0000000 --- a/system/modules/certspotter.nix +++ /dev/null @@ -1,115 +0,0 @@ -{ config -, lib -, pkgs -, ... }: - -let - cfg = config.services.certspotter; -in { - options.services.certspotter = { - enable = lib.mkEnableOption "Cert Spotter, a Certificate Transparency log monitor"; - sendmailPath = lib.mkOption { - type = lib.types.path; - description = '' - Path to the `sendmail` binary. By default, the local sendmail wrapper is used - (see `config.services.mail.sendmailSetuidWrapper`). - ''; - example = lib.literalExpression ''"''${pkgs.system-sendmail}/bin/sendmail"''; - }; - watchlist = lib.mkOption { - type = with lib.types; listOf str; - description = "Domain names to watch. To monitor a domain with all subdomains, prefix its name with `.` (e.g. `.example.org`)."; - default = [ ]; - example = [ ".example.org" "another.example.com" ]; - }; - emailRecipients = lib.mkOption { - type = with lib.types; listOf str; - description = "A list of email addresses to send certificate updates to."; - default = [ ]; - }; - hooks = lib.mkOption { - type = with lib.types; listOf path; - description = '' - Scripts to run upon the detection of a new certificate. See `man 8 certspotter-script` or [the GitHub page](https://github.com/SSLMate/certspotter/blob/master/man/certspotter-script.md) for more info. - ''; - default = []; - example = lib.literalExpression '' - [ - (pkgs.writeShellScript "certspotter-hook" ''' - echo "Event summary: $SUMMARY." - ''') - ] - ''; - }; - extraFlags = lib.mkOption { - type = with lib.types; listOf str; - description = "Extra command-line arguments to pass to Cert Spotter"; - example = [ "-start_at_end" ]; - default = [ ]; - }; - }; - config = lib.mkIf cfg.enable { - assertions = [ - { - assertion = cfg.watchlist != [ ]; - message = "You must specify at least one domain for Cert Spotter to watch"; - } - { - assertion = cfg.hooks != [] || cfg.emailRecipients != []; - message = "You must specify at least one hook or email recipient for Cert Spotter"; - } - { - assertion = (cfg.emailRecipients != []) -> (cfg.sendmailPath != "/run/current-system/sw/bin/false"); - message = '' - You must configure the sendmail setuid wrapper (services.mail.sendmailSetuidWrapper) - or services.certspotter.sendmailPath - ''; - } - ]; - services.certspotter.sendmailPath = lib.mkMerge [ - (lib.mkIf (config.services.mail.sendmailSetuidWrapper != null) (lib.mkOptionDefault "/run/wrappers/bin/sendmail")) - (lib.mkIf (config.services.mail.sendmailSetuidWrapper == null) (lib.mkOptionDefault "/run/current-system/sw/bin/false")) - ]; - users.users.certspotter = { - group = "certspotter"; - home = "/var/lib/certspotter"; - createHome = true; - isSystemUser = true; - # uid = config.ids.uids.certspotter; - }; - users.groups.certspotter = { - # gid = config.ids.gids.certspotter; - }; - systemd.services.certspotter = { - description = "Cert Spotter - Certificate Transparency Monitor"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - environment.CERTSPOTTER_CONFIG_DIR = pkgs.linkFarm "certspotter-config" - (lib.toList { - name = "watchlist"; - path = pkgs.writeText "cerspotter-watchlist" (builtins.concatStringsSep "\n" cfg.watchlist); - } - ++ lib.optional (cfg.emailRecipients != [ ]) { - name = "email_recipients"; - path = pkgs.writeText "cerspotter-email_recipients" (builtins.concatStringsSep "\n" cfg.emailRecipients); - } - ++ lib.optional (cfg.hooks != [ ]) { - name = "hooks.d"; - path = pkgs.linkFarm "certspotter-hooks" (lib.imap1 (i: path: { - inherit path; - name = "hook${toString i}"; - }) cfg.hooks); - }); - serviceConfig = { - User = "certspotter"; - Group = "certspotter"; - StateDirectory = "certspotter"; - }; - script = '' - export CERTSPOTTER_STATE_DIR="$STATE_DIRECTORY" - cd "$CERTSPOTTER_STATE_DIR" - ${pkgs.certspotter}/bin/certspotter -sendmail ${cfg.sendmailPath} ${lib.escapeShellArgs cfg.extraFlags} - ''; - }; - }; -}