Re: [PATCH 3/3] drm/amd/display: Support DRM_AMD_DC_FP on RISC-V

2023-12-11 Thread Samuel Holland
Hi Alex,

On 2023-12-11 9:17 AM, Alex Deucher wrote:
> On Sun, Dec 10, 2023 at 5:10 AM Samuel Holland
>  wrote:
>>
>> Hi Arnd,
>>
>> On 2023-12-09 2:38 PM, Arnd Bergmann wrote:
>>> On Fri, Dec 8, 2023, at 06:04, Samuel Holland wrote:
>>>> On 2023-11-29 6:42 PM, Nathan Chancellor wrote:
>>>>> On Thu, Nov 23, 2023 at 02:23:01PM +, Conor Dooley wrote:
>>>>>> On Tue, Nov 21, 2023 at 07:05:15PM -0800, Samuel Holland wrote:
>>>>>>> RISC-V uses kernel_fpu_begin()/kernel_fpu_end() like several other
>>>>>>> architectures. Enabling hardware FP requires overriding the ISA string
>>>>>>> for the relevant compilation units.
>>>>>>
>>>>>> Ah yes, bringing the joy of frame-larger-than warnings to RISC-V:
>>>>>> ../drivers/gpu/drm/amd/amdgpu/../display/dc/dml/dcn32/display_mode_vba_32.c:58:13:
>>>>>>  warning: stack frame size (2416) exceeds limit (2048) in 
>>>>>> 'DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerformanceCalculation'
>>>>>>  [-Wframe-larger-than]
>>>>>
>>>>> :(
>>>>>
>>>>>> Nathan, have you given up on these being sorted out?
>>>>>
>>>>> Does your configuration have KASAN (I don't think RISC-V supports
>>>>> KCSAN)? It is possible that dml/dcn32 needs something similar to commit
>>>>> 6740ec97bcdb ("drm/amd/display: Increase frame warning limit with KASAN
>>>>> or KCSAN in dml2")?
>>>>>
>>>>> I am not really interested in playing whack-a-mole with these warnings
>>>>> like I have done in the past for the reasons I outlined here:
>>>>>
>>>>> https://lore.kernel.org/20231019205117.GA839902@dev-arch.thelio-3990X/
>>>>
>>>> I also see one of these with clang 17 even with KASAN disabled:
>>>>
>>>> drivers/gpu/drm/amd/amdgpu/../display/dc/dml/dcn32/display_mode_vba_32.c:37:6:
>>>> warning: stack frame size (2208) exceeds limit (2048) in 
>>>> 'dml32_recalculate'
>>>> [-Wframe-larger-than]
>>>> void dml32_recalculate(struct display_mode_lib *mode_lib)
>>>>
>>>>  ^
>>>> 1532/2208 (69.38%) spills, 676/2208 (30.62%) variables
>>>>
>>>> So I'm in favor of just raising the limit for these files for clang, like 
>>>> you
>>>> suggested in the linked thread.
>>>
>>> How about just adding a BUG_ON(IS_ENABLED(CONFIG_RISCV))
>>> in that function? That should also avoid the build failure
>>> but give a better indication of where the problem is
>>> if someone actually runs into that function and triggers
>>> a runtime stack overflow.
>>
>> Won't that break actual users of the driver, trading an unlikely but
>> theoretically possible stack overflow for a guaranteed crash? The intent of 
>> this
>> series is that I have one of these GPUs plugged in to a RISC-V board, and I 
>> want
>> to use it.
> 
> Does this patch address the issue?
> https://gitlab.freedesktop.org/agd5f/linux/-/commit/72ada8603e36291ad91e4f40f10ef742ef79bc4e

No, I get the warning without any of these debugging options enabled. I can
reproduce with just defconfig + CONFIG_DRM_AMDGPU=m when built with clang 17.

Regards,
Samuel



Re: [PATCH 3/3] drm/amd/display: Support DRM_AMD_DC_FP on RISC-V

2023-12-10 Thread Samuel Holland
Hi Arnd,

On 2023-12-09 2:38 PM, Arnd Bergmann wrote:
> On Fri, Dec 8, 2023, at 06:04, Samuel Holland wrote:
>> On 2023-11-29 6:42 PM, Nathan Chancellor wrote:
>>> On Thu, Nov 23, 2023 at 02:23:01PM +, Conor Dooley wrote:
>>>> On Tue, Nov 21, 2023 at 07:05:15PM -0800, Samuel Holland wrote:
>>>>> RISC-V uses kernel_fpu_begin()/kernel_fpu_end() like several other
>>>>> architectures. Enabling hardware FP requires overriding the ISA string
>>>>> for the relevant compilation units.
>>>>
>>>> Ah yes, bringing the joy of frame-larger-than warnings to RISC-V:
>>>> ../drivers/gpu/drm/amd/amdgpu/../display/dc/dml/dcn32/display_mode_vba_32.c:58:13:
>>>>  warning: stack frame size (2416) exceeds limit (2048) in 
>>>> 'DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerformanceCalculation'
>>>>  [-Wframe-larger-than]
>>>
>>> :(
>>>
>>>> Nathan, have you given up on these being sorted out?
>>>
>>> Does your configuration have KASAN (I don't think RISC-V supports
>>> KCSAN)? It is possible that dml/dcn32 needs something similar to commit
>>> 6740ec97bcdb ("drm/amd/display: Increase frame warning limit with KASAN
>>> or KCSAN in dml2")?
>>>
>>> I am not really interested in playing whack-a-mole with these warnings
>>> like I have done in the past for the reasons I outlined here:
>>>
>>> https://lore.kernel.org/20231019205117.GA839902@dev-arch.thelio-3990X/
>>
>> I also see one of these with clang 17 even with KASAN disabled:
>>
>> drivers/gpu/drm/amd/amdgpu/../display/dc/dml/dcn32/display_mode_vba_32.c:37:6:
>> warning: stack frame size (2208) exceeds limit (2048) in 'dml32_recalculate'
>> [-Wframe-larger-than]
>> void dml32_recalculate(struct display_mode_lib *mode_lib)
>>
>>  ^
>> 1532/2208 (69.38%) spills, 676/2208 (30.62%) variables
>>
>> So I'm in favor of just raising the limit for these files for clang, like you
>> suggested in the linked thread.
> 
> How about just adding a BUG_ON(IS_ENABLED(CONFIG_RISCV))
> in that function? That should also avoid the build failure
> but give a better indication of where the problem is
> if someone actually runs into that function and triggers
> a runtime stack overflow.

Won't that break actual users of the driver, trading an unlikely but
theoretically possible stack overflow for a guaranteed crash? The intent of this
series is that I have one of these GPUs plugged in to a RISC-V board, and I want
to use it.

Regards,
Samuel



Re: [PATCH 1/3] riscv: Add support for kernel-mode FPU

2023-12-08 Thread Samuel Holland
Hi Christoph,

On 2023-11-22 2:33 AM, Christoph Hellwig wrote:
> On Tue, Nov 21, 2023 at 07:05:13PM -0800, Samuel Holland wrote:
>> +static inline void kernel_fpu_begin(void)
>> +{
>> +preempt_disable();
>> +fstate_save(current, task_pt_regs(current));
>> +csr_set(CSR_SSTATUS, SR_FS);
>> +}
>> +
>> +static inline void kernel_fpu_end(void)
>> +{
>> +csr_clear(CSR_SSTATUS, SR_FS);
>> +fstate_restore(current, task_pt_regs(current));
>> +preempt_enable();
>> +}
> 
> Is there any critical reason to inline these two?  I'd much rather see
> them out of line and exported instead of the low-level helpers.

No, I will define them out of line in v2.

Regards,
Samuel



Re: [PATCH 3/3] drm/amd/display: Support DRM_AMD_DC_FP on RISC-V

2023-12-08 Thread Samuel Holland
Hi Christoph,

On 2023-11-22 2:40 AM, Christoph Hellwig wrote:
>> -select DRM_AMD_DC_FP if (X86 || LOONGARCH || (PPC64 && ALTIVEC) || 
>> (ARM64 && KERNEL_MODE_NEON && !CC_IS_CLANG))
>> +select DRM_AMD_DC_FP if ARM64 && KERNEL_MODE_NEON && !CC_IS_CLANG
>> +select DRM_AMD_DC_FP if PPC64 && ALTIVEC
>> +select DRM_AMD_DC_FP if RISCV && FPU
>> +select DRM_AMD_DC_FP if LOONGARCH || X86
> 
> This really is a mess.  Can you add a ARCH_HAS_KERNEL_FPU_SUPPORT
> symbol that all architetures that have it select instead, and them
> make DRM_AMD_DC_FP depend on it?

Yes, I have done this for v2, which I will send shortly.

>> -#if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH)
>> +#if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH) || 
>> defined(CONFIG_RISCV)
>>  kernel_fpu_begin();
>>  #elif defined(CONFIG_PPC64)
>>  if (cpu_has_feature(CPU_FTR_VSX_COMP))
>> @@ -122,7 +124,7 @@ void dc_fpu_end(const char *function_name, const int 
>> line)
>>  
>>  depth = __this_cpu_dec_return(fpu_recursion_depth);
>>  if (depth == 0) {
>> -#if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH)
>> +#if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH) || 
>> defined(CONFIG_RISCV)
>>  kernel_fpu_end();
>>  #elif defined(CONFIG_PPC64)
>>  if (cpu_has_feature(CPU_FTR_VSX_COMP))
> 
> And then this mess can go away.  We'll need to decide if we want to
> cover all the in-kernel vector support as part of it, which would
> seem reasonable to me, or have a separate generic kernel_vector_begin
> with it's own option.

I think we may want to keep vector separate for performance on architectures
with separate FP and vector register files. For now, I have limited my changes
to FPU support only, which means I have removed VSX/Altivec from here; the
AMDGPU code doesn't need Altivec anyway.

>> diff --git a/drivers/gpu/drm/amd/display/dc/dml/Makefile 
>> b/drivers/gpu/drm/amd/display/dc/dml/Makefile
>> index ea7d60f9a9b4..5c8f840ef323 100644
>> --- a/drivers/gpu/drm/amd/display/dc/dml/Makefile
>> +++ b/drivers/gpu/drm/amd/display/dc/dml/Makefile
>> @@ -43,6 +43,12 @@ dml_ccflags := -mfpu=64
>>  dml_rcflags := -msoft-float
>>  endif
>>  
>> +ifdef CONFIG_RISCV
>> +include $(srctree)/arch/riscv/Makefile.isa
>> +# Remove V from the ISA string, like in arch/riscv/Makefile, but keep F and 
>> D.
>> +dml_ccflags := -march=$(shell echo $(riscv-march-y) | sed -E 
>> 's/(rv32ima|rv64ima)([^v_]*)v?/\1\2/')
>> +endif
>> +
>>  ifdef CONFIG_CC_IS_GCC
>>  ifneq ($(call gcc-min-version, 70100),y)
>>  IS_OLD_GCC = 1
> 
> And this is again not really something we should be doing.
> Instead we need a generic way in Kconfig to enable FPU support
> for an object file or set of, that the arch support can hook
> into.

I've included this in v2 as well.

> Btw, I'm also really worried about folks using the FPU instructions
> outside the kernel_fpu_begin/end windows in general (not directly
> related to the RISC-V support).  Can we have objecttool checks
> for that similar to only allowing the unsafe uaccess in the
> uaccess begin/end pairs?

ARM partially enforces this at compile time: it disallows calling
kernel_neon_begin() inside a translation unit that has NEON enabled. That
doesn't prevent the programmer from calling a FPU-enabled function from outside
a begin/end section, but it does prevent the compiler from generating unexpected
FPU usage behind your back. I implemented this same functionality for RISC-V.

Actually tracking all possibly-FPU-tainted functions and their call sites is
probably possible, but a much larger task.

Regards,
Samuel



Re: [PATCH 3/3] drm/amd/display: Support DRM_AMD_DC_FP on RISC-V

2023-12-08 Thread Samuel Holland
Hi Nathan,

On 2023-11-29 6:42 PM, Nathan Chancellor wrote:
> On Thu, Nov 23, 2023 at 02:23:01PM +, Conor Dooley wrote:
>> On Tue, Nov 21, 2023 at 07:05:15PM -0800, Samuel Holland wrote:
>>> RISC-V uses kernel_fpu_begin()/kernel_fpu_end() like several other
>>> architectures. Enabling hardware FP requires overriding the ISA string
>>> for the relevant compilation units.
>>
>> Ah yes, bringing the joy of frame-larger-than warnings to RISC-V:
>> ../drivers/gpu/drm/amd/amdgpu/../display/dc/dml/dcn32/display_mode_vba_32.c:58:13:
>>  warning: stack frame size (2416) exceeds limit (2048) in 
>> 'DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerformanceCalculation'
>>  [-Wframe-larger-than]
> 
> :(
> 
>> Nathan, have you given up on these being sorted out?
> 
> Does your configuration have KASAN (I don't think RISC-V supports
> KCSAN)? It is possible that dml/dcn32 needs something similar to commit
> 6740ec97bcdb ("drm/amd/display: Increase frame warning limit with KASAN
> or KCSAN in dml2")?
> 
> I am not really interested in playing whack-a-mole with these warnings
> like I have done in the past for the reasons I outlined here:
> 
> https://lore.kernel.org/20231019205117.GA839902@dev-arch.thelio-3990X/

I also see one of these with clang 17 even with KASAN disabled:

drivers/gpu/drm/amd/amdgpu/../display/dc/dml/dcn32/display_mode_vba_32.c:37:6:
warning: stack frame size (2208) exceeds limit (2048) in 'dml32_recalculate'
[-Wframe-larger-than]
void dml32_recalculate(struct display_mode_lib *mode_lib)

 ^
1532/2208 (69.38%) spills, 676/2208 (30.62%) variables

So I'm in favor of just raising the limit for these files for clang, like you
suggested in the linked thread.

Regards,
Samuel



[PATCH 1/3] riscv: Add support for kernel-mode FPU

2023-11-22 Thread Samuel Holland
This is needed to support recent hardware in the amdgpu DRM driver. The
FPU code in that driver is not performance-critical, so only provide the
minimal support.

Signed-off-by: Samuel Holland 
---

 arch/riscv/include/asm/switch_to.h | 14 ++
 arch/riscv/kernel/process.c|  3 +++
 2 files changed, 17 insertions(+)

diff --git a/arch/riscv/include/asm/switch_to.h 
b/arch/riscv/include/asm/switch_to.h
index f90d8e42f3c7..4b15f1292fc4 100644
--- a/arch/riscv/include/asm/switch_to.h
+++ b/arch/riscv/include/asm/switch_to.h
@@ -63,6 +63,20 @@ static __always_inline bool has_fpu(void)
return riscv_has_extension_likely(RISCV_ISA_EXT_f) ||
riscv_has_extension_likely(RISCV_ISA_EXT_d);
 }
+
+static inline void kernel_fpu_begin(void)
+{
+   preempt_disable();
+   fstate_save(current, task_pt_regs(current));
+   csr_set(CSR_SSTATUS, SR_FS);
+}
+
+static inline void kernel_fpu_end(void)
+{
+   csr_clear(CSR_SSTATUS, SR_FS);
+   fstate_restore(current, task_pt_regs(current));
+   preempt_enable();
+}
 #else
 static __always_inline bool has_fpu(void) { return false; }
 #define fstate_save(task, regs) do { } while (0)
diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c
index 4f21d970a129..6a18bc709d1c 100644
--- a/arch/riscv/kernel/process.c
+++ b/arch/riscv/kernel/process.c
@@ -225,3 +225,6 @@ int copy_thread(struct task_struct *p, const struct 
kernel_clone_args *args)
p->thread.sp = (unsigned long)childregs; /* kernel sp */
return 0;
 }
+
+EXPORT_SYMBOL_GPL(__fstate_save);
+EXPORT_SYMBOL_GPL(__fstate_restore);
-- 
2.42.0



[PATCH 0/3] riscv: Add kernel-mode FPU support for amdgpu

2023-11-22 Thread Samuel Holland
This series allows using newer AMD GPUs (e.g. Navi) on RISC-V boards
such as SiFive's HiFive Unmatched. Those GPUs need CONFIG_DRM_AMD_DC_FP
to initialize, which requires kernel-mode FPU support.

I'm sending these patches as one series so there is a user along with
the infrastructure being added. I assume patch 3 would be merged
separately, after patches 1-2 are merged.


Samuel Holland (3):
  riscv: Add support for kernel-mode FPU
  riscv: Factor out riscv-march-y to a separate Makefile
  drm/amd/display: Support DRM_AMD_DC_FP on RISC-V

 arch/riscv/Makefile| 12 +---
 arch/riscv/Makefile.isa| 15 +++
 arch/riscv/include/asm/switch_to.h | 14 ++
 arch/riscv/kernel/process.c|  3 +++
 drivers/gpu/drm/amd/display/Kconfig|  5 -
 drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c |  6 --
 drivers/gpu/drm/amd/display/dc/dml/Makefile|  6 ++
 drivers/gpu/drm/amd/display/dc/dml2/Makefile   |  6 ++
 8 files changed, 53 insertions(+), 14 deletions(-)
 create mode 100644 arch/riscv/Makefile.isa

-- 
2.42.0



[PATCH 2/3] riscv: Factor out riscv-march-y to a separate Makefile

2023-11-22 Thread Samuel Holland
Since it is not possible to incrementally add/remove extensions from the
compiler's ISA string by appending arguments, any code that wants to
modify the ISA string must recreate the whole thing. To support this,
factor out the logic for generating the -march argument so it can be
reused where needed.

Signed-off-by: Samuel Holland 
---

 arch/riscv/Makefile | 12 +---
 arch/riscv/Makefile.isa | 15 +++
 2 files changed, 16 insertions(+), 11 deletions(-)
 create mode 100644 arch/riscv/Makefile.isa

diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
index a74be78678eb..c738eafe67a0 100644
--- a/arch/riscv/Makefile
+++ b/arch/riscv/Makefile
@@ -58,22 +58,12 @@ ifeq ($(CONFIG_SHADOW_CALL_STACK),y)
KBUILD_LDFLAGS += --no-relax-gp
 endif
 
-# ISA string setting
-riscv-march-$(CONFIG_ARCH_RV32I)   := rv32ima
-riscv-march-$(CONFIG_ARCH_RV64I)   := rv64ima
-riscv-march-$(CONFIG_FPU)  := $(riscv-march-y)fd
-riscv-march-$(CONFIG_RISCV_ISA_C)  := $(riscv-march-y)c
-riscv-march-$(CONFIG_RISCV_ISA_V)  := $(riscv-march-y)v
-
 ifdef CONFIG_TOOLCHAIN_NEEDS_OLD_ISA_SPEC
 KBUILD_CFLAGS += -Wa,-misa-spec=2.2
 KBUILD_AFLAGS += -Wa,-misa-spec=2.2
-else
-riscv-march-$(CONFIG_TOOLCHAIN_NEEDS_EXPLICIT_ZICSR_ZIFENCEI) := 
$(riscv-march-y)_zicsr_zifencei
 endif
 
-# Check if the toolchain supports Zihintpause extension
-riscv-march-$(CONFIG_TOOLCHAIN_HAS_ZIHINTPAUSE) := $(riscv-march-y)_zihintpause
+include $(srctree)/arch/riscv/Makefile.isa
 
 # Remove F,D,V from isa string for all. Keep extensions between "fd" and "v" by
 # matching non-v and non-multi-letter extensions out with the filter ([^v_]*)
diff --git a/arch/riscv/Makefile.isa b/arch/riscv/Makefile.isa
new file mode 100644
index ..e10c77e26fe6
--- /dev/null
+++ b/arch/riscv/Makefile.isa
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+# ISA string setting
+riscv-march-$(CONFIG_ARCH_RV32I)   := rv32ima
+riscv-march-$(CONFIG_ARCH_RV64I)   := rv64ima
+riscv-march-$(CONFIG_FPU)  := $(riscv-march-y)fd
+riscv-march-$(CONFIG_RISCV_ISA_C)  := $(riscv-march-y)c
+riscv-march-$(CONFIG_RISCV_ISA_V)  := $(riscv-march-y)v
+
+ifndef CONFIG_TOOLCHAIN_NEEDS_OLD_ISA_SPEC
+riscv-march-$(CONFIG_TOOLCHAIN_NEEDS_EXPLICIT_ZICSR_ZIFENCEI) := 
$(riscv-march-y)_zicsr_zifencei
+endif
+
+# Check if the toolchain supports Zihintpause extension
+riscv-march-$(CONFIG_TOOLCHAIN_HAS_ZIHINTPAUSE) := $(riscv-march-y)_zihintpause
-- 
2.42.0



[PATCH 3/3] drm/amd/display: Support DRM_AMD_DC_FP on RISC-V

2023-11-22 Thread Samuel Holland
RISC-V uses kernel_fpu_begin()/kernel_fpu_end() like several other
architectures. Enabling hardware FP requires overriding the ISA string
for the relevant compilation units.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/amd/display/Kconfig| 5 -
 drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c | 6 --
 drivers/gpu/drm/amd/display/dc/dml/Makefile| 6 ++
 drivers/gpu/drm/amd/display/dc/dml2/Makefile   | 6 ++
 4 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/Kconfig 
b/drivers/gpu/drm/amd/display/Kconfig
index 901d1961b739..49b33b2f6701 100644
--- a/drivers/gpu/drm/amd/display/Kconfig
+++ b/drivers/gpu/drm/amd/display/Kconfig
@@ -8,7 +8,10 @@ config DRM_AMD_DC
depends on BROKEN || !CC_IS_CLANG || ARM64 || RISCV || SPARC64 || X86_64
select SND_HDA_COMPONENT if SND_HDA_CORE
# !CC_IS_CLANG: https://github.com/ClangBuiltLinux/linux/issues/1752
-   select DRM_AMD_DC_FP if (X86 || LOONGARCH || (PPC64 && ALTIVEC) || 
(ARM64 && KERNEL_MODE_NEON && !CC_IS_CLANG))
+   select DRM_AMD_DC_FP if ARM64 && KERNEL_MODE_NEON && !CC_IS_CLANG
+   select DRM_AMD_DC_FP if PPC64 && ALTIVEC
+   select DRM_AMD_DC_FP if RISCV && FPU
+   select DRM_AMD_DC_FP if LOONGARCH || X86
help
  Choose this option if you want to use the new display engine
  support for AMDGPU. This adds required support for Vega and
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c
index 4ae4720535a5..834dca0396f1 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c
@@ -35,6 +35,8 @@
 #include 
 #elif defined(CONFIG_LOONGARCH)
 #include 
+#elif defined(CONFIG_RISCV)
+#include 
 #endif
 
 /**
@@ -89,7 +91,7 @@ void dc_fpu_begin(const char *function_name, const int line)
depth = __this_cpu_inc_return(fpu_recursion_depth);
 
if (depth == 1) {
-#if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH)
+#if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH) || defined(CONFIG_RISCV)
kernel_fpu_begin();
 #elif defined(CONFIG_PPC64)
if (cpu_has_feature(CPU_FTR_VSX_COMP))
@@ -122,7 +124,7 @@ void dc_fpu_end(const char *function_name, const int line)
 
depth = __this_cpu_dec_return(fpu_recursion_depth);
if (depth == 0) {
-#if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH)
+#if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH) || defined(CONFIG_RISCV)
kernel_fpu_end();
 #elif defined(CONFIG_PPC64)
if (cpu_has_feature(CPU_FTR_VSX_COMP))
diff --git a/drivers/gpu/drm/amd/display/dc/dml/Makefile 
b/drivers/gpu/drm/amd/display/dc/dml/Makefile
index ea7d60f9a9b4..5c8f840ef323 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/Makefile
+++ b/drivers/gpu/drm/amd/display/dc/dml/Makefile
@@ -43,6 +43,12 @@ dml_ccflags := -mfpu=64
 dml_rcflags := -msoft-float
 endif
 
+ifdef CONFIG_RISCV
+include $(srctree)/arch/riscv/Makefile.isa
+# Remove V from the ISA string, like in arch/riscv/Makefile, but keep F and D.
+dml_ccflags := -march=$(shell echo $(riscv-march-y) | sed -E 
's/(rv32ima|rv64ima)([^v_]*)v?/\1\2/')
+endif
+
 ifdef CONFIG_CC_IS_GCC
 ifneq ($(call gcc-min-version, 70100),y)
 IS_OLD_GCC = 1
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/Makefile 
b/drivers/gpu/drm/amd/display/dc/dml2/Makefile
index acff3449b8d7..15ad6e3a2173 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/Makefile
+++ b/drivers/gpu/drm/amd/display/dc/dml2/Makefile
@@ -42,6 +42,12 @@ dml2_ccflags := -mfpu=64
 dml2_rcflags := -msoft-float
 endif
 
+ifdef CONFIG_RISCV
+include $(srctree)/arch/riscv/Makefile.isa
+# Remove V from the ISA string, like in arch/riscv/Makefile, but keep F and D.
+dml2_ccflags := -march=$(shell echo $(riscv-march-y) | sed -E 
's/(rv32ima|rv64ima)([^v_]*)v?/\1\2/')
+endif
+
 ifdef CONFIG_CC_IS_GCC
 ifeq ($(call cc-ifversion, -lt, 0701, y), y)
 IS_OLD_GCC = 1
-- 
2.42.0



[PATCH] drm/amd/pm: Replace 1-element arrays with flexible array members

2023-08-31 Thread Samuel Holland
Since commit df8fc4e934c1 ("kbuild: Enable -fstrict-flex-arrays=3"),
UBSAN_BOUNDS no longer pretends 1-element arrays are unbounded. The
bounds check is tripped in smu7_get_pp_table_entry_callback_func_v1(),
while reading from mclk_dep_table.

For consistency, fix all affected struct definitions in this file.

Signed-off-by: Samuel Holland 
---

 .../drm/amd/pm/powerplay/hwmgr/pptable_v1_0.h | 20 +--
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pptable_v1_0.h 
b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pptable_v1_0.h
index b0ac4d121adc..fb5e935ef786 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pptable_v1_0.h
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pptable_v1_0.h
@@ -164,7 +164,7 @@ typedef struct _ATOM_Tonga_State {
 typedef struct _ATOM_Tonga_State_Array {
UCHAR ucRevId;
UCHAR ucNumEntries; /* Number of entries. */
-   ATOM_Tonga_State entries[1];/* Dynamically allocate entries. */
+   ATOM_Tonga_State entries[]; /* Dynamically allocate entries. */
 } ATOM_Tonga_State_Array;
 
 typedef struct _ATOM_Tonga_MCLK_Dependency_Record {
@@ -179,7 +179,7 @@ typedef struct _ATOM_Tonga_MCLK_Dependency_Record {
 typedef struct _ATOM_Tonga_MCLK_Dependency_Table {
UCHAR ucRevId;
UCHAR ucNumEntries; 
/* Number of entries. */
-   ATOM_Tonga_MCLK_Dependency_Record entries[1];   
/* Dynamically allocate entries. */
+   ATOM_Tonga_MCLK_Dependency_Record entries[];
/* Dynamically allocate entries. */
 } ATOM_Tonga_MCLK_Dependency_Table;
 
 typedef struct _ATOM_Tonga_SCLK_Dependency_Record {
@@ -194,7 +194,7 @@ typedef struct _ATOM_Tonga_SCLK_Dependency_Record {
 typedef struct _ATOM_Tonga_SCLK_Dependency_Table {
UCHAR ucRevId;
UCHAR ucNumEntries; 
/* Number of entries. */
-   ATOM_Tonga_SCLK_Dependency_Record entries[1];   
 /* Dynamically allocate entries. */
+   ATOM_Tonga_SCLK_Dependency_Record entries[];
 /* Dynamically allocate entries. */
 } ATOM_Tonga_SCLK_Dependency_Table;
 
 typedef struct _ATOM_Polaris_SCLK_Dependency_Record {
@@ -210,7 +210,7 @@ typedef struct _ATOM_Polaris_SCLK_Dependency_Record {
 typedef struct _ATOM_Polaris_SCLK_Dependency_Table {
UCHAR ucRevId;
UCHAR ucNumEntries; 
/* Number of entries. */
-   ATOM_Polaris_SCLK_Dependency_Record entries[1]; 
 /* Dynamically allocate entries. */
+   ATOM_Polaris_SCLK_Dependency_Record entries[];  
 /* Dynamically allocate entries. */
 } ATOM_Polaris_SCLK_Dependency_Table;
 
 typedef struct _ATOM_Tonga_PCIE_Record {
@@ -222,7 +222,7 @@ typedef struct _ATOM_Tonga_PCIE_Record {
 typedef struct _ATOM_Tonga_PCIE_Table {
UCHAR ucRevId;
UCHAR ucNumEntries; 
/* Number of entries. */
-   ATOM_Tonga_PCIE_Record entries[1];  
/* Dynamically allocate entries. */
+   ATOM_Tonga_PCIE_Record entries[];   
/* Dynamically allocate entries. */
 } ATOM_Tonga_PCIE_Table;
 
 typedef struct _ATOM_Polaris10_PCIE_Record {
@@ -235,7 +235,7 @@ typedef struct _ATOM_Polaris10_PCIE_Record {
 typedef struct _ATOM_Polaris10_PCIE_Table {
UCHAR ucRevId;
UCHAR ucNumEntries; /* Number 
of entries. */
-   ATOM_Polaris10_PCIE_Record entries[1];  /* 
Dynamically allocate entries. */
+   ATOM_Polaris10_PCIE_Record entries[];   /* 
Dynamically allocate entries. */
 } ATOM_Polaris10_PCIE_Table;
 
 
@@ -252,7 +252,7 @@ typedef struct _ATOM_Tonga_MM_Dependency_Record {
 typedef struct _ATOM_Tonga_MM_Dependency_Table {
UCHAR ucRevId;
UCHAR ucNumEntries; 
/* Number of entries. */
-   ATOM_Tonga_MM_Dependency_Record entries[1];/* 
Dynamically allocate entries. */
+   ATOM_Tonga_MM_Dependency_Record entries[]; /* 
Dynamically allocate entries. */
 } ATOM_Tonga_MM_Dependency_Table;
 
 typedef struct _ATOM_Tonga_Voltage_Lookup_Record {
@@ -265,7 +265,7 @@ typedef struct _ATOM_Tonga_Voltage_Lookup_Record {
 typedef struct _ATOM_Tonga_Voltage_Lookup_Table {
UCHAR ucRevId;
UCHAR ucNumEntries; 
/* Number of entries. */
-   ATOM_Tonga_Voltage_Lookup_Record entries[1];
/* Dynamicall

[PATCH] drm/amd/display: Allow building DC with clang on RISC-V

2023-07-18 Thread Samuel Holland
clang on RISC-V appears to be unaffected by the bug causing excessive
stack usage in calculate_bandwidth(). clang 16 with -fstack-usage
reports a 304 byte stack frame size with CONFIG_ARCH_RV32I, and 512
bytes with CONFIG_ARCH_RV64I.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/amd/display/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/amd/display/Kconfig 
b/drivers/gpu/drm/amd/display/Kconfig
index bf0a655d009e..901d1961b739 100644
--- a/drivers/gpu/drm/amd/display/Kconfig
+++ b/drivers/gpu/drm/amd/display/Kconfig
@@ -5,7 +5,7 @@ menu "Display Engine Configuration"
 config DRM_AMD_DC
bool "AMD DC - Enable new display engine"
default y
-   depends on BROKEN || !CC_IS_CLANG || X86_64 || SPARC64 || ARM64
+   depends on BROKEN || !CC_IS_CLANG || ARM64 || RISCV || SPARC64 || X86_64
select SND_HDA_COMPONENT if SND_HDA_CORE
# !CC_IS_CLANG: https://github.com/ClangBuiltLinux/linux/issues/1752
select DRM_AMD_DC_FP if (X86 || LOONGARCH || (PPC64 && ALTIVEC) || 
(ARM64 && KERNEL_MODE_NEON && !CC_IS_CLANG))
-- 
2.40.1



Re: [PATCH v2 06/26] drm: sun4i: Define and use generic PM ops

2022-11-29 Thread Samuel Holland
On 11/29/22 13:17, Paul Cercueil wrote:
> Use the new DEFINE_DRM_MODE_CONFIG_HELPER_PM_OPS() macro to create a
> "struct dev_pm_ops" that can be used by this driver, instead of using
> custom PM callbacks with the same behaviour.
> 
> v2: Use the DEFINE_DRM_MODE_CONFIG_HELPER_PM_OPS() macro instead of an
> exported dev_pm_ops.
> 
> Signed-off-by: Paul Cercueil 
> ---
> 
> Samuel: since the code changed I had to remove your reviewed-by, sorry
> about that.
> 
> Cc: Maxime Ripard 
> Cc: Chen-Yu Tsai 
> Cc: Jernej Skrabec 
> Cc: Samuel Holland 
> Cc: linux-arm-ker...@lists.infradead.org
> Cc: linux-su...@lists.linux.dev
> ---
>  drivers/gpu/drm/sun4i/sun4i_drv.c | 26 --
>  1 file changed, 4 insertions(+), 22 deletions(-)

Reviewed-by: Samuel Holland 



Re: [PATCH 06/26] drm: sun4i: Use the dev_pm_ops provided by modeset helper

2022-11-13 Thread Samuel Holland
On 11/7/22 11:50, Paul Cercueil wrote:
> Use the drm_mode_config_pm_ops structure exported by
> drm_modeset_helper.c, which provides the exact same PM callbacks.
> 
> Signed-off-by: Paul Cercueil 
> ---
> Cc: Maxime Ripard 
> Cc: Chen-Yu Tsai 
> Cc: Jernej Skrabec 
> Cc: Samuel Holland 
> Cc: linux-arm-ker...@lists.infradead.org
> Cc: linux-su...@lists.linux.dev
> ---
>  drivers/gpu/drm/sun4i/sun4i_drv.c | 24 ++--
>  1 file changed, 2 insertions(+), 22 deletions(-)

Reviewed-by: Samuel Holland 



[PATCH v2 1/4] dt-bindings: display: sun6i-dsi: Fix clock conditional

2022-11-06 Thread Samuel Holland
The A64 case should have limited maxItems, instead of duplicating the
minItems value from the main binding. While here, simplify the binding
by making this an "else" case of the two-clock conditional block.

Fixes: fe5040f2843a ("dt-bindings: sun6i-dsi: Document A64 MIPI-DSI controller")
Acked-by: Krzysztof Kozlowski 
Signed-off-by: Samuel Holland 
---

(no changes since v1)

 .../bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml | 10 ++
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml 
b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
index 7910831fa4b8..bf9bfe8f88ae 100644
--- 
a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
+++ 
b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
@@ -78,16 +78,10 @@ allOf:
   required:
 - clock-names
 
-  - if:
-  properties:
-compatible:
-  contains:
-const: allwinner,sun50i-a64-mipi-dsi
-
-then:
+else:
   properties:
 clocks:
-  minItems: 1
+  maxItems: 1
 
 unevaluatedProperties: false
 
-- 
2.37.3



[PATCH v2 2/4] dt-bindings: display: sun6i-dsi: Add the A100 variant

2022-11-06 Thread Samuel Holland
The "40nm" MIPI DSI controller found in the A100 and D1 SoCs has the
same register layout as previous SoC integrations. However, its module
clock now comes from the TCON, which means it no longer runs at a fixed
rate, so this needs to be distinguished in the driver.

The controller also now uses pins on Port D instead of dedicated pins,
so it drops the separate power domain.

Acked-by: Krzysztof Kozlowski 
Signed-off-by: Samuel Holland 
---
Removal of the vcc-dsi-supply is maybe a bit questionable. Since there
is no "VCC-DSI" pin anymore, it's not obvious which pin actually does
power the DSI controller/PHY. Possibly power comes from VCC-PD or VCC-IO
or VCC-LVDS. So far, all boards have all of these as always-on supplies,
so it is hard to test.

(no changes since v1)

 .../display/allwinner,sun6i-a31-mipi-dsi.yaml | 28 +++
 1 file changed, 23 insertions(+), 5 deletions(-)

diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml 
b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
index bf9bfe8f88ae..c731fbdc2fe0 100644
--- 
a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
+++ 
b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
@@ -12,9 +12,14 @@ maintainers:
 
 properties:
   compatible:
-enum:
-  - allwinner,sun6i-a31-mipi-dsi
-  - allwinner,sun50i-a64-mipi-dsi
+oneOf:
+  - enum:
+  - allwinner,sun6i-a31-mipi-dsi
+  - allwinner,sun50i-a64-mipi-dsi
+  - allwinner,sun50i-a100-mipi-dsi
+  - items:
+  - const: allwinner,sun20i-d1-mipi-dsi
+  - const: allwinner,sun50i-a100-mipi-dsi
 
   reg:
 maxItems: 1
@@ -59,7 +64,6 @@ required:
   - phys
   - phy-names
   - resets
-  - vcc-dsi-supply
   - port
 
 allOf:
@@ -68,7 +72,9 @@ allOf:
   properties:
 compatible:
   contains:
-const: allwinner,sun6i-a31-mipi-dsi
+enum:
+  - allwinner,sun6i-a31-mipi-dsi
+  - allwinner,sun50i-a100-mipi-dsi
 
 then:
   properties:
@@ -83,6 +89,18 @@ allOf:
 clocks:
   maxItems: 1
 
+  - if:
+  properties:
+compatible:
+  contains:
+enum:
+  - allwinner,sun6i-a31-mipi-dsi
+  - allwinner,sun50i-a64-mipi-dsi
+
+then:
+  required:
+- vcc-dsi-supply
+
 unevaluatedProperties: false
 
 examples:
-- 
2.37.3



[PATCH v2 0/4] drm/sun4i: dsi: Support the A100/D1 controller variant

2022-11-06 Thread Samuel Holland
This series adds support for the digital part of the DSI controller
found in the A100 and D1 SoCs (plus T7, which is not supported by
mainline Linux). There are two changes to the hardware integration:
  1) the module clock routes through the TCON TOP, and
  2) the separate I/O domain is removed.

The actual register interface appears to be the same as before. The
register definitions in the D1 BSP exactly match the A64 BSP.

The BSP describes this as the "40nm" DSI controller variant. There is
also a "28nm" variant with a different register interface; that one is
found in a different subset of SoCs (V5 and A50).

A100/D1 also come with an updated DPHY, described by the BSP as a
"combo" PHY, which is now also used for LVDS channel 0. (LVDS and DSI
share the same pins on Port D.) Since that is a different subsystem,
I am sending that as a separate series.

Changes in v2:
 - Add the variant check to the probe error path

Samuel Holland (4):
  dt-bindings: display: sun6i-dsi: Fix clock conditional
  dt-bindings: display: sun6i-dsi: Add the A100 variant
  drm/sun4i: dsi: Add a variant structure
  drm/sun4i: dsi: Add the A100 variant

 .../display/allwinner,sun6i-a31-mipi-dsi.yaml | 30 ++---
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c| 61 +--
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h|  7 +++
 3 files changed, 71 insertions(+), 27 deletions(-)

-- 
2.37.3



[PATCH v2 3/4] drm/sun4i: dsi: Add a variant structure

2022-11-06 Thread Samuel Holland
Replace the ad-hoc calls to of_device_is_compatible() with a structure
describing the differences between variants. This is in preparation for
adding more variants to the driver.

Reviewed-by: Jernej Skrabec 
Signed-off-by: Samuel Holland 
---

Changes in v2:
 - Add the variant check to the probe error path

 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 53 +-
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h |  7 
 2 files changed, 42 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c 
b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index 34234a144e87..f22c96cc8408 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -1101,12 +1101,16 @@ static const struct component_ops sun6i_dsi_ops = {
 
 static int sun6i_dsi_probe(struct platform_device *pdev)
 {
+   const struct sun6i_dsi_variant *variant;
struct device *dev = >dev;
-   const char *bus_clk_name = NULL;
struct sun6i_dsi *dsi;
void __iomem *base;
int ret;
 
+   variant = device_get_match_data(dev);
+   if (!variant)
+   return -EINVAL;
+
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
if (!dsi)
return -ENOMEM;
@@ -1114,10 +1118,7 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
dsi->dev = dev;
dsi->host.ops = _dsi_host_ops;
dsi->host.dev = dev;
-
-   if (of_device_is_compatible(dev->of_node,
-   "allwinner,sun6i-a31-mipi-dsi"))
-   bus_clk_name = "bus";
+   dsi->variant = variant;
 
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) {
@@ -1142,7 +1143,7 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
return PTR_ERR(dsi->regs);
}
 
-   dsi->bus_clk = devm_clk_get(dev, bus_clk_name);
+   dsi->bus_clk = devm_clk_get(dev, variant->has_mod_clk ? "bus" : NULL);
if (IS_ERR(dsi->bus_clk))
return dev_err_probe(dev, PTR_ERR(dsi->bus_clk),
 "Couldn't get the DSI bus clock\n");
@@ -1151,21 +1152,21 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
if (ret)
return ret;
 
-   if (of_device_is_compatible(dev->of_node,
-   "allwinner,sun6i-a31-mipi-dsi")) {
+   if (variant->has_mod_clk) {
dsi->mod_clk = devm_clk_get(dev, "mod");
if (IS_ERR(dsi->mod_clk)) {
dev_err(dev, "Couldn't get the DSI mod clock\n");
ret = PTR_ERR(dsi->mod_clk);
goto err_attach_clk;
}
-   }
 
-   /*
-* In order to operate properly, that clock seems to be always
-* set to 297MHz.
-*/
-   clk_set_rate_exclusive(dsi->mod_clk, 29700);
+   /*
+* In order to operate properly, the module clock on the
+* A31 variant always seems to be set to 297MHz.
+*/
+   if (variant->set_mod_clk)
+   clk_set_rate_exclusive(dsi->mod_clk, 29700);
+   }
 
dsi->dphy = devm_phy_get(dev, "dphy");
if (IS_ERR(dsi->dphy)) {
@@ -1191,7 +1192,8 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
 err_remove_dsi_host:
mipi_dsi_host_unregister(>host);
 err_unprotect_clk:
-   clk_rate_exclusive_put(dsi->mod_clk);
+   if (dsi->variant->has_mod_clk && dsi->variant->set_mod_clk)
+   clk_rate_exclusive_put(dsi->mod_clk);
 err_attach_clk:
regmap_mmio_detach_clk(dsi->regs);
 
@@ -1205,16 +1207,31 @@ static int sun6i_dsi_remove(struct platform_device 
*pdev)
 
component_del(>dev, _dsi_ops);
mipi_dsi_host_unregister(>host);
-   clk_rate_exclusive_put(dsi->mod_clk);
+   if (dsi->variant->has_mod_clk && dsi->variant->set_mod_clk)
+   clk_rate_exclusive_put(dsi->mod_clk);
 
regmap_mmio_detach_clk(dsi->regs);
 
return 0;
 }
 
+static const struct sun6i_dsi_variant sun6i_a31_mipi_dsi_variant = {
+   .has_mod_clk= true,
+   .set_mod_clk= true,
+};
+
+static const struct sun6i_dsi_variant sun50i_a64_mipi_dsi_variant = {
+};
+
 static const struct of_device_id sun6i_dsi_of_table[] = {
-   { .compatible = "allwinner,sun6i-a31-mipi-dsi" },
-   { .compatible = "allwinner,sun50i-a64-mipi-dsi" },
+   {
+   .compatible = "allwinner,sun6i-a31-mipi-dsi",
+   .data   = _a31_mipi_dsi_variant,
+   },
+   {
+   .compatible = "allwinner,sun50i-a64-mipi-dsi",
+   .data   

[PATCH v2 4/4] drm/sun4i: dsi: Add the A100 variant

2022-11-06 Thread Samuel Holland
The A100 variant of the MIPI DSI controller now gets its module clock
from the TCON via the TCON TOP, so the clock rate cannot be set to a
fixed value. Otherwise, it appears to be the same as the A31 variant.

Reviewed-by: Jernej Skrabec 
Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 8 
 1 file changed, 8 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c 
b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index f22c96cc8408..760ff05eabf4 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -1223,6 +1223,10 @@ static const struct sun6i_dsi_variant 
sun6i_a31_mipi_dsi_variant = {
 static const struct sun6i_dsi_variant sun50i_a64_mipi_dsi_variant = {
 };
 
+static const struct sun6i_dsi_variant sun50i_a100_mipi_dsi_variant = {
+   .has_mod_clk= true,
+};
+
 static const struct of_device_id sun6i_dsi_of_table[] = {
{
.compatible = "allwinner,sun6i-a31-mipi-dsi",
@@ -1232,6 +1236,10 @@ static const struct of_device_id sun6i_dsi_of_table[] = {
.compatible = "allwinner,sun50i-a64-mipi-dsi",
.data   = _a64_mipi_dsi_variant,
},
+   {
+   .compatible = "allwinner,sun50i-a100-mipi-dsi",
+   .data   = _a100_mipi_dsi_variant,
+   },
{ }
 };
 MODULE_DEVICE_TABLE(of, sun6i_dsi_of_table);
-- 
2.37.3



Re: [PATCH v3 1/4] dt-bindings: vendor-prefixes: Add prefix for ClockworkPi

2022-10-29 Thread Samuel Holland
Hi Max,

On 9/17/22 22:44, Max Fierke wrote:
> Add a prefix for Clockwork Tech LLC, known as ClockworkPi. They
> produce a number of hobbyist devices, including the ClockworkPi
> DevTerm and GameShell.
> 
> Signed-off-by: Max Fierke 
> Acked-by: Krzysztof Kozlowski 
> ---
>  Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml 
> b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> index 2f0151e9f6be..64f4b899c40c 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> @@ -258,6 +258,8 @@ patternProperties:
>  description: Cirrus Logic, Inc.
>"^cisco,.*":
>  description: Cisco Systems, Inc.
> +  "^clockworkpi,.*":
> +description: Clockwork Tech LLC

The vendor uses "clockwork" as the prefix in their downstream
devicetrees[1][2][3], so I would suggest using the same here. I think
there is a distinction between "Clockwork" the company and "ClockworkPi"
the product. This is what I did for the board devicetree I sent[4].

Regards,
Samuel

[1]:
https://github.com/clockworkpi/DevTerm/blob/main/Code/patch/armbian_build_a04/userpatches/kernel/sunxi-current/kernel_001_dts.patch#L31
[2]:
https://github.com/clockworkpi/DevTerm/blob/main/Code/patch/armbian_build_a04/userpatches/kernel/sunxi-current/kernel_001_dts.patch#L127
[3]:
https://github.com/clockworkpi/DevTerm/blob/main/Code/patch/armbian_build_a06/patch/kernel-001-a06-dts.patch#L37
[4]:
https://lore.kernel.org/lkml/20220815050815.22340-12-sam...@sholland.org/

>"^cloudengines,.*":
>  description: Cloud Engines, Inc.
>"^cnm,.*":



Re: [PATCH] drm/sun4i: dsi: Prevent underflow when computing packet sizes

2022-08-14 Thread Samuel Holland
On 8/14/22 2:55 AM, Jernej Škrabec wrote:
> Dne petek, 12. avgust 2022 ob 05:16:23 CEST je Samuel Holland napisal(a):
>> Currently, the packet overhead is subtracted using unsigned arithmetic.
>> With a short sync pulse, this could underflow and wrap around to near
>> the maximal u16 value. Fix this by using signed subtraction. The call to
>> max() will correctly handle any negative numbers that are produced.
>>
>> Apply the same fix to the other timings, even though those subtractions
>> are less likely to underflow.
>>
>> Fixes: 133add5b5ad4 ("drm/sun4i: Add Allwinner A31 MIPI-DSI controller
>> support") Signed-off-by: Samuel Holland 
>> ---
>>
>>  drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 10 +-
>>  1 file changed, 5 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
>> b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c index b4dfa166eccd..34234a144e87
>> 100644
>> --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
>> +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
>> @@ -522,77 +522,77 @@ static void sun6i_dsi_setup_format(struct sun6i_dsi
>> *dsi, SUN6I_DSI_PIXEL_PF1_CRC_INIT_LINE0(0x) |
>>   SUN6I_DSI_PIXEL_PF1_CRC_INIT_LINEN(0x));
>>
>>  regmap_write(dsi->regs, SUN6I_DSI_PIXEL_CTL0_REG,
>>   SUN6I_DSI_PIXEL_CTL0_PD_PLUG_DISABLE |
>>   SUN6I_DSI_PIXEL_CTL0_FORMAT(fmt));
>>  }
>>
>>  static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
>>  struct drm_display_mode *mode)
>>  {
>>  struct mipi_dsi_device *device = dsi->device;
>> -unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
>> +int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
> 
> Nit: mipi_dsi_pixel_format_to_bpp() can return -EINVAL in case of unsupported 
> format. Would it make sense to check it?

The switch statement in mipi_dsi_pixel_format_to_bpp() handles every value in
the enumeration, so I think the -EINVAL is just there to keep GCC from
complaining. If we do want to handle this case, it would need to be in
sun6i_dsi_attach(), since the other places we use mipi_dsi_pixel_format_to_bpp()
are way too late to handle any errors.

Regards,
Samuel


Re: [PATCH 2/4] dt-bindings: display: sun6i-dsi: Add the A100 variant

2022-08-12 Thread Samuel Holland
Hi Krzysztof,

On 8/12/22 5:49 AM, Krzysztof Kozlowski wrote:
> On 12/08/2022 10:42, Samuel Holland wrote:
>> The "40nm" MIPI DSI controller found in the A100 and D1 SoCs has the
>> same register layout as previous SoC integrations. However, its module
>> clock now comes from the TCON, which means it no longer runs at a fixed
>> rate, so this needs to be distinguished in the driver.
>>
>> The controller also now uses pins on Port D instead of dedicated pins,
>> so it drops the separate power domain.
>>
>> Signed-off-by: Samuel Holland 
>> ---
>> Removal of the vcc-dsi-supply is maybe a bit questionable. Since there
>> is no "VCC-DSI" pin anymore, it's not obvious which pin actually does
>> power the DSI controller/PHY. Possibly power comes from VCC-PD or VCC-IO
>> or VCC-LVDS. So far, all boards have all of these as always-on supplies,
>> so it is hard to test.
>>
>>  .../display/allwinner,sun6i-a31-mipi-dsi.yaml | 28 +++
>>  1 file changed, 23 insertions(+), 5 deletions(-)
>>
>> diff --git 
>> a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
>>  
>> b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
>> index ae55ef3fb1fe..c53c25b87bd4 100644
>> --- 
>> a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
>> +++ 
>> b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
>> @@ -12,9 +12,14 @@ maintainers:
>>  
>>  properties:
>>compatible:
>> -enum:
>> -  - allwinner,sun6i-a31-mipi-dsi
>> -  - allwinner,sun50i-a64-mipi-dsi
>> +oneOf:
>> +  - enum:
>> +  - allwinner,sun6i-a31-mipi-dsi
>> +  - allwinner,sun50i-a64-mipi-dsi
>> +  - allwinner,sun50i-a100-mipi-dsi
> 
> While you are moving code, how about bringing alphabetical order?

I have put the sun*i prefix in numeric order, which matches (almost) all of our
other bindings. It roughly corresponds to chronological order as well. It
doesn't make much sense to me to sort sun50i (ARM64 SoCs) between sun5i and
sun6i (early ARMv7 SoCs).

Regards,
Samuel


[PATCH 4/4] drm/sun4i: dsi: Add the A100 variant

2022-08-12 Thread Samuel Holland
The A100 variant of the MIPI DSI controller now gets its module clock
from the TCON via the TCON TOP, so the clock rate cannot be set to a
fixed value. Otherwise, it appears to be the same as the A31 variant.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 8 
 1 file changed, 8 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c 
b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index 6479ade416b9..5db5ecdc2fc6 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -1222,6 +1222,10 @@ static const struct sun6i_dsi_variant 
sun6i_a31_mipi_dsi_variant = {
 static const struct sun6i_dsi_variant sun50i_a64_mipi_dsi_variant = {
 };
 
+static const struct sun6i_dsi_variant sun50i_a100_mipi_dsi_variant = {
+   .has_mod_clk= true,
+};
+
 static const struct of_device_id sun6i_dsi_of_table[] = {
{
.compatible = "allwinner,sun6i-a31-mipi-dsi",
@@ -1231,6 +1235,10 @@ static const struct of_device_id sun6i_dsi_of_table[] = {
.compatible = "allwinner,sun50i-a64-mipi-dsi",
.data   = _a64_mipi_dsi_variant,
},
+   {
+   .compatible = "allwinner,sun50i-a100-mipi-dsi",
+   .data   = _a100_mipi_dsi_variant,
+   },
{ }
 };
 MODULE_DEVICE_TABLE(of, sun6i_dsi_of_table);
-- 
2.35.1



[PATCH 3/4] drm/sun4i: dsi: Add a variant structure

2022-08-12 Thread Samuel Holland
Replace the ad-hoc calls to of_device_is_compatible() with a structure
describing the differences between variants. This is in preparation for
adding more variants to the driver.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 50 +-
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h |  7 
 2 files changed, 40 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c 
b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index b4dfa166eccd..6479ade416b9 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -1101,12 +1101,16 @@ static const struct component_ops sun6i_dsi_ops = {
 
 static int sun6i_dsi_probe(struct platform_device *pdev)
 {
+   const struct sun6i_dsi_variant *variant;
struct device *dev = >dev;
-   const char *bus_clk_name = NULL;
struct sun6i_dsi *dsi;
void __iomem *base;
int ret;
 
+   variant = device_get_match_data(dev);
+   if (!variant)
+   return -EINVAL;
+
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
if (!dsi)
return -ENOMEM;
@@ -1114,10 +1118,7 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
dsi->dev = dev;
dsi->host.ops = _dsi_host_ops;
dsi->host.dev = dev;
-
-   if (of_device_is_compatible(dev->of_node,
-   "allwinner,sun6i-a31-mipi-dsi"))
-   bus_clk_name = "bus";
+   dsi->variant = variant;
 
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) {
@@ -1142,7 +1143,7 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
return PTR_ERR(dsi->regs);
}
 
-   dsi->bus_clk = devm_clk_get(dev, bus_clk_name);
+   dsi->bus_clk = devm_clk_get(dev, variant->has_mod_clk ? "bus" : NULL);
if (IS_ERR(dsi->bus_clk))
return dev_err_probe(dev, PTR_ERR(dsi->bus_clk),
 "Couldn't get the DSI bus clock\n");
@@ -1151,21 +1152,21 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
if (ret)
return ret;
 
-   if (of_device_is_compatible(dev->of_node,
-   "allwinner,sun6i-a31-mipi-dsi")) {
+   if (variant->has_mod_clk) {
dsi->mod_clk = devm_clk_get(dev, "mod");
if (IS_ERR(dsi->mod_clk)) {
dev_err(dev, "Couldn't get the DSI mod clock\n");
ret = PTR_ERR(dsi->mod_clk);
goto err_attach_clk;
}
-   }
 
-   /*
-* In order to operate properly, that clock seems to be always
-* set to 297MHz.
-*/
-   clk_set_rate_exclusive(dsi->mod_clk, 29700);
+   /*
+* In order to operate properly, the module clock on the
+* A31 variant always seems to be set to 297MHz.
+*/
+   if (variant->set_mod_clk)
+   clk_set_rate_exclusive(dsi->mod_clk, 29700);
+   }
 
dsi->dphy = devm_phy_get(dev, "dphy");
if (IS_ERR(dsi->dphy)) {
@@ -1205,16 +1206,31 @@ static int sun6i_dsi_remove(struct platform_device 
*pdev)
 
component_del(>dev, _dsi_ops);
mipi_dsi_host_unregister(>host);
-   clk_rate_exclusive_put(dsi->mod_clk);
+   if (dsi->variant->has_mod_clk && dsi->variant->set_mod_clk)
+   clk_rate_exclusive_put(dsi->mod_clk);
 
regmap_mmio_detach_clk(dsi->regs);
 
return 0;
 }
 
+static const struct sun6i_dsi_variant sun6i_a31_mipi_dsi_variant = {
+   .has_mod_clk= true,
+   .set_mod_clk= true,
+};
+
+static const struct sun6i_dsi_variant sun50i_a64_mipi_dsi_variant = {
+};
+
 static const struct of_device_id sun6i_dsi_of_table[] = {
-   { .compatible = "allwinner,sun6i-a31-mipi-dsi" },
-   { .compatible = "allwinner,sun50i-a64-mipi-dsi" },
+   {
+   .compatible = "allwinner,sun6i-a31-mipi-dsi",
+   .data   = _a31_mipi_dsi_variant,
+   },
+   {
+   .compatible = "allwinner,sun50i-a64-mipi-dsi",
+   .data   = _a64_mipi_dsi_variant,
+   },
{ }
 };
 MODULE_DEVICE_TABLE(of, sun6i_dsi_of_table);
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h 
b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
index c863900ae3b4..f1ddefe0f554 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
@@ -15,6 +15,11 @@
 
 #define SUN6I_DSI_TCON_DIV 4
 
+struct sun6i_dsi_variant {
+   boolhas_mod_clk;
+   boolset_mod_clk;
+};
+
 struct sun6i

[PATCH 2/4] dt-bindings: display: sun6i-dsi: Add the A100 variant

2022-08-12 Thread Samuel Holland
The "40nm" MIPI DSI controller found in the A100 and D1 SoCs has the
same register layout as previous SoC integrations. However, its module
clock now comes from the TCON, which means it no longer runs at a fixed
rate, so this needs to be distinguished in the driver.

The controller also now uses pins on Port D instead of dedicated pins,
so it drops the separate power domain.

Signed-off-by: Samuel Holland 
---
Removal of the vcc-dsi-supply is maybe a bit questionable. Since there
is no "VCC-DSI" pin anymore, it's not obvious which pin actually does
power the DSI controller/PHY. Possibly power comes from VCC-PD or VCC-IO
or VCC-LVDS. So far, all boards have all of these as always-on supplies,
so it is hard to test.

 .../display/allwinner,sun6i-a31-mipi-dsi.yaml | 28 +++
 1 file changed, 23 insertions(+), 5 deletions(-)

diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml 
b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
index ae55ef3fb1fe..c53c25b87bd4 100644
--- 
a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
+++ 
b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
@@ -12,9 +12,14 @@ maintainers:
 
 properties:
   compatible:
-enum:
-  - allwinner,sun6i-a31-mipi-dsi
-  - allwinner,sun50i-a64-mipi-dsi
+oneOf:
+  - enum:
+  - allwinner,sun6i-a31-mipi-dsi
+  - allwinner,sun50i-a64-mipi-dsi
+  - allwinner,sun50i-a100-mipi-dsi
+  - items:
+  - const: allwinner,sun20i-d1-mipi-dsi
+  - const: allwinner,sun50i-a100-mipi-dsi
 
   reg:
 maxItems: 1
@@ -59,7 +64,6 @@ required:
   - phys
   - phy-names
   - resets
-  - vcc-dsi-supply
   - port
 
 allOf:
@@ -68,7 +72,9 @@ allOf:
   properties:
 compatible:
   contains:
-const: allwinner,sun6i-a31-mipi-dsi
+enum:
+  - allwinner,sun6i-a31-mipi-dsi
+  - allwinner,sun50i-a100-mipi-dsi
 
 then:
   properties:
@@ -83,6 +89,18 @@ allOf:
 clocks:
   maxItems: 1
 
+  - if:
+  properties:
+compatible:
+  contains:
+enum:
+  - allwinner,sun6i-a31-mipi-dsi
+  - allwinner,sun50i-a64-mipi-dsi
+
+then:
+  required:
+- vcc-dsi-supply
+
 unevaluatedProperties: false
 
 examples:
-- 
2.35.1



[PATCH 0/4] drm/sun4i: dsi: Support the A100/D1 controller variant

2022-08-12 Thread Samuel Holland
This series adds support for the digital part of the DSI controller
found in the A100 and D1 SoCs (plus T7, which is not supported by
mainline Linux). There are two changes to the hardware integration:
  1) the module clock routes through the TCON TOP, and
  2) the separate I/O domain is removed.

The actual register interface appears to be the same as before. The
register definitions in the D1 BSP exactly match the A64 BSP.

The BSP describes this as the "40nm" DSI controller variant. There is
also a "28nm" variant with a different register interface; that one is
found in a different subset of SoCs (V5 and A50).

A100/D1 also come with an updated DPHY, described by the BSP as a
"combo" PHY, which is now also used for LVDS channel 0. (LVDS and DSI
share the same pins on Port D.) Since that is a different subsystem,
I am sending that as a separate series.


Samuel Holland (4):
  dt-bindings: display: sun6i-dsi: Fix clock conditional
  dt-bindings: display: sun6i-dsi: Add the A100 variant
  drm/sun4i: dsi: Add a variant structure
  drm/sun4i: dsi: Add the A100 variant

 .../display/allwinner,sun6i-a31-mipi-dsi.yaml | 30 +++---
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c| 58 +--
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h|  7 +++
 3 files changed, 69 insertions(+), 26 deletions(-)

-- 
2.35.1



[PATCH 1/4] dt-bindings: display: sun6i-dsi: Fix clock conditional

2022-08-12 Thread Samuel Holland
The A64 case should have limited maxItems, instead of duplicating the
minItems value from the main binding. While here, simplify the binding
by making this an "else" case of the two-clock conditional block.

Fixes: fe5040f2843a ("dt-bindings: sun6i-dsi: Document A64 MIPI-DSI controller")
Signed-off-by: Samuel Holland 
---

 .../bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml | 10 ++
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml 
b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
index bf0bdf54e5f9..ae55ef3fb1fe 100644
--- 
a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
+++ 
b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml
@@ -78,16 +78,10 @@ allOf:
   required:
 - clock-names
 
-  - if:
-  properties:
-compatible:
-  contains:
-const: allwinner,sun50i-a64-mipi-dsi
-
-then:
+else:
   properties:
 clocks:
-  minItems: 1
+  maxItems: 1
 
 unevaluatedProperties: false
 
-- 
2.35.1



[PATCH] dt-bindings: display: sun4i: Add D1 TCONs to conditionals

2022-08-12 Thread Samuel Holland
When adding the D1 TCON bindings, I missed the conditional blocks that
restrict the binding for TCON LCD vs TCON TV hardware. Add the D1 TCON
variants to the appropriate blocks for DE2 TCON LCDs and TCON TVs.

Fixes: ae5a5d26c15c ("dt-bindings: display: Add D1 display engine compatibles")
Signed-off-by: Samuel Holland 
---

 .../devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml | 4 
 1 file changed, 4 insertions(+)

diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml 
b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml
index 4a92a4c7dcd7..f8168986a0a9 100644
--- a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml
+++ b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml
@@ -224,43 +224,45 @@ allOf:
 - const: ahb
 - const: tcon-ch0
 - const: lvds-alt
 
   - if:
   properties:
 compatible:
   contains:
 enum:
   - allwinner,sun8i-a83t-tcon-lcd
   - allwinner,sun8i-v3s-tcon
   - allwinner,sun9i-a80-tcon-lcd
+  - allwinner,sun20i-d1-tcon-lcd
 
 then:
   properties:
 clocks:
   minItems: 2
 
 clock-names:
   items:
 - const: ahb
 - const: tcon-ch0
 
   - if:
   properties:
 compatible:
   contains:
 enum:
   - allwinner,sun8i-a83t-tcon-tv
   - allwinner,sun8i-r40-tcon-tv
   - allwinner,sun9i-a80-tcon-tv
+  - allwinner,sun20i-d1-tcon-tv
 
 then:
   properties:
 clocks:
   minItems: 2
 
 clock-names:
   items:
 - const: ahb
 - const: tcon-ch1
 
   - if:
@@ -269,40 +271,42 @@ allOf:
   contains:
 enum:
   - allwinner,sun5i-a13-tcon
   - allwinner,sun6i-a31-tcon
   - allwinner,sun6i-a31s-tcon
   - allwinner,sun7i-a20-tcon
   - allwinner,sun8i-a23-tcon
   - allwinner,sun8i-a33-tcon
   - allwinner,sun8i-v3s-tcon
   - allwinner,sun9i-a80-tcon-lcd
   - allwinner,sun4i-a10-tcon
   - allwinner,sun8i-a83t-tcon-lcd
+  - allwinner,sun20i-d1-tcon-lcd
 
 then:
   required:
 - "#clock-cells"
 - clock-output-names
 
   - if:
   properties:
 compatible:
   contains:
 enum:
   - allwinner,sun6i-a31-tcon
   - allwinner,sun6i-a31s-tcon
   - allwinner,sun8i-a23-tcon
   - allwinner,sun8i-a33-tcon
   - allwinner,sun8i-a83t-tcon-lcd
+  - allwinner,sun20i-d1-tcon-lcd
 
 then:
   properties:
 resets:
   minItems: 2
 
 reset-names:
   items:
 - const: lcd
 - const: lvds
 
   - if:
-- 
2.35.1



[PATCH] drm/sun4i: dsi: Prevent underflow when computing packet sizes

2022-08-11 Thread Samuel Holland
Currently, the packet overhead is subtracted using unsigned arithmetic.
With a short sync pulse, this could underflow and wrap around to near
the maximal u16 value. Fix this by using signed subtraction. The call to
max() will correctly handle any negative numbers that are produced.

Apply the same fix to the other timings, even though those subtractions
are less likely to underflow.

Fixes: 133add5b5ad4 ("drm/sun4i: Add Allwinner A31 MIPI-DSI controller support")
Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c 
b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index b4dfa166eccd..34234a144e87 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -522,77 +522,77 @@ static void sun6i_dsi_setup_format(struct sun6i_dsi *dsi,
 SUN6I_DSI_PIXEL_PF1_CRC_INIT_LINE0(0x) |
 SUN6I_DSI_PIXEL_PF1_CRC_INIT_LINEN(0x));
 
regmap_write(dsi->regs, SUN6I_DSI_PIXEL_CTL0_REG,
 SUN6I_DSI_PIXEL_CTL0_PD_PLUG_DISABLE |
 SUN6I_DSI_PIXEL_CTL0_FORMAT(fmt));
 }
 
 static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
struct drm_display_mode *mode)
 {
struct mipi_dsi_device *device = dsi->device;
-   unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
+   int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
u16 hbp = 0, hfp = 0, hsa = 0, hblk = 0, vblk = 0;
u32 basic_ctl = 0;
size_t bytes;
u8 *buffer;
 
/* Do all timing calculations up front to allocate buffer space */
 
if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
hblk = mode->hdisplay * Bpp;
basic_ctl = SUN6I_DSI_BASIC_CTL_VIDEO_BURST |
SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS |
SUN6I_DSI_BASIC_CTL_HBP_DIS;
 
if (device->lanes == 4)
basic_ctl |= SUN6I_DSI_BASIC_CTL_TRAIL_FILL |
 SUN6I_DSI_BASIC_CTL_TRAIL_INV(0xc);
} else {
/*
 * A sync period is composed of a blanking packet (4
 * bytes + payload + 2 bytes) and a sync event packet
 * (4 bytes). Its minimal size is therefore 10 bytes
 */
 #define HSA_PACKET_OVERHEAD10
-   hsa = max((unsigned int)HSA_PACKET_OVERHEAD,
+   hsa = max(HSA_PACKET_OVERHEAD,
  (mode->hsync_end - mode->hsync_start) * Bpp - 
HSA_PACKET_OVERHEAD);
 
/*
 * The backporch is set using a blanking packet (4
 * bytes + payload + 2 bytes). Its minimal size is
 * therefore 6 bytes
 */
 #define HBP_PACKET_OVERHEAD6
-   hbp = max((unsigned int)HBP_PACKET_OVERHEAD,
+   hbp = max(HBP_PACKET_OVERHEAD,
  (mode->htotal - mode->hsync_end) * Bpp - 
HBP_PACKET_OVERHEAD);
 
/*
 * The frontporch is set using a sync event (4 bytes)
 * and two blanking packets (each one is 4 bytes +
 * payload + 2 bytes). Its minimal size is therefore
 * 16 bytes
 */
 #define HFP_PACKET_OVERHEAD16
-   hfp = max((unsigned int)HFP_PACKET_OVERHEAD,
+   hfp = max(HFP_PACKET_OVERHEAD,
  (mode->hsync_start - mode->hdisplay) * Bpp - 
HFP_PACKET_OVERHEAD);
 
/*
 * The blanking is set using a sync event (4 bytes)
 * and a blanking packet (4 bytes + payload + 2
 * bytes). Its minimal size is therefore 10 bytes.
 */
 #define HBLK_PACKET_OVERHEAD   10
-   hblk = max((unsigned int)HBLK_PACKET_OVERHEAD,
+   hblk = max(HBLK_PACKET_OVERHEAD,
   (mode->htotal - (mode->hsync_end - 
mode->hsync_start)) * Bpp -
   HBLK_PACKET_OVERHEAD);
 
/*
 * And I'm not entirely sure what vblk is about. The driver in
 * Allwinner BSP is using a rather convoluted calculation
 * there only for 4 lanes. However, using 0 (the !4 lanes
 * case) even with a 4 lanes screen seems to work...
 */
vblk = 0;
}
 
-- 
2.35.1



Re: [PATCH v2 1/2] dt-bindings: arm: sunxi: Add binding for RenewWorldOutReach R16-Vista-E board

2022-07-04 Thread Samuel Holland
On 6/15/22 4:38 AM, Suniel Mahesh wrote:
> Add a binding for the RenewWorldOutReach R16-Vista-E board based on
> allwinner R16.
> 
> Signed-off-by: Christopher Vollo 
> Signed-off-by: Jagan Teki 
> Signed-off-by: Suniel Mahesh 

The primary author of the commit (the From:) should be the first signer, unless
you are using Co-developed-by:. See the examples here:

https://www.kernel.org/doc/html/latest/process/submitting-patches.html#when-to-use-acked-by-cc-and-co-developed-by

> ---
> Changes for v2:
> - Add missing compatible string
> - insert missing signatures of contributors
> ---
>  Documentation/devicetree/bindings/arm/sunxi.yaml | 6 ++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/arm/sunxi.yaml 
> b/Documentation/devicetree/bindings/arm/sunxi.yaml
> index 95278a6a9a8e..52b8c9aba6f3 100644
> --- a/Documentation/devicetree/bindings/arm/sunxi.yaml
> +++ b/Documentation/devicetree/bindings/arm/sunxi.yaml
> @@ -787,6 +787,12 @@ properties:
>- const: allwinner,r7-tv-dongle
>- const: allwinner,sun5i-a10s
>  
> +  - description: RenewWorldOutreach R16-Vista-E
> +items:
> +  - const: renewworldoutreach,r16-vista-e

This vendor prefix should be documented.

Regards,
Samuel

> +  - const: allwinner,sun8i-r16
> +  - const: allwinner,sun8i-a33
> +
>- description: RerVision H3-DVK
>  items:
>- const: rervision,h3-dvk
> 



Re: [PATCH v2 2/2] ARM: dts: sun8i: Add R16 Vista E board from RenewWorldOutreach

2022-07-04 Thread Samuel Holland
On 6/15/22 4:39 AM, Suniel Mahesh wrote:
> The R16-Vista-E board from RenewWorldOutreach based on allwinner
> R16(A33).
> 
> General features:
> - 1GB RAM
> - microSD slot
> - Realtek Wifi
> - 1 x USB 2.0
> - HDMI IN
> - HDMI OUT
> - Audio out
> - MIPI DSI
> - TI DLPC3433
> 
> It has also connectors to connect an external mini keypad.
> 
> Signed-off-by: Christopher Vollo 
> Signed-off-by: Jagan Teki 
> Signed-off-by: Suniel Mahesh 
> 
> ---
> Changes for v2:
> - Add missing compatible string
> - insert missing signatures of contributors
> ---
>  arch/arm/boot/dts/Makefile|   1 +
>  arch/arm/boot/dts/sun8i-r16-renew-vista-e.dts | 361 ++
>  2 files changed, 362 insertions(+)
>  create mode 100644 arch/arm/boot/dts/sun8i-r16-renew-vista-e.dts
> 
> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
> index 184899808ee7..b5966c0742e1 100644
> --- a/arch/arm/boot/dts/Makefile
> +++ b/arch/arm/boot/dts/Makefile
> @@ -1353,6 +1353,7 @@ dtb-$(CONFIG_MACH_SUN8I) += \
>   sun8i-r16-nintendo-nes-classic.dtb \
>   sun8i-r16-nintendo-super-nes-classic.dtb \
>   sun8i-r16-parrot.dtb \
> + sun8i-r16-renew-vista-e.dtb \
>   sun8i-r40-bananapi-m2-ultra.dtb \
>   sun8i-r40-oka40i-c.dtb \
>   sun8i-s3-elimo-initium.dtb \
> diff --git a/arch/arm/boot/dts/sun8i-r16-renew-vista-e.dts 
> b/arch/arm/boot/dts/sun8i-r16-renew-vista-e.dts
> new file mode 100644
> index ..45f620203c33
> --- /dev/null
> +++ b/arch/arm/boot/dts/sun8i-r16-renew-vista-e.dts
> @@ -0,0 +1,361 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (C) 2022 RenewWorldOutreach
> + * Copyright (C) 2022 Amarula Solutions(India)
> + */
> +
> +/dts-v1/;
> +#include "sun8i-a33.dtsi"
> +
> +#include 
> +#include 
> +#include 
> +
> +/ {
> + model = "RenewWorldOutreach R16-Vista-E";
> + compatible = "renewworldoutreach,r16-vista-e", "allwinner,sun8i-r16", 
> "allwinner,sun8i-a33";
> +
> + aliases {
> + serial0 = 
> + };
> +
> + chosen {
> + stdout-path = "serial0:115200n8";
> + };
> +
> + gpio-keys-polled {
> + compatible = "gpio-keys-polled";
> + poll-interval = <100>;
> +
> + ok {
> + label = "ok";
> + linux,code = ;
> + gpios = < 4 0 GPIO_ACTIVE_LOW>;
> + };
> +
> + left {
> + label = "left";
> + linux,code = ;
> + gpios = < 4 1 GPIO_ACTIVE_LOW>;
> + };
> +
> + right {
> + label = "right";
> + linux,code = ;
> + gpios = < 4 2 GPIO_ACTIVE_LOW>;
> + };
> +
> + up {
> + label = "up";
> + linux,code = ;
> + gpios = < 4 3 GPIO_ACTIVE_LOW>;
> + };
> +
> + down {
> + label = "down";
> + linux,code = ;
> + gpios = < 4 4 GPIO_ACTIVE_LOW>;
> + };
> +
> + back {
> + label = "back";
> + linux,code = ;
> + gpios = < 4 5 GPIO_ACTIVE_LOW>;
> + };
> +
> + power {
> + label = "power";
> + linux,code = ;
> + gpios = < 4 6 GPIO_ACTIVE_LOW>;
> + };
> +
> + vol-down {
> + label = "vol-down";
> + linux,code = ;
> + gpios = < 7 3 GPIO_ACTIVE_LOW>;
> + };
> +
> + vol-up {
> + label = "vol-up";
> + linux,code = ;
> + gpios = < 7 9 GPIO_ACTIVE_LOW>;
> + };
> + };
> +
> + leds {
> + compatible = "gpio-leds";
> +
> + battery-led0 {
> + label = "renew-e:battery-led0";
> + gpios = <_pio 0 2 GPIO_ACTIVE_HIGH>;
> + };
> +
> + battery-led1 {
> + label = "renew-e:battery-led1";
> + gpios = <_pio 0 3 GPIO_ACTIVE_HIGH>;
> + };
> +
> + battery-led2 {
> + label = "renew-e:battery-led2";
> + gpios = <_pio 0 4 GPIO_ACTIVE_HIGH>;
> + };
> +
> + battery-led3 {
> + label = "renew-e:battery-led3";
> + gpios = <_pio 0 5 GPIO_ACTIVE_HIGH>;
> + };
> +
> + pad-intz {
> + label = "renew-e:pad-intz";
> + gpios = < 4 16 GPIO_ACTIVE_HIGH>;
> + default-state = "on";
> + };

Is this really an LED, or just a generic output?

> +
> + battery-led4 {
> + label = "renew-e:battery-led4";
> +

[PATCH] drm/sun4i: Update Kconfig defaults and descriptions

2022-07-04 Thread Samuel Holland
Allwinner display drivers are split roughly into two generations. The
first generation was found on early 32-bit ARM SoCs and contains DE1.0
and a custom HDMI controller. Clarify that these options only apply to
a specific list of SoCs, and limit selecting them to 32-bit ARM, to
avoid confusion.

The second generation, found in A83T and newer SoCs (both 32-bit and
64-bit), contains a DE2.0 and a DesignWare HDMI controller. Since this
is the most widely-used generation, enable it by default. The previous
default condition (MACH_SUN8I) was limited to 32-bit SoCs. Also enable
the DSI controller by default, which is found on 64-bit SoCs as well.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/sun4i/Kconfig | 26 +++---
 1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index 3a43c436c74a..1c2f8909f3cd 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -16,23 +16,25 @@ config DRM_SUN4I
 if DRM_SUN4I
 
 config DRM_SUN4I_HDMI
-   tristate "Allwinner A10 HDMI Controller Support"
+   tristate "Allwinner A10/A10s/A20/A31 HDMI Controller Support"
+   depends on ARM || COMPILE_TEST
default DRM_SUN4I
help
- Choose this option if you have an Allwinner SoC with an HDMI
- controller.
+ Choose this option if you have an Allwinner A10/A10s/A20/A31
+ SoC with an HDMI controller.
 
 config DRM_SUN4I_HDMI_CEC
-   bool "Allwinner A10 HDMI CEC Support"
+   bool "Allwinner A10/A10s/A20/A31 HDMI CEC Support"
depends on DRM_SUN4I_HDMI
select CEC_CORE
select CEC_PIN
help
- Choose this option if you have an Allwinner SoC with an HDMI
- controller and want to use CEC.
+ Choose this option if you have an Allwinner A10/A10s/A20/A31
+ SoC with an HDMI controller and want to use CEC.
 
 config DRM_SUN4I_BACKEND
tristate "Support for Allwinner A10 Display Engine Backend"
+   depends on ARM || COMPILE_TEST
default DRM_SUN4I
help
  Choose this option if you have an Allwinner SoC with the
@@ -41,8 +43,8 @@ config DRM_SUN4I_BACKEND
  selected the module will be called sun4i-backend.
 
 config DRM_SUN6I_DSI
-   tristate "Allwinner A31 MIPI-DSI Controller Support"
-   default MACH_SUN8I
+   tristate "Allwinner A31/A64 MIPI-DSI Controller Support"
+   default DRM_SUN4I
select CRC_CCITT
select DRM_MIPI_DSI
select RESET_CONTROLLER
@@ -55,15 +57,17 @@ config DRM_SUN6I_DSI
 config DRM_SUN8I_DW_HDMI
tristate "Support for Allwinner version of DesignWare HDMI"
depends on DRM_SUN4I
+   default DRM_SUN4I
select DRM_DW_HDMI
help
  Choose this option if you have an Allwinner SoC with the
- DesignWare HDMI controller with custom HDMI PHY. If M is
+ DesignWare HDMI controller. SoCs that support HDMI and
+ have a Display Engine 2.0 contain this controller. If M is
  selected the module will be called sun8i_dw_hdmi.
 
 config DRM_SUN8I_MIXER
tristate "Support for Allwinner Display Engine 2.0 Mixer"
-   default MACH_SUN8I
+   default DRM_SUN4I
help
  Choose this option if you have an Allwinner SoC with the
  Allwinner Display Engine 2.0, which has a mixer to do some
@@ -75,6 +79,6 @@ config DRM_SUN8I_TCON_TOP
default DRM_SUN4I if DRM_SUN8I_MIXER!=n
help
  TCON TOP is responsible for configuring display pipeline for
- HTMI, TVE and LCD.
+ HDMI, TVE and LCD.
 
 endif
-- 
2.35.1



[PATCH] dt-bindings: display: sun4i: Fix D1 pipeline count

2022-07-01 Thread Samuel Holland
When adding the bindings for the D1 display engine, I missed the
condition for the number of pipelines. D1 has two mixers, so it
will have two pipeline references.

Fixes: ae5a5d26c15c ("dt-bindings: display: Add D1 display engine compatibles")
Signed-off-by: Samuel Holland 
---

 .../bindings/display/allwinner,sun4i-a10-display-engine.yaml | 1 +
 1 file changed, 1 insertion(+)

diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-engine.yaml
 
b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-engine.yaml
index c388ae5da1e4..c9c346e6228e 100644
--- 
a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-engine.yaml
+++ 
b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-engine.yaml
@@ -94,6 +94,7 @@ if:
   - allwinner,sun8i-a83t-display-engine
   - allwinner,sun8i-r40-display-engine
   - allwinner,sun9i-a80-display-engine
+  - allwinner,sun20i-d1-display-engine
   - allwinner,sun50i-a64-display-engine
 
 then:
-- 
2.35.1



Re: [PATCH v2] drm/sun4i: Add DMA mask and segment size

2022-06-20 Thread Samuel Holland
On 6/20/22 1:13 PM, Jernej Skrabec wrote:
> Kernel occasionally complains that there is mismatch in segment size
> when trying to render HW decoded videos and rendering them directly with
> sun4i DRM driver. Following message can be observed on H6 SoC:
> 
> [  184.298308] [ cut here ]
> [  184.298326] DMA-API: sun4i-drm display-engine: mapping sg segment longer 
> than device claims to support [len=6144000] [max=65536]
> [  184.298364] WARNING: CPU: 1 PID: 382 at kernel/dma/debug.c:1162 
> debug_dma_map_sg+0x2b0/0x350
> [  184.322997] CPU: 1 PID: 382 Comm: ffmpeg Not tainted 5.19.0-rc1+ #1331
> [  184.329533] Hardware name: Tanix TX6 (DT)
> [  184.333544] pstate: 6005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
> [  184.340512] pc : debug_dma_map_sg+0x2b0/0x350
> [  184.344882] lr : debug_dma_map_sg+0x2b0/0x350
> [  184.349250] sp : 89f33a50
> [  184.352567] x29: 89f33a50 x28: 0001 x27: 
> 01b86c00
> [  184.359725] x26:  x25: 05d8cc80 x24: 
> 
> [  184.366879] x23: 8939ab18 x22: 0001 x21: 
> 0001
> [  184.374031] x20:  x19: 018a7410 x18: 
> 
> [  184.381186] x17:  x16:  x15: 
> 
> [  184.388338] x14: 0001 x13: 89534e86 x12: 
> 6f70707573206f74
> [  184.395493] x11: 20736d69616c6320 x10: 000a x9 : 
> 0001
> [  184.402647] x8 : 893b6d40 x7 : 89f33850 x6 : 
> 000c
> [  184.409800] x5 : bf997940 x4 :  x3 : 
> 0027
> [  184.416953] x2 :  x1 :  x0 : 
> 03960e80
> [  184.424106] Call trace:
> [  184.426556]  debug_dma_map_sg+0x2b0/0x350
> [  184.430580]  __dma_map_sg_attrs+0xa0/0x110
> [  184.434687]  dma_map_sgtable+0x28/0x4c
> [  184.438447]  vb2_dc_dmabuf_ops_map+0x60/0xcc
> [  184.442729]  __map_dma_buf+0x2c/0xd4
> [  184.446321]  dma_buf_map_attachment+0xa0/0x130
> [  184.450777]  drm_gem_prime_import_dev+0x7c/0x18c
> [  184.455410]  drm_gem_prime_fd_to_handle+0x1b8/0x214
> [  184.460300]  drm_prime_fd_to_handle_ioctl+0x2c/0x40
> [  184.465190]  drm_ioctl_kernel+0xc4/0x174
> [  184.469123]  drm_ioctl+0x204/0x420
> [  184.472534]  __arm64_sys_ioctl+0xac/0xf0
> [  184.476474]  invoke_syscall+0x48/0x114
> [  184.480240]  el0_svc_common.constprop.0+0x44/0xec
> [  184.484956]  do_el0_svc+0x2c/0xc0
> [  184.488283]  el0_svc+0x2c/0x84
> [  184.491354]  el0t_64_sync_handler+0x11c/0x150
> [  184.495723]  el0t_64_sync+0x18c/0x190
> [  184.499397] ---[ end trace  ]---
> 
> Fix that by setting DMA mask and segment size.
> 
> Signed-off-by: Jernej Skrabec 

Reviewed-by: Samuel Holland 


Re: [PATCH] drm/sun4i: Add DMA mask and segment size

2022-06-16 Thread Samuel Holland
On 6/16/22 11:13 PM, Jernej Škrabec wrote:
> Dne petek, 17. junij 2022 ob 05:03:11 CEST je Samuel Holland napisal(a):
>> Hi Jernej,
>>
>> On 6/16/22 4:32 PM, Jernej Skrabec wrote:
>>> Kernel occasionally complains that there is mismatch in segment size
>>> when trying to render HW decoded videos and rendering them directly with
>>> sun4i DRM driver.
>>>
>>> Fix that by setting DMA mask and segment size.
>>>
>>> Signed-off-by: Jernej Skrabec 
>>> ---
>>>
>>>  drivers/gpu/drm/sun4i/sun4i_drv.c | 4 
>>>  1 file changed, 4 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c
>>> b/drivers/gpu/drm/sun4i/sun4i_drv.c index 275f7e4a03ae..83f4e87f77f6
>>> 100644
>>> --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
>>> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
>>> @@ -7,6 +7,7 @@
>>>
>>>   */
>>>  
>>>  #include 
>>>
>>> +#include 
>>>
>>>  #include 
>>>  #include 
>>>  #include 
>>>
>>> @@ -367,6 +368,9 @@ static int sun4i_drv_probe(struct platform_device
>>> *pdev)> 
>>> INIT_KFIFO(list.fifo);
>>>
>>> +   dma_set_mask_and_coherent(>dev, DMA_BIT_MASK(32));
>>
>> Isn't this already the default, from of_dma_configure_id or
>> setup_pdev_dma_masks?
> 
> Not sure, I need to check.

Looking at the DE2 and DE3 spec to answer your question, I noticed that all of
the address fields have a corresponding 8-bit "high address" field, so from a
hardware perspective, the DMA mask should be 40 bits.

However, currently we do not use these fields, so we are limited to 32 bits. I
would suggest we comment that, so I am fine with setting the mask even if
redundant, as it provides a good place to add the comment.

>>> +   dma_set_max_seg_size(>dev, DMA_BIT_MASK(32));
>>
>> This looks like a good change. In fact, I think we need a similar change in
>> some other drivers.
> 
> Should be DMA_BIT_MASK(25) as in your other patch?

No, that limit is specific to the other DMA engine. I do not see any limit for
the display engine. BSP uses sg_tables only for dma-buf, and then it puts the
entire framebuffer in one segment. So I think DMA_BIT_MASK(32) (the maximum
possible value) is correct.

Regards,
Samuel


Re: [PATCH] drm/sun4i: Add DMA mask and segment size

2022-06-16 Thread Samuel Holland
Hi Jernej,

On 6/16/22 4:32 PM, Jernej Skrabec wrote:
> Kernel occasionally complains that there is mismatch in segment size
> when trying to render HW decoded videos and rendering them directly with
> sun4i DRM driver.
> 
> Fix that by setting DMA mask and segment size.
> 
> Signed-off-by: Jernej Skrabec 
> ---
>  drivers/gpu/drm/sun4i/sun4i_drv.c | 4 
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c 
> b/drivers/gpu/drm/sun4i/sun4i_drv.c
> index 275f7e4a03ae..83f4e87f77f6 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
> @@ -7,6 +7,7 @@
>   */
>  
>  #include 
> +#include 
>  #include 
>  #include 
>  #include 
> @@ -367,6 +368,9 @@ static int sun4i_drv_probe(struct platform_device *pdev)
>  
>   INIT_KFIFO(list.fifo);
>  
> + dma_set_mask_and_coherent(>dev, DMA_BIT_MASK(32));

Isn't this already the default, from of_dma_configure_id or 
setup_pdev_dma_masks?

> + dma_set_max_seg_size(>dev, DMA_BIT_MASK(32));

This looks like a good change. In fact, I think we need a similar change in some
other drivers.

Regards,
Samuel


[PATCH] drm/sun4i: Fix crash during suspend after component bind failure

2022-06-14 Thread Samuel Holland
If the component driver fails to bind, or is unbound, the driver data
for the top-level platform device points to a freed drm_device. If the
system is then suspended, the driver passes this dangling pointer to
drm_mode_config_helper_suspend(), which crashes.

Fix this by only setting the driver data while the platform driver holds
a reference to the drm_device.

Fixes: 624b4b48d9d8 ("drm: sun4i: Add support for suspending the display 
driver")
Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/sun4i/sun4i_drv.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c 
b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 275f7e4a03ae..8841dba989ee 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -73,7 +73,6 @@ static int sun4i_drv_bind(struct device *dev)
goto free_drm;
}
 
-   dev_set_drvdata(dev, drm);
drm->dev_private = drv;
INIT_LIST_HEAD(>frontend_list);
INIT_LIST_HEAD(>engine_list);
@@ -114,6 +113,8 @@ static int sun4i_drv_bind(struct device *dev)
 
drm_fbdev_generic_setup(drm, 32);
 
+   dev_set_drvdata(dev, drm);
+
return 0;
 
 finish_poll:
@@ -130,6 +131,7 @@ static void sun4i_drv_unbind(struct device *dev)
 {
struct drm_device *drm = dev_get_drvdata(dev);
 
+   dev_set_drvdata(dev, NULL);
drm_dev_unregister(drm);
drm_kms_helper_poll_fini(drm);
drm_atomic_helper_shutdown(drm);
-- 
2.35.1



[PATCH v2 6/6] drm/sun4i: sun8i-hdmi-phy: Group PHY ops functions by generation

2022-06-14 Thread Samuel Holland
Now that the PHY ops are separated, sort them topologically, with the
common sun8i_hdmi_phy_set_polarity helper at the top. No function
contents are changed in this commit.

Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 67 --
 1 file changed, 32 insertions(+), 35 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index f94c1bad..ca53b5e9fffc 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -124,7 +124,19 @@ static const struct dw_hdmi_phy_config 
sun50i_h6_phy_config[] = {
 };
 
 static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy,
-   const struct drm_display_mode *mode);
+   const struct drm_display_mode *mode)
+{
+   u32 val = 0;
+
+   if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+   val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC;
+
+   if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+   val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC;
+
+   regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
+  SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
+};
 
 static int sun8i_a83t_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
  const struct drm_display_info *display,
@@ -193,6 +205,25 @@ static int sun8i_a83t_hdmi_phy_config(struct dw_hdmi 
*hdmi, void *data,
return 0;
 }
 
+static void sun8i_a83t_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
+{
+   struct sun8i_hdmi_phy *phy = data;
+
+   dw_hdmi_phy_gen2_txpwron(hdmi, 0);
+   dw_hdmi_phy_gen2_pddq(hdmi, 1);
+
+   regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
+  SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0);
+}
+
+static const struct dw_hdmi_phy_ops sun8i_a83t_hdmi_phy_ops = {
+   .init   = sun8i_a83t_hdmi_phy_config,
+   .disable= sun8i_a83t_hdmi_phy_disable,
+   .read_hpd   = dw_hdmi_phy_read_hpd,
+   .update_hpd = dw_hdmi_phy_update_hpd,
+   .setup_hpd  = dw_hdmi_phy_setup_hpd,
+};
+
 static int sun8i_h3_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
const struct drm_display_info *display,
const struct drm_display_mode *mode)
@@ -348,32 +379,6 @@ static int sun8i_h3_hdmi_phy_config(struct dw_hdmi *hdmi, 
void *data,
return 0;
 }
 
-static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy,
-   const struct drm_display_mode *mode)
-{
-   u32 val = 0;
-
-   if (mode->flags & DRM_MODE_FLAG_NHSYNC)
-   val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC;
-
-   if (mode->flags & DRM_MODE_FLAG_NVSYNC)
-   val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC;
-
-   regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
-  SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
-};
-
-static void sun8i_a83t_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
-{
-   struct sun8i_hdmi_phy *phy = data;
-
-   dw_hdmi_phy_gen2_txpwron(hdmi, 0);
-   dw_hdmi_phy_gen2_pddq(hdmi, 1);
-
-   regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
-  SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0);
-}
-
 static void sun8i_h3_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
 {
struct sun8i_hdmi_phy *phy = data;
@@ -385,14 +390,6 @@ static void sun8i_h3_hdmi_phy_disable(struct dw_hdmi 
*hdmi, void *data)
regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 0);
 }
 
-static const struct dw_hdmi_phy_ops sun8i_a83t_hdmi_phy_ops = {
-   .init   = sun8i_a83t_hdmi_phy_config,
-   .disable= sun8i_a83t_hdmi_phy_disable,
-   .read_hpd   = dw_hdmi_phy_read_hpd,
-   .update_hpd = dw_hdmi_phy_update_hpd,
-   .setup_hpd  = dw_hdmi_phy_setup_hpd,
-};
-
 static const struct dw_hdmi_phy_ops sun8i_h3_hdmi_phy_ops = {
.init   = sun8i_h3_hdmi_phy_config,
.disable= sun8i_h3_hdmi_phy_disable,
-- 
2.35.1



[PATCH v2 4/6] drm/sun4i: sun8i-hdmi-phy: Support multiple custom PHY ops

2022-06-14 Thread Samuel Holland
The D1 SoC comes with a new custom HDMI PHY, which does not share any
registers with the existing custom PHY. So it needs a new set of ops.
Instead of providing a flag in the variant structure, provide the ops
themselves.

Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h  |  2 +-
 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 12 ++--
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h 
b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
index 0adbfac6eb31..f0b1aaad27d9 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
@@ -151,10 +151,10 @@ struct sun8i_hdmi_phy;
 struct sun8i_hdmi_phy_variant {
bool has_phy_clk;
bool has_second_pll;
-   unsigned int is_custom_phy : 1;
const struct dw_hdmi_curr_ctrl *cur_ctr;
const struct dw_hdmi_mpll_config *mpll_cfg;
const struct dw_hdmi_phy_config *phy_cfg;
+   const struct dw_hdmi_phy_ops *phy_ops;
void (*phy_init)(struct sun8i_hdmi_phy *phy);
void (*phy_disable)(struct dw_hdmi *hdmi,
struct sun8i_hdmi_phy *phy);
diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 9086ce547fad..e6d25bbe3d2f 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -567,8 +567,8 @@ void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
 {
const struct sun8i_hdmi_phy_variant *variant = phy->variant;
 
-   if (variant->is_custom_phy) {
-   plat_data->phy_ops = _hdmi_phy_ops;
+   if (variant->phy_ops) {
+   plat_data->phy_ops = variant->phy_ops;
plat_data->phy_name = "sun8i_dw_hdmi_phy";
plat_data->phy_data = phy;
} else {
@@ -587,7 +587,7 @@ static const struct regmap_config 
sun8i_hdmi_phy_regmap_config = {
 };
 
 static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = {
-   .is_custom_phy = true,
+   .phy_ops = _hdmi_phy_ops,
.phy_init = _hdmi_phy_init_a83t,
.phy_disable = _hdmi_phy_disable_a83t,
.phy_config = _hdmi_phy_config_a83t,
@@ -595,7 +595,7 @@ static const struct sun8i_hdmi_phy_variant 
sun8i_a83t_hdmi_phy = {
 
 static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = {
.has_phy_clk = true,
-   .is_custom_phy = true,
+   .phy_ops = _hdmi_phy_ops,
.phy_init = _hdmi_phy_init_h3,
.phy_disable = _hdmi_phy_disable_h3,
.phy_config = _hdmi_phy_config_h3,
@@ -604,7 +604,7 @@ static const struct sun8i_hdmi_phy_variant 
sun8i_h3_hdmi_phy = {
 static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = {
.has_phy_clk = true,
.has_second_pll = true,
-   .is_custom_phy = true,
+   .phy_ops = _hdmi_phy_ops,
.phy_init = _hdmi_phy_init_h3,
.phy_disable = _hdmi_phy_disable_h3,
.phy_config = _hdmi_phy_config_h3,
@@ -612,7 +612,7 @@ static const struct sun8i_hdmi_phy_variant 
sun8i_r40_hdmi_phy = {
 
 static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = {
.has_phy_clk = true,
-   .is_custom_phy = true,
+   .phy_ops = _hdmi_phy_ops,
.phy_init = _hdmi_phy_init_h3,
.phy_disable = _hdmi_phy_disable_h3,
.phy_config = _hdmi_phy_config_h3,
-- 
2.35.1



[PATCH v2 5/6] drm/sun4i: sun8i-hdmi-phy: Separate A83T and H3 PHY ops

2022-06-14 Thread Samuel Holland
Since the driver already needs to support multiple sets of ops, we can
drop the mid-layer used by the A83T and H3 PHYs. They share only a small
amount of code; factor this out as sun8i_hdmi_phy_set_polarity.

For clarity, this commit keeps the existing function order.

Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h  |  5 --
 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 89 +-
 2 files changed, 46 insertions(+), 48 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h 
b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
index f0b1aaad27d9..f082b8ecfe2c 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
@@ -156,11 +156,6 @@ struct sun8i_hdmi_phy_variant {
const struct dw_hdmi_phy_config *phy_cfg;
const struct dw_hdmi_phy_ops *phy_ops;
void (*phy_init)(struct sun8i_hdmi_phy *phy);
-   void (*phy_disable)(struct dw_hdmi *hdmi,
-   struct sun8i_hdmi_phy *phy);
-   int  (*phy_config)(struct dw_hdmi *hdmi,
-  struct sun8i_hdmi_phy *phy,
-  unsigned int clk_rate);
 };
 
 struct sun8i_hdmi_phy {
diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index e6d25bbe3d2f..f94c1bad 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -123,10 +123,18 @@ static const struct dw_hdmi_phy_config 
sun50i_h6_phy_config[] = {
{ ~0UL,  0x, 0x, 0x}
 };
 
-static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi *hdmi,
- struct sun8i_hdmi_phy *phy,
- unsigned int clk_rate)
+static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy,
+   const struct drm_display_mode *mode);
+
+static int sun8i_a83t_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
+ const struct drm_display_info *display,
+ const struct drm_display_mode *mode)
 {
+   unsigned int clk_rate = mode->crtc_clock * 1000;
+   struct sun8i_hdmi_phy *phy = data;
+
+   sun8i_hdmi_phy_set_polarity(phy, mode);
+
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
@@ -185,10 +193,12 @@ static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi 
*hdmi,
return 0;
 }
 
-static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi,
-   struct sun8i_hdmi_phy *phy,
-   unsigned int clk_rate)
+static int sun8i_h3_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
+   const struct drm_display_info *display,
+   const struct drm_display_mode *mode)
 {
+   unsigned int clk_rate = mode->crtc_clock * 1000;
+   struct sun8i_hdmi_phy *phy = data;
u32 pll_cfg1_init;
u32 pll_cfg2_init;
u32 ana_cfg1_end;
@@ -197,6 +207,11 @@ static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi,
u32 b_offset = 0;
u32 val;
 
+   if (phy->variant->has_phy_clk)
+   clk_set_rate(phy->clk_phy, clk_rate);
+
+   sun8i_hdmi_phy_set_polarity(phy, mode);
+
/* bandwidth / frequency independent settings */
 
pll_cfg1_init = SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN |
@@ -333,11 +348,9 @@ static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi,
return 0;
 }
 
-static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
-const struct drm_display_info *display,
-const struct drm_display_mode *mode)
+static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy,
+   const struct drm_display_mode *mode)
 {
-   struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data;
u32 val = 0;
 
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
@@ -348,16 +361,12 @@ static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, 
void *data,
 
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
   SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
-
-   if (phy->variant->has_phy_clk)
-   clk_set_rate(phy->clk_phy, mode->crtc_clock * 1000);
-
-   return phy->variant->phy_config(hdmi, phy, mode->crtc_clock * 1000);
 };
 
-static void sun8i_hdmi_phy_disable_a83t(struct dw_hdmi *hdmi,
-   struct sun8i_hdmi_phy *phy)
+static void sun8i_a83t_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
 {
+   struct sun8i_hdmi_phy *phy = data;
+
dw_hdmi_phy_gen2_txpwron(hdmi, 0);
dw_hdmi_phy_gen2_pddq(hdmi, 1);
 
@@ -365,9 +374,10 

[PATCH v2 3/6] drm/sun4i: sun8i-hdmi-phy: Used device-managed clocks/resets

2022-06-14 Thread Samuel Holland
Now that the HDMI PHY is using a platform driver, it can use device-
managed resources. Use these, as well as the dev_err_probe helper, to
simplify the probe function and get rid of the remove function.

Signed-off-by: Samuel Holland 
---

Changes in v2:
 - Move error handling inside variant checks in probe function

 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 90 --
 1 file changed, 26 insertions(+), 64 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 10504c2de132..9086ce547fad 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -673,10 +673,8 @@ int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct 
device_node *node)
 static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
 {
struct device *dev = >dev;
-   struct device_node *node = dev->of_node;
struct sun8i_hdmi_phy *phy;
void __iomem *regs;
-   int ret;
 
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
if (!phy)
@@ -686,88 +684,52 @@ static int sun8i_hdmi_phy_probe(struct platform_device 
*pdev)
phy->dev = dev;
 
regs = devm_platform_ioremap_resource(pdev, 0);
-   if (IS_ERR(regs)) {
-   dev_err(dev, "Couldn't map the HDMI PHY registers\n");
-   return PTR_ERR(regs);
-   }
+   if (IS_ERR(regs))
+   return dev_err_probe(dev, PTR_ERR(regs),
+"Couldn't map the HDMI PHY registers\n");
 
phy->regs = devm_regmap_init_mmio(dev, regs,
  _hdmi_phy_regmap_config);
-   if (IS_ERR(phy->regs)) {
-   dev_err(dev, "Couldn't create the HDMI PHY regmap\n");
-   return PTR_ERR(phy->regs);
-   }
+   if (IS_ERR(phy->regs))
+   return dev_err_probe(dev, PTR_ERR(phy->regs),
+"Couldn't create the HDMI PHY regmap\n");
 
-   phy->clk_bus = of_clk_get_by_name(node, "bus");
-   if (IS_ERR(phy->clk_bus)) {
-   dev_err(dev, "Could not get bus clock\n");
-   return PTR_ERR(phy->clk_bus);
-   }
+   phy->clk_bus = devm_clk_get(dev, "bus");
+   if (IS_ERR(phy->clk_bus))
+   return dev_err_probe(dev, PTR_ERR(phy->clk_bus),
+"Could not get bus clock\n");
 
-   phy->clk_mod = of_clk_get_by_name(node, "mod");
-   if (IS_ERR(phy->clk_mod)) {
-   dev_err(dev, "Could not get mod clock\n");
-   ret = PTR_ERR(phy->clk_mod);
-   goto err_put_clk_bus;
-   }
+   phy->clk_mod = devm_clk_get(dev, "mod");
+   if (IS_ERR(phy->clk_mod))
+   return dev_err_probe(dev, PTR_ERR(phy->clk_mod),
+"Could not get mod clock\n");
 
if (phy->variant->has_phy_clk) {
-   phy->clk_pll0 = of_clk_get_by_name(node, "pll-0");
-   if (IS_ERR(phy->clk_pll0)) {
-   dev_err(dev, "Could not get pll-0 clock\n");
-   ret = PTR_ERR(phy->clk_pll0);
-   goto err_put_clk_mod;
-   }
+   phy->clk_pll0 = devm_clk_get(dev, "pll-0");
+   if (IS_ERR(phy->clk_pll0))
+   return dev_err_probe(dev, PTR_ERR(phy->clk_pll0),
+"Could not get pll-0 clock\n");
 
if (phy->variant->has_second_pll) {
-   phy->clk_pll1 = of_clk_get_by_name(node, "pll-1");
-   if (IS_ERR(phy->clk_pll1)) {
-   dev_err(dev, "Could not get pll-1 clock\n");
-   ret = PTR_ERR(phy->clk_pll1);
-   goto err_put_clk_pll0;
-   }
+   phy->clk_pll1 = devm_clk_get(dev, "pll-1");
+   if (IS_ERR(phy->clk_pll1))
+   return dev_err_probe(dev, 
PTR_ERR(phy->clk_pll1),
+"Could not get pll-1 
clock\n");
}
}
 
-   phy->rst_phy = of_reset_control_get_shared(node, "phy");
-   if (IS_ERR(phy->rst_phy)) {
-   dev_err(dev, "Could not get phy reset control\n");
-   ret = PTR_ERR(phy->rst_phy);
-   goto err_put_clk_pll1;
-   }
+   phy->rst_phy = devm_reset_control_get_shared(dev, "phy");
+   if (IS_ERR(phy->rst_phy))
+   return dev_err_probe(dev, PTR_ERR(phy->rst_phy),
+   

[PATCH v2 2/6] drm/sun4i: sun8i-hdmi-phy: Use devm_platform_ioremap_resource

2022-06-14 Thread Samuel Holland
The struct resource is not used for anything else, so we can simplify
the code a bit by using the helper function.

Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 9 +
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 4553e04144fe..10504c2de132 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -675,7 +675,6 @@ static int sun8i_hdmi_phy_probe(struct platform_device 
*pdev)
struct device *dev = >dev;
struct device_node *node = dev->of_node;
struct sun8i_hdmi_phy *phy;
-   struct resource res;
void __iomem *regs;
int ret;
 
@@ -686,13 +685,7 @@ static int sun8i_hdmi_phy_probe(struct platform_device 
*pdev)
phy->variant = of_device_get_match_data(dev);
phy->dev = dev;
 
-   ret = of_address_to_resource(node, 0, );
-   if (ret) {
-   dev_err(dev, "phy: Couldn't get our resources\n");
-   return ret;
-   }
-
-   regs = devm_ioremap_resource(dev, );
+   regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs)) {
dev_err(dev, "Couldn't map the HDMI PHY registers\n");
return PTR_ERR(regs);
-- 
2.35.1



[PATCH v2 1/6] drm/sun4i: sun8i-hdmi-phy: Use of_device_get_match_data

2022-06-14 Thread Samuel Holland
Now that the HDMI PHY is using a platform driver, we can use the usual
helper function for getting the variant structure.

Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h  |  2 +-
 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 11 ++-
 2 files changed, 3 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h 
b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
index bffe1b9cd3dc..0adbfac6eb31 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
@@ -173,7 +173,7 @@ struct sun8i_hdmi_phy {
unsigned intrcal;
struct regmap   *regs;
struct reset_control*rst_phy;
-   struct sun8i_hdmi_phy_variant   *variant;
+   const struct sun8i_hdmi_phy_variant *variant;
 };
 
 struct sun8i_dw_hdmi_quirks {
diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 2860e6bff8b7..4553e04144fe 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -565,7 +565,7 @@ void sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy *phy)
 void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
struct dw_hdmi_plat_data *plat_data)
 {
-   struct sun8i_hdmi_phy_variant *variant = phy->variant;
+   const struct sun8i_hdmi_phy_variant *variant = phy->variant;
 
if (variant->is_custom_phy) {
plat_data->phy_ops = _hdmi_phy_ops;
@@ -672,7 +672,6 @@ int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct 
device_node *node)
 
 static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
 {
-   const struct of_device_id *match;
struct device *dev = >dev;
struct device_node *node = dev->of_node;
struct sun8i_hdmi_phy *phy;
@@ -680,17 +679,11 @@ static int sun8i_hdmi_phy_probe(struct platform_device 
*pdev)
void __iomem *regs;
int ret;
 
-   match = of_match_node(sun8i_hdmi_phy_of_table, node);
-   if (!match) {
-   dev_err(dev, "Incompatible HDMI PHY\n");
-   return -EINVAL;
-   }
-
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
if (!phy)
return -ENOMEM;
 
-   phy->variant = (struct sun8i_hdmi_phy_variant *)match->data;
+   phy->variant = of_device_get_match_data(dev);
phy->dev = dev;
 
ret = of_address_to_resource(node, 0, );
-- 
2.35.1



[PATCH v2 0/6] drm/sun4i: HDMI PHY cleanup/refactoring

2022-06-14 Thread Samuel Holland
This series prepares the sun8i HDMI PHY driver for supporting the new
custom PHY in the Allwinner D1 SoC. No functional change intended here.

This series was tested on D1, H3, and H6.

Changes in v2:
 - Move error handling inside variant checks in probe function

Samuel Holland (6):
  drm/sun4i: sun8i-hdmi-phy: Use of_device_get_match_data
  drm/sun4i: sun8i-hdmi-phy: Use devm_platform_ioremap_resource
  drm/sun4i: sun8i-hdmi-phy: Used device-managed clocks/resets
  drm/sun4i: sun8i-hdmi-phy: Support multiple custom PHY ops
  drm/sun4i: sun8i-hdmi-phy: Separate A83T and H3 PHY ops
  drm/sun4i: sun8i-hdmi-phy: Group PHY ops functions by generation

 drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h  |   9 +-
 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 242 ++---
 2 files changed, 97 insertions(+), 154 deletions(-)

-- 
2.35.1



[PATCH] drm/sun4i: dw-hdmi: Fix ddc-en GPIO consumer conflict

2022-06-14 Thread Samuel Holland
commit 6de79dd3a920 ("drm/bridge: display-connector: add ddc-en gpio
support") added a consumer for this GPIO in the HDMI connector device.
This new consumer conflicts with the pre-existing GPIO consumer in the
sun8i HDMI controller driver, which prevents the driver from probing:

  [4.983358] display-connector connector: GPIO lookup for consumer ddc-en
  [4.983364] display-connector connector: using device tree for GPIO lookup
  [4.983392] gpio-226 (ddc-en): gpiod_request: status -16
  [4.983399] sun8i-dw-hdmi 600.hdmi: Couldn't get ddc-en gpio
  [4.983618] sun4i-drm display-engine: failed to bind 600.hdmi (ops 
sun8i_dw_hdmi_ops [sun8i_drm_hdmi]): -16
  [4.984082] sun4i-drm display-engine: Couldn't bind all pipelines 
components
  [4.984171] sun4i-drm display-engine: adev bind failed: -16
  [4.984179] sun8i-dw-hdmi: probe of 600.hdmi failed with error -16

Both drivers have the same behavior: they leave the GPIO active for the
life of the device. Let's take advantage of the new implementation, and
drop the now-obsolete code from the HDMI controller driver.

Fixes: 6de79dd3a920 ("drm/bridge: display-connector: add ddc-en gpio support")
Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 54 ++-
 drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h |  2 -
 2 files changed, 4 insertions(+), 52 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c 
b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
index a8d75fd7e9f4..477cb6985b4d 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
@@ -93,34 +93,10 @@ static u32 sun8i_dw_hdmi_find_possible_crtcs(struct 
drm_device *drm,
return crtcs;
 }
 
-static int sun8i_dw_hdmi_find_connector_pdev(struct device *dev,
-struct platform_device **pdev_out)
-{
-   struct platform_device *pdev;
-   struct device_node *remote;
-
-   remote = of_graph_get_remote_node(dev->of_node, 1, -1);
-   if (!remote)
-   return -ENODEV;
-
-   if (!of_device_is_compatible(remote, "hdmi-connector")) {
-   of_node_put(remote);
-   return -ENODEV;
-   }
-
-   pdev = of_find_device_by_node(remote);
-   of_node_put(remote);
-   if (!pdev)
-   return -ENODEV;
-
-   *pdev_out = pdev;
-   return 0;
-}
-
 static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
  void *data)
 {
-   struct platform_device *pdev = to_platform_device(dev), *connector_pdev;
+   struct platform_device *pdev = to_platform_device(dev);
struct dw_hdmi_plat_data *plat_data;
struct drm_device *drm = data;
struct device_node *phy_node;
@@ -167,30 +143,16 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct 
device *master,
return dev_err_probe(dev, PTR_ERR(hdmi->regulator),
 "Couldn't get regulator\n");
 
-   ret = sun8i_dw_hdmi_find_connector_pdev(dev, _pdev);
-   if (!ret) {
-   hdmi->ddc_en = gpiod_get_optional(_pdev->dev,
- "ddc-en", GPIOD_OUT_HIGH);
-   platform_device_put(connector_pdev);
-
-   if (IS_ERR(hdmi->ddc_en)) {
-   dev_err(dev, "Couldn't get ddc-en gpio\n");
-   return PTR_ERR(hdmi->ddc_en);
-   }
-   }
-
ret = regulator_enable(hdmi->regulator);
if (ret) {
dev_err(dev, "Failed to enable regulator\n");
-   goto err_unref_ddc_en;
+   return ret;
}
 
-   gpiod_set_value(hdmi->ddc_en, 1);
-
ret = reset_control_deassert(hdmi->rst_ctrl);
if (ret) {
dev_err(dev, "Could not deassert ctrl reset control\n");
-   goto err_disable_ddc_en;
+   goto err_disable_regulator;
}
 
ret = clk_prepare_enable(hdmi->clk_tmds);
@@ -245,12 +207,8 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct 
device *master,
clk_disable_unprepare(hdmi->clk_tmds);
 err_assert_ctrl_reset:
reset_control_assert(hdmi->rst_ctrl);
-err_disable_ddc_en:
-   gpiod_set_value(hdmi->ddc_en, 0);
+err_disable_regulator:
regulator_disable(hdmi->regulator);
-err_unref_ddc_en:
-   if (hdmi->ddc_en)
-   gpiod_put(hdmi->ddc_en);
 
return ret;
 }
@@ -264,11 +222,7 @@ static void sun8i_dw_hdmi_unbind(struct device *dev, 
struct device *master,
sun8i_hdmi_phy_deinit(hdmi->phy);
clk_disable_unprepare(hdmi->clk_tmds);
reset_control_assert(hdmi->rst_ctrl);
-   gpiod_set_value(hdmi->ddc_en, 0);
regulator_disable(hdmi->regulator);
-
-   if (hdmi->

Re: [PATCH AUTOSEL 4.19 16/38] drm/sun4i: Add support for D1 TCONs

2022-05-30 Thread Samuel Holland
Hi Sasha,

On 5/30/22 8:49 AM, Sasha Levin wrote:
> From: Samuel Holland 
> 
> [ Upstream commit b9b52d2f4aafa2bd637ace0f24615bdad8e49f01 ]
> 
> D1 has a TCON TOP, so its quirks are similar to those for the R40 TCONs.
> While there are some register changes, the part of the TCON TV supported
> by the driver matches the R40 quirks, so that quirks structure can be
> reused. D1 has the first supported TCON LCD with a TCON TOP, so the TCON
> LCD needs a new quirks structure.
> 
> D1's TCON LCD hardware supports LVDS; in fact it provides dual-link LVDS
> from a single TCON. However, it comes with a brand new LVDS PHY. Since
> this PHY has not been tested, leave out LVDS driver support for now.
> 
> Signed-off-by: Samuel Holland 
> Reviewed-by: Jernej Skrabec 
> Signed-off-by: Maxime Ripard 
> Link: 
> https://patchwork.freedesktop.org/patch/msgid/20220424162633.12369-14-sam...@sholland.org
> Signed-off-by: Sasha Levin 

This patch adds support for hardware in a SoC that will not boot on earlier
kernel releases, so there is no benefit to backporting the patch (to any
previous release).

Regards,
Samuel

> ---
>  drivers/gpu/drm/sun4i/sun4i_tcon.c | 8 
>  1 file changed, 8 insertions(+)
> 
> diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c 
> b/drivers/gpu/drm/sun4i/sun4i_tcon.c
> index 113c032a2720..0ebb7c1dfee6 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
> @@ -1316,6 +1316,12 @@ static const struct sun4i_tcon_quirks 
> sun9i_a80_tcon_tv_quirks = {
>   .needs_edp_reset = true,
>  };
>  
> +static const struct sun4i_tcon_quirks sun20i_d1_lcd_quirks = {
> + .has_channel_0  = true,
> + .dclk_min_div   = 1,
> + .set_mux= sun8i_r40_tcon_tv_set_mux,
> +};
> +
>  /* sun4i_drv uses this list to check if a device node is a TCON */
>  const struct of_device_id sun4i_tcon_of_table[] = {
>   { .compatible = "allwinner,sun4i-a10-tcon", .data = _a10_quirks },
> @@ -1329,6 +1335,8 @@ const struct of_device_id sun4i_tcon_of_table[] = {
>   { .compatible = "allwinner,sun8i-v3s-tcon", .data = _v3s_quirks },
>   { .compatible = "allwinner,sun9i-a80-tcon-lcd", .data = 
> _a80_tcon_lcd_quirks },
>   { .compatible = "allwinner,sun9i-a80-tcon-tv", .data = 
> _a80_tcon_tv_quirks },
> + { .compatible = "allwinner,sun20i-d1-tcon-lcd", .data = 
> _d1_lcd_quirks },
> + { .compatible = "allwinner,sun20i-d1-tcon-tv", .data = 
> _r40_tv_quirks },
>   { }
>  };
>  MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
> 



Re: [PATCH] drm/sun4i: mixer: fix scanline for V3s and D1

2022-05-24 Thread Samuel Holland
On 5/23/22 8:14 AM, Icenowy Zheng wrote:
> 在 2022-05-22星期日的 10:36 +0200,Jernej Škrabec写道:
>> Hi!
>>
>> Dne sobota, 21. maj 2022 ob 15:34:43 CEST je Genfu Pan napisal(a):
>>> Accrording the SDK from Allwinner, the scanline value of yuv and
>>> rgb for
>>> V3s are both 1024.
>>
>> s/scanline value/scanline length/
>>
>> Which SDK? All SDKs that I have or found on internet don't mention
>> YUV nor RGB 
>> scanline limit. That doesn't mean there is none, I'm just unable to
>> verify 
>> your claim. Did you test this by yourself? Also, please make YUV
>> scanline 
>> change separate patch with fixes tag.
> 
> BTW I think chip manuals all say that the chip supports NxN resolution
> in DE2 chapter, e.g. the V3 datasheet says DE2 "Output size up to
> 1024x1024".
> 
> However there's no information about D1's second mixer.

My information comes from the BSP driver[0]:

static const int sun8iw20_de_scale_line_buffer[] = {
/* DISP0 */
2048,
/* DISP1 */
1024,
};

It looks like the value returned from de_feat_get_scale_linebuf() may be used
for RGB as well, if scaling is enabled. This appears to be the "format == 3"
case in de_rtmx_get_coarse_fac[1]. On the other hand, the code for V3 has
specific code for the RGB limit[2].

Is there some test I can do on D1 to see what the right value for RGB is?

Regards,
Samuel

[0]:
https://github.com/Tina-Linux/tina-d1x-linux-5.4/blob/master/drivers/video/fbdev/sunxi/disp2/disp/de/lowlevel_v2x/de_feat.c#L182
[1]:
https://github.com/Tina-Linux/tina-d1x-linux-5.4/blob/master/drivers/video/fbdev/sunxi/disp2/disp/de/lowlevel_v2x/de_rtmx.c#L1588
[2]:
https://github.com/Tina-Linux/tina-d1x-linux-5.4/blob/master/drivers/video/fbdev/sunxi/disp2/disp/de/lowlevel_sun8iw8/de_rtmx.c#L1211

>>> The is also the same for mixer 1 of D1. Currently the
>>> scanline value of rgb is hardcoded to 2048 for all SOCs.
>>>
>>> Change the scanline_yuv property of V3s to 1024. > Add the
>>> scanline_rgb
>>> property to the mixer config and replace the hardcoded value with
>>> it before
>>> scaling.
>>
>> I guess RGB scanline patch would also need fixes tag, since it fixes
>> existing 
>> bug.
>>
>>>
>>> Signed-off-by: Genfu Pan 
>>> ---
>>>  drivers/gpu/drm/sun4i/sun8i_mixer.c    | 13 -
>>>  drivers/gpu/drm/sun4i/sun8i_mixer.h    |  1 +
>>>  drivers/gpu/drm/sun4i/sun8i_vi_layer.c |  3 +--
>>>  3 files changed, 14 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c
>>> b/drivers/gpu/drm/sun4i/sun8i_mixer.c index 875a1156c..e64e08207
>>> 100644
>>> --- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
>>> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>>> @@ -567,6 +567,7 @@ static const struct sun8i_mixer_cfg
>>> sun8i_a83t_mixer0_cfg = { .ccsc = CCSC_MIXER0_LAYOUT,
>>> .scaler_mask= 0xf,
>>> .scanline_yuv   = 2048,
>>> +   .scanline_rgb   = 2048,
>>> .ui_num = 3,
>>> .vi_num = 1,
>>>  };
>>> @@ -575,6 +576,7 @@ static const struct sun8i_mixer_cfg
>>> sun8i_a83t_mixer1_cfg = { .ccsc = CCSC_MIXER1_LAYOUT,
>>> .scaler_mask= 0x3,
>>> .scanline_yuv   = 2048,
>>> +   .scanline_rgb   = 2048,
>>> .ui_num = 1,
>>> .vi_num = 1,
>>>  };
>>> @@ -584,6 +586,7 @@ static const struct sun8i_mixer_cfg
>>> sun8i_h3_mixer0_cfg
>>> = { .mod_rate   = 43200,
>>> .scaler_mask= 0xf,
>>> .scanline_yuv   = 2048,
>>> +   .scanline_rgb   = 2048,
>>> .ui_num = 3,
>>> .vi_num = 1,
>>>  };
>>> @@ -593,6 +596,7 @@ static const struct sun8i_mixer_cfg
>>> sun8i_r40_mixer0_cfg
>>> = { .mod_rate   = 29700,
>>> .scaler_mask= 0xf,
>>> .scanline_yuv   = 2048,
>>> +   .scanline_rgb   = 2048,
>>> .ui_num = 3,
>>> .vi_num = 1,
>>>  };
>>> @@ -602,6 +606,7 @@ static const struct sun8i_mixer_cfg
>>> sun8i_r40_mixer1_cfg
>>> = { .mod_rate   = 29700,
>>> .scaler_mask= 0x3,
>>> .scanline_yuv   = 2048,
>>> +   .scanline_rgb   = 2048,
>>> .ui_num = 1,
>>> .vi_num = 1,
>>>  };
>>> @@ -610,7 +615,8 @@ static const struct sun8i_mixer_cfg
>>> sun8i_v3s_mixer_cfg
>>> = { .vi_num = 2,
>>> .ui_num = 1,
>>> .scaler_mask = 0x3,
>>> -   .scanline_yuv = 2048,
>>> +   .scanline_yuv = 1024,
>>> +   .scanline_rgb = 1024,
>>> .ccsc = CCSC_MIXER0_LAYOUT,
>>> .mod_rate = 15000,
>>>  };
>>> @@ -620,6 +626,7 @@ static const struct sun8i_mixer_cfg
>>> sun20i_d1_mixer0_cfg
>>> = { .mod_rate   = 29700,
>>> .scaler_mask= 0x3,
>>> .scanline_yuv   = 2048,
>>> +   .scanline_rgb   = 2048,
>>> .ui_num = 1,
>>> .vi_num = 1,
>>>  };
>>> @@ -629,6 +636,7 @@ static const struct sun8i_mixer_cfg
>>> sun20i_d1_mixer1_cfg
>>> = { .mod_rate   = 29700,
>>> .scaler_mask= 0x1,
>>> .scanline_yuv   = 

[PATCH v3 13/14] drm/sun4i: Add support for D1 TCONs

2022-04-24 Thread Samuel Holland
D1 has a TCON TOP, so its quirks are similar to those for the R40 TCONs.
While there are some register changes, the part of the TCON TV supported
by the driver matches the R40 quirks, so that quirks structure can be
reused. D1 has the first supported TCON LCD with a TCON TOP, so the TCON
LCD needs a new quirks structure.

D1's TCON LCD hardware supports LVDS; in fact it provides dual-link LVDS
from a single TCON. However, it comes with a brand new LVDS PHY. Since
this PHY has not been tested, leave out LVDS driver support for now.

Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun4i_tcon.c | 8 
 1 file changed, 8 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c 
b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 88db2d2a9336..2ee158aaeb9e 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -1542,6 +1542,12 @@ static const struct sun4i_tcon_quirks 
sun9i_a80_tcon_tv_quirks = {
.needs_edp_reset = true,
 };
 
+static const struct sun4i_tcon_quirks sun20i_d1_lcd_quirks = {
+   .has_channel_0  = true,
+   .dclk_min_div   = 1,
+   .set_mux= sun8i_r40_tcon_tv_set_mux,
+};
+
 /* sun4i_drv uses this list to check if a device node is a TCON */
 const struct of_device_id sun4i_tcon_of_table[] = {
{ .compatible = "allwinner,sun4i-a10-tcon", .data = _a10_quirks },
@@ -1559,6 +1565,8 @@ const struct of_device_id sun4i_tcon_of_table[] = {
{ .compatible = "allwinner,sun8i-v3s-tcon", .data = _v3s_quirks },
{ .compatible = "allwinner,sun9i-a80-tcon-lcd", .data = 
_a80_tcon_lcd_quirks },
{ .compatible = "allwinner,sun9i-a80-tcon-tv", .data = 
_a80_tcon_tv_quirks },
+   { .compatible = "allwinner,sun20i-d1-tcon-lcd", .data = 
_d1_lcd_quirks },
+   { .compatible = "allwinner,sun20i-d1-tcon-tv", .data = 
_r40_tv_quirks },
{ }
 };
 MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
-- 
2.35.1



[PATCH v3 14/14] drm/sun4i: Add compatible for D1 display engine

2022-04-24 Thread Samuel Holland
Now that the various blocks in the D1 display engine pipeline are
supported, we can enable the overall engine.

Acked-by: Jernej Skrabec 
Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun4i_drv.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c 
b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 6a9ba8a77c77..275f7e4a03ae 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -418,6 +418,7 @@ static const struct of_device_id sun4i_drv_of_table[] = {
{ .compatible = "allwinner,sun8i-r40-display-engine" },
{ .compatible = "allwinner,sun8i-v3s-display-engine" },
{ .compatible = "allwinner,sun9i-a80-display-engine" },
+   { .compatible = "allwinner,sun20i-d1-display-engine" },
{ .compatible = "allwinner,sun50i-a64-display-engine" },
{ .compatible = "allwinner,sun50i-h6-display-engine" },
{ }
-- 
2.35.1



[PATCH v3 11/14] drm/sun4i: Add support for D1 mixers

2022-04-24 Thread Samuel Holland
D1 has a display engine with the usual pair of mixers, albeit with
relatively few layers. In fact, D1 appears to be the first SoC to have
a mixer without any UI layers. Add support for these new variants.

Acked-by: Jernej Skrabec 
Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun8i_mixer.c | 26 ++
 1 file changed, 26 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c 
b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 4ce593c99807..875a1156c04e 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -615,6 +615,24 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
.mod_rate = 15000,
 };
 
+static const struct sun8i_mixer_cfg sun20i_d1_mixer0_cfg = {
+   .ccsc   = CCSC_D1_MIXER0_LAYOUT,
+   .mod_rate   = 29700,
+   .scaler_mask= 0x3,
+   .scanline_yuv   = 2048,
+   .ui_num = 1,
+   .vi_num = 1,
+};
+
+static const struct sun8i_mixer_cfg sun20i_d1_mixer1_cfg = {
+   .ccsc   = CCSC_MIXER1_LAYOUT,
+   .mod_rate   = 29700,
+   .scaler_mask= 0x1,
+   .scanline_yuv   = 1024,
+   .ui_num = 0,
+   .vi_num = 1,
+};
+
 static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = {
.ccsc   = CCSC_MIXER0_LAYOUT,
.mod_rate   = 29700,
@@ -668,6 +686,14 @@ static const struct of_device_id sun8i_mixer_of_table[] = {
.compatible = "allwinner,sun8i-v3s-de2-mixer",
.data = _v3s_mixer_cfg,
},
+   {
+   .compatible = "allwinner,sun20i-d1-de2-mixer-0",
+   .data = _d1_mixer0_cfg,
+   },
+   {
+   .compatible = "allwinner,sun20i-d1-de2-mixer-1",
+   .data = _d1_mixer1_cfg,
+   },
{
.compatible = "allwinner,sun50i-a64-de2-mixer-0",
.data = _a64_mixer0_cfg,
-- 
2.35.1



[PATCH v3 10/14] drm/sun4i: csc: Add support for the new MMIO layout

2022-04-24 Thread Samuel Holland
D1 changes the MMIO offsets for the CSC blocks in the first mixer. The
mixers' ccsc property is used as an index into the ccsc_base array. Use
an enumeration to describe this index, and add the new set of offsets.

Signed-off-by: Samuel Holland 
---

(no changes since v2)

Changes in v2:
 - Use an enumeration for the ccsc value.

 drivers/gpu/drm/sun4i/sun8i_csc.c   |  7 ---
 drivers/gpu/drm/sun4i/sun8i_csc.h   |  1 +
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 18 +-
 drivers/gpu/drm/sun4i/sun8i_mixer.h | 14 ++
 4 files changed, 24 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c 
b/drivers/gpu/drm/sun4i/sun8i_csc.c
index 9bd62de0c288..58480d8e4f70 100644
--- a/drivers/gpu/drm/sun4i/sun8i_csc.c
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.c
@@ -8,9 +8,10 @@
 #include "sun8i_csc.h"
 #include "sun8i_mixer.h"
 
-static const u32 ccsc_base[2][2] = {
-   {CCSC00_OFFSET, CCSC01_OFFSET},
-   {CCSC10_OFFSET, CCSC11_OFFSET},
+static const u32 ccsc_base[][2] = {
+   [CCSC_MIXER0_LAYOUT]= {CCSC00_OFFSET, CCSC01_OFFSET},
+   [CCSC_MIXER1_LAYOUT]= {CCSC10_OFFSET, CCSC11_OFFSET},
+   [CCSC_D1_MIXER0_LAYOUT] = {CCSC00_OFFSET, CCSC01_D1_OFFSET},
 };
 
 /*
diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.h 
b/drivers/gpu/drm/sun4i/sun8i_csc.h
index 022cafa6c06c..828b86fd0cab 100644
--- a/drivers/gpu/drm/sun4i/sun8i_csc.h
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.h
@@ -13,6 +13,7 @@ struct sun8i_mixer;
 /* VI channel CSC units offsets */
 #define CCSC00_OFFSET 0xAA050
 #define CCSC01_OFFSET 0xFA050
+#define CCSC01_D1_OFFSET 0xFA000
 #define CCSC10_OFFSET 0xA
 #define CCSC11_OFFSET 0xF
 
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c 
b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 6b1711a9a71f..4ce593c99807 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -564,7 +564,7 @@ static int sun8i_mixer_remove(struct platform_device *pdev)
 }
 
 static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
-   .ccsc   = 0,
+   .ccsc   = CCSC_MIXER0_LAYOUT,
.scaler_mask= 0xf,
.scanline_yuv   = 2048,
.ui_num = 3,
@@ -572,7 +572,7 @@ static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = 
{
 };
 
 static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = {
-   .ccsc   = 1,
+   .ccsc   = CCSC_MIXER1_LAYOUT,
.scaler_mask= 0x3,
.scanline_yuv   = 2048,
.ui_num = 1,
@@ -580,7 +580,7 @@ static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = 
{
 };
 
 static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
-   .ccsc   = 0,
+   .ccsc   = CCSC_MIXER0_LAYOUT,
.mod_rate   = 43200,
.scaler_mask= 0xf,
.scanline_yuv   = 2048,
@@ -589,7 +589,7 @@ static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
 };
 
 static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
-   .ccsc   = 0,
+   .ccsc   = CCSC_MIXER0_LAYOUT,
.mod_rate   = 29700,
.scaler_mask= 0xf,
.scanline_yuv   = 2048,
@@ -598,7 +598,7 @@ static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
 };
 
 static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = {
-   .ccsc   = 1,
+   .ccsc   = CCSC_MIXER1_LAYOUT,
.mod_rate   = 29700,
.scaler_mask= 0x3,
.scanline_yuv   = 2048,
@@ -611,12 +611,12 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = 
{
.ui_num = 1,
.scaler_mask = 0x3,
.scanline_yuv = 2048,
-   .ccsc = 0,
+   .ccsc = CCSC_MIXER0_LAYOUT,
.mod_rate = 15000,
 };
 
 static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = {
-   .ccsc   = 0,
+   .ccsc   = CCSC_MIXER0_LAYOUT,
.mod_rate   = 29700,
.scaler_mask= 0xf,
.scanline_yuv   = 4096,
@@ -625,7 +625,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = 
{
 };
 
 static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = {
-   .ccsc   = 1,
+   .ccsc   = CCSC_MIXER1_LAYOUT,
.mod_rate   = 29700,
.scaler_mask= 0x3,
.scanline_yuv   = 2048,
@@ -634,7 +634,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = 
{
 };
 
 static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = {
-   .ccsc   = 0,
+   .ccsc   = CCSC_MIXER0_LAYOUT,
.is_de3 = true,
.mod_rate   = 6,
.scaler_mask= 0xf,
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h 
b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index 5b3fbee18671..85c94884fb9a 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -141,6 +141,15 @@
 #define SUN50I_MIXER_CDC0_EN   0xd
 #define SUN50I_MIXER_CDC1_EN  

[PATCH v3 09/14] drm/sun4i: Allow VI layers to be primary planes

2022-04-24 Thread Samuel Holland
D1's mixer 1 has no UI layers, only a single VI layer. That means the
mixer can only be used if the primary plane comes from this VI layer.
Add the code to handle this case.

Signed-off-by: Samuel Holland 
---

(no changes since v2)

Changes in v2:
 - Use Jernej's patches for mixer mode setting.

 drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c 
b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
index bb7c43036dfa..f7d0b082d634 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -542,6 +542,7 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct 
drm_device *drm,
   struct sun8i_mixer *mixer,
   int index)
 {
+   enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
u32 supported_encodings, supported_ranges;
unsigned int plane_cnt, format_count;
struct sun8i_vi_layer *layer;
@@ -560,12 +561,15 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct 
drm_device *drm,
format_count = ARRAY_SIZE(sun8i_vi_layer_formats);
}
 
+   if (!mixer->cfg->ui_num && index == 0)
+   type = DRM_PLANE_TYPE_PRIMARY;
+
/* possible crtcs are set later */
ret = drm_universal_plane_init(drm, >plane, 0,
   _vi_layer_funcs,
   formats, format_count,
   sun8i_layer_modifiers,
-  DRM_PLANE_TYPE_OVERLAY, NULL);
+  type, NULL);
if (ret) {
dev_err(drm->dev, "Couldn't initialize layer\n");
return ERR_PTR(ret);
-- 
2.35.1



[PATCH v3 12/14] drm/sun4i: Add support for D1 TCON TOP

2022-04-24 Thread Samuel Holland
D1 has a TCON TOP with TCON TV0 and DSI, but no TCON TV1. This puts the
DSI clock name at index 1 in clock-output-names. Support this by only
incrementing the index for clocks that are actually supported.

Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun8i_tcon_top.c | 15 ---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c 
b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c
index 1b9b8b48f4a7..da97682b6835 100644
--- a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c
+++ b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c
@@ -189,22 +189,23 @@ static int sun8i_tcon_top_bind(struct device *dev, struct 
device *master,
 * if TVE is active on each TCON TV. If it is, mux should be switched
 * to TVE clock parent.
 */
+   i = 0;
clk_data->hws[CLK_TCON_TOP_TV0] =
sun8i_tcon_top_register_gate(dev, "tcon-tv0", regs,
 _top->reg_lock,
-TCON_TOP_TCON_TV0_GATE, 0);
+TCON_TOP_TCON_TV0_GATE, i++);
 
if (quirks->has_tcon_tv1)
clk_data->hws[CLK_TCON_TOP_TV1] =
sun8i_tcon_top_register_gate(dev, "tcon-tv1", regs,
 _top->reg_lock,
-TCON_TOP_TCON_TV1_GATE, 1);
+TCON_TOP_TCON_TV1_GATE, 
i++);
 
if (quirks->has_dsi)
clk_data->hws[CLK_TCON_TOP_DSI] =
sun8i_tcon_top_register_gate(dev, "dsi", regs,
 _top->reg_lock,
-TCON_TOP_TCON_DSI_GATE, 2);
+TCON_TOP_TCON_DSI_GATE, 
i++);
 
for (i = 0; i < CLK_NUM; i++)
if (IS_ERR(clk_data->hws[i])) {
@@ -272,6 +273,10 @@ static const struct sun8i_tcon_top_quirks 
sun8i_r40_tcon_top_quirks = {
.has_dsi= true,
 };
 
+static const struct sun8i_tcon_top_quirks sun20i_d1_tcon_top_quirks = {
+   .has_dsi= true,
+};
+
 static const struct sun8i_tcon_top_quirks sun50i_h6_tcon_top_quirks = {
/* Nothing special */
 };
@@ -282,6 +287,10 @@ const struct of_device_id sun8i_tcon_top_of_table[] = {
.compatible = "allwinner,sun8i-r40-tcon-top",
.data = _r40_tcon_top_quirks
},
+   {
+   .compatible = "allwinner,sun20i-d1-tcon-top",
+   .data = _d1_tcon_top_quirks
+   },
{
.compatible = "allwinner,sun50i-h6-tcon-top",
.data = _h6_tcon_top_quirks
-- 
2.35.1



[PATCH v3 07/14] sun4i/drm: backend: use mode_set engine callback

2022-04-24 Thread Samuel Holland
From: Jernej Skrabec 

Newly introduced mode_set callback in engine structure is a much better
place for setting backend output size and interlace mode for following
reasons:
1. Aforementioned properties change only when mode changes, so it's
   enough to be set only once per mode set. Currently it's done whenever
   properties of primary plane are changed.
2. It's assumed that primary plane will always cover whole screen. While
   this is true most of the time, it's not always. Planes are universal.
   There is no reason to add artificial limitation to primary plane.

Signed-off-by: Jernej Skrabec 
[Samuel: drop unused 'interlaced' variable]
Signed-off-by: Samuel Holland 
---

(no changes since v2)

Changes in v2:
 - Use Jernej's patches for mixer mode setting.

 drivers/gpu/drm/sun4i/sun4i_backend.c | 40 +--
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c 
b/drivers/gpu/drm/sun4i/sun4i_backend.c
index f52ff4e6c662..decd95ad519d 100644
--- a/drivers/gpu/drm/sun4i/sun4i_backend.c
+++ b/drivers/gpu/drm/sun4i/sun4i_backend.c
@@ -172,14 +172,6 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend 
*backend,
 
DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
 
-   if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
-   DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: 
%u\n",
-state->crtc_w, state->crtc_h);
-   regmap_write(backend->engine.regs, SUN4I_BACKEND_DISSIZE_REG,
-SUN4I_BACKEND_DISSIZE(state->crtc_w,
-  state->crtc_h));
-   }
-
/* Set height and width */
DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
 state->crtc_w, state->crtc_h);
@@ -259,7 +251,6 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend 
*backend,
 {
struct drm_plane_state *state = plane->state;
struct drm_framebuffer *fb = state->fb;
-   bool interlaced = false;
u32 val;
int ret;
 
@@ -267,17 +258,6 @@ int sun4i_backend_update_layer_formats(struct 
sun4i_backend *backend,
regmap_update_bits(backend->engine.regs, 
SUN4I_BACKEND_ATTCTL_REG0(layer),
   SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN, 0);
 
-   if (plane->state->crtc)
-   interlaced = plane->state->crtc->state->adjusted_mode.flags
-   & DRM_MODE_FLAG_INTERLACE;
-
-   regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG,
-  SUN4I_BACKEND_MODCTL_ITLMOD_EN,
-  interlaced ? SUN4I_BACKEND_MODCTL_ITLMOD_EN : 0);
-
-   DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n",
-interlaced ? "on" : "off");
-
val = SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA(state->alpha >> 8);
if (state->alpha != DRM_BLEND_ALPHA_OPAQUE)
val |= SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA_EN;
@@ -654,6 +634,25 @@ static void sun4i_backend_vblank_quirk(struct sunxi_engine 
*engine)
spin_unlock(>frontend_lock);
 };
 
+static void sun4i_backend_mode_set(struct sunxi_engine *engine,
+  const struct drm_display_mode *mode)
+{
+   bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+
+   DRM_DEBUG_DRIVER("Updating global size W: %u H: %u\n",
+mode->hdisplay, mode->vdisplay);
+
+   regmap_write(engine->regs, SUN4I_BACKEND_DISSIZE_REG,
+SUN4I_BACKEND_DISSIZE(mode->hdisplay, mode->vdisplay));
+
+   regmap_update_bits(engine->regs, SUN4I_BACKEND_MODCTL_REG,
+  SUN4I_BACKEND_MODCTL_ITLMOD_EN,
+  interlaced ? SUN4I_BACKEND_MODCTL_ITLMOD_EN : 0);
+
+   DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n",
+interlaced ? "on" : "off");
+}
+
 static int sun4i_backend_init_sat(struct device *dev) {
struct sun4i_backend *backend = dev_get_drvdata(dev);
int ret;
@@ -765,6 +764,7 @@ static const struct sunxi_engine_ops 
sun4i_backend_engine_ops = {
.apply_color_correction = sun4i_backend_apply_color_correction,
.disable_color_correction   = 
sun4i_backend_disable_color_correction,
.vblank_quirk   = sun4i_backend_vblank_quirk,
+   .mode_set   = sun4i_backend_mode_set,
 };
 
 static const struct regmap_config sun4i_backend_regmap_config = {
-- 
2.35.1



[PATCH v3 05/14] drm/sun4i: Allow building the driver on RISC-V

2022-04-24 Thread Samuel Holland
Allwinner D1 is a RISC-V SoC which contains a DE 2.0 engine. Let's
remove the dependency on a specific CPU architecture, so the driver can
be built wherever ARCH_SUNXI is selected.

Acked-by: Jernej Skrabec 
Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index befc5a80222d..3a43c436c74a 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config DRM_SUN4I
tristate "DRM Support for Allwinner A10 Display Engine"
-   depends on DRM && (ARM || ARM64) && COMMON_CLK
+   depends on DRM && COMMON_CLK
depends on ARCH_SUNXI || COMPILE_TEST
select DRM_GEM_CMA_HELPER
select DRM_KMS_HELPER
-- 
2.35.1



[PATCH v3 08/14] sun4i/drm: sun8i: use mode_set engine callback

2022-04-24 Thread Samuel Holland
From: Jernej Skrabec 

Newly introduced mode_set callback in engine structure is a much better
place for setting mixer output size and interlace mode for the following
reasons:
1. Aforementioned properties change only when mode changes, so it's
   enough to be set only once per mode set. Currently it's done whenever
   properties of primary plane are changed.
2. It's assumed that primary plane will always cover whole screen. While
   this is true most of the time, it's not always. DE2/3 planes are
   universal and mostly equal in functionality. There is no reason to
   add artificial limitation to primary planes.
3. The current code only works for UI layers, but some mixers do not
   have any UI layers.

Signed-off-by: Jernej Skrabec 
[Samuel: update commit message]
Signed-off-by: Samuel Holland 
---

(no changes since v2)

Changes in v2:
 - Use Jernej's patches for mixer mode setting.

 drivers/gpu/drm/sun4i/sun8i_mixer.c| 30 ++
 drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 30 --
 2 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c 
b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index f5e8aeaa3cdf..6b1711a9a71f 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -298,9 +298,39 @@ static struct drm_plane **sun8i_layers_init(struct 
drm_device *drm,
return planes;
 }
 
+static void sun8i_mixer_mode_set(struct sunxi_engine *engine,
+const struct drm_display_mode *mode)
+{
+   struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
+   u32 bld_base, size, val;
+   bool interlaced;
+
+   bld_base = sun8i_blender_base(mixer);
+   interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+   size = SUN8I_MIXER_SIZE(mode->hdisplay, mode->vdisplay);
+
+   DRM_DEBUG_DRIVER("Updating global size W: %u H: %u\n",
+mode->hdisplay, mode->vdisplay);
+
+   regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_SIZE, size);
+   regmap_write(engine->regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size);
+
+   if (interlaced)
+   val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
+   else
+   val = 0;
+
+   regmap_update_bits(engine->regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base),
+  SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, val);
+
+   DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
+interlaced ? "on" : "off");
+}
+
 static const struct sunxi_engine_ops sun8i_engine_ops = {
.commit = sun8i_mixer_commit,
.layers_init= sun8i_layers_init,
+   .mode_set   = sun8i_mixer_mode_set,
 };
 
 static const struct regmap_config sun8i_mixer_regmap_config = {
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c 
b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
index 7845c2a53a7f..4632dea2dc1e 100644
--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
@@ -120,36 +120,6 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer 
*mixer, int channel,
insize = SUN8I_MIXER_SIZE(src_w, src_h);
outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
 
-   if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
-   bool interlaced = false;
-   u32 val;
-
-   DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: 
%u\n",
-dst_w, dst_h);
-   regmap_write(mixer->engine.regs,
-SUN8I_MIXER_GLOBAL_SIZE,
-outsize);
-   regmap_write(mixer->engine.regs,
-SUN8I_MIXER_BLEND_OUTSIZE(bld_base), outsize);
-
-   if (state->crtc)
-   interlaced = state->crtc->state->adjusted_mode.flags
-   & DRM_MODE_FLAG_INTERLACE;
-
-   if (interlaced)
-   val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
-   else
-   val = 0;
-
-   regmap_update_bits(mixer->engine.regs,
-  SUN8I_MIXER_BLEND_OUTCTL(bld_base),
-  SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
-  val);
-
-   DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
-interlaced ? "on" : "off");
-   }
-
/* Set height and width */
DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
 state->src.x1 >> 16, state->src.y1 >> 16);
-- 
2.35.1



[PATCH v3 04/14] drm/sun4i: hdmi: Use more portable I/O helpers

2022-04-24 Thread Samuel Holland
readsb/writesb are unavailable on some architectures. In preparation for
removing the Kconfig architecture dependency, switch to the equivalent
but more portable ioread/write8_rep helpers.

Reported-by: kernel test robot 
Signed-off-by: Samuel Holland 
---

(no changes since v2)

Changes in v2:
 - New patch: I/O helper portability

 drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c 
b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c
index b66fa27fe6ea..c7d7e9fff91c 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c
@@ -56,9 +56,9 @@ static int fifo_transfer(struct sun4i_hdmi *hdmi, u8 *buf, 
int len, bool read)
return -EIO;
 
if (read)
-   readsb(hdmi->base + hdmi->variant->ddc_fifo_reg, buf, len);
+   ioread8_rep(hdmi->base + hdmi->variant->ddc_fifo_reg, buf, len);
else
-   writesb(hdmi->base + hdmi->variant->ddc_fifo_reg, buf, len);
+   iowrite8_rep(hdmi->base + hdmi->variant->ddc_fifo_reg, buf, 
len);
 
/* Clear FIFO request bit by forcing a write to that bit */
regmap_field_force_write(hdmi->field_ddc_int_status,
-- 
2.35.1



[PATCH v3 06/14] sun4i/drm: engine: Add mode_set callback

2022-04-24 Thread Samuel Holland
From: Jernej Skrabec 

This optional callback is useful for setting properties which depends
only on current mode. Such properties are width, height and interlaced
output.

These properties are currently set in update layer callback for primary
plane which is less than ideal. More about that in follow up patches,
which will migrate that code to this newly defined callback.

Signed-off-by: Jernej Skrabec 
Signed-off-by: Samuel Holland 
---

(no changes since v2)

Changes in v2:
 - Use Jernej's patches for mixer mode setting.

 drivers/gpu/drm/sun4i/sun4i_crtc.c   |  1 +
 drivers/gpu/drm/sun4i/sunxi_engine.h | 27 +++
 2 files changed, 28 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c 
b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index 45d9eb552d86..c06d7cd45388 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -146,6 +146,7 @@ static void sun4i_crtc_mode_set_nofb(struct drm_crtc *crtc)
struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
 
sun4i_tcon_mode_set(scrtc->tcon, encoder, mode);
+   sunxi_engine_mode_set(scrtc->engine, mode);
 }
 
 static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = {
diff --git a/drivers/gpu/drm/sun4i/sunxi_engine.h 
b/drivers/gpu/drm/sun4i/sunxi_engine.h
index 548710a936d5..ec8cf9b2bda4 100644
--- a/drivers/gpu/drm/sun4i/sunxi_engine.h
+++ b/drivers/gpu/drm/sun4i/sunxi_engine.h
@@ -9,6 +9,7 @@
 struct drm_plane;
 struct drm_device;
 struct drm_crtc_state;
+struct drm_display_mode;
 
 struct sunxi_engine;
 
@@ -108,6 +109,17 @@ struct sunxi_engine_ops {
 * This function is optional.
 */
void (*vblank_quirk)(struct sunxi_engine *engine);
+
+   /**
+* @mode_set
+*
+* This callback is used to set mode related parameters
+* like interlacing, screen size, etc. once per mode set.
+*
+* This function is optional.
+*/
+   void (*mode_set)(struct sunxi_engine *engine,
+const struct drm_display_mode *mode);
 };
 
 /**
@@ -181,4 +193,19 @@ sunxi_engine_disable_color_correction(struct sunxi_engine 
*engine)
if (engine->ops && engine->ops->disable_color_correction)
engine->ops->disable_color_correction(engine);
 }
+
+/**
+ * sunxi_engine_mode_set - Inform engine of a new mode
+ * @engine:pointer to the engine
+ * @mode:  new mode
+ *
+ * Engine can use this functionality to set specifics once per mode change.
+ */
+static inline void
+sunxi_engine_mode_set(struct sunxi_engine *engine,
+ const struct drm_display_mode *mode)
+{
+   if (engine->ops && engine->ops->mode_set)
+   engine->ops->mode_set(engine, mode);
+}
 #endif /* _SUNXI_ENGINE_H_ */
-- 
2.35.1



[PATCH v3 01/14] dt-bindings: display: Separate clock item lists by compatible

2022-04-24 Thread Samuel Holland
So far, the binding and driver have relied on the fact that the H6
clocks are both a prefix and a subset of the R40 clocks. This allows
them to share the clocks/clock-names items and the clock-output-names
order between the hardware variants.

However, the D1 hardware has TCON TV0 and DSI, but no TCON TV1. This
cannot be supported by the existing scheme because it puts a gap in the
middle of the item lists. To prepare for adding D1 support, use separate
lists for variants with different combinations of clocks.

Reviewed-by: Krzysztof Kozlowski 
Signed-off-by: Samuel Holland 
---

Changes in v3:
 - Drop redundant minItems and maxItems

 .../display/allwinner,sun8i-r40-tcon-top.yaml | 105 ++
 1 file changed, 61 insertions(+), 44 deletions(-)

diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml 
b/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml
index 61ef7b337218..449fa99aa51b 100644
--- 
a/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml
+++ 
b/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml
@@ -48,31 +48,15 @@ properties:
 
   clocks:
 minItems: 2
-items:
-  - description: The TCON TOP interface clock
-  - description: The TCON TOP TV0 clock
-  - description: The TCON TOP TVE0 clock
-  - description: The TCON TOP TV1 clock
-  - description: The TCON TOP TVE1 clock
-  - description: The TCON TOP MIPI DSI clock
+maxItems: 6
 
   clock-names:
 minItems: 2
-items:
-  - const: bus
-  - const: tcon-tv0
-  - const: tve0
-  - const: tcon-tv1
-  - const: tve1
-  - const: dsi
+maxItems: 6
 
   clock-output-names:
 minItems: 1
 maxItems: 3
-description: >
-  The first item is the name of the clock created for the TV0
-  channel, the second item is the name of the TCON TV1 channel
-  clock and the third one is the name of the DSI channel clock.
 
   resets:
 maxItems: 1
@@ -129,32 +113,65 @@ required:
 
 additionalProperties: false
 
-if:
-  properties:
-compatible:
-  contains:
-const: allwinner,sun50i-h6-tcon-top
-
-then:
-  properties:
-clocks:
-  maxItems: 2
-
-clock-output-names:
-  maxItems: 1
-
-else:
-  properties:
-clocks:
-  minItems: 6
-
-clock-output-names:
-  minItems: 3
-
-ports:
-  required:
-- port@2
-- port@3
+allOf:
+  - if:
+  properties:
+compatible:
+  contains:
+const: allwinner,sun8i-r40-tcon-top
+
+then:
+  properties:
+clocks:
+  items:
+- description: The TCON TOP interface clock
+- description: The TCON TOP TV0 clock
+- description: The TCON TOP TVE0 clock
+- description: The TCON TOP TV1 clock
+- description: The TCON TOP TVE1 clock
+- description: The TCON TOP MIPI DSI clock
+
+clock-names:
+  items:
+- const: bus
+- const: tcon-tv0
+- const: tve0
+- const: tcon-tv1
+- const: tve1
+- const: dsi
+
+clock-output-names:
+  items:
+- description: TCON TV0 output clock name
+- description: TCON TV1 output clock name
+- description: DSI output clock name
+
+ports:
+  required:
+- port@2
+- port@3
+
+  - if:
+  properties:
+compatible:
+  contains:
+const: allwinner,sun50i-h6-tcon-top
+
+then:
+  properties:
+clocks:
+  items:
+- description: The TCON TOP interface clock
+- description: The TCON TOP TV0 clock
+
+clock-names:
+  items:
+- const: bus
+- const: tcon-tv0
+
+clock-output-names:
+  items:
+- description: TCON TV0 output clock name
 
 examples:
   - |
-- 
2.35.1



[PATCH v3 03/14] drm/sun4i: Remove obsolete references to PHYS_OFFSET

2022-04-24 Thread Samuel Holland
commit b4bdc4fbf8d0 ("soc: sunxi: Deal with the MBUS DMA offsets in a
central place") added a platform device notifier that sets the DMA
offset for all of the display engine frontend and backend devices.

The code applying the offset to DMA buffer physical addresses was then
removed from the backend driver in commit 756668ba682e ("drm/sun4i:
backend: Remove the MBUS quirks"), but the code subtracting PHYS_OFFSET
was left in the frontend driver.

As a result, the offset was applied twice in the frontend driver. This
likely went unnoticed because it only affects specific configurations
(scaling or certain pixel formats) where the frontend is used, on boards
with both one of these older SoCs and more than 1 GB of DRAM.

In addition, the references to PHYS_OFFSET prevent compiling the driver
on architectures where PHYS_OFFSET is not defined.

Fixes: b4bdc4fbf8d0 ("soc: sunxi: Deal with the MBUS DMA offsets in a central 
place")
Reviewed-by: Jernej Skrabec 
Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun4i_frontend.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.c 
b/drivers/gpu/drm/sun4i/sun4i_frontend.c
index 56ae38389db0..462fae73eae9 100644
--- a/drivers/gpu/drm/sun4i/sun4i_frontend.c
+++ b/drivers/gpu/drm/sun4i/sun4i_frontend.c
@@ -222,13 +222,11 @@ void sun4i_frontend_update_buffer(struct sun4i_frontend 
*frontend,
 
/* Set the physical address of the buffer in memory */
paddr = drm_fb_cma_get_gem_addr(fb, state, 0);
-   paddr -= PHYS_OFFSET;
DRM_DEBUG_DRIVER("Setting buffer #0 address to %pad\n", );
regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR0_REG, paddr);
 
if (fb->format->num_planes > 1) {
paddr = drm_fb_cma_get_gem_addr(fb, state, swap ? 2 : 1);
-   paddr -= PHYS_OFFSET;
DRM_DEBUG_DRIVER("Setting buffer #1 address to %pad\n", );
regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR1_REG,
 paddr);
@@ -236,7 +234,6 @@ void sun4i_frontend_update_buffer(struct sun4i_frontend 
*frontend,
 
if (fb->format->num_planes > 2) {
paddr = drm_fb_cma_get_gem_addr(fb, state, swap ? 1 : 2);
-   paddr -= PHYS_OFFSET;
DRM_DEBUG_DRIVER("Setting buffer #2 address to %pad\n", );
regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR2_REG,
 paddr);
-- 
2.35.1



[PATCH v3 02/14] dt-bindings: display: Add D1 display engine compatibles

2022-04-24 Thread Samuel Holland
Allwinner D1 contains a display engine 2.0. It features two mixers, a
TCON TOP (with DSI and HDMI), one TCON LCD, and one TCON TV.

Reviewed-by: Krzysztof Kozlowski 
Signed-off-by: Samuel Holland 
---

Changes in v3:
 - Drop redundant minItems and maxItems

 .../allwinner,sun4i-a10-display-engine.yaml   |  1 +
 .../display/allwinner,sun4i-a10-tcon.yaml |  2 ++
 .../allwinner,sun8i-a83t-de2-mixer.yaml   |  2 ++
 .../display/allwinner,sun8i-r40-tcon-top.yaml | 28 +++
 4 files changed, 33 insertions(+)

diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-engine.yaml
 
b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-engine.yaml
index d4412aea7b73..c388ae5da1e4 100644
--- 
a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-engine.yaml
+++ 
b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-engine.yaml
@@ -62,6 +62,7 @@ properties:
   - allwinner,sun8i-r40-display-engine
   - allwinner,sun8i-v3s-display-engine
   - allwinner,sun9i-a80-display-engine
+  - allwinner,sun20i-d1-display-engine
   - allwinner,sun50i-a64-display-engine
   - allwinner,sun50i-h6-display-engine
 
diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml 
b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml
index 3a7d5d731712..4a92a4c7dcd7 100644
--- a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml
+++ b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml
@@ -33,6 +33,8 @@ properties:
   - const: allwinner,sun8i-v3s-tcon
   - const: allwinner,sun9i-a80-tcon-lcd
   - const: allwinner,sun9i-a80-tcon-tv
+  - const: allwinner,sun20i-d1-tcon-lcd
+  - const: allwinner,sun20i-d1-tcon-tv
 
   - items:
   - enum:
diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml 
b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml
index 4f91eec26de9..cb243bc58ef7 100644
--- 
a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml
+++ 
b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml
@@ -19,6 +19,8 @@ properties:
   - allwinner,sun8i-r40-de2-mixer-0
   - allwinner,sun8i-r40-de2-mixer-1
   - allwinner,sun8i-v3s-de2-mixer
+  - allwinner,sun20i-d1-de2-mixer-0
+  - allwinner,sun20i-d1-de2-mixer-1
   - allwinner,sun50i-a64-de2-mixer-0
   - allwinner,sun50i-a64-de2-mixer-1
   - allwinner,sun50i-h6-de3-mixer-0
diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml 
b/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml
index 449fa99aa51b..845e226d7aff 100644
--- 
a/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml
+++ 
b/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml
@@ -41,6 +41,7 @@ properties:
   compatible:
 enum:
   - allwinner,sun8i-r40-tcon-top
+  - allwinner,sun20i-d1-tcon-top
   - allwinner,sun50i-h6-tcon-top
 
   reg:
@@ -151,6 +152,33 @@ allOf:
 - port@2
 - port@3
 
+  - if:
+  properties:
+compatible:
+  contains:
+const: allwinner,sun20i-d1-tcon-top
+
+then:
+  properties:
+clocks:
+  items:
+- description: The TCON TOP interface clock
+- description: The TCON TOP TV0 clock
+- description: The TCON TOP TVE0 clock
+- description: The TCON TOP MIPI DSI clock
+
+clock-names:
+  items:
+- const: bus
+- const: tcon-tv0
+- const: tve0
+- const: dsi
+
+clock-output-names:
+  items:
+- description: TCON TV0 output clock name
+- description: DSI output clock name
+
   - if:
   properties:
 compatible:
-- 
2.35.1



[PATCH v3 00/14] drm/sun4i: Allwinner D1 Display Engine 2.0 Support

2022-04-24 Thread Samuel Holland
This series adds binding and driver support for Display Engine 2.0
variant found in the Allwinner D1.

So far it has only been tested with HDMI. I will be sending the HDMI
support series separately, because the hardware comes with a brand new
custom HDMI PHY, which requires some refactoring to support cleanly.

This series was tested on A33, D1 and H3.

Changes in v3:
 - Drop redundant minItems and maxItems

Changes in v2:
 - New patch: I/O helper portability
 - Use Jernej's patches for mixer mode setting.
 - Use an enumeration for the ccsc value.

Jernej Skrabec (3):
  sun4i/drm: engine: Add mode_set callback
  sun4i/drm: backend: use mode_set engine callback
  sun4i/drm: sun8i: use mode_set engine callback

Samuel Holland (11):
  dt-bindings: display: Separate clock item lists by compatible
  dt-bindings: display: Add D1 display engine compatibles
  drm/sun4i: Remove obsolete references to PHYS_OFFSET
  drm/sun4i: hdmi: Use more portable I/O helpers
  drm/sun4i: Allow building the driver on RISC-V
  drm/sun4i: Allow VI layers to be primary planes
  drm/sun4i: csc: Add support for the new MMIO layout
  drm/sun4i: Add support for D1 mixers
  drm/sun4i: Add support for D1 TCON TOP
  drm/sun4i: Add support for D1 TCONs
  drm/sun4i: Add compatible for D1 display engine

 .../allwinner,sun4i-a10-display-engine.yaml   |   1 +
 .../display/allwinner,sun4i-a10-tcon.yaml |   2 +
 .../allwinner,sun8i-a83t-de2-mixer.yaml   |   2 +
 .../display/allwinner,sun8i-r40-tcon-top.yaml | 133 --
 drivers/gpu/drm/sun4i/Kconfig |   2 +-
 drivers/gpu/drm/sun4i/sun4i_backend.c |  40 +++---
 drivers/gpu/drm/sun4i/sun4i_crtc.c|   1 +
 drivers/gpu/drm/sun4i/sun4i_drv.c |   1 +
 drivers/gpu/drm/sun4i/sun4i_frontend.c|   3 -
 drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c|   4 +-
 drivers/gpu/drm/sun4i/sun4i_tcon.c|   8 ++
 drivers/gpu/drm/sun4i/sun8i_csc.c |   7 +-
 drivers/gpu/drm/sun4i/sun8i_csc.h |   1 +
 drivers/gpu/drm/sun4i/sun8i_mixer.c   |  74 --
 drivers/gpu/drm/sun4i/sun8i_mixer.h   |  14 +-
 drivers/gpu/drm/sun4i/sun8i_tcon_top.c|  15 +-
 drivers/gpu/drm/sun4i/sun8i_ui_layer.c|  30 
 drivers/gpu/drm/sun4i/sun8i_vi_layer.c|   6 +-
 drivers/gpu/drm/sun4i/sunxi_engine.h  |  27 
 19 files changed, 251 insertions(+), 120 deletions(-)

-- 
2.35.1



Re: [RFC PATCH 02/16] dt-bindings: display: rockchip: Add EBC binding

2022-04-14 Thread Samuel Holland
Hi Andreas,

Thanks for the comments.

On 4/14/22 3:15 AM, Andreas Kemnade wrote:
> Hi Samuel,
> 
> for comparison, here is my submission for the IMX EPDC bindings:
> 
> https://lore.kernel.org/linux-devicetree/20220206080016.796556-2-andr...@kemnade.info/
> 
> On Wed, 13 Apr 2022 17:19:02 -0500
> Samuel Holland  wrote:
> 
> [...]
> we have sy7636a driver in kernel which should be suitable for powering a EPD
> and temperature measurement. So I would expect that to be 
>> +  io-channels:
>> +maxItems: 1
>> +description: I/O channel for panel temperature measurement
>> +
> so how would I reference the hwmon/thermal(-zone) of the sy7636a here?

It seems the consensus is to use a thermal zone for panel temperature, so I will
need to change this.

I think it's best to reference the thermal zone by phandle, not by name, even if
it requires extending the thermal zone API to support this.

>> +  panel-supply:
>> +description: Regulator supplying the panel's logic voltage
>> +
>> +  power-domains:
>> +maxItems: 1
>> +
>> +  vcom-supply:
>> +description: Regulator supplying the panel's compensation voltage
>> +
>> +  vdrive-supply:
>> +description: Regulator supplying the panel's gate and source drivers
>> +
> SY7636a has only one logical regulator in kernel for for the latter two.

Both properties could point to the same regulator node if there are more
consumers than regulators. I don't know of a clean way to handle the opposite
situation.

The other benefit of separating out VCOM is that the controller or panel driver
can set a calibrated voltage from e.g. NVMEM or the panel's DT node.

> If we have a separate panel node, than maybe these regulators should go
> there as they belong to the panel as they are powering the panel and
> not the EBC.

I agree on this. It doesn't work with panel-simple, but as Maxime points out, we
have more flexibility with a custom panel driver.

>> +  port:
>> +$ref: /schemas/graph.yaml#/properties/port
>> +description: OF graph port for the attached display panel
>> +
> In my approach for the IMX EPDC, (I will send a better commented one
> soon) I have no separate subnode to avoid messing with additional
> display parameters. Not sure what is really better here.

I tried to match the existing abstractions as much as possible, and I saw there
was already an "eink,vb3300-kca" display in panel-simple. I believe that one was
added for the reMarkable 2, where the existing LCD controller driver already
depends on the DRM panel code (although I have concerns about hooking that up to
a driver that doesn't understand EPDs).

My thought here is that the timings for a given panel should be the same across
controllers, both dedicated EPD controllers and LCD controllers. Or at least it
should be possible to derive the timings from some common set of parameters.

The panel node also usually hooks up to the backlight, although I am not sure
that is the right thing to do for EPDs. (And the PineNote has a separate issue
of having two backlights [warm/cool] for one display.)

Regards,
Samuel


[RFC PATCH 12/16] drm/rockchip: ebc: Add support for direct mode

2022-04-13 Thread Samuel Holland
Currently, 3-window mode causes some artifacting. Until the cause is
determined, provide an option to use direct mode instead. Direct mode
does the waveform lookups in software, so it has much higher CPU usage.
This limits the frame rate below the panel's ideal 85 Hz, so it leads to
slightly lower brightness accuracy. On the other hand, it doesn't leave
random lines all over the screen.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/rockchip/rockchip_ebc.c | 97 ++---
 1 file changed, 88 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c 
b/drivers/gpu/drm/rockchip/rockchip_ebc.c
index dcd8c8e8208e..93d689ff0933 100644
--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
+++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
@@ -162,6 +162,10 @@ static bool diff_mode = true;
 module_param(diff_mode, bool, 0644);
 MODULE_PARM_DESC(diff_mode, "only compute waveforms for changed pixels");
 
+static bool direct_mode = true;
+module_param(direct_mode, bool, 0444);
+MODULE_PARM_DESC(direct_mode, "compute waveforms in software (software LUT)");
+
 static bool skip_reset;
 module_param(skip_reset, bool, 0444);
 MODULE_PARM_DESC(skip_reset, "skip the initial display reset");
@@ -370,6 +374,59 @@ static bool rockchip_ebc_schedule_area(struct list_head 
*areas,
return true;
 }
 
+static void rockchip_ebc_blit_direct(const struct rockchip_ebc_ctx *ctx,
+u8 *dst, u8 phase,
+const struct drm_epd_lut *lut,
+const struct drm_rect *clip)
+{
+   const u32 *phase_lut = (const u32 *)lut->buf + 16 * phase;
+   unsigned int dst_pitch = ctx->phase_pitch / 4;
+   unsigned int src_pitch = ctx->gray4_pitch;
+   unsigned int x, y;
+   u8 *dst_line;
+   u32 src_line;
+
+   dst_line = dst + clip->y1 * dst_pitch + clip->x1 / 4;
+   src_line = clip->y1 * src_pitch + clip->x1 / 2;
+
+   for (y = clip->y1; y < clip->y2; y++) {
+   u32 src_offset = src_line;
+   u8 *dbuf = dst_line;
+
+   for (x = clip->x1; x < clip->x2; x += 4) {
+   u8 prev0 = ctx->prev[src_offset];
+   u8 next0 = ctx->next[src_offset++];
+   u8 prev1 = ctx->prev[src_offset];
+   u8 next1 = ctx->next[src_offset++];
+
+   /*
+* The LUT is 256 phases * 16 next * 16 previous levels.
+* Each value is two bits, so the last dimension neatly
+* fits in a 32-bit word.
+*/
+   u8 data = ((phase_lut[next0 & 0xf] >> ((prev0 & 0xf) << 
1)) & 0x3) << 0 |
+ ((phase_lut[next0 >>  4] >> ((prev0 >>  4) << 
1)) & 0x3) << 2 |
+ ((phase_lut[next1 & 0xf] >> ((prev1 & 0xf) << 
1)) & 0x3) << 4 |
+ ((phase_lut[next1 >>  4] >> ((prev1 >>  4) << 
1)) & 0x3) << 6;
+
+   /* Diff mode ignores pixels that did not change 
brightness. */
+   if (diff_mode) {
+   u8 mask = ((next0 ^ prev0) & 0x0f ? 0x03 : 0) |
+ ((next0 ^ prev0) & 0xf0 ? 0x0c : 0) |
+ ((next1 ^ prev1) & 0x0f ? 0x30 : 0) |
+ ((next1 ^ prev1) & 0xf0 ? 0xc0 : 0);
+
+   data &= mask;
+   }
+
+   *dbuf++ = data;
+   }
+
+   dst_line += dst_pitch;
+   src_line += src_pitch;
+   }
+}
+
 static void rockchip_ebc_blit_phase(const struct rockchip_ebc_ctx *ctx,
u8 *dst, u8 phase,
const struct drm_rect *clip)
@@ -472,8 +529,13 @@ static void rockchip_ebc_partial_refresh(struct 
rockchip_ebc *ebc,
 * be neutral for every waveform.
 */
phase = frame_delta >= last_phase ? 0xff : frame_delta;
-   rockchip_ebc_blit_phase(ctx, phase_buffer, phase,
-   >clip);
+   if (direct_mode)
+   rockchip_ebc_blit_direct(ctx, phase_buffer,
+phase, >lut,
+>clip);
+   else
+   rockchip_ebc_blit_phase(ctx, phase_buffer,
+ 

[RFC PATCH 16/16] [DO NOT MERGE] arm64: dts: rockchip: pinenote: Enable EBC display pipeline

2022-04-13 Thread Samuel Holland
The PineNote contains an eInk ED103TC2 panel connected to the EBC,
powered by a TI TPS651851 PMIC.

Signed-off-by: Samuel Holland 
---

 .../boot/dts/rockchip/rk3566-pinenote.dtsi| 80 +++
 1 file changed, 80 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi 
b/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi
index fea748adfa90..4a53931c3f92 100644
--- a/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi
@@ -72,6 +72,16 @@ led-0 {
};
};
 
+   panel {
+   compatible = "eink,ed103tc2";
+
+   port {
+   panel_in_ebc: endpoint {
+   remote-endpoint = <_out_panel>;
+   };
+   };
+   };
+
sdio_pwrseq: sdio-pwrseq {
compatible = "mmc-pwrseq-simple";
clocks = < 1>;
@@ -213,6 +223,20 @@  {
cpu-supply = <_cpu>;
 };
 
+ {
+   io-channels = <_pmic 0>;
+   panel-supply = <>;
+   vcom-supply = <>;
+   vdrive-supply = <>;
+   status = "okay";
+
+   port {
+   ebc_out_panel: endpoint {
+   remote-endpoint = <_in_ebc>;
+   };
+   };
+};
+
  {
status = "okay";
 
@@ -466,6 +490,47 @@ led@1 {
default-brightness = <0>;
};
};
+
+   /* TODO: write binding */
+   ebc_pmic: pmic@68 {
+   compatible = "ti,tps65185";
+   reg = <0x68>;
+   interrupt-parent = <>;
+   interrupts = ;
+   #io-channel-cells = <1>;
+   pinctrl-0 = <_pmic_pins>;
+   pinctrl-names = "default";
+   powerup-gpios = < RK_PB0 GPIO_ACTIVE_HIGH>;
+   pwr_good-gpios = < RK_PA7 GPIO_ACTIVE_HIGH>;
+   vcom_ctrl-gpios = < RK_PB2 GPIO_ACTIVE_HIGH>;
+   vin-supply = <_bat>;
+   vin3p3-supply = <_3v3>;
+   wakeup-gpios = < RK_PA5 GPIO_ACTIVE_HIGH>;
+   ti,up-sequence = <1>, <0>, <2>, <3>;
+   ti,up-delay-ms = <3>, <3>, <3>, <3>;
+   ti,down-sequence = <2>, <3>, <1>, <0>;
+   ti,down-delay-ms = <3>, <6>, <6>, <6>;
+
+   regulators {
+   v3p3: v3p3 {
+   regulator-name = "v3p3";
+   regulator-always-on;
+   regulator-min-microvolt = <330>;
+   regulator-max-microvolt = <330>;
+   };
+
+   vcom: vcom {
+   regulator-name = "vcom";
+   /* voltage range is board-specific */
+   };
+
+   vdrive: vdrive {
+   regulator-name = "vdrive";
+   regulator-min-microvolt = <1500>;
+   regulator-max-microvolt = <1500>;
+   };
+   };
+   };
 };
 
 _8ch {
@@ -508,6 +573,21 @@ bt_wake_h: bt-wake-h {
};
};
 
+   ebc-pmic {
+   ebc_pmic_pins: ebc-pmic-pins {
+   rockchip,pins = /* wakeup */
+   <3 RK_PA5 RK_FUNC_GPIO _pull_none>,
+   /* int */
+   <3 RK_PA6 RK_FUNC_GPIO _pull_up>,
+   /* pwr_good */
+   <3 RK_PA7 RK_FUNC_GPIO _pull_none>,
+   /* pwrup */
+   <3 RK_PB0 RK_FUNC_GPIO _pull_none>,
+   /* vcom_ctrl */
+   <4 RK_PB2 RK_FUNC_GPIO _pull_none>;
+   };
+   };
+
led {
led_pin: led-pin {
rockchip,pins = <3 RK_PC5 RK_FUNC_GPIO _pull_none>;
-- 
2.35.1



[RFC PATCH 15/16] arm64: dts: rockchip: rk356x: Add EBC node

2022-04-13 Thread Samuel Holland
The RK356x SoCs contain an EBC. Add its node.

Signed-off-by: Samuel Holland 
---

 arch/arm64/boot/dts/rockchip/rk356x.dtsi | 14 ++
 1 file changed, 14 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi 
b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
index 7cdef800cb3c..58c26f240af0 100644
--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
@@ -508,6 +508,20 @@ gpu: gpu@fde6 {
status = "disabled";
};
 
+   ebc: ebc@fdec {
+   compatible = "rockchip,rk3568-ebc";
+   reg = <0x0 0xfdec 0x0 0x5000>;
+   interrupts = ;
+   clocks = < HCLK_EBC>, < DCLK_EBC>;
+   clock-names = "hclk", "dclk";
+   pinctrl-0 = <_pins>;
+   pinctrl-names = "default";
+   power-domains = < RK3568_PD_RGA>;
+   resets = < SRST_H_EBC>, < SRST_D_EBC>;
+   reset-names = "hclk", "dclk";
+   status = "disabled";
+   };
+
sdmmc2: mmc@fe00 {
compatible = "rockchip,rk3568-dw-mshc", 
"rockchip,rk3288-dw-mshc";
reg = <0x0 0xfe00 0x0 0x4000>;
-- 
2.35.1



[RFC PATCH 14/16] drm/panel-simple: Add eInk ED103TC2

2022-04-13 Thread Samuel Holland
ED103TC2 is a 10.3" 1872x1404 eInk panel which supports up to 16 levels
of grayscale and an 85 Hz frame rate. The timings and polarities here
were taken from the manufacturer's datasheet.

Since this panel is an electrophoretic display (EPD), the color depth is
independent from the bus width. Instead, it is largely determined by the
number of frames in the selected waveform. Each pixel uses two parallel
data lines to specify one of only three states each frame: positive,
negative, or no voltage.

This specific panel has a 16-bit data bus, allowing it to update 8
pixels during each source driver (horizontal) clock cycle. As a result,
the horizontal timings given in the datasheet were all multiplied by 8
to convert the units from clock cycles to pixels.

Since the 16-bit data bus is double the width of the usual 8-bit bus
used by eInk panels, the source driver clock will be half the usual
frequency. This is signified by the DRM_MODE_FLAG_CLKDIV2 flag.

The hskew parameter provides the spacing between the horizontal sync
puls and the gate driver (vertical) clock pulse. This spacing is
symmetrical on both sides, so it can be used to compute the gate
driver clock pulse width.

Datasheet: 
https://files.pine64.org/doc/quartz64/Eink%20P-511-828-V1_ED103TC2%20Formal%20Spec%20V1.0_20190514.pdf
Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/panel/panel-simple.c | 31 
 1 file changed, 31 insertions(+)

diff --git a/drivers/gpu/drm/panel/panel-simple.c 
b/drivers/gpu/drm/panel/panel-simple.c
index a34f4198a534..c6b104ba01ee 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -1686,6 +1686,34 @@ static const struct panel_desc edt_etmv570g2dhu = {
.connector_type = DRM_MODE_CONNECTOR_DPI,
 };
 
+static const struct drm_display_mode eink_ed103tc2_mode = {
+   .clock = 266693,
+   .hdisplay = 1872,
+   .hsync_start = 1872 + 184,
+   .hsync_end = 1872 + 184 + 88,
+   .htotal = 1872 + 184 + 88 + 64,
+   .hskew = 136,
+   .vdisplay = 1404,
+   .vsync_start = 1404 + 12,
+   .vsync_end = 1404 + 12 + 1,
+   .vtotal = 1404 + 12 + 1 + 4,
+   .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC |
+DRM_MODE_FLAG_HSKEW | DRM_MODE_FLAG_CLKDIV2,
+};
+
+static const struct panel_desc eink_ed103tc2 = {
+   .modes = _ed103tc2_mode,
+   .num_modes = 1,
+   .bpc = 4,
+   .size = {
+   .width = 210,
+   .height = 157,
+   },
+   .bus_format = MEDIA_BUS_FMT_FIXED,
+   .bus_flags = DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
+   .connector_type = DRM_MODE_CONNECTOR_DPI,
+};
+
 static const struct display_timing eink_vb3300_kca_timing = {
.pixelclock = { 4000, 4000, 4000 },
.hactive = { 334, 334, 334 },
@@ -3807,6 +3835,9 @@ static const struct of_device_id platform_of_match[] = {
}, {
.compatible = "edt,etmv570g2dhu",
.data = _etmv570g2dhu,
+   }, {
+   .compatible = "eink,ed103tc2",
+   .data = _ed103tc2,
}, {
.compatible = "eink,vb3300-kca",
.data = _vb3300_kca,
-- 
2.35.1



[RFC PATCH 10/16] drm/rockchip: ebc: Implement partial refreshes

2022-04-13 Thread Samuel Holland
Several areas of the display can be refreshed concurrently, but only if
they do not overlap. This commit adds a queue of damaged areas, and
schedules them for refresh based on collision with other areas. While
the queue is unbounded, there is logic to quickly drop duplicate areas.

Because three-window mode disables the hardware's frame counter, a
separate buffer is required for each frame. (In other words, there is no
automatic increment.) To minimize overhead, swap between two buffers for
phase numbers. This requires extending the loop for one extra frame to
clear the phase numbers in both buffers when an area completes. (This
extra frame is a no-op and is not sent to the hardware.)

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/rockchip/rockchip_ebc.c | 346 +++-
 1 file changed, 344 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c 
b/drivers/gpu/drm/rockchip/rockchip_ebc.c
index cb6dc567e94c..c3e4b65bdee6 100644
--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
+++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -123,10 +124,14 @@
 #define EBC_WIN_MST2   0x0058
 #define EBC_LUT_DATA   0x1000
 
+#define EBC_FRAME_PENDING  (-1U)
+
 #define EBC_MAX_PHASES 256
+
 #define EBC_NUM_LUT_REGS   0x1000
 #define EBC_NUM_SUPPLIES   3
 
+#define EBC_FRAME_TIMEOUT  msecs_to_jiffies(25)
 #define EBC_REFRESH_TIMEOUTmsecs_to_jiffies(3000)
 #define EBC_SUSPEND_DELAY_MS   2000
 
@@ -177,10 +182,25 @@ static const struct drm_mode_config_funcs 
rockchip_ebc_mode_config_funcs = {
.atomic_commit  = drm_atomic_helper_commit,
 };
 
+/**
+ * struct rockchip_ebc_area - describes a damaged area of the display
+ *
+ * @list: Used to put this area in the state/context/refresh thread list
+ * @clip: The rectangular clip of this damage area
+ * @frame_begin: The frame number when this damage area starts being refreshed
+ */
+struct rockchip_ebc_area {
+   struct list_headlist;
+   struct drm_rect clip;
+   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
@@ -192,6 +212,8 @@ static const struct drm_mode_config_funcs 
rockchip_ebc_mode_config_funcs = {
  */
 struct rockchip_ebc_ctx {
struct kref kref;
+   struct list_headqueue;
+   spinlock_t  queue_lock;
u8  *prev;
u8  *next;
u8  *final;
@@ -204,6 +226,10 @@ struct rockchip_ebc_ctx {
 
 static void rockchip_ebc_ctx_free(struct rockchip_ebc_ctx *ctx)
 {
+   struct rockchip_ebc_area *area;
+
+   list_for_each_entry(area, >queue, list)
+   kfree(area);
kfree(ctx->prev);
kfree(ctx->next);
kfree(ctx->final);
@@ -234,6 +260,8 @@ static struct rockchip_ebc_ctx *rockchip_ebc_ctx_alloc(u32 
width, u32 height)
}
 
kref_init(>kref);
+   INIT_LIST_HEAD(>queue);
+   spin_lock_init(>queue_lock);
ctx->gray4_pitch = width / 2;
ctx->gray4_size  = gray4_size;
ctx->phase_pitch = width;
@@ -291,12 +319,204 @@ static void rockchip_ebc_global_refresh(struct 
rockchip_ebc *ebc,
memcpy(ctx->prev, ctx->next, gray4_size);
 }
 
+static bool rockchip_ebc_schedule_area(struct list_head *areas,
+  struct rockchip_ebc_area *area,
+  u32 current_frame, u32 num_phases)
+{
+   struct rockchip_ebc_area *other;
+   u32 frame_begin = current_frame;
+
+   list_for_each_entry(other, areas, list) {
+   struct drm_rect intersection;
+   u32 other_end;
+
+   /* Only consider areas before this one in the list. */
+   if (other == area)
+   break;
+
+   /* Skip areas that finish refresh before this area begins. */
+   other_end = other->frame_begin + num_phases;
+   if (other_end <= frame_begin)
+   continue;
+
+   /* If there is no collision, the areas are independent. */
+   intersection = area->clip;
+   if (!drm_rect_intersect(, >clip))
+   continue;
+
+   /* If the ot

[RFC PATCH 09/16] drm/rockchip: ebc: Implement global refreshes

2022-04-13 Thread Samuel Holland
The global refresh mode is used to initialize and clear the screen.
It is the most efficient refresh mode. It uses two pixel buffers (old
and new) and a frame count. The frame count is set to the number of
phases in the waveform. The hardware then looks up the combination of
(old pixel value, new pixel value, frame number) in the LUT and sends
the resulting polarity value to the display.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/rockchip/rockchip_ebc.c | 48 -
 1 file changed, 47 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c 
b/drivers/gpu/drm/rockchip/rockchip_ebc.c
index ca3173b28d1c..cb6dc567e94c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
+++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
@@ -127,6 +127,7 @@
 #define EBC_NUM_LUT_REGS   0x1000
 #define EBC_NUM_SUPPLIES   3
 
+#define EBC_REFRESH_TIMEOUTmsecs_to_jiffies(3000)
 #define EBC_SUSPEND_DELAY_MS   2000
 
 struct rockchip_ebc {
@@ -269,8 +270,23 @@ static void rockchip_ebc_global_refresh(struct 
rockchip_ebc *ebc,
 {
struct drm_device *drm = >drm;
u32 gray4_size = ctx->gray4_size;
+   struct device *dev = drm->dev;
 
-   drm_dbg(drm, "global refresh\n");
+   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),
+  gray4_size, DMA_TO_DEVICE);
+
+   reinit_completion(>display_end);
+   regmap_write(ebc->regmap, EBC_CONFIG_DONE,
+EBC_CONFIG_DONE_REG_CONFIG_DONE);
+   regmap_write(ebc->regmap, EBC_DSP_START,
+ebc->dsp_start |
+EBC_DSP_START_DSP_FRM_TOTAL(ebc->lut.num_phases - 1) |
+EBC_DSP_START_DSP_FRM_START);
+   if (!wait_for_completion_timeout(>display_end,
+EBC_REFRESH_TIMEOUT))
+   drm_err(drm, "Refresh timed out!\n");
 
memcpy(ctx->prev, ctx->next, gray4_size);
 }
@@ -289,6 +305,7 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
 enum drm_epd_waveform waveform)
 {
struct drm_device *drm = >drm;
+   u32 dsp_ctrl = 0, epd_ctrl = 0;
struct device *dev = drm->dev;
int ret, temperature;
 
@@ -334,11 +351,40 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
  ebc->lut.buf, EBC_NUM_LUT_REGS);
}
 
+   regmap_write(ebc->regmap, EBC_DSP_START,
+ebc->dsp_start);
+
+   /*
+* The hardware has a separate bit for each mode, with some priority
+* scheme between them. For clarity, only set one bit at a time.
+*/
+   if (global_refresh) {
+   dsp_ctrl |= EBC_DSP_CTRL_DSP_LUT_MODE;
+   } else {
+   epd_ctrl |= EBC_EPD_CTRL_DSP_THREE_WIN_MODE;
+   }
+   regmap_update_bits(ebc->regmap, EBC_EPD_CTRL,
+  EBC_EPD_CTRL_DSP_THREE_WIN_MODE,
+  epd_ctrl);
+   regmap_update_bits(ebc->regmap, EBC_DSP_CTRL,
+  EBC_DSP_CTRL_DSP_LUT_MODE,
+  dsp_ctrl);
+
+   regmap_write(ebc->regmap, EBC_WIN_MST0,
+virt_to_phys(ctx->next));
+   regmap_write(ebc->regmap, EBC_WIN_MST1,
+virt_to_phys(ctx->prev));
+
if (global_refresh)
rockchip_ebc_global_refresh(ebc, ctx);
else
rockchip_ebc_partial_refresh(ebc, ctx);
 
+   /* Drive the output pins low once the refresh is complete. */
+   regmap_write(ebc->regmap, EBC_DSP_START,
+ebc->dsp_start |
+EBC_DSP_START_DSP_OUT_LOW);
+
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
 }
-- 
2.35.1



[RFC PATCH 13/16] drm/rockchip: ebc: Add a panel reflection option

2022-04-13 Thread Samuel Holland
Some panels, like the one in the PineNote, are reflected. Since the
driver already has to copy pixels, the driver can handle this with
little additional overhead.

Currently, there is no devicetree binding for this situation, so control
the behavior via a module parameter.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/rockchip/rockchip_ebc.c | 25 +
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c 
b/drivers/gpu/drm/rockchip/rockchip_ebc.c
index 93d689ff0933..9d0b2cdc5fdc 100644
--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
+++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
@@ -166,6 +166,10 @@ static bool direct_mode = true;
 module_param(direct_mode, bool, 0444);
 MODULE_PARM_DESC(direct_mode, "compute waveforms in software (software LUT)");
 
+static bool panel_reflection = true;
+module_param(panel_reflection, bool, 0644);
+MODULE_PARM_DESC(panel_reflection, "reflect the image horizontally");
+
 static bool skip_reset;
 module_param(skip_reset, bool, 0444);
 MODULE_PARM_DESC(skip_reset, "skip the initial display reset");
@@ -1046,23 +1050,29 @@ static bool rockchip_ebc_blit_fb(const struct 
rockchip_ebc_ctx *ctx,
 {
unsigned int dst_pitch = ctx->gray4_pitch;
unsigned int src_pitch = fb->pitches[0];
-   unsigned int x, y;
+   unsigned int start_x, x, y;
const void *src;
u8 changed = 0;
+   int delta_x;
void *dst;
 
+   delta_x = panel_reflection ? -1 : 1;
+   start_x = panel_reflection ? src_clip->x2 - 1 : src_clip->x1;
+
dst = ctx->final + dst_clip->y1 * dst_pitch + dst_clip->x1 / 2;
-   src = vaddr + src_clip->y1 * src_pitch + src_clip->x1 * 
fb->format->cpp[0];
+   src = vaddr + src_clip->y1 * src_pitch + start_x * fb->format->cpp[0];
 
for (y = src_clip->y1; y < src_clip->y2; y++) {
const u32 *sbuf = src;
u8 *dbuf = dst;
 
for (x = src_clip->x1; x < src_clip->x2; x += 2) {
-   u32 rgb0 = *sbuf++;
-   u32 rgb1 = *sbuf++;
+   u32 rgb0, rgb1;
u8 gray;
 
+   rgb0 = *sbuf; sbuf += delta_x;
+   rgb1 = *sbuf; sbuf += delta_x;
+
/* Truncate the RGB values to 5 bits each. */
rgb0 &= 0x00f8f8f8U; rgb1 &= 0x00f8f8f8U;
/* Put the sum 2R+5G+B in bits 24-31. */
@@ -1136,6 +1146,13 @@ static void rockchip_ebc_plane_atomic_update(struct 
drm_plane *plane,
dst_clip->x2 += adjust;
src_clip.x2  += adjust;
 
+   if (panel_reflection) {
+   int x1 = dst_clip->x1, x2 = dst_clip->x2;
+
+   dst_clip->x1 = plane_state->dst.x2 - x2;
+   dst_clip->x2 = plane_state->dst.x2 - x1;
+   }
+
if (!rockchip_ebc_blit_fb(ctx, dst_clip, vaddr,
  plane_state->fb, _clip)) {
/* Drop the area if the FB didn't actually change. */
-- 
2.35.1



[RFC PATCH 05/16] drm/rockchip: ebc: Add CRTC mode setting

2022-04-13 Thread Samuel Holland
EPDs require some additional timing data beyond what is normally
provided by drm_display_mode, as that struct is designed for CRTs/LCDs.
For example, EPDs care about the width and position of the gate driver
(vertical) clock pulse within a line.

EPDs also update some number of pixels in parallel, based on the
interface width, which of course varies by panel. Only two data bits are
used for each pixel, to choose between driving it positive, negative, or
neither direction. Color depth is thus not limited by interface width,
but by time (the number of phases in the active waveform).

This additional timing information is packed inside drm_display_mode as
hskew and DRM_MODE_FLAG_CLKDIV2. This allows getting the complete mode
from a DRM bridge.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/rockchip/rockchip_ebc.c | 102 
 1 file changed, 102 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c 
b/drivers/gpu/drm/rockchip/rockchip_ebc.c
index f75fd23adda2..5f9502313657 100644
--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
+++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
@@ -135,6 +135,7 @@ struct rockchip_ebc {
struct drm_planeplane;
struct regmap   *regmap;
struct regulator_bulk_data  supplies[EBC_NUM_SUPPLIES];
+   u32 dsp_start;
 };
 
 DEFINE_DRM_GEM_FOPS(rockchip_ebc_fops);
@@ -178,11 +179,112 @@ static inline struct rockchip_ebc *crtc_to_ebc(struct 
drm_crtc *crtc)
 
 static void rockchip_ebc_crtc_mode_set_nofb(struct drm_crtc *crtc)
 {
+   struct rockchip_ebc *ebc = crtc_to_ebc(crtc);
+   struct drm_display_mode mode = crtc->state->adjusted_mode;
+   struct drm_display_mode sdck;
+   u16 hsync_width, vsync_width;
+   u16 hact_start, vact_start;
+   u16 pixels_per_sdck;
+   bool bus_16bit;
+
+   /*
+* Hardware needs horizontal timings in SDCK (source driver clock)
+* cycles, not pixels. Bus width is either 8 bits (normal) or 16 bits
+* (DRM_MODE_FLAG_CLKDIV2), and each pixel uses two data bits.
+*/
+   bus_16bit = !!(mode.flags & DRM_MODE_FLAG_CLKDIV2);
+   pixels_per_sdck = bus_16bit ? 8 : 4;
+   sdck.hdisplay = mode.hdisplay / pixels_per_sdck;
+   sdck.hsync_start = mode.hsync_start / pixels_per_sdck;
+   sdck.hsync_end = mode.hsync_end / pixels_per_sdck;
+   sdck.htotal = mode.htotal / pixels_per_sdck;
+   sdck.hskew = mode.hskew / pixels_per_sdck;
+
+   /*
+* Linux timing order is display/fp/sync/bp. Hardware timing order is
+* sync/bp/display/fp, aka sync/start/display/end.
+*/
+   hact_start = sdck.htotal - sdck.hsync_start;
+   vact_start = mode.vtotal - mode.vsync_start;
+
+   hsync_width = sdck.hsync_end - sdck.hsync_start;
+   vsync_width = mode.vsync_end - mode.vsync_start;
+
+   clk_set_rate(ebc->dclk, mode.clock * 1000);
+
+   ebc->dsp_start = EBC_DSP_START_DSP_SDCE_WIDTH(sdck.hdisplay) |
+EBC_DSP_START_SW_BURST_CTRL;
+   regmap_write(ebc->regmap, EBC_EPD_CTRL,
+EBC_EPD_CTRL_DSP_GD_END(sdck.htotal - sdck.hskew) |
+EBC_EPD_CTRL_DSP_GD_ST(hsync_width + sdck.hskew) |
+EBC_EPD_CTRL_DSP_SDDW_MODE * bus_16bit);
+   regmap_write(ebc->regmap, EBC_DSP_CTRL,
+/* no swap */
+EBC_DSP_CTRL_DSP_SWAP_MODE(bus_16bit ? 2 : 3) |
+EBC_DSP_CTRL_DSP_SDCLK_DIV(pixels_per_sdck - 1));
+   regmap_write(ebc->regmap, EBC_DSP_HTIMING0,
+EBC_DSP_HTIMING0_DSP_HTOTAL(sdck.htotal) |
+/* sync end == sync width */
+EBC_DSP_HTIMING0_DSP_HS_END(hsync_width));
+   regmap_write(ebc->regmap, EBC_DSP_HTIMING1,
+EBC_DSP_HTIMING1_DSP_HACT_END(hact_start + sdck.hdisplay) |
+/* minus 1 for fixed delay in timing sequence */
+EBC_DSP_HTIMING1_DSP_HACT_ST(hact_start - 1));
+   regmap_write(ebc->regmap, EBC_DSP_VTIMING0,
+EBC_DSP_VTIMING0_DSP_VTOTAL(mode.vtotal) |
+/* sync end == sync width */
+EBC_DSP_VTIMING0_DSP_VS_END(vsync_width));
+   regmap_write(ebc->regmap, EBC_DSP_VTIMING1,
+EBC_DSP_VTIMING1_DSP_VACT_END(vact_start + mode.vdisplay) |
+EBC_DSP_VTIMING1_DSP_VACT_ST(vact_start));
+   regmap_write(ebc->regmap, EBC_DSP_ACT_INFO,
+EBC_DSP_ACT_INFO_DSP_HEIGHT(mode.vdisplay) |
+EBC_DSP_ACT_INFO_DSP_WIDTH(mode.hdisplay));
+   regmap_write(ebc->regmap, EBC_WIN_CTRL,
+/* FIFO depth - 16 */
+EBC_WIN_CTRL_WIN2_FIFO_THRESHOLD(496) |
+EBC_WIN_CTRL_WIN_EN |
+/* INCR16 */
+EBC_WIN_CTRL_AHB_BURST_REG(7) |

[RFC PATCH 11/16] drm/rockchip: ebc: Enable diff mode for partial refreshes

2022-04-13 Thread Samuel Holland
Some waveforms, such as GC16, cause the display to flash even when the
previous and next pixel values are the same. This can be helpful,
because it produces more consistent brightness, but usually it is more
distracting. Add an option, enabled by default, for the hardware to
ignore the LUT and always send zeroes for pixels with unchanged values.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/rockchip/rockchip_ebc.c | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c 
b/drivers/gpu/drm/rockchip/rockchip_ebc.c
index c3e4b65bdee6..dcd8c8e8208e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
+++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
@@ -158,6 +158,10 @@ static int default_waveform = DRM_EPD_WF_GC16;
 module_param(default_waveform, int, 0644);
 MODULE_PARM_DESC(default_waveform, "waveform to use for display updates");
 
+static bool diff_mode = true;
+module_param(diff_mode, bool, 0644);
+MODULE_PARM_DESC(diff_mode, "only compute waveforms for changed pixels");
+
 static bool skip_reset;
 module_param(skip_reset, bool, 0444);
 MODULE_PARM_DESC(skip_reset, "skip the initial display reset");
@@ -582,11 +586,14 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
dsp_ctrl |= EBC_DSP_CTRL_DSP_LUT_MODE;
} else {
epd_ctrl |= EBC_EPD_CTRL_DSP_THREE_WIN_MODE;
+   if (diff_mode)
+   dsp_ctrl |= EBC_DSP_CTRL_DSP_DIFF_MODE;
}
regmap_update_bits(ebc->regmap, EBC_EPD_CTRL,
   EBC_EPD_CTRL_DSP_THREE_WIN_MODE,
   epd_ctrl);
regmap_update_bits(ebc->regmap, EBC_DSP_CTRL,
+  EBC_DSP_CTRL_DSP_DIFF_MODE |
   EBC_DSP_CTRL_DSP_LUT_MODE,
   dsp_ctrl);
 
-- 
2.35.1



[RFC PATCH 06/16] drm/rockchip: ebc: Add CRTC refresh thread

2022-04-13 Thread Samuel Holland
EPD refreshes are extremely slow; they take anywhere between hundreds of
milliseconds and several seconds. To avoid blocking userspace, perform
these refreshes on a separate thread. The thread will also take care of
initializing the display before first use and clearing it when the CRTC
is disabled.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/rockchip/rockchip_ebc.c | 82 -
 1 file changed, 81 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c 
b/drivers/gpu/drm/rockchip/rockchip_ebc.c
index 5f9502313657..ebe60d5e011a 100644
--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
+++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
@@ -6,6 +6,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -135,9 +136,15 @@ struct rockchip_ebc {
struct drm_planeplane;
struct regmap   *regmap;
struct regulator_bulk_data  supplies[EBC_NUM_SUPPLIES];
+   struct task_struct  *refresh_thread;
u32 dsp_start;
+   boolreset_complete;
 };
 
+static bool skip_reset;
+module_param(skip_reset, bool, 0444);
+MODULE_PARM_DESC(skip_reset, "skip the initial display reset");
+
 DEFINE_DRM_GEM_FOPS(rockchip_ebc_fops);
 
 static const struct drm_driver rockchip_ebc_drm_driver = {
@@ -172,6 +179,42 @@ to_ebc_crtc_state(struct drm_crtc_state *crtc_state)
return container_of(crtc_state, struct ebc_crtc_state, base);
 }
 
+static int rockchip_ebc_refresh_thread(void *data)
+{
+   struct rockchip_ebc *ebc = data;
+
+   while (!kthread_should_stop()) {
+   /*
+* LUTs use both the old and the new pixel values as inputs.
+* However, the initial contents of the display are unknown.
+* The special RESET waveform will initialize the display to
+* known contents (white) regardless of its current contents.
+*/
+   if (!ebc->reset_complete) {
+   ebc->reset_complete = true;
+   drm_dbg(>drm, "display reset\n");
+   }
+
+   while (!kthread_should_park()) {
+   drm_dbg(>drm, "display update\n");
+
+   set_current_state(TASK_IDLE);
+   schedule();
+   __set_current_state(TASK_RUNNING);
+   }
+
+   /*
+* Clear the display before disabling the CRTC. Use the
+* highest-quality waveform to minimize visible artifacts.
+*/
+   drm_dbg(>drm, "display clear\n");
+
+   kthread_parkme();
+   }
+
+   return 0;
+}
+
 static inline struct rockchip_ebc *crtc_to_ebc(struct drm_crtc *crtc)
 {
return container_of(crtc, struct rockchip_ebc, crtc);
@@ -296,11 +339,23 @@ static void rockchip_ebc_crtc_atomic_flush(struct 
drm_crtc *crtc,
 static void rockchip_ebc_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
 {
+   struct rockchip_ebc *ebc = crtc_to_ebc(crtc);
+   struct drm_crtc_state *crtc_state;
+
+   crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+   if (crtc_state->mode_changed)
+   kthread_unpark(ebc->refresh_thread);
 }
 
 static void rockchip_ebc_crtc_atomic_disable(struct drm_crtc *crtc,
 struct drm_atomic_state *state)
 {
+   struct rockchip_ebc *ebc = crtc_to_ebc(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);
 }
 
 static const struct drm_crtc_helper_funcs rockchip_ebc_crtc_helper_funcs = {
@@ -408,6 +463,14 @@ static int rockchip_ebc_plane_atomic_check(struct 
drm_plane *plane,
 static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane,
 struct drm_atomic_state *state)
 {
+   struct rockchip_ebc *ebc = plane_to_ebc(plane);
+   struct drm_plane_state *plane_state;
+
+   plane_state = drm_atomic_get_new_plane_state(state, plane);
+   if (!plane_state->crtc)
+   return;
+
+   wake_up_process(ebc->refresh_thread);
 }
 
 static const struct drm_plane_helper_funcs rockchip_ebc_plane_helper_funcs = {
@@ -673,6 +736,7 @@ static int rockchip_ebc_probe(struct platform_device *pdev)
 
platform_set_drvdata(pdev, ebc);
init_completion(>display_end);
+   ebc->reset_complete = skip_reset;
 
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
@@ -716,12 +780,26 @@ static int rockchip_ebc_probe(struct platform_device 
*pdev)
return ret;
 

[RFC PATCH 07/16] drm/rockchip: ebc: Add CRTC buffer management

2022-04-13 Thread Samuel Holland
This commit adds the "context" structure which holds all buffers needed
to refresh the display. It is allocated separately from the CRTC state
because it is reused as long as no mode change occurs.

There are three buffers holding Y4 (grayscale 4 bits/pixel) pixel data:
  - "prev" - contents of the display at the beginning of a refresh.
  - "next" - contents of the display at the end of that refresh. When a
refresh finishes, the "next" buffer is copied into "prev".
  - "final" - contents of the display at the end of the final refresh.
This buffer is necessary because a refresh waveform cannot be
modified or interrupted once it is started. If a pixel's value is
changed while it is already being refreshed, the "next" buffer
cannot be updated until the first waveform completes. At that time,
the "final" buffer is copied into "next", and another refresh is
started. The name "final" refers to the write-combining behavior of
this buffer; any number of plane updates can change this buffer
while waiting for the current refresh to complete.

Then there are two buffers holding "phase" data. These are only used
during partial refreshes. The phase represents the time component (the X
coordinate) of the waveform. Since the EBC supports a maximum of 256
phases in a waveform, the phase number requires one byte per pixel. The
driver swaps between two buffers to minimize the delay between phases,
as these buffers must be updated for every phase in the waveform.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/rockchip/rockchip_ebc.c | 166 +++-
 1 file changed, 163 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c 
b/drivers/gpu/drm/rockchip/rockchip_ebc.c
index ebe60d5e011a..095d66e67c2f 100644
--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
+++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -141,6 +142,10 @@ struct rockchip_ebc {
boolreset_complete;
 };
 
+static int default_waveform = DRM_EPD_WF_GC16;
+module_param(default_waveform, int, 0644);
+MODULE_PARM_DESC(default_waveform, "waveform to use for display updates");
+
 static bool skip_reset;
 module_param(skip_reset, bool, 0444);
 MODULE_PARM_DESC(skip_reset, "skip the initial display reset");
@@ -165,12 +170,86 @@ static const struct drm_mode_config_funcs 
rockchip_ebc_mode_config_funcs = {
.atomic_commit  = drm_atomic_helper_commit,
 };
 
+/**
+ * struct rockchip_ebc_ctx - context for performing display refreshes
+ *
+ * @kref: Reference count, maintained as part of the CRTC's atomic state
+ * @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;
+   u8  *prev;
+   u8  *next;
+   u8  *final;
+   u8  *phase[2];
+   u32 gray4_pitch;
+   u32 gray4_size;
+   u32 phase_pitch;
+   u32 phase_size;
+};
+
+static void rockchip_ebc_ctx_free(struct rockchip_ebc_ctx *ctx)
+{
+   kfree(ctx->prev);
+   kfree(ctx->next);
+   kfree(ctx->final);
+   kfree(ctx->phase[0]);
+   kfree(ctx->phase[1]);
+   kfree(ctx);
+}
+
+static struct rockchip_ebc_ctx *rockchip_ebc_ctx_alloc(u32 width, u32 height)
+{
+   u32 gray4_size = width * height / 2;
+   u32 phase_size = width * height;
+   struct rockchip_ebc_ctx *ctx;
+
+   ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+   if (!ctx)
+   return NULL;
+
+   ctx->prev = kmalloc(gray4_size, GFP_KERNEL);
+   ctx->next = kmalloc(gray4_size, GFP_KERNEL);
+   ctx->final = kmalloc(gray4_size, GFP_KERNEL);
+   ctx->phase[0] = kmalloc(phase_size, GFP_KERNEL);
+   ctx->phase[1] = kmalloc(phase_size, GFP_KERNEL);
+   if (!ctx->prev || !ctx->next || !ctx->final ||
+   !ctx->phase[0] || !ctx->phase[1]) {
+   rockchip_ebc_ctx_free(ctx);
+   return NULL;
+   }
+
+   kref_init(>kref);
+   ctx->gray4_pitch = width / 2;
+   ctx->gray4_size  = gray4_size;
+   ctx-

[RFC PATCH 08/16] drm/rockchip: ebc: Add LUT loading

2022-04-13 Thread Samuel Holland
The EBC contains a 16 KiB SRAM which stores the current LUT. It needs to
be programmed any time the LUT changes or the hardware block is enabled.
Since both of these triggers can happen at the same time, use a flag to
avoid writing the LUT twice.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/rockchip/Kconfig|  3 +-
 drivers/gpu/drm/rockchip/rockchip_ebc.c | 76 +
 2 files changed, 78 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 9d3273a5fd97..efe4476e336d 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -94,7 +94,8 @@ endif
 
 config DRM_ROCKCHIP_EBC
tristate "DRM Support for Rockchip EBC"
-   depends on DRM
+   depends on DRM && IIO
+   select DRM_EPD_HELPER
select DRM_GEM_SHMEM_HELPER
select DRM_KMS_HELPER
help
diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c 
b/drivers/gpu/drm/rockchip/rockchip_ebc.c
index 095d66e67c2f..ca3173b28d1c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
+++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
@@ -5,6 +5,7 @@
 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -122,6 +123,7 @@
 #define EBC_WIN_MST2   0x0058
 #define EBC_LUT_DATA   0x1000
 
+#define EBC_MAX_PHASES 256
 #define EBC_NUM_LUT_REGS   0x1000
 #define EBC_NUM_SUPPLIES   3
 
@@ -134,11 +136,15 @@ struct rockchip_ebc {
struct drm_crtc crtc;
struct drm_device   drm;
struct drm_encoder  encoder;
+   struct drm_epd_lut  lut;
+   struct drm_epd_lut_file lut_file;
struct drm_planeplane;
+   struct iio_channel  *temperature_channel;
struct regmap   *regmap;
struct regulator_bulk_data  supplies[EBC_NUM_SUPPLIES];
struct task_struct  *refresh_thread;
u32 dsp_start;
+   boollut_changed;
boolreset_complete;
 };
 
@@ -282,10 +288,59 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
 bool global_refresh,
 enum drm_epd_waveform waveform)
 {
+   struct drm_device *drm = >drm;
+   struct device *dev = drm->dev;
+   int ret, temperature;
+
+   /* Resume asynchronously while preparing to refresh. */
+   ret = pm_runtime_get(dev);
+   if (ret < 0) {
+   drm_err(drm, "Failed to request resume: %d\n", ret);
+   return;
+   }
+
+   ret = iio_read_channel_processed(ebc->temperature_channel, 
);
+   if (ret < 0) {
+   drm_err(drm, "Failed to get temperature: %d\n", ret);
+   } else {
+   /* Convert from millicelsius to celsius. */
+   temperature /= 1000;
+
+   ret = drm_epd_lut_set_temperature(>lut, temperature);
+   if (ret < 0)
+   drm_err(drm, "Failed to set LUT temperature: %d\n", 
ret);
+   else if (ret)
+   ebc->lut_changed = true;
+   }
+
+   ret = drm_epd_lut_set_waveform(>lut, waveform);
+   if (ret < 0)
+   drm_err(drm, "Failed to set LUT waveform: %d\n", ret);
+   else if (ret)
+   ebc->lut_changed = true;
+
+   /* Wait for the resume to complete before writing any registers. */
+   ret = pm_runtime_resume(dev);
+   if (ret < 0) {
+   drm_err(drm, "Failed to resume: %d\n", ret);
+   pm_runtime_put(dev);
+   return;
+   }
+
+   /* This flag may have been set above, or by the runtime PM callback. */
+   if (ebc->lut_changed) {
+   ebc->lut_changed = false;
+   regmap_bulk_write(ebc->regmap, EBC_LUT_DATA,
+ ebc->lut.buf, EBC_NUM_LUT_REGS);
+   }
+
if (global_refresh)
rockchip_ebc_global_refresh(ebc, ctx);
else
rockchip_ebc_partial_refresh(ebc, ctx);
+
+   pm_runtime_mark_last_busy(dev);
+   pm_runtime_put_autosuspend(dev);
 }
 
 static int rockchip_ebc_refresh_thread(void *data)
@@ -708,6 +763,15 @@ static int rockchip_ebc_drm_init(struct rockchip_ebc *ebc)
struct drm_bridge *bridge;
int ret;
 
+   ret = drmm_epd_lut_file_init(drm, >lut_file, "rockchip/ebc.wbf");
+   if (ret)
+   return ret;
+
+   ret = drmm_epd_lut_init(>lut_file, >lut,
+   DRM_EPD_LUT_4BIT_PACKED, EBC_MAX_PHASES);
+   if (ret)
+   return ret;
+
ret = drmm_mode_config_init(drm);
 

[RFC PATCH 03/16] drm/rockchip: Add EBC platform driver skeleton

2022-04-13 Thread Samuel Holland
The Rockchip E-Book Controller (EBC) is a timing controller (TCON)
responsible for sending timing signals and pixel update waveforms to an
electrophoretic display (EPD). The EBC has several modes of operation.
In direct mode, it reads precomputed source driver polarity data from a
series of buffers in RAM. In the other modes, it reads pixel luminance
data from RAM, and uses a lookup table (LUT) to compute the source
driver polarity for each phase within the waveform.

This commit adds a platform driver skeleton for the EBC, containing the
IRQ handler and runtime PM hooks. The EBC only needs to be powered up
when the display is actively being refreshed. regcache is used to allow
configuration changes (i.e. modeset) while the EBC is powered down.

While two of the regulator consumers here actually power the display
panel, not the EBC hardware, they are consumed here because again they
are only needed during display refreshes. They do not match the normal
panel prepare/enable lifecycle.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/Makefile|   2 +-
 drivers/gpu/drm/rockchip/Kconfig|  11 +
 drivers/gpu/drm/rockchip/Makefile   |   2 +
 drivers/gpu/drm/rockchip/rockchip_ebc.c | 324 
 4 files changed, 338 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_ebc.c

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 49380ccfe9d6..e940f81a2acf 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -93,7 +93,7 @@ obj-$(CONFIG_DRM_VGEM)+= vgem/
 obj-$(CONFIG_DRM_VKMS) += vkms/
 obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
 obj-$(CONFIG_DRM_EXYNOS) +=exynos/
-obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
+obj-y  +=rockchip/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index fa5cfda4e90e..9d3273a5fd97 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -91,3 +91,14 @@ config ROCKCHIP_RK3066_HDMI
  for the RK3066 HDMI driver. If you want to enable
  HDMI on RK3066 based SoC, you should select this option.
 endif
+
+config DRM_ROCKCHIP_EBC
+   tristate "DRM Support for Rockchip EBC"
+   depends on DRM
+   select DRM_GEM_SHMEM_HELPER
+   select DRM_KMS_HELPER
+   help
+ This selects DRM/KMS support for the Rockchip E-Book Controller (EBC).
+ Choose this option if you have a Rockchip SoC and an electrophoretic
+ display. This hardware and driver is separate from the normal Rockchip
+ display hardware and DRM driver.
diff --git a/drivers/gpu/drm/rockchip/Makefile 
b/drivers/gpu/drm/rockchip/Makefile
index 1a56f696558c..e3accc526438 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -16,3 +16,5 @@ rockchipdrm-$(CONFIG_ROCKCHIP_RGB) += rockchip_rgb.o
 rockchipdrm-$(CONFIG_ROCKCHIP_RK3066_HDMI) += rk3066_hdmi.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
+
+obj-$(CONFIG_DRM_ROCKCHIP_EBC) += rockchip_ebc.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c 
b/drivers/gpu/drm/rockchip/rockchip_ebc.c
new file mode 100644
index ..5ed66c6cd2f0
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021-2022 Samuel Holland 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define EBC_DSP_START  0x
+#define EBC_DSP_START_DSP_OUT_LOW  BIT(31)
+#define EBC_DSP_START_DSP_SDCE_WIDTH(x)((x) << 16)
+#define EBC_DSP_START_DSP_EINK_MODEBIT(13)
+#define EBC_DSP_START_SW_BURST_CTRLBIT(12)
+#define EBC_DSP_START_DSP_FRM_TOTAL(x) ((x) << 2)
+#define EBC_DSP_START_DSP_RST  BIT(1)
+#define EBC_DSP_START_DSP_FRM_STARTBIT(0)
+#define EBC_EPD_CTRL   0x0004
+#define EBC_EPD_CTRL_EINK_MODE_SWAPBIT(31)
+#define EBC_EPD_CTRL_DSP_GD_END(x) ((x) << 16)
+#define EBC_EPD_CTRL_DSP_GD_ST(x)  ((x) << 8)
+#define EBC_EPD_CTRL_DSP_THREE_WIN_MODEBIT(7)
+#define EBC_EPD_CTRL_DSP_SDDW_MODE BIT(6)
+#define EBC_EPD_CTRL_EPD_AUO   BIT(5)
+#define EBC_EPD_CTRL_EPD_PWR(x)((x) << 2)
+#define EBC_EPD_CTRL_EPD_GDRL  BIT(1)
+#define EBC_EPD_CTRL_EPD_SDSHR BIT(0)
+#define EBC_DSP_CTRL   0x0008
+#define EBC_DSP_CTRL_DSP_SWAP_MODE(x)  ((x) << 30)
+#define EBC_DSP_CTRL_DSP_DIFF_MODE BIT(29)
+#define EBC_DSP_CTRL_DSP_LUT_MODE  BIT(28)
+#define EBC_DSP_CTRL_DSP_VCOM_MODE BIT(27)
+#define EBC_DSP_CTRL_DSP_GDOE_POL  BIT(26)
+#define EBC_DSP_C

[RFC PATCH 04/16] drm/rockchip: ebc: Add DRM driver skeleton

2022-04-13 Thread Samuel Holland
The Rockchip E-Book Controller (EBC) has a relatively simple and self-
contained display pipeline. The pipeline consists of a single CRTC,
encoder, and bridge, with the bridge normally attached to a panel.

Initially, there is also a single plane. Since all blitting is done in
software, the driver could eventually support any number of planes. For
example, it could expose one plane for each supported waveform.

However, EPD controller hardware has some unique properties which
complicate using drm_simple_display_pipe:
  - EPDs operate on relative pixel values, not absolute pixel values.
This requires the driver to maintain multiple shadow buffers for the
"previous" and "next" framebuffer contents.
  - It also means that disabling the CRTC (i.e. clearing the screen)
requires access to these shadow buffers, as it requires knowing the
previous contents of the framebuffer. And of course it requires a
buffer for the blank image.
  - Atomically managing these shadow buffers needs reference counting in
.atomic_check. However, drm_simple_display_pipe_funcs::check is only
called while the plane is visible, complicating this.
  - Furthermore, because all plane blitting/blending must be done in
software, the number and location of these planes is arbitrary.
drm_simple_display_pipe enforces an unnecessary limitation that a
single plane covers the entire CRTC.

For these reasons, drm_simple_display_pipe is not used.

This commit adds the structure for this pipeline. The atomic helper
callbacks are left empty. They will be filled in incrementally by the
next several commits.

Both the CRTC and the pipe need extra state information, so this commit
adds the state hook boilerplate. Additionally, the plane takes advantage
of the shadow plane helpers.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/rockchip/rockchip_ebc.c | 359 +++-
 1 file changed, 356 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c 
b/drivers/gpu/drm/rockchip/rockchip_ebc.c
index 5ed66c6cd2f0..f75fd23adda2 100644
--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
+++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
@@ -12,6 +12,17 @@
 #include 
 #include 
 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
 #define EBC_DSP_START  0x
 #define EBC_DSP_START_DSP_OUT_LOW  BIT(31)
 #define EBC_DSP_START_DSP_SDCE_WIDTH(x)((x) << 16)
@@ -118,10 +129,332 @@ struct rockchip_ebc {
struct clk  *dclk;
struct clk  *hclk;
struct completion   display_end;
+   struct drm_crtc crtc;
+   struct drm_device   drm;
+   struct drm_encoder  encoder;
+   struct drm_planeplane;
struct regmap   *regmap;
struct regulator_bulk_data  supplies[EBC_NUM_SUPPLIES];
 };
 
+DEFINE_DRM_GEM_FOPS(rockchip_ebc_fops);
+
+static const struct drm_driver rockchip_ebc_drm_driver = {
+   .lastclose  = drm_fb_helper_lastclose,
+   DRM_GEM_SHMEM_DRIVER_OPS,
+   .major  = 0,
+   .minor  = 3,
+   .name   = "rockchip-ebc",
+   .desc   = "Rockchip E-Book Controller",
+   .date   = "20220303",
+   .driver_features= DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
+   .fops   = _ebc_fops,
+};
+
+static const struct drm_mode_config_funcs rockchip_ebc_mode_config_funcs = {
+   .fb_create  = drm_gem_fb_create_with_dirty,
+   .atomic_check   = drm_atomic_helper_check,
+   .atomic_commit  = drm_atomic_helper_commit,
+};
+
+/*
+ * CRTC
+ */
+
+struct ebc_crtc_state {
+   struct drm_crtc_state   base;
+};
+
+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 inline struct rockchip_ebc *crtc_to_ebc(struct drm_crtc *crtc)
+{
+   return container_of(crtc, struct rockchip_ebc, crtc);
+}
+
+static void rockchip_ebc_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+}
+
+static int rockchip_ebc_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+   return 0;
+}
+
+static void rockchip_ebc_crtc_atomic_flush(struct drm_crtc *crtc,
+  struct drm_atomic_state *state)
+{
+}
+
+static void rockchip_ebc_crtc_atomic_enable(struct drm_crtc *crtc,
+   struct drm_atomic_state *state)
+{
+}
+
+static void rockchip_ebc_crtc_atomic_disable(struct drm_crtc *crtc,
+struct drm_atomic_state *s

[RFC PATCH 01/16] drm: Add a helper library for EPD drivers

2022-04-13 Thread Samuel Holland
Currently, this library is focused on LUTs and LUT files, specifically
the PVI wbf-format files shipped with E-Ink displays. It provides
helpers to load and validate LUT files, and extract LUTs from them.

Since EPD controllers need the LUTs in various formats, this library
allows drivers to declare their desired format. It will then convert
LUTs to that format before returning them.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/Kconfig  |   6 +
 drivers/gpu/drm/Makefile |   2 +
 drivers/gpu/drm/drm_epd_helper.c | 663 +++
 include/drm/drm_epd_helper.h | 104 +
 4 files changed, 775 insertions(+)
 create mode 100644 drivers/gpu/drm/drm_epd_helper.c
 create mode 100644 include/drm/drm_epd_helper.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f1422bee3dcc..ad96cf605444 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -198,6 +198,12 @@ config DRM_DP_CEC
  Note: not all adapters support this feature, and even for those
  that do support this they often do not hook up the CEC pin.
 
+config DRM_EPD_HELPER
+   tristate
+   depends on DRM
+   help
+ Choose this if you need the EPD (LUT, etc.) helper functions
+
 config DRM_TTM
tristate
depends on DRM && MMU
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index c2ef5f9fce54..49380ccfe9d6 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -33,6 +33,8 @@ drm-$(CONFIG_DRM_PRIVACY_SCREEN) += drm_privacy_screen.o 
drm_privacy_screen_x86.
 
 obj-$(CONFIG_DRM_NOMODESET) += drm_nomodeset.o
 
+obj-$(CONFIG_DRM_EPD_HELPER) += drm_epd_helper.o
+
 drm_cma_helper-y := drm_gem_cma_helper.o
 drm_cma_helper-$(CONFIG_DRM_KMS_HELPER) += drm_fb_cma_helper.o
 obj-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_cma_helper.o
diff --git a/drivers/gpu/drm/drm_epd_helper.c b/drivers/gpu/drm/drm_epd_helper.c
new file mode 100644
index ..433a6728ef3e
--- /dev/null
+++ b/drivers/gpu/drm/drm_epd_helper.c
@@ -0,0 +1,663 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Electrophoretic Display Helper Library
+ *
+ * Copyright (C) 2022 Samuel Holland 
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+/**
+ * DOC: overview
+ *
+ * This library provides functions for working with the lookup tables (LUTs)
+ * used by electrophoretic displays (EPDs). It fills in a LUT buffer based on
+ * the selected waveform, the panel temperature, and the buffer format needed
+ * by the driver.
+ */
+
+struct pvi_wbf_offset {
+   u8  b[3];
+};
+
+struct pvi_wbf_pointer {
+   struct pvi_wbf_offset   offset;
+   u8  checksum;
+};
+
+struct pvi_wbf_file_header {
+   __le32  checksum;   // 0x00
+   __le32  file_size;  // 0x04
+   __le32  serial; // 0x08
+   u8  run_type;   // 0x0c
+   u8  fpl_platform;   // 0x0d
+   __le16  fpl_lot;// 0x0e
+   u8  mode_version;   // 0x10
+   u8  wf_version; // 0x11
+   u8  wf_subversion;  // 0x12
+   u8  wf_type;// 0x13
+   u8  panel_size; // 0x14
+   u8  amepd_part_number;  // 0x15
+   u8  wf_rev; // 0x16
+   u8  frame_rate_bcd; // 0x17
+   u8  frame_rate_hex; // 0x18
+   u8  vcom_offset;  

[RFC PATCH 02/16] dt-bindings: display: rockchip: Add EBC binding

2022-04-13 Thread Samuel Holland
The Rockchip E-Book Controller (EBC) is a controller for Electrophoretic
Displays (EPDs). It is self-contained; it does not interact directly
with the VOP or the RGA.

While two of the regulator consumers here actually power the display
panel, not the EBC hardware, they are consumed here because they are
only needed during display refreshes. They do not match the normal
panel prepare/enable lifecycle.

Signed-off-by: Samuel Holland 
---

 .../display/rockchip/rockchip,rk3568-ebc.yaml | 106 ++
 1 file changed, 106 insertions(+)
 create mode 100644 
Documentation/devicetree/bindings/display/rockchip/rockchip,rk3568-ebc.yaml

diff --git 
a/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3568-ebc.yaml 
b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3568-ebc.yaml
new file mode 100644
index ..957ca874ab02
--- /dev/null
+++ 
b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3568-ebc.yaml
@@ -0,0 +1,106 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/rockchip/rockchip,rk3568-ebc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip SoC E-Book Controller (EBC)
+
+description:
+  Rockchip EBC is a controller for Electrophoretic Displays (EPDs).
+
+maintainers:
+  - Samuel Holland 
+
+properties:
+  compatible:
+enum:
+  - rockchip,rk3568-ebc
+
+  reg:
+maxItems: 1
+
+  interrupts:
+maxItems: 1
+
+  clocks:
+items:
+  - description: AHB register clock
+  - description: Pixel clock
+
+  clock-names:
+items:
+  - const: hclk
+  - const: dclk
+
+  resets:
+items:
+  - description: hclk domain reset
+  - description: dclk domain reset
+
+  reset-names:
+items:
+  - const: hclk
+  - const: dclk
+
+  io-channels:
+maxItems: 1
+description: I/O channel for panel temperature measurement
+
+  panel-supply:
+description: Regulator supplying the panel's logic voltage
+
+  power-domains:
+maxItems: 1
+
+  vcom-supply:
+description: Regulator supplying the panel's compensation voltage
+
+  vdrive-supply:
+description: Regulator supplying the panel's gate and source drivers
+
+  port:
+$ref: /schemas/graph.yaml#/properties/port
+description: OF graph port for the attached display panel
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+  - power-domains
+  - panel-supply
+  - vcom-supply
+  - vdrive-supply
+
+additionalProperties: false
+
+examples:
+  - |
+#include 
+#include 
+#include 
+
+ebc: ebc@fdec {
+  compatible = "rockchip,rk3568-ebc";
+  reg = <0x0 0xfdec 0x0 0x5000>;
+  interrupts = ;
+  clocks = < HCLK_EBC>, < DCLK_EBC>;
+  clock-names = "hclk", "dclk";
+  resets = < SRST_H_EBC>, < SRST_D_EBC>;
+  reset-names = "hclk", "dclk";
+  power-domains = < RK3568_PD_RGA>;
+
+  panel-supply = <>;
+  vcom-supply = <>;
+  vdrive-supply = <>;
+
+  port {
+ebc_out_panel: endpoint {
+  remote-endpoint = <_in_ebc>;
+};
+  };
+};
-- 
2.35.1



[RFC PATCH 00/16] drm/rockchip: Rockchip EBC ("E-Book Controller") display driver

2022-04-13 Thread Samuel Holland
ts doing the whole pipeline in software. So
that's why I wrote a separate helper library; I hope this code can
be reused.

Thanks for any input!
Samuel

[0]: 
https://dl.radxa.com/rock3/docs/hw/datasheet/Rockchip%20RK3568%20TRM%20Part2%20V1.1-20210301.pdf
[1]: https://wiki.pine64.org/wiki/PineNote
[2]: 
https://lore.kernel.org/lkml/20220206080016.796556-1-andr...@kemnade.info/T/
[3]: 
https://files.pine64.org/doc/quartz64/Eink%20P-511-828-V1_ED103TC2%20Formal%20Spec%20V1.0_20190514.pdf
[4]: https://lore.kernel.org/lkml/cover.1646683502.git.ge...@linux-m68k.org/T/
[5]: 
https://lore.kernel.org/lkml/20220330094126.30252-1-alist...@alistair23.me/T/
[6]: https://github.com/megous/linux/commits/pb-5.17
[7]: https://github.com/megous/linux/commit/3cdf84388959
[8]: https://github.com/fread-ink/inkwave


Samuel Holland (16):
  drm: Add a helper library for EPD drivers
  dt-bindings: display: rockchip: Add EBC binding
  drm/rockchip: Add EBC platform driver skeleton
  drm/rockchip: ebc: Add DRM driver skeleton
  drm/rockchip: ebc: Add CRTC mode setting
  drm/rockchip: ebc: Add CRTC refresh thread
  drm/rockchip: ebc: Add CRTC buffer management
  drm/rockchip: ebc: Add LUT loading
  drm/rockchip: ebc: Implement global refreshes
  drm/rockchip: ebc: Implement partial refreshes
  drm/rockchip: ebc: Enable diff mode for partial refreshes
  drm/rockchip: ebc: Add support for direct mode
  drm/rockchip: ebc: Add a panel reflection option
  drm/panel-simple: Add eInk ED103TC2
  arm64: dts: rockchip: rk356x: Add EBC node
  [DO NOT MERGE] arm64: dts: rockchip: pinenote: Enable EBC display
pipeline

 .../display/rockchip/rockchip,rk3568-ebc.yaml |  106 ++
 .../boot/dts/rockchip/rk3566-pinenote.dtsi|   80 +
 arch/arm64/boot/dts/rockchip/rk356x.dtsi  |   14 +
 drivers/gpu/drm/Kconfig   |6 +
 drivers/gpu/drm/Makefile  |4 +-
 drivers/gpu/drm/drm_epd_helper.c  |  663 +++
 drivers/gpu/drm/panel/panel-simple.c  |   31 +
 drivers/gpu/drm/rockchip/Kconfig  |   12 +
 drivers/gpu/drm/rockchip/Makefile |2 +
 drivers/gpu/drm/rockchip/rockchip_ebc.c   | 1586 +
 include/drm/drm_epd_helper.h  |  104 ++
 11 files changed, 2607 insertions(+), 1 deletion(-)
 create mode 100644 
Documentation/devicetree/bindings/display/rockchip/rockchip,rk3568-ebc.yaml
 create mode 100644 drivers/gpu/drm/drm_epd_helper.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_ebc.c
 create mode 100644 include/drm/drm_epd_helper.h

-- 
2.35.1



Re: [PATCH 3/6] drm/sun4i: sun8i-hdmi-phy: Used device-managed clocks/resets

2022-04-12 Thread Samuel Holland
On 4/12/22 8:23 AM, Maxime Ripard wrote:
> Hi,
> 
> On Mon, Apr 11, 2022 at 11:35:08PM -0500, Samuel Holland wrote:
>> Now that the HDMI PHY is using a platform driver, it can use device-
>> managed resources. Use these, as well as the dev_err_probe helper, to
>> simplify the probe function and get rid of the remove function.
>>
>> Signed-off-by: Samuel Holland 
>> ---
>>
>>  drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 100 -
>>  1 file changed, 30 insertions(+), 70 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
>> b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
>> index 1effa30bfe62..1351e633d485 100644
>> --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
>> +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
>> @@ -673,10 +673,8 @@ int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, 
>> struct device_node *node)
>>  static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
>>  {
>>  struct device *dev = >dev;
>> -struct device_node *node = dev->of_node;
>>  struct sun8i_hdmi_phy *phy;
>>  void __iomem *regs;
>> -int ret;
>>  
>>  phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
>>  if (!phy)
>> @@ -686,88 +684,50 @@ static int sun8i_hdmi_phy_probe(struct platform_device 
>> *pdev)
>>  phy->dev = dev;
>>  
>>  regs = devm_platform_ioremap_resource(pdev, 0);
>> -if (IS_ERR(regs)) {
>> -dev_err(dev, "Couldn't map the HDMI PHY registers\n");
>> -return PTR_ERR(regs);
>> -}
>> +if (IS_ERR(regs))
>> +return dev_err_probe(dev, PTR_ERR(regs),
>> + "Couldn't map the HDMI PHY registers\n");
>>  
>>  phy->regs = devm_regmap_init_mmio(dev, regs,
>>_hdmi_phy_regmap_config);
>> -if (IS_ERR(phy->regs)) {
>> -dev_err(dev, "Couldn't create the HDMI PHY regmap\n");
>> -return PTR_ERR(phy->regs);
>> -}
>> +if (IS_ERR(phy->regs))
>> +return dev_err_probe(dev, PTR_ERR(phy->regs),
>> + "Couldn't create the HDMI PHY regmap\n");
>>  
>> -phy->clk_bus = of_clk_get_by_name(node, "bus");
>> -if (IS_ERR(phy->clk_bus)) {
>> -dev_err(dev, "Could not get bus clock\n");
>> -return PTR_ERR(phy->clk_bus);
>> -}
>> -
>> -phy->clk_mod = of_clk_get_by_name(node, "mod");
>> -if (IS_ERR(phy->clk_mod)) {
>> -dev_err(dev, "Could not get mod clock\n");
>> -ret = PTR_ERR(phy->clk_mod);
>> -goto err_put_clk_bus;
>> -}
>> +phy->clk_bus = devm_clk_get(dev, "bus");
>> +if (IS_ERR(phy->clk_bus))
>> +return dev_err_probe(dev, PTR_ERR(phy->clk_bus),
>> + "Could not get bus clock\n");
>>  
>> -if (phy->variant->has_phy_clk) {
>> -phy->clk_pll0 = of_clk_get_by_name(node, "pll-0");
>> -if (IS_ERR(phy->clk_pll0)) {
>> -dev_err(dev, "Could not get pll-0 clock\n");
>> -ret = PTR_ERR(phy->clk_pll0);
>> -goto err_put_clk_mod;
>> -}
>> -
>> -if (phy->variant->has_second_pll) {
>> -phy->clk_pll1 = of_clk_get_by_name(node, "pll-1");
>> -if (IS_ERR(phy->clk_pll1)) {
>> -dev_err(dev, "Could not get pll-1 clock\n");
>> -ret = PTR_ERR(phy->clk_pll1);
>> -goto err_put_clk_pll0;
>> -}
>> -}
>> -}
>> +phy->clk_mod = devm_clk_get(dev, "mod");
>> +if (IS_ERR(phy->clk_mod))
>> +return dev_err_probe(dev, PTR_ERR(phy->clk_mod),
>> + "Could not get mod clock\n");
>>  
>> -phy->rst_phy = of_reset_control_get_shared(node, "phy");
>> -if (IS_ERR(phy->rst_phy)) {
>> -dev_err(dev, "Could not get phy reset control\n");
>> -ret = PTR_ERR(phy->rst_phy);
>> -goto err_put_clk_pll1;
>> -}
>> +if (phy->variant->has_phy_clk)
>> +phy->clk_pll0 = de

[PATCH 6/6] drm/sun4i: sun8i-hdmi-phy: Group PHY ops functions by generation

2022-04-11 Thread Samuel Holland
Now that the PHY ops are separated, sort them topologically, with the
common sun8i_hdmi_phy_set_polarity helper at the top. No function
contents are changed in this commit.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 67 --
 1 file changed, 32 insertions(+), 35 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 5be5c360ca7d..cc239106ba49 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -124,7 +124,19 @@ static const struct dw_hdmi_phy_config 
sun50i_h6_phy_config[] = {
 };
 
 static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy,
-   const struct drm_display_mode *mode);
+   const struct drm_display_mode *mode)
+{
+   u32 val = 0;
+
+   if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+   val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC;
+
+   if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+   val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC;
+
+   regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
+  SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
+};
 
 static int sun8i_a83t_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
  const struct drm_display_info *display,
@@ -193,6 +205,25 @@ static int sun8i_a83t_hdmi_phy_config(struct dw_hdmi 
*hdmi, void *data,
return 0;
 }
 
+static void sun8i_a83t_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
+{
+   struct sun8i_hdmi_phy *phy = data;
+
+   dw_hdmi_phy_gen2_txpwron(hdmi, 0);
+   dw_hdmi_phy_gen2_pddq(hdmi, 1);
+
+   regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
+  SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0);
+}
+
+static const struct dw_hdmi_phy_ops sun8i_a83t_hdmi_phy_ops = {
+   .init   = sun8i_a83t_hdmi_phy_config,
+   .disable= sun8i_a83t_hdmi_phy_disable,
+   .read_hpd   = dw_hdmi_phy_read_hpd,
+   .update_hpd = dw_hdmi_phy_update_hpd,
+   .setup_hpd  = dw_hdmi_phy_setup_hpd,
+};
+
 static int sun8i_h3_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
const struct drm_display_info *display,
const struct drm_display_mode *mode)
@@ -348,32 +379,6 @@ static int sun8i_h3_hdmi_phy_config(struct dw_hdmi *hdmi, 
void *data,
return 0;
 }
 
-static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy,
-   const struct drm_display_mode *mode)
-{
-   u32 val = 0;
-
-   if (mode->flags & DRM_MODE_FLAG_NHSYNC)
-   val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC;
-
-   if (mode->flags & DRM_MODE_FLAG_NVSYNC)
-   val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC;
-
-   regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
-  SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
-};
-
-static void sun8i_a83t_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
-{
-   struct sun8i_hdmi_phy *phy = data;
-
-   dw_hdmi_phy_gen2_txpwron(hdmi, 0);
-   dw_hdmi_phy_gen2_pddq(hdmi, 1);
-
-   regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
-  SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0);
-}
-
 static void sun8i_h3_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
 {
struct sun8i_hdmi_phy *phy = data;
@@ -385,14 +390,6 @@ static void sun8i_h3_hdmi_phy_disable(struct dw_hdmi 
*hdmi, void *data)
regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 0);
 }
 
-static const struct dw_hdmi_phy_ops sun8i_a83t_hdmi_phy_ops = {
-   .init   = sun8i_a83t_hdmi_phy_config,
-   .disable= sun8i_a83t_hdmi_phy_disable,
-   .read_hpd   = dw_hdmi_phy_read_hpd,
-   .update_hpd = dw_hdmi_phy_update_hpd,
-   .setup_hpd  = dw_hdmi_phy_setup_hpd,
-};
-
 static const struct dw_hdmi_phy_ops sun8i_h3_hdmi_phy_ops = {
.init   = sun8i_h3_hdmi_phy_config,
.disable= sun8i_h3_hdmi_phy_disable,
-- 
2.35.1



[PATCH 5/6] drm/sun4i: sun8i-hdmi-phy: Separate A83T and H3 PHY ops

2022-04-11 Thread Samuel Holland
Since the driver already needs to support multiple sets of ops, we can
drop the mid-layer used by the A83T and H3 PHYs. They share only a small
amount of code; factor this out as sun8i_hdmi_phy_set_polarity.

For clarity, this commit keeps the existing function order.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h  |  5 --
 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 89 +-
 2 files changed, 46 insertions(+), 48 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h 
b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
index f0b1aaad27d9..f082b8ecfe2c 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
@@ -156,11 +156,6 @@ struct sun8i_hdmi_phy_variant {
const struct dw_hdmi_phy_config *phy_cfg;
const struct dw_hdmi_phy_ops *phy_ops;
void (*phy_init)(struct sun8i_hdmi_phy *phy);
-   void (*phy_disable)(struct dw_hdmi *hdmi,
-   struct sun8i_hdmi_phy *phy);
-   int  (*phy_config)(struct dw_hdmi *hdmi,
-  struct sun8i_hdmi_phy *phy,
-  unsigned int clk_rate);
 };
 
 struct sun8i_hdmi_phy {
diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 71062e4e8666..5be5c360ca7d 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -123,10 +123,18 @@ static const struct dw_hdmi_phy_config 
sun50i_h6_phy_config[] = {
{ ~0UL,  0x, 0x, 0x}
 };
 
-static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi *hdmi,
- struct sun8i_hdmi_phy *phy,
- unsigned int clk_rate)
+static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy,
+   const struct drm_display_mode *mode);
+
+static int sun8i_a83t_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
+ const struct drm_display_info *display,
+ const struct drm_display_mode *mode)
 {
+   unsigned int clk_rate = mode->crtc_clock * 1000;
+   struct sun8i_hdmi_phy *phy = data;
+
+   sun8i_hdmi_phy_set_polarity(phy, mode);
+
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
@@ -185,10 +193,12 @@ static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi 
*hdmi,
return 0;
 }
 
-static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi,
-   struct sun8i_hdmi_phy *phy,
-   unsigned int clk_rate)
+static int sun8i_h3_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
+   const struct drm_display_info *display,
+   const struct drm_display_mode *mode)
 {
+   unsigned int clk_rate = mode->crtc_clock * 1000;
+   struct sun8i_hdmi_phy *phy = data;
u32 pll_cfg1_init;
u32 pll_cfg2_init;
u32 ana_cfg1_end;
@@ -197,6 +207,11 @@ static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi,
u32 b_offset = 0;
u32 val;
 
+   if (phy->variant->has_phy_clk)
+   clk_set_rate(phy->clk_phy, clk_rate);
+
+   sun8i_hdmi_phy_set_polarity(phy, mode);
+
/* bandwidth / frequency independent settings */
 
pll_cfg1_init = SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN |
@@ -333,11 +348,9 @@ static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi,
return 0;
 }
 
-static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
-const struct drm_display_info *display,
-const struct drm_display_mode *mode)
+static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy,
+   const struct drm_display_mode *mode)
 {
-   struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data;
u32 val = 0;
 
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
@@ -348,16 +361,12 @@ static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, 
void *data,
 
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
   SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
-
-   if (phy->variant->has_phy_clk)
-   clk_set_rate(phy->clk_phy, mode->crtc_clock * 1000);
-
-   return phy->variant->phy_config(hdmi, phy, mode->crtc_clock * 1000);
 };
 
-static void sun8i_hdmi_phy_disable_a83t(struct dw_hdmi *hdmi,
-   struct sun8i_hdmi_phy *phy)
+static void sun8i_a83t_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
 {
+   struct sun8i_hdmi_phy *phy = data;
+
dw_hdmi_phy_gen2_txpwron(hdmi, 0);
dw_hdmi_phy_gen2_pddq(hdmi, 1);
 
@@ -365,9 +374,10 @@ static void 

[PATCH 4/6] drm/sun4i: sun8i-hdmi-phy: Support multiple custom PHY ops

2022-04-11 Thread Samuel Holland
The D1 SoC comes with a new custom HDMI PHY, which does not share any
registers with the existing custom PHY. So it needs a new set of ops.
Instead of providing a flag in the variant structure, provide the ops
themselves.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h  |  2 +-
 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 12 ++--
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h 
b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
index 0adbfac6eb31..f0b1aaad27d9 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
@@ -151,10 +151,10 @@ struct sun8i_hdmi_phy;
 struct sun8i_hdmi_phy_variant {
bool has_phy_clk;
bool has_second_pll;
-   unsigned int is_custom_phy : 1;
const struct dw_hdmi_curr_ctrl *cur_ctr;
const struct dw_hdmi_mpll_config *mpll_cfg;
const struct dw_hdmi_phy_config *phy_cfg;
+   const struct dw_hdmi_phy_ops *phy_ops;
void (*phy_init)(struct sun8i_hdmi_phy *phy);
void (*phy_disable)(struct dw_hdmi *hdmi,
struct sun8i_hdmi_phy *phy);
diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 1351e633d485..71062e4e8666 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -567,8 +567,8 @@ void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
 {
const struct sun8i_hdmi_phy_variant *variant = phy->variant;
 
-   if (variant->is_custom_phy) {
-   plat_data->phy_ops = _hdmi_phy_ops;
+   if (variant->phy_ops) {
+   plat_data->phy_ops = variant->phy_ops;
plat_data->phy_name = "sun8i_dw_hdmi_phy";
plat_data->phy_data = phy;
} else {
@@ -587,7 +587,7 @@ static const struct regmap_config 
sun8i_hdmi_phy_regmap_config = {
 };
 
 static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = {
-   .is_custom_phy = true,
+   .phy_ops = _hdmi_phy_ops,
.phy_init = _hdmi_phy_init_a83t,
.phy_disable = _hdmi_phy_disable_a83t,
.phy_config = _hdmi_phy_config_a83t,
@@ -595,7 +595,7 @@ static const struct sun8i_hdmi_phy_variant 
sun8i_a83t_hdmi_phy = {
 
 static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = {
.has_phy_clk = true,
-   .is_custom_phy = true,
+   .phy_ops = _hdmi_phy_ops,
.phy_init = _hdmi_phy_init_h3,
.phy_disable = _hdmi_phy_disable_h3,
.phy_config = _hdmi_phy_config_h3,
@@ -604,7 +604,7 @@ static const struct sun8i_hdmi_phy_variant 
sun8i_h3_hdmi_phy = {
 static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = {
.has_phy_clk = true,
.has_second_pll = true,
-   .is_custom_phy = true,
+   .phy_ops = _hdmi_phy_ops,
.phy_init = _hdmi_phy_init_h3,
.phy_disable = _hdmi_phy_disable_h3,
.phy_config = _hdmi_phy_config_h3,
@@ -612,7 +612,7 @@ static const struct sun8i_hdmi_phy_variant 
sun8i_r40_hdmi_phy = {
 
 static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = {
.has_phy_clk = true,
-   .is_custom_phy = true,
+   .phy_ops = _hdmi_phy_ops,
.phy_init = _hdmi_phy_init_h3,
.phy_disable = _hdmi_phy_disable_h3,
.phy_config = _hdmi_phy_config_h3,
-- 
2.35.1



[PATCH 3/6] drm/sun4i: sun8i-hdmi-phy: Used device-managed clocks/resets

2022-04-11 Thread Samuel Holland
Now that the HDMI PHY is using a platform driver, it can use device-
managed resources. Use these, as well as the dev_err_probe helper, to
simplify the probe function and get rid of the remove function.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 100 -
 1 file changed, 30 insertions(+), 70 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 1effa30bfe62..1351e633d485 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -673,10 +673,8 @@ int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct 
device_node *node)
 static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
 {
struct device *dev = >dev;
-   struct device_node *node = dev->of_node;
struct sun8i_hdmi_phy *phy;
void __iomem *regs;
-   int ret;
 
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
if (!phy)
@@ -686,88 +684,50 @@ static int sun8i_hdmi_phy_probe(struct platform_device 
*pdev)
phy->dev = dev;
 
regs = devm_platform_ioremap_resource(pdev, 0);
-   if (IS_ERR(regs)) {
-   dev_err(dev, "Couldn't map the HDMI PHY registers\n");
-   return PTR_ERR(regs);
-   }
+   if (IS_ERR(regs))
+   return dev_err_probe(dev, PTR_ERR(regs),
+"Couldn't map the HDMI PHY registers\n");
 
phy->regs = devm_regmap_init_mmio(dev, regs,
  _hdmi_phy_regmap_config);
-   if (IS_ERR(phy->regs)) {
-   dev_err(dev, "Couldn't create the HDMI PHY regmap\n");
-   return PTR_ERR(phy->regs);
-   }
+   if (IS_ERR(phy->regs))
+   return dev_err_probe(dev, PTR_ERR(phy->regs),
+"Couldn't create the HDMI PHY regmap\n");
 
-   phy->clk_bus = of_clk_get_by_name(node, "bus");
-   if (IS_ERR(phy->clk_bus)) {
-   dev_err(dev, "Could not get bus clock\n");
-   return PTR_ERR(phy->clk_bus);
-   }
-
-   phy->clk_mod = of_clk_get_by_name(node, "mod");
-   if (IS_ERR(phy->clk_mod)) {
-   dev_err(dev, "Could not get mod clock\n");
-   ret = PTR_ERR(phy->clk_mod);
-   goto err_put_clk_bus;
-   }
+   phy->clk_bus = devm_clk_get(dev, "bus");
+   if (IS_ERR(phy->clk_bus))
+   return dev_err_probe(dev, PTR_ERR(phy->clk_bus),
+"Could not get bus clock\n");
 
-   if (phy->variant->has_phy_clk) {
-   phy->clk_pll0 = of_clk_get_by_name(node, "pll-0");
-   if (IS_ERR(phy->clk_pll0)) {
-   dev_err(dev, "Could not get pll-0 clock\n");
-   ret = PTR_ERR(phy->clk_pll0);
-   goto err_put_clk_mod;
-   }
-
-   if (phy->variant->has_second_pll) {
-   phy->clk_pll1 = of_clk_get_by_name(node, "pll-1");
-   if (IS_ERR(phy->clk_pll1)) {
-   dev_err(dev, "Could not get pll-1 clock\n");
-   ret = PTR_ERR(phy->clk_pll1);
-   goto err_put_clk_pll0;
-   }
-   }
-   }
+   phy->clk_mod = devm_clk_get(dev, "mod");
+   if (IS_ERR(phy->clk_mod))
+   return dev_err_probe(dev, PTR_ERR(phy->clk_mod),
+"Could not get mod clock\n");
 
-   phy->rst_phy = of_reset_control_get_shared(node, "phy");
-   if (IS_ERR(phy->rst_phy)) {
-   dev_err(dev, "Could not get phy reset control\n");
-   ret = PTR_ERR(phy->rst_phy);
-   goto err_put_clk_pll1;
-   }
+   if (phy->variant->has_phy_clk)
+   phy->clk_pll0 = devm_clk_get(dev, "pll-0");
+   if (IS_ERR(phy->clk_pll0))
+   return dev_err_probe(dev, PTR_ERR(phy->clk_pll0),
+"Could not get pll-0 clock\n");
+
+   if (phy->variant->has_second_pll)
+   phy->clk_pll1 = devm_clk_get(dev, "pll-1");
+   if (IS_ERR(phy->clk_pll1))
+   return dev_err_probe(dev, PTR_ERR(phy->clk_pll1),
+"Could not get pll-1 clock\n");
+
+   phy->rst_phy = devm_reset_control_get_shared(dev, "phy");
+   if (IS_ERR(phy->rst_phy))
+   return dev_err_probe(dev, PTR_ERR(phy->rst_phy),
+"Could not get phy 

[PATCH 2/6] drm/sun4i: sun8i-hdmi-phy: Use devm_platform_ioremap_resource

2022-04-11 Thread Samuel Holland
The struct resource is not used for anything else, so we can simplify
the code a bit by using the helper function.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 9 +
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 7b901aef789a..1effa30bfe62 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -675,7 +675,6 @@ static int sun8i_hdmi_phy_probe(struct platform_device 
*pdev)
struct device *dev = >dev;
struct device_node *node = dev->of_node;
struct sun8i_hdmi_phy *phy;
-   struct resource res;
void __iomem *regs;
int ret;
 
@@ -686,13 +685,7 @@ static int sun8i_hdmi_phy_probe(struct platform_device 
*pdev)
phy->variant = of_device_get_match_data(dev);
phy->dev = dev;
 
-   ret = of_address_to_resource(node, 0, );
-   if (ret) {
-   dev_err(dev, "phy: Couldn't get our resources\n");
-   return ret;
-   }
-
-   regs = devm_ioremap_resource(dev, );
+   regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs)) {
dev_err(dev, "Couldn't map the HDMI PHY registers\n");
return PTR_ERR(regs);
-- 
2.35.1



[PATCH 1/6] drm/sun4i: sun8i-hdmi-phy: Use of_device_get_match_data

2022-04-11 Thread Samuel Holland
Now that the HDMI PHY is using a platform driver, we can use the usual
helper function for getting the variant structure.

Signed-off-by: Samuel Holland 
---

 drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h  |  2 +-
 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 11 ++-
 2 files changed, 3 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h 
b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
index bffe1b9cd3dc..0adbfac6eb31 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
@@ -173,7 +173,7 @@ struct sun8i_hdmi_phy {
unsigned intrcal;
struct regmap   *regs;
struct reset_control*rst_phy;
-   struct sun8i_hdmi_phy_variant   *variant;
+   const struct sun8i_hdmi_phy_variant *variant;
 };
 
 struct sun8i_dw_hdmi_quirks {
diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c 
b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 5e2b0175df36..7b901aef789a 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -565,7 +565,7 @@ void sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy *phy)
 void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
struct dw_hdmi_plat_data *plat_data)
 {
-   struct sun8i_hdmi_phy_variant *variant = phy->variant;
+   const struct sun8i_hdmi_phy_variant *variant = phy->variant;
 
if (variant->is_custom_phy) {
plat_data->phy_ops = _hdmi_phy_ops;
@@ -672,7 +672,6 @@ int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct 
device_node *node)
 
 static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
 {
-   const struct of_device_id *match;
struct device *dev = >dev;
struct device_node *node = dev->of_node;
struct sun8i_hdmi_phy *phy;
@@ -680,17 +679,11 @@ static int sun8i_hdmi_phy_probe(struct platform_device 
*pdev)
void __iomem *regs;
int ret;
 
-   match = of_match_node(sun8i_hdmi_phy_of_table, node);
-   if (!match) {
-   dev_err(dev, "Incompatible HDMI PHY\n");
-   return -EINVAL;
-   }
-
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
if (!phy)
return -ENOMEM;
 
-   phy->variant = (struct sun8i_hdmi_phy_variant *)match->data;
+   phy->variant = of_device_get_match_data(dev);
phy->dev = dev;
 
ret = of_address_to_resource(node, 0, );
-- 
2.35.1



[PATCH 0/6] drm/sun4i: HDMI PHY cleanup/refactoring

2022-04-11 Thread Samuel Holland
This series prepares the sun8i HDMI PHY driver for supporting the new
custom PHY in the Allwinner D1 SoC. No functional change intended here.

This series was tested on D1 and H3.


Samuel Holland (6):
  drm/sun4i: sun8i-hdmi-phy: Use of_device_get_match_data
  drm/sun4i: sun8i-hdmi-phy: Use devm_platform_ioremap_resource
  drm/sun4i: sun8i-hdmi-phy: Used device-managed clocks/resets
  drm/sun4i: sun8i-hdmi-phy: Support multiple custom PHY ops
  drm/sun4i: sun8i-hdmi-phy: Separate A83T and H3 PHY ops
  drm/sun4i: sun8i-hdmi-phy: Group PHY ops functions by generation

 drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h  |   9 +-
 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 252 ++---
 2 files changed, 101 insertions(+), 160 deletions(-)

-- 
2.35.1



[PATCH v2 14/14] drm/sun4i: Add compatible for D1 display engine

2022-04-11 Thread Samuel Holland
Now that the various blocks in the D1 display engine pipeline are
supported, we can enable the overall engine.

Acked-by: Jernej Skrabec 
Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun4i_drv.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c 
b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 6a9ba8a77c77..275f7e4a03ae 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -418,6 +418,7 @@ static const struct of_device_id sun4i_drv_of_table[] = {
{ .compatible = "allwinner,sun8i-r40-display-engine" },
{ .compatible = "allwinner,sun8i-v3s-display-engine" },
{ .compatible = "allwinner,sun9i-a80-display-engine" },
+   { .compatible = "allwinner,sun20i-d1-display-engine" },
{ .compatible = "allwinner,sun50i-a64-display-engine" },
{ .compatible = "allwinner,sun50i-h6-display-engine" },
{ }
-- 
2.35.1



[PATCH v2 13/14] drm/sun4i: Add support for D1 TCONs

2022-04-11 Thread Samuel Holland
D1 has a TCON TOP, so its quirks are similar to those for the R40 TCONs.
While there are some register changes, the part of the TCON TV supported
by the driver matches the R40 quirks, so that quirks structure can be
reused. D1 has the first supported TCON LCD with a TCON TOP, so the TCON
LCD needs a new quirks structure.

D1's TCON LCD hardware supports LVDS; in fact it provides dual-link LVDS
from a single TCON. However, it comes with a brand new LVDS PHY. Since
this PHY has not been tested, leave out LVDS driver support for now.

Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun4i_tcon.c | 8 
 1 file changed, 8 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c 
b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 88db2d2a9336..2ee158aaeb9e 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -1542,6 +1542,12 @@ static const struct sun4i_tcon_quirks 
sun9i_a80_tcon_tv_quirks = {
.needs_edp_reset = true,
 };
 
+static const struct sun4i_tcon_quirks sun20i_d1_lcd_quirks = {
+   .has_channel_0  = true,
+   .dclk_min_div   = 1,
+   .set_mux= sun8i_r40_tcon_tv_set_mux,
+};
+
 /* sun4i_drv uses this list to check if a device node is a TCON */
 const struct of_device_id sun4i_tcon_of_table[] = {
{ .compatible = "allwinner,sun4i-a10-tcon", .data = _a10_quirks },
@@ -1559,6 +1565,8 @@ const struct of_device_id sun4i_tcon_of_table[] = {
{ .compatible = "allwinner,sun8i-v3s-tcon", .data = _v3s_quirks },
{ .compatible = "allwinner,sun9i-a80-tcon-lcd", .data = 
_a80_tcon_lcd_quirks },
{ .compatible = "allwinner,sun9i-a80-tcon-tv", .data = 
_a80_tcon_tv_quirks },
+   { .compatible = "allwinner,sun20i-d1-tcon-lcd", .data = 
_d1_lcd_quirks },
+   { .compatible = "allwinner,sun20i-d1-tcon-tv", .data = 
_r40_tv_quirks },
{ }
 };
 MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
-- 
2.35.1



[PATCH v2 12/14] drm/sun4i: Add support for D1 TCON TOP

2022-04-11 Thread Samuel Holland
D1 has a TCON TOP with TCON TV0 and DSI, but no TCON TV1. This puts the
DSI clock name at index 1 in clock-output-names. Support this by only
incrementing the index for clocks that are actually supported.

Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun8i_tcon_top.c | 15 ---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c 
b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c
index 1b9b8b48f4a7..da97682b6835 100644
--- a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c
+++ b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c
@@ -189,22 +189,23 @@ static int sun8i_tcon_top_bind(struct device *dev, struct 
device *master,
 * if TVE is active on each TCON TV. If it is, mux should be switched
 * to TVE clock parent.
 */
+   i = 0;
clk_data->hws[CLK_TCON_TOP_TV0] =
sun8i_tcon_top_register_gate(dev, "tcon-tv0", regs,
 _top->reg_lock,
-TCON_TOP_TCON_TV0_GATE, 0);
+TCON_TOP_TCON_TV0_GATE, i++);
 
if (quirks->has_tcon_tv1)
clk_data->hws[CLK_TCON_TOP_TV1] =
sun8i_tcon_top_register_gate(dev, "tcon-tv1", regs,
 _top->reg_lock,
-TCON_TOP_TCON_TV1_GATE, 1);
+TCON_TOP_TCON_TV1_GATE, 
i++);
 
if (quirks->has_dsi)
clk_data->hws[CLK_TCON_TOP_DSI] =
sun8i_tcon_top_register_gate(dev, "dsi", regs,
 _top->reg_lock,
-TCON_TOP_TCON_DSI_GATE, 2);
+TCON_TOP_TCON_DSI_GATE, 
i++);
 
for (i = 0; i < CLK_NUM; i++)
if (IS_ERR(clk_data->hws[i])) {
@@ -272,6 +273,10 @@ static const struct sun8i_tcon_top_quirks 
sun8i_r40_tcon_top_quirks = {
.has_dsi= true,
 };
 
+static const struct sun8i_tcon_top_quirks sun20i_d1_tcon_top_quirks = {
+   .has_dsi= true,
+};
+
 static const struct sun8i_tcon_top_quirks sun50i_h6_tcon_top_quirks = {
/* Nothing special */
 };
@@ -282,6 +287,10 @@ const struct of_device_id sun8i_tcon_top_of_table[] = {
.compatible = "allwinner,sun8i-r40-tcon-top",
.data = _r40_tcon_top_quirks
},
+   {
+   .compatible = "allwinner,sun20i-d1-tcon-top",
+   .data = _d1_tcon_top_quirks
+   },
{
.compatible = "allwinner,sun50i-h6-tcon-top",
.data = _h6_tcon_top_quirks
-- 
2.35.1



[PATCH v2 11/14] drm/sun4i: Add support for D1 mixers

2022-04-11 Thread Samuel Holland
D1 has a display engine with the usual pair of mixers, albeit with
relatively few layers. In fact, D1 appears to be the first SoC to have
a mixer without any UI layers. Add support for these new variants.

Acked-by: Jernej Skrabec 
Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun8i_mixer.c | 26 ++
 1 file changed, 26 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c 
b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 4ce593c99807..875a1156c04e 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -615,6 +615,24 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
.mod_rate = 15000,
 };
 
+static const struct sun8i_mixer_cfg sun20i_d1_mixer0_cfg = {
+   .ccsc   = CCSC_D1_MIXER0_LAYOUT,
+   .mod_rate   = 29700,
+   .scaler_mask= 0x3,
+   .scanline_yuv   = 2048,
+   .ui_num = 1,
+   .vi_num = 1,
+};
+
+static const struct sun8i_mixer_cfg sun20i_d1_mixer1_cfg = {
+   .ccsc   = CCSC_MIXER1_LAYOUT,
+   .mod_rate   = 29700,
+   .scaler_mask= 0x1,
+   .scanline_yuv   = 1024,
+   .ui_num = 0,
+   .vi_num = 1,
+};
+
 static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = {
.ccsc   = CCSC_MIXER0_LAYOUT,
.mod_rate   = 29700,
@@ -668,6 +686,14 @@ static const struct of_device_id sun8i_mixer_of_table[] = {
.compatible = "allwinner,sun8i-v3s-de2-mixer",
.data = _v3s_mixer_cfg,
},
+   {
+   .compatible = "allwinner,sun20i-d1-de2-mixer-0",
+   .data = _d1_mixer0_cfg,
+   },
+   {
+   .compatible = "allwinner,sun20i-d1-de2-mixer-1",
+   .data = _d1_mixer1_cfg,
+   },
{
.compatible = "allwinner,sun50i-a64-de2-mixer-0",
.data = _a64_mixer0_cfg,
-- 
2.35.1



[PATCH v2 10/14] drm/sun4i: csc: Add support for the new MMIO layout

2022-04-11 Thread Samuel Holland
D1 changes the MMIO offsets for the CSC blocks in the first mixer. The
mixers' ccsc property is used as an index into the ccsc_base array. Use
an enumeration to describe this index, and add the new set of offsets.

Signed-off-by: Samuel Holland 
---

Changes in v2:
 - Use an enumeration for the ccsc value.

 drivers/gpu/drm/sun4i/sun8i_csc.c   |  7 ---
 drivers/gpu/drm/sun4i/sun8i_csc.h   |  1 +
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 18 +-
 drivers/gpu/drm/sun4i/sun8i_mixer.h | 14 ++
 4 files changed, 24 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c 
b/drivers/gpu/drm/sun4i/sun8i_csc.c
index 9bd62de0c288..58480d8e4f70 100644
--- a/drivers/gpu/drm/sun4i/sun8i_csc.c
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.c
@@ -8,9 +8,10 @@
 #include "sun8i_csc.h"
 #include "sun8i_mixer.h"
 
-static const u32 ccsc_base[2][2] = {
-   {CCSC00_OFFSET, CCSC01_OFFSET},
-   {CCSC10_OFFSET, CCSC11_OFFSET},
+static const u32 ccsc_base[][2] = {
+   [CCSC_MIXER0_LAYOUT]= {CCSC00_OFFSET, CCSC01_OFFSET},
+   [CCSC_MIXER1_LAYOUT]= {CCSC10_OFFSET, CCSC11_OFFSET},
+   [CCSC_D1_MIXER0_LAYOUT] = {CCSC00_OFFSET, CCSC01_D1_OFFSET},
 };
 
 /*
diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.h 
b/drivers/gpu/drm/sun4i/sun8i_csc.h
index 022cafa6c06c..828b86fd0cab 100644
--- a/drivers/gpu/drm/sun4i/sun8i_csc.h
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.h
@@ -13,6 +13,7 @@ struct sun8i_mixer;
 /* VI channel CSC units offsets */
 #define CCSC00_OFFSET 0xAA050
 #define CCSC01_OFFSET 0xFA050
+#define CCSC01_D1_OFFSET 0xFA000
 #define CCSC10_OFFSET 0xA
 #define CCSC11_OFFSET 0xF
 
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c 
b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 6b1711a9a71f..4ce593c99807 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -564,7 +564,7 @@ static int sun8i_mixer_remove(struct platform_device *pdev)
 }
 
 static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
-   .ccsc   = 0,
+   .ccsc   = CCSC_MIXER0_LAYOUT,
.scaler_mask= 0xf,
.scanline_yuv   = 2048,
.ui_num = 3,
@@ -572,7 +572,7 @@ static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = 
{
 };
 
 static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = {
-   .ccsc   = 1,
+   .ccsc   = CCSC_MIXER1_LAYOUT,
.scaler_mask= 0x3,
.scanline_yuv   = 2048,
.ui_num = 1,
@@ -580,7 +580,7 @@ static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = 
{
 };
 
 static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
-   .ccsc   = 0,
+   .ccsc   = CCSC_MIXER0_LAYOUT,
.mod_rate   = 43200,
.scaler_mask= 0xf,
.scanline_yuv   = 2048,
@@ -589,7 +589,7 @@ static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
 };
 
 static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
-   .ccsc   = 0,
+   .ccsc   = CCSC_MIXER0_LAYOUT,
.mod_rate   = 29700,
.scaler_mask= 0xf,
.scanline_yuv   = 2048,
@@ -598,7 +598,7 @@ static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
 };
 
 static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = {
-   .ccsc   = 1,
+   .ccsc   = CCSC_MIXER1_LAYOUT,
.mod_rate   = 29700,
.scaler_mask= 0x3,
.scanline_yuv   = 2048,
@@ -611,12 +611,12 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = 
{
.ui_num = 1,
.scaler_mask = 0x3,
.scanline_yuv = 2048,
-   .ccsc = 0,
+   .ccsc = CCSC_MIXER0_LAYOUT,
.mod_rate = 15000,
 };
 
 static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = {
-   .ccsc   = 0,
+   .ccsc   = CCSC_MIXER0_LAYOUT,
.mod_rate   = 29700,
.scaler_mask= 0xf,
.scanline_yuv   = 4096,
@@ -625,7 +625,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = 
{
 };
 
 static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = {
-   .ccsc   = 1,
+   .ccsc   = CCSC_MIXER1_LAYOUT,
.mod_rate   = 29700,
.scaler_mask= 0x3,
.scanline_yuv   = 2048,
@@ -634,7 +634,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = 
{
 };
 
 static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = {
-   .ccsc   = 0,
+   .ccsc   = CCSC_MIXER0_LAYOUT,
.is_de3 = true,
.mod_rate   = 6,
.scaler_mask= 0xf,
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h 
b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index 5b3fbee18671..85c94884fb9a 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -141,6 +141,15 @@
 #define SUN50I_MIXER_CDC0_EN   0xd
 #define SUN50I_MIXER_CDC1_EN   0xd8000
 
+enum {
+ 

[PATCH v2 09/14] drm/sun4i: Allow VI layers to be primary planes

2022-04-11 Thread Samuel Holland
D1's mixer 1 has no UI layers, only a single VI layer. That means the
mixer can only be used if the primary plane comes from this VI layer.
Add the code to handle this case.

Signed-off-by: Samuel Holland 
---

Changes in v2:
 - Use Jernej's patches for mixer mode setting.

 drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c 
b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
index bb7c43036dfa..f7d0b082d634 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -542,6 +542,7 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct 
drm_device *drm,
   struct sun8i_mixer *mixer,
   int index)
 {
+   enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
u32 supported_encodings, supported_ranges;
unsigned int plane_cnt, format_count;
struct sun8i_vi_layer *layer;
@@ -560,12 +561,15 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct 
drm_device *drm,
format_count = ARRAY_SIZE(sun8i_vi_layer_formats);
}
 
+   if (!mixer->cfg->ui_num && index == 0)
+   type = DRM_PLANE_TYPE_PRIMARY;
+
/* possible crtcs are set later */
ret = drm_universal_plane_init(drm, >plane, 0,
   _vi_layer_funcs,
   formats, format_count,
   sun8i_layer_modifiers,
-  DRM_PLANE_TYPE_OVERLAY, NULL);
+  type, NULL);
if (ret) {
dev_err(drm->dev, "Couldn't initialize layer\n");
return ERR_PTR(ret);
-- 
2.35.1



[PATCH v2 08/14] sun4i/drm: sun8i: use mode_set engine callback

2022-04-11 Thread Samuel Holland
From: Jernej Skrabec 

Newly introduced mode_set callback in engine structure is a much better
place for setting mixer output size and interlace mode for the following
reasons:
1. Aforementioned properties change only when mode changes, so it's
   enough to be set only once per mode set. Currently it's done whenever
   properties of primary plane are changed.
2. It's assumed that primary plane will always cover whole screen. While
   this is true most of the time, it's not always. DE2/3 planes are
   universal and mostly equal in functionality. There is no reason to
   add artificial limitation to primary planes.
3. The current code only works for UI layers, but some mixers do not
   have any UI layers.

Signed-off-by: Jernej Skrabec 
[Samuel: update commit message]
Signed-off-by: Samuel Holland 
---

Changes in v2:
 - Use Jernej's patches for mixer mode setting.

 drivers/gpu/drm/sun4i/sun8i_mixer.c| 30 ++
 drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 30 --
 2 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c 
b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index f5e8aeaa3cdf..6b1711a9a71f 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -298,9 +298,39 @@ static struct drm_plane **sun8i_layers_init(struct 
drm_device *drm,
return planes;
 }
 
+static void sun8i_mixer_mode_set(struct sunxi_engine *engine,
+const struct drm_display_mode *mode)
+{
+   struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
+   u32 bld_base, size, val;
+   bool interlaced;
+
+   bld_base = sun8i_blender_base(mixer);
+   interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+   size = SUN8I_MIXER_SIZE(mode->hdisplay, mode->vdisplay);
+
+   DRM_DEBUG_DRIVER("Updating global size W: %u H: %u\n",
+mode->hdisplay, mode->vdisplay);
+
+   regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_SIZE, size);
+   regmap_write(engine->regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size);
+
+   if (interlaced)
+   val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
+   else
+   val = 0;
+
+   regmap_update_bits(engine->regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base),
+  SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, val);
+
+   DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
+interlaced ? "on" : "off");
+}
+
 static const struct sunxi_engine_ops sun8i_engine_ops = {
.commit = sun8i_mixer_commit,
.layers_init= sun8i_layers_init,
+   .mode_set   = sun8i_mixer_mode_set,
 };
 
 static const struct regmap_config sun8i_mixer_regmap_config = {
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c 
b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
index 7845c2a53a7f..4632dea2dc1e 100644
--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
@@ -120,36 +120,6 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer 
*mixer, int channel,
insize = SUN8I_MIXER_SIZE(src_w, src_h);
outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
 
-   if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
-   bool interlaced = false;
-   u32 val;
-
-   DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: 
%u\n",
-dst_w, dst_h);
-   regmap_write(mixer->engine.regs,
-SUN8I_MIXER_GLOBAL_SIZE,
-outsize);
-   regmap_write(mixer->engine.regs,
-SUN8I_MIXER_BLEND_OUTSIZE(bld_base), outsize);
-
-   if (state->crtc)
-   interlaced = state->crtc->state->adjusted_mode.flags
-   & DRM_MODE_FLAG_INTERLACE;
-
-   if (interlaced)
-   val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
-   else
-   val = 0;
-
-   regmap_update_bits(mixer->engine.regs,
-  SUN8I_MIXER_BLEND_OUTCTL(bld_base),
-  SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
-  val);
-
-   DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
-interlaced ? "on" : "off");
-   }
-
/* Set height and width */
DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
 state->src.x1 >> 16, state->src.y1 >> 16);
-- 
2.35.1



[PATCH v2 07/14] sun4i/drm: backend: use mode_set engine callback

2022-04-11 Thread Samuel Holland
From: Jernej Skrabec 

Newly introduced mode_set callback in engine structure is a much better
place for setting backend output size and interlace mode for following
reasons:
1. Aforementioned properties change only when mode changes, so it's
   enough to be set only once per mode set. Currently it's done whenever
   properties of primary plane are changed.
2. It's assumed that primary plane will always cover whole screen. While
   this is true most of the time, it's not always. Planes are universal.
   There is no reason to add artificial limitation to primary plane.

Signed-off-by: Jernej Skrabec 
[Samuel: drop unused 'interlaced' variable]
Signed-off-by: Samuel Holland 
---

Changes in v2:
 - Use Jernej's patches for mixer mode setting.

 drivers/gpu/drm/sun4i/sun4i_backend.c | 40 +--
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c 
b/drivers/gpu/drm/sun4i/sun4i_backend.c
index f52ff4e6c662..decd95ad519d 100644
--- a/drivers/gpu/drm/sun4i/sun4i_backend.c
+++ b/drivers/gpu/drm/sun4i/sun4i_backend.c
@@ -172,14 +172,6 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend 
*backend,
 
DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
 
-   if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
-   DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: 
%u\n",
-state->crtc_w, state->crtc_h);
-   regmap_write(backend->engine.regs, SUN4I_BACKEND_DISSIZE_REG,
-SUN4I_BACKEND_DISSIZE(state->crtc_w,
-  state->crtc_h));
-   }
-
/* Set height and width */
DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
 state->crtc_w, state->crtc_h);
@@ -259,7 +251,6 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend 
*backend,
 {
struct drm_plane_state *state = plane->state;
struct drm_framebuffer *fb = state->fb;
-   bool interlaced = false;
u32 val;
int ret;
 
@@ -267,17 +258,6 @@ int sun4i_backend_update_layer_formats(struct 
sun4i_backend *backend,
regmap_update_bits(backend->engine.regs, 
SUN4I_BACKEND_ATTCTL_REG0(layer),
   SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN, 0);
 
-   if (plane->state->crtc)
-   interlaced = plane->state->crtc->state->adjusted_mode.flags
-   & DRM_MODE_FLAG_INTERLACE;
-
-   regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG,
-  SUN4I_BACKEND_MODCTL_ITLMOD_EN,
-  interlaced ? SUN4I_BACKEND_MODCTL_ITLMOD_EN : 0);
-
-   DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n",
-interlaced ? "on" : "off");
-
val = SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA(state->alpha >> 8);
if (state->alpha != DRM_BLEND_ALPHA_OPAQUE)
val |= SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA_EN;
@@ -654,6 +634,25 @@ static void sun4i_backend_vblank_quirk(struct sunxi_engine 
*engine)
spin_unlock(>frontend_lock);
 };
 
+static void sun4i_backend_mode_set(struct sunxi_engine *engine,
+  const struct drm_display_mode *mode)
+{
+   bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+
+   DRM_DEBUG_DRIVER("Updating global size W: %u H: %u\n",
+mode->hdisplay, mode->vdisplay);
+
+   regmap_write(engine->regs, SUN4I_BACKEND_DISSIZE_REG,
+SUN4I_BACKEND_DISSIZE(mode->hdisplay, mode->vdisplay));
+
+   regmap_update_bits(engine->regs, SUN4I_BACKEND_MODCTL_REG,
+  SUN4I_BACKEND_MODCTL_ITLMOD_EN,
+  interlaced ? SUN4I_BACKEND_MODCTL_ITLMOD_EN : 0);
+
+   DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n",
+interlaced ? "on" : "off");
+}
+
 static int sun4i_backend_init_sat(struct device *dev) {
struct sun4i_backend *backend = dev_get_drvdata(dev);
int ret;
@@ -765,6 +764,7 @@ static const struct sunxi_engine_ops 
sun4i_backend_engine_ops = {
.apply_color_correction = sun4i_backend_apply_color_correction,
.disable_color_correction   = 
sun4i_backend_disable_color_correction,
.vblank_quirk   = sun4i_backend_vblank_quirk,
+   .mode_set   = sun4i_backend_mode_set,
 };
 
 static const struct regmap_config sun4i_backend_regmap_config = {
-- 
2.35.1



[PATCH v2 06/14] sun4i/drm: engine: Add mode_set callback

2022-04-11 Thread Samuel Holland
From: Jernej Skrabec 

This optional callback is useful for setting properties which depends
only on current mode. Such properties are width, height and interlaced
output.

These properties are currently set in update layer callback for primary
plane which is less than ideal. More about that in follow up patches,
which will migrate that code to this newly defined callback.

Signed-off-by: Jernej Skrabec 
Signed-off-by: Samuel Holland 
---

Changes in v2:
 - Use Jernej's patches for mixer mode setting.

 drivers/gpu/drm/sun4i/sun4i_crtc.c   |  1 +
 drivers/gpu/drm/sun4i/sunxi_engine.h | 27 +++
 2 files changed, 28 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c 
b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index 45d9eb552d86..c06d7cd45388 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -146,6 +146,7 @@ static void sun4i_crtc_mode_set_nofb(struct drm_crtc *crtc)
struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
 
sun4i_tcon_mode_set(scrtc->tcon, encoder, mode);
+   sunxi_engine_mode_set(scrtc->engine, mode);
 }
 
 static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = {
diff --git a/drivers/gpu/drm/sun4i/sunxi_engine.h 
b/drivers/gpu/drm/sun4i/sunxi_engine.h
index 548710a936d5..ec8cf9b2bda4 100644
--- a/drivers/gpu/drm/sun4i/sunxi_engine.h
+++ b/drivers/gpu/drm/sun4i/sunxi_engine.h
@@ -9,6 +9,7 @@
 struct drm_plane;
 struct drm_device;
 struct drm_crtc_state;
+struct drm_display_mode;
 
 struct sunxi_engine;
 
@@ -108,6 +109,17 @@ struct sunxi_engine_ops {
 * This function is optional.
 */
void (*vblank_quirk)(struct sunxi_engine *engine);
+
+   /**
+* @mode_set
+*
+* This callback is used to set mode related parameters
+* like interlacing, screen size, etc. once per mode set.
+*
+* This function is optional.
+*/
+   void (*mode_set)(struct sunxi_engine *engine,
+const struct drm_display_mode *mode);
 };
 
 /**
@@ -181,4 +193,19 @@ sunxi_engine_disable_color_correction(struct sunxi_engine 
*engine)
if (engine->ops && engine->ops->disable_color_correction)
engine->ops->disable_color_correction(engine);
 }
+
+/**
+ * sunxi_engine_mode_set - Inform engine of a new mode
+ * @engine:pointer to the engine
+ * @mode:  new mode
+ *
+ * Engine can use this functionality to set specifics once per mode change.
+ */
+static inline void
+sunxi_engine_mode_set(struct sunxi_engine *engine,
+ const struct drm_display_mode *mode)
+{
+   if (engine->ops && engine->ops->mode_set)
+   engine->ops->mode_set(engine, mode);
+}
 #endif /* _SUNXI_ENGINE_H_ */
-- 
2.35.1



[PATCH v2 04/14] drm/sun4i: hdmi: Use more portable I/O helpers

2022-04-11 Thread Samuel Holland
readsb/writesb are unavailable on some architectures. In preparation for
removing the Kconfig architecture dependency, switch to the equivalent
but more portable ioread/write8_rep helpers.

Reported-by: kernel test robot 
Signed-off-by: Samuel Holland 
---

Changes in v2:
 - New patch: I/O helper portability

 drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c 
b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c
index b66fa27fe6ea..c7d7e9fff91c 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c
@@ -56,9 +56,9 @@ static int fifo_transfer(struct sun4i_hdmi *hdmi, u8 *buf, 
int len, bool read)
return -EIO;
 
if (read)
-   readsb(hdmi->base + hdmi->variant->ddc_fifo_reg, buf, len);
+   ioread8_rep(hdmi->base + hdmi->variant->ddc_fifo_reg, buf, len);
else
-   writesb(hdmi->base + hdmi->variant->ddc_fifo_reg, buf, len);
+   iowrite8_rep(hdmi->base + hdmi->variant->ddc_fifo_reg, buf, 
len);
 
/* Clear FIFO request bit by forcing a write to that bit */
regmap_field_force_write(hdmi->field_ddc_int_status,
-- 
2.35.1



[PATCH v2 05/14] drm/sun4i: Allow building the driver on RISC-V

2022-04-11 Thread Samuel Holland
Allwinner D1 is a RISC-V SoC which contains a DE 2.0 engine. Let's
remove the dependency on a specific CPU architecture, so the driver can
be built wherever ARCH_SUNXI is selected.

Acked-by: Jernej Skrabec 
Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index befc5a80222d..3a43c436c74a 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config DRM_SUN4I
tristate "DRM Support for Allwinner A10 Display Engine"
-   depends on DRM && (ARM || ARM64) && COMMON_CLK
+   depends on DRM && COMMON_CLK
depends on ARCH_SUNXI || COMPILE_TEST
select DRM_GEM_CMA_HELPER
select DRM_KMS_HELPER
-- 
2.35.1



[PATCH v2 03/14] drm/sun4i: Remove obsolete references to PHYS_OFFSET

2022-04-11 Thread Samuel Holland
commit b4bdc4fbf8d0 ("soc: sunxi: Deal with the MBUS DMA offsets in a
central place") added a platform device notifier that sets the DMA
offset for all of the display engine frontend and backend devices.

The code applying the offset to DMA buffer physical addresses was then
removed from the backend driver in commit 756668ba682e ("drm/sun4i:
backend: Remove the MBUS quirks"), but the code subtracting PHYS_OFFSET
was left in the frontend driver.

As a result, the offset was applied twice in the frontend driver. This
likely went unnoticed because it only affects specific configurations
(scaling or certain pixel formats) where the frontend is used, on boards
with both one of these older SoCs and more than 1 GB of DRAM.

In addition, the references to PHYS_OFFSET prevent compiling the driver
on architectures where PHYS_OFFSET is not defined.

Fixes: b4bdc4fbf8d0 ("soc: sunxi: Deal with the MBUS DMA offsets in a central 
place")
Reviewed-by: Jernej Skrabec 
Signed-off-by: Samuel Holland 
---

(no changes since v1)

 drivers/gpu/drm/sun4i/sun4i_frontend.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.c 
b/drivers/gpu/drm/sun4i/sun4i_frontend.c
index 56ae38389db0..462fae73eae9 100644
--- a/drivers/gpu/drm/sun4i/sun4i_frontend.c
+++ b/drivers/gpu/drm/sun4i/sun4i_frontend.c
@@ -222,13 +222,11 @@ void sun4i_frontend_update_buffer(struct sun4i_frontend 
*frontend,
 
/* Set the physical address of the buffer in memory */
paddr = drm_fb_cma_get_gem_addr(fb, state, 0);
-   paddr -= PHYS_OFFSET;
DRM_DEBUG_DRIVER("Setting buffer #0 address to %pad\n", );
regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR0_REG, paddr);
 
if (fb->format->num_planes > 1) {
paddr = drm_fb_cma_get_gem_addr(fb, state, swap ? 2 : 1);
-   paddr -= PHYS_OFFSET;
DRM_DEBUG_DRIVER("Setting buffer #1 address to %pad\n", );
regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR1_REG,
 paddr);
@@ -236,7 +234,6 @@ void sun4i_frontend_update_buffer(struct sun4i_frontend 
*frontend,
 
if (fb->format->num_planes > 2) {
paddr = drm_fb_cma_get_gem_addr(fb, state, swap ? 1 : 2);
-   paddr -= PHYS_OFFSET;
DRM_DEBUG_DRIVER("Setting buffer #2 address to %pad\n", );
regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR2_REG,
 paddr);
-- 
2.35.1



[PATCH v2 02/14] dt-bindings: display: Add D1 display engine compatibles

2022-04-11 Thread Samuel Holland
Allwinner D1 contains a display engine 2.0. It features two mixers, a
TCON TOP (with DSI and HDMI), one TCON LCD, and one TCON TV.

Reviewed-by: Krzysztof Kozlowski 
Signed-off-by: Samuel Holland 
---

(no changes since v1)

 .../allwinner,sun4i-a10-display-engine.yaml   |  1 +
 .../display/allwinner,sun4i-a10-tcon.yaml |  2 ++
 .../allwinner,sun8i-a83t-de2-mixer.yaml   |  2 ++
 .../display/allwinner,sun8i-r40-tcon-top.yaml | 34 +++
 4 files changed, 39 insertions(+)

diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-engine.yaml
 
b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-engine.yaml
index d4412aea7b73..c388ae5da1e4 100644
--- 
a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-engine.yaml
+++ 
b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-engine.yaml
@@ -62,6 +62,7 @@ properties:
   - allwinner,sun8i-r40-display-engine
   - allwinner,sun8i-v3s-display-engine
   - allwinner,sun9i-a80-display-engine
+  - allwinner,sun20i-d1-display-engine
   - allwinner,sun50i-a64-display-engine
   - allwinner,sun50i-h6-display-engine
 
diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml 
b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml
index 3a7d5d731712..4a92a4c7dcd7 100644
--- a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml
+++ b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml
@@ -33,6 +33,8 @@ properties:
   - const: allwinner,sun8i-v3s-tcon
   - const: allwinner,sun9i-a80-tcon-lcd
   - const: allwinner,sun9i-a80-tcon-tv
+  - const: allwinner,sun20i-d1-tcon-lcd
+  - const: allwinner,sun20i-d1-tcon-tv
 
   - items:
   - enum:
diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml 
b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml
index 4f91eec26de9..cb243bc58ef7 100644
--- 
a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml
+++ 
b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml
@@ -19,6 +19,8 @@ properties:
   - allwinner,sun8i-r40-de2-mixer-0
   - allwinner,sun8i-r40-de2-mixer-1
   - allwinner,sun8i-v3s-de2-mixer
+  - allwinner,sun20i-d1-de2-mixer-0
+  - allwinner,sun20i-d1-de2-mixer-1
   - allwinner,sun50i-a64-de2-mixer-0
   - allwinner,sun50i-a64-de2-mixer-1
   - allwinner,sun50i-h6-de3-mixer-0
diff --git 
a/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml 
b/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml
index 784b267635fb..cc32e2faed91 100644
--- 
a/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml
+++ 
b/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml
@@ -41,6 +41,7 @@ properties:
   compatible:
 enum:
   - allwinner,sun8i-r40-tcon-top
+  - allwinner,sun20i-d1-tcon-top
   - allwinner,sun50i-h6-tcon-top
 
   reg:
@@ -154,6 +155,39 @@ allOf:
 - port@2
 - port@3
 
+  - if:
+  properties:
+compatible:
+  contains:
+const: allwinner,sun20i-d1-tcon-top
+
+then:
+  properties:
+clocks:
+  minItems: 4
+  maxItems: 4
+  items:
+- description: The TCON TOP interface clock
+- description: The TCON TOP TV0 clock
+- description: The TCON TOP TVE0 clock
+- description: The TCON TOP MIPI DSI clock
+
+clock-names:
+  minItems: 4
+  maxItems: 4
+  items:
+- const: bus
+- const: tcon-tv0
+- const: tve0
+- const: dsi
+
+clock-output-names:
+  minItems: 2
+  maxItems: 2
+  items:
+- description: TCON TV0 output clock name
+- description: DSI output clock name
+
   - if:
   properties:
 compatible:
-- 
2.35.1



  1   2   >