Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input layer fixes from Dmitry Torokhov: "Second round of updates for the input subsystem. Mostly small fixups to the code merged in the first round (atmel_mxt_ts, wacom) but also a smallish patch to xbox driver to support Xbox One controllers and a patch to better handle Synaptics profile sensors found in Cr-48 Chromebooks that should not affect any other devices" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: Input: edt-ft5x06 - remove superfluous assignment Input: xpad - add support for Xbox One controllers Input: atmel_mxt_ts - fix a few issues reported by Coverity Input: atmel_mxt_ts - split config update a bit Input: atmel_mxt_ts - simplify mxt_initialize a bit Input: joystick - use get_cycles on ARMv8 Input: wacom - fix compiler warning if !CONFIG_PM Input: cap1106 - allow changing key mapping from userspace Input: synaptics - use firmware data for Cr-48 Input: synaptics - properly initialize slots for semi-MT Input: MT - make slot cleanup callable outside mt_sync_frame() Input: atmel_mxt_ts - mXT224 DMA quirk was fixed in firmware v2.0.AA
This commit is contained in:
commit
2f39691f31
@ -1416,6 +1416,7 @@ static void wacom_remove(struct hid_device *hdev)
|
||||
kfree(wacom);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int wacom_resume(struct hid_device *hdev)
|
||||
{
|
||||
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||
@ -1436,6 +1437,7 @@ static int wacom_reset_resume(struct hid_device *hdev)
|
||||
{
|
||||
return wacom_resume(hdev);
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static struct hid_driver wacom_driver = {
|
||||
.name = "wacom",
|
||||
|
@ -236,6 +236,31 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_report_pointer_emulation);
|
||||
|
||||
/**
|
||||
* input_mt_drop_unused() - Inactivate slots not seen in this frame
|
||||
* @dev: input device with allocated MT slots
|
||||
*
|
||||
* Lift all slots not seen since the last call to this function.
|
||||
*/
|
||||
void input_mt_drop_unused(struct input_dev *dev)
|
||||
{
|
||||
struct input_mt *mt = dev->mt;
|
||||
int i;
|
||||
|
||||
if (!mt)
|
||||
return;
|
||||
|
||||
for (i = 0; i < mt->num_slots; i++) {
|
||||
if (!input_mt_is_used(mt, &mt->slots[i])) {
|
||||
input_mt_slot(dev, i);
|
||||
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
|
||||
}
|
||||
}
|
||||
|
||||
mt->frame++;
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_drop_unused);
|
||||
|
||||
/**
|
||||
* input_mt_sync_frame() - synchronize mt frame
|
||||
* @dev: input device with allocated MT slots
|
||||
@ -247,27 +272,18 @@ EXPORT_SYMBOL(input_mt_report_pointer_emulation);
|
||||
void input_mt_sync_frame(struct input_dev *dev)
|
||||
{
|
||||
struct input_mt *mt = dev->mt;
|
||||
struct input_mt_slot *s;
|
||||
bool use_count = false;
|
||||
|
||||
if (!mt)
|
||||
return;
|
||||
|
||||
if (mt->flags & INPUT_MT_DROP_UNUSED) {
|
||||
for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
|
||||
if (input_mt_is_used(mt, s))
|
||||
continue;
|
||||
input_mt_slot(dev, s - mt->slots);
|
||||
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
|
||||
}
|
||||
}
|
||||
if (mt->flags & INPUT_MT_DROP_UNUSED)
|
||||
input_mt_drop_unused(dev);
|
||||
|
||||
if ((mt->flags & INPUT_MT_POINTER) && !(mt->flags & INPUT_MT_SEMI_MT))
|
||||
use_count = true;
|
||||
|
||||
input_mt_report_pointer_emulation(dev, use_count);
|
||||
|
||||
mt->frame++;
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_sync_frame);
|
||||
|
||||
|
@ -158,7 +158,7 @@ static unsigned int get_time_pit(void)
|
||||
#define GET_TIME(x) rdtscl(x)
|
||||
#define DELTA(x,y) ((y)-(x))
|
||||
#define TIME_NAME "TSC"
|
||||
#elif defined(__alpha__) || defined(CONFIG_MN10300) || defined(CONFIG_ARM) || defined(CONFIG_TILE)
|
||||
#elif defined(__alpha__) || defined(CONFIG_MN10300) || defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_TILE)
|
||||
#define GET_TIME(x) do { x = get_cycles(); } while (0)
|
||||
#define DELTA(x,y) ((y)-(x))
|
||||
#define TIME_NAME "get_cycles"
|
||||
|
@ -95,7 +95,8 @@
|
||||
#define XTYPE_XBOX 0
|
||||
#define XTYPE_XBOX360 1
|
||||
#define XTYPE_XBOX360W 2
|
||||
#define XTYPE_UNKNOWN 3
|
||||
#define XTYPE_XBOXONE 3
|
||||
#define XTYPE_UNKNOWN 4
|
||||
|
||||
static bool dpad_to_buttons;
|
||||
module_param(dpad_to_buttons, bool, S_IRUGO);
|
||||
@ -121,6 +122,7 @@ static const struct xpad_device {
|
||||
{ 0x045e, 0x0287, "Microsoft Xbox Controller S", 0, XTYPE_XBOX },
|
||||
{ 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, 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 },
|
||||
@ -231,10 +233,12 @@ static const signed short xpad_abs_triggers[] = {
|
||||
-1
|
||||
};
|
||||
|
||||
/* Xbox 360 has a vendor-specific class, so we cannot match it with only
|
||||
/*
|
||||
* Xbox 360 has a vendor-specific class, so we cannot match it with only
|
||||
* USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we
|
||||
* match against vendor id as well. Wired Xbox 360 devices have protocol 1,
|
||||
* wireless controllers have protocol 129. */
|
||||
* wireless controllers have protocol 129.
|
||||
*/
|
||||
#define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \
|
||||
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \
|
||||
.idVendor = (vend), \
|
||||
@ -245,9 +249,20 @@ static const signed short xpad_abs_triggers[] = {
|
||||
{ XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \
|
||||
{ XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) }
|
||||
|
||||
/* The Xbox One controller uses subclass 71 and protocol 208. */
|
||||
#define XPAD_XBOXONE_VENDOR_PROTOCOL(vend, pr) \
|
||||
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \
|
||||
.idVendor = (vend), \
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
|
||||
.bInterfaceSubClass = 71, \
|
||||
.bInterfaceProtocol = (pr)
|
||||
#define XPAD_XBOXONE_VENDOR(vend) \
|
||||
{ XPAD_XBOXONE_VENDOR_PROTOCOL(vend, 208) }
|
||||
|
||||
static struct usb_device_id xpad_table[] = {
|
||||
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */
|
||||
XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */
|
||||
XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One controllers */
|
||||
XPAD_XBOX360_VENDOR(0x046d), /* Logitech X-Box 360 style controllers */
|
||||
XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */
|
||||
{ USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */
|
||||
@ -278,12 +293,10 @@ struct usb_xpad {
|
||||
struct urb *bulk_out;
|
||||
unsigned char *bdata;
|
||||
|
||||
#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS)
|
||||
struct urb *irq_out; /* urb for interrupt out report */
|
||||
unsigned char *odata; /* output data */
|
||||
dma_addr_t odata_dma;
|
||||
struct mutex odata_mutex;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_JOYSTICK_XPAD_LEDS)
|
||||
struct xpad_led *led;
|
||||
@ -470,6 +483,105 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha
|
||||
xpad360_process_packet(xpad, cmd, &data[4]);
|
||||
}
|
||||
|
||||
/*
|
||||
* xpadone_process_buttons
|
||||
*
|
||||
* Process a button update packet from an Xbox one controller.
|
||||
*/
|
||||
static void xpadone_process_buttons(struct usb_xpad *xpad,
|
||||
struct input_dev *dev,
|
||||
unsigned char *data)
|
||||
{
|
||||
/* menu/view buttons */
|
||||
input_report_key(dev, BTN_START, data[4] & 0x04);
|
||||
input_report_key(dev, BTN_SELECT, data[4] & 0x08);
|
||||
|
||||
/* buttons A,B,X,Y */
|
||||
input_report_key(dev, BTN_A, data[4] & 0x10);
|
||||
input_report_key(dev, BTN_B, data[4] & 0x20);
|
||||
input_report_key(dev, BTN_X, data[4] & 0x40);
|
||||
input_report_key(dev, BTN_Y, data[4] & 0x80);
|
||||
|
||||
/* digital pad */
|
||||
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
|
||||
/* dpad as buttons (left, right, up, down) */
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[5] & 0x04);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[5] & 0x08);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[5] & 0x01);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[5] & 0x02);
|
||||
} else {
|
||||
input_report_abs(dev, ABS_HAT0X,
|
||||
!!(data[5] & 0x08) - !!(data[5] & 0x04));
|
||||
input_report_abs(dev, ABS_HAT0Y,
|
||||
!!(data[5] & 0x02) - !!(data[5] & 0x01));
|
||||
}
|
||||
|
||||
/* TL/TR */
|
||||
input_report_key(dev, BTN_TL, data[5] & 0x10);
|
||||
input_report_key(dev, BTN_TR, data[5] & 0x20);
|
||||
|
||||
/* stick press left/right */
|
||||
input_report_key(dev, BTN_THUMBL, data[5] & 0x40);
|
||||
input_report_key(dev, BTN_THUMBR, data[5] & 0x80);
|
||||
|
||||
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
|
||||
/* left stick */
|
||||
input_report_abs(dev, ABS_X,
|
||||
(__s16) le16_to_cpup((__le16 *)(data + 10)));
|
||||
input_report_abs(dev, ABS_Y,
|
||||
~(__s16) le16_to_cpup((__le16 *)(data + 12)));
|
||||
|
||||
/* right stick */
|
||||
input_report_abs(dev, ABS_RX,
|
||||
(__s16) le16_to_cpup((__le16 *)(data + 14)));
|
||||
input_report_abs(dev, ABS_RY,
|
||||
~(__s16) le16_to_cpup((__le16 *)(data + 16)));
|
||||
}
|
||||
|
||||
/* triggers left/right */
|
||||
if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
|
||||
input_report_key(dev, BTN_TL2,
|
||||
(__u16) le16_to_cpup((__le16 *)(data + 6)));
|
||||
input_report_key(dev, BTN_TR2,
|
||||
(__u16) le16_to_cpup((__le16 *)(data + 8)));
|
||||
} else {
|
||||
input_report_abs(dev, ABS_Z,
|
||||
(__u16) le16_to_cpup((__le16 *)(data + 6)));
|
||||
input_report_abs(dev, ABS_RZ,
|
||||
(__u16) le16_to_cpup((__le16 *)(data + 8)));
|
||||
}
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* xpadone_process_packet
|
||||
*
|
||||
* Completes a request by converting the data into events for the
|
||||
* input subsystem. This version is for the Xbox One controller.
|
||||
*
|
||||
* The report format was gleaned from
|
||||
* https://github.com/kylelemons/xbox/blob/master/xbox.go
|
||||
*/
|
||||
|
||||
static void xpadone_process_packet(struct usb_xpad *xpad,
|
||||
u16 cmd, unsigned char *data)
|
||||
{
|
||||
struct input_dev *dev = xpad->dev;
|
||||
|
||||
switch (data[0]) {
|
||||
case 0x20:
|
||||
xpadone_process_buttons(xpad, dev, data);
|
||||
break;
|
||||
|
||||
case 0x07:
|
||||
/* the xbox button has its own special report */
|
||||
input_report_key(dev, BTN_MODE, data[4] & 0x01);
|
||||
input_sync(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void xpad_irq_in(struct urb *urb)
|
||||
{
|
||||
struct usb_xpad *xpad = urb->context;
|
||||
@ -502,6 +614,9 @@ static void xpad_irq_in(struct urb *urb)
|
||||
case XTYPE_XBOX360W:
|
||||
xpad360w_process_packet(xpad, 0, xpad->idata);
|
||||
break;
|
||||
case XTYPE_XBOXONE:
|
||||
xpadone_process_packet(xpad, 0, xpad->idata);
|
||||
break;
|
||||
default:
|
||||
xpad_process_packet(xpad, 0, xpad->idata);
|
||||
}
|
||||
@ -535,7 +650,6 @@ static void xpad_bulk_out(struct urb *urb)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS)
|
||||
static void xpad_irq_out(struct urb *urb)
|
||||
{
|
||||
struct usb_xpad *xpad = urb->context;
|
||||
@ -573,6 +687,7 @@ exit:
|
||||
static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
|
||||
{
|
||||
struct usb_endpoint_descriptor *ep_irq_out;
|
||||
int ep_irq_out_idx;
|
||||
int error;
|
||||
|
||||
if (xpad->xtype == XTYPE_UNKNOWN)
|
||||
@ -593,7 +708,10 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
ep_irq_out = &intf->cur_altsetting->endpoint[1].desc;
|
||||
/* Xbox One controller has in/out endpoints swapped. */
|
||||
ep_irq_out_idx = xpad->xtype == XTYPE_XBOXONE ? 0 : 1;
|
||||
ep_irq_out = &intf->cur_altsetting->endpoint[ep_irq_out_idx].desc;
|
||||
|
||||
usb_fill_int_urb(xpad->irq_out, xpad->udev,
|
||||
usb_sndintpipe(xpad->udev, ep_irq_out->bEndpointAddress),
|
||||
xpad->odata, XPAD_PKT_LEN,
|
||||
@ -621,11 +739,6 @@ static void xpad_deinit_output(struct usb_xpad *xpad)
|
||||
xpad->odata, xpad->odata_dma);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) { return 0; }
|
||||
static void xpad_deinit_output(struct usb_xpad *xpad) {}
|
||||
static void xpad_stop_output(struct usb_xpad *xpad) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_JOYSTICK_XPAD_FF
|
||||
static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect)
|
||||
@ -692,7 +805,7 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect
|
||||
|
||||
static int xpad_init_ff(struct usb_xpad *xpad)
|
||||
{
|
||||
if (xpad->xtype == XTYPE_UNKNOWN)
|
||||
if (xpad->xtype == XTYPE_UNKNOWN || xpad->xtype == XTYPE_XBOXONE)
|
||||
return 0;
|
||||
|
||||
input_set_capability(xpad->dev, EV_FF, FF_RUMBLE);
|
||||
@ -801,6 +914,14 @@ static int xpad_open(struct input_dev *dev)
|
||||
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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -816,6 +937,7 @@ static void xpad_close(struct input_dev *dev)
|
||||
|
||||
static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
|
||||
{
|
||||
struct usb_xpad *xpad = input_get_drvdata(input_dev);
|
||||
set_bit(abs, input_dev->absbit);
|
||||
|
||||
switch (abs) {
|
||||
@ -827,7 +949,10 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
|
||||
break;
|
||||
case ABS_Z:
|
||||
case ABS_RZ: /* the triggers (if mapped to axes) */
|
||||
input_set_abs_params(input_dev, abs, 0, 255, 0, 0);
|
||||
if (xpad->xtype == XTYPE_XBOXONE)
|
||||
input_set_abs_params(input_dev, abs, 0, 1023, 0, 0);
|
||||
else
|
||||
input_set_abs_params(input_dev, abs, 0, 255, 0, 0);
|
||||
break;
|
||||
case ABS_HAT0X:
|
||||
case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */
|
||||
@ -842,6 +967,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
struct usb_xpad *xpad;
|
||||
struct input_dev *input_dev;
|
||||
struct usb_endpoint_descriptor *ep_irq_in;
|
||||
int ep_irq_in_idx;
|
||||
int i, error;
|
||||
|
||||
for (i = 0; xpad_device[i].idVendor; i++) {
|
||||
@ -850,6 +976,16 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
break;
|
||||
}
|
||||
|
||||
if (xpad_device[i].xtype == XTYPE_XBOXONE &&
|
||||
intf->cur_altsetting->desc.bInterfaceNumber != 0) {
|
||||
/*
|
||||
* The Xbox One controller lists three interfaces all with the
|
||||
* same interface class, subclass and protocol. Differentiate by
|
||||
* interface number.
|
||||
*/
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!xpad || !input_dev) {
|
||||
@ -920,7 +1056,8 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
__set_bit(xpad_common_btn[i], input_dev->keybit);
|
||||
|
||||
/* set up model-specific ones */
|
||||
if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W) {
|
||||
if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W ||
|
||||
xpad->xtype == XTYPE_XBOXONE) {
|
||||
for (i = 0; xpad360_btn[i] >= 0; i++)
|
||||
__set_bit(xpad360_btn[i], input_dev->keybit);
|
||||
} else {
|
||||
@ -933,7 +1070,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
__set_bit(xpad_btn_pad[i], input_dev->keybit);
|
||||
} else {
|
||||
for (i = 0; xpad_abs_pad[i] >= 0; i++)
|
||||
xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
|
||||
xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
|
||||
}
|
||||
|
||||
if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
|
||||
@ -956,7 +1093,10 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
if (error)
|
||||
goto fail5;
|
||||
|
||||
ep_irq_in = &intf->cur_altsetting->endpoint[0].desc;
|
||||
/* Xbox One controller has in/out endpoints swapped. */
|
||||
ep_irq_in_idx = xpad->xtype == XTYPE_XBOXONE ? 1 : 0;
|
||||
ep_irq_in = &intf->cur_altsetting->endpoint[ep_irq_in_idx].desc;
|
||||
|
||||
usb_fill_int_urb(xpad->irq_in, udev,
|
||||
usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),
|
||||
xpad->idata, XPAD_PKT_LEN, xpad_irq_in,
|
||||
|
@ -64,7 +64,7 @@ struct cap1106_priv {
|
||||
struct input_dev *idev;
|
||||
|
||||
/* config */
|
||||
unsigned int keycodes[CAP1106_NUM_CHN];
|
||||
unsigned short keycodes[CAP1106_NUM_CHN];
|
||||
};
|
||||
|
||||
static const struct reg_default cap1106_reg_defaults[] = {
|
||||
@ -272,6 +272,12 @@ static int cap1106_i2c_probe(struct i2c_client *i2c_client,
|
||||
for (i = 0; i < CAP1106_NUM_CHN; i++)
|
||||
__set_bit(priv->keycodes[i], priv->idev->keybit);
|
||||
|
||||
__clear_bit(KEY_RESERVED, priv->idev->keybit);
|
||||
|
||||
priv->idev->keycode = priv->keycodes;
|
||||
priv->idev->keycodesize = sizeof(priv->keycodes[0]);
|
||||
priv->idev->keycodemax = ARRAY_SIZE(priv->keycodes);
|
||||
|
||||
priv->idev->id.vendor = CAP1106_MANUFACTURER_ID;
|
||||
priv->idev->id.product = CAP1106_PRODUCT_ID;
|
||||
priv->idev->id.version = rev;
|
||||
|
@ -117,6 +117,9 @@ void synaptics_reset(struct psmouse *psmouse)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
|
||||
|
||||
static bool cr48_profile_sensor;
|
||||
|
||||
struct min_max_quirk {
|
||||
const char * const *pnp_ids;
|
||||
int x_min, x_max, y_min, y_max;
|
||||
@ -1152,6 +1155,42 @@ static void synaptics_image_sensor_process(struct psmouse *psmouse,
|
||||
priv->agm_pending = false;
|
||||
}
|
||||
|
||||
static void synaptics_profile_sensor_process(struct psmouse *psmouse,
|
||||
struct synaptics_hw_state *sgm,
|
||||
int num_fingers)
|
||||
{
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct synaptics_data *priv = psmouse->private;
|
||||
struct synaptics_hw_state *hw[2] = { sgm, &priv->agm };
|
||||
struct input_mt_pos pos[2];
|
||||
int slot[2], nsemi, i;
|
||||
|
||||
nsemi = clamp_val(num_fingers, 0, 2);
|
||||
|
||||
for (i = 0; i < nsemi; i++) {
|
||||
pos[i].x = hw[i]->x;
|
||||
pos[i].y = synaptics_invert_y(hw[i]->y);
|
||||
}
|
||||
|
||||
input_mt_assign_slots(dev, slot, pos, nsemi);
|
||||
|
||||
for (i = 0; i < nsemi; i++) {
|
||||
input_mt_slot(dev, slot[i]);
|
||||
input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
|
||||
input_report_abs(dev, ABS_MT_POSITION_X, pos[i].x);
|
||||
input_report_abs(dev, ABS_MT_POSITION_Y, pos[i].y);
|
||||
input_report_abs(dev, ABS_MT_PRESSURE, hw[i]->z);
|
||||
}
|
||||
|
||||
input_mt_drop_unused(dev);
|
||||
input_mt_report_pointer_emulation(dev, false);
|
||||
input_mt_report_finger_count(dev, num_fingers);
|
||||
|
||||
synaptics_report_buttons(psmouse, sgm);
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* called for each full received packet from the touchpad
|
||||
*/
|
||||
@ -1215,6 +1254,11 @@ static void synaptics_process_packet(struct psmouse *psmouse)
|
||||
finger_width = 0;
|
||||
}
|
||||
|
||||
if (cr48_profile_sensor) {
|
||||
synaptics_profile_sensor_process(psmouse, &hw, num_fingers);
|
||||
return;
|
||||
}
|
||||
|
||||
if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
|
||||
synaptics_report_semi_mt_data(dev, &hw, &priv->agm,
|
||||
num_fingers);
|
||||
@ -1360,6 +1404,9 @@ static void set_input_params(struct psmouse *psmouse,
|
||||
set_abs_position_params(dev, priv, ABS_X, ABS_Y);
|
||||
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
|
||||
|
||||
if (cr48_profile_sensor)
|
||||
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
|
||||
|
||||
if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
|
||||
set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
|
||||
ABS_MT_POSITION_Y);
|
||||
@ -1371,11 +1418,16 @@ static void set_input_params(struct psmouse *psmouse,
|
||||
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
|
||||
__set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
|
||||
} else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
|
||||
/* Non-image sensors with AGM use semi-mt */
|
||||
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
|
||||
input_mt_init_slots(dev, 2, 0);
|
||||
set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
|
||||
ABS_MT_POSITION_Y);
|
||||
/*
|
||||
* Profile sensor in CR-48 tracks contacts reasonably well,
|
||||
* other non-image sensors with AGM use semi-mt.
|
||||
*/
|
||||
input_mt_init_slots(dev, 2,
|
||||
INPUT_MT_POINTER |
|
||||
(cr48_profile_sensor ?
|
||||
INPUT_MT_TRACK : INPUT_MT_SEMI_MT));
|
||||
}
|
||||
|
||||
if (SYN_CAP_PALMDETECT(priv->capabilities))
|
||||
@ -1577,10 +1629,24 @@ static const struct dmi_system_id olpc_dmi_table[] __initconst = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct dmi_system_id __initconst cr48_dmi_table[] = {
|
||||
#if defined(CONFIG_DMI) && defined(CONFIG_X86)
|
||||
{
|
||||
/* Cr-48 Chromebook (Codename Mario) */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "IEC"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
|
||||
},
|
||||
},
|
||||
#endif
|
||||
{ }
|
||||
};
|
||||
|
||||
void __init synaptics_module_init(void)
|
||||
{
|
||||
impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
|
||||
broken_olpc_ec = dmi_check_system(olpc_dmi_table);
|
||||
cr48_profile_sensor = dmi_check_system(cr48_dmi_table);
|
||||
}
|
||||
|
||||
static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
|
||||
|
@ -359,7 +359,6 @@ static int mxt_bootloader_read(struct mxt_data *data,
|
||||
msg.buf = val;
|
||||
|
||||
ret = i2c_transfer(data->client->adapter, &msg, 1);
|
||||
|
||||
if (ret == 1) {
|
||||
ret = 0;
|
||||
} else {
|
||||
@ -414,6 +413,7 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
|
||||
case 0x5b:
|
||||
bootloader = appmode - 0x26;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&data->client->dev,
|
||||
"Appmode i2c address 0x%02x not found\n",
|
||||
@ -425,20 +425,20 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxt_probe_bootloader(struct mxt_data *data, bool retry)
|
||||
static int mxt_probe_bootloader(struct mxt_data *data, bool alt_address)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
int ret;
|
||||
int error;
|
||||
u8 val;
|
||||
bool crc_failure;
|
||||
|
||||
ret = mxt_lookup_bootloader_address(data, retry);
|
||||
if (ret)
|
||||
return ret;
|
||||
error = mxt_lookup_bootloader_address(data, alt_address);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
ret = mxt_bootloader_read(data, &val, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
error = mxt_bootloader_read(data, &val, 1);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Check app crc fail mode */
|
||||
crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL;
|
||||
@ -1064,131 +1064,21 @@ static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off)
|
||||
return crc;
|
||||
}
|
||||
|
||||
/*
|
||||
* mxt_update_cfg - download configuration to chip
|
||||
*
|
||||
* Atmel Raw Config File Format
|
||||
*
|
||||
* The first four lines of the raw config file contain:
|
||||
* 1) Version
|
||||
* 2) Chip ID Information (first 7 bytes of device memory)
|
||||
* 3) Chip Information Block 24-bit CRC Checksum
|
||||
* 4) Chip Configuration 24-bit CRC Checksum
|
||||
*
|
||||
* The rest of the file consists of one line per object instance:
|
||||
* <TYPE> <INSTANCE> <SIZE> <CONTENTS>
|
||||
*
|
||||
* <TYPE> - 2-byte object type as hex
|
||||
* <INSTANCE> - 2-byte object instance number as hex
|
||||
* <SIZE> - 2-byte object size as hex
|
||||
* <CONTENTS> - array of <SIZE> 1-byte hex values
|
||||
*/
|
||||
static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
||||
static int mxt_prepare_cfg_mem(struct mxt_data *data,
|
||||
const struct firmware *cfg,
|
||||
unsigned int data_pos,
|
||||
unsigned int cfg_start_ofs,
|
||||
u8 *config_mem,
|
||||
size_t config_mem_size)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
struct mxt_info cfg_info;
|
||||
struct mxt_object *object;
|
||||
int ret;
|
||||
unsigned int type, instance, size, byte_offset;
|
||||
int offset;
|
||||
int data_pos;
|
||||
int byte_offset;
|
||||
int ret;
|
||||
int i;
|
||||
int cfg_start_ofs;
|
||||
u32 info_crc, config_crc, calculated_crc;
|
||||
u8 *config_mem;
|
||||
size_t config_mem_size;
|
||||
unsigned int type, instance, size;
|
||||
u8 val;
|
||||
u16 reg;
|
||||
|
||||
mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1);
|
||||
|
||||
if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) {
|
||||
dev_err(dev, "Unrecognised config file\n");
|
||||
ret = -EINVAL;
|
||||
goto release;
|
||||
}
|
||||
|
||||
data_pos = strlen(MXT_CFG_MAGIC);
|
||||
|
||||
/* Load information block and check */
|
||||
for (i = 0; i < sizeof(struct mxt_info); i++) {
|
||||
ret = sscanf(cfg->data + data_pos, "%hhx%n",
|
||||
(unsigned char *)&cfg_info + i,
|
||||
&offset);
|
||||
if (ret != 1) {
|
||||
dev_err(dev, "Bad format\n");
|
||||
ret = -EINVAL;
|
||||
goto release;
|
||||
}
|
||||
|
||||
data_pos += offset;
|
||||
}
|
||||
|
||||
if (cfg_info.family_id != data->info.family_id) {
|
||||
dev_err(dev, "Family ID mismatch!\n");
|
||||
ret = -EINVAL;
|
||||
goto release;
|
||||
}
|
||||
|
||||
if (cfg_info.variant_id != data->info.variant_id) {
|
||||
dev_err(dev, "Variant ID mismatch!\n");
|
||||
ret = -EINVAL;
|
||||
goto release;
|
||||
}
|
||||
|
||||
/* Read CRCs */
|
||||
ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset);
|
||||
if (ret != 1) {
|
||||
dev_err(dev, "Bad format: failed to parse Info CRC\n");
|
||||
ret = -EINVAL;
|
||||
goto release;
|
||||
}
|
||||
data_pos += offset;
|
||||
|
||||
ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset);
|
||||
if (ret != 1) {
|
||||
dev_err(dev, "Bad format: failed to parse Config CRC\n");
|
||||
ret = -EINVAL;
|
||||
goto release;
|
||||
}
|
||||
data_pos += offset;
|
||||
|
||||
/*
|
||||
* The Info Block CRC is calculated over mxt_info and the object
|
||||
* table. If it does not match then we are trying to load the
|
||||
* configuration from a different chip or firmware version, so
|
||||
* the configuration CRC is invalid anyway.
|
||||
*/
|
||||
if (info_crc == data->info_crc) {
|
||||
if (config_crc == 0 || data->config_crc == 0) {
|
||||
dev_info(dev, "CRC zero, attempting to apply config\n");
|
||||
} else if (config_crc == data->config_crc) {
|
||||
dev_dbg(dev, "Config CRC 0x%06X: OK\n",
|
||||
data->config_crc);
|
||||
ret = 0;
|
||||
goto release;
|
||||
} else {
|
||||
dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
|
||||
data->config_crc, config_crc);
|
||||
}
|
||||
} else {
|
||||
dev_warn(dev,
|
||||
"Warning: Info CRC error - device=0x%06X file=0x%06X\n",
|
||||
data->info_crc, info_crc);
|
||||
}
|
||||
|
||||
/* Malloc memory to store configuration */
|
||||
cfg_start_ofs = MXT_OBJECT_START +
|
||||
data->info.object_num * sizeof(struct mxt_object) +
|
||||
MXT_INFO_CHECKSUM_SIZE;
|
||||
config_mem_size = data->mem_size - cfg_start_ofs;
|
||||
config_mem = kzalloc(config_mem_size, GFP_KERNEL);
|
||||
if (!config_mem) {
|
||||
dev_err(dev, "Failed to allocate memory\n");
|
||||
ret = -ENOMEM;
|
||||
goto release;
|
||||
}
|
||||
u8 val;
|
||||
|
||||
while (data_pos < cfg->size) {
|
||||
/* Read type, instance, length */
|
||||
@ -1199,8 +1089,7 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
||||
break;
|
||||
} else if (ret != 3) {
|
||||
dev_err(dev, "Bad format: failed to parse object\n");
|
||||
ret = -EINVAL;
|
||||
goto release_mem;
|
||||
return -EINVAL;
|
||||
}
|
||||
data_pos += offset;
|
||||
|
||||
@ -1209,8 +1098,12 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
||||
/* Skip object */
|
||||
for (i = 0; i < size; i++) {
|
||||
ret = sscanf(cfg->data + data_pos, "%hhx%n",
|
||||
&val,
|
||||
&offset);
|
||||
&val, &offset);
|
||||
if (ret != 1) {
|
||||
dev_err(dev, "Bad format in T%d at %d\n",
|
||||
type, i);
|
||||
return -EINVAL;
|
||||
}
|
||||
data_pos += offset;
|
||||
}
|
||||
continue;
|
||||
@ -1240,8 +1133,7 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
||||
|
||||
if (instance >= mxt_obj_instances(object)) {
|
||||
dev_err(dev, "Object instances exceeded!\n");
|
||||
ret = -EINVAL;
|
||||
goto release_mem;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = object->start_address + mxt_obj_size(object) * instance;
|
||||
@ -1251,9 +1143,9 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
||||
&val,
|
||||
&offset);
|
||||
if (ret != 1) {
|
||||
dev_err(dev, "Bad format in T%d\n", type);
|
||||
ret = -EINVAL;
|
||||
goto release_mem;
|
||||
dev_err(dev, "Bad format in T%d at %d\n",
|
||||
type, i);
|
||||
return -EINVAL;
|
||||
}
|
||||
data_pos += offset;
|
||||
|
||||
@ -1262,18 +1154,165 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
||||
|
||||
byte_offset = reg + i - cfg_start_ofs;
|
||||
|
||||
if ((byte_offset >= 0)
|
||||
&& (byte_offset <= config_mem_size)) {
|
||||
if (byte_offset >= 0 && byte_offset < config_mem_size) {
|
||||
*(config_mem + byte_offset) = val;
|
||||
} else {
|
||||
dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n",
|
||||
reg, object->type, byte_offset);
|
||||
ret = -EINVAL;
|
||||
goto release_mem;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxt_upload_cfg_mem(struct mxt_data *data, unsigned int cfg_start,
|
||||
u8 *config_mem, size_t config_mem_size)
|
||||
{
|
||||
unsigned int byte_offset = 0;
|
||||
int error;
|
||||
|
||||
/* Write configuration as blocks */
|
||||
while (byte_offset < config_mem_size) {
|
||||
unsigned int size = config_mem_size - byte_offset;
|
||||
|
||||
if (size > MXT_MAX_BLOCK_WRITE)
|
||||
size = MXT_MAX_BLOCK_WRITE;
|
||||
|
||||
error = __mxt_write_reg(data->client,
|
||||
cfg_start + byte_offset,
|
||||
size, config_mem + byte_offset);
|
||||
if (error) {
|
||||
dev_err(&data->client->dev,
|
||||
"Config write error, ret=%d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
byte_offset += size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* mxt_update_cfg - download configuration to chip
|
||||
*
|
||||
* Atmel Raw Config File Format
|
||||
*
|
||||
* The first four lines of the raw config file contain:
|
||||
* 1) Version
|
||||
* 2) Chip ID Information (first 7 bytes of device memory)
|
||||
* 3) Chip Information Block 24-bit CRC Checksum
|
||||
* 4) Chip Configuration 24-bit CRC Checksum
|
||||
*
|
||||
* The rest of the file consists of one line per object instance:
|
||||
* <TYPE> <INSTANCE> <SIZE> <CONTENTS>
|
||||
*
|
||||
* <TYPE> - 2-byte object type as hex
|
||||
* <INSTANCE> - 2-byte object instance number as hex
|
||||
* <SIZE> - 2-byte object size as hex
|
||||
* <CONTENTS> - array of <SIZE> 1-byte hex values
|
||||
*/
|
||||
static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
struct mxt_info cfg_info;
|
||||
int ret;
|
||||
int offset;
|
||||
int data_pos;
|
||||
int i;
|
||||
int cfg_start_ofs;
|
||||
u32 info_crc, config_crc, calculated_crc;
|
||||
u8 *config_mem;
|
||||
size_t config_mem_size;
|
||||
|
||||
mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1);
|
||||
|
||||
if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) {
|
||||
dev_err(dev, "Unrecognised config file\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data_pos = strlen(MXT_CFG_MAGIC);
|
||||
|
||||
/* Load information block and check */
|
||||
for (i = 0; i < sizeof(struct mxt_info); i++) {
|
||||
ret = sscanf(cfg->data + data_pos, "%hhx%n",
|
||||
(unsigned char *)&cfg_info + i,
|
||||
&offset);
|
||||
if (ret != 1) {
|
||||
dev_err(dev, "Bad format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data_pos += offset;
|
||||
}
|
||||
|
||||
if (cfg_info.family_id != data->info.family_id) {
|
||||
dev_err(dev, "Family ID mismatch!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cfg_info.variant_id != data->info.variant_id) {
|
||||
dev_err(dev, "Variant ID mismatch!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Read CRCs */
|
||||
ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset);
|
||||
if (ret != 1) {
|
||||
dev_err(dev, "Bad format: failed to parse Info CRC\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
data_pos += offset;
|
||||
|
||||
ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset);
|
||||
if (ret != 1) {
|
||||
dev_err(dev, "Bad format: failed to parse Config CRC\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
data_pos += offset;
|
||||
|
||||
/*
|
||||
* The Info Block CRC is calculated over mxt_info and the object
|
||||
* table. If it does not match then we are trying to load the
|
||||
* configuration from a different chip or firmware version, so
|
||||
* the configuration CRC is invalid anyway.
|
||||
*/
|
||||
if (info_crc == data->info_crc) {
|
||||
if (config_crc == 0 || data->config_crc == 0) {
|
||||
dev_info(dev, "CRC zero, attempting to apply config\n");
|
||||
} else if (config_crc == data->config_crc) {
|
||||
dev_dbg(dev, "Config CRC 0x%06X: OK\n",
|
||||
data->config_crc);
|
||||
return 0;
|
||||
} else {
|
||||
dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
|
||||
data->config_crc, config_crc);
|
||||
}
|
||||
} else {
|
||||
dev_warn(dev,
|
||||
"Warning: Info CRC error - device=0x%06X file=0x%06X\n",
|
||||
data->info_crc, info_crc);
|
||||
}
|
||||
|
||||
/* Malloc memory to store configuration */
|
||||
cfg_start_ofs = MXT_OBJECT_START +
|
||||
data->info.object_num * sizeof(struct mxt_object) +
|
||||
MXT_INFO_CHECKSUM_SIZE;
|
||||
config_mem_size = data->mem_size - cfg_start_ofs;
|
||||
config_mem = kzalloc(config_mem_size, GFP_KERNEL);
|
||||
if (!config_mem) {
|
||||
dev_err(dev, "Failed to allocate memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = mxt_prepare_cfg_mem(data, cfg, data_pos, cfg_start_ofs,
|
||||
config_mem, config_mem_size);
|
||||
if (ret)
|
||||
goto release_mem;
|
||||
|
||||
/* Calculate crc of the received configs (not the raw config file) */
|
||||
if (data->T7_address < cfg_start_ofs) {
|
||||
dev_err(dev, "Bad T7 address, T7addr = %x, config offset %x\n",
|
||||
@ -1286,28 +1325,14 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
||||
data->T7_address - cfg_start_ofs,
|
||||
config_mem_size);
|
||||
|
||||
if (config_crc > 0 && (config_crc != calculated_crc))
|
||||
if (config_crc > 0 && config_crc != calculated_crc)
|
||||
dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n",
|
||||
calculated_crc, config_crc);
|
||||
|
||||
/* Write configuration as blocks */
|
||||
byte_offset = 0;
|
||||
while (byte_offset < config_mem_size) {
|
||||
size = config_mem_size - byte_offset;
|
||||
|
||||
if (size > MXT_MAX_BLOCK_WRITE)
|
||||
size = MXT_MAX_BLOCK_WRITE;
|
||||
|
||||
ret = __mxt_write_reg(data->client,
|
||||
cfg_start_ofs + byte_offset,
|
||||
size, config_mem + byte_offset);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Config write error, ret=%d\n", ret);
|
||||
goto release_mem;
|
||||
}
|
||||
|
||||
byte_offset += size;
|
||||
}
|
||||
ret = mxt_upload_cfg_mem(data, cfg_start_ofs,
|
||||
config_mem, config_mem_size);
|
||||
if (ret)
|
||||
goto release_mem;
|
||||
|
||||
mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE);
|
||||
|
||||
@ -1319,8 +1344,6 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
||||
|
||||
release_mem:
|
||||
kfree(config_mem);
|
||||
release:
|
||||
release_firmware(cfg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1422,10 +1445,12 @@ static int mxt_get_object_table(struct mxt_data *data)
|
||||
|
||||
switch (object->type) {
|
||||
case MXT_GEN_MESSAGE_T5:
|
||||
if (data->info.family_id == 0x80) {
|
||||
if (data->info.family_id == 0x80 &&
|
||||
data->info.version < 0x20) {
|
||||
/*
|
||||
* On mXT224 read and discard unused CRC byte
|
||||
* otherwise DMA reads are misaligned
|
||||
* On mXT224 firmware versions prior to V2.0
|
||||
* read and discard unused CRC byte otherwise
|
||||
* DMA reads are misaligned.
|
||||
*/
|
||||
data->T5_msg_size = mxt_obj_size(object);
|
||||
} else {
|
||||
@ -1433,6 +1458,7 @@ static int mxt_get_object_table(struct mxt_data *data)
|
||||
data->T5_msg_size = mxt_obj_size(object) - 1;
|
||||
}
|
||||
data->T5_address = object->start_address;
|
||||
break;
|
||||
case MXT_GEN_COMMAND_T6:
|
||||
data->T6_reportid = min_id;
|
||||
data->T6_address = object->start_address;
|
||||
@ -1638,46 +1664,45 @@ static int mxt_configure_objects(struct mxt_data *data,
|
||||
static void mxt_config_cb(const struct firmware *cfg, void *ctx)
|
||||
{
|
||||
mxt_configure_objects(ctx, cfg);
|
||||
release_firmware(cfg);
|
||||
}
|
||||
|
||||
static int mxt_initialize(struct mxt_data *data)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int recovery_attempts = 0;
|
||||
int error;
|
||||
bool alt_bootloader_addr = false;
|
||||
bool retry = false;
|
||||
|
||||
retry_info:
|
||||
error = mxt_get_info(data);
|
||||
if (error) {
|
||||
retry_bootloader:
|
||||
error = mxt_probe_bootloader(data, alt_bootloader_addr);
|
||||
while (1) {
|
||||
error = mxt_get_info(data);
|
||||
if (!error)
|
||||
break;
|
||||
|
||||
/* Check bootloader state */
|
||||
error = mxt_probe_bootloader(data, false);
|
||||
if (error) {
|
||||
if (alt_bootloader_addr) {
|
||||
dev_info(&client->dev, "Trying alternate bootloader address\n");
|
||||
error = mxt_probe_bootloader(data, true);
|
||||
if (error) {
|
||||
/* Chip is not in appmode or bootloader mode */
|
||||
return error;
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "Trying alternate bootloader address\n");
|
||||
alt_bootloader_addr = true;
|
||||
goto retry_bootloader;
|
||||
} else {
|
||||
if (retry) {
|
||||
dev_err(&client->dev, "Could not recover from bootloader mode\n");
|
||||
/*
|
||||
* We can reflash from this state, so do not
|
||||
* abort init
|
||||
*/
|
||||
data->in_bootloader = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Attempt to exit bootloader into app mode */
|
||||
mxt_send_bootloader_cmd(data, false);
|
||||
msleep(MXT_FW_RESET_TIME);
|
||||
retry = true;
|
||||
goto retry_info;
|
||||
}
|
||||
|
||||
/* OK, we are in bootloader, see if we can recover */
|
||||
if (++recovery_attempts > 1) {
|
||||
dev_err(&client->dev, "Could not recover from bootloader mode\n");
|
||||
/*
|
||||
* We can reflash from this state, so do not
|
||||
* abort initialization.
|
||||
*/
|
||||
data->in_bootloader = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Attempt to exit bootloader into app mode */
|
||||
mxt_send_bootloader_cmd(data, false);
|
||||
msleep(MXT_FW_RESET_TIME);
|
||||
}
|
||||
|
||||
/* Get object table information */
|
||||
@ -1687,13 +1712,18 @@ retry_bootloader:
|
||||
return error;
|
||||
}
|
||||
|
||||
mxt_acquire_irq(data);
|
||||
error = mxt_acquire_irq(data);
|
||||
if (error)
|
||||
goto err_free_object_table;
|
||||
|
||||
request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME,
|
||||
&data->client->dev, GFP_KERNEL, data,
|
||||
mxt_config_cb);
|
||||
error = request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME,
|
||||
&client->dev, GFP_KERNEL, data,
|
||||
mxt_config_cb);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to invoke firmware loader: %d\n",
|
||||
error);
|
||||
goto err_free_object_table;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -262,7 +262,6 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
|
||||
case M06:
|
||||
wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
|
||||
wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
|
||||
wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
|
||||
wrbuf[2] = value;
|
||||
wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
|
||||
return edt_ft5x06_ts_readwrite(tsdata->client, 4,
|
||||
|
@ -105,6 +105,7 @@ void input_mt_report_slot_state(struct input_dev *dev,
|
||||
|
||||
void input_mt_report_finger_count(struct input_dev *dev, int count);
|
||||
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count);
|
||||
void input_mt_drop_unused(struct input_dev *dev);
|
||||
|
||||
void input_mt_sync_frame(struct input_dev *dev);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user