+++ /dev/null
-Device tree bindings for TI's ADS7843, ADS7845, ADS7846, ADS7873, TSC2046
-SPI driven touch screen controllers.
-
-The node for this driver must be a child node of a SPI controller, hence
-all mandatory properties described in
-
- Documentation/devicetree/bindings/spi/spi-bus.txt
-
-must be specified.
-
-Additional required properties:
-
- compatible Must be one of the following, depending on the
- model:
- "ti,tsc2046"
- "ti,ads7843"
- "ti,ads7845"
- "ti,ads7846"
- "ti,ads7873"
-
- interrupt-parent
- interrupts An interrupt node describing the IRQ line the chip's
- !PENIRQ pin is connected to.
- vcc-supply A regulator node for the supply voltage.
-
-
-Optional properties:
-
- ti,vref-delay-usecs vref supply delay in usecs, 0 for
- external vref (u16).
- ti,vref-mv The VREF voltage, in millivolts (u16).
- Set to 0 to use internal references
- (ADS7846).
- ti,keep-vref-on set to keep vref on for differential
- measurements as well
- ti,swap-xy swap x and y axis
- ti,settle-delay-usec Settling time of the analog signals;
- a function of Vcc and the capacitance
- on the X/Y drivers. If set to non-zero,
- two samples are taken with settle_delay
- us apart, and the second one is used.
- ~150 uSec with 0.01uF caps (u16).
- ti,penirq-recheck-delay-usecs If set to non-zero, after samples are
- taken this delay is applied and penirq
- is rechecked, to help avoid false
- events. This value is affected by the
- material used to build the touch layer
- (u16).
- ti,x-plate-ohms Resistance of the X-plate,
- in Ohms (u16).
- ti,y-plate-ohms Resistance of the Y-plate,
- in Ohms (u16).
- ti,x-min Minimum value on the X axis (u16).
- ti,y-min Minimum value on the Y axis (u16).
- ti,x-max Maximum value on the X axis (u16).
- ti,y-max Minimum value on the Y axis (u16).
- ti,pressure-min Minimum reported pressure value
- (threshold) - u16.
- ti,pressure-max Maximum reported pressure value (u16).
- ti,debounce-max Max number of additional readings per
- sample (u16).
- ti,debounce-tol Tolerance used for filtering (u16).
- ti,debounce-rep Additional consecutive good readings
- required after the first two (u16).
- ti,pendown-gpio-debounce Platform specific debounce time for the
- pendown-gpio (u32).
- pendown-gpio GPIO handle describing the pin the !PENIRQ
- line is connected to.
- wakeup-source use any event on touchscreen as wakeup event.
- (Legacy property support: "linux,wakeup")
-
-
-Example for a TSC2046 chip connected to an McSPI controller of an OMAP SoC::
-
- spi_controller {
- tsc2046@0 {
- reg = <0>; /* CS0 */
- compatible = "ti,tsc2046";
- interrupt-parent = <&gpio1>;
- interrupts = <8 0>; /* BOOT6 / GPIO 8 */
- spi-max-frequency = <1000000>;
- pendown-gpio = <&gpio1 8 0>;
- vcc-supply = <®_vcc3>;
-
- ti,x-min = /bits/ 16 <0>;
- ti,x-max = /bits/ 16 <8000>;
- ti,y-min = /bits/ 16 <0>;
- ti,y-max = /bits/ 16 <4800>;
- ti,x-plate-ohms = /bits/ 16 <40>;
- ti,pressure-max = /bits/ 16 <255>;
-
- wakeup-source;
- };
- };
- debounce-delay-ms: debounce interval in milliseconds
- col-scan-delay-us: delay, measured in microseconds, that is needed
before we can scan keypad after activating column gpio
+- drive-inactive-cols: drive inactive columns during scan,
+ default is to turn inactive columns into inputs.
Example:
matrix-keypad {
Optional properties:
- amp-supply: phandle to a regulator that acts as an amplifier for the beeper
+- beeper-hz: bell frequency in Hz
Example:
- adi,conversion-interval: : 0 : convert one time only
1-255: 515us + val * 35us (up to 9.440ms)
This property has to be a '/bits/ 8' value
+- gpio-controller : Switch AUX/VBAT/GPIO pin to GPIO mode
Example:
adi,averaging = /bits/ 8 <1>;
adi,conversion-interval = /bits/ 8 <255>;
};
+
+ ad7879@1 {
+ compatible = "adi,ad7879";
+ spi-max-frequency = <5000000>;
+ reg = <1>;
+ spi-cpol;
+ spi-cpha;
+ gpio-controller;
+ interrupt-parent = <&gpio1>;
+ interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
+ touchscreen-max-pressure = <4096>;
+ adi,resistance-plate-x = <120>;
+ adi,first-conversion-delay = /bits/ 8 <3>;
+ adi,acquisition-time = /bits/ 8 <1>;
+ adi,median-filter-size = /bits/ 8 <2>;
+ adi,averaging = /bits/ 8 <1>;
+ adi,conversion-interval = /bits/ 8 <255>;
+ };
--- /dev/null
+Device tree bindings for TI's ADS7843, ADS7845, ADS7846, ADS7873, TSC2046
+SPI driven touch screen controllers.
+
+The node for this driver must be a child node of a SPI controller, hence
+all mandatory properties described in
+
+ Documentation/devicetree/bindings/spi/spi-bus.txt
+
+must be specified.
+
+Additional required properties:
+
+ compatible Must be one of the following, depending on the
+ model:
+ "ti,tsc2046"
+ "ti,ads7843"
+ "ti,ads7845"
+ "ti,ads7846"
+ "ti,ads7873"
+
+ interrupt-parent
+ interrupts An interrupt node describing the IRQ line the chip's
+ !PENIRQ pin is connected to.
+ vcc-supply A regulator node for the supply voltage.
+
+
+Optional properties:
+
+ ti,vref-delay-usecs vref supply delay in usecs, 0 for
+ external vref (u16).
+ ti,vref-mv The VREF voltage, in millivolts (u16).
+ Set to 0 to use internal references
+ (ADS7846).
+ ti,keep-vref-on set to keep vref on for differential
+ measurements as well
+ ti,swap-xy swap x and y axis
+ ti,settle-delay-usec Settling time of the analog signals;
+ a function of Vcc and the capacitance
+ on the X/Y drivers. If set to non-zero,
+ two samples are taken with settle_delay
+ us apart, and the second one is used.
+ ~150 uSec with 0.01uF caps (u16).
+ ti,penirq-recheck-delay-usecs If set to non-zero, after samples are
+ taken this delay is applied and penirq
+ is rechecked, to help avoid false
+ events. This value is affected by the
+ material used to build the touch layer
+ (u16).
+ ti,x-plate-ohms Resistance of the X-plate,
+ in Ohms (u16).
+ ti,y-plate-ohms Resistance of the Y-plate,
+ in Ohms (u16).
+ ti,x-min Minimum value on the X axis (u16).
+ ti,y-min Minimum value on the Y axis (u16).
+ ti,x-max Maximum value on the X axis (u16).
+ ti,y-max Minimum value on the Y axis (u16).
+ ti,pressure-min Minimum reported pressure value
+ (threshold) - u16.
+ ti,pressure-max Maximum reported pressure value (u16).
+ ti,debounce-max Max number of additional readings per
+ sample (u16).
+ ti,debounce-tol Tolerance used for filtering (u16).
+ ti,debounce-rep Additional consecutive good readings
+ required after the first two (u16).
+ ti,pendown-gpio-debounce Platform specific debounce time for the
+ pendown-gpio (u32).
+ pendown-gpio GPIO handle describing the pin the !PENIRQ
+ line is connected to.
+ wakeup-source use any event on touchscreen as wakeup event.
+ (Legacy property support: "linux,wakeup")
+
+
+Example for a TSC2046 chip connected to an McSPI controller of an OMAP SoC::
+
+ spi_controller {
+ tsc2046@0 {
+ reg = <0>; /* CS0 */
+ compatible = "ti,tsc2046";
+ interrupt-parent = <&gpio1>;
+ interrupts = <8 0>; /* BOOT6 / GPIO 8 */
+ spi-max-frequency = <1000000>;
+ pendown-gpio = <&gpio1 8 0>;
+ vcc-supply = <®_vcc3>;
+
+ ti,x-min = /bits/ 16 <0>;
+ ti,x-max = /bits/ 16 <8000>;
+ ti,y-min = /bits/ 16 <0>;
+ ti,y-max = /bits/ 16 <4800>;
+ ti,x-plate-ohms = /bits/ 16 <40>;
+ ti,pressure-max = /bits/ 16 <255>;
+
+ wakeup-source;
+ };
+ };
--- /dev/null
+* MAXI MAX11801 Resistive touch screen controller with i2c interface
+
+Required properties:
+- compatible: must be "maxim,max11801"
+- reg: i2c slave address
+- interrupt-parent: the phandle for the interrupt controller
+- interrupts: touch controller interrupt
+
+Example:
+
+&i2c1 {
+ max11801: touchscreen@48 {
+ compatible = "maxim,max11801";
+ reg = <0x48>;
+ interrupt-parent = <&gpio3>;
+ interrupts = <31 IRQ_TYPE_EDGE_FALLING>;
+ };
+};
* GSL 1680 touchscreen controller
Required properties:
-- compatible : "silead,gsl1680"
+- compatible : Must be one of the following, depending on the model:
+ "silead,gsl1680"
+ "silead,gsl1688"
+ "silead,gsl3670"
+ "silead,gsl3675"
+ "silead,gsl3692"
- reg : I2C slave address of the chip (0x40)
- interrupt-parent : a phandle pointing to the interrupt controller
serving the interrupt for this chip
}
if (!try_module_get(owner)) {
- pr_warning("Can't get module reference, dropping event %d\n",
- event_type);
+ pr_warn("Can't get module reference, dropping event %d\n",
+ event_type);
kfree(event);
retval = -EINVAL;
goto out;
err = gc_n64_init_ff(input_dev, idx);
if (err) {
- pr_warning("Failed to initiate rumble for N64 device %d\n", idx);
+ pr_warn("Failed to initiate rumble for N64 device %d\n",
+ idx);
goto err_free_dev;
}
#include <linux/notifier.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/sysrq.h>
#include <linux/input/matrix_keypad.h>
#include <linux/mfd/cros_ec.h>
#include <linux/mfd/cros_ec_commands.h>
ckdev->ec->event_size);
break;
+ case EC_MKBP_EVENT_SYSRQ:
+ val = get_unaligned_le32(&ckdev->ec->event_data.data.sysrq);
+ dev_dbg(ckdev->dev, "sysrq code from EC: %#x\n", val);
+ handle_sysrq(val);
+ break;
+
case EC_MKBP_EVENT_BUTTON:
case EC_MKBP_EVENT_SWITCH:
/*
};
/*
- * NOTE: normally the GPIO has to be put into HiZ when de-activated to cause
- * minmal side effect when scanning other columns, here it is configured to
- * be input, and it should work on most platforms.
+ * NOTE: If drive_inactive_cols is false, then the GPIO has to be put into
+ * HiZ when de-activated to cause minmal side effect when scanning other
+ * columns. In that case it is configured here to be input, otherwise it is
+ * driven with the inactive value.
*/
static void __activate_col(const struct matrix_keypad_platform_data *pdata,
int col, bool on)
gpio_direction_output(pdata->col_gpios[col], level_on);
} else {
gpio_set_value_cansleep(pdata->col_gpios[col], !level_on);
- gpio_direction_input(pdata->col_gpios[col]);
+ if (!pdata->drive_inactive_cols)
+ gpio_direction_input(pdata->col_gpios[col]);
}
}
if (of_get_property(np, "gpio-activelow", NULL))
pdata->active_low = true;
+ pdata->drive_inactive_cols =
+ of_property_read_bool(np, "drive-inactive-cols");
+
of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms);
of_property_read_u32(np, "col-scan-delay-us",
&pdata->col_scan_delay_us);
};
MODULE_DEVICE_TABLE(i2c, qt1070_id);
+#ifdef CONFIG_OF
+static const struct of_device_id qt1070_of_match[] = {
+ { .compatible = "qt1070", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qt1070_of_match);
+#endif
+
static struct i2c_driver qt1070_driver = {
.driver = {
.name = "qt1070",
+ .of_match_table = of_match_ptr(qt1070_of_match),
.pm = &qt1070_pm_ops,
},
.id_table = qt1070_id,
input_event(input, EV_MSC, MSC_SCAN, code);
input_report_key(input, keymap[code], state);
- /* Read for next loop */
- error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, ®);
} while (1);
input_sync(input);
if (devno >= APANEL_DEV_MAX)
pr_notice(APANEL ": unknown device %u found\n", devno);
else if (device_chip[devno] != CHIP_NONE)
- pr_warning(APANEL ": duplicate entry for devno %u\n", devno);
+ pr_warn(APANEL ": duplicate entry for devno %u\n",
+ devno);
else if (method != 1 && method != 2 && method != 4) {
pr_notice(APANEL ": unknown method %u for devno %u\n",
* GNU General Public License for more details.
*/
+#include <linux/acpi.h>
#include <linux/errno.h>
#include <linux/irq.h>
#include <linux/init.h>
sysfs_remove_group(&dev->kobj, &axp20x_attribute_group);
}
-static int axp20x_pek_probe(struct platform_device *pdev)
+static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek,
+ struct platform_device *pdev)
{
- struct axp20x_pek *axp20x_pek;
- struct axp20x_dev *axp20x;
+ struct axp20x_dev *axp20x = axp20x_pek->axp20x;
struct input_dev *idev;
int error;
- axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
- GFP_KERNEL);
- if (!axp20x_pek)
- return -ENOMEM;
-
- axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
- axp20x = axp20x_pek->axp20x;
-
axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
if (axp20x_pek->irq_dbr < 0) {
dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n",
axp20x_pek_irq, 0,
"axp20x-pek-dbr", idev);
if (error < 0) {
- dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n",
+ dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n",
axp20x_pek->irq_dbr, error);
return error;
}
axp20x_pek_irq, 0,
"axp20x-pek-dbf", idev);
if (error < 0) {
- dev_err(axp20x->dev, "Failed to request dbf IRQ#%d: %d\n",
+ dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n",
axp20x_pek->irq_dbf, error);
return error;
}
- error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group);
+ error = input_register_device(idev);
if (error) {
- dev_err(axp20x->dev, "Failed to create sysfs attributes: %d\n",
+ dev_err(&pdev->dev, "Can't register input device: %d\n",
error);
return error;
}
- error = devm_add_action(&pdev->dev,
- axp20x_remove_sysfs_group, &pdev->dev);
+ return 0;
+}
+
+static int axp20x_pek_probe(struct platform_device *pdev)
+{
+ struct axp20x_pek *axp20x_pek;
+ int error;
+
+ axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
+ GFP_KERNEL);
+ if (!axp20x_pek)
+ return -ENOMEM;
+
+ axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
+
+ /*
+ * Do not register the input device if there is an "INTCFD9"
+ * gpio button ACPI device, that handles the power button too,
+ * and otherwise we end up reporting all presses twice.
+ */
+ if (!acpi_dev_found("INTCFD9") ||
+ !IS_ENABLED(CONFIG_INPUT_SOC_BUTTON_ARRAY)) {
+ error = axp20x_pek_probe_input_device(axp20x_pek, pdev);
+ if (error)
+ return error;
+ }
+
+ error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group);
if (error) {
- axp20x_remove_sysfs_group(&pdev->dev);
- dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n",
+ dev_err(&pdev->dev, "Failed to create sysfs attributes: %d\n",
error);
return error;
}
- error = input_register_device(idev);
+ error = devm_add_action(&pdev->dev,
+ axp20x_remove_sysfs_group, &pdev->dev);
if (error) {
- dev_err(axp20x->dev, "Can't register input device: %d\n",
+ axp20x_remove_sysfs_group(&pdev->dev);
+ dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n",
error);
return error;
}
#define BMA150_CFG_5_REG 0x11
#define BMA150_CHIP_ID 2
-#define BMA180_CHIP_ID 3
#define BMA150_CHIP_ID_REG BMA150_DATA_0_REG
#define BMA150_ACC_X_LSB_REG BMA150_DATA_2_REG
return -EIO;
}
- /*
- * Note if the IIO CONFIG_BMA180 driver is enabled we want to fail
- * the probe for the bma180 as the iio driver is preferred.
- */
chip_id = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG);
- if (chip_id != BMA150_CHIP_ID &&
- (IS_ENABLED(CONFIG_BMA180) || chip_id != BMA180_CHIP_ID)) {
+ if (chip_id != BMA150_CHIP_ID) {
dev_err(&client->dev, "BMA150 chip id error: %d\n", chip_id);
return -EINVAL;
}
static const struct i2c_device_id bma150_id[] = {
{ "bma150", 0 },
-#if !IS_ENABLED(CONFIG_BMA180)
- { "bma180", 0 },
-#endif
{ "smb380", 0 },
{ "bma023", 0 },
{ }
struct dm355evm_keys {
struct input_dev *input;
struct device *dev;
- int irq;
};
/* These initial keycodes can be remapped */
{
struct dm355evm_keys *keys;
struct input_dev *input;
- int status;
+ int irq;
+ int error;
- /* allocate instance struct and input dev */
- keys = kzalloc(sizeof *keys, GFP_KERNEL);
- input = input_allocate_device();
- if (!keys || !input) {
- status = -ENOMEM;
- goto fail1;
- }
+ keys = devm_kzalloc(&pdev->dev, sizeof (*keys), GFP_KERNEL);
+ if (!keys)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(&pdev->dev);
+ if (!input)
+ return -ENOMEM;
keys->dev = &pdev->dev;
keys->input = input;
- /* set up "threaded IRQ handler" */
- status = platform_get_irq(pdev, 0);
- if (status < 0)
- goto fail1;
- keys->irq = status;
-
input->name = "DM355 EVM Controls";
input->phys = "dm355evm/input0";
- input->dev.parent = &pdev->dev;
input->id.bustype = BUS_I2C;
input->id.product = 0x0355;
input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
- status = sparse_keymap_setup(input, dm355evm_keys, NULL);
- if (status)
- goto fail1;
+ error = sparse_keymap_setup(input, dm355evm_keys, NULL);
+ if (error)
+ return error;
/* REVISIT: flush the event queue? */
- status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- dev_name(&pdev->dev), keys);
- if (status < 0)
- goto fail2;
-
- /* register */
- status = input_register_device(input);
- if (status < 0)
- goto fail3;
-
- platform_set_drvdata(pdev, keys);
-
- return 0;
-
-fail3:
- free_irq(keys->irq, keys);
-fail2:
- sparse_keymap_free(input);
-fail1:
- input_free_device(input);
- kfree(keys);
- dev_err(&pdev->dev, "can't register, err %d\n", status);
-
- return status;
-}
+ /* set up "threaded IRQ handler" */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
-static int dm355evm_keys_remove(struct platform_device *pdev)
-{
- struct dm355evm_keys *keys = platform_get_drvdata(pdev);
+ error = devm_request_threaded_irq(&pdev->dev, irq,
+ NULL, dm355evm_keys_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(&pdev->dev), keys);
+ if (error)
+ return error;
- free_irq(keys->irq, keys);
- sparse_keymap_free(keys->input);
- input_unregister_device(keys->input);
- kfree(keys);
+ /* register */
+ error = input_register_device(input);
+ if (error)
+ return error;
return 0;
}
*/
static struct platform_driver dm355evm_keys_driver = {
.probe = dm355evm_keys_probe,
- .remove = dm355evm_keys_remove,
.driver = {
.name = "dm355evm_keys",
},
};
MODULE_DEVICE_TABLE(i2c, drv260x_id);
-#ifdef CONFIG_OF
static const struct of_device_id drv260x_of_match[] = {
{ .compatible = "ti,drv2604", },
{ .compatible = "ti,drv2604l", },
{ }
};
MODULE_DEVICE_TABLE(of, drv260x_of_match);
-#endif
static struct i2c_driver drv260x_driver = {
.probe = drv260x_probe,
.driver = {
.name = "drv260x-haptics",
- .of_match_table = of_match_ptr(drv260x_of_match),
+ .of_match_table = drv260x_of_match,
.pm = &drv260x_pm_ops,
},
.id_table = drv260x_id,
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
struct regulator *amplifier;
struct work_struct work;
unsigned long period;
+ unsigned int bell_frequency;
bool suspended;
bool amplifier_on;
};
switch (code) {
case SND_BELL:
- value = value ? 1000 : 0;
+ value = value ? beeper->bell_frequency : 0;
break;
case SND_TONE:
break;
struct device *dev = &pdev->dev;
struct pwm_beeper *beeper;
struct pwm_state state;
+ u32 bell_frequency;
int error;
beeper = devm_kzalloc(dev, sizeof(*beeper), GFP_KERNEL);
INIT_WORK(&beeper->work, pwm_beeper_work);
+ error = device_property_read_u32(dev, "beeper-hz", &bell_frequency);
+ if (error) {
+ bell_frequency = 1000;
+ dev_dbg(dev,
+ "failed to parse 'beeper-hz' property, using default: %uHz\n",
+ bell_frequency);
+ }
+
+ beeper->bell_frequency = bell_frequency;
+
beeper->input = devm_input_allocate_device(dev);
if (!beeper->input) {
dev_err(dev, "Failed to allocate input device\n");
#include <linux/gpio.h>
#include <linux/platform_device.h>
-/*
- * Definition of buttons on the tablet. The ACPI index of each button
- * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
- * Platforms"
- */
-#define MAX_NBUTTONS 5
-
struct soc_button_info {
const char *name;
int acpi_index;
struct gpio_desc *desc;
int gpio;
- desc = gpiod_get_index(dev, KBUILD_MODNAME, acpi_index, GPIOD_ASIS);
+ desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS);
if (IS_ERR(desc))
return PTR_ERR(desc);
int gpio;
int error;
+ for (info = button_info; info->name; info++)
+ if (info->autorepeat == autorepeat)
+ n_buttons++;
+
gpio_keys_pdata = devm_kzalloc(&pdev->dev,
sizeof(*gpio_keys_pdata) +
- sizeof(*gpio_keys) * MAX_NBUTTONS,
+ sizeof(*gpio_keys) * n_buttons,
GFP_KERNEL);
if (!gpio_keys_pdata)
return ERR_PTR(-ENOMEM);
gpio_keys = (void *)(gpio_keys_pdata + 1);
+ n_buttons = 0;
for (info = button_info; info->name; info++) {
if (info->autorepeat != autorepeat)
return ERR_PTR(error);
}
+static int soc_button_get_acpi_object_int(const union acpi_object *obj)
+{
+ if (obj->type != ACPI_TYPE_INTEGER)
+ return -1;
+
+ return obj->integer.value;
+}
+
+/* Parse a single ACPI0011 _DSD button descriptor */
+static int soc_button_parse_btn_desc(struct device *dev,
+ const union acpi_object *desc,
+ int collection_uid,
+ struct soc_button_info *info)
+{
+ int upage, usage;
+
+ if (desc->type != ACPI_TYPE_PACKAGE ||
+ desc->package.count != 5 ||
+ /* First byte should be 1 (control) */
+ soc_button_get_acpi_object_int(&desc->package.elements[0]) != 1 ||
+ /* Third byte should be collection uid */
+ soc_button_get_acpi_object_int(&desc->package.elements[2]) !=
+ collection_uid) {
+ dev_err(dev, "Invalid ACPI Button Descriptor\n");
+ return -ENODEV;
+ }
+
+ info->event_type = EV_KEY;
+ info->acpi_index =
+ soc_button_get_acpi_object_int(&desc->package.elements[1]);
+ upage = soc_button_get_acpi_object_int(&desc->package.elements[3]);
+ usage = soc_button_get_acpi_object_int(&desc->package.elements[4]);
+
+ /*
+ * The UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e descriptors use HID
+ * usage page and usage codes, but otherwise the device is not HID
+ * compliant: it uses one irq per button instead of generating HID
+ * input reports and some buttons should generate wakeups where as
+ * others should not, so we cannot use the HID subsystem.
+ *
+ * Luckily all devices only use a few usage page + usage combinations,
+ * so we can simply check for the known combinations here.
+ */
+ if (upage == 0x01 && usage == 0x81) {
+ info->name = "power";
+ info->event_code = KEY_POWER;
+ info->wakeup = true;
+ } else if (upage == 0x07 && usage == 0xe3) {
+ info->name = "home";
+ info->event_code = KEY_HOMEPAGE;
+ info->wakeup = true;
+ } else if (upage == 0x0c && usage == 0xe9) {
+ info->name = "volume_up";
+ info->event_code = KEY_VOLUMEUP;
+ info->autorepeat = true;
+ } else if (upage == 0x0c && usage == 0xea) {
+ info->name = "volume_down";
+ info->event_code = KEY_VOLUMEDOWN;
+ info->autorepeat = true;
+ } else {
+ dev_warn(dev, "Unknown button index %d upage %02x usage %02x, ignoring\n",
+ info->acpi_index, upage, usage);
+ info->name = "unknown";
+ info->event_code = KEY_RESERVED;
+ }
+
+ return 0;
+}
+
+/* ACPI0011 _DSD btns descriptors UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e */
+static const u8 btns_desc_uuid[16] = {
+ 0x25, 0xd6, 0x6b, 0xfa, 0xe8, 0x9c, 0x0d, 0x47,
+ 0xa2, 0xc7, 0xb3, 0xca, 0x36, 0xc4, 0x28, 0x2e
+};
+
+/* Parse ACPI0011 _DSD button descriptors */
+static struct soc_button_info *soc_button_get_button_info(struct device *dev)
+{
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+ const union acpi_object *desc, *el0, *uuid, *btns_desc = NULL;
+ struct soc_button_info *button_info;
+ acpi_status status;
+ int i, btn, collection_uid = -1;
+
+ status = acpi_evaluate_object_typed(ACPI_HANDLE(dev), "_DSD", NULL,
+ &buf, ACPI_TYPE_PACKAGE);
+ if (ACPI_FAILURE(status)) {
+ dev_err(dev, "ACPI _DSD object not found\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* Look for the Button Descriptors UUID */
+ desc = buf.pointer;
+ for (i = 0; (i + 1) < desc->package.count; i += 2) {
+ uuid = &desc->package.elements[i];
+
+ if (uuid->type != ACPI_TYPE_BUFFER ||
+ uuid->buffer.length != 16 ||
+ desc->package.elements[i + 1].type != ACPI_TYPE_PACKAGE) {
+ break;
+ }
+
+ if (memcmp(uuid->buffer.pointer, btns_desc_uuid, 16) == 0) {
+ btns_desc = &desc->package.elements[i + 1];
+ break;
+ }
+ }
+
+ if (!btns_desc) {
+ dev_err(dev, "ACPI Button Descriptors not found\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* The first package describes the collection */
+ el0 = &btns_desc->package.elements[0];
+ if (el0->type == ACPI_TYPE_PACKAGE &&
+ el0->package.count == 5 &&
+ /* First byte should be 0 (collection) */
+ soc_button_get_acpi_object_int(&el0->package.elements[0]) == 0 &&
+ /* Third byte should be 0 (top level collection) */
+ soc_button_get_acpi_object_int(&el0->package.elements[2]) == 0) {
+ collection_uid = soc_button_get_acpi_object_int(
+ &el0->package.elements[1]);
+ }
+ if (collection_uid == -1) {
+ dev_err(dev, "Invalid Button Collection Descriptor\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* There are package.count - 1 buttons + 1 terminating empty entry */
+ button_info = devm_kcalloc(dev, btns_desc->package.count,
+ sizeof(*button_info), GFP_KERNEL);
+ if (!button_info)
+ return ERR_PTR(-ENOMEM);
+
+ /* Parse the button descriptors */
+ for (i = 1, btn = 0; i < btns_desc->package.count; i++, btn++) {
+ if (soc_button_parse_btn_desc(dev,
+ &btns_desc->package.elements[i],
+ collection_uid,
+ &button_info[btn]))
+ return ERR_PTR(-ENODEV);
+ }
+
+ return button_info;
+}
+
static int soc_button_remove(struct platform_device *pdev)
{
struct soc_button_data *priv = platform_get_drvdata(pdev);
if (!id)
return -ENODEV;
- button_info = (struct soc_button_info *)id->driver_data;
+ if (!id->driver_data) {
+ button_info = soc_button_get_button_info(dev);
+ if (IS_ERR(button_info))
+ return PTR_ERR(button_info);
+ } else {
+ button_info = (struct soc_button_info *)id->driver_data;
+ }
- if (gpiod_count(dev, KBUILD_MODNAME) <= 0) {
+ if (gpiod_count(dev, NULL) <= 0) {
dev_dbg(dev, "no GPIO attached, ignoring...\n");
return -ENODEV;
}
if (!priv->children[0] && !priv->children[1])
return -ENODEV;
+ if (!id->driver_data)
+ devm_kfree(dev, button_info);
+
return 0;
}
+/*
+ * Definition of buttons on the tablet. The ACPI index of each button
+ * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
+ * Platforms"
+ */
static struct soc_button_info soc_button_PNP0C40[] = {
{ "power", 0, EV_KEY, KEY_POWER, false, true },
{ "home", 1, EV_KEY, KEY_LEFTMETA, false, true },
static const struct acpi_device_id soc_button_acpi_match[] = {
{ "PNP0C40", (unsigned long)soc_button_PNP0C40 },
+ { "ACPI0011", 0 },
{ }
};
error = input_register_polled_device(wistron_idev);
if (error)
- goto err_free_keymap;
+ goto err_free_dev;
return 0;
- err_free_keymap:
- sparse_keymap_free(input_dev);
err_free_dev:
input_free_polled_device(wistron_idev);
return error;
{
wistron_led_remove();
input_unregister_polled_device(wistron_idev);
- sparse_keymap_free(wistron_idev->input);
input_free_polled_device(wistron_idev);
bios_detach();
input_report_key(dev, event->key.keycode,
event->key.pressed);
else
- pr_warning("unhandled keycode 0x%x\n",
- event->key.keycode);
+ pr_warn("unhandled keycode 0x%x\n",
+ event->key.keycode);
break;
case XENKBD_TYPE_POS:
input_report_abs(dev, ABS_X, event->pos.abs_x);
ret = xenbus_write(XBT_NIL, dev->nodename,
"request-abs-pointer", "1");
if (ret) {
- pr_warning("xenkbd: can't request abs-pointer");
+ pr_warn("xenkbd: can't request abs-pointer\n");
abs = 0;
}
}
ret = xenbus_write(XBT_NIL, info->xbdev->nodename,
"request-abs-pointer", "1");
if (ret)
- pr_warning("xenkbd: can't request abs-pointer");
+ pr_warn("xenkbd: can't request abs-pointer\n");
}
xenbus_switch_state(dev, XenbusStateConnected);
If unsure, say Y.
+config MOUSE_PS2_SYNAPTICS_SMBUS
+ bool "Synaptics PS/2 SMbus companion" if EXPERT
+ default y
+ depends on MOUSE_PS2
+ depends on I2C=y || I2C=MOUSE_PS2
+ select MOUSE_PS2_SMBUS
+ help
+ Say Y here if you have a Synaptics RMI4 touchpad connected to
+ to an SMBus, but enumerated through PS/2.
+
+ If unsure, say Y.
+
config MOUSE_PS2_CYPRESS
bool "Cypress PS/2 mouse protocol extension" if EXPERT
default y
If unsure, say N.
+config MOUSE_PS2_SMBUS
+ bool
+ depends on MOUSE_PS2
+
config MOUSE_SERIAL
tristate "Serial mouse"
select SERIO
psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o
psmouse-$(CONFIG_MOUSE_PS2_VMMOUSE) += vmmouse.o
+psmouse-$(CONFIG_MOUSE_PS2_SMBUS) += psmouse-smbus.o
+
elan_i2c-objs := elan_i2c_core.o
elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_I2C) += elan_i2c_i2c.o
elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_SMBUS) += elan_i2c_smbus.o
#define ALPS_DUALPOINT_WITH_PRESSURE 0x400 /* device can report trackpoint pressure */
static const struct alps_model_info alps_model_data[] = {
- { { 0x32, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Toshiba Salellite Pro M10 */
- { { 0x33, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V1, 0x88, 0xf8, 0 } }, /* UMAX-530T */
- { { 0x53, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
- { { 0x53, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
- { { 0x60, 0x03, 0xc8 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, /* HP ze1115 */
- { { 0x63, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
- { { 0x63, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
- { { 0x63, 0x02, 0x28 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Fujitsu Siemens S6010 */
- { { 0x63, 0x02, 0x3c }, 0x00, { ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL } }, /* Toshiba Satellite S2400-103 */
- { { 0x63, 0x02, 0x50 }, 0x00, { ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 } }, /* NEC Versa L320 */
- { { 0x63, 0x02, 0x64 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
- { { 0x63, 0x03, 0xc8 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D800 */
- { { 0x73, 0x00, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT } }, /* ThinkPad R61 8918-5QG */
- { { 0x73, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
- { { 0x73, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Ahtec Laptop */
-
/*
* XXX This entry is suspicious. First byte has zero lower nibble,
* which is what a normal mouse would report. Also, the value 0x0e
* isn't valid per PS/2 spec.
*/
- { { 0x20, 0x02, 0x0e }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
-
- { { 0x22, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
- { { 0x22, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D600 */
- /* Dell Latitude E5500, E6400, E6500, Precision M4400 */
- { { 0x62, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xcf, 0xcf,
- ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } },
- { { 0x73, 0x00, 0x14 }, 0x00, { ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT } }, /* Dell XT2 */
- { { 0x73, 0x02, 0x50 }, 0x00, { ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS } }, /* Dell Vostro 1400 */
- { { 0x52, 0x01, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xff, 0xff,
+ { { 0x20, 0x02, 0x0e }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
+
+ { { 0x22, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
+ { { 0x22, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D600 */
+ { { 0x32, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Toshiba Salellite Pro M10 */
+ { { 0x33, 0x02, 0x0a }, { ALPS_PROTO_V1, 0x88, 0xf8, 0 } }, /* UMAX-530T */
+ { { 0x52, 0x01, 0x14 }, { ALPS_PROTO_V2, 0xff, 0xff,
ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } }, /* Toshiba Tecra A11-11L */
- { { 0x73, 0x02, 0x64 }, 0x8a, { ALPS_PROTO_V4, 0x8f, 0x8f, 0 } },
+ { { 0x53, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+ { { 0x53, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+ { { 0x60, 0x03, 0xc8 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, /* HP ze1115 */
+ { { 0x62, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xcf, 0xcf,
+ ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } }, /* Dell Latitude E5500, E6400, E6500, Precision M4400 */
+ { { 0x63, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+ { { 0x63, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+ { { 0x63, 0x02, 0x28 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Fujitsu Siemens S6010 */
+ { { 0x63, 0x02, 0x3c }, { ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL } }, /* Toshiba Satellite S2400-103 */
+ { { 0x63, 0x02, 0x50 }, { ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 } }, /* NEC Versa L320 */
+ { { 0x63, 0x02, 0x64 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+ { { 0x63, 0x03, 0xc8 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D800 */
+ { { 0x73, 0x00, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT } }, /* ThinkPad R61 8918-5QG */
+ { { 0x73, 0x00, 0x14 }, { ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT } }, /* Dell XT2 */
+ { { 0x73, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+ { { 0x73, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Ahtec Laptop */
+ { { 0x73, 0x02, 0x50 }, { ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS } }, /* Dell Vostro 1400 */
};
static const struct alps_protocol_info alps_v3_protocol_data = {
ALPS_PROTO_V3_RUSHMORE, 0x8f, 0x8f, ALPS_DUALPOINT
};
+static const struct alps_protocol_info alps_v4_protocol_data = {
+ ALPS_PROTO_V4, 0x8f, 0x8f, 0
+};
+
static const struct alps_protocol_info alps_v5_protocol_data = {
ALPS_PROTO_V5, 0xc8, 0xd8, 0
};
ALPS_PROTO_V8, 0x18, 0x18, 0
};
+static const struct alps_protocol_info alps_v9_protocol_data = {
+ ALPS_PROTO_V9, 0xc8, 0xc8, 0
+};
+
/*
* Some v2 models report the stick buttons in separate bits
*/
for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) {
model = &alps_model_data[i];
- if (!memcmp(e7, model->signature, sizeof(model->signature)) &&
- (!model->command_mode_resp ||
- model->command_mode_resp == ec[2])) {
-
+ if (!memcmp(e7, model->signature, sizeof(model->signature)))
return &model->protocol_info;
- }
}
return NULL;
protocol = alps_match_table(e7, ec);
if (!protocol) {
- if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
+ if (e7[0] == 0x73 && e7[1] == 0x02 && e7[2] == 0x64 &&
+ ec[2] == 0x8a) {
+ protocol = &alps_v4_protocol_data;
+ } else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
ec[0] == 0x73 && (ec[1] == 0x01 || ec[1] == 0x02)) {
protocol = &alps_v5_protocol_data;
} else if (ec[0] == 0x88 &&
} else if (e7[0] == 0x73 && e7[1] == 0x03 &&
(e7[2] == 0x14 || e7[2] == 0x28)) {
protocol = &alps_v8_protocol_data;
+ } else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0xc8) {
+ protocol = &alps_v9_protocol_data;
+ psmouse_warn(psmouse,
+ "Unsupported ALPS V9 touchpad: E7=%3ph, EC=%3ph\n",
+ e7, ec);
+ return -EINVAL;
} else {
psmouse_dbg(psmouse,
"Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
#define ALPS_PROTO_V6 0x600
#define ALPS_PROTO_V7 0x700 /* t3btl t4s */
#define ALPS_PROTO_V8 0x800 /* SS4btl SS4s */
+#define ALPS_PROTO_V9 0x900 /* ss3btl */
#define MAX_TOUCHES 4
/**
* struct alps_model_info - touchpad ID table
* @signature: E7 response string to match.
- * @command_mode_resp: For V3/V4 touchpads, the final byte of the EC response
- * (aka command mode response) identifies the firmware minor version. This
- * can be used to distinguish different hardware models which are not
- * uniquely identifiable through their E7 responses.
* @protocol_info: information about protocol used by the device.
*
* Many (but not all) ALPS touchpads can be identified by looking at the
*/
struct alps_model_info {
u8 signature[3];
- u8 command_mode_resp;
struct alps_protocol_info protocol_info;
};
static struct workqueue_struct *kpsmoused_wq;
-struct psmouse_protocol {
- enum psmouse_type type;
- bool maxproto;
- bool ignore_parity; /* Protocol should ignore parity errors from KBC */
- bool try_passthru; /* Try protocol also on passthrough ports */
- const char *name;
- const char *alias;
- int (*detect)(struct psmouse *, bool);
- int (*init)(struct psmouse *);
-};
-
static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons)
{
input_report_key(dev, BTN_LEFT, buttons & BIT(0));
/* Full packet accumulated, process it */
- switch (psmouse->type) {
+ switch (psmouse->protocol->type) {
case PSMOUSE_IMPS:
/* IntelliMouse has scroll wheel */
input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
goto out;
if (unlikely((flags & SERIO_TIMEOUT) ||
- ((flags & SERIO_PARITY) && !psmouse->ignore_parity))) {
+ ((flags & SERIO_PARITY) &&
+ !psmouse->protocol->ignore_parity))) {
if (psmouse->state == PSMOUSE_ACTIVATED)
psmouse_warn(psmouse,
}
if (psmouse->packet[1] == PSMOUSE_RET_ID ||
- (psmouse->type == PSMOUSE_HGPK &&
+ (psmouse->protocol->type == PSMOUSE_HGPK &&
psmouse->packet[1] == PSMOUSE_RET_BAT)) {
__psmouse_set_state(psmouse, PSMOUSE_IGNORE);
serio_reconnect(serio);
.name = "SynPS/2",
.alias = "synaptics",
.detect = synaptics_detect,
- .init = synaptics_init,
+ .init = synaptics_init_absolute,
},
{
.type = PSMOUSE_SYNAPTICS_RELATIVE,
.init = synaptics_init_relative,
},
#endif
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS
+ {
+ .type = PSMOUSE_SYNAPTICS_SMBUS,
+ .name = "SynSMBus",
+ .alias = "synaptics-smbus",
+ .detect = synaptics_detect,
+ .init = synaptics_init_smbus,
+ .smbus_companion = true,
+ },
+#endif
#ifdef CONFIG_MOUSE_PS2_ALPS
{
.type = PSMOUSE_ALPS,
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+ psmouse->protocol = &psmouse_protocols[0];
+
psmouse->set_rate = psmouse_set_rate;
psmouse->set_resolution = psmouse_set_resolution;
psmouse->set_scale = psmouse_set_scale;
psmouse->protocol_handler = psmouse_process_byte;
psmouse->pktsize = 3;
psmouse->reconnect = NULL;
+ psmouse->fast_reconnect = NULL;
psmouse->disconnect = NULL;
psmouse->cleanup = NULL;
psmouse->pt_activate = NULL;
unsigned int max_proto, bool set_properties)
{
bool synaptics_hardware = false;
+ int ret;
/*
* Always check for focaltech, this is safe as it uses pnp-id
* enabled first, since we try detecting Synaptics
* even when protocol is disabled.
*/
- if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) &&
- (!set_properties || synaptics_init(psmouse) == 0)) {
- return PSMOUSE_SYNAPTICS;
+ if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) ||
+ IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)) {
+ if (!set_properties)
+ return PSMOUSE_SYNAPTICS;
+
+ ret = synaptics_init(psmouse);
+ if (ret >= 0)
+ return ret;
}
/*
*/
static void psmouse_disconnect(struct serio *serio)
{
- struct psmouse *psmouse, *parent = NULL;
-
- psmouse = serio_get_drvdata(serio);
+ struct psmouse *psmouse = serio_get_drvdata(serio);
+ struct psmouse *parent = NULL;
sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group);
serio_close(serio);
serio_set_drvdata(serio, NULL);
- input_unregister_device(psmouse->dev);
+
+ if (psmouse->dev)
+ input_unregister_device(psmouse->dev);
+
kfree(psmouse);
if (parent)
{
const struct psmouse_protocol *selected_proto;
struct input_dev *input_dev = psmouse->dev;
+ enum psmouse_type type;
input_dev->dev.parent = &psmouse->ps2dev.serio->dev;
if (proto->init && proto->init(psmouse) < 0)
return -1;
- psmouse->type = proto->type;
selected_proto = proto;
} else {
- psmouse->type = psmouse_extensions(psmouse,
- psmouse_max_proto, true);
- selected_proto = psmouse_protocol_by_type(psmouse->type);
+ type = psmouse_extensions(psmouse, psmouse_max_proto, true);
+ selected_proto = psmouse_protocol_by_type(type);
}
- psmouse->ignore_parity = selected_proto->ignore_parity;
+ psmouse->protocol = selected_proto;
/*
* If mouse's packet size is 3 there is no point in polling the
input_dev->phys = psmouse->phys;
input_dev->id.bustype = BUS_I8042;
input_dev->id.vendor = 0x0002;
- input_dev->id.product = psmouse->type;
+ input_dev->id.product = psmouse->protocol->type;
input_dev->id.version = psmouse->model;
return 0;
psmouse_switch_protocol(psmouse, NULL);
- psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
- psmouse_initialize(psmouse);
+ if (!psmouse->protocol->smbus_companion) {
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+ psmouse_initialize(psmouse);
- error = input_register_device(psmouse->dev);
- if (error)
- goto err_protocol_disconnect;
+ error = input_register_device(input_dev);
+ if (error)
+ goto err_protocol_disconnect;
+ } else {
+ /* Smbus companion will be reporting events, not us. */
+ input_free_device(input_dev);
+ psmouse->dev = input_dev = NULL;
+ }
if (parent && parent->pt_activate)
parent->pt_activate(parent);
if (error)
goto err_pt_deactivate;
- psmouse_activate(psmouse);
+ /*
+ * PS/2 devices having SMBus companions should stay disabled
+ * on PS/2 side, in order to have SMBus part operable.
+ */
+ if (!psmouse->protocol->smbus_companion)
+ psmouse_activate(psmouse);
out:
/* If this is a pass-through port the parent needs to be re-activated */
err_pt_deactivate:
if (parent && parent->pt_deactivate)
parent->pt_deactivate(parent);
- input_unregister_device(psmouse->dev);
- input_dev = NULL; /* so we don't try to free it below */
+ if (input_dev) {
+ input_unregister_device(input_dev);
+ input_dev = NULL; /* so we don't try to free it below */
+ }
err_protocol_disconnect:
if (psmouse->disconnect)
psmouse->disconnect(psmouse);
goto out;
}
-static int psmouse_reconnect(struct serio *serio)
+static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect)
{
struct psmouse *psmouse = serio_get_drvdata(serio);
struct psmouse *parent = NULL;
- unsigned char type;
+ int (*reconnect_handler)(struct psmouse *);
+ enum psmouse_type type;
int rc = -1;
mutex_lock(&psmouse_mutex);
+ if (fast_reconnect) {
+ reconnect_handler = psmouse->fast_reconnect;
+ if (!reconnect_handler) {
+ rc = -ENOENT;
+ goto out_unlock;
+ }
+ } else {
+ reconnect_handler = psmouse->reconnect;
+ }
+
if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
parent = serio_get_drvdata(serio->parent);
psmouse_deactivate(parent);
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
- if (psmouse->reconnect) {
- if (psmouse->reconnect(psmouse))
+ if (reconnect_handler) {
+ if (reconnect_handler(psmouse))
goto out;
} else {
psmouse_reset(psmouse);
goto out;
type = psmouse_extensions(psmouse, psmouse_max_proto, false);
- if (psmouse->type != type)
+ if (psmouse->protocol->type != type)
goto out;
}
* OK, the device type (and capabilities) match the old one,
* we can continue using it, complete initialization
*/
- psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
-
- psmouse_initialize(psmouse);
+ if (!psmouse->protocol->smbus_companion) {
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+ psmouse_initialize(psmouse);
+ }
if (parent && parent->pt_activate)
parent->pt_activate(parent);
- psmouse_activate(psmouse);
+ /*
+ * PS/2 devices having SMBus companions should stay disabled
+ * on PS/2 side, in order to have SMBus part operable.
+ */
+ if (!psmouse->protocol->smbus_companion)
+ psmouse_activate(psmouse);
+
rc = 0;
out:
if (parent)
psmouse_activate(parent);
+out_unlock:
mutex_unlock(&psmouse_mutex);
return rc;
}
+static int psmouse_reconnect(struct serio *serio)
+{
+ return __psmouse_reconnect(serio, false);
+}
+
+static int psmouse_fast_reconnect(struct serio *serio)
+{
+ return __psmouse_reconnect(serio, true);
+}
+
static struct serio_device_id psmouse_serio_ids[] = {
{
.type = SERIO_8042,
.interrupt = psmouse_interrupt,
.connect = psmouse_connect,
.reconnect = psmouse_reconnect,
+ .fast_reconnect = psmouse_fast_reconnect,
.disconnect = psmouse_disconnect,
.cleanup = psmouse_cleanup,
};
{
struct serio *serio = to_serio_port(dev);
struct psmouse_attribute *attr = to_psmouse_attr(devattr);
- struct psmouse *psmouse;
+ struct psmouse *psmouse = serio_get_drvdata(serio);
- psmouse = serio_get_drvdata(serio);
+ if (psmouse->protocol->smbus_companion &&
+ devattr != &psmouse_attr_protocol.dattr)
+ return -ENOENT;
return attr->show(psmouse, attr->data, buf);
}
psmouse = serio_get_drvdata(serio);
+ if (psmouse->protocol->smbus_companion &&
+ devattr != &psmouse_attr_protocol.dattr) {
+ retval = -ENOENT;
+ goto out_unlock;
+ }
+
if (attr->protect) {
if (psmouse->state == PSMOUSE_IGNORE) {
retval = -ENODEV;
psmouse_deactivate(parent);
}
- psmouse_deactivate(psmouse);
+ if (!psmouse->protocol->smbus_companion)
+ psmouse_deactivate(psmouse);
}
retval = attr->set(psmouse, attr->data, buf, count);
if (attr->protect) {
- if (retval != -ENODEV)
+ if (retval != -ENODEV && !psmouse->protocol->smbus_companion)
psmouse_activate(psmouse);
if (parent)
static ssize_t psmouse_attr_show_protocol(struct psmouse *psmouse, void *data, char *buf)
{
- return sprintf(buf, "%s\n", psmouse_protocol_by_type(psmouse->type)->name);
+ return sprintf(buf, "%s\n", psmouse->protocol->name);
}
static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, const char *buf, size_t count)
if (!proto)
return -EINVAL;
- if (psmouse->type == proto->type)
+ if (psmouse->protocol == proto)
return count;
new_dev = input_allocate_device();
return -ENODEV;
}
- if (psmouse->type == proto->type) {
+ if (psmouse->protocol == proto) {
input_free_device(new_dev);
return count; /* switched by other thread */
}
}
old_dev = psmouse->dev;
- old_proto = psmouse_protocol_by_type(psmouse->type);
+ old_proto = psmouse->protocol;
if (psmouse->disconnect)
psmouse->disconnect(psmouse);
psmouse_initialize(psmouse);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
- error = input_register_device(psmouse->dev);
- if (error) {
- if (psmouse->disconnect)
- psmouse->disconnect(psmouse);
+ if (psmouse->protocol->smbus_companion) {
+ input_free_device(psmouse->dev);
+ psmouse->dev = NULL;
+ } else {
+ error = input_register_device(psmouse->dev);
+ if (error) {
+ if (psmouse->disconnect)
+ psmouse->disconnect(psmouse);
- psmouse_set_state(psmouse, PSMOUSE_IGNORE);
- input_free_device(new_dev);
- psmouse->dev = old_dev;
- psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
- psmouse_switch_protocol(psmouse, old_proto);
- psmouse_initialize(psmouse);
- psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+ psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+ input_free_device(new_dev);
+ psmouse->dev = old_dev;
+ psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+ psmouse_switch_protocol(psmouse, old_proto);
+ psmouse_initialize(psmouse);
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
- return error;
+ return error;
+ }
}
- input_unregister_device(old_dev);
+ if (old_dev)
+ input_unregister_device(old_dev);
if (parent && parent->pt_activate)
parent->pt_activate(parent);
synaptics_module_init();
hgpk_module_init();
+ err = psmouse_smbus_module_init();
+ if (err)
+ return err;
+
kpsmoused_wq = alloc_ordered_workqueue("kpsmoused", 0);
if (!kpsmoused_wq) {
pr_err("failed to create kpsmoused workqueue\n");
- return -ENOMEM;
+ err = -ENOMEM;
+ goto err_smbus_exit;
}
err = serio_register_driver(&psmouse_drv);
if (err)
- destroy_workqueue(kpsmoused_wq);
+ goto err_destroy_wq;
+
+ return 0;
+err_destroy_wq:
+ destroy_workqueue(kpsmoused_wq);
+err_smbus_exit:
+ psmouse_smbus_module_exit();
return err;
}
{
serio_unregister_driver(&psmouse_drv);
destroy_workqueue(kpsmoused_wq);
+ psmouse_smbus_module_exit();
}
module_init(psmouse_init);
--- /dev/null
+/*
+ * Copyright (c) 2017 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/libps2.h>
+#include <linux/i2c.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include "psmouse.h"
+
+struct psmouse_smbus_dev {
+ struct i2c_board_info board;
+ struct psmouse *psmouse;
+ struct i2c_client *client;
+ struct list_head node;
+ bool dead;
+};
+
+static LIST_HEAD(psmouse_smbus_list);
+static DEFINE_MUTEX(psmouse_smbus_mutex);
+
+static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
+{
+ struct psmouse_smbus_dev *smbdev;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
+ return;
+
+ mutex_lock(&psmouse_smbus_mutex);
+
+ list_for_each_entry(smbdev, &psmouse_smbus_list, node) {
+ if (smbdev->dead)
+ continue;
+
+ if (smbdev->client)
+ continue;
+
+ /*
+ * Here would be a good place to check if device is actually
+ * present, but it seems that SMBus will not respond unless we
+ * fully reset PS/2 connection. So cross our fingers, and try
+ * to switch over, hopefully our system will not have too many
+ * "host notify" I2C adapters.
+ */
+ psmouse_dbg(smbdev->psmouse,
+ "SMBus candidate adapter appeared, triggering rescan\n");
+ serio_rescan(smbdev->psmouse->ps2dev.serio);
+ }
+
+ mutex_unlock(&psmouse_smbus_mutex);
+}
+
+static void psmouse_smbus_detach_i2c_client(struct i2c_client *client)
+{
+ struct psmouse_smbus_dev *smbdev, *tmp;
+
+ mutex_lock(&psmouse_smbus_mutex);
+
+ list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
+ if (smbdev->client != client)
+ continue;
+
+ kfree(client->dev.platform_data);
+ client->dev.platform_data = NULL;
+
+ if (!smbdev->dead) {
+ psmouse_dbg(smbdev->psmouse,
+ "Marking SMBus companion %s as gone\n",
+ dev_name(&smbdev->client->dev));
+ smbdev->dead = true;
+ serio_rescan(smbdev->psmouse->ps2dev.serio);
+ } else {
+ list_del(&smbdev->node);
+ kfree(smbdev);
+ }
+ }
+
+ mutex_unlock(&psmouse_smbus_mutex);
+}
+
+static int psmouse_smbus_notifier_call(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (dev->type == &i2c_adapter_type)
+ psmouse_smbus_check_adapter(to_i2c_adapter(dev));
+ break;
+
+ case BUS_NOTIFY_REMOVED_DEVICE:
+ if (dev->type != &i2c_adapter_type)
+ psmouse_smbus_detach_i2c_client(to_i2c_client(dev));
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block psmouse_smbus_notifier = {
+ .notifier_call = psmouse_smbus_notifier_call,
+};
+
+static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse)
+{
+ return PSMOUSE_FULL_PACKET;
+}
+
+static int psmouse_smbus_reconnect(struct psmouse *psmouse)
+{
+ psmouse_deactivate(psmouse);
+
+ return 0;
+}
+
+struct psmouse_smbus_removal_work {
+ struct work_struct work;
+ struct i2c_client *client;
+};
+
+static void psmouse_smbus_remove_i2c_device(struct work_struct *work)
+{
+ struct psmouse_smbus_removal_work *rwork =
+ container_of(work, struct psmouse_smbus_removal_work, work);
+
+ dev_dbg(&rwork->client->dev, "destroying SMBus companion device\n");
+ i2c_unregister_device(rwork->client);
+
+ kfree(rwork);
+}
+
+/*
+ * This schedules removal of SMBus companion device. We have to do
+ * it in a separate tread to avoid deadlocking on psmouse_mutex in
+ * case the device has a trackstick (which is also driven by psmouse).
+ *
+ * Note that this may be racing with i2c adapter removal, but we
+ * can't do anything about that: i2c automatically destroys clients
+ * attached to an adapter that is being removed. This has to be
+ * fixed in i2c core.
+ */
+static void psmouse_smbus_schedule_remove(struct i2c_client *client)
+{
+ struct psmouse_smbus_removal_work *rwork;
+
+ rwork = kzalloc(sizeof(*rwork), GFP_KERNEL);
+ if (rwork) {
+ INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device);
+ rwork->client = client;
+
+ schedule_work(&rwork->work);
+ }
+}
+
+static void psmouse_smbus_disconnect(struct psmouse *psmouse)
+{
+ struct psmouse_smbus_dev *smbdev = psmouse->private;
+
+ mutex_lock(&psmouse_smbus_mutex);
+
+ if (smbdev->dead) {
+ list_del(&smbdev->node);
+ kfree(smbdev);
+ } else {
+ smbdev->dead = true;
+ psmouse_dbg(smbdev->psmouse,
+ "posting removal request for SMBus companion %s\n",
+ dev_name(&smbdev->client->dev));
+ psmouse_smbus_schedule_remove(smbdev->client);
+ }
+
+ mutex_unlock(&psmouse_smbus_mutex);
+
+ psmouse->private = NULL;
+}
+
+static int psmouse_smbus_create_companion(struct device *dev, void *data)
+{
+ struct psmouse_smbus_dev *smbdev = data;
+ unsigned short addr_list[] = { smbdev->board.addr, I2C_CLIENT_END };
+ struct i2c_adapter *adapter;
+
+ adapter = i2c_verify_adapter(dev);
+ if (!adapter)
+ return 0;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
+ return 0;
+
+ smbdev->client = i2c_new_probed_device(adapter, &smbdev->board,
+ addr_list, NULL);
+ if (!smbdev->client)
+ return 0;
+
+ /* We have our(?) device, stop iterating i2c bus. */
+ return 1;
+}
+
+void psmouse_smbus_cleanup(struct psmouse *psmouse)
+{
+ struct psmouse_smbus_dev *smbdev, *tmp;
+
+ mutex_lock(&psmouse_smbus_mutex);
+
+ list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
+ if (psmouse == smbdev->psmouse) {
+ list_del(&smbdev->node);
+ kfree(smbdev);
+ }
+ }
+
+ mutex_unlock(&psmouse_smbus_mutex);
+}
+
+int psmouse_smbus_init(struct psmouse *psmouse,
+ const struct i2c_board_info *board,
+ const void *pdata, size_t pdata_size,
+ bool leave_breadcrumbs)
+{
+ struct psmouse_smbus_dev *smbdev;
+ int error;
+
+ smbdev = kzalloc(sizeof(*smbdev), GFP_KERNEL);
+ if (!smbdev)
+ return -ENOMEM;
+
+ smbdev->psmouse = psmouse;
+ smbdev->board = *board;
+
+ smbdev->board.platform_data = kmemdup(pdata, pdata_size, GFP_KERNEL);
+ if (!smbdev->board.platform_data) {
+ kfree(smbdev);
+ return -ENOMEM;
+ }
+
+ psmouse->private = smbdev;
+ psmouse->protocol_handler = psmouse_smbus_process_byte;
+ psmouse->reconnect = psmouse_smbus_reconnect;
+ psmouse->fast_reconnect = psmouse_smbus_reconnect;
+ psmouse->disconnect = psmouse_smbus_disconnect;
+ psmouse->resync_time = 0;
+
+ psmouse_deactivate(psmouse);
+
+ mutex_lock(&psmouse_smbus_mutex);
+ list_add_tail(&smbdev->node, &psmouse_smbus_list);
+ mutex_unlock(&psmouse_smbus_mutex);
+
+ /* Bind to already existing adapters right away */
+ error = i2c_for_each_dev(smbdev, psmouse_smbus_create_companion);
+
+ if (smbdev->client) {
+ /* We have our companion device */
+ return 0;
+ }
+
+ /*
+ * If we did not create i2c device we will not need platform
+ * data even if we are leaving breadcrumbs.
+ */
+ kfree(smbdev->board.platform_data);
+ smbdev->board.platform_data = NULL;
+
+ if (error < 0 || !leave_breadcrumbs) {
+ mutex_lock(&psmouse_smbus_mutex);
+ list_del(&smbdev->node);
+ mutex_unlock(&psmouse_smbus_mutex);
+
+ kfree(smbdev);
+ }
+
+ return error < 0 ? error : -EAGAIN;
+}
+
+int __init psmouse_smbus_module_init(void)
+{
+ int error;
+
+ error = bus_register_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
+ if (error) {
+ pr_err("failed to register i2c bus notifier: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+void psmouse_smbus_module_exit(void)
+{
+ bus_unregister_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
+ flush_scheduled_work();
+}
PSMOUSE_SCALE21
};
+enum psmouse_type {
+ PSMOUSE_NONE,
+ PSMOUSE_PS2,
+ PSMOUSE_PS2PP,
+ PSMOUSE_THINKPS,
+ PSMOUSE_GENPS,
+ PSMOUSE_IMPS,
+ PSMOUSE_IMEX,
+ PSMOUSE_SYNAPTICS,
+ PSMOUSE_ALPS,
+ PSMOUSE_LIFEBOOK,
+ PSMOUSE_TRACKPOINT,
+ PSMOUSE_TOUCHKIT_PS2,
+ PSMOUSE_CORTRON,
+ PSMOUSE_HGPK,
+ PSMOUSE_ELANTECH,
+ PSMOUSE_FSP,
+ PSMOUSE_SYNAPTICS_RELATIVE,
+ PSMOUSE_CYPRESS,
+ PSMOUSE_FOCALTECH,
+ PSMOUSE_VMMOUSE,
+ PSMOUSE_BYD,
+ PSMOUSE_SYNAPTICS_SMBUS,
+ PSMOUSE_AUTO /* This one should always be last */
+};
+
+struct psmouse;
+
+struct psmouse_protocol {
+ enum psmouse_type type;
+ bool maxproto;
+ bool ignore_parity; /* Protocol should ignore parity errors from KBC */
+ bool try_passthru; /* Try protocol also on passthrough ports */
+ bool smbus_companion; /* "Protocol" is a stub, device is on SMBus */
+ const char *name;
+ const char *alias;
+ int (*detect)(struct psmouse *, bool);
+ int (*init)(struct psmouse *);
+};
+
struct psmouse {
void *private;
struct input_dev *dev;
struct ps2dev ps2dev;
struct delayed_work resync_work;
- char *vendor;
- char *name;
+ const char *vendor;
+ const char *name;
+ const struct psmouse_protocol *protocol;
unsigned char packet[8];
unsigned char badbyte;
unsigned char pktcnt;
unsigned char pktsize;
- unsigned char type;
unsigned char oob_data_type;
unsigned char extra_buttons;
- bool ignore_parity;
bool acks_disable_command;
unsigned int model;
unsigned long last;
void (*set_scale)(struct psmouse *psmouse, enum psmouse_scale scale);
int (*reconnect)(struct psmouse *psmouse);
+ int (*fast_reconnect)(struct psmouse *psmouse);
void (*disconnect)(struct psmouse *psmouse);
void (*cleanup)(struct psmouse *psmouse);
int (*poll)(struct psmouse *psmouse);
void (*pt_deactivate)(struct psmouse *psmouse);
};
-enum psmouse_type {
- PSMOUSE_NONE,
- PSMOUSE_PS2,
- PSMOUSE_PS2PP,
- PSMOUSE_THINKPS,
- PSMOUSE_GENPS,
- PSMOUSE_IMPS,
- PSMOUSE_IMEX,
- PSMOUSE_SYNAPTICS,
- PSMOUSE_ALPS,
- PSMOUSE_LIFEBOOK,
- PSMOUSE_TRACKPOINT,
- PSMOUSE_TOUCHKIT_PS2,
- PSMOUSE_CORTRON,
- PSMOUSE_HGPK,
- PSMOUSE_ELANTECH,
- PSMOUSE_FSP,
- PSMOUSE_SYNAPTICS_RELATIVE,
- PSMOUSE_CYPRESS,
- PSMOUSE_FOCALTECH,
- PSMOUSE_VMMOUSE,
- PSMOUSE_BYD,
- PSMOUSE_AUTO /* This one should always be last */
-};
-
void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
unsigned long delay);
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
&(psmouse)->ps2dev.serio->dev, \
psmouse_fmt(format), ##__VA_ARGS__)
+#ifdef CONFIG_MOUSE_PS2_SMBUS
+
+int psmouse_smbus_module_init(void);
+void psmouse_smbus_module_exit(void);
+
+struct i2c_board_info;
+
+int psmouse_smbus_init(struct psmouse *psmouse,
+ const struct i2c_board_info *board,
+ const void *pdata, size_t pdata_size,
+ bool leave_breadcrumbs);
+void psmouse_smbus_cleanup(struct psmouse *psmouse);
+
+#else /* !CONFIG_MOUSE_PS2_SMBUS */
+
+static inline int psmouse_smbus_module_init(void)
+{
+ return 0;
+}
+
+static inline void psmouse_smbus_module_exit(void)
+{
+}
+
+static inline void psmouse_smbus_cleanup(struct psmouse *psmouse)
+{
+}
+
+#endif /* CONFIG_MOUSE_PS2_SMBUS */
#endif /* _PSMOUSE_H */
#include <linux/input/mt.h>
#include <linux/serio.h>
#include <linux/libps2.h>
+#include <linux/rmi.h>
+#include <linux/i2c.h>
#include <linux/slab.h>
#include "psmouse.h"
#include "synaptics.h"
synaptics_mode_cmd(psmouse, 0);
}
-#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
-
-static bool cr48_profile_sensor;
-
-#define ANY_BOARD_ID 0
-struct min_max_quirk {
- const char * const *pnp_ids;
- struct {
- unsigned long int min, max;
- } board_id;
- int x_min, x_max, y_min, y_max;
-};
-
-static const struct min_max_quirk min_max_pnpid_table[] = {
- {
- (const char * const []){"LEN0033", NULL},
- {ANY_BOARD_ID, ANY_BOARD_ID},
- 1024, 5052, 2258, 4832
- },
- {
- (const char * const []){"LEN0042", NULL},
- {ANY_BOARD_ID, ANY_BOARD_ID},
- 1232, 5710, 1156, 4696
- },
- {
- (const char * const []){"LEN0034", "LEN0036", "LEN0037",
- "LEN0039", "LEN2002", "LEN2004",
- NULL},
- {ANY_BOARD_ID, 2961},
- 1024, 5112, 2024, 4832
- },
- {
- (const char * const []){"LEN2000", NULL},
- {ANY_BOARD_ID, ANY_BOARD_ID},
- 1024, 5113, 2021, 4832
- },
- {
- (const char * const []){"LEN2001", NULL},
- {ANY_BOARD_ID, ANY_BOARD_ID},
- 1024, 5022, 2508, 4832
- },
- {
- (const char * const []){"LEN2006", NULL},
- {2691, 2691},
- 1024, 5045, 2457, 4832
- },
- {
- (const char * const []){"LEN2006", NULL},
- {ANY_BOARD_ID, ANY_BOARD_ID},
- 1264, 5675, 1171, 4688
- },
- { }
-};
+#if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \
+ defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)
/* This list has been kindly provided by Synaptics. */
static const char * const topbuttonpad_pnp_ids[] = {
NULL
};
-/* This list has been kindly provided by Synaptics. */
-static const char * const forcepad_pnp_ids[] = {
- "SYN300D",
- "SYN3014",
+static const char * const smbus_pnp_ids[] = {
+ /* all of the topbuttonpad_pnp_ids are valid, we just add some extras */
+ "LEN0048", /* X1 Carbon 3 */
+ "LEN0046", /* X250 */
+ "LEN004a", /* W541 */
+ "LEN200f", /* T450s */
NULL
};
-/*****************************************************************************
- * Synaptics communications functions
- ****************************************************************************/
-
/*
- * Synaptics touchpads report the y coordinate from bottom to top, which is
- * opposite from what userspace expects.
- * This function is used to invert y before reporting.
+ * Send a command to the synpatics touchpad by special commands
*/
-static int synaptics_invert_y(int y)
+static int synaptics_send_cmd(struct psmouse *psmouse,
+ unsigned char c, unsigned char *param)
{
- return YMAX_NOMINAL + YMIN_NOMINAL - y;
+ int error;
+
+ error = psmouse_sliced_command(psmouse, c);
+ if (error)
+ return error;
+
+ error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO);
+ if (error)
+ return error;
+
+ return 0;
}
/*
- * Send a command to the synpatics touchpad by special commands
+ * Identify Touchpad
+ * See also the SYN_ID_* macros
*/
-static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param)
+static int synaptics_identify(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
- if (psmouse_sliced_command(psmouse, c))
- return -1;
- if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
- return -1;
- return 0;
+ unsigned char id[3];
+ int error;
+
+ error = synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id);
+ if (error)
+ return error;
+
+ info->identity = (id[0] << 16) | (id[1] << 8) | id[2];
+ return SYN_ID_IS_SYNAPTICS(info->identity) ? 0 : -ENXIO;
}
/*
* Read the model-id bytes from the touchpad
* see also SYN_MODEL_* macros
*/
-static int synaptics_model_id(struct psmouse *psmouse)
+static int synaptics_model_id(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
- struct synaptics_data *priv = psmouse->private;
unsigned char mi[3];
+ int error;
- if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi))
- return -1;
- priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2];
+ error = synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi);
+ if (error)
+ return error;
+
+ info->model_id = (mi[0] << 16) | (mi[1] << 8) | mi[2];
return 0;
}
-static int synaptics_more_extended_queries(struct psmouse *psmouse)
+/*
+ * Read the firmware id from the touchpad
+ */
+static int synaptics_firmware_id(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
- struct synaptics_data *priv = psmouse->private;
- unsigned char buf[3];
+ unsigned char fwid[3];
+ int error;
- if (synaptics_send_cmd(psmouse, SYN_QUE_MEXT_CAPAB_10, buf))
- return -1;
+ error = synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid);
+ if (error)
+ return error;
+
+ info->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2];
+ return 0;
+}
+
+static int synaptics_more_extended_queries(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
+{
+ unsigned char buf[3];
+ int error;
- priv->ext_cap_10 = (buf[0]<<16) | (buf[1]<<8) | buf[2];
+ error = synaptics_send_cmd(psmouse, SYN_QUE_MEXT_CAPAB_10, buf);
+ if (error)
+ return error;
+ info->ext_cap_10 = (buf[0] << 16) | (buf[1] << 8) | buf[2];
return 0;
}
* Read the board id and the "More Extended Queries" from the touchpad
* The board id is encoded in the "QUERY MODES" response
*/
-static int synaptics_query_modes(struct psmouse *psmouse)
+static int synaptics_query_modes(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
- struct synaptics_data *priv = psmouse->private;
unsigned char bid[3];
+ int error;
/* firmwares prior 7.5 have no board_id encoded */
- if (SYN_ID_FULL(priv->identity) < 0x705)
+ if (SYN_ID_FULL(info->identity) < 0x705)
return 0;
- if (synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid))
- return -1;
- priv->board_id = ((bid[0] & 0xfc) << 6) | bid[1];
+ error = synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid);
+ if (error)
+ return error;
- if (SYN_MEXT_CAP_BIT(bid[0]))
- return synaptics_more_extended_queries(psmouse);
+ info->board_id = ((bid[0] & 0xfc) << 6) | bid[1];
- return 0;
-}
-
-/*
- * Read the firmware id from the touchpad
- */
-static int synaptics_firmware_id(struct psmouse *psmouse)
-{
- struct synaptics_data *priv = psmouse->private;
- unsigned char fwid[3];
+ if (SYN_MEXT_CAP_BIT(bid[0]))
+ return synaptics_more_extended_queries(psmouse, info);
- if (synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid))
- return -1;
- priv->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2];
return 0;
}
* Read the capability-bits from the touchpad
* see also the SYN_CAP_* macros
*/
-static int synaptics_capability(struct psmouse *psmouse)
+static int synaptics_capability(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
- struct synaptics_data *priv = psmouse->private;
unsigned char cap[3];
+ int error;
- if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap))
- return -1;
- priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
- priv->ext_cap = priv->ext_cap_0c = 0;
+ error = synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap);
+ if (error)
+ return error;
+
+ info->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+ info->ext_cap = info->ext_cap_0c = 0;
/*
* Older firmwares had submodel ID fixed to 0x47
*/
- if (SYN_ID_FULL(priv->identity) < 0x705 &&
- SYN_CAP_SUBMODEL_ID(priv->capabilities) != 0x47) {
- return -1;
+ if (SYN_ID_FULL(info->identity) < 0x705 &&
+ SYN_CAP_SUBMODEL_ID(info->capabilities) != 0x47) {
+ return -ENXIO;
}
/*
* Unless capExtended is set the rest of the flags should be ignored
*/
- if (!SYN_CAP_EXTENDED(priv->capabilities))
- priv->capabilities = 0;
+ if (!SYN_CAP_EXTENDED(info->capabilities))
+ info->capabilities = 0;
- if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) {
+ if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 1) {
if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) {
psmouse_warn(psmouse,
"device claims to have extended capabilities, but I'm not able to read them.\n");
} else {
- priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+ info->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
/*
* if nExtBtn is greater than 8 it should be considered
* invalid and treated as 0
*/
- if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8)
- priv->ext_cap &= 0xff0fff;
+ if (SYN_CAP_MULTI_BUTTON_NO(info->ext_cap) > 8)
+ info->ext_cap &= 0xff0fff;
}
}
- if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 4) {
- if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap)) {
+ if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 4) {
+ error = synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap);
+ if (error)
psmouse_warn(psmouse,
"device claims to have extended capability 0x0c, but I'm not able to read it.\n");
- } else {
- priv->ext_cap_0c = (cap[0] << 16) | (cap[1] << 8) | cap[2];
- }
+ else
+ info->ext_cap_0c =
+ (cap[0] << 16) | (cap[1] << 8) | cap[2];
}
return 0;
}
-/*
- * Identify Touchpad
- * See also the SYN_ID_* macros
- */
-static int synaptics_identify(struct psmouse *psmouse)
-{
- struct synaptics_data *priv = psmouse->private;
- unsigned char id[3];
-
- if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id))
- return -1;
- priv->identity = (id[0]<<16) | (id[1]<<8) | id[2];
- if (SYN_ID_IS_SYNAPTICS(priv->identity))
- return 0;
- return -1;
-}
-
/*
* Read touchpad resolution and maximum reported coordinates
* Resolution is left zero if touchpad does not support the query
*/
-
-static int synaptics_resolution(struct psmouse *psmouse)
+static int synaptics_resolution(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
- struct synaptics_data *priv = psmouse->private;
unsigned char resp[3];
+ int error;
- if (SYN_ID_MAJOR(priv->identity) < 4)
+ if (SYN_ID_MAJOR(info->identity) < 4)
return 0;
- if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp) == 0) {
+ error = synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp);
+ if (!error) {
if (resp[0] != 0 && (resp[1] & 0x80) && resp[2] != 0) {
- priv->x_res = resp[0]; /* x resolution in units/mm */
- priv->y_res = resp[2]; /* y resolution in units/mm */
+ info->x_res = resp[0]; /* x resolution in units/mm */
+ info->y_res = resp[2]; /* y resolution in units/mm */
}
}
- if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 &&
- SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) {
- if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MAX_COORDS, resp)) {
+ if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 5 &&
+ SYN_CAP_MAX_DIMENSIONS(info->ext_cap_0c)) {
+ error = synaptics_send_cmd(psmouse,
+ SYN_QUE_EXT_MAX_COORDS, resp);
+ if (error) {
psmouse_warn(psmouse,
"device claims to have max coordinates query, but I'm not able to read it.\n");
} else {
- priv->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
- priv->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
+ info->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
+ info->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
psmouse_info(psmouse,
"queried max coordinates: x [..%d], y [..%d]\n",
- priv->x_max, priv->y_max);
+ info->x_max, info->y_max);
}
}
- if (SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c) &&
- (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 ||
+ if (SYN_CAP_MIN_DIMENSIONS(info->ext_cap_0c) &&
+ (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 7 ||
/*
* Firmware v8.1 does not report proper number of extended
* capabilities, but has been proven to report correct min
* coordinates.
*/
- SYN_ID_FULL(priv->identity) == 0x801)) {
- if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MIN_COORDS, resp)) {
+ SYN_ID_FULL(info->identity) == 0x801)) {
+ error = synaptics_send_cmd(psmouse,
+ SYN_QUE_EXT_MIN_COORDS, resp);
+ if (error) {
psmouse_warn(psmouse,
"device claims to have min coordinates query, but I'm not able to read it.\n");
} else {
- priv->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
- priv->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
+ info->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
+ info->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
psmouse_info(psmouse,
"queried min coordinates: x [%d..], y [%d..]\n",
- priv->x_min, priv->y_min);
+ info->x_min, info->y_min);
}
}
return 0;
}
+static int synaptics_query_hardware(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
+{
+ int error;
+
+ error = synaptics_identify(psmouse, info);
+ if (error)
+ return error;
+
+ error = synaptics_model_id(psmouse, info);
+ if (error)
+ return error;
+
+ error = synaptics_firmware_id(psmouse, info);
+ if (error)
+ return error;
+
+ error = synaptics_query_modes(psmouse, info);
+ if (error)
+ return error;
+
+ error = synaptics_capability(psmouse, info);
+ if (error)
+ return error;
+
+ error = synaptics_resolution(psmouse, info);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+#endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
+
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
+
+static bool cr48_profile_sensor;
+
+#define ANY_BOARD_ID 0
+struct min_max_quirk {
+ const char * const *pnp_ids;
+ struct {
+ u32 min, max;
+ } board_id;
+ u32 x_min, x_max, y_min, y_max;
+};
+
+static const struct min_max_quirk min_max_pnpid_table[] = {
+ {
+ (const char * const []){"LEN0033", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
+ 1024, 5052, 2258, 4832
+ },
+ {
+ (const char * const []){"LEN0042", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
+ 1232, 5710, 1156, 4696
+ },
+ {
+ (const char * const []){"LEN0034", "LEN0036", "LEN0037",
+ "LEN0039", "LEN2002", "LEN2004",
+ NULL},
+ {ANY_BOARD_ID, 2961},
+ 1024, 5112, 2024, 4832
+ },
+ {
+ (const char * const []){"LEN2000", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
+ 1024, 5113, 2021, 4832
+ },
+ {
+ (const char * const []){"LEN2001", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
+ 1024, 5022, 2508, 4832
+ },
+ {
+ (const char * const []){"LEN2006", NULL},
+ {2691, 2691},
+ 1024, 5045, 2457, 4832
+ },
+ {
+ (const char * const []){"LEN2006", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
+ 1264, 5675, 1171, 4688
+ },
+ { }
+};
+
+/* This list has been kindly provided by Synaptics. */
+static const char * const forcepad_pnp_ids[] = {
+ "SYN300D",
+ "SYN3014",
+ NULL
+};
+
+/*****************************************************************************
+ * Synaptics communications functions
+ ****************************************************************************/
+
+/*
+ * Synaptics touchpads report the y coordinate from bottom to top, which is
+ * opposite from what userspace expects.
+ * This function is used to invert y before reporting.
+ */
+static int synaptics_invert_y(int y)
+{
+ return YMAX_NOMINAL + YMIN_NOMINAL - y;
+}
+
/*
* Apply quirk(s) if the hardware matches
*/
-
-static void synaptics_apply_quirks(struct psmouse *psmouse)
+static void synaptics_apply_quirks(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
- struct synaptics_data *priv = psmouse->private;
int i;
for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) {
continue;
if (min_max_pnpid_table[i].board_id.min != ANY_BOARD_ID &&
- priv->board_id < min_max_pnpid_table[i].board_id.min)
+ info->board_id < min_max_pnpid_table[i].board_id.min)
continue;
if (min_max_pnpid_table[i].board_id.max != ANY_BOARD_ID &&
- priv->board_id > min_max_pnpid_table[i].board_id.max)
+ info->board_id > min_max_pnpid_table[i].board_id.max)
continue;
- priv->x_min = min_max_pnpid_table[i].x_min;
- priv->x_max = min_max_pnpid_table[i].x_max;
- priv->y_min = min_max_pnpid_table[i].y_min;
- priv->y_max = min_max_pnpid_table[i].y_max;
+ info->x_min = min_max_pnpid_table[i].x_min;
+ info->x_max = min_max_pnpid_table[i].x_max;
+ info->y_min = min_max_pnpid_table[i].y_min;
+ info->y_max = min_max_pnpid_table[i].y_max;
psmouse_info(psmouse,
"quirked min/max coordinates: x [%d..%d], y [%d..%d]\n",
- priv->x_min, priv->x_max,
- priv->y_min, priv->y_max);
+ info->x_min, info->x_max,
+ info->y_min, info->y_max);
break;
}
}
-static int synaptics_query_hardware(struct psmouse *psmouse)
-{
- if (synaptics_identify(psmouse))
- return -1;
- if (synaptics_model_id(psmouse))
- return -1;
- if (synaptics_firmware_id(psmouse))
- return -1;
- if (synaptics_query_modes(psmouse))
- return -1;
- if (synaptics_capability(psmouse))
- return -1;
- if (synaptics_resolution(psmouse))
- return -1;
-
- synaptics_apply_quirks(psmouse);
-
- return 0;
-}
-
static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
{
static unsigned char param = 0xc8;
struct synaptics_data *priv = psmouse->private;
- if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
- SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)))
+ if (!(SYN_CAP_ADV_GESTURE(priv->info.ext_cap_0c) ||
+ SYN_CAP_IMAGE_SENSOR(priv->info.ext_cap_0c)))
return 0;
if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
return -1;
/* Advanced gesture mode also sends multi finger data */
- priv->capabilities |= BIT(1);
+ priv->info.capabilities |= BIT(1);
return 0;
}
priv->mode |= SYN_BIT_DISABLE_GESTURE;
if (psmouse->rate >= 80)
priv->mode |= SYN_BIT_HIGH_RATE;
- if (SYN_CAP_EXTENDED(priv->capabilities))
+ if (SYN_CAP_EXTENDED(priv->info.capabilities))
priv->mode |= SYN_BIT_W_MODE;
if (synaptics_mode_cmd(psmouse, priv->mode))
struct synaptics_hw_state *hw)
{
unsigned int ext_bits =
- (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1;
+ (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) + 1) >> 1;
unsigned int ext_mask = GENMASK(ext_bits - 1, 0);
hw->ext_buttons = buf[4] & ext_mask;
{
memset(hw, 0, sizeof(struct synaptics_hw_state));
- if (SYN_MODEL_NEWABS(priv->model_id)) {
+ if (SYN_MODEL_NEWABS(priv->info.model_id)) {
hw->w = (((buf[0] & 0x30) >> 2) |
((buf[0] & 0x04) >> 1) |
((buf[3] & 0x04) >> 2));
- if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
- SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) &&
+ if ((SYN_CAP_ADV_GESTURE(priv->info.ext_cap_0c) ||
+ SYN_CAP_IMAGE_SENSOR(priv->info.ext_cap_0c)) &&
hw->w == 2) {
synaptics_parse_agm(buf, priv, hw);
return 1;
hw->left = priv->report_press;
- } else if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
+ } else if (SYN_CAP_CLICKPAD(priv->info.ext_cap_0c)) {
/*
* Clickpad's button is transmitted as middle button,
* however, since it is primary button, we will report
*/
hw->left = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
- } else if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
+ } else if (SYN_CAP_MIDDLE_BUTTON(priv->info.capabilities)) {
hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
if (hw->w == 2)
hw->scroll = (signed char)(buf[1]);
}
- if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+ if (SYN_CAP_FOUR_BUTTON(priv->info.capabilities)) {
hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
}
- if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 0 &&
+ if (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) > 0 &&
((buf[0] ^ buf[3]) & 0x02)) {
synaptics_parse_ext_buttons(buf, priv, hw);
}
{
struct input_dev *dev = psmouse->dev;
struct synaptics_data *priv = psmouse->private;
- int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1;
+ int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) + 1) >> 1;
int i;
- if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
+ if (!SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap))
return;
/* Bug in FW 8.1 & 8.2, buttons are reported only when ExtBit is 1 */
- if ((SYN_ID_FULL(priv->identity) == 0x801 ||
- SYN_ID_FULL(priv->identity) == 0x802) &&
+ if ((SYN_ID_FULL(priv->info.identity) == 0x801 ||
+ SYN_ID_FULL(priv->info.identity) == 0x802) &&
!((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02))
return;
- if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) {
+ if (!SYN_CAP_EXT_BUTTONS_STICK(priv->info.ext_cap_10)) {
for (i = 0; i < ext_bits; i++) {
input_report_key(dev, BTN_0 + 2 * i,
hw->ext_buttons & (1 << i));
u8 pt_buttons;
/* The trackstick expects at most 3 buttons */
- pt_buttons = SYN_CAP_EXT_BUTTON_STICK_L(hw->ext_buttons) |
- SYN_CAP_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 |
- SYN_CAP_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2;
+ pt_buttons = SYN_EXT_BUTTON_STICK_L(hw->ext_buttons) |
+ SYN_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 |
+ SYN_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2;
serio_interrupt(priv->pt_port,
PSMOUSE_OOB_EXTRA_BTNS, SERIO_OOB_DATA);
input_report_key(dev, BTN_LEFT, hw->left);
input_report_key(dev, BTN_RIGHT, hw->right);
- if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+ if (SYN_CAP_MIDDLE_BUTTON(priv->info.capabilities))
input_report_key(dev, BTN_MIDDLE, hw->middle);
- if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+ if (SYN_CAP_FOUR_BUTTON(priv->info.capabilities)) {
input_report_key(dev, BTN_FORWARD, hw->up);
input_report_key(dev, BTN_BACK, hw->down);
}
pos[i].y = synaptics_invert_y(hw[i]->y);
}
- input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res);
+ input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->info.x_res);
for (i = 0; i < nsemi; i++) {
input_mt_slot(dev, slot[i]);
{
struct input_dev *dev = psmouse->dev;
struct synaptics_data *priv = psmouse->private;
+ struct synaptics_device_info *info = &priv->info;
struct synaptics_hw_state hw;
int num_fingers;
int finger_width;
if (synaptics_parse_hw_state(psmouse->packet, priv, &hw))
return;
- if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+ if (SYN_CAP_IMAGE_SENSOR(info->ext_cap_0c)) {
synaptics_image_sensor_process(psmouse, &hw);
return;
}
if (hw.z > 0 && hw.x > 1) {
num_fingers = 1;
finger_width = 5;
- if (SYN_CAP_EXTENDED(priv->capabilities)) {
+ if (SYN_CAP_EXTENDED(info->capabilities)) {
switch (hw.w) {
case 0 ... 1:
- if (SYN_CAP_MULTIFINGER(priv->capabilities))
+ if (SYN_CAP_MULTIFINGER(info->capabilities))
num_fingers = hw.w + 2;
break;
case 2:
- if (SYN_MODEL_PEN(priv->model_id))
+ if (SYN_MODEL_PEN(info->model_id))
; /* Nothing, treat a pen as a single finger */
break;
case 4 ... 15:
- if (SYN_CAP_PALMDETECT(priv->capabilities))
+ if (SYN_CAP_PALMDETECT(info->capabilities))
finger_width = hw.w;
break;
}
return;
}
- if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
+ if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c))
synaptics_report_semi_mt_data(dev, &hw, &priv->agm,
num_fingers);
}
input_report_abs(dev, ABS_PRESSURE, hw.z);
- if (SYN_CAP_PALMDETECT(priv->capabilities))
+ if (SYN_CAP_PALMDETECT(info->capabilities))
input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
- if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
+ if (SYN_CAP_MULTIFINGER(info->capabilities)) {
input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
}
if (unlikely(priv->pkt_type == SYN_NEWABS))
priv->pkt_type = synaptics_detect_pkt_type(psmouse);
- if (SYN_CAP_PASS_THROUGH(priv->capabilities) &&
+ if (SYN_CAP_PASS_THROUGH(priv->info.capabilities) &&
synaptics_is_pt_packet(psmouse->packet)) {
if (priv->pt_port)
synaptics_pass_pt_packet(priv->pt_port,
* Driver initialization/cleanup functions
****************************************************************************/
static void set_abs_position_params(struct input_dev *dev,
- struct synaptics_data *priv, int x_code,
- int y_code)
+ struct synaptics_device_info *info,
+ int x_code, int y_code)
{
- int x_min = priv->x_min ?: XMIN_NOMINAL;
- int x_max = priv->x_max ?: XMAX_NOMINAL;
- int y_min = priv->y_min ?: YMIN_NOMINAL;
- int y_max = priv->y_max ?: YMAX_NOMINAL;
- int fuzz = SYN_CAP_REDUCED_FILTERING(priv->ext_cap_0c) ?
+ int x_min = info->x_min ?: XMIN_NOMINAL;
+ int x_max = info->x_max ?: XMAX_NOMINAL;
+ int y_min = info->y_min ?: YMIN_NOMINAL;
+ int y_max = info->y_max ?: YMAX_NOMINAL;
+ int fuzz = SYN_CAP_REDUCED_FILTERING(info->ext_cap_0c) ?
SYN_REDUCED_FILTER_FUZZ : 0;
input_set_abs_params(dev, x_code, x_min, x_max, fuzz, 0);
input_set_abs_params(dev, y_code, y_min, y_max, fuzz, 0);
- input_abs_set_res(dev, x_code, priv->x_res);
- input_abs_set_res(dev, y_code, priv->y_res);
+ input_abs_set_res(dev, x_code, info->x_res);
+ input_abs_set_res(dev, y_code, info->y_res);
}
static void set_input_params(struct psmouse *psmouse,
struct synaptics_data *priv)
{
struct input_dev *dev = psmouse->dev;
+ struct synaptics_device_info *info = &priv->info;
int i;
/* Things that apply to both modes */
__set_bit(BTN_LEFT, dev->keybit);
__set_bit(BTN_RIGHT, dev->keybit);
- if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+ if (SYN_CAP_MIDDLE_BUTTON(info->capabilities))
__set_bit(BTN_MIDDLE, dev->keybit);
if (!priv->absolute_mode) {
/* Absolute mode */
__set_bit(EV_ABS, dev->evbit);
- set_abs_position_params(dev, priv, ABS_X, ABS_Y);
+ set_abs_position_params(dev, &priv->info, ABS_X, ABS_Y);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
if (cr48_profile_sensor)
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
- if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
- set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
- ABS_MT_POSITION_Y);
+ if (SYN_CAP_IMAGE_SENSOR(info->ext_cap_0c)) {
+ set_abs_position_params(dev, info,
+ ABS_MT_POSITION_X, ABS_MT_POSITION_Y);
/* Image sensors can report per-contact pressure */
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK);
/* Image sensors can signal 4 and 5 finger clicks */
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
__set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
- } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
- set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
- ABS_MT_POSITION_Y);
+ } else if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c)) {
+ set_abs_position_params(dev, info,
+ ABS_MT_POSITION_X, ABS_MT_POSITION_Y);
/*
* Profile sensor in CR-48 tracks contacts reasonably well,
* other non-image sensors with AGM use semi-mt.
INPUT_MT_TRACK : INPUT_MT_SEMI_MT));
}
- if (SYN_CAP_PALMDETECT(priv->capabilities))
+ if (SYN_CAP_PALMDETECT(info->capabilities))
input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
__set_bit(BTN_TOUCH, dev->keybit);
__set_bit(BTN_TOOL_FINGER, dev->keybit);
- if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
+ if (SYN_CAP_MULTIFINGER(info->capabilities)) {
__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
}
- if (SYN_CAP_FOUR_BUTTON(priv->capabilities) ||
- SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
+ if (SYN_CAP_FOUR_BUTTON(info->capabilities) ||
+ SYN_CAP_MIDDLE_BUTTON(info->capabilities)) {
__set_bit(BTN_FORWARD, dev->keybit);
__set_bit(BTN_BACK, dev->keybit);
}
- if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10))
- for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
+ if (!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
+ for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(info->ext_cap); i++)
__set_bit(BTN_0 + i, dev->keybit);
__clear_bit(EV_REL, dev->evbit);
__clear_bit(REL_X, dev->relbit);
__clear_bit(REL_Y, dev->relbit);
- if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
+ if (SYN_CAP_CLICKPAD(info->ext_cap_0c)) {
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
- !SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10))
+ !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
__set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit);
/* Clickpads report only left button */
__clear_bit(BTN_RIGHT, dev->keybit);
{
struct synaptics_data *priv = psmouse->private;
- if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity))
+ /*
+ * We might have left a breadcrumb when trying to
+ * set up SMbus companion.
+ */
+ psmouse_smbus_cleanup(psmouse);
+
+ if (!priv->absolute_mode &&
+ SYN_ID_DISGEST_SUPPORTED(priv->info.identity))
device_remove_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_disable_gesture.dattr);
static int synaptics_reconnect(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
- struct synaptics_data old_priv = *priv;
+ struct synaptics_device_info info;
unsigned char param[2];
int retry = 0;
int error;
if (retry > 1)
psmouse_dbg(psmouse, "reconnected after %d tries\n", retry);
- if (synaptics_query_hardware(psmouse)) {
+ if (synaptics_query_hardware(psmouse, &info)) {
psmouse_err(psmouse, "Unable to query device.\n");
return -1;
}
return -1;
}
- if (old_priv.identity != priv->identity ||
- old_priv.model_id != priv->model_id ||
- old_priv.capabilities != priv->capabilities ||
- old_priv.ext_cap != priv->ext_cap) {
+ if (info.identity != priv->info.identity ||
+ info.model_id != priv->info.model_id ||
+ info.capabilities != priv->info.capabilities ||
+ info.ext_cap != priv->info.ext_cap) {
psmouse_err(psmouse,
- "hardware appears to be different: id(%ld-%ld), model(%ld-%ld), caps(%lx-%lx), ext(%lx-%lx).\n",
- old_priv.identity, priv->identity,
- old_priv.model_id, priv->model_id,
- old_priv.capabilities, priv->capabilities,
- old_priv.ext_cap, priv->ext_cap);
+ "hardware appears to be different: id(%u-%u), model(%u-%u), caps(%x-%x), ext(%x-%x).\n",
+ priv->info.identity, info.identity,
+ priv->info.model_id, info.model_id,
+ priv->info.capabilities, info.capabilities,
+ priv->info.ext_cap, info.ext_cap);
return -1;
}
cr48_profile_sensor = dmi_check_system(cr48_dmi_table);
}
-static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+static int synaptics_init_ps2(struct psmouse *psmouse,
+ struct synaptics_device_info *info,
+ bool absolute_mode)
{
struct synaptics_data *priv;
- int err = -1;
+ int err;
- /*
- * The OLPC XO has issues with Synaptics' absolute mode; the constant
- * packet spew overloads the EC such that key presses on the keyboard
- * are missed. Given that, don't even attempt to use Absolute mode.
- * Relative mode seems to work just fine.
- */
- if (absolute_mode && broken_olpc_ec) {
- psmouse_info(psmouse,
- "OLPC XO detected, not enabling Synaptics protocol.\n");
- return -ENODEV;
- }
+ synaptics_apply_quirks(psmouse, info);
psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- psmouse_reset(psmouse);
-
- if (synaptics_query_hardware(psmouse)) {
- psmouse_err(psmouse, "Unable to query device.\n");
- goto init_fail;
- }
-
+ priv->info = *info;
priv->absolute_mode = absolute_mode;
- if (SYN_ID_DISGEST_SUPPORTED(priv->identity))
+ if (SYN_ID_DISGEST_SUPPORTED(info->identity))
priv->disable_gesture = true;
/*
*/
priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids);
- if (synaptics_set_mode(psmouse)) {
+ err = synaptics_set_mode(psmouse);
+ if (err) {
psmouse_err(psmouse, "Unable to initialize device.\n");
goto init_fail;
}
- priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
+ priv->pkt_type = SYN_MODEL_NEWABS(info->model_id) ?
+ SYN_NEWABS : SYN_OLDABS;
psmouse_info(psmouse,
- "Touchpad model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx/%#lx, board id: %lu, fw id: %lu\n",
- SYN_ID_MODEL(priv->identity),
- SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity),
- priv->model_id,
- priv->capabilities, priv->ext_cap, priv->ext_cap_0c,
- priv->ext_cap_10, priv->board_id, priv->firmware_id);
+ "Touchpad model: %u, fw: %u.%u, id: %#x, caps: %#x/%#x/%#x/%#x, board id: %u, fw id: %u\n",
+ SYN_ID_MODEL(info->identity),
+ SYN_ID_MAJOR(info->identity), SYN_ID_MINOR(info->identity),
+ info->model_id,
+ info->capabilities, info->ext_cap, info->ext_cap_0c,
+ info->ext_cap_10, info->board_id, info->firmware_id);
set_input_params(psmouse, priv);
* Hardware info bits seem to be good candidates as they
* are documented to be for Synaptics corp. internal use.
*/
- psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) |
- (priv->model_id & 0x000000ff);
+ psmouse->model = ((info->model_id & 0x00ff0000) >> 8) |
+ (info->model_id & 0x000000ff);
if (absolute_mode) {
psmouse->protocol_handler = synaptics_process_byte;
/* Synaptics can usually stay in sync without extra help */
psmouse->resync_time = 0;
- if (SYN_CAP_PASS_THROUGH(priv->capabilities))
+ if (SYN_CAP_PASS_THROUGH(info->capabilities))
synaptics_pt_create(psmouse);
/*
psmouse->rate = 40;
}
- if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity)) {
+ if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(info->identity)) {
err = device_create_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_disable_gesture.dattr);
if (err) {
return err;
}
-int synaptics_init(struct psmouse *psmouse)
+static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+{
+ struct synaptics_device_info info;
+ int error;
+
+ psmouse_reset(psmouse);
+
+ error = synaptics_query_hardware(psmouse, &info);
+ if (error) {
+ psmouse_err(psmouse, "Unable to query device: %d\n", error);
+ return error;
+ }
+
+ return synaptics_init_ps2(psmouse, &info, absolute_mode);
+}
+
+int synaptics_init_absolute(struct psmouse *psmouse)
{
return __synaptics_init(psmouse, true);
}
return __synaptics_init(psmouse, false);
}
+static int synaptics_setup_ps2(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
+{
+ bool absolute_mode = true;
+ int error;
+
+ /*
+ * The OLPC XO has issues with Synaptics' absolute mode; the constant
+ * packet spew overloads the EC such that key presses on the keyboard
+ * are missed. Given that, don't even attempt to use Absolute mode.
+ * Relative mode seems to work just fine.
+ */
+ if (broken_olpc_ec) {
+ psmouse_info(psmouse,
+ "OLPC XO detected, forcing relative protocol.\n");
+ absolute_mode = false;
+ }
+
+ error = synaptics_init_ps2(psmouse, info, absolute_mode);
+ if (error)
+ return error;
+
+ return absolute_mode ? PSMOUSE_SYNAPTICS : PSMOUSE_SYNAPTICS_RELATIVE;
+}
+
#else /* CONFIG_MOUSE_PS2_SYNAPTICS */
void __init synaptics_module_init(void)
{
}
-int synaptics_init(struct psmouse *psmouse)
+static int __maybe_unused
+synaptics_setup_ps2(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
return -ENOSYS;
}
#endif /* CONFIG_MOUSE_PS2_SYNAPTICS */
+
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS
+
+/*
+ * The newest Synaptics device can use a secondary bus (called InterTouch) which
+ * provides a better bandwidth and allow a better control of the touchpads.
+ * This is used to decide if we need to use this bus or not.
+ */
+enum {
+ SYNAPTICS_INTERTOUCH_NOT_SET = -1,
+ SYNAPTICS_INTERTOUCH_OFF,
+ SYNAPTICS_INTERTOUCH_ON,
+};
+
+static int synaptics_intertouch = SYNAPTICS_INTERTOUCH_NOT_SET;
+module_param_named(synaptics_intertouch, synaptics_intertouch, int, 0644);
+MODULE_PARM_DESC(synaptics_intertouch, "Use a secondary bus for the Synaptics device.");
+
+static int synaptics_create_intertouch(struct psmouse *psmouse,
+ struct synaptics_device_info *info,
+ bool leave_breadcrumbs)
+{
+ bool topbuttonpad =
+ psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
+ !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10);
+ const struct rmi_device_platform_data pdata = {
+ .sensor_pdata = {
+ .sensor_type = rmi_sensor_touchpad,
+ .axis_align.flip_y = true,
+ /* to prevent cursors jumps: */
+ .kernel_tracking = true,
+ .topbuttonpad = topbuttonpad,
+ },
+ .f30_data = {
+ .buttonpad = SYN_CAP_CLICKPAD(info->ext_cap_0c),
+ .trackstick_buttons =
+ !!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10),
+ },
+ };
+ const struct i2c_board_info intertouch_board = {
+ I2C_BOARD_INFO("rmi4_smbus", 0x2c),
+ .flags = I2C_CLIENT_HOST_NOTIFY,
+ };
+
+ return psmouse_smbus_init(psmouse, &intertouch_board,
+ &pdata, sizeof(pdata),
+ leave_breadcrumbs);
+}
+
+/**
+ * synaptics_setup_intertouch - called once the PS/2 devices are enumerated
+ * and decides to instantiate a SMBus InterTouch device.
+ */
+static int synaptics_setup_intertouch(struct psmouse *psmouse,
+ struct synaptics_device_info *info,
+ bool leave_breadcrumbs)
+{
+ int error;
+
+ if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_OFF)
+ return -ENXIO;
+
+ if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_NOT_SET) {
+ if (!psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
+ !psmouse_matches_pnp_id(psmouse, smbus_pnp_ids))
+ return -ENXIO;
+ }
+
+ psmouse_info(psmouse, "Trying to set up SMBus access\n");
+
+ error = synaptics_create_intertouch(psmouse, info, leave_breadcrumbs);
+ if (error) {
+ if (error == -EAGAIN)
+ psmouse_info(psmouse, "SMbus companion is not ready yet\n");
+ else
+ psmouse_err(psmouse, "unable to create intertouch device\n");
+
+ return error;
+ }
+
+ return 0;
+}
+
+int synaptics_init_smbus(struct psmouse *psmouse)
+{
+ struct synaptics_device_info info;
+ int error;
+
+ psmouse_reset(psmouse);
+
+ error = synaptics_query_hardware(psmouse, &info);
+ if (error) {
+ psmouse_err(psmouse, "Unable to query device: %d\n", error);
+ return error;
+ }
+
+ if (!SYN_CAP_INTERTOUCH(info.ext_cap_0c))
+ return -ENXIO;
+
+ return synaptics_create_intertouch(psmouse, &info, false);
+}
+
+#else /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
+
+static int __maybe_unused
+synaptics_setup_intertouch(struct psmouse *psmouse,
+ struct synaptics_device_info *info,
+ bool leave_breadcrumbs)
+{
+ return -ENOSYS;
+}
+
+int synaptics_init_smbus(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
+
+#if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \
+ defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)
+
+int synaptics_init(struct psmouse *psmouse)
+{
+ struct synaptics_device_info info;
+ int error;
+ int retval;
+
+ psmouse_reset(psmouse);
+
+ error = synaptics_query_hardware(psmouse, &info);
+ if (error) {
+ psmouse_err(psmouse, "Unable to query device: %d\n", error);
+ return error;
+ }
+
+ if (SYN_CAP_INTERTOUCH(info.ext_cap_0c)) {
+ error = synaptics_setup_intertouch(psmouse, &info, true);
+ if (!error)
+ return PSMOUSE_SYNAPTICS_SMBUS;
+ }
+
+ retval = synaptics_setup_ps2(psmouse, &info);
+ if (retval < 0) {
+ /*
+ * Not using any flavor of Synaptics support, so clean up
+ * SMbus breadcrumbs, if any.
+ */
+ psmouse_smbus_cleanup(psmouse);
+ }
+
+ return retval;
+}
+
+#else /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
+
+int synaptics_init(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
#define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000)
#define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400)
#define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & 0x000800)
+#define SYN_CAP_INTERTOUCH(ex0c) ((ex0c) & 0x004000)
/*
* The following descibes response for the 0x10 query.
#define SYN_CAP_EXT_BUTTONS_STICK(ex10) ((ex10) & 0x010000)
#define SYN_CAP_SECUREPAD(ex10) ((ex10) & 0x020000)
-#define SYN_CAP_EXT_BUTTON_STICK_L(eb) (!!((eb) & 0x01))
-#define SYN_CAP_EXT_BUTTON_STICK_M(eb) (!!((eb) & 0x02))
-#define SYN_CAP_EXT_BUTTON_STICK_R(eb) (!!((eb) & 0x04))
+#define SYN_EXT_BUTTON_STICK_L(eb) (((eb) & BIT(0)) >> 0)
+#define SYN_EXT_BUTTON_STICK_M(eb) (((eb) & BIT(1)) >> 1)
+#define SYN_EXT_BUTTON_STICK_R(eb) (((eb) & BIT(2)) >> 2)
/* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
signed char scroll;
};
+/* Data read from the touchpad */
+struct synaptics_device_info {
+ u32 model_id; /* Model-ID */
+ u32 firmware_id; /* Firmware-ID */
+ u32 board_id; /* Board-ID */
+ u32 capabilities; /* Capabilities */
+ u32 ext_cap; /* Extended Capabilities */
+ u32 ext_cap_0c; /* Ext Caps from 0x0c query */
+ u32 ext_cap_10; /* Ext Caps from 0x10 query */
+ u32 identity; /* Identification */
+ u32 x_res, y_res; /* X/Y resolution in units/mm */
+ u32 x_max, y_max; /* Max coordinates (from FW) */
+ u32 x_min, y_min; /* Min coordinates (from FW) */
+};
+
struct synaptics_data {
- /* Data read from the touchpad */
- unsigned long int model_id; /* Model-ID */
- unsigned long int firmware_id; /* Firmware-ID */
- unsigned long int board_id; /* Board-ID */
- unsigned long int capabilities; /* Capabilities */
- unsigned long int ext_cap; /* Extended Capabilities */
- unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */
- unsigned long int ext_cap_10; /* Ext Caps from 0x10 query */
- unsigned long int identity; /* Identification */
- unsigned int x_res, y_res; /* X/Y resolution in units/mm */
- unsigned int x_max, y_max; /* Max coordinates (from FW) */
- unsigned int x_min, y_min; /* Min coordinates (from FW) */
+ struct synaptics_device_info info;
unsigned char pkt_type; /* packet type - old, new, etc */
unsigned char mode; /* current mode byte */
void synaptics_module_init(void);
int synaptics_detect(struct psmouse *psmouse, bool set_properties);
-int synaptics_init(struct psmouse *psmouse);
+int synaptics_init_absolute(struct psmouse *psmouse);
int synaptics_init_relative(struct psmouse *psmouse);
+int synaptics_init_smbus(struct psmouse *psmouse);
+int synaptics_init(struct psmouse *psmouse);
void synaptics_reset(struct psmouse *psmouse);
#endif /* _SYNAPTICS_H */
};
MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table);
+#ifdef CONFIG_OF
+static const struct of_device_id synaptics_i2c_of_match[] = {
+ { .compatible = "synaptics,synaptics_i2c", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, synaptics_i2c_of_match);
+#endif
+
static struct i2c_driver synaptics_i2c_driver = {
.driver = {
.name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(synaptics_i2c_of_match),
.pm = &synaptics_i2c_pm,
},
}
if (!try_module_get(owner)) {
- pr_warning("Can't get module reference, dropping event %d\n",
- event_type);
+ pr_warn("Can't get module reference, dropping event %d\n",
+ event_type);
kfree(event);
retval = -EINVAL;
goto out;
error = driver_attach(&drv->driver);
if (error)
- pr_warning("driver_attach() failed for %s with error %d\n",
- drv->driver.name, error);
+ pr_warn("driver_attach() failed for %s with error %d\n",
+ drv->driver.name, error);
}
int __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name)
static int serio_resume(struct device *dev)
{
struct serio *serio = to_serio_port(dev);
+ int error = -ENOENT;
- /*
- * Driver reconnect can take a while, so better let kseriod
- * deal with it.
- */
- serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
+ mutex_lock(&serio->drv_mutex);
+ if (serio->drv && serio->drv->fast_reconnect) {
+ error = serio->drv->fast_reconnect(serio);
+ if (error && error != -ENOENT)
+ dev_warn(dev, "fast reconnect failed with error %d\n",
+ error);
+ }
+ mutex_unlock(&serio->drv_mutex);
+
+ if (error) {
+ /*
+ * Driver reconnect can take a while, so better let
+ * kseriod deal with it.
+ */
+ serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
+ }
return 0;
}
* @keymap: Keymap in form of array of &key_entry structures ending
* with %KE_END type entry
* @setup: Function that can be used to adjust keymap entries
- * depending on device's deeds, may be %NULL
+ * depending on device's needs, may be %NULL
*
* The function calculates size and allocates copy of the original
* keymap after which sets up input device event bits appropriately.
- * Before destroying input device allocated keymap should be freed
- * with a call to sparse_keymap_free().
+ * The allocated copy of the keymap is automatically freed when it
+ * is no longer needed.
*/
int sparse_keymap_setup(struct input_dev *dev,
const struct key_entry *keymap,
for (e = keymap; e->type != KE_END; e++)
map_size++;
- map = kcalloc(map_size, sizeof(struct key_entry), GFP_KERNEL);
+ map = devm_kmemdup(&dev->dev, keymap, map_size * sizeof(*map),
+ GFP_KERNEL);
if (!map)
return -ENOMEM;
- memcpy(map, keymap, map_size * sizeof(struct key_entry));
-
for (i = 0; i < map_size; i++) {
entry = &map[i];
if (setup) {
error = setup(dev, entry);
if (error)
- goto err_out;
+ return error;
}
switch (entry->type) {
dev->setkeycode = sparse_keymap_setkeycode;
return 0;
-
- err_out:
- kfree(map);
- return error;
}
EXPORT_SYMBOL(sparse_keymap_setup);
* sparse_keymap_free - free memory allocated for sparse keymap
* @dev: Input device using sparse keymap
*
- * This function is used to free memory allocated by sparse keymap
+ * This function used to free memory allocated by sparse keymap
* in an input device that was set up by sparse_keymap_setup().
- * NOTE: It is safe to cal this function while input device is
- * still registered (however the drivers should care not to try to
- * use freed keymap and thus have to shut off interrupts/polling
- * before freeing the keymap).
+ * Since sparse_keymap_setup() now uses a managed allocation for the
+ * keymap copy, use of this function is deprecated.
*/
void sparse_keymap_free(struct input_dev *dev)
{
- unsigned long flags;
-
- /*
- * Take event lock to prevent racing with input_get_keycode()
- * and input_set_keycode() if we are called while input device
- * is still registered.
- */
- spin_lock_irqsave(&dev->event_lock, flags);
-
- kfree(dev->keycode);
- dev->keycode = NULL;
- dev->keycodemax = 0;
-
- spin_unlock_irqrestore(&dev->event_lock, flags);
}
EXPORT_SYMBOL(sparse_keymap_free);
config TOUCHSCREEN_AD7879_I2C
tristate "support I2C bus connection"
depends on TOUCHSCREEN_AD7879 && I2C
+ select REGMAP_I2C
help
Say Y here if you have AD7879-1/AD7889-1 hooked to an I2C bus.
config TOUCHSCREEN_AD7879_SPI
tristate "support SPI bus connection"
depends on TOUCHSCREEN_AD7879 && SPI_MASTER
+ select REGMAP_SPI
help
Say Y here if you have AD7879-1/AD7889-1 hooked to a SPI bus.
To compile this driver as a module, choose M here: the
module will be called tsc2007.
+config TOUCHSCREEN_TSC2007_IIO
+ bool "IIO interface for external ADC input and temperature"
+ depends on TOUCHSCREEN_TSC2007
+ depends on IIO=y || IIO=TOUCHSCREEN_TSC2007
+ help
+ Saying Y here adds an iio interface to the tsc2007 which
+ provides values for the AUX input (used for e.g. battery
+ or ambient light monitoring), temperature and raw input
+ values.
+
config TOUCHSCREEN_W90X900
tristate "W90P910 touchscreen driver"
depends on ARCH_W90X900
obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o
obj-$(CONFIG_TOUCHSCREEN_TSC2004) += tsc2004.o
obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o
+tsc2007-y := tsc2007_core.o
+tsc2007-$(CONFIG_TOUCHSCREEN_TSC2007_IIO) += tsc2007_iio.o
obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
#include <linux/types.h>
#include <linux/of.h>
#include <linux/pm.h>
+#include <linux/regmap.h>
#include "ad7879.h"
#define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */
-/* All registers are word-sized.
- * AD7879 uses a high-byte first convention.
- */
-static int ad7879_i2c_read(struct device *dev, u8 reg)
-{
- struct i2c_client *client = to_i2c_client(dev);
-
- return i2c_smbus_read_word_swapped(client, reg);
-}
-
-static int ad7879_i2c_multi_read(struct device *dev,
- u8 first_reg, u8 count, u16 *buf)
-{
- struct i2c_client *client = to_i2c_client(dev);
- u8 idx;
-
- i2c_smbus_read_i2c_block_data(client, first_reg, count * 2, (u8 *)buf);
-
- for (idx = 0; idx < count; ++idx)
- buf[idx] = swab16(buf[idx]);
-
- return 0;
-}
-
-static int ad7879_i2c_write(struct device *dev, u8 reg, u16 val)
-{
- struct i2c_client *client = to_i2c_client(dev);
-
- return i2c_smbus_write_word_swapped(client, reg, val);
-}
-
-static const struct ad7879_bus_ops ad7879_i2c_bus_ops = {
- .bustype = BUS_I2C,
- .read = ad7879_i2c_read,
- .multi_read = ad7879_i2c_multi_read,
- .write = ad7879_i2c_write,
+static const struct regmap_config ad7879_i2c_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = 15,
};
static int ad7879_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct ad7879 *ts;
+ struct regmap *regmap;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA)) {
return -EIO;
}
- ts = ad7879_probe(&client->dev, AD7879_DEVID, client->irq,
- &ad7879_i2c_bus_ops);
- if (IS_ERR(ts))
- return PTR_ERR(ts);
-
- i2c_set_clientdata(client, ts);
-
- return 0;
-}
-
-static int ad7879_i2c_remove(struct i2c_client *client)
-{
- struct ad7879 *ts = i2c_get_clientdata(client);
-
- ad7879_remove(ts);
+ regmap = devm_regmap_init_i2c(client, &ad7879_i2c_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
- return 0;
+ return ad7879_probe(&client->dev, regmap, client->irq,
+ BUS_I2C, AD7879_DEVID);
}
static const struct i2c_device_id ad7879_id[] = {
.of_match_table = of_match_ptr(ad7879_i2c_dt_ids),
},
.probe = ad7879_i2c_probe,
- .remove = ad7879_i2c_remove,
.id_table = ad7879_id,
};
module_i2c_driver(ad7879_i2c_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver");
MODULE_LICENSE("GPL");
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/regmap.h>
#include "ad7879.h"
#define AD7879_DEVID 0x7A /* AD7879/AD7889 */
#define MAX_SPI_FREQ_HZ 5000000
-#define AD7879_CMD_MAGIC 0xE000
-#define AD7879_CMD_READ (1 << 10)
-#define AD7879_CMD(reg) (AD7879_CMD_MAGIC | ((reg) & 0xF))
-#define AD7879_WRITECMD(reg) (AD7879_CMD(reg))
-#define AD7879_READCMD(reg) (AD7879_CMD(reg) | AD7879_CMD_READ)
-/*
- * ad7879_read/write are only used for initial setup and for sysfs controls.
- * The main traffic is done in ad7879_collect().
- */
-
-static int ad7879_spi_xfer(struct spi_device *spi,
- u16 cmd, u8 count, u16 *tx_buf, u16 *rx_buf)
-{
- struct spi_message msg;
- struct spi_transfer *xfers;
- void *spi_data;
- u16 *command;
- u16 *_rx_buf = _rx_buf; /* shut gcc up */
- u8 idx;
- int ret;
-
- xfers = spi_data = kzalloc(sizeof(*xfers) * (count + 2), GFP_KERNEL);
- if (!spi_data)
- return -ENOMEM;
-
- spi_message_init(&msg);
-
- command = spi_data;
- command[0] = cmd;
- if (count == 1) {
- /* ad7879_spi_{read,write} gave us buf on stack */
- command[1] = *tx_buf;
- tx_buf = &command[1];
- _rx_buf = rx_buf;
- rx_buf = &command[2];
- }
-
- ++xfers;
- xfers[0].tx_buf = command;
- xfers[0].len = 2;
- spi_message_add_tail(&xfers[0], &msg);
- ++xfers;
-
- for (idx = 0; idx < count; ++idx) {
- if (rx_buf)
- xfers[idx].rx_buf = &rx_buf[idx];
- if (tx_buf)
- xfers[idx].tx_buf = &tx_buf[idx];
- xfers[idx].len = 2;
- spi_message_add_tail(&xfers[idx], &msg);
- }
-
- ret = spi_sync(spi, &msg);
-
- if (count == 1)
- _rx_buf[0] = command[2];
-
- kfree(spi_data);
-
- return ret;
-}
-
-static int ad7879_spi_multi_read(struct device *dev,
- u8 first_reg, u8 count, u16 *buf)
-{
- struct spi_device *spi = to_spi_device(dev);
+#define AD7879_CMD_MAGIC 0xE0
+#define AD7879_CMD_READ BIT(2)
- return ad7879_spi_xfer(spi, AD7879_READCMD(first_reg), count, NULL, buf);
-}
-
-static int ad7879_spi_read(struct device *dev, u8 reg)
-{
- struct spi_device *spi = to_spi_device(dev);
- u16 ret, dummy;
-
- return ad7879_spi_xfer(spi, AD7879_READCMD(reg), 1, &dummy, &ret) ? : ret;
-}
-
-static int ad7879_spi_write(struct device *dev, u8 reg, u16 val)
-{
- struct spi_device *spi = to_spi_device(dev);
- u16 dummy;
-
- return ad7879_spi_xfer(spi, AD7879_WRITECMD(reg), 1, &val, &dummy);
-}
-
-static const struct ad7879_bus_ops ad7879_spi_bus_ops = {
- .bustype = BUS_SPI,
- .read = ad7879_spi_read,
- .multi_read = ad7879_spi_multi_read,
- .write = ad7879_spi_write,
+static const struct regmap_config ad7879_spi_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = 15,
+ .read_flag_mask = AD7879_CMD_MAGIC | AD7879_CMD_READ,
+ .write_flag_mask = AD7879_CMD_MAGIC,
};
static int ad7879_spi_probe(struct spi_device *spi)
{
- struct ad7879 *ts;
- int err;
+ struct regmap *regmap;
/* don't exceed max specified SPI CLK frequency */
if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
return -EINVAL;
}
- spi->bits_per_word = 16;
- err = spi_setup(spi);
- if (err) {
- dev_dbg(&spi->dev, "spi master doesn't support 16 bits/word\n");
- return err;
- }
-
- ts = ad7879_probe(&spi->dev, AD7879_DEVID, spi->irq, &ad7879_spi_bus_ops);
- if (IS_ERR(ts))
- return PTR_ERR(ts);
-
- spi_set_drvdata(spi, ts);
-
- return 0;
-}
-
-static int ad7879_spi_remove(struct spi_device *spi)
-{
- struct ad7879 *ts = spi_get_drvdata(spi);
-
- ad7879_remove(ts);
+ regmap = devm_regmap_init_spi(spi, &ad7879_spi_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
- return 0;
+ return ad7879_probe(&spi->dev, regmap, spi->irq, BUS_SPI, AD7879_DEVID);
}
#ifdef CONFIG_OF
.of_match_table = of_match_ptr(ad7879_spi_dt_ids),
},
.probe = ad7879_spi_probe,
- .remove = ad7879_spi_remove,
};
module_spi_driver(ad7879_spi_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("AD7879(-1) touchscreen SPI bus driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:ad7879");
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
-#include <linux/spi/spi.h>
-#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/input/touchscreen.h>
#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(50)
struct ad7879 {
- const struct ad7879_bus_ops *bops;
-
+ struct regmap *regmap;
struct device *dev;
struct input_dev *input;
struct timer_list timer;
static int ad7879_read(struct ad7879 *ts, u8 reg)
{
- return ts->bops->read(ts->dev, reg);
-}
+ unsigned int val;
+ int error;
-static int ad7879_multi_read(struct ad7879 *ts, u8 first_reg, u8 count, u16 *buf)
-{
- return ts->bops->multi_read(ts->dev, first_reg, count, buf);
+ error = regmap_read(ts->regmap, reg, &val);
+ if (error) {
+ dev_err(ts->dev, "failed to read register %#02x: %d\n",
+ reg, error);
+ return error;
+ }
+
+ return val;
}
static int ad7879_write(struct ad7879 *ts, u8 reg, u16 val)
{
- return ts->bops->write(ts->dev, reg, val);
+ int error;
+
+ error = regmap_write(ts->regmap, reg, val);
+ if (error) {
+ dev_err(ts->dev,
+ "failed to write %#04x to register %#02x: %d\n",
+ val, reg, error);
+ return error;
+ }
+
+ return 0;
}
static int ad7879_report(struct ad7879 *ts)
{
struct ad7879 *ts = handle;
- ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data);
+ regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS,
+ ts->conversion_data, AD7879_NR_SENSE);
if (!ad7879_report(ts))
mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
static int ad7879_gpio_add(struct ad7879 *ts,
const struct ad7879_platform_data *pdata)
{
+ bool gpio_export;
+ int gpio_base;
int ret = 0;
+ if (pdata) {
+ gpio_export = pdata->gpio_export;
+ gpio_base = pdata->gpio_base;
+ } else {
+ gpio_export = device_property_read_bool(ts->dev,
+ "gpio-controller");
+ gpio_base = -1;
+ }
+
mutex_init(&ts->mutex);
- if (pdata->gpio_export) {
+ if (gpio_export) {
ts->gc.direction_input = ad7879_gpio_direction_input;
ts->gc.direction_output = ad7879_gpio_direction_output;
ts->gc.get = ad7879_gpio_get_value;
ts->gc.set = ad7879_gpio_set_value;
ts->gc.can_sleep = 1;
- ts->gc.base = pdata->gpio_base;
+ ts->gc.base = gpio_base;
ts->gc.ngpio = 1;
ts->gc.label = "AD7879-GPIO";
ts->gc.owner = THIS_MODULE;
ts->gc.parent = ts->dev;
- ret = gpiochip_add_data(&ts->gc, ts);
+ ret = devm_gpiochip_add_data(ts->dev, &ts->gc, ts);
if (ret)
dev_err(ts->dev, "failed to register gpio %d\n",
ts->gc.base);
return ret;
}
-
-static void ad7879_gpio_remove(struct ad7879 *ts)
-{
- const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev);
-
- if (pdata && pdata->gpio_export)
- gpiochip_remove(&ts->gc);
-
-}
#else
-static inline int ad7879_gpio_add(struct ad7879 *ts,
- const struct ad7879_platform_data *pdata)
+static int ad7879_gpio_add(struct ad7879 *ts,
+ const struct ad7879_platform_data *pdata)
{
return 0;
}
-
-static inline void ad7879_gpio_remove(struct ad7879 *ts)
-{
-}
#endif
static int ad7879_parse_dt(struct device *dev, struct ad7879 *ts)
return 0;
}
-struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
- const struct ad7879_bus_ops *bops)
+static void ad7879_cleanup_sysfs(void *_ts)
+{
+ struct ad7879 *ts = _ts;
+
+ sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group);
+}
+
+int ad7879_probe(struct device *dev, struct regmap *regmap,
+ int irq, u16 bustype, u8 devid)
{
struct ad7879_platform_data *pdata = dev_get_platdata(dev);
struct ad7879 *ts;
int err;
u16 revid;
- if (!irq) {
+ if (irq <= 0) {
dev_err(dev, "No IRQ specified\n");
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
if (!ts)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
if (pdata) {
/* Platform data use swapped axis (backward compatibility) */
ts->averaging = pdata->averaging;
ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
ts->median = pdata->median;
- } else if (dev->of_node) {
- ad7879_parse_dt(dev, ts);
} else {
- dev_err(dev, "No platform data\n");
- return ERR_PTR(-EINVAL);
+ err = ad7879_parse_dt(dev, ts);
+ if (err)
+ return err;
}
input_dev = devm_input_allocate_device(dev);
if (!input_dev) {
dev_err(dev, "Failed to allocate input device\n");
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
}
- ts->bops = bops;
ts->dev = dev;
ts->input = input_dev;
ts->irq = irq;
+ ts->regmap = regmap;
setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
input_dev->name = "AD7879 Touchscreen";
input_dev->phys = ts->phys;
input_dev->dev.parent = dev;
- input_dev->id.bustype = bops->bustype;
+ input_dev->id.bustype = bustype;
input_dev->open = ad7879_open;
input_dev->close = ad7879_close;
input_set_drvdata(input_dev, ts);
- __set_bit(EV_ABS, input_dev->evbit);
- __set_bit(ABS_X, input_dev->absbit);
- __set_bit(ABS_Y, input_dev->absbit);
- __set_bit(ABS_PRESSURE, input_dev->absbit);
-
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(BTN_TOUCH, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
if (pdata) {
input_set_abs_params(input_dev, ABS_X,
} else {
input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
+ input_set_capability(input_dev, EV_ABS, ABS_PRESSURE);
touchscreen_parse_properties(input_dev, false, NULL);
if (!input_abs_get_max(input_dev, ABS_PRESSURE)) {
dev_err(dev, "Touchscreen pressure is not specified\n");
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
}
err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET);
if (err < 0) {
dev_err(dev, "Failed to write %s\n", input_dev->name);
- return ERR_PTR(err);
+ return err;
}
revid = ad7879_read(ts, AD7879_REG_REVID);
if (input_dev->id.product != devid) {
dev_err(dev, "Failed to probe %s (%x vs %x)\n",
input_dev->name, devid, revid);
- return ERR_PTR(-ENODEV);
+ return -ENODEV;
}
ts->cmd_crtl3 = AD7879_YPLUS_BIT |
dev_name(dev), ts);
if (err) {
dev_err(dev, "Failed to request IRQ: %d\n", err);
- return ERR_PTR(err);
+ return err;
}
__ad7879_disable(ts);
err = sysfs_create_group(&dev->kobj, &ad7879_attr_group);
if (err)
- goto err_out;
+ return err;
- if (pdata) {
- err = ad7879_gpio_add(ts, pdata);
- if (err)
- goto err_remove_attr;
- }
+ err = devm_add_action_or_reset(dev, ad7879_cleanup_sysfs, ts);
+ if (err)
+ return err;
+
+ err = ad7879_gpio_add(ts, pdata);
+ if (err)
+ return err;
err = input_register_device(input_dev);
if (err)
- goto err_remove_gpio;
+ return err;
- return ts;
+ dev_set_drvdata(dev, ts);
-err_remove_gpio:
- ad7879_gpio_remove(ts);
-err_remove_attr:
- sysfs_remove_group(&dev->kobj, &ad7879_attr_group);
-err_out:
- return ERR_PTR(err);
+ return 0;
}
EXPORT_SYMBOL(ad7879_probe);
-void ad7879_remove(struct ad7879 *ts)
-{
- ad7879_gpio_remove(ts);
- sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group);
-}
-EXPORT_SYMBOL(ad7879_remove);
-
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver");
MODULE_LICENSE("GPL");
#include <linux/types.h>
-struct ad7879;
struct device;
-
-struct ad7879_bus_ops {
- u16 bustype;
- int (*read)(struct device *dev, u8 reg);
- int (*multi_read)(struct device *dev, u8 first_reg, u8 count, u16 *buf);
- int (*write)(struct device *dev, u8 reg, u16 val);
-};
+struct regmap;
extern const struct dev_pm_ops ad7879_pm_ops;
-struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq,
- const struct ad7879_bus_ops *bops);
-void ad7879_remove(struct ad7879 *);
+int ad7879_probe(struct device *dev, struct regmap *regmap,
+ int irq, u16 bustype, u8 devid);
#endif
msecs_to_jiffies(TS_POLL_PERIOD));
}
- if (ts->pendown) {
+ if (ts->pendown && !ts->stopped) {
struct input_dev *input = ts->input;
input_report_key(input, BTN_TOUCH, 0);
};
MODULE_DEVICE_TABLE(i2c, max11801_ts_id);
+static const struct of_device_id max11801_ts_dt_ids[] = {
+ { .compatible = "maxim,max11801" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, max11801_ts_dt_ids);
+
static struct i2c_driver max11801_ts_driver = {
.driver = {
.name = "max11801_ts",
+ .of_match_table = max11801_ts_dt_ids,
},
.id_table = max11801_ts_id,
.probe = max11801_ts_probe,
*/
static int mip4_query_device(struct mip4_ts *ts)
{
+ union i2c_smbus_data dummy;
int error;
u8 cmd[2];
u8 buf[14];
+ /*
+ * Make sure there is something at this address as we do not
+ * consider subsequent failures as fatal.
+ */
+ if (i2c_smbus_xfer(ts->client->adapter, ts->client->addr,
+ 0, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+ dev_err(&ts->client->dev, "nothing at this address\n");
+ return -ENXIO;
+ }
+
/* Product name */
cmd[0] = MIP4_R0_INFO;
cmd[1] = MIP4_R1_INFO_PRODUCT_NAME;
MODULE_DEVICE_TABLE(acpi, silead_ts_acpi_match);
#endif
+#ifdef CONFIG_OF
+static const struct of_device_id silead_ts_of_match[] = {
+ { .compatible = "silead,gsl1680" },
+ { .compatible = "silead,gsl1688" },
+ { .compatible = "silead,gsl3670" },
+ { .compatible = "silead,gsl3675" },
+ { .compatible = "silead,gsl3692" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, silead_ts_of_match);
+#endif
+
static struct i2c_driver silead_ts_driver = {
.probe = silead_ts_probe,
.id_table = silead_ts_id,
.driver = {
.name = SILEAD_TS_NAME,
.acpi_match_table = ACPI_PTR(silead_ts_acpi_match),
+ .of_match_table = of_match_ptr(silead_ts_of_match),
.pm = &silead_ts_pm,
},
};
*/
init_data = tps_board->tps6507x_ts_init_data;
- tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL);
+ tsc = devm_kzalloc(&pdev->dev, sizeof(struct tps6507x_ts), GFP_KERNEL);
if (!tsc) {
dev_err(tps6507x_dev->dev, "failed to allocate driver data\n");
return -ENOMEM;
snprintf(tsc->phys, sizeof(tsc->phys),
"%s/input0", dev_name(tsc->dev));
- poll_dev = input_allocate_polled_device();
+ poll_dev = devm_input_allocate_polled_device(&pdev->dev);
if (!poll_dev) {
dev_err(tsc->dev, "Failed to allocate polled input device.\n");
- error = -ENOMEM;
- goto err_free_mem;
+ return -ENOMEM;
}
tsc->poll_dev = poll_dev;
error = tps6507x_adc_standby(tsc);
if (error)
- goto err_free_polled_dev;
+ return error;
error = input_register_polled_device(poll_dev);
if (error)
- goto err_free_polled_dev;
-
- platform_set_drvdata(pdev, tsc);
-
- return 0;
-
-err_free_polled_dev:
- input_free_polled_device(poll_dev);
-err_free_mem:
- kfree(tsc);
- return error;
-}
-
-static int tps6507x_ts_remove(struct platform_device *pdev)
-{
- struct tps6507x_ts *tsc = platform_get_drvdata(pdev);
- struct input_polled_dev *poll_dev = tsc->poll_dev;
-
- input_unregister_polled_device(poll_dev);
- input_free_polled_device(poll_dev);
-
- kfree(tsc);
+ return error;
return 0;
}
.name = "tps6507x-ts",
},
.probe = tps6507x_ts_probe,
- .remove = tps6507x_ts_remove,
};
module_platform_driver(tps6507x_ts_driver);
+++ /dev/null
-/*
- * drivers/input/touchscreen/tsc2007.c
- *
- * Copyright (c) 2008 MtekVision Co., Ltd.
- * Kwangwoo Lee <kwlee@mtekvision.com>
- *
- * Using code from:
- * - ads7846.c
- * Copyright (c) 2005 David Brownell
- * Copyright (c) 2006 Nokia Corporation
- * - corgi_ts.c
- * Copyright (C) 2004-2005 Richard Purdie
- * - omap_ts.[hc], ads7846.h, ts_osk.c
- * Copyright (C) 2002 MontaVista Software
- * Copyright (C) 2004 Texas Instruments
- * Copyright (C) 2005 Dirk Behme
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/input.h>
-#include <linux/interrupt.h>
-#include <linux/i2c.h>
-#include <linux/i2c/tsc2007.h>
-#include <linux/of_device.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
-
-#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
-#define TSC2007_MEASURE_AUX (0x2 << 4)
-#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
-#define TSC2007_ACTIVATE_XN (0x8 << 4)
-#define TSC2007_ACTIVATE_YN (0x9 << 4)
-#define TSC2007_ACTIVATE_YP_XN (0xa << 4)
-#define TSC2007_SETUP (0xb << 4)
-#define TSC2007_MEASURE_X (0xc << 4)
-#define TSC2007_MEASURE_Y (0xd << 4)
-#define TSC2007_MEASURE_Z1 (0xe << 4)
-#define TSC2007_MEASURE_Z2 (0xf << 4)
-
-#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
-#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
-#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
-#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
-
-#define TSC2007_12BIT (0x0 << 1)
-#define TSC2007_8BIT (0x1 << 1)
-
-#define MAX_12BIT ((1 << 12) - 1)
-
-#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
-
-#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
-#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
-#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
-#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
-#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
-
-struct ts_event {
- u16 x;
- u16 y;
- u16 z1, z2;
-};
-
-struct tsc2007 {
- struct input_dev *input;
- char phys[32];
-
- struct i2c_client *client;
-
- u16 model;
- u16 x_plate_ohms;
- u16 max_rt;
- unsigned long poll_period; /* in jiffies */
- int fuzzx;
- int fuzzy;
- int fuzzz;
-
- unsigned gpio;
- int irq;
-
- wait_queue_head_t wait;
- bool stopped;
-
- int (*get_pendown_state)(struct device *);
- void (*clear_penirq)(void);
-};
-
-static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
-{
- s32 data;
- u16 val;
-
- data = i2c_smbus_read_word_data(tsc->client, cmd);
- if (data < 0) {
- dev_err(&tsc->client->dev, "i2c io error: %d\n", data);
- return data;
- }
-
- /* The protocol and raw data format from i2c interface:
- * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
- * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].
- */
- val = swab16(data) >> 4;
-
- dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);
-
- return val;
-}
-
-static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
-{
- /* y- still on; turn on only y+ (and ADC) */
- tc->y = tsc2007_xfer(tsc, READ_Y);
-
- /* turn y- off, x+ on, then leave in lowpower */
- tc->x = tsc2007_xfer(tsc, READ_X);
-
- /* turn y+ off, x- on; we'll use formula #1 */
- tc->z1 = tsc2007_xfer(tsc, READ_Z1);
- tc->z2 = tsc2007_xfer(tsc, READ_Z2);
-
- /* Prepare for next touch reading - power down ADC, enable PENIRQ */
- tsc2007_xfer(tsc, PWRDOWN);
-}
-
-static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
-{
- u32 rt = 0;
-
- /* range filtering */
- if (tc->x == MAX_12BIT)
- tc->x = 0;
-
- if (likely(tc->x && tc->z1)) {
- /* compute touch pressure resistance using equation #1 */
- rt = tc->z2 - tc->z1;
- rt *= tc->x;
- rt *= tsc->x_plate_ohms;
- rt /= tc->z1;
- rt = (rt + 2047) >> 12;
- }
-
- return rt;
-}
-
-static bool tsc2007_is_pen_down(struct tsc2007 *ts)
-{
- /*
- * NOTE: We can't rely on the pressure to determine the pen down
- * state, even though this controller has a pressure sensor.
- * The pressure value can fluctuate for quite a while after
- * lifting the pen and in some cases may not even settle at the
- * expected value.
- *
- * The only safe way to check for the pen up condition is in the
- * work function by reading the pen signal state (it's a GPIO
- * and IRQ). Unfortunately such callback is not always available,
- * in that case we assume that the pen is down and expect caller
- * to fall back on the pressure reading.
- */
-
- if (!ts->get_pendown_state)
- return true;
-
- return ts->get_pendown_state(&ts->client->dev);
-}
-
-static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
-{
- struct tsc2007 *ts = handle;
- struct input_dev *input = ts->input;
- struct ts_event tc;
- u32 rt;
-
- while (!ts->stopped && tsc2007_is_pen_down(ts)) {
-
- /* pen is down, continue with the measurement */
- tsc2007_read_values(ts, &tc);
-
- rt = tsc2007_calculate_pressure(ts, &tc);
-
- if (!rt && !ts->get_pendown_state) {
- /*
- * If pressure reported is 0 and we don't have
- * callback to check pendown state, we have to
- * assume that pen was lifted up.
- */
- break;
- }
-
- if (rt <= ts->max_rt) {
- dev_dbg(&ts->client->dev,
- "DOWN point(%4d,%4d), pressure (%4u)\n",
- tc.x, tc.y, rt);
-
- input_report_key(input, BTN_TOUCH, 1);
- input_report_abs(input, ABS_X, tc.x);
- input_report_abs(input, ABS_Y, tc.y);
- input_report_abs(input, ABS_PRESSURE, rt);
-
- input_sync(input);
-
- } else {
- /*
- * Sample found inconsistent by debouncing or pressure is
- * beyond the maximum. Don't report it to user space,
- * repeat at least once more the measurement.
- */
- dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
- }
-
- wait_event_timeout(ts->wait, ts->stopped, ts->poll_period);
- }
-
- dev_dbg(&ts->client->dev, "UP\n");
-
- input_report_key(input, BTN_TOUCH, 0);
- input_report_abs(input, ABS_PRESSURE, 0);
- input_sync(input);
-
- if (ts->clear_penirq)
- ts->clear_penirq();
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
-{
- struct tsc2007 *ts = handle;
-
- if (tsc2007_is_pen_down(ts))
- return IRQ_WAKE_THREAD;
-
- if (ts->clear_penirq)
- ts->clear_penirq();
-
- return IRQ_HANDLED;
-}
-
-static void tsc2007_stop(struct tsc2007 *ts)
-{
- ts->stopped = true;
- mb();
- wake_up(&ts->wait);
-
- disable_irq(ts->irq);
-}
-
-static int tsc2007_open(struct input_dev *input_dev)
-{
- struct tsc2007 *ts = input_get_drvdata(input_dev);
- int err;
-
- ts->stopped = false;
- mb();
-
- enable_irq(ts->irq);
-
- /* Prepare for touch readings - power down ADC and enable PENIRQ */
- err = tsc2007_xfer(ts, PWRDOWN);
- if (err < 0) {
- tsc2007_stop(ts);
- return err;
- }
-
- return 0;
-}
-
-static void tsc2007_close(struct input_dev *input_dev)
-{
- struct tsc2007 *ts = input_get_drvdata(input_dev);
-
- tsc2007_stop(ts);
-}
-
-#ifdef CONFIG_OF
-static int tsc2007_get_pendown_state_gpio(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct tsc2007 *ts = i2c_get_clientdata(client);
-
- return !gpio_get_value(ts->gpio);
-}
-
-static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
-{
- struct device_node *np = client->dev.of_node;
- u32 val32;
- u64 val64;
-
- if (!np) {
- dev_err(&client->dev, "missing device tree data\n");
- return -EINVAL;
- }
-
- if (!of_property_read_u32(np, "ti,max-rt", &val32))
- ts->max_rt = val32;
- else
- ts->max_rt = MAX_12BIT;
-
- if (!of_property_read_u32(np, "ti,fuzzx", &val32))
- ts->fuzzx = val32;
-
- if (!of_property_read_u32(np, "ti,fuzzy", &val32))
- ts->fuzzy = val32;
-
- if (!of_property_read_u32(np, "ti,fuzzz", &val32))
- ts->fuzzz = val32;
-
- if (!of_property_read_u64(np, "ti,poll-period", &val64))
- ts->poll_period = msecs_to_jiffies(val64);
- else
- ts->poll_period = msecs_to_jiffies(1);
-
- if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
- ts->x_plate_ohms = val32;
- } else {
- dev_err(&client->dev, "missing ti,x-plate-ohms devicetree property.");
- return -EINVAL;
- }
-
- ts->gpio = of_get_gpio(np, 0);
- if (gpio_is_valid(ts->gpio))
- ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
- else
- dev_warn(&client->dev,
- "GPIO not specified in DT (of_get_gpio returned %d)\n",
- ts->gpio);
-
- return 0;
-}
-#else
-static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
-{
- dev_err(&client->dev, "platform data is required!\n");
- return -EINVAL;
-}
-#endif
-
-static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
- const struct tsc2007_platform_data *pdata,
- const struct i2c_device_id *id)
-{
- ts->model = pdata->model;
- ts->x_plate_ohms = pdata->x_plate_ohms;
- ts->max_rt = pdata->max_rt ? : MAX_12BIT;
- ts->poll_period = msecs_to_jiffies(pdata->poll_period ? : 1);
- ts->get_pendown_state = pdata->get_pendown_state;
- ts->clear_penirq = pdata->clear_penirq;
- ts->fuzzx = pdata->fuzzx;
- ts->fuzzy = pdata->fuzzy;
- ts->fuzzz = pdata->fuzzz;
-
- if (pdata->x_plate_ohms == 0) {
- dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void tsc2007_call_exit_platform_hw(void *data)
-{
- struct device *dev = data;
- const struct tsc2007_platform_data *pdata = dev_get_platdata(dev);
-
- pdata->exit_platform_hw();
-}
-
-static int tsc2007_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
- struct tsc2007 *ts;
- struct input_dev *input_dev;
- int err;
-
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_READ_WORD_DATA))
- return -EIO;
-
- ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
- if (!ts)
- return -ENOMEM;
-
- if (pdata)
- err = tsc2007_probe_pdev(client, ts, pdata, id);
- else
- err = tsc2007_probe_dt(client, ts);
- if (err)
- return err;
-
- input_dev = devm_input_allocate_device(&client->dev);
- if (!input_dev)
- return -ENOMEM;
-
- i2c_set_clientdata(client, ts);
-
- ts->client = client;
- ts->irq = client->irq;
- ts->input = input_dev;
- init_waitqueue_head(&ts->wait);
-
- snprintf(ts->phys, sizeof(ts->phys),
- "%s/input0", dev_name(&client->dev));
-
- input_dev->name = "TSC2007 Touchscreen";
- input_dev->phys = ts->phys;
- input_dev->id.bustype = BUS_I2C;
-
- input_dev->open = tsc2007_open;
- input_dev->close = tsc2007_close;
-
- input_set_drvdata(input_dev, ts);
-
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
-
- input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, ts->fuzzx, 0);
- input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,
- ts->fuzzz, 0);
-
- if (pdata) {
- if (pdata->exit_platform_hw) {
- err = devm_add_action(&client->dev,
- tsc2007_call_exit_platform_hw,
- &client->dev);
- if (err) {
- dev_err(&client->dev,
- "Failed to register exit_platform_hw action, %d\n",
- err);
- return err;
- }
- }
-
- if (pdata->init_platform_hw)
- pdata->init_platform_hw();
- }
-
- err = devm_request_threaded_irq(&client->dev, ts->irq,
- tsc2007_hard_irq, tsc2007_soft_irq,
- IRQF_ONESHOT,
- client->dev.driver->name, ts);
- if (err) {
- dev_err(&client->dev, "Failed to request irq %d: %d\n",
- ts->irq, err);
- return err;
- }
-
- tsc2007_stop(ts);
-
- err = input_register_device(input_dev);
- if (err) {
- dev_err(&client->dev,
- "Failed to register input device: %d\n", err);
- return err;
- }
-
- return 0;
-}
-
-static const struct i2c_device_id tsc2007_idtable[] = {
- { "tsc2007", 0 },
- { }
-};
-
-MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
-
-#ifdef CONFIG_OF
-static const struct of_device_id tsc2007_of_match[] = {
- { .compatible = "ti,tsc2007" },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, tsc2007_of_match);
-#endif
-
-static struct i2c_driver tsc2007_driver = {
- .driver = {
- .name = "tsc2007",
- .of_match_table = of_match_ptr(tsc2007_of_match),
- },
- .id_table = tsc2007_idtable,
- .probe = tsc2007_probe,
-};
-
-module_i2c_driver(tsc2007_driver);
-
-MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>");
-MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");
-MODULE_LICENSE("GPL");
--- /dev/null
+
+/*
+ * Copyright (c) 2008 MtekVision Co., Ltd.
+ * Kwangwoo Lee <kwlee@mtekvision.com>
+ *
+ * Using code from:
+ * - ads7846.c
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * - corgi_ts.c
+ * Copyright (C) 2004-2005 Richard Purdie
+ * - omap_ts.[hc], ads7846.h, ts_osk.c
+ * Copyright (C) 2002 MontaVista Software
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2005 Dirk Behme
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _TSC2007_H
+#define _TSC2007_H
+
+#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
+#define TSC2007_MEASURE_AUX (0x2 << 4)
+#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
+#define TSC2007_ACTIVATE_XN (0x8 << 4)
+#define TSC2007_ACTIVATE_YN (0x9 << 4)
+#define TSC2007_ACTIVATE_YP_XN (0xa << 4)
+#define TSC2007_SETUP (0xb << 4)
+#define TSC2007_MEASURE_X (0xc << 4)
+#define TSC2007_MEASURE_Y (0xd << 4)
+#define TSC2007_MEASURE_Z1 (0xe << 4)
+#define TSC2007_MEASURE_Z2 (0xf << 4)
+
+#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
+#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
+#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
+#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
+
+#define TSC2007_12BIT (0x0 << 1)
+#define TSC2007_8BIT (0x1 << 1)
+
+#define MAX_12BIT ((1 << 12) - 1)
+
+#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
+
+#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
+#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
+#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
+#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
+#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
+
+struct ts_event {
+ u16 x;
+ u16 y;
+ u16 z1, z2;
+};
+
+struct tsc2007 {
+ struct input_dev *input;
+ char phys[32];
+
+ struct i2c_client *client;
+
+ u16 model;
+ u16 x_plate_ohms;
+ u16 max_rt;
+ unsigned long poll_period; /* in jiffies */
+ int fuzzx;
+ int fuzzy;
+ int fuzzz;
+
+ unsigned int gpio;
+ int irq;
+
+ wait_queue_head_t wait;
+ bool stopped;
+
+ int (*get_pendown_state)(struct device *);
+ void (*clear_penirq)(void);
+
+ struct mutex mlock;
+};
+
+int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd);
+u32 tsc2007_calculate_resistance(struct tsc2007 *tsc, struct ts_event *tc);
+bool tsc2007_is_pen_down(struct tsc2007 *ts);
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TSC2007_IIO)
+/* defined in tsc2007_iio.c */
+int tsc2007_iio_configure(struct tsc2007 *ts);
+#else
+static inline int tsc2007_iio_configure(struct tsc2007 *ts)
+{
+ return 0;
+}
+#endif /* CONFIG_TOUCHSCREEN_TSC2007_IIO */
+
+#endif /* _TSC2007_H */
--- /dev/null
+/*
+ * drivers/input/touchscreen/tsc2007.c
+ *
+ * Copyright (c) 2008 MtekVision Co., Ltd.
+ * Kwangwoo Lee <kwlee@mtekvision.com>
+ *
+ * Using code from:
+ * - ads7846.c
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * - corgi_ts.c
+ * Copyright (C) 2004-2005 Richard Purdie
+ * - omap_ts.[hc], ads7846.h, ts_osk.c
+ * Copyright (C) 2002 MontaVista Software
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2005 Dirk Behme
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/i2c/tsc2007.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include "tsc2007.h"
+
+int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
+{
+ s32 data;
+ u16 val;
+
+ data = i2c_smbus_read_word_data(tsc->client, cmd);
+ if (data < 0) {
+ dev_err(&tsc->client->dev, "i2c io error: %d\n", data);
+ return data;
+ }
+
+ /* The protocol and raw data format from i2c interface:
+ * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
+ * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].
+ */
+ val = swab16(data) >> 4;
+
+ dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);
+
+ return val;
+}
+
+static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
+{
+ /* y- still on; turn on only y+ (and ADC) */
+ tc->y = tsc2007_xfer(tsc, READ_Y);
+
+ /* turn y- off, x+ on, then leave in lowpower */
+ tc->x = tsc2007_xfer(tsc, READ_X);
+
+ /* turn y+ off, x- on; we'll use formula #1 */
+ tc->z1 = tsc2007_xfer(tsc, READ_Z1);
+ tc->z2 = tsc2007_xfer(tsc, READ_Z2);
+
+ /* Prepare for next touch reading - power down ADC, enable PENIRQ */
+ tsc2007_xfer(tsc, PWRDOWN);
+}
+
+u32 tsc2007_calculate_resistance(struct tsc2007 *tsc, struct ts_event *tc)
+{
+ u32 rt = 0;
+
+ /* range filtering */
+ if (tc->x == MAX_12BIT)
+ tc->x = 0;
+
+ if (likely(tc->x && tc->z1)) {
+ /* compute touch resistance using equation #1 */
+ rt = tc->z2 - tc->z1;
+ rt *= tc->x;
+ rt *= tsc->x_plate_ohms;
+ rt /= tc->z1;
+ rt = (rt + 2047) >> 12;
+ }
+
+ return rt;
+}
+
+bool tsc2007_is_pen_down(struct tsc2007 *ts)
+{
+ /*
+ * NOTE: We can't rely on the pressure to determine the pen down
+ * state, even though this controller has a pressure sensor.
+ * The pressure value can fluctuate for quite a while after
+ * lifting the pen and in some cases may not even settle at the
+ * expected value.
+ *
+ * The only safe way to check for the pen up condition is in the
+ * work function by reading the pen signal state (it's a GPIO
+ * and IRQ). Unfortunately such callback is not always available,
+ * in that case we assume that the pen is down and expect caller
+ * to fall back on the pressure reading.
+ */
+
+ if (!ts->get_pendown_state)
+ return true;
+
+ return ts->get_pendown_state(&ts->client->dev);
+}
+
+static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
+{
+ struct tsc2007 *ts = handle;
+ struct input_dev *input = ts->input;
+ struct ts_event tc;
+ u32 rt;
+
+ while (!ts->stopped && tsc2007_is_pen_down(ts)) {
+
+ /* pen is down, continue with the measurement */
+
+ mutex_lock(&ts->mlock);
+ tsc2007_read_values(ts, &tc);
+ mutex_unlock(&ts->mlock);
+
+ rt = tsc2007_calculate_resistance(ts, &tc);
+
+ if (!rt && !ts->get_pendown_state) {
+ /*
+ * If pressure reported is 0 and we don't have
+ * callback to check pendown state, we have to
+ * assume that pen was lifted up.
+ */
+ break;
+ }
+
+ if (rt <= ts->max_rt) {
+ dev_dbg(&ts->client->dev,
+ "DOWN point(%4d,%4d), resistance (%4u)\n",
+ tc.x, tc.y, rt);
+
+ rt = ts->max_rt - rt;
+
+ input_report_key(input, BTN_TOUCH, 1);
+ input_report_abs(input, ABS_X, tc.x);
+ input_report_abs(input, ABS_Y, tc.y);
+ input_report_abs(input, ABS_PRESSURE, rt);
+
+ input_sync(input);
+
+ } else {
+ /*
+ * Sample found inconsistent by debouncing or pressure is
+ * beyond the maximum. Don't report it to user space,
+ * repeat at least once more the measurement.
+ */
+ dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
+ }
+
+ wait_event_timeout(ts->wait, ts->stopped, ts->poll_period);
+ }
+
+ dev_dbg(&ts->client->dev, "UP\n");
+
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_sync(input);
+
+ if (ts->clear_penirq)
+ ts->clear_penirq();
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
+{
+ struct tsc2007 *ts = handle;
+
+ if (tsc2007_is_pen_down(ts))
+ return IRQ_WAKE_THREAD;
+
+ if (ts->clear_penirq)
+ ts->clear_penirq();
+
+ return IRQ_HANDLED;
+}
+
+static void tsc2007_stop(struct tsc2007 *ts)
+{
+ ts->stopped = true;
+ mb();
+ wake_up(&ts->wait);
+
+ disable_irq(ts->irq);
+}
+
+static int tsc2007_open(struct input_dev *input_dev)
+{
+ struct tsc2007 *ts = input_get_drvdata(input_dev);
+ int err;
+
+ ts->stopped = false;
+ mb();
+
+ enable_irq(ts->irq);
+
+ /* Prepare for touch readings - power down ADC and enable PENIRQ */
+ err = tsc2007_xfer(ts, PWRDOWN);
+ if (err < 0) {
+ tsc2007_stop(ts);
+ return err;
+ }
+
+ return 0;
+}
+
+static void tsc2007_close(struct input_dev *input_dev)
+{
+ struct tsc2007 *ts = input_get_drvdata(input_dev);
+
+ tsc2007_stop(ts);
+}
+
+#ifdef CONFIG_OF
+static int tsc2007_get_pendown_state_gpio(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tsc2007 *ts = i2c_get_clientdata(client);
+
+ return !gpio_get_value(ts->gpio);
+}
+
+static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
+{
+ struct device_node *np = client->dev.of_node;
+ u32 val32;
+ u64 val64;
+
+ if (!np) {
+ dev_err(&client->dev, "missing device tree data\n");
+ return -EINVAL;
+ }
+
+ if (!of_property_read_u32(np, "ti,max-rt", &val32))
+ ts->max_rt = val32;
+ else
+ ts->max_rt = MAX_12BIT;
+
+ if (!of_property_read_u32(np, "ti,fuzzx", &val32))
+ ts->fuzzx = val32;
+
+ if (!of_property_read_u32(np, "ti,fuzzy", &val32))
+ ts->fuzzy = val32;
+
+ if (!of_property_read_u32(np, "ti,fuzzz", &val32))
+ ts->fuzzz = val32;
+
+ if (!of_property_read_u64(np, "ti,poll-period", &val64))
+ ts->poll_period = msecs_to_jiffies(val64);
+ else
+ ts->poll_period = msecs_to_jiffies(1);
+
+ if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
+ ts->x_plate_ohms = val32;
+ } else {
+ dev_err(&client->dev, "missing ti,x-plate-ohms devicetree property.");
+ return -EINVAL;
+ }
+
+ ts->gpio = of_get_gpio(np, 0);
+ if (gpio_is_valid(ts->gpio))
+ ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
+ else
+ dev_warn(&client->dev,
+ "GPIO not specified in DT (of_get_gpio returned %d)\n",
+ ts->gpio);
+
+ return 0;
+}
+#else
+static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
+{
+ dev_err(&client->dev, "platform data is required!\n");
+ return -EINVAL;
+}
+#endif
+
+static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
+ const struct tsc2007_platform_data *pdata,
+ const struct i2c_device_id *id)
+{
+ ts->model = pdata->model;
+ ts->x_plate_ohms = pdata->x_plate_ohms;
+ ts->max_rt = pdata->max_rt ? : MAX_12BIT;
+ ts->poll_period = msecs_to_jiffies(pdata->poll_period ? : 1);
+ ts->get_pendown_state = pdata->get_pendown_state;
+ ts->clear_penirq = pdata->clear_penirq;
+ ts->fuzzx = pdata->fuzzx;
+ ts->fuzzy = pdata->fuzzy;
+ ts->fuzzz = pdata->fuzzz;
+
+ if (pdata->x_plate_ohms == 0) {
+ dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void tsc2007_call_exit_platform_hw(void *data)
+{
+ struct device *dev = data;
+ const struct tsc2007_platform_data *pdata = dev_get_platdata(dev);
+
+ pdata->exit_platform_hw();
+}
+
+static int tsc2007_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct tsc2007_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+ struct tsc2007 *ts;
+ struct input_dev *input_dev;
+ int err;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -EIO;
+
+ ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ if (pdata)
+ err = tsc2007_probe_pdev(client, ts, pdata, id);
+ else
+ err = tsc2007_probe_dt(client, ts);
+ if (err)
+ return err;
+
+ input_dev = devm_input_allocate_device(&client->dev);
+ if (!input_dev)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, ts);
+
+ ts->client = client;
+ ts->irq = client->irq;
+ ts->input = input_dev;
+
+ init_waitqueue_head(&ts->wait);
+ mutex_init(&ts->mlock);
+
+ snprintf(ts->phys, sizeof(ts->phys),
+ "%s/input0", dev_name(&client->dev));
+
+ input_dev->name = "TSC2007 Touchscreen";
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = BUS_I2C;
+
+ input_dev->open = tsc2007_open;
+ input_dev->close = tsc2007_close;
+
+ input_set_drvdata(input_dev, ts);
+
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, ts->fuzzx, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,
+ ts->fuzzz, 0);
+
+ if (pdata) {
+ if (pdata->exit_platform_hw) {
+ err = devm_add_action(&client->dev,
+ tsc2007_call_exit_platform_hw,
+ &client->dev);
+ if (err) {
+ dev_err(&client->dev,
+ "Failed to register exit_platform_hw action, %d\n",
+ err);
+ return err;
+ }
+ }
+
+ if (pdata->init_platform_hw)
+ pdata->init_platform_hw();
+ }
+
+ err = devm_request_threaded_irq(&client->dev, ts->irq,
+ tsc2007_hard_irq, tsc2007_soft_irq,
+ IRQF_ONESHOT,
+ client->dev.driver->name, ts);
+ if (err) {
+ dev_err(&client->dev, "Failed to request irq %d: %d\n",
+ ts->irq, err);
+ return err;
+ }
+
+ tsc2007_stop(ts);
+
+ /* power down the chip (TSC2007_SETUP does not ACK on I2C) */
+ err = tsc2007_xfer(ts, PWRDOWN);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "Failed to setup chip: %d\n", err);
+ return err; /* chip does not respond */
+ }
+
+ err = input_register_device(input_dev);
+ if (err) {
+ dev_err(&client->dev,
+ "Failed to register input device: %d\n", err);
+ return err;
+ }
+
+ err = tsc2007_iio_configure(ts);
+ if (err) {
+ dev_err(&client->dev,
+ "Failed to register with IIO: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id tsc2007_idtable[] = {
+ { "tsc2007", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
+
+#ifdef CONFIG_OF
+static const struct of_device_id tsc2007_of_match[] = {
+ { .compatible = "ti,tsc2007" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tsc2007_of_match);
+#endif
+
+static struct i2c_driver tsc2007_driver = {
+ .driver = {
+ .name = "tsc2007",
+ .of_match_table = of_match_ptr(tsc2007_of_match),
+ },
+ .id_table = tsc2007_idtable,
+ .probe = tsc2007_probe,
+};
+
+module_i2c_driver(tsc2007_driver);
+
+MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>");
+MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Copyright (c) 2016 Golden Delicious Comp. GmbH&Co. KG
+ * Nikolaus Schaller <hns@goldelico.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include "tsc2007.h"
+
+struct tsc2007_iio {
+ struct tsc2007 *ts;
+};
+
+#define TSC2007_CHAN_IIO(_chan, _name, _type, _chan_info) \
+{ \
+ .datasheet_name = _name, \
+ .type = _type, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(_chan_info), \
+ .indexed = 1, \
+ .channel = _chan, \
+}
+
+static const struct iio_chan_spec tsc2007_iio_channel[] = {
+ TSC2007_CHAN_IIO(0, "x", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+ TSC2007_CHAN_IIO(1, "y", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+ TSC2007_CHAN_IIO(2, "z1", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+ TSC2007_CHAN_IIO(3, "z2", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+ TSC2007_CHAN_IIO(4, "adc", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+ TSC2007_CHAN_IIO(5, "rt", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), /* Ohms? */
+ TSC2007_CHAN_IIO(6, "pen", IIO_PRESSURE, IIO_CHAN_INFO_RAW),
+ TSC2007_CHAN_IIO(7, "temp0", IIO_TEMP, IIO_CHAN_INFO_RAW),
+ TSC2007_CHAN_IIO(8, "temp1", IIO_TEMP, IIO_CHAN_INFO_RAW),
+};
+
+static int tsc2007_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct tsc2007_iio *iio = iio_priv(indio_dev);
+ struct tsc2007 *tsc = iio->ts;
+ int adc_chan = chan->channel;
+ int ret = 0;
+
+ if (adc_chan >= ARRAY_SIZE(tsc2007_iio_channel))
+ return -EINVAL;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ mutex_lock(&tsc->mlock);
+
+ switch (chan->channel) {
+ case 0:
+ *val = tsc2007_xfer(tsc, READ_X);
+ break;
+ case 1:
+ *val = tsc2007_xfer(tsc, READ_Y);
+ break;
+ case 2:
+ *val = tsc2007_xfer(tsc, READ_Z1);
+ break;
+ case 3:
+ *val = tsc2007_xfer(tsc, READ_Z2);
+ break;
+ case 4:
+ *val = tsc2007_xfer(tsc, (ADC_ON_12BIT | TSC2007_MEASURE_AUX));
+ break;
+ case 5: {
+ struct ts_event tc;
+
+ tc.x = tsc2007_xfer(tsc, READ_X);
+ tc.z1 = tsc2007_xfer(tsc, READ_Z1);
+ tc.z2 = tsc2007_xfer(tsc, READ_Z2);
+ *val = tsc2007_calculate_resistance(tsc, &tc);
+ break;
+ }
+ case 6:
+ *val = tsc2007_is_pen_down(tsc);
+ break;
+ case 7:
+ *val = tsc2007_xfer(tsc,
+ (ADC_ON_12BIT | TSC2007_MEASURE_TEMP0));
+ break;
+ case 8:
+ *val = tsc2007_xfer(tsc,
+ (ADC_ON_12BIT | TSC2007_MEASURE_TEMP1));
+ break;
+ }
+
+ /* Prepare for next touch reading - power down ADC, enable PENIRQ */
+ tsc2007_xfer(tsc, PWRDOWN);
+
+ mutex_unlock(&tsc->mlock);
+
+ ret = IIO_VAL_INT;
+
+ return ret;
+}
+
+static const struct iio_info tsc2007_iio_info = {
+ .read_raw = tsc2007_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+int tsc2007_iio_configure(struct tsc2007 *ts)
+{
+ struct iio_dev *indio_dev;
+ struct tsc2007_iio *iio;
+ int error;
+
+ indio_dev = devm_iio_device_alloc(&ts->client->dev, sizeof(*iio));
+ if (!indio_dev) {
+ dev_err(&ts->client->dev, "iio_device_alloc failed\n");
+ return -ENOMEM;
+ }
+
+ iio = iio_priv(indio_dev);
+ iio->ts = ts;
+
+ indio_dev->name = "tsc2007";
+ indio_dev->dev.parent = &ts->client->dev;
+ indio_dev->info = &tsc2007_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = tsc2007_iio_channel;
+ indio_dev->num_channels = ARRAY_SIZE(tsc2007_iio_channel);
+
+ error = devm_iio_device_register(&ts->client->dev, indio_dev);
+ if (error) {
+ dev_err(&ts->client->dev,
+ "iio_device_register() failed: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
* @wakeup: controls whether the device should be set up as wakeup
* source
* @no_autorepeat: disable key autorepeat
+ * @drive_inactive_cols: drive inactive columns during scan, rather than
+ * making them inputs.
*
* This structure represents platform-specific data that use used by
* matrix_keypad driver to perform proper initialization.
bool active_low;
bool wakeup;
bool no_autorepeat;
+ bool drive_inactive_cols;
};
int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data,
/* The state of the switches have changed. */
EC_MKBP_EVENT_SWITCH = 4,
+ /* EC sent a sysrq command */
+ EC_MKBP_EVENT_SYSRQ = 6,
+
/* Number of MKBP events */
EC_MKBP_EVENT_COUNT,
};
uint32_t buttons;
uint32_t switches;
+ uint32_t sysrq;
} __packed;
struct ec_response_get_next_event {
irqreturn_t (*interrupt)(struct serio *, unsigned char, unsigned int);
int (*connect)(struct serio *, struct serio_driver *drv);
int (*reconnect)(struct serio *);
+ int (*fast_reconnect)(struct serio *);
void (*disconnect)(struct serio *);
void (*cleanup)(struct serio *);