summaryrefslogtreecommitdiff
path: root/SOURCES/t2linux.patch
diff options
context:
space:
mode:
authorJan200101 <sentrycraft123@gmail.com>2024-02-08 22:07:04 +0100
committerJan200101 <sentrycraft123@gmail.com>2024-02-08 22:07:04 +0100
commita76b9e93d4de9f14a7e4aaa6d19fc721fc2e17d3 (patch)
treec77f07714c04275e3aa69b885a4ef7673985245d /SOURCES/t2linux.patch
parent32a4026b609472b5b278fb1a9c2e5d740782edd2 (diff)
downloadkernel-fsync-a76b9e93d4de9f14a7e4aaa6d19fc721fc2e17d3.tar.gz
kernel-fsync-a76b9e93d4de9f14a7e4aaa6d19fc721fc2e17d3.zip
kernel 6.7.3
Diffstat (limited to 'SOURCES/t2linux.patch')
-rw-r--r--SOURCES/t2linux.patch12153
1 files changed, 12153 insertions, 0 deletions
diff --git a/SOURCES/t2linux.patch b/SOURCES/t2linux.patch
new file mode 100644
index 0000000..ba58dde
--- /dev/null
+++ b/SOURCES/t2linux.patch
@@ -0,0 +1,12153 @@
+From 80093f92d42d77f27de6b204550baf4622070732 Mon Sep 17 00:00:00 2001
+From: Aditya Garg <gargaditya08@live.com>
+Date: Tue, 12 Sep 2023 12:26:12 +0530
+Subject: [PATCH] Add apple-bce driver
+
+---
+ drivers/staging/apple-bce/Makefile | 28 +
+ drivers/staging/apple-bce/apple_bce.c | 443 ++++++++++
+ drivers/staging/apple-bce/apple_bce.h | 38 +
+ drivers/staging/apple-bce/audio/audio.c | 711 ++++++++++++++++
+ drivers/staging/apple-bce/audio/audio.h | 123 +++
+ drivers/staging/apple-bce/audio/description.h | 42 +
+ drivers/staging/apple-bce/audio/pcm.c | 308 +++++++
+ drivers/staging/apple-bce/audio/pcm.h | 16 +
+ drivers/staging/apple-bce/audio/protocol.c | 347 ++++++++
+ drivers/staging/apple-bce/audio/protocol.h | 147 ++++
+ .../staging/apple-bce/audio/protocol_bce.c | 226 ++++++
+ .../staging/apple-bce/audio/protocol_bce.h | 72 ++
+ drivers/staging/apple-bce/mailbox.c | 151 ++++
+ drivers/staging/apple-bce/mailbox.h | 53 ++
+ drivers/staging/apple-bce/queue.c | 390 +++++++++
+ drivers/staging/apple-bce/queue.h | 177 ++++
+ drivers/staging/apple-bce/queue_dma.c | 220 +++++
+ drivers/staging/apple-bce/queue_dma.h | 50 ++
+ drivers/staging/apple-bce/vhci/command.h | 204 +++++
+ drivers/staging/apple-bce/vhci/queue.c | 268 +++++++
+ drivers/staging/apple-bce/vhci/queue.h | 76 ++
+ drivers/staging/apple-bce/vhci/transfer.c | 661 +++++++++++++++
+ drivers/staging/apple-bce/vhci/transfer.h | 71 ++
+ drivers/staging/apple-bce/vhci/vhci.c | 759 ++++++++++++++++++
+ drivers/staging/apple-bce/vhci/vhci.h | 48 ++
+ 25 files changed, 5629 insertions(+)
+ create mode 100644 drivers/staging/apple-bce/Makefile
+ create mode 100644 drivers/staging/apple-bce/apple_bce.c
+ create mode 100644 drivers/staging/apple-bce/apple_bce.h
+ create mode 100644 drivers/staging/apple-bce/audio/audio.c
+ create mode 100644 drivers/staging/apple-bce/audio/audio.h
+ create mode 100644 drivers/staging/apple-bce/audio/description.h
+ create mode 100644 drivers/staging/apple-bce/audio/pcm.c
+ create mode 100644 drivers/staging/apple-bce/audio/pcm.h
+ create mode 100644 drivers/staging/apple-bce/audio/protocol.c
+ create mode 100644 drivers/staging/apple-bce/audio/protocol.h
+ create mode 100644 drivers/staging/apple-bce/audio/protocol_bce.c
+ create mode 100644 drivers/staging/apple-bce/audio/protocol_bce.h
+ create mode 100644 drivers/staging/apple-bce/mailbox.c
+ create mode 100644 drivers/staging/apple-bce/mailbox.h
+ create mode 100644 drivers/staging/apple-bce/queue.c
+ create mode 100644 drivers/staging/apple-bce/queue.h
+ create mode 100644 drivers/staging/apple-bce/queue_dma.c
+ create mode 100644 drivers/staging/apple-bce/queue_dma.h
+ create mode 100644 drivers/staging/apple-bce/vhci/command.h
+ create mode 100644 drivers/staging/apple-bce/vhci/queue.c
+ create mode 100644 drivers/staging/apple-bce/vhci/queue.h
+ create mode 100644 drivers/staging/apple-bce/vhci/transfer.c
+ create mode 100644 drivers/staging/apple-bce/vhci/transfer.h
+ create mode 100644 drivers/staging/apple-bce/vhci/vhci.c
+ create mode 100644 drivers/staging/apple-bce/vhci/vhci.h
+
+diff --git a/drivers/staging/apple-bce/Makefile b/drivers/staging/apple-bce/Makefile
+new file mode 100644
+index 000000000..a6a656f06
+--- /dev/null
++++ b/drivers/staging/apple-bce/Makefile
+@@ -0,0 +1,28 @@
++modname := apple-bce
++obj-m += $(modname).o
++
++apple-bce-objs := apple_bce.o mailbox.o queue.o queue_dma.o vhci/vhci.o vhci/queue.o vhci/transfer.o audio/audio.o audio/protocol.o audio/protocol_bce.o audio/pcm.o
++
++MY_CFLAGS += -DWITHOUT_NVME_PATCH
++#MY_CFLAGS += -g -DDEBUG
++ccflags-y += ${MY_CFLAGS}
++CC += ${MY_CFLAGS}
++
++KVERSION := $(KERNELRELEASE)
++ifeq ($(origin KERNELRELEASE), undefined)
++KVERSION := $(shell uname -r)
++endif
++
++KDIR := /lib/modules/$(KVERSION)/build
++PWD := $(shell pwd)
++
++.PHONY: all
++
++all:
++ $(MAKE) -C $(KDIR) M=$(PWD) modules
++
++clean:
++ $(MAKE) -C $(KDIR) M=$(PWD) clean
++
++install:
++ $(MAKE) -C $(KDIR) M=$(PWD) modules_install
+diff --git a/drivers/staging/apple-bce/apple_bce.c b/drivers/staging/apple-bce/apple_bce.c
+new file mode 100644
+index 000000000..ad89632df
+--- /dev/null
++++ b/drivers/staging/apple-bce/apple_bce.c
+@@ -0,0 +1,443 @@
++#include "apple_bce.h"
++#include <linux/module.h>
++#include <linux/crc32.h>
++#include "audio/audio.h"
++#include <linux/version.h>
++
++static dev_t bce_chrdev;
++static struct class *bce_class;
++
++struct apple_bce_device *global_bce;
++
++static int bce_create_command_queues(struct apple_bce_device *bce);
++static void bce_free_command_queues(struct apple_bce_device *bce);
++static irqreturn_t bce_handle_mb_irq(int irq, void *dev);
++static irqreturn_t bce_handle_dma_irq(int irq, void *dev);
++static int bce_fw_version_handshake(struct apple_bce_device *bce);
++static int bce_register_command_queue(struct apple_bce_device *bce, struct bce_queue_memcfg *cfg, int is_sq);
++
++static int apple_bce_probe(struct pci_dev *dev, const struct pci_device_id *id)
++{
++ struct apple_bce_device *bce = NULL;
++ int status = 0;
++ int nvec;
++
++ pr_info("apple-bce: capturing our device\n");
++
++ if (pci_enable_device(dev))
++ return -ENODEV;
++ if (pci_request_regions(dev, "apple-bce")) {
++ status = -ENODEV;
++ goto fail;
++ }
++ pci_set_master(dev);
++ nvec = pci_alloc_irq_vectors(dev, 1, 8, PCI_IRQ_MSI);
++ if (nvec < 5) {
++ status = -EINVAL;
++ goto fail;
++ }
++
++ bce = kzalloc(sizeof(struct apple_bce_device), GFP_KERNEL);
++ if (!bce) {
++ status = -ENOMEM;
++ goto fail;
++ }
++
++ bce->pci = dev;
++ pci_set_drvdata(dev, bce);
++
++ bce->devt = bce_chrdev;
++ bce->dev = device_create(bce_class, &dev->dev, bce->devt, NULL, "apple-bce");
++ if (IS_ERR_OR_NULL(bce->dev)) {
++ status = PTR_ERR(bce_class);
++ goto fail;
++ }
++
++ bce->reg_mem_mb = pci_iomap(dev, 4, 0);
++ bce->reg_mem_dma = pci_iomap(dev, 2, 0);
++
++ if (IS_ERR_OR_NULL(bce->reg_mem_mb) || IS_ERR_OR_NULL(bce->reg_mem_dma)) {
++ dev_warn(&dev->dev, "apple-bce: Failed to pci_iomap required regions\n");
++ goto fail;
++ }
++
++ bce_mailbox_init(&bce->mbox, bce->reg_mem_mb);
++ bce_timestamp_init(&bce->timestamp, bce->reg_mem_mb);
++
++ spin_lock_init(&bce->queues_lock);
++ ida_init(&bce->queue_ida);
++
++ if ((status = pci_request_irq(dev, 0, bce_handle_mb_irq, NULL, dev, "bce_mbox")))
++ goto fail;
++ if ((status = pci_request_irq(dev, 4, NULL, bce_handle_dma_irq, dev, "bce_dma")))
++ goto fail_interrupt_0;
++
++ if ((status = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(37)))) {
++ dev_warn(&dev->dev, "dma: Setting mask failed\n");
++ goto fail_interrupt;
++ }
++
++ /* Gets the function 0's interface. This is needed because Apple only accepts DMA on our function if function 0
++ is a bus master, so we need to work around this. */
++ bce->pci0 = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
++#ifndef WITHOUT_NVME_PATCH
++ if ((status = pci_enable_device_mem(bce->pci0))) {
++ dev_warn(&dev->dev, "apple-bce: failed to enable function 0\n");
++ goto fail_dev0;
++ }
++#endif
++ pci_set_master(bce->pci0);
++
++ bce_timestamp_start(&bce->timestamp, true);
++
++ if ((status = bce_fw_version_handshake(bce)))
++ goto fail_ts;
++ pr_info("apple-bce: handshake done\n");
++
++ if ((status = bce_create_command_queues(bce))) {
++ pr_info("apple-bce: Creating command queues failed\n");
++ goto fail_ts;
++ }
++
++ global_bce = bce;
++
++ bce_vhci_create(bce, &bce->vhci);
++
++ return 0;
++
++fail_ts:
++ bce_timestamp_stop(&bce->timestamp);
++#ifndef WITHOUT_NVME_PATCH
++ pci_disable_device(bce->pci0);
++fail_dev0:
++#endif
++ pci_dev_put(bce->pci0);
++fail_interrupt:
++ pci_free_irq(dev, 4, dev);
++fail_interrupt_0:
++ pci_free_irq(dev, 0, dev);
++fail:
++ if (bce && bce->dev) {
++ device_destroy(bce_class, bce->devt);
++
++ if (!IS_ERR_OR_NULL(bce->reg_mem_mb))
++ pci_iounmap(dev, bce->reg_mem_mb);
++ if (!IS_ERR_OR_NULL(bce->reg_mem_dma))
++ pci_iounmap(dev, bce->reg_mem_dma);
++
++ kfree(bce);
++ }
++
++ pci_free_irq_vectors(dev);
++ pci_release_regions(dev);
++ pci_disable_device(dev);
++
++ if (!status)
++ status = -EINVAL;
++ return status;
++}
++
++static int bce_create_command_queues(struct apple_bce_device *bce)
++{
++ int status;
++ struct bce_queue_memcfg *cfg;
++
++ bce->cmd_cq = bce_alloc_cq(bce, 0, 0x20);
++ bce->cmd_cmdq = bce_alloc_cmdq(bce, 1, 0x20);
++ if (bce->cmd_cq == NULL || bce->cmd_cmdq == NULL) {
++ status = -ENOMEM;
++ goto err;
++ }
++ bce->queues[0] = (struct bce_queue *) bce->cmd_cq;
++ bce->queues[1] = (struct bce_queue *) bce->cmd_cmdq->sq;
++
++ cfg = kzalloc(sizeof(struct bce_queue_memcfg), GFP_KERNEL);
++ if (!cfg) {
++ status = -ENOMEM;
++ goto err;
++ }
++ bce_get_cq_memcfg(bce->cmd_cq, cfg);
++ if ((status = bce_register_command_queue(bce, cfg, false)))
++ goto err;
++ bce_get_sq_memcfg(bce->cmd_cmdq->sq, bce->cmd_cq, cfg);
++ if ((status = bce_register_command_queue(bce, cfg, true)))
++ goto err;
++ kfree(cfg);
++
++ return 0;
++
++err:
++ if (bce->cmd_cq)
++ bce_free_cq(bce, bce->cmd_cq);
++ if (bce->cmd_cmdq)
++ bce_free_cmdq(bce, bce->cmd_cmdq);
++ return status;
++}
++
++static void bce_free_command_queues(struct apple_bce_device *bce)
++{
++ bce_free_cq(bce, bce->cmd_cq);
++ bce_free_cmdq(bce, bce->cmd_cmdq);
++ bce->cmd_cq = NULL;
++ bce->queues[0] = NULL;
++}
++
++static irqreturn_t bce_handle_mb_irq(int irq, void *dev)
++{
++ struct apple_bce_device *bce = pci_get_drvdata(dev);
++ bce_mailbox_handle_interrupt(&bce->mbox);
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t bce_handle_dma_irq(int irq, void *dev)
++{
++ int i;
++ struct apple_bce_device *bce = pci_get_drvdata(dev);
++ spin_lock(&bce->queues_lock);
++ for (i = 0; i < BCE_MAX_QUEUE_COUNT; i++)
++ if (bce->queues[i] && bce->queues[i]->type == BCE_QUEUE_CQ)
++ bce_handle_cq_completions(bce, (struct bce_queue_cq *) bce->queues[i]);
++ spin_unlock(&bce->queues_lock);
++ return IRQ_HANDLED;
++}
++
++static int bce_fw_version_handshake(struct apple_bce_device *bce)
++{
++ u64 result;
++ int status;
++
++ if ((status = bce_mailbox_send(&bce->mbox, BCE_MB_MSG(BCE_MB_SET_FW_PROTOCOL_VERSION, BC_PROTOCOL_VERSION),
++ &result)))
++ return status;
++ if (BCE_MB_TYPE(result) != BCE_MB_SET_FW_PROTOCOL_VERSION ||
++ BCE_MB_VALUE(result) != BC_PROTOCOL_VERSION) {
++ pr_err("apple-bce: FW version handshake failed %x:%llx\n", BCE_MB_TYPE(result), BCE_MB_VALUE(result));
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static int bce_register_command_queue(struct apple_bce_device *bce, struct bce_queue_memcfg *cfg, int is_sq)
++{
++ int status;
++ int cmd_type;
++ u64 result;
++ // OS X uses an bidirectional direction, but that's not really needed
++ dma_addr_t a = dma_map_single(&bce->pci->dev, cfg, sizeof(struct bce_queue_memcfg), DMA_TO_DEVICE);
++ if (dma_mapping_error(&bce->pci->dev, a))
++ return -ENOMEM;
++ cmd_type = is_sq ? BCE_MB_REGISTER_COMMAND_SQ : BCE_MB_REGISTER_COMMAND_CQ;
++ status = bce_mailbox_send(&bce->mbox, BCE_MB_MSG(cmd_type, a), &result);
++ dma_unmap_single(&bce->pci->dev, a, sizeof(struct bce_queue_memcfg), DMA_TO_DEVICE);
++ if (status)
++ return status;
++ if (BCE_MB_TYPE(result) != BCE_MB_REGISTER_COMMAND_QUEUE_REPLY)
++ return -EINVAL;
++ return 0;
++}
++
++static void apple_bce_remove(struct pci_dev *dev)
++{
++ struct apple_bce_device *bce = pci_get_drvdata(dev);
++ bce->is_being_removed = true;
++
++ bce_vhci_destroy(&bce->vhci);
++
++ bce_timestamp_stop(&bce->timestamp);
++#ifndef WITHOUT_NVME_PATCH
++ pci_disable_device(bce->pci0);
++#endif
++ pci_dev_put(bce->pci0);
++ pci_free_irq(dev, 0, dev);
++ pci_free_irq(dev, 4, dev);
++ bce_free_command_queues(bce);
++ pci_iounmap(dev, bce->reg_mem_mb);
++ pci_iounmap(dev, bce->reg_mem_dma);
++ device_destroy(bce_class, bce->devt);
++ pci_free_irq_vectors(dev);
++ pci_release_regions(dev);
++ pci_disable_device(dev);
++ kfree(bce);
++}
++
++static int bce_save_state_and_sleep(struct apple_bce_device *bce)
++{
++ int attempt, status = 0;
++ u64 resp;
++ dma_addr_t dma_addr;
++ void *dma_ptr = NULL;
++ size_t size = max(PAGE_SIZE, 4096UL);
++
++ for (attempt = 0; attempt < 5; ++attempt) {
++ pr_debug("apple-bce: suspend: attempt %i, buffer size %li\n", attempt, size);
++ dma_ptr = dma_alloc_coherent(&bce->pci->dev, size, &dma_addr, GFP_KERNEL);
++ if (!dma_ptr) {
++ pr_err("apple-bce: suspend failed (data alloc failed)\n");
++ break;
++ }
++ BUG_ON((dma_addr % 4096) != 0);
++ status = bce_mailbox_send(&bce->mbox,
++ BCE_MB_MSG(BCE_MB_SAVE_STATE_AND_SLEEP, (dma_addr & ~(4096LLU - 1)) | (size / 4096)), &resp);
++ if (status) {
++ pr_err("apple-bce: suspend failed (mailbox send)\n");
++ break;
++ }
++ if (BCE_MB_TYPE(resp) == BCE_MB_SAVE_RESTORE_STATE_COMPLETE) {
++ bce->saved_data_dma_addr = dma_addr;
++ bce->saved_data_dma_ptr = dma_ptr;
++ bce->saved_data_dma_size = size;
++ return 0;
++ } else if (BCE_MB_TYPE(resp) == BCE_MB_SAVE_STATE_AND_SLEEP_FAILURE) {
++ dma_free_coherent(&bce->pci->dev, size, dma_ptr, dma_addr);
++ /* The 0x10ff magic value was extracted from Apple's driver */
++ size = (BCE_MB_VALUE(resp) + 0x10ff) & ~(4096LLU - 1);
++ pr_debug("apple-bce: suspend: device requested a larger buffer (%li)\n", size);
++ continue;
++ } else {
++ pr_err("apple-bce: suspend failed (invalid device response)\n");
++ status = -EINVAL;
++ break;
++ }
++ }
++ if (dma_ptr)
++ dma_free_coherent(&bce->pci->dev, size, dma_ptr, dma_addr);
++ if (!status)
++ return bce_mailbox_send(&bce->mbox, BCE_MB_MSG(BCE_MB_SLEEP_NO_STATE, 0), &resp);
++ return status;
++}
++
++static int bce_restore_state_and_wake(struct apple_bce_device *bce)
++{
++ int status;
++ u64 resp;
++ if (!bce->saved_data_dma_ptr) {
++ if ((status = bce_mailbox_send(&bce->mbox, BCE_MB_MSG(BCE_MB_RESTORE_NO_STATE, 0), &resp))) {
++ pr_err("apple-bce: resume with no state failed (mailbox send)\n");
++ return status;
++ }
++ if (BCE_MB_TYPE(resp) != BCE_MB_RESTORE_NO_STATE) {
++ pr_err("apple-bce: resume with no state failed (invalid device response)\n");
++ return -EINVAL;
++ }
++ return 0;
++ }
++
++ if ((status = bce_mailbox_send(&bce->mbox, BCE_MB_MSG(BCE_MB_RESTORE_STATE_AND_WAKE,
++ (bce->saved_data_dma_addr & ~(4096LLU - 1)) | (bce->saved_data_dma_size / 4096)), &resp))) {
++ pr_err("apple-bce: resume with state failed (mailbox send)\n");
++ goto finish_with_state;
++ }
++ if (BCE_MB_TYPE(resp) != BCE_MB_SAVE_RESTORE_STATE_COMPLETE) {
++ pr_err("apple-bce: resume with state failed (invalid device response)\n");
++ status = -EINVAL;
++ goto finish_with_state;
++ }
++
++finish_with_state:
++ dma_free_coherent(&bce->pci->dev, bce->saved_data_dma_size, bce->saved_data_dma_ptr, bce->saved_data_dma_addr);
++ bce->saved_data_dma_ptr = NULL;
++ return status;
++}
++
++static int apple_bce_suspend(struct device *dev)
++{
++ struct apple_bce_device *bce = pci_get_drvdata(to_pci_dev(dev));
++ int status;
++
++ bce_timestamp_stop(&bce->timestamp);
++
++ if ((status = bce_save_state_and_sleep(bce)))
++ return status;
++
++ return 0;
++}
++
++static int apple_bce_resume(struct device *dev)
++{
++ struct apple_bce_device *bce = pci_get_drvdata(to_pci_dev(dev));
++ int status;
++
++ pci_set_master(bce->pci);
++ pci_set_master(bce->pci0);
++
++ if ((status = bce_restore_state_and_wake(bce)))
++ return status;
++
++ bce_timestamp_start(&bce->timestamp, false);
++
++ return 0;
++}
++
++static struct pci_device_id apple_bce_ids[ ] = {
++ { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x1801) },
++ { 0, },
++};
++
++struct dev_pm_ops apple_bce_pci_driver_pm = {
++ .suspend = apple_bce_suspend,
++ .resume = apple_bce_resume
++};
++struct pci_driver apple_bce_pci_driver = {
++ .name = "apple-bce",
++ .id_table = apple_bce_ids,
++ .probe = apple_bce_probe,
++ .remove = apple_bce_remove,
++ .driver = {
++ .pm = &apple_bce_pci_driver_pm
++ }
++};
++
++
++static int __init apple_bce_module_init(void)
++{
++ int result;
++ if ((result = alloc_chrdev_region(&bce_chrdev, 0, 1, "apple-bce")))
++ goto fail_chrdev;
++#if LINUX_VERSION_CODE < KERNEL_VERSION(6,4,0)
++ bce_class = class_create(THIS_MODULE, "apple-bce");
++#else
++ bce_class = class_create("apple-bce");
++#endif
++ if (IS_ERR(bce_class)) {
++ result = PTR_ERR(bce_class);
++ goto fail_class;
++ }
++ if ((result = bce_vhci_module_init())) {
++ pr_err("apple-bce: bce-vhci init failed");
++ goto fail_class;
++ }
++
++ result = pci_register_driver(&apple_bce_pci_driver);
++ if (result)
++ goto fail_drv;
++
++ aaudio_module_init();
++
++ return 0;
++
++fail_drv:
++ pci_unregister_driver(&apple_bce_pci_driver);
++fail_class:
++ class_destroy(bce_class);
++fail_chrdev:
++ unregister_chrdev_region(bce_chrdev, 1);
++ if (!result)
++ result = -EINVAL;
++ return result;
++}
++static void __exit apple_bce_module_exit(void)
++{
++ pci_unregister_driver(&apple_bce_pci_driver);
++
++ aaudio_module_exit();
++ bce_vhci_module_exit();
++ class_destroy(bce_class);
++ unregister_chrdev_region(bce_chrdev, 1);
++}
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("MrARM");
++MODULE_DESCRIPTION("Apple BCE Driver");
++MODULE_VERSION("0.01");
++module_init(apple_bce_module_init);
++module_exit(apple_bce_module_exit);
+diff --git a/drivers/staging/apple-bce/apple_bce.h b/drivers/staging/apple-bce/apple_bce.h
+new file mode 100644
+index 000000000..f13ab8d57
+--- /dev/null
++++ b/drivers/staging/apple-bce/apple_bce.h
+@@ -0,0 +1,38 @@
++#pragma once
++
++#include <linux/pci.h>
++#include <linux/spinlock.h>
++#include "mailbox.h"
++#include "queue.h"
++#include "vhci/vhci.h"
++
++#define BC_PROTOCOL_VERSION 0x20001
++#define BCE_MAX_QUEUE_COUNT 0x100
++
++#define BCE_QUEUE_USER_MIN 2
++#define BCE_QUEUE_USER_MAX (BCE_MAX_QUEUE_COUNT - 1)
++
++struct apple_bce_device {
++ struct pci_dev *pci, *pci0;
++ dev_t devt;
++ struct device *dev;
++ void __iomem *reg_mem_mb;
++ void __iomem *reg_mem_dma;
++ struct bce_mailbox mbox;
++ struct bce_timestamp timestamp;
++ struct bce_queue *queues[BCE_MAX_QUEUE_COUNT];
++ struct spinlock queues_lock;
++ struct ida queue_ida;
++ struct bce_queue_cq *cmd_cq;
++ struct bce_queue_cmdq *cmd_cmdq;
++ struct bce_queue_sq *int_sq_list[BCE_MAX_QUEUE_COUNT];
++ bool is_being_removed;
++
++ dma_addr_t saved_data_dma_addr;
++ void *saved_data_dma_ptr;
++ size_t saved_data_dma_size;
++
++ struct bce_vhci vhci;
++};
++
++extern struct apple_bce_device *global_bce;
+\ No newline at end of file
+diff --git a/drivers/staging/apple-bce/audio/audio.c b/drivers/staging/apple-bce/audio/audio.c
+new file mode 100644
+index 000000000..bd16ddd16
+--- /dev/null
++++ b/drivers/staging/apple-bce/audio/audio.c
+@@ -0,0 +1,711 @@
++#include <linux/pci.h>
++#include <linux/spinlock.h>
++#include <linux/module.h>
++#include <linux/random.h>
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++#include <sound/jack.h>
++#include "audio.h"
++#include "pcm.h"
++#include <linux/version.h>
++
++static int aaudio_alsa_index = SNDRV_DEFAULT_IDX1;
++static char *aaudio_alsa_id = SNDRV_DEFAULT_STR1;
++
++static dev_t aaudio_chrdev;
++static struct class *aaudio_class;
++
++static int aaudio_init_cmd(struct aaudio_device *a);
++static int aaudio_init_bs(struct aaudio_device *a);
++static void aaudio_init_dev(struct aaudio_device *a, aaudio_device_id_t dev_id);
++static void aaudio_free_dev(struct aaudio_subdevice *sdev);
++
++static int aaudio_probe(struct pci_dev *dev, const struct pci_device_id *id)
++{
++ struct aaudio_device *aaudio = NULL;
++ struct aaudio_subdevice *sdev = NULL;
++ int status = 0;
++ u32 cfg;
++
++ pr_info("aaudio: capturing our device\n");
++
++ if (pci_enable_device(dev))
++ return -ENODEV;
++ if (pci_request_regions(dev, "aaudio")) {
++ status = -ENODEV;
++ goto fail;
++ }
++ pci_set_master(dev);
++
++ aaudio = kzalloc(sizeof(struct aaudio_device), GFP_KERNEL);
++ if (!aaudio) {
++ status = -ENOMEM;
++ goto fail;
++ }
++
++ aaudio->bce = global_bce;
++ if (!aaudio->bce) {
++ dev_warn(&dev->dev, "aaudio: No BCE available\n");
++ status = -EINVAL;
++ goto fail;
++ }
++
++ aaudio->pci = dev;
++ pci_set_drvdata(dev, aaudio);
++
++ aaudio->devt = aaudio_chrdev;
++ aaudio->dev = device_create(aaudio_class, &dev->dev, aaudio->devt, NULL, "aaudio");
++ if (IS_ERR_OR_NULL(aaudio->dev)) {
++ status = PTR_ERR(aaudio_class);
++ goto fail;
++ }
++ device_link_add(aaudio->dev, aaudio->bce->dev, DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER);
++
++ init_completion(&aaudio->remote_alive);
++ INIT_LIST_HEAD(&aaudio->subdevice_list);
++
++ /* Init: set an unknown flag in the bitset */
++ if (pci_read_config_dword(dev, 4, &cfg))
++ dev_warn(&dev->dev, "aaudio: pci_read_config_dword fail\n");
++ if (pci_write_config_dword(dev, 4, cfg | 6u))
++ dev_warn(&dev->dev, "aaudio: pci_write_config_dword fail\n");
++
++ dev_info(aaudio->dev, "aaudio: bs len = %llx\n", pci_resource_len(dev, 0));
++ aaudio->reg_mem_bs_dma = pci_resource_start(dev, 0);
++ aaudio->reg_mem_bs = pci_iomap(dev, 0, 0);
++ aaudio->reg_mem_cfg = pci_iomap(dev, 4, 0);
++
++ aaudio->reg_mem_gpr = (u32 __iomem *) ((u8 __iomem *) aaudio->reg_mem_cfg + 0xC000);
++
++ if (IS_ERR_OR_NULL(aaudio->reg_mem_bs) || IS_ERR_OR_NULL(aaudio->reg_mem_cfg)) {
++ dev_warn(&dev->dev, "aaudio: Failed to pci_iomap required regions\n");
++ goto fail;
++ }
++
++ if (aaudio_bce_init(aaudio)) {
++ dev_warn(&dev->dev, "aaudio: Failed to init BCE command transport\n");
++ goto fail;
++ }
++
++ if (snd_card_new(aaudio->dev, aaudio_alsa_index, aaudio_alsa_id, THIS_MODULE, 0, &aaudio->card)) {
++ dev_err(&dev->dev, "aaudio: Failed to create ALSA card\n");
++ goto fail;
++ }
++
++ strcpy(aaudio->card->shortname, "Apple T2 Audio");
++ strcpy(aaudio->card->longname, "Apple T2 Audio");
++ strcpy(aaudio->card->mixername, "Apple T2 Audio");
++ /* Dynamic alsa ids start at 100 */
++ aaudio->next_alsa_id = 100;
++
++ if (aaudio_init_cmd(aaudio)) {
++ dev_err(&dev->dev, "aaudio: Failed to initialize over BCE\n");
++ goto fail_snd;
++ }
++
++ if (aaudio_init_bs(aaudio)) {
++ dev_err(&dev->dev, "aaudio: Failed to initialize BufferStruct\n");
++ goto fail_snd;
++ }
++
++ if ((status = aaudio_cmd_set_remote_access(aaudio, AAUDIO_REMOTE_ACCESS_ON))) {
++ dev_err(&dev->dev, "Failed to set remote access\n");
++ return status;
++ }
++
++ if (snd_card_register(aaudio->card)) {
++ dev_err(&dev->dev, "aaudio: Failed to register ALSA sound device\n");
++ goto fail_snd;
++ }
++
++ list_for_each_entry(sdev, &aaudio->subdevice_list, list) {
++ struct aaudio_buffer_struct_device *dev = &aaudio->bs->devices[sdev->buf_id];
++
++ if (sdev->out_stream_cnt == 1 && !strcmp(dev->name, "Speaker")) {
++ struct snd_pcm_hardware *hw = sdev->out_streams[0].alsa_hw_desc;
++
++ snprintf(aaudio->card->driver, sizeof(aaudio->card->driver) / sizeof(char), "AppleT2x%d", hw->channels_min);
++ }
++ }
++
++ return 0;
++
++fail_snd:
++ snd_card_free(aaudio->card);
++fail:
++ if (aaudio && aaudio->dev)
++ device_destroy(aaudio_class, aaudio->devt);
++ kfree(aaudio);
++
++ if (!IS_ERR_OR_NULL(aaudio->reg_mem_bs))
++ pci_iounmap(dev, aaudio->reg_mem_bs);
++ if (!IS_ERR_OR_NULL(aaudio->reg_mem_cfg))
++ pci_iounmap(dev, aaudio->reg_mem_cfg);
++
++ pci_release_regions(dev);
++ pci_disable_device(dev);
++
++ if (!status)
++ status = -EINVAL;
++ return status;
++}
++
++
++
++static void aaudio_remove(struct pci_dev *dev)
++{
++ struct aaudio_subdevice *sdev;
++ struct aaudio_device *aaudio = pci_get_drvdata(dev);
++
++ snd_card_free(aaudio->card);
++ while (!list_empty(&aaudio->subdevice_list)) {
++ sdev = list_first_entry(&aaudio->subdevice_list, struct aaudio_subdevice, list);
++ list_del(&sdev->list);
++ aaudio_free_dev(sdev);
++ }
++ pci_iounmap(dev, aaudio->reg_mem_bs);
++ pci_iounmap(dev, aaudio->reg_mem_cfg);
++ device_destroy(aaudio_class, aaudio->devt);
++ pci_free_irq_vectors(dev);
++ pci_release_regions(dev);
++ pci_disable_device(dev);
++ kfree(aaudio);
++}
++
++static int aaudio_suspend(struct device *dev)
++{
++ struct aaudio_device *aaudio = pci_get_drvdata(to_pci_dev(dev));
++
++ if (aaudio_cmd_set_remote_access(aaudio, AAUDIO_REMOTE_ACCESS_OFF))
++ dev_warn(aaudio->dev, "Failed to reset remote access\n");
++
++ pci_disable_device(aaudio->pci);
++ return 0;
++}
++
++static int aaudio_resume(struct device *dev)
++{
++ int status;
++ struct aaudio_device *aaudio = pci_get_drvdata(to_pci_dev(dev));
++
++ if ((status = pci_enable_device(aaudio->pci)))
++ return status;
++ pci_set_master(aaudio->pci);
++
++ if ((status = aaudio_cmd_set_remote_access(aaudio, AAUDIO_REMOTE_ACCESS_ON))) {
++ dev_err(aaudio->dev, "Failed to set remote access\n");
++ return status;
++ }
++
++ return 0;
++}
++
++static int aaudio_init_cmd(struct aaudio_device *a)
++{
++ int status;
++ struct aaudio_send_ctx sctx;
++ struct aaudio_msg buf;
++ u64 dev_cnt, dev_i;
++ aaudio_device_id_t *dev_l;
++
++ if ((status = aaudio_send(a, &sctx, 500,
++ aaudio_msg_write_alive_notification, 1, 3))) {
++ dev_err(a->dev, "Sending alive notification failed\n");
++ return status;
++ }
++
++ if (wait_for_completion_timeout(&a->remote_alive, msecs_to_jiffies(500)) == 0) {
++ dev_err(a->dev, "Timed out waiting for remote\n");
++ return -ETIMEDOUT;
++ }
++ dev_info(a->dev, "Continuing init\n");
++
++ buf = aaudio_reply_alloc();
++ if ((status = aaudio_cmd_get_device_list(a, &buf, &dev_l, &dev_cnt))) {
++ dev_err(a->dev, "Failed to get device list\n");
++ aaudio_reply_free(&buf);
++ return status;
++ }
++ for (dev_i = 0; dev_i < dev_cnt; ++dev_i)
++ aaudio_init_dev(a, dev_l[dev_i]);
++ aaudio_reply_free(&buf);
++
++ return 0;
++}
++
++static void aaudio_init_stream_info(struct aaudio_subdevice *sdev, struct aaudio_stream *strm);
++static void aaudio_handle_jack_connection_change(struct aaudio_subdevice *sdev);
++
++static void aaudio_init_dev(struct aaudio_device *a, aaudio_device_id_t dev_id)
++{
++ struct aaudio_subdevice *sdev;
++ struct aaudio_msg buf = aaudio_reply_alloc();
++ u64 uid_len, stream_cnt, i;
++ aaudio_object_id_t *stream_list;
++ char *uid;
++
++ sdev = kzalloc(sizeof(struct aaudio_subdevice), GFP_KERNEL);
++
++ if (aaudio_cmd_get_property(a, &buf, dev_id, dev_id, AAUDIO_PROP(AAUDIO_PROP_SCOPE_GLOBAL, AAUDIO_PROP_UID, 0),
++ NULL, 0, (void **) &uid, &uid_len) || uid_len > AAUDIO_DEVICE_MAX_UID_LEN) {
++ dev_err(a->dev, "Failed to get device uid for device %llx\n", dev_id);
++ goto fail;
++ }
++ dev_info(a->dev, "Remote device %llx %.*s\n", dev_id, (int) uid_len, uid);
++
++ sdev->a = a;
++ INIT_LIST_HEAD(&sdev->list);
++ sdev->dev_id = dev_id;
++ sdev->buf_id = AAUDIO_BUFFER_ID_NONE;
++ strncpy(sdev->uid, uid, uid_len);
++ sdev->uid[uid_len + 1] = '\0';
++
++ if (aaudio_cmd_get_primitive_property(a, dev_id, dev_id,
++ AAUDIO_PROP(AAUDIO_PROP_SCOPE_INPUT, AAUDIO_PROP_LATENCY, 0), NULL, 0, &sdev->in_latency, sizeof(u32)))
++ dev_warn(a->dev, "Failed to query device input latency\n");
++ if (aaudio_cmd_get_primitive_property(a, dev_id, dev_id,
++ AAUDIO_PROP(AAUDIO_PROP_SCOPE_OUTPUT, AAUDIO_PROP_LATENCY, 0), NULL, 0, &sdev->out_latency, sizeof(u32)))
++ dev_warn(a->dev, "Failed to query device output latency\n");
++
++ if (aaudio_cmd_get_input_stream_list(a, &buf, dev_id, &stream_list, &stream_cnt)) {
++ dev_err(a->dev, "Failed to get input stream list for device %llx\n", dev_id);
++ goto fail;
++ }
++ if (stream_cnt > AAUDIO_DEIVCE_MAX_INPUT_STREAMS) {
++ dev_warn(a->dev, "Device %s input stream count %llu is larger than the supported count of %u\n",
++ sdev->uid, stream_cnt, AAUDIO_DEIVCE_MAX_INPUT_STREAMS);
++ stream_cnt = AAUDIO_DEIVCE_MAX_INPUT_STREAMS;
++ }
++ sdev->in_stream_cnt = stream_cnt;
++ for (i = 0; i < stream_cnt; i++) {
++ sdev->in_streams[i].id = stream_list[i];
++ sdev->in_streams[i].buffer_cnt = 0;
++ aaudio_init_stream_info(sdev, &sdev->in_streams[i]);
++ sdev->in_streams[i].latency += sdev->in_latency;
++ }
++
++ if (aaudio_cmd_get_output_stream_list(a, &buf, dev_id, &stream_list, &stream_cnt)) {
++ dev_err(a->dev, "Failed to get output stream list for device %llx\n", dev_id);
++ goto fail;
++ }
++ if (stream_cnt > AAUDIO_DEIVCE_MAX_OUTPUT_STREAMS) {
++ dev_warn(a->dev, "Device %s input stream count %llu is larger than the supported count of %u\n",
++ sdev->uid, stream_cnt, AAUDIO_DEIVCE_MAX_OUTPUT_STREAMS);
++ stream_cnt = AAUDIO_DEIVCE_MAX_OUTPUT_STREAMS;
++ }
++ sdev->out_stream_cnt = stream_cnt;
++ for (i = 0; i < stream_cnt; i++) {
++ sdev->out_streams[i].id = stream_list[i];
++ sdev->out_streams[i].buffer_cnt = 0;
++ aaudio_init_stream_info(sdev, &sdev->out_streams[i]);
++ sdev->out_streams[i].latency += sdev->in_latency;
++ }
++
++ if (sdev->is_pcm)
++ aaudio_create_pcm(sdev);
++ /* Headphone Jack status */
++ if (!strcmp(sdev->uid, "Codec Output")) {
++ if (snd_jack_new(a->card, sdev->uid, SND_JACK_HEADPHONE, &sdev->jack, true, false))
++ dev_warn(a->dev, "Failed to create an attached jack for %s\n", sdev->uid);
++ aaudio_cmd_property_listener(a, sdev->dev_id, sdev->dev_id,
++ AAUDIO_PROP(AAUDIO_PROP_SCOPE_OUTPUT, AAUDIO_PROP_JACK_PLUGGED, 0));
++ aaudio_handle_jack_connection_change(sdev);
++ }
++
++ aaudio_reply_free(&buf);
++
++ list_add_tail(&sdev->list, &a->subdevice_list);
++ return;
++
++fail:
++ aaudio_reply_free(&buf);
++ kfree(sdev);
++}
++
++static void aaudio_init_stream_info(struct aaudio_subdevice *sdev, struct aaudio_stream *strm)
++{
++ if (aaudio_cmd_get_primitive_property(sdev->a, sdev->dev_id, strm->id,
++ AAUDIO_PROP(AAUDIO_PROP_SCOPE_GLOBAL, AAUDIO_PROP_PHYS_FORMAT, 0), NULL, 0,
++ &strm->desc, sizeof(strm->desc)))
++ dev_warn(sdev->a->dev, "Failed to query stream descriptor\n");
++ if (aaudio_cmd_get_primitive_property(sdev->a, sdev->dev_id, strm->id,
++ AAUDIO_PROP(AAUDIO_PROP_SCOPE_GLOBAL, AAUDIO_PROP_LATENCY, 0), NULL, 0, &strm->latency, sizeof(u32)))
++ dev_warn(sdev->a->dev, "Failed to query stream latency\n");
++ if (strm->desc.format_id == AAUDIO_FORMAT_LPCM)
++ sdev->is_pcm = true;
++}
++
++static void aaudio_free_dev(struct aaudio_subdevice *sdev)
++{
++ size_t i;
++ for (i = 0; i < sdev->in_stream_cnt; i++) {
++ if (sdev->in_streams[i].alsa_hw_desc)
++ kfree(sdev->in_streams[i].alsa_hw_desc);
++ if (sdev->in_streams[i].buffers)
++ kfree(sdev->in_streams[i].buffers);
++ }
++ for (i = 0; i < sdev->out_stream_cnt; i++) {
++ if (sdev->out_streams[i].alsa_hw_desc)
++ kfree(sdev->out_streams[i].alsa_hw_desc);
++ if (sdev->out_streams[i].buffers)
++ kfree(sdev->out_streams[i].buffers);
++ }
++ kfree(sdev);
++}
++
++static struct aaudio_subdevice *aaudio_find_dev_by_dev_id(struct aaudio_device *a, aaudio_device_id_t dev_id)
++{
++ struct aaudio_subdevice *sdev;
++ list_for_each_entry(sdev, &a->subdevice_list, list) {
++ if (dev_id == sdev->dev_id)
++ return sdev;
++ }
++ return NULL;
++}
++
++static struct aaudio_subdevice *aaudio_find_dev_by_uid(struct aaudio_device *a, const char *uid)
++{
++ struct aaudio_subdevice *sdev;
++ list_for_each_entry(sdev, &a->subdevice_list, list) {
++ if (!strcmp(uid, sdev->uid))
++ return sdev;
++ }
++ return NULL;
++}
++
++static void aaudio_init_bs_stream(struct aaudio_device *a, struct aaudio_stream *strm,
++ struct aaudio_buffer_struct_stream *bs_strm);
++static void aaudio_init_bs_stream_host(struct aaudio_device *a, struct aaudio_stream *strm,
++ struct aaudio_buffer_struct_stream *bs_strm);
++
++static int aaudio_init_bs(struct aaudio_device *a)
++{
++ int i, j;
++ struct aaudio_buffer_struct_device *dev;
++ struct aaudio_subdevice *sdev;
++ u32 ver, sig, bs_base;
++
++ ver = ioread32(&a->reg_mem_gpr[0]);
++ if (ver < 3) {
++ dev_err(a->dev, "aaudio: Bad GPR version (%u)", ver);
++ return -EINVAL;
++ }
++ sig = ioread32(&a->reg_mem_gpr[1]);
++ if (sig != AAUDIO_SIG) {
++ dev_err(a->dev, "aaudio: Bad GPR sig (%x)", sig);
++ return -EINVAL;
++ }
++ bs_base = ioread32(&a->reg_mem_gpr[2]);
++ a->bs = (struct aaudio_buffer_struct *) ((u8 *) a->reg_mem_bs + bs_base);
++ if (a->bs->signature != AAUDIO_SIG) {
++ dev_err(a->dev, "aaudio: Bad BufferStruct sig (%x)", a->bs->signature);
++ return -EINVAL;
++ }
++ dev_info(a->dev, "aaudio: BufferStruct ver = %i\n", a->bs->version);
++ dev_info(a->dev, "aaudio: Num devices = %i\n", a->bs->num_devices);
++ for (i = 0; i < a->bs->num_devices; i++) {
++ dev = &a->bs->devices[i];
++ dev_info(a->dev, "aaudio: Device %i %s\n", i, dev->name);
++
++ sdev = aaudio_find_dev_by_uid(a, dev->name);
++ if (!sdev) {
++ dev_err(a->dev, "aaudio: Subdevice not found for BufferStruct device %s\n", dev->name);
++ continue;
++ }
++ sdev->buf_id = (u8) i;
++ dev->num_input_streams = 0;
++ for (j = 0; j < dev->num_output_streams; j++) {
++ dev_info(a->dev, "aaudio: Device %i Stream %i: Output; Buffer Count = %i\n", i, j,
++ dev->output_streams[j].num_buffers);
++ if (j < sdev->out_stream_cnt)
++ aaudio_init_bs_stream(a, &sdev->out_streams[j], &dev->output_streams[j]);
++ }
++ }
++
++ list_for_each_entry(sdev, &a->subdevice_list, list) {
++ if (sdev->buf_id != AAUDIO_BUFFER_ID_NONE)
++ continue;
++ sdev->buf_id = i;
++ dev_info(a->dev, "aaudio: Created device %i %s\n", i, sdev->uid);
++ strcpy(a->bs->devices[i].name, sdev->uid);
++ a->bs->devices[i].num_input_streams = 0;
++ a->bs->devices[i].num_output_streams = 0;
++ a->bs->num_devices = ++i;
++ }
++ list_for_each_entry(sdev, &a->subdevice_list, list) {
++ if (sdev->in_stream_cnt == 1) {
++ dev_info(a->dev, "aaudio: Device %i Host Stream; Input\n", sdev->buf_id);
++ aaudio_init_bs_stream_host(a, &sdev->in_streams[0], &a->bs->devices[sdev->buf_id].input_streams[0]);
++ a->bs->devices[sdev->buf_id].num_input_streams = 1;
++ wmb();
++
++ if (aaudio_cmd_set_input_stream_address_ranges(a, sdev->dev_id)) {
++ dev_err(a->dev, "aaudio: Failed to set input stream address ranges\n");
++ }
++ }
++ }
++
++ return 0;
++}
++
++static void aaudio_init_bs_stream(struct aaudio_device *a, struct aaudio_stream *strm,
++ struct aaudio_buffer_struct_stream *bs_strm)
++{
++ size_t i;
++ strm->buffer_cnt = bs_strm->num_buffers;
++ if (bs_strm->num_buffers > AAUDIO_DEIVCE_MAX_BUFFER_COUNT) {
++ dev_warn(a->dev, "BufferStruct buffer count %u exceeds driver limit of %u\n", bs_strm->num_buffers,
++ AAUDIO_DEIVCE_MAX_BUFFER_COUNT);
++ strm->buffer_cnt = AAUDIO_DEIVCE_MAX_BUFFER_COUNT;
++ }
++ if (!strm->buffer_cnt)
++ return;
++ strm->buffers = kmalloc_array(strm->buffer_cnt, sizeof(struct aaudio_dma_buf), GFP_KERNEL);
++ if (!strm->buffers) {
++ dev_err(a->dev, "Buffer list allocation failed\n");
++ return;
++ }
++ for (i = 0; i < strm->buffer_cnt; i++) {
++ strm->buffers[i].dma_addr = a->reg_mem_bs_dma + (dma_addr_t) bs_strm->buffers[i].address;
++ strm->buffers[i].ptr = a->reg_mem_bs + bs_strm->buffers[i].address;
++ strm->buffers[i].size = bs_strm->buffers[i].size;
++ }
++
++ if (strm->buffer_cnt == 1) {
++ strm->alsa_hw_desc = kmalloc(sizeof(struct snd_pcm_hardware), GFP_KERNEL);
++ if (aaudio_create_hw_info(&strm->desc, strm->alsa_hw_desc, strm->buffers[0].size)) {
++ kfree(strm->alsa_hw_desc);
++ strm->alsa_hw_desc = NULL;
++ }
++ }
++}
++
++static void aaudio_init_bs_stream_host(struct aaudio_device *a, struct aaudio_stream *strm,
++ struct aaudio_buffer_struct_stream *bs_strm)
++{
++ size_t size;
++ dma_addr_t dma_addr;
++ void *dma_ptr;
++ size = strm->desc.bytes_per_packet * 16640;
++ dma_ptr = dma_alloc_coherent(&a->pci->dev, size, &dma_addr, GFP_KERNEL);
++ if (!dma_ptr) {
++ dev_err(a->dev, "dma_alloc_coherent failed\n");
++ return;
++ }
++ bs_strm->buffers[0].address = dma_addr;
++ bs_strm->buffers[0].size = size;
++ bs_strm->num_buffers = 1;
++
++ memset(dma_ptr, 0, size);
++
++ strm->buffer_cnt = 1;
++ strm->buffers = kmalloc_array(strm->buffer_cnt, sizeof(struct aaudio_dma_buf), GFP_KERNEL);
++ if (!strm->buffers) {
++ dev_err(a->dev, "Buffer list allocation failed\n");
++ return;
++ }
++ strm->buffers[0].dma_addr = dma_addr;
++ strm->buffers[0].ptr = dma_ptr;
++ strm->buffers[0].size = size;
++
++ strm->alsa_hw_desc = kmalloc(sizeof(struct snd_pcm_hardware), GFP_KERNEL);
++ if (aaudio_create_hw_info(&strm->desc, strm->alsa_hw_desc, strm->buffers[0].size)) {
++ kfree(strm->alsa_hw_desc);
++ strm->alsa_hw_desc = NULL;
++ }
++}
++
++static void aaudio_handle_prop_change(struct aaudio_device *a, struct aaudio_msg *msg);
++
++void aaudio_handle_notification(struct aaudio_device *a, struct aaudio_msg *msg)
++{
++ struct aaudio_send_ctx sctx;
++ struct aaudio_msg_base base;
++ if (aaudio_msg_read_base(msg, &base))
++ return;
++ switch (base.msg) {
++ case AAUDIO_MSG_NOTIFICATION_BOOT:
++ dev_info(a->dev, "Received boot notification from remote\n");
++
++ /* Resend the alive notify */
++ if (aaudio_send(a, &sctx, 500,
++ aaudio_msg_write_alive_notification, 1, 3)) {
++ pr_err("Sending alive notification failed\n");
++ }
++ break;
++ case AAUDIO_MSG_NOTIFICATION_ALIVE:
++ dev_info(a->dev, "Received alive notification from remote\n");
++ complete_all(&a->remote_alive);
++ break;
++ case AAUDIO_MSG_PROPERTY_CHANGED:
++ aaudio_handle_prop_change(a, msg);
++ break;
++ default:
++ dev_info(a->dev, "Unhandled notification %i", base.msg);
++ break;
++ }
++}
++
++struct aaudio_prop_change_work_struct {
++ struct work_struct ws;
++ struct aaudio_device *a;
++ aaudio_device_id_t dev;
++ aaudio_object_id_t obj;
++ struct aaudio_prop_addr prop;
++};
++
++static void aaudio_handle_jack_connection_change(struct aaudio_subdevice *sdev)
++{
++ u32 plugged;
++ if (!sdev->jack)
++ return;
++ /* NOTE: Apple made the plug status scoped to the input and output streams. This makes no sense for us, so I just
++ * always pick the OUTPUT status. */
++ if (aaudio_cmd_get_primitive_property(sdev->a, sdev->dev_id, sdev->dev_id,
++ AAUDIO_PROP(AAUDIO_PROP_SCOPE_OUTPUT, AAUDIO_PROP_JACK_PLUGGED, 0), NULL, 0, &plugged, sizeof(plugged))) {
++ dev_err(sdev->a->dev, "Failed to get jack enable status\n");
++ return;
++ }
++ dev_dbg(sdev->a->dev, "Jack is now %s\n", plugged ? "plugged" : "unplugged");
++ snd_jack_report(sdev->jack, plugged ? sdev->jack->type : 0);
++}
++
++void aaudio_handle_prop_change_work(struct work_struct *ws)
++{
++ struct aaudio_prop_change_work_struct *work = container_of(ws, struct aaudio_prop_change_work_struct, ws);
++ struct aaudio_subdevice *sdev;
++
++ sdev = aaudio_find_dev_by_dev_id(work->a, work->dev);
++ if (!sdev) {
++ dev_err(work->a->dev, "Property notification change: device not found\n");
++ goto done;
++ }
++ dev_dbg(work->a->dev, "Property changed for device: %s\n", sdev->uid);
++
++ if (work->prop.scope == AAUDIO_PROP_SCOPE_OUTPUT && work->prop.selector == AAUDIO_PROP_JACK_PLUGGED) {
++ aaudio_handle_jack_connection_change(sdev);
++ }
++
++done:
++ kfree(work);
++}
++
++void aaudio_handle_prop_change(struct aaudio_device *a, struct aaudio_msg *msg)
++{
++ /* NOTE: This is a scheduled work because this callback will generally need to query device information and this
++ * is not possible when we are in the reply parsing code's context. */
++ struct aaudio_prop_change_work_struct *work;
++ work = kmalloc(sizeof(struct aaudio_prop_change_work_struct), GFP_KERNEL);
++ work->a = a;
++ INIT_WORK(&work->ws, aaudio_handle_prop_change_work);
++ aaudio_msg_read_property_changed(msg, &work->dev, &work->obj, &work->prop);
++ schedule_work(&work->ws);
++}
++
++#define aaudio_send_cmd_response(a, sctx, msg, fn, ...) \
++ if (aaudio_send_with_tag(a, sctx, ((struct aaudio_msg_header *) msg->data)->tag, 500, fn, ##__VA_ARGS__)) \
++ pr_err("aaudio: Failed to reply to a command\n");
++
++void aaudio_handle_cmd_timestamp(struct aaudio_device *a, struct aaudio_msg *msg)
++{
++ ktime_t time_os = ktime_get_boottime();
++ struct aaudio_send_ctx sctx;
++ struct aaudio_subdevice *sdev;
++ u64 devid, timestamp, update_seed;
++ aaudio_msg_read_update_timestamp(msg, &devid, &timestamp, &update_seed);
++ dev_dbg(a->dev, "Received timestamp update for dev=%llx ts=%llx seed=%llx\n", devid, timestamp, update_seed);
++
++ sdev = aaudio_find_dev_by_dev_id(a, devid);
++ aaudio_handle_timestamp(sdev, time_os, timestamp);
++
++ aaudio_send_cmd_response(a, &sctx, msg,
++ aaudio_msg_write_update_timestamp_response);
++}
++
++void aaudio_handle_command(struct aaudio_device *a, struct aaudio_msg *msg)
++{
++ struct aaudio_msg_base base;
++ if (aaudio_msg_read_base(msg, &base))
++ return;
++ switch (base.msg) {
++ case AAUDIO_MSG_UPDATE_TIMESTAMP:
++ aaudio_handle_cmd_timestamp(a, msg);
++ break;
++ default:
++ dev_info(a->dev, "Unhandled device command %i", base.msg);
++ break;
++ }
++}
++
++static struct pci_device_id aaudio_ids[ ] = {
++ { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x1803) },
++ { 0, },
++};
++
++struct dev_pm_ops aaudio_pci_driver_pm = {
++ .suspend = aaudio_suspend,
++ .resume = aaudio_resume
++};
++struct pci_driver aaudio_pci_driver = {
++ .name = "aaudio",
++ .id_table = aaudio_ids,
++ .probe = aaudio_probe,
++ .remove = aaudio_remove,
++ .driver = {
++ .pm = &aaudio_pci_driver_pm
++ }
++};
++
++
++int aaudio_module_init(void)
++{
++ int result;
++ if ((result = alloc_chrdev_region(&aaudio_chrdev, 0, 1, "aaudio")))
++ goto fail_chrdev;
++#if LINUX_VERSION_CODE < KERNEL_VERSION(6,4,0)
++ aaudio_class = class_create(THIS_MODULE, "aaudio");
++#else
++ aaudio_class = class_create("aaudio");
++#endif
++ if (IS_ERR(aaudio_class)) {
++ result = PTR_ERR(aaudio_class);
++ goto fail_class;
++ }
++
++ result = pci_register_driver(&aaudio_pci_driver);
++ if (result)
++ goto fail_drv;
++ return 0;
++
++fail_drv:
++ pci_unregister_driver(&aaudio_pci_driver);
++fail_class:
++ class_destroy(aaudio_class);
++fail_chrdev:
++ unregister_chrdev_region(aaudio_chrdev, 1);
++ if (!result)
++ result = -EINVAL;
++ return result;
++}
++
++void aaudio_module_exit(void)
++{
++ pci_unregister_driver(&aaudio_pci_driver);
++ class_destroy(aaudio_class);
++ unregister_chrdev_region(aaudio_chrdev, 1);
++}
++
++struct aaudio_alsa_pcm_id_mapping aaudio_alsa_id_mappings[] = {
++ {"Speaker", 0},
++ {"Digital Mic", 1},
++ {"Codec Output", 2},
++ {"Codec Input", 3},
++ {"Bridge Loopback", 4},
++ {}
++};
++
++module_param_named(index, aaudio_alsa_index, int, 0444);
++MODULE_PARM_DESC(index, "Index value for Apple Internal Audio soundcard.");
++module_param_named(id, aaudio_alsa_id, charp, 0444);
++MODULE_PARM_DESC(id, "ID string for Apple Internal Audio soundcard.");
+diff --git a/drivers/staging/apple-bce/audio/audio.h b/drivers/staging/apple-bce/audio/audio.h
+new file mode 100644
+index 000000000..693006e93
+--- /dev/null
++++ b/drivers/staging/apple-bce/audio/audio.h
+@@ -0,0 +1,123 @@
++#ifndef AAUDIO_H
++#define AAUDIO_H
++
++#include <linux/types.h>
++#include <sound/pcm.h>
++#include "../apple_bce.h"
++#include "protocol_bce.h"
++#include "description.h"
++
++#define AAUDIO_SIG 0x19870423
++
++#define AAUDIO_DEVICE_MAX_UID_LEN 128
++#define AAUDIO_DEIVCE_MAX_INPUT_STREAMS 1
++#define AAUDIO_DEIVCE_MAX_OUTPUT_STREAMS 1
++#define AAUDIO_DEIVCE_MAX_BUFFER_COUNT 1
++
++#define AAUDIO_BUFFER_ID_NONE 0xffu
++
++struct snd_card;
++struct snd_pcm;
++struct snd_pcm_hardware;
++struct snd_jack;
++
++struct __attribute__((packed)) __attribute__((aligned(4))) aaudio_buffer_struct_buffer {
++ size_t address;
++ size_t size;
++ size_t pad[4];
++};
++struct aaudio_buffer_struct_stream {
++ u8 num_buffers;
++ struct aaudio_buffer_struct_buffer buffers[100];
++ char filler[32];
++};
++struct aaudio_buffer_struct_device {
++ char name[128];
++ u8 num_input_streams;
++ u8 num_output_streams;
++ struct aaudio_buffer_struct_stream input_streams[5];
++ struct aaudio_buffer_struct_stream output_streams[5];
++ char filler[128];
++};
++struct aaudio_buffer_struct {
++ u32 version;
++ u32 signature;
++ u32 flags;
++ u8 num_devices;
++ struct aaudio_buffer_struct_device devices[20];
++};
++
++struct aaudio_device;
++struct aaudio_dma_buf {
++ dma_addr_t dma_addr;
++ void *ptr;
++ size_t size;
++};
++struct aaudio_stream {
++ aaudio_object_id_t id;
++ size_t buffer_cnt;
++ struct aaudio_dma_buf *buffers;
++
++ struct aaudio_apple_description desc;
++ struct snd_pcm_hardware *alsa_hw_desc;
++ u32 latency;
++
++ bool waiting_for_first_ts;
++
++ ktime_t remote_timestamp;
++ snd_pcm_sframes_t frame_min;
++ int started;
++};
++struct aaudio_subdevice {
++ struct aaudio_device *a;
++ struct list_head list;
++ aaudio_device_id_t dev_id;
++ u32 in_latency, out_latency;
++ u8 buf_id;
++ int alsa_id;
++ char uid[AAUDIO_DEVICE_MAX_UID_LEN + 1];
++ size_t in_stream_cnt;
++ struct aaudio_stream in_streams[AAUDIO_DEIVCE_MAX_INPUT_STREAMS];
++ size_t out_stream_cnt;
++ struct aaudio_stream out_streams[AAUDIO_DEIVCE_MAX_OUTPUT_STREAMS];
++ bool is_pcm;
++ struct snd_pcm *pcm;
++ struct snd_jack *jack;
++};
++struct aaudio_alsa_pcm_id_mapping {
++ const char *name;
++ int alsa_id;
++};
++
++struct aaudio_device {
++ struct pci_dev *pci;
++ dev_t devt;
++ struct device *dev;
++ void __iomem *reg_mem_bs;
++ dma_addr_t reg_mem_bs_dma;
++ void __iomem *reg_mem_cfg;
++
++ u32 __iomem *reg_mem_gpr;
++
++ struct aaudio_buffer_struct *bs;
++
++ struct apple_bce_device *bce;
++ struct aaudio_bce bcem;
++
++ struct snd_card *card;
++
++ struct list_head subdevice_list;
++ int next_alsa_id;
++
++ struct completion remote_alive;
++};
++
++void aaudio_handle_notification(struct aaudio_device *a, struct aaudio_msg *msg);
++void aaudio_handle_command(struct aaudio_device *a, struct aaudio_msg *msg);
++
++int aaudio_module_init(void);
++void aaudio_module_exit(void);
++
++extern struct aaudio_alsa_pcm_id_mapping aaudio_alsa_id_mappings[];
++
++#endif //AAUDIO_H
+diff --git a/drivers/staging/apple-bce/audio/description.h b/drivers/staging/apple-bce/audio/description.h
+new file mode 100644
+index 000000000..dfef3ab68
+--- /dev/null
++++ b/drivers/staging/apple-bce/audio/description.h
+@@ -0,0 +1,42 @@
++#ifndef AAUDIO_DESCRIPTION_H
++#define AAUDIO_DESCRIPTION_H
++
++#include <linux/types.h>
++
++struct aaudio_apple_description {
++ u64 sample_rate_double;
++ u32 format_id;
++ u32 format_flags;
++ u32 bytes_per_packet;
++ u32 frames_per_packet;
++ u32 bytes_per_frame;
++ u32 channels_per_frame;
++ u32 bits_per_channel;
++ u32 reserved;
++};
++
++enum {
++ AAUDIO_FORMAT_LPCM = 0x6c70636d // 'lpcm'
++};
++
++enum {
++ AAUDIO_FORMAT_FLAG_FLOAT = 1,
++ AAUDIO_FORMAT_FLAG_BIG_ENDIAN = 2,
++ AAUDIO_FORMAT_FLAG_SIGNED = 4,
++ AAUDIO_FORMAT_FLAG_PACKED = 8,
++ AAUDIO_FORMAT_FLAG_ALIGNED_HIGH = 16,
++ AAUDIO_FORMAT_FLAG_NON_INTERLEAVED = 32,
++ AAUDIO_FORMAT_FLAG_NON_MIXABLE = 64
++};
++
++static inline u64 aaudio_double_to_u64(u64 d)
++{
++ u8 sign = (u8) ((d >> 63) & 1);
++ s32 exp = (s32) ((d >> 52) & 0x7ff) - 1023;
++ u64 fr = d & ((1LL << 52) - 1);
++ if (sign || exp < 0)
++ return 0;
++ return (u64) ((1LL << exp) + (fr >> (52 - exp)));
++}
++
++#endif //AAUDIO_DESCRIPTION_H
+diff --git a/drivers/staging/apple-bce/audio/pcm.c b/drivers/staging/apple-bce/audio/pcm.c
+new file mode 100644
+index 000000000..1026e10a9
+--- /dev/null
++++ b/drivers/staging/apple-bce/audio/pcm.c
+@@ -0,0 +1,308 @@
++#include "pcm.h"
++#include "audio.h"
++
++static u64 aaudio_get_alsa_fmtbit(struct aaudio_apple_description *desc)
++{
++ if (desc->format_flags & AAUDIO_FORMAT_FLAG_FLOAT) {
++ if (desc->bits_per_channel == 32) {
++ if (desc->format_flags & AAUDIO_FORMAT_FLAG_BIG_ENDIAN)
++ return SNDRV_PCM_FMTBIT_FLOAT_BE;
++ else
++ return SNDRV_PCM_FMTBIT_FLOAT_LE;
++ } else if (desc->bits_per_channel == 64) {
++ if (desc->format_flags & AAUDIO_FORMAT_FLAG_BIG_ENDIAN)
++ return SNDRV_PCM_FMTBIT_FLOAT64_BE;
++ else
++ return SNDRV_PCM_FMTBIT_FLOAT64_LE;
++ } else {
++ pr_err("aaudio: unsupported bits per channel for float format: %u\n", desc->bits_per_channel);
++ return 0;
++ }
++ }
++#define DEFINE_BPC_OPTION(val, b) \
++ case val: \
++ if (desc->format_flags & AAUDIO_FORMAT_FLAG_BIG_ENDIAN) { \
++ if (desc->format_flags & AAUDIO_FORMAT_FLAG_SIGNED) \
++ return SNDRV_PCM_FMTBIT_S ## b ## BE; \
++ else \
++ return SNDRV_PCM_FMTBIT_U ## b ## BE; \
++ } else { \
++ if (desc->format_flags & AAUDIO_FORMAT_FLAG_SIGNED) \
++ return SNDRV_PCM_FMTBIT_S ## b ## LE; \
++ else \
++ return SNDRV_PCM_FMTBIT_U ## b ## LE; \
++ }
++ if (desc->format_flags & AAUDIO_FORMAT_FLAG_PACKED) {
++ switch (desc->bits_per_channel) {
++ case 8:
++ case 16:
++ case 32:
++ break;
++ DEFINE_BPC_OPTION(24, 24_3)
++ default:
++ pr_err("aaudio: unsupported bits per channel for packed format: %u\n", desc->bits_per_channel);
++ return 0;
++ }
++ }
++ if (desc->format_flags & AAUDIO_FORMAT_FLAG_ALIGNED_HIGH) {
++ switch (desc->bits_per_channel) {
++ DEFINE_BPC_OPTION(24, 32_)
++ default:
++ pr_err("aaudio: unsupported bits per channel for high-aligned format: %u\n", desc->bits_per_channel);
++ return 0;
++ }
++ }
++ switch (desc->bits_per_channel) {
++ case 8:
++ if (desc->format_flags & AAUDIO_FORMAT_FLAG_SIGNED)
++ return SNDRV_PCM_FMTBIT_S8;
++ else
++ return SNDRV_PCM_FMTBIT_U8;
++ DEFINE_BPC_OPTION(16, 16_)
++ DEFINE_BPC_OPTION(24, 24_)
++ DEFINE_BPC_OPTION(32, 32_)
++ default:
++ pr_err("aaudio: unsupported bits per channel: %u\n", desc->bits_per_channel);
++ return 0;
++ }
++}
++int aaudio_create_hw_info(struct aaudio_apple_description *desc, struct snd_pcm_hardware *alsa_hw,
++ size_t buf_size)
++{
++ uint rate;
++ alsa_hw->info = (SNDRV_PCM_INFO_MMAP |
++ SNDRV_PCM_INFO_BLOCK_TRANSFER |
++ SNDRV_PCM_INFO_MMAP_VALID |
++ SNDRV_PCM_INFO_DOUBLE);
++ if (desc->format_flags & AAUDIO_FORMAT_FLAG_NON_MIXABLE)
++ pr_warn("aaudio: unsupported hw flag: NON_MIXABLE\n");
++ if (!(desc->format_flags & AAUDIO_FORMAT_FLAG_NON_INTERLEAVED))
++ alsa_hw->info |= SNDRV_PCM_INFO_INTERLEAVED;
++ alsa_hw->formats = aaudio_get_alsa_fmtbit(desc);
++ if (!alsa_hw->formats)
++ return -EINVAL;
++ rate = (uint) aaudio_double_to_u64(desc->sample_rate_double);
++ alsa_hw->rates = snd_pcm_rate_to_rate_bit(rate);
++ alsa_hw->rate_min = rate;
++ alsa_hw->rate_max = rate;
++ alsa_hw->channels_min = desc->channels_per_frame;
++ alsa_hw->channels_max = desc->channels_per_frame;
++ alsa_hw->buffer_bytes_max = buf_size;
++ alsa_hw->period_bytes_min = desc->bytes_per_packet;
++ alsa_hw->period_bytes_max = desc->bytes_per_packet;
++ alsa_hw->periods_min = (uint) (buf_size / desc->bytes_per_packet);
++ alsa_hw->periods_max = (uint) (buf_size / desc->bytes_per_packet);
++ pr_debug("aaudio_create_hw_info: format = %llu, rate = %u/%u. channels = %u, periods = %u, period size = %lu\n",
++ alsa_hw->formats, alsa_hw->rate_min, alsa_hw->rates, alsa_hw->channels_min, alsa_hw->periods_min,
++ alsa_hw->period_bytes_min);
++ return 0;
++}
++
++static struct aaudio_stream *aaudio_pcm_stream(struct snd_pcm_substream *substream)
++{
++ struct aaudio_subdevice *sdev = snd_pcm_substream_chip(substream);
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ return &sdev->out_streams[substream->number];
++ else
++ return &sdev->in_streams[substream->number];
++}
++
++static int aaudio_pcm_open(struct snd_pcm_substream *substream)
++{
++ pr_debug("aaudio_pcm_open\n");
++ substream->runtime->hw = *aaudio_pcm_stream(substream)->alsa_hw_desc;
++
++ return 0;
++}
++
++static int aaudio_pcm_close(struct snd_pcm_substream *substream)
++{
++ pr_debug("aaudio_pcm_close\n");
++ return 0;
++}
++
++static int aaudio_pcm_prepare(struct snd_pcm_substream *substream)
++{
++ return 0;
++}
++
++static int aaudio_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
++{
++ struct aaudio_stream *astream = aaudio_pcm_stream(substream);
++ pr_debug("aaudio_pcm_hw_params\n");
++
++ if (!astream->buffer_cnt || !astream->buffers)
++ return -EINVAL;
++
++ substream->runtime->dma_area = astream->buffers[0].ptr;
++ substream->runtime->dma_addr = astream->buffers[0].dma_addr;
++ substream->runtime->dma_bytes = astream->buffers[0].size;
++ return 0;
++}
++
++static int aaudio_pcm_hw_free(struct snd_pcm_substream *substream)
++{
++ pr_debug("aaudio_pcm_hw_free\n");
++ return 0;
++}
++
++static void aaudio_pcm_start(struct snd_pcm_substream *substream)
++{
++ struct aaudio_subdevice *sdev = snd_pcm_substream_chip(substream);
++ struct aaudio_stream *stream = aaudio_pcm_stream(substream);
++ void *buf;
++ size_t s;
++ ktime_t time_start, time_end;
++ bool back_buffer;
++ time_start = ktime_get();
++
++ back_buffer = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
++
++ if (back_buffer) {
++ s = frames_to_bytes(substream->runtime, substream->runtime->control->appl_ptr);
++ buf = kmalloc(s, GFP_KERNEL);
++ memcpy_fromio(buf, substream->runtime->dma_area, s);
++ time_end = ktime_get();
++ pr_debug("aaudio: Backed up the buffer in %lluns [%li]\n", ktime_to_ns(time_end - time_start),
++ substream->runtime->control->appl_ptr);
++ }
++
++ stream->waiting_for_first_ts = true;
++ stream->frame_min = stream->latency;
++
++ aaudio_cmd_start_io(sdev->a, sdev->dev_id);
++ if (back_buffer)
++ memcpy_toio(substream->runtime->dma_area, buf, s);
++
++ time_end = ktime_get();
++ pr_debug("aaudio: Started the audio device in %lluns\n", ktime_to_ns(time_end - time_start));
++}
++
++static int aaudio_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ struct aaudio_subdevice *sdev = snd_pcm_substream_chip(substream);
++ struct aaudio_stream *stream = aaudio_pcm_stream(substream);
++ pr_debug("aaudio_pcm_trigger %x\n", cmd);
++
++ /* We only supports triggers on the #0 buffer */
++ if (substream->number != 0)
++ return 0;
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ aaudio_pcm_start(substream);
++ stream->started = 1;
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ aaudio_cmd_stop_io(sdev->a, sdev->dev_id);
++ stream->started = 0;
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static snd_pcm_uframes_t aaudio_pcm_pointer(struct snd_pcm_substream *substream)
++{
++ struct aaudio_stream *stream = aaudio_pcm_stream(substream);
++ ktime_t time_from_start;
++ snd_pcm_sframes_t frames;
++ snd_pcm_sframes_t buffer_time_length;
++
++ if (!stream->started || stream->waiting_for_first_ts) {
++ pr_warn("aaudio_pcm_pointer while not started\n");
++ return 0;
++ }
++
++ /* Approximate the pointer based on the last received timestamp */
++ time_from_start = ktime_get_boottime() - stream->remote_timestamp;
++ buffer_time_length = NSEC_PER_SEC * substream->runtime->buffer_size / substream->runtime->rate;
++ frames = (ktime_to_ns(time_from_start) % buffer_time_length) * substream->runtime->buffer_size / buffer_time_length;
++ if (ktime_to_ns(time_from_start) < buffer_time_length) {
++ if (frames < stream->frame_min)
++ frames = stream->frame_min;
++ else
++ stream->frame_min = 0;
++ } else {
++ if (ktime_to_ns(time_from_start) < 2 * buffer_time_length)
++ stream->frame_min = frames;
++ else
++ stream->frame_min = 0; /* Heavy desync */
++ }
++ frames -= stream->latency;
++ if (frames < 0)
++ frames += ((-frames - 1) / substream->runtime->buffer_size + 1) * substream->runtime->buffer_size;
++ return (snd_pcm_uframes_t) frames;
++}
++
++static struct snd_pcm_ops aaudio_pcm_ops = {
++ .open = aaudio_pcm_open,
++ .close = aaudio_pcm_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = aaudio_pcm_hw_params,
++ .hw_free = aaudio_pcm_hw_free,
++ .prepare = aaudio_pcm_prepare,
++ .trigger = aaudio_pcm_trigger,
++ .pointer = aaudio_pcm_pointer,
++ .mmap = snd_pcm_lib_mmap_iomem
++};
++
++int aaudio_create_pcm(struct aaudio_subdevice *sdev)
++{
++ struct snd_pcm *pcm;
++ struct aaudio_alsa_pcm_id_mapping *id_mapping;
++ int err;
++
++ if (!sdev->is_pcm || (sdev->in_stream_cnt == 0 && sdev->out_stream_cnt == 0)) {
++ return -EINVAL;
++ }
++
++ for (id_mapping = aaudio_alsa_id_mappings; id_mapping->name; id_mapping++) {
++ if (!strcmp(sdev->uid, id_mapping->name)) {
++ sdev->alsa_id = id_mapping->alsa_id;
++ break;
++ }
++ }
++ if (!id_mapping->name)
++ sdev->alsa_id = sdev->a->next_alsa_id++;
++ err = snd_pcm_new(sdev->a->card, sdev->uid, sdev->alsa_id,
++ (int) sdev->out_stream_cnt, (int) sdev->in_stream_cnt, &pcm);
++ if (err < 0)
++ return err;
++ pcm->private_data = sdev;
++ pcm->nonatomic = 1;
++ sdev->pcm = pcm;
++ strcpy(pcm->name, sdev->uid);
++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaudio_pcm_ops);
++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaudio_pcm_ops);
++ return 0;
++}
++
++static void aaudio_handle_stream_timestamp(struct snd_pcm_substream *substream, ktime_t timestamp)
++{
++ unsigned long flags;
++ struct aaudio_stream *stream;
++
++ stream = aaudio_pcm_stream(substream);
++ snd_pcm_stream_lock_irqsave(substream, flags);
++ stream->remote_timestamp = timestamp;
++ if (stream->waiting_for_first_ts) {
++ stream->waiting_for_first_ts = false;
++ snd_pcm_stream_unlock_irqrestore(substream, flags);
++ return;
++ }
++ snd_pcm_stream_unlock_irqrestore(substream, flags);
++ snd_pcm_period_elapsed(substream);
++}
++
++void aaudio_handle_timestamp(struct aaudio_subdevice *sdev, ktime_t os_timestamp, u64 dev_timestamp)
++{
++ struct snd_pcm_substream *substream;
++
++ substream = sdev->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
++ if (substream)
++ aaudio_handle_stream_timestamp(substream, dev_timestamp);
++ substream = sdev->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
++ if (substream)
++ aaudio_handle_stream_timestamp(substream, os_timestamp);
++}
+diff --git a/drivers/staging/apple-bce/audio/pcm.h b/drivers/staging/apple-bce/audio/pcm.h
+new file mode 100644
+index 000000000..ea5f35fbe
+--- /dev/null
++++ b/drivers/staging/apple-bce/audio/pcm.h
+@@ -0,0 +1,16 @@
++#ifndef AAUDIO_PCM_H
++#define AAUDIO_PCM_H
++
++#include <linux/types.h>
++#include <linux/ktime.h>
++
++struct aaudio_subdevice;
++struct aaudio_apple_description;
++struct snd_pcm_hardware;
++
++int aaudio_create_hw_info(struct aaudio_apple_description *desc, struct snd_pcm_hardware *alsa_hw, size_t buf_size);
++int aaudio_create_pcm(struct aaudio_subdevice *sdev);
++
++void aaudio_handle_timestamp(struct aaudio_subdevice *sdev, ktime_t os_timestamp, u64 dev_timestamp);
++
++#endif //AAUDIO_PCM_H
+diff --git a/drivers/staging/apple-bce/audio/protocol.c b/drivers/staging/apple-bce/audio/protocol.c
+new file mode 100644
+index 000000000..2314813ae
+--- /dev/null
++++ b/drivers/staging/apple-bce/audio/protocol.c
+@@ -0,0 +1,347 @@
++#include "protocol.h"
++#include "protocol_bce.h"
++#include "audio.h"
++
++int aaudio_msg_read_base(struct aaudio_msg *msg, struct aaudio_msg_base *base)
++{
++ if (msg->size < sizeof(struct aaudio_msg_header) + sizeof(struct aaudio_msg_base) * 2)
++ return -EINVAL;
++ *base = *((struct aaudio_msg_base *) ((struct aaudio_msg_header *) msg->data + 1));
++ return 0;
++}
++
++#define READ_START(type) \
++ size_t offset = sizeof(struct aaudio_msg_header) + sizeof(struct aaudio_msg_base); (void)offset; \
++ if (((struct aaudio_msg_base *) ((struct aaudio_msg_header *) msg->data + 1))->msg != type) \
++ return -EINVAL;
++#define READ_DEVID_VAR(devid) *devid = ((struct aaudio_msg_header *) msg->data)->device_id
++#define READ_VAL(type) ({ offset += sizeof(type); *((type *) ((u8 *) msg->data + offset - sizeof(type))); })
++#define READ_VAR(type, var) *var = READ_VAL(type)
++
++int aaudio_msg_read_start_io_response(struct aaudio_msg *msg)
++{
++ READ_START(AAUDIO_MSG_START_IO_RESPONSE);
++ return 0;
++}
++
++int aaudio_msg_read_stop_io_response(struct aaudio_msg *msg)
++{
++ READ_START(AAUDIO_MSG_STOP_IO_RESPONSE);
++ return 0;
++}
++
++int aaudio_msg_read_update_timestamp(struct aaudio_msg *msg, aaudio_device_id_t *devid,
++ u64 *timestamp, u64 *update_seed)
++{
++ READ_START(AAUDIO_MSG_UPDATE_TIMESTAMP);
++ READ_DEVID_VAR(devid);
++ READ_VAR(u64, timestamp);
++ READ_VAR(u64, update_seed);
++ return 0;
++}
++
++int aaudio_msg_read_get_property_response(struct aaudio_msg *msg, aaudio_object_id_t *obj,
++ struct aaudio_prop_addr *prop, void **data, u64 *data_size)
++{
++ READ_START(AAUDIO_MSG_GET_PROPERTY_RESPONSE);
++ READ_VAR(aaudio_object_id_t, obj);
++ READ_VAR(u32, &prop->element);
++ READ_VAR(u32, &prop->scope);
++ READ_VAR(u32, &prop->selector);
++ READ_VAR(u64, data_size);
++ *data = ((u8 *) msg->data + offset);
++ /* offset += data_size; */
++ return 0;
++}
++
++int aaudio_msg_read_set_property_response(struct aaudio_msg *msg, aaudio_object_id_t *obj)
++{
++ READ_START(AAUDIO_MSG_SET_PROPERTY_RESPONSE);
++ READ_VAR(aaudio_object_id_t, obj);
++ return 0;
++}
++
++int aaudio_msg_read_property_listener_response(struct aaudio_msg *msg, aaudio_object_id_t *obj,
++ struct aaudio_prop_addr *prop)
++{
++ READ_START(AAUDIO_MSG_PROPERTY_LISTENER_RESPONSE);
++ READ_VAR(aaudio_object_id_t, obj);
++ READ_VAR(u32, &prop->element);
++ READ_VAR(u32, &prop->scope);
++ READ_VAR(u32, &prop->selector);
++ return 0;
++}
++
++int aaudio_msg_read_property_changed(struct aaudio_msg *msg, aaudio_device_id_t *devid, aaudio_object_id_t *obj,
++ struct aaudio_prop_addr *prop)
++{
++ READ_START(AAUDIO_MSG_PROPERTY_CHANGED);
++ READ_DEVID_VAR(devid);
++ READ_VAR(aaudio_object_id_t, obj);
++ READ_VAR(u32, &prop->element);
++ READ_VAR(u32, &prop->scope);
++ READ_VAR(u32, &prop->selector);
++ return 0;
++}
++
++int aaudio_msg_read_set_input_stream_address_ranges_response(struct aaudio_msg *msg)
++{
++ READ_START(AAUDIO_MSG_SET_INPUT_STREAM_ADDRESS_RANGES_RESPONSE);
++ return 0;
++}
++
++int aaudio_msg_read_get_input_stream_list_response(struct aaudio_msg *msg, aaudio_object_id_t **str_l, u64 *str_cnt)
++{
++ READ_START(AAUDIO_MSG_GET_INPUT_STREAM_LIST_RESPONSE);
++ READ_VAR(u64, str_cnt);
++ *str_l = (aaudio_device_id_t *) ((u8 *) msg->data + offset);
++ /* offset += str_cnt * sizeof(aaudio_object_id_t); */
++ return 0;
++}
++
++int aaudio_msg_read_get_output_stream_list_response(struct aaudio_msg *msg, aaudio_object_id_t **str_l, u64 *str_cnt)
++{
++ READ_START(AAUDIO_MSG_GET_OUTPUT_STREAM_LIST_RESPONSE);
++ READ_VAR(u64, str_cnt);
++ *str_l = (aaudio_device_id_t *) ((u8 *) msg->data + offset);
++ /* offset += str_cnt * sizeof(aaudio_object_id_t); */
++ return 0;
++}
++
++int aaudio_msg_read_set_remote_access_response(struct aaudio_msg *msg)
++{
++ READ_START(AAUDIO_MSG_SET_REMOTE_ACCESS_RESPONSE);
++ return 0;
++}
++
++int aaudio_msg_read_get_device_list_response(struct aaudio_msg *msg, aaudio_device_id_t **dev_l, u64 *dev_cnt)
++{
++ READ_START(AAUDIO_MSG_GET_DEVICE_LIST_RESPONSE);
++ READ_VAR(u64, dev_cnt);
++ *dev_l = (aaudio_device_id_t *) ((u8 *) msg->data + offset);
++ /* offset += dev_cnt * sizeof(aaudio_device_id_t); */
++ return 0;
++}
++
++#define WRITE_START_OF_TYPE(typev, devid) \
++ size_t offset = sizeof(struct aaudio_msg_header); (void) offset; \
++ ((struct aaudio_msg_header *) msg->data)->type = (typev); \
++ ((struct aaudio_msg_header *) msg->data)->device_id = (devid);
++#define WRITE_START_COMMAND(devid) WRITE_START_OF_TYPE(AAUDIO_MSG_TYPE_COMMAND, devid)
++#define WRITE_START_RESPONSE() WRITE_START_OF_TYPE(AAUDIO_MSG_TYPE_RESPONSE, 0)
++#define WRITE_START_NOTIFICATION() WRITE_START_OF_TYPE(AAUDIO_MSG_TYPE_NOTIFICATION, 0)
++#define WRITE_VAL(type, value) { *((type *) ((u8 *) msg->data + offset)) = value; offset += sizeof(value); }
++#define WRITE_BIN(value, size) { memcpy((u8 *) msg->data + offset, value, size); offset += size; }
++#define WRITE_BASE(type) WRITE_VAL(u32, type) WRITE_VAL(u32, 0)
++#define WRITE_END() { msg->size = offset; }
++
++void aaudio_msg_write_start_io(struct aaudio_msg *msg, aaudio_device_id_t dev)
++{
++ WRITE_START_COMMAND(dev);
++ WRITE_BASE(AAUDIO_MSG_START_IO);
++ WRITE_END();
++}
++
++void aaudio_msg_write_stop_io(struct aaudio_msg *msg, aaudio_device_id_t dev)
++{
++ WRITE_START_COMMAND(dev);
++ WRITE_BASE(AAUDIO_MSG_STOP_IO);
++ WRITE_END();
++}
++
++void aaudio_msg_write_get_property(struct aaudio_msg *msg, aaudio_device_id_t dev, aaudio_object_id_t obj,
++ struct aaudio_prop_addr prop, void *qualifier, u64 qualifier_size)
++{
++ WRITE_START_COMMAND(dev);
++ WRITE_BASE(AAUDIO_MSG_GET_PROPERTY);
++ WRITE_VAL(aaudio_object_id_t, obj);
++ WRITE_VAL(u32, prop.element);
++ WRITE_VAL(u32, prop.scope);
++ WRITE_VAL(u32, prop.selector);
++ WRITE_VAL(u64, qualifier_size);
++ WRITE_BIN(qualifier, qualifier_size);
++ WRITE_END();
++}
++
++void aaudio_msg_write_set_property(struct aaudio_msg *msg, aaudio_device_id_t dev, aaudio_object_id_t obj,
++ struct aaudio_prop_addr prop, void *data, u64 data_size, void *qualifier, u64 qualifier_size)
++{
++ WRITE_START_COMMAND(dev);
++ WRITE_BASE(AAUDIO_MSG_SET_PROPERTY);
++ WRITE_VAL(aaudio_object_id_t, obj);
++ WRITE_VAL(u32, prop.element);
++ WRITE_VAL(u32, prop.scope);
++ WRITE_VAL(u32, prop.selector);
++ WRITE_VAL(u64, data_size);
++ WRITE_BIN(data, data_size);
++ WRITE_VAL(u64, qualifier_size);
++ WRITE_BIN(qualifier, qualifier_size);
++ WRITE_END();
++}
++
++void aaudio_msg_write_property_listener(struct aaudio_msg *msg, aaudio_device_id_t dev, aaudio_object_id_t obj,
++ struct aaudio_prop_addr prop)
++{
++ WRITE_START_COMMAND(dev);
++ WRITE_BASE(AAUDIO_MSG_PROPERTY_LISTENER);
++ WRITE_VAL(aaudio_object_id_t, obj);
++ WRITE_VAL(u32, prop.element);
++ WRITE_VAL(u32, prop.scope);
++ WRITE_VAL(u32, prop.selector);
++ WRITE_END();
++}
++
++void aaudio_msg_write_set_input_stream_address_ranges(struct aaudio_msg *msg, aaudio_device_id_t devid)
++{
++ WRITE_START_COMMAND(devid);
++ WRITE_BASE(AAUDIO_MSG_SET_INPUT_STREAM_ADDRESS_RANGES);
++ WRITE_END();
++}
++
++void aaudio_msg_write_get_input_stream_list(struct aaudio_msg *msg, aaudio_device_id_t devid)
++{
++ WRITE_START_COMMAND(devid);
++ WRITE_BASE(AAUDIO_MSG_GET_INPUT_STREAM_LIST);
++ WRITE_END();
++}
++
++void aaudio_msg_write_get_output_stream_list(struct aaudio_msg *msg, aaudio_device_id_t devid)
++{
++ WRITE_START_COMMAND(devid);
++ WRITE_BASE(AAUDIO_MSG_GET_OUTPUT_STREAM_LIST);
++ WRITE_END();
++}
++
++void aaudio_msg_write_set_remote_access(struct aaudio_msg *msg, u64 mode)
++{
++ WRITE_START_COMMAND(0);
++ WRITE_BASE(AAUDIO_MSG_SET_REMOTE_ACCESS);
++ WRITE_VAL(u64, mode);
++ WRITE_END();
++}
++
++void aaudio_msg_write_alive_notification(struct aaudio_msg *msg, u32 proto_ver, u32 msg_ver)
++{
++ WRITE_START_NOTIFICATION();
++ WRITE_BASE(AAUDIO_MSG_NOTIFICATION_ALIVE);
++ WRITE_VAL(u32, proto_ver);
++ WRITE_VAL(u32, msg_ver);
++ WRITE_END();
++}
++
++void aaudio_msg_write_update_timestamp_response(struct aaudio_msg *msg)
++{
++ WRITE_START_RESPONSE();
++ WRITE_BASE(AAUDIO_MSG_UPDATE_TIMESTAMP_RESPONSE);
++ WRITE_END();
++}
++
++void aaudio_msg_write_get_device_list(struct aaudio_msg *msg)
++{
++ WRITE_START_COMMAND(0);
++ WRITE_BASE(AAUDIO_MSG_GET_DEVICE_LIST);
++ WRITE_END();
++}
++
++#define CMD_SHARED_VARS_NO_REPLY \
++ int status = 0; \
++ struct aaudio_send_ctx sctx;
++#define CMD_SHARED_VARS \
++ CMD_SHARED_VARS_NO_REPLY \
++ struct aaudio_msg reply = aaudio_reply_alloc(); \
++ struct aaudio_msg *buf = &reply;
++#define CMD_SEND_REQUEST(fn, ...) \
++ if ((status = aaudio_send_cmd_sync(a, &sctx, buf, 500, fn, ##__VA_ARGS__))) \
++ return status;
++#define CMD_DEF_SHARED_AND_SEND(fn, ...) \
++ CMD_SHARED_VARS \
++ CMD_SEND_REQUEST(fn, ##__VA_ARGS__);
++#define CMD_DEF_SHARED_NO_REPLY_AND_SEND(fn, ...) \
++ CMD_SHARED_VARS_NO_REPLY \
++ CMD_SEND_REQUEST(fn, ##__VA_ARGS__);
++#define CMD_HNDL_REPLY_NO_FREE(fn, ...) \
++ status = fn(buf, ##__VA_ARGS__); \
++ return status;
++#define CMD_HNDL_REPLY_AND_FREE(fn, ...) \
++ status = fn(buf, ##__VA_ARGS__); \
++ aaudio_reply_free(&reply); \
++ return status;
++
++int aaudio_cmd_start_io(struct aaudio_device *a, aaudio_device_id_t devid)
++{
++ CMD_DEF_SHARED_AND_SEND(aaudio_msg_write_start_io, devid);
++ CMD_HNDL_REPLY_AND_FREE(aaudio_msg_read_start_io_response);
++}
++int aaudio_cmd_stop_io(struct aaudio_device *a, aaudio_device_id_t devid)
++{
++ CMD_DEF_SHARED_AND_SEND(aaudio_msg_write_stop_io, devid);
++ CMD_HNDL_REPLY_AND_FREE(aaudio_msg_read_stop_io_response);
++}
++int aaudio_cmd_get_property(struct aaudio_device *a, struct aaudio_msg *buf,
++ aaudio_device_id_t devid, aaudio_object_id_t obj,
++ struct aaudio_prop_addr prop, void *qualifier, u64 qualifier_size, void **data, u64 *data_size)
++{
++ CMD_DEF_SHARED_NO_REPLY_AND_SEND(aaudio_msg_write_get_property, devid, obj, prop, qualifier, qualifier_size);
++ CMD_HNDL_REPLY_NO_FREE(aaudio_msg_read_get_property_response, &obj, &prop, data, data_size);
++}
++int aaudio_cmd_get_primitive_property(struct aaudio_device *a,
++ aaudio_device_id_t devid, aaudio_object_id_t obj,
++ struct aaudio_prop_addr prop, void *qualifier, u64 qualifier_size, void *data, u64 data_size)
++{
++ int status;
++ struct aaudio_msg reply = aaudio_reply_alloc();
++ void *r_data;
++ u64 r_data_size;
++ if ((status = aaudio_cmd_get_property(a, &reply, devid, obj, prop, qualifier, qualifier_size,
++ &r_data, &r_data_size)))
++ goto finish;
++ if (r_data_size != data_size) {
++ status = -EINVAL;
++ goto finish;
++ }
++ memcpy(data, r_data, data_size);
++finish:
++ aaudio_reply_free(&reply);
++ return status;
++}
++int aaudio_cmd_set_property(struct aaudio_device *a, aaudio_device_id_t devid, aaudio_object_id_t obj,
++ struct aaudio_prop_addr prop, void *qualifier, u64 qualifier_size, void *data, u64 data_size)
++{
++ CMD_DEF_SHARED_AND_SEND(aaudio_msg_write_set_property, devid, obj, prop, data, data_size,
++ qualifier, qualifier_size);
++ CMD_HNDL_REPLY_AND_FREE(aaudio_msg_read_set_property_response, &obj);
++}
++int aaudio_cmd_property_listener(struct aaudio_device *a, aaudio_device_id_t devid, aaudio_object_id_t obj,
++ struct aaudio_prop_addr prop)
++{
++ CMD_DEF_SHARED_AND_SEND(aaudio_msg_write_property_listener, devid, obj, prop);
++ CMD_HNDL_REPLY_AND_FREE(aaudio_msg_read_property_listener_response, &obj, &prop);
++}
++int aaudio_cmd_set_input_stream_address_ranges(struct aaudio_device *a, aaudio_device_id_t devid)
++{
++ CMD_DEF_SHARED_AND_SEND(aaudio_msg_write_set_input_stream_address_ranges, devid);
++ CMD_HNDL_REPLY_AND_FREE(aaudio_msg_read_set_input_stream_address_ranges_response);
++}
++int aaudio_cmd_get_input_stream_list(struct aaudio_device *a, struct aaudio_msg *buf, aaudio_device_id_t devid,
++ aaudio_object_id_t **str_l, u64 *str_cnt)
++{
++ CMD_DEF_SHARED_NO_REPLY_AND_SEND(aaudio_msg_write_get_input_stream_list, devid);
++ CMD_HNDL_REPLY_NO_FREE(aaudio_msg_read_get_input_stream_list_response, str_l, str_cnt);
++}
++int aaudio_cmd_get_output_stream_list(struct aaudio_device *a, struct aaudio_msg *buf, aaudio_device_id_t devid,
++ aaudio_object_id_t **str_l, u64 *str_cnt)
++{
++ CMD_DEF_SHARED_NO_REPLY_AND_SEND(aaudio_msg_write_get_output_stream_list, devid);
++ CMD_HNDL_REPLY_NO_FREE(aaudio_msg_read_get_output_stream_list_response, str_l, str_cnt);
++}
++int aaudio_cmd_set_remote_access(struct aaudio_device *a, u64 mode)
++{
++ CMD_DEF_SHARED_AND_SEND(aaudio_msg_write_set_remote_access, mode);
++ CMD_HNDL_REPLY_AND_FREE(aaudio_msg_read_set_remote_access_response);
++}
++int aaudio_cmd_get_device_list(struct aaudio_device *a, struct aaudio_msg *buf,
++ aaudio_device_id_t **dev_l, u64 *dev_cnt)
++{
++ CMD_DEF_SHARED_NO_REPLY_AND_SEND(aaudio_msg_write_get_device_list);
++ CMD_HNDL_REPLY_NO_FREE(aaudio_msg_read_get_device_list_response, dev_l, dev_cnt);
++}
+\ No newline at end of file
+diff --git a/drivers/staging/apple-bce/audio/protocol.h b/drivers/staging/apple-bce/audio/protocol.h
+new file mode 100644
+index 000000000..3427486f3
+--- /dev/null
++++ b/drivers/staging/apple-bce/audio/protocol.h
+@@ -0,0 +1,147 @@
++#ifndef AAUDIO_PROTOCOL_H
++#define AAUDIO_PROTOCOL_H
++
++#include <linux/types.h>
++
++struct aaudio_device;
++
++typedef u64 aaudio_device_id_t;
++typedef u64 aaudio_object_id_t;
++
++struct aaudio_msg {
++ void *data;
++ size_t size;
++};
++
++struct __attribute__((packed)) aaudio_msg_header {
++ char tag[4];
++ u8 type;
++ aaudio_device_id_t device_id; // Idk, use zero for commands?
++};
++struct __attribute__((packed)) aaudio_msg_base {
++ u32 msg;
++ u32 status;
++};
++
++struct aaudio_prop_addr {
++ u32 scope;
++ u32 selector;
++ u32 element;
++};
++#define AAUDIO_PROP(scope, sel, el) (struct aaudio_prop_addr) { scope, sel, el }
++
++enum {
++ AAUDIO_MSG_TYPE_COMMAND = 1,
++ AAUDIO_MSG_TYPE_RESPONSE = 2,
++ AAUDIO_MSG_TYPE_NOTIFICATION = 3
++};
++
++enum {
++ AAUDIO_MSG_START_IO = 0,
++ AAUDIO_MSG_START_IO_RESPONSE = 1,
++ AAUDIO_MSG_STOP_IO = 2,
++ AAUDIO_MSG_STOP_IO_RESPONSE = 3,
++ AAUDIO_MSG_UPDATE_TIMESTAMP = 4,
++ AAUDIO_MSG_GET_PROPERTY = 7,
++ AAUDIO_MSG_GET_PROPERTY_RESPONSE = 8,
++ AAUDIO_MSG_SET_PROPERTY = 9,
++ AAUDIO_MSG_SET_PROPERTY_RESPONSE = 10,
++ AAUDIO_MSG_PROPERTY_LISTENER = 11,
++ AAUDIO_MSG_PROPERTY_LISTENER_RESPONSE = 12,
++ AAUDIO_MSG_PROPERTY_CHANGED = 13,
++ AAUDIO_MSG_SET_INPUT_STREAM_ADDRESS_RANGES = 18,
++ AAUDIO_MSG_SET_INPUT_STREAM_ADDRESS_RANGES_RESPONSE = 19,
++ AAUDIO_MSG_GET_INPUT_STREAM_LIST = 24,
++ AAUDIO_MSG_GET_INPUT_STREAM_LIST_RESPONSE = 25,
++ AAUDIO_MSG_GET_OUTPUT_STREAM_LIST = 26,
++ AAUDIO_MSG_GET_OUTPUT_STREAM_LIST_RESPONSE = 27,
++ AAUDIO_MSG_SET_REMOTE_ACCESS = 32,
++ AAUDIO_MSG_SET_REMOTE_ACCESS_RESPONSE = 33,
++ AAUDIO_MSG_UPDATE_TIMESTAMP_RESPONSE = 34,
++
++ AAUDIO_MSG_NOTIFICATION_ALIVE = 100,
++ AAUDIO_MSG_GET_DEVICE_LIST = 101,
++ AAUDIO_MSG_GET_DEVICE_LIST_RESPONSE = 102,
++ AAUDIO_MSG_NOTIFICATION_BOOT = 104
++};
++
++enum {
++ AAUDIO_REMOTE_ACCESS_OFF = 0,
++ AAUDIO_REMOTE_ACCESS_ON = 2
++};
++
++enum {
++ AAUDIO_PROP_SCOPE_GLOBAL = 0x676c6f62, // 'glob'
++ AAUDIO_PROP_SCOPE_INPUT = 0x696e7074, // 'inpt'
++ AAUDIO_PROP_SCOPE_OUTPUT = 0x6f757470 // 'outp'
++};
++
++enum {
++ AAUDIO_PROP_UID = 0x75696420, // 'uid '
++ AAUDIO_PROP_BOOL_VALUE = 0x6263766c, // 'bcvl'
++ AAUDIO_PROP_JACK_PLUGGED = 0x6a61636b, // 'jack'
++ AAUDIO_PROP_SEL_VOLUME = 0x64656176, // 'deav'
++ AAUDIO_PROP_LATENCY = 0x6c746e63, // 'ltnc'
++ AAUDIO_PROP_PHYS_FORMAT = 0x70667420 // 'pft '
++};
++
++int aaudio_msg_read_base(struct aaudio_msg *msg, struct aaudio_msg_base *base);
++
++int aaudio_msg_read_start_io_response(struct aaudio_msg *msg);
++int aaudio_msg_read_stop_io_response(struct aaudio_msg *msg);
++int aaudio_msg_read_update_timestamp(struct aaudio_msg *msg, aaudio_device_id_t *devid,
++ u64 *timestamp, u64 *update_seed);
++int aaudio_msg_read_get_property_response(struct aaudio_msg *msg, aaudio_object_id_t *obj,
++ struct aaudio_prop_addr *prop, void **data, u64 *data_size);
++int aaudio_msg_read_set_property_response(struct aaudio_msg *msg, aaudio_object_id_t *obj);
++int aaudio_msg_read_property_listener_response(struct aaudio_msg *msg,aaudio_object_id_t *obj,
++ struct aaudio_prop_addr *prop);
++int aaudio_msg_read_property_changed(struct aaudio_msg *msg, aaudio_device_id_t *devid, aaudio_object_id_t *obj,
++ struct aaudio_prop_addr *prop);
++int aaudio_msg_read_set_input_stream_address_ranges_response(struct aaudio_msg *msg);
++int aaudio_msg_read_get_input_stream_list_response(struct aaudio_msg *msg, aaudio_object_id_t **str_l, u64 *str_cnt);
++int aaudio_msg_read_get_output_stream_list_response(struct aaudio_msg *msg, aaudio_object_id_t **str_l, u64 *str_cnt);
++int aaudio_msg_read_set_remote_access_response(struct aaudio_msg *msg);
++int aaudio_msg_read_get_device_list_response(struct aaudio_msg *msg, aaudio_device_id_t **dev_l, u64 *dev_cnt);
++
++void aaudio_msg_write_start_io(struct aaudio_msg *msg, aaudio_device_id_t dev);
++void aaudio_msg_write_stop_io(struct aaudio_msg *msg, aaudio_device_id_t dev);
++void aaudio_msg_write_get_property(struct aaudio_msg *msg, aaudio_device_id_t dev, aaudio_object_id_t obj,
++ struct aaudio_prop_addr prop, void *qualifier, u64 qualifier_size);
++void aaudio_msg_write_set_property(struct aaudio_msg *msg, aaudio_device_id_t dev, aaudio_object_id_t obj,
++ struct aaudio_prop_addr prop, void *data, u64 data_size, void *qualifier, u64 qualifier_size);
++void aaudio_msg_write_property_listener(struct aaudio_msg *msg, aaudio_device_id_t dev, aaudio_object_id_t obj,
++ struct aaudio_prop_addr prop);
++void aaudio_msg_write_set_input_stream_address_ranges(struct aaudio_msg *msg, aaudio_device_id_t devid);
++void aaudio_msg_write_get_input_stream_list(struct aaudio_msg *msg, aaudio_device_id_t devid);
++void aaudio_msg_write_get_output_stream_list(struct aaudio_msg *msg, aaudio_device_id_t devid);
++void aaudio_msg_write_set_remote_access(struct aaudio_msg *msg, u64 mode);
++void aaudio_msg_write_alive_notification(struct aaudio_msg *msg, u32 proto_ver, u32 msg_ver);
++void aaudio_msg_write_update_timestamp_response(struct aaudio_msg *msg);
++void aaudio_msg_write_get_device_list(struct aaudio_msg *msg);
++
++
++int aaudio_cmd_start_io(struct aaudio_device *a, aaudio_device_id_t devid);
++int aaudio_cmd_stop_io(struct aaudio_device *a, aaudio_device_id_t devid);
++int aaudio_cmd_get_property(struct aaudio_device *a, struct aaudio_msg *buf,
++ aaudio_device_id_t devid, aaudio_object_id_t obj,
++ struct aaudio_prop_addr prop, void *qualifier, u64 qualifier_size, void **data, u64 *data_size);
++int aaudio_cmd_get_primitive_property(struct aaudio_device *a,
++ aaudio_device_id_t devid, aaudio_object_id_t obj,
++ struct aaudio_prop_addr prop, void *qualifier, u64 qualifier_size, void *data, u64 data_size);
++int aaudio_cmd_set_property(struct aaudio_device *a, aaudio_device_id_t devid, aaudio_object_id_t obj,
++ struct aaudio_prop_addr prop, void *qualifier, u64 qualifier_size, void *data, u64 data_size);
++int aaudio_cmd_property_listener(struct aaudio_device *a, aaudio_device_id_t devid, aaudio_object_id_t obj,
++ struct aaudio_prop_addr prop);
++int aaudio_cmd_set_input_stream_address_ranges(struct aaudio_device *a, aaudio_device_id_t devid);
++int aaudio_cmd_get_input_stream_list(struct aaudio_device *a, struct aaudio_msg *buf, aaudio_device_id_t devid,
++ aaudio_object_id_t **str_l, u64 *str_cnt);
++int aaudio_cmd_get_output_stream_list(struct aaudio_device *a, struct aaudio_msg *buf, aaudio_device_id_t devid,
++ aaudio_object_id_t **str_l, u64 *str_cnt);
++int aaudio_cmd_set_remote_access(struct aaudio_device *a, u64 mode);
++int aaudio_cmd_get_device_list(struct aaudio_device *a, struct aaudio_msg *buf,
++ aaudio_device_id_t **dev_l, u64 *dev_cnt);
++
++
++
++#endif //AAUDIO_PROTOCOL_H
+diff --git a/drivers/staging/apple-bce/audio/protocol_bce.c b/drivers/staging/apple-bce/audio/protocol_bce.c
+new file mode 100644
+index 000000000..28f2dfd44
+--- /dev/null
++++ b/drivers/staging/apple-bce/audio/protocol_bce.c
+@@ -0,0 +1,226 @@
++#include "protocol_bce.h"
++
++#include "audio.h"
++
++static void aaudio_bce_out_queue_completion(struct bce_queue_sq *sq);
++static void aaudio_bce_in_queue_completion(struct bce_queue_sq *sq);
++static int aaudio_bce_queue_init(struct aaudio_device *dev, struct aaudio_bce_queue *q, const char *name, int direction,
++ bce_sq_completion cfn);
++void aaudio_bce_in_queue_submit_pending(struct aaudio_bce_queue *q, size_t count);
++
++int aaudio_bce_init(struct aaudio_device *dev)
++{
++ int status;
++ struct aaudio_bce *bce = &dev->bcem;
++ bce->cq = bce_create_cq(dev->bce, 0x80);
++ spin_lock_init(&bce->spinlock);
++ if (!bce->cq)
++ return -EINVAL;
++ if ((status = aaudio_bce_queue_init(dev, &bce->qout, "com.apple.BridgeAudio.IntelToARM", DMA_TO_DEVICE,
++ aaudio_bce_out_queue_completion))) {
++ return status;
++ }
++ if ((status = aaudio_bce_queue_init(dev, &bce->qin, "com.apple.BridgeAudio.ARMToIntel", DMA_FROM_DEVICE,
++ aaudio_bce_in_queue_completion))) {
++ return status;
++ }
++ aaudio_bce_in_queue_submit_pending(&bce->qin, bce->qin.el_count);
++ return 0;
++}
++
++int aaudio_bce_queue_init(struct aaudio_device *dev, struct aaudio_bce_queue *q, const char *name, int direction,
++ bce_sq_completion cfn)
++{
++ q->cq = dev->bcem.cq;
++ q->el_size = AAUDIO_BCE_QUEUE_ELEMENT_SIZE;
++ q->el_count = AAUDIO_BCE_QUEUE_ELEMENT_COUNT;
++ /* NOTE: The Apple impl uses 0x80 as the queue size, however we use 21 (in fact 20) to simplify the impl */
++ q->sq = bce_create_sq(dev->bce, q->cq, name, (u32) (q->el_count + 1), direction, cfn, dev);
++ if (!q->sq)
++ return -EINVAL;
++
++ q->data = dma_alloc_coherent(&dev->bce->pci->dev, q->el_size * q->el_count, &q->dma_addr, GFP_KERNEL);
++ if (!q->data) {
++ bce_destroy_sq(dev->bce, q->sq);
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static void aaudio_send_create_tag(struct aaudio_bce *b, int *tagn, char tag[4])
++{
++ char tag_zero[5];
++ b->tag_num = (b->tag_num + 1) % AAUDIO_BCE_QUEUE_TAG_COUNT;
++ *tagn = b->tag_num;
++ snprintf(tag_zero, 5, "S%03d", b->tag_num);
++ *((u32 *) tag) = *((u32 *) tag_zero);
++}
++
++int __aaudio_send_prepare(struct aaudio_bce *b, struct aaudio_send_ctx *ctx, char *tag)
++{
++ int status;
++ size_t index;
++ void *dptr;
++ struct aaudio_msg_header *header;
++ if ((status = bce_reserve_submission(b->qout.sq, &ctx->timeout)))
++ return status;
++ spin_lock_irqsave(&b->spinlock, ctx->irq_flags);
++ index = b->qout.data_tail;
++ dptr = (u8 *) b->qout.data + index * b->qout.el_size;
++ ctx->msg.data = dptr;
++ header = dptr;
++ if (tag)
++ *((u32 *) header->tag) = *((u32 *) tag);
++ else
++ aaudio_send_create_tag(b, &ctx->tag_n, header->tag);
++ return 0;
++}
++
++void __aaudio_send(struct aaudio_bce *b, struct aaudio_send_ctx *ctx)
++{
++ struct bce_qe_submission *s = bce_next_submission(b->qout.sq);
++#ifdef DEBUG
++ pr_debug("aaudio: Sending command data\n");
++ print_hex_dump(KERN_DEBUG, "aaudio:OUT ", DUMP_PREFIX_NONE, 32, 1, ctx->msg.data, ctx->msg.size, true);
++#endif
++ bce_set_submission_single(s, b->qout.dma_addr + (dma_addr_t) (ctx->msg.data - b->qout.data), ctx->msg.size);
++ bce_submit_to_device(b->qout.sq);
++ b->qout.data_tail = (b->qout.data_tail + 1) % b->qout.el_count;
++ spin_unlock_irqrestore(&b->spinlock, ctx->irq_flags);
++}
++
++int __aaudio_send_cmd_sync(struct aaudio_bce *b, struct aaudio_send_ctx *ctx, struct aaudio_msg *reply)
++{
++ struct aaudio_bce_queue_entry ent;
++ DECLARE_COMPLETION_ONSTACK(cmpl);
++ ent.msg = reply;
++ ent.cmpl = &cmpl;
++ b->pending_entries[ctx->tag_n] = &ent;
++ __aaudio_send(b, ctx); /* unlocks the spinlock */
++ ctx->timeout = wait_for_completion_timeout(&cmpl, ctx->timeout);
++ if (ctx->timeout == 0) {
++ /* Remove the pending queue entry; this will be normally handled by the completion route but
++ * during a timeout it won't */
++ spin_lock_irqsave(&b->spinlock, ctx->irq_flags);
++ if (b->pending_entries[ctx->tag_n] == &ent)
++ b->pending_entries[ctx->tag_n] = NULL;
++ spin_unlock_irqrestore(&b->spinlock, ctx->irq_flags);
++ return -ETIMEDOUT;
++ }
++ return 0;
++}
++
++static void aaudio_handle_reply(struct aaudio_bce *b, struct aaudio_msg *reply)
++{
++ const char *tag;
++ int tagn;
++ unsigned long irq_flags;
++ char tag_zero[5];
++ struct aaudio_bce_queue_entry *entry;
++
++ tag = ((struct aaudio_msg_header *) reply->data)->tag;
++ if (tag[0] != 'S') {
++ pr_err("aaudio_handle_reply: Unexpected tag: %.4s\n", tag);
++ return;
++ }
++ *((u32 *) tag_zero) = *((u32 *) tag);
++ tag_zero[4] = 0;
++ if (kstrtoint(&tag_zero[1], 10, &tagn)) {
++ pr_err("aaudio_handle_reply: Tag parse failed: %.4s\n", tag);
++ return;
++ }
++
++ spin_lock_irqsave(&b->spinlock, irq_flags);
++ entry = b->pending_entries[tagn];
++ if (entry) {
++ if (reply->size < entry->msg->size)
++ entry->msg->size = reply->size;
++ memcpy(entry->msg->data, reply->data, entry->msg->size);
++ complete(entry->cmpl);
++
++ b->pending_entries[tagn] = NULL;
++ } else {
++ pr_err("aaudio_handle_reply: No queued item found for tag: %.4s\n", tag);
++ }
++ spin_unlock_irqrestore(&b->spinlock, irq_flags);
++}
++
++static void aaudio_bce_out_queue_completion(struct bce_queue_sq *sq)
++{
++ while (bce_next_completion(sq)) {
++ //pr_info("aaudio: Send confirmed\n");
++ bce_notify_submission_complete(sq);
++ }
++}
++
++static void aaudio_bce_in_queue_handle_msg(struct aaudio_device *a, struct aaudio_msg *msg);
++
++static void aaudio_bce_in_queue_completion(struct bce_queue_sq *sq)
++{
++ struct aaudio_msg msg;
++ struct aaudio_device *dev = sq->userdata;
++ struct aaudio_bce_queue *q = &dev->bcem.qin;
++ struct bce_sq_completion_data *c;
++ size_t cnt = 0;
++
++ mb();
++ while ((c = bce_next_completion(sq))) {
++ msg.data = (u8 *) q->data + q->data_head * q->el_size;
++ msg.size = c->data_size;
++#ifdef DEBUG
++ pr_debug("aaudio: Received command data %llx\n", c->data_size);
++ print_hex_dump(KERN_DEBUG, "aaudio:IN ", DUMP_PREFIX_NONE, 32, 1, msg.data, min(msg.size, 128UL), true);
++#endif
++ aaudio_bce_in_queue_handle_msg(dev, &msg);
++
++ q->data_head = (q->data_head + 1) % q->el_count;
++
++ bce_notify_submission_complete(sq);
++ ++cnt;
++ }
++ aaudio_bce_in_queue_submit_pending(q, cnt);
++}
++
++static void aaudio_bce_in_queue_handle_msg(struct aaudio_device *a, struct aaudio_msg *msg)
++{
++ struct aaudio_msg_header *header = (struct aaudio_msg_header *) msg->data;
++ if (msg->size < sizeof(struct aaudio_msg_header)) {
++ pr_err("aaudio: Msg size smaller than header (%lx)", msg->size);
++ return;
++ }
++ if (header->type == AAUDIO_MSG_TYPE_RESPONSE) {
++ aaudio_handle_reply(&a->bcem, msg);
++ } else if (header->type == AAUDIO_MSG_TYPE_COMMAND) {
++ aaudio_handle_command(a, msg);
++ } else if (header->type == AAUDIO_MSG_TYPE_NOTIFICATION) {
++ aaudio_handle_notification(a, msg);
++ }
++}
++
++void aaudio_bce_in_queue_submit_pending(struct aaudio_bce_queue *q, size_t count)
++{
++ struct bce_qe_submission *s;
++ while (count--) {
++ if (bce_reserve_submission(q->sq, NULL)) {
++ pr_err("aaudio: Failed to reserve an event queue submission\n");
++ break;
++ }
++ s = bce_next_submission(q->sq);
++ bce_set_submission_single(s, q->dma_addr + (dma_addr_t) (q->data_tail * q->el_size), q->el_size);
++ q->data_tail = (q->data_tail + 1) % q->el_count;
++ }
++ bce_submit_to_device(q->sq);
++}
++
++struct aaudio_msg aaudio_reply_alloc(void)
++{
++ struct aaudio_msg ret;
++ ret.size = AAUDIO_BCE_QUEUE_ELEMENT_SIZE;
++ ret.data = kmalloc(ret.size, GFP_KERNEL);
++ return ret;
++}
++
++void aaudio_reply_free(struct aaudio_msg *reply)
++{
++ kfree(reply->data);
++}
+diff --git a/drivers/staging/apple-bce/audio/protocol_bce.h b/drivers/staging/apple-bce/audio/protocol_bce.h
+new file mode 100644
+index 000000000..14d26c05d
+--- /dev/null
++++ b/drivers/staging/apple-bce/audio/protocol_bce.h
+@@ -0,0 +1,72 @@
++#ifndef AAUDIO_PROTOCOL_BCE_H
++#define AAUDIO_PROTOCOL_BCE_H
++
++#include "protocol.h"
++#include "../queue.h"
++
++#define AAUDIO_BCE_QUEUE_ELEMENT_SIZE 0x1000
++#define AAUDIO_BCE_QUEUE_ELEMENT_COUNT 20
++
++#define AAUDIO_BCE_QUEUE_TAG_COUNT 1000
++
++struct aaudio_device;
++
++struct aaudio_bce_queue_entry {
++ struct aaudio_msg *msg;
++ struct completion *cmpl;
++};
++struct aaudio_bce_queue {
++ struct bce_queue_cq *cq;
++ struct bce_queue_sq *sq;
++ void *data;
++ dma_addr_t dma_addr;
++ size_t data_head, data_tail;
++ size_t el_size, el_count;
++};
++struct aaudio_bce {
++ struct bce_queue_cq *cq;
++ struct aaudio_bce_queue qin;
++ struct aaudio_bce_queue qout;
++ int tag_num;
++ struct aaudio_bce_queue_entry *pending_entries[AAUDIO_BCE_QUEUE_TAG_COUNT];
++ struct spinlock spinlock;
++};
++
++struct aaudio_send_ctx {
++ int status;
++ int tag_n;
++ unsigned long irq_flags;
++ struct aaudio_msg msg;
++ unsigned long timeout;
++};
++
++int aaudio_bce_init(struct aaudio_device *dev);
++int __aaudio_send_prepare(struct aaudio_bce *b, struct aaudio_send_ctx *ctx, char *tag);
++void __aaudio_send(struct aaudio_bce *b, struct aaudio_send_ctx *ctx);
++int __aaudio_send_cmd_sync(struct aaudio_bce *b, struct aaudio_send_ctx *ctx, struct aaudio_msg *reply);
++
++#define aaudio_send_with_tag(a, ctx, tag, tout, fn, ...) ({ \
++ (ctx)->timeout = msecs_to_jiffies(tout); \
++ (ctx)->status = __aaudio_send_prepare(&(a)->bcem, (ctx), (tag)); \
++ if (!(ctx)->status) { \
++ fn(&(ctx)->msg, ##__VA_ARGS__); \
++ __aaudio_send(&(a)->bcem, (ctx)); \
++ } \
++ (ctx)->status; \
++})
++#define aaudio_send(a, ctx, tout, fn, ...) aaudio_send_with_tag(a, ctx, NULL, tout, fn, ##__VA_ARGS__)
++
++#define aaudio_send_cmd_sync(a, ctx, reply, tout, fn, ...) ({ \
++ (ctx)->timeout = msecs_to_jiffies(tout); \
++ (ctx)->status = __aaudio_send_prepare(&(a)->bcem, (ctx), NULL); \
++ if (!(ctx)->status) { \
++ fn(&(ctx)->msg, ##__VA_ARGS__); \
++ (ctx)->status = __aaudio_send_cmd_sync(&(a)->bcem, (ctx), (reply)); \
++ } \
++ (ctx)->status; \
++})
++
++struct aaudio_msg aaudio_reply_alloc(void);
++void aaudio_reply_free(struct aaudio_msg *reply);
++
++#endif //AAUDIO_PROTOCOL_BCE_H
+diff --git a/drivers/staging/apple-bce/mailbox.c b/drivers/staging/apple-bce/mailbox.c
+new file mode 100644
+index 000000000..e24bd3521
+--- /dev/null
++++ b/drivers/staging/apple-bce/mailbox.c
+@@ -0,0 +1,151 @@
++#include "mailbox.h"
++#include <linux/atomic.h>
++#include "apple_bce.h"
++
++#define REG_MBOX_OUT_BASE 0x820
++#define REG_MBOX_REPLY_COUNTER 0x108
++#define REG_MBOX_REPLY_BASE 0x810
++#define REG_TIMESTAMP_BASE 0xC000
++
++#define BCE_MBOX_TIMEOUT_MS 200
++
++void bce_mailbox_init(struct bce_mailbox *mb, void __iomem *reg_mb)
++{
++ mb->reg_mb = reg_mb;
++ init_completion(&mb->mb_completion);
++}
++
++int bce_mailbox_send(struct bce_mailbox *mb, u64 msg, u64* recv)
++{
++ u32 __iomem *regb;
++
++ if (atomic_cmpxchg(&mb->mb_status, 0, 1) != 0) {
++ return -EEXIST; // We don't support two messages at once
++ }
++ reinit_completion(&mb->mb_completion);
++
++ pr_debug("bce_mailbox_send: %llx\n", msg);
++ regb = (u32*) ((u8*) mb->reg_mb + REG_MBOX_OUT_BASE);
++ iowrite32((u32) msg, regb);
++ iowrite32((u32) (msg >> 32), regb + 1);
++ iowrite32(0, regb + 2);
++ iowrite32(0, regb + 3);
++
++ wait_for_completion_timeout(&mb->mb_completion, msecs_to_jiffies(BCE_MBOX_TIMEOUT_MS));
++ if (atomic_read(&mb->mb_status) != 2) { // Didn't get the reply
++ atomic_set(&mb->mb_status, 0);
++ return -ETIMEDOUT;
++ }
++
++ *recv = mb->mb_result;
++ pr_debug("bce_mailbox_send: reply %llx\n", *recv);
++
++ atomic_set(&mb->mb_status, 0);
++ return 0;
++}
++
++static int bce_mailbox_retrive_response(struct bce_mailbox *mb)
++{
++ u32 __iomem *regb;
++ u32 lo, hi;
++ int count, counter;
++ u32 res = ioread32((u8*) mb->reg_mb + REG_MBOX_REPLY_COUNTER);
++ count = (res >> 20) & 0xf;
++ counter = count;
++ pr_debug("bce_mailbox_retrive_response count=%i\n", count);
++ while (counter--) {
++ regb = (u32*) ((u8*) mb->reg_mb + REG_MBOX_REPLY_BASE);
++ lo = ioread32(regb);
++ hi = ioread32(regb + 1);
++ ioread32(regb + 2);
++ ioread32(regb + 3);
++ pr_debug("bce_mailbox_retrive_response %llx\n", ((u64) hi << 32) | lo);
++ mb->mb_result = ((u64) hi << 32) | lo;
++ }
++ return count > 0 ? 0 : -ENODATA;
++}
++
++int bce_mailbox_handle_interrupt(struct bce_mailbox *mb)
++{
++ int status = bce_mailbox_retrive_response(mb);
++ if (!status) {
++ atomic_set(&mb->mb_status, 2);
++ complete(&mb->mb_completion);
++ }
++ return status;
++}
++
++static void bc_send_timestamp(struct timer_list *tl);
++
++void bce_timestamp_init(struct bce_timestamp *ts, void __iomem *reg)
++{
++ u32 __iomem *regb;
++
++ spin_lock_init(&ts->stop_sl);
++ ts->stopped = false;
++
++ ts->reg = reg;
++
++ regb = (u32*) ((u8*) ts->reg + REG_TIMESTAMP_BASE);
++
++ ioread32(regb);
++ mb();
++
++ timer_setup(&ts->timer, bc_send_timestamp, 0);
++}
++
++void bce_timestamp_start(struct bce_timestamp *ts, bool is_initial)
++{
++ unsigned long flags;
++ u32 __iomem *regb = (u32*) ((u8*) ts->reg + REG_TIMESTAMP_BASE);
++
++ if (is_initial) {
++ iowrite32((u32) -4, regb + 2);
++ iowrite32((u32) -1, regb);
++ } else {
++ iowrite32((u32) -3, regb + 2);
++ iowrite32((u32) -1, regb);
++ }
++
++ spin_lock_irqsave(&ts->stop_sl, flags);
++ ts->stopped = false;
++ spin_unlock_irqrestore(&ts->stop_sl, flags);
++ mod_timer(&ts->timer, jiffies + msecs_to_jiffies(150));
++}
++
++void bce_timestamp_stop(struct bce_timestamp *ts)
++{
++ unsigned long flags;
++ u32 __iomem *regb = (u32*) ((u8*) ts->reg + REG_TIMESTAMP_BASE);
++
++ spin_lock_irqsave(&ts->stop_sl, flags);
++ ts->stopped = true;
++ spin_unlock_irqrestore(&ts->stop_sl, flags);
++ del_timer_sync(&ts->timer);
++
++ iowrite32((u32) -2, regb + 2);
++ iowrite32((u32) -1, regb);
++}
++
++static void bc_send_timestamp(struct timer_list *tl)
++{
++ struct bce_timestamp *ts;
++ unsigned long flags;
++ u32 __iomem *regb;
++ ktime_t bt;
++
++ ts = container_of(tl, struct bce_timestamp, timer);
++ regb = (u32*) ((u8*) ts->reg + REG_TIMESTAMP_BASE);
++ local_irq_save(flags);
++ ioread32(regb + 2);
++ mb();
++ bt = ktime_get_boottime();
++ iowrite32((u32) bt, regb + 2);
++ iowrite32((u32) (bt >> 32), regb);
++
++ spin_lock(&ts->stop_sl);
++ if (!ts->stopped)
++ mod_timer(&ts->timer, jiffies + msecs_to_jiffies(150));
++ spin_unlock(&ts->stop_sl);
++ local_irq_restore(flags);
++}
+\ No newline at end of file
+diff --git a/drivers/staging/apple-bce/mailbox.h b/drivers/staging/apple-bce/mailbox.h
+new file mode 100644
+index 000000000..f3323f95b
+--- /dev/null
++++ b/drivers/staging/apple-bce/mailbox.h
+@@ -0,0 +1,53 @@
++#ifndef BCE_MAILBOX_H
++#define BCE_MAILBOX_H
++
++#include <linux/completion.h>
++#include <linux/pci.h>
++#include <linux/timer.h>
++
++struct bce_mailbox {
++ void __iomem *reg_mb;
++
++ atomic_t mb_status; // possible statuses: 0 (no msg), 1 (has active msg), 2 (got reply)
++ struct completion mb_completion;
++ uint64_t mb_result;
++};
++
++enum bce_message_type {
++ BCE_MB_REGISTER_COMMAND_SQ = 0x7, // to-device
++ BCE_MB_REGISTER_COMMAND_CQ = 0x8, // to-device
++ BCE_MB_REGISTER_COMMAND_QUEUE_REPLY = 0xB, // to-host
++ BCE_MB_SET_FW_PROTOCOL_VERSION = 0xC, // both
++ BCE_MB_SLEEP_NO_STATE = 0x14, // to-device
++ BCE_MB_RESTORE_NO_STATE = 0x15, // to-device
++ BCE_MB_SAVE_STATE_AND_SLEEP = 0x17, // to-device
++ BCE_MB_RESTORE_STATE_AND_WAKE = 0x18, // to-device
++ BCE_MB_SAVE_STATE_AND_SLEEP_FAILURE = 0x19, // from-device
++ BCE_MB_SAVE_RESTORE_STATE_COMPLETE = 0x1A, // from-device
++};
++
++#define BCE_MB_MSG(type, value) (((u64) (type) << 58) | ((value) & 0x3FFFFFFFFFFFFFFLL))
++#define BCE_MB_TYPE(v) ((u32) (v >> 58))
++#define BCE_MB_VALUE(v) (v & 0x3FFFFFFFFFFFFFFLL)
++
++void bce_mailbox_init(struct bce_mailbox *mb, void __iomem *reg_mb);
++
++int bce_mailbox_send(struct bce_mailbox *mb, u64 msg, u64* recv);
++
++int bce_mailbox_handle_interrupt(struct bce_mailbox *mb);
++
++
++struct bce_timestamp {
++ void __iomem *reg;
++ struct timer_list timer;
++ struct spinlock stop_sl;
++ bool stopped;
++};
++
++void bce_timestamp_init(struct bce_timestamp *ts, void __iomem *reg);
++
++void bce_timestamp_start(struct bce_timestamp *ts, bool is_initial);
++
++void bce_timestamp_stop(struct bce_timestamp *ts);
++
++#endif //BCEDRIVER_MAILBOX_H
+diff --git a/drivers/staging/apple-bce/queue.c b/drivers/staging/apple-bce/queue.c
+new file mode 100644
+index 000000000..bc9cd3bc6
+--- /dev/null
++++ b/drivers/staging/apple-bce/queue.c
+@@ -0,0 +1,390 @@
++#include "queue.h"
++#include "apple_bce.h"
++
++#define REG_DOORBELL_BASE 0x44000
++
++struct bce_queue_cq *bce_alloc_cq(struct apple_bce_device *dev, int qid, u32 el_count)
++{
++ struct bce_queue_cq *q;
++ q = kzalloc(sizeof(struct bce_queue_cq), GFP_KERNEL);
++ q->qid = qid;
++ q->type = BCE_QUEUE_CQ;
++ q->el_count = el_count;
++ q->data = dma_alloc_coherent(&dev->pci->dev, el_count * sizeof(struct bce_qe_completion),
++ &q->dma_handle, GFP_KERNEL);
++ if (!q->data) {
++ pr_err("DMA queue memory alloc failed\n");
++ kfree(q);
++ return NULL;
++ }
++ return q;
++}
++
++void bce_get_cq_memcfg(struct bce_queue_cq *cq, struct bce_queue_memcfg *cfg)
++{
++ cfg->qid = (u16) cq->qid;
++ cfg->el_count = (u16) cq->el_count;
++ cfg->vector_or_cq = 0;
++ cfg->_pad = 0;
++ cfg->addr = cq->dma_handle;
++ cfg->length = cq->el_count * sizeof(struct bce_qe_completion);
++}
++
++void bce_free_cq(struct apple_bce_device *dev, struct bce_queue_cq *cq)
++{
++ dma_free_coherent(&dev->pci->dev, cq->el_count * sizeof(struct bce_qe_completion), cq->data, cq->dma_handle);
++ kfree(cq);
++}
++
++static void bce_handle_cq_completion(struct apple_bce_device *dev, struct bce_qe_completion *e, size_t *ce)
++{
++ struct bce_queue *target;
++ struct bce_queue_sq *target_sq;
++ struct bce_sq_completion_data *cmpl;
++ if (e->qid >= BCE_MAX_QUEUE_COUNT) {
++ pr_err("Device sent a response for qid (%u) >= BCE_MAX_QUEUE_COUNT\n", e->qid);
++ return;
++ }
++ target = dev->queues[e->qid];
++ if (!target || target->type != BCE_QUEUE_SQ) {
++ pr_err("Device sent a response for qid (%u), which does not exist\n", e->qid);
++ return;
++ }
++ target_sq = (struct bce_queue_sq *) target;
++ if (target_sq->completion_tail != e->completion_index) {
++ pr_err("Completion index mismatch; this is likely going to make this driver unusable\n");
++ return;
++ }
++ if (!target_sq->has_pending_completions) {
++ target_sq->has_pending_completions = true;
++ dev->int_sq_list[(*ce)++] = target_sq;
++ }
++ cmpl = &target_sq->completion_data[e->completion_index];
++ cmpl->status = e->status;
++ cmpl->data_size = e->data_size;
++ cmpl->result = e->result;
++ wmb();
++ target_sq->completion_tail = (target_sq->completion_tail + 1) % target_sq->el_count;
++}
++
++void bce_handle_cq_completions(struct apple_bce_device *dev, struct bce_queue_cq *cq)
++{
++ size_t ce = 0;
++ struct bce_qe_completion *e;
++ struct bce_queue_sq *sq;
++ e = bce_cq_element(cq, cq->index);
++ if (!(e->flags & BCE_COMPLETION_FLAG_PENDING))
++ return;
++ mb();
++ while (true) {
++ e = bce_cq_element(cq, cq->index);
++ if (!(e->flags & BCE_COMPLETION_FLAG_PENDING))
++ break;
++ // pr_info("apple-bce: compl: %i: %i %llx %llx", e->qid, e->status, e->data_size, e->result);
++ bce_handle_cq_completion(dev, e, &ce);
++ e->flags = 0;
++ cq->index = (cq->index + 1) % cq->el_count;
++ }
++ mb();
++ iowrite32(cq->index, (u32 *) ((u8 *) dev->reg_mem_dma + REG_DOORBELL_BASE) + cq->qid);
++ while (ce) {
++ --ce;
++ sq = dev->int_sq_list[ce];
++ sq->completion(sq);
++ sq->has_pending_completions = false;
++ }
++}
++
++
++struct bce_queue_sq *bce_alloc_sq(struct apple_bce_device *dev, int qid, u32 el_size, u32 el_count,
++ bce_sq_completion compl, void *userdata)
++{
++ struct bce_queue_sq *q;
++ q = kzalloc(sizeof(struct bce_queue_sq), GFP_KERNEL);
++ q->qid = qid;
++ q->type = BCE_QUEUE_SQ;
++ q->el_size = el_size;
++ q->el_count = el_count;
++ q->data = dma_alloc_coherent(&dev->pci->dev, el_count * el_size,
++ &q->dma_handle, GFP_KERNEL);
++ q->completion = compl;
++ q->userdata = userdata;
++ q->completion_data = kzalloc(sizeof(struct bce_sq_completion_data) * el_count, GFP_KERNEL);
++ q->reg_mem_dma = dev->reg_mem_dma;
++ atomic_set(&q->available_commands, el_count - 1);
++ init_completion(&q->available_command_completion);
++ atomic_set(&q->available_command_completion_waiting_count, 0);
++ if (!q->data) {
++ pr_err("DMA queue memory alloc failed\n");
++ kfree(q);
++ return NULL;
++ }
++ return q;
++}
++
++void bce_get_sq_memcfg(struct bce_queue_sq *sq, struct bce_queue_cq *cq, struct bce_queue_memcfg *cfg)
++{
++ cfg->qid = (u16) sq->qid;
++ cfg->el_count = (u16) sq->el_count;
++ cfg->vector_or_cq = (u16) cq->qid;
++ cfg->_pad = 0;
++ cfg->addr = sq->dma_handle;
++ cfg->length = sq->el_count * sq->el_size;
++}
++
++void bce_free_sq(struct apple_bce_device *dev, struct bce_queue_sq *sq)
++{
++ dma_free_coherent(&dev->pci->dev, sq->el_count * sq->el_size, sq->data, sq->dma_handle);
++ kfree(sq);
++}
++
++int bce_reserve_submission(struct bce_queue_sq *sq, unsigned long *timeout)
++{
++ while (atomic_dec_if_positive(&sq->available_commands) < 0) {
++ if (!timeout || !*timeout)
++ return -EAGAIN;
++ atomic_inc(&sq->available_command_completion_waiting_count);
++ *timeout = wait_for_completion_timeout(&sq->available_command_completion, *timeout);
++ if (!*timeout) {
++ if (atomic_dec_if_positive(&sq->available_command_completion_waiting_count) < 0)
++ try_wait_for_completion(&sq->available_command_completion); /* consume the pending completion */
++ }
++ }
++ return 0;
++}
++
++void bce_cancel_submission_reservation(struct bce_queue_sq *sq)
++{
++ atomic_inc(&sq->available_commands);
++}
++
++void *bce_next_submission(struct bce_queue_sq *sq)
++{
++ void *ret = bce_sq_element(sq, sq->tail);
++ sq->tail = (sq->tail + 1) % sq->el_count;
++ return ret;
++}
++
++void bce_submit_to_device(struct bce_queue_sq *sq)
++{
++ mb();
++ iowrite32(sq->tail, (u32 *) ((u8 *) sq->reg_mem_dma + REG_DOORBELL_BASE) + sq->qid);
++}
++
++void bce_notify_submission_complete(struct bce_queue_sq *sq)
++{
++ sq->head = (sq->head + 1) % sq->el_count;
++ atomic_inc(&sq->available_commands);
++ if (atomic_dec_if_positive(&sq->available_command_completion_waiting_count) >= 0) {
++ complete(&sq->available_command_completion);
++ }
++}
++
++void bce_set_submission_single(struct bce_qe_submission *element, dma_addr_t addr, size_t size)
++{
++ element->addr = addr;
++ element->length = size;
++ element->segl_addr = element->segl_length = 0;
++}
++
++static void bce_cmdq_completion(struct bce_queue_sq *q);
++
++struct bce_queue_cmdq *bce_alloc_cmdq(struct apple_bce_device *dev, int qid, u32 el_count)
++{
++ struct bce_queue_cmdq *q;
++ q = kzalloc(sizeof(struct bce_queue_cmdq), GFP_KERNEL);
++ q->sq = bce_alloc_sq(dev, qid, BCE_CMD_SIZE, el_count, bce_cmdq_completion, q);
++ if (!q->sq) {
++ kfree(q);
++ return NULL;
++ }
++ spin_lock_init(&q->lck);
++ q->tres = kzalloc(sizeof(struct bce_queue_cmdq_result_el*) * el_count, GFP_KERNEL);
++ if (!q->tres) {
++ kfree(q);
++ return NULL;
++ }
++ return q;
++}
++
++void bce_free_cmdq(struct apple_bce_device *dev, struct bce_queue_cmdq *cmdq)
++{
++ bce_free_sq(dev, cmdq->sq);
++ kfree(cmdq->tres);
++ kfree(cmdq);
++}
++
++void bce_cmdq_completion(struct bce_queue_sq *q)
++{
++ struct bce_queue_cmdq_result_el *el;
++ struct bce_queue_cmdq *cmdq = q->userdata;
++ struct bce_sq_completion_data *result;
++
++ spin_lock(&cmdq->lck);
++ while ((result = bce_next_completion(q))) {
++ el = cmdq->tres[cmdq->sq->head];
++ if (el) {
++ el->result = result->result;
++ el->status = result->status;
++ mb();
++ complete(&el->cmpl);
++ } else {
++ pr_err("apple-bce: Unexpected command queue completion\n");
++ }
++ cmdq->tres[cmdq->sq->head] = NULL;
++ bce_notify_submission_complete(q);
++ }
++ spin_unlock(&cmdq->lck);
++}
++
++static __always_inline void *bce_cmd_start(struct bce_queue_cmdq *cmdq, struct bce_queue_cmdq_result_el *res)
++{
++ void *ret;
++ unsigned long timeout;
++ init_completion(&res->cmpl);
++ mb();
++
++ timeout = msecs_to_jiffies(1000L * 60 * 5); /* wait for up to ~5 minutes */
++ if (bce_reserve_submission(cmdq->sq, &timeout))
++ return NULL;
++
++ spin_lock(&cmdq->lck);
++ cmdq->tres[cmdq->sq->tail] = res;
++ ret = bce_next_submission(cmdq->sq);
++ return ret;
++}
++
++static __always_inline void bce_cmd_finish(struct bce_queue_cmdq *cmdq, struct bce_queue_cmdq_result_el *res)
++{
++ bce_submit_to_device(cmdq->sq);
++ spin_unlock(&cmdq->lck);
++
++ wait_for_completion(&res->cmpl);
++ mb();
++}
++
++u32 bce_cmd_register_queue(struct bce_queue_cmdq *cmdq, struct bce_queue_memcfg *cfg, const char *name, bool isdirout)
++{
++ struct bce_queue_cmdq_result_el res;
++ struct bce_cmdq_register_memory_queue_cmd *cmd = bce_cmd_start(cmdq, &res);
++ if (!cmd)
++ return (u32) -1;
++ cmd->cmd = BCE_CMD_REGISTER_MEMORY_QUEUE;
++ cmd->flags = (u16) ((name ? 2 : 0) | (isdirout ? 1 : 0));
++ cmd->qid = cfg->qid;
++ cmd->el_count = cfg->el_count;
++ cmd->vector_or_cq = cfg->vector_or_cq;
++ memset(cmd->name, 0, sizeof(cmd->name));
++ if (name) {
++ cmd->name_len = (u16) min(strlen(name), (size_t) sizeof(cmd->name));
++ memcpy(cmd->name, name, cmd->name_len);
++ } else {
++ cmd->name_len = 0;
++ }
++ cmd->addr = cfg->addr;
++ cmd->length = cfg->length;
++
++ bce_cmd_finish(cmdq, &res);
++ return res.status;
++}
++
++u32 bce_cmd_unregister_memory_queue(struct bce_queue_cmdq *cmdq, u16 qid)
++{
++ struct bce_queue_cmdq_result_el res;
++ struct bce_cmdq_simple_memory_queue_cmd *cmd = bce_cmd_start(cmdq, &res);
++ if (!cmd)
++ return (u32) -1;
++ cmd->cmd = BCE_CMD_UNREGISTER_MEMORY_QUEUE;
++ cmd->flags = 0;
++ cmd->qid = qid;
++ bce_cmd_finish(cmdq, &res);
++ return res.status;
++}
++
++u32 bce_cmd_flush_memory_queue(struct bce_queue_cmdq *cmdq, u16 qid)
++{
++ struct bce_queue_cmdq_result_el res;
++ struct bce_cmdq_simple_memory_queue_cmd *cmd = bce_cmd_start(cmdq, &res);
++ if (!cmd)
++ return (u32) -1;
++ cmd->cmd = BCE_CMD_FLUSH_MEMORY_QUEUE;
++ cmd->flags = 0;
++ cmd->qid = qid;
++ bce_cmd_finish(cmdq, &res);
++ return res.status;
++}
++
++
++struct bce_queue_cq *bce_create_cq(struct apple_bce_device *dev, u32 el_count)
++{
++ struct bce_queue_cq *cq;
++ struct bce_queue_memcfg cfg;
++ int qid = ida_simple_get(&dev->queue_ida, BCE_QUEUE_USER_MIN, BCE_QUEUE_USER_MAX, GFP_KERNEL);
++ if (qid < 0)
++ return NULL;
++ cq = bce_alloc_cq(dev, qid, el_count);
++ if (!cq)
++ return NULL;
++ bce_get_cq_memcfg(cq, &cfg);
++ if (bce_cmd_register_queue(dev->cmd_cmdq, &cfg, NULL, false) != 0) {
++ pr_err("apple-bce: CQ registration failed (%i)", qid);
++ bce_free_cq(dev, cq);
++ ida_simple_remove(&dev->queue_ida, (uint) qid);
++ return NULL;
++ }
++ dev->queues[qid] = (struct bce_queue *) cq;
++ return cq;
++}
++
++struct bce_queue_sq *bce_create_sq(struct apple_bce_device *dev, struct bce_queue_cq *cq, const char *name, u32 el_count,
++ int direction, bce_sq_completion compl, void *userdata)
++{
++ struct bce_queue_sq *sq;
++ struct bce_queue_memcfg cfg;
++ int qid;
++ if (cq == NULL)
++ return NULL; /* cq can not be null */
++ if (name == NULL)
++ return NULL; /* name can not be null */
++ if (direction != DMA_TO_DEVICE && direction != DMA_FROM_DEVICE)
++ return NULL; /* unsupported direction */
++ qid = ida_simple_get(&dev->queue_ida, BCE_QUEUE_USER_MIN, BCE_QUEUE_USER_MAX, GFP_KERNEL);
++ if (qid < 0)
++ return NULL;
++ sq = bce_alloc_sq(dev, qid, sizeof(struct bce_qe_submission), el_count, compl, userdata);
++ if (!sq)
++ return NULL;
++ bce_get_sq_memcfg(sq, cq, &cfg);
++ if (bce_cmd_register_queue(dev->cmd_cmdq, &cfg, name, direction != DMA_FROM_DEVICE) != 0) {
++ pr_err("apple-bce: SQ registration failed (%i)", qid);
++ bce_free_sq(dev, sq);
++ ida_simple_remove(&dev->queue_ida, (uint) qid);
++ return NULL;
++ }
++ spin_lock(&dev->queues_lock);
++ dev->queues[qid] = (struct bce_queue *) sq;
++ spin_unlock(&dev->queues_lock);
++ return sq;
++}
++
++void bce_destroy_cq(struct apple_bce_device *dev, struct bce_queue_cq *cq)
++{
++ if (!dev->is_being_removed && bce_cmd_unregister_memory_queue(dev->cmd_cmdq, (u16) cq->qid))
++ pr_err("apple-bce: CQ unregister failed");
++ spin_lock(&dev->queues_lock);
++ dev->queues[cq->qid] = NULL;
++ spin_unlock(&dev->queues_lock);
++ ida_simple_remove(&dev->queue_ida, (uint) cq->qid);
++ bce_free_cq(dev, cq);
++}
++
++void bce_destroy_sq(struct apple_bce_device *dev, struct bce_queue_sq *sq)
++{
++ if (!dev->is_being_removed && bce_cmd_unregister_memory_queue(dev->cmd_cmdq, (u16) sq->qid))
++ pr_err("apple-bce: CQ unregister failed");
++ spin_lock(&dev->queues_lock);
++ dev->queues[sq->qid] = NULL;
++ spin_unlock(&dev->queues_lock);
++ ida_simple_remove(&dev->queue_ida, (uint) sq->qid);
++ bce_free_sq(dev, sq);
++}
+\ No newline at end of file
+diff --git a/drivers/staging/apple-bce/queue.h b/drivers/staging/apple-bce/queue.h
+new file mode 100644
+index 000000000..8368ac5df
+--- /dev/null
++++ b/drivers/staging/apple-bce/queue.h
+@@ -0,0 +1,177 @@
++#ifndef BCE_QUEUE_H
++#define BCE_QUEUE_H
++
++#include <linux/completion.h>
++#include <linux/pci.h>
++
++#define BCE_CMD_SIZE 0x40
++
++struct apple_bce_device;
++
++enum bce_queue_type {
++ BCE_QUEUE_CQ, BCE_QUEUE_SQ
++};
++struct bce_queue {
++ int qid;
++ int type;
++};
++struct bce_queue_cq {
++ int qid;
++ int type;
++ u32 el_count;
++ dma_addr_t dma_handle;
++ void *data;
++
++ u32 index;
++};
++struct bce_queue_sq;
++typedef void (*bce_sq_completion)(struct bce_queue_sq *q);
++struct bce_sq_completion_data {
++ u32 status;
++ u64 data_size;
++ u64 result;
++};
++struct bce_queue_sq {
++ int qid;
++ int type;
++ u32 el_size;
++ u32 el_count;
++ dma_addr_t dma_handle;
++ void *data;
++ void *userdata;
++ void __iomem *reg_mem_dma;
++
++ atomic_t available_commands;
++ struct completion available_command_completion;
++ atomic_t available_command_completion_waiting_count;
++ u32 head, tail;
++
++ u32 completion_cidx, completion_tail;
++ struct bce_sq_completion_data *completion_data;
++ bool has_pending_completions;
++ bce_sq_completion completion;
++};
++
++struct bce_queue_cmdq_result_el {
++ struct completion cmpl;
++ u32 status;
++ u64 result;
++};
++struct bce_queue_cmdq {
++ struct bce_queue_sq *sq;
++ struct spinlock lck;
++ struct bce_queue_cmdq_result_el **tres;
++};
++
++struct bce_queue_memcfg {
++ u16 qid;
++ u16 el_count;
++ u16 vector_or_cq;
++ u16 _pad;
++ u64 addr;
++ u64 length;
++};
++
++enum bce_qe_completion_status {
++ BCE_COMPLETION_SUCCESS = 0,
++ BCE_COMPLETION_ERROR = 1,
++ BCE_COMPLETION_ABORTED = 2,
++ BCE_COMPLETION_NO_SPACE = 3,
++ BCE_COMPLETION_OVERRUN = 4
++};
++enum bce_qe_completion_flags {
++ BCE_COMPLETION_FLAG_PENDING = 0x8000
++};
++struct bce_qe_completion {
++ u64 result;
++ u64 data_size;
++ u16 qid;
++ u16 completion_index;
++ u16 status; // bce_qe_completion_status
++ u16 flags; // bce_qe_completion_flags
++};
++
++struct bce_qe_submission {
++ u64 length;
++ u64 addr;
++
++ u64 segl_addr;
++ u64 segl_length;
++};
++
++enum bce_cmdq_command {
++ BCE_CMD_REGISTER_MEMORY_QUEUE = 0x20,
++ BCE_CMD_UNREGISTER_MEMORY_QUEUE = 0x30,
++ BCE_CMD_FLUSH_MEMORY_QUEUE = 0x40,
++ BCE_CMD_SET_MEMORY_QUEUE_PROPERTY = 0x50
++};
++struct bce_cmdq_simple_memory_queue_cmd {
++ u16 cmd; // bce_cmdq_command
++ u16 flags;
++ u16 qid;
++};
++struct bce_cmdq_register_memory_queue_cmd {
++ u16 cmd; // bce_cmdq_command
++ u16 flags;
++ u16 qid;
++ u16 _pad;
++ u16 el_count;
++ u16 vector_or_cq;
++ u16 _pad2;
++ u16 name_len;
++ char name[0x20];
++ u64 addr;
++ u64 length;
++};
++
++static __always_inline void *bce_sq_element(struct bce_queue_sq *q, int i) {
++ return (void *) ((u8 *) q->data + q->el_size * i);
++}
++static __always_inline void *bce_cq_element(struct bce_queue_cq *q, int i) {
++ return (void *) ((struct bce_qe_completion *) q->data + i);
++}
++
++static __always_inline struct bce_sq_completion_data *bce_next_completion(struct bce_queue_sq *sq) {
++ struct bce_sq_completion_data *res;
++ rmb();
++ if (sq->completion_cidx == sq->completion_tail)
++ return NULL;
++ res = &sq->completion_data[sq->completion_cidx];
++ sq->completion_cidx = (sq->completion_cidx + 1) % sq->el_count;
++ return res;
++}
++
++struct bce_queue_cq *bce_alloc_cq(struct apple_bce_device *dev, int qid, u32 el_count);
++void bce_get_cq_memcfg(struct bce_queue_cq *cq, struct bce_queue_memcfg *cfg);
++void bce_free_cq(struct apple_bce_device *dev, struct bce_queue_cq *cq);
++void bce_handle_cq_completions(struct apple_bce_device *dev, struct bce_queue_cq *cq);
++
++struct bce_queue_sq *bce_alloc_sq(struct apple_bce_device *dev, int qid, u32 el_size, u32 el_count,
++ bce_sq_completion compl, void *userdata);
++void bce_get_sq_memcfg(struct bce_queue_sq *sq, struct bce_queue_cq *cq, struct bce_queue_memcfg *cfg);
++void bce_free_sq(struct apple_bce_device *dev, struct bce_queue_sq *sq);
++int bce_reserve_submission(struct bce_queue_sq *sq, unsigned long *timeout);
++void bce_cancel_submission_reservation(struct bce_queue_sq *sq);
++void *bce_next_submission(struct bce_queue_sq *sq);
++void bce_submit_to_device(struct bce_queue_sq *sq);
++void bce_notify_submission_complete(struct bce_queue_sq *sq);
++
++void bce_set_submission_single(struct bce_qe_submission *element, dma_addr_t addr, size_t size);
++
++struct bce_queue_cmdq *bce_alloc_cmdq(struct apple_bce_device *dev, int qid, u32 el_count);
++void bce_free_cmdq(struct apple_bce_device *dev, struct bce_queue_cmdq *cmdq);
++
++u32 bce_cmd_register_queue(struct bce_queue_cmdq *cmdq, struct bce_queue_memcfg *cfg, const char *name, bool isdirout);
++u32 bce_cmd_unregister_memory_queue(struct bce_queue_cmdq *cmdq, u16 qid);
++u32 bce_cmd_flush_memory_queue(struct bce_queue_cmdq *cmdq, u16 qid);
++
++
++/* User API - Creates and registers the queue */
++
++struct bce_queue_cq *bce_create_cq(struct apple_bce_device *dev, u32 el_count);
++struct bce_queue_sq *bce_create_sq(struct apple_bce_device *dev, struct bce_queue_cq *cq, const char *name, u32 el_count,
++ int direction, bce_sq_completion compl, void *userdata);
++void bce_destroy_cq(struct apple_bce_device *dev, struct bce_queue_cq *cq);
++void bce_destroy_sq(struct apple_bce_device *dev, struct bce_queue_sq *sq);
++
++#endif //BCEDRIVER_MAILBOX_H
+diff --git a/drivers/staging/apple-bce/queue_dma.c b/drivers/staging/apple-bce/queue_dma.c
+new file mode 100644
+index 000000000..b23661328
+--- /dev/null
++++ b/drivers/staging/apple-bce/queue_dma.c
+@@ -0,0 +1,220 @@
++#include "queue_dma.h"
++#include <linux/vmalloc.h>
++#include <linux/mm.h>
++#include "queue.h"
++
++static int bce_alloc_scatterlist_from_vm(struct sg_table *tbl, void *data, size_t len);
++static struct bce_segment_list_element_hostinfo *bce_map_segment_list(
++ struct device *dev, struct scatterlist *pages, int pagen);
++static void bce_unmap_segement_list(struct device *dev, struct bce_segment_list_element_hostinfo *list);
++
++int bce_map_dma_buffer(struct device *dev, struct bce_dma_buffer *buf, struct sg_table scatterlist,
++ enum dma_data_direction dir)
++{
++ int cnt;
++
++ buf->direction = dir;
++ buf->scatterlist = scatterlist;
++ buf->seglist_hostinfo = NULL;
++
++ cnt = dma_map_sg(dev, buf->scatterlist.sgl, buf->scatterlist.nents, dir);
++ if (cnt != buf->scatterlist.nents) {
++ pr_err("apple-bce: DMA scatter list mapping returned an unexpected count: %i\n", cnt);
++ dma_unmap_sg(dev, buf->scatterlist.sgl, buf->scatterlist.nents, dir);
++ return -EIO;
++ }
++ if (cnt == 1)
++ return 0;
++
++ buf->seglist_hostinfo = bce_map_segment_list(dev, buf->scatterlist.sgl, buf->scatterlist.nents);
++ if (!buf->seglist_hostinfo) {
++ pr_err("apple-bce: Creating segment list failed\n");
++ dma_unmap_sg(dev, buf->scatterlist.sgl, buf->scatterlist.nents, dir);
++ return -EIO;
++ }
++ return 0;
++}
++
++int bce_map_dma_buffer_vm(struct device *dev, struct bce_dma_buffer *buf, void *data, size_t len,
++ enum dma_data_direction dir)
++{
++ int status;
++ struct sg_table scatterlist;
++ if ((status = bce_alloc_scatterlist_from_vm(&scatterlist, data, len)))
++ return status;
++ if ((status = bce_map_dma_buffer(dev, buf, scatterlist, dir))) {
++ sg_free_table(&scatterlist);
++ return status;
++ }
++ return 0;
++}
++
++int bce_map_dma_buffer_km(struct device *dev, struct bce_dma_buffer *buf, void *data, size_t len,
++ enum dma_data_direction dir)
++{
++ /* Kernel memory is continuous which is great for us. */
++ int status;
++ struct sg_table scatterlist;
++ if ((status = sg_alloc_table(&scatterlist, 1, GFP_KERNEL))) {
++ sg_free_table(&scatterlist);
++ return status;
++ }
++ sg_set_buf(scatterlist.sgl, data, (uint) len);
++ if ((status = bce_map_dma_buffer(dev, buf, scatterlist, dir))) {
++ sg_free_table(&scatterlist);
++ return status;
++ }
++ return 0;
++}
++
++void bce_unmap_dma_buffer(struct device *dev, struct bce_dma_buffer *buf)
++{
++ dma_unmap_sg(dev, buf->scatterlist.sgl, buf->scatterlist.nents, buf->direction);
++ bce_unmap_segement_list(dev, buf->seglist_hostinfo);
++}
++
++
++static int bce_alloc_scatterlist_from_vm(struct sg_table *tbl, void *data, size_t len)
++{
++ int status, i;
++ struct page **pages;
++ size_t off, start_page, end_page, page_count;
++ off = (size_t) data % PAGE_SIZE;
++ start_page = (size_t) data / PAGE_SIZE;
++ end_page = ((size_t) data + len - 1) / PAGE_SIZE;
++ page_count = end_page - start_page + 1;
++
++ if (page_count > PAGE_SIZE / sizeof(struct page *))
++ pages = vmalloc(page_count * sizeof(struct page *));
++ else
++ pages = kmalloc(page_count * sizeof(struct page *), GFP_KERNEL);
++
++ for (i = 0; i < page_count; i++)
++ pages[i] = vmalloc_to_page((void *) ((start_page + i) * PAGE_SIZE));
++
++ if ((status = sg_alloc_table_from_pages(tbl, pages, page_count, (unsigned int) off, len, GFP_KERNEL))) {
++ sg_free_table(tbl);
++ }
++
++ if (page_count > PAGE_SIZE / sizeof(struct page *))
++ vfree(pages);
++ else
++ kfree(pages);
++ return status;
++}
++
++#define BCE_ELEMENTS_PER_PAGE ((PAGE_SIZE - sizeof(struct bce_segment_list_header)) \
++ / sizeof(struct bce_segment_list_element))
++#define BCE_ELEMENTS_PER_ADDITIONAL_PAGE (PAGE_SIZE / sizeof(struct bce_segment_list_element))
++
++static struct bce_segment_list_element_hostinfo *bce_map_segment_list(
++ struct device *dev, struct scatterlist *pages, int pagen)
++{
++ size_t ptr, pptr = 0;
++ struct bce_segment_list_header theader; /* a temp header, to store the initial seg */
++ struct bce_segment_list_header *header;
++ struct bce_segment_list_element *el, *el_end;
++ struct bce_segment_list_element_hostinfo *out, *pout, *out_root;
++ struct scatterlist *sg;
++ int i;
++ header = &theader;
++ out = out_root = NULL;
++ el = el_end = NULL;
++ for_each_sg(pages, sg, pagen, i) {
++ if (el >= el_end) {
++ /* allocate a new page, this will be also done for the first element */
++ ptr = __get_free_page(GFP_KERNEL);
++ if (pptr && ptr == pptr + PAGE_SIZE) {
++ out->page_count++;
++ header->element_count += BCE_ELEMENTS_PER_ADDITIONAL_PAGE;
++ el_end += BCE_ELEMENTS_PER_ADDITIONAL_PAGE;
++ } else {
++ header = (void *) ptr;
++ header->element_count = BCE_ELEMENTS_PER_PAGE;
++ header->data_size = 0;
++ header->next_segl_addr = 0;
++ header->next_segl_length = 0;
++ el = (void *) (header + 1);
++ el_end = el + BCE_ELEMENTS_PER_PAGE;
++
++ if (out) {
++ out->next = kmalloc(sizeof(struct bce_segment_list_element_hostinfo), GFP_KERNEL);
++ out = out->next;
++ } else {
++ out_root = out = kmalloc(sizeof(struct bce_segment_list_element_hostinfo), GFP_KERNEL);
++ }
++ out->page_start = (void *) ptr;
++ out->page_count = 1;
++ out->dma_start = DMA_MAPPING_ERROR;
++ out->next = NULL;
++ }
++ pptr = ptr;
++ }
++ el->addr = sg->dma_address;
++ el->length = sg->length;
++ header->data_size += el->length;
++ }
++
++ /* DMA map */
++ out = out_root;
++ pout = NULL;
++ while (out) {
++ out->dma_start = dma_map_single(dev, out->page_start, out->page_count * PAGE_SIZE, DMA_TO_DEVICE);
++ if (dma_mapping_error(dev, out->dma_start))
++ goto error;
++ if (pout) {
++ header = pout->page_start;
++ header->next_segl_addr = out->dma_start;
++ header->next_segl_length = out->page_count * PAGE_SIZE;
++ }
++ pout = out;
++ out = out->next;
++ }
++ return out_root;
++
++ error:
++ bce_unmap_segement_list(dev, out_root);
++ return NULL;
++}
++
++static void bce_unmap_segement_list(struct device *dev, struct bce_segment_list_element_hostinfo *list)
++{
++ struct bce_segment_list_element_hostinfo *next;
++ while (list) {
++ if (list->dma_start != DMA_MAPPING_ERROR)
++ dma_unmap_single(dev, list->dma_start, list->page_count * PAGE_SIZE, DMA_TO_DEVICE);
++ next = list->next;
++ kfree(list);
++ list = next;
++ }
++}
++
++int bce_set_submission_buf(struct bce_qe_submission *element, struct bce_dma_buffer *buf, size_t offset, size_t length)
++{
++ struct bce_segment_list_element_hostinfo *seg;
++ struct bce_segment_list_header *seg_header;
++
++ seg = buf->seglist_hostinfo;
++ if (!seg) {
++ element->addr = buf->scatterlist.sgl->dma_address + offset;
++ element->length = length;
++ element->segl_addr = 0;
++ element->segl_length = 0;
++ return 0;
++ }
++
++ while (seg) {
++ seg_header = seg->page_start;
++ if (offset <= seg_header->data_size)
++ break;
++ offset -= seg_header->data_size;
++ seg = seg->next;
++ }
++ if (!seg)
++ return -EINVAL;
++ element->addr = offset;
++ element->length = buf->scatterlist.sgl->dma_length;
++ element->segl_addr = seg->dma_start;
++ element->segl_length = seg->page_count * PAGE_SIZE;
++ return 0;
++}
+\ No newline at end of file
+diff --git a/drivers/staging/apple-bce/queue_dma.h b/drivers/staging/apple-bce/queue_dma.h
+new file mode 100644
+index 000000000..f8a57e50e
+--- /dev/null
++++ b/drivers/staging/apple-bce/queue_dma.h
+@@ -0,0 +1,50 @@
++#ifndef BCE_QUEUE_DMA_H
++#define BCE_QUEUE_DMA_H
++
++#include <linux/pci.h>
++
++struct bce_qe_submission;
++
++struct bce_segment_list_header {
++ u64 element_count;
++ u64 data_size;
++
++ u64 next_segl_addr;
++ u64 next_segl_length;
++};
++struct bce_segment_list_element {
++ u64 addr;
++ u64 length;
++};
++
++struct bce_segment_list_element_hostinfo {
++ struct bce_segment_list_element_hostinfo *next;
++ void *page_start;
++ size_t page_count;
++ dma_addr_t dma_start;
++};
++
++
++struct bce_dma_buffer {
++ enum dma_data_direction direction;
++ struct sg_table scatterlist;
++ struct bce_segment_list_element_hostinfo *seglist_hostinfo;
++};
++
++/* NOTE: Takes ownership of the sg_table if it succeeds. Ownership is not transferred on failure. */
++int bce_map_dma_buffer(struct device *dev, struct bce_dma_buffer *buf, struct sg_table scatterlist,
++ enum dma_data_direction dir);
++
++/* Creates a buffer from virtual memory (vmalloc) */
++int bce_map_dma_buffer_vm(struct device *dev, struct bce_dma_buffer *buf, void *data, size_t len,
++ enum dma_data_direction dir);
++
++/* Creates a buffer from kernel memory (kmalloc) */
++int bce_map_dma_buffer_km(struct device *dev, struct bce_dma_buffer *buf, void *data, size_t len,
++ enum dma_data_direction dir);
++
++void bce_unmap_dma_buffer(struct device *dev, struct bce_dma_buffer *buf);
++
++int bce_set_submission_buf(struct bce_qe_submission *element, struct bce_dma_buffer *buf, size_t offset, size_t length);
++
++#endif //BCE_QUEUE_DMA_H
+diff --git a/drivers/staging/apple-bce/vhci/command.h b/drivers/staging/apple-bce/vhci/command.h
+new file mode 100644
+index 000000000..26619e0bc
+--- /dev/null
++++ b/drivers/staging/apple-bce/vhci/command.h
+@@ -0,0 +1,204 @@
++#ifndef BCE_VHCI_COMMAND_H
++#define BCE_VHCI_COMMAND_H
++
++#include "queue.h"
++#include <linux/jiffies.h>
++#include <linux/usb.h>
++
++#define BCE_VHCI_CMD_TIMEOUT_SHORT msecs_to_jiffies(2000)
++#define BCE_VHCI_CMD_TIMEOUT_LONG msecs_to_jiffies(30000)
++
++#define BCE_VHCI_BULK_MAX_ACTIVE_URBS_POW2 2
++#define BCE_VHCI_BULK_MAX_ACTIVE_URBS (1 << BCE_VHCI_BULK_MAX_ACTIVE_URBS_POW2)
++
++typedef u8 bce_vhci_port_t;
++typedef u8 bce_vhci_device_t;
++
++enum bce_vhci_command {
++ BCE_VHCI_CMD_CONTROLLER_ENABLE = 1,
++ BCE_VHCI_CMD_CONTROLLER_DISABLE = 2,
++ BCE_VHCI_CMD_CONTROLLER_START = 3,
++ BCE_VHCI_CMD_CONTROLLER_PAUSE = 4,
++
++ BCE_VHCI_CMD_PORT_POWER_ON = 0x10,
++ BCE_VHCI_CMD_PORT_POWER_OFF = 0x11,
++ BCE_VHCI_CMD_PORT_RESUME = 0x12,
++ BCE_VHCI_CMD_PORT_SUSPEND = 0x13,
++ BCE_VHCI_CMD_PORT_RESET = 0x14,
++ BCE_VHCI_CMD_PORT_DISABLE = 0x15,
++ BCE_VHCI_CMD_PORT_STATUS = 0x16,
++
++ BCE_VHCI_CMD_DEVICE_CREATE = 0x30,
++ BCE_VHCI_CMD_DEVICE_DESTROY = 0x31,
++
++ BCE_VHCI_CMD_ENDPOINT_CREATE = 0x40,
++ BCE_VHCI_CMD_ENDPOINT_DESTROY = 0x41,
++ BCE_VHCI_CMD_ENDPOINT_SET_STATE = 0x42,
++ BCE_VHCI_CMD_ENDPOINT_RESET = 0x44,
++
++ /* Device to host only */
++ BCE_VHCI_CMD_ENDPOINT_REQUEST_STATE = 0x43,
++ BCE_VHCI_CMD_TRANSFER_REQUEST = 0x1000,
++ BCE_VHCI_CMD_CONTROL_TRANSFER_STATUS = 0x1005
++};
++
++enum bce_vhci_endpoint_state {
++ BCE_VHCI_ENDPOINT_ACTIVE = 0,
++ BCE_VHCI_ENDPOINT_PAUSED = 1,
++ BCE_VHCI_ENDPOINT_STALLED = 2
++};
++
++static inline int bce_vhci_cmd_controller_enable(struct bce_vhci_command_queue *q, u8 busNum, u16 *portMask)
++{
++ int status;
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_CONTROLLER_ENABLE;
++ cmd.param1 = 0x7100u | busNum;
++ status = bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_LONG);
++ if (!status)
++ *portMask = (u16) res.param2;
++ return status;
++}
++static inline int bce_vhci_cmd_controller_disable(struct bce_vhci_command_queue *q)
++{
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_CONTROLLER_DISABLE;
++ return bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_LONG);
++}
++static inline int bce_vhci_cmd_controller_start(struct bce_vhci_command_queue *q)
++{
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_CONTROLLER_START;
++ return bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_LONG);
++}
++static inline int bce_vhci_cmd_controller_pause(struct bce_vhci_command_queue *q)
++{
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_CONTROLLER_PAUSE;
++ return bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_LONG);
++}
++
++static inline int bce_vhci_cmd_port_power_on(struct bce_vhci_command_queue *q, bce_vhci_port_t port)
++{
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_PORT_POWER_ON;
++ cmd.param1 = port;
++ return bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_SHORT);
++}
++static inline int bce_vhci_cmd_port_power_off(struct bce_vhci_command_queue *q, bce_vhci_port_t port)
++{
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_PORT_POWER_OFF;
++ cmd.param1 = port;
++ return bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_SHORT);
++}
++static inline int bce_vhci_cmd_port_resume(struct bce_vhci_command_queue *q, bce_vhci_port_t port)
++{
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_PORT_RESUME;
++ cmd.param1 = port;
++ return bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_LONG);
++}
++static inline int bce_vhci_cmd_port_suspend(struct bce_vhci_command_queue *q, bce_vhci_port_t port)
++{
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_PORT_SUSPEND;
++ cmd.param1 = port;
++ return bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_LONG);
++}
++static inline int bce_vhci_cmd_port_reset(struct bce_vhci_command_queue *q, bce_vhci_port_t port, u32 timeout)
++{
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_PORT_RESET;
++ cmd.param1 = port;
++ cmd.param2 = timeout;
++ return bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_SHORT);
++}
++static inline int bce_vhci_cmd_port_disable(struct bce_vhci_command_queue *q, bce_vhci_port_t port)
++{
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_PORT_DISABLE;
++ cmd.param1 = port;
++ return bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_SHORT);
++}
++static inline int bce_vhci_cmd_port_status(struct bce_vhci_command_queue *q, bce_vhci_port_t port,
++ u32 clearFlags, u32 *resStatus)
++{
++ int status;
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_PORT_STATUS;
++ cmd.param1 = port;
++ cmd.param2 = clearFlags & 0x560000;
++ status = bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_SHORT);
++ if (status >= 0)
++ *resStatus = (u32) res.param2;
++ return status;
++}
++
++static inline int bce_vhci_cmd_device_create(struct bce_vhci_command_queue *q, bce_vhci_port_t port,
++ bce_vhci_device_t *dev)
++{
++ int status;
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_DEVICE_CREATE;
++ cmd.param1 = port;
++ status = bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_SHORT);
++ if (!status)
++ *dev = (bce_vhci_device_t) res.param2;
++ return status;
++}
++static inline int bce_vhci_cmd_device_destroy(struct bce_vhci_command_queue *q, bce_vhci_device_t dev)
++{
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_DEVICE_DESTROY;
++ cmd.param1 = dev;
++ return bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_LONG);
++}
++
++static inline int bce_vhci_cmd_endpoint_create(struct bce_vhci_command_queue *q, bce_vhci_device_t dev,
++ struct usb_endpoint_descriptor *desc)
++{
++ struct bce_vhci_message cmd, res;
++ int endpoint_type = usb_endpoint_type(desc);
++ int maxp = usb_endpoint_maxp(desc);
++ int maxp_burst = usb_endpoint_maxp_mult(desc) * maxp;
++ u8 max_active_requests_pow2 = 0;
++ cmd.cmd = BCE_VHCI_CMD_ENDPOINT_CREATE;
++ cmd.param1 = dev | ((desc->bEndpointAddress & 0x8Fu) << 8);
++ if (endpoint_type == USB_ENDPOINT_XFER_BULK)
++ max_active_requests_pow2 = BCE_VHCI_BULK_MAX_ACTIVE_URBS_POW2;
++ cmd.param2 = endpoint_type | ((max_active_requests_pow2 & 0xf) << 4) | (maxp << 16) | ((u64) maxp_burst << 32);
++ if (endpoint_type == USB_ENDPOINT_XFER_INT)
++ cmd.param2 |= (desc->bInterval - 1) << 8;
++ return bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_SHORT);
++}
++static inline int bce_vhci_cmd_endpoint_destroy(struct bce_vhci_command_queue *q, bce_vhci_device_t dev, u8 endpoint)
++{
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_ENDPOINT_DESTROY;
++ cmd.param1 = dev | (endpoint << 8);
++ return bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_SHORT);
++}
++static inline int bce_vhci_cmd_endpoint_set_state(struct bce_vhci_command_queue *q, bce_vhci_device_t dev, u8 endpoint,
++ enum bce_vhci_endpoint_state newState, enum bce_vhci_endpoint_state *retState)
++{
++ int status;
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_ENDPOINT_SET_STATE;
++ cmd.param1 = dev | (endpoint << 8);
++ cmd.param2 = (u64) newState;
++ status = bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_SHORT);
++ if (status != BCE_VHCI_INTERNAL_ERROR && status != BCE_VHCI_NO_POWER)
++ *retState = (enum bce_vhci_endpoint_state) res.param2;
++ return status;
++}
++static inline int bce_vhci_cmd_endpoint_reset(struct bce_vhci_command_queue *q, bce_vhci_device_t dev, u8 endpoint)
++{
++ struct bce_vhci_message cmd, res;
++ cmd.cmd = BCE_VHCI_CMD_ENDPOINT_RESET;
++ cmd.param1 = dev | (endpoint << 8);
++ return bce_vhci_command_queue_execute(q, &cmd, &res, BCE_VHCI_CMD_TIMEOUT_SHORT);
++}
++
++
++#endif //BCE_VHCI_COMMAND_H
+diff --git a/drivers/staging/apple-bce/vhci/queue.c b/drivers/staging/apple-bce/vhci/queue.c
+new file mode 100644
+index 000000000..7b0b50271
+--- /dev/null
++++ b/drivers/staging/apple-bce/vhci/queue.c
+@@ -0,0 +1,268 @@
++#include "queue.h"
++#include "vhci.h"
++#include "../apple_bce.h"
++
++
++static void bce_vhci_message_queue_completion(struct bce_queue_sq *sq);
++
++int bce_vhci_message_queue_create(struct bce_vhci *vhci, struct bce_vhci_message_queue *ret, const char *name)
++{
++ int status;
++ ret->cq = bce_create_cq(vhci->dev, VHCI_EVENT_QUEUE_EL_COUNT);
++ if (!ret->cq)
++ return -EINVAL;
++ ret->sq = bce_create_sq(vhci->dev, ret->cq, name, VHCI_EVENT_QUEUE_EL_COUNT, DMA_TO_DEVICE,
++ bce_vhci_message_queue_completion, ret);
++ if (!ret->sq) {
++ status = -EINVAL;
++ goto fail_cq;
++ }
++ ret->data = dma_alloc_coherent(&vhci->dev->pci->dev, sizeof(struct bce_vhci_message) * VHCI_EVENT_QUEUE_EL_COUNT,
++ &ret->dma_addr, GFP_KERNEL);
++ if (!ret->data) {
++ status = -EINVAL;
++ goto fail_sq;
++ }
++ return 0;
++
++fail_sq:
++ bce_destroy_sq(vhci->dev, ret->sq);
++ ret->sq = NULL;
++fail_cq:
++ bce_destroy_cq(vhci->dev, ret->cq);
++ ret->cq = NULL;
++ return status;
++}
++
++void bce_vhci_message_queue_destroy(struct bce_vhci *vhci, struct bce_vhci_message_queue *q)
++{
++ if (!q->cq)
++ return;
++ dma_free_coherent(&vhci->dev->pci->dev, sizeof(struct bce_vhci_message) * VHCI_EVENT_QUEUE_EL_COUNT,
++ q->data, q->dma_addr);
++ bce_destroy_sq(vhci->dev, q->sq);
++ bce_destroy_cq(vhci->dev, q->cq);
++}
++
++void bce_vhci_message_queue_write(struct bce_vhci_message_queue *q, struct bce_vhci_message *req)
++{
++ int sidx;
++ struct bce_qe_submission *s;
++ sidx = q->sq->tail;
++ s = bce_next_submission(q->sq);
++ pr_debug("bce-vhci: Send message: %x s=%x p1=%x p2=%llx\n", req->cmd, req->status, req->param1, req->param2);
++ q->data[sidx] = *req;
++ bce_set_submission_single(s, q->dma_addr + sizeof(struct bce_vhci_message) * sidx,
++ sizeof(struct bce_vhci_message));
++ bce_submit_to_device(q->sq);
++}
++
++static void bce_vhci_message_queue_completion(struct bce_queue_sq *sq)
++{
++ while (bce_next_completion(sq))
++ bce_notify_submission_complete(sq);
++}
++
++
++
++static void bce_vhci_event_queue_completion(struct bce_queue_sq *sq);
++
++int __bce_vhci_event_queue_create(struct bce_vhci *vhci, struct bce_vhci_event_queue *ret, const char *name,
++ bce_sq_completion compl)
++{
++ ret->vhci = vhci;
++
++ ret->sq = bce_create_sq(vhci->dev, vhci->ev_cq, name, VHCI_EVENT_QUEUE_EL_COUNT, DMA_FROM_DEVICE, compl, ret);
++ if (!ret->sq)
++ return -EINVAL;
++ ret->data = dma_alloc_coherent(&vhci->dev->pci->dev, sizeof(struct bce_vhci_message) * VHCI_EVENT_QUEUE_EL_COUNT,
++ &ret->dma_addr, GFP_KERNEL);
++ if (!ret->data) {
++ bce_destroy_sq(vhci->dev, ret->sq);
++ ret->sq = NULL;
++ return -EINVAL;
++ }
++
++ init_completion(&ret->queue_empty_completion);
++ bce_vhci_event_queue_submit_pending(ret, VHCI_EVENT_PENDING_COUNT);
++ return 0;
++}
++
++int bce_vhci_event_queue_create(struct bce_vhci *vhci, struct bce_vhci_event_queue *ret, const char *name,
++ bce_vhci_event_queue_callback cb)
++{
++ ret->cb = cb;
++ return __bce_vhci_event_queue_create(vhci, ret, name, bce_vhci_event_queue_completion);
++}
++
++void bce_vhci_event_queue_destroy(struct bce_vhci *vhci, struct bce_vhci_event_queue *q)
++{
++ if (!q->sq)
++ return;
++ dma_free_coherent(&vhci->dev->pci->dev, sizeof(struct bce_vhci_message) * VHCI_EVENT_QUEUE_EL_COUNT,
++ q->data, q->dma_addr);
++ bce_destroy_sq(vhci->dev, q->sq);
++}
++
++static void bce_vhci_event_queue_completion(struct bce_queue_sq *sq)
++{
++ struct bce_sq_completion_data *cd;
++ struct bce_vhci_event_queue *ev = sq->userdata;
++ struct bce_vhci_message *msg;
++ size_t cnt = 0;
++
++ while ((cd = bce_next_completion(sq))) {
++ if (cd->status == BCE_COMPLETION_ABORTED) { /* We flushed the queue */
++ bce_notify_submission_complete(sq);
++ continue;
++ }
++ msg = &ev->data[sq->head];
++ pr_debug("bce-vhci: Got event: %x s=%x p1=%x p2=%llx\n", msg->cmd, msg->status, msg->param1, msg->param2);
++ ev->cb(ev, msg);
++
++ bce_notify_submission_complete(sq);
++ ++cnt;
++ }
++ bce_vhci_event_queue_submit_pending(ev, cnt);
++ if (atomic_read(&sq->available_commands) == sq->el_count - 1)
++ complete(&ev->queue_empty_completion);
++}
++
++void bce_vhci_event_queue_submit_pending(struct bce_vhci_event_queue *q, size_t count)
++{
++ int idx;
++ struct bce_qe_submission *s;
++ while (count--) {
++ if (bce_reserve_submission(q->sq, NULL)) {
++ pr_err("bce-vhci: Failed to reserve an event queue submission\n");
++ break;
++ }
++ idx = q->sq->tail;
++ s = bce_next_submission(q->sq);
++ bce_set_submission_single(s,
++ q->dma_addr + idx * sizeof(struct bce_vhci_message), sizeof(struct bce_vhci_message));
++ }
++ bce_submit_to_device(q->sq);
++}
++
++void bce_vhci_event_queue_pause(struct bce_vhci_event_queue *q)
++{
++ unsigned long timeout;
++ reinit_completion(&q->queue_empty_completion);
++ if (bce_cmd_flush_memory_queue(q->vhci->dev->cmd_cmdq, q->sq->qid))
++ pr_warn("bce-vhci: failed to flush event queue\n");
++ timeout = msecs_to_jiffies(5000);
++ while (atomic_read(&q->sq->available_commands) != q->sq->el_count - 1) {
++ timeout = wait_for_completion_timeout(&q->queue_empty_completion, timeout);
++ if (timeout == 0) {
++ pr_err("bce-vhci: waiting for queue to be flushed timed out\n");
++ break;
++ }
++ }
++}
++
++void bce_vhci_event_queue_resume(struct bce_vhci_event_queue *q)
++{
++ if (atomic_read(&q->sq->available_commands) != q->sq->el_count - 1) {
++ pr_err("bce-vhci: resume of a queue with pending submissions\n");
++ return;
++ }
++ bce_vhci_event_queue_submit_pending(q, VHCI_EVENT_PENDING_COUNT);
++}
++
++void bce_vhci_command_queue_create(struct bce_vhci_command_queue *ret, struct bce_vhci_message_queue *mq)
++{
++ ret->mq = mq;
++ ret->completion.result = NULL;
++ init_completion(&ret->completion.completion);
++ spin_lock_init(&ret->completion_lock);
++ mutex_init(&ret->mutex);
++}
++
++void bce_vhci_command_queue_destroy(struct bce_vhci_command_queue *cq)
++{
++ spin_lock(&cq->completion_lock);
++ if (cq->completion.result) {
++ memset(cq->completion.result, 0, sizeof(struct bce_vhci_message));
++ cq->completion.result->status = BCE_VHCI_ABORT;
++ complete(&cq->completion.completion);
++ cq->completion.result = NULL;
++ }
++ spin_unlock(&cq->completion_lock);
++ mutex_lock(&cq->mutex);
++ mutex_unlock(&cq->mutex);
++ mutex_destroy(&cq->mutex);
++}
++
++void bce_vhci_command_queue_deliver_completion(struct bce_vhci_command_queue *cq, struct bce_vhci_message *msg)
++{
++ struct bce_vhci_command_queue_completion *c = &cq->completion;
++
++ spin_lock(&cq->completion_lock);
++ if (c->result) {
++ *c->result = *msg;
++ complete(&c->completion);
++ c->result = NULL;
++ }
++ spin_unlock(&cq->completion_lock);
++}
++
++static int __bce_vhci_command_queue_execute(struct bce_vhci_command_queue *cq, struct bce_vhci_message *req,
++ struct bce_vhci_message *res, unsigned long timeout)
++{
++ int status;
++ struct bce_vhci_command_queue_completion *c;
++ struct bce_vhci_message creq;
++ c = &cq->completion;
++
++ if ((status = bce_reserve_submission(cq->mq->sq, &timeout)))
++ return status;
++
++ spin_lock(&cq->completion_lock);
++ c->result = res;
++ reinit_completion(&c->completion);
++ spin_unlock(&cq->completion_lock);
++
++ bce_vhci_message_queue_write(cq->mq, req);
++
++ if (!wait_for_completion_timeout(&c->completion, timeout)) {
++ /* we ran out of time, send cancellation */
++ pr_debug("bce-vhci: command timed out req=%x\n", req->cmd);
++ if ((status = bce_reserve_submission(cq->mq->sq, &timeout)))
++ return status;
++
++ creq = *req;
++ creq.cmd |= 0x4000;
++ bce_vhci_message_queue_write(cq->mq, &creq);
++
++ if (!wait_for_completion_timeout(&c->completion, 1000)) {
++ pr_err("bce-vhci: Possible desync, cmd cancel timed out\n");
++
++ spin_lock(&cq->completion_lock);
++ c->result = NULL;
++ spin_unlock(&cq->completion_lock);
++ return -ETIMEDOUT;
++ }
++ if ((res->cmd & ~0x8000) == creq.cmd)
++ return -ETIMEDOUT;
++ /* reply for the previous command most likely arrived */
++ }
++
++ if ((res->cmd & ~0x8000) != req->cmd) {
++ pr_err("bce-vhci: Possible desync, cmd reply mismatch req=%x, res=%x\n", req->cmd, res->cmd);
++ return -EIO;
++ }
++ if (res->status == BCE_VHCI_SUCCESS)
++ return 0;
++ return res->status;
++}
++
++int bce_vhci_command_queue_execute(struct bce_vhci_command_queue *cq, struct bce_vhci_message *req,
++ struct bce_vhci_message *res, unsigned long timeout)
++{
++ int status;
++ mutex_lock(&cq->mutex);
++ status = __bce_vhci_command_queue_execute(cq, req, res, timeout);
++ mutex_unlock(&cq->mutex);
++ return status;
++}
+diff --git a/drivers/staging/apple-bce/vhci/queue.h b/drivers/staging/apple-bce/vhci/queue.h
+new file mode 100644
+index 000000000..adb705b6b
+--- /dev/null
++++ b/drivers/staging/apple-bce/vhci/queue.h
+@@ -0,0 +1,76 @@
++#ifndef BCE_VHCI_QUEUE_H
++#define BCE_VHCI_QUEUE_H
++
++#include <linux/completion.h>
++#include "../queue.h"
++
++#define VHCI_EVENT_QUEUE_EL_COUNT 256
++#define VHCI_EVENT_PENDING_COUNT 32
++
++struct bce_vhci;
++struct bce_vhci_event_queue;
++
++enum bce_vhci_message_status {
++ BCE_VHCI_SUCCESS = 1,
++ BCE_VHCI_ERROR = 2,
++ BCE_VHCI_USB_PIPE_STALL = 3,
++ BCE_VHCI_ABORT = 4,
++ BCE_VHCI_BAD_ARGUMENT = 5,
++ BCE_VHCI_OVERRUN = 6,
++ BCE_VHCI_INTERNAL_ERROR = 7,
++ BCE_VHCI_NO_POWER = 8,
++ BCE_VHCI_UNSUPPORTED = 9
++};
++struct bce_vhci_message {
++ u16 cmd;
++ u16 status; // bce_vhci_message_status
++ u32 param1;
++ u64 param2;
++};
++
++struct bce_vhci_message_queue {
++ struct bce_queue_cq *cq;
++ struct bce_queue_sq *sq;
++ struct bce_vhci_message *data;
++ dma_addr_t dma_addr;
++};
++typedef void (*bce_vhci_event_queue_callback)(struct bce_vhci_event_queue *q, struct bce_vhci_message *msg);
++struct bce_vhci_event_queue {
++ struct bce_vhci *vhci;
++ struct bce_queue_sq *sq;
++ struct bce_vhci_message *data;
++ dma_addr_t dma_addr;
++ bce_vhci_event_queue_callback cb;
++ struct completion queue_empty_completion;
++};
++struct bce_vhci_command_queue_completion {
++ struct bce_vhci_message *result;
++ struct completion completion;
++};
++struct bce_vhci_command_queue {
++ struct bce_vhci_message_queue *mq;
++ struct bce_vhci_command_queue_completion completion;
++ struct spinlock completion_lock;
++ struct mutex mutex;
++};
++
++int bce_vhci_message_queue_create(struct bce_vhci *vhci, struct bce_vhci_message_queue *ret, const char *name);
++void bce_vhci_message_queue_destroy(struct bce_vhci *vhci, struct bce_vhci_message_queue *q);
++void bce_vhci_message_queue_write(struct bce_vhci_message_queue *q, struct bce_vhci_message *req);
++
++int __bce_vhci_event_queue_create(struct bce_vhci *vhci, struct bce_vhci_event_queue *ret, const char *name,
++ bce_sq_completion compl);
++int bce_vhci_event_queue_create(struct bce_vhci *vhci, struct bce_vhci_event_queue *ret, const char *name,
++ bce_vhci_event_queue_callback cb);
++void bce_vhci_event_queue_destroy(struct bce_vhci *vhci, struct bce_vhci_event_queue *q);
++void bce_vhci_event_queue_submit_pending(struct bce_vhci_event_queue *q, size_t count);
++void bce_vhci_event_queue_pause(struct bce_vhci_event_queue *q);
++void bce_vhci_event_queue_resume(struct bce_vhci_event_queue *q);
++
++void bce_vhci_command_queue_create(struct bce_vhci_command_queue *ret, struct bce_vhci_message_queue *mq);
++void bce_vhci_command_queue_destroy(struct bce_vhci_command_queue *cq);
++int bce_vhci_command_queue_execute(struct bce_vhci_command_queue *cq, struct bce_vhci_message *req,
++ struct bce_vhci_message *res, unsigned long timeout);
++void bce_vhci_command_queue_deliver_completion(struct bce_vhci_command_queue *cq, struct bce_vhci_message *msg);
++
++#endif //BCE_VHCI_QUEUE_H
+diff --git a/drivers/staging/apple-bce/vhci/transfer.c b/drivers/staging/apple-bce/vhci/transfer.c
+new file mode 100644
+index 000000000..8226363d6
+--- /dev/null
++++ b/drivers/staging/apple-bce/vhci/transfer.c
+@@ -0,0 +1,661 @@
++#include "transfer.h"
++#include "../queue.h"
++#include "vhci.h"
++#include "../apple_bce.h"
++#include <linux/usb/hcd.h>
++
++static void bce_vhci_transfer_queue_completion(struct bce_queue_sq *sq);
++static void bce_vhci_transfer_queue_giveback(struct bce_vhci_transfer_queue *q);
++static void bce_vhci_transfer_queue_remove_pending(struct bce_vhci_transfer_queue *q);
++
++static int bce_vhci_urb_init(struct bce_vhci_urb *vurb);
++static int bce_vhci_urb_update(struct bce_vhci_urb *urb, struct bce_vhci_message *msg);
++static int bce_vhci_urb_transfer_completion(struct bce_vhci_urb *urb, struct bce_sq_completion_data *c);
++
++static void bce_vhci_transfer_queue_reset_w(struct work_struct *work);
++
++void bce_vhci_create_transfer_queue(struct bce_vhci *vhci, struct bce_vhci_transfer_queue *q,
++ struct usb_host_endpoint *endp, bce_vhci_device_t dev_addr, enum dma_data_direction dir)
++{
++ char name[0x21];
++ INIT_LIST_HEAD(&q->evq);
++ INIT_LIST_HEAD(&q->giveback_urb_list);
++ spin_lock_init(&q->urb_lock);
++ mutex_init(&q->pause_lock);
++ q->vhci = vhci;
++ q->endp = endp;
++ q->dev_addr = dev_addr;
++ q->endp_addr = (u8) (endp->desc.bEndpointAddress & 0x8F);
++ q->state = BCE_VHCI_ENDPOINT_ACTIVE;
++ q->active = true;
++ q->stalled = false;
++ q->max_active_requests = 1;
++ if (usb_endpoint_type(&endp->desc) == USB_ENDPOINT_XFER_BULK)
++ q->max_active_requests = BCE_VHCI_BULK_MAX_ACTIVE_URBS;
++ q->remaining_active_requests = q->max_active_requests;
++ q->cq = bce_create_cq(vhci->dev, 0x100);
++ INIT_WORK(&q->w_reset, bce_vhci_transfer_queue_reset_w);
++ q->sq_in = NULL;
++ if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) {
++ snprintf(name, sizeof(name), "VHC1-%i-%02x", dev_addr, 0x80 | usb_endpoint_num(&endp->desc));
++ q->sq_in = bce_create_sq(vhci->dev, q->cq, name, 0x100, DMA_FROM_DEVICE,
++ bce_vhci_transfer_queue_completion, q);
++ }
++ q->sq_out = NULL;
++ if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) {
++ snprintf(name, sizeof(name), "VHC1-%i-%02x", dev_addr, usb_endpoint_num(&endp->desc));
++ q->sq_out = bce_create_sq(vhci->dev, q->cq, name, 0x100, DMA_TO_DEVICE,
++ bce_vhci_transfer_queue_completion, q);
++ }
++}
++
++void bce_vhci_destroy_transfer_queue(struct bce_vhci *vhci, struct bce_vhci_transfer_queue *q)
++{
++ bce_vhci_transfer_queue_giveback(q);
++ bce_vhci_transfer_queue_remove_pending(q);
++ if (q->sq_in)
++ bce_destroy_sq(vhci->dev, q->sq_in);
++ if (q->sq_out)
++ bce_destroy_sq(vhci->dev, q->sq_out);
++ bce_destroy_cq(vhci->dev, q->cq);
++}
++
++static inline bool bce_vhci_transfer_queue_can_init_urb(struct bce_vhci_transfer_queue *q)
++{
++ return q->remaining_active_requests > 0;
++}
++
++static void bce_vhci_transfer_queue_defer_event(struct bce_vhci_transfer_queue *q, struct bce_vhci_message *msg)
++{
++ struct bce_vhci_list_message *lm;
++ lm = kmalloc(sizeof(struct bce_vhci_list_message), GFP_KERNEL);
++ INIT_LIST_HEAD(&lm->list);
++ lm->msg = *msg;
++ list_add_tail(&lm->list, &q->evq);
++}
++
++static void bce_vhci_transfer_queue_giveback(struct bce_vhci_transfer_queue *q)
++{
++ unsigned long flags;
++ struct urb *urb;
++ spin_lock_irqsave(&q->urb_lock, flags);
++ while (!list_empty(&q->giveback_urb_list)) {
++ urb = list_first_entry(&q->giveback_urb_list, struct urb, urb_list);
++ list_del(&urb->urb_list);
++
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++ usb_hcd_giveback_urb(q->vhci->hcd, urb, urb->status);
++ spin_lock_irqsave(&q->urb_lock, flags);
++ }
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++}
++
++static void bce_vhci_transfer_queue_init_pending_urbs(struct bce_vhci_transfer_queue *q);
++
++static void bce_vhci_transfer_queue_deliver_pending(struct bce_vhci_transfer_queue *q)
++{
++ struct urb *urb;
++ struct bce_vhci_list_message *lm;
++
++ while (!list_empty(&q->endp->urb_list) && !list_empty(&q->evq)) {
++ urb = list_first_entry(&q->endp->urb_list, struct urb, urb_list);
++
++ lm = list_first_entry(&q->evq, struct bce_vhci_list_message, list);
++ if (bce_vhci_urb_update(urb->hcpriv, &lm->msg) == -EAGAIN)
++ break;
++ list_del(&lm->list);
++ kfree(lm);
++ }
++
++ /* some of the URBs could have been completed, so initialize more URBs if possible */
++ bce_vhci_transfer_queue_init_pending_urbs(q);
++}
++
++static void bce_vhci_transfer_queue_remove_pending(struct bce_vhci_transfer_queue *q)
++{
++ unsigned long flags;
++ struct bce_vhci_list_message *lm;
++ spin_lock_irqsave(&q->urb_lock, flags);
++ while (!list_empty(&q->evq)) {
++ lm = list_first_entry(&q->evq, struct bce_vhci_list_message, list);
++ list_del(&lm->list);
++ kfree(lm);
++ }
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++}
++
++void bce_vhci_transfer_queue_event(struct bce_vhci_transfer_queue *q, struct bce_vhci_message *msg)
++{
++ unsigned long flags;
++ struct bce_vhci_urb *turb;
++ struct urb *urb;
++ spin_lock_irqsave(&q->urb_lock, flags);
++ bce_vhci_transfer_queue_deliver_pending(q);
++
++ if (msg->cmd == BCE_VHCI_CMD_TRANSFER_REQUEST &&
++ (!list_empty(&q->evq) || list_empty(&q->endp->urb_list))) {
++ bce_vhci_transfer_queue_defer_event(q, msg);
++ goto complete;
++ }
++ if (list_empty(&q->endp->urb_list)) {
++ pr_err("bce-vhci: [%02x] Unexpected transfer queue event\n", q->endp_addr);
++ goto complete;
++ }
++ urb = list_first_entry(&q->endp->urb_list, struct urb, urb_list);
++ turb = urb->hcpriv;
++ if (bce_vhci_urb_update(turb, msg) == -EAGAIN) {
++ bce_vhci_transfer_queue_defer_event(q, msg);
++ } else {
++ bce_vhci_transfer_queue_init_pending_urbs(q);
++ }
++
++complete:
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++ bce_vhci_transfer_queue_giveback(q);
++}
++
++static void bce_vhci_transfer_queue_completion(struct bce_queue_sq *sq)
++{
++ unsigned long flags;
++ struct bce_sq_completion_data *c;
++ struct urb *urb;
++ struct bce_vhci_transfer_queue *q = sq->userdata;
++ spin_lock_irqsave(&q->urb_lock, flags);
++ while ((c = bce_next_completion(sq))) {
++ if (c->status == BCE_COMPLETION_ABORTED) { /* We flushed the queue */
++ pr_debug("bce-vhci: [%02x] Got an abort completion\n", q->endp_addr);
++ bce_notify_submission_complete(sq);
++ continue;
++ }
++ if (list_empty(&q->endp->urb_list)) {
++ pr_err("bce-vhci: [%02x] Got a completion while no requests are pending\n", q->endp_addr);
++ continue;
++ }
++ pr_debug("bce-vhci: [%02x] Got a transfer queue completion\n", q->endp_addr);
++ urb = list_first_entry(&q->endp->urb_list, struct urb, urb_list);
++ bce_vhci_urb_transfer_completion(urb->hcpriv, c);
++ bce_notify_submission_complete(sq);
++ }
++ bce_vhci_transfer_queue_deliver_pending(q);
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++ bce_vhci_transfer_queue_giveback(q);
++}
++
++int bce_vhci_transfer_queue_do_pause(struct bce_vhci_transfer_queue *q)
++{
++ unsigned long flags;
++ int status;
++ u8 endp_addr = (u8) (q->endp->desc.bEndpointAddress & 0x8F);
++ spin_lock_irqsave(&q->urb_lock, flags);
++ q->active = false;
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++ if (q->sq_out) {
++ pr_err("bce-vhci: Not implemented: wait for pending output requests\n");
++ }
++ bce_vhci_transfer_queue_remove_pending(q);
++ if ((status = bce_vhci_cmd_endpoint_set_state(
++ &q->vhci->cq, q->dev_addr, endp_addr, BCE_VHCI_ENDPOINT_PAUSED, &q->state)))
++ return status;
++ if (q->state != BCE_VHCI_ENDPOINT_PAUSED)
++ return -EINVAL;
++ if (q->sq_in)
++ bce_cmd_flush_memory_queue(q->vhci->dev->cmd_cmdq, (u16) q->sq_in->qid);
++ if (q->sq_out)
++ bce_cmd_flush_memory_queue(q->vhci->dev->cmd_cmdq, (u16) q->sq_out->qid);
++ return 0;
++}
++
++static void bce_vhci_urb_resume(struct bce_vhci_urb *urb);
++
++int bce_vhci_transfer_queue_do_resume(struct bce_vhci_transfer_queue *q)
++{
++ unsigned long flags;
++ int status;
++ struct urb *urb, *urbt;
++ struct bce_vhci_urb *vurb;
++ u8 endp_addr = (u8) (q->endp->desc.bEndpointAddress & 0x8F);
++ if ((status = bce_vhci_cmd_endpoint_set_state(
++ &q->vhci->cq, q->dev_addr, endp_addr, BCE_VHCI_ENDPOINT_ACTIVE, &q->state)))
++ return status;
++ if (q->state != BCE_VHCI_ENDPOINT_ACTIVE)
++ return -EINVAL;
++ spin_lock_irqsave(&q->urb_lock, flags);
++ q->active = true;
++ list_for_each_entry_safe(urb, urbt, &q->endp->urb_list, urb_list) {
++ vurb = urb->hcpriv;
++ if (vurb->state == BCE_VHCI_URB_INIT_PENDING) {
++ if (!bce_vhci_transfer_queue_can_init_urb(q))
++ break;
++ bce_vhci_urb_init(vurb);
++ } else {
++ bce_vhci_urb_resume(vurb);
++ }
++ }
++ bce_vhci_transfer_queue_deliver_pending(q);
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++ return 0;
++}
++
++int bce_vhci_transfer_queue_pause(struct bce_vhci_transfer_queue *q, enum bce_vhci_pause_source src)
++{
++ int ret = 0;
++ mutex_lock(&q->pause_lock);
++ if ((q->paused_by & src) != src) {
++ if (!q->paused_by)
++ ret = bce_vhci_transfer_queue_do_pause(q);
++ if (!ret)
++ q->paused_by |= src;
++ }
++ mutex_unlock(&q->pause_lock);
++ return ret;
++}
++
++int bce_vhci_transfer_queue_resume(struct bce_vhci_transfer_queue *q, enum bce_vhci_pause_source src)
++{
++ int ret = 0;
++ mutex_lock(&q->pause_lock);
++ if (q->paused_by & src) {
++ if (!(q->paused_by & ~src))
++ ret = bce_vhci_transfer_queue_do_resume(q);
++ if (!ret)
++ q->paused_by &= ~src;
++ }
++ mutex_unlock(&q->pause_lock);
++ return ret;
++}
++
++static void bce_vhci_transfer_queue_reset_w(struct work_struct *work)
++{
++ unsigned long flags;
++ struct bce_vhci_transfer_queue *q = container_of(work, struct bce_vhci_transfer_queue, w_reset);
++
++ mutex_lock(&q->pause_lock);
++ spin_lock_irqsave(&q->urb_lock, flags);
++ if (!q->stalled) {
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++ mutex_unlock(&q->pause_lock);
++ return;
++ }
++ q->active = false;
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++ q->paused_by |= BCE_VHCI_PAUSE_INTERNAL_WQ;
++ bce_vhci_transfer_queue_remove_pending(q);
++ if (q->sq_in)
++ bce_cmd_flush_memory_queue(q->vhci->dev->cmd_cmdq, (u16) q->sq_in->qid);
++ if (q->sq_out)
++ bce_cmd_flush_memory_queue(q->vhci->dev->cmd_cmdq, (u16) q->sq_out->qid);
++ bce_vhci_cmd_endpoint_reset(&q->vhci->cq, q->dev_addr, (u8) (q->endp->desc.bEndpointAddress & 0x8F));
++ spin_lock_irqsave(&q->urb_lock, flags);
++ q->stalled = false;
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++ mutex_unlock(&q->pause_lock);
++ bce_vhci_transfer_queue_resume(q, BCE_VHCI_PAUSE_INTERNAL_WQ);
++}
++
++void bce_vhci_transfer_queue_request_reset(struct bce_vhci_transfer_queue *q)
++{
++ queue_work(q->vhci->tq_state_wq, &q->w_reset);
++}
++
++static void bce_vhci_transfer_queue_init_pending_urbs(struct bce_vhci_transfer_queue *q)
++{
++ struct urb *urb, *urbt;
++ struct bce_vhci_urb *vurb;
++ list_for_each_entry_safe(urb, urbt, &q->endp->urb_list, urb_list) {
++ vurb = urb->hcpriv;
++ if (!bce_vhci_transfer_queue_can_init_urb(q))
++ break;
++ if (vurb->state == BCE_VHCI_URB_INIT_PENDING)
++ bce_vhci_urb_init(vurb);
++ }
++}
++
++
++
++static int bce_vhci_urb_data_start(struct bce_vhci_urb *urb, unsigned long *timeout);
++
++int bce_vhci_urb_create(struct bce_vhci_transfer_queue *q, struct urb *urb)
++{
++ unsigned long flags;
++ int status = 0;
++ struct bce_vhci_urb *vurb;
++ vurb = kzalloc(sizeof(struct bce_vhci_urb), GFP_KERNEL);
++ urb->hcpriv = vurb;
++
++ vurb->q = q;
++ vurb->urb = urb;
++ vurb->dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
++ vurb->is_control = (usb_endpoint_num(&urb->ep->desc) == 0);
++
++ spin_lock_irqsave(&q->urb_lock, flags);
++ status = usb_hcd_link_urb_to_ep(q->vhci->hcd, urb);
++ if (status) {
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++ urb->hcpriv = NULL;
++ kfree(vurb);
++ return status;
++ }
++
++ if (q->active) {
++ if (bce_vhci_transfer_queue_can_init_urb(vurb->q))
++ status = bce_vhci_urb_init(vurb);
++ else
++ vurb->state = BCE_VHCI_URB_INIT_PENDING;
++ } else {
++ if (q->stalled)
++ bce_vhci_transfer_queue_request_reset(q);
++ vurb->state = BCE_VHCI_URB_INIT_PENDING;
++ }
++ if (status) {
++ usb_hcd_unlink_urb_from_ep(q->vhci->hcd, urb);
++ urb->hcpriv = NULL;
++ kfree(vurb);
++ } else {
++ bce_vhci_transfer_queue_deliver_pending(q);
++ }
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++ pr_debug("bce-vhci: [%02x] URB enqueued (dir = %s, size = %i)\n", q->endp_addr,
++ usb_urb_dir_in(urb) ? "IN" : "OUT", urb->transfer_buffer_length);
++ return status;
++}
++
++static int bce_vhci_urb_init(struct bce_vhci_urb *vurb)
++{
++ int status = 0;
++
++ if (vurb->q->remaining_active_requests == 0) {
++ pr_err("bce-vhci: cannot init request (remaining_active_requests = 0)\n");
++ return -EINVAL;
++ }
++
++ if (vurb->is_control) {
++ vurb->state = BCE_VHCI_URB_CONTROL_WAITING_FOR_SETUP_REQUEST;
++ } else {
++ status = bce_vhci_urb_data_start(vurb, NULL);
++ }
++
++ if (!status) {
++ --vurb->q->remaining_active_requests;
++ }
++ return status;
++}
++
++static void bce_vhci_urb_complete(struct bce_vhci_urb *urb, int status)
++{
++ struct bce_vhci_transfer_queue *q = urb->q;
++ struct bce_vhci *vhci = q->vhci;
++ struct urb *real_urb = urb->urb;
++ pr_debug("bce-vhci: [%02x] URB complete %i\n", q->endp_addr, status);
++ usb_hcd_unlink_urb_from_ep(vhci->hcd, real_urb);
++ real_urb->hcpriv = NULL;
++ real_urb->status = status;
++ if (urb->state != BCE_VHCI_URB_INIT_PENDING)
++ ++urb->q->remaining_active_requests;
++ kfree(urb);
++ list_add_tail(&real_urb->urb_list, &q->giveback_urb_list);
++}
++
++int bce_vhci_urb_request_cancel(struct bce_vhci_transfer_queue *q, struct urb *urb, int status)
++{
++ struct bce_vhci_urb *vurb;
++ unsigned long flags;
++ int ret;
++
++ spin_lock_irqsave(&q->urb_lock, flags);
++ if ((ret = usb_hcd_check_unlink_urb(q->vhci->hcd, urb, status))) {
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++ return ret;
++ }
++
++ vurb = urb->hcpriv;
++ /* If the URB wasn't posted to the device yet, we can still remove it on the host without pausing the queue. */
++ if (vurb->state != BCE_VHCI_URB_INIT_PENDING) {
++ pr_debug("bce-vhci: [%02x] Cancelling URB\n", q->endp_addr);
++
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++ bce_vhci_transfer_queue_pause(q, BCE_VHCI_PAUSE_INTERNAL_WQ);
++ spin_lock_irqsave(&q->urb_lock, flags);
++
++ ++q->remaining_active_requests;
++ }
++
++ usb_hcd_unlink_urb_from_ep(q->vhci->hcd, urb);
++
++ spin_unlock_irqrestore(&q->urb_lock, flags);
++
++ usb_hcd_giveback_urb(q->vhci->hcd, urb, status);
++
++ if (vurb->state != BCE_VHCI_URB_INIT_PENDING)
++ bce_vhci_transfer_queue_resume(q, BCE_VHCI_PAUSE_INTERNAL_WQ);
++
++ kfree(vurb);
++
++ return 0;
++}
++
++static int bce_vhci_urb_data_transfer_in(struct bce_vhci_urb *urb, unsigned long *timeout)
++{
++ struct bce_vhci_message msg;
++ struct bce_qe_submission *s;
++ u32 tr_len;
++ int reservation1, reservation2 = -EFAULT;
++
++ pr_debug("bce-vhci: [%02x] DMA from device %llx %x\n", urb->q->endp_addr,
++ (u64) urb->urb->transfer_dma, urb->urb->transfer_buffer_length);
++
++ /* Reserve both a message and a submission, so we don't run into issues later. */
++ reservation1 = bce_reserve_submission(urb->q->vhci->msg_asynchronous.sq, timeout);
++ if (!reservation1)
++ reservation2 = bce_reserve_submission(urb->q->sq_in, timeout);
++ if (reservation1 || reservation2) {
++ pr_err("bce-vhci: Failed to reserve a submission for URB data transfer\n");
++ if (!reservation1)
++ bce_cancel_submission_reservation(urb->q->vhci->msg_asynchronous.sq);
++ return -ENOMEM;
++ }
++
++ urb->send_offset = urb->receive_offset;
++
++ tr_len = urb->urb->transfer_buffer_length - urb->send_offset;
++
++ spin_lock(&urb->q->vhci->msg_asynchronous_lock);
++ msg.cmd = BCE_VHCI_CMD_TRANSFER_REQUEST;
++ msg.status = 0;
++ msg.param1 = ((urb->urb->ep->desc.bEndpointAddress & 0x8Fu) << 8) | urb->q->dev_addr;
++ msg.param2 = tr_len;
++ bce_vhci_message_queue_write(&urb->q->vhci->msg_asynchronous, &msg);
++ spin_unlock(&urb->q->vhci->msg_asynchronous_lock);
++
++ s = bce_next_submission(urb->q->sq_in);
++ bce_set_submission_single(s, urb->urb->transfer_dma + urb->send_offset, tr_len);
++ bce_submit_to_device(urb->q->sq_in);
++
++ urb->state = BCE_VHCI_URB_WAITING_FOR_COMPLETION;
++ return 0;
++}
++
++static int bce_vhci_urb_data_start(struct bce_vhci_urb *urb, unsigned long *timeout)
++{
++ if (urb->dir == DMA_TO_DEVICE) {
++ if (urb->urb->transfer_buffer_length > 0)
++ urb->state = BCE_VHCI_URB_WAITING_FOR_TRANSFER_REQUEST;
++ else
++ urb->state = BCE_VHCI_URB_DATA_TRANSFER_COMPLETE;
++ return 0;
++ } else {
++ return bce_vhci_urb_data_transfer_in(urb, timeout);
++ }
++}
++
++static int bce_vhci_urb_send_out_data(struct bce_vhci_urb *urb, dma_addr_t addr, size_t size)
++{
++ struct bce_qe_submission *s;
++ unsigned long timeout = 0;
++ if (bce_reserve_submission(urb->q->sq_out, &timeout)) {
++ pr_err("bce-vhci: Failed to reserve a submission for URB data transfer\n");
++ return -EPIPE;
++ }
++
++ pr_debug("bce-vhci: [%02x] DMA to device %llx %lx\n", urb->q->endp_addr, (u64) addr, size);
++
++ s = bce_next_submission(urb->q->sq_out);
++ bce_set_submission_single(s, addr, size);
++ bce_submit_to_device(urb->q->sq_out);
++ return 0;
++}
++
++static int bce_vhci_urb_data_update(struct bce_vhci_urb *urb, struct bce_vhci_message *msg)
++{
++ u32 tr_len;
++ int status;
++ if (urb->state == BCE_VHCI_URB_WAITING_FOR_TRANSFER_REQUEST) {
++ if (msg->cmd == BCE_VHCI_CMD_TRANSFER_REQUEST) {
++ tr_len = min(urb->urb->transfer_buffer_length - urb->send_offset, (u32) msg->param2);
++ if ((status = bce_vhci_urb_send_out_data(urb, urb->urb->transfer_dma + urb->send_offset, tr_len)))
++ return status;
++ urb->send_offset += tr_len;
++ urb->state = BCE_VHCI_URB_WAITING_FOR_COMPLETION;
++ return 0;
++ }
++ }
++
++ /* 0x1000 in out queues aren't really unexpected */
++ if (msg->cmd == BCE_VHCI_CMD_TRANSFER_REQUEST && urb->q->sq_out != NULL)
++ return -EAGAIN;
++ pr_err("bce-vhci: [%02x] %s URB unexpected message (state = %x, msg: %x %x %x %llx)\n",
++ urb->q->endp_addr, (urb->is_control ? "Control (data update)" : "Data"), urb->state,
++ msg->cmd, msg->status, msg->param1, msg->param2);
++ return -EAGAIN;
++}
++
++static int bce_vhci_urb_data_transfer_completion(struct bce_vhci_urb *urb, struct bce_sq_completion_data *c)
++{
++ if (urb->state == BCE_VHCI_URB_WAITING_FOR_COMPLETION) {
++ urb->receive_offset += c->data_size;
++ if (urb->dir == DMA_FROM_DEVICE || urb->receive_offset >= urb->urb->transfer_buffer_length) {
++ urb->urb->actual_length = (u32) urb->receive_offset;
++ urb->state = BCE_VHCI_URB_DATA_TRANSFER_COMPLETE;
++ if (!urb->is_control) {
++ bce_vhci_urb_complete(urb, 0);
++ return -ENOENT;
++ }
++ }
++ } else {
++ pr_err("bce-vhci: [%02x] Data URB unexpected completion\n", urb->q->endp_addr);
++ }
++ return 0;
++}
++
++
++static int bce_vhci_urb_control_check_status(struct bce_vhci_urb *urb)
++{
++ struct bce_vhci_transfer_queue *q = urb->q;
++ if (urb->received_status == 0)
++ return 0;
++ if (urb->state == BCE_VHCI_URB_DATA_TRANSFER_COMPLETE ||
++ (urb->received_status != BCE_VHCI_SUCCESS && urb->state != BCE_VHCI_URB_CONTROL_WAITING_FOR_SETUP_REQUEST &&
++ urb->state != BCE_VHCI_URB_CONTROL_WAITING_FOR_SETUP_COMPLETION)) {
++ urb->state = BCE_VHCI_URB_CONTROL_COMPLETE;
++ if (urb->received_status != BCE_VHCI_SUCCESS) {
++ pr_err("bce-vhci: [%02x] URB failed: %x\n", urb->q->endp_addr, urb->received_status);
++ urb->q->active = false;
++ urb->q->stalled = true;
++ bce_vhci_urb_complete(urb, -EPIPE);
++ if (!list_empty(&q->endp->urb_list))
++ bce_vhci_transfer_queue_request_reset(q);
++ return -ENOENT;
++ }
++ bce_vhci_urb_complete(urb, 0);
++ return -ENOENT;
++ }
++ return 0;
++}
++
++static int bce_vhci_urb_control_update(struct bce_vhci_urb *urb, struct bce_vhci_message *msg)
++{
++ int status;
++ if (msg->cmd == BCE_VHCI_CMD_CONTROL_TRANSFER_STATUS) {
++ urb->received_status = msg->status;
++ return bce_vhci_urb_control_check_status(urb);
++ }
++
++ if (urb->state == BCE_VHCI_URB_CONTROL_WAITING_FOR_SETUP_REQUEST) {
++ if (msg->cmd == BCE_VHCI_CMD_TRANSFER_REQUEST) {
++ if (bce_vhci_urb_send_out_data(urb, urb->urb->setup_dma, sizeof(struct usb_ctrlrequest))) {
++ pr_err("bce-vhci: [%02x] Failed to start URB setup transfer\n", urb->q->endp_addr);
++ return 0; /* TODO: fail the URB? */
++ }
++ urb->state = BCE_VHCI_URB_CONTROL_WAITING_FOR_SETUP_COMPLETION;
++ pr_debug("bce-vhci: [%02x] Sent setup %llx\n", urb->q->endp_addr, urb->urb->setup_dma);
++ return 0;
++ }
++ } else if (urb->state == BCE_VHCI_URB_WAITING_FOR_TRANSFER_REQUEST ||
++ urb->state == BCE_VHCI_URB_WAITING_FOR_COMPLETION) {
++ if ((status = bce_vhci_urb_data_update(urb, msg)))
++ return status;
++ return bce_vhci_urb_control_check_status(urb);
++ }
++
++ /* 0x1000 in out queues aren't really unexpected */
++ if (msg->cmd == BCE_VHCI_CMD_TRANSFER_REQUEST && urb->q->sq_out != NULL)
++ return -EAGAIN;
++ pr_err("bce-vhci: [%02x] Control URB unexpected message (state = %x, msg: %x %x %x %llx)\n", urb->q->endp_addr,
++ urb->state, msg->cmd, msg->status, msg->param1, msg->param2);
++ return -EAGAIN;
++}
++
++static int bce_vhci_urb_control_transfer_completion(struct bce_vhci_urb *urb, struct bce_sq_completion_data *c)
++{
++ int status;
++ unsigned long timeout;
++
++ if (urb->state == BCE_VHCI_URB_CONTROL_WAITING_FOR_SETUP_COMPLETION) {
++ if (c->data_size != sizeof(struct usb_ctrlrequest))
++ pr_err("bce-vhci: [%02x] transfer complete data size mistmatch for usb_ctrlrequest (%llx instead of %lx)\n",
++ urb->q->endp_addr, c->data_size, sizeof(struct usb_ctrlrequest));
++
++ timeout = 1000;
++ status = bce_vhci_urb_data_start(urb, &timeout);
++ if (status) {
++ bce_vhci_urb_complete(urb, status);
++ return -ENOENT;
++ }
++ return 0;
++ } else if (urb->state == BCE_VHCI_URB_WAITING_FOR_TRANSFER_REQUEST ||
++ urb->state == BCE_VHCI_URB_WAITING_FOR_COMPLETION) {
++ if ((status = bce_vhci_urb_data_transfer_completion(urb, c)))
++ return status;
++ return bce_vhci_urb_control_check_status(urb);
++ } else {
++ pr_err("bce-vhci: [%02x] Control URB unexpected completion (state = %x)\n", urb->q->endp_addr, urb->state);
++ }
++ return 0;
++}
++
++static int bce_vhci_urb_update(struct bce_vhci_urb *urb, struct bce_vhci_message *msg)
++{
++ if (urb->state == BCE_VHCI_URB_INIT_PENDING)
++ return -EAGAIN;
++ if (urb->is_control)
++ return bce_vhci_urb_control_update(urb, msg);
++ else
++ return bce_vhci_urb_data_update(urb, msg);
++}
++
++static int bce_vhci_urb_transfer_completion(struct bce_vhci_urb *urb, struct bce_sq_completion_data *c)
++{
++ if (urb->is_control)
++ return bce_vhci_urb_control_transfer_completion(urb, c);
++ else
++ return bce_vhci_urb_data_transfer_completion(urb, c);
++}
++
++static void bce_vhci_urb_resume(struct bce_vhci_urb *urb)
++{
++ int status = 0;
++ if (urb->state == BCE_VHCI_URB_WAITING_FOR_COMPLETION) {
++ status = bce_vhci_urb_data_transfer_in(urb, NULL);
++ }
++ if (status)
++ bce_vhci_urb_complete(urb, status);
++}
+diff --git a/drivers/staging/apple-bce/vhci/transfer.h b/drivers/staging/apple-bce/vhci/transfer.h
+new file mode 100644
+index 000000000..6a62a00b2
+--- /dev/null
++++ b/drivers/staging/apple-bce/vhci/transfer.h
+@@ -0,0 +1,71 @@
++#ifndef BCEDRIVER_TRANSFER_H
++#define BCEDRIVER_TRANSFER_H
++
++#include <linux/usb.h>
++#include "queue.h"
++#include "command.h"
++#include "../queue.h"
++
++struct bce_vhci_list_message {
++ struct list_head list;
++ struct bce_vhci_message msg;
++};
++enum bce_vhci_pause_source {
++ BCE_VHCI_PAUSE_INTERNAL_WQ = 1,
++ BCE_VHCI_PAUSE_FIRMWARE = 2,
++ BCE_VHCI_PAUSE_SUSPEND = 4,
++ BCE_VHCI_PAUSE_SHUTDOWN = 8
++};
++struct bce_vhci_transfer_queue {
++ struct bce_vhci *vhci;
++ struct usb_host_endpoint *endp;
++ enum bce_vhci_endpoint_state state;
++ u32 max_active_requests, remaining_active_requests;
++ bool active, stalled;
++ u32 paused_by;
++ bce_vhci_device_t dev_addr;
++ u8 endp_addr;
++ struct bce_queue_cq *cq;
++ struct bce_queue_sq *sq_in;
++ struct bce_queue_sq *sq_out;
++ struct list_head evq;
++ struct spinlock urb_lock;
++ struct mutex pause_lock;
++ struct list_head giveback_urb_list;
++
++ struct work_struct w_reset;
++};
++enum bce_vhci_urb_state {
++ BCE_VHCI_URB_INIT_PENDING,
++
++ BCE_VHCI_URB_WAITING_FOR_TRANSFER_REQUEST,
++ BCE_VHCI_URB_WAITING_FOR_COMPLETION,
++ BCE_VHCI_URB_DATA_TRANSFER_COMPLETE,
++
++ BCE_VHCI_URB_CONTROL_WAITING_FOR_SETUP_REQUEST,
++ BCE_VHCI_URB_CONTROL_WAITING_FOR_SETUP_COMPLETION,
++ BCE_VHCI_URB_CONTROL_COMPLETE
++};
++struct bce_vhci_urb {
++ struct urb *urb;
++ struct bce_vhci_transfer_queue *q;
++ enum dma_data_direction dir;
++ bool is_control;
++ enum bce_vhci_urb_state state;
++ int received_status;
++ u32 send_offset;
++ u32 receive_offset;
++};
++
++void bce_vhci_create_transfer_queue(struct bce_vhci *vhci, struct bce_vhci_transfer_queue *q,
++ struct usb_host_endpoint *endp, bce_vhci_device_t dev_addr, enum dma_data_direction dir);
++void bce_vhci_destroy_transfer_queue(struct bce_vhci *vhci, struct bce_vhci_transfer_queue *q);
++void bce_vhci_transfer_queue_event(struct bce_vhci_transfer_queue *q, struct bce_vhci_message *msg);
++int bce_vhci_transfer_queue_pause(struct bce_vhci_transfer_queue *q, enum bce_vhci_pause_source src);
++int bce_vhci_transfer_queue_resume(struct bce_vhci_transfer_queue *q, enum bce_vhci_pause_source src);
++void bce_vhci_transfer_queue_request_reset(struct bce_vhci_transfer_queue *q);
++
++int bce_vhci_urb_create(struct bce_vhci_transfer_queue *q, struct urb *urb);
++int bce_vhci_urb_request_cancel(struct bce_vhci_transfer_queue *q, struct urb *urb, int status);
++
++#endif //BCEDRIVER_TRANSFER_H
+diff --git a/drivers/staging/apple-bce/vhci/vhci.c b/drivers/staging/apple-bce/vhci/vhci.c
+new file mode 100644
+index 000000000..053a9f39e
+--- /dev/null
++++ b/drivers/staging/apple-bce/vhci/vhci.c
+@@ -0,0 +1,759 @@
++#include "vhci.h"
++#include "../apple_bce.h"
++#include "command.h"
++#include <linux/usb.h>
++#include <linux/usb/hcd.h>
++#include <linux/module.h>
++#include <linux/version.h>
++
++static dev_t bce_vhci_chrdev;
++static struct class *bce_vhci_class;
++static const struct hc_driver bce_vhci_driver;
++static u16 bce_vhci_port_mask = U16_MAX;
++
++static int bce_vhci_create_event_queues(struct bce_vhci *vhci);
++static void bce_vhci_destroy_event_queues(struct bce_vhci *vhci);
++static int bce_vhci_create_message_queues(struct bce_vhci *vhci);
++static void bce_vhci_destroy_message_queues(struct bce_vhci *vhci);
++static void bce_vhci_handle_firmware_events_w(struct work_struct *ws);
++static void bce_vhci_firmware_event_completion(struct bce_queue_sq *sq);
++
++int bce_vhci_create(struct apple_bce_device *dev, struct bce_vhci *vhci)
++{
++ int status;
++
++ spin_lock_init(&vhci->hcd_spinlock);
++
++ vhci->dev = dev;
++
++ vhci->vdevt = bce_vhci_chrdev;
++ vhci->vdev = device_create(bce_vhci_class, dev->dev, vhci->vdevt, NULL, "bce-vhci");
++ if (IS_ERR_OR_NULL(vhci->vdev)) {
++ status = PTR_ERR(vhci->vdev);
++ goto fail_dev;
++ }
++
++ if ((status = bce_vhci_create_message_queues(vhci)))
++ goto fail_mq;
++ if ((status = bce_vhci_create_event_queues(vhci)))
++ goto fail_eq;
++
++ vhci->tq_state_wq = alloc_ordered_workqueue("bce-vhci-tq-state", 0);
++ INIT_WORK(&vhci->w_fw_events, bce_vhci_handle_firmware_events_w);
++
++ vhci->hcd = usb_create_hcd(&bce_vhci_driver, vhci->vdev, "bce-vhci");
++ if (!vhci->hcd) {
++ status = -ENOMEM;
++ goto fail_hcd;
++ }
++ vhci->hcd->self.sysdev = &dev->pci->dev;
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0)
++ vhci->hcd->self.uses_dma = 1;
++#endif
++ *((struct bce_vhci **) vhci->hcd->hcd_priv) = vhci;
++ vhci->hcd->speed = HCD_USB2;
++
++ if ((status = usb_add_hcd(vhci->hcd, 0, 0)))
++ goto fail_hcd;
++
++ return 0;
++
++fail_hcd:
++ bce_vhci_destroy_event_queues(vhci);
++fail_eq:
++ bce_vhci_destroy_message_queues(vhci);
++fail_mq:
++ device_destroy(bce_vhci_class, vhci->vdevt);
++fail_dev:
++ if (!status)
++ status = -EINVAL;
++ return status;
++}
++
++void bce_vhci_destroy(struct bce_vhci *vhci)
++{
++ usb_remove_hcd(vhci->hcd);
++ bce_vhci_destroy_event_queues(vhci);
++ bce_vhci_destroy_message_queues(vhci);
++ device_destroy(bce_vhci_class, vhci->vdevt);
++}
++
++struct bce_vhci *bce_vhci_from_hcd(struct usb_hcd *hcd)
++{
++ return *((struct bce_vhci **) hcd->hcd_priv);
++}
++
++int bce_vhci_start(struct usb_hcd *hcd)
++{
++ struct bce_vhci *vhci = bce_vhci_from_hcd(hcd);
++ int status;
++ u16 port_mask = 0;
++ bce_vhci_port_t port_no = 0;
++ if ((status = bce_vhci_cmd_controller_enable(&vhci->cq, 1, &port_mask)))
++ return status;
++ vhci->port_mask = port_mask;
++ vhci->port_power_mask = 0;
++ if ((status = bce_vhci_cmd_controller_start(&vhci->cq)))
++ return status;
++ port_mask = vhci->port_mask;
++ while (port_mask) {
++ port_no += 1;
++ port_mask >>= 1;
++ }
++ vhci->port_count = port_no;
++ return 0;
++}
++
++void bce_vhci_stop(struct usb_hcd *hcd)
++{
++ struct bce_vhci *vhci = bce_vhci_from_hcd(hcd);
++ bce_vhci_cmd_controller_disable(&vhci->cq);
++}
++
++static int bce_vhci_hub_status_data(struct usb_hcd *hcd, char *buf)
++{
++ return 0;
++}
++
++static int bce_vhci_reset_device(struct bce_vhci *vhci, int index, u16 timeout);
++
++static int bce_vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength)
++{
++ struct bce_vhci *vhci = bce_vhci_from_hcd(hcd);
++ int status;
++ struct usb_hub_descriptor *hd;
++ struct usb_hub_status *hs;
++ struct usb_port_status *ps;
++ u32 port_status;
++ // pr_info("bce-vhci: bce_vhci_hub_control %x %i %i [bufl=%i]\n", typeReq, wValue, wIndex, wLength);
++ if (typeReq == GetHubDescriptor && wLength >= sizeof(struct usb_hub_descriptor)) {
++ hd = (struct usb_hub_descriptor *) buf;
++ memset(hd, 0, sizeof(*hd));
++ hd->bDescLength = sizeof(struct usb_hub_descriptor);
++ hd->bDescriptorType = USB_DT_HUB;
++ hd->bNbrPorts = (u8) vhci->port_count;
++ hd->wHubCharacteristics = HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_INDV_PORT_OCPM;
++ hd->bPwrOn2PwrGood = 0;
++ hd->bHubContrCurrent = 0;
++ return 0;
++ } else if (typeReq == GetHubStatus && wLength >= sizeof(struct usb_hub_status)) {
++ hs = (struct usb_hub_status *) buf;
++ memset(hs, 0, sizeof(*hs));
++ hs->wHubStatus = 0;
++ hs->wHubChange = 0;
++ return 0;
++ } else if (typeReq == GetPortStatus && wLength >= 4 /* usb 2.0 */) {
++ ps = (struct usb_port_status *) buf;
++ ps->wPortStatus = 0;
++ ps->wPortChange = 0;
++
++ if (vhci->port_power_mask & BIT(wIndex))
++ ps->wPortStatus |= USB_PORT_STAT_POWER;
++
++ if (!(bce_vhci_port_mask & BIT(wIndex)))
++ return 0;
++
++ if ((status = bce_vhci_cmd_port_status(&vhci->cq, (u8) wIndex, 0, &port_status)))
++ return status;
++
++ if (port_status & 16)
++ ps->wPortStatus |= USB_PORT_STAT_ENABLE | USB_PORT_STAT_HIGH_SPEED;
++ if (port_status & 4)
++ ps->wPortStatus |= USB_PORT_STAT_CONNECTION;
++ if (port_status & 2)
++ ps->wPortStatus |= USB_PORT_STAT_OVERCURRENT;
++ if (port_status & 8)
++ ps->wPortStatus |= USB_PORT_STAT_RESET;
++ if (port_status & 0x60)
++ ps->wPortStatus |= USB_PORT_STAT_SUSPEND;
++
++ if (port_status & 0x40000)
++ ps->wPortChange |= USB_PORT_STAT_C_CONNECTION;
++
++ pr_debug("bce-vhci: Translated status %x to %x:%x\n", port_status, ps->wPortStatus, ps->wPortChange);
++ return 0;
++ } else if (typeReq == SetPortFeature) {
++ if (wValue == USB_PORT_FEAT_POWER) {
++ status = bce_vhci_cmd_port_power_on(&vhci->cq, (u8) wIndex);
++ /* As far as I am aware, power status is not part of the port status so store it separately */
++ if (!status)
++ vhci->port_power_mask |= BIT(wIndex);
++ return status;
++ }
++ if (wValue == USB_PORT_FEAT_RESET) {
++ return bce_vhci_reset_device(vhci, wIndex, wValue);
++ }
++ if (wValue == USB_PORT_FEAT_SUSPEND) {
++ /* TODO: Am I supposed to also suspend the endpoints? */
++ pr_debug("bce-vhci: Suspending port %i\n", wIndex);
++ return bce_vhci_cmd_port_suspend(&vhci->cq, (u8) wIndex);
++ }
++ } else if (typeReq == ClearPortFeature) {
++ if (wValue == USB_PORT_FEAT_ENABLE)
++ return bce_vhci_cmd_port_disable(&vhci->cq, (u8) wIndex);
++ if (wValue == USB_PORT_FEAT_POWER) {
++ status = bce_vhci_cmd_port_power_off(&vhci->cq, (u8) wIndex);
++ if (!status)
++ vhci->port_power_mask &= ~BIT(wIndex);
++ return status;
++ }
++ if (wValue == USB_PORT_FEAT_C_CONNECTION)
++ return bce_vhci_cmd_port_status(&vhci->cq, (u8) wIndex, 0x40000, &port_status);
++ if (wValue == USB_PORT_FEAT_C_RESET) { /* I don't think I can transfer it in any way */
++ return 0;
++ }
++ if (wValue == USB_PORT_FEAT_SUSPEND) {
++ pr_debug("bce-vhci: Resuming port %i\n", wIndex);
++ return bce_vhci_cmd_port_resume(&vhci->cq, (u8) wIndex);
++ }
++ }
++ pr_err("bce-vhci: bce_vhci_hub_control unhandled request: %x %i %i [bufl=%i]\n", typeReq, wValue, wIndex, wLength);
++ dump_stack();
++ return -EIO;
++}
++
++static int bce_vhci_enable_device(struct usb_hcd *hcd, struct usb_device *udev)
++{
++ struct bce_vhci *vhci = bce_vhci_from_hcd(hcd);
++ struct bce_vhci_device *vdev;
++ bce_vhci_device_t devid;
++ pr_info("bce_vhci_enable_device\n");
++
++ if (vhci->port_to_device[udev->portnum])
++ return 0;
++
++ /* We need to early address the device */
++ if (bce_vhci_cmd_device_create(&vhci->cq, udev->portnum, &devid))
++ return -EIO;
++
++ pr_info("bce_vhci_cmd_device_create %i -> %i\n", udev->portnum, devid);
++
++ vdev = kzalloc(sizeof(struct bce_vhci_device), GFP_KERNEL);
++ vhci->port_to_device[udev->portnum] = devid;
++ vhci->devices[devid] = vdev;
++
++ bce_vhci_create_transfer_queue(vhci, &vdev->tq[0], &udev->ep0, devid, DMA_BIDIRECTIONAL);
++ udev->ep0.hcpriv = &vdev->tq[0];
++ vdev->tq_mask |= BIT(0);
++
++ bce_vhci_cmd_endpoint_create(&vhci->cq, devid, &udev->ep0.desc);
++ return 0;
++}
++
++static int bce_vhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
++{
++ /* This is the same as enable_device, but instead in the old scheme */
++ return bce_vhci_enable_device(hcd, udev);
++}
++
++static void bce_vhci_free_device(struct usb_hcd *hcd, struct usb_device *udev)
++{
++ struct bce_vhci *vhci = bce_vhci_from_hcd(hcd);
++ int i;
++ bce_vhci_device_t devid;
++ struct bce_vhci_device *dev;
++ pr_info("bce_vhci_free_device %i\n", udev->portnum);
++ if (!vhci->port_to_device[udev->portnum])
++ return;
++ devid = vhci->port_to_device[udev->portnum];
++ dev = vhci->devices[devid];
++ for (i = 0; i < 32; i++) {
++ if (dev->tq_mask & BIT(i)) {
++ bce_vhci_transfer_queue_pause(&dev->tq[i], BCE_VHCI_PAUSE_SHUTDOWN);
++ bce_vhci_cmd_endpoint_destroy(&vhci->cq, devid, (u8) i);
++ bce_vhci_destroy_transfer_queue(vhci, &dev->tq[i]);
++ }
++ }
++ vhci->devices[devid] = NULL;
++ vhci->port_to_device[udev->portnum] = 0;
++ bce_vhci_cmd_device_destroy(&vhci->cq, devid);
++ kfree(dev);
++}
++
++static int bce_vhci_reset_device(struct bce_vhci *vhci, int index, u16 timeout)
++{
++ struct bce_vhci_device *dev = NULL;
++ bce_vhci_device_t devid;
++ int i;
++ int status;
++ enum dma_data_direction dir;
++ pr_info("bce_vhci_reset_device %i\n", index);
++
++ devid = vhci->port_to_device[index];
++ if (devid) {
++ dev = vhci->devices[devid];
++
++ for (i = 0; i < 32; i++) {
++ if (dev->tq_mask & BIT(i)) {
++ bce_vhci_transfer_queue_pause(&dev->tq[i], BCE_VHCI_PAUSE_SHUTDOWN);
++ bce_vhci_cmd_endpoint_destroy(&vhci->cq, devid, (u8) i);
++ bce_vhci_destroy_transfer_queue(vhci, &dev->tq[i]);
++ }
++ }
++ vhci->devices[devid] = NULL;
++ vhci->port_to_device[index] = 0;
++ bce_vhci_cmd_device_destroy(&vhci->cq, devid);
++ }
++ status = bce_vhci_cmd_port_reset(&vhci->cq, (u8) index, timeout);
++
++ if (dev) {
++ if ((status = bce_vhci_cmd_device_create(&vhci->cq, index, &devid)))
++ return status;
++ vhci->devices[devid] = dev;
++ vhci->port_to_device[index] = devid;
++
++ for (i = 0; i < 32; i++) {
++ if (dev->tq_mask & BIT(i)) {
++ dir = usb_endpoint_dir_in(&dev->tq[i].endp->desc) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
++ if (i == 0)
++ dir = DMA_BIDIRECTIONAL;
++ bce_vhci_create_transfer_queue(vhci, &dev->tq[i], dev->tq[i].endp, devid, dir);
++ bce_vhci_cmd_endpoint_create(&vhci->cq, devid, &dev->tq[i].endp->desc);
++ }
++ }
++ }
++
++ return status;
++}
++
++static int bce_vhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
++{
++ return 0;
++}
++
++static int bce_vhci_get_frame_number(struct usb_hcd *hcd)
++{
++ return 0;
++}
++
++static int bce_vhci_bus_suspend(struct usb_hcd *hcd)
++{
++ int i, j;
++ int status;
++ struct bce_vhci *vhci = bce_vhci_from_hcd(hcd);
++ pr_info("bce_vhci: suspend started\n");
++
++ pr_info("bce_vhci: suspend endpoints\n");
++ for (i = 0; i < 16; i++) {
++ if (!vhci->port_to_device[i])
++ continue;
++ for (j = 0; j < 32; j++) {
++ if (!(vhci->devices[vhci->port_to_device[i]]->tq_mask & BIT(j)))
++ continue;
++ bce_vhci_transfer_queue_pause(&vhci->devices[vhci->port_to_device[i]]->tq[j],
++ BCE_VHCI_PAUSE_SUSPEND);
++ }
++ }
++
++ pr_info("bce_vhci: suspend ports\n");
++ for (i = 0; i < 16; i++) {
++ if (!vhci->port_to_device[i])
++ continue;
++ bce_vhci_cmd_port_suspend(&vhci->cq, i);
++ }
++ pr_info("bce_vhci: suspend controller\n");
++ if ((status = bce_vhci_cmd_controller_pause(&vhci->cq)))
++ return status;
++
++ bce_vhci_event_queue_pause(&vhci->ev_commands);
++ bce_vhci_event_queue_pause(&vhci->ev_system);
++ bce_vhci_event_queue_pause(&vhci->ev_isochronous);
++ bce_vhci_event_queue_pause(&vhci->ev_interrupt);
++ bce_vhci_event_queue_pause(&vhci->ev_asynchronous);
++ pr_info("bce_vhci: suspend done\n");
++ return 0;
++}
++
++static int bce_vhci_bus_resume(struct usb_hcd *hcd)
++{
++ int i, j;
++ int status;
++ struct bce_vhci *vhci = bce_vhci_from_hcd(hcd);
++ pr_info("bce_vhci: resume started\n");
++
++ bce_vhci_event_queue_resume(&vhci->ev_system);
++ bce_vhci_event_queue_resume(&vhci->ev_isochronous);
++ bce_vhci_event_queue_resume(&vhci->ev_interrupt);
++ bce_vhci_event_queue_resume(&vhci->ev_asynchronous);
++ bce_vhci_event_queue_resume(&vhci->ev_commands);
++
++ pr_info("bce_vhci: resume controller\n");
++ if ((status = bce_vhci_cmd_controller_start(&vhci->cq)))
++ return status;
++
++ pr_info("bce_vhci: resume ports\n");
++ for (i = 0; i < 16; i++) {
++ if (!vhci->port_to_device[i])
++ continue;
++ bce_vhci_cmd_port_resume(&vhci->cq, i);
++ }
++ pr_info("bce_vhci: resume endpoints\n");
++ for (i = 0; i < 16; i++) {
++ if (!vhci->port_to_device[i])
++ continue;
++ for (j = 0; j < 32; j++) {
++ if (!(vhci->devices[vhci->port_to_device[i]]->tq_mask & BIT(j)))
++ continue;
++ bce_vhci_transfer_queue_resume(&vhci->devices[vhci->port_to_device[i]]->tq[j],
++ BCE_VHCI_PAUSE_SUSPEND);
++ }
++ }
++
++ pr_info("bce_vhci: resume done\n");
++ return 0;
++}
++
++static int bce_vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
++{
++ struct bce_vhci_transfer_queue *q = urb->ep->hcpriv;
++ pr_debug("bce_vhci_urb_enqueue %i:%x\n", q->dev_addr, urb->ep->desc.bEndpointAddress);
++ if (!q)
++ return -ENOENT;
++ return bce_vhci_urb_create(q, urb);
++}
++
++static int bce_vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
++{
++ struct bce_vhci_transfer_queue *q = urb->ep->hcpriv;
++ pr_debug("bce_vhci_urb_dequeue %x\n", urb->ep->desc.bEndpointAddress);
++ return bce_vhci_urb_request_cancel(q, urb, status);
++}
++
++static void bce_vhci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
++{
++ struct bce_vhci_transfer_queue *q = ep->hcpriv;
++ pr_debug("bce_vhci_endpoint_reset\n");
++ if (q)
++ bce_vhci_transfer_queue_request_reset(q);
++}
++
++static u8 bce_vhci_endpoint_index(u8 addr)
++{
++ if (addr & 0x80)
++ return (u8) (0x10 + (addr & 0xf));
++ return (u8) (addr & 0xf);
++}
++
++static int bce_vhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *endp)
++{
++ u8 endp_index = bce_vhci_endpoint_index(endp->desc.bEndpointAddress);
++ struct bce_vhci *vhci = bce_vhci_from_hcd(hcd);
++ bce_vhci_device_t devid = vhci->port_to_device[udev->portnum];
++ struct bce_vhci_device *vdev = vhci->devices[devid];
++ pr_debug("bce_vhci_add_endpoint %x/%x:%x\n", udev->portnum, devid, endp_index);
++
++ if (udev->bus->root_hub == udev) /* The USB hub */
++ return 0;
++ if (vdev == NULL)
++ return -ENODEV;
++ if (vdev->tq_mask & BIT(endp_index)) {
++ endp->hcpriv = &vdev->tq[endp_index];
++ return 0;
++ }
++
++ bce_vhci_create_transfer_queue(vhci, &vdev->tq[endp_index], endp, devid,
++ usb_endpoint_dir_in(&endp->desc) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
++ endp->hcpriv = &vdev->tq[endp_index];
++ vdev->tq_mask |= BIT(endp_index);
++
++ bce_vhci_cmd_endpoint_create(&vhci->cq, devid, &endp->desc);
++ return 0;
++}
++
++static int bce_vhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *endp)
++{
++ u8 endp_index = bce_vhci_endpoint_index(endp->desc.bEndpointAddress);
++ struct bce_vhci *vhci = bce_vhci_from_hcd(hcd);
++ bce_vhci_device_t devid = vhci->port_to_device[udev->portnum];
++ struct bce_vhci_transfer_queue *q = endp->hcpriv;
++ struct bce_vhci_device *vdev = vhci->devices[devid];
++ pr_info("bce_vhci_drop_endpoint %x:%x\n", udev->portnum, endp_index);
++ if (!q) {
++ if (vdev && vdev->tq_mask & BIT(endp_index)) {
++ pr_err("something deleted the hcpriv?\n");
++ q = &vdev->tq[endp_index];
++ } else {
++ return 0;
++ }
++ }
++
++ bce_vhci_cmd_endpoint_destroy(&vhci->cq, devid, (u8) (endp->desc.bEndpointAddress & 0x8Fu));
++ vhci->devices[devid]->tq_mask &= ~BIT(endp_index);
++ bce_vhci_destroy_transfer_queue(vhci, q);
++ return 0;
++}
++
++static int bce_vhci_create_message_queues(struct bce_vhci *vhci)
++{
++ if (bce_vhci_message_queue_create(vhci, &vhci->msg_commands, "VHC1HostCommands") ||
++ bce_vhci_message_queue_create(vhci, &vhci->msg_system, "VHC1HostSystemEvents") ||
++ bce_vhci_message_queue_create(vhci, &vhci->msg_isochronous, "VHC1HostIsochronousEvents") ||
++ bce_vhci_message_queue_create(vhci, &vhci->msg_interrupt, "VHC1HostInterruptEvents") ||
++ bce_vhci_message_queue_create(vhci, &vhci->msg_asynchronous, "VHC1HostAsynchronousEvents")) {
++ bce_vhci_destroy_message_queues(vhci);
++ return -EINVAL;
++ }
++ spin_lock_init(&vhci->msg_asynchronous_lock);
++ bce_vhci_command_queue_create(&vhci->cq, &vhci->msg_commands);
++ return 0;
++}
++
++static void bce_vhci_destroy_message_queues(struct bce_vhci *vhci)
++{
++ bce_vhci_command_queue_destroy(&vhci->cq);
++ bce_vhci_message_queue_destroy(vhci, &vhci->msg_commands);
++ bce_vhci_message_queue_destroy(vhci, &vhci->msg_system);
++ bce_vhci_message_queue_destroy(vhci, &vhci->msg_isochronous);
++ bce_vhci_message_queue_destroy(vhci, &vhci->msg_interrupt);
++ bce_vhci_message_queue_destroy(vhci, &vhci->msg_asynchronous);
++}
++
++static void bce_vhci_handle_system_event(struct bce_vhci_event_queue *q, struct bce_vhci_message *msg);
++static void bce_vhci_handle_usb_event(struct bce_vhci_event_queue *q, struct bce_vhci_message *msg);
++
++static int bce_vhci_create_event_queues(struct bce_vhci *vhci)
++{
++ vhci->ev_cq = bce_create_cq(vhci->dev, 0x100);
++ if (!vhci->ev_cq)
++ return -EINVAL;
++#define CREATE_EVENT_QUEUE(field, name, cb) bce_vhci_event_queue_create(vhci, &vhci->field, name, cb)
++ if (__bce_vhci_event_queue_create(vhci, &vhci->ev_commands, "VHC1FirmwareCommands",
++ bce_vhci_firmware_event_completion) ||
++ CREATE_EVENT_QUEUE(ev_system, "VHC1FirmwareSystemEvents", bce_vhci_handle_system_event) ||
++ CREATE_EVENT_QUEUE(ev_isochronous, "VHC1FirmwareIsochronousEvents", bce_vhci_handle_usb_event) ||
++ CREATE_EVENT_QUEUE(ev_interrupt, "VHC1FirmwareInterruptEvents", bce_vhci_handle_usb_event) ||
++ CREATE_EVENT_QUEUE(ev_asynchronous, "VHC1FirmwareAsynchronousEvents", bce_vhci_handle_usb_event)) {
++ bce_vhci_destroy_event_queues(vhci);
++ return -EINVAL;
++ }
++#undef CREATE_EVENT_QUEUE
++ return 0;
++}
++
++static void bce_vhci_destroy_event_queues(struct bce_vhci *vhci)
++{
++ bce_vhci_event_queue_destroy(vhci, &vhci->ev_commands);
++ bce_vhci_event_queue_destroy(vhci, &vhci->ev_system);
++ bce_vhci_event_queue_destroy(vhci, &vhci->ev_isochronous);
++ bce_vhci_event_queue_destroy(vhci, &vhci->ev_interrupt);
++ bce_vhci_event_queue_destroy(vhci, &vhci->ev_asynchronous);
++ if (vhci->ev_cq)
++ bce_destroy_cq(vhci->dev, vhci->ev_cq);
++}
++
++static void bce_vhci_send_fw_event_response(struct bce_vhci *vhci, struct bce_vhci_message *req, u16 status)
++{
++ unsigned long timeout = 1000;
++ struct bce_vhci_message r = *req;
++ r.cmd = (u16) (req->cmd | 0x8000u);
++ r.status = status;
++ r.param1 = req->param1;
++ r.param2 = 0;
++
++ if (bce_reserve_submission(vhci->msg_system.sq, &timeout)) {
++ pr_err("bce-vhci: Cannot reserve submision for FW event reply\n");
++ return;
++ }
++ bce_vhci_message_queue_write(&vhci->msg_system, &r);
++}
++
++static int bce_vhci_handle_firmware_event(struct bce_vhci *vhci, struct bce_vhci_message *msg)
++{
++ unsigned long flags;
++ bce_vhci_device_t devid;
++ u8 endp;
++ struct bce_vhci_device *dev;
++ struct bce_vhci_transfer_queue *tq;
++ if (msg->cmd == BCE_VHCI_CMD_ENDPOINT_REQUEST_STATE || msg->cmd == BCE_VHCI_CMD_ENDPOINT_SET_STATE) {
++ devid = (bce_vhci_device_t) (msg->param1 & 0xff);
++ endp = bce_vhci_endpoint_index((u8) ((msg->param1 >> 8) & 0xff));
++ dev = vhci->devices[devid];
++ if (!dev || !(dev->tq_mask & BIT(endp)))
++ return BCE_VHCI_BAD_ARGUMENT;
++ tq = &dev->tq[endp];
++ }
++
++ if (msg->cmd == BCE_VHCI_CMD_ENDPOINT_REQUEST_STATE) {
++ if (msg->param2 == BCE_VHCI_ENDPOINT_ACTIVE) {
++ bce_vhci_transfer_queue_resume(tq, BCE_VHCI_PAUSE_FIRMWARE);
++ return BCE_VHCI_SUCCESS;
++ } else if (msg->param2 == BCE_VHCI_ENDPOINT_PAUSED) {
++ bce_vhci_transfer_queue_pause(tq, BCE_VHCI_PAUSE_FIRMWARE);
++ return BCE_VHCI_SUCCESS;
++ }
++ return BCE_VHCI_BAD_ARGUMENT;
++ } else if (msg->cmd == BCE_VHCI_CMD_ENDPOINT_SET_STATE) {
++ if (msg->param2 == BCE_VHCI_ENDPOINT_STALLED) {
++ tq->state = msg->param2;
++ spin_lock_irqsave(&tq->urb_lock, flags);
++ tq->stalled = true;
++ spin_unlock_irqrestore(&tq->urb_lock, flags);
++ return BCE_VHCI_SUCCESS;
++ }
++ return BCE_VHCI_BAD_ARGUMENT;
++ }
++ pr_warn("bce-vhci: Unhandled firmware event: %x s=%x p1=%x p2=%llx\n",
++ msg->cmd, msg->status, msg->param1, msg->param2);
++ return BCE_VHCI_BAD_ARGUMENT;
++}
++
++static void bce_vhci_handle_firmware_events_w(struct work_struct *ws)
++{
++ size_t cnt = 0;
++ int result;
++ struct bce_vhci *vhci = container_of(ws, struct bce_vhci, w_fw_events);
++ struct bce_queue_sq *sq = vhci->ev_commands.sq;
++ struct bce_sq_completion_data *cq;
++ struct bce_vhci_message *msg, *msg2 = NULL;
++
++ while (true) {
++ if (msg2) {
++ msg = msg2;
++ msg2 = NULL;
++ } else if ((cq = bce_next_completion(sq))) {
++ if (cq->status == BCE_COMPLETION_ABORTED) {
++ bce_notify_submission_complete(sq);
++ continue;
++ }
++ msg = &vhci->ev_commands.data[sq->head];
++ } else {
++ break;
++ }
++
++ pr_debug("bce-vhci: Got fw event: %x s=%x p1=%x p2=%llx\n", msg->cmd, msg->status, msg->param1, msg->param2);
++ if ((cq = bce_next_completion(sq))) {
++ msg2 = &vhci->ev_commands.data[(sq->head + 1) % sq->el_count];
++ pr_debug("bce-vhci: Got second fw event: %x s=%x p1=%x p2=%llx\n",
++ msg->cmd, msg->status, msg->param1, msg->param2);
++ if (cq->status != BCE_COMPLETION_ABORTED &&
++ msg2->cmd == (msg->cmd | 0x4000) && msg2->param1 == msg->param1) {
++ /* Take two elements */
++ pr_debug("bce-vhci: Cancelled\n");
++ bce_vhci_send_fw_event_response(vhci, msg, BCE_VHCI_ABORT);
++
++ bce_notify_submission_complete(sq);
++ bce_notify_submission_complete(sq);
++ msg2 = NULL;
++ cnt += 2;
++ continue;
++ }
++
++ pr_warn("bce-vhci: Handle fw event - unexpected cancellation\n");
++ }
++
++ result = bce_vhci_handle_firmware_event(vhci, msg);
++ bce_vhci_send_fw_event_response(vhci, msg, (u16) result);
++
++
++ bce_notify_submission_complete(sq);
++ ++cnt;
++ }
++ bce_vhci_event_queue_submit_pending(&vhci->ev_commands, cnt);
++ if (atomic_read(&sq->available_commands) == sq->el_count - 1) {
++ pr_debug("bce-vhci: complete\n");
++ complete(&vhci->ev_commands.queue_empty_completion);
++ }
++}
++
++static void bce_vhci_firmware_event_completion(struct bce_queue_sq *sq)
++{
++ struct bce_vhci_event_queue *q = sq->userdata;
++ queue_work(q->vhci->tq_state_wq, &q->vhci->w_fw_events);
++}
++
++static void bce_vhci_handle_system_event(struct bce_vhci_event_queue *q, struct bce_vhci_message *msg)
++{
++ if (msg->cmd & 0x8000) {
++ bce_vhci_command_queue_deliver_completion(&q->vhci->cq, msg);
++ } else {
++ pr_warn("bce-vhci: Unhandled system event: %x s=%x p1=%x p2=%llx\n",
++ msg->cmd, msg->status, msg->param1, msg->param2);
++ }
++}
++
++static void bce_vhci_handle_usb_event(struct bce_vhci_event_queue *q, struct bce_vhci_message *msg)
++{
++ bce_vhci_device_t devid;
++ u8 endp;
++ struct bce_vhci_device *dev;
++ if (msg->cmd & 0x8000) {
++ bce_vhci_command_queue_deliver_completion(&q->vhci->cq, msg);
++ } else if (msg->cmd == BCE_VHCI_CMD_TRANSFER_REQUEST || msg->cmd == BCE_VHCI_CMD_CONTROL_TRANSFER_STATUS) {
++ devid = (bce_vhci_device_t) (msg->param1 & 0xff);
++ endp = bce_vhci_endpoint_index((u8) ((msg->param1 >> 8) & 0xff));
++ dev = q->vhci->devices[devid];
++ if (!dev || (dev->tq_mask & BIT(endp)) == 0) {
++ pr_err("bce-vhci: Didn't find destination for transfer queue event\n");
++ return;
++ }
++ bce_vhci_transfer_queue_event(&dev->tq[endp], msg);
++ } else {
++ pr_warn("bce-vhci: Unhandled USB event: %x s=%x p1=%x p2=%llx\n",
++ msg->cmd, msg->status, msg->param1, msg->param2);
++ }
++}
++
++
++
++static const struct hc_driver bce_vhci_driver = {
++ .description = "bce-vhci",
++ .product_desc = "BCE VHCI Host Controller",
++ .hcd_priv_size = sizeof(struct bce_vhci *),
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0)
++ .flags = HCD_USB2,
++#else
++ .flags = HCD_USB2 | HCD_DMA,
++#endif
++
++ .start = bce_vhci_start,
++ .stop = bce_vhci_stop,
++ .hub_status_data = bce_vhci_hub_status_data,
++ .hub_control = bce_vhci_hub_control,
++ .urb_enqueue = bce_vhci_urb_enqueue,
++ .urb_dequeue = bce_vhci_urb_dequeue,
++ .enable_device = bce_vhci_enable_device,
++ .free_dev = bce_vhci_free_device,
++ .address_device = bce_vhci_address_device,
++ .add_endpoint = bce_vhci_add_endpoint,
++ .drop_endpoint = bce_vhci_drop_endpoint,
++ .endpoint_reset = bce_vhci_endpoint_reset,
++ .check_bandwidth = bce_vhci_check_bandwidth,
++ .get_frame_number = bce_vhci_get_frame_number,
++ .bus_suspend = bce_vhci_bus_suspend,
++ .bus_resume = bce_vhci_bus_resume
++};
++
++
++int __init bce_vhci_module_init(void)
++{
++ int result;
++ if ((result = alloc_chrdev_region(&bce_vhci_chrdev, 0, 1, "bce-vhci")))
++ goto fail_chrdev;
++#if LINUX_VERSION_CODE < KERNEL_VERSION(6,4,0)
++ bce_vhci_class = class_create(THIS_MODULE, "bce-vhci");
++#else
++ bce_vhci_class = class_create("bce-vhci");
++#endif
++ if (IS_ERR(bce_vhci_class)) {
++ result = PTR_ERR(bce_vhci_class);
++ goto fail_class;
++ }
++ return 0;
++
++fail_class:
++ class_destroy(bce_vhci_class);
++fail_chrdev:
++ unregister_chrdev_region(bce_vhci_chrdev, 1);
++ if (!result)
++ result = -EINVAL;
++ return result;
++}
++void __exit bce_vhci_module_exit(void)
++{
++ class_destroy(bce_vhci_class);
++ unregister_chrdev_region(bce_vhci_chrdev, 1);
++}
++
++module_param_named(vhci_port_mask, bce_vhci_port_mask, ushort, 0444);
++MODULE_PARM_DESC(vhci_port_mask, "Specifies which VHCI ports are enabled");
+diff --git a/drivers/staging/apple-bce/vhci/vhci.h b/drivers/staging/apple-bce/vhci/vhci.h
+new file mode 100644
+index 000000000..90641d1ba
+--- /dev/null
++++ b/drivers/staging/apple-bce/vhci/vhci.h
+@@ -0,0 +1,48 @@
++#ifndef BCE_VHCI_H
++#define BCE_VHCI_H
++
++#include "queue.h"
++#include "transfer.h"
++
++struct usb_hcd;
++struct bce_queue_cq;
++
++struct bce_vhci_device {
++ struct bce_vhci_transfer_queue tq[32];
++ u32 tq_mask;
++};
++struct bce_vhci {
++ struct apple_bce_device *dev;
++ dev_t vdevt;
++ struct device *vdev;
++ struct usb_hcd *hcd;
++ struct spinlock hcd_spinlock;
++ struct bce_vhci_message_queue msg_commands;
++ struct bce_vhci_message_queue msg_system;
++ struct bce_vhci_message_queue msg_isochronous;
++ struct bce_vhci_message_queue msg_interrupt;
++ struct bce_vhci_message_queue msg_asynchronous;
++ struct spinlock msg_asynchronous_lock;
++ struct bce_vhci_command_queue cq;
++ struct bce_queue_cq *ev_cq;
++ struct bce_vhci_event_queue ev_commands;
++ struct bce_vhci_event_queue ev_system;
++ struct bce_vhci_event_queue ev_isochronous;
++ struct bce_vhci_event_queue ev_interrupt;
++ struct bce_vhci_event_queue ev_asynchronous;
++ u16 port_mask;
++ u8 port_count;
++ u16 port_power_mask;
++ bce_vhci_device_t port_to_device[16];
++ struct bce_vhci_device *devices[16];
++ struct workqueue_struct *tq_state_wq;
++ struct work_struct w_fw_events;
++};
++
++int __init bce_vhci_module_init(void);
++void __exit bce_vhci_module_exit(void);
++
++int bce_vhci_create(struct apple_bce_device *dev, struct bce_vhci *vhci);
++void bce_vhci_destroy(struct bce_vhci *vhci);
++
++#endif //BCE_VHCI_H
+--
+2.39.2
+
+From dc27d4db5787546ae5eacf3483f3b87f2d4fb1c1 Mon Sep 17 00:00:00 2001
+From: Redecorating <69827514+Redecorating@users.noreply.github.com>
+Date: Mon, 7 Nov 2022 14:56:34 +0530
+Subject: [PATCH] Put apple-bce in drivers/staging
+
+---
+ drivers/staging/Kconfig | 2 ++
+ drivers/staging/Makefile | 1 +
+ drivers/staging/apple-bce/Kconfig | 18 ++++++++++++++++++
+ drivers/staging/apple-bce/Makefile | 2 +-
+ 4 files changed, 22 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/staging/apple-bce/Kconfig
+
+diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
+index 5cfabd537..3b8e61d26 100644
+--- a/drivers/staging/Kconfig
++++ b/drivers/staging/Kconfig
+@@ -80,4 +80,6 @@ source "drivers/staging/qlge/Kconfig"
+
+ source "drivers/staging/vme_user/Kconfig"
+
++source "drivers/staging/apple-bce/Kconfig"
++
+ endif # STAGING
+diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
+index f8c3aa9c2..1e148d6c3 100644
+--- a/drivers/staging/Makefile
++++ b/drivers/staging/Makefile
+@@ -29,3 +29,4 @@ obj-$(CONFIG_PI433) += pi433/
+ obj-$(CONFIG_PI433) += pi433/
+ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
+ obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/
++obj-$(CONFIG_APPLE_BCE) += apple-bce/
+diff --git a/drivers/staging/apple-bce/Kconfig b/drivers/staging/apple-bce/Kconfig
+new file mode 100644
+index 000000000..fe92bc441
+--- /dev/null
++++ b/drivers/staging/apple-bce/Kconfig
+@@ -0,0 +1,18 @@
++config APPLE_BCE
++ tristate "Apple BCE driver (VHCI and Audio support)"
++ default m
++ depends on X86
++ select SOUND
++ select SND
++ select SND_PCM
++ select SND_JACK
++ help
++ VHCI and audio support on Apple MacBooks with the T2 Chip.
++ This driver is divided in three components:
++ - BCE (Buffer Copy Engine): which establishes a basic communication
++ channel with the T2 chip. This component is required by the other two:
++ - VHCI (Virtual Host Controller Interface): Access to keyboard, mouse
++ and other system devices depend on this virtual USB host controller
++ - Audio: a driver for the T2 audio interface.
++
++ If "M" is selected, the module will be called apple-bce.'
+diff --git a/drivers/staging/apple-bce/Makefile b/drivers/staging/apple-bce/Makefile
+index a6a656f06..8cfbd3f64 100644
+--- a/drivers/staging/apple-bce/Makefile
++++ b/drivers/staging/apple-bce/Makefile
+@@ -1,5 +1,5 @@
+ modname := apple-bce
+-obj-m += $(modname).o
++obj-$(CONFIG_APPLE_BCE) += $(modname).o
+
+ apple-bce-objs := apple_bce.o mailbox.o queue.o queue_dma.o vhci/vhci.o vhci/queue.o vhci/transfer.o audio/audio.o audio/protocol.o audio/protocol_bce.o audio/pcm.o
+
+--
+2.34.1
+From 153b587ed53135eaf244144f6f8bdd5a0fe6b69e Mon Sep 17 00:00:00 2001
+From: Redecorating <69827514+Redecorating@users.noreply.github.com>
+Date: Fri, 24 Dec 2021 18:12:25 +1100
+Subject: [PATCH 1/1] add modalias to apple-bce
+
+---
+ drivers/staging/apple-bce/apple_bce.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/staging/apple-bce/apple_bce.c b/drivers/staging/apple-bce/apple_bce.c
+index ad89632df..5e2f2f3b9 100644
+--- a/drivers/staging/apple-bce/apple_bce.c
++++ b/drivers/staging/apple-bce/apple_bce.c
+@@ -439,5 +439,6 @@ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("MrARM");
+ MODULE_DESCRIPTION("Apple BCE Driver");
+ MODULE_VERSION("0.01");
++MODULE_ALIAS("pci:v0000106Bd00001801sv*sd*bc*sc*i*");
+ module_init(apple_bce_module_init);
+ module_exit(apple_bce_module_exit);
+--
+2.43.0
+
+From 75ca57b64ce6846622d8aefac5a76fc638a2123d Mon Sep 17 00:00:00 2001
+From: Kerem Karabay <kekrby@gmail.com>
+Date: Sun, 5 Mar 2023 19:12:53 +0300
+Subject: [PATCH 01/12] HID: core: add helper for finding a field with a
+ certain usage
+
+This helper will allow HID drivers to easily determine if they should
+bind to a hid_device by checking for the prescence of a certain field
+when its ID is not enough, which can be the case on USB devices with
+multiple interfaces and/or configurations.
+
+Signed-off-by: Kerem Karabay <kekrby@gmail.com>
+---
+ drivers/hid/hid-core.c | 25 +++++++++++++++++++++++++
+ drivers/hid/hid-google-hammer.c | 27 ++-------------------------
+ include/linux/hid.h | 2 ++
+ 3 files changed, 29 insertions(+), 25 deletions(-)
+
+diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
+index 8992e3c1e..6395bdc2e 100644
+--- a/drivers/hid/hid-core.c
++++ b/drivers/hid/hid-core.c
+@@ -1906,6 +1906,31 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
+ }
+ EXPORT_SYMBOL_GPL(hid_set_field);
+
++struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_type,
++ unsigned int application, unsigned int usage)
++{
++ struct list_head *report_list = &hdev->report_enum[report_type].report_list;
++ struct hid_report *report;
++ int i, j;
++
++ list_for_each_entry(report, report_list, list) {
++ if (report->application != application)
++ continue;
++
++ for (i = 0; i < report->maxfield; i++) {
++ struct hid_field *field = report->field[i];
++
++ for (j = 0; j < field->maxusage; j++) {
++ if (field->usage[j].hid == usage)
++ return field;
++ }
++ }
++ }
++
++ return NULL;
++}
++EXPORT_SYMBOL_GPL(hid_find_field);
++
+ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,
+ const u8 *data)
+ {
+diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c
+index c6bdb9c4e..fba3652aa 100644
+--- a/drivers/hid/hid-google-hammer.c
++++ b/drivers/hid/hid-google-hammer.c
+@@ -419,38 +419,15 @@ static int hammer_event(struct hid_device *hid, struct hid_field *field,
+ return 0;
+ }
+
+-static bool hammer_has_usage(struct hid_device *hdev, unsigned int report_type,
+- unsigned application, unsigned usage)
+-{
+- struct hid_report_enum *re = &hdev->report_enum[report_type];
+- struct hid_report *report;
+- int i, j;
+-
+- list_for_each_entry(report, &re->report_list, list) {
+- if (report->application != application)
+- continue;
+-
+- for (i = 0; i < report->maxfield; i++) {
+- struct hid_field *field = report->field[i];
+-
+- for (j = 0; j < field->maxusage; j++)
+- if (field->usage[j].hid == usage)
+- return true;
+- }
+- }
+-
+- return false;
+-}
+-
+ static bool hammer_has_folded_event(struct hid_device *hdev)
+ {
+- return hammer_has_usage(hdev, HID_INPUT_REPORT,
++ return !!hid_find_field(hdev, HID_INPUT_REPORT,
+ HID_GD_KEYBOARD, HID_USAGE_KBD_FOLDED);
+ }
+
+ static bool hammer_has_backlight_control(struct hid_device *hdev)
+ {
+- return hammer_has_usage(hdev, HID_OUTPUT_REPORT,
++ return !!hid_find_field(hdev, HID_OUTPUT_REPORT,
+ HID_GD_KEYBOARD, HID_AD_BRIGHTNESS);
+ }
+
+diff --git a/include/linux/hid.h b/include/linux/hid.h
+index 39e21e381..9520fdfdd 100644
+--- a/include/linux/hid.h
++++ b/include/linux/hid.h
+@@ -913,6 +913,8 @@ extern void hidinput_report_event(struct hid_device *hid, struct hid_report *rep
+ extern int hidinput_connect(struct hid_device *hid, unsigned int force);
+ extern void hidinput_disconnect(struct hid_device *);
+
++struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_type,
++ unsigned int application, unsigned int usage);
+ int hid_set_field(struct hid_field *, unsigned, __s32);
+ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
+ int interrupt);
+--
+2.42.0
+
+From 05cd738ce1c0e1a930a1dab02528fd9f1c702c38 Mon Sep 17 00:00:00 2001
+From: Kerem Karabay <kekrby@gmail.com>
+Date: Sun, 5 Mar 2023 18:52:43 +0300
+Subject: [PATCH 02/12] HID: hid-appletb-bl: add driver for the backlight of
+ Apple Touch Bars
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This commit adds a driver for the backlight of Apple Touch Bars on x86
+Macs. Note that currently only T2 Macs are supported.
+
+This driver is based on previous work done by Ronald Tschalär
+<ronald@innovation.ch>.
+
+Signed-off-by: Kerem Karabay <kekrby@gmail.com>
+---
+ MAINTAINERS | 6 ++
+ drivers/hid/Kconfig | 10 ++
+ drivers/hid/Makefile | 1 +
+ drivers/hid/hid-appletb-bl.c | 193 +++++++++++++++++++++++++++++++++++
+ drivers/hid/hid-quirks.c | 4 +-
+ 5 files changed, 213 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/hid/hid-appletb-bl.c
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 4cc6bf79f..519b3b736 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -9157,6 +9157,12 @@ F: include/linux/pm.h
+ F: include/linux/suspend.h
+ F: kernel/power/
+
++HID APPLE TOUCH BAR DRIVERS
++M: Kerem Karabay <kekrby@gmail.com>
++L: linux-input@vger.kernel.org
++S: Maintained
++F: drivers/hid/hid-appletb-*
++
+ HID CORE LAYER
+ M: Jiri Kosina <jikos@kernel.org>
+ M: Benjamin Tissoires <benjamin.tissoires@redhat.com>
+diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
+index e11c1c803..cf19a3b33 100644
+--- a/drivers/hid/Kconfig
++++ b/drivers/hid/Kconfig
+@@ -148,6 +148,16 @@ config HID_APPLEIR
+
+ Say Y here if you want support for Apple infrared remote control.
+
++config HID_APPLETB_BL
++ tristate "Apple Touch Bar Backlight"
++ depends on BACKLIGHT_CLASS_DEVICE
++ help
++ Say Y here if you want support for the backlight of Touch Bars on x86
++ MacBook Pros.
++
++ To compile this driver as a module, choose M here: the
++ module will be called hid-appletb-bl.
++
+ config HID_ASUS
+ tristate "Asus"
+ depends on USB_HID
+diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
+index 7a9e16015..bc86e38b2 100644
+--- a/drivers/hid/Makefile
++++ b/drivers/hid/Makefile
+@@ -29,6 +29,7 @@ obj-$(CONFIG_HID_ALPS) += hid-alps.o
+ obj-$(CONFIG_HID_ACRUX) += hid-axff.o
+ obj-$(CONFIG_HID_APPLE) += hid-apple.o
+ obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o
++obj-$(CONFIG_HID_APPLETB_BL) += hid-appletb-bl.o
+ obj-$(CONFIG_HID_CREATIVE_SB0540) += hid-creative-sb0540.o
+ obj-$(CONFIG_HID_ASUS) += hid-asus.o
+ obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
+diff --git a/drivers/hid/hid-appletb-bl.c b/drivers/hid/hid-appletb-bl.c
+new file mode 100644
+index 000000000..0c5e4b776
+--- /dev/null
++++ b/drivers/hid/hid-appletb-bl.c
+@@ -0,0 +1,193 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Apple Touch Bar Backlight Driver
++ *
++ * Copyright (c) 2017-2018 Ronald Tschalär
++ * Copyright (c) 2022-2023 Kerem Karabay <kekrby@gmail.com>
++ */
++
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
++#include <linux/hid.h>
++#include <linux/backlight.h>
++
++#include "hid-ids.h"
++
++#define APPLETB_BL_ON 1
++#define APPLETB_BL_DIM 3
++#define APPLETB_BL_OFF 4
++
++#define HID_UP_APPLEVENDOR_TB_BL 0xff120000
++
++#define HID_VD_APPLE_TB_BRIGHTNESS 0xff120001
++#define HID_USAGE_AUX1 0xff120020
++#define HID_USAGE_BRIGHTNESS 0xff120021
++
++struct appletb_bl {
++ struct hid_field *aux1_field, *brightness_field;
++ struct backlight_device *bdev;
++
++ bool full_on;
++};
++
++const u8 appletb_bl_brightness_map[] = {
++ APPLETB_BL_OFF,
++ APPLETB_BL_DIM,
++ APPLETB_BL_ON
++};
++
++static int appletb_bl_set_brightness(struct appletb_bl *bl, u8 brightness)
++{
++ struct hid_report *report = bl->brightness_field->report;
++ struct hid_device *hdev = report->device;
++ int ret;
++
++ ret = hid_set_field(bl->aux1_field, 0, 1);
++ if (ret) {
++ hid_err(hdev, "Failed to set auxiliary field (%pe)\n", ERR_PTR(ret));
++ return ret;
++ }
++
++ ret = hid_set_field(bl->brightness_field, 0, brightness);
++ if (ret) {
++ hid_err(hdev, "Failed to set brightness field (%pe)\n", ERR_PTR(ret));
++ return ret;
++ }
++
++ if (!bl->full_on) {
++ ret = hid_hw_power(hdev, PM_HINT_FULLON);
++ if (ret < 0) {
++ hid_err(hdev, "Device didn't power on (%pe)\n", ERR_PTR(ret));
++ return ret;
++ }
++
++ bl->full_on = true;
++ }
++
++ hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
++
++ if (brightness == APPLETB_BL_OFF) {
++ hid_hw_power(hdev, PM_HINT_NORMAL);
++ bl->full_on = false;
++ }
++
++ return 0;
++}
++
++static int appletb_bl_update_status(struct backlight_device *bdev)
++{
++ struct appletb_bl *bl = bl_get_data(bdev);
++ u16 brightness;
++
++ if (bdev->props.state & BL_CORE_SUSPENDED)
++ brightness = 0;
++ else
++ brightness = backlight_get_brightness(bdev);
++
++ return appletb_bl_set_brightness(bl, appletb_bl_brightness_map[brightness]);
++}
++
++static const struct backlight_ops appletb_bl_backlight_ops = {
++ .options = BL_CORE_SUSPENDRESUME,
++ .update_status = appletb_bl_update_status,
++};
++
++static int appletb_bl_probe(struct hid_device *hdev, const struct hid_device_id *id)
++{
++ struct hid_field *aux1_field, *brightness_field;
++ struct backlight_properties bl_props = { 0 };
++ struct device *dev = &hdev->dev;
++ struct appletb_bl *bl;
++ int ret;
++
++ ret = hid_parse(hdev);
++ if (ret)
++ return dev_err_probe(dev, ret, "HID parse failed\n");
++
++ aux1_field = hid_find_field(hdev, HID_FEATURE_REPORT,
++ HID_VD_APPLE_TB_BRIGHTNESS, HID_USAGE_AUX1);
++
++ brightness_field = hid_find_field(hdev, HID_FEATURE_REPORT,
++ HID_VD_APPLE_TB_BRIGHTNESS, HID_USAGE_BRIGHTNESS);
++
++ if (!aux1_field || !brightness_field)
++ return -ENODEV;
++
++ if (aux1_field->report != brightness_field->report)
++ return dev_err_probe(dev, -ENODEV, "Encountered unexpected report structure\n");
++
++ bl = devm_kzalloc(dev, sizeof(*bl), GFP_KERNEL);
++ if (!bl)
++ return -ENOMEM;
++
++ ret = hid_hw_start(hdev, HID_CONNECT_DRIVER);
++ if (ret)
++ return dev_err_probe(dev, ret, "HID hardware start failed\n");
++
++ ret = hid_hw_open(hdev);
++ if (ret) {
++ dev_err_probe(dev, ret, "HID hardware open failed\n");
++ goto stop_hw;
++ }
++
++ bl->aux1_field = aux1_field;
++ bl->brightness_field = brightness_field;
++
++ ret = appletb_bl_set_brightness(bl, APPLETB_BL_OFF);
++ if (ret) {
++ dev_err_probe(dev, ret, "Failed to set touch bar brightness to off\n");
++ goto close_hw;
++ }
++
++ bl_props.type = BACKLIGHT_RAW;
++ bl_props.max_brightness = ARRAY_SIZE(appletb_bl_brightness_map) - 1;
++
++ bl->bdev = devm_backlight_device_register(dev, "appletb_backlight", dev, bl,
++ &appletb_bl_backlight_ops, &bl_props);
++ if (IS_ERR(bl->bdev)) {
++ ret = PTR_ERR(bl->bdev);
++ dev_err_probe(dev, ret, "Failed to register backlight device\n");
++ goto close_hw;
++ }
++
++ hid_set_drvdata(hdev, bl);
++
++ return 0;
++
++close_hw:
++ hid_hw_close(hdev);
++stop_hw:
++ hid_hw_stop(hdev);
++
++ return ret;
++}
++
++static void appletb_bl_remove(struct hid_device *hdev)
++{
++ struct appletb_bl *bl = hid_get_drvdata(hdev);
++
++ appletb_bl_set_brightness(bl, APPLETB_BL_OFF);
++
++ hid_hw_close(hdev);
++ hid_hw_stop(hdev);
++}
++
++static const struct hid_device_id appletb_bl_hid_ids[] = {
++ /* MacBook Pro's 2018, 2019, with T2 chip: iBridge DFR Brightness */
++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) },
++ { }
++};
++MODULE_DEVICE_TABLE(hid, appletb_bl_hid_ids);
++
++static struct hid_driver appletb_bl_hid_driver = {
++ .name = "hid-appletb-bl",
++ .id_table = appletb_bl_hid_ids,
++ .probe = appletb_bl_probe,
++ .remove = appletb_bl_remove,
++};
++module_hid_driver(appletb_bl_hid_driver);
++
++MODULE_AUTHOR("Ronald Tschalär");
++MODULE_AUTHOR("Kerem Karabay <kekrby@gmail.com>");
++MODULE_DESCRIPTION("MacBookPro Touch Bar Backlight Driver");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
+index 3983b4f28..82e7a80c9 100644
+--- a/drivers/hid/hid-quirks.c
++++ b/drivers/hid/hid-quirks.c
+@@ -325,7 +325,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021) },
+- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) },
+ #endif
+ #if IS_ENABLED(CONFIG_HID_APPLEIR)
+@@ -335,6 +334,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) },
+ #endif
++#if IS_ENABLED(CONFIG_HID_APPLETB_BL)
++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) },
++#endif
+ #if IS_ENABLED(CONFIG_HID_ASUS)
+ { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD) },
+ { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD) },
+--
+2.42.0
+
+From 514b4f088b7ed916c634ca6f61de72c5f86268dd Mon Sep 17 00:00:00 2001
+From: Kerem Karabay <kekrby@gmail.com>
+Date: Sun, 5 Mar 2023 18:17:23 +0300
+Subject: [PATCH 03/12] HID: hid-appletb-kbd: add driver for the keyboard mode
+ of Apple Touch Bars
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The Touch Bars found on x86 Macs support two USB configurations: one
+where the device presents itself as a HID keyboard and can display
+predefined sets of keys, and one where the operating system has full
+control over what is displayed. This commit adds a driver for the
+display functionality of the first configuration.
+
+Note that currently only T2 Macs are supported.
+
+This driver is based on previous work done by Ronald Tschalär
+<ronald@innovation.ch>.
+
+Signed-off-by: Kerem Karabay <kekrby@gmail.com>
+---
+ .../ABI/testing/sysfs-driver-hid-appletb-kbd | 13 +
+ drivers/hid/Kconfig | 11 +
+ drivers/hid/Makefile | 1 +
+ drivers/hid/hid-appletb-kbd.c | 289 ++++++++++++++++++
+ drivers/hid/hid-quirks.c | 4 +-
+ 5 files changed, 317 insertions(+), 1 deletion(-)
+ create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-appletb-kbd
+ create mode 100644 drivers/hid/hid-appletb-kbd.c
+
+diff --git a/Documentation/ABI/testing/sysfs-driver-hid-appletb-kbd b/Documentation/ABI/testing/sysfs-driver-hid-appletb-kbd
+new file mode 100644
+index 000000000..2a19584d0
+--- /dev/null
++++ b/Documentation/ABI/testing/sysfs-driver-hid-appletb-kbd
+@@ -0,0 +1,13 @@
++What: /sys/bus/hid/drivers/hid-appletb-kbd/<dev>/mode
++Date: September, 2023
++KernelVersion: 6.5
++Contact: linux-input@vger.kernel.org
++Description:
++ The set of keys displayed on the Touch Bar.
++ Valid values are:
++ == =================
++ 0 Escape key only
++ 1 Function keys
++ 2 Media/brightness keys
++ 3 None
++ == =================
+diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
+index cf19a3b33..852de13aa 100644
+--- a/drivers/hid/Kconfig
++++ b/drivers/hid/Kconfig
+@@ -158,6 +158,17 @@ config HID_APPLETB_BL
+ To compile this driver as a module, choose M here: the
+ module will be called hid-appletb-bl.
+
++config HID_APPLETB_KBD
++ tristate "Apple Touch Bar Keyboard Mode"
++ depends on USB_HID
++ help
++ Say Y here if you want support for the keyboard mode (escape,
++ function, media and brightness keys) of Touch Bars on x86 MacBook
++ Pros.
++
++ To compile this driver as a module, choose M here: the
++ module will be called hid-appletb-kbd.
++
+ config HID_ASUS
+ tristate "Asus"
+ depends on USB_HID
+diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
+index bc86e38b2..5b60015fd 100644
+--- a/drivers/hid/Makefile
++++ b/drivers/hid/Makefile
+@@ -30,6 +30,7 @@ obj-$(CONFIG_HID_ACRUX) += hid-axff.o
+ obj-$(CONFIG_HID_APPLE) += hid-apple.o
+ obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o
+ obj-$(CONFIG_HID_APPLETB_BL) += hid-appletb-bl.o
++obj-$(CONFIG_HID_APPLETB_KBD) += hid-appletb-kbd.o
+ obj-$(CONFIG_HID_CREATIVE_SB0540) += hid-creative-sb0540.o
+ obj-$(CONFIG_HID_ASUS) += hid-asus.o
+ obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
+diff --git a/drivers/hid/hid-appletb-kbd.c b/drivers/hid/hid-appletb-kbd.c
+new file mode 100644
+index 000000000..bc004c408
+--- /dev/null
++++ b/drivers/hid/hid-appletb-kbd.c
+@@ -0,0 +1,289 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Apple Touch Bar Keyboard Mode Driver
++ *
++ * Copyright (c) 2017-2018 Ronald Tschalär
++ * Copyright (c) 2022-2023 Kerem Karabay <kekrby@gmail.com>
++ */
++
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
++#include <linux/hid.h>
++#include <linux/usb.h>
++#include <linux/input.h>
++#include <linux/sysfs.h>
++#include <linux/bitops.h>
++#include <linux/module.h>
++#include <linux/string.h>
++#include <linux/input/sparse-keymap.h>
++
++#include "hid-ids.h"
++
++#define APPLETB_KBD_MODE_ESC 0
++#define APPLETB_KBD_MODE_FN 1
++#define APPLETB_KBD_MODE_SPCL 2
++#define APPLETB_KBD_MODE_OFF 3
++#define APPLETB_KBD_MODE_MAX APPLETB_KBD_MODE_OFF
++
++#define HID_USAGE_MODE 0x00ff0004
++
++struct appletb_kbd {
++ struct hid_field *mode_field;
++
++ u8 saved_mode;
++ u8 current_mode;
++};
++
++static const struct key_entry appletb_kbd_keymap[] = {
++ { KE_KEY, KEY_ESC, { KEY_ESC } },
++ { KE_KEY, KEY_F1, { KEY_BRIGHTNESSDOWN } },
++ { KE_KEY, KEY_F2, { KEY_BRIGHTNESSUP } },
++ { KE_KEY, KEY_F3, { KEY_RESERVED } },
++ { KE_KEY, KEY_F4, { KEY_RESERVED } },
++ { KE_KEY, KEY_F5, { KEY_KBDILLUMDOWN } },
++ { KE_KEY, KEY_F6, { KEY_KBDILLUMUP } },
++ { KE_KEY, KEY_F7, { KEY_PREVIOUSSONG } },
++ { KE_KEY, KEY_F8, { KEY_PLAYPAUSE } },
++ { KE_KEY, KEY_F9, { KEY_NEXTSONG } },
++ { KE_KEY, KEY_F10, { KEY_MUTE } },
++ { KE_KEY, KEY_F11, { KEY_VOLUMEDOWN } },
++ { KE_KEY, KEY_F12, { KEY_VOLUMEUP } },
++ { KE_END, 0 }
++};
++
++static int appletb_kbd_set_mode(struct appletb_kbd *kbd, u8 mode)
++{
++ struct hid_report *report = kbd->mode_field->report;
++ struct hid_device *hdev = report->device;
++ int ret;
++
++ ret = hid_hw_power(hdev, PM_HINT_FULLON);
++ if (ret) {
++ hid_err(hdev, "Device didn't resume (%pe)\n", ERR_PTR(ret));
++ return ret;
++ }
++
++ ret = hid_set_field(kbd->mode_field, 0, mode);
++ if (ret) {
++ hid_err(hdev, "Failed to set mode field to %u (%pe)\n", mode, ERR_PTR(ret));
++ goto power_normal;
++ }
++
++ hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
++
++ kbd->current_mode = mode;
++
++power_normal:
++ hid_hw_power(hdev, PM_HINT_NORMAL);
++
++ return ret;
++}
++
++static ssize_t mode_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct appletb_kbd *kbd = dev_get_drvdata(dev);
++
++ return sysfs_emit(buf, "%d\n", kbd->current_mode);
++}
++
++static ssize_t mode_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t size)
++{
++ struct appletb_kbd *kbd = dev_get_drvdata(dev);
++ u8 mode;
++ int ret;
++
++ ret = kstrtou8(buf, 0, &mode);
++ if (ret)
++ return ret;
++
++ if (mode > APPLETB_KBD_MODE_MAX)
++ return -EINVAL;
++
++ ret = appletb_kbd_set_mode(kbd, mode);
++
++ return ret < 0 ? ret : size;
++}
++static DEVICE_ATTR_RW(mode);
++
++struct attribute *appletb_kbd_attrs[] = {
++ &dev_attr_mode.attr,
++ NULL
++};
++ATTRIBUTE_GROUPS(appletb_kbd);
++
++static int appletb_tb_key_to_slot(unsigned int code)
++{
++ switch (code) {
++ case KEY_ESC:
++ return 0;
++ case KEY_F1 ... KEY_F10:
++ return code - KEY_F1 + 1;
++ case KEY_F11 ... KEY_F12:
++ return code - KEY_F11 + 11;
++
++ default:
++ return -EINVAL;
++ }
++}
++
++static int appletb_kbd_hid_event(struct hid_device *hdev, struct hid_field *field,
++ struct hid_usage *usage, __s32 value)
++{
++ struct appletb_kbd *kbd = hid_get_drvdata(hdev);
++ struct key_entry *translation;
++ struct input_dev *input;
++ int slot;
++
++ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD || usage->type != EV_KEY)
++ return 0;
++
++ input = field->hidinput->input;
++
++ /*
++ * Skip non-touch-bar keys.
++ *
++ * Either the touch bar itself or usbhid generate a slew of key-down
++ * events for all the meta keys. None of which we're at all interested
++ * in.
++ */
++ slot = appletb_tb_key_to_slot(usage->code);
++ if (slot < 0)
++ return 0;
++
++ translation = sparse_keymap_entry_from_scancode(input, usage->code);
++
++ if (translation && kbd->current_mode == APPLETB_KBD_MODE_SPCL) {
++ input_event(input, usage->type, translation->keycode, value);
++
++ return 1;
++ }
++
++ return kbd->current_mode == APPLETB_KBD_MODE_OFF;
++}
++
++static int appletb_kbd_input_configured(struct hid_device *hdev, struct hid_input *hidinput)
++{
++ struct input_dev *input = hidinput->input;
++
++ /*
++ * Clear various input capabilities that are blindly set by the hid
++ * driver (usbkbd.c)
++ */
++ memset(input->evbit, 0, sizeof(input->evbit));
++ memset(input->keybit, 0, sizeof(input->keybit));
++ memset(input->ledbit, 0, sizeof(input->ledbit));
++
++ __set_bit(EV_REP, input->evbit);
++
++ return sparse_keymap_setup(input, appletb_kbd_keymap, NULL);
++}
++
++static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id *id)
++{
++ struct appletb_kbd *kbd;
++ struct device *dev = &hdev->dev;
++ struct hid_field *mode_field;
++ int ret;
++
++ ret = hid_parse(hdev);
++ if (ret)
++ return dev_err_probe(dev, ret, "HID parse failed\n");
++
++ mode_field = hid_find_field(hdev, HID_OUTPUT_REPORT,
++ HID_GD_KEYBOARD, HID_USAGE_MODE);
++ if (!mode_field)
++ return -ENODEV;
++
++ kbd = devm_kzalloc(dev, sizeof(*kbd), GFP_KERNEL);
++ if (!kbd)
++ return -ENOMEM;
++
++ kbd->mode_field = mode_field;
++
++ ret = hid_hw_start(hdev, HID_CONNECT_HIDINPUT);
++ if (ret)
++ return dev_err_probe(dev, ret, "HID hw start failed\n");
++
++ ret = hid_hw_open(hdev);
++ if (ret) {
++ dev_err_probe(dev, ret, "HID hw open failed\n");
++ goto stop_hw;
++ }
++
++ ret = appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF);
++ if (ret) {
++ dev_err_probe(dev, ret, "Failed to set touchbar mode\n");
++ goto close_hw;
++ }
++
++ hid_set_drvdata(hdev, kbd);
++
++ return 0;
++
++close_hw:
++ hid_hw_close(hdev);
++stop_hw:
++ hid_hw_stop(hdev);
++ return ret;
++}
++
++static void appletb_kbd_remove(struct hid_device *hdev)
++{
++ struct appletb_kbd *kbd = hid_get_drvdata(hdev);
++
++ appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF);
++
++ hid_hw_close(hdev);
++ hid_hw_stop(hdev);
++}
++
++#ifdef CONFIG_PM
++static int appletb_kbd_suspend(struct hid_device *hdev, pm_message_t msg)
++{
++ struct appletb_kbd *kbd = hid_get_drvdata(hdev);
++
++ kbd->saved_mode = kbd->current_mode;
++ appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF);
++
++ return 0;
++}
++
++static int appletb_kbd_reset_resume(struct hid_device *hdev)
++{
++ struct appletb_kbd *kbd = hid_get_drvdata(hdev);
++
++ appletb_kbd_set_mode(kbd, kbd->saved_mode);
++
++ return 0;
++}
++#endif
++
++static const struct hid_device_id appletb_kbd_hid_ids[] = {
++ /* MacBook Pro's 2018, 2019, with T2 chip: iBridge Display */
++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) },
++ { }
++};
++MODULE_DEVICE_TABLE(hid, appletb_kbd_hid_ids);
++
++static struct hid_driver appletb_kbd_hid_driver = {
++ .name = "hid-appletb-kbd",
++ .id_table = appletb_kbd_hid_ids,
++ .probe = appletb_kbd_probe,
++ .remove = appletb_kbd_remove,
++ .event = appletb_kbd_hid_event,
++ .input_configured = appletb_kbd_input_configured,
++#ifdef CONFIG_PM
++ .suspend = appletb_kbd_suspend,
++ .reset_resume = appletb_kbd_reset_resume,
++#endif
++ .driver.dev_groups = appletb_kbd_groups,
++};
++module_hid_driver(appletb_kbd_hid_driver);
++
++MODULE_AUTHOR("Ronald Tschalär");
++MODULE_AUTHOR("Kerem Karabay <kekrby@gmail.com>");
++MODULE_DESCRIPTION("MacBookPro Touch Bar Keyboard Mode Driver");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
+index 82e7a80c9..82be9dfaf 100644
+--- a/drivers/hid/hid-quirks.c
++++ b/drivers/hid/hid-quirks.c
+@@ -325,7 +325,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021) },
+- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) },
+ #endif
+ #if IS_ENABLED(CONFIG_HID_APPLEIR)
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+@@ -337,6 +336,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
+ #if IS_ENABLED(CONFIG_HID_APPLETB_BL)
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) },
+ #endif
++#if IS_ENABLED(CONFIG_HID_APPLETB_KBD)
++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) },
++#endif
+ #if IS_ENABLED(CONFIG_HID_ASUS)
+ { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD) },
+ { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD) },
+--
+2.42.0
+
+From 2f9be28549307b4ac51e8d66bf3b8d5e0621466d Mon Sep 17 00:00:00 2001
+From: Kerem Karabay <kekrby@gmail.com>
+Date: Wed, 19 Jul 2023 19:37:14 +0300
+Subject: [PATCH 04/12] HID: multitouch: support getting the contact ID from
+ HID_DG_TRANSDUCER_INDEX fields
+
+This is needed to support Apple Touch Bars, where the contact ID is
+contained in fields with the HID_DG_TRANSDUCER_INDEX usage.
+
+Signed-off-by: Kerem Karabay <kekrby@gmail.com>
+---
+ drivers/hid/hid-multitouch.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
+index e31be0cb8..902a59928 100644
+--- a/drivers/hid/hid-multitouch.c
++++ b/drivers/hid/hid-multitouch.c
+@@ -636,7 +636,9 @@ static struct mt_report_data *mt_allocate_report_data(struct mt_device *td,
+
+ if (field->logical == HID_DG_FINGER || td->hdev->group != HID_GROUP_MULTITOUCH_WIN_8) {
+ for (n = 0; n < field->report_count; n++) {
+- if (field->usage[n].hid == HID_DG_CONTACTID) {
++ unsigned int hid = field->usage[n].hid;
++
++ if (hid == HID_DG_CONTACTID || hid == HID_DG_TRANSDUCER_INDEX) {
+ rdata->is_mt_collection = true;
+ break;
+ }
+@@ -815,6 +817,7 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ MT_STORE_FIELD(tip_state);
+ return 1;
+ case HID_DG_CONTACTID:
++ case HID_DG_TRANSDUCER_INDEX:
+ MT_STORE_FIELD(contactid);
+ app->touches_by_report++;
+ return 1;
+--
+2.42.0
+
+From 6162d328fe7b2cf5a3ee8c29bdb229e9528c7a6c Mon Sep 17 00:00:00 2001
+From: Kerem Karabay <kekrby@gmail.com>
+Date: Wed, 19 Jul 2023 19:44:10 +0300
+Subject: [PATCH 05/12] HID: multitouch: support getting the tip state from
+ HID_DG_TOUCH fields
+
+This is necessary on Apple Touch Bars, where the tip state is contained
+in fields with the HID_DG_TOUCH usage. This feature is gated by a quirk
+in order to prevent breaking other devices, see commit c2ef8f21ea8f
+("HID: multitouch: add support for trackpads").
+
+Signed-off-by: Kerem Karabay <kekrby@gmail.com>
+---
+ drivers/hid/hid-multitouch.c | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
+index 902a59928..dd5509eeb 100644
+--- a/drivers/hid/hid-multitouch.c
++++ b/drivers/hid/hid-multitouch.c
+@@ -78,6 +78,8 @@ MODULE_LICENSE("GPL");
+ #define MT_QUIRK_ORIENTATION_INVERT BIT(22)
+ #define MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT BIT(23)
+ #define MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH BIT(24)
++#define MT_QUIRK_TOUCH_IS_TIPSTATE BIT(25)
++
+
+ #define MT_INPUTMODE_TOUCHSCREEN 0x02
+ #define MT_INPUTMODE_TOUCHPAD 0x03
+@@ -810,6 +811,15 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+
+ MT_STORE_FIELD(confidence_state);
+ return 1;
++ case HID_DG_TOUCH:
++ /*
++ * Legacy devices use TIPSWITCH and not TOUCH.
++ * Let's just ignore this field unless the quirk is set.
++ */
++ if (!(cls->quirks & MT_QUIRK_TOUCH_IS_TIPSTATE))
++ return -1;
++
++ fallthrough;
+ case HID_DG_TIPSWITCH:
+ if (field->application != HID_GD_SYSTEM_MULTIAXIS)
+ input_set_capability(hi->input,
+@@ -873,10 +883,6 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ case HID_DG_CONTACTMAX:
+ /* contact max are global to the report */
+ return -1;
+- case HID_DG_TOUCH:
+- /* Legacy devices use TIPSWITCH and not TOUCH.
+- * Let's just ignore this field. */
+- return -1;
+ }
+ /* let hid-input decide for the others */
+ return 0;
+--
+2.42.0
+
+From e923c6e1a5a508e341851ae020cdb3e7333ccd18 Mon Sep 17 00:00:00 2001
+From: Kerem Karabay <kekrby@gmail.com>
+Date: Wed, 19 Jul 2023 19:26:57 +0300
+Subject: [PATCH 06/12] HID: multitouch: take cls->maxcontacts into account for
+ devices without a HID_DG_CONTACTMAX field too
+
+This is needed for Apple Touch Bars, where no HID_DG_CONTACTMAX field is
+present and the maximum contact count is greater than the default.
+
+Signed-off-by: Kerem Karabay <kekrby@gmail.com>
+---
+ drivers/hid/hid-multitouch.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
+index dd5509eeb..624c1d3cc 100644
+--- a/drivers/hid/hid-multitouch.c
++++ b/drivers/hid/hid-multitouch.c
+@@ -491,9 +491,6 @@ static void mt_feature_mapping(struct hid_device *hdev,
+ if (!td->maxcontacts &&
+ field->logical_maximum <= MT_MAX_MAXCONTACT)
+ td->maxcontacts = field->logical_maximum;
+- if (td->mtclass.maxcontacts)
+- /* check if the maxcontacts is given by the class */
+- td->maxcontacts = td->mtclass.maxcontacts;
+
+ break;
+ case HID_DG_BUTTONTYPE:
+@@ -1310,6 +1307,10 @@ static int mt_touch_input_configured(struct hid_device *hdev,
+ struct input_dev *input = hi->input;
+ int ret;
+
++ /* check if the maxcontacts is given by the class */
++ if (cls->maxcontacts)
++ td->maxcontacts = cls->maxcontacts;
++
+ if (!td->maxcontacts)
+ td->maxcontacts = MT_DEFAULT_MAXCONTACT;
+
+--
+2.42.0
+
+From b9f7232d2696b91ae98fadd7b14c531aa8edceb5 Mon Sep 17 00:00:00 2001
+From: Kerem Karabay <kekrby@gmail.com>
+Date: Wed, 19 Jul 2023 19:39:53 +0300
+Subject: [PATCH 07/12] HID: multitouch: allow specifying if a device is direct
+ in a class
+
+Currently the driver determines the device type based on the
+application, but this value is not reliable on Apple Touch Bars, where
+the application is HID_DG_TOUCHPAD even though the devices are direct,
+so allow setting it in classes.
+
+Signed-off-by: Kerem Karabay <kekrby@gmail.com>
+---
+ drivers/hid/hid-multitouch.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
+index 624c1d3cc..f98fb36ff 100644
+--- a/drivers/hid/hid-multitouch.c
++++ b/drivers/hid/hid-multitouch.c
+@@ -147,6 +147,7 @@ struct mt_class {
+ __s32 sn_height; /* Signal/noise ratio for height events */
+ __s32 sn_pressure; /* Signal/noise ratio for pressure events */
+ __u8 maxcontacts;
++ bool is_direct; /* true for touchscreens */
+ bool is_indirect; /* true for touchpads */
+ bool export_all_inputs; /* do not ignore mouse, keyboards, etc... */
+ };
+@@ -564,13 +565,13 @@ static struct mt_application *mt_allocate_application(struct mt_device *td,
+ mt_application->application = application;
+ INIT_LIST_HEAD(&mt_application->mt_usages);
+
+- if (application == HID_DG_TOUCHSCREEN)
++ if (application == HID_DG_TOUCHSCREEN && !td->mtclass.is_indirect)
+ mt_application->mt_flags |= INPUT_MT_DIRECT;
+
+ /*
+ * Model touchscreens providing buttons as touchpads.
+ */
+- if (application == HID_DG_TOUCHPAD) {
++ if (application == HID_DG_TOUCHPAD && !td->mtclass.is_direct) {
+ mt_application->mt_flags |= INPUT_MT_POINTER;
+ td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
+ }
+@@ -1318,6 +1319,9 @@ static int mt_touch_input_configured(struct hid_device *hdev,
+ if (td->serial_maybe)
+ mt_post_parse_default_settings(td, app);
+
++ if (cls->is_direct)
++ app->mt_flags |= INPUT_MT_DIRECT;
++
+ if (cls->is_indirect)
+ app->mt_flags |= INPUT_MT_POINTER;
+
+--
+2.42.0
+
+From a74de0b6f2e1b79d54e84dbeab1b310232275d6c Mon Sep 17 00:00:00 2001
+From: Kerem Karabay <kekrby@gmail.com>
+Date: Wed, 19 Jul 2023 19:46:02 +0300
+Subject: [PATCH 08/12] HID: multitouch: add device ID for Apple Touch Bars
+
+Note that this is device ID is for T2 Macs. Testing on T1 Macs would be
+appreciated.
+
+Signed-off-by: Kerem Karabay <kekrby@gmail.com>
+---
+ drivers/hid/Kconfig | 1 +
+ drivers/hid/hid-multitouch.c | 26 ++++++++++++++++++++++----
+ 2 files changed, 23 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
+index 852de13aa..4e238df87 100644
+--- a/drivers/hid/Kconfig
++++ b/drivers/hid/Kconfig
+@@ -737,6 +737,7 @@ config HID_MULTITOUCH
+ Say Y here if you have one of the following devices:
+ - 3M PCT touch screens
+ - ActionStar dual touch panels
++ - Touch Bars on x86 MacBook Pros
+ - Atmel panels
+ - Cando dual touch panels
+ - Chunghwa panels
+diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
+index f98fb36ff..f881b19db 100644
+--- a/drivers/hid/hid-multitouch.c
++++ b/drivers/hid/hid-multitouch.c
+@@ -226,6 +229,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app);
+ #define MT_CLS_RAZER_BLADE_STEALTH 0x0112
+ #define MT_CLS_SMART_TECH 0x0113
+ #define MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER 0x0114
++#define MT_CLS_APPLE_TOUCHBAR 0x0115
+
+ #define MT_DEFAULT_MAXCONTACT 10
+ #define MT_MAX_MAXCONTACT 250
+@@ -421,6 +421,13 @@
+ MT_QUIRK_WIN8_PTP_BUTTONS,
+ .export_all_inputs = true
+ },
++ { .name = MT_CLS_APPLE_TOUCHBAR,
++ .quirks = MT_QUIRK_HOVERING |
++ MT_QUIRK_TOUCH_IS_TIPSTATE |
++ MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE,
++ .is_direct = true,
++ .maxcontacts = 11,
++ },
+ { }
+ };
+
+@@ -1883,6 +1906,17 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
+ }
+ }
+
++ ret = hid_parse(hdev);
++ if (ret != 0) {
++ unregister_pm_notifier(&td->pm_notifier);
++ return ret;
++ }
++
++ if (mtclass->name == MT_CLS_APPLE_TOUCHBAR &&
++ !hid_find_field(hdev, HID_INPUT_REPORT,
++ HID_DG_TOUCHPAD, HID_DG_TRANSDUCER_INDEX))
++ return -ENODEV;
++
+ td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL);
+ if (!td) {
+ dev_err(&hdev->dev, "cannot allocate multitouch data\n");
+@@ -1933,12 +1967,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
+
+ timer_setup(&td->release_timer, mt_expired_timeout, 0);
+
+- ret = hid_parse(hdev);
+- if (ret != 0) {
+- unregister_pm_notifier(&td->pm_notifier);
+- return ret;
+- }
+-
+ if (mtclass->quirks & MT_QUIRK_FIX_CONST_CONTACT_ID)
+ mt_fix_const_fields(hdev, HID_DG_CONTACTID);
+
+@@ -2418,6 +2418,11 @@
+ HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY,
+ USB_VENDOR_ID_MICROSOFT, 0x09c0) },
+
++ /* Apple Touch Bars */
++ { .driver_data = MT_CLS_APPLE_TOUCHBAR,
++ HID_USB_DEVICE(USB_VENDOR_ID_APPLE,
++ USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) },
++
+ /* Google MT devices */
+ { .driver_data = MT_CLS_GOOGLE,
+ HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE,
+--
+2.42.0
+
+From f6ab7e4580962c9d82e7dc40dd074d47b2bce034 Mon Sep 17 00:00:00 2001
+From: Hector Martin <marcan@marcan.st>
+Date: Tue, 1 Feb 2022 00:40:51 +0900
+Subject: [PATCH 09/12] lib/vsprintf: Add support for generic FOURCCs by
+ extending %p4cc
+
+%p4cc is designed for DRM/V4L2 FOURCCs with their specific quirks, but
+it's useful to be able to print generic 4-character codes formatted as
+an integer. Extend it to add format specifiers for printing generic
+32-bit FOURCCs with various endian semantics:
+
+%p4ch Host-endian
+%p4cl Little-endian
+%p4cb Big-endian
+%p4cr Reverse-endian
+
+The endianness determines how bytes are interpreted as a u32, and the
+FOURCC is then always printed MSByte-first (this is the opposite of
+V4L/DRM FOURCCs). This covers most practical cases, e.g. %p4cr would
+allow printing LSByte-first FOURCCs stored in host endian order
+(other than the hex form being in character order, not the integer
+value).
+
+Signed-off-by: Hector Martin <marcan@marcan.st>
+Signed-off-by: Kerem Karabay <kekrby@gmail.com>
+---
+ Documentation/core-api/printk-formats.rst | 32 ++++++++++++++++++++
+ lib/test_printf.c | 20 +++++++++----
+ lib/vsprintf.c | 36 +++++++++++++++++++----
+ scripts/checkpatch.pl | 2 +-
+ 4 files changed, 77 insertions(+), 13 deletions(-)
+
+diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst
+index dfe7e75a7..0ccef63e6 100644
+--- a/Documentation/core-api/printk-formats.rst
++++ b/Documentation/core-api/printk-formats.rst
+@@ -631,6 +631,38 @@ Examples::
+ %p4cc Y10 little-endian (0x20303159)
+ %p4cc NV12 big-endian (0xb231564e)
+
++Generic FourCC code
++-------------------
++
++::
++ %p4c[hnbl] gP00 (0x67503030)
++
++Print a generic FourCC code, as both ASCII characters and its numerical
++value as hexadecimal.
++
++The additional ``h``, ``r``, ``b``, and ``l`` specifiers are used to specify
++host, reversed, big or little endian order data respectively. Host endian
++order means the data is interpreted as a 32-bit integer and the most
++significant byte is printed first; that is, the character code as printed
++matches the byte order stored in memory on big-endian systems, and is reversed
++on little-endian systems.
++
++Passed by reference.
++
++Examples for a little-endian machine, given &(u32)0x67503030::
++
++ %p4ch gP00 (0x67503030)
++ %p4cl gP00 (0x67503030)
++ %p4cb 00Pg (0x30305067)
++ %p4cr 00Pg (0x30305067)
++
++Examples for a big-endian machine, given &(u32)0x67503030::
++
++ %p4ch gP00 (0x67503030)
++ %p4cl 00Pg (0x30305067)
++ %p4cb gP00 (0x67503030)
++ %p4cr 00Pg (0x30305067)
++
+ Rust
+ ----
+
+diff --git a/lib/test_printf.c b/lib/test_printf.c
+index 7677ebccf..2355be36f 100644
+--- a/lib/test_printf.c
++++ b/lib/test_printf.c
+@@ -746,18 +746,26 @@ static void __init fwnode_pointer(void)
+ static void __init fourcc_pointer(void)
+ {
+ struct {
++ char type;
+ u32 code;
+ char *str;
+ } const try[] = {
+- { 0x3231564e, "NV12 little-endian (0x3231564e)", },
+- { 0xb231564e, "NV12 big-endian (0xb231564e)", },
+- { 0x10111213, ".... little-endian (0x10111213)", },
+- { 0x20303159, "Y10 little-endian (0x20303159)", },
++ { 'c', 0x3231564e, "NV12 little-endian (0x3231564e)", },
++ { 'c', 0xb231564e, "NV12 big-endian (0xb231564e)", },
++ { 'c', 0x10111213, ".... little-endian (0x10111213)", },
++ { 'c', 0x20303159, "Y10 little-endian (0x20303159)", },
++ { 'h', 0x67503030, "gP00 (0x67503030)", },
++ { 'r', 0x30305067, "gP00 (0x67503030)", },
++ { 'l', cpu_to_le32(0x67503030), "gP00 (0x67503030)", },
++ { 'b', cpu_to_be32(0x67503030), "gP00 (0x67503030)", },
+ };
+ unsigned int i;
+
+- for (i = 0; i < ARRAY_SIZE(try); i++)
+- test(try[i].str, "%p4cc", &try[i].code);
++ for (i = 0; i < ARRAY_SIZE(try); i++) {
++ char fmt[] = { '%', 'p', '4', 'c', try[i].type, '\0' };
++
++ test(try[i].str, fmt, &try[i].code);
++ }
+ }
+
+ static void __init
+diff --git a/lib/vsprintf.c b/lib/vsprintf.c
+index 40f560959..bd9af783c 100644
+--- a/lib/vsprintf.c
++++ b/lib/vsprintf.c
+@@ -1758,27 +1758,50 @@ char *fourcc_string(char *buf, char *end, const u32 *fourcc,
+ char output[sizeof("0123 little-endian (0x01234567)")];
+ char *p = output;
+ unsigned int i;
++ bool pix_fmt = false;
+ u32 orig, val;
+
+- if (fmt[1] != 'c' || fmt[2] != 'c')
++ if (fmt[1] != 'c')
+ return error_string(buf, end, "(%p4?)", spec);
+
+ if (check_pointer(&buf, end, fourcc, spec))
+ return buf;
+
+ orig = get_unaligned(fourcc);
+- val = orig & ~BIT(31);
++ switch (fmt[2]) {
++ case 'h':
++ val = orig;
++ break;
++ case 'r':
++ val = orig = swab32(orig);
++ break;
++ case 'l':
++ val = orig = le32_to_cpu(orig);
++ break;
++ case 'b':
++ val = orig = be32_to_cpu(orig);
++ break;
++ case 'c':
++ /* Pixel formats are printed LSB-first */
++ val = swab32(orig & ~BIT(31));
++ pix_fmt = true;
++ break;
++ default:
++ return error_string(buf, end, "(%p4?)", spec);
++ }
+
+ for (i = 0; i < sizeof(u32); i++) {
+- unsigned char c = val >> (i * 8);
++ unsigned char c = val >> ((3 - i) * 8);
+
+ /* Print non-control ASCII characters as-is, dot otherwise */
+ *p++ = isascii(c) && isprint(c) ? c : '.';
+ }
+
+- *p++ = ' ';
+- strcpy(p, orig & BIT(31) ? "big-endian" : "little-endian");
+- p += strlen(p);
++ if (pix_fmt) {
++ *p++ = ' ';
++ strcpy(p, orig & BIT(31) ? "big-endian" : "little-endian");
++ p += strlen(p);
++ }
+
+ *p++ = ' ';
+ *p++ = '(';
+@@ -2348,6 +2371,7 @@ char *rust_fmt_argument(char *buf, char *end, void *ptr);
+ * read the documentation (path below) first.
+ * - 'NF' For a netdev_features_t
+ * - '4cc' V4L2 or DRM FourCC code, with endianness and raw numerical value.
++ * - '4c[hlbr]' Generic FourCC code.
+ * - 'h[CDN]' For a variable-length buffer, it prints it as a hex string with
+ * a certain separator (' ' by default):
+ * C colon
+diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
+index 880fde13d..f080e33a4 100755
+--- a/scripts/checkpatch.pl
++++ b/scripts/checkpatch.pl
+@@ -6906,7 +6906,7 @@ sub process {
+ ($extension eq "f" &&
+ defined $qualifier && $qualifier !~ /^w/) ||
+ ($extension eq "4" &&
+- defined $qualifier && $qualifier !~ /^cc/)) {
++ defined $qualifier && $qualifier !~ /^c[chlbr]/)) {
+ $bad_specifier = $specifier;
+ last;
+ }
+--
+2.42.0
+
+From f893444f7c842f97f3707897ba29f2c8dd77c8df Mon Sep 17 00:00:00 2001
+From: Kerem Karabay <kekrby@gmail.com>
+Date: Mon, 7 Aug 2023 20:29:27 +0300
+Subject: [PATCH 10/12] USB: core: add 'shutdown' callback to usb_driver
+
+This simplifies running code on shutdown for USB drivers.
+
+Signed-off-by: Kerem Karabay <kekrby@gmail.com>
+---
+ drivers/usb/core/driver.c | 14 ++++++++++++++
+ drivers/usb/storage/uas.c | 5 ++---
+ include/linux/usb.h | 3 +++
+ 3 files changed, 19 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
+index f58a0299f..dc0f86376 100644
+--- a/drivers/usb/core/driver.c
++++ b/drivers/usb/core/driver.c
+@@ -514,6 +514,19 @@ static int usb_unbind_interface(struct device *dev)
+ return 0;
+ }
+
++static void usb_shutdown_interface(struct device *dev)
++{
++ struct usb_interface *intf = to_usb_interface(dev);
++ struct usb_driver *driver;
++
++ if (!dev->driver)
++ return;
++
++ driver = to_usb_driver(dev->driver);
++ if (driver->shutdown)
++ driver->shutdown(intf);
++}
++
+ /**
+ * usb_driver_claim_interface - bind a driver to an interface
+ * @driver: the driver to be bound
+@@ -1053,6 +1066,7 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
+ new_driver->drvwrap.driver.bus = &usb_bus_type;
+ new_driver->drvwrap.driver.probe = usb_probe_interface;
+ new_driver->drvwrap.driver.remove = usb_unbind_interface;
++ new_driver->drvwrap.driver.shutdown = usb_shutdown_interface;
+ new_driver->drvwrap.driver.owner = owner;
+ new_driver->drvwrap.driver.mod_name = mod_name;
+ new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups;
+diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
+index 2583ee981..591fa0379 100644
+--- a/drivers/usb/storage/uas.c
++++ b/drivers/usb/storage/uas.c
+@@ -1221,9 +1221,8 @@ static void uas_disconnect(struct usb_interface *intf)
+ * hang on reboot when the device is still in uas mode. Note the reset is
+ * necessary as some devices won't revert to usb-storage mode without it.
+ */
+-static void uas_shutdown(struct device *dev)
++static void uas_shutdown(struct usb_interface *intf)
+ {
+- struct usb_interface *intf = to_usb_interface(dev);
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct Scsi_Host *shost = usb_get_intfdata(intf);
+ struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;
+@@ -1246,7 +1245,7 @@ static struct usb_driver uas_driver = {
+ .suspend = uas_suspend,
+ .resume = uas_resume,
+ .reset_resume = uas_reset_resume,
+- .drvwrap.driver.shutdown = uas_shutdown,
++ .shutdown = uas_shutdown,
+ .id_table = uas_usb_ids,
+ };
+
+diff --git a/include/linux/usb.h b/include/linux/usb.h
+index 25f8e62a3..5f3ae2186 100644
+--- a/include/linux/usb.h
++++ b/include/linux/usb.h
+@@ -1194,6 +1194,7 @@ struct usbdrv_wrap {
+ * post_reset method is called.
+ * @post_reset: Called by usb_reset_device() after the device
+ * has been reset
++ * @shutdown: Called at shut-down time to quiesce the device.
+ * @id_table: USB drivers use ID table to support hotplugging.
+ * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set
+ * or your driver's probe function will never get called.
+@@ -1245,6 +1246,8 @@ struct usb_driver {
+ int (*pre_reset)(struct usb_interface *intf);
+ int (*post_reset)(struct usb_interface *intf);
+
++ void (*shutdown)(struct usb_interface *intf);
++
+ const struct usb_device_id *id_table;
+ const struct attribute_group **dev_groups;
+
+--
+2.42.0
+
+From 337d6f6e34daaa786a0fb70d0dbd553288cd5ecd Mon Sep 17 00:00:00 2001
+From: Kerem Karabay <kekrby@gmail.com>
+Date: Fri, 4 Aug 2023 17:49:25 +0300
+Subject: [PATCH 11/12] drm/format-helper: add helper for BGR888 to XRGB8888
+ conversion
+
+Add XRGB8888 emulation helper for devices that only support BGR888.
+
+Signed-off-by: Kerem Karabay <kekrby@gmail.com>
+---
+ drivers/gpu/drm/drm_format_helper.c | 53 ++++++++++++++
+ .../gpu/drm/tests/drm_format_helper_test.c | 69 +++++++++++++++++++
+ include/drm/drm_format_helper.h | 3 +
+ 3 files changed, 125 insertions(+)
+
+diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c
+index f93a4efce..5508fbde1 100644
+--- a/drivers/gpu/drm/drm_format_helper.c
++++ b/drivers/gpu/drm/drm_format_helper.c
+@@ -601,6 +601,56 @@ void drm_fb_xrgb8888_to_rgb888(struct iosys_map *dst, const unsigned int *dst_pi
+ }
+ EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888);
+
++static void drm_fb_xrgb8888_to_bgr888_line(void *dbuf, const void *sbuf, unsigned int pixels)
++{
++ u8 *dbuf8 = dbuf;
++ const __le32 *sbuf32 = sbuf;
++ unsigned int x;
++ u32 pix;
++
++ for (x = 0; x < pixels; x++) {
++ pix = le32_to_cpu(sbuf32[x]);
++ /* write red-green-blue to output in little endianness */
++ *dbuf8++ = (pix & 0x00FF0000) >> 16;
++ *dbuf8++ = (pix & 0x0000FF00) >> 8;
++ *dbuf8++ = (pix & 0x000000FF) >> 0;
++ }
++}
++
++/**
++ * drm_fb_xrgb8888_to_bgr888 - Convert XRGB8888 to BGR888 clip buffer
++ * @dst: Array of BGR888 destination buffers
++ * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines
++ * within @dst; can be NULL if scanlines are stored next to each other.
++ * @src: Array of XRGB8888 source buffers
++ * @fb: DRM framebuffer
++ * @clip: Clip rectangle area to copy
++ *
++ * This function copies parts of a framebuffer to display memory and converts the
++ * color format during the process. Destination and framebuffer formats must match. The
++ * parameters @dst, @dst_pitch and @src refer to arrays. Each array must have at
++ * least as many entries as there are planes in @fb's format. Each entry stores the
++ * value for the format's respective color plane at the same index.
++ *
++ * This function does not apply clipping on @dst (i.e. the destination is at the
++ * top-left corner).
++ *
++ * Drivers can use this function for BGR888 devices that don't natively
++ * support XRGB8888.
++ */
++void drm_fb_xrgb8888_to_bgr888(struct iosys_map *dst, const unsigned int *dst_pitch,
++ const struct iosys_map *src, const struct drm_framebuffer *fb,
++ const struct drm_rect *clip)
++{
++ static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = {
++ 3,
++ };
++
++ drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false,
++ drm_fb_xrgb8888_to_bgr888_line);
++}
++EXPORT_SYMBOL(drm_fb_xrgb8888_to_bgr888);
++
+ static void drm_fb_xrgb8888_to_argb8888_line(void *dbuf, const void *sbuf, unsigned int pixels)
+ {
+ __le32 *dbuf32 = dbuf;
+@@ -925,6 +975,9 @@ int drm_fb_blit(struct iosys_map *dst, const unsigned int *dst_pitch, uint32_t d
+ } else if (dst_format == DRM_FORMAT_RGB888) {
+ drm_fb_xrgb8888_to_rgb888(dst, dst_pitch, src, fb, clip);
+ return 0;
++ } else if (dst_format == DRM_FORMAT_BGR888) {
++ drm_fb_xrgb8888_to_bgr888(dst, dst_pitch, src, fb, clip);
++ return 0;
+ } else if (dst_format == DRM_FORMAT_ARGB8888) {
+ drm_fb_xrgb8888_to_argb8888(dst, dst_pitch, src, fb, clip);
+ return 0;
+diff --git a/drivers/gpu/drm/tests/drm_format_helper_test.c b/drivers/gpu/drm/tests/drm_format_helper_test.c
+index 474bb7a1c..dff7fabd9 100644
+--- a/drivers/gpu/drm/tests/drm_format_helper_test.c
++++ b/drivers/gpu/drm/tests/drm_format_helper_test.c
+@@ -52,6 +52,11 @@ struct convert_to_rgb888_result {
+ const u8 expected[TEST_BUF_SIZE];
+ };
+
++struct convert_to_bgr888_result {
++ unsigned int dst_pitch;
++ const u8 expected[TEST_BUF_SIZE];
++};
++
+ struct convert_to_argb8888_result {
+ unsigned int dst_pitch;
+ const u32 expected[TEST_BUF_SIZE];
+@@ -84,6 +89,7 @@ struct convert_xrgb8888_case {
+ struct convert_to_argb1555_result argb1555_result;
+ struct convert_to_rgba5551_result rgba5551_result;
+ struct convert_to_rgb888_result rgb888_result;
++ struct convert_to_bgr888_result bgr888_result;
+ struct convert_to_argb8888_result argb8888_result;
+ struct convert_to_xrgb2101010_result xrgb2101010_result;
+ struct convert_to_argb2101010_result argb2101010_result;
+@@ -125,6 +131,10 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = {
+ .dst_pitch = TEST_USE_DEFAULT_PITCH,
+ .expected = { 0x00, 0x00, 0xFF },
+ },
++ .bgr888_result = {
++ .dst_pitch = TEST_USE_DEFAULT_PITCH,
++ .expected = { 0xFF, 0x00, 0x00 },
++ },
+ .argb8888_result = {
+ .dst_pitch = TEST_USE_DEFAULT_PITCH,
+ .expected = { 0xFFFF0000 },
+@@ -179,6 +189,10 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = {
+ .dst_pitch = TEST_USE_DEFAULT_PITCH,
+ .expected = { 0x00, 0x00, 0xFF },
+ },
++ .bgr888_result = {
++ .dst_pitch = TEST_USE_DEFAULT_PITCH,
++ .expected = { 0xFF, 0x00, 0x00 },
++ },
+ .argb8888_result = {
+ .dst_pitch = TEST_USE_DEFAULT_PITCH,
+ .expected = { 0xFFFF0000 },
+@@ -280,6 +294,15 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = {
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
+ },
+ },
++ .bgr888_result = {
++ .dst_pitch = TEST_USE_DEFAULT_PITCH,
++ .expected = {
++ 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
++ 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00,
++ 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF,
++ 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
++ },
++ },
+ .argb8888_result = {
+ .dst_pitch = TEST_USE_DEFAULT_PITCH,
+ .expected = {
+@@ -391,6 +414,17 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ },
++ .bgr888_result = {
++ .dst_pitch = 15,
++ .expected = {
++ 0x0E, 0x44, 0x9C, 0x11, 0x4D, 0x05, 0xA8, 0xF3, 0x03,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x6C, 0xF0, 0x73, 0x0E, 0x44, 0x9C, 0x11, 0x4D, 0x05,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xA8, 0x03, 0x03, 0x6C, 0xF0, 0x73, 0x0E, 0x44, 0x9C,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ },
++ },
+ .argb8888_result = {
+ .dst_pitch = 20,
+ .expected = {
+@@ -727,6 +761,40 @@ static void drm_test_fb_xrgb8888_to_rgb888(struct kunit *test)
+ KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size);
+ }
+
++static void drm_test_fb_xrgb8888_to_bgr888(struct kunit *test)
++{
++ const struct convert_xrgb8888_case *params = test->param_value;
++ const struct convert_to_bgr888_result *result = &params->bgr888_result;
++ size_t dst_size;
++ u8 *buf = NULL;
++ __le32 *xrgb8888 = NULL;
++ struct iosys_map dst, src;
++
++ struct drm_framebuffer fb = {
++ .format = drm_format_info(DRM_FORMAT_XRGB8888),
++ .pitches = { params->pitch, 0, 0 },
++ };
++
++ dst_size = conversion_buf_size(DRM_FORMAT_BGR888, result->dst_pitch,
++ &params->clip, 0);
++ KUNIT_ASSERT_GT(test, dst_size, 0);
++
++ buf = kunit_kzalloc(test, dst_size, GFP_KERNEL);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf);
++ iosys_map_set_vaddr(&dst, buf);
++
++ xrgb8888 = cpubuf_to_le32(test, params->xrgb8888, TEST_BUF_SIZE);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888);
++ iosys_map_set_vaddr(&src, xrgb8888);
++
++ /*
++ * BGR888 expected results are already in little-endian
++ * order, so there's no need to convert the test output.
++ */
++ drm_fb_xrgb8888_to_bgr888(&dst, &result->dst_pitch, &src, &fb, &params->clip);
++ KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size);
++}
++
+ static void drm_test_fb_xrgb8888_to_argb8888(struct kunit *test)
+ {
+ const struct convert_xrgb8888_case *params = test->param_value;
+@@ -858,6 +926,7 @@ static struct kunit_case drm_format_helper_test_cases[] = {
+ KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_argb1555, convert_xrgb8888_gen_params),
+ KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_rgba5551, convert_xrgb8888_gen_params),
+ KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_rgb888, convert_xrgb8888_gen_params),
++ KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_bgr888, convert_xrgb8888_gen_params),
+ KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_argb8888, convert_xrgb8888_gen_params),
+ KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_xrgb2101010, convert_xrgb8888_gen_params),
+ KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_argb2101010, convert_xrgb8888_gen_params),
+diff --git a/include/drm/drm_format_helper.h b/include/drm/drm_format_helper.h
+index 291deb094..7fc553318 100644
+--- a/include/drm/drm_format_helper.h
++++ b/include/drm/drm_format_helper.h
+@@ -42,6 +42,9 @@ void drm_fb_xrgb8888_to_rgba5551(struct iosys_map *dst, const unsigned int *dst_
+ void drm_fb_xrgb8888_to_rgb888(struct iosys_map *dst, const unsigned int *dst_pitch,
+ const struct iosys_map *src, const struct drm_framebuffer *fb,
+ const struct drm_rect *clip);
++void drm_fb_xrgb8888_to_bgr888(struct iosys_map *dst, const unsigned int *dst_pitch,
++ const struct iosys_map *src, const struct drm_framebuffer *fb,
++ const struct drm_rect *clip);
+ void drm_fb_xrgb8888_to_argb8888(struct iosys_map *dst, const unsigned int *dst_pitch,
+ const struct iosys_map *src, const struct drm_framebuffer *fb,
+ const struct drm_rect *clip);
+--
+2.42.0
+
+From 1f0b6c21c4d56f5be74c4d7d0665525862e307c3 Mon Sep 17 00:00:00 2001
+From: Kerem Karabay <kekrby@gmail.com>
+Date: Sat, 6 May 2023 17:30:09 +0300
+Subject: [PATCH 12/12] drm/tiny: add driver for Apple Touch Bars in x86 Macs
+
+The Touch Bars found on x86 Macs support two USB configurations: one
+where the device presents itself as a HID keyboard and can display
+predefined sets of keys, and one where the operating system has full
+control over what is displayed. This commit adds support for the display
+functionality of the second configuration.
+
+Note that this driver has only been tested on T2 Macs, and only includes
+the USB device ID for these devices. Testing on T1 Macs would be
+appreciated.
+
+Credit goes to @imbushuo on GitHub for reverse engineering most of the
+protocol.
+
+Signed-off-by: Kerem Karabay <kekrby@gmail.com>
+---
+ MAINTAINERS | 6 +
+ drivers/gpu/drm/tiny/Kconfig | 12 +
+ drivers/gpu/drm/tiny/Makefile | 1 +
+ drivers/gpu/drm/tiny/appletbdrm.c | 624 ++++++++++++++++++++++++++++++
+ 4 files changed, 643 insertions(+)
+ create mode 100644 drivers/gpu/drm/tiny/appletbdrm.c
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 519b3b736..dfc63d257 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -6372,6 +6372,12 @@ S: Supported
+ T: git git://anongit.freedesktop.org/drm/drm-misc
+ F: drivers/gpu/drm/sun4i/sun8i*
+
++DRM DRIVER FOR APPLE TOUCH BARS
++M: Kerem Karabay <kekrby@gmail.com>
++L: dri-devel@lists.freedesktop.org
++S: Maintained
++F: drivers/gpu/drm/tiny/appletbdrm.c
++
+ DRM DRIVER FOR ARM PL111 CLCD
+ M: Emma Anholt <emma@anholt.net>
+ S: Supported
+diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig
+index f6889f649..559a97bce 100644
+--- a/drivers/gpu/drm/tiny/Kconfig
++++ b/drivers/gpu/drm/tiny/Kconfig
+@@ -1,5 +1,17 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+
++config DRM_APPLETBDRM
++ tristate "DRM support for Apple Touch Bars"
++ depends on DRM && USB && MMU
++ select DRM_KMS_HELPER
++ select DRM_GEM_SHMEM_HELPER
++ help
++ Say Y here if you want support for the display of Touch Bars on x86
++ MacBook Pros.
++
++ To compile this driver as a module, choose M here: the
++ module will be called appletbdrm.
++
+ config DRM_ARCPGU
+ tristate "ARC PGU"
+ depends on DRM && OF
+diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile
+index 76dde89a0..9a1b412e7 100644
+--- a/drivers/gpu/drm/tiny/Makefile
++++ b/drivers/gpu/drm/tiny/Makefile
+@@ -1,5 +1,6 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+
++obj-$(CONFIG_DRM_APPLETBDRM) += appletbdrm.o
+ obj-$(CONFIG_DRM_ARCPGU) += arcpgu.o
+ obj-$(CONFIG_DRM_BOCHS) += bochs.o
+ obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus.o
+diff --git a/drivers/gpu/drm/tiny/appletbdrm.c b/drivers/gpu/drm/tiny/appletbdrm.c
+new file mode 100644
+index 000000000..33a99436b
+--- /dev/null
++++ b/drivers/gpu/drm/tiny/appletbdrm.c
+@@ -0,0 +1,624 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Apple Touch Bar DRM Driver
++ *
++ * Copyright (c) 2023 Kerem Karabay <kekrby@gmail.com>
++ */
++
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
++#include <asm/unaligned.h>
++
++#include <linux/usb.h>
++#include <linux/module.h>
++
++#include <drm/drm_drv.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_probe_helper.h>
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_damage_helper.h>
++#include <drm/drm_format_helper.h>
++#include <drm/drm_gem_shmem_helper.h>
++#include <drm/drm_gem_atomic_helper.h>
++#include <drm/drm_simple_kms_helper.h>
++#include <drm/drm_gem_framebuffer_helper.h>
++
++#define _APPLETBDRM_FOURCC(s) (((s)[0] << 24) | ((s)[1] << 16) | ((s)[2] << 8) | (s)[3])
++#define APPLETBDRM_FOURCC(s) _APPLETBDRM_FOURCC(#s)
++
++#define APPLETBDRM_PIXEL_FORMAT APPLETBDRM_FOURCC(RGBA) /* The actual format is BGR888 */
++#define APPLETBDRM_BITS_PER_PIXEL 24
++
++#define APPLETBDRM_MSG_CLEAR_DISPLAY APPLETBDRM_FOURCC(CLRD)
++#define APPLETBDRM_MSG_GET_INFORMATION APPLETBDRM_FOURCC(GINF)
++#define APPLETBDRM_MSG_UPDATE_COMPLETE APPLETBDRM_FOURCC(UDCL)
++#define APPLETBDRM_MSG_SIGNAL_READINESS APPLETBDRM_FOURCC(REDY)
++
++#define APPLETBDRM_BULK_MSG_TIMEOUT 1000
++
++#define drm_to_adev(_drm) container_of(_drm, struct appletbdrm_device, drm)
++#define adev_to_udev(adev) interface_to_usbdev(to_usb_interface(adev->dev))
++
++struct appletbdrm_device {
++ struct device *dev;
++
++ u8 in_ep;
++ u8 out_ep;
++
++ u32 width;
++ u32 height;
++
++ struct drm_device drm;
++ struct drm_display_mode mode;
++ struct drm_connector connector;
++ struct drm_simple_display_pipe pipe;
++
++ bool readiness_signal_received;
++};
++
++struct appletbdrm_request_header {
++ __le16 unk_00;
++ __le16 unk_02;
++ __le32 unk_04;
++ __le32 unk_08;
++ __le32 size;
++} __packed;
++
++struct appletbdrm_response_header {
++ u8 unk_00[16];
++ u32 msg;
++} __packed;
++
++struct appletbdrm_simple_request {
++ struct appletbdrm_request_header header;
++ u32 msg;
++ u8 unk_14[8];
++ __le32 size;
++} __packed;
++
++struct appletbdrm_information {
++ struct appletbdrm_response_header header;
++ u8 unk_14[12];
++ __le32 width;
++ __le32 height;
++ u8 bits_per_pixel;
++ __le32 bytes_per_row;
++ __le32 orientation;
++ __le32 bitmap_info;
++ u32 pixel_format;
++ __le32 width_inches; /* floating point */
++ __le32 height_inches; /* floating point */
++} __packed;
++
++struct appletbdrm_frame {
++ __le16 begin_x;
++ __le16 begin_y;
++ __le16 width;
++ __le16 height;
++ __le32 buf_size;
++ u8 buf[];
++} __packed;
++
++struct appletbdrm_fb_request_footer {
++ u8 unk_00[12];
++ __le32 unk_0c;
++ u8 unk_10[12];
++ __le32 unk_1c;
++ __le64 timestamp;
++ u8 unk_28[12];
++ __le32 unk_34;
++ u8 unk_38[20];
++ __le32 unk_4c;
++} __packed;
++
++struct appletbdrm_fb_request {
++ struct appletbdrm_request_header header;
++ __le16 unk_10;
++ u8 msg_id;
++ u8 unk_13[29];
++ /*
++ * Contents of `data`:
++ * - struct appletbdrm_frame frames[];
++ * - struct appletbdrm_fb_request_footer footer;
++ * - padding to make the total size a multiple of 16
++ */
++ u8 data[];
++} __packed;
++
++struct appletbdrm_fb_request_response {
++ struct appletbdrm_response_header header;
++ u8 unk_14[12];
++ __le64 timestamp;
++} __packed;
++
++static int appletbdrm_send_request(struct appletbdrm_device *adev,
++ struct appletbdrm_request_header *request, size_t size)
++{
++ struct usb_device *udev = adev_to_udev(adev);
++ struct drm_device *drm = &adev->drm;
++ int ret, actual_size;
++
++ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, adev->out_ep),
++ request, size, &actual_size, APPLETBDRM_BULK_MSG_TIMEOUT);
++ if (ret) {
++ drm_err(drm, "Failed to send message (%pe)\n", ERR_PTR(ret));
++ return ret;
++ }
++
++ if (actual_size != size) {
++ drm_err(drm, "Actual size (%d) doesn't match expected size (%lu)\n",
++ actual_size, size);
++ return -EIO;
++ }
++
++ return ret;
++}
++
++static int appletbdrm_read_response(struct appletbdrm_device *adev,
++ struct appletbdrm_response_header *response,
++ size_t size, u32 expected_response)
++{
++ struct usb_device *udev = adev_to_udev(adev);
++ struct drm_device *drm = &adev->drm;
++ int ret, actual_size;
++
++retry:
++ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, adev->in_ep),
++ response, size, &actual_size, APPLETBDRM_BULK_MSG_TIMEOUT);
++ if (ret) {
++ drm_err(drm, "Failed to read response (%pe)\n", ERR_PTR(ret));
++ return ret;
++ }
++
++ /*
++ * The device responds to the first request sent in a particular
++ * timeframe after the USB device configuration is set with a readiness
++ * signal, in which case the response should be read again
++ */
++ if (response->msg == APPLETBDRM_MSG_SIGNAL_READINESS) {
++ if (!adev->readiness_signal_received) {
++ adev->readiness_signal_received = true;
++ goto retry;
++ }
++
++ drm_err(drm, "Encountered unexpected readiness signal\n");
++ return -EIO;
++ }
++
++ if (actual_size != size) {
++ drm_err(drm, "Actual size (%d) doesn't match expected size (%lu)\n",
++ actual_size, size);
++ return -EIO;
++ }
++
++ if (response->msg != expected_response) {
++ drm_err(drm, "Unexpected response from device (expected %p4ch found %p4ch)\n",
++ &expected_response, &response->msg);
++ return -EIO;
++ }
++
++ return 0;
++}
++
++static int appletbdrm_send_msg(struct appletbdrm_device *adev, u32 msg)
++{
++ struct appletbdrm_simple_request *request;
++ int ret;
++
++ request = kzalloc(sizeof(*request), GFP_KERNEL);
++ if (!request)
++ return -ENOMEM;
++
++ request->header.unk_00 = cpu_to_le16(2);
++ request->header.unk_02 = cpu_to_le16(0x1512);
++ request->header.size = cpu_to_le32(sizeof(*request) - sizeof(request->header));
++ request->msg = msg;
++ request->size = request->header.size;
++
++ ret = appletbdrm_send_request(adev, &request->header, sizeof(*request));
++
++ kfree(request);
++
++ return ret;
++}
++
++static int appletbdrm_clear_display(struct appletbdrm_device *adev)
++{
++ return appletbdrm_send_msg(adev, APPLETBDRM_MSG_CLEAR_DISPLAY);
++}
++
++static int appletbdrm_signal_readiness(struct appletbdrm_device *adev)
++{
++ return appletbdrm_send_msg(adev, APPLETBDRM_MSG_SIGNAL_READINESS);
++}
++
++static int appletbdrm_get_information(struct appletbdrm_device *adev)
++{
++ struct appletbdrm_information *info;
++ struct drm_device *drm = &adev->drm;
++ u8 bits_per_pixel;
++ u32 pixel_format;
++ int ret;
++
++ info = kzalloc(sizeof(*info), GFP_KERNEL);
++ if (!info)
++ return -ENOMEM;
++
++ ret = appletbdrm_send_msg(adev, APPLETBDRM_MSG_GET_INFORMATION);
++ if (ret)
++ return ret;
++
++ ret = appletbdrm_read_response(adev, &info->header, sizeof(*info),
++ APPLETBDRM_MSG_GET_INFORMATION);
++ if (ret)
++ goto free_info;
++
++ bits_per_pixel = info->bits_per_pixel;
++ pixel_format = get_unaligned(&info->pixel_format);
++
++ adev->width = get_unaligned_le32(&info->width);
++ adev->height = get_unaligned_le32(&info->height);
++
++ if (bits_per_pixel != APPLETBDRM_BITS_PER_PIXEL) {
++ drm_err(drm, "Encountered unexpected bits per pixel value (%d)\n", bits_per_pixel);
++ ret = -EINVAL;
++ goto free_info;
++ }
++
++ if (pixel_format != APPLETBDRM_PIXEL_FORMAT) {
++ drm_err(drm, "Encountered unknown pixel format (%p4ch)\n", &pixel_format);
++ ret = -EINVAL;
++ goto free_info;
++ }
++
++free_info:
++ kfree(info);
++
++ return ret;
++}
++
++static u32 rect_size(struct drm_rect *rect)
++{
++ return drm_rect_width(rect) * drm_rect_height(rect) * (APPLETBDRM_BITS_PER_PIXEL / 8);
++}
++
++static int appletbdrm_flush_damage(struct appletbdrm_device *adev,
++ struct drm_plane_state *old_state,
++ struct drm_plane_state *state)
++{
++ struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
++ struct appletbdrm_fb_request_response *response;
++ struct appletbdrm_fb_request_footer *footer;
++ struct drm_atomic_helper_damage_iter iter;
++ struct drm_framebuffer *fb = state->fb;
++ struct appletbdrm_fb_request *request;
++ struct drm_device *drm = &adev->drm;
++ struct appletbdrm_frame *frame;
++ u64 timestamp = ktime_get_ns();
++ struct drm_rect damage;
++ size_t frames_size = 0;
++ size_t request_size;
++ int ret;
++
++ drm_atomic_helper_damage_iter_init(&iter, old_state, state);
++ drm_atomic_for_each_plane_damage(&iter, &damage) {
++ frames_size += struct_size(frame, buf, rect_size(&damage));
++ }
++
++ if (!frames_size)
++ return 0;
++
++ request_size = ALIGN(sizeof(*request) + frames_size + sizeof(*footer), 16);
++
++ request = kzalloc(request_size, GFP_KERNEL);
++ if (!request)
++ return -ENOMEM;
++
++ response = kzalloc(sizeof(*response), GFP_KERNEL);
++ if (!response) {
++ ret = -ENOMEM;
++ goto free_request;
++ }
++
++ ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
++ if (ret) {
++ drm_err(drm, "Failed to start CPU framebuffer access (%pe)\n", ERR_PTR(ret));
++ goto free_response;
++ }
++
++ request->header.unk_00 = cpu_to_le16(2);
++ request->header.unk_02 = cpu_to_le16(0x12);
++ request->header.unk_04 = cpu_to_le32(9);
++ request->header.size = cpu_to_le32(request_size - sizeof(request->header));
++ request->unk_10 = cpu_to_le16(1);
++ request->msg_id = timestamp & 0xff;
++
++ frame = (struct appletbdrm_frame *)request->data;
++
++ drm_atomic_helper_damage_iter_init(&iter, old_state, state);
++ drm_atomic_for_each_plane_damage(&iter, &damage) {
++ struct iosys_map dst = IOSYS_MAP_INIT_VADDR(frame->buf);
++ u32 buf_size = rect_size(&damage);
++
++ /*
++ * The coordinates need to be translated to the coordinate
++ * system the device expects, see the comment in
++ * appletbdrm_setup_mode_config
++ */
++ frame->begin_x = cpu_to_le16(damage.y1);
++ frame->begin_y = cpu_to_le16(adev->height - damage.x2);
++ frame->width = cpu_to_le16(drm_rect_height(&damage));
++ frame->height = cpu_to_le16(drm_rect_width(&damage));
++ frame->buf_size = cpu_to_le32(buf_size);
++
++ ret = drm_fb_blit(&dst, NULL, DRM_FORMAT_BGR888,
++ &shadow_plane_state->data[0], fb, &damage);
++ if (ret) {
++ drm_err(drm, "Failed to copy damage clip (%pe)\n", ERR_PTR(ret));
++ goto end_fb_cpu_access;
++ }
++
++ frame = (void *)frame + struct_size(frame, buf, buf_size);
++ }
++
++ footer = (struct appletbdrm_fb_request_footer *)&request->data[frames_size];
++
++ footer->unk_0c = cpu_to_le32(0xfffe);
++ footer->unk_1c = cpu_to_le32(0x80001);
++ footer->unk_34 = cpu_to_le32(0x80002);
++ footer->unk_4c = cpu_to_le32(0xffff);
++ footer->timestamp = cpu_to_le64(timestamp);
++
++ ret = appletbdrm_send_request(adev, &request->header, request_size);
++ if (ret)
++ goto end_fb_cpu_access;
++
++ ret = appletbdrm_read_response(adev, &response->header, sizeof(*response),
++ APPLETBDRM_MSG_UPDATE_COMPLETE);
++ if (ret)
++ goto end_fb_cpu_access;
++
++ if (response->timestamp != footer->timestamp) {
++ drm_err(drm, "Response timestamp (%llu) doesn't match request timestamp (%llu)\n",
++ le64_to_cpu(response->timestamp), timestamp);
++ goto end_fb_cpu_access;
++ }
++
++end_fb_cpu_access:
++ drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
++free_response:
++ kfree(response);
++free_request:
++ kfree(request);
++
++ return ret;
++}
++
++static int appletbdrm_connector_helper_get_modes(struct drm_connector *connector)
++{
++ struct appletbdrm_device *adev = drm_to_adev(connector->dev);
++
++ return drm_connector_helper_get_modes_fixed(connector, &adev->mode);
++}
++
++static enum drm_mode_status appletbdrm_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
++ const struct drm_display_mode *mode)
++{
++ struct drm_crtc *crtc = &pipe->crtc;
++ struct appletbdrm_device *adev = drm_to_adev(crtc->dev);
++
++ return drm_crtc_helper_mode_valid_fixed(crtc, mode, &adev->mode);
++}
++
++static void appletbdrm_pipe_disable(struct drm_simple_display_pipe *pipe)
++{
++ struct appletbdrm_device *adev = drm_to_adev(pipe->crtc.dev);
++ int idx;
++
++ if (!drm_dev_enter(&adev->drm, &idx))
++ return;
++
++ appletbdrm_clear_display(adev);
++
++ drm_dev_exit(idx);
++}
++
++static void appletbdrm_pipe_update(struct drm_simple_display_pipe *pipe,
++ struct drm_plane_state *old_state)
++{
++ struct drm_crtc *crtc = &pipe->crtc;
++ struct appletbdrm_device *adev = drm_to_adev(crtc->dev);
++ int idx;
++
++ if (!crtc->state->active || !drm_dev_enter(&adev->drm, &idx))
++ return;
++
++ appletbdrm_flush_damage(adev, old_state, pipe->plane.state);
++
++ drm_dev_exit(idx);
++}
++
++static const u32 appletbdrm_formats[] = {
++ DRM_FORMAT_BGR888,
++ DRM_FORMAT_XRGB8888, /* emulated */
++};
++
++static const struct drm_mode_config_funcs appletbdrm_mode_config_funcs = {
++ .fb_create = drm_gem_fb_create_with_dirty,
++ .atomic_check = drm_atomic_helper_check,
++ .atomic_commit = drm_atomic_helper_commit,
++};
++
++static const struct drm_connector_funcs appletbdrm_connector_funcs = {
++ .reset = drm_atomic_helper_connector_reset,
++ .destroy = drm_connector_cleanup,
++ .fill_modes = drm_helper_probe_single_connector_modes,
++ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
++ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
++};
++
++static const struct drm_connector_helper_funcs appletbdrm_connector_helper_funcs = {
++ .get_modes = appletbdrm_connector_helper_get_modes,
++};
++
++static const struct drm_simple_display_pipe_funcs appletbdrm_pipe_funcs = {
++ DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS,
++ .update = appletbdrm_pipe_update,
++ .disable = appletbdrm_pipe_disable,
++ .mode_valid = appletbdrm_pipe_mode_valid,
++};
++
++DEFINE_DRM_GEM_FOPS(appletbdrm_drm_fops);
++
++static const struct drm_driver appletbdrm_drm_driver = {
++ DRM_GEM_SHMEM_DRIVER_OPS,
++ .name = "appletbdrm",
++ .desc = "Apple Touch Bar DRM Driver",
++ .date = "20230910",
++ .major = 1,
++ .minor = 0,
++ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
++ .fops = &appletbdrm_drm_fops,
++};
++
++static int appletbdrm_setup_mode_config(struct appletbdrm_device *adev)
++{
++ struct drm_connector *connector = &adev->connector;
++ struct drm_device *drm = &adev->drm;
++ struct device *dev = adev->dev;
++ int ret;
++
++ ret = drmm_mode_config_init(drm);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to initialize mode configuration\n");
++
++ /*
++ * The coordinate system used by the device is different from the
++ * coordinate system of the framebuffer in that the x and y axes are
++ * swapped, and that the y axis is inverted; so what the device reports
++ * as the height is actually the width of the framebuffer and vice
++ * versa
++ */
++ drm->mode_config.min_width = 0;
++ drm->mode_config.min_height = 0;
++ drm->mode_config.max_width = max(adev->height, DRM_SHADOW_PLANE_MAX_WIDTH);
++ drm->mode_config.max_height = max(adev->width, DRM_SHADOW_PLANE_MAX_HEIGHT);
++ drm->mode_config.preferred_depth = APPLETBDRM_BITS_PER_PIXEL;
++ drm->mode_config.funcs = &appletbdrm_mode_config_funcs;
++
++ adev->mode = (struct drm_display_mode) {
++ DRM_MODE_INIT(60, adev->height, adev->width,
++ DRM_MODE_RES_MM(adev->height, 218),
++ DRM_MODE_RES_MM(adev->width, 218))
++ };
++
++ ret = drm_connector_init(drm, connector,
++ &appletbdrm_connector_funcs, DRM_MODE_CONNECTOR_USB);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to initialize connector\n");
++
++ drm_connector_helper_add(connector, &appletbdrm_connector_helper_funcs);
++
++ ret = drm_connector_set_panel_orientation(connector,
++ DRM_MODE_PANEL_ORIENTATION_RIGHT_UP);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to set panel orientation\n");
++
++ connector->display_info.non_desktop = true;
++ ret = drm_object_property_set_value(&connector->base,
++ drm->mode_config.non_desktop_property, true);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to set non-desktop property\n");
++
++ ret = drm_simple_display_pipe_init(drm, &adev->pipe, &appletbdrm_pipe_funcs,
++ appletbdrm_formats, ARRAY_SIZE(appletbdrm_formats),
++ NULL, &adev->connector);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to initialize simple display pipe\n");
++
++ drm_plane_enable_fb_damage_clips(&adev->pipe.plane);
++
++ drm_mode_config_reset(drm);
++
++ ret = drm_dev_register(drm, 0);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to register DRM device\n");
++
++ return 0;
++}
++
++static int appletbdrm_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ struct usb_endpoint_descriptor *bulk_in, *bulk_out;
++ struct device *dev = &intf->dev;
++ struct appletbdrm_device *adev;
++ int ret;
++
++ ret = usb_find_common_endpoints(intf->cur_altsetting, &bulk_in, &bulk_out, NULL, NULL);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to find bulk endpoints\n");
++
++ adev = devm_drm_dev_alloc(dev, &appletbdrm_drm_driver, struct appletbdrm_device, drm);
++ if (IS_ERR(adev))
++ return PTR_ERR(adev);
++
++ adev->dev = dev;
++ adev->in_ep = bulk_in->bEndpointAddress;
++ adev->out_ep = bulk_out->bEndpointAddress;
++
++ usb_set_intfdata(intf, adev);
++
++ ret = appletbdrm_get_information(adev);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to get display information\n");
++
++ ret = appletbdrm_signal_readiness(adev);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to signal readiness\n");
++
++ ret = appletbdrm_clear_display(adev);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to clear display\n");
++
++ return appletbdrm_setup_mode_config(adev);
++}
++
++static void appletbdrm_disconnect(struct usb_interface *intf)
++{
++ struct appletbdrm_device *adev = usb_get_intfdata(intf);
++ struct drm_device *drm = &adev->drm;
++
++ drm_dev_unplug(drm);
++ drm_atomic_helper_shutdown(drm);
++}
++
++static void appletbdrm_shutdown(struct usb_interface *intf)
++{
++ struct appletbdrm_device *adev = usb_get_intfdata(intf);
++
++ /*
++ * The framebuffer needs to be cleared on shutdown since its content
++ * persists across boots
++ */
++ drm_atomic_helper_shutdown(&adev->drm);
++}
++
++static const struct usb_device_id appletbdrm_usb_id_table[] = {
++ { USB_DEVICE_INTERFACE_CLASS(0x05ac, 0x8302, USB_CLASS_AUDIO_VIDEO) },
++ {}
++};
++MODULE_DEVICE_TABLE(usb, appletbdrm_usb_id_table);
++
++static struct usb_driver appletbdrm_usb_driver = {
++ .name = "appletbdrm",
++ .probe = appletbdrm_probe,
++ .disconnect = appletbdrm_disconnect,
++ .shutdown = appletbdrm_shutdown,
++ .id_table = appletbdrm_usb_id_table,
++};
++module_usb_driver(appletbdrm_usb_driver);
++
++MODULE_AUTHOR("Kerem Karabay <kekrby@gmail.com>");
++MODULE_DESCRIPTION("Apple Touch Bar DRM Driver");
++MODULE_LICENSE("GPL");
+--
+2.42.0
+
+From e34c6d09241ba826a6e9b2b0e50e306b273b7bda Mon Sep 17 00:00:00 2001
+From: Orlando Chamberlain <orlandoch.dev@gmail.com>
+Date: Thu, 16 Feb 2023 12:32:34 +1100
+Subject: [PATCH 5/8] Documentation: leds: standardise keyboard backlight led
+ names
+
+Advice use of either "input*:*:kbd_backlight" or ":*:kbd_backlight". We
+don't want people using vendor or product name (e.g. "smc", "apple",
+"asus") as this information is available from sysfs anyway, and it made the
+folder names inconsistent.
+
+Signed-off-by: Orlando Chamberlain <orlandoch.dev@gmail.com>
+---
+ Documentation/leds/well-known-leds.txt | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/Documentation/leds/well-known-leds.txt b/Documentation/leds/well-known-leds.txt
+index 2160382c86be..4e5429fce4d8 100644
+--- a/Documentation/leds/well-known-leds.txt
++++ b/Documentation/leds/well-known-leds.txt
+@@ -44,6 +44,14 @@ Legacy: "lp5523:kb{1,2,3,4,5,6}" (Nokia N900)
+
+ Frontlight/backlight of main keyboard.
+
++Good: ":*:kbd_backlight"
++Good: "input*:*:kbd_backlight"
++Legacy: "*:*:kbd_backlight"
++
++Many drivers have the vendor or product name as the first field of the led name,
++this makes names inconsistent and is redundant as that information is already in
++sysfs.
++
+ Legacy: "button-backlight" (Motorola Droid 4)
+
+ Some phones have touch buttons below screen; it is different from main
+--
+2.39.1
+
+From c124f5401040d02abd6d349979be29acd1e88545 Mon Sep 17 00:00:00 2001
+From: Orlando Chamberlain <orlandoch.dev@gmail.com>
+Date: Fri, 10 Feb 2023 23:14:31 +1100
+Subject: [PATCH 6/8] HID: hid-apple-magic-backlight: Add driver for keyboard
+ backlight on internal Magic Keyboards
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This driver adds support for the keyboard backlight on Intel T2 Macs
+with internal Magic Keyboards (MacBookPro16,x and MacBookAir9,1)
+
+Co-developed-by: Kerem Karabay <kekrby@gmail.com>
+Signed-off-by: Kerem Karabay <kekrby@gmail.com>
+Signed-off-by: Orlando Chamberlain <orlandoch.dev@gmail.com>
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Reviewed-by: Thomas Weißschuh <linux@weissschuh.net>
+---
+ MAINTAINERS | 6 ++
+ drivers/hid/Kconfig | 13 +++
+ drivers/hid/Makefile | 1 +
+ drivers/hid/hid-apple-magic-backlight.c | 120 ++++++++++++++++++++++++
+ 4 files changed, 140 insertions(+)
+ create mode 100644 drivers/hid/hid-apple-magic-backlight.c
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index dfc63d257..9148bda0a 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -9169,6 +9169,12 @@ L: linux-input@vger.kernel.org
+ S: Maintained
+ F: drivers/hid/hid-appletb-*
+
++HID APPLE MAGIC BACKLIGHT DRIVER
++M: Orlando Chamberlain <orlandoch.dev@gmail.com>
++L: linux-input@vger.kernel.org
++S: Maintained
++F: drivers/hid/apple-magic-backlight.c
++
+ HID CORE LAYER
+ M: Jiri Kosina <jikos@kernel.org>
+ M: Benjamin Tissoires <benjamin.tissoires@redhat.com>
+diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
+index 4e238df87..83fbab6d4 100644
+--- a/drivers/hid/Kconfig
++++ b/drivers/hid/Kconfig
+@@ -169,6 +169,19 @@ config HID_APPLETB_KBD
+ To compile this driver as a module, choose M here: the
+ module will be called hid-appletb-kbd.
+
++config HID_APPLE_MAGIC_BACKLIGHT
++ tristate "Apple Magic Keyboard Backlight"
++ depends on USB_HID
++ depends on LEDS_CLASS
++ depends on NEW_LEDS
++ help
++ Say Y here if you want support for the keyboard backlight on Macs with
++ the magic keyboard (MacBookPro16,x and MacBookAir9,1). Note that this
++ driver is not for external magic keyboards.
++
++ To compile this driver as a module, choose M here: the
++ module will be called hid-apple-magic-backlight.
++
+ config HID_ASUS
+ tristate "Asus"
+ depends on USB_HID
+diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
+index 5b60015fd..581f5e720 100644
+--- a/drivers/hid/Makefile
++++ b/drivers/hid/Makefile
+@@ -31,6 +31,7 @@ obj-$(CONFIG_HID_APPLE) += hid-apple.o
+ obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o
+ obj-$(CONFIG_HID_APPLETB_BL) += hid-appletb-bl.o
+ obj-$(CONFIG_HID_APPLETB_KBD) += hid-appletb-kbd.o
++obj-$(CONFIG_HID_APPLE_MAGIC_BACKLIGHT) += hid-apple-magic-backlight.o
+ obj-$(CONFIG_HID_CREATIVE_SB0540) += hid-creative-sb0540.o
+ obj-$(CONFIG_HID_ASUS) += hid-asus.o
+ obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
+diff --git a/drivers/hid/hid-apple-magic-backlight.c b/drivers/hid/hid-apple-magic-backlight.c
+new file mode 100644
+index 000000000..f0fc02ff3
+--- /dev/null
++++ b/drivers/hid/hid-apple-magic-backlight.c
+@@ -0,0 +1,120 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Apple Magic Keyboard Backlight Driver
++ *
++ * For Intel Macs with internal Magic Keyboard (MacBookPro16,1-4 and MacBookAir9,1)
++ *
++ * Copyright (c) 2022 Kerem Karabay <kekrby@gmail.com>
++ * Copyright (c) 2023 Orlando Chamberlain <orlandoch.dev@gmail.com>
++ */
++
++#include <linux/hid.h>
++#include <linux/leds.h>
++#include <linux/device.h>
++#include <linux/errno.h>
++#include <dt-bindings/leds/common.h>
++
++#include "hid-ids.h"
++
++#define HID_USAGE_MAGIC_BL 0xff00000f
++
++#define APPLE_MAGIC_REPORT_ID_POWER 3
++#define APPLE_MAGIC_REPORT_ID_BRIGHTNESS 1
++
++struct apple_magic_backlight {
++ struct led_classdev cdev;
++ struct hid_report *brightness;
++ struct hid_report *power;
++};
++
++static void apple_magic_backlight_report_set(struct hid_report *rep, s32 value, u8 rate)
++{
++ rep->field[0]->value[0] = value;
++ rep->field[1]->value[0] = 0x5e; /* Mimic Windows */
++ rep->field[1]->value[0] |= rate << 8;
++
++ hid_hw_request(rep->device, rep, HID_REQ_SET_REPORT);
++}
++
++static void apple_magic_backlight_set(struct apple_magic_backlight *backlight,
++ int brightness, char rate)
++{
++ apple_magic_backlight_report_set(backlight->power, brightness ? 1 : 0, rate);
++ if (brightness)
++ apple_magic_backlight_report_set(backlight->brightness, brightness, rate);
++}
++
++static int apple_magic_backlight_led_set(struct led_classdev *led_cdev,
++ enum led_brightness brightness)
++{
++ struct apple_magic_backlight *backlight = container_of(led_cdev,
++ struct apple_magic_backlight, cdev);
++
++ apple_magic_backlight_set(backlight, brightness, 1);
++ return 0;
++}
++
++static int apple_magic_backlight_probe(struct hid_device *hdev,
++ const struct hid_device_id *id)
++{
++ struct apple_magic_backlight *backlight;
++ int rc;
++
++ rc = hid_parse(hdev);
++ if (rc)
++ return rc;
++
++ /*
++ * Ensure this usb endpoint is for the keyboard backlight, not touchbar
++ * backlight.
++ */
++ if (hdev->collection[0].usage != HID_USAGE_MAGIC_BL)
++ return -ENODEV;
++
++ backlight = devm_kzalloc(&hdev->dev, sizeof(*backlight), GFP_KERNEL);
++ if (!backlight)
++ return -ENOMEM;
++
++ rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
++ if (rc)
++ return rc;
++
++ backlight->brightness = hid_register_report(hdev, HID_FEATURE_REPORT,
++ APPLE_MAGIC_REPORT_ID_BRIGHTNESS, 0);
++ backlight->power = hid_register_report(hdev, HID_FEATURE_REPORT,
++ APPLE_MAGIC_REPORT_ID_POWER, 0);
++
++ if (!backlight->brightness || !backlight->power) {
++ rc = -ENODEV;
++ goto hw_stop;
++ }
++
++ backlight->cdev.name = ":white:" LED_FUNCTION_KBD_BACKLIGHT;
++ backlight->cdev.max_brightness = backlight->brightness->field[0]->logical_maximum;
++ backlight->cdev.brightness_set_blocking = apple_magic_backlight_led_set;
++
++ apple_magic_backlight_set(backlight, 0, 0);
++
++ return devm_led_classdev_register(&hdev->dev, &backlight->cdev);
++
++hw_stop:
++ hid_hw_stop(hdev);
++ return rc;
++}
++
++static const struct hid_device_id apple_magic_backlight_hid_ids[] = {
++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) },
++ { }
++};
++MODULE_DEVICE_TABLE(hid, apple_magic_backlight_hid_ids);
++
++static struct hid_driver apple_magic_backlight_hid_driver = {
++ .name = "hid-apple-magic-backlight",
++ .id_table = apple_magic_backlight_hid_ids,
++ .probe = apple_magic_backlight_probe,
++};
++module_hid_driver(apple_magic_backlight_hid_driver);
++
++MODULE_DESCRIPTION("MacBook Magic Keyboard Backlight");
++MODULE_AUTHOR("Orlando Chamberlain <orlandoch.dev@gmail.com>");
++MODULE_LICENSE("GPL");
+--
+2.39.2
+
+From 12c7a3306a631a651464ef56318a218dc4cdb157 Mon Sep 17 00:00:00 2001
+From: Orlando Chamberlain <orlandoch.dev@gmail.com>
+Date: Sat, 18 Feb 2023 23:05:05 +1100
+Subject: [PATCH 8/9] i915: 4 lane quirk for mbp15,1
+
+Needed to use iGPU when dGPU was boot GPU
+
+Patch written by Kerem Karabay <kekrby@gmail.com>
+---
+ drivers/gpu/drm/i915/display/intel_ddi.c | 3 +++
+ drivers/gpu/drm/i915/display/intel_quirks.c | 15 +++++++++++++++
+ drivers/gpu/drm/i915/display/intel_quirks.h | 1 +
+ 3 files changed, 19 insertions(+)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
+index 0f1ec2a98cc8..1ec67390f623 100644
+--- a/drivers/gpu/drm/i915/display/intel_ddi.c
++++ b/drivers/gpu/drm/i915/display/intel_ddi.c
+@@ -4097,6 +4097,9 @@ static bool intel_ddi_a_force_4_lanes(struct intel_digital_port *dig_port)
+ if (dig_port->saved_port_bits & DDI_A_4_LANES)
+ return false;
+
++ if (intel_has_quirk(dev_priv, QUIRK_DDI_A_FORCE_4_LANES))
++ return true;
++
+ /* Broxton/Geminilake: Bspec says that DDI_A_4_LANES is the only
+ * supported configuration
+ */
+diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c
+index 6e48d3bcdfec..a8c55e165b46 100644
+--- a/drivers/gpu/drm/i915/display/intel_quirks.c
++++ b/drivers/gpu/drm/i915/display/intel_quirks.c
+@@ -59,6 +59,18 @@ static void quirk_increase_ddi_disabled_time(struct drm_i915_private *i915)
+ drm_info(&i915->drm, "Applying Increase DDI Disabled quirk\n");
+ }
+
++/*
++ * In some cases, the firmware might not set the lane count to 4 (for example,
++ * when booting in some dual GPU Macs with the dGPU as the default GPU), this
++ * quirk is used to force it as otherwise it might not be possible to compute a
++ * valid link configuration.
++ */
++static void quirk_ddi_a_force_4_lanes(struct drm_i915_private *i915)
++{
++ intel_set_quirk(i915, QUIRK_DDI_A_FORCE_4_LANES);
++ drm_info(&i915->drm, "Applying DDI A Forced 4 Lanes quirk\n");
++}
++
+ static void quirk_no_pps_backlight_power_hook(struct drm_i915_private *i915)
+ {
+ intel_set_quirk(i915, QUIRK_NO_PPS_BACKLIGHT_POWER_HOOK);
+@@ -199,6 +211,9 @@ static struct intel_quirk intel_quirks[] = {
+ { 0x3184, 0x1019, 0xa94d, quirk_increase_ddi_disabled_time },
+ /* HP Notebook - 14-r206nv */
+ { 0x0f31, 0x103c, 0x220f, quirk_invert_brightness },
++
++ /* Apple MacBookPro15,1 */
++ { 0x3e9b, 0x106b, 0x0176, quirk_ddi_a_force_4_lanes },
+ };
+
+ void intel_init_quirks(struct drm_i915_private *i915)
+diff --git a/drivers/gpu/drm/i915/display/intel_quirks.h b/drivers/gpu/drm/i915/display/intel_quirks.h
+index 10a4d163149f..78aacf1f6f5c 100644
+--- a/drivers/gpu/drm/i915/display/intel_quirks.h
++++ b/drivers/gpu/drm/i915/display/intel_quirks.h
+@@ -17,6 +17,7 @@ enum intel_quirk_id {
+ QUIRK_INVERT_BRIGHTNESS,
+ QUIRK_LVDS_SSC_DISABLE,
+ QUIRK_NO_PPS_BACKLIGHT_POWER_HOOK,
++ QUIRK_DDI_A_FORCE_4_LANES,
+ };
+
+ void intel_init_quirks(struct drm_i915_private *i915);
+--
+2.39.1
+
+From bd8e785c74e22978648ced004552eb9c137f1eb6 Mon Sep 17 00:00:00 2001
+From: Orlando Chamberlain <orlandoch.dev@gmail.com>
+Date: Fri, 10 Feb 2023 22:45:00 +1100
+Subject: [PATCH 9/9] apple-gmux: allow switching to igpu at probe
+
+This means user don't need to set the gpu-power-prefs efivar to use the
+igpu while runtime switching isn't working, so macOS will be unaffected.
+
+This isn't really upstreamable, what we want upstream is the ability to
+switch at runtime (so both gpus need to be able to probe the eDP panel).
+
+Based off of work by Kerem Karabay <kekrby@gmail.com>
+---
+ drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 3 +++
+ drivers/gpu/vga/vga_switcheroo.c | 7 +------
+ drivers/pci/vgaarb.c | 1 +
+ drivers/platform/x86/apple-gmux.c | 18 ++++++++++++++++++
+ 4 files changed, 23 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+index 81edf66dbea8..8f3daf28665b 100644
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+@@ -2051,6 +2051,9 @@ static int amdgpu_pci_probe(struct pci_dev *pdev,
+ int ret, retry = 0, i;
+ bool supports_atomic = false;
+
++ if (vga_switcheroo_client_probe_defer(pdev))
++ return -EPROBE_DEFER;
++
+ /* skip devices which are owned by radeon */
+ for (i = 0; i < ARRAY_SIZE(amdgpu_unsupported_pciidlist); i++) {
+ if (amdgpu_unsupported_pciidlist[i] == pdev->device)
+diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
+index 365e6ddbe90f..cf357cd3389d 100644
+--- a/drivers/gpu/vga/vga_switcheroo.c
++++ b/drivers/gpu/vga/vga_switcheroo.c
+@@ -438,12 +438,7 @@ find_active_client(struct list_head *head)
+ bool vga_switcheroo_client_probe_defer(struct pci_dev *pdev)
+ {
+ if ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
+- /*
+- * apple-gmux is needed on pre-retina MacBook Pro
+- * to probe the panel if pdev is the inactive GPU.
+- */
+- if (apple_gmux_present() && pdev != vga_default_device() &&
+- !vgasr_priv.handler_flags)
++ if (apple_gmux_present() && !vgasr_priv.handler_flags)
+ return true;
+ }
+
+diff --git a/drivers/pci/vgaarb.c b/drivers/pci/vgaarb.c
+index 5e6b1eb54c64..1f11701d37d1 100644
+--- a/drivers/pci/vgaarb.c
++++ b/drivers/pci/vgaarb.c
+@@ -143,6 +143,7 @@ void vga_set_default_device(struct pci_dev *pdev)
+ pci_dev_put(vga_default);
+ vga_default = pci_dev_get(pdev);
+ }
++EXPORT_SYMBOL_GPL(vga_set_default_device);
+
+ /**
+ * vga_remove_vgacon - deactivate VGA console
+diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
+index 1417e230edbd..e69785af8e1d 100644
+--- a/drivers/platform/x86/apple-gmux.c
++++ b/drivers/platform/x86/apple-gmux.c
+@@ -21,6 +21,7 @@
+ #include <linux/delay.h>
+ #include <linux/pci.h>
+ #include <linux/vga_switcheroo.h>
++#include <linux/vgaarb.h>
+ #include <linux/debugfs.h>
+ #include <acpi/video.h>
+ #include <asm/io.h>
+@@ -107,6 +108,10 @@ struct apple_gmux_config {
+
+ # define MMIO_GMUX_MAX_BRIGHTNESS 0xffff
+
++static bool force_igd;
++module_param(force_igd, bool, 0);
++MODULE_PARM_DESC(force_idg, "Switch gpu to igd on module load. Make sure that you have apple-set-os set up and the iGPU is in `lspci -s 00:02.0`. (default: false) (bool)");
++
+ static u8 gmux_pio_read8(struct apple_gmux_data *gmux_data, int port)
+ {
+ return inb(gmux_data->iostart + port);
+@@ -945,6 +950,19 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
+ gmux_enable_interrupts(gmux_data);
+ gmux_read_switch_state(gmux_data);
+
++ if (force_igd) {
++ struct pci_dev *pdev;
++
++ pdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(2, 0));
++ if (pdev) {
++ pr_info("Switching to IGD");
++ gmux_switchto(VGA_SWITCHEROO_IGD);
++ vga_set_default_device(pdev);
++ } else {
++ pr_err("force_idg is true, but couldn't find iGPU at 00:02.0! Is apple-set-os working?");
++ }
++ }
++
+ /*
+ * Retina MacBook Pros cannot switch the panel's AUX separately
+ * and need eDP pre-calibration. They are distinguishable from
+--
+2.43.0
+
+From 6adb501c697cd0e3246e75237ee8e43eb5a92cc3 Mon Sep 17 00:00:00 2001
+From: Kerem Karabay <kekrby@gmail.com>
+Date: Thu, 23 Nov 2023 18:58:51 +0530
+Subject: [PATCH] efi: libstub: add support for the apple_set_os protocol
+
+On dual GPU EFI Macs, the EFI stub needs to report that it is booting
+macOS in order to prevent the firmware from disabling the iGPU.
+
+See also this patch for GRUB by Andreas Heider <andreas@heider.io>:
+https://lists.gnu.org/archive/html/grub-devel/2013-12/msg00442.html
+---
+ .../admin-guide/kernel-parameters.txt | 2 ++
+ .../firmware/efi/libstub/efi-stub-helper.c | 3 +++
+ drivers/firmware/efi/libstub/efistub.h | 14 ++++++++++
+ drivers/firmware/efi/libstub/x86-stub.c | 27 +++++++++++++++++++
+ include/linux/efi.h | 1 +
+ 5 files changed, 47 insertions(+)
+
+diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
+index 41644336e..cbd4697a5 100644
+--- a/Documentation/admin-guide/kernel-parameters.txt
++++ b/Documentation/admin-guide/kernel-parameters.txt
+@@ -399,6 +399,8 @@
+ useful so that a dump capture kernel won't be
+ shot down by NMI
+
++ apple_set_os [KNL] Report that macOS is being booted to the firmware
++
+ autoconf= [IPV6]
+ See Documentation/networking/ipv6.rst.
+
+diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c
+index bfa30625f..3d99acc1a 100644
+--- a/drivers/firmware/efi/libstub/efi-stub-helper.c
++++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
+@@ -19,6 +19,7 @@
+ bool efi_nochunk;
+ bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE);
+ bool efi_novamap;
++bool efi_apple_set_os;
+
+ static bool efi_noinitrd;
+ static bool efi_nosoftreserve;
+@@ -73,6 +74,8 @@ efi_status_t efi_parse_options(char const *cmdline)
+ efi_loglevel = CONSOLE_LOGLEVEL_QUIET;
+ } else if (!strcmp(param, "noinitrd")) {
+ efi_noinitrd = true;
++ } else if (!strcmp(param, "apple_set_os")) {
++ efi_apple_set_os = true;
+ } else if (IS_ENABLED(CONFIG_X86_64) && !strcmp(param, "no5lvl")) {
+ efi_no5lvl = true;
+ } else if (!strcmp(param, "efi") && val) {
+diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
+index 212687c30..21b414d09 100644
+--- a/drivers/firmware/efi/libstub/efistub.h
++++ b/drivers/firmware/efi/libstub/efistub.h
+@@ -38,6 +38,7 @@ extern bool efi_nochunk;
+ extern bool efi_nokaslr;
+ extern int efi_loglevel;
+ extern bool efi_novamap;
++extern bool efi_apple_set_os;
+
+ extern const efi_system_table_t *efi_system_table;
+
+@@ -825,6 +826,19 @@ union apple_properties_protocol {
+ } mixed_mode;
+ };
+
++typedef struct apple_set_os_protocol apple_set_os_protocol_t;
++
++struct apple_set_os_protocol {
++ u64 version;
++ efi_status_t (__efiapi *set_os_version) (const char *);
++ efi_status_t (__efiapi *set_os_vendor) (const char *);
++ struct {
++ u32 version;
++ u32 set_os_version;
++ u32 set_os_vendor;
++ } mixed_mode;
++};
++
+ typedef u32 efi_tcg2_event_log_format;
+
+ #define INITRD_EVENT_TAG_ID 0x8F3B22ECU
+diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c
+index 70b325a2f..2131f8543 100644
+--- a/drivers/firmware/efi/libstub/x86-stub.c
++++ b/drivers/firmware/efi/libstub/x86-stub.c
+@@ -223,6 +223,30 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
+ }
+ }
+
++static void apple_set_os(void)
++{
++ efi_guid_t guid = APPLE_SET_OS_PROTOCOL_GUID;
++ apple_set_os_protocol_t *set_os;
++ efi_status_t status;
++
++ status = efi_bs_call(locate_protocol, &guid, NULL, (void **)&set_os);
++ if (status != EFI_SUCCESS)
++ return;
++
++ if (efi_table_attr(set_os, version) >= 2) {
++ status = efi_fn_call(set_os, set_os_vendor, "Apple Inc.");
++ if (status != EFI_SUCCESS)
++ efi_err("Failed to set OS vendor via apple_set_os\n");
++ }
++
++ /* The version being set doesn't seem to matter */
++ if (efi_table_attr(set_os, version) > 0) {
++ status = efi_fn_call(set_os, set_os_version, "Mac OS X 10.9");
++ if (status != EFI_SUCCESS)
++ efi_err("Failed to set OS version via apple_set_os\n");
++ }
++}
++
+ void efi_adjust_memory_range_protection(unsigned long start,
+ unsigned long size)
+ {
+@@ -321,6 +345,9 @@ static void setup_quirks(struct boot_params *boot_params)
+ if (IS_ENABLED(CONFIG_APPLE_PROPERTIES) &&
+ !memcmp(efistub_fw_vendor(), apple, sizeof(apple)))
+ retrieve_apple_device_properties(boot_params);
++
++ if (efi_apple_set_os)
++ apple_set_os();
+ }
+
+ /*
+diff --git a/include/linux/efi.h b/include/linux/efi.h
+index 80b21d1c6..f1e58e027 100644
+--- a/include/linux/efi.h
++++ b/include/linux/efi.h
+@@ -387,6 +387,7 @@ void efi_native_runtime_setup(void);
+ #define EFI_MEMORY_ATTRIBUTES_TABLE_GUID EFI_GUID(0xdcfa911d, 0x26eb, 0x469f, 0xa2, 0x20, 0x38, 0xb7, 0xdc, 0x46, 0x12, 0x20)
+ #define EFI_CONSOLE_OUT_DEVICE_GUID EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
+ #define APPLE_PROPERTIES_PROTOCOL_GUID EFI_GUID(0x91bd12fe, 0xf6c3, 0x44fb, 0xa5, 0xb7, 0x51, 0x22, 0xab, 0x30, 0x3a, 0xe0)
++#define APPLE_SET_OS_PROTOCOL_GUID EFI_GUID(0xc5c5da95, 0x7d5c, 0x45e6, 0xb2, 0xf1, 0x3f, 0xd5, 0x2b, 0xb1, 0x00, 0x77)
+ #define EFI_TCG2_PROTOCOL_GUID EFI_GUID(0x607f766c, 0x7455, 0x42be, 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f)
+ #define EFI_LOAD_FILE_PROTOCOL_GUID EFI_GUID(0x56ec3091, 0x954c, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
+ #define EFI_LOAD_FILE2_PROTOCOL_GUID EFI_GUID(0x4006c0c1, 0xfcb3, 0x403e, 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d)
+--
+2.34.1
+
+From 09dd6c563cd73d72e917de07e8d59358c41e051d Mon Sep 17 00:00:00 2001
+From: Paul Pawlowski <paul@mrarm.io>
+Date: Sun, 17 Nov 2019 23:12:55 +0100
+Subject: [PATCH 1/6] applesmc: convert static structures to drvdata
+
+All static data structures have been moved to an applesmc_device struct,
+which is then associated with the platform device.
+This change is intended to ease the migration to an acpi_device, where
+static data would preferably be avoided.
+
+Signed-off-by: Aun-Ali Zaidi <admin@kodeit.net>
+---
+ drivers/hwmon/applesmc.c | 540 +++++++++++++++++++++++----------------
+ 1 file changed, 319 insertions(+), 221 deletions(-)
+
+diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
+index 79b498f816fe..62211b590a61 100644
+--- a/drivers/hwmon/applesmc.c
++++ b/drivers/hwmon/applesmc.c
+@@ -6,6 +6,7 @@
+ *
+ * Copyright (C) 2007 Nicolas Boichat <nicolas@boichat.ch>
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
++ * Copyright (C) 2019 Paul Pawlowski <paul@mrarm.io>
+ *
+ * Based on hdaps.c driver:
+ * Copyright (C) 2005 Robert Love <rml@novell.com>
+@@ -119,7 +120,7 @@ struct applesmc_entry {
+ };
+
+ /* Register lookup and registers common to all SMCs */
+-static struct applesmc_registers {
++struct applesmc_registers {
+ struct mutex mutex; /* register read/write mutex */
+ unsigned int key_count; /* number of SMC registers */
+ unsigned int fan_count; /* number of fans */
+@@ -133,26 +134,32 @@ static struct applesmc_registers {
+ bool init_complete; /* true when fully initialized */
+ struct applesmc_entry *cache; /* cached key entries */
+ const char **index; /* temperature key index */
+-} smcreg = {
+- .mutex = __MUTEX_INITIALIZER(smcreg.mutex),
+ };
+
+-static const int debug;
+-static struct platform_device *pdev;
+-static s16 rest_x;
+-static s16 rest_y;
+-static u8 backlight_state[2];
++struct applesmc_device {
++ struct platform_device *dev;
++ struct applesmc_registers reg;
+
+-static struct device *hwmon_dev;
+-static struct input_dev *applesmc_idev;
++ s16 rest_x;
++ s16 rest_y;
+
+-/*
+- * Last index written to key_at_index sysfs file, and value to use for all other
+- * key_at_index_* sysfs files.
+- */
+-static unsigned int key_at_index;
++ u8 backlight_state[2];
++
++ struct device *hwmon_dev;
++ struct input_dev *idev;
++
++ /*
++ * Last index written to key_at_index sysfs file, and value to use for all other
++ * key_at_index_* sysfs files.
++ */
++ unsigned int key_at_index;
++
++ struct workqueue_struct *backlight_wq;
++ struct work_struct backlight_work;
++ struct led_classdev backlight_dev;
++};
+
+-static struct workqueue_struct *applesmc_led_wq;
++static const int debug;
+
+ /*
+ * Wait for specific status bits with a mask on the SMC.
+@@ -338,36 +345,37 @@ static int read_register_count(unsigned int *count)
+ * All functions below are concurrency safe - callers should NOT hold lock.
+ */
+
+-static int applesmc_read_entry(const struct applesmc_entry *entry,
+- u8 *buf, u8 len)
++static int applesmc_read_entry(struct applesmc_device *smc,
++ const struct applesmc_entry *entry, u8 *buf, u8 len)
+ {
+ int ret;
+
+ if (entry->len != len)
+ return -EINVAL;
+- mutex_lock(&smcreg.mutex);
++ mutex_lock(&smc->reg.mutex);
+ ret = read_smc(APPLESMC_READ_CMD, entry->key, buf, len);
+- mutex_unlock(&smcreg.mutex);
++ mutex_unlock(&smc->reg.mutex);
+
+ return ret;
+ }
+
+-static int applesmc_write_entry(const struct applesmc_entry *entry,
+- const u8 *buf, u8 len)
++static int applesmc_write_entry(struct applesmc_device *smc,
++ const struct applesmc_entry *entry, const u8 *buf, u8 len)
+ {
+ int ret;
+
+ if (entry->len != len)
+ return -EINVAL;
+- mutex_lock(&smcreg.mutex);
++ mutex_lock(&smc->reg.mutex);
+ ret = write_smc(APPLESMC_WRITE_CMD, entry->key, buf, len);
+- mutex_unlock(&smcreg.mutex);
++ mutex_unlock(&smc->reg.mutex);
+ return ret;
+ }
+
+-static const struct applesmc_entry *applesmc_get_entry_by_index(int index)
++static const struct applesmc_entry *applesmc_get_entry_by_index(
++ struct applesmc_device *smc, int index)
+ {
+- struct applesmc_entry *cache = &smcreg.cache[index];
++ struct applesmc_entry *cache = &smc->reg.cache[index];
+ u8 key[4], info[6];
+ __be32 be;
+ int ret = 0;
+@@ -375,7 +383,7 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(int index)
+ if (cache->valid)
+ return cache;
+
+- mutex_lock(&smcreg.mutex);
++ mutex_lock(&smc->reg.mutex);
+
+ if (cache->valid)
+ goto out;
+@@ -394,20 +402,21 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(int index)
+ cache->valid = true;
+
+ out:
+- mutex_unlock(&smcreg.mutex);
++ mutex_unlock(&smc->reg.mutex);
+ if (ret)
+ return ERR_PTR(ret);
+ return cache;
+ }
+
+-static int applesmc_get_lower_bound(unsigned int *lo, const char *key)
++static int applesmc_get_lower_bound(struct applesmc_device *smc,
++ unsigned int *lo, const char *key)
+ {
+- int begin = 0, end = smcreg.key_count;
++ int begin = 0, end = smc->reg.key_count;
+ const struct applesmc_entry *entry;
+
+ while (begin != end) {
+ int middle = begin + (end - begin) / 2;
+- entry = applesmc_get_entry_by_index(middle);
++ entry = applesmc_get_entry_by_index(smc, middle);
+ if (IS_ERR(entry)) {
+ *lo = 0;
+ return PTR_ERR(entry);
+@@ -422,16 +431,17 @@ static int applesmc_get_lower_bound(unsigned int *lo, const char *key)
+ return 0;
+ }
+
+-static int applesmc_get_upper_bound(unsigned int *hi, const char *key)
++static int applesmc_get_upper_bound(struct applesmc_device *smc,
++ unsigned int *hi, const char *key)
+ {
+- int begin = 0, end = smcreg.key_count;
++ int begin = 0, end = smc->reg.key_count;
+ const struct applesmc_entry *entry;
+
+ while (begin != end) {
+ int middle = begin + (end - begin) / 2;
+- entry = applesmc_get_entry_by_index(middle);
++ entry = applesmc_get_entry_by_index(smc, middle);
+ if (IS_ERR(entry)) {
+- *hi = smcreg.key_count;
++ *hi = smc->reg.key_count;
+ return PTR_ERR(entry);
+ }
+ if (strcmp(key, entry->key) < 0)
+@@ -444,50 +454,54 @@ static int applesmc_get_upper_bound(unsigned int *hi, const char *key)
+ return 0;
+ }
+
+-static const struct applesmc_entry *applesmc_get_entry_by_key(const char *key)
++static const struct applesmc_entry *applesmc_get_entry_by_key(
++ struct applesmc_device *smc, const char *key)
+ {
+ int begin, end;
+ int ret;
+
+- ret = applesmc_get_lower_bound(&begin, key);
++ ret = applesmc_get_lower_bound(smc, &begin, key);
+ if (ret)
+ return ERR_PTR(ret);
+- ret = applesmc_get_upper_bound(&end, key);
++ ret = applesmc_get_upper_bound(smc, &end, key);
+ if (ret)
+ return ERR_PTR(ret);
+ if (end - begin != 1)
+ return ERR_PTR(-EINVAL);
+
+- return applesmc_get_entry_by_index(begin);
++ return applesmc_get_entry_by_index(smc, begin);
+ }
+
+-static int applesmc_read_key(const char *key, u8 *buffer, u8 len)
++static int applesmc_read_key(struct applesmc_device *smc,
++ const char *key, u8 *buffer, u8 len)
+ {
+ const struct applesmc_entry *entry;
+
+- entry = applesmc_get_entry_by_key(key);
++ entry = applesmc_get_entry_by_key(smc, key);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+- return applesmc_read_entry(entry, buffer, len);
++ return applesmc_read_entry(smc, entry, buffer, len);
+ }
+
+-static int applesmc_write_key(const char *key, const u8 *buffer, u8 len)
++static int applesmc_write_key(struct applesmc_device *smc,
++ const char *key, const u8 *buffer, u8 len)
+ {
+ const struct applesmc_entry *entry;
+
+- entry = applesmc_get_entry_by_key(key);
++ entry = applesmc_get_entry_by_key(smc, key);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+- return applesmc_write_entry(entry, buffer, len);
++ return applesmc_write_entry(smc, entry, buffer, len);
+ }
+
+-static int applesmc_has_key(const char *key, bool *value)
++static int applesmc_has_key(struct applesmc_device *smc,
++ const char *key, bool *value)
+ {
+ const struct applesmc_entry *entry;
+
+- entry = applesmc_get_entry_by_key(key);
++ entry = applesmc_get_entry_by_key(smc, key);
+ if (IS_ERR(entry) && PTR_ERR(entry) != -EINVAL)
+ return PTR_ERR(entry);
+
+@@ -498,12 +512,13 @@ static int applesmc_has_key(const char *key, bool *value)
+ /*
+ * applesmc_read_s16 - Read 16-bit signed big endian register
+ */
+-static int applesmc_read_s16(const char *key, s16 *value)
++static int applesmc_read_s16(struct applesmc_device *smc,
++ const char *key, s16 *value)
+ {
+ u8 buffer[2];
+ int ret;
+
+- ret = applesmc_read_key(key, buffer, 2);
++ ret = applesmc_read_key(smc, key, buffer, 2);
+ if (ret)
+ return ret;
+
+@@ -514,28 +529,29 @@ static int applesmc_read_s16(const char *key, s16 *value)
+ /*
+ * applesmc_device_init - initialize the accelerometer. Can sleep.
+ */
+-static void applesmc_device_init(void)
++static void applesmc_device_init(struct applesmc_device *smc)
+ {
+ int total;
+ u8 buffer[2];
+
+- if (!smcreg.has_accelerometer)
++ if (!smc->reg.has_accelerometer)
+ return;
+
+ for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
+- if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) &&
++ if (!applesmc_read_key(smc, MOTION_SENSOR_KEY, buffer, 2) &&
+ (buffer[0] != 0x00 || buffer[1] != 0x00))
+ return;
+ buffer[0] = 0xe0;
+ buffer[1] = 0x00;
+- applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2);
++ applesmc_write_key(smc, MOTION_SENSOR_KEY, buffer, 2);
+ msleep(INIT_WAIT_MSECS);
+ }
+
+ pr_warn("failed to init the device\n");
+ }
+
+-static int applesmc_init_index(struct applesmc_registers *s)
++static int applesmc_init_index(struct applesmc_device *smc,
++ struct applesmc_registers *s)
+ {
+ const struct applesmc_entry *entry;
+ unsigned int i;
+@@ -548,7 +564,7 @@ static int applesmc_init_index(struct applesmc_registers *s)
+ return -ENOMEM;
+
+ for (i = s->temp_begin; i < s->temp_end; i++) {
+- entry = applesmc_get_entry_by_index(i);
++ entry = applesmc_get_entry_by_index(smc, i);
+ if (IS_ERR(entry))
+ continue;
+ if (strcmp(entry->type, TEMP_SENSOR_TYPE))
+@@ -562,9 +578,9 @@ static int applesmc_init_index(struct applesmc_registers *s)
+ /*
+ * applesmc_init_smcreg_try - Try to initialize register cache. Idempotent.
+ */
+-static int applesmc_init_smcreg_try(void)
++static int applesmc_init_smcreg_try(struct applesmc_device *smc)
+ {
+- struct applesmc_registers *s = &smcreg;
++ struct applesmc_registers *s = &smc->reg;
+ bool left_light_sensor = false, right_light_sensor = false;
+ unsigned int count;
+ u8 tmp[1];
+@@ -590,35 +606,35 @@ static int applesmc_init_smcreg_try(void)
+ if (!s->cache)
+ return -ENOMEM;
+
+- ret = applesmc_read_key(FANS_COUNT, tmp, 1);
++ ret = applesmc_read_key(smc, FANS_COUNT, tmp, 1);
+ if (ret)
+ return ret;
+ s->fan_count = tmp[0];
+ if (s->fan_count > 10)
+ s->fan_count = 10;
+
+- ret = applesmc_get_lower_bound(&s->temp_begin, "T");
++ ret = applesmc_get_lower_bound(smc, &s->temp_begin, "T");
+ if (ret)
+ return ret;
+- ret = applesmc_get_lower_bound(&s->temp_end, "U");
++ ret = applesmc_get_lower_bound(smc, &s->temp_end, "U");
+ if (ret)
+ return ret;
+ s->temp_count = s->temp_end - s->temp_begin;
+
+- ret = applesmc_init_index(s);
++ ret = applesmc_init_index(smc, s);
+ if (ret)
+ return ret;
+
+- ret = applesmc_has_key(LIGHT_SENSOR_LEFT_KEY, &left_light_sensor);
++ ret = applesmc_has_key(smc, LIGHT_SENSOR_LEFT_KEY, &left_light_sensor);
+ if (ret)
+ return ret;
+- ret = applesmc_has_key(LIGHT_SENSOR_RIGHT_KEY, &right_light_sensor);
++ ret = applesmc_has_key(smc, LIGHT_SENSOR_RIGHT_KEY, &right_light_sensor);
+ if (ret)
+ return ret;
+- ret = applesmc_has_key(MOTION_SENSOR_KEY, &s->has_accelerometer);
++ ret = applesmc_has_key(smc, MOTION_SENSOR_KEY, &s->has_accelerometer);
+ if (ret)
+ return ret;
+- ret = applesmc_has_key(BACKLIGHT_KEY, &s->has_key_backlight);
++ ret = applesmc_has_key(smc, BACKLIGHT_KEY, &s->has_key_backlight);
+ if (ret)
+ return ret;
+
+@@ -634,13 +650,13 @@ static int applesmc_init_smcreg_try(void)
+ return 0;
+ }
+
+-static void applesmc_destroy_smcreg(void)
++static void applesmc_destroy_smcreg(struct applesmc_device *smc)
+ {
+- kfree(smcreg.index);
+- smcreg.index = NULL;
+- kfree(smcreg.cache);
+- smcreg.cache = NULL;
+- smcreg.init_complete = false;
++ kfree(smc->reg.index);
++ smc->reg.index = NULL;
++ kfree(smc->reg.cache);
++ smc->reg.cache = NULL;
++ smc->reg.init_complete = false;
+ }
+
+ /*
+@@ -649,12 +665,12 @@ static void applesmc_destroy_smcreg(void)
+ * Retries until initialization is successful, or the operation times out.
+ *
+ */
+-static int applesmc_init_smcreg(void)
++static int applesmc_init_smcreg(struct applesmc_device *smc)
+ {
+ int ms, ret;
+
+ for (ms = 0; ms < INIT_TIMEOUT_MSECS; ms += INIT_WAIT_MSECS) {
+- ret = applesmc_init_smcreg_try();
++ ret = applesmc_init_smcreg_try(smc);
+ if (!ret) {
+ if (ms)
+ pr_info("init_smcreg() took %d ms\n", ms);
+@@ -663,21 +679,58 @@ static int applesmc_init_smcreg(void)
+ msleep(INIT_WAIT_MSECS);
+ }
+
+- applesmc_destroy_smcreg();
++ applesmc_destroy_smcreg(smc);
+
+ return ret;
+ }
+
+ /* Device model stuff */
++static int applesmc_create_modules(struct applesmc_device *smc);
++static void applesmc_destroy_modules(struct applesmc_device *smc);
+ static int applesmc_probe(struct platform_device *dev)
+ {
++ struct applesmc_device *smc;
+ int ret;
+
+- ret = applesmc_init_smcreg();
++ smc = kzalloc(sizeof(struct applesmc_device), GFP_KERNEL);
++ if (!smc)
++ return -ENOMEM;
++ smc->dev = dev;
++ mutex_init(&smc->reg.mutex);
++
++ platform_set_drvdata(dev, smc);
++
++ ret = applesmc_init_smcreg(smc);
+ if (ret)
+- return ret;
++ goto out_mem;
++
++ applesmc_device_init(smc);
++
++ ret = applesmc_create_modules(smc);
++ if (ret)
++ goto out_reg;
++
++ return 0;
++
++out_reg:
++ applesmc_destroy_smcreg(smc);
++out_mem:
++ platform_set_drvdata(dev, NULL);
++ mutex_destroy(&smc->reg.mutex);
++ kfree(smc);
+
+- applesmc_device_init();
++ return ret;
++}
++
++static int applesmc_remove(struct platform_device *dev)
++{
++ struct applesmc_device *smc = platform_get_drvdata(dev);
++
++ applesmc_destroy_modules(smc);
++ applesmc_destroy_smcreg(smc);
++
++ mutex_destroy(&smc->reg.mutex);
++ kfree(smc);
+
+ return 0;
+ }
+@@ -685,15 +738,21 @@ static int applesmc_probe(struct platform_device *dev)
+ /* Synchronize device with memorized backlight state */
+ static int applesmc_pm_resume(struct device *dev)
+ {
+- if (smcreg.has_key_backlight)
+- applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
++ struct applesmc_device *smc = dev_get_drvdata(dev);
++
++ if (smc->reg.has_key_backlight)
++ applesmc_write_key(smc, BACKLIGHT_KEY, smc->backlight_state, 2);
++
+ return 0;
+ }
+
+ /* Reinitialize device on resume from hibernation */
+ static int applesmc_pm_restore(struct device *dev)
+ {
+- applesmc_device_init();
++ struct applesmc_device *smc = dev_get_drvdata(dev);
++
++ applesmc_device_init(smc);
++
+ return applesmc_pm_resume(dev);
+ }
+
+@@ -704,6 +763,7 @@ static const struct dev_pm_ops applesmc_pm_ops = {
+
+ static struct platform_driver applesmc_driver = {
+ .probe = applesmc_probe,
++ .remove = applesmc_remove,
+ .driver = {
+ .name = "applesmc",
+ .pm = &applesmc_pm_ops,
+@@ -714,25 +774,26 @@ static struct platform_driver applesmc_driver = {
+ * applesmc_calibrate - Set our "resting" values. Callers must
+ * hold applesmc_lock.
+ */
+-static void applesmc_calibrate(void)
++static void applesmc_calibrate(struct applesmc_device *smc)
+ {
+- applesmc_read_s16(MOTION_SENSOR_X_KEY, &rest_x);
+- applesmc_read_s16(MOTION_SENSOR_Y_KEY, &rest_y);
+- rest_x = -rest_x;
++ applesmc_read_s16(smc, MOTION_SENSOR_X_KEY, &smc->rest_x);
++ applesmc_read_s16(smc, MOTION_SENSOR_Y_KEY, &smc->rest_y);
++ smc->rest_x = -smc->rest_x;
+ }
+
+ static void applesmc_idev_poll(struct input_dev *idev)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(&idev->dev);
+ s16 x, y;
+
+- if (applesmc_read_s16(MOTION_SENSOR_X_KEY, &x))
++ if (applesmc_read_s16(smc, MOTION_SENSOR_X_KEY, &x))
+ return;
+- if (applesmc_read_s16(MOTION_SENSOR_Y_KEY, &y))
++ if (applesmc_read_s16(smc, MOTION_SENSOR_Y_KEY, &y))
+ return;
+
+ x = -x;
+- input_report_abs(idev, ABS_X, x - rest_x);
+- input_report_abs(idev, ABS_Y, y - rest_y);
++ input_report_abs(idev, ABS_X, x - smc->rest_x);
++ input_report_abs(idev, ABS_Y, y - smc->rest_y);
+ input_sync(idev);
+ }
+
+@@ -747,16 +808,17 @@ static ssize_t applesmc_name_show(struct device *dev,
+ static ssize_t applesmc_position_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(dev);
+ int ret;
+ s16 x, y, z;
+
+- ret = applesmc_read_s16(MOTION_SENSOR_X_KEY, &x);
++ ret = applesmc_read_s16(smc, MOTION_SENSOR_X_KEY, &x);
+ if (ret)
+ goto out;
+- ret = applesmc_read_s16(MOTION_SENSOR_Y_KEY, &y);
++ ret = applesmc_read_s16(smc, MOTION_SENSOR_Y_KEY, &y);
+ if (ret)
+ goto out;
+- ret = applesmc_read_s16(MOTION_SENSOR_Z_KEY, &z);
++ ret = applesmc_read_s16(smc, MOTION_SENSOR_Z_KEY, &z);
+ if (ret)
+ goto out;
+
+@@ -770,6 +832,7 @@ static ssize_t applesmc_position_show(struct device *dev,
+ static ssize_t applesmc_light_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(dev);
+ const struct applesmc_entry *entry;
+ static int data_length;
+ int ret;
+@@ -777,7 +840,7 @@ static ssize_t applesmc_light_show(struct device *dev,
+ u8 buffer[10];
+
+ if (!data_length) {
+- entry = applesmc_get_entry_by_key(LIGHT_SENSOR_LEFT_KEY);
++ entry = applesmc_get_entry_by_key(smc, LIGHT_SENSOR_LEFT_KEY);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+ if (entry->len > 10)
+@@ -786,7 +849,7 @@ static ssize_t applesmc_light_show(struct device *dev,
+ pr_info("light sensor data length set to %d\n", data_length);
+ }
+
+- ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length);
++ ret = applesmc_read_key(smc, LIGHT_SENSOR_LEFT_KEY, buffer, data_length);
+ if (ret)
+ goto out;
+ /* newer macbooks report a single 10-bit bigendian value */
+@@ -796,7 +859,7 @@ static ssize_t applesmc_light_show(struct device *dev,
+ }
+ left = buffer[2];
+
+- ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length);
++ ret = applesmc_read_key(smc, LIGHT_SENSOR_RIGHT_KEY, buffer, data_length);
+ if (ret)
+ goto out;
+ right = buffer[2];
+@@ -812,7 +875,8 @@ static ssize_t applesmc_light_show(struct device *dev,
+ static ssize_t applesmc_show_sensor_label(struct device *dev,
+ struct device_attribute *devattr, char *sysfsbuf)
+ {
+- const char *key = smcreg.index[to_index(devattr)];
++ struct applesmc_device *smc = dev_get_drvdata(dev);
++ const char *key = smc->reg.index[to_index(devattr)];
+
+ return sysfs_emit(sysfsbuf, "%s\n", key);
+ }
+@@ -821,12 +885,13 @@ static ssize_t applesmc_show_sensor_label(struct device *dev,
+ static ssize_t applesmc_show_temperature(struct device *dev,
+ struct device_attribute *devattr, char *sysfsbuf)
+ {
+- const char *key = smcreg.index[to_index(devattr)];
++ struct applesmc_device *smc = dev_get_drvdata(dev);
++ const char *key = smc->reg.index[to_index(devattr)];
+ int ret;
+ s16 value;
+ int temp;
+
+- ret = applesmc_read_s16(key, &value);
++ ret = applesmc_read_s16(smc, key, &value);
+ if (ret)
+ return ret;
+
+@@ -838,6 +903,7 @@ static ssize_t applesmc_show_temperature(struct device *dev,
+ static ssize_t applesmc_show_fan_speed(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(dev);
+ int ret;
+ unsigned int speed = 0;
+ char newkey[5];
+@@ -846,7 +912,7 @@ static ssize_t applesmc_show_fan_speed(struct device *dev,
+ scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)],
+ to_index(attr));
+
+- ret = applesmc_read_key(newkey, buffer, 2);
++ ret = applesmc_read_key(smc, newkey, buffer, 2);
+ if (ret)
+ return ret;
+
+@@ -858,6 +924,7 @@ static ssize_t applesmc_store_fan_speed(struct device *dev,
+ struct device_attribute *attr,
+ const char *sysfsbuf, size_t count)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(dev);
+ int ret;
+ unsigned long speed;
+ char newkey[5];
+@@ -871,7 +938,7 @@ static ssize_t applesmc_store_fan_speed(struct device *dev,
+
+ buffer[0] = (speed >> 6) & 0xff;
+ buffer[1] = (speed << 2) & 0xff;
+- ret = applesmc_write_key(newkey, buffer, 2);
++ ret = applesmc_write_key(smc, newkey, buffer, 2);
+
+ if (ret)
+ return ret;
+@@ -882,11 +949,12 @@ static ssize_t applesmc_store_fan_speed(struct device *dev,
+ static ssize_t applesmc_show_fan_manual(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(dev);
+ int ret;
+ u16 manual = 0;
+ u8 buffer[2];
+
+- ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
++ ret = applesmc_read_key(smc, FANS_MANUAL, buffer, 2);
+ if (ret)
+ return ret;
+
+@@ -898,6 +966,7 @@ static ssize_t applesmc_store_fan_manual(struct device *dev,
+ struct device_attribute *attr,
+ const char *sysfsbuf, size_t count)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(dev);
+ int ret;
+ u8 buffer[2];
+ unsigned long input;
+@@ -906,7 +975,7 @@ static ssize_t applesmc_store_fan_manual(struct device *dev,
+ if (kstrtoul(sysfsbuf, 10, &input) < 0)
+ return -EINVAL;
+
+- ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
++ ret = applesmc_read_key(smc, FANS_MANUAL, buffer, 2);
+ if (ret)
+ goto out;
+
+@@ -920,7 +989,7 @@ static ssize_t applesmc_store_fan_manual(struct device *dev,
+ buffer[0] = (val >> 8) & 0xFF;
+ buffer[1] = val & 0xFF;
+
+- ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
++ ret = applesmc_write_key(smc, FANS_MANUAL, buffer, 2);
+
+ out:
+ if (ret)
+@@ -932,13 +1001,14 @@ static ssize_t applesmc_store_fan_manual(struct device *dev,
+ static ssize_t applesmc_show_fan_position(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(dev);
+ int ret;
+ char newkey[5];
+ u8 buffer[17];
+
+ scnprintf(newkey, sizeof(newkey), FAN_ID_FMT, to_index(attr));
+
+- ret = applesmc_read_key(newkey, buffer, 16);
++ ret = applesmc_read_key(smc, newkey, buffer, 16);
+ buffer[16] = 0;
+
+ if (ret)
+@@ -950,30 +1020,36 @@ static ssize_t applesmc_show_fan_position(struct device *dev,
+ static ssize_t applesmc_calibrate_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+ {
+- return sysfs_emit(sysfsbuf, "(%d,%d)\n", rest_x, rest_y);
++ struct applesmc_device *smc = dev_get_drvdata(dev);
++
++ return sysfs_emit(sysfsbuf, "(%d,%d)\n", smc->rest_x, smc->rest_y);
+ }
+
+ static ssize_t applesmc_calibrate_store(struct device *dev,
+ struct device_attribute *attr, const char *sysfsbuf, size_t count)
+ {
+- applesmc_calibrate();
++ struct applesmc_device *smc = dev_get_drvdata(dev);
++
++ applesmc_calibrate(smc);
+
+ return count;
+ }
+
+ static void applesmc_backlight_set(struct work_struct *work)
+ {
+- applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
++ struct applesmc_device *smc = container_of(work, struct applesmc_device, backlight_work);
++
++ applesmc_write_key(smc, BACKLIGHT_KEY, smc->backlight_state, 2);
+ }
+-static DECLARE_WORK(backlight_work, &applesmc_backlight_set);
+
+ static void applesmc_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(led_cdev->dev);
+ int ret;
+
+- backlight_state[0] = value;
+- ret = queue_work(applesmc_led_wq, &backlight_work);
++ smc->backlight_state[0] = value;
++ ret = queue_work(smc->backlight_wq, &smc->backlight_work);
+
+ if (debug && (!ret))
+ dev_dbg(led_cdev->dev, "work was already on the queue.\n");
+@@ -982,11 +1058,12 @@ static void applesmc_brightness_set(struct led_classdev *led_cdev,
+ static ssize_t applesmc_key_count_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(dev);
+ int ret;
+ u8 buffer[4];
+ u32 count;
+
+- ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4);
++ ret = applesmc_read_key(smc, KEY_COUNT_KEY, buffer, 4);
+ if (ret)
+ return ret;
+
+@@ -998,13 +1075,14 @@ static ssize_t applesmc_key_count_show(struct device *dev,
+ static ssize_t applesmc_key_at_index_read_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(dev);
+ const struct applesmc_entry *entry;
+ int ret;
+
+- entry = applesmc_get_entry_by_index(key_at_index);
++ entry = applesmc_get_entry_by_index(smc, smc->key_at_index);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+- ret = applesmc_read_entry(entry, sysfsbuf, entry->len);
++ ret = applesmc_read_entry(smc, entry, sysfsbuf, entry->len);
+ if (ret)
+ return ret;
+
+@@ -1014,9 +1092,10 @@ static ssize_t applesmc_key_at_index_read_show(struct device *dev,
+ static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(dev);
+ const struct applesmc_entry *entry;
+
+- entry = applesmc_get_entry_by_index(key_at_index);
++ entry = applesmc_get_entry_by_index(smc, smc->key_at_index);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+@@ -1026,9 +1105,10 @@ static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
+ static ssize_t applesmc_key_at_index_type_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(dev);
+ const struct applesmc_entry *entry;
+
+- entry = applesmc_get_entry_by_index(key_at_index);
++ entry = applesmc_get_entry_by_index(smc, smc->key_at_index);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+@@ -1038,9 +1118,10 @@ static ssize_t applesmc_key_at_index_type_show(struct device *dev,
+ static ssize_t applesmc_key_at_index_name_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(dev);
+ const struct applesmc_entry *entry;
+
+- entry = applesmc_get_entry_by_index(key_at_index);
++ entry = applesmc_get_entry_by_index(smc, smc->key_at_index);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+@@ -1050,28 +1131,25 @@ static ssize_t applesmc_key_at_index_name_show(struct device *dev,
+ static ssize_t applesmc_key_at_index_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+ {
+- return sysfs_emit(sysfsbuf, "%d\n", key_at_index);
++ struct applesmc_device *smc = dev_get_drvdata(dev);
++
++ return sysfs_emit(sysfsbuf, "%d\n", smc->key_at_index);
+ }
+
+ static ssize_t applesmc_key_at_index_store(struct device *dev,
+ struct device_attribute *attr, const char *sysfsbuf, size_t count)
+ {
++ struct applesmc_device *smc = dev_get_drvdata(dev);
+ unsigned long newkey;
+
+ if (kstrtoul(sysfsbuf, 10, &newkey) < 0
+- || newkey >= smcreg.key_count)
++ || newkey >= smc->reg.key_count)
+ return -EINVAL;
+
+- key_at_index = newkey;
++ smc->key_at_index = newkey;
+ return count;
+ }
+
+-static struct led_classdev applesmc_backlight = {
+- .name = "smc::kbd_backlight",
+- .default_trigger = "nand-disk",
+- .brightness_set = applesmc_brightness_set,
+-};
+-
+ static struct applesmc_node_group info_group[] = {
+ { "name", applesmc_name_show },
+ { "key_count", applesmc_key_count_show },
+@@ -1116,14 +1194,15 @@ static struct applesmc_node_group temp_group[] = {
+ /*
+ * applesmc_destroy_nodes - remove files and free associated memory
+ */
+-static void applesmc_destroy_nodes(struct applesmc_node_group *groups)
++static void applesmc_destroy_nodes(struct applesmc_device *smc,
++ struct applesmc_node_group *groups)
+ {
+ struct applesmc_node_group *grp;
+ struct applesmc_dev_attr *node;
+
+ for (grp = groups; grp->nodes; grp++) {
+ for (node = grp->nodes; node->sda.dev_attr.attr.name; node++)
+- sysfs_remove_file(&pdev->dev.kobj,
++ sysfs_remove_file(&smc->dev->dev.kobj,
+ &node->sda.dev_attr.attr);
+ kfree(grp->nodes);
+ grp->nodes = NULL;
+@@ -1133,7 +1212,8 @@ static void applesmc_destroy_nodes(struct applesmc_node_group *groups)
+ /*
+ * applesmc_create_nodes - create a two-dimensional group of sysfs files
+ */
+-static int applesmc_create_nodes(struct applesmc_node_group *groups, int num)
++static int applesmc_create_nodes(struct applesmc_device *smc,
++ struct applesmc_node_group *groups, int num)
+ {
+ struct applesmc_node_group *grp;
+ struct applesmc_dev_attr *node;
+@@ -1157,7 +1237,7 @@ static int applesmc_create_nodes(struct applesmc_node_group *groups, int num)
+ sysfs_attr_init(attr);
+ attr->name = node->name;
+ attr->mode = 0444 | (grp->store ? 0200 : 0);
+- ret = sysfs_create_file(&pdev->dev.kobj, attr);
++ ret = sysfs_create_file(&smc->dev->dev.kobj, attr);
+ if (ret) {
+ attr->name = NULL;
+ goto out;
+@@ -1167,57 +1247,57 @@ static int applesmc_create_nodes(struct applesmc_node_group *groups, int num)
+
+ return 0;
+ out:
+- applesmc_destroy_nodes(groups);
++ applesmc_destroy_nodes(smc, groups);
+ return ret;
+ }
+
+ /* Create accelerometer resources */
+-static int applesmc_create_accelerometer(void)
++static int applesmc_create_accelerometer(struct applesmc_device *smc)
+ {
+ int ret;
+
+- if (!smcreg.has_accelerometer)
++ if (!smc->reg.has_accelerometer)
+ return 0;
+
+- ret = applesmc_create_nodes(accelerometer_group, 1);
++ ret = applesmc_create_nodes(smc, accelerometer_group, 1);
+ if (ret)
+ goto out;
+
+- applesmc_idev = input_allocate_device();
+- if (!applesmc_idev) {
++ smc->idev = input_allocate_device();
++ if (!smc->idev) {
+ ret = -ENOMEM;
+ goto out_sysfs;
+ }
+
+ /* initial calibrate for the input device */
+- applesmc_calibrate();
++ applesmc_calibrate(smc);
+
+ /* initialize the input device */
+- applesmc_idev->name = "applesmc";
+- applesmc_idev->id.bustype = BUS_HOST;
+- applesmc_idev->dev.parent = &pdev->dev;
+- input_set_abs_params(applesmc_idev, ABS_X,
++ smc->idev->name = "applesmc";
++ smc->idev->id.bustype = BUS_HOST;
++ smc->idev->dev.parent = &smc->dev->dev;
++ input_set_abs_params(smc->idev, ABS_X,
+ -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+- input_set_abs_params(applesmc_idev, ABS_Y,
++ input_set_abs_params(smc->idev, ABS_Y,
+ -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+
+- ret = input_setup_polling(applesmc_idev, applesmc_idev_poll);
++ ret = input_setup_polling(smc->idev, applesmc_idev_poll);
+ if (ret)
+ goto out_idev;
+
+- input_set_poll_interval(applesmc_idev, APPLESMC_POLL_INTERVAL);
++ input_set_poll_interval(smc->idev, APPLESMC_POLL_INTERVAL);
+
+- ret = input_register_device(applesmc_idev);
++ ret = input_register_device(smc->idev);
+ if (ret)
+ goto out_idev;
+
+ return 0;
+
+ out_idev:
+- input_free_device(applesmc_idev);
++ input_free_device(smc->idev);
+
+ out_sysfs:
+- applesmc_destroy_nodes(accelerometer_group);
++ applesmc_destroy_nodes(smc, accelerometer_group);
+
+ out:
+ pr_warn("driver init failed (ret=%d)!\n", ret);
+@@ -1225,44 +1305,55 @@ static int applesmc_create_accelerometer(void)
+ }
+
+ /* Release all resources used by the accelerometer */
+-static void applesmc_release_accelerometer(void)
++static void applesmc_release_accelerometer(struct applesmc_device *smc)
+ {
+- if (!smcreg.has_accelerometer)
++ if (!smc->reg.has_accelerometer)
+ return;
+- input_unregister_device(applesmc_idev);
+- applesmc_destroy_nodes(accelerometer_group);
++ input_unregister_device(smc->idev);
++ applesmc_destroy_nodes(smc, accelerometer_group);
+ }
+
+-static int applesmc_create_light_sensor(void)
++static int applesmc_create_light_sensor(struct applesmc_device *smc)
+ {
+- if (!smcreg.num_light_sensors)
++ if (!smc->reg.num_light_sensors)
+ return 0;
+- return applesmc_create_nodes(light_sensor_group, 1);
++ return applesmc_create_nodes(smc, light_sensor_group, 1);
+ }
+
+-static void applesmc_release_light_sensor(void)
++static void applesmc_release_light_sensor(struct applesmc_device *smc)
+ {
+- if (!smcreg.num_light_sensors)
++ if (!smc->reg.num_light_sensors)
+ return;
+- applesmc_destroy_nodes(light_sensor_group);
++ applesmc_destroy_nodes(smc, light_sensor_group);
+ }
+
+-static int applesmc_create_key_backlight(void)
++static int applesmc_create_key_backlight(struct applesmc_device *smc)
+ {
+- if (!smcreg.has_key_backlight)
++ int ret;
++
++ if (!smc->reg.has_key_backlight)
+ return 0;
+- applesmc_led_wq = create_singlethread_workqueue("applesmc-led");
+- if (!applesmc_led_wq)
++ smc->backlight_wq = create_singlethread_workqueue("applesmc-led");
++ if (!smc->backlight_wq)
+ return -ENOMEM;
+- return led_classdev_register(&pdev->dev, &applesmc_backlight);
++
++ INIT_WORK(&smc->backlight_work, applesmc_backlight_set);
++ smc->backlight_dev.name = "smc::kbd_backlight";
++ smc->backlight_dev.default_trigger = "nand-disk";
++ smc->backlight_dev.brightness_set = applesmc_brightness_set;
++ ret = led_classdev_register(&smc->dev->dev, &smc->backlight_dev);
++ if (ret)
++ destroy_workqueue(smc->backlight_wq);
++
++ return ret;
+ }
+
+-static void applesmc_release_key_backlight(void)
++static void applesmc_release_key_backlight(struct applesmc_device *smc)
+ {
+- if (!smcreg.has_key_backlight)
++ if (!smc->reg.has_key_backlight)
+ return;
+- led_classdev_unregister(&applesmc_backlight);
+- destroy_workqueue(applesmc_led_wq);
++ led_classdev_unregister(&smc->backlight_dev);
++ destroy_workqueue(smc->backlight_wq);
+ }
+
+ static int applesmc_dmi_match(const struct dmi_system_id *id)
+@@ -1302,86 +1393,100 @@ static const struct dmi_system_id applesmc_whitelist[] __initconst = {
+ { .ident = NULL }
+ };
+
+-static int __init applesmc_init(void)
++static int applesmc_create_modules(struct applesmc_device *smc)
+ {
+ int ret;
+
+- if (!dmi_check_system(applesmc_whitelist)) {
+- pr_warn("supported laptop not found!\n");
+- ret = -ENODEV;
+- goto out;
+- }
+-
+- if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
+- "applesmc")) {
+- ret = -ENXIO;
+- goto out;
+- }
+-
+- ret = platform_driver_register(&applesmc_driver);
+- if (ret)
+- goto out_region;
+-
+- pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT,
+- NULL, 0);
+- if (IS_ERR(pdev)) {
+- ret = PTR_ERR(pdev);
+- goto out_driver;
+- }
+-
+- /* create register cache */
+- ret = applesmc_init_smcreg();
+- if (ret)
+- goto out_device;
+-
+- ret = applesmc_create_nodes(info_group, 1);
++ ret = applesmc_create_nodes(smc, info_group, 1);
+ if (ret)
+- goto out_smcreg;
++ goto out;
+
+- ret = applesmc_create_nodes(fan_group, smcreg.fan_count);
++ ret = applesmc_create_nodes(smc, fan_group, smc->reg.fan_count);
+ if (ret)
+ goto out_info;
+
+- ret = applesmc_create_nodes(temp_group, smcreg.index_count);
++ ret = applesmc_create_nodes(smc, temp_group, smc->reg.index_count);
+ if (ret)
+ goto out_fans;
+
+- ret = applesmc_create_accelerometer();
++ ret = applesmc_create_accelerometer(smc);
+ if (ret)
+ goto out_temperature;
+
+- ret = applesmc_create_light_sensor();
++ ret = applesmc_create_light_sensor(smc);
+ if (ret)
+ goto out_accelerometer;
+
+- ret = applesmc_create_key_backlight();
++ ret = applesmc_create_key_backlight(smc);
+ if (ret)
+ goto out_light_sysfs;
+
+- hwmon_dev = hwmon_device_register(&pdev->dev);
+- if (IS_ERR(hwmon_dev)) {
+- ret = PTR_ERR(hwmon_dev);
++ smc->hwmon_dev = hwmon_device_register(&smc->dev->dev);
++ if (IS_ERR(smc->hwmon_dev)) {
++ ret = PTR_ERR(smc->hwmon_dev);
+ goto out_light_ledclass;
+ }
+
+ return 0;
+
+ out_light_ledclass:
+- applesmc_release_key_backlight();
++ applesmc_release_key_backlight(smc);
+ out_light_sysfs:
+- applesmc_release_light_sensor();
++ applesmc_release_light_sensor(smc);
+ out_accelerometer:
+- applesmc_release_accelerometer();
++ applesmc_release_accelerometer(smc);
+ out_temperature:
+- applesmc_destroy_nodes(temp_group);
++ applesmc_destroy_nodes(smc, temp_group);
+ out_fans:
+- applesmc_destroy_nodes(fan_group);
++ applesmc_destroy_nodes(smc, fan_group);
+ out_info:
+- applesmc_destroy_nodes(info_group);
+-out_smcreg:
+- applesmc_destroy_smcreg();
+-out_device:
+- platform_device_unregister(pdev);
++ applesmc_destroy_nodes(smc, info_group);
++out:
++ return ret;
++}
++
++static void applesmc_destroy_modules(struct applesmc_device *smc)
++{
++ hwmon_device_unregister(smc->hwmon_dev);
++ applesmc_release_key_backlight(smc);
++ applesmc_release_light_sensor(smc);
++ applesmc_release_accelerometer(smc);
++ applesmc_destroy_nodes(smc, temp_group);
++ applesmc_destroy_nodes(smc, fan_group);
++ applesmc_destroy_nodes(smc, info_group);
++}
++
++static struct platform_device *pdev;
++
++static int __init applesmc_init(void)
++{
++ int ret;
++
++ if (!dmi_check_system(applesmc_whitelist)) {
++ pr_warn("supported laptop not found!\n");
++ ret = -ENODEV;
++ goto out;
++ }
++
++ if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
++ "applesmc")) {
++ ret = -ENXIO;
++ goto out;
++ }
++
++ ret = platform_driver_register(&applesmc_driver);
++ if (ret)
++ goto out_region;
++
++ pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT,
++ NULL, 0);
++ if (IS_ERR(pdev)) {
++ ret = PTR_ERR(pdev);
++ goto out_driver;
++ }
++
++ return 0;
++
+ out_driver:
+ platform_driver_unregister(&applesmc_driver);
+ out_region:
+@@ -1393,14 +1498,6 @@ static int __init applesmc_init(void)
+
+ static void __exit applesmc_exit(void)
+ {
+- hwmon_device_unregister(hwmon_dev);
+- applesmc_release_key_backlight();
+- applesmc_release_light_sensor();
+- applesmc_release_accelerometer();
+- applesmc_destroy_nodes(temp_group);
+- applesmc_destroy_nodes(fan_group);
+- applesmc_destroy_nodes(info_group);
+- applesmc_destroy_smcreg();
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&applesmc_driver);
+ release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+@@ -1410,6 +1507,7 @@ module_init(applesmc_init);
+ module_exit(applesmc_exit);
+
+ MODULE_AUTHOR("Nicolas Boichat");
++MODULE_AUTHOR("Paul Pawlowski");
+ MODULE_DESCRIPTION("Apple SMC");
+ MODULE_LICENSE("GPL v2");
+ MODULE_DEVICE_TABLE(dmi, applesmc_whitelist);
+--
+2.30.0
+
+From 713e78b8dbb8adb92d4ee09ea11e726b05577689 Mon Sep 17 00:00:00 2001
+From: Paul Pawlowski <paul@mrarm.io>
+Date: Sun, 17 Nov 2019 23:11:56 +0100
+Subject: [PATCH 2/6] applesmc: make io port base addr dynamic
+
+This change makes the port base runtime configurable.
+The reason why this change is made is so that when we switch to an
+acpi_device we can resolve the port base addr from ACPI.
+
+This change is not strictly required for T2 support - the base
+address is still 0x300 on T2 Macs.
+
+Signed-off-by: Aun-Ali Zaidi <admin@kodeit.net>
+---
+ drivers/hwmon/applesmc.c | 91 +++++++++++++++++++++-------------------
+ 1 file changed, 49 insertions(+), 42 deletions(-)
+
+diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
+index 62211b590a61..39ed0bb21365 100644
+--- a/drivers/hwmon/applesmc.c
++++ b/drivers/hwmon/applesmc.c
+@@ -35,10 +35,11 @@
+ #include <linux/err.h>
+ #include <linux/bits.h>
+
++#define APPLESMC_PORT_BASE 0x300
+ /* data port used by Apple SMC */
+-#define APPLESMC_DATA_PORT 0x300
++#define APPLESMC_DATA_PORT 0
+ /* command/status port used by Apple SMC */
+-#define APPLESMC_CMD_PORT 0x304
++#define APPLESMC_CMD_PORT 4
+
+ #define APPLESMC_NR_PORTS 32 /* 0x300-0x31f */
+
+@@ -140,6 +141,8 @@ struct applesmc_device {
+ struct platform_device *dev;
+ struct applesmc_registers reg;
+
++ u16 port_base;
++
+ s16 rest_x;
+ s16 rest_y;
+
+@@ -169,7 +172,7 @@ static const int debug;
+ * run out past 500ms.
+ */
+
+-static int wait_status(u8 val, u8 mask)
++static int wait_status(struct applesmc_device *smc, u8 val, u8 mask)
+ {
+ u8 status;
+ int us;
+@@ -177,7 +180,7 @@ static int wait_status(u8 val, u8 mask)
+
+ us = APPLESMC_MIN_WAIT;
+ for (i = 0; i < 24 ; i++) {
+- status = inb(APPLESMC_CMD_PORT);
++ status = inb(smc->port_base + APPLESMC_CMD_PORT);
+ if ((status & mask) == val)
+ return 0;
+ usleep_range(us, us * 2);
+@@ -189,11 +192,11 @@ static int wait_status(u8 val, u8 mask)
+
+ /* send_byte - Write to SMC data port. Callers must hold applesmc_lock. */
+
+-static int send_byte(u8 cmd, u16 port)
++static int send_byte(struct applesmc_device *smc, u8 cmd, u16 port)
+ {
+ int status;
+
+- status = wait_status(0, SMC_STATUS_IB_CLOSED);
++ status = wait_status(smc, 0, SMC_STATUS_IB_CLOSED);
+ if (status)
+ return status;
+ /*
+@@ -202,24 +205,24 @@ static int send_byte(u8 cmd, u16 port)
+ * this extra read may not happen if status returns both
+ * simultaneously and this would appear to be required.
+ */
+- status = wait_status(SMC_STATUS_BUSY, SMC_STATUS_BUSY);
++ status = wait_status(smc, SMC_STATUS_BUSY, SMC_STATUS_BUSY);
+ if (status)
+ return status;
+
+- outb(cmd, port);
++ outb(cmd, smc->port_base + port);
+ return 0;
+ }
+
+ /* send_command - Write a command to the SMC. Callers must hold applesmc_lock. */
+
+-static int send_command(u8 cmd)
++static int send_command(struct applesmc_device *smc, u8 cmd)
+ {
+ int ret;
+
+- ret = wait_status(0, SMC_STATUS_IB_CLOSED);
++ ret = wait_status(smc, 0, SMC_STATUS_IB_CLOSED);
+ if (ret)
+ return ret;
+- outb(cmd, APPLESMC_CMD_PORT);
++ outb(cmd, smc->port_base + APPLESMC_CMD_PORT);
+ return 0;
+ }
+
+@@ -229,108 +232,112 @@ static int send_command(u8 cmd)
+ * If busy is stuck high after the command then the SMC is jammed.
+ */
+
+-static int smc_sane(void)
++static int smc_sane(struct applesmc_device *smc)
+ {
+ int ret;
+
+- ret = wait_status(0, SMC_STATUS_BUSY);
++ ret = wait_status(smc, 0, SMC_STATUS_BUSY);
+ if (!ret)
+ return ret;
+- ret = send_command(APPLESMC_READ_CMD);
++ ret = send_command(smc, APPLESMC_READ_CMD);
+ if (ret)
+ return ret;
+- return wait_status(0, SMC_STATUS_BUSY);
++ return wait_status(smc, 0, SMC_STATUS_BUSY);
+ }
+
+-static int send_argument(const char *key)
++static int send_argument(struct applesmc_device *smc, const char *key)
+ {
+ int i;
+
+ for (i = 0; i < 4; i++)
+- if (send_byte(key[i], APPLESMC_DATA_PORT))
++ if (send_byte(smc, key[i], APPLESMC_DATA_PORT))
+ return -EIO;
+ return 0;
+ }
+
+-static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
++static int read_smc(struct applesmc_device *smc, u8 cmd, const char *key,
++ u8 *buffer, u8 len)
+ {
+ u8 status, data = 0;
+ int i;
+ int ret;
+
+- ret = smc_sane();
++ ret = smc_sane(smc);
+ if (ret)
+ return ret;
+
+- if (send_command(cmd) || send_argument(key)) {
++ if (send_command(smc, cmd) || send_argument(smc, key)) {
+ pr_warn("%.4s: read arg fail\n", key);
+ return -EIO;
+ }
+
+ /* This has no effect on newer (2012) SMCs */
+- if (send_byte(len, APPLESMC_DATA_PORT)) {
++ if (send_byte(smc, len, APPLESMC_DATA_PORT)) {
+ pr_warn("%.4s: read len fail\n", key);
+ return -EIO;
+ }
+
+ for (i = 0; i < len; i++) {
+- if (wait_status(SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY,
++ if (wait_status(smc,
++ SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY,
+ SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY)) {
+ pr_warn("%.4s: read data[%d] fail\n", key, i);
+ return -EIO;
+ }
+- buffer[i] = inb(APPLESMC_DATA_PORT);
++ buffer[i] = inb(smc->port_base + APPLESMC_DATA_PORT);
+ }
+
+ /* Read the data port until bit0 is cleared */
+ for (i = 0; i < 16; i++) {
+ udelay(APPLESMC_MIN_WAIT);
+- status = inb(APPLESMC_CMD_PORT);
++ status = inb(smc->port_base + APPLESMC_CMD_PORT);
+ if (!(status & SMC_STATUS_AWAITING_DATA))
+ break;
+- data = inb(APPLESMC_DATA_PORT);
++ data = inb(smc->port_base + APPLESMC_DATA_PORT);
+ }
+ if (i)
+ pr_warn("flushed %d bytes, last value is: %d\n", i, data);
+
+- return wait_status(0, SMC_STATUS_BUSY);
++ return wait_status(smc, 0, SMC_STATUS_BUSY);
+ }
+
+-static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len)
++static int write_smc(struct applesmc_device *smc, u8 cmd, const char *key,
++ const u8 *buffer, u8 len)
+ {
+ int i;
+ int ret;
+
+- ret = smc_sane();
++ ret = smc_sane(smc);
+ if (ret)
+ return ret;
+
+- if (send_command(cmd) || send_argument(key)) {
++ if (send_command(smc, cmd) || send_argument(smc, key)) {
+ pr_warn("%s: write arg fail\n", key);
+ return -EIO;
+ }
+
+- if (send_byte(len, APPLESMC_DATA_PORT)) {
++ if (send_byte(smc, len, APPLESMC_DATA_PORT)) {
+ pr_warn("%.4s: write len fail\n", key);
+ return -EIO;
+ }
+
+ for (i = 0; i < len; i++) {
+- if (send_byte(buffer[i], APPLESMC_DATA_PORT)) {
++ if (send_byte(smc, buffer[i], APPLESMC_DATA_PORT)) {
+ pr_warn("%s: write data fail\n", key);
+ return -EIO;
+ }
+ }
+
+- return wait_status(0, SMC_STATUS_BUSY);
++ return wait_status(smc, 0, SMC_STATUS_BUSY);
+ }
+
+-static int read_register_count(unsigned int *count)
++static int read_register_count(struct applesmc_device *smc,
++ unsigned int *count)
+ {
+ __be32 be;
+ int ret;
+
+- ret = read_smc(APPLESMC_READ_CMD, KEY_COUNT_KEY, (u8 *)&be, 4);
++ ret = read_smc(smc, APPLESMC_READ_CMD, KEY_COUNT_KEY, (u8 *)&be, 4);
+ if (ret)
+ return ret;
+
+@@ -353,7 +360,7 @@ static int applesmc_read_entry(struct applesmc_device *smc,
+ if (entry->len != len)
+ return -EINVAL;
+ mutex_lock(&smc->reg.mutex);
+- ret = read_smc(APPLESMC_READ_CMD, entry->key, buf, len);
++ ret = read_smc(smc, APPLESMC_READ_CMD, entry->key, buf, len);
+ mutex_unlock(&smc->reg.mutex);
+
+ return ret;
+@@ -367,7 +374,7 @@ static int applesmc_write_entry(struct applesmc_device *smc,
+ if (entry->len != len)
+ return -EINVAL;
+ mutex_lock(&smc->reg.mutex);
+- ret = write_smc(APPLESMC_WRITE_CMD, entry->key, buf, len);
++ ret = write_smc(smc, APPLESMC_WRITE_CMD, entry->key, buf, len);
+ mutex_unlock(&smc->reg.mutex);
+ return ret;
+ }
+@@ -388,10 +395,10 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(
+ if (cache->valid)
+ goto out;
+ be = cpu_to_be32(index);
+- ret = read_smc(APPLESMC_GET_KEY_BY_INDEX_CMD, (u8 *)&be, key, 4);
++ ret = read_smc(smc, APPLESMC_GET_KEY_BY_INDEX_CMD, (u8 *)&be, key, 4);
+ if (ret)
+ goto out;
+- ret = read_smc(APPLESMC_GET_KEY_TYPE_CMD, key, info, 6);
++ ret = read_smc(smc, APPLESMC_GET_KEY_TYPE_CMD, key, info, 6);
+ if (ret)
+ goto out;
+
+@@ -589,7 +596,7 @@ static int applesmc_init_smcreg_try(struct applesmc_device *smc)
+ if (s->init_complete)
+ return 0;
+
+- ret = read_register_count(&count);
++ ret = read_register_count(smc, &count);
+ if (ret)
+ return ret;
+
+@@ -1468,7 +1475,7 @@ static int __init applesmc_init(void)
+ goto out;
+ }
+
+- if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
++ if (!request_region(APPLESMC_PORT_BASE, APPLESMC_NR_PORTS,
+ "applesmc")) {
+ ret = -ENXIO;
+ goto out;
+@@ -1490,7 +1497,7 @@ static int __init applesmc_init(void)
+ out_driver:
+ platform_driver_unregister(&applesmc_driver);
+ out_region:
+- release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
++ release_region(APPLESMC_PORT_BASE, APPLESMC_NR_PORTS);
+ out:
+ pr_warn("driver init failed (ret=%d)!\n", ret);
+ return ret;
+@@ -1500,7 +1507,7 @@ static void __exit applesmc_exit(void)
+ {
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&applesmc_driver);
+- release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
++ release_region(APPLESMC_PORT_BASE, APPLESMC_NR_PORTS);
+ }
+
+ module_init(applesmc_init);
+--
+2.30.0
+
+From ee3d4bf4a01bc94553bde2ae3e806a63a13faa12 Mon Sep 17 00:00:00 2001
+From: Paul Pawlowski <paul@mrarm.io>
+Date: Sun, 17 Nov 2019 23:12:08 +0100
+Subject: [PATCH 3/6] applesmc: switch to acpi_device (from platform)
+
+This change makes the change from platform_device
+to acpi_device. The rationale for this change is
+that on T2 Macs, an additional FixedMemory32
+region is needed for device operation, and it can
+be easily resolved via ACPI tables (this will be
+done in another commit).
+
+Additionally, on older Macs, the OS X driver also
+looks for the specified ACPI device to resolve
+its memory regions, and therefore this change
+should not result in any incompatibilities.
+
+Signed-off-by: Aun-Ali Zaidi <admin@kodeit.net>
+---
+ drivers/hwmon/applesmc.c | 125 ++++++++++++++++++++++++++-------------
+ 1 file changed, 85 insertions(+), 40 deletions(-)
+
+diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
+index 39ed0bb21365..bdaaf696f7b6 100644
+--- a/drivers/hwmon/applesmc.c
++++ b/drivers/hwmon/applesmc.c
+@@ -19,7 +19,7 @@
+ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+ #include <linux/delay.h>
+-#include <linux/platform_device.h>
++#include <linux/acpi.h>
+ #include <linux/input.h>
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+@@ -35,7 +35,6 @@
+ #include <linux/err.h>
+ #include <linux/bits.h>
+
+-#define APPLESMC_PORT_BASE 0x300
+ /* data port used by Apple SMC */
+ #define APPLESMC_DATA_PORT 0
+ /* command/status port used by Apple SMC */
+@@ -138,9 +137,10 @@ struct applesmc_registers {
+ };
+
+ struct applesmc_device {
+- struct platform_device *dev;
++ struct acpi_device *dev;
+ struct applesmc_registers reg;
+
++ bool port_base_set;
+ u16 port_base;
+
+ s16 rest_x;
+@@ -692,9 +692,13 @@ static int applesmc_init_smcreg(struct applesmc_device *smc)
+ }
+
+ /* Device model stuff */
++
++static int applesmc_init_resources(struct applesmc_device *smc);
++static void applesmc_free_resources(struct applesmc_device *smc);
+ static int applesmc_create_modules(struct applesmc_device *smc);
+ static void applesmc_destroy_modules(struct applesmc_device *smc);
+-static int applesmc_probe(struct platform_device *dev)
++
++static int applesmc_add(struct acpi_device *dev)
+ {
+ struct applesmc_device *smc;
+ int ret;
+@@ -705,12 +709,16 @@ static int applesmc_probe(struct platform_device *dev)
+ smc->dev = dev;
+ mutex_init(&smc->reg.mutex);
+
+- platform_set_drvdata(dev, smc);
++ dev_set_drvdata(&dev->dev, smc);
+
+- ret = applesmc_init_smcreg(smc);
++ ret = applesmc_init_resources(smc);
+ if (ret)
+ goto out_mem;
+
++ ret = applesmc_init_smcreg(smc);
++ if (ret)
++ goto out_res;
++
+ applesmc_device_init(smc);
+
+ ret = applesmc_create_modules(smc);
+@@ -721,20 +729,23 @@ static int applesmc_probe(struct platform_device *dev)
+
+ out_reg:
+ applesmc_destroy_smcreg(smc);
++out_res:
++ applesmc_free_resources(smc);
+ out_mem:
+- platform_set_drvdata(dev, NULL);
++ dev_set_drvdata(&dev->dev, NULL);
+ mutex_destroy(&smc->reg.mutex);
+ kfree(smc);
+
+ return ret;
+ }
+
+-static int applesmc_remove(struct platform_device *dev)
++static int applesmc_remove(struct acpi_device *dev)
+ {
+- struct applesmc_device *smc = platform_get_drvdata(dev);
++ struct applesmc_device *smc = dev_get_drvdata(&dev->dev);
+
+ applesmc_destroy_modules(smc);
+ applesmc_destroy_smcreg(smc);
++ applesmc_free_resources(smc);
+
+ mutex_destroy(&smc->reg.mutex);
+ kfree(smc);
+@@ -742,6 +753,52 @@ static int applesmc_remove(struct platform_device *dev)
+ return 0;
+ }
+
++static acpi_status applesmc_walk_resources(struct acpi_resource *res,
++ void *data)
++{
++ struct applesmc_device *smc = data;
++
++ switch (res->type) {
++ case ACPI_RESOURCE_TYPE_IO:
++ if (!smc->port_base_set) {
++ if (res->data.io.address_length < APPLESMC_NR_PORTS)
++ return AE_ERROR;
++ smc->port_base = res->data.io.minimum;
++ smc->port_base_set = true;
++ }
++ return AE_OK;
++
++ case ACPI_RESOURCE_TYPE_END_TAG:
++ if (smc->port_base_set)
++ return AE_OK;
++ else
++ return AE_NOT_FOUND;
++
++ default:
++ return AE_OK;
++ }
++}
++
++static int applesmc_init_resources(struct applesmc_device *smc)
++{
++ int ret;
++
++ ret = acpi_walk_resources(smc->dev->handle, METHOD_NAME__CRS,
++ applesmc_walk_resources, smc);
++ if (ACPI_FAILURE(ret))
++ return -ENXIO;
++
++ if (!request_region(smc->port_base, APPLESMC_NR_PORTS, "applesmc"))
++ return -ENXIO;
++
++ return 0;
++}
++
++static void applesmc_free_resources(struct applesmc_device *smc)
++{
++ release_region(smc->port_base, APPLESMC_NR_PORTS);
++}
++
+ /* Synchronize device with memorized backlight state */
+ static int applesmc_pm_resume(struct device *dev)
+ {
+@@ -763,18 +820,28 @@ static int applesmc_pm_restore(struct device *dev)
+ return applesmc_pm_resume(dev);
+ }
+
++static const struct acpi_device_id applesmc_ids[] = {
++ {"APP0001", 0},
++ {"", 0},
++};
++
+ static const struct dev_pm_ops applesmc_pm_ops = {
+ .resume = applesmc_pm_resume,
+ .restore = applesmc_pm_restore,
+ };
+
+-static struct platform_driver applesmc_driver = {
+- .probe = applesmc_probe,
+- .remove = applesmc_remove,
+- .driver = {
+- .name = "applesmc",
+- .pm = &applesmc_pm_ops,
++static struct acpi_driver applesmc_driver = {
++ .name = "applesmc",
++ .class = "applesmc",
++ .ids = applesmc_ids,
++ .ops = {
++ .add = applesmc_add,
++ .remove = applesmc_remove
+ },
++ .drv = {
++ .pm = &applesmc_pm_ops
++ },
++ .owner = THIS_MODULE
+ };
+
+ /*
+@@ -1262,7 +1329,6 @@ static int applesmc_create_nodes(struct applesmc_device *smc,
+ static int applesmc_create_accelerometer(struct applesmc_device *smc)
+ {
+ int ret;
+-
+ if (!smc->reg.has_accelerometer)
+ return 0;
+
+@@ -1463,8 +1529,6 @@ static void applesmc_destroy_modules(struct applesmc_device *smc)
+ applesmc_destroy_nodes(smc, info_group);
+ }
+
+-static struct platform_device *pdev;
+-
+ static int __init applesmc_init(void)
+ {
+ int ret;
+@@ -1475,29 +1539,12 @@ static int __init applesmc_init(void)
+ goto out;
+ }
+
+- if (!request_region(APPLESMC_PORT_BASE, APPLESMC_NR_PORTS,
+- "applesmc")) {
+- ret = -ENXIO;
+- goto out;
+- }
+-
+- ret = platform_driver_register(&applesmc_driver);
++ ret = acpi_bus_register_driver(&applesmc_driver);
+ if (ret)
+- goto out_region;
+-
+- pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT,
+- NULL, 0);
+- if (IS_ERR(pdev)) {
+- ret = PTR_ERR(pdev);
+- goto out_driver;
+- }
++ goto out;
+
+ return 0;
+
+-out_driver:
+- platform_driver_unregister(&applesmc_driver);
+-out_region:
+- release_region(APPLESMC_PORT_BASE, APPLESMC_NR_PORTS);
+ out:
+ pr_warn("driver init failed (ret=%d)!\n", ret);
+ return ret;
+@@ -1505,9 +1552,7 @@ static int __init applesmc_init(void)
+
+ static void __exit applesmc_exit(void)
+ {
+- platform_device_unregister(pdev);
+- platform_driver_unregister(&applesmc_driver);
+- release_region(APPLESMC_PORT_BASE, APPLESMC_NR_PORTS);
++ acpi_bus_unregister_driver(&applesmc_driver);
+ }
+
+ module_init(applesmc_init);
+--
+2.30.0
+
+From 43df89a1377782788760808d8ea4bcf0730effbb Mon Sep 17 00:00:00 2001
+From: Paul Pawlowski <paul@mrarm.io>
+Date: Sun, 17 Nov 2019 23:12:14 +0100
+Subject: [PATCH 4/6] applesmc: key interface wrappers
+
+This change replaces the read_smc and write_smc
+methods with wrappers, additionally removing the
+command id parameter from them (and introducing
+get_smc_key_by_index and get_smc_key_info).
+
+This is done as to allow simple implementation
+replacement on T2 Macs. The newly introduced
+methods mentioned in the previous paragraph need
+special handling on T2 and as such had to be
+separated.
+
+Signed-off-by: Aun-Ali Zaidi <admin@kodeit.net>
+---
+ drivers/hwmon/applesmc.c | 119 ++++++++++++++++++++++++++-------------
+ 1 file changed, 79 insertions(+), 40 deletions(-)
+
+diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
+index bdaaf696f7b6..3017d8ca2c79 100644
+--- a/drivers/hwmon/applesmc.c
++++ b/drivers/hwmon/applesmc.c
+@@ -172,7 +172,7 @@ static const int debug;
+ * run out past 500ms.
+ */
+
+-static int wait_status(struct applesmc_device *smc, u8 val, u8 mask)
++static int port_wait_status(struct applesmc_device *smc, u8 val, u8 mask)
+ {
+ u8 status;
+ int us;
+@@ -190,13 +190,13 @@ static int wait_status(struct applesmc_device *smc, u8 val, u8 mask)
+ return -EIO;
+ }
+
+-/* send_byte - Write to SMC data port. Callers must hold applesmc_lock. */
++/* port_send_byte - Write to SMC data port. Callers must hold applesmc_lock. */
+
+-static int send_byte(struct applesmc_device *smc, u8 cmd, u16 port)
++static int port_send_byte(struct applesmc_device *smc, u8 cmd, u16 port)
+ {
+ int status;
+
+- status = wait_status(smc, 0, SMC_STATUS_IB_CLOSED);
++ status = port_wait_status(smc, 0, SMC_STATUS_IB_CLOSED);
+ if (status)
+ return status;
+ /*
+@@ -205,7 +205,7 @@ static int send_byte(struct applesmc_device *smc, u8 cmd, u16 port)
+ * this extra read may not happen if status returns both
+ * simultaneously and this would appear to be required.
+ */
+- status = wait_status(smc, SMC_STATUS_BUSY, SMC_STATUS_BUSY);
++ status = port_wait_status(smc, SMC_STATUS_BUSY, SMC_STATUS_BUSY);
+ if (status)
+ return status;
+
+@@ -213,15 +213,16 @@ static int send_byte(struct applesmc_device *smc, u8 cmd, u16 port)
+ return 0;
+ }
+
+-/* send_command - Write a command to the SMC. Callers must hold applesmc_lock. */
++/* port_send_command - Write a command to the SMC. Callers must hold applesmc_lock. */
+
+-static int send_command(struct applesmc_device *smc, u8 cmd)
++static int port_send_command(struct applesmc_device *smc, u8 cmd)
+ {
+ int ret;
+
+- ret = wait_status(smc, 0, SMC_STATUS_IB_CLOSED);
++ ret = port_wait_status(smc, 0, SMC_STATUS_IB_CLOSED);
+ if (ret)
+ return ret;
++
+ outb(cmd, smc->port_base + APPLESMC_CMD_PORT);
+ return 0;
+ }
+@@ -232,53 +233,53 @@ static int send_command(struct applesmc_device *smc, u8 cmd)
+ * If busy is stuck high after the command then the SMC is jammed.
+ */
+
+-static int smc_sane(struct applesmc_device *smc)
++static int port_smc_sane(struct applesmc_device *smc)
+ {
+ int ret;
+
+- ret = wait_status(smc, 0, SMC_STATUS_BUSY);
++ ret = port_wait_status(smc, 0, SMC_STATUS_BUSY);
+ if (!ret)
+ return ret;
+- ret = send_command(smc, APPLESMC_READ_CMD);
++ ret = port_send_command(smc, APPLESMC_READ_CMD);
+ if (ret)
+ return ret;
+- return wait_status(smc, 0, SMC_STATUS_BUSY);
++ return port_wait_status(smc, 0, SMC_STATUS_BUSY);
+ }
+
+-static int send_argument(struct applesmc_device *smc, const char *key)
++static int port_send_argument(struct applesmc_device *smc, const char *key)
+ {
+ int i;
+
+ for (i = 0; i < 4; i++)
+- if (send_byte(smc, key[i], APPLESMC_DATA_PORT))
++ if (port_send_byte(smc, key[i], APPLESMC_DATA_PORT))
+ return -EIO;
+ return 0;
+ }
+
+-static int read_smc(struct applesmc_device *smc, u8 cmd, const char *key,
++static int port_read_smc(struct applesmc_device *smc, u8 cmd, const char *key,
+ u8 *buffer, u8 len)
+ {
+ u8 status, data = 0;
+ int i;
+ int ret;
+
+- ret = smc_sane(smc);
++ ret = port_smc_sane(smc);
+ if (ret)
+ return ret;
+
+- if (send_command(smc, cmd) || send_argument(smc, key)) {
++ if (port_send_command(smc, cmd) || port_send_argument(smc, key)) {
+ pr_warn("%.4s: read arg fail\n", key);
+ return -EIO;
+ }
+
+ /* This has no effect on newer (2012) SMCs */
+- if (send_byte(smc, len, APPLESMC_DATA_PORT)) {
++ if (port_send_byte(smc, len, APPLESMC_DATA_PORT)) {
+ pr_warn("%.4s: read len fail\n", key);
+ return -EIO;
+ }
+
+ for (i = 0; i < len; i++) {
+- if (wait_status(smc,
++ if (port_wait_status(smc,
+ SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY,
+ SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY)) {
+ pr_warn("%.4s: read data[%d] fail\n", key, i);
+@@ -298,37 +299,80 @@ static int read_smc(struct applesmc_device *smc, u8 cmd, const char *key,
+ if (i)
+ pr_warn("flushed %d bytes, last value is: %d\n", i, data);
+
+- return wait_status(smc, 0, SMC_STATUS_BUSY);
++ return port_wait_status(smc, 0, SMC_STATUS_BUSY);
+ }
+
+-static int write_smc(struct applesmc_device *smc, u8 cmd, const char *key,
++static int port_write_smc(struct applesmc_device *smc, u8 cmd, const char *key,
+ const u8 *buffer, u8 len)
+ {
+ int i;
+ int ret;
+
+- ret = smc_sane(smc);
++ ret = port_smc_sane(smc);
+ if (ret)
+ return ret;
+
+- if (send_command(smc, cmd) || send_argument(smc, key)) {
++ if (port_send_command(smc, cmd) || port_send_argument(smc, key)) {
+ pr_warn("%s: write arg fail\n", key);
+ return -EIO;
+ }
+
+- if (send_byte(smc, len, APPLESMC_DATA_PORT)) {
++ if (port_send_byte(smc, len, APPLESMC_DATA_PORT)) {
+ pr_warn("%.4s: write len fail\n", key);
+ return -EIO;
+ }
+
+ for (i = 0; i < len; i++) {
+- if (send_byte(smc, buffer[i], APPLESMC_DATA_PORT)) {
++ if (port_send_byte(smc, buffer[i], APPLESMC_DATA_PORT)) {
+ pr_warn("%s: write data fail\n", key);
+ return -EIO;
+ }
+ }
+
+- return wait_status(smc, 0, SMC_STATUS_BUSY);
++ return port_wait_status(smc, 0, SMC_STATUS_BUSY);
++}
++
++static int port_get_smc_key_info(struct applesmc_device *smc,
++ const char *key, struct applesmc_entry *info)
++{
++ int ret;
++ u8 raw[6];
++
++ ret = port_read_smc(smc, APPLESMC_GET_KEY_TYPE_CMD, key, raw, 6);
++ if (ret)
++ return ret;
++ info->len = raw[0];
++ memcpy(info->type, &raw[1], 4);
++ info->flags = raw[5];
++ return 0;
++}
++
++static int read_smc(struct applesmc_device *smc, const char *key,
++ u8 *buffer, u8 len)
++{
++ return port_read_smc(smc, APPLESMC_READ_CMD, key, buffer, len);
++}
++
++static int write_smc(struct applesmc_device *smc, const char *key,
++ const u8 *buffer, u8 len)
++{
++ return port_write_smc(smc, APPLESMC_WRITE_CMD, key, buffer, len);
++}
++
++static int get_smc_key_by_index(struct applesmc_device *smc,
++ unsigned int index, char *key)
++{
++ __be32 be;
++
++ be = cpu_to_be32(index);
++ return port_read_smc(smc, APPLESMC_GET_KEY_BY_INDEX_CMD,
++ (const char *) &be, (u8 *) key, 4);
++}
++
++static int get_smc_key_info(struct applesmc_device *smc, const char *key,
++ struct applesmc_entry *info)
++{
++ return port_get_smc_key_info(smc, key, info);
+ }
+
+ static int read_register_count(struct applesmc_device *smc,
+@@ -337,8 +381,8 @@ static int read_register_count(struct applesmc_device *smc,
+ __be32 be;
+ int ret;
+
+- ret = read_smc(smc, APPLESMC_READ_CMD, KEY_COUNT_KEY, (u8 *)&be, 4);
+- if (ret)
++ ret = read_smc(smc, KEY_COUNT_KEY, (u8 *)&be, 4);
++ if (ret < 0)
+ return ret;
+
+ *count = be32_to_cpu(be);
+@@ -360,7 +404,7 @@ static int applesmc_read_entry(struct applesmc_device *smc,
+ if (entry->len != len)
+ return -EINVAL;
+ mutex_lock(&smc->reg.mutex);
+- ret = read_smc(smc, APPLESMC_READ_CMD, entry->key, buf, len);
++ ret = read_smc(smc, entry->key, buf, len);
+ mutex_unlock(&smc->reg.mutex);
+
+ return ret;
+@@ -374,7 +418,7 @@ static int applesmc_write_entry(struct applesmc_device *smc,
+ if (entry->len != len)
+ return -EINVAL;
+ mutex_lock(&smc->reg.mutex);
+- ret = write_smc(smc, APPLESMC_WRITE_CMD, entry->key, buf, len);
++ ret = write_smc(smc, entry->key, buf, len);
+ mutex_unlock(&smc->reg.mutex);
+ return ret;
+ }
+@@ -383,8 +427,7 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(
+ struct applesmc_device *smc, int index)
+ {
+ struct applesmc_entry *cache = &smc->reg.cache[index];
+- u8 key[4], info[6];
+- __be32 be;
++ char key[4];
+ int ret = 0;
+
+ if (cache->valid)
+@@ -394,18 +437,14 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(
+
+ if (cache->valid)
+ goto out;
+- be = cpu_to_be32(index);
+- ret = read_smc(smc, APPLESMC_GET_KEY_BY_INDEX_CMD, (u8 *)&be, key, 4);
++ ret = get_smc_key_by_index(smc, index, key);
+ if (ret)
+ goto out;
+- ret = read_smc(smc, APPLESMC_GET_KEY_TYPE_CMD, key, info, 6);
++ memcpy(cache->key, key, 4);
++
++ ret = get_smc_key_info(smc, key, cache);
+ if (ret)
+ goto out;
+-
+- memcpy(cache->key, key, 4);
+- cache->len = info[0];
+- memcpy(cache->type, &info[1], 4);
+- cache->flags = info[5];
+ cache->valid = true;
+
+ out:
+--
+2.30.0
+
+From 799e7a54c62a36007f7874c58d7dac87c9651759 Mon Sep 17 00:00:00 2001
+From: Aun-Ali Zaidi <admin@kodeit.net>
+Date: Sun, 17 Nov 2019 23:12:16 +0100
+Subject: [PATCH 5/6] applesmc: basic mmio interface implementation
+
+This change introduces a basic MMIO-based
+interface implementation required to communicate
+with the SMC on T2 Macs. The MMIO interface is
+enabled only when it's supported on the running
+system.
+
+The MMIO interface replaces legacy port-based SMC
+key reads, writes and metadata requests (getting
+key by index and getting key info).
+
+(Based on patch by @mcmrarm)
+
+Signed-off-by: Aun-Ali Zaidi <admin@kodeit.net>
+---
+ drivers/hwmon/applesmc.c | 237 ++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 231 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
+index 3017d8ca2c79..2d23bb9ad9dd 100644
+--- a/drivers/hwmon/applesmc.c
++++ b/drivers/hwmon/applesmc.c
+@@ -42,6 +42,18 @@
+
+ #define APPLESMC_NR_PORTS 32 /* 0x300-0x31f */
+
++#define APPLESMC_IOMEM_KEY_DATA 0
++#define APPLESMC_IOMEM_KEY_STATUS 0x4005
++#define APPLESMC_IOMEM_KEY_NAME 0x78
++#define APPLESMC_IOMEM_KEY_DATA_LEN 0x7D
++#define APPLESMC_IOMEM_KEY_SMC_ID 0x7E
++#define APPLESMC_IOMEM_KEY_CMD 0x7F
++#define APPLESMC_IOMEM_MIN_SIZE 0x4006
++
++#define APPLESMC_IOMEM_KEY_TYPE_CODE 0
++#define APPLESMC_IOMEM_KEY_TYPE_DATA_LEN 5
++#define APPLESMC_IOMEM_KEY_TYPE_FLAGS 6
++
+ #define APPLESMC_MAX_DATA_LENGTH 32
+
+ /* Apple SMC status bits */
+@@ -138,10 +150,13 @@ struct applesmc_registers {
+
+ struct applesmc_device {
+ struct acpi_device *dev;
++ struct device *ldev;
+ struct applesmc_registers reg;
+
+- bool port_base_set;
++ bool port_base_set, iomem_base_set;
+ u16 port_base;
++ u8 *__iomem iomem_base;
++ u32 iomem_base_addr, iomem_base_size;
+
+ s16 rest_x;
+ s16 rest_y;
+@@ -347,16 +362,156 @@ static int port_get_smc_key_info(struct applesmc_device *smc,
+ return 0;
+ }
+
++
++/*
++ * MMIO based communication.
++ * TODO: Use updated mechanism for cmd timeout/retry
++ */
++
++static void iomem_clear_status(struct applesmc_device *smc)
++{
++ if (ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_STATUS))
++ iowrite8(0, smc->iomem_base + APPLESMC_IOMEM_KEY_STATUS);
++}
++
++static int iomem_wait_read(struct applesmc_device *smc)
++{
++ u8 status;
++ int us;
++ int i;
++
++ us = APPLESMC_MIN_WAIT;
++ for (i = 0; i < 24 ; i++) {
++ status = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_STATUS);
++ if (status & 0x20)
++ return 0;
++ usleep_range(us, us * 2);
++ if (i > 9)
++ us <<= 1;
++ }
++
++ dev_warn(smc->ldev, "%s... timeout\n", __func__);
++ return -EIO;
++}
++
++static int iomem_read_smc(struct applesmc_device *smc, u8 cmd, const char *key,
++ u8 *buffer, u8 len)
++{
++ u8 err, remote_len;
++ u32 key_int = *((u32 *) key);
++
++ iomem_clear_status(smc);
++ iowrite32(key_int, smc->iomem_base + APPLESMC_IOMEM_KEY_NAME);
++ iowrite32(0, smc->iomem_base + APPLESMC_IOMEM_KEY_SMC_ID);
++ iowrite32(cmd, smc->iomem_base + APPLESMC_IOMEM_KEY_CMD);
++
++ if (iomem_wait_read(smc))
++ return -EIO;
++
++ err = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_CMD);
++ if (err != 0) {
++ dev_warn(smc->ldev, "read_smc_mmio(%x %8x/%.4s) failed: %u\n",
++ cmd, key_int, key, err);
++ return -EIO;
++ }
++
++ if (cmd == APPLESMC_READ_CMD) {
++ remote_len = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_DATA_LEN);
++ if (remote_len != len) {
++ dev_warn(smc->ldev,
++ "read_smc_mmio(%x %8x/%.4s) failed: buffer length mismatch (remote = %u, requested = %u)\n",
++ cmd, key_int, key, remote_len, len);
++ return -EINVAL;
++ }
++ } else {
++ remote_len = len;
++ }
++
++ memcpy_fromio(buffer, smc->iomem_base + APPLESMC_IOMEM_KEY_DATA,
++ remote_len);
++
++ dev_dbg(smc->ldev, "read_smc_mmio(%x %8x/%.4s): buflen=%u reslen=%u\n",
++ cmd, key_int, key, len, remote_len);
++ print_hex_dump_bytes("read_smc_mmio(): ", DUMP_PREFIX_NONE, buffer, remote_len);
++ return 0;
++}
++
++static int iomem_get_smc_key_type(struct applesmc_device *smc, const char *key,
++ struct applesmc_entry *e)
++{
++ u8 err;
++ u8 cmd = APPLESMC_GET_KEY_TYPE_CMD;
++ u32 key_int = *((u32 *) key);
++
++ iomem_clear_status(smc);
++ iowrite32(key_int, smc->iomem_base + APPLESMC_IOMEM_KEY_NAME);
++ iowrite32(0, smc->iomem_base + APPLESMC_IOMEM_KEY_SMC_ID);
++ iowrite32(cmd, smc->iomem_base + APPLESMC_IOMEM_KEY_CMD);
++
++ if (iomem_wait_read(smc))
++ return -EIO;
++
++ err = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_CMD);
++ if (err != 0) {
++ dev_warn(smc->ldev, "get_smc_key_type_mmio(%.4s) failed: %u\n", key, err);
++ return -EIO;
++ }
++
++ e->len = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_TYPE_DATA_LEN);
++ *((uint32_t *) e->type) = ioread32(
++ smc->iomem_base + APPLESMC_IOMEM_KEY_TYPE_CODE);
++ e->flags = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_TYPE_FLAGS);
++
++ dev_dbg(smc->ldev, "get_smc_key_type_mmio(%.4s): len=%u type=%.4s flags=%x\n",
++ key, e->len, e->type, e->flags);
++ return 0;
++}
++
++static int iomem_write_smc(struct applesmc_device *smc, u8 cmd, const char *key,
++ const u8 *buffer, u8 len)
++{
++ u8 err;
++ u32 key_int = *((u32 *) key);
++
++ iomem_clear_status(smc);
++ iowrite32(key_int, smc->iomem_base + APPLESMC_IOMEM_KEY_NAME);
++ memcpy_toio(smc->iomem_base + APPLESMC_IOMEM_KEY_DATA, buffer, len);
++ iowrite32(len, smc->iomem_base + APPLESMC_IOMEM_KEY_DATA_LEN);
++ iowrite32(0, smc->iomem_base + APPLESMC_IOMEM_KEY_SMC_ID);
++ iowrite32(cmd, smc->iomem_base + APPLESMC_IOMEM_KEY_CMD);
++
++ if (iomem_wait_read(smc))
++ return -EIO;
++
++ err = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_CMD);
++ if (err != 0) {
++ dev_warn(smc->ldev, "write_smc_mmio(%x %.4s) failed: %u\n", cmd, key, err);
++ print_hex_dump_bytes("write_smc_mmio(): ", DUMP_PREFIX_NONE, buffer, len);
++ return -EIO;
++ }
++
++ dev_dbg(smc->ldev, "write_smc_mmio(%x %.4s): buflen=%u\n", cmd, key, len);
++ print_hex_dump_bytes("write_smc_mmio(): ", DUMP_PREFIX_NONE, buffer, len);
++ return 0;
++}
++
++
+ static int read_smc(struct applesmc_device *smc, const char *key,
+ u8 *buffer, u8 len)
+ {
+- return port_read_smc(smc, APPLESMC_READ_CMD, key, buffer, len);
++ if (smc->iomem_base_set)
++ return iomem_read_smc(smc, APPLESMC_READ_CMD, key, buffer, len);
++ else
++ return port_read_smc(smc, APPLESMC_READ_CMD, key, buffer, len);
+ }
+
+ static int write_smc(struct applesmc_device *smc, const char *key,
+ const u8 *buffer, u8 len)
+ {
+- return port_write_smc(smc, APPLESMC_WRITE_CMD, key, buffer, len);
++ if (smc->iomem_base_set)
++ return iomem_write_smc(smc, APPLESMC_WRITE_CMD, key, buffer, len);
++ else
++ return port_write_smc(smc, APPLESMC_WRITE_CMD, key, buffer, len);
+ }
+
+ static int get_smc_key_by_index(struct applesmc_device *smc,
+@@ -365,14 +520,21 @@ static int get_smc_key_by_index(struct applesmc_device *smc,
+ __be32 be;
+
+ be = cpu_to_be32(index);
+- return port_read_smc(smc, APPLESMC_GET_KEY_BY_INDEX_CMD,
+- (const char *) &be, (u8 *) key, 4);
++ if (smc->iomem_base_set)
++ return iomem_read_smc(smc, APPLESMC_GET_KEY_BY_INDEX_CMD,
++ (const char *) &be, (u8 *) key, 4);
++ else
++ return port_read_smc(smc, APPLESMC_GET_KEY_BY_INDEX_CMD,
++ (const char *) &be, (u8 *) key, 4);
+ }
+
+ static int get_smc_key_info(struct applesmc_device *smc, const char *key,
+ struct applesmc_entry *info)
+ {
+- return port_get_smc_key_info(smc, key, info);
++ if (smc->iomem_base_set)
++ return iomem_get_smc_key_type(smc, key, info);
++ else
++ return port_get_smc_key_info(smc, key, info);
+ }
+
+ static int read_register_count(struct applesmc_device *smc,
+@@ -746,6 +908,7 @@ static int applesmc_add(struct acpi_device *dev)
+ if (!smc)
+ return -ENOMEM;
+ smc->dev = dev;
++ smc->ldev = &dev->dev;
+ mutex_init(&smc->reg.mutex);
+
+ dev_set_drvdata(&dev->dev, smc);
+@@ -807,6 +970,20 @@ static acpi_status applesmc_walk_resources(struct acpi_resource *res,
+ }
+ return AE_OK;
+
++ case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
++ if (!smc->iomem_base_set) {
++ if (res->data.fixed_memory32.address_length <
++ APPLESMC_IOMEM_MIN_SIZE) {
++ dev_warn(smc->ldev, "found iomem but it's too small: %u\n",
++ res->data.fixed_memory32.address_length);
++ return AE_OK;
++ }
++ smc->iomem_base_addr = res->data.fixed_memory32.address;
++ smc->iomem_base_size = res->data.fixed_memory32.address_length;
++ smc->iomem_base_set = true;
++ }
++ return AE_OK;
++
+ case ACPI_RESOURCE_TYPE_END_TAG:
+ if (smc->port_base_set)
+ return AE_OK;
+@@ -818,6 +995,8 @@ static acpi_status applesmc_walk_resources(struct acpi_resource *res,
+ }
+ }
+
++static int applesmc_try_enable_iomem(struct applesmc_device *smc);
++
+ static int applesmc_init_resources(struct applesmc_device *smc)
+ {
+ int ret;
+@@ -830,11 +1009,57 @@ static int applesmc_init_resources(struct applesmc_device *smc)
+ if (!request_region(smc->port_base, APPLESMC_NR_PORTS, "applesmc"))
+ return -ENXIO;
+
++ if (smc->iomem_base_set) {
++ if (applesmc_try_enable_iomem(smc))
++ smc->iomem_base_set = false;
++ }
++
+ return 0;
+ }
+
++static int applesmc_try_enable_iomem(struct applesmc_device *smc)
++{
++ u8 test_val, ldkn_version;
++
++ dev_dbg(smc->ldev, "Trying to enable iomem based communication\n");
++ smc->iomem_base = ioremap(smc->iomem_base_addr, smc->iomem_base_size);
++ if (!smc->iomem_base)
++ goto out;
++
++ /* Apple's driver does this check for some reason */
++ test_val = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_STATUS);
++ if (test_val == 0xff) {
++ dev_warn(smc->ldev,
++ "iomem enable failed: initial status is 0xff (is %x)\n",
++ test_val);
++ goto out_iomem;
++ }
++
++ if (read_smc(smc, "LDKN", &ldkn_version, 1)) {
++ dev_warn(smc->ldev, "iomem enable failed: ldkn read failed\n");
++ goto out_iomem;
++ }
++
++ if (ldkn_version < 2) {
++ dev_warn(smc->ldev,
++ "iomem enable failed: ldkn version %u is less than minimum (2)\n",
++ ldkn_version);
++ goto out_iomem;
++ }
++
++ return 0;
++
++out_iomem:
++ iounmap(smc->iomem_base);
++
++out:
++ return -ENXIO;
++}
++
+ static void applesmc_free_resources(struct applesmc_device *smc)
+ {
++ if (smc->iomem_base_set)
++ iounmap(smc->iomem_base);
+ release_region(smc->port_base, APPLESMC_NR_PORTS);
+ }
+
+--
+2.30.0
+
+From 4e63e9b77422aae8e7411ddc7a8458c2585c86df Mon Sep 17 00:00:00 2001
+From: Paul Pawlowski <paul@mrarm.io>
+Date: Sun, 17 Nov 2019 23:12:18 +0100
+Subject: [PATCH 6/6] applesmc: fan support on T2 Macs
+
+T2 Macs changed the fan values from shorts to
+floats, and changed the fan manual override
+setting from a bitmask to a per-fan boolean
+named F0Md (thanks to @kleuter for mentioning
+it).
+
+A minimal soft-float implementation has been
+written for convert floats to integers (and vice
+versa).
+
+Signed-off-by: Aun-Ali Zaidi <admin@kodeit.net>
+---
+ drivers/hwmon/applesmc.c | 119 +++++++++++++++++++++++++++++++++------
+ 1 file changed, 102 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
+index 2d23bb9ad9dd..0938227be612 100644
+--- a/drivers/hwmon/applesmc.c
++++ b/drivers/hwmon/applesmc.c
+@@ -87,6 +87,7 @@
+ #define FAN_ID_FMT "F%dID" /* r-o char[16] */
+
+ #define TEMP_SENSOR_TYPE "sp78"
++#define FLOAT_TYPE "flt "
+
+ /* List of keys used to read/write fan speeds */
+ static const char *const fan_speed_fmt[] = {
+@@ -96,6 +97,7 @@ static const char *const fan_speed_fmt[] = {
+ "F%dSf", /* safe speed - not all models */
+ "F%dTg", /* target speed (manual: rw) */
+ };
++#define FAN_MANUAL_FMT "F%dMd"
+
+ #define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */
+ #define INIT_WAIT_MSECS 50 /* ... in 50ms increments */
+@@ -734,6 +736,42 @@ static int applesmc_read_s16(struct applesmc_device *smc,
+ return 0;
+ }
+
++/**
++ * applesmc_float_to_u32 - Retrieve the integral part of a float.
++ * This is needed because Apple made fans use float values in the T2.
++ * The fractional point is not significantly useful though, and the integral
++ * part can be easily extracted.
++ */
++static inline u32 applesmc_float_to_u32(u32 d)
++{
++ u8 sign = (u8) ((d >> 31) & 1);
++ s32 exp = (s32) ((d >> 23) & 0xff) - 0x7f;
++ u32 fr = d & ((1u << 23) - 1);
++
++ if (sign || exp < 0)
++ return 0;
++
++ return (u32) ((1u << exp) + (fr >> (23 - exp)));
++}
++
++/**
++ * applesmc_u32_to_float - Convert an u32 into a float.
++ * See applesmc_float_to_u32 for a rationale.
++ */
++static inline u32 applesmc_u32_to_float(u32 d)
++{
++ u32 dc = d, bc = 0, exp;
++
++ if (!d)
++ return 0;
++
++ while (dc >>= 1)
++ ++bc;
++ exp = 0x7f + bc;
++
++ return (u32) ((exp << 23) |
++ ((d << (23 - (exp - 0x7f))) & ((1u << 23) - 1)));
++}
+ /*
+ * applesmc_device_init - initialize the accelerometer. Can sleep.
+ */
+@@ -1242,6 +1280,7 @@ static ssize_t applesmc_show_fan_speed(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+ {
+ struct applesmc_device *smc = dev_get_drvdata(dev);
++ const struct applesmc_entry *entry;
+ int ret;
+ unsigned int speed = 0;
+ char newkey[5];
+@@ -1250,11 +1289,21 @@ static ssize_t applesmc_show_fan_speed(struct device *dev,
+ scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)],
+ to_index(attr));
+
+- ret = applesmc_read_key(smc, newkey, buffer, 2);
++ entry = applesmc_get_entry_by_key(smc, newkey);
++ if (IS_ERR(entry))
++ return PTR_ERR(entry);
++
++ if (!strcmp(entry->type, FLOAT_TYPE)) {
++ ret = applesmc_read_entry(smc, entry, (u8 *) &speed, 4);
++ speed = applesmc_float_to_u32(speed);
++ } else {
++ ret = applesmc_read_entry(smc, entry, buffer, 2);
++ speed = ((buffer[0] << 8 | buffer[1]) >> 2);
++ }
++
+ if (ret)
+ return ret;
+
+- speed = ((buffer[0] << 8 | buffer[1]) >> 2);
+ return sysfs_emit(sysfsbuf, "%u\n", speed);
+ }
+
+@@ -1263,6 +1312,7 @@ static ssize_t applesmc_store_fan_speed(struct device *dev,
+ const char *sysfsbuf, size_t count)
+ {
+ struct applesmc_device *smc = dev_get_drvdata(dev);
++ const struct applesmc_entry *entry;
+ int ret;
+ unsigned long speed;
+ char newkey[5];
+@@ -1274,9 +1324,18 @@ static ssize_t applesmc_store_fan_speed(struct device *dev,
+ scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)],
+ to_index(attr));
+
+- buffer[0] = (speed >> 6) & 0xff;
+- buffer[1] = (speed << 2) & 0xff;
+- ret = applesmc_write_key(smc, newkey, buffer, 2);
++ entry = applesmc_get_entry_by_key(smc, newkey);
++ if (IS_ERR(entry))
++ return PTR_ERR(entry);
++
++ if (!strcmp(entry->type, FLOAT_TYPE)) {
++ speed = applesmc_u32_to_float(speed);
++ ret = applesmc_write_entry(smc, entry, (u8 *) &speed, 4);
++ } else {
++ buffer[0] = (speed >> 6) & 0xff;
++ buffer[1] = (speed << 2) & 0xff;
++ ret = applesmc_write_key(smc, newkey, buffer, 2);
++ }
+
+ if (ret)
+ return ret;
+@@ -1291,12 +1350,26 @@ static ssize_t applesmc_show_fan_manual(struct device *dev,
+ int ret;
+ u16 manual = 0;
+ u8 buffer[2];
++ char newkey[5];
++ bool has_newkey = false;
++
++ scnprintf(newkey, sizeof(newkey), FAN_MANUAL_FMT, to_index(attr));
++
++ ret = applesmc_has_key(smc, newkey, &has_newkey);
++ if (ret)
++ return ret;
++
++ if (has_newkey) {
++ ret = applesmc_read_key(smc, newkey, buffer, 1);
++ manual = buffer[0];
++ } else {
++ ret = applesmc_read_key(smc, FANS_MANUAL, buffer, 2);
++ manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01;
++ }
+
+- ret = applesmc_read_key(smc, FANS_MANUAL, buffer, 2);
+ if (ret)
+ return ret;
+
+- manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01;
+ return sysfs_emit(sysfsbuf, "%d\n", manual);
+ }
+
+@@ -1307,27 +1380,39 @@ static ssize_t applesmc_store_fan_manual(struct device *dev,
+ struct applesmc_device *smc = dev_get_drvdata(dev);
+ int ret;
+ u8 buffer[2];
++ char newkey[5];
++ bool has_newkey = false;
+ unsigned long input;
+ u16 val;
+
+ if (kstrtoul(sysfsbuf, 10, &input) < 0)
+ return -EINVAL;
+
+- ret = applesmc_read_key(smc, FANS_MANUAL, buffer, 2);
++ scnprintf(newkey, sizeof(newkey), FAN_MANUAL_FMT, to_index(attr));
++
++ ret = applesmc_has_key(smc, newkey, &has_newkey);
+ if (ret)
+- goto out;
++ return ret;
+
+- val = (buffer[0] << 8 | buffer[1]);
++ if (has_newkey) {
++ buffer[0] = input & 1;
++ ret = applesmc_write_key(smc, newkey, buffer, 1);
++ } else {
++ ret = applesmc_read_key(smc, FANS_MANUAL, buffer, 2);
++ val = (buffer[0] << 8 | buffer[1]);
++ if (ret)
++ goto out;
+
+- if (input)
+- val = val | (0x01 << to_index(attr));
+- else
+- val = val & ~(0x01 << to_index(attr));
++ if (input)
++ val = val | (0x01 << to_index(attr));
++ else
++ val = val & ~(0x01 << to_index(attr));
+
+- buffer[0] = (val >> 8) & 0xFF;
+- buffer[1] = val & 0xFF;
++ buffer[0] = (val >> 8) & 0xFF;
++ buffer[1] = val & 0xFF;
+
+- ret = applesmc_write_key(smc, FANS_MANUAL, buffer, 2);
++ ret = applesmc_write_key(smc, FANS_MANUAL, buffer, 2);
++ }
+
+ out:
+ if (ret)
+--
+2.30.0
+
+From 58868e6f356229eab48cfdee1603011653a19c79 Mon Sep 17 00:00:00 2001
+From: Orlando Chamberlain <redecorating@protonmail.com>
+Date: Sun, 9 Oct 2022 15:59:01 +0530
+Subject: [PATCH] applesmc: Add iMacPro to applesmc_whitelist
+
+The iMacPro1,1 is the only iMacPro released before the line was
+discontinued. Add it to the applesmc_whitelist.
+
+Signed-off-by: Orlando Chamberlain <redecorating@protonmail.com>
+---
+ drivers/hwmon/applesmc.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
+index 8b3f73fcb..493f95bb0 100644
+--- a/drivers/hwmon/applesmc.c
++++ b/drivers/hwmon/applesmc.c
+@@ -1804,6 +1804,10 @@ static const struct dmi_system_id applesmc_whitelist[] __initconst = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Macmini") },
+ },
++ { applesmc_dmi_match, "Apple iMacPro", {
++ DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "iMacPro") },
++ },
+ { applesmc_dmi_match, "Apple MacPro", {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") },
+--
+2.34.1
+
+From e52b0fad357b6203691942831715fce4f26d66e2 Mon Sep 17 00:00:00 2001
+From: Orlando Chamberlain <orlandoch.dev@gmail.com>
+Date: Tue, 24 Jan 2023 15:46:48 +1100
+Subject: [PATCH 1/1] applesmc: make applesmc_remove void
+
+for linux6.2 compatibility
+---
+ drivers/hwmon/applesmc.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
+index d071130ff68d..12be9269a314 100644
+--- a/drivers/hwmon/applesmc.c
++++ b/drivers/hwmon/applesmc.c
+@@ -979,7 +979,7 @@ static int applesmc_add(struct acpi_device *dev)
+ return ret;
+ }
+
+-static int applesmc_remove(struct acpi_device *dev)
++static void applesmc_remove(struct acpi_device *dev)
+ {
+ struct applesmc_device *smc = dev_get_drvdata(&dev->dev);
+
+@@ -990,7 +990,7 @@ static int applesmc_remove(struct acpi_device *dev)
+ mutex_destroy(&smc->reg.mutex);
+ kfree(smc);
+
+- return 0;
++ return;
+ }
+
+ static acpi_status applesmc_walk_resources(struct acpi_resource *res,
+--
+2.39.1
+
+From 38786c7979c8ece013b5b7d5cb07dc2aa40198be Mon Sep 17 00:00:00 2001
+From: Orlando Chamberlain <orlandoch.dev@gmail.com>
+Date: Mon, 30 Jan 2023 18:42:21 +1100
+Subject: [PATCH 1/1] applesmc: battery charge limiter
+
+---
+ drivers/hwmon/applesmc.c | 42 +++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 41 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
+index 12be9269a314..bc1eec74cfef 100644
+--- a/drivers/hwmon/applesmc.c
++++ b/drivers/hwmon/applesmc.c
+@@ -1478,6 +1478,35 @@ static void applesmc_brightness_set(struct led_classdev *led_cdev,
+ dev_dbg(led_cdev->dev, "work was already on the queue.\n");
+ }
+
++static ssize_t applesmc_BCLM_store(struct device *dev,
++ struct device_attribute *attr, char *sysfsbuf, size_t count)
++{
++ struct applesmc_device *smc = dev_get_drvdata(dev);
++ u8 val;
++
++ if (kstrtou8(sysfsbuf, 10, &val) < 0)
++ return -EINVAL;
++
++ if (val < 0 || val > 100)
++ return -EINVAL;
++
++ if (applesmc_write_key(smc, "BCLM", &val, 1))
++ return -ENODEV;
++ return count;
++}
++
++static ssize_t applesmc_BCLM_show(struct device *dev,
++ struct device_attribute *attr, char *sysfsbuf)
++{
++ struct applesmc_device *smc = dev_get_drvdata(dev);
++ u8 val;
++
++ if (applesmc_read_key(smc, "BCLM", &val, 1))
++ return -ENODEV;
++
++ return sysfs_emit(sysfsbuf, "%d\n", val);
++}
++
+ static ssize_t applesmc_key_count_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+ {
+@@ -1612,6 +1641,11 @@ static struct applesmc_node_group temp_group[] = {
+ { }
+ };
+
++static struct applesmc_node_group BCLM_group[] = {
++ { "battery_charge_limit", applesmc_BCLM_show, applesmc_BCLM_store },
++ { }
++};
++
+ /* Module stuff */
+
+ /*
+@@ -1830,10 +1864,13 @@ static int applesmc_create_modules(struct applesmc_device *smc)
+ ret = applesmc_create_nodes(smc, info_group, 1);
+ if (ret)
+ goto out;
++ ret = applesmc_create_nodes(smc, BCLM_group, 1);
++ if (ret)
++ goto out_info;
+
+ ret = applesmc_create_nodes(smc, fan_group, smc->reg.fan_count);
+ if (ret)
+- goto out_info;
++ goto out_bclm;
+
+ ret = applesmc_create_nodes(smc, temp_group, smc->reg.index_count);
+ if (ret)
+@@ -1869,6 +1906,8 @@ static int applesmc_create_modules(struct applesmc_device *smc)
+ applesmc_destroy_nodes(smc, temp_group);
+ out_fans:
+ applesmc_destroy_nodes(smc, fan_group);
++out_bclm:
++ applesmc_destroy_nodes(smc, BCLM_group);
+ out_info:
+ applesmc_destroy_nodes(smc, info_group);
+ out:
+@@ -1883,6 +1922,7 @@ static void applesmc_destroy_modules(struct applesmc_device *smc)
+ applesmc_release_accelerometer(smc);
+ applesmc_destroy_nodes(smc, temp_group);
+ applesmc_destroy_nodes(smc, fan_group);
++ applesmc_destroy_nodes(smc, BCLM_group);
+ applesmc_destroy_nodes(smc, info_group);
+ }
+
+--
+2.39.1
+
+From 327e6e1d0f6e8db68c124dff4d6a326b381ccedb Mon Sep 17 00:00:00 2001
+From: Aun-Ali Zaidi <admin@kodeit.net>
+Date: Wed, 23 Mar 2022 17:12:21 +0530
+Subject: [PATCH] Input: bcm5974 - Add support for the T2 Macs
+
+---
+ drivers/input/mouse/bcm5974.c | 138 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 138 insertions(+)
+
+diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
+index 59a14505b..88f17f21a 100644
+--- a/drivers/input/mouse/bcm5974.c
++++ b/drivers/input/mouse/bcm5974.c
+@@ -83,6 +83,24 @@
+ #define USB_DEVICE_ID_APPLE_WELLSPRING9_ISO 0x0273
+ #define USB_DEVICE_ID_APPLE_WELLSPRING9_JIS 0x0274
+
++/* T2-Attached Devices */
++/* MacbookAir8,1 (2018) */
++#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K 0x027a
++/* MacbookPro15,2 (2018) */
++#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132 0x027b
++/* MacbookPro15,1 (2018) */
++#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680 0x027c
++/* MacbookPro15,4 (2019) */
++#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213 0x027d
++/* MacbookPro16,2 (2020) */
++#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K 0x027e
++/* MacbookPro16,3 (2020) */
++#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223 0x027f
++/* MacbookAir9,1 (2020) */
++#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K 0x0280
++/* MacbookPro16,1 (2019)*/
++#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F 0x0340
++
+ #define BCM5974_DEVICE(prod) { \
+ .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_INT_CLASS | \
+@@ -147,6 +165,22 @@ static const struct usb_device_id bcm5974_table[] = {
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_ISO),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_JIS),
++ /* MacbookAir8,1 */
++ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K),
++ /* MacbookPro15,2 */
++ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132),
++ /* MacbookPro15,1 */
++ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680),
++ /* MacbookPro15,4 */
++ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213),
++ /* MacbookPro16,2 */
++ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K),
++ /* MacbookPro16,3 */
++ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223),
++ /* MacbookAir9,1 */
++ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K),
++ /* MacbookPro16,1 */
++ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F),
+ /* Terminating entry */
+ {}
+ };
+@@ -483,6 +517,110 @@ static const struct bcm5974_config bcm5974_config_table[] = {
+ { SN_COORD, -203, 6803 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
+ },
++ {
++ USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K,
++ 0,
++ 0,
++ HAS_INTEGRATED_BUTTON,
++ 0, sizeof(struct bt_data),
++ 0x83, DATAFORMAT(TYPE4),
++ { SN_PRESSURE, 0, 300 },
++ { SN_WIDTH, 0, 2048 },
++ { SN_COORD, -6243, 6749 },
++ { SN_COORD, -170, 7685 },
++ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
++ },
++ {
++ USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132,
++ 0,
++ 0,
++ HAS_INTEGRATED_BUTTON,
++ 0, sizeof(struct bt_data),
++ 0x83, DATAFORMAT(TYPE4),
++ { SN_PRESSURE, 0, 300 },
++ { SN_WIDTH, 0, 2048 },
++ { SN_COORD, -6243, 6749 },
++ { SN_COORD, -170, 7685 },
++ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
++ },
++ {
++ USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680,
++ 0,
++ 0,
++ HAS_INTEGRATED_BUTTON,
++ 0, sizeof(struct bt_data),
++ 0x83, DATAFORMAT(TYPE4),
++ { SN_PRESSURE, 0, 300 },
++ { SN_WIDTH, 0, 2048 },
++ { SN_COORD, -7456, 7976 },
++ { SN_COORD, -1768, 7685 },
++ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
++ },
++ {
++ USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213,
++ 0,
++ 0,
++ HAS_INTEGRATED_BUTTON,
++ 0, sizeof(struct bt_data),
++ 0x83, DATAFORMAT(TYPE4),
++ { SN_PRESSURE, 0, 300 },
++ { SN_WIDTH, 0, 2048 },
++ { SN_COORD, -6243, 6749 },
++ { SN_COORD, -170, 7685 },
++ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
++ },
++ {
++ USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K,
++ 0,
++ 0,
++ HAS_INTEGRATED_BUTTON,
++ 0, sizeof(struct bt_data),
++ 0x83, DATAFORMAT(TYPE4),
++ { SN_PRESSURE, 0, 300 },
++ { SN_WIDTH, 0, 2048 },
++ { SN_COORD, -7823, 8329 },
++ { SN_COORD, -370, 7925 },
++ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
++ },
++ {
++ USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223,
++ 0,
++ 0,
++ HAS_INTEGRATED_BUTTON,
++ 0, sizeof(struct bt_data),
++ 0x83, DATAFORMAT(TYPE4),
++ { SN_PRESSURE, 0, 300 },
++ { SN_WIDTH, 0, 2048 },
++ { SN_COORD, -6243, 6749 },
++ { SN_COORD, -170, 7685 },
++ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
++ },
++ {
++ USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K,
++ 0,
++ 0,
++ HAS_INTEGRATED_BUTTON,
++ 0, sizeof(struct bt_data),
++ 0x83, DATAFORMAT(TYPE4),
++ { SN_PRESSURE, 0, 300 },
++ { SN_WIDTH, 0, 2048 },
++ { SN_COORD, -6243, 6749 },
++ { SN_COORD, -170, 7685 },
++ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
++ },
++ {
++ USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F,
++ 0,
++ 0,
++ HAS_INTEGRATED_BUTTON,
++ 0, sizeof(struct bt_data),
++ 0x83, DATAFORMAT(TYPE4),
++ { SN_PRESSURE, 0, 300 },
++ { SN_WIDTH, 0, 2048 },
++ { SN_COORD, -8916, 9918 },
++ { SN_COORD, -1934, 9835 },
++ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
++ },
+ {}
+ };
+
+--
+2.25.1
+
+From: Ashish Arora <ashisharora.linux@outlook.com>
+Subject: Re: [PATCH] drm/i915: Discard large BIOS framebuffers causing display corruption.
+Date: Sat, 08 Jan 2022 21:43:18 +1100
+
+On certain 4k panels, the BIOS framebuffer is larger than what panel
+requires causing display corruption. Introduce a check for the same.
+
+Signed-off-by: Ashish Arora <ashisharora.linux@outlook.com>
+---
+ drivers/gpu/drm/i915/display/intel_fbdev.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c
+index 112aa04..8fb8bcc 100644
+--- a/drivers/gpu/drm/i915/display/intel_fbdev.c
++++ b/drivers/gpu/drm/i915/display/intel_fbdev.c
+@@ -217,10 +217,10 @@ static int intelfb_create(struct drm_fb_helper *helper,
+ return ret;
+
+ if (intel_fb &&
+- (sizes->fb_width > intel_fb->base.width ||
+- sizes->fb_height > intel_fb->base.height)) {
++ (sizes->fb_width != intel_fb->base.width ||
++ sizes->fb_height != intel_fb->base.height)) {
+ drm_dbg_kms(&dev_priv->drm,
+- "BIOS fb too small (%dx%d), we require (%dx%d),"
++ "BIOS fb not valid (%dx%d), we require (%dx%d),"
+ " releasing it\n",
+ intel_fb->base.width, intel_fb->base.height,
+ sizes->fb_width, sizes->fb_height);
+--
+1.8.3.1
+
+From 3d4a4a3d62815f90fc65a827a3e2de96c4571350 Mon Sep 17 00:00:00 2001
+From: Orlando Chamberlain <orlandoch.dev@gmail.com>
+Date: Mon, 20 Nov 2023 10:32:23 +1100
+Subject: [PATCH 1/1] acpi video: force native for some T2 macbooks
+
+The intel backlight is needed for these.
+
+MacBookPro15,2/4 or MacBookPro16,3 or MacBookAir8,1/2 might also need
+this so I'm not going to be submitting this upstream yet
+
+mbp16,3 was reported not to have this issue for 6.5.8 at least.
+---
+ drivers/acpi/video_detect.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
+index 442396f6ed1f..baf7264d7b94 100644
+--- a/drivers/acpi/video_detect.c
++++ b/drivers/acpi/video_detect.c
+@@ -513,6 +513,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "iMac12,2"),
+ },
+ },
++ {
++ .callback = video_detect_force_native,
++ /* Apple MacBook Air 9,1 */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir9,1"),
++ },
++ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1217249 */
+ .callback = video_detect_force_native,
+@@ -522,6 +530,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"),
+ },
+ },
++ {
++ .callback = video_detect_force_native,
++ /* Apple MacBook Pro 16,2 */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,2"),
++ },
++ },
+ {
+ .callback = video_detect_force_native,
+ /* Dell Inspiron N4010 */
+--
+2.42.1
+
+From 923cfe9b86c71761b164f995631817e9af169f29 Mon Sep 17 00:00:00 2001
+From: Hector Martin <marcan@marcan.st>
+Date: Tue, 14 Feb 2023 18:33:19 +0900
+Subject: [PATCH] brcmfmac: cfg80211: Use WSEC to set SAE password
+
+Using the WSEC command instead of sae_password seems to be the supported
+mechanism on newer firmware, and also how the brcmdhd driver does it.
+
+Signed-off-by: Hector Martin <marcan@marcan.st>
+---
+ .../broadcom/brcm80211/brcmfmac/cfg80211.c | 46 ++++++++-----------
+ .../broadcom/brcm80211/brcmfmac/fwil_types.h | 2 +-
+ 2 files changed, 20 insertions(+), 28 deletions(-)
+
+diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+index 87f4d53fb..7ccdbafca 100644
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -1682,52 +1682,44 @@ static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
+ return reason;
+ }
+
+-static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len)
++static int brcmf_set_wsec(struct brcmf_if *ifp, const u8 *key, u16 key_len, u16 flags)
+ {
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct brcmf_wsec_pmk_le pmk;
+ int err;
+
++ if (key_len > sizeof(pmk.key)) {
++ bphy_err(drvr, "key must be less than %zu bytes\n",
++ sizeof(pmk.key));
++ return -EINVAL;
++ }
++
+ memset(&pmk, 0, sizeof(pmk));
+
+- /* pass pmk directly */
+- pmk.key_len = cpu_to_le16(pmk_len);
+- pmk.flags = cpu_to_le16(0);
+- memcpy(pmk.key, pmk_data, pmk_len);
++ /* pass key material directly */
++ pmk.key_len = cpu_to_le16(key_len);
++ pmk.flags = cpu_to_le16(flags);
++ memcpy(pmk.key, key, key_len);
+
+- /* store psk in firmware */
++ /* store key material in firmware */
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK,
+ &pmk, sizeof(pmk));
+ if (err < 0)
+ bphy_err(drvr, "failed to change PSK in firmware (len=%u)\n",
+- pmk_len);
++ key_len);
+
+ return err;
+ }
+
++static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len)
++{
++ return brcmf_set_wsec(ifp, pmk_data, pmk_len, 0);
++}
++
+ static int brcmf_set_sae_password(struct brcmf_if *ifp, const u8 *pwd_data,
+ u16 pwd_len)
+ {
+- struct brcmf_pub *drvr = ifp->drvr;
+- struct brcmf_wsec_sae_pwd_le sae_pwd;
+- int err;
+-
+- if (pwd_len > BRCMF_WSEC_MAX_SAE_PASSWORD_LEN) {
+- bphy_err(drvr, "sae_password must be less than %d\n",
+- BRCMF_WSEC_MAX_SAE_PASSWORD_LEN);
+- return -EINVAL;
+- }
+-
+- sae_pwd.key_len = cpu_to_le16(pwd_len);
+- memcpy(sae_pwd.key, pwd_data, pwd_len);
+-
+- err = brcmf_fil_iovar_data_set(ifp, "sae_password", &sae_pwd,
+- sizeof(sae_pwd));
+- if (err < 0)
+- bphy_err(drvr, "failed to set SAE password in firmware (len=%u)\n",
+- pwd_len);
+-
+- return err;
++ return brcmf_set_wsec(ifp, pwd_data, pwd_len, BRCMF_WSEC_PASSPHRASE);
+ }
+
+ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason,
+diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+index 792adaf88..3ba90878c 100644
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+@@ -574,7 +574,7 @@ struct brcmf_wsec_key_le {
+ struct brcmf_wsec_pmk_le {
+ __le16 key_len;
+ __le16 flags;
+- u8 key[2 * BRCMF_WSEC_MAX_PSK_LEN + 1];
++ u8 key[BRCMF_WSEC_MAX_SAE_PASSWORD_LEN];
+ };
+
+ /**
+--
+2.37.2
+
+From patchwork Wed Dec 27 10:10:03 2023
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+X-Patchwork-Submitter: Johan Hovold <johan+linaro@kernel.org>
+X-Patchwork-Id: 13505281
+Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org
+ [10.30.226.201])
+ (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
+ (No client certificate requested)
+ by smtp.subspace.kernel.org (Postfix) with ESMTPS id 731F42D602;
+ Wed, 27 Dec 2023 10:10:50 +0000 (UTC)
+Authentication-Results: smtp.subspace.kernel.org;
+ dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org
+ header.b="OE5gY3Rg"
+Received: by smtp.kernel.org (Postfix) with ESMTPSA id E1F71C433C8;
+ Wed, 27 Dec 2023 10:10:49 +0000 (UTC)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org;
+ s=k20201202; t=1703671849;
+ bh=HNX2qe5wIUjgAOE0bih0cYXbYbw77i5qRYAGTFhWf8Q=;
+ h=From:To:Cc:Subject:Date:From;
+ b=OE5gY3RgSNMMNpr/DemitpLvv8B5KUxkea+huKa97KhEilNAbl/OG/gZPSswoI3kl
+ ifwN2LiGgFt8jyQh8hVsCoIrrOOGgiqeJ9ivyZI86fxAmaICglCBVc65vzpPozQdYn
+ YsryqO/D6A6i3egHRr7G52DifE/DihYN9uZqhAIHTY+ESsr/mJvwodvV8HNt60TaF9
+ dFeWSj4rAgt/QaclFNs1wznkamzzJ3UloOq2NJbzC3F6ILEsWfuPRm8iKBlgwdNTZ+
+ bn4JmN3Zh0Mr2uaTVg902uWeLcZ93sY9BmqH1AOBDEXTlUvPd7n6xVrSnOLfdlTR5k
+ O5JKLTyyjvyTQ==
+Received: from johan by xi.lan with local (Exim 4.96.2)
+ (envelope-from <johan+linaro@kernel.org>)
+ id 1rIQs7-0002kc-0u;
+ Wed, 27 Dec 2023 11:10:44 +0100
+From: Johan Hovold <johan+linaro@kernel.org>
+To: Luiz Augusto von Dentz <luiz.dentz@gmail.com>,
+ Marcel Holtmann <marcel@holtmann.org>,
+ Johan Hedberg <johan.hedberg@gmail.com>
+Cc: Hector Martin <marcan@marcan.st>,
+ Sven Peter <sven@svenpeter.dev>,
+ Alyssa Rosenzweig <alyssa@rosenzweig.io>,
+ asahi@lists.linux.dev,
+ linux-arm-kernel@lists.infradead.org,
+ linux-bluetooth@vger.kernel.org,
+ linux-kernel@vger.kernel.org,
+ Johan Hovold <johan+linaro@kernel.org>,
+ stable@vger.kernel.org,
+ Felix Zhang <mrman@mrman314.tech>
+Subject: [PATCH] Bluetooth: hci_bcm4377: do not mark valid bd_addr as invalid
+Date: Wed, 27 Dec 2023 11:10:03 +0100
+Message-ID: <20231227101003.10534-1-johan+linaro@kernel.org>
+X-Mailer: git-send-email 2.41.0
+Precedence: bulk
+X-Mailing-List: linux-bluetooth@vger.kernel.org
+List-Id: <linux-bluetooth.vger.kernel.org>
+List-Subscribe: <mailto:linux-bluetooth+subscribe@vger.kernel.org>
+List-Unsubscribe: <mailto:linux-bluetooth+unsubscribe@vger.kernel.org>
+MIME-Version: 1.0
+
+A recent commit restored the original (and still documented) semantics
+for the HCI_QUIRK_USE_BDADDR_PROPERTY quirk so that the device address
+is considered invalid unless an address is provided by firmware.
+
+This specifically means that this flag must only be set for devices with
+invalid addresses, but the Broadcom BCM4377 driver has so far been
+setting this flag unconditionally.
+
+Fortunately the driver already checks for invalid addresses during setup
+and sets the HCI_QUIRK_INVALID_BDADDR flag, which can simply be replaced
+with HCI_QUIRK_USE_BDADDR_PROPERTY to indicate that the default address
+is invalid but can be overridden by firmware (long term, this should
+probably just always be allowed).
+
+Fixes: 6945795bc81a ("Bluetooth: fix use-bdaddr-property quirk")
+Cc: stable@vger.kernel.org # 6.5
+Reported-by: Felix Zhang <mrman@mrman314.tech>
+Link: https://lore.kernel.org/r/77419ffacc5b4875e920e038332575a2a5bff29f.camel@mrman314.tech/
+Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
+Reported-by: Felix Zhang <mrman@mrman314.tech>
+---
+ drivers/bluetooth/hci_bcm4377.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c
+index a61757835695..9a7243d5db71 100644
+--- a/drivers/bluetooth/hci_bcm4377.c
++++ b/drivers/bluetooth/hci_bcm4377.c
+@@ -1417,7 +1417,7 @@ static int bcm4377_check_bdaddr(struct bcm4377_data *bcm4377)
+
+ bda = (struct hci_rp_read_bd_addr *)skb->data;
+ if (!bcm4377_is_valid_bdaddr(bcm4377, &bda->bdaddr))
+- set_bit(HCI_QUIRK_INVALID_BDADDR, &bcm4377->hdev->quirks);
++ set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &bcm4377->hdev->quirks);
+
+ kfree_skb(skb);
+ return 0;
+@@ -2368,7 +2368,6 @@ static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+ hdev->set_bdaddr = bcm4377_hci_set_bdaddr;
+ hdev->setup = bcm4377_hci_setup;
+
+- set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
+ if (bcm4377->hw->broken_mws_transport_config)
+ set_bit(HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG, &hdev->quirks);
+ if (bcm4377->hw->broken_ext_scan)