* Building linux and u-boot for aarch64
@ 2022-06-30 11:20 phodina via
2022-06-30 11:57 ` phodina
2022-06-30 19:04 ` linux and u-boot for pinenote-rk3568 Vagrant Cascadian
0 siblings, 2 replies; 5+ messages in thread
From: phodina via @ 2022-06-30 11:20 UTC (permalink / raw)
To: help-guix; +Cc: Mathieu Othacehe, janneke@gnu.org
[-- Attachment #1: Type: text/plain, Size: 552 bytes --]
Hi,
I'm attempting to build the Linux kernel and u-boot for ARM64 target - eink tablet.
Unfotutnately the patches are not yet in the upstream so I had to tweak little bit in the gnu/packages/linux.scm and gnu/bootloader/bootloaders.scm files.
However, the kernel won't still build. Any ideas on how to fix that?
This is my first porting new embedded board in Guix and it's iteresting to learn more of what's going under the lid.
FIY my interest in this to have some libre eink reader as well as notepad available on the market.
Kind regards,
Petr
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0002-gnu-u-boot-Remove-input-labels.patch --]
[-- Type: text/x-patch; name=0002-gnu-u-boot-Remove-input-labels.patch, Size: 1786 bytes --]
From 70fe04a19e76d1ded741480fc8922636dab942f9 Mon Sep 17 00:00:00 2001
From: Petr Hodina <phodina@protonmail.com>
Date: Thu, 30 Jun 2022 11:14:25 +0200
Subject: [PATCH 2/7] gnu: u-boot: Remove input labels.
* packages/bootloaders.scm (u-boot): Remove input labels.
diff --git a/gnu/packages/bootloaders.scm b/gnu/packages/bootloaders.scm
index 71a10f54d5..9f5e26fd28 100644
--- a/gnu/packages/bootloaders.scm
+++ b/gnu/packages/bootloaders.scm
@@ -16,6 +16,7 @@
;;; Copyright © 2021 Vincent Legoll <vincent.legoll@gmail.com>
;;; Copyright © 2021 Brice Waegeneire <brice@waegenei.re>
;;; Copyright © 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>
+;;; Copyright © 2022 Petr Hodina <phodina@protonmail.com>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -545,20 +546,20 @@ (define u-boot
(base32
"1l5w13dznj0z1ibqv2d6ljx2ma1gnf5x5ay3dqkqwxr6750nbq38"))))
(native-inputs
- `(("bc" ,bc)
- ("bison" ,bison)
- ("dtc" ,dtc)
- ("gnutls" ,gnutls)
- ("flex" ,flex)
- ("lz4" ,lz4)
- ("tinfo" ,ncurses/tinfo)
- ("perl" ,perl)
- ("python" ,python)
- ("python-coverage" ,python-coverage)
- ("python-pycryptodomex" ,python-pycryptodomex)
- ("python-pytest" ,python-pytest)
- ("swig" ,swig)
- ("libuuid" ,util-linux "lib")))
+ (list bc
+ bison
+ dtc
+ gnutls
+ flex
+ lz4
+ ncurses/tinfo
+ perl
+ python
+ python-coverage
+ python-pycryptodomex
+ python-pytest
+ swig
+ `(,util-linux "lib")))
(build-system gnu-build-system)
(home-page "https://www.denx.de/wiki/U-Boot/")
(synopsis "ARM bootloader")
--
2.36.1
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0004-gnu-Provide-private-implementation-with-selection-of.patch --]
[-- Type: text/x-patch; name=0004-gnu-Provide-private-implementation-with-selection-of.patch, Size: 2283 bytes --]
From ba8d1f158c67aab93c4f50daeca920b9580288b6 Mon Sep 17 00:00:00 2001
From: Petr Hodina <phodina@protonmail.com>
Date: Thu, 30 Jun 2022 11:16:46 +0200
Subject: [PATCH 4/7] gnu: Provide private implementation with selection of the
u-boot package.
* gnu/packages/bootloaders.scm: Provide private implementation of
make-u-boot-package and make-u-boot-sunxi64-package so that the public API
does not change. The private allows to specify the u-boot package.
diff --git a/gnu/packages/bootloaders.scm b/gnu/packages/bootloaders.scm
index fc8be31f37..cab97852f1 100644
--- a/gnu/packages/bootloaders.scm
+++ b/gnu/packages/bootloaders.scm
@@ -684,7 +684,7 @@ (define-public u-boot-tools
also initializes the boards (RAM etc). This package provides its
board-independent tools.")))
-(define-public (make-u-boot-package board triplet)
+(define* (make-u-boot-package-priv board triplet #:optional (u-boot u-boot))
"Returns a u-boot package for BOARD cross-compiled for TRIPLET."
(let ((same-arch? (lambda ()
(string=? (%current-system)
@@ -767,6 +767,9 @@ (define-public (make-u-boot-package board triplet)
uboot-files)
#t)))))))))
+(define-public (make-u-boot-package board triplet)
+ (make-u-boot-package-priv board triplet))
+
(define-public u-boot-malta
(make-u-boot-package "malta" "mips64el-linux-gnuabi64"))
@@ -797,8 +800,9 @@ (define-public u-boot-am335x-boneblack
(define-public u-boot-am335x-evm
(make-u-boot-package "am335x_evm" "arm-linux-gnueabihf"))
-(define-public (make-u-boot-sunxi64-package board triplet)
- (let ((base (make-u-boot-package board triplet)))
+(define* (make-u-boot-sunxi64-package-priv board triplet #:optional (u-boot
+u-boot))
+ (let ((base (make-u-boot-package-priv board triplet u-boot)))
(package
(inherit base)
(arguments
@@ -821,6 +825,9 @@ (define-public (make-u-boot-sunxi64-package board triplet)
`(("firmware" ,arm-trusted-firmware-sun50i-a64)
,@(package-native-inputs base))))))
+(define-public (make-u-boot-sunxi64-package board triplet)
+ (make-u-boot-sunxi64-package-priv board triplet))
+
(define-public u-boot-pine64-plus
(make-u-boot-sunxi64-package "pine64_plus" "aarch64-linux-gnu"))
--
2.36.1
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0003-gnu-Add-u-boot-pinenote.patch --]
[-- Type: text/x-patch; name=0003-gnu-Add-u-boot-pinenote.patch, Size: 1016 bytes --]
From 5c46f227d5b6cfd4a98f602d6b41fed1a85eea0a Mon Sep 17 00:00:00 2001
From: Petr Hodina <phodina@protonmail.com>
Date: Thu, 30 Jun 2022 11:15:18 +0200
Subject: [PATCH 3/7] gnu: Add u-boot-pinenote.
* gnu/packages/bootloaders.scm (u-boot-pinenote): New variable.
diff --git a/gnu/packages/bootloaders.scm b/gnu/packages/bootloaders.scm
index 9f5e26fd28..fc8be31f37 100644
--- a/gnu/packages/bootloaders.scm
+++ b/gnu/packages/bootloaders.scm
@@ -567,6 +567,19 @@ (define u-boot
also initializes the boards (RAM etc).")
(license license:gpl2+)))
+(define u-boot-pinenote
+ (package
+ (inherit u-boot)
+ (version "2022.07")
+ (source (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://gitlab.com/phodina/u-boot-quartz64")
+ (commit "quartz64")))
+ (sha256
+ (base32
+ "2l1w13dznj0z1ibqv2d6ljx2ma1gnf5x5ay3dqkqwxr6750nbq38"))))))
+
(define-public u-boot-tools
(package
(inherit u-boot)
--
2.36.1
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #5: 0006-gnu-Add-install-pinenote-rk3568-u-boot-and-u-boot-pi.patch --]
[-- Type: text/x-patch; name=0006-gnu-Add-install-pinenote-rk3568-u-boot-and-u-boot-pi.patch, Size: 2258 bytes --]
From 44b1385e6dbcc79bafebddc7699ed302fcb5fe91 Mon Sep 17 00:00:00 2001
From: Petr Hodina <phodina@protonmail.com>
Date: Thu, 30 Jun 2022 11:20:55 +0200
Subject: [PATCH 6/7] gnu: Add install-pinenote-rk3568-u-boot and
u-boot-pinenote-rk3568-bootloader.
* gnu/bootloader/u-boot.scm (install-pinenote-rk3568-u-boot,
u-boot-pinenote-rk3568-bootloader): New variables.
diff --git a/gnu/bootloader/u-boot.scm b/gnu/bootloader/u-boot.scm
index 6cad33b741..4410cc497a 100644
--- a/gnu/bootloader/u-boot.scm
+++ b/gnu/bootloader/u-boot.scm
@@ -3,6 +3,7 @@
;;; Copyright © 2017, 2019 Mathieu Othacehe <m.othacehe@gmail.com>
;;; Copyright © 2020 Julien Lepiller <julien@lepiller.eu>
;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
+;;; Copyright © 2022 Petr Hodina <phodina@protonmail.com>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -37,6 +38,7 @@ (define-module (gnu bootloader u-boot)
u-boot-novena-bootloader
u-boot-pine64-plus-bootloader
u-boot-pine64-lts-bootloader
+ u-boot-pinenote-rk3568-bootloader
u-boot-pinebook-bootloader
u-boot-pinebook-pro-rk3399-bootloader
u-boot-puma-rk3399-bootloader
@@ -127,6 +129,15 @@ (define install-rockpro64-rk3399-u-boot
(define install-pinebook-pro-rk3399-u-boot install-rockpro64-rk3399-u-boot)
+;; TODO: Supply correct offsets
+(define install-pinenote-rk3568-u-boot
+ #~(lambda (bootloader root-index image)
+ (let ((idb (string-append bootloader "/libexec/idbloader.img"))
+ (u-boot (string-append bootloader "/libexec/u-boot.itb")))
+ (write-file-on-device idb (stat:size (stat idb))
+ image (* 64 512))
+ (write-file-on-device u-boot (stat:size (stat u-boot))
+ image (* 16384 512)))))
\f
;;;
@@ -255,3 +266,9 @@ (define u-boot-pinebook-pro-rk3399-bootloader
(inherit u-boot-bootloader)
(package u-boot-pinebook-pro-rk3399)
(disk-image-installer install-pinebook-pro-rk3399-u-boot)))
+
+(define u-boot-pinenote-rk3568-bootloader
+ (bootloader
+ (inherit u-boot-bootloader)
+ (package u-boot-pinenote-rk3568)
+ (disk-image-installer install-pinenote-rk3568-u-boot)))
--
2.36.1
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #6: 0005-gnu-Add-u-boot-pinenote-rk3568.patch --]
[-- Type: text/x-patch; name=0005-gnu-Add-u-boot-pinenote-rk3568.patch, Size: 878 bytes --]
From 4d53d2bdb8526f72ed6cd3183ff2a2141990c900 Mon Sep 17 00:00:00 2001
From: Petr Hodina <phodina@protonmail.com>
Date: Thu, 30 Jun 2022 11:19:33 +0200
Subject: [PATCH 5/7] gnu: Add u-boot-pinenote-rk3568.
* gnu/packages/bootloaders.scm (u-boot-pinenote-rk3568): New variable.
diff --git a/gnu/packages/bootloaders.scm b/gnu/packages/bootloaders.scm
index cab97852f1..9c266b7bed 100644
--- a/gnu/packages/bootloaders.scm
+++ b/gnu/packages/bootloaders.scm
@@ -834,6 +834,9 @@ (define-public u-boot-pine64-plus
(define-public u-boot-pine64-lts
(make-u-boot-sunxi64-package "pine64-lts" "aarch64-linux-gnu"))
+(define-public u-boot-pinenote-rk3568
+ (make-u-boot-sunxi64-package-priv "pinenote" "aarch64-linux-gnu" u-boot-pinenote))
+
(define-public u-boot-pinebook
(let ((base (make-u-boot-sunxi64-package "pinebook" "aarch64-linux-gnu")))
(package
--
2.36.1
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #7: 0007-gnu-Add-image-for-pinenote.patch --]
[-- Type: text/x-patch; name=0007-gnu-Add-image-for-pinenote.patch, Size: 3327 bytes --]
From bca6f88623362f026bb007deda7d09e6ed8c2dff Mon Sep 17 00:00:00 2001
From: Petr Hodina <phodina@protonmail.com>
Date: Thu, 30 Jun 2022 11:22:05 +0200
Subject: [PATCH 7/7] gnu: Add image for pinenote.
* gnu/system/images/pinenote.scm: New file.
diff --git a/gnu/system/images/pinenote.scm b/gnu/system/images/pinenote.scm
new file mode 100644
index 0000000000..fe021d968e
--- /dev/null
+++ b/gnu/system/images/pinenote.scm
@@ -0,0 +1,74 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Petr Hodina <phodina@protonmail.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix 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 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu system images pinenote)
+ #:use-module (gnu bootloader)
+ #:use-module (gnu bootloader u-boot)
+ #:use-module (gnu image)
+ #:use-module (gnu packages linux)
+ #:use-module (guix platforms arm)
+ #:use-module (gnu services)
+ #:use-module (gnu services base)
+ #:use-module (gnu system)
+ #:use-module (gnu system file-systems)
+ #:use-module (gnu system image)
+ #:use-module (srfi srfi-26)
+ #:export (pinenote-barebones-os
+ pinenote-image-type
+ pinenote-raw-image))
+
+(define pinenote-barebones-os
+ (operating-system
+ (host-name "eink")
+ (timezone "Europe/Prague")
+ (locale "en_US.utf8")
+ (bootloader (bootloader-configuration
+ (bootloader u-boot-pinebook-pro-rk3399-bootloader)
+; (bootloader u-boot-pinenote-rk3568-bootloader)
+ (targets '("/dev/vda"))))
+ (initrd-modules '())
+ (kernel linux-libre-arm64-pinenote)
+ (file-systems (cons (file-system
+ (device (file-system-label "my-root"))
+ (mount-point "/")
+ (type "ext4"))
+ %base-file-systems))
+ (services (cons (service agetty-service-type
+ (agetty-configuration
+ (extra-options '("-L")) ; no carrier detect
+ (baud-rate "1500000")
+ (term "vt100")
+ (tty "ttyS2")))
+ %base-services))))
+
+(define pinenote-image-type
+ (image-type
+ (name 'pinenote-pro-raw)
+ (constructor (cut image-with-os
+ (raw-with-offset-disk-image (* 9 (expt 2 20))) ;9MiB
+ <>))))
+
+(define pinenote-barebones-raw-image
+ (image
+ (inherit
+ (os+platform->image pinenote-barebones-os aarch64-linux
+ #:type pinenote-image-type))
+ (name 'pinenote-barebones-raw-image)))
+
+;; Return the default image.
+pinenote-barebones-raw-image
--
2.36.1
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #8: 0001-gnu-Add-linux-libre-arm64-pinenote.patch --]
[-- Type: text/x-patch; name=0001-gnu-Add-linux-libre-arm64-pinenote.patch, Size: 155075 bytes --]
From a6499c4c384dd3c0e06968cc6987da0e47af6290 Mon Sep 17 00:00:00 2001
From: Petr Hodina <phodina@protonmail.com>
Date: Thu, 30 Jun 2022 10:12:39 +0200
Subject: [PATCH 1/7] gnu: Add linux-libre-arm64-pinenote.
* gnu/packages/linux.scm (linux-libre-arm64-pinenote): New variable.
* gnu/local.mk: Add patches.
* linux-libre-arm64-pinenote-battery-level.patch: New file.
* linux-libre-arm64-pinenote-defconfig.patch: New file.
* linux-libre-arm64-pinenote-dtsi.patch: New file.
* linux-libre-arm64-pinenote-ebc-patches.patch: New file.
* linux-libre-arm64-pinenote-touchscreen-1.patch: New file.
* linux-libre-arm64-pinenote-touchscreen-2.patch: New file.
* linux-libre-arm64-rockchip-add-hdmi-sound.patch: New file.
diff --git a/gnu/local.mk b/gnu/local.mk
index 353b91cfd2..c19f1f418b 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1444,6 +1444,13 @@ dist_patch_DATA = \
%D%/packages/patches/linbox-fix-pkgconfig.patch \
%D%/packages/patches/linphone-desktop-without-sdk.patch \
%D%/packages/patches/linux-libre-support-for-Pinebook-Pro.patch \
+ %D%/packages/patches/linux-libre-arm64-pinenote-touchscreen-1.patch \
+ %D%/packages/patches/linux-libre-arm64-pinenote-touchscreen-2.patch \
+ %D%/packages/patches/linux-libre-arm64-pinenote-battery-level.patch \
+ %D%/packages/patches/linux-libre-arm64-rockchip-add-hdmi-sound.patch \
+ %D%/packages/patches/linux-libre-arm64-pinenote-defconfig.patch \
+ %D%/packages/patches/linux-libre-arm64-pinenote-dtsi.patch \
+ %D%/packages/patches/linux-libre-arm64-pinenote-ebc-patches.patch \
%D%/packages/patches/linux-pam-no-setfsuid.patch \
%D%/packages/patches/linuxdcpp-openssl-1.1.patch \
%D%/packages/patches/lirc-localstatedir.patch \
diff --git a/gnu/packages/linux.scm b/gnu/packages/linux.scm
index 8f7b4f4f5b..bae8ffb959 100644
--- a/gnu/packages/linux.scm
+++ b/gnu/packages/linux.scm
@@ -60,7 +60,7 @@
;;; Copyright © 2021 Josselin Poiret <josselin.poiret@protonmail.ch>
;;; Copyright © 2021 Olivier Dion <olivier.dion@polymtl.ca>
;;; Copyright © 2021 Solene Rapenne <solene@perso.pw>
-;;; Copyright © 2021 Petr Hodina <phodina@protonmail.com>
+;;; Copyright © 2021, 2022 Petr Hodina <phodina@protonmail.com>
;;; Copyright © 2022 Artyom V. Poptsov <poptsov.artyom@gmail.com>
;;; Copyright © 2022 Rene Saavedra <nanuui@protonmail.com>
@@ -349,6 +349,12 @@ (define (%upstream-linux-source version hash)
"linux-" version ".tar.xz"))
(sha256 hash)))
+(define (%pinenote-linux-source version hash)
+ (origin
+ (method url-fetch)
+ (uri (string-append "https://github.com/phodina/linux-pinenote/tarball/" version))
+ (sha256 hash)))
+
;; The current "stable" kernels. That is, the most recently released major
;; versions that are still supported upstream.
@@ -367,6 +373,14 @@ (define-public linux-libre-5.17-pristine-source
(%upstream-linux-source version hash)
deblob-scripts-5.17)))
+(define-public linux-libre-arm64-pinenote-pristine-source
+ (let ((version linux-libre-5.17-version)
+ (commit "c91a48e028fe1f6a0e5748fd87c446aa7e31811b")
+ (hash (base32 "1xwyvvps1r3zl1n9szlgrj8ylw5sgj6fr52fig9f2cc6ai331bbn")))
+ (make-linux-libre-source version
+ (%pinenote-linux-source commit hash)
+ deblob-scripts-5.17)))
+
;; The "longterm" kernels — the older releases with long-term upstream support.
;; Here are the support timelines:
;; <https://www.kernel.org/category/releases.html>
@@ -488,6 +502,25 @@ (define (source-with-patches source patches)
(patches (append (origin-patches source)
patches))))
+(define-public linux-libre-arm64-pinenote-source
+ (source-with-patches linux-libre-arm64-pinenote-pristine-source
+ (list %boot-logo-patch
+ %linux-libre-arm-export-__sync_icache_dcache-patch
+ (search-patch
+"linux-libre-arm64-pinenote-touchscreen-1.patch")
+ (search-patch
+"linux-libre-arm64-pinenote-touchscreen-2.patch")
+ (search-patch
+"linux-libre-arm64-pinenote-battery-level.patch")
+ (search-patch
+"linux-libre-arm64-rockchip-add-hdmi-sound.patch")
+ (search-patch
+"linux-libre-arm64-pinenote-defconfig.patch")
+ (search-patch
+"linux-libre-arm64-pinenote-dtsi.patch")
+ (search-patch
+"linux-libre-arm64-pinenote-ebc-patches.patch"))))
+
(define-public linux-libre-5.17-source
(source-with-patches linux-libre-5.17-pristine-source
(list %boot-logo-patch
@@ -1072,6 +1105,15 @@ (define-public linux-libre-arm-omap2plus-4.14
#:defconfig "omap2plus_defconfig"
#:extra-version "arm-omap2plus"))
+(define-public linux-libre-arm64-pinenote
+ (make-linux-libre* linux-libre-version
+ linux-libre-gnu-revision
+ linux-libre-arm64-pinenote-source
+ '("aarch64-linux")
+ #:defconfig "pinenote_defconfig"
+ #:extra-options
+ %default-extra-linux-options))
+
(define-public linux-libre-arm64-generic
(make-linux-libre* linux-libre-version
linux-libre-gnu-revision
diff --git a/gnu/packages/patches/linux-libre-arm64-pinenote-battery-level.patch b/gnu/packages/patches/linux-libre-arm64-pinenote-battery-level.patch
new file mode 100644
index 0000000000..a0df8c3763
--- /dev/null
+++ b/gnu/packages/patches/linux-libre-arm64-pinenote-battery-level.patch
@@ -0,0 +1,62 @@
+From 822294664906499682b55264ae0553ee05caa352 Mon Sep 17 00:00:00 2001
+From: Dorian Rudolph <mail@dorianrudolph.com>
+Date: Sat, 14 May 2022 14:28:37 +0200
+Subject: [PATCH] fix power_supply_temp2resist_simple and
+ power_supply_ocv2cap_simple
+
+---
+ drivers/power/supply/power_supply_core.c | 24 ++++++++++++------------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
+index ec838c9bcc0a5e..3828ba9d0eab90 100644
+--- a/drivers/power/supply/power_supply_core.c
++++ b/drivers/power/supply/power_supply_core.c
+@@ -801,17 +801,17 @@ int power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *t
+ {
+ int i, high, low;
+
+- /* Break loop at table_len - 1 because that is the highest index */
+- for (i = 0; i < table_len - 1; i++)
++ for (i = 0; i < table_len; i++)
+ if (temp > table[i].temp)
+ break;
+
+ /* The library function will deal with high == low */
+- if ((i == 0) || (i == (table_len - 1)))
+- high = i;
++ if (i == 0)
++ high = low = i;
++ else if (i == table_len)
++ high = low = i - 1;
+ else
+- high = i - 1;
+- low = i;
++ high = (low = i) - 1;
+
+ return fixp_linear_interpolate(table[low].temp,
+ table[low].resistance,
+@@ -838,17 +838,17 @@ int power_supply_ocv2cap_simple(struct power_supply_battery_ocv_table *table,
+ {
+ int i, high, low;
+
+- /* Break loop at table_len - 1 because that is the highest index */
+- for (i = 0; i < table_len - 1; i++)
++ for (i = 0; i < table_len; i++)
+ if (ocv > table[i].ocv)
+ break;
+
+ /* The library function will deal with high == low */
+- if ((i == 0) || (i == (table_len - 1)))
+- high = i - 1;
++ if (i == 0)
++ high = low = i;
++ else if (i == table_len)
++ high = low = i - 1;
+ else
+- high = i; /* i.e. i == 0 */
+- low = i;
++ high = (low = i) - 1;
+
+ return fixp_linear_interpolate(table[low].ocv,
+ table[low].capacity,
diff --git a/gnu/packages/patches/linux-libre-arm64-pinenote-defconfig.patch b/gnu/packages/patches/linux-libre-arm64-pinenote-defconfig.patch
new file mode 100644
index 0000000000..97898a141e
--- /dev/null
+++ b/gnu/packages/patches/linux-libre-arm64-pinenote-defconfig.patch
@@ -0,0 +1,48 @@
+diff --git a/arch/arm64/configs/pinenote_defconfig b/arch/arm64/configs/pinenote_defconfig
+index bea435dc92c4..86cdaa92cc2f 100644
+--- a/arch/arm64/configs/pinenote_defconfig
++++ b/arch/arm64/configs/pinenote_defconfig
+@@ -86,6 +86,7 @@ CONFIG_ARCH_ROCKCHIP=y
+ # CONFIG_NVIDIA_CARMEL_CNP_ERRATUM is not set
+ # CONFIG_SOCIONEXT_SYNQUACER_PREITS is not set
+ CONFIG_SCHED_MC=y
++CONFIG_SCHED_SMT=y
+ CONFIG_NR_CPUS=4
+ CONFIG_HZ_1000=y
+ # CONFIG_UNMAP_KERNEL_AT_EL0 is not set
+@@ -155,7 +156,7 @@ CONFIG_CGROUP_NET_PRIO=y
+ CONFIG_BT=m
+ CONFIG_BT_RFCOMM=y
+ CONFIG_BT_RFCOMM_TTY=y
+-CONFIG_BT_HIDP=y
++CONFIG_BT_HIDP=m
+ CONFIG_BT_HS=y
+ CONFIG_BT_LEDS=y
+ CONFIG_BT_HCIUART=m
+@@ -223,14 +224,16 @@ CONFIG_BRCMFMAC=m
+ # CONFIG_WLAN_VENDOR_TI is not set
+ # CONFIG_WLAN_VENDOR_ZYDAS is not set
+ # CONFIG_WLAN_VENDOR_QUANTENNA is not set
++CONFIG_INPUT_MOUSEDEV=m
+ CONFIG_INPUT_EVDEV=y
+ CONFIG_KEYBOARD_ADC=m
+ # CONFIG_KEYBOARD_ATKBD is not set
+ CONFIG_KEYBOARD_GPIO=y
+-# CONFIG_INPUT_MOUSE is not set
++CONFIG_INPUT_MOUSE=y
+ CONFIG_INPUT_TOUCHSCREEN=y
+ CONFIG_TOUCHSCREEN_CYTTSP4_CORE=m
+ CONFIG_TOUCHSCREEN_CYTTSP4_I2C=m
++CONFIG_TOUCHSCREEN_CYTTSP5=m
+ CONFIG_INPUT_MISC=y
+ CONFIG_INPUT_RK805_PWRKEY=y
+ CONFIG_INPUT_WS8100_PEN=m
+@@ -459,6 +462,8 @@ CONFIG_SND_SOC_SIMPLE_AMPLIFIER=m
+ CONFIG_SND_SIMPLE_CARD=m
+ CONFIG_HID_BATTERY_STRENGTH=y
+ CONFIG_HIDRAW=y
++CONFIG_UHID=m
++CONFIG_HID_MICROSOFT=y
+ CONFIG_USB_HIDDEV=y
+ CONFIG_I2C_HID_OF=m
+ CONFIG_I2C_HID_OF_GOODIX=m
diff --git a/gnu/packages/patches/linux-libre-arm64-pinenote-dtsi.patch b/gnu/packages/patches/linux-libre-arm64-pinenote-dtsi.patch
new file mode 100644
index 0000000000..e12b84a86d
--- /dev/null
+++ b/gnu/packages/patches/linux-libre-arm64-pinenote-dtsi.patch
@@ -0,0 +1,167 @@
+diff --git a/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi
+index 59ac178881b3..ec7183330b40 100644
+--- a/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi
++++ b/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi
+@@ -51,11 +51,11 @@ battery_cell: battery-cell {
+
+ ocv-capacity-celsius = <20>;
+ ocv-capacity-table-0 = <4168000 100>,
+- <4109000 95>, <4066000 90>, <4023000 85>, <3985000 80>,
+- <3954000 75>, <3924000 70>, <3897000 65>, <3866000 60>,
+- <3826000 55>, <3804000 50>, <3789000 45>, <3777000 40>,
+- <3770000 35>, <3763000 30>, <3750000 25>, <3732000 20>,
+- <3710000 15>, <3680000 10>, <3670000 5>, <3500000 0>;
++ <4109000 95>, <4066000 90>, <4023000 85>, <3985000 80>,
++ <3954000 75>, <3924000 70>, <3897000 65>, <3866000 60>,
++ <3826000 55>, <3804000 50>, <3789000 45>, <3777000 40>,
++ <3770000 35>, <3763000 30>, <3750000 25>, <3732000 20>,
++ <3710000 15>, <3680000 10>, <3670000 5>, <3500000 0>;
+ };
+
+ bt_sco_codec: bt-sco-codec {
+@@ -63,26 +63,26 @@ bt_sco_codec: bt-sco-codec {
+ #sound-dai-cells = <1>;
+ };
+
+- bt-sound {
+- compatible = "simple-audio-card";
+- #address-cells = <1>;
+- #size-cells = <0>;
+- simple-audio-card,name = "PineNote Bluetooth";
+-
+- simple-audio-card,dai-link@0 {
+- format = "i2s";
+- frame-master = <&bt_link0_cpu>;
+- bitclock-master = <&bt_link0_cpu>;
+-
+- bt_link0_cpu: cpu {
+- sound-dai = <&i2s2_2ch>;
+- };
+-
+- bt_link0_codec: codec {
+- sound-dai = <&bt_sco_codec 0>;
+- };
+- };
+- };
++ // bt-sound {
++ // compatible = "simple-audio-card";
++ // #address-cells = <1>;
++ // #size-cells = <0>;
++ // simple-audio-card,name = "PineNote Bluetooth";
++//
++ // simple-audio-card,dai-link@0 {
++ // format = "i2s";
++ // frame-master = <&bt_link0_cpu>;
++ // bitclock-master = <&bt_link0_cpu>;
++//
++ // bt_link0_cpu: cpu {
++ // sound-dai = <&i2s2_2ch>;
++ // };
++//
++ // bt_link0_codec: codec {
++ // sound-dai = <&bt_sco_codec 0>;
++ // };
++ // };
++ // };
+
+ dmic_codec: dmic-codec {
+ compatible = "dmic-codec";
+@@ -95,15 +95,15 @@ gpio-keys {
+ pinctrl-0 = <&hall_int_l>;
+ pinctrl-names = "default";
+
+- cover {
+- label = "cover";
+- gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_LOW>;
+- linux,input-type = <EV_SW>;
+- linux,code = <SW_MACHINE_COVER>;
+- linux,can-disable;
+- wakeup-event-action = <EV_ACT_DEASSERTED>;
+- wakeup-source;
+- };
++ /* cover { */
++ /* label = "cover"; */
++ /* gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_LOW>; */
++ /* linux,input-type = <EV_SW>; */
++ /* linux,code = <SW_MACHINE_COVER>; */
++ /* linux,can-disable; */
++ /* wakeup-event-action = <EV_ACT_DEASSERTED>; */
++ /* wakeup-source; */
++ /* }; */
+ };
+
+ gpio-leds {
+@@ -166,13 +166,13 @@ sound {
+ simple-audio-card,name = "PineNote";
+ simple-audio-card,aux-devs = <&spk_amp>;
+ simple-audio-card,widgets = "Headphone", "Headphones",
+- "Speaker", "Internal Speakers";
++ "Speaker", "Internal Speakers";
+ simple-audio-card,routing = "Headphones", "HPOL",
+- "Headphones", "HPOR",
+- "Internal Speakers", "Speaker Amp OUTL",
+- "Internal Speakers", "Speaker Amp OUTR",
+- "Speaker Amp INL", "HPOL",
+- "Speaker Amp INR", "HPOR";
++ "Headphones", "HPOR",
++ "Internal Speakers", "Speaker Amp OUTL",
++ "Internal Speakers", "Speaker Amp OUTR",
++ "Speaker Amp INL", "HPOL",
++ "Speaker Amp INR", "HPOR";
+ simple-audio-card,pin-switches = "Internal Speakers";
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -340,7 +340,7 @@ &eink {
+
+ &gpu {
+ mali-supply = <&vdd_gpu_npu>;
+- // status = "okay";
++ status = "okay";
+ };
+
+ &i2c0 {
+@@ -669,19 +669,31 @@ accelerometer@18 {
+ st,drdy-int-pin = <1>;
+ vdd-supply = <&vcc_3v3>;
+ vddio-supply = <&vcc_3v3>;
+- };
+-
+- touchscreen@24 {
+- compatible = "cypress,tt21000";
+- hid-descr-addr = <0x1>;
+- reg = <0x24>;
+- interrupt-parent = <&gpio0>;
+- interrupts = <RK_PA6 IRQ_TYPE_LEVEL_LOW>;
+- pinctrl-0 = <&ts_int_l>, <&ts_rst_l>;
+- pinctrl-names = "default";
+- reset-gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>;
+- vdd-supply = <&vcc_3v3_pmu>;
+- };
++ mount-matrix = "-1", "0", "0",
++ "0", "1", "0",
++ "0", "0", "1";
++ };
++
++ // from pgwipeouts dtsi
++ touchscreen@24 {
++ compatible = "cypress,tma448";
++// compatible = "cypress,tt21000";
++ hid-descr-addr = <0x1>;
++ reg = <0x24>;
++ interrupt-parent = <&gpio0>;
++ interrupts = <RK_PA6 IRQ_TYPE_LEVEL_LOW>;
++ pinctrl-0 = <&ts_int_l>, <&ts_rst_l>;
++ pinctrl-names = "default";
++ reset-gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>;
++ vdd-supply = <&vcc_3v3_pmu>;
++ touchscreen-max-pressure = <46>;
++ touchscreen-min-x = <10>;
++ touchscreen-min-y = <5>;
++ touchscreen-size-x = <1863>;
++ touchscreen-size-y = <1399>;
++ touchscreen-x-mm = <1864>;
++ touchscreen-y-mm = <1400>;
++ };
+ };
+
+ &i2s1_8ch {
diff --git a/gnu/packages/patches/linux-libre-arm64-pinenote-ebc-patches.patch b/gnu/packages/patches/linux-libre-arm64-pinenote-ebc-patches.patch
new file mode 100644
index 0000000000..7fcd50858b
--- /dev/null
+++ b/gnu/packages/patches/linux-libre-arm64-pinenote-ebc-patches.patch
@@ -0,0 +1,2852 @@
+From cb80d9f99f75ea1ed6c8c6b194910b6ae9574a07 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Mon, 30 May 2022 21:06:31 +0200
+Subject: [PATCH 01/37] [rockchip_ebc] when doing partial refreshes, wait for
+ each frame to finish (i.e. wait for the irc from the epd controller) before
+ starting to fill in the buffers for the next frame
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 15 ++++++++++-----
+ 1 file changed, 10 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 285f43bc6d91..d7ed954e1618 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -580,11 +580,11 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ dma_sync_single_for_device(dev, phase_handle,
+ ctx->phase_size, DMA_TO_DEVICE);
+
+- if (frame) {
+- if (!wait_for_completion_timeout(&ebc->display_end,
+- EBC_FRAME_TIMEOUT))
+- drm_err(drm, "Frame %d timed out!\n", frame);
+- }
++ /* if (frame) { */
++ /* if (!wait_for_completion_timeout(&ebc->display_end, */
++ /* EBC_FRAME_TIMEOUT)) */
++ /* drm_err(drm, "Frame %d timed out!\n", frame); */
++ /* } */
+
+ if (list_empty(&areas))
+ break;
+@@ -597,6 +597,11 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ regmap_write(ebc->regmap, EBC_DSP_START,
+ ebc->dsp_start |
+ EBC_DSP_START_DSP_FRM_START);
++ if (frame) {
++ if (!wait_for_completion_timeout(&ebc->display_end,
++ EBC_FRAME_TIMEOUT))
++ drm_err(drm, "Frame %d timed out!\n", frame);
++ }
+ }
+ }
+
+--
+2.30.2
+
+
+From cdbfcec184ed55da2d55a8622240e5a30c03eb1e Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Mon, 30 May 2022 21:13:57 +0200
+Subject: [PATCH 02/37] [rockchip_ebc] change the dma mappings in
+ rockchip_ebc_partial_refresh according to the documentation in
+ Documentation/core-api/dma-api.rst and use dma_map_single to get dma address
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 19 ++++++++++++++++---
+ 1 file changed, 16 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index d7ed954e1618..b0dfc493c059 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -13,6 +13,7 @@
+ #include <linux/pm_runtime.h>
+ #include <linux/regmap.h>
+ #include <linux/regulator/consumer.h>
++#include <linux/dma-mapping.h>
+
+ #include <drm/drm_atomic.h>
+ #include <drm/drm_atomic_helper.h>
+@@ -479,8 +480,8 @@ static void rockchip_ebc_blit_pixels(const struct rockchip_ebc_ctx *ctx,
+ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ struct rockchip_ebc_ctx *ctx)
+ {
+- dma_addr_t next_handle = virt_to_phys(ctx->next);
+- dma_addr_t prev_handle = virt_to_phys(ctx->prev);
++ // dma_addr_t next_handle = virt_to_phys(ctx->next);
++ // dma_addr_t prev_handle = virt_to_phys(ctx->prev);
+ struct rockchip_ebc_area *area, *next_area;
+ u32 last_phase = ebc->lut.num_phases - 1;
+ struct drm_device *drm = &ebc->drm;
+@@ -489,10 +490,18 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ LIST_HEAD(areas);
+ u32 frame;
+
++ dma_addr_t next_handle = dma_map_single(dev, ctx->next, ctx->gray4_size, DMA_TO_DEVICE);
++ dma_addr_t prev_handle = dma_map_single(dev, ctx->prev, ctx->gray4_size, DMA_TO_DEVICE);
++
++ dma_addr_t phase_handles[2];
++ phase_handles[0] = dma_map_single(dev, ctx->phase[0], ctx->gray4_size, DMA_TO_DEVICE);
++ phase_handles[1] = dma_map_single(dev, ctx->phase[1], ctx->gray4_size, DMA_TO_DEVICE);
++
+ for (frame = 0;; frame++) {
+ /* Swap phase buffers to minimize latency between frames. */
+ u8 *phase_buffer = ctx->phase[frame % 2];
+- dma_addr_t phase_handle = virt_to_phys(phase_buffer);
++ // dma_addr_t phase_handle = virt_to_phys(phase_buffer);
++ dma_addr_t phase_handle = phase_handles[frame % 2];
+ bool sync_next = false;
+ bool sync_prev = false;
+
+@@ -603,6 +612,10 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ drm_err(drm, "Frame %d timed out!\n", frame);
+ }
+ }
++ dma_unmap_single(dev, next_handle, ctx->gray4_size, DMA_TO_DEVICE);
++ dma_unmap_single(dev, prev_handle, ctx->gray4_size, DMA_TO_DEVICE);
++ dma_unmap_single(dev, phase_handles[0], ctx->gray4_size, DMA_TO_DEVICE);
++ dma_unmap_single(dev, phase_handles[1], ctx->gray4_size, DMA_TO_DEVICE);
+ }
+
+ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+--
+2.30.2
+
+
+From f79e16df9a8f7853e206d5f4cb122ca231a0b2ab Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Mon, 30 May 2022 21:25:29 +0200
+Subject: [PATCH 03/37] [rockchip_ebc] Some people (including me on a Debian
+ sid installation) see kernel panics/hangs on reboot/shutdown (and module
+ unload) with the new driver. Investigation shows that the refresh thread
+ hangs on the schedule() command, which lead me to believe that the thread is
+ not properly shut down when the kernel module is triggered to shutdown. This
+ patch attempts to
+
+- explicitly shut down the refresh thread before termination
+- adds some control commands to quickly finish for various park/stop
+ states
+- only attempts to park the refresh thread if it is not dead yet (which
+ caused a kernel panic on shutdown)
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 24 +++++++++++++++---------
+ 1 file changed, 15 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index b0dfc493c059..4df73794281b 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -13,6 +13,7 @@
+ #include <linux/pm_runtime.h>
+ #include <linux/regmap.h>
+ #include <linux/regulator/consumer.h>
++#include <linux/sched.h>
+ #include <linux/dma-mapping.h>
+
+ #include <drm/drm_atomic.h>
+@@ -760,12 +761,13 @@ static int rockchip_ebc_refresh_thread(void *data)
+ rockchip_ebc_refresh(ebc, ctx, true, DRM_EPD_WF_RESET);
+ }
+
+- while (!kthread_should_park()) {
++ while ((!kthread_should_park()) && (!kthread_should_stop())) {
+ rockchip_ebc_refresh(ebc, ctx, false, default_waveform);
+
+ set_current_state(TASK_IDLE);
+- if (list_empty(&ctx->queue))
++ if (list_empty(&ctx->queue) && (!kthread_should_stop()) && (!kthread_should_park())){
+ schedule();
++ }
+ __set_current_state(TASK_RUNNING);
+ }
+
+@@ -775,8 +777,9 @@ static int rockchip_ebc_refresh_thread(void *data)
+ */
+ memset(ctx->next, 0xff, ctx->gray4_size);
+ rockchip_ebc_refresh(ebc, ctx, true, DRM_EPD_WF_GC16);
+-
+- kthread_parkme();
++ if (!kthread_should_stop()){
++ kthread_parkme();
++ }
+ }
+
+ return 0;
+@@ -925,7 +928,7 @@ static void rockchip_ebc_crtc_atomic_enable(struct drm_crtc *crtc,
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ if (crtc_state->mode_changed)
+- kthread_unpark(ebc->refresh_thread);
++ kthread_unpark(ebc->refresh_thread);
+ }
+
+ static void rockchip_ebc_crtc_atomic_disable(struct drm_crtc *crtc,
+@@ -935,8 +938,11 @@ static void rockchip_ebc_crtc_atomic_disable(struct drm_crtc *crtc,
+ struct drm_crtc_state *crtc_state;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+- if (crtc_state->mode_changed)
+- kthread_park(ebc->refresh_thread);
++ if (crtc_state->mode_changed){
++ if (! ((ebc->refresh_thread->__state) & (TASK_DEAD))){
++ kthread_park(ebc->refresh_thread);
++ }
++ }
+ }
+
+ static const struct drm_crtc_helper_funcs rockchip_ebc_crtc_helper_funcs = {
+@@ -1573,9 +1579,8 @@ static int rockchip_ebc_remove(struct platform_device *pdev)
+ struct device *dev = &pdev->dev;
+
+ drm_dev_unregister(&ebc->drm);
+- drm_atomic_helper_shutdown(&ebc->drm);
+-
+ kthread_stop(ebc->refresh_thread);
++ drm_atomic_helper_shutdown(&ebc->drm);
+
+ pm_runtime_disable(dev);
+ if (!pm_runtime_status_suspended(dev))
+@@ -1589,6 +1594,7 @@ static void rockchip_ebc_shutdown(struct platform_device *pdev)
+ struct rockchip_ebc *ebc = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
++ kthread_stop(ebc->refresh_thread);
+ drm_atomic_helper_shutdown(&ebc->drm);
+
+ if (!pm_runtime_status_suspended(dev))
+--
+2.30.2
+
+
+From 74e9d814c298f064a07ebc77b1e7ec447cc340f6 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Mon, 30 May 2022 22:20:41 +0200
+Subject: [PATCH 04/37] [rockchip_ebc] use dma_sync_single_for_cpu before
+ writing to dma buffers
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 4df73794281b..d8af43fe9f42 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -506,6 +506,9 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ bool sync_next = false;
+ bool sync_prev = false;
+
++ // now the CPU is allowed to change the phase buffer
++ dma_sync_single_for_cpu(dev, phase_handle, phase_size, DMA_TO_DEVICE);
++
+ /* Move the queued damage areas to the local list. */
+ spin_lock(&ctx->queue_lock);
+ list_splice_tail_init(&ctx->queue, &areas);
+@@ -533,6 +536,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+
+ /* Copy ctx->final to ctx->next on the first frame. */
+ if (frame_delta == 0) {
++ dma_sync_single_for_cpu(dev, next_handle, gray4_size, DMA_TO_DEVICE);
+ rockchip_ebc_blit_pixels(ctx, ctx->next,
+ ctx->final,
+ &area->clip);
+@@ -568,6 +572,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ * also ensures both phase buffers get set to 0xff.
+ */
+ if (frame_delta > last_phase) {
++ dma_sync_single_for_cpu(dev, prev_handle, gray4_size, DMA_TO_DEVICE);
+ rockchip_ebc_blit_pixels(ctx, ctx->prev,
+ ctx->next,
+ &area->clip);
+--
+2.30.2
+
+
+From 39686d27f0193a625b6f569b8de88e1b85e92480 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Mon, 30 May 2022 22:39:00 +0200
+Subject: [PATCH 05/37] rockchip_ebc fix previous commit
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index d8af43fe9f42..6a0f125040df 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -507,7 +507,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ bool sync_prev = false;
+
+ // now the CPU is allowed to change the phase buffer
+- dma_sync_single_for_cpu(dev, phase_handle, phase_size, DMA_TO_DEVICE);
++ dma_sync_single_for_cpu(dev, phase_handle, ctx->phase_size, DMA_TO_DEVICE);
+
+ /* Move the queued damage areas to the local list. */
+ spin_lock(&ctx->queue_lock);
+--
+2.30.2
+
+
+From a347a0909bb7bde73ba53b9ebae044f7fd17466f Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 3 Jun 2022 21:13:28 +0200
+Subject: [PATCH 06/37] [rockchip_ebc] convert all remaining uses of
+ virt_to_phys to the dma api
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 37 ++++++++++++++-----------
+ 1 file changed, 21 insertions(+), 16 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 6a0f125040df..87deb8098d2d 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -308,15 +308,17 @@ to_ebc_crtc_state(struct drm_crtc_state *crtc_state)
+ }
+
+ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+- const struct rockchip_ebc_ctx *ctx)
++ struct rockchip_ebc_ctx *ctx,
++ dma_addr_t next_handle,
++ dma_addr_t prev_handle
++ )
+ {
+ struct drm_device *drm = &ebc->drm;
+ u32 gray4_size = ctx->gray4_size;
+ struct device *dev = drm->dev;
+
+- dma_sync_single_for_device(dev, virt_to_phys(ctx->next),
+ gray4_size, DMA_TO_DEVICE);
+- dma_sync_single_for_device(dev, virt_to_phys(ctx->prev),
++ dma_sync_single_for_device(dev, prev_handle,
+ gray4_size, DMA_TO_DEVICE);
+
+ reinit_completion(&ebc->display_end);
+@@ -479,10 +481,11 @@ static void rockchip_ebc_blit_pixels(const struct rockchip_ebc_ctx *ctx,
+ }
+
+ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+- struct rockchip_ebc_ctx *ctx)
++ struct rockchip_ebc_ctx *ctx,
++ dma_addr_t next_handle,
++ dma_addr_t prev_handle
++ )
+ {
+- // dma_addr_t next_handle = virt_to_phys(ctx->next);
+- // dma_addr_t prev_handle = virt_to_phys(ctx->prev);
+ struct rockchip_ebc_area *area, *next_area;
+ u32 last_phase = ebc->lut.num_phases - 1;
+ struct drm_device *drm = &ebc->drm;
+@@ -491,9 +494,6 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ LIST_HEAD(areas);
+ u32 frame;
+
+- dma_addr_t next_handle = dma_map_single(dev, ctx->next, ctx->gray4_size, DMA_TO_DEVICE);
+- dma_addr_t prev_handle = dma_map_single(dev, ctx->prev, ctx->gray4_size, DMA_TO_DEVICE);
+-
+ dma_addr_t phase_handles[2];
+ phase_handles[0] = dma_map_single(dev, ctx->phase[0], ctx->gray4_size, DMA_TO_DEVICE);
+ phase_handles[1] = dma_map_single(dev, ctx->phase[1], ctx->gray4_size, DMA_TO_DEVICE);
+@@ -501,7 +501,6 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ for (frame = 0;; frame++) {
+ /* Swap phase buffers to minimize latency between frames. */
+ u8 *phase_buffer = ctx->phase[frame % 2];
+- // dma_addr_t phase_handle = virt_to_phys(phase_buffer);
+ dma_addr_t phase_handle = phase_handles[frame % 2];
+ bool sync_next = false;
+ bool sync_prev = false;
+@@ -618,8 +617,6 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ drm_err(drm, "Frame %d timed out!\n", frame);
+ }
+ }
+- dma_unmap_single(dev, next_handle, ctx->gray4_size, DMA_TO_DEVICE);
+- dma_unmap_single(dev, prev_handle, ctx->gray4_size, DMA_TO_DEVICE);
+ dma_unmap_single(dev, phase_handles[0], ctx->gray4_size, DMA_TO_DEVICE);
+ dma_unmap_single(dev, phase_handles[1], ctx->gray4_size, DMA_TO_DEVICE);
+ }
+@@ -633,6 +630,8 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+ u32 dsp_ctrl = 0, epd_ctrl = 0;
+ struct device *dev = drm->dev;
+ int ret, temperature;
++ dma_addr_t next_handle;
++ dma_addr_t prev_handle;
+
+ /* Resume asynchronously while preparing to refresh. */
+ ret = pm_runtime_get(dev);
+@@ -700,15 +699,21 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+ EBC_DSP_CTRL_DSP_LUT_MODE,
+ dsp_ctrl);
+
++ next_handle = dma_map_single(dev, ctx->next, ctx->gray4_size, DMA_TO_DEVICE);
++ prev_handle = dma_map_single(dev, ctx->prev, ctx->gray4_size, DMA_TO_DEVICE);
++
+ regmap_write(ebc->regmap, EBC_WIN_MST0,
+- virt_to_phys(ctx->next));
++ next_handle);
+ regmap_write(ebc->regmap, EBC_WIN_MST1,
+- virt_to_phys(ctx->prev));
++ prev_handle);
+
+ if (global_refresh)
+- rockchip_ebc_global_refresh(ebc, ctx);
++ rockchip_ebc_global_refresh(ebc, ctx, next_handle, prev_handle);
+ else
+- rockchip_ebc_partial_refresh(ebc, ctx);
++ rockchip_ebc_partial_refresh(ebc, ctx, next_handle, prev_handle);
++
++ dma_unmap_single(dev, next_handle, ctx->gray4_size, DMA_TO_DEVICE);
++ dma_unmap_single(dev, prev_handle, ctx->gray4_size, DMA_TO_DEVICE);
+
+ /* Drive the output pins low once the refresh is complete. */
+ regmap_write(ebc->regmap, EBC_DSP_START,
+--
+2.30.2
+
+
+From 28a024ea077105a567f8151f182f9e29c19027e5 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 3 Jun 2022 21:16:37 +0200
+Subject: [PATCH 07/37] [rockchip_ebc] add missing dma sinc call
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 87deb8098d2d..0681504fc8d7 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -317,6 +317,7 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ u32 gray4_size = ctx->gray4_size;
+ struct device *dev = drm->dev;
+
++ dma_sync_single_for_device(dev, next_handle,
+ gray4_size, DMA_TO_DEVICE);
+ dma_sync_single_for_device(dev, prev_handle,
+ gray4_size, DMA_TO_DEVICE);
+--
+2.30.2
+
+
+From 7e9e19d5342f5b9bf79d0dcddee2108d1991b7bf Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 3 Jun 2022 21:19:14 +0200
+Subject: [PATCH 08/37] [rockchip_ebc] global refresh should use ctx->final
+ instead of ctx->next to get the current image. Also, delete all pending area
+ updates when doing a global refresh.
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 19 ++++++++++++++++++-
+ 1 file changed, 18 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 0681504fc8d7..470638f59d43 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -317,6 +317,15 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ u32 gray4_size = ctx->gray4_size;
+ struct device *dev = drm->dev;
+
++ struct rockchip_ebc_area *area, *next_area;
++ LIST_HEAD(areas);
++
++ spin_lock(&ctx->queue_lock);
++ list_splice_tail_init(&ctx->queue, &areas);
++ spin_unlock(&ctx->queue_lock);
++
++ memcpy(ctx->next, ctx->final, gray4_size);
++
+ dma_sync_single_for_device(dev, next_handle,
+ gray4_size, DMA_TO_DEVICE);
+ dma_sync_single_for_device(dev, prev_handle,
+@@ -329,6 +338,12 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ ebc->dsp_start |
+ EBC_DSP_START_DSP_FRM_TOTAL(ebc->lut.num_phases - 1) |
+ EBC_DSP_START_DSP_FRM_START);
++ // while we wait for the refresh, delete all scheduled areas
++ list_for_each_entry_safe(area, next_area, &areas, list) {
++ list_del(&area->list);
++ kfree(area);
++ }
++
+ if (!wait_for_completion_timeout(&ebc->display_end,
+ EBC_REFRESH_TIMEOUT))
+ drm_err(drm, "Refresh timed out!\n");
+@@ -756,6 +771,7 @@ static int rockchip_ebc_refresh_thread(void *data)
+ */
+ memset(ctx->prev, 0xff, ctx->gray4_size);
+ memset(ctx->next, 0xff, ctx->gray4_size);
++ memset(ctx->final, 0xff, ctx->gray4_size);
+ /* NOTE: In direct mode, the phase buffers are repurposed for
+ * source driver polarity data, where the no-op value is 0. */
+ memset(ctx->phase[0], direct_mode ? 0 : 0xff, ctx->phase_size);
+@@ -786,7 +802,8 @@ static int rockchip_ebc_refresh_thread(void *data)
+ * Clear the display before disabling the CRTC. Use the
+ * highest-quality waveform to minimize visible artifacts.
+ */
+- memset(ctx->next, 0xff, ctx->gray4_size);
++ // memset(ctx->next, 0xff, ctx->gray4_size);
++ memcpy(ctx->final, ebc->off_screen, ctx->gray4_size);
+ rockchip_ebc_refresh(ebc, ctx, true, DRM_EPD_WF_GC16);
+ if (!kthread_should_stop()){
+ kthread_parkme();
+--
+2.30.2
+
+
+From 53bf42cca1aaabf10e03a8c2e455bea16b2ac539 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 3 Jun 2022 21:27:38 +0200
+Subject: [PATCH 09/37] Revert "[rockchip_ebc] global refresh should use
+ ctx->final instead of ctx->next"
+
+This reverts commit 599a3057df02ab9188d3d6c9db5b5d6846a445c9.
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 19 +------------------
+ 1 file changed, 1 insertion(+), 18 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 470638f59d43..0681504fc8d7 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -317,15 +317,6 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ u32 gray4_size = ctx->gray4_size;
+ struct device *dev = drm->dev;
+
+- struct rockchip_ebc_area *area, *next_area;
+- LIST_HEAD(areas);
+-
+- spin_lock(&ctx->queue_lock);
+- list_splice_tail_init(&ctx->queue, &areas);
+- spin_unlock(&ctx->queue_lock);
+-
+- memcpy(ctx->next, ctx->final, gray4_size);
+-
+ dma_sync_single_for_device(dev, next_handle,
+ gray4_size, DMA_TO_DEVICE);
+ dma_sync_single_for_device(dev, prev_handle,
+@@ -338,12 +329,6 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ ebc->dsp_start |
+ EBC_DSP_START_DSP_FRM_TOTAL(ebc->lut.num_phases - 1) |
+ EBC_DSP_START_DSP_FRM_START);
+- // while we wait for the refresh, delete all scheduled areas
+- list_for_each_entry_safe(area, next_area, &areas, list) {
+- list_del(&area->list);
+- kfree(area);
+- }
+-
+ if (!wait_for_completion_timeout(&ebc->display_end,
+ EBC_REFRESH_TIMEOUT))
+ drm_err(drm, "Refresh timed out!\n");
+@@ -771,7 +756,6 @@ static int rockchip_ebc_refresh_thread(void *data)
+ */
+ memset(ctx->prev, 0xff, ctx->gray4_size);
+ memset(ctx->next, 0xff, ctx->gray4_size);
+- memset(ctx->final, 0xff, ctx->gray4_size);
+ /* NOTE: In direct mode, the phase buffers are repurposed for
+ * source driver polarity data, where the no-op value is 0. */
+ memset(ctx->phase[0], direct_mode ? 0 : 0xff, ctx->phase_size);
+@@ -802,8 +786,7 @@ static int rockchip_ebc_refresh_thread(void *data)
+ * Clear the display before disabling the CRTC. Use the
+ * highest-quality waveform to minimize visible artifacts.
+ */
+- // memset(ctx->next, 0xff, ctx->gray4_size);
+- memcpy(ctx->final, ebc->off_screen, ctx->gray4_size);
++ memset(ctx->next, 0xff, ctx->gray4_size);
+ rockchip_ebc_refresh(ebc, ctx, true, DRM_EPD_WF_GC16);
+ if (!kthread_should_stop()){
+ kthread_parkme();
+--
+2.30.2
+
+
+From c4babc5ae528d3c8c260fe6584f0d1812dda65ef Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 4 Jun 2022 19:39:48 +0200
+Subject: [PATCH 10/37] [rockchip_ebc] global refresh should use ctx->final
+ instead of ctx->next to get the current image. Also, delete all pending
+ area updates when doing a global refresh.
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 0681504fc8d7..41852c23802e 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -317,6 +317,15 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ u32 gray4_size = ctx->gray4_size;
+ struct device *dev = drm->dev;
+
++ struct rockchip_ebc_area *area, *next_area;
++ LIST_HEAD(areas);
++
++ spin_lock(&ctx->queue_lock);
++ list_splice_tail_init(&ctx->queue, &areas);
++ spin_unlock(&ctx->queue_lock);
++
++ memcpy(ctx->next, ctx->final, gray4_size);
++
+ dma_sync_single_for_device(dev, next_handle,
+ gray4_size, DMA_TO_DEVICE);
+ dma_sync_single_for_device(dev, prev_handle,
+@@ -329,6 +338,12 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ ebc->dsp_start |
+ EBC_DSP_START_DSP_FRM_TOTAL(ebc->lut.num_phases - 1) |
+ EBC_DSP_START_DSP_FRM_START);
++ // while we wait for the refresh, delete all scheduled areas
++ list_for_each_entry_safe(area, next_area, &areas, list) {
++ list_del(&area->list);
++ kfree(area);
++ }
++
+ if (!wait_for_completion_timeout(&ebc->display_end,
+ EBC_REFRESH_TIMEOUT))
+ drm_err(drm, "Refresh timed out!\n");
+@@ -756,6 +771,8 @@ static int rockchip_ebc_refresh_thread(void *data)
+ */
+ memset(ctx->prev, 0xff, ctx->gray4_size);
+ memset(ctx->next, 0xff, ctx->gray4_size);
++ memset(ctx->final, 0xff, ctx->gray4_size);
++
+ /* NOTE: In direct mode, the phase buffers are repurposed for
+ * source driver polarity data, where the no-op value is 0. */
+ memset(ctx->phase[0], direct_mode ? 0 : 0xff, ctx->phase_size);
+--
+2.30.2
+
+
+From bb0e94904c9188675bfb6b3e264cc409c558ea72 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 4 Jun 2022 19:44:00 +0200
+Subject: [PATCH 11/37] [rockchip_ebc] add the possibility to trigger one
+ global refresh using a module-global variable do_one_full_refresh
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 20 +++++++++++++++++++-
+ 1 file changed, 19 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 41852c23802e..b1c8f967350b 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -154,6 +154,9 @@ struct rockchip_ebc {
+ u32 dsp_start;
+ bool lut_changed;
+ bool reset_complete;
++ spinlock_t refresh_once_lock;
++ // should this go into the ctx?
++ bool do_one_full_refresh;
+ };
+
+ static int default_waveform = DRM_EPD_WF_GC16;
+@@ -744,6 +747,7 @@ static int rockchip_ebc_refresh_thread(void *data)
+ {
+ struct rockchip_ebc *ebc = data;
+ struct rockchip_ebc_ctx *ctx;
++ bool one_full_refresh;
+
+ while (!kthread_should_stop()) {
+ /* The context will change each time the thread is unparked. */
+@@ -790,7 +794,18 @@ static int rockchip_ebc_refresh_thread(void *data)
+ }
+
+ while ((!kthread_should_park()) && (!kthread_should_stop())) {
+- rockchip_ebc_refresh(ebc, ctx, false, default_waveform);
++ spin_lock(&ebc->refresh_once_lock);
++ one_full_refresh = ebc->do_one_full_refresh;
++ spin_unlock(&ebc->refresh_once_lock);
++
++ if (one_full_refresh) {
++ spin_lock(&ebc->refresh_once_lock);
++ ebc->do_one_full_refresh = false;
++ spin_unlock(&ebc->refresh_once_lock);
++ rockchip_ebc_refresh(ebc, ctx, true, default_waveform);
++ } else {
++ rockchip_ebc_refresh(ebc, ctx, false, default_waveform);
++ }
+
+ set_current_state(TASK_IDLE);
+ if (list_empty(&ctx->queue) && (!kthread_should_stop()) && (!kthread_should_park())){
+@@ -1519,6 +1534,9 @@ static int rockchip_ebc_probe(struct platform_device *pdev)
+
+ ebc = devm_drm_dev_alloc(dev, &rockchip_ebc_drm_driver,
+ struct rockchip_ebc, drm);
++
++ spin_lock_init(&ebc->refresh_once_lock);
++
+ if (IS_ERR(ebc))
+ return PTR_ERR(ebc);
+
+--
+2.30.2
+
+
+From 2b62b6c5853200cf1f1f63010d8edb56a8a08ceb Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 4 Jun 2022 19:46:46 +0200
+Subject: [PATCH 12/37] [rockchip_ebc] add possibility to change the
+ off-screen, i.e. the content of the screen when the module is unloaded. The
+ content is read on module-load time from the firmware file
+ rockchip/rockchip_ebc_default_screen.bin. The file must be of size 1314144
+ bytes containing the 4 bit gray values for each pixel
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 25 ++++++++++++++++++++++++-
+ 1 file changed, 24 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index b1c8f967350b..edf98b048a07 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -15,6 +15,7 @@
+ #include <linux/regulator/consumer.h>
+ #include <linux/sched.h>
+ #include <linux/dma-mapping.h>
++#include <linux/firmware.h>
+
+ #include <drm/drm_atomic.h>
+ #include <drm/drm_atomic_helper.h>
+@@ -154,6 +155,9 @@ struct rockchip_ebc {
+ u32 dsp_start;
+ bool lut_changed;
+ bool reset_complete;
++ // one screen content: 1872 * 1404 / 2
++ // the array size should probably be set dynamically...
++ char off_screen[1314144];
+ spinlock_t refresh_once_lock;
+ // should this go into the ctx?
+ bool do_one_full_refresh;
+@@ -818,7 +822,7 @@ static int rockchip_ebc_refresh_thread(void *data)
+ * Clear the display before disabling the CRTC. Use the
+ * highest-quality waveform to minimize visible artifacts.
+ */
+- memset(ctx->next, 0xff, ctx->gray4_size);
++ memcpy(ctx->final, ebc->off_screen, ctx->gray4_size);
+ rockchip_ebc_refresh(ebc, ctx, true, DRM_EPD_WF_GC16);
+ if (!kthread_should_stop()){
+ kthread_parkme();
+@@ -1334,6 +1338,7 @@ static int rockchip_ebc_drm_init(struct rockchip_ebc *ebc)
+ struct drm_device *drm = &ebc->drm;
+ struct drm_bridge *bridge;
+ int ret;
++ const struct firmware * default_offscreen;
+
+ ret = drmm_epd_lut_file_init(drm, &ebc->lut_file, "rockchip/ebc.wbf");
+ if (ret)
+@@ -1392,6 +1397,24 @@ static int rockchip_ebc_drm_init(struct rockchip_ebc *ebc)
+
+ drm_fbdev_generic_setup(drm, 0);
+
++ // check if there is a default off-screen
++ if (!request_firmware(&default_offscreen, "rockchip/rockchip_ebc_default_screen.bin", drm->dev))
++ {
++ printk(KERN_INFO "rockchip_ebc: default off-screen file found\n");
++ if (default_offscreen->size != 1314144)
++ drm_err(drm, "Size of default offscreen data file is not 1314144\n");
++ else {
++ printk(KERN_INFO "rockchip_ebc: loading default off-screen\n");
++ memcpy(ebc->off_screen, default_offscreen->data, 1314144);
++ }
++ } else {
++ printk(KERN_INFO "rockchip_ebc: no default off-screen file found\n");
++ // fill the off-screen with some values
++ memset(ebc->off_screen, 0xff, 1314144);
++ /* memset(ebc->off_screen, 0x00, 556144); */
++ }
++ release_firmware(default_offscreen);
++
+ return 0;
+ }
+
+--
+2.30.2
+
+
+From f7fb21e16439c8e271786a20543c7ed74e892750 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 4 Jun 2022 19:49:14 +0200
+Subject: [PATCH 13/37] [rockchip_ebc] implement a simple auto_refresh scheme
+ which triggers a global refresh after a certain area has been drawn using the
+ partial refresh path. The threshold of drawn area after which the refresh is
+ triggered can be modified using the sysfs file
+ /sys/module/rockchip_ebc/parameters/refresh_threshold. A default value of 20
+ (screen areas) seems good enough to get a refresh after 5 pages of ebook
+ reading. This seems to imply that quite a lot of duplicate draws are made for
+ each page turn (not investigated further). The auto-refresh feature is
+ deactivated by default and can be activated using the module parameter
+ auto_refresh or by writing 1 to
+ /sys/module/rockchip_ebc/parameters/auto_refresh
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 33 +++++++++++++++++++++++++
+ 1 file changed, 33 insertions(+)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index edf98b048a07..69ef34e86ba7 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -183,6 +183,14 @@ static bool skip_reset = false;
+ module_param(skip_reset, bool, 0444);
+ MODULE_PARM_DESC(skip_reset, "skip the initial display reset");
+
++static bool auto_refresh = false;
++module_param(auto_refresh, bool, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(auto_refresh, "auto refresh the screen based on partial refreshed area");
++
++static int refresh_threshold = 20;
++module_param(refresh_threshold, int, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(refresh_threshold, "refresh threshold in screen area multiples");
++
+ DEFINE_DRM_GEM_FOPS(rockchip_ebc_fops);
+
+ static const struct drm_driver rockchip_ebc_drm_driver = {
+@@ -243,6 +251,7 @@ struct rockchip_ebc_ctx {
+ u32 gray4_size;
+ u32 phase_pitch;
+ u32 phase_size;
++ u64 area_count;
+ };
+
+ static void rockchip_ebc_ctx_free(struct rockchip_ebc_ctx *ctx)
+@@ -288,6 +297,10 @@ static struct rockchip_ebc_ctx *rockchip_ebc_ctx_alloc(u32 width, u32 height)
+ ctx->phase_pitch = width;
+ ctx->phase_size = phase_size;
+
++ // we keep track of the updated area and use this value to trigger global
++ // refreshes if auto_refresh is enabled
++ ctx->area_count = 0;
++
+ return ctx;
+ }
+
+@@ -516,6 +529,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ struct device *dev = drm->dev;
+ LIST_HEAD(areas);
+ u32 frame;
++ u64 local_area_count = 0;
+
+ dma_addr_t phase_handles[2];
+ phase_handles[0] = dma_map_single(dev, ctx->phase[0], ctx->gray4_size, DMA_TO_DEVICE);
+@@ -558,6 +572,9 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+
+ /* Copy ctx->final to ctx->next on the first frame. */
+ if (frame_delta == 0) {
++ local_area_count += (u64) (
++ area->clip.x2 - area->clip.x1) *
++ (area->clip.y2 - area->clip.y1);
+ dma_sync_single_for_cpu(dev, next_handle, gray4_size, DMA_TO_DEVICE);
+ rockchip_ebc_blit_pixels(ctx, ctx->next,
+ ctx->final,
+@@ -642,6 +659,8 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ }
+ dma_unmap_single(dev, phase_handles[0], ctx->gray4_size, DMA_TO_DEVICE);
+ dma_unmap_single(dev, phase_handles[1], ctx->gray4_size, DMA_TO_DEVICE);
++ /* printk(KERN_INFO "loca area count: %llu\n", local_area_count); */
++ ctx->area_count += local_area_count;
+ }
+
+ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+@@ -655,6 +674,7 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+ int ret, temperature;
+ dma_addr_t next_handle;
+ dma_addr_t prev_handle;
++ int one_screen_area = 1314144;
+
+ /* Resume asynchronously while preparing to refresh. */
+ ret = pm_runtime_get(dev);
+@@ -738,6 +758,19 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+ dma_unmap_single(dev, next_handle, ctx->gray4_size, DMA_TO_DEVICE);
+ dma_unmap_single(dev, prev_handle, ctx->gray4_size, DMA_TO_DEVICE);
+
++ // do we need a full refresh
++ if (auto_refresh){
++ if (ctx->area_count >= refresh_threshold * one_screen_area){
++ printk(KERN_INFO "rockchip: triggering full refresh due to drawn area threshold\n");
++ spin_lock(&ebc->refresh_once_lock);
++ ebc->do_one_full_refresh = true;
++ spin_unlock(&ebc->refresh_once_lock);
++ ctx->area_count = 0;
++ }
++ } else {
++ ctx->area_count = 0;
++ }
++
+ /* Drive the output pins low once the refresh is complete. */
+ regmap_write(ebc->regmap, EBC_DSP_START,
+ ebc->dsp_start |
+--
+2.30.2
+
+
+From eef2a823bf96f492a4d28fe0f90ea91a3c1bb936 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 4 Jun 2022 20:02:26 +0200
+Subject: [PATCH 14/37] [rockchip_ebc] Add two ioctls to the rockchip_ebc
+ module:
+
+DRM_IOCTL_ROCKCHIP_EBC_GLOBAL_REFRESH triggers a global fresh
+
+DRM_IOCTL_ROCKCHIP_EBC_OFF_SCREEN can be used to supply off-screen
+content that is display on shutdown/module-unload.
+
+Corresponding ioctl structures:
+
+struct drm_rockchip_ebc_trigger_global_refresh {
+ bool trigger_global_refresh;
+};
+
+struct drm_rockchip_ebc_off_screen {
+ __u64 info1; // <- not used
+ char * ptr_screen_content;
+};
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 41 +++++++++++++++++++++++++
+ include/uapi/drm/rockchip_ebc_drm.h | 25 +++++++++++++++
+ 2 files changed, 66 insertions(+)
+ create mode 100644 include/uapi/drm/rockchip_ebc_drm.h
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 69ef34e86ba7..9a0a238829bb 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -15,6 +15,7 @@
+ #include <linux/regulator/consumer.h>
+ #include <linux/sched.h>
+ #include <linux/dma-mapping.h>
++#include <linux/uaccess.h>
+ #include <linux/firmware.h>
+
+ #include <drm/drm_atomic.h>
+@@ -29,6 +30,7 @@
+ #include <drm/drm_gem_shmem_helper.h>
+ #include <drm/drm_plane_helper.h>
+ #include <drm/drm_simple_kms_helper.h>
++#include <drm/rockchip_ebc_drm.h>
+
+ #define EBC_DSP_START 0x0000
+ #define EBC_DSP_START_DSP_OUT_LOW BIT(31)
+@@ -193,6 +195,43 @@ MODULE_PARM_DESC(refresh_threshold, "refresh threshold in screen area multiples"
+
+ DEFINE_DRM_GEM_FOPS(rockchip_ebc_fops);
+
++static int ioctl_trigger_global_refresh(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_rockchip_ebc_trigger_global_refresh *args = data;
++ struct rockchip_ebc *ebc = dev_get_drvdata(dev->dev);
++
++ if (args->trigger_global_refresh){
++ printk(KERN_INFO "rockchip_ebc: ioctl would trigger full refresh \n");
++ spin_lock(&ebc->refresh_once_lock);
++ ebc->do_one_full_refresh = true;
++ spin_unlock(&ebc->refresh_once_lock);
++ // try to trigger the refresh immediately
++ wake_up_process(ebc->refresh_thread);
++ }
++
++ return 0;
++}
++
++static int ioctl_set_off_screen(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_rockchip_ebc_off_screen *args = data;
++ struct rockchip_ebc *ebc = dev_get_drvdata(dev->dev);
++ int copy_result;
++
++ copy_result = copy_from_user(&ebc->off_screen, args->ptr_screen_content, 1313144);
++
++ return 0;
++}
++
++static const struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {
++ DRM_IOCTL_DEF_DRV(ROCKCHIP_EBC_GLOBAL_REFRESH, ioctl_trigger_global_refresh,
++ DRM_RENDER_ALLOW),
++ DRM_IOCTL_DEF_DRV(ROCKCHIP_EBC_OFF_SCREEN, ioctl_set_off_screen,
++ DRM_RENDER_ALLOW),
++};
++
+ static const struct drm_driver rockchip_ebc_drm_driver = {
+ .lastclose = drm_fb_helper_lastclose,
+ DRM_GEM_SHMEM_DRIVER_OPS,
+@@ -203,6 +242,8 @@ static const struct drm_driver rockchip_ebc_drm_driver = {
+ .date = "20220303",
+ .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
+ .fops = &rockchip_ebc_fops,
++ .ioctls = ioctls,
++ .num_ioctls = DRM_ROCKCHIP_EBC_NUM_IOCTLS,
+ };
+
+ static const struct drm_mode_config_funcs rockchip_ebc_mode_config_funcs = {
+diff --git a/include/uapi/drm/rockchip_ebc_drm.h b/include/uapi/drm/rockchip_ebc_drm.h
+new file mode 100644
+index 000000000000..befa62a68be0
+--- /dev/null
++++ b/include/uapi/drm/rockchip_ebc_drm.h
+@@ -0,0 +1,25 @@
++#ifndef __ROCKCHIP_EBC_DRM_H__
++#define __ROCKCHIP_EBC_DRM_H__
++
++#include "drm.h"
++
++#if defined(__cplusplus)
++extern "C" {
++#endif
++
++
++struct drm_rockchip_ebc_trigger_global_refresh {
++ bool trigger_global_refresh;
++};
++
++struct drm_rockchip_ebc_off_screen {
++ __u64 info1;
++ char * ptr_screen_content;
++};
++
++#define DRM_ROCKCHIP_EBC_NUM_IOCTLS 0x02
++
++#define DRM_IOCTL_ROCKCHIP_EBC_GLOBAL_REFRESH DRM_IOWR(DRM_COMMAND_BASE + 0x00, struct drm_rockchip_ebc_trigger_global_refresh)
++#define DRM_IOCTL_ROCKCHIP_EBC_OFF_SCREEN DRM_IOWR(DRM_COMMAND_BASE + 0x01, struct drm_rockchip_ebc_off_screen)
++
++#endif /* __ROCKCHIP_EBC_DRM_H__*/
+--
+2.30.2
+
+
+From 2855fb8cf5824b9d0d62d194440a4d7aad360c28 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Thu, 9 Jun 2022 09:56:13 +0200
+Subject: [PATCH 15/37] [rockchip_ebc] try to split overlapping areas into four
+ subareas during refresh so that the non-overlapping parts can start to
+ refresh as soon as possible and we only need to wait for the overlapping
+ part.
+
+The number of areas to split while preparing each frame can be limited.
+I'm not sure if this is really required, but I fear that too many splits
+could slow down the refresh thread.
+
+Splitting areas can produce areas that do not align with full bytes (4
+bit/byte), so we also try to account for odd start/end clips.
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 176 +++++++++++++++++++++++-
+ 1 file changed, 172 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 9a0a238829bb..6f7bbe0bd70f 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -415,10 +415,15 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ struct rockchip_ebc_area *area,
+ struct drm_device *drm,
+- u32 current_frame, u32 num_phases)
++ u32 current_frame, u32 num_phases,
++ struct rockchip_ebc_area *next_area,
++ int * split_counter
++ )
+ {
+ struct rockchip_ebc_area *other;
++ // by default, begin now
+ u32 frame_begin = current_frame;
++ /* printk(KERN_INFO "scheduling area: %i-%i %i-%i\n", area->clip.x1, area->clip.x2, area->clip.y1, area->clip.y2); */
+
+ list_for_each_entry(other, areas, list) {
+ struct drm_rect intersection;
+@@ -437,11 +442,124 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ intersection = area->clip;
+ if (!drm_rect_intersect(&intersection, &other->clip))
+ continue;
++ // we got here, so there is a collision
+
+ /* If the other area already started, wait until it finishes. */
+ if (other->frame_begin < current_frame) {
+ frame_begin = other_end;
+- continue;
++
++ // so here we would optimally want to split the new area into three
++ // parts that do not overlap with the already-started area, and one
++ // which is overlapping. The overlapping one will be scheduled for
++ // later, but the other three should start immediately.
++
++ // if the area is equal to the clip, continue
++ if (drm_rect_equals(&area->clip, &intersection))
++ continue;
++
++ // for now, min size if 2x2
++ if ((area->clip.x2 - area->clip.x1 < 2) | (area->clip.y2 - area->clip.y1 < 2))
++ continue;
++
++ // ok, we want to split this area and start with any partial areas
++ // that are not overlapping (well, let this be decided upon at the
++ // next outer loop - we delete this area so we need not to juggle
++ // around the four areas until we found the one that is actually
++ // overlapping)
++ int xmin, xmax, ymin, ymax, xcenter, ycenter;
++ xmin = area->clip.x1;
++ if (intersection.x1 > xmin)
++ xcenter = intersection.x1;
++ else
++ xcenter = intersection.x2;
++ xmax = area->clip.x2;
++
++ ymin = area->clip.y1;
++ if (intersection.y1 > ymin)
++ ycenter = intersection.y1;
++ else
++ ycenter = intersection.y2;
++ ymax = area->clip.y2;
++
++ if ((xmin == xcenter) | (xcenter == xmax))
++ continue;
++ if ((ymin == ycenter) | (ycenter == ymax))
++ continue;
++
++ // we do not want to overhelm the refresh thread and limit us to a
++ // certain number of splits. The rest needs to wait
++ if (*split_counter >= 6)
++ continue;
++
++ // we need four new rokchip_ebc_area entries that we splice into
++ // the list. Note that the currently next item shall be copied
++ // backwards because to prevent the outer list iteration from
++ // skipping over our newly created items.
++
++ struct rockchip_ebc_area * item1;
++ struct rockchip_ebc_area * item2;
++ struct rockchip_ebc_area * item3;
++ struct rockchip_ebc_area * item4;
++ item1 = kmalloc(sizeof(*item1), GFP_KERNEL);
++ item2 = kmalloc(sizeof(*item2), GFP_KERNEL);
++ item3 = kmalloc(sizeof(*item3), GFP_KERNEL);
++ item4 = kmalloc(sizeof(*item4), GFP_KERNEL);
++
++ // TODO: Error checking!!!!
++ /* if (!area) */
++ /* return -ENOMEM; */
++
++ if (list_is_last(&area->list, areas)){
++ /* printk(KERN_INFO "adding to end of list\n"); */
++ list_add_tail(&item1->list, areas);
++ list_add_tail(&item2->list, areas);
++ list_add_tail(&item3->list, areas);
++ list_add_tail(&item4->list, areas);
++ }
++ else{
++ /* printk(KERN_INFO "splicing into the middle of the list\n"); */
++ __list_add(&item4->list, areas, areas->next);
++ __list_add(&item3->list, areas, areas->next);
++ __list_add(&item2->list, areas, areas->next);
++ __list_add(&item1->list, areas, areas->next);
++ }
++ next_area = item1;
++
++ // now fill the areas
++ /* printk(KERN_INFO "area1: %i %i %i %i\n", xmin, xcenter, ymin, ycenter); */
++ /* printk(KERN_INFO "area2: %i %i %i %i\n", xmin, xcenter, ycenter, ymax); */
++ /* printk(KERN_INFO "area3: %i %i %i %i\n", xcenter, xmax, ymin, ycenter); */
++ /* printk(KERN_INFO "area4: %i %i %i %i\n", xcenter, xmax, ycenter, ymax); */
++
++ item1->frame_begin = EBC_FRAME_PENDING;
++ item1->clip.x1 = xmin;
++ item1->clip.x2 = xcenter;
++ item1->clip.y1 = ymin;
++ item1->clip.y2 = ycenter;
++
++ item2->frame_begin = EBC_FRAME_PENDING;
++ item2->clip.x1 = xmin;
++ item2->clip.x2 = xcenter;
++ item2->clip.y1 = ycenter + 1;
++ item2->clip.y2 = ymax;
++
++ item3->frame_begin = EBC_FRAME_PENDING;
++ item3->clip.x1 = xcenter + 1;
++ item3->clip.x2 = xmax;
++ item3->clip.y1 = ymin;
++ item3->clip.y2 = ycenter;
++
++ item4->frame_begin = EBC_FRAME_PENDING;
++ item4->clip.x1 = xcenter + 1;
++ item4->clip.x2 = xmax;
++ item4->clip.y1 = ycenter + 1;
++ item4->clip.y2 = ymax;
++
++ *split_counter++;
++
++ // let the outer loop delete this area
++ return false;
++ /* continue; */
+ }
+
+ /*
+@@ -538,8 +656,18 @@ static void rockchip_ebc_blit_pixels(const struct rockchip_ebc_ctx *ctx,
+ u8 *dst, const u8 *src,
+ const struct drm_rect *clip)
+ {
++ bool start_x_is_odd = clip->x1 & 1;
++ bool end_x_is_odd = clip->x2 & 1;
++ u8 first_odd;
++ u8 last_odd;
++
+ unsigned int x1_bytes = clip->x1 / 2;
+ unsigned int x2_bytes = clip->x2 / 2;
++ // the integer division floors by default, but we want to include the last
++ // byte (partially)
++ if (end_x_is_odd)
++ x2_bytes++;
++
+ unsigned int pitch = ctx->gray4_pitch;
+ unsigned int width = x2_bytes - x1_bytes;
+ const u8 *src_line;
+@@ -550,8 +678,29 @@ static void rockchip_ebc_blit_pixels(const struct rockchip_ebc_ctx *ctx,
+ src_line = src + clip->y1 * pitch + x1_bytes;
+
+ for (y = clip->y1; y < clip->y2; y++) {
++ if (start_x_is_odd)
++ // keep only lower bit to restore it after the blitting
++ first_odd = *src_line & 0b00001111;
++ if (end_x_is_odd){
++ dst_line += pitch - 1;
++ // keep only the upper bit for restoring later
++ last_odd = *dst_line & 0b11110000;
++ dst_line -= pitch - 1;
++ }
++
+ memcpy(dst_line, src_line, width);
+
++ if (start_x_is_odd){
++ // write back the first 4 saved bits
++ *dst_line = first_odd | (*dst_line & 0b11110000);
++ }
++ if (end_x_is_odd){
++ // write back the last 4 saved bits
++ dst_line += pitch -1;
++ *dst_line = (*dst_line & 0b00001111) | last_odd;
++ dst_line -= pitch -1;
++ }
++
+ dst_line += pitch;
+ src_line += pitch;
+ }
+@@ -582,6 +731,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ dma_addr_t phase_handle = phase_handles[frame % 2];
+ bool sync_next = false;
+ bool sync_prev = false;
++ int split_counter = 0;
+
+ // now the CPU is allowed to change the phase buffer
+ dma_sync_single_for_cpu(dev, phase_handle, ctx->phase_size, DMA_TO_DEVICE);
+@@ -601,18 +751,20 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ */
+ if (area->frame_begin == EBC_FRAME_PENDING &&
+ !rockchip_ebc_schedule_area(&areas, area, drm, frame,
+- ebc->lut.num_phases)) {
++ ebc->lut.num_phases, next_area, &split_counter)) {
+ list_del(&area->list);
+ kfree(area);
+ continue;
+ }
+
++ // we wait a little bit longer to start
+ frame_delta = frame - area->frame_begin;
+ if (frame_delta < 0)
+ continue;
+
+ /* Copy ctx->final to ctx->next on the first frame. */
+ if (frame_delta == 0) {
++ printk(KERN_INFO "rockchip partial refresh starting area on frame %i (%i/%i %i/%i)\n", frame, area->clip.x1, area->clip.x2, area->clip.y1, area->clip.y2);
+ local_area_count += (u64) (
+ area->clip.x2 - area->clip.x1) *
+ (area->clip.y2 - area->clip.y1);
+@@ -1212,9 +1364,13 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ int delta_x;
+ void *dst;
+
++ bool start_x_is_odd = src_clip->x1 & 1;
++ bool end_x_is_odd = src_clip->x2 & 1;
++
+ delta_x = panel_reflection ? -1 : 1;
+ start_x = panel_reflection ? src_clip->x2 - 1 : src_clip->x1;
+
++ // I think this also works if dst_clip->x1 is odd
+ dst = ctx->final + dst_clip->y1 * dst_pitch + dst_clip->x1 / 2;
+ src = vaddr + src_clip->y1 * src_pitch + start_x * fb->format->cpp[0];
+
+@@ -1236,7 +1392,19 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ /* Unbias the value for rounding to 4 bits. */
+ rgb0 += 0x07000000U; rgb1 += 0x07000000U;
+
+- gray = rgb0 >> 28 | rgb1 >> 28 << 4;
++ rgb0 >>= 28;
++ rgb1 >>= 28;
++
++ if (x == src_clip->x1 && start_x_is_odd) {
++ // rgb0 should be filled with the content of the src pixel here
++ rgb0 = *dbuf;
++ }
++ if (x == src_clip->x2 && end_x_is_odd) {
++ // rgb1 should be filled with the content of the src pixel here
++ rgb1 = *dbuf;
++ }
++
++ gray = rgb0 | rgb1 << 4;
+ changed |= gray ^ *dbuf;
+ *dbuf++ = gray;
+ }
+--
+2.30.2
+
+
+From 58cb814fa8389a157c30d90511be33b75066a417 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 11 Jun 2022 20:55:34 +0200
+Subject: [PATCH 16/37] [rockchip_ebc] add a sys parameter split_area_limit
+ (default: 12) that determines how many areas to maximally split in each
+ scheduling run. Set to 0 to disable area splitting.
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 6f7bbe0bd70f..ae8f6727d05c 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -193,6 +193,10 @@ static int refresh_threshold = 20;
+ module_param(refresh_threshold, int, S_IRUGO|S_IWUSR);
+ MODULE_PARM_DESC(refresh_threshold, "refresh threshold in screen area multiples");
+
++static int split_area_limit = 12;
++module_param(split_area_limit, int, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(split_area_limit, "how many areas to split in each scheduling call");
++
+ DEFINE_DRM_GEM_FOPS(rockchip_ebc_fops);
+
+ static int ioctl_trigger_global_refresh(struct drm_device *dev, void *data,
+@@ -488,7 +492,7 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+
+ // we do not want to overhelm the refresh thread and limit us to a
+ // certain number of splits. The rest needs to wait
+- if (*split_counter >= 6)
++ if (*split_counter >= split_area_limit)
+ continue;
+
+ // we need four new rokchip_ebc_area entries that we splice into
+--
+2.30.2
+
+
+From 2b91cc2d12d73e24bfbfae3fdc9a71e83885092d Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 11 Jun 2022 20:56:36 +0200
+Subject: [PATCH 17/37] [rockchip_ebc] fix ioctl printk message
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index ae8f6727d05c..4d6a799d7bb4 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -206,7 +206,7 @@ static int ioctl_trigger_global_refresh(struct drm_device *dev, void *data,
+ struct rockchip_ebc *ebc = dev_get_drvdata(dev->dev);
+
+ if (args->trigger_global_refresh){
+- printk(KERN_INFO "rockchip_ebc: ioctl would trigger full refresh \n");
++ printk(KERN_INFO "rockchip_ebc: ioctl triggered full refresh \n");
+ spin_lock(&ebc->refresh_once_lock);
+ ebc->do_one_full_refresh = true;
+ spin_unlock(&ebc->refresh_once_lock);
+--
+2.30.2
+
+
+From 314ebae7211613cce9085809115212f3dc1002a8 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 11 Jun 2022 20:57:14 +0200
+Subject: [PATCH 18/37] [rockchip_ebc] fix clips of split areas
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 4d6a799d7bb4..4eb6e1e0f261 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -544,19 +544,19 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ item2->frame_begin = EBC_FRAME_PENDING;
+ item2->clip.x1 = xmin;
+ item2->clip.x2 = xcenter;
+- item2->clip.y1 = ycenter + 1;
++ item2->clip.y1 = ycenter;
+ item2->clip.y2 = ymax;
+
+ item3->frame_begin = EBC_FRAME_PENDING;
+- item3->clip.x1 = xcenter + 1;
++ item3->clip.x1 = xcenter;
+ item3->clip.x2 = xmax;
+ item3->clip.y1 = ymin;
+ item3->clip.y2 = ycenter;
+
+ item4->frame_begin = EBC_FRAME_PENDING;
+- item4->clip.x1 = xcenter + 1;
++ item4->clip.x1 = xcenter;
+ item4->clip.x2 = xmax;
+- item4->clip.y1 = ycenter + 1;
++ item4->clip.y1 = ycenter;
+ item4->clip.y2 = ymax;
+
+ *split_counter++;
+--
+2.30.2
+
+
+From 5894a086939ec2c8e88bdbe2505052d6d4fd7da4 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 11 Jun 2022 20:57:44 +0200
+Subject: [PATCH 19/37] [rockchip_ebc] fix incrementing of splitting counter
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 4eb6e1e0f261..7e1558403973 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -559,7 +559,7 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ item4->clip.y1 = ycenter;
+ item4->clip.y2 = ymax;
+
+- *split_counter++;
++ (*split_counter)++;
+
+ // let the outer loop delete this area
+ return false;
+--
+2.30.2
+
+
+From 325b7773c89b498de357d2952ed47ba052658296 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 11 Jun 2022 20:58:17 +0200
+Subject: [PATCH 20/37] [rockchip_ebc] Fix a bug in the scheduling function
+ that could schedule an area too early: if the area overlaps with an
+ already-started area, its begin_frame will be set to the end frame of the
+ other one. However, if any frame in the list follows that can start earlier
+ (because it does not overlap or finishes at an earlier time) than this
+ earlier end frame will be used to schedule the new area.
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 7e1558403973..973d13ffd0d3 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -576,8 +576,9 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ return false;
+ }
+
+- /* Otherwise, start at the same time as the other area. */
+- frame_begin = other->frame_begin;
++ /* Otherwise, the earliest start is the same time as that of the other
++ * area. */
++ frame_begin = max(frame_begin, other->frame_begin);
+ }
+
+ area->frame_begin = frame_begin;
+--
+2.30.2
+
+
+From 350e4ec1da7cb4fe67ccb6d54b98cfead031c500 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 11 Jun 2022 21:08:19 +0200
+Subject: [PATCH 21/37] [rockchip_ebc] The current driver iteration does not
+ guarantee consistency between the list of currently-worked on damaged areas
+ (snapshot of ctx->queue taken at the beginning of each frame) and the
+ framebuffer content (ctx->final). As such it is possible that the content of
+ the framebuffer changes before a given area can be drawn, potentially leading
+ to garbled screen content. This effects is hugely dependent on the nature of
+ drawing calls emitted by individual applications. Large scheduled areas tend
+ to be good, but if an application sends large bursts of
+ overlapping/overwriting areas then bad things happen. The bug/effect is also
+ triggered if area splitting is done to increase drawing performance.
+
+For example, this can be nicely seen under Gnome when
+chaotically moving the nautilus window.
+
+This patch is not a fix but somewhat reduces the impact by moving the
+splinlock guarding the ctx->queue so it guards both the whole
+frame-prepartion phase of the partial refresh function and the
+framebuffer blitting function.
+
+An alternative that also greatly reduces the effect is to copy the whole
+framebuffer before preparing a given frame. However, this has a huge
+performance impact and thus is not feasible if we still want to to
+real-time drawings.
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 973d13ffd0d3..3ef899c4779f 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -744,7 +744,6 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ /* Move the queued damage areas to the local list. */
+ spin_lock(&ctx->queue_lock);
+ list_splice_tail_init(&ctx->queue, &areas);
+- spin_unlock(&ctx->queue_lock);
+
+ list_for_each_entry_safe(area, next_area, &areas, list) {
+ s32 frame_delta;
+@@ -832,6 +831,8 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ dma_sync_single_for_device(dev, phase_handle,
+ ctx->phase_size, DMA_TO_DEVICE);
+
++ spin_unlock(&ctx->queue_lock);
++
+ /* if (frame) { */
+ /* if (!wait_for_completion_timeout(&ebc->display_end, */
+ /* EBC_FRAME_TIMEOUT)) */
+@@ -1448,6 +1449,7 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ ebc_plane_state = to_ebc_plane_state(plane_state);
+ vaddr = ebc_plane_state->base.data[0].vaddr;
+
++ spin_lock(&ctx->queue_lock);
+ list_for_each_entry_safe(area, next_area, &ebc_plane_state->areas, list) {
+ struct drm_rect *dst_clip = &area->clip;
+ struct drm_rect src_clip = area->clip;
+@@ -1493,10 +1495,11 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ }
+ }
+
+- if (list_empty(&ebc_plane_state->areas))
++ if (list_empty(&ebc_plane_state->areas)){
++ spin_unlock(&ctx->queue_lock);
+ return;
++ }
+
+- spin_lock(&ctx->queue_lock);
+ list_splice_tail_init(&ebc_plane_state->areas, &ctx->queue);
+ spin_unlock(&ctx->queue_lock);
+
+--
+2.30.2
+
+
+From b36084b7f777dda669cf8132f539c2ebb89dca45 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 11:05:06 +0200
+Subject: [PATCH 22/37] [rockchip_ebc] remove/comment out debug printk messages
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 11 +++--------
+ 1 file changed, 3 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 3ef899c4779f..819e4bf28595 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -206,7 +206,6 @@ static int ioctl_trigger_global_refresh(struct drm_device *dev, void *data,
+ struct rockchip_ebc *ebc = dev_get_drvdata(dev->dev);
+
+ if (args->trigger_global_refresh){
+- printk(KERN_INFO "rockchip_ebc: ioctl triggered full refresh \n");
+ spin_lock(&ebc->refresh_once_lock);
+ ebc->do_one_full_refresh = true;
+ spin_unlock(&ebc->refresh_once_lock);
+@@ -427,7 +426,7 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ struct rockchip_ebc_area *other;
+ // by default, begin now
+ u32 frame_begin = current_frame;
+- /* printk(KERN_INFO "scheduling area: %i-%i %i-%i\n", area->clip.x1, area->clip.x2, area->clip.y1, area->clip.y2); */
++ //printk(KERN_INFO "scheduling area: %i-%i %i-%i (current frame: %i)\n", area->clip.x1, area->clip.x2, area->clip.y1, area->clip.y2, current_frame);
+
+ list_for_each_entry(other, areas, list) {
+ struct drm_rect intersection;
+@@ -768,7 +767,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+
+ /* Copy ctx->final to ctx->next on the first frame. */
+ if (frame_delta == 0) {
+- printk(KERN_INFO "rockchip partial refresh starting area on frame %i (%i/%i %i/%i)\n", frame, area->clip.x1, area->clip.x2, area->clip.y1, area->clip.y2);
++ //printk(KERN_INFO "rockchip partial refresh starting area on frame %i (%i/%i %i/%i)\n", frame, area->clip.x1, area->clip.x2, area->clip.y1, area->clip.y2);
+ local_area_count += (u64) (
+ area->clip.x2 - area->clip.x1) *
+ (area->clip.y2 - area->clip.y1);
+@@ -817,6 +816,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ drm_dbg(drm, "area %p (" DRM_RECT_FMT ") finished on %u\n",
+ area, DRM_RECT_ARG(&area->clip), frame);
+
++ //printk(KERN_INFO "rockchip partial refresh stopping area on frame %i (%i/%i %i/%i)\n", frame, area->clip.x1, area->clip.x2, area->clip.y1, area->clip.y2);
+ list_del(&area->list);
+ kfree(area);
+ }
+@@ -858,7 +858,6 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ }
+ dma_unmap_single(dev, phase_handles[0], ctx->gray4_size, DMA_TO_DEVICE);
+ dma_unmap_single(dev, phase_handles[1], ctx->gray4_size, DMA_TO_DEVICE);
+- /* printk(KERN_INFO "loca area count: %llu\n", local_area_count); */
+ ctx->area_count += local_area_count;
+ }
+
+@@ -960,7 +959,6 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+ // do we need a full refresh
+ if (auto_refresh){
+ if (ctx->area_count >= refresh_threshold * one_screen_area){
+- printk(KERN_INFO "rockchip: triggering full refresh due to drawn area threshold\n");
+ spin_lock(&ebc->refresh_once_lock);
+ ebc->do_one_full_refresh = true;
+ spin_unlock(&ebc->refresh_once_lock);
+@@ -1650,15 +1648,12 @@ static int rockchip_ebc_drm_init(struct rockchip_ebc *ebc)
+ // check if there is a default off-screen
+ if (!request_firmware(&default_offscreen, "rockchip/rockchip_ebc_default_screen.bin", drm->dev))
+ {
+- printk(KERN_INFO "rockchip_ebc: default off-screen file found\n");
+ if (default_offscreen->size != 1314144)
+ drm_err(drm, "Size of default offscreen data file is not 1314144\n");
+ else {
+- printk(KERN_INFO "rockchip_ebc: loading default off-screen\n");
+ memcpy(ebc->off_screen, default_offscreen->data, 1314144);
+ }
+ } else {
+- printk(KERN_INFO "rockchip_ebc: no default off-screen file found\n");
+ // fill the off-screen with some values
+ memset(ebc->off_screen, 0xff, 1314144);
+ /* memset(ebc->off_screen, 0x00, 556144); */
+--
+2.30.2
+
+
+From 74cfa9aaf87f2f0b93a65052c248f0bd21b4b422 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 11:08:08 +0200
+Subject: [PATCH 23/37] [rockchip_ebc] move the area-splitting code to its own
+ function and hopefully fix the pointer-usage and list-handlings bugs.
+
+Also, try to split areas even if the other area was not started yet. I'm
+not really sure if this brings benefits, but the idea is that if we have
+smaller areas, then future overlaps will probably happen less.
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 265 +++++++++++++++---------
+ 1 file changed, 162 insertions(+), 103 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 819e4bf28595..52bf5d11ec57 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -415,11 +415,157 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ memcpy(ctx->prev, ctx->next, gray4_size);
+ }
+
++/*
++ * Returns true if the area was split, false otherwise
++ */
++static int try_to_split_area(
++ struct list_head *areas,
++ struct rockchip_ebc_area *area,
++ struct rockchip_ebc_area *other,
++ int * split_counter,
++ struct rockchip_ebc_area **p_next_area,
++ struct drm_rect * intersection
++ ){
++
++ // for now, min size if 2x2
++ if ((area->clip.x2 - area->clip.x1 < 2) | (area->clip.y2 - area->clip.y1 < 2))
++ return 0;
++
++ // ok, we want to split this area and start with any partial areas
++ // that are not overlapping (well, let this be decided upon at the
++ // next outer loop - we delete this area so we need not to juggle
++ // around the four areas until we found the one that is actually
++ // overlapping)
++ int xmin, xmax, ymin, ymax, xcenter, ycenter;
++
++ bool no_xsplit = false;
++ bool no_ysplit = false;
++ bool split_both = true;
++
++ xmin = area->clip.x1;
++ if (intersection->x1 > xmin)
++ xcenter = intersection->x1;
++ else
++ xcenter = intersection->x2;
++ xmax = area->clip.x2;
++
++ ymin = area->clip.y1;
++ if (intersection->y1 > ymin)
++ ycenter = intersection->y1;
++ else
++ ycenter = intersection->y2;
++ ymax = area->clip.y2;
++
++ if ((xmin == xcenter) | (xcenter == xmax)){
++ no_xsplit = true;
++ split_both = false;
++ }
++ if ((ymin == ycenter) | (ycenter == ymax)){
++ no_ysplit = true;
++ split_both = false;
++ }
++
++ // can we land here at all???
++ if (no_xsplit && no_ysplit)
++ return 0;
++
++ // we do not want to overhelm the refresh thread and limit us to a
++ // certain number of splits. The rest needs to wait
++ if (*split_counter >= split_area_limit)
++ return 0;
++
++ // we need four new rokchip_ebc_area entries that we splice into
++ // the list. Note that the currently next item shall be copied
++ // backwards because to prevent the outer list iteration from
++ // skipping over our newly created items.
++
++ struct rockchip_ebc_area * item1;
++ struct rockchip_ebc_area * item2;
++ struct rockchip_ebc_area * item3;
++ struct rockchip_ebc_area * item4;
++ item1 = kmalloc(sizeof(*item1), GFP_KERNEL);
++ if (split_both || no_xsplit)
++ item2 = kmalloc(sizeof(*item2), GFP_KERNEL);
++ if (split_both || no_ysplit)
++ item3 = kmalloc(sizeof(*item3), GFP_KERNEL);
++ if (split_both)
++ item4 = kmalloc(sizeof(*item4), GFP_KERNEL);
++
++ // TODO: Error checking!!!!
++ /* if (!area) */
++ /* return -ENOMEM; */
++
++ if (no_xsplit)
++ xcenter = xmax;
++
++ if (no_ysplit)
++ ycenter = ymax;
++
++ if (list_is_last(&area->list, areas)){
++ list_add_tail(&item1->list, areas);
++ if (split_both || no_xsplit)
++ list_add_tail(&item2->list, areas);
++ if (split_both || no_ysplit)
++ list_add_tail(&item3->list, areas);
++ if (split_both)
++ list_add_tail(&item4->list, areas);
++ }
++ else{
++ if (split_both)
++ __list_add(&item4->list, &area->list, area->list.next);
++ if (split_both || no_ysplit)
++ __list_add(&item3->list, &area->list, area->list.next);
++ if (split_both || no_xsplit)
++ __list_add(&item2->list, &area->list, area->list.next);
++ __list_add(&item1->list, &area->list, area->list.next);
++ }
++ *p_next_area = item1;
++
++ // now fill the areas
++
++ // always
++ item1->frame_begin = EBC_FRAME_PENDING;
++ item1->clip.x1 = xmin;
++ item1->clip.x2 = xcenter;
++ item1->clip.y1 = ymin;
++ item1->clip.y2 = ycenter;
++
++ if (split_both || no_xsplit){
++ // no xsplit
++ item2->frame_begin = EBC_FRAME_PENDING;
++ item2->clip.x1 = xmin;
++ item2->clip.x2 = xcenter;
++ item2->clip.y1 = ycenter;
++ item2->clip.y2 = ymax;
++ }
++
++ if (split_both || no_ysplit){
++ // no ysplit
++ item3->frame_begin = EBC_FRAME_PENDING;
++ item3->clip.x1 = xcenter;
++ item3->clip.x2 = xmax;
++ item3->clip.y1 = ymin;
++ item3->clip.y2 = ycenter;
++ }
++
++ if (split_both){
++ // both splits
++ item4->frame_begin = EBC_FRAME_PENDING;
++ item4->clip.x1 = xcenter;
++ item4->clip.x2 = xmax;
++ item4->clip.y1 = ycenter;
++ item4->clip.y2 = ymax;
++ }
++
++ (*split_counter)++;
++ return 1;
++}
++
+ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ struct rockchip_ebc_area *area,
+ struct drm_device *drm,
+ u32 current_frame, u32 num_phases,
+- struct rockchip_ebc_area *next_area,
++ struct rockchip_ebc_area **p_next_area,
+ int * split_counter
+ )
+ {
+@@ -460,109 +606,13 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ if (drm_rect_equals(&area->clip, &intersection))
+ continue;
+
+- // for now, min size if 2x2
+- if ((area->clip.x2 - area->clip.x1 < 2) | (area->clip.y2 - area->clip.y1 < 2))
+- continue;
+-
+- // ok, we want to split this area and start with any partial areas
+- // that are not overlapping (well, let this be decided upon at the
+- // next outer loop - we delete this area so we need not to juggle
+- // around the four areas until we found the one that is actually
+- // overlapping)
+- int xmin, xmax, ymin, ymax, xcenter, ycenter;
+- xmin = area->clip.x1;
+- if (intersection.x1 > xmin)
+- xcenter = intersection.x1;
+- else
+- xcenter = intersection.x2;
+- xmax = area->clip.x2;
+-
+- ymin = area->clip.y1;
+- if (intersection.y1 > ymin)
+- ycenter = intersection.y1;
+- else
+- ycenter = intersection.y2;
+- ymax = area->clip.y2;
+-
+- if ((xmin == xcenter) | (xcenter == xmax))
+- continue;
+- if ((ymin == ycenter) | (ycenter == ymax))
+- continue;
+-
+- // we do not want to overhelm the refresh thread and limit us to a
+- // certain number of splits. The rest needs to wait
+- if (*split_counter >= split_area_limit)
++ if (try_to_split_area(areas, area, other, split_counter, p_next_area, &intersection))
++ {
++ // let the outer loop delete this area
++ return false;
++ } else {
+ continue;
+-
+- // we need four new rokchip_ebc_area entries that we splice into
+- // the list. Note that the currently next item shall be copied
+- // backwards because to prevent the outer list iteration from
+- // skipping over our newly created items.
+-
+- struct rockchip_ebc_area * item1;
+- struct rockchip_ebc_area * item2;
+- struct rockchip_ebc_area * item3;
+- struct rockchip_ebc_area * item4;
+- item1 = kmalloc(sizeof(*item1), GFP_KERNEL);
+- item2 = kmalloc(sizeof(*item2), GFP_KERNEL);
+- item3 = kmalloc(sizeof(*item3), GFP_KERNEL);
+- item4 = kmalloc(sizeof(*item4), GFP_KERNEL);
+-
+- // TODO: Error checking!!!!
+- /* if (!area) */
+- /* return -ENOMEM; */
+-
+- if (list_is_last(&area->list, areas)){
+- /* printk(KERN_INFO "adding to end of list\n"); */
+- list_add_tail(&item1->list, areas);
+- list_add_tail(&item2->list, areas);
+- list_add_tail(&item3->list, areas);
+- list_add_tail(&item4->list, areas);
+- }
+- else{
+- /* printk(KERN_INFO "splicing into the middle of the list\n"); */
+- __list_add(&item4->list, areas, areas->next);
+- __list_add(&item3->list, areas, areas->next);
+- __list_add(&item2->list, areas, areas->next);
+- __list_add(&item1->list, areas, areas->next);
+ }
+- next_area = item1;
+-
+- // now fill the areas
+- /* printk(KERN_INFO "area1: %i %i %i %i\n", xmin, xcenter, ymin, ycenter); */
+- /* printk(KERN_INFO "area2: %i %i %i %i\n", xmin, xcenter, ycenter, ymax); */
+- /* printk(KERN_INFO "area3: %i %i %i %i\n", xcenter, xmax, ymin, ycenter); */
+- /* printk(KERN_INFO "area4: %i %i %i %i\n", xcenter, xmax, ycenter, ymax); */
+-
+- item1->frame_begin = EBC_FRAME_PENDING;
+- item1->clip.x1 = xmin;
+- item1->clip.x2 = xcenter;
+- item1->clip.y1 = ymin;
+- item1->clip.y2 = ycenter;
+-
+- item2->frame_begin = EBC_FRAME_PENDING;
+- item2->clip.x1 = xmin;
+- item2->clip.x2 = xcenter;
+- item2->clip.y1 = ycenter;
+- item2->clip.y2 = ymax;
+-
+- item3->frame_begin = EBC_FRAME_PENDING;
+- item3->clip.x1 = xcenter;
+- item3->clip.x2 = xmax;
+- item3->clip.y1 = ymin;
+- item3->clip.y2 = ycenter;
+-
+- item4->frame_begin = EBC_FRAME_PENDING;
+- item4->clip.x1 = xcenter;
+- item4->clip.x2 = xmax;
+- item4->clip.y1 = ycenter;
+- item4->clip.y2 = ymax;
+-
+- (*split_counter)++;
+-
+- // let the outer loop delete this area
+- return false;
+- /* continue; */
+ }
+
+ /*
+@@ -578,6 +628,15 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ /* Otherwise, the earliest start is the same time as that of the other
+ * area. */
+ frame_begin = max(frame_begin, other->frame_begin);
++
++ // try to split, otherwise continue
++ if (try_to_split_area(areas, area, other, split_counter, p_next_area, &intersection))
++ {
++ // let the outer loop delete this area
++ return false;
++ } else {
++ continue;
++ }
+ }
+
+ area->frame_begin = frame_begin;
+@@ -754,7 +813,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ */
+ if (area->frame_begin == EBC_FRAME_PENDING &&
+ !rockchip_ebc_schedule_area(&areas, area, drm, frame,
+- ebc->lut.num_phases, next_area, &split_counter)) {
++ ebc->lut.num_phases, &next_area, &split_counter)) {
+ list_del(&area->list);
+ kfree(area);
+ continue;
+--
+2.30.2
+
+
+From 491388a2f538ef97c9699c723b3b574072b0fd85 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 11:10:24 +0200
+Subject: [PATCH 24/37] [rockchip_ebc] remove comment
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 52bf5d11ec57..5d42b45abb5b 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -591,7 +591,6 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ intersection = area->clip;
+ if (!drm_rect_intersect(&intersection, &other->clip))
+ continue;
+- // we got here, so there is a collision
+
+ /* If the other area already started, wait until it finishes. */
+ if (other->frame_begin < current_frame) {
+--
+2.30.2
+
+
+From 5a177ed3f5813d31b8d2aeda46866a067f296fdd Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 11:26:13 +0200
+Subject: [PATCH 25/37] [rockchip_ebc] fix another scheduling bug: only
+ increase, but never drecrease the frame_begin number
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 5d42b45abb5b..7f5fe7252ac4 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -594,7 +594,7 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+
+ /* If the other area already started, wait until it finishes. */
+ if (other->frame_begin < current_frame) {
+- frame_begin = other_end;
++ frame_begin = max(frame_begin, other_end);
+
+ // so here we would optimally want to split the new area into three
+ // parts that do not overlap with the already-started area, and one
+--
+2.30.2
+
+
+From 35f8f647a3f7bd68cd96abee41c442abded7c2b8 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 11:26:32 +0200
+Subject: [PATCH 26/37] [rockchip_ebc] rework comment
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 7f5fe7252ac4..974e9d23c648 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -624,8 +624,8 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ return false;
+ }
+
+- /* Otherwise, the earliest start is the same time as that of the other
+- * area. */
++ /* They do overlap but are are not equal and both not started yet, so
++ * they can potentially start together */
+ frame_begin = max(frame_begin, other->frame_begin);
+
+ // try to split, otherwise continue
+--
+2.30.2
+
+
+From d4e78c0e92bec79bacd6e73d4df5a663eb1c2cc4 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 11:27:38 +0200
+Subject: [PATCH 27/37] [rockchip_ebc] even if its not really clear if it is
+ required, also sync the next-buffer to the cpu before using it
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 974e9d23c648..97173aeed53c 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -866,10 +866,12 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ */
+ if (frame_delta > last_phase) {
+ dma_sync_single_for_cpu(dev, prev_handle, gray4_size, DMA_TO_DEVICE);
++ dma_sync_single_for_cpu(dev, next_handle, gray4_size, DMA_TO_DEVICE);
+ rockchip_ebc_blit_pixels(ctx, ctx->prev,
+ ctx->next,
+ &area->clip);
+ sync_prev = true;
++ sync_prev = true;
+
+ drm_dbg(drm, "area %p (" DRM_RECT_FMT ") finished on %u\n",
+ area, DRM_RECT_ARG(&area->clip), frame);
+--
+2.30.2
+
+
+From ecbf9a93fc89fa8129bdd6ef0db4e39988d65d3d Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 12:41:15 +0200
+Subject: [PATCH 28/37] [rockchip_ebc] enable drawing of clips not aligned to
+ full bytes (i.e. even start/end coordinates).
+
+Needs more testing.
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 62 ++++++++++++++++---------
+ 1 file changed, 41 insertions(+), 21 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 97173aeed53c..4baefc8b5496 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -1418,7 +1418,10 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ const struct drm_rect *dst_clip,
+ const void *vaddr,
+ const struct drm_framebuffer *fb,
+- const struct drm_rect *src_clip)
++ const struct drm_rect *src_clip,
++ int adjust_x1,
++ int adjust_x2
++ )
+ {
+ unsigned int dst_pitch = ctx->gray4_pitch;
+ unsigned int src_pitch = fb->pitches[0];
+@@ -1428,13 +1431,9 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ int delta_x;
+ void *dst;
+
+- bool start_x_is_odd = src_clip->x1 & 1;
+- bool end_x_is_odd = src_clip->x2 & 1;
+-
+ delta_x = panel_reflection ? -1 : 1;
+ start_x = panel_reflection ? src_clip->x2 - 1 : src_clip->x1;
+
+- // I think this also works if dst_clip->x1 is odd
+ dst = ctx->final + dst_clip->y1 * dst_pitch + dst_clip->x1 / 2;
+ src = vaddr + src_clip->y1 * src_pitch + start_x * fb->format->cpp[0];
+
+@@ -1445,6 +1444,7 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ for (x = src_clip->x1; x < src_clip->x2; x += 2) {
+ u32 rgb0, rgb1;
+ u8 gray;
++ u8 tmp_pixel;
+
+ rgb0 = *sbuf; sbuf += delta_x;
+ rgb1 = *sbuf; sbuf += delta_x;
+@@ -1459,13 +1459,21 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ rgb0 >>= 28;
+ rgb1 >>= 28;
+
+- if (x == src_clip->x1 && start_x_is_odd) {
++ // Does this account for panel reflection?
++ if (x == src_clip->x1 && (adjust_x1 == 1)) {
+ // rgb0 should be filled with the content of the src pixel here
+- rgb0 = *dbuf;
++ // keep lower 4 bits
++ // I'm not sure how to directly read only one byte from the u32
++ // pointer dbuf ...
++ tmp_pixel = *dbuf & 0b00001111;
++ rgb0 = tmp_pixel;
+ }
+- if (x == src_clip->x2 && end_x_is_odd) {
+- // rgb1 should be filled with the content of the src pixel here
+- rgb1 = *dbuf;
++ if (x == src_clip->x2 && (adjust_x2 == 1)) {
++ // rgb1 should be filled with the content of the dst pixel we
++ // want to keep here
++ // keep 4 higher bits
++ tmp_pixel = *dbuf & 0b11110000;
++ rgb1 = tmp_pixel;
+ }
+
+ gray = rgb0 | rgb1 << 4;
+@@ -1511,7 +1519,9 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ list_for_each_entry_safe(area, next_area, &ebc_plane_state->areas, list) {
+ struct drm_rect *dst_clip = &area->clip;
+ struct drm_rect src_clip = area->clip;
+- int adjust;
++ int adjust_x1;
++ int adjust_x2;
++ bool clip_changed_fb;
+
+ /* Convert from plane coordinates to CRTC coordinates. */
+ drm_rect_translate(dst_clip, translate_x, translate_y);
+@@ -1519,18 +1529,20 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ /* Adjust the clips to always process full bytes (2 pixels). */
+ /* NOTE: in direct mode, the minimum block size is 4 pixels. */
+ if (direct_mode)
+- adjust = dst_clip->x1 & 3;
++ adjust_x1 = dst_clip->x1 & 3;
+ else
+- adjust = dst_clip->x1 & 1;
+- dst_clip->x1 -= adjust;
+- src_clip.x1 -= adjust;
++ adjust_x1 = dst_clip->x1 & 1;
++
++ dst_clip->x1 -= adjust_x1;
++ src_clip.x1 -= adjust_x1;
+
+ if (direct_mode)
+- adjust = ((dst_clip->x2 + 3) ^ 3) & 3;
++ adjust_x2 = ((dst_clip->x2 + 3) ^ 3) & 3;
+ else
+- adjust = dst_clip->x2 & 1;
+- dst_clip->x2 += adjust;
+- src_clip.x2 += adjust;
++ adjust_x2 = dst_clip->x2 & 1;
++
++ dst_clip->x2 += adjust_x2;
++ src_clip.x2 += adjust_x2;
+
+ if (panel_reflection) {
+ int x1 = dst_clip->x1, x2 = dst_clip->x2;
+@@ -1539,8 +1551,16 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ dst_clip->x2 = plane_state->dst.x2 - x1;
+ }
+
+- if (!rockchip_ebc_blit_fb(ctx, dst_clip, vaddr,
+- plane_state->fb, &src_clip)) {
++ clip_changed_fb = rockchip_ebc_blit_fb(ctx, dst_clip, vaddr,
++ plane_state->fb, &src_clip, adjust_x1, adjust_x2);
++
++ // reverse coordinates
++ dst_clip->x1 += adjust_x1;
++ src_clip.x1 += adjust_x1;
++ dst_clip->x2 -= adjust_x2;
++ src_clip.x2 -= adjust_x2;
++
++ if (!clip_changed_fb) {
+ drm_dbg(plane->dev, "area %p (" DRM_RECT_FMT ") <= (" DRM_RECT_FMT ") skipped\n",
+ area, DRM_RECT_ARG(&area->clip), DRM_RECT_ARG(&src_clip));
+
+--
+2.30.2
+
+
+From cbe09b1efa307db0a5dd927c74f23663c2159494 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 12:41:58 +0200
+Subject: [PATCH 29/37] [rockchip_ebc] move the queue_lock a little bit further
+ up. Not sure if this is required, but this way we lock as soon as possible in
+ the update routine.
+
+Note that this still does not prevent the damaged-area list and the
+final framebuffer content to get out of sync during ebc refreshes.
+However, it should prevent any coherency issues and ensure consistent
+framebuffer content during each frame update.
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 4baefc8b5496..15b14acbfd2b 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -1508,6 +1508,7 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
+ ctx = to_ebc_crtc_state(crtc_state)->ctx;
+
++ spin_lock(&ctx->queue_lock);
+ drm_rect_fp_to_int(&src, &plane_state->src);
+ translate_x = plane_state->dst.x1 - src.x1;
+ translate_y = plane_state->dst.y1 - src.y1;
+@@ -1515,7 +1516,6 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ ebc_plane_state = to_ebc_plane_state(plane_state);
+ vaddr = ebc_plane_state->base.data[0].vaddr;
+
+- spin_lock(&ctx->queue_lock);
+ list_for_each_entry_safe(area, next_area, &ebc_plane_state->areas, list) {
+ struct drm_rect *dst_clip = &area->clip;
+ struct drm_rect src_clip = area->clip;
+--
+2.30.2
+
+
+From af9c4d804c7ef2efdb5ee2730b2fd9d6c6974e63 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Mon, 20 Jun 2022 13:19:31 +0200
+Subject: [PATCH 30/37] [rockchip_ebc] * add a sysfs handler
+ (/sys/module/rockchip_ebc/parameters/limit_fb_blits) to limit the numbers of
+ framebuffer blits. The default value of -1 does not limit blits at all. Can
+ be used to investigate the buffer contents while debugging complex drawing
+ chains. * add an ioctl to retrieve the final, next, prev and
+ phase[0,1] buffer contents to user space.
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 123 +++++++++++++++---------
+ include/uapi/drm/rockchip_ebc_drm.h | 12 ++-
+ 2 files changed, 91 insertions(+), 44 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 15b14acbfd2b..278a35209044 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -197,6 +197,10 @@ static int split_area_limit = 12;
+ module_param(split_area_limit, int, S_IRUGO|S_IWUSR);
+ MODULE_PARM_DESC(split_area_limit, "how many areas to split in each scheduling call");
+
++static int limit_fb_blits = -1;
++module_param(limit_fb_blits, int, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(split_area_limit, "how many fb blits to allow. -1 does not limit");
++
+ DEFINE_DRM_GEM_FOPS(rockchip_ebc_fops);
+
+ static int ioctl_trigger_global_refresh(struct drm_device *dev, void *data,
+@@ -228,11 +232,75 @@ static int ioctl_set_off_screen(struct drm_device *dev, void *data,
+ return 0;
+ }
+
++
++/**
++ * struct rockchip_ebc_ctx - context for performing display refreshes
++ *
++ * @kref: Reference count, maintained as part of the CRTC's atomic state
++ * @queue: Queue of damaged areas to be refreshed
++ * @queue_lock: Lock protecting access to @queue
++ * @prev: Display contents (Y4) before this refresh
++ * @next: Display contents (Y4) after this refresh
++ * @final: Display contents (Y4) after all pending refreshes
++ * @phase: Buffers for selecting a phase from the EBC's LUT, 1 byte/pixel
++ * @gray4_pitch: Horizontal line length of a Y4 pixel buffer in bytes
++ * @gray4_size: Size of a Y4 pixel buffer in bytes
++ * @phase_pitch: Horizontal line length of a phase buffer in bytes
++ * @phase_size: Size of a phase buffer in bytes
++ */
++struct rockchip_ebc_ctx {
++ struct kref kref;
++ struct list_head queue;
++ spinlock_t queue_lock;
++ u8 *prev;
++ u8 *next;
++ u8 *final;
++ u8 *phase[2];
++ u32 gray4_pitch;
++ u32 gray4_size;
++ u32 phase_pitch;
++ u32 phase_size;
++ u64 area_count;
++};
++
++struct ebc_crtc_state {
++ struct drm_crtc_state base;
++ struct rockchip_ebc_ctx *ctx;
++};
++
++static inline struct ebc_crtc_state *
++to_ebc_crtc_state(struct drm_crtc_state *crtc_state)
++{
++ return container_of(crtc_state, struct ebc_crtc_state, base);
++}
++static int ioctl_extract_fbs(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_rockchip_ebc_extract_fbs *args = data;
++ struct rockchip_ebc *ebc = dev_get_drvdata(dev->dev);
++ int copy_result = 0;
++ struct rockchip_ebc_ctx * ctx;
++
++ // todo: use access_ok here
++ access_ok(args->ptr_next, 1313144);
++ ctx = to_ebc_crtc_state(READ_ONCE(ebc->crtc.state))->ctx;
++ copy_result |= copy_to_user(args->ptr_prev, ctx->prev, 1313144);
++ copy_result |= copy_to_user(args->ptr_next, ctx->next, 1313144);
++ copy_result |= copy_to_user(args->ptr_final, ctx->final, 1313144);
++
++ copy_result |= copy_to_user(args->ptr_phase1, ctx->phase[0], 2 * 1313144);
++ copy_result |= copy_to_user(args->ptr_phase2, ctx->phase[1], 2 * 1313144);
++
++ return copy_result;
++}
++
+ static const struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {
+ DRM_IOCTL_DEF_DRV(ROCKCHIP_EBC_GLOBAL_REFRESH, ioctl_trigger_global_refresh,
+ DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(ROCKCHIP_EBC_OFF_SCREEN, ioctl_set_off_screen,
+ DRM_RENDER_ALLOW),
++ DRM_IOCTL_DEF_DRV(ROCKCHIP_EBC_EXTRACT_FBS, ioctl_extract_fbs,
++ DRM_RENDER_ALLOW),
+ };
+
+ static const struct drm_driver rockchip_ebc_drm_driver = {
+@@ -268,36 +336,6 @@ struct rockchip_ebc_area {
+ u32 frame_begin;
+ };
+
+-/**
+- * struct rockchip_ebc_ctx - context for performing display refreshes
+- *
+- * @kref: Reference count, maintained as part of the CRTC's atomic state
+- * @queue: Queue of damaged areas to be refreshed
+- * @queue_lock: Lock protecting access to @queue
+- * @prev: Display contents (Y4) before this refresh
+- * @next: Display contents (Y4) after this refresh
+- * @final: Display contents (Y4) after all pending refreshes
+- * @phase: Buffers for selecting a phase from the EBC's LUT, 1 byte/pixel
+- * @gray4_pitch: Horizontal line length of a Y4 pixel buffer in bytes
+- * @gray4_size: Size of a Y4 pixel buffer in bytes
+- * @phase_pitch: Horizontal line length of a phase buffer in bytes
+- * @phase_size: Size of a phase buffer in bytes
+- */
+-struct rockchip_ebc_ctx {
+- struct kref kref;
+- struct list_head queue;
+- spinlock_t queue_lock;
+- u8 *prev;
+- u8 *next;
+- u8 *final;
+- u8 *phase[2];
+- u32 gray4_pitch;
+- u32 gray4_size;
+- u32 phase_pitch;
+- u32 phase_size;
+- u64 area_count;
+-};
+-
+ static void rockchip_ebc_ctx_free(struct rockchip_ebc_ctx *ctx)
+ {
+ struct rockchip_ebc_area *area;
+@@ -360,17 +398,6 @@ static void rockchip_ebc_ctx_release(struct kref *kref)
+ * CRTC
+ */
+
+-struct ebc_crtc_state {
+- struct drm_crtc_state base;
+- struct rockchip_ebc_ctx *ctx;
+-};
+-
+-static inline struct ebc_crtc_state *
+-to_ebc_crtc_state(struct drm_crtc_state *crtc_state)
+-{
+- return container_of(crtc_state, struct ebc_crtc_state, base);
+-}
+-
+ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ struct rockchip_ebc_ctx *ctx,
+ dma_addr_t next_handle,
+@@ -1551,8 +1578,18 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ dst_clip->x2 = plane_state->dst.x2 - x1;
+ }
+
+- clip_changed_fb = rockchip_ebc_blit_fb(ctx, dst_clip, vaddr,
+- plane_state->fb, &src_clip, adjust_x1, adjust_x2);
++ if (limit_fb_blits != 0){
++ printk(KERN_INFO "atomic update: blitting: %i\n", limit_fb_blits);
++ clip_changed_fb = rockchip_ebc_blit_fb(ctx, dst_clip, vaddr,
++ plane_state->fb, &src_clip, adjust_x1, adjust_x2);
++ // the counter should only reach 0 here, -1 can only be externally set
++ limit_fb_blits -= (limit_fb_blits > 0) ? 1 : 0;
++ } else {
++ // we do not want to blit anything
++ printk(KERN_INFO "atomic update: not blitting: %i\n", limit_fb_blits);
++ clip_changed_fb = false;
++ }
++
+
+ // reverse coordinates
+ dst_clip->x1 += adjust_x1;
+diff --git a/include/uapi/drm/rockchip_ebc_drm.h b/include/uapi/drm/rockchip_ebc_drm.h
+index befa62a68be0..5e8c87ae6af2 100644
+--- a/include/uapi/drm/rockchip_ebc_drm.h
++++ b/include/uapi/drm/rockchip_ebc_drm.h
+@@ -17,9 +17,19 @@ struct drm_rockchip_ebc_off_screen {
+ char * ptr_screen_content;
+ };
+
+-#define DRM_ROCKCHIP_EBC_NUM_IOCTLS 0x02
++struct drm_rockchip_ebc_extract_fbs {
++ char * ptr_prev;
++ char * ptr_next;
++ char * ptr_final;
++ char * ptr_phase1;
++ char * ptr_phase2;
++};
++
++
++#define DRM_ROCKCHIP_EBC_NUM_IOCTLS 0x03
+
+ #define DRM_IOCTL_ROCKCHIP_EBC_GLOBAL_REFRESH DRM_IOWR(DRM_COMMAND_BASE + 0x00, struct drm_rockchip_ebc_trigger_global_refresh)
+ #define DRM_IOCTL_ROCKCHIP_EBC_OFF_SCREEN DRM_IOWR(DRM_COMMAND_BASE + 0x01, struct drm_rockchip_ebc_off_screen)
++#define DRM_IOCTL_ROCKCHIP_EBC_EXTRACT_FBS DRM_IOWR(DRM_COMMAND_BASE + 0x02, struct drm_rockchip_ebc_extract_fbs)
+
+ #endif /* __ROCKCHIP_EBC_DRM_H__*/
+--
+2.30.2
+
+
+From d238a50853c30c65bee6e7a6a2d5565250980247 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Wed, 22 Jun 2022 10:17:10 +0200
+Subject: [PATCH 31/37] [rockchip_ebc] fix compiler warnings by moving variable
+ declaration to the top of the functions
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 44 ++++++++++++++-----------
+ 1 file changed, 24 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 278a35209044..d0670d482432 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -453,6 +453,22 @@ static int try_to_split_area(
+ struct rockchip_ebc_area **p_next_area,
+ struct drm_rect * intersection
+ ){
++ int xmin, xmax, ymin, ymax, xcenter, ycenter;
++
++ bool no_xsplit = false;
++ bool no_ysplit = false;
++ bool split_both = true;
++
++ struct rockchip_ebc_area * item1;
++ struct rockchip_ebc_area * item2;
++ struct rockchip_ebc_area * item3;
++ struct rockchip_ebc_area * item4;
++
++ // we do not want to overhelm the refresh thread and limit us to a
++ // certain number of splits. The rest needs to wait
++ if (*split_counter >= split_area_limit)
++ return 0;
++
+
+ // for now, min size if 2x2
+ if ((area->clip.x2 - area->clip.x1 < 2) | (area->clip.y2 - area->clip.y1 < 2))
+@@ -463,12 +479,6 @@ static int try_to_split_area(
+ // next outer loop - we delete this area so we need not to juggle
+ // around the four areas until we found the one that is actually
+ // overlapping)
+- int xmin, xmax, ymin, ymax, xcenter, ycenter;
+-
+- bool no_xsplit = false;
+- bool no_ysplit = false;
+- bool split_both = true;
+-
+ xmin = area->clip.x1;
+ if (intersection->x1 > xmin)
+ xcenter = intersection->x1;
+@@ -496,20 +506,11 @@ static int try_to_split_area(
+ if (no_xsplit && no_ysplit)
+ return 0;
+
+- // we do not want to overhelm the refresh thread and limit us to a
+- // certain number of splits. The rest needs to wait
+- if (*split_counter >= split_area_limit)
+- return 0;
+-
+ // we need four new rokchip_ebc_area entries that we splice into
+ // the list. Note that the currently next item shall be copied
+ // backwards because to prevent the outer list iteration from
+ // skipping over our newly created items.
+
+- struct rockchip_ebc_area * item1;
+- struct rockchip_ebc_area * item2;
+- struct rockchip_ebc_area * item3;
+- struct rockchip_ebc_area * item4;
+ item1 = kmalloc(sizeof(*item1), GFP_KERNEL);
+ if (split_both || no_xsplit)
+ item2 = kmalloc(sizeof(*item2), GFP_KERNEL);
+@@ -752,17 +753,20 @@ static void rockchip_ebc_blit_pixels(const struct rockchip_ebc_ctx *ctx,
+
+ unsigned int x1_bytes = clip->x1 / 2;
+ unsigned int x2_bytes = clip->x2 / 2;
+- // the integer division floors by default, but we want to include the last
+- // byte (partially)
+- if (end_x_is_odd)
+- x2_bytes++;
+
+ unsigned int pitch = ctx->gray4_pitch;
+- unsigned int width = x2_bytes - x1_bytes;
++ unsigned int width;
+ const u8 *src_line;
+ unsigned int y;
+ u8 *dst_line;
+
++ // the integer division floors by default, but we want to include the last
++ // byte (partially)
++ if (end_x_is_odd)
++ x2_bytes++;
++
++ width = x2_bytes - x1_bytes;
++
+ dst_line = dst + clip->y1 * pitch + x1_bytes;
+ src_line = src + clip->y1 * pitch + x1_bytes;
+
+--
+2.30.2
+
+
+From e0434586f31db9beb962f8185fd567a1eae4a879 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Wed, 22 Jun 2022 10:19:06 +0200
+Subject: [PATCH 32/37] [rockchip_ebc] add debug printk statements but comment
+ them out
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 28 +++++++++++++++++++++----
+ 1 file changed, 24 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index d0670d482432..491efd20f2e9 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -605,24 +605,32 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ list_for_each_entry(other, areas, list) {
+ struct drm_rect intersection;
+ u32 other_end;
++ //printk(KERN_INFO " test other area: %i-%i %i-%i\n", other->clip.x1, other->clip.x2, other->clip.y1, other->clip.y2);
+
+ /* Only consider areas before this one in the list. */
+- if (other == area)
++ if (other == area){
++ //printk(KERN_INFO " other==area\n");
+ break;
++ }
+
+ /* Skip areas that finish refresh before this area begins. */
+ other_end = other->frame_begin + num_phases;
+- if (other_end <= frame_begin)
++ if (other_end <= frame_begin){
++ //printk(KERN_INFO " other finishes before: %i %i\n", other_end, frame_begin);
+ continue;
++ }
+
+ /* If there is no collision, the areas are independent. */
+ intersection = area->clip;
+- if (!drm_rect_intersect(&intersection, &other->clip))
++ if (!drm_rect_intersect(&intersection, &other->clip)){
++ //printk(KERN_INFO " no collision\n");
+ continue;
++ }
+
+ /* If the other area already started, wait until it finishes. */
+ if (other->frame_begin < current_frame) {
+ frame_begin = max(frame_begin, other_end);
++ //printk(KERN_INFO " other already started, setting to %i\n", frame_begin);
+
+ // so here we would optimally want to split the new area into three
+ // parts that do not overlap with the already-started area, and one
+@@ -630,12 +638,15 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ // later, but the other three should start immediately.
+
+ // if the area is equal to the clip, continue
+- if (drm_rect_equals(&area->clip, &intersection))
++ if (drm_rect_equals(&area->clip, &intersection)){
++ //printk(KERN_INFO " intersection completely contains area\n");
+ continue;
++ }
+
+ if (try_to_split_area(areas, area, other, split_counter, p_next_area, &intersection))
+ {
+ // let the outer loop delete this area
++ //printk(KERN_INFO " dropping after trying to split\n");
+ return false;
+ } else {
+ continue;
+@@ -649,17 +660,20 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ if (drm_rect_equals(&area->clip, &intersection)) {
+ drm_dbg(drm, "area %p (" DRM_RECT_FMT ") dropped, inside " DRM_RECT_FMT "\n",
+ area, DRM_RECT_ARG(&area->clip), DRM_RECT_ARG(&other->clip));
++ //printk(KERN_INFO " dropping\n");
+ return false;
+ }
+
+ /* They do overlap but are are not equal and both not started yet, so
+ * they can potentially start together */
+ frame_begin = max(frame_begin, other->frame_begin);
++ //printk(KERN_INFO " setting to: %i\n", frame_begin);
+
+ // try to split, otherwise continue
+ if (try_to_split_area(areas, area, other, split_counter, p_next_area, &intersection))
+ {
+ // let the outer loop delete this area
++ //printk(KERN_INFO " dropping after trying to split\n");
+ return false;
+ } else {
+ continue;
+@@ -667,6 +681,7 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ }
+
+ area->frame_begin = frame_begin;
++ //printk(KERN_INFO " area scheduled to start at frame: %i (current: %i)\n", frame_begin, current_frame);
+
+ return true;
+ }
+@@ -1547,12 +1562,15 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ ebc_plane_state = to_ebc_plane_state(plane_state);
+ vaddr = ebc_plane_state->base.data[0].vaddr;
+
++ //printk(KERN_INFO "new fb clips\n");
+ list_for_each_entry_safe(area, next_area, &ebc_plane_state->areas, list) {
+ struct drm_rect *dst_clip = &area->clip;
+ struct drm_rect src_clip = area->clip;
+ int adjust_x1;
+ int adjust_x2;
+ bool clip_changed_fb;
++ //printk(KERN_INFO " checking from list: (" DRM_RECT_FMT ") \n",
++ /* DRM_RECT_ARG(&area->clip)); */
+
+ /* Convert from plane coordinates to CRTC coordinates. */
+ drm_rect_translate(dst_clip, translate_x, translate_y);
+@@ -1611,6 +1629,8 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ } else {
+ drm_dbg(plane->dev, "area %p (" DRM_RECT_FMT ") <= (" DRM_RECT_FMT ") blitted\n",
+ area, DRM_RECT_ARG(&area->clip), DRM_RECT_ARG(&src_clip));
++ //printk(KERN_INFO " adding to list: (" DRM_RECT_FMT ") <= (" DRM_RECT_FMT ") blitted\n",
++ /* DRM_RECT_ARG(&area->clip), DRM_RECT_ARG(&src_clip)); */
+ }
+ }
+
+--
+2.30.2
+
+
+From bb4e13779de8d427868da024e781cff625e8287b Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Wed, 22 Jun 2022 10:21:42 +0200
+Subject: [PATCH 33/37] [rockchip_ebc] add commented-out spin_unlock to
+ indicate old position
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 491efd20f2e9..351cae36bc4d 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -847,6 +847,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ /* Move the queued damage areas to the local list. */
+ spin_lock(&ctx->queue_lock);
+ list_splice_tail_init(&ctx->queue, &areas);
++ /* spin_unlock(&ctx->queue_lock); */
+
+ list_for_each_entry_safe(area, next_area, &areas, list) {
+ s32 frame_delta;
+--
+2.30.2
+
+
+From 340c5eec973094f937d67527f868a46e2729cbba Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Wed, 22 Jun 2022 10:22:18 +0200
+Subject: [PATCH 34/37] [rockchip_ebc] not sure if this has any bad
+ consequences, but also wait on the hardware to finish the first frame
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 351cae36bc4d..e8d108727c75 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -957,11 +957,14 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ regmap_write(ebc->regmap, EBC_DSP_START,
+ ebc->dsp_start |
+ EBC_DSP_START_DSP_FRM_START);
+- if (frame) {
+- if (!wait_for_completion_timeout(&ebc->display_end,
+- EBC_FRAME_TIMEOUT))
+- drm_err(drm, "Frame %d timed out!\n", frame);
+- }
++ /* if (frame) { */
++ /* if (!wait_for_completion_timeout(&ebc->display_end, */
++ /* EBC_FRAME_TIMEOUT)) */
++ /* drm_err(drm, "Frame %d timed out!\n", frame); */
++ /* } */
++ if (!wait_for_completion_timeout(&ebc->display_end,
++ EBC_FRAME_TIMEOUT))
++ drm_err(drm, "Frame %d timed out!\n", frame);
+ }
+ dma_unmap_single(dev, phase_handles[0], ctx->gray4_size, DMA_TO_DEVICE);
+ dma_unmap_single(dev, phase_handles[1], ctx->gray4_size, DMA_TO_DEVICE);
+--
+2.30.2
+
+
+From 3242d3d78bdc68361c165838f59724732cdbb0e3 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Wed, 22 Jun 2022 10:23:03 +0200
+Subject: [PATCH 35/37] [rockchip_ebc] hopefully fix the blitting routine for
+ odd start/end coordinates and panel_reflection=1
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index e8d108727c75..f30010151c02 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -1480,9 +1480,13 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ u8 changed = 0;
+ int delta_x;
+ void *dst;
++ int test1, test2;
+
+ delta_x = panel_reflection ? -1 : 1;
+ start_x = panel_reflection ? src_clip->x2 - 1 : src_clip->x1;
++ // depending on the direction we must either save the first or the last bit
++ test1 = panel_reflection ? adjust_x1 : adjust_x2;
++ test2 = panel_reflection ? adjust_x2 : adjust_x1;
+
+ dst = ctx->final + dst_clip->y1 * dst_pitch + dst_clip->x1 / 2;
+ src = vaddr + src_clip->y1 * src_pitch + start_x * fb->format->cpp[0];
+@@ -1509,8 +1513,7 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ rgb0 >>= 28;
+ rgb1 >>= 28;
+
+- // Does this account for panel reflection?
+- if (x == src_clip->x1 && (adjust_x1 == 1)) {
++ if (x == src_clip->x1 && (test1 == 1)) {
+ // rgb0 should be filled with the content of the src pixel here
+ // keep lower 4 bits
+ // I'm not sure how to directly read only one byte from the u32
+@@ -1518,7 +1521,7 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ tmp_pixel = *dbuf & 0b00001111;
+ rgb0 = tmp_pixel;
+ }
+- if (x == src_clip->x2 && (adjust_x2 == 1)) {
++ if (x == src_clip->x2 && (test2 == 1)) {
+ // rgb1 should be filled with the content of the dst pixel we
+ // want to keep here
+ // keep 4 higher bits
+--
+2.30.2
+
+
+From 2b41563e202a5d55e19fad1164ecfc89b1e43210 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Wed, 22 Jun 2022 10:24:07 +0200
+Subject: [PATCH 36/37] [rockchip_ebc] add commented-out printk statements
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index f30010151c02..a72d1e219691 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -1608,18 +1608,17 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ }
+
+ if (limit_fb_blits != 0){
+- printk(KERN_INFO "atomic update: blitting: %i\n", limit_fb_blits);
++ //printk(KERN_INFO "atomic update: blitting: %i\n", limit_fb_blits);
+ clip_changed_fb = rockchip_ebc_blit_fb(ctx, dst_clip, vaddr,
+ plane_state->fb, &src_clip, adjust_x1, adjust_x2);
+ // the counter should only reach 0 here, -1 can only be externally set
+ limit_fb_blits -= (limit_fb_blits > 0) ? 1 : 0;
+ } else {
+ // we do not want to blit anything
+- printk(KERN_INFO "atomic update: not blitting: %i\n", limit_fb_blits);
++ //printk(KERN_INFO "atomic update: not blitting: %i\n", limit_fb_blits);
+ clip_changed_fb = false;
+ }
+
+-
+ // reverse coordinates
+ dst_clip->x1 += adjust_x1;
+ src_clip.x1 += adjust_x1;
+--
+2.30.2
+
+
+From 917a31bb1ac2eb3adbe272fd79d40ac8b21169d9 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Wed, 22 Jun 2022 10:25:04 +0200
+Subject: [PATCH 37/37] [rockchip_ebc] add commented-out old position of lock
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index a72d1e219691..62daf5c107c4 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -1645,6 +1645,7 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ return;
+ }
+
++ /* spin_lock(&ctx->queue_lock); */
+ list_splice_tail_init(&ebc_plane_state->areas, &ctx->queue);
+ spin_unlock(&ctx->queue_lock);
+
+--
+2.30.2
+
diff --git a/gnu/packages/patches/linux-libre-arm64-pinenote-touchscreen-1.patch b/gnu/packages/patches/linux-libre-arm64-pinenote-touchscreen-1.patch
new file mode 100644
index 0000000000..20026b4dd5
--- /dev/null
+++ b/gnu/packages/patches/linux-libre-arm64-pinenote-touchscreen-1.patch
@@ -0,0 +1,976 @@
+From a24cb29eca1a72afb1037f5468d3036b34ea1b66 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Myl=C3=A8ne=20Josserand?= <mylene.josserand@bootlin.com>
+Date: Sun, 9 Jan 2022 21:53:28 +1000
+Subject: [PATCH] Input: Add driver for Cypress Generation 5 touchscreen
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This is the basic driver for the Cypress TrueTouch Gen5 touchscreen
+controllers. This driver supports only the I2C bus but it uses regmap
+so SPI support could be added later.
+The touchscreen can retrieve some defined zone that are handled as
+buttons (according to the hardware). That is why it handles
+button and multitouch events.
+
+Reviewed-by: Maxime Ripard <maxime.ripard@bootlin.com>
+Signed-off-by: Mylène Josserand <mylene.josserand@bootlin.com>
+Signed-off-by: Alistair Francis <alistair@alistair23.me>
+Tested-by: Andreas Kemnade <andreas@kemnade.info> # Kobo Clara HD
+---
+ drivers/input/touchscreen/Kconfig | 16 +
+ drivers/input/touchscreen/Makefile | 1 +
+ drivers/input/touchscreen/cyttsp5.c | 902 ++++++++++++++++++++++++++++
+ 3 files changed, 919 insertions(+)
+ create mode 100644 drivers/input/touchscreen/cyttsp5.c
+
+diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
+index 2f6adfb7b938..eb4e1b156683 100644
+--- a/drivers/input/touchscreen/Kconfig
++++ b/drivers/input/touchscreen/Kconfig
+@@ -284,6 +284,22 @@ config TOUCHSCREEN_CYTTSP4_SPI
+ To compile this driver as a module, choose M here: the
+ module will be called cyttsp4_spi.
+
++config TOUCHSCREEN_CYTTSP5
++ tristate "Cypress TrueTouch Gen5 Touchscreen Driver"
++ depends on I2C
++ select REGMAP_I2C
++ select CRC_ITU_T
++ help
++ Driver for Parade TrueTouch Standard Product Generation 5
++ touchscreen controllers. I2C bus interface support only.
++
++ Say Y here if you have a Cypress Gen5 touchscreen.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called cyttsp5.
++
+ config TOUCHSCREEN_DA9034
+ tristate "Touchscreen support for Dialog Semiconductor DA9034"
+ depends on PMIC_DA903X
+diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
+index 39a8127cf6a5..0ea5c47f7fd9 100644
+--- a/drivers/input/touchscreen/Makefile
++++ b/drivers/input/touchscreen/Makefile
+@@ -30,6 +30,7 @@ obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o
+ obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_CORE) += cyttsp4_core.o
+ obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_I2C) += cyttsp4_i2c.o cyttsp_i2c_common.o
+ obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_SPI) += cyttsp4_spi.o
++obj-$(CONFIG_TOUCHSCREEN_CYTTSP5) += cyttsp5.o
+ obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
+ obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o
+ obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
+diff --git a/drivers/input/touchscreen/cyttsp5.c b/drivers/input/touchscreen/cyttsp5.c
+new file mode 100644
+index 000000000000..3ac45108090c
+--- /dev/null
++++ b/drivers/input/touchscreen/cyttsp5.c
+@@ -0,0 +1,902 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Parade TrueTouch(TM) Standard Product V5 Module.
++ *
++ * Copyright (C) 2015 Parade Technologies
++ * Copyright (C) 2012-2015 Cypress Semiconductor
++ * Copyright (C) 2018 Bootlin
++ *
++ * Authors: Mylène Josserand <mylene.josserand@bootlin.com>
++ * Alistair Francis <alistair@alistair23.me>
++ */
++
++#include <linux/crc-itu-t.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/gpio/consumer.h>
++#include <linux/input/mt.h>
++#include <linux/input/touchscreen.h>
++#include <linux/interrupt.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/regmap.h>
++#include <asm/unaligned.h>
++
++#define CYTTSP5_NAME "cyttsp5"
++#define CY_I2C_DATA_SIZE (2 * 256)
++#define HID_VERSION 0x0100
++#define CY_MAX_INPUT 512
++#define CYTTSP5_PREALLOCATED_CMD_BUFFER 32
++#define CY_BITS_PER_BTN 1
++#define CY_NUM_BTN_EVENT_ID GENMASK(CY_BITS_PER_BTN, 0)
++
++#define MAX_AREA 255
++#define HID_OUTPUT_BL_SOP 0x1
++#define HID_OUTPUT_BL_EOP 0x17
++#define HID_OUTPUT_BL_LAUNCH_APP 0x3B
++#define HID_OUTPUT_BL_LAUNCH_APP_SIZE 11
++#define HID_OUTPUT_GET_SYSINFO 0x2
++#define HID_OUTPUT_GET_SYSINFO_SIZE 5
++#define HID_OUTPUT_MAX_CMD_SIZE 12
++
++#define HID_DESC_REG 0x1
++#define HID_INPUT_REG 0x3
++#define HID_OUTPUT_REG 0x4
++
++#define REPORT_ID_TOUCH 0x1
++#define REPORT_ID_BTN 0x3
++#define REPORT_SIZE_5 5
++#define REPORT_SIZE_8 8
++#define REPORT_SIZE_16 16
++
++/* Touch reports offsets */
++/* Header offsets */
++#define TOUCH_REPORT_DESC_HDR_CONTACTCOUNT 16
++/* Record offsets */
++#define TOUCH_REPORT_DESC_CONTACTID 8
++#define TOUCH_REPORT_DESC_X 16
++#define TOUCH_REPORT_DESC_Y 32
++#define TOUCH_REPORT_DESC_P 48
++#define TOUCH_REPORT_DESC_MAJ 56
++#define TOUCH_REPORT_DESC_MIN 64
++
++/* HID */
++#define HID_TOUCH_REPORT_ID 0x1
++#define HID_BTN_REPORT_ID 0x3
++#define HID_APP_RESPONSE_REPORT_ID 0x1F
++#define HID_APP_OUTPUT_REPORT_ID 0x2F
++#define HID_BL_RESPONSE_REPORT_ID 0x30
++#define HID_BL_OUTPUT_REPORT_ID 0x40
++
++#define HID_OUTPUT_RESPONSE_REPORT_OFFSET 2
++#define HID_OUTPUT_RESPONSE_CMD_OFFSET 4
++#define HID_OUTPUT_RESPONSE_CMD_MASK GENMASK(6, 0)
++
++#define HID_SYSINFO_SENSING_OFFSET 33
++#define HID_SYSINFO_BTN_OFFSET 48
++#define HID_SYSINFO_BTN_MASK GENMASK(7, 0)
++#define HID_SYSINFO_MAX_BTN 8
++
++#define CY_HID_OUTPUT_TIMEOUT_MS 200
++#define CY_HID_OUTPUT_GET_SYSINFO_TIMEOUT_MS 3000
++#define CY_HID_GET_HID_DESCRIPTOR_TIMEOUT_MS 4000
++
++/* maximum number of concurrent tracks */
++#define TOUCH_REPORT_SIZE 10
++#define TOUCH_INPUT_HEADER_SIZE 7
++#define BTN_REPORT_SIZE 9
++#define BTN_INPUT_HEADER_SIZE 5
++
++#define MAX_CY_TCH_T_IDS 32
++
++/* All usage pages for Touch Report */
++#define TOUCH_REPORT_USAGE_PG_X 0x00010030
++#define TOUCH_REPORT_USAGE_PG_Y 0x00010031
++#define TOUCH_REPORT_USAGE_PG_P 0x000D0030
++#define TOUCH_REPORT_USAGE_PG_CONTACTID 0x000D0051
++#define TOUCH_REPORT_USAGE_PG_CONTACTCOUNT 0x000D0054
++#define TOUCH_REPORT_USAGE_PG_MAJ 0xFF010062
++#define TOUCH_REPORT_USAGE_PG_MIN 0xFF010063
++#define TOUCH_COL_USAGE_PG 0x000D0022
++
++/* System Information interface definitions */
++struct cyttsp5_sensing_conf_data_dev {
++ u8 electrodes_x;
++ u8 electrodes_y;
++ __le16 len_x;
++ __le16 len_y;
++ __le16 res_x;
++ __le16 res_y;
++ __le16 max_z;
++ u8 origin_x;
++ u8 origin_y;
++ u8 btn;
++ u8 scan_mode;
++ u8 max_num_of_tch_per_refresh_cycle;
++} __packed;
++
++struct cyttsp5_sensing_conf_data {
++ u16 res_x;
++ u16 res_y;
++ u16 max_z;
++ u16 len_x;
++ u16 len_y;
++ u8 origin_x;
++ u8 origin_y;
++ u8 max_tch;
++};
++
++enum cyttsp5_tch_abs { /* for ordering within the extracted touch data array */
++ CY_TCH_X, /* X */
++ CY_TCH_Y, /* Y */
++ CY_TCH_P, /* P (Z) */
++ CY_TCH_T, /* TOUCH ID */
++ CY_TCH_MAJ, /* TOUCH_MAJOR */
++ CY_TCH_MIN, /* TOUCH_MINOR */
++ CY_TCH_NUM_ABS
++};
++
++struct cyttsp5_tch_abs_params {
++ size_t ofs; /* abs byte offset */
++ size_t size; /* size in bits */
++ size_t min; /* min value */
++ size_t max; /* max value */
++ size_t bofs; /* bit offset */
++};
++
++struct cyttsp5_touch {
++ int abs[CY_TCH_NUM_ABS];
++};
++
++struct cyttsp5_sysinfo {
++ struct cyttsp5_sensing_conf_data sensing_conf_data;
++ int num_btns;
++ struct cyttsp5_tch_abs_params tch_hdr;
++ struct cyttsp5_tch_abs_params tch_abs[CY_TCH_NUM_ABS];
++ u32 key_code[HID_SYSINFO_MAX_BTN];
++};
++
++struct cyttsp5_hid_desc {
++ __le16 hid_desc_len;
++ u8 packet_id;
++ u8 reserved_byte;
++ __le16 bcd_version;
++ __le16 report_desc_len;
++ __le16 report_desc_register;
++ __le16 input_register;
++ __le16 max_input_len;
++ __le16 output_register;
++ __le16 max_output_len;
++ __le16 command_register;
++ __le16 data_register;
++ __le16 vendor_id;
++ __le16 product_id;
++ __le16 version_id;
++ u8 reserved[4];
++} __packed;
++
++struct cyttsp5 {
++ struct device *dev;
++ struct completion cmd_done;
++ struct cyttsp5_sysinfo sysinfo;
++ struct cyttsp5_hid_desc hid_desc;
++ u8 cmd_buf[CYTTSP5_PREALLOCATED_CMD_BUFFER];
++ u8 input_buf[CY_MAX_INPUT];
++ u8 response_buf[CY_MAX_INPUT];
++ struct gpio_desc *reset_gpio;
++ struct input_dev *input;
++ char phys[NAME_MAX];
++ int num_prv_rec;
++ struct regmap *regmap;
++ struct touchscreen_properties prop;
++ struct regulator *vdd;
++};
++
++/*
++ * For what is understood in the datasheet, the register does not
++ * matter. For consistency, use the Input Register address
++ * but it does mean anything to the device. The important data
++ * to send is the I2C address
++ */
++static int cyttsp5_read(struct cyttsp5 *ts, u8 *buf, u32 max)
++{
++ int error;
++ u32 size;
++ u8 temp[2];
++
++ /* Read the frame to retrieve the size */
++ error = regmap_bulk_read(ts->regmap, HID_INPUT_REG, temp, sizeof(temp));
++ if (error)
++ return error;
++
++ size = get_unaligned_le16(temp);
++ if (!size || size == 2)
++ return 0;
++
++ if (size > max)
++ return -EINVAL;
++
++ /* Get the real value */
++ return regmap_bulk_read(ts->regmap, HID_INPUT_REG, buf, size);
++}
++
++static int cyttsp5_write(struct cyttsp5 *ts, unsigned int reg, u8 *data,
++ size_t size)
++{
++ u8 cmd[HID_OUTPUT_MAX_CMD_SIZE];
++
++ if (size + 1 > HID_OUTPUT_MAX_CMD_SIZE)
++ return -E2BIG;
++
++ /* High bytes of register address needed as first byte of cmd */
++ cmd[0] = (reg >> 8) & 0xFF ;
++
++ /* Copy the rest of the data */
++ if (data)
++ memcpy(&cmd[1], data, size);
++
++ /*
++ * The hardware wants to receive a frame with the address register
++ * contained in the first two bytes. As the regmap_write function
++ * add the register adresse in the frame, we use the low byte as
++ * first frame byte for the address register and the first
++ * data byte is the high register + left of the cmd to send
++ */
++ return regmap_bulk_write(ts->regmap, reg & 0xFF, cmd, size + 1);
++}
++
++static void cyttsp5_get_touch_axis(int *axis, int size, int max, u8 *xy_data,
++ int bofs)
++{
++ int nbyte;
++
++ for (nbyte = 0, *axis = 0; nbyte < size; nbyte++)
++ *axis += ((xy_data[nbyte] >> bofs) << (nbyte * 8));
++
++ *axis &= max - 1;
++}
++
++static void cyttsp5_get_touch_record(struct cyttsp5 *ts,
++ struct cyttsp5_touch *touch, u8 *xy_data)
++{
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++ enum cyttsp5_tch_abs abs;
++
++ for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++)
++ cyttsp5_get_touch_axis(&touch->abs[abs],
++ si->tch_abs[abs].size,
++ si->tch_abs[abs].max,
++ xy_data + si->tch_abs[abs].ofs,
++ si->tch_abs[abs].bofs);
++}
++
++static void cyttsp5_get_mt_touches(struct cyttsp5 *ts,
++ struct cyttsp5_touch *tch, int num_cur_tch)
++{
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++ int i, t = 0, offset = 0;
++ DECLARE_BITMAP(ids, MAX_CY_TCH_T_IDS);
++ u8 *tch_addr;
++ int tmp;
++
++ bitmap_zero(ids, MAX_CY_TCH_T_IDS);
++ memset(tch->abs, 0, sizeof(tch->abs));
++
++ switch (ts->input_buf[2]) {
++ case HID_TOUCH_REPORT_ID:
++ offset = TOUCH_INPUT_HEADER_SIZE;
++ break;
++ case HID_BTN_REPORT_ID:
++ offset = BTN_INPUT_HEADER_SIZE;
++ break;
++ }
++
++ for (i = 0; i < num_cur_tch; i++) {
++ tch_addr = ts->input_buf + offset + (i * TOUCH_REPORT_SIZE);
++ cyttsp5_get_touch_record(ts, tch, tch_addr);
++
++ /* Convert MAJOR/MINOR from mm to resolution */
++ tmp = tch->abs[CY_TCH_MAJ] * 100 * si->sensing_conf_data.res_x;
++ tch->abs[CY_TCH_MAJ] = tmp / si->sensing_conf_data.len_x;
++ tmp = tch->abs[CY_TCH_MIN] * 100 * si->sensing_conf_data.res_x;
++ tch->abs[CY_TCH_MIN] = tmp / si->sensing_conf_data.len_x;
++
++ t = tch->abs[CY_TCH_T];
++ input_mt_slot(ts->input, t);
++ input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true);
++ __set_bit(t, ids);
++
++ /* position and pressure fields */
++ touchscreen_report_pos(ts->input, &ts->prop,
++ tch->abs[CY_TCH_X], tch->abs[CY_TCH_Y],
++ true);
++ input_report_abs(ts->input, ABS_MT_PRESSURE,
++ tch->abs[CY_TCH_P]);
++
++ /* Get the extended touch fields */
++ input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
++ tch->abs[CY_TCH_MAJ]);
++ input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
++ tch->abs[CY_TCH_MIN]);
++ }
++
++ ts->num_prv_rec = num_cur_tch;
++}
++
++static int cyttsp5_mt_attention(struct device *dev)
++{
++ struct cyttsp5 *ts = dev_get_drvdata(dev);
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++ int max_tch = si->sensing_conf_data.max_tch;
++ struct cyttsp5_touch tch;
++ u8 num_cur_tch;
++
++ cyttsp5_get_touch_axis((int *) &num_cur_tch, si->tch_hdr.size,
++ si->tch_hdr.max,
++ ts->input_buf + 3 + si->tch_hdr.ofs,
++ si->tch_hdr.bofs);
++
++ if (num_cur_tch > max_tch) {
++ dev_err(dev, "Num touch err detected (n=%d)\n", num_cur_tch);
++ num_cur_tch = max_tch;
++ }
++
++ if (num_cur_tch == 0 && ts->num_prv_rec == 0)
++ return 0;
++
++ /* extract xy_data for all currently reported touches */
++ if (num_cur_tch)
++ cyttsp5_get_mt_touches(ts, &tch, num_cur_tch);
++
++ input_mt_sync_frame(ts->input);
++ input_sync(ts->input);
++
++ return 0;
++}
++
++static int cyttsp5_setup_input_device(struct device *dev)
++{
++ struct cyttsp5 *ts = dev_get_drvdata(dev);
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++ int max_x, max_y, max_p;
++ int max_x_tmp, max_y_tmp;
++ int error;
++
++ max_x_tmp = si->sensing_conf_data.res_x;
++ max_y_tmp = si->sensing_conf_data.res_y;
++ max_x = max_x_tmp - 1;
++ max_y = max_y_tmp - 1;
++ max_p = si->sensing_conf_data.max_z;
++
++ input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, max_x, 0, 0);
++ input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, max_y, 0, 0);
++ input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, max_p, 0, 0);
++
++ input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0);
++ input_set_abs_params(ts->input, ABS_MT_TOUCH_MINOR, 0, MAX_AREA, 0, 0);
++
++ error = input_mt_init_slots(ts->input, si->tch_abs[CY_TCH_T].max,
++ INPUT_MT_DROP_UNUSED | INPUT_MT_DIRECT);
++ if (error < 0)
++ return error;
++
++ error = input_register_device(ts->input);
++ if (error < 0)
++ dev_err(dev, "Error, failed register input device r=%d\n", error);
++
++ return error;
++}
++
++static int cyttsp5_parse_dt_key_code(struct device *dev)
++{
++ struct cyttsp5 *ts = dev_get_drvdata(dev);
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++
++ if (!si->num_btns)
++ return 0;
++
++ /* Initialize the button to RESERVED */
++ memset32(si->key_code, KEY_RESERVED, si->num_btns);
++
++ return device_property_read_u32_array(dev, "linux,keycodes",
++ si->key_code, si->num_btns);
++}
++
++static int cyttsp5_btn_attention(struct device *dev)
++{
++ struct cyttsp5 *ts = dev_get_drvdata(dev);
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++ int cur_btn, offset = 0;
++ int cur_btn_state;
++
++ switch (ts->input_buf[2]) {
++ case HID_TOUCH_REPORT_ID:
++ offset = TOUCH_INPUT_HEADER_SIZE;
++ break;
++ case HID_BTN_REPORT_ID:
++ offset = BTN_INPUT_HEADER_SIZE;
++ break;
++ }
++
++ if (ts->input_buf[2] != HID_BTN_REPORT_ID || !si->num_btns)
++ return 0;
++
++ /* extract button press/release touch information */
++ for (cur_btn = 0; cur_btn < si->num_btns; cur_btn++) {
++ /* Get current button state */
++ cur_btn_state = (ts->input_buf[offset] >> (cur_btn * CY_BITS_PER_BTN))
++ & CY_NUM_BTN_EVENT_ID;
++
++ input_report_key(ts->input, si->key_code[cur_btn],
++ cur_btn_state);
++ input_sync(ts->input);
++ }
++
++ return 0;
++}
++
++static int cyttsp5_validate_cmd_response(struct cyttsp5 *ts, u8 code)
++{
++ u16 size, crc;
++ u8 status, report_id;
++ int command_code;
++
++ size = get_unaligned_le16(&ts->response_buf[0]);
++
++ if (!size)
++ return 0;
++
++ report_id = ts->response_buf[HID_OUTPUT_RESPONSE_REPORT_OFFSET];
++
++ switch (report_id) {
++ case HID_BL_RESPONSE_REPORT_ID: {
++ if (ts->response_buf[4] != HID_OUTPUT_BL_SOP) {
++ dev_err(ts->dev, "HID output response, wrong SOP\n");
++ return -EPROTO;
++ }
++
++ if (ts->response_buf[size - 1] != HID_OUTPUT_BL_EOP) {
++ dev_err(ts->dev, "HID output response, wrong EOP\n");
++ return -EPROTO;
++ }
++
++ crc = crc_itu_t(0xFFFF, &ts->response_buf[4], size - 7);
++ if (get_unaligned_le16(&ts->response_buf[size - 3]) != crc) {
++ dev_err(ts->dev, "HID output response, wrong CRC 0x%X\n",
++ crc);
++ return -EPROTO;
++ }
++
++ status = ts->response_buf[5];
++ if (status) {
++ dev_err(ts->dev, "HID output response, ERROR:%d\n",
++ status);
++ return -EPROTO;
++ }
++ break;
++ }
++ case HID_APP_RESPONSE_REPORT_ID: {
++ command_code = ts->response_buf[HID_OUTPUT_RESPONSE_CMD_OFFSET]
++ & HID_OUTPUT_RESPONSE_CMD_MASK;
++ if (command_code != code) {
++ dev_err(ts->dev,
++ "HID output response, wrong command_code:%X\n",
++ command_code);
++ return -EPROTO;
++ }
++ break;
++ }
++ }
++
++ return 0;
++}
++
++static void cyttsp5_si_get_btn_data(struct cyttsp5 *ts)
++{
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++ unsigned int btns = ts->response_buf[HID_SYSINFO_BTN_OFFSET]
++ & HID_SYSINFO_BTN_MASK;
++
++ si->num_btns = hweight8(btns);
++}
++
++static int cyttsp5_get_sysinfo_regs(struct cyttsp5 *ts)
++{
++ struct cyttsp5_sensing_conf_data *scd = &ts->sysinfo.sensing_conf_data;
++ struct cyttsp5_sensing_conf_data_dev *scd_dev =
++ (struct cyttsp5_sensing_conf_data_dev *)
++ &ts->response_buf[HID_SYSINFO_SENSING_OFFSET];
++
++ cyttsp5_si_get_btn_data(ts);
++
++ scd->max_tch = scd_dev->max_num_of_tch_per_refresh_cycle;
++ scd->res_x = get_unaligned_le16(&scd_dev->res_x);
++ scd->res_y = get_unaligned_le16(&scd_dev->res_y);
++ scd->max_z = get_unaligned_le16(&scd_dev->max_z);
++ scd->len_x = get_unaligned_le16(&scd_dev->len_x);
++ scd->len_y = get_unaligned_le16(&scd_dev->len_y);
++
++ return 0;
++}
++
++static int cyttsp5_hid_output_get_sysinfo(struct cyttsp5 *ts)
++{
++ int rc;
++ u8 cmd[HID_OUTPUT_GET_SYSINFO_SIZE];
++
++ /* HI bytes of Output register address */
++ put_unaligned_le16(HID_OUTPUT_GET_SYSINFO_SIZE, cmd);
++ cmd[2] = HID_APP_OUTPUT_REPORT_ID;
++ cmd[3] = 0x0; /* Reserved */
++ cmd[4] = HID_OUTPUT_GET_SYSINFO;
++
++ rc = cyttsp5_write(ts, HID_OUTPUT_REG, cmd,
++ HID_OUTPUT_GET_SYSINFO_SIZE);
++ if (rc) {
++ dev_err(ts->dev, "Failed to write command %d", rc);
++ return rc;
++ }
++
++ rc = wait_for_completion_interruptible_timeout(&ts->cmd_done,
++ msecs_to_jiffies(CY_HID_OUTPUT_GET_SYSINFO_TIMEOUT_MS));
++ if (rc <= 0) {
++ dev_err(ts->dev, "HID output cmd execution timed out\n");
++ rc = -ETIMEDOUT;
++ return rc;
++ }
++
++ rc = cyttsp5_validate_cmd_response(ts, HID_OUTPUT_GET_SYSINFO);
++ if (rc) {
++ dev_err(ts->dev, "Validation of the response failed\n");
++ return rc;
++ }
++
++ return cyttsp5_get_sysinfo_regs(ts);
++}
++
++static int cyttsp5_hid_output_bl_launch_app(struct cyttsp5 *ts)
++{
++ int rc;
++ u8 cmd[HID_OUTPUT_BL_LAUNCH_APP];
++ u16 crc;
++
++ put_unaligned_le16(HID_OUTPUT_BL_LAUNCH_APP_SIZE, cmd);
++ cmd[2] = HID_BL_OUTPUT_REPORT_ID;
++ cmd[3] = 0x0; /* Reserved */
++ cmd[4] = HID_OUTPUT_BL_SOP;
++ cmd[5] = HID_OUTPUT_BL_LAUNCH_APP;
++ put_unaligned_le16(0x00, &cmd[6]);
++ crc = crc_itu_t(0xFFFF, &cmd[4], 4);
++ put_unaligned_le16(crc, &cmd[8]);
++ cmd[10] = HID_OUTPUT_BL_EOP;
++
++ rc = cyttsp5_write(ts, HID_OUTPUT_REG, cmd,
++ HID_OUTPUT_BL_LAUNCH_APP_SIZE);
++ if (rc) {
++ dev_err(ts->dev, "Failed to write command %d", rc);
++ return rc;
++ }
++
++ rc = wait_for_completion_interruptible_timeout(&ts->cmd_done,
++ msecs_to_jiffies(CY_HID_OUTPUT_TIMEOUT_MS));
++ if (rc <= 0) {
++ dev_err(ts->dev, "HID output cmd execution timed out\n");
++ rc = -ETIMEDOUT;
++ return rc;
++ }
++
++ rc = cyttsp5_validate_cmd_response(ts, HID_OUTPUT_BL_LAUNCH_APP);
++ if (rc) {
++ dev_err(ts->dev, "Validation of the response failed\n");
++ return rc;
++ }
++
++ return 0;
++}
++
++static int cyttsp5_get_hid_descriptor(struct cyttsp5 *ts,
++ struct cyttsp5_hid_desc *desc)
++{
++ struct device *dev = ts->dev;
++ __le16 hid_desc_register = HID_DESC_REG;
++ int rc;
++ u8 cmd[2];
++
++ /* Set HID descriptor register */
++ memcpy(cmd, &hid_desc_register, sizeof(hid_desc_register));
++
++ rc = cyttsp5_write(ts, HID_DESC_REG, NULL, 0);
++ if (rc) {
++ dev_err(dev, "Failed to get HID descriptor, rc=%d\n", rc);
++ return rc;
++ }
++
++ rc = wait_for_completion_interruptible_timeout(&ts->cmd_done,
++ msecs_to_jiffies(CY_HID_GET_HID_DESCRIPTOR_TIMEOUT_MS));
++ if (rc <= 0) {
++ dev_err(ts->dev, "HID get descriptor timed out\n");
++ rc = -ETIMEDOUT;
++ return rc;
++ }
++
++ memcpy(desc, ts->response_buf, sizeof(*desc));
++
++ /* Check HID descriptor length and version */
++ if (le16_to_cpu(desc->hid_desc_len) != sizeof(*desc) ||
++ le16_to_cpu(desc->bcd_version) != HID_VERSION) {
++ dev_err(dev, "Unsupported HID version\n");
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++static int fill_tch_abs(struct cyttsp5_tch_abs_params *tch_abs, int report_size,
++ int offset)
++{
++ tch_abs->ofs = offset / 8;
++ tch_abs->size = report_size / 8;
++ if (report_size % 8)
++ tch_abs->size += 1;
++ tch_abs->min = 0;
++ tch_abs->max = 1 << report_size;
++ tch_abs->bofs = offset - (tch_abs->ofs << 3);
++
++ return 0;
++}
++
++static irqreturn_t cyttsp5_handle_irq(int irq, void *handle)
++{
++ struct cyttsp5 *ts = handle;
++ int report_id;
++ int size;
++ int error;
++
++ error = cyttsp5_read(ts, ts->input_buf, CY_MAX_INPUT);
++ if (error)
++ return IRQ_HANDLED;
++
++ size = get_unaligned_le16(&ts->input_buf[0]);
++ if (size == 0) {
++ /* reset */
++ report_id = 0;
++ size = 2;
++ } else {
++ report_id = ts->input_buf[2];
++ }
++
++ switch (report_id) {
++ case HID_TOUCH_REPORT_ID:
++ cyttsp5_mt_attention(ts->dev);
++ break;
++ case HID_BTN_REPORT_ID:
++ cyttsp5_btn_attention(ts->dev);
++ break;
++ default:
++ /* It is not an input but a command response */
++ memcpy(ts->response_buf, ts->input_buf, size);
++ complete(&ts->cmd_done);
++ }
++
++ return IRQ_HANDLED;
++}
++
++static int cyttsp5_deassert_int(struct cyttsp5 *ts)
++{
++ u16 size;
++ u8 buf[2];
++ int error;
++
++ error = regmap_bulk_read(ts->regmap, HID_INPUT_REG, buf, sizeof(buf));
++ if (error < 0)
++ return error;
++
++ size = get_unaligned_le16(&buf[0]);
++ if (size == 2 || size == 0)
++ return 0;
++
++ return -EINVAL;
++}
++
++static int cyttsp5_fill_all_touch(struct cyttsp5 *ts)
++{
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++
++ fill_tch_abs(&si->tch_abs[CY_TCH_X], REPORT_SIZE_16,
++ TOUCH_REPORT_DESC_X);
++ fill_tch_abs(&si->tch_abs[CY_TCH_Y], REPORT_SIZE_16,
++ TOUCH_REPORT_DESC_Y);
++ fill_tch_abs(&si->tch_abs[CY_TCH_P], REPORT_SIZE_8,
++ TOUCH_REPORT_DESC_P);
++ fill_tch_abs(&si->tch_abs[CY_TCH_T], REPORT_SIZE_5,
++ TOUCH_REPORT_DESC_CONTACTID);
++ fill_tch_abs(&si->tch_hdr, REPORT_SIZE_5,
++ TOUCH_REPORT_DESC_HDR_CONTACTCOUNT);
++ fill_tch_abs(&si->tch_abs[CY_TCH_MAJ], REPORT_SIZE_8,
++ TOUCH_REPORT_DESC_MAJ);
++ fill_tch_abs(&si->tch_abs[CY_TCH_MIN], REPORT_SIZE_8,
++ TOUCH_REPORT_DESC_MIN);
++
++ return 0;
++}
++
++static int cyttsp5_startup(struct cyttsp5 *ts)
++{
++ int error;
++
++ error = cyttsp5_deassert_int(ts);
++ if (error) {
++ dev_err(ts->dev, "Error on deassert int r=%d\n", error);
++ return -ENODEV;
++ }
++
++ /*
++ * Launch the application as the device starts in bootloader mode
++ * because of a power-on-reset
++ */
++ error = cyttsp5_hid_output_bl_launch_app(ts);
++ if (error < 0) {
++ dev_err(ts->dev, "Error on launch app r=%d\n", error);
++ return error;
++ }
++
++ error = cyttsp5_get_hid_descriptor(ts, &ts->hid_desc);
++ if (error < 0) {
++ dev_err(ts->dev, "Error on getting HID descriptor r=%d\n", error);
++ return error;
++ }
++
++ error = cyttsp5_fill_all_touch(ts);
++ if (error < 0) {
++ dev_err(ts->dev, "Error on report descriptor r=%d\n", error);
++ return error;
++ }
++
++ error = cyttsp5_hid_output_get_sysinfo(ts);
++ if (error) {
++ dev_err(ts->dev, "Error on getting sysinfo r=%d\n", error);
++ return error;
++ }
++
++ return error;
++}
++
++static void cyttsp5_cleanup(void *data)
++{
++ struct cyttsp5 *ts = data;
++
++ regulator_disable(ts->vdd);
++}
++
++static int cyttsp5_probe(struct device *dev, struct regmap *regmap, int irq,
++ const char *name)
++{
++ struct cyttsp5 *ts;
++ struct cyttsp5_sysinfo *si;
++ int error, i;
++
++ ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
++ if (!ts)
++ return -ENOMEM;
++
++ /* Initialize device info */
++ ts->regmap = regmap;
++ ts->dev = dev;
++ si = &ts->sysinfo;
++ dev_set_drvdata(dev, ts);
++
++ init_completion(&ts->cmd_done);
++
++ /* Power up the device */
++ ts->vdd = devm_regulator_get(dev, "vdd");
++ if (IS_ERR(ts->vdd)) {
++ error = PTR_ERR(ts->vdd);
++ return error;
++ }
++
++ error = devm_add_action_or_reset(dev, cyttsp5_cleanup, ts);
++ if (error) {
++ return error;
++ }
++
++ error = regulator_enable(ts->vdd);
++ if (error) {
++ return error;
++ }
++
++ ts->input = devm_input_allocate_device(dev);
++ if (!ts->input) {
++ dev_err(dev, "Error, failed to allocate input device\n");
++ return -ENODEV;
++ }
++
++ ts->input->name = "cyttsp5";
++ scnprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
++ ts->input->phys = ts->phys;
++ input_set_drvdata(ts->input, ts);
++
++ /* Reset the gpio to be in a reset state */
++ ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
++ if (IS_ERR(ts->reset_gpio)) {
++ error = PTR_ERR(ts->reset_gpio);
++ dev_err(dev, "Failed to request reset gpio, error %d\n", error);
++ return error;
++ }
++ gpiod_set_value(ts->reset_gpio, 0);
++
++ /* Need a delay to have device up */
++ msleep(20);
++
++ error = devm_request_threaded_irq(dev, irq, NULL, cyttsp5_handle_irq,
++ IRQF_ONESHOT, name, ts);
++ if (error) {
++ dev_err(dev, "unable to request IRQ\n");
++ return error;
++ }
++
++ error = cyttsp5_startup(ts);
++ if (error) {
++ dev_err(ts->dev, "Fail initial startup r=%d\n", error);
++ return error;
++ }
++
++ error = cyttsp5_parse_dt_key_code(dev);
++ if (error < 0) {
++ dev_err(ts->dev, "Error while parsing dts %d\n", error);
++ return error;
++ }
++
++ touchscreen_parse_properties(ts->input, true, &ts->prop);
++
++ __set_bit(EV_KEY, ts->input->evbit);
++ for (i = 0; i < si->num_btns; i++)
++ __set_bit(si->key_code[i], ts->input->keybit);
++
++ return cyttsp5_setup_input_device(dev);
++}
++
++static int cyttsp5_i2c_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
++{
++ struct regmap *regmap;
++ static const struct regmap_config config = {
++ .reg_bits = 8,
++ .val_bits = 8,
++ };
++
++ regmap = devm_regmap_init_i2c(client, &config);
++ if (IS_ERR(regmap)) {
++ dev_err(&client->dev, "regmap allocation failed: %ld\n",
++ PTR_ERR(regmap));
++ return PTR_ERR(regmap);
++ }
++
++ return cyttsp5_probe(&client->dev, regmap, client->irq, client->name);
++}
++
++static const struct of_device_id cyttsp5_of_match[] = {
++ { .compatible = "cypress,tt21000", },
++ { }
++};
++MODULE_DEVICE_TABLE(of, cyttsp5_of_match);
++
++static const struct i2c_device_id cyttsp5_i2c_id[] = {
++ { CYTTSP5_NAME, 0, },
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, cyttsp5_i2c_id);
++
++static struct i2c_driver cyttsp5_i2c_driver = {
++ .driver = {
++ .name = CYTTSP5_NAME,
++ .of_match_table = cyttsp5_of_match,
++ },
++ .probe = cyttsp5_i2c_probe,
++ .id_table = cyttsp5_i2c_id,
++};
++module_i2c_driver(cyttsp5_i2c_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Touchscreen driver for Cypress TrueTouch Gen 5 Product");
++MODULE_AUTHOR("Mylène Josserand <mylene.josserand@bootlin.com>");
+--
+GitLab
+
diff --git a/gnu/packages/patches/linux-libre-arm64-pinenote-touchscreen-2.patch b/gnu/packages/patches/linux-libre-arm64-pinenote-touchscreen-2.patch
new file mode 100644
index 0000000000..9ccef92ad7
--- /dev/null
+++ b/gnu/packages/patches/linux-libre-arm64-pinenote-touchscreen-2.patch
@@ -0,0 +1,108 @@
+From d6bb8a6b5a5210fea70bc590350bfca3a9e3a7a2 Mon Sep 17 00:00:00 2001
+From: Peter Geis <pgwipeout@gmail.com>
+Date: Sat, 15 Jan 2022 21:50:45 -0500
+Subject: [PATCH] Input: cyttsp5: support touchscreen device tree overrides
+
+It is possible for the cyttsp5 chip to not have a configuration burned
+to it.
+This leads to a sitatuion where all calibration values return zero,
+leading to a broken touchscreen configuration.
+
+The current driver does not support utilizing overrides from the device
+tree.
+Extend the driver to support this, and permit it to do some basic sanity
+checking of the values for the touchscreen and abort if they are
+invalid.
+
+Signed-off-by: Peter Geis <pgwipeout@gmail.com>
+---
+ drivers/input/touchscreen/cyttsp5.c | 62 ++++++++++++++++++++++++++---
+ 1 file changed, 57 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/input/touchscreen/cyttsp5.c b/drivers/input/touchscreen/cyttsp5.c
+index 3ac45108090c..e837985d199a 100644
+--- a/drivers/input/touchscreen/cyttsp5.c
++++ b/drivers/input/touchscreen/cyttsp5.c
+@@ -507,15 +507,66 @@ static int cyttsp5_get_sysinfo_regs(struct cyttsp5 *ts)
+ struct cyttsp5_sensing_conf_data_dev *scd_dev =
+ (struct cyttsp5_sensing_conf_data_dev *)
+ &ts->response_buf[HID_SYSINFO_SENSING_OFFSET];
++ u32 tmp;
+
+ cyttsp5_si_get_btn_data(ts);
+
+ scd->max_tch = scd_dev->max_num_of_tch_per_refresh_cycle;
+- scd->res_x = get_unaligned_le16(&scd_dev->res_x);
+- scd->res_y = get_unaligned_le16(&scd_dev->res_y);
+- scd->max_z = get_unaligned_le16(&scd_dev->max_z);
+- scd->len_x = get_unaligned_le16(&scd_dev->len_x);
+- scd->len_y = get_unaligned_le16(&scd_dev->len_y);
++
++ if (scd->max_tch == 0) {
++ dev_dbg(ts->dev, "Max touch points cannot be zero\n");
++ scd->max_tch = 2;
++ }
++
++ if(device_property_read_u32(ts->dev, "touchscreen-size-x", &tmp))
++ scd->res_x = get_unaligned_le16(&scd_dev->res_x);
++ else
++ scd->res_x = tmp;
++
++ if (scd->res_x == 0) {
++ dev_err(ts->dev, "ABS_X cannot be zero\n");
++ return -ENODATA;
++ }
++
++ if(device_property_read_u32(ts->dev, "touchscreen-size-y", &tmp))
++ scd->res_y = get_unaligned_le16(&scd_dev->res_y);
++ else
++ scd->res_y = tmp;
++
++ if (scd->res_y == 0) {
++ dev_err(ts->dev, "ABS_Y cannot be zero\n");
++ return -ENODATA;
++ }
++
++ if(device_property_read_u32(ts->dev, "touchscreen-max-pressure", &tmp))
++ scd->max_z = get_unaligned_le16(&scd_dev->max_z);
++ else
++ scd->max_z = tmp;
++
++ if (scd->max_z == 0) {
++ dev_err(ts->dev, "ABS_PRESSURE cannot be zero\n");
++ return -ENODATA;
++ }
++
++ if(device_property_read_u32(ts->dev, "touchscreen-x-mm", &tmp))
++ scd->len_x = get_unaligned_le16(&scd_dev->len_x);
++ else
++ scd->len_x = tmp;
++
++ if (scd->len_x == 0) {
++ dev_dbg(ts->dev, "Touchscreen size x cannot be zero\n");
++ scd->len_x = scd->res_x + 1;
++ }
++
++ if(device_property_read_u32(ts->dev, "touchscreen-y-mm", &tmp))
++ scd->len_y = get_unaligned_le16(&scd_dev->len_y);
++ else
++ scd->len_y = tmp;
++
++ if (scd->len_y == 0) {
++ dev_dbg(ts->dev, "Touchscreen size y cannot be zero\n");
++ scd->len_y = scd->res_y + 1;
++ }
+
+ return 0;
+ }
+@@ -877,6 +928,7 @@ static int cyttsp5_i2c_probe(struct i2c_client *client,
+
+ static const struct of_device_id cyttsp5_of_match[] = {
+ { .compatible = "cypress,tt21000", },
++ { .compatible = "cypress,tma448", },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, cyttsp5_of_match);
+--
+GitLab
+
diff --git a/gnu/packages/patches/linux-libre-arm64-rockchip-add-hdmi-sound.patch b/gnu/packages/patches/linux-libre-arm64-rockchip-add-hdmi-sound.patch
new file mode 100644
index 0000000000..8479c1ee11
--- /dev/null
+++ b/gnu/packages/patches/linux-libre-arm64-rockchip-add-hdmi-sound.patch
@@ -0,0 +1,40 @@
+From 41b224b927be41acb19b1af869394a62bc686b92 Mon Sep 17 00:00:00 2001
+From: Dan Johansen <strit@manjaro.org>
+Date: Fri, 10 Jun 2022 16:13:23 +0200
+Subject: [PATCH 1/2] arm64: dts: rockchip: Add HDMI sound node to Quartz64-B
+
+Signed-off-by: Dan Johansen <strit@manjaro.org>
+---
+ arch/arm64/boot/dts/rockchip/rk3566-quartz64-b.dts | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-b.dts b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-b.dts
+index 8d05d8a44699..f7a70470a90e 100644
+--- a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-b.dts
++++ b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-b.dts
+@@ -203,6 +203,10 @@ hdmi_out_con: endpoint {
+ };
+ };
+
++&hdmi_sound {
++ status = "okay";
++};
++
+ &i2c0 {
+ status = "okay";
+
+@@ -478,6 +482,10 @@ &i2c5 {
+ status = "disabled";
+ };
+
++&i2s0_8ch {
++ status = "okay";
++};
++
+ &mdio1 {
+ rgmii_phy1: ethernet-phy@1 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+--
+2.36.1
+
+
--
2.36.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: Building linux and u-boot for aarch64
2022-06-30 11:20 Building linux and u-boot for aarch64 phodina via
@ 2022-06-30 11:57 ` phodina
2022-06-30 19:04 ` linux and u-boot for pinenote-rk3568 Vagrant Cascadian
1 sibling, 0 replies; 5+ messages in thread
From: phodina @ 2022-06-30 11:57 UTC (permalink / raw)
To: phodina; +Cc: help-guix, Mathieu Othacehe, janneke@gnu.org
[-- Attachment #1: Type: text/plain, Size: 28 bytes --]
And the build log is here.
[-- Attachment #2: linux-libre-5.17.15-guix.tar.xz.drv.gz --]
[-- Type: application/gzip, Size: 436311 bytes --]
^ permalink raw reply [flat|nested] 5+ messages in thread
* linux and u-boot for pinenote-rk3568
2022-06-30 11:20 Building linux and u-boot for aarch64 phodina via
2022-06-30 11:57 ` phodina
@ 2022-06-30 19:04 ` Vagrant Cascadian
2022-06-30 19:58 ` Vagrant Cascadian
2022-07-01 20:42 ` phodina
1 sibling, 2 replies; 5+ messages in thread
From: Vagrant Cascadian @ 2022-06-30 19:04 UTC (permalink / raw)
To: phodina, help-guix; +Cc: Mathieu Othacehe, janneke@gnu.org
[-- Attachment #1: Type: text/plain, Size: 10046 bytes --]
On 2022-06-30, phodina via wrote:
> I'm attempting to build the Linux kernel and u-boot for ARM64 target -
> eink tablet.
I can infer what you're talking about from the later patches, but it
would be good to mention what specific hardware you're talking about up
front and/or in the subject. :)
> Unfotutnately the patches are not yet in the upstream so I had to
> tweak little bit in the gnu/packages/linux.scm and
> gnu/bootloader/bootloaders.scm files.
Had to do similar things early on to get the pinebook-pro support
available in guix before it was fully upstream... wheee!
A partial review with comments inline follows...
> Subject: [PATCH 4/7] gnu: Provide private implementation with selection of the
> u-boot package.
>
> * gnu/packages/bootloaders.scm: Provide private implementation of
> make-u-boot-package and make-u-boot-sunxi64-package so that the public API
> does not change. The private allows to specify the u-boot package.
This seems unecessarily complicated; you could just use a different
source for the eventual u-boot-pinenote* packages rather than renaming
existing functions and/or adding new ones... see what
u-boot-nintendo-nes-classic-edition does, mentioned below...
> From 44b1385e6dbcc79bafebddc7699ed302fcb5fe91 Mon Sep 17 00:00:00 2001
> From: Petr Hodina <phodina@protonmail.com>
> Date: Thu, 30 Jun 2022 11:20:55 +0200
> Subject: [PATCH 6/7] gnu: Add install-pinenote-rk3568-u-boot and
> u-boot-pinenote-rk3568-bootloader.
>
> * gnu/bootloader/u-boot.scm (install-pinenote-rk3568-u-boot,
> u-boot-pinenote-rk3568-bootloader): New variables.
The patch order is wrong, this is patch 6/7 followed by patch 5/7
... sometimes the order doesn't matter, but this order was actually a
bit confusing for me. Please submit patches in the appropriate order. :)
> diff --git a/gnu/bootloader/u-boot.scm b/gnu/bootloader/u-boot.scm
> index 6cad33b741..4410cc497a 100644
> --- a/gnu/bootloader/u-boot.scm
> +++ b/gnu/bootloader/u-boot.scm
...
> @@ -127,6 +129,15 @@ (define install-rockpro64-rk3399-u-boot
>
> (define install-pinebook-pro-rk3399-u-boot install-rockpro64-rk3399-u-boot)
>
> +;; TODO: Supply correct offsets
> +(define install-pinenote-rk3568-u-boot
> + #~(lambda (bootloader root-index image)
> + (let ((idb (string-append bootloader "/libexec/idbloader.img"))
> + (u-boot (string-append bootloader "/libexec/u-boot.itb")))
> + (write-file-on-device idb (stat:size (stat idb))
> + image (* 64 512))
> + (write-file-on-device u-boot (stat:size (stat u-boot))
> + image (* 16384 512)))))
Does it really use different offsets than other rockchip platforms, or
is that just a note to confirm if it does?
> From 4d53d2bdb8526f72ed6cd3183ff2a2141990c900 Mon Sep 17 00:00:00 2001
> From: Petr Hodina <phodina@protonmail.com>
> Date: Thu, 30 Jun 2022 11:19:33 +0200
> Subject: [PATCH 5/7] gnu: Add u-boot-pinenote-rk3568.
>
> * gnu/packages/bootloaders.scm (u-boot-pinenote-rk3568): New variable.
>
> diff --git a/gnu/packages/bootloaders.scm b/gnu/packages/bootloaders.scm
> index cab97852f1..9c266b7bed 100644
> --- a/gnu/packages/bootloaders.scm
> +++ b/gnu/packages/bootloaders.scm
> @@ -834,6 +834,9 @@ (define-public u-boot-pine64-plus
> (define-public u-boot-pine64-lts
> (make-u-boot-sunxi64-package "pine64-lts" "aarch64-linux-gnu"))
>
> +(define-public u-boot-pinenote-rk3568
> + (make-u-boot-sunxi64-package-priv "pinenote" "aarch64-linux-gnu" u-boot-pinenote))
> +
> (define-public u-boot-pinebook
> (let ((base (make-u-boot-sunxi64-package "pinebook" "aarch64-linux-gnu")))
> (package
Definitely not u-boot-sunxi64-package. Probably more similar to the
u-boot-pinebook-pro-rk3399 or u-boot-rock64-rk3328, which i think use
the standard make-u-boot-package.
For using an entirely different source repository for a specific
package, see the u-boot-nintendo-nes-classic-edition and how that uses a
different source, e.g.
(define-public u-boot-nintendo-nes-classic-edition
(let ((base (make-u-boot-package "Nintendo_NES_Classic_Edition"
"arm-linux-gnueabihf")))
(package
(inherit base)
;; Starting with 2019.01, FEL doesn't work anymore on A33.
(version "2018.11")
(source (origin
...
> From a6499c4c384dd3c0e06968cc6987da0e47af6290 Mon Sep 17 00:00:00 2001
> From: Petr Hodina <phodina@protonmail.com>
> Date: Thu, 30 Jun 2022 10:12:39 +0200
> Subject: [PATCH 1/7] gnu: Add linux-libre-arm64-pinenote.
>
> * gnu/packages/linux.scm (linux-libre-arm64-pinenote): New variable.
> * gnu/local.mk: Add patches.
> * linux-libre-arm64-pinenote-battery-level.patch: New file.
> * linux-libre-arm64-pinenote-defconfig.patch: New file.
> * linux-libre-arm64-pinenote-dtsi.patch: New file.
> * linux-libre-arm64-pinenote-ebc-patches.patch: New file.
> * linux-libre-arm64-pinenote-touchscreen-1.patch: New file.
> * linux-libre-arm64-pinenote-touchscreen-2.patch: New file.
> * linux-libre-arm64-rockchip-add-hdmi-sound.patch: New file.
I'm going to make some comments on this patch, but I get plenty confused
with patches in gnu/packages/linux.scm as it can be a little more
complicated than your typical packaging, so any other reviewers really
ought to jump in too! :)
> diff --git a/gnu/local.mk b/gnu/local.mk
> index 353b91cfd2..c19f1f418b 100644
> --- a/gnu/local.mk
> +++ b/gnu/local.mk
> @@ -1444,6 +1444,13 @@ dist_patch_DATA = \
> %D%/packages/patches/linbox-fix-pkgconfig.patch \
> %D%/packages/patches/linphone-desktop-without-sdk.patch \
> %D%/packages/patches/linux-libre-support-for-Pinebook-Pro.patch \
> + %D%/packages/patches/linux-libre-arm64-pinenote-touchscreen-1.patch \
> + %D%/packages/patches/linux-libre-arm64-pinenote-touchscreen-2.patch \
> + %D%/packages/patches/linux-libre-arm64-pinenote-battery-level.patch \
> + %D%/packages/patches/linux-libre-arm64-rockchip-add-hdmi-sound.patch \
> + %D%/packages/patches/linux-libre-arm64-pinenote-defconfig.patch \
> + %D%/packages/patches/linux-libre-arm64-pinenote-dtsi.patch \
whitespace inconsistancy on the previous few lines...
> diff --git a/gnu/packages/linux.scm b/gnu/packages/linux.scm
> index 8f7b4f4f5b..bae8ffb959 100644
> --- a/gnu/packages/linux.scm
> +++ b/gnu/packages/linux.scm
> @@ -349,6 +349,12 @@ (define (%upstream-linux-source version hash)
> "linux-" version ".tar.xz"))
> (sha256 hash)))
>
> +(define (%pinenote-linux-source version hash)
> + (origin
> + (method url-fetch)
> + (uri (string-append "https://github.com/phodina/linux-pinenote/tarball/" version))
> + (sha256 hash)))
> +
I think tarballs from github are generally discouraged; although with
the kernel that can be quite a large git checkout... but...
> ;; The current "stable" kernels. That is, the most recently released major
> ;; versions that are still supported upstream.
>
> @@ -367,6 +373,14 @@ (define-public linux-libre-5.17-pristine-source
> (%upstream-linux-source version hash)
> deblob-scripts-5.17)))
>
> +(define-public linux-libre-arm64-pinenote-pristine-source
> + (let ((version linux-libre-5.17-version)
> + (commit "c91a48e028fe1f6a0e5748fd87c446aa7e31811b")
> + (hash (base32 "1xwyvvps1r3zl1n9szlgrj8ylw5sgj6fr52fig9f2cc6ai331bbn")))
> + (make-linux-libre-source version
> + (%pinenote-linux-source commit hash)
> + deblob-scripts-5.17)))
> +
Oh, wait, you do pull from git? Is the tarball used, or the git
checkout? Seems like you shouldn't need both.
> +(define-public linux-libre-arm64-pinenote-source
> + (source-with-patches linux-libre-arm64-pinenote-pristine-source
> + (list %boot-logo-patch
> + %linux-libre-arm-export-__sync_icache_dcache-patch
> + (search-patch
> +"linux-libre-arm64-pinenote-touchscreen-1.patch")
> + (search-patch
> +"linux-libre-arm64-pinenote-touchscreen-2.patch")
> + (search-patch
> +"linux-libre-arm64-pinenote-battery-level.patch")
> + (search-patch
> +"linux-libre-arm64-rockchip-add-hdmi-sound.patch")
> + (search-patch
> +"linux-libre-arm64-pinenote-defconfig.patch")
> + (search-patch
> +"linux-libre-arm64-pinenote-dtsi.patch")
> + (search-patch
> +"linux-libre-arm64-pinenote-ebc-patches.patch"))))
> +
Maybe use:
(search-patches "A.patch"
"B.patch"
...
"N.patch")
Though you may have to append two lists together for that to work. As to
how... ask someone who understands guile. :)
Also wondering if there is a branch with all the patches you need
already applied, since you're using a separate source origin already...
> +(define-public linux-libre-arm64-pinenote
> + (make-linux-libre* linux-libre-version
> + linux-libre-gnu-revision
> + linux-libre-arm64-pinenote-source
> + '("aarch64-linux")
> + #:defconfig "pinenote_defconfig"
> + #:extra-options
> + %default-extra-linux-options))
You might want to not assume to use the same versions as
(linux-libre-version, linux-libre-gnu-revision), as they might change
out from under you... those were recently updated to 5.18.x ... instead
just pick the versions relevent to the upstream source.
Hope that's helpful, good luck and thanks for working on adding support
for this very interesting platform!
live well,
vagrant
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 227 bytes --]
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: linux and u-boot for pinenote-rk3568
2022-06-30 19:04 ` linux and u-boot for pinenote-rk3568 Vagrant Cascadian
@ 2022-06-30 19:58 ` Vagrant Cascadian
2022-07-01 20:42 ` phodina
1 sibling, 0 replies; 5+ messages in thread
From: Vagrant Cascadian @ 2022-06-30 19:58 UTC (permalink / raw)
To: phodina, help-guix; +Cc: Mathieu Othacehe, janneke@gnu.org
[-- Attachment #1: Type: text/plain, Size: 1890 bytes --]
On 2022-06-30, Vagrant Cascadian wrote:
> On 2022-06-30, phodina via wrote:
>> From 4d53d2bdb8526f72ed6cd3183ff2a2141990c900 Mon Sep 17 00:00:00 2001
>> From: Petr Hodina <phodina@protonmail.com>
>> Date: Thu, 30 Jun 2022 11:19:33 +0200
>> Subject: [PATCH 5/7] gnu: Add u-boot-pinenote-rk3568.
>>
>> * gnu/packages/bootloaders.scm (u-boot-pinenote-rk3568): New variable.
>>
>> diff --git a/gnu/packages/bootloaders.scm b/gnu/packages/bootloaders.scm
>> index cab97852f1..9c266b7bed 100644
>> --- a/gnu/packages/bootloaders.scm
>> +++ b/gnu/packages/bootloaders.scm
>> @@ -834,6 +834,9 @@ (define-public u-boot-pine64-plus
>> (define-public u-boot-pine64-lts
>> (make-u-boot-sunxi64-package "pine64-lts" "aarch64-linux-gnu"))
>>
>> +(define-public u-boot-pinenote-rk3568
>> + (make-u-boot-sunxi64-package-priv "pinenote" "aarch64-linux-gnu" u-boot-pinenote))
>> +
>> (define-public u-boot-pinebook
>> (let ((base (make-u-boot-sunxi64-package "pinebook" "aarch64-linux-gnu")))
>> (package
>
> Definitely not u-boot-sunxi64-package. Probably more similar to the
> u-boot-pinebook-pro-rk3399 or u-boot-rock64-rk3328, which i think use
> the standard make-u-boot-package.
>
> For using an entirely different source repository for a specific
> package, see the u-boot-nintendo-nes-classic-edition and how that uses a
> different source, e.g.
>
> (define-public u-boot-nintendo-nes-classic-edition
> (let ((base (make-u-boot-package "Nintendo_NES_Classic_Edition"
> "arm-linux-gnueabihf")))
> (package
> (inherit base)
> ;; Starting with 2019.01, FEL doesn't work anymore on A33.
> (version "2018.11")
> (source (origin
> ...
You also almost certainly need an arm-trusted-firmware for rk3568, which
is not yet supported upstream.
live well,
vagrant
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 227 bytes --]
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: linux and u-boot for pinenote-rk3568
2022-06-30 19:04 ` linux and u-boot for pinenote-rk3568 Vagrant Cascadian
2022-06-30 19:58 ` Vagrant Cascadian
@ 2022-07-01 20:42 ` phodina
1 sibling, 0 replies; 5+ messages in thread
From: phodina @ 2022-07-01 20:42 UTC (permalink / raw)
To: Vagrant Cascadian; +Cc: help-guix, Mathieu Othacehe, janneke@gnu.org
[-- Attachment #1: Type: text/plain, Size: 12300 bytes --]
Hi!
> On 2022-06-30, phodina via wrote:
>
> > I'm attempting to build the Linux kernel and u-boot for ARM64 target -
> > eink tablet.
>
>
> I can infer what you're talking about from the later patches, but it
> would be good to mention what specific hardware you're talking about up
> front and/or in the subject. :)
Sorry for the confusion. I should have stated it's Pinenote eink tablet directly.
>
> > Unfotutnately the patches are not yet in the upstream so I had to
> > tweak little bit in the gnu/packages/linux.scm and
> > gnu/bootloader/bootloaders.scm files.
>
>
> Had to do similar things early on to get the pinebook-pro support
> available in guix before it was fully upstream... wheee!
Thanks! I do have Guix running on Pinebook Pro laptop so I'd CC you in the help :-)
>
>
> A partial review with comments inline follows...
>
> > Subject: [PATCH 4/7] gnu: Provide private implementation with selection of the
> > u-boot package.
> > Sent with Proton Mail secure email.
------- Original Message -------
On Thursday, June 30th, 2022 at 9:04 PM, Vagrant Cascadian <vagrant@debian.org> wrote:
> > * gnu/packages/bootloaders.scm: Provide private implementation of
> > make-u-boot-package and make-u-boot-sunxi64-package so that the public API
> > does not change. The private allows to specify the u-boot package.
>
>
> This seems unecessarily complicated; you could just use a different
> source for the eventual u-boot-pinenote* packages rather than renaming
> existing functions and/or adding new ones... see what
> u-boot-nintendo-nes-classic-edition does, mentioned below...
Thanks for the hint. I had a look and reimplemented the u-boot. No there is no need to provide private implementation!
> > From 44b1385e6dbcc79bafebddc7699ed302fcb5fe91 Mon Sep 17 00:00:00 2001
> > From: Petr Hodina phodina@protonmail.com
> > Date: Thu, 30 Jun 2022 11:20:55 +0200
> > Subject: [PATCH 6/7] gnu: Add install-pinenote-rk3568-u-boot and
> > u-boot-pinenote-rk3568-bootloader.
> >
> > * gnu/bootloader/u-boot.scm (install-pinenote-rk3568-u-boot,
> > u-boot-pinenote-rk3568-bootloader): New variables.
>
>
> The patch order is wrong, this is patch 6/7 followed by patch 5/7
> ... sometimes the order doesn't matter, but this order was actually a
> bit confusing for me. Please submit patches in the appropriate order. :)
This was done by mistake as I build the system image in the last step so the order didn't matter. It's fixed in the new patch set.
>
> > diff --git a/gnu/bootloader/u-boot.scm b/gnu/bootloader/u-boot.scm
> > index 6cad33b741..4410cc497a 100644
> > --- a/gnu/bootloader/u-boot.scm
> > +++ b/gnu/bootloader/u-boot.scm
>
> ...
>
> > @@ -127,6 +129,15 @@ (define install-rockpro64-rk3399-u-boot
> >
> > (define install-pinebook-pro-rk3399-u-boot install-rockpro64-rk3399-u-boot)
> >
> > +;; TODO: Supply correct offsets
> > +(define install-pinenote-rk3568-u-boot
> > + #~(lambda (bootloader root-index image)
> > + (let ((idb (string-append bootloader "/libexec/idbloader.img"))
> > + (u-boot (string-append bootloader "/libexec/u-boot.itb")))
> > + (write-file-on-device idb (stat:size (stat idb))
> > + image (* 64 512))
> > + (write-file-on-device u-boot (stat:size (stat u-boot))
> > + image (* 16384 512)))))
>
>
> Does it really use different offsets than other rockchip platforms, or
> is that just a note to confirm if it does?
The TODO note is here more as a warning as I do put it there as a check.
Slight issue here is that the ATF is not released so even though I have gone through some patches form downstream and applied them on the upstream I think it won't work.
Prefereably I'd like to use it on Quartz64 board which I now use as Guix Aarch64 native build machine before flashing to the Pinenote.
>
> > From 4d53d2bdb8526f72ed6cd3183ff2a2141990c900 Mon Sep 17 00:00:00 2001
> > From: Petr Hodina phodina@protonmail.com
> > Date: Thu, 30 Jun 2022 11:19:33 +0200
> > Subject: [PATCH 5/7] gnu: Add u-boot-pinenote-rk3568.
> >
> > * gnu/packages/bootloaders.scm (u-boot-pinenote-rk3568): New variable.
> >
> > diff --git a/gnu/packages/bootloaders.scm b/gnu/packages/bootloaders.scm
> > index cab97852f1..9c266b7bed 100644
> > --- a/gnu/packages/bootloaders.scm
> > +++ b/gnu/packages/bootloaders.scm
> > @@ -834,6 +834,9 @@ (define-public u-boot-pine64-plus
> > (define-public u-boot-pine64-lts
> > (make-u-boot-sunxi64-package "pine64-lts" "aarch64-linux-gnu"))
> >
> > +(define-public u-boot-pinenote-rk3568
> > + (make-u-boot-sunxi64-package-priv "pinenote" "aarch64-linux-gnu" u-boot-pinenote))
> > +
> > (define-public u-boot-pinebook
> > (let ((base (make-u-boot-sunxi64-package "pinebook" "aarch64-linux-gnu")))
> > (package
>
>
> Definitely not u-boot-sunxi64-package. Probably more similar to the
> u-boot-pinebook-pro-rk3399 or u-boot-rock64-rk3328, which i think use
> the standard make-u-boot-package.
I've applied the change. Haven't built the downstream u-boot yet but I'll check the dump from Pinenote.
>
> For using an entirely different source repository for a specific
> package, see the u-boot-nintendo-nes-classic-edition and how that uses a
> different source, e.g.
>
> (define-public u-boot-nintendo-nes-classic-edition
> (let ((base (make-u-boot-package "Nintendo_NES_Classic_Edition"
> "arm-linux-gnueabihf")))
> (package
> (inherit base)
> ;; Starting with 2019.01, FEL doesn't work anymore on A33.
> (version "2018.11")
> (source (origin
> ...
>
> > From a6499c4c384dd3c0e06968cc6987da0e47af6290 Mon Sep 17 00:00:00 2001
> > From: Petr Hodina phodina@protonmail.com
> > Date: Thu, 30 Jun 2022 10:12:39 +0200
> > Subject: [PATCH 1/7] gnu: Add linux-libre-arm64-pinenote.
> >
> > * gnu/packages/linux.scm (linux-libre-arm64-pinenote): New variable.
> > * gnu/local.mk: Add patches.
> > * linux-libre-arm64-pinenote-battery-level.patch: New file.
> > * linux-libre-arm64-pinenote-defconfig.patch: New file.
> > * linux-libre-arm64-pinenote-dtsi.patch: New file.
> > * linux-libre-arm64-pinenote-ebc-patches.patch: New file.
> > * linux-libre-arm64-pinenote-touchscreen-1.patch: New file.
> > * linux-libre-arm64-pinenote-touchscreen-2.patch: New file.
> > * linux-libre-arm64-rockchip-add-hdmi-sound.patch: New file.
>
>
> I'm going to make some comments on this patch, but I get plenty confused
> with patches in gnu/packages/linux.scm as it can be a little more
> complicated than your typical packaging, so any other reviewers really
> ought to jump in too! :)
I've removed some patches as they are not for this kernel release and will need more polishing.
>
> > diff --git a/gnu/local.mk b/gnu/local.mk
> > index 353b91cfd2..c19f1f418b 100644
> > --- a/gnu/local.mk
> > +++ b/gnu/local.mk
> > @@ -1444,6 +1444,13 @@ dist_patch_DATA = \
> > %D%/packages/patches/linbox-fix-pkgconfig.patch \
> > %D%/packages/patches/linphone-desktop-without-sdk.patch \
> > %D%/packages/patches/linux-libre-support-for-Pinebook-Pro.patch \
> > + %D%/packages/patches/linux-libre-arm64-pinenote-touchscreen-1.patch \
> > + %D%/packages/patches/linux-libre-arm64-pinenote-touchscreen-2.patch \
> > + %D%/packages/patches/linux-libre-arm64-pinenote-battery-level.patch \
> > + %D%/packages/patches/linux-libre-arm64-rockchip-add-hdmi-sound.patch \
> > + %D%/packages/patches/linux-libre-arm64-pinenote-defconfig.patch \
> > + %D%/packages/patches/linux-libre-arm64-pinenote-dtsi.patch \
>
>
> whitespace inconsistancy on the previous few lines...
It was meant more for the process of porting rather than packing it into Guix atm so there inconsistencies are there unfortunately now.
>
> > diff --git a/gnu/packages/linux.scm b/gnu/packages/linux.scm
> > index 8f7b4f4f5b..bae8ffb959 100644
> > --- a/gnu/packages/linux.scm
> > +++ b/gnu/packages/linux.scm
> > @@ -349,6 +349,12 @@ (define (%upstream-linux-source version hash)
> > "linux-" version ".tar.xz"))
> > (sha256 hash)))
> >
> > +(define (%pinenote-linux-source version hash)
> > + (origin
> > + (method url-fetch)
> > + (uri (string-append "https://github.com/phodina/linux-pinenote/tarball/" version))
> > + (sha256 hash)))
> > +
>
>
> I think tarballs from github are generally discouraged; although with
> the kernel that can be quite a large git checkout... but...
>
> > ;; The current "stable" kernels. That is, the most recently released major
> > ;; versions that are still supported upstream.
The tarball is smaller than the git checkout. Now there are defines for the exact kernel version.
> >
> > @@ -367,6 +373,14 @@ (define-public linux-libre-5.17-pristine-source
> > (%upstream-linux-source version hash)
> > deblob-scripts-5.17)))
> >
> > +(define-public linux-libre-arm64-pinenote-pristine-source
> > + (let ((version linux-libre-5.17-version)
> > + (commit "c91a48e028fe1f6a0e5748fd87c446aa7e31811b")
> > + (hash (base32 "1xwyvvps1r3zl1n9szlgrj8ylw5sgj6fr52fig9f2cc6ai331bbn")))
> > + (make-linux-libre-source version
> > + (%pinenote-linux-source commit hash)
> > + deblob-scripts-5.17)))
> > +
>
>
> Oh, wait, you do pull from git? Is the tarball used, or the git
> checkout? Seems like you shouldn't need both.
I do download the tarball just specify the exact commit rather than to say the name of branch as that can change and break the build due to sha has mismatch.
>
> > +(define-public linux-libre-arm64-pinenote-source
> > + (source-with-patches linux-libre-arm64-pinenote-pristine-source
> > + (list %boot-logo-patch
> > + %linux-libre-arm-export-__sync_icache_dcache-patch
> > + (search-patch
> > +"linux-libre-arm64-pinenote-touchscreen-1.patch")
> > + (search-patch
> > +"linux-libre-arm64-pinenote-touchscreen-2.patch")
> > + (search-patch
> > +"linux-libre-arm64-pinenote-battery-level.patch")
> > + (search-patch
> > +"linux-libre-arm64-rockchip-add-hdmi-sound.patch")
> > + (search-patch
> > +"linux-libre-arm64-pinenote-defconfig.patch")
> > + (search-patch
> > +"linux-libre-arm64-pinenote-dtsi.patch")
> > + (search-patch
> > +"linux-libre-arm64-pinenote-ebc-patches.patch"))))
> > +
>
>
> Maybe use:
>
> (search-patches "A.patch"
> "B.patch"
> ...
> "N.patch")
>
> Though you may have to append two lists together for that to work. As to
> how... ask someone who understands guile. :)
That's what I attempted but failed. Now it's fixed.
>
> Also wondering if there is a branch with all the patches you need
> already applied, since you're using a separate source origin already...
Well, I could apply them to the kernel fork. I'll open the issue on the github to discuss them.
>
> > +(define-public linux-libre-arm64-pinenote
> > + (make-linux-libre* linux-libre-version
> > + linux-libre-gnu-revision
> > + linux-libre-arm64-pinenote-source
> > + '("aarch64-linux")
> > + #:defconfig "pinenote_defconfig"
> > + #:extra-options
> > + %default-extra-linux-options))
>
>
> You might want to not assume to use the same versions as
> (linux-libre-version, linux-libre-gnu-revision), as they might change
> out from under you... those were recently updated to 5.18.x ... instead
> just pick the versions relevent to the upstream source.
>
>
> Hope that's helpful, good luck and thanks for working on adding support
> for this very interesting platform!
Thanks Vagrant for the support and help. It now builds the system image. It's still not something that will work but we're getting there.
In the original email I attached the build log. The deblobing the kernel sources still fails and therefore I commented out now the list of the blobs. However, I'd like remove this modification. I assume it fails due to some assembly file which is neccessary for the eink driver to work.
Is there a way how to exclude it? It's in assembly so the source code is there (not machine code but also not that readable, there are some attempts to replace it). Also it's little bit pointless to remove it if the device will loose the display :-D
Also how do I specify the system image format so I can that I get the bootloader, kernel, initramfs and the rootfs on separate partitions that can be later flashed to the board using the rkdeveloptool?
----
Petr
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: v2-0003-gnu-Add-u-boot-pinenote-rk3568.patch --]
[-- Type: text/x-patch; name=v2-0003-gnu-Add-u-boot-pinenote-rk3568.patch, Size: 1303 bytes --]
From 5b7cd86dd2fb82af7a01c00c686fbc76e0622d9a Mon Sep 17 00:00:00 2001
From: Petr Hodina <phodina@protonmail.com>
Date: Fri, 1 Jul 2022 12:14:18 +0200
Subject: [PATCH v2 3/5] gnu: Add u-boot-pinenote-rk3568.
* gnu/packages/bootloaders.scm (u-boot-pinenote-rk3568): New variable.
diff --git a/gnu/packages/bootloaders.scm b/gnu/packages/bootloaders.scm
index 9f5e26fd28..8d50a7e481 100644
--- a/gnu/packages/bootloaders.scm
+++ b/gnu/packages/bootloaders.scm
@@ -814,6 +814,21 @@ (define-public u-boot-pine64-plus
(define-public u-boot-pine64-lts
(make-u-boot-sunxi64-package "pine64-lts" "aarch64-linux-gnu"))
+(define-public u-boot-pinenote-rk3568
+ (let ((base (make-u-boot-package "pinenote"
+ "aarch64-linux-gnu")))
+ (package
+ (inherit base)
+ (version "2022.07")
+ (source (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://gitlab.com/phodina/u-boot-quartz64")
+ (commit "quartz64")))
+ (sha256
+ (base32
+ "2l1w13dznj0z1ibqv2d6ljx2ma1gnf5x5ay3dqkqwxr6750nbq38")))))))
+
(define-public u-boot-pinebook
(let ((base (make-u-boot-sunxi64-package "pinebook" "aarch64-linux-gnu")))
(package
--
2.36.1
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: v2-0005-gnu-Add-image-for-pinenote.patch --]
[-- Type: text/x-patch; name=v2-0005-gnu-Add-image-for-pinenote.patch, Size: 3330 bytes --]
From d9e747100d29e7183d18d1016b35ad5b7c3e54d6 Mon Sep 17 00:00:00 2001
From: Petr Hodina <phodina@protonmail.com>
Date: Thu, 30 Jun 2022 11:22:05 +0200
Subject: [PATCH v2 5/5] gnu: Add image for pinenote.
* gnu/system/images/pinenote.scm: New file.
diff --git a/gnu/system/images/pinenote.scm b/gnu/system/images/pinenote.scm
new file mode 100644
index 0000000000..fe021d968e
--- /dev/null
+++ b/gnu/system/images/pinenote.scm
@@ -0,0 +1,74 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Petr Hodina <phodina@protonmail.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix 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 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu system images pinenote)
+ #:use-module (gnu bootloader)
+ #:use-module (gnu bootloader u-boot)
+ #:use-module (gnu image)
+ #:use-module (gnu packages linux)
+ #:use-module (guix platforms arm)
+ #:use-module (gnu services)
+ #:use-module (gnu services base)
+ #:use-module (gnu system)
+ #:use-module (gnu system file-systems)
+ #:use-module (gnu system image)
+ #:use-module (srfi srfi-26)
+ #:export (pinenote-barebones-os
+ pinenote-image-type
+ pinenote-raw-image))
+
+(define pinenote-barebones-os
+ (operating-system
+ (host-name "eink")
+ (timezone "Europe/Prague")
+ (locale "en_US.utf8")
+ (bootloader (bootloader-configuration
+ (bootloader u-boot-pinebook-pro-rk3399-bootloader)
+; (bootloader u-boot-pinenote-rk3568-bootloader)
+ (targets '("/dev/vda"))))
+ (initrd-modules '())
+ (kernel linux-libre-arm64-pinenote)
+ (file-systems (cons (file-system
+ (device (file-system-label "my-root"))
+ (mount-point "/")
+ (type "ext4"))
+ %base-file-systems))
+ (services (cons (service agetty-service-type
+ (agetty-configuration
+ (extra-options '("-L")) ; no carrier detect
+ (baud-rate "1500000")
+ (term "vt100")
+ (tty "ttyS2")))
+ %base-services))))
+
+(define pinenote-image-type
+ (image-type
+ (name 'pinenote-pro-raw)
+ (constructor (cut image-with-os
+ (raw-with-offset-disk-image (* 9 (expt 2 20))) ;9MiB
+ <>))))
+
+(define pinenote-barebones-raw-image
+ (image
+ (inherit
+ (os+platform->image pinenote-barebones-os aarch64-linux
+ #:type pinenote-image-type))
+ (name 'pinenote-barebones-raw-image)))
+
+;; Return the default image.
+pinenote-barebones-raw-image
--
2.36.1
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: v2-0002-gnu-u-boot-Remove-input-labels.patch --]
[-- Type: text/x-patch; name=v2-0002-gnu-u-boot-Remove-input-labels.patch, Size: 1789 bytes --]
From 09e174b0fe1f94594bc8ab09b920bc32e716b6cc Mon Sep 17 00:00:00 2001
From: Petr Hodina <phodina@protonmail.com>
Date: Thu, 30 Jun 2022 11:14:25 +0200
Subject: [PATCH v2 2/5] gnu: u-boot: Remove input labels.
* packages/bootloaders.scm (u-boot): Remove input labels.
diff --git a/gnu/packages/bootloaders.scm b/gnu/packages/bootloaders.scm
index 71a10f54d5..9f5e26fd28 100644
--- a/gnu/packages/bootloaders.scm
+++ b/gnu/packages/bootloaders.scm
@@ -16,6 +16,7 @@
;;; Copyright © 2021 Vincent Legoll <vincent.legoll@gmail.com>
;;; Copyright © 2021 Brice Waegeneire <brice@waegenei.re>
;;; Copyright © 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>
+;;; Copyright © 2022 Petr Hodina <phodina@protonmail.com>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -545,20 +546,20 @@ (define u-boot
(base32
"1l5w13dznj0z1ibqv2d6ljx2ma1gnf5x5ay3dqkqwxr6750nbq38"))))
(native-inputs
- `(("bc" ,bc)
- ("bison" ,bison)
- ("dtc" ,dtc)
- ("gnutls" ,gnutls)
- ("flex" ,flex)
- ("lz4" ,lz4)
- ("tinfo" ,ncurses/tinfo)
- ("perl" ,perl)
- ("python" ,python)
- ("python-coverage" ,python-coverage)
- ("python-pycryptodomex" ,python-pycryptodomex)
- ("python-pytest" ,python-pytest)
- ("swig" ,swig)
- ("libuuid" ,util-linux "lib")))
+ (list bc
+ bison
+ dtc
+ gnutls
+ flex
+ lz4
+ ncurses/tinfo
+ perl
+ python
+ python-coverage
+ python-pycryptodomex
+ python-pytest
+ swig
+ `(,util-linux "lib")))
(build-system gnu-build-system)
(home-page "https://www.denx.de/wiki/U-Boot/")
(synopsis "ARM bootloader")
--
2.36.1
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #5: v2-0004-gnu-Add-install-pinenote-rk3568-u-boot-and-u-boot.patch --]
[-- Type: text/x-patch; name=v2-0004-gnu-Add-install-pinenote-rk3568-u-boot-and-u-boot.patch, Size: 2261 bytes --]
From 61dab769ff062fa69d94a4a126333f542489be52 Mon Sep 17 00:00:00 2001
From: Petr Hodina <phodina@protonmail.com>
Date: Thu, 30 Jun 2022 11:20:55 +0200
Subject: [PATCH v2 4/5] gnu: Add install-pinenote-rk3568-u-boot and
u-boot-pinenote-rk3568-bootloader.
* gnu/bootloader/u-boot.scm (install-pinenote-rk3568-u-boot,
u-boot-pinenote-rk3568-bootloader): New variables.
diff --git a/gnu/bootloader/u-boot.scm b/gnu/bootloader/u-boot.scm
index 6cad33b741..4410cc497a 100644
--- a/gnu/bootloader/u-boot.scm
+++ b/gnu/bootloader/u-boot.scm
@@ -3,6 +3,7 @@
;;; Copyright © 2017, 2019 Mathieu Othacehe <m.othacehe@gmail.com>
;;; Copyright © 2020 Julien Lepiller <julien@lepiller.eu>
;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
+;;; Copyright © 2022 Petr Hodina <phodina@protonmail.com>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -37,6 +38,7 @@ (define-module (gnu bootloader u-boot)
u-boot-novena-bootloader
u-boot-pine64-plus-bootloader
u-boot-pine64-lts-bootloader
+ u-boot-pinenote-rk3568-bootloader
u-boot-pinebook-bootloader
u-boot-pinebook-pro-rk3399-bootloader
u-boot-puma-rk3399-bootloader
@@ -127,6 +129,15 @@ (define install-rockpro64-rk3399-u-boot
(define install-pinebook-pro-rk3399-u-boot install-rockpro64-rk3399-u-boot)
+;; TODO: Supply correct offsets
+(define install-pinenote-rk3568-u-boot
+ #~(lambda (bootloader root-index image)
+ (let ((idb (string-append bootloader "/libexec/idbloader.img"))
+ (u-boot (string-append bootloader "/libexec/u-boot.itb")))
+ (write-file-on-device idb (stat:size (stat idb))
+ image (* 64 512))
+ (write-file-on-device u-boot (stat:size (stat u-boot))
+ image (* 16384 512)))))
\f
;;;
@@ -255,3 +266,9 @@ (define u-boot-pinebook-pro-rk3399-bootloader
(inherit u-boot-bootloader)
(package u-boot-pinebook-pro-rk3399)
(disk-image-installer install-pinebook-pro-rk3399-u-boot)))
+
+(define u-boot-pinenote-rk3568-bootloader
+ (bootloader
+ (inherit u-boot-bootloader)
+ (package u-boot-pinenote-rk3568)
+ (disk-image-installer install-pinenote-rk3568-u-boot)))
--
2.36.1
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #6: v2-0001-gnu-Add-linux-libre-arm64-pinenote.patch --]
[-- Type: text/x-patch; name=v2-0001-gnu-Add-linux-libre-arm64-pinenote.patch, Size: 155475 bytes --]
From c4085bb45fce17bcd55e6e49bd99f3543c116ab7 Mon Sep 17 00:00:00 2001
From: Petr Hodina <phodina@protonmail.com>
Date: Thu, 30 Jun 2022 10:12:39 +0200
Subject: [PATCH v2 1/5] gnu: Add linux-libre-arm64-pinenote.
* gnu/packages/linux.scm (linux-libre-arm64-pinenote): New variable.
* gnu/local.mk: Add patches.
* gnu/packages/patches/linux-libre-arm64-pinenote-defconfig.patch: New file.
* gnu/packages/patches/linux-libre-arm64-pinenote-touchscreen-1.patch: New file.
* gnu/packages/patches/linux-libre-arm64-pinenote-touchscreen-2.patch: New file.
diff --git a/gnu/local.mk b/gnu/local.mk
index 353b91cfd2..24d1660206 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1444,6 +1444,9 @@ dist_patch_DATA = \
%D%/packages/patches/linbox-fix-pkgconfig.patch \
%D%/packages/patches/linphone-desktop-without-sdk.patch \
%D%/packages/patches/linux-libre-support-for-Pinebook-Pro.patch \
+ %D%/packages/patches/linux-libre-arm64-pinenote-touchscreen-1.patch \
+ %D%/packages/patches/linux-libre-arm64-pinenote-touchscreen-2.patch \
+ %D%/packages/patches/linux-libre-arm64-pinenote-defconfig.patch \
%D%/packages/patches/linux-pam-no-setfsuid.patch \
%D%/packages/patches/linuxdcpp-openssl-1.1.patch \
%D%/packages/patches/lirc-localstatedir.patch \
diff --git a/gnu/packages/linux.scm b/gnu/packages/linux.scm
index 58d33140bd..b7b3a9f59a 100644
--- a/gnu/packages/linux.scm
+++ b/gnu/packages/linux.scm
@@ -60,7 +60,7 @@
;;; Copyright © 2021 Josselin Poiret <josselin.poiret@protonmail.ch>
;;; Copyright © 2021 Olivier Dion <olivier.dion@polymtl.ca>
;;; Copyright © 2021 Solene Rapenne <solene@perso.pw>
-;;; Copyright © 2021 Petr Hodina <phodina@protonmail.com>
+;;; Copyright © 2021, 2022 Petr Hodina <phodina@protonmail.com>
;;; Copyright © 2022 Artyom V. Poptsov <poptsov.artyom@gmail.com>
;;; Copyright © 2022 Rene Saavedra <nanuui@protonmail.com>
@@ -316,9 +316,9 @@ (define (make-linux-libre-source version
"--hard-dereference"
dir)
- (format #t "~%Scanning the generated tarball for blobs...~%")
- (invoke "/tmp/bin/deblob-check" "--use-awk" "--list-blobs"
- #$output))))))))))
+ ;(format #t "~%Scanning the generated tarball for blobs...~%")
+ ;(invoke "/tmp/bin/deblob-check" "--use-awk" "--list-blobs" #$output)
+ )))))))))
\f
;;;
@@ -349,6 +349,12 @@ (define (%upstream-linux-source version hash)
"linux-" version ".tar.xz"))
(sha256 hash)))
+(define (%pinenote-linux-source version hash)
+ (origin
+ (method url-fetch)
+ (uri (string-append "https://github.com/smaeul/linux/tarball/" version))
+ (sha256 hash)))
+
;; The current "stable" kernels. That is, the most recently released major
;; versions that are still supported upstream.
@@ -360,6 +366,7 @@ (define deblob-scripts-5.18
linux-libre-5.18-gnu-revision
(base32 "09aikdhij4d89wqd8mmkdr0nrfwqz6dx3n74qm6wx815rfngd2dz")
(base32 "0vjpn8iw9yg39sr6jfhzyvivf159h9zfgnjamwa283zfll0h0a53")))
+
(define-public linux-libre-5.18-pristine-source
(let ((version linux-libre-5.18-version)
(hash (base32 "0nsj44p1wn7ysckhv4a99ncj0a9xxhvi54v63w1047sspxjd18m1")))
@@ -367,6 +374,23 @@ (define-public linux-libre-5.18-pristine-source
(%upstream-linux-source version hash)
deblob-scripts-5.18)))
+(define-public linux-libre-5.16-version "5.16")
+(define-public linux-libre-5.16-gnu-revision "gnu")
+(define deblob-scripts-5.16
+ (linux-libre-deblob-scripts
+ linux-libre-5.16-version
+ linux-libre-5.16-gnu-revision
+ (base32 "0c9c8zd85p84r8k4xhys8xw15pds71v0ca2b6hm1pr4f6lpzck0g")
+ (base32 "0c5ld3ii3ixnr27sp59mbh40340jlmxaxk7z1xbl4v94mnzmwz3x")))
+
+(define-public linux-libre-arm64-pinenote-pristine-source
+ (let ((version linux-libre-5.16-version)
+ (commit "46e87f1f9c7dd22af26d99f60eb83d2cace43cb5")
+ (hash (base32 "1ymkgcq3ryf306hzyrm3nvh5cfaan77jkpd4b6q4z3zfp8qdyy1h")))
+ (make-linux-libre-source version
+ (%pinenote-linux-source commit hash)
+ deblob-scripts-5.16)))
+
;; The "longterm" kernels — the older releases with long-term upstream support.
;; Here are the support timelines:
;; <https://www.kernel.org/category/releases.html>
@@ -488,6 +512,15 @@ (define (source-with-patches source patches)
(patches (append (origin-patches source)
patches))))
+(define-public linux-libre-arm64-pinenote-source
+ (source-with-patches linux-libre-arm64-pinenote-pristine-source
+ (cons* %boot-logo-patch
+ %linux-libre-arm-export-__sync_icache_dcache-patch
+ (search-patches
+ "linux-libre-arm64-pinenote-touchscreen-1.patch"
+ "linux-libre-arm64-pinenote-touchscreen-2.patch"
+ "linux-libre-arm64-pinenote-defconfig.patch"))))
+
(define-public linux-libre-5.18-source
(source-with-patches linux-libre-5.18-pristine-source
(list %boot-logo-patch
@@ -1072,6 +1105,14 @@ (define-public linux-libre-arm-omap2plus-4.14
#:defconfig "omap2plus_defconfig"
#:extra-version "arm-omap2plus"))
+(define-public linux-libre-arm64-pinenote
+ (make-linux-libre* linux-libre-version
+ linux-libre-gnu-revision
+ linux-libre-arm64-pinenote-source
+ '("aarch64-linux")
+
+ #:defconfig "pinenote_defconfig"))
+
(define-public linux-libre-arm64-generic
(make-linux-libre* linux-libre-version
linux-libre-gnu-revision
diff --git a/gnu/packages/patches/linux-libre-arm64-pinenote-battery-level.patch b/gnu/packages/patches/linux-libre-arm64-pinenote-battery-level.patch
new file mode 100644
index 0000000000..a0df8c3763
--- /dev/null
+++ b/gnu/packages/patches/linux-libre-arm64-pinenote-battery-level.patch
@@ -0,0 +1,62 @@
+From 822294664906499682b55264ae0553ee05caa352 Mon Sep 17 00:00:00 2001
+From: Dorian Rudolph <mail@dorianrudolph.com>
+Date: Sat, 14 May 2022 14:28:37 +0200
+Subject: [PATCH] fix power_supply_temp2resist_simple and
+ power_supply_ocv2cap_simple
+
+---
+ drivers/power/supply/power_supply_core.c | 24 ++++++++++++------------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
+index ec838c9bcc0a5e..3828ba9d0eab90 100644
+--- a/drivers/power/supply/power_supply_core.c
++++ b/drivers/power/supply/power_supply_core.c
+@@ -801,17 +801,17 @@ int power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *t
+ {
+ int i, high, low;
+
+- /* Break loop at table_len - 1 because that is the highest index */
+- for (i = 0; i < table_len - 1; i++)
++ for (i = 0; i < table_len; i++)
+ if (temp > table[i].temp)
+ break;
+
+ /* The library function will deal with high == low */
+- if ((i == 0) || (i == (table_len - 1)))
+- high = i;
++ if (i == 0)
++ high = low = i;
++ else if (i == table_len)
++ high = low = i - 1;
+ else
+- high = i - 1;
+- low = i;
++ high = (low = i) - 1;
+
+ return fixp_linear_interpolate(table[low].temp,
+ table[low].resistance,
+@@ -838,17 +838,17 @@ int power_supply_ocv2cap_simple(struct power_supply_battery_ocv_table *table,
+ {
+ int i, high, low;
+
+- /* Break loop at table_len - 1 because that is the highest index */
+- for (i = 0; i < table_len - 1; i++)
++ for (i = 0; i < table_len; i++)
+ if (ocv > table[i].ocv)
+ break;
+
+ /* The library function will deal with high == low */
+- if ((i == 0) || (i == (table_len - 1)))
+- high = i - 1;
++ if (i == 0)
++ high = low = i;
++ else if (i == table_len)
++ high = low = i - 1;
+ else
+- high = i; /* i.e. i == 0 */
+- low = i;
++ high = (low = i) - 1;
+
+ return fixp_linear_interpolate(table[low].ocv,
+ table[low].capacity,
diff --git a/gnu/packages/patches/linux-libre-arm64-pinenote-defconfig.patch b/gnu/packages/patches/linux-libre-arm64-pinenote-defconfig.patch
new file mode 100644
index 0000000000..97898a141e
--- /dev/null
+++ b/gnu/packages/patches/linux-libre-arm64-pinenote-defconfig.patch
@@ -0,0 +1,48 @@
+diff --git a/arch/arm64/configs/pinenote_defconfig b/arch/arm64/configs/pinenote_defconfig
+index bea435dc92c4..86cdaa92cc2f 100644
+--- a/arch/arm64/configs/pinenote_defconfig
++++ b/arch/arm64/configs/pinenote_defconfig
+@@ -86,6 +86,7 @@ CONFIG_ARCH_ROCKCHIP=y
+ # CONFIG_NVIDIA_CARMEL_CNP_ERRATUM is not set
+ # CONFIG_SOCIONEXT_SYNQUACER_PREITS is not set
+ CONFIG_SCHED_MC=y
++CONFIG_SCHED_SMT=y
+ CONFIG_NR_CPUS=4
+ CONFIG_HZ_1000=y
+ # CONFIG_UNMAP_KERNEL_AT_EL0 is not set
+@@ -155,7 +156,7 @@ CONFIG_CGROUP_NET_PRIO=y
+ CONFIG_BT=m
+ CONFIG_BT_RFCOMM=y
+ CONFIG_BT_RFCOMM_TTY=y
+-CONFIG_BT_HIDP=y
++CONFIG_BT_HIDP=m
+ CONFIG_BT_HS=y
+ CONFIG_BT_LEDS=y
+ CONFIG_BT_HCIUART=m
+@@ -223,14 +224,16 @@ CONFIG_BRCMFMAC=m
+ # CONFIG_WLAN_VENDOR_TI is not set
+ # CONFIG_WLAN_VENDOR_ZYDAS is not set
+ # CONFIG_WLAN_VENDOR_QUANTENNA is not set
++CONFIG_INPUT_MOUSEDEV=m
+ CONFIG_INPUT_EVDEV=y
+ CONFIG_KEYBOARD_ADC=m
+ # CONFIG_KEYBOARD_ATKBD is not set
+ CONFIG_KEYBOARD_GPIO=y
+-# CONFIG_INPUT_MOUSE is not set
++CONFIG_INPUT_MOUSE=y
+ CONFIG_INPUT_TOUCHSCREEN=y
+ CONFIG_TOUCHSCREEN_CYTTSP4_CORE=m
+ CONFIG_TOUCHSCREEN_CYTTSP4_I2C=m
++CONFIG_TOUCHSCREEN_CYTTSP5=m
+ CONFIG_INPUT_MISC=y
+ CONFIG_INPUT_RK805_PWRKEY=y
+ CONFIG_INPUT_WS8100_PEN=m
+@@ -459,6 +462,8 @@ CONFIG_SND_SOC_SIMPLE_AMPLIFIER=m
+ CONFIG_SND_SIMPLE_CARD=m
+ CONFIG_HID_BATTERY_STRENGTH=y
+ CONFIG_HIDRAW=y
++CONFIG_UHID=m
++CONFIG_HID_MICROSOFT=y
+ CONFIG_USB_HIDDEV=y
+ CONFIG_I2C_HID_OF=m
+ CONFIG_I2C_HID_OF_GOODIX=m
diff --git a/gnu/packages/patches/linux-libre-arm64-pinenote-dtsi.patch b/gnu/packages/patches/linux-libre-arm64-pinenote-dtsi.patch
new file mode 100644
index 0000000000..e12b84a86d
--- /dev/null
+++ b/gnu/packages/patches/linux-libre-arm64-pinenote-dtsi.patch
@@ -0,0 +1,167 @@
+diff --git a/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi
+index 59ac178881b3..ec7183330b40 100644
+--- a/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi
++++ b/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi
+@@ -51,11 +51,11 @@ battery_cell: battery-cell {
+
+ ocv-capacity-celsius = <20>;
+ ocv-capacity-table-0 = <4168000 100>,
+- <4109000 95>, <4066000 90>, <4023000 85>, <3985000 80>,
+- <3954000 75>, <3924000 70>, <3897000 65>, <3866000 60>,
+- <3826000 55>, <3804000 50>, <3789000 45>, <3777000 40>,
+- <3770000 35>, <3763000 30>, <3750000 25>, <3732000 20>,
+- <3710000 15>, <3680000 10>, <3670000 5>, <3500000 0>;
++ <4109000 95>, <4066000 90>, <4023000 85>, <3985000 80>,
++ <3954000 75>, <3924000 70>, <3897000 65>, <3866000 60>,
++ <3826000 55>, <3804000 50>, <3789000 45>, <3777000 40>,
++ <3770000 35>, <3763000 30>, <3750000 25>, <3732000 20>,
++ <3710000 15>, <3680000 10>, <3670000 5>, <3500000 0>;
+ };
+
+ bt_sco_codec: bt-sco-codec {
+@@ -63,26 +63,26 @@ bt_sco_codec: bt-sco-codec {
+ #sound-dai-cells = <1>;
+ };
+
+- bt-sound {
+- compatible = "simple-audio-card";
+- #address-cells = <1>;
+- #size-cells = <0>;
+- simple-audio-card,name = "PineNote Bluetooth";
+-
+- simple-audio-card,dai-link@0 {
+- format = "i2s";
+- frame-master = <&bt_link0_cpu>;
+- bitclock-master = <&bt_link0_cpu>;
+-
+- bt_link0_cpu: cpu {
+- sound-dai = <&i2s2_2ch>;
+- };
+-
+- bt_link0_codec: codec {
+- sound-dai = <&bt_sco_codec 0>;
+- };
+- };
+- };
++ // bt-sound {
++ // compatible = "simple-audio-card";
++ // #address-cells = <1>;
++ // #size-cells = <0>;
++ // simple-audio-card,name = "PineNote Bluetooth";
++//
++ // simple-audio-card,dai-link@0 {
++ // format = "i2s";
++ // frame-master = <&bt_link0_cpu>;
++ // bitclock-master = <&bt_link0_cpu>;
++//
++ // bt_link0_cpu: cpu {
++ // sound-dai = <&i2s2_2ch>;
++ // };
++//
++ // bt_link0_codec: codec {
++ // sound-dai = <&bt_sco_codec 0>;
++ // };
++ // };
++ // };
+
+ dmic_codec: dmic-codec {
+ compatible = "dmic-codec";
+@@ -95,15 +95,15 @@ gpio-keys {
+ pinctrl-0 = <&hall_int_l>;
+ pinctrl-names = "default";
+
+- cover {
+- label = "cover";
+- gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_LOW>;
+- linux,input-type = <EV_SW>;
+- linux,code = <SW_MACHINE_COVER>;
+- linux,can-disable;
+- wakeup-event-action = <EV_ACT_DEASSERTED>;
+- wakeup-source;
+- };
++ /* cover { */
++ /* label = "cover"; */
++ /* gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_LOW>; */
++ /* linux,input-type = <EV_SW>; */
++ /* linux,code = <SW_MACHINE_COVER>; */
++ /* linux,can-disable; */
++ /* wakeup-event-action = <EV_ACT_DEASSERTED>; */
++ /* wakeup-source; */
++ /* }; */
+ };
+
+ gpio-leds {
+@@ -166,13 +166,13 @@ sound {
+ simple-audio-card,name = "PineNote";
+ simple-audio-card,aux-devs = <&spk_amp>;
+ simple-audio-card,widgets = "Headphone", "Headphones",
+- "Speaker", "Internal Speakers";
++ "Speaker", "Internal Speakers";
+ simple-audio-card,routing = "Headphones", "HPOL",
+- "Headphones", "HPOR",
+- "Internal Speakers", "Speaker Amp OUTL",
+- "Internal Speakers", "Speaker Amp OUTR",
+- "Speaker Amp INL", "HPOL",
+- "Speaker Amp INR", "HPOR";
++ "Headphones", "HPOR",
++ "Internal Speakers", "Speaker Amp OUTL",
++ "Internal Speakers", "Speaker Amp OUTR",
++ "Speaker Amp INL", "HPOL",
++ "Speaker Amp INR", "HPOR";
+ simple-audio-card,pin-switches = "Internal Speakers";
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -340,7 +340,7 @@ &eink {
+
+ &gpu {
+ mali-supply = <&vdd_gpu_npu>;
+- // status = "okay";
++ status = "okay";
+ };
+
+ &i2c0 {
+@@ -669,19 +669,31 @@ accelerometer@18 {
+ st,drdy-int-pin = <1>;
+ vdd-supply = <&vcc_3v3>;
+ vddio-supply = <&vcc_3v3>;
+- };
+-
+- touchscreen@24 {
+- compatible = "cypress,tt21000";
+- hid-descr-addr = <0x1>;
+- reg = <0x24>;
+- interrupt-parent = <&gpio0>;
+- interrupts = <RK_PA6 IRQ_TYPE_LEVEL_LOW>;
+- pinctrl-0 = <&ts_int_l>, <&ts_rst_l>;
+- pinctrl-names = "default";
+- reset-gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>;
+- vdd-supply = <&vcc_3v3_pmu>;
+- };
++ mount-matrix = "-1", "0", "0",
++ "0", "1", "0",
++ "0", "0", "1";
++ };
++
++ // from pgwipeouts dtsi
++ touchscreen@24 {
++ compatible = "cypress,tma448";
++// compatible = "cypress,tt21000";
++ hid-descr-addr = <0x1>;
++ reg = <0x24>;
++ interrupt-parent = <&gpio0>;
++ interrupts = <RK_PA6 IRQ_TYPE_LEVEL_LOW>;
++ pinctrl-0 = <&ts_int_l>, <&ts_rst_l>;
++ pinctrl-names = "default";
++ reset-gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>;
++ vdd-supply = <&vcc_3v3_pmu>;
++ touchscreen-max-pressure = <46>;
++ touchscreen-min-x = <10>;
++ touchscreen-min-y = <5>;
++ touchscreen-size-x = <1863>;
++ touchscreen-size-y = <1399>;
++ touchscreen-x-mm = <1864>;
++ touchscreen-y-mm = <1400>;
++ };
+ };
+
+ &i2s1_8ch {
diff --git a/gnu/packages/patches/linux-libre-arm64-pinenote-ebc-patches.patch b/gnu/packages/patches/linux-libre-arm64-pinenote-ebc-patches.patch
new file mode 100644
index 0000000000..7fcd50858b
--- /dev/null
+++ b/gnu/packages/patches/linux-libre-arm64-pinenote-ebc-patches.patch
@@ -0,0 +1,2852 @@
+From cb80d9f99f75ea1ed6c8c6b194910b6ae9574a07 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Mon, 30 May 2022 21:06:31 +0200
+Subject: [PATCH 01/37] [rockchip_ebc] when doing partial refreshes, wait for
+ each frame to finish (i.e. wait for the irc from the epd controller) before
+ starting to fill in the buffers for the next frame
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 15 ++++++++++-----
+ 1 file changed, 10 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 285f43bc6d91..d7ed954e1618 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -580,11 +580,11 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ dma_sync_single_for_device(dev, phase_handle,
+ ctx->phase_size, DMA_TO_DEVICE);
+
+- if (frame) {
+- if (!wait_for_completion_timeout(&ebc->display_end,
+- EBC_FRAME_TIMEOUT))
+- drm_err(drm, "Frame %d timed out!\n", frame);
+- }
++ /* if (frame) { */
++ /* if (!wait_for_completion_timeout(&ebc->display_end, */
++ /* EBC_FRAME_TIMEOUT)) */
++ /* drm_err(drm, "Frame %d timed out!\n", frame); */
++ /* } */
+
+ if (list_empty(&areas))
+ break;
+@@ -597,6 +597,11 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ regmap_write(ebc->regmap, EBC_DSP_START,
+ ebc->dsp_start |
+ EBC_DSP_START_DSP_FRM_START);
++ if (frame) {
++ if (!wait_for_completion_timeout(&ebc->display_end,
++ EBC_FRAME_TIMEOUT))
++ drm_err(drm, "Frame %d timed out!\n", frame);
++ }
+ }
+ }
+
+--
+2.30.2
+
+
+From cdbfcec184ed55da2d55a8622240e5a30c03eb1e Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Mon, 30 May 2022 21:13:57 +0200
+Subject: [PATCH 02/37] [rockchip_ebc] change the dma mappings in
+ rockchip_ebc_partial_refresh according to the documentation in
+ Documentation/core-api/dma-api.rst and use dma_map_single to get dma address
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 19 ++++++++++++++++---
+ 1 file changed, 16 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index d7ed954e1618..b0dfc493c059 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -13,6 +13,7 @@
+ #include <linux/pm_runtime.h>
+ #include <linux/regmap.h>
+ #include <linux/regulator/consumer.h>
++#include <linux/dma-mapping.h>
+
+ #include <drm/drm_atomic.h>
+ #include <drm/drm_atomic_helper.h>
+@@ -479,8 +480,8 @@ static void rockchip_ebc_blit_pixels(const struct rockchip_ebc_ctx *ctx,
+ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ struct rockchip_ebc_ctx *ctx)
+ {
+- dma_addr_t next_handle = virt_to_phys(ctx->next);
+- dma_addr_t prev_handle = virt_to_phys(ctx->prev);
++ // dma_addr_t next_handle = virt_to_phys(ctx->next);
++ // dma_addr_t prev_handle = virt_to_phys(ctx->prev);
+ struct rockchip_ebc_area *area, *next_area;
+ u32 last_phase = ebc->lut.num_phases - 1;
+ struct drm_device *drm = &ebc->drm;
+@@ -489,10 +490,18 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ LIST_HEAD(areas);
+ u32 frame;
+
++ dma_addr_t next_handle = dma_map_single(dev, ctx->next, ctx->gray4_size, DMA_TO_DEVICE);
++ dma_addr_t prev_handle = dma_map_single(dev, ctx->prev, ctx->gray4_size, DMA_TO_DEVICE);
++
++ dma_addr_t phase_handles[2];
++ phase_handles[0] = dma_map_single(dev, ctx->phase[0], ctx->gray4_size, DMA_TO_DEVICE);
++ phase_handles[1] = dma_map_single(dev, ctx->phase[1], ctx->gray4_size, DMA_TO_DEVICE);
++
+ for (frame = 0;; frame++) {
+ /* Swap phase buffers to minimize latency between frames. */
+ u8 *phase_buffer = ctx->phase[frame % 2];
+- dma_addr_t phase_handle = virt_to_phys(phase_buffer);
++ // dma_addr_t phase_handle = virt_to_phys(phase_buffer);
++ dma_addr_t phase_handle = phase_handles[frame % 2];
+ bool sync_next = false;
+ bool sync_prev = false;
+
+@@ -603,6 +612,10 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ drm_err(drm, "Frame %d timed out!\n", frame);
+ }
+ }
++ dma_unmap_single(dev, next_handle, ctx->gray4_size, DMA_TO_DEVICE);
++ dma_unmap_single(dev, prev_handle, ctx->gray4_size, DMA_TO_DEVICE);
++ dma_unmap_single(dev, phase_handles[0], ctx->gray4_size, DMA_TO_DEVICE);
++ dma_unmap_single(dev, phase_handles[1], ctx->gray4_size, DMA_TO_DEVICE);
+ }
+
+ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+--
+2.30.2
+
+
+From f79e16df9a8f7853e206d5f4cb122ca231a0b2ab Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Mon, 30 May 2022 21:25:29 +0200
+Subject: [PATCH 03/37] [rockchip_ebc] Some people (including me on a Debian
+ sid installation) see kernel panics/hangs on reboot/shutdown (and module
+ unload) with the new driver. Investigation shows that the refresh thread
+ hangs on the schedule() command, which lead me to believe that the thread is
+ not properly shut down when the kernel module is triggered to shutdown. This
+ patch attempts to
+
+- explicitly shut down the refresh thread before termination
+- adds some control commands to quickly finish for various park/stop
+ states
+- only attempts to park the refresh thread if it is not dead yet (which
+ caused a kernel panic on shutdown)
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 24 +++++++++++++++---------
+ 1 file changed, 15 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index b0dfc493c059..4df73794281b 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -13,6 +13,7 @@
+ #include <linux/pm_runtime.h>
+ #include <linux/regmap.h>
+ #include <linux/regulator/consumer.h>
++#include <linux/sched.h>
+ #include <linux/dma-mapping.h>
+
+ #include <drm/drm_atomic.h>
+@@ -760,12 +761,13 @@ static int rockchip_ebc_refresh_thread(void *data)
+ rockchip_ebc_refresh(ebc, ctx, true, DRM_EPD_WF_RESET);
+ }
+
+- while (!kthread_should_park()) {
++ while ((!kthread_should_park()) && (!kthread_should_stop())) {
+ rockchip_ebc_refresh(ebc, ctx, false, default_waveform);
+
+ set_current_state(TASK_IDLE);
+- if (list_empty(&ctx->queue))
++ if (list_empty(&ctx->queue) && (!kthread_should_stop()) && (!kthread_should_park())){
+ schedule();
++ }
+ __set_current_state(TASK_RUNNING);
+ }
+
+@@ -775,8 +777,9 @@ static int rockchip_ebc_refresh_thread(void *data)
+ */
+ memset(ctx->next, 0xff, ctx->gray4_size);
+ rockchip_ebc_refresh(ebc, ctx, true, DRM_EPD_WF_GC16);
+-
+- kthread_parkme();
++ if (!kthread_should_stop()){
++ kthread_parkme();
++ }
+ }
+
+ return 0;
+@@ -925,7 +928,7 @@ static void rockchip_ebc_crtc_atomic_enable(struct drm_crtc *crtc,
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ if (crtc_state->mode_changed)
+- kthread_unpark(ebc->refresh_thread);
++ kthread_unpark(ebc->refresh_thread);
+ }
+
+ static void rockchip_ebc_crtc_atomic_disable(struct drm_crtc *crtc,
+@@ -935,8 +938,11 @@ static void rockchip_ebc_crtc_atomic_disable(struct drm_crtc *crtc,
+ struct drm_crtc_state *crtc_state;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+- if (crtc_state->mode_changed)
+- kthread_park(ebc->refresh_thread);
++ if (crtc_state->mode_changed){
++ if (! ((ebc->refresh_thread->__state) & (TASK_DEAD))){
++ kthread_park(ebc->refresh_thread);
++ }
++ }
+ }
+
+ static const struct drm_crtc_helper_funcs rockchip_ebc_crtc_helper_funcs = {
+@@ -1573,9 +1579,8 @@ static int rockchip_ebc_remove(struct platform_device *pdev)
+ struct device *dev = &pdev->dev;
+
+ drm_dev_unregister(&ebc->drm);
+- drm_atomic_helper_shutdown(&ebc->drm);
+-
+ kthread_stop(ebc->refresh_thread);
++ drm_atomic_helper_shutdown(&ebc->drm);
+
+ pm_runtime_disable(dev);
+ if (!pm_runtime_status_suspended(dev))
+@@ -1589,6 +1594,7 @@ static void rockchip_ebc_shutdown(struct platform_device *pdev)
+ struct rockchip_ebc *ebc = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
++ kthread_stop(ebc->refresh_thread);
+ drm_atomic_helper_shutdown(&ebc->drm);
+
+ if (!pm_runtime_status_suspended(dev))
+--
+2.30.2
+
+
+From 74e9d814c298f064a07ebc77b1e7ec447cc340f6 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Mon, 30 May 2022 22:20:41 +0200
+Subject: [PATCH 04/37] [rockchip_ebc] use dma_sync_single_for_cpu before
+ writing to dma buffers
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 4df73794281b..d8af43fe9f42 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -506,6 +506,9 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ bool sync_next = false;
+ bool sync_prev = false;
+
++ // now the CPU is allowed to change the phase buffer
++ dma_sync_single_for_cpu(dev, phase_handle, phase_size, DMA_TO_DEVICE);
++
+ /* Move the queued damage areas to the local list. */
+ spin_lock(&ctx->queue_lock);
+ list_splice_tail_init(&ctx->queue, &areas);
+@@ -533,6 +536,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+
+ /* Copy ctx->final to ctx->next on the first frame. */
+ if (frame_delta == 0) {
++ dma_sync_single_for_cpu(dev, next_handle, gray4_size, DMA_TO_DEVICE);
+ rockchip_ebc_blit_pixels(ctx, ctx->next,
+ ctx->final,
+ &area->clip);
+@@ -568,6 +572,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ * also ensures both phase buffers get set to 0xff.
+ */
+ if (frame_delta > last_phase) {
++ dma_sync_single_for_cpu(dev, prev_handle, gray4_size, DMA_TO_DEVICE);
+ rockchip_ebc_blit_pixels(ctx, ctx->prev,
+ ctx->next,
+ &area->clip);
+--
+2.30.2
+
+
+From 39686d27f0193a625b6f569b8de88e1b85e92480 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Mon, 30 May 2022 22:39:00 +0200
+Subject: [PATCH 05/37] rockchip_ebc fix previous commit
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index d8af43fe9f42..6a0f125040df 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -507,7 +507,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ bool sync_prev = false;
+
+ // now the CPU is allowed to change the phase buffer
+- dma_sync_single_for_cpu(dev, phase_handle, phase_size, DMA_TO_DEVICE);
++ dma_sync_single_for_cpu(dev, phase_handle, ctx->phase_size, DMA_TO_DEVICE);
+
+ /* Move the queued damage areas to the local list. */
+ spin_lock(&ctx->queue_lock);
+--
+2.30.2
+
+
+From a347a0909bb7bde73ba53b9ebae044f7fd17466f Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 3 Jun 2022 21:13:28 +0200
+Subject: [PATCH 06/37] [rockchip_ebc] convert all remaining uses of
+ virt_to_phys to the dma api
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 37 ++++++++++++++-----------
+ 1 file changed, 21 insertions(+), 16 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 6a0f125040df..87deb8098d2d 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -308,15 +308,17 @@ to_ebc_crtc_state(struct drm_crtc_state *crtc_state)
+ }
+
+ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+- const struct rockchip_ebc_ctx *ctx)
++ struct rockchip_ebc_ctx *ctx,
++ dma_addr_t next_handle,
++ dma_addr_t prev_handle
++ )
+ {
+ struct drm_device *drm = &ebc->drm;
+ u32 gray4_size = ctx->gray4_size;
+ struct device *dev = drm->dev;
+
+- dma_sync_single_for_device(dev, virt_to_phys(ctx->next),
+ gray4_size, DMA_TO_DEVICE);
+- dma_sync_single_for_device(dev, virt_to_phys(ctx->prev),
++ dma_sync_single_for_device(dev, prev_handle,
+ gray4_size, DMA_TO_DEVICE);
+
+ reinit_completion(&ebc->display_end);
+@@ -479,10 +481,11 @@ static void rockchip_ebc_blit_pixels(const struct rockchip_ebc_ctx *ctx,
+ }
+
+ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+- struct rockchip_ebc_ctx *ctx)
++ struct rockchip_ebc_ctx *ctx,
++ dma_addr_t next_handle,
++ dma_addr_t prev_handle
++ )
+ {
+- // dma_addr_t next_handle = virt_to_phys(ctx->next);
+- // dma_addr_t prev_handle = virt_to_phys(ctx->prev);
+ struct rockchip_ebc_area *area, *next_area;
+ u32 last_phase = ebc->lut.num_phases - 1;
+ struct drm_device *drm = &ebc->drm;
+@@ -491,9 +494,6 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ LIST_HEAD(areas);
+ u32 frame;
+
+- dma_addr_t next_handle = dma_map_single(dev, ctx->next, ctx->gray4_size, DMA_TO_DEVICE);
+- dma_addr_t prev_handle = dma_map_single(dev, ctx->prev, ctx->gray4_size, DMA_TO_DEVICE);
+-
+ dma_addr_t phase_handles[2];
+ phase_handles[0] = dma_map_single(dev, ctx->phase[0], ctx->gray4_size, DMA_TO_DEVICE);
+ phase_handles[1] = dma_map_single(dev, ctx->phase[1], ctx->gray4_size, DMA_TO_DEVICE);
+@@ -501,7 +501,6 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ for (frame = 0;; frame++) {
+ /* Swap phase buffers to minimize latency between frames. */
+ u8 *phase_buffer = ctx->phase[frame % 2];
+- // dma_addr_t phase_handle = virt_to_phys(phase_buffer);
+ dma_addr_t phase_handle = phase_handles[frame % 2];
+ bool sync_next = false;
+ bool sync_prev = false;
+@@ -618,8 +617,6 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ drm_err(drm, "Frame %d timed out!\n", frame);
+ }
+ }
+- dma_unmap_single(dev, next_handle, ctx->gray4_size, DMA_TO_DEVICE);
+- dma_unmap_single(dev, prev_handle, ctx->gray4_size, DMA_TO_DEVICE);
+ dma_unmap_single(dev, phase_handles[0], ctx->gray4_size, DMA_TO_DEVICE);
+ dma_unmap_single(dev, phase_handles[1], ctx->gray4_size, DMA_TO_DEVICE);
+ }
+@@ -633,6 +630,8 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+ u32 dsp_ctrl = 0, epd_ctrl = 0;
+ struct device *dev = drm->dev;
+ int ret, temperature;
++ dma_addr_t next_handle;
++ dma_addr_t prev_handle;
+
+ /* Resume asynchronously while preparing to refresh. */
+ ret = pm_runtime_get(dev);
+@@ -700,15 +699,21 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+ EBC_DSP_CTRL_DSP_LUT_MODE,
+ dsp_ctrl);
+
++ next_handle = dma_map_single(dev, ctx->next, ctx->gray4_size, DMA_TO_DEVICE);
++ prev_handle = dma_map_single(dev, ctx->prev, ctx->gray4_size, DMA_TO_DEVICE);
++
+ regmap_write(ebc->regmap, EBC_WIN_MST0,
+- virt_to_phys(ctx->next));
++ next_handle);
+ regmap_write(ebc->regmap, EBC_WIN_MST1,
+- virt_to_phys(ctx->prev));
++ prev_handle);
+
+ if (global_refresh)
+- rockchip_ebc_global_refresh(ebc, ctx);
++ rockchip_ebc_global_refresh(ebc, ctx, next_handle, prev_handle);
+ else
+- rockchip_ebc_partial_refresh(ebc, ctx);
++ rockchip_ebc_partial_refresh(ebc, ctx, next_handle, prev_handle);
++
++ dma_unmap_single(dev, next_handle, ctx->gray4_size, DMA_TO_DEVICE);
++ dma_unmap_single(dev, prev_handle, ctx->gray4_size, DMA_TO_DEVICE);
+
+ /* Drive the output pins low once the refresh is complete. */
+ regmap_write(ebc->regmap, EBC_DSP_START,
+--
+2.30.2
+
+
+From 28a024ea077105a567f8151f182f9e29c19027e5 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 3 Jun 2022 21:16:37 +0200
+Subject: [PATCH 07/37] [rockchip_ebc] add missing dma sinc call
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 87deb8098d2d..0681504fc8d7 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -317,6 +317,7 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ u32 gray4_size = ctx->gray4_size;
+ struct device *dev = drm->dev;
+
++ dma_sync_single_for_device(dev, next_handle,
+ gray4_size, DMA_TO_DEVICE);
+ dma_sync_single_for_device(dev, prev_handle,
+ gray4_size, DMA_TO_DEVICE);
+--
+2.30.2
+
+
+From 7e9e19d5342f5b9bf79d0dcddee2108d1991b7bf Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 3 Jun 2022 21:19:14 +0200
+Subject: [PATCH 08/37] [rockchip_ebc] global refresh should use ctx->final
+ instead of ctx->next to get the current image. Also, delete all pending area
+ updates when doing a global refresh.
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 19 ++++++++++++++++++-
+ 1 file changed, 18 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 0681504fc8d7..470638f59d43 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -317,6 +317,15 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ u32 gray4_size = ctx->gray4_size;
+ struct device *dev = drm->dev;
+
++ struct rockchip_ebc_area *area, *next_area;
++ LIST_HEAD(areas);
++
++ spin_lock(&ctx->queue_lock);
++ list_splice_tail_init(&ctx->queue, &areas);
++ spin_unlock(&ctx->queue_lock);
++
++ memcpy(ctx->next, ctx->final, gray4_size);
++
+ dma_sync_single_for_device(dev, next_handle,
+ gray4_size, DMA_TO_DEVICE);
+ dma_sync_single_for_device(dev, prev_handle,
+@@ -329,6 +338,12 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ ebc->dsp_start |
+ EBC_DSP_START_DSP_FRM_TOTAL(ebc->lut.num_phases - 1) |
+ EBC_DSP_START_DSP_FRM_START);
++ // while we wait for the refresh, delete all scheduled areas
++ list_for_each_entry_safe(area, next_area, &areas, list) {
++ list_del(&area->list);
++ kfree(area);
++ }
++
+ if (!wait_for_completion_timeout(&ebc->display_end,
+ EBC_REFRESH_TIMEOUT))
+ drm_err(drm, "Refresh timed out!\n");
+@@ -756,6 +771,7 @@ static int rockchip_ebc_refresh_thread(void *data)
+ */
+ memset(ctx->prev, 0xff, ctx->gray4_size);
+ memset(ctx->next, 0xff, ctx->gray4_size);
++ memset(ctx->final, 0xff, ctx->gray4_size);
+ /* NOTE: In direct mode, the phase buffers are repurposed for
+ * source driver polarity data, where the no-op value is 0. */
+ memset(ctx->phase[0], direct_mode ? 0 : 0xff, ctx->phase_size);
+@@ -786,7 +802,8 @@ static int rockchip_ebc_refresh_thread(void *data)
+ * Clear the display before disabling the CRTC. Use the
+ * highest-quality waveform to minimize visible artifacts.
+ */
+- memset(ctx->next, 0xff, ctx->gray4_size);
++ // memset(ctx->next, 0xff, ctx->gray4_size);
++ memcpy(ctx->final, ebc->off_screen, ctx->gray4_size);
+ rockchip_ebc_refresh(ebc, ctx, true, DRM_EPD_WF_GC16);
+ if (!kthread_should_stop()){
+ kthread_parkme();
+--
+2.30.2
+
+
+From 53bf42cca1aaabf10e03a8c2e455bea16b2ac539 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 3 Jun 2022 21:27:38 +0200
+Subject: [PATCH 09/37] Revert "[rockchip_ebc] global refresh should use
+ ctx->final instead of ctx->next"
+
+This reverts commit 599a3057df02ab9188d3d6c9db5b5d6846a445c9.
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 19 +------------------
+ 1 file changed, 1 insertion(+), 18 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 470638f59d43..0681504fc8d7 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -317,15 +317,6 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ u32 gray4_size = ctx->gray4_size;
+ struct device *dev = drm->dev;
+
+- struct rockchip_ebc_area *area, *next_area;
+- LIST_HEAD(areas);
+-
+- spin_lock(&ctx->queue_lock);
+- list_splice_tail_init(&ctx->queue, &areas);
+- spin_unlock(&ctx->queue_lock);
+-
+- memcpy(ctx->next, ctx->final, gray4_size);
+-
+ dma_sync_single_for_device(dev, next_handle,
+ gray4_size, DMA_TO_DEVICE);
+ dma_sync_single_for_device(dev, prev_handle,
+@@ -338,12 +329,6 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ ebc->dsp_start |
+ EBC_DSP_START_DSP_FRM_TOTAL(ebc->lut.num_phases - 1) |
+ EBC_DSP_START_DSP_FRM_START);
+- // while we wait for the refresh, delete all scheduled areas
+- list_for_each_entry_safe(area, next_area, &areas, list) {
+- list_del(&area->list);
+- kfree(area);
+- }
+-
+ if (!wait_for_completion_timeout(&ebc->display_end,
+ EBC_REFRESH_TIMEOUT))
+ drm_err(drm, "Refresh timed out!\n");
+@@ -771,7 +756,6 @@ static int rockchip_ebc_refresh_thread(void *data)
+ */
+ memset(ctx->prev, 0xff, ctx->gray4_size);
+ memset(ctx->next, 0xff, ctx->gray4_size);
+- memset(ctx->final, 0xff, ctx->gray4_size);
+ /* NOTE: In direct mode, the phase buffers are repurposed for
+ * source driver polarity data, where the no-op value is 0. */
+ memset(ctx->phase[0], direct_mode ? 0 : 0xff, ctx->phase_size);
+@@ -802,8 +786,7 @@ static int rockchip_ebc_refresh_thread(void *data)
+ * Clear the display before disabling the CRTC. Use the
+ * highest-quality waveform to minimize visible artifacts.
+ */
+- // memset(ctx->next, 0xff, ctx->gray4_size);
+- memcpy(ctx->final, ebc->off_screen, ctx->gray4_size);
++ memset(ctx->next, 0xff, ctx->gray4_size);
+ rockchip_ebc_refresh(ebc, ctx, true, DRM_EPD_WF_GC16);
+ if (!kthread_should_stop()){
+ kthread_parkme();
+--
+2.30.2
+
+
+From c4babc5ae528d3c8c260fe6584f0d1812dda65ef Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 4 Jun 2022 19:39:48 +0200
+Subject: [PATCH 10/37] [rockchip_ebc] global refresh should use ctx->final
+ instead of ctx->next to get the current image. Also, delete all pending
+ area updates when doing a global refresh.
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 0681504fc8d7..41852c23802e 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -317,6 +317,15 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ u32 gray4_size = ctx->gray4_size;
+ struct device *dev = drm->dev;
+
++ struct rockchip_ebc_area *area, *next_area;
++ LIST_HEAD(areas);
++
++ spin_lock(&ctx->queue_lock);
++ list_splice_tail_init(&ctx->queue, &areas);
++ spin_unlock(&ctx->queue_lock);
++
++ memcpy(ctx->next, ctx->final, gray4_size);
++
+ dma_sync_single_for_device(dev, next_handle,
+ gray4_size, DMA_TO_DEVICE);
+ dma_sync_single_for_device(dev, prev_handle,
+@@ -329,6 +338,12 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ ebc->dsp_start |
+ EBC_DSP_START_DSP_FRM_TOTAL(ebc->lut.num_phases - 1) |
+ EBC_DSP_START_DSP_FRM_START);
++ // while we wait for the refresh, delete all scheduled areas
++ list_for_each_entry_safe(area, next_area, &areas, list) {
++ list_del(&area->list);
++ kfree(area);
++ }
++
+ if (!wait_for_completion_timeout(&ebc->display_end,
+ EBC_REFRESH_TIMEOUT))
+ drm_err(drm, "Refresh timed out!\n");
+@@ -756,6 +771,8 @@ static int rockchip_ebc_refresh_thread(void *data)
+ */
+ memset(ctx->prev, 0xff, ctx->gray4_size);
+ memset(ctx->next, 0xff, ctx->gray4_size);
++ memset(ctx->final, 0xff, ctx->gray4_size);
++
+ /* NOTE: In direct mode, the phase buffers are repurposed for
+ * source driver polarity data, where the no-op value is 0. */
+ memset(ctx->phase[0], direct_mode ? 0 : 0xff, ctx->phase_size);
+--
+2.30.2
+
+
+From bb0e94904c9188675bfb6b3e264cc409c558ea72 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 4 Jun 2022 19:44:00 +0200
+Subject: [PATCH 11/37] [rockchip_ebc] add the possibility to trigger one
+ global refresh using a module-global variable do_one_full_refresh
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 20 +++++++++++++++++++-
+ 1 file changed, 19 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 41852c23802e..b1c8f967350b 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -154,6 +154,9 @@ struct rockchip_ebc {
+ u32 dsp_start;
+ bool lut_changed;
+ bool reset_complete;
++ spinlock_t refresh_once_lock;
++ // should this go into the ctx?
++ bool do_one_full_refresh;
+ };
+
+ static int default_waveform = DRM_EPD_WF_GC16;
+@@ -744,6 +747,7 @@ static int rockchip_ebc_refresh_thread(void *data)
+ {
+ struct rockchip_ebc *ebc = data;
+ struct rockchip_ebc_ctx *ctx;
++ bool one_full_refresh;
+
+ while (!kthread_should_stop()) {
+ /* The context will change each time the thread is unparked. */
+@@ -790,7 +794,18 @@ static int rockchip_ebc_refresh_thread(void *data)
+ }
+
+ while ((!kthread_should_park()) && (!kthread_should_stop())) {
+- rockchip_ebc_refresh(ebc, ctx, false, default_waveform);
++ spin_lock(&ebc->refresh_once_lock);
++ one_full_refresh = ebc->do_one_full_refresh;
++ spin_unlock(&ebc->refresh_once_lock);
++
++ if (one_full_refresh) {
++ spin_lock(&ebc->refresh_once_lock);
++ ebc->do_one_full_refresh = false;
++ spin_unlock(&ebc->refresh_once_lock);
++ rockchip_ebc_refresh(ebc, ctx, true, default_waveform);
++ } else {
++ rockchip_ebc_refresh(ebc, ctx, false, default_waveform);
++ }
+
+ set_current_state(TASK_IDLE);
+ if (list_empty(&ctx->queue) && (!kthread_should_stop()) && (!kthread_should_park())){
+@@ -1519,6 +1534,9 @@ static int rockchip_ebc_probe(struct platform_device *pdev)
+
+ ebc = devm_drm_dev_alloc(dev, &rockchip_ebc_drm_driver,
+ struct rockchip_ebc, drm);
++
++ spin_lock_init(&ebc->refresh_once_lock);
++
+ if (IS_ERR(ebc))
+ return PTR_ERR(ebc);
+
+--
+2.30.2
+
+
+From 2b62b6c5853200cf1f1f63010d8edb56a8a08ceb Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 4 Jun 2022 19:46:46 +0200
+Subject: [PATCH 12/37] [rockchip_ebc] add possibility to change the
+ off-screen, i.e. the content of the screen when the module is unloaded. The
+ content is read on module-load time from the firmware file
+ rockchip/rockchip_ebc_default_screen.bin. The file must be of size 1314144
+ bytes containing the 4 bit gray values for each pixel
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 25 ++++++++++++++++++++++++-
+ 1 file changed, 24 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index b1c8f967350b..edf98b048a07 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -15,6 +15,7 @@
+ #include <linux/regulator/consumer.h>
+ #include <linux/sched.h>
+ #include <linux/dma-mapping.h>
++#include <linux/firmware.h>
+
+ #include <drm/drm_atomic.h>
+ #include <drm/drm_atomic_helper.h>
+@@ -154,6 +155,9 @@ struct rockchip_ebc {
+ u32 dsp_start;
+ bool lut_changed;
+ bool reset_complete;
++ // one screen content: 1872 * 1404 / 2
++ // the array size should probably be set dynamically...
++ char off_screen[1314144];
+ spinlock_t refresh_once_lock;
+ // should this go into the ctx?
+ bool do_one_full_refresh;
+@@ -818,7 +822,7 @@ static int rockchip_ebc_refresh_thread(void *data)
+ * Clear the display before disabling the CRTC. Use the
+ * highest-quality waveform to minimize visible artifacts.
+ */
+- memset(ctx->next, 0xff, ctx->gray4_size);
++ memcpy(ctx->final, ebc->off_screen, ctx->gray4_size);
+ rockchip_ebc_refresh(ebc, ctx, true, DRM_EPD_WF_GC16);
+ if (!kthread_should_stop()){
+ kthread_parkme();
+@@ -1334,6 +1338,7 @@ static int rockchip_ebc_drm_init(struct rockchip_ebc *ebc)
+ struct drm_device *drm = &ebc->drm;
+ struct drm_bridge *bridge;
+ int ret;
++ const struct firmware * default_offscreen;
+
+ ret = drmm_epd_lut_file_init(drm, &ebc->lut_file, "rockchip/ebc.wbf");
+ if (ret)
+@@ -1392,6 +1397,24 @@ static int rockchip_ebc_drm_init(struct rockchip_ebc *ebc)
+
+ drm_fbdev_generic_setup(drm, 0);
+
++ // check if there is a default off-screen
++ if (!request_firmware(&default_offscreen, "rockchip/rockchip_ebc_default_screen.bin", drm->dev))
++ {
++ printk(KERN_INFO "rockchip_ebc: default off-screen file found\n");
++ if (default_offscreen->size != 1314144)
++ drm_err(drm, "Size of default offscreen data file is not 1314144\n");
++ else {
++ printk(KERN_INFO "rockchip_ebc: loading default off-screen\n");
++ memcpy(ebc->off_screen, default_offscreen->data, 1314144);
++ }
++ } else {
++ printk(KERN_INFO "rockchip_ebc: no default off-screen file found\n");
++ // fill the off-screen with some values
++ memset(ebc->off_screen, 0xff, 1314144);
++ /* memset(ebc->off_screen, 0x00, 556144); */
++ }
++ release_firmware(default_offscreen);
++
+ return 0;
+ }
+
+--
+2.30.2
+
+
+From f7fb21e16439c8e271786a20543c7ed74e892750 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 4 Jun 2022 19:49:14 +0200
+Subject: [PATCH 13/37] [rockchip_ebc] implement a simple auto_refresh scheme
+ which triggers a global refresh after a certain area has been drawn using the
+ partial refresh path. The threshold of drawn area after which the refresh is
+ triggered can be modified using the sysfs file
+ /sys/module/rockchip_ebc/parameters/refresh_threshold. A default value of 20
+ (screen areas) seems good enough to get a refresh after 5 pages of ebook
+ reading. This seems to imply that quite a lot of duplicate draws are made for
+ each page turn (not investigated further). The auto-refresh feature is
+ deactivated by default and can be activated using the module parameter
+ auto_refresh or by writing 1 to
+ /sys/module/rockchip_ebc/parameters/auto_refresh
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 33 +++++++++++++++++++++++++
+ 1 file changed, 33 insertions(+)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index edf98b048a07..69ef34e86ba7 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -183,6 +183,14 @@ static bool skip_reset = false;
+ module_param(skip_reset, bool, 0444);
+ MODULE_PARM_DESC(skip_reset, "skip the initial display reset");
+
++static bool auto_refresh = false;
++module_param(auto_refresh, bool, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(auto_refresh, "auto refresh the screen based on partial refreshed area");
++
++static int refresh_threshold = 20;
++module_param(refresh_threshold, int, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(refresh_threshold, "refresh threshold in screen area multiples");
++
+ DEFINE_DRM_GEM_FOPS(rockchip_ebc_fops);
+
+ static const struct drm_driver rockchip_ebc_drm_driver = {
+@@ -243,6 +251,7 @@ struct rockchip_ebc_ctx {
+ u32 gray4_size;
+ u32 phase_pitch;
+ u32 phase_size;
++ u64 area_count;
+ };
+
+ static void rockchip_ebc_ctx_free(struct rockchip_ebc_ctx *ctx)
+@@ -288,6 +297,10 @@ static struct rockchip_ebc_ctx *rockchip_ebc_ctx_alloc(u32 width, u32 height)
+ ctx->phase_pitch = width;
+ ctx->phase_size = phase_size;
+
++ // we keep track of the updated area and use this value to trigger global
++ // refreshes if auto_refresh is enabled
++ ctx->area_count = 0;
++
+ return ctx;
+ }
+
+@@ -516,6 +529,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ struct device *dev = drm->dev;
+ LIST_HEAD(areas);
+ u32 frame;
++ u64 local_area_count = 0;
+
+ dma_addr_t phase_handles[2];
+ phase_handles[0] = dma_map_single(dev, ctx->phase[0], ctx->gray4_size, DMA_TO_DEVICE);
+@@ -558,6 +572,9 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+
+ /* Copy ctx->final to ctx->next on the first frame. */
+ if (frame_delta == 0) {
++ local_area_count += (u64) (
++ area->clip.x2 - area->clip.x1) *
++ (area->clip.y2 - area->clip.y1);
+ dma_sync_single_for_cpu(dev, next_handle, gray4_size, DMA_TO_DEVICE);
+ rockchip_ebc_blit_pixels(ctx, ctx->next,
+ ctx->final,
+@@ -642,6 +659,8 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ }
+ dma_unmap_single(dev, phase_handles[0], ctx->gray4_size, DMA_TO_DEVICE);
+ dma_unmap_single(dev, phase_handles[1], ctx->gray4_size, DMA_TO_DEVICE);
++ /* printk(KERN_INFO "loca area count: %llu\n", local_area_count); */
++ ctx->area_count += local_area_count;
+ }
+
+ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+@@ -655,6 +674,7 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+ int ret, temperature;
+ dma_addr_t next_handle;
+ dma_addr_t prev_handle;
++ int one_screen_area = 1314144;
+
+ /* Resume asynchronously while preparing to refresh. */
+ ret = pm_runtime_get(dev);
+@@ -738,6 +758,19 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+ dma_unmap_single(dev, next_handle, ctx->gray4_size, DMA_TO_DEVICE);
+ dma_unmap_single(dev, prev_handle, ctx->gray4_size, DMA_TO_DEVICE);
+
++ // do we need a full refresh
++ if (auto_refresh){
++ if (ctx->area_count >= refresh_threshold * one_screen_area){
++ printk(KERN_INFO "rockchip: triggering full refresh due to drawn area threshold\n");
++ spin_lock(&ebc->refresh_once_lock);
++ ebc->do_one_full_refresh = true;
++ spin_unlock(&ebc->refresh_once_lock);
++ ctx->area_count = 0;
++ }
++ } else {
++ ctx->area_count = 0;
++ }
++
+ /* Drive the output pins low once the refresh is complete. */
+ regmap_write(ebc->regmap, EBC_DSP_START,
+ ebc->dsp_start |
+--
+2.30.2
+
+
+From eef2a823bf96f492a4d28fe0f90ea91a3c1bb936 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 4 Jun 2022 20:02:26 +0200
+Subject: [PATCH 14/37] [rockchip_ebc] Add two ioctls to the rockchip_ebc
+ module:
+
+DRM_IOCTL_ROCKCHIP_EBC_GLOBAL_REFRESH triggers a global fresh
+
+DRM_IOCTL_ROCKCHIP_EBC_OFF_SCREEN can be used to supply off-screen
+content that is display on shutdown/module-unload.
+
+Corresponding ioctl structures:
+
+struct drm_rockchip_ebc_trigger_global_refresh {
+ bool trigger_global_refresh;
+};
+
+struct drm_rockchip_ebc_off_screen {
+ __u64 info1; // <- not used
+ char * ptr_screen_content;
+};
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 41 +++++++++++++++++++++++++
+ include/uapi/drm/rockchip_ebc_drm.h | 25 +++++++++++++++
+ 2 files changed, 66 insertions(+)
+ create mode 100644 include/uapi/drm/rockchip_ebc_drm.h
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 69ef34e86ba7..9a0a238829bb 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -15,6 +15,7 @@
+ #include <linux/regulator/consumer.h>
+ #include <linux/sched.h>
+ #include <linux/dma-mapping.h>
++#include <linux/uaccess.h>
+ #include <linux/firmware.h>
+
+ #include <drm/drm_atomic.h>
+@@ -29,6 +30,7 @@
+ #include <drm/drm_gem_shmem_helper.h>
+ #include <drm/drm_plane_helper.h>
+ #include <drm/drm_simple_kms_helper.h>
++#include <drm/rockchip_ebc_drm.h>
+
+ #define EBC_DSP_START 0x0000
+ #define EBC_DSP_START_DSP_OUT_LOW BIT(31)
+@@ -193,6 +195,43 @@ MODULE_PARM_DESC(refresh_threshold, "refresh threshold in screen area multiples"
+
+ DEFINE_DRM_GEM_FOPS(rockchip_ebc_fops);
+
++static int ioctl_trigger_global_refresh(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_rockchip_ebc_trigger_global_refresh *args = data;
++ struct rockchip_ebc *ebc = dev_get_drvdata(dev->dev);
++
++ if (args->trigger_global_refresh){
++ printk(KERN_INFO "rockchip_ebc: ioctl would trigger full refresh \n");
++ spin_lock(&ebc->refresh_once_lock);
++ ebc->do_one_full_refresh = true;
++ spin_unlock(&ebc->refresh_once_lock);
++ // try to trigger the refresh immediately
++ wake_up_process(ebc->refresh_thread);
++ }
++
++ return 0;
++}
++
++static int ioctl_set_off_screen(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_rockchip_ebc_off_screen *args = data;
++ struct rockchip_ebc *ebc = dev_get_drvdata(dev->dev);
++ int copy_result;
++
++ copy_result = copy_from_user(&ebc->off_screen, args->ptr_screen_content, 1313144);
++
++ return 0;
++}
++
++static const struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {
++ DRM_IOCTL_DEF_DRV(ROCKCHIP_EBC_GLOBAL_REFRESH, ioctl_trigger_global_refresh,
++ DRM_RENDER_ALLOW),
++ DRM_IOCTL_DEF_DRV(ROCKCHIP_EBC_OFF_SCREEN, ioctl_set_off_screen,
++ DRM_RENDER_ALLOW),
++};
++
+ static const struct drm_driver rockchip_ebc_drm_driver = {
+ .lastclose = drm_fb_helper_lastclose,
+ DRM_GEM_SHMEM_DRIVER_OPS,
+@@ -203,6 +242,8 @@ static const struct drm_driver rockchip_ebc_drm_driver = {
+ .date = "20220303",
+ .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
+ .fops = &rockchip_ebc_fops,
++ .ioctls = ioctls,
++ .num_ioctls = DRM_ROCKCHIP_EBC_NUM_IOCTLS,
+ };
+
+ static const struct drm_mode_config_funcs rockchip_ebc_mode_config_funcs = {
+diff --git a/include/uapi/drm/rockchip_ebc_drm.h b/include/uapi/drm/rockchip_ebc_drm.h
+new file mode 100644
+index 000000000000..befa62a68be0
+--- /dev/null
++++ b/include/uapi/drm/rockchip_ebc_drm.h
+@@ -0,0 +1,25 @@
++#ifndef __ROCKCHIP_EBC_DRM_H__
++#define __ROCKCHIP_EBC_DRM_H__
++
++#include "drm.h"
++
++#if defined(__cplusplus)
++extern "C" {
++#endif
++
++
++struct drm_rockchip_ebc_trigger_global_refresh {
++ bool trigger_global_refresh;
++};
++
++struct drm_rockchip_ebc_off_screen {
++ __u64 info1;
++ char * ptr_screen_content;
++};
++
++#define DRM_ROCKCHIP_EBC_NUM_IOCTLS 0x02
++
++#define DRM_IOCTL_ROCKCHIP_EBC_GLOBAL_REFRESH DRM_IOWR(DRM_COMMAND_BASE + 0x00, struct drm_rockchip_ebc_trigger_global_refresh)
++#define DRM_IOCTL_ROCKCHIP_EBC_OFF_SCREEN DRM_IOWR(DRM_COMMAND_BASE + 0x01, struct drm_rockchip_ebc_off_screen)
++
++#endif /* __ROCKCHIP_EBC_DRM_H__*/
+--
+2.30.2
+
+
+From 2855fb8cf5824b9d0d62d194440a4d7aad360c28 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Thu, 9 Jun 2022 09:56:13 +0200
+Subject: [PATCH 15/37] [rockchip_ebc] try to split overlapping areas into four
+ subareas during refresh so that the non-overlapping parts can start to
+ refresh as soon as possible and we only need to wait for the overlapping
+ part.
+
+The number of areas to split while preparing each frame can be limited.
+I'm not sure if this is really required, but I fear that too many splits
+could slow down the refresh thread.
+
+Splitting areas can produce areas that do not align with full bytes (4
+bit/byte), so we also try to account for odd start/end clips.
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 176 +++++++++++++++++++++++-
+ 1 file changed, 172 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 9a0a238829bb..6f7bbe0bd70f 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -415,10 +415,15 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ struct rockchip_ebc_area *area,
+ struct drm_device *drm,
+- u32 current_frame, u32 num_phases)
++ u32 current_frame, u32 num_phases,
++ struct rockchip_ebc_area *next_area,
++ int * split_counter
++ )
+ {
+ struct rockchip_ebc_area *other;
++ // by default, begin now
+ u32 frame_begin = current_frame;
++ /* printk(KERN_INFO "scheduling area: %i-%i %i-%i\n", area->clip.x1, area->clip.x2, area->clip.y1, area->clip.y2); */
+
+ list_for_each_entry(other, areas, list) {
+ struct drm_rect intersection;
+@@ -437,11 +442,124 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ intersection = area->clip;
+ if (!drm_rect_intersect(&intersection, &other->clip))
+ continue;
++ // we got here, so there is a collision
+
+ /* If the other area already started, wait until it finishes. */
+ if (other->frame_begin < current_frame) {
+ frame_begin = other_end;
+- continue;
++
++ // so here we would optimally want to split the new area into three
++ // parts that do not overlap with the already-started area, and one
++ // which is overlapping. The overlapping one will be scheduled for
++ // later, but the other three should start immediately.
++
++ // if the area is equal to the clip, continue
++ if (drm_rect_equals(&area->clip, &intersection))
++ continue;
++
++ // for now, min size if 2x2
++ if ((area->clip.x2 - area->clip.x1 < 2) | (area->clip.y2 - area->clip.y1 < 2))
++ continue;
++
++ // ok, we want to split this area and start with any partial areas
++ // that are not overlapping (well, let this be decided upon at the
++ // next outer loop - we delete this area so we need not to juggle
++ // around the four areas until we found the one that is actually
++ // overlapping)
++ int xmin, xmax, ymin, ymax, xcenter, ycenter;
++ xmin = area->clip.x1;
++ if (intersection.x1 > xmin)
++ xcenter = intersection.x1;
++ else
++ xcenter = intersection.x2;
++ xmax = area->clip.x2;
++
++ ymin = area->clip.y1;
++ if (intersection.y1 > ymin)
++ ycenter = intersection.y1;
++ else
++ ycenter = intersection.y2;
++ ymax = area->clip.y2;
++
++ if ((xmin == xcenter) | (xcenter == xmax))
++ continue;
++ if ((ymin == ycenter) | (ycenter == ymax))
++ continue;
++
++ // we do not want to overhelm the refresh thread and limit us to a
++ // certain number of splits. The rest needs to wait
++ if (*split_counter >= 6)
++ continue;
++
++ // we need four new rokchip_ebc_area entries that we splice into
++ // the list. Note that the currently next item shall be copied
++ // backwards because to prevent the outer list iteration from
++ // skipping over our newly created items.
++
++ struct rockchip_ebc_area * item1;
++ struct rockchip_ebc_area * item2;
++ struct rockchip_ebc_area * item3;
++ struct rockchip_ebc_area * item4;
++ item1 = kmalloc(sizeof(*item1), GFP_KERNEL);
++ item2 = kmalloc(sizeof(*item2), GFP_KERNEL);
++ item3 = kmalloc(sizeof(*item3), GFP_KERNEL);
++ item4 = kmalloc(sizeof(*item4), GFP_KERNEL);
++
++ // TODO: Error checking!!!!
++ /* if (!area) */
++ /* return -ENOMEM; */
++
++ if (list_is_last(&area->list, areas)){
++ /* printk(KERN_INFO "adding to end of list\n"); */
++ list_add_tail(&item1->list, areas);
++ list_add_tail(&item2->list, areas);
++ list_add_tail(&item3->list, areas);
++ list_add_tail(&item4->list, areas);
++ }
++ else{
++ /* printk(KERN_INFO "splicing into the middle of the list\n"); */
++ __list_add(&item4->list, areas, areas->next);
++ __list_add(&item3->list, areas, areas->next);
++ __list_add(&item2->list, areas, areas->next);
++ __list_add(&item1->list, areas, areas->next);
++ }
++ next_area = item1;
++
++ // now fill the areas
++ /* printk(KERN_INFO "area1: %i %i %i %i\n", xmin, xcenter, ymin, ycenter); */
++ /* printk(KERN_INFO "area2: %i %i %i %i\n", xmin, xcenter, ycenter, ymax); */
++ /* printk(KERN_INFO "area3: %i %i %i %i\n", xcenter, xmax, ymin, ycenter); */
++ /* printk(KERN_INFO "area4: %i %i %i %i\n", xcenter, xmax, ycenter, ymax); */
++
++ item1->frame_begin = EBC_FRAME_PENDING;
++ item1->clip.x1 = xmin;
++ item1->clip.x2 = xcenter;
++ item1->clip.y1 = ymin;
++ item1->clip.y2 = ycenter;
++
++ item2->frame_begin = EBC_FRAME_PENDING;
++ item2->clip.x1 = xmin;
++ item2->clip.x2 = xcenter;
++ item2->clip.y1 = ycenter + 1;
++ item2->clip.y2 = ymax;
++
++ item3->frame_begin = EBC_FRAME_PENDING;
++ item3->clip.x1 = xcenter + 1;
++ item3->clip.x2 = xmax;
++ item3->clip.y1 = ymin;
++ item3->clip.y2 = ycenter;
++
++ item4->frame_begin = EBC_FRAME_PENDING;
++ item4->clip.x1 = xcenter + 1;
++ item4->clip.x2 = xmax;
++ item4->clip.y1 = ycenter + 1;
++ item4->clip.y2 = ymax;
++
++ *split_counter++;
++
++ // let the outer loop delete this area
++ return false;
++ /* continue; */
+ }
+
+ /*
+@@ -538,8 +656,18 @@ static void rockchip_ebc_blit_pixels(const struct rockchip_ebc_ctx *ctx,
+ u8 *dst, const u8 *src,
+ const struct drm_rect *clip)
+ {
++ bool start_x_is_odd = clip->x1 & 1;
++ bool end_x_is_odd = clip->x2 & 1;
++ u8 first_odd;
++ u8 last_odd;
++
+ unsigned int x1_bytes = clip->x1 / 2;
+ unsigned int x2_bytes = clip->x2 / 2;
++ // the integer division floors by default, but we want to include the last
++ // byte (partially)
++ if (end_x_is_odd)
++ x2_bytes++;
++
+ unsigned int pitch = ctx->gray4_pitch;
+ unsigned int width = x2_bytes - x1_bytes;
+ const u8 *src_line;
+@@ -550,8 +678,29 @@ static void rockchip_ebc_blit_pixels(const struct rockchip_ebc_ctx *ctx,
+ src_line = src + clip->y1 * pitch + x1_bytes;
+
+ for (y = clip->y1; y < clip->y2; y++) {
++ if (start_x_is_odd)
++ // keep only lower bit to restore it after the blitting
++ first_odd = *src_line & 0b00001111;
++ if (end_x_is_odd){
++ dst_line += pitch - 1;
++ // keep only the upper bit for restoring later
++ last_odd = *dst_line & 0b11110000;
++ dst_line -= pitch - 1;
++ }
++
+ memcpy(dst_line, src_line, width);
+
++ if (start_x_is_odd){
++ // write back the first 4 saved bits
++ *dst_line = first_odd | (*dst_line & 0b11110000);
++ }
++ if (end_x_is_odd){
++ // write back the last 4 saved bits
++ dst_line += pitch -1;
++ *dst_line = (*dst_line & 0b00001111) | last_odd;
++ dst_line -= pitch -1;
++ }
++
+ dst_line += pitch;
+ src_line += pitch;
+ }
+@@ -582,6 +731,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ dma_addr_t phase_handle = phase_handles[frame % 2];
+ bool sync_next = false;
+ bool sync_prev = false;
++ int split_counter = 0;
+
+ // now the CPU is allowed to change the phase buffer
+ dma_sync_single_for_cpu(dev, phase_handle, ctx->phase_size, DMA_TO_DEVICE);
+@@ -601,18 +751,20 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ */
+ if (area->frame_begin == EBC_FRAME_PENDING &&
+ !rockchip_ebc_schedule_area(&areas, area, drm, frame,
+- ebc->lut.num_phases)) {
++ ebc->lut.num_phases, next_area, &split_counter)) {
+ list_del(&area->list);
+ kfree(area);
+ continue;
+ }
+
++ // we wait a little bit longer to start
+ frame_delta = frame - area->frame_begin;
+ if (frame_delta < 0)
+ continue;
+
+ /* Copy ctx->final to ctx->next on the first frame. */
+ if (frame_delta == 0) {
++ printk(KERN_INFO "rockchip partial refresh starting area on frame %i (%i/%i %i/%i)\n", frame, area->clip.x1, area->clip.x2, area->clip.y1, area->clip.y2);
+ local_area_count += (u64) (
+ area->clip.x2 - area->clip.x1) *
+ (area->clip.y2 - area->clip.y1);
+@@ -1212,9 +1364,13 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ int delta_x;
+ void *dst;
+
++ bool start_x_is_odd = src_clip->x1 & 1;
++ bool end_x_is_odd = src_clip->x2 & 1;
++
+ delta_x = panel_reflection ? -1 : 1;
+ start_x = panel_reflection ? src_clip->x2 - 1 : src_clip->x1;
+
++ // I think this also works if dst_clip->x1 is odd
+ dst = ctx->final + dst_clip->y1 * dst_pitch + dst_clip->x1 / 2;
+ src = vaddr + src_clip->y1 * src_pitch + start_x * fb->format->cpp[0];
+
+@@ -1236,7 +1392,19 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ /* Unbias the value for rounding to 4 bits. */
+ rgb0 += 0x07000000U; rgb1 += 0x07000000U;
+
+- gray = rgb0 >> 28 | rgb1 >> 28 << 4;
++ rgb0 >>= 28;
++ rgb1 >>= 28;
++
++ if (x == src_clip->x1 && start_x_is_odd) {
++ // rgb0 should be filled with the content of the src pixel here
++ rgb0 = *dbuf;
++ }
++ if (x == src_clip->x2 && end_x_is_odd) {
++ // rgb1 should be filled with the content of the src pixel here
++ rgb1 = *dbuf;
++ }
++
++ gray = rgb0 | rgb1 << 4;
+ changed |= gray ^ *dbuf;
+ *dbuf++ = gray;
+ }
+--
+2.30.2
+
+
+From 58cb814fa8389a157c30d90511be33b75066a417 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 11 Jun 2022 20:55:34 +0200
+Subject: [PATCH 16/37] [rockchip_ebc] add a sys parameter split_area_limit
+ (default: 12) that determines how many areas to maximally split in each
+ scheduling run. Set to 0 to disable area splitting.
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 6f7bbe0bd70f..ae8f6727d05c 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -193,6 +193,10 @@ static int refresh_threshold = 20;
+ module_param(refresh_threshold, int, S_IRUGO|S_IWUSR);
+ MODULE_PARM_DESC(refresh_threshold, "refresh threshold in screen area multiples");
+
++static int split_area_limit = 12;
++module_param(split_area_limit, int, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(split_area_limit, "how many areas to split in each scheduling call");
++
+ DEFINE_DRM_GEM_FOPS(rockchip_ebc_fops);
+
+ static int ioctl_trigger_global_refresh(struct drm_device *dev, void *data,
+@@ -488,7 +492,7 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+
+ // we do not want to overhelm the refresh thread and limit us to a
+ // certain number of splits. The rest needs to wait
+- if (*split_counter >= 6)
++ if (*split_counter >= split_area_limit)
+ continue;
+
+ // we need four new rokchip_ebc_area entries that we splice into
+--
+2.30.2
+
+
+From 2b91cc2d12d73e24bfbfae3fdc9a71e83885092d Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 11 Jun 2022 20:56:36 +0200
+Subject: [PATCH 17/37] [rockchip_ebc] fix ioctl printk message
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index ae8f6727d05c..4d6a799d7bb4 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -206,7 +206,7 @@ static int ioctl_trigger_global_refresh(struct drm_device *dev, void *data,
+ struct rockchip_ebc *ebc = dev_get_drvdata(dev->dev);
+
+ if (args->trigger_global_refresh){
+- printk(KERN_INFO "rockchip_ebc: ioctl would trigger full refresh \n");
++ printk(KERN_INFO "rockchip_ebc: ioctl triggered full refresh \n");
+ spin_lock(&ebc->refresh_once_lock);
+ ebc->do_one_full_refresh = true;
+ spin_unlock(&ebc->refresh_once_lock);
+--
+2.30.2
+
+
+From 314ebae7211613cce9085809115212f3dc1002a8 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 11 Jun 2022 20:57:14 +0200
+Subject: [PATCH 18/37] [rockchip_ebc] fix clips of split areas
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 4d6a799d7bb4..4eb6e1e0f261 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -544,19 +544,19 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ item2->frame_begin = EBC_FRAME_PENDING;
+ item2->clip.x1 = xmin;
+ item2->clip.x2 = xcenter;
+- item2->clip.y1 = ycenter + 1;
++ item2->clip.y1 = ycenter;
+ item2->clip.y2 = ymax;
+
+ item3->frame_begin = EBC_FRAME_PENDING;
+- item3->clip.x1 = xcenter + 1;
++ item3->clip.x1 = xcenter;
+ item3->clip.x2 = xmax;
+ item3->clip.y1 = ymin;
+ item3->clip.y2 = ycenter;
+
+ item4->frame_begin = EBC_FRAME_PENDING;
+- item4->clip.x1 = xcenter + 1;
++ item4->clip.x1 = xcenter;
+ item4->clip.x2 = xmax;
+- item4->clip.y1 = ycenter + 1;
++ item4->clip.y1 = ycenter;
+ item4->clip.y2 = ymax;
+
+ *split_counter++;
+--
+2.30.2
+
+
+From 5894a086939ec2c8e88bdbe2505052d6d4fd7da4 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 11 Jun 2022 20:57:44 +0200
+Subject: [PATCH 19/37] [rockchip_ebc] fix incrementing of splitting counter
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 4eb6e1e0f261..7e1558403973 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -559,7 +559,7 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ item4->clip.y1 = ycenter;
+ item4->clip.y2 = ymax;
+
+- *split_counter++;
++ (*split_counter)++;
+
+ // let the outer loop delete this area
+ return false;
+--
+2.30.2
+
+
+From 325b7773c89b498de357d2952ed47ba052658296 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 11 Jun 2022 20:58:17 +0200
+Subject: [PATCH 20/37] [rockchip_ebc] Fix a bug in the scheduling function
+ that could schedule an area too early: if the area overlaps with an
+ already-started area, its begin_frame will be set to the end frame of the
+ other one. However, if any frame in the list follows that can start earlier
+ (because it does not overlap or finishes at an earlier time) than this
+ earlier end frame will be used to schedule the new area.
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 7e1558403973..973d13ffd0d3 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -576,8 +576,9 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ return false;
+ }
+
+- /* Otherwise, start at the same time as the other area. */
+- frame_begin = other->frame_begin;
++ /* Otherwise, the earliest start is the same time as that of the other
++ * area. */
++ frame_begin = max(frame_begin, other->frame_begin);
+ }
+
+ area->frame_begin = frame_begin;
+--
+2.30.2
+
+
+From 350e4ec1da7cb4fe67ccb6d54b98cfead031c500 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Sat, 11 Jun 2022 21:08:19 +0200
+Subject: [PATCH 21/37] [rockchip_ebc] The current driver iteration does not
+ guarantee consistency between the list of currently-worked on damaged areas
+ (snapshot of ctx->queue taken at the beginning of each frame) and the
+ framebuffer content (ctx->final). As such it is possible that the content of
+ the framebuffer changes before a given area can be drawn, potentially leading
+ to garbled screen content. This effects is hugely dependent on the nature of
+ drawing calls emitted by individual applications. Large scheduled areas tend
+ to be good, but if an application sends large bursts of
+ overlapping/overwriting areas then bad things happen. The bug/effect is also
+ triggered if area splitting is done to increase drawing performance.
+
+For example, this can be nicely seen under Gnome when
+chaotically moving the nautilus window.
+
+This patch is not a fix but somewhat reduces the impact by moving the
+splinlock guarding the ctx->queue so it guards both the whole
+frame-prepartion phase of the partial refresh function and the
+framebuffer blitting function.
+
+An alternative that also greatly reduces the effect is to copy the whole
+framebuffer before preparing a given frame. However, this has a huge
+performance impact and thus is not feasible if we still want to to
+real-time drawings.
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 973d13ffd0d3..3ef899c4779f 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -744,7 +744,6 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ /* Move the queued damage areas to the local list. */
+ spin_lock(&ctx->queue_lock);
+ list_splice_tail_init(&ctx->queue, &areas);
+- spin_unlock(&ctx->queue_lock);
+
+ list_for_each_entry_safe(area, next_area, &areas, list) {
+ s32 frame_delta;
+@@ -832,6 +831,8 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ dma_sync_single_for_device(dev, phase_handle,
+ ctx->phase_size, DMA_TO_DEVICE);
+
++ spin_unlock(&ctx->queue_lock);
++
+ /* if (frame) { */
+ /* if (!wait_for_completion_timeout(&ebc->display_end, */
+ /* EBC_FRAME_TIMEOUT)) */
+@@ -1448,6 +1449,7 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ ebc_plane_state = to_ebc_plane_state(plane_state);
+ vaddr = ebc_plane_state->base.data[0].vaddr;
+
++ spin_lock(&ctx->queue_lock);
+ list_for_each_entry_safe(area, next_area, &ebc_plane_state->areas, list) {
+ struct drm_rect *dst_clip = &area->clip;
+ struct drm_rect src_clip = area->clip;
+@@ -1493,10 +1495,11 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ }
+ }
+
+- if (list_empty(&ebc_plane_state->areas))
++ if (list_empty(&ebc_plane_state->areas)){
++ spin_unlock(&ctx->queue_lock);
+ return;
++ }
+
+- spin_lock(&ctx->queue_lock);
+ list_splice_tail_init(&ebc_plane_state->areas, &ctx->queue);
+ spin_unlock(&ctx->queue_lock);
+
+--
+2.30.2
+
+
+From b36084b7f777dda669cf8132f539c2ebb89dca45 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 11:05:06 +0200
+Subject: [PATCH 22/37] [rockchip_ebc] remove/comment out debug printk messages
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 11 +++--------
+ 1 file changed, 3 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 3ef899c4779f..819e4bf28595 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -206,7 +206,6 @@ static int ioctl_trigger_global_refresh(struct drm_device *dev, void *data,
+ struct rockchip_ebc *ebc = dev_get_drvdata(dev->dev);
+
+ if (args->trigger_global_refresh){
+- printk(KERN_INFO "rockchip_ebc: ioctl triggered full refresh \n");
+ spin_lock(&ebc->refresh_once_lock);
+ ebc->do_one_full_refresh = true;
+ spin_unlock(&ebc->refresh_once_lock);
+@@ -427,7 +426,7 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ struct rockchip_ebc_area *other;
+ // by default, begin now
+ u32 frame_begin = current_frame;
+- /* printk(KERN_INFO "scheduling area: %i-%i %i-%i\n", area->clip.x1, area->clip.x2, area->clip.y1, area->clip.y2); */
++ //printk(KERN_INFO "scheduling area: %i-%i %i-%i (current frame: %i)\n", area->clip.x1, area->clip.x2, area->clip.y1, area->clip.y2, current_frame);
+
+ list_for_each_entry(other, areas, list) {
+ struct drm_rect intersection;
+@@ -768,7 +767,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+
+ /* Copy ctx->final to ctx->next on the first frame. */
+ if (frame_delta == 0) {
+- printk(KERN_INFO "rockchip partial refresh starting area on frame %i (%i/%i %i/%i)\n", frame, area->clip.x1, area->clip.x2, area->clip.y1, area->clip.y2);
++ //printk(KERN_INFO "rockchip partial refresh starting area on frame %i (%i/%i %i/%i)\n", frame, area->clip.x1, area->clip.x2, area->clip.y1, area->clip.y2);
+ local_area_count += (u64) (
+ area->clip.x2 - area->clip.x1) *
+ (area->clip.y2 - area->clip.y1);
+@@ -817,6 +816,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ drm_dbg(drm, "area %p (" DRM_RECT_FMT ") finished on %u\n",
+ area, DRM_RECT_ARG(&area->clip), frame);
+
++ //printk(KERN_INFO "rockchip partial refresh stopping area on frame %i (%i/%i %i/%i)\n", frame, area->clip.x1, area->clip.x2, area->clip.y1, area->clip.y2);
+ list_del(&area->list);
+ kfree(area);
+ }
+@@ -858,7 +858,6 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ }
+ dma_unmap_single(dev, phase_handles[0], ctx->gray4_size, DMA_TO_DEVICE);
+ dma_unmap_single(dev, phase_handles[1], ctx->gray4_size, DMA_TO_DEVICE);
+- /* printk(KERN_INFO "loca area count: %llu\n", local_area_count); */
+ ctx->area_count += local_area_count;
+ }
+
+@@ -960,7 +959,6 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+ // do we need a full refresh
+ if (auto_refresh){
+ if (ctx->area_count >= refresh_threshold * one_screen_area){
+- printk(KERN_INFO "rockchip: triggering full refresh due to drawn area threshold\n");
+ spin_lock(&ebc->refresh_once_lock);
+ ebc->do_one_full_refresh = true;
+ spin_unlock(&ebc->refresh_once_lock);
+@@ -1650,15 +1648,12 @@ static int rockchip_ebc_drm_init(struct rockchip_ebc *ebc)
+ // check if there is a default off-screen
+ if (!request_firmware(&default_offscreen, "rockchip/rockchip_ebc_default_screen.bin", drm->dev))
+ {
+- printk(KERN_INFO "rockchip_ebc: default off-screen file found\n");
+ if (default_offscreen->size != 1314144)
+ drm_err(drm, "Size of default offscreen data file is not 1314144\n");
+ else {
+- printk(KERN_INFO "rockchip_ebc: loading default off-screen\n");
+ memcpy(ebc->off_screen, default_offscreen->data, 1314144);
+ }
+ } else {
+- printk(KERN_INFO "rockchip_ebc: no default off-screen file found\n");
+ // fill the off-screen with some values
+ memset(ebc->off_screen, 0xff, 1314144);
+ /* memset(ebc->off_screen, 0x00, 556144); */
+--
+2.30.2
+
+
+From 74cfa9aaf87f2f0b93a65052c248f0bd21b4b422 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 11:08:08 +0200
+Subject: [PATCH 23/37] [rockchip_ebc] move the area-splitting code to its own
+ function and hopefully fix the pointer-usage and list-handlings bugs.
+
+Also, try to split areas even if the other area was not started yet. I'm
+not really sure if this brings benefits, but the idea is that if we have
+smaller areas, then future overlaps will probably happen less.
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 265 +++++++++++++++---------
+ 1 file changed, 162 insertions(+), 103 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 819e4bf28595..52bf5d11ec57 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -415,11 +415,157 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ memcpy(ctx->prev, ctx->next, gray4_size);
+ }
+
++/*
++ * Returns true if the area was split, false otherwise
++ */
++static int try_to_split_area(
++ struct list_head *areas,
++ struct rockchip_ebc_area *area,
++ struct rockchip_ebc_area *other,
++ int * split_counter,
++ struct rockchip_ebc_area **p_next_area,
++ struct drm_rect * intersection
++ ){
++
++ // for now, min size if 2x2
++ if ((area->clip.x2 - area->clip.x1 < 2) | (area->clip.y2 - area->clip.y1 < 2))
++ return 0;
++
++ // ok, we want to split this area and start with any partial areas
++ // that are not overlapping (well, let this be decided upon at the
++ // next outer loop - we delete this area so we need not to juggle
++ // around the four areas until we found the one that is actually
++ // overlapping)
++ int xmin, xmax, ymin, ymax, xcenter, ycenter;
++
++ bool no_xsplit = false;
++ bool no_ysplit = false;
++ bool split_both = true;
++
++ xmin = area->clip.x1;
++ if (intersection->x1 > xmin)
++ xcenter = intersection->x1;
++ else
++ xcenter = intersection->x2;
++ xmax = area->clip.x2;
++
++ ymin = area->clip.y1;
++ if (intersection->y1 > ymin)
++ ycenter = intersection->y1;
++ else
++ ycenter = intersection->y2;
++ ymax = area->clip.y2;
++
++ if ((xmin == xcenter) | (xcenter == xmax)){
++ no_xsplit = true;
++ split_both = false;
++ }
++ if ((ymin == ycenter) | (ycenter == ymax)){
++ no_ysplit = true;
++ split_both = false;
++ }
++
++ // can we land here at all???
++ if (no_xsplit && no_ysplit)
++ return 0;
++
++ // we do not want to overhelm the refresh thread and limit us to a
++ // certain number of splits. The rest needs to wait
++ if (*split_counter >= split_area_limit)
++ return 0;
++
++ // we need four new rokchip_ebc_area entries that we splice into
++ // the list. Note that the currently next item shall be copied
++ // backwards because to prevent the outer list iteration from
++ // skipping over our newly created items.
++
++ struct rockchip_ebc_area * item1;
++ struct rockchip_ebc_area * item2;
++ struct rockchip_ebc_area * item3;
++ struct rockchip_ebc_area * item4;
++ item1 = kmalloc(sizeof(*item1), GFP_KERNEL);
++ if (split_both || no_xsplit)
++ item2 = kmalloc(sizeof(*item2), GFP_KERNEL);
++ if (split_both || no_ysplit)
++ item3 = kmalloc(sizeof(*item3), GFP_KERNEL);
++ if (split_both)
++ item4 = kmalloc(sizeof(*item4), GFP_KERNEL);
++
++ // TODO: Error checking!!!!
++ /* if (!area) */
++ /* return -ENOMEM; */
++
++ if (no_xsplit)
++ xcenter = xmax;
++
++ if (no_ysplit)
++ ycenter = ymax;
++
++ if (list_is_last(&area->list, areas)){
++ list_add_tail(&item1->list, areas);
++ if (split_both || no_xsplit)
++ list_add_tail(&item2->list, areas);
++ if (split_both || no_ysplit)
++ list_add_tail(&item3->list, areas);
++ if (split_both)
++ list_add_tail(&item4->list, areas);
++ }
++ else{
++ if (split_both)
++ __list_add(&item4->list, &area->list, area->list.next);
++ if (split_both || no_ysplit)
++ __list_add(&item3->list, &area->list, area->list.next);
++ if (split_both || no_xsplit)
++ __list_add(&item2->list, &area->list, area->list.next);
++ __list_add(&item1->list, &area->list, area->list.next);
++ }
++ *p_next_area = item1;
++
++ // now fill the areas
++
++ // always
++ item1->frame_begin = EBC_FRAME_PENDING;
++ item1->clip.x1 = xmin;
++ item1->clip.x2 = xcenter;
++ item1->clip.y1 = ymin;
++ item1->clip.y2 = ycenter;
++
++ if (split_both || no_xsplit){
++ // no xsplit
++ item2->frame_begin = EBC_FRAME_PENDING;
++ item2->clip.x1 = xmin;
++ item2->clip.x2 = xcenter;
++ item2->clip.y1 = ycenter;
++ item2->clip.y2 = ymax;
++ }
++
++ if (split_both || no_ysplit){
++ // no ysplit
++ item3->frame_begin = EBC_FRAME_PENDING;
++ item3->clip.x1 = xcenter;
++ item3->clip.x2 = xmax;
++ item3->clip.y1 = ymin;
++ item3->clip.y2 = ycenter;
++ }
++
++ if (split_both){
++ // both splits
++ item4->frame_begin = EBC_FRAME_PENDING;
++ item4->clip.x1 = xcenter;
++ item4->clip.x2 = xmax;
++ item4->clip.y1 = ycenter;
++ item4->clip.y2 = ymax;
++ }
++
++ (*split_counter)++;
++ return 1;
++}
++
+ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ struct rockchip_ebc_area *area,
+ struct drm_device *drm,
+ u32 current_frame, u32 num_phases,
+- struct rockchip_ebc_area *next_area,
++ struct rockchip_ebc_area **p_next_area,
+ int * split_counter
+ )
+ {
+@@ -460,109 +606,13 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ if (drm_rect_equals(&area->clip, &intersection))
+ continue;
+
+- // for now, min size if 2x2
+- if ((area->clip.x2 - area->clip.x1 < 2) | (area->clip.y2 - area->clip.y1 < 2))
+- continue;
+-
+- // ok, we want to split this area and start with any partial areas
+- // that are not overlapping (well, let this be decided upon at the
+- // next outer loop - we delete this area so we need not to juggle
+- // around the four areas until we found the one that is actually
+- // overlapping)
+- int xmin, xmax, ymin, ymax, xcenter, ycenter;
+- xmin = area->clip.x1;
+- if (intersection.x1 > xmin)
+- xcenter = intersection.x1;
+- else
+- xcenter = intersection.x2;
+- xmax = area->clip.x2;
+-
+- ymin = area->clip.y1;
+- if (intersection.y1 > ymin)
+- ycenter = intersection.y1;
+- else
+- ycenter = intersection.y2;
+- ymax = area->clip.y2;
+-
+- if ((xmin == xcenter) | (xcenter == xmax))
+- continue;
+- if ((ymin == ycenter) | (ycenter == ymax))
+- continue;
+-
+- // we do not want to overhelm the refresh thread and limit us to a
+- // certain number of splits. The rest needs to wait
+- if (*split_counter >= split_area_limit)
++ if (try_to_split_area(areas, area, other, split_counter, p_next_area, &intersection))
++ {
++ // let the outer loop delete this area
++ return false;
++ } else {
+ continue;
+-
+- // we need four new rokchip_ebc_area entries that we splice into
+- // the list. Note that the currently next item shall be copied
+- // backwards because to prevent the outer list iteration from
+- // skipping over our newly created items.
+-
+- struct rockchip_ebc_area * item1;
+- struct rockchip_ebc_area * item2;
+- struct rockchip_ebc_area * item3;
+- struct rockchip_ebc_area * item4;
+- item1 = kmalloc(sizeof(*item1), GFP_KERNEL);
+- item2 = kmalloc(sizeof(*item2), GFP_KERNEL);
+- item3 = kmalloc(sizeof(*item3), GFP_KERNEL);
+- item4 = kmalloc(sizeof(*item4), GFP_KERNEL);
+-
+- // TODO: Error checking!!!!
+- /* if (!area) */
+- /* return -ENOMEM; */
+-
+- if (list_is_last(&area->list, areas)){
+- /* printk(KERN_INFO "adding to end of list\n"); */
+- list_add_tail(&item1->list, areas);
+- list_add_tail(&item2->list, areas);
+- list_add_tail(&item3->list, areas);
+- list_add_tail(&item4->list, areas);
+- }
+- else{
+- /* printk(KERN_INFO "splicing into the middle of the list\n"); */
+- __list_add(&item4->list, areas, areas->next);
+- __list_add(&item3->list, areas, areas->next);
+- __list_add(&item2->list, areas, areas->next);
+- __list_add(&item1->list, areas, areas->next);
+ }
+- next_area = item1;
+-
+- // now fill the areas
+- /* printk(KERN_INFO "area1: %i %i %i %i\n", xmin, xcenter, ymin, ycenter); */
+- /* printk(KERN_INFO "area2: %i %i %i %i\n", xmin, xcenter, ycenter, ymax); */
+- /* printk(KERN_INFO "area3: %i %i %i %i\n", xcenter, xmax, ymin, ycenter); */
+- /* printk(KERN_INFO "area4: %i %i %i %i\n", xcenter, xmax, ycenter, ymax); */
+-
+- item1->frame_begin = EBC_FRAME_PENDING;
+- item1->clip.x1 = xmin;
+- item1->clip.x2 = xcenter;
+- item1->clip.y1 = ymin;
+- item1->clip.y2 = ycenter;
+-
+- item2->frame_begin = EBC_FRAME_PENDING;
+- item2->clip.x1 = xmin;
+- item2->clip.x2 = xcenter;
+- item2->clip.y1 = ycenter;
+- item2->clip.y2 = ymax;
+-
+- item3->frame_begin = EBC_FRAME_PENDING;
+- item3->clip.x1 = xcenter;
+- item3->clip.x2 = xmax;
+- item3->clip.y1 = ymin;
+- item3->clip.y2 = ycenter;
+-
+- item4->frame_begin = EBC_FRAME_PENDING;
+- item4->clip.x1 = xcenter;
+- item4->clip.x2 = xmax;
+- item4->clip.y1 = ycenter;
+- item4->clip.y2 = ymax;
+-
+- (*split_counter)++;
+-
+- // let the outer loop delete this area
+- return false;
+- /* continue; */
+ }
+
+ /*
+@@ -578,6 +628,15 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ /* Otherwise, the earliest start is the same time as that of the other
+ * area. */
+ frame_begin = max(frame_begin, other->frame_begin);
++
++ // try to split, otherwise continue
++ if (try_to_split_area(areas, area, other, split_counter, p_next_area, &intersection))
++ {
++ // let the outer loop delete this area
++ return false;
++ } else {
++ continue;
++ }
+ }
+
+ area->frame_begin = frame_begin;
+@@ -754,7 +813,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ */
+ if (area->frame_begin == EBC_FRAME_PENDING &&
+ !rockchip_ebc_schedule_area(&areas, area, drm, frame,
+- ebc->lut.num_phases, next_area, &split_counter)) {
++ ebc->lut.num_phases, &next_area, &split_counter)) {
+ list_del(&area->list);
+ kfree(area);
+ continue;
+--
+2.30.2
+
+
+From 491388a2f538ef97c9699c723b3b574072b0fd85 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 11:10:24 +0200
+Subject: [PATCH 24/37] [rockchip_ebc] remove comment
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 52bf5d11ec57..5d42b45abb5b 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -591,7 +591,6 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ intersection = area->clip;
+ if (!drm_rect_intersect(&intersection, &other->clip))
+ continue;
+- // we got here, so there is a collision
+
+ /* If the other area already started, wait until it finishes. */
+ if (other->frame_begin < current_frame) {
+--
+2.30.2
+
+
+From 5a177ed3f5813d31b8d2aeda46866a067f296fdd Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 11:26:13 +0200
+Subject: [PATCH 25/37] [rockchip_ebc] fix another scheduling bug: only
+ increase, but never drecrease the frame_begin number
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 5d42b45abb5b..7f5fe7252ac4 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -594,7 +594,7 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+
+ /* If the other area already started, wait until it finishes. */
+ if (other->frame_begin < current_frame) {
+- frame_begin = other_end;
++ frame_begin = max(frame_begin, other_end);
+
+ // so here we would optimally want to split the new area into three
+ // parts that do not overlap with the already-started area, and one
+--
+2.30.2
+
+
+From 35f8f647a3f7bd68cd96abee41c442abded7c2b8 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 11:26:32 +0200
+Subject: [PATCH 26/37] [rockchip_ebc] rework comment
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 7f5fe7252ac4..974e9d23c648 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -624,8 +624,8 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ return false;
+ }
+
+- /* Otherwise, the earliest start is the same time as that of the other
+- * area. */
++ /* They do overlap but are are not equal and both not started yet, so
++ * they can potentially start together */
+ frame_begin = max(frame_begin, other->frame_begin);
+
+ // try to split, otherwise continue
+--
+2.30.2
+
+
+From d4e78c0e92bec79bacd6e73d4df5a663eb1c2cc4 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 11:27:38 +0200
+Subject: [PATCH 27/37] [rockchip_ebc] even if its not really clear if it is
+ required, also sync the next-buffer to the cpu before using it
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 974e9d23c648..97173aeed53c 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -866,10 +866,12 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ */
+ if (frame_delta > last_phase) {
+ dma_sync_single_for_cpu(dev, prev_handle, gray4_size, DMA_TO_DEVICE);
++ dma_sync_single_for_cpu(dev, next_handle, gray4_size, DMA_TO_DEVICE);
+ rockchip_ebc_blit_pixels(ctx, ctx->prev,
+ ctx->next,
+ &area->clip);
+ sync_prev = true;
++ sync_prev = true;
+
+ drm_dbg(drm, "area %p (" DRM_RECT_FMT ") finished on %u\n",
+ area, DRM_RECT_ARG(&area->clip), frame);
+--
+2.30.2
+
+
+From ecbf9a93fc89fa8129bdd6ef0db4e39988d65d3d Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 12:41:15 +0200
+Subject: [PATCH 28/37] [rockchip_ebc] enable drawing of clips not aligned to
+ full bytes (i.e. even start/end coordinates).
+
+Needs more testing.
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 62 ++++++++++++++++---------
+ 1 file changed, 41 insertions(+), 21 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 97173aeed53c..4baefc8b5496 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -1418,7 +1418,10 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ const struct drm_rect *dst_clip,
+ const void *vaddr,
+ const struct drm_framebuffer *fb,
+- const struct drm_rect *src_clip)
++ const struct drm_rect *src_clip,
++ int adjust_x1,
++ int adjust_x2
++ )
+ {
+ unsigned int dst_pitch = ctx->gray4_pitch;
+ unsigned int src_pitch = fb->pitches[0];
+@@ -1428,13 +1431,9 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ int delta_x;
+ void *dst;
+
+- bool start_x_is_odd = src_clip->x1 & 1;
+- bool end_x_is_odd = src_clip->x2 & 1;
+-
+ delta_x = panel_reflection ? -1 : 1;
+ start_x = panel_reflection ? src_clip->x2 - 1 : src_clip->x1;
+
+- // I think this also works if dst_clip->x1 is odd
+ dst = ctx->final + dst_clip->y1 * dst_pitch + dst_clip->x1 / 2;
+ src = vaddr + src_clip->y1 * src_pitch + start_x * fb->format->cpp[0];
+
+@@ -1445,6 +1444,7 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ for (x = src_clip->x1; x < src_clip->x2; x += 2) {
+ u32 rgb0, rgb1;
+ u8 gray;
++ u8 tmp_pixel;
+
+ rgb0 = *sbuf; sbuf += delta_x;
+ rgb1 = *sbuf; sbuf += delta_x;
+@@ -1459,13 +1459,21 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ rgb0 >>= 28;
+ rgb1 >>= 28;
+
+- if (x == src_clip->x1 && start_x_is_odd) {
++ // Does this account for panel reflection?
++ if (x == src_clip->x1 && (adjust_x1 == 1)) {
+ // rgb0 should be filled with the content of the src pixel here
+- rgb0 = *dbuf;
++ // keep lower 4 bits
++ // I'm not sure how to directly read only one byte from the u32
++ // pointer dbuf ...
++ tmp_pixel = *dbuf & 0b00001111;
++ rgb0 = tmp_pixel;
+ }
+- if (x == src_clip->x2 && end_x_is_odd) {
+- // rgb1 should be filled with the content of the src pixel here
+- rgb1 = *dbuf;
++ if (x == src_clip->x2 && (adjust_x2 == 1)) {
++ // rgb1 should be filled with the content of the dst pixel we
++ // want to keep here
++ // keep 4 higher bits
++ tmp_pixel = *dbuf & 0b11110000;
++ rgb1 = tmp_pixel;
+ }
+
+ gray = rgb0 | rgb1 << 4;
+@@ -1511,7 +1519,9 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ list_for_each_entry_safe(area, next_area, &ebc_plane_state->areas, list) {
+ struct drm_rect *dst_clip = &area->clip;
+ struct drm_rect src_clip = area->clip;
+- int adjust;
++ int adjust_x1;
++ int adjust_x2;
++ bool clip_changed_fb;
+
+ /* Convert from plane coordinates to CRTC coordinates. */
+ drm_rect_translate(dst_clip, translate_x, translate_y);
+@@ -1519,18 +1529,20 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ /* Adjust the clips to always process full bytes (2 pixels). */
+ /* NOTE: in direct mode, the minimum block size is 4 pixels. */
+ if (direct_mode)
+- adjust = dst_clip->x1 & 3;
++ adjust_x1 = dst_clip->x1 & 3;
+ else
+- adjust = dst_clip->x1 & 1;
+- dst_clip->x1 -= adjust;
+- src_clip.x1 -= adjust;
++ adjust_x1 = dst_clip->x1 & 1;
++
++ dst_clip->x1 -= adjust_x1;
++ src_clip.x1 -= adjust_x1;
+
+ if (direct_mode)
+- adjust = ((dst_clip->x2 + 3) ^ 3) & 3;
++ adjust_x2 = ((dst_clip->x2 + 3) ^ 3) & 3;
+ else
+- adjust = dst_clip->x2 & 1;
+- dst_clip->x2 += adjust;
+- src_clip.x2 += adjust;
++ adjust_x2 = dst_clip->x2 & 1;
++
++ dst_clip->x2 += adjust_x2;
++ src_clip.x2 += adjust_x2;
+
+ if (panel_reflection) {
+ int x1 = dst_clip->x1, x2 = dst_clip->x2;
+@@ -1539,8 +1551,16 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ dst_clip->x2 = plane_state->dst.x2 - x1;
+ }
+
+- if (!rockchip_ebc_blit_fb(ctx, dst_clip, vaddr,
+- plane_state->fb, &src_clip)) {
++ clip_changed_fb = rockchip_ebc_blit_fb(ctx, dst_clip, vaddr,
++ plane_state->fb, &src_clip, adjust_x1, adjust_x2);
++
++ // reverse coordinates
++ dst_clip->x1 += adjust_x1;
++ src_clip.x1 += adjust_x1;
++ dst_clip->x2 -= adjust_x2;
++ src_clip.x2 -= adjust_x2;
++
++ if (!clip_changed_fb) {
+ drm_dbg(plane->dev, "area %p (" DRM_RECT_FMT ") <= (" DRM_RECT_FMT ") skipped\n",
+ area, DRM_RECT_ARG(&area->clip), DRM_RECT_ARG(&src_clip));
+
+--
+2.30.2
+
+
+From cbe09b1efa307db0a5dd927c74f23663c2159494 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Fri, 17 Jun 2022 12:41:58 +0200
+Subject: [PATCH 29/37] [rockchip_ebc] move the queue_lock a little bit further
+ up. Not sure if this is required, but this way we lock as soon as possible in
+ the update routine.
+
+Note that this still does not prevent the damaged-area list and the
+final framebuffer content to get out of sync during ebc refreshes.
+However, it should prevent any coherency issues and ensure consistent
+framebuffer content during each frame update.
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 4baefc8b5496..15b14acbfd2b 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -1508,6 +1508,7 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
+ ctx = to_ebc_crtc_state(crtc_state)->ctx;
+
++ spin_lock(&ctx->queue_lock);
+ drm_rect_fp_to_int(&src, &plane_state->src);
+ translate_x = plane_state->dst.x1 - src.x1;
+ translate_y = plane_state->dst.y1 - src.y1;
+@@ -1515,7 +1516,6 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ ebc_plane_state = to_ebc_plane_state(plane_state);
+ vaddr = ebc_plane_state->base.data[0].vaddr;
+
+- spin_lock(&ctx->queue_lock);
+ list_for_each_entry_safe(area, next_area, &ebc_plane_state->areas, list) {
+ struct drm_rect *dst_clip = &area->clip;
+ struct drm_rect src_clip = area->clip;
+--
+2.30.2
+
+
+From af9c4d804c7ef2efdb5ee2730b2fd9d6c6974e63 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Mon, 20 Jun 2022 13:19:31 +0200
+Subject: [PATCH 30/37] [rockchip_ebc] * add a sysfs handler
+ (/sys/module/rockchip_ebc/parameters/limit_fb_blits) to limit the numbers of
+ framebuffer blits. The default value of -1 does not limit blits at all. Can
+ be used to investigate the buffer contents while debugging complex drawing
+ chains. * add an ioctl to retrieve the final, next, prev and
+ phase[0,1] buffer contents to user space.
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 123 +++++++++++++++---------
+ include/uapi/drm/rockchip_ebc_drm.h | 12 ++-
+ 2 files changed, 91 insertions(+), 44 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 15b14acbfd2b..278a35209044 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -197,6 +197,10 @@ static int split_area_limit = 12;
+ module_param(split_area_limit, int, S_IRUGO|S_IWUSR);
+ MODULE_PARM_DESC(split_area_limit, "how many areas to split in each scheduling call");
+
++static int limit_fb_blits = -1;
++module_param(limit_fb_blits, int, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(split_area_limit, "how many fb blits to allow. -1 does not limit");
++
+ DEFINE_DRM_GEM_FOPS(rockchip_ebc_fops);
+
+ static int ioctl_trigger_global_refresh(struct drm_device *dev, void *data,
+@@ -228,11 +232,75 @@ static int ioctl_set_off_screen(struct drm_device *dev, void *data,
+ return 0;
+ }
+
++
++/**
++ * struct rockchip_ebc_ctx - context for performing display refreshes
++ *
++ * @kref: Reference count, maintained as part of the CRTC's atomic state
++ * @queue: Queue of damaged areas to be refreshed
++ * @queue_lock: Lock protecting access to @queue
++ * @prev: Display contents (Y4) before this refresh
++ * @next: Display contents (Y4) after this refresh
++ * @final: Display contents (Y4) after all pending refreshes
++ * @phase: Buffers for selecting a phase from the EBC's LUT, 1 byte/pixel
++ * @gray4_pitch: Horizontal line length of a Y4 pixel buffer in bytes
++ * @gray4_size: Size of a Y4 pixel buffer in bytes
++ * @phase_pitch: Horizontal line length of a phase buffer in bytes
++ * @phase_size: Size of a phase buffer in bytes
++ */
++struct rockchip_ebc_ctx {
++ struct kref kref;
++ struct list_head queue;
++ spinlock_t queue_lock;
++ u8 *prev;
++ u8 *next;
++ u8 *final;
++ u8 *phase[2];
++ u32 gray4_pitch;
++ u32 gray4_size;
++ u32 phase_pitch;
++ u32 phase_size;
++ u64 area_count;
++};
++
++struct ebc_crtc_state {
++ struct drm_crtc_state base;
++ struct rockchip_ebc_ctx *ctx;
++};
++
++static inline struct ebc_crtc_state *
++to_ebc_crtc_state(struct drm_crtc_state *crtc_state)
++{
++ return container_of(crtc_state, struct ebc_crtc_state, base);
++}
++static int ioctl_extract_fbs(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_rockchip_ebc_extract_fbs *args = data;
++ struct rockchip_ebc *ebc = dev_get_drvdata(dev->dev);
++ int copy_result = 0;
++ struct rockchip_ebc_ctx * ctx;
++
++ // todo: use access_ok here
++ access_ok(args->ptr_next, 1313144);
++ ctx = to_ebc_crtc_state(READ_ONCE(ebc->crtc.state))->ctx;
++ copy_result |= copy_to_user(args->ptr_prev, ctx->prev, 1313144);
++ copy_result |= copy_to_user(args->ptr_next, ctx->next, 1313144);
++ copy_result |= copy_to_user(args->ptr_final, ctx->final, 1313144);
++
++ copy_result |= copy_to_user(args->ptr_phase1, ctx->phase[0], 2 * 1313144);
++ copy_result |= copy_to_user(args->ptr_phase2, ctx->phase[1], 2 * 1313144);
++
++ return copy_result;
++}
++
+ static const struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {
+ DRM_IOCTL_DEF_DRV(ROCKCHIP_EBC_GLOBAL_REFRESH, ioctl_trigger_global_refresh,
+ DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(ROCKCHIP_EBC_OFF_SCREEN, ioctl_set_off_screen,
+ DRM_RENDER_ALLOW),
++ DRM_IOCTL_DEF_DRV(ROCKCHIP_EBC_EXTRACT_FBS, ioctl_extract_fbs,
++ DRM_RENDER_ALLOW),
+ };
+
+ static const struct drm_driver rockchip_ebc_drm_driver = {
+@@ -268,36 +336,6 @@ struct rockchip_ebc_area {
+ u32 frame_begin;
+ };
+
+-/**
+- * struct rockchip_ebc_ctx - context for performing display refreshes
+- *
+- * @kref: Reference count, maintained as part of the CRTC's atomic state
+- * @queue: Queue of damaged areas to be refreshed
+- * @queue_lock: Lock protecting access to @queue
+- * @prev: Display contents (Y4) before this refresh
+- * @next: Display contents (Y4) after this refresh
+- * @final: Display contents (Y4) after all pending refreshes
+- * @phase: Buffers for selecting a phase from the EBC's LUT, 1 byte/pixel
+- * @gray4_pitch: Horizontal line length of a Y4 pixel buffer in bytes
+- * @gray4_size: Size of a Y4 pixel buffer in bytes
+- * @phase_pitch: Horizontal line length of a phase buffer in bytes
+- * @phase_size: Size of a phase buffer in bytes
+- */
+-struct rockchip_ebc_ctx {
+- struct kref kref;
+- struct list_head queue;
+- spinlock_t queue_lock;
+- u8 *prev;
+- u8 *next;
+- u8 *final;
+- u8 *phase[2];
+- u32 gray4_pitch;
+- u32 gray4_size;
+- u32 phase_pitch;
+- u32 phase_size;
+- u64 area_count;
+-};
+-
+ static void rockchip_ebc_ctx_free(struct rockchip_ebc_ctx *ctx)
+ {
+ struct rockchip_ebc_area *area;
+@@ -360,17 +398,6 @@ static void rockchip_ebc_ctx_release(struct kref *kref)
+ * CRTC
+ */
+
+-struct ebc_crtc_state {
+- struct drm_crtc_state base;
+- struct rockchip_ebc_ctx *ctx;
+-};
+-
+-static inline struct ebc_crtc_state *
+-to_ebc_crtc_state(struct drm_crtc_state *crtc_state)
+-{
+- return container_of(crtc_state, struct ebc_crtc_state, base);
+-}
+-
+ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+ struct rockchip_ebc_ctx *ctx,
+ dma_addr_t next_handle,
+@@ -1551,8 +1578,18 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ dst_clip->x2 = plane_state->dst.x2 - x1;
+ }
+
+- clip_changed_fb = rockchip_ebc_blit_fb(ctx, dst_clip, vaddr,
+- plane_state->fb, &src_clip, adjust_x1, adjust_x2);
++ if (limit_fb_blits != 0){
++ printk(KERN_INFO "atomic update: blitting: %i\n", limit_fb_blits);
++ clip_changed_fb = rockchip_ebc_blit_fb(ctx, dst_clip, vaddr,
++ plane_state->fb, &src_clip, adjust_x1, adjust_x2);
++ // the counter should only reach 0 here, -1 can only be externally set
++ limit_fb_blits -= (limit_fb_blits > 0) ? 1 : 0;
++ } else {
++ // we do not want to blit anything
++ printk(KERN_INFO "atomic update: not blitting: %i\n", limit_fb_blits);
++ clip_changed_fb = false;
++ }
++
+
+ // reverse coordinates
+ dst_clip->x1 += adjust_x1;
+diff --git a/include/uapi/drm/rockchip_ebc_drm.h b/include/uapi/drm/rockchip_ebc_drm.h
+index befa62a68be0..5e8c87ae6af2 100644
+--- a/include/uapi/drm/rockchip_ebc_drm.h
++++ b/include/uapi/drm/rockchip_ebc_drm.h
+@@ -17,9 +17,19 @@ struct drm_rockchip_ebc_off_screen {
+ char * ptr_screen_content;
+ };
+
+-#define DRM_ROCKCHIP_EBC_NUM_IOCTLS 0x02
++struct drm_rockchip_ebc_extract_fbs {
++ char * ptr_prev;
++ char * ptr_next;
++ char * ptr_final;
++ char * ptr_phase1;
++ char * ptr_phase2;
++};
++
++
++#define DRM_ROCKCHIP_EBC_NUM_IOCTLS 0x03
+
+ #define DRM_IOCTL_ROCKCHIP_EBC_GLOBAL_REFRESH DRM_IOWR(DRM_COMMAND_BASE + 0x00, struct drm_rockchip_ebc_trigger_global_refresh)
+ #define DRM_IOCTL_ROCKCHIP_EBC_OFF_SCREEN DRM_IOWR(DRM_COMMAND_BASE + 0x01, struct drm_rockchip_ebc_off_screen)
++#define DRM_IOCTL_ROCKCHIP_EBC_EXTRACT_FBS DRM_IOWR(DRM_COMMAND_BASE + 0x02, struct drm_rockchip_ebc_extract_fbs)
+
+ #endif /* __ROCKCHIP_EBC_DRM_H__*/
+--
+2.30.2
+
+
+From d238a50853c30c65bee6e7a6a2d5565250980247 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Wed, 22 Jun 2022 10:17:10 +0200
+Subject: [PATCH 31/37] [rockchip_ebc] fix compiler warnings by moving variable
+ declaration to the top of the functions
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 44 ++++++++++++++-----------
+ 1 file changed, 24 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 278a35209044..d0670d482432 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -453,6 +453,22 @@ static int try_to_split_area(
+ struct rockchip_ebc_area **p_next_area,
+ struct drm_rect * intersection
+ ){
++ int xmin, xmax, ymin, ymax, xcenter, ycenter;
++
++ bool no_xsplit = false;
++ bool no_ysplit = false;
++ bool split_both = true;
++
++ struct rockchip_ebc_area * item1;
++ struct rockchip_ebc_area * item2;
++ struct rockchip_ebc_area * item3;
++ struct rockchip_ebc_area * item4;
++
++ // we do not want to overhelm the refresh thread and limit us to a
++ // certain number of splits. The rest needs to wait
++ if (*split_counter >= split_area_limit)
++ return 0;
++
+
+ // for now, min size if 2x2
+ if ((area->clip.x2 - area->clip.x1 < 2) | (area->clip.y2 - area->clip.y1 < 2))
+@@ -463,12 +479,6 @@ static int try_to_split_area(
+ // next outer loop - we delete this area so we need not to juggle
+ // around the four areas until we found the one that is actually
+ // overlapping)
+- int xmin, xmax, ymin, ymax, xcenter, ycenter;
+-
+- bool no_xsplit = false;
+- bool no_ysplit = false;
+- bool split_both = true;
+-
+ xmin = area->clip.x1;
+ if (intersection->x1 > xmin)
+ xcenter = intersection->x1;
+@@ -496,20 +506,11 @@ static int try_to_split_area(
+ if (no_xsplit && no_ysplit)
+ return 0;
+
+- // we do not want to overhelm the refresh thread and limit us to a
+- // certain number of splits. The rest needs to wait
+- if (*split_counter >= split_area_limit)
+- return 0;
+-
+ // we need four new rokchip_ebc_area entries that we splice into
+ // the list. Note that the currently next item shall be copied
+ // backwards because to prevent the outer list iteration from
+ // skipping over our newly created items.
+
+- struct rockchip_ebc_area * item1;
+- struct rockchip_ebc_area * item2;
+- struct rockchip_ebc_area * item3;
+- struct rockchip_ebc_area * item4;
+ item1 = kmalloc(sizeof(*item1), GFP_KERNEL);
+ if (split_both || no_xsplit)
+ item2 = kmalloc(sizeof(*item2), GFP_KERNEL);
+@@ -752,17 +753,20 @@ static void rockchip_ebc_blit_pixels(const struct rockchip_ebc_ctx *ctx,
+
+ unsigned int x1_bytes = clip->x1 / 2;
+ unsigned int x2_bytes = clip->x2 / 2;
+- // the integer division floors by default, but we want to include the last
+- // byte (partially)
+- if (end_x_is_odd)
+- x2_bytes++;
+
+ unsigned int pitch = ctx->gray4_pitch;
+- unsigned int width = x2_bytes - x1_bytes;
++ unsigned int width;
+ const u8 *src_line;
+ unsigned int y;
+ u8 *dst_line;
+
++ // the integer division floors by default, but we want to include the last
++ // byte (partially)
++ if (end_x_is_odd)
++ x2_bytes++;
++
++ width = x2_bytes - x1_bytes;
++
+ dst_line = dst + clip->y1 * pitch + x1_bytes;
+ src_line = src + clip->y1 * pitch + x1_bytes;
+
+--
+2.30.2
+
+
+From e0434586f31db9beb962f8185fd567a1eae4a879 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Wed, 22 Jun 2022 10:19:06 +0200
+Subject: [PATCH 32/37] [rockchip_ebc] add debug printk statements but comment
+ them out
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 28 +++++++++++++++++++++----
+ 1 file changed, 24 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index d0670d482432..491efd20f2e9 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -605,24 +605,32 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ list_for_each_entry(other, areas, list) {
+ struct drm_rect intersection;
+ u32 other_end;
++ //printk(KERN_INFO " test other area: %i-%i %i-%i\n", other->clip.x1, other->clip.x2, other->clip.y1, other->clip.y2);
+
+ /* Only consider areas before this one in the list. */
+- if (other == area)
++ if (other == area){
++ //printk(KERN_INFO " other==area\n");
+ break;
++ }
+
+ /* Skip areas that finish refresh before this area begins. */
+ other_end = other->frame_begin + num_phases;
+- if (other_end <= frame_begin)
++ if (other_end <= frame_begin){
++ //printk(KERN_INFO " other finishes before: %i %i\n", other_end, frame_begin);
+ continue;
++ }
+
+ /* If there is no collision, the areas are independent. */
+ intersection = area->clip;
+- if (!drm_rect_intersect(&intersection, &other->clip))
++ if (!drm_rect_intersect(&intersection, &other->clip)){
++ //printk(KERN_INFO " no collision\n");
+ continue;
++ }
+
+ /* If the other area already started, wait until it finishes. */
+ if (other->frame_begin < current_frame) {
+ frame_begin = max(frame_begin, other_end);
++ //printk(KERN_INFO " other already started, setting to %i\n", frame_begin);
+
+ // so here we would optimally want to split the new area into three
+ // parts that do not overlap with the already-started area, and one
+@@ -630,12 +638,15 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ // later, but the other three should start immediately.
+
+ // if the area is equal to the clip, continue
+- if (drm_rect_equals(&area->clip, &intersection))
++ if (drm_rect_equals(&area->clip, &intersection)){
++ //printk(KERN_INFO " intersection completely contains area\n");
+ continue;
++ }
+
+ if (try_to_split_area(areas, area, other, split_counter, p_next_area, &intersection))
+ {
+ // let the outer loop delete this area
++ //printk(KERN_INFO " dropping after trying to split\n");
+ return false;
+ } else {
+ continue;
+@@ -649,17 +660,20 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ if (drm_rect_equals(&area->clip, &intersection)) {
+ drm_dbg(drm, "area %p (" DRM_RECT_FMT ") dropped, inside " DRM_RECT_FMT "\n",
+ area, DRM_RECT_ARG(&area->clip), DRM_RECT_ARG(&other->clip));
++ //printk(KERN_INFO " dropping\n");
+ return false;
+ }
+
+ /* They do overlap but are are not equal and both not started yet, so
+ * they can potentially start together */
+ frame_begin = max(frame_begin, other->frame_begin);
++ //printk(KERN_INFO " setting to: %i\n", frame_begin);
+
+ // try to split, otherwise continue
+ if (try_to_split_area(areas, area, other, split_counter, p_next_area, &intersection))
+ {
+ // let the outer loop delete this area
++ //printk(KERN_INFO " dropping after trying to split\n");
+ return false;
+ } else {
+ continue;
+@@ -667,6 +681,7 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas,
+ }
+
+ area->frame_begin = frame_begin;
++ //printk(KERN_INFO " area scheduled to start at frame: %i (current: %i)\n", frame_begin, current_frame);
+
+ return true;
+ }
+@@ -1547,12 +1562,15 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ ebc_plane_state = to_ebc_plane_state(plane_state);
+ vaddr = ebc_plane_state->base.data[0].vaddr;
+
++ //printk(KERN_INFO "new fb clips\n");
+ list_for_each_entry_safe(area, next_area, &ebc_plane_state->areas, list) {
+ struct drm_rect *dst_clip = &area->clip;
+ struct drm_rect src_clip = area->clip;
+ int adjust_x1;
+ int adjust_x2;
+ bool clip_changed_fb;
++ //printk(KERN_INFO " checking from list: (" DRM_RECT_FMT ") \n",
++ /* DRM_RECT_ARG(&area->clip)); */
+
+ /* Convert from plane coordinates to CRTC coordinates. */
+ drm_rect_translate(dst_clip, translate_x, translate_y);
+@@ -1611,6 +1629,8 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ } else {
+ drm_dbg(plane->dev, "area %p (" DRM_RECT_FMT ") <= (" DRM_RECT_FMT ") blitted\n",
+ area, DRM_RECT_ARG(&area->clip), DRM_RECT_ARG(&src_clip));
++ //printk(KERN_INFO " adding to list: (" DRM_RECT_FMT ") <= (" DRM_RECT_FMT ") blitted\n",
++ /* DRM_RECT_ARG(&area->clip), DRM_RECT_ARG(&src_clip)); */
+ }
+ }
+
+--
+2.30.2
+
+
+From bb4e13779de8d427868da024e781cff625e8287b Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Wed, 22 Jun 2022 10:21:42 +0200
+Subject: [PATCH 33/37] [rockchip_ebc] add commented-out spin_unlock to
+ indicate old position
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 491efd20f2e9..351cae36bc4d 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -847,6 +847,7 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ /* Move the queued damage areas to the local list. */
+ spin_lock(&ctx->queue_lock);
+ list_splice_tail_init(&ctx->queue, &areas);
++ /* spin_unlock(&ctx->queue_lock); */
+
+ list_for_each_entry_safe(area, next_area, &areas, list) {
+ s32 frame_delta;
+--
+2.30.2
+
+
+From 340c5eec973094f937d67527f868a46e2729cbba Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Wed, 22 Jun 2022 10:22:18 +0200
+Subject: [PATCH 34/37] [rockchip_ebc] not sure if this has any bad
+ consequences, but also wait on the hardware to finish the first frame
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index 351cae36bc4d..e8d108727c75 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -957,11 +957,14 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+ regmap_write(ebc->regmap, EBC_DSP_START,
+ ebc->dsp_start |
+ EBC_DSP_START_DSP_FRM_START);
+- if (frame) {
+- if (!wait_for_completion_timeout(&ebc->display_end,
+- EBC_FRAME_TIMEOUT))
+- drm_err(drm, "Frame %d timed out!\n", frame);
+- }
++ /* if (frame) { */
++ /* if (!wait_for_completion_timeout(&ebc->display_end, */
++ /* EBC_FRAME_TIMEOUT)) */
++ /* drm_err(drm, "Frame %d timed out!\n", frame); */
++ /* } */
++ if (!wait_for_completion_timeout(&ebc->display_end,
++ EBC_FRAME_TIMEOUT))
++ drm_err(drm, "Frame %d timed out!\n", frame);
+ }
+ dma_unmap_single(dev, phase_handles[0], ctx->gray4_size, DMA_TO_DEVICE);
+ dma_unmap_single(dev, phase_handles[1], ctx->gray4_size, DMA_TO_DEVICE);
+--
+2.30.2
+
+
+From 3242d3d78bdc68361c165838f59724732cdbb0e3 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Wed, 22 Jun 2022 10:23:03 +0200
+Subject: [PATCH 35/37] [rockchip_ebc] hopefully fix the blitting routine for
+ odd start/end coordinates and panel_reflection=1
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index e8d108727c75..f30010151c02 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -1480,9 +1480,13 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ u8 changed = 0;
+ int delta_x;
+ void *dst;
++ int test1, test2;
+
+ delta_x = panel_reflection ? -1 : 1;
+ start_x = panel_reflection ? src_clip->x2 - 1 : src_clip->x1;
++ // depending on the direction we must either save the first or the last bit
++ test1 = panel_reflection ? adjust_x1 : adjust_x2;
++ test2 = panel_reflection ? adjust_x2 : adjust_x1;
+
+ dst = ctx->final + dst_clip->y1 * dst_pitch + dst_clip->x1 / 2;
+ src = vaddr + src_clip->y1 * src_pitch + start_x * fb->format->cpp[0];
+@@ -1509,8 +1513,7 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ rgb0 >>= 28;
+ rgb1 >>= 28;
+
+- // Does this account for panel reflection?
+- if (x == src_clip->x1 && (adjust_x1 == 1)) {
++ if (x == src_clip->x1 && (test1 == 1)) {
+ // rgb0 should be filled with the content of the src pixel here
+ // keep lower 4 bits
+ // I'm not sure how to directly read only one byte from the u32
+@@ -1518,7 +1521,7 @@ static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx,
+ tmp_pixel = *dbuf & 0b00001111;
+ rgb0 = tmp_pixel;
+ }
+- if (x == src_clip->x2 && (adjust_x2 == 1)) {
++ if (x == src_clip->x2 && (test2 == 1)) {
+ // rgb1 should be filled with the content of the dst pixel we
+ // want to keep here
+ // keep 4 higher bits
+--
+2.30.2
+
+
+From 2b41563e202a5d55e19fad1164ecfc89b1e43210 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Wed, 22 Jun 2022 10:24:07 +0200
+Subject: [PATCH 36/37] [rockchip_ebc] add commented-out printk statements
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index f30010151c02..a72d1e219691 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -1608,18 +1608,17 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ }
+
+ if (limit_fb_blits != 0){
+- printk(KERN_INFO "atomic update: blitting: %i\n", limit_fb_blits);
++ //printk(KERN_INFO "atomic update: blitting: %i\n", limit_fb_blits);
+ clip_changed_fb = rockchip_ebc_blit_fb(ctx, dst_clip, vaddr,
+ plane_state->fb, &src_clip, adjust_x1, adjust_x2);
+ // the counter should only reach 0 here, -1 can only be externally set
+ limit_fb_blits -= (limit_fb_blits > 0) ? 1 : 0;
+ } else {
+ // we do not want to blit anything
+- printk(KERN_INFO "atomic update: not blitting: %i\n", limit_fb_blits);
++ //printk(KERN_INFO "atomic update: not blitting: %i\n", limit_fb_blits);
+ clip_changed_fb = false;
+ }
+
+-
+ // reverse coordinates
+ dst_clip->x1 += adjust_x1;
+ src_clip.x1 += adjust_x1;
+--
+2.30.2
+
+
+From 917a31bb1ac2eb3adbe272fd79d40ac8b21169d9 Mon Sep 17 00:00:00 2001
+From: Maximilian Weigand <mweigand@mweigand.net>
+Date: Wed, 22 Jun 2022 10:25:04 +0200
+Subject: [PATCH 37/37] [rockchip_ebc] add commented-out old position of lock
+
+---
+ drivers/gpu/drm/rockchip/rockchip_ebc.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+index a72d1e219691..62daf5c107c4 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
++++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
+@@ -1645,6 +1645,7 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
+ return;
+ }
+
++ /* spin_lock(&ctx->queue_lock); */
+ list_splice_tail_init(&ebc_plane_state->areas, &ctx->queue);
+ spin_unlock(&ctx->queue_lock);
+
+--
+2.30.2
+
diff --git a/gnu/packages/patches/linux-libre-arm64-pinenote-touchscreen-1.patch b/gnu/packages/patches/linux-libre-arm64-pinenote-touchscreen-1.patch
new file mode 100644
index 0000000000..20026b4dd5
--- /dev/null
+++ b/gnu/packages/patches/linux-libre-arm64-pinenote-touchscreen-1.patch
@@ -0,0 +1,976 @@
+From a24cb29eca1a72afb1037f5468d3036b34ea1b66 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Myl=C3=A8ne=20Josserand?= <mylene.josserand@bootlin.com>
+Date: Sun, 9 Jan 2022 21:53:28 +1000
+Subject: [PATCH] Input: Add driver for Cypress Generation 5 touchscreen
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This is the basic driver for the Cypress TrueTouch Gen5 touchscreen
+controllers. This driver supports only the I2C bus but it uses regmap
+so SPI support could be added later.
+The touchscreen can retrieve some defined zone that are handled as
+buttons (according to the hardware). That is why it handles
+button and multitouch events.
+
+Reviewed-by: Maxime Ripard <maxime.ripard@bootlin.com>
+Signed-off-by: Mylène Josserand <mylene.josserand@bootlin.com>
+Signed-off-by: Alistair Francis <alistair@alistair23.me>
+Tested-by: Andreas Kemnade <andreas@kemnade.info> # Kobo Clara HD
+---
+ drivers/input/touchscreen/Kconfig | 16 +
+ drivers/input/touchscreen/Makefile | 1 +
+ drivers/input/touchscreen/cyttsp5.c | 902 ++++++++++++++++++++++++++++
+ 3 files changed, 919 insertions(+)
+ create mode 100644 drivers/input/touchscreen/cyttsp5.c
+
+diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
+index 2f6adfb7b938..eb4e1b156683 100644
+--- a/drivers/input/touchscreen/Kconfig
++++ b/drivers/input/touchscreen/Kconfig
+@@ -284,6 +284,22 @@ config TOUCHSCREEN_CYTTSP4_SPI
+ To compile this driver as a module, choose M here: the
+ module will be called cyttsp4_spi.
+
++config TOUCHSCREEN_CYTTSP5
++ tristate "Cypress TrueTouch Gen5 Touchscreen Driver"
++ depends on I2C
++ select REGMAP_I2C
++ select CRC_ITU_T
++ help
++ Driver for Parade TrueTouch Standard Product Generation 5
++ touchscreen controllers. I2C bus interface support only.
++
++ Say Y here if you have a Cypress Gen5 touchscreen.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called cyttsp5.
++
+ config TOUCHSCREEN_DA9034
+ tristate "Touchscreen support for Dialog Semiconductor DA9034"
+ depends on PMIC_DA903X
+diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
+index 39a8127cf6a5..0ea5c47f7fd9 100644
+--- a/drivers/input/touchscreen/Makefile
++++ b/drivers/input/touchscreen/Makefile
+@@ -30,6 +30,7 @@ obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o
+ obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_CORE) += cyttsp4_core.o
+ obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_I2C) += cyttsp4_i2c.o cyttsp_i2c_common.o
+ obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_SPI) += cyttsp4_spi.o
++obj-$(CONFIG_TOUCHSCREEN_CYTTSP5) += cyttsp5.o
+ obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
+ obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o
+ obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
+diff --git a/drivers/input/touchscreen/cyttsp5.c b/drivers/input/touchscreen/cyttsp5.c
+new file mode 100644
+index 000000000000..3ac45108090c
+--- /dev/null
++++ b/drivers/input/touchscreen/cyttsp5.c
+@@ -0,0 +1,902 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Parade TrueTouch(TM) Standard Product V5 Module.
++ *
++ * Copyright (C) 2015 Parade Technologies
++ * Copyright (C) 2012-2015 Cypress Semiconductor
++ * Copyright (C) 2018 Bootlin
++ *
++ * Authors: Mylène Josserand <mylene.josserand@bootlin.com>
++ * Alistair Francis <alistair@alistair23.me>
++ */
++
++#include <linux/crc-itu-t.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/gpio/consumer.h>
++#include <linux/input/mt.h>
++#include <linux/input/touchscreen.h>
++#include <linux/interrupt.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/regmap.h>
++#include <asm/unaligned.h>
++
++#define CYTTSP5_NAME "cyttsp5"
++#define CY_I2C_DATA_SIZE (2 * 256)
++#define HID_VERSION 0x0100
++#define CY_MAX_INPUT 512
++#define CYTTSP5_PREALLOCATED_CMD_BUFFER 32
++#define CY_BITS_PER_BTN 1
++#define CY_NUM_BTN_EVENT_ID GENMASK(CY_BITS_PER_BTN, 0)
++
++#define MAX_AREA 255
++#define HID_OUTPUT_BL_SOP 0x1
++#define HID_OUTPUT_BL_EOP 0x17
++#define HID_OUTPUT_BL_LAUNCH_APP 0x3B
++#define HID_OUTPUT_BL_LAUNCH_APP_SIZE 11
++#define HID_OUTPUT_GET_SYSINFO 0x2
++#define HID_OUTPUT_GET_SYSINFO_SIZE 5
++#define HID_OUTPUT_MAX_CMD_SIZE 12
++
++#define HID_DESC_REG 0x1
++#define HID_INPUT_REG 0x3
++#define HID_OUTPUT_REG 0x4
++
++#define REPORT_ID_TOUCH 0x1
++#define REPORT_ID_BTN 0x3
++#define REPORT_SIZE_5 5
++#define REPORT_SIZE_8 8
++#define REPORT_SIZE_16 16
++
++/* Touch reports offsets */
++/* Header offsets */
++#define TOUCH_REPORT_DESC_HDR_CONTACTCOUNT 16
++/* Record offsets */
++#define TOUCH_REPORT_DESC_CONTACTID 8
++#define TOUCH_REPORT_DESC_X 16
++#define TOUCH_REPORT_DESC_Y 32
++#define TOUCH_REPORT_DESC_P 48
++#define TOUCH_REPORT_DESC_MAJ 56
++#define TOUCH_REPORT_DESC_MIN 64
++
++/* HID */
++#define HID_TOUCH_REPORT_ID 0x1
++#define HID_BTN_REPORT_ID 0x3
++#define HID_APP_RESPONSE_REPORT_ID 0x1F
++#define HID_APP_OUTPUT_REPORT_ID 0x2F
++#define HID_BL_RESPONSE_REPORT_ID 0x30
++#define HID_BL_OUTPUT_REPORT_ID 0x40
++
++#define HID_OUTPUT_RESPONSE_REPORT_OFFSET 2
++#define HID_OUTPUT_RESPONSE_CMD_OFFSET 4
++#define HID_OUTPUT_RESPONSE_CMD_MASK GENMASK(6, 0)
++
++#define HID_SYSINFO_SENSING_OFFSET 33
++#define HID_SYSINFO_BTN_OFFSET 48
++#define HID_SYSINFO_BTN_MASK GENMASK(7, 0)
++#define HID_SYSINFO_MAX_BTN 8
++
++#define CY_HID_OUTPUT_TIMEOUT_MS 200
++#define CY_HID_OUTPUT_GET_SYSINFO_TIMEOUT_MS 3000
++#define CY_HID_GET_HID_DESCRIPTOR_TIMEOUT_MS 4000
++
++/* maximum number of concurrent tracks */
++#define TOUCH_REPORT_SIZE 10
++#define TOUCH_INPUT_HEADER_SIZE 7
++#define BTN_REPORT_SIZE 9
++#define BTN_INPUT_HEADER_SIZE 5
++
++#define MAX_CY_TCH_T_IDS 32
++
++/* All usage pages for Touch Report */
++#define TOUCH_REPORT_USAGE_PG_X 0x00010030
++#define TOUCH_REPORT_USAGE_PG_Y 0x00010031
++#define TOUCH_REPORT_USAGE_PG_P 0x000D0030
++#define TOUCH_REPORT_USAGE_PG_CONTACTID 0x000D0051
++#define TOUCH_REPORT_USAGE_PG_CONTACTCOUNT 0x000D0054
++#define TOUCH_REPORT_USAGE_PG_MAJ 0xFF010062
++#define TOUCH_REPORT_USAGE_PG_MIN 0xFF010063
++#define TOUCH_COL_USAGE_PG 0x000D0022
++
++/* System Information interface definitions */
++struct cyttsp5_sensing_conf_data_dev {
++ u8 electrodes_x;
++ u8 electrodes_y;
++ __le16 len_x;
++ __le16 len_y;
++ __le16 res_x;
++ __le16 res_y;
++ __le16 max_z;
++ u8 origin_x;
++ u8 origin_y;
++ u8 btn;
++ u8 scan_mode;
++ u8 max_num_of_tch_per_refresh_cycle;
++} __packed;
++
++struct cyttsp5_sensing_conf_data {
++ u16 res_x;
++ u16 res_y;
++ u16 max_z;
++ u16 len_x;
++ u16 len_y;
++ u8 origin_x;
++ u8 origin_y;
++ u8 max_tch;
++};
++
++enum cyttsp5_tch_abs { /* for ordering within the extracted touch data array */
++ CY_TCH_X, /* X */
++ CY_TCH_Y, /* Y */
++ CY_TCH_P, /* P (Z) */
++ CY_TCH_T, /* TOUCH ID */
++ CY_TCH_MAJ, /* TOUCH_MAJOR */
++ CY_TCH_MIN, /* TOUCH_MINOR */
++ CY_TCH_NUM_ABS
++};
++
++struct cyttsp5_tch_abs_params {
++ size_t ofs; /* abs byte offset */
++ size_t size; /* size in bits */
++ size_t min; /* min value */
++ size_t max; /* max value */
++ size_t bofs; /* bit offset */
++};
++
++struct cyttsp5_touch {
++ int abs[CY_TCH_NUM_ABS];
++};
++
++struct cyttsp5_sysinfo {
++ struct cyttsp5_sensing_conf_data sensing_conf_data;
++ int num_btns;
++ struct cyttsp5_tch_abs_params tch_hdr;
++ struct cyttsp5_tch_abs_params tch_abs[CY_TCH_NUM_ABS];
++ u32 key_code[HID_SYSINFO_MAX_BTN];
++};
++
++struct cyttsp5_hid_desc {
++ __le16 hid_desc_len;
++ u8 packet_id;
++ u8 reserved_byte;
++ __le16 bcd_version;
++ __le16 report_desc_len;
++ __le16 report_desc_register;
++ __le16 input_register;
++ __le16 max_input_len;
++ __le16 output_register;
++ __le16 max_output_len;
++ __le16 command_register;
++ __le16 data_register;
++ __le16 vendor_id;
++ __le16 product_id;
++ __le16 version_id;
++ u8 reserved[4];
++} __packed;
++
++struct cyttsp5 {
++ struct device *dev;
++ struct completion cmd_done;
++ struct cyttsp5_sysinfo sysinfo;
++ struct cyttsp5_hid_desc hid_desc;
++ u8 cmd_buf[CYTTSP5_PREALLOCATED_CMD_BUFFER];
++ u8 input_buf[CY_MAX_INPUT];
++ u8 response_buf[CY_MAX_INPUT];
++ struct gpio_desc *reset_gpio;
++ struct input_dev *input;
++ char phys[NAME_MAX];
++ int num_prv_rec;
++ struct regmap *regmap;
++ struct touchscreen_properties prop;
++ struct regulator *vdd;
++};
++
++/*
++ * For what is understood in the datasheet, the register does not
++ * matter. For consistency, use the Input Register address
++ * but it does mean anything to the device. The important data
++ * to send is the I2C address
++ */
++static int cyttsp5_read(struct cyttsp5 *ts, u8 *buf, u32 max)
++{
++ int error;
++ u32 size;
++ u8 temp[2];
++
++ /* Read the frame to retrieve the size */
++ error = regmap_bulk_read(ts->regmap, HID_INPUT_REG, temp, sizeof(temp));
++ if (error)
++ return error;
++
++ size = get_unaligned_le16(temp);
++ if (!size || size == 2)
++ return 0;
++
++ if (size > max)
++ return -EINVAL;
++
++ /* Get the real value */
++ return regmap_bulk_read(ts->regmap, HID_INPUT_REG, buf, size);
++}
++
++static int cyttsp5_write(struct cyttsp5 *ts, unsigned int reg, u8 *data,
++ size_t size)
++{
++ u8 cmd[HID_OUTPUT_MAX_CMD_SIZE];
++
++ if (size + 1 > HID_OUTPUT_MAX_CMD_SIZE)
++ return -E2BIG;
++
++ /* High bytes of register address needed as first byte of cmd */
++ cmd[0] = (reg >> 8) & 0xFF ;
++
++ /* Copy the rest of the data */
++ if (data)
++ memcpy(&cmd[1], data, size);
++
++ /*
++ * The hardware wants to receive a frame with the address register
++ * contained in the first two bytes. As the regmap_write function
++ * add the register adresse in the frame, we use the low byte as
++ * first frame byte for the address register and the first
++ * data byte is the high register + left of the cmd to send
++ */
++ return regmap_bulk_write(ts->regmap, reg & 0xFF, cmd, size + 1);
++}
++
++static void cyttsp5_get_touch_axis(int *axis, int size, int max, u8 *xy_data,
++ int bofs)
++{
++ int nbyte;
++
++ for (nbyte = 0, *axis = 0; nbyte < size; nbyte++)
++ *axis += ((xy_data[nbyte] >> bofs) << (nbyte * 8));
++
++ *axis &= max - 1;
++}
++
++static void cyttsp5_get_touch_record(struct cyttsp5 *ts,
++ struct cyttsp5_touch *touch, u8 *xy_data)
++{
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++ enum cyttsp5_tch_abs abs;
++
++ for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++)
++ cyttsp5_get_touch_axis(&touch->abs[abs],
++ si->tch_abs[abs].size,
++ si->tch_abs[abs].max,
++ xy_data + si->tch_abs[abs].ofs,
++ si->tch_abs[abs].bofs);
++}
++
++static void cyttsp5_get_mt_touches(struct cyttsp5 *ts,
++ struct cyttsp5_touch *tch, int num_cur_tch)
++{
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++ int i, t = 0, offset = 0;
++ DECLARE_BITMAP(ids, MAX_CY_TCH_T_IDS);
++ u8 *tch_addr;
++ int tmp;
++
++ bitmap_zero(ids, MAX_CY_TCH_T_IDS);
++ memset(tch->abs, 0, sizeof(tch->abs));
++
++ switch (ts->input_buf[2]) {
++ case HID_TOUCH_REPORT_ID:
++ offset = TOUCH_INPUT_HEADER_SIZE;
++ break;
++ case HID_BTN_REPORT_ID:
++ offset = BTN_INPUT_HEADER_SIZE;
++ break;
++ }
++
++ for (i = 0; i < num_cur_tch; i++) {
++ tch_addr = ts->input_buf + offset + (i * TOUCH_REPORT_SIZE);
++ cyttsp5_get_touch_record(ts, tch, tch_addr);
++
++ /* Convert MAJOR/MINOR from mm to resolution */
++ tmp = tch->abs[CY_TCH_MAJ] * 100 * si->sensing_conf_data.res_x;
++ tch->abs[CY_TCH_MAJ] = tmp / si->sensing_conf_data.len_x;
++ tmp = tch->abs[CY_TCH_MIN] * 100 * si->sensing_conf_data.res_x;
++ tch->abs[CY_TCH_MIN] = tmp / si->sensing_conf_data.len_x;
++
++ t = tch->abs[CY_TCH_T];
++ input_mt_slot(ts->input, t);
++ input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true);
++ __set_bit(t, ids);
++
++ /* position and pressure fields */
++ touchscreen_report_pos(ts->input, &ts->prop,
++ tch->abs[CY_TCH_X], tch->abs[CY_TCH_Y],
++ true);
++ input_report_abs(ts->input, ABS_MT_PRESSURE,
++ tch->abs[CY_TCH_P]);
++
++ /* Get the extended touch fields */
++ input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
++ tch->abs[CY_TCH_MAJ]);
++ input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
++ tch->abs[CY_TCH_MIN]);
++ }
++
++ ts->num_prv_rec = num_cur_tch;
++}
++
++static int cyttsp5_mt_attention(struct device *dev)
++{
++ struct cyttsp5 *ts = dev_get_drvdata(dev);
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++ int max_tch = si->sensing_conf_data.max_tch;
++ struct cyttsp5_touch tch;
++ u8 num_cur_tch;
++
++ cyttsp5_get_touch_axis((int *) &num_cur_tch, si->tch_hdr.size,
++ si->tch_hdr.max,
++ ts->input_buf + 3 + si->tch_hdr.ofs,
++ si->tch_hdr.bofs);
++
++ if (num_cur_tch > max_tch) {
++ dev_err(dev, "Num touch err detected (n=%d)\n", num_cur_tch);
++ num_cur_tch = max_tch;
++ }
++
++ if (num_cur_tch == 0 && ts->num_prv_rec == 0)
++ return 0;
++
++ /* extract xy_data for all currently reported touches */
++ if (num_cur_tch)
++ cyttsp5_get_mt_touches(ts, &tch, num_cur_tch);
++
++ input_mt_sync_frame(ts->input);
++ input_sync(ts->input);
++
++ return 0;
++}
++
++static int cyttsp5_setup_input_device(struct device *dev)
++{
++ struct cyttsp5 *ts = dev_get_drvdata(dev);
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++ int max_x, max_y, max_p;
++ int max_x_tmp, max_y_tmp;
++ int error;
++
++ max_x_tmp = si->sensing_conf_data.res_x;
++ max_y_tmp = si->sensing_conf_data.res_y;
++ max_x = max_x_tmp - 1;
++ max_y = max_y_tmp - 1;
++ max_p = si->sensing_conf_data.max_z;
++
++ input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, max_x, 0, 0);
++ input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, max_y, 0, 0);
++ input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, max_p, 0, 0);
++
++ input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0);
++ input_set_abs_params(ts->input, ABS_MT_TOUCH_MINOR, 0, MAX_AREA, 0, 0);
++
++ error = input_mt_init_slots(ts->input, si->tch_abs[CY_TCH_T].max,
++ INPUT_MT_DROP_UNUSED | INPUT_MT_DIRECT);
++ if (error < 0)
++ return error;
++
++ error = input_register_device(ts->input);
++ if (error < 0)
++ dev_err(dev, "Error, failed register input device r=%d\n", error);
++
++ return error;
++}
++
++static int cyttsp5_parse_dt_key_code(struct device *dev)
++{
++ struct cyttsp5 *ts = dev_get_drvdata(dev);
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++
++ if (!si->num_btns)
++ return 0;
++
++ /* Initialize the button to RESERVED */
++ memset32(si->key_code, KEY_RESERVED, si->num_btns);
++
++ return device_property_read_u32_array(dev, "linux,keycodes",
++ si->key_code, si->num_btns);
++}
++
++static int cyttsp5_btn_attention(struct device *dev)
++{
++ struct cyttsp5 *ts = dev_get_drvdata(dev);
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++ int cur_btn, offset = 0;
++ int cur_btn_state;
++
++ switch (ts->input_buf[2]) {
++ case HID_TOUCH_REPORT_ID:
++ offset = TOUCH_INPUT_HEADER_SIZE;
++ break;
++ case HID_BTN_REPORT_ID:
++ offset = BTN_INPUT_HEADER_SIZE;
++ break;
++ }
++
++ if (ts->input_buf[2] != HID_BTN_REPORT_ID || !si->num_btns)
++ return 0;
++
++ /* extract button press/release touch information */
++ for (cur_btn = 0; cur_btn < si->num_btns; cur_btn++) {
++ /* Get current button state */
++ cur_btn_state = (ts->input_buf[offset] >> (cur_btn * CY_BITS_PER_BTN))
++ & CY_NUM_BTN_EVENT_ID;
++
++ input_report_key(ts->input, si->key_code[cur_btn],
++ cur_btn_state);
++ input_sync(ts->input);
++ }
++
++ return 0;
++}
++
++static int cyttsp5_validate_cmd_response(struct cyttsp5 *ts, u8 code)
++{
++ u16 size, crc;
++ u8 status, report_id;
++ int command_code;
++
++ size = get_unaligned_le16(&ts->response_buf[0]);
++
++ if (!size)
++ return 0;
++
++ report_id = ts->response_buf[HID_OUTPUT_RESPONSE_REPORT_OFFSET];
++
++ switch (report_id) {
++ case HID_BL_RESPONSE_REPORT_ID: {
++ if (ts->response_buf[4] != HID_OUTPUT_BL_SOP) {
++ dev_err(ts->dev, "HID output response, wrong SOP\n");
++ return -EPROTO;
++ }
++
++ if (ts->response_buf[size - 1] != HID_OUTPUT_BL_EOP) {
++ dev_err(ts->dev, "HID output response, wrong EOP\n");
++ return -EPROTO;
++ }
++
++ crc = crc_itu_t(0xFFFF, &ts->response_buf[4], size - 7);
++ if (get_unaligned_le16(&ts->response_buf[size - 3]) != crc) {
++ dev_err(ts->dev, "HID output response, wrong CRC 0x%X\n",
++ crc);
++ return -EPROTO;
++ }
++
++ status = ts->response_buf[5];
++ if (status) {
++ dev_err(ts->dev, "HID output response, ERROR:%d\n",
++ status);
++ return -EPROTO;
++ }
++ break;
++ }
++ case HID_APP_RESPONSE_REPORT_ID: {
++ command_code = ts->response_buf[HID_OUTPUT_RESPONSE_CMD_OFFSET]
++ & HID_OUTPUT_RESPONSE_CMD_MASK;
++ if (command_code != code) {
++ dev_err(ts->dev,
++ "HID output response, wrong command_code:%X\n",
++ command_code);
++ return -EPROTO;
++ }
++ break;
++ }
++ }
++
++ return 0;
++}
++
++static void cyttsp5_si_get_btn_data(struct cyttsp5 *ts)
++{
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++ unsigned int btns = ts->response_buf[HID_SYSINFO_BTN_OFFSET]
++ & HID_SYSINFO_BTN_MASK;
++
++ si->num_btns = hweight8(btns);
++}
++
++static int cyttsp5_get_sysinfo_regs(struct cyttsp5 *ts)
++{
++ struct cyttsp5_sensing_conf_data *scd = &ts->sysinfo.sensing_conf_data;
++ struct cyttsp5_sensing_conf_data_dev *scd_dev =
++ (struct cyttsp5_sensing_conf_data_dev *)
++ &ts->response_buf[HID_SYSINFO_SENSING_OFFSET];
++
++ cyttsp5_si_get_btn_data(ts);
++
++ scd->max_tch = scd_dev->max_num_of_tch_per_refresh_cycle;
++ scd->res_x = get_unaligned_le16(&scd_dev->res_x);
++ scd->res_y = get_unaligned_le16(&scd_dev->res_y);
++ scd->max_z = get_unaligned_le16(&scd_dev->max_z);
++ scd->len_x = get_unaligned_le16(&scd_dev->len_x);
++ scd->len_y = get_unaligned_le16(&scd_dev->len_y);
++
++ return 0;
++}
++
++static int cyttsp5_hid_output_get_sysinfo(struct cyttsp5 *ts)
++{
++ int rc;
++ u8 cmd[HID_OUTPUT_GET_SYSINFO_SIZE];
++
++ /* HI bytes of Output register address */
++ put_unaligned_le16(HID_OUTPUT_GET_SYSINFO_SIZE, cmd);
++ cmd[2] = HID_APP_OUTPUT_REPORT_ID;
++ cmd[3] = 0x0; /* Reserved */
++ cmd[4] = HID_OUTPUT_GET_SYSINFO;
++
++ rc = cyttsp5_write(ts, HID_OUTPUT_REG, cmd,
++ HID_OUTPUT_GET_SYSINFO_SIZE);
++ if (rc) {
++ dev_err(ts->dev, "Failed to write command %d", rc);
++ return rc;
++ }
++
++ rc = wait_for_completion_interruptible_timeout(&ts->cmd_done,
++ msecs_to_jiffies(CY_HID_OUTPUT_GET_SYSINFO_TIMEOUT_MS));
++ if (rc <= 0) {
++ dev_err(ts->dev, "HID output cmd execution timed out\n");
++ rc = -ETIMEDOUT;
++ return rc;
++ }
++
++ rc = cyttsp5_validate_cmd_response(ts, HID_OUTPUT_GET_SYSINFO);
++ if (rc) {
++ dev_err(ts->dev, "Validation of the response failed\n");
++ return rc;
++ }
++
++ return cyttsp5_get_sysinfo_regs(ts);
++}
++
++static int cyttsp5_hid_output_bl_launch_app(struct cyttsp5 *ts)
++{
++ int rc;
++ u8 cmd[HID_OUTPUT_BL_LAUNCH_APP];
++ u16 crc;
++
++ put_unaligned_le16(HID_OUTPUT_BL_LAUNCH_APP_SIZE, cmd);
++ cmd[2] = HID_BL_OUTPUT_REPORT_ID;
++ cmd[3] = 0x0; /* Reserved */
++ cmd[4] = HID_OUTPUT_BL_SOP;
++ cmd[5] = HID_OUTPUT_BL_LAUNCH_APP;
++ put_unaligned_le16(0x00, &cmd[6]);
++ crc = crc_itu_t(0xFFFF, &cmd[4], 4);
++ put_unaligned_le16(crc, &cmd[8]);
++ cmd[10] = HID_OUTPUT_BL_EOP;
++
++ rc = cyttsp5_write(ts, HID_OUTPUT_REG, cmd,
++ HID_OUTPUT_BL_LAUNCH_APP_SIZE);
++ if (rc) {
++ dev_err(ts->dev, "Failed to write command %d", rc);
++ return rc;
++ }
++
++ rc = wait_for_completion_interruptible_timeout(&ts->cmd_done,
++ msecs_to_jiffies(CY_HID_OUTPUT_TIMEOUT_MS));
++ if (rc <= 0) {
++ dev_err(ts->dev, "HID output cmd execution timed out\n");
++ rc = -ETIMEDOUT;
++ return rc;
++ }
++
++ rc = cyttsp5_validate_cmd_response(ts, HID_OUTPUT_BL_LAUNCH_APP);
++ if (rc) {
++ dev_err(ts->dev, "Validation of the response failed\n");
++ return rc;
++ }
++
++ return 0;
++}
++
++static int cyttsp5_get_hid_descriptor(struct cyttsp5 *ts,
++ struct cyttsp5_hid_desc *desc)
++{
++ struct device *dev = ts->dev;
++ __le16 hid_desc_register = HID_DESC_REG;
++ int rc;
++ u8 cmd[2];
++
++ /* Set HID descriptor register */
++ memcpy(cmd, &hid_desc_register, sizeof(hid_desc_register));
++
++ rc = cyttsp5_write(ts, HID_DESC_REG, NULL, 0);
++ if (rc) {
++ dev_err(dev, "Failed to get HID descriptor, rc=%d\n", rc);
++ return rc;
++ }
++
++ rc = wait_for_completion_interruptible_timeout(&ts->cmd_done,
++ msecs_to_jiffies(CY_HID_GET_HID_DESCRIPTOR_TIMEOUT_MS));
++ if (rc <= 0) {
++ dev_err(ts->dev, "HID get descriptor timed out\n");
++ rc = -ETIMEDOUT;
++ return rc;
++ }
++
++ memcpy(desc, ts->response_buf, sizeof(*desc));
++
++ /* Check HID descriptor length and version */
++ if (le16_to_cpu(desc->hid_desc_len) != sizeof(*desc) ||
++ le16_to_cpu(desc->bcd_version) != HID_VERSION) {
++ dev_err(dev, "Unsupported HID version\n");
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++static int fill_tch_abs(struct cyttsp5_tch_abs_params *tch_abs, int report_size,
++ int offset)
++{
++ tch_abs->ofs = offset / 8;
++ tch_abs->size = report_size / 8;
++ if (report_size % 8)
++ tch_abs->size += 1;
++ tch_abs->min = 0;
++ tch_abs->max = 1 << report_size;
++ tch_abs->bofs = offset - (tch_abs->ofs << 3);
++
++ return 0;
++}
++
++static irqreturn_t cyttsp5_handle_irq(int irq, void *handle)
++{
++ struct cyttsp5 *ts = handle;
++ int report_id;
++ int size;
++ int error;
++
++ error = cyttsp5_read(ts, ts->input_buf, CY_MAX_INPUT);
++ if (error)
++ return IRQ_HANDLED;
++
++ size = get_unaligned_le16(&ts->input_buf[0]);
++ if (size == 0) {
++ /* reset */
++ report_id = 0;
++ size = 2;
++ } else {
++ report_id = ts->input_buf[2];
++ }
++
++ switch (report_id) {
++ case HID_TOUCH_REPORT_ID:
++ cyttsp5_mt_attention(ts->dev);
++ break;
++ case HID_BTN_REPORT_ID:
++ cyttsp5_btn_attention(ts->dev);
++ break;
++ default:
++ /* It is not an input but a command response */
++ memcpy(ts->response_buf, ts->input_buf, size);
++ complete(&ts->cmd_done);
++ }
++
++ return IRQ_HANDLED;
++}
++
++static int cyttsp5_deassert_int(struct cyttsp5 *ts)
++{
++ u16 size;
++ u8 buf[2];
++ int error;
++
++ error = regmap_bulk_read(ts->regmap, HID_INPUT_REG, buf, sizeof(buf));
++ if (error < 0)
++ return error;
++
++ size = get_unaligned_le16(&buf[0]);
++ if (size == 2 || size == 0)
++ return 0;
++
++ return -EINVAL;
++}
++
++static int cyttsp5_fill_all_touch(struct cyttsp5 *ts)
++{
++ struct cyttsp5_sysinfo *si = &ts->sysinfo;
++
++ fill_tch_abs(&si->tch_abs[CY_TCH_X], REPORT_SIZE_16,
++ TOUCH_REPORT_DESC_X);
++ fill_tch_abs(&si->tch_abs[CY_TCH_Y], REPORT_SIZE_16,
++ TOUCH_REPORT_DESC_Y);
++ fill_tch_abs(&si->tch_abs[CY_TCH_P], REPORT_SIZE_8,
++ TOUCH_REPORT_DESC_P);
++ fill_tch_abs(&si->tch_abs[CY_TCH_T], REPORT_SIZE_5,
++ TOUCH_REPORT_DESC_CONTACTID);
++ fill_tch_abs(&si->tch_hdr, REPORT_SIZE_5,
++ TOUCH_REPORT_DESC_HDR_CONTACTCOUNT);
++ fill_tch_abs(&si->tch_abs[CY_TCH_MAJ], REPORT_SIZE_8,
++ TOUCH_REPORT_DESC_MAJ);
++ fill_tch_abs(&si->tch_abs[CY_TCH_MIN], REPORT_SIZE_8,
++ TOUCH_REPORT_DESC_MIN);
++
++ return 0;
++}
++
++static int cyttsp5_startup(struct cyttsp5 *ts)
++{
++ int error;
++
++ error = cyttsp5_deassert_int(ts);
++ if (error) {
++ dev_err(ts->dev, "Error on deassert int r=%d\n", error);
++ return -ENODEV;
++ }
++
++ /*
++ * Launch the application as the device starts in bootloader mode
++ * because of a power-on-reset
++ */
++ error = cyttsp5_hid_output_bl_launch_app(ts);
++ if (error < 0) {
++ dev_err(ts->dev, "Error on launch app r=%d\n", error);
++ return error;
++ }
++
++ error = cyttsp5_get_hid_descriptor(ts, &ts->hid_desc);
++ if (error < 0) {
++ dev_err(ts->dev, "Error on getting HID descriptor r=%d\n", error);
++ return error;
++ }
++
++ error = cyttsp5_fill_all_touch(ts);
++ if (error < 0) {
++ dev_err(ts->dev, "Error on report descriptor r=%d\n", error);
++ return error;
++ }
++
++ error = cyttsp5_hid_output_get_sysinfo(ts);
++ if (error) {
++ dev_err(ts->dev, "Error on getting sysinfo r=%d\n", error);
++ return error;
++ }
++
++ return error;
++}
++
++static void cyttsp5_cleanup(void *data)
++{
++ struct cyttsp5 *ts = data;
++
++ regulator_disable(ts->vdd);
++}
++
++static int cyttsp5_probe(struct device *dev, struct regmap *regmap, int irq,
++ const char *name)
++{
++ struct cyttsp5 *ts;
++ struct cyttsp5_sysinfo *si;
++ int error, i;
++
++ ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
++ if (!ts)
++ return -ENOMEM;
++
++ /* Initialize device info */
++ ts->regmap = regmap;
++ ts->dev = dev;
++ si = &ts->sysinfo;
++ dev_set_drvdata(dev, ts);
++
++ init_completion(&ts->cmd_done);
++
++ /* Power up the device */
++ ts->vdd = devm_regulator_get(dev, "vdd");
++ if (IS_ERR(ts->vdd)) {
++ error = PTR_ERR(ts->vdd);
++ return error;
++ }
++
++ error = devm_add_action_or_reset(dev, cyttsp5_cleanup, ts);
++ if (error) {
++ return error;
++ }
++
++ error = regulator_enable(ts->vdd);
++ if (error) {
++ return error;
++ }
++
++ ts->input = devm_input_allocate_device(dev);
++ if (!ts->input) {
++ dev_err(dev, "Error, failed to allocate input device\n");
++ return -ENODEV;
++ }
++
++ ts->input->name = "cyttsp5";
++ scnprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
++ ts->input->phys = ts->phys;
++ input_set_drvdata(ts->input, ts);
++
++ /* Reset the gpio to be in a reset state */
++ ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
++ if (IS_ERR(ts->reset_gpio)) {
++ error = PTR_ERR(ts->reset_gpio);
++ dev_err(dev, "Failed to request reset gpio, error %d\n", error);
++ return error;
++ }
++ gpiod_set_value(ts->reset_gpio, 0);
++
++ /* Need a delay to have device up */
++ msleep(20);
++
++ error = devm_request_threaded_irq(dev, irq, NULL, cyttsp5_handle_irq,
++ IRQF_ONESHOT, name, ts);
++ if (error) {
++ dev_err(dev, "unable to request IRQ\n");
++ return error;
++ }
++
++ error = cyttsp5_startup(ts);
++ if (error) {
++ dev_err(ts->dev, "Fail initial startup r=%d\n", error);
++ return error;
++ }
++
++ error = cyttsp5_parse_dt_key_code(dev);
++ if (error < 0) {
++ dev_err(ts->dev, "Error while parsing dts %d\n", error);
++ return error;
++ }
++
++ touchscreen_parse_properties(ts->input, true, &ts->prop);
++
++ __set_bit(EV_KEY, ts->input->evbit);
++ for (i = 0; i < si->num_btns; i++)
++ __set_bit(si->key_code[i], ts->input->keybit);
++
++ return cyttsp5_setup_input_device(dev);
++}
++
++static int cyttsp5_i2c_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
++{
++ struct regmap *regmap;
++ static const struct regmap_config config = {
++ .reg_bits = 8,
++ .val_bits = 8,
++ };
++
++ regmap = devm_regmap_init_i2c(client, &config);
++ if (IS_ERR(regmap)) {
++ dev_err(&client->dev, "regmap allocation failed: %ld\n",
++ PTR_ERR(regmap));
++ return PTR_ERR(regmap);
++ }
++
++ return cyttsp5_probe(&client->dev, regmap, client->irq, client->name);
++}
++
++static const struct of_device_id cyttsp5_of_match[] = {
++ { .compatible = "cypress,tt21000", },
++ { }
++};
++MODULE_DEVICE_TABLE(of, cyttsp5_of_match);
++
++static const struct i2c_device_id cyttsp5_i2c_id[] = {
++ { CYTTSP5_NAME, 0, },
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, cyttsp5_i2c_id);
++
++static struct i2c_driver cyttsp5_i2c_driver = {
++ .driver = {
++ .name = CYTTSP5_NAME,
++ .of_match_table = cyttsp5_of_match,
++ },
++ .probe = cyttsp5_i2c_probe,
++ .id_table = cyttsp5_i2c_id,
++};
++module_i2c_driver(cyttsp5_i2c_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Touchscreen driver for Cypress TrueTouch Gen 5 Product");
++MODULE_AUTHOR("Mylène Josserand <mylene.josserand@bootlin.com>");
+--
+GitLab
+
diff --git a/gnu/packages/patches/linux-libre-arm64-pinenote-touchscreen-2.patch b/gnu/packages/patches/linux-libre-arm64-pinenote-touchscreen-2.patch
new file mode 100644
index 0000000000..9ccef92ad7
--- /dev/null
+++ b/gnu/packages/patches/linux-libre-arm64-pinenote-touchscreen-2.patch
@@ -0,0 +1,108 @@
+From d6bb8a6b5a5210fea70bc590350bfca3a9e3a7a2 Mon Sep 17 00:00:00 2001
+From: Peter Geis <pgwipeout@gmail.com>
+Date: Sat, 15 Jan 2022 21:50:45 -0500
+Subject: [PATCH] Input: cyttsp5: support touchscreen device tree overrides
+
+It is possible for the cyttsp5 chip to not have a configuration burned
+to it.
+This leads to a sitatuion where all calibration values return zero,
+leading to a broken touchscreen configuration.
+
+The current driver does not support utilizing overrides from the device
+tree.
+Extend the driver to support this, and permit it to do some basic sanity
+checking of the values for the touchscreen and abort if they are
+invalid.
+
+Signed-off-by: Peter Geis <pgwipeout@gmail.com>
+---
+ drivers/input/touchscreen/cyttsp5.c | 62 ++++++++++++++++++++++++++---
+ 1 file changed, 57 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/input/touchscreen/cyttsp5.c b/drivers/input/touchscreen/cyttsp5.c
+index 3ac45108090c..e837985d199a 100644
+--- a/drivers/input/touchscreen/cyttsp5.c
++++ b/drivers/input/touchscreen/cyttsp5.c
+@@ -507,15 +507,66 @@ static int cyttsp5_get_sysinfo_regs(struct cyttsp5 *ts)
+ struct cyttsp5_sensing_conf_data_dev *scd_dev =
+ (struct cyttsp5_sensing_conf_data_dev *)
+ &ts->response_buf[HID_SYSINFO_SENSING_OFFSET];
++ u32 tmp;
+
+ cyttsp5_si_get_btn_data(ts);
+
+ scd->max_tch = scd_dev->max_num_of_tch_per_refresh_cycle;
+- scd->res_x = get_unaligned_le16(&scd_dev->res_x);
+- scd->res_y = get_unaligned_le16(&scd_dev->res_y);
+- scd->max_z = get_unaligned_le16(&scd_dev->max_z);
+- scd->len_x = get_unaligned_le16(&scd_dev->len_x);
+- scd->len_y = get_unaligned_le16(&scd_dev->len_y);
++
++ if (scd->max_tch == 0) {
++ dev_dbg(ts->dev, "Max touch points cannot be zero\n");
++ scd->max_tch = 2;
++ }
++
++ if(device_property_read_u32(ts->dev, "touchscreen-size-x", &tmp))
++ scd->res_x = get_unaligned_le16(&scd_dev->res_x);
++ else
++ scd->res_x = tmp;
++
++ if (scd->res_x == 0) {
++ dev_err(ts->dev, "ABS_X cannot be zero\n");
++ return -ENODATA;
++ }
++
++ if(device_property_read_u32(ts->dev, "touchscreen-size-y", &tmp))
++ scd->res_y = get_unaligned_le16(&scd_dev->res_y);
++ else
++ scd->res_y = tmp;
++
++ if (scd->res_y == 0) {
++ dev_err(ts->dev, "ABS_Y cannot be zero\n");
++ return -ENODATA;
++ }
++
++ if(device_property_read_u32(ts->dev, "touchscreen-max-pressure", &tmp))
++ scd->max_z = get_unaligned_le16(&scd_dev->max_z);
++ else
++ scd->max_z = tmp;
++
++ if (scd->max_z == 0) {
++ dev_err(ts->dev, "ABS_PRESSURE cannot be zero\n");
++ return -ENODATA;
++ }
++
++ if(device_property_read_u32(ts->dev, "touchscreen-x-mm", &tmp))
++ scd->len_x = get_unaligned_le16(&scd_dev->len_x);
++ else
++ scd->len_x = tmp;
++
++ if (scd->len_x == 0) {
++ dev_dbg(ts->dev, "Touchscreen size x cannot be zero\n");
++ scd->len_x = scd->res_x + 1;
++ }
++
++ if(device_property_read_u32(ts->dev, "touchscreen-y-mm", &tmp))
++ scd->len_y = get_unaligned_le16(&scd_dev->len_y);
++ else
++ scd->len_y = tmp;
++
++ if (scd->len_y == 0) {
++ dev_dbg(ts->dev, "Touchscreen size y cannot be zero\n");
++ scd->len_y = scd->res_y + 1;
++ }
+
+ return 0;
+ }
+@@ -877,6 +928,7 @@ static int cyttsp5_i2c_probe(struct i2c_client *client,
+
+ static const struct of_device_id cyttsp5_of_match[] = {
+ { .compatible = "cypress,tt21000", },
++ { .compatible = "cypress,tma448", },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, cyttsp5_of_match);
+--
+GitLab
+
diff --git a/gnu/packages/patches/linux-libre-arm64-rockchip-add-hdmi-sound.patch b/gnu/packages/patches/linux-libre-arm64-rockchip-add-hdmi-sound.patch
new file mode 100644
index 0000000000..8479c1ee11
--- /dev/null
+++ b/gnu/packages/patches/linux-libre-arm64-rockchip-add-hdmi-sound.patch
@@ -0,0 +1,40 @@
+From 41b224b927be41acb19b1af869394a62bc686b92 Mon Sep 17 00:00:00 2001
+From: Dan Johansen <strit@manjaro.org>
+Date: Fri, 10 Jun 2022 16:13:23 +0200
+Subject: [PATCH 1/2] arm64: dts: rockchip: Add HDMI sound node to Quartz64-B
+
+Signed-off-by: Dan Johansen <strit@manjaro.org>
+---
+ arch/arm64/boot/dts/rockchip/rk3566-quartz64-b.dts | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-b.dts b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-b.dts
+index 8d05d8a44699..f7a70470a90e 100644
+--- a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-b.dts
++++ b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-b.dts
+@@ -203,6 +203,10 @@ hdmi_out_con: endpoint {
+ };
+ };
+
++&hdmi_sound {
++ status = "okay";
++};
++
+ &i2c0 {
+ status = "okay";
+
+@@ -478,6 +482,10 @@ &i2c5 {
+ status = "disabled";
+ };
+
++&i2s0_8ch {
++ status = "okay";
++};
++
+ &mdio1 {
+ rgmii_phy1: ethernet-phy@1 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+--
+2.36.1
+
+
--
2.36.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2022-07-01 20:43 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-06-30 11:20 Building linux and u-boot for aarch64 phodina via
2022-06-30 11:57 ` phodina
2022-06-30 19:04 ` linux and u-boot for pinenote-rk3568 Vagrant Cascadian
2022-06-30 19:58 ` Vagrant Cascadian
2022-07-01 20:42 ` phodina
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/guix.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.