diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2016-12-16 06:36:09 +0100 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2016-12-16 06:36:09 +0100 |
commit | ebfb0184ef560897fad35005989e82433419202c (patch) | |
tree | 226a8195fdae6c79d90d76baa1cbdaf80f794bb0 /drivers/input/touchscreen | |
parent | Input: imx6ul_tsc - generalize the averaging property (diff) | |
parent | Input: synaptics-rmi4 - add support for F34 V7 bootloader (diff) | |
download | linux-ebfb0184ef560897fad35005989e82433419202c.tar.xz linux-ebfb0184ef560897fad35005989e82433419202c.zip |
Merge branch 'synaptics-rmi4' into next
Merge updated Synaptics RMI4 support, including support for SMBus
controllers and flashing firmware.
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 9 | ||||
-rw-r--r-- | drivers/input/touchscreen/atmel_mxt_ts.c | 521 | ||||
-rw-r--r-- | drivers/input/touchscreen/sur40.c | 142 |
3 files changed, 638 insertions, 34 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 507981356921..efca0133e266 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -115,6 +115,15 @@ config TOUCHSCREEN_ATMEL_MXT To compile this driver as a module, choose M here: the module will be called atmel_mxt_ts. +config TOUCHSCREEN_ATMEL_MXT_T37 + bool "Support T37 Diagnostic Data" + depends on TOUCHSCREEN_ATMEL_MXT + depends on VIDEO_V4L2=y || (TOUCHSCREEN_ATMEL_MXT=m && VIDEO_V4L2=m) + select VIDEOBUF2_VMALLOC + help + Say Y here if you want support to output data from the T37 + Diagnostic Data object using a V4L device. + config TOUCHSCREEN_AUO_PIXCIR tristate "AUO in-cell touchscreen using Pixcir ICs" depends on I2C diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 5af7907d0af4..e5d185fe69b9 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -4,6 +4,7 @@ * Copyright (C) 2010 Samsung Electronics Co.Ltd * Copyright (C) 2011-2014 Atmel Corporation * Copyright (C) 2012 Google, Inc. + * Copyright (C) 2016 Zodiac Inflight Innovations * * Author: Joonyoung Shim <jy0922.shim@samsung.com> * @@ -28,6 +29,10 @@ #include <linux/of.h> #include <linux/slab.h> #include <asm/unaligned.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-vmalloc.h> /* Firmware files */ #define MXT_FW_NAME "maxtouch.fw" @@ -99,6 +104,8 @@ struct t7_config { /* MXT_TOUCH_MULTI_T9 field */ #define MXT_T9_CTRL 0 +#define MXT_T9_XSIZE 3 +#define MXT_T9_YSIZE 4 #define MXT_T9_ORIENT 9 #define MXT_T9_RANGE 18 @@ -119,11 +126,31 @@ struct t9_range { /* MXT_TOUCH_MULTI_T9 orient */ #define MXT_T9_ORIENT_SWITCH (1 << 0) +#define MXT_T9_ORIENT_INVERTX (1 << 1) +#define MXT_T9_ORIENT_INVERTY (1 << 2) /* MXT_SPT_COMMSCONFIG_T18 */ #define MXT_COMMS_CTRL 0 #define MXT_COMMS_CMD 1 +/* MXT_DEBUG_DIAGNOSTIC_T37 */ +#define MXT_DIAGNOSTIC_PAGEUP 0x01 +#define MXT_DIAGNOSTIC_DELTAS 0x10 +#define MXT_DIAGNOSTIC_REFS 0x11 +#define MXT_DIAGNOSTIC_SIZE 128 + +#define MXT_FAMILY_1386 160 +#define MXT1386_COLUMNS 3 +#define MXT1386_PAGES_PER_COLUMN 8 + +struct t37_debug { +#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 + u8 mode; + u8 page; + u8 data[MXT_DIAGNOSTIC_SIZE]; +#endif +}; + /* Define for MXT_GEN_COMMAND_T6 */ #define MXT_BOOT_VALUE 0xa5 #define MXT_RESET_VALUE 0x01 @@ -133,10 +160,14 @@ struct t9_range { #define MXT_T100_CTRL 0 #define MXT_T100_CFG1 1 #define MXT_T100_TCHAUX 3 +#define MXT_T100_XSIZE 9 #define MXT_T100_XRANGE 13 +#define MXT_T100_YSIZE 20 #define MXT_T100_YRANGE 24 #define MXT_T100_CFG_SWITCHXY BIT(5) +#define MXT_T100_CFG_INVERTY BIT(6) +#define MXT_T100_CFG_INVERTX BIT(7) #define MXT_T100_TCHAUX_VECT BIT(0) #define MXT_T100_TCHAUX_AMPL BIT(1) @@ -205,6 +236,37 @@ struct mxt_object { u8 num_report_ids; } __packed; +struct mxt_dbg { + u16 t37_address; + u16 diag_cmd_address; + struct t37_debug *t37_buf; + unsigned int t37_pages; + unsigned int t37_nodes; + + struct v4l2_device v4l2; + struct v4l2_pix_format format; + struct video_device vdev; + struct vb2_queue queue; + struct mutex lock; + int input; +}; + +enum v4l_dbg_inputs { + MXT_V4L_INPUT_DELTAS, + MXT_V4L_INPUT_REFS, + MXT_V4L_INPUT_MAX, +}; + +static const struct v4l2_file_operations mxt_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, +}; + /* Each client has this additional data */ struct mxt_data { struct i2c_client *client; @@ -216,7 +278,11 @@ struct mxt_data { unsigned int irq; unsigned int max_x; unsigned int max_y; + bool invertx; + bool inverty; bool xy_switch; + u8 xsize; + u8 ysize; bool in_bootloader; u16 mem_size; u8 t100_aux_ampl; @@ -233,6 +299,7 @@ struct mxt_data { u8 num_touchids; u8 multitouch; struct t7_config t7_cfg; + struct mxt_dbg dbg; /* Cached parameters from object table */ u16 T5_address; @@ -257,6 +324,11 @@ struct mxt_data { struct completion crc_completion; }; +struct mxt_vb2_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + static size_t mxt_obj_size(const struct mxt_object *obj) { return obj->size_minus_one + 1; @@ -1503,6 +1575,11 @@ static void mxt_free_input_device(struct mxt_data *data) static void mxt_free_object_table(struct mxt_data *data) { +#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 + video_unregister_device(&data->dbg.vdev); + v4l2_device_unregister(&data->dbg.v4l2); +#endif + kfree(data->object_table); data->object_table = NULL; kfree(data->msg_buf); @@ -1661,6 +1738,18 @@ static int mxt_read_t9_resolution(struct mxt_data *data) return -EINVAL; error = __mxt_read_reg(client, + object->start_address + MXT_T9_XSIZE, + sizeof(data->xsize), &data->xsize); + if (error) + return error; + + error = __mxt_read_reg(client, + object->start_address + MXT_T9_YSIZE, + sizeof(data->ysize), &data->ysize); + if (error) + return error; + + error = __mxt_read_reg(client, object->start_address + MXT_T9_RANGE, sizeof(range), &range); if (error) @@ -1676,6 +1765,8 @@ static int mxt_read_t9_resolution(struct mxt_data *data) return error; data->xy_switch = orient & MXT_T9_ORIENT_SWITCH; + data->invertx = orient & MXT_T9_ORIENT_INVERTX; + data->inverty = orient & MXT_T9_ORIENT_INVERTY; return 0; } @@ -1710,6 +1801,18 @@ static int mxt_read_t100_config(struct mxt_data *data) data->max_y = get_unaligned_le16(&range_y); + error = __mxt_read_reg(client, + object->start_address + MXT_T100_XSIZE, + sizeof(data->xsize), &data->xsize); + if (error) + return error; + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_YSIZE, + sizeof(data->ysize), &data->ysize); + if (error) + return error; + /* read orientation config */ error = __mxt_read_reg(client, object->start_address + MXT_T100_CFG1, @@ -1718,6 +1821,8 @@ static int mxt_read_t100_config(struct mxt_data *data) return error; data->xy_switch = cfg & MXT_T100_CFG_SWITCHXY; + data->invertx = cfg & MXT_T100_CFG_INVERTX; + data->inverty = cfg & MXT_T100_CFG_INVERTY; /* allocate aux bytes */ error = __mxt_read_reg(client, @@ -2043,6 +2148,420 @@ recheck: return 0; } +#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 +static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x, + unsigned int y) +{ + struct mxt_info *info = &data->info; + struct mxt_dbg *dbg = &data->dbg; + unsigned int ofs, page; + unsigned int col = 0; + unsigned int col_width; + + if (info->family_id == MXT_FAMILY_1386) { + col_width = info->matrix_ysize / MXT1386_COLUMNS; + col = y / col_width; + y = y % col_width; + } else { + col_width = info->matrix_ysize; + } + + ofs = (y + (x * col_width)) * sizeof(u16); + page = ofs / MXT_DIAGNOSTIC_SIZE; + ofs %= MXT_DIAGNOSTIC_SIZE; + + if (info->family_id == MXT_FAMILY_1386) + page += col * MXT1386_PAGES_PER_COLUMN; + + return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]); +} + +static int mxt_convert_debug_pages(struct mxt_data *data, u16 *outbuf) +{ + struct mxt_dbg *dbg = &data->dbg; + unsigned int x = 0; + unsigned int y = 0; + unsigned int i, rx, ry; + + for (i = 0; i < dbg->t37_nodes; i++) { + /* Handle orientation */ + rx = data->xy_switch ? y : x; + ry = data->xy_switch ? x : y; + rx = data->invertx ? (data->xsize - 1 - rx) : rx; + ry = data->inverty ? (data->ysize - 1 - ry) : ry; + + outbuf[i] = mxt_get_debug_value(data, rx, ry); + + /* Next value */ + if (++x >= (data->xy_switch ? data->ysize : data->xsize)) { + x = 0; + y++; + } + } + + return 0; +} + +static int mxt_read_diagnostic_debug(struct mxt_data *data, u8 mode, + u16 *outbuf) +{ + struct mxt_dbg *dbg = &data->dbg; + int retries = 0; + int page; + int ret; + u8 cmd = mode; + struct t37_debug *p; + u8 cmd_poll; + + for (page = 0; page < dbg->t37_pages; page++) { + p = dbg->t37_buf + page; + + ret = mxt_write_reg(data->client, dbg->diag_cmd_address, + cmd); + if (ret) + return ret; + + retries = 0; + msleep(20); +wait_cmd: + /* Read back command byte */ + ret = __mxt_read_reg(data->client, dbg->diag_cmd_address, + sizeof(cmd_poll), &cmd_poll); + if (ret) + return ret; + + /* Field is cleared once the command has been processed */ + if (cmd_poll) { + if (retries++ > 100) + return -EINVAL; + + msleep(20); + goto wait_cmd; + } + + /* Read T37 page */ + ret = __mxt_read_reg(data->client, dbg->t37_address, + sizeof(struct t37_debug), p); + if (ret) + return ret; + + if (p->mode != mode || p->page != page) { + dev_err(&data->client->dev, "T37 page mismatch\n"); + return -EINVAL; + } + + dev_dbg(&data->client->dev, "%s page:%d retries:%d\n", + __func__, page, retries); + + /* For remaining pages, write PAGEUP rather than mode */ + cmd = MXT_DIAGNOSTIC_PAGEUP; + } + + return mxt_convert_debug_pages(data, outbuf); +} + +static int mxt_queue_setup(struct vb2_queue *q, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct mxt_data *data = q->drv_priv; + size_t size = data->dbg.t37_nodes * sizeof(u16); + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static void mxt_buffer_queue(struct vb2_buffer *vb) +{ + struct mxt_data *data = vb2_get_drv_priv(vb->vb2_queue); + u16 *ptr; + int ret; + u8 mode; + + ptr = vb2_plane_vaddr(vb, 0); + if (!ptr) { + dev_err(&data->client->dev, "Error acquiring frame ptr\n"); + goto fault; + } + + switch (data->dbg.input) { + case MXT_V4L_INPUT_DELTAS: + default: + mode = MXT_DIAGNOSTIC_DELTAS; + break; + + case MXT_V4L_INPUT_REFS: + mode = MXT_DIAGNOSTIC_REFS; + break; + } + + ret = mxt_read_diagnostic_debug(data, mode, ptr); + if (ret) + goto fault; + + vb2_set_plane_payload(vb, 0, data->dbg.t37_nodes * sizeof(u16)); + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + return; + +fault: + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); +} + +/* V4L2 structures */ +static const struct vb2_ops mxt_queue_ops = { + .queue_setup = mxt_queue_setup, + .buf_queue = mxt_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static const struct vb2_queue mxt_queue = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ, + .buf_struct_size = sizeof(struct mxt_vb2_buffer), + .ops = &mxt_queue_ops, + .mem_ops = &vb2_vmalloc_memops, + .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC, + .min_buffers_needed = 1, +}; + +static int mxt_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct mxt_data *data = video_drvdata(file); + + strlcpy(cap->driver, "atmel_mxt_ts", sizeof(cap->driver)); + strlcpy(cap->card, "atmel_mxt_ts touch", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "I2C:%s", dev_name(&data->client->dev)); + return 0; +} + +static int mxt_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index >= MXT_V4L_INPUT_MAX) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_TOUCH; + + switch (i->index) { + case MXT_V4L_INPUT_REFS: + strlcpy(i->name, "Mutual Capacitance References", + sizeof(i->name)); + break; + case MXT_V4L_INPUT_DELTAS: + strlcpy(i->name, "Mutual Capacitance Deltas", sizeof(i->name)); + break; + } + + return 0; +} + +static int mxt_set_input(struct mxt_data *data, unsigned int i) +{ + struct v4l2_pix_format *f = &data->dbg.format; + + if (i >= MXT_V4L_INPUT_MAX) + return -EINVAL; + + if (i == MXT_V4L_INPUT_DELTAS) + f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + else + f->pixelformat = V4L2_TCH_FMT_TU16; + + f->width = data->xy_switch ? data->ysize : data->xsize; + f->height = data->xy_switch ? data->xsize : data->ysize; + f->field = V4L2_FIELD_NONE; + f->colorspace = V4L2_COLORSPACE_RAW; + f->bytesperline = f->width * sizeof(u16); + f->sizeimage = f->width * f->height * sizeof(u16); + + data->dbg.input = i; + + return 0; +} + +static int mxt_vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + return mxt_set_input(video_drvdata(file), i); +} + +static int mxt_vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct mxt_data *data = video_drvdata(file); + + *i = data->dbg.input; + + return 0; +} + +static int mxt_vidioc_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct mxt_data *data = video_drvdata(file); + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix = data->dbg.format; + + return 0; +} + +static int mxt_vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (fmt->index) { + case 0: + fmt->pixelformat = V4L2_TCH_FMT_TU16; + break; + + case 1: + fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int mxt_vidioc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + a->parm.capture.readbuffers = 1; + a->parm.capture.timeperframe.numerator = 1; + a->parm.capture.timeperframe.denominator = 10; + return 0; +} + +static const struct v4l2_ioctl_ops mxt_video_ioctl_ops = { + .vidioc_querycap = mxt_vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = mxt_vidioc_enum_fmt, + .vidioc_s_fmt_vid_cap = mxt_vidioc_fmt, + .vidioc_g_fmt_vid_cap = mxt_vidioc_fmt, + .vidioc_try_fmt_vid_cap = mxt_vidioc_fmt, + .vidioc_g_parm = mxt_vidioc_g_parm, + + .vidioc_enum_input = mxt_vidioc_enum_input, + .vidioc_g_input = mxt_vidioc_g_input, + .vidioc_s_input = mxt_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 mxt_video_device = { + .name = "Atmel maxTouch", + .fops = &mxt_video_fops, + .ioctl_ops = &mxt_video_ioctl_ops, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING, +}; + +static void mxt_debug_init(struct mxt_data *data) +{ + struct mxt_info *info = &data->info; + struct mxt_dbg *dbg = &data->dbg; + struct mxt_object *object; + int error; + + object = mxt_get_object(data, MXT_GEN_COMMAND_T6); + if (!object) + goto error; + + dbg->diag_cmd_address = object->start_address + MXT_COMMAND_DIAGNOSTIC; + + object = mxt_get_object(data, MXT_DEBUG_DIAGNOSTIC_T37); + if (!object) + goto error; + + if (mxt_obj_size(object) != sizeof(struct t37_debug)) { + dev_warn(&data->client->dev, "Bad T37 size"); + goto error; + } + + dbg->t37_address = object->start_address; + + /* Calculate size of data and allocate buffer */ + dbg->t37_nodes = data->xsize * data->ysize; + + if (info->family_id == MXT_FAMILY_1386) + dbg->t37_pages = MXT1386_COLUMNS * MXT1386_PAGES_PER_COLUMN; + else + dbg->t37_pages = DIV_ROUND_UP(data->xsize * + data->info.matrix_ysize * + sizeof(u16), + sizeof(dbg->t37_buf->data)); + + dbg->t37_buf = devm_kmalloc_array(&data->client->dev, dbg->t37_pages, + sizeof(struct t37_debug), GFP_KERNEL); + if (!dbg->t37_buf) + goto error; + + /* init channel to zero */ + mxt_set_input(data, 0); + + /* register video device */ + snprintf(dbg->v4l2.name, sizeof(dbg->v4l2.name), "%s", "atmel_mxt_ts"); + error = v4l2_device_register(&data->client->dev, &dbg->v4l2); + if (error) + goto error; + + /* initialize the queue */ + mutex_init(&dbg->lock); + dbg->queue = mxt_queue; + dbg->queue.drv_priv = data; + dbg->queue.lock = &dbg->lock; + dbg->queue.dev = &data->client->dev; + + error = vb2_queue_init(&dbg->queue); + if (error) + goto error_unreg_v4l2; + + dbg->vdev = mxt_video_device; + dbg->vdev.v4l2_dev = &dbg->v4l2; + dbg->vdev.lock = &dbg->lock; + dbg->vdev.vfl_dir = VFL_DIR_RX; + dbg->vdev.queue = &dbg->queue; + video_set_drvdata(&dbg->vdev, data); + + error = video_register_device(&dbg->vdev, VFL_TYPE_TOUCH, -1); + if (error) + goto error_unreg_v4l2; + + return; + +error_unreg_v4l2: + v4l2_device_unregister(&dbg->v4l2); +error: + dev_warn(&data->client->dev, "Error initializing T37\n"); +} +#else +static void mxt_debug_init(struct mxt_data *data) +{ +} +#endif + static int mxt_configure_objects(struct mxt_data *data, const struct firmware *cfg) { @@ -2070,6 +2589,8 @@ static int mxt_configure_objects(struct mxt_data *data, dev_warn(dev, "No touch object detected\n"); } + mxt_debug_init(data); + dev_info(dev, "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", info->family_id, info->variant_id, info->version >> 4, diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 4ea475775d58..aefb6e11f88a 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -139,6 +139,27 @@ struct sur40_image_header { #define SUR40_GET_STATE 0xc5 /* 4 bytes state (?) */ #define SUR40_GET_SENSORS 0xb1 /* 8 bytes sensors */ +static const struct v4l2_pix_format sur40_pix_format[] = { + { + .pixelformat = V4L2_TCH_FMT_TU08, + .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), + }, + { + .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), + } +}; + /* master device state */ struct sur40_state { @@ -149,6 +170,7 @@ struct sur40_state { struct v4l2_device v4l2; struct video_device vdev; struct mutex lock; + struct v4l2_pix_format pix_fmt; struct vb2_queue queue; struct list_head buf_list; @@ -169,7 +191,6 @@ struct sur40_buffer { /* 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); @@ -420,7 +441,7 @@ static void sur40_process_video(struct sur40_state *sur40) goto err_poll; } - if (le32_to_cpu(img->size) != sur40_video_format.sizeimage) { + if (le32_to_cpu(img->size) != sur40->pix_fmt.sizeimage) { dev_err(sur40->dev, "image size mismatch\n"); goto err_poll; } @@ -431,7 +452,7 @@ static void sur40_process_video(struct sur40_state *sur40) result = usb_sg_init(&sgr, sur40->usbdev, usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0, - sgt->sgl, sgt->nents, sur40_video_format.sizeimage, 0); + sgt->sgl, sgt->nents, sur40->pix_fmt.sizeimage, 0); if (result < 0) { dev_err(sur40->dev, "error %d in usb_sg_init\n", result); goto err_poll; @@ -586,13 +607,14 @@ static int sur40_probe(struct usb_interface *interface, if (error) goto err_unreg_v4l2; + sur40->pix_fmt = sur40_pix_format[0]; 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); + error = video_register_device(&sur40->vdev, VFL_TYPE_TOUCH, -1); if (error) { dev_err(&interface->dev, "Unable to register video subdevice."); @@ -647,14 +669,16 @@ static int sur40_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { + struct sur40_state *sur40 = vb2_get_drv_priv(q); + if (q->num_buffers + *nbuffers < 3) *nbuffers = 3 - q->num_buffers; if (*nplanes) - return sizes[0] < sur40_video_format.sizeimage ? -EINVAL : 0; + return sizes[0] < sur40->pix_fmt.sizeimage ? -EINVAL : 0; *nplanes = 1; - sizes[0] = sur40_video_format.sizeimage; + sizes[0] = sur40->pix_fmt.sizeimage; return 0; } @@ -666,7 +690,7 @@ static int sur40_queue_setup(struct vb2_queue *q, 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; + unsigned long size = sur40->pix_fmt.sizeimage; if (vb2_plane_size(vb, 0) < size) { dev_err(&sur40->usbdev->dev, "buffer too small (%lu < %lu)\n", @@ -741,7 +765,7 @@ static int sur40_vidioc_querycap(struct file *file, void *priv, 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 | + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; @@ -753,7 +777,7 @@ static int sur40_vidioc_enum_input(struct file *file, void *priv, { if (i->index != 0) return -EINVAL; - i->type = V4L2_INPUT_TYPE_CAMERA; + i->type = V4L2_INPUT_TYPE_TOUCH; i->std = V4L2_STD_UNKNOWN; strlcpy(i->name, "In-Cell Sensor", sizeof(i->name)); i->capabilities = 0; @@ -771,19 +795,70 @@ static int sur40_vidioc_g_input(struct file *file, void *priv, unsigned int *i) return 0; } -static int sur40_vidioc_fmt(struct file *file, void *priv, +static int sur40_vidioc_try_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_GREY: + f->fmt.pix = sur40_pix_format[1]; + break; + + default: + f->fmt.pix = sur40_pix_format[0]; + break; + } + + return 0; +} + +static int sur40_vidioc_s_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct sur40_state *sur40 = video_drvdata(file); + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_GREY: + sur40->pix_fmt = sur40_pix_format[1]; + break; + + default: + sur40->pix_fmt = sur40_pix_format[0]; + break; + } + + f->fmt.pix = sur40->pix_fmt; + return 0; +} + +static int sur40_vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { - f->fmt.pix = sur40_video_format; + struct sur40_state *sur40 = video_drvdata(file); + + f->fmt.pix = sur40->pix_fmt; + return 0; +} + +static int sur40_ioctl_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + p->parm.capture.timeperframe.numerator = 1; + p->parm.capture.timeperframe.denominator = 60; + p->parm.capture.readbuffers = 3; return 0; } static int sur40_vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - if (f->index != 0) + if (f->index >= ARRAY_SIZE(sur40_pix_format)) return -EINVAL; - f->pixelformat = V4L2_PIX_FMT_GREY; + + f->pixelformat = sur40_pix_format[f->index].pixelformat; f->flags = 0; return 0; } @@ -791,25 +866,31 @@ static int sur40_vidioc_enum_fmt(struct file *file, void *priv, 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)) + struct sur40_state *sur40 = video_drvdata(file); + + if ((f->index != 0) || ((f->pixel_format != V4L2_TCH_FMT_TU08) + && (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; + f->discrete.width = sur40->pix_fmt.width; + f->discrete.height = sur40->pix_fmt.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; + struct sur40_state *sur40 = video_drvdata(file); + + if ((f->index > 0) || ((f->pixel_format != V4L2_TCH_FMT_TU08) + && (f->pixel_format != V4L2_PIX_FMT_GREY)) + || (f->width != sur40->pix_fmt.width) + || (f->height != sur40->pix_fmt.height)) + return -EINVAL; f->type = V4L2_FRMIVAL_TYPE_DISCRETE; - f->discrete.denominator = 60/(f->index+1); + f->discrete.denominator = 60; f->discrete.numerator = 1; return 0; } @@ -862,13 +943,16 @@ 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_try_fmt_vid_cap = sur40_vidioc_try_fmt, + .vidioc_s_fmt_vid_cap = sur40_vidioc_s_fmt, + .vidioc_g_fmt_vid_cap = sur40_vidioc_g_fmt, .vidioc_enum_framesizes = sur40_vidioc_enum_framesizes, .vidioc_enum_frameintervals = sur40_vidioc_enum_frameintervals, + .vidioc_g_parm = sur40_ioctl_parm, + .vidioc_s_parm = sur40_ioctl_parm, + .vidioc_enum_input = sur40_vidioc_enum_input, .vidioc_g_input = sur40_vidioc_g_input, .vidioc_s_input = sur40_vidioc_s_input, @@ -891,16 +975,6 @@ static const struct video_device sur40_video_device = { .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, |