linux/drivers/input/misc/soc_button_array.c
<<
>>
Prefs
   1/*
   2 * Supports for the button array on SoC tablets originally running
   3 * Windows 8.
   4 *
   5 * (C) Copyright 2014 Intel Corporation
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License
   9 * as published by the Free Software Foundation; version 2
  10 * of the License.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/input.h>
  15#include <linux/init.h>
  16#include <linux/kernel.h>
  17#include <linux/acpi.h>
  18#include <linux/gpio/consumer.h>
  19#include <linux/gpio_keys.h>
  20#include <linux/input.h>
  21#include <linux/platform_device.h>
  22#include <linux/pnp.h>
  23
  24/*
  25 * Definition of buttons on the tablet. The ACPI index of each button
  26 * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
  27 * Platforms"
  28 */
  29#define MAX_NBUTTONS    5
  30
  31struct soc_button_info {
  32        const char *name;
  33        int acpi_index;
  34        unsigned int event_type;
  35        unsigned int event_code;
  36        bool autorepeat;
  37        bool wakeup;
  38};
  39
  40/*
  41 * Some of the buttons like volume up/down are auto repeat, while others
  42 * are not. To support both, we register two platform devices, and put
  43 * buttons into them based on whether the key should be auto repeat.
  44 */
  45#define BUTTON_TYPES    2
  46
  47struct soc_button_data {
  48        struct platform_device *children[BUTTON_TYPES];
  49};
  50
  51/*
  52 * Get the Nth GPIO number from the ACPI object.
  53 */
  54static int soc_button_lookup_gpio(struct device *dev, int acpi_index)
  55{
  56        struct gpio_desc *desc;
  57        int gpio;
  58
  59        desc = gpiod_get_index(dev, KBUILD_MODNAME, acpi_index);
  60        if (IS_ERR(desc))
  61                return PTR_ERR(desc);
  62
  63        gpio = desc_to_gpio(desc);
  64
  65        gpiod_put(desc);
  66
  67        return gpio;
  68}
  69
  70static struct platform_device *
  71soc_button_device_create(struct pnp_dev *pdev,
  72                         const struct soc_button_info *button_info,
  73                         bool autorepeat)
  74{
  75        const struct soc_button_info *info;
  76        struct platform_device *pd;
  77        struct gpio_keys_button *gpio_keys;
  78        struct gpio_keys_platform_data *gpio_keys_pdata;
  79        int n_buttons = 0;
  80        int gpio;
  81        int error;
  82
  83        gpio_keys_pdata = devm_kzalloc(&pdev->dev,
  84                                       sizeof(*gpio_keys_pdata) +
  85                                        sizeof(*gpio_keys) * MAX_NBUTTONS,
  86                                       GFP_KERNEL);
  87        gpio_keys = (void *)(gpio_keys_pdata + 1);
  88
  89        for (info = button_info; info->name; info++) {
  90                if (info->autorepeat != autorepeat)
  91                        continue;
  92
  93                gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index);
  94                if (gpio < 0)
  95                        continue;
  96
  97                gpio_keys[n_buttons].type = info->event_type;
  98                gpio_keys[n_buttons].code = info->event_code;
  99                gpio_keys[n_buttons].gpio = gpio;
 100                gpio_keys[n_buttons].active_low = 1;
 101                gpio_keys[n_buttons].desc = info->name;
 102                gpio_keys[n_buttons].wakeup = info->wakeup;
 103                n_buttons++;
 104        }
 105
 106        if (n_buttons == 0) {
 107                error = -ENODEV;
 108                goto err_free_mem;
 109        }
 110
 111        gpio_keys_pdata->buttons = gpio_keys;
 112        gpio_keys_pdata->nbuttons = n_buttons;
 113        gpio_keys_pdata->rep = autorepeat;
 114
 115        pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO);
 116        if (!pd) {
 117                error = -ENOMEM;
 118                goto err_free_mem;
 119        }
 120
 121        error = platform_device_add_data(pd, gpio_keys_pdata,
 122                                         sizeof(*gpio_keys_pdata));
 123        if (error)
 124                goto err_free_pdev;
 125
 126        error = platform_device_add(pd);
 127        if (error)
 128                goto err_free_pdev;
 129
 130        return pd;
 131
 132err_free_pdev:
 133        platform_device_put(pd);
 134err_free_mem:
 135        devm_kfree(&pdev->dev, gpio_keys_pdata);
 136        return ERR_PTR(error);
 137}
 138
 139static void soc_button_remove(struct pnp_dev *pdev)
 140{
 141        struct soc_button_data *priv = pnp_get_drvdata(pdev);
 142        int i;
 143
 144        for (i = 0; i < BUTTON_TYPES; i++)
 145                if (priv->children[i])
 146                        platform_device_unregister(priv->children[i]);
 147}
 148
 149static int soc_button_pnp_probe(struct pnp_dev *pdev,
 150                                const struct pnp_device_id *id)
 151{
 152        const struct soc_button_info *button_info = (void *)id->driver_data;
 153        struct soc_button_data *priv;
 154        struct platform_device *pd;
 155        int i;
 156        int error;
 157
 158        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 159        if (!priv)
 160                return -ENOMEM;
 161
 162        pnp_set_drvdata(pdev, priv);
 163
 164        for (i = 0; i < BUTTON_TYPES; i++) {
 165                pd = soc_button_device_create(pdev, button_info, i == 0);
 166                if (IS_ERR(pd)) {
 167                        error = PTR_ERR(pd);
 168                        if (error != -ENODEV) {
 169                                soc_button_remove(pdev);
 170                                return error;
 171                        }
 172                        continue;
 173                }
 174
 175                priv->children[i] = pd;
 176        }
 177
 178        if (!priv->children[0] && !priv->children[1])
 179                return -ENODEV;
 180
 181        return 0;
 182}
 183
 184static struct soc_button_info soc_button_PNP0C40[] = {
 185        { "power", 0, EV_KEY, KEY_POWER, false, true },
 186        { "home", 1, EV_KEY, KEY_HOME, false, true },
 187        { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
 188        { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false },
 189        { "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false },
 190        { }
 191};
 192
 193static const struct pnp_device_id soc_button_pnp_match[] = {
 194        { .id = "PNP0C40", .driver_data = (long)soc_button_PNP0C40 },
 195        { .id = "" }
 196};
 197MODULE_DEVICE_TABLE(pnp, soc_button_pnp_match);
 198
 199static struct pnp_driver soc_button_pnp_driver = {
 200        .name           = KBUILD_MODNAME,
 201        .id_table       = soc_button_pnp_match,
 202        .probe          = soc_button_pnp_probe,
 203        .remove         = soc_button_remove,
 204};
 205
 206static int __init soc_button_init(void)
 207{
 208        return pnp_register_driver(&soc_button_pnp_driver);
 209}
 210
 211static void __exit soc_button_exit(void)
 212{
 213        pnp_unregister_driver(&soc_button_pnp_driver);
 214}
 215
 216module_init(soc_button_init);
 217module_exit(soc_button_exit);
 218
 219MODULE_LICENSE("GPL");
 220