* Re: Re: [PATCH] HID: uclogic: Remove useless loop
From: Stefan Berzl @ 2024-04-18 13:31 UTC (permalink / raw)
To: Jiri Kosina; +Cc: linux-input, linux-kernel, Nikolai Kondrashov
In-Reply-To: <nycvar.YFH.7.76.2404121751250.5680@cbobk.fhfr.pm>
On 12/04/2024 17:52, Jiri Kosina wrote:
> On Mon, 1 Apr 2024, Stefan Berzl wrote:
>
>> The while in question does nothing except provide the possibility
>> to have an infinite loop in case the subreport id is actually the same
>> as the pen id.
>>
>> Signed-off-by: Stefan Berzl <stefanberzl@gmail.com>
>
> Let me CC Nicolai, the author of the code of question (8b013098be2c9).
I agree that Nicolai's opinion would be invaluable, but even without it,
the patch is trivially correct. If we have a subreport that matches the
packet, we change the report_id accordingly. If we then loop back to the
beginning, either the report_id is different or we are caught in an
infinite loop. None of these are hardware registers where the access
itself would matter.
>> ---
>> drivers/hid/hid-uclogic-core.c | 55 ++++++++++++++++------------------
>> 1 file changed, 25 insertions(+), 30 deletions(-)
>>
>> diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c
>> index ad74cbc9a0aa..a56f4de216de 100644
>> --- a/drivers/hid/hid-uclogic-core.c
>> +++ b/drivers/hid/hid-uclogic-core.c
>> @@ -431,40 +431,35 @@ static int uclogic_raw_event(struct hid_device *hdev,
>> if (uclogic_exec_event_hook(params, data, size))
>> return 0;
>>
>> - while (true) {
>> - /* Tweak pen reports, if necessary */
>> - if ((report_id == params->pen.id) && (size >= 2)) {
>> - subreport_list_end =
>> - params->pen.subreport_list +
>> - ARRAY_SIZE(params->pen.subreport_list);
>> - /* Try to match a subreport */
>> - for (subreport = params->pen.subreport_list;
>> - subreport < subreport_list_end; subreport++) {
>> - if (subreport->value != 0 &&
>> - subreport->value == data[1]) {
>> - break;
>> - }
>> - }
>> - /* If a subreport matched */
>> - if (subreport < subreport_list_end) {
>> - /* Change to subreport ID, and restart */
>> - report_id = data[0] = subreport->id;
>> - continue;
>> - } else {
>> - return uclogic_raw_event_pen(drvdata, data, size);
>> + /* Tweak pen reports, if necessary */
>> + if ((report_id == params->pen.id) && (size >= 2)) {
>> + subreport_list_end =
>> + params->pen.subreport_list +
>> + ARRAY_SIZE(params->pen.subreport_list);
>> + /* Try to match a subreport */
>> + for (subreport = params->pen.subreport_list;
>> + subreport < subreport_list_end; subreport++) {
>> + if (subreport->value != 0 &&
>> + subreport->value == data[1]) {
>> + break;
>> }
>> }
>> -
>> - /* Tweak frame control reports, if necessary */
>> - for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {
>> - if (report_id == params->frame_list[i].id) {
>> - return uclogic_raw_event_frame(
>> - drvdata, ¶ms->frame_list[i],
>> - data, size);
>> - }
>> + /* If a subreport matched */
>> + if (subreport < subreport_list_end) {
>> + /* Change to subreport ID, and restart */
>> + report_id = data[0] = subreport->id;
>> + } else {
>> + return uclogic_raw_event_pen(drvdata, data, size);
>> }
>> + }
>>
>> - break;
>> + /* Tweak frame control reports, if necessary */
>> + for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {
>> + if (report_id == params->frame_list[i].id) {
>> + return uclogic_raw_event_frame(
>> + drvdata, ¶ms->frame_list[i],
>> + data, size);
>> + }
>> }
>>
>> return 0;
>> --
>> 2.43.0
>>
>
^ permalink raw reply
* Re: [PATCH] HID: uclogic: Remove useless loop
From: Nikolai Kondrashov @ 2024-04-18 17:04 UTC (permalink / raw)
To: Stefan Berzl, Jiri Kosina; +Cc: linux-input, linux-kernel
In-Reply-To: <4ae4be2f-4edd-4d1e-87e9-df5687627d00@gmail.com>
Hi Jiri, Stefan,
On 4/18/24 4:31 PM, Stefan Berzl wrote:
>
> On 12/04/2024 17:52, Jiri Kosina wrote:
>> On Mon, 1 Apr 2024, Stefan Berzl wrote:
>>
>>> The while in question does nothing except provide the possibility
>>> to have an infinite loop in case the subreport id is actually the same
>>> as the pen id.
>>>
>>> Signed-off-by: Stefan Berzl <stefanberzl@gmail.com>
>>
>> Let me CC Nicolai, the author of the code of question (8b013098be2c9).
>
> I agree that Nicolai's opinion would be invaluable, but even without it,
> the patch is trivially correct. If we have a subreport that matches the
> packet, we change the report_id accordingly. If we then loop back to the
> beginning, either the report_id is different or we are caught in an
> infinite loop. None of these are hardware registers where the access
> itself would matter.
Yes, Stefan is right. I was trying to implement general rewrite logic, and if
we really had that, then the fix would need to be checking that the new ID is
different. As such there's really no need, and Stefan's fix is fine.
Only perhaps amend that comment to something like
/* Change to the (non-pen) subreport ID, and continue */
Or at least remove ", and restart".
Thank you, Stefan and Jiri!
Nick
^ permalink raw reply
* Re: [PATCH] HID: uclogic: Remove useless loop
From: Stefan Berzl @ 2024-04-18 18:46 UTC (permalink / raw)
To: Nikolai Kondrashov, Jiri Kosina; +Cc: linux-input, linux-kernel
In-Reply-To: <f2429c78-9189-410d-9c6a-644ae8a4d12c@gmail.com>
Hi!
On 18/04/2024 19:04, Nikolai Kondrashov wrote:
> Hi Jiri, Stefan,
>
> On 4/18/24 4:31 PM, Stefan Berzl wrote:
>>
>> On 12/04/2024 17:52, Jiri Kosina wrote:
>>> On Mon, 1 Apr 2024, Stefan Berzl wrote:
>>>
>>>> The while in question does nothing except provide the possibility
>>>> to have an infinite loop in case the subreport id is actually the same
>>>> as the pen id.
>>>>
>>>> Signed-off-by: Stefan Berzl <stefanberzl@gmail.com>
>>>
>>> Let me CC Nicolai, the author of the code of question (8b013098be2c9).
>>
>> I agree that Nicolai's opinion would be invaluable, but even without it,
>> the patch is trivially correct. If we have a subreport that matches the
>> packet, we change the report_id accordingly. If we then loop back to the
>> beginning, either the report_id is different or we are caught in an
>> infinite loop. None of these are hardware registers where the access
>> itself would matter.
>
> Yes, Stefan is right. I was trying to implement general rewrite logic, and if
> we really had that, then the fix would need to be checking that the new ID is
> different. As such there's really no need, and Stefan's fix is fine.
>
> Only perhaps amend that comment to something like
>
> /* Change to the (non-pen) subreport ID, and continue */
>
> Or at least remove ", and restart".
>
Will do! I'll send a v2 with the comment updated.
Regards
^ permalink raw reply
* [PATCH v2] HID: uclogic: Remove useless loop
From: Stefan Berzl @ 2024-04-18 19:40 UTC (permalink / raw)
To: jikos, spbnick; +Cc: linux-input, linux-kernel
The while in question does nothing except provide the possibility
to have an infinite loop in case the subreport id is actually the same
as the pen id.
Signed-off-by: Stefan Berzl <stefanberzl@gmail.com>
---
drivers/hid/hid-uclogic-core.c | 55 ++++++++++++++++------------------
1 file changed, 25 insertions(+), 30 deletions(-)
diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c
index ad74cbc9a0aa..8219d3dc9de4 100644
--- a/drivers/hid/hid-uclogic-core.c
+++ b/drivers/hid/hid-uclogic-core.c
@@ -431,40 +431,35 @@ static int uclogic_raw_event(struct hid_device *hdev,
if (uclogic_exec_event_hook(params, data, size))
return 0;
- while (true) {
- /* Tweak pen reports, if necessary */
- if ((report_id == params->pen.id) && (size >= 2)) {
- subreport_list_end =
- params->pen.subreport_list +
- ARRAY_SIZE(params->pen.subreport_list);
- /* Try to match a subreport */
- for (subreport = params->pen.subreport_list;
- subreport < subreport_list_end; subreport++) {
- if (subreport->value != 0 &&
- subreport->value == data[1]) {
- break;
- }
- }
- /* If a subreport matched */
- if (subreport < subreport_list_end) {
- /* Change to subreport ID, and restart */
- report_id = data[0] = subreport->id;
- continue;
- } else {
- return uclogic_raw_event_pen(drvdata, data, size);
+ /* Tweak pen reports, if necessary */
+ if ((report_id == params->pen.id) && (size >= 2)) {
+ subreport_list_end =
+ params->pen.subreport_list +
+ ARRAY_SIZE(params->pen.subreport_list);
+ /* Try to match a subreport */
+ for (subreport = params->pen.subreport_list;
+ subreport < subreport_list_end; subreport++) {
+ if (subreport->value != 0 &&
+ subreport->value == data[1]) {
+ break;
}
}
-
- /* Tweak frame control reports, if necessary */
- for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {
- if (report_id == params->frame_list[i].id) {
- return uclogic_raw_event_frame(
- drvdata, ¶ms->frame_list[i],
- data, size);
- }
+ /* If a subreport matched */
+ if (subreport < subreport_list_end) {
+ /* Change to the (non-pen) subreport ID and continue */
+ report_id = data[0] = subreport->id;
+ } else {
+ return uclogic_raw_event_pen(drvdata, data, size);
}
+ }
- break;
+ /* Tweak frame control reports, if necessary */
+ for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {
+ if (report_id == params->frame_list[i].id) {
+ return uclogic_raw_event_frame(
+ drvdata, ¶ms->frame_list[i],
+ data, size);
+ }
}
return 0;
--
2.43.0
^ permalink raw reply related
* [syzbot] [fs?] [usb?] [input?] INFO: rcu detected stall in sys_close (7)
From: syzbot @ 2024-04-19 1:14 UTC (permalink / raw)
To: brauner, jack, linux-fsdevel, linux-input, linux-kernel,
linux-usb, syzkaller-bugs, viro
Hello,
syzbot found the following issue on:
HEAD commit: 9ed46da14b9b Add linux-next specific files for 20240412
git tree: linux-next
console output: https://syzkaller.appspot.com/x/log.txt?x=16fe1a57180000
kernel config: https://syzkaller.appspot.com/x/.config?x=7ea0abc478c49859
dashboard link: https://syzkaller.appspot.com/bug?extid=393022c13d02e1f680e3
compiler: Debian clang version 15.0.6, GNU ld (GNU Binutils for Debian) 2.40
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=1572d74d180000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=11f4ce2b180000
Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/fc649744d68c/disk-9ed46da1.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/11eab7b9945d/vmlinux-9ed46da1.xz
kernel image: https://storage.googleapis.com/syzbot-assets/e7885afd198d/bzImage-9ed46da1.xz
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+393022c13d02e1f680e3@syzkaller.appspotmail.com
Total swap = 0kB
2097051 pages RAM
0 pages HighMem/MovableOnly
400839 pages reserved
0 pages cma reserved
rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu: Tasks blocked on level-0 rcu_node (CPUs 0-1):
P5112/1:b..l
rcu: (detected by 1, t=10504 jiffies, g=5477, q=222 ncpus=2)
task:udevd state:R
running task stack:23024 pid:5112 tgid:5112 ppid:4540 flags:0x00000002
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5409 [inline]
__schedule+0x17e8/0x4a50 kernel/sched/core.c:6746
preempt_schedule_notrace+0x100/0x140 kernel/sched/core.c:7018
preempt_schedule_notrace_thunk+0x1a/0x30 arch/x86/entry/thunk.S:13
rcu_is_watching+0x7e/0xb0 kernel/rcu/tree.c:726
trace_lock_release include/trace/events/lock.h:69 [inline]
lock_release+0xbf/0x9f0 kernel/locking/lockdep.c:5765
rcu_lock_release include/linux/rcupdate.h:339 [inline]
rcu_read_unlock include/linux/rcupdate.h:872 [inline]
is_bpf_text_address+0x280/0x2a0 kernel/bpf/core.c:769
kernel_text_address+0xa7/0xe0 kernel/extable.c:125
__kernel_text_address+0xd/0x40 kernel/extable.c:79
unwind_get_return_address+0x5d/0xc0 arch/x86/kernel/unwind_orc.c:369
arch_stack_walk+0x125/0x1b0 arch/x86/kernel/stacktrace.c:26
stack_trace_save+0x118/0x1d0 kernel/stacktrace.c:122
kasan_save_stack mm/kasan/common.c:47 [inline]
kasan_save_track+0x3f/0x80 mm/kasan/common.c:68
unpoison_slab_object mm/kasan/common.c:312 [inline]
__kasan_slab_alloc+0x66/0x80 mm/kasan/common.c:338
kasan_slab_alloc include/linux/kasan.h:201 [inline]
slab_post_alloc_hook mm/slub.c:3897 [inline]
slab_alloc_node mm/slub.c:3957 [inline]
kmem_cache_alloc_noprof+0x135/0x290 mm/slub.c:3964
getname_flags+0xbd/0x4f0 fs/namei.c:139
user_path_at_empty+0x2c/0x60 fs/namei.c:2920
do_readlinkat+0x118/0x3b0 fs/stat.c:499
__do_sys_readlink fs/stat.c:532 [inline]
__se_sys_readlink fs/stat.c:529 [inline]
__x64_sys_readlink+0x7f/0x90 fs/stat.c:529
do_syscall_x64 arch/x86/entry/common.c:74 [inline]
do_syscall_64+0xfa/0x250 arch/x86/entry/common.c:105
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fb17ad17d47
RSP: 002b:00007ffec561b8e8 EFLAGS: 00000246
ORIG_RAX: 0000000000000059
RAX: ffffffffffffffda RBX: 00007ffec561b8f8 RCX: 00007fb17ad17d47
RDX: 0000000000000400 RSI: 00007ffec561b8f8 RDI: 00007ffec561bdd8
RBP: 0000000000000400 R08: 00007ffec561c61c R09: 0000000000000000
R10: 0000000000000010 R11: 0000000000000246 R12: 00007ffec561bdd8
R13: 00007ffec561bd48 R14: 00005586f0f47910 R15: 00005586d5ea8a04
</TASK>
rcu: rcu_preempt kthread starved for 10502 jiffies! g5477 f0x0 RCU_GP_WAIT_FQS(5) ->state=0x0 ->cpu=1
rcu: Unless rcu_preempt kthread gets sufficient CPU time, OOM is now expected behavior.
rcu: RCU grace-period kthread stack dump:
task:rcu_preempt state:R
running task stack:24880 pid:17 tgid:17 ppid:2 flags:0x00004000
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5409 [inline]
__schedule+0x17e8/0x4a50 kernel/sched/core.c:6746
__schedule_loop kernel/sched/core.c:6823 [inline]
schedule+0x14b/0x320 kernel/sched/core.c:6838
schedule_timeout+0x1be/0x310 kernel/time/timer.c:2582
rcu_gp_fqs_loop+0x2df/0x1370 kernel/rcu/tree.c:2030
rcu_gp_kthread+0xa7/0x3b0 kernel/rcu/tree.c:2232
kthread+0x2f0/0x390 kernel/kthread.c:389
ret_from_fork+0x4b/0x80 arch/x86/kernel/process.c:147
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:244
</TASK>
rcu: Stack dump where RCU GP kthread last ran:
CPU: 1 PID: 4540 Comm: udevd Not tainted 6.9.0-rc3-next-20240412-syzkaller #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 03/27/2024
RIP: 0010:__raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:152 [inline]
RIP: 0010:_raw_spin_unlock_irqrestore+0xd8/0x140 kernel/locking/spinlock.c:194
Code: 9c 8f 44 24 20 42 80 3c 23 00 74 08 4c 89 f7 e8 ae 28 6c f6 f6 44 24 21 02 75 52 41 f7 c7 00 02 00 00 74 01 fb bf 01 00 00 00 <e8> 23 50 d6 f5 65 8b 05 84 47 74 74 85 c0 74 43 48 c7 04 24 0e 36
RSP: 0018:ffffc90000a18b20 EFLAGS: 00000206
RAX: 3fe8bac19e004400 RBX: 1ffff92000143168 RCX: ffffffff81731f5a
RDX: dffffc0000000000 RSI: ffffffff8bcad2a0 RDI: 0000000000000001
RBP: ffffc90000a18bb0 R08: ffffffff92f73677 R09: 1ffffffff25ee6ce
R10: dffffc0000000000 R11: fffffbfff25ee6cf R12: dffffc0000000000
R13: 1ffff92000143164 R14: ffffc90000a18b40 R15: 0000000000000246
FS: 00007fb17b114c80(0000) GS:ffff8880b9500000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00005586d5ec3048 CR3: 0000000077262000 CR4: 00000000003506f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
<IRQ>
call_timer_fn+0x18e/0x650 kernel/time/timer.c:1793
expire_timers kernel/time/timer.c:1844 [inline]
__run_timers kernel/time/timer.c:2418 [inline]
__run_timer_base+0x66a/0x8e0 kernel/time/timer.c:2429
run_timer_base kernel/time/timer.c:2438 [inline]
run_timer_softirq+0xb7/0x170 kernel/time/timer.c:2448
__do_softirq+0x2c6/0x980 kernel/softirq.c:554
invoke_softirq kernel/softirq.c:428 [inline]
__irq_exit_rcu+0xf2/0x1c0 kernel/softirq.c:633
irq_exit_rcu+0x9/0x30 kernel/softirq.c:645
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1043 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1043
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:702
RIP: 0010:__raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:152 [inline]
RIP: 0010:_raw_spin_unlock_irqrestore+0xd8/0x140 kernel/locking/spinlock.c:194
Code: 9c 8f 44 24 20 42 80 3c 23 00 74 08 4c 89 f7 e8 ae 28 6c f6 f6 44 24 21 02 75 52 41 f7 c7 00 02 00 00 74 01 fb bf 01 00 00 00 <e8> 23 50 d6 f5 65 8b 05 84 47 74 74 85 c0 74 43 48 c7 04 24 0e 36
RSP: 0018:ffffc9000477fc60 EFLAGS: 00000206
RAX: 3fe8bac19e004400 RBX: 1ffff920008eff90 RCX: ffffffff9476b703
RDX: dffffc0000000000 RSI: ffffffff8bcad2a0 RDI: 0000000000000001
RBP: ffffc9000477fcf0 R08: ffffffff8fabe6af R09: 1ffffffff1f57cd5
R10: dffffc0000000000 R11: fffffbfff1f57cd6 R12: dffffc0000000000
R13: 1ffff920008eff8c R14: ffffc9000477fc80 R15: 0000000000000246
__debug_check_no_obj_freed lib/debugobjects.c:998 [inline]
debug_check_no_obj_freed+0x561/0x580 lib/debugobjects.c:1019
slab_free_hook mm/slub.c:2162 [inline]
slab_free mm/slub.c:4393 [inline]
kmem_cache_free+0x10f/0x340 mm/slub.c:4468
file_free+0x24/0x1f0 fs/file_table.c:65
__do_sys_close fs/open.c:1556 [inline]
__se_sys_close fs/open.c:1541 [inline]
__x64_sys_close+0x7f/0x110 fs/open.c:1541
do_syscall_x64 arch/x86/entry/common.c:74 [inline]
do_syscall_64+0xfa/0x250 arch/x86/entry/common.c:105
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fb17ad170a8
Code: 48 8b 05 83 9d 0d 00 64 c7 00 16 00 00 00 83 c8 ff 48 83 c4 20 5b c3 64 8b 04 25 18 00 00 00 85 c0 75 20 b8 03 00 00 00 0f 05 <48> 3d 00 f0 ff ff 76 5b 48 8b 15 51 9d 0d 00 f7 d8 64 89 02 48 83
RSP: 002b:00007ffec56214e8 EFLAGS: 00000246
ORIG_RAX: 0000000000000003
RAX: ffffffffffffffda RBX: 00007fb17b114ae0 RCX: 00007fb17ad170a8
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 000000000000000c
RBP: 000000000000000c R08: 00000000ffffffff R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000002
R13: ffffffffffffffff R14: 00000000ffffffff R15: 00000000ffffffff
</TASK>
Mem-Info:
active_anon:3219 inactive_anon:0 isolated_anon:0
active_file:0 inactive_file:14600 isolated_file:0
unevictable:768 dirty:215 writeback:0
slab_reclaimable:7620 slab_unreclaimable:77925
mapped:2039 shmem:1251 pagetables:497
sec_pagetables:0 bounce:0
kernel_misc_reclaimable:0
free:1496034 free_pcp:915 free_cma:0
Node 0 active_anon:12876kB inactive_anon:0kB active_file:0kB inactive_file:58332kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:8156kB dirty:856kB writeback:0kB shmem:3468kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:8544kB pagetables:1988kB sec_pagetables:0kB all_unreclaimable? no
Node 1 active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:0kB dirty:4kB writeback:0kB shmem:1536kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:16kB pagetables:0kB sec_pagetables:0kB all_unreclaimable? no
Node 0 DMA free:15360kB boost:0kB min:204kB low:252kB high:300kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB writepending:0kB present:15992kB managed:15360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 2571 2571 0 0
Node 0 DMA32 free:2022004kB boost:0kB min:35108kB low:43884kB high:52660kB reserved_highatomic:0KB active_anon:12840kB inactive_anon:0kB active_file:0kB inactive_file:58008kB unevictable:1536kB writepending:852kB present:3129332kB managed:2660008kB mlocked:0kB bounce:0kB free_pcp:3660kB local_pcp:1220kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 Normal free:0kB boost:0kB min:4kB low:4kB high:4kB reserved_highatomic:0KB active_anon:36kB inactive_anon:0kB active_file:0kB inactive_file:324kB unevictable:0kB writepending:4kB present:1048576kB managed:360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 1 Normal free:3946772kB boost:0kB min:54788kB low:68484kB high:82180kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB writepending:4kB present:4194304kB managed:4109120kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 DMA: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 1*1024kB (U) 1*2048kB (M) 3*4096kB (M) = 15360kB
Node 0 DMA32: 73*4kB (UM) 66*8kB (UM) 1*16kB (E) 11*32kB (UM) 0*64kB 1*128kB (M) 1*256kB (M) 2*512kB (UM) 2*1024kB (ME) 1*2048kB (U) 492*4096kB (M) = 2021924kB
Node 0 Normal: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 0kB
Node 1 Normal: 5*4kB (U) 4*8kB (U) 4*16kB (U) 5*32kB (U) 8*64kB (UM) 4*128kB (U) 2*256kB (UM) 3*512kB (UM) 1*1024kB (U) 3*2048kB (UM) 961*4096kB (M) = 3946772kB
Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 0 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
Node 1 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 1 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
15851 total pagecache pages
0 pages in swap cache
Free swap = 0kB
Total swap = 0kB
2097051 pages RAM
0 pages HighMem/MovableOnly
400839 pages reserved
0 pages cma reserved
Mem-Info:
active_anon:3219 inactive_anon:0 isolated_anon:0
active_file:0 inactive_file:14600 isolated_file:0
unevictable:768 dirty:215 writeback:0
slab_reclaimable:7620 slab_unreclaimable:77925
mapped:2039 shmem:1251 pagetables:497
sec_pagetables:0 bounce:0
kernel_misc_reclaimable:0
free:1496034 free_pcp:915 free_cma:0
Node 0 active_anon:12876kB inactive_anon:0kB active_file:0kB inactive_file:58332kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:8156kB dirty:856kB writeback:0kB shmem:3468kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:8544kB pagetables:1988kB sec_pagetables:0kB all_unreclaimable? no
Node 1 active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:0kB dirty:4kB writeback:0kB shmem:1536kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:16kB pagetables:0kB sec_pagetables:0kB all_unreclaimable? no
Node 0 DMA free:15360kB boost:0kB min:204kB low:252kB high:300kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB writepending:0kB present:15992kB managed:15360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 2571 2571 0 0
Node 0 DMA32 free:2022004kB boost:0kB min:35108kB low:43884kB high:52660kB reserved_highatomic:0KB active_anon:12840kB inactive_anon:0kB active_file:0kB inactive_file:58008kB unevictable:1536kB writepending:852kB present:3129332kB managed:2660008kB mlocked:0kB bounce:0kB free_pcp:3660kB local_pcp:1220kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 Normal free:0kB boost:0kB min:4kB low:4kB high:4kB reserved_highatomic:0KB active_anon:36kB inactive_anon:0kB active_file:0kB inactive_file:324kB unevictable:0kB writepending:4kB present:1048576kB managed:360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 1 Normal free:3946772kB boost:0kB min:54788kB low:68484kB high:82180kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB writepending:4kB present:4194304kB managed:4109120kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 DMA: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 1*1024kB (U) 1*2048kB (M) 3*4096kB (M) = 15360kB
Node 0 DMA32: 73*4kB (UM) 66*8kB (UM) 1*16kB (E) 11*32kB (UM) 0*64kB 1*128kB (M) 1*256kB (M) 2*512kB (UM) 2*1024kB (ME) 1*2048kB (U) 492*4096kB (M) = 2021924kB
Node 0 Normal: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 0kB
Node 1 Normal: 5*4kB (U) 4*8kB (U) 4*16kB (U) 5*32kB (U) 8*64kB (UM) 4*128kB (U) 2*256kB (UM) 3*512kB (UM) 1*1024kB (U) 3*2048kB (UM) 961*4096kB (M) = 3946772kB
Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 0 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
Node 1 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 1 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
15851 total pagecache pages
0 pages in swap cache
Free swap = 0kB
Total swap = 0kB
2097051 pages RAM
0 pages HighMem/MovableOnly
400839 pages reserved
0 pages cma reserved
Mem-Info:
active_anon:3223 inactive_anon:0 isolated_anon:0
active_file:0 inactive_file:14602 isolated_file:0
unevictable:768 dirty:15 writeback:200
slab_reclaimable:7637 slab_unreclaimable:77900
mapped:2039 shmem:1253 pagetables:508
sec_pagetables:0 bounce:0
kernel_misc_reclaimable:0
free:1495958 free_pcp:992 free_cma:0
Node 0 active_anon:12892kB inactive_anon:0kB active_file:0kB inactive_file:58340kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:8156kB dirty:56kB writeback:800kB shmem:3476kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:8560kB pagetables:2032kB sec_pagetables:0kB all_unreclaimable? no
Node 1 active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:0kB dirty:4kB writeback:0kB shmem:1536kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:16kB pagetables:0kB sec_pagetables:0kB all_unreclaimable? no
Node 0 DMA free:15360kB boost:0kB min:204kB low:252kB high:300kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB writepending:0kB present:15992kB managed:15360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 2571 2571 0 0
Node 0 DMA32 free:2021700kB boost:0kB min:35108kB low:43884kB high:52660kB reserved_highatomic:0KB active_anon:12856kB inactive_anon:0kB active_file:0kB inactive_file:58016kB unevictable:1536kB writepending:852kB present:3129332kB managed:2660008kB mlocked:0kB bounce:0kB free_pcp:3968kB local_pcp:1272kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 Normal free:0kB boost:0kB min:4kB low:4kB high:4kB reserved_highatomic:0KB active_anon:36kB inactive_anon:0kB active_file:0kB inactive_file:324kB unevictable:0kB writepending:4kB present:1048576kB managed:360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 1 Normal free:3946772kB boost:0kB min:54788kB low:68484kB high:82180kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB writepending:4kB present:4194304kB managed:4109120kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 DMA: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 1*1024kB (U) 1*2048kB (M) 3*4096kB (M) = 15360kB
Node 0 DMA32: 73*4kB (UM) 66*8kB (UM) 1*16kB (E) 4*32kB (UM) 0*64kB 1*128kB (M) 1*256kB (M) 2*512kB (UM) 2*1024kB (ME) 1*2048kB (U) 492*4096kB (M) = 2021700kB
Node 0 Normal: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 0kB
Node 1 Normal: 5*4kB (U) 4*8kB (U) 4*16kB (U) 5*32kB (U) 8*64kB (UM) 4*128kB (U) 2*256kB (UM) 3*512kB (UM) 1*1024kB (U) 3*2048kB (UM) 961*4096kB (M) = 3946772kB
Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 0 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
Node 1 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 1 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
15855 total pagecache pages
0 pages in swap cache
Free swap = 0kB
Total swap = 0kB
2097051 pages RAM
0 pages HighMem/MovableOnly
400839 pages reserved
0 pages cma reserved
Mem-Info:
active_anon:3223 inactive_anon:0 isolated_anon:0
active_file:0 inactive_file:14602 isolated_file:0
unevictable:768 dirty:15 writeback:200
slab_reclaimable:7637 slab_unreclaimable:77900
mapped:2039 shmem:1253 pagetables:508
sec_pagetables:0 bounce:0
kernel_misc_reclaimable:0
free:1495958 free_pcp:992 free_cma:0
Node 0 active_anon:12892kB inactive_anon:0kB active_file:0kB inactive_file:58340kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:8156kB dirty:56kB writeback:800kB shmem:3476kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:8560kB pagetables:2032kB sec_pagetables:0kB all_unreclaimable? no
Node 1 active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:0kB dirty:4kB writeback:0kB shmem:1536kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:16kB pagetables:0kB sec_pagetables:0kB all_unreclaimable? no
Node 0 DMA free:15360kB boost:0kB min:204kB low:252kB high:300kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB writepending:0kB present:15992kB managed:15360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 2571 2571 0 0
Node 0 DMA32 free:2021700kB boost:0kB min:35108kB low:43884kB high:52660kB reserved_highatomic:0KB active_anon:12856kB inactive_anon:0kB active_file:0kB inactive_file:58016kB unevictable:1536kB writepending:852kB present:3129332kB managed:2660008kB mlocked:0kB bounce:0kB free_pcp:3968kB local_pcp:1272kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 Normal free:0kB boost:0kB min:4kB low:4kB high:4kB reserved_highatomic:0KB active_anon:36kB inactive_anon:0kB active_file:0kB inactive_file:324kB unevictable:0kB writepending:4kB present:1048576kB managed:360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 1 Normal free:3946772kB boost:0kB min:54788kB low:68484kB high:82180kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB writepending:4kB present:4194304kB managed:4109120kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 DMA: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 1*1024kB (U) 1*2048kB (M) 3*4096kB (M) = 15360kB
Node 0 DMA32: 73*4kB (UM) 66*8kB (UM) 1*16kB (E) 4*32kB (UM) 0*64kB 1*128kB (M) 1*256kB (M) 2*512kB (UM) 2*1024kB (ME) 1*2048kB (U) 492*4096kB (M) = 2021700kB
Node 0 Normal: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 0kB
Node 1 Normal: 5*4kB (U) 4*8kB (U) 4*16kB (U) 5*32kB (U) 8*64kB (UM) 4*128kB (U) 2*256kB (UM) 3*512kB (UM) 1*1024kB (U) 3*2048kB (UM) 961*4096kB (M) = 3946772kB
Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 0 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
Node 1 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 1 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
15855 total pagecache pages
0 pages in swap cache
Free swap = 0kB
Total swap = 0kB
2097051 pages RAM
0 pages HighMem/MovableOnly
400839 pages reserved
0 pages cma reserved
Mem-Info:
active_anon:3223 inactive_anon:0 isolated_anon:0
active_file:0 inactive_file:14602 isolated_file:0
unevictable:768 dirty:15 writeback:200
slab_reclaimable:7637 slab_unreclaimable:77900
mapped:2039 shmem:1253 pagetables:508
sec_pagetables:0 bounce:0
kernel_misc_reclaimable:0
free:1495958 free_pcp:992 free_cma:0
Node 0 active_anon:12892kB inactive_anon:0kB active_file:0kB inactive_file:58340kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:8156kB dirty:56kB writeback:800kB shmem:3476kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:8560kB pagetables:2032kB sec_pagetables:0kB all_unreclaimable? no
Node 1 active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:0kB dirty:4kB writeback:0kB shmem:1536kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:16kB pagetables:0kB sec_pagetables:0kB all_unreclaimable? no
Node 0 DMA free:15360kB boost:0kB min:204kB low:252kB high:300kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB writepending:0kB present:15992kB managed:15360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 2571 2571 0 0
Node 0 DMA32 free:2021700kB boost:0kB min:35108kB low:43884kB high:52660kB reserved_highatomic:0KB active_anon:12856kB inactive_anon:0kB active_file:0kB inactive_file:58016kB unevictable:1536kB writepending:852kB present:3129332kB managed:2660008kB mlocked:0kB bounce:0kB free_pcp:3968kB local_pcp:1272kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 Normal free:0kB boost:0kB min:4kB low:4kB high:4kB reserved_highatomic:0KB active_anon:36kB inactive_anon:0kB active_file:0kB inactive_file:324kB unevictable:0kB writepending:4kB present:1048576kB managed:360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 1 Normal free:3946772kB boost:0kB min:54788kB low:68484kB high:82180kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB writepending:4kB present:4194304kB managed:4109120kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 DMA: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 1*1024kB (U) 1*2048kB (M) 3*4096kB (M) = 15360kB
Node 0 DMA32: 73*4kB (UM) 66*8kB (UM) 1*16kB (E) 4*32kB (UM) 0*64kB 1*128kB (M) 1*256kB (M) 2*512kB (UM) 2*1024kB (ME) 1*2048kB (U) 492*4096kB (M) = 2021700kB
Node 0 Normal: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 0kB
Node 1 Normal: 5*4kB (U) 4*8kB (U) 4*16kB (U) 5*32kB (U) 8*64kB (UM) 4*128kB (U) 2*256kB (UM) 3*512kB (UM) 1*1024kB (U) 3*2048kB (UM) 961*4096kB (M) = 3946772kB
Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 0 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
Node 1 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 1 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
15855 total pagecache pages
0 pages in swap cache
Free swap = 0kB
Total swap = 0kB
2097051 pages RAM
0 pages HighMem/MovableOnly
400839 pages reserved
0 pages cma reserved
Mem-Info:
active_anon:3223 inactive_anon:0 isolated_anon:0
active_file:0 inactive_file:14602 isolated_file:0
unevictable:768 dirty:15 writeback:200
slab_reclaimable:7637 slab_unreclaimable:77900
mapped:2039 shmem:1253 pagetables:508
sec_pagetables:0 bounce:0
kernel_misc_reclaimable:0
free:1495958 free_pcp:991 free_cma:0
Node 0 active_anon:12892kB inactive_anon:0kB active_file:0kB inactive_file:58340kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:8156kB dirty:56kB writeback:800kB shmem:3476kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:8560kB pagetables:2032kB sec_pagetables:0kB all_unreclaimable? no
Node 1 active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:0kB dirty:4kB writeback:0kB shmem:1536kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:16kB pagetables:0kB sec_pagetables:0kB all_unreclaimable? no
Node 0 DMA free:15360kB boost:0kB min:204kB low:252kB high:300kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB writepending:0kB present:15992kB managed:15360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 2571 2571 0 0
Node 0 DMA32 free:2021700kB boost:0kB min:35108kB low:43884kB high:52660kB reserved_highatomic:0KB active_anon:12856kB inactive_anon:0kB active_file:0kB inactive_file:58016kB unevictable:1536kB writepending:852kB present:3129332kB managed:2660008kB mlocked:0kB bounce:0kB free_pcp:3964kB local_pcp:1272kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 Normal free:0kB boost:0kB min:4kB low:4kB high:4kB reserved_highatomic:0KB active_anon:36kB inactive_anon:0kB active_file:0kB inactive_file:324kB unevictable:0kB writepending:4kB present:1048576kB managed:360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 1 Normal free:3946772kB boost:0kB min:54788kB low:68484kB high:82180kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB writepending:4kB present:4194304kB managed:4109120kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 DMA: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 1*1024kB (U) 1*2048kB (M) 3*4096kB (M) = 15360kB
Node 0 DMA32: 73*4kB (UM) 66*8kB (UM) 1*16kB (E) 4*32kB (UM) 0*64kB 1*128kB (M) 1*256kB (M) 2*512kB (UM) 2*1024kB (ME) 1*2048kB (U) 492*4096kB (M) = 2021700kB
Node 0 Normal: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 0kB
Node 1 Normal: 5*4kB (U) 4*8kB (U) 4*16kB (U) 5*32kB (U) 8*64kB (UM) 4*128kB (U) 2*256kB (UM) 3*512kB (UM) 1*1024kB (U) 3*2048kB (UM) 961*4096kB (M) = 3946772kB
Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 0 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
Node 1 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 1 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
15855 total pagecache pages
0 pages in swap cache
Free swap = 0kB
Total swap = 0kB
2097051 pages RAM
0 pages HighMem/MovableOnly
400839 pages reserved
0 pages cma reserved
Mem-Info:
active_anon:3223 inactive_anon:0 isolated_anon:0
active_file:0 inactive_file:14602 isolated_file:0
unevictable:768 dirty:15 writeback:200
slab_reclaimable:7637 slab_unreclaimable:77900
mapped:2039 shmem:1253 pagetables:508
sec_pagetables:0 bounce:0
kernel_misc_reclaimable:0
free:1495958 free_pcp:991 free_cma:0
Node 0 active_anon:12892kB inactive_anon:0kB active_file:0kB inactive_file:58340kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:8156kB dirty:56kB writeback:800kB shmem:3476kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:8560kB pagetables:2032kB sec_pagetables:0kB all_unreclaimable? no
Node 1 active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:0kB dirty:4kB writeback:0kB shmem:1536kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:16kB pagetables:0kB sec_pagetables:0kB all_unreclaimable? no
Node 0 DMA free:15360kB boost:0kB min:204kB low:252kB high:300kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB writepending:0kB present:15992kB managed:15360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 2571 2571 0 0
Node 0 DMA32 free:2021700kB boost:0kB min:35108kB low:43884kB high:52660kB reserved_highatomic:0KB active_anon:12856kB inactive_anon:0kB active_file:0kB inactive_file:58016kB unevictable:1536kB writepending:852kB present:3129332kB managed:2660008kB mlocked:0kB bounce:0kB free_pcp:3964kB local_pcp:1272kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 Normal free:0kB boost:0kB min:4kB low:4kB high:4kB reserved_highatomic:0KB active_anon:36kB inactive_anon:0kB active_file:0kB inactive_file:324kB unevictable:0kB writepending:4kB present:1048576kB managed:360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 1 Normal free:3946772kB boost:0kB min:54788kB low:68484kB high:82180kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB writepending:4kB present:4194304kB managed:4109120kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 DMA: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 1*1024kB (U) 1*2048kB (M) 3*4096kB (M) = 15360kB
Node 0 DMA32: 73*4kB (UM) 66*8kB (UM) 1*16kB (E) 4*32kB (UM) 0*64kB 1*128kB (M) 1*256kB (M) 2*512kB (UM) 2*1024kB (ME) 1*2048kB (U) 492*4096kB (M) = 2021700kB
Node 0 Normal: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 0kB
Node 1 Normal: 5*4kB (U) 4*8kB (U) 4*16kB (U) 5*32kB (U) 8*64kB (UM) 4*128kB (U) 2*256kB (UM) 3*512kB (UM) 1*1024kB (U) 3*2048kB (UM) 961*4096kB (M) = 3946772kB
Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 0 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
Node 1 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 1 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
15855 total pagecache pages
0 pages in swap cache
Free swap = 0kB
Total swap = 0kB
2097051 pages RAM
0 pages HighMem/MovableOnly
400839 pages reserved
0 pages cma reserved
Mem-Info:
active_anon:3223 inactive_anon:0 isolated_anon:0
active_file:0 inactive_file:14602 isolated_file:0
unevictable:768 dirty:15 writeback:200
slab_reclaimable:7637 slab_unreclaimable:77900
mapped:2039 shmem:1253 pagetables:508
sec_pagetables:0 bounce:0
kernel_misc_reclaimable:0
free:1495958 free_pcp:991 free_cma:0
Node 0 active_anon:12892kB inactive_anon:0kB active_file:0kB inactive_file:58340kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:8156kB dirty:56kB writeback:800kB shmem:3476kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:8560kB pagetables:2032kB sec_pagetables:0kB all_unreclaimable? no
Node 1 active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:0kB dirty:4kB writeback:0kB shmem:1536kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:16kB pagetables:0kB sec_pagetables:0kB all_unreclaimable? no
Node 0 DMA free:15360kB boost:0kB min:204kB low:252kB high:300kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB writepending:0kB present:15992kB managed:15360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 2571 2571 0 0
Node 0 DMA32 free:2021700kB boost:0kB min:35108kB low:43884kB high:52660kB reserved_highatomic:0KB active_anon:12856kB inactive_anon:0kB active_file:0kB inactive_file:58016kB unevictable:1536kB writepending:852kB present:3129332kB managed:2660008kB mlocked:0kB bounce:0kB free_pcp:3964kB local_pcp:1272kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 Normal free:0kB boost:0kB min:4kB low:4kB high:4kB reserved_highatomic:0KB active_anon:36kB inactive_anon:0kB active_file:0kB inactive_file:324kB unevictable:0kB writepending:4kB present:1048576kB managed:360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 1 Normal free:3946772kB boost:0kB min:54788kB low:68484kB high:82180kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB writepending:4kB present:4194304kB managed:4109120kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 DMA: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 1*1024kB (U) 1*2048kB (M) 3*4096kB (M) = 15360kB
Node 0 DMA32: 73*4kB (UM) 66*8kB (UM) 1*16kB (E) 4*32kB (UM) 0*64kB 1*128kB (M) 1*256kB (M) 2*512kB (UM) 2*1024kB (ME) 1*2048kB (U) 492*4096kB (M) = 2021700kB
Node 0 Normal: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 0kB
Node 1 Normal: 5*4kB (U) 4*8kB (U) 4*16kB (U) 5*32kB (U) 8*64kB (UM) 4*128kB (U) 2*256kB (UM) 3*512kB (UM) 1*1024kB (U) 3*2048kB (UM) 961*4096kB (M) = 3946772kB
Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 0 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
Node 1 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 1 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
15855 total pagecache pages
0 pages in swap cache
Free swap = 0kB
Total swap = 0kB
2097051 pages RAM
0 pages HighMem/MovableOnly
400839 pages reserved
0 pages cma reserved
Mem-Info:
active_anon:3223 inactive_anon:0 isolated_anon:0
active_file:0 inactive_file:14602 isolated_file:0
unevictable:768 dirty:15 writeback:200
slab_reclaimable:7637 slab_unreclaimable:77900
mapped:2039 shmem:1253 pagetables:508
sec_pagetables:0 bounce:0
kernel_misc_reclaimable:0
free:1495958 free_pcp:991 free_cma:0
Node 0 active_anon:12892kB inactive_anon:0kB active_file:0kB inactive_file:58340kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:8156kB dirty:56kB writeback:800kB shmem:3476kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:8560kB pagetables:2032kB sec_pagetables:0kB all_unreclaimable? no
Node 1 active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:0kB dirty:4kB writeback:0kB shmem:1536kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:16kB pagetables:0kB sec_pagetables:0kB all_unreclaimable? no
Node 0 DMA free:15360kB boost:0kB min:204kB low:252kB high:300kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB writepending:0kB present:15992kB managed:15360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 2571 2571 0 0
Node 0 DMA32 free:2021700kB boost:0kB min:35108kB low:43884kB high:52660kB reserved_highatomic:0KB active_anon:12856kB inactive_anon:0kB active_file:0kB inactive_file:58016kB unevictable:1536kB writepending:852kB present:3129332kB managed:2660008kB mlocked:0kB bounce:0kB free_pcp:3964kB local_pcp:1272kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 Normal free:0kB boost:0kB min:4kB low:4kB high:4kB reserved_highatomic:0KB active_anon:36kB inactive_anon:0kB active_file:0kB inactive_file:324kB unevictable:0kB writepending:4kB present:1048576kB managed:360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 1 Normal free:3946772kB boost:0kB min:54788kB low:68484kB high:82180kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB writepending:4kB present:4194304kB managed:4109120kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 DMA: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 1*1024kB (U) 1*2048kB (M) 3*4096kB (M) = 15360kB
Node 0 DMA32: 73*4kB (UM) 66*8kB (UM) 1*16kB (E) 4*32kB (UM) 0*64kB 1*128kB (M) 1*256kB (M) 2*512kB (UM) 2*1024kB (ME) 1*2048kB (U) 492*4096kB (M) = 2021700kB
Node 0 Normal: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 0kB
Node 1 Normal: 5*4kB (U) 4*8kB (U) 4*16kB (U) 5*32kB (U) 8*64kB (UM) 4*128kB (U) 2*256kB (UM) 3*512kB (UM) 1*1024kB (U) 3*2048kB (UM) 961*4096kB (M) = 3946772kB
Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 0 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
Node 1 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 1 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
15855 total pagecache pages
0 pages in swap cache
Free swap = 0kB
Total swap = 0kB
2097051 pages RAM
0 pages HighMem/MovableOnly
400839 pages reserved
0 pages cma reserved
Mem-Info:
active_anon:3223 inactive_anon:0 isolated_anon:0
active_file:0 inactive_file:14602 isolated_file:0
unevictable:768 dirty:15 writeback:200
slab_reclaimable:7637 slab_unreclaimable:77900
mapped:2039 shmem:1253 pagetables:508
sec_pagetables:0 bounce:0
kernel_misc_reclaimable:0
free:1495958 free_pcp:991 free_cma:0
Node 0 active_anon:12892kB inactive_anon:0kB active_file:0kB inactive_file:58340kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:8156kB dirty:56kB writeback:800kB shmem:3476kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:8560kB pagetables:2032kB sec_pagetables:0kB all_unreclaimable? no
Node 1 active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB isolated(anon):0kB isolated(file):0kB mapped:0kB dirty:4kB writeback:0kB shmem:1536kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB writeback_tmp:0kB kernel_stack:16kB pagetables:0kB sec_pagetables:0kB all_unreclaimable? no
Node 0 DMA free:15360kB boost:0kB min:204kB low:252kB high:300kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB writepending:0kB present:15992kB managed:15360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 2571 2571 0 0
Node 0 DMA32 free:2021700kB boost:0kB min:35108kB low:43884kB high:52660kB reserved_highatomic:0KB active_anon:12856kB inactive_anon:0kB active_file:0kB inactive_file:58016kB unevictable:1536kB writepending:852kB present:3129332kB managed:2660008kB mlocked:0kB bounce:0kB free_pcp:3964kB local_pcp:1272kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 Normal free:0kB boost:0kB min:4kB low:4kB high:4kB reserved_highatomic:0KB active_anon:36kB inactive_anon:0kB active_file:0kB inactive_file:324kB unevictable:0kB writepending:4kB present:1048576kB managed:360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 1 Normal free:3946772kB boost:0kB min:54788kB low:68484kB high:82180kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:68kB unevictable:1536kB writepending:4kB present:4194304kB managed:4109120kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
lowmem_reserve[]: 0 0 0 0 0
Node 0 DMA: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 1*1024kB (U) 1*2048kB (M) 3*4096kB (M) = 15360kB
Node 0 DMA32: 73*4kB (UM) 66*8kB (UM) 1*16kB (E) 4*32kB (UM) 0*64kB 1*128kB (M) 1*256kB (M) 2*512kB (UM) 2*1024kB (ME) 1*2048kB (U) 492*4096kB (M) = 2021700kB
Node 0 Normal: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 0kB
Node 1 Normal: 5*4kB (U) 4*8kB (U) 4*16kB (U) 5*32kB (U) 8*64kB (UM) 4*128kB (U) 2*256kB (UM) 3*512kB (UM) 1*1024kB (U) 3*2048kB (UM) 961*4096kB (M) = 3946772kB
Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 0 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
Node 1 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Node 1 hugepages_total=2 hugepages_free=2 hugepages_surp=0 hugepages_size=2048kB
15855 total pagecache pages
0 pages in swap cache
Free swap = 0kB
Total swap = 0kB
2097051 pages RAM
0 pages HighMem/MovableOnly
400839 pages reserved
0 pages cma reserved
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title
If you want syzbot to run the reproducer, reply with:
#syz test: git://repo/address.git branch-or-commit-hash
If you attach or paste a git patch, syzbot will apply it before testing.
If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)
If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report
If you want to undo deduplication, reply with:
#syz undup
^ permalink raw reply
* Re: [PATCH v9 3/3] Input: Add TouchNetix axiom i2c touchscreen driver
From: Kamel BOUHARA @ 2024-04-19 8:33 UTC (permalink / raw)
To: Marco Felsch
Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Jeff LaBundy, catalin.popescu, mark.satterthwaite,
Thomas Petazzoni, Gregory Clement, bsp-development.geo
In-Reply-To: <20240318080533.GA35033@tpx1.home>
Le 2024-03-18 09:05, Kamel Bouhara a écrit :
> Le Wed, Mar 13, 2024 at 09:21:35PM +0100, Marco Felsch a écrit :
>> Hi Kamel,
>>
>
> Hello Marco,
>
> [...]
>
Hello,
>> > +static int axiom_i2c_probe(struct i2c_client *client)
>> > +{
>> > + struct device *dev = &client->dev;
>> > + struct input_dev *input_dev;
>> > + struct axiom_data *ts;
>> > + u32 poll_interval;
>> > + int target;
>> > + int error;
>> > +
>> > + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
>> > + if (!ts)
>> > + return -ENOMEM;
>> > +
>> > + i2c_set_clientdata(client, ts);
>> > + ts->client = client;
>> > + ts->dev = dev;
>> > +
>> > + ts->regmap = devm_regmap_init_i2c(client, &axiom_i2c_regmap_config);
>> > + error = PTR_ERR_OR_ZERO(ts->regmap);
>> > + if (error) {
>> > + dev_err(dev, "Failed to initialize regmap: %d\n", error);
>> > + return error;
>> > + }
>> > +
>> > + ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
>> > + if (IS_ERR(ts->reset_gpio))
>> > + return dev_err_probe(dev, PTR_ERR(ts->reset_gpio), "failed to get reset GPIO\n");
>> > +
>> > + if (ts->reset_gpio)
>> > + axiom_reset(ts->reset_gpio);
>>
>> This seems useless, since you doing an reset without enabling the
>> power
>> supply (below). I know there are systems which do have the supply
>> always
>> connected or for ACPI the supply is managed via firmware, but the
>> driver
>> should implement the correct logic and for DT/OF case this is not
>> correct.
>>
>> > +
>> > + ts->vddi = devm_regulator_get_optional(dev, "vddi");
>> > + if (!IS_ERR(ts->vddi)) {
>> > + error = devm_regulator_get_enable(dev, "vddi");
>>
>> Regulators are ref counted and now you request the regulator twice.
>> Also
>> the regulator is not optional, it is required for the device to work.
>> Same applies to the vdda below.
>>
>
> While it is true most of the time, it occurs that for x86 based boards,
> adding a regulator entirely is not always possible.
>
> In our particular case, the I2C controller for this touchscreen is
> behind a CPLD (aka embedded controller) so I have no direct access to
> the I2C controller and it isn't described in the ACPI table.
>
> In a normal case, I would use ACPI override to pass regulator
> properties, but here it's not possible.
>
> Having a CPLD exposing this kind of controller is quite common on x86
> based boards. So, we need a way to support the case when a regulator
> can't be described. The optional regulator looked like a good option,
> but if you have a better alternative, I am open to considering it.
>
I actually confirmed this case is already handled in
acpi_subsystem_init():
...
» » /*
» » * If the system is using ACPI then we can be reasonably
» » * confident that any regulators are managed by the
firmware
» » * so tell the regulator core it has everything it needs
to
» » * know.
» » */
» » regulator_has_full_constraints();
Thanks Marco for the clue :) !
Regards,
--
Kamel Bouhara, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply
* [PATCH v10 0/3] Input: Add TouchNetix axiom touchscreen driver
From: Kamel Bouhara @ 2024-04-19 8:33 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Marco Felsch, Jeff LaBundy
Cc: catalin.popescu, mark.satterthwaite, Thomas Petazzoni,
Gregory Clement, bsp-development.geo, Kamel Bouhara
Add a new driver for the TouchNetix's axiom family of touchscreen
controller. This driver only support i2c and can be later adapted for
SPI and USB support.
--
Changes in v10:
- Set regulators as required
- Enable power supply before reset
- Fix ref count due to regulator requested twice
- Rebase on v6.9-rc4
Changes in v9:
- Fix issue reported in https://lore.kernel.org/oe-kbuild-all/202402201157.BKo97uWl-lkp@intel.com/
- Rebase on v6.8-rc2
Changes in v8:
- Fix missing call to input_report_slot_state()
- Fix issue reported in https://lore.kernel.org/oe-kbuild-all/202402020623.8T1Ah513-lkp@intel.com/
Changes in v7:
- Remove startup time from dt-binding
- Fix usage table not correctly populated
Changes in v6:
- Fix missing unevaluatedProperties.in dt-binding
- Use __le16 to correctly deal with device endianness
- Use standart kernel types s/char/u8/
- Use regmap api as driver might support spi later
- Use get_unaligned_le16() for the sake of clarity
- Use devm_regulator_enable_optional()
Changes in v5:
- Fix wrong message constructed in axiom_i2c_read
- Delay required between i2c reads is >= 250us
- Do not split report reading in two phases as we'll
have to wait 500us
- Use lower-case in properties names
- Make regulators properties are required in dt-binding
- Fix bug report: https://lore.kernel.org/lkml/202312051457.y3N1q3sZ-lkp@intel.com/
- Fix bug report: https://lore.kernel.org/lkml/6f8e3b64-5b21-4a50-8680-063ef7a93bdb@suswa.mountain/
Changes in v4:
- Cleanup unused headers and macros
- Use standard kernel type
- Namespace structures and functions
- Use packed struct when possible to avoid bitfield operators
- Fix missing break when address is found in axiom_populate_target_address()
- Split reads in two steps for the reports, first length then report
itself so we only read required bytes
- Get poll-interval from devicetree
- Add VDDI/VDDA regulators
- Add a startup delay of 110 ms required after VDDA/VDDI is applied
- Remove axiom_i2c_write() as it is no more used
Changes in v3:
- Remove irq-gpios property in dt-binding
- Use a generic node name
- Fix issues reported in https://lore.kernel.org/oe-kbuild-all/202310100300.oAC2M62R-lkp@intel.com/
Changes in v2:
- Add device tree binding documentation
- Move core functions in axiom_i2c as we only care about i2c support now
- Use static function when required
- Use syntax dev_err_probe()
- Add an hardware based reset
Kamel Bouhara (3):
dt-bindings: vendor-prefixes: Add TouchNetix AS
dt-bindings: input: Add TouchNetix axiom touchscreen
Input: Add TouchNetix axiom i2c touchscreen driver
.../input/touchscreen/touchnetix,ax54a.yaml | 62 ++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
MAINTAINERS | 8 +
drivers/input/touchscreen/Kconfig | 12 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/touchnetix_axiom.c | 659 ++++++++++++++++++
6 files changed, 744 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
create mode 100644 drivers/input/touchscreen/touchnetix_axiom.c
--
2.25.1
^ permalink raw reply
* [PATCH v10 1/3] dt-bindings: vendor-prefixes: Add TouchNetix AS
From: Kamel Bouhara @ 2024-04-19 8:33 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Marco Felsch, Jeff LaBundy
Cc: catalin.popescu, mark.satterthwaite, Thomas Petazzoni,
Gregory Clement, bsp-development.geo, Kamel Bouhara,
Krzysztof Kozlowski
In-Reply-To: <20240419083342.61199-1-kamel.bouhara@bootlin.com>
Add vendor prefix for TouchNetix AS (https://www.touchnetix.com/products/).
Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
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 b97d298b3eb6..33205dea558b 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1481,6 +1481,8 @@ patternProperties:
description: Toradex AG
"^toshiba,.*":
description: Toshiba Corporation
+ "^touchnetix,.*":
+ description: TouchNetix AS
"^toumaz,.*":
description: Toumaz
"^tpk,.*":
--
2.25.1
^ permalink raw reply related
* [PATCH v10 3/3] Input: Add TouchNetix axiom i2c touchscreen driver
From: Kamel Bouhara @ 2024-04-19 8:33 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Marco Felsch, Jeff LaBundy
Cc: catalin.popescu, mark.satterthwaite, Thomas Petazzoni,
Gregory Clement, bsp-development.geo, Kamel Bouhara
In-Reply-To: <20240419083342.61199-1-kamel.bouhara@bootlin.com>
Add a new driver for the TouchNetix's axiom family of
touchscreen controllers. This driver only supports i2c
and can be later adapted for SPI and USB support.
Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
---
MAINTAINERS | 2 +
drivers/input/touchscreen/Kconfig | 12 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/touchnetix_axiom.c | 659 +++++++++++++++++++
4 files changed, 674 insertions(+)
create mode 100644 drivers/input/touchscreen/touchnetix_axiom.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 3c59cb760071..d9535d7e4061 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22421,9 +22421,11 @@ F: drivers/platform/x86/toshiba-wmi.c
TOUCHNETIX AXIOM I2C TOUCHSCREEN DRIVER
M: Kamel Bouhara <kamel.bouhara@bootlin.com>
+M: bsp-development.geo@leica-geosystems.com
L: linux-input@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
+F: drivers/input/touchscreen/touchnetix_axiom.c
TPM DEVICE DRIVER
M: Peter Huewe <peterhuewe@gmx.de>
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index c821fe3ee794..503ccea5c1b0 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -834,6 +834,18 @@ config TOUCHSCREEN_MIGOR
To compile this driver as a module, choose M here: the
module will be called migor_ts.
+config TOUCHSCREEN_TOUCHNETIX_AXIOM
+ tristate "TouchNetix AXIOM based touchscreen controllers"
+ depends on I2C
+ help
+ Say Y here if you have a axiom touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called axiom.
+
config TOUCHSCREEN_TOUCHRIGHT
tristate "Touchright serial touchscreen"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index a81cb5aa21a5..6ce7b804adc7 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -91,6 +91,7 @@ obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o
obj-$(CONFIG_TOUCHSCREEN_SURFACE3_SPI) += surface3_spi.o
obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHNETIX_AXIOM) += touchnetix_axiom.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
obj-$(CONFIG_TOUCHSCREEN_TS4800) += ts4800-ts.o
diff --git a/drivers/input/touchscreen/touchnetix_axiom.c b/drivers/input/touchscreen/touchnetix_axiom.c
new file mode 100644
index 000000000000..a2142de55a88
--- /dev/null
+++ b/drivers/input/touchscreen/touchnetix_axiom.c
@@ -0,0 +1,659 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TouchNetix axiom Touchscreen Driver
+ *
+ * Copyright (C) 2020-2023 TouchNetix Ltd.
+ *
+ * Author(s): Bart Prescott <bartp@baasheep.co.uk>
+ * Pedro Torruella <pedro.torruella@touchnetix.com>
+ * Mark Satterthwaite <mark.satterthwaite@touchnetix.com>
+ * Hannah Rossiter <hannah.rossiter@touchnetix.com>
+ * Kamel Bouhara <kamel.bouhara@bootlin.com>
+ *
+ */
+#include <linux/bitfield.h>
+#include <linux/crc16.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+
+#include <asm/unaligned.h>
+#define AXIOM_PROX_LEVEL -128
+#define AXIOM_DMA_OPS_DELAY_USEC 250
+#define AXIOM_STARTUP_TIME_MS 110
+/*
+ * Register group u31 has 2 pages for usage table entries.
+ */
+#define AXIOM_U31_MAX_USAGES 0xff
+#define AXIOM_U31_BYTES_PER_USAGE 6
+#define AXIOM_U31_PAGE0_LENGTH 0x0C
+#define AXIOM_U31_BOOTMODE_MASK BIT(7)
+#define AXIOM_U31_DEVID_MASK GENMASK(14, 0)
+
+#define AXIOM_MAX_REPORT_LEN 0x7f
+
+#define AXIOM_CMD_HEADER_READ_MASK BIT(15)
+#define AXIOM_U41_MAX_TARGETS 10
+
+#define AXIOM_U46_AUX_CHANNELS 4
+#define AXIOM_U46_AUX_MASK GENMASK(11, 0)
+
+#define AXIOM_COMMS_MAX_USAGE_PAGES 3
+#define AXIOM_COMMS_PAGE_SIZE 256
+#define AXIOM_COMMS_REPORT_LEN_MASK GENMASK(6, 0)
+
+#define AXIOM_REPORT_USAGE_ID 0x34
+#define AXIOM_DEVINFO_USAGE_ID 0x31
+#define AXIOM_USAGE_2HB_REPORT_ID 0x01
+#define AXIOM_USAGE_2AUX_REPORT_ID 0x46
+#define AXIOM_USAGE_2DCTS_REPORT_ID 0x41
+
+#define AXIOM_PAGE_OFFSET_MASK GENMASK(6, 0)
+
+struct axiom_devinfo {
+ __le16 device_id;
+ u8 fw_minor;
+ u8 fw_major;
+ u8 fw_info_extra;
+ u8 tcp_revision;
+ u8 bootloader_fw_minor;
+ u8 bootloader_fw_major;
+ __le16 jedec_id;
+ u8 num_usages;
+} __packed;
+
+/*
+ * Describes parameters of a specific usage, essentially a single element of
+ * the "Usage Table"
+ */
+struct axiom_usage_entry {
+ u8 id;
+ u8 is_report;
+ u8 start_page;
+ u8 num_pages;
+};
+
+/*
+ * Represents state of a touch or target when detected prior to a touch (eg.
+ * hover or proximity events).
+ */
+enum axiom_target_state {
+ AXIOM_TARGET_STATE_NOT_PRESENT = 0,
+ AXIOM_TARGET_STATE_PROX = 1,
+ AXIOM_TARGET_STATE_HOVER = 2,
+ AXIOM_TARGET_STATE_TOUCHING = 3,
+};
+
+struct axiom_u41_target {
+ enum axiom_target_state state;
+ u16 x;
+ u16 y;
+ s8 z;
+ bool insert;
+ bool touch;
+};
+
+struct axiom_target_report {
+ u8 index;
+ u8 present;
+ u16 x;
+ u16 y;
+ s8 z;
+};
+
+struct axiom_cmd_header {
+ __le16 target_address;
+ __le16 length;
+} __packed;
+
+struct axiom_data {
+ struct axiom_devinfo devinfo;
+ struct device *dev;
+ struct gpio_desc *reset_gpio;
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ u32 max_report_len;
+ u8 rx_buf[AXIOM_COMMS_MAX_USAGE_PAGES * AXIOM_COMMS_PAGE_SIZE];
+ struct axiom_u41_target targets[AXIOM_U41_MAX_TARGETS];
+ struct axiom_usage_entry usage_table[AXIOM_U31_MAX_USAGES];
+ bool usage_table_populated;
+ struct regulator *vdda;
+ struct regulator *vddi;
+ struct regmap *regmap;
+ struct touchscreen_properties prop;
+};
+
+static const struct regmap_config axiom_i2c_regmap_config = {
+ .reg_bits = 32,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_bits = 8,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+/*
+ * axiom devices are typically configured to report touches at a rate
+ * of 100Hz (10ms) for systems that require polling for reports.
+ * When reports are polled, it will be expected to occasionally
+ * observe the overflow bit being set in the reports.
+ * This indicates that reports are not being read fast enough.
+ */
+#define POLL_INTERVAL_DEFAULT_MS 10
+
+/* Translate usage/page/offset triplet into physical address. */
+static u16 axiom_usage_to_target_address(struct axiom_data *ts, u8 usage, u8 page,
+ char offset)
+{
+ /* At the moment the convention is that u31 is always at physical address 0x0 */
+ if (!ts->usage_table_populated) {
+ if (usage == AXIOM_DEVINFO_USAGE_ID)
+ return ((page << 8) + offset);
+ else
+ return 0xffff;
+ }
+
+ if (page >= ts->usage_table[usage].num_pages) {
+ dev_err(ts->dev, "Invalid usage table! usage: u%02x, page: %02x, offset: %02x\n",
+ usage, page, offset);
+ return 0xffff;
+ }
+
+ return ((ts->usage_table[usage].start_page + page) << 8) + offset;
+}
+
+static int axiom_read(struct axiom_data *ts, u8 usage, u8 page, void *buf, u16 len)
+{
+ struct axiom_cmd_header cmd_header;
+ u32 preamble;
+ int ret;
+
+ cmd_header.target_address = cpu_to_le16(axiom_usage_to_target_address(ts, usage, page, 0));
+ cmd_header.length = cpu_to_le16(len | AXIOM_CMD_HEADER_READ_MASK);
+
+ preamble = get_unaligned_le32(&cmd_header);
+
+ ret = regmap_write(ts->regmap, preamble, 0);
+ if (ret) {
+ dev_err(ts->dev, "failed to write preamble, error %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_raw_read(ts->regmap, 0, buf, len);
+ if (ret) {
+ dev_err(ts->dev, "failed to read target address %04x, error %d\n",
+ cmd_header.target_address, ret);
+ return ret;
+ }
+
+ /* Wait device's DMA operations */
+ usleep_range(AXIOM_DMA_OPS_DELAY_USEC, AXIOM_DMA_OPS_DELAY_USEC + 50);
+
+ return 0;
+}
+
+/*
+ * One of the main purposes for reading the usage table is to identify
+ * which usages reside at which target address.
+ * When performing subsequent reads or writes to AXIOM, the target address
+ * is used to specify which usage is being accessed.
+ * Consider the following discovery code which will build up the usage table.
+ */
+static u32 axiom_populate_usage_table(struct axiom_data *ts)
+{
+ struct axiom_usage_entry *usage_table;
+ u8 *rx_data = ts->rx_buf;
+ u32 max_report_len = 0;
+ u32 usage_id;
+ int error;
+
+ usage_table = ts->usage_table;
+
+ /* Read the second page of usage u31 to get the usage table */
+ error = axiom_read(ts, AXIOM_DEVINFO_USAGE_ID, 1, rx_data,
+ (AXIOM_U31_BYTES_PER_USAGE * ts->devinfo.num_usages));
+
+ if (error)
+ return error;
+
+ for (usage_id = 0; usage_id < ts->devinfo.num_usages; usage_id++) {
+ u16 offset = (usage_id * AXIOM_U31_BYTES_PER_USAGE);
+ u8 id = rx_data[offset + 0];
+ u8 start_page = rx_data[offset + 1];
+ u8 num_pages = rx_data[offset + 2];
+ u32 max_offset = ((rx_data[offset + 3] & AXIOM_PAGE_OFFSET_MASK) + 1) * 2;
+
+ usage_table[id].is_report = !num_pages;
+
+ /* Store the entry into the usage table */
+ usage_table[id].id = id;
+ usage_table[id].start_page = start_page;
+ usage_table[id].num_pages = num_pages;
+
+ dev_dbg(ts->dev, "Usage u%02x Info: %*ph\n", id, AXIOM_U31_BYTES_PER_USAGE,
+ &rx_data[offset]);
+
+ /* Identify the max report length the module will receive */
+ if (usage_table[id].is_report && max_offset > max_report_len)
+ max_report_len = max_offset;
+ }
+
+ ts->usage_table_populated = true;
+
+ return max_report_len;
+}
+
+static int axiom_discover(struct axiom_data *ts)
+{
+ int error;
+
+ /*
+ * Fetch the first page of usage u31 to get the
+ * device information and the number of usages
+ */
+ error = axiom_read(ts, AXIOM_DEVINFO_USAGE_ID, 0, &ts->devinfo, AXIOM_U31_PAGE0_LENGTH);
+ if (error)
+ return error;
+
+ dev_dbg(ts->dev, " Boot Mode : %s\n",
+ FIELD_GET(AXIOM_U31_BOOTMODE_MASK,
+ le16_to_cpu(ts->devinfo.device_id)) ? "BLP" : "TCP");
+ dev_dbg(ts->dev, " Device ID : %04lx\n",
+ FIELD_GET(AXIOM_U31_DEVID_MASK, le16_to_cpu(ts->devinfo.device_id)));
+ dev_dbg(ts->dev, " Firmware Rev : %02x.%02x\n", ts->devinfo.fw_major,
+ ts->devinfo.fw_minor);
+ dev_dbg(ts->dev, " Bootloader Rev : %02x.%02x\n", ts->devinfo.bootloader_fw_major,
+ ts->devinfo.bootloader_fw_minor);
+ dev_dbg(ts->dev, " FW Extra Info : %04x\n", ts->devinfo.fw_info_extra);
+ dev_dbg(ts->dev, " Silicon : %04x\n", le16_to_cpu(ts->devinfo.jedec_id));
+ dev_dbg(ts->dev, " Number usages : %04x\n", ts->devinfo.num_usages);
+
+ ts->max_report_len = axiom_populate_usage_table(ts);
+ if (!ts->max_report_len || !ts->devinfo.num_usages ||
+ ts->max_report_len > AXIOM_MAX_REPORT_LEN) {
+ dev_err(ts->dev, "Invalid report length or usages number");
+ return -EINVAL;
+ }
+
+ dev_dbg(ts->dev, "Max Report Length: %u\n", ts->max_report_len);
+
+ return 0;
+}
+
+/*
+ * Support function to axiom_process_u41_report.
+ * Generates input-subsystem events for every target.
+ * After calling this function the caller shall issue
+ * a Sync to the input sub-system.
+ */
+static bool axiom_process_u41_report_target(struct axiom_data *ts,
+ struct axiom_target_report *target)
+{
+ struct input_dev *input_dev = ts->input_dev;
+ struct axiom_u41_target *target_prev_state;
+ enum axiom_target_state current_state;
+ int id;
+
+ /* Verify the target index */
+ if (target->index >= AXIOM_U41_MAX_TARGETS) {
+ dev_err(ts->dev, "Invalid target index! %u\n", target->index);
+ return false;
+ }
+
+ target_prev_state = &ts->targets[target->index];
+
+ current_state = AXIOM_TARGET_STATE_NOT_PRESENT;
+
+ if (target->present) {
+ if (target->z >= 0)
+ current_state = AXIOM_TARGET_STATE_TOUCHING;
+ else if (target->z > AXIOM_PROX_LEVEL && target->z < 0)
+ current_state = AXIOM_TARGET_STATE_HOVER;
+ else if (target->z == AXIOM_PROX_LEVEL)
+ current_state = AXIOM_TARGET_STATE_PROX;
+ }
+
+ if (target_prev_state->state == current_state &&
+ target_prev_state->x == target->x &&
+ target_prev_state->y == target->y &&
+ target_prev_state->z == target->z)
+ return false;
+
+ id = target->index;
+
+ dev_dbg(ts->dev, "U41 Target T%u, present:%u, x:%u, y:%u, z:%d\n",
+ target->index, target->present,
+ target->x, target->y, target->z);
+
+ switch (current_state) {
+ case AXIOM_TARGET_STATE_NOT_PRESENT:
+ case AXIOM_TARGET_STATE_PROX:
+ if (!target_prev_state->insert)
+ break;
+ target_prev_state->insert = false;
+
+ if (!id)
+ input_report_key(input_dev, BTN_TOUCH, 0);
+
+ input_mt_report_slot_inactive(input_dev);
+ /*
+ * make sure the previous coordinates are
+ * all off screen when the finger comes back
+ */
+ target->x = 65535;
+ target->y = 65535;
+ target->z = AXIOM_PROX_LEVEL;
+ break;
+ case AXIOM_TARGET_STATE_HOVER:
+ case AXIOM_TARGET_STATE_TOUCHING:
+ target_prev_state->insert = true;
+ input_report_abs(input_dev, ABS_MT_TRACKING_ID, id);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, target->x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, target->y);
+
+ if (current_state == AXIOM_TARGET_STATE_TOUCHING) {
+ input_report_abs(input_dev, ABS_MT_DISTANCE, 0);
+ input_report_abs(input_dev, ABS_DISTANCE, 0);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, target->z);
+ input_report_abs(input_dev, ABS_PRESSURE, target->z);
+ } else {
+ input_report_abs(input_dev, ABS_MT_DISTANCE, -target->z);
+ input_report_abs(input_dev, ABS_DISTANCE, -target->z);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, 0);
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ }
+
+ if (!id)
+ input_report_key(input_dev, BTN_TOUCH, (current_state ==
+ AXIOM_TARGET_STATE_TOUCHING));
+ break;
+ default:
+ break;
+ }
+
+ target_prev_state->state = current_state;
+ target_prev_state->x = target->x;
+ target_prev_state->y = target->y;
+ target_prev_state->z = target->z;
+
+ return true;
+}
+
+/*
+ * U41 is the output report of the 2D CTS and contains the status of targets
+ * (including contacts and pre-contacts) along with their X,Y,Z values.
+ * When a target has been removed (no longer detected),
+ * the corresponding X,Y,Z values will be zeroed.
+ */
+static bool axiom_process_u41_report(struct axiom_data *ts, u8 *rx_buf)
+{
+ struct axiom_target_report target;
+ bool update_done = false;
+ u16 target_status;
+ int i;
+
+ target_status = get_unaligned_le16(rx_buf + 1);
+
+ for (i = 0; i < AXIOM_U41_MAX_TARGETS; i++) {
+ u8 *target_step = &rx_buf[i * 4];
+
+ target.index = i;
+ input_mt_slot(ts->input_dev, i);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
+ target.present = ((target_status & (1 << i)) != 0) ? 1 : 0;
+ target.x = get_unaligned_le16(target_step + 3);
+ target.y = get_unaligned_le16(target_step + 5);
+ target.z = (s8)(rx_buf[i + 43]);
+ touchscreen_report_pos(ts->input_dev, &ts->prop, target.x, target.y, true);
+ update_done |= axiom_process_u41_report_target(ts, &target);
+ }
+
+ return update_done;
+}
+
+/*
+ * U46 report contains a low level measurement data generated by the capacitive
+ * displacement sensor (CDS) algorithms from the auxiliary channels.
+ * This information is useful when tuning multi-press to assess mechanical
+ * consistency in the unit's construction.
+ */
+static void axiom_process_u46_report(struct axiom_data *ts, u8 *rx_buf)
+{
+ struct input_dev *input_dev = ts->input_dev;
+ u32 event_value;
+ u16 aux_value;
+ int i;
+
+ for (i = 0; i < AXIOM_U46_AUX_CHANNELS; i++) {
+ u8 *target_step = &rx_buf[i * 2];
+
+ input_mt_slot(input_dev, i);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
+ aux_value = get_unaligned_le16(target_step + 1) & AXIOM_U46_AUX_MASK;
+ event_value = (i << 16) | (aux_value);
+ input_event(input_dev, EV_MSC, MSC_RAW, event_value);
+ }
+}
+
+/*
+ * Validates the crc and demultiplexes the axiom reports to the appropriate
+ * report handler
+ */
+static int axiom_handle_events(struct axiom_data *ts)
+{
+ struct input_dev *input_dev = ts->input_dev;
+ u8 *report_data = ts->rx_buf;
+ struct device *dev = ts->dev;
+ u16 crc_report;
+ u8 *crc_bytes;
+ u16 crc_calc;
+ int error;
+ u8 len;
+
+ error = axiom_read(ts, AXIOM_REPORT_USAGE_ID, 0, report_data, ts->max_report_len);
+ if (error)
+ return error;
+
+ len = (report_data[0] & AXIOM_COMMS_REPORT_LEN_MASK) << 1;
+ if (len <= 2) {
+ dev_err(dev, "Zero length report discarded.\n");
+ return -ENODATA;
+ }
+
+ /* Validate the report CRC */
+ crc_bytes = &report_data[len];
+
+ crc_report = get_unaligned_le16(crc_bytes - 2);
+ /* Length is in 16 bit words and remove the size of the CRC16 itself */
+ crc_calc = crc16(0, report_data, (len - 2));
+
+ if (crc_calc != crc_report) {
+ dev_err(dev,
+ "CRC mismatch! Expected: %#x, Calculated CRC: %#x.\n",
+ crc_report, crc_calc);
+ return -EINVAL;
+ }
+
+ switch (report_data[1]) {
+ case AXIOM_USAGE_2DCTS_REPORT_ID:
+ if (axiom_process_u41_report(ts, &report_data[1])) {
+ input_mt_sync_frame(input_dev);
+ input_sync(input_dev);
+ }
+ break;
+
+ case AXIOM_USAGE_2AUX_REPORT_ID:
+ /* This is an aux report (force) */
+ axiom_process_u46_report(ts, &report_data[1]);
+ input_mt_sync(input_dev);
+ input_sync(input_dev);
+ break;
+
+ case AXIOM_USAGE_2HB_REPORT_ID:
+ /* This is a heartbeat report */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void axiom_i2c_poll(struct input_dev *input_dev)
+{
+ struct axiom_data *ts = input_get_drvdata(input_dev);
+
+ axiom_handle_events(ts);
+}
+
+static irqreturn_t axiom_irq(int irq, void *dev_id)
+{
+ struct axiom_data *ts = dev_id;
+
+ axiom_handle_events(ts);
+
+ return IRQ_HANDLED;
+}
+
+static void axiom_reset(struct gpio_desc *reset_gpio)
+{
+ gpiod_set_value_cansleep(reset_gpio, 1);
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(reset_gpio, 0);
+ msleep(AXIOM_STARTUP_TIME_MS);
+}
+
+static int axiom_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct input_dev *input_dev;
+ struct axiom_data *ts;
+ u32 poll_interval;
+ int target;
+ int error;
+
+ ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, ts);
+ ts->client = client;
+ ts->dev = dev;
+
+ ts->regmap = devm_regmap_init_i2c(client, &axiom_i2c_regmap_config);
+ error = PTR_ERR_OR_ZERO(ts->regmap);
+ if (error) {
+ dev_err(dev, "Failed to initialize regmap: %d\n", error);
+ return error;
+ }
+
+ ts->vddi = devm_regulator_get(dev, "VDDI");
+ if (IS_ERR(ts->vddi))
+ return dev_err_probe(&client->dev, PTR_ERR(ts->vddi),
+ "Failed to enable VDDI regulator\n");
+
+ ts->vdda = devm_regulator_get(dev, "VDDA");
+ if (IS_ERR(ts->vdda))
+ return dev_err_probe(&client->dev, PTR_ERR(ts->vdda),
+ "Failed to enable VDDA regulator\n");
+
+ ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(ts->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ts->reset_gpio), "failed to get reset GPIO\n");
+
+ if (ts->reset_gpio)
+ axiom_reset(ts->reset_gpio);
+ else
+ msleep(AXIOM_STARTUP_TIME_MS);
+
+ error = axiom_discover(ts);
+ if (error)
+ return dev_err_probe(dev, error, "Failed touchscreen discover\n");
+
+ input_dev = devm_input_allocate_device(ts->dev);
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_dev->name = "TouchNetix axiom Touchscreen";
+ input_dev->phys = "input/axiom_ts";
+
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 65535, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 65535, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_DISTANCE, 0, 127, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 127, 0, 0);
+
+ touchscreen_parse_properties(input_dev, true, &ts->prop);
+
+ /* Registers the axiom device as a touchscreen instead of a mouse pointer */
+ error = input_mt_init_slots(input_dev, AXIOM_U41_MAX_TARGETS, INPUT_MT_DIRECT);
+ if (error)
+ return error;
+
+ /* Enables the raw data for up to 4 force channels to be sent to the input subsystem */
+ set_bit(EV_REL, input_dev->evbit);
+ set_bit(EV_MSC, input_dev->evbit);
+ /* Declare that we support "RAW" Miscellaneous events */
+ set_bit(MSC_RAW, input_dev->mscbit);
+
+ ts->input_dev = input_dev;
+ input_set_drvdata(ts->input_dev, ts);
+
+ /* Ensure that all reports are initialised to not be present. */
+ for (target = 0; target < AXIOM_U41_MAX_TARGETS; target++)
+ ts->targets[target].state = AXIOM_TARGET_STATE_NOT_PRESENT;
+
+ error = devm_request_threaded_irq(dev, client->irq, NULL,
+ axiom_irq, IRQF_ONESHOT, dev_name(dev), ts);
+ if (error) {
+ dev_info(dev, "Request irq failed, falling back to polling mode");
+
+ error = input_setup_polling(input_dev, axiom_i2c_poll);
+ if (error)
+ return dev_err_probe(ts->dev, error, "Unable to set up polling mode\n");
+
+ if (!device_property_read_u32(ts->dev, "poll-interval", &poll_interval))
+ input_set_poll_interval(input_dev, poll_interval);
+ else
+ input_set_poll_interval(input_dev, POLL_INTERVAL_DEFAULT_MS);
+ }
+
+ return input_register_device(input_dev);
+}
+
+static const struct i2c_device_id axiom_i2c_id_table[] = {
+ { "ax54a" },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, axiom_i2c_id_table);
+
+static const struct of_device_id axiom_i2c_of_match[] = {
+ { .compatible = "touchnetix,ax54a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, axiom_i2c_of_match);
+
+static struct i2c_driver axiom_i2c_driver = {
+ .driver = {
+ .name = "axiom",
+ .of_match_table = axiom_i2c_of_match,
+ },
+ .id_table = axiom_i2c_id_table,
+ .probe = axiom_i2c_probe,
+};
+module_i2c_driver(axiom_i2c_driver);
+
+MODULE_AUTHOR("Bart Prescott <bartp@baasheep.co.uk>");
+MODULE_AUTHOR("Pedro Torruella <pedro.torruella@touchnetix.com>");
+MODULE_AUTHOR("Mark Satterthwaite <mark.satterthwaite@touchnetix.com>");
+MODULE_AUTHOR("Hannah Rossiter <hannah.rossiter@touchnetix.com>");
+MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
+MODULE_DESCRIPTION("TouchNetix axiom touchscreen I2C bus driver");
+MODULE_LICENSE("GPL");
--
2.25.1
^ permalink raw reply related
* [PATCH v10 2/3] dt-bindings: input: Add TouchNetix axiom touchscreen
From: Kamel Bouhara @ 2024-04-19 8:33 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Marco Felsch, Jeff LaBundy
Cc: catalin.popescu, mark.satterthwaite, Thomas Petazzoni,
Gregory Clement, bsp-development.geo, Kamel Bouhara,
Krzysztof Kozlowski
In-Reply-To: <20240419083342.61199-1-kamel.bouhara@bootlin.com>
Add the TouchNetix axiom I2C touchscreen device tree bindings
documentation.
Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
.../input/touchscreen/touchnetix,ax54a.yaml | 62 +++++++++++++++++++
MAINTAINERS | 6 ++
2 files changed, 68 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
diff --git a/Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml b/Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
new file mode 100644
index 000000000000..66229a4d6f15
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/touchscreen/touchnetix,ax54a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TouchNetix Axiom series touchscreen controller
+
+maintainers:
+ - Kamel Bouhara <kamel.bouhara@bootlin.com>
+
+allOf:
+ - $ref: /schemas/input/touchscreen/touchscreen.yaml#
+ - $ref: /schemas/input/input.yaml#
+
+properties:
+ compatible:
+ const: touchnetix,ax54a
+
+ reg:
+ const: 0x66
+
+ interrupts:
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+
+ vdda-supply:
+ description: Analog power supply regulator on VDDA pin
+
+ vddi-supply:
+ description: I/O power supply regulator on VDDI pin
+
+required:
+ - compatible
+ - reg
+ - vdda-supply
+ - vddi-supply
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ touchscreen@66 {
+ compatible = "touchnetix,ax54a";
+ reg = <0x66>;
+ interrupt-parent = <&gpio2>;
+ interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
+ reset-gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>;
+ vdda-supply = <&vdda_reg>;
+ vddi-supply = <&vddi_reg>;
+ poll-interval = <20>;
+ };
+ };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 7c121493f43d..3c59cb760071 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22419,6 +22419,12 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/toshiba-wmi.c
+TOUCHNETIX AXIOM I2C TOUCHSCREEN DRIVER
+M: Kamel Bouhara <kamel.bouhara@bootlin.com>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
+
TPM DEVICE DRIVER
M: Peter Huewe <peterhuewe@gmx.de>
M: Jarkko Sakkinen <jarkko@kernel.org>
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v10 3/3] Input: Add TouchNetix axiom i2c touchscreen driver
From: Marco Felsch @ 2024-04-19 9:28 UTC (permalink / raw)
To: Kamel Bouhara
Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Jeff LaBundy, catalin.popescu, mark.satterthwaite,
Thomas Petazzoni, Gregory Clement, bsp-development.geo
In-Reply-To: <20240419083342.61199-4-kamel.bouhara@bootlin.com>
Hi Kamel,
thank you for the patch. Again just a rough review.
On 24-04-19, Kamel Bouhara wrote:
> Add a new driver for the TouchNetix's axiom family of
> touchscreen controllers. This driver only supports i2c
> and can be later adapted for SPI and USB support.
>
> Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
> ---
...
> +static int axiom_i2c_probe(struct i2c_client *client)
> +{
> + struct device *dev = &client->dev;
> + struct input_dev *input_dev;
> + struct axiom_data *ts;
> + u32 poll_interval;
> + int target;
> + int error;
> +
> + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
> + if (!ts)
> + return -ENOMEM;
> +
> + i2c_set_clientdata(client, ts);
> + ts->client = client;
> + ts->dev = dev;
> +
> + ts->regmap = devm_regmap_init_i2c(client, &axiom_i2c_regmap_config);
> + error = PTR_ERR_OR_ZERO(ts->regmap);
> + if (error) {
> + dev_err(dev, "Failed to initialize regmap: %d\n", error);
> + return error;
> + }
> +
> + ts->vddi = devm_regulator_get(dev, "VDDI");
This does not match the dt-bindings.
> + if (IS_ERR(ts->vddi))
> + return dev_err_probe(&client->dev, PTR_ERR(ts->vddi),
> + "Failed to enable VDDI regulator\n");
> +
> + ts->vdda = devm_regulator_get(dev, "VDDA");
Here as well.
> + if (IS_ERR(ts->vdda))
> + return dev_err_probe(&client->dev, PTR_ERR(ts->vdda),
> + "Failed to enable VDDA regulator\n");
Now we handle it as always but..
> + ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
> + if (IS_ERR(ts->reset_gpio))
> + return dev_err_probe(dev, PTR_ERR(ts->reset_gpio), "failed to get reset GPIO\n");
> +
> + if (ts->reset_gpio)
> + axiom_reset(ts->reset_gpio);
> + else
> + msleep(AXIOM_STARTUP_TIME_MS);
still the reset is useless since you never enabled the regulators. So
either use devm_regulator_get_enable() or you do the enable/disable
separate via regulator_enable()/disable(). If there are no strict
enablement restrictions like orders and timings you could also make use
of the regulator_bulk API (e.g. devm_regulator_bulk_get_enable).
Regards,
Marco
> +
> + error = axiom_discover(ts);
> + if (error)
> + return dev_err_probe(dev, error, "Failed touchscreen discover\n");
> +
> + input_dev = devm_input_allocate_device(ts->dev);
> + if (!input_dev)
> + return -ENOMEM;
> +
> + input_dev->name = "TouchNetix axiom Touchscreen";
> + input_dev->phys = "input/axiom_ts";
> +
> + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 65535, 0, 0);
> + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 65535, 0, 0);
> + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
> + input_set_abs_params(input_dev, ABS_MT_DISTANCE, 0, 127, 0, 0);
> + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 127, 0, 0);
> +
> + touchscreen_parse_properties(input_dev, true, &ts->prop);
> +
> + /* Registers the axiom device as a touchscreen instead of a mouse pointer */
> + error = input_mt_init_slots(input_dev, AXIOM_U41_MAX_TARGETS, INPUT_MT_DIRECT);
> + if (error)
> + return error;
> +
> + /* Enables the raw data for up to 4 force channels to be sent to the input subsystem */
> + set_bit(EV_REL, input_dev->evbit);
> + set_bit(EV_MSC, input_dev->evbit);
> + /* Declare that we support "RAW" Miscellaneous events */
> + set_bit(MSC_RAW, input_dev->mscbit);
> +
> + ts->input_dev = input_dev;
> + input_set_drvdata(ts->input_dev, ts);
> +
> + /* Ensure that all reports are initialised to not be present. */
> + for (target = 0; target < AXIOM_U41_MAX_TARGETS; target++)
> + ts->targets[target].state = AXIOM_TARGET_STATE_NOT_PRESENT;
> +
> + error = devm_request_threaded_irq(dev, client->irq, NULL,
> + axiom_irq, IRQF_ONESHOT, dev_name(dev), ts);
> + if (error) {
> + dev_info(dev, "Request irq failed, falling back to polling mode");
> +
> + error = input_setup_polling(input_dev, axiom_i2c_poll);
> + if (error)
> + return dev_err_probe(ts->dev, error, "Unable to set up polling mode\n");
> +
> + if (!device_property_read_u32(ts->dev, "poll-interval", &poll_interval))
> + input_set_poll_interval(input_dev, poll_interval);
> + else
> + input_set_poll_interval(input_dev, POLL_INTERVAL_DEFAULT_MS);
> + }
> +
> + return input_register_device(input_dev);
> +}
> +
> +static const struct i2c_device_id axiom_i2c_id_table[] = {
> + { "ax54a" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(i2c, axiom_i2c_id_table);
> +
> +static const struct of_device_id axiom_i2c_of_match[] = {
> + { .compatible = "touchnetix,ax54a", },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, axiom_i2c_of_match);
> +
> +static struct i2c_driver axiom_i2c_driver = {
> + .driver = {
> + .name = "axiom",
> + .of_match_table = axiom_i2c_of_match,
> + },
> + .id_table = axiom_i2c_id_table,
> + .probe = axiom_i2c_probe,
> +};
> +module_i2c_driver(axiom_i2c_driver);
> +
> +MODULE_AUTHOR("Bart Prescott <bartp@baasheep.co.uk>");
> +MODULE_AUTHOR("Pedro Torruella <pedro.torruella@touchnetix.com>");
> +MODULE_AUTHOR("Mark Satterthwaite <mark.satterthwaite@touchnetix.com>");
> +MODULE_AUTHOR("Hannah Rossiter <hannah.rossiter@touchnetix.com>");
> +MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
> +MODULE_DESCRIPTION("TouchNetix axiom touchscreen I2C bus driver");
> +MODULE_LICENSE("GPL");
> --
> 2.25.1
>
>
^ permalink raw reply
* Re: [PATCH v10 3/3] Input: Add TouchNetix axiom i2c touchscreen driver
From: Kamel BOUHARA @ 2024-04-19 12:32 UTC (permalink / raw)
To: Marco Felsch
Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Jeff LaBundy, catalin.popescu, mark.satterthwaite,
Thomas Petazzoni, Gregory Clement, bsp-development.geo
In-Reply-To: <20240419092826.2gq72etn4fh4q7ph@pengutronix.de>
Le 2024-04-19 11:28, Marco Felsch a écrit :
> Hi Kamel,
>
Hi,
> thank you for the patch. Again just a rough review.
>
> On 24-04-19, Kamel Bouhara wrote:
>> Add a new driver for the TouchNetix's axiom family of
>> touchscreen controllers. This driver only supports i2c
>> and can be later adapted for SPI and USB support.
>>
>> Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
>> ---
>
> ...
>
>> +static int axiom_i2c_probe(struct i2c_client *client)
>> +{
>> + struct device *dev = &client->dev;
>> + struct input_dev *input_dev;
>> + struct axiom_data *ts;
>> + u32 poll_interval;
>> + int target;
>> + int error;
>> +
>> + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
>> + if (!ts)
>> + return -ENOMEM;
>> +
>> + i2c_set_clientdata(client, ts);
>> + ts->client = client;
>> + ts->dev = dev;
>> +
>> + ts->regmap = devm_regmap_init_i2c(client, &axiom_i2c_regmap_config);
>> + error = PTR_ERR_OR_ZERO(ts->regmap);
>> + if (error) {
>> + dev_err(dev, "Failed to initialize regmap: %d\n", error);
>> + return error;
>> + }
>> +
>> + ts->vddi = devm_regulator_get(dev, "VDDI");
>
> This does not match the dt-bindings.
>
>> + if (IS_ERR(ts->vddi))
>> + return dev_err_probe(&client->dev, PTR_ERR(ts->vddi),
>> + "Failed to enable VDDI regulator\n");
>> +
>> + ts->vdda = devm_regulator_get(dev, "VDDA");
>
> Here as well.
>
>> + if (IS_ERR(ts->vdda))
>> + return dev_err_probe(&client->dev, PTR_ERR(ts->vdda),
>> + "Failed to enable VDDA regulator\n");
>
> Now we handle it as always but..
>
>> + ts->reset_gpio = devm_gpiod_get_optional(dev, "reset",
>> GPIOD_OUT_HIGH);
>> + if (IS_ERR(ts->reset_gpio))
>> + return dev_err_probe(dev, PTR_ERR(ts->reset_gpio), "failed to get
>> reset GPIO\n");
>> +
>> + if (ts->reset_gpio)
>> + axiom_reset(ts->reset_gpio);
>> + else
>> + msleep(AXIOM_STARTUP_TIME_MS);
>
> still the reset is useless since you never enabled the regulators. So
> either use devm_regulator_get_enable() or you do the enable/disable
> separate via regulator_enable()/disable(). If there are no strict
> enablement restrictions like orders and timings you could also make use
> of the regulator_bulk API (e.g. devm_regulator_bulk_get_enable).
I will just go with the devm_regulator_get_enable() solution in v11,
regulator have to be enabled
before reset is asserted here.
Thanks again!
Kamel
>
> Regards,
> Marco
>
>> +
>> + error = axiom_discover(ts);
>> + if (error)
>> + return dev_err_probe(dev, error, "Failed touchscreen discover\n");
>> +
>> + input_dev = devm_input_allocate_device(ts->dev);
>> + if (!input_dev)
>> + return -ENOMEM;
>> +
>> + input_dev->name = "TouchNetix axiom Touchscreen";
>> + input_dev->phys = "input/axiom_ts";
>> +
>> + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 65535, 0, 0);
>> + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 65535, 0, 0);
>> + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0,
>> 0);
>> + input_set_abs_params(input_dev, ABS_MT_DISTANCE, 0, 127, 0, 0);
>> + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 127, 0, 0);
>> +
>> + touchscreen_parse_properties(input_dev, true, &ts->prop);
>> +
>> + /* Registers the axiom device as a touchscreen instead of a mouse
>> pointer */
>> + error = input_mt_init_slots(input_dev, AXIOM_U41_MAX_TARGETS,
>> INPUT_MT_DIRECT);
>> + if (error)
>> + return error;
>> +
>> + /* Enables the raw data for up to 4 force channels to be sent to the
>> input subsystem */
>> + set_bit(EV_REL, input_dev->evbit);
>> + set_bit(EV_MSC, input_dev->evbit);
>> + /* Declare that we support "RAW" Miscellaneous events */
>> + set_bit(MSC_RAW, input_dev->mscbit);
>> +
>> + ts->input_dev = input_dev;
>> + input_set_drvdata(ts->input_dev, ts);
>> +
>> + /* Ensure that all reports are initialised to not be present. */
>> + for (target = 0; target < AXIOM_U41_MAX_TARGETS; target++)
>> + ts->targets[target].state = AXIOM_TARGET_STATE_NOT_PRESENT;
>> +
>> + error = devm_request_threaded_irq(dev, client->irq, NULL,
>> + axiom_irq, IRQF_ONESHOT, dev_name(dev), ts);
>> + if (error) {
>> + dev_info(dev, "Request irq failed, falling back to polling mode");
>> +
>> + error = input_setup_polling(input_dev, axiom_i2c_poll);
>> + if (error)
>> + return dev_err_probe(ts->dev, error, "Unable to set up polling
>> mode\n");
>> +
>> + if (!device_property_read_u32(ts->dev, "poll-interval",
>> &poll_interval))
>> + input_set_poll_interval(input_dev, poll_interval);
>> + else
>> + input_set_poll_interval(input_dev, POLL_INTERVAL_DEFAULT_MS);
>> + }
>> +
>> + return input_register_device(input_dev);
>> +}
>> +
>> +static const struct i2c_device_id axiom_i2c_id_table[] = {
>> + { "ax54a" },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(i2c, axiom_i2c_id_table);
>> +
>> +static const struct of_device_id axiom_i2c_of_match[] = {
>> + { .compatible = "touchnetix,ax54a", },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(of, axiom_i2c_of_match);
>> +
>> +static struct i2c_driver axiom_i2c_driver = {
>> + .driver = {
>> + .name = "axiom",
>> + .of_match_table = axiom_i2c_of_match,
>> + },
>> + .id_table = axiom_i2c_id_table,
>> + .probe = axiom_i2c_probe,
>> +};
>> +module_i2c_driver(axiom_i2c_driver);
>> +
>> +MODULE_AUTHOR("Bart Prescott <bartp@baasheep.co.uk>");
>> +MODULE_AUTHOR("Pedro Torruella <pedro.torruella@touchnetix.com>");
>> +MODULE_AUTHOR("Mark Satterthwaite
>> <mark.satterthwaite@touchnetix.com>");
>> +MODULE_AUTHOR("Hannah Rossiter <hannah.rossiter@touchnetix.com>");
>> +MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
>> +MODULE_DESCRIPTION("TouchNetix axiom touchscreen I2C bus driver");
>> +MODULE_LICENSE("GPL");
>> --
>> 2.25.1
>>
>>
--
--
Kamel Bouhara, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply
* [PATCH v11 1/3] dt-bindings: vendor-prefixes: Add TouchNetix AS
From: Kamel Bouhara @ 2024-04-19 12:38 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Marco Felsch, Jeff LaBundy
Cc: catalin.popescu, mark.satterthwaite, Thomas Petazzoni,
Gregory Clement, bsp-development.geo, Kamel Bouhara,
Krzysztof Kozlowski
In-Reply-To: <20240419123829.120396-1-kamel.bouhara@bootlin.com>
Add vendor prefix for TouchNetix AS (https://www.touchnetix.com/products/).
Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
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 b97d298b3eb6..33205dea558b 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1481,6 +1481,8 @@ patternProperties:
description: Toradex AG
"^toshiba,.*":
description: Toshiba Corporation
+ "^touchnetix,.*":
+ description: TouchNetix AS
"^toumaz,.*":
description: Toumaz
"^tpk,.*":
--
2.25.1
^ permalink raw reply related
* [PATCH v11 2/3] dt-bindings: input: Add TouchNetix axiom touchscreen
From: Kamel Bouhara @ 2024-04-19 12:38 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Marco Felsch, Jeff LaBundy
Cc: catalin.popescu, mark.satterthwaite, Thomas Petazzoni,
Gregory Clement, bsp-development.geo, Kamel Bouhara,
Krzysztof Kozlowski
In-Reply-To: <20240419123829.120396-1-kamel.bouhara@bootlin.com>
Add the TouchNetix axiom I2C touchscreen device tree bindings
documentation.
Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
.../input/touchscreen/touchnetix,ax54a.yaml | 62 +++++++++++++++++++
MAINTAINERS | 6 ++
2 files changed, 68 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
diff --git a/Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml b/Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
new file mode 100644
index 000000000000..66229a4d6f15
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/touchscreen/touchnetix,ax54a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TouchNetix Axiom series touchscreen controller
+
+maintainers:
+ - Kamel Bouhara <kamel.bouhara@bootlin.com>
+
+allOf:
+ - $ref: /schemas/input/touchscreen/touchscreen.yaml#
+ - $ref: /schemas/input/input.yaml#
+
+properties:
+ compatible:
+ const: touchnetix,ax54a
+
+ reg:
+ const: 0x66
+
+ interrupts:
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+
+ vdda-supply:
+ description: Analog power supply regulator on VDDA pin
+
+ vddi-supply:
+ description: I/O power supply regulator on VDDI pin
+
+required:
+ - compatible
+ - reg
+ - vdda-supply
+ - vddi-supply
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ touchscreen@66 {
+ compatible = "touchnetix,ax54a";
+ reg = <0x66>;
+ interrupt-parent = <&gpio2>;
+ interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
+ reset-gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>;
+ vdda-supply = <&vdda_reg>;
+ vddi-supply = <&vddi_reg>;
+ poll-interval = <20>;
+ };
+ };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index c23fda1aa1f0..d8208dc23a98 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22414,6 +22414,12 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/toshiba-wmi.c
+TOUCHNETIX AXIOM I2C TOUCHSCREEN DRIVER
+M: Kamel Bouhara <kamel.bouhara@bootlin.com>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
+
TPM DEVICE DRIVER
M: Peter Huewe <peterhuewe@gmx.de>
M: Jarkko Sakkinen <jarkko@kernel.org>
--
2.25.1
^ permalink raw reply related
* [PATCH v11 0/3] Input: Add TouchNetix axiom touchscreen driver
From: Kamel Bouhara @ 2024-04-19 12:38 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Marco Felsch, Jeff LaBundy
Cc: catalin.popescu, mark.satterthwaite, Thomas Petazzoni,
Gregory Clement, bsp-development.geo, Kamel Bouhara
Add a new driver for the TouchNetix's axiom family of touchscreen
controller. This driver only support i2c and can be later adapted for
SPI and USB support.
--
Changes in v11:
- Fix regulators name to match dt-binding
- Enable regulators before reset is asserted
Changes in v10:
- Set regulators as required
- Enable power supply before reset
- Fix ref count due to regulator requested twice
- Rebase on v6.9-rc4
Changes in v9:
- Fix issue reported in https://lore.kernel.org/oe-kbuild-all/202402201157.BKo97uWl-lkp@intel.com/
- Rebase on v6.8-rc2
Changes in v8:
- Fix missing call to input_report_slot_state()
- Fix issue reported in https://lore.kernel.org/oe-kbuild-all/202402020623.8T1Ah513-lkp@intel.com/
Changes in v7:
- Remove startup time from dt-binding
- Fix usage table not correctly populated
Changes in v6:
- Fix missing unevaluatedProperties.in dt-binding
- Use __le16 to correctly deal with device endianness
- Use standart kernel types s/char/u8/
- Use regmap api as driver might support spi later
- Use get_unaligned_le16() for the sake of clarity
- Use devm_regulator_enable_optional()
Changes in v5:
- Fix wrong message constructed in axiom_i2c_read
- Delay required between i2c reads is >= 250us
- Do not split report reading in two phases as we'll
have to wait 500us
- Use lower-case in properties names
- Make regulators properties are required in dt-binding
- Fix bug report: https://lore.kernel.org/lkml/202312051457.y3N1q3sZ-lkp@intel.com/
- Fix bug report: https://lore.kernel.org/lkml/6f8e3b64-5b21-4a50-8680-063ef7a93bdb@suswa.mountain/
Changes in v4:
- Cleanup unused headers and macros
- Use standard kernel type
- Namespace structures and functions
- Use packed struct when possible to avoid bitfield operators
- Fix missing break when address is found in axiom_populate_target_address()
- Split reads in two steps for the reports, first length then report
itself so we only read required bytes
- Get poll-interval from devicetree
- Add VDDI/VDDA regulators
- Add a startup delay of 110 ms required after VDDA/VDDI is applied
- Remove axiom_i2c_write() as it is no more used
Changes in v3:
- Remove irq-gpios property in dt-binding
- Use a generic node name
- Fix issues reported in https://lore.kernel.org/oe-kbuild-all/202310100300.oAC2M62R-lkp@intel.com/
Changes in v2:
- Add device tree binding documentation
- Move core functions in axiom_i2c as we only care about i2c support now
- Use static function when required
- Use syntax dev_err_probe()
- Add an hardware based reset
Kamel Bouhara (3):
dt-bindings: vendor-prefixes: Add TouchNetix AS
dt-bindings: input: Add TouchNetix axiom touchscreen
Input: Add TouchNetix axiom i2c touchscreen driver
.../input/touchscreen/touchnetix,ax54a.yaml | 62 ++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
MAINTAINERS | 8 +
drivers/input/touchscreen/Kconfig | 12 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/touchnetix_axiom.c | 657 ++++++++++++++++++
6 files changed, 742 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
create mode 100644 drivers/input/touchscreen/touchnetix_axiom.c
--
2.25.1
^ permalink raw reply
* [PATCH v11 3/3] Input: Add TouchNetix axiom i2c touchscreen driver
From: Kamel Bouhara @ 2024-04-19 12:38 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Marco Felsch, Jeff LaBundy
Cc: catalin.popescu, mark.satterthwaite, Thomas Petazzoni,
Gregory Clement, bsp-development.geo, Kamel Bouhara
In-Reply-To: <20240419123829.120396-1-kamel.bouhara@bootlin.com>
Add a new driver for the TouchNetix's axiom family of
touchscreen controllers. This driver only supports i2c
and can be later adapted for SPI and USB support.
Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
---
MAINTAINERS | 2 +
drivers/input/touchscreen/Kconfig | 12 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/touchnetix_axiom.c | 657 +++++++++++++++++++
4 files changed, 672 insertions(+)
create mode 100644 drivers/input/touchscreen/touchnetix_axiom.c
diff --git a/MAINTAINERS b/MAINTAINERS
index d8208dc23a98..6b9790414c55 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22416,9 +22416,11 @@ F: drivers/platform/x86/toshiba-wmi.c
TOUCHNETIX AXIOM I2C TOUCHSCREEN DRIVER
M: Kamel Bouhara <kamel.bouhara@bootlin.com>
+M: bsp-development.geo@leica-geosystems.com
L: linux-input@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
+F: drivers/input/touchscreen/touchnetix_axiom.c
TPM DEVICE DRIVER
M: Peter Huewe <peterhuewe@gmx.de>
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index c821fe3ee794..503ccea5c1b0 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -834,6 +834,18 @@ config TOUCHSCREEN_MIGOR
To compile this driver as a module, choose M here: the
module will be called migor_ts.
+config TOUCHSCREEN_TOUCHNETIX_AXIOM
+ tristate "TouchNetix AXIOM based touchscreen controllers"
+ depends on I2C
+ help
+ Say Y here if you have a axiom touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called axiom.
+
config TOUCHSCREEN_TOUCHRIGHT
tristate "Touchright serial touchscreen"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index a81cb5aa21a5..6ce7b804adc7 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -91,6 +91,7 @@ obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o
obj-$(CONFIG_TOUCHSCREEN_SURFACE3_SPI) += surface3_spi.o
obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHNETIX_AXIOM) += touchnetix_axiom.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
obj-$(CONFIG_TOUCHSCREEN_TS4800) += ts4800-ts.o
diff --git a/drivers/input/touchscreen/touchnetix_axiom.c b/drivers/input/touchscreen/touchnetix_axiom.c
new file mode 100644
index 000000000000..09550847392e
--- /dev/null
+++ b/drivers/input/touchscreen/touchnetix_axiom.c
@@ -0,0 +1,657 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TouchNetix axiom Touchscreen Driver
+ *
+ * Copyright (C) 2020-2023 TouchNetix Ltd.
+ *
+ * Author(s): Bart Prescott <bartp@baasheep.co.uk>
+ * Pedro Torruella <pedro.torruella@touchnetix.com>
+ * Mark Satterthwaite <mark.satterthwaite@touchnetix.com>
+ * Hannah Rossiter <hannah.rossiter@touchnetix.com>
+ * Kamel Bouhara <kamel.bouhara@bootlin.com>
+ *
+ */
+#include <linux/bitfield.h>
+#include <linux/crc16.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+
+#include <asm/unaligned.h>
+#define AXIOM_PROX_LEVEL -128
+#define AXIOM_DMA_OPS_DELAY_USEC 250
+#define AXIOM_STARTUP_TIME_MS 110
+/*
+ * Register group u31 has 2 pages for usage table entries.
+ */
+#define AXIOM_U31_MAX_USAGES 0xff
+#define AXIOM_U31_BYTES_PER_USAGE 6
+#define AXIOM_U31_PAGE0_LENGTH 0x0C
+#define AXIOM_U31_BOOTMODE_MASK BIT(7)
+#define AXIOM_U31_DEVID_MASK GENMASK(14, 0)
+
+#define AXIOM_MAX_REPORT_LEN 0x7f
+
+#define AXIOM_CMD_HEADER_READ_MASK BIT(15)
+#define AXIOM_U41_MAX_TARGETS 10
+
+#define AXIOM_U46_AUX_CHANNELS 4
+#define AXIOM_U46_AUX_MASK GENMASK(11, 0)
+
+#define AXIOM_COMMS_MAX_USAGE_PAGES 3
+#define AXIOM_COMMS_PAGE_SIZE 256
+#define AXIOM_COMMS_REPORT_LEN_MASK GENMASK(6, 0)
+
+#define AXIOM_REPORT_USAGE_ID 0x34
+#define AXIOM_DEVINFO_USAGE_ID 0x31
+#define AXIOM_USAGE_2HB_REPORT_ID 0x01
+#define AXIOM_USAGE_2AUX_REPORT_ID 0x46
+#define AXIOM_USAGE_2DCTS_REPORT_ID 0x41
+
+#define AXIOM_PAGE_OFFSET_MASK GENMASK(6, 0)
+
+struct axiom_devinfo {
+ __le16 device_id;
+ u8 fw_minor;
+ u8 fw_major;
+ u8 fw_info_extra;
+ u8 tcp_revision;
+ u8 bootloader_fw_minor;
+ u8 bootloader_fw_major;
+ __le16 jedec_id;
+ u8 num_usages;
+} __packed;
+
+/*
+ * Describes parameters of a specific usage, essentially a single element of
+ * the "Usage Table"
+ */
+struct axiom_usage_entry {
+ u8 id;
+ u8 is_report;
+ u8 start_page;
+ u8 num_pages;
+};
+
+/*
+ * Represents state of a touch or target when detected prior to a touch (eg.
+ * hover or proximity events).
+ */
+enum axiom_target_state {
+ AXIOM_TARGET_STATE_NOT_PRESENT = 0,
+ AXIOM_TARGET_STATE_PROX = 1,
+ AXIOM_TARGET_STATE_HOVER = 2,
+ AXIOM_TARGET_STATE_TOUCHING = 3,
+};
+
+struct axiom_u41_target {
+ enum axiom_target_state state;
+ u16 x;
+ u16 y;
+ s8 z;
+ bool insert;
+ bool touch;
+};
+
+struct axiom_target_report {
+ u8 index;
+ u8 present;
+ u16 x;
+ u16 y;
+ s8 z;
+};
+
+struct axiom_cmd_header {
+ __le16 target_address;
+ __le16 length;
+} __packed;
+
+struct axiom_data {
+ struct axiom_devinfo devinfo;
+ struct device *dev;
+ struct gpio_desc *reset_gpio;
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ u32 max_report_len;
+ u8 rx_buf[AXIOM_COMMS_MAX_USAGE_PAGES * AXIOM_COMMS_PAGE_SIZE];
+ struct axiom_u41_target targets[AXIOM_U41_MAX_TARGETS];
+ struct axiom_usage_entry usage_table[AXIOM_U31_MAX_USAGES];
+ bool usage_table_populated;
+ struct regmap *regmap;
+ struct touchscreen_properties prop;
+};
+
+static const struct regmap_config axiom_i2c_regmap_config = {
+ .reg_bits = 32,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_bits = 8,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+/*
+ * axiom devices are typically configured to report touches at a rate
+ * of 100Hz (10ms) for systems that require polling for reports.
+ * When reports are polled, it will be expected to occasionally
+ * observe the overflow bit being set in the reports.
+ * This indicates that reports are not being read fast enough.
+ */
+#define POLL_INTERVAL_DEFAULT_MS 10
+
+/* Translate usage/page/offset triplet into physical address. */
+static u16 axiom_usage_to_target_address(struct axiom_data *ts, u8 usage, u8 page,
+ char offset)
+{
+ /* At the moment the convention is that u31 is always at physical address 0x0 */
+ if (!ts->usage_table_populated) {
+ if (usage == AXIOM_DEVINFO_USAGE_ID)
+ return ((page << 8) + offset);
+ else
+ return 0xffff;
+ }
+
+ if (page >= ts->usage_table[usage].num_pages) {
+ dev_err(ts->dev, "Invalid usage table! usage: u%02x, page: %02x, offset: %02x\n",
+ usage, page, offset);
+ return 0xffff;
+ }
+
+ return ((ts->usage_table[usage].start_page + page) << 8) + offset;
+}
+
+static int axiom_read(struct axiom_data *ts, u8 usage, u8 page, void *buf, u16 len)
+{
+ struct axiom_cmd_header cmd_header;
+ u32 preamble;
+ int ret;
+
+ cmd_header.target_address = cpu_to_le16(axiom_usage_to_target_address(ts, usage, page, 0));
+ cmd_header.length = cpu_to_le16(len | AXIOM_CMD_HEADER_READ_MASK);
+
+ preamble = get_unaligned_le32(&cmd_header);
+
+ ret = regmap_write(ts->regmap, preamble, 0);
+ if (ret) {
+ dev_err(ts->dev, "failed to write preamble, error %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_raw_read(ts->regmap, 0, buf, len);
+ if (ret) {
+ dev_err(ts->dev, "failed to read target address %04x, error %d\n",
+ cmd_header.target_address, ret);
+ return ret;
+ }
+
+ /* Wait device's DMA operations */
+ usleep_range(AXIOM_DMA_OPS_DELAY_USEC, AXIOM_DMA_OPS_DELAY_USEC + 50);
+
+ return 0;
+}
+
+/*
+ * One of the main purposes for reading the usage table is to identify
+ * which usages reside at which target address.
+ * When performing subsequent reads or writes to AXIOM, the target address
+ * is used to specify which usage is being accessed.
+ * Consider the following discovery code which will build up the usage table.
+ */
+static u32 axiom_populate_usage_table(struct axiom_data *ts)
+{
+ struct axiom_usage_entry *usage_table;
+ u8 *rx_data = ts->rx_buf;
+ u32 max_report_len = 0;
+ u32 usage_id;
+ int error;
+
+ usage_table = ts->usage_table;
+
+ /* Read the second page of usage u31 to get the usage table */
+ error = axiom_read(ts, AXIOM_DEVINFO_USAGE_ID, 1, rx_data,
+ (AXIOM_U31_BYTES_PER_USAGE * ts->devinfo.num_usages));
+
+ if (error)
+ return error;
+
+ for (usage_id = 0; usage_id < ts->devinfo.num_usages; usage_id++) {
+ u16 offset = (usage_id * AXIOM_U31_BYTES_PER_USAGE);
+ u8 id = rx_data[offset + 0];
+ u8 start_page = rx_data[offset + 1];
+ u8 num_pages = rx_data[offset + 2];
+ u32 max_offset = ((rx_data[offset + 3] & AXIOM_PAGE_OFFSET_MASK) + 1) * 2;
+
+ usage_table[id].is_report = !num_pages;
+
+ /* Store the entry into the usage table */
+ usage_table[id].id = id;
+ usage_table[id].start_page = start_page;
+ usage_table[id].num_pages = num_pages;
+
+ dev_dbg(ts->dev, "Usage u%02x Info: %*ph\n", id, AXIOM_U31_BYTES_PER_USAGE,
+ &rx_data[offset]);
+
+ /* Identify the max report length the module will receive */
+ if (usage_table[id].is_report && max_offset > max_report_len)
+ max_report_len = max_offset;
+ }
+
+ ts->usage_table_populated = true;
+
+ return max_report_len;
+}
+
+static int axiom_discover(struct axiom_data *ts)
+{
+ int error;
+
+ /*
+ * Fetch the first page of usage u31 to get the
+ * device information and the number of usages
+ */
+ error = axiom_read(ts, AXIOM_DEVINFO_USAGE_ID, 0, &ts->devinfo, AXIOM_U31_PAGE0_LENGTH);
+ if (error)
+ return error;
+
+ dev_dbg(ts->dev, " Boot Mode : %s\n",
+ FIELD_GET(AXIOM_U31_BOOTMODE_MASK,
+ le16_to_cpu(ts->devinfo.device_id)) ? "BLP" : "TCP");
+ dev_dbg(ts->dev, " Device ID : %04lx\n",
+ FIELD_GET(AXIOM_U31_DEVID_MASK, le16_to_cpu(ts->devinfo.device_id)));
+ dev_dbg(ts->dev, " Firmware Rev : %02x.%02x\n", ts->devinfo.fw_major,
+ ts->devinfo.fw_minor);
+ dev_dbg(ts->dev, " Bootloader Rev : %02x.%02x\n", ts->devinfo.bootloader_fw_major,
+ ts->devinfo.bootloader_fw_minor);
+ dev_dbg(ts->dev, " FW Extra Info : %04x\n", ts->devinfo.fw_info_extra);
+ dev_dbg(ts->dev, " Silicon : %04x\n", le16_to_cpu(ts->devinfo.jedec_id));
+ dev_dbg(ts->dev, " Number usages : %04x\n", ts->devinfo.num_usages);
+
+ ts->max_report_len = axiom_populate_usage_table(ts);
+ if (!ts->max_report_len || !ts->devinfo.num_usages ||
+ ts->max_report_len > AXIOM_MAX_REPORT_LEN) {
+ dev_err(ts->dev, "Invalid report length or usages number");
+ return -EINVAL;
+ }
+
+ dev_dbg(ts->dev, "Max Report Length: %u\n", ts->max_report_len);
+
+ return 0;
+}
+
+/*
+ * Support function to axiom_process_u41_report.
+ * Generates input-subsystem events for every target.
+ * After calling this function the caller shall issue
+ * a Sync to the input sub-system.
+ */
+static bool axiom_process_u41_report_target(struct axiom_data *ts,
+ struct axiom_target_report *target)
+{
+ struct input_dev *input_dev = ts->input_dev;
+ struct axiom_u41_target *target_prev_state;
+ enum axiom_target_state current_state;
+ int id;
+
+ /* Verify the target index */
+ if (target->index >= AXIOM_U41_MAX_TARGETS) {
+ dev_err(ts->dev, "Invalid target index! %u\n", target->index);
+ return false;
+ }
+
+ target_prev_state = &ts->targets[target->index];
+
+ current_state = AXIOM_TARGET_STATE_NOT_PRESENT;
+
+ if (target->present) {
+ if (target->z >= 0)
+ current_state = AXIOM_TARGET_STATE_TOUCHING;
+ else if (target->z > AXIOM_PROX_LEVEL && target->z < 0)
+ current_state = AXIOM_TARGET_STATE_HOVER;
+ else if (target->z == AXIOM_PROX_LEVEL)
+ current_state = AXIOM_TARGET_STATE_PROX;
+ }
+
+ if (target_prev_state->state == current_state &&
+ target_prev_state->x == target->x &&
+ target_prev_state->y == target->y &&
+ target_prev_state->z == target->z)
+ return false;
+
+ id = target->index;
+
+ dev_dbg(ts->dev, "U41 Target T%u, present:%u, x:%u, y:%u, z:%d\n",
+ target->index, target->present,
+ target->x, target->y, target->z);
+
+ switch (current_state) {
+ case AXIOM_TARGET_STATE_NOT_PRESENT:
+ case AXIOM_TARGET_STATE_PROX:
+ if (!target_prev_state->insert)
+ break;
+ target_prev_state->insert = false;
+
+ if (!id)
+ input_report_key(input_dev, BTN_TOUCH, 0);
+
+ input_mt_report_slot_inactive(input_dev);
+ /*
+ * make sure the previous coordinates are
+ * all off screen when the finger comes back
+ */
+ target->x = 65535;
+ target->y = 65535;
+ target->z = AXIOM_PROX_LEVEL;
+ break;
+ case AXIOM_TARGET_STATE_HOVER:
+ case AXIOM_TARGET_STATE_TOUCHING:
+ target_prev_state->insert = true;
+ input_report_abs(input_dev, ABS_MT_TRACKING_ID, id);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, target->x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, target->y);
+
+ if (current_state == AXIOM_TARGET_STATE_TOUCHING) {
+ input_report_abs(input_dev, ABS_MT_DISTANCE, 0);
+ input_report_abs(input_dev, ABS_DISTANCE, 0);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, target->z);
+ input_report_abs(input_dev, ABS_PRESSURE, target->z);
+ } else {
+ input_report_abs(input_dev, ABS_MT_DISTANCE, -target->z);
+ input_report_abs(input_dev, ABS_DISTANCE, -target->z);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, 0);
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ }
+
+ if (!id)
+ input_report_key(input_dev, BTN_TOUCH, (current_state ==
+ AXIOM_TARGET_STATE_TOUCHING));
+ break;
+ default:
+ break;
+ }
+
+ target_prev_state->state = current_state;
+ target_prev_state->x = target->x;
+ target_prev_state->y = target->y;
+ target_prev_state->z = target->z;
+
+ return true;
+}
+
+/*
+ * U41 is the output report of the 2D CTS and contains the status of targets
+ * (including contacts and pre-contacts) along with their X,Y,Z values.
+ * When a target has been removed (no longer detected),
+ * the corresponding X,Y,Z values will be zeroed.
+ */
+static bool axiom_process_u41_report(struct axiom_data *ts, u8 *rx_buf)
+{
+ struct axiom_target_report target;
+ bool update_done = false;
+ u16 target_status;
+ int i;
+
+ target_status = get_unaligned_le16(rx_buf + 1);
+
+ for (i = 0; i < AXIOM_U41_MAX_TARGETS; i++) {
+ u8 *target_step = &rx_buf[i * 4];
+
+ target.index = i;
+ input_mt_slot(ts->input_dev, i);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
+ target.present = ((target_status & (1 << i)) != 0) ? 1 : 0;
+ target.x = get_unaligned_le16(target_step + 3);
+ target.y = get_unaligned_le16(target_step + 5);
+ target.z = (s8)(rx_buf[i + 43]);
+ touchscreen_report_pos(ts->input_dev, &ts->prop, target.x, target.y, true);
+ update_done |= axiom_process_u41_report_target(ts, &target);
+ }
+
+ return update_done;
+}
+
+/*
+ * U46 report contains a low level measurement data generated by the capacitive
+ * displacement sensor (CDS) algorithms from the auxiliary channels.
+ * This information is useful when tuning multi-press to assess mechanical
+ * consistency in the unit's construction.
+ */
+static void axiom_process_u46_report(struct axiom_data *ts, u8 *rx_buf)
+{
+ struct input_dev *input_dev = ts->input_dev;
+ u32 event_value;
+ u16 aux_value;
+ int i;
+
+ for (i = 0; i < AXIOM_U46_AUX_CHANNELS; i++) {
+ u8 *target_step = &rx_buf[i * 2];
+
+ input_mt_slot(input_dev, i);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
+ aux_value = get_unaligned_le16(target_step + 1) & AXIOM_U46_AUX_MASK;
+ event_value = (i << 16) | (aux_value);
+ input_event(input_dev, EV_MSC, MSC_RAW, event_value);
+ }
+}
+
+/*
+ * Validates the crc and demultiplexes the axiom reports to the appropriate
+ * report handler
+ */
+static int axiom_handle_events(struct axiom_data *ts)
+{
+ struct input_dev *input_dev = ts->input_dev;
+ u8 *report_data = ts->rx_buf;
+ struct device *dev = ts->dev;
+ u16 crc_report;
+ u8 *crc_bytes;
+ u16 crc_calc;
+ int error;
+ u8 len;
+
+ error = axiom_read(ts, AXIOM_REPORT_USAGE_ID, 0, report_data, ts->max_report_len);
+ if (error)
+ return error;
+
+ len = (report_data[0] & AXIOM_COMMS_REPORT_LEN_MASK) << 1;
+ if (len <= 2) {
+ dev_err(dev, "Zero length report discarded.\n");
+ return -ENODATA;
+ }
+
+ /* Validate the report CRC */
+ crc_bytes = &report_data[len];
+
+ crc_report = get_unaligned_le16(crc_bytes - 2);
+ /* Length is in 16 bit words and remove the size of the CRC16 itself */
+ crc_calc = crc16(0, report_data, (len - 2));
+
+ if (crc_calc != crc_report) {
+ dev_err(dev,
+ "CRC mismatch! Expected: %#x, Calculated CRC: %#x.\n",
+ crc_report, crc_calc);
+ return -EINVAL;
+ }
+
+ switch (report_data[1]) {
+ case AXIOM_USAGE_2DCTS_REPORT_ID:
+ if (axiom_process_u41_report(ts, &report_data[1])) {
+ input_mt_sync_frame(input_dev);
+ input_sync(input_dev);
+ }
+ break;
+
+ case AXIOM_USAGE_2AUX_REPORT_ID:
+ /* This is an aux report (force) */
+ axiom_process_u46_report(ts, &report_data[1]);
+ input_mt_sync(input_dev);
+ input_sync(input_dev);
+ break;
+
+ case AXIOM_USAGE_2HB_REPORT_ID:
+ /* This is a heartbeat report */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void axiom_i2c_poll(struct input_dev *input_dev)
+{
+ struct axiom_data *ts = input_get_drvdata(input_dev);
+
+ axiom_handle_events(ts);
+}
+
+static irqreturn_t axiom_irq(int irq, void *dev_id)
+{
+ struct axiom_data *ts = dev_id;
+
+ axiom_handle_events(ts);
+
+ return IRQ_HANDLED;
+}
+
+static void axiom_reset(struct gpio_desc *reset_gpio)
+{
+ gpiod_set_value_cansleep(reset_gpio, 1);
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(reset_gpio, 0);
+ msleep(AXIOM_STARTUP_TIME_MS);
+}
+
+static int axiom_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct input_dev *input_dev;
+ struct axiom_data *ts;
+ u32 poll_interval;
+ int target;
+ int error;
+
+ ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, ts);
+ ts->client = client;
+ ts->dev = dev;
+
+ ts->regmap = devm_regmap_init_i2c(client, &axiom_i2c_regmap_config);
+ error = PTR_ERR_OR_ZERO(ts->regmap);
+ if (error) {
+ dev_err(dev, "Failed to initialize regmap: %d\n", error);
+ return error;
+ }
+
+ error = devm_regulator_get_enable(dev, "vddi");
+ if (error)
+ return dev_err_probe(&client->dev, error,
+ "Failed to enable VDDI regulator\n");
+
+ error = devm_regulator_get_enable(dev, "vdda");
+ if (error)
+ return dev_err_probe(&client->dev, error,
+ "Failed to enable VDDA regulator\n");
+
+ ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(ts->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ts->reset_gpio), "failed to get reset GPIO\n");
+
+ if (ts->reset_gpio)
+ axiom_reset(ts->reset_gpio);
+ else
+ msleep(AXIOM_STARTUP_TIME_MS);
+
+ error = axiom_discover(ts);
+ if (error)
+ return dev_err_probe(dev, error, "Failed touchscreen discover\n");
+
+ input_dev = devm_input_allocate_device(ts->dev);
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_dev->name = "TouchNetix axiom Touchscreen";
+ input_dev->phys = "input/axiom_ts";
+
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 65535, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 65535, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_DISTANCE, 0, 127, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 127, 0, 0);
+
+ touchscreen_parse_properties(input_dev, true, &ts->prop);
+
+ /* Registers the axiom device as a touchscreen instead of a mouse pointer */
+ error = input_mt_init_slots(input_dev, AXIOM_U41_MAX_TARGETS, INPUT_MT_DIRECT);
+ if (error)
+ return error;
+
+ /* Enables the raw data for up to 4 force channels to be sent to the input subsystem */
+ set_bit(EV_REL, input_dev->evbit);
+ set_bit(EV_MSC, input_dev->evbit);
+ /* Declare that we support "RAW" Miscellaneous events */
+ set_bit(MSC_RAW, input_dev->mscbit);
+
+ ts->input_dev = input_dev;
+ input_set_drvdata(ts->input_dev, ts);
+
+ /* Ensure that all reports are initialised to not be present. */
+ for (target = 0; target < AXIOM_U41_MAX_TARGETS; target++)
+ ts->targets[target].state = AXIOM_TARGET_STATE_NOT_PRESENT;
+
+ error = devm_request_threaded_irq(dev, client->irq, NULL,
+ axiom_irq, IRQF_ONESHOT, dev_name(dev), ts);
+ if (error) {
+ dev_info(dev, "Request irq failed, falling back to polling mode");
+
+ error = input_setup_polling(input_dev, axiom_i2c_poll);
+ if (error)
+ return dev_err_probe(ts->dev, error, "Unable to set up polling mode\n");
+
+ if (!device_property_read_u32(ts->dev, "poll-interval", &poll_interval))
+ input_set_poll_interval(input_dev, poll_interval);
+ else
+ input_set_poll_interval(input_dev, POLL_INTERVAL_DEFAULT_MS);
+ }
+
+ return input_register_device(input_dev);
+}
+
+static const struct i2c_device_id axiom_i2c_id_table[] = {
+ { "ax54a" },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, axiom_i2c_id_table);
+
+static const struct of_device_id axiom_i2c_of_match[] = {
+ { .compatible = "touchnetix,ax54a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, axiom_i2c_of_match);
+
+static struct i2c_driver axiom_i2c_driver = {
+ .driver = {
+ .name = "axiom",
+ .of_match_table = axiom_i2c_of_match,
+ },
+ .id_table = axiom_i2c_id_table,
+ .probe = axiom_i2c_probe,
+};
+module_i2c_driver(axiom_i2c_driver);
+
+MODULE_AUTHOR("Bart Prescott <bartp@baasheep.co.uk>");
+MODULE_AUTHOR("Pedro Torruella <pedro.torruella@touchnetix.com>");
+MODULE_AUTHOR("Mark Satterthwaite <mark.satterthwaite@touchnetix.com>");
+MODULE_AUTHOR("Hannah Rossiter <hannah.rossiter@touchnetix.com>");
+MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
+MODULE_DESCRIPTION("TouchNetix axiom touchscreen I2C bus driver");
+MODULE_LICENSE("GPL");
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v10 3/3] Input: Add TouchNetix axiom i2c touchscreen driver
From: Kamel BOUHARA @ 2024-04-19 14:02 UTC (permalink / raw)
To: Marco Felsch
Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Jeff LaBundy, catalin.popescu, mark.satterthwaite,
Thomas Petazzoni, Gregory Clement, bsp-development.geo
In-Reply-To: <20240419092826.2gq72etn4fh4q7ph@pengutronix.de>
Le 2024-04-19 11:28, Marco Felsch a écrit :
> Hi Kamel,
>
Hello,
> thank you for the patch. Again just a rough review.
>
> On 24-04-19, Kamel Bouhara wrote:
>> Add a new driver for the TouchNetix's axiom family of
>> touchscreen controllers. This driver only supports i2c
>> and can be later adapted for SPI and USB support.
>>
>> Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
>> ---
>
> ...
>
>> +static int axiom_i2c_probe(struct i2c_client *client)
>> +{
>> + struct device *dev = &client->dev;
>> + struct input_dev *input_dev;
>> + struct axiom_data *ts;
>> + u32 poll_interval;
>> + int target;
>> + int error;
>> +
>> + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
>> + if (!ts)
>> + return -ENOMEM;
>> +
>> + i2c_set_clientdata(client, ts);
>> + ts->client = client;
>> + ts->dev = dev;
>> +
>> + ts->regmap = devm_regmap_init_i2c(client, &axiom_i2c_regmap_config);
>> + error = PTR_ERR_OR_ZERO(ts->regmap);
>> + if (error) {
>> + dev_err(dev, "Failed to initialize regmap: %d\n", error);
>> + return error;
>> + }
>> +
>> + ts->vddi = devm_regulator_get(dev, "VDDI");
>
> This does not match the dt-bindings.
>
>> + if (IS_ERR(ts->vddi))
>> + return dev_err_probe(&client->dev, PTR_ERR(ts->vddi),
>> + "Failed to enable VDDI regulator\n");
>> +
>> + ts->vdda = devm_regulator_get(dev, "VDDA");
>
> Here as well.
>
>> + if (IS_ERR(ts->vdda))
>> + return dev_err_probe(&client->dev, PTR_ERR(ts->vdda),
>> + "Failed to enable VDDA regulator\n");
>
> Now we handle it as always but..
>
>> + ts->reset_gpio = devm_gpiod_get_optional(dev, "reset",
>> GPIOD_OUT_HIGH);
>> + if (IS_ERR(ts->reset_gpio))
>> + return dev_err_probe(dev, PTR_ERR(ts->reset_gpio), "failed to get
>> reset GPIO\n");
>> +
>> + if (ts->reset_gpio)
>> + axiom_reset(ts->reset_gpio);
>> + else
>> + msleep(AXIOM_STARTUP_TIME_MS);
>
> still the reset is useless since you never enabled the regulators. So
> either use devm_regulator_get_enable() or you do the enable/disable
> separate via regulator_enable()/disable(). If there are no strict
> enablement restrictions like orders and timings you could also make use
> of the regulator_bulk API (e.g. devm_regulator_bulk_get_enable).
>
Fixed in v11, thanks !
> Regards,
> Marco
>
>> +
>> + error = axiom_discover(ts);
>> + if (error)
>> + return dev_err_probe(dev, error, "Failed touchscreen discover\n");
>> +
>> + input_dev = devm_input_allocate_device(ts->dev);
>> + if (!input_dev)
>> + return -ENOMEM;
>> +
>> + input_dev->name = "TouchNetix axiom Touchscreen";
>> + input_dev->phys = "input/axiom_ts";
>> +
>> + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 65535, 0, 0);
>> + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 65535, 0, 0);
>> + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0,
>> 0);
>> + input_set_abs_params(input_dev, ABS_MT_DISTANCE, 0, 127, 0, 0);
>> + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 127, 0, 0);
>> +
>> + touchscreen_parse_properties(input_dev, true, &ts->prop);
>> +
>> + /* Registers the axiom device as a touchscreen instead of a mouse
>> pointer */
>> + error = input_mt_init_slots(input_dev, AXIOM_U41_MAX_TARGETS,
>> INPUT_MT_DIRECT);
>> + if (error)
>> + return error;
>> +
>> + /* Enables the raw data for up to 4 force channels to be sent to the
>> input subsystem */
>> + set_bit(EV_REL, input_dev->evbit);
>> + set_bit(EV_MSC, input_dev->evbit);
>> + /* Declare that we support "RAW" Miscellaneous events */
>> + set_bit(MSC_RAW, input_dev->mscbit);
>> +
>> + ts->input_dev = input_dev;
>> + input_set_drvdata(ts->input_dev, ts);shfs
>> kamel@build3.bootlin.com:/home/kamel/granitenet/modem/cmux cmux/
>> +
>> + /* Ensure that all reports are initialised to not be present. */
>> + for (target = 0; target < AXIOM_U41_MAX_TARGETS; target++)
>> + ts->targets[target].state = AXIOM_TARGET_STATE_NOT_PRESENT;
>> +
>> + error = devm_request_threaded_irq(dev, client->irq, NULL,
>> + axiom_irq, IRQF_ONESHOT, dev_name(dev), ts);
>> + if (error) {
>> + dev_info(dev, "Request irq failed, falling back to polling mode");
>> +
>> + error = input_setup_polling(input_dev, axiom_i2c_poll);
>> + if (error)
>> + return dev_err_probe(ts->dev, error, "Unable to set up polling
>> mode\n");
>> +
>> + if (!device_property_read_u32(ts->dev, "poll-interval",
>> &poll_interval))
>> + input_set_poll_interval(input_dev, poll_interval);
>> + else
>> + input_set_poll_interval(input_dev, POLL_INTERVAL_DEFAULT_MS);
>> + }
>> +
>> + return input_register_device(input_dev);
>> +}
>> +
>> +static const struct i2c_device_id axiom_i2c_id_table[] = {
>> + { "ax54a" },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(i2c, axiom_i2c_id_table);
>> +
>> +static const struct of_device_id axiom_i2c_of_match[] = {
>> + { .compatible = "touchnetix,ax54a", },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(of, axiom_i2c_of_match);
>> +
>> +static struct i2c_driver axiom_i2c_driver = {
>> + .driver = {
>> + .name = "axiom",
>> + .of_match_table = axiom_i2c_of_match,
>> + },
>> + .id_table = axiom_i2c_id_table,
>> + .probe = axiom_i2c_probe,
>> +};
>> +module_i2c_driver(axiom_i2c_driver);
>> +
>> +MODULE_AUTHOR("Bart Prescott <bartp@baasheep.co.uk>");
>> +MODULE_AUTHOR("Pedro Torruella <pedro.torruella@touchnetix.com>");
>> +MODULE_AUTHOR("Mark Satterthwaite
>> <mark.satterthwaite@touchnetix.com>");
>> +MODULE_AUTHOR("Hannah Rossiter <hannah.rossiter@touchnetix.com>");
>> +MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
>> +MODULE_DESCRIPTION("TouchNetix axiom touchscreen I2C bus driver");
>> +MODULE_LICENSE("GPL");
>> --
>> 2.25.1
>>
>>
--
--
Kamel Bouhara, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply
* [PATCH 0/3] HID: bpf: some fixes for pre-loading HID-BPF
From: Benjamin Tissoires @ 2024-04-19 14:47 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: linux-input, linux-kernel, Benjamin Tissoires, stable
As I am working on the next functionalities of HID-BPF, I realized that
I had a few issues while preloading the skeleton at boot.
None of the errors are terrible as they are not inducing a kernel crash,
so it's not super urgent IMO.
Regarding the last one, I'm not sure what makes RHEL behave slightly
different than upstream. But I am not sure also that the code matches
upstream everywhere, so lazy loading it seems like a sensible idea.
Furthermore, that also means that the code will not be available until
requested by user space, which fits well in the whole idea of HID-BPF:
if the user doesn't want it, then it shouldn't be it.
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
---
Benjamin Tissoires (3):
HID: bpf: fix a comment in a define
HID: bpf: fix return value of entrypoints_*__attach()
HID: bpf: lazy load the hid_tail_call entrypoint
drivers/hid/bpf/hid_bpf_dispatch.c | 6 ------
drivers/hid/bpf/hid_bpf_jmp_table.c | 17 ++++++++++++-----
2 files changed, 12 insertions(+), 11 deletions(-)
---
base-commit: b912cf042072e12e93faa874265b30cc0aa521b9
change-id: 20240419-hid_bpf_lazy_skel-ab0d674cb49b
Best regards,
--
Benjamin Tissoires <bentiss@kernel.org>
^ permalink raw reply
* [PATCH 1/3] HID: bpf: fix a comment in a define
From: Benjamin Tissoires @ 2024-04-19 14:47 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: linux-input, linux-kernel, Benjamin Tissoires, stable
In-Reply-To: <20240419-hid_bpf_lazy_skel-v1-0-9210bcd4b61c@kernel.org>
It compiles fine, but my editor is troubled by this.
Makes things clearer by putting the comment above the #define, this
way we are sure it all goes well.
Cc: stable@vger.kernel.org
Fixes: f5c27da4e3c8 ("HID: initial BPF implementation")
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
---
drivers/hid/bpf/hid_bpf_jmp_table.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/hid/bpf/hid_bpf_jmp_table.c b/drivers/hid/bpf/hid_bpf_jmp_table.c
index aa8e1c79cdf5..89496aabbe15 100644
--- a/drivers/hid/bpf/hid_bpf_jmp_table.c
+++ b/drivers/hid/bpf/hid_bpf_jmp_table.c
@@ -19,10 +19,10 @@
#include "hid_bpf_dispatch.h"
#include "entrypoints/entrypoints.lskel.h"
-#define HID_BPF_MAX_PROGS 1024 /* keep this in sync with preloaded bpf,
- * needs to be a power of 2 as we use it as
- * a circular buffer
- */
+/* keep this in sync with preloaded bpf,
+ * needs to be a power of 2 as we use it as a circular buffer
+ */
+#define HID_BPF_MAX_PROGS 1024
#define NEXT(idx) (((idx) + 1) & (HID_BPF_MAX_PROGS - 1))
#define PREV(idx) (((idx) - 1) & (HID_BPF_MAX_PROGS - 1))
--
2.44.0
^ permalink raw reply related
* [PATCH 2/3] HID: bpf: fix return value of entrypoints_*__attach()
From: Benjamin Tissoires @ 2024-04-19 14:47 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: linux-input, linux-kernel, Benjamin Tissoires, stable
In-Reply-To: <20240419-hid_bpf_lazy_skel-v1-0-9210bcd4b61c@kernel.org>
Turns out that this function returns the attached fd, so a positive
or null value.
Given that we were having no other fds before, we were receiving 0,
and the test passed.
Cc: stable@vger.kernel.org
Fixes: f5c27da4e3c8 ("HID: initial BPF implementation")
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
---
drivers/hid/bpf/hid_bpf_jmp_table.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hid/bpf/hid_bpf_jmp_table.c b/drivers/hid/bpf/hid_bpf_jmp_table.c
index 89496aabbe15..301ac79db241 100644
--- a/drivers/hid/bpf/hid_bpf_jmp_table.c
+++ b/drivers/hid/bpf/hid_bpf_jmp_table.c
@@ -521,7 +521,7 @@ void hid_bpf_free_links_and_skel(void)
#define ATTACH_AND_STORE_LINK(__name) do { \
err = entrypoints_bpf__##__name##__attach(skel); \
- if (err) \
+ if (err < 0) \
goto out; \
\
links[idx] = bpf_link_get_from_fd(skel->links.__name##_fd); \
--
2.44.0
^ permalink raw reply related
* [PATCH 3/3] HID: bpf: lazy load the hid_tail_call entrypoint
From: Benjamin Tissoires @ 2024-04-19 14:47 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: linux-input, linux-kernel, Benjamin Tissoires, stable
In-Reply-To: <20240419-hid_bpf_lazy_skel-v1-0-9210bcd4b61c@kernel.org>
Turns out that on some professional distributions, with things partly
backported (not sure exactly), loading this kernel bpf program might
enter a RCU task deadlock.
Given that it actually does not make sense to preload this in every
environment, we can lazy load it the first time we need it, i.e. the
first time the kfunc hid_bpf_attach_prog() is called.
Cc: stable@vger.kernel.org
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
---
drivers/hid/bpf/hid_bpf_dispatch.c | 6 ------
drivers/hid/bpf/hid_bpf_jmp_table.c | 7 +++++++
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
index 10289f44d0cc..1946ad962d03 100644
--- a/drivers/hid/bpf/hid_bpf_dispatch.c
+++ b/drivers/hid/bpf/hid_bpf_dispatch.c
@@ -642,12 +642,6 @@ static int __init hid_bpf_init(void)
return 0;
}
- err = hid_bpf_preload_skel();
- if (err) {
- pr_warn("error while preloading HID BPF dispatcher: %d", err);
- return 0;
- }
-
/* register tracing kfuncs after we are sure we can load our preloaded bpf program */
err = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &hid_bpf_kfunc_set);
if (err) {
diff --git a/drivers/hid/bpf/hid_bpf_jmp_table.c b/drivers/hid/bpf/hid_bpf_jmp_table.c
index 301ac79db241..75ce215f0ada 100644
--- a/drivers/hid/bpf/hid_bpf_jmp_table.c
+++ b/drivers/hid/bpf/hid_bpf_jmp_table.c
@@ -404,6 +404,13 @@ __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type,
mutex_lock(&hid_bpf_attach_lock);
+ if (!jmp_table.map) {
+ err = hid_bpf_preload_skel();
+ WARN_ONCE(err, "error while preloading HID BPF dispatcher: %d", err);
+ if (err)
+ goto err_unlock;
+ }
+
link = kzalloc(sizeof(*link), GFP_USER);
if (!link) {
err = -ENOMEM;
--
2.44.0
^ permalink raw reply related
* Re: [PATCH RESEND v10 4/5] Input: cs40l50 - Add support for the CS40L50 haptic driver
From: James Ogletree @ 2024-04-19 18:26 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: James Ogletree, robh+dt@kernel.org, Krzysztof Kozlowski,
Conor Dooley, Lee Jones, Mark Brown, Jeff LaBundy,
open list:CIRRUS LOGIC HAPTIC DRIVERS,
open list:SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEM...,
open list:INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN)...,
devicetree@vger.kernel.org
In-Reply-To: <Zh8JimSH4cFZsy3o@google.com>
Hi Dmitry,
> On Apr 16, 2024, at 6:28 PM, Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote:
>
> On Mon, Apr 08, 2024 at 03:32:13PM +0000, James Ogletree wrote:
>> Introduce support for Cirrus Logic Device CS40L50: a
>> haptic driver with waveform memory, integrated DSP,
>> and closed-loop algorithms.
>>
>> The input driver provides the interface for control of
>> haptic effects through the device.
>>
>> Signed-off-by: James Ogletree <jogletre@opensource.cirrus.com>
>> ---
>> v10:
>> Minor concern that playback stop is still misused with respect to not
>> specifying an effect ID. The device can only play one effect at a
>> time, but setting max effects for the EVIOCGEFFECTS ioctl to 1 would
>> restrict the number of uploads to 1 as well.
>
> Sorry for a long delay on my part.
Not to worry at all. I am very grateful for the thorough review.
>
> Unfortunately this is not a minor concern, because it breaks the API
> that we so far been presenting to userspace. EVIOCGEFFECTS ioctl which returns
> input->ff->max_effects is described as "Report number of effects
> playable at the same time".
>
> I suggest you limit number of effects to 1 so that we can merge the
> driver with such limitation, and then try to figure out how to expand
> the API. We will probably have to split the notion of number of playable
> effects vs number of uploadable effects, and only accept uploads of
> effects that exceed number of playable effects if they all have the same
> owner, so that different processes would not be able to affect each
> other in case when number of simultaneously playable effects is smaller.
I understand. I will do just that for this submission.
>> +/* Describes an area in DSP memory populated by effects */
>> +struct cs40l50_bank {
>> + enum cs40l50_bank_type type;
>> + u32 base_index;
>> + u32 max_index;
>
> This looks like it is written to the device, so I think this needs
> proper endianness annotation. Is there any concern about padding between
> the type and base_index?
The structure as a whole is not written, so there is no concern about padding.
Only base_index is written, and the endianness conversion is handled by regmap.
“cs40l50_owt_header” would be an example of a struct which is written, and there
care is taken for padding, and there also regmap handles the endianness
conversion.
>> +static int cs40l50_upload_owt(struct cs40l50_work *work_data)
>> +{
>> + u32 len = 2 * work_data->custom_len, wt_offset, wt_size;
>> + struct cs40l50_vibra *vibra = work_data->vibra;
>> + struct cs40l50_owt_header header;
>> + u8 *out_data;
>> + int error;
>> +
>> + error = regmap_read(vibra->regmap, vibra->dsp.owt_size_reg, &wt_size);
>> + if (error)
>> + return error;
>> +
>> + if ((wt_size * sizeof(u32)) < sizeof(header) + len) {
>> + dev_err(vibra->dev, "No space in OWT bank for effect\n");
>> + return -ENOSPC;
>> + }
>> +
>> + out_data = kzalloc(sizeof(header) + len, GFP_KERNEL);
>
> You can make this as
>
> u8 *out_data __free(kfree) = kzalloc(...);
>
> and then you do not need to explicitly kfree() it.
>
> Also, I wonder if you have to zero it and can't use kmalloc() given that
> you copy over the entire thing.
Ack. I will declare it as:
u8 *out_data __free(kfree) = NULL;
And then initialize it in the same place with kmalloc().
This will allow me to keep reverse Christmas tree style.
>> +static int cs40l50_add(struct input_dev *dev, struct ff_effect *effect,
>> + struct ff_effect *old)
>> +{
>> + struct ff_periodic_effect *periodic = &effect->u.periodic;
>> + struct cs40l50_vibra *vibra = input_get_drvdata(dev);
>> + u32 len = effect->u.periodic.custom_len;
>> + struct cs40l50_work work_data;
>> +
>> + if (effect->type != FF_PERIODIC || periodic->waveform != FF_CUSTOM) {
>> + dev_err(vibra->dev, "Type (%#X) or waveform (%#X) unsupported\n",
>> + effect->type, periodic->waveform);
>> + return -EINVAL;
>> + }
>> +
>> + work_data.custom_data = kcalloc(len, sizeof(s16), GFP_KERNEL);
>> + if (!work_data.custom_data)
>> + return -ENOMEM;
>> +
>> + if (copy_from_user(work_data.custom_data, effect->u.periodic.custom_data,
>> + sizeof(s16) * len)) {
>> + work_data.error = -EFAULT;
>> + goto err_free;
>> + }
>
> work_data.custom_data = memdup_array_user(effect->u.periodic.custom_data,
> len, sizeof(s16));
> if (IS_ERR(work_data.custom_data))
> return PTR_ERR(work_data.custom_data);
Ack.
>> +static void cs40l50_start_worker(struct work_struct *work)
>> +{
>> + struct cs40l50_work *work_data = container_of(work, struct cs40l50_work, work);
>> + struct cs40l50_vibra *vibra = work_data->vibra;
>> + struct cs40l50_effect *start_effect;
>> +
>> + if (pm_runtime_resume_and_get(vibra->dev) < 0)
>> + goto err_free;
>> +
>> + mutex_lock(&vibra->lock);
>> +
>> + start_effect = cs40l50_find_effect(work_data->effect->id, &vibra->effect_head);
>> + if (start_effect) {
>> + while (--work_data->count >= 0) {
>> + vibra->dsp.write(vibra->dev, vibra->regmap, start_effect->index);
>> + usleep_range(work_data->effect->replay.length,
>> + work_data->effect->replay.length + 100);
>
> If (I am reading this right you are spinning here playing the effect. It
> would be much better if you tracked outstanding number of replays for an
> effect and had a work re-scheduled that would play an instance.
> Similarly to what code in ff-memless.c is doing.
I think you are reading it right.
My concern with the ff-memless.c method is that it seems to risk out of order
executions when adopted to our driver, because of the use of the workqueue.
If a playback is issued with a high enough repeat value, it seems very plausible
that another operation, e.g. erase, could arrive in the middle of the chain of
re-scheduling and disrupt the playbacks which have yet to be queued. Currently,
the driver handles all repeats in the same work item, so it is guaranteed to repeat
the effect for the specified number of times without being interrupted.
Please let me know what you think.
>> +static int cs40l50_erase(struct input_dev *dev, int effect_id)
>> +{
>> + struct cs40l50_vibra *vibra = input_get_drvdata(dev);
>> + struct cs40l50_work work_data;
>> +
>> + work_data.vibra = vibra;
>> + work_data.effect = &dev->ff->effects[effect_id];
>> +
>> + INIT_WORK(&work_data.work, cs40l50_erase_worker);
>> +
>> + /* Push to workqueue to serialize with playbacks */
>> + queue_work(vibra->vibe_wq, &work_data.work);
>> + flush_work(&work_data.work);
>
> You already take the lock when you play, upload or erase an effect. Why
> do we need additional single-thread workqueue for this? Why do we need
> additional ordering and synchronization?
I think there is unnecessary serialization here, but I think it is the mutexes
which are excessive.
Without the workqueue I think there will be out of order operations. The only
reason the workqueue is necessary is because playback blocks (via
pm_runtime_resume_and_get), and so playback must defer work.
Now upload and erase are not called in atomic context. But without queueing
those executions, they could cut in front of any playbacks waiting in the
workqueue. This is the meaning of the comment "Push to workqueue to
serialize with playbacks”.
But if they are already serialized in this way, then I don’t see the need
for the locks. That’s my thinking, let me know if it is sound.
>> + return input_register_device(vibra->input);
>
> Please no this kind of short hands when there are multiple exists from a
> function.
>
> error = input_register_device(vibra->input);
> if (error)
> return error;
>
> return 0;
>
> This way it is much easier to move the code around when needed.
Ack.
Best,
James
^ permalink raw reply
* [PATCH v2] HID: hid-steam: Add Deck IMU support
From: Max Maisel @ 2024-04-20 12:34 UTC (permalink / raw)
To: jikos, benjamin.tissoires, linux-input, linux-kernel, vi; +Cc: mmm-1
The Deck's controller features an accelerometer and gyroscope which
send their measurement values by default in the main HID input report.
Expose both sensors to userspace through a separate evdev node as it
is done by the hid-nintendo and hid-playstation drivers.
Signed-off-by: Max Maisel <mmm-1@posteo.net>
---
Changes in v2:
* Increased gyroscope range to 32768.
* Removed comment about factory calibration of sensor values.
* Removed STEAM_QUIRK_DECK check in steam_raw_event function.
* Silenced the IMU when gamepad mode is disabled.
* Added improved fuzz values for the input device.
The new values are based on the average deviation from the average
sensor values at rest.
* Rebased onto kernel v6.9-rc4.
* Improved the test procedure below.
Test procedure:
This patch was tested on a Steam Deck running Arch Linux. With it,
applications using latest SDL2/3 git libraries will pick up the sensors
without hidraw access. This was tested against the antimicrox gamepad
mapper.
Measurement value scaling was tested by logging and comparing sensors
values between the deck and a dualsense controller.
For the accelerometer, both controllers were aligned to gravity on all axes
and the reported values were compared.
For the gyroscope, both controllers were placed on a makeshift
rotational plate and the reported absolute angular velocity was compared.
Furthermore, it was tested that the axes have the same orientation
between the two controller types.
Finally, it was tested that the full scale values for both sensor types
can be reached by doing jerky movements with the deck.
All observed values matched within a few percent error range.
drivers/hid/hid-steam.c | 155 +++++++++++++++++++++++++++++++++++++---
1 file changed, 147 insertions(+), 8 deletions(-)
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index b08a5ab58528..f166188c21ec 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -66,6 +66,14 @@ static LIST_HEAD(steam_devices);
#define STEAM_DECK_TRIGGER_RESOLUTION 5461
/* Joystick runs are about 5 mm and 32768 units */
#define STEAM_DECK_JOYSTICK_RESOLUTION 6553
+/* Accelerometer has 16 bit resolution and a range of +/- 2g */
+#define STEAM_DECK_ACCEL_RES_PER_G 16384
+#define STEAM_DECK_ACCEL_RANGE 32768
+#define STEAM_DECK_ACCEL_FUZZ 32
+/* Gyroscope has 16 bit resolution and a range of +/- 2000 dps */
+#define STEAM_DECK_GYRO_RES_PER_DPS 16
+#define STEAM_DECK_GYRO_RANGE 32768
+#define STEAM_DECK_GYRO_FUZZ 1
#define STEAM_PAD_FUZZ 256
@@ -288,6 +296,7 @@ struct steam_device {
struct mutex report_mutex;
unsigned long client_opened;
struct input_dev __rcu *input;
+ struct input_dev __rcu *sensors;
unsigned long quirks;
struct work_struct work_connect;
bool connected;
@@ -302,6 +311,7 @@ struct steam_device {
struct work_struct rumble_work;
u16 rumble_left;
u16 rumble_right;
+ unsigned int sensor_timestamp_us;
};
static int steam_recv_report(struct steam_device *steam,
@@ -825,6 +835,74 @@ static int steam_input_register(struct steam_device *steam)
return ret;
}
+static int steam_sensors_register(struct steam_device *steam)
+{
+ struct hid_device *hdev = steam->hdev;
+ struct input_dev *sensors;
+ int ret;
+
+ if (!(steam->quirks & STEAM_QUIRK_DECK))
+ return 0;
+
+ rcu_read_lock();
+ sensors = rcu_dereference(steam->sensors);
+ rcu_read_unlock();
+ if (sensors) {
+ dbg_hid("%s: already connected\n", __func__);
+ return 0;
+ }
+
+ sensors = input_allocate_device();
+ if (!sensors)
+ return -ENOMEM;
+
+ input_set_drvdata(sensors, steam);
+ sensors->dev.parent = &hdev->dev;
+
+ sensors->name = "Steam Deck Motion Sensors";
+ sensors->phys = hdev->phys;
+ sensors->uniq = steam->serial_no;
+ sensors->id.bustype = hdev->bus;
+ sensors->id.vendor = hdev->vendor;
+ sensors->id.product = hdev->product;
+ sensors->id.version = hdev->version;
+
+ __set_bit(INPUT_PROP_ACCELEROMETER, sensors->propbit);
+ __set_bit(EV_MSC, sensors->evbit);
+ __set_bit(MSC_TIMESTAMP, sensors->mscbit);
+
+ input_set_abs_params(sensors, ABS_X, -STEAM_DECK_ACCEL_RANGE,
+ STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
+ input_set_abs_params(sensors, ABS_Y, -STEAM_DECK_ACCEL_RANGE,
+ STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
+ input_set_abs_params(sensors, ABS_Z, -STEAM_DECK_ACCEL_RANGE,
+ STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
+ input_abs_set_res(sensors, ABS_X, STEAM_DECK_ACCEL_RES_PER_G);
+ input_abs_set_res(sensors, ABS_Y, STEAM_DECK_ACCEL_RES_PER_G);
+ input_abs_set_res(sensors, ABS_Z, STEAM_DECK_ACCEL_RES_PER_G);
+
+ input_set_abs_params(sensors, ABS_RX, -STEAM_DECK_GYRO_RANGE,
+ STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0);
+ input_set_abs_params(sensors, ABS_RY, -STEAM_DECK_GYRO_RANGE,
+ STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0);
+ input_set_abs_params(sensors, ABS_RZ, -STEAM_DECK_GYRO_RANGE,
+ STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0);
+ input_abs_set_res(sensors, ABS_RX, STEAM_DECK_GYRO_RES_PER_DPS);
+ input_abs_set_res(sensors, ABS_RY, STEAM_DECK_GYRO_RES_PER_DPS);
+ input_abs_set_res(sensors, ABS_RZ, STEAM_DECK_GYRO_RES_PER_DPS);
+
+ ret = input_register_device(sensors);
+ if (ret)
+ goto sensors_register_fail;
+
+ rcu_assign_pointer(steam->sensors, sensors);
+ return 0;
+
+sensors_register_fail:
+ input_free_device(sensors);
+ return ret;
+}
+
static void steam_input_unregister(struct steam_device *steam)
{
struct input_dev *input;
@@ -838,6 +916,24 @@ static void steam_input_unregister(struct steam_device *steam)
input_unregister_device(input);
}
+static void steam_sensors_unregister(struct steam_device *steam)
+{
+ struct input_dev *sensors;
+
+ if (!(steam->quirks & STEAM_QUIRK_DECK))
+ return;
+
+ rcu_read_lock();
+ sensors = rcu_dereference(steam->sensors);
+ rcu_read_unlock();
+
+ if (!sensors)
+ return;
+ RCU_INIT_POINTER(steam->sensors, NULL);
+ synchronize_rcu();
+ input_unregister_device(sensors);
+}
+
static void steam_battery_unregister(struct steam_device *steam)
{
struct power_supply *battery;
@@ -890,18 +986,28 @@ static int steam_register(struct steam_device *steam)
spin_lock_irqsave(&steam->lock, flags);
client_opened = steam->client_opened;
spin_unlock_irqrestore(&steam->lock, flags);
+
if (!client_opened) {
steam_set_lizard_mode(steam, lizard_mode);
ret = steam_input_register(steam);
- } else
- ret = 0;
+ if (ret != 0)
+ goto steam_register_input_fail;
+ ret = steam_sensors_register(steam);
+ if (ret != 0)
+ goto steam_register_sensors_fail;
+ }
+ return 0;
+steam_register_sensors_fail:
+ steam_input_unregister(steam);
+steam_register_input_fail:
return ret;
}
static void steam_unregister(struct steam_device *steam)
{
steam_battery_unregister(steam);
+ steam_sensors_unregister(steam);
steam_input_unregister(steam);
if (steam->serial_no[0]) {
hid_info(steam->hdev, "Steam Controller '%s' disconnected",
@@ -1010,6 +1116,7 @@ static int steam_client_ll_open(struct hid_device *hdev)
steam->client_opened++;
spin_unlock_irqrestore(&steam->lock, flags);
+ steam_sensors_unregister(steam);
steam_input_unregister(steam);
return 0;
@@ -1030,6 +1137,7 @@ static void steam_client_ll_close(struct hid_device *hdev)
if (connected) {
steam_set_lizard_mode(steam, lizard_mode);
steam_input_register(steam);
+ steam_sensors_register(steam);
}
}
@@ -1121,6 +1229,7 @@ static int steam_probe(struct hid_device *hdev,
INIT_DELAYED_WORK(&steam->mode_switch, steam_mode_switch_cb);
INIT_LIST_HEAD(&steam->list);
INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
+ steam->sensor_timestamp_us = 0;
/*
* With the real steam controller interface, do not connect hidraw.
@@ -1380,12 +1489,12 @@ static void steam_do_input_event(struct steam_device *steam,
* 18-19 | s16 | ABS_HAT0Y | left-pad Y value
* 20-21 | s16 | ABS_HAT1X | right-pad X value
* 22-23 | s16 | ABS_HAT1Y | right-pad Y value
- * 24-25 | s16 | -- | accelerometer X value
- * 26-27 | s16 | -- | accelerometer Y value
- * 28-29 | s16 | -- | accelerometer Z value
- * 30-31 | s16 | -- | gyro X value
- * 32-33 | s16 | -- | gyro Y value
- * 34-35 | s16 | -- | gyro Z value
+ * 24-25 | s16 | IMU ABS_X | accelerometer X value
+ * 26-27 | s16 | IMU ABS_Z | accelerometer Y value
+ * 28-29 | s16 | IMU ABS_Y | accelerometer Z value
+ * 30-31 | s16 | IMU ABS_RX | gyro X value
+ * 32-33 | s16 | IMU ABS_RZ | gyro Y value
+ * 34-35 | s16 | IMU ABS_RY | gyro Z value
* 36-37 | s16 | -- | quaternion W value
* 38-39 | s16 | -- | quaternion X value
* 40-41 | s16 | -- | quaternion Y value
@@ -1546,6 +1655,32 @@ static void steam_do_deck_input_event(struct steam_device *steam,
input_sync(input);
}
+static void steam_do_deck_sensors_event(struct steam_device *steam,
+ struct input_dev *sensors, u8 *data)
+{
+ /*
+ * The deck input report is received every 4 ms on average,
+ * with a jitter of +/- 4 ms even though the USB descriptor claims
+ * that it uses 1 kHz.
+ * Since the HID report does not include a sensor timestamp,
+ * use a fixed increment here.
+ */
+ steam->sensor_timestamp_us += 4000;
+
+ if (!steam->gamepad_mode)
+ return;
+
+ input_event(sensors, EV_MSC, MSC_TIMESTAMP, steam->sensor_timestamp_us);
+ input_report_abs(sensors, ABS_X, steam_le16(data + 24));
+ input_report_abs(sensors, ABS_Z, -steam_le16(data + 26));
+ input_report_abs(sensors, ABS_Y, steam_le16(data + 28));
+ input_report_abs(sensors, ABS_RX, steam_le16(data + 30));
+ input_report_abs(sensors, ABS_RZ, -steam_le16(data + 32));
+ input_report_abs(sensors, ABS_RY, steam_le16(data + 34));
+
+ input_sync(sensors);
+}
+
/*
* The size for this message payload is 11.
* The known values are:
@@ -1583,6 +1718,7 @@ static int steam_raw_event(struct hid_device *hdev,
{
struct steam_device *steam = hid_get_drvdata(hdev);
struct input_dev *input;
+ struct input_dev *sensors;
struct power_supply *battery;
if (!steam)
@@ -1628,6 +1764,9 @@ static int steam_raw_event(struct hid_device *hdev,
input = rcu_dereference(steam->input);
if (likely(input))
steam_do_deck_input_event(steam, input, data);
+ sensors = rcu_dereference(steam->sensors);
+ if (likely(sensors))
+ steam_do_deck_sensors_event(steam, sensors, data);
rcu_read_unlock();
break;
case ID_CONTROLLER_WIRELESS:
base-commit: 0bbac3facb5d6cc0171c45c9873a2dc96bea9680
--
2.44.0
^ permalink raw reply related
* Re: [PATCH v3] Input: xen-kbdfront - drop keys to shrink modalias
From: Jason Andryuk @ 2024-04-21 23:57 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-kernel, Phillip Susi, stable, Mattijs Korpershoek,
linux-input, xen-devel, Juergen Gross
In-Reply-To: <ZgWxYvQH4A_Vh1i4@google.com>
Hi Dmitry,
On Thu, Mar 28, 2024 at 2:05 PM Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
>
> Hi Jason,
>
> On Wed, Mar 20, 2024 at 01:42:27PM -0400, Jason Andryuk wrote:
> > Hi Dmitry,
> >
> > Do you have any feedback, or can you pick up this patch? It solves a
> > real issue affecting udev, which crashes the Debian installer and
> > breaks the mouse for Gnome.
> >
> > Or would you be okay if this patch went in via the Xen tree?
>
> I'd rather not. Could you please ping me in 2 weeks on this. I promise
> we find a solution before the next release.
It's been ~3 weeks. Any ideas?
If you think this patch should be pursued in this form, I'd like to
post a v4 that adds BTN_DPAD_{UP,DOWN,LEFT,RIGHT} on the off chance
someone wants to use a controller. I dropped them initially since
they are not keyboard keys, but button presses are delivered through
the keyboard. Hence, they should be included.
Thanks,
Jason
^ permalink raw reply
* [PATCH 08/11] can: bcm: Add LIN answer offloading for responder mode
From: Christoph Fritz @ 2024-04-22 6:51 UTC (permalink / raw)
To: Oliver Hartkopp, Marc Kleine-Budde, Vincent Mailhol,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jiri Kosina,
Benjamin Tissoires, Greg Kroah-Hartman, Jiri Slaby
Cc: Andreas Lauser, Jonathan Corbet, linux-can, netdev, devicetree,
linux-input, linux-serial
In-Reply-To: <20240422065114.3185505-1-christoph.fritz@hexdev.de>
Enhance CAN broadcast manager with RX_LIN_SETUP and RX_LIN_DELETE
operations to setup automatic LIN frame responses in responder mode.
Additionally, the patch introduces the LIN_EVENT_FRAME flag to
setup event-triggered LIN frames.
Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
---
include/uapi/linux/can/bcm.h | 5 ++-
net/can/bcm.c | 74 +++++++++++++++++++++++++++++++++++-
2 files changed, 77 insertions(+), 2 deletions(-)
diff --git a/include/uapi/linux/can/bcm.h b/include/uapi/linux/can/bcm.h
index f1e45f533a72c..c46268a114078 100644
--- a/include/uapi/linux/can/bcm.h
+++ b/include/uapi/linux/can/bcm.h
@@ -86,7 +86,9 @@ enum {
TX_EXPIRED, /* notification on performed transmissions (count=0) */
RX_STATUS, /* reply to RX_READ request */
RX_TIMEOUT, /* cyclic message is absent */
- RX_CHANGED /* updated CAN frame (detected content change) */
+ RX_CHANGED, /* updated CAN frame (detected content change) */
+ RX_LIN_SETUP, /* create auto-response for LIN frame */
+ RX_LIN_DELETE, /* remove auto-response for LIN frame */
};
#define SETTIMER 0x0001
@@ -101,5 +103,6 @@ enum {
#define TX_RESET_MULTI_IDX 0x0200
#define RX_RTR_FRAME 0x0400
#define CAN_FD_FRAME 0x0800
+#define LIN_EVENT_FRAME 0x1000
#endif /* !_UAPI_CAN_BCM_H */
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 27d5fcf0eac9d..a717e594234d1 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -59,6 +59,7 @@
#include <linux/can/bcm.h>
#include <linux/slab.h>
#include <net/sock.h>
+#include <net/lin.h>
#include <net/net_namespace.h>
/*
@@ -1330,6 +1331,59 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk,
return cfsiz + MHSIZ;
}
+static int bcm_lin_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ int ifindex, struct sock *sk, int cfsiz, int is_active)
+{
+ struct lin_responder_answer answ;
+ struct net_device *dev;
+ struct sk_buff *skb;
+ struct canfd_frame cf;
+ netdevice_tracker tracker;
+ size_t sz;
+ int ret;
+
+ if (msg_head->nframes > 1)
+ return -EINVAL;
+
+ if (!(msg_head->flags & CAN_FD_FRAME))
+ return -EINVAL;
+
+ ret = memcpy_from_msg(&cf, msg, cfsiz);
+ if (ret < 0)
+ return ret;
+
+ answ.lf.lin_id = cf.can_id & LIN_ID_MASK;
+ answ.is_active = is_active;
+ answ.is_event_frame = !!(msg_head->flags & LIN_EVENT_FRAME);
+ answ.event_associated_id = msg_head->can_id;
+ answ.lf.len = min(cf.len, LIN_MAX_DLEN);
+ memcpy(answ.lf.data, cf.data, answ.lf.len);
+ sz = min(sizeof(struct lin_responder_answer), sizeof(cf.data));
+ cf.can_id |= LIN_RXOFFLOAD_DATA_FLAG;
+ memcpy(cf.data, &answ, sz);
+
+ dev = netdev_get_by_index(sock_net(sk), ifindex, &tracker, GFP_KERNEL);
+ if (!dev)
+ return -ENODEV;
+
+ skb = alloc_skb(cfsiz + sizeof(struct can_skb_priv), gfp_any());
+ if (!skb)
+ goto lin_out;
+
+ can_skb_reserve(skb);
+ can_skb_prv(skb)->ifindex = dev->ifindex;
+ can_skb_prv(skb)->skbcnt = 0;
+ skb_put_data(skb, &cf, cfsiz);
+
+ skb->dev = dev;
+ can_skb_set_owner(skb, sk);
+ ret = can_send(skb, 1); /* send with loopback */
+
+lin_out:
+ netdev_put(dev, &tracker);
+ return ret;
+}
+
/*
* bcm_sendmsg - process BCM commands (opcodes) from the userspace
*/
@@ -1429,12 +1483,30 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
case TX_SEND:
/* we need exactly one CAN frame behind the msg head */
- if ((msg_head.nframes != 1) || (size != cfsiz + MHSIZ))
+ if (msg_head.nframes != 1 || size != cfsiz + MHSIZ)
ret = -EINVAL;
else
ret = bcm_tx_send(msg, ifindex, sk, cfsiz);
break;
+ case RX_LIN_SETUP:
+ /* we need exactly one CAN frame behind the msg head */
+ if (msg_head.nframes != 1 || size != cfsiz + MHSIZ)
+ ret = -EINVAL;
+ else
+ ret = bcm_lin_setup(&msg_head, msg, ifindex, sk, cfsiz,
+ 1);
+ break;
+
+ case RX_LIN_DELETE:
+ /* we need exactly one CAN frame behind the msg head */
+ if (msg_head.nframes != 1 || size != cfsiz + MHSIZ)
+ ret = -EINVAL;
+ else
+ ret = bcm_lin_setup(&msg_head, msg, ifindex, sk, cfsiz,
+ 0);
+ break;
+
default:
ret = -EINVAL;
break;
--
2.39.2
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox