diff options
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/ff-memless.c | 18 | ||||
-rw-r--r-- | drivers/input/input-mt.c | 9 | ||||
-rw-r--r-- | drivers/input/keyboard/Kconfig | 2 | ||||
-rw-r--r-- | drivers/input/keyboard/cros_ec_keyb.c | 34 | ||||
-rw-r--r-- | drivers/input/keyboard/gpio_keys_polled.c | 2 | ||||
-rw-r--r-- | drivers/input/misc/ati_remote2.c | 4 | ||||
-rw-r--r-- | drivers/input/mouse/psmouse-base.c | 2 | ||||
-rw-r--r-- | drivers/input/serio/serport.c | 5 | ||||
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 3 | ||||
-rw-r--r-- | drivers/input/touchscreen/sur40.c | 480 |
10 files changed, 520 insertions, 39 deletions
diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c index 74c0d8c6002a..fcc6c3368182 100644 --- a/drivers/input/ff-memless.c +++ b/drivers/input/ff-memless.c @@ -237,6 +237,18 @@ static u16 ml_calculate_direction(u16 direction, u16 force, (force + new_force)) << 1; } +#define FRAC_N 8 +static inline s16 fixp_new16(s16 a) +{ + return ((s32)a) >> (16 - FRAC_N); +} + +static inline s16 fixp_mult(s16 a, s16 b) +{ + a = ((s32)a * 0x100) / 0x7fff; + return ((s32)(a * b)) >> FRAC_N; +} + /* * Combine two effects and apply gain. */ @@ -247,7 +259,7 @@ static void ml_combine_effects(struct ff_effect *effect, struct ff_effect *new = state->effect; unsigned int strong, weak, i; int x, y; - fixp_t level; + s16 level; switch (new->type) { case FF_CONSTANT: @@ -255,8 +267,8 @@ static void ml_combine_effects(struct ff_effect *effect, level = fixp_new16(apply_envelope(state, new->u.constant.level, &new->u.constant.envelope)); - x = fixp_mult(fixp_sin(i), level) * gain / 0xffff; - y = fixp_mult(-fixp_cos(i), level) * gain / 0xffff; + x = fixp_mult(fixp_sin16(i), level) * gain / 0xffff; + y = fixp_mult(-fixp_cos16(i), level) * gain / 0xffff; /* * here we abuse ff_ramp to hold x and y of constant force * If in future any driver wants something else than x and y diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index b097af269e00..54fce56c8023 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -88,10 +88,13 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, goto err_mem; } - /* Mark slots as 'unused' */ + /* Mark slots as 'inactive' */ for (i = 0; i < num_slots; i++) input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1); + /* Mark slots as 'unused' */ + mt->frame = 1; + dev->mt = mt; return 0; err_mem: @@ -447,6 +450,8 @@ EXPORT_SYMBOL(input_mt_assign_slots); * set the key on the first unused slot and return. * * If no available slot can be found, -1 is returned. + * Note that for this function to work properly, input_mt_sync_frame() has + * to be called at each frame. */ int input_mt_get_slot_by_key(struct input_dev *dev, int key) { @@ -461,7 +466,7 @@ int input_mt_get_slot_by_key(struct input_dev *dev, int key) return s - mt->slots; for (s = mt->slots; s != mt->slots + mt->num_slots; s++) - if (!input_mt_is_active(s)) { + if (!input_mt_is_active(s) && !input_mt_is_used(mt, s)) { s->key = key; return s - mt->slots; } diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 4d75062a6206..4cd94fd6cbad 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -678,7 +678,7 @@ config KEYBOARD_W90P910 config KEYBOARD_CROS_EC tristate "ChromeOS EC keyboard" select INPUT_MATRIXKMAP - depends on MFD_CROS_EC + depends on CROS_EC_PROTO help Say Y here to enable the matrix keyboard used by ChromeOS devices and implemented on the ChromeOS EC. You must enable one bus option diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 64b9b59ad4cb..b01966dc7eb3 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -148,16 +148,28 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) { - struct cros_ec_command msg = { - .version = 0, - .command = EC_CMD_MKBP_STATE, - .outdata = NULL, - .outsize = 0, - .indata = kb_state, - .insize = ckdev->cols, - }; - - return cros_ec_cmd_xfer(ckdev->ec, &msg); + int ret = 0; + struct cros_ec_command *msg; + + msg = kmalloc(sizeof(*msg) + ckdev->cols, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->version = 0; + msg->command = EC_CMD_MKBP_STATE; + msg->insize = ckdev->cols; + msg->outsize = 0; + + ret = cros_ec_cmd_xfer(ckdev->ec, msg); + if (ret < 0) { + dev_err(ckdev->dev, "Error transferring EC message %d\n", ret); + goto exit; + } + + memcpy(kb_state, msg->data, ckdev->cols); +exit: + kfree(msg); + return ret; } static irqreturn_t cros_ec_keyb_irq(int irq, void *data) @@ -263,7 +275,7 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) ckdev->dev = dev; dev_set_drvdata(&pdev->dev, ckdev); - idev->name = ec->ec_name; + idev->name = CROS_EC_DEV_NAME; idev->phys = ec->phys_name; __set_bit(EV_REP, idev->evbit); diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 959b826d25c8..c6dc644aa580 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -125,7 +125,7 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct device_for_each_child_node(dev, child) { struct gpio_desc *desc; - desc = devm_get_gpiod_from_child(dev, child); + desc = devm_get_gpiod_from_child(dev, NULL, child); if (IS_ERR(desc)) { error = PTR_ERR(desc); if (error != -EPROBE_DEFER) diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index f63341f20b91..cfd58e87da26 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -94,7 +94,7 @@ static int ati_remote2_get_mode_mask(char *buffer, static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK; #define param_check_channel_mask(name, p) __param_check(name, p, unsigned int) -static struct kernel_param_ops param_ops_channel_mask = { +static const struct kernel_param_ops param_ops_channel_mask = { .set = ati_remote2_set_channel_mask, .get = ati_remote2_get_channel_mask, }; @@ -103,7 +103,7 @@ MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...< static unsigned int mode_mask = ATI_REMOTE2_MAX_MODE_MASK; #define param_check_mode_mask(name, p) __param_check(name, p, unsigned int) -static struct kernel_param_ops param_ops_mode_mask = { +static const struct kernel_param_ops param_ops_mode_mask = { .set = ati_remote2_set_mode_mask, .get = ati_remote2_get_mode_mask, }; diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 7c4ba43d253e..ec3477036150 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -47,7 +47,7 @@ MODULE_LICENSE("GPL"); static unsigned int psmouse_max_proto = PSMOUSE_AUTO; static int psmouse_set_maxproto(const char *val, const struct kernel_param *); static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp); -static struct kernel_param_ops param_ops_proto_abbrev = { +static const struct kernel_param_ops param_ops_proto_abbrev = { .set = psmouse_set_maxproto, .get = psmouse_get_maxproto, }; diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 69175b825346..9c927d35c1f5 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -167,7 +167,6 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u { struct serport *serport = (struct serport*) tty->disc_data; struct serio *serio; - char name[64]; if (test_and_set_bit(SERPORT_BUSY, &serport->flags)) return -EBUSY; @@ -177,7 +176,7 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u return -ENOMEM; strlcpy(serio->name, "Serial port", sizeof(serio->name)); - snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name)); + snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty)); serio->id = serport->id; serio->id.type = SERIO_RS232; serio->write = serport_serio_write; @@ -187,7 +186,7 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u serio->dev.parent = tty->dev; serio_register_port(serport->serio); - printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name)); + printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty)); wait_event_interruptible(serport->wait, test_bit(SERPORT_DEAD, &serport->flags)); serio_unregister_port(serport->serio); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index aa2b5f21b89b..a854c6e5f09e 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -992,8 +992,9 @@ config TOUCHSCREEN_SUN4I config TOUCHSCREEN_SUR40 tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen" - depends on USB + depends on USB && MEDIA_USB_SUPPORT && HAS_DMA select INPUT_POLLDEV + select VIDEOBUF2_DMA_SG help Say Y here if you want support for the Samsung SUR40 touchscreen (also known as Microsoft Surface 2.0 or Microsoft PixelSense). diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index f1cb05148b46..8be7b9b79f20 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -1,7 +1,7 @@ /* * Surface2.0/SUR40/PixelSense input driver * - * Copyright (c) 2013 by Florian 'floe' Echtler <floe@butterbrot.org> + * Copyright (c) 2014 by Florian 'floe' Echtler <floe@butterbrot.org> * * Derived from the USB Skeleton driver 1.1, * Copyright (c) 2003 Greg Kroah-Hartman (greg@kroah.com) @@ -12,6 +12,9 @@ * and from the generic hid-multitouch driver, * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr> * + * and from the v4l2-pci-skeleton driver, + * Copyright (c) Copyright 2014 Cisco Systems, Inc. + * * 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 @@ -31,6 +34,11 @@ #include <linux/input-polldev.h> #include <linux/input/mt.h> #include <linux/usb/input.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-dma-sg.h> /* read 512 bytes from endpoint 0x86 -> get header + blobs */ struct sur40_header { @@ -82,9 +90,19 @@ struct sur40_data { struct sur40_blob blobs[]; } __packed; +/* read 512 bytes from endpoint 0x82 -> get header below + * continue reading 16k blocks until header.size bytes read */ +struct sur40_image_header { + __le32 magic; /* "SUBF" */ + __le32 packet_id; + __le32 size; /* always 0x0007e900 = 960x540 */ + __le32 timestamp; /* milliseconds (increases by 16 or 17 each frame) */ + __le32 unknown; /* "epoch?" always 02/03 00 00 00 */ +} __packed; /* version information */ #define DRIVER_SHORT "sur40" +#define DRIVER_LONG "Samsung SUR40" #define DRIVER_AUTHOR "Florian 'floe' Echtler <floe@butterbrot.org>" #define DRIVER_DESC "Surface2.0/SUR40/PixelSense input driver" @@ -99,8 +117,15 @@ struct sur40_data { /* touch data endpoint */ #define TOUCH_ENDPOINT 0x86 +/* video data endpoint */ +#define VIDEO_ENDPOINT 0x82 + +/* video header fields */ +#define VIDEO_HEADER_MAGIC 0x46425553 +#define VIDEO_PACKET_SIZE 16384 + /* polling interval (ms) */ -#define POLL_INTERVAL 10 +#define POLL_INTERVAL 4 /* maximum number of contacts FIXME: this is a guess? */ #define MAX_CONTACTS 64 @@ -113,21 +138,23 @@ struct sur40_data { #define SUR40_GET_STATE 0xc5 /* 4 bytes state (?) */ #define SUR40_GET_SENSORS 0xb1 /* 8 bytes sensors */ -/* - * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT - * here by mistake which is very likely to have corrupted the firmware EEPROM - * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug. - * Should you ever run into a similar problem, the background story to this - * incident and instructions on how to fix the corrupted EEPROM are available - * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html -*/ - +/* master device state */ struct sur40_state { struct usb_device *usbdev; struct device *dev; struct input_polled_dev *input; + struct v4l2_device v4l2; + struct video_device vdev; + struct mutex lock; + + struct vb2_queue queue; + struct vb2_alloc_ctx *alloc_ctx; + struct list_head buf_list; + spinlock_t qlock; + int sequence; + struct sur40_data *bulk_in_buffer; size_t bulk_in_size; u8 bulk_in_epaddr; @@ -135,6 +162,27 @@ struct sur40_state { char phys[64]; }; +struct sur40_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +/* forward declarations */ +static const struct video_device sur40_video_device; +static const struct v4l2_pix_format sur40_video_format; +static const struct vb2_queue sur40_queue; +static void sur40_process_video(struct sur40_state *sur40); + +/* + * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT + * here by mistake which is very likely to have corrupted the firmware EEPROM + * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug. + * Should you ever run into a similar problem, the background story to this + * incident and instructions on how to fix the corrupted EEPROM are available + * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html +*/ + +/* command wrapper */ static int sur40_command(struct sur40_state *dev, u8 command, u16 index, void *buffer, u16 size) { @@ -247,7 +295,6 @@ static void sur40_report_blob(struct sur40_blob *blob, struct input_dev *input) /* core function: poll for new input data */ static void sur40_poll(struct input_polled_dev *polldev) { - struct sur40_state *sur40 = polldev->private; struct input_dev *input = polldev->input; int result, bulk_read, need_blobs, packet_blobs, i; @@ -295,7 +342,7 @@ static void sur40_poll(struct input_polled_dev *polldev) * instead of at the end. */ if (packet_id != header->packet_id) - dev_warn(sur40->dev, "packet ID mismatch\n"); + dev_dbg(sur40->dev, "packet ID mismatch\n"); packet_blobs = result / sizeof(struct sur40_blob); dev_dbg(sur40->dev, "received %d blobs\n", packet_blobs); @@ -314,6 +361,97 @@ static void sur40_poll(struct input_polled_dev *polldev) input_mt_sync_frame(input); input_sync(input); + + sur40_process_video(sur40); +} + +/* deal with video data */ +static void sur40_process_video(struct sur40_state *sur40) +{ + + struct sur40_image_header *img = (void *)(sur40->bulk_in_buffer); + struct sur40_buffer *new_buf; + struct usb_sg_request sgr; + struct sg_table *sgt; + int result, bulk_read; + + if (!vb2_start_streaming_called(&sur40->queue)) + return; + + /* get a new buffer from the list */ + spin_lock(&sur40->qlock); + if (list_empty(&sur40->buf_list)) { + dev_dbg(sur40->dev, "buffer queue empty\n"); + spin_unlock(&sur40->qlock); + return; + } + new_buf = list_entry(sur40->buf_list.next, struct sur40_buffer, list); + list_del(&new_buf->list); + spin_unlock(&sur40->qlock); + + dev_dbg(sur40->dev, "buffer acquired\n"); + + /* retrieve data via bulk read */ + result = usb_bulk_msg(sur40->usbdev, + usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), + sur40->bulk_in_buffer, sur40->bulk_in_size, + &bulk_read, 1000); + + if (result < 0) { + dev_err(sur40->dev, "error in usb_bulk_read\n"); + goto err_poll; + } + + if (bulk_read != sizeof(struct sur40_image_header)) { + dev_err(sur40->dev, "received %d bytes (%zd expected)\n", + bulk_read, sizeof(struct sur40_image_header)); + goto err_poll; + } + + if (le32_to_cpu(img->magic) != VIDEO_HEADER_MAGIC) { + dev_err(sur40->dev, "image magic mismatch\n"); + goto err_poll; + } + + if (le32_to_cpu(img->size) != sur40_video_format.sizeimage) { + dev_err(sur40->dev, "image size mismatch\n"); + goto err_poll; + } + + dev_dbg(sur40->dev, "header acquired\n"); + + sgt = vb2_dma_sg_plane_desc(&new_buf->vb, 0); + + result = usb_sg_init(&sgr, sur40->usbdev, + usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0, + sgt->sgl, sgt->nents, sur40_video_format.sizeimage, 0); + if (result < 0) { + dev_err(sur40->dev, "error %d in usb_sg_init\n", result); + goto err_poll; + } + + usb_sg_wait(&sgr); + if (sgr.status < 0) { + dev_err(sur40->dev, "error %d in usb_sg_wait\n", sgr.status); + goto err_poll; + } + + dev_dbg(sur40->dev, "image acquired\n"); + + /* return error if streaming was stopped in the meantime */ + if (sur40->sequence == -1) + goto err_poll; + + /* mark as finished */ + v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp); + new_buf->vb.v4l2_buf.sequence = sur40->sequence++; + new_buf->vb.v4l2_buf.field = V4L2_FIELD_NONE; + vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE); + dev_dbg(sur40->dev, "buffer marked done\n"); + return; + +err_poll: + vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_ERROR); } /* Initialize input device parameters. */ @@ -377,6 +515,11 @@ static int sur40_probe(struct usb_interface *interface, goto err_free_dev; } + /* initialize locks/lists */ + INIT_LIST_HEAD(&sur40->buf_list); + spin_lock_init(&sur40->qlock); + mutex_init(&sur40->lock); + /* Set up polled input device control structure */ poll_dev->private = sur40; poll_dev->poll_interval = POLL_INTERVAL; @@ -387,7 +530,7 @@ static int sur40_probe(struct usb_interface *interface, /* Set up regular input device structure */ sur40_input_setup(poll_dev->input); - poll_dev->input->name = "Samsung SUR40"; + poll_dev->input->name = DRIVER_LONG; usb_to_input_id(usbdev, &poll_dev->input->id); usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys)); strlcat(sur40->phys, "/input0", sizeof(sur40->phys)); @@ -408,6 +551,7 @@ static int sur40_probe(struct usb_interface *interface, goto err_free_polldev; } + /* register the polled input device */ error = input_register_polled_device(poll_dev); if (error) { dev_err(&interface->dev, @@ -415,12 +559,54 @@ static int sur40_probe(struct usb_interface *interface, goto err_free_buffer; } + /* register the video master device */ + snprintf(sur40->v4l2.name, sizeof(sur40->v4l2.name), "%s", DRIVER_LONG); + error = v4l2_device_register(sur40->dev, &sur40->v4l2); + if (error) { + dev_err(&interface->dev, + "Unable to register video master device."); + goto err_unreg_v4l2; + } + + /* initialize the lock and subdevice */ + sur40->queue = sur40_queue; + sur40->queue.drv_priv = sur40; + sur40->queue.lock = &sur40->lock; + + /* initialize the queue */ + error = vb2_queue_init(&sur40->queue); + if (error) + goto err_unreg_v4l2; + + sur40->alloc_ctx = vb2_dma_sg_init_ctx(sur40->dev); + if (IS_ERR(sur40->alloc_ctx)) { + dev_err(sur40->dev, "Can't allocate buffer context"); + goto err_unreg_v4l2; + } + + sur40->vdev = sur40_video_device; + sur40->vdev.v4l2_dev = &sur40->v4l2; + sur40->vdev.lock = &sur40->lock; + sur40->vdev.queue = &sur40->queue; + video_set_drvdata(&sur40->vdev, sur40); + + error = video_register_device(&sur40->vdev, VFL_TYPE_GRABBER, -1); + if (error) { + dev_err(&interface->dev, + "Unable to register video subdevice."); + goto err_unreg_video; + } + /* we can register the device now, as it is ready */ usb_set_intfdata(interface, sur40); dev_dbg(&interface->dev, "%s is now attached\n", DRIVER_DESC); return 0; +err_unreg_video: + video_unregister_device(&sur40->vdev); +err_unreg_v4l2: + v4l2_device_unregister(&sur40->v4l2); err_free_buffer: kfree(sur40->bulk_in_buffer); err_free_polldev: @@ -436,6 +622,10 @@ static void sur40_disconnect(struct usb_interface *interface) { struct sur40_state *sur40 = usb_get_intfdata(interface); + video_unregister_device(&sur40->vdev); + v4l2_device_unregister(&sur40->v4l2); + vb2_dma_sg_cleanup_ctx(sur40->alloc_ctx); + input_unregister_polled_device(sur40->input); input_free_polled_device(sur40->input); kfree(sur40->bulk_in_buffer); @@ -445,12 +635,274 @@ static void sur40_disconnect(struct usb_interface *interface) dev_dbg(&interface->dev, "%s is now disconnected\n", DRIVER_DESC); } +/* + * Setup the constraints of the queue: besides setting the number of planes + * per buffer and the size and allocation context of each plane, it also + * checks if sufficient buffers have been allocated. Usually 3 is a good + * minimum number: many DMA engines need a minimum of 2 buffers in the + * queue and you need to have another available for userspace processing. + */ +static int sur40_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(q); + + if (q->num_buffers + *nbuffers < 3) + *nbuffers = 3 - q->num_buffers; + + if (fmt && fmt->fmt.pix.sizeimage < sur40_video_format.sizeimage) + return -EINVAL; + + *nplanes = 1; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : sur40_video_format.sizeimage; + alloc_ctxs[0] = sur40->alloc_ctx; + + return 0; +} + +/* + * Prepare the buffer for queueing to the DMA engine: check and set the + * payload size. + */ +static int sur40_buffer_prepare(struct vb2_buffer *vb) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = sur40_video_format.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(&sur40->usbdev->dev, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + return 0; +} + +/* + * Queue this buffer to the DMA engine. + */ +static void sur40_buffer_queue(struct vb2_buffer *vb) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue); + struct sur40_buffer *buf = (struct sur40_buffer *)vb; + + spin_lock(&sur40->qlock); + list_add_tail(&buf->list, &sur40->buf_list); + spin_unlock(&sur40->qlock); +} + +static void return_all_buffers(struct sur40_state *sur40, + enum vb2_buffer_state state) +{ + struct sur40_buffer *buf, *node; + + spin_lock(&sur40->qlock); + list_for_each_entry_safe(buf, node, &sur40->buf_list, list) { + vb2_buffer_done(&buf->vb, state); + list_del(&buf->list); + } + spin_unlock(&sur40->qlock); +} + +/* + * Start streaming. First check if the minimum number of buffers have been + * queued. If not, then return -ENOBUFS and the vb2 framework will call + * this function again the next time a buffer has been queued until enough + * buffers are available to actually start the DMA engine. + */ +static int sur40_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(vq); + + sur40->sequence = 0; + return 0; +} + +/* + * Stop the DMA engine. Any remaining buffers in the DMA queue are dequeued + * and passed on to the vb2 framework marked as STATE_ERROR. + */ +static void sur40_stop_streaming(struct vb2_queue *vq) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(vq); + sur40->sequence = -1; + + /* Release all active buffers */ + return_all_buffers(sur40, VB2_BUF_STATE_ERROR); +} + +/* V4L ioctl */ +static int sur40_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct sur40_state *sur40 = video_drvdata(file); + + strlcpy(cap->driver, DRIVER_SHORT, sizeof(cap->driver)); + strlcpy(cap->card, DRIVER_LONG, sizeof(cap->card)); + usb_make_path(sur40->usbdev, cap->bus_info, sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int sur40_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + i->type = V4L2_INPUT_TYPE_CAMERA; + i->std = V4L2_STD_UNKNOWN; + strlcpy(i->name, "In-Cell Sensor", sizeof(i->name)); + i->capabilities = 0; + return 0; +} + +static int sur40_vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + return (i == 0) ? 0 : -EINVAL; +} + +static int sur40_vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int sur40_vidioc_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + f->fmt.pix = sur40_video_format; + return 0; +} + +static int sur40_vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index != 0) + return -EINVAL; + strlcpy(f->description, "8-bit greyscale", sizeof(f->description)); + f->pixelformat = V4L2_PIX_FMT_GREY; + f->flags = 0; + return 0; +} + +static int sur40_vidioc_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *f) +{ + if ((f->index != 0) || (f->pixel_format != V4L2_PIX_FMT_GREY)) + return -EINVAL; + + f->type = V4L2_FRMSIZE_TYPE_DISCRETE; + f->discrete.width = sur40_video_format.width; + f->discrete.height = sur40_video_format.height; + return 0; +} + +static int sur40_vidioc_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *f) +{ + if ((f->index > 1) || (f->pixel_format != V4L2_PIX_FMT_GREY) + || (f->width != sur40_video_format.width) + || (f->height != sur40_video_format.height)) + return -EINVAL; + + f->type = V4L2_FRMIVAL_TYPE_DISCRETE; + f->discrete.denominator = 60/(f->index+1); + f->discrete.numerator = 1; + return 0; +} + + static const struct usb_device_id sur40_table[] = { { USB_DEVICE(ID_MICROSOFT, ID_SUR40) }, /* Samsung SUR40 */ { } /* terminating null entry */ }; MODULE_DEVICE_TABLE(usb, sur40_table); +/* V4L2 structures */ +static const struct vb2_ops sur40_queue_ops = { + .queue_setup = sur40_queue_setup, + .buf_prepare = sur40_buffer_prepare, + .buf_queue = sur40_buffer_queue, + .start_streaming = sur40_start_streaming, + .stop_streaming = sur40_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static const struct vb2_queue sur40_queue = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + /* + * VB2_USERPTR in currently not enabled: passing a user pointer to + * dma-sg will result in segment sizes that are not a multiple of + * 512 bytes, which is required by the host controller. + */ + .io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF, + .buf_struct_size = sizeof(struct sur40_buffer), + .ops = &sur40_queue_ops, + .mem_ops = &vb2_dma_sg_memops, + .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC, + .min_buffers_needed = 3, +}; + +static const struct v4l2_file_operations sur40_video_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = { + + .vidioc_querycap = sur40_vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = sur40_vidioc_enum_fmt, + .vidioc_try_fmt_vid_cap = sur40_vidioc_fmt, + .vidioc_s_fmt_vid_cap = sur40_vidioc_fmt, + .vidioc_g_fmt_vid_cap = sur40_vidioc_fmt, + + .vidioc_enum_framesizes = sur40_vidioc_enum_framesizes, + .vidioc_enum_frameintervals = sur40_vidioc_enum_frameintervals, + + .vidioc_enum_input = sur40_vidioc_enum_input, + .vidioc_g_input = sur40_vidioc_g_input, + .vidioc_s_input = sur40_vidioc_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct video_device sur40_video_device = { + .name = DRIVER_LONG, + .fops = &sur40_video_fops, + .ioctl_ops = &sur40_video_ioctl_ops, + .release = video_device_release_empty, +}; + +static const struct v4l2_pix_format sur40_video_format = { + .pixelformat = V4L2_PIX_FMT_GREY, + .width = SENSOR_RES_X / 2, + .height = SENSOR_RES_Y / 2, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .bytesperline = SENSOR_RES_X / 2, + .sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2), +}; + /* USB-specific object needed to register this driver with the USB subsystem. */ static struct usb_driver sur40_driver = { .name = DRIVER_SHORT, |