diff --git a/patches/6.1/0005-ithc.patch b/patches/6.1/0005-ithc.patch index b08eba2a3..17ce45201 100644 --- a/patches/6.1/0005-ithc.patch +++ b/patches/6.1/0005-ithc.patch @@ -1431,3 +1431,1594 @@ index 000000000000..6a9b0d480bc1 -- 2.42.0 +From 302fe1328fae9cd750c191b0a23f6c244b7ea382 Mon Sep 17 00:00:00 2001 +From: quo +Date: Mon, 23 Oct 2023 10:15:29 +0200 +Subject: [PATCH] Update ITHC from module repo + +Changes: + - Added some comments and fixed a few checkpatch warnings + - Improved CPU latency QoS handling + - Retry reading the report descriptor on error / timeout + +Based on https://github.com/quo/ithc-linux/commit/0b8b45d9775e756d6bd3a699bfaf9f5bd7b9b10b + +Signed-off-by: Dorian Stoll +Patchset: ithc +--- + drivers/hid/ithc/ithc-debug.c | 94 +++++--- + drivers/hid/ithc/ithc-dma.c | 231 +++++++++++++----- + drivers/hid/ithc/ithc-dma.h | 4 +- + drivers/hid/ithc/ithc-main.c | 430 ++++++++++++++++++++++++---------- + drivers/hid/ithc/ithc-regs.c | 68 ++++-- + drivers/hid/ithc/ithc-regs.h | 19 +- + drivers/hid/ithc/ithc.h | 13 +- + 7 files changed, 623 insertions(+), 236 deletions(-) + +diff --git a/drivers/hid/ithc/ithc-debug.c b/drivers/hid/ithc/ithc-debug.c +index 57bf125c45bd..1f1f1e33f2e5 100644 +--- a/drivers/hid/ithc/ithc-debug.c ++++ b/drivers/hid/ithc/ithc-debug.c +@@ -1,10 +1,14 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause ++ + #include "ithc.h" + +-void ithc_log_regs(struct ithc *ithc) { +- if (!ithc->prev_regs) return; +- u32 __iomem *cur = (__iomem void*)ithc->regs; +- u32 *prev = (void*)ithc->prev_regs; +- for (int i = 1024; i < sizeof *ithc->regs / 4; i++) { ++void ithc_log_regs(struct ithc *ithc) ++{ ++ if (!ithc->prev_regs) ++ return; ++ u32 __iomem *cur = (__iomem void *)ithc->regs; ++ u32 *prev = (void *)ithc->prev_regs; ++ for (int i = 1024; i < sizeof(*ithc->regs) / 4; i++) { + u32 x = readl(cur + i); + if (x != prev[i]) { + pci_info(ithc->pci, "reg %04x: %08x -> %08x\n", i * 4, prev[i], x); +@@ -13,55 +17,79 @@ void ithc_log_regs(struct ithc *ithc) { + } + } + +-static ssize_t ithc_debugfs_cmd_write(struct file *f, const char __user *buf, size_t len, loff_t *offset) { ++static ssize_t ithc_debugfs_cmd_write(struct file *f, const char __user *buf, size_t len, ++ loff_t *offset) ++{ ++ // Debug commands consist of a single letter followed by a list of numbers (decimal or ++ // hexadecimal, space-separated). + struct ithc *ithc = file_inode(f)->i_private; + char cmd[256]; +- if (!ithc || !ithc->pci) return -ENODEV; +- if (!len) return -EINVAL; +- if (len >= sizeof cmd) return -EINVAL; +- if (copy_from_user(cmd, buf, len)) return -EFAULT; ++ if (!ithc || !ithc->pci) ++ return -ENODEV; ++ if (!len) ++ return -EINVAL; ++ if (len >= sizeof(cmd)) ++ return -EINVAL; ++ if (copy_from_user(cmd, buf, len)) ++ return -EFAULT; + cmd[len] = 0; +- if (cmd[len-1] == '\n') cmd[len-1] = 0; ++ if (cmd[len-1] == '\n') ++ cmd[len-1] = 0; + pci_info(ithc->pci, "debug command: %s\n", cmd); ++ ++ // Parse the list of arguments into a u32 array. + u32 n = 0; + const char *s = cmd + 1; + u32 a[32]; + while (*s && *s != '\n') { +- if (n >= ARRAY_SIZE(a)) return -EINVAL; +- if (*s++ != ' ') return -EINVAL; ++ if (n >= ARRAY_SIZE(a)) ++ return -EINVAL; ++ if (*s++ != ' ') ++ return -EINVAL; + char *e; + a[n++] = simple_strtoul(s, &e, 0); +- if (e == s) return -EINVAL; ++ if (e == s) ++ return -EINVAL; + s = e; + } + ithc_log_regs(ithc); +- switch(cmd[0]) { ++ ++ // Execute the command. ++ switch (cmd[0]) { + case 'x': // reset + ithc_reset(ithc); + break; + case 'w': // write register: offset mask value +- if (n != 3 || (a[0] & 3)) return -EINVAL; +- pci_info(ithc->pci, "debug write 0x%04x = 0x%08x (mask 0x%08x)\n", a[0], a[2], a[1]); ++ if (n != 3 || (a[0] & 3)) ++ return -EINVAL; ++ pci_info(ithc->pci, "debug write 0x%04x = 0x%08x (mask 0x%08x)\n", ++ a[0], a[2], a[1]); + bitsl(((__iomem u32 *)ithc->regs) + a[0] / 4, a[1], a[2]); + break; + case 'r': // read register: offset +- if (n != 1 || (a[0] & 3)) return -EINVAL; +- pci_info(ithc->pci, "debug read 0x%04x = 0x%08x\n", a[0], readl(((__iomem u32 *)ithc->regs) + a[0] / 4)); ++ if (n != 1 || (a[0] & 3)) ++ return -EINVAL; ++ pci_info(ithc->pci, "debug read 0x%04x = 0x%08x\n", a[0], ++ readl(((__iomem u32 *)ithc->regs) + a[0] / 4)); + break; + case 's': // spi command: cmd offset len data... + // read config: s 4 0 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // set touch cfg: s 6 12 4 XX +- if (n < 3 || a[2] > (n - 3) * 4) return -EINVAL; ++ if (n < 3 || a[2] > (n - 3) * 4) ++ return -EINVAL; + pci_info(ithc->pci, "debug spi command %u with %u bytes of data\n", a[0], a[2]); + if (!CHECK(ithc_spi_command, ithc, a[0], a[1], a[2], a + 3)) +- for (u32 i = 0; i < (a[2] + 3) / 4; i++) pci_info(ithc->pci, "resp %u = 0x%08x\n", i, a[3+i]); ++ for (u32 i = 0; i < (a[2] + 3) / 4; i++) ++ pci_info(ithc->pci, "resp %u = 0x%08x\n", i, a[3+i]); + break; + case 'd': // dma command: cmd len data... + // get report descriptor: d 7 8 0 0 + // enable multitouch: d 3 2 0x0105 +- if (n < 2 || a[1] > (n - 2) * 4) return -EINVAL; ++ if (n < 2 || a[1] > (n - 2) * 4) ++ return -EINVAL; + pci_info(ithc->pci, "debug dma command %u with %u bytes of data\n", a[0], a[1]); +- if (ithc_dma_tx(ithc, a[0], a[1], a + 2)) pci_err(ithc->pci, "dma tx failed\n"); ++ if (ithc_dma_tx(ithc, a[0], a[1], a + 2)) ++ pci_err(ithc->pci, "dma tx failed\n"); + break; + default: + return -EINVAL; +@@ -75,21 +103,27 @@ static const struct file_operations ithc_debugfops_cmd = { + .write = ithc_debugfs_cmd_write, + }; + +-static void ithc_debugfs_devres_release(struct device *dev, void *res) { ++static void ithc_debugfs_devres_release(struct device *dev, void *res) ++{ + struct dentry **dbgm = res; +- if (*dbgm) debugfs_remove_recursive(*dbgm); ++ if (*dbgm) ++ debugfs_remove_recursive(*dbgm); + } + +-int ithc_debug_init(struct ithc *ithc) { +- struct dentry **dbgm = devres_alloc(ithc_debugfs_devres_release, sizeof *dbgm, GFP_KERNEL); +- if (!dbgm) return -ENOMEM; ++int ithc_debug_init(struct ithc *ithc) ++{ ++ struct dentry **dbgm = devres_alloc(ithc_debugfs_devres_release, sizeof(*dbgm), GFP_KERNEL); ++ if (!dbgm) ++ return -ENOMEM; + devres_add(&ithc->pci->dev, dbgm); + struct dentry *dbg = debugfs_create_dir(DEVNAME, NULL); +- if (IS_ERR(dbg)) return PTR_ERR(dbg); ++ if (IS_ERR(dbg)) ++ return PTR_ERR(dbg); + *dbgm = dbg; + + struct dentry *cmd = debugfs_create_file("cmd", 0220, dbg, ithc, &ithc_debugfops_cmd); +- if (IS_ERR(cmd)) return PTR_ERR(cmd); ++ if (IS_ERR(cmd)) ++ return PTR_ERR(cmd); + + return 0; + } +diff --git a/drivers/hid/ithc/ithc-dma.c b/drivers/hid/ithc/ithc-dma.c +index 7e89b3496918..ffb8689b8a78 100644 +--- a/drivers/hid/ithc/ithc-dma.c ++++ b/drivers/hid/ithc/ithc-dma.c +@@ -1,59 +1,91 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause ++ + #include "ithc.h" + +-static int ithc_dma_prd_alloc(struct ithc *ithc, struct ithc_dma_prd_buffer *p, unsigned num_buffers, unsigned num_pages, enum dma_data_direction dir) { ++// The THC uses tables of PRDs (physical region descriptors) to describe the TX and RX data buffers. ++// Each PRD contains the DMA address and size of a block of DMA memory, and some status flags. ++// This allows each data buffer to consist of multiple non-contiguous blocks of memory. ++ ++static int ithc_dma_prd_alloc(struct ithc *ithc, struct ithc_dma_prd_buffer *p, ++ unsigned int num_buffers, unsigned int num_pages, enum dma_data_direction dir) ++{ + p->num_pages = num_pages; + p->dir = dir; ++ // We allocate enough space to have one PRD per data buffer page, however if the data ++ // buffer pages happen to be contiguous, we can describe the buffer using fewer PRDs, so ++ // some will remain unused (which is fine). + p->size = round_up(num_buffers * num_pages * sizeof(struct ithc_phys_region_desc), PAGE_SIZE); + p->addr = dmam_alloc_coherent(&ithc->pci->dev, p->size, &p->dma_addr, GFP_KERNEL); +- if (!p->addr) return -ENOMEM; +- if (p->dma_addr & (PAGE_SIZE - 1)) return -EFAULT; ++ if (!p->addr) ++ return -ENOMEM; ++ if (p->dma_addr & (PAGE_SIZE - 1)) ++ return -EFAULT; + return 0; + } + ++// Devres managed sg_table wrapper. + struct ithc_sg_table { + void *addr; + struct sg_table sgt; + enum dma_data_direction dir; + }; +-static void ithc_dma_sgtable_free(struct sg_table *sgt) { ++static void ithc_dma_sgtable_free(struct sg_table *sgt) ++{ + struct scatterlist *sg; + int i; + for_each_sgtable_sg(sgt, sg, i) { + struct page *p = sg_page(sg); +- if (p) __free_page(p); ++ if (p) ++ __free_page(p); + } + sg_free_table(sgt); + } +-static void ithc_dma_data_devres_release(struct device *dev, void *res) { ++static void ithc_dma_data_devres_release(struct device *dev, void *res) ++{ + struct ithc_sg_table *sgt = res; +- if (sgt->addr) vunmap(sgt->addr); ++ if (sgt->addr) ++ vunmap(sgt->addr); + dma_unmap_sgtable(dev, &sgt->sgt, sgt->dir, 0); + ithc_dma_sgtable_free(&sgt->sgt); + } + +-static int ithc_dma_data_alloc(struct ithc* ithc, struct ithc_dma_prd_buffer *prds, struct ithc_dma_data_buffer *b) { +- // We don't use dma_alloc_coherent for data buffers, because they don't have to be contiguous (we can use one PRD per page) or coherent (they are unidirectional). +- // Instead we use an sg_table of individually allocated pages (5.13 has dma_alloc_noncontiguous for this, but we'd like to support 5.10 for now). ++static int ithc_dma_data_alloc(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, ++ struct ithc_dma_data_buffer *b) ++{ ++ // We don't use dma_alloc_coherent() for data buffers, because they don't have to be ++ // coherent (they are unidirectional) or contiguous (we can use one PRD per page). ++ // We could use dma_alloc_noncontiguous(), however this still always allocates a single ++ // DMA mapped segment, which is more restrictive than what we need. ++ // Instead we use an sg_table of individually allocated pages. + struct page *pages[16]; +- if (prds->num_pages == 0 || prds->num_pages > ARRAY_SIZE(pages)) return -EINVAL; ++ if (prds->num_pages == 0 || prds->num_pages > ARRAY_SIZE(pages)) ++ return -EINVAL; + b->active_idx = -1; +- struct ithc_sg_table *sgt = devres_alloc(ithc_dma_data_devres_release, sizeof *sgt, GFP_KERNEL); +- if (!sgt) return -ENOMEM; ++ struct ithc_sg_table *sgt = devres_alloc( ++ ithc_dma_data_devres_release, sizeof(*sgt), GFP_KERNEL); ++ if (!sgt) ++ return -ENOMEM; + sgt->dir = prds->dir; ++ + if (!sg_alloc_table(&sgt->sgt, prds->num_pages, GFP_KERNEL)) { + struct scatterlist *sg; + int i; + bool ok = true; + for_each_sgtable_sg(&sgt->sgt, sg, i) { +- struct page *p = pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO); // don't need __GFP_DMA for PCI DMA +- if (!p) { ok = false; break; } ++ // NOTE: don't need __GFP_DMA for PCI DMA ++ struct page *p = pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO); ++ if (!p) { ++ ok = false; ++ break; ++ } + sg_set_page(sg, p, PAGE_SIZE, 0); + } + if (ok && !dma_map_sgtable(&ithc->pci->dev, &sgt->sgt, prds->dir, 0)) { + devres_add(&ithc->pci->dev, sgt); + b->sgt = &sgt->sgt; + b->addr = sgt->addr = vmap(pages, prds->num_pages, 0, PAGE_KERNEL); +- if (!b->addr) return -ENOMEM; ++ if (!b->addr) ++ return -ENOMEM; + return 0; + } + ithc_dma_sgtable_free(&sgt->sgt); +@@ -62,17 +94,29 @@ static int ithc_dma_data_alloc(struct ithc* ithc, struct ithc_dma_prd_buffer *pr + return -ENOMEM; + } + +-static int ithc_dma_data_buffer_put(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, struct ithc_dma_data_buffer *b, unsigned idx) { ++static int ithc_dma_data_buffer_put(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, ++ struct ithc_dma_data_buffer *b, unsigned int idx) ++{ ++ // Give a buffer to the THC. + struct ithc_phys_region_desc *prd = prds->addr; + prd += idx * prds->num_pages; +- if (b->active_idx >= 0) { pci_err(ithc->pci, "buffer already active\n"); return -EINVAL; } ++ if (b->active_idx >= 0) { ++ pci_err(ithc->pci, "buffer already active\n"); ++ return -EINVAL; ++ } + b->active_idx = idx; + if (prds->dir == DMA_TO_DEVICE) { +- if (b->data_size > PAGE_SIZE) return -EINVAL; ++ // TX buffer: Caller should have already filled the data buffer, so just fill ++ // the PRD and flush. ++ // (TODO: Support multi-page TX buffers. So far no device seems to use or need ++ // these though.) ++ if (b->data_size > PAGE_SIZE) ++ return -EINVAL; + prd->addr = sg_dma_address(b->sgt->sgl) >> 10; + prd->size = b->data_size | PRD_FLAG_END; + flush_kernel_vmap_range(b->addr, b->data_size); + } else if (prds->dir == DMA_FROM_DEVICE) { ++ // RX buffer: Reset PRDs. + struct scatterlist *sg; + int i; + for_each_sgtable_dma_sg(b->sgt, sg, i) { +@@ -87,21 +131,34 @@ static int ithc_dma_data_buffer_put(struct ithc *ithc, struct ithc_dma_prd_buffe + return 0; + } + +-static int ithc_dma_data_buffer_get(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, struct ithc_dma_data_buffer *b, unsigned idx) { ++static int ithc_dma_data_buffer_get(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, ++ struct ithc_dma_data_buffer *b, unsigned int idx) ++{ ++ // Take a buffer from the THC. + struct ithc_phys_region_desc *prd = prds->addr; + prd += idx * prds->num_pages; +- if (b->active_idx != idx) { pci_err(ithc->pci, "wrong buffer index\n"); return -EINVAL; } ++ // This is purely a sanity check. We don't strictly need the idx parameter for this ++ // function, because it should always be the same as active_idx, unless we have a bug. ++ if (b->active_idx != idx) { ++ pci_err(ithc->pci, "wrong buffer index\n"); ++ return -EINVAL; ++ } + b->active_idx = -1; + if (prds->dir == DMA_FROM_DEVICE) { ++ // RX buffer: Calculate actual received data size from PRDs. + dma_rmb(); // for the prds + b->data_size = 0; + struct scatterlist *sg; + int i; + for_each_sgtable_dma_sg(b->sgt, sg, i) { +- unsigned size = prd->size; ++ unsigned int size = prd->size; + b->data_size += size & PRD_SIZE_MASK; +- if (size & PRD_FLAG_END) break; +- if ((size & PRD_SIZE_MASK) != sg_dma_len(sg)) { pci_err(ithc->pci, "truncated prd\n"); break; } ++ if (size & PRD_FLAG_END) ++ break; ++ if ((size & PRD_SIZE_MASK) != sg_dma_len(sg)) { ++ pci_err(ithc->pci, "truncated prd\n"); ++ break; ++ } + prd++; + } + invalidate_kernel_vmap_range(b->addr, b->data_size); +@@ -110,93 +167,139 @@ static int ithc_dma_data_buffer_get(struct ithc *ithc, struct ithc_dma_prd_buffe + return 0; + } + +-int ithc_dma_rx_init(struct ithc *ithc, u8 channel, const char *devname) { ++int ithc_dma_rx_init(struct ithc *ithc, u8 channel) ++{ + struct ithc_dma_rx *rx = &ithc->dma_rx[channel]; + mutex_init(&rx->mutex); ++ ++ // Allocate buffers. + u32 buf_size = DEVCFG_DMA_RX_SIZE(ithc->config.dma_buf_sizes); +- unsigned num_pages = (buf_size + PAGE_SIZE - 1) / PAGE_SIZE; +- pci_dbg(ithc->pci, "allocating rx buffers: num = %u, size = %u, pages = %u\n", NUM_RX_BUF, buf_size, num_pages); ++ unsigned int num_pages = (buf_size + PAGE_SIZE - 1) / PAGE_SIZE; ++ pci_dbg(ithc->pci, "allocating rx buffers: num = %u, size = %u, pages = %u\n", ++ NUM_RX_BUF, buf_size, num_pages); + CHECK_RET(ithc_dma_prd_alloc, ithc, &rx->prds, NUM_RX_BUF, num_pages, DMA_FROM_DEVICE); +- for (unsigned i = 0; i < NUM_RX_BUF; i++) ++ for (unsigned int i = 0; i < NUM_RX_BUF; i++) + CHECK_RET(ithc_dma_data_alloc, ithc, &rx->prds, &rx->bufs[i]); ++ ++ // Init registers. + writeb(DMA_RX_CONTROL2_RESET, &ithc->regs->dma_rx[channel].control2); + lo_hi_writeq(rx->prds.dma_addr, &ithc->regs->dma_rx[channel].addr); + writeb(NUM_RX_BUF - 1, &ithc->regs->dma_rx[channel].num_bufs); + writeb(num_pages - 1, &ithc->regs->dma_rx[channel].num_prds); + u8 head = readb(&ithc->regs->dma_rx[channel].head); +- if (head) { pci_err(ithc->pci, "head is nonzero (%u)\n", head); return -EIO; } +- for (unsigned i = 0; i < NUM_RX_BUF; i++) ++ if (head) { ++ pci_err(ithc->pci, "head is nonzero (%u)\n", head); ++ return -EIO; ++ } ++ ++ // Init buffers. ++ for (unsigned int i = 0; i < NUM_RX_BUF; i++) + CHECK_RET(ithc_dma_data_buffer_put, ithc, &rx->prds, &rx->bufs[i], i); ++ + writeb(head ^ DMA_RX_WRAP_FLAG, &ithc->regs->dma_rx[channel].tail); + return 0; + } +-void ithc_dma_rx_enable(struct ithc *ithc, u8 channel) { +- bitsb_set(&ithc->regs->dma_rx[channel].control, DMA_RX_CONTROL_ENABLE | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_DATA); +- CHECK(waitl, ithc, &ithc->regs->dma_rx[1].status, DMA_RX_STATUS_ENABLED, DMA_RX_STATUS_ENABLED); ++ ++void ithc_dma_rx_enable(struct ithc *ithc, u8 channel) ++{ ++ bitsb_set(&ithc->regs->dma_rx[channel].control, ++ DMA_RX_CONTROL_ENABLE | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_DATA); ++ CHECK(waitl, ithc, &ithc->regs->dma_rx[channel].status, ++ DMA_RX_STATUS_ENABLED, DMA_RX_STATUS_ENABLED); + } + +-int ithc_dma_tx_init(struct ithc *ithc) { ++int ithc_dma_tx_init(struct ithc *ithc) ++{ + struct ithc_dma_tx *tx = &ithc->dma_tx; + mutex_init(&tx->mutex); ++ ++ // Allocate buffers. + tx->max_size = DEVCFG_DMA_TX_SIZE(ithc->config.dma_buf_sizes); +- unsigned num_pages = (tx->max_size + PAGE_SIZE - 1) / PAGE_SIZE; +- pci_dbg(ithc->pci, "allocating tx buffers: size = %u, pages = %u\n", tx->max_size, num_pages); ++ unsigned int num_pages = (tx->max_size + PAGE_SIZE - 1) / PAGE_SIZE; ++ pci_dbg(ithc->pci, "allocating tx buffers: size = %u, pages = %u\n", ++ tx->max_size, num_pages); + CHECK_RET(ithc_dma_prd_alloc, ithc, &tx->prds, 1, num_pages, DMA_TO_DEVICE); + CHECK_RET(ithc_dma_data_alloc, ithc, &tx->prds, &tx->buf); ++ ++ // Init registers. + lo_hi_writeq(tx->prds.dma_addr, &ithc->regs->dma_tx.addr); + writeb(num_pages - 1, &ithc->regs->dma_tx.num_prds); ++ ++ // Init buffers. + CHECK_RET(ithc_dma_data_buffer_put, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0); + return 0; + } + +-static int ithc_dma_rx_process_buf(struct ithc *ithc, struct ithc_dma_data_buffer *data, u8 channel, u8 buf) { ++static int ithc_dma_rx_process_buf(struct ithc *ithc, struct ithc_dma_data_buffer *data, ++ u8 channel, u8 buf) ++{ + if (buf >= NUM_RX_BUF) { + pci_err(ithc->pci, "invalid dma ringbuffer index\n"); + return -EINVAL; + } +- ithc_set_active(ithc); + u32 len = data->data_size; + struct ithc_dma_rx_header *hdr = data->addr; + u8 *hiddata = (void *)(hdr + 1); +- if (len >= sizeof *hdr && hdr->code == DMA_RX_CODE_RESET) { ++ if (len >= sizeof(*hdr) && hdr->code == DMA_RX_CODE_RESET) { ++ // The THC sends a reset request when we need to reinitialize the device. ++ // This usually only happens if we send an invalid command or put the device ++ // in a bad state. + CHECK(ithc_reset, ithc); +- } else if (len < sizeof *hdr || len != sizeof *hdr + hdr->data_size) { ++ } else if (len < sizeof(*hdr) || len != sizeof(*hdr) + hdr->data_size) { + if (hdr->code == DMA_RX_CODE_INPUT_REPORT) { +- // When the CPU enters a low power state during DMA, we can get truncated messages. +- // Typically this will be a single touch HID report that is only 1 byte, or a multitouch report that is 257 bytes. ++ // When the CPU enters a low power state during DMA, we can get truncated ++ // messages. For Surface devices, this will typically be a single touch ++ // report that is only 1 byte, or a multitouch report that is 257 bytes. + // See also ithc_set_active(). + } else { +- pci_err(ithc->pci, "invalid dma rx data! channel %u, buffer %u, size %u, code %u, data size %u\n", channel, buf, len, hdr->code, hdr->data_size); +- print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, hdr, min(len, 0x400u), 0); ++ pci_err(ithc->pci, "invalid dma rx data! channel %u, buffer %u, size %u, code %u, data size %u\n", ++ channel, buf, len, hdr->code, hdr->data_size); ++ print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, ++ hdr, min(len, 0x400u), 0); + } + } else if (hdr->code == DMA_RX_CODE_REPORT_DESCRIPTOR && hdr->data_size > 8) { ++ // Response to a 'get report descriptor' request. ++ // The actual descriptor is preceded by 8 nul bytes. + CHECK(hid_parse_report, ithc->hid, hiddata + 8, hdr->data_size - 8); + WRITE_ONCE(ithc->hid_parse_done, true); + wake_up(&ithc->wait_hid_parse); + } else if (hdr->code == DMA_RX_CODE_INPUT_REPORT) { ++ // Standard HID input report containing touch data. + CHECK(hid_input_report, ithc->hid, HID_INPUT_REPORT, hiddata, hdr->data_size, 1); + } else if (hdr->code == DMA_RX_CODE_FEATURE_REPORT) { ++ // Response to a 'get feature' request. + bool done = false; + mutex_lock(&ithc->hid_get_feature_mutex); + if (ithc->hid_get_feature_buf) { +- if (hdr->data_size < ithc->hid_get_feature_size) ithc->hid_get_feature_size = hdr->data_size; ++ if (hdr->data_size < ithc->hid_get_feature_size) ++ ithc->hid_get_feature_size = hdr->data_size; + memcpy(ithc->hid_get_feature_buf, hiddata, ithc->hid_get_feature_size); + ithc->hid_get_feature_buf = NULL; + done = true; + } + mutex_unlock(&ithc->hid_get_feature_mutex); +- if (done) wake_up(&ithc->wait_hid_get_feature); +- else CHECK(hid_input_report, ithc->hid, HID_FEATURE_REPORT, hiddata, hdr->data_size, 1); ++ if (done) { ++ wake_up(&ithc->wait_hid_get_feature); ++ } else { ++ // Received data without a matching request, or the request already ++ // timed out. (XXX What's the correct thing to do here?) ++ CHECK(hid_input_report, ithc->hid, HID_FEATURE_REPORT, ++ hiddata, hdr->data_size, 1); ++ } + } else { +- pci_dbg(ithc->pci, "unhandled dma rx data! channel %u, buffer %u, size %u, code %u\n", channel, buf, len, hdr->code); +- print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, hdr, min(len, 0x400u), 0); ++ pci_dbg(ithc->pci, "unhandled dma rx data! channel %u, buffer %u, size %u, code %u\n", ++ channel, buf, len, hdr->code); ++ print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, ++ hdr, min(len, 0x400u), 0); + } + return 0; + } + +-static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel) { ++static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel) ++{ ++ // Process all filled RX buffers from the ringbuffer. + struct ithc_dma_rx *rx = &ithc->dma_rx[channel]; +- unsigned n = rx->num_received; ++ unsigned int n = rx->num_received; + u8 head_wrap = readb(&ithc->regs->dma_rx[channel].head); + while (1) { + u8 tail = n % NUM_RX_BUF; +@@ -204,7 +307,8 @@ static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel) { + writeb(tail_wrap, &ithc->regs->dma_rx[channel].tail); + // ringbuffer is full if tail_wrap == head_wrap + // ringbuffer is empty if tail_wrap == head_wrap ^ WRAP_FLAG +- if (tail_wrap == (head_wrap ^ DMA_RX_WRAP_FLAG)) return 0; ++ if (tail_wrap == (head_wrap ^ DMA_RX_WRAP_FLAG)) ++ return 0; + + // take the buffer that the device just filled + struct ithc_dma_data_buffer *b = &rx->bufs[n % NUM_RX_BUF]; +@@ -218,7 +322,8 @@ static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel) { + CHECK_RET(ithc_dma_data_buffer_put, ithc, &rx->prds, b, tail); + } + } +-int ithc_dma_rx(struct ithc *ithc, u8 channel) { ++int ithc_dma_rx(struct ithc *ithc, u8 channel) ++{ + struct ithc_dma_rx *rx = &ithc->dma_rx[channel]; + mutex_lock(&rx->mutex); + int ret = ithc_dma_rx_unlocked(ithc, channel); +@@ -226,14 +331,21 @@ int ithc_dma_rx(struct ithc *ithc, u8 channel) { + return ret; + } + +-static int ithc_dma_tx_unlocked(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data) { ++static int ithc_dma_tx_unlocked(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data) ++{ ++ ithc_set_active(ithc, 100 * USEC_PER_MSEC); ++ ++ // Send a single TX buffer to the THC. + pci_dbg(ithc->pci, "dma tx command %u, size %u\n", cmdcode, datasize); + struct ithc_dma_tx_header *hdr; ++ // Data must be padded to next 4-byte boundary. + u8 padding = datasize & 3 ? 4 - (datasize & 3) : 0; +- unsigned fullsize = sizeof *hdr + datasize + padding; +- if (fullsize > ithc->dma_tx.max_size || fullsize > PAGE_SIZE) return -EINVAL; ++ unsigned int fullsize = sizeof(*hdr) + datasize + padding; ++ if (fullsize > ithc->dma_tx.max_size || fullsize > PAGE_SIZE) ++ return -EINVAL; + CHECK_RET(ithc_dma_data_buffer_get, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0); + ++ // Fill the TX buffer with header and data. + ithc->dma_tx.buf.data_size = fullsize; + hdr = ithc->dma_tx.buf.addr; + hdr->code = cmdcode; +@@ -241,15 +353,18 @@ static int ithc_dma_tx_unlocked(struct ithc *ithc, u32 cmdcode, u32 datasize, vo + u8 *dest = (void *)(hdr + 1); + memcpy(dest, data, datasize); + dest += datasize; +- for (u8 p = 0; p < padding; p++) *dest++ = 0; ++ for (u8 p = 0; p < padding; p++) ++ *dest++ = 0; + CHECK_RET(ithc_dma_data_buffer_put, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0); + ++ // Let the THC process the buffer. + bitsb_set(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND); + CHECK_RET(waitb, ithc, &ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND, 0); + writel(DMA_TX_STATUS_DONE, &ithc->regs->dma_tx.status); + return 0; + } +-int ithc_dma_tx(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data) { ++int ithc_dma_tx(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data) ++{ + mutex_lock(&ithc->dma_tx.mutex); + int ret = ithc_dma_tx_unlocked(ithc, cmdcode, datasize, data); + mutex_unlock(&ithc->dma_tx.mutex); +diff --git a/drivers/hid/ithc/ithc-dma.h b/drivers/hid/ithc/ithc-dma.h +index d9f2c19a13f3..93652e4476bf 100644 +--- a/drivers/hid/ithc/ithc-dma.h ++++ b/drivers/hid/ithc/ithc-dma.h +@@ -1,3 +1,5 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ ++ + #define PRD_SIZE_MASK 0xffffff + #define PRD_FLAG_END 0x1000000 + #define PRD_FLAG_SUCCESS 0x2000000 +@@ -59,7 +61,7 @@ struct ithc_dma_rx { + struct ithc_dma_data_buffer bufs[NUM_RX_BUF]; + }; + +-int ithc_dma_rx_init(struct ithc *ithc, u8 channel, const char *devname); ++int ithc_dma_rx_init(struct ithc *ithc, u8 channel); + void ithc_dma_rx_enable(struct ithc *ithc, u8 channel); + int ithc_dma_tx_init(struct ithc *ithc); + int ithc_dma_rx(struct ithc *ithc, u8 channel); +diff --git a/drivers/hid/ithc/ithc-main.c b/drivers/hid/ithc/ithc-main.c +index 09512b9cb4d3..87ed4aa70fda 100644 +--- a/drivers/hid/ithc/ithc-main.c ++++ b/drivers/hid/ithc/ithc-main.c +@@ -1,3 +1,5 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause ++ + #include "ithc.h" + + MODULE_DESCRIPTION("Intel Touch Host Controller driver"); +@@ -42,6 +44,9 @@ static const struct pci_device_id ithc_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_MTL_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_MTL_PORT2) }, ++ // XXX So far the THC seems to be the only Intel PCI device with PCI_CLASS_INPUT_PEN, ++ // so instead of the device list we could just do: ++ // { .vendor = PCI_VENDOR_ID_INTEL, .device = PCI_ANY_ID, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .class = PCI_CLASS_INPUT_PEN, .class_mask = ~0, }, + {} + }; + MODULE_DEVICE_TABLE(pci, ithc_pci_tbl); +@@ -52,6 +57,7 @@ static bool ithc_use_polling = false; + module_param_named(poll, ithc_use_polling, bool, 0); + MODULE_PARM_DESC(poll, "Use polling instead of interrupts"); + ++// Since all known devices seem to use only channel 1, by default we disable channel 0. + static bool ithc_use_rx0 = false; + module_param_named(rx0, ithc_use_rx0, bool, 0); + MODULE_PARM_DESC(rx0, "Use DMA RX channel 0"); +@@ -60,37 +66,56 @@ static bool ithc_use_rx1 = true; + module_param_named(rx1, ithc_use_rx1, bool, 0); + MODULE_PARM_DESC(rx1, "Use DMA RX channel 1"); + ++// Values below 250 seem to work well on the SP7+. If this is set too high, you may observe cursor stuttering. ++static int ithc_dma_latency_us = 200; ++module_param_named(dma_latency_us, ithc_dma_latency_us, int, 0); ++MODULE_PARM_DESC(dma_latency_us, "Determines the CPU latency QoS value for DMA transfers (in microseconds), -1 to disable latency QoS"); ++ ++// Values above 1700 seem to work well on the SP7+. If this is set too low, you may observe cursor stuttering. ++static unsigned int ithc_dma_early_us = 2000; ++module_param_named(dma_early_us, ithc_dma_early_us, uint, 0); ++MODULE_PARM_DESC(dma_early_us, "Determines how early the CPU latency QoS value is applied before the next expected IRQ (in microseconds)"); ++ + static bool ithc_log_regs_enabled = false; + module_param_named(logregs, ithc_log_regs_enabled, bool, 0); + MODULE_PARM_DESC(logregs, "Log changes in register values (for debugging)"); + + // Sysfs attributes + +-static bool ithc_is_config_valid(struct ithc *ithc) { ++static bool ithc_is_config_valid(struct ithc *ithc) ++{ + return ithc->config.device_id == DEVCFG_DEVICE_ID_TIC; + } + +-static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, char *buf) { ++static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, char *buf) ++{ + struct ithc *ithc = dev_get_drvdata(dev); +- if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV; ++ if (!ithc || !ithc_is_config_valid(ithc)) ++ return -ENODEV; + return sprintf(buf, "0x%04x", ithc->config.vendor_id); + } + static DEVICE_ATTR_RO(vendor); +-static ssize_t product_show(struct device *dev, struct device_attribute *attr, char *buf) { ++static ssize_t product_show(struct device *dev, struct device_attribute *attr, char *buf) ++{ + struct ithc *ithc = dev_get_drvdata(dev); +- if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV; ++ if (!ithc || !ithc_is_config_valid(ithc)) ++ return -ENODEV; + return sprintf(buf, "0x%04x", ithc->config.product_id); + } + static DEVICE_ATTR_RO(product); +-static ssize_t revision_show(struct device *dev, struct device_attribute *attr, char *buf) { ++static ssize_t revision_show(struct device *dev, struct device_attribute *attr, char *buf) ++{ + struct ithc *ithc = dev_get_drvdata(dev); +- if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV; ++ if (!ithc || !ithc_is_config_valid(ithc)) ++ return -ENODEV; + return sprintf(buf, "%u", ithc->config.revision); + } + static DEVICE_ATTR_RO(revision); +-static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { ++static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) ++{ + struct ithc *ithc = dev_get_drvdata(dev); +- if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV; ++ if (!ithc || !ithc_is_config_valid(ithc)) ++ return -ENODEV; + u32 v = ithc->config.fw_version; + return sprintf(buf, "%i.%i.%i.%i", v >> 24, v >> 16 & 0xff, v >> 8 & 0xff, v & 0xff); + } +@@ -117,45 +142,75 @@ static void ithc_hid_stop(struct hid_device *hdev) { } + static int ithc_hid_open(struct hid_device *hdev) { return 0; } + static void ithc_hid_close(struct hid_device *hdev) { } + +-static int ithc_hid_parse(struct hid_device *hdev) { ++static int ithc_hid_parse(struct hid_device *hdev) ++{ + struct ithc *ithc = hdev->driver_data; + u64 val = 0; + WRITE_ONCE(ithc->hid_parse_done, false); +- CHECK_RET(ithc_dma_tx, ithc, DMA_TX_CODE_GET_REPORT_DESCRIPTOR, sizeof val, &val); +- if (!wait_event_timeout(ithc->wait_hid_parse, READ_ONCE(ithc->hid_parse_done), msecs_to_jiffies(1000))) return -ETIMEDOUT; +- return 0; ++ for (int retries = 0; ; retries++) { ++ CHECK_RET(ithc_dma_tx, ithc, DMA_TX_CODE_GET_REPORT_DESCRIPTOR, sizeof(val), &val); ++ if (wait_event_timeout(ithc->wait_hid_parse, READ_ONCE(ithc->hid_parse_done), ++ msecs_to_jiffies(200))) ++ return 0; ++ if (retries > 5) { ++ pci_err(ithc->pci, "failed to read report descriptor\n"); ++ return -ETIMEDOUT; ++ } ++ pci_warn(ithc->pci, "failed to read report descriptor, retrying\n"); ++ } + } + +-static int ithc_hid_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, size_t len, unsigned char rtype, int reqtype) { ++static int ithc_hid_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, ++ size_t len, unsigned char rtype, int reqtype) ++{ + struct ithc *ithc = hdev->driver_data; +- if (!buf || !len) return -EINVAL; ++ if (!buf || !len) ++ return -EINVAL; + u32 code; +- if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) code = DMA_TX_CODE_OUTPUT_REPORT; +- else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT) code = DMA_TX_CODE_SET_FEATURE; +- else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT) code = DMA_TX_CODE_GET_FEATURE; +- else { +- pci_err(ithc->pci, "unhandled hid request %i %i for report id %i\n", rtype, reqtype, reportnum); ++ if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) { ++ code = DMA_TX_CODE_OUTPUT_REPORT; ++ } else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT) { ++ code = DMA_TX_CODE_SET_FEATURE; ++ } else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT) { ++ code = DMA_TX_CODE_GET_FEATURE; ++ } else { ++ pci_err(ithc->pci, "unhandled hid request %i %i for report id %i\n", ++ rtype, reqtype, reportnum); + return -EINVAL; + } + buf[0] = reportnum; ++ + if (reqtype == HID_REQ_GET_REPORT) { ++ // Prepare for response. + mutex_lock(&ithc->hid_get_feature_mutex); + ithc->hid_get_feature_buf = buf; + ithc->hid_get_feature_size = len; + mutex_unlock(&ithc->hid_get_feature_mutex); ++ ++ // Transmit 'get feature' request. + int r = CHECK(ithc_dma_tx, ithc, code, 1, buf); + if (!r) { +- r = wait_event_interruptible_timeout(ithc->wait_hid_get_feature, !ithc->hid_get_feature_buf, msecs_to_jiffies(1000)); +- if (!r) r = -ETIMEDOUT; +- else if (r < 0) r = -EINTR; +- else r = 0; ++ r = wait_event_interruptible_timeout(ithc->wait_hid_get_feature, ++ !ithc->hid_get_feature_buf, msecs_to_jiffies(1000)); ++ if (!r) ++ r = -ETIMEDOUT; ++ else if (r < 0) ++ r = -EINTR; ++ else ++ r = 0; + } ++ ++ // If everything went ok, the buffer has been filled with the response data. ++ // Return the response size. + mutex_lock(&ithc->hid_get_feature_mutex); + ithc->hid_get_feature_buf = NULL; +- if (!r) r = ithc->hid_get_feature_size; ++ if (!r) ++ r = ithc->hid_get_feature_size; + mutex_unlock(&ithc->hid_get_feature_mutex); + return r; + } ++ ++ // 'Set feature', or 'output report'. These don't have a response. + CHECK_RET(ithc_dma_tx, ithc, code, len, buf); + return 0; + } +@@ -169,17 +224,22 @@ static struct hid_ll_driver ithc_ll_driver = { + .raw_request = ithc_hid_raw_request, + }; + +-static void ithc_hid_devres_release(struct device *dev, void *res) { ++static void ithc_hid_devres_release(struct device *dev, void *res) ++{ + struct hid_device **hidm = res; +- if (*hidm) hid_destroy_device(*hidm); ++ if (*hidm) ++ hid_destroy_device(*hidm); + } + +-static int ithc_hid_init(struct ithc *ithc) { +- struct hid_device **hidm = devres_alloc(ithc_hid_devres_release, sizeof *hidm, GFP_KERNEL); +- if (!hidm) return -ENOMEM; ++static int ithc_hid_init(struct ithc *ithc) ++{ ++ struct hid_device **hidm = devres_alloc(ithc_hid_devres_release, sizeof(*hidm), GFP_KERNEL); ++ if (!hidm) ++ return -ENOMEM; + devres_add(&ithc->pci->dev, hidm); + struct hid_device *hid = hid_allocate_device(); +- if (IS_ERR(hid)) return PTR_ERR(hid); ++ if (IS_ERR(hid)) ++ return PTR_ERR(hid); + *hidm = hid; + + strscpy(hid->name, DEVFULLNAME, sizeof(hid->name)); +@@ -198,27 +258,45 @@ static int ithc_hid_init(struct ithc *ithc) { + + // Interrupts/polling + +-static void ithc_activity_timer_callback(struct timer_list *t) { +- struct ithc *ithc = container_of(t, struct ithc, activity_timer); +- cpu_latency_qos_update_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE); ++static enum hrtimer_restart ithc_activity_start_timer_callback(struct hrtimer *t) ++{ ++ struct ithc *ithc = container_of(t, struct ithc, activity_start_timer); ++ ithc_set_active(ithc, ithc_dma_early_us * 2 + USEC_PER_MSEC); ++ return HRTIMER_NORESTART; + } + +-void ithc_set_active(struct ithc *ithc) { +- // When CPU usage is very low, the CPU can enter various low power states (C2-C10). +- // This disrupts DMA, causing truncated DMA messages. ERROR_FLAG_DMA_UNKNOWN_12 will be set when this happens. +- // The amount of truncated messages can become very high, resulting in user-visible effects (laggy/stuttering cursor). +- // To avoid this, we use a CPU latency QoS request to prevent the CPU from entering low power states during touch interactions. +- cpu_latency_qos_update_request(&ithc->activity_qos, 0); +- mod_timer(&ithc->activity_timer, jiffies + msecs_to_jiffies(1000)); +-} +- +-static int ithc_set_device_enabled(struct ithc *ithc, bool enable) { +- u32 x = ithc->config.touch_cfg = (ithc->config.touch_cfg & ~(u32)DEVCFG_TOUCH_MASK) | DEVCFG_TOUCH_UNKNOWN_2 +- | (enable ? DEVCFG_TOUCH_ENABLE | DEVCFG_TOUCH_UNKNOWN_3 | DEVCFG_TOUCH_UNKNOWN_4 : 0); +- return ithc_spi_command(ithc, SPI_CMD_CODE_WRITE, offsetof(struct ithc_device_config, touch_cfg), sizeof x, &x); ++static enum hrtimer_restart ithc_activity_end_timer_callback(struct hrtimer *t) ++{ ++ struct ithc *ithc = container_of(t, struct ithc, activity_end_timer); ++ cpu_latency_qos_update_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE); ++ return HRTIMER_NORESTART; + } + +-static void ithc_disable_interrupts(struct ithc *ithc) { ++void ithc_set_active(struct ithc *ithc, unsigned int duration_us) ++{ ++ if (ithc_dma_latency_us < 0) ++ return; ++ // When CPU usage is very low, the CPU can enter various low power states (C2-C10). ++ // This disrupts DMA, causing truncated DMA messages. ERROR_FLAG_DMA_RX_TIMEOUT will be ++ // set when this happens. The amount of truncated messages can become very high, resulting ++ // in user-visible effects (laggy/stuttering cursor). To avoid this, we use a CPU latency ++ // QoS request to prevent the CPU from entering low power states during touch interactions. ++ cpu_latency_qos_update_request(&ithc->activity_qos, ithc_dma_latency_us); ++ hrtimer_start_range_ns(&ithc->activity_end_timer, ++ ns_to_ktime(duration_us * NSEC_PER_USEC), duration_us * NSEC_PER_USEC, HRTIMER_MODE_REL); ++} ++ ++static int ithc_set_device_enabled(struct ithc *ithc, bool enable) ++{ ++ u32 x = ithc->config.touch_cfg = ++ (ithc->config.touch_cfg & ~(u32)DEVCFG_TOUCH_MASK) | DEVCFG_TOUCH_UNKNOWN_2 | ++ (enable ? DEVCFG_TOUCH_ENABLE | DEVCFG_TOUCH_UNKNOWN_3 | DEVCFG_TOUCH_UNKNOWN_4 : 0); ++ return ithc_spi_command(ithc, SPI_CMD_CODE_WRITE, ++ offsetof(struct ithc_device_config, touch_cfg), sizeof(x), &x); ++} ++ ++static void ithc_disable_interrupts(struct ithc *ithc) ++{ + writel(0, &ithc->regs->error_control); + bitsb(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_IRQ, 0); + bitsb(&ithc->regs->dma_rx[0].control, DMA_RX_CONTROL_IRQ_UNKNOWN_1 | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_UNKNOWN_4 | DMA_RX_CONTROL_IRQ_DATA, 0); +@@ -226,43 +304,85 @@ static void ithc_disable_interrupts(struct ithc *ithc) { + bitsb(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_IRQ, 0); + } + +-static void ithc_clear_dma_rx_interrupts(struct ithc *ithc, unsigned channel) { +- writel(DMA_RX_STATUS_ERROR | DMA_RX_STATUS_UNKNOWN_4 | DMA_RX_STATUS_HAVE_DATA, &ithc->regs->dma_rx[channel].status); ++static void ithc_clear_dma_rx_interrupts(struct ithc *ithc, unsigned int channel) ++{ ++ writel(DMA_RX_STATUS_ERROR | DMA_RX_STATUS_UNKNOWN_4 | DMA_RX_STATUS_HAVE_DATA, ++ &ithc->regs->dma_rx[channel].status); + } + +-static void ithc_clear_interrupts(struct ithc *ithc) { ++static void ithc_clear_interrupts(struct ithc *ithc) ++{ + writel(0xffffffff, &ithc->regs->error_flags); + writel(ERROR_STATUS_DMA | ERROR_STATUS_SPI, &ithc->regs->error_status); + writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status); + ithc_clear_dma_rx_interrupts(ithc, 0); + ithc_clear_dma_rx_interrupts(ithc, 1); +- writel(DMA_TX_STATUS_DONE | DMA_TX_STATUS_ERROR | DMA_TX_STATUS_UNKNOWN_2, &ithc->regs->dma_tx.status); ++ writel(DMA_TX_STATUS_DONE | DMA_TX_STATUS_ERROR | DMA_TX_STATUS_UNKNOWN_2, ++ &ithc->regs->dma_tx.status); + } + +-static void ithc_process(struct ithc *ithc) { ++static void ithc_process(struct ithc *ithc) ++{ + ithc_log_regs(ithc); + +- // read and clear error bits ++ bool rx0 = ithc_use_rx0 && (readl(&ithc->regs->dma_rx[0].status) & (DMA_RX_STATUS_ERROR | DMA_RX_STATUS_HAVE_DATA)) != 0; ++ bool rx1 = ithc_use_rx1 && (readl(&ithc->regs->dma_rx[1].status) & (DMA_RX_STATUS_ERROR | DMA_RX_STATUS_HAVE_DATA)) != 0; ++ ++ // Track time between DMA rx transfers, so we can try to predict when we need to enable CPU latency QoS for the next transfer ++ ktime_t t = ktime_get(); ++ ktime_t dt = ktime_sub(t, ithc->last_rx_time); ++ if (rx0 || rx1) { ++ ithc->last_rx_time = t; ++ if (dt > ms_to_ktime(100)) { ++ ithc->cur_rx_seq_count = 0; ++ ithc->cur_rx_seq_errors = 0; ++ } ++ ithc->cur_rx_seq_count++; ++ if (!ithc_use_polling && ithc_dma_latency_us >= 0) { ++ // Disable QoS, since the DMA transfer has completed (we re-enable it after a delay below) ++ cpu_latency_qos_update_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE); ++ hrtimer_try_to_cancel(&ithc->activity_end_timer); ++ } ++ } ++ ++ // Read and clear error bits + u32 err = readl(&ithc->regs->error_flags); + if (err) { +- if (err & ~ERROR_FLAG_DMA_UNKNOWN_12) pci_err(ithc->pci, "error flags: 0x%08x\n", err); + writel(err, &ithc->regs->error_flags); ++ if (err & ~ERROR_FLAG_DMA_RX_TIMEOUT) ++ pci_err(ithc->pci, "error flags: 0x%08x\n", err); ++ if (err & ERROR_FLAG_DMA_RX_TIMEOUT) { ++ // Only log an error if we see a significant number of these errors. ++ ithc->cur_rx_seq_errors++; ++ if (ithc->cur_rx_seq_errors && ithc->cur_rx_seq_errors % 50 == 0 && ithc->cur_rx_seq_errors > ithc->cur_rx_seq_count / 10) ++ pci_err(ithc->pci, "High number of DMA RX timeouts/errors (%u/%u, dt=%lldus). Try adjusting dma_early_us and/or dma_latency_us.\n", ++ ithc->cur_rx_seq_errors, ithc->cur_rx_seq_count, ktime_to_us(dt)); ++ } + } + +- // process DMA rx ++ // Process DMA rx + if (ithc_use_rx0) { + ithc_clear_dma_rx_interrupts(ithc, 0); +- ithc_dma_rx(ithc, 0); ++ if (rx0) ++ ithc_dma_rx(ithc, 0); + } + if (ithc_use_rx1) { + ithc_clear_dma_rx_interrupts(ithc, 1); +- ithc_dma_rx(ithc, 1); ++ if (rx1) ++ ithc_dma_rx(ithc, 1); ++ } ++ ++ // Start timer to re-enable QoS for next rx, but only if we've seen an ERROR_FLAG_DMA_RX_TIMEOUT ++ if ((rx0 || rx1) && !ithc_use_polling && ithc_dma_latency_us >= 0 && ithc->cur_rx_seq_errors > 0) { ++ ktime_t expires = ktime_add(t, ktime_sub_us(dt, ithc_dma_early_us)); ++ hrtimer_start_range_ns(&ithc->activity_start_timer, expires, 10 * NSEC_PER_USEC, HRTIMER_MODE_ABS); + } + + ithc_log_regs(ithc); + } + +-static irqreturn_t ithc_interrupt_thread(int irq, void *arg) { ++static irqreturn_t ithc_interrupt_thread(int irq, void *arg) ++{ + struct ithc *ithc = arg; + pci_dbg(ithc->pci, "IRQ! err=%08x/%08x/%08x, cmd=%02x/%08x, rx0=%02x/%08x, rx1=%02x/%08x, tx=%02x/%08x\n", + readl(&ithc->regs->error_control), readl(&ithc->regs->error_status), readl(&ithc->regs->error_flags), +@@ -274,14 +394,21 @@ static irqreturn_t ithc_interrupt_thread(int irq, void *arg) { + return IRQ_HANDLED; + } + +-static int ithc_poll_thread(void *arg) { ++static int ithc_poll_thread(void *arg) ++{ + struct ithc *ithc = arg; +- unsigned sleep = 100; ++ unsigned int sleep = 100; + while (!kthread_should_stop()) { + u32 n = ithc->dma_rx[1].num_received; + ithc_process(ithc); +- if (n != ithc->dma_rx[1].num_received) sleep = 20; +- else sleep = min(200u, sleep + (sleep >> 4) + 1); ++ // Decrease polling interval to 20ms if we received data, otherwise slowly ++ // increase it up to 200ms. ++ if (n != ithc->dma_rx[1].num_received) { ++ ithc_set_active(ithc, 100 * USEC_PER_MSEC); ++ sleep = 20; ++ } else { ++ sleep = min(200u, sleep + (sleep >> 4) + 1); ++ } + msleep_interruptible(sleep); + } + return 0; +@@ -289,7 +416,8 @@ static int ithc_poll_thread(void *arg) { + + // Device initialization and shutdown + +-static void ithc_disable(struct ithc *ithc) { ++static void ithc_disable(struct ithc *ithc) ++{ + bitsl_set(&ithc->regs->control_bits, CONTROL_QUIESCE); + CHECK(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, CONTROL_IS_QUIESCED); + bitsl(&ithc->regs->control_bits, CONTROL_NRESET, 0); +@@ -301,81 +429,112 @@ static void ithc_disable(struct ithc *ithc) { + ithc_clear_interrupts(ithc); + } + +-static int ithc_init_device(struct ithc *ithc) { ++static int ithc_init_device(struct ithc *ithc) ++{ + ithc_log_regs(ithc); + bool was_enabled = (readl(&ithc->regs->control_bits) & CONTROL_NRESET) != 0; + ithc_disable(ithc); + CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_READY, CONTROL_READY); ++ ++ // Since we don't yet know which SPI config the device wants, use default speed and mode ++ // initially for reading config data. + ithc_set_spi_config(ithc, 10, 0); +- bitsl_set(&ithc->regs->dma_rx[0].unknown_init_bits, 0x80000000); // seems to help with reading config + +- if (was_enabled) if (msleep_interruptible(100)) return -EINTR; ++ // Setting the following bit seems to make reading the config more reliable. ++ bitsl_set(&ithc->regs->dma_rx[0].unknown_init_bits, 0x80000000); ++ ++ // If the device was previously enabled, wait a bit to make sure it's fully shut down. ++ if (was_enabled) ++ if (msleep_interruptible(100)) ++ return -EINTR; ++ ++ // Take the touch device out of reset. + bitsl(&ithc->regs->control_bits, CONTROL_QUIESCE, 0); + CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, 0); + for (int retries = 0; ; retries++) { + ithc_log_regs(ithc); + bitsl_set(&ithc->regs->control_bits, CONTROL_NRESET); +- if (!waitl(ithc, &ithc->regs->state, 0xf, 2)) break; ++ if (!waitl(ithc, &ithc->regs->state, 0xf, 2)) ++ break; + if (retries > 5) { +- pci_err(ithc->pci, "too many retries, failed to reset device\n"); ++ pci_err(ithc->pci, "failed to reset device, state = 0x%08x\n", readl(&ithc->regs->state)); + return -ETIMEDOUT; + } +- pci_err(ithc->pci, "invalid state, retrying reset\n"); ++ pci_warn(ithc->pci, "invalid state, retrying reset\n"); + bitsl(&ithc->regs->control_bits, CONTROL_NRESET, 0); +- if (msleep_interruptible(1000)) return -EINTR; ++ if (msleep_interruptible(1000)) ++ return -EINTR; + } + ithc_log_regs(ithc); + ++ // Waiting for the following status bit makes reading config much more reliable, ++ // however the official driver does not seem to do this... + CHECK(waitl, ithc, &ithc->regs->dma_rx[0].status, DMA_RX_STATUS_UNKNOWN_4, DMA_RX_STATUS_UNKNOWN_4); + +- // read config ++ // Read configuration data. + for (int retries = 0; ; retries++) { + ithc_log_regs(ithc); +- memset(&ithc->config, 0, sizeof ithc->config); +- CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, 0, sizeof ithc->config, &ithc->config); ++ memset(&ithc->config, 0, sizeof(ithc->config)); ++ CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, 0, sizeof(ithc->config), &ithc->config); + u32 *p = (void *)&ithc->config; + pci_info(ithc->pci, "config: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); +- if (ithc_is_config_valid(ithc)) break; ++ if (ithc_is_config_valid(ithc)) ++ break; + if (retries > 10) { +- pci_err(ithc->pci, "failed to read config, unknown device ID 0x%08x\n", ithc->config.device_id); ++ pci_err(ithc->pci, "failed to read config, unknown device ID 0x%08x\n", ++ ithc->config.device_id); + return -EIO; + } +- pci_err(ithc->pci, "failed to read config, retrying\n"); +- if (msleep_interruptible(100)) return -EINTR; ++ pci_warn(ithc->pci, "failed to read config, retrying\n"); ++ if (msleep_interruptible(100)) ++ return -EINTR; + } + ithc_log_regs(ithc); + +- CHECK_RET(ithc_set_spi_config, ithc, DEVCFG_SPI_MAX_FREQ(ithc->config.spi_config), DEVCFG_SPI_MODE(ithc->config.spi_config)); ++ // Apply SPI config and enable touch device. ++ CHECK_RET(ithc_set_spi_config, ithc, ++ DEVCFG_SPI_MAX_FREQ(ithc->config.spi_config), ++ DEVCFG_SPI_MODE(ithc->config.spi_config)); + CHECK_RET(ithc_set_device_enabled, ithc, true); + ithc_log_regs(ithc); + return 0; + } + +-int ithc_reset(struct ithc *ithc) { +- // FIXME This should probably do devres_release_group()+ithc_start(). But because this is called during DMA +- // processing, that would have to be done asynchronously (schedule_work()?). And with extra locking? ++int ithc_reset(struct ithc *ithc) ++{ ++ // FIXME This should probably do devres_release_group()+ithc_start(). ++ // But because this is called during DMA processing, that would have to be done ++ // asynchronously (schedule_work()?). And with extra locking? + pci_err(ithc->pci, "reset\n"); + CHECK(ithc_init_device, ithc); +- if (ithc_use_rx0) ithc_dma_rx_enable(ithc, 0); +- if (ithc_use_rx1) ithc_dma_rx_enable(ithc, 1); ++ if (ithc_use_rx0) ++ ithc_dma_rx_enable(ithc, 0); ++ if (ithc_use_rx1) ++ ithc_dma_rx_enable(ithc, 1); + ithc_log_regs(ithc); + pci_dbg(ithc->pci, "reset completed\n"); + return 0; + } + +-static void ithc_stop(void *res) { ++static void ithc_stop(void *res) ++{ + struct ithc *ithc = res; + pci_dbg(ithc->pci, "stopping\n"); + ithc_log_regs(ithc); +- if (ithc->poll_thread) CHECK(kthread_stop, ithc->poll_thread); +- if (ithc->irq >= 0) disable_irq(ithc->irq); ++ ++ if (ithc->poll_thread) ++ CHECK(kthread_stop, ithc->poll_thread); ++ if (ithc->irq >= 0) ++ disable_irq(ithc->irq); + CHECK(ithc_set_device_enabled, ithc, false); + ithc_disable(ithc); +- del_timer_sync(&ithc->activity_timer); ++ hrtimer_cancel(&ithc->activity_start_timer); ++ hrtimer_cancel(&ithc->activity_end_timer); + cpu_latency_qos_remove_request(&ithc->activity_qos); +- // clear dma config +- for(unsigned i = 0; i < 2; i++) { ++ ++ // Clear DMA config. ++ for (unsigned int i = 0; i < 2; i++) { + CHECK(waitl, ithc, &ithc->regs->dma_rx[i].status, DMA_RX_STATUS_ENABLED, 0); + lo_hi_writeq(0, &ithc->regs->dma_rx[i].addr); + writeb(0, &ithc->regs->dma_rx[i].num_bufs); +@@ -383,35 +542,43 @@ static void ithc_stop(void *res) { + } + lo_hi_writeq(0, &ithc->regs->dma_tx.addr); + writeb(0, &ithc->regs->dma_tx.num_prds); ++ + ithc_log_regs(ithc); + pci_dbg(ithc->pci, "stopped\n"); + } + +-static void ithc_clear_drvdata(void *res) { ++static void ithc_clear_drvdata(void *res) ++{ + struct pci_dev *pci = res; + pci_set_drvdata(pci, NULL); + } + +-static int ithc_start(struct pci_dev *pci) { ++static int ithc_start(struct pci_dev *pci) ++{ + pci_dbg(pci, "starting\n"); + if (pci_get_drvdata(pci)) { + pci_err(pci, "device already initialized\n"); + return -EINVAL; + } +- if (!devres_open_group(&pci->dev, ithc_start, GFP_KERNEL)) return -ENOMEM; ++ if (!devres_open_group(&pci->dev, ithc_start, GFP_KERNEL)) ++ return -ENOMEM; + +- struct ithc *ithc = devm_kzalloc(&pci->dev, sizeof *ithc, GFP_KERNEL); +- if (!ithc) return -ENOMEM; ++ // Allocate/init main driver struct. ++ struct ithc *ithc = devm_kzalloc(&pci->dev, sizeof(*ithc), GFP_KERNEL); ++ if (!ithc) ++ return -ENOMEM; + ithc->irq = -1; + ithc->pci = pci; +- snprintf(ithc->phys, sizeof ithc->phys, "pci-%s/" DEVNAME, pci_name(pci)); ++ snprintf(ithc->phys, sizeof(ithc->phys), "pci-%s/" DEVNAME, pci_name(pci)); + init_waitqueue_head(&ithc->wait_hid_parse); + init_waitqueue_head(&ithc->wait_hid_get_feature); + mutex_init(&ithc->hid_get_feature_mutex); + pci_set_drvdata(pci, ithc); + CHECK_RET(devm_add_action_or_reset, &pci->dev, ithc_clear_drvdata, pci); +- if (ithc_log_regs_enabled) ithc->prev_regs = devm_kzalloc(&pci->dev, sizeof *ithc->prev_regs, GFP_KERNEL); ++ if (ithc_log_regs_enabled) ++ ithc->prev_regs = devm_kzalloc(&pci->dev, sizeof(*ithc->prev_regs), GFP_KERNEL); + ++ // PCI initialization. + CHECK_RET(pcim_enable_device, pci); + pci_set_master(pci); + CHECK_RET(pcim_iomap_regions, pci, BIT(0), DEVNAME " regs"); +@@ -419,29 +586,39 @@ static int ithc_start(struct pci_dev *pci) { + CHECK_RET(pci_set_power_state, pci, PCI_D0); + ithc->regs = pcim_iomap_table(pci)[0]; + ++ // Allocate IRQ. + if (!ithc_use_polling) { + CHECK_RET(pci_alloc_irq_vectors, pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_MSIX); + ithc->irq = CHECK(pci_irq_vector, pci, 0); +- if (ithc->irq < 0) return ithc->irq; ++ if (ithc->irq < 0) ++ return ithc->irq; + } + ++ // Initialize THC and touch device. + CHECK_RET(ithc_init_device, ithc); + CHECK(devm_device_add_groups, &pci->dev, ithc_attribute_groups); +- if (ithc_use_rx0) CHECK_RET(ithc_dma_rx_init, ithc, 0, ithc_use_rx1 ? DEVNAME "0" : DEVNAME); +- if (ithc_use_rx1) CHECK_RET(ithc_dma_rx_init, ithc, 1, ithc_use_rx0 ? DEVNAME "1" : DEVNAME); ++ if (ithc_use_rx0) ++ CHECK_RET(ithc_dma_rx_init, ithc, 0); ++ if (ithc_use_rx1) ++ CHECK_RET(ithc_dma_rx_init, ithc, 1); + CHECK_RET(ithc_dma_tx_init, ithc); + +- CHECK_RET(ithc_hid_init, ithc); +- + cpu_latency_qos_add_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE); +- timer_setup(&ithc->activity_timer, ithc_activity_timer_callback, 0); ++ hrtimer_init(&ithc->activity_start_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); ++ ithc->activity_start_timer.function = ithc_activity_start_timer_callback; ++ hrtimer_init(&ithc->activity_end_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ ithc->activity_end_timer.function = ithc_activity_end_timer_callback; + +- // add ithc_stop callback AFTER setting up DMA buffers, so that polling/irqs/DMA are disabled BEFORE the buffers are freed ++ // Add ithc_stop() callback AFTER setting up DMA buffers, so that polling/irqs/DMA are ++ // disabled BEFORE the buffers are freed. + CHECK_RET(devm_add_action_or_reset, &pci->dev, ithc_stop, ithc); + ++ CHECK_RET(ithc_hid_init, ithc); ++ ++ // Start polling/IRQ. + if (ithc_use_polling) { + pci_info(pci, "using polling instead of irq\n"); +- // use a thread instead of simple timer because we want to be able to sleep ++ // Use a thread instead of simple timer because we want to be able to sleep. + ithc->poll_thread = kthread_run(ithc_poll_thread, ithc, DEVNAME "poll"); + if (IS_ERR(ithc->poll_thread)) { + int err = PTR_ERR(ithc->poll_thread); +@@ -449,13 +626,17 @@ static int ithc_start(struct pci_dev *pci) { + return err; + } + } else { +- CHECK_RET(devm_request_threaded_irq, &pci->dev, ithc->irq, NULL, ithc_interrupt_thread, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, DEVNAME, ithc); ++ CHECK_RET(devm_request_threaded_irq, &pci->dev, ithc->irq, NULL, ++ ithc_interrupt_thread, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, DEVNAME, ithc); + } + +- if (ithc_use_rx0) ithc_dma_rx_enable(ithc, 0); +- if (ithc_use_rx1) ithc_dma_rx_enable(ithc, 1); ++ if (ithc_use_rx0) ++ ithc_dma_rx_enable(ithc, 0); ++ if (ithc_use_rx1) ++ ithc_dma_rx_enable(ithc, 1); + +- // hid_add_device can only be called after irq/polling is started and DMA is enabled, because it calls ithc_hid_parse which reads the report descriptor via DMA ++ // hid_add_device() can only be called after irq/polling is started and DMA is enabled, ++ // because it calls ithc_hid_parse() which reads the report descriptor via DMA. + CHECK_RET(hid_add_device, ithc->hid); + + CHECK(ithc_debug_init, ithc); +@@ -464,43 +645,54 @@ static int ithc_start(struct pci_dev *pci) { + return 0; + } + +-static int ithc_probe(struct pci_dev *pci, const struct pci_device_id *id) { ++static int ithc_probe(struct pci_dev *pci, const struct pci_device_id *id) ++{ + pci_dbg(pci, "device probe\n"); + return ithc_start(pci); + } + +-static void ithc_remove(struct pci_dev *pci) { ++static void ithc_remove(struct pci_dev *pci) ++{ + pci_dbg(pci, "device remove\n"); + // all cleanup is handled by devres + } + +-static int ithc_suspend(struct device *dev) { ++// For suspend/resume, we just deinitialize and reinitialize everything. ++// TODO It might be cleaner to keep the HID device around, however we would then have to signal ++// to userspace that the touch device has lost state and userspace needs to e.g. resend 'set ++// feature' requests. Hidraw does not seem to have a facility to do that. ++static int ithc_suspend(struct device *dev) ++{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm suspend\n"); + devres_release_group(dev, ithc_start); + return 0; + } + +-static int ithc_resume(struct device *dev) { ++static int ithc_resume(struct device *dev) ++{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm resume\n"); + return ithc_start(pci); + } + +-static int ithc_freeze(struct device *dev) { ++static int ithc_freeze(struct device *dev) ++{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm freeze\n"); + devres_release_group(dev, ithc_start); + return 0; + } + +-static int ithc_thaw(struct device *dev) { ++static int ithc_thaw(struct device *dev) ++{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm thaw\n"); + return ithc_start(pci); + } + +-static int ithc_restore(struct device *dev) { ++static int ithc_restore(struct device *dev) ++{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm restore\n"); + return ithc_start(pci); +@@ -521,11 +713,13 @@ static struct pci_driver ithc_driver = { + //.dev_groups = ithc_attribute_groups, // could use this (since 5.14), however the attributes won't have valid values until config has been read anyway + }; + +-static int __init ithc_init(void) { ++static int __init ithc_init(void) ++{ + return pci_register_driver(&ithc_driver); + } + +-static void __exit ithc_exit(void) { ++static void __exit ithc_exit(void) ++{ + pci_unregister_driver(&ithc_driver); + } + +diff --git a/drivers/hid/ithc/ithc-regs.c b/drivers/hid/ithc/ithc-regs.c +index 85d567b05761..e058721886e3 100644 +--- a/drivers/hid/ithc/ithc-regs.c ++++ b/drivers/hid/ithc/ithc-regs.c +@@ -1,63 +1,95 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause ++ + #include "ithc.h" + + #define reg_num(r) (0x1fff & (u16)(__force u64)(r)) + +-void bitsl(__iomem u32 *reg, u32 mask, u32 val) { +- if (val & ~mask) pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n", reg_num(reg), val, mask); ++void bitsl(__iomem u32 *reg, u32 mask, u32 val) ++{ ++ if (val & ~mask) ++ pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n", ++ reg_num(reg), val, mask); + writel((readl(reg) & ~mask) | (val & mask), reg); + } + +-void bitsb(__iomem u8 *reg, u8 mask, u8 val) { +- if (val & ~mask) pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n", reg_num(reg), val, mask); ++void bitsb(__iomem u8 *reg, u8 mask, u8 val) ++{ ++ if (val & ~mask) ++ pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n", ++ reg_num(reg), val, mask); + writeb((readb(reg) & ~mask) | (val & mask), reg); + } + +-int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val) { +- pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", reg_num(reg), mask, val); ++int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val) ++{ ++ pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", ++ reg_num(reg), mask, val); + u32 x; + if (readl_poll_timeout(reg, x, (x & mask) == val, 200, 1000*1000)) { +- pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", reg_num(reg), mask, val); ++ pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", ++ reg_num(reg), mask, val); + return -ETIMEDOUT; + } + pci_dbg(ithc->pci, "done waiting\n"); + return 0; + } + +-int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val) { +- pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", reg_num(reg), mask, val); ++int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val) ++{ ++ pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", ++ reg_num(reg), mask, val); + u8 x; + if (readb_poll_timeout(reg, x, (x & mask) == val, 200, 1000*1000)) { +- pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", reg_num(reg), mask, val); ++ pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", ++ reg_num(reg), mask, val); + return -ETIMEDOUT; + } + pci_dbg(ithc->pci, "done waiting\n"); + return 0; + } + +-int ithc_set_spi_config(struct ithc *ithc, u8 speed, u8 mode) { ++int ithc_set_spi_config(struct ithc *ithc, u8 speed, u8 mode) ++{ + pci_dbg(ithc->pci, "setting SPI speed to %i, mode %i\n", speed, mode); +- if (mode == 3) mode = 2; ++ if (mode == 3) ++ mode = 2; + bitsl(&ithc->regs->spi_config, + SPI_CONFIG_MODE(0xff) | SPI_CONFIG_SPEED(0xff) | SPI_CONFIG_UNKNOWN_18(0xff) | SPI_CONFIG_SPEED2(0xff), + SPI_CONFIG_MODE(mode) | SPI_CONFIG_SPEED(speed) | SPI_CONFIG_UNKNOWN_18(0) | SPI_CONFIG_SPEED2(speed)); + return 0; + } + +-int ithc_spi_command(struct ithc *ithc, u8 command, u32 offset, u32 size, void *data) { ++int ithc_spi_command(struct ithc *ithc, u8 command, u32 offset, u32 size, void *data) ++{ + pci_dbg(ithc->pci, "SPI command %u, size %u, offset %u\n", command, size, offset); +- if (size > sizeof ithc->regs->spi_cmd.data) return -EINVAL; ++ if (size > sizeof(ithc->regs->spi_cmd.data)) ++ return -EINVAL; ++ ++ // Wait if the device is still busy. + CHECK_RET(waitl, ithc, &ithc->regs->spi_cmd.status, SPI_CMD_STATUS_BUSY, 0); ++ // Clear result flags. + writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status); ++ ++ // Init SPI command data. + writeb(command, &ithc->regs->spi_cmd.code); + writew(size, &ithc->regs->spi_cmd.size); + writel(offset, &ithc->regs->spi_cmd.offset); + u32 *p = data, n = (size + 3) / 4; +- for (u32 i = 0; i < n; i++) writel(p[i], &ithc->regs->spi_cmd.data[i]); ++ for (u32 i = 0; i < n; i++) ++ writel(p[i], &ithc->regs->spi_cmd.data[i]); ++ ++ // Start transmission. + bitsb_set(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_SEND); + CHECK_RET(waitl, ithc, &ithc->regs->spi_cmd.status, SPI_CMD_STATUS_BUSY, 0); +- if ((readl(&ithc->regs->spi_cmd.status) & (SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR)) != SPI_CMD_STATUS_DONE) return -EIO; +- if (readw(&ithc->regs->spi_cmd.size) != size) return -EMSGSIZE; +- for (u32 i = 0; i < n; i++) p[i] = readl(&ithc->regs->spi_cmd.data[i]); ++ ++ // Read response. ++ if ((readl(&ithc->regs->spi_cmd.status) & (SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR)) != SPI_CMD_STATUS_DONE) ++ return -EIO; ++ if (readw(&ithc->regs->spi_cmd.size) != size) ++ return -EMSGSIZE; ++ for (u32 i = 0; i < n; i++) ++ p[i] = readl(&ithc->regs->spi_cmd.data[i]); ++ + writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status); + return 0; + } +diff --git a/drivers/hid/ithc/ithc-regs.h b/drivers/hid/ithc/ithc-regs.h +index 1a96092ed7ee..d4007d9e2bac 100644 +--- a/drivers/hid/ithc/ithc-regs.h ++++ b/drivers/hid/ithc/ithc-regs.h +@@ -1,3 +1,5 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ ++ + #define CONTROL_QUIESCE BIT(1) + #define CONTROL_IS_QUIESCED BIT(2) + #define CONTROL_NRESET BIT(3) +@@ -24,7 +26,7 @@ + + #define ERROR_FLAG_DMA_UNKNOWN_9 BIT(9) + #define ERROR_FLAG_DMA_UNKNOWN_10 BIT(10) +-#define ERROR_FLAG_DMA_UNKNOWN_12 BIT(12) // set when we receive a truncated DMA message ++#define ERROR_FLAG_DMA_RX_TIMEOUT BIT(12) // set when we receive a truncated DMA message + #define ERROR_FLAG_DMA_UNKNOWN_13 BIT(13) + #define ERROR_FLAG_SPI_BUS_TURNAROUND BIT(16) + #define ERROR_FLAG_SPI_RESPONSE_TIMEOUT BIT(17) +@@ -67,6 +69,7 @@ + #define DMA_RX_STATUS_HAVE_DATA BIT(5) + #define DMA_RX_STATUS_ENABLED BIT(8) + ++// COUNTER_RESET can be written to counter registers to reset them to zero. However, in some cases this can mess up the THC. + #define COUNTER_RESET BIT(31) + + struct ithc_registers { +@@ -147,15 +150,15 @@ static_assert(sizeof(struct ithc_registers) == 0x1300); + #define DEVCFG_SPI_MAX_FREQ(x) (((x) >> 1) & 0xf) // high bit = use high speed mode? + #define DEVCFG_SPI_MODE(x) (((x) >> 6) & 3) + #define DEVCFG_SPI_UNKNOWN_8(x) (((x) >> 8) & 0x3f) +-#define DEVCFG_SPI_NEEDS_HEARTBEAT BIT(20) +-#define DEVCFG_SPI_HEARTBEAT_INTERVAL (((x) >> 21) & 7) ++#define DEVCFG_SPI_NEEDS_HEARTBEAT BIT(20) // TODO implement heartbeat ++#define DEVCFG_SPI_HEARTBEAT_INTERVAL(x) (((x) >> 21) & 7) + #define DEVCFG_SPI_UNKNOWN_25 BIT(25) + #define DEVCFG_SPI_UNKNOWN_26 BIT(26) + #define DEVCFG_SPI_UNKNOWN_27 BIT(27) +-#define DEVCFG_SPI_DELAY (((x) >> 28) & 7) +-#define DEVCFG_SPI_USE_EXT_READ_CFG BIT(31) ++#define DEVCFG_SPI_DELAY(x) (((x) >> 28) & 7) // TODO use this ++#define DEVCFG_SPI_USE_EXT_READ_CFG BIT(31) // TODO use this? + +-struct ithc_device_config { ++struct ithc_device_config { // (Example values are from an SP7+.) + u32 _unknown_00; // 00 = 0xe0000402 (0xe0000401 after DMA_RX_CODE_RESET) + u32 _unknown_04; // 04 = 0x00000000 + u32 dma_buf_sizes; // 08 = 0x000a00ff +@@ -166,9 +169,9 @@ struct ithc_device_config { + u16 vendor_id; // 1c = 0x045e = Microsoft Corp. + u16 product_id; // 1e = 0x0c1a + u32 revision; // 20 = 0x00000001 +- u32 fw_version; // 24 = 0x05008a8b = 5.0.138.139 ++ u32 fw_version; // 24 = 0x05008a8b = 5.0.138.139 (this value looks more random on newer devices) + u32 _unknown_28; // 28 = 0x00000000 +- u32 fw_mode; // 2c = 0x00000000 ++ u32 fw_mode; // 2c = 0x00000000 (for fw update?) + u32 _unknown_30; // 30 = 0x00000000 + u32 _unknown_34; // 34 = 0x0404035e (u8,u8,u8,u8 = version?) + u32 _unknown_38; // 38 = 0x000001c0 (0x000001c1 after DMA_RX_CODE_RESET) +diff --git a/drivers/hid/ithc/ithc.h b/drivers/hid/ithc/ithc.h +index 6a9b0d480bc1..028e55a4ec53 100644 +--- a/drivers/hid/ithc/ithc.h ++++ b/drivers/hid/ithc/ithc.h +@@ -1,3 +1,5 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ ++ + #include + #include + #include +@@ -21,7 +23,7 @@ + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + + #define CHECK(fn, ...) ({ int r = fn(__VA_ARGS__); if (r < 0) pci_err(ithc->pci, "%s: %s failed with %i\n", __func__, #fn, r); r; }) +-#define CHECK_RET(...) do { int r = CHECK(__VA_ARGS__); if (r < 0) return r; } while(0) ++#define CHECK_RET(...) do { int r = CHECK(__VA_ARGS__); if (r < 0) return r; } while (0) + + #define NUM_RX_BUF 16 + +@@ -35,8 +37,13 @@ struct ithc { + struct pci_dev *pci; + int irq; + struct task_struct *poll_thread; ++ + struct pm_qos_request activity_qos; +- struct timer_list activity_timer; ++ struct hrtimer activity_start_timer; ++ struct hrtimer activity_end_timer; ++ ktime_t last_rx_time; ++ unsigned int cur_rx_seq_count; ++ unsigned int cur_rx_seq_errors; + + struct hid_device *hid; + bool hid_parse_done; +@@ -54,7 +61,7 @@ struct ithc { + }; + + int ithc_reset(struct ithc *ithc); +-void ithc_set_active(struct ithc *ithc); ++void ithc_set_active(struct ithc *ithc, unsigned int duration_us); + int ithc_debug_init(struct ithc *ithc); + void ithc_log_regs(struct ithc *ithc); + +-- +2.42.0 + diff --git a/patches/6.1/0006-surface-sam.patch b/patches/6.1/0006-surface-sam.patch index 49dd87dd7..99576e9cc 100644 --- a/patches/6.1/0006-surface-sam.patch +++ b/patches/6.1/0006-surface-sam.patch @@ -1,4 +1,4 @@ -From 7b947e28b02aea17aaa069c4a88179fa1b93e647 Mon Sep 17 00:00:00 2001 +From a69beadaa0d1681de669c332c97877ecada4ff04 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Fri, 2 Dec 2022 23:33:20 +0100 Subject: [PATCH] platform/surface: aggregator: Improve documentation and @@ -289,7 +289,7 @@ index 45501b6e54e8..5c4ae1a26183 100644 -- 2.42.0 -From 1df4eb71a41b40ab9c65874cf629cd5a577009db Mon Sep 17 00:00:00 2001 +From 2538a6474f85c84ffe2ae147756b63dbf6f956ed Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Fri, 2 Dec 2022 23:33:21 +0100 Subject: [PATCH] platform/surface: aggregator: Add target and source IDs to @@ -486,7 +486,7 @@ index 2a2c17771d01..55cc61bba1da 100644 -- 2.42.0 -From 129001ed2269657d940af23a00b684a77aeb508a Mon Sep 17 00:00:00 2001 +From 349ed980a0d8b04357223f6b10223fc32416564c Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Fri, 2 Dec 2022 23:33:22 +0100 Subject: [PATCH] platform/surface: aggregator_hub: Use target-ID enum instead @@ -529,7 +529,7 @@ index 43061514be38..62f27cdb6ca8 100644 -- 2.42.0 -From c44b24d4a71d687973bd97e87f4ae789bc5832b3 Mon Sep 17 00:00:00 2001 +From 989f1ef2c2527fb281a443833e78ab2ab1c6c7eb Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Fri, 2 Dec 2022 23:33:23 +0100 Subject: [PATCH] platform/surface: aggregator_tabletsw: Use target-ID enum @@ -581,7 +581,7 @@ index af8b547cffdc..c8ecbdbb516c 100644 -- 2.42.0 -From 1d6bffa4ac16424b8a000c98b381bff7be9f0a4a Mon Sep 17 00:00:00 2001 +From 018110bd419ac873908a8c2a65d49428371a6063 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Fri, 2 Dec 2022 23:33:24 +0100 Subject: [PATCH] platform/surface: dtx: Use target-ID enum instead of @@ -679,7 +679,7 @@ index ed36944467f9..0de76a784a35 100644 -- 2.42.0 -From 6a0db605335a9e849412ded9be8f6371a6566a8a Mon Sep 17 00:00:00 2001 +From 7908cc5cdb1f8a1caa3cab9f773433b1c549f665 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Fri, 2 Dec 2022 23:33:25 +0100 Subject: [PATCH] HID: surface-hid: Use target-ID enum instead of hard-coding @@ -713,7 +713,7 @@ index 0635341bc517..42933bf3e925 100644 -- 2.42.0 -From 72444374cb597c90d72583215eef48a8a985ac0e Mon Sep 17 00:00:00 2001 +From 45354a56977fbc63bcb36c4e803732ab437c718a Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Fri, 2 Dec 2022 23:33:26 +0100 Subject: [PATCH] platform/surface: aggregator: Enforce use of target-ID enum @@ -949,7 +949,7 @@ index 46c45d1b6368..4da20b7a0ee5 100644 -- 2.42.0 -From e6c060f1a1091fc902a8258205cb871ab081583f Mon Sep 17 00:00:00 2001 +From 29c332eb0f81e97e2c9ff1f2bacbbc6712756f11 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Fri, 2 Dec 2022 23:33:27 +0100 Subject: [PATCH] platform/surface: aggregator_registry: Fix target-ID of @@ -1004,7 +1004,7 @@ index 023f126121d7..296f72d52e6a 100644 -- 2.42.0 -From 2d1d5d8fc6a6c345d400582e3a8d1e16c08995fc Mon Sep 17 00:00:00 2001 +From 1915385579364bcd7c078530586722dca08d9772 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Tue, 20 Dec 2022 18:56:08 +0100 Subject: [PATCH] platform/surface: aggregator: Rename top-level request @@ -1551,7 +1551,7 @@ index 4da20b7a0ee5..1545e5567b15 100644 -- 2.42.0 -From 6b39ff5dfc64b6d1cb8f0995e80b7dc923922407 Mon Sep 17 00:00:00 2001 +From 0057fe4e98e3d75b3c045e9ca0e071fdee753589 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 18 Jan 2023 11:38:23 +0200 Subject: [PATCH] platform/surface: Switch to use acpi_evaluate_dsm_typed() @@ -1607,7 +1607,7 @@ index f004a2495201..7b6d887dccdb 100644 -- 2.42.0 -From 755d4ebede69122942e998bdd864ab60755eefbb Mon Sep 17 00:00:00 2001 +From 89013890d7ce671acb4e4bec8034dbae7071b4cb Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 4 Mar 2023 20:09:36 +0100 Subject: [PATCH] platform/surface: aggregator_tabletsw: Properly handle @@ -1915,7 +1915,7 @@ index a18e9fc7896b..f9a58db6afde 100644 -- 2.42.0 -From 6d0e87b34f0a9e6a2673205f56982f533e91dcbb Mon Sep 17 00:00:00 2001 +From cf85d1e0cb3613baa936f43ad87b9a0d9122fecc Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 19 Feb 2023 23:33:43 +0100 Subject: [PATCH] platform/surface: aggregator_tabletsw: Add support for @@ -2032,7 +2032,7 @@ index f9a58db6afde..4a029f5db20a 100644 -- 2.42.0 -From 950a7a72939437954ca3cd3e515c8abfe1f6e9ce Mon Sep 17 00:00:00 2001 +From 20688f06510a9db93799ca6957453a2a12f07287 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Thu, 18 May 2023 22:57:17 +0200 Subject: [PATCH] platform/surface: aggregator_tabletsw: Add support for book @@ -2086,7 +2086,7 @@ index 4a029f5db20a..c0a1a5869246 100644 -- 2.42.0 -From 9ceb31c45d3dd5b0bd7903e4edae96d0bf695d23 Mon Sep 17 00:00:00 2001 +From 1c18d2daf0e8ea9d70a62f7782e5b47a5ecf6abc Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 19 Feb 2023 23:41:18 +0100 Subject: [PATCH] platform/surface: aggregator_registry: Add support for @@ -2117,7 +2117,7 @@ index 296f72d52e6a..0fe5be539652 100644 -- 2.42.0 -From 08bded49f1ca35c1602bc26522a4b0ed71053947 Mon Sep 17 00:00:00 2001 +From 18debe9b40bddd21921b14eeb1d4f1db675a2bda Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 22 Oct 2023 14:57:11 +0200 Subject: [PATCH] platform/surface: aggregator_registry: Add support for diff --git a/patches/6.1/0007-surface-sam-over-hid.patch b/patches/6.1/0007-surface-sam-over-hid.patch index 164a8823a..b15405cac 100644 --- a/patches/6.1/0007-surface-sam-over-hid.patch +++ b/patches/6.1/0007-surface-sam-over-hid.patch @@ -1,4 +1,4 @@ -From 5cfda2b8e2d5c4938bd8c9d4ad9cb97a290a6a9f Mon Sep 17 00:00:00 2001 +From 09df94d2f7ff01d5fef9a69008fcae6e97350628 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 25 Jul 2020 17:19:53 +0200 Subject: [PATCH] i2c: acpi: Implement RawBytes read access @@ -110,7 +110,7 @@ index 4dd777cc0c89..b2338618163a 100644 -- 2.42.0 -From 716585855060beac67709b41ef04243f9c9d1b20 Mon Sep 17 00:00:00 2001 +From 5ef2051244e47117ef10b8494c91dd5a27ba01ef Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 13 Feb 2021 16:41:18 +0100 Subject: [PATCH] platform/surface: Add driver for Surface Book 1 dGPU switch diff --git a/patches/6.1/0008-surface-button.patch b/patches/6.1/0008-surface-button.patch index 5b71bfc7c..eb475f500 100644 --- a/patches/6.1/0008-surface-button.patch +++ b/patches/6.1/0008-surface-button.patch @@ -1,4 +1,4 @@ -From 0e35a68f7e84ef473c2466bad97d4769585fe796 Mon Sep 17 00:00:00 2001 +From 4e8c3bb082ce7a86c0e075ec1328e50837074c91 Mon Sep 17 00:00:00 2001 From: Sachi King Date: Tue, 5 Oct 2021 00:05:09 +1100 Subject: [PATCH] Input: soc_button_array - support AMD variant Surface devices @@ -75,7 +75,7 @@ index e79f5497948b..2bddbe6e9ea4 100644 -- 2.42.0 -From 29d3ef542cee9bbdc71cb914fd418a363a18bf35 Mon Sep 17 00:00:00 2001 +From 519c892e7fcb3660829da97c6bc0dd0ea835a897 Mon Sep 17 00:00:00 2001 From: Sachi King Date: Tue, 5 Oct 2021 00:22:57 +1100 Subject: [PATCH] platform/surface: surfacepro3_button: don't load on amd diff --git a/patches/6.1/0009-surface-typecover.patch b/patches/6.1/0009-surface-typecover.patch index 91882afe9..7556b06c3 100644 --- a/patches/6.1/0009-surface-typecover.patch +++ b/patches/6.1/0009-surface-typecover.patch @@ -1,4 +1,4 @@ -From a8eba604526e45b8bbfa4d26c6f9b78491972146 Mon Sep 17 00:00:00 2001 +From c9aed26b8f46ee570b5b76912c288c6340ee7121 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 18 Feb 2023 01:02:49 +0100 Subject: [PATCH] USB: quirks: Add USB_QUIRK_DELAY_INIT for Surface Go 3 @@ -39,7 +39,7 @@ index 15e9bd180a1d..0d70461d01e1 100644 -- 2.42.0 -From 3335886f84474ec6a492654b769ffaf47d5d299e Mon Sep 17 00:00:00 2001 +From bde20e67508ff86d50f53918e90e58642acb5fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Thu, 5 Nov 2020 13:09:45 +0100 Subject: [PATCH] hid/multitouch: Turn off Type Cover keyboard backlight when @@ -272,7 +272,7 @@ index 521b2ffb4244..c8f3d05c8866 100644 -- 2.42.0 -From 074cb7306da2bcf12cab2f88c34df3fad4e845e9 Mon Sep 17 00:00:00 2001 +From 30849651b4c66e2b904e5b7ccd3a973f7f91c96a Mon Sep 17 00:00:00 2001 From: PJungkamp Date: Fri, 25 Feb 2022 12:04:25 +0100 Subject: [PATCH] hid/multitouch: Add support for surface pro type cover tablet diff --git a/patches/6.1/0010-surface-shutdown.patch b/patches/6.1/0010-surface-shutdown.patch index 4c1c1d4a9..40d41f4f3 100644 --- a/patches/6.1/0010-surface-shutdown.patch +++ b/patches/6.1/0010-surface-shutdown.patch @@ -1,4 +1,4 @@ -From d174bcee5d96b1eb1101dfafb34383d50652ea16 Mon Sep 17 00:00:00 2001 +From 024ce474ffd5215a369f76a4014c6ff3b506c39f Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 19 Feb 2023 22:12:24 +0100 Subject: [PATCH] PCI: Add quirk to prevent calling shutdown mehtod diff --git a/patches/6.1/0011-surface-gpe.patch b/patches/6.1/0011-surface-gpe.patch index 003ab1e2c..6678994ce 100644 --- a/patches/6.1/0011-surface-gpe.patch +++ b/patches/6.1/0011-surface-gpe.patch @@ -1,4 +1,4 @@ -From f7fe57ddd6e7281450131712753441182eeba3d9 Mon Sep 17 00:00:00 2001 +From 31dcb0a4451d13d722879d122e6c324d11e31c86 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 12 Mar 2023 01:41:57 +0100 Subject: [PATCH] platform/surface: gpe: Add support for Surface Pro 9 diff --git a/patches/6.1/0012-cameras.patch b/patches/6.1/0012-cameras.patch index 1de235dac..422c8e0e7 100644 --- a/patches/6.1/0012-cameras.patch +++ b/patches/6.1/0012-cameras.patch @@ -1,4 +1,4 @@ -From 342ce3ffacdcb7e18376d027ecccde48bdfe7952 Mon Sep 17 00:00:00 2001 +From d07be9c0bd2d2f63326d85c35de3bfcd6f8851d4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Oct 2021 20:56:57 +0200 Subject: [PATCH] ACPI: delay enumeration of devices with a _DEP pointing to an @@ -74,7 +74,7 @@ index a0e347f6f97e..167dbf4cf623 100644 -- 2.42.0 -From b3c5d4d884d1e03e9ec6982c97e22b0abe472197 Mon Sep 17 00:00:00 2001 +From 71ad307fa27f57849d6a77a78a7f135e9d89a33a Mon Sep 17 00:00:00 2001 From: zouxiaoh Date: Fri, 25 Jun 2021 08:52:59 +0800 Subject: [PATCH] iommu: intel-ipu: use IOMMU passthrough mode for Intel IPUs @@ -191,7 +191,7 @@ index 66e8a5c18fb8..73beca5d7ca9 100644 -- 2.42.0 -From c57d3a7addf24697c9f17c85a515e09a4ef746fe Mon Sep 17 00:00:00 2001 +From ce2efa0851b6fa4e88f1d59cdd4d95a8989a74d1 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Sun, 10 Oct 2021 20:57:02 +0200 Subject: [PATCH] platform/x86: int3472: Enable I2c daisy chain @@ -228,7 +228,7 @@ index 5b8d1a9620a5..6a0ff035cf20 100644 -- 2.42.0 -From 6703e1544439a3889f13512730417846c8404ac9 Mon Sep 17 00:00:00 2001 +From 5d384365a6d21ab507243f11dd8f2c3e2633cde9 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 28 Oct 2021 21:55:16 +0100 Subject: [PATCH] media: i2c: Add driver for DW9719 VCM @@ -732,7 +732,7 @@ index 000000000000..180b04d2a6b3 -- 2.42.0 -From f0e0af3340f690697e0bed39cbb9c2ed06ef5e4c Mon Sep 17 00:00:00 2001 +From b4ce4a1930c03eb60e3a4362f0daf5595eabb3c0 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Wed, 4 May 2022 23:21:45 +0100 Subject: [PATCH] media: ipu3-cio2: Move functionality from .complete() to @@ -847,7 +847,7 @@ index 1bbe58b24d99..159f0d6cab8f 100644 -- 2.42.0 -From bc5adcf9cd5850cec06114c3b78c8a3eab50747e Mon Sep 17 00:00:00 2001 +From 95c34d5d2613b851abbeb9803112090f979b6b55 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 2 Jun 2022 22:15:56 +0100 Subject: [PATCH] media: ipu3-cio2: Re-add .complete() to ipu3-cio2 @@ -890,7 +890,7 @@ index 159f0d6cab8f..0b2abfa0c724 100644 -- 2.42.0 -From 4a9a5c4ed74dc2402a8f8352a61de4c520c0e2b0 Mon Sep 17 00:00:00 2001 +From bac0ebe98ba2cf810d2c4d988ac088e3a32a0c78 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Fri, 15 Jul 2022 23:48:00 +0200 Subject: [PATCH] drivers/media/i2c: Fix DW9719 dependencies @@ -919,7 +919,7 @@ index fbaa2ce7fa3e..7a46ad85dbd4 100644 -- 2.42.0 -From a5769a892e1d60ecfbd7053ac95280f99daefd5b Mon Sep 17 00:00:00 2001 +From 46dc23e063ec36df32e808356b0de9ffc8a437b1 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 2 Mar 2023 12:59:39 +0000 Subject: [PATCH] platform/x86: int3472: Remap reset GPIO for INT347E diff --git a/patches/6.1/0013-amd-gpio.patch b/patches/6.1/0013-amd-gpio.patch index 67d5e3466..a4da15bb0 100644 --- a/patches/6.1/0013-amd-gpio.patch +++ b/patches/6.1/0013-amd-gpio.patch @@ -1,4 +1,4 @@ -From ab3f9a416283af6038396edc47660bd494694fda Mon Sep 17 00:00:00 2001 +From d6d0f8e62b97df24a5dce5c4e2432078db93bf51 Mon Sep 17 00:00:00 2001 From: Sachi King Date: Sat, 29 May 2021 17:47:38 +1000 Subject: [PATCH] ACPI: Add quirk for Surface Laptop 4 AMD missing irq 7 @@ -65,7 +65,7 @@ index 0f762070a5e1..6362dd452233 100644 -- 2.42.0 -From ad1ecd1dcdf47a6e4a907636dc28c42b3dfcac62 Mon Sep 17 00:00:00 2001 +From 18e978524e3da6faa7023cdfc2e399ea085f0153 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Thu, 3 Jun 2021 14:04:26 +0200 Subject: [PATCH] ACPI: Add AMD 13" Surface Laptop 4 model to irq 7 override diff --git a/patches/6.1/0014-rtc.patch b/patches/6.1/0014-rtc.patch index c6ecfb083..781b0da76 100644 --- a/patches/6.1/0014-rtc.patch +++ b/patches/6.1/0014-rtc.patch @@ -1,4 +1,4 @@ -From 577211a83b8fb93fe5e00ed3939ec0998bb4591b Mon Sep 17 00:00:00 2001 +From b607f45b1e64e99f7a92d14f326a1fdd1e63a521 Mon Sep 17 00:00:00 2001 From: "Bart Groeneveld | GPX Solutions B.V" Date: Mon, 5 Dec 2022 16:08:46 +0100 Subject: [PATCH] acpi: allow usage of acpi_tad on HW-reduced platforms