diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c7981ddd8776..914a19b4290a 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -793,11 +793,21 @@ #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1 0x0732 #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_600 0x0750 #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c -#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD 0x00e3 -#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799 -#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 -#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 -#define USB_DEVICE_ID_MS_POWER_COVER 0x07da +#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD 0x00e3 +#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799 +#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 +#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 +#define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de +#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 0x07dc +#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1 0x07de +#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2 +#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd +#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4 0x07e8 +#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1 0x07e4 +#define USB_DEVICE_ID_MS_SURFACE_BOOK 0x07cd +#define USB_DEVICE_ID_MS_SURFACE_BOOK_2 0x0922 +#define USB_DEVICE_ID_MS_SURFACE_LAPTOP 0xf001 +#define USB_DEVICE_ID_MS_POWER_COVER 0x07da #define USB_VENDOR_ID_MOJO 0x8282 #define USB_DEVICE_ID_RETRO_ADAPTER 0x3201 diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 96e7d3231d2f..e55097221eec 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -278,7 +278,8 @@ static const struct hid_device_id ms_devices[] = { .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD), .driver_data = MS_ERGONOMY}, - + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_LAPTOP), + .driver_data = MS_HIDINPUT}, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT), .driver_data = MS_PRESENTER }, { } diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index a0e053c4dc1c..5298793b1eaf 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1754,6 +1754,58 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_LG, USB_DEVICE_ID_LG_MELFAS_MT) }, + /* Microsoft Touch Cover */ + { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, + MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_TOUCH_COVER_2) }, + + /* Microsoft Type Cover */ + { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, + MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_TYPE_COVER_2) }, + { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, + MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_TYPE_COVER_3) }, + { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, + MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) }, + { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, + MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1) }, + { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, + MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) }, + { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, + MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) }, + { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, + MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_TYPE_COVER_PRO_4) }, + { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, + MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1) }, + + /* Microsoft Surface Book */ + { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, + MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_SURFACE_BOOK) }, + + /* Microsoft Surface Book 2 */ + { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, + MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_SURFACE_BOOK_2) }, + + /* Microsoft Surface Laptop */ + { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, + HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, + USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_SURFACE_LAPTOP) }, + + /* Microsoft Power Cover */ + { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, + MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_POWER_COVER) }, + /* MosArt panels */ { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, MT_USB_DEVICE(USB_VENDOR_ID_ASUS, diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 249d49b6b16c..28fa76011df3 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -111,6 +111,16 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_PRO_2), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_BOOK), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_BOOK_2), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_LAPTOP), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_MULTIPLE_1781, USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD), HID_QUIRK_MULTI_INPUT }, diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 28c4cede2d91..4afa91fdb4da 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -85,6 +85,8 @@ obj-$(CONFIG_SURFACEBOOK2_BUTTON) += surfacebook2_button.o obj-$(CONFIG_ACPI_SURFACE) += surface_acpi.o obj-$(CONFIG_ACPI_SURFACE) += surface_i2c.o obj-$(CONFIG_ACPI_SURFACE) += surface_platform.o +obj-$(CONFIG_ACPI_SURFACE) += surface_vhf.o +obj-$(CONFIG_ACPI_SURFACE) += surface_vhf_keyboard.o obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ diff --git a/drivers/platform/x86/surface_vhf.c b/drivers/platform/x86/surface_vhf.c new file mode 100644 index 000000000000..2e9f2a670455 --- /dev/null +++ b/drivers/platform/x86/surface_vhf.c @@ -0,0 +1,328 @@ +/* + * surface_vhf.c - Microsoft Surface Virtual HID Framework Driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * The full GNU General Public License is included in the distribution in + * the file called "COPYING". + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jake Day"); + +#define STATUS_REG 0x0C +#define DATA_REG 0x10 +#define DATA_AVAIL 0x2 + +static const struct acpi_device_id surface_vhf_ids[] = { + {"MSHW0096", 0}, + {"", 0}, +}; + +struct surface_kbd { + struct input_dev *input; + struct resource *res; + void __iomem *io_base; + struct clk *clk; + unsigned short keycodes[256]; +}; + +struct kbd_platform_data { + const struct matrix_keymap_data *keymap; + bool rep; +}; + +static const struct key_entry surface_vhf_keymap[] = { + { KE_KEY, 1, { KEY_ESC } }, + { KE_KEY, 2, { KEY_F1 } }, + { KE_KEY, 3, { KEY_F2 } }, + { KE_KEY, 4, { KEY_F3 } }, + { KE_KEY, 5, { KEY_F4 } }, + { KE_KEY, 6, { KEY_F5 } }, + { KE_KEY, 7, { KEY_F6 } }, + { KE_KEY, 8, { KEY_F7 } }, + { KE_KEY, 9, { KEY_F8 } }, + { KE_KEY, 10, { KEY_F9 } }, + { KE_KEY, 11, { KEY_F10 } }, + { KE_KEY, 12, { KEY_F11 } }, + { KE_KEY, 13, { KEY_F12 } }, + { KE_KEY, 14, { KEY_POWER } }, + { KE_KEY, 15, { KEY_DELETE } }, + { KE_KEY, 16, { KEY_GRAVE } }, + { KE_KEY, 17, { KEY_1 } }, + { KE_KEY, 18, { KEY_2 } }, + { KE_KEY, 19, { KEY_3 } }, + { KE_KEY, 20, { KEY_4 } }, + { KE_KEY, 21, { KEY_5 } }, + { KE_KEY, 23, { KEY_6 } }, + { KE_KEY, 23, { KEY_7 } }, + { KE_KEY, 24, { KEY_8 } }, + { KE_KEY, 25, { KEY_9 } }, + { KE_KEY, 26, { KEY_0 } }, + { KE_KEY, 27, { KEY_MINUS } }, + { KE_KEY, 28, { KEY_EQUAL } }, + { KE_KEY, 29, { KEY_BACKSPACE } }, + { KE_KEY, 30, { KEY_TAB } }, + { KE_KEY, 31, { KEY_Q } }, + { KE_KEY, 32, { KEY_W } }, + { KE_KEY, 33, { KEY_E } }, + { KE_KEY, 34, { KEY_R } }, + { KE_KEY, 35, { KEY_T } }, + { KE_KEY, 36, { KEY_Y } }, + { KE_KEY, 37, { KEY_U } }, + { KE_KEY, 38, { KEY_I } }, + { KE_KEY, 39, { KEY_O } }, + { KE_KEY, 40, { KEY_P } }, + { KE_KEY, 41, { KEY_LEFTBRACE } }, + { KE_KEY, 42, { KEY_RIGHTBRACE } }, + { KE_KEY, 43, { KEY_BACKSLASH } }, + { KE_KEY, 44, { KEY_CAPSLOCK } }, + { KE_KEY, 45, { KEY_A } }, + { KE_KEY, 46, { KEY_S } }, + { KE_KEY, 47, { KEY_D } }, + { KE_KEY, 48, { KEY_F } }, + { KE_KEY, 49, { KEY_G } }, + { KE_KEY, 50, { KEY_H } }, + { KE_KEY, 51, { KEY_J } }, + { KE_KEY, 52, { KEY_K } }, + { KE_KEY, 53, { KEY_L } }, + { KE_KEY, 54, { KEY_SEMICOLON } }, + { KE_KEY, 55, { KEY_APOSTROPHE } }, + { KE_KEY, 56, { KEY_ENTER } }, + { KE_KEY, 57, { KEY_LEFTSHIFT } }, + { KE_KEY, 58, { KEY_Z } }, + { KE_KEY, 59, { KEY_X } }, + { KE_KEY, 60, { KEY_C } }, + { KE_KEY, 61, { KEY_V } }, + { KE_KEY, 62, { KEY_B } }, + { KE_KEY, 63, { KEY_N } }, + { KE_KEY, 64, { KEY_M } }, + { KE_KEY, 65, { KEY_COMMA } }, + { KE_KEY, 66, { KEY_DOT } }, + { KE_KEY, 67, { KEY_SLASH } }, + { KE_KEY, 68, { KEY_RIGHTSHIFT } }, + { KE_KEY, 69, { KEY_LEFTCTRL } }, + { KE_KEY, 70, { KEY_FN } }, + { KE_KEY, 71, { KEY_KPASTERISK } }, + { KE_KEY, 72, { KEY_LEFTALT } }, + { KE_KEY, 73, { KEY_SPACE } }, + { KE_KEY, 74, { KEY_RIGHTALT } }, + { KE_KEY, 75, { KEY_MENU } }, + { KE_KEY, 76, { KEY_LEFT } }, + { KE_KEY, 77, { KEY_UP } }, + { KE_KEY, 78, { KEY_DOWN } }, + { KE_KEY, 79, { KEY_RIGHT } }, + { KE_END }, +}; + + +static irqreturn_t surface_kbd_interrupt(int irq, void *dev_id) +{ + struct surface_kbd *kbd = dev_id; + struct input_dev *input = kbd->input; + unsigned int key; + u8 sts, val; + + sts = readb(kbd->io_base + STATUS_REG); + if (!(sts & DATA_AVAIL)) + return IRQ_NONE; + + val = readb(kbd->io_base + DATA_REG); + key = kbd->keycodes[val]; + + input_event(input, EV_MSC, MSC_SCAN, val); + input_report_key(input, key, 1); + input_sync(input); + + writeb(0, kbd->io_base + STATUS_REG); + + return IRQ_HANDLED; +} + +static int surface_vhf_probe(struct platform_device *pdev) +{ + struct resource *res; + struct surface_kbd *kbd; + struct input_dev *input_dev; + int error; + int ret; + int irq; + + pr_info("Surface VHF found\n"); + + pr_info("Surface VHF resources: %u\n", pdev->num_resources); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Surface VHF: No keyboard resource defined\n"); + return -EBUSY; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "not able to get irq for the device\n"); + return irq; + } + + kbd = kzalloc(sizeof(*kbd), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!kbd || !input_dev) { + dev_err(&pdev->dev, "Surface VHF: Out of memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + kbd->input = input_dev; + + /*kbd->res = request_mem_region(res->start, resource_size(res), + pdev->name); + if (!kbd->res) { + dev_err(&pdev->dev, "keyboard region already claimed\n"); + error = -EBUSY; + goto err_free_mem; + }*/ + + kbd->io_base = ioremap(res->start, resource_size(res)); + if (!kbd->io_base) { + dev_err(&pdev->dev, "Surface VHF: ioremap failed for kbd region\n"); + error = -ENOMEM; + goto err_release_mem_region; + } + + kbd->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(kbd->clk)) { + error = PTR_ERR(kbd->clk); + goto err_iounmap; + } + + input_dev->name = "Surface Laptop Keyboard"; + input_dev->phys = "keyboard/input0"; + input_dev->dev.parent = &pdev->dev; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x045e; + input_dev->id.product = 0xf001; + input_dev->id.version = 0x0001; + + __set_bit(EV_KEY, input_dev->evbit); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + input_dev->keycode = kbd->keycodes; + input_dev->keycodesize = sizeof(kbd->keycodes[0]); + input_dev->keycodemax = ARRAY_SIZE(kbd->keycodes); + + input_set_drvdata(input_dev, kbd); + + ret = sparse_keymap_setup(input_dev, surface_vhf_keymap, NULL); + if (ret) + return ret; + + error = request_irq(irq, surface_kbd_interrupt, 0, "keyboard", kbd); + if (error) { + dev_err(&pdev->dev, "Surface VHF: Request_irq fail\n"); + goto err_put_clk; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, "Surface VHF: Unable to register keyboard device\n"); + return 0; + } + + device_init_wakeup(&pdev->dev, 1); + platform_set_drvdata(pdev, kbd); + + return 0; + +/*err_free_irq: + free_irq(kbd->irq, kbd);*/ +err_put_clk: + clk_put(kbd->clk); +err_iounmap: + iounmap(kbd->io_base); +err_release_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free_mem: + input_free_device(input_dev); + kfree(kbd); + + return error; +} + +static int surface_vhf_remove(struct platform_device *pdev) +{ + device_init_wakeup(&pdev->dev, false); + + return 0; +} + +static struct platform_driver surface_vhf_driver = { + .driver = { + .name = "surface_vhf", + .acpi_match_table = surface_vhf_ids, + }, + .probe = surface_vhf_probe, + .remove = surface_vhf_remove, +}; +MODULE_DEVICE_TABLE(acpi, surface_vhf_ids); + +static acpi_status __init +check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + const struct acpi_device_id *ids = context; + struct acpi_device *dev; + + if (acpi_bus_get_device(handle, &dev) != 0) + return AE_OK; + + if (acpi_match_device_ids(dev, ids) == 0) + if (acpi_create_platform_device(dev, NULL)) + dev_info(&dev->dev, + "Surface VHF: Created platform device\n"); + + return AE_OK; +} + +static int __init surface_vhf_init(void) +{ + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, check_acpi_dev, NULL, + (void *)surface_vhf_ids, NULL); + + return platform_driver_register(&surface_vhf_driver); +} +module_init(surface_vhf_init); + +static void __exit surface_vhf_exit(void) +{ + platform_driver_unregister(&surface_vhf_driver); +} +module_exit(surface_vhf_exit); diff --git a/drivers/platform/x86/surface_vhf_keyboard.c b/drivers/platform/x86/surface_vhf_keyboard.c new file mode 100644 index 000000000000..c619d599e4ea --- /dev/null +++ b/drivers/platform/x86/surface_vhf_keyboard.c @@ -0,0 +1,73 @@ +/* + * surface_vhf_keyboard.c - Microsoft Surface Virtual HID Framework Keyboard Device + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * The full GNU General Public License is included in the distribution in + * the file called "COPYING". + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jake Day"); + +static struct resource surface_vhf_keyboard_resources[] = { + { + .start = 0x1a7bbaf9, + .end = 0x2d356b9e, + .flags = IORESOURCE_MEM, + .name = "io-memory" + }, + { + .start = 21, + .end = 21, + .flags = IORESOURCE_IRQ, + .name = "irq", + } +}; + +static struct platform_device surface_vhf_keyboard = { + .name = "surface_vhf", + .resource = surface_vhf_keyboard_resources, + .num_resources = ARRAY_SIZE(surface_vhf_keyboard_resources), +}; + +static int __init surface_hid_init(void) +{ + return platform_device_register(&surface_vhf_keyboard); +} +module_init(surface_hid_init); + +static void __exit surface_hid_exit(void) +{ + platform_device_unregister(&surface_vhf_keyboard); +} +module_exit(surface_hid_exit);