Commit 83c2713a authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'platform-drivers-x86-v5.17-2' of...

Merge tag 'platform-drivers-x86-v5.17-2' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86

Pull x86 platform driver fixes from Hans de Goede:
 "This consists of various build- and bug-fixes as well as a few
  hardware-id additions.

  Highlights:
   - Bunch of fixes for the new x86-android-tablets module
   - Misc other fixes
   - A couple of hw-id additions"

* tag 'platform-drivers-x86-v5.17-2' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86:
  platform/x86: thinkpad_acpi: Fix incorrect use of platform profile on AMD platforms
  platform/x86: amd-pmc: Correct usage of SMU version
  platform/x86: asus-tf103c-dock: Make 2 global structs static
  platform/x86: amd-pmc: Make amd_pmc_stb_debugfs_fops static
  platform/x86: ISST: Fix possible circular locking dependency detected
  platform/x86: intel_crystal_cove_charger: Fix IRQ masking / unmasking
  platform/x86: thinkpad_acpi: Add quirk for ThinkPads without a fan
  platform/x86: touchscreen_dmi: Add info for the RWC NANOTE P8 AY07J 2-in-1
  platform/surface: Reinstate platform dependency
  platform/x86: x86-android-tablets: Trivial typo fix for MODULE_AUTHOR
  platform/x86: x86-android-tablets: Fix the buttons on CZC P10T tablet
  platform/x86: x86-android-tablets: Constify the gpiod_lookup_tables arrays
  platform/x86: x86-android-tablets: Add an init() callback to struct x86_dev_info
  platform/x86: x86-android-tablets: Add support for disabling ACPI _AEI handlers
  platform/x86: x86-android-tablets: Correct crystal_cove_charger module name
