in touchscreen/elants_i2c.c [1416:1585]
static int elants_i2c_probe(struct i2c_client *client)
{
union i2c_smbus_data dummy;
struct elants_data *ts;
unsigned long irqflags;
int error;
/* Don't bind to i2c-hid compatible devices, these are handled by the i2c-hid drv. */
if (elants_acpi_is_hid_device(&client->dev)) {
dev_warn(&client->dev, "This device appears to be an I2C-HID device, not binding\n");
return -ENODEV;
}
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "I2C check functionality error\n");
return -ENXIO;
}
ts = devm_kzalloc(&client->dev, sizeof(struct elants_data), GFP_KERNEL);
if (!ts)
return -ENOMEM;
mutex_init(&ts->sysfs_mutex);
init_completion(&ts->cmd_done);
ts->client = client;
ts->chip_id = (enum elants_chip_id)(uintptr_t)device_get_match_data(&client->dev);
i2c_set_clientdata(client, ts);
ts->vcc33 = devm_regulator_get(&client->dev, "vcc33");
if (IS_ERR(ts->vcc33)) {
error = PTR_ERR(ts->vcc33);
if (error != -EPROBE_DEFER)
dev_err(&client->dev,
"Failed to get 'vcc33' regulator: %d\n",
error);
return error;
}
ts->vccio = devm_regulator_get(&client->dev, "vccio");
if (IS_ERR(ts->vccio)) {
error = PTR_ERR(ts->vccio);
if (error != -EPROBE_DEFER)
dev_err(&client->dev,
"Failed to get 'vccio' regulator: %d\n",
error);
return error;
}
ts->reset_gpio = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ts->reset_gpio)) {
error = PTR_ERR(ts->reset_gpio);
if (error == -EPROBE_DEFER)
return error;
if (error != -ENOENT && error != -ENOSYS) {
dev_err(&client->dev,
"failed to get reset gpio: %d\n",
error);
return error;
}
ts->keep_power_in_suspend = true;
}
error = elants_i2c_power_on(ts);
if (error)
return error;
error = devm_add_action_or_reset(&client->dev,
elants_i2c_power_off, ts);
if (error) {
dev_err(&client->dev,
"failed to install power off action: %d\n", error);
return error;
}
/* Make sure there is something at this address */
if (i2c_smbus_xfer(client->adapter, client->addr, 0,
I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
dev_err(&client->dev, "nothing at this address\n");
return -ENXIO;
}
error = elants_i2c_initialize(ts);
if (error) {
dev_err(&client->dev, "failed to initialize: %d\n", error);
return error;
}
ts->input = devm_input_allocate_device(&client->dev);
if (!ts->input) {
dev_err(&client->dev, "Failed to allocate input device\n");
return -ENOMEM;
}
ts->input->name = "Elan Touchscreen";
ts->input->id.bustype = BUS_I2C;
/* Multitouch input params setup */
input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->x_max, 0, 0);
input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->y_max, 0, 0);
input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(ts->input, ABS_MT_TOOL_TYPE,
0, MT_TOOL_PALM, 0, 0);
touchscreen_parse_properties(ts->input, true, &ts->prop);
if (ts->chip_id == EKTF3624 && ts->phy_x && ts->phy_y) {
/* calculate resolution from size */
ts->x_res = DIV_ROUND_CLOSEST(ts->prop.max_x, ts->phy_x);
ts->y_res = DIV_ROUND_CLOSEST(ts->prop.max_y, ts->phy_y);
}
input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->x_res);
input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->y_res);
input_abs_set_res(ts->input, ABS_MT_TOUCH_MAJOR, ts->major_res);
error = input_mt_init_slots(ts->input, MAX_CONTACT_NUM,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
if (error) {
dev_err(&client->dev,
"failed to initialize MT slots: %d\n", error);
return error;
}
error = input_register_device(ts->input);
if (error) {
dev_err(&client->dev,
"unable to register input device: %d\n", error);
return error;
}
/*
* Platform code (ACPI, DTS) should normally set up interrupt
* for us, but in case it did not let's fall back to using falling
* edge to be compatible with older Chromebooks.
*/
irqflags = irq_get_trigger_type(client->irq);
if (!irqflags)
irqflags = IRQF_TRIGGER_FALLING;
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, elants_i2c_irq,
irqflags | IRQF_ONESHOT,
client->name, ts);
if (error) {
dev_err(&client->dev, "Failed to register interrupt\n");
return error;
}
/*
* Systems using device tree should set up wakeup via DTS,
* the rest will configure device as wakeup source by default.
*/
if (!client->dev.of_node)
device_init_wakeup(&client->dev, true);
error = devm_device_add_group(&client->dev, &elants_attribute_group);
if (error) {
dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
error);
return error;
}
return 0;
}