diff options
Diffstat (limited to 'drivers/input')
49 files changed, 4742 insertions, 1330 deletions
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index fd4100d56d8c..6727954ab74b 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -76,10 +76,13 @@ */ #include <linux/kernel.h> +#include <linux/input.h> +#include <linux/rcupdate.h> #include <linux/slab.h> #include <linux/stat.h> #include <linux/module.h> #include <linux/usb/input.h> +#include <linux/usb/quirks.h> #define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>" #define DRIVER_DESC "X-Box pad driver" @@ -125,7 +128,7 @@ static const struct xpad_device { { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", 0, XTYPE_XBOX }, { 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 }, { 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE }, - { 0x045e, 0x02dd, "Microsoft X-Box One pad (Covert Forces)", 0, XTYPE_XBOXONE }, + { 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE }, { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX }, @@ -317,21 +320,42 @@ static struct usb_device_id xpad_table[] = { MODULE_DEVICE_TABLE(usb, xpad_table); +struct xpad_output_packet { + u8 data[XPAD_PKT_LEN]; + u8 len; + bool pending; +}; + +#define XPAD_OUT_CMD_IDX 0 +#define XPAD_OUT_FF_IDX 1 +#define XPAD_OUT_LED_IDX (1 + IS_ENABLED(CONFIG_JOYSTICK_XPAD_FF)) +#define XPAD_NUM_OUT_PACKETS (1 + \ + IS_ENABLED(CONFIG_JOYSTICK_XPAD_FF) + \ + IS_ENABLED(CONFIG_JOYSTICK_XPAD_LEDS)) + struct usb_xpad { struct input_dev *dev; /* input device interface */ + struct input_dev __rcu *x360w_dev; struct usb_device *udev; /* usb device */ struct usb_interface *intf; /* usb interface */ - int pad_present; + bool pad_present; + bool input_created; struct urb *irq_in; /* urb for interrupt in report */ unsigned char *idata; /* input data */ dma_addr_t idata_dma; struct urb *irq_out; /* urb for interrupt out report */ + struct usb_anchor irq_out_anchor; + bool irq_out_active; /* we must not use an active URB */ + u8 odata_serial; /* serial number for xbox one protocol */ unsigned char *odata; /* output data */ dma_addr_t odata_dma; - struct mutex odata_mutex; + spinlock_t odata_lock; + + struct xpad_output_packet out_packets[XPAD_NUM_OUT_PACKETS]; + int last_out_packet; #if defined(CONFIG_JOYSTICK_XPAD_LEDS) struct xpad_led *led; @@ -343,8 +367,12 @@ struct usb_xpad { int xtype; /* type of xbox device */ int pad_nr; /* the order x360 pads were attached */ const char *name; /* name of the device */ + struct work_struct work; /* init/remove device from callback */ }; +static int xpad_init_input(struct usb_xpad *xpad); +static void xpad_deinit_input(struct usb_xpad *xpad); + /* * xpad_process_packet * @@ -424,11 +452,9 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d * http://www.free60.org/wiki/Gamepad */ -static void xpad360_process_packet(struct usb_xpad *xpad, +static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev, u16 cmd, unsigned char *data) { - struct input_dev *dev = xpad->dev; - /* digital pad */ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { /* dpad as buttons (left, right, up, down) */ @@ -495,7 +521,30 @@ static void xpad360_process_packet(struct usb_xpad *xpad, input_sync(dev); } -static void xpad_identify_controller(struct usb_xpad *xpad); +static void xpad_presence_work(struct work_struct *work) +{ + struct usb_xpad *xpad = container_of(work, struct usb_xpad, work); + int error; + + if (xpad->pad_present) { + error = xpad_init_input(xpad); + if (error) { + /* complain only, not much else we can do here */ + dev_err(&xpad->dev->dev, + "unable to init device: %d\n", error); + } else { + rcu_assign_pointer(xpad->x360w_dev, xpad->dev); + } + } else { + RCU_INIT_POINTER(xpad->x360w_dev, NULL); + synchronize_rcu(); + /* + * Now that we are sure xpad360w_process_packet is not + * using input device we can get rid of it. + */ + xpad_deinit_input(xpad); + } +} /* * xpad360w_process_packet @@ -513,24 +562,28 @@ static void xpad_identify_controller(struct usb_xpad *xpad); */ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) { + struct input_dev *dev; + bool present; + /* Presence change */ if (data[0] & 0x08) { - if (data[1] & 0x80) { - xpad->pad_present = 1; - /* - * Light up the segment corresponding to - * controller number. - */ - xpad_identify_controller(xpad); - } else - xpad->pad_present = 0; + present = (data[1] & 0x80) != 0; + + if (xpad->pad_present != present) { + xpad->pad_present = present; + schedule_work(&xpad->work); + } } /* Valid pad data */ - if (!(data[1] & 0x1)) + if (data[1] != 0x1) return; - xpad360_process_packet(xpad, cmd, &data[4]); + rcu_read_lock(); + dev = rcu_dereference(xpad->x360w_dev); + if (dev) + xpad360_process_packet(xpad, dev, cmd, &data[4]); + rcu_read_unlock(); } /* @@ -659,7 +712,7 @@ static void xpad_irq_in(struct urb *urb) switch (xpad->xtype) { case XTYPE_XBOX360: - xpad360_process_packet(xpad, 0, xpad->idata); + xpad360_process_packet(xpad, xpad->dev, 0, xpad->idata); break; case XTYPE_XBOX360W: xpad360w_process_packet(xpad, 0, xpad->idata); @@ -678,18 +731,73 @@ exit: __func__, retval); } +/* Callers must hold xpad->odata_lock spinlock */ +static bool xpad_prepare_next_out_packet(struct usb_xpad *xpad) +{ + struct xpad_output_packet *pkt, *packet = NULL; + int i; + + for (i = 0; i < XPAD_NUM_OUT_PACKETS; i++) { + if (++xpad->last_out_packet >= XPAD_NUM_OUT_PACKETS) + xpad->last_out_packet = 0; + + pkt = &xpad->out_packets[xpad->last_out_packet]; + if (pkt->pending) { + dev_dbg(&xpad->intf->dev, + "%s - found pending output packet %d\n", + __func__, xpad->last_out_packet); + packet = pkt; + break; + } + } + + if (packet) { + memcpy(xpad->odata, packet->data, packet->len); + xpad->irq_out->transfer_buffer_length = packet->len; + return true; + } + + return false; +} + +/* Callers must hold xpad->odata_lock spinlock */ +static int xpad_try_sending_next_out_packet(struct usb_xpad *xpad) +{ + int error; + + if (!xpad->irq_out_active && xpad_prepare_next_out_packet(xpad)) { + usb_anchor_urb(xpad->irq_out, &xpad->irq_out_anchor); + error = usb_submit_urb(xpad->irq_out, GFP_ATOMIC); + if (error) { + dev_err(&xpad->intf->dev, + "%s - usb_submit_urb failed with result %d\n", + __func__, error); + usb_unanchor_urb(xpad->irq_out); + return -EIO; + } + + xpad->irq_out_active = true; + } + + return 0; +} + static void xpad_irq_out(struct urb *urb) { struct usb_xpad *xpad = urb->context; struct device *dev = &xpad->intf->dev; - int retval, status; + int status = urb->status; + int error; + unsigned long flags; - status = urb->status; + spin_lock_irqsave(&xpad->odata_lock, flags); switch (status) { case 0: /* success */ - return; + xpad->out_packets[xpad->last_out_packet].pending = false; + xpad->irq_out_active = xpad_prepare_next_out_packet(xpad); + break; case -ECONNRESET: case -ENOENT: @@ -697,19 +805,28 @@ static void xpad_irq_out(struct urb *urb) /* this urb is terminated, clean up */ dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status); - return; + xpad->irq_out_active = false; + break; default: dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status); - goto exit; + break; } -exit: - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "%s - usb_submit_urb failed with result %d\n", - __func__, retval); + if (xpad->irq_out_active) { + usb_anchor_urb(urb, &xpad->irq_out_anchor); + error = usb_submit_urb(urb, GFP_ATOMIC); + if (error) { + dev_err(dev, + "%s - usb_submit_urb failed with result %d\n", + __func__, error); + usb_unanchor_urb(urb); + xpad->irq_out_active = false; + } + } + + spin_unlock_irqrestore(&xpad->odata_lock, flags); } static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) @@ -721,6 +838,8 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) if (xpad->xtype == XTYPE_UNKNOWN) return 0; + init_usb_anchor(&xpad->irq_out_anchor); + xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN, GFP_KERNEL, &xpad->odata_dma); if (!xpad->odata) { @@ -728,7 +847,7 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) goto fail1; } - mutex_init(&xpad->odata_mutex); + spin_lock_init(&xpad->odata_lock); xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); if (!xpad->irq_out) { @@ -755,8 +874,14 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) static void xpad_stop_output(struct usb_xpad *xpad) { - if (xpad->xtype != XTYPE_UNKNOWN) - usb_kill_urb(xpad->irq_out); + if (xpad->xtype != XTYPE_UNKNOWN) { + if (!usb_wait_anchor_empty_timeout(&xpad->irq_out_anchor, + 5000)) { + dev_warn(&xpad->intf->dev, + "timed out waiting for output URB to complete, killing\n"); + usb_kill_anchored_urbs(&xpad->irq_out_anchor); + } + } } static void xpad_deinit_output(struct usb_xpad *xpad) @@ -770,27 +895,60 @@ static void xpad_deinit_output(struct usb_xpad *xpad) static int xpad_inquiry_pad_presence(struct usb_xpad *xpad) { + struct xpad_output_packet *packet = + &xpad->out_packets[XPAD_OUT_CMD_IDX]; + unsigned long flags; int retval; - mutex_lock(&xpad->odata_mutex); + spin_lock_irqsave(&xpad->odata_lock, flags); + + packet->data[0] = 0x08; + packet->data[1] = 0x00; + packet->data[2] = 0x0F; + packet->data[3] = 0xC0; + packet->data[4] = 0x00; + packet->data[5] = 0x00; + packet->data[6] = 0x00; + packet->data[7] = 0x00; + packet->data[8] = 0x00; + packet->data[9] = 0x00; + packet->data[10] = 0x00; + packet->data[11] = 0x00; + packet->len = 12; + packet->pending = true; + + /* Reset the sequence so we send out presence first */ + xpad->last_out_packet = -1; + retval = xpad_try_sending_next_out_packet(xpad); + + spin_unlock_irqrestore(&xpad->odata_lock, flags); - xpad->odata[0] = 0x08; - xpad->odata[1] = 0x00; - xpad->odata[2] = 0x0F; - xpad->odata[3] = 0xC0; - xpad->odata[4] = 0x00; - xpad->odata[5] = 0x00; - xpad->odata[6] = 0x00; - xpad->odata[7] = 0x00; - xpad->odata[8] = 0x00; - xpad->odata[9] = 0x00; - xpad->odata[10] = 0x00; - xpad->odata[11] = 0x00; - xpad->irq_out->transfer_buffer_length = 12; + return retval; +} + +static int xpad_start_xbox_one(struct usb_xpad *xpad) +{ + struct xpad_output_packet *packet = + &xpad->out_packets[XPAD_OUT_CMD_IDX]; + unsigned long flags; + int retval; - retval = usb_submit_urb(xpad->irq_out, GFP_KERNEL); + spin_lock_irqsave(&xpad->odata_lock, flags); - mutex_unlock(&xpad->odata_mutex); + /* Xbox one controller needs to be initialized. */ + packet->data[0] = 0x05; + packet->data[1] = 0x20; + packet->data[2] = xpad->odata_serial++; /* packet serial */ + packet->data[3] = 0x01; /* rumble bit enable? */ + packet->data[4] = 0x00; + packet->len = 5; + packet->pending = true; + + /* Reset the sequence so we send out start packet first */ + xpad->last_out_packet = -1; + retval = xpad_try_sending_next_out_packet(xpad); + + spin_unlock_irqrestore(&xpad->odata_lock, flags); return retval; } @@ -799,8 +957,11 @@ static int xpad_inquiry_pad_presence(struct usb_xpad *xpad) static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) { struct usb_xpad *xpad = input_get_drvdata(dev); + struct xpad_output_packet *packet = &xpad->out_packets[XPAD_OUT_FF_IDX]; __u16 strong; __u16 weak; + int retval; + unsigned long flags; if (effect->type != FF_RUMBLE) return 0; @@ -808,69 +969,81 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect strong = effect->u.rumble.strong_magnitude; weak = effect->u.rumble.weak_magnitude; + spin_lock_irqsave(&xpad->odata_lock, flags); + switch (xpad->xtype) { case XTYPE_XBOX: - xpad->odata[0] = 0x00; - xpad->odata[1] = 0x06; - xpad->odata[2] = 0x00; - xpad->odata[3] = strong / 256; /* left actuator */ - xpad->odata[4] = 0x00; - xpad->odata[5] = weak / 256; /* right actuator */ - xpad->irq_out->transfer_buffer_length = 6; + packet->data[0] = 0x00; + packet->data[1] = 0x06; + packet->data[2] = 0x00; + packet->data[3] = strong / 256; /* left actuator */ + packet->data[4] = 0x00; + packet->data[5] = weak / 256; /* right actuator */ + packet->len = 6; + packet->pending = true; break; case XTYPE_XBOX360: - xpad->odata[0] = 0x00; - xpad->odata[1] = 0x08; - xpad->odata[2] = 0x00; - xpad->odata[3] = strong / 256; /* left actuator? */ - xpad->odata[4] = weak / 256; /* right actuator? */ - xpad->odata[5] = 0x00; - xpad->odata[6] = 0x00; - xpad->odata[7] = 0x00; - xpad->irq_out->transfer_buffer_length = 8; + packet->data[0] = 0x00; + packet->data[1] = 0x08; + packet->data[2] = 0x00; + packet->data[3] = strong / 256; /* left actuator? */ + packet->data[4] = weak / 256; /* right actuator? */ + packet->data[5] = 0x00; + packet->data[6] = 0x00; + packet->data[7] = 0x00; + packet->len = 8; + packet->pending = true; break; case XTYPE_XBOX360W: - xpad->odata[0] = 0x00; - xpad->odata[1] = 0x01; - xpad->odata[2] = 0x0F; - xpad->odata[3] = 0xC0; - xpad->odata[4] = 0x00; - xpad->odata[5] = strong / 256; - xpad->odata[6] = weak / 256; - xpad->odata[7] = 0x00; - xpad->odata[8] = 0x00; - xpad->odata[9] = 0x00; - xpad->odata[10] = 0x00; - xpad->odata[11] = 0x00; - xpad->irq_out->transfer_buffer_length = 12; + packet->data[0] = 0x00; + packet->data[1] = 0x01; + packet->data[2] = 0x0F; + packet->data[3] = 0xC0; + packet->data[4] = 0x00; + packet->data[5] = strong / 256; + packet->data[6] = weak / 256; + packet->data[7] = 0x00; + packet->data[8] = 0x00; + packet->data[9] = 0x00; + packet->data[10] = 0x00; + packet->data[11] = 0x00; + packet->len = 12; + packet->pending = true; break; case XTYPE_XBOXONE: - xpad->odata[0] = 0x09; /* activate rumble */ - xpad->odata[1] = 0x08; - xpad->odata[2] = 0x00; - xpad->odata[3] = 0x08; /* continuous effect */ - xpad->odata[4] = 0x00; /* simple rumble mode */ - xpad->odata[5] = 0x03; /* L and R actuator only */ - xpad->odata[6] = 0x00; /* TODO: LT actuator */ - xpad->odata[7] = 0x00; /* TODO: RT actuator */ - xpad->odata[8] = strong / 256; /* left actuator */ - xpad->odata[9] = weak / 256; /* right actuator */ - xpad->odata[10] = 0x80; /* length of pulse */ - xpad->odata[11] = 0x00; /* stop period of pulse */ - xpad->irq_out->transfer_buffer_length = 12; + packet->data[0] = 0x09; /* activate rumble */ + packet->data[1] = 0x08; + packet->data[2] = xpad->odata_serial++; + packet->data[3] = 0x08; /* continuous effect */ + packet->data[4] = 0x00; /* simple rumble mode */ + packet->data[5] = 0x03; /* L and R actuator only */ + packet->data[6] = 0x00; /* TODO: LT actuator */ + packet->data[7] = 0x00; /* TODO: RT actuator */ + packet->data[8] = strong / 512; /* left actuator */ + packet->data[9] = weak / 512; /* right actuator */ + packet->data[10] = 0x80; /* length of pulse */ + packet->data[11] = 0x00; /* stop period of pulse */ + packet->data[12] = 0x00; + packet->len = 13; + packet->pending = true; break; default: dev_dbg(&xpad->dev->dev, "%s - rumble command sent to unsupported xpad type: %d\n", __func__, xpad->xtype); - return -EINVAL; + retval = -EINVAL; + goto out; } - return usb_submit_urb(xpad->irq_out, GFP_ATOMIC); + retval = xpad_try_sending_next_out_packet(xpad); + +out: + spin_unlock_irqrestore(&xpad->odata_lock, flags); + return retval; } static int xpad_init_ff(struct usb_xpad *xpad) @@ -921,36 +1094,44 @@ struct xpad_led { */ static void xpad_send_led_command(struct usb_xpad *xpad, int command) { + struct xpad_output_packet *packet = + &xpad->out_packets[XPAD_OUT_LED_IDX]; + unsigned long flags; + command %= 16; - mutex_lock(&xpad->odata_mutex); + spin_lock_irqsave(&xpad->odata_lock, flags); switch (xpad->xtype) { case XTYPE_XBOX360: - xpad->odata[0] = 0x01; - xpad->odata[1] = 0x03; - xpad->odata[2] = command; - xpad->irq_out->transfer_buffer_length = 3; + packet->data[0] = 0x01; + packet->data[1] = 0x03; + packet->data[2] = command; + packet->len = 3; + packet->pending = true; break; + case XTYPE_XBOX360W: - xpad->odata[0] = 0x00; - xpad->odata[1] = 0x00; - xpad->odata[2] = 0x08; - xpad->odata[3] = 0x40 + command; - xpad->odata[4] = 0x00; - xpad->odata[5] = 0x00; - xpad->odata[6] = 0x00; - xpad->odata[7] = 0x00; - xpad->odata[8] = 0x00; - xpad->odata[9] = 0x00; - xpad->odata[10] = 0x00; - xpad->odata[11] = 0x00; - xpad->irq_out->transfer_buffer_length = 12; + packet->data[0] = 0x00; + packet->data[1] = 0x00; + packet->data[2] = 0x08; + packet->data[3] = 0x40 + command; + packet->data[4] = 0x00; + packet->data[5] = 0x00; + packet->data[6] = 0x00; + packet->data[7] = 0x00; + packet->data[8] = 0x00; + packet->data[9] = 0x00; + packet->data[10] = 0x00; + packet->data[11] = 0x00; + packet->len = 12; + packet->pending = true; break; } - usb_submit_urb(xpad->irq_out, GFP_KERNEL); - mutex_unlock(&xpad->odata_mutex); + xpad_try_sending_next_out_packet(xpad); + + spin_unlock_irqrestore(&xpad->odata_lock, flags); } /* @@ -959,7 +1140,7 @@ static void xpad_send_led_command(struct usb_xpad *xpad, int command) */ static void xpad_identify_controller(struct usb_xpad *xpad) { - xpad_send_led_command(xpad, (xpad->pad_nr % 4) + 2); + led_set_brightness(&xpad->led->led_cdev, (xpad->pad_nr % 4) + 2); } static void xpad_led_set(struct led_classdev *led_cdev, @@ -1001,14 +1182,7 @@ static int xpad_led_probe(struct usb_xpad *xpad) if (error) goto err_free_id; - if (xpad->xtype == XTYPE_XBOX360) { - /* - * Light up the segment corresponding to controller - * number on wired devices. On wireless we'll do that - * when they respond to "presence" packet. - */ - xpad_identify_controller(xpad); - } + xpad_identify_controller(xpad); return 0; @@ -1036,37 +1210,73 @@ static void xpad_led_disconnect(struct usb_xpad *xpad) { } static void xpad_identify_controller(struct usb_xpad *xpad) { } #endif -static int xpad_open(struct input_dev *dev) +static int xpad_start_input(struct usb_xpad *xpad) { - struct usb_xpad *xpad = input_get_drvdata(dev); - - /* URB was submitted in probe */ - if (xpad->xtype == XTYPE_XBOX360W) - return 0; + int error; - xpad->irq_in->dev = xpad->udev; if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) return -EIO; if (xpad->xtype == XTYPE_XBOXONE) { - /* Xbox one controller needs to be initialized. */ - xpad->odata[0] = 0x05; - xpad->odata[1] = 0x20; - xpad->irq_out->transfer_buffer_length = 2; - return usb_submit_urb(xpad->irq_out, GFP_KERNEL); + error = xpad_start_xbox_one(xpad); + if (error) { + usb_kill_urb(xpad->irq_in); + return error; + } } return 0; } -static void xpad_close(struct input_dev *dev) +static void xpad_stop_input(struct usb_xpad *xpad) { - struct usb_xpad *xpad = input_get_drvdata(dev); + usb_kill_urb(xpad->irq_in); +} + +static int xpad360w_start_input(struct usb_xpad *xpad) +{ + int error; - if (xpad->xtype != XTYPE_XBOX360W) + error = usb_submit_urb(xpad->irq_in, GFP_KERNEL); + if (error) + return -EIO; + + /* + * Send presence packet. + * This will force the controller to resend connection packets. + * This is useful in the case we activate the module after the + * adapter has been plugged in, as it won't automatically + * send us info about the controllers. + */ + error = xpad_inquiry_pad_presence(xpad); + if (error) { usb_kill_urb(xpad->irq_in); + return error; + } - xpad_stop_output(xpad); + return 0; +} + +static void xpad360w_stop_input(struct usb_xpad *xpad) +{ + usb_kill_urb(xpad->irq_in); + + /* Make sure we are done with presence work if it was scheduled */ + flush_work(&xpad->work); +} + +static int xpad_open(struct input_dev *dev) +{ + struct usb_xpad *xpad = input_get_drvdata(dev); + + return xpad_start_input(xpad); +} + +static void xpad_close(struct input_dev *dev) +{ + struct usb_xpad *xpad = input_get_drvdata(dev); + + xpad_stop_input(xpad); } static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) @@ -1097,8 +1307,11 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) static void xpad_deinit_input(struct usb_xpad *xpad) { - xpad_led_disconnect(xpad); - input_unregister_device(xpad->dev); + if (xpad->input_created) { + xpad->input_created = false; + xpad_led_disconnect(xpad); + input_unregister_device(xpad->dev); + } } static int xpad_init_input(struct usb_xpad *xpad) @@ -1118,8 +1331,10 @@ static int xpad_init_input(struct usb_xpad *xpad) input_set_drvdata(input_dev, xpad); - input_dev->open = xpad_open; - input_dev->close = xpad_close; + if (xpad->xtype != XTYPE_XBOX360W) { + input_dev->open = xpad_open; + input_dev->close = xpad_close; + } __set_bit(EV_KEY, input_dev->evbit); @@ -1181,6 +1396,7 @@ static int xpad_init_input(struct usb_xpad *xpad) if (error) goto err_disconnect_led; + xpad->input_created = true; return 0; err_disconnect_led: @@ -1241,6 +1457,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id xpad->mapping = xpad_device[i].mapping; xpad->xtype = xpad_device[i].xtype; xpad->name = xpad_device[i].name; + INIT_WORK(&xpad->work, xpad_presence_work); if (xpad->xtype == XTYPE_UNKNOWN) { if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) { @@ -1277,10 +1494,6 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id usb_set_intfdata(intf, xpad); - error = xpad_init_input(xpad); - if (error) - goto err_deinit_output; - if (xpad->xtype == XTYPE_XBOX360W) { /* * Submit the int URB immediately rather than waiting for open @@ -1289,28 +1502,24 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id * exactly the message that a controller has arrived that * we're waiting for. */ - xpad->irq_in->dev = xpad->udev; - error = usb_submit_urb(xpad->irq_in, GFP_KERNEL); + error = xpad360w_start_input(xpad); if (error) - goto err_deinit_input; - + goto err_deinit_output; /* - * Send presence packet. - * This will force the controller to resend connection packets. - * This is useful in the case we activate the module after the - * adapter has been plugged in, as it won't automatically - * send us info about the controllers. + * Wireless controllers require RESET_RESUME to work properly + * after suspend. Ideally this quirk should be in usb core + * quirk list, but we have too many vendors producing these + * controllers and we'd need to maintain 2 identical lists + * here in this driver and in usb core. */ - error = xpad_inquiry_pad_presence(xpad); + udev->quirks |= USB_QUIRK_RESET_RESUME; + } else { + error = xpad_init_input(xpad); if (error) - goto err_kill_in_urb; + goto err_deinit_output; } return 0; -err_kill_in_urb: - usb_kill_urb(xpad->irq_in); -err_deinit_input: - xpad_deinit_input(xpad); err_deinit_output: xpad_deinit_output(xpad); err_free_in_urb: @@ -1320,19 +1529,24 @@ err_free_idata: err_free_mem: kfree(xpad); return error; - } static void xpad_disconnect(struct usb_interface *intf) { - struct usb_xpad *xpad = usb_get_intfdata (intf); + struct usb_xpad *xpad = usb_get_intfdata(intf); + + if (xpad->xtype == XTYPE_XBOX360W) + xpad360w_stop_input(xpad); xpad_deinit_input(xpad); - xpad_deinit_output(xpad); - if (xpad->xtype == XTYPE_XBOX360W) { - usb_kill_urb(xpad->irq_in); - } + /* + * Now that both input device and LED device are gone we can + * stop output URB. + */ + xpad_stop_output(xpad); + + xpad_deinit_output(xpad); usb_free_urb(xpad->irq_in); usb_free_coherent(xpad->udev, XPAD_PKT_LEN, @@ -1343,10 +1557,55 @@ static void xpad_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); } +static int xpad_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_xpad *xpad = usb_get_intfdata(intf); + struct input_dev *input = xpad->dev; + + if (xpad->xtype == XTYPE_XBOX360W) { + /* + * Wireless controllers always listen to input so + * they are notified when controller shows up + * or goes away. + */ + xpad360w_stop_input(xpad); + } else { + mutex_lock(&input->mutex); + if (input->users) + xpad_stop_input(xpad); + mutex_unlock(&input->mutex); + } + + xpad_stop_output(xpad); + + return 0; +} + +static int xpad_resume(struct usb_interface *intf) +{ + struct usb_xpad *xpad = usb_get_intfdata(intf); + struct input_dev *input = xpad->dev; + int retval = 0; + + if (xpad->xtype == XTYPE_XBOX360W) { + retval = xpad360w_start_input(xpad); + } else { + mutex_lock(&input->mutex); + if (input->users) + retval = xpad_start_input(xpad); + mutex_unlock(&input->mutex); + } + + return retval; +} + static struct usb_driver xpad_driver = { .name = "xpad", .probe = xpad_probe, .disconnect = xpad_disconnect, + .suspend = xpad_suspend, + .resume = xpad_resume, + .reset_resume = xpad_resume, .id_table = xpad_table, }; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index ddd8148d51d7..509608c95994 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -560,7 +560,7 @@ config KEYBOARD_SUNKBD config KEYBOARD_SH_KEYSC tristate "SuperH KEYSC keypad support" - depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST + depends on ARCH_SHMOBILE || COMPILE_TEST help Say Y here if you want to use a keypad attached to the KEYSC block on SuperH processors such as sh7722 and sh7343. diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c index 907e4e278fce..f6e643b589b6 100644 --- a/drivers/input/keyboard/goldfish_events.c +++ b/drivers/input/keyboard/goldfish_events.c @@ -22,6 +22,7 @@ #include <linux/slab.h> #include <linux/irq.h> #include <linux/io.h> +#include <linux/acpi.h> enum { REG_READ = 0x00, @@ -178,10 +179,26 @@ static int events_probe(struct platform_device *pdev) return 0; } +static const struct of_device_id goldfish_events_of_match[] = { + { .compatible = "google,goldfish-events-keypad", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_events_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id goldfish_events_acpi_match[] = { + { "GFSH0002", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, goldfish_events_acpi_match); +#endif + static struct platform_driver events_driver = { .probe = events_probe, .driver = { .name = "goldfish_events", + .of_match_table = goldfish_events_of_match, + .acpi_match_table = ACPI_PTR(goldfish_events_acpi_match), }, }; diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index bef317ff7352..29093657f2ef 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -96,7 +96,7 @@ struct gpio_keys_drvdata { * Return value of this function can be used to allocate bitmap * large enough to hold all bits for given type. */ -static inline int get_n_events_by_type(int type) +static int get_n_events_by_type(int type) { BUG_ON(type != EV_SW && type != EV_KEY); @@ -104,6 +104,22 @@ static inline int get_n_events_by_type(int type) } /** + * get_bm_events_by_type() - returns bitmap of supported events per @type + * @input: input device from which bitmap is retrieved + * @type: type of button (%EV_KEY, %EV_SW) + * + * Return value of this function can be used to allocate bitmap + * large enough to hold all bits for given type. + */ +static const unsigned long *get_bm_events_by_type(struct input_dev *dev, + int type) +{ + BUG_ON(type != EV_SW && type != EV_KEY); + + return (type == EV_KEY) ? dev->keybit : dev->swbit; +} + +/** * gpio_keys_disable_button() - disables given GPIO button * @bdata: button data for button to be disabled * @@ -213,6 +229,7 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, const char *buf, unsigned int type) { int n_events = get_n_events_by_type(type); + const unsigned long *bitmap = get_bm_events_by_type(ddata->input, type); unsigned long *bits; ssize_t error; int i; @@ -226,6 +243,11 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, goto out; /* First validate */ + if (!bitmap_subset(bits, bitmap, n_events)) { + error = -EINVAL; + goto out; + } + for (i = 0; i < ddata->pdata->nbuttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; @@ -239,11 +261,6 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, } } - if (i == ddata->pdata->nbuttons) { - error = -EINVAL; - goto out; - } - mutex_lock(&ddata->disable_lock); for (i = 0; i < ddata->pdata->nbuttons; i++) { @@ -613,7 +630,7 @@ gpio_keys_get_devtree_pdata(struct device *dev) if (!node) return ERR_PTR(-ENODEV); - nbuttons = of_get_child_count(node); + nbuttons = of_get_available_child_count(node); if (nbuttons == 0) return ERR_PTR(-ENODEV); @@ -628,8 +645,10 @@ gpio_keys_get_devtree_pdata(struct device *dev) pdata->rep = !!of_get_property(node, "autorepeat", NULL); + of_property_read_string(node, "label", &pdata->name); + i = 0; - for_each_child_of_node(node, pp) { + for_each_available_child_of_node(node, pp) { enum of_gpio_flags flags; button = &pdata->buttons[i++]; diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 7502e46165fa..e0d72c8c01e4 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -155,14 +155,6 @@ static void omap_kp_tasklet(unsigned long data) "pressed" : "released"); #else key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)]; - if (key < 0) { - printk(KERN_WARNING - "omap-keypad: Spurious key event %d-%d\n", - col, row); - /* We scan again after a couple of seconds */ - spurious = 1; - continue; - } if (!(kp_cur_group == (key & GROUP_MASK) || kp_cur_group == -1)) @@ -292,8 +284,8 @@ static int omap_kp_probe(struct platform_device *pdev) setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp); /* get the irq and init timer*/ - tasklet_enable(&kp_tasklet); kp_tasklet.data = (unsigned long) omap_kp; + tasklet_enable(&kp_tasklet); ret = device_create_file(&pdev->dev, &dev_attr_enable); if (ret < 0) diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c index 9adf13a5864a..b0ffadeb208c 100644 --- a/drivers/input/keyboard/snvs_pwrkey.c +++ b/drivers/input/keyboard/snvs_pwrkey.c @@ -180,7 +180,7 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) return 0; } -static int imx_snvs_pwrkey_suspend(struct device *dev) +static int __maybe_unused imx_snvs_pwrkey_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); @@ -191,7 +191,7 @@ static int imx_snvs_pwrkey_suspend(struct device *dev) return 0; } -static int imx_snvs_pwrkey_resume(struct device *dev) +static int __maybe_unused imx_snvs_pwrkey_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index 623d451767e3..8083eaa0524a 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -288,8 +288,7 @@ static int spear_kbd_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int spear_kbd_suspend(struct device *dev) +static int __maybe_unused spear_kbd_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct spear_kbd *kbd = platform_get_drvdata(pdev); @@ -342,7 +341,7 @@ static int spear_kbd_suspend(struct device *dev) return 0; } -static int spear_kbd_resume(struct device *dev) +static int __maybe_unused spear_kbd_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct spear_kbd *kbd = platform_get_drvdata(pdev); @@ -368,7 +367,6 @@ static int spear_kbd_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume); diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index 1d0e61d7c131..b0d445390ee4 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -147,7 +147,7 @@ struct bma150_data { * are stated and verified by Bosch Sensortec where they are configured * to provide a generic sensitivity performance. */ -static struct bma150_cfg default_cfg = { +static const struct bma150_cfg default_cfg = { .any_motion_int = 1, .hg_int = 1, .lg_int = 1, diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c index 8eb697db82d0..bb863e062b03 100644 --- a/drivers/input/misc/da9063_onkey.c +++ b/drivers/input/misc/da9063_onkey.c @@ -179,13 +179,13 @@ static irqreturn_t da9063_onkey_irq_handler(int irq, void *data) input_report_key(onkey->input, KEY_POWER, 1); input_sync(onkey->input); schedule_delayed_work(&onkey->work, 0); - dev_dbg(onkey->dev, "KEY_POWER pressed.\n"); + dev_dbg(onkey->dev, "KEY_POWER long press.\n"); } else { - input_report_key(onkey->input, KEY_SLEEP, 1); + input_report_key(onkey->input, KEY_POWER, 1); input_sync(onkey->input); - input_report_key(onkey->input, KEY_SLEEP, 0); + input_report_key(onkey->input, KEY_POWER, 0); input_sync(onkey->input); - dev_dbg(onkey->dev, "KEY_SLEEP pressed.\n"); + dev_dbg(onkey->dev, "KEY_POWER short press.\n"); } return IRQ_HANDLED; diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 8aee71986430..96c486de49e0 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -20,70 +20,78 @@ #include <linux/input.h> #include <linux/device.h> #include <linux/platform_device.h> -#include <linux/gpio.h> -#include <linux/rotary_encoder.h> +#include <linux/gpio/consumer.h> #include <linux/slab.h> #include <linux/of.h> -#include <linux/of_platform.h> -#include <linux/of_gpio.h> #include <linux/pm.h> +#include <linux/property.h> #define DRV_NAME "rotary-encoder" struct rotary_encoder { struct input_dev *input; - const struct rotary_encoder_platform_data *pdata; - unsigned int axis; + struct mutex access_mutex; + + u32 steps; + u32 axis; + bool relative_axis; + bool rollover; + unsigned int pos; - unsigned int irq_a; - unsigned int irq_b; + struct gpio_descs *gpios; + + unsigned int *irq; bool armed; - unsigned char dir; /* 0 - clockwise, 1 - CCW */ + signed char dir; /* 1 - clockwise, -1 - CCW */ - char last_stable; + unsigned last_stable; }; -static int rotary_encoder_get_state(const struct rotary_encoder_platform_data *pdata) +static unsigned rotary_encoder_get_state(struct rotary_encoder *encoder) { - int a = !!gpio_get_value(pdata->gpio_a); - int b = !!gpio_get_value(pdata->gpio_b); + int i; + unsigned ret = 0; - a ^= pdata->inverted_a; - b ^= pdata->inverted_b; + for (i = 0; i < encoder->gpios->ndescs; ++i) { + int val = gpiod_get_value_cansleep(encoder->gpios->desc[i]); + /* convert from gray encoding to normal */ + if (ret & 1) + val = !val; - return ((a << 1) | b); + ret = ret << 1 | val; + } + + return ret & 3; } static void rotary_encoder_report_event(struct rotary_encoder *encoder) { - const struct rotary_encoder_platform_data *pdata = encoder->pdata; - - if (pdata->relative_axis) { + if (encoder->relative_axis) { input_report_rel(encoder->input, - pdata->axis, encoder->dir ? -1 : 1); + encoder->axis, encoder->dir); } else { unsigned int pos = encoder->pos; - if (encoder->dir) { + if (encoder->dir < 0) { /* turning counter-clockwise */ - if (pdata->rollover) - pos += pdata->steps; + if (encoder->rollover) + pos += encoder->steps; if (pos) pos--; } else { /* turning clockwise */ - if (pdata->rollover || pos < pdata->steps) + if (encoder->rollover || pos < encoder->steps) pos++; } - if (pdata->rollover) - pos %= pdata->steps; + if (encoder->rollover) + pos %= encoder->steps; encoder->pos = pos; - input_report_abs(encoder->input, pdata->axis, encoder->pos); + input_report_abs(encoder->input, encoder->axis, encoder->pos); } input_sync(encoder->input); @@ -92,9 +100,11 @@ static void rotary_encoder_report_event(struct rotary_encoder *encoder) static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) { struct rotary_encoder *encoder = dev_id; - int state; + unsigned state; - state = rotary_encoder_get_state(encoder->pdata); + mutex_lock(&encoder->access_mutex); + + state = rotary_encoder_get_state(encoder); switch (state) { case 0x0: @@ -105,334 +115,227 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) break; case 0x1: - case 0x2: + case 0x3: if (encoder->armed) - encoder->dir = state - 1; + encoder->dir = 2 - state; break; - case 0x3: + case 0x2: encoder->armed = true; break; } + mutex_unlock(&encoder->access_mutex); + return IRQ_HANDLED; } static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) { struct rotary_encoder *encoder = dev_id; - int state; + unsigned int state; - state = rotary_encoder_get_state(encoder->pdata); + mutex_lock(&encoder->access_mutex); - switch (state) { - case 0x00: - case 0x03: + state = rotary_encoder_get_state(encoder); + + if (state & 1) { + encoder->dir = ((encoder->last_stable - state + 1) % 4) - 1; + } else { if (state != encoder->last_stable) { rotary_encoder_report_event(encoder); encoder->last_stable = state; } - break; - - case 0x01: - case 0x02: - encoder->dir = (encoder->last_stable + state) & 0x01; - break; } + mutex_unlock(&encoder->access_mutex); + return IRQ_HANDLED; } static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id) { struct rotary_encoder *encoder = dev_id; - unsigned char sum; - int state; - - state = rotary_encoder_get_state(encoder->pdata); - - /* - * We encode the previous and the current state using a byte. - * The previous state in the MSB nibble, the current state in the LSB - * nibble. Then use a table to decide the direction of the turn. - */ - sum = (encoder->last_stable << 4) + state; - switch (sum) { - case 0x31: - case 0x10: - case 0x02: - case 0x23: - encoder->dir = 0; /* clockwise */ - break; + unsigned int state; - case 0x13: - case 0x01: - case 0x20: - case 0x32: - encoder->dir = 1; /* counter-clockwise */ - break; + mutex_lock(&encoder->access_mutex); - default: - /* - * Ignore all other values. This covers the case when the - * state didn't change (a spurious interrupt) and the - * cases where the state changed by two steps, making it - * impossible to tell the direction. - * - * In either case, don't report any event and save the - * state for later. - */ + state = rotary_encoder_get_state(encoder); + + if ((encoder->last_stable + 1) % 4 == state) + encoder->dir = 1; + else if (encoder->last_stable == (state + 1) % 4) + encoder->dir = -1; + else goto out; - } rotary_encoder_report_event(encoder); out: encoder->last_stable = state; + mutex_unlock(&encoder->access_mutex); + return IRQ_HANDLED; } -#ifdef CONFIG_OF -static const struct of_device_id rotary_encoder_of_match[] = { - { .compatible = "rotary-encoder", }, - { }, -}; -MODULE_DEVICE_TABLE(of, rotary_encoder_of_match); - -static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct device *dev) +static int rotary_encoder_probe(struct platform_device *pdev) { - const struct of_device_id *of_id = - of_match_device(rotary_encoder_of_match, dev); - struct device_node *np = dev->of_node; - struct rotary_encoder_platform_data *pdata; - enum of_gpio_flags flags; - int error; - - if (!of_id || !np) - return NULL; - - pdata = kzalloc(sizeof(struct rotary_encoder_platform_data), - GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - - of_property_read_u32(np, "rotary-encoder,steps", &pdata->steps); - of_property_read_u32(np, "linux,axis", &pdata->axis); + struct device *dev = &pdev->dev; + struct rotary_encoder *encoder; + struct input_dev *input; + irq_handler_t handler; + u32 steps_per_period; + unsigned int i; + int err; - pdata->gpio_a = of_get_gpio_flags(np, 0, &flags); - pdata->inverted_a = flags & OF_GPIO_ACTIVE_LOW; + encoder = devm_kzalloc(dev, sizeof(struct rotary_encoder), GFP_KERNEL); + if (!encoder) + return -ENOMEM; - pdata->gpio_b = of_get_gpio_flags(np, 1, &flags); - pdata->inverted_b = flags & OF_GPIO_ACTIVE_LOW; + mutex_init(&encoder->access_mutex); - pdata->relative_axis = - of_property_read_bool(np, "rotary-encoder,relative-axis"); - pdata->rollover = of_property_read_bool(np, "rotary-encoder,rollover"); + device_property_read_u32(dev, "rotary-encoder,steps", &encoder->steps); - error = of_property_read_u32(np, "rotary-encoder,steps-per-period", - &pdata->steps_per_period); - if (error) { + err = device_property_read_u32(dev, "rotary-encoder,steps-per-period", + &steps_per_period); + if (err) { /* - * The 'half-period' property has been deprecated, you must use - * 'steps-per-period' and set an appropriate value, but we still - * need to parse it to maintain compatibility. + * The 'half-period' property has been deprecated, you must + * use 'steps-per-period' and set an appropriate value, but + * we still need to parse it to maintain compatibility. If + * neither property is present we fall back to the one step + * per period behavior. */ - if (of_property_read_bool(np, "rotary-encoder,half-period")) { - pdata->steps_per_period = 2; - } else { - /* Fallback to one step per period behavior */ - pdata->steps_per_period = 1; - } + steps_per_period = device_property_read_bool(dev, + "rotary-encoder,half-period") ? 2 : 1; } - pdata->wakeup_source = of_property_read_bool(np, "wakeup-source"); + encoder->rollover = + device_property_read_bool(dev, "rotary-encoder,rollover"); - return pdata; -} -#else -static inline struct rotary_encoder_platform_data * -rotary_encoder_parse_dt(struct device *dev) -{ - return NULL; -} -#endif - -static int rotary_encoder_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - const struct rotary_encoder_platform_data *pdata = dev_get_platdata(dev); - struct rotary_encoder *encoder; - struct input_dev *input; - irq_handler_t handler; - int err; - - if (!pdata) { - pdata = rotary_encoder_parse_dt(dev); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); + device_property_read_u32(dev, "linux,axis", &encoder->axis); + encoder->relative_axis = + device_property_read_bool(dev, "rotary-encoder,relative-axis"); - if (!pdata) { - dev_err(dev, "missing platform data\n"); - return -EINVAL; - } + encoder->gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN); + if (IS_ERR(encoder->gpios)) { + dev_err(dev, "unable to get gpios\n"); + return PTR_ERR(encoder->gpios); } - - encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL); - input = input_allocate_device(); - if (!encoder || !input) { - err = -ENOMEM; - goto exit_free_mem; + if (encoder->gpios->ndescs < 2) { + dev_err(dev, "not enough gpios found\n"); + return -EINVAL; } + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + encoder->input = input; - encoder->pdata = pdata; input->name = pdev->name; input->id.bustype = BUS_HOST; input->dev.parent = dev; - if (pdata->relative_axis) { - input->evbit[0] = BIT_MASK(EV_REL); - input->relbit[0] = BIT_MASK(pdata->axis); - } else { - input->evbit[0] = BIT_MASK(EV_ABS); - input_set_abs_params(encoder->input, - pdata->axis, 0, pdata->steps, 0, 1); - } - - /* request the GPIOs */ - err = gpio_request_one(pdata->gpio_a, GPIOF_IN, dev_name(dev)); - if (err) { - dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_a); - goto exit_free_mem; - } - - err = gpio_request_one(pdata->gpio_b, GPIOF_IN, dev_name(dev)); - if (err) { - dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_b); - goto exit_free_gpio_a; - } - - encoder->irq_a = gpio_to_irq(pdata->gpio_a); - encoder->irq_b = gpio_to_irq(pdata->gpio_b); + if (encoder->relative_axis) + input_set_capability(input, EV_REL, encoder->axis); + else + input_set_abs_params(input, + encoder->axis, 0, encoder->steps, 0, 1); - switch (pdata->steps_per_period) { + switch (steps_per_period >> (encoder->gpios->ndescs - 2)) { case 4: handler = &rotary_encoder_quarter_period_irq; - encoder->last_stable = rotary_encoder_get_state(pdata); + encoder->last_stable = rotary_encoder_get_state(encoder); break; case 2: handler = &rotary_encoder_half_period_irq; - encoder->last_stable = rotary_encoder_get_state(pdata); + encoder->last_stable = rotary_encoder_get_state(encoder); break; case 1: handler = &rotary_encoder_irq; break; default: dev_err(dev, "'%d' is not a valid steps-per-period value\n", - pdata->steps_per_period); - err = -EINVAL; - goto exit_free_gpio_b; + steps_per_period); + return -EINVAL; } - err = request_irq(encoder->irq_a, handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - DRV_NAME, encoder); - if (err) { - dev_err(dev, "unable to request IRQ %d\n", encoder->irq_a); - goto exit_free_gpio_b; - } - - err = request_irq(encoder->irq_b, handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - DRV_NAME, encoder); - if (err) { - dev_err(dev, "unable to request IRQ %d\n", encoder->irq_b); - goto exit_free_irq_a; + encoder->irq = + devm_kzalloc(dev, + sizeof(*encoder->irq) * encoder->gpios->ndescs, + GFP_KERNEL); + if (!encoder->irq) + return -ENOMEM; + + for (i = 0; i < encoder->gpios->ndescs; ++i) { + encoder->irq[i] = gpiod_to_irq(encoder->gpios->desc[i]); + + err = devm_request_threaded_irq(dev, encoder->irq[i], + NULL, handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + DRV_NAME, encoder); + if (err) { + dev_err(dev, "unable to request IRQ %d (gpio#%d)\n", + encoder->irq[i], i); + return err; + } } err = input_register_device(input); if (err) { dev_err(dev, "failed to register input device\n"); - goto exit_free_irq_b; + return err; } - device_init_wakeup(&pdev->dev, pdata->wakeup_source); + device_init_wakeup(dev, + device_property_read_bool(dev, "wakeup-source")); platform_set_drvdata(pdev, encoder); return 0; - -exit_free_irq_b: - free_irq(encoder->irq_b, encoder); -exit_free_irq_a: - free_irq(encoder->irq_a, encoder); -exit_free_gpio_b: - gpio_free(pdata->gpio_b); -exit_free_gpio_a: - gpio_free(pdata->gpio_a); -exit_free_mem: - input_free_device(input); - kfree(encoder); - if (!dev_get_platdata(&pdev->dev)) - kfree(pdata); - - return err; } -static int rotary_encoder_remove(struct platform_device *pdev) -{ - struct rotary_encoder *encoder = platform_get_drvdata(pdev); - const struct rotary_encoder_platform_data *pdata = encoder->pdata; - - device_init_wakeup(&pdev->dev, false); - - free_irq(encoder->irq_a, encoder); - free_irq(encoder->irq_b, encoder); - gpio_free(pdata->gpio_a); - gpio_free(pdata->gpio_b); - - input_unregister_device(encoder->input); - kfree(encoder); - - if (!dev_get_platdata(&pdev->dev)) - kfree(pdata); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int rotary_encoder_suspend(struct device *dev) +static int __maybe_unused rotary_encoder_suspend(struct device *dev) { struct rotary_encoder *encoder = dev_get_drvdata(dev); + unsigned int i; if (device_may_wakeup(dev)) { - enable_irq_wake(encoder->irq_a); - enable_irq_wake(encoder->irq_b); + for (i = 0; i < encoder->gpios->ndescs; ++i) + enable_irq_wake(encoder->irq[i]); } return 0; } -static int rotary_encoder_resume(struct device *dev) +static int __maybe_unused rotary_encoder_resume(struct device *dev) { struct rotary_encoder *encoder = dev_get_drvdata(dev); + unsigned int i; if (device_may_wakeup(dev)) { - disable_irq_wake(encoder->irq_a); - disable_irq_wake(encoder->irq_b); + for (i = 0; i < encoder->gpios->ndescs; ++i) + disable_irq_wake(encoder->irq[i]); } return 0; } -#endif static SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops, - rotary_encoder_suspend, rotary_encoder_resume); + rotary_encoder_suspend, rotary_encoder_resume); + +#ifdef CONFIG_OF +static const struct of_device_id rotary_encoder_of_match[] = { + { .compatible = "rotary-encoder", }, + { }, +}; +MODULE_DEVICE_TABLE(of, rotary_encoder_of_match); +#endif static struct platform_driver rotary_encoder_driver = { .probe = rotary_encoder_probe, - .remove = rotary_encoder_remove, .driver = { .name = DRV_NAME, .pm = &rotary_encoder_pm_ops, diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index 6f997aa49183..4a5afc7fe96e 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -345,23 +345,19 @@ static struct platform_driver grover_beep_driver = { .shutdown = sparcspkr_shutdown, }; +static struct platform_driver * const drivers[] = { + &bbc_beep_driver, + &grover_beep_driver, +}; + static int __init sparcspkr_init(void) { - int err = platform_driver_register(&bbc_beep_driver); - - if (!err) { - err = platform_driver_register(&grover_beep_driver); - if (err) - platform_driver_unregister(&bbc_beep_driver); - } - - return err; + return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); } static void __exit sparcspkr_exit(void) { - platform_driver_unregister(&bbc_beep_driver); - platform_driver_unregister(&grover_beep_driver); + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); } module_init(sparcspkr_init); diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 5adbcedcb81c..4eb9e4d94f46 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -256,13 +256,29 @@ static void uinput_destroy_device(struct uinput_device *udev) static int uinput_create_device(struct uinput_device *udev) { struct input_dev *dev = udev->dev; - int error; + int error, nslot; if (udev->state != UIST_SETUP_COMPLETE) { printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME); return -EINVAL; } + if (test_bit(ABS_MT_SLOT, dev->absbit)) { + nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; + error = input_mt_init_slots(dev, nslot, 0); + if (error) + goto fail1; + } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { + input_set_events_per_packet(dev, 60); + } + + if (test_bit(EV_FF, dev->evbit) && !udev->ff_effects_max) { + printk(KERN_DEBUG "%s: ff_effects_max should be non-zero when FF_BIT is set\n", + UINPUT_NAME); + error = -EINVAL; + goto fail1; + } + if (udev->ff_effects_max) { error = input_ff_create(dev, udev->ff_effects_max); if (error) @@ -308,10 +324,35 @@ static int uinput_open(struct inode *inode, struct file *file) return 0; } +static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code, + const struct input_absinfo *abs) +{ + int min, max; + + min = abs->minimum; + max = abs->maximum; + + if ((min != 0 || max != 0) && max <= min) { + printk(KERN_DEBUG + "%s: invalid abs[%02x] min:%d max:%d\n", + UINPUT_NAME, code, min, max); + return -EINVAL; + } + + if (abs->flat > max - min) { + printk(KERN_DEBUG + "%s: abs_flat #%02x out of range: %d (min:%d/max:%d)\n", + UINPUT_NAME, code, abs->flat, min, max); + return -EINVAL; + } + + return 0; +} + static int uinput_validate_absbits(struct input_dev *dev) { unsigned int cnt; - int nslot; + int error; if (!test_bit(EV_ABS, dev->evbit)) return 0; @@ -321,38 +362,12 @@ static int uinput_validate_absbits(struct input_dev *dev) */ for_each_set_bit(cnt, dev->absbit, ABS_CNT) { - int min, max; - - min = input_abs_get_min(dev, cnt); - max = input_abs_get_max(dev, cnt); - - if ((min != 0 || max != 0) && max <= min) { - printk(KERN_DEBUG - "%s: invalid abs[%02x] min:%d max:%d\n", - UINPUT_NAME, cnt, - input_abs_get_min(dev, cnt), - input_abs_get_max(dev, cnt)); + if (!dev->absinfo) return -EINVAL; - } - if (input_abs_get_flat(dev, cnt) > - input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) { - printk(KERN_DEBUG - "%s: abs_flat #%02x out of range: %d " - "(min:%d/max:%d)\n", - UINPUT_NAME, cnt, - input_abs_get_flat(dev, cnt), - input_abs_get_min(dev, cnt), - input_abs_get_max(dev, cnt)); - return -EINVAL; - } - } - - if (test_bit(ABS_MT_SLOT, dev->absbit)) { - nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; - input_mt_init_slots(dev, nslot, 0); - } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { - input_set_events_per_packet(dev, 60); + error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]); + if (error) + return error; } return 0; @@ -370,8 +385,71 @@ static int uinput_allocate_device(struct uinput_device *udev) return 0; } -static int uinput_setup_device(struct uinput_device *udev, - const char __user *buffer, size_t count) +static int uinput_dev_setup(struct uinput_device *udev, + struct uinput_setup __user *arg) +{ + struct uinput_setup setup; + struct input_dev *dev; + + if (udev->state == UIST_CREATED) + return -EINVAL; + + if (copy_from_user(&setup, arg, sizeof(setup))) + return -EFAULT; + + if (!setup.name[0]) + return -EINVAL; + + dev = udev->dev; + dev->id = setup.id; + udev->ff_effects_max = setup.ff_effects_max; + + kfree(dev->name); + dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL); + if (!dev->name) + return -ENOMEM; + + udev->state = UIST_SETUP_COMPLETE; + return 0; +} + +static int uinput_abs_setup(struct uinput_device *udev, + struct uinput_setup __user *arg, size_t size) +{ + struct uinput_abs_setup setup = {}; + struct input_dev *dev; + int error; + + if (size > sizeof(setup)) + return -E2BIG; + + if (udev->state == UIST_CREATED) + return -EINVAL; + + if (copy_from_user(&setup, arg, size)) + return -EFAULT; + + if (setup.code > ABS_MAX) + return -ERANGE; + + dev = udev->dev; + + error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo); + if (error) + return error; + + input_alloc_absinfo(dev); + if (!dev->absinfo) + return -ENOMEM; + + set_bit(setup.code, dev->absbit); + dev->absinfo[setup.code] = setup.absinfo; + return 0; +} + +/* legacy setup via write() */ +static int uinput_setup_device_legacy(struct uinput_device *udev, + const char __user *buffer, size_t count) { struct uinput_user_dev *user_dev; struct input_dev *dev; @@ -474,7 +552,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer, retval = udev->state == UIST_CREATED ? uinput_inject_events(udev, buffer, count) : - uinput_setup_device(udev, buffer, count); + uinput_setup_device_legacy(udev, buffer, count); mutex_unlock(&udev->mutex); @@ -735,6 +813,12 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, uinput_destroy_device(udev); goto out; + case UI_DEV_SETUP: + retval = uinput_dev_setup(udev, p); + goto out; + + /* UI_ABS_SETUP is handled in the variable size ioctls */ + case UI_SET_EVBIT: retval = uinput_set_bit(arg, evbit, EV_MAX); goto out; @@ -879,6 +963,10 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, name = dev_name(&udev->dev->dev); retval = uinput_str_to_user(p, name, size); goto out; + + case UI_ABS_SETUP & ~IOCSIZE_MASK: + retval = uinput_abs_setup(udev, p, size); + goto out; } retval = -EINVAL; diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 17f97e5e11e7..096abb4ad5cd 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -48,6 +48,16 @@ config MOUSE_PS2_ALPS If unsure, say Y. +config MOUSE_PS2_BYD + bool "BYD PS/2 mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + Say Y here if you have a BYD PS/2 touchpad connected to + your system. + + If unsure, say Y. + config MOUSE_PS2_LOGIPS2PP bool "Logitech PS/2++ mouse protocol extension" if EXPERT default y diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index ee6a6e9563d4..6168b134937b 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -28,6 +28,7 @@ cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o cyapa_gen6.o psmouse-objs := psmouse-base.o synaptics.o focaltech.o psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o +psmouse-$(CONFIG_MOUSE_PS2_BYD) += byd.o psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 41e6cb501e6a..936f07a4e35f 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -31,6 +31,7 @@ #define ALPS_CMD_NIBBLE_10 0x01f2 #define ALPS_REG_BASE_RUSHMORE 0xc2c0 +#define ALPS_REG_BASE_V7 0xc2c0 #define ALPS_REG_BASE_PINNACLE 0x0000 static const struct alps_nibble_commands alps_v3_nibble_commands[] = { @@ -2047,7 +2048,7 @@ static int alps_absolute_mode_v3(struct psmouse *psmouse) return 0; } -static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base) +static int alps_probe_trackstick_v3_v7(struct psmouse *psmouse, int reg_base) { int ret = -EIO, reg_val; @@ -2128,15 +2129,12 @@ error: static int alps_hw_init_v3(struct psmouse *psmouse) { + struct alps_data *priv = psmouse->private; struct ps2dev *ps2dev = &psmouse->ps2dev; int reg_val; unsigned char param[4]; - reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE); - if (reg_val == -EIO) - goto error; - - if (reg_val == 0 && + if ((priv->flags & ALPS_DUALPOINT) && alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO) goto error; @@ -2613,6 +2611,11 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->decode_fields = alps_decode_pinnacle; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; + + if (alps_probe_trackstick_v3_v7(psmouse, + ALPS_REG_BASE_PINNACLE) < 0) + priv->flags &= ~ALPS_DUALPOINT; + break; case ALPS_PROTO_V3_RUSHMORE: @@ -2625,8 +2628,8 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->x_bits = 16; priv->y_bits = 12; - if (alps_probe_trackstick_v3(psmouse, - ALPS_REG_BASE_RUSHMORE) < 0) + if (alps_probe_trackstick_v3_v7(psmouse, + ALPS_REG_BASE_RUSHMORE) < 0) priv->flags &= ~ALPS_DUALPOINT; break; @@ -2676,6 +2679,9 @@ static int alps_set_protocol(struct psmouse *psmouse, if (priv->fw_ver[1] != 0xba) priv->flags |= ALPS_BUTTONPAD; + if (alps_probe_trackstick_v3_v7(psmouse, ALPS_REG_BASE_V7) < 0) + priv->flags &= ~ALPS_DUALPOINT; + break; case ALPS_PROTO_V8: diff --git a/drivers/input/mouse/byd.c b/drivers/input/mouse/byd.c new file mode 100644 index 000000000000..9425e0f6c5ce --- /dev/null +++ b/drivers/input/mouse/byd.c @@ -0,0 +1,337 @@ +/* + * BYD TouchPad PS/2 mouse driver + * + * Copyright (C) 2015 Chris Diamand <chris@diamand.org> + * + * 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/delay.h> +#include <linux/input.h> +#include <linux/libps2.h> +#include <linux/serio.h> + +#include "psmouse.h" +#include "byd.h" + +#define PS2_Y_OVERFLOW BIT_MASK(7) +#define PS2_X_OVERFLOW BIT_MASK(6) +#define PS2_Y_SIGN BIT_MASK(5) +#define PS2_X_SIGN BIT_MASK(4) +#define PS2_ALWAYS_1 BIT_MASK(3) +#define PS2_MIDDLE BIT_MASK(2) +#define PS2_RIGHT BIT_MASK(1) +#define PS2_LEFT BIT_MASK(0) + +/* + * The touchpad reports gestures in the last byte of each packet. It can take + * any of the following values: + */ + +/* One-finger scrolling in one of the edge scroll zones. */ +#define BYD_SCROLLUP 0xCA +#define BYD_SCROLLDOWN 0x36 +#define BYD_SCROLLLEFT 0xCB +#define BYD_SCROLLRIGHT 0x35 +/* Two-finger scrolling. */ +#define BYD_2DOWN 0x2B +#define BYD_2UP 0xD5 +#define BYD_2LEFT 0xD6 +#define BYD_2RIGHT 0x2A +/* Pinching in or out. */ +#define BYD_ZOOMOUT 0xD8 +#define BYD_ZOOMIN 0x28 +/* Three-finger swipe. */ +#define BYD_3UP 0xD3 +#define BYD_3DOWN 0x2D +#define BYD_3LEFT 0xD4 +#define BYD_3RIGHT 0x2C +/* Four-finger swipe. */ +#define BYD_4UP 0xCD +#define BYD_4DOWN 0x33 + +int byd_detect(struct psmouse *psmouse, bool set_properties) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[4]; + + param[0] = 0x03; + param[1] = 0x00; + param[2] = 0x00; + param[3] = 0x00; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return -1; + + if (param[1] != 0x03 || param[2] != 0x64) + return -ENODEV; + + psmouse_dbg(psmouse, "BYD touchpad detected\n"); + + if (set_properties) { + psmouse->vendor = "BYD"; + psmouse->name = "TouchPad"; + } + + return 0; +} + +static psmouse_ret_t byd_process_byte(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + u8 *pkt = psmouse->packet; + + if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) { + psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n", + pkt[0]); + return PSMOUSE_BAD_DATA; + } + + if (psmouse->pktcnt < psmouse->pktsize) + return PSMOUSE_GOOD_DATA; + + /* Otherwise, a full packet has been received */ + switch (pkt[3]) { + case 0: { + /* Standard packet */ + /* Sign-extend if a sign bit is set. */ + unsigned int signx = pkt[0] & PS2_X_SIGN ? ~0xFF : 0; + unsigned int signy = pkt[0] & PS2_Y_SIGN ? ~0xFF : 0; + int dx = signx | (int) pkt[1]; + int dy = signy | (int) pkt[2]; + + input_report_rel(psmouse->dev, REL_X, dx); + input_report_rel(psmouse->dev, REL_Y, -dy); + + input_report_key(psmouse->dev, BTN_LEFT, pkt[0] & PS2_LEFT); + input_report_key(psmouse->dev, BTN_RIGHT, pkt[0] & PS2_RIGHT); + input_report_key(psmouse->dev, BTN_MIDDLE, pkt[0] & PS2_MIDDLE); + break; + } + + case BYD_SCROLLDOWN: + case BYD_2DOWN: + input_report_rel(dev, REL_WHEEL, -1); + break; + + case BYD_SCROLLUP: + case BYD_2UP: + input_report_rel(dev, REL_WHEEL, 1); + break; + + case BYD_SCROLLLEFT: + case BYD_2LEFT: + input_report_rel(dev, REL_HWHEEL, -1); + break; + + case BYD_SCROLLRIGHT: + case BYD_2RIGHT: + input_report_rel(dev, REL_HWHEEL, 1); + break; + + case BYD_ZOOMOUT: + case BYD_ZOOMIN: + case BYD_3UP: + case BYD_3DOWN: + case BYD_3LEFT: + case BYD_3RIGHT: + case BYD_4UP: + case BYD_4DOWN: + break; + + default: + psmouse_warn(psmouse, + "Unrecognized Z: pkt = %02x %02x %02x %02x\n", + psmouse->packet[0], psmouse->packet[1], + psmouse->packet[2], psmouse->packet[3]); + return PSMOUSE_BAD_DATA; + } + + input_sync(dev); + + return PSMOUSE_FULL_PACKET; +} + +/* Send a sequence of bytes, where each is ACKed before the next is sent. */ +static int byd_send_sequence(struct psmouse *psmouse, const u8 *seq, size_t len) +{ + unsigned int i; + + for (i = 0; i < len; ++i) { + if (ps2_command(&psmouse->ps2dev, NULL, seq[i])) + return -1; + } + return 0; +} + +/* Keep scrolling after fingers are removed. */ +#define SCROLL_INERTIAL 0x01 +#define SCROLL_NO_INERTIAL 0x02 + +/* Clicking can be done by tapping or pressing. */ +#define CLICK_BOTH 0x01 +/* Clicking can only be done by pressing. */ +#define CLICK_PRESS_ONLY 0x02 + +static int byd_enable(struct psmouse *psmouse) +{ + const u8 seq1[] = { 0xE2, 0x00, 0xE0, 0x02, 0xE0 }; + const u8 seq2[] = { + 0xD3, 0x01, + 0xD0, 0x00, + 0xD0, 0x04, + /* Whether clicking is done by tapping or pressing. */ + 0xD4, CLICK_PRESS_ONLY, + 0xD5, 0x01, + 0xD7, 0x03, + /* Vertical and horizontal one-finger scroll zone inertia. */ + 0xD8, SCROLL_INERTIAL, + 0xDA, 0x05, + 0xDB, 0x02, + 0xE4, 0x05, + 0xD6, 0x01, + 0xDE, 0x04, + 0xE3, 0x01, + 0xCF, 0x00, + 0xD2, 0x03, + /* Vertical and horizontal two-finger scrolling inertia. */ + 0xE5, SCROLL_INERTIAL, + 0xD9, 0x02, + 0xD9, 0x07, + 0xDC, 0x03, + 0xDD, 0x03, + 0xDF, 0x03, + 0xE1, 0x03, + 0xD1, 0x00, + 0xCE, 0x00, + 0xCC, 0x00, + 0xE0, 0x00, + 0xE2, 0x01 + }; + u8 param[4]; + + if (byd_send_sequence(psmouse, seq1, ARRAY_SIZE(seq1))) + return -1; + + /* Send a 0x01 command, which should return 4 bytes. */ + if (ps2_command(&psmouse->ps2dev, param, 0x0401)) + return -1; + + if (byd_send_sequence(psmouse, seq2, ARRAY_SIZE(seq2))) + return -1; + + return 0; +} + +/* + * Send the set of PS/2 commands required to make it identify as an + * intellimouse with 4-byte instead of 3-byte packets. + */ +static int byd_send_intellimouse_sequence(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + u8 param[4]; + int i; + const struct { + u16 command; + u8 arg; + } seq[] = { + { PSMOUSE_CMD_RESET_BAT, 0 }, + { PSMOUSE_CMD_RESET_BAT, 0 }, + { PSMOUSE_CMD_GETID, 0 }, + { PSMOUSE_CMD_SETSCALE11, 0 }, + { PSMOUSE_CMD_SETSCALE11, 0 }, + { PSMOUSE_CMD_SETSCALE11, 0 }, + { PSMOUSE_CMD_GETINFO, 0 }, + { PSMOUSE_CMD_SETRES, 0x03 }, + { PSMOUSE_CMD_SETRATE, 0xC8 }, + { PSMOUSE_CMD_SETRATE, 0x64 }, + { PSMOUSE_CMD_SETRATE, 0x50 }, + { PSMOUSE_CMD_GETID, 0 }, + { PSMOUSE_CMD_SETRATE, 0xC8 }, + { PSMOUSE_CMD_SETRATE, 0xC8 }, + { PSMOUSE_CMD_SETRATE, 0x50 }, + { PSMOUSE_CMD_GETID, 0 }, + { PSMOUSE_CMD_SETRATE, 0x64 }, + { PSMOUSE_CMD_SETRES, 0x03 }, + { PSMOUSE_CMD_ENABLE, 0 } + }; + + memset(param, 0, sizeof(param)); + for (i = 0; i < ARRAY_SIZE(seq); ++i) { + param[0] = seq[i].arg; + if (ps2_command(ps2dev, param, seq[i].command)) + return -1; + } + + return 0; +} + +static int byd_reset_touchpad(struct psmouse *psmouse) +{ + if (byd_send_intellimouse_sequence(psmouse)) + return -EIO; + + if (byd_enable(psmouse)) + return -EIO; + + return 0; +} + +static int byd_reconnect(struct psmouse *psmouse) +{ + int retry = 0, error = 0; + + psmouse_dbg(psmouse, "Reconnect\n"); + do { + psmouse_reset(psmouse); + if (retry) + ssleep(1); + error = byd_detect(psmouse, 0); + } while (error && ++retry < 3); + + if (error) + return error; + + psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry); + + error = byd_reset_touchpad(psmouse); + if (error) { + psmouse_err(psmouse, "Unable to initialize device\n"); + return error; + } + + return 0; +} + +int byd_init(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + + if (psmouse_reset(psmouse)) + return -EIO; + + if (byd_reset_touchpad(psmouse)) + return -EIO; + + psmouse->reconnect = byd_reconnect; + psmouse->protocol_handler = byd_process_byte; + psmouse->pktsize = 4; + psmouse->resync_time = 0; + + __set_bit(BTN_MIDDLE, dev->keybit); + __set_bit(REL_WHEEL, dev->relbit); + __set_bit(REL_HWHEEL, dev->relbit); + + return 0; +} diff --git a/drivers/input/mouse/byd.h b/drivers/input/mouse/byd.h new file mode 100644 index 000000000000..d6c120cf36cd --- /dev/null +++ b/drivers/input/mouse/byd.h @@ -0,0 +1,18 @@ +#ifndef _BYD_H +#define _BYD_H + +#ifdef CONFIG_MOUSE_PS2_BYD +int byd_detect(struct psmouse *psmouse, bool set_properties); +int byd_init(struct psmouse *psmouse); +#else +static inline int byd_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +static inline int byd_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif /* CONFIG_MOUSE_PS2_BYD */ + +#endif /* _BYD_H */ diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index eb76b61418f3..dc2394292088 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -383,7 +383,7 @@ static int cyapa_open(struct input_dev *input) * when in operational mode. */ error = cyapa->ops->set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); if (error) { dev_warn(dev, "set active power failed: %d\n", error); goto out; @@ -424,7 +424,8 @@ static void cyapa_close(struct input_dev *input) pm_runtime_set_suspended(dev); if (cyapa->operational) - cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false); + cyapa->ops->set_power_mode(cyapa, + PWR_MODE_OFF, 0, CYAPA_PM_DEACTIVE); mutex_unlock(&cyapa->state_sync_lock); } @@ -534,7 +535,7 @@ static void cyapa_enable_irq_for_cmd(struct cyapa *cyapa) */ if (!input || cyapa->operational) cyapa->ops->set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); /* Gen3 always using polling mode for command. */ if (cyapa->gen >= CYAPA_GEN5) enable_irq(cyapa->client->irq); @@ -550,7 +551,7 @@ static void cyapa_disable_irq_for_cmd(struct cyapa *cyapa) disable_irq(cyapa->client->irq); if (!input || cyapa->operational) cyapa->ops->set_power_mode(cyapa, - PWR_MODE_OFF, 0, false); + PWR_MODE_OFF, 0, CYAPA_PM_ACTIVE); } } @@ -617,7 +618,8 @@ static int cyapa_initialize(struct cyapa *cyapa) /* Power down the device until we need it. */ if (cyapa->operational) - cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false); + cyapa->ops->set_power_mode(cyapa, + PWR_MODE_OFF, 0, CYAPA_PM_ACTIVE); return 0; } @@ -634,7 +636,7 @@ static int cyapa_reinitialize(struct cyapa *cyapa) /* Avoid command failures when TP was in OFF state. */ if (cyapa->operational) cyapa->ops->set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); error = cyapa_detect(cyapa); if (error) @@ -654,7 +656,7 @@ out: /* Reset to power OFF state to save power when no user open. */ if (cyapa->operational) cyapa->ops->set_power_mode(cyapa, - PWR_MODE_OFF, 0, false); + PWR_MODE_OFF, 0, CYAPA_PM_DEACTIVE); } else if (!error && cyapa->operational) { /* * Make sure only enable runtime PM when device is @@ -1392,7 +1394,7 @@ static int __maybe_unused cyapa_suspend(struct device *dev) power_mode = device_may_wakeup(dev) ? cyapa->suspend_power_mode : PWR_MODE_OFF; error = cyapa->ops->set_power_mode(cyapa, power_mode, - cyapa->suspend_sleep_time, true); + cyapa->suspend_sleep_time, CYAPA_PM_SUSPEND); if (error) dev_err(dev, "suspend set power mode failed: %d\n", error); @@ -1447,7 +1449,7 @@ static int __maybe_unused cyapa_runtime_suspend(struct device *dev) error = cyapa->ops->set_power_mode(cyapa, cyapa->runtime_suspend_power_mode, cyapa->runtime_suspend_sleep_time, - false); + CYAPA_PM_RUNTIME_SUSPEND); if (error) dev_warn(dev, "runtime suspend failed: %d\n", error); @@ -1460,7 +1462,7 @@ static int __maybe_unused cyapa_runtime_resume(struct device *dev) int error; error = cyapa->ops->set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_RUNTIME_RESUME); if (error) dev_warn(dev, "runtime resume failed: %d\n", error); diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h index b812bba8cdd7..ce951fe4516a 100644 --- a/drivers/input/mouse/cyapa.h +++ b/drivers/input/mouse/cyapa.h @@ -250,6 +250,15 @@ struct cyapa; typedef bool (*cb_sort)(struct cyapa *, u8 *, int); +enum cyapa_pm_stage { + CYAPA_PM_DEACTIVE, + CYAPA_PM_ACTIVE, + CYAPA_PM_SUSPEND, + CYAPA_PM_RESUME, + CYAPA_PM_RUNTIME_SUSPEND, + CYAPA_PM_RUNTIME_RESUME, +}; + struct cyapa_dev_ops { int (*check_fw)(struct cyapa *, const struct firmware *); int (*bl_enter)(struct cyapa *); @@ -273,7 +282,7 @@ struct cyapa_dev_ops { int (*sort_empty_output_data)(struct cyapa *, u8 *, int *, cb_sort); - int (*set_power_mode)(struct cyapa *, u8, u16, bool); + int (*set_power_mode)(struct cyapa *, u8, u16, enum cyapa_pm_stage); int (*set_proximity)(struct cyapa *, bool); }; @@ -289,6 +298,9 @@ struct cyapa_pip_cmd_states { u8 *resp_data; int *resp_len; + enum cyapa_pm_stage pm_stage; + struct mutex pm_stage_lock; + u8 irq_cmd_buf[CYAPA_REG_MAP_SIZE]; u8 empty_buf[CYAPA_REG_MAP_SIZE]; }; diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c index 1a9d12ae7538..f9600753eca5 100644 --- a/drivers/input/mouse/cyapa_gen3.c +++ b/drivers/input/mouse/cyapa_gen3.c @@ -269,6 +269,7 @@ static const struct cyapa_cmd_len cyapa_smbus_cmds[] = { { CYAPA_SMBUS_MIN_BASELINE, 1 }, /* CYAPA_CMD_MIN_BASELINE */ }; +static int cyapa_gen3_try_poll_handler(struct cyapa *cyapa); /* * cyapa_smbus_read_block - perform smbus block read command @@ -950,12 +951,14 @@ static u16 cyapa_get_wait_time_for_pwr_cmd(u8 pwr_mode) * Device power mode can only be set when device is in operational mode. */ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, - u16 always_unused, bool is_suspend_unused) + u16 always_unused, enum cyapa_pm_stage pm_stage) { - int ret; + struct input_dev *input = cyapa->input; u8 power; int tries; - u16 sleep_time; + int sleep_time; + int interval; + int ret; if (cyapa->state != CYAPA_STATE_OP) return 0; @@ -977,7 +980,7 @@ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, if ((ret & PWR_MODE_MASK) == power_mode) return 0; - sleep_time = cyapa_get_wait_time_for_pwr_cmd(ret & PWR_MODE_MASK); + sleep_time = (int)cyapa_get_wait_time_for_pwr_cmd(ret & PWR_MODE_MASK); power = ret; power &= ~PWR_MODE_MASK; power |= power_mode & PWR_MODE_MASK; @@ -995,7 +998,23 @@ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, * doing so before issuing the next command may result in errors * depending on the command's content. */ - msleep(sleep_time); + if (cyapa->operational && input && input->users && + (pm_stage == CYAPA_PM_RUNTIME_SUSPEND || + pm_stage == CYAPA_PM_RUNTIME_RESUME)) { + /* Try to polling in 120Hz, read may fail, just ignore it. */ + interval = 1000 / 120; + while (sleep_time > 0) { + if (sleep_time > interval) + msleep(interval); + else + msleep(sleep_time); + sleep_time -= interval; + cyapa_gen3_try_poll_handler(cyapa); + } + } else { + msleep(sleep_time); + } + return ret; } @@ -1112,7 +1131,7 @@ static int cyapa_gen3_do_operational_check(struct cyapa *cyapa) * may cause problems, so we set the power mode first here. */ error = cyapa_gen3_set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); if (error) dev_err(dev, "%s: set full power mode failed: %d\n", __func__, error); @@ -1168,32 +1187,16 @@ static bool cyapa_gen3_irq_cmd_handler(struct cyapa *cyapa) return false; } -static int cyapa_gen3_irq_handler(struct cyapa *cyapa) +static int cyapa_gen3_event_process(struct cyapa *cyapa, + struct cyapa_reg_data *data) { struct input_dev *input = cyapa->input; - struct device *dev = &cyapa->client->dev; - struct cyapa_reg_data data; int num_fingers; - int ret; int i; - ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); - if (ret != sizeof(data)) { - dev_err(dev, "failed to read report data, (%d)\n", ret); - return -EINVAL; - } - - if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || - (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || - (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) { - dev_err(dev, "invalid device state bytes, %02x %02x\n", - data.device_status, data.finger_btn); - return -EINVAL; - } - - num_fingers = (data.finger_btn >> 4) & 0x0f; + num_fingers = (data->finger_btn >> 4) & 0x0f; for (i = 0; i < num_fingers; i++) { - const struct cyapa_touch *touch = &data.touches[i]; + const struct cyapa_touch *touch = &data->touches[i]; /* Note: touch->id range is 1 to 15; slots are 0 to 14. */ int slot = touch->id - 1; @@ -1210,18 +1213,65 @@ static int cyapa_gen3_irq_handler(struct cyapa *cyapa) if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) input_report_key(input, BTN_LEFT, - !!(data.finger_btn & OP_DATA_LEFT_BTN)); + !!(data->finger_btn & OP_DATA_LEFT_BTN)); if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) input_report_key(input, BTN_MIDDLE, - !!(data.finger_btn & OP_DATA_MIDDLE_BTN)); + !!(data->finger_btn & OP_DATA_MIDDLE_BTN)); if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) input_report_key(input, BTN_RIGHT, - !!(data.finger_btn & OP_DATA_RIGHT_BTN)); + !!(data->finger_btn & OP_DATA_RIGHT_BTN)); input_sync(input); return 0; } +static int cyapa_gen3_irq_handler(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; + struct cyapa_reg_data data; + int ret; + + ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); + if (ret != sizeof(data)) { + dev_err(dev, "failed to read report data, (%d)\n", ret); + return -EINVAL; + } + + if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || + (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || + (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) { + dev_err(dev, "invalid device state bytes: %02x %02x\n", + data.device_status, data.finger_btn); + return -EINVAL; + } + + return cyapa_gen3_event_process(cyapa, &data); +} + +/* + * This function will be called in the cyapa_gen3_set_power_mode function, + * and it's known that it may failed in some situation after the set power + * mode command was sent. So this function is aimed to avoid the knwon + * and unwanted output I2C and data parse error messages. + */ +static int cyapa_gen3_try_poll_handler(struct cyapa *cyapa) +{ + struct cyapa_reg_data data; + int ret; + + ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); + if (ret != sizeof(data)) + return -EINVAL; + + if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || + (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || + (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) + return -EINVAL; + + return cyapa_gen3_event_process(cyapa, &data); + +} + static int cyapa_gen3_initialize(struct cyapa *cyapa) { return 0; } static int cyapa_gen3_bl_initiate(struct cyapa *cyapa, const struct firmware *fw) { return 0; } diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c index 118ba977181e..5775d40b3d53 100644 --- a/drivers/input/mouse/cyapa_gen5.c +++ b/drivers/input/mouse/cyapa_gen5.c @@ -342,6 +342,9 @@ u8 pip_bl_read_app_info[] = { 0x04, 0x00, 0x0b, 0x00, 0x40, 0x00, static u8 cyapa_pip_bl_cmd_key[] = { 0xa5, 0x01, 0x02, 0x03, 0xff, 0xfe, 0xfd, 0x5a }; +static int cyapa_pip_event_process(struct cyapa *cyapa, + struct cyapa_pip_report_data *report_data); + int cyapa_pip_cmd_state_initialize(struct cyapa *cyapa) { struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; @@ -350,6 +353,9 @@ int cyapa_pip_cmd_state_initialize(struct cyapa *cyapa) atomic_set(&pip->cmd_issued, 0); mutex_init(&pip->cmd_lock); + mutex_init(&pip->pm_stage_lock); + pip->pm_stage = CYAPA_PM_DEACTIVE; + pip->resp_sort_func = NULL; pip->in_progress_cmd = PIP_INVALID_CMD; pip->resp_data = NULL; @@ -397,6 +403,38 @@ ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size) return 0; } +static void cyapa_set_pip_pm_state(struct cyapa *cyapa, + enum cyapa_pm_stage pm_stage) +{ + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; + + mutex_lock(&pip->pm_stage_lock); + pip->pm_stage = pm_stage; + mutex_unlock(&pip->pm_stage_lock); +} + +static void cyapa_reset_pip_pm_state(struct cyapa *cyapa) +{ + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; + + /* Indicates the pip->pm_stage is not valid. */ + mutex_lock(&pip->pm_stage_lock); + pip->pm_stage = CYAPA_PM_DEACTIVE; + mutex_unlock(&pip->pm_stage_lock); +} + +static enum cyapa_pm_stage cyapa_get_pip_pm_state(struct cyapa *cyapa) +{ + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; + enum cyapa_pm_stage pm_stage; + + mutex_lock(&pip->pm_stage_lock); + pm_stage = pip->pm_stage; + mutex_unlock(&pip->pm_stage_lock); + + return pm_stage; +} + /** * This function is aimed to dump all not read data in Gen5 trackpad * before send any command, otherwise, the interrupt line will be blocked. @@ -404,7 +442,9 @@ ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size) int cyapa_empty_pip_output_data(struct cyapa *cyapa, u8 *buf, int *len, cb_sort func) { + struct input_dev *input = cyapa->input; struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; + enum cyapa_pm_stage pm_stage = cyapa_get_pip_pm_state(cyapa); int length; int report_count; int empty_count; @@ -478,6 +518,12 @@ int cyapa_empty_pip_output_data(struct cyapa *cyapa, *len = length; /* Response found, success. */ return 0; + } else if (cyapa->operational && input && input->users && + (pm_stage == CYAPA_PM_RUNTIME_RESUME || + pm_stage == CYAPA_PM_RUNTIME_SUSPEND)) { + /* Parse the data and report it if it's valid. */ + cyapa_pip_event_process(cyapa, + (struct cyapa_pip_report_data *)pip->empty_buf); } error = -EINVAL; @@ -1566,15 +1612,17 @@ int cyapa_pip_deep_sleep(struct cyapa *cyapa, u8 state) } static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, - u8 power_mode, u16 sleep_time, bool is_suspend) + u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage) { struct device *dev = &cyapa->client->dev; u8 power_state; - int error; + int error = 0; if (cyapa->state != CYAPA_STATE_GEN5_APP) return 0; + cyapa_set_pip_pm_state(cyapa, pm_stage); + if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) { /* * Assume TP in deep sleep mode when driver is loaded, @@ -1597,7 +1645,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, power_mode == PWR_MODE_BTN_ONLY || PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) { /* Has in correct power mode state, early return. */ - return 0; + goto out; } } @@ -1605,11 +1653,11 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, error = cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF); if (error) { dev_err(dev, "enter deep sleep fail: %d\n", error); - return error; + goto out; } PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); - return 0; + goto out; } /* @@ -1621,7 +1669,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, error = cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON); if (error) { dev_err(dev, "deep sleep wake fail: %d\n", error); - return error; + goto out; } } @@ -1630,7 +1678,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, GEN5_POWER_STATE_ACTIVE); if (error) { dev_err(dev, "change to active fail: %d\n", error); - return error; + goto out; } PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE); @@ -1639,7 +1687,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, GEN5_POWER_STATE_BTN_ONLY); if (error) { dev_err(dev, "fail to button only mode: %d\n", error); - return error; + goto out; } PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY); @@ -1664,7 +1712,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, if (error) { dev_err(dev, "set power state to 0x%02x failed: %d\n", power_state, error); - return error; + goto out; } /* @@ -1677,14 +1725,16 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, * is suspending which may cause interrupt line unable to be * asserted again. */ - if (is_suspend) + if (pm_stage == CYAPA_PM_SUSPEND) cyapa_gen5_disable_pip_report(cyapa); PIP_DEV_SET_PWR_STATE(cyapa, cyapa_sleep_time_to_pwr_cmd(sleep_time)); } - return 0; +out: + cyapa_reset_pip_pm_state(cyapa); + return error; } int cyapa_pip_resume_scanning(struct cyapa *cyapa) @@ -2513,7 +2563,7 @@ static int cyapa_gen5_do_operational_check(struct cyapa *cyapa) * the device state is required. */ error = cyapa_gen5_set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); if (error) dev_warn(dev, "%s: failed to set power active mode.\n", __func__); @@ -2715,7 +2765,6 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa) struct device *dev = &cyapa->client->dev; struct cyapa_pip_report_data report_data; unsigned int report_len; - u8 report_id; int ret; if (!cyapa_is_pip_app_mode(cyapa)) { @@ -2752,7 +2801,23 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa) return -EINVAL; } - report_id = report_data.report_head[PIP_RESP_REPORT_ID_OFFSET]; + return cyapa_pip_event_process(cyapa, &report_data); +} + +static int cyapa_pip_event_process(struct cyapa *cyapa, + struct cyapa_pip_report_data *report_data) +{ + struct device *dev = &cyapa->client->dev; + unsigned int report_len; + u8 report_id; + + report_len = get_unaligned_le16( + &report_data->report_head[PIP_RESP_LENGTH_OFFSET]); + /* Idle, no data for report. */ + if (report_len == PIP_RESP_LENGTH_SIZE) + return 0; + + report_id = report_data->report_head[PIP_RESP_REPORT_ID_OFFSET]; if (report_id == PIP_WAKEUP_EVENT_REPORT_ID && report_len == PIP_WAKEUP_EVENT_SIZE) { /* @@ -2805,11 +2870,11 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa) } if (report_id == PIP_TOUCH_REPORT_ID) - cyapa_pip_report_touches(cyapa, &report_data); + cyapa_pip_report_touches(cyapa, report_data); else if (report_id == PIP_PROXIMITY_REPORT_ID) - cyapa_pip_report_proximity(cyapa, &report_data); + cyapa_pip_report_proximity(cyapa, report_data); else - cyapa_pip_report_buttons(cyapa, &report_data); + cyapa_pip_report_buttons(cyapa, report_data); return 0; } diff --git a/drivers/input/mouse/cyapa_gen6.c b/drivers/input/mouse/cyapa_gen6.c index e4eb048d1bf6..016397850b1b 100644 --- a/drivers/input/mouse/cyapa_gen6.c +++ b/drivers/input/mouse/cyapa_gen6.c @@ -425,7 +425,7 @@ static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state) } static int cyapa_gen6_set_power_mode(struct cyapa *cyapa, - u8 power_mode, u16 sleep_time, bool is_suspend) + u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage) { struct device *dev = &cyapa->client->dev; struct gen6_interval_setting *interval_setting = @@ -689,7 +689,7 @@ static int cyapa_gen6_operational_check(struct cyapa *cyapa) * the device state is required. */ error = cyapa_gen6_set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); if (error) dev_warn(dev, "%s: failed to set power active mode.\n", __func__); diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 537ebb0e193a..78f93cf68840 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1222,7 +1222,7 @@ static int elantech_set_input_params(struct psmouse *psmouse) input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, ETP_WMAX_V2, 0, 0); } - input_mt_init_slots(dev, 2, 0); + input_mt_init_slots(dev, 2, INPUT_MT_SEMI_MT); input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); break; diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index 4d5576de81be..c8c6a8cc329d 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -49,12 +49,6 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties) return 0; } -static void focaltech_reset(struct psmouse *psmouse) -{ - ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); - psmouse_reset(psmouse); -} - #ifdef CONFIG_MOUSE_PS2_FOCALTECH /* @@ -300,6 +294,12 @@ static int focaltech_switch_protocol(struct psmouse *psmouse) return 0; } +static void focaltech_reset(struct psmouse *psmouse) +{ + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); + psmouse_reset(psmouse); +} + static void focaltech_disconnect(struct psmouse *psmouse) { focaltech_reset(psmouse); @@ -456,14 +456,4 @@ fail: kfree(priv); return error; } - -#else /* CONFIG_MOUSE_PS2_FOCALTECH */ - -int focaltech_init(struct psmouse *psmouse) -{ - focaltech_reset(psmouse); - - return 0; -} - #endif /* CONFIG_MOUSE_PS2_FOCALTECH */ diff --git a/drivers/input/mouse/focaltech.h b/drivers/input/mouse/focaltech.h index ca61ebff373e..783b28e8e10b 100644 --- a/drivers/input/mouse/focaltech.h +++ b/drivers/input/mouse/focaltech.h @@ -18,6 +18,14 @@ #define _FOCALTECH_H int focaltech_detect(struct psmouse *psmouse, bool set_properties); + +#ifdef CONFIG_MOUSE_PS2_FOCALTECH int focaltech_init(struct psmouse *psmouse); +#else +static inline int focaltech_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif #endif diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index 136e222e2a16..422da1cd9e76 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -325,7 +325,7 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse, * that support it. */ -int ps2pp_init(struct psmouse *psmouse, bool set_properties) +int ps2pp_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[4]; diff --git a/drivers/input/mouse/logips2pp.h b/drivers/input/mouse/logips2pp.h index 0c186f0282d9..bf629453e095 100644 --- a/drivers/input/mouse/logips2pp.h +++ b/drivers/input/mouse/logips2pp.h @@ -12,9 +12,9 @@ #define _LOGIPS2PP_H #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP -int ps2pp_init(struct psmouse *psmouse, bool set_properties); +int ps2pp_detect(struct psmouse *psmouse, bool set_properties); #else -inline int ps2pp_init(struct psmouse *psmouse, bool set_properties) +static inline int ps2pp_detect(struct psmouse *psmouse, bool set_properties) { return -ENOSYS; } diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index ad18dab0ac47..39d1becd35c9 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -37,6 +37,7 @@ #include "cypress_ps2.h" #include "focaltech.h" #include "vmmouse.h" +#include "byd.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -119,6 +120,7 @@ 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); @@ -129,7 +131,6 @@ struct psmouse_protocol { * psmouse_process_byte() analyzes the PS/2 data stream and reports * relevant events to the input module once full packet has arrived. */ - psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) { struct input_dev *dev = psmouse->dev; @@ -138,22 +139,16 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) if (psmouse->pktcnt < psmouse->pktsize) return PSMOUSE_GOOD_DATA; -/* - * Full packet accumulated, process it - */ + /* Full packet accumulated, process it */ -/* - * Scroll wheel on IntelliMice, scroll buttons on NetMice - */ - - if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS) + switch (psmouse->type) { + case PSMOUSE_IMPS: + /* IntelliMouse has scroll wheel */ input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); + break; -/* - * Scroll wheel and buttons on IntelliMouse Explorer - */ - - if (psmouse->type == PSMOUSE_IMEX) { + case PSMOUSE_IMEX: + /* Scroll wheel and buttons on IntelliMouse Explorer */ switch (packet[3] & 0xC0) { case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */ input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); @@ -168,39 +163,42 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1); break; } - } + break; -/* - * Extra buttons on Genius NewNet 3D - */ + case PSMOUSE_GENPS: + /* Report scroll buttons on NetMice */ + input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); - if (psmouse->type == PSMOUSE_GENPS) { + /* Extra buttons on Genius NewNet 3D */ input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1); input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1); - } + break; -/* - * Extra button on ThinkingMouse - */ - if (psmouse->type == PSMOUSE_THINKPS) { + case PSMOUSE_THINKPS: + /* Extra button on ThinkingMouse */ input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1); - /* Without this bit of weirdness moving up gives wildly high Y changes. */ + + /* + * Without this bit of weirdness moving up gives wildly + * high Y changes. + */ packet[1] |= (packet[0] & 0x40) << 1; - } + break; -/* - * Cortron PS2 Trackball reports SIDE button on the 4th bit of the first - * byte. - */ - if (psmouse->type == PSMOUSE_CORTRON) { + case PSMOUSE_CORTRON: + /* + * Cortron PS2 Trackball reports SIDE button in the + * 4th bit of the first byte. + */ input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1); packet[0] |= 0x08; - } + break; -/* - * Generic PS/2 Mouse - */ + default: + break; + } + /* Generic PS/2 Mouse */ input_report_key(dev, BTN_LEFT, packet[0] & 1); input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); @@ -222,7 +220,6 @@ void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, /* * __psmouse_set_state() sets new psmouse state and resets all flags. */ - static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) { psmouse->state = new_state; @@ -231,13 +228,11 @@ static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_sta psmouse->last = jiffies; } - /* * psmouse_set_state() sets new psmouse state and resets all flags and * counters while holding serio lock so fighting with interrupt handler * is not a concern. */ - void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) { serio_pause_rx(psmouse->ps2dev.serio); @@ -249,7 +244,6 @@ void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) * psmouse_handle_byte() processes one byte of the input data stream * by calling corresponding protocol handler. */ - static int psmouse_handle_byte(struct psmouse *psmouse) { psmouse_ret_t rc = psmouse->protocol_handler(psmouse); @@ -292,7 +286,6 @@ static int psmouse_handle_byte(struct psmouse *psmouse) * psmouse_interrupt() handles incoming characters, either passing them * for normal processing or gathering them as command response. */ - static irqreturn_t psmouse_interrupt(struct serio *serio, unsigned char data, unsigned int flags) { @@ -335,9 +328,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, } psmouse->packet[psmouse->pktcnt++] = data; -/* - * Check if this is a new device announcement (0xAA 0x00) - */ + + /* Check if this is a new device announcement (0xAA 0x00) */ if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) { if (psmouse->pktcnt == 1) { psmouse->last = jiffies; @@ -351,9 +343,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, serio_reconnect(serio); goto out; } -/* - * Not a new device, try processing first byte normally - */ + + /* Not a new device, try processing first byte normally */ psmouse->pktcnt = 1; if (psmouse_handle_byte(psmouse)) goto out; @@ -361,9 +352,10 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, psmouse->packet[psmouse->pktcnt++] = data; } -/* - * See if we need to force resync because mouse was idle for too long - */ + /* + * See if we need to force resync because mouse was idle for + * too long. + */ if (psmouse->state == PSMOUSE_ACTIVATED && psmouse->pktcnt == 1 && psmouse->resync_time && time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) { @@ -380,7 +372,6 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, return IRQ_HANDLED; } - /* * psmouse_sliced_command() sends an extended PS/2 command to the mouse * using sliced syntax, understood by advanced devices, such as Logitech @@ -404,7 +395,6 @@ int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command) return 0; } - /* * psmouse_reset() resets the mouse into power-on state. */ @@ -424,7 +414,6 @@ int psmouse_reset(struct psmouse *psmouse) /* * Here we set the mouse resolution. */ - void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) { static const unsigned char params[] = { 0, 1, 2, 2, 3 }; @@ -441,7 +430,6 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) /* * Here we set the mouse report rate. */ - static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) { static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; @@ -457,7 +445,6 @@ static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) /* * Here we set the mouse scaling. */ - static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale) { ps2_command(&psmouse->ps2dev, NULL, @@ -468,7 +455,6 @@ static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale) /* * psmouse_poll() - default poll handler. Everyone except for ALPS uses it. */ - static int psmouse_poll(struct psmouse *psmouse) { return ps2_command(&psmouse->ps2dev, psmouse->packet, @@ -602,7 +588,7 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties) if (param[0] != 4) return -1; -/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */ + /* Magic to enable horizontal scrolling on IntelliMouse 4.0 */ param[0] = 200; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); param[0] = 80; @@ -672,10 +658,10 @@ static int ps2bare_detect(struct psmouse *psmouse, bool set_properties) if (!psmouse->name) psmouse->name = "Mouse"; -/* - * We have no way of figuring true number of buttons so let's - * assume that the device has 3. - */ + /* + * We have no way of figuring true number of buttons so let's + * assume that the device has 3. + */ __set_bit(BTN_MIDDLE, psmouse->dev->keybit); } @@ -699,284 +685,6 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties) return 0; } -/* - * Apply default settings to the psmouse structure. Most of them will - * be overridden by individual protocol initialization routines. - */ - -static void psmouse_apply_defaults(struct psmouse *psmouse) -{ - struct input_dev *input_dev = psmouse->dev; - - memset(input_dev->evbit, 0, sizeof(input_dev->evbit)); - memset(input_dev->keybit, 0, sizeof(input_dev->keybit)); - memset(input_dev->relbit, 0, sizeof(input_dev->relbit)); - memset(input_dev->absbit, 0, sizeof(input_dev->absbit)); - memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit)); - - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(EV_REL, input_dev->evbit); - - __set_bit(BTN_LEFT, input_dev->keybit); - __set_bit(BTN_RIGHT, input_dev->keybit); - - __set_bit(REL_X, input_dev->relbit); - __set_bit(REL_Y, input_dev->relbit); - - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - - psmouse->set_rate = psmouse_set_rate; - psmouse->set_resolution = psmouse_set_resolution; - psmouse->set_scale = psmouse_set_scale; - psmouse->poll = psmouse_poll; - psmouse->protocol_handler = psmouse_process_byte; - psmouse->pktsize = 3; - psmouse->reconnect = NULL; - psmouse->disconnect = NULL; - psmouse->cleanup = NULL; - psmouse->pt_activate = NULL; - psmouse->pt_deactivate = NULL; -} - -/* - * Apply default settings to the psmouse structure and call specified - * protocol detection or initialization routine. - */ -static int psmouse_do_detect(int (*detect)(struct psmouse *psmouse, - bool set_properties), - struct psmouse *psmouse, bool set_properties) -{ - if (set_properties) - psmouse_apply_defaults(psmouse); - - return detect(psmouse, set_properties); -} - -/* - * psmouse_extensions() probes for any extensions to the basic PS/2 protocol - * the mouse may have. - */ - -static int psmouse_extensions(struct psmouse *psmouse, - unsigned int max_proto, bool set_properties) -{ - bool synaptics_hardware = false; - -/* Always check for focaltech, this is safe as it uses pnp-id matching */ - if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) { - if (max_proto > PSMOUSE_IMEX) { - if (!set_properties || focaltech_init(psmouse) == 0) { - if (IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH)) - return PSMOUSE_FOCALTECH; - /* - * Note that we need to also restrict - * psmouse_max_proto so that psmouse_initialize() - * does not try to reset rate and resolution, - * because even that upsets the device. - */ - psmouse_max_proto = PSMOUSE_PS2; - return PSMOUSE_PS2; - } - } - } - -/* - * We always check for lifebook because it does not disturb mouse - * (it only checks DMI information). - */ - if (psmouse_do_detect(lifebook_detect, psmouse, set_properties) == 0) { - if (max_proto > PSMOUSE_IMEX) { - if (!set_properties || lifebook_init(psmouse) == 0) - return PSMOUSE_LIFEBOOK; - } - } - - if (psmouse_do_detect(vmmouse_detect, psmouse, set_properties) == 0) { - if (max_proto > PSMOUSE_IMEX) { - if (!set_properties || vmmouse_init(psmouse) == 0) - return PSMOUSE_VMMOUSE; - } - } - -/* - * Try Kensington ThinkingMouse (we try first, because synaptics probe - * upsets the thinkingmouse). - */ - - if (max_proto > PSMOUSE_IMEX && - psmouse_do_detect(thinking_detect, psmouse, set_properties) == 0) { - return PSMOUSE_THINKPS; - } - -/* - * Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol - * support is disabled in config - we need to know if it is synaptics so we - * can reset it properly after probing for intellimouse. - */ - if (max_proto > PSMOUSE_PS2 && - psmouse_do_detect(synaptics_detect, psmouse, set_properties) == 0) { - synaptics_hardware = true; - - if (max_proto > PSMOUSE_IMEX) { -/* - * Try activating protocol, but check if support is 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; - } - -/* - * Some Synaptics touchpads can emulate extended protocols (like IMPS/2). - * Unfortunately Logitech/Genius probes confuse some firmware versions so - * we'll have to skip them. - */ - max_proto = PSMOUSE_IMEX; - } -/* - * Make sure that touchpad is in relative mode, gestures (taps) are enabled - */ - synaptics_reset(psmouse); - } - -/* - * Try Cypress Trackpad. - * Must try it before Finger Sensing Pad because Finger Sensing Pad probe - * upsets some modules of Cypress Trackpads. - */ - if (max_proto > PSMOUSE_IMEX && - cypress_detect(psmouse, set_properties) == 0) { - if (IS_ENABLED(CONFIG_MOUSE_PS2_CYPRESS)) { - if (cypress_init(psmouse) == 0) - return PSMOUSE_CYPRESS; - - /* - * Finger Sensing Pad probe upsets some modules of - * Cypress Trackpad, must avoid Finger Sensing Pad - * probe if Cypress Trackpad device detected. - */ - return PSMOUSE_PS2; - } - - max_proto = PSMOUSE_IMEX; - } - -/* - * Try ALPS TouchPad - */ - if (max_proto > PSMOUSE_IMEX) { - ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); - if (psmouse_do_detect(alps_detect, - psmouse, set_properties) == 0) { - if (!set_properties || alps_init(psmouse) == 0) - return PSMOUSE_ALPS; -/* - * Init failed, try basic relative protocols - */ - max_proto = PSMOUSE_IMEX; - } - } - -/* - * Try OLPC HGPK touchpad. - */ - if (max_proto > PSMOUSE_IMEX && - psmouse_do_detect(hgpk_detect, psmouse, set_properties) == 0) { - if (!set_properties || hgpk_init(psmouse) == 0) - return PSMOUSE_HGPK; -/* - * Init failed, try basic relative protocols - */ - max_proto = PSMOUSE_IMEX; - } - -/* - * Try Elantech touchpad. - */ - if (max_proto > PSMOUSE_IMEX && - psmouse_do_detect(elantech_detect, psmouse, set_properties) == 0) { - if (!set_properties || elantech_init(psmouse) == 0) - return PSMOUSE_ELANTECH; -/* - * Init failed, try basic relative protocols - */ - max_proto = PSMOUSE_IMEX; - } - - if (max_proto > PSMOUSE_IMEX) { - if (psmouse_do_detect(genius_detect, - psmouse, set_properties) == 0) - return PSMOUSE_GENPS; - - if (psmouse_do_detect(ps2pp_init, - psmouse, set_properties) == 0) - return PSMOUSE_PS2PP; - - if (psmouse_do_detect(trackpoint_detect, - psmouse, set_properties) == 0) - return PSMOUSE_TRACKPOINT; - - if (psmouse_do_detect(touchkit_ps2_detect, - psmouse, set_properties) == 0) - return PSMOUSE_TOUCHKIT_PS2; - } - -/* - * Try Finger Sensing Pad. We do it here because its probe upsets - * Trackpoint devices (causing TP_READ_ID command to time out). - */ - if (max_proto > PSMOUSE_IMEX) { - if (psmouse_do_detect(fsp_detect, - psmouse, set_properties) == 0) { - if (!set_properties || fsp_init(psmouse) == 0) - return PSMOUSE_FSP; -/* - * Init failed, try basic relative protocols - */ - max_proto = PSMOUSE_IMEX; - } - } - -/* - * Reset to defaults in case the device got confused by extended - * protocol probes. Note that we follow up with full reset because - * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS. - */ - ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); - psmouse_reset(psmouse); - - if (max_proto >= PSMOUSE_IMEX && - psmouse_do_detect(im_explorer_detect, - psmouse, set_properties) == 0) { - return PSMOUSE_IMEX; - } - - if (max_proto >= PSMOUSE_IMPS && - psmouse_do_detect(intellimouse_detect, - psmouse, set_properties) == 0) { - return PSMOUSE_IMPS; - } - -/* - * Okay, all failed, we have a standard mouse here. The number of the buttons - * is still a question, though. We assume 3. - */ - psmouse_do_detect(ps2bare_detect, psmouse, set_properties); - - if (synaptics_hardware) { -/* - * We detected Synaptics hardware but it did not respond to IMPS/2 probes. - * We need to reset the touchpad because if there is a track point on the - * pass through port it could get disabled while probing for protocol - * extensions. - */ - psmouse_reset(psmouse); - } - - return PSMOUSE_PS2; -} - static const struct psmouse_protocol psmouse_protocols[] = { { .type = PSMOUSE_PS2, @@ -985,13 +693,14 @@ static const struct psmouse_protocol psmouse_protocols[] = { .maxproto = true, .ignore_parity = true, .detect = ps2bare_detect, + .try_passthru = true, }, #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP { .type = PSMOUSE_PS2PP, .name = "PS2++", .alias = "logitech", - .detect = ps2pp_init, + .detect = ps2pp_detect, }, #endif { @@ -1022,6 +731,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .maxproto = true, .ignore_parity = true, .detect = intellimouse_detect, + .try_passthru = true, }, { .type = PSMOUSE_IMEX, @@ -1030,6 +740,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .maxproto = true, .ignore_parity = true, .detect = im_explorer_detect, + .try_passthru = true, }, #ifdef CONFIG_MOUSE_PS2_SYNAPTICS { @@ -1061,6 +772,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .type = PSMOUSE_LIFEBOOK, .name = "LBPS/2", .alias = "lifebook", + .detect = lifebook_detect, .init = lifebook_init, }, #endif @@ -1070,6 +782,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .name = "TPPS/2", .alias = "trackpoint", .detect = trackpoint_detect, + .try_passthru = true, }, #endif #ifdef CONFIG_MOUSE_PS2_TOUCHKIT @@ -1130,6 +843,15 @@ static const struct psmouse_protocol psmouse_protocols[] = { .init = vmmouse_init, }, #endif +#ifdef CONFIG_MOUSE_PS2_BYD + { + .type = PSMOUSE_BYD, + .name = "BydPS/2", + .alias = "byd", + .detect = byd_detect, + .init = byd_init, + }, +#endif { .type = PSMOUSE_AUTO, .name = "auto", @@ -1138,7 +860,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { }, }; -static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) +static const struct psmouse_protocol *__psmouse_protocol_by_type(enum psmouse_type type) { int i; @@ -1146,6 +868,17 @@ static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type if (psmouse_protocols[i].type == type) return &psmouse_protocols[i]; + return NULL; +} + +static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) +{ + const struct psmouse_protocol *proto; + + proto = __psmouse_protocol_by_type(type); + if (proto) + return proto; + WARN_ON(1); return &psmouse_protocols[0]; } @@ -1166,23 +899,292 @@ static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, return NULL; } +/* + * Apply default settings to the psmouse structure. Most of them will + * be overridden by individual protocol initialization routines. + */ +static void psmouse_apply_defaults(struct psmouse *psmouse) +{ + struct input_dev *input_dev = psmouse->dev; + + memset(input_dev->evbit, 0, sizeof(input_dev->evbit)); + memset(input_dev->keybit, 0, sizeof(input_dev->keybit)); + memset(input_dev->relbit, 0, sizeof(input_dev->relbit)); + memset(input_dev->absbit, 0, sizeof(input_dev->absbit)); + memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit)); + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_REL, input_dev->evbit); + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + + __set_bit(REL_X, input_dev->relbit); + __set_bit(REL_Y, input_dev->relbit); + + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + + psmouse->set_rate = psmouse_set_rate; + psmouse->set_resolution = psmouse_set_resolution; + psmouse->set_scale = psmouse_set_scale; + psmouse->poll = psmouse_poll; + psmouse->protocol_handler = psmouse_process_byte; + psmouse->pktsize = 3; + psmouse->reconnect = NULL; + psmouse->disconnect = NULL; + psmouse->cleanup = NULL; + psmouse->pt_activate = NULL; + psmouse->pt_deactivate = NULL; +} + +static bool psmouse_try_protocol(struct psmouse *psmouse, + enum psmouse_type type, + unsigned int *max_proto, + bool set_properties, bool init_allowed) +{ + const struct psmouse_protocol *proto; + + proto = __psmouse_protocol_by_type(type); + if (!proto) + return false; + + if (psmouse->ps2dev.serio->id.type == SERIO_PS_PSTHRU && + !proto->try_passthru) { + return false; + } + + if (set_properties) + psmouse_apply_defaults(psmouse); + + if (proto->detect(psmouse, set_properties) != 0) + return false; + + if (set_properties && proto->init && init_allowed) { + if (proto->init(psmouse) != 0) { + /* + * We detected device, but init failed. Adjust + * max_proto so we only try standard protocols. + */ + if (*max_proto > PSMOUSE_IMEX) + *max_proto = PSMOUSE_IMEX; + + return false; + } + } + + return true; +} /* - * psmouse_probe() probes for a PS/2 mouse. + * psmouse_extensions() probes for any extensions to the basic PS/2 protocol + * the mouse may have. */ +static int psmouse_extensions(struct psmouse *psmouse, + unsigned int max_proto, bool set_properties) +{ + bool synaptics_hardware = false; + + /* + * Always check for focaltech, this is safe as it uses pnp-id + * matching. + */ + if (psmouse_try_protocol(psmouse, PSMOUSE_FOCALTECH, + &max_proto, set_properties, false)) { + if (max_proto > PSMOUSE_IMEX && + IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) && + (!set_properties || focaltech_init(psmouse) == 0)) { + return PSMOUSE_FOCALTECH; + } + /* + * Restrict psmouse_max_proto so that psmouse_initialize() + * does not try to reset rate and resolution, because even + * that upsets the device. + * This also causes us to basically fall through to basic + * protocol detection, where we fully reset the mouse, + * and set it up as bare PS/2 protocol device. + */ + psmouse_max_proto = max_proto = PSMOUSE_PS2; + } + + /* + * We always check for LifeBook because it does not disturb mouse + * (it only checks DMI information). + */ + if (psmouse_try_protocol(psmouse, PSMOUSE_LIFEBOOK, &max_proto, + set_properties, max_proto > PSMOUSE_IMEX)) + return PSMOUSE_LIFEBOOK; + + if (psmouse_try_protocol(psmouse, PSMOUSE_VMMOUSE, &max_proto, + set_properties, max_proto > PSMOUSE_IMEX)) + return PSMOUSE_VMMOUSE; + + /* + * Try Kensington ThinkingMouse (we try first, because Synaptics + * probe upsets the ThinkingMouse). + */ + if (max_proto > PSMOUSE_IMEX && + psmouse_try_protocol(psmouse, PSMOUSE_THINKPS, &max_proto, + set_properties, true)) { + return PSMOUSE_THINKPS; + } + + /* + * Try Synaptics TouchPad. Note that probing is done even if + * Synaptics protocol support is disabled in config - we need to + * know if it is Synaptics so we can reset it properly after + * probing for IntelliMouse. + */ + if (max_proto > PSMOUSE_PS2 && + psmouse_try_protocol(psmouse, PSMOUSE_SYNAPTICS, &max_proto, + set_properties, false)) { + synaptics_hardware = true; + if (max_proto > PSMOUSE_IMEX) { + /* + * Try activating protocol, but check if support is + * 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; + } + + /* + * Some Synaptics touchpads can emulate extended + * protocols (like IMPS/2). Unfortunately + * Logitech/Genius probes confuse some firmware + * versions so we'll have to skip them. + */ + max_proto = PSMOUSE_IMEX; + } + + /* + * Make sure that touchpad is in relative mode, gestures + * (taps) are enabled. + */ + synaptics_reset(psmouse); + } + + /* + * Try Cypress Trackpad. We must try it before Finger Sensing Pad + * because Finger Sensing Pad probe upsets some modules of Cypress + * Trackpads. + */ + if (max_proto > PSMOUSE_IMEX && + psmouse_try_protocol(psmouse, PSMOUSE_CYPRESS, &max_proto, + set_properties, true)) { + return PSMOUSE_CYPRESS; + } + + /* Try ALPS TouchPad */ + if (max_proto > PSMOUSE_IMEX) { + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); + if (psmouse_try_protocol(psmouse, PSMOUSE_ALPS, + &max_proto, set_properties, true)) + return PSMOUSE_ALPS; + } + + /* Try OLPC HGPK touchpad */ + if (max_proto > PSMOUSE_IMEX && + psmouse_try_protocol(psmouse, PSMOUSE_HGPK, &max_proto, + set_properties, true)) { + return PSMOUSE_HGPK; + } + + /* Try Elantech touchpad */ + if (max_proto > PSMOUSE_IMEX && + psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH, + &max_proto, set_properties, true)) { + return PSMOUSE_ELANTECH; + } + + if (max_proto > PSMOUSE_IMEX) { + if (psmouse_try_protocol(psmouse, PSMOUSE_GENPS, + &max_proto, set_properties, true)) + return PSMOUSE_GENPS; + + if (psmouse_try_protocol(psmouse, PSMOUSE_PS2PP, + &max_proto, set_properties, true)) + return PSMOUSE_PS2PP; + + if (psmouse_try_protocol(psmouse, PSMOUSE_TRACKPOINT, + &max_proto, set_properties, true)) + return PSMOUSE_TRACKPOINT; + + if (psmouse_try_protocol(psmouse, PSMOUSE_TOUCHKIT_PS2, + &max_proto, set_properties, true)) + return PSMOUSE_TOUCHKIT_PS2; + + if (psmouse_try_protocol(psmouse, PSMOUSE_BYD, + &max_proto, set_properties, true)) + return PSMOUSE_BYD; + } + + /* + * Try Finger Sensing Pad. We do it here because its probe upsets + * Trackpoint devices (causing TP_READ_ID command to time out). + */ + if (max_proto > PSMOUSE_IMEX && + psmouse_try_protocol(psmouse, PSMOUSE_FSP, + &max_proto, set_properties, true)) { + return PSMOUSE_FSP; + } + + /* + * Reset to defaults in case the device got confused by extended + * protocol probes. Note that we follow up with full reset because + * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS. + */ + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); + psmouse_reset(psmouse); + + if (max_proto >= PSMOUSE_IMEX && + psmouse_try_protocol(psmouse, PSMOUSE_IMEX, + &max_proto, set_properties, true)) { + return PSMOUSE_IMEX; + } + + if (max_proto >= PSMOUSE_IMPS && + psmouse_try_protocol(psmouse, PSMOUSE_IMPS, + &max_proto, set_properties, true)) { + return PSMOUSE_IMPS; + } + + /* + * Okay, all failed, we have a standard mouse here. The number of + * the buttons is still a question, though. We assume 3. + */ + psmouse_try_protocol(psmouse, PSMOUSE_PS2, + &max_proto, set_properties, true); + + if (synaptics_hardware) { + /* + * We detected Synaptics hardware but it did not respond to + * IMPS/2 probes. We need to reset the touchpad because if + * there is a track point on the pass through port it could + * get disabled while probing for protocol extensions. + */ + psmouse_reset(psmouse); + } + + return PSMOUSE_PS2; +} + +/* + * psmouse_probe() probes for a PS/2 mouse. + */ static int psmouse_probe(struct psmouse *psmouse) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[2]; -/* - * First, we check if it's a mouse. It should send 0x00 or 0x03 - * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer. - * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and subsequent - * ID queries, probably due to a firmware bug. - */ - + /* + * First, we check if it's a mouse. It should send 0x00 or 0x03 in + * case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer. + * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and + * subsequent ID queries, probably due to a firmware bug. + */ param[0] = 0xa5; if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) return -1; @@ -1191,10 +1193,10 @@ static int psmouse_probe(struct psmouse *psmouse) param[0] != 0x04 && param[0] != 0xff) return -1; -/* - * Then we reset and disable the mouse so that it doesn't generate events. - */ - + /* + * Then we reset and disable the mouse so that it doesn't generate + * events. + */ if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS)) psmouse_warn(psmouse, "Failed to reset mouse on %s\n", ps2dev->serio->phys); @@ -1205,13 +1207,11 @@ static int psmouse_probe(struct psmouse *psmouse) /* * psmouse_initialize() initializes the mouse to a sane state. */ - static void psmouse_initialize(struct psmouse *psmouse) { -/* - * We set the mouse report rate, resolution and scaling. - */ - + /* + * We set the mouse report rate, resolution and scaling. + */ if (psmouse_max_proto != PSMOUSE_PS2) { psmouse->set_rate(psmouse, psmouse->rate); psmouse->set_resolution(psmouse, psmouse->resolution); @@ -1222,7 +1222,6 @@ static void psmouse_initialize(struct psmouse *psmouse) /* * psmouse_activate() enables the mouse so that we get motion reports from it. */ - int psmouse_activate(struct psmouse *psmouse) { if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { @@ -1236,10 +1235,9 @@ int psmouse_activate(struct psmouse *psmouse) } /* - * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion - * reports from it unless we explicitly request it. + * psmouse_deactivate() puts the mouse into poll mode so that we don't get + * motion reports from it unless we explicitly request it. */ - int psmouse_deactivate(struct psmouse *psmouse) { if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) { @@ -1252,11 +1250,9 @@ int psmouse_deactivate(struct psmouse *psmouse) return 0; } - /* * psmouse_resync() attempts to re-validate current protocol. */ - static void psmouse_resync(struct work_struct *work) { struct psmouse *parent = NULL, *psmouse = @@ -1276,16 +1272,16 @@ static void psmouse_resync(struct work_struct *work) psmouse_deactivate(parent); } -/* - * Some mice don't ACK commands sent while they are in the middle of - * transmitting motion packet. To avoid delay we use ps2_sendbyte() - * instead of ps2_command() which would wait for 200ms for an ACK - * that may never come. - * As an additional quirk ALPS touchpads may not only forget to ACK - * disable command but will stop reporting taps, so if we see that - * mouse at least once ACKs disable we will do full reconnect if ACK - * is missing. - */ + /* + * Some mice don't ACK commands sent while they are in the middle of + * transmitting motion packet. To avoid delay we use ps2_sendbyte() + * instead of ps2_command() which would wait for 200ms for an ACK + * that may never come. + * As an additional quirk ALPS touchpads may not only forget to ACK + * disable command but will stop reporting taps, so if we see that + * mouse at least once ACKs disable we will do full reconnect if ACK + * is missing. + */ psmouse->num_resyncs++; if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) { @@ -1294,13 +1290,13 @@ static void psmouse_resync(struct work_struct *work) } else psmouse->acks_disable_command = true; -/* - * Poll the mouse. If it was reset the packet will be shorter than - * psmouse->pktsize and ps2_command will fail. We do not expect and - * do not handle scenario when mouse "upgrades" its protocol while - * disconnected since it would require additional delay. If we ever - * see a mouse that does it we'll adjust the code. - */ + /* + * Poll the mouse. If it was reset the packet will be shorter than + * psmouse->pktsize and ps2_command will fail. We do not expect and + * do not handle scenario when mouse "upgrades" its protocol while + * disconnected since it would require additional delay. If we ever + * see a mouse that does it we'll adjust the code. + */ if (!failed) { if (psmouse->poll(psmouse)) failed = true; @@ -1317,11 +1313,12 @@ static void psmouse_resync(struct work_struct *work) psmouse_set_state(psmouse, PSMOUSE_RESYNCING); } } -/* - * Now try to enable mouse. We try to do that even if poll failed and also - * repeat our attempts 5 times, otherwise we may be left out with disabled - * mouse. - */ + + /* + * Now try to enable mouse. We try to do that even if poll failed + * and also repeat our attempts 5 times, otherwise we may be left + * out with disabled mouse. + */ for (i = 0; i < 5; i++) { if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { enabled = true; @@ -1353,7 +1350,6 @@ static void psmouse_resync(struct work_struct *work) /* * psmouse_cleanup() resets the mouse into power-on state. */ - static void psmouse_cleanup(struct serio *serio) { struct psmouse *psmouse = serio_get_drvdata(serio); @@ -1378,15 +1374,15 @@ static void psmouse_cleanup(struct serio *serio) if (psmouse->cleanup) psmouse->cleanup(psmouse); -/* - * Reset the mouse to defaults (bare PS/2 protocol). - */ + /* + * Reset the mouse to defaults (bare PS/2 protocol). + */ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); -/* - * Some boxes, such as HP nx7400, get terribly confused if mouse - * is not fully enabled before suspending/shutting down. - */ + /* + * Some boxes, such as HP nx7400, get terribly confused if mouse + * is not fully enabled before suspending/shutting down. + */ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); if (parent) { @@ -1402,7 +1398,6 @@ static void psmouse_cleanup(struct serio *serio) /* * psmouse_disconnect() closes and frees. */ - static void psmouse_disconnect(struct serio *serio) { struct psmouse *psmouse, *parent = NULL; @@ -1602,7 +1597,6 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) goto out; } - static int psmouse_reconnect(struct serio *serio) { struct psmouse *psmouse = serio_get_drvdata(serio); diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index ad5a5a1ea872..e0ca6cda3d16 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -104,6 +104,7 @@ enum psmouse_type { PSMOUSE_CYPRESS, PSMOUSE_FOCALTECH, PSMOUSE_VMMOUSE, + PSMOUSE_BYD, PSMOUSE_AUTO /* This one should always be last */ }; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index ae33da7ab51f..c7afa57f6287 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -295,6 +295,16 @@ config TOUCHSCREEN_EGALAX To compile this driver as a module, choose M here: the module will be called egalax_ts. +config TOUCHSCREEN_EGALAX_SERIAL + tristate "EETI eGalax serial touchscreen" + select SERIO + help + Say Y here to enable support for serial connected EETI + eGalax touch panels. + + To compile this driver as a module, choose M here: the + module will be called egalax_ts_serial. + config TOUCHSCREEN_FT6236 tristate "FT6236 I2C touchscreen" depends on I2C @@ -324,6 +334,7 @@ config TOUCHSCREEN_FUJITSU config TOUCHSCREEN_GOODIX tristate "Goodix I2C touchscreen" depends on I2C + depends on GPIOLIB || COMPILE_TEST help Say Y here if you have the Goodix touchscreen (such as one installed in Onda v975w tablets) connected to your @@ -480,6 +491,17 @@ config TOUCHSCREEN_MMS114 To compile this driver as a module, choose M here: the module will be called mms114. +config TOUCHSCREEN_MELFAS_MIP4 + tristate "MELFAS MIP4 Touchscreen" + depends on I2C + help + Say Y here if you have a MELFAS MIP4 Touchscreen device. + + If unsure, say N. + + To compile this driver as a module, choose M here: + the module will be called melfas_mip4. + config TOUCHSCREEN_MTOUCH tristate "MicroTouch serial touchscreens" select SERIO @@ -927,6 +949,23 @@ config TOUCHSCREEN_TOUCHIT213 To compile this driver as a module, choose M here: the module will be called touchit213. +config TOUCHSCREEN_TS4800 + tristate "TS-4800 touchscreen" + depends on HAS_IOMEM && OF + depends on SOC_IMX51 || COMPILE_TEST + select MFD_SYSCON + select INPUT_POLLDEV + help + Say Y here if you have a touchscreen on a TS-4800 board. + + On TS-4800, the touchscreen is not handled directly by Linux but by + a companion FPGA. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ts4800_ts. + config TOUCHSCREEN_TSC_SERIO tristate "TSC-10/25/40 serial touchscreen support" select SERIO @@ -1085,7 +1124,8 @@ config TOUCHSCREEN_ZFORCE config TOUCHSCREEN_COLIBRI_VF50 tristate "Toradex Colibri on board touchscreen driver" - depends on GPIOLIB && IIO && VF610_ADC + depends on IIO && VF610_ADC + depends on GPIOLIB || COMPILE_TEST help Say Y here if you have a Colibri VF50 and plan to use the on-board provided 4-wire touchscreen driver. diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index cbaa6abb08da..4b518c76e0d7 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o +obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o @@ -47,6 +48,7 @@ obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o +obj-$(CONFIG_TOUCHSCREEN_MELFAS_MIP4) += melfas_mip4.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o obj-$(CONFIG_TOUCHSCREEN_MMS114) += mms114.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o @@ -68,6 +70,7 @@ obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o +obj-$(CONFIG_TOUCHSCREEN_TS4800) += ts4800-ts.o obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o obj-$(CONFIG_TOUCHSCREEN_TSC2004) += tsc2004.o diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c index d66962c5b1c2..58f72e0246ab 100644 --- a/drivers/input/touchscreen/ad7879-i2c.c +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -10,6 +10,7 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/types.h> +#include <linux/of.h> #include <linux/pm.h> #include "ad7879.h" @@ -91,10 +92,19 @@ static const struct i2c_device_id ad7879_id[] = { }; MODULE_DEVICE_TABLE(i2c, ad7879_id); +#ifdef CONFIG_OF +static const struct of_device_id ad7879_i2c_dt_ids[] = { + { .compatible = "adi,ad7879-1", }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7879_i2c_dt_ids); +#endif + static struct i2c_driver ad7879_i2c_driver = { .driver = { .name = "ad7879", .pm = &ad7879_pm_ops, + .of_match_table = of_match_ptr(ad7879_i2c_dt_ids), }, .probe = ad7879_i2c_probe, .remove = ad7879_i2c_remove, diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c index 48033c2689ab..d42b6b9af191 100644 --- a/drivers/input/touchscreen/ad7879-spi.c +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -10,6 +10,7 @@ #include <linux/pm.h> #include <linux/spi/spi.h> #include <linux/module.h> +#include <linux/of.h> #include "ad7879.h" @@ -146,10 +147,19 @@ static int ad7879_spi_remove(struct spi_device *spi) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id ad7879_spi_dt_ids[] = { + { .compatible = "adi,ad7879", }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7879_spi_dt_ids); +#endif + static struct spi_driver ad7879_spi_driver = { .driver = { .name = "ad7879", .pm = &ad7879_pm_ops, + .of_match_table = of_match_ptr(ad7879_spi_dt_ids), }, .probe = ad7879_spi_probe, .remove = ad7879_spi_remove, diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index fec66ad80513..cd013434473d 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -31,7 +31,8 @@ #include <linux/i2c.h> #include <linux/gpio.h> -#include <linux/spi/ad7879.h> +#include <linux/input/touchscreen.h> +#include <linux/platform_data/ad7879.h> #include <linux/module.h> #include "ad7879.h" @@ -94,8 +95,8 @@ #define AD7879_TEMP_BIT (1<<1) enum { - AD7879_SEQ_XPOS = 0, - AD7879_SEQ_YPOS = 1, + AD7879_SEQ_YPOS = 0, + AD7879_SEQ_XPOS = 1, AD7879_SEQ_Z1 = 2, AD7879_SEQ_Z2 = 3, AD7879_NR_SENSE = 4, @@ -126,7 +127,6 @@ struct ad7879 { u8 pen_down_acc_interval; u8 median; u16 x_plate_ohms; - u16 pressure_max; u16 cmd_crtl1; u16 cmd_crtl2; u16 cmd_crtl3; @@ -170,10 +170,10 @@ static int ad7879_report(struct ad7879 *ts) * filter. The combination of these two techniques provides a robust * solution, discarding the spurious noise in the signal and keeping * only the data of interest. The size of both filters is - * programmable. (dev.platform_data, see linux/spi/ad7879.h) Other - * user-programmable conversion controls include variable acquisition - * time, and first conversion delay. Up to 16 averages can be taken - * per conversion. + * programmable. (dev.platform_data, see linux/platform_data/ad7879.h) + * Other user-programmable conversion controls include variable + * acquisition time, and first conversion delay. Up to 16 averages can + * be taken per conversion. */ if (likely(x && z1)) { @@ -186,7 +186,7 @@ static int ad7879_report(struct ad7879 *ts) * Sample found inconsistent, pressure is beyond * the maximum. Don't report it to user space. */ - if (Rt > ts->pressure_max) + if (Rt > input_abs_get_max(input_dev, ABS_PRESSURE)) return -EINVAL; /* @@ -469,7 +469,7 @@ static void ad7879_gpio_remove(struct ad7879 *ts) { const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev); - if (pdata->gpio_export) + if (pdata && pdata->gpio_export) gpiochip_remove(&ts->gc); } @@ -485,6 +485,32 @@ static inline void ad7879_gpio_remove(struct ad7879 *ts) } #endif +static int ad7879_parse_dt(struct device *dev, struct ad7879 *ts) +{ + int err; + u32 tmp; + + err = device_property_read_u32(dev, "adi,resistance-plate-x", &tmp); + if (err) { + dev_err(dev, "failed to get resistance-plate-x property\n"); + return err; + } + ts->x_plate_ohms = (u16)tmp; + + device_property_read_u8(dev, "adi,first-conversion-delay", + &ts->first_conversion_delay); + device_property_read_u8(dev, "adi,acquisition-time", + &ts->acquisition_time); + device_property_read_u8(dev, "adi,median-filter-size", &ts->median); + device_property_read_u8(dev, "adi,averaging", &ts->averaging); + device_property_read_u8(dev, "adi,conversion-interval", + &ts->pen_down_acc_interval); + + ts->swap_xy = device_property_read_bool(dev, "touchscreen-swapped-x-y"); + + return 0; +} + struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, const struct ad7879_bus_ops *bops) { @@ -495,41 +521,44 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, u16 revid; if (!irq) { - dev_err(dev, "no IRQ?\n"); - err = -EINVAL; - goto err_out; + dev_err(dev, "No IRQ specified\n"); + return ERR_PTR(-EINVAL); } - if (!pdata) { - dev_err(dev, "no platform data?\n"); - err = -EINVAL; - goto err_out; + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return ERR_PTR(-ENOMEM); + + if (pdata) { + /* Platform data use swapped axis (backward compatibility) */ + ts->swap_xy = !pdata->swap_xy; + + ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; + + ts->first_conversion_delay = pdata->first_conversion_delay; + ts->acquisition_time = pdata->acquisition_time; + 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); } - ts = kzalloc(sizeof(*ts), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ts || !input_dev) { - err = -ENOMEM; - goto err_free_mem; + input_dev = devm_input_allocate_device(dev); + if (!input_dev) { + dev_err(dev, "Failed to allocate input device\n"); + return ERR_PTR(-ENOMEM); } ts->bops = bops; ts->dev = dev; ts->input = input_dev; ts->irq = irq; - ts->swap_xy = pdata->swap_xy; setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); - - ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; - ts->pressure_max = pdata->pressure_max ? : ~0; - - ts->first_conversion_delay = pdata->first_conversion_delay; - ts->acquisition_time = pdata->acquisition_time; - ts->averaging = pdata->averaging; - ts->pen_down_acc_interval = pdata->pen_down_acc_interval; - ts->median = pdata->median; - snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); input_dev->name = "AD7879 Touchscreen"; @@ -550,21 +579,33 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, __set_bit(EV_KEY, input_dev->evbit); __set_bit(BTN_TOUCH, input_dev->keybit); - input_set_abs_params(input_dev, ABS_X, - pdata->x_min ? : 0, - pdata->x_max ? : MAX_12BIT, - 0, 0); - input_set_abs_params(input_dev, ABS_Y, - pdata->y_min ? : 0, - pdata->y_max ? : MAX_12BIT, - 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, - pdata->pressure_min, pdata->pressure_max, 0, 0); + if (pdata) { + input_set_abs_params(input_dev, ABS_X, + pdata->x_min ? : 0, + pdata->x_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_Y, + pdata->y_min ? : 0, + pdata->y_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + pdata->pressure_min, + pdata->pressure_max ? : ~0, + 0, 0); + } 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); + touchscreen_parse_properties(input_dev, false); + if (!input_abs_get_max(input_dev, ABS_PRESSURE)) { + dev_err(dev, "Touchscreen pressure is not specified\n"); + return ERR_PTR(-EINVAL); + } + } err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET); if (err < 0) { dev_err(dev, "Failed to write %s\n", input_dev->name); - goto err_free_mem; + return ERR_PTR(err); } revid = ad7879_read(ts, AD7879_REG_REVID); @@ -573,8 +614,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, if (input_dev->id.product != devid) { dev_err(dev, "Failed to probe %s (%x vs %x)\n", input_dev->name, devid, revid); - err = -ENODEV; - goto err_free_mem; + return ERR_PTR(-ENODEV); } ts->cmd_crtl3 = AD7879_YPLUS_BIT | @@ -594,23 +634,25 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, AD7879_ACQ(ts->acquisition_time) | AD7879_TMR(ts->pen_down_acc_interval); - err = request_threaded_irq(ts->irq, NULL, ad7879_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(dev), ts); + err = devm_request_threaded_irq(dev, ts->irq, NULL, ad7879_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(dev), ts); if (err) { - dev_err(dev, "irq %d busy?\n", ts->irq); - goto err_free_mem; + dev_err(dev, "Failed to request IRQ: %d\n", err); + return ERR_PTR(err); } __ad7879_disable(ts); err = sysfs_create_group(&dev->kobj, &ad7879_attr_group); if (err) - goto err_free_irq; + goto err_out; - err = ad7879_gpio_add(ts, pdata); - if (err) - goto err_remove_attr; + if (pdata) { + err = ad7879_gpio_add(ts, pdata); + if (err) + goto err_remove_attr; + } err = input_register_device(input_dev); if (err) @@ -622,11 +664,6 @@ err_remove_gpio: ad7879_gpio_remove(ts); err_remove_attr: sysfs_remove_group(&dev->kobj, &ad7879_attr_group); -err_free_irq: - free_irq(ts->irq, ts); -err_free_mem: - input_free_device(input_dev); - kfree(ts); err_out: return ERR_PTR(err); } @@ -636,9 +673,6 @@ void ad7879_remove(struct ad7879 *ts) { ad7879_gpio_remove(ts); sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group); - free_irq(ts->irq, ts); - input_unregister_device(ts->input); - kfree(ts); } EXPORT_SYMBOL(ad7879_remove); diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 2d5794ec338b..2160512e861a 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -113,8 +113,8 @@ struct t7_config { #define MXT_T9_DETECT (1 << 7) struct t9_range { - u16 x; - u16 y; + __le16 x; + __le16 y; } __packed; /* MXT_TOUCH_MULTI_T9 orient */ @@ -216,6 +216,7 @@ struct mxt_data { unsigned int irq; unsigned int max_x; unsigned int max_y; + bool xy_switch; bool in_bootloader; u16 mem_size; u8 t100_aux_ampl; @@ -1665,8 +1666,8 @@ static int mxt_read_t9_resolution(struct mxt_data *data) if (error) return error; - le16_to_cpus(&range.x); - le16_to_cpus(&range.y); + data->max_x = get_unaligned_le16(&range.x); + data->max_y = get_unaligned_le16(&range.y); error = __mxt_read_reg(client, object->start_address + MXT_T9_ORIENT, @@ -1674,23 +1675,7 @@ static int mxt_read_t9_resolution(struct mxt_data *data) if (error) return error; - /* Handle default values */ - if (range.x == 0) - range.x = 1023; - - if (range.y == 0) - range.y = 1023; - - if (orient & MXT_T9_ORIENT_SWITCH) { - data->max_x = range.y; - data->max_y = range.x; - } else { - data->max_x = range.x; - data->max_y = range.y; - } - - dev_dbg(&client->dev, - "Touchscreen size X%uY%u\n", data->max_x, data->max_y); + data->xy_switch = orient & MXT_T9_ORIENT_SWITCH; return 0; } @@ -1708,13 +1693,14 @@ static int mxt_read_t100_config(struct mxt_data *data) if (!object) return -EINVAL; + /* read touchscreen dimensions */ error = __mxt_read_reg(client, object->start_address + MXT_T100_XRANGE, sizeof(range_x), &range_x); if (error) return error; - le16_to_cpus(&range_x); + data->max_x = get_unaligned_le16(&range_x); error = __mxt_read_reg(client, object->start_address + MXT_T100_YRANGE, @@ -1722,36 +1708,24 @@ static int mxt_read_t100_config(struct mxt_data *data) if (error) return error; - le16_to_cpus(&range_y); + data->max_y = get_unaligned_le16(&range_y); + /* read orientation config */ error = __mxt_read_reg(client, object->start_address + MXT_T100_CFG1, 1, &cfg); if (error) return error; + data->xy_switch = cfg & MXT_T100_CFG_SWITCHXY; + + /* allocate aux bytes */ error = __mxt_read_reg(client, object->start_address + MXT_T100_TCHAUX, 1, &tchaux); if (error) return error; - /* Handle default values */ - if (range_x == 0) - range_x = 1023; - - if (range_y == 0) - range_y = 1023; - - if (cfg & MXT_T100_CFG_SWITCHXY) { - data->max_x = range_y; - data->max_y = range_x; - } else { - data->max_x = range_x; - data->max_y = range_y; - } - - /* allocate aux bytes */ aux = 6; if (tchaux & MXT_T100_TCHAUX_VECT) @@ -1767,9 +1741,6 @@ static int mxt_read_t100_config(struct mxt_data *data) "T100 aux mappings vect:%u ampl:%u area:%u\n", data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area); - dev_info(&client->dev, - "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y); - return 0; } @@ -1828,6 +1799,19 @@ static int mxt_initialize_input_device(struct mxt_data *data) return -EINVAL; } + /* Handle default values and orientation switch */ + if (data->max_x == 0) + data->max_x = 1023; + + if (data->max_y == 0) + data->max_y = 1023; + + if (data->xy_switch) + swap(data->max_x, data->max_y); + + dev_info(dev, "Touchscreen size X%uY%u\n", data->max_x, data->max_y); + + /* Register input device */ input_dev = input_allocate_device(); if (!input_dev) { dev_err(dev, "Failed to allocate memory\n"); diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 5b74e8b84e79..91cda8f8119d 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -30,9 +30,12 @@ #include <linux/delay.h> #include <linux/input.h> #include <linux/input/mt.h> +#include <linux/input/touchscreen.h> #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/slab.h> +#include <linux/property.h> +#include <linux/gpio/consumer.h> #include "cyttsp_core.h" @@ -57,6 +60,7 @@ #define CY_DELAY_DFLT 20 /* ms */ #define CY_DELAY_MAX 500 #define CY_ACT_DIST_DFLT 0xF8 +#define CY_ACT_DIST_MASK 0x0F #define CY_HNDSHK_BIT 0x80 /* device mode bits */ #define CY_OPERATE_MODE 0x00 @@ -120,7 +124,7 @@ static int ttsp_send_command(struct cyttsp *ts, u8 cmd) static int cyttsp_handshake(struct cyttsp *ts) { - if (ts->pdata->use_hndshk) + if (ts->use_hndshk) return ttsp_send_command(ts, ts->xy_data.hst_mode ^ CY_HNDSHK_BIT); @@ -142,9 +146,9 @@ static int cyttsp_exit_bl_mode(struct cyttsp *ts) u8 bl_cmd[sizeof(bl_command)]; memcpy(bl_cmd, bl_command, sizeof(bl_command)); - if (ts->pdata->bl_keys) + if (ts->bl_keys) memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS], - ts->pdata->bl_keys, CY_NUM_BL_KEYS); + ts->bl_keys, CY_NUM_BL_KEYS); error = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(bl_cmd), bl_cmd); @@ -217,14 +221,14 @@ static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) { int retval = 0; - if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT || - ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT || - ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) { + if (ts->act_intrvl != CY_ACT_INTRVL_DFLT || + ts->tch_tmout != CY_TCH_TMOUT_DFLT || + ts->lp_intrvl != CY_LP_INTRVL_DFLT) { u8 intrvl_ray[] = { - ts->pdata->act_intrvl, - ts->pdata->tch_tmout, - ts->pdata->lp_intrvl + ts->act_intrvl, + ts->tch_tmout, + ts->lp_intrvl }; /* set intrvl registers */ @@ -236,6 +240,16 @@ static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) return retval; } +static void cyttsp_hard_reset(struct cyttsp *ts) +{ + if (ts->reset_gpio) { + gpiod_set_value_cansleep(ts->reset_gpio, 1); + msleep(CY_DELAY_DFLT); + gpiod_set_value_cansleep(ts->reset_gpio, 0); + msleep(CY_DELAY_DFLT); + } +} + static int cyttsp_soft_reset(struct cyttsp *ts) { unsigned long timeout; @@ -263,7 +277,7 @@ out: static int cyttsp_act_dist_setup(struct cyttsp *ts) { - u8 act_dist_setup = ts->pdata->act_dist; + u8 act_dist_setup = ts->act_dist; /* Init gesture; active distance setup */ return ttsp_write_block_data(ts, CY_REG_ACT_DIST, @@ -528,45 +542,110 @@ static void cyttsp_close(struct input_dev *dev) cyttsp_disable(ts); } +static int cyttsp_parse_properties(struct cyttsp *ts) +{ + struct device *dev = ts->dev; + u32 dt_value; + int ret; + + ts->bl_keys = devm_kzalloc(dev, CY_NUM_BL_KEYS, GFP_KERNEL); + if (!ts->bl_keys) + return -ENOMEM; + + /* Set some default values */ + ts->use_hndshk = false; + ts->act_dist = CY_ACT_DIST_DFLT; + ts->act_intrvl = CY_ACT_INTRVL_DFLT; + ts->tch_tmout = CY_TCH_TMOUT_DFLT; + ts->lp_intrvl = CY_LP_INTRVL_DFLT; + + ret = device_property_read_u8_array(dev, "bootloader-key", + ts->bl_keys, CY_NUM_BL_KEYS); + if (ret) { + dev_err(dev, + "bootloader-key property could not be retrieved\n"); + return ret; + } + + ts->use_hndshk = device_property_present(dev, "use-handshake"); + + if (!device_property_read_u32(dev, "active-distance", &dt_value)) { + if (dt_value > 15) { + dev_err(dev, "active-distance (%u) must be [0-15]\n", + dt_value); + return -EINVAL; + } + ts->act_dist &= ~CY_ACT_DIST_MASK; + ts->act_dist |= dt_value; + } + + if (!device_property_read_u32(dev, "active-interval-ms", &dt_value)) { + if (dt_value > 255) { + dev_err(dev, "active-interval-ms (%u) must be [0-255]\n", + dt_value); + return -EINVAL; + } + ts->act_intrvl = dt_value; + } + + if (!device_property_read_u32(dev, "lowpower-interval-ms", &dt_value)) { + if (dt_value > 2550) { + dev_err(dev, "lowpower-interval-ms (%u) must be [0-2550]\n", + dt_value); + return -EINVAL; + } + /* Register value is expressed in 0.01s / bit */ + ts->lp_intrvl = dt_value / 10; + } + + if (!device_property_read_u32(dev, "touch-timeout-ms", &dt_value)) { + if (dt_value > 2550) { + dev_err(dev, "touch-timeout-ms (%u) must be [0-2550]\n", + dt_value); + return -EINVAL; + } + /* Register value is expressed in 0.01s / bit */ + ts->tch_tmout = dt_value / 10; + } + + return 0; +} + struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, struct device *dev, int irq, size_t xfer_buf_size) { - const struct cyttsp_platform_data *pdata = dev_get_platdata(dev); struct cyttsp *ts; struct input_dev *input_dev; int error; - if (!pdata || !pdata->name || irq <= 0) { - error = -EINVAL; - goto err_out; - } + ts = devm_kzalloc(dev, sizeof(*ts) + xfer_buf_size, GFP_KERNEL); + if (!ts) + return ERR_PTR(-ENOMEM); - ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ts || !input_dev) { - error = -ENOMEM; - goto err_free_mem; - } + input_dev = devm_input_allocate_device(dev); + if (!input_dev) + return ERR_PTR(-ENOMEM); ts->dev = dev; ts->input = input_dev; - ts->pdata = dev_get_platdata(dev); ts->bus_ops = bus_ops; ts->irq = irq; + ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ts->reset_gpio)) { + error = PTR_ERR(ts->reset_gpio); + dev_err(dev, "Failed to request reset gpio, error %d\n", error); + return ERR_PTR(error); + } + + error = cyttsp_parse_properties(ts); + if (error) + return ERR_PTR(error); + init_completion(&ts->bl_ready); snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); - if (pdata->init) { - error = pdata->init(); - if (error) { - dev_err(ts->dev, "platform init failed, err: %d\n", - error); - goto err_free_mem; - } - } - - input_dev->name = pdata->name; + input_dev->name = "Cypress TTSP TouchScreen"; input_dev->phys = ts->phys; input_dev->id.bustype = bus_ops->bustype; input_dev->dev.parent = ts->dev; @@ -576,63 +655,44 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, input_set_drvdata(input_dev, ts); - __set_bit(EV_ABS, input_dev->evbit); - input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, pdata->maxx, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, pdata->maxy, 0, 0); - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, CY_MAXZ, 0, 0); + input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X); + input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y); + touchscreen_parse_properties(input_dev, true); - input_mt_init_slots(input_dev, CY_MAX_ID, 0); + error = input_mt_init_slots(input_dev, CY_MAX_ID, 0); + if (error) { + dev_err(dev, "Unable to init MT slots.\n"); + return ERR_PTR(error); + } - error = request_threaded_irq(ts->irq, NULL, cyttsp_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - pdata->name, ts); + error = devm_request_threaded_irq(dev, ts->irq, NULL, cyttsp_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "cyttsp", ts); if (error) { dev_err(ts->dev, "failed to request IRQ %d, err: %d\n", ts->irq, error); - goto err_platform_exit; + return ERR_PTR(error); } disable_irq(ts->irq); + cyttsp_hard_reset(ts); + error = cyttsp_power_on(ts); if (error) - goto err_free_irq; + return ERR_PTR(error); error = input_register_device(input_dev); if (error) { dev_err(ts->dev, "failed to register input device: %d\n", error); - goto err_free_irq; + return ERR_PTR(error); } return ts; - -err_free_irq: - free_irq(ts->irq, ts); -err_platform_exit: - if (pdata->exit) - pdata->exit(); -err_free_mem: - input_free_device(input_dev); - kfree(ts); -err_out: - return ERR_PTR(error); } EXPORT_SYMBOL_GPL(cyttsp_probe); -void cyttsp_remove(struct cyttsp *ts) -{ - free_irq(ts->irq, ts); - input_unregister_device(ts->input); - if (ts->pdata->exit) - ts->pdata->exit(); - kfree(ts); -} -EXPORT_SYMBOL_GPL(cyttsp_remove); - MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core"); MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h index 07074110a902..7835e2bacf5a 100644 --- a/drivers/input/touchscreen/cyttsp_core.h +++ b/drivers/input/touchscreen/cyttsp_core.h @@ -129,7 +129,6 @@ struct cyttsp { int irq; struct input_dev *input; char phys[32]; - const struct cyttsp_platform_data *pdata; const struct cyttsp_bus_ops *bus_ops; struct cyttsp_bootloader_data bl_data; struct cyttsp_sysinfo_data sysinfo_data; @@ -138,12 +137,19 @@ struct cyttsp { enum cyttsp_state state; bool suspended; + struct gpio_desc *reset_gpio; + bool use_hndshk; + u8 act_dist; + u8 act_intrvl; + u8 tch_tmout; + u8 lp_intrvl; + u8 *bl_keys; + u8 xfer_buf[] ____cacheline_aligned; }; struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, struct device *dev, int irq, size_t xfer_buf_size); -void cyttsp_remove(struct cyttsp *ts); int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr, u8 length, const void *values); diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c index eee51b3f2e3f..1edfdba96ede 100644 --- a/drivers/input/touchscreen/cyttsp_i2c.c +++ b/drivers/input/touchscreen/cyttsp_i2c.c @@ -56,15 +56,6 @@ static int cyttsp_i2c_probe(struct i2c_client *client, return 0; } -static int cyttsp_i2c_remove(struct i2c_client *client) -{ - struct cyttsp *ts = i2c_get_clientdata(client); - - cyttsp_remove(ts); - - return 0; -} - static const struct i2c_device_id cyttsp_i2c_id[] = { { CY_I2C_NAME, 0 }, { } @@ -77,7 +68,6 @@ static struct i2c_driver cyttsp_i2c_driver = { .pm = &cyttsp_pm_ops, }, .probe = cyttsp_i2c_probe, - .remove = cyttsp_i2c_remove, .id_table = cyttsp_i2c_id, }; diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c index bbeeb2488b57..3c9d18b1b6ef 100644 --- a/drivers/input/touchscreen/cyttsp_spi.c +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -170,22 +170,12 @@ static int cyttsp_spi_probe(struct spi_device *spi) return 0; } -static int cyttsp_spi_remove(struct spi_device *spi) -{ - struct cyttsp *ts = spi_get_drvdata(spi); - - cyttsp_remove(ts); - - return 0; -} - static struct spi_driver cyttsp_spi_driver = { .driver = { .name = CY_SPI_NAME, .pm = &cyttsp_pm_ops, }, .probe = cyttsp_spi_probe, - .remove = cyttsp_spi_remove, }; module_spi_driver(cyttsp_spi_driver); diff --git a/drivers/input/touchscreen/egalax_ts_serial.c b/drivers/input/touchscreen/egalax_ts_serial.c new file mode 100644 index 000000000000..657bbae608c8 --- /dev/null +++ b/drivers/input/touchscreen/egalax_ts_serial.c @@ -0,0 +1,194 @@ +/* + * EETI Egalax serial touchscreen driver + * + * Copyright (c) 2015 Zoltán Böszörményi <zboszor@pr.hu> + * + * based on the + * + * Hampshire serial touchscreen driver (Copyright (c) 2010 Adam Bennett) + */ + +/* + * 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/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/serio.h> + +#define DRIVER_DESC "EETI Egalax serial touchscreen driver" + +/* + * Definitions & global arrays. + */ + +#define EGALAX_FORMAT_MAX_LENGTH 6 +#define EGALAX_FORMAT_START_BIT BIT(7) +#define EGALAX_FORMAT_PRESSURE_BIT BIT(6) +#define EGALAX_FORMAT_TOUCH_BIT BIT(0) +#define EGALAX_FORMAT_RESOLUTION_MASK 0x06 + +#define EGALAX_MIN_XC 0 +#define EGALAX_MAX_XC 0x4000 +#define EGALAX_MIN_YC 0 +#define EGALAX_MAX_YC 0x4000 + +/* + * Per-touchscreen data. + */ +struct egalax { + struct input_dev *input; + struct serio *serio; + int idx; + u8 data[EGALAX_FORMAT_MAX_LENGTH]; + char phys[32]; +}; + +static void egalax_process_data(struct egalax *egalax) +{ + struct input_dev *dev = egalax->input; + u8 *data = egalax->data; + u16 x, y; + u8 shift; + u8 mask; + + shift = 3 - ((data[0] & EGALAX_FORMAT_RESOLUTION_MASK) >> 1); + mask = 0xff >> (shift + 1); + + x = (((u16)(data[1] & mask) << 7) | (data[2] & 0x7f)) << shift; + y = (((u16)(data[3] & mask) << 7) | (data[4] & 0x7f)) << shift; + + input_report_key(dev, BTN_TOUCH, data[0] & EGALAX_FORMAT_TOUCH_BIT); + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_sync(dev); +} + +static irqreturn_t egalax_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct egalax *egalax = serio_get_drvdata(serio); + int pkt_len; + + egalax->data[egalax->idx++] = data; + + if (likely(egalax->data[0] & EGALAX_FORMAT_START_BIT)) { + pkt_len = egalax->data[0] & EGALAX_FORMAT_PRESSURE_BIT ? 6 : 5; + if (pkt_len == egalax->idx) { + egalax_process_data(egalax); + egalax->idx = 0; + } + } else { + dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n", + egalax->data[0]); + egalax->idx = 0; + } + + return IRQ_HANDLED; +} + +/* + * egalax_connect() is the routine that is called when someone adds a + * new serio device that supports egalax protocol and registers it as + * an input device. This is usually accomplished using inputattach. + */ +static int egalax_connect(struct serio *serio, struct serio_driver *drv) +{ + struct egalax *egalax; + struct input_dev *input_dev; + int error; + + egalax = kzalloc(sizeof(struct egalax), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!egalax || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + egalax->serio = serio; + egalax->input = input_dev; + snprintf(egalax->phys, sizeof(egalax->phys), + "%s/input0", serio->phys); + + input_dev->name = "EETI eGalaxTouch Serial TouchScreen"; + input_dev->phys = egalax->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_EGALAX; + input_dev->id.product = 0; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &serio->dev; + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, + EGALAX_MIN_XC, EGALAX_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + EGALAX_MIN_YC, EGALAX_MAX_YC, 0, 0); + + serio_set_drvdata(serio, egalax); + + error = serio_open(serio, drv); + if (error) + goto err_reset_drvdata; + + error = input_register_device(input_dev); + if (error) + goto err_close_serio; + + return 0; + +err_close_serio: + serio_close(serio); +err_reset_drvdata: + serio_set_drvdata(serio, NULL); +err_free_mem: + input_free_device(input_dev); + kfree(egalax); + return error; +} + +static void egalax_disconnect(struct serio *serio) +{ + struct egalax *egalax = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(egalax->input); + kfree(egalax); +} + +/* + * The serio driver structure. + */ + +static const struct serio_device_id egalax_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_EGALAX, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, egalax_serio_ids); + +static struct serio_driver egalax_drv = { + .driver = { + .name = "egalax", + }, + .description = DRIVER_DESC, + .id_table = egalax_serio_ids, + .interrupt = egalax_interrupt, + .connect = egalax_connect, + .disconnect = egalax_disconnect, +}; +module_serio_driver(egalax_drv); + +MODULE_AUTHOR("Zoltán Böszörményi <zboszor@pr.hu>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 4d113c9e4b77..240b16f3ee97 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -2,6 +2,7 @@ * Driver for Goodix Touchscreens * * Copyright (c) 2014 Red Hat Inc. + * Copyright (c) 2015 K. Merker <merker@debian.org> * * This code is based on gt9xx.c authored by andrew@goodix.com: * @@ -16,6 +17,8 @@ #include <linux/kernel.h> #include <linux/dmi.h> +#include <linux/firmware.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/input/mt.h> @@ -33,11 +36,24 @@ struct goodix_ts_data { struct input_dev *input_dev; int abs_x_max; int abs_y_max; + bool swapped_x_y; + bool inverted_x; + bool inverted_y; unsigned int max_touch_num; unsigned int int_trigger_type; - bool rotated_screen; + int cfg_len; + struct gpio_desc *gpiod_int; + struct gpio_desc *gpiod_rst; + u16 id; + u16 version; + const char *cfg_name; + struct completion firmware_loading_complete; + unsigned long irq_flags; }; +#define GOODIX_GPIO_INT_NAME "irq" +#define GOODIX_GPIO_RST_NAME "reset" + #define GOODIX_MAX_HEIGHT 4096 #define GOODIX_MAX_WIDTH 4096 #define GOODIX_INT_TRIGGER 1 @@ -45,8 +61,13 @@ struct goodix_ts_data { #define GOODIX_MAX_CONTACTS 10 #define GOODIX_CONFIG_MAX_LENGTH 240 +#define GOODIX_CONFIG_911_LENGTH 186 +#define GOODIX_CONFIG_967_LENGTH 228 /* Register defines */ +#define GOODIX_REG_COMMAND 0x8040 +#define GOODIX_CMD_SCREEN_OFF 0x05 + #define GOODIX_READ_COOR_ADDR 0x814E #define GOODIX_REG_CONFIG_DATA 0x8047 #define GOODIX_REG_ID 0x8140 @@ -115,6 +136,63 @@ static int goodix_i2c_read(struct i2c_client *client, return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0); } +/** + * goodix_i2c_write - write data to a register of the i2c slave device. + * + * @client: i2c device. + * @reg: the register to write to. + * @buf: raw data buffer to write. + * @len: length of the buffer to write + */ +static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, + unsigned len) +{ + u8 *addr_buf; + struct i2c_msg msg; + int ret; + + addr_buf = kmalloc(len + 2, GFP_KERNEL); + if (!addr_buf) + return -ENOMEM; + + addr_buf[0] = reg >> 8; + addr_buf[1] = reg & 0xFF; + memcpy(&addr_buf[2], buf, len); + + msg.flags = 0; + msg.addr = client->addr; + msg.buf = addr_buf; + msg.len = len + 2; + + ret = i2c_transfer(client->adapter, &msg, 1); + kfree(addr_buf); + return ret < 0 ? ret : (ret != 1 ? -EIO : 0); +} + +static int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value) +{ + return goodix_i2c_write(client, reg, &value, sizeof(value)); +} + +static int goodix_get_cfg_len(u16 id) +{ + switch (id) { + case 911: + case 9271: + case 9110: + case 927: + case 928: + return GOODIX_CONFIG_911_LENGTH; + + case 912: + case 967: + return GOODIX_CONFIG_967_LENGTH; + + default: + return GOODIX_CONFIG_MAX_LENGTH; + } +} + static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) { int touch_num; @@ -155,10 +233,13 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) int input_y = get_unaligned_le16(&coor_data[3]); int input_w = get_unaligned_le16(&coor_data[5]); - if (ts->rotated_screen) { + /* Inversions have to happen before axis swapping */ + if (ts->inverted_x) input_x = ts->abs_x_max - input_x; + if (ts->inverted_y) input_y = ts->abs_y_max - input_y; - } + if (ts->swapped_x_y) + swap(input_x, input_y); input_mt_slot(ts->input_dev, id); input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); @@ -202,21 +283,195 @@ static void goodix_process_events(struct goodix_ts_data *ts) */ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) { - static const u8 end_cmd[] = { - GOODIX_READ_COOR_ADDR >> 8, - GOODIX_READ_COOR_ADDR & 0xff, - 0 - }; struct goodix_ts_data *ts = dev_id; goodix_process_events(ts); - if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0) + if (goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0) < 0) dev_err(&ts->client->dev, "I2C write end_cmd error\n"); return IRQ_HANDLED; } +static void goodix_free_irq(struct goodix_ts_data *ts) +{ + devm_free_irq(&ts->client->dev, ts->client->irq, ts); +} + +static int goodix_request_irq(struct goodix_ts_data *ts) +{ + return devm_request_threaded_irq(&ts->client->dev, ts->client->irq, + NULL, goodix_ts_irq_handler, + ts->irq_flags, ts->client->name, ts); +} + +/** + * goodix_check_cfg - Checks if config fw is valid + * + * @ts: goodix_ts_data pointer + * @cfg: firmware config data + */ +static int goodix_check_cfg(struct goodix_ts_data *ts, + const struct firmware *cfg) +{ + int i, raw_cfg_len; + u8 check_sum = 0; + + if (cfg->size > GOODIX_CONFIG_MAX_LENGTH) { + dev_err(&ts->client->dev, + "The length of the config fw is not correct"); + return -EINVAL; + } + + raw_cfg_len = cfg->size - 2; + for (i = 0; i < raw_cfg_len; i++) + check_sum += cfg->data[i]; + check_sum = (~check_sum) + 1; + if (check_sum != cfg->data[raw_cfg_len]) { + dev_err(&ts->client->dev, + "The checksum of the config fw is not correct"); + return -EINVAL; + } + + if (cfg->data[raw_cfg_len + 1] != 1) { + dev_err(&ts->client->dev, + "Config fw must have Config_Fresh register set"); + return -EINVAL; + } + + return 0; +} + +/** + * goodix_send_cfg - Write fw config to device + * + * @ts: goodix_ts_data pointer + * @cfg: config firmware to write to device + */ +static int goodix_send_cfg(struct goodix_ts_data *ts, + const struct firmware *cfg) +{ + int error; + + error = goodix_check_cfg(ts, cfg); + if (error) + return error; + + error = goodix_i2c_write(ts->client, GOODIX_REG_CONFIG_DATA, cfg->data, + cfg->size); + if (error) { + dev_err(&ts->client->dev, "Failed to write config data: %d", + error); + return error; + } + dev_dbg(&ts->client->dev, "Config sent successfully."); + + /* Let the firmware reconfigure itself, so sleep for 10ms */ + usleep_range(10000, 11000); + + return 0; +} + +static int goodix_int_sync(struct goodix_ts_data *ts) +{ + int error; + + error = gpiod_direction_output(ts->gpiod_int, 0); + if (error) + return error; + + msleep(50); /* T5: 50ms */ + + error = gpiod_direction_input(ts->gpiod_int); + if (error) + return error; + + return 0; +} + +/** + * goodix_reset - Reset device during power on + * + * @ts: goodix_ts_data pointer + */ +static int goodix_reset(struct goodix_ts_data *ts) +{ + int error; + + /* begin select I2C slave addr */ + error = gpiod_direction_output(ts->gpiod_rst, 0); + if (error) + return error; + + msleep(20); /* T2: > 10ms */ + + /* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */ + error = gpiod_direction_output(ts->gpiod_int, ts->client->addr == 0x14); + if (error) + return error; + + usleep_range(100, 2000); /* T3: > 100us */ + + error = gpiod_direction_output(ts->gpiod_rst, 1); + if (error) + return error; + + usleep_range(6000, 10000); /* T4: > 5ms */ + + /* end select I2C slave addr */ + error = gpiod_direction_input(ts->gpiod_rst); + if (error) + return error; + + error = goodix_int_sync(ts); + if (error) + return error; + + return 0; +} + +/** + * goodix_get_gpio_config - Get GPIO config from ACPI/DT + * + * @ts: goodix_ts_data pointer + */ +static int goodix_get_gpio_config(struct goodix_ts_data *ts) +{ + int error; + struct device *dev; + struct gpio_desc *gpiod; + + if (!ts->client) + return -EINVAL; + dev = &ts->client->dev; + + /* Get the interrupt GPIO pin number */ + gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN); + if (IS_ERR(gpiod)) { + error = PTR_ERR(gpiod); + if (error != -EPROBE_DEFER) + dev_dbg(dev, "Failed to get %s GPIO: %d\n", + GOODIX_GPIO_INT_NAME, error); + return error; + } + + ts->gpiod_int = gpiod; + + /* Get the reset line GPIO pin number */ + gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_IN); + if (IS_ERR(gpiod)) { + error = PTR_ERR(gpiod); + if (error != -EPROBE_DEFER) + dev_dbg(dev, "Failed to get %s GPIO: %d\n", + GOODIX_GPIO_RST_NAME, error); + return error; + } + + ts->gpiod_rst = gpiod; + + return 0; +} + /** * goodix_read_config - Read the embedded configuration of the panel * @@ -230,14 +485,15 @@ static void goodix_read_config(struct goodix_ts_data *ts) int error; error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA, - config, - GOODIX_CONFIG_MAX_LENGTH); + config, ts->cfg_len); if (error) { dev_warn(&ts->client->dev, "Error reading config (%d), using defaults\n", error); ts->abs_x_max = GOODIX_MAX_WIDTH; ts->abs_y_max = GOODIX_MAX_HEIGHT; + if (ts->swapped_x_y) + swap(ts->abs_x_max, ts->abs_y_max); ts->int_trigger_type = GOODIX_INT_TRIGGER; ts->max_touch_num = GOODIX_MAX_CONTACTS; return; @@ -245,6 +501,8 @@ static void goodix_read_config(struct goodix_ts_data *ts) ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]); ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); + if (ts->swapped_x_y) + swap(ts->abs_x_max, ts->abs_y_max); ts->int_trigger_type = config[TRIGGER_LOC] & 0x03; ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f; if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) { @@ -252,42 +510,45 @@ static void goodix_read_config(struct goodix_ts_data *ts) "Invalid config, using defaults\n"); ts->abs_x_max = GOODIX_MAX_WIDTH; ts->abs_y_max = GOODIX_MAX_HEIGHT; + if (ts->swapped_x_y) + swap(ts->abs_x_max, ts->abs_y_max); ts->max_touch_num = GOODIX_MAX_CONTACTS; } - ts->rotated_screen = dmi_check_system(rotated_screen); - if (ts->rotated_screen) + if (dmi_check_system(rotated_screen)) { + ts->inverted_x = true; + ts->inverted_y = true; dev_dbg(&ts->client->dev, "Applying '180 degrees rotated screen' quirk\n"); + } } /** * goodix_read_version - Read goodix touchscreen version * - * @client: the i2c client - * @version: output buffer containing the version on success - * @id: output buffer containing the id on success + * @ts: our goodix_ts_data pointer */ -static int goodix_read_version(struct i2c_client *client, u16 *version, u16 *id) +static int goodix_read_version(struct goodix_ts_data *ts) { int error; u8 buf[6]; char id_str[5]; - error = goodix_i2c_read(client, GOODIX_REG_ID, buf, sizeof(buf)); + error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf)); if (error) { - dev_err(&client->dev, "read version failed: %d\n", error); + dev_err(&ts->client->dev, "read version failed: %d\n", error); return error; } memcpy(id_str, buf, 4); id_str[4] = 0; - if (kstrtou16(id_str, 10, id)) - *id = 0x1001; + if (kstrtou16(id_str, 10, &ts->id)) + ts->id = 0x1001; - *version = get_unaligned_le16(&buf[4]); + ts->version = get_unaligned_le16(&buf[4]); - dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version); + dev_info(&ts->client->dev, "ID %d, version: %04x\n", ts->id, + ts->version); return 0; } @@ -321,13 +582,10 @@ static int goodix_i2c_test(struct i2c_client *client) * goodix_request_input_dev - Allocate, populate and register the input device * * @ts: our goodix_ts_data pointer - * @version: device firmware version - * @id: device ID * * Must be called during probe */ -static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version, - u16 id) +static int goodix_request_input_dev(struct goodix_ts_data *ts) { int error; @@ -351,8 +609,8 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version, ts->input_dev->phys = "input/ts"; ts->input_dev->id.bustype = BUS_I2C; ts->input_dev->id.vendor = 0x0416; - ts->input_dev->id.product = id; - ts->input_dev->id.version = version; + ts->input_dev->id.product = ts->id; + ts->input_dev->id.version = ts->version; error = input_register_device(ts->input_dev); if (error) { @@ -364,13 +622,75 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version, return 0; } +/** + * goodix_configure_dev - Finish device initialization + * + * @ts: our goodix_ts_data pointer + * + * Must be called from probe to finish initialization of the device. + * Contains the common initialization code for both devices that + * declare gpio pins and devices that do not. It is either called + * directly from probe or from request_firmware_wait callback. + */ +static int goodix_configure_dev(struct goodix_ts_data *ts) +{ + int error; + + ts->swapped_x_y = device_property_read_bool(&ts->client->dev, + "touchscreen-swapped-x-y"); + ts->inverted_x = device_property_read_bool(&ts->client->dev, + "touchscreen-inverted-x"); + ts->inverted_y = device_property_read_bool(&ts->client->dev, + "touchscreen-inverted-y"); + + goodix_read_config(ts); + + error = goodix_request_input_dev(ts); + if (error) + return error; + + ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; + error = goodix_request_irq(ts); + if (error) { + dev_err(&ts->client->dev, "request IRQ failed: %d\n", error); + return error; + } + + return 0; +} + +/** + * goodix_config_cb - Callback to finish device init + * + * @ts: our goodix_ts_data pointer + * + * request_firmware_wait callback that finishes + * initialization of the device. + */ +static void goodix_config_cb(const struct firmware *cfg, void *ctx) +{ + struct goodix_ts_data *ts = ctx; + int error; + + if (cfg) { + /* send device configuration to the firmware */ + error = goodix_send_cfg(ts, cfg); + if (error) + goto err_release_cfg; + } + + goodix_configure_dev(ts); + +err_release_cfg: + release_firmware(cfg); + complete_all(&ts->firmware_loading_complete); +} + static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct goodix_ts_data *ts; - unsigned long irq_flags; int error; - u16 version_info, id_info; dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr); @@ -385,6 +705,20 @@ static int goodix_ts_probe(struct i2c_client *client, ts->client = client; i2c_set_clientdata(client, ts); + init_completion(&ts->firmware_loading_complete); + + error = goodix_get_gpio_config(ts); + if (error) + return error; + + if (ts->gpiod_int && ts->gpiod_rst) { + /* reset the controller */ + error = goodix_reset(ts); + if (error) { + dev_err(&client->dev, "Controller reset failed.\n"); + return error; + } + } error = goodix_i2c_test(client); if (error) { @@ -392,30 +726,125 @@ static int goodix_ts_probe(struct i2c_client *client, return error; } - error = goodix_read_version(client, &version_info, &id_info); + error = goodix_read_version(ts); if (error) { dev_err(&client->dev, "Read version failed.\n"); return error; } - goodix_read_config(ts); + ts->cfg_len = goodix_get_cfg_len(ts->id); + + if (ts->gpiod_int && ts->gpiod_rst) { + /* update device config */ + ts->cfg_name = devm_kasprintf(&client->dev, GFP_KERNEL, + "goodix_%d_cfg.bin", ts->id); + if (!ts->cfg_name) + return -ENOMEM; + + error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name, + &client->dev, GFP_KERNEL, ts, + goodix_config_cb); + if (error) { + dev_err(&client->dev, + "Failed to invoke firmware loader: %d\n", + error); + return error; + } - error = goodix_request_input_dev(ts, version_info, id_info); - if (error) - return error; + return 0; + } else { + error = goodix_configure_dev(ts); + if (error) + return error; + } + + return 0; +} + +static int goodix_ts_remove(struct i2c_client *client) +{ + struct goodix_ts_data *ts = i2c_get_clientdata(client); + + if (ts->gpiod_int && ts->gpiod_rst) + wait_for_completion(&ts->firmware_loading_complete); + + return 0; +} + +static int __maybe_unused goodix_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct goodix_ts_data *ts = i2c_get_clientdata(client); + int error; - irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; - error = devm_request_threaded_irq(&ts->client->dev, client->irq, - NULL, goodix_ts_irq_handler, - irq_flags, client->name, ts); + /* We need gpio pins to suspend/resume */ + if (!ts->gpiod_int || !ts->gpiod_rst) + return 0; + + wait_for_completion(&ts->firmware_loading_complete); + + /* Free IRQ as IRQ pin is used as output in the suspend sequence */ + goodix_free_irq(ts); + + /* Output LOW on the INT pin for 5 ms */ + error = gpiod_direction_output(ts->gpiod_int, 0); if (error) { - dev_err(&client->dev, "request IRQ failed: %d\n", error); + goodix_request_irq(ts); return error; } + usleep_range(5000, 6000); + + error = goodix_i2c_write_u8(ts->client, GOODIX_REG_COMMAND, + GOODIX_CMD_SCREEN_OFF); + if (error) { + dev_err(&ts->client->dev, "Screen off command failed\n"); + gpiod_direction_input(ts->gpiod_int); + goodix_request_irq(ts); + return -EAGAIN; + } + + /* + * The datasheet specifies that the interval between sending screen-off + * command and wake-up should be longer than 58 ms. To avoid waking up + * sooner, delay 58ms here. + */ + msleep(58); + return 0; +} + +static int __maybe_unused goodix_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct goodix_ts_data *ts = i2c_get_clientdata(client); + int error; + + if (!ts->gpiod_int || !ts->gpiod_rst) + return 0; + + /* + * Exit sleep mode by outputting HIGH level to INT pin + * for 2ms~5ms. + */ + error = gpiod_direction_output(ts->gpiod_int, 1); + if (error) + return error; + + usleep_range(2000, 5000); + + error = goodix_int_sync(ts); + if (error) + return error; + + error = goodix_request_irq(ts); + if (error) + return error; + return 0; } +static SIMPLE_DEV_PM_OPS(goodix_pm_ops, goodix_suspend, goodix_resume); + static const struct i2c_device_id goodix_ts_id[] = { { "GDIX1001:00", 0 }, { } @@ -446,11 +875,13 @@ MODULE_DEVICE_TABLE(of, goodix_of_match); static struct i2c_driver goodix_ts_driver = { .probe = goodix_ts_probe, + .remove = goodix_ts_remove, .id_table = goodix_ts_id, .driver = { .name = "Goodix-TS", .acpi_match_table = ACPI_PTR(goodix_acpi_match), .of_match_table = of_match_ptr(goodix_of_match), + .pm = &goodix_pm_ops, }, }; module_i2c_driver(goodix_ts_driver); diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c new file mode 100644 index 000000000000..892729734c51 --- /dev/null +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -0,0 +1,1517 @@ +/* + * MELFAS MIP4 Touchscreen + * + * Copyright (C) 2016 MELFAS Inc. + * + * Author : Sangwon Jee <jeesw@melfas.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <asm/unaligned.h> + +#define MIP4_DEVICE_NAME "mip4_ts" + +/***************************************************************** + * Protocol + * Version : MIP 4.0 Rev 4.6 + *****************************************************************/ + +/* Address */ +#define MIP4_R0_BOOT 0x00 +#define MIP4_R1_BOOT_MODE 0x01 +#define MIP4_R1_BOOT_BUF_ADDR 0x10 +#define MIP4_R1_BOOT_STATUS 0x20 +#define MIP4_R1_BOOT_CMD 0x30 +#define MIP4_R1_BOOT_TARGET_ADDR 0x40 +#define MIP4_R1_BOOT_SIZE 0x44 + +#define MIP4_R0_INFO 0x01 +#define MIP4_R1_INFO_PRODUCT_NAME 0x00 +#define MIP4_R1_INFO_RESOLUTION_X 0x10 +#define MIP4_R1_INFO_RESOLUTION_Y 0x12 +#define MIP4_R1_INFO_NODE_NUM_X 0x14 +#define MIP4_R1_INFO_NODE_NUM_Y 0x15 +#define MIP4_R1_INFO_KEY_NUM 0x16 +#define MIP4_R1_INFO_PRESSURE_NUM 0x17 +#define MIP4_R1_INFO_LENGTH_X 0x18 +#define MIP4_R1_INFO_LENGTH_Y 0x1A +#define MIP4_R1_INFO_PPM_X 0x1C +#define MIP4_R1_INFO_PPM_Y 0x1D +#define MIP4_R1_INFO_VERSION_BOOT 0x20 +#define MIP4_R1_INFO_VERSION_CORE 0x22 +#define MIP4_R1_INFO_VERSION_APP 0x24 +#define MIP4_R1_INFO_VERSION_PARAM 0x26 +#define MIP4_R1_INFO_SECT_BOOT_START 0x30 +#define MIP4_R1_INFO_SECT_BOOT_END 0x31 +#define MIP4_R1_INFO_SECT_CORE_START 0x32 +#define MIP4_R1_INFO_SECT_CORE_END 0x33 +#define MIP4_R1_INFO_SECT_APP_START 0x34 +#define MIP4_R1_INFO_SECT_APP_END 0x35 +#define MIP4_R1_INFO_SECT_PARAM_START 0x36 +#define MIP4_R1_INFO_SECT_PARAM_END 0x37 +#define MIP4_R1_INFO_BUILD_DATE 0x40 +#define MIP4_R1_INFO_BUILD_TIME 0x44 +#define MIP4_R1_INFO_CHECKSUM_PRECALC 0x48 +#define MIP4_R1_INFO_CHECKSUM_REALTIME 0x4A +#define MIP4_R1_INFO_PROTOCOL_NAME 0x50 +#define MIP4_R1_INFO_PROTOCOL_VERSION 0x58 +#define MIP4_R1_INFO_IC_ID 0x70 +#define MIP4_R1_INFO_IC_NAME 0x71 +#define MIP4_R1_INFO_IC_VENDOR_ID 0x75 +#define MIP4_R1_INFO_IC_HW_CATEGORY 0x77 +#define MIP4_R1_INFO_CONTACT_THD_SCR 0x78 +#define MIP4_R1_INFO_CONTACT_THD_KEY 0x7A + +#define MIP4_R0_EVENT 0x02 +#define MIP4_R1_EVENT_SUPPORTED_FUNC 0x00 +#define MIP4_R1_EVENT_FORMAT 0x04 +#define MIP4_R1_EVENT_SIZE 0x06 +#define MIP4_R1_EVENT_PACKET_INFO 0x10 +#define MIP4_R1_EVENT_PACKET_DATA 0x11 + +#define MIP4_R0_CTRL 0x06 +#define MIP4_R1_CTRL_READY_STATUS 0x00 +#define MIP4_R1_CTRL_EVENT_READY 0x01 +#define MIP4_R1_CTRL_MODE 0x10 +#define MIP4_R1_CTRL_EVENT_TRIGGER_TYPE 0x11 +#define MIP4_R1_CTRL_RECALIBRATE 0x12 +#define MIP4_R1_CTRL_POWER_STATE 0x13 +#define MIP4_R1_CTRL_GESTURE_TYPE 0x14 +#define MIP4_R1_CTRL_DISABLE_ESD_ALERT 0x18 +#define MIP4_R1_CTRL_CHARGER_MODE 0x19 +#define MIP4_R1_CTRL_HIGH_SENS_MODE 0x1A +#define MIP4_R1_CTRL_WINDOW_MODE 0x1B +#define MIP4_R1_CTRL_PALM_REJECTION 0x1C +#define MIP4_R1_CTRL_EDGE_CORRECTION 0x1D +#define MIP4_R1_CTRL_ENTER_GLOVE_MODE 0x1E +#define MIP4_R1_CTRL_I2C_ON_LPM 0x1F +#define MIP4_R1_CTRL_GESTURE_DEBUG 0x20 +#define MIP4_R1_CTRL_PALM_EVENT 0x22 +#define MIP4_R1_CTRL_PROXIMITY_SENSING 0x23 + +/* Value */ +#define MIP4_BOOT_MODE_BOOT 0x01 +#define MIP4_BOOT_MODE_APP 0x02 + +#define MIP4_BOOT_STATUS_BUSY 0x05 +#define MIP4_BOOT_STATUS_ERROR 0x0E +#define MIP4_BOOT_STATUS_DONE 0xA0 + +#define MIP4_BOOT_CMD_MASS_ERASE 0x15 +#define MIP4_BOOT_CMD_PROGRAM 0x54 +#define MIP4_BOOT_CMD_ERASE 0x8F +#define MIP4_BOOT_CMD_WRITE 0xA5 +#define MIP4_BOOT_CMD_READ 0xC2 + +#define MIP4_EVENT_INPUT_TYPE_KEY 0 +#define MIP4_EVENT_INPUT_TYPE_SCREEN 1 +#define MIP4_EVENT_INPUT_TYPE_PROXIMITY 2 + +#define I2C_RETRY_COUNT 3 /* 2~ */ + +#define MIP4_BUF_SIZE 128 +#define MIP4_MAX_FINGERS 10 +#define MIP4_MAX_KEYS 4 + +#define MIP4_TOUCH_MAJOR_MIN 0 +#define MIP4_TOUCH_MAJOR_MAX 255 +#define MIP4_TOUCH_MINOR_MIN 0 +#define MIP4_TOUCH_MINOR_MAX 255 +#define MIP4_PRESSURE_MIN 0 +#define MIP4_PRESSURE_MAX 255 + +#define MIP4_FW_NAME "melfas_mip4.fw" +#define MIP4_FW_UPDATE_DEBUG 0 /* 0 (default) or 1 */ + +struct mip4_fw_version { + u16 boot; + u16 core; + u16 app; + u16 param; +}; + +struct mip4_ts { + struct i2c_client *client; + struct input_dev *input; + struct gpio_desc *gpio_ce; + + char phys[32]; + char product_name[16]; + + unsigned int max_x; + unsigned int max_y; + u8 node_x; + u8 node_y; + u8 node_key; + unsigned int ppm_x; + unsigned int ppm_y; + + struct mip4_fw_version fw_version; + + unsigned int event_size; + unsigned int event_format; + + unsigned int key_num; + unsigned short key_code[MIP4_MAX_KEYS]; + + bool wake_irq_enabled; + + u8 buf[MIP4_BUF_SIZE]; +}; + +static int mip4_i2c_xfer(struct mip4_ts *ts, + char *write_buf, unsigned int write_len, + char *read_buf, unsigned int read_len) +{ + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = write_buf, + .len = write_len, + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .buf = read_buf, + .len = read_len, + }, + }; + int retry = I2C_RETRY_COUNT; + int res; + int error; + + do { + res = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (res == ARRAY_SIZE(msg)) + return 0; + + error = res < 0 ? res : -EIO; + dev_err(&ts->client->dev, + "%s - i2c_transfer failed: %d (%d)\n", + __func__, error, res); + } while (--retry); + + return error; +} + +static void mip4_parse_fw_version(const u8 *buf, struct mip4_fw_version *v) +{ + v->boot = get_unaligned_le16(buf + 0); + v->core = get_unaligned_le16(buf + 2); + v->app = get_unaligned_le16(buf + 4); + v->param = get_unaligned_le16(buf + 6); +} + +/* + * Read chip firmware version + */ +static int mip4_get_fw_version(struct mip4_ts *ts) +{ + u8 cmd[] = { MIP4_R0_INFO, MIP4_R1_INFO_VERSION_BOOT }; + u8 buf[sizeof(ts->fw_version)]; + int error; + + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, sizeof(buf)); + if (error) { + memset(&ts->fw_version, 0xff, sizeof(ts->fw_version)); + return error; + } + + mip4_parse_fw_version(buf, &ts->fw_version); + + return 0; +} + +/* + * Fetch device characteristics + */ +static int mip4_query_device(struct mip4_ts *ts) +{ + int error; + u8 cmd[2]; + u8 buf[14]; + + /* Product name */ + cmd[0] = MIP4_R0_INFO; + cmd[1] = MIP4_R1_INFO_PRODUCT_NAME; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), + ts->product_name, sizeof(ts->product_name)); + if (error) + dev_warn(&ts->client->dev, + "Failed to retrieve product name: %d\n", error); + else + dev_dbg(&ts->client->dev, "product name: %.*s\n", + (int)sizeof(ts->product_name), ts->product_name); + + /* Firmware version */ + error = mip4_get_fw_version(ts); + if (error) + dev_warn(&ts->client->dev, + "Failed to retrieve FW version: %d\n", error); + else + dev_dbg(&ts->client->dev, "F/W Version: %04X %04X %04X %04X\n", + ts->fw_version.boot, ts->fw_version.core, + ts->fw_version.app, ts->fw_version.param); + + /* Resolution */ + cmd[0] = MIP4_R0_INFO; + cmd[1] = MIP4_R1_INFO_RESOLUTION_X; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 14); + if (error) { + dev_warn(&ts->client->dev, + "Failed to retrieve touchscreen parameters: %d\n", + error); + } else { + ts->max_x = get_unaligned_le16(&buf[0]); + ts->max_y = get_unaligned_le16(&buf[2]); + dev_dbg(&ts->client->dev, "max_x: %d, max_y: %d\n", + ts->max_x, ts->max_y); + + ts->node_x = buf[4]; + ts->node_y = buf[5]; + ts->node_key = buf[6]; + dev_dbg(&ts->client->dev, + "node_x: %d, node_y: %d, node_key: %d\n", + ts->node_x, ts->node_y, ts->node_key); + + ts->ppm_x = buf[12]; + ts->ppm_y = buf[13]; + dev_dbg(&ts->client->dev, "ppm_x: %d, ppm_y: %d\n", + ts->ppm_x, ts->ppm_y); + + /* Key ts */ + if (ts->node_key > 0) + ts->key_num = ts->node_key; + } + + /* Protocol */ + cmd[0] = MIP4_R0_EVENT; + cmd[1] = MIP4_R1_EVENT_SUPPORTED_FUNC; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 7); + if (error) { + dev_warn(&ts->client->dev, + "Failed to retrieve device type: %d\n", error); + ts->event_format = 0xff; + } else { + ts->event_format = get_unaligned_le16(&buf[4]); + ts->event_size = buf[6]; + dev_dbg(&ts->client->dev, "event_format: %d, event_size: %d\n", + ts->event_format, ts->event_size); + + if (ts->event_format == 2 || ts->event_format > 3) + dev_warn(&ts->client->dev, + "Unknown event format %d\n", ts->event_format); + } + + return 0; +} + +static int mip4_power_on(struct mip4_ts *ts) +{ + if (ts->gpio_ce) { + gpiod_set_value_cansleep(ts->gpio_ce, 1); + + /* Booting delay : 200~300ms */ + usleep_range(200 * 1000, 300 * 1000); + } + + return 0; +} + +static void mip4_power_off(struct mip4_ts *ts) +{ + if (ts->gpio_ce) + gpiod_set_value_cansleep(ts->gpio_ce, 0); +} + +/* + * Clear touch input event status + */ +static void mip4_clear_input(struct mip4_ts *ts) +{ + int i; + + /* Screen */ + for (i = 0; i < MIP4_MAX_FINGERS; i++) { + input_mt_slot(ts->input, i); + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0); + } + + /* Keys */ + for (i = 0; i < ts->key_num; i++) + input_report_key(ts->input, ts->key_code[i], 0); + + input_sync(ts->input); +} + +static int mip4_enable(struct mip4_ts *ts) +{ + int error; + + error = mip4_power_on(ts); + if (error) + return error; + + enable_irq(ts->client->irq); + + return 0; +} + +static void mip4_disable(struct mip4_ts *ts) +{ + disable_irq(ts->client->irq); + + mip4_power_off(ts); + + mip4_clear_input(ts); +} + +/***************************************************************** + * Input handling + *****************************************************************/ + +static void mip4_report_keys(struct mip4_ts *ts, u8 *packet) +{ + u8 key; + bool down; + + switch (ts->event_format) { + case 0: + case 1: + key = packet[0] & 0x0F; + down = packet[0] & 0x80; + break; + + case 3: + default: + key = packet[0] & 0x0F; + down = packet[1] & 0x01; + break; + } + + /* Report key event */ + if (key >= 1 && key <= ts->key_num) { + unsigned short keycode = ts->key_code[key - 1]; + + dev_dbg(&ts->client->dev, + "Key - ID: %d, keycode: %d, state: %d\n", + key, keycode, down); + + input_event(ts->input, EV_MSC, MSC_SCAN, keycode); + input_report_key(ts->input, keycode, down); + + } else { + dev_err(&ts->client->dev, "Unknown key: %d\n", key); + } +} + +static void mip4_report_touch(struct mip4_ts *ts, u8 *packet) +{ + int id; + bool hover; + bool palm; + bool state; + u16 x, y; + u8 pressure_stage = 0; + u8 pressure; + u8 size; + u8 touch_major; + u8 touch_minor; + + switch (ts->event_format) { + case 0: + case 1: + /* Touch only */ + state = packet[0] & BIT(7); + hover = packet[0] & BIT(5); + palm = packet[0] & BIT(4); + id = (packet[0] & 0x0F) - 1; + x = ((packet[1] & 0x0F) << 8) | packet[2]; + y = (((packet[1] >> 4) & 0x0F) << 8) | + packet[3]; + pressure = packet[4]; + size = packet[5]; + if (ts->event_format == 0) { + touch_major = packet[5]; + touch_minor = packet[5]; + } else { + touch_major = packet[6]; + touch_minor = packet[7]; + } + break; + + case 3: + default: + /* Touch + Force(Pressure) */ + id = (packet[0] & 0x0F) - 1; + hover = packet[1] & BIT(2); + palm = packet[1] & BIT(1); + state = packet[1] & BIT(0); + x = ((packet[2] & 0x0F) << 8) | packet[3]; + y = (((packet[2] >> 4) & 0x0F) << 8) | + packet[4]; + size = packet[6]; + pressure_stage = (packet[7] & 0xF0) >> 4; + pressure = ((packet[7] & 0x0F) << 8) | + packet[8]; + touch_major = packet[9]; + touch_minor = packet[10]; + break; + } + + dev_dbg(&ts->client->dev, + "Screen - Slot: %d State: %d X: %04d Y: %04d Z: %d\n", + id, state, x, y, pressure); + + if (unlikely(id < 0 || id >= MIP4_MAX_FINGERS)) { + dev_err(&ts->client->dev, "Screen - invalid slot ID: %d\n", id); + } else if (state) { + /* Press or Move event */ + input_mt_slot(ts->input, id); + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true); + input_report_abs(ts->input, ABS_MT_POSITION_X, x); + input_report_abs(ts->input, ABS_MT_POSITION_Y, y); + input_report_abs(ts->input, ABS_MT_PRESSURE, pressure); + input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, touch_major); + input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, touch_minor); + } else { + /* Release event */ + input_mt_slot(ts->input, id); + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0); + } + + input_mt_sync_frame(ts->input); +} + +static int mip4_handle_packet(struct mip4_ts *ts, u8 *packet) +{ + u8 type; + + switch (ts->event_format) { + case 0: + case 1: + type = (packet[0] & 0x40) >> 6; + break; + + case 3: + type = (packet[0] & 0xF0) >> 4; + break; + + default: + /* Should not happen unless we have corrupted firmware */ + return -EINVAL; + } + + dev_dbg(&ts->client->dev, "Type: %d\n", type); + + /* Report input event */ + switch (type) { + case MIP4_EVENT_INPUT_TYPE_KEY: + mip4_report_keys(ts, packet); + break; + + case MIP4_EVENT_INPUT_TYPE_SCREEN: + mip4_report_touch(ts, packet); + break; + + default: + dev_err(&ts->client->dev, "Unknown event type: %d\n", type); + break; + } + + return 0; +} + +static irqreturn_t mip4_interrupt(int irq, void *dev_id) +{ + struct mip4_ts *ts = dev_id; + struct i2c_client *client = ts->client; + unsigned int i; + int error; + u8 cmd[2]; + u8 size; + bool alert; + + /* Read packet info */ + cmd[0] = MIP4_R0_EVENT; + cmd[1] = MIP4_R1_EVENT_PACKET_INFO; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), ts->buf, 1); + if (error) { + dev_err(&client->dev, + "Failed to read packet info: %d\n", error); + goto out; + } + + size = ts->buf[0] & 0x7F; + alert = ts->buf[0] & BIT(7); + dev_dbg(&client->dev, "packet size: %d, alert: %d\n", size, alert); + + /* Check size */ + if (!size) { + dev_err(&client->dev, "Empty packet\n"); + goto out; + } + + /* Read packet data */ + cmd[0] = MIP4_R0_EVENT; + cmd[1] = MIP4_R1_EVENT_PACKET_DATA; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), ts->buf, size); + if (error) { + dev_err(&client->dev, + "Failed to read packet data: %d\n", error); + goto out; + } + + if (alert) { + dev_dbg(&client->dev, "Alert: %d\n", ts->buf[0]); + } else { + for (i = 0; i < size; i += ts->event_size) { + error = mip4_handle_packet(ts, &ts->buf[i]); + if (error) + break; + } + + input_sync(ts->input); + } + +out: + return IRQ_HANDLED; +} + +static int mip4_input_open(struct input_dev *dev) +{ + struct mip4_ts *ts = input_get_drvdata(dev); + + return mip4_enable(ts); +} + +static void mip4_input_close(struct input_dev *dev) +{ + struct mip4_ts *ts = input_get_drvdata(dev); + + mip4_disable(ts); +} + +/***************************************************************** + * Firmware update + *****************************************************************/ + +/* Firmware Info */ +#define MIP4_BL_PAGE_SIZE 512 /* 512 */ +#define MIP4_BL_PACKET_SIZE 512 /* 512, 256, 128, 64, ... */ + +/* + * Firmware binary tail info + */ + +struct mip4_bin_tail { + u8 tail_mark[4]; + u8 chip_name[4]; + + __le32 bin_start_addr; + __le32 bin_length; + + __le16 ver_boot; + __le16 ver_core; + __le16 ver_app; + __le16 ver_param; + + u8 boot_start; + u8 boot_end; + u8 core_start; + u8 core_end; + u8 app_start; + u8 app_end; + u8 param_start; + u8 param_end; + + u8 checksum_type; + u8 hw_category; + + __le16 param_id; + __le32 param_length; + __le32 build_date; + __le32 build_time; + + __le32 reserved1; + __le32 reserved2; + __le16 reserved3; + __le16 tail_size; + __le32 crc; +} __packed; + +#define MIP4_BIN_TAIL_MARK "MBT\001" +#define MIP4_BIN_TAIL_SIZE (sizeof(struct mip4_bin_tail)) + +/* +* Bootloader - Read status +*/ +static int mip4_bl_read_status(struct mip4_ts *ts) +{ + u8 cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_STATUS }; + u8 result; + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = cmd, + .len = sizeof(cmd), + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .buf = &result, + .len = sizeof(result), + }, + }; + int ret; + int error; + int retry = 1000; + + do { + ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to read bootloader status: %d\n", + error); + return error; + } + + switch (result) { + case MIP4_BOOT_STATUS_DONE: + dev_dbg(&ts->client->dev, "%s - done\n", __func__); + return 0; + + case MIP4_BOOT_STATUS_ERROR: + dev_err(&ts->client->dev, "Bootloader failure\n"); + return -EIO; + + case MIP4_BOOT_STATUS_BUSY: + dev_dbg(&ts->client->dev, "%s - Busy\n", __func__); + error = -EBUSY; + break; + + default: + dev_err(&ts->client->dev, + "Unexpected bootloader status: %#02x\n", + result); + error = -EINVAL; + break; + } + + usleep_range(1000, 2000); + } while (--retry); + + return error; +} + +/* +* Bootloader - Change mode +*/ +static int mip4_bl_change_mode(struct mip4_ts *ts, u8 mode) +{ + u8 mode_chg_cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_MODE, mode }; + u8 mode_read_cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_MODE }; + u8 result; + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = mode_read_cmd, + .len = sizeof(mode_read_cmd), + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .buf = &result, + .len = sizeof(result), + }, + }; + int retry = 10; + int ret; + int error; + + do { + /* Send mode change command */ + ret = i2c_master_send(ts->client, + mode_chg_cmd, sizeof(mode_chg_cmd)); + if (ret != sizeof(mode_chg_cmd)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send %d mode change: %d (%d)\n", + mode, error, ret); + return error; + } + + dev_dbg(&ts->client->dev, + "Sent mode change request (mode: %d)\n", mode); + + /* Wait */ + msleep(1000); + + /* Verify target mode */ + ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to read device mode: %d\n", error); + return error; + } + + dev_dbg(&ts->client->dev, + "Current device mode: %d, want: %d\n", result, mode); + + if (result == mode) + return 0; + + } while (--retry); + + return -EIO; +} + +/* + * Bootloader - Start bootloader mode + */ +static int mip4_bl_enter(struct mip4_ts *ts) +{ + return mip4_bl_change_mode(ts, MIP4_BOOT_MODE_BOOT); +} + +/* + * Bootloader - Exit bootloader mode + */ +static int mip4_bl_exit(struct mip4_ts *ts) +{ + return mip4_bl_change_mode(ts, MIP4_BOOT_MODE_APP); +} + +static int mip4_bl_get_address(struct mip4_ts *ts, u16 *buf_addr) +{ + u8 cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_BUF_ADDR }; + u8 result[sizeof(u16)]; + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = cmd, + .len = sizeof(cmd), + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .buf = result, + .len = sizeof(result), + }, + }; + int ret; + int error; + + ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to retrieve bootloader buffer address: %d\n", + error); + return error; + } + + *buf_addr = get_unaligned_le16(result); + dev_dbg(&ts->client->dev, + "Bootloader buffer address %#04x\n", *buf_addr); + + return 0; +} + +static int mip4_bl_program_page(struct mip4_ts *ts, int offset, + const u8 *data, int length, u16 buf_addr) +{ + u8 cmd[6]; + u8 *data_buf; + u16 buf_offset; + int ret; + int error; + + dev_dbg(&ts->client->dev, "Writing page @%#06x (%d)\n", + offset, length); + + if (length > MIP4_BL_PAGE_SIZE || length % MIP4_BL_PACKET_SIZE) { + dev_err(&ts->client->dev, + "Invalid page length: %d\n", length); + return -EINVAL; + } + + data_buf = kmalloc(2 + MIP4_BL_PACKET_SIZE, GFP_KERNEL); + if (!data_buf) + return -ENOMEM; + + /* Addr */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_TARGET_ADDR; + put_unaligned_le32(offset, &cmd[2]); + ret = i2c_master_send(ts->client, cmd, 6); + if (ret != 6) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send write page address: %d\n", error); + goto out; + } + + /* Size */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_SIZE; + put_unaligned_le32(length, &cmd[2]); + ret = i2c_master_send(ts->client, cmd, 6); + if (ret != 6) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send write page size: %d\n", error); + goto out; + } + + /* Data */ + for (buf_offset = 0; + buf_offset < length; + buf_offset += MIP4_BL_PACKET_SIZE) { + dev_dbg(&ts->client->dev, + "writing chunk at %#04x (size %d)\n", + buf_offset, MIP4_BL_PACKET_SIZE); + put_unaligned_be16(buf_addr + buf_offset, data_buf); + memcpy(&data_buf[2], &data[buf_offset], MIP4_BL_PACKET_SIZE); + ret = i2c_master_send(ts->client, + data_buf, 2 + MIP4_BL_PACKET_SIZE); + if (ret != 2 + MIP4_BL_PACKET_SIZE) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to read chunk at %#04x (size %d): %d\n", + buf_offset, MIP4_BL_PACKET_SIZE, error); + goto out; + } + } + + /* Command */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_CMD; + cmd[2] = MIP4_BOOT_CMD_PROGRAM; + ret = i2c_master_send(ts->client, cmd, 3); + if (ret != 3) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send 'write' command: %d\n", error); + goto out; + } + + /* Status */ + error = mip4_bl_read_status(ts); + +out: + kfree(data_buf); + return error ? error : 0; +} + +static int mip4_bl_verify_page(struct mip4_ts *ts, int offset, + const u8 *data, int length, int buf_addr) +{ + u8 cmd[8]; + u8 *read_buf; + int buf_offset; + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = cmd, + .len = 2, + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .len = MIP4_BL_PACKET_SIZE, + }, + }; + int ret; + int error; + + dev_dbg(&ts->client->dev, "Validating page @%#06x (%d)\n", + offset, length); + + /* Addr */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_TARGET_ADDR; + put_unaligned_le32(offset, &cmd[2]); + ret = i2c_master_send(ts->client, cmd, 6); + if (ret != 6) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send read page address: %d\n", error); + return error; + } + + /* Size */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_SIZE; + put_unaligned_le32(length, &cmd[2]); + ret = i2c_master_send(ts->client, cmd, 6); + if (ret != 6) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send read page size: %d\n", error); + return error; + } + + /* Command */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_CMD; + cmd[2] = MIP4_BOOT_CMD_READ; + ret = i2c_master_send(ts->client, cmd, 3); + if (ret != 3) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send 'read' command: %d\n", error); + return error; + } + + /* Status */ + error = mip4_bl_read_status(ts); + if (error) + return error; + + /* Read */ + msg[1].buf = read_buf = kmalloc(MIP4_BL_PACKET_SIZE, GFP_KERNEL); + if (!read_buf) + return -ENOMEM; + + for (buf_offset = 0; + buf_offset < length; + buf_offset += MIP4_BL_PACKET_SIZE) { + dev_dbg(&ts->client->dev, + "reading chunk at %#04x (size %d)\n", + buf_offset, MIP4_BL_PACKET_SIZE); + put_unaligned_be16(buf_addr + buf_offset, cmd); + ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to read chunk at %#04x (size %d): %d\n", + buf_offset, MIP4_BL_PACKET_SIZE, error); + break; + } + + if (memcmp(&data[buf_offset], read_buf, MIP4_BL_PACKET_SIZE)) { + dev_err(&ts->client->dev, + "Failed to validate chunk at %#04x (size %d)\n", + buf_offset, MIP4_BL_PACKET_SIZE); +#if MIP4_FW_UPDATE_DEBUG + print_hex_dump(KERN_DEBUG, + MIP4_DEVICE_NAME " F/W File: ", + DUMP_PREFIX_OFFSET, 16, 1, + data + offset, MIP4_BL_PACKET_SIZE, + false); + print_hex_dump(KERN_DEBUG, + MIP4_DEVICE_NAME " F/W Chip: ", + DUMP_PREFIX_OFFSET, 16, 1, + read_buf, MIP4_BL_PAGE_SIZE, false); +#endif + error = -EINVAL; + break; + } + } + + kfree(read_buf); + return error ? error : 0; +} + +/* + * Flash chip firmware + */ +static int mip4_flash_fw(struct mip4_ts *ts, + const u8 *fw_data, u32 fw_size, u32 fw_offset) +{ + struct i2c_client *client = ts->client; + int offset; + u16 buf_addr; + int error, error2; + + /* Enter bootloader mode */ + dev_dbg(&client->dev, "Entering bootloader mode\n"); + + error = mip4_bl_enter(ts); + if (error) { + dev_err(&client->dev, + "Failed to enter bootloader mode: %d\n", + error); + return error; + } + + /* Read info */ + error = mip4_bl_get_address(ts, &buf_addr); + if (error) + goto exit_bl; + + /* Program & Verify */ + dev_dbg(&client->dev, + "Program & Verify, page size: %d, packet size: %d\n", + MIP4_BL_PAGE_SIZE, MIP4_BL_PACKET_SIZE); + + for (offset = fw_offset; + offset < fw_offset + fw_size; + offset += MIP4_BL_PAGE_SIZE) { + /* Program */ + error = mip4_bl_program_page(ts, offset, fw_data + offset, + MIP4_BL_PAGE_SIZE, buf_addr); + if (error) + break; + + /* Verify */ + error = mip4_bl_verify_page(ts, offset, fw_data + offset, + MIP4_BL_PAGE_SIZE, buf_addr); + if (error) + break; + } + +exit_bl: + /* Exit bootloader mode */ + dev_dbg(&client->dev, "Exiting bootloader mode\n"); + + error2 = mip4_bl_exit(ts); + if (error2) { + dev_err(&client->dev, + "Failed to exit bootloader mode: %d\n", error2); + if (!error) + error = error2; + } + + /* Reset chip */ + mip4_power_off(ts); + mip4_power_on(ts); + + mip4_query_device(ts); + + /* Refresh device parameters */ + input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->max_x, 0, 0); + input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->max_y, 0, 0); + input_set_abs_params(ts->input, ABS_X, 0, ts->max_x, 0, 0); + input_set_abs_params(ts->input, ABS_Y, 0, ts->max_y, 0, 0); + input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->ppm_x); + input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->ppm_y); + input_abs_set_res(ts->input, ABS_X, ts->ppm_x); + input_abs_set_res(ts->input, ABS_Y, ts->ppm_y); + + return error ? error : 0; +} + +static int mip4_parse_firmware(struct mip4_ts *ts, const struct firmware *fw, + u32 *fw_offset_start, u32 *fw_size, + const struct mip4_bin_tail **pfw_info) +{ + const struct mip4_bin_tail *fw_info; + struct mip4_fw_version fw_version; + u16 tail_size; + + if (fw->size < MIP4_BIN_TAIL_SIZE) { + dev_err(&ts->client->dev, + "Invalid firmware, size mismatch (tail %zd vs %zd)\n", + MIP4_BIN_TAIL_SIZE, fw->size); + return -EINVAL; + } + + fw_info = (const void *)&fw->data[fw->size - MIP4_BIN_TAIL_SIZE]; + +#if MIP4_FW_UPDATE_DEBUG + print_hex_dump(KERN_ERR, MIP4_DEVICE_NAME " Bin Info: ", + DUMP_PREFIX_OFFSET, 16, 1, *fw_info, tail_size, false); +#endif + + tail_size = get_unaligned_le16(&fw_info->tail_size); + if (tail_size != MIP4_BIN_TAIL_SIZE) { + dev_err(&ts->client->dev, + "wrong tail size: %d (expected %zd)\n", + tail_size, MIP4_BIN_TAIL_SIZE); + return -EINVAL; + } + + /* Check bin format */ + if (memcmp(fw_info->tail_mark, MIP4_BIN_TAIL_MARK, + sizeof(fw_info->tail_mark))) { + dev_err(&ts->client->dev, + "unable to locate tail marker (%*ph vs %*ph)\n", + (int)sizeof(fw_info->tail_mark), fw_info->tail_mark, + (int)sizeof(fw_info->tail_mark), MIP4_BIN_TAIL_MARK); + return -EINVAL; + } + + *fw_offset_start = get_unaligned_le32(&fw_info->bin_start_addr); + *fw_size = get_unaligned_le32(&fw_info->bin_length); + + dev_dbg(&ts->client->dev, + "F/W Data offset: %#08x, size: %d\n", + *fw_offset_start, *fw_size); + + if (*fw_size % MIP4_BL_PAGE_SIZE) { + dev_err(&ts->client->dev, + "encoded fw length %d is not multiple of pages (%d)\n", + *fw_size, MIP4_BL_PAGE_SIZE); + return -EINVAL; + } + + if (fw->size != *fw_offset_start + *fw_size) { + dev_err(&ts->client->dev, + "Wrong firmware size, expected %d bytes, got %zd\n", + *fw_offset_start + *fw_size, fw->size); + return -EINVAL; + } + + mip4_parse_fw_version((const u8 *)&fw_info->ver_boot, &fw_version); + + dev_dbg(&ts->client->dev, + "F/W file version %04X %04X %04X %04X\n", + fw_version.boot, fw_version.core, + fw_version.app, fw_version.param); + + dev_dbg(&ts->client->dev, "F/W chip version: %04X %04X %04X %04X\n", + ts->fw_version.boot, ts->fw_version.core, + ts->fw_version.app, ts->fw_version.param); + + /* Check F/W type */ + if (fw_version.boot != 0xEEEE && fw_version.boot != 0xFFFF && + fw_version.core == 0xEEEE && + fw_version.app == 0xEEEE && + fw_version.param == 0xEEEE) { + dev_dbg(&ts->client->dev, "F/W type: Bootloader\n"); + } else if (fw_version.boot == 0xEEEE && + fw_version.core != 0xEEEE && fw_version.core != 0xFFFF && + fw_version.app != 0xEEEE && fw_version.app != 0xFFFF && + fw_version.param != 0xEEEE && fw_version.param != 0xFFFF) { + dev_dbg(&ts->client->dev, "F/W type: Main\n"); + } else { + dev_err(&ts->client->dev, "Wrong firmware type\n"); + return -EINVAL; + } + + return 0; +} + +static int mip4_execute_fw_update(struct mip4_ts *ts, const struct firmware *fw) +{ + const struct mip4_bin_tail *fw_info; + u32 fw_start_offset; + u32 fw_size; + int retires = 3; + int error; + + error = mip4_parse_firmware(ts, fw, + &fw_start_offset, &fw_size, &fw_info); + if (error) + return error; + + if (ts->input->users) { + disable_irq(ts->client->irq); + } else { + error = mip4_power_on(ts); + if (error) + return error; + } + + /* Update firmware */ + do { + error = mip4_flash_fw(ts, fw->data, fw_size, fw_start_offset); + if (!error) + break; + } while (--retires); + + if (error) + dev_err(&ts->client->dev, + "Failed to flash firmware: %d\n", error); + + /* Enable IRQ */ + if (ts->input->users) + enable_irq(ts->client->irq); + else + mip4_power_off(ts); + + return error ? error : 0; +} + +static ssize_t mip4_sysfs_fw_update(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + const struct firmware *fw; + int error; + + error = request_firmware(&fw, MIP4_FW_NAME, dev); + if (error) { + dev_err(&ts->client->dev, + "Failed to retrieve firmware %s: %d\n", + MIP4_FW_NAME, error); + return error; + } + + /* + * Take input mutex to prevent racing with itself and also with + * userspace opening and closing the device and also suspend/resume + * transitions. + */ + mutex_lock(&ts->input->mutex); + + error = mip4_execute_fw_update(ts, fw); + + mutex_unlock(&ts->input->mutex); + + release_firmware(fw); + + if (error) { + dev_err(&ts->client->dev, + "Firmware update failed: %d\n", error); + return error; + } + + return count; +} + +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mip4_sysfs_fw_update); + +static ssize_t mip4_sysfs_read_fw_version(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + size_t count; + + /* Take lock to prevent racing with firmware update */ + mutex_lock(&ts->input->mutex); + + count = snprintf(buf, PAGE_SIZE, "%04X %04X %04X %04X\n", + ts->fw_version.boot, ts->fw_version.core, + ts->fw_version.app, ts->fw_version.param); + + mutex_unlock(&ts->input->mutex); + + return count; +} + +static DEVICE_ATTR(fw_version, S_IRUGO, mip4_sysfs_read_fw_version, NULL); + +static struct attribute *mip4_attrs[] = { + &dev_attr_fw_version.attr, + &dev_attr_update_fw.attr, + NULL, +}; + +static const struct attribute_group mip4_attr_group = { + .attrs = mip4_attrs, +}; + +static void mip4_sysfs_remove(void *_data) +{ + struct mip4_ts *ts = _data; + + sysfs_remove_group(&ts->client->dev.kobj, &mip4_attr_group); +} + +static int mip4_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct mip4_ts *ts; + struct input_dev *input; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "Not supported I2C adapter\n"); + return -ENXIO; + } + + ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + input = devm_input_allocate_device(&client->dev); + if (!input) + return -ENOMEM; + + ts->client = client; + ts->input = input; + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input0", dev_name(&client->dev)); + + ts->gpio_ce = devm_gpiod_get_optional(&client->dev, + "ce", GPIOD_OUT_LOW); + if (IS_ERR(ts->gpio_ce)) { + error = PTR_ERR(ts->gpio_ce); + if (error != EPROBE_DEFER) + dev_err(&client->dev, + "Failed to get gpio: %d\n", error); + return error; + } + + error = mip4_power_on(ts); + if (error) + return error; + error = mip4_query_device(ts); + mip4_power_off(ts); + if (error) + return error; + + input->name = "MELFAS MIP4 Touchscreen"; + input->phys = ts->phys; + + input->id.bustype = BUS_I2C; + input->id.vendor = 0x13c5; + + input->open = mip4_input_open; + input->close = mip4_input_close; + + input_set_drvdata(input, ts); + + input->keycode = ts->key_code; + input->keycodesize = sizeof(*ts->key_code); + input->keycodemax = ts->key_num; + + input_set_abs_params(input, ABS_MT_POSITION_X, 0, ts->max_x, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ts->max_y, 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, + MIP4_PRESSURE_MIN, MIP4_PRESSURE_MAX, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, + MIP4_TOUCH_MAJOR_MIN, MIP4_TOUCH_MAJOR_MAX, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, + MIP4_TOUCH_MINOR_MIN, MIP4_TOUCH_MINOR_MAX, 0, 0); + input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->ppm_x); + input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->ppm_y); + + error = input_mt_init_slots(input, MIP4_MAX_FINGERS, INPUT_MT_DIRECT); + if (error) + return error; + + i2c_set_clientdata(client, ts); + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, mip4_interrupt, + IRQF_ONESHOT, MIP4_DEVICE_NAME, ts); + if (error) { + dev_err(&client->dev, + "Failed to request interrupt %d: %d\n", + client->irq, error); + return error; + } + + disable_irq(client->irq); + + error = input_register_device(input); + if (error) { + dev_err(&client->dev, + "Failed to register input device: %d\n", error); + return error; + } + + error = sysfs_create_group(&client->dev.kobj, &mip4_attr_group); + if (error) { + dev_err(&client->dev, + "Failed to create sysfs attribute group: %d\n", error); + return error; + } + + error = devm_add_action(&client->dev, mip4_sysfs_remove, ts); + if (error) { + mip4_sysfs_remove(ts); + dev_err(&client->dev, + "Failed to install sysfs remoce action: %d\n", error); + return error; + } + + return 0; +} + +static int __maybe_unused mip4_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + struct input_dev *input = ts->input; + + mutex_lock(&input->mutex); + + if (device_may_wakeup(dev)) + ts->wake_irq_enabled = enable_irq_wake(client->irq) == 0; + else if (input->users) + mip4_disable(ts); + + mutex_unlock(&input->mutex); + + return 0; +} + +static int __maybe_unused mip4_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + struct input_dev *input = ts->input; + + mutex_lock(&input->mutex); + + if (ts->wake_irq_enabled) + disable_irq_wake(client->irq); + else if (input->users) + mip4_enable(ts); + + mutex_unlock(&input->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(mip4_pm_ops, mip4_suspend, mip4_resume); + +#ifdef CONFIG_OF +static const struct of_device_id mip4_of_match[] = { + { .compatible = "melfas,"MIP4_DEVICE_NAME, }, + { }, +}; +MODULE_DEVICE_TABLE(of, mip4_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id mip4_acpi_match[] = { + { "MLFS0000", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, mip4_acpi_match); +#endif + +static const struct i2c_device_id mip4_i2c_ids[] = { + { MIP4_DEVICE_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, mip4_i2c_ids); + +static struct i2c_driver mip4_driver = { + .id_table = mip4_i2c_ids, + .probe = mip4_probe, + .driver = { + .name = MIP4_DEVICE_NAME, + .of_match_table = of_match_ptr(mip4_of_match), + .acpi_match_table = ACPI_PTR(mip4_acpi_match), + .pm = &mip4_pm_ops, + }, +}; +module_i2c_driver(mip4_driver); + +MODULE_DESCRIPTION("MELFAS MIP4 Touchscreen"); +MODULE_VERSION("2016.03.03"); +MODULE_AUTHOR("Sangwon Jee <jeesw@melfas.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c index 23a354a392ae..0e3fc419a3cf 100644 --- a/drivers/input/touchscreen/pcap_ts.c +++ b/drivers/input/touchscreen/pcap_ts.c @@ -87,7 +87,7 @@ static void pcap_ts_read_xy(void *data, u16 res[2]) static void pcap_ts_work(struct work_struct *work) { - struct delayed_work *dw = container_of(work, struct delayed_work, work); + struct delayed_work *dw = to_delayed_work(work); struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work); u8 ch[2]; diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 4b961ad9f0b5..09523a3d3f23 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -38,6 +38,8 @@ struct pixcir_i2c_ts_data { struct input_dev *input; struct gpio_desc *gpio_attb; struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_enable; + struct gpio_desc *gpio_wake; const struct pixcir_i2c_chip_data *chip; int max_fingers; /* Max fingers supported in this instance */ bool running; @@ -208,6 +210,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts, struct device *dev = &ts->client->dev; int ret; + if (mode == PIXCIR_POWER_ACTIVE || mode == PIXCIR_POWER_IDLE) { + if (ts->gpio_wake) + gpiod_set_value_cansleep(ts->gpio_wake, 1); + } + ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE); if (ret < 0) { dev_err(dev, "%s: can't read reg 0x%x : %d\n", @@ -228,6 +235,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts, return ret; } + if (mode == PIXCIR_POWER_HALT) { + if (ts->gpio_wake) + gpiod_set_value_cansleep(ts->gpio_wake, 0); + } + return 0; } @@ -302,6 +314,11 @@ static int pixcir_start(struct pixcir_i2c_ts_data *ts) struct device *dev = &ts->client->dev; int error; + if (ts->gpio_enable) { + gpiod_set_value_cansleep(ts->gpio_enable, 1); + msleep(100); + } + /* LEVEL_TOUCH interrupt with active low polarity */ error = pixcir_set_int_mode(ts, PIXCIR_INT_LEVEL_TOUCH, 0); if (error) { @@ -343,6 +360,9 @@ static int pixcir_stop(struct pixcir_i2c_ts_data *ts) /* Wait till running ISR is complete */ synchronize_irq(ts->client->irq); + if (ts->gpio_enable) + gpiod_set_value_cansleep(ts->gpio_enable, 0); + return 0; } @@ -534,6 +554,27 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, return error; } + tsdata->gpio_wake = devm_gpiod_get_optional(dev, "wake", + GPIOD_OUT_HIGH); + if (IS_ERR(tsdata->gpio_wake)) { + error = PTR_ERR(tsdata->gpio_wake); + if (error != -EPROBE_DEFER) + dev_err(dev, "Failed to get wake gpio: %d\n", error); + return error; + } + + tsdata->gpio_enable = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(tsdata->gpio_enable)) { + error = PTR_ERR(tsdata->gpio_enable); + if (error != -EPROBE_DEFER) + dev_err(dev, "Failed to get enable gpio: %d\n", error); + return error; + } + + if (tsdata->gpio_enable) + msleep(100); + error = devm_request_threaded_irq(dev, client->irq, NULL, pixcir_ts_isr, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, tsdata); diff --git a/drivers/input/touchscreen/rohm_bu21023.c b/drivers/input/touchscreen/rohm_bu21023.c index ba6024f93469..611156a2ef80 100644 --- a/drivers/input/touchscreen/rohm_bu21023.c +++ b/drivers/input/touchscreen/rohm_bu21023.c @@ -725,7 +725,7 @@ static int rohm_ts_load_firmware(struct i2c_client *client, break; error = -EIO; - } while (++retry >= FIRMWARE_RETRY_MAX); + } while (++retry <= FIRMWARE_RETRY_MAX); out: error2 = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL); diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index e414d43e5159..2a78e27b4495 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -63,6 +63,37 @@ #define STMPE_TS_NAME "stmpe-ts" #define XY_MASK 0xfff +/** + * struct stmpe_touch - stmpe811 touch screen controller state + * @stmpe: pointer back to STMPE MFD container + * @idev: registered input device + * @work: a work item used to scan the device + * @dev: a pointer back to the MFD cell struct device* + * @sample_time: ADC converstion time in number of clock. + * (0 -> 36 clocks, 1 -> 44 clocks, 2 -> 56 clocks, 3 -> 64 clocks, + * 4 -> 80 clocks, 5 -> 96 clocks, 6 -> 144 clocks), + * recommended is 4. + * @mod_12b: ADC Bit mode (0 -> 10bit ADC, 1 -> 12bit ADC) + * @ref_sel: ADC reference source + * (0 -> internal reference, 1 -> external reference) + * @adc_freq: ADC Clock speed + * (0 -> 1.625 MHz, 1 -> 3.25 MHz, 2 || 3 -> 6.5 MHz) + * @ave_ctrl: Sample average control + * (0 -> 1 sample, 1 -> 2 samples, 2 -> 4 samples, 3 -> 8 samples) + * @touch_det_delay: Touch detect interrupt delay + * (0 -> 10 us, 1 -> 50 us, 2 -> 100 us, 3 -> 500 us, + * 4-> 1 ms, 5 -> 5 ms, 6 -> 10 ms, 7 -> 50 ms) + * recommended is 3 + * @settling: Panel driver settling time + * (0 -> 10 us, 1 -> 100 us, 2 -> 500 us, 3 -> 1 ms, + * 4 -> 5 ms, 5 -> 10 ms, 6 for 50 ms, 7 -> 100 ms) + * recommended is 2 + * @fraction_z: Length of the fractional part in z + * (fraction_z ([0..7]) = Count of the fractional part) + * recommended is 7 + * @i_drive: current limit value of the touchscreen drivers + * (0 -> 20 mA typical 35 mA max, 1 -> 50 mA typical 80 mA max) + */ struct stmpe_touch { struct stmpe *stmpe; struct input_dev *idev; diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 191a1b87895f..a21a07c3ab6d 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -273,8 +273,6 @@ static irqreturn_t titsc_irq(int irq, void *dev) status = titsc_readl(ts_dev, REG_RAWIRQSTATUS); if (status & IRQENB_HW_PEN) { ts_dev->pen_down = true; - titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00); - titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); irqclr |= IRQENB_HW_PEN; } diff --git a/drivers/input/touchscreen/ts4800-ts.c b/drivers/input/touchscreen/ts4800-ts.c new file mode 100644 index 000000000000..3c3dd78303be --- /dev/null +++ b/drivers/input/touchscreen/ts4800-ts.c @@ -0,0 +1,216 @@ +/* + * Touchscreen driver for the TS-4800 board + * + * Copyright (c) 2015 - Savoir-faire Linux + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/bitops.h> +#include <linux/input.h> +#include <linux/input-polldev.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +/* polling interval in ms */ +#define POLL_INTERVAL 3 + +#define DEBOUNCE_COUNT 1 + +/* sensor values are 12-bit wide */ +#define MAX_12BIT ((1 << 12) - 1) + +#define PENDOWN_MASK 0x1 + +#define X_OFFSET 0x0 +#define Y_OFFSET 0x2 + +struct ts4800_ts { + struct input_polled_dev *poll_dev; + struct device *dev; + char phys[32]; + + void __iomem *base; + struct regmap *regmap; + unsigned int reg; + unsigned int bit; + + bool pendown; + int debounce; +}; + +static void ts4800_ts_open(struct input_polled_dev *dev) +{ + struct ts4800_ts *ts = dev->private; + int ret; + + ts->pendown = false; + ts->debounce = DEBOUNCE_COUNT; + + ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit); + if (ret) + dev_warn(ts->dev, "Failed to enable touchscreen\n"); +} + +static void ts4800_ts_close(struct input_polled_dev *dev) +{ + struct ts4800_ts *ts = dev->private; + int ret; + + ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0); + if (ret) + dev_warn(ts->dev, "Failed to disable touchscreen\n"); + +} + +static void ts4800_ts_poll(struct input_polled_dev *dev) +{ + struct input_dev *input_dev = dev->input; + struct ts4800_ts *ts = dev->private; + u16 last_x = readw(ts->base + X_OFFSET); + u16 last_y = readw(ts->base + Y_OFFSET); + bool pendown = last_x & PENDOWN_MASK; + + if (pendown) { + if (ts->debounce) { + ts->debounce--; + return; + } + + if (!ts->pendown) { + input_report_key(input_dev, BTN_TOUCH, 1); + ts->pendown = true; + } + + last_x = ((~last_x) >> 4) & MAX_12BIT; + last_y = ((~last_y) >> 4) & MAX_12BIT; + + input_report_abs(input_dev, ABS_X, last_x); + input_report_abs(input_dev, ABS_Y, last_y); + input_sync(input_dev); + } else if (ts->pendown) { + ts->pendown = false; + ts->debounce = DEBOUNCE_COUNT; + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); + } +} + +static int ts4800_parse_dt(struct platform_device *pdev, + struct ts4800_ts *ts) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *syscon_np; + u32 reg, bit; + int error; + + syscon_np = of_parse_phandle(np, "syscon", 0); + if (!syscon_np) { + dev_err(dev, "no syscon property\n"); + return -ENODEV; + } + + error = of_property_read_u32_index(np, "syscon", 1, ®); + if (error < 0) { + dev_err(dev, "no offset in syscon\n"); + return error; + } + + ts->reg = reg; + + error = of_property_read_u32_index(np, "syscon", 2, &bit); + if (error < 0) { + dev_err(dev, "no bit in syscon\n"); + return error; + } + + ts->bit = BIT(bit); + + ts->regmap = syscon_node_to_regmap(syscon_np); + if (IS_ERR(ts->regmap)) { + dev_err(dev, "cannot get parent's regmap\n"); + return PTR_ERR(ts->regmap); + } + + return 0; +} + +static int ts4800_ts_probe(struct platform_device *pdev) +{ + struct input_polled_dev *poll_dev; + struct ts4800_ts *ts; + struct resource *res; + int error; + + ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + error = ts4800_parse_dt(pdev, ts); + if (error) + return error; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ts->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ts->base)) + return PTR_ERR(ts->base); + + poll_dev = devm_input_allocate_polled_device(&pdev->dev); + if (!poll_dev) + return -ENOMEM; + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev)); + ts->poll_dev = poll_dev; + ts->dev = &pdev->dev; + + poll_dev->private = ts; + poll_dev->poll_interval = POLL_INTERVAL; + poll_dev->open = ts4800_ts_open; + poll_dev->close = ts4800_ts_close; + poll_dev->poll = ts4800_ts_poll; + + poll_dev->input->name = "TS-4800 Touchscreen"; + poll_dev->input->phys = ts->phys; + + input_set_capability(poll_dev->input, EV_KEY, BTN_TOUCH); + input_set_abs_params(poll_dev->input, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(poll_dev->input, ABS_Y, 0, MAX_12BIT, 0, 0); + + error = input_register_polled_device(poll_dev); + if (error) { + dev_err(&pdev->dev, + "Unabled to register polled input device (%d)\n", + error); + return error; + } + + return 0; +} + +static const struct of_device_id ts4800_ts_of_match[] = { + { .compatible = "technologic,ts4800-ts", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ts4800_ts_of_match); + +static struct platform_driver ts4800_ts_driver = { + .driver = { + .name = "ts4800-ts", + .of_match_table = ts4800_ts_of_match, + }, + .probe = ts4800_ts_probe, +}; +module_platform_driver(ts4800_ts_driver); + +MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>"); +MODULE_DESCRIPTION("TS-4800 Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:ts4800_ts"); diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 2792ca397dd0..bab3c6acf6a2 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -80,7 +80,8 @@ struct w8001_touch_query { */ struct w8001 { - struct input_dev *dev; + struct input_dev *pen_dev; + struct input_dev *touch_dev; struct serio *serio; struct completion cmd_done; int id; @@ -95,7 +96,10 @@ struct w8001 { u16 max_touch_y; u16 max_pen_x; u16 max_pen_y; - char name[64]; + char pen_name[64]; + char touch_name[64]; + int open_count; + struct mutex mutex; }; static void parse_pen_data(u8 *data, struct w8001_coord *coord) @@ -141,7 +145,7 @@ static void scale_touch_coordinates(struct w8001 *w8001, static void parse_multi_touch(struct w8001 *w8001) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->touch_dev; unsigned char *data = w8001->data; unsigned int x, y; int i; @@ -151,7 +155,6 @@ static void parse_multi_touch(struct w8001 *w8001) bool touch = data[0] & (1 << i); input_mt_slot(dev, i); - input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch); if (touch) { x = (data[6 * i + 1] << 7) | data[6 * i + 2]; y = (data[6 * i + 3] << 7) | data[6 * i + 4]; @@ -207,7 +210,7 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query) static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->pen_dev; /* * We have 1 bit for proximity (rdy) and 3 bits for tip, side, @@ -233,11 +236,6 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) break; case BTN_TOOL_FINGER: - input_report_key(dev, BTN_TOUCH, 0); - input_report_key(dev, BTN_TOOL_FINGER, 0); - input_sync(dev); - /* fall through */ - case KEY_RESERVED: w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; break; @@ -261,7 +259,7 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->touch_dev; unsigned int x = coord->x; unsigned int y = coord->y; @@ -271,7 +269,6 @@ static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord) input_report_abs(dev, ABS_X, x); input_report_abs(dev, ABS_Y, y); input_report_key(dev, BTN_TOUCH, coord->tsw); - input_report_key(dev, BTN_TOOL_FINGER, coord->tsw); input_sync(dev); @@ -369,22 +366,36 @@ static int w8001_command(struct w8001 *w8001, unsigned char command, static int w8001_open(struct input_dev *dev) { struct w8001 *w8001 = input_get_drvdata(dev); + int err; - return w8001_command(w8001, W8001_CMD_START, false); + err = mutex_lock_interruptible(&w8001->mutex); + if (err) + return err; + + if (w8001->open_count++ == 0) { + err = w8001_command(w8001, W8001_CMD_START, false); + if (err) + w8001->open_count--; + } + + mutex_unlock(&w8001->mutex); + return err; } static void w8001_close(struct input_dev *dev) { struct w8001 *w8001 = input_get_drvdata(dev); - w8001_command(w8001, W8001_CMD_STOP, false); + mutex_lock(&w8001->mutex); + + if (--w8001->open_count == 0) + w8001_command(w8001, W8001_CMD_STOP, false); + + mutex_unlock(&w8001->mutex); } -static int w8001_setup(struct w8001 *w8001) +static int w8001_detect(struct w8001 *w8001) { - struct input_dev *dev = w8001->dev; - struct w8001_coord coord; - struct w8001_touch_query touch; int error; error = w8001_command(w8001, W8001_CMD_STOP, false); @@ -393,105 +404,145 @@ static int w8001_setup(struct w8001 *w8001) msleep(250); /* wait 250ms before querying the device */ - dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name)); + return 0; +} - __set_bit(INPUT_PROP_DIRECT, dev->propbit); +static int w8001_setup_pen(struct w8001 *w8001, char *basename, + size_t basename_sz) +{ + struct input_dev *dev = w8001->pen_dev; + struct w8001_coord coord; + int error; /* penabled? */ error = w8001_command(w8001, W8001_CMD_QUERY, true); - if (!error) { - __set_bit(BTN_TOUCH, dev->keybit); - __set_bit(BTN_TOOL_PEN, dev->keybit); - __set_bit(BTN_TOOL_RUBBER, dev->keybit); - __set_bit(BTN_STYLUS, dev->keybit); - __set_bit(BTN_STYLUS2, dev->keybit); - - parse_pen_data(w8001->response, &coord); - w8001->max_pen_x = coord.x; - w8001->max_pen_y = coord.y; - - input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); - input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); - input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION); - input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION); - input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); - if (coord.tilt_x && coord.tilt_y) { - input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); - input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); - } - w8001->id = 0x90; - strlcat(w8001->name, " Penabled", sizeof(w8001->name)); + if (error) + return error; + + __set_bit(EV_KEY, dev->evbit); + __set_bit(EV_ABS, dev->evbit); + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_PEN, dev->keybit); + __set_bit(BTN_TOOL_RUBBER, dev->keybit); + __set_bit(BTN_STYLUS, dev->keybit); + __set_bit(BTN_STYLUS2, dev->keybit); + __set_bit(INPUT_PROP_DIRECT, dev->propbit); + + parse_pen_data(w8001->response, &coord); + w8001->max_pen_x = coord.x; + w8001->max_pen_y = coord.y; + + input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); + input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION); + input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION); + input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); + if (coord.tilt_x && coord.tilt_y) { + input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); + input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); } + w8001->id = 0x90; + strlcat(basename, " Penabled", basename_sz); + + return 0; +} + +static int w8001_setup_touch(struct w8001 *w8001, char *basename, + size_t basename_sz) +{ + struct input_dev *dev = w8001->touch_dev; + struct w8001_touch_query touch; + int error; + + /* Touch enabled? */ error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); - + if (error) + return error; /* * Some non-touch devices may reply to the touch query. But their * second byte is empty, which indicates touch is not supported. */ - if (!error && w8001->response[1]) { - __set_bit(BTN_TOUCH, dev->keybit); - __set_bit(BTN_TOOL_FINGER, dev->keybit); - - parse_touchquery(w8001->response, &touch); - w8001->max_touch_x = touch.x; - w8001->max_touch_y = touch.y; - - if (w8001->max_pen_x && w8001->max_pen_y) { - /* if pen is supported scale to pen maximum */ - touch.x = w8001->max_pen_x; - touch.y = w8001->max_pen_y; - touch.panel_res = W8001_PEN_RESOLUTION; - } + if (!w8001->response[1]) + return -ENXIO; - input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0); - input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0); - input_abs_set_res(dev, ABS_X, touch.panel_res); - input_abs_set_res(dev, ABS_Y, touch.panel_res); - - switch (touch.sensor_id) { - case 0: - case 2: - w8001->pktlen = W8001_PKTLEN_TOUCH93; - w8001->id = 0x93; - strlcat(w8001->name, " 1FG", sizeof(w8001->name)); - break; + __set_bit(EV_KEY, dev->evbit); + __set_bit(EV_ABS, dev->evbit); + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(INPUT_PROP_DIRECT, dev->propbit); - case 1: - case 3: - case 4: - w8001->pktlen = W8001_PKTLEN_TOUCH9A; - strlcat(w8001->name, " 1FG", sizeof(w8001->name)); - w8001->id = 0x9a; - break; + parse_touchquery(w8001->response, &touch); + w8001->max_touch_x = touch.x; + w8001->max_touch_y = touch.y; - case 5: - w8001->pktlen = W8001_PKTLEN_TOUCH2FG; - - input_mt_init_slots(dev, 2, 0); - input_set_abs_params(dev, ABS_MT_POSITION_X, - 0, touch.x, 0, 0); - input_set_abs_params(dev, ABS_MT_POSITION_Y, - 0, touch.y, 0, 0); - input_set_abs_params(dev, ABS_MT_TOOL_TYPE, - 0, MT_TOOL_MAX, 0, 0); - - strlcat(w8001->name, " 2FG", sizeof(w8001->name)); - if (w8001->max_pen_x && w8001->max_pen_y) - w8001->id = 0xE3; - else - w8001->id = 0xE2; - break; - } + if (w8001->max_pen_x && w8001->max_pen_y) { + /* if pen is supported scale to pen maximum */ + touch.x = w8001->max_pen_x; + touch.y = w8001->max_pen_y; + touch.panel_res = W8001_PEN_RESOLUTION; + } + + input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0); + input_abs_set_res(dev, ABS_X, touch.panel_res); + input_abs_set_res(dev, ABS_Y, touch.panel_res); + + switch (touch.sensor_id) { + case 0: + case 2: + w8001->pktlen = W8001_PKTLEN_TOUCH93; + w8001->id = 0x93; + strlcat(basename, " 1FG", basename_sz); + break; + + case 1: + case 3: + case 4: + w8001->pktlen = W8001_PKTLEN_TOUCH9A; + strlcat(basename, " 1FG", basename_sz); + w8001->id = 0x9a; + break; + + case 5: + w8001->pktlen = W8001_PKTLEN_TOUCH2FG; + + __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); + input_mt_init_slots(dev, 2, 0); + input_set_abs_params(dev, ABS_MT_POSITION_X, + 0, touch.x, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, + 0, touch.y, 0, 0); + + strlcat(basename, " 2FG", basename_sz); + if (w8001->max_pen_x && w8001->max_pen_y) + w8001->id = 0xE3; + else + w8001->id = 0xE2; + break; } - strlcat(w8001->name, " Touchscreen", sizeof(w8001->name)); + strlcat(basename, " Touchscreen", basename_sz); return 0; } +static void w8001_set_devdata(struct input_dev *dev, struct w8001 *w8001, + struct serio *serio) +{ + dev->phys = w8001->phys; + dev->id.bustype = BUS_RS232; + dev->id.product = w8001->id; + dev->id.vendor = 0x056a; + dev->id.version = 0x0100; + dev->open = w8001_open; + dev->close = w8001_close; + + dev->dev.parent = &serio->dev; + + input_set_drvdata(dev, w8001); +} + /* * w8001_disconnect() is the opposite of w8001_connect() */ @@ -502,7 +553,10 @@ static void w8001_disconnect(struct serio *serio) serio_close(serio); - input_unregister_device(w8001->dev); + if (w8001->pen_dev) + input_unregister_device(w8001->pen_dev); + if (w8001->touch_dev) + input_unregister_device(w8001->touch_dev); kfree(w8001); serio_set_drvdata(serio, NULL); @@ -517,18 +571,23 @@ static void w8001_disconnect(struct serio *serio) static int w8001_connect(struct serio *serio, struct serio_driver *drv) { struct w8001 *w8001; - struct input_dev *input_dev; - int err; + struct input_dev *input_dev_pen; + struct input_dev *input_dev_touch; + char basename[64]; + int err, err_pen, err_touch; w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!w8001 || !input_dev) { + input_dev_pen = input_allocate_device(); + input_dev_touch = input_allocate_device(); + if (!w8001 || !input_dev_pen || !input_dev_touch) { err = -ENOMEM; goto fail1; } w8001->serio = serio; - w8001->dev = input_dev; + w8001->pen_dev = input_dev_pen; + w8001->touch_dev = input_dev_touch; + mutex_init(&w8001->mutex); init_completion(&w8001->cmd_done); snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); @@ -537,35 +596,67 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) if (err) goto fail2; - err = w8001_setup(w8001); + err = w8001_detect(w8001); if (err) goto fail3; - input_dev->name = w8001->name; - input_dev->phys = w8001->phys; - input_dev->id.product = w8001->id; - input_dev->id.bustype = BUS_RS232; - input_dev->id.vendor = 0x056a; - input_dev->id.version = 0x0100; - input_dev->dev.parent = &serio->dev; + /* For backwards-compatibility we compose the basename based on + * capabilities and then just append the tool type + */ + strlcpy(basename, "Wacom Serial", sizeof(basename)); + + err_pen = w8001_setup_pen(w8001, basename, sizeof(basename)); + err_touch = w8001_setup_touch(w8001, basename, sizeof(basename)); + if (err_pen && err_touch) { + err = -ENXIO; + goto fail3; + } + + if (!err_pen) { + strlcpy(w8001->pen_name, basename, sizeof(w8001->pen_name)); + strlcat(w8001->pen_name, " Pen", sizeof(w8001->pen_name)); + input_dev_pen->name = w8001->pen_name; - input_dev->open = w8001_open; - input_dev->close = w8001_close; + w8001_set_devdata(input_dev_pen, w8001, serio); - input_set_drvdata(input_dev, w8001); + err = input_register_device(w8001->pen_dev); + if (err) + goto fail3; + } else { + input_free_device(input_dev_pen); + input_dev_pen = NULL; + w8001->pen_dev = NULL; + } - err = input_register_device(w8001->dev); - if (err) - goto fail3; + if (!err_touch) { + strlcpy(w8001->touch_name, basename, sizeof(w8001->touch_name)); + strlcat(w8001->touch_name, " Finger", + sizeof(w8001->touch_name)); + input_dev_touch->name = w8001->touch_name; + + w8001_set_devdata(input_dev_touch, w8001, serio); + + err = input_register_device(w8001->touch_dev); + if (err) + goto fail4; + } else { + input_free_device(input_dev_touch); + input_dev_touch = NULL; + w8001->touch_dev = NULL; + } return 0; +fail4: + if (w8001->pen_dev) + input_unregister_device(w8001->pen_dev); fail3: serio_close(serio); fail2: serio_set_drvdata(serio, NULL); fail1: - input_free_device(input_dev); + input_free_device(input_dev_pen); + input_free_device(input_dev_touch); kfree(w8001); return err; } |