parents 24d7f48c 836f35f7
......@@ -5,6 +5,7 @@
menuconfig SURFACE_PLATFORMS
bool "Microsoft Surface Platform-Specific Device Drivers"
depends on ARM64 || X86 || COMPILE_TEST
default y
help
Say Y here to get to see options for platform-specific device drivers
......
......@@ -124,9 +124,10 @@ struct amd_pmc_dev {
u32 cpu_id;
u32 active_ips;
/* SMU version information */
u16 major;
u16 minor;
u16 rev;
u8 smu_program;
u8 major;
u8 minor;
u8 rev;
struct device *dev;
struct pci_dev *rdev;
struct mutex lock; /* generic mutex lock */
......@@ -180,11 +181,13 @@ static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev)
if (rc)
return rc;
dev->major = (val >> 16) & GENMASK(15, 0);
dev->smu_program = (val >> 24) & GENMASK(7, 0);
dev->major = (val >> 16) & GENMASK(7, 0);
dev->minor = (val >> 8) & GENMASK(7, 0);
dev->rev = (val >> 0) & GENMASK(7, 0);
dev_dbg(dev->dev, "SMU version is %u.%u.%u\n", dev->major, dev->minor, dev->rev);
dev_dbg(dev->dev, "SMU program %u version is %u.%u.%u\n",
dev->smu_program, dev->major, dev->minor, dev->rev);
return 0;
}
......@@ -226,7 +229,7 @@ static int amd_pmc_stb_debugfs_release(struct inode *inode, struct file *filp)
return 0;
}
const struct file_operations amd_pmc_stb_debugfs_fops = {
static const struct file_operations amd_pmc_stb_debugfs_fops = {
.owner = THIS_MODULE,
.open = amd_pmc_stb_debugfs_open,
.read = amd_pmc_stb_debugfs_read,
......
......@@ -250,7 +250,7 @@ static int tf103c_dock_hid_raw_request(struct hid_device *hid, u8 reportnum,
return 0;
}
struct hid_ll_driver tf103c_dock_hid_ll_driver = {
static struct hid_ll_driver tf103c_dock_hid_ll_driver = {
.parse = tf103c_dock_hid_parse,
.start = tf103c_dock_hid_start,
.stop = tf103c_dock_hid_stop,
......@@ -921,7 +921,7 @@ static int __maybe_unused tf103c_dock_resume(struct device *dev)
return 0;
}
SIMPLE_DEV_PM_OPS(tf103c_dock_pm_ops, tf103c_dock_suspend, tf103c_dock_resume);
static SIMPLE_DEV_PM_OPS(tf103c_dock_pm_ops, tf103c_dock_suspend, tf103c_dock_resume);
static const struct acpi_device_id tf103c_dock_acpi_match[] = {
{"NPCE69A"},
......
......@@ -17,6 +17,7 @@
#include <linux/regmap.h>
#define CHGRIRQ_REG 0x0a
#define MCHGRIRQ_REG 0x17
struct crystal_cove_charger_data {
struct mutex buslock; /* irq_bus_lock */
......@@ -25,8 +26,8 @@ struct crystal_cove_charger_data {
struct irq_domain *irq_domain;
int irq;
int charger_irq;
bool irq_enabled;
bool irq_is_enabled;
u8 mask;
u8 new_mask;
};
static irqreturn_t crystal_cove_charger_irq(int irq, void *data)
......@@ -53,13 +54,9 @@ static void crystal_cove_charger_irq_bus_sync_unlock(struct irq_data *data)
{
struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data);
if (charger->irq_is_enabled != charger->irq_enabled) {
if (charger->irq_enabled)
enable_irq(charger->irq);
else
disable_irq(charger->irq);
charger->irq_is_enabled = charger->irq_enabled;
if (charger->mask != charger->new_mask) {
regmap_write(charger->regmap, MCHGRIRQ_REG, charger->new_mask);
charger->mask = charger->new_mask;
}
mutex_unlock(&charger->buslock);
......@@ -69,14 +66,14 @@ static void crystal_cove_charger_irq_unmask(struct irq_data *data)
{
struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data);
charger->irq_enabled = true;
charger->new_mask &= ~BIT(data->hwirq);
}
static void crystal_cove_charger_irq_mask(struct irq_data *data)
{
struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data);
charger->irq_enabled = false;
charger->new_mask |= BIT(data->hwirq);
}
static void crystal_cove_charger_rm_irq_domain(void *data)
......@@ -130,10 +127,13 @@ static int crystal_cove_charger_probe(struct platform_device *pdev)
irq_set_nested_thread(charger->charger_irq, true);
irq_set_noprobe(charger->charger_irq);
/* Mask the single 2nd level IRQ before enabling the 1st level IRQ */
charger->mask = charger->new_mask = BIT(0);
regmap_write(charger->regmap, MCHGRIRQ_REG, charger->mask);
ret = devm_request_threaded_irq(&pdev->dev, charger->irq, NULL,
crystal_cove_charger_irq,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
KBUILD_MODNAME, charger);
IRQF_ONESHOT, KBUILD_MODNAME, charger);
if (ret)
return dev_err_probe(&pdev->dev, ret, "requesting irq\n");
......
......@@ -596,7 +596,10 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
return ret;
}
static DEFINE_MUTEX(punit_misc_dev_lock);
/* Lock to prevent module registration when already opened by user space */
static DEFINE_MUTEX(punit_misc_dev_open_lock);
/* Lock to allow one share misc device for all ISST interace */
static DEFINE_MUTEX(punit_misc_dev_reg_lock);
static int misc_usage_count;
static int misc_device_ret;
static int misc_device_open;
......@@ -606,7 +609,7 @@ static int isst_if_open(struct inode *inode, struct file *file)
int i, ret = 0;
/* Fail open, if a module is going away */
mutex_lock(&punit_misc_dev_lock);
mutex_lock(&punit_misc_dev_open_lock);
for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
struct isst_if_cmd_cb *cb = &punit_callbacks[i];
......@@ -628,7 +631,7 @@ static int isst_if_open(struct inode *inode, struct file *file)
} else {
misc_device_open++;
}
mutex_unlock(&punit_misc_dev_lock);
mutex_unlock(&punit_misc_dev_open_lock);
return ret;
}
......@@ -637,7 +640,7 @@ static int isst_if_relase(struct inode *inode, struct file *f)
{
int i;
mutex_lock(&punit_misc_dev_lock);
mutex_lock(&punit_misc_dev_open_lock);
misc_device_open--;
for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
struct isst_if_cmd_cb *cb = &punit_callbacks[i];
......@@ -645,7 +648,7 @@ static int isst_if_relase(struct inode *inode, struct file *f)
if (cb->registered)
module_put(cb->owner);
}
mutex_unlock(&punit_misc_dev_lock);
mutex_unlock(&punit_misc_dev_open_lock);
return 0;
}
......@@ -662,6 +665,43 @@ static struct miscdevice isst_if_char_driver = {
.fops = &isst_if_char_driver_ops,
};
static int isst_misc_reg(void)
{
mutex_lock(&punit_misc_dev_reg_lock);
if (misc_device_ret)
goto unlock_exit;
if (!misc_usage_count) {
misc_device_ret = isst_if_cpu_info_init();
if (misc_device_ret)
goto unlock_exit;
misc_device_ret = misc_register(&isst_if_char_driver);
if (misc_device_ret) {
isst_if_cpu_info_exit();
goto unlock_exit;
}
}
misc_usage_count++;
unlock_exit:
mutex_unlock(&punit_misc_dev_reg_lock);
return misc_device_ret;
}
static void isst_misc_unreg(void)
{
mutex_lock(&punit_misc_dev_reg_lock);
if (misc_usage_count)
misc_usage_count--;
if (!misc_usage_count && !misc_device_ret) {
misc_deregister(&isst_if_char_driver);
isst_if_cpu_info_exit();
}
mutex_unlock(&punit_misc_dev_reg_lock);
}
/**
* isst_if_cdev_register() - Register callback for IOCTL
* @device_type: The device type this callback handling.
......@@ -679,38 +719,31 @@ static struct miscdevice isst_if_char_driver = {
*/
int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb)
{
if (misc_device_ret)
return misc_device_ret;
int ret;
if (device_type >= ISST_IF_DEV_MAX)
return -EINVAL;
mutex_lock(&punit_misc_dev_lock);
mutex_lock(&punit_misc_dev_open_lock);
/* Device is already open, we don't want to add new callbacks */
if (misc_device_open) {
mutex_unlock(&punit_misc_dev_lock);
mutex_unlock(&punit_misc_dev_open_lock);
return -EAGAIN;
}
if (!misc_usage_count) {
int ret;
misc_device_ret = misc_register(&isst_if_char_driver);
if (misc_device_ret)
goto unlock_exit;
ret = isst_if_cpu_info_init();
if (ret) {
misc_deregister(&isst_if_char_driver);
misc_device_ret = ret;
goto unlock_exit;
}
}
memcpy(&punit_callbacks[device_type], cb, sizeof(*cb));
punit_callbacks[device_type].registered = 1;
misc_usage_count++;
unlock_exit:
mutex_unlock(&punit_misc_dev_lock);
mutex_unlock(&punit_misc_dev_open_lock);
return misc_device_ret;
ret = isst_misc_reg();
if (ret) {
/*
* No need of mutex as the misc device register failed
* as no one can open device yet. Hence no contention.
*/
punit_callbacks[device_type].registered = 0;
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(isst_if_cdev_register);
......@@ -725,16 +758,12 @@ EXPORT_SYMBOL_GPL(isst_if_cdev_register);
*/
void isst_if_cdev_unregister(int device_type)
{
mutex_lock(&punit_misc_dev_lock);
misc_usage_count--;
isst_misc_unreg();
mutex_lock(&punit_misc_dev_open_lock);
punit_callbacks[device_type].registered = 0;
if (device_type == ISST_IF_DEV_MBOX)
isst_delete_hash();
if (!misc_usage_count && !misc_device_ret) {
misc_deregister(&isst_if_char_driver);
isst_if_cpu_info_exit();
}
mutex_unlock(&punit_misc_dev_lock);
mutex_unlock(&punit_misc_dev_open_lock);
}
EXPORT_SYMBOL_GPL(isst_if_cdev_unregister);
......
......@@ -8679,9 +8679,10 @@ static const struct attribute_group fan_driver_attr_group = {
.attrs = fan_driver_attributes,
};
#define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */
#define TPACPI_FAN_2FAN 0x0002 /* EC 0x31 bit 0 selects fan2 */
#define TPACPI_FAN_2CTL 0x0004 /* selects fan2 control */
#define TPACPI_FAN_Q1 0x0001 /* Uninitialized HFSP */
#define TPACPI_FAN_2FAN 0x0002 /* EC 0x31 bit 0 selects fan2 */
#define TPACPI_FAN_2CTL 0x0004 /* selects fan2 control */
#define TPACPI_FAN_NOFAN 0x0008 /* no fan available */
static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
TPACPI_QEC_IBM('1', 'Y', TPACPI_FAN_Q1),
......@@ -8702,6 +8703,7 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
TPACPI_Q_LNV3('N', '4', '0', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (4nd gen) */
TPACPI_Q_LNV3('N', '3', '0', TPACPI_FAN_2CTL), /* P15 (1st gen) / P15v (1st gen) */
TPACPI_Q_LNV3('N', '3', '2', TPACPI_FAN_2CTL), /* X1 Carbon (9th gen) */
TPACPI_Q_LNV3('N', '1', 'O', TPACPI_FAN_NOFAN), /* X1 Tablet (2nd gen) */
};
static int __init fan_init(struct ibm_init_struct *iibm)
......@@ -8730,6 +8732,11 @@ static int __init fan_init(struct ibm_init_struct *iibm)
quirks = tpacpi_check_quirks(fan_quirk_table,
ARRAY_SIZE(fan_quirk_table));
if (quirks & TPACPI_FAN_NOFAN) {
pr_info("No integrated ThinkPad fan available\n");
return -ENODEV;
}
if (gfan_handle) {
/* 570, 600e/x, 770e, 770x */
fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
......@@ -10112,6 +10119,9 @@ static struct ibm_struct proxsensor_driver_data = {
#define DYTC_CMD_MMC_GET 8 /* To get current MMC function and mode */
#define DYTC_CMD_RESET 0x1ff /* To reset back to default */
#define DYTC_CMD_FUNC_CAP 3 /* To get DYTC capabilities */
#define DYTC_FC_MMC 27 /* MMC Mode supported */
#define DYTC_GET_FUNCTION_BIT 8 /* Bits 8-11 - function setting */
#define DYTC_GET_MODE_BIT 12 /* Bits 12-15 - mode setting */
......@@ -10324,6 +10334,15 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
if (dytc_version < 5)
return -ENODEV;
/* Check what capabilities are supported. Currently MMC is needed */
err = dytc_command(DYTC_CMD_FUNC_CAP, &output);
if (err)
return err;
if (!(output & BIT(DYTC_FC_MMC))) {
dbg_printk(TPACPI_DBG_INIT, " DYTC MMC mode not supported\n");
return -ENODEV;
}
dbg_printk(TPACPI_DBG_INIT,
"DYTC version %d: thermal mode available\n", dytc_version);
/*
......
......@@ -770,6 +770,21 @@ static const struct ts_dmi_data predia_basic_data = {
.properties = predia_basic_props,
};
static const struct property_entry rwc_nanote_p8_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-y", 46),
PROPERTY_ENTRY_U32("touchscreen-size-x", 1728),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-rwc-nanote-p8.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
static const struct ts_dmi_data rwc_nanote_p8_data = {
.acpi_name = "MSSL1680:00",
.properties = rwc_nanote_p8_props,
};
static const struct property_entry schneider_sct101ctm_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1715),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
......@@ -1394,6 +1409,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
DMI_EXACT_MATCH(DMI_BOARD_NAME, "0E57"),
},
},
{
/* RWC NANOTE P8 */
.driver_data = (void *)&rwc_nanote_p8_data,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Default string"),
DMI_MATCH(DMI_PRODUCT_NAME, "AY07J"),
DMI_MATCH(DMI_PRODUCT_SKU, "0001")
},
},
{
/* Schneider SCT101CTM */
.driver_data = (void *)&schneider_sct101ctm_data,
......
......@@ -26,6 +26,7 @@
#include <linux/string.h>
/* For gpio_get_desc() which is EXPORT_SYMBOL_GPL() */
#include "../../gpio/gpiolib.h"
#include "../../gpio/gpiolib-acpi.h"
/*
* Helper code to get Linux IRQ numbers given a description of the IRQ source
......@@ -47,7 +48,7 @@ struct x86_acpi_irq_data {
int polarity; /* ACPI_ACTIVE_HIGH / ACPI_ACTIVE_LOW / ACPI_ACTIVE_BOTH */
};
static int x86_acpi_irq_helper_gpiochip_find(struct gpio_chip *gc, void *data)
static int gpiochip_find_match_label(struct gpio_chip *gc, void *data)
{
return gc->label && !strcmp(gc->label, data);
}
......@@ -73,7 +74,7 @@ static int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data)
return irq;
case X86_ACPI_IRQ_TYPE_GPIOINT:
/* Like acpi_dev_gpio_irq_get(), but without parsing ACPI resources */
chip = gpiochip_find(data->chip, x86_acpi_irq_helper_gpiochip_find);
chip = gpiochip_find(data->chip, gpiochip_find_match_label);
if (!chip) {
pr_err("error cannot find GPIO chip %s\n", data->chip);
return -ENODEV;
......@@ -143,14 +144,17 @@ struct x86_serdev_info {
};
struct x86_dev_info {
char *invalid_aei_gpiochip;
const char * const *modules;
struct gpiod_lookup_table **gpiod_lookup_tables;
struct gpiod_lookup_table * const *gpiod_lookup_tables;
const struct x86_i2c_client_info *i2c_client_info;
const struct platform_device_info *pdev_info;
const struct x86_serdev_info *serdev_info;
int i2c_client_count;
int pdev_count;
int serdev_count;
int (*init)(void);
void (*exit)(void);
};
/* Generic / shared bq24190 settings */
......@@ -187,8 +191,8 @@ static struct bq24190_platform_data bq24190_pdata = {
};
static const char * const bq24190_modules[] __initconst = {
"crystal_cove_charger", /* For the bq24190 IRQ */
"bq24190_charger", /* For the Vbus regulator for intel-int3496 */
"intel_crystal_cove_charger", /* For the bq24190 IRQ */
"bq24190_charger", /* For the Vbus regulator for intel-int3496 */
NULL
};
......@@ -302,7 +306,7 @@ static struct gpiod_lookup_table asus_me176c_goodix_gpios = {
},
};
static struct gpiod_lookup_table *asus_me176c_gpios[] = {
static struct gpiod_lookup_table * const asus_me176c_gpios[] = {
&int3496_gpo2_pin22_gpios,
&asus_me176c_goodix_gpios,
NULL
......@@ -317,6 +321,7 @@ static const struct x86_dev_info asus_me176c_info __initconst = {
.serdev_count = ARRAY_SIZE(asus_me176c_serdevs),
.gpiod_lookup_tables = asus_me176c_gpios,
.modules = bq24190_modules,
.invalid_aei_gpiochip = "INT33FC:02",
};
/* Asus TF103C tablets have an Android factory img with everything hardcoded */
......@@ -405,7 +410,7 @@ static const struct x86_i2c_client_info asus_tf103c_i2c_clients[] __initconst =
},
};
static struct gpiod_lookup_table *asus_tf103c_gpios[] = {
static struct gpiod_lookup_table * const asus_tf103c_gpios[] = {
&int3496_gpo2_pin22_gpios,
NULL
};
......@@ -417,6 +422,7 @@ static const struct x86_dev_info asus_tf103c_info __initconst = {
.pdev_count = ARRAY_SIZE(int3496_pdevs),
.gpiod_lookup_tables = asus_tf103c_gpios,
.modules = bq24190_modules,
.invalid_aei_gpiochip = "INT33FC:02",
};
/*
......@@ -490,6 +496,39 @@ static const struct x86_dev_info chuwi_hi8_info __initconst = {
.i2c_client_count = ARRAY_SIZE(chuwi_hi8_i2c_clients),
};
#define CZC_EC_EXTRA_PORT 0x68
#define CZC_EC_ANDROID_KEYS 0x63
static int __init czc_p10t_init(void)
{
/*
* The device boots up in "Windows 7" mode, when the home button sends a
* Windows specific key sequence (Left Meta + D) and the second button
* sends an unknown one while also toggling the Radio Kill Switch.
* This is a surprising behavior when the second button is labeled "Back".
*
* The vendor-supplied Android-x86 build switches the device to a "Android"
* mode by writing value 0x63 to the I/O port 0x68. This just seems to just
* set bit 6 on address 0x96 in the EC region; switching the bit directly
* seems to achieve the same result. It uses a "p10t_switcher" to do the
* job. It doesn't seem to be able to do anything else, and no other use
* of the port 0x68 is known.
*
* In the Android mode, the home button sends just a single scancode,
* which can be handled in Linux userspace more reasonably and the back
* button only sends a scancode without toggling the kill switch.
* The scancode can then be mapped either to Back or RF Kill functionality
* in userspace, depending on how the button is labeled on that particular
* model.
*/
outb(CZC_EC_ANDROID_KEYS, CZC_EC_EXTRA_PORT);
return 0;
}
static const struct x86_dev_info czc_p10t __initconst = {
.init = czc_p10t_init,
};
/*
* Whitelabel (sold as various brands) TM800A550L tablets.
* These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices
......@@ -559,7 +598,7 @@ static struct gpiod_lookup_table whitelabel_tm800a550l_goodix_gpios = {
},
};
static struct gpiod_lookup_table *whitelabel_tm800a550l_gpios[] = {
static struct gpiod_lookup_table * const whitelabel_tm800a550l_gpios[] = {
&whitelabel_tm800a550l_goodix_gpios,
NULL
};
......@@ -641,6 +680,24 @@ static const struct dmi_system_id x86_android_tablet_ids[] __initconst = {
},
.driver_data = (void *)&chuwi_hi8_info,
},
{
/* CZC P10T */
.ident = "CZC ODEON TPC-10 (\"P10T\")",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "CZC"),
DMI_MATCH(DMI_PRODUCT_NAME, "ODEON*TPC-10"),
},
.driver_data = (void *)&czc_p10t,
},
{
/* A variant of CZC P10T */
.ident = "ViewSonic ViewPad 10",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ViewSonic"),
DMI_MATCH(DMI_PRODUCT_NAME, "VPAD10"),
},
.driver_data = (void *)&czc_p10t,
},
{
/* Whitelabel (sold as various brands) TM800A550L */
.matches = {
......@@ -669,7 +726,8 @@ static int serdev_count;
static struct i2c_client **i2c_clients;
static struct platform_device **pdevs;
static struct serdev_device **serdevs;
static struct gpiod_lookup_table **gpiod_lookup_tables;
static struct gpiod_lookup_table * const *gpiod_lookup_tables;
static void (*exit_handler)(void);
static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info,
int idx)
......@@ -787,6 +845,9 @@ static void x86_android_tablet_cleanup(void)
kfree(i2c_clients);
if (exit_handler)
exit_handler();
for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++)
gpiod_remove_lookup_table(gpiod_lookup_tables[i]);
}
......@@ -795,6 +856,7 @@ static __init int x86_android_tablet_init(void)
{
const struct x86_dev_info *dev_info;
const struct dmi_system_id *id;
struct gpio_chip *chip;
int i, ret = 0;
id = dmi_first_match(x86_android_tablet_ids);
......@@ -803,6 +865,20 @@ static __init int x86_android_tablet_init(void)
dev_info = id->driver_data;
/*
* The broken DSDTs on these devices often also include broken
* _AEI (ACPI Event Interrupt) handlers, disable these.
*/
if (dev_info->invalid_aei_gpiochip) {
chip = gpiochip_find(dev_info->invalid_aei_gpiochip,
gpiochip_find_match_label);
if (!chip) {
pr_err("error cannot find GPIO chip %s\n", dev_info->invalid_aei_gpiochip);
return -ENODEV;
}
acpi_gpiochip_free_interrupts(chip);
}
/*
* Since this runs from module_init() it cannot use -EPROBE_DEFER,
* instead pre-load any modules which are listed as requirements.
......@@ -814,6 +890,15 @@ static __init int x86_android_tablet_init(void)
for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++)
gpiod_add_lookup_table(gpiod_lookup_tables[i]);
if (dev_info->init) {
ret = dev_info->init();
if (ret < 0) {
x86_android_tablet_cleanup();
return ret;
}
exit_handler = dev_info->exit;
}
i2c_clients = kcalloc(dev_info->i2c_client_count, sizeof(*i2c_clients), GFP_KERNEL);
if (!i2c_clients) {
x86_android_tablet_cleanup();
......@@ -865,6 +950,6 @@ static __init int x86_android_tablet_init(void)
module_init(x86_android_tablet_init);
module_exit(x86_android_tablet_cleanup);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("X86 Android tablets DSDT fixups driver");
MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